///|
/// Calls/minute and minutes: never mix these with per-hour arrival data.
/// Compare separate assumptions, not interchangeable customer outcomes.
fn main raise {
  let arrival = 12.0
  let service = 0.5
  let sla = @queue.sla_staffing(
    arrival,
    service,
    0.8,
    0.5,
    maximum_utilization=0.9,
    maximum_servers=64,
  )
  let sized : @queue.Mmc = { arrival, service, servers: sla.servers, }
  assert_true(1.0 - sized.wait_survival(0.5) >= 0.8 - 1.0e-12)
  let capacity = @queue.arrival_capacity(
    service,
    sla.servers,
    0.8,
    0.5,
    30.0,
    maximum_utilization=0.9,
  )
  assert_true(capacity.arrival >= arrival - 1.0e-10)
  let scenarios = @queue.mmc_sweep(service, sla.servers, [8.0, 12.0, 40.0], 0.5)
  assert_true(scenarios[0].stable)
  assert_true(!scenarios[2].stable)
  // Abandonment uses an exponential waiting-only hazard of 0.1/minute.
  // Size a different finite-buffer model: blocking <=1%, abandonment <=5%.
  let finite = @queue.finite_staffing(arrival, service, 8, 0.1, 0.01, 0.05, 64)
  assert_true(finite.metrics.blocked_fraction <= 0.01)
  assert_true(finite.outcomes.abandoned_given_admission <= 0.05)
  assert_true(finite.outcomes.relative_rate_residual < 1.0e-8)
  // Loss-only trunk planning admits no waiting and no retries.
  let channels = @queue.loss_staffing(arrival / service, 0.01, 100)
  assert_true(@queue.erlang_b(arrival / service, channels) <= 0.01)
  println("infinite_waiting_room_staffing=" + sla.to_json().stringify())
  println("same_SLA_arrival_capacity=" + capacity.to_json().stringify())
  println("finite_buffer_staffing=" + finite.to_json().stringify())
  println("loss_only_channels=\{channels}")
}