Skip to content

Commit 7e58e41

Browse files
Merge branch 'master' into python-support
2 parents ea35f85 + 22e0bca commit 7e58e41

File tree

8 files changed

+200
-20
lines changed

8 files changed

+200
-20
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ description = "algebraic numbers library"
1010
keywords = ["algebraic-numbers", "arbitrary-precision", "polynomials", "real-numbers", "exact-arithmetic"]
1111
repository = "https://salsa.debian.org/Kazan-team/algebraics"
1212
readme = "README.md"
13+
categories = ["algorithms", "data-structures", "science"]
1314

1415
[features]
1516
default = []

src/algebraic_numbers.rs

Lines changed: 180 additions & 14 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;
@@ -623,35 +625,43 @@ impl RealAlgebraicNumber {
623625
pub fn trunc(&self) -> Self {
624626
self.to_integer_trunc().into()
625627
}
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)> {
633631
let sign = match self.cmp_with_zero() {
634-
Ordering::Equal => unreachable!("already checked for zero"),
632+
Ordering::Equal => return None,
635633
Ordering::Less => Sign::Negative,
636634
Ordering::Greater => Sign::Positive,
637635
};
638-
let mut value = self.clone();
639636
match sign {
640637
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();
643640
}
644641
}
645642
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();
648645
}
649646
}
650647
}
651-
let mut interval_shrinker = value.interval_shrinker();
648+
let mut interval_shrinker = self.interval_shrinker();
652649
while interval_shrinker.interval.contains_zero() {
653650
interval_shrinker.shrink();
654651
}
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");
655665
let RealAlgebraicNumberData {
656666
minimal_polynomial,
657667
interval,
@@ -802,6 +812,65 @@ impl RealAlgebraicNumber {
802812
pub fn checked_pow<E: IntoRationalExponent>(&self, exponent: E) -> Option<Self> {
803813
Self::checked_pow_impl(Cow::Borrowed(self), exponent.into_rational_exponent())
804814
}
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+
}
805874
}
806875

807876
fn neg(value: Cow<RealAlgebraicNumber>) -> RealAlgebraicNumber {
@@ -1970,4 +2039,101 @@ mod tests {
19702039
)),
19712040
);
19722041
}
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+
}
19732139
}

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 {

src/polynomial.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1237,7 +1237,7 @@ impl<T: PolynomialCoefficient> Polynomial<T> {
12371237
T: GCD<Output = T> + PartialOrd,
12381238
{
12391239
if let Some(mut retval) = self.iter().fold(None, |lhs: Option<T>, rhs| match lhs {
1240-
None => Some(rhs.clone()),
1240+
None => Some(rhs),
12411241
Some(lhs) => Some(lhs.gcd(&rhs)),
12421242
}) {
12431243
let c = self

src/polynomial/div_rem.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,11 @@ impl<'a, T: PolynomialCoefficient + for<'b> ExactDiv<&'b T, Output = T>> ExactDi
470470
}
471471

472472
impl<T: PolynomialDivSupported> Polynomial<T> {
473-
pub fn checked_powmod<E: Clone + Integer>(&self, exponent: E, modulus: &Self) -> Option<Self> {
473+
pub fn checked_powmod<E: Clone + Integer>(
474+
&self,
475+
mut exponent: E,
476+
modulus: &Self,
477+
) -> Option<Self> {
474478
if exponent < Zero::zero() {
475479
return None;
476480
}
@@ -481,7 +485,6 @@ impl<T: PolynomialDivSupported> Polynomial<T> {
481485
if exponent.is_one() {
482486
return Some(base);
483487
}
484-
let mut exponent = exponent.clone();
485488
let mut retval: Option<Self> = None;
486489
loop {
487490
if exponent.is_odd() {

src/polynomial/same_degree_factorization.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ where
5454
.to_bigint()
5555
.expect("can't convert modulus/characteristic to BigInt");
5656
let polynomial_exponent = (bigint_characteristic.pow(factor_degree) - 1u32) / 2u32;
57-
let coefficient_uniform = Uniform::new(V::zero(), characteristic.clone());
57+
let coefficient_uniform = Uniform::new(V::zero(), characteristic);
5858
let mut retval = Vec::new();
5959
let mut factoring_stack = vec![self.clone()];
6060
while let Some(mut polynomial) = factoring_stack.pop() {

src/traits.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,6 @@ pub trait ExactDivAssign<Rhs = Self>: ExactDiv<Rhs, Output = Self> {
560560
panic!("exact division failed");
561561
}
562562
}
563-
#[must_use]
564563
fn checked_exact_div_assign(&mut self, rhs: Rhs) -> Result<(), ()>;
565564
}
566565

src/util.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ pub trait IsPseudoPrime:
634634
return false;
635635
}
636636
let sqrt = self.sqrt();
637-
if sqrt.clone() * sqrt.clone() == *self {
637+
if sqrt.clone() * sqrt == *self {
638638
return false;
639639
}
640640
if let Some(value) = self.to_u128() {

0 commit comments

Comments
 (0)