///|
/// Return a stable strategy label.
pub fn strategy_name(strategy : Strategy) -> String {
match strategy {
SinglePoint => "single"
RepeatedPoint(_) => "repeated"
Scan(_) => "scan"
}
}
///|
/// Return the sample count represented by a strategy.
pub fn strategy_sample_count(strategy : Strategy) -> Int {
match strategy {
SinglePoint => 1
RepeatedPoint(count~) => count
Scan(count~) => count
}
}
///|
/// Return whether a strategy uses more than one sample.
pub fn strategy_is_repeated(strategy : Strategy) -> Bool {
strategy_sample_count(strategy) > 1
}
///|
/// Return whether a strategy is a scan.
pub fn strategy_is_scan(strategy : Strategy) -> Bool {
match strategy {
Scan(_) => true
_ => false
}
}
///|
/// Return a simple time cost for a strategy.
pub fn strategy_cost(strategy : Strategy) -> Double {
match strategy {
SinglePoint => 1.0
RepeatedPoint(count~) => count.to_double() * 1.5
Scan(count~) => count.to_double() * 2.0
}
}
///|
/// Return a conservative risk for a strategy.
pub fn strategy_risk(strategy : Strategy) -> RiskLevel {
match strategy {
SinglePoint => RiskLevel::High
RepeatedPoint(_) => RiskLevel::Medium
Scan(_) => RiskLevel::Low
}
}
///|
/// Return a stable verdict code.
pub fn verdict_code(verdict : Verdict) -> String {
match verdict {
Pass => "PASS"
Fail(_) => "FAIL"
Unmeasured => "UNMEASURED"
}
}
///|
/// Return whether a verdict is a hard failure.
pub fn verdict_is_failure(verdict : Verdict) -> Bool {
verdict is Verdict::Fail(_)
}
///|
/// Return whether a verdict passed.
pub fn verdict_is_pass(verdict : Verdict) -> Bool {
verdict is Verdict::Pass
}
///|
/// Return a stable verdict explanation.
pub fn verdict_reason(verdict : Verdict) -> String {
match verdict {
Pass => "within tolerance"
Fail(reason~) => reason
Unmeasured => "no observation"
}
}
///|
/// Return whether a value is inside inclusive limits.
pub fn within_tolerance(feature : Feature, value : Double) -> Bool {
value >= lower_limit(feature) && value <= upper_limit(feature)
}
///|
/// Return whether tolerance limits are ordered and non-zero in width.
pub fn tolerance_is_valid(feature : Feature) -> Bool {
feature.lower <= feature.upper && tolerance_half_width(feature) > 0.0
}
///|
/// Return which side of a feature's limits contains a value.
pub fn limit_position(feature : Feature, value : Double) -> String {
if value < lower_limit(feature) {
"below"
} else if value > upper_limit(feature) {
"above"
} else if value == feature.nominal {
"center"
} else if value < feature.nominal {
"lower-half"
} else {
"upper-half"
}
}
///|
/// Return the normalized uncertainty ratio.
pub fn uncertainty_ratio(feature : Feature, uncertainty : Double) -> Double {
let tolerance = tolerance_half_width(feature)
if tolerance == 0.0 {
1.0
} else {
uncertainty.abs() / tolerance
}
}
///|
/// Recommend sample count using existing conservative planning rules.
pub fn recommended_samples(feature : Feature, accuracy : Double) -> Int {
repeat_count(feature, Device::{
id: "temporary",
accuracy,
reach: feature.nominal.abs() + 1.0,
supported_kinds: [feature.kind],
})
}
///|
/// Return a human-readable confidence band.
pub fn confidence_label(feature : Feature, uncertainty : Double) -> String {
let ratio = uncertainty_ratio(feature, uncertainty)
if ratio <= 0.25 {
"high"
} else if ratio <= 0.5 {
"medium"
} else {
"low"
}
}
///|
/// Resolve a strategy from a feature kind and requested sample count.
pub fn resolve_strategy(kind : FeatureKind, count : Int) -> Strategy {
if kind_requires_scan(kind) {
Scan(count=if count < 3 { 3 } else { count })
} else if count <= 1 {
SinglePoint
} else {
RepeatedPoint(count~)
}
}
///|
/// Return whether a strategy can support a feature's tolerance.
pub fn strategy_can_resolve(
feature : Feature,
strategy : Strategy,
device : Device,
) -> Bool {
capability_ok(feature, estimate_uncertainty(strategy, device))
}
///|
/// Return the better of two verdicts using fail > unmeasured > pass priority.
pub fn worse_verdict(left : Verdict, right : Verdict) -> Verdict {
if verdict_is_failure(left) {
left
} else if verdict_is_failure(right) {
right
} else if left is Verdict::Unmeasured {
left
} else if right is Verdict::Unmeasured {
right
} else {
Pass
}
}
///|
/// Reduce a collection of verdicts to its worst outcome.
pub fn worst_verdict(verdicts : Array[Verdict]) -> Verdict {
let mut result = Verdict::Pass
for verdict in verdicts {
result = worse_verdict(result, verdict)
}
result
}