@@ -4437,34 +4437,43 @@ impl Computable {
44374437 }
44384438 }
44394439
4440- /// Do not call this function if `self` and `other` may be the same.
4441- pub fn compare_to ( & self , other : & Self ) -> Ordering {
4440+ /// Try to compare two computable values exactly.
4441+ ///
4442+ /// Returns `None` when bounded refinement cannot prove an ordering. This is
4443+ /// the public comparison API for callers that may be comparing equal or
4444+ /// semantically equivalent values.
4445+ pub fn try_compare_to ( & self , other : & Self ) -> Option < Ordering > {
4446+ if Self :: internal_structural_eq ( self , other) {
4447+ return Some ( Ordering :: Equal ) ;
4448+ }
4449+
44424450 // Keep exact leaf comparisons allocation-free for the hot path where both
44434451 // operands are already exact. This avoids creating temporary rationals
44444452 // on every comparator call.
44454453 if let Some ( order) = self . exact_rational_leaf_cmp ( other) {
44464454 crate :: trace_dispatch!( "computable" , "compare_to" , "exact-rational" ) ;
4447- return order;
4455+ return Some ( order) ;
44484456 }
44494457
44504458 if let ( Some ( left) , Some ( right) ) = ( self . exact_rational ( ) , other. exact_rational ( ) ) {
44514459 // Exact rationals compare directly; escalating to approximate comparison here is
44524460 // both slower and can burn cache precision unnecessarily.
44534461 crate :: trace_dispatch!( "computable" , "compare_to" , "exact-rational" ) ;
4454- return left
4455- . partial_cmp ( & right)
4456- . expect ( "exact rationals should be comparable" ) ;
4462+ return Some (
4463+ left. partial_cmp ( & right)
4464+ . expect ( "exact rationals should be comparable" ) ,
4465+ ) ;
44574466 }
44584467
44594468 if let ( Some ( left) , Some ( right) ) = ( self . exact_sign ( ) , other. exact_sign ( ) ) {
44604469 match ( left, right) {
44614470 ( Sign :: Minus , Sign :: Plus | Sign :: NoSign ) | ( Sign :: NoSign , Sign :: Plus ) => {
44624471 crate :: trace_dispatch!( "computable" , "compare_to" , "exact-sign-opposite" ) ;
4463- return Ordering :: Less ;
4472+ return Some ( Ordering :: Less ) ;
44644473 }
44654474 ( Sign :: Plus , Sign :: Minus | Sign :: NoSign ) | ( Sign :: NoSign , Sign :: Minus ) => {
44664475 crate :: trace_dispatch!( "computable" , "compare_to" , "exact-sign-opposite" ) ;
4467- return Ordering :: Greater ;
4476+ return Some ( Ordering :: Greater ) ;
44684477 }
44694478 _ => { }
44704479 }
@@ -4480,11 +4489,11 @@ impl Computable {
44804489 // Same-sign values with different most-significant digits have a known
44814490 // order without evaluating either value to a requested precision.
44824491 crate :: trace_dispatch!( "computable" , "compare_to" , "exact-sign-msd-gap" ) ;
4483- return match left {
4492+ return Some ( match left {
44844493 Sign :: Plus => left_msd. cmp ( & right_msd) ,
44854494 Sign :: Minus => right_msd. cmp ( & left_msd) ,
44864495 Sign :: NoSign => unreachable ! ( ) ,
4487- } ;
4496+ } ) ;
44884497 }
44894498 }
44904499
@@ -4496,13 +4505,13 @@ impl Computable {
44964505 match ( left, right) {
44974506 ( Sign :: Minus , Sign :: Plus ) | ( Sign :: NoSign , Sign :: Plus ) => {
44984507 crate :: trace_dispatch!( "computable" , "compare_to" , "cheap-bound-opposite-sign" ) ;
4499- return Ordering :: Less ;
4508+ return Some ( Ordering :: Less ) ;
45004509 }
45014510 ( Sign :: Plus , Sign :: Minus ) | ( Sign :: Plus , Sign :: NoSign ) => {
45024511 crate :: trace_dispatch!( "computable" , "compare_to" , "cheap-bound-opposite-sign" ) ;
4503- return Ordering :: Greater ;
4512+ return Some ( Ordering :: Greater ) ;
45044513 }
4505- ( Sign :: NoSign , Sign :: NoSign ) => return Ordering :: Equal ,
4514+ ( Sign :: NoSign , Sign :: NoSign ) => return Some ( Ordering :: Equal ) ,
45064515 _ => { }
45074516 }
45084517 if left == right
@@ -4513,23 +4522,23 @@ impl Computable {
45134522 // Same-sign structural bounds can decide exact ordering
45144523 // before entering tolerance refinement.
45154524 crate :: trace_dispatch!( "computable" , "compare_to" , "cheap-bound-msd-gap" ) ;
4516- return match left {
4525+ return Some ( match left {
45174526 Sign :: Plus => left_msd. cmp ( & right_msd) ,
45184527 Sign :: Minus => right_msd. cmp ( & left_msd) ,
45194528 Sign :: NoSign => Ordering :: Equal ,
4520- } ;
4529+ } ) ;
45214530 }
45224531 }
45234532 crate :: trace_dispatch!( "computable" , "compare_to" , "approx-refinement" ) ;
45244533 let mut tolerance = -20 ;
45254534 while tolerance > Precision :: MIN {
45264535 let order = self . compare_absolute ( other, tolerance) ;
45274536 if order != Ordering :: Equal {
4528- return order;
4537+ return Some ( order) ;
45294538 }
45304539 tolerance *= 2 ;
45314540 }
4532- panic ! ( "Apparently called Computable::compare_to on equal values" ) ;
4541+ None
45334542 }
45344543
45354544 /// Compare two values to a specified tolerance (more negative numbers are more precise).
@@ -4813,9 +4822,9 @@ mod tests {
48134822 let five = Computable :: integer ( five. clone ( ) ) ;
48144823 let four = Computable :: integer ( four. clone ( ) ) ;
48154824
4816- assert_eq ! ( six. compare_to ( & five) , Ordering :: Greater ) ;
4817- assert_eq ! ( five. compare_to ( & six) , Ordering :: Less ) ;
4818- assert_eq ! ( four. compare_to ( & six) , Ordering :: Less ) ;
4825+ assert_eq ! ( six. try_compare_to ( & five) , Some ( Ordering :: Greater ) ) ;
4826+ assert_eq ! ( five. try_compare_to ( & six) , Some ( Ordering :: Less ) ) ;
4827+ assert_eq ! ( four. try_compare_to ( & six) , Some ( Ordering :: Less ) ) ;
48194828 }
48204829
48214830 #[ test]
@@ -5138,11 +5147,21 @@ mod tests {
51385147 fn compare_to_uses_exact_sign_and_rational_shortcuts ( ) {
51395148 let minus_pi = Computable :: pi ( ) . negate ( ) ;
51405149 let pi = Computable :: pi ( ) ;
5141- assert_eq ! ( minus_pi. compare_to ( & pi) , Ordering :: Less ) ;
5150+ assert_eq ! ( minus_pi. try_compare_to ( & pi) , Some ( Ordering :: Less ) ) ;
51425151
51435152 let left = Computable :: rational ( Rational :: fraction ( 7 , 8 ) . unwrap ( ) ) ;
51445153 let right = Computable :: rational ( Rational :: fraction ( 9 , 10 ) . unwrap ( ) ) ;
5145- assert_eq ! ( left. compare_to( & right) , Ordering :: Less ) ;
5154+ assert_eq ! ( left. try_compare_to( & right) , Some ( Ordering :: Less ) ) ;
5155+ }
5156+
5157+ #[ test]
5158+ fn try_compare_to_handles_identical_symbolic_values ( ) {
5159+ let pi = Computable :: pi ( ) ;
5160+ assert_eq ! ( pi. try_compare_to( & pi) , Some ( Ordering :: Equal ) ) ;
5161+
5162+ let left = Computable :: rational ( Rational :: fraction ( 3 , 7 ) . unwrap ( ) ) ;
5163+ let right = Computable :: rational ( Rational :: fraction ( 3 , 7 ) . unwrap ( ) ) ;
5164+ assert_eq ! ( left. try_compare_to( & right) , Some ( Ordering :: Equal ) ) ;
51465165 }
51475166
51485167 #[ test]
@@ -5154,13 +5173,16 @@ mod tests {
51545173 . multiply ( Computable :: rational ( Rational :: from_bigint (
51555174 BigInt :: from ( 1_u8 ) << 200 ,
51565175 ) ) ) ;
5157- assert_eq ! ( huge. compare_to ( & base) , Ordering :: Greater ) ;
5158- assert_eq ! ( base. compare_to ( & huge) , Ordering :: Less ) ;
5176+ assert_eq ! ( huge. try_compare_to ( & base) , Some ( Ordering :: Greater ) ) ;
5177+ assert_eq ! ( base. try_compare_to ( & huge) , Some ( Ordering :: Less ) ) ;
51595178
51605179 let minus_base = base. negate ( ) ;
51615180 let minus_huge = huge. negate ( ) ;
5162- assert_eq ! ( minus_huge. compare_to( & minus_base) , Ordering :: Less ) ;
5163- assert_eq ! ( minus_base. compare_to( & minus_huge) , Ordering :: Greater ) ;
5181+ assert_eq ! ( minus_huge. try_compare_to( & minus_base) , Some ( Ordering :: Less ) ) ;
5182+ assert_eq ! (
5183+ minus_base. try_compare_to( & minus_huge) ,
5184+ Some ( Ordering :: Greater )
5185+ ) ;
51645186 }
51655187
51665188 #[ test]
0 commit comments