@@ -990,7 +990,7 @@ trait RootSelector: Sized {
990990 . collect ( ) ;
991991 let mut factors_temp = Vec :: with_capacity ( factors. len ( ) ) ;
992992 loop {
993- let interval = self . get_interval ( ) ;
993+ let mut interval = self . get_interval ( ) ;
994994 let ( lower_bound, upper_bound) = interval. to_ratio_range ( ) ;
995995 mem:: swap ( & mut factors, & mut factors_temp) ;
996996 factors. clear ( ) ;
@@ -1019,10 +1019,94 @@ trait RootSelector: Sized {
10191019 }
10201020 }
10211021 if roots_left <= 1 {
1022- break RealAlgebraicNumber :: new_unchecked (
1023- factors. remove ( 0 ) . into_factor ( ) ,
1024- interval,
1025- ) ;
1022+ let factor = factors. remove ( 0 ) ;
1023+ if factor. factor ( ) . degree ( ) . is_some_and ( |deg| deg > 1 ) {
1024+ // Reduce the precision of the interval as much as possible to prevent the precision from exploding due to repeated computation
1025+ // Skip this step if the number is rational
1026+
1027+ let mut precomputed_lower_bound_sign_changes = None ;
1028+ let mut precomputed_upper_bound_sign_changes = None ;
1029+
1030+ while interval. log2_denom ( ) > 0 {
1031+ let trial_interval = interval
1032+ . clone ( )
1033+ . into_converted_log2_denom ( interval. log2_denom ( ) - 1 ) ;
1034+
1035+ let lower_bound_sign_changes = sign_changes_at (
1036+ & factor. primitive_sturm_sequence ,
1037+ ValueOrInfinity :: Value ( & trial_interval. lower_bound ( ) ) ,
1038+ ) ;
1039+ let upper_bound_sign_changes = sign_changes_at (
1040+ & factor. primitive_sturm_sequence ,
1041+ ValueOrInfinity :: Value ( & trial_interval. upper_bound ( ) ) ,
1042+ ) ;
1043+ // We don't need to check if the lower bound is a root because we know that the root is irrational
1044+ let num_roots = distance (
1045+ lower_bound_sign_changes. sign_change_count ,
1046+ upper_bound_sign_changes. sign_change_count ,
1047+ ) ;
1048+
1049+ assert ! ( num_roots > 0 ) ;
1050+
1051+ if num_roots == 1 {
1052+ interval = trial_interval;
1053+ precomputed_lower_bound_sign_changes = Some ( lower_bound_sign_changes) ;
1054+ precomputed_upper_bound_sign_changes = Some ( upper_bound_sign_changes) ;
1055+ } else {
1056+ break ;
1057+ }
1058+ }
1059+
1060+ // Tighten the range as much as possible to prevent that from exploding too
1061+
1062+ let lower_bound_sign_changes = precomputed_lower_bound_sign_changes
1063+ . unwrap_or_else ( || {
1064+ sign_changes_at (
1065+ & factor. primitive_sturm_sequence ,
1066+ ValueOrInfinity :: Value ( & interval. lower_bound ( ) ) ,
1067+ )
1068+ } ) ;
1069+ let upper_bound_sign_changes = precomputed_upper_bound_sign_changes
1070+ . unwrap_or_else ( || {
1071+ sign_changes_at (
1072+ & factor. primitive_sturm_sequence ,
1073+ ValueOrInfinity :: Value ( & interval. upper_bound ( ) ) ,
1074+ )
1075+ } ) ;
1076+
1077+ while interval. upper_bound_numer ( ) - interval. lower_bound_numer ( )
1078+ > BigInt :: from ( 1 )
1079+ {
1080+ let middle = ( interval. lower_bound_numer ( ) + interval. upper_bound_numer ( ) )
1081+ / BigInt :: from ( 2 ) ;
1082+
1083+ let middle_sign_changes = sign_changes_at (
1084+ & factor. primitive_sturm_sequence ,
1085+ ValueOrInfinity :: Value ( & Ratio :: new (
1086+ middle. clone ( ) ,
1087+ BigInt :: one ( ) << interval. log2_denom ( ) ,
1088+ ) ) ,
1089+ ) ;
1090+
1091+ if middle_sign_changes. sign_change_count
1092+ == lower_bound_sign_changes. sign_change_count
1093+ {
1094+ interval. set_lower_bound_numer ( middle) ;
1095+ } else {
1096+ assert_eq ! (
1097+ middle_sign_changes. sign_change_count,
1098+ upper_bound_sign_changes. sign_change_count
1099+ ) ;
1100+ interval. set_upper_bound_numer ( middle) ;
1101+ }
1102+ }
1103+ }
1104+
1105+ let v = RealAlgebraicNumber :: new_unchecked ( factor. into_factor ( ) , interval) ;
1106+ break match v. to_rational ( ) {
1107+ Some ( rational) => RealAlgebraicNumber :: from ( rational) ,
1108+ None => v,
1109+ } ;
10261110 }
10271111 self . shrink_interval ( ) ;
10281112 }
@@ -1665,6 +1749,35 @@ mod tests {
16651749 ) ;
16661750 }
16671751
1752+ #[ test]
1753+ fn prevent_exploding_intervals ( ) {
1754+ let mut num = RealAlgebraicNumber :: from ( 2 ) / RealAlgebraicNumber :: from ( 3 ) ;
1755+ let original = num. clone ( ) ;
1756+
1757+ for _ in 0 ..100 {
1758+ num = num. pow ( ( 1 , 2 ) ) ;
1759+ num = num. pow ( ( 2 , 1 ) ) ;
1760+ }
1761+
1762+ assert_eq ! ( num, original) ;
1763+ assert_eq ! ( num. interval( ) . log2_denom( ) , 0 ) ;
1764+ assert_eq ! ( num. interval( ) . lower_bound_numer( ) , & BigInt :: from( 0 ) ) ;
1765+ assert_eq ! ( num. interval( ) . upper_bound_numer( ) , & BigInt :: from( 1 ) ) ;
1766+ }
1767+
1768+ #[ test]
1769+ fn prevent_exploding_precisions ( ) {
1770+ let v1 = -( RealAlgebraicNumber :: from ( 2 ) / RealAlgebraicNumber :: from ( 81 ) ) . pow ( ( 1 , 2 ) ) ;
1771+ let v2 = ( RealAlgebraicNumber :: from ( 200 ) / RealAlgebraicNumber :: from ( 81 ) ) . pow ( ( 1 , 2 ) ) ;
1772+
1773+ let num = v1 + v2;
1774+
1775+ assert_eq ! ( num, RealAlgebraicNumber :: from( 2 ) . pow( ( 1 , 2 ) ) ) ;
1776+ assert_eq ! ( num. interval( ) . log2_denom( ) , 0 ) ;
1777+ assert_eq ! ( num. interval( ) . lower_bound_numer( ) , & BigInt :: from( 1 ) ) ;
1778+ assert_eq ! ( num. interval( ) . upper_bound_numer( ) , & BigInt :: from( 2 ) ) ;
1779+ }
1780+
16681781 #[ test]
16691782 fn test_add ( ) {
16701783 fn test_case <
0 commit comments