Skip to main content

range_set_blaze/
integer.rs

1use crate::UIntPlusOne;
2#[cfg(feature = "from_slice")]
3use crate::{RangeSetBlaze, from_slice::FromSliceIter};
4use core::hash::Hash;
5use core::net::{Ipv4Addr, Ipv6Addr};
6use core::ops::{AddAssign, SubAssign};
7use core::panic;
8use core::{fmt, ops::RangeInclusive};
9use num_traits::ops::overflowing::OverflowingSub;
10
11#[cfg(feature = "from_slice")]
12#[allow(clippy::redundant_pub_crate)]
13pub(crate) const LANES: usize = 16;
14
15#[allow(unused_imports)]
16use num_traits::Zero;
17
18/// Represents elements that can be used within [`RangeSetBlaze`] and as keys in [`RangeMapBlaze`].
19///
20/// This includes integer types from `u8` to `u128` (including `usize`), `i8` to `i128` (including `isize`),
21/// as well as `char`, `Ipv4Addr`, and `Ipv6Addr`.
22///
23/// [`RangeSetBlaze`]: crate::RangeSetBlaze
24/// [`RangeMapBlaze`]: crate::RangeMapBlaze
25pub trait Integer: Copy + PartialEq + PartialOrd + Ord + fmt::Debug + Send + Sync {
26    /// Attempts to add one to the current value, returning `None` if the operation would overflow.
27    fn checked_add_one(self) -> Option<Self>;
28
29    /// Adds one to the current value, panicking in debug mode if the operation overflows.
30    ///
31    /// # Examples
32    /// ```
33    /// use range_set_blaze::Integer;
34    ///
35    /// assert_eq!(5u8.add_one(), 6);
36    /// ```
37    #[must_use]
38    fn add_one(self) -> Self;
39
40    /// Subtracts one from the current value, panicking in debug mode if the operation underflows.
41    ///
42    /// # Examples
43    /// ```
44    /// use range_set_blaze::Integer;
45    ///
46    /// assert_eq!(5u8.sub_one(), 4);
47    /// ```
48    #[must_use]
49    fn sub_one(self) -> Self;
50
51    /// Subtracts one from the current value and assigns it back to `self`.
52    fn assign_sub_one(&mut self);
53
54    /// Returns an exhausted range, which is a range that starts from the maximum value and ends at the minimum value.
55    /// This results in an empty range.
56    ///
57    /// # Examples
58    /// ```
59    /// use range_set_blaze::Integer;
60    ///
61    /// let range = u8::exhausted_range();
62    /// assert!(range.is_empty());
63    /// ```
64    #[must_use]
65    fn exhausted_range() -> RangeInclusive<Self> {
66        Self::max_value()..=Self::min_value()
67    }
68
69    /// Advances the iterator for the given range by one step, returning the next value or `None` if the range is exhausted.
70    ///
71    /// This method needs to be defined on each type of interest because the `core::Step` trait is not stable yet.
72    fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self>;
73
74    /// Advances the iterator for the given range in reverse by one step, returning the previous value or `None` if the range is exhausted.
75    ///
76    /// This method needs to be defined on each type of interest because the `core::Step` trait is not stable yet.
77    fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self>;
78
79    /// Returns the minimum value that can be represented by the type.
80    ///
81    /// # Examples
82    /// ```
83    /// use range_set_blaze::Integer;
84    ///
85    /// assert_eq!(u8::min_value(), 0);
86    /// ```
87    #[must_use]
88    fn min_value() -> Self;
89
90    /// Returns the maximum value that can be represented by the type.
91    ///
92    /// # Examples
93    /// ```
94    /// use range_set_blaze::Integer;
95    ///
96    /// assert_eq!(u8::max_value(), 255);
97    /// ```
98    #[must_use]
99    fn max_value() -> Self;
100
101    #[cfg(feature = "from_slice")]
102    /// Creates a [`RangeSetBlaze`] from a slice, specific to the integer type.
103    ///
104    /// [`RangeSetBlaze`]: crate::RangeSetBlaze
105    fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self>;
106
107    /// The type representing the safe length for a [`RangeSetBlaze`]. For example, the length of a `RangeSetBlaze<u8>` is `u16` to handle ranges up to 256 elements.
108    /// For larger types like `u128`, this is represented by a custom type `UIntPlusOne<u128>`.
109    ///
110    /// [`RangeSetBlaze`]: crate::RangeSetBlaze
111    ///
112    /// # Examples
113    /// ```
114    /// use range_set_blaze::{RangeSetBlaze, Integer};
115    ///
116    /// let len: <u8 as Integer>::SafeLen = RangeSetBlaze::from_iter([0u8..=255]).len();
117    /// assert_eq!(len, 256);
118    /// ```
119    type SafeLen: Hash
120        + Copy
121        + PartialEq
122        + PartialOrd
123        + num_traits::Zero
124        + num_traits::One
125        + AddAssign
126        + SubAssign;
127
128    /// Calculates the length of a range without overflow.
129    ///
130    /// # Examples
131    /// ```
132    /// use range_set_blaze::Integer;
133    ///
134    /// assert_eq!(<u8 as Integer>::safe_len(&(0..=255)), 256);
135    /// ```
136    fn safe_len(range: &RangeInclusive<Self>) -> <Self as Integer>::SafeLen;
137
138    // FUTURE define .len() SortedDisjoint
139
140    /// Converts a `f64` to [`Integer::SafeLen`] using the formula `f as Self::SafeLen`. For large integer types, this will result in a loss of precision.
141    fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen;
142
143    /// Converts [`Integer::SafeLen`] to `f64`, potentially losing precision for large values.
144    fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64;
145
146    /// Computes `self + (b - 1)` where `b` is of type [`Integer::SafeLen`].
147    #[must_use]
148    fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self;
149
150    /// Computes `self - (b - 1)` where `b` is of type [`Integer::SafeLen`].
151    #[must_use]
152    fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self;
153}
154
155/// Macro to implement the `Integer` trait for specific integer types.
156macro_rules! impl_integer_ops {
157    ($type:ty, $type2:ty) => {
158        #[inline]
159        fn checked_add_one(self) -> Option<Self> {
160            self.checked_add(1)
161        }
162
163        #[inline]
164        fn add_one(self) -> Self {
165            self + 1
166        }
167
168        #[inline]
169        fn sub_one(self) -> Self {
170            self - 1
171        }
172
173        #[inline]
174        fn assign_sub_one(&mut self) {
175            *self -= 1;
176        }
177
178        #[inline]
179        fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
180            range.next()
181        }
182
183        #[inline]
184        fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
185            range.next_back()
186        }
187
188        #[inline]
189        fn min_value() -> Self {
190            Self::MIN
191        }
192
193        #[inline]
194        fn max_value() -> Self {
195            Self::MAX
196        }
197
198        #[cfg(feature = "from_slice")]
199        #[inline]
200        fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
201            FromSliceIter::<Self, LANES>::new(slice.as_ref()).collect()
202        }
203
204        #[allow(clippy::cast_sign_loss)]
205        fn safe_len(r: &RangeInclusive<Self>) -> <Self as Integer>::SafeLen {
206            // 1️⃣ Contract: caller promises start ≤ end  (checked only in debug builds)
207            debug_assert!(r.start() <= r.end(), "start ≤ end required");
208
209            // 2️⃣ Compute distance in `Self` then reinterpret‑cast to the first
210            //     widening type `$type2` (loss‑free under the invariant above).
211            let delta_wide: $type2 = r.end().overflowing_sub(r.start()).0 as $type2;
212
213            // 3️⃣ Final widening to `SafeLen`.
214            //    `try_from` is infallible here, so LLVM removes the check in release.
215            <<Self as Integer>::SafeLen>::try_from(delta_wide)
216                .expect("$type2 ⊆ SafeLen; optimizer drops this in release")
217                + 1 // 4️⃣ Inclusive length = distance + 1
218        }
219
220        #[allow(clippy::cast_precision_loss, clippy::cast_lossless)]
221        fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
222            len as f64
223        }
224
225        #[allow(clippy::cast_sign_loss)]
226        #[allow(clippy::cast_precision_loss)]
227        #[allow(clippy::cast_possible_truncation)]
228        fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
229            f as Self::SafeLen
230        }
231
232        #[allow(clippy::cast_possible_truncation)]
233        #[allow(clippy::cast_possible_wrap)]
234        fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
235            #[cfg(debug_assertions)]
236            {
237                let max_len = Self::safe_len(&(self..=Self::MAX));
238                assert!(
239                    b > 0 && b <= max_len,
240                    "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
241                );
242            }
243            // If b is in range, two’s-complement wrap-around yields the correct inclusive end even if the add overflows
244            self.wrapping_add((b - 1) as Self)
245        }
246
247        #[allow(clippy::cast_possible_truncation)]
248        #[allow(clippy::cast_possible_wrap)]
249        fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
250            #[cfg(debug_assertions)]
251            {
252                let max_len = Self::safe_len(&(Self::MIN..=self));
253                assert!(
254                    0 < b && b <= max_len,
255                    "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
256                );
257            }
258            // If b is in range, two’s-complement wrap-around yields the correct start even if the sub overflows
259            self.wrapping_sub((b - 1) as Self)
260        }
261    };
262}
263
264impl Integer for i8 {
265    type SafeLen = u16;
266    impl_integer_ops!(i8, u8);
267}
268
269impl Integer for u8 {
270    type SafeLen = u16;
271    impl_integer_ops!(u8, Self);
272}
273
274impl Integer for i32 {
275    type SafeLen = u64;
276
277    impl_integer_ops!(i32, u32);
278}
279
280impl Integer for u32 {
281    type SafeLen = u64;
282
283    impl_integer_ops!(u32, Self);
284}
285
286impl Integer for i64 {
287    type SafeLen = u128;
288
289    impl_integer_ops!(i64, u64);
290}
291
292impl Integer for u64 {
293    type SafeLen = u128;
294
295    impl_integer_ops!(u64, Self);
296}
297
298impl Integer for i128 {
299    type SafeLen = UIntPlusOne<u128>;
300
301    #[inline]
302    fn checked_add_one(self) -> Option<Self> {
303        self.checked_add(1)
304    }
305
306    #[inline]
307    fn add_one(self) -> Self {
308        self + 1
309    }
310
311    #[inline]
312    fn sub_one(self) -> Self {
313        self - 1
314    }
315
316    #[inline]
317    fn assign_sub_one(&mut self) {
318        *self -= 1;
319    }
320
321    #[inline]
322    fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
323        range.next()
324    }
325
326    #[inline]
327    fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
328        range.next_back()
329    }
330
331    #[inline]
332    fn min_value() -> Self {
333        Self::MIN
334    }
335
336    #[inline]
337    fn max_value() -> Self {
338        Self::MAX
339    }
340
341    #[cfg(feature = "from_slice")]
342    #[inline]
343    fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
344        RangeSetBlaze::from_iter(slice.as_ref())
345    }
346
347    #[allow(clippy::cast_sign_loss)]
348    fn safe_len(r: &RangeInclusive<Self>) -> <Self as Integer>::SafeLen {
349        debug_assert!(r.start() <= r.end());
350        // Signed sub may overflow, but casting preserves correct unsigned distance
351        let less1 = r.end().overflowing_sub(r.start()).0 as u128;
352        let less1 = UIntPlusOne::UInt(less1);
353        less1 + UIntPlusOne::UInt(1)
354    }
355
356    #[allow(clippy::cast_precision_loss)]
357    #[allow(clippy::cast_possible_truncation)]
358    fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
359        match len {
360            UIntPlusOne::UInt(v) => v as f64,
361            UIntPlusOne::MaxPlusOne => UIntPlusOne::<u128>::max_plus_one_as_f64(),
362        }
363    }
364
365    #[allow(clippy::cast_sign_loss)]
366    #[allow(clippy::cast_possible_truncation)]
367    fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
368        if f >= UIntPlusOne::<u128>::max_plus_one_as_f64() {
369            UIntPlusOne::MaxPlusOne
370        } else {
371            UIntPlusOne::UInt(f as u128)
372        }
373    }
374
375    /// Computes the inclusive end of a range starting at `self` with length `b`,
376    /// by returning `self + (b - 1)`.
377    ///
378    /// # Panics
379    /// In debug builds, panics if `b` is zero or too large to compute a valid result.
380    /// In release builds, this will either panic or wrap on overflow; the result may be meaningless,
381    /// but it is always defined and safe (never causes undefined behavior)
382    #[allow(clippy::cast_possible_wrap)]
383    fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
384        #[cfg(debug_assertions)]
385        {
386            let max_len = Self::safe_len(&(self..=Self::MAX));
387            assert!(
388                UIntPlusOne::zero() < b && b <= max_len,
389                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
390            );
391        }
392
393        let UIntPlusOne::UInt(b) = b else {
394            if self == Self::MIN {
395                return Self::MAX;
396            }
397            // This is only reached in release builds.
398            let max_len = Self::safe_len(&(self..=Self::MAX));
399            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
400        };
401        // If b is in range, two’s-complement wrap-around yields the correct inclusive end even if the add overflows
402        self.wrapping_add((b - 1) as Self)
403    }
404
405    /// Computes the inclusive start of a range ending at `self` with length `b`,
406    /// by returning `self - (b - 1)`.
407    ///
408    /// # Panics
409    /// In debug builds, panics if `b` is zero or too large to compute a valid result.
410    /// In release builds, this will either panic or wrap on overflow; the result may be meaningless,
411    /// but it is always defined and safe (never causes undefined behavior).
412    #[allow(clippy::cast_possible_wrap)]
413    fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
414        #[cfg(debug_assertions)]
415        {
416            let max_len = Self::safe_len(&(Self::MIN..=self));
417            assert!(
418                UIntPlusOne::zero() < b && b <= max_len,
419                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
420            );
421        }
422
423        let UIntPlusOne::UInt(b) = b else {
424            if self == Self::MAX {
425                return Self::MIN;
426            }
427            // This is only reached in release builds.
428            let max_len = Self::safe_len(&(Self::MIN..=self));
429            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
430        };
431
432        // If b is in range, two’s-complement wrap-around yields the correct inclusive start even if the subtraction overflows
433        self.wrapping_sub((b - 1) as Self)
434    }
435}
436
437impl Integer for u128 {
438    type SafeLen = UIntPlusOne<Self>;
439
440    #[inline]
441    fn checked_add_one(self) -> Option<Self> {
442        self.checked_add(1)
443    }
444
445    #[inline]
446    fn add_one(self) -> Self {
447        self + 1
448    }
449
450    #[inline]
451    fn sub_one(self) -> Self {
452        self - 1
453    }
454
455    #[inline]
456    fn assign_sub_one(&mut self) {
457        *self -= 1;
458    }
459
460    #[inline]
461    fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
462        range.next()
463    }
464
465    #[inline]
466    fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
467        range.next_back()
468    }
469
470    #[inline]
471    fn min_value() -> Self {
472        Self::MIN
473    }
474
475    #[inline]
476    fn max_value() -> Self {
477        Self::MAX
478    }
479
480    #[cfg(feature = "from_slice")]
481    #[inline]
482    fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
483        RangeSetBlaze::from_iter(slice.as_ref())
484    }
485
486    fn safe_len(r: &RangeInclusive<Self>) -> <Self as Integer>::SafeLen {
487        debug_assert!(r.start() <= r.end());
488        UIntPlusOne::UInt(r.end() - r.start()) + UIntPlusOne::UInt(1)
489    }
490
491    #[allow(clippy::cast_precision_loss)]
492    fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
493        match len {
494            UIntPlusOne::UInt(len) => len as f64,
495            UIntPlusOne::MaxPlusOne => UIntPlusOne::<Self>::max_plus_one_as_f64(),
496        }
497    }
498
499    #[allow(clippy::cast_sign_loss)]
500    #[allow(clippy::cast_possible_truncation)]
501    fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
502        if f >= UIntPlusOne::<Self>::max_plus_one_as_f64() {
503            UIntPlusOne::MaxPlusOne
504        } else {
505            UIntPlusOne::UInt(f as Self)
506        }
507    }
508
509    /// Computes the inclusive end of a range starting at `self` with length `b`,
510    /// by returning `self + (b - 1)`.
511    ///
512    /// # Panics
513    /// In debug builds, panics if `b` is zero or too large to compute a valid result.
514    /// In release builds, this will either panic or wrap on overflow; the result may be meaningless,
515    /// but it is always defined and safe (never causes undefined behavior)
516    fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
517        #[cfg(debug_assertions)]
518        {
519            let max_len = Self::safe_len(&(self..=Self::MAX));
520            assert!(
521                UIntPlusOne::zero() < b && b <= max_len,
522                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
523            );
524        }
525
526        let UIntPlusOne::UInt(b) = b else {
527            if self == Self::MIN {
528                return Self::MAX;
529            }
530            // This is only reached in release builds.
531            let max_len = Self::safe_len(&(self..=Self::MAX));
532            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
533        };
534        // If b is in range, two’s-complement wrap-around yields the correct inclusive end even if the add overflows
535        self.wrapping_add((b - 1) as Self)
536    }
537
538    /// Computes the inclusive start of a range ending at `self` with length `b`,
539    /// by returning `self - (b - 1)`.
540    ///
541    /// # Panics
542    /// In debug builds, panics if `b` is zero or too large to compute a valid result.
543    /// In release builds, this will either panic or wrap on overflow; the result may be meaningless,
544    /// but it is always defined and safe (never causes undefined behavior).
545    #[allow(clippy::cast_possible_wrap)]
546    fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
547        #[cfg(debug_assertions)]
548        {
549            let max_len = Self::safe_len(&(Self::MIN..=self));
550            assert!(
551                UIntPlusOne::zero() < b && b <= max_len,
552                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
553            );
554        }
555
556        let UIntPlusOne::UInt(b) = b else {
557            if self == Self::MAX {
558                return Self::MIN;
559            }
560            // This is only reached in release builds.
561            let max_len = Self::safe_len(&(Self::MIN..=self));
562            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
563        };
564
565        // If b is in range, two’s-complement wrap-around yields the correct inclusive start even if the subtraction overflows
566        self.wrapping_sub((b - 1) as Self)
567    }
568}
569
570impl Integer for isize {
571    #[cfg(target_pointer_width = "32")]
572    type SafeLen = u64;
573    #[cfg(target_pointer_width = "64")]
574    type SafeLen = u128;
575
576    impl_integer_ops!(isize, usize);
577}
578
579impl Integer for usize {
580    #[cfg(target_pointer_width = "32")]
581    type SafeLen = u64;
582    #[cfg(target_pointer_width = "64")]
583    type SafeLen = u128;
584
585    impl_integer_ops!(usize, Self);
586}
587
588impl Integer for i16 {
589    type SafeLen = u32;
590
591    impl_integer_ops!(i16, u16);
592}
593
594impl Integer for u16 {
595    type SafeLen = u32;
596
597    impl_integer_ops!(u16, Self);
598}
599
600impl Integer for Ipv4Addr {
601    type SafeLen = u64;
602
603    #[inline]
604    fn checked_add_one(self) -> Option<Self> {
605        let num = u32::from(self);
606        num.checked_add(1).map(Self::from)
607    }
608
609    #[inline]
610    fn add_one(self) -> Self {
611        let num = u32::from(self);
612        Self::from(num + 1)
613    }
614
615    #[inline]
616    fn sub_one(self) -> Self {
617        let num = u32::from(self);
618        Self::from(num - 1)
619    }
620
621    #[inline]
622    fn assign_sub_one(&mut self) {
623        let num = u32::from(*self);
624        *self = Self::from(num - 1);
625    }
626
627    #[inline]
628    fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
629        range.next()
630    }
631
632    #[inline]
633    fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
634        range.next_back()
635    }
636
637    #[inline]
638    fn min_value() -> Self {
639        Self::new(0, 0, 0, 0)
640    }
641
642    #[inline]
643    fn max_value() -> Self {
644        Self::new(255, 255, 255, 255)
645    }
646
647    #[cfg(feature = "from_slice")]
648    #[inline]
649    fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
650        RangeSetBlaze::from_iter(slice.as_ref())
651    }
652
653    #[allow(clippy::cast_lossless)]
654    fn safe_len(r: &RangeInclusive<Self>) -> <Self as Integer>::SafeLen {
655        let start_num = u32::from(*r.start());
656        let end_num = u32::from(*r.end());
657        debug_assert!(start_num <= end_num);
658        // Signed sub may overflow, but casting preserves correct unsigned distance
659        end_num.overflowing_sub(start_num).0 as <Self as Integer>::SafeLen + 1
660    }
661
662    #[allow(clippy::cast_precision_loss)]
663    fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
664        len as f64
665    }
666
667    #[allow(clippy::cast_possible_truncation)]
668    #[allow(clippy::cast_sign_loss)]
669    fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
670        f as Self::SafeLen
671    }
672
673    #[allow(clippy::cast_possible_truncation)]
674    #[allow(clippy::cast_possible_wrap)]
675    fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
676        #[cfg(debug_assertions)]
677        {
678            let max_len = Self::safe_len(&(self..=Self::max_value()));
679            assert!(
680                b > 0 && b <= max_len,
681                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
682            );
683        }
684        // If b is in range, two’s-complement wrap-around yields the correct inclusive end even if the add overflows
685        u32::from(self).wrapping_add((b - 1) as u32).into()
686    }
687
688    #[allow(clippy::cast_possible_truncation)]
689    #[allow(clippy::cast_possible_wrap)]
690    fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
691        #[cfg(debug_assertions)]
692        {
693            let max_len = Self::safe_len(&(Self::min_value()..=self));
694            assert!(
695                0 < b && b <= max_len,
696                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
697            );
698        }
699        // If b is in range, two’s-complement wrap-around yields the correct start even if the sub overflows
700        u32::from(self).wrapping_sub((b - 1) as u32).into()
701    }
702}
703
704impl Integer for Ipv6Addr {
705    type SafeLen = UIntPlusOne<u128>;
706
707    #[inline]
708    fn checked_add_one(self) -> Option<Self> {
709        let num = u128::from(self);
710        num.checked_add(1).map(Self::from)
711    }
712
713    #[inline]
714    fn add_one(self) -> Self {
715        let num = u128::from(self);
716        Self::from(num + 1)
717    }
718
719    #[inline]
720    fn sub_one(self) -> Self {
721        let num = u128::from(self);
722        Self::from(num - 1)
723    }
724
725    #[inline]
726    fn assign_sub_one(&mut self) {
727        let num = u128::from(*self);
728        *self = Self::from(num - 1);
729    }
730
731    #[inline]
732    fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
733        range.next()
734    }
735
736    #[inline]
737    fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
738        range.next_back()
739    }
740
741    #[inline]
742    fn min_value() -> Self {
743        Self::new(0, 0, 0, 0, 0, 0, 0, 0)
744    }
745
746    #[inline]
747    fn max_value() -> Self {
748        Self::from(u128::MAX)
749    }
750
751    #[cfg(feature = "from_slice")]
752    #[inline]
753    fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
754        RangeSetBlaze::from_iter(slice.as_ref())
755    }
756
757    fn safe_len(r: &RangeInclusive<Self>) -> <Self as Integer>::SafeLen {
758        let start_num = u128::from(*r.start());
759        let end_num = u128::from(*r.end());
760
761        debug_assert!(start_num <= end_num);
762        UIntPlusOne::UInt(end_num - start_num) + UIntPlusOne::UInt(1)
763    }
764
765    #[allow(clippy::cast_precision_loss)]
766    fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
767        match len {
768            UIntPlusOne::UInt(len) => len as f64,
769            UIntPlusOne::MaxPlusOne => UIntPlusOne::<u128>::max_plus_one_as_f64(),
770        }
771    }
772
773    #[allow(clippy::cast_possible_truncation)]
774    #[allow(clippy::cast_sign_loss)]
775    fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
776        if f >= UIntPlusOne::<u128>::max_plus_one_as_f64() {
777            UIntPlusOne::MaxPlusOne
778        } else {
779            UIntPlusOne::UInt(f as u128)
780        }
781    }
782
783    /// Computes the inclusive end of a range starting at `self` with length `b`,
784    /// by returning `self + (b - 1)`.
785    ///
786    /// # Panics
787    /// In debug builds, panics if `b` is zero or too large to compute a valid result.
788    /// In release builds, this will either panic or wrap on overflow; the result may be meaningless,
789    /// but it is always defined and safe (never causes undefined behavior)
790    fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
791        #[cfg(debug_assertions)]
792        {
793            let max_len = Self::safe_len(&(self..=Self::max_value()));
794            assert!(
795                UIntPlusOne::zero() < b && b <= max_len,
796                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
797            );
798        }
799
800        let UIntPlusOne::UInt(b) = b else {
801            if self == Self::min_value() {
802                return Self::max_value();
803            }
804            // This is only reached in release builds.
805            let max_len = Self::safe_len(&(self..=Self::max_value()));
806            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
807        };
808        // If b is in range, two’s-complement wrap-around yields the correct inclusive end even if the add overflows
809        u128::from(self).wrapping_add(b - 1).into()
810    }
811
812    /// Computes the inclusive start of a range ending at `self` with length `b`,
813    /// by returning `self - (b - 1)`.
814    ///
815    /// # Panics
816    /// In debug builds, panics if `b` is zero or too large to compute a valid result.
817    /// In release builds, this will either panic or wrap on overflow; the result may be meaningless,
818    /// but it is always defined and safe (never causes undefined behavior).
819    #[allow(clippy::cast_possible_wrap)]
820    fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
821        #[cfg(debug_assertions)]
822        {
823            let max_len = Self::safe_len(&(Self::min_value()..=self));
824            assert!(
825                UIntPlusOne::zero() < b && b <= max_len,
826                "b must be in range 1..=max_len (b = {b}, max_len = {max_len})"
827            );
828        }
829
830        let UIntPlusOne::UInt(b) = b else {
831            if self == Self::max_value() {
832                return Self::min_value();
833            }
834            // This is only reached in release builds.
835            let max_len = Self::safe_len(&(Self::min_value()..=self));
836            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
837        };
838
839        // If b is in range, two’s-complement wrap-around yields the correct inclusive start even if the subtraction overflows
840        u128::from(self).wrapping_sub(b - 1).into()
841    }
842}
843
844// all inclusive
845const SURROGATE_START: u32 = 0xD800;
846const SURROGATE_END: u32 = 0xDFFF;
847
848impl Integer for char {
849    // in general, the length of a 32-bit inclusive range does not fit in a u32,
850    // but unicode doesn't use the full range, so it does fit
851    type SafeLen = u32;
852
853    #[inline]
854    fn checked_add_one(self) -> Option<Self> {
855        // Can't overflow u64 because of the range of char
856        let mut num = u32::from(self) + 1;
857        // skip over the surrogate range
858        if num == SURROGATE_START {
859            num = SURROGATE_END + 1;
860        }
861        // Will report char overflow as None
862        Self::from_u32(num)
863    }
864
865    #[inline]
866    fn add_one(self) -> Self {
867        self.checked_add_one().unwrap_or_else(|| {
868            panic!("char overflow"); // Panics in both debug and release modes
869        })
870    }
871
872    #[inline]
873    fn sub_one(self) -> Self {
874        let mut num = u32::from(self).wrapping_sub(1);
875        if num == SURROGATE_END {
876            num = SURROGATE_START - 1;
877        }
878        Self::from_u32(num).expect("sub_one: underflow or invalid char (e.g., called on '\\u{0}')")
879    }
880
881    #[inline]
882    fn assign_sub_one(&mut self) {
883        *self = self.sub_one();
884    }
885
886    #[inline]
887    fn range_next(range: &mut RangeInclusive<Self>) -> Option<Self> {
888        range.next()
889    }
890
891    #[inline]
892    fn range_next_back(range: &mut RangeInclusive<Self>) -> Option<Self> {
893        range.next_back()
894    }
895
896    #[inline]
897    fn min_value() -> Self {
898        '\u{0}'
899    }
900
901    #[inline]
902    fn max_value() -> Self {
903        '\u{10FFFF}'
904    }
905
906    #[cfg(feature = "from_slice")]
907    #[inline]
908    fn from_slice(slice: impl AsRef<[Self]>) -> RangeSetBlaze<Self> {
909        RangeSetBlaze::from_iter(slice.as_ref())
910    }
911
912    fn safe_len(r: &RangeInclusive<Self>) -> <Self as Integer>::SafeLen {
913        // assume valid, non-empty range
914        let start_num = u32::from(*r.start());
915        let end_num = u32::from(*r.end());
916        let mut len = (end_num - start_num) as <Self as Integer>::SafeLen + 1;
917        if start_num < SURROGATE_START && SURROGATE_END < end_num {
918            len -= (SURROGATE_END - SURROGATE_START + 1) as <Self as Integer>::SafeLen;
919        }
920        len
921    }
922
923    #[allow(clippy::cast_precision_loss, clippy::cast_lossless)]
924    fn safe_len_to_f64_lossy(len: Self::SafeLen) -> f64 {
925        len as f64
926    }
927
928    #[allow(clippy::cast_possible_truncation)]
929    #[allow(clippy::cast_sign_loss)]
930    fn f64_to_safe_len_lossy(f: f64) -> Self::SafeLen {
931        f as Self::SafeLen
932    }
933
934    fn inclusive_end_from_start(self, b: Self::SafeLen) -> Self {
935        fn private_panic(a: char, b: u32) -> ! {
936            let max_len = char::safe_len(&(char::MIN..=a));
937            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
938        }
939
940        let Some(b_minus_one) = b.checked_sub(1) else {
941            private_panic(self, b);
942        };
943
944        let a = u32::from(self);
945        let Some(mut num) = a.checked_add(b_minus_one) else {
946            private_panic(self, b);
947        };
948        if a < SURROGATE_START && SURROGATE_START <= num {
949            let Some(num2) = num.checked_add(SURROGATE_END - SURROGATE_START + 1) else {
950                private_panic(self, b);
951            };
952            num = num2;
953        }
954
955        let Some(result) = Self::from_u32(num) else {
956            private_panic(self, b);
957        };
958        result
959    }
960
961    fn start_from_inclusive_end(self, b: Self::SafeLen) -> Self {
962        fn private_panic(a: char, b: u32) -> ! {
963            let max_len = char::safe_len(&(char::MIN..=a));
964            panic!("b must be in range 1..=max_len (b = {b}, max_len = {max_len})");
965        }
966
967        let Some(b_minus_one) = b.checked_sub(1) else {
968            private_panic(self, b);
969        };
970
971        let a = u32::from(self);
972        let Some(mut num) = a.checked_sub(b_minus_one) else {
973            private_panic(self, b);
974        };
975        if num <= SURROGATE_END && SURROGATE_END < a {
976            let Some(num2) = num.checked_sub(SURROGATE_END - SURROGATE_START + 1) else {
977                private_panic(self, b);
978            };
979            num = num2;
980        }
981
982        Self::from_u32(num).expect("Real Assert: Impossible for this to fail")
983    }
984}
985
986#[cfg(test)]
987mod tests {
988    use super::*;
989    use crate::prelude::*;
990    use num_traits::{One, Zero};
991    use syntactic_for::syntactic_for;
992
993    use wasm_bindgen_test::*;
994    wasm_bindgen_test_configure!(run_in_browser);
995
996    #[test]
997    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
998    fn coverage_integer() {
999        let mut a = 0u8..=0u8;
1000        assert_eq!(u8::range_next_back(&mut a), Some(0));
1001        assert_eq!(u8::range_next(&mut a), None);
1002
1003        let mut b = 0i128..=0i128;
1004        assert_eq!(i128::range_next_back(&mut b), Some(0));
1005        assert_eq!(i128::range_next(&mut b), None);
1006
1007        let mut b = 0i128;
1008        i128::assign_sub_one(&mut b);
1009        assert_eq!(b, -1);
1010
1011        // convert  UIntPlusOne::MaxPlusOne to f64 and back
1012        let f = i128::safe_len_to_f64_lossy(UIntPlusOne::MaxPlusOne);
1013        let i = i128::f64_to_safe_len_lossy(f);
1014        assert_eq!(i, UIntPlusOne::MaxPlusOne);
1015
1016        let mut b = 0u128..=0u128;
1017        assert_eq!(u128::range_next_back(&mut b), Some(0));
1018        assert_eq!(u128::range_next(&mut b), None);
1019
1020        let mut b = 1u128;
1021        u128::assign_sub_one(&mut b);
1022        assert_eq!(b, 0);
1023
1024        // convert  UIntPlusOne::MaxPlusOne to f64 and back
1025        let f = u128::safe_len_to_f64_lossy(UIntPlusOne::MaxPlusOne);
1026        let i = u128::f64_to_safe_len_lossy(f);
1027        assert_eq!(i, UIntPlusOne::MaxPlusOne);
1028    }
1029
1030    #[test]
1031    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1032    #[should_panic(expected = "1")]
1033    #[cfg(debug_assertions)] // Only run this test in debug mode
1034    fn test_add_len_less_one_with_max_plus_one() {
1035        let value: i128 = 100;
1036        let len = UIntPlusOne::MaxPlusOne;
1037        let _ = value.inclusive_end_from_start(len); // This should panic in debug mode
1038    }
1039
1040    #[test]
1041    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1042    #[should_panic(expected = "2")]
1043    #[cfg(debug_assertions)] // Only run this test in debug mode
1044    fn test_sub_len_less_one_with_max_plus_one() {
1045        let value: i128 = 100;
1046        let len = UIntPlusOne::MaxPlusOne;
1047        let _ = value.start_from_inclusive_end(len); // This should panic in debug mode
1048    }
1049
1050    #[test]
1051    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1052    #[allow(clippy::cognitive_complexity, clippy::legacy_numeric_constants)]
1053    fn test_ipv4_and_ipv6_etc() {
1054        syntactic_for! { ty in [char, Ipv6Addr, u128, i128, Ipv4Addr] {
1055            $(
1056            // Test the minimum value for the type
1057            let a = <$ty>::min_value();
1058            let b = a.checked_add_one();
1059            assert_eq!(b, Some(<$ty>::min_value().add_one()));
1060
1061            // Show overflow behavior
1062            let a = <$ty>::max_value();
1063            let b = a.checked_add_one();
1064            assert_eq!(b, None);
1065
1066            let a = <$ty>::min_value();
1067            let mut b = a.add_one();
1068            assert_eq!(b, <$ty>::min_value().add_one());
1069
1070            let c = b.sub_one();
1071            assert_eq!(c, a);
1072
1073            b.assign_sub_one();
1074            assert_eq!(b, a);
1075
1076            let mut a = <$ty>::min_value()..=<$ty>::min_value();
1077            let b = <$ty>::range_next(&mut a);
1078            assert_eq!(b, Some(<$ty>::min_value()));
1079            let b = <$ty>::range_next(&mut a);
1080            assert_eq!(b, None);
1081
1082            let mut a = <$ty>::min_value()..=<$ty>::max_value();
1083            let b = <$ty>::range_next_back(&mut a);
1084            assert_eq!(b, Some(<$ty>::max_value()));
1085
1086            assert_eq!(<$ty>::min_value(), <$ty>::min_value());
1087
1088            let universe = <$ty>::min_value()..=<$ty>::max_value();
1089            let len = <$ty>::safe_len(&universe);
1090            assert_eq!(len, <$ty>::safe_len(&(<$ty>::min_value()..=<$ty>::max_value())));
1091
1092            let len_via_f64 = <$ty>::f64_to_safe_len_lossy(<$ty>::safe_len_to_f64_lossy(len));
1093            assert_eq!(len, len_via_f64);
1094
1095            let short = <$ty>::min_value()..=<$ty>::min_value();
1096            let len = <$ty>::safe_len(&short);
1097            let len_via_f64 = <$ty>::f64_to_safe_len_lossy(<$ty>::safe_len_to_f64_lossy(len));
1098            assert_eq!(len, len_via_f64);
1099
1100            let len = <$ty>::safe_len(&universe);
1101            let b = <$ty>::min_value().inclusive_end_from_start(len);
1102            assert_eq!(b, <$ty>::max_value());
1103
1104            let c = b.start_from_inclusive_end(len);
1105            assert_eq!(c, <$ty>::min_value());
1106
1107            let range = <$ty>::min_value()..=<$ty>::min_value().add_one();
1108            let len2 = <$ty>::safe_len(&range);
1109            let b = <$ty>::min_value().inclusive_end_from_start(len2);
1110            assert_eq!(b, <$ty>::min_value().add_one());
1111
1112            let b = <$ty>::max_value().start_from_inclusive_end(len2);
1113            assert_eq!(b, <$ty>::max_value().sub_one());
1114
1115            #[cfg(feature = "from_slice")]
1116            {
1117                let range_set_blaze = <$ty>::from_slice(&[<$ty>::min_value()]);
1118                assert_eq!(range_set_blaze, RangeSetBlaze::from_iter([<$ty>::min_value()]));
1119            }
1120            )*
1121        }}
1122    }
1123
1124    #[test]
1125    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1126    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1127    #[allow(clippy::legacy_numeric_constants)]
1128    fn test_i128_overflow() {
1129        let value: i128 = i128::max_value();
1130        let _ = value.inclusive_end_from_start(UIntPlusOne::MaxPlusOne);
1131    }
1132
1133    #[test]
1134    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1135    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1136    #[allow(clippy::legacy_numeric_constants)]
1137    fn test_i128_underflow() {
1138        let value: i128 = i128::min_value();
1139        let _ = value.start_from_inclusive_end(UIntPlusOne::MaxPlusOne);
1140    }
1141
1142    #[test]
1143    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1144    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1145    #[allow(clippy::legacy_numeric_constants)]
1146    fn test_u128_overflow() {
1147        let value: u128 = u128::max_value();
1148        let _ = value.inclusive_end_from_start(UIntPlusOne::MaxPlusOne);
1149    }
1150
1151    #[test]
1152    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1153    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1154    #[allow(clippy::legacy_numeric_constants)]
1155    fn test_u128_underflow() {
1156        let value: u128 = u128::min_value();
1157        let _ = value.start_from_inclusive_end(UIntPlusOne::MaxPlusOne);
1158    }
1159
1160    #[test]
1161    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1162    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1163    #[allow(clippy::legacy_numeric_constants)]
1164    fn test_ipv6_overflow() {
1165        let value: Ipv6Addr = Ipv6Addr::max_value();
1166        let _ = value.inclusive_end_from_start(UIntPlusOne::MaxPlusOne);
1167    }
1168
1169    #[test]
1170    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1171    #[should_panic(expected = "char overflow")]
1172    #[allow(clippy::legacy_numeric_constants)]
1173    fn test_char0_overflow() {
1174        let value: char = char::max_value();
1175        let _ = value.add_one();
1176    }
1177
1178    #[test]
1179    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1180    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1112064)")]
1181    #[allow(clippy::legacy_numeric_constants)]
1182    fn test_char1_overflow() {
1183        let value: char = char::max_value();
1184        let len2 = char::safe_len(&(char::min_value()..=char::min_value().add_one()));
1185        let _ = value.inclusive_end_from_start(len2);
1186    }
1187
1188    #[test]
1189    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1190    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1191    #[allow(clippy::legacy_numeric_constants)]
1192    fn test_char1_underflow() {
1193        let value: char = char::min_value();
1194        let len2 = char::safe_len(&(char::min_value()..=char::min_value().add_one()));
1195        let _ = value.start_from_inclusive_end(len2);
1196    }
1197
1198    #[test]
1199    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1200    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1201    fn test_ipv6_underflow() {
1202        let value: Ipv6Addr = Ipv6Addr::min_value();
1203        let _ = value.start_from_inclusive_end(UIntPlusOne::MaxPlusOne);
1204    }
1205
1206    #[test]
1207    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1208    #[allow(clippy::cognitive_complexity)]
1209    fn test_char() {
1210        // This loops over 1 million characters, but it seems fast enough.
1211        // Define the universe as the complement of an empty RangeSetBlaze
1212        let universe = !RangeSetBlaze::<char>::default();
1213
1214        // Check add_one and sub_one behavior
1215        let max_value = <char as Integer>::max_value();
1216        assert_eq!(max_value.checked_add_one(), None);
1217
1218        let mut prev = None;
1219        let mut len = <char as Integer>::SafeLen::zero();
1220        for item in <char as Integer>::min_value()..=max_value {
1221            let len2b = <char as Integer>::safe_len(&(item..=max_value));
1222            let mut expected = universe.len();
1223            expected -= len;
1224            assert_eq!(len2b, expected);
1225
1226            let item2 = max_value.start_from_inclusive_end(len2b);
1227            assert_eq!(item2, item);
1228
1229            let item3 = item2.inclusive_end_from_start(len2b);
1230            assert_eq!(item3, max_value);
1231
1232            len += <char as Integer>::SafeLen::one();
1233            let len2 = <char as Integer>::safe_len(&(<char as Integer>::min_value()..=item));
1234            assert_eq!(len, len2);
1235            assert_eq!(
1236                len2,
1237                <char as Integer>::f64_to_safe_len_lossy(<char as Integer>::safe_len_to_f64_lossy(
1238                    len2
1239                ))
1240            );
1241
1242            let item2 = <char as Integer>::min_value().inclusive_end_from_start(len);
1243            assert_eq!(item2, item);
1244
1245            let item3 = item.start_from_inclusive_end(len);
1246            assert_eq!(item3, <char as Integer>::min_value());
1247
1248            if let Some(prev) = prev {
1249                assert!(universe.contains(prev));
1250                assert!(universe.contains(item));
1251                assert!(universe.is_superset(&RangeSetBlaze::from_iter([prev..=item])));
1252
1253                assert_eq!(prev.checked_add_one(), Some(item));
1254                assert_eq!(prev.add_one(), item);
1255
1256                assert_eq!(item.sub_one(), prev);
1257                let mut item2 = item;
1258                item2.assign_sub_one();
1259                assert_eq!(item2, prev);
1260            }
1261
1262            prev = Some(item);
1263        }
1264        assert_eq!(universe.len(), len);
1265
1266        // Additional checks can be added here if needed for coverage
1267    }
1268
1269    #[test]
1270    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1271    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 66)")]
1272    fn test_add_len_less_one_panic_conditions1() {
1273        // Case 1: `b.checked_sub(1)` returns `None`
1274        let character = 'A';
1275        let b = 0;
1276        _ = character.inclusive_end_from_start(b); // This should panic due to overflow
1277    }
1278
1279    #[test]
1280    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1281    #[should_panic(expected = "b must be in range 1..=max_len (b = 3, max_len = 1112064)")]
1282    fn test_add_len_less_one_panic_conditions2() {
1283        // Case 2: `self.checked_add(b_less_one)` returns `None`
1284        let character = char::MAX;
1285        let b = 3;
1286        _ = character.inclusive_end_from_start(b); // This should panic due to overflow
1287    }
1288
1289    #[test]
1290    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1291    #[should_panic(expected = "b must be in range 1..=max_len (b = 4294967295, max_len = 66)")]
1292    fn test_add_len_less_one_panic_conditions3() {
1293        // Case 3: overflow when adding `b - 1` to `self`
1294        let character = 'A';
1295        let b = u32::MAX;
1296        _ = character.inclusive_end_from_start(b); // This should panic due to overflow
1297    }
1298
1299    #[test]
1300    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1301    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 66)")]
1302    fn test_sub_len_less_one_panic_conditions1() {
1303        // Case 1: `b.checked_sub(1)` fails, causing an immediate panic.
1304        let character = 'A';
1305        let b = 0;
1306        _ = character.start_from_inclusive_end(b); // This should panic due to underflow
1307    }
1308
1309    #[test]
1310    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1311    #[should_panic(expected = "b must be in range 1..=max_len (b = 4294967295, max_len = 66)")]
1312    fn test_sub_len_less_one_panic_conditions2() {
1313        // Case 2: `a.checked_sub(b_less_one)` fails, causing underflow.
1314        let character = 'A';
1315        let b = u32::MAX;
1316        _ = character.start_from_inclusive_end(b); // This should panic due to underflow
1317    }
1318
1319    #[allow(clippy::legacy_numeric_constants, clippy::cognitive_complexity)]
1320    #[test]
1321    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1322    fn test_use_of_as_00() {
1323        syntactic_for! { ty in [char, i8, i16, i32, i64, i128, isize, Ipv4Addr, Ipv6Addr, u8, u16, u32, u64, u128, usize] {
1324            $(
1325        let a = <$ty>::min_value();
1326        let b = <$ty>::max_value();
1327        let len = <$ty>::safe_len(&(a..=b));
1328        assert_eq!(<$ty>::inclusive_end_from_start(a, len), b);
1329        assert_eq!(<$ty>::start_from_inclusive_end(b, len), a);
1330            )*
1331        }}
1332    }
1333
1334    #[cfg(debug_assertions)]
1335    #[test]
1336    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 1)")]
1337    fn test_use_of_as_01() {
1338        let _ = 127i8.inclusive_end_from_start(0);
1339    }
1340
1341    #[cfg(not(debug_assertions))]
1342    #[test]
1343    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1344    fn test_use_of_as_02() {
1345        assert_eq!(127i8.inclusive_end_from_start(0), 126);
1346        assert_eq!(127i8.start_from_inclusive_end(0), -128);
1347        assert_eq!(127i8.inclusive_end_from_start(2), -128);
1348        assert_eq!((-126i8).start_from_inclusive_end(4), 127);
1349    }
1350
1351    #[cfg(debug_assertions)]
1352    #[test]
1353    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 256)")]
1354    fn test_use_of_as_03() {
1355        let _ = 127i8.start_from_inclusive_end(0);
1356    }
1357
1358    #[cfg(debug_assertions)]
1359    #[test]
1360    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1361    fn test_use_of_as_04() {
1362        let _ = 127i8.inclusive_end_from_start(2);
1363    }
1364
1365    #[cfg(debug_assertions)]
1366    #[test]
1367    #[should_panic(expected = "b must be in range 1..=max_len (b = 4, max_len = 3)")]
1368    fn test_use_of_as_05() {
1369        let _ = (-126i8).start_from_inclusive_end(4);
1370    }
1371
1372    #[test]
1373    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1374    fn test_use_of_as_06() {
1375        for a in (-128i8)..=127i8 {
1376            let b = i8::safe_len(&(a..=127i8));
1377            assert_eq!(a.inclusive_end_from_start(b), 127i8);
1378            let b = i8::safe_len(&(i8::MIN..=a));
1379            assert_eq!(a.start_from_inclusive_end(b), -128i8);
1380        }
1381    }
1382
1383    // make full tests for i128
1384    #[cfg(debug_assertions)]
1385    #[test]
1386    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 1)")]
1387    fn test_use_of_as_11() {
1388        let _ = i128::MAX.inclusive_end_from_start(UIntPlusOne::zero());
1389    }
1390
1391    #[cfg(not(debug_assertions))]
1392    #[test]
1393    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1394    fn test_use_of_as_12() {
1395        assert_eq!(
1396            i128::MAX.inclusive_end_from_start(UIntPlusOne::zero()),
1397            170141183460469231731687303715884105726
1398        );
1399        assert_eq!(
1400            i128::MAX.start_from_inclusive_end(UIntPlusOne::zero()),
1401            -170141183460469231731687303715884105728
1402        );
1403        assert_eq!(
1404            i128::MAX.inclusive_end_from_start(UIntPlusOne::UInt(2)),
1405            -170141183460469231731687303715884105728
1406        );
1407        assert_eq!(
1408            (i128::MIN).start_from_inclusive_end(UIntPlusOne::UInt(2)),
1409            170141183460469231731687303715884105727
1410        );
1411    }
1412
1413    #[cfg(debug_assertions)]
1414    #[test]
1415    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = (u128::MAX + 1)")]
1416    fn test_use_of_as_13() {
1417        let _ = i128::MAX.start_from_inclusive_end(UIntPlusOne::zero());
1418    }
1419
1420    #[cfg(debug_assertions)]
1421    #[test]
1422    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1423    fn test_use_of_as_14() {
1424        let _ = i128::MAX.inclusive_end_from_start(UIntPlusOne::UInt(2));
1425    }
1426
1427    #[cfg(debug_assertions)]
1428    #[test]
1429    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1430    fn test_use_of_as_15() {
1431        let _ = (i128::MIN).start_from_inclusive_end(UIntPlusOne::UInt(2));
1432    }
1433
1434    #[test]
1435    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1436    fn test_use_of_as_16() {
1437        assert_eq!(
1438            (i128::MIN).inclusive_end_from_start(UIntPlusOne::MaxPlusOne),
1439            i128::MAX
1440        );
1441        assert_eq!(
1442            (i128::MAX).start_from_inclusive_end(UIntPlusOne::MaxPlusOne),
1443            i128::MIN
1444        );
1445    }
1446
1447    #[test]
1448    #[should_panic(
1449        expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 170141183460469231731687303715884105728)"
1450    )]
1451    fn test_use_of_as_17() {
1452        let _ = (0i128).inclusive_end_from_start(UIntPlusOne::MaxPlusOne);
1453    }
1454
1455    #[test]
1456    #[should_panic(
1457        expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 170141183460469231731687303715884105729)"
1458    )]
1459    fn test_use_of_as_18() {
1460        let _ = (0i128).start_from_inclusive_end(UIntPlusOne::MaxPlusOne);
1461    }
1462
1463    // make full tests for u128
1464    #[cfg(debug_assertions)]
1465    #[test]
1466    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 1)")]
1467    fn test_use_of_as_21() {
1468        let _ = u128::MAX.inclusive_end_from_start(UIntPlusOne::zero());
1469    }
1470
1471    #[cfg(not(debug_assertions))]
1472    #[test]
1473    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1474    fn test_use_of_as_22() {
1475        assert_eq!(
1476            u128::MAX.inclusive_end_from_start(UIntPlusOne::zero()),
1477            340282366920938463463374607431768211454
1478        );
1479        assert_eq!(u128::MAX.start_from_inclusive_end(UIntPlusOne::zero()), 0);
1480        assert_eq!(u128::MAX.inclusive_end_from_start(UIntPlusOne::UInt(2)), 0);
1481        assert_eq!(
1482            (u128::MIN).start_from_inclusive_end(UIntPlusOne::UInt(2)),
1483            340282366920938463463374607431768211455
1484        );
1485    }
1486
1487    #[cfg(debug_assertions)]
1488    #[test]
1489    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = (u128::MAX + 1)")]
1490    fn test_use_of_as_23() {
1491        let _ = u128::MAX.start_from_inclusive_end(UIntPlusOne::zero());
1492    }
1493
1494    #[cfg(debug_assertions)]
1495    #[test]
1496    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1497    fn test_use_of_as_24() {
1498        let _ = u128::MAX.inclusive_end_from_start(UIntPlusOne::UInt(2));
1499    }
1500
1501    #[cfg(debug_assertions)]
1502    #[test]
1503    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1504    fn test_use_of_as_25() {
1505        let _ = (u128::MIN).start_from_inclusive_end(UIntPlusOne::UInt(2));
1506    }
1507
1508    #[test]
1509    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1510    fn test_use_of_as_26() {
1511        assert_eq!(
1512            (u128::MIN).inclusive_end_from_start(UIntPlusOne::MaxPlusOne),
1513            u128::MAX
1514        );
1515        assert_eq!(
1516            (u128::MAX).start_from_inclusive_end(UIntPlusOne::MaxPlusOne),
1517            u128::MIN
1518        );
1519    }
1520
1521    #[test]
1522    #[should_panic(
1523        expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 340282366920938463463374607431768211454)"
1524    )]
1525    fn test_use_of_as_27() {
1526        let _ = (2u128).inclusive_end_from_start(UIntPlusOne::MaxPlusOne);
1527    }
1528
1529    #[test]
1530    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1531    fn test_use_of_as_28() {
1532        let _ = (0u128).start_from_inclusive_end(UIntPlusOne::MaxPlusOne);
1533    }
1534
1535    // make full tests for Ipv4Addr
1536    #[cfg(debug_assertions)]
1537    #[test]
1538    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 1)")]
1539    fn test_use_of_as_31() {
1540        let _ = Ipv4Addr::max_value().inclusive_end_from_start(0);
1541    }
1542
1543    #[cfg(not(debug_assertions))]
1544    #[test]
1545    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1546    fn test_use_of_as_32() {
1547        assert_eq!(
1548            Ipv4Addr::max_value().inclusive_end_from_start(0),
1549            Ipv4Addr::new(255, 255, 255, 254)
1550        );
1551        assert_eq!(
1552            Ipv4Addr::max_value().start_from_inclusive_end(0),
1553            Ipv4Addr::from(0)
1554        );
1555        assert_eq!(
1556            Ipv4Addr::max_value().inclusive_end_from_start(2),
1557            Ipv4Addr::from(0)
1558        );
1559        assert_eq!(
1560            Ipv4Addr::min_value().start_from_inclusive_end(2),
1561            Ipv4Addr::new(255, 255, 255, 255)
1562        );
1563        assert_eq!(
1564            Ipv4Addr::new(0, 0, 0, 2).inclusive_end_from_start(u64::MAX),
1565            Ipv4Addr::from(0)
1566        );
1567
1568        assert_eq!(
1569            Ipv4Addr::new(0, 0, 0, 0).start_from_inclusive_end(u64::MAX),
1570            Ipv4Addr::new(0, 0, 0, 2)
1571        );
1572    }
1573
1574    #[cfg(debug_assertions)]
1575    #[test]
1576    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 4294967296)")]
1577    fn test_use_of_as_33() {
1578        let _ = Ipv4Addr::max_value().start_from_inclusive_end(0);
1579    }
1580
1581    #[cfg(debug_assertions)]
1582    #[test]
1583    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1584    fn test_use_of_as_34() {
1585        let _ = Ipv4Addr::max_value().inclusive_end_from_start(2);
1586    }
1587
1588    #[cfg(debug_assertions)]
1589    #[test]
1590    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1591    fn test_use_of_as_35() {
1592        let _ = (Ipv4Addr::min_value()).start_from_inclusive_end(2);
1593    }
1594
1595    // ipv6
1596
1597    #[cfg(debug_assertions)]
1598    #[test]
1599    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 1)")]
1600    fn test_use_of_as_41() {
1601        let _ = Ipv6Addr::max_value().inclusive_end_from_start(UIntPlusOne::zero());
1602    }
1603
1604    #[cfg(not(debug_assertions))]
1605    #[test]
1606    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1607    fn test_use_of_as_42() {
1608        assert_eq!(
1609            Ipv6Addr::max_value().inclusive_end_from_start(UIntPlusOne::zero()),
1610            Ipv6Addr::from(340282366920938463463374607431768211454)
1611        );
1612        assert_eq!(
1613            Ipv6Addr::max_value().start_from_inclusive_end(UIntPlusOne::zero()),
1614            Ipv6Addr::from(0)
1615        );
1616        assert_eq!(
1617            Ipv6Addr::max_value().inclusive_end_from_start(UIntPlusOne::UInt(2)),
1618            Ipv6Addr::from(0)
1619        );
1620        assert_eq!(
1621            (Ipv6Addr::min_value()).start_from_inclusive_end(UIntPlusOne::UInt(2)),
1622            Ipv6Addr::from(340282366920938463463374607431768211455)
1623        );
1624    }
1625
1626    #[cfg(debug_assertions)]
1627    #[test]
1628    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = (u128::MAX + 1)")]
1629    fn test_use_of_as_43() {
1630        let _ = Ipv6Addr::max_value().start_from_inclusive_end(UIntPlusOne::zero());
1631    }
1632
1633    #[cfg(debug_assertions)]
1634    #[test]
1635    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1636    fn test_use_of_as_44() {
1637        let _ = Ipv6Addr::max_value().inclusive_end_from_start(UIntPlusOne::UInt(2));
1638    }
1639
1640    #[cfg(debug_assertions)]
1641    #[test]
1642    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1643    fn test_use_of_as_45() {
1644        let _ = (Ipv6Addr::min_value()).start_from_inclusive_end(UIntPlusOne::UInt(2));
1645    }
1646
1647    #[test]
1648    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1649    fn test_use_of_as_46() {
1650        assert_eq!(
1651            (Ipv6Addr::min_value()).inclusive_end_from_start(UIntPlusOne::MaxPlusOne),
1652            Ipv6Addr::max_value()
1653        );
1654        assert_eq!(
1655            (Ipv6Addr::max_value()).start_from_inclusive_end(UIntPlusOne::MaxPlusOne),
1656            Ipv6Addr::min_value()
1657        );
1658    }
1659
1660    #[test]
1661    #[should_panic(
1662        expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 340282366920938463463374607431768211454)"
1663    )]
1664    fn test_use_of_as_47() {
1665        let _ = Ipv6Addr::from(2u128).inclusive_end_from_start(UIntPlusOne::MaxPlusOne);
1666    }
1667
1668    #[test]
1669    #[should_panic(expected = "b must be in range 1..=max_len (b = (u128::MAX + 1, max_len = 1)")]
1670    fn test_use_of_as_48() {
1671        let _ = Ipv6Addr::from(0u128).start_from_inclusive_end(UIntPlusOne::MaxPlusOne);
1672    }
1673
1674    // char
1675
1676    #[test]
1677    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 1112064)")]
1678    fn test_use_of_as_51() {
1679        let _ = char::max_value().inclusive_end_from_start(0);
1680    }
1681
1682    #[test]
1683    #[should_panic(expected = "b must be in range 1..=max_len (b = 0, max_len = 1112064)")]
1684    fn test_use_of_as_53() {
1685        let _ = char::max_value().start_from_inclusive_end(0);
1686    }
1687
1688    #[test]
1689    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1112064)")]
1690    fn test_use_of_as_54() {
1691        let _ = char::max_value().inclusive_end_from_start(2);
1692    }
1693
1694    #[test]
1695    #[should_panic(expected = "b must be in range 1..=max_len (b = 2, max_len = 1)")]
1696    fn test_use_of_as_55() {
1697        let _ = (char::min_value()).start_from_inclusive_end(2);
1698    }
1699
1700    #[test]
1701    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1702    fn test_use_of_as_56() {
1703        assert_eq!(
1704            (char::min_value()).inclusive_end_from_start(1_112_064),
1705            char::max_value()
1706        );
1707        assert_eq!(
1708            (char::max_value()).start_from_inclusive_end(1_112_064),
1709            char::min_value()
1710        );
1711    }
1712
1713    #[test]
1714    #[should_panic(expected = "b must be in range 1..=max_len (b = 1112064, max_len = 3)")]
1715    fn test_use_of_as_57() {
1716        let _ = '\x02'.inclusive_end_from_start(1_112_064);
1717    }
1718
1719    #[test]
1720    #[should_panic(expected = "b must be in range 1..=max_len (b = 1112064, max_len = 1)")]
1721    fn test_use_of_as_58() {
1722        let _ = '\x00'.start_from_inclusive_end(1_112_064);
1723    }
1724
1725    #[test]
1726    #[cfg(debug_assertions)]
1727    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1728    #[should_panic(expected = "assertion failed: r.start() <= r.end()")]
1729    #[allow(clippy::reversed_empty_ranges)]
1730    fn test_safe_len() {
1731        let i = 0u128..=0u128;
1732        assert_eq!(u128::safe_len(&i), UIntPlusOne::UInt(1));
1733
1734        let i = 0u128..=1u128;
1735        assert_eq!(u128::safe_len(&i), UIntPlusOne::UInt(2));
1736
1737        let i = 1u128..=0u128;
1738        // This call is expected to panic due to the debug_assert in safe_len
1739        let _ = u128::safe_len(&i);
1740    }
1741
1742    #[test]
1743    #[cfg(debug_assertions)]
1744    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1745    #[should_panic(expected = "assertion failed: r.start() <= r.end()")]
1746    #[allow(clippy::reversed_empty_ranges)]
1747    fn safe_len2() {
1748        let i = 0u128..=0u128;
1749        assert_eq!(u128::safe_len(&i), UIntPlusOne::UInt(1));
1750
1751        let i = 0u128..=1u128;
1752        assert_eq!(u128::safe_len(&i), UIntPlusOne::UInt(2));
1753
1754        let i = 1u128..=0u128;
1755        // This call is expected to panic due to the debug_assert in safe_len
1756        let _ = u128::safe_len(&i);
1757    }
1758
1759    #[test]
1760    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1761    #[should_panic(expected = "b must be in range 1..=max_len (b = 4294911999, max_len = 55295)")]
1762    fn safe_len_char1() {
1763        let a = '\u{D7FE}';
1764        let len = 4_294_911_999u32;
1765        let _ = a.inclusive_end_from_start(len);
1766    }
1767
1768    #[test]
1769    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1770    #[should_panic(expected = "b must be in range 1..=max_len (b = 57343, max_len = 55297)")]
1771    fn safe_len_char2() {
1772        let a = '\u{E000}';
1773        let len = 0xDFFFu32;
1774        let _ = a.start_from_inclusive_end(len);
1775    }
1776}