Skip to content

Commit e38c358

Browse files
implement checked_(floor|ceil)_log2 and to_integer_log2
1 parent 0302b02 commit e38c358

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

src/algebraic_numbers.rs

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ use crate::interval_arithmetic::DyadicFractionInterval;
55
use crate::polynomial::Polynomial;
66
use crate::traits::AlwaysExactDiv;
77
use crate::traits::AlwaysExactDivAssign;
8+
use crate::traits::CeilLog2;
89
use crate::traits::ExactDiv;
910
use crate::traits::ExactDivAssign;
11+
use crate::traits::FloorLog2;
1012
use crate::util::DebugAsDisplay;
1113
use crate::util::Sign;
1214
use num_bigint::BigInt;
@@ -810,6 +812,65 @@ impl RealAlgebraicNumber {
810812
pub fn checked_pow<E: IntoRationalExponent>(&self, exponent: E) -> Option<Self> {
811813
Self::checked_pow_impl(Cow::Borrowed(self), exponent.into_rational_exponent())
812814
}
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+
}
813874
}
814875

815876
fn neg(value: Cow<RealAlgebraicNumber>) -> RealAlgebraicNumber {
@@ -1978,4 +2039,101 @@ mod tests {
19782039
)),
19792040
);
19802041
}
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+
}
19812139
}

src/interval_arithmetic.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use crate::traits::AlwaysExactDiv;
55
use crate::traits::AlwaysExactDivAssign;
6+
use crate::traits::CeilLog2;
67
use crate::traits::ExactDiv;
78
use crate::traits::ExactDivAssign;
89
use crate::traits::FloorLog2;
@@ -838,6 +839,16 @@ impl DyadicFractionInterval {
838839
pub fn ceil(&self, new_log2_denom: usize) -> Self {
839840
self.clone().into_ceil(new_log2_denom)
840841
}
842+
pub(crate) fn floor_log2(&self) -> Option<(i64, i64)> {
843+
let lower_bound = self.lower_bound_numer.floor_log2()? as i64 - self.log2_denom as i64;
844+
let upper_bound = self.upper_bound_numer.floor_log2()? as i64 - self.log2_denom as i64;
845+
Some((lower_bound, upper_bound))
846+
}
847+
pub(crate) fn ceil_log2(&self) -> Option<(i64, i64)> {
848+
let lower_bound = self.lower_bound_numer.ceil_log2()? as i64 - self.log2_denom as i64;
849+
let upper_bound = self.upper_bound_numer.ceil_log2()? as i64 - self.log2_denom as i64;
850+
Some((lower_bound, upper_bound))
851+
}
841852
}
842853

843854
impl fmt::Debug for DyadicFractionInterval {

0 commit comments

Comments
 (0)