/**
   * Updates the processing of cloudlets running under management of this scheduler.
   *
   * @param currentTime current simulation time
   * @param mipsShare array with MIPS share of each processor available to the scheduler
   * @return time predicted completion time of the earliest finishing cloudlet, or 0 if there is no
   *     next events
   * @pre currentTime >= 0
   * @post $none
   */
  @Override
  public double updateVmProcessing(double currentTime, List<Double> mipsShare) {
    setCurrentMipsShare(mipsShare);
    double timeSpam = currentTime - getPreviousTime(); // time since last update
    double capacity = 0.0;
    int cpus = 0;

    for (Double mips : mipsShare) { // count the CPUs available to the VMM
      capacity += mips;
      if (mips > 0) {
        cpus++;
      }
    }
    currentCpus = cpus;
    capacity /= cpus; // average capacity of each cpu

    // each machine in the exec list has the same amount of cpu
    for (ResCloudlet rcl : getCloudletExecList()) {
      rcl.updateCloudletFinishedSoFar(
          (long) (capacity * timeSpam * rcl.getNumberOfPes() * 1000000));
    }

    // no more cloudlets in this scheduler
    if (getCloudletExecList().size() == 0 && getCloudletWaitingList().size() == 0) {
      setPreviousTime(currentTime);
      return 0.0;
    }

    // update each cloudlet
    int finished = 0;
    List<ResCloudlet> toRemove = new ArrayList<ResCloudlet>();
    for (ResCloudlet rcl : getCloudletExecList()) {
      // finished anyway, rounding issue...
      if (rcl.getRemainingCloudletLength() == 0) {
        toRemove.add(rcl);
        cloudletFinish(rcl);
        finished++;
      }
    }
    getCloudletExecList().removeAll(toRemove);

    // for each finished cloudlet, add a new one from the waiting list
    if (!getCloudletWaitingList().isEmpty()) {
      for (int i = 0; i < finished; i++) {
        toRemove.clear();
        for (ResCloudlet rcl : getCloudletWaitingList()) {
          if ((currentCpus - usedPes) >= rcl.getNumberOfPes()) {
            rcl.setCloudletStatus(Cloudlet.INEXEC);
            for (int k = 0; k < rcl.getNumberOfPes(); k++) {
              rcl.setMachineAndPeId(0, i);
            }
            getCloudletExecList().add(rcl);
            usedPes += rcl.getNumberOfPes();
            toRemove.add(rcl);
            break;
          }
        }
        getCloudletWaitingList().removeAll(toRemove);
      }
    }

    // estimate finish time of cloudlets in the execution queue
    double nextEvent = Double.MAX_VALUE;
    for (ResCloudlet rcl : getCloudletExecList()) {
      double remainingLength = rcl.getRemainingCloudletLength();
      double estimatedFinishTime =
          currentTime + (remainingLength / (capacity * rcl.getNumberOfPes()));
      if (estimatedFinishTime - currentTime < 0.1) {
        estimatedFinishTime = currentTime + 0.1;
      }
      if (estimatedFinishTime < nextEvent) {
        nextEvent = estimatedFinishTime;
      }
    }
    setPreviousTime(currentTime);
    return nextEvent;
  }