///|
/// Domain, numerical range, resource, and unstable-model failures are distinct.
pub suberror QueueError {
  Invalid(String)
  Numerical(String)
  Limit(String)
  Unstable(String)
  Infeasible(String)
} derive(Debug)

///|
fn finite(value : Double, field : String) -> Unit raise QueueError {
  if value.is_nan() || value.is_inf() {
    raise Numerical("non-finite " + field)
  }
}

///|
fn nonnegative(value : Double, field : String) -> Unit raise QueueError {
  finite(value, field)
  if value < 0.0 {
    raise Invalid(field + " must be nonnegative")
  }
}

///|
fn positive(value : Double, field : String) -> Unit raise QueueError {
  finite(value, field)
  if value <= 0.0 {
    raise Invalid(field + " must be positive")
  }
}

///|
fn rate(value : Double, field : String) -> Unit raise QueueError {
  nonnegative(value, field)
  if value > 1.0e12 {
    raise Limit(field + " exceeds 1e12 per time unit")
  }
}

///|
fn servers(value : Int) -> Unit raise QueueError {
  if value < 1 || value > 10000 {
    raise Limit("server count must be 1..10000")
  }
}

///|
fn probability(value : Double, field : String) -> Unit raise QueueError {
  nonnegative(value, field)
  if value > 1.0 {
    raise Invalid(field + " must not exceed one")
  }
}

///|
fn checked_ratio(
  a : Double,
  b : Double,
  field : String,
) -> Double raise QueueError {
  let result = a / b
  finite(result, field)
  result
}