@@ -90,9 +90,6 @@ impl<T: Clone + Integer> Ratio<T> {
9090 /// Creates a new `Ratio`. Fails if `denom` is zero.
9191 #[ inline]
9292 pub fn new ( numer : T , denom : T ) -> Ratio < T > {
93- if denom. is_zero ( ) {
94- panic ! ( "denominator == 0" ) ;
95- }
9693 let mut ret = Ratio :: new_raw ( numer, denom) ;
9794 ret. reduce ( ) ;
9895 ret
@@ -118,6 +115,17 @@ impl<T: Clone + Integer> Ratio<T> {
118115
119116 /// Puts self into lowest terms, with denom > 0.
120117 fn reduce ( & mut self ) {
118+ if self . denom . is_zero ( ) {
119+ panic ! ( "denominator == 0" ) ;
120+ }
121+ if self . numer . is_zero ( ) {
122+ self . denom . set_one ( ) ;
123+ return ;
124+ }
125+ if self . numer == self . denom {
126+ self . set_one ( ) ;
127+ return ;
128+ }
121129 let g: T = self . numer . gcd ( & self . denom ) ;
122130
123131 // FIXME(#5992): assignment operator overloads
@@ -776,17 +784,40 @@ where
776784 if rhs. is_zero ( ) {
777785 return None ;
778786 }
779- let gcd_ac = self . numer . gcd ( & rhs. numer ) ;
780- let gcd_bd = self . denom . gcd ( & rhs. denom ) ;
781- let denom = ( self . denom . clone ( ) / gcd_bd. clone ( ) )
782- . checked_mul ( & ( rhs. numer . clone ( ) / gcd_ac. clone ( ) ) ) ?;
787+ let ( numer, denom) = if self . denom == rhs. denom {
788+ ( self . numer . clone ( ) , rhs. numer . clone ( ) )
789+ } else if self . numer == rhs. numer {
790+ ( rhs. denom . clone ( ) , self . denom . clone ( ) )
791+ } else {
792+ let gcd_ac = self . numer . gcd ( & rhs. numer ) ;
793+ let gcd_bd = self . denom . gcd ( & rhs. denom ) ;
794+ (
795+ ( self . numer . clone ( ) / gcd_ac. clone ( ) )
796+ . checked_mul ( & ( rhs. denom . clone ( ) / gcd_bd. clone ( ) ) ) ?,
797+ ( self . denom . clone ( ) / gcd_bd) . checked_mul ( & ( rhs. numer . clone ( ) / gcd_ac) ) ?,
798+ )
799+ } ;
800+ // Manual `reduce()`, avoiding sharp edges
783801 if denom. is_zero ( ) {
784- return None ;
802+ None
803+ } else if numer. is_zero ( ) {
804+ Some ( Self :: zero ( ) )
805+ } else if numer == denom {
806+ Some ( Self :: one ( ) )
807+ } else {
808+ let g = numer. gcd ( & denom) ;
809+ let numer = numer / g. clone ( ) ;
810+ let denom = denom / g;
811+ let raw = if denom < T :: zero ( ) {
812+ // We need to keep denom positive, but 2's-complement MIN may
813+ // overflow negation -- instead we can check multiplying -1.
814+ let n1 = T :: zero ( ) - T :: one ( ) ;
815+ Ratio :: new_raw ( numer. checked_mul ( & n1) ?, denom. checked_mul ( & n1) ?)
816+ } else {
817+ Ratio :: new_raw ( numer, denom)
818+ } ;
819+ Some ( raw)
785820 }
786- Some ( Ratio :: new (
787- ( self . numer . clone ( ) / gcd_ac) . checked_mul ( & ( rhs. denom . clone ( ) / gcd_bd) ) ?,
788- denom,
789- ) )
790821 }
791822}
792823
@@ -1295,6 +1326,7 @@ mod test {
12951326
12961327 use core:: f64;
12971328 use core:: i32;
1329+ use core:: isize;
12981330 use core:: str:: FromStr ;
12991331 use num_integer:: Integer ;
13001332 use num_traits:: { FromPrimitive , One , Pow , Signed , Zero } ;
@@ -1331,6 +1363,22 @@ mod test {
13311363 numer : -2 ,
13321364 denom : 3 ,
13331365 } ;
1366+ pub const _MIN: Rational = Ratio {
1367+ numer : isize:: MIN ,
1368+ denom : 1 ,
1369+ } ;
1370+ pub const _MIN_P1: Rational = Ratio {
1371+ numer : isize:: MIN + 1 ,
1372+ denom : 1 ,
1373+ } ;
1374+ pub const _MAX: Rational = Ratio {
1375+ numer : isize:: MAX ,
1376+ denom : 1 ,
1377+ } ;
1378+ pub const _MAX_M1: Rational = Ratio {
1379+ numer : isize:: MAX - 1 ,
1380+ denom : 1 ,
1381+ } ;
13341382
13351383 #[ cfg( feature = "bigint" ) ]
13361384 pub fn to_big ( n : Rational ) -> BigRational {
@@ -1361,9 +1409,9 @@ mod test {
13611409
13621410 #[ test]
13631411 fn test_new_reduce ( ) {
1364- let one22 = Ratio :: new ( 2 , 2 ) ;
1365-
1366- assert_eq ! ( one22 , One :: one( ) ) ;
1412+ assert_eq ! ( Ratio :: new( 2 , 2 ) , One :: one ( ) ) ;
1413+ assert_eq ! ( Ratio :: new ( 0 , i32 :: MIN ) , Zero :: zero ( ) ) ;
1414+ assert_eq ! ( Ratio :: new ( i32 :: MIN , i32 :: MIN ) , One :: one( ) ) ;
13671415 }
13681416 #[ test]
13691417 #[ should_panic]
@@ -1521,7 +1569,7 @@ mod test {
15211569
15221570 mod arith {
15231571 use super :: super :: { Ratio , Rational } ;
1524- use super :: { to_big, _0, _1, _1_2, _2, _3_2, _5_2, _NEG1_2} ;
1572+ use super :: { to_big, _0, _1, _1_2, _2, _3_2, _5_2, _MAX , _MAX_M1 , _MIN , _MIN_P1 , _NEG1_2} ;
15251573 use core:: fmt:: Debug ;
15261574 use num_integer:: Integer ;
15271575 use num_traits:: { Bounded , CheckedAdd , CheckedDiv , CheckedMul , CheckedSub , NumAssign } ;
@@ -1953,6 +2001,69 @@ mod test {
19532001 assert_eq ! ( _0. checked_mul( & _0) , Some ( _0) ) ;
19542002 assert_eq ! ( _0. checked_div( & _0) , None ) ;
19552003 }
2004+
2005+ #[ test]
2006+ fn test_checked_min ( ) {
2007+ assert_eq ! ( _MIN. checked_add( & _MIN) , None ) ;
2008+ assert_eq ! ( _MIN. checked_sub( & _MIN) , Some ( _0) ) ;
2009+ assert_eq ! ( _MIN. checked_mul( & _MIN) , None ) ;
2010+ assert_eq ! ( _MIN. checked_div( & _MIN) , Some ( _1) ) ;
2011+ assert_eq ! ( _0. checked_add( & _MIN) , Some ( _MIN) ) ;
2012+ assert_eq ! ( _0. checked_sub( & _MIN) , None ) ;
2013+ assert_eq ! ( _0. checked_mul( & _MIN) , Some ( _0) ) ;
2014+ assert_eq ! ( _0. checked_div( & _MIN) , Some ( _0) ) ;
2015+ assert_eq ! ( _1. checked_add( & _MIN) , Some ( _MIN_P1) ) ;
2016+ assert_eq ! ( _1. checked_sub( & _MIN) , None ) ;
2017+ assert_eq ! ( _1. checked_mul( & _MIN) , Some ( _MIN) ) ;
2018+ assert_eq ! ( _1. checked_div( & _MIN) , None ) ;
2019+ assert_eq ! ( _MIN. checked_add( & _0) , Some ( _MIN) ) ;
2020+ assert_eq ! ( _MIN. checked_sub( & _0) , Some ( _MIN) ) ;
2021+ assert_eq ! ( _MIN. checked_mul( & _0) , Some ( _0) ) ;
2022+ assert_eq ! ( _MIN. checked_div( & _0) , None ) ;
2023+ assert_eq ! ( _MIN. checked_add( & _1) , Some ( _MIN_P1) ) ;
2024+ assert_eq ! ( _MIN. checked_sub( & _1) , None ) ;
2025+ assert_eq ! ( _MIN. checked_mul( & _1) , Some ( _MIN) ) ;
2026+ assert_eq ! ( _MIN. checked_div( & _1) , Some ( _MIN) ) ;
2027+ }
2028+
2029+ #[ test]
2030+ fn test_checked_max ( ) {
2031+ assert_eq ! ( _MAX. checked_add( & _MAX) , None ) ;
2032+ assert_eq ! ( _MAX. checked_sub( & _MAX) , Some ( _0) ) ;
2033+ assert_eq ! ( _MAX. checked_mul( & _MAX) , None ) ;
2034+ assert_eq ! ( _MAX. checked_div( & _MAX) , Some ( _1) ) ;
2035+ assert_eq ! ( _0. checked_add( & _MAX) , Some ( _MAX) ) ;
2036+ assert_eq ! ( _0. checked_sub( & _MAX) , Some ( _MIN_P1) ) ;
2037+ assert_eq ! ( _0. checked_mul( & _MAX) , Some ( _0) ) ;
2038+ assert_eq ! ( _0. checked_div( & _MAX) , Some ( _0) ) ;
2039+ assert_eq ! ( _1. checked_add( & _MAX) , None ) ;
2040+ assert_eq ! ( _1. checked_sub( & _MAX) , Some ( -_MAX_M1) ) ;
2041+ assert_eq ! ( _1. checked_mul( & _MAX) , Some ( _MAX) ) ;
2042+ assert_eq ! ( _1. checked_div( & _MAX) , Some ( _MAX. recip( ) ) ) ;
2043+ assert_eq ! ( _MAX. checked_add( & _0) , Some ( _MAX) ) ;
2044+ assert_eq ! ( _MAX. checked_sub( & _0) , Some ( _MAX) ) ;
2045+ assert_eq ! ( _MAX. checked_mul( & _0) , Some ( _0) ) ;
2046+ assert_eq ! ( _MAX. checked_div( & _0) , None ) ;
2047+ assert_eq ! ( _MAX. checked_add( & _1) , None ) ;
2048+ assert_eq ! ( _MAX. checked_sub( & _1) , Some ( _MAX_M1) ) ;
2049+ assert_eq ! ( _MAX. checked_mul( & _1) , Some ( _MAX) ) ;
2050+ assert_eq ! ( _MAX. checked_div( & _1) , Some ( _MAX) ) ;
2051+ }
2052+
2053+ #[ test]
2054+ fn test_checked_min_max ( ) {
2055+ assert_eq ! ( _MIN. checked_add( & _MAX) , Some ( -_1) ) ;
2056+ assert_eq ! ( _MIN. checked_sub( & _MAX) , None ) ;
2057+ assert_eq ! ( _MIN. checked_mul( & _MAX) , None ) ;
2058+ assert_eq ! (
2059+ _MIN. checked_div( & _MAX) ,
2060+ Some ( Ratio :: new( _MIN. numer, _MAX. numer) )
2061+ ) ;
2062+ assert_eq ! ( _MAX. checked_add( & _MIN) , Some ( -_1) ) ;
2063+ assert_eq ! ( _MAX. checked_sub( & _MIN) , None ) ;
2064+ assert_eq ! ( _MAX. checked_mul( & _MIN) , None ) ;
2065+ assert_eq ! ( _MAX. checked_div( & _MIN) , None ) ;
2066+ }
19562067 }
19572068
19582069 #[ test]
0 commit comments