• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 
11 //! Numeric traits for generic mathematics
12 //!
13 //! ## Compatibility
14 //!
15 //! The `num-traits` crate is tested for rustc 1.8 and greater.
16 
17 #![doc(html_root_url = "https://docs.rs/num-traits/0.2")]
18 #![deny(unconditional_recursion)]
19 #![no_std]
20 #[cfg(feature = "std")]
21 extern crate std;
22 
23 // Only `no_std` builds actually use `libm`.
24 #[cfg(all(not(feature = "std"), feature = "libm"))]
25 extern crate libm;
26 
27 use core::fmt;
28 use core::num::Wrapping;
29 use core::ops::{Add, Div, Mul, Rem, Sub};
30 use core::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
31 
32 pub use bounds::Bounded;
33 #[cfg(any(feature = "std", feature = "libm"))]
34 pub use float::Float;
35 pub use float::FloatConst;
36 // pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`.
37 pub use cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive};
38 pub use identities::{one, zero, One, Zero};
39 pub use int::PrimInt;
40 pub use ops::checked::{
41     CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,
42 };
43 pub use ops::euclid::{CheckedEuclid, Euclid};
44 pub use ops::inv::Inv;
45 pub use ops::mul_add::{MulAdd, MulAddAssign};
46 pub use ops::saturating::{Saturating, SaturatingAdd, SaturatingMul, SaturatingSub};
47 pub use ops::wrapping::{
48     WrappingAdd, WrappingMul, WrappingNeg, WrappingShl, WrappingShr, WrappingSub,
49 };
50 pub use pow::{checked_pow, pow, Pow};
51 pub use sign::{abs, abs_sub, signum, Signed, Unsigned};
52 
53 #[macro_use]
54 mod macros;
55 
56 pub mod bounds;
57 pub mod cast;
58 pub mod float;
59 pub mod identities;
60 pub mod int;
61 pub mod ops;
62 pub mod pow;
63 pub mod real;
64 pub mod sign;
65 
66 /// The base trait for numeric types, covering `0` and `1` values,
67 /// comparisons, basic numeric operations, and string conversion.
68 pub trait Num: PartialEq + Zero + One + NumOps {
69     type FromStrRadixErr;
70 
71     /// Convert from a string and radix (typically `2..=36`).
72     ///
73     /// # Examples
74     ///
75     /// ```rust
76     /// use num_traits::Num;
77     ///
78     /// let result = <i32 as Num>::from_str_radix("27", 10);
79     /// assert_eq!(result, Ok(27));
80     ///
81     /// let result = <i32 as Num>::from_str_radix("foo", 10);
82     /// assert!(result.is_err());
83     /// ```
84     ///
85     /// # Supported radices
86     ///
87     /// The exact range of supported radices is at the discretion of each type implementation. For
88     /// primitive integers, this is implemented by the inherent `from_str_radix` methods in the
89     /// standard library, which **panic** if the radix is not in the range from 2 to 36. The
90     /// implementation in this crate for primitive floats is similar.
91     ///
92     /// For third-party types, it is suggested that implementations should follow suit and at least
93     /// accept `2..=36` without panicking, but an `Err` may be returned for any unsupported radix.
94     /// It's possible that a type might not even support the common radix 10, nor any, if string
95     /// parsing doesn't make sense for that type.
from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>96     fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>;
97 }
98 
99 /// Generic trait for types implementing basic numeric operations
100 ///
101 /// This is automatically implemented for types which implement the operators.
102 pub trait NumOps<Rhs = Self, Output = Self>:
103     Add<Rhs, Output = Output>
104     + Sub<Rhs, Output = Output>
105     + Mul<Rhs, Output = Output>
106     + Div<Rhs, Output = Output>
107     + Rem<Rhs, Output = Output>
108 {
109 }
110 
111 impl<T, Rhs, Output> NumOps<Rhs, Output> for T where
112     T: Add<Rhs, Output = Output>
113         + Sub<Rhs, Output = Output>
114         + Mul<Rhs, Output = Output>
115         + Div<Rhs, Output = Output>
116         + Rem<Rhs, Output = Output>
117 {
118 }
119 
120 /// The trait for `Num` types which also implement numeric operations taking
121 /// the second operand by reference.
122 ///
123 /// This is automatically implemented for types which implement the operators.
124 pub trait NumRef: Num + for<'r> NumOps<&'r Self> {}
125 impl<T> NumRef for T where T: Num + for<'r> NumOps<&'r T> {}
126 
127 /// The trait for `Num` references which implement numeric operations, taking the
128 /// second operand either by value or by reference.
129 ///
130 /// This is automatically implemented for all types which implement the operators. It covers
131 /// every type implementing the operations though, regardless of it being a reference or
132 /// related to `Num`.
133 pub trait RefNum<Base>: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}
134 impl<T, Base> RefNum<Base> for T where T: NumOps<Base, Base> + for<'r> NumOps<&'r Base, Base> {}
135 
136 /// Generic trait for types implementing numeric assignment operators (like `+=`).
137 ///
138 /// This is automatically implemented for types which implement the operators.
139 pub trait NumAssignOps<Rhs = Self>:
140     AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
141 {
142 }
143 
144 impl<T, Rhs> NumAssignOps<Rhs> for T where
145     T: AddAssign<Rhs> + SubAssign<Rhs> + MulAssign<Rhs> + DivAssign<Rhs> + RemAssign<Rhs>
146 {
147 }
148 
149 /// The trait for `Num` types which also implement assignment operators.
150 ///
151 /// This is automatically implemented for types which implement the operators.
152 pub trait NumAssign: Num + NumAssignOps {}
153 impl<T> NumAssign for T where T: Num + NumAssignOps {}
154 
155 /// The trait for `NumAssign` types which also implement assignment operations
156 /// taking the second operand by reference.
157 ///
158 /// This is automatically implemented for types which implement the operators.
159 pub trait NumAssignRef: NumAssign + for<'r> NumAssignOps<&'r Self> {}
160 impl<T> NumAssignRef for T where T: NumAssign + for<'r> NumAssignOps<&'r T> {}
161 
162 macro_rules! int_trait_impl {
163     ($name:ident for $($t:ty)*) => ($(
164         impl $name for $t {
165             type FromStrRadixErr = ::core::num::ParseIntError;
166             #[inline]
167             fn from_str_radix(s: &str, radix: u32)
168                               -> Result<Self, ::core::num::ParseIntError>
169             {
170                 <$t>::from_str_radix(s, radix)
171             }
172         }
173     )*)
174 }
175 int_trait_impl!(Num for usize u8 u16 u32 u64 isize i8 i16 i32 i64);
176 #[cfg(has_i128)]
177 int_trait_impl!(Num for u128 i128);
178 
179 impl<T: Num> Num for Wrapping<T>
180 where
181     Wrapping<T>: NumOps,
182 {
183     type FromStrRadixErr = T::FromStrRadixErr;
from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr>184     fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
185         T::from_str_radix(str, radix).map(Wrapping)
186     }
187 }
188 
189 #[derive(Debug)]
190 pub enum FloatErrorKind {
191     Empty,
192     Invalid,
193 }
194 // FIXME: core::num::ParseFloatError is stable in 1.0, but opaque to us,
195 // so there's not really any way for us to reuse it.
196 #[derive(Debug)]
197 pub struct ParseFloatError {
198     pub kind: FloatErrorKind,
199 }
200 
201 impl fmt::Display for ParseFloatError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result202     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203         let description = match self.kind {
204             FloatErrorKind::Empty => "cannot parse float from empty string",
205             FloatErrorKind::Invalid => "invalid float literal",
206         };
207 
208         description.fmt(f)
209     }
210 }
211 
str_to_ascii_lower_eq_str(a: &str, b: &str) -> bool212 fn str_to_ascii_lower_eq_str(a: &str, b: &str) -> bool {
213     a.len() == b.len()
214         && a.bytes().zip(b.bytes()).all(|(a, b)| {
215             let a_to_ascii_lower = a | (((b'A' <= a && a <= b'Z') as u8) << 5);
216             a_to_ascii_lower == b
217         })
218 }
219 
220 // FIXME: The standard library from_str_radix on floats was deprecated, so we're stuck
221 // with this implementation ourselves until we want to make a breaking change.
222 // (would have to drop it from `Num` though)
223 macro_rules! float_trait_impl {
224     ($name:ident for $($t:ident)*) => ($(
225         impl $name for $t {
226             type FromStrRadixErr = ParseFloatError;
227 
228             fn from_str_radix(src: &str, radix: u32)
229                               -> Result<Self, Self::FromStrRadixErr>
230             {
231                 use self::FloatErrorKind::*;
232                 use self::ParseFloatError as PFE;
233 
234                 // Special case radix 10 to use more accurate standard library implementation
235                 if radix == 10 {
236                     return src.parse().map_err(|_| PFE {
237                         kind: if src.is_empty() { Empty } else { Invalid },
238                     });
239                 }
240 
241                 // Special values
242                 if str_to_ascii_lower_eq_str(src, "inf")
243                     || str_to_ascii_lower_eq_str(src, "infinity")
244                 {
245                     return Ok(core::$t::INFINITY);
246                 } else if str_to_ascii_lower_eq_str(src, "-inf")
247                     || str_to_ascii_lower_eq_str(src, "-infinity")
248                 {
249                     return Ok(core::$t::NEG_INFINITY);
250                 } else if str_to_ascii_lower_eq_str(src, "nan") {
251                     return Ok(core::$t::NAN);
252                 } else if str_to_ascii_lower_eq_str(src, "-nan") {
253                     return Ok(-core::$t::NAN);
254                 }
255 
256                 fn slice_shift_char(src: &str) -> Option<(char, &str)> {
257                     let mut chars = src.chars();
258                     if let Some(ch) = chars.next() {
259                         Some((ch, chars.as_str()))
260                     } else {
261                         None
262                     }
263                 }
264 
265                 let (is_positive, src) =  match slice_shift_char(src) {
266                     None             => return Err(PFE { kind: Empty }),
267                     Some(('-', ""))  => return Err(PFE { kind: Empty }),
268                     Some(('-', src)) => (false, src),
269                     Some((_, _))     => (true,  src),
270                 };
271 
272                 // The significand to accumulate
273                 let mut sig = if is_positive { 0.0 } else { -0.0 };
274                 // Necessary to detect overflow
275                 let mut prev_sig = sig;
276                 let mut cs = src.chars().enumerate();
277                 // Exponent prefix and exponent index offset
278                 let mut exp_info = None::<(char, usize)>;
279 
280                 // Parse the integer part of the significand
281                 for (i, c) in cs.by_ref() {
282                     match c.to_digit(radix) {
283                         Some(digit) => {
284                             // shift significand one digit left
285                             sig = sig * (radix as $t);
286 
287                             // add/subtract current digit depending on sign
288                             if is_positive {
289                                 sig = sig + ((digit as isize) as $t);
290                             } else {
291                                 sig = sig - ((digit as isize) as $t);
292                             }
293 
294                             // Detect overflow by comparing to last value, except
295                             // if we've not seen any non-zero digits.
296                             if prev_sig != 0.0 {
297                                 if is_positive && sig <= prev_sig
298                                     { return Ok(core::$t::INFINITY); }
299                                 if !is_positive && sig >= prev_sig
300                                     { return Ok(core::$t::NEG_INFINITY); }
301 
302                                 // Detect overflow by reversing the shift-and-add process
303                                 if is_positive && (prev_sig != (sig - digit as $t) / radix as $t)
304                                     { return Ok(core::$t::INFINITY); }
305                                 if !is_positive && (prev_sig != (sig + digit as $t) / radix as $t)
306                                     { return Ok(core::$t::NEG_INFINITY); }
307                             }
308                             prev_sig = sig;
309                         },
310                         None => match c {
311                             'e' | 'E' | 'p' | 'P' => {
312                                 exp_info = Some((c, i + 1));
313                                 break;  // start of exponent
314                             },
315                             '.' => {
316                                 break;  // start of fractional part
317                             },
318                             _ => {
319                                 return Err(PFE { kind: Invalid });
320                             },
321                         },
322                     }
323                 }
324 
325                 // If we are not yet at the exponent parse the fractional
326                 // part of the significand
327                 if exp_info.is_none() {
328                     let mut power = 1.0;
329                     for (i, c) in cs.by_ref() {
330                         match c.to_digit(radix) {
331                             Some(digit) => {
332                                 // Decrease power one order of magnitude
333                                 power = power / (radix as $t);
334                                 // add/subtract current digit depending on sign
335                                 sig = if is_positive {
336                                     sig + (digit as $t) * power
337                                 } else {
338                                     sig - (digit as $t) * power
339                                 };
340                                 // Detect overflow by comparing to last value
341                                 if is_positive && sig < prev_sig
342                                     { return Ok(core::$t::INFINITY); }
343                                 if !is_positive && sig > prev_sig
344                                     { return Ok(core::$t::NEG_INFINITY); }
345                                 prev_sig = sig;
346                             },
347                             None => match c {
348                                 'e' | 'E' | 'p' | 'P' => {
349                                     exp_info = Some((c, i + 1));
350                                     break; // start of exponent
351                                 },
352                                 _ => {
353                                     return Err(PFE { kind: Invalid });
354                                 },
355                             },
356                         }
357                     }
358                 }
359 
360                 // Parse and calculate the exponent
361                 let exp = match exp_info {
362                     Some((c, offset)) => {
363                         let base = match c {
364                             'E' | 'e' if radix == 10 => 10.0,
365                             'P' | 'p' if radix == 16 => 2.0,
366                             _ => return Err(PFE { kind: Invalid }),
367                         };
368 
369                         // Parse the exponent as decimal integer
370                         let src = &src[offset..];
371                         let (is_positive, exp) = match slice_shift_char(src) {
372                             Some(('-', src)) => (false, src.parse::<usize>()),
373                             Some(('+', src)) => (true,  src.parse::<usize>()),
374                             Some((_, _))     => (true,  src.parse::<usize>()),
375                             None             => return Err(PFE { kind: Invalid }),
376                         };
377 
378                         #[cfg(feature = "std")]
379                         fn pow(base: $t, exp: usize) -> $t {
380                             Float::powi(base, exp as i32)
381                         }
382                         // otherwise uses the generic `pow` from the root
383 
384                         match (is_positive, exp) {
385                             (true,  Ok(exp)) => pow(base, exp),
386                             (false, Ok(exp)) => 1.0 / pow(base, exp),
387                             (_, Err(_))      => return Err(PFE { kind: Invalid }),
388                         }
389                     },
390                     None => 1.0, // no exponent
391                 };
392 
393                 Ok(sig * exp)
394             }
395         }
396     )*)
397 }
398 float_trait_impl!(Num for f32 f64);
399 
400 /// A value bounded by a minimum and a maximum
401 ///
402 ///  If input is less than min then this returns min.
403 ///  If input is greater than max then this returns max.
404 ///  Otherwise this returns input.
405 ///
406 /// **Panics** in debug mode if `!(min <= max)`.
407 #[inline]
clamp<T: PartialOrd>(input: T, min: T, max: T) -> T408 pub fn clamp<T: PartialOrd>(input: T, min: T, max: T) -> T {
409     debug_assert!(min <= max, "min must be less than or equal to max");
410     if input < min {
411         min
412     } else if input > max {
413         max
414     } else {
415         input
416     }
417 }
418 
419 /// A value bounded by a minimum value
420 ///
421 ///  If input is less than min then this returns min.
422 ///  Otherwise this returns input.
423 ///  `clamp_min(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::min(std::f32::NAN, 1.0)`.
424 ///
425 /// **Panics** in debug mode if `!(min == min)`. (This occurs if `min` is `NAN`.)
426 #[inline]
427 #[allow(clippy::eq_op)]
clamp_min<T: PartialOrd>(input: T, min: T) -> T428 pub fn clamp_min<T: PartialOrd>(input: T, min: T) -> T {
429     debug_assert!(min == min, "min must not be NAN");
430     if input < min {
431         min
432     } else {
433         input
434     }
435 }
436 
437 /// A value bounded by a maximum value
438 ///
439 ///  If input is greater than max then this returns max.
440 ///  Otherwise this returns input.
441 ///  `clamp_max(std::f32::NAN, 1.0)` preserves `NAN` different from `f32::max(std::f32::NAN, 1.0)`.
442 ///
443 /// **Panics** in debug mode if `!(max == max)`. (This occurs if `max` is `NAN`.)
444 #[inline]
445 #[allow(clippy::eq_op)]
clamp_max<T: PartialOrd>(input: T, max: T) -> T446 pub fn clamp_max<T: PartialOrd>(input: T, max: T) -> T {
447     debug_assert!(max == max, "max must not be NAN");
448     if input > max {
449         max
450     } else {
451         input
452     }
453 }
454 
455 #[test]
clamp_test()456 fn clamp_test() {
457     // Int test
458     assert_eq!(1, clamp(1, -1, 2));
459     assert_eq!(-1, clamp(-2, -1, 2));
460     assert_eq!(2, clamp(3, -1, 2));
461     assert_eq!(1, clamp_min(1, -1));
462     assert_eq!(-1, clamp_min(-2, -1));
463     assert_eq!(-1, clamp_max(1, -1));
464     assert_eq!(-2, clamp_max(-2, -1));
465 
466     // Float test
467     assert_eq!(1.0, clamp(1.0, -1.0, 2.0));
468     assert_eq!(-1.0, clamp(-2.0, -1.0, 2.0));
469     assert_eq!(2.0, clamp(3.0, -1.0, 2.0));
470     assert_eq!(1.0, clamp_min(1.0, -1.0));
471     assert_eq!(-1.0, clamp_min(-2.0, -1.0));
472     assert_eq!(-1.0, clamp_max(1.0, -1.0));
473     assert_eq!(-2.0, clamp_max(-2.0, -1.0));
474     assert!(clamp(::core::f32::NAN, -1.0, 1.0).is_nan());
475     assert!(clamp_min(::core::f32::NAN, 1.0).is_nan());
476     assert!(clamp_max(::core::f32::NAN, 1.0).is_nan());
477 }
478 
479 #[test]
480 #[should_panic]
481 #[cfg(debug_assertions)]
clamp_nan_min()482 fn clamp_nan_min() {
483     clamp(0., ::core::f32::NAN, 1.);
484 }
485 
486 #[test]
487 #[should_panic]
488 #[cfg(debug_assertions)]
clamp_nan_max()489 fn clamp_nan_max() {
490     clamp(0., -1., ::core::f32::NAN);
491 }
492 
493 #[test]
494 #[should_panic]
495 #[cfg(debug_assertions)]
clamp_nan_min_max()496 fn clamp_nan_min_max() {
497     clamp(0., ::core::f32::NAN, ::core::f32::NAN);
498 }
499 
500 #[test]
501 #[should_panic]
502 #[cfg(debug_assertions)]
clamp_min_nan_min()503 fn clamp_min_nan_min() {
504     clamp_min(0., ::core::f32::NAN);
505 }
506 
507 #[test]
508 #[should_panic]
509 #[cfg(debug_assertions)]
clamp_max_nan_max()510 fn clamp_max_nan_max() {
511     clamp_max(0., ::core::f32::NAN);
512 }
513 
514 #[test]
from_str_radix_unwrap()515 fn from_str_radix_unwrap() {
516     // The Result error must impl Debug to allow unwrap()
517 
518     let i: i32 = Num::from_str_radix("0", 10).unwrap();
519     assert_eq!(i, 0);
520 
521     let f: f32 = Num::from_str_radix("0.0", 10).unwrap();
522     assert_eq!(f, 0.0);
523 }
524 
525 #[test]
from_str_radix_multi_byte_fail()526 fn from_str_radix_multi_byte_fail() {
527     // Ensure parsing doesn't panic, even on invalid sign characters
528     assert!(f32::from_str_radix("™0.2", 10).is_err());
529 
530     // Even when parsing the exponent sign
531     assert!(f32::from_str_radix("0.2E™1", 10).is_err());
532 }
533 
534 #[test]
from_str_radix_ignore_case()535 fn from_str_radix_ignore_case() {
536     assert_eq!(
537         f32::from_str_radix("InF", 16).unwrap(),
538         ::core::f32::INFINITY
539     );
540     assert_eq!(
541         f32::from_str_radix("InfinitY", 16).unwrap(),
542         ::core::f32::INFINITY
543     );
544     assert_eq!(
545         f32::from_str_radix("-InF", 8).unwrap(),
546         ::core::f32::NEG_INFINITY
547     );
548     assert_eq!(
549         f32::from_str_radix("-InfinitY", 8).unwrap(),
550         ::core::f32::NEG_INFINITY
551     );
552     assert!(f32::from_str_radix("nAn", 4).unwrap().is_nan());
553     assert!(f32::from_str_radix("-nAn", 4).unwrap().is_nan());
554 }
555 
556 #[test]
wrapping_is_num()557 fn wrapping_is_num() {
558     fn require_num<T: Num>(_: &T) {}
559     require_num(&Wrapping(42_u32));
560     require_num(&Wrapping(-42));
561 }
562 
563 #[test]
wrapping_from_str_radix()564 fn wrapping_from_str_radix() {
565     macro_rules! test_wrapping_from_str_radix {
566         ($($t:ty)+) => {
567             $(
568                 for &(s, r) in &[("42", 10), ("42", 2), ("-13.0", 10), ("foo", 10)] {
569                     let w = Wrapping::<$t>::from_str_radix(s, r).map(|w| w.0);
570                     assert_eq!(w, <$t as Num>::from_str_radix(s, r));
571                 }
572             )+
573         };
574     }
575 
576     test_wrapping_from_str_radix!(usize u8 u16 u32 u64 isize i8 i16 i32 i64);
577 }
578 
579 #[test]
check_num_ops()580 fn check_num_ops() {
581     fn compute<T: Num + Copy>(x: T, y: T) -> T {
582         x * y / y % y + y - y
583     }
584     assert_eq!(compute(1, 2), 1)
585 }
586 
587 #[test]
check_numref_ops()588 fn check_numref_ops() {
589     fn compute<T: NumRef>(x: T, y: &T) -> T {
590         x * y / y % y + y - y
591     }
592     assert_eq!(compute(1, &2), 1)
593 }
594 
595 #[test]
check_refnum_ops()596 fn check_refnum_ops() {
597     fn compute<T: Copy>(x: &T, y: T) -> T
598     where
599         for<'a> &'a T: RefNum<T>,
600     {
601         &(&(&(&(x * y) / y) % y) + y) - y
602     }
603     assert_eq!(compute(&1, 2), 1)
604 }
605 
606 #[test]
check_refref_ops()607 fn check_refref_ops() {
608     fn compute<T>(x: &T, y: &T) -> T
609     where
610         for<'a> &'a T: RefNum<T>,
611     {
612         &(&(&(&(x * y) / y) % y) + y) - y
613     }
614     assert_eq!(compute(&1, &2), 1)
615 }
616 
617 #[test]
check_numassign_ops()618 fn check_numassign_ops() {
619     fn compute<T: NumAssign + Copy>(mut x: T, y: T) -> T {
620         x *= y;
621         x /= y;
622         x %= y;
623         x += y;
624         x -= y;
625         x
626     }
627     assert_eq!(compute(1, 2), 1)
628 }
629 
630 #[cfg(has_int_assignop_ref)]
631 #[test]
check_numassignref_ops()632 fn check_numassignref_ops() {
633     fn compute<T: NumAssignRef + Copy>(mut x: T, y: &T) -> T {
634         x *= y;
635         x /= y;
636         x %= y;
637         x += y;
638         x -= y;
639         x
640     }
641     assert_eq!(compute(1, &2), 1)
642 }
643