import static choco.Choco.*;
import choco.cp.model.CPModel;
import choco.cp.solver.CPSolver;
import choco.cp.solver.search.integer.varselector.StaticVarOrder;
import choco.cp.solver.search.integer.varselector.MinDomain;
import choco.kernel.model.Model;
import choco.kernel.solver.Solver;
import choco.kernel.model.variables.integer.IntegerVariable;
import choco.kernel.solver.variables.integer.IntDomainVar;

public class SteinerTriple04 {    
  
  public static void main(String[] args) {

      CPModel model = new CPModel();
      int v = Integer.parseInt(args[0]); // # points
      int b = v*(v-1)/6;                 // # blocks

      IntegerVariable[][] A = new IntegerVariable[v][v];
      //
      // A[i][j] = k <-> the pair of points (i,j) is in block k
      // where there v points, b blocks, each block is of size 3
      // NOTE: this forces each pair of points to occur once and once only!
      //

      for (int i=0;i<v-1;i++)
	  for (int j=0;j<v;j++)
	      A[i][j] = A[j][i] = makeIntVar("A",0,b-1);
      for (int i=0;i<v;i++) A[i][i] = constant(0);

      IntegerVariable[][] block = new IntegerVariable[b][v];
      //
      // block[i][j] = 1 <-> block i contains point j
      //

      for (int i=0;i<b;i++)
	  for (int j=0;j<v;j++)
	      block[i][j] = makeIntVar("block",0,1);
      
      for (int i=0;i<v-1;i++)
	  for (int j=i+1;j<v;j++)
	      for (int k=0;k<b;k++)
		  model.addConstraint(ifOnlyIf(eq(A[i][j],k),and(eq(block[k][i],1),eq(block[k][j],1))));
      //
      // channel between A and block
      // A[i][j] = k <-> block[k][i] && block[k][j]
      // i.e. pair (i,j) \in block k iff block[k][i] is true and block[k][j] is true
      //

      for (int i=0;i<b;i++) model.addConstraint(eq(sum(block[i]),3));
      //
      // every block contains 3 points
      //

      if (args.length == 2 && Boolean.parseBoolean(args[1]))
	  for (int i=0;i<v-1;i++)
	      model.addConstraint(lex(A[i],A[i+1]));
      //
      // symmetry breaker, a redundant constraint
      //

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

      IntDomainVar[] decision = new IntDomainVar[v*(v-1)/2];
      for (int i=0,k=0;i<v-1;i++)
	  for (int j=i+1;j<v;j++,k++)
	      decision[k] = sol.getVar(A[i][j]);
      sol.setVarIntSelector(new MinDomain(sol,decision));
      // sol.setVarIntSelector(new StaticVarOrder(decision));
      //
      // decision variables are the pairs to be assigned to a block
      //

      System.out.println(sol.solve());
      sol.printRuntimeSatistics();
      for (int i=0;i<b;i++){
	  for (int j=0;j<v;j++)
	      if(sol.getVar(block[i][j]).getVal() == 1) System.out.print(j +" ");
	  for (int j=0;j<v;j++)
	      System.out.print(sol.getVar(block[i][j]).getVal());
	  System.out.println();
      }

      System.out.println();

      for (int i=0;i<v;i++){
	  for (int j=0;j<v;j++)
	      if (i!=j) System.out.print(sol.getVar(A[i][j]).getVal() +" ");
	      else System.out.print("- ");
	  System.out.println();
      }
  }
}