score_set/traits.rs
1use alloc::boxed::Box;
2
3use witnessed::Witnessed;
4
5/// Witness attached to values known to be valid for the normalized `[0, 1]`
6/// scoring boundary.
7pub struct V01;
8
9/// Error returned when a value cannot be proven to be in the normalized
10/// `[0, 1]` range.
11#[derive(Clone, Copy, Debug, Eq, PartialEq)]
12pub struct V01Error;
13
14/// Proves that an `f32` value is in the normalized `[0, 1]` range.
15///
16/// This function is suitable for use with [`witnessed::Witnessing::by`]:
17///
18/// ```
19/// use score_set::traits::{prove_v01_f32, V01};
20/// use witnessed::{WitnessExt, Witnessed};
21///
22/// let score: Witnessed<f32, V01> = 0.75_f32.witness().by(prove_v01_f32).unwrap();
23/// assert_eq!(*score, 0.75);
24/// ```
25///
26/// Values such as `NaN` are rejected because neither range comparison
27/// succeeds for them.
28pub fn prove_v01_f32(value: &f32) -> Result<V01, V01Error> {
29 (0.0..=1.0).contains(value).then_some(V01).ok_or(V01Error)
30}
31
32/// Proves that an `f64` value is in the normalized `[0, 1]` range.
33///
34/// Values such as `NaN` are rejected because neither range comparison
35/// succeeds for them.
36pub fn prove_v01_f64(value: &f64) -> Result<V01, V01Error> {
37 (0.0..=1.0).contains(value).then_some(V01).ok_or(V01Error)
38}
39
40/// Measures a context and returns a value chosen by the implementation.
41///
42/// `Output` is the value consumed by a corresponding `Map01F32` or `Map01F64`
43/// implementation.
44pub trait Measure<Ctx: ?Sized>: Send + Sync {
45 /// The value produced by [`Measure::measure`].
46 type Output;
47
48 /// Extracts a measurable value from `ctx`.
49 fn measure(&self, ctx: &Ctx) -> Self::Output;
50}
51
52/// Maps a measurement into the `[0, 1]` range.
53///
54/// `Input` is associated with the mapper rather than fixed in the trait, so a
55/// mapper can consume any measurement type. The normalized result carries the
56/// [`V01`] witness.
57pub trait Map01F32: Send + Sync {
58 /// The value accepted by [`Map01F32::map`].
59 type Input;
60
61 /// Converts `value` into a normalized score.
62 fn map(&self, value: Self::Input) -> Witnessed<f32, V01>;
63}
64
65/// Maps a measurement into the `[0, 1]` range.
66///
67/// `Input` is associated with the mapper rather than fixed in the trait, so a
68/// mapper can consume any measurement type. The normalized result carries the
69/// [`V01`] witness.
70pub trait Map01F64: Send + Sync {
71 /// The value accepted by [`Map01F64::map`].
72 type Input;
73
74 /// Converts `value` into a normalized score.
75 fn map(&self, value: Self::Input) -> Witnessed<f64, V01>;
76}
77
78/// Evaluates a context into an `f64` score.
79pub trait EvalF64<Ctx: ?Sized>: Send + Sync {
80 /// Computes a score from `ctx`.
81 fn eval(&self, ctx: &Ctx) -> f64;
82}
83
84impl<Ctx, E> EvalF64<Ctx> for Box<E>
85where
86 Ctx: ?Sized,
87 E: EvalF64<Ctx> + ?Sized,
88{
89 #[inline]
90 fn eval(&self, ctx: &Ctx) -> f64 {
91 self.as_ref().eval(ctx)
92 }
93}
94
95/// Evaluates a context into an `f32` score.
96pub trait EvalF32<Ctx: ?Sized>: Send + Sync {
97 /// Computes a score from `ctx`.
98 fn eval(&self, ctx: &Ctx) -> f32;
99}
100
101impl<Ctx, E> EvalF32<Ctx> for Box<E>
102where
103 Ctx: ?Sized,
104 E: EvalF32<Ctx> + ?Sized,
105{
106 #[inline]
107 fn eval(&self, ctx: &Ctx) -> f32 {
108 self.as_ref().eval(ctx)
109 }
110}