#![deny(unconditional_recursion)]
#![allow(clippy::wrong_self_convention)]
#![allow(clippy::manual_range_contains)]
#![no_std]
#![cfg_attr(
feature = "nightly",
feature(const_trait_impl, const_ops, const_cmp, const_destruct)
)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "std")]
extern crate std;
use core::fmt;
use core::num::Wrapping;
use core::ops::{Add, Div, Mul, Rem, Sub};
use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
pub use crate::bounds::Bounded;
#[cfg(any(feature = "std", feature = "libm"))]
pub use crate::float::Float;
pub use crate::float::FloatConst;
pub use crate::cast::{AsPrimitive, FromPrimitive, NumCast, ToPrimitive, cast};
pub use crate::identities::{ConstOne, ConstZero, One, Zero, one, zero};
pub use crate::int::{PrimBits, PrimInt};
pub use crate::ops::bits::{
BitWidth, DepositBits, ExtractBits, FunnelShl, FunnelShr, HighestOne, IsolateHighestOne,
IsolateLowestOne, LowestOne, ShlExact, ShrExact, UnboundedShl, UnboundedShr,
};
pub use crate::ops::bytes::{FromBytes, ToBytes};
pub use crate::ops::carrying::{BorrowingSub, CarryingAdd, CarryingMul, WideningMul};
pub use crate::ops::checked::{
CheckedAbs, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedPow, CheckedRem, CheckedShl,
CheckedShr, CheckedSub,
};
pub use crate::ops::clmul::{CarryingCarrylessMul, CarrylessMul, WideningCarrylessMul};
pub use crate::ops::convert::{
AbsDiff, CastSigned, CastUnsigned, CheckedCast, ClampMagnitude, SaturatingCast, StrictCast,
Truncate, UnsignedAbs, Widen, WrappingCast,
};
#[cfg(feature = "ct")]
#[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
pub use crate::ops::ct::{
CtCheckedAdd, CtCheckedMul, CtCheckedNeg, CtCheckedSignedDiff, CtCheckedSub, CtIsPowerOfTwo,
CtIsZero, CtParity,
};
pub use crate::ops::euclid::{CheckedEuclid, Euclid, OverflowingEuclid, WrappingEuclid};
pub use crate::ops::float_ops::{
Algebraic, Erf, FloatBits, Gamma, Maximum, Minimum, NextDown, NextUp, RoundTiesEven,
};
pub use crate::ops::from_ascii::{AsciiErrorKind, AsciiParseError, FromAscii};
pub use crate::ops::inv::Inv;
pub use crate::ops::log::{Ilog, Ilog2, Ilog10};
pub use crate::ops::mixed::{
CheckedAddSigned, CheckedAddUnsigned, CheckedSignedDiff, CheckedSubSigned, CheckedSubUnsigned,
OverflowingAddSigned, OverflowingAddUnsigned, OverflowingSubSigned, OverflowingSubUnsigned,
SaturatingAddSigned, SaturatingAddUnsigned, SaturatingSubSigned, SaturatingSubUnsigned,
StrictAddSigned, StrictAddUnsigned, StrictSubSigned, StrictSubUnsigned, WrappingAddSigned,
WrappingAddUnsigned, WrappingSubSigned, WrappingSubUnsigned,
};
pub use crate::ops::mul_add::{MulAdd, MulAddAssign};
pub use crate::ops::overflowing::{
OverflowingAbs, OverflowingAdd, OverflowingDiv, OverflowingMul, OverflowingNeg, OverflowingPow,
OverflowingRem, OverflowingShl, OverflowingShr, OverflowingSub,
};
pub use crate::ops::parity::Parity;
pub use crate::ops::pow2::{IsPowerOfTwo, NextPowerOfTwo};
pub use crate::ops::rounding::{DivCeil, DivExact, DivFloor, Midpoint, MultipleOf, NextMultipleOf};
pub use crate::ops::saturating::{
Saturating, SaturatingAbs, SaturatingAdd, SaturatingDiv, SaturatingMul, SaturatingNeg,
SaturatingPow, SaturatingSub,
};
pub use crate::ops::sqrt::{CheckedIsqrt, Isqrt};
pub use crate::ops::strict::{
StrictAbs, StrictAdd, StrictDiv, StrictEuclid, StrictMul, StrictNeg, StrictPow, StrictRem,
StrictShl, StrictShr, StrictSub,
};
#[cfg(feature = "ct")]
#[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
pub use crate::ops::typestate::CtNonZero;
pub use crate::ops::typestate::{
BitIndex, BitIndexOps, DivNonZero, Even, Finite, HasNonZero, NonMin, NonNegative, Odd,
Positive, PowerOfTwo, PowerOfTwoOps, TypestateError,
};
pub use crate::ops::wrapping::{
WrappingAbs, WrappingAdd, WrappingDiv, WrappingMul, WrappingNeg, WrappingPow, WrappingRem,
WrappingShl, WrappingShr, WrappingSub,
};
pub use crate::personality::{Ct, Nct, Personality, PersonalityMarker, PersonalityTag};
pub use crate::pow::{Pow, checked_pow, pow};
pub use crate::sign::{Signed, Signum, Unsigned, abs, abs_sub, signum};
#[macro_use]
mod macros;
pub mod bounds;
pub mod cast;
pub mod float;
pub mod identities;
pub mod int;
pub mod ops;
pub mod personality;
pub mod pow;
pub mod real;
pub mod sign;
pub mod prelude {
pub use crate::bounds::*;
pub use crate::cast::*;
pub use crate::float::*;
pub use crate::identities::*;
pub use crate::int::*;
pub use crate::ops::bits::*;
pub use crate::ops::bytes::*;
pub use crate::ops::carrying::*;
pub use crate::ops::checked::*;
pub use crate::ops::clmul::*;
pub use crate::ops::convert::*;
#[cfg(feature = "ct")]
#[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
pub use crate::ops::ct::*;
pub use crate::ops::euclid::*;
pub use crate::ops::float_ops::*;
pub use crate::ops::from_ascii::*;
pub use crate::ops::inv::*;
pub use crate::ops::log::*;
pub use crate::ops::mixed::*;
pub use crate::ops::mul_add::*;
pub use crate::ops::overflowing::*;
pub use crate::ops::parity::*;
pub use crate::ops::pow2::*;
pub use crate::ops::rounding::*;
pub use crate::personality::*;
pub use crate::ops::saturating::*;
pub use crate::ops::sqrt::*;
pub use crate::ops::strict::*;
#[cfg(feature = "ct")]
#[cfg_attr(docsrs, doc(cfg(feature = "ct")))]
pub use crate::ops::typestate::CtNonZero;
pub use crate::ops::typestate::{BitIndexOps, DivNonZero, HasNonZero, PowerOfTwoOps};
pub use crate::ops::wrapping::*;
pub use crate::pow::*;
#[cfg(any(feature = "std", feature = "libm"))]
pub use crate::real::*;
pub use crate::sign::*;
pub use crate::{
FromStrRadix, Num, NumAssign, NumAssignOps, NumAssignRef, NumOps, NumRef, RefNum, RingOps,
};
}
c0nst::c0nst! {
pub c0nst trait Num: [c0nst] PartialEq + [c0nst] Zero + [c0nst] One + [c0nst] NumOps {
type FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>;
}
}
c0nst::c0nst! {
pub c0nst trait RingOps<Rhs = Self, Output = Self>:
[c0nst] Add<Rhs, Output = Output>
+ [c0nst] Sub<Rhs, Output = Output>
+ [c0nst] Mul<Rhs, Output = Output>
{
}
}
c0nst::c0nst! {
c0nst impl<T, Rhs, Output> RingOps<Rhs, Output> for T where
T: [c0nst] Add<Rhs, Output = Output>
+ [c0nst] Sub<Rhs, Output = Output>
+ [c0nst] Mul<Rhs, Output = Output>
{
}
}
c0nst::c0nst! {
pub c0nst trait NumOps<Rhs = Self, Output = Self>:
[c0nst] RingOps<Rhs, Output>
+ [c0nst] Div<Rhs, Output = Output>
+ [c0nst] Rem<Rhs, Output = Output>
{
}
}
c0nst::c0nst! {
c0nst impl<T, Rhs, Output> NumOps<Rhs, Output> for T where
T: [c0nst] Add<Rhs, Output = Output>
+ [c0nst] Sub<Rhs, Output = Output>
+ [c0nst] Mul<Rhs, Output = Output>
+ [c0nst] Div<Rhs, Output = Output>
+ [c0nst] Rem<Rhs, Output = Output>
{
}
}
c0nst::c0nst! {
pub c0nst trait NumRef: [c0nst] Num + for<'r> [c0nst] NumOps<&'r Self> {}
}
c0nst::c0nst! {
c0nst impl<T> NumRef for T where T: [c0nst] Num + for<'r> [c0nst] NumOps<&'r T> {}
}
c0nst::c0nst! {
pub c0nst trait RefNum<Base>: [c0nst] NumOps<Base, Base> + for<'r> [c0nst] NumOps<&'r Base, Base> {}
}
c0nst::c0nst! {
c0nst impl<T, Base> RefNum<Base> for T where T: [c0nst] NumOps<Base, Base> + for<'r> [c0nst] NumOps<&'r Base, Base> {}
}
c0nst::c0nst! {
pub c0nst trait NumAssignOps<Rhs = Self>:
[c0nst] AddAssign<Rhs> + [c0nst] SubAssign<Rhs> + [c0nst] MulAssign<Rhs> + [c0nst] DivAssign<Rhs> + [c0nst] RemAssign<Rhs>
{
}
}
c0nst::c0nst! {
c0nst impl<T, Rhs> NumAssignOps<Rhs> for T where
T: [c0nst] AddAssign<Rhs> + [c0nst] SubAssign<Rhs> + [c0nst] MulAssign<Rhs> + [c0nst] DivAssign<Rhs> + [c0nst] RemAssign<Rhs>
{
}
}
c0nst::c0nst! {
pub c0nst trait NumAssign: [c0nst] Num + [c0nst] NumAssignOps {}
}
c0nst::c0nst! {
c0nst impl<T> NumAssign for T where T: [c0nst] Num + [c0nst] NumAssignOps {}
}
c0nst::c0nst! {
pub c0nst trait NumAssignRef: [c0nst] NumAssign + for<'r> [c0nst] NumAssignOps<&'r Self> {}
}
c0nst::c0nst! {
c0nst impl<T> NumAssignRef for T where T: [c0nst] NumAssign + for<'r> [c0nst] NumAssignOps<&'r T> {}
}
pub trait FromStrRadix: Sized {
type Err;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err>;
}
macro_rules! from_str_radix_atom_impl {
($($t:ty)*) => ($(
impl FromStrRadix for $t {
type Err = ::core::num::ParseIntError;
#[inline]
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err> {
<$t>::from_str_radix(s, radix)
}
}
)*)
}
from_str_radix_atom_impl!(usize u8 u16 u32 u64 u128);
from_str_radix_atom_impl!(isize i8 i16 i32 i64 i128);
macro_rules! from_str_radix_atom_float_impl {
($($t:ty)*) => ($(
impl FromStrRadix for $t {
type Err = ParseFloatError;
#[inline]
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::Err> {
<$t as Num>::from_str_radix(s, radix)
}
}
)*)
}
impl<T: FromStrRadix> FromStrRadix for Wrapping<T> {
type Err = T::Err;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err> {
T::from_str_radix(str, radix).map(Wrapping)
}
}
impl<T: FromStrRadix> FromStrRadix for core::num::Saturating<T> {
type Err = T::Err;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::Err> {
T::from_str_radix(str, radix).map(core::num::Saturating)
}
}
macro_rules! int_trait_impl {
($name:ident for $($t:ty)*) => ($(
c0nst::c0nst! {
c0nst impl $name for $t {
type FromStrRadixErr = ::core::num::ParseIntError;
#[inline]
fn from_str_radix(s: &str, radix: u32)
-> Result<Self, ::core::num::ParseIntError>
{
<$t>::from_str_radix(s, radix)
}
}
}
)*)
}
int_trait_impl!(Num for usize u8 u16 u32 u64 u128);
int_trait_impl!(Num for isize i8 i16 i32 i64 i128);
impl<T: Num> Num for Wrapping<T>
where
Wrapping<T>: NumOps,
{
type FromStrRadixErr = T::FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
T::from_str_radix(str, radix).map(Wrapping)
}
}
impl<T: Num> Num for core::num::Saturating<T>
where
core::num::Saturating<T>: NumOps,
{
type FromStrRadixErr = T::FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
T::from_str_radix(str, radix).map(core::num::Saturating)
}
}
#[derive(Debug)]
pub enum FloatErrorKind {
Empty,
Invalid,
}
#[derive(Debug)]
pub struct ParseFloatError {
pub kind: FloatErrorKind,
}
impl fmt::Display for ParseFloatError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let description = match self.kind {
FloatErrorKind::Empty => "cannot parse float from empty string",
FloatErrorKind::Invalid => "invalid float literal",
};
description.fmt(f)
}
}
fn str_to_ascii_lower_eq_str(a: &str, b: &str) -> bool {
a.len() == b.len()
&& a.bytes().zip(b.bytes()).all(|(a, b)| {
let a_to_ascii_lower = a | ((a.is_ascii_uppercase() as u8) << 5);
a_to_ascii_lower == b
})
}
macro_rules! float_trait_impl {
($name:ident for $($t:ident)*) => ($(
impl $name for $t {
type FromStrRadixErr = ParseFloatError;
fn from_str_radix(src: &str, radix: u32)
-> Result<Self, Self::FromStrRadixErr>
{
use self::FloatErrorKind::*;
use self::ParseFloatError as PFE;
if radix == 10 {
return src.parse().map_err(|_| PFE {
kind: if src.is_empty() { Empty } else { Invalid },
});
}
if str_to_ascii_lower_eq_str(src, "inf")
|| str_to_ascii_lower_eq_str(src, "infinity")
{
return Ok($t::INFINITY);
} else if str_to_ascii_lower_eq_str(src, "-inf")
|| str_to_ascii_lower_eq_str(src, "-infinity")
{
return Ok($t::NEG_INFINITY);
} else if str_to_ascii_lower_eq_str(src, "nan") {
return Ok($t::NAN);
} else if str_to_ascii_lower_eq_str(src, "-nan") {
return Ok(-$t::NAN);
}
fn slice_shift_char(src: &str) -> Option<(char, &str)> {
let mut chars = src.chars();
Some((chars.next()?, chars.as_str()))
}
let (is_positive, src) = match slice_shift_char(src) {
None => return Err(PFE { kind: Empty }),
Some(('-', "")) => return Err(PFE { kind: Empty }),
Some(('-', src)) => (false, src),
Some((_, _)) => (true, src),
};
let mut sig = if is_positive { 0.0 } else { -0.0 };
let mut prev_sig = sig;
let mut cs = src.chars().enumerate();
let mut exp_info = None::<(char, usize)>;
for (i, c) in cs.by_ref() {
match c.to_digit(radix) {
Some(digit) => {
sig *= radix as $t;
if is_positive {
sig += (digit as isize) as $t;
} else {
sig -= (digit as isize) as $t;
}
if prev_sig != 0.0 {
if is_positive && sig <= prev_sig
{ return Ok($t::INFINITY); }
if !is_positive && sig >= prev_sig
{ return Ok($t::NEG_INFINITY); }
if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
{ return Ok($t::INFINITY); }
if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
{ return Ok($t::NEG_INFINITY); }
}
prev_sig = sig;
},
None => match c {
'e' | 'E' | 'p' | 'P' => {
exp_info = Some((c, i + 1));
break; },
'.' => {
break; },
_ => {
return Err(PFE { kind: Invalid });
},
},
}
}
if exp_info.is_none() {
let mut power = 1.0;
for (i, c) in cs.by_ref() {
match c.to_digit(radix) {
Some(digit) => {
power /= radix as $t;
sig = if is_positive {
sig + (digit as $t) * power
} else {
sig - (digit as $t) * power
};
if is_positive && sig < prev_sig
{ return Ok($t::INFINITY); }
if !is_positive && sig > prev_sig
{ return Ok($t::NEG_INFINITY); }
prev_sig = sig;
},
None => match c {
'e' | 'E' | 'p' | 'P' => {
exp_info = Some((c, i + 1));
break; },
_ => {
return Err(PFE { kind: Invalid });
},
},
}
}
}
let exp = match exp_info {
Some((c, offset)) => {
let base = match c {
'E' | 'e' if radix == 10 => 10.0,
'P' | 'p' if radix == 16 => 2.0,
_ => return Err(PFE { kind: Invalid }),
};
let src = &src[offset..];
let (is_positive, exp) = match slice_shift_char(src) {
Some(('-', src)) => (false, src.parse::<usize>()),
Some(('+', src)) => (true, src.parse::<usize>()),
Some((_, _)) => (true, src.parse::<usize>()),
None => return Err(PFE { kind: Invalid }),
};
#[cfg(feature = "std")]
fn pow(base: $t, exp: usize) -> $t {
Float::powi(base, exp as i32)
}
match (is_positive, exp) {
(true, Ok(exp)) => pow(base, exp),
(false, Ok(exp)) => 1.0 / pow(base, exp),
(_, Err(_)) => return Err(PFE { kind: Invalid }),
}
},
None => 1.0, };
Ok(sig * exp)
}
}
)*)
}
float_trait_impl!(Num for f32 f64);
from_str_radix_atom_float_impl!(f32 f64);
c0nst::c0nst! {
#[inline]
pub c0nst fn clamp<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, min: T, max: T) -> T {
debug_assert!(min <= max, "min must be less than or equal to max");
if input < min {
min
} else if input > max {
max
} else {
input
}
}
}
c0nst::c0nst! {
#[inline]
#[allow(clippy::eq_op)]
pub c0nst fn clamp_min<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, min: T) -> T {
debug_assert!(min == min, "min must not be NAN");
if input < min {
min
} else {
input
}
}
}
c0nst::c0nst! {
#[inline]
#[allow(clippy::eq_op)]
pub c0nst fn clamp_max<T: [c0nst] PartialOrd + [c0nst] Destruct>(input: T, max: T) -> T {
debug_assert!(max == max, "max must not be NAN");
if input > max {
max
} else {
input
}
}
}
#[test]
fn clamp_test() {
assert_eq!(1, clamp(1, -1, 2));
assert_eq!(-1, clamp(-2, -1, 2));
assert_eq!(2, clamp(3, -1, 2));
assert_eq!(1, clamp_min(1, -1));
assert_eq!(-1, clamp_min(-2, -1));
assert_eq!(-1, clamp_max(1, -1));
assert_eq!(-2, clamp_max(-2, -1));
assert_eq!(1.0, clamp(1.0, -1.0, 2.0));
assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0));
assert_eq!(2.0, clamp(3.0, -1.0, 2.0));
assert_eq!(1.0, clamp_min(1.0, -1.0));
assert_eq!(-1.0, clamp_min(-2.0, -1.0));
assert_eq!(-1.0, clamp_max(1.0, -1.0));
assert_eq!(-2.0, clamp_max(-2.0, -1.0));
assert!(clamp(f32::NAN, -1.0, 1.0).is_nan());
assert!(clamp_min(f32::NAN, 1.0).is_nan());
assert!(clamp_max(f32::NAN, 1.0).is_nan());
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_min() {
clamp(0., f32::NAN, 1.);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_max() {
clamp(0., -1., f32::NAN);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_nan_min_max() {
clamp(0., f32::NAN, f32::NAN);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_min_nan_min() {
clamp_min(0., f32::NAN);
}
#[test]
#[should_panic]
#[cfg(debug_assertions)]
fn clamp_max_nan_max() {
clamp_max(0., f32::NAN);
}
#[test]
fn from_str_radix_unwrap() {
let i: i32 = Num::from_str_radix("0", 10).unwrap();
assert_eq!(i, 0);
let f: f32 = Num::from_str_radix("0.0", 10).unwrap();
assert_eq!(f, 0.0);
}
#[test]
fn from_str_radix_multi_byte_fail() {
assert!(<f32 as Num>::from_str_radix("™0.2", 10).is_err());
assert!(<f32 as Num>::from_str_radix("0.2E™1", 10).is_err());
}
#[test]
fn from_str_radix_ignore_case() {
assert_eq!(
<f32 as Num>::from_str_radix("InF", 16).unwrap(),
f32::INFINITY
);
assert_eq!(
<f32 as Num>::from_str_radix("InfinitY", 16).unwrap(),
f32::INFINITY
);
assert_eq!(
<f32 as Num>::from_str_radix("-InF", 8).unwrap(),
f32::NEG_INFINITY
);
assert_eq!(
<f32 as Num>::from_str_radix("-InfinitY", 8).unwrap(),
f32::NEG_INFINITY
);
assert!(<f32 as Num>::from_str_radix("nAn", 4).unwrap().is_nan());
assert!(<f32 as Num>::from_str_radix("-nAn", 4).unwrap().is_nan());
}
#[test]
fn wrapping_is_num() {
fn require_num<T: Num>(_: &T) {}
require_num(&Wrapping(42_u32));
require_num(&Wrapping(-42));
}
#[test]
fn wrapping_from_str_radix() {
macro_rules! test_wrapping_from_str_radix {
($($t:ty)+) => {
$(
for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
let w = <Wrapping<$t> as Num>::from_str_radix(s, r).map(|w| w.0);
assert_eq!(w, <$t as Num>::from_str_radix(s, r));
}
)+
};
}
test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
}
#[test]
fn saturating_is_num() {
fn require_num<T: Num>(_: &T) {}
require_num(&core::num::Saturating(42_u32));
require_num(&core::num::Saturating(-42));
}
#[test]
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> as Num>::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<T: Num + Copy>(x: T, y: T) -> T {
x * y / y % y + y - y
}
assert_eq!(compute(1, 2), 1)
}
#[test]
fn check_numref_ops() {
fn compute<T: NumRef>(x: T, y: &T) -> T {
x * y / y % y + y - y
}
assert_eq!(compute(1, &2), 1)
}
#[test]
fn check_refnum_ops() {
fn compute<T: Copy>(x: &T, y: T) -> T
where
for<'a> &'a T: RefNum<T>,
{
&(&(&(&(x * y) / y) % y) + y) - y
}
assert_eq!(compute(&1, 2), 1)
}
#[test]
fn check_refref_ops() {
fn compute<T>(x: &T, y: &T) -> T
where
for<'a> &'a T: RefNum<T>,
{
&(&(&(&(x * y) / y) % y) + y) - y
}
assert_eq!(compute(&1, &2), 1)
}
#[test]
fn check_numassign_ops() {
fn compute<T: NumAssign + Copy>(mut x: T, y: T) -> T {
x *= y;
x /= y;
x %= y;
x += y;
x -= y;
x
}
assert_eq!(compute(1, 2), 1)
}
#[test]
fn check_numassignref_ops() {
fn compute<T: NumAssignRef + Copy>(mut x: T, y: &T) -> T {
x *= y;
x /= y;
x %= y;
x += y;
x -= y;
x
}
assert_eq!(compute(1, &2), 1)
}