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::{Ct, Nct, Personality, PersonalityMarker, PersonalityTag};
146pub use crate::pow::{Pow, checked_pow, pow};
147pub use crate::sign::{Signed, Signum, Unsigned, abs, abs_sub, signum};
148
149#[macro_use]
150mod macros;
151
152pub mod bounds;
153pub mod cast;
154pub mod float;
155pub mod identities;
156pub mod int;
157pub mod ops;
158pub mod personality;
159pub mod pow;
160pub mod real;
161pub mod sign;
162
163pub mod prelude {
172 pub use crate::bounds::*;
173 pub use crate::cast::*;
174 pub use crate::float::*;
175 pub use crate::identities::*;
176 pub use crate::int::*;
177 pub use crate::ops::bits::*;
178 pub use crate::ops::bytes::*;
179 pub use crate::ops::carrying::*;
180 pub use crate::ops::checked::*;
181 pub use crate::ops::clmul::*;
182 pub use crate::ops::convert::*;
183 #[cfg(feature = "ct")]
184 #[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
185 pub use crate::ops::ct::*;
186 pub use crate::ops::euclid::*;
187 pub use crate::ops::float_ops::*;
188 pub use crate::ops::from_ascii::*;
189 pub use crate::ops::inv::*;
190 pub use crate::ops::log::*;
191 pub use crate::ops::mixed::*;
192 pub use crate::ops::mul_add::*;
193 pub use crate::ops::overflowing::*;
194 pub use crate::ops::parity::*;
195 pub use crate::ops::pow2::*;
196 pub use crate::ops::rounding::*;
197 pub use crate::personality::*;
198 pub use crate::ops::saturating::*;
202 pub use crate::ops::sqrt::*;
203 pub use crate::ops::strict::*;
204 #[cfg(feature = "ct")]
205 #[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
206 pub use crate::ops::typestate::CtNonZero;
207 pub use crate::ops::typestate::{BitIndexOps, DivNonZero, HasNonZero, PowerOfTwoOps};
208 pub use crate::ops::wrapping::*;
209 pub use crate::pow::*;
210 #[cfg(any(feature = "std", feature = "libm"))]
211 pub use crate::real::*;
212 pub use crate::sign::*;
213 pub use crate::{
214 FromStrRadix, Num, NumAssign, NumAssignOps, NumAssignRef, NumOps, NumRef, RefNum, RingOps,
215 };
216}
217
218c0nst::c0nst! {
219pub c0nst trait Num: [c0nst] PartialEq + [c0nst] Zero + [c0nst] One + [c0nst] NumOps {
222 type FromStrRadixErr;
223
224 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>;
250}
251}
252
253c0nst::c0nst! {
254pub c0nst trait RingOps<Rhs = Self, Output = Self>:
263 [c0nst] Add<Rhs, Output = Output>
264 + [c0nst] Sub<Rhs, Output = Output>
265 + [c0nst] Mul<Rhs, Output = Output>
266{
267}
268}
269
270c0nst::c0nst! {
271c0nst impl<T, Rhs, Output> RingOps<Rhs, Output> for T where
272 T: [c0nst] Add<Rhs, Output = Output>
273 + [c0nst] Sub<Rhs, Output = Output>
274 + [c0nst] Mul<Rhs, Output = Output>
275{
276}
277}
278
279c0nst::c0nst! {
280pub c0nst trait NumOps<Rhs = Self, Output = Self>:
284 [c0nst] RingOps<Rhs, Output>
285 + [c0nst] Div<Rhs, Output = Output>
286 + [c0nst] Rem<Rhs, Output = Output>
287{
288}
289}
290
291c0nst::c0nst! {
292c0nst impl<T, Rhs, Output> NumOps<Rhs, Output> for T where
293 T: [c0nst] Add<Rhs, Output = Output>
294 + [c0nst] Sub<Rhs, Output = Output>
295 + [c0nst] Mul<Rhs, Output = Output>
296 + [c0nst] Div<Rhs, Output = Output>
297 + [c0nst] Rem<Rhs, Output = Output>
298{
299}
300}
301
302c0nst::c0nst! {
303pub c0nst trait NumRef: [c0nst] Num + for<'r> [c0nst] NumOps<&'r Self> {}
308}
309c0nst::c0nst! {
310c0nst impl<T> NumRef for T where T: [c0nst] Num + for<'r> [c0nst] NumOps<&'r T> {}
311}
312
313c0nst::c0nst! {
314pub c0nst trait RefNum<Base>: [c0nst] NumOps<Base, Base> + for<'r> [c0nst] NumOps<&'r Base, Base> {}
321}
322c0nst::c0nst! {
323c0nst impl<T, Base> RefNum<Base> for T where T: [c0nst] NumOps<Base, Base> + for<'r> [c0nst] NumOps<&'r Base, Base> {}
324}
325
326c0nst::c0nst! {
327pub c0nst trait NumAssignOps<Rhs = Self>:
331 [c0nst] AddAssign<Rhs> + [c0nst] SubAssign<Rhs> + [c0nst] MulAssign<Rhs> + [c0nst] DivAssign<Rhs> + [c0nst] RemAssign<Rhs>
332{
333}
334}
335
336c0nst::c0nst! {
337c0nst impl<T, Rhs> NumAssignOps<Rhs> for T where
338 T: [c0nst] AddAssign<Rhs> + [c0nst] SubAssign<Rhs> + [c0nst] MulAssign<Rhs> + [c0nst] DivAssign<Rhs> + [c0nst] RemAssign<Rhs>
339{
340}
341}
342
343c0nst::c0nst! {
344pub c0nst trait NumAssign: [c0nst] Num + [c0nst] NumAssignOps {}
348}
349c0nst::c0nst! {
350c0nst impl<T> NumAssign for T where T: [c0nst] Num + [c0nst] NumAssignOps {}
351}
352
353c0nst::c0nst! {
354pub c0nst trait NumAssignRef: [c0nst] NumAssign + for<'r> [c0nst] NumAssignOps<&'r Self> {}
359}
360c0nst::c0nst! {
361c0nst impl<T> NumAssignRef for T where T: [c0nst] NumAssign + for<'r> [c0nst] NumAssignOps<&'r T> {}
362}
363
364pub trait FromStrRadix: Sized {
374 type Err;
376
377 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err>;
381}
382
383macro_rules! from_str_radix_atom_impl {
384 ($($t:ty)*) => ($(
385 impl FromStrRadix for $t {
386 type Err = ::core::num::ParseIntError;
387 #[inline]
388 fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err> {
389 <$t>::from_str_radix(s, radix)
390 }
391 }
392 )*)
393}
394from_str_radix_atom_impl!(usize u8 u16 u32 u64 u128);
395from_str_radix_atom_impl!(isize i8 i16 i32 i64 i128);
396
397macro_rules! from_str_radix_atom_float_impl {
398 ($($t:ty)*) => ($(
399 impl FromStrRadix for $t {
400 type Err = ParseFloatError;
401 #[inline]
402 fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err> {
403 <$t as Num>::from_str_radix(s, radix)
405 }
406 }
407 )*)
408}
409
410impl<T: FromStrRadix> FromStrRadix for Wrapping<T> {
411 type Err = T::Err;
412 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err> {
413 T::from_str_radix(str, radix).map(Wrapping)
414 }
415}
416
417impl<T: FromStrRadix> FromStrRadix for core::num::Saturating<T> {
418 type Err = T::Err;
419 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err> {
420 T::from_str_radix(str, radix).map(core::num::Saturating)
421 }
422}
423
424macro_rules! int_trait_impl {
425 ($name:ident for $($t:ty)*) => ($(
426 c0nst::c0nst! {
427 c0nst impl $name for $t {
428 type FromStrRadixErr = ::core::num::ParseIntError;
429 #[inline]
430 fn from_str_radix(s: &str, radix: u32)
431 -> Result<Self, ::core::num::ParseIntError>
432 {
433 <$t>::from_str_radix(s, radix)
434 }
435 }
436 }
437 )*)
438}
439int_trait_impl!(Num for usize u8 u16 u32 u64 u128);
440int_trait_impl!(Num for isize i8 i16 i32 i64 i128);
441
442impl<T: Num> Num for Wrapping<T>
445where
446 Wrapping<T>: NumOps,
447{
448 type FromStrRadixErr = T::FromStrRadixErr;
449 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
450 T::from_str_radix(str, radix).map(Wrapping)
451 }
452}
453
454impl<T: Num> Num for core::num::Saturating<T>
456where
457 core::num::Saturating<T>: NumOps,
458{
459 type FromStrRadixErr = T::FromStrRadixErr;
460 fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
461 T::from_str_radix(str, radix).map(core::num::Saturating)
462 }
463}
464
465#[derive(Debug)]
466pub enum FloatErrorKind {
467 Empty,
468 Invalid,
469}
470#[derive(Debug)]
473pub struct ParseFloatError {
474 pub kind: FloatErrorKind,
475}
476
477impl fmt::Display for ParseFloatError {
478 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
479 let description = match self.kind {
480 FloatErrorKind::Empty => "cannot parse float from empty string",
481 FloatErrorKind::Invalid => "invalid float literal",
482 };
483
484 description.fmt(f)
485 }
486}
487
488fn str_to_ascii_lower_eq_str(a: &str, b: &str) -> bool {
489 a.len() == b.len()
490 && a.bytes().zip(b.bytes()).all(|(a, b)| {
491 let a_to_ascii_lower = a | ((a.is_ascii_uppercase() as u8) << 5);
492 a_to_ascii_lower == b
493 })
494}
495
496macro_rules! float_trait_impl {
503 ($name:ident for $($t:ident)*) => ($(
504 impl $name for $t {
505 type FromStrRadixErr = ParseFloatError;
506
507 fn from_str_radix(src: &str, radix: u32)
508 -> Result<Self, Self::FromStrRadixErr>
509 {
510 use self::FloatErrorKind::*;
511 use self::ParseFloatError as PFE;
512
513 if radix == 10 {
515 return src.parse().map_err(|_| PFE {
516 kind: if src.is_empty() { Empty } else { Invalid },
517 });
518 }
519
520 if str_to_ascii_lower_eq_str(src, "inf")
522 || str_to_ascii_lower_eq_str(src, "infinity")
523 {
524 return Ok($t::INFINITY);
525 } else if str_to_ascii_lower_eq_str(src, "-inf")
526 || str_to_ascii_lower_eq_str(src, "-infinity")
527 {
528 return Ok($t::NEG_INFINITY);
529 } else if str_to_ascii_lower_eq_str(src, "nan") {
530 return Ok($t::NAN);
531 } else if str_to_ascii_lower_eq_str(src, "-nan") {
532 return Ok(-$t::NAN);
533 }
534
535 fn slice_shift_char(src: &str) -> Option<(char, &str)> {
536 let mut chars = src.chars();
537 Some((chars.next()?, chars.as_str()))
538 }
539
540 let (is_positive, src) = match slice_shift_char(src) {
541 None => return Err(PFE { kind: Empty }),
542 Some(('-', "")) => return Err(PFE { kind: Empty }),
543 Some(('-', src)) => (false, src),
544 Some((_, _)) => (true, src),
545 };
546
547 let mut sig = if is_positive { 0.0 } else { -0.0 };
549 let mut prev_sig = sig;
551 let mut cs = src.chars().enumerate();
552 let mut exp_info = None::<(char, usize)>;
554
555 for (i, c) in cs.by_ref() {
557 match c.to_digit(radix) {
558 Some(digit) => {
559 sig *= radix as $t;
561
562 if is_positive {
564 sig += (digit as isize) as $t;
565 } else {
566 sig -= (digit as isize) as $t;
567 }
568
569 if prev_sig != 0.0 {
572 if is_positive && sig <= prev_sig
573 { return Ok($t::INFINITY); }
574 if !is_positive && sig >= prev_sig
575 { return Ok($t::NEG_INFINITY); }
576
577 if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
579 { return Ok($t::INFINITY); }
580 if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
581 { return Ok($t::NEG_INFINITY); }
582 }
583 prev_sig = sig;
584 },
585 None => match c {
586 'e' | 'E' | 'p' | 'P' => {
587 exp_info = Some((c, i + 1));
588 break; },
590 '.' => {
591 break; },
593 _ => {
594 return Err(PFE { kind: Invalid });
595 },
596 },
597 }
598 }
599
600 if exp_info.is_none() {
603 let mut power = 1.0;
604 for (i, c) in cs.by_ref() {
605 match c.to_digit(radix) {
606 Some(digit) => {
607 power /= radix as $t;
609 sig = if is_positive {
611 sig + (digit as $t) * power
612 } else {
613 sig - (digit as $t) * power
614 };
615 if is_positive && sig < prev_sig
617 { return Ok($t::INFINITY); }
618 if !is_positive && sig > prev_sig
619 { return Ok($t::NEG_INFINITY); }
620 prev_sig = sig;
621 },
622 None => match c {
623 'e' | 'E' | 'p' | 'P' => {
624 exp_info = Some((c, i + 1));
625 break; },
627 _ => {
628 return Err(PFE { kind: Invalid });
629 },
630 },
631 }
632 }
633 }
634
635 let exp = match exp_info {
637 Some((c, offset)) => {
638 let base = match c {
639 'E' | 'e' if radix == 10 => 10.0,
640 'P' | 'p' if radix == 16 => 2.0,
641 _ => return Err(PFE { kind: Invalid }),
642 };
643
644 let src = &src[offset..];
646 let (is_positive, exp) = match slice_shift_char(src) {
647 Some(('-', src)) => (false, src.parse::<usize>()),
648 Some(('+', src)) => (true, src.parse::<usize>()),
649 Some((_, _)) => (true, src.parse::<usize>()),
650 None => return Err(PFE { kind: Invalid }),
651 };
652
653 #[cfg(feature = "std")]
654 fn pow(base: $t, exp: usize) -> $t {
655 Float::powi(base, exp as i32)
656 }
657 match (is_positive, exp) {
660 (true, Ok(exp)) => pow(base, exp),
661 (false, Ok(exp)) => 1.0 / pow(base, exp),
662 (_, Err(_)) => return Err(PFE { kind: Invalid }),
663 }
664 },
665 None => 1.0, };
667
668 Ok(sig * exp)
669 }
670 }
671 )*)
672}
673float_trait_impl!(Num for f32 f64);
674from_str_radix_atom_float_impl!(f32 f64);
675
676c0nst::c0nst! {
677#[inline]
685pub c0nst fn clamp<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, min: T, max: T) -> T {
686 debug_assert!(min <= max, "min must be less than or equal to max");
687 if input < min {
688 min
689 } else if input > max {
690 max
691 } else {
692 input
693 }
694}
695}
696
697c0nst::c0nst! {
698#[inline]
706#[allow(clippy::eq_op)]
707pub c0nst fn clamp_min<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, min: T) -> T {
708 debug_assert!(min == min, "min must not be NAN");
709 if input < min {
710 min
711 } else {
712 input
713 }
714}
715}
716
717c0nst::c0nst! {
718#[inline]
726#[allow(clippy::eq_op)]
727pub c0nst fn clamp_max<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, max: T) -> T {
728 debug_assert!(max == max, "max must not be NAN");
729 if input > max {
730 max
731 } else {
732 input
733 }
734}
735}
736
737#[test]
738fn clamp_test() {
739 assert_eq!(1, clamp(1, -1, 2));
741 assert_eq!(-1, clamp(-2, -1, 2));
742 assert_eq!(2, clamp(3, -1, 2));
743 assert_eq!(1, clamp_min(1, -1));
744 assert_eq!(-1, clamp_min(-2, -1));
745 assert_eq!(-1, clamp_max(1, -1));
746 assert_eq!(-2, clamp_max(-2, -1));
747
748 assert_eq!(1.0, clamp(1.0, -1.0, 2.0));
750 assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0));
751 assert_eq!(2.0, clamp(3.0, -1.0, 2.0));
752 assert_eq!(1.0, clamp_min(1.0, -1.0));
753 assert_eq!(-1.0, clamp_min(-2.0, -1.0));
754 assert_eq!(-1.0, clamp_max(1.0, -1.0));
755 assert_eq!(-2.0, clamp_max(-2.0, -1.0));
756 assert!(clamp(f32::NAN, -1.0, 1.0).is_nan());
757 assert!(clamp_min(f32::NAN, 1.0).is_nan());
758 assert!(clamp_max(f32::NAN, 1.0).is_nan());
759}
760
761#[test]
762#[should_panic]
763#[cfg(debug_assertions)]
764fn clamp_nan_min() {
765 clamp(0., f32::NAN, 1.);
766}
767
768#[test]
769#[should_panic]
770#[cfg(debug_assertions)]
771fn clamp_nan_max() {
772 clamp(0., -1., f32::NAN);
773}
774
775#[test]
776#[should_panic]
777#[cfg(debug_assertions)]
778fn clamp_nan_min_max() {
779 clamp(0., f32::NAN, f32::NAN);
780}
781
782#[test]
783#[should_panic]
784#[cfg(debug_assertions)]
785fn clamp_min_nan_min() {
786 clamp_min(0., f32::NAN);
787}
788
789#[test]
790#[should_panic]
791#[cfg(debug_assertions)]
792fn clamp_max_nan_max() {
793 clamp_max(0., f32::NAN);
794}
795
796#[test]
797fn from_str_radix_unwrap() {
798 let i: i32 = Num::from_str_radix("0", 10).unwrap();
801 assert_eq!(i, 0);
802
803 let f: f32 = Num::from_str_radix("0.0", 10).unwrap();
804 assert_eq!(f, 0.0);
805}
806
807#[test]
808fn from_str_radix_multi_byte_fail() {
809 assert!(<f32 as Num>::from_str_radix("™0.2", 10).is_err());
811
812 assert!(<f32 as Num>::from_str_radix("0.2E™1", 10).is_err());
814}
815
816#[test]
817fn from_str_radix_ignore_case() {
818 assert_eq!(
819 <f32 as Num>::from_str_radix("InF", 16).unwrap(),
820 f32::INFINITY
821 );
822 assert_eq!(
823 <f32 as Num>::from_str_radix("InfinitY", 16).unwrap(),
824 f32::INFINITY
825 );
826 assert_eq!(
827 <f32 as Num>::from_str_radix("-InF", 8).unwrap(),
828 f32::NEG_INFINITY
829 );
830 assert_eq!(
831 <f32 as Num>::from_str_radix("-InfinitY", 8).unwrap(),
832 f32::NEG_INFINITY
833 );
834 assert!(<f32 as Num>::from_str_radix("nAn", 4).unwrap().is_nan());
835 assert!(<f32 as Num>::from_str_radix("-nAn", 4).unwrap().is_nan());
836}
837
838#[test]
839fn wrapping_is_num() {
840 fn require_num<T: Num>(_: &T) {}
841 require_num(&Wrapping(42_u32));
842 require_num(&Wrapping(-42));
843}
844
845#[test]
846fn wrapping_from_str_radix() {
847 macro_rules! test_wrapping_from_str_radix {
848 ($($t:ty)+) => {
849 $(
850 for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
851 let w = <Wrapping<$t> as Num>::from_str_radix(s, r).map(|w| w.0);
852 assert_eq!(w, <$t as Num>::from_str_radix(s, r));
853 }
854 )+
855 };
856 }
857
858 test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
859}
860
861#[test]
862fn saturating_is_num() {
863 fn require_num<T: Num>(_: &T) {}
864 require_num(&core::num::Saturating(42_u32));
865 require_num(&core::num::Saturating(-42));
866}
867
868#[test]
869fn saturating_from_str_radix() {
870 macro_rules! test_saturating_from_str_radix {
871 ($($t:ty)+) => {
872 $(
873 for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
874 let w = <core::num::Saturating<$t> as Num>::from_str_radix(s, r).map(|w| w.0);
875 assert_eq!(w, <$t as Num>::from_str_radix(s, r));
876 }
877 )+
878 };
879 }
880
881 test_saturating_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
882}
883
884#[test]
885fn check_num_ops() {
886 fn compute<T: Num + Copy>(x: T, y: T) -> T {
887 x * y / y % y + y - y
888 }
889 assert_eq!(compute(1, 2), 1)
890}
891
892#[test]
893fn check_numref_ops() {
894 fn compute<T: NumRef>(x: T, y: &T) -> T {
895 x * y / y % y + y - y
896 }
897 assert_eq!(compute(1, &2), 1)
898}
899
900#[test]
901fn check_refnum_ops() {
902 fn compute<T: Copy>(x: &T, y: T) -> T
903 where
904 for<'a> &'a T: RefNum<T>,
905 {
906 &(&(&(&(x * y) / y) % y) + y) - y
907 }
908 assert_eq!(compute(&1, 2), 1)
909}
910
911#[test]
912fn check_refref_ops() {
913 fn compute<T>(x: &T, y: &T) -> T
914 where
915 for<'a> &'a T: RefNum<T>,
916 {
917 &(&(&(&(x * y) / y) % y) + y) - y
918 }
919 assert_eq!(compute(&1, &2), 1)
920}
921
922#[test]
923fn check_numassign_ops() {
924 fn compute<T: NumAssign + Copy>(mut x: T, y: T) -> T {
925 x *= y;
926 x /= y;
927 x %= y;
928 x += y;
929 x -= y;
930 x
931 }
932 assert_eq!(compute(1, 2), 1)
933}
934
935#[test]
936fn check_numassignref_ops() {
937 fn compute<T: NumAssignRef + Copy>(mut x: T, y: &T) -> T {
938 x *= y;
939 x /= y;
940 x %= y;
941 x += y;
942 x -= y;
943 x
944 }
945 assert_eq!(compute(1, &2), 1)
946}