diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c03416ba..e580e2c5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -11,6 +11,7 @@ jobs: rust: [ 1.60.0, # MSRV 1.62.0, # has_total_cmp + 1.74.0, # has_num_saturating stable, beta, nightly, diff --git a/Cargo.toml b/Cargo.toml index 226d4164..b0ddbe27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,4 +31,4 @@ std = [] i128 = [] [build-dependencies] -autocfg = "1" +autocfg = "1.2" diff --git a/build.rs b/build.rs index 98b06bef..7da75312 100644 --- a/build.rs +++ b/build.rs @@ -1,7 +1,9 @@ fn main() { - let ac = autocfg::new(); + let mut ac = autocfg::new(); + ac.set_no_std(true); ac.emit_expression_cfg("1f64.total_cmp(&2f64)", "has_total_cmp"); // 1.62 + ac.emit_path_cfg("core::num::Saturating", "has_num_saturating"); // 1.74 autocfg::rerun_path("build.rs"); } diff --git a/ci/rustup.sh b/ci/rustup.sh index a07aa610..e2679409 100755 --- a/ci/rustup.sh +++ b/ci/rustup.sh @@ -5,6 +5,6 @@ set -ex ci=$(dirname $0) -for version in 1.60.0 1.62.0 stable beta nightly; do +for version in 1.60.0 1.62.0 1.74.0 stable beta nightly; do rustup run "$version" "$ci/test_full.sh" done diff --git a/src/identities.rs b/src/identities.rs index c30cd1dc..0a198a1f 100644 --- a/src/identities.rs +++ b/src/identities.rs @@ -1,6 +1,9 @@ use core::num::Wrapping; use core::ops::{Add, Mul}; +#[cfg(has_num_saturating)] +use core::num::Saturating; + /// Defines an additive identity element for `Self`. /// /// # Laws @@ -95,6 +98,32 @@ where const ZERO: Self = Wrapping(T::ZERO); } +#[cfg(has_num_saturating)] +impl Zero for Saturating +where + Saturating: Add>, +{ + fn is_zero(&self) -> bool { + self.0.is_zero() + } + + fn set_zero(&mut self) { + self.0.set_zero(); + } + + fn zero() -> Self { + Saturating(T::zero()) + } +} + +#[cfg(has_num_saturating)] +impl ConstZero for Saturating +where + Saturating: Add>, +{ + const ZERO: Self = Saturating(T::ZERO); +} + /// Defines a multiplicative identity element for `Self`. /// /// # Laws @@ -196,6 +225,28 @@ where const ONE: Self = Wrapping(T::ONE); } +#[cfg(has_num_saturating)] +impl One for Saturating +where + Saturating: Mul>, +{ + fn set_one(&mut self) { + self.0.set_one(); + } + + fn one() -> Self { + Saturating(T::one()) + } +} + +#[cfg(has_num_saturating)] +impl ConstOne for Saturating +where + Saturating: Mul>, +{ + const ONE: Self = Saturating(T::ONE); +} + // Some helper functions provided for backwards compatibility. /// Returns the additive identity, `0`. @@ -236,3 +287,33 @@ fn wrapping_is_one() { fn require_one(_: &T) {} require_one(&Wrapping(42)); } + +#[test] +#[cfg(has_num_saturating)] +fn saturating_identities() { + macro_rules! test_saturating_identities { + ($($t:ty)+) => { + $( + assert_eq!(zero::<$t>(), zero::>().0); + assert_eq!(one::<$t>(), one::>().0); + assert_eq!((0 as $t).is_zero(), Saturating(0 as $t).is_zero()); + assert_eq!((1 as $t).is_zero(), Saturating(1 as $t).is_zero()); + )+ + }; + } + + test_saturating_identities!(isize i8 i16 i32 i64 usize u8 u16 u32 u64); +} + +#[test] +#[cfg(has_num_saturating)] +fn saturating_is_zero() { + fn require_zero(_: &T) {} + require_zero(&Saturating(42)); +} +#[test] +#[cfg(has_num_saturating)] +fn saturating_is_one() { + fn require_one(_: &T) {} + require_one(&Saturating(42)); +} diff --git a/src/lib.rs b/src/lib.rs index d392e920..43212c77 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,6 +184,17 @@ where } } +#[cfg(has_num_saturating)] +impl Num for core::num::Saturating +where + core::num::Saturating: NumOps, +{ + type FromStrRadixErr = T::FromStrRadixErr; + fn from_str_radix(str: &str, radix: u32) -> Result { + T::from_str_radix(str, radix).map(core::num::Saturating) + } +} + #[derive(Debug)] pub enum FloatErrorKind { Empty, @@ -570,6 +581,31 @@ fn wrapping_from_str_radix() { test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); } +#[test] +#[cfg(has_num_saturating)] +fn saturating_is_num() { + fn require_num(_: &T) {} + require_num(&core::num::Saturating(42_u32)); + require_num(&core::num::Saturating(-42)); +} + +#[test] +#[cfg(has_num_saturating)] +fn saturating_from_str_radix() { + macro_rules! test_saturating_from_str_radix { + ($($t:ty)+) => { + $( + for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] { + let w = core::num::Saturating::<$t>::from_str_radix(s, r).map(|w| w.0); + assert_eq!(w, <$t as Num>::from_str_radix(s, r)); + } + )+ + }; + } + + test_saturating_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64); +} + #[test] fn check_num_ops() { fn compute(x: T, y: T) -> T {