/*
 * Copyright 2002, 2003, 2004, 2005 - <A href="http://www.koalog.com">Koalog</A>
 */
package com.koalog.jcs.examples;

import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.Category;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.HashMap;
import com.koalog.jcs.variable.BooleanVariable;
import com.koalog.jcs.variable.IntegerVariable;
import com.koalog.jcs.constraint.BaseProblem;
import com.koalog.jcs.constraint.arithmetic.Atmost;
import com.koalog.jcs.constraint.arithmetic.Atleast;
import com.koalog.jcs.constraint.arithmetic.Exactly_SPARSE;
import com.koalog.jcs.constraint.arithmetic.Element;
import com.koalog.jcs.domain.SparseDomain;

/**
 * The cars sequencing problem (problem prob001 in CSPLIB).
 *
 <P>Cars have to be scheduled on a production line.
 * The cars differ by their options 
 * (the set of options of a car defines its type/class).
 * Options are installed by stations: 
 * for each block of b consecutive cars,
 * each station can handle at most s cars 
 * (s and b depend on the option/station). 
 *
 <P>The problem has been shown to be NP-complete (Gent 1999).
 *
 @author Yan Georget
 */
public class CarsSequencingProblem extends BaseProblem {
    //------------------------------------------------------------------------
    // PROPERTIES
    //------------------------------------------------------------------------
    /**
     * The problem data:
     <UL>
     <LI>First line: number of cars; number of options; number of types.
     <LI>Second line: for each option, 
     * the maximum number of cars (s) with that option in a block. 
     <LI>Third line: for each option, 
     * the block size (b) to which the maximum number refers; 
     * then for each type: index; number of cars in this type;
     * for each option, whether or not this type requires it (1 or 0). 
     </UL>
     */
    int[][] data;
    /** A map mapping a variable to an array of costs. */
    Map costs;

    //------------------------------------------------------------------------
    // CONSTRUCTOR
    //------------------------------------------------------------------------
    /**
     * Sole constructor.
     @param data the problem data
     */
    public CarsSequencingProblem(int[][] data) {
        super();
        this.data = data;
        int carsNb = data[0][0];
        int optionsNb = data[0][1];
        int typesNb = data[0][2];
        IntegerVariable[] type = new IntegerVariable[carsNb];
        for (int i=0; i<carsNb; i++) {
            type[inew IntegerVariable("type"+i,
                                          new SparseDomain(0, typesNb-1));
        }
        for (int k=0; k<typesNb; k++) {
            // faster than a GCC_SPARSE
            add(new Exactly_SPARSE(data[3+k][1], type, k));
        }
        BooleanVariable[][] o = new BooleanVariable[optionsNb][carsNb];
        for (int j=0; j<optionsNb; j++) {
            int[] l = new int[typesNb];
            for (int k=0; k<typesNb; k++) {
                l[k= data[3+k][2+j];
            }
            for (int i=0; i<carsNb; i++) {
                o[j][inew BooleanVariable("o"+j+"_"+i);
                add(new Element(o[j][i], type[i], l));
            }
        }
        // using[j] is the number of cars using option j
        int[] using = new int[optionsNb]
        // utilization[j] is the utilization of option j
        final int[] utilization = new int[optionsNb];
        for (int j=0; j<optionsNb; j++) {
            int b = data[2][j]// the block size 
            int s = data[1][j]// the maximal size 
            using[j0;
            for (int k=0; k<typesNb; k++) {
                 using[j+= data[3+k][1* data[3+k][2+j];
            }
            // utilization is rounded to an int using the precision/scale
            utilization[j(SCALE*using[j]*b(carsNb*s);
            cat.debug("using[" + j + "]=" + using[j]);
            cat.debug("utilization[" + j + "]=" + utilization[j]);
            // sequence constraints
            for (int i=0; i<carsNb+1-b; i++) {
                IntegerVariable[] block = new IntegerVariable[b];
                for (int k=0; k<b; k++) {
                    block[k= o[j][i+k]
                }
                add(new Atmost(s, block, 1));
            }
            // redundant constraints
            for (int t=1; b*t<carsNb; t++) {
                if (using[j]-s*t > 0) {
                    int length = carsNb-b*t;
                    BooleanVariable[] pf = new BooleanVariable[length];
                    System.arraycopy(o[j]0, pf, 0, length);
                    add(new Atleast(using[j]-s*t, pf, 1));
                    // useless with the variable heuristic chosen
                    // BooleanVariable[] pl = new BooleanVariable[length];
                    // System.arraycopy(o[j], carsNb-length, pl, 0, length);
                    // internalAdd(new Atleast(using[j]-s*t, pl, 1));
                }
            }
        }
        setVariables(type);
        // an array of pairs (option index, option utilization) 
        // as an array of arrays
        int[][] optionUtilization = new int[optionsNb][2];
        for (int j=0; j<optionsNb; j++) {
            optionUtilization[jnew int[] {j, utilization[j]};
        }
        // we sort the options according to their utilization
        Arrays.sort(optionUtilization, new Comparator() {
                public int compare(Object o1, Object o2) {
                    int[] ou1 = (int[]) o1;
                    int[] ou2 = (int[]) o2;
                    return ou1[1- ou2[1];
                }
            });
        // for each option, its 'cost'
        int[] optionCost = new int[optionsNb];
        int shift = 1;
        for (int j=0; j<optionsNb; j++) {
            optionCost[optionUtilization[j][0]] 
                optionUtilization[j][1* shift;
            shift *= SCALE;
            cat.debug("optionCost[" + j + "]=" + optionCost[j]);
        }
        costs = new HashMap(carsNb);
        for (int i=0; i<carsNb; i++) {
            // associates to each type, a cost 
            // which is the sum of the costs of the options in this type
            int[] c = new int[typesNb];
            for (int k=0; k<typesNb; k++) {
                c[k0
                for (int j=0; j<optionsNb; j++) {
                    if (data[3+k][2+j== 1) {
                        // because we use MinCost instead of MaxCost
                        c[k+= -optionCost[j];
                    }
                }
            }
            costs.put(type[i], c);
        }
    }
    
    //------------------------------------------------------------------------
    // STATIC METHODS
    //------------------------------------------------------------------------
    /**
     * Runs the problem.
     @param args the command line arguments
     * args[0] must contain a log4j properties file location
     */
    public static void main(String[] args) {
        PropertyConfigurator.configure(args[0]);
        new CarsSequencingSolver(new CarsSequencingProblem(ECAI88)).solve();
    }
        
    //------------------------------------------------------------------------
    // STATIC PROPERTIES
    //------------------------------------------------------------------------
    private static Category cat = 
       Category.getInstance(CarsSequencingTest.class);

    //------------------------------------------------------------------------
    // CONSTANTS
    //------------------------------------------------------------------------
    /** 
     * A scale/precision for ordering options.
     */
    public static final int SCALE = 20;

    /**
     * A problem proposed by Dincbas at ECAI88.
     */
    public static final int[][] ECAI88 = new int[][] {
        {1056},
        {12121},
        {23355},
        {0110110}
        {1100010}
        {2201001}
        {3201010}
        {4210100}
        {5211000
    };

    /**
     * A problem proposed by Regin & Puget (no solution).
     */
    public static final int[][] RP1 = new int[][] {
        {100522},
        {12121},
        {23355},
        {0610010},
        {11011100}
        {2211001}
        {3201100}
        {4800010}
        {51501000}
        {6101110}
        {7500110}
        {8210110}
        {9300100}
        {10210100}
        {11111101}
        {12801010}
        {13310011}
        {141010000}
        {15401001}
        {16400001}
        {17210001}
        {18411000}
        {19611010}
        {20110101}
        {21111111}
    };

    /**
     * A problem proposed by Smith (60%).
     */
    public static final int[][] S60_01 = new int[][] {
        {200524},
        {12121},
        {23355},
        {0310010},
        {18401000},
        {2501010},
        {3110011},
        {4811000},
        {53400100},
        {6311110},
        {71100001},
        {8301100},
        {9110101},
        {10411010},
        {11200101},
        {12111001},
        {13111101},
        {14300110},
        {15110001},
        {161200010},
        {171510000},
        {18201110},
        {19110110},
        {20111011},
        {21111100},
        {22100111},
        {23201001}
    };
        
    /**
     * A problem proposed by Smith (70%).
     */
    public static final int[][] S70_01 = new int[][] {
        {200526},
        {12121},
        {23355},
        {0410100},
        {1610010},
        {25901000},
        {31001010},
        {4110011},
        {51311000},
        {62300100},
        {7311110},
        {81100001},
        {9601100},
        {10110101},
        {11711010},
        {12200101},
        {13111001},
        {14111101},
        {15500110},
        {16210001},
        {17100011},
        {181400010},
        {191710000},
        {20201110},
        {21210110},
        {22111011},
        {23111100},
        {24100101},
        {25601000}
    };

    /**
     * A problem proposed by Smith (70%).
     */
    public static final int[][] S70_02 = new int[][] {
        {200523},
        {12121},
        {23355},
        {0310110},
        {1410001},
        {21211000},
        {3311100},
        {41900001},
        {5211001},
        {6311010},
        {7701010},
        {8200101},
        {9110101},
        {102600100},
        {113500010},
        {12201101},
        {13600110},
        {14710100},
        {15110011},
        {16100111},
        {17200011},
        {184801000},
        {19201100},
        {20201001},
        {21910000},
        {22310010}
    };

    /**
     * A problem proposed by Smith (80%).
     */
    public static final int[][] S80_01 = new int[][] {
        {200526},
        {12121},
        {23355},
        {0610100},
        {1810010},
        {23901000},
        {31401010},
        {4110011},
        {51511000},
        {61300100},
        {7411110},
        {81100001},
        {9901100},
        {10110101},
        {11911010},
        {12400101},
        {13211