Skip to main content

score_set/
value.rs

1use crate::float::Float;
2use witnessed::{WitnessExt, Witnessed};
3
4// ---------------------------------------------------------------------------
5// Value01 — value in [0, 1], finite
6// ---------------------------------------------------------------------------
7
8/// Witness credential for a value validated to be finite and in `[0, 1]`.
9///
10/// ```ignore
11/// let v: Witnessed<f64, Value01> = Value01::witness(0.5)?;
12/// ```
13pub struct Value01;
14
15impl Value01 {
16    /// Validate `v` and return a `Witnessed` credential.
17    pub fn witness<T: Float>(v: T) -> Result<Witnessed<T, Self>, &'static str> {
18        if !v.is_finite() {
19            return Err("Value01: value must be finite");
20        }
21        if v < T::zero() || v > T::one() {
22            return Err("Value01: value must be in [0, 1]");
23        }
24        v.witness().by(|_| Ok(Value01))
25    }
26}
27
28// ---------------------------------------------------------------------------
29// GtZero — strictly greater than zero, finite
30// ---------------------------------------------------------------------------
31
32/// Witness credential for a value validated to be finite and strictly > 0.
33pub struct GtZero;
34
35impl GtZero {
36    /// Validate `v` and return a `Witnessed` credential.
37    pub fn witness<T: Float>(v: T) -> Result<Witnessed<T, Self>, &'static str> {
38        if !v.is_finite() {
39            return Err("GtZero: value must be finite");
40        }
41        if v <= T::zero() {
42            return Err("GtZero: value must be strictly positive");
43        }
44        v.witness().by(|_| Ok(GtZero))
45    }
46}
47
48// ---------------------------------------------------------------------------
49// NormalizedWeight — weight credential
50// ---------------------------------------------------------------------------
51
52/// Witness credential for a single normalized weight.
53///
54/// Created via [`NormalizedWeight::from_normalized_container`], which
55/// binary-searches a validated sorted container to confirm membership.
56///
57/// ```ignore
58/// let set = NormalizedContainer::witness(weights)?;
59/// let w = 0.3_f64.witness().by(
60///     |v| NormalizedWeight::from_normalized_container(*v, &set)
61/// )?;
62/// ```
63pub struct NormalizedWeight;
64
65impl NormalizedWeight {
66    /// Verify `value` is a member of a validated normalized set.
67    ///
68    /// Binary-searches the sorted `container`.
69    pub fn from_normalized_container<T: Float>(
70        value: T,
71        container: &Witnessed<Vec<T>, NormalizedContainer>,
72    ) -> Result<Self, &'static str> {
73        if container
74            .binary_search_by(|a| a.partial_cmp(&value).unwrap_or(core::cmp::Ordering::Equal))
75            .is_ok()
76        {
77            Ok(NormalizedWeight)
78        } else {
79            Err("NormalizedWeight: value not found in validated set")
80        }
81    }
82}
83
84// ---------------------------------------------------------------------------
85// NormalizedContainer — validated set of normalized weights
86// ---------------------------------------------------------------------------
87
88/// Witness credential for a container validated as a complete set of
89/// normalized weights (each in `[0, 1]`, sum to 1).
90///
91/// ```ignore
92/// let set = NormalizedContainer::witness(vec![0.2, 0.3, 0.5])?;
93/// ```
94pub struct NormalizedContainer;
95
96impl NormalizedContainer {
97    /// Validate every value is finite, in `[0, 1]`, and the set sums to 1.
98    pub fn witness<T: Float>(weights: Vec<T>) -> Result<Witnessed<Vec<T>, Self>, &'static str> {
99        let mut sum = T::zero();
100        let len = weights.len();
101        for &w in &weights {
102            if !w.is_finite() {
103                return Err("NormalizedContainer: value must be finite");
104            }
105            if w < T::zero() || w > T::one() {
106                return Err("NormalizedContainer: value must be in [0, 1]");
107            }
108            sum = sum + w;
109        }
110        let diff = if sum > T::one() {
111            sum - T::one()
112        } else {
113            T::one() - sum
114        };
115        let tol = T::from_f64(1e-9) * T::from_f64(len as f64).max(T::one());
116        if diff > tol {
117            return Err("NormalizedContainer: set must sum to 1");
118        }
119        weights.witness().by(|_| Ok(NormalizedContainer))
120    }
121}
122
123#[cfg(test)]
124mod tests_for_value;