//
//  Copyright: Copyright (c) MOSEK ApS, Denmark. All rights reserved.
//
//  File:      tsp.cs
//
//  Purpose: Demonstrates a simple technique to the TSP
//           usign the Fusion API.
//
using System;
using System.Collections.Generic;
using System.Collections;
using mosek.fusion;

namespace mosek.fusion.example
{
  public class tsp {
    public static void Main(string [] args) {
      int []A_i = new int[] {0, 1, 2, 3, 1, 0, 2, 0};
      int []A_j = new int[] {1, 2, 3, 0, 0, 2, 1, 3};
      double []C_v =  new double[] {1.0, 1.0, 1.0, 1.0, 0.1, 0.1, 0.1, 0.1};

      int n = 4;
      Matrix adj = Matrix.Sparse(n, n, A_i, A_j, 1.0);
      Matrix costs = Matrix.Sparse(n, n, A_i, A_j, C_v);

      tsp_fusion(n, adj, costs , true, true);
      tsp_fusion(n, adj, costs , true, false);
    }

    public static void tsp_fusion(int n, Matrix A, Matrix C,
                                  bool remove_loops,
                                  bool remove_2_hop_loops) {
      Model M = new Model();

      Variable x = M.Variable(new int[] {n, n}, Domain.Binary());

      M.Constraint(Expr.Sum(x, 0), Domain.EqualsTo(1.0));
      M.Constraint(Expr.Sum(x, 1), Domain.EqualsTo(1.0));
      M.Constraint(x, Domain.LessThan( A ));

      M.Objective(ObjectiveSense.Minimize, Expr.Dot(C, x));

      if (remove_2_hop_loops)
        M.Constraint(Expr.Add(x, x.Transpose()), Domain.LessThan(1.0));

      if (remove_loops)
        M.Constraint(x.Diag(), Domain.EqualsTo(0.0));

      for (int it = 0; true; it++)
      {
        M.Solve();
        try
        {
          Console.WriteLine("\nit #{0} - solution cost: {1}", it, M.PrimalObjValue());
        } catch (SolutionError e) {}

        ArrayList cycles = new ArrayList();

        for (int i = 0; i < n; i++) {
          for (int j = 0; j < n; j++) {
            try {
              if ( x.Level()[i * n + j] <= 0.5) continue;
            } catch (SolutionError e) {}

            bool found = false;
            foreach (ArrayList c in cycles) {
              foreach (int[] a in c)
                if ( i == a[0] || j == a[0] ||
                     i == a[1] || j == a[1]  )
                {
                  c.Add( new int[] {i, j} );
                  found = true;
                  break;
                }
              if (found == true) break;
            }

            if (found != true) {
              ArrayList ll = new ArrayList();
              ll.Add( new int[] {i, j} );
              cycles.Add(ll);
            }
          }
        }

        Console.WriteLine("\ncycles:");
        foreach (ArrayList c in cycles) {
          foreach (int[] a in c)
            Console.Write("[{0},{1}] - ", a[0], a[1]);
          Console.Write("\n");
        }

        if (cycles.Count == 1) break;

        foreach (ArrayList c in cycles) {
          int [,] cc = new int[c.Count, 2];
          int i = 0;
          foreach (int[] a in c) {
            cc[i, 0] = a[0];
            cc[i, 1] = a[1];
            i++;
          }
          M.Constraint(Expr.Sum(x.Pick(cc)), Domain.LessThan( 1.0 * c.Count - 1 ));
        }
      }

      try {
        Console.WriteLine("\nsolution:");
        for (int i = 0; i < n; i++) {
          for (int j = 0; j < n; j++)
            Console.Write(" {0} ", ((int) x.Level()[i * n + j]));
          Console.Write("\n");
        }
      } catch (SolutionError e) {}
    }
  }
}



