///|
/// Check whether a device can reach and measure a feature.
pub fn Device::can_measure(self : Device, feature : Feature) -> Bool {
  let mut supported = false
  for kind in self.supported_kinds {
    if kind == feature.kind {
      supported = true
    }
  }
  self.reach >= feature.nominal.abs() && supported
}

///|
/// Select a repeat count from the requested confidence and device accuracy.
pub fn repeat_count(feature : Feature, device : Device) -> Int {
  let tolerance = (feature.upper - feature.lower).abs() / 2.0
  if tolerance <= device.accuracy {
    5
  } else if tolerance <= device.accuracy * 3.0 {
    3
  } else {
    1
  }
}

///|
/// Estimate conservative uncertainty for a strategy.
pub fn estimate_uncertainty(strategy : Strategy, device : Device) -> Double {
  match strategy {
    SinglePoint => device.accuracy
    RepeatedPoint(count~) => device.accuracy / count.to_double().sqrt()
    Scan(count~) => device.accuracy / count.to_double().sqrt() * 0.8
  }
}

///|
/// Check if a device can resolve the feature tolerance.
pub fn capability_ok(feature : Feature, uncertainty : Double) -> Bool {
  let tolerance = (feature.upper - feature.lower).abs() / 2.0
  uncertainty <= tolerance
}

///|
/// Classify a measurement against bilateral limits.
pub fn classify(feature : Feature, observation : Observation?) -> Verdict {
  match observation {
    None => Unmeasured
    Some(observation) =>
      if observation.feature_id != feature.id {
        Fail(reason="feature id mismatch")
      } else if observation.value >= feature.nominal + feature.lower &&
        observation.value <= feature.nominal + feature.upper {
        Pass
      } else {
        Fail(reason="value outside tolerance")
      }
  }
}