/*
   * (non-Javadoc)
   * @see org.cloudbus.cloudsim.VmAllocationPolicy#allocateHostForVm(org.cloudbus.cloudsim.Vm,
   * org.cloudbus.cloudsim.Host)
   */
  @Override
  public boolean allocateHostForVm(Vm vm, Host host) {
    if (host == null) {
      Log.formatLine("%.2f: No suitable host found for VM #" + vm.getId() + "\n", CloudSim.clock());
      return false;
    }
    if (host.vmCreate(vm)) { // if vm has been succesfully created in the host
      getVmTable().put(vm.getUid(), host);

      /*
       * update host cache pain information
       * in CacheMatrix.HOST_PAIN_LIST
       */
      CacheMatrix.update_host_pain_add_vm(vm, host);
      /*
       * end update host cache pain information
       */

      Log.formatLine(
          "%.2f: VM #" + vm.getId() + " has been allocated to the host #" + host.getId(),
          CloudSim.clock());
      return true;
    }
    Log.formatLine(
        "%.2f: Creation of VM #" + vm.getId() + " on the host #" + host.getId() + " failed\n",
        CloudSim.clock());
    return false;
  }
  /**
   * Find host for vm.
   *
   * @param vm the vm
   * @param excludedHosts the excluded hosts
   * @return the power host
   */
  public PowerHost findHostForVm(Vm vm, Set<? extends Host> excludedHosts) {

    PowerHost allocatedHost = null;
    double minPain = Double.MAX_VALUE;

    for (PowerHost host : this.<PowerHost>getHostList()) {
      if (excludedHosts.contains(host)) {
        continue;
      }
      if (host.isSuitableForVm(vm)) {
        if (getUtilizationOfCpuMips(host) != 0 && isHostOverUtilizedAfterAllocation(host, vm)) {
          continue;
        }

        if (host.isSuitableForVm(vm)) {
          if (host.getUtilizationOfCpuMips() != 0 && isHostOverUtilizedAfterAllocation(host, vm)) {
            continue;
          }

          /*
           * select host based on pain
           */
          double pain = CacheMatrix.get_pain_with_host(vm.getId(), host);
          if (pain < minPain) {
            minPain = pain;
            allocatedHost = host;
          }
        }
      }
    }
    return allocatedHost;
  }
  /**
   * Optimize allocation of the VMs according to current utilization.
   *
   * @param vmList the vm list
   * @return the array list< hash map< string, object>>
   */
  @Override
  public List<Map<String, Object>> optimizeAllocation(List<? extends Vm> vmList) {

    /*
     * Calculate total pain
     */
    Log.printLine();
    double total_pain = 0.0;
    for (PowerHostUtilizationHistory host : this.<PowerHostUtilizationHistory>getHostList()) {
      double host_pain = 0.0;
      for (Vm vm_i : host.getVmList()) {
        for (Vm vm_j : host.getVmList()) {
          host_pain += CacheMatrix.get_pain(vm_i.getId(), vm_j.getId());
        }
      }
      Log.printLine("Pain of Host " + host.getId() + " : " + host_pain);
      total_pain += host_pain;
    }
    Log.printLine("==========================");
    Log.printLine("Total pain : " + total_pain);
    Log.printLine("==========================");
    Log.printLine();

    ExecutionTimeMeasurer.start("optimizeAllocationTotal");

    ExecutionTimeMeasurer.start("optimizeAllocationHostSelection");
    List<PowerHostUtilizationHistory> overUtilizedHosts = getOverUtilizedHosts();
    getExecutionTimeHistoryHostSelection()
        .add(ExecutionTimeMeasurer.end("optimizeAllocationHostSelection"));

    printOverUtilizedHosts(overUtilizedHosts);

    saveAllocation();

    ExecutionTimeMeasurer.start("optimizeAllocationVmSelection");
    List<? extends Vm> vmsToMigrate = getVmsToMigrateFromHosts(overUtilizedHosts);
    getExecutionTimeHistoryVmSelection()
        .add(ExecutionTimeMeasurer.end("optimizeAllocationVmSelection"));

    Log.printLine("Reallocation of VMs from the over-utilized hosts:");
    ExecutionTimeMeasurer.start("optimizeAllocationVmReallocation");
    List<Map<String, Object>> migrationMap =
        getNewVmPlacement(vmsToMigrate, new HashSet<Host>(overUtilizedHosts));
    getExecutionTimeHistoryVmReallocation()
        .add(ExecutionTimeMeasurer.end("optimizeAllocationVmReallocation"));
    Log.printLine();

    /*
     * Comment this line to disable turning off underutilized hosts
     */
    // migrationMap.addAll(getMigrationMapFromUnderUtilizedHosts(overUtilizedHosts));

    restoreAllocation();

    getExecutionTimeHistoryTotal().add(ExecutionTimeMeasurer.end("optimizeAllocationTotal"));

    return migrationMap;
  }
  /** @param args */
  public static void main(String[] args) {
    // TODO Auto-generated method stub

    CacheMatrix cm = new CacheMatrix();
    cm.init();

    try {
      // Create a problem with |y_{ijk}| variables and 0 constraints
      int ijk =
          ExpConstants.NUMBER_OF_VMS
              * // i
              ExpConstants.NUMBER_OF_VMS
              * // j
              ExpConstants.NUMBER_OF_HOSTS; // k

      LpSolve solver = LpSolve.makeLp(0, ijk);

      // claim that y_{ijk} is binary
      for (int p = 1; p <= ijk; p++) {
        solver.setBinary(p, true);
      }

      // index of y_{ijk} is (i+j*n_t+k*n_t*n_t)+1
      // +1 because LpSolve index starts from 1

      String coeff = "";
      for (int kk = 0; kk < ExpConstants.NUMBER_OF_HOSTS; kk++) {
        // ********begin********//
        coeff = "";
        for (int k = 0; k < ExpConstants.NUMBER_OF_HOSTS; k++) {
          for (int j = 0; j < ExpConstants.NUMBER_OF_VMS; j++) {
            for (int i = 0; i < ExpConstants.NUMBER_OF_VMS; i++) {
              if (k == kk) {
                coeff += "1 ";
              } else {
                coeff += "0 ";
              }
            }
          }
        }
        /*
         *  add constraints
         */
        int upper = 2 * ExpConstants.NUMBER_OF_VMS * 4;
        // int lower = upper - ExpConstants.NUMBER_OF_VMS * ExpConstants.NUMBER_OF_VMS;

        // \sum_{i=1}^{n_t}\sum_{j=1}^{n_t}y_{ijk} \leq 2n_{t}c_{k}
        solver.strAddConstraint(coeff, LpSolve.LE, upper);

        // 2n_{t}c_{k}-n_{t}^2 \leq \sum_{i=1}^{n_t}\sum_{j=1}^{n_t}y_{ijk}
        // solver.strAddConstraint(coeff, LpSolve.GE, 0);

        // System.out.println();
        // System.out.print(coeff);
        // System.out.println("<="+upper);
        // System.out.println();
        // System.out.println();

        // ********end*********//
      }

      for (int ii = 0; ii < ExpConstants.NUMBER_OF_VMS; ii++) {
        for (int jj = 0; jj < ExpConstants.NUMBER_OF_VMS; jj++) {
          // ********begin********//
          coeff = "";
          for (int k = 0; k < ExpConstants.NUMBER_OF_HOSTS; k++) {
            for (int j = 0; j < ExpConstants.NUMBER_OF_VMS; j++) {
              for (int i = 0; i < ExpConstants.NUMBER_OF_VMS; i++) {
                if (i == ii && j == jj) {
                  coeff += "1 ";
                } else {
                  coeff += "0 ";
                }
              }
            }
          }
          /*
           *  add constraints
           */

          // \sum_{k=1}^{n_p}y_{ijk} \leq 2
          solver.strAddConstraint(coeff, LpSolve.LE, 1);

          // 1 \leq \sum_{k=1}^{n_p}y_{ijk}
          // solver.strAddConstraint(coeff, LpSolve.GE, 1);

          // System.out.println();
          // System.out.print("1<= ");
          // System.out.print(coeff);
          // System.out.print("<=2");
          // System.out.println();

          // ********end*********//
        }
      }

      coeff = "";
      for (int kk = 0; kk < ExpConstants.NUMBER_OF_HOSTS; kk++) {
        for (int jj = 0; jj < ExpConstants.NUMBER_OF_VMS; jj++) {
          for (int ii = 0; ii < ExpConstants.NUMBER_OF_VMS; ii++) {
            coeff += "1 ";
          }
        }
      }
      int upper = (int) Math.ceil(ExpConstants.NUMBER_OF_VMS / ExpConstants.NUMBER_OF_HOSTS);
      int lower = (int) Math.floor(ExpConstants.NUMBER_OF_VMS / ExpConstants.NUMBER_OF_HOSTS);
      int r = ExpConstants.NUMBER_OF_VMS % ExpConstants.NUMBER_OF_HOSTS;
      solver.strAddConstraint(
          coeff,
          LpSolve.GE,
          upper * upper * r + lower * lower * (ExpConstants.NUMBER_OF_HOSTS - r));

      /*
       * set objective function
       */
      coeff = "";
      for (int k = 0; k < ExpConstants.NUMBER_OF_HOSTS; k++) {
        for (int j = 0; j < ExpConstants.NUMBER_OF_VMS; j++) {
          for (int i = 0; i < ExpConstants.NUMBER_OF_VMS; i++) {
            coeff += String.valueOf(CacheMatrix.get_pain(i, j)) + " ";
          }
        }
      }

      // System.out.println(coeff);
      // System.out.println();

      solver.strSetObjFn(coeff);
      solver.setMinim();

      // solve the problem
      solver.solve();

      // print solution
      System.out.println("Value of objective function: " + solver.getObjective());
      /*double[] var = solver.getPtrVariables();
      for (int i = 0; i < var.length; i++) {
        System.out.println("Value of var[" + i + "] = " + var[i]);
      }*/

      // delete the problem and free memory
      solver.deleteLp();

    } catch (LpSolveException e) {
      e.printStackTrace();
    }
  }