///|
/// A feasible lower endpoint of the maximum admitted offered arrival rate,
/// bounded by the caller's upper rate and a strict utilization ceiling.
pub(all) struct ArrivalCapacity {
  arrival : Double
  probability_within_threshold : Double
  utilization : Double
  upper_bounded : Bool
} derive(Debug, ToJson)

///|
pub fn arrival_capacity(
  service : Double,
  server_count : Int,
  target_probability : Double,
  wait_threshold : Double,
  upper : Double,
  maximum_utilization? : Double = 0.95,
) -> ArrivalCapacity raise QueueError {
  rate(service, "service")
  positive(service, "service")
  servers(server_count)
  probability(target_probability, "SLA probability")
  nonnegative(wait_threshold, "wait threshold")
  rate(upper, "arrival upper bound")
  probability(maximum_utilization, "maximum utilization")
  if maximum_utilization >= 1.0 {
    raise Invalid("capacity utilization must be below one")
  }
  let ceiling = upper.min(
    service * server_count.to_double() * maximum_utilization,
  )
  let within = fn(arrival : Double) -> Double raise QueueError {
    let q : Mmc = { arrival, service, servers: server_count, }
    1.0 - q.wait_survival(wait_threshold)
  }
  let mut lo = 0.0
  let mut hi = ceiling
  if within(ceiling) >= target_probability {
    lo = ceiling
  } else {
    for _ in 0..<100 {
      let mid = lo + (hi - lo) * 0.5
      if within(mid) >= target_probability {
        lo = mid
      } else {
        hi = mid
      }
    }
  }
  {
    arrival: lo,
    probability_within_threshold: within(lo),
    utilization: lo / service / server_count.to_double(),
    upper_bounded: lo == upper,
  }
}

///|
pub extend ArrivalCapacity with Debug::{to_repr}

///|
pub extend ArrivalCapacity with ToJson::{to_json}