@@ -5,8 +5,10 @@ use crate::interval_arithmetic::DyadicFractionInterval;
5
5
use crate :: polynomial:: Polynomial ;
6
6
use crate :: traits:: AlwaysExactDiv ;
7
7
use crate :: traits:: AlwaysExactDivAssign ;
8
+ use crate :: traits:: CeilLog2 ;
8
9
use crate :: traits:: ExactDiv ;
9
10
use crate :: traits:: ExactDivAssign ;
11
+ use crate :: traits:: FloorLog2 ;
10
12
use crate :: util:: DebugAsDisplay ;
11
13
use crate :: util:: Sign ;
12
14
use num_bigint:: BigInt ;
@@ -623,35 +625,43 @@ impl RealAlgebraicNumber {
623
625
pub fn trunc ( & self ) -> Self {
624
626
self . to_integer_trunc ( ) . into ( )
625
627
}
626
- pub fn checked_recip ( & self ) -> Option < Self > {
627
- if let Some ( value) = self . to_rational ( ) {
628
- if value. is_zero ( ) {
629
- return None ;
630
- }
631
- return Some ( value. recip ( ) . into ( ) ) ;
632
- }
628
+ /// shrinks the interval till it doesn't contain zero
629
+ #[ must_use]
630
+ fn remove_zero_from_interval ( & mut self ) -> Option < ( Sign , IntervalShrinker ) > {
633
631
let sign = match self . cmp_with_zero ( ) {
634
- Ordering :: Equal => unreachable ! ( "already checked for zero" ) ,
632
+ Ordering :: Equal => return None ,
635
633
Ordering :: Less => Sign :: Negative ,
636
634
Ordering :: Greater => Sign :: Positive ,
637
635
} ;
638
- let mut value = self . clone ( ) ;
639
636
match sign {
640
637
Sign :: Negative => {
641
- if value . interval ( ) . upper_bound_numer ( ) . is_positive ( ) {
642
- value . data . interval . set_upper_bound_to_zero ( ) ;
638
+ if self . interval ( ) . upper_bound_numer ( ) . is_positive ( ) {
639
+ self . data . interval . set_upper_bound_to_zero ( ) ;
643
640
}
644
641
}
645
642
Sign :: Positive => {
646
- if value . interval ( ) . lower_bound_numer ( ) . is_negative ( ) {
647
- value . data . interval . set_lower_bound_to_zero ( ) ;
643
+ if self . interval ( ) . lower_bound_numer ( ) . is_negative ( ) {
644
+ self . data . interval . set_lower_bound_to_zero ( ) ;
648
645
}
649
646
}
650
647
}
651
- let mut interval_shrinker = value . interval_shrinker ( ) ;
648
+ let mut interval_shrinker = self . interval_shrinker ( ) ;
652
649
while interval_shrinker. interval . contains_zero ( ) {
653
650
interval_shrinker. shrink ( ) ;
654
651
}
652
+ Some ( ( sign, interval_shrinker) )
653
+ }
654
+ pub fn checked_recip ( & self ) -> Option < Self > {
655
+ if let Some ( value) = self . to_rational ( ) {
656
+ if value. is_zero ( ) {
657
+ return None ;
658
+ }
659
+ return Some ( value. recip ( ) . into ( ) ) ;
660
+ }
661
+ let mut value = self . clone ( ) ;
662
+ value
663
+ . remove_zero_from_interval ( )
664
+ . expect ( "known to be non-zero" ) ;
655
665
let RealAlgebraicNumberData {
656
666
minimal_polynomial,
657
667
interval,
@@ -802,6 +812,65 @@ impl RealAlgebraicNumber {
802
812
pub fn checked_pow < E : IntoRationalExponent > ( & self , exponent : E ) -> Option < Self > {
803
813
Self :: checked_pow_impl ( Cow :: Borrowed ( self ) , exponent. into_rational_exponent ( ) )
804
814
}
815
+ /// returns `Some(log2(self))` if self is a power of 2, otherwise `None`
816
+ pub fn to_integer_log2 ( & self ) -> Option < i64 > {
817
+ let ( numer, denom) = self . to_rational ( ) ?. into ( ) ;
818
+ if denom. is_one ( ) {
819
+ let retval = numer. floor_log2 ( ) ?;
820
+ if retval == numer. ceil_log2 ( ) . expect ( "known to be positive" ) {
821
+ return Some ( retval. to_i64 ( ) . expect ( "overflow" ) ) ;
822
+ }
823
+ } else if numer. is_one ( ) {
824
+ let retval = denom. floor_log2 ( ) . expect ( "known to be positive" ) ;
825
+ if retval == denom. ceil_log2 ( ) . expect ( "known to be positive" ) {
826
+ return Some ( -retval. to_i64 ( ) . expect ( "overflow" ) ) ;
827
+ }
828
+ }
829
+ None
830
+ }
831
+ fn do_checked_floor_ceil_log2 <
832
+ FloorCeilLog2 : Fn ( & DyadicFractionInterval ) -> Option < ( i64 , i64 ) > ,
833
+ > (
834
+ value : Cow < Self > ,
835
+ floor_ceil_log2 : FloorCeilLog2 ,
836
+ ) -> Option < i64 > {
837
+ if !value. is_positive ( ) {
838
+ return None ;
839
+ }
840
+ if let Some ( retval) = value. to_integer_log2 ( ) {
841
+ Some ( retval)
842
+ } else {
843
+ let mut value = value. into_owned ( ) ;
844
+ let mut interval_shrinker = value
845
+ . remove_zero_from_interval ( )
846
+ . expect ( "known to be positive" )
847
+ . 1 ;
848
+ loop {
849
+ let ( retval_lower_bound, retval_upper_bound) =
850
+ floor_ceil_log2 ( & interval_shrinker. interval ) . expect ( "known to be positive" ) ;
851
+ if retval_lower_bound == retval_upper_bound {
852
+ return Some ( retval_lower_bound) ;
853
+ }
854
+ interval_shrinker. shrink ( ) ;
855
+ }
856
+ }
857
+ }
858
+ /// returns `Some(floor(log2(self)))` if `self` is positive, otherwise `None`
859
+ pub fn into_checked_floor_log2 ( self ) -> Option < i64 > {
860
+ Self :: do_checked_floor_ceil_log2 ( Cow :: Owned ( self ) , DyadicFractionInterval :: floor_log2)
861
+ }
862
+ /// returns `Some(floor(log2(self)))` if `self` is positive, otherwise `None`
863
+ pub fn checked_floor_log2 ( & self ) -> Option < i64 > {
864
+ Self :: do_checked_floor_ceil_log2 ( Cow :: Borrowed ( self ) , DyadicFractionInterval :: floor_log2)
865
+ }
866
+ /// returns `Some(ceil(log2(self)))` if `self` is positive, otherwise `None`
867
+ pub fn into_checked_ceil_log2 ( self ) -> Option < i64 > {
868
+ Self :: do_checked_floor_ceil_log2 ( Cow :: Owned ( self ) , DyadicFractionInterval :: ceil_log2)
869
+ }
870
+ /// returns `Some(floor(log2(self)))` if `self` is positive, otherwise `None`
871
+ pub fn checked_ceil_log2 ( & self ) -> Option < i64 > {
872
+ Self :: do_checked_floor_ceil_log2 ( Cow :: Borrowed ( self ) , DyadicFractionInterval :: ceil_log2)
873
+ }
805
874
}
806
875
807
876
fn neg ( value : Cow < RealAlgebraicNumber > ) -> RealAlgebraicNumber {
@@ -1970,4 +2039,101 @@ mod tests {
1970
2039
) ) ,
1971
2040
) ;
1972
2041
}
2042
+
2043
+ #[ test]
2044
+ fn test_integer_floor_ceil_log2 ( ) {
2045
+ fn test_case < V : Into < RealAlgebraicNumber > > (
2046
+ value : V ,
2047
+ expected_integer_log2 : Option < i64 > ,
2048
+ expected_floor_log2 : Option < i64 > ,
2049
+ expected_ceil_log2 : Option < i64 > ,
2050
+ ) {
2051
+ let value = value. into ( ) ;
2052
+ println ! ( "value: {:?}" , value) ;
2053
+ println ! ( "expected_integer_log2: {:?}" , expected_integer_log2) ;
2054
+ println ! ( "expected_floor_log2: {:?}" , expected_floor_log2) ;
2055
+ println ! ( "expected_ceil_log2: {:?}" , expected_ceil_log2) ;
2056
+ let integer_log2 = value. to_integer_log2 ( ) ;
2057
+ println ! ( "integer_log2: {:?}" , integer_log2) ;
2058
+ let floor_log2 = value. checked_floor_log2 ( ) ;
2059
+ println ! ( "floor_log2: {:?}" , floor_log2) ;
2060
+ let ceil_log2 = value. into_checked_ceil_log2 ( ) ;
2061
+ println ! ( "ceil_log2: {:?}" , ceil_log2) ;
2062
+ assert ! ( expected_integer_log2 == integer_log2) ;
2063
+ assert ! ( expected_floor_log2 == floor_log2) ;
2064
+ assert ! ( expected_ceil_log2 == ceil_log2) ;
2065
+ }
2066
+ test_case ( 1 , Some ( 0 ) , Some ( 0 ) , Some ( 0 ) ) ;
2067
+ test_case ( 16 , Some ( 4 ) , Some ( 4 ) , Some ( 4 ) ) ;
2068
+ test_case ( r ( 1 , 16 ) , Some ( -4 ) , Some ( -4 ) , Some ( -4 ) ) ;
2069
+ test_case ( r ( 6 , 5 ) , None , Some ( 0 ) , Some ( 1 ) ) ;
2070
+ test_case ( r ( -6 , 5 ) , None , None , None ) ;
2071
+ test_case ( 0 , None , None , None ) ;
2072
+ test_case ( r ( 4 , 5 ) , None , Some ( -1 ) , Some ( 0 ) ) ;
2073
+ test_case ( r ( -1 , 5 ) , None , None , None ) ;
2074
+ test_case (
2075
+ make_sqrt ( 2 , DyadicFractionInterval :: from_int_range ( bi ( 1 ) , bi ( 2 ) , 0 ) ) ,
2076
+ None ,
2077
+ Some ( 0 ) ,
2078
+ Some ( 1 ) ,
2079
+ ) ;
2080
+ test_case (
2081
+ make_sqrt (
2082
+ 2_000_000 ,
2083
+ DyadicFractionInterval :: from_int_range ( bi ( 1000 ) , bi ( 2000 ) , 0 ) ,
2084
+ ) ,
2085
+ None ,
2086
+ Some ( 10 ) ,
2087
+ Some ( 11 ) ,
2088
+ ) ;
2089
+ test_case (
2090
+ make_sqrt (
2091
+ 200_000_000_000_000_000_000_000_000_000 ,
2092
+ DyadicFractionInterval :: from_int_range (
2093
+ bi ( 1000 ) ,
2094
+ bi ( 200_000_000_000_000_000_000 ) ,
2095
+ 0 ,
2096
+ ) ,
2097
+ ) ,
2098
+ None ,
2099
+ Some ( 48 ) ,
2100
+ Some ( 49 ) ,
2101
+ ) ;
2102
+ test_case (
2103
+ make_sqrt (
2104
+ 5_00000_00000 ,
2105
+ DyadicFractionInterval :: from_int_range ( bi ( 1_00000 ) , bi ( 3_00000 ) , 0 ) ,
2106
+ ) ,
2107
+ None ,
2108
+ Some ( 17 ) ,
2109
+ Some ( 18 ) ,
2110
+ ) ;
2111
+ test_case (
2112
+ RealAlgebraicNumber :: new_unchecked (
2113
+ p ( & [ 1 , 0 , -10 , 0 , 1 ] ) ,
2114
+ DyadicFractionInterval :: from_int_range ( bi ( -1 ) , bi ( 0 ) , 0 ) ,
2115
+ ) ,
2116
+ None ,
2117
+ None ,
2118
+ None ,
2119
+ ) ;
2120
+ test_case (
2121
+ RealAlgebraicNumber :: new_unchecked (
2122
+ p ( & [ 1 , 0 , -10 , 0 , 1 ] ) ,
2123
+ DyadicFractionInterval :: from_int_range ( bi ( 0 ) , bi ( 1 ) , 0 ) ,
2124
+ ) ,
2125
+ None ,
2126
+ Some ( -2 ) ,
2127
+ Some ( -1 ) ,
2128
+ ) ;
2129
+ test_case (
2130
+ RealAlgebraicNumber :: new_unchecked (
2131
+ p ( & [ -1 , 3 , -2 , 1 ] ) ,
2132
+ DyadicFractionInterval :: from_int_range ( bi ( -1000 ) , bi ( 1000 ) , 0 ) ,
2133
+ ) ,
2134
+ None ,
2135
+ Some ( -2 ) ,
2136
+ Some ( -1 ) ,
2137
+ ) ;
2138
+ }
1973
2139
}
0 commit comments