i24/types/
i24.rs

1#[cfg(feature = "alloc")]
2extern crate alloc;
3
4#[cfg(feature = "alloc")]
5use alloc::vec::Vec;
6use bytemuck::{NoUninit, Pod, Zeroable};
7use core::fmt;
8use core::fmt::{Debug, Display, LowerHex, Octal, UpperHex};
9use core::hash::{Hash, Hasher};
10use core::num::ParseIntError;
11use core::ops::{
12    Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
13    Mul, MulAssign, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
14};
15use core::{
16    ops::{Neg, Not},
17    str::FromStr,
18};
19
20#[cfg(feature = "num-cast")]
21use num_traits::{FromPrimitive, Num, NumCast, One, Signed, ToBytes, ToPrimitive, Zero};
22#[cfg(not(feature = "num-cast"))]
23use num_traits::{FromPrimitive, Num, One, Signed, ToBytes, ToPrimitive, Zero};
24
25use crate::repr::I24Repr;
26use crate::{TryFromIntError, i24, out_of_range};
27
28#[allow(non_camel_case_types)]
29#[repr(transparent)]
30#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
31/// A signed 24-bit integer type.
32///
33/// The `I24` type represents a 24-bit signed integer in two's complement format. It fills the gap between
34/// `i16` and `i32`, offering reduced memory usage while preserving a larger numeric range than `i16`.
35///
36/// This type is particularly suited to applications such as audio processing, binary file manipulation,
37/// embedded systems, or networking protocols where 24-bit integers are common.
38///
39/// ## Range
40///
41/// The valid value range is:
42///
43/// ```text
44///   MIN = -8_388_608   // -2^23
45///   MAX =  8_388_607   //  2^23 - 1
46/// ```
47///
48/// Arithmetic operations are implemented to match Rust’s standard integer behavior,
49/// including overflow and checked variants.
50///
51/// ## Memory Layout and Safety
52///
53/// `I24` is implemented as a `#[repr(transparent)]` wrapper around a 4-byte internal representation.
54/// Although the logical width is 24 bits, one additional byte is used for alignment and padding control.
55///
56/// This struct:
57///
58/// - Contains **no uninitialized or padding bytes**
59/// - Is **safe to use** with [`bytemuck::NoUninit`](https://docs.rs/bytemuck/latest/bytemuck/trait.NoUninit.html)
60/// - Can be cast safely with [`bytemuck::cast_slice`] for use in FFI and low-level applications
61///
62/// The layout is verified via compile-time assertions to ensure portability and correctness.
63///
64/// ## Binary Serialization
65///
66/// Although `I24` occupies 4 bytes in memory, binary formats (e.g., WAV files, network protocols) often
67/// store 24-bit integers in a 3-byte representation. To support this:
68///
69/// - `I24` provides [`I24::from_be_bytes`], [`I24::from_le_bytes`], and [`I24::from_ne_bytes`] methods for constructing
70///   values from 3-byte on-disk representations
71/// - Corresponding [`I24::to_be_bytes`], [`I24::to_le_bytes`], and [`I24::to_ne_bytes`] methods convert to 3-byte representations
72///
73/// For efficient bulk deserialization, use the inherent methods on `I24`.
74///
75///  Note: Requires the ``alloc`` feature to be enabled.
76///
77/// ```
78/// # #[cfg(feature = "alloc")] {
79/// use i24::I24;
80/// let raw: &[u8] = &[0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF];
81/// let values = I24::read_i24s_be(raw).expect("Test value should convert successfully");
82/// assert_eq!(values[0].to_i32(), 1);
83/// assert_eq!(values[1].to_i32(), -1);
84/// # }
85/// ```
86///
87/// ## Usage Notes
88///
89/// - Use the `i24!` macro for compile-time checked construction
90/// - Use `.to_i32()` to convert to standard Rust types
91/// - Supports traits like `Add`, `Sub`, `Display`, `Hash`, `Ord`, and `FromStr`
92///
93/// ## Features
94///
95/// - **`serde`**: Enables `Serialize` and `Deserialize` support via JSON or other formats
96/// - **`pyo3`**: Exposes the `I24` type to Python via PyO3 bindings (as `I24`)
97/// - **`alloc`**: Enables `I24DiskMethods` for efficient bulk serialization/deserialization
98pub struct I24(I24Repr);
99
100// Safety: repr(transparent) and so if I24Repr is Zeroable so should I24 be
101unsafe impl Zeroable for I24 where I24Repr: Zeroable {}
102
103// Safety: repr(transparent) and so if I24Repr is NoUninit so should I24 be
104// Must be NoUninit due to the padding byte.
105unsafe impl NoUninit for I24 where I24Repr: NoUninit {}
106
107impl FromPrimitive for I24
108where
109    I24Repr: FromPrimitive,
110{
111    #[inline]
112    fn from_i64(n: i64) -> Option<Self> {
113        I24Repr::from_i64(n).map(Self)
114    }
115
116    #[inline]
117    fn from_u64(n: u64) -> Option<Self> {
118        I24Repr::from_u64(n).map(Self)
119    }
120}
121
122impl I24 {
123    /// The size of this integer type in bits
124    pub const BITS: u32 = 24;
125
126    /// The smallest value that can be represented by this integer type (-2<sup>23</sup>)
127    pub const MIN: I24 = i24!(I24Repr::MIN);
128
129    /// The largest value that can be represented by this integer type (2<sup>23</sup> − 1).
130    pub const MAX: I24 = i24!(I24Repr::MAX);
131
132    /// Creates a new `I24` with all bits set to zero.
133    pub const ZERO: I24 = i24!(I24Repr::ZERO);
134
135    /// Returns a reference to the underlying 32-bit representation.
136    ///
137    /// The 24-bit value is stored in the lower 24 bits, with the upper 8 bits guaranteed to be zero.
138    /// This method provides direct access to the internal bit representation for advanced use cases.
139    #[inline]
140    pub const fn as_bits(&self) -> &u32 {
141        self.0.as_bits()
142    }
143
144    /// Returns the underlying 32-bit representation.
145    ///
146    /// The 24-bit value is stored in the lower 24 bits, with the upper 8 bits guaranteed to be zero.
147    /// This method extracts the internal bit representation for advanced use cases.
148    #[inline]
149    pub const fn to_bits(self) -> u32 {
150        self.0.to_bits()
151    }
152
153    /// Safety: see `I24Repr::from_bits`
154    #[inline]
155    const unsafe fn from_bits(bits: u32) -> I24 {
156        Self(unsafe { I24Repr::from_bits(bits) })
157    }
158
159    /// same as `Self::from_bits` but always truncates
160    #[inline]
161    const fn from_bits_truncate(bits: u32) -> I24 {
162        // the most significant byte is zeroed out
163        Self(unsafe { I24Repr::from_bits(bits & I24Repr::BITS_MASK) })
164    }
165
166    /// Converts the 24-bit integer to a 32-bit signed integer.
167    ///
168    /// This method performs sign extension if the 24-bit integer is negative.
169    ///
170    /// # Returns
171    ///
172    /// The 32-bit signed integer representation of this `I24`.
173    #[inline]
174    pub const fn to_i32(self) -> i32 {
175        self.0.to_i32()
176    }
177
178    /// Creates an `I24` from a 32-bit signed integer.
179    ///
180    /// This method truncates the input to 24 bits if it's outside the valid range.
181    ///
182    /// # Arguments
183    ///
184    /// * `n` - The 32-bit signed integer to convert.
185    ///
186    /// # Returns
187    ///
188    /// An `I24` instance representing the input value.
189    #[inline]
190    pub const fn wrapping_from_i32(n: i32) -> Self {
191        Self(I24Repr::wrapping_from_i32(n))
192    }
193
194    /// Creates an `I24` from a 32-bit signed integer.
195    ///
196    /// This method saturates the input if it's outside the valid range.
197    ///
198    /// # Arguments
199    ///
200    /// * `n` - The 32-bit signed integer to convert.
201    ///
202    /// # Returns
203    ///
204    /// An `I24` instance representing the input value.
205    #[inline]
206    pub const fn saturating_from_i32(n: i32) -> Self {
207        Self(I24Repr::saturating_from_i32(n))
208    }
209
210    /// Reverses the byte order of the integer.
211    #[inline]
212    pub const fn swap_bytes(self) -> Self {
213        Self(self.0.swap_bytes())
214    }
215
216    /// Converts self to little endian from the target's endianness.
217    /// On little endian this is a no-op. On big endian the bytes are swapped.
218    #[inline]
219    pub const fn to_le(self) -> Self {
220        Self(self.0.to_le())
221    }
222
223    /// Converts self to big endian from the target's endianness.
224    /// On big endian this is a no-op. On little endian the bytes are swapped.
225    #[inline]
226    pub const fn to_be(self) -> Self {
227        Self(self.0.to_be())
228    }
229
230    /// Return the memory representation of this integer as a byte array in native byte order.
231    /// As the target platform's native endianness is used,
232    /// portable code should use to_be_bytes or to_le_bytes, as appropriate, instead.
233    #[inline]
234    pub const fn to_ne_bytes(self) -> [u8; 3] {
235        self.0.to_ne_bytes()
236    }
237
238    /// Create a native endian integer value from its representation as a byte array in little endian.
239    #[inline]
240    pub const fn to_le_bytes(self) -> [u8; 3] {
241        self.0.to_le_bytes()
242    }
243
244    /// Return the memory representation of this integer as a byte array in big-endian (network) byte order.
245    #[inline]
246    pub const fn to_be_bytes(self) -> [u8; 3] {
247        self.0.to_be_bytes()
248    }
249
250    /// Creates an `I24` from three bytes in **native endian** order.
251    ///
252    /// # Arguments
253    ///
254    /// * `bytes` - An array of 3 bytes representing the 24-bit integer.
255    ///
256    /// # Returns
257    ///
258    /// An `I24` instance containing the input bytes.
259    #[inline]
260    pub const fn from_ne_bytes(bytes: [u8; 3]) -> Self {
261        Self(I24Repr::from_ne_bytes(bytes))
262    }
263
264    /// Creates an `I24` from three bytes in **little-endian** order.
265    ///
266    /// # Arguments
267    ///
268    /// * `bytes` - An array of 3 bytes representing the 24-bit integer in little-endian order.
269    ///
270    /// # Returns
271    ///
272    /// An `I24` instance containing the input bytes.
273    #[inline]
274    pub const fn from_le_bytes(bytes: [u8; 3]) -> Self {
275        Self(I24Repr::from_le_bytes(bytes))
276    }
277
278    /// Creates an `I24` from three bytes in **big-endian** order.
279    ///
280    /// # Arguments
281    ///
282    /// * `bytes` - An array of 3 bytes representing the 24-bit integer in big-endian order.
283    ///
284    /// # Returns
285    ///
286    /// An `I24` instance containing the input bytes interpreted as big-endian.
287    #[inline]
288    pub const fn from_be_bytes(bytes: [u8; 3]) -> Self {
289        Self(I24Repr::from_be_bytes(bytes))
290    }
291
292    /// Performs checked addition.
293    ///
294    /// # Arguments
295    ///
296    /// * `other` - The `I24` to add to this value.
297    ///
298    /// # Returns
299    ///
300    /// `Some(I24)` if the addition was successful, or `None` if it would overflow.
301    pub fn checked_add(self, other: Self) -> Option<Self> {
302        self.to_i32()
303            .checked_add(other.to_i32())
304            .and_then(Self::try_from_i32)
305    }
306
307    /// Performs checked subtraction.
308    ///
309    /// # Arguments
310    ///
311    /// * `other` - The `I24` to subtract from this value.
312    ///
313    /// # Returns
314    ///
315    /// `Some(I24)` if the subtraction was successful, or `None` if it would overflow.
316    pub fn checked_sub(self, other: Self) -> Option<Self> {
317        self.to_i32()
318            .checked_sub(other.to_i32())
319            .and_then(Self::try_from_i32)
320    }
321
322    /// Performs checked multiplication.
323    ///
324    /// # Arguments
325    ///
326    /// * `other` - The `I24` to multiply with this value.
327    ///
328    /// # Returns
329    ///
330    /// `Some(I24)` if the multiplication was successful, or `None` if it would overflow.
331    pub fn checked_mul(self, other: Self) -> Option<Self> {
332        self.to_i32()
333            .checked_mul(other.to_i32())
334            .and_then(Self::try_from_i32)
335    }
336
337    /// Performs checked division.
338    ///
339    /// # Arguments
340    ///
341    /// * `other` - The `I24` to divide this value by.
342    ///
343    /// # Returns
344    ///
345    /// `Some(I24)` if the division was successful, or `None` if the divisor is zero or if the division would overflow.
346    pub fn checked_div(self, other: Self) -> Option<Self> {
347        self.to_i32()
348            .checked_div(other.to_i32())
349            .and_then(Self::try_from_i32)
350    }
351
352    /// Performs checked integer remainder.
353    ///
354    /// # Arguments
355    ///
356    /// * `other` - The `I24` to divide this value by.
357    ///
358    /// # Returns
359    ///
360    /// `Some(I24)` if the remainder operation was successful, or `None` if the divisor is zero or if the division would overflow.
361    pub fn checked_rem(self, other: Self) -> Option<Self> {
362        self.to_i32()
363            .checked_rem(other.to_i32())
364            .and_then(Self::try_from_i32)
365    }
366
367    /// Performs checked negation.
368    ///
369    /// # Returns
370    ///
371    /// `Some(I24)` if the negation was successful, or `None` if it would overflow.
372    /// This happens when negating `I24::MIN` (since `-I24::MIN` cannot be represented in 24 bits).
373    pub fn checked_neg(self) -> Option<Self> {
374        self.to_i32().checked_neg().and_then(Self::try_from_i32)
375    }
376
377    /// Computes the absolute value of `self`.
378    ///
379    /// # Returns
380    ///
381    /// The absolute value of `self`. Note that `I24::MIN.abs()` will overflow and wrap to `I24::MIN`.
382    ///
383    /// # Examples
384    ///
385    /// ```
386    /// use i24::I24;;
387    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").abs(), I24::try_from_i32(10).expect("Test value should convert successfully"));
388    /// assert_eq!(I24::try_from_i32(-10).expect("Test value should convert successfully").abs(), I24::try_from_i32(10).expect("Test value should convert successfully"));
389    /// ```
390    #[inline]
391    pub fn abs(self) -> Self {
392        if self.to_i32() < 0 { -self } else { self }
393    }
394
395    /// Computes the absolute value of `self` without any wrapping or panicking.
396    ///
397    /// # Returns
398    ///
399    /// The absolute value of `self`. For `I24::MIN`, returns `I24::MIN` (wrapping behavior).
400    ///
401    /// # Examples
402    ///
403    /// ```
404    /// use i24::I24;;
405    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").wrapping_abs(), I24::try_from_i32(10).expect("Test value should convert successfully"));
406    /// assert_eq!(I24::try_from_i32(-10).expect("Test value should convert successfully").wrapping_abs(), I24::try_from_i32(10).expect("Test value should convert successfully"));
407    /// assert_eq!(I24::MIN.wrapping_abs(), I24::MIN); // Wraps around
408    /// ```
409    #[inline]
410    pub fn wrapping_abs(self) -> Self {
411        if self.to_i32() < 0 { -self } else { self }
412    }
413
414    /// Computes the absolute value of `self`, saturating at the bounds instead of overflowing.
415    ///
416    /// # Returns
417    ///
418    /// The absolute value of `self`. For `I24::MIN`, returns `I24::MAX` (saturating behavior).
419    ///
420    /// # Examples
421    ///
422    /// ```
423    /// use i24::I24;;
424    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").saturating_abs(), I24::try_from_i32(10).expect("Test value should convert successfully"));
425    /// assert_eq!(I24::try_from_i32(-10).expect("Test value should convert successfully").saturating_abs(), I24::try_from_i32(10).expect("Test value should convert successfully"));
426    /// assert_eq!(I24::MIN.saturating_abs(), I24::MAX); // Saturates to MAX
427    /// ```
428    #[inline]
429    pub fn saturating_abs(self) -> Self {
430        if self == Self::MIN {
431            Self::MAX
432        } else if self.to_i32() < 0 {
433            -self
434        } else {
435            self
436        }
437    }
438
439    /// Returns a number representing the sign of `self`.
440    ///
441    /// # Returns
442    ///
443    /// * `1` if `self` is positive
444    /// * `0` if `self` is zero
445    /// * `-1` if `self` is negative
446    ///
447    /// # Examples
448    ///
449    /// ```
450    /// use i24::I24;;
451    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").signum(), I24::try_from_i32(1).expect("Test value should convert successfully"));
452    /// assert_eq!(I24::try_from_i32(0).expect("Test value should convert successfully").signum(), I24::try_from_i32(0).expect("Test value should convert successfully"));
453    /// assert_eq!(I24::try_from_i32(-10).expect("Test value should convert successfully").signum(), I24::try_from_i32(-1).expect("Test value should convert successfully"));
454    /// ```
455    #[inline]
456    pub fn signum(self) -> Self {
457        let val = self.to_i32();
458        match val.cmp(&0) {
459            core::cmp::Ordering::Greater => Self::one(),
460            core::cmp::Ordering::Less => -Self::one(),
461            core::cmp::Ordering::Equal => Self::zero(),
462        }
463    }
464
465    /// Returns `true` if `self` is negative and `false` if the number is zero or positive.
466    ///
467    /// # Examples
468    ///
469    /// ```
470    /// use i24::I24;;
471    /// assert!(!I24::try_from_i32(10).expect("Test value should convert successfully").is_negative());
472    /// assert!(!I24::try_from_i32(0).expect("Test value should convert successfully").is_negative());
473    /// assert!(I24::try_from_i32(-10).expect("Test value should convert successfully").is_negative());
474    /// ```
475    #[inline]
476    pub const fn is_negative(self) -> bool {
477        self.to_i32() < 0
478    }
479
480    /// Returns `true` if `self` is positive and `false` if the number is zero or negative.
481    ///
482    /// # Examples
483    ///
484    /// ```
485    /// use i24::I24;;
486    /// assert!(I24::try_from_i32(10).expect("Test value should convert successfully").is_positive());
487    /// assert!(!I24::try_from_i32(0).expect("Test value should convert successfully").is_positive());
488    /// assert!(!I24::try_from_i32(-10).expect("Test value should convert successfully").is_positive());
489    /// ```
490    #[inline]
491    pub const fn is_positive(self) -> bool {
492        self.to_i32() > 0
493    }
494
495    /// The positive difference of two numbers.
496    ///
497    /// Returns zero if the number is less than or equal to the other,
498    /// otherwise the difference between `self` and `other` is returned.
499    ///
500    /// # Examples
501    ///
502    /// ```
503    /// use i24::I24;;
504    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").abs_sub(&I24::try_from_i32(5).expect("Test value should convert successfully")), I24::try_from_i32(5).expect("Test value should convert successfully"));
505    /// assert_eq!(I24::try_from_i32(5).expect("Test value should convert successfully").abs_sub(&I24::try_from_i32(10).expect("Test value should convert successfully")), I24::try_from_i32(0).expect("Test value should convert successfully"));
506    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").abs_sub(&I24::try_from_i32(10).expect("Test value should convert successfully")), I24::try_from_i32(0).expect("Test value should convert successfully"));
507    /// ```
508    #[inline]
509    pub fn abs_sub(&self, other: &Self) -> Self {
510        if *self <= *other {
511            Self::zero()
512        } else {
513            *self - *other
514        }
515    }
516
517    /// Restricts the value to a certain interval.
518    ///
519    /// Returns `max` if `self` is greater than `max`, and `min` if `self` is less than `min`.
520    /// Otherwise, returns `self`.
521    ///
522    /// # Panics
523    ///
524    /// Panics if `min > max`.
525    ///
526    /// # Examples
527    ///
528    /// ```
529    /// use i24::I24;;
530    /// assert_eq!(I24::try_from_i32(-3).expect("Test value should convert successfully").clamp(I24::try_from_i32(-2).expect("Test value should convert successfully"), I24::try_from_i32(1).expect("Test value should convert successfully")), I24::try_from_i32(-2).expect("Test value should convert successfully"));
531    /// assert_eq!(I24::try_from_i32(0).expect("Test value should convert successfully").clamp(I24::try_from_i32(-2).expect("Test value should convert successfully"), I24::try_from_i32(1).expect("Test value should convert successfully")), I24::try_from_i32(0).expect("Test value should convert successfully"));
532    /// assert_eq!(I24::try_from_i32(2).expect("Test value should convert successfully").clamp(I24::try_from_i32(-2).expect("Test value should convert successfully"), I24::try_from_i32(1).expect("Test value should convert successfully")), I24::try_from_i32(1).expect("Test value should convert successfully"));
533    /// ```
534    #[inline]
535    pub fn clamp(self, min: Self, max: Self) -> Self {
536        assert!(min <= max);
537        if self < min {
538            min
539        } else if self > max {
540            max
541        } else {
542            self
543        }
544    }
545
546    /// Computes the minimum of `self` and `other`.
547    ///
548    /// # Examples
549    ///
550    /// ```
551    /// use i24::I24;;
552    /// assert_eq!(I24::try_from_i32(1).expect("Test value should convert successfully").min(I24::try_from_i32(2).expect("Test value should convert successfully")), I24::try_from_i32(1).expect("Test value should convert successfully"));
553    /// assert_eq!(I24::try_from_i32(2).expect("Test value should convert successfully").min(I24::try_from_i32(1).expect("Test value should convert successfully")), I24::try_from_i32(1).expect("Test value should convert successfully"));
554    /// ```
555    #[inline]
556    pub fn min(self, other: Self) -> Self {
557        if self <= other { self } else { other }
558    }
559
560    /// Computes the maximum of `self` and `other`.
561    ///
562    /// # Examples
563    ///
564    /// ```
565    /// use i24::I24;;
566    /// assert_eq!(I24::try_from_i32(1).expect("Test value should convert successfully").max(I24::try_from_i32(2).expect("Test value should convert successfully")), I24::try_from_i32(2).expect("Test value should convert successfully"));
567    /// assert_eq!(I24::try_from_i32(2).expect("Test value should convert successfully").max(I24::try_from_i32(1).expect("Test value should convert successfully")), I24::try_from_i32(2).expect("Test value should convert successfully"));
568    /// ```
569    #[inline]
570    pub fn max(self, other: Self) -> Self {
571        if self >= other { self } else { other }
572    }
573
574    /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at the boundary of the type.
575    ///
576    /// # Examples
577    ///
578    /// ```
579    /// use i24::I24;;
580    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").wrapping_add(I24::try_from_i32(27).expect("Test value should convert successfully")), I24::try_from_i32(127).expect("Test value should convert successfully"));
581    /// assert_eq!(I24::MAX.wrapping_add(I24::try_from_i32(2).expect("Test value should convert successfully")), I24::MIN + I24::try_from_i32(1).expect("Test value should convert successfully"));
582    /// ```
583    #[inline]
584    pub fn wrapping_add(self, rhs: Self) -> Self {
585        self + rhs // Addition already wraps
586    }
587
588    /// Wrapping (modular) subtraction. Computes `self - rhs`, wrapping around at the boundary of the type.
589    ///
590    /// # Examples
591    ///
592    /// ```
593    /// use i24::I24;;
594    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").wrapping_sub(I24::try_from_i32(100).expect("Test value should convert successfully")), I24::try_from_i32(0).expect("Test value should convert successfully"));
595    /// assert_eq!(I24::MIN.wrapping_sub(I24::try_from_i32(1).expect("Test value should convert successfully")), I24::MAX);
596    /// ```
597    #[inline]
598    pub fn wrapping_sub(self, rhs: Self) -> Self {
599        self - rhs // Subtraction already wraps
600    }
601
602    /// Wrapping (modular) multiplication. Computes `self * rhs`, wrapping around at the boundary of the type.
603    ///
604    /// # Examples
605    ///
606    /// ```
607    /// use i24::I24;;
608    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").wrapping_mul(I24::try_from_i32(12).expect("Test value should convert successfully")), I24::try_from_i32(120).expect("Test value should convert successfully"));
609    /// assert_eq!(I24::try_from_i32(25).expect("Test value should convert successfully").wrapping_mul(I24::try_from_i32(4).expect("Test value should convert successfully")), I24::try_from_i32(100).expect("Test value should convert successfully"));
610    /// ```
611    #[inline]
612    pub fn wrapping_mul(self, rhs: Self) -> Self {
613        self * rhs // Multiplication already wraps
614    }
615
616    /// Wrapping (modular) division. Computes `self / rhs`, wrapping around at the boundary of the type.
617    ///
618    /// The only case where wrapping can occur is when dividing the minimum value by -1, which would
619    /// overflow but instead wraps to the minimum value.
620    ///
621    /// # Panics
622    ///
623    /// This function will panic if `rhs` is 0.
624    ///
625    /// # Examples
626    ///
627    /// ```
628    /// use i24::I24;;
629    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").wrapping_div(I24::try_from_i32(10).expect("Test value should convert successfully")), I24::try_from_i32(10).expect("Test value should convert successfully"));
630    /// assert_eq!(I24::MIN.wrapping_div(I24::try_from_i32(-1).expect("Test value should convert successfully")), I24::MIN); // Wraps around
631    /// ```
632    #[inline]
633    pub fn wrapping_div(self, rhs: Self) -> Self {
634        self / rhs // Division already wraps for the MIN / -1 case
635    }
636
637    /// Wrapping (modular) remainder. Computes `self % rhs`, wrapping around at the boundary of the type.
638    ///
639    /// The only case where wrapping can occur is when computing the remainder of the minimum value
640    /// divided by -1, which would overflow but instead wraps to 0.
641    ///
642    /// # Panics
643    ///
644    /// This function will panic if `rhs` is 0.
645    ///
646    /// # Examples
647    ///
648    /// ```
649    /// use i24::I24;;
650    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").wrapping_rem(I24::try_from_i32(10).expect("Test value should convert successfully")), I24::try_from_i32(0).expect("Test value should convert successfully"));
651    /// assert_eq!(I24::MIN.wrapping_rem(I24::try_from_i32(-1).expect("Test value should convert successfully")), I24::try_from_i32(0).expect("Test value should convert successfully")); // Wraps around
652    /// ```
653    #[inline]
654    pub fn wrapping_rem(self, rhs: Self) -> Self {
655        self % rhs // Remainder already wraps for the MIN % -1 case
656    }
657
658    /// Wrapping (modular) negation. Computes `-self`, wrapping around at the boundary of the type.
659    ///
660    /// # Examples
661    ///
662    /// ```
663    /// use i24::I24;;
664    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").wrapping_neg(), I24::try_from_i32(-100).expect("Test value should convert successfully"));
665    /// assert_eq!(I24::MIN.wrapping_neg(), I24::MIN); // Wraps around
666    /// ```
667    #[inline]
668    pub fn wrapping_neg(self) -> Self {
669        -self // Negation already wraps
670    }
671
672    /// Saturating integer addition. Computes `self + rhs`, saturating at the numeric bounds.
673    ///
674    /// # Examples
675    ///
676    /// ```
677    /// use i24::I24;;
678    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").saturating_add(I24::try_from_i32(1).expect("Test value should convert successfully")), I24::try_from_i32(101).expect("Test value should convert successfully"));
679    /// assert_eq!(I24::MAX.saturating_add(I24::try_from_i32(1).expect("Test value should convert successfully")), I24::MAX);
680    /// assert_eq!(I24::MIN.saturating_add(I24::try_from_i32(-1).expect("Test value should convert successfully")), I24::MIN);
681    /// ```
682    #[inline]
683    pub fn saturating_add(self, rhs: Self) -> Self {
684        self.to_i32()
685            .saturating_add(rhs.to_i32())
686            .try_into()
687            .unwrap_or_else(|_| {
688                if rhs.to_i32() > 0 {
689                    Self::MAX
690                } else {
691                    Self::MIN
692                }
693            })
694    }
695
696    /// Saturating integer subtraction. Computes `self - rhs`, saturating at the numeric bounds.
697    ///
698    /// # Examples
699    ///
700    /// ```
701    /// use i24::I24;;
702    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").saturating_sub(I24::try_from_i32(127).expect("Test value should convert successfully")), I24::try_from_i32(-27).expect("Test value should convert successfully"));
703    /// assert_eq!(I24::MIN.saturating_sub(I24::try_from_i32(1).expect("Test value should convert successfully")), I24::MIN);
704    /// assert_eq!(I24::MAX.saturating_sub(I24::try_from_i32(-1).expect("Test value should convert successfully")), I24::MAX);
705    /// ```
706    #[inline]
707    pub fn saturating_sub(self, rhs: Self) -> Self {
708        self.to_i32()
709            .saturating_sub(rhs.to_i32())
710            .try_into()
711            .unwrap_or_else(|_| {
712                if rhs.to_i32() < 0 {
713                    Self::MAX
714                } else {
715                    Self::MIN
716                }
717            })
718    }
719
720    /// Saturating integer multiplication. Computes `self * rhs`, saturating at the numeric bounds.
721    ///
722    /// # Examples
723    ///
724    /// ```
725    /// use i24::I24;;
726    /// assert_eq!(I24::try_from_i32(10).expect("Test value should convert successfully").saturating_mul(I24::try_from_i32(12).expect("Test value should convert successfully")), I24::try_from_i32(120).expect("Test value should convert successfully"));
727    /// assert_eq!(I24::try_from_i32(1000000).expect("Test value should convert successfully").saturating_mul(I24::try_from_i32(10).expect("Test value should convert successfully")), I24::MAX);
728    /// assert_eq!(I24::try_from_i32(-1000000).expect("Test value should convert successfully").saturating_mul(I24::try_from_i32(10).expect("Test value should convert successfully")), I24::MIN);
729    /// ```
730    #[inline]
731    pub const fn saturating_mul(self, rhs: Self) -> Self {
732        let result = self.to_i32().saturating_mul(rhs.to_i32());
733        Self::saturating_from_i32(result)
734    }
735
736    /// Saturating integer division. Computes `self / rhs`, saturating at the numeric bounds.
737    ///
738    /// The only case where saturation can occur is when dividing the minimum value by -1,
739    /// which would overflow but instead saturates to the maximum value.
740    ///
741    /// # Panics
742    ///
743    /// This function will panic if `rhs` is 0.
744    ///
745    /// # Examples
746    ///
747    /// ```
748    /// use i24::I24;;
749    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").saturating_div(I24::try_from_i32(10).expect("Test value should convert successfully")), I24::try_from_i32(10).expect("Test value should convert successfully"));
750    /// assert_eq!(I24::MIN.saturating_div(I24::try_from_i32(-1).expect("Test value should convert successfully")), I24::MAX); // Saturates
751    /// ```
752    #[inline]
753    pub fn saturating_div(self, rhs: Self) -> Self {
754        if self == Self::MIN && rhs.to_i32() == -1 {
755            Self::MAX
756        } else {
757            self / rhs
758        }
759    }
760
761    /// Saturating integer negation. Computes `-self`, saturating at the numeric bounds.
762    ///
763    /// # Examples
764    ///
765    /// ```
766    /// use i24::I24;
767    /// assert_eq!(I24::try_from_i32(100).expect("Test value should convert successfully").saturating_neg(), I24::try_from_i32(-100).expect("Test value should convert successfully"));
768    /// assert_eq!(I24::MIN.saturating_neg(), I24::MAX); // Saturates
769    /// ```
770    #[inline]
771    pub fn saturating_neg(self) -> Self {
772        if self == Self::MIN { Self::MAX } else { -self }
773    }
774
775    /// Converts a byte slice in little-endian order to a Vec of I24 values.
776    ///
777    /// This method interprets the input byte slice as a sequence of 24-bit unsigned integers
778    /// stored in little-endian format (least significant byte first). Each `I24` value
779    /// requires exactly 3 bytes.
780    ///
781    /// # Arguments
782    ///
783    /// * `bytes` - A byte slice containing 24-bit unsigned integers in little-endian format.
784    ///   The length must be a multiple of 3.
785    ///
786    /// # Returns
787    ///
788    /// `Some(Vec<I24>)` containing the parsed values, or `None` if the input slice
789    /// length is not a multiple of 3.
790    #[cfg(feature = "alloc")]
791    pub fn read_i24s_le(bytes: &[u8]) -> Option<Vec<I24>> {
792        if !bytes.len().is_multiple_of(3) {
793            return None;
794        }
795
796        let mut result = Vec::with_capacity(bytes.len() / 3);
797
798        bytes.chunks_exact(3).for_each(|chunk| {
799            result.push(I24::from_le_bytes([chunk[0], chunk[1], chunk[2]]));
800        });
801
802        Some(result)
803    }
804
805    /// Converts a byte slice in little-endian order to a slice of I24 values.
806    ///
807    /// This method interprets the input byte slice as a sequence of 24-bit integers
808    /// stored in little-endian format. Each `I24` value requires exactly 3 bytes.
809    ///
810    /// # Arguments
811    ///
812    /// * `bytes` - A byte slice containing 24-bit integers in little-endian format.
813    ///   The length must be a multiple of 3.
814    ///
815    /// # Returns
816    ///
817    /// `Some(&[I24])` containing a slice view of the parsed values, or `None` if the input slice
818    /// length is not a multiple of 3.
819    #[cfg(feature = "alloc")]
820    pub const fn read_i24s_le_slice(bytes: &[u8]) -> Option<&[I24]> {
821        if !bytes.len().is_multiple_of(3) {
822            return None;
823        }
824
825        let result =
826            unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const I24, bytes.len() / 3) };
827
828        Some(result)
829    }
830
831    /// Converts a byte slice in big-endian order to a Vec of I24 values.
832    ///
833    /// This method interprets the input byte slice as a sequence of 24-bit unsigned integers
834    /// stored in big-endian format (most significant byte first). Each `I24` value
835    /// requires exactly 3 bytes.
836    ///
837    /// # Arguments
838    ///
839    /// * `bytes` - A byte slice containing 24-bit unsigned integers in big-endian format.
840    ///   The length must be a multiple of 3.
841    ///
842    /// # Returns
843    ///
844    /// `Some(Vec<I24>)` containing the parsed values, or `None` if the input slice
845    /// length is not a multiple of 3.
846    #[cfg(feature = "alloc")]
847    pub fn read_i24s_be(bytes: &[u8]) -> Option<Vec<I24>> {
848        if !bytes.len().is_multiple_of(3) {
849            return None;
850        }
851
852        let mut result = Vec::with_capacity(bytes.len() / 3);
853
854        bytes.chunks_exact(3).for_each(|chunk| {
855            result.push(I24::from_be_bytes([chunk[0], chunk[1], chunk[2]]));
856        });
857
858        Some(result)
859    }
860
861    /// Reads multiple `I24` values from a byte slice in little-endian format without length validation.
862    ///
863    /// # Safety
864    ///
865    /// The input slice length must be a multiple of 3 bytes. Undefined behavior will occur
866    /// if this precondition is not met.
867    ///
868    /// # Arguments
869    ///
870    /// * `bytes` - A byte slice whose length must be a multiple of 3
871    ///
872    /// # Returns
873    ///
874    /// A vector containing the parsed `I24` values.
875    #[cfg(feature = "alloc")]
876    pub unsafe fn read_i24s_le_unchecked(bytes: &[u8]) -> Vec<I24> {
877        debug_assert!(bytes.len().is_multiple_of(3));
878        let chunks: &[I24Bytes] = bytemuck::cast_slice(bytes);
879        chunks.iter().map(|b| b.to_i24_le()).collect()
880    }
881
882    #[cfg(feature = "alloc")]
883    /// Reads multiple `I24` values from a byte slice in little-endian format without length validation.
884    ///
885    /// # Safety
886    ///
887    /// The caller must ensure that `bytes.len()` is a multiple of 3.
888    pub unsafe fn read_i24s_le_slice_unchecked(bytes: &[u8]) -> &[I24] {
889        debug_assert!(bytes.len().is_multiple_of(3));
890        unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const I24, bytes.len() / 3) }
891    }
892
893    /// Reads multiple `I24` values from a byte slice in big-endian format without length validation.
894    ///
895    /// # Safety
896    ///
897    /// The input slice length must be a multiple of 3 bytes. Undefined behavior will occur
898    /// if this precondition is not met.
899    ///
900    /// # Arguments
901    ///
902    /// * `bytes` - A byte slice whose length must be a multiple of 3
903    ///
904    /// # Returns
905    ///
906    /// A vector containing the parsed `I24` values.
907    #[cfg(feature = "alloc")]
908    pub unsafe fn read_i24s_be_unchecked(bytes: &[u8]) -> Vec<I24> {
909        debug_assert!(bytes.len().is_multiple_of(3));
910        let chunks: &[I24Bytes] = bytemuck::cast_slice(bytes);
911        chunks.iter().map(|b| b.to_i24_be()).collect()
912    }
913
914    /// Converts a slice of I24 values to a Vec of bytes in little-endian order.
915    #[cfg(feature = "alloc")]
916    pub fn write_i24s_le(values: &[I24]) -> Vec<u8> {
917        let mut bytes = Vec::with_capacity(values.len() * 3);
918        for &value in values {
919            bytes.extend_from_slice(&value.to_le_bytes());
920        }
921        bytes
922    }
923
924    /// Converts a slice of I24 values to a Vec of bytes in big-endian order.
925    #[cfg(feature = "alloc")]
926    pub fn write_i24s_be(values: &[I24]) -> Vec<u8> {
927        let mut bytes = Vec::with_capacity(values.len() * 3);
928        for &value in values {
929            bytes.extend_from_slice(&value.to_be_bytes());
930        }
931        bytes
932    }
933
934    /// Reads multiple `I24` values from a byte slice in native-endian format.
935    ///
936    /// # Arguments
937    ///
938    /// * `bytes` - A byte slice whose length must be a multiple of 3
939    ///
940    /// # Returns
941    ///
942    /// `Some(Vec<I24>)` containing the parsed values, or `None` if the input slice
943    /// length is not a multiple of 3.
944    #[cfg(feature = "alloc")]
945    pub fn read_i24s_ne(bytes: &[u8]) -> Option<Vec<I24>> {
946        if !bytes.len().is_multiple_of(3) {
947            return None;
948        }
949
950        let mut result = Vec::with_capacity(bytes.len() / 3);
951
952        bytes.chunks_exact(3).for_each(|chunk| {
953            result.push(I24::from_ne_bytes([chunk[0], chunk[1], chunk[2]]));
954        });
955
956        Some(result)
957    }
958
959    /// Converts a slice of I24 values to a Vec of bytes in native-endian order.
960    #[cfg(feature = "alloc")]
961    pub fn write_i24s_ne(values: &[I24]) -> Vec<u8> {
962        let mut bytes = Vec::with_capacity(values.len() * 3);
963        for &value in values {
964            bytes.extend_from_slice(&value.to_ne_bytes());
965        }
966        bytes
967    }
968}
969
970macro_rules! impl_bin_op {
971    ($(impl $op: ident = $assign: ident $assign_fn: ident { $($impl: tt)* })+) => {$(
972        impl_bin_op!(impl $op = $assign $assign_fn for I24 { $($impl)* });
973        impl_bin_op!(impl $op = $assign $assign_fn for &I24 { $($impl)* });
974    )+};
975
976    (impl $op: ident = $assign: ident $assign_fn: ident for $ty:ty {
977         fn $meth: ident($self: tt, $other: ident) {
978            $($impl: tt)*
979         }
980    }) => {
981        impl $op<$ty> for I24 {
982            type Output = Self;
983
984            #[inline]
985            fn $meth($self, $other: $ty) -> Self {
986                $($impl)*
987            }
988        }
989
990        impl $op<$ty> for &I24 {
991            type Output = I24;
992
993            #[inline]
994            fn $meth(self, other: $ty) -> I24 {
995                <I24 as $op<$ty>>::$meth(*self, other)
996            }
997        }
998
999        impl $assign<$ty> for I24 {
1000            #[inline]
1001            fn $assign_fn(&mut self, rhs: $ty) {
1002                *self = $op::$meth(*self, rhs)
1003            }
1004        }
1005    };
1006}
1007
1008impl_bin_op! {
1009    impl Add = AddAssign add_assign {
1010        fn add(self, other) {
1011            // we use two's complement and so signed and unsigned addition are strictly the same
1012            // so no need to cast to an i32
1013            Self::from_bits_truncate(self.to_bits().wrapping_add(other.to_bits()))
1014        }
1015    }
1016
1017    impl Sub = SubAssign sub_assign {
1018        fn sub(self, other) {
1019            // we use two's complement and so signed and unsigned subtraction are strictly the same
1020            // so no need to cast to an i32
1021            Self::from_bits_truncate(self.to_bits().wrapping_sub(other.to_bits()))
1022        }
1023    }
1024
1025    impl Mul = MulAssign mul_assign {
1026        fn mul(self, other) {
1027            // we use two's complement and so signed and unsigned non-widening multiplication are strictly the same
1028            // so no need to cast to an i32
1029            Self::from_bits_truncate(self.to_bits().wrapping_mul(other.to_bits()))
1030        }
1031    }
1032
1033    impl Div = DivAssign div_assign {
1034        fn div(self, other) {
1035            let other_val = unsafe { I24::from_bits(other.to_bits()) };
1036            let result = <I24>::to_i32(self).wrapping_div(<I24>::to_i32(other_val));
1037            Self::wrapping_from_i32(result)
1038        }
1039    }
1040
1041    impl Rem = RemAssign rem_assign {
1042        fn rem(self, other) {
1043            let other_val = unsafe { I24::from_bits(other.to_bits()) };
1044            let result = <I24>::to_i32(self).wrapping_rem(<I24>::to_i32(other_val));
1045            Self::wrapping_from_i32(result)
1046        }
1047    }
1048
1049
1050    impl BitAnd = BitAndAssign bitand_assign {
1051        fn bitand(self, rhs) {
1052            let bits = self.to_bits() & rhs.to_bits();
1053            // Safety:
1054            // since we and 2 values that both have the most significant byte set to zero
1055            // the output will always have the most significant byte set to zero
1056            unsafe { I24::from_bits(bits) }
1057        }
1058    }
1059
1060    impl BitOr = BitOrAssign bitor_assign {
1061        fn bitor(self, rhs) {
1062            let bits = self.to_bits() | rhs.to_bits();
1063            // Safety:
1064            // since we and 2 values that both have the most significant byte set to zero
1065            // the output will always have the most significant byte set to zero
1066            unsafe { I24::from_bits(bits) }
1067        }
1068    }
1069
1070    impl BitXor = BitXorAssign bitxor_assign {
1071        fn bitxor(self, rhs) {
1072            let bits = self.to_bits() ^ rhs.to_bits();
1073            // Safety:
1074            // since we and 2 values that both have the most significant byte set to zero
1075            // the output will always have the most significant byte set to zero
1076            unsafe { I24::from_bits(bits) }
1077        }
1078    }
1079}
1080
1081impl Hash for I24 {
1082    fn hash<H: Hasher>(&self, state: &mut H) {
1083        I24Repr::hash(&self.0, state)
1084    }
1085
1086    fn hash_slice<H: Hasher>(data: &[Self], state: &mut H)
1087    where
1088        Self: Sized,
1089    {
1090        // I24 is repr(transparent) and compile-time assertions above ensure safety
1091        I24Repr::hash_slice(
1092            unsafe { core::mem::transmute::<&[Self], &[I24Repr]>(data) },
1093            state,
1094        )
1095    }
1096}
1097
1098macro_rules! impl_from {
1099    ($($ty: ty : $func_name: ident),+ $(,)?) => {$(
1100        impl From<$ty> for I24 {
1101            fn from(value: $ty) -> Self {
1102                Self::$func_name(value)
1103            }
1104        }
1105
1106        impl I24 {
1107            #[doc = concat!("Creates an `I24` from a `", stringify!($ty), "` value.")]
1108            ///
1109            /// This conversion is guaranteed to succeed as the source type fits within the 24-bit range.
1110            pub const fn $func_name(value: $ty) -> Self {
1111                Self(I24Repr::$func_name(value))
1112            }
1113        }
1114    )+};
1115}
1116
1117macro_rules! impl_try {
1118    ($($ty: ty : $func_name: ident),+ $(,)?) => {$(
1119        impl TryFrom<$ty> for I24 {
1120            type Error = TryFromIntError;
1121
1122            fn try_from(value: $ty) -> Result<Self, Self::Error> {
1123                Self::$func_name(value).ok_or_else(out_of_range)
1124            }
1125        }
1126
1127        impl I24 {
1128            #[doc = concat!("Tries to create an `I24` from a `", stringify!($ty), "` value.")]
1129            ///
1130            /// Returns `Some(I24)` if the value fits within the 24-bit signed range [-8,388,608, 8,388,607],
1131            /// or `None` if the value is out of range.
1132            pub const fn $func_name(value: $ty) -> Option<Self> {
1133                match I24Repr::$func_name(value) {
1134                    Some(x) => Some(Self(x)),
1135                    None => None
1136                }
1137            }
1138        }
1139    )+};
1140}
1141
1142impl_from! {
1143    u8: from_u8,
1144    u16: from_u16,
1145    bool: from_bool,
1146
1147    i8: from_i8,
1148    i16: from_i16,
1149}
1150
1151impl_try! {
1152    u32 : try_from_u32,
1153    u64 : try_from_u64,
1154    u128: try_from_u128,
1155
1156    i32 : try_from_i32,
1157    i64 : try_from_i64,
1158    i128: try_from_i128,
1159}
1160
1161// From<I24> for larger integer types (always succeeds)
1162impl From<I24> for i32 {
1163    #[inline]
1164    fn from(value: I24) -> Self {
1165        value.to_i32()
1166    }
1167}
1168
1169impl From<I24> for i64 {
1170    #[inline]
1171    fn from(value: I24) -> Self {
1172        value.to_i32() as i64
1173    }
1174}
1175
1176impl From<I24> for i128 {
1177    #[inline]
1178    fn from(value: I24) -> Self {
1179        value.to_i32() as i128
1180    }
1181}
1182
1183impl From<I24> for isize {
1184    #[inline]
1185    fn from(value: I24) -> Self {
1186        value.to_i32() as isize
1187    }
1188}
1189
1190impl One for I24 {
1191    fn one() -> Self {
1192        i24!(1)
1193    }
1194}
1195
1196impl Zero for I24 {
1197    #[inline]
1198    fn zero() -> Self {
1199        Self::zeroed()
1200    }
1201
1202    #[inline]
1203    fn is_zero(&self) -> bool {
1204        Self::zeroed() == *self
1205    }
1206}
1207
1208pub const fn from_str_error(bad_val: &str) -> ParseIntError {
1209    match i8::from_str_radix(bad_val, 10) {
1210        Err(err) => err,
1211        Ok(_) => unreachable!(),
1212    }
1213}
1214
1215const POSITIVE_OVERFLOW: ParseIntError = from_str_error("9999999999999999999999999999999999999999");
1216
1217const NEGATIVE_OVERFLOW: ParseIntError =
1218    from_str_error("-9999999999999999999999999999999999999999");
1219
1220macro_rules! from_str {
1221    ($meth: ident($($args: tt)*)) => {
1222        i32::$meth($($args)*)
1223            .and_then(|x| I24::try_from_i32(x).ok_or_else(|| {
1224                if x < 0 {
1225                    NEGATIVE_OVERFLOW
1226                } else {
1227                    POSITIVE_OVERFLOW
1228                }
1229            }))
1230    };
1231}
1232
1233impl Num for I24 {
1234    type FromStrRadixErr = ParseIntError;
1235    fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
1236        from_str!(from_str_radix(str, radix))
1237    }
1238}
1239
1240impl FromStr for I24 {
1241    type Err = ParseIntError;
1242
1243    fn from_str(str: &str) -> Result<Self, Self::Err> {
1244        from_str!(from_str(str))
1245    }
1246}
1247
1248impl Neg for I24 {
1249    type Output = Self;
1250
1251    #[inline]
1252    fn neg(self) -> Self {
1253        // this is how you negate two's complement numbers
1254        I24::from_bits_truncate((!self.to_bits()) + 1)
1255    }
1256}
1257
1258impl Signed for I24 {
1259    #[inline]
1260    fn abs(&self) -> Self {
1261        (*self).abs()
1262    }
1263
1264    #[inline]
1265    fn abs_sub(&self, other: &Self) -> Self {
1266        (*self).abs_sub(other)
1267    }
1268
1269    #[inline]
1270    fn signum(&self) -> Self {
1271        (*self).signum()
1272    }
1273
1274    #[inline]
1275    fn is_positive(&self) -> bool {
1276        (*self).is_positive()
1277    }
1278
1279    #[inline]
1280    fn is_negative(&self) -> bool {
1281        (*self).is_negative()
1282    }
1283}
1284
1285impl Not for I24 {
1286    type Output = Self;
1287
1288    #[inline]
1289    fn not(self) -> Self {
1290        I24::from_bits_truncate(!self.to_bits())
1291    }
1292}
1293
1294impl Shl<u32> for I24 {
1295    type Output = Self;
1296
1297    #[inline]
1298    fn shl(self, rhs: u32) -> Self::Output {
1299        // Match Rust's standard behavior: mask shift count to bit width
1300        let n = rhs % 24;
1301        Self::from_bits_truncate(self.to_bits() << n)
1302    }
1303}
1304
1305impl Shr<u32> for I24 {
1306    type Output = Self;
1307
1308    #[inline]
1309    fn shr(self, rhs: u32) -> Self::Output {
1310        // Match Rust's standard behavior: mask shift count to bit width
1311        let n = rhs % 24;
1312
1313        // Safety:
1314        // we do a logical shift right by 8 at the end
1315        // and so the most significant octet/byte is set to 0
1316
1317        // logic:
1318        // <8 bits empty> <I24 sign bit> <rest>
1319        // we shift everything up by 8
1320        // <I24 sign bit on i32 sign bit> <rest> <8 bits empty>
1321        // then we do an arithmetic shift
1322        // <sign bit * n> <I24 sign bit> <rest> <8 - n bits empty>
1323        // after we shift everything down by 8
1324        // <8 bits empty> <sign bit * n> <sign bit> <first 23 - n bits of rest>
1325        unsafe { Self::from_bits(((self.to_bits() << 8) as i32 >> n) as u32 >> 8) }
1326    }
1327}
1328
1329impl ShrAssign<u32> for I24 {
1330    #[inline]
1331    fn shr_assign(&mut self, rhs: u32) {
1332        *self = Shr::shr(*self, rhs)
1333    }
1334}
1335
1336impl ShlAssign<u32> for I24 {
1337    #[inline]
1338    fn shl_assign(&mut self, rhs: u32) {
1339        *self = Shl::shl(*self, rhs)
1340    }
1341}
1342
1343macro_rules! impl_fmt {
1344    ($(impl $name: path)+) => {$(
1345        impl $name for I24 {
1346            #[inline]
1347            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1348                <i32 as $name>::fmt(&I24::to_i32(*self), f)
1349            }
1350        }
1351    )*};
1352}
1353
1354macro_rules! impl_bits_fmt {
1355    ($(impl $name: path)+) => {$(
1356        impl $name for I24 {
1357            #[inline]
1358            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1359                <u32 as $name>::fmt(self.as_bits(), f)
1360            }
1361        }
1362    )*};
1363}
1364
1365impl_fmt! {
1366    impl Display
1367    impl Debug
1368}
1369
1370// Custom hex formatting for 24-bit width (6 digits)
1371impl UpperHex for I24 {
1372    #[inline]
1373    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1374        write!(f, "{:06X}", self.to_bits() & 0x00FF_FFFF)
1375    }
1376}
1377
1378impl LowerHex for I24 {
1379    #[inline]
1380    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1381        write!(f, "{:06x}", self.to_bits() & 0x00FF_FFFF)
1382    }
1383}
1384
1385impl_bits_fmt! {
1386    impl Octal
1387    impl fmt::Binary
1388}
1389
1390#[cfg(feature = "serde")]
1391mod serde {
1392    impl serde::Serialize for crate::I24 {
1393        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1394        where
1395            S: serde::Serializer,
1396        {
1397            serializer.serialize_i32(self.to_i32())
1398        }
1399    }
1400
1401    impl<'de> serde::Deserialize<'de> for crate::I24 {
1402        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1403        where
1404            D: serde::Deserializer<'de>,
1405        {
1406            deserializer.deserialize_any(I24Visitor)
1407        }
1408    }
1409
1410    struct I24Visitor;
1411
1412    macro_rules! impl_deserialize_infallible {
1413        ($([$ty: path, $visit: ident, $from: ident])+) => {$(
1414            fn $visit<E>(self, v: $ty) -> Result<Self::Value, E> {
1415                Ok(crate::I24::$from(v))
1416            }
1417        )*};
1418    }
1419
1420    macro_rules! impl_deserialize_fallible {
1421        ($([$ty: path, $visit: ident, $try_from: ident])+) => {$(
1422            fn $visit<E>(self, v: $ty) -> Result<Self::Value, E> where E: serde::de::Error {
1423                crate::I24::$try_from(v).ok_or_else(|| E::custom("I24 out of range!"))
1424            }
1425        )*};
1426    }
1427
1428    impl serde::de::Visitor<'_> for I24Visitor {
1429        type Value = crate::I24;
1430
1431        fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
1432            formatter.write_str("an integer between -2^23 and 2^23")
1433        }
1434
1435        impl_deserialize_infallible! {
1436            [u8, visit_u8, from_u8]
1437            [i8, visit_i8, from_i8]
1438            [u16, visit_u16, from_u16]
1439            [i16, visit_i16, from_i16]
1440        }
1441
1442        impl_deserialize_fallible! {
1443            [u32, visit_u32, try_from_u32]
1444            [i32, visit_i32, try_from_i32]
1445            [u64, visit_u64, try_from_u64]
1446            [i64, visit_i64, try_from_i64]
1447            [u128, visit_u128, try_from_u128]
1448            [i128, visit_i128, try_from_i128]
1449        }
1450    }
1451}
1452
1453// Compile-time assertions to ensure transmute safety
1454const _: () = assert!(core::mem::size_of::<I24>() == core::mem::size_of::<I24Repr>());
1455const _: () = assert!(core::mem::align_of::<I24>() == core::mem::align_of::<I24Repr>());
1456
1457impl ToBytes for I24 {
1458    type Bytes = [u8; 3];
1459
1460    fn to_be_bytes(&self) -> Self::Bytes {
1461        self.0.to_be_bytes()
1462    }
1463
1464    fn to_le_bytes(&self) -> Self::Bytes {
1465        self.0.to_le_bytes()
1466    }
1467}
1468
1469impl ToPrimitive for I24 {
1470    #[inline]
1471    fn to_i64(&self) -> Option<i64> {
1472        Some(I24::to_i32(*self) as i64)
1473    }
1474
1475    #[inline]
1476    fn to_u64(&self) -> Option<u64> {
1477        let val = I24::to_i32(*self);
1478        if val < 0 { None } else { Some(val as u64) }
1479    }
1480
1481    #[inline]
1482    fn to_i32(&self) -> Option<i32> {
1483        Some(I24::to_i32(*self))
1484    }
1485
1486    #[inline]
1487    fn to_u32(&self) -> Option<u32> {
1488        let val = I24::to_i32(*self);
1489        if val < 0 { None } else { Some(val as u32) }
1490    }
1491
1492    #[inline]
1493    fn to_i16(&self) -> Option<i16> {
1494        let val = I24::to_i32(*self);
1495        if val < i16::MIN as i32 || val > i16::MAX as i32 {
1496            None
1497        } else {
1498            Some(val as i16)
1499        }
1500    }
1501
1502    #[inline]
1503    fn to_u16(&self) -> Option<u16> {
1504        let val = I24::to_i32(*self);
1505        if val < 0 || val > u16::MAX as i32 {
1506            None
1507        } else {
1508            Some(val as u16)
1509        }
1510    }
1511
1512    #[inline]
1513    fn to_i8(&self) -> Option<i8> {
1514        let val = I24::to_i32(*self);
1515        if val < i8::MIN as i32 || val > i8::MAX as i32 {
1516            None
1517        } else {
1518            Some(val as i8)
1519        }
1520    }
1521
1522    #[inline]
1523    fn to_u8(&self) -> Option<u8> {
1524        let val = I24::to_i32(*self);
1525        if val < 0 || val > u8::MAX as i32 {
1526            None
1527        } else {
1528            Some(val as u8)
1529        }
1530    }
1531
1532    #[inline]
1533    fn to_isize(&self) -> Option<isize> {
1534        Some(I24::to_i32(*self) as isize)
1535    }
1536
1537    #[inline]
1538    fn to_usize(&self) -> Option<usize> {
1539        let val = I24::to_i32(*self);
1540        if val < 0 { None } else { Some(val as usize) }
1541    }
1542
1543    #[inline]
1544    fn to_f32(&self) -> Option<f32> {
1545        Some(I24::to_i32(*self) as f32)
1546    }
1547
1548    #[inline]
1549    fn to_f64(&self) -> Option<f64> {
1550        Some(I24::to_i32(*self) as f64)
1551    }
1552
1553    #[inline]
1554    fn to_i128(&self) -> Option<i128> {
1555        Some(I24::to_i32(*self) as i128)
1556    }
1557
1558    #[inline]
1559    fn to_u128(&self) -> Option<u128> {
1560        let val = I24::to_i32(*self);
1561        if val < 0 { None } else { Some(val as u128) }
1562    }
1563}
1564
1565/// Implementation of the `NumCast` trait for `I24`.
1566///
1567/// This allows safe casting from any type that implements `ToPrimitive`
1568/// to `I24`. The conversion returns `None` if the source value cannot
1569/// be represented within the 24-bit signed integer range [-8,388,608, 8,388,607].
1570#[cfg(feature = "num-cast")]
1571impl NumCast for I24 {
1572    /// Converts a value of type `T` to `I24`.
1573    ///
1574    /// # Arguments
1575    ///
1576    /// * `n` - The value to convert, which must implement `ToPrimitive`.
1577    ///
1578    /// # Returns
1579    ///
1580    /// * `Some(I24)` if the conversion succeeds and the value is within range.
1581    /// * `None` if the conversion fails or the value is out of range.
1582    ///
1583    /// # Examples
1584    ///
1585    /// ```
1586    /// use i24::I24;
1587    /// use num_traits::NumCast;
1588    ///
1589    /// // Successful conversions
1590    /// assert_eq!(<I24 as NumCast>::from(1000i32), Some(I24::try_from_i32(1000).unwrap()));
1591    /// assert_eq!(<I24 as NumCast>::from(500u16), Some(I24::try_from_i32(500).unwrap()));
1592    ///
1593    /// // Out of range conversions
1594    /// assert_eq!(<I24 as NumCast>::from(10_000_000i32), None);
1595    /// assert_eq!(<I24 as NumCast>::from(-10_000_000i32), None);
1596    /// ```
1597    #[inline]
1598    fn from<T: ToPrimitive>(n: T) -> Option<Self> {
1599        n.to_i64().and_then(Self::try_from_i64)
1600    }
1601}
1602
1603#[cfg(feature = "ndarray")]
1604mod ndarray_support {
1605    impl ndarray::ScalarOperand for crate::I24 {}
1606    impl ndarray::ScalarOperand for crate::I24Bytes {}
1607}
1608
1609#[cfg(feature = "pyo3")]
1610pub(crate) mod python {
1611    extern crate std;
1612
1613    use crate::I24;
1614    use numpy::{Element, PyArrayDescr};
1615    use pyo3::Py;
1616    use pyo3::{
1617        conversion::{FromPyObject, IntoPyObject},
1618        prelude::*,
1619        sync::PyOnceLock,
1620    };
1621
1622    /// Python wrapper for the `I24` type.
1623    ///
1624    /// This struct provides Python bindings for the 24-bit signed integer type,
1625    /// allowing `I24` values to be used from Python code via PyO3.
1626    #[pyclass(name = "I24", frozen)]
1627    pub struct PyI24 {
1628        /// The wrapped `I24` value.
1629        pub value: I24,
1630    }
1631
1632    impl PyI24 {
1633        /// Creates a new `PyI24` instance from an `I24` value.
1634        pub fn new(value: I24) -> Self {
1635            PyI24 { value }
1636        }
1637    }
1638
1639    #[pymethods]
1640    impl PyI24 {
1641        #[new]
1642        fn py_new(value: i32) -> PyResult<Self> {
1643            match I24::try_from_i32(value) {
1644                Some(v) => Ok(PyI24 { value: v }),
1645                None => Err(pyo3::exceptions::PyValueError::new_err(format!(
1646                    "Value {} is out of range for I24 (-8388608 to 8388607)",
1647                    value
1648                ))),
1649            }
1650        }
1651
1652        #[staticmethod]
1653        fn from_bytes(bytes: &[u8], byteorder: &str) -> PyResult<Self> {
1654            if bytes.len() != 3 {
1655                return Err(pyo3::exceptions::PyValueError::new_err(
1656                    "bytes must be exactly 3 bytes long",
1657                ));
1658            }
1659            let byte_array: [u8; 3] = [bytes[0], bytes[1], bytes[2]];
1660            let value = match byteorder {
1661                "little" => I24::from_le_bytes(byte_array),
1662                "big" => I24::from_be_bytes(byte_array),
1663                "native" => I24::from_ne_bytes(byte_array),
1664                _ => {
1665                    return Err(pyo3::exceptions::PyValueError::new_err(
1666                        "byteorder must be 'little', 'big', or 'native'",
1667                    ));
1668                }
1669            };
1670            Ok(PyI24 { value })
1671        }
1672
1673        fn to_int(&self) -> i32 {
1674            self.value.to_i32()
1675        }
1676
1677        fn to_bytes(&self, byteorder: &str) -> PyResult<Vec<u8>> {
1678            let bytes = match byteorder {
1679                "little" => self.value.to_le_bytes(),
1680                "big" => self.value.to_be_bytes(),
1681                "native" => self.value.to_ne_bytes(),
1682                _ => {
1683                    return Err(pyo3::exceptions::PyValueError::new_err(
1684                        "byteorder must be 'little', 'big', or 'native'",
1685                    ));
1686                }
1687            };
1688            Ok(bytes.to_vec())
1689        }
1690
1691        fn __str__(&self) -> String {
1692            format!("{}", self.value.to_i32())
1693        }
1694
1695        fn __repr__(&self) -> String {
1696            format!("I24({})", self.value.to_i32())
1697        }
1698
1699        fn __int__(&self) -> i32 {
1700            self.value.to_i32()
1701        }
1702
1703        // Comparison operators
1704        fn __eq__(&self, other: &PyI24) -> bool {
1705            self.value == other.value
1706        }
1707
1708        fn __ne__(&self, other: &PyI24) -> bool {
1709            self.value != other.value
1710        }
1711
1712        fn __lt__(&self, other: &PyI24) -> bool {
1713            self.value < other.value
1714        }
1715
1716        fn __le__(&self, other: &PyI24) -> bool {
1717            self.value <= other.value
1718        }
1719
1720        fn __gt__(&self, other: &PyI24) -> bool {
1721            self.value > other.value
1722        }
1723
1724        fn __ge__(&self, other: &PyI24) -> bool {
1725            self.value >= other.value
1726        }
1727
1728        // Cross-type comparisons with Python int
1729        fn __eq_int__(&self, other: i32) -> bool {
1730            self.value.to_i32() == other
1731        }
1732
1733        fn __ne_int__(&self, other: i32) -> bool {
1734            self.value.to_i32() != other
1735        }
1736
1737        fn __lt_int__(&self, other: i32) -> bool {
1738            self.value.to_i32() < other
1739        }
1740
1741        fn __le_int__(&self, other: i32) -> bool {
1742            self.value.to_i32() <= other
1743        }
1744
1745        fn __gt_int__(&self, other: i32) -> bool {
1746            self.value.to_i32() > other
1747        }
1748
1749        fn __ge_int__(&self, other: i32) -> bool {
1750            self.value.to_i32() >= other
1751        }
1752
1753        // Arithmetic operators
1754        fn __add__(&self, other: &PyI24) -> PyResult<PyI24> {
1755            match self.value.checked_add(other.value) {
1756                Some(result) => Ok(PyI24 { value: result }),
1757                None => Err(pyo3::exceptions::PyOverflowError::new_err(
1758                    "I24 addition overflow",
1759                )),
1760            }
1761        }
1762
1763        fn __sub__(&self, other: &PyI24) -> PyResult<PyI24> {
1764            match self.value.checked_sub(other.value) {
1765                Some(result) => Ok(PyI24 { value: result }),
1766                None => Err(pyo3::exceptions::PyOverflowError::new_err(
1767                    "I24 subtraction overflow",
1768                )),
1769            }
1770        }
1771
1772        fn __mul__(&self, other: &PyI24) -> PyResult<PyI24> {
1773            match self.value.checked_mul(other.value) {
1774                Some(result) => Ok(PyI24 { value: result }),
1775                None => Err(pyo3::exceptions::PyOverflowError::new_err(
1776                    "I24 multiplication overflow",
1777                )),
1778            }
1779        }
1780
1781        fn __truediv__(&self, other: &PyI24) -> PyResult<f64> {
1782            if other.value.to_i32() == 0 {
1783                return Err(pyo3::exceptions::PyZeroDivisionError::new_err(
1784                    "division by zero",
1785                ));
1786            }
1787            Ok(self.value.to_i32() as f64 / other.value.to_i32() as f64)
1788        }
1789
1790        fn __floordiv__(&self, other: &PyI24) -> PyResult<PyI24> {
1791            match self.value.checked_div(other.value) {
1792                Some(result) => Ok(PyI24 { value: result }),
1793                None => Err(pyo3::exceptions::PyZeroDivisionError::new_err(
1794                    "division by zero",
1795                )),
1796            }
1797        }
1798
1799        fn __mod__(&self, other: &PyI24) -> PyResult<PyI24> {
1800            match self.value.checked_rem(other.value) {
1801                Some(result) => Ok(PyI24 { value: result }),
1802                None => Err(pyo3::exceptions::PyZeroDivisionError::new_err(
1803                    "division by zero",
1804                )),
1805            }
1806        }
1807
1808        // Bitwise operations
1809        fn __and__(&self, other: &PyI24) -> PyI24 {
1810            PyI24 {
1811                value: self.value & other.value,
1812            }
1813        }
1814
1815        fn __or__(&self, other: &PyI24) -> PyI24 {
1816            PyI24 {
1817                value: self.value | other.value,
1818            }
1819        }
1820
1821        fn __xor__(&self, other: &PyI24) -> PyI24 {
1822            PyI24 {
1823                value: self.value ^ other.value,
1824            }
1825        }
1826
1827        fn __lshift__(&self, other: u32) -> PyResult<PyI24> {
1828            if other >= 32 {
1829                return Err(pyo3::exceptions::PyValueError::new_err(
1830                    "shift count out of range",
1831                ));
1832            }
1833            let result = (self.value.to_i32() as u32) << other;
1834            match I24::try_from_i32(result as i32) {
1835                Some(val) => Ok(PyI24 { value: val }),
1836                None => Err(pyo3::exceptions::PyOverflowError::new_err(
1837                    "I24 left shift overflow",
1838                )),
1839            }
1840        }
1841
1842        fn __rshift__(&self, other: u32) -> PyResult<PyI24> {
1843            if other >= 32 {
1844                return Err(pyo3::exceptions::PyValueError::new_err(
1845                    "shift count out of range",
1846                ));
1847            }
1848            let result = self.value.to_i32() >> other;
1849            Ok(PyI24 {
1850                value: I24::wrapping_from_i32(result),
1851            })
1852        }
1853
1854        // Utility methods
1855        #[staticmethod]
1856        fn min_value() -> PyI24 {
1857            PyI24 { value: I24::MIN }
1858        }
1859
1860        #[staticmethod]
1861        fn max_value() -> PyI24 {
1862            PyI24 { value: I24::MAX }
1863        }
1864
1865        fn count_ones(&self) -> u32 {
1866            self.value.to_i32().count_ones()
1867        }
1868
1869        fn count_zeros(&self) -> u32 {
1870            self.value.to_i32().count_zeros()
1871        }
1872
1873        fn leading_zeros(&self) -> u32 {
1874            self.value.to_i32().leading_zeros()
1875        }
1876
1877        fn trailing_zeros(&self) -> u32 {
1878            self.value.to_i32().trailing_zeros()
1879        }
1880
1881        // Checked methods
1882        fn checked_add(&self, other: &PyI24) -> Option<PyI24> {
1883            self.value
1884                .checked_add(other.value)
1885                .map(|v| PyI24 { value: v })
1886        }
1887
1888        fn checked_sub(&self, other: &PyI24) -> Option<PyI24> {
1889            self.value
1890                .checked_sub(other.value)
1891                .map(|v| PyI24 { value: v })
1892        }
1893
1894        fn checked_mul(&self, other: &PyI24) -> Option<PyI24> {
1895            self.value
1896                .checked_mul(other.value)
1897                .map(|v| PyI24 { value: v })
1898        }
1899
1900        fn checked_div(&self, other: &PyI24) -> Option<PyI24> {
1901            self.value
1902                .checked_div(other.value)
1903                .map(|v| PyI24 { value: v })
1904        }
1905
1906        fn wrapping_add(&self, other: &PyI24) -> PyI24 {
1907            let result = self.value.to_i32().wrapping_add(other.value.to_i32());
1908            PyI24 {
1909                value: I24::wrapping_from_i32(result),
1910            }
1911        }
1912
1913        fn wrapping_sub(&self, other: &PyI24) -> PyI24 {
1914            let result = self.value.to_i32().wrapping_sub(other.value.to_i32());
1915            PyI24 {
1916                value: I24::wrapping_from_i32(result),
1917            }
1918        }
1919
1920        fn wrapping_mul(&self, other: &PyI24) -> PyI24 {
1921            let result = self.value.to_i32().wrapping_mul(other.value.to_i32());
1922            PyI24 {
1923                value: I24::wrapping_from_i32(result),
1924            }
1925        }
1926
1927        fn saturating_add(&self, other: &PyI24) -> PyI24 {
1928            let result = self.value.to_i32().saturating_add(other.value.to_i32());
1929            PyI24 {
1930                value: I24::saturating_from_i32(result),
1931            }
1932        }
1933
1934        fn saturating_sub(&self, other: &PyI24) -> PyI24 {
1935            let result = self.value.to_i32().saturating_sub(other.value.to_i32());
1936            PyI24 {
1937                value: I24::saturating_from_i32(result),
1938            }
1939        }
1940
1941        fn saturating_mul(&self, other: &PyI24) -> PyI24 {
1942            let result = self.value.to_i32().saturating_mul(other.value.to_i32());
1943            PyI24 {
1944                value: I24::saturating_from_i32(result),
1945            }
1946        }
1947
1948        // Additional Python magic methods
1949        fn __hash__(&self) -> u64 {
1950            use std::collections::hash_map::DefaultHasher;
1951            use std::hash::{Hash, Hasher};
1952            let mut hasher = DefaultHasher::new();
1953            self.value.hash(&mut hasher);
1954            hasher.finish()
1955        }
1956
1957        fn __abs__(&self) -> PyResult<PyI24> {
1958            let abs_val = self.value.to_i32().abs();
1959            match I24::try_from_i32(abs_val) {
1960                Some(val) => Ok(PyI24 { value: val }),
1961                None => Err(pyo3::exceptions::PyOverflowError::new_err(
1962                    "Absolute value overflow for I24::MIN",
1963                )),
1964            }
1965        }
1966
1967        fn __neg__(&self) -> PyResult<PyI24> {
1968            match self.value.checked_neg() {
1969                Some(result) => Ok(PyI24 { value: result }),
1970                None => Err(pyo3::exceptions::PyOverflowError::new_err(
1971                    "I24 negation overflow",
1972                )),
1973            }
1974        }
1975
1976        fn __pos__(&self) -> PyI24 {
1977            PyI24 { value: self.value }
1978        }
1979
1980        fn __invert__(&self) -> PyI24 {
1981            let inverted = !self.value;
1982            PyI24 { value: inverted }
1983        }
1984
1985        // Pythonic method names
1986        fn bit_length(&self) -> u32 {
1987            let val = if self.value.to_i32() < 0 {
1988                (!self.value.to_i32()) as u32
1989            } else {
1990                self.value.to_i32() as u32
1991            };
1992            32 - val.leading_zeros()
1993        }
1994
1995        fn bit_count(&self) -> u32 {
1996            let abs_val = self.value.to_i32().unsigned_abs();
1997            abs_val.count_ones()
1998        }
1999
2000        fn as_integer_ratio(&self) -> (i32, i32) {
2001            (self.value.to_i32(), 1)
2002        }
2003
2004        #[pyo3(signature = (ndigits = None))]
2005        fn __round__(&self, ndigits: Option<i32>) -> PyResult<PyI24> {
2006            match ndigits {
2007                None => Ok(PyI24 { value: self.value }),
2008                Some(0) => Ok(PyI24 { value: self.value }),
2009                Some(_) => Ok(PyI24 { value: self.value }),
2010            }
2011        }
2012
2013        fn __ceil__(&self) -> PyI24 {
2014            PyI24 { value: self.value }
2015        }
2016
2017        fn __floor__(&self) -> PyI24 {
2018            PyI24 { value: self.value }
2019        }
2020
2021        fn __trunc__(&self) -> PyI24 {
2022            PyI24 { value: self.value }
2023        }
2024    }
2025
2026    unsafe impl Element for I24 {
2027        const IS_COPY: bool = true;
2028
2029        fn clone_ref(&self, _py: Python<'_>) -> Self {
2030            *self
2031        }
2032
2033        fn get_dtype(py: Python<'_>) -> Bound<'_, PyArrayDescr> {
2034            // IMPORTANT: do not call `numpy::dtype::<I24>` here; that dispatches to
2035            // `I24::get_dtype` and will recurse.
2036            //
2037            // NumPy has no native 24-bit integer dtype, and `I24` is represented in-memory
2038            // as a 4-byte value with the top byte always zero. We therefore expose it as an
2039            // opaque 4-byte dtype.
2040            static DTYPE: PyOnceLock<Py<PyArrayDescr>> = PyOnceLock::new();
2041
2042            DTYPE
2043                .get_or_init(py, || {
2044                    PyArrayDescr::new(py, "V4")
2045                        .expect("Failed to construct NumPy dtype for I24")
2046                        .unbind()
2047                })
2048                .clone_ref(py)
2049                .into_bound(py)
2050        }
2051    }
2052
2053    // IntoPyObject implementation for I24 - converts to Python int
2054    impl<'py> IntoPyObject<'py> for I24 {
2055        type Target = pyo3::types::PyInt;
2056        type Output = Bound<'py, pyo3::types::PyInt>;
2057        type Error = pyo3::PyErr;
2058
2059        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2060            Ok(self.to_i32().into_pyobject(py)?)
2061        }
2062    }
2063
2064    impl<'py> IntoPyObject<'py> for &I24 {
2065        type Target = pyo3::types::PyInt;
2066        type Output = Bound<'py, pyo3::types::PyInt>;
2067        type Error = pyo3::PyErr;
2068
2069        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2070            Ok(self.to_i32().into_pyobject(py)?)
2071        }
2072    }
2073
2074    // FromPyObject implementation for I24 - converts from Python int
2075    impl<'a, 'py> FromPyObject<'a, 'py> for I24 {
2076        type Error = pyo3::PyErr;
2077
2078        fn extract(obj: pyo3::Borrowed<'a, 'py, pyo3::PyAny>) -> PyResult<Self> {
2079            let py_int: i32 = obj.extract()?;
2080            I24::try_from_i32(py_int).ok_or_else(|| {
2081                pyo3::exceptions::PyOverflowError::new_err(format!(
2082                    "Value {} is out of range for I24 (-8388608 to 8388607)",
2083                    py_int
2084                ))
2085            })
2086        }
2087    }
2088}
2089
2090/// Public 3-byte wire representation of a signed 24-bit integer.
2091///
2092/// This type represents the exact on-wire format of an `I24` value as 3 consecutive bytes.
2093/// Unlike the runtime `I24` type which is 4-byte aligned, `I24Bytes` is exactly 3 bytes
2094/// and can be safely used in packed structs for binary deserialization.
2095///
2096/// # Safety for Mixed Packed Structs
2097///
2098/// `I24Bytes` is designed to solve the problem of mixed packed structs containing both
2099/// standard types (like `u32`) and 24-bit integers. Since `bytemuck::Pod` forbids
2100/// packed structs with native integer fields, you should use byte-oriented types
2101/// like `I24Bytes` in your wire structs, then convert to native types.
2102///
2103/// # Examples
2104///
2105/// ```
2106/// use i24::I24Bytes;
2107///
2108/// // Convert from I24 to wire format
2109/// let value = i24::I24::try_from(123456).expect("Test value should convert successfully");
2110/// let wire_bytes = I24Bytes::from_i24_le(value);
2111///
2112/// // Convert back to I24
2113/// let recovered = wire_bytes.to_i24_le();
2114/// assert_eq!(value, recovered);
2115/// ```
2116///
2117/// For use in mixed packed structs, see the examples directory for complete patterns.
2118#[repr(transparent)]
2119#[derive(Copy, Clone, Eq, PartialEq, Debug)]
2120#[cfg_attr(
2121    feature = "zerocopy",
2122    derive(zerocopy::FromBytes, zerocopy::Unaligned, zerocopy::IntoBytes)
2123)]
2124pub struct I24Bytes(pub [u8; 3]);
2125
2126// Safety: I24Bytes is transparent over [u8; 3], which is always valid for any bit pattern
2127unsafe impl Pod for I24Bytes {}
2128
2129// Safety: I24Bytes can be safely zero-initialized
2130unsafe impl Zeroable for I24Bytes {}
2131
2132impl I24Bytes {
2133    /// Converts this wire representation to an `I24` value, interpreting bytes as little-endian.
2134    ///
2135    /// # Examples
2136    ///
2137    /// ```
2138    /// use i24::I24Bytes;
2139    ///
2140    /// let wire = I24Bytes([0x40, 0xE2, 0x01]); // 123456 in little-endian
2141    /// let value = wire.to_i24_le();
2142    /// assert_eq!(value, i24::I24::try_from(123456).expect("Test value should convert successfully"));
2143    /// ```
2144    #[inline]
2145    pub const fn to_i24_le(self) -> I24 {
2146        I24::from_le_bytes(self.0)
2147    }
2148
2149    /// Converts this wire representation to an `I24` value, interpreting bytes as big-endian.
2150    ///
2151    /// # Examples
2152    ///
2153    /// ```
2154    /// use i24::I24Bytes;
2155    ///
2156    /// let wire = I24Bytes([0x01, 0xE2, 0x40]); // 123456 in big-endian
2157    /// let value = wire.to_i24_be();
2158    /// assert_eq!(value, i24::I24::try_from(123456).expect("Test value should convert successfully"));
2159    /// ```
2160    #[inline]
2161    pub const fn to_i24_be(self) -> I24 {
2162        I24::from_be_bytes(self.0)
2163    }
2164
2165    /// Converts this wire representation to an `I24` value, interpreting bytes as native-endian.
2166    ///
2167    /// # Examples
2168    ///
2169    /// ```
2170    /// use i24::I24Bytes;
2171    ///
2172    /// let original = i24::I24::try_from(123456).expect("Test value should convert successfully");
2173    /// let wire = I24Bytes::from_i24_ne(original);
2174    /// let value = wire.to_i24_ne();
2175    /// assert_eq!(value, original);
2176    /// ```
2177    #[inline]
2178    pub const fn to_i24_ne(self) -> I24 {
2179        I24::from_ne_bytes(self.0)
2180    }
2181
2182    /// Creates a wire representation from an `I24` value in little-endian byte order.
2183    ///
2184    /// # Examples
2185    ///
2186    /// ```
2187    /// use i24::I24Bytes;
2188    ///
2189    /// let value = i24::I24::try_from(123456).expect("Test value should convert successfully");
2190    /// let wire = I24Bytes::from_i24_le(value);
2191    /// assert_eq!(wire.0, [0x40, 0xE2, 0x01]); // little-endian bytes
2192    /// ```
2193    #[inline]
2194    pub const fn from_i24_le(value: I24) -> Self {
2195        Self(value.to_le_bytes())
2196    }
2197
2198    /// Creates a wire representation from an `I24` value in big-endian byte order.
2199    ///
2200    /// # Examples
2201    ///
2202    /// ```
2203    /// use i24::I24Bytes;
2204    ///
2205    /// let value = i24::I24::try_from(123456).expect("Test value should convert successfully");
2206    /// let wire = I24Bytes::from_i24_be(value);
2207    /// assert_eq!(wire.0, [0x01, 0xE2, 0x40]); // big-endian bytes
2208    /// ```
2209    #[inline]
2210    pub const fn from_i24_be(value: I24) -> Self {
2211        Self(value.to_be_bytes())
2212    }
2213
2214    /// Creates a wire representation from an `I24` value in native-endian byte order.
2215    ///
2216    /// # Examples
2217    ///
2218    /// ```
2219    /// use i24::I24Bytes;
2220    ///
2221    /// let value = i24::I24::try_from(123456).expect("Test value should convert successfully");
2222    /// let wire = I24Bytes::from_i24_ne(value);
2223    /// // Byte order depends on target architecture endianness
2224    /// ```
2225    #[inline]
2226    pub const fn from_i24_ne(value: I24) -> Self {
2227        Self(value.to_ne_bytes())
2228    }
2229
2230    /// Creates a wire representation directly from a 3-byte array.
2231    ///
2232    /// The byte order interpretation is deferred until conversion with
2233    /// `to_i24_le()` or `to_i24_be()`.
2234    ///
2235    /// # Examples
2236    ///
2237    /// ```
2238    /// use i24::I24Bytes;
2239    ///
2240    /// let wire = I24Bytes::from_bytes([0x40, 0xE2, 0x01]);
2241    /// assert_eq!(wire.0, [0x40, 0xE2, 0x01]);
2242    /// ```
2243    #[inline]
2244    pub const fn from_bytes(bytes: [u8; 3]) -> Self {
2245        Self(bytes)
2246    }
2247
2248    /// Returns the raw byte array of this wire representation.
2249    ///
2250    /// # Examples
2251    ///
2252    /// ```
2253    /// use i24::I24Bytes;
2254    ///
2255    /// let wire = I24Bytes([0x40, 0xE2, 0x01]);
2256    /// assert_eq!(wire.to_bytes(), [0x40, 0xE2, 0x01]);
2257    /// ```
2258    #[inline]
2259    pub const fn to_bytes(self) -> [u8; 3] {
2260        self.0
2261    }
2262}
2263
2264// Additional trait implementations for I24Bytes
2265impl AsRef<[u8; 3]> for I24Bytes {
2266    #[inline]
2267    fn as_ref(&self) -> &[u8; 3] {
2268        &self.0
2269    }
2270}
2271
2272impl AsMut<[u8; 3]> for I24Bytes {
2273    #[inline]
2274    fn as_mut(&mut self) -> &mut [u8; 3] {
2275        &mut self.0
2276    }
2277}
2278
2279impl AsRef<[u8]> for I24Bytes {
2280    #[inline]
2281    fn as_ref(&self) -> &[u8] {
2282        &self.0[..]
2283    }
2284}
2285
2286impl AsMut<[u8]> for I24Bytes {
2287    #[inline]
2288    fn as_mut(&mut self) -> &mut [u8] {
2289        &mut self.0[..]
2290    }
2291}
2292
2293impl From<[u8; 3]> for I24Bytes {
2294    #[inline]
2295    fn from(bytes: [u8; 3]) -> Self {
2296        Self(bytes)
2297    }
2298}
2299
2300impl From<I24Bytes> for [u8; 3] {
2301    #[inline]
2302    fn from(i24_bytes: I24Bytes) -> Self {
2303        i24_bytes.0
2304    }
2305}
2306
2307impl core::ops::Deref for I24Bytes {
2308    type Target = [u8; 3];
2309
2310    #[inline]
2311    fn deref(&self) -> &Self::Target {
2312        &self.0
2313    }
2314}
2315
2316impl core::ops::DerefMut for I24Bytes {
2317    #[inline]
2318    fn deref_mut(&mut self) -> &mut Self::Target {
2319        &mut self.0
2320    }
2321}
2322
2323// PyO3 implementations for I24Bytes
2324#[cfg(feature = "pyo3")]
2325mod i24_bytes_pyo3 {
2326    use super::I24Bytes;
2327    use pyo3::{
2328        conversion::{FromPyObject, IntoPyObject},
2329        prelude::*,
2330    };
2331
2332    // IntoPyObject implementation for I24Bytes - converts to Python bytes
2333    impl<'py> IntoPyObject<'py> for I24Bytes {
2334        type Target = pyo3::types::PyBytes;
2335        type Output = Bound<'py, pyo3::types::PyBytes>;
2336        type Error = pyo3::PyErr;
2337
2338        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2339            Ok(pyo3::types::PyBytes::new(py, &self.0))
2340        }
2341    }
2342
2343    impl<'py> IntoPyObject<'py> for &I24Bytes {
2344        type Target = pyo3::types::PyBytes;
2345        type Output = Bound<'py, pyo3::types::PyBytes>;
2346        type Error = pyo3::PyErr;
2347
2348        fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
2349            Ok(pyo3::types::PyBytes::new(py, &self.0))
2350        }
2351    }
2352
2353    // FromPyObject implementation for I24Bytes - converts from Python bytes
2354    impl<'a, 'py> FromPyObject<'a, 'py> for I24Bytes {
2355        type Error = pyo3::PyErr;
2356
2357        fn extract(obj: pyo3::Borrowed<'a, 'py, pyo3::PyAny>) -> PyResult<Self> {
2358            let py_bytes: &[u8] = obj.extract()?;
2359            if py_bytes.len() != 3 {
2360                return Err(pyo3::exceptions::PyValueError::new_err(format!(
2361                    "Expected exactly 3 bytes for I24Bytes, got {}",
2362                    py_bytes.len()
2363                )));
2364            }
2365            Ok(I24Bytes([py_bytes[0], py_bytes[1], py_bytes[2]]))
2366        }
2367    }
2368}
2369
2370#[cfg(test)]
2371mod i24_tests {
2372    extern crate std;
2373
2374    use super::*;
2375    use std::format;
2376    use std::num::IntErrorKind;
2377
2378    #[test]
2379    fn test_arithmetic_operations() {
2380        let a = i24!(100);
2381        let b = i24!(50);
2382
2383        assert_eq!(a + b, i24!(150));
2384        assert_eq!(a - b, i24!(50));
2385        assert_eq!(a * b, i24!(5000));
2386        assert_eq!(a / b, i24!(2));
2387        assert_eq!(a % b, i24!(0));
2388    }
2389
2390    #[test]
2391    fn test_negative_operations() {
2392        let a = i24!(100);
2393        let b = i24!(-50);
2394
2395        assert_eq!(a + b, i24!(50));
2396        assert_eq!(a - b, i24!(150));
2397        assert_eq!(a * b, i24!(-5000));
2398        assert_eq!(a / b, i24!(-2));
2399    }
2400
2401    #[test]
2402    fn test_bitwise_operations() {
2403        let a = i24!(0b101010);
2404        let b = i24!(0b110011);
2405
2406        assert_eq!(a & b, i24!(0b100010));
2407        assert_eq!(a | b, i24!(0b111011));
2408        assert_eq!(a ^ b, i24!(0b011001));
2409        assert_eq!(a << 2, i24!(0b10101000));
2410        assert_eq!(a >> 2, i24!(0b1010));
2411    }
2412
2413    #[test]
2414    fn test_checked_addition() {
2415        assert_eq!(i24!(10).checked_add(i24!(20)), Some(i24!(30)));
2416        assert_eq!(i24!(10).checked_add(i24!(-20)), Some(i24!(-10)));
2417        // Overflow cases
2418        assert_eq!(I24::MAX.checked_add(I24::one()), None);
2419        assert_eq!(
2420            (I24::MAX - I24::one()).checked_add(I24::one() * i24!(2)),
2421            None
2422        );
2423    }
2424
2425    #[test]
2426    fn test_checked_subtraction() {
2427        assert_eq!(i24!(10).checked_sub(i24!(20)), Some(i24!(-10)));
2428        assert_eq!(i24!(10).checked_sub(i24!(-20)), Some(i24!(30)));
2429
2430        // Overflow cases
2431        assert_eq!(I24::MIN.checked_sub(I24::one()), None);
2432        assert_eq!(
2433            (I24::MIN + I24::one()).checked_sub(I24::one() * i24!(2)),
2434            None
2435        );
2436    }
2437
2438    #[test]
2439    fn test_checked_division() {
2440        assert_eq!(i24!(20).checked_div(i24!(5)), Some(i24!(4)));
2441        assert_eq!(i24!(20).checked_div(i24!(0)), None);
2442    }
2443
2444    #[test]
2445    fn test_checked_multiplication() {
2446        assert_eq!(i24!(5).checked_mul(i24!(6)), Some(i24!(30)));
2447        assert_eq!(I24::MAX.checked_mul(i24!(2)), None);
2448    }
2449
2450    #[test]
2451    fn test_checked_remainder() {
2452        assert_eq!(i24!(20).checked_rem(i24!(5)), Some(i24!(0)));
2453        assert_eq!(i24!(20).checked_rem(i24!(0)), None);
2454    }
2455
2456    #[test]
2457    fn test_unary_operations() {
2458        let a = i24!(100);
2459
2460        assert_eq!(-a, i24!(-100));
2461        assert_eq!(!a, i24!(-101));
2462    }
2463
2464    #[test]
2465    fn test_from_bytes() {
2466        let le = i24!(0x030201);
2467        let be = i24!(0x010203);
2468        assert_eq!(
2469            I24::from_ne_bytes([0x01, 0x02, 0x03]),
2470            if cfg!(target_endian = "big") { be } else { le }
2471        );
2472        assert_eq!(I24::from_le_bytes([0x01, 0x02, 0x03]), le);
2473        assert_eq!(I24::from_be_bytes([0x01, 0x02, 0x03]), be);
2474    }
2475
2476    #[test]
2477    fn test_zero_and_one() {
2478        assert_eq!(
2479            I24::zero(),
2480            I24::try_from_i32(0).expect("Zero should convert successfully")
2481        );
2482
2483        assert_eq!(I24::zero(), i24!(0));
2484        assert_eq!(I24::one(), i24!(1));
2485    }
2486
2487    #[test]
2488    fn test_from_str() {
2489        assert_eq!(
2490            I24::from_str("100").expect("100 should parse successfully"),
2491            i24!(100)
2492        );
2493        assert_eq!(
2494            I24::from_str("-100").expect("-100 should parse successfully"),
2495            i24!(-100)
2496        );
2497        assert_eq!(
2498            I24::from_str(&format!("{}", I24::MAX)).expect("MAX should parse successfully"),
2499            I24::MAX
2500        );
2501        assert_eq!(
2502            I24::from_str(&format!("{}", I24::MIN)).expect("MIN should parse successfully"),
2503            I24::MIN
2504        );
2505        assert_eq!(
2506            *I24::from_str("8388608")
2507                .expect_err("Expected parse error")
2508                .kind(),
2509            IntErrorKind::PosOverflow
2510        );
2511        assert_eq!(
2512            *I24::from_str("-8388609")
2513                .expect_err("Expected parse error")
2514                .kind(),
2515            IntErrorKind::NegOverflow
2516        );
2517    }
2518
2519    #[test]
2520    fn test_display() {
2521        assert_eq!(format!("{}", i24!(100)), "100");
2522        assert_eq!(format!("{}", i24!(-100)), "-100");
2523    }
2524
2525    #[test]
2526    fn test_wrapping_behavior() {
2527        assert_eq!(I24::MAX + I24::one(), I24::MIN);
2528        assert_eq!(I24::MAX + I24::one() + I24::one(), I24::MIN + I24::one());
2529
2530        assert_eq!(I24::MIN - I24::one(), I24::MAX);
2531        assert_eq!(I24::MIN - (I24::one() + I24::one()), I24::MAX - I24::one());
2532
2533        assert_eq!(-I24::MIN, I24::MIN)
2534    }
2535
2536    #[test]
2537    fn discriminant_optimization() {
2538        // this isn't guaranteed by rustc, but this should still hold true
2539        // if this fails because rustc stops doing it, just remove this test
2540        // otherwise investigate why this isn't working
2541        assert_eq!(size_of::<I24>(), size_of::<Option<I24>>());
2542        assert_eq!(size_of::<I24>(), size_of::<Option<Option<I24>>>());
2543        assert_eq!(size_of::<I24>(), size_of::<Option<Option<Option<I24>>>>());
2544        assert_eq!(
2545            size_of::<I24>(),
2546            size_of::<Option<Option<Option<Option<I24>>>>>()
2547        );
2548    }
2549
2550    #[test]
2551    fn test_shift_operations() {
2552        let a = i24!(0b1);
2553
2554        // Left shift
2555        assert_eq!(a << 23, I24::MIN); // 0x800000, which is the minimum negative value
2556        assert_eq!(a << 24, a); // Wraps around (same as << 0)
2557
2558        // Right shift
2559        let b = i24!(-1); // All bits set
2560        assert_eq!(b >> 1, i24!(-1)); // Sign extension
2561        assert_eq!(b >> 23, i24!(-1)); // Still all bits set due to sign extension
2562        assert_eq!(b >> 24, i24!(-1)); // No change after 23 bits
2563
2564        // Edge case: maximum positive value
2565        let c = i24!(0x7FFFFF); // 8388607
2566        assert_eq!(c << 1, i24!(-2)); // 0xFFFFFE in 24-bit, which is -2 when sign-extended
2567
2568        // Edge case: minimum negative value
2569        let d = I24::MIN; // (-0x800000)
2570        assert_eq!(d >> 1, i24!(-0x400000));
2571        assert_eq!(d >> 2, i24!(-0x200000));
2572        assert_eq!(d >> 3, i24!(-0x100000));
2573        assert_eq!(d >> 4, i24!(-0x080000));
2574
2575        // Additional test for left shift wrapping
2576        assert_eq!(c << 1, i24!(-2)); // 0xFFFFFE
2577        assert_eq!(c << 2, i24!(-4)); // 0xFFFFFC
2578        assert_eq!(c << 3, i24!(-8)); // 0xFFFFF8
2579    }
2580
2581    #[test]
2582    fn test_to_from_i32() {
2583        for i in I24Repr::MIN..=I24Repr::MAX {
2584            assert_eq!(
2585                I24::try_from_i32(i)
2586                    .expect("Value in range should convert successfully")
2587                    .to_i32(),
2588                i
2589            )
2590        }
2591    }
2592
2593    #[test]
2594    fn test_from() {
2595        macro_rules! impl_t {
2596            ($($ty: ty),+) => {{$(
2597                for x in <$ty>::MIN..=<$ty>::MAX {
2598                    assert_eq!(<$ty>::try_from(<I24 as From<$ty>>::from(x).to_i32()).expect("Value should convert back"), x)
2599                }
2600            )+}};
2601        }
2602
2603        assert_eq!(<I24 as From<bool>>::from(true), I24::one());
2604        assert_eq!(<I24 as From<bool>>::from(false), I24::zero());
2605
2606        impl_t!(i8, i16, u8, u16)
2607    }
2608
2609    #[test]
2610    fn test_try_from() {
2611        macro_rules! impl_t {
2612            (signed $($ty: ty),+) => {{$(
2613                for x in I24Repr::MIN..=I24Repr::MAX {
2614                    assert_eq!(I24::try_from(<$ty as From<i32>>::from(x)).expect("Value should convert successfully").to_i32(), x)
2615                }
2616            )+}};
2617
2618            (unsigned $($ty: ty),+) => {{$(
2619                for x in 0..=I24Repr::MAX {
2620                    assert_eq!(I24::try_from(<$ty>::try_from(x).expect("Value should fit in type")).expect("Value should convert to I24").to_i32(), x)
2621                }
2622            )+}};
2623        }
2624
2625        impl_t!(signed i32, i64, i128);
2626        impl_t!(unsigned u32, u64, u128);
2627    }
2628
2629    #[test]
2630    fn test_to_from_bits() {
2631        for i in 0..(1 << 24) {
2632            assert_eq!(I24::from_bits_truncate(i).to_bits(), i)
2633        }
2634    }
2635
2636    #[test]
2637    #[cfg(feature = "serde")]
2638    fn test_deserialize_json() {
2639        #[derive(Debug, PartialEq, ::serde::Deserialize)]
2640        struct TestStruct {
2641            value: I24,
2642        }
2643
2644        let test: TestStruct =
2645            serde_json::from_str("{ \"value\": 11 }").expect("Failed to deserialize!");
2646        let expected = TestStruct {
2647            value: I24::from_u8(11),
2648        };
2649
2650        assert_eq!(test, expected);
2651    }
2652
2653    #[test]
2654    #[cfg(feature = "serde")]
2655    fn test_serialize_json() {
2656        #[derive(Debug, PartialEq, ::serde::Serialize)]
2657        struct TestStruct {
2658            value: I24,
2659        }
2660
2661        let test_struct = TestStruct {
2662            value: I24::from_u8(11),
2663        };
2664        let test = serde_json::to_string(&test_struct).expect("Failed to serialize!");
2665        assert_eq!(test, "{\"value\":11}");
2666    }
2667
2668    #[test]
2669    fn test_to_primitive_signed() {
2670        use num_traits::ToPrimitive;
2671
2672        // Test positive values
2673        let val = i24!(100);
2674        assert_eq!(val.to_i8(), Some(100i8));
2675        assert_eq!(val.to_i16(), Some(100i16));
2676        assert_eq!(ToPrimitive::to_i32(&val), Some(100i32));
2677        assert_eq!(val.to_i64(), Some(100i64));
2678        assert_eq!(val.to_i128(), Some(100i128));
2679        assert_eq!(val.to_isize(), Some(100isize));
2680
2681        // Test negative values
2682        let val = i24!(-100);
2683        assert_eq!(val.to_i8(), Some(-100i8));
2684        assert_eq!(val.to_i16(), Some(-100i16));
2685        assert_eq!(ToPrimitive::to_i32(&val), Some(-100i32));
2686        assert_eq!(val.to_i64(), Some(-100i64));
2687        assert_eq!(val.to_i128(), Some(-100i128));
2688        assert_eq!(val.to_isize(), Some(-100isize));
2689
2690        // Test overflow cases for smaller types
2691        let val = I24::MAX;
2692        assert_eq!(val.to_i8(), None); // I24::MAX > i8::MAX
2693        assert_eq!(val.to_i16(), None); // I24::MAX > i16::MAX
2694        assert_eq!(ToPrimitive::to_i32(&val), Some(I24::MAX.to_i32()));
2695
2696        let val = I24::MIN;
2697        assert_eq!(val.to_i8(), None); // I24::MIN < i8::MIN
2698        assert_eq!(val.to_i16(), None); // I24::MIN < i16::MIN
2699        assert_eq!(ToPrimitive::to_i32(&val), Some(I24::MIN.to_i32()));
2700    }
2701
2702    #[test]
2703    fn test_to_primitive_unsigned() {
2704        use num_traits::ToPrimitive;
2705
2706        // Test positive values
2707        let val = i24!(100);
2708        assert_eq!(val.to_u8(), Some(100u8));
2709        assert_eq!(val.to_u16(), Some(100u16));
2710        assert_eq!(val.to_u32(), Some(100u32));
2711        assert_eq!(val.to_u64(), Some(100u64));
2712        assert_eq!(val.to_u128(), Some(100u128));
2713        assert_eq!(val.to_usize(), Some(100usize));
2714
2715        // Test negative values should return None for unsigned types
2716        let val = i24!(-100);
2717        assert_eq!(val.to_u8(), None);
2718        assert_eq!(val.to_u16(), None);
2719        assert_eq!(val.to_u32(), None);
2720        assert_eq!(val.to_u64(), None);
2721        assert_eq!(val.to_u128(), None);
2722        assert_eq!(val.to_usize(), None);
2723
2724        // Test overflow cases for smaller unsigned types
2725        let val = I24::MAX;
2726        assert_eq!(val.to_u8(), None); // I24::MAX > u8::MAX
2727        assert_eq!(val.to_u16(), None); // I24::MAX > u16::MAX
2728        assert_eq!(val.to_u32(), Some(I24::MAX.to_i32() as u32));
2729
2730        // Test zero
2731        let val = i24!(0);
2732        assert_eq!(val.to_u8(), Some(0u8));
2733        assert_eq!(val.to_u16(), Some(0u16));
2734        assert_eq!(val.to_u32(), Some(0u32));
2735        assert_eq!(val.to_u64(), Some(0u64));
2736        assert_eq!(val.to_u128(), Some(0u128));
2737        assert_eq!(val.to_usize(), Some(0usize));
2738    }
2739
2740    #[test]
2741    fn test_to_primitive_floats() {
2742        use num_traits::ToPrimitive;
2743
2744        // Test positive values
2745        let val = i24!(100);
2746        assert_eq!(val.to_f32(), Some(100.0f32));
2747        assert_eq!(val.to_f64(), Some(100.0f64));
2748
2749        // Test negative values
2750        let val = i24!(-100);
2751        assert_eq!(val.to_f32(), Some(-100.0f32));
2752        assert_eq!(val.to_f64(), Some(-100.0f64));
2753
2754        // Test extreme values
2755        let val = I24::MAX;
2756        assert_eq!(val.to_f32(), Some(I24::MAX.to_i32() as f32));
2757        assert_eq!(val.to_f64(), Some(I24::MAX.to_i32() as f64));
2758
2759        let val = I24::MIN;
2760        assert_eq!(val.to_f32(), Some(I24::MIN.to_i32() as f32));
2761        assert_eq!(val.to_f64(), Some(I24::MIN.to_i32() as f64));
2762    }
2763
2764    #[test]
2765    fn test_to_primitive_boundary_values() {
2766        use num_traits::ToPrimitive;
2767
2768        // Test values at the boundaries of smaller types
2769        let val = i24!(127); // i8::MAX
2770        assert_eq!(val.to_i8(), Some(127i8));
2771        assert_eq!(val.to_u8(), Some(127u8));
2772
2773        let val = i24!(128); // i8::MAX + 1
2774        assert_eq!(val.to_i8(), None);
2775        assert_eq!(val.to_u8(), Some(128u8));
2776
2777        let val = i24!(255); // u8::MAX
2778        assert_eq!(val.to_u8(), Some(255u8));
2779
2780        let val = i24!(256); // u8::MAX + 1
2781        assert_eq!(val.to_u8(), None);
2782
2783        let val = i24!(32767); // i16::MAX
2784        assert_eq!(val.to_i16(), Some(32767i16));
2785        assert_eq!(val.to_u16(), Some(32767u16));
2786
2787        let val = i24!(32768); // i16::MAX + 1
2788        assert_eq!(val.to_i16(), None);
2789        assert_eq!(val.to_u16(), Some(32768u16));
2790
2791        let val = i24!(65535); // u16::MAX
2792        assert_eq!(val.to_u16(), Some(65535u16));
2793
2794        let val = i24!(65536); // u16::MAX + 1
2795        assert_eq!(val.to_u16(), None);
2796    }
2797
2798    #[cfg(feature = "alloc")]
2799    #[test]
2800    fn test_packed_struct_utilities() {
2801        use crate::PackedStruct;
2802        use alloc::vec;
2803        use alloc::vec::Vec;
2804
2805        // Define a test structure similar to the issue example
2806        #[derive(Debug, Clone, PartialEq)]
2807        struct TestDataStruct {
2808            t: u32,
2809            ch1: I24,
2810            ch2: I24,
2811            ch3: I24,
2812            ch4: I24,
2813            s: I24,
2814        }
2815
2816        impl PackedStruct for TestDataStruct {
2817            const PACKED_SIZE: usize = 4 + 5 * 3; // u32 + 5 * I24 = 19 bytes
2818
2819            fn from_packed_bytes(bytes: &[u8]) -> Option<Self> {
2820                if bytes.len() < Self::PACKED_SIZE {
2821                    return None;
2822                }
2823
2824                let t = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
2825                let ch1 = I24::from_le_bytes([bytes[4], bytes[5], bytes[6]]);
2826                let ch2 = I24::from_le_bytes([bytes[7], bytes[8], bytes[9]]);
2827                let ch3 = I24::from_le_bytes([bytes[10], bytes[11], bytes[12]]);
2828                let ch4 = I24::from_le_bytes([bytes[13], bytes[14], bytes[15]]);
2829                let s = I24::from_le_bytes([bytes[16], bytes[17], bytes[18]]);
2830
2831                Some(TestDataStruct {
2832                    t,
2833                    ch1,
2834                    ch2,
2835                    ch3,
2836                    ch4,
2837                    s,
2838                })
2839            }
2840
2841            fn to_packed_bytes(&self) -> Vec<u8> {
2842                let mut bytes = Vec::with_capacity(Self::PACKED_SIZE);
2843                bytes.extend_from_slice(&self.t.to_le_bytes());
2844                bytes.extend_from_slice(&self.ch1.to_le_bytes());
2845                bytes.extend_from_slice(&self.ch2.to_le_bytes());
2846                bytes.extend_from_slice(&self.ch3.to_le_bytes());
2847                bytes.extend_from_slice(&self.ch4.to_le_bytes());
2848                bytes.extend_from_slice(&self.s.to_le_bytes());
2849                bytes
2850            }
2851        }
2852
2853        // Test data
2854        let original = TestDataStruct {
2855            t: 0x12345678,
2856            ch1: I24::try_from_i32(0x123456).expect("Test value should convert successfully"),
2857            ch2: I24::try_from_i32(-1000).expect("Test value should convert successfully"),
2858            ch3: I24::try_from_i32(0).expect("Test value should convert successfully"),
2859            ch4: I24::try_from_i32(8388607).expect("Test value should convert successfully"), // MAX
2860            s: I24::try_from_i32(-8388608).expect("Test value should convert successfully"),  // MIN
2861        };
2862
2863        // Test round-trip serialization
2864        let packed_bytes = original.to_packed_bytes();
2865        assert_eq!(packed_bytes.len(), TestDataStruct::PACKED_SIZE);
2866
2867        let deserialized = TestDataStruct::from_packed_bytes(&packed_bytes)
2868            .expect("Test value should convert successfully");
2869        assert_eq!(original, deserialized);
2870
2871        // Test multiple structures
2872        let structs = vec![original.clone(), original];
2873        let packed_multiple = TestDataStruct::to_packed_slice(&structs);
2874        assert_eq!(packed_multiple.len(), 2 * TestDataStruct::PACKED_SIZE);
2875
2876        let deserialized_multiple = TestDataStruct::from_packed_slice(&packed_multiple)
2877            .expect("Test value should convert successfully");
2878        assert_eq!(structs, deserialized_multiple);
2879
2880        // Test invalid length handling
2881        let short_bytes = vec![0u8; TestDataStruct::PACKED_SIZE - 1];
2882        assert!(TestDataStruct::from_packed_bytes(&short_bytes).is_none());
2883
2884        let invalid_multiple = vec![0u8; TestDataStruct::PACKED_SIZE + 1];
2885        assert!(TestDataStruct::from_packed_slice(&invalid_multiple).is_none());
2886    }
2887
2888    #[cfg(test)]
2889    mod property_tests {
2890        use super::*;
2891        use proptest::prelude::*;
2892
2893        // Custom strategy to generate valid I24 values
2894        prop_compose! {
2895            fn valid_i24()(value in I24Repr::MIN..=I24Repr::MAX) -> I24 {
2896                I24::try_from_i32(value).expect("Test value should convert successfully")
2897            }
2898        }
2899
2900        // Strategy for generating pairs of valid I24 values
2901        prop_compose! {
2902            fn i24_pair()(a in valid_i24(), b in valid_i24()) -> (I24, I24) {
2903                (a, b)
2904            }
2905        }
2906
2907        proptest! {
2908            #[test]
2909            fn prop_to_from_i32_roundtrip(value in I24Repr::MIN..=I24Repr::MAX) {
2910                let i24_val = I24::try_from_i32(value).expect("Value in range should convert successfully");
2911                prop_assert_eq!(i24_val.to_i32(), value);
2912            }
2913
2914            #[test]
2915            fn prop_wrapping_from_i32_in_range(value in I24Repr::MIN..=I24Repr::MAX) {
2916                let i24_val = I24::wrapping_from_i32(value);
2917                prop_assert_eq!(i24_val.to_i32(), value);
2918            }
2919
2920            #[test]
2921            fn prop_saturating_from_i32_in_range(value in I24Repr::MIN..=I24Repr::MAX) {
2922                let i24_val = I24::saturating_from_i32(value);
2923                prop_assert_eq!(i24_val.to_i32(), value);
2924            }
2925
2926            #[test]
2927            fn prop_saturating_from_i32_above_max(value in I24Repr::MAX+1..=i32::MAX) {
2928                let i24_val = I24::saturating_from_i32(value);
2929                prop_assert_eq!(i24_val, I24::MAX);
2930            }
2931
2932            #[test]
2933            fn prop_saturating_from_i32_below_min(value in i32::MIN..=I24Repr::MIN-1) {
2934                let i24_val = I24::saturating_from_i32(value);
2935                prop_assert_eq!(i24_val, I24::MIN);
2936            }
2937
2938            #[test]
2939            fn prop_addition_commutative((a, b) in i24_pair()) {
2940                prop_assert_eq!(a + b, b + a);
2941            }
2942
2943            #[test]
2944            fn prop_addition_associative(a in valid_i24(), b in valid_i24(), c in valid_i24()) {
2945                prop_assert_eq!((a + b) + c, a + (b + c));
2946            }
2947
2948            #[test]
2949            fn prop_addition_identity(a in valid_i24()) {
2950                prop_assert_eq!(a + I24::zero(), a);
2951                prop_assert_eq!(I24::zero() + a, a);
2952            }
2953
2954            #[test]
2955            fn prop_subtraction_identity(a in valid_i24()) {
2956                prop_assert_eq!(a - I24::zero(), a);
2957            }
2958
2959            #[test]
2960            fn prop_subtraction_inverse(a in valid_i24()) {
2961                prop_assert_eq!(a - a, I24::zero());
2962            }
2963
2964            #[test]
2965            fn prop_multiplication_commutative((a, b) in i24_pair()) {
2966                prop_assert_eq!(a * b, b * a);
2967            }
2968
2969            #[test]
2970            fn prop_multiplication_identity(a in valid_i24()) {
2971                prop_assert_eq!(a * I24::one(), a);
2972                prop_assert_eq!(I24::one() * a, a);
2973            }
2974
2975            #[test]
2976            fn prop_multiplication_zero(a in valid_i24()) {
2977                prop_assert_eq!(a * I24::zero(), I24::zero());
2978                prop_assert_eq!(I24::zero() * a, I24::zero());
2979            }
2980
2981            #[test]
2982            fn prop_bitwise_and_commutative((a, b) in i24_pair()) {
2983                prop_assert_eq!(a & b, b & a);
2984            }
2985
2986            #[test]
2987            fn prop_bitwise_or_commutative((a, b) in i24_pair()) {
2988                prop_assert_eq!(a | b, b | a);
2989            }
2990
2991            #[test]
2992            fn prop_bitwise_xor_commutative((a, b) in i24_pair()) {
2993                prop_assert_eq!(a ^ b, b ^ a);
2994            }
2995
2996            #[test]
2997            fn prop_bitwise_xor_self_zero(a in valid_i24()) {
2998                prop_assert_eq!(a ^ a, I24::zero());
2999            }
3000
3001            #[test]
3002            fn prop_negation_involution(a in valid_i24()) {
3003                // Note: This won't hold for MIN due to two's complement overflow
3004                if a != I24::MIN {
3005                    prop_assert_eq!(-(-a), a);
3006                }
3007            }
3008
3009            #[test]
3010            fn prop_byte_roundtrip_le(a in valid_i24()) {
3011                let bytes = a.to_le_bytes();
3012                let reconstructed = I24::from_le_bytes(bytes);
3013                prop_assert_eq!(a, reconstructed);
3014            }
3015
3016            #[test]
3017            fn prop_byte_roundtrip_be(a in valid_i24()) {
3018                let bytes = a.to_be_bytes();
3019                let reconstructed = I24::from_be_bytes(bytes);
3020                prop_assert_eq!(a, reconstructed);
3021            }
3022
3023            #[test]
3024            fn prop_byte_roundtrip_ne(a in valid_i24()) {
3025                let bytes = a.to_ne_bytes();
3026                let reconstructed = I24::from_ne_bytes(bytes);
3027                prop_assert_eq!(a, reconstructed);
3028            }
3029
3030            #[test]
3031            fn prop_checked_arithmetic_consistency(
3032                (a, b) in i24_pair()
3033            ) {
3034                // Checked arithmetic should match wrapping arithmetic when no overflow occurs
3035                let a_i32 = a.to_i32();
3036                let b_i32 = b.to_i32();
3037
3038                if let Some(expected_add) = a_i32.checked_add(b_i32) {
3039                    if (I24Repr::MIN..=I24Repr::MAX).contains(&expected_add) {
3040                        prop_assert_eq!(a.checked_add(b), Some(I24::try_from_i32(expected_add).expect("Test value should convert successfully")));
3041                    }
3042                } else {
3043                    prop_assert_eq!(a.checked_add(b), None);
3044                }
3045
3046                if let Some(expected_sub) = a_i32.checked_sub(b_i32) {
3047                    if (I24Repr::MIN..=I24Repr::MAX).contains(&expected_sub) {
3048                        prop_assert_eq!(a.checked_sub(b), Some(I24::try_from_i32(expected_sub).expect("Test value should convert successfully")));
3049                    }
3050                } else {
3051                    prop_assert_eq!(a.checked_sub(b), None);
3052                }
3053
3054                if let Some(expected_mul) = a_i32.checked_mul(b_i32) {
3055                    if (I24Repr::MIN..=I24Repr::MAX).contains(&expected_mul) {
3056                        prop_assert_eq!(a.checked_mul(b), Some(I24::try_from_i32(expected_mul).expect("Test value should convert successfully")));
3057                    }
3058                } else {
3059                    prop_assert_eq!(a.checked_mul(b), None);
3060                }
3061            }
3062
3063            #[test]
3064            fn prop_from_str_parse_display_roundtrip(a in valid_i24()) {
3065                let s = format!("{}", a);
3066                let parsed = I24::from_str(&s).expect("Test value should convert successfully");
3067                prop_assert_eq!(a, parsed);
3068            }
3069
3070            #[test]
3071            fn prop_try_from_u8_always_succeeds(value in any::<u8>()) {
3072                let result = <I24 as From<u8>>::from(value);
3073                prop_assert_eq!(result.to_i32(), value as i32);
3074            }
3075
3076            #[test]
3077            fn prop_try_from_i8_always_succeeds(value in any::<i8>()) {
3078                let result = <I24 as From<i8>>::from(value);
3079                prop_assert_eq!(result.to_i32(), value as i32);
3080            }
3081
3082            #[test]
3083            fn prop_try_from_u16_always_succeeds(value in any::<u16>()) {
3084                let result = <I24 as From<u16>>::from(value);
3085                prop_assert_eq!(result.to_i32(), value as i32);
3086            }
3087
3088            #[test]
3089            fn prop_try_from_i16_always_succeeds(value in any::<i16>()) {
3090                let result = <I24 as From<i16>>::from(value);
3091                prop_assert_eq!(result.to_i32(), value as i32);
3092            }
3093
3094            #[test]
3095            fn prop_try_from_u32_range_check(value in any::<u32>()) {
3096                let result = I24::try_from(value);
3097                if value <= I24Repr::MAX as u32 {
3098                    prop_assert!(result.is_ok());
3099                    prop_assert_eq!(result.expect("Test value should convert successfully").to_i32(), value as i32);
3100                } else {
3101                    prop_assert!(result.is_err());
3102                }
3103            }
3104
3105            #[test]
3106            fn prop_try_from_i32_range_check(value in any::<i32>()) {
3107                let result = I24::try_from(value);
3108                if (I24Repr::MIN..=I24Repr::MAX).contains(&value) {
3109                    prop_assert!(result.is_ok());
3110                    prop_assert_eq!(result.expect("Test value should convert successfully").to_i32(), value);
3111                } else {
3112                    prop_assert!(result.is_err());
3113                }
3114            }
3115
3116            #[test]
3117            fn prop_shift_operations(a in valid_i24(), shift in 0u32..8u32) {
3118                // Test basic shift properties with small shifts to avoid overflow issues
3119                let shifted_left = a << shift;
3120                let shifted_right = a >> shift;
3121
3122                // Left shift by 0 should be identity
3123                if shift == 0 {
3124                    prop_assert_eq!(shifted_left, a);
3125                    prop_assert_eq!(shifted_right, a);
3126                }
3127
3128                // For very small positive values and small shifts,
3129                // left shift followed by right shift should recover the value
3130                if a.to_i32() >= 0 && a.to_i32() <= (I24Repr::MAX >> shift) {
3131                    let back_shifted = shifted_left >> shift;
3132                    prop_assert_eq!(back_shifted, a);
3133                }
3134            }
3135
3136            #[cfg(feature = "alloc")]
3137            #[test]
3138            fn prop_bulk_serialization_roundtrip(values in proptest::collection::vec(valid_i24(), 0..100)) {
3139                // Test big-endian roundtrip
3140                let be_bytes = I24::write_i24s_be(&values);
3141                let be_reconstructed = I24::read_i24s_be(&be_bytes).expect("Test value should convert successfully");
3142                prop_assert_eq!(&values, &be_reconstructed);
3143
3144                // Test little-endian roundtrip
3145                let le_bytes = I24::write_i24s_le(&values);
3146                let le_reconstructed = I24::read_i24s_le(&le_bytes).expect("Test value should convert successfully");
3147                prop_assert_eq!(&values, &le_reconstructed);
3148
3149                // Test native-endian roundtrip
3150                let ne_bytes = I24::write_i24s_ne(&values);
3151                let ne_reconstructed = I24::read_i24s_ne(&ne_bytes).expect("Test value should convert successfully");
3152                prop_assert_eq!(&values, &ne_reconstructed);
3153            }
3154
3155            #[test]
3156            fn prop_ordering_consistency(a in valid_i24(), b in valid_i24()) {
3157                let a_i32 = a.to_i32();
3158                let b_i32 = b.to_i32();
3159
3160                prop_assert_eq!(a.cmp(&b), a_i32.cmp(&b_i32));
3161                prop_assert_eq!(a < b, a_i32 < b_i32);
3162                prop_assert_eq!(a <= b, a_i32 <= b_i32);
3163                prop_assert_eq!(a > b, a_i32 > b_i32);
3164                prop_assert_eq!(a >= b, a_i32 >= b_i32);
3165                prop_assert_eq!(a == b, a_i32 == b_i32);
3166                prop_assert_eq!(a != b, a_i32 != b_i32);
3167            }
3168
3169            #[test]
3170            fn prop_from_str_error_handling(s in "\\PC*") {
3171                // Test that parsing invalid strings fails appropriately
3172                match I24::from_str(&s) {
3173                    Ok(val) => {
3174                        // If it succeeds, verify the value is in range
3175                        prop_assert!(val.to_i32() >= I24Repr::MIN);
3176                        prop_assert!(val.to_i32() <= I24Repr::MAX);
3177                    }
3178                    Err(e) => {
3179                        // Different error types should be appropriate
3180                        // Any error type is acceptable for invalid input strings
3181                        let _ = e.kind();
3182                    }
3183                }
3184            }
3185        }
3186    }
3187
3188    #[test]
3189    fn test_shift_operations_with_large_counts() {
3190        // Test shift counts >= 24 to ensure they're properly masked
3191        let a = i24!(0b1);
3192
3193        // Test left shifts with various counts
3194        assert_eq!(a << 0, i24!(0b1));
3195        assert_eq!(a << 1, i24!(0b10));
3196        assert_eq!(a << 23, I24::MIN); // 0x800000, which is the minimum negative value
3197        assert_eq!(a << 24, i24!(0b1)); // Should wrap around due to % 24
3198        assert_eq!(a << 25, i24!(0b10)); // Should be same as << 1
3199        assert_eq!(a << 47, I24::MIN); // Should be same as << 23
3200        assert_eq!(a << 48, i24!(0b1)); // Should be same as << 0
3201
3202        // Test right shifts with negative values for sign extension
3203        let b = i24!(-1); // All bits set
3204        assert_eq!(b >> 0, i24!(-1));
3205        assert_eq!(b >> 1, i24!(-1)); // Sign extension
3206        assert_eq!(b >> 23, i24!(-1)); // Still all bits set due to sign extension
3207        assert_eq!(b >> 24, i24!(-1)); // Should be same as >> 0
3208        assert_eq!(b >> 25, i24!(-1)); // Should be same as >> 1
3209        assert_eq!(b >> 47, i24!(-1)); // Should be same as >> 23
3210        assert_eq!(b >> 48, i24!(-1)); // Should be same as >> 0
3211
3212        // Test positive values
3213        let c = i24!(0x7FFFFF); // Maximum positive value
3214        assert_eq!(c >> 24, c); // Should be same as >> 0
3215        assert_eq!(c >> 25, c >> 1); // Should be same as >> 1
3216    }
3217
3218    #[test]
3219    fn test_ordering_across_boundaries() {
3220        // Test that ordering works correctly across negative/positive boundaries
3221        assert!(i24!(-1) < i24!(0));
3222        assert!(i24!(0) < i24!(1));
3223        assert!(i24!(-1) < i24!(1));
3224
3225        // Test MIN and MAX boundaries
3226        assert!(I24::MIN < i24!(-1));
3227        assert!(i24!(-1) < i24!(0));
3228        assert!(i24!(0) < i24!(1));
3229        assert!(i24!(1) < I24::MAX);
3230        assert!(I24::MIN < I24::MAX);
3231
3232        // Test that I24 ordering matches i32 ordering
3233        for a in [-1000, -1, 0, 1, 1000] {
3234            for b in [-1000, -1, 0, 1, 1000] {
3235                let i24_a = I24::try_from_i32(a).expect("Test value should convert successfully");
3236                let i24_b = I24::try_from_i32(b).expect("Test value should convert successfully");
3237
3238                assert_eq!(
3239                    i24_a < i24_b,
3240                    a < b,
3241                    "Ordering mismatch: I24({}) < I24({}) should be {}, but got {}",
3242                    a,
3243                    b,
3244                    a < b,
3245                    i24_a < i24_b
3246                );
3247                assert_eq!(
3248                    i24_a == i24_b,
3249                    a == b,
3250                    "Equality mismatch: I24({}) == I24({}) should be {}, but got {}",
3251                    a,
3252                    b,
3253                    a == b,
3254                    i24_a == i24_b
3255                );
3256            }
3257        }
3258    }
3259
3260    #[test]
3261    fn test_convenience_methods() {
3262        // Test abs method
3263        assert_eq!(i24!(10).abs(), i24!(10));
3264        assert_eq!(i24!(-10).abs(), i24!(10));
3265        assert_eq!(I24::MIN.abs(), I24::MIN); // Wraps around
3266
3267        // Test signum method
3268        assert_eq!(i24!(10).signum(), i24!(1));
3269        assert_eq!(i24!(0).signum(), i24!(0));
3270        assert_eq!(i24!(-10).signum(), i24!(-1));
3271
3272        // Test is_negative/is_positive
3273        assert!(!i24!(10).is_negative());
3274        assert!(!i24!(0).is_negative());
3275        assert!(i24!(-10).is_negative());
3276
3277        assert!(i24!(10).is_positive());
3278        assert!(!i24!(0).is_positive());
3279        assert!(!i24!(-10).is_positive());
3280
3281        // Test clamp
3282        assert_eq!(i24!(-3).clamp(i24!(-2), i24!(1)), i24!(-2));
3283        assert_eq!(i24!(0).clamp(i24!(-2), i24!(1)), i24!(0));
3284        assert_eq!(i24!(2).clamp(i24!(-2), i24!(1)), i24!(1));
3285
3286        // Test min/max
3287        assert_eq!(i24!(1).min(i24!(2)), i24!(1));
3288        assert_eq!(i24!(2).max(i24!(1)), i24!(2));
3289    }
3290
3291    #[test]
3292    fn test_signed_trait() {
3293        use num_traits::Signed;
3294
3295        // Test abs_sub method specifically
3296        let a = i24!(10);
3297        let b = i24!(5);
3298        let c = i24!(-3);
3299
3300        // Test positive difference
3301        assert_eq!(a.abs_sub(&b), i24!(5)); // 10 - 5 = 5
3302
3303        // Test when self <= other (should return 0)
3304        assert_eq!(b.abs_sub(&a), i24!(0)); // 5 - 10 <= 0, so return 0
3305        assert_eq!(a.abs_sub(&a), i24!(0)); // 10 - 10 = 0
3306
3307        // Test with negative numbers
3308        assert_eq!(a.abs_sub(&c), i24!(13)); // 10 - (-3) = 13
3309        assert_eq!(c.abs_sub(&a), i24!(0)); // -3 - 10 <= 0, so return 0
3310
3311        // Test that trait methods work correctly (should call our implementations)
3312        assert_eq!(Signed::abs(&i24!(10)), i24!(10));
3313        assert_eq!(Signed::abs(&i24!(-10)), i24!(10));
3314        assert_eq!(Signed::signum(&i24!(10)), i24!(1));
3315        assert_eq!(Signed::signum(&i24!(-10)), i24!(-1));
3316        assert_eq!(Signed::signum(&i24!(0)), i24!(0));
3317        assert!(Signed::is_positive(&i24!(10)));
3318        assert!(!Signed::is_positive(&i24!(-10)));
3319        assert!(!Signed::is_positive(&i24!(0)));
3320        assert!(!Signed::is_negative(&i24!(10)));
3321        assert!(Signed::is_negative(&i24!(-10)));
3322        assert!(!Signed::is_negative(&i24!(0)));
3323
3324        // Test edge cases for abs_sub
3325        assert_eq!(I24::MAX.abs_sub(&I24::MIN), I24::MAX - I24::MIN);
3326        assert_eq!(I24::MIN.abs_sub(&I24::MAX), i24!(0));
3327    }
3328
3329    #[test]
3330    fn test_wrapping_methods() {
3331        // Test that wrapping methods match operator behavior
3332        let a = i24!(100);
3333        let b = i24!(27);
3334
3335        assert_eq!(a.wrapping_add(b), a + b);
3336        assert_eq!(a.wrapping_sub(b), a - b);
3337        assert_eq!(a.wrapping_mul(b), a * b);
3338        assert_eq!(a.wrapping_div(b), a / b);
3339        assert_eq!(a.wrapping_rem(b), a % b);
3340        assert_eq!(a.wrapping_neg(), -a);
3341
3342        // Test wrapping behavior at boundaries
3343        assert_eq!(I24::MAX.wrapping_add(i24!(1)), I24::MIN);
3344        assert_eq!(I24::MIN.wrapping_sub(i24!(1)), I24::MAX);
3345        assert_eq!(I24::MIN.wrapping_neg(), I24::MIN);
3346    }
3347
3348    #[test]
3349    fn test_saturating_methods() {
3350        // Test normal operations
3351        let a = i24!(100);
3352        let b = i24!(27);
3353
3354        assert_eq!(a.saturating_add(b), i24!(127));
3355        assert_eq!(a.saturating_sub(b), i24!(73));
3356        assert_eq!(a.saturating_mul(b), i24!(2700));
3357
3358        // Test saturation at boundaries
3359        assert_eq!(I24::MAX.saturating_add(i24!(1)), I24::MAX);
3360        assert_eq!(I24::MIN.saturating_sub(i24!(1)), I24::MIN);
3361        assert_eq!(I24::MIN.saturating_neg(), I24::MAX);
3362        assert_eq!(I24::MIN.saturating_div(i24!(-1)), I24::MAX);
3363    }
3364
3365    #[cfg(feature = "num-cast")]
3366    #[test]
3367    fn test_num_cast_trait() {
3368        use num_traits::NumCast;
3369
3370        // Test successful conversions from various types
3371        assert_eq!(
3372            <I24 as NumCast>::from(1000i32),
3373            Some(I24::try_from_i32(1000).unwrap())
3374        );
3375        assert_eq!(
3376            <I24 as NumCast>::from(500u16),
3377            Some(I24::try_from_i32(500).unwrap())
3378        );
3379        assert_eq!(
3380            <I24 as NumCast>::from(100i8),
3381            Some(I24::try_from_i32(100).unwrap())
3382        );
3383        assert_eq!(
3384            <I24 as NumCast>::from(200u8),
3385            Some(I24::try_from_i32(200).unwrap())
3386        );
3387        assert_eq!(
3388            <I24 as NumCast>::from(-1000i32),
3389            Some(I24::try_from_i32(-1000).unwrap())
3390        );
3391
3392        // Test out of range conversions return None
3393        assert_eq!(<I24 as NumCast>::from(10_000_000i32), None);
3394        assert_eq!(<I24 as NumCast>::from(-10_000_000i32), None);
3395        assert_eq!(<I24 as NumCast>::from(20_000_000u32), None);
3396
3397        // Test edge cases
3398        assert_eq!(<I24 as NumCast>::from(I24::MAX.to_i32()), Some(I24::MAX));
3399        assert_eq!(<I24 as NumCast>::from(I24::MIN.to_i32()), Some(I24::MIN));
3400
3401        // Test floating point conversions
3402        assert_eq!(
3403            <I24 as NumCast>::from(1000.0f32),
3404            Some(I24::try_from_i32(1000).unwrap())
3405        );
3406        assert_eq!(
3407            <I24 as NumCast>::from(-500.5f32),
3408            Some(I24::try_from_i32(-500).unwrap())
3409        );
3410        assert_eq!(<I24 as NumCast>::from(1e10f64), None); // Too large
3411    }
3412}
3413#[cfg(test)]
3414mod wire_tests {
3415    use super::*;
3416
3417    #[test]
3418    fn test_i24bytes_size_and_alignment() {
3419        assert_eq!(core::mem::size_of::<I24Bytes>(), 3);
3420        assert_eq!(core::mem::align_of::<I24Bytes>(), 1);
3421    }
3422
3423    #[test]
3424    fn test_i24bytes_round_trip_le() {
3425        let test_values = [
3426            0i32, 1, -1, 123456, -123456, 8388607,  // MAX
3427            -8388608, // MIN
3428            255, -255, 65536, -65536,
3429        ];
3430
3431        for &value in &test_values {
3432            let i24_val = I24::try_from(value).expect("Test value should convert successfully");
3433            let wire = I24Bytes::from_i24_le(i24_val);
3434            let recovered = wire.to_i24_le();
3435            assert_eq!(i24_val, recovered, "Round-trip failed for value: {}", value);
3436        }
3437    }
3438
3439    #[test]
3440    fn test_i24bytes_round_trip_be() {
3441        let test_values = [
3442            0i32, 1, -1, 123456, -123456, 8388607,  // MAX
3443            -8388608, // MIN
3444        ];
3445
3446        for &value in &test_values {
3447            let i24_val = I24::try_from(value).expect("Test value should convert successfully");
3448            let wire = I24Bytes::from_i24_be(i24_val);
3449            let recovered = wire.to_i24_be();
3450            assert_eq!(i24_val, recovered, "Round-trip failed for value: {}", value);
3451        }
3452    }
3453
3454    #[test]
3455    fn test_i24bytes_endianness_difference() {
3456        let value = I24::try_from(0x123456).expect("Test value should convert successfully");
3457        let le_bytes = I24Bytes::from_i24_le(value);
3458        let be_bytes = I24Bytes::from_i24_be(value);
3459
3460        // The byte arrays should be different for non-symmetric values
3461        assert_ne!(le_bytes.to_bytes(), be_bytes.to_bytes());
3462
3463        // But they should convert to the same value when interpreted correctly
3464        assert_eq!(le_bytes.to_i24_le(), be_bytes.to_i24_be());
3465    }
3466
3467    #[test]
3468    fn test_i24bytes_specific_byte_patterns() {
3469        // Test specific known byte patterns
3470        let le_bytes = I24Bytes([0x40, 0xE2, 0x01]); // 123456 in LE
3471        let be_bytes = I24Bytes([0x01, 0xE2, 0x40]); // 123456 in BE
3472
3473        assert_eq!(
3474            le_bytes.to_i24_le(),
3475            I24::try_from(123456).expect("Test value should convert successfully")
3476        );
3477        assert_eq!(
3478            be_bytes.to_i24_be(),
3479            I24::try_from(123456).expect("Test value should convert successfully")
3480        );
3481    }
3482
3483    #[test]
3484    fn test_i24bytes_from_raw_bytes() {
3485        let raw_bytes = [0x12, 0x34, 0x56];
3486        let wire = I24Bytes::from_bytes(raw_bytes);
3487        assert_eq!(wire.to_bytes(), raw_bytes);
3488    }
3489
3490    #[test]
3491    fn test_i24bytes_bytemuck_traits() {
3492        // First verify that I24Bytes is indeed 3 bytes and aligned properly
3493        assert_eq!(core::mem::size_of::<I24Bytes>(), 3);
3494        assert_eq!(core::mem::align_of::<I24Bytes>(), 1);
3495
3496        // Test that I24Bytes can be used with bytemuck
3497        let bytes = [0x12, 0x34, 0x56, 0xAB, 0xCD, 0xEF];
3498
3499        // Try explicit casting to avoid confusion
3500        let first_i24 = I24Bytes([0x12, 0x34, 0x56]);
3501
3502        // Test casting individual I24Bytes to bytes
3503        let first_array = [first_i24];
3504        let first_bytes: &[u8] = bytemuck::cast_slice(&first_array);
3505        assert_eq!(first_bytes, &[0x12, 0x34, 0x56]);
3506
3507        // Test casting the byte array - use try_cast_slice for better error info
3508        let wire_slice = bytemuck::try_cast_slice::<u8, I24Bytes>(&bytes)
3509            .expect("Cast should succeed for properly aligned bytes");
3510        assert_eq!(wire_slice.len(), 2);
3511        assert_eq!(wire_slice[0].to_bytes(), [0x12, 0x34, 0x56]);
3512        assert_eq!(wire_slice[1].to_bytes(), [0xAB, 0xCD, 0xEF]);
3513    }
3514
3515    #[test]
3516    fn test_i24bytes_in_simple_struct() {
3517        use bytemuck::{Pod, Zeroable};
3518
3519        // Test I24Bytes as part of a simple struct (non-packed to avoid complications)
3520        #[repr(C)]
3521        #[derive(Copy, Clone, Debug, Pod, Zeroable)]
3522        struct SimpleWire {
3523            value: I24Bytes,
3524            padding: [u8; 1], // Add padding to make it clear this isn't about exact packing
3525        }
3526
3527        let wire = SimpleWire {
3528            value: I24Bytes::from_i24_le(
3529                I24::try_from(123456).expect("Test value should convert successfully"),
3530            ),
3531            padding: [0xFF],
3532        };
3533
3534        // Test that we can convert to bytes and back
3535        let bytes: [u8; 4] = bytemuck::cast(wire);
3536        let reconstructed: SimpleWire = bytemuck::cast(bytes);
3537
3538        assert_eq!(
3539            reconstructed.value.to_i24_le(),
3540            I24::try_from(123456).expect("Test value should convert successfully")
3541        );
3542        assert_eq!(reconstructed.padding, [0xFF]);
3543    }
3544
3545    #[test]
3546    fn test_i24bytes_sign_extension() {
3547        // Test that sign extension works correctly for negative values
3548        let negative_value = I24::try_from(-1).expect("Test value should convert successfully");
3549        let wire_le = I24Bytes::from_i24_le(negative_value);
3550        let wire_be = I24Bytes::from_i24_be(negative_value);
3551
3552        // -1 should be 0xFFFFFF in two's complement
3553        let expected_bytes = [0xFF, 0xFF, 0xFF];
3554        assert_eq!(wire_le.to_bytes(), expected_bytes);
3555        assert_eq!(wire_be.to_bytes(), expected_bytes);
3556
3557        // Both should convert back to -1
3558        assert_eq!(wire_le.to_i24_le(), negative_value);
3559        assert_eq!(wire_be.to_i24_be(), negative_value);
3560    }
3561
3562    #[test]
3563    fn test_i24bytes_zero_initialization() {
3564        let zero_wire: I24Bytes = bytemuck::Zeroable::zeroed();
3565        assert_eq!(zero_wire.to_bytes(), [0, 0, 0]);
3566        assert_eq!(
3567            zero_wire.to_i24_le(),
3568            I24::try_from(0).expect("Test value should convert successfully")
3569        );
3570        assert_eq!(
3571            zero_wire.to_i24_be(),
3572            I24::try_from(0).expect("Test value should convert successfully")
3573        );
3574    }
3575
3576    #[cfg(feature = "zerocopy")]
3577    #[test]
3578    fn test_i24bytes_zerocopy_traits() {
3579        // Test that I24Bytes implements the zerocopy traits correctly
3580        let bytes = [0x12, 0x34, 0x56];
3581
3582        // Test that we can create from bytes and convert back
3583        let wire = I24Bytes::from_bytes(bytes);
3584        assert_eq!(wire.to_bytes(), bytes);
3585
3586        // Test that I24Bytes has the expected alignment (should be 1)
3587        assert_eq!(core::mem::align_of::<I24Bytes>(), 1);
3588
3589        // The zerocopy traits are derive-only in this version, so we mainly
3590        // test that the derives compiled successfully and basic functionality works
3591    }
3592
3593    #[cfg(feature = "ndarray")]
3594    #[test]
3595    fn test_ndarray_scalar_operand() {
3596        // Test that I24 and I24Bytes implement ScalarOperand trait
3597        // This test mainly verifies the trait implementations compile correctly
3598        use ndarray::ScalarOperand;
3599
3600        let i24_val = crate::i24!(100);
3601        let i24_bytes = I24Bytes([0x64, 0x00, 0x00]); // 100 in little endian
3602
3603        // Test that we can use I24 as a scalar operand (trait bound check)
3604        fn check_scalar_operand<T: ScalarOperand>(_: T) {}
3605        check_scalar_operand(i24_val);
3606        check_scalar_operand(i24_bytes);
3607
3608        // These operations should compile if ScalarOperand is properly implemented
3609        // Note: actual arithmetic depends on ndarray implementing ops for I24/i32
3610    }
3611}