1#![deny(unconditional_recursion)]
50#![allow(clippy::wrong_self_convention)]
53#![allow(clippy::manual_range_contains)]
56#![no_std]
57#![cfg_attr(
58 feature = "nightly",
59 feature(const_trait_impl, const_ops, const_cmp, const_destruct)
60)]
61#![cfg_attr(docsrs, feature(doc_cfg))]
64
65#[cfg(feature = "std")]
67extern crate std;
68
69use core::fmt;
70use core::num::Wrapping;
71use core::ops::{Add, Div, Mul, Rem, Sub};
72use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
73
74pub use crate::bounds::Bounded;
75#[cfg(any(feature = "std", feature = "libm"))]
76pub use crate::float::Float;
77pub use crate::float::FloatConst;
78pub use crate::cast::{AsPrimitive, FromPrimitive, NumCast, ToPrimitive, cast};
80pub use crate::identities::{ConstOne, ConstZero, One, Zero, one, zero};
81pub use crate::int::{PrimBits, PrimInt};
82pub use crate::ops::bits::{
83 BitWidth, DepositBits, ExtractBits, FunnelShl, FunnelShr, HighestOne, IsolateHighestOne,
84 IsolateLowestOne, LowestOne, ShlExact, ShrExact, UnboundedShl, UnboundedShr,
85};
86pub use crate::ops::bytes::{FromBytes, ToBytes};
87pub use crate::ops::carrying::{BorrowingSub, CarryingAdd, CarryingMul, WideningMul};
88pub use crate::ops::checked::{
89 CheckedAbs, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedPow, CheckedRem, CheckedShl,
90 CheckedShr, CheckedSub,
91};
92pub use crate::ops::clmul::{CarryingCarrylessMul, CarrylessMul, WideningCarrylessMul};
93pub use crate::ops::convert::{
94 AbsDiff, CastSigned, CastUnsigned, CheckedCast, ClampMagnitude, SaturatingCast, StrictCast,
95 Truncate, UnsignedAbs, Widen, WrappingCast,
96};
97#[cfg(feature = "ct")]
98#[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
99pub use crate::ops::ct::{
100 CtCheckedAdd, CtCheckedMul, CtCheckedNeg, CtCheckedSignedDiff, CtCheckedSub, CtIsPowerOfTwo,
101 CtIsZero, CtParity,
102};
103pub use crate::ops::euclid::{CheckedEuclid, Euclid, OverflowingEuclid, WrappingEuclid};
104pub use crate::ops::float_ops::{
105 Algebraic, Erf, FloatBits, Gamma, Maximum, Minimum, NextDown, NextUp, RoundTiesEven,
106};
107pub use crate::ops::from_ascii::{AsciiErrorKind, AsciiParseError, FromAscii};
108pub use crate::ops::inv::Inv;
109pub use crate::ops::log::{Ilog, Ilog2, Ilog10};
110pub use crate::ops::mixed::{
111 CheckedAddSigned, CheckedAddUnsigned, CheckedSignedDiff, CheckedSubSigned, CheckedSubUnsigned,
112 OverflowingAddSigned, OverflowingAddUnsigned, OverflowingSubSigned, OverflowingSubUnsigned,
113 SaturatingAddSigned, SaturatingAddUnsigned, SaturatingSubSigned, SaturatingSubUnsigned,
114 StrictAddSigned, StrictAddUnsigned, StrictSubSigned, StrictSubUnsigned, WrappingAddSigned,
115 WrappingAddUnsigned, WrappingSubSigned, WrappingSubUnsigned,
116};
117pub use crate::ops::mul_add::{MulAdd, MulAddAssign};
118pub use crate::ops::overflowing::{
119 OverflowingAbs, OverflowingAdd, OverflowingDiv, OverflowingMul, OverflowingNeg, OverflowingPow,
120 OverflowingRem, OverflowingShl, OverflowingShr, OverflowingSub,
121};
122pub use crate::ops::parity::Parity;
123pub use crate::ops::pow2::{IsPowerOfTwo, NextPowerOfTwo};
124pub use crate::ops::rounding::{DivCeil, DivExact, DivFloor, Midpoint, MultipleOf, NextMultipleOf};
125pub use crate::ops::saturating::{
126 Saturating, SaturatingAbs, SaturatingAdd, SaturatingDiv, SaturatingMul, SaturatingNeg,
127 SaturatingPow, SaturatingSub,
128};
129pub use crate::ops::sqrt::{CheckedIsqrt, Isqrt};
130pub use crate::ops::strict::{
131 StrictAbs, StrictAdd, StrictDiv, StrictEuclid, StrictMul, StrictNeg, StrictPow, StrictRem,
132 StrictShl, StrictShr, StrictSub,
133};
134#[cfg(feature = "ct")]
135#[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
136pub use crate::ops::typestate::CtNonZero;
137pub use crate::ops::typestate::{
138 BitIndex, BitIndexOps, DivNonZero, Even, Finite, HasNonZero, NonMin, NonNegative, Odd,
139 Positive, PowerOfTwo, PowerOfTwoOps, TypestateError,
140};
141pub use crate::ops::wrapping::{
142 WrappingAbs, WrappingAdd, WrappingDiv, WrappingMul, WrappingNeg, WrappingPow, WrappingRem,
143 WrappingShl, WrappingShr, WrappingSub,
144};
145pub use crate::personality::{
146 Ct, HasPersonality, Nct, Personality, PersonalityMarker, PersonalityTag,
147};
148pub use crate::pow::{Pow, checked_pow, pow};
149pub use crate::sign::{Signed, Signum, Unsigned, abs, abs_sub, signum};
150
151#[macro_use]
152mod macros;
153
154pub mod bounds;
155pub mod cast;
156pub mod float;
157pub mod identities;
158pub mod int;
159pub mod ops;
160pub mod personality;
161pub mod pow;
162pub mod real;
163pub mod sign;
164
165pub mod prelude {
174 pub use crate::bounds::*;
175 pub use crate::cast::*;
176 pub use crate::float::*;
177 pub use crate::identities::*;
178 pub use crate::int::*;
179 pub use crate::ops::bits::*;
180 pub use crate::ops::bytes::*;
181 pub use crate::ops::carrying::*;
182 pub use crate::ops::checked::*;
183 pub use crate::ops::clmul::*;
184 pub use crate::ops::convert::*;
185 #[cfg(feature = "ct")]
186 #[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
187 pub use crate::ops::ct::*;
188 pub use crate::ops::euclid::*;
189 pub use crate::ops::float_ops::*;
190 pub use crate::ops::from_ascii::*;
191 pub use crate::ops::inv::*;
192 pub use crate::ops::log::*;
193 pub use crate::ops::mixed::*;
194 pub use crate::ops::mul_add::*;
195 pub use crate::ops::overflowing::*;
196 pub use crate::ops::parity::*;
197 pub use crate::ops::pow2::*;
198 pub use crate::ops::rounding::*;
199 pub use crate::personality::*;
200 pub use crate::ops::saturating::*;
204 pub use crate::ops::sqrt::*;
205 pub use crate::ops::strict::*;
206 #[cfg(feature = "ct")]
207 #[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
208 pub use crate::ops::typestate::CtNonZero;
209 pub use crate::ops::typestate::{BitIndexOps, DivNonZero, HasNonZero, PowerOfTwoOps};
210 pub use crate::ops::wrapping::*;
211 pub use crate::pow::*;
212 #[cfg(any(feature = "std", feature = "libm"))]
213 pub use crate::real::*;
214 pub use crate::sign::*;
215 pub use crate::{
216 FromStrRadix, Num, NumAssign, NumAssignOps, NumAssignRef, NumOps, NumRef, RefNum, RingOps,
217 };
218}
219
220c0nst::c0nst! {
221pub c0nst trait Num: [c0nst] PartialEq + [c0nst] Zero + [c0nst] One + [c0nst] NumOps {
224 type FromStrRadixErr;
225
226 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>;
252}
253}
254
255c0nst::c0nst! {
256pub c0nst trait RingOps<Rhs = Self, Output = Self>:
265 [c0nst] Add<Rhs, Output = Output>
266 + [c0nst] Sub<Rhs, Output = Output>
267 + [c0nst] Mul<Rhs, Output = Output>
268{
269}
270}
271
272c0nst::c0nst! {
273c0nst impl<T, Rhs, Output> RingOps<Rhs, Output> for T where
274 T: [c0nst] Add<Rhs, Output = Output>
275 + [c0nst] Sub<Rhs, Output = Output>
276 + [c0nst] Mul<Rhs, Output = Output>
277{
278}
279}
280
281c0nst::c0nst! {
282pub c0nst trait NumOps<Rhs = Self, Output = Self>:
286 [c0nst] RingOps<Rhs, Output>
287 + [c0nst] Div<Rhs, Output = Output>
288 + [c0nst] Rem<Rhs, Output = Output>
289{
290}
291}
292
293c0nst::c0nst! {
294c0nst impl<T, Rhs, Output> NumOps<Rhs, Output> for T where
295 T: [c0nst] Add<Rhs, Output = Output>
296 + [c0nst] Sub<Rhs, Output = Output>
297 + [c0nst] Mul<Rhs, Output = Output>
298 + [c0nst] Div<Rhs, Output = Output>
299 + [c0nst] Rem<Rhs, Output = Output>
300{
301}
302}
303
304c0nst::c0nst! {
305pub c0nst trait NumRef: [c0nst] Num + for<'r> [c0nst] NumOps<&'r Self> {}
310}
311c0nst::c0nst! {
312c0nst impl<T> NumRef for T where T: [c0nst] Num + for<'r> [c0nst] NumOps<&'r T> {}
313}
314
315c0nst::c0nst! {
316pub c0nst trait RefNum<Base>: [c0nst] NumOps<Base, Base> + for<'r> [c0nst] NumOps<&'r Base, Base> {}
323}
324c0nst::c0nst! {
325c0nst impl<T, Base> RefNum<Base> for T where T: [c0nst] NumOps<Base, Base> + for<'r> [c0nst] NumOps<&'r Base, Base> {}
326}
327
328c0nst::c0nst! {
329pub c0nst trait NumAssignOps<Rhs = Self>:
333 [c0nst] AddAssign<Rhs> + [c0nst] SubAssign<Rhs> + [c0nst] MulAssign<Rhs> + [c0nst] DivAssign<Rhs> + [c0nst] RemAssign<Rhs>
334{
335}
336}
337
338c0nst::c0nst! {
339c0nst impl<T, Rhs> NumAssignOps<Rhs> for T where
340 T: [c0nst] AddAssign<Rhs> + [c0nst] SubAssign<Rhs> + [c0nst] MulAssign<Rhs> + [c0nst] DivAssign<Rhs> + [c0nst] RemAssign<Rhs>
341{
342}
343}
344
345c0nst::c0nst! {
346pub c0nst trait NumAssign: [c0nst] Num + [c0nst] NumAssignOps {}
350}
351c0nst::c0nst! {
352c0nst impl<T> NumAssign for T where T: [c0nst] Num + [c0nst] NumAssignOps {}
353}
354
355c0nst::c0nst! {
356pub c0nst trait NumAssignRef: [c0nst] NumAssign + for<'r> [c0nst] NumAssignOps<&'r Self> {}
361}
362c0nst::c0nst! {
363c0nst impl<T> NumAssignRef for T where T: [c0nst] NumAssign + for<'r> [c0nst] NumAssignOps<&'r T> {}
364}
365
366pub trait FromStrRadix: Sized {
376 type Err;
378
379 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err>;
383}
384
385macro_rules! from_str_radix_atom_impl {
386 ($($t:ty)*) => ($(
387 impl FromStrRadix for $t {
388 type Err = ::core::num::ParseIntError;
389 #[inline]
390 fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err> {
391 <$t>::from_str_radix(s, radix)
392 }
393 }
394 )*)
395}
396from_str_radix_atom_impl!(usize u8 u16 u32 u64 u128);
397from_str_radix_atom_impl!(isize i8 i16 i32 i64 i128);
398
399macro_rules! from_str_radix_atom_float_impl {
400 ($($t:ty)*) => ($(
401 impl FromStrRadix for $t {
402 type Err = ParseFloatError;
403 #[inline]
404 fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err> {
405 <$t as Num>::from_str_radix(s, radix)
407 }
408 }
409 )*)
410}
411
412impl<T: FromStrRadix> FromStrRadix for Wrapping<T> {
413 type Err = T::Err;
414 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err> {
415 T::from_str_radix(str, radix).map(Wrapping)
416 }
417}
418
419impl<T: FromStrRadix> FromStrRadix for core::num::Saturating<T> {
420 type Err = T::Err;
421 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err> {
422 T::from_str_radix(str, radix).map(core::num::Saturating)
423 }
424}
425
426macro_rules! int_trait_impl {
427 ($name:ident for $($t:ty)*) => ($(
428 c0nst::c0nst! {
429 c0nst impl $name for $t {
430 type FromStrRadixErr = ::core::num::ParseIntError;
431 #[inline]
432 fn from_str_radix(s: &str, radix: u32)
433 -> Result<Self, ::core::num::ParseIntError>
434 {
435 <$t>::from_str_radix(s, radix)
436 }
437 }
438 }
439 )*)
440}
441int_trait_impl!(Num for usize u8 u16 u32 u64 u128);
442int_trait_impl!(Num for isize i8 i16 i32 i64 i128);
443
444impl<T: Num> Num for Wrapping<T>
447where
448 Wrapping<T>: NumOps,
449{
450 type FromStrRadixErr = T::FromStrRadixErr;
451 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
452 T::from_str_radix(str, radix).map(Wrapping)
453 }
454}
455
456impl<T: Num> Num for core::num::Saturating<T>
458where
459 core::num::Saturating<T>: NumOps,
460{
461 type FromStrRadixErr = T::FromStrRadixErr;
462 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
463 T::from_str_radix(str, radix).map(core::num::Saturating)
464 }
465}
466
467#[derive(Debug)]
468pub enum FloatErrorKind {
469 Empty,
470 Invalid,
471}
472#[derive(Debug)]
475pub struct ParseFloatError {
476 pub kind: FloatErrorKind,
477}
478
479impl fmt::Display for ParseFloatError {
480 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481 let description = match self.kind {
482 FloatErrorKind::Empty => "cannot parse float from empty string",
483 FloatErrorKind::Invalid => "invalid float literal",
484 };
485
486 description.fmt(f)
487 }
488}
489
490fn str_to_ascii_lower_eq_str(a: &str, b: &str) -> bool {
491 a.len() == b.len()
492 && a.bytes().zip(b.bytes()).all(|(a, b)| {
493 let a_to_ascii_lower = a | ((a.is_ascii_uppercase() as u8) << 5);
494 a_to_ascii_lower == b
495 })
496}
497
498macro_rules! float_trait_impl {
505 ($name:ident for $($t:ident)*) => ($(
506 impl $name for $t {
507 type FromStrRadixErr = ParseFloatError;
508
509 fn from_str_radix(src: &str, radix: u32)
510 -> Result<Self, Self::FromStrRadixErr>
511 {
512 use self::FloatErrorKind::*;
513 use self::ParseFloatError as PFE;
514
515 if radix == 10 {
517 return src.parse().map_err(|_| PFE {
518 kind: if src.is_empty() { Empty } else { Invalid },
519 });
520 }
521
522 if str_to_ascii_lower_eq_str(src, "inf")
524 || str_to_ascii_lower_eq_str(src, "infinity")
525 {
526 return Ok($t::INFINITY);
527 } else if str_to_ascii_lower_eq_str(src, "-inf")
528 || str_to_ascii_lower_eq_str(src, "-infinity")
529 {
530 return Ok($t::NEG_INFINITY);
531 } else if str_to_ascii_lower_eq_str(src, "nan") {
532 return Ok($t::NAN);
533 } else if str_to_ascii_lower_eq_str(src, "-nan") {
534 return Ok(-$t::NAN);
535 }
536
537 fn slice_shift_char(src: &str) -> Option<(char, &str)> {
538 let mut chars = src.chars();
539 Some((chars.next()?, chars.as_str()))
540 }
541
542 let (is_positive, src) = match slice_shift_char(src) {
543 None => return Err(PFE { kind: Empty }),
544 Some(('-', "")) => return Err(PFE { kind: Empty }),
545 Some(('-', src)) => (false, src),
546 Some((_, _)) => (true, src),
547 };
548
549 let mut sig = if is_positive { 0.0 } else { -0.0 };
551 let mut prev_sig = sig;
553 let mut cs = src.chars().enumerate();
554 let mut exp_info = None::<(char, usize)>;
556
557 for (i, c) in cs.by_ref() {
559 match c.to_digit(radix) {
560 Some(digit) => {
561 sig *= radix as $t;
563
564 if is_positive {
566 sig += (digit as isize) as $t;
567 } else {
568 sig -= (digit as isize) as $t;
569 }
570
571 if prev_sig != 0.0 {
574 if is_positive && sig <= prev_sig
575 { return Ok($t::INFINITY); }
576 if !is_positive && sig >= prev_sig
577 { return Ok($t::NEG_INFINITY); }
578
579 if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
581 { return Ok($t::INFINITY); }
582 if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
583 { return Ok($t::NEG_INFINITY); }
584 }
585 prev_sig = sig;
586 },
587 None => match c {
588 'e' | 'E' | 'p' | 'P' => {
589 exp_info = Some((c, i + 1));
590 break; },
592 '.' => {
593 break; },
595 _ => {
596 return Err(PFE { kind: Invalid });
597 },
598 },
599 }
600 }
601
602 if exp_info.is_none() {
605 let mut power = 1.0;
606 for (i, c) in cs.by_ref() {
607 match c.to_digit(radix) {
608 Some(digit) => {
609 power /= radix as $t;
611 sig = if is_positive {
613 sig + (digit as $t) * power
614 } else {
615 sig - (digit as $t) * power
616 };
617 if is_positive && sig < prev_sig
619 { return Ok($t::INFINITY); }
620 if !is_positive && sig > prev_sig
621 { return Ok($t::NEG_INFINITY); }
622 prev_sig = sig;
623 },
624 None => match c {
625 'e' | 'E' | 'p' | 'P' => {
626 exp_info = Some((c, i + 1));
627 break; },
629 _ => {
630 return Err(PFE { kind: Invalid });
631 },
632 },
633 }
634 }
635 }
636
637 let exp = match exp_info {
639 Some((c, offset)) => {
640 let base = match c {
641 'E' | 'e' if radix == 10 => 10.0,
642 'P' | 'p' if radix == 16 => 2.0,
643 _ => return Err(PFE { kind: Invalid }),
644 };
645
646 let src = &src[offset..];
648 let (is_positive, exp) = match slice_shift_char(src) {
649 Some(('-', src)) => (false, src.parse::<usize>()),
650 Some(('+', src)) => (true, src.parse::<usize>()),
651 Some((_, _)) => (true, src.parse::<usize>()),
652 None => return Err(PFE { kind: Invalid }),
653 };
654
655 #[cfg(feature = "std")]
656 fn pow(base: $t, exp: usize) -> $t {
657 Float::powi(base, exp as i32)
658 }
659 match (is_positive, exp) {
662 (true, Ok(exp)) => pow(base, exp),
663 (false, Ok(exp)) => 1.0 / pow(base, exp),
664 (_, Err(_)) => return Err(PFE { kind: Invalid }),
665 }
666 },
667 None => 1.0, };
669
670 Ok(sig * exp)
671 }
672 }
673 )*)
674}
675float_trait_impl!(Num for f32 f64);
676from_str_radix_atom_float_impl!(f32 f64);
677
678c0nst::c0nst! {
679#[inline]
687pub c0nst fn clamp<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, min: T, max: T) -> T {
688 debug_assert!(min <= max, "min must be less than or equal to max");
689 if input < min {
690 min
691 } else if input > max {
692 max
693 } else {
694 input
695 }
696}
697}
698
699c0nst::c0nst! {
700#[inline]
708#[allow(clippy::eq_op)]
709pub c0nst fn clamp_min<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, min: T) -> T {
710 debug_assert!(min == min, "min must not be NAN");
711 if input < min {
712 min
713 } else {
714 input
715 }
716}
717}
718
719c0nst::c0nst! {
720#[inline]
728#[allow(clippy::eq_op)]
729pub c0nst fn clamp_max<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, max: T) -> T {
730 debug_assert!(max == max, "max must not be NAN");
731 if input > max {
732 max
733 } else {
734 input
735 }
736}
737}
738
739#[test]
740fn clamp_test() {
741 assert_eq!(1, clamp(1, -1, 2));
743 assert_eq!(-1, clamp(-2, -1, 2));
744 assert_eq!(2, clamp(3, -1, 2));
745 assert_eq!(1, clamp_min(1, -1));
746 assert_eq!(-1, clamp_min(-2, -1));
747 assert_eq!(-1, clamp_max(1, -1));
748 assert_eq!(-2, clamp_max(-2, -1));
749
750 assert_eq!(1.0, clamp(1.0, -1.0, 2.0));
752 assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0));
753 assert_eq!(2.0, clamp(3.0, -1.0, 2.0));
754 assert_eq!(1.0, clamp_min(1.0, -1.0));
755 assert_eq!(-1.0, clamp_min(-2.0, -1.0));
756 assert_eq!(-1.0, clamp_max(1.0, -1.0));
757 assert_eq!(-2.0, clamp_max(-2.0, -1.0));
758 assert!(clamp(f32::NAN, -1.0, 1.0).is_nan());
759 assert!(clamp_min(f32::NAN, 1.0).is_nan());
760 assert!(clamp_max(f32::NAN, 1.0).is_nan());
761}
762
763#[test]
764#[should_panic]
765#[cfg(debug_assertions)]
766fn clamp_nan_min() {
767 clamp(0., f32::NAN, 1.);
768}
769
770#[test]
771#[should_panic]
772#[cfg(debug_assertions)]
773fn clamp_nan_max() {
774 clamp(0., -1., f32::NAN);
775}
776
777#[test]
778#[should_panic]
779#[cfg(debug_assertions)]
780fn clamp_nan_min_max() {
781 clamp(0., f32::NAN, f32::NAN);
782}
783
784#[test]
785#[should_panic]
786#[cfg(debug_assertions)]
787fn clamp_min_nan_min() {
788 clamp_min(0., f32::NAN);
789}
790
791#[test]
792#[should_panic]
793#[cfg(debug_assertions)]
794fn clamp_max_nan_max() {
795 clamp_max(0., f32::NAN);
796}
797
798#[test]
799fn from_str_radix_unwrap() {
800 let i: i32 = Num::from_str_radix("0", 10).unwrap();
803 assert_eq!(i, 0);
804
805 let f: f32 = Num::from_str_radix("0.0", 10).unwrap();
806 assert_eq!(f, 0.0);
807}
808
809#[test]
810fn from_str_radix_multi_byte_fail() {
811 assert!(<f32 as Num>::from_str_radix("™0.2", 10).is_err());
813
814 assert!(<f32 as Num>::from_str_radix("0.2E™1", 10).is_err());
816}
817
818#[test]
819fn from_str_radix_ignore_case() {
820 assert_eq!(
821 <f32 as Num>::from_str_radix("InF", 16).unwrap(),
822 f32::INFINITY
823 );
824 assert_eq!(
825 <f32 as Num>::from_str_radix("InfinitY", 16).unwrap(),
826 f32::INFINITY
827 );
828 assert_eq!(
829 <f32 as Num>::from_str_radix("-InF", 8).unwrap(),
830 f32::NEG_INFINITY
831 );
832 assert_eq!(
833 <f32 as Num>::from_str_radix("-InfinitY", 8).unwrap(),
834 f32::NEG_INFINITY
835 );
836 assert!(<f32 as Num>::from_str_radix("nAn", 4).unwrap().is_nan());
837 assert!(<f32 as Num>::from_str_radix("-nAn", 4).unwrap().is_nan());
838}
839
840#[test]
841fn wrapping_is_num() {
842 fn require_num<T: Num>(_: &T) {}
843 require_num(&Wrapping(42_u32));
844 require_num(&Wrapping(-42));
845}
846
847#[test]
848fn wrapping_from_str_radix() {
849 macro_rules! test_wrapping_from_str_radix {
850 ($($t:ty)+) => {
851 $(
852 for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
853 let w = <Wrapping<$t> as Num>::from_str_radix(s, r).map(|w| w.0);
854 assert_eq!(w, <$t as Num>::from_str_radix(s, r));
855 }
856 )+
857 };
858 }
859
860 test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
861}
862
863#[test]
864fn saturating_is_num() {
865 fn require_num<T: Num>(_: &T) {}
866 require_num(&core::num::Saturating(42_u32));
867 require_num(&core::num::Saturating(-42));
868}
869
870#[test]
871fn saturating_from_str_radix() {
872 macro_rules! test_saturating_from_str_radix {
873 ($($t:ty)+) => {
874 $(
875 for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
876 let w = <core::num::Saturating<$t> as Num>::from_str_radix(s, r).map(|w| w.0);
877 assert_eq!(w, <$t as Num>::from_str_radix(s, r));
878 }
879 )+
880 };
881 }
882
883 test_saturating_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
884}
885
886#[test]
887fn check_num_ops() {
888 fn compute<T: Num + Copy>(x: T, y: T) -> T {
889 x * y / y % y + y - y
890 }
891 assert_eq!(compute(1, 2), 1)
892}
893
894#[test]
895fn check_numref_ops() {
896 fn compute<T: NumRef>(x: T, y: &T) -> T {
897 x * y / y % y + y - y
898 }
899 assert_eq!(compute(1, &2), 1)
900}
901
902#[test]
903fn check_refnum_ops() {
904 fn compute<T: Copy>(x: &T, y: T) -> T
905 where
906 for<'a> &'a T: RefNum<T>,
907 {
908 &(&(&(&(x * y) / y) % y) + y) - y
909 }
910 assert_eq!(compute(&1, 2), 1)
911}
912
913#[test]
914fn check_refref_ops() {
915 fn compute<T>(x: &T, y: &T) -> T
916 where
917 for<'a> &'a T: RefNum<T>,
918 {
919 &(&(&(&(x * y) / y) % y) + y) - y
920 }
921 assert_eq!(compute(&1, &2), 1)
922}
923
924#[test]
925fn check_numassign_ops() {
926 fn compute<T: NumAssign + Copy>(mut x: T, y: T) -> T {
927 x *= y;
928 x /= y;
929 x %= y;
930 x += y;
931 x -= y;
932 x
933 }
934 assert_eq!(compute(1, 2), 1)
935}
936
937#[test]
938fn check_numassignref_ops() {
939 fn compute<T: NumAssignRef + Copy>(mut x: T, y: &T) -> T {
940 x *= y;
941 x /= y;
942 x %= y;
943 x += y;
944 x -= y;
945 x
946 }
947 assert_eq!(compute(1, &2), 1)
948}