@Override
  public RLESparseResourceAllocation availableResources(
      RLESparseResourceAllocation available,
      Plan plan,
      String user,
      ReservationId oldId,
      long start,
      long end)
      throws PlanningException {

    // this only propagates the instantaneous maxInst properties, while
    // the time-varying one depends on the current allocation as well
    // and are not easily captured here
    Resource planTotalCapacity = plan.getTotalCapacity();
    Resource maxInsRes = Resources.multiply(planTotalCapacity, maxInst);
    NavigableMap<Long, Resource> instQuota = new TreeMap<Long, Resource>();
    instQuota.put(start, maxInsRes);

    RLESparseResourceAllocation instRLEQuota =
        new RLESparseResourceAllocation(instQuota, plan.getResourceCalculator());

    RLESparseResourceAllocation used = plan.getConsumptionForUserOverTime(user, start, end);

    instRLEQuota =
        RLESparseResourceAllocation.merge(
            plan.getResourceCalculator(),
            planTotalCapacity,
            instRLEQuota,
            used,
            RLEOperator.subtract,
            start,
            end);

    instRLEQuota =
        RLESparseResourceAllocation.merge(
            plan.getResourceCalculator(),
            planTotalCapacity,
            available,
            instRLEQuota,
            RLEOperator.min,
            start,
            end);

    return instRLEQuota;
  }
  @Override
  public void validate(Plan plan, ReservationAllocation reservation) throws PlanningException {

    // this is entire method invoked under a write-lock on the plan, no need
    // to synchronize accesses to the plan further

    // Try to verify whether there is already a reservation with this ID in
    // the system (remove its contribution during validation to simulate a
    // try-n-swap
    // update).
    ReservationAllocation oldReservation = plan.getReservationById(reservation.getReservationId());

    // sanity check that the update of a reservation is not changing username
    if (oldReservation != null && !oldReservation.getUser().equals(reservation.getUser())) {
      throw new MismatchedUserException(
          "Updating an existing reservation with mismatched user:"******" != "
              + reservation.getUser());
    }

    long startTime = reservation.getStartTime();
    long endTime = reservation.getEndTime();
    long step = plan.getStep();

    Resource planTotalCapacity = plan.getTotalCapacity();

    Resource maxAvgRes = Resources.multiply(planTotalCapacity, maxAvg);
    Resource maxInsRes = Resources.multiply(planTotalCapacity, maxInst);

    // define variable that will store integral of resources (need diff class to
    // avoid overflow issues for long/large allocations)
    IntegralResource runningTot = new IntegralResource(0L, 0L);
    IntegralResource maxAllowed = new IntegralResource(maxAvgRes);
    maxAllowed.multiplyBy(validWindow / step);

    RLESparseResourceAllocation userCons =
        plan.getConsumptionForUserOverTime(
            reservation.getUser(), startTime - validWindow, endTime + validWindow);

    // check that the resources offered to the user during any window of length
    // "validWindow" overlapping this allocation are within maxAllowed
    // also enforce instantaneous and physical constraints during this pass
    for (long t = startTime - validWindow; t < endTime + validWindow; t += step) {

      Resource currExistingAllocTot = plan.getTotalCommittedResources(t);
      Resource currExistingAllocForUser = userCons.getCapacityAtTime(t);
      Resource currNewAlloc = reservation.getResourcesAtTime(t);
      Resource currOldAlloc = Resources.none();
      if (oldReservation != null) {
        currOldAlloc = oldReservation.getResourcesAtTime(t);
      }

      // throw exception if the cluster is overcommitted
      // tot_allocated - old + new > capacity
      Resource inst =
          Resources.subtract(Resources.add(currExistingAllocTot, currNewAlloc), currOldAlloc);
      if (Resources.greaterThan(
          plan.getResourceCalculator(), planTotalCapacity, inst, planTotalCapacity)) {
        throw new ResourceOverCommitException(
            " Resources at time "
                + t
                + " would be overcommitted ("
                + inst
                + " over "
                + plan.getTotalCapacity()
                + ") by accepting reservation: "
                + reservation.getReservationId());
      }

      // throw exception if instantaneous limits are violated
      // tot_alloc_to_this_user - old + new > inst_limit
      if (Resources.greaterThan(
          plan.getResourceCalculator(),
          planTotalCapacity,
          Resources.subtract(Resources.add(currExistingAllocForUser, currNewAlloc), currOldAlloc),
          maxInsRes)) {
        throw new PlanningQuotaException(
            "Instantaneous quota capacity "
                + maxInst
                + " would be passed at time "
                + t
                + " by accepting reservation: "
                + reservation.getReservationId());
      }

      // throw exception if the running integral of utilization over validWindow
      // is violated. We perform a delta check, adding/removing instants at the
      // boundary of the window from runningTot.

      // runningTot = previous_runningTot + currExistingAllocForUser +
      // currNewAlloc - currOldAlloc - pastNewAlloc - pastOldAlloc;

      // Where:
      // 1) currNewAlloc, currExistingAllocForUser represent the contribution of
      // the instant in time added in this pass.
      // 2) pastNewAlloc, pastOldAlloc are the contributions relative to time
      // instants that are being retired from the the window
      // 3) currOldAlloc is the contribution (if any) of the previous version of
      // this reservation (the one we are updating)

      runningTot.add(currExistingAllocForUser);
      runningTot.add(currNewAlloc);
      runningTot.subtract(currOldAlloc);

      // expire contributions from instant in time before (t - validWindow)
      if (t > startTime) {
        Resource pastOldAlloc = userCons.getCapacityAtTime(t - validWindow);
        Resource pastNewAlloc = reservation.getResourcesAtTime(t - validWindow);

        // runningTot = runningTot - pastExistingAlloc - pastNewAlloc;
        runningTot.subtract(pastOldAlloc);
        runningTot.subtract(pastNewAlloc);
      }

      // check integral
      // runningTot > maxAvg * validWindow
      // NOTE: we need to use comparator of IntegralResource directly, as
      // Resource and ResourceCalculator assume "int" amount of resources,
      // which is not sufficient when comparing integrals (out-of-bound)
      if (maxAllowed.compareTo(runningTot) < 0) {
        throw new PlanningQuotaException(
            "Integral (avg over time) quota capacity "
                + maxAvg
                + " over a window of "
                + validWindow / 1000
                + " seconds, "
                + " would be passed at time "
                + t
                + "("
                + new Date(t)
                + ") by accepting reservation: "
                + reservation.getReservationId());
      }
    }
  }