/** Update task set dependencies */
  private void updateTaskSetDependencies() {

    Collection sets = mTask2TaskSet.values();
    for (Iterator it = sets.iterator(); it.hasNext(); ) {
      TaskSet set = (TaskSet) it.next();
      if (!set.hasChecked) {
        set.hasChecked = true;
        set.getChildList().clear();
        set.getParentList().clear();
        for (Task task : set.getTaskList()) {
          for (Iterator tIt = task.getParentList().iterator(); tIt.hasNext(); ) {
            Task parent = (Task) tIt.next();
            TaskSet parentSet = (TaskSet) mTask2TaskSet.get(parent);
            if (!set.getParentList().contains(parentSet) && set != parentSet) {
              set.getParentList().add(parentSet);
            }
          }
          for (Iterator tIt = task.getChildList().iterator(); tIt.hasNext(); ) {
            Task child = (Task) tIt.next();
            TaskSet childSet = (TaskSet) mTask2TaskSet.get(child);
            if (!set.getChildList().contains(childSet) && set != childSet) {
              set.getChildList().add(childSet);
            }
          }
        }
      }
    }
    // within each method
    cleanTaskSetChecked();
  }
  /** The main function */
  @Override
  public void run() {
    if (clusterNum > 0 || clusterSize > 0) {
      for (Iterator it = getTaskList().iterator(); it.hasNext(); ) {
        Task task = (Task) it.next();
        int depth = task.getDepth();
        if (!mDepth2Task.containsKey(depth)) {
          mDepth2Task.put(depth, new ArrayList<>());
        }
        List list = mDepth2Task.get(depth);
        if (!list.contains(task)) {
          list.add(task);
        }
      }
    }
    /** if clusters.num is set. */
    if (clusterNum > 0) {
      bundleClustering();
      /** else if clusters.size is set. */
    } else if (clusterSize > 0) {
      collapseClustering();
    }

    updateDependencies();
    addClustDelay();
  }
 /** Add the pair from the mRecover. */
 private void recover() {
   for (Iterator it = mRecover.entrySet().iterator(); it.hasNext(); ) {
     Entry entry = (Entry) it.next();
     Task set = (Task) entry.getKey();
     Task children = (Task) entry.getValue();
     set.getChildList().add(children);
     children.getParentList().add(set);
   }
 }
  /** The main function */
  @Override
  public void run() {

    for (Iterator it = getTaskList().iterator(); it.hasNext(); ) {
      Task task = (Task) it.next();
      double duration = task.getCloudletLength() / 1000;

      for (int i = 0; i < task.getParentList().size(); i++) {
        Task parent = task.getParentList().get(i);
      }

      for (int i = 0; i < task.getChildList().size(); i++) {
        Task child = task.getChildList().get(i);
      }

      int vmNum = getVmList().size();
      /** Randomly choose a vm */
      Random random = new Random((long) duration);
      int vmId = random.nextInt(vmNum);

      CondorVM vm = (CondorVM) getVmList().get(vmId);
      // This shows the cpu capability of a vm
      double mips = vm.getMips();

      task.setVmId(vm.getId());

      long deadline = Parameters.getDeadline();
    }
  }
  /** Print out the clustering information. */
  private void printOut() {
    Collection sets = mTask2TaskSet.values();
    for (Iterator it = sets.iterator(); it.hasNext(); ) {
      TaskSet set = (TaskSet) it.next();
      if (!set.hasChecked) {
        set.hasChecked = true;

        Log.printLine("Job");
        for (Task task : set.getTaskList()) {
          Log.printLine(
              "Task "
                  + task.getCloudletId()
                  + " "
                  + task.getImpact()
                  + " "
                  + task.getCloudletLength());
        }
      }
    }
    // within each method
    cleanTaskSetChecked();
  }
 /**
  * Check whether a task is an ancessor of another set
  *
  * @param ancessor ancessor
  * @param set child
  * @return
  */
 private boolean check(Task ancessor, Task set) {
   if (ancessor == null || set == null) {
     return false;
   }
   if (ancessor == set) {
     return true;
   }
   for (Iterator it = set.getParentList().iterator(); it.hasNext(); ) {
     Task parent = (Task) it.next();
     if (check(ancessor, parent)) {
       return true;
     } else {
       // parent.hasChecked = true;
     }
   }
   return false;
 }
  /** Add pairs that needs to remove to mRecover. */
  private void remove() {

    for (Task set : this.getTaskList()) {
      if (set.getChildList().size() >= 2) {
        for (int i = 0; i < set.getChildList().size(); i++) {
          Task children = (Task) set.getChildList().get(i);
          for (int j = i + 1; j < set.getChildList().size(); j++) {
            Task another = (Task) set.getChildList().get(j);
            // avoid unnecessary checks
            if (children.getDepth() > another.getDepth()) {
              if (check(another, children)) {
                // remove i
                set.getChildList().remove(children);
                children.getParentList().remove(set);
                i--;
                mRecover.put(set, children);
                // cleanTaskSetChecked();
                break;
              } else {
                // cleanTaskSetChecked();
              }
            }
            if (another.getDepth() > children.getDepth()) {
              if (check(children, another)) {
                set.getChildList().remove(another);
                another.getParentList().remove(set);
                i--;
                mRecover.put(set, another);
                // cleanTaskSetChecked();
                break;
              } else {
                // cleanTaskSetChecked();
              }
            }
          }
        }
      }
    }
  }
  /** The main function */
  @Override
  public void run() {

    double[][] bandwidths = Parameters.getBandwidths();
    int vmNum = getVmList().size();
    int taskNum = getTaskList().size();
    double[] availableTime = new double[vmNum];
    // cloudlet id starts from 1
    double[][] earliestStartTime = new double[taskNum + 1][vmNum];
    double[][] earliestFinishTime = new double[taskNum + 1][vmNum];
    int[] allocation = new int[taskNum + 1];

    List<Task> taskList = new ArrayList<Task>(getTaskList());
    List<Task> readyList = new ArrayList<Task>();
    while (!taskList.isEmpty()) {
      readyList.clear();
      for (Task task : taskList) {
        boolean ready = true;
        for (Task parent : task.getParentList()) {
          if (taskList.contains(parent)) {
            ready = false;
            break;
          }
        }
        if (ready) {
          readyList.add(task);
        }
      }
      taskList.removeAll(readyList);
      // schedule readylist
      for (Task task : readyList) {
        long[] fileSizes = new long[task.getParentList().size()];
        int parentIndex = 0;
        for (Task parent : task.getParentList()) {
          long fileSize = 0;
          for (Iterator fileIter = task.getFileList().iterator(); fileIter.hasNext(); ) {
            File file = (File) fileIter.next();
            if (file.getType() == 1) {
              for (Iterator fileIter2 = parent.getFileList().iterator(); fileIter2.hasNext(); ) {
                File file2 = (File) fileIter2.next();
                if (file2.getType() == 2 && file2.getName().equals(file.getName())) {
                  fileSize += file.getSize();
                }
              }
            }
          }
          fileSizes[parentIndex] = fileSize;
          parentIndex++;
        }

        double minTime = Double.MAX_VALUE;
        int minTimeIndex = 0;

        for (int vmIndex = 0; vmIndex < getVmList().size(); vmIndex++) {
          Vm vm = (Vm) getVmList().get(vmIndex);
          double startTime = availableTime[vm.getId()];
          parentIndex = 0;
          for (Task parent : task.getParentList()) {
            int allocatedVmId = allocation[parent.getCloudletId()];
            double actualFinishTime = earliestFinishTime[parent.getCloudletId()][allocatedVmId];
            double communicationTime =
                fileSizes[parentIndex] / bandwidths[allocatedVmId][vm.getId()];

            if (actualFinishTime + communicationTime > startTime) {
              startTime = actualFinishTime + communicationTime;
            }
            parentIndex++;
          }
          earliestStartTime[task.getCloudletId()][vm.getId()] = startTime;
          double runtime = task.getCloudletLength() / vm.getMips();
          earliestFinishTime[task.getCloudletId()][vm.getId()] = runtime + startTime;

          if (runtime + startTime < minTime) {
            minTime = runtime + startTime;
            minTimeIndex = vmIndex;
          }
        }

        allocation[task.getCloudletId()] =
            minTimeIndex; // we do not really need it use task.getVmId
        task.setVmId(minTimeIndex);
        availableTime[minTimeIndex] = minTime;
      }
    }
  }
  /**
   * Prints the job objects
   *
   * @param list list of jobs
   */
  protected static void printJobList(List<Job> list) {
    String indent = "    ";
    Log.printLine();
    Log.printLine("========== OUTPUT ==========");
    Log.printLine(
        "Job ID"
            + indent
            + "Task ID"
            + indent
            + "STATUS"
            + indent
            + "Data center ID"
            + indent
            + "VM ID"
            + indent
            + indent
            + "Time"
            + indent
            + "Start Time"
            + indent
            + "Finish Time"
            + indent
            + "Depth");
    DecimalFormat dft = new DecimalFormat("###.##");
    for (Job job : list) {
      Log.print(indent + job.getCloudletId() + indent + indent);
      if (job.getClassType() == Parameters.ClassType.STAGE_IN.value) {
        Log.print("Stage-in");
      }
      for (Task task : job.getTaskList()) {
        Log.print(task.getCloudletId() + ",");
      }
      Log.print(indent);

      if (job.getCloudletStatus() == Cloudlet.SUCCESS) {
        Log.print("SUCCESS");
        Log.printLine(
            indent
                + indent
                + job.getResourceId()
                + indent
                + indent
                + indent
                + job.getVmId()
                + indent
                + indent
                + indent
                + dft.format(job.getActualCPUTime())
                + indent
                + indent
                + dft.format(job.getExecStartTime())
                + indent
                + indent
                + indent
                + dft.format(job.getFinishTime())
                + indent
                + indent
                + indent
                + job.getDepth());
      } else if (job.getCloudletStatus() == Cloudlet.FAILED) {
        Log.print("FAILED");
        Log.printLine(
            indent
                + indent
                + job.getResourceId()
                + indent
                + indent
                + indent
                + job.getVmId()
                + indent
                + indent
                + indent
                + dft.format(job.getActualCPUTime())
                + indent
                + indent
                + dft.format(job.getExecStartTime())
                + indent
                + indent
                + indent
                + dft.format(job.getFinishTime())
                + indent
                + indent
                + indent
                + job.getDepth());
      }
    }
  }