sigma_types/
sigma.rs

1//! Type that maintains a given invariant.
2
3#![expect(
4    clippy::arbitrary_source_item_ordering,
5    reason = "macros must be defined before they're used"
6)]
7
8use {
9    crate::{
10        CanBeInfinite, Finite, Negative, NonNegative, NonPositive, NonZero, OnUnit, One, Positive,
11        Zero,
12    },
13    core::{
14        borrow::Borrow,
15        cmp::Ordering,
16        fmt,
17        hash::{Hash, Hasher},
18        marker::PhantomData,
19        ops,
20    },
21};
22
23#[cfg(test)]
24use {paste::paste, quickcheck::TestResult, quickcheck_macros::quickcheck};
25
26#[cfg(feature = "std")]
27use std::env;
28
29#[cfg(any(test, feature = "quickcheck"))]
30use quickcheck::{Arbitrary, Gen};
31
32#[cfg(all(any(test, feature = "quickcheck"), not(feature = "std")))]
33use alloc::boxed::Box;
34
35/// Implement a unary operation.
36macro_rules! impl_op_1 {
37    ($op:ident, $fn:ident, $lhs:ident, $out:ident $(, $($bound:ident),* $(,)?)?) => {
38        impl<
39            L: $($($bound +)*)? fmt::Debug + ops::$op<Output: $($($bound +)*)? fmt::Debug>,
40        > ops::$op for $lhs<L>
41        {
42            type Output = $out<<L as ops::$op>::Output>;
43
44            #[inline]
45            fn $fn(self) -> Self::Output {
46                self.map(|lhs| lhs.$fn())
47            }
48        }
49
50        #[cfg(test)]
51        paste! {
52            #[cfg(test)]
53            #[expect(trivial_casts, clippy::as_conversions, reason = "must be an implementation detail of `quickcheck`")]
54            mod [< $lhs:snake _ $fn >] {
55                use super::*;
56
57                #[quickcheck]
58                fn doesnt_panic(lhs: $lhs</* L */ f64>) -> TestResult {
59                    if lhs.abs() > 1e64_f64 { return TestResult::discard() }
60                    _ = <$lhs</* L */ f64> as ops::$op>::$fn(lhs);
61                    TestResult::passed()
62                }
63            }
64        }
65    };
66}
67
68/// Implement a binary operation.
69macro_rules! impl_op_2 {
70    ($op:ident, $fn:ident, $lhs:ident, $rhs:ident, $out:ident, $reject:expr $(, $($bound:ident),* $(,)?)?) => {
71        impl<
72            L: $($($bound +)*)? fmt::Debug + ops::$op<R, Output: $($($bound +)*)? fmt::Debug>,
73            R: $($($bound +)*)? fmt::Debug,
74        > ops::$op<$rhs<R>> for $lhs<L>
75        {
76            type Output = $out<<L as ops::$op<R>>::Output>;
77
78            #[inline]
79            fn $fn(self, rhs: $rhs<R>) -> Self::Output {
80                self.map(|lhs| lhs.$fn(rhs.get()))
81            }
82        }
83
84        #[cfg(test)]
85        paste! {
86            #[cfg(test)]
87            #[expect(trivial_casts, clippy::as_conversions, reason = "must be an implementation detail of `quickcheck`")]
88            mod [< $lhs:snake _ $fn _ $rhs:snake >] {
89                use super::*;
90
91                #[quickcheck]
92                fn doesnt_panic(lhs: $lhs</* L */ f64>, rhs: $rhs</* R */ f64>) -> TestResult {
93                    if lhs.abs() > 1e64_f64 { return TestResult::discard() }
94                    if rhs.abs() > 1e64_f64 { return TestResult::discard() }
95                    let toss: fn(&$lhs</* L */ f64>, &$rhs</* R */ f64>) -> bool = $reject;
96                    if toss(&lhs, &rhs) { return TestResult::discard() }
97                    _ = <$lhs</* L */ f64> as ops::$op<$rhs</* R */ f64>>>::$fn(lhs, rhs);
98                    TestResult::passed()
99                }
100            }
101        }
102
103        impl<
104            'rhs,
105            L: $($($bound +)*)? fmt::Debug + ops::$op<&'rhs R, Output: $($($bound +)*)? fmt::Debug>,
106            R: $($($bound +)*)? fmt::Debug,
107        > ops::$op<&'rhs $rhs<R>> for $lhs<L>
108        {
109            type Output = $out<<L as ops::$op<&'rhs R>>::Output>;
110
111            #[inline]
112            fn $fn(self, rhs: &'rhs $rhs<R>) -> Self::Output {
113                self.map(|lhs| lhs.$fn(rhs.get_ref()))
114            }
115        }
116
117        #[cfg(test)]
118        paste! {
119            #[cfg(test)]
120            #[expect(trivial_casts, clippy::as_conversions, reason = "must be an implementation detail of `quickcheck`")]
121            mod [< $lhs:snake _ $fn _ $rhs:snake _ref >] {
122                use super::*;
123
124                #[quickcheck]
125                fn doesnt_panic(lhs: $lhs</* L */ f64>, rhs: $rhs</* R */ f64>) -> TestResult {
126                    if lhs.abs() > 1e64_f64 { return TestResult::discard() }
127                    if rhs.abs() > 1e64_f64 { return TestResult::discard() }
128                    let toss: fn(&$lhs</* L */ f64>, &$rhs</* R */ f64>) -> bool = $reject;
129                    if toss(&lhs, &rhs) { return TestResult::discard() }
130                    _ = <$lhs</* L */ f64> as ops::$op<&$rhs</* R */ f64>>>::$fn(lhs, &rhs);
131                    TestResult::passed()
132                }
133            }
134        }
135
136        /*
137        impl<
138            'lhs,
139            L: $($($bound +)*)? fmt::Debug,
140            R: $($($bound +)*)? fmt::Debug,
141        > ops::$op<$rhs<R>> for &'lhs $lhs<L>
142        where
143            &'lhs L: ops::$op<R, Output: $($($bound +)*)? fmt::Debug>,
144        {
145            type Output = $out<<&'lhs L as ops::$op<R>>::Output>;
146
147            #[inline]
148            fn $fn(self, rhs: $rhs<R>) -> Self::Output {
149                self.map_ref(|lhs| lhs.$fn(rhs.get()))
150            }
151        }
152
153        #[cfg(test)]
154        #[quickcheck]
155        fn [< doesnt_panic_ $lhs:snake _ $fn _ $rhs:snake >] {
156            fn doesnt_panic(lhs: $lhs</* L */ f64>, rhs: $rhs</* R */ f64>) -> TestResult {
157            _ = <$lhs</* L */ f64> as ops::$ops>::$fn(lhs, rhs);
158            TestResult::passed()
159        }
160
161        impl<
162            'lhs,
163            'rhs,
164            L: $($($bound +)*)? fmt::Debug,
165            R: $($($bound +)*)? fmt::Debug,
166        > ops::$op<&'rhs $rhs<R>> for &'lhs $lhs<L>
167        where
168            &'lhs L: ops::$op<&'rhs R, Output: $($($bound +)*)? fmt::Debug>,
169        {
170            type Output = $out<<&'lhs L as ops::$op<&'rhs R>>::Output>;
171
172            #[inline]
173            fn $fn(self, rhs: &'rhs $rhs<R>) -> Self::Output {
174                self.map_ref(|lhs| lhs.$fn(rhs.get_ref()))
175            }
176        }
177
178        #[cfg(test)]
179        #[quickcheck]
180        fn [< doesnt_panic_ $lhs:snake _ $fn _ $rhs:snake _ref >] {
181            fn doesnt_panic(lhs: $lhs</* L */ f64>, rhs: $rhs</* R */ f64>) -> TestResult {
182            _ = <$lhs</* L */ f64> as ops::$ops>::$fn(lhs, rhs);
183            TestResult::passed()
184        }
185        */
186    };
187}
188
189/// Implement a unary assignment operator.
190macro_rules! impl_op_assign {
191    ($op:ident, $fn:ident, $lhs:ident, $rhs:ident, $reject:expr $(, $($bound:ident),* $(,)?)?) => {
192        impl<
193            L: $($($bound +)*)? fmt::Debug + ops::$op<R>,
194            R: $($($bound +)*)? fmt::Debug,
195        > ops::$op<$rhs<R>> for $lhs<L>
196        {
197            #[inline]
198            fn $fn(&mut self, rhs: $rhs<R>) {
199                self.map_mut(|lhs| lhs.$fn(rhs.get()))
200            }
201        }
202
203        #[cfg(test)]
204        paste! {
205            #[cfg(test)]
206            #[expect(trivial_casts, clippy::as_conversions, reason = "must be an implementation detail of `quickcheck`")]
207            mod [< $lhs:snake _ $fn _ $rhs:snake >] {
208                use super::*;
209
210                #[quickcheck]
211                fn doesnt_panic(mut lhs: $lhs</* L */ f64>, rhs: $rhs</* R */ f64>) -> TestResult {
212                    if lhs.abs() > 1e64_f64 { return TestResult::discard() }
213                    if rhs.abs() > 1e64_f64 { return TestResult::discard() }
214                    let toss: fn(&$lhs</* L */ f64>, &$rhs</* R */ f64>) -> bool = $reject;
215                    if toss(&lhs, &rhs) { return TestResult::discard() }
216                    _ = <$lhs</* L */ f64> as ops::$op<$rhs</* R */ f64>>>::$fn(&mut lhs, rhs);
217                    TestResult::passed()
218                }
219            }
220        }
221
222        impl<
223            'rhs,
224            L: $($($bound +)*)? fmt::Debug + ops::$op<&'rhs R>,
225            R: $($($bound +)*)? fmt::Debug,
226        > ops::$op<&'rhs $rhs<R>> for $lhs<L>
227        {
228            #[inline]
229            fn $fn(&mut self, rhs: &'rhs $rhs<R>) {
230                self.map_mut(|lhs| lhs.$fn(rhs.get_ref()))
231            }
232        }
233
234        #[cfg(test)]
235        paste! {
236            #[cfg(test)]
237            #[expect(trivial_casts, clippy::as_conversions, reason = "must be an implementation detail of `quickcheck`")]
238            mod [< $lhs:snake _ $fn _ $rhs:snake _ref >] {
239                use super::*;
240
241                #[quickcheck]
242                fn doesnt_panic(mut lhs: $lhs</* L */ f64>, rhs: $rhs</* R */ f64>) -> TestResult {
243                    if lhs.abs() > 1e64_f64 { return TestResult::discard() }
244                    if rhs.abs() > 1e64_f64 { return TestResult::discard() }
245                    let toss: fn(&$lhs</* L */ f64>, &$rhs</* R */ f64>) -> bool = $reject;
246                    if toss(&lhs, &rhs) { return TestResult::discard() }
247                    _ = <$lhs</* L */ f64> as ops::$op<&$rhs</* R */ f64>>>::$fn(&mut lhs, &rhs);
248                    TestResult::passed()
249                }
250            }
251        }
252    };
253}
254
255/// Addition only (and -assignment).
256macro_rules! impl_add {
257    ($lhs:ident, $rhs:ident, $out:ident $(, $($bound:ident),* $(,)?)?) => {
258        impl_op_2!(Add, add, $lhs, $rhs, $out, |_, _| false $(, $($bound,)*)?);
259        impl_op_assign!(AddAssign, add_assign, $lhs, $rhs, |_, _| false $(, $($bound,)*)?);
260    };
261}
262
263/// Subtraction only (and -assignment).
264macro_rules! impl_sub {
265    ($lhs:ident, $rhs:ident, $out:ident $(, $($bound:ident),* $(,)?)?) => {
266        impl_op_2!(Sub, sub, $lhs, $rhs, $out, |_, _| false $(, $($bound,)*)?);
267        impl_op_assign!(SubAssign, sub_assign, $lhs, $rhs, |_, _| false $(, $($bound,)*)?);
268    };
269}
270
271/// All multiplicative traits (e.g. multiplication, division, ...).
272macro_rules! impl_mul {
273    ($lhs:ident, $rhs:ident, $out:ident $(, $($bound:ident),* $(,)?)?) => {
274        impl_op_2!(Div, div, $lhs, $rhs, $out, |_, rhs| rhs.get() == 0_f64 $(, $($bound,)*)?);
275        impl_op_2!(Mul, mul, $lhs, $rhs, $out, |_, _| false $(, $($bound,)*)?);
276    };
277}
278
279/// All multiplicative traits (e.g. multiplication, division, ...).
280macro_rules! impl_mul_assign {
281    ($lhs:ident, $rhs:ident $(, $($bound:ident),* $(,)?)?) => {
282        impl_op_assign!(DivAssign, div_assign, $lhs, $rhs, |_, rhs| rhs.get() == 0_f64 $(, $($bound,)*)?);
283        impl_op_assign!(MulAssign, mul_assign, $lhs, $rhs, |_, _| false $(, $($bound,)*)?);
284    };
285}
286
287impl<Z: CanBeInfinite + One + fmt::Debug> One for Finite<Z> {
288    const ONE: Self = Self {
289        phantom: PhantomData,
290        raw: Z::ONE,
291    };
292}
293
294impl<Z: CanBeInfinite + Zero + fmt::Debug> Zero for Finite<Z> {
295    const ZERO: Self = Self {
296        phantom: PhantomData,
297        raw: Z::ZERO,
298    };
299}
300
301impl_add!(Finite, Finite, Finite, CanBeInfinite);
302impl_sub!(Finite, Finite, Finite, CanBeInfinite);
303impl_mul!(Finite, Finite, Finite, CanBeInfinite);
304impl_mul_assign!(Finite, Finite, CanBeInfinite);
305impl_op_1!(Neg, neg, Finite, Finite, CanBeInfinite);
306
307impl_add!(Negative, Negative, Negative, PartialOrd, Zero);
308impl_sub!(Negative, NonNegative, Negative, PartialOrd, Zero);
309impl_add!(Negative, NonPositive, Negative, PartialOrd, Zero);
310impl_sub!(Negative, Positive, Negative, PartialOrd, Zero);
311impl_mul!(Negative, Negative, Positive, PartialOrd, Zero);
312impl_mul!(Negative, NonNegative, NonPositive, PartialOrd, Zero);
313impl_mul!(Negative, NonPositive, NonNegative, PartialOrd, Zero);
314impl_mul!(Negative, Positive, Negative, PartialOrd, Zero);
315impl_mul_assign!(Negative, Positive, PartialOrd, Zero);
316impl_op_1!(Neg, neg, Negative, Positive, PartialOrd, Zero);
317
318impl<T: One + PartialOrd + Zero + fmt::Debug> One for NonNegative<T> {
319    const ONE: Self = Self {
320        phantom: PhantomData,
321        raw: T::ONE,
322    };
323}
324
325impl<Z: PartialOrd + Zero + fmt::Debug> Zero for NonNegative<Z> {
326    const ZERO: Self = Self {
327        phantom: PhantomData,
328        raw: Z::ZERO,
329    };
330}
331
332impl_sub!(NonNegative, Negative, Positive, PartialOrd, Zero);
333impl_add!(NonNegative, NonNegative, NonNegative, PartialOrd, Zero);
334impl_sub!(NonNegative, NonPositive, NonNegative, PartialOrd, Zero);
335impl_add!(NonNegative, Positive, Positive, PartialOrd, Zero);
336impl_mul!(NonNegative, Negative, NonPositive, PartialOrd, Zero);
337impl_mul!(NonNegative, NonNegative, NonNegative, PartialOrd, Zero);
338impl_mul!(NonNegative, NonPositive, NonPositive, PartialOrd, Zero);
339impl_mul!(NonNegative, Positive, NonNegative, PartialOrd, Zero);
340impl_mul_assign!(NonNegative, NonNegative, PartialOrd, Zero);
341impl_mul_assign!(NonNegative, Positive, PartialOrd, Zero);
342impl_op_1!(Neg, neg, NonNegative, NonPositive, PartialOrd, Zero);
343
344impl_add!(NonPositive, Negative, Negative, PartialOrd, Zero);
345impl_sub!(NonPositive, NonNegative, NonPositive, PartialOrd, Zero);
346impl_add!(NonPositive, NonPositive, NonPositive, PartialOrd, Zero);
347impl_sub!(NonPositive, Positive, Negative, PartialOrd, Zero);
348impl_mul!(NonPositive, Negative, NonNegative, PartialOrd, Zero);
349impl_mul!(NonPositive, NonNegative, NonPositive, PartialOrd, Zero);
350impl_mul!(NonPositive, NonPositive, NonNegative, PartialOrd, Zero);
351impl_mul!(NonPositive, Positive, NonPositive, PartialOrd, Zero);
352impl_mul_assign!(NonPositive, NonNegative, PartialOrd, Zero);
353impl_mul_assign!(NonPositive, Positive, PartialOrd, Zero);
354impl_op_1!(Neg, neg, NonPositive, NonNegative, PartialOrd, Zero);
355
356impl_mul!(NonZero, NonZero, NonZero, PartialEq, Zero);
357impl_op_1!(Neg, neg, NonZero, NonZero, PartialEq, Zero);
358
359impl<T: One + PartialOrd + Zero + fmt::Debug, const INCLUSIVE_AT_ZERO: bool> One
360    for OnUnit<T, INCLUSIVE_AT_ZERO, true>
361{
362    const ONE: Self = Self {
363        phantom: PhantomData,
364        raw: T::ONE,
365    };
366}
367
368impl<Z: One + PartialOrd + Zero + fmt::Debug, const INCLUSIVE_AT_ONE: bool> Zero
369    for OnUnit<Z, true, INCLUSIVE_AT_ONE>
370{
371    const ZERO: Self = Self {
372        phantom: PhantomData,
373        raw: Z::ZERO,
374    };
375}
376
377impl_sub!(Positive, Negative, Positive, PartialOrd, Zero);
378impl_add!(Positive, NonNegative, Positive, PartialOrd, Zero);
379impl_sub!(Positive, NonPositive, Positive, PartialOrd, Zero);
380impl_add!(Positive, Positive, Positive, PartialOrd, Zero);
381impl_mul!(Positive, Negative, Negative, PartialOrd, Zero);
382impl_mul!(Positive, NonNegative, NonNegative, PartialOrd, Zero);
383impl_mul!(Positive, NonPositive, NonPositive, PartialOrd, Zero);
384impl_mul!(Positive, Positive, Positive, PartialOrd, Zero);
385impl_mul_assign!(Positive, Positive, PartialOrd, Zero);
386impl_op_1!(Neg, neg, Positive, Negative, PartialOrd, Zero);
387
388impl<T: One + PartialOrd + Zero + fmt::Debug> One for Positive<T> {
389    const ONE: Self = Self {
390        phantom: PhantomData,
391        raw: T::ONE,
392    };
393}
394
395impl<Raw: fmt::Debug, Invariant: crate::Test<Raw, 1>> From<Raw> for Sigma<Raw, Invariant> {
396    #[inline(always)]
397    fn from(value: Raw) -> Self {
398        Self::new(value)
399    }
400}
401
402/// Type that maintains a given invariant.
403#[repr(transparent)]
404pub struct Sigma<Raw: fmt::Debug, Invariant: crate::Test<Raw, 1>> {
405    /// Only to silence compiler errors.
406    phantom: PhantomData<Invariant>,
407    /// Internal type (to which this type will reduce in release builds).
408    raw: Raw,
409}
410
411impl<Raw: fmt::Debug, Invariant: crate::Test<Raw, 1>> Sigma<Raw, Invariant> {
412    /// Check all elements of an array.
413    #[inline]
414    pub fn all<const N: usize>(array: &[Raw; N]) -> &[Self; N] {
415        let pointer: *const [Raw; N] = array;
416        let cast: *const [Self; N] = pointer.cast();
417        // SAFETY:
418        // `repr(transparent)`
419        let provisional = unsafe { &*cast };
420        for element in provisional {
421            element.check();
422        }
423        provisional
424    }
425
426    /// Without changing its internal value,
427    /// view one sigma-typed value as implementing another sigma type
428    /// by checking the latter invariant at runtime (iff debug assertions are enabled).
429    /// # Panics
430    /// If the latter invariant does not hold.
431    #[inline(always)]
432    pub fn also<OtherInvariant: crate::Test<Raw, 1>>(self) -> Sigma<Raw, OtherInvariant> {
433        Sigma::new(self.get())
434    }
435
436    /// Without changing its internal value,
437    /// view one sigma-typed value as implementing another sigma type
438    /// by checking the latter invariant at runtime (iff debug assertions are enabled).
439    /// # Panics
440    /// If the latter invariant does not hold.
441    #[inline]
442    #[cfg(debug_assertions)]
443    pub fn also_ref<OtherInvariant: crate::Test<Raw, 1>>(&self) -> &Sigma<Raw, OtherInvariant> {
444        let ptr: *const Self = self;
445        // SAFETY:
446        // Pointer reinterpretation. See `repr(transparent)` above.
447        // All non-zero-sized fields are identical across the cast.
448        let transmuted: &Sigma<Raw, OtherInvariant> = unsafe { &*ptr.cast() };
449        transmuted.check();
450        transmuted
451    }
452
453    /// Without changing its internal value,
454    /// view one sigma-typed value as implementing another sigma type
455    /// by checking the latter invariant at runtime (iff debug assertions are enabled).
456    #[inline]
457    #[cfg(not(debug_assertions))]
458    pub const fn also_ref<OtherInvariant: crate::Test<Raw, 1>>(
459        &self,
460    ) -> &Sigma<Raw, OtherInvariant> {
461        let ptr: *const Self = self;
462        // SAFETY:
463        // Pointer reinterpretation. See `repr(transparent)` above.
464        // All non-zero-sized fields are identical across the cast.
465        unsafe { &*ptr.cast() }
466    }
467
468    /// Check an invariant if and only if debug assertions are enabled.
469    /// # Panics
470    /// If the invariant does not hold ***and*** debug assertions are enabled.
471    #[inline]
472    #[cfg(debug_assertions)]
473    pub fn check(&self) {
474        #[expect(
475            clippy::panic,
476            reason = "Returning a result would break API in release builds"
477        )]
478        match Invariant::test([&self.raw]) {
479            Ok(()) => {}
480            Err(message) => {
481                panic!("{:#?} is not {}: {message}", self.raw, Invariant::ADJECTIVE);
482            }
483        }
484    }
485
486    /// Do nothing (since debug assertions are disabled).
487    #[inline]
488    #[cfg(not(debug_assertions))]
489    pub const fn check(&self) {}
490
491    /// Unwrap the internal value that satisfies the invariant.
492    /// If you're using this to create another value that should
493    /// also maintain an invariant, use `map` instead.
494    #[inline(always)]
495    pub fn get(self) -> Raw {
496        self.raw
497    }
498
499    /// Unwrap the internal value that satisfies the invariant.
500    /// If you're using this to create another value that should
501    /// also maintain an invariant, use `map` instead.
502    #[inline(always)]
503    #[expect(clippy::allow_attributes, reason = "Edition 2021 only")]
504    #[allow(tail_expr_drop_order, reason = "just for miri")]
505    pub fn get_by<Y, F: FnOnce(Raw) -> Y>(self, f: F) -> Y {
506        f(self.get())
507    }
508
509    /// Unwrap the internal value that satisfies the invariant.
510    /// If you're using this to create another value that should
511    /// also maintain an invariant, use `map` instead.
512    #[inline(always)]
513    pub fn get_by_mut<Y, F: FnOnce(&mut Raw) -> Y>(&mut self, f: F) -> Y {
514        f(self.get_mut())
515    }
516
517    /// Unwrap the internal value that satisfies the invariant.
518    /// If you're using this to create another value that should
519    /// also maintain an invariant, use `map` instead.
520    #[inline(always)]
521    pub fn get_by_ref<Y, F: FnOnce(&Raw) -> Y>(&self, f: F) -> Y {
522        f(self)
523    }
524
525    /// Unwrap the internal value that satisfies the invariant.
526    /// If you're using this to create another value that should
527    /// also maintain an invariant, use `map` instead.
528    #[inline(always)]
529    pub const fn get_mut(&mut self) -> &mut Raw {
530        &mut self.raw
531    }
532
533    /// Unwrap the internal value that satisfies the invariant.
534    /// If you're using this to create another value that should
535    /// also maintain an invariant, use `map` instead.
536    #[inline(always)]
537    pub const fn get_ref(&self) -> &Raw {
538        &self.raw
539    }
540
541    /// Apply a function to a term that implements a given invariant (say, A),
542    /// then check the output for a (possibly different) invariant (say, B).
543    #[inline]
544    #[expect(clippy::allow_attributes, reason = "Edition 2021 only")]
545    #[allow(tail_expr_drop_order, reason = "just for miri")]
546    pub fn map<
547        OtherRaw: fmt::Debug,
548        OtherInvariant: crate::Test<OtherRaw, 1>,
549        F: FnOnce(Raw) -> OtherRaw,
550    >(
551        self,
552        f: F,
553    ) -> Sigma<OtherRaw, OtherInvariant> {
554        Sigma::new(f(self.get()))
555    }
556
557    /// Apply a function that mutates this value,
558    /// then check that the operation maintained this invariant.
559    #[inline]
560    pub fn map_mut<Y, F: FnOnce(&mut Raw) -> Y>(&mut self, f: F) -> Y {
561        let raw = self.get_mut();
562        let y = f(raw);
563        self.check();
564        y
565    }
566
567    /// Apply a function to a term that implements a given invariant (say, A),
568    /// then check the output for a (possibly different) invariant (say, B).
569    #[inline]
570    #[expect(clippy::allow_attributes, reason = "Edition 2021 only")]
571    #[allow(tail_expr_drop_order, reason = "just for miri")]
572    pub fn map_ref<
573        OtherRaw: fmt::Debug,
574        OtherInvariant: crate::Test<OtherRaw, 1>,
575        F: FnOnce(&Raw) -> OtherRaw,
576    >(
577        &self,
578        f: F,
579    ) -> Sigma<OtherRaw, OtherInvariant> {
580        Sigma::new(f(self))
581    }
582
583    /// Create a new sigma type instance by checking an invariant.
584    /// # Panics
585    /// If the invariant does not hold ***and*** debug assertions are enabled.
586    #[inline]
587    #[cfg(debug_assertions)]
588    pub fn new(raw: Raw) -> Self {
589        let provisional = Self {
590            phantom: PhantomData,
591            raw,
592        };
593        provisional.check();
594        provisional
595    }
596
597    /// Create a new sigma type instance by checking an invariant.
598    /// # Panics
599    /// If the invariant does not hold ***and*** debug assertions are enabled.
600    #[inline]
601    #[cfg(not(debug_assertions))]
602    pub const fn new(raw: Raw) -> Self {
603        Self {
604            phantom: PhantomData,
605            raw,
606        }
607    }
608
609    /// Without changing its internal value,
610    /// try to view one sigma-typed value as implementing another sigma type
611    /// by checking the latter invariant at runtime.
612    /// # Errors
613    /// If the latter invariant does not hold.
614    #[inline]
615    pub fn try_also<OtherInvariant: crate::Test<Raw, 1>>(
616        self,
617    ) -> Result<Sigma<Raw, OtherInvariant>, Self> {
618        Sigma::try_new(self.get()).map_err(|raw| Self {
619            phantom: PhantomData,
620            raw,
621        })
622    }
623
624    /// Without changing its internal value,
625    /// try to view one sigma-typed value as implementing another sigma type
626    /// by checking the latter invariant at runtime.
627    /// # Errors
628    /// If the latter invariant does not hold.
629    #[inline]
630    pub fn try_also_ref<OtherInvariant: crate::Test<Raw, 1>>(
631        &self,
632    ) -> Result<&Sigma<Raw, OtherInvariant>, OtherInvariant::Error<'_>> {
633        let ptr: *const Self = self;
634        // SAFETY:
635        // Pointer reinterpretation. See `repr(transparent)` above.
636        // All non-zero-sized fields are identical across the cast.
637        let transmuted: &Sigma<Raw, OtherInvariant> = unsafe { &*ptr.cast() };
638        transmuted.try_check()?;
639        Ok(transmuted)
640    }
641
642    /// Check an invariant without panicking.
643    /// # Errors
644    /// If the invariant does not hold.
645    #[inline(always)]
646    pub fn try_check(&self) -> Result<(), Invariant::Error<'_>> {
647        Invariant::test([&self.raw])
648    }
649
650    /// Create a new sigma type instance by checking an invariant.
651    /// # Errors
652    /// If the invariant does not hold.
653    /// In this case, return the original input unchanged.
654    #[inline]
655    pub fn try_new(raw: Raw) -> Result<Self, Raw> {
656        let provisional = Self {
657            phantom: PhantomData,
658            raw,
659        };
660
661        if provisional.try_check().is_ok() {
662            Ok(provisional)
663        } else {
664            Err(provisional.raw)
665        }
666    }
667
668    /// Wrap a reference through pointer reinterpretation magic.
669    #[inline(always)]
670    #[cfg(debug_assertions)]
671    pub fn wrap(reference: &Raw) -> &Self {
672        let raw_pointer: *const _ = reference;
673        let sigma_pointer = raw_pointer.cast::<Self>();
674        // SAFETY:
675        // `repr(transparent)`
676        let wrapped = unsafe { &*sigma_pointer };
677        wrapped.check();
678        wrapped
679    }
680
681    /// Wrap a reference through pointer reinterpretation magic.
682    #[inline(always)]
683    #[cfg(not(debug_assertions))]
684    pub const fn wrap(reference: &Raw) -> &Self {
685        let raw_pointer: *const _ = reference;
686        let sigma_pointer = raw_pointer.cast::<Self>();
687        // SAFETY:
688        // `repr(transparent)`
689        unsafe { &*sigma_pointer }
690    }
691
692    /// Wrap a reference through pointer reinterpretation magic.
693    #[inline(always)]
694    pub fn wrap_mut(reference: &mut Raw) -> &mut Self {
695        let raw_pointer: *mut _ = reference;
696        let sigma_pointer = raw_pointer.cast::<Self>();
697        // SAFETY:
698        // `repr(transparent)`
699        let wrapped = unsafe { &mut *sigma_pointer };
700        wrapped.check();
701        wrapped
702    }
703}
704
705#[cfg(any(test, feature = "quickcheck"))]
706impl<Raw: Arbitrary + fmt::Debug, Invariant: 'static + crate::Test<Raw, 1>> Arbitrary
707    for Sigma<Raw, Invariant>
708{
709    #[inline]
710    fn arbitrary(g: &mut Gen) -> Self {
711        loop {
712            let raw: Raw = Arbitrary::arbitrary(g);
713            if let Ok(sigma) = Self::try_new(raw) {
714                return sigma;
715            }
716        }
717    }
718
719    #[inline]
720    fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
721        let Self {
722            phantom: PhantomData,
723            ref raw,
724        } = *self;
725        Box::new(raw.shrink().filter_map(|shrunk| Self::try_new(shrunk).ok()))
726    }
727}
728
729impl<Raw: fmt::Debug, Invariant: crate::Test<Raw, 1>> AsRef<Raw> for Sigma<Raw, Invariant> {
730    #[inline(always)]
731    fn as_ref(&self) -> &Raw {
732        &self.raw
733    }
734}
735
736impl<Raw: fmt::Debug, Invariant: crate::Test<Raw, 1>> Borrow<Raw> for Sigma<Raw, Invariant> {
737    #[inline(always)]
738    fn borrow(&self) -> &Raw {
739        &self.raw
740    }
741}
742
743impl<Raw: CanBeInfinite + fmt::Debug, Invariant: crate::Test<Raw>> CanBeInfinite
744    for Sigma<Raw, Invariant>
745{
746    #[inline(always)]
747    fn check_finite(&self) -> bool {
748        self.raw.check_finite()
749    }
750}
751
752impl<Raw: Clone + fmt::Debug, Invariant: crate::Test<Raw, 1>> Clone for Sigma<Raw, Invariant> {
753    #[inline(always)]
754    fn clone(&self) -> Self {
755        Self::new(self.raw.clone())
756    }
757
758    #[inline(always)]
759    fn clone_from(&mut self, source: &Self) {
760        self.raw.clone_from(&source.raw);
761        self.check();
762    }
763}
764
765impl<Raw: Copy + fmt::Debug, Invariant: crate::Test<Raw, 1>> Copy for Sigma<Raw, Invariant> {}
766
767impl<Raw: Default + fmt::Debug, Invariant: crate::Test<Raw, 1>> Default for Sigma<Raw, Invariant> {
768    #[inline(always)]
769    fn default() -> Self {
770        Self::new(Raw::default())
771    }
772}
773
774impl<Raw: Eq + fmt::Debug, Invariant: crate::Test<Raw, 1>> Eq for Sigma<Raw, Invariant> {
775    #[inline(always)]
776    fn assert_receiver_is_total_eq(&self) {
777        self.raw.assert_receiver_is_total_eq();
778    }
779}
780
781impl<I, Raw: FromIterator<I> + fmt::Debug, Invariant: crate::Test<Raw, 1>> FromIterator<I>
782    for Sigma<Raw, Invariant>
783{
784    #[inline]
785    fn from_iter<T: IntoIterator<Item = I>>(iter: T) -> Self {
786        Self::new(Raw::from_iter(iter))
787    }
788}
789
790impl<Raw: Hash + fmt::Debug, Invariant: crate::Test<Raw, 1>> Hash for Sigma<Raw, Invariant> {
791    #[inline(always)]
792    fn hash<H: Hasher>(&self, state: &mut H) {
793        self.raw.hash(state);
794    }
795
796    #[inline(always)]
797    fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
798        let ptr: *const [Self] = data;
799        #[expect(clippy::as_conversions, reason = "marked `repr(transparent)` above")]
800        let transparent = ptr as *const [Raw];
801        // SAFETY:
802        // Marked `repr(transparent)` above
803        let reinterpreted: &[Raw] = unsafe { &*transparent };
804        Raw::hash_slice(reinterpreted, state);
805    }
806}
807
808impl<Raw: IntoIterator + fmt::Debug, Invariant: crate::Test<Raw, 1>> IntoIterator
809    for Sigma<Raw, Invariant>
810{
811    type IntoIter = <Raw as IntoIterator>::IntoIter;
812    type Item = <Raw as IntoIterator>::Item;
813
814    #[inline(always)]
815    fn into_iter(self) -> Self::IntoIter {
816        Raw::into_iter(self.get())
817    }
818}
819
820/*
821impl<Raw: Iterator + fmt::Debug, Invariant: crate::Test<Raw, 1>> Iterator
822    for Sigma<Raw, Invariant>
823{
824}
825*/
826
827impl<Raw: Ord + fmt::Debug, Invariant: crate::Test<Raw, 1>> Ord for Sigma<Raw, Invariant> {
828    #[inline(always)]
829    fn clamp(self, min: Self, max: Self) -> Self {
830        Self::new(self.raw.clamp(min.raw, max.raw))
831    }
832
833    #[inline(always)]
834    fn cmp(&self, other: &Self) -> Ordering {
835        self.raw.cmp(&other.raw)
836    }
837
838    #[inline(always)]
839    fn max(self, other: Self) -> Self {
840        Self::new(self.raw.max(other.raw))
841    }
842
843    #[inline(always)]
844    fn min(self, other: Self) -> Self {
845        Self::new(self.raw.min(other.raw))
846    }
847}
848
849impl<Raw: PartialEq + fmt::Debug, Invariant: crate::Test<Raw, 1>> PartialEq
850    for Sigma<Raw, Invariant>
851{
852    #[inline(always)]
853    fn eq(&self, other: &Self) -> bool {
854        self.raw.eq(&other.raw)
855    }
856
857    #[inline(always)]
858    #[expect(
859        clippy::partialeq_ne_impl,
860        reason = "arbitrary choice between competing lints"
861    )]
862    fn ne(&self, other: &Self) -> bool {
863        self.raw.ne(&other.raw)
864    }
865}
866
867impl<Raw: PartialOrd + fmt::Debug, Invariant: crate::Test<Raw, 1>> PartialOrd
868    for Sigma<Raw, Invariant>
869{
870    #[inline(always)]
871    fn ge(&self, other: &Self) -> bool {
872        self.raw.ge(&other.raw)
873    }
874
875    #[inline(always)]
876    fn gt(&self, other: &Self) -> bool {
877        self.raw.gt(&other.raw)
878    }
879
880    #[inline(always)]
881    fn le(&self, other: &Self) -> bool {
882        self.raw.le(&other.raw)
883    }
884
885    #[inline(always)]
886    fn lt(&self, other: &Self) -> bool {
887        self.raw.lt(&other.raw)
888    }
889
890    #[inline(always)]
891    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
892        self.raw.partial_cmp(&other.raw)
893    }
894}
895
896impl<Raw: fmt::Debug, Invariant: crate::Test<Raw, 1>> fmt::Debug for Sigma<Raw, Invariant> {
897    #[inline(always)]
898    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
899        #[cfg(feature = "std")]
900        if env::var("DEBUG_SIGMA_TYPES").is_ok_and(|s| s != "0") {
901            write!(f, "({}) ", Invariant::ADJECTIVE)?;
902        }
903        fmt::Debug::fmt(&self.raw, f)
904    }
905}
906
907impl<Raw: fmt::Debug + fmt::Display, Invariant: crate::Test<Raw, 1>> fmt::Display
908    for Sigma<Raw, Invariant>
909{
910    #[inline(always)]
911    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
912        fmt::Display::fmt(&self.raw, f)
913    }
914}
915
916impl<Raw: fmt::Debug, Invariant: crate::Test<Raw, 1>> ops::Deref for Sigma<Raw, Invariant> {
917    type Target = Raw;
918
919    #[inline(always)]
920    fn deref(&self) -> &Self::Target {
921        &self.raw
922    }
923}
924
925#[cfg(feature = "serde")]
926#[expect(clippy::missing_trait_methods, reason = "I'm no expert")]
927impl<'de, Raw: fmt::Debug + serde::Deserialize<'de>, Invariant: crate::Test<Raw, 1>>
928    serde::Deserialize<'de> for Sigma<Raw, Invariant>
929{
930    #[inline]
931    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
932        use serde::de::Error;
933
934        let raw = Raw::deserialize(deserializer)?;
935        let provisional = Self {
936            phantom: PhantomData,
937            raw,
938        };
939        match provisional.try_check() {
940            Ok(()) => {}
941            Err(e) => return Err(Error::custom(e)),
942        }
943        Ok(provisional)
944    }
945}
946
947#[cfg(feature = "serde")]
948impl<Raw: fmt::Debug + serde::Serialize, Invariant: crate::Test<Raw, 1>> serde::Serialize
949    for Sigma<Raw, Invariant>
950{
951    #[inline(always)]
952    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
953        self.raw.serialize(serializer)
954    }
955}