use crate::*;
use num::rational::Ratio;
pub const QQ: ReducedFractions<Integers> = ReducedFractions { base: Integers() };
#[derive(Clone, Debug)]
pub struct ReducedFractions<A>
where
A: EuclideanDomain,
{
base: A,
}
impl<A> ReducedFractions<A>
where
A: EuclideanDomain,
{
pub fn new(base: A) -> Self {
assert!(!base.is_zero(&base.one()));
Self { base }
}
pub fn base(&self) -> &A {
&self.base
}
pub fn reduce(&self, elem: &Ratio<A::Elem>) -> Ratio<A::Elem> {
assert!(!self.base.is_zero(elem.denom()));
let gcd = self.base.gcd(elem.numer(), elem.denom());
let numer = self.base.quo(elem.numer(), &gcd);
let denom = self.base.quo(elem.denom(), &gcd);
let coef = self.base.associate_coef(&denom);
let (numer, denom) = if !self.base.is_one(&coef) {
(self.base.mul(&numer, &coef), self.base.mul(&denom, &coef))
} else {
(numer, denom)
};
Ratio::new_raw(numer, denom)
}
}
impl<A> Domain for ReducedFractions<A>
where
A: EuclideanDomain,
{
type Elem = Ratio<A::Elem>;
fn contains(&self, elem: &Self::Elem) -> bool {
self.base.relative_primes(elem.numer(), elem.denom())
&& self.base.is_one(&self.base.associate_coef(elem.denom()))
}
fn equals(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> bool {
self.base.equals(elem1.numer(), elem2.numer())
&& self.base.equals(elem1.denom(), elem2.denom())
}
}
impl<A> Semigroup for ReducedFractions<A>
where
A: EuclideanDomain,
{
fn mul(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Self::Elem {
let elem = Self::Elem::new_raw(
self.base.mul(elem1.numer(), elem2.numer()),
self.base.mul(elem1.denom(), elem2.denom()),
);
self.reduce(&elem)
}
}
impl<A> Monoid for ReducedFractions<A>
where
A: EuclideanDomain,
{
fn one(&self) -> Self::Elem {
Self::Elem::new_raw(self.base.one(), self.base.one())
}
fn is_one(&self, elem: &Self::Elem) -> bool {
self.base.is_one(elem.numer()) && self.base.is_one(elem.denom())
}
fn try_inv(&self, elem: &Self::Elem) -> Option<Self::Elem> {
if self.invertible(elem) {
let elem = Self::Elem::new_raw(elem.denom().clone(), elem.numer().clone());
Some(self.reduce(&elem))
} else {
None
}
}
fn invertible(&self, elem: &Self::Elem) -> bool {
!self.base.is_zero(elem.numer())
}
}
impl<A> AbelianGroup for ReducedFractions<A>
where
A: EuclideanDomain,
{
fn zero(&self) -> Self::Elem {
Self::Elem::new_raw(self.base.zero(), self.base.one())
}
fn neg(&self, elem: &Self::Elem) -> Self::Elem {
Self::Elem::new_raw(self.base.neg(elem.numer()), elem.denom().clone())
}
fn add(&self, elem1: &Self::Elem, elem2: &Self::Elem) -> Self::Elem {
let elem = if self.base.equals(elem1.denom(), elem2.denom()) {
Self::Elem::new_raw(
self.base.add(elem1.numer(), elem2.numer()),
elem1.denom().clone(),
)
} else {
Self::Elem::new_raw(
self.base.add(
&self.base.mul(elem1.numer(), elem2.denom()),
&self.base.mul(elem1.denom(), elem2.numer()),
),
self.base.mul(elem1.denom(), elem2.denom()),
)
};
self.reduce(&elem)
}
fn times(&self, num: isize, elem: &Self::Elem) -> Self::Elem {
let elem = Self::Elem::new_raw(self.base.times(num, elem.numer()), elem.denom().clone());
self.reduce(&elem)
}
}
impl<A> UnitaryRing for ReducedFractions<A> where A: EuclideanDomain {}
impl<A> Field for ReducedFractions<A>
where
A: EuclideanDomain,
{
fn inv(&self, elem: &Self::Elem) -> Self::Elem {
assert!(!self.base.is_zero(elem.numer()));
let numer = elem.denom();
let denom = elem.numer();
let coef = self.base.associate_coef(denom);
let (numer, denom) = if !self.base.is_one(&coef) {
(self.base.mul(numer, &coef), self.base.mul(denom, &coef))
} else {
(numer.clone(), denom.clone())
};
Ratio::new_raw(numer, denom)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ops() {
let field = ReducedFractions::new(I32);
for numer in -20..20 {
for denom in -20..20 {
if denom == 0 {
continue;
}
let elem1 = Ratio::<i32>::new_raw(numer, denom);
assert_eq!(*elem1.numer(), numer);
assert_eq!(*elem1.denom(), denom);
let elem1 = field.reduce(&elem1);
let elem2 = Ratio::<i32>::new(numer, denom);
assert_eq!(elem1, elem2);
let elem2 = Ratio::<i32>::new(-2, 3);
assert_eq!(elem1 + elem2, field.add(&elem1, &elem2));
assert_eq!(elem1 * elem2, field.mul(&elem1, &elem2));
assert_eq!(elem1 / elem2, field.div(&elem1, &elem2));
if numer != 0 {
assert_eq!(elem2 / elem1, field.div(&elem2, &elem1));
}
let elem2 = Ratio::<i32>::new(7, 5);
assert_eq!(elem1 + elem2, field.add(&elem1, &elem2));
assert_eq!(elem1 * elem2, field.mul(&elem1, &elem2));
assert_eq!(elem1 / elem2, field.div(&elem1, &elem2));
if numer != 0 {
assert_eq!(elem2 / elem1, field.div(&elem2, &elem1));
}
}
}
}
}