///|
pub fn is_finite(value : Double) -> Bool {
  !value.is_nan() && !value.is_inf()
}

///|
pub fn require_finite(
  name : String,
  value : Double,
) -> Unit raise ExtractionError {
  if !is_finite(value) {
    raise ExtractionError::InvalidArgument(
      message="\{name} must be finite, got \{value}",
    )
  }
}

///|
pub fn require_fraction(
  name : String,
  value : Double,
) -> Unit raise ExtractionError {
  require_finite(name, value)
  if value < 0.0 || value > 1.0 {
    raise ExtractionError::InvalidFraction(name~, value~)
  }
}

///|
pub fn require_positive(
  name : String,
  value : Double,
) -> Unit raise ExtractionError {
  require_finite(name, value)
  if value <= 0.0 {
    raise ExtractionError::InvalidFlow(name~, value~)
  }
}

///|
pub fn require_non_negative(
  name : String,
  value : Double,
) -> Unit raise ExtractionError {
  require_finite(name, value)
  if value < 0.0 {
    raise ExtractionError::InvalidCoefficient(name~, value~)
  }
}

///|
pub fn approximately_equal(
  left : Double,
  right : Double,
  tolerance? : Double = 1.0e-9,
) -> Bool {
  (left - right).abs() <= tolerance
}