class KeyedBudgetManager {

	AcquisitionResult<K> acquirePagedBudgetForKeys(Iterable<K> keys, long numberOfPages, long pageSize) {
		Preconditions.checkNotNull(keys);
		Preconditions.checkArgument(numberOfPages >= 0L, "The requested number of pages has to be positive");
		Preconditions.checkArgument(pageSize > 0L, "The page size has to be greater than zero");

		synchronized (lock) {
			long leftPagesToReserve = numberOfPages;
			Map<K, Long> pagesToReserveByKey = new HashMap<>();
			for (K key : keys) {
				long availableBudgetOfCurrentKey = availableBudgetByKey.getOrDefault(key, 0L);
				long availablePagesOfCurrentKey = availableBudgetOfCurrentKey / pageSize;
				if (leftPagesToReserve <= availablePagesOfCurrentKey) {
					pagesToReserveByKey.put(key, leftPagesToReserve);
					leftPagesToReserve = 0L;
					break;
				} else if (availablePagesOfCurrentKey > 0L) {
					pagesToReserveByKey.put(key, availablePagesOfCurrentKey);
					leftPagesToReserve -= availablePagesOfCurrentKey;
				}
			}
			boolean possibleToAcquire = leftPagesToReserve == 0L;
			if (possibleToAcquire) {
				for (Entry<K, Long> pagesToReserveForKey : pagesToReserveByKey.entrySet()) {
					//noinspection ConstantConditions
					availableBudgetByKey.compute(
						pagesToReserveForKey.getKey(),
						(k, v) -> v - (pagesToReserveForKey.getValue() * pageSize));
				}
			}
			return possibleToAcquire ?
				AcquisitionResult.success(pagesToReserveByKey) : AcquisitionResult.failure(numberOfPages - leftPagesToReserve);
		}
	}

	public  KeyedBudgetManager(Map<K, Long> maxBudgetByKey, long defaultPageSize);

}