1use alloc::vec::Vec;
2use core::ops::{Add, Mul, Sub};
3use witnessed::{WitnessExt, Witnessed};
4
5pub trait ScoreOps:
14 Copy + PartialOrd + PartialEq + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self>
15{
16 fn is_finite(self) -> bool;
17 fn from_f64(v: f64) -> Self;
18}
19
20impl ScoreOps for f32 {
21 #[inline]
22 fn is_finite(self) -> bool {
23 f32::is_finite(self)
24 }
25 #[inline]
26 fn from_f64(v: f64) -> Self {
27 v as f32
28 }
29}
30
31impl ScoreOps for f64 {
32 #[inline]
33 fn is_finite(self) -> bool {
34 f64::is_finite(self)
35 }
36 #[inline]
37 fn from_f64(v: f64) -> Self {
38 v
39 }
40}
41
42pub struct Value01;
48
49impl Value01 {
50 #[inline]
52 pub fn witness<T: ScoreOps>(v: T) -> Result<Witnessed<T, Self>, &'static str> {
53 if !v.is_finite() {
54 return Err("Value01: value must be finite");
55 }
56 let zero = T::from_f64(0.0);
57 let one = T::from_f64(1.0);
58 if v < zero || v > one {
59 return Err("Value01: value must be in [0, 1]");
60 }
61 v.witness().by(|_| Ok(Value01))
62 }
63}
64
65pub struct GtZero;
71
72impl GtZero {
73 #[inline]
75 pub fn witness<T: ScoreOps>(v: T) -> Result<Witnessed<T, Self>, &'static str> {
76 if !v.is_finite() {
77 return Err("GtZero: value must be finite");
78 }
79 let zero = T::from_f64(0.0);
80 if v <= zero {
81 return Err("GtZero: value must be strictly positive");
82 }
83 v.witness().by(|_| Ok(GtZero))
84 }
85}
86
87pub struct NormalizedWeight;
96
97impl NormalizedWeight {
98 #[inline]
102 pub fn from_normalized_container<T: ScoreOps>(
103 value: T,
104 container: &Witnessed<Vec<T>, NormalizedContainer>,
105 ) -> Result<Self, &'static str> {
106 if container
107 .binary_search_by(|a| a.partial_cmp(&value).unwrap_or(core::cmp::Ordering::Equal))
108 .is_ok()
109 {
110 Ok(NormalizedWeight)
111 } else {
112 Err("NormalizedWeight: value not found in validated set")
113 }
114 }
115}
116
117pub struct NormalizedContainer;
124
125impl NormalizedContainer {
126 #[inline]
128 pub fn witness<T: ScoreOps>(weights: Vec<T>) -> Result<Witnessed<Vec<T>, Self>, &'static str> {
129 let zero = T::from_f64(0.0);
130 let one = T::from_f64(1.0);
131 let mut sum = zero;
132 let len = weights.len();
133 for &w in &weights {
134 if !w.is_finite() {
135 return Err("NormalizedContainer: value must be finite");
136 }
137 if w < zero || w > one {
138 return Err("NormalizedContainer: value must be in [0, 1]");
139 }
140 sum = sum + w;
141 }
142 let diff = if sum > one { sum - one } else { one - sum };
143 let len_f = T::from_f64(len as f64);
144 let scale = if len_f > one { len_f } else { one };
145 let tol = T::from_f64(1e-9) * scale;
146 if diff > tol {
147 return Err("NormalizedContainer: set must sum to 1");
148 }
149 weights.witness().by(|_| Ok(NormalizedContainer))
150 }
151}
152
153#[cfg(test)]
154mod tests_for_value;