use std::fmt::Debug;
use crate::divisibility::*;
use crate::homomorphism::*;
use crate::ordered::*;
use crate::pid::*;
use crate::reduce_lift::poly_eval::EvalPolyLocallyRing;
use crate::reduce_lift::poly_factor_gcd::IntegerPolyGCDRing;
use crate::ring::*;
#[cfg(feature = "mpir")]
pub type BigIntRing = crate::rings::mpir::MPZ;
#[cfg(not(feature = "mpir"))]
pub type BigIntRing = crate::rings::rust_bigint::RustBigintRing;
#[cfg(feature = "mpir")]
pub type BigIntRingBase = crate::rings::mpir::MPZBase;
#[cfg(not(feature = "mpir"))]
pub type BigIntRingBase = crate::rings::rust_bigint::RustBigintRingBase;
pub trait IntegerRing:
Domain + EuclideanRing + OrderedRing + HashableElRing + IntegerPolyGCDRing + EvalPolyLocallyRing + Debug
{
fn to_float_approx(&self, value: &Self::Element) -> f64;
fn from_float_approx(&self, value: f64) -> Option<Self::Element>;
fn abs_is_bit_set(&self, value: &Self::Element, i: usize) -> bool;
fn abs_highest_set_bit(&self, value: &Self::Element) -> Option<usize>;
fn abs_lowest_set_bit(&self, value: &Self::Element) -> Option<usize>;
fn euclidean_div_pow_2(&self, value: &mut Self::Element, power: usize);
fn mul_pow_2(&self, value: &mut Self::Element, power: usize);
fn get_uniformly_random_bits<G: FnMut() -> u64>(&self, log2_bound_exclusive: usize, rng: G) -> Self::Element;
fn rounded_div(&self, lhs: Self::Element, rhs: &Self::Element) -> Self::Element {
let mut rhs_half = self.abs(self.clone_el(rhs));
self.euclidean_div_pow_2(&mut rhs_half, 1);
if self.is_neg(&lhs) {
return self.euclidean_div(self.sub(lhs, rhs_half), rhs);
} else {
return self.euclidean_div(self.add(lhs, rhs_half), rhs);
}
}
fn ceil_div(&self, lhs: Self::Element, rhs: &Self::Element) -> Self::Element {
assert!(!self.is_zero(rhs));
if self.is_zero(&lhs) {
return self.zero();
}
let one = self.one();
return match (self.is_pos(&lhs), self.is_pos(rhs)) {
(true, true) => self.add(self.euclidean_div(self.sub_ref_snd(lhs, &one), rhs), one),
(false, true) => self.euclidean_div(lhs, rhs),
(true, false) => self.euclidean_div(lhs, rhs),
(false, false) => self.add(self.euclidean_div(self.add_ref_snd(lhs, &one), rhs), one),
};
}
fn floor_div(&self, lhs: Self::Element, rhs: &Self::Element) -> Self::Element {
self.negate(self.ceil_div(self.negate(lhs), rhs))
}
fn power_of_two(&self, power: usize) -> Self::Element {
let mut result = self.one();
self.mul_pow_2(&mut result, power);
return result;
}
fn representable_bits(&self) -> Option<usize>;
fn parse(&self, string: &str, base: u32) -> Result<Self::Element, ()> {
generic_impls::parse(RingRef::new(self), string, base)
}
}
impl<I, J> CanHomFrom<I> for J
where
I: ?Sized + IntegerRing,
J: ?Sized + IntegerRing,
{
type Homomorphism = ();
fn has_canonical_hom(&self, _: &I) -> Option<Self::Homomorphism> { Some(()) }
fn map_in(&self, from: &I, el: <I as RingBase>::Element, _: &Self::Homomorphism) -> Self::Element {
int_cast(el, &RingRef::new(self), &RingRef::new(from))
}
default fn map_in_ref(&self, from: &I, el: &<I as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
<J as CanHomFrom<I>>::map_in(self, from, from.clone_el(el), hom)
}
}
impl<I, J> CanIsoFromTo<I> for J
where
I: ?Sized + IntegerRing,
J: ?Sized + IntegerRing,
{
type Isomorphism = ();
fn has_canonical_iso(&self, _: &I) -> Option<Self::Isomorphism> { Some(()) }
fn map_out(&self, from: &I, el: Self::Element, _: &Self::Isomorphism) -> <I as RingBase>::Element {
int_cast(el, &RingRef::new(from), &RingRef::new(self))
}
}
pub trait IntCast<F: ?Sized + IntegerRing>: IntegerRing {
fn cast(&self, from: &F, value: F::Element) -> Self::Element;
}
impl<F: ?Sized + IntegerRing, T: ?Sized + IntegerRing> IntCast<F> for T {
default fn cast(&self, from: &F, value: F::Element) -> Self::Element {
generic_impls::map_from_integer_ring(RingRef::new(from), RingRef::new(self), value)
}
}
pub fn int_cast<T: RingStore, F: RingStore>(value: El<F>, to: T, from: F) -> El<T>
where
T::Type: IntegerRing,
F::Type: IntegerRing,
{
<T::Type as IntCast<F::Type>>::cast(to.get_ring(), from.get_ring(), value)
}
#[stability::unstable(feature = "enable")]
pub fn binomial<I>(n: El<I>, k: &El<I>, ring: I) -> El<I>
where
I: RingStore + Copy,
I::Type: IntegerRing,
{
if ring.is_neg(&n) {
let mut result = binomial(ring.sub(ring.sub_ref_fst(k, n), ring.one()), k, ring);
if !ring.is_even(k) {
ring.negate_inplace(&mut result);
}
return result;
} else if ring.is_neg(k) || ring.is_gt(k, &n) {
return ring.zero();
} else {
let n_neg_k = ring.sub_ref(&n, k);
if ring.is_lt(&n_neg_k, k) {
return binomial(n, &n_neg_k, ring);
}
let mut result = ring.one();
let mut i = ring.one();
while ring.is_leq(&i, k) {
ring.mul_assign(&mut result, ring.sub_ref_snd(ring.add_ref_fst(&n, ring.one()), &i));
result = ring.checked_div(&result, &i).unwrap();
ring.add_assign(&mut i, ring.one());
}
return result;
}
}
#[stability::unstable(feature = "enable")]
pub fn factorial<I>(n: &El<I>, ring: I) -> El<I>
where
I: RingStore + Copy,
I::Type: IntegerRing,
{
let mut current = ring.zero();
let one = ring.one();
return ring.prod((0..).map_while(|_| {
if ring.is_lt(¤t, n) {
ring.add_assign_ref(&mut current, &one);
return Some(ring.clone_el(¤t));
} else {
return None;
}
}));
}
pub trait IntegerRingStore: RingStore
where
Self::Type: IntegerRing,
{
delegate! { IntegerRing, fn to_float_approx(&self, value: &El<Self>) -> f64 }
delegate! { IntegerRing, fn from_float_approx(&self, value: f64) -> Option<El<Self>> }
delegate! { IntegerRing, fn abs_is_bit_set(&self, value: &El<Self>, i: usize) -> bool }
delegate! { IntegerRing, fn abs_highest_set_bit(&self, value: &El<Self>) -> Option<usize> }
delegate! { IntegerRing, fn abs_lowest_set_bit(&self, value: &El<Self>) -> Option<usize> }
delegate! { IntegerRing, fn euclidean_div_pow_2(&self, value: &mut El<Self>, power: usize) -> () }
delegate! { IntegerRing, fn mul_pow_2(&self, value: &mut El<Self>, power: usize) -> () }
delegate! { IntegerRing, fn power_of_two(&self, power: usize) -> El<Self> }
delegate! { IntegerRing, fn rounded_div(&self, lhs: El<Self>, rhs: &El<Self>) -> El<Self> }
delegate! { IntegerRing, fn floor_div(&self, lhs: El<Self>, rhs: &El<Self>) -> El<Self> }
delegate! { IntegerRing, fn ceil_div(&self, lhs: El<Self>, rhs: &El<Self>) -> El<Self> }
delegate! { IntegerRing, fn parse(&self, string: &str, base: u32) -> Result<El<Self>, ()> }
fn get_uniformly_random<G: FnMut() -> u64>(&self, bound_exclusive: &El<Self>, mut rng: G) -> El<Self> {
assert!(self.is_gt(bound_exclusive, &self.zero()));
let log2_ceil_bound = self.abs_highest_set_bit(bound_exclusive).unwrap() + 1;
let mut result = self.get_ring().get_uniformly_random_bits(log2_ceil_bound, &mut rng);
while self.is_geq(&result, bound_exclusive) {
result = self.get_ring().get_uniformly_random_bits(log2_ceil_bound, &mut rng);
}
return result;
}
fn abs_log2_floor(&self, value: &El<Self>) -> Option<usize> { self.abs_highest_set_bit(value) }
fn abs_log2_ceil(&self, value: &El<Self>) -> Option<usize> {
let highest_bit = self.abs_highest_set_bit(value)?;
if self.abs_lowest_set_bit(value).unwrap() == highest_bit {
return Some(highest_bit);
} else {
return Some(highest_bit + 1);
}
}
fn is_even(&self, value: &El<Self>) -> bool { !self.abs_is_bit_set(value, 0) }
fn is_odd(&self, value: &El<Self>) -> bool { !self.is_even(value) }
fn half_exact(&self, mut value: El<Self>) -> El<Self> {
assert!(self.is_even(&value));
self.euclidean_div_pow_2(&mut value, 1);
return value;
}
}
impl<R> IntegerRingStore for R
where
R: RingStore,
R::Type: IntegerRing,
{
}
pub mod generic_impls {
use super::*;
use crate::primitive_int::*;
#[stability::unstable(feature = "enable")]
pub fn map_from_integer_ring<I, R>(from: I, to: R, mut x: El<I>) -> El<R>
where
I: RingStore,
I::Type: IntegerRing,
R: RingStore,
{
let basis = to.int_hom().map(1 << 16);
let is_neg = if from.is_neg(&x) {
from.negate_inplace(&mut x);
true
} else {
false
};
let mut current = to.zero();
let mut current_pow = to.one();
while !from.is_zero(&x) {
let mut quo = from.clone_el(&x);
from.euclidean_div_pow_2(&mut quo, 16);
let mut rem = from.clone_el(&quo);
from.mul_pow_2(&mut rem, 16);
from.sub_self_assign(&mut rem, x);
let rem = int_cast(rem, StaticRing::<i32>::RING, &from);
to.add_assign(&mut current, to.mul_ref_snd(to.int_hom().map(rem), ¤t_pow));
x = quo;
to.mul_assign_ref(&mut current_pow, &basis);
}
if is_neg {
return to.negate(current);
} else {
return current;
}
}
#[stability::unstable(feature = "enable")]
pub fn parse<I>(ring: I, string: &str, base: u32) -> Result<El<I>, ()>
where
I: RingStore,
I::Type: IntegerRing,
{
let (negative, rest) = if string.starts_with('-') {
(true, string.split_at(1).1)
} else if string.starts_with('+') {
(false, string.split_at(1).1)
} else {
(false, string)
};
assert!(base >= 2);
let bits_per_chunk = u32::BITS as usize;
assert!(bits_per_chunk <= i64::BITS as usize - 2);
let chunk_size = (bits_per_chunk as f32 / (base as f32).log2()).floor() as usize;
let it = <str as AsRef<[u8]>>::as_ref(rest)
.rchunks(chunk_size)
.rev()
.map(std::str::from_utf8)
.map(|chunk| chunk.map_err(|_| ()))
.map(|chunk| chunk.and_then(|n| u64::from_str_radix(n, base).map_err(|_| ())));
let chunk_base = ring.pow(int_cast(base as i64, &ring, StaticRing::<i64>::RING), chunk_size);
let result = it.fold(Ok(ring.zero()), |current, next| {
current.and_then(|mut current| {
next.map(|next| {
ring.mul_assign_ref(&mut current, &chunk_base);
ring.add(current, int_cast(next as i64, &ring, StaticRing::<i64>::RING))
})
})
});
if negative {
return result.map(|result| ring.negate(result));
} else {
return result;
}
}
}
#[cfg(test)]
use generic_impls::map_from_integer_ring;
#[cfg(test)]
use crate::primitive_int::*;
#[allow(missing_docs)]
#[cfg(any(test, feature = "generic_tests"))]
pub mod generic_tests {
use super::*;
use crate::ring::El;
pub fn test_integer_get_uniformly_random<R: RingStore>(ring: R)
where
R::Type: IntegerRing,
{
for b in [15, 16] {
let bound = ring.int_hom().map(b);
let mut rng = oorandom::Rand64::new(1);
let elements: Vec<El<R>> = (0..1000)
.map(|_| ring.get_uniformly_random(&bound, || rng.rand_u64()))
.collect();
for i in 0..b {
assert!(elements.iter().any(|x| ring.eq_el(x, &ring.int_hom().map(i))))
}
for x in &elements {
assert!(ring.is_lt(x, &bound));
}
}
}
pub fn test_integer_axioms<R: IntegerRingStore, I: Iterator<Item = El<R>>>(ring: R, edge_case_elements: I)
where
R::Type: IntegerRing,
{
let elements = edge_case_elements.collect::<Vec<_>>();
assert_eq!(None, ring.abs_highest_set_bit(&ring.int_hom().map(0)));
assert_eq!(Some(0), ring.abs_highest_set_bit(&ring.int_hom().map(1)));
assert_eq!(Some(1), ring.abs_highest_set_bit(&ring.int_hom().map(2)));
for a in &elements {
let mut ceil_pow_2 = ring.int_hom().map(2);
ring.mul_pow_2(&mut ceil_pow_2, ring.abs_highest_set_bit(a).unwrap_or(0));
assert!(ring.is_lt(a, &ceil_pow_2));
assert!(ring.is_lt(&ring.negate(ring.clone_el(a)), &ceil_pow_2));
for i in 0..ring.abs_highest_set_bit(a).unwrap_or(0) {
let mut pow_2 = ring.one();
ring.mul_pow_2(&mut pow_2, i);
let mut b = ring.clone_el(a);
ring.mul_pow_2(&mut b, i);
assert_el_eq!(ring, b, ring.mul(ring.clone_el(a), ring.clone_el(&pow_2)));
ring.euclidean_div_pow_2(&mut b, i);
assert_el_eq!(ring, b, a);
ring.euclidean_div_pow_2(&mut b, i);
assert_el_eq!(ring, b, ring.euclidean_div(ring.clone_el(a), &pow_2));
}
}
let d = ring.int_hom().map(8);
for k in -10..=10 {
let mut a = ring.int_hom().map(k);
assert_el_eq!(
ring,
ring.int_hom().map(k / 8),
ring.euclidean_div(ring.clone_el(&a), &d)
);
ring.euclidean_div_pow_2(&mut a, 3);
assert_el_eq!(ring, ring.int_hom().map(k / 8), a);
}
let d = ring.int_hom().map(-8);
for k in -10..=10 {
let a = ring.int_hom().map(k);
assert_el_eq!(
ring,
ring.int_hom().map(k / -8),
ring.euclidean_div(ring.clone_el(&a), &d)
);
}
assert_el_eq!(
ring,
ring.int_hom().map(2),
ring.rounded_div(ring.int_hom().map(7), &ring.int_hom().map(3))
);
assert_el_eq!(
ring,
ring.int_hom().map(-2),
ring.rounded_div(ring.int_hom().map(-7), &ring.int_hom().map(3))
);
assert_el_eq!(
ring,
ring.int_hom().map(-2),
ring.rounded_div(ring.int_hom().map(7), &ring.int_hom().map(-3))
);
assert_el_eq!(
ring,
ring.int_hom().map(2),
ring.rounded_div(ring.int_hom().map(-7), &ring.int_hom().map(-3))
);
assert_el_eq!(
ring,
ring.int_hom().map(3),
ring.rounded_div(ring.int_hom().map(8), &ring.int_hom().map(3))
);
assert_el_eq!(
ring,
ring.int_hom().map(-3),
ring.rounded_div(ring.int_hom().map(-8), &ring.int_hom().map(3))
);
assert_el_eq!(
ring,
ring.int_hom().map(-3),
ring.rounded_div(ring.int_hom().map(8), &ring.int_hom().map(-3))
);
assert_el_eq!(
ring,
ring.int_hom().map(3),
ring.rounded_div(ring.int_hom().map(-8), &ring.int_hom().map(-3))
);
assert_el_eq!(
ring,
ring.int_hom().map(4),
ring.rounded_div(ring.int_hom().map(7), &ring.int_hom().map(2))
);
assert_el_eq!(
ring,
ring.int_hom().map(-4),
ring.rounded_div(ring.int_hom().map(-7), &ring.int_hom().map(2))
);
assert_el_eq!(
ring,
ring.int_hom().map(-4),
ring.rounded_div(ring.int_hom().map(7), &ring.int_hom().map(-2))
);
assert_el_eq!(
ring,
ring.int_hom().map(4),
ring.rounded_div(ring.int_hom().map(-7), &ring.int_hom().map(-2))
);
}
}
#[test]
fn test_int_div_assumption() {
assert_eq!(-1, -10 / 8);
assert_eq!(-1, 10 / -8);
assert_eq!(1, 10 / 8);
assert_eq!(1, -10 / -8);
}
#[test]
fn test_rounded_div() {
let ZZ = StaticRing::<i32>::RING;
assert_el_eq!(ZZ, 3, ZZ.rounded_div(20, &7));
assert_el_eq!(ZZ, -3, ZZ.rounded_div(-20, &7));
assert_el_eq!(ZZ, -3, ZZ.rounded_div(20, &-7));
assert_el_eq!(ZZ, 3, ZZ.rounded_div(-20, &-7));
assert_el_eq!(ZZ, 3, ZZ.rounded_div(22, &7));
assert_el_eq!(ZZ, -3, ZZ.rounded_div(-22, &7));
assert_el_eq!(ZZ, -3, ZZ.rounded_div(22, &-7));
assert_el_eq!(ZZ, 3, ZZ.rounded_div(-22, &-7));
}
#[test]
fn test_binomial() {
let ZZ = StaticRing::<i32>::RING;
assert_eq!(0, binomial(-4, &-1, ZZ));
assert_eq!(1, binomial(-4, &0, ZZ));
assert_eq!(-4, binomial(-4, &1, ZZ));
assert_eq!(10, binomial(-4, &2, ZZ));
assert_eq!(-20, binomial(-4, &3, ZZ));
assert_eq!(35, binomial(-4, &4, ZZ));
assert_eq!(-56, binomial(-4, &5, ZZ));
assert_eq!(0, binomial(3, &-1, ZZ));
assert_eq!(1, binomial(3, &0, ZZ));
assert_eq!(3, binomial(3, &1, ZZ));
assert_eq!(3, binomial(3, &2, ZZ));
assert_eq!(1, binomial(3, &3, ZZ));
assert_eq!(0, binomial(3, &4, ZZ));
assert_eq!(0, binomial(8, &-1, ZZ));
assert_eq!(1, binomial(8, &0, ZZ));
assert_eq!(8, binomial(8, &1, ZZ));
assert_eq!(28, binomial(8, &2, ZZ));
assert_eq!(56, binomial(8, &3, ZZ));
assert_eq!(70, binomial(8, &4, ZZ));
assert_eq!(145422675, binomial(30, &14, ZZ));
}
#[test]
fn test_factorial() {
let ZZ = StaticRing::<i32>::RING;
assert_eq!(1, factorial(&0, ZZ));
assert_eq!(1, factorial(&1, ZZ));
assert_eq!(2, factorial(&2, ZZ));
assert_eq!(6, factorial(&3, ZZ));
assert_eq!(24, factorial(&4, ZZ));
}
#[test]
fn test_ceil_floor_div() {
let ZZ = StaticRing::<i32>::RING;
for rhs in [-10, -3, -2, -1, 1, 2, 3, 10] {
for lhs in [-10, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 10] {
let result = ZZ.ceil_div(lhs, &rhs);
assert_eq!(i32::div_ceil(lhs, rhs), result);
assert_eq!((lhs as f64 / rhs as f64).ceil() as i32, result);
let result = ZZ.floor_div(lhs, &rhs);
assert_eq!(i32::div_floor(lhs, rhs), result);
assert_eq!((lhs as f64 / rhs as f64).floor() as i32, result);
}
}
}
#[test]
fn test_parse() {
let ZZbig = BigIntRing::RING;
assert_el_eq!(&ZZbig, &ZZbig.int_hom().map(3), ZZbig.parse("3", 10).unwrap());
assert_el_eq!(
&ZZbig,
&ZZbig.power_of_two(100),
ZZbig.parse("1267650600228229401496703205376", 10).unwrap()
);
assert_el_eq!(
&ZZbig,
&ZZbig.power_of_two(100),
ZZbig.parse("+1267650600228229401496703205376", 10).unwrap()
);
assert_el_eq!(
&ZZbig,
&ZZbig.negate(ZZbig.power_of_two(100)),
ZZbig.parse("-1267650600228229401496703205376", 10).unwrap()
);
assert_el_eq!(
&ZZbig,
&ZZbig.mul(ZZbig.pow(ZZbig.int_hom().map(11), 26), ZZbig.int_hom().map(10)),
ZZbig.parse("a00000000000000000000000000", 11).unwrap()
);
assert!(ZZbig.parse("238597a", 10).is_err());
}
#[test]
fn test_map_from_integer() {
let ZZbig = BigIntRing::RING;
assert_el_eq!(
&ZZbig,
ZZbig.parse("0", 10).unwrap(),
map_from_integer_ring(ZZbig, ZZbig, ZZbig.parse("0", 10).unwrap())
);
assert_el_eq!(
&ZZbig,
ZZbig.parse("-1", 10).unwrap(),
map_from_integer_ring(ZZbig, ZZbig, ZZbig.parse("-1", 10).unwrap())
);
assert_el_eq!(
&ZZbig,
ZZbig.parse("80934517340527398320561", 10).unwrap(),
map_from_integer_ring(ZZbig, ZZbig, ZZbig.parse("80934517340527398320561", 10).unwrap())
);
assert_el_eq!(
&ZZbig,
ZZbig.parse("-4861235897136598713465987138952643", 10).unwrap(),
map_from_integer_ring(
ZZbig,
ZZbig,
ZZbig.parse("-4861235897136598713465987138952643", 10).unwrap()
)
);
}