
// JacopUtils.java
// Hakan Kjellerstrand; see http://www.hakank.org/JaCoP/
// Modified by Andrew Davison, ad@coe.psu.ac.th, Dec. 2022

/* Jacop utilities, mostly for printing search results,
   with a few array methods.

  More info on Jacop: https://jacop.cs.lth.se/
*/


import org.jacop.constraints.*;
import org.jacop.core.*;
import org.jacop.search.*;

import java.io.*;
import java.util.*;
import java.text.*;


public class JacopUtils
{
  private static final int MAX_COLS = 5;



  public static void printSolns(Search<IntVar> search)
  /* print the values for all the variables included
     in the search. If the search was for all solutions
     then print all the different bindings.
  */
  {
    SolutionListener<IntVar> sl = search.getSolutionListener();
    if (sl.getSolutions() == null) {
      System.out.println("No solutions found");
      return;
    }

    IntVar[] vars = sl.getVariables();

    if (sl.isRecordingSolutions()) {
      int numSolns = sl.solutionsNo();
      System.out.println("No. of Solutions: " + numSolns);
      for (int s = 1; s <= numSolns; s++) {
        Domain[] res = sl.getSolution(s);
        if (numSolns > 1)
          System.out.print(s + ". ");
        printDomain(vars, res);
      }
    }
    else { // only first solution available
      Domain[] res = sl.getSolution(1);
      printDomain(vars, res);
    }
  } // end of printSolns()



  private static void printDomain(IntVar[] vars, Domain[] res)
  {
    int numCols = 0;
    for(int i = 0; i < res.length; i++) {
      System.out.print(vars[i].id() +   //vars[i].value() + 
                       " = " + res[i] + ";  ");
      numCols++;
      if (numCols%MAX_COLS == 0)  // limit no. of vars on one line
        System.out.println();
    }
    System.out.println();
  }  // end of printDomain()



  public static void printGrouped(Search<IntVar> search)
  /* Variables with the same value are printed together.
  */
  {
    SolutionListener<IntVar> sl = search.getSolutionListener();
    if (sl.getSolutions() == null) {
      System.out.println("No solutions found");
      return;
    }

    IntVar[] vars = sl.getVariables();
    if (sl.isRecordingSolutions()) {
      int numSolns = sl.solutionsNo();
      System.out.println("No. of Solutions: " + numSolns);
      for (int s = 1; s <= numSolns; s++) {
        ValsMap map = new ValsMap();
        Domain[] res = sl.getSolution(s);
        if (numSolns > 1)
          System.out.print(s + ". ");
        mapValToVars(vars, res, map);
        map.print();
      }
    }
    else { // only first solution available
      Domain[] res = sl.getSolution(1);
      ValsMap map = new ValsMap();
      mapValToVars(vars, res, map);
      map.print();
    }
  } // end of printGrouped()



  private static void mapValToVars(IntVar[] vars, Domain[] res, 
                                                    ValsMap map)
  /* map value to a list of variables that have been 
     assigned that value */
  {
    for(int i = 0; i < res.length; i++) {
      Domain d = res[i];
      if (d.singleton() && (d instanceof IntDomain)) {
        int val = ((IntDomain)d).value();
        collectVars(val, vars, map); 
      }
    }
  }  // end of mapValToVars()


  private static void collectVars(int val, IntVar[] vars, ValsMap map)
  // find all vars for this value
  {
    for(int i = 0; i < vars.length; i++) {
      if (vars[i].singleton())
        if (vars[i].value() == val)
          map.add(val, vars[i].id());
    }
  }  // end of collectVars()




  public static void printVars(String id, Search<IntVar> search)
  // print the value assigned to the id variable
  {  printVars(new String[]{ id }, search);  }


  public static void printVars(String[] ids, Search<IntVar> search)
  /* only print the values assigned to the supplied 
     list of variables */
  {
    SolutionListener<IntVar> sl = search.getSolutionListener();
    if (sl.getSolutions() == null) {
      System.out.println("No solutions found");
      return;
    }

    IntVar[] vars = sl.getVariables();

    if (sl.isRecordingSolutions()) {
      int numSolns = sl.solutionsNo();
      System.out.println("No. of Solutions: " + numSolns);
      for (int s = 1; s <= numSolns; s++) {
        Domain[] res = sl.getSolution(s);
        if (numSolns > 1)
          System.out.print(s + ". ");
        printIDs(ids, vars, res);
      }
    }
    else { // only first solution available
      Domain[] res = sl.getSolution(1);
      printIDs(ids, vars, res);
    }
  } // end of printVars()


  private static void printIDs(String[] ids, IntVar[] vars, Domain[] res)
  {
    int numCols = 0;
    for(int i = 0; i < res.length; i++) {
      String id = vars[i].id();
      if (contains(ids, id)) {
        System.out.print(id + " = " + res[i] + ";  ");
        numCols++;
        if (numCols%MAX_COLS == 0)
          System.out.println();
      }
    }
    System.out.println();
  }  // end of printIDs()


  private static boolean contains(String[] arr, String s)
  {
    for (String val : arr) {
      if (s.equals(val))
        return true;
    }
    return false;
  }  // end of contains() 



  public static void printGrid(IntVar[][] grid)
  {
    for(int i = 0; i < grid.length; i++) {
      for(int j = 0; j < grid[0].length; j++)
        System.out.printf("%2d ", grid[i][j].value());
      System.out.println();
    }
  }   // end of printGrid()


  // ---------------- array methods ------------


  public static IntVar[] toArray(List<IntVar> ls)
  // list to 1D array
  {  return ls.toArray(new IntVar[1]);  }


  public static IntVar[] toArray(IntVar[][] arr)
  // 2D to 1D array
  {
    IntVar[] vs = new IntVar[arr.length * arr[0].length];
    for(int i = 0; i < arr.length; i++)
      for(int j = 0; j < arr[0].length; j++)  // assume array is regular
        vs[(i * arr.length) + j] = arr[i][j];
    return vs;
  }  // end of toArray()




  public static IntVar[] concat(IntVar[] arr, IntVar elem) 
  // add one element to end of an array
  { 
    IntVar[] nArr = new IntVar[arr.length + 1]; 
    for (int i = 0; i < arr.length; i++) 
      nArr[i] = arr[i]; 
    nArr[arr.length] = elem; 
    return nArr; 
  }  // end of concat()


  public static IntVar[] concat(IntVar[] arr1, IntVar[] arr2) 
  // concatenate two arrays
  {
    IntVar[] nArr = Arrays.copyOf(arr1, arr1.length + arr2.length);
    System.arraycopy(arr2, 0, nArr, arr1.length, arr2.length);
    return nArr;
  }


}  // end of JacopUtils class

