Skip to main content

arithmetic_nonmax/
lib.rs

1//! `NonMax` provides integer types that cannot be the maximum value of their underlying primitive type.
2//!
3//! # Memory Optimization
4//!
5//! The main benefit of `NonMax<T>` is that `Option<NonMax<T>>` has the same size as `T`.
6//! This is achieved through Rust's "niche optimization", where the bit pattern of the
7//! maximum value is used to represent `None`.
8//!
9//! | Primitive | `size_of::<T>()` | `size_of::<Option<T>>()` | `size_of::<Option<NonMax<T>>>()` |
10//! |-----------|------------------|--------------------------|---------------------------------|
11//! | `u32`     | 4                | 8                        | **4**                           |
12//! | `i32`     | 4                | 8                        | **4**                           |
13//! | `u8`      | 1                | 2                        | **1**                           |
14
15#![no_std]
16
17use core::convert::TryFrom;
18use core::fmt::{self, Binary, Display, LowerHex, Octal, UpperHex};
19use core::hash::Hash;
20use core::marker::PhantomData;
21use core::num::NonZero;
22use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
23
24/// Creates a `NonMax` value at compile-time.
25///
26/// This macro checks at compile-time that the provided value is not the maximum
27/// value for its type. If the value is the maximum, the program will fail to compile.
28///
29/// # Examples
30/// ```
31/// # use arithmetic_nonmax::non_max;
32/// let x = non_max!(123u8);
33/// ```
34///
35/// ```compile_fail
36/// # use arithmetic_nonmax::non_max;
37/// let x = non_max!(255u8); // This fails to compile
38/// ```
39///
40/// ```compile_fail
41/// # use arithmetic_nonmax::non_max;
42/// let x = non_max!(127i8); // i8::MAX fails
43/// ```
44///
45/// ```compile_fail
46/// # use arithmetic_nonmax::non_max;
47/// let x = non_max!(u16::MAX); // Explicit MAX fails
48/// ```
49#[macro_export]
50macro_rules! non_max {
51    ($val:expr) => {{
52        const _: () = const {
53            if $val == <_ as $crate::NonMaxItem>::MAX {
54                panic!("provided value is the maximum value for this type");
55            }
56        };
57        unsafe { <_ as $crate::NonMaxItem>::create_nonmax_unchecked($val) }
58    }};
59}
60
61/// Error type returned when a value is the maximum for its type.
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct MaxValueError;
64
65impl Display for MaxValueError {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        write!(f, "provided value is the maximum value for this type")
68    }
69}
70
71impl core::error::Error for MaxValueError {}
72
73/// A wrapper type for an integer that cannot be its maximum value.
74///
75/// This type leverages Rust's `NonZero` optimization by mapping the maximum value to zero internally.
76/// As a result, `Option<NonMax<T>>` has the same size as the underlying primitive type `T`.
77///
78/// # Examples
79/// ```
80/// # use arithmetic_nonmax::NonMaxU32;
81/// # use core::mem::size_of;
82/// assert_eq!(size_of::<NonMaxU32>(), 4);
83/// assert_eq!(size_of::<Option<NonMaxU32>>(), 4);
84/// ```
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
86pub struct NonMax<T: NonMaxItem>(T::NonZero);
87
88impl<T: NonMaxItem + Copy> NonMax<T> {
89    /// Creates a new `NonMax` if the given value is not the maximum value.
90    ///
91    /// # Examples
92    /// ```
93    /// # use arithmetic_nonmax::NonMaxU8;
94    /// assert!(NonMaxU8::new(254).is_some());
95    /// assert!(NonMaxU8::new(255).is_none());
96    /// ```
97    pub fn new(value: T) -> Option<Self> {
98        Value::new(value).to_inner_repr().to_nonmax()
99    }
100
101    fn to_real_repr(self) -> Value<T, Real> {
102        T::from_nonzero(self.0).to_real_repr()
103    }
104
105    /// Returns the underlying primitive value.
106    ///
107    /// # Examples
108    /// ```
109    /// # use arithmetic_nonmax::NonMaxU32;
110    /// let x = NonMaxU32::new(123).unwrap();
111    /// assert_eq!(x.get(), 123);
112    /// ```
113    pub fn get(&self) -> T {
114        self.to_real_repr().value()
115    }
116}
117
118impl<T: NonMaxItem + Copy + PartialEq> NonMax<T> {
119    /// Returns `true` if this is the minimum value.
120    pub fn is_min(&self) -> bool {
121        self.get() == T::MIN_VALUE
122    }
123
124    /// Returns `true` if this is the maximum possible value for this type.
125    pub fn is_max(&self) -> bool {
126        self.get() == T::MAX_SAFE
127    }
128
129    /// Returns `true` if the value is zero.
130    pub fn is_zero(&self) -> bool {
131        self.get() == T::ZERO_VALUE
132    }
133}
134
135impl<T: NonMaxItem + Copy> NonMax<T> {
136    /// Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred
137    /// or if the result is the maximum value.
138    pub fn checked_add(self, rhs: Self) -> Option<Self> {
139        self.get().checked_add(rhs.get()).and_then(Self::new)
140    }
141
142    /// Checked integer subtraction. Computes `self - rhs`, returning `None` if overflow occurred
143    /// or if the result is the maximum value.
144    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
145        self.get().checked_sub(rhs.get()).and_then(Self::new)
146    }
147
148    /// Checked integer multiplication. Computes `self * rhs`, returning `None` if overflow occurred
149    /// or if the result is the maximum value.
150    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
151        self.get().checked_mul(rhs.get()).and_then(Self::new)
152    }
153
154    /// Checked integer division. Computes `self / rhs`, returning `None` if the divisor is zero
155    /// or if the result is the maximum value.
156    pub fn checked_div(self, rhs: Self) -> Option<Self> {
157        self.get().checked_div(rhs.get()).and_then(Self::new)
158    }
159
160    /// Checked integer remainder. Computes `self % rhs`, returning `None` if the divisor is zero
161    /// or if the result is the maximum value.
162    pub fn checked_rem(self, rhs: Self) -> Option<Self> {
163        self.get().checked_rem(rhs.get()).and_then(Self::new)
164    }
165
166    /// Checked addition with a primitive value.
167    ///
168    /// # Examples
169    /// ```
170    /// # use arithmetic_nonmax::NonMaxU8;
171    /// let x = NonMaxU8::new(100).unwrap();
172    /// assert_eq!(x.checked_add_val(50).unwrap().get(), 150);
173    /// assert!(x.checked_add_val(155).is_none()); // 255 is MAX
174    /// ```
175    pub fn checked_add_val(self, rhs: T) -> Option<Self> {
176        self.get().checked_add(rhs).and_then(Self::new)
177    }
178
179    /// Checked subtraction with a primitive value.
180    pub fn checked_sub_val(self, rhs: T) -> Option<Self> {
181        self.get().checked_sub(rhs).and_then(Self::new)
182    }
183
184    /// Checked multiplication with a primitive value.
185    pub fn checked_mul_val(self, rhs: T) -> Option<Self> {
186        self.get().checked_mul(rhs).and_then(Self::new)
187    }
188
189    /// Checked division with a primitive value.
190    pub fn checked_div_val(self, rhs: T) -> Option<Self> {
191        self.get().checked_div(rhs).and_then(Self::new)
192    }
193
194    /// Checked remainder with a primitive value.
195    pub fn checked_rem_val(self, rhs: T) -> Option<Self> {
196        self.get().checked_rem(rhs).and_then(Self::new)
197    }
198}
199
200impl<T: NonMaxItem + Copy + Add<Output = T>> Add for NonMax<T> {
201    type Output = Self;
202    fn add(self, rhs: Self) -> Self::Output {
203        self.checked_add(rhs)
204            .expect("attempt to add with overflow or to maximum value")
205    }
206}
207
208impl<T: NonMaxItem + Copy + Add<Output = T>> Add<T> for NonMax<T> {
209    type Output = Self;
210    fn add(self, rhs: T) -> Self::Output {
211        self.checked_add_val(rhs)
212            .expect("attempt to add with overflow or to maximum value")
213    }
214}
215
216impl<T: NonMaxItem + Copy + Add<Output = T>> AddAssign for NonMax<T> {
217    fn add_assign(&mut self, rhs: Self) {
218        *self = *self + rhs;
219    }
220}
221
222impl<T: NonMaxItem + Copy + Add<Output = T>> AddAssign<T> for NonMax<T> {
223    fn add_assign(&mut self, rhs: T) {
224        *self = *self + rhs;
225    }
226}
227
228impl<T: NonMaxItem + Copy + Sub<Output = T>> Sub for NonMax<T> {
229    type Output = Self;
230    fn sub(self, rhs: Self) -> Self::Output {
231        self.checked_sub(rhs)
232            .expect("attempt to subtract with overflow or to maximum value")
233    }
234}
235
236impl<T: NonMaxItem + Copy + Sub<Output = T>> Sub<T> for NonMax<T> {
237    type Output = Self;
238    fn sub(self, rhs: T) -> Self::Output {
239        self.checked_sub_val(rhs)
240            .expect("attempt to subtract with overflow or to maximum value")
241    }
242}
243
244impl<T: NonMaxItem + Copy + Sub<Output = T>> SubAssign for NonMax<T> {
245    fn sub_assign(&mut self, rhs: Self) {
246        *self = *self - rhs;
247    }
248}
249
250impl<T: NonMaxItem + Copy + Sub<Output = T>> SubAssign<T> for NonMax<T> {
251    fn sub_assign(&mut self, rhs: T) {
252        *self = *self - rhs;
253    }
254}
255
256impl<T: NonMaxItem + Copy + Mul<Output = T>> Mul for NonMax<T> {
257    type Output = Self;
258    fn mul(self, rhs: Self) -> Self::Output {
259        self.checked_mul(rhs)
260            .expect("attempt to multiply with overflow or to maximum value")
261    }
262}
263
264impl<T: NonMaxItem + Copy + Mul<Output = T>> Mul<T> for NonMax<T> {
265    type Output = Self;
266    fn mul(self, rhs: T) -> Self::Output {
267        self.checked_mul_val(rhs)
268            .expect("attempt to multiply with overflow or to maximum value")
269    }
270}
271
272impl<T: NonMaxItem + Copy + Mul<Output = T>> MulAssign for NonMax<T> {
273    fn mul_assign(&mut self, rhs: Self) {
274        *self = *self * rhs;
275    }
276}
277
278impl<T: NonMaxItem + Copy + Mul<Output = T>> MulAssign<T> for NonMax<T> {
279    fn mul_assign(&mut self, rhs: T) {
280        *self = *self * rhs;
281    }
282}
283
284impl<T: NonMaxItem + Copy + Div<Output = T>> Div for NonMax<T> {
285    type Output = Self;
286    fn div(self, rhs: Self) -> Self::Output {
287        self.checked_div(rhs)
288            .expect("attempt to divide by zero or to maximum value")
289    }
290}
291
292impl<T: NonMaxItem + Copy + Div<T, Output = T>> Div<T> for NonMax<T> {
293    type Output = Self;
294    fn div(self, rhs: T) -> Self::Output {
295        self.checked_div_val(rhs)
296            .expect("attempt to divide by zero or to maximum value")
297    }
298}
299
300impl<T: NonMaxItem + Copy + Div<Output = T>> DivAssign for NonMax<T> {
301    fn div_assign(&mut self, rhs: Self) {
302        *self = *self / rhs;
303    }
304}
305
306impl<T: NonMaxItem + Copy + Div<T, Output = T>> DivAssign<T> for NonMax<T> {
307    fn div_assign(&mut self, rhs: T) {
308        *self = *self / rhs;
309    }
310}
311
312impl<T: NonMaxItem + Copy + Rem<Output = T>> Rem for NonMax<T> {
313    type Output = Self;
314    fn rem(self, rhs: Self) -> Self::Output {
315        self.checked_rem(rhs)
316            .expect("attempt to calculate remainder by zero or to maximum value")
317    }
318}
319
320impl<T: NonMaxItem + Copy + Rem<T, Output = T>> Rem<T> for NonMax<T> {
321    type Output = Self;
322    fn rem(self, rhs: T) -> Self::Output {
323        self.checked_rem_val(rhs)
324            .expect("attempt to calculate remainder by zero or to maximum value")
325    }
326}
327
328impl<T: NonMaxItem + Copy + Rem<Output = T>> RemAssign for NonMax<T> {
329    fn rem_assign(&mut self, rhs: Self) {
330        *self = *self % rhs;
331    }
332}
333
334impl<T: NonMaxItem + Copy + Rem<T, Output = T>> RemAssign<T> for NonMax<T> {
335    fn rem_assign(&mut self, rhs: T) {
336        *self = *self % rhs;
337    }
338}
339
340impl<T: NonMaxItem + Copy + PartialOrd> PartialOrd for NonMax<T> {
341    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
342        self.to_real_repr().partial_cmp(&other.to_real_repr())
343    }
344}
345
346impl<T: NonMaxItem + Copy + Ord> Ord for NonMax<T> {
347    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
348        self.to_real_repr().cmp(&other.to_real_repr())
349    }
350}
351
352impl<T: NonMaxItem + Copy + Display> Display for NonMax<T> {
353    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354        Display::fmt(&self.get(), f)
355    }
356}
357
358impl<T: NonMaxItem + Copy + Binary> Binary for NonMax<T> {
359    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
360        Binary::fmt(&self.get(), f)
361    }
362}
363
364impl<T: NonMaxItem + Copy + Octal> Octal for NonMax<T> {
365    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
366        Octal::fmt(&self.get(), f)
367    }
368}
369
370impl<T: NonMaxItem + Copy + LowerHex> LowerHex for NonMax<T> {
371    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
372        LowerHex::fmt(&self.get(), f)
373    }
374}
375
376impl<T: NonMaxItem + Copy + UpperHex> UpperHex for NonMax<T> {
377    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
378        UpperHex::fmt(&self.get(), f)
379    }
380}
381
382impl<T: NonMaxItem + Copy> Default for NonMax<T> {
383    fn default() -> Self {
384        Self::new(T::ZERO_VALUE).unwrap()
385    }
386}
387
388#[doc(hidden)]
389pub trait NonMaxItem: Sized {
390    type NonZero: Copy + PartialEq + Eq + PartialOrd + Ord + Hash;
391    const MIN_VALUE: Self;
392    const MAX: Self;
393    const MAX_SAFE: Self;
394    const ZERO_VALUE: Self;
395    fn transform(self) -> Self;
396    fn to_nonzero(value: Value<Self, Inner>) -> Option<Self::NonZero>;
397    unsafe fn to_nonzero_unchecked(value: Value<Self, Inner>) -> Self::NonZero;
398    fn from_nonzero(value: Self::NonZero) -> Value<Self, Inner>;
399
400    fn checked_add(self, rhs: Self) -> Option<Self>;
401    fn checked_sub(self, rhs: Self) -> Option<Self>;
402    fn checked_mul(self, rhs: Self) -> Option<Self>;
403    fn checked_div(self, rhs: Self) -> Option<Self>;
404    fn checked_rem(self, rhs: Self) -> Option<Self>;
405
406    /// Creates a `NonMax` from this value without checking.
407    ///
408    /// # Safety
409    /// The value must not be the maximum value.
410    unsafe fn create_nonmax_unchecked(self) -> NonMax<Self>;
411}
412
413macro_rules! impl_non_max_item {
414    ($($t:ty, $name:ident, $doc:expr),*) => {
415        $(
416            impl NonMaxItem for $t {
417                type NonZero = NonZero<$t>;
418                const MIN_VALUE: Self = <$t>::MIN;
419                const MAX: Self = <$t>::MAX;
420                const MAX_SAFE: Self = <$t>::MAX - 1;
421                const ZERO_VALUE: Self = 0;
422                fn transform(self) -> Self {
423                    self ^ <$t>::MAX
424                }
425                fn to_nonzero(value: Value<Self, Inner>) -> Option<Self::NonZero> {
426                    Self::NonZero::new(value.value())
427                }
428                unsafe fn to_nonzero_unchecked(value: Value<Self, Inner>) -> Self::NonZero {
429                    unsafe { Self::NonZero::new_unchecked(value.value()) }
430                }
431                fn from_nonzero(value: Self::NonZero) -> Value<Self, Inner> {
432                    Value::new(value.get())
433                }
434
435                fn checked_add(self, rhs: Self) -> Option<Self> { self.checked_add(rhs) }
436                fn checked_sub(self, rhs: Self) -> Option<Self> { self.checked_sub(rhs) }
437                fn checked_mul(self, rhs: Self) -> Option<Self> { self.checked_mul(rhs) }
438                fn checked_div(self, rhs: Self) -> Option<Self> { self.checked_div(rhs) }
439                fn checked_rem(self, rhs: Self) -> Option<Self> { self.checked_rem(rhs) }
440
441                unsafe fn create_nonmax_unchecked(self) -> NonMax<Self> {
442                    unsafe { NonMax::<$t>::new_unchecked(self) }
443                }
444            }
445
446            impl From<NonMax<$t>> for $t {
447                fn from(value: NonMax<$t>) -> Self {
448                    value.get()
449                }
450            }
451
452            impl TryFrom<$t> for NonMax<$t> {
453                type Error = MaxValueError;
454
455                fn try_from(value: $t) -> Result<Self, Self::Error> {
456                    Self::new(value).ok_or(MaxValueError)
457                }
458            }
459
460            #[doc = $doc]
461            pub type $name = NonMax<$t>;
462
463            impl $name {
464                /// The minimum value for this type.
465                pub const MIN: Self = unsafe { Self(NonZero::new_unchecked(<$t>::MIN ^ <$t>::MAX)) };
466                /// The maximum value for this type.
467                pub const MAX: Self = unsafe { Self(NonZero::new_unchecked((<$t>::MAX - 1) ^ <$t>::MAX)) };
468                /// The zero value for this type.
469                pub const ZERO: Self = unsafe { Self(NonZero::new_unchecked(0 ^ <$t>::MAX)) };
470
471                /// Creates a new `NonMax` without checking the value.
472                ///
473                /// # Safety
474                /// The value must not be the maximum value of the underlying type.
475                pub const unsafe fn new_unchecked(value: $t) -> Self {
476                    Self(unsafe { NonZero::new_unchecked(value ^ <$t>::MAX) })
477                }
478            }
479        )*
480    };
481}
482
483impl_non_max_item!(
484    u8,
485    NonMaxU8,
486    "An unsigned 8-bit integer that cannot be `u8::MAX`.",
487    u16,
488    NonMaxU16,
489    "An unsigned 16-bit integer that cannot be `u16::MAX`.",
490    u32,
491    NonMaxU32,
492    "An unsigned 32-bit integer that cannot be `u32::MAX`.",
493    u64,
494    NonMaxU64,
495    "An unsigned 64-bit integer that cannot be `u64::MAX`.",
496    u128,
497    NonMaxU128,
498    "An unsigned 128-bit integer that cannot be `u128::MAX`.",
499    usize,
500    NonMaxUsize,
501    "An unsigned pointer-sized integer that cannot be `usize::MAX`.",
502    i8,
503    NonMaxI8,
504    "A signed 8-bit integer that cannot be `i8::MAX`.",
505    i16,
506    NonMaxI16,
507    "A signed 16-bit integer that cannot be `i16::MAX`.",
508    i32,
509    NonMaxI32,
510    "A signed 32-bit integer that cannot be `i32::MAX`.",
511    i64,
512    NonMaxI64,
513    "A signed 64-bit integer that cannot be `i64::MAX`.",
514    i128,
515    NonMaxI128,
516    "A signed 128-bit integer that cannot be `i128::MAX`.",
517    isize,
518    NonMaxIsize,
519    "A signed pointer-sized integer that cannot be `isize::MAX`."
520);
521
522#[doc(hidden)]
523#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
524pub struct Real;
525#[doc(hidden)]
526#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
527pub struct Inner;
528
529#[doc(hidden)]
530#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
531pub struct Value<T, M> {
532    value: T,
533    _marker: PhantomData<M>,
534}
535
536impl<T: NonMaxItem + Copy, M> Value<T, M> {
537    fn new(value: T) -> Self {
538        Self {
539            value,
540            _marker: PhantomData,
541        }
542    }
543
544    fn value(&self) -> T {
545        self.value
546    }
547}
548
549impl<T: NonMaxItem + Copy> Value<T, Real> {
550    fn to_inner_repr(self) -> Value<T, Inner> {
551        Value::new(T::transform(self.value))
552    }
553}
554
555impl<T: NonMaxItem + Copy + Add<Output = T>> Add for Value<T, Real> {
556    type Output = Self;
557    fn add(self, rhs: Self) -> Self::Output {
558        Self::new(self.value() + rhs.value())
559    }
560}
561
562impl<T: NonMaxItem + Copy + Sub<Output = T>> Sub for Value<T, Real> {
563    type Output = Self;
564    fn sub(self, rhs: Self) -> Self::Output {
565        Self::new(self.value() - rhs.value())
566    }
567}
568
569impl<T: NonMaxItem + Copy + Mul<Output = T>> Mul for Value<T, Real> {
570    type Output = Self;
571    fn mul(self, rhs: Self) -> Self::Output {
572        Self::new(self.value() * rhs.value())
573    }
574}
575
576impl<T: NonMaxItem + Copy + Div<Output = T>> Div for Value<T, Real> {
577    type Output = Self;
578    fn div(self, rhs: Self) -> Self::Output {
579        Self::new(self.value() / rhs.value())
580    }
581}
582
583impl<T: NonMaxItem + Copy + Rem<Output = T>> Rem for Value<T, Real> {
584    type Output = Self;
585    fn rem(self, rhs: Self) -> Self::Output {
586        Self::new(self.value() % rhs.value())
587    }
588}
589
590impl<T: NonMaxItem + Copy> Value<T, Inner> {
591    fn to_real_repr(self) -> Value<T, Real> {
592        Value::new(T::transform(self.value))
593    }
594
595    fn to_nonmax(self) -> Option<NonMax<T>> {
596        T::to_nonzero(self).map(NonMax)
597    }
598}
599
600impl<T: NonMaxItem + Copy + PartialOrd> PartialOrd for Value<T, Real> {
601    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
602        self.value().partial_cmp(&other.value())
603    }
604}
605
606impl<T: NonMaxItem + Copy + Ord> Ord for Value<T, Real> {
607    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
608        self.value().cmp(&other.value())
609    }
610}
611
612impl<T: NonMaxItem + Copy + Display> Display for Value<T, Real> {
613    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
614        write!(f, "{}", self.value())
615    }
616}
617
618#[cfg(test)]
619mod tests {
620    extern crate std;
621    use super::*;
622    use core::mem::size_of;
623    use std::collections::HashSet;
624
625    #[test]
626    fn test_hash() {
627        let mut set = HashSet::new();
628        set.insert(NonMaxU32::new(1).unwrap());
629        set.insert(NonMaxU32::new(2).unwrap());
630        set.insert(NonMaxU32::new(1).unwrap());
631
632        assert_eq!(set.len(), 2);
633        assert!(set.contains(&NonMaxU32::new(1).unwrap()));
634    }
635
636    #[test]
637    fn test_sizes() {
638        assert_eq!(size_of::<NonMaxU32>(), 4);
639        assert_eq!(size_of::<Option<NonMaxU32>>(), 4);
640
641        assert_eq!(size_of::<NonMaxI32>(), 4);
642        assert_eq!(size_of::<Option<NonMaxI32>>(), 4);
643
644        assert_eq!(size_of::<NonMaxU8>(), 1);
645        assert_eq!(size_of::<Option<NonMaxU8>>(), 1);
646    }
647
648    #[test]
649    fn test_conversions() {
650        let x = NonMaxU8::try_from(100).unwrap();
651        assert_eq!(u8::from(x), 100);
652
653        let max_val = u8::MAX;
654        assert!(NonMaxU8::try_from(max_val).is_err());
655    }
656
657    #[test]
658    fn test_arithmetic_with_val() {
659        let x = NonMaxU8::new(100).unwrap();
660        let y = x + 50;
661        assert_eq!(u8::from(y), 150);
662
663        let mut z = NonMaxU8::new(10).unwrap();
664        z += 20;
665        assert_eq!(u8::from(z), 30);
666
667        let a = NonMaxU8::new(10).unwrap();
668        let b = a * 5;
669        assert_eq!(u8::from(b), 50);
670
671        let c = NonMaxU8::new(100).unwrap();
672        let d = c / 3;
673        assert_eq!(u8::from(d), 33);
674    }
675
676    #[test]
677    fn test_add_overflow() {
678        let x = NonMaxU8::try_from(250).unwrap();
679        // Now it should return None instead of panicking
680        assert!(x.checked_add_val(10).is_none());
681    }
682
683    #[test]
684    fn test_add_to_max() {
685        let x = NonMaxU8::try_from(250).unwrap();
686        // Result is 255 (MAX), so it should return None
687        assert!(x.checked_add_val(5).is_none());
688    }
689
690    #[test]
691    fn test_signed_integer() {
692        // i8: -128 to 127. MAX is 127.
693        let x = NonMaxI8::try_from(100).unwrap();
694        let y = x + 20;
695        assert_eq!(i8::from(y), 120);
696
697        let z = NonMaxI8::try_from(-50).unwrap();
698        let w = z + 10;
699        assert_eq!(i8::from(w), -40);
700
701        // MIN (-128) is allowed
702        let min_val = NonMaxI8::try_from(i8::MIN).unwrap();
703        assert_eq!(i8::from(min_val), -128);
704    }
705
706    #[test]
707    fn test_signed_overflow() {
708        let x = NonMaxI8::try_from(120).unwrap();
709        // Overflow detected
710        assert!(x.checked_add_val(10).is_none());
711    }
712
713    #[test]
714    fn test_signed_to_max() {
715        let x = NonMaxI8::try_from(120).unwrap();
716        // Result is 127 (MAX), so None
717        assert!(x.checked_add_val(7).is_none());
718    }
719
720    #[test]
721    fn test_formatting() {
722        let x = NonMaxU8::new(254).unwrap();
723        assert_eq!(std::format!("{}", x), "254");
724        assert_eq!(std::format!("{:b}", x), "11111110");
725        assert_eq!(std::format!("{:o}", x), "376");
726        assert_eq!(std::format!("{:x}", x), "fe");
727        assert_eq!(std::format!("{:X}", x), "FE");
728    }
729
730    #[test]
731    fn test_min_max_constants() {
732        assert_eq!(NonMaxU8::MIN.get(), 0);
733        assert_eq!(NonMaxU8::MAX.get(), 254);
734        assert!(NonMaxU8::MIN.is_min());
735        assert!(NonMaxU8::MAX.is_max());
736        assert!(!NonMaxU8::MIN.is_max());
737        assert!(!NonMaxU8::MAX.is_min());
738
739        assert_eq!(NonMaxI8::MIN.get(), -128);
740        assert_eq!(NonMaxI8::MAX.get(), 126);
741    }
742
743    #[test]
744    fn test_zero_constant() {
745        assert_eq!(NonMaxU8::ZERO.get(), 0);
746        assert!(NonMaxU8::ZERO.is_zero());
747        assert_eq!(NonMaxI32::ZERO.get(), 0);
748        assert!(NonMaxI32::ZERO.is_zero());
749    }
750
751    #[test]
752    fn test_non_max_macro() {
753        let x = non_max!(123u8);
754        assert_eq!(x.get(), 123);
755
756        let y = non_max!(456u32);
757        assert_eq!(y.get(), 456);
758
759        let z = non_max!(-10i32);
760        assert_eq!(z.get(), -10);
761    }
762}