use alloc::vec::Vec;
use core::ops::{Add, Mul, Sub};
use witnessed::{WitnessExt, Witnessed};
pub(crate) trait ScoreOps:
Copy + PartialOrd + PartialEq + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self>
{
fn is_finite(self) -> bool;
fn from_f64(v: f64) -> Self;
}
impl ScoreOps for f32 {
#[inline]
fn is_finite(self) -> bool {
f32::is_finite(self)
}
#[inline]
fn from_f64(v: f64) -> Self {
v as f32
}
}
impl ScoreOps for f64 {
#[inline]
fn is_finite(self) -> bool {
f64::is_finite(self)
}
#[inline]
fn from_f64(v: f64) -> Self {
v
}
}
pub struct Value01;
impl Value01 {
#[inline]
#[allow(private_bounds)]
pub fn witness<T: ScoreOps>(v: T) -> Result<Witnessed<T, Self>, &'static str> {
if !v.is_finite() {
return Err("Value01: value must be finite");
}
let zero = T::from_f64(0.0);
let one = T::from_f64(1.0);
if v < zero || v > one {
return Err("Value01: value must be in [0, 1]");
}
v.witness().by(|_| Ok(Value01))
}
}
pub struct GtZero;
impl GtZero {
#[inline]
#[allow(private_bounds)]
pub fn witness<T: ScoreOps>(v: T) -> Result<Witnessed<T, Self>, &'static str> {
if !v.is_finite() {
return Err("GtZero: value must be finite");
}
let zero = T::from_f64(0.0);
if v <= zero {
return Err("GtZero: value must be strictly positive");
}
v.witness().by(|_| Ok(GtZero))
}
}
pub struct NormalizedWeight;
impl NormalizedWeight {
#[inline]
#[allow(private_bounds)]
pub fn from_normalized_container<T: ScoreOps>(
value: T,
container: &Witnessed<Vec<T>, NormalizedContainer>,
) -> Result<Self, &'static str> {
if container
.binary_search_by(|a| a.partial_cmp(&value).unwrap_or(core::cmp::Ordering::Equal))
.is_ok()
{
Ok(NormalizedWeight)
} else {
Err("NormalizedWeight: value not found in validated set")
}
}
}
pub struct NormalizedContainer;
impl NormalizedContainer {
#[inline]
#[allow(private_bounds)]
pub fn witness<T: ScoreOps>(weights: Vec<T>) -> Result<Witnessed<Vec<T>, Self>, &'static str> {
let zero = T::from_f64(0.0);
let one = T::from_f64(1.0);
let mut sum = zero;
let len = weights.len();
for &w in &weights {
if !w.is_finite() {
return Err("NormalizedContainer: value must be finite");
}
if w < zero || w > one {
return Err("NormalizedContainer: value must be in [0, 1]");
}
sum = sum + w;
}
let diff = if sum > one { sum - one } else { one - sum };
let len_f = T::from_f64(len as f64);
let scale = if len_f > one { len_f } else { one };
let tol = T::from_f64(1e-9) * scale;
if diff > tol {
return Err("NormalizedContainer: set must sum to 1");
}
weights.witness().by(|_| Ok(NormalizedContainer))
}
}
#[cfg(test)]
mod tests_for_value;