///|
/// 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}")
}