Skip to main content

karpal_proof/
refinement.rs

1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use karpal_core::Semigroup;
5
6/// A `Vec<T>` guaranteed to contain at least one element.
7///
8/// Unlike `NonEmptyVec<T>` in karpal-core (which has a structurally
9/// different representation with separate `head` and `tail` fields),
10/// `NonEmpty<Vec<T>>` wraps a standard `Vec<T>` with a refinement
11/// invariant. Construction is only possible via `try_new` (which checks)
12/// or `from_parts` (which requires at least one element).
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct NonEmpty<C> {
15    inner: C,
16}
17
18impl<T> NonEmpty<Vec<T>> {
19    /// Attempt to construct a `NonEmpty<Vec<T>>` from a `Vec<T>`.
20    /// Returns `None` if the vector is empty.
21    pub fn try_new(v: Vec<T>) -> Option<Self> {
22        if v.is_empty() {
23            None
24        } else {
25            Some(NonEmpty { inner: v })
26        }
27    }
28
29    /// Construct from a head element and remaining tail.
30    pub fn from_parts(head: T, tail: Vec<T>) -> Self {
31        let mut v = tail;
32        v.insert(0, head);
33        NonEmpty { inner: v }
34    }
35
36    /// Construct a single-element vector.
37    pub fn singleton(value: T) -> Self {
38        NonEmpty {
39            inner: [value].into(),
40        }
41    }
42
43    /// The first element (always exists).
44    pub fn head(&self) -> &T {
45        // Safety: invariant guarantees non-empty
46        &self.inner[0]
47    }
48
49    /// Number of elements (always >= 1).
50    pub fn len(&self) -> usize {
51        self.inner.len()
52    }
53
54    /// Always returns `false` — a `NonEmpty` is never empty by construction.
55    pub fn is_empty(&self) -> bool {
56        false
57    }
58
59    /// Push a new element.
60    pub fn push(&mut self, value: T) {
61        self.inner.push(value);
62    }
63
64    /// Access the underlying slice.
65    pub fn as_slice(&self) -> &[T] {
66        &self.inner
67    }
68
69    /// Convert into the underlying `Vec<T>`, discarding the proof.
70    pub fn into_vec(self) -> Vec<T> {
71        self.inner
72    }
73
74    /// Iterate over references.
75    pub fn iter(&self) -> core::slice::Iter<'_, T> {
76        self.inner.iter()
77    }
78
79    /// Map a function over all elements, preserving non-emptiness.
80    pub fn map<U>(self, f: impl FnMut(T) -> U) -> NonEmpty<Vec<U>> {
81        NonEmpty {
82            inner: self.inner.into_iter().map(f).collect(),
83        }
84    }
85}
86
87impl<T: Semigroup + Clone> Semigroup for NonEmpty<Vec<T>> {
88    fn combine(mut self, other: Self) -> Self {
89        self.inner.extend(other.inner);
90        self
91    }
92}
93
94/// A numeric value guaranteed to be strictly positive (> 0).
95///
96/// Useful for operations that require nonzero values, such as
97/// `Field::reciprocal`.
98#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
99pub struct Positive<T> {
100    value: T,
101}
102
103macro_rules! impl_positive_float {
104    ($($t:ty),*) => {
105        $(
106            impl Positive<$t> {
107                /// Attempt to construct a `Positive` from a value.
108                /// Returns `None` if the value is not strictly positive.
109                pub fn try_new(v: $t) -> Option<Self> {
110                    if v > 0.0 && v.is_finite() {
111                        Some(Positive { value: v })
112                    } else {
113                        None
114                    }
115                }
116
117                /// Get the inner value.
118                pub fn get(self) -> $t {
119                    self.value
120                }
121
122                /// Safe reciprocal: always valid for positive values.
123                pub fn reciprocal(self) -> Self {
124                    Positive { value: 1.0 / self.value }
125                }
126            }
127        )*
128    };
129}
130
131impl_positive_float!(f32, f64);
132
133macro_rules! impl_positive_int {
134    ($($t:ty),*) => {
135        $(
136            impl Positive<$t> {
137                /// Attempt to construct a `Positive` from a value.
138                /// Returns `None` if the value is zero or negative.
139                pub fn try_new(v: $t) -> Option<Self> {
140                    if v > 0 {
141                        Some(Positive { value: v })
142                    } else {
143                        None
144                    }
145                }
146
147                /// Get the inner value.
148                pub fn get(self) -> $t {
149                    self.value
150                }
151            }
152        )*
153    };
154}
155
156impl_positive_int!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
157
158macro_rules! impl_positive_unsigned {
159    ($($t:ty),*) => {
160        $(
161            impl Positive<$t> {
162                /// Construct from a nonzero unsigned value.
163                /// Returns `None` if zero.
164                pub fn from_nonzero(v: $t) -> Option<Self> {
165                    if v > 0 {
166                        Some(Positive { value: v })
167                    } else {
168                        None
169                    }
170                }
171            }
172        )*
173    };
174}
175
176impl_positive_unsigned!(u8, u16, u32, u64, u128);
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn non_empty_try_new() {
184        assert!(NonEmpty::try_new(Vec::<i32>::new()).is_none());
185        let ne = NonEmpty::try_new(vec![1, 2, 3]).unwrap();
186        assert_eq!(*ne.head(), 1);
187        assert_eq!(ne.len(), 3);
188    }
189
190    #[test]
191    fn non_empty_from_parts() {
192        let ne = NonEmpty::from_parts(10, vec![20, 30]);
193        assert_eq!(*ne.head(), 10);
194        assert_eq!(ne.as_slice(), &[10, 20, 30]);
195    }
196
197    #[test]
198    fn non_empty_singleton() {
199        let ne = NonEmpty::singleton(42);
200        assert_eq!(*ne.head(), 42);
201        assert_eq!(ne.len(), 1);
202    }
203
204    #[test]
205    fn non_empty_push() {
206        let mut ne = NonEmpty::singleton(1);
207        ne.push(2);
208        assert_eq!(ne.len(), 2);
209        assert_eq!(ne.as_slice(), &[1, 2]);
210    }
211
212    #[test]
213    fn non_empty_map() {
214        let ne = NonEmpty::from_parts(1, vec![2, 3]);
215        let doubled = ne.map(|x| x * 2);
216        assert_eq!(doubled.as_slice(), &[2, 4, 6]);
217    }
218
219    #[test]
220    fn non_empty_into_vec() {
221        let ne = NonEmpty::from_parts(1, vec![2]);
222        assert_eq!(ne.into_vec(), vec![1, 2]);
223    }
224
225    #[test]
226    fn non_empty_iter() {
227        let ne = NonEmpty::from_parts(1, vec![2, 3]);
228        let sum: i32 = ne.iter().sum();
229        assert_eq!(sum, 6);
230    }
231
232    #[test]
233    fn non_empty_semigroup() {
234        let a = NonEmpty::from_parts(1, vec![2]);
235        let b = NonEmpty::from_parts(3, vec![4]);
236        let c = a.combine(b);
237        assert_eq!(c.as_slice(), &[1, 2, 3, 4]);
238    }
239
240    #[test]
241    fn positive_f64() {
242        assert!(Positive::<f64>::try_new(0.0).is_none());
243        assert!(Positive::<f64>::try_new(-1.0).is_none());
244        assert!(Positive::<f64>::try_new(f64::NAN).is_none());
245        assert!(Positive::<f64>::try_new(f64::INFINITY).is_none());
246
247        let p = Positive::<f64>::try_new(4.0).unwrap();
248        assert_eq!(p.get(), 4.0);
249
250        let r = p.reciprocal();
251        assert!((r.get() - 0.25).abs() < 1e-10);
252    }
253
254    #[test]
255    fn positive_f32() {
256        let p = Positive::<f32>::try_new(2.0).unwrap();
257        assert!((p.reciprocal().get() - 0.5).abs() < 1e-6);
258    }
259
260    #[test]
261    fn positive_i32() {
262        assert!(Positive::<i32>::try_new(0).is_none());
263        assert!(Positive::<i32>::try_new(-5).is_none());
264        let p = Positive::<i32>::try_new(7).unwrap();
265        assert_eq!(p.get(), 7);
266    }
267
268    #[test]
269    fn positive_u32() {
270        assert!(Positive::<u32>::try_new(0).is_none());
271        let p = Positive::<u32>::try_new(10).unwrap();
272        assert_eq!(p.get(), 10);
273    }
274
275    #[test]
276    fn positive_u32_from_nonzero() {
277        assert!(Positive::<u32>::from_nonzero(0).is_none());
278        let p = Positive::<u32>::from_nonzero(5).unwrap();
279        assert_eq!(p.get(), 5);
280    }
281}