///|
/// Finite queue sizing result; capacity = servers + waiting_places. Targets
/// constrain attempted-customer blocking and admitted-customer abandonment.
pub(all) struct FiniteStaffing {
servers : Int
capacity : Int
metrics : FiniteMetrics
outcomes : FiniteOutcomes
} derive(Debug, ToJson)
///|
pub fn finite_staffing(
arrival : Double,
service : Double,
waiting_places : Int,
abandonment : Double,
maximum_blocking : Double,
maximum_abandonment : Double,
maximum_servers : Int,
) -> FiniteStaffing raise QueueError {
rate(arrival, "staffing arrival")
rate(service, "staffing service")
positive(service, "staffing service")
rate(abandonment, "staffing abandonment")
probability(maximum_blocking, "blocking target")
probability(maximum_abandonment, "abandonment target")
if waiting_places < 0 ||
maximum_servers < 1 ||
maximum_servers > 10000 ||
waiting_places > 10000 - maximum_servers {
raise Limit("staffing capacity must be at most 10000")
}
if maximum_servers.to_double() *
(waiting_places + maximum_servers).to_double() >
2.0e6 {
raise Limit("finite staffing exceeds two million state evaluations")
}
for count in 1..<=maximum_servers {
let q : FiniteQueue = {
arrival,
service,
servers: count,
capacity: count + waiting_places,
abandonment,
}
let metrics = q.analyze()
let outcomes = q.outcomes()
if metrics.blocked_fraction <= maximum_blocking &&
outcomes.abandoned_given_admission <= maximum_abandonment {
return { servers: count, capacity: q.capacity, metrics, outcomes, }
}
}
raise Infeasible("finite queue targets not met within server bound")
}
///|
pub extend FiniteStaffing with Debug::{to_repr}
///|
pub extend FiniteStaffing with ToJson::{to_json}