karpal_proof/
refinement.rs1#[cfg(not(feature = "std"))]
2use alloc::vec::Vec;
3
4use karpal_core::Semigroup;
5
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub struct NonEmpty<C> {
15 inner: C,
16}
17
18impl<T> NonEmpty<Vec<T>> {
19 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 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 pub fn singleton(value: T) -> Self {
38 NonEmpty {
39 inner: [value].into(),
40 }
41 }
42
43 pub fn head(&self) -> &T {
45 &self.inner[0]
47 }
48
49 pub fn len(&self) -> usize {
51 self.inner.len()
52 }
53
54 pub fn is_empty(&self) -> bool {
56 false
57 }
58
59 pub fn push(&mut self, value: T) {
61 self.inner.push(value);
62 }
63
64 pub fn as_slice(&self) -> &[T] {
66 &self.inner
67 }
68
69 pub fn into_vec(self) -> Vec<T> {
71 self.inner
72 }
73
74 pub fn iter(&self) -> core::slice::Iter<'_, T> {
76 self.inner.iter()
77 }
78
79 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#[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 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 pub fn get(self) -> $t {
119 self.value
120 }
121
122 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 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 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 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}