use crate::*;
use num::{PrimInt, Signed};
use std::convert::{From, TryFrom, TryInto};
use std::fmt::Debug;
use std::marker::PhantomData;
pub const I8: CheckedInts<i8> = CheckedInts {
phantom: PhantomData,
};
pub const I16: CheckedInts<i16> = CheckedInts {
phantom: PhantomData,
};
pub const I32: CheckedInts<i32> = CheckedInts {
phantom: PhantomData,
};
pub const I64: CheckedInts<i64> = CheckedInts {
phantom: PhantomData,
};
#[derive(Clone, Debug)]
pub struct CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
phantom: PhantomData<E>,
}
impl<E> Domain for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
type Elem = E;
fn equals(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> bool {
elem1 == elem2
}
}
impl<E> Semigroup for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
fn mul(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Self::Elem {
elem1.checked_mul(elem2).unwrap()
}
}
impl<E> Monoid for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
fn one(&self) -> Self::Elem {
1.into()
}
fn is_one(&self, elem: &Self::Elem) -> bool {
*elem == 1.into()
}
fn try_inv(&self, elem: &Self::Elem) -> Option<Self::Elem> {
if self.invertible(elem) {
Some(*elem)
} else {
None
}
}
fn invertible(&self, elem: &Self::Elem) -> bool {
*elem == 1.into() || *elem == (-1).into()
}
}
impl<E> AbelianGroup for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
fn zero(&self) -> Self::Elem {
0.into()
}
fn is_zero(&self, elem: &Self::Elem) -> bool {
elem.is_zero()
}
fn neg(&self, elem: &Self::Elem) -> Self::Elem {
self.zero().checked_sub(elem).unwrap()
}
fn add(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Self::Elem {
elem1.checked_add(elem2).unwrap()
}
fn sub(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Self::Elem {
elem1.checked_sub(elem2).unwrap()
}
fn times(&self, num: isize, elem: &Self::Elem) -> Self::Elem {
let num: Option<Self::Elem> = num.try_into().ok();
elem.checked_mul(&num.expect("too large value")).unwrap()
}
}
impl<E> UnitaryRing for CheckedInts<E> where E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize> {}
impl<E> IntegralDomain for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
fn try_div(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Option<Self::Elem> {
assert!(*elem2 != 0.into());
let quo = elem1.checked_div(elem2).unwrap();
if *elem1 == quo * *elem2 {
Some(quo)
} else {
None
}
}
fn associate_repr(&self, elem: &Self::Elem) -> Self::Elem {
if *elem < 0.into() {
self.neg(elem)
} else {
*elem
}
}
fn associate_coef(&self, elem: &Self::Elem) -> Self::Elem {
assert!(*elem != 0.into());
if *elem < 0.into() {
(-1).into()
} else {
1.into()
}
}
}
impl<E> EuclideanDomain for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
fn quo_rem(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> (Self::Elem, Self::Elem) {
assert!(*elem2 != 0.into());
let quo = elem1.checked_div(elem2).unwrap();
let rem = *elem1 - quo * *elem2;
if rem < 0.into() {
if *elem2 < 0.into() {
(quo + 1.into(), rem - *elem2)
} else {
(quo - 1.into(), rem + *elem2)
}
} else {
(quo, rem)
}
}
fn reduced(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> bool {
if *elem2 == 0.into() {
true
} else if *elem1 < 0.into() {
false
} else if *elem2 >= 0.into() {
*elem1 < *elem2
} else {
-*elem1 > *elem2
}
}
}
impl<E> PartialOrder for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
fn leq(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> bool {
*elem1 <= *elem2
}
}
impl<E> Lattice for CheckedInts<E>
where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>,
{
fn meet(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Self::Elem {
*elem1.min(elem2)
}
fn join(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Self::Elem {
*elem1.max(elem2)
}
}
impl<E> DistributiveLattice for CheckedInts<E> where
E: PrimInt + Signed + Debug + From<i8> + TryFrom<isize>
{
}
#[cfg(test)]
mod tests {
use super::*;
use num::BigInt;
#[test]
fn quo_rem() {
let ring1 = I32;
let ring2 = Integers();
let mut elems: Vec<i32> = Default::default();
for i in 0..10 {
elems.push(i);
if i > 0 {
elems.push(-i);
}
elems.push(i32::MIN + i);
elems.push(i32::MAX - i);
}
let min2: BigInt = i32::MIN.into();
let max2: BigInt = i32::MAX.into();
for &n1 in elems.iter() {
let n2: BigInt = n1.into();
for &m1 in elems.iter() {
if m1 == 0 {
continue;
}
let m2: BigInt = m1.into();
let (q2, r2) = ring2.quo_rem(&n2, &m2);
if min2 <= q2 && q2 <= max2 && min2 <= r2 && r2 <= max2 {
let (q1, r1) = ring1.quo_rem(&n1, &m1);
println!(
"n1={}, m1={}, q1={}, r1={}, q2={}, r2={}",
n1, m1, q1, r1, q2, r2
);
assert_eq!(q2, q1.into());
assert_eq!(r2, r1.into());
assert_eq!(ring1.divisible(&n1, &m1), ring2.divisible(&n2, &m2));
assert_eq!(ring1.reduced(&n1, &m1), ring2.reduced(&n2, &m2));
} else {
let result = std::panic::catch_unwind(|| {
ring1.quo_rem(&n1, &m1);
});
assert!(result.is_err());
}
}
}
}
#[test]
fn extended_gcd() {
let ring = I32;
for a in -10..10 {
for b in -10..10 {
let (g, x, y) = ring.extended_gcd(&a, &b);
println!("a:{}, b:{}, g:{}, x:{}, y:{}", a, b, g, x, y);
assert_eq!(g, a * x + b * y);
assert_eq!(g, ring.gcd(&a, &b));
assert!(ring.divisible(&a, &g));
assert!(ring.divisible(&b, &g));
}
}
}
}