use num_rational::Ratio;
use std::fmt::Debug;
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
pub trait Ring:
Sized
+ Clone
+ PartialEq
+ Debug
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Neg<Output = Self>
{
fn zero() -> Self;
fn one() -> Self;
fn is_zero(&self) -> bool;
fn is_one(&self) -> bool;
#[inline(always)]
fn wrapping_add(&self, other: &Self) -> Self {
self.clone() + other.clone()
}
#[inline(always)]
fn wrapping_sub(&self, other: &Self) -> Self {
self.clone() - other.clone()
}
#[inline(always)]
fn wrapping_mul(&self, other: &Self) -> Self {
self.clone() * other.clone()
}
}
pub trait EuclideanDomain: Ring + Div<Output = Self> + Rem<Output = Self> {
fn div_rem(&self, other: &Self) -> (Self, Self);
fn gcd(&self, other: &Self) -> Self {
let mut a = self.clone();
let mut b = other.clone();
while !b.is_zero() {
let (_, r) = a.div_rem(&b);
a = b;
b = r;
}
a
}
fn abs(&self) -> Self;
}
pub trait Field: EuclideanDomain {
fn inv(&self) -> Option<Self>;
}
impl Ring for i64 {
#[inline(always)]
fn zero() -> Self {
0
}
#[inline(always)]
fn one() -> Self {
1
}
#[inline(always)]
fn is_zero(&self) -> bool {
*self == 0
}
#[inline(always)]
fn is_one(&self) -> bool {
*self == 1
}
#[inline(always)]
fn wrapping_add(&self, other: &Self) -> Self {
i64::wrapping_add(*self, *other)
}
#[inline(always)]
fn wrapping_sub(&self, other: &Self) -> Self {
i64::wrapping_sub(*self, *other)
}
#[inline(always)]
fn wrapping_mul(&self, other: &Self) -> Self {
i64::wrapping_mul(*self, *other)
}
}
impl EuclideanDomain for i64 {
#[inline(always)]
fn div_rem(&self, other: &Self) -> (Self, Self) {
(*self / *other, *self % *other)
}
#[inline(always)]
fn abs(&self) -> Self {
i64::abs(*self)
}
}
impl Ring for Ratio<i64> {
#[inline(always)]
fn zero() -> Self {
Ratio::new(0, 1)
}
#[inline(always)]
fn one() -> Self {
Ratio::new(1, 1)
}
#[inline(always)]
fn is_zero(&self) -> bool {
self.numer() == &0
}
#[inline(always)]
fn is_one(&self) -> bool {
self.numer() == &1 && self.denom() == &1
}
}
impl EuclideanDomain for Ratio<i64> {
#[inline(always)]
fn div_rem(&self, other: &Self) -> (Self, Self) {
(self / other, Ratio::zero())
}
#[inline(always)]
fn abs(&self) -> Self {
Ratio::new(self.numer().abs(), *self.denom())
}
}
impl Field for Ratio<i64> {
#[inline]
fn inv(&self) -> Option<Self> {
if self.is_zero() {
None
} else {
Some(self.recip())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i64_ring_properties() {
assert_eq!(i64::zero(), 0);
assert_eq!(i64::one(), 1);
assert!(0i64.is_zero());
assert!(1i64.is_one());
assert!(!5i64.is_zero());
assert!(!5i64.is_one());
}
#[test]
fn test_i64_euclidean_domain() {
let (q, r) = 17i64.div_rem(&5);
assert_eq!(q, 3);
assert_eq!(r, 2);
assert_eq!(17, 3 * 5 + 2);
}
#[test]
fn test_i64_gcd() {
assert_eq!(12i64.gcd(&18), 6);
assert_eq!(17i64.gcd(&19), 1);
assert_eq!(0i64.gcd(&5), 5);
assert_eq!(5i64.gcd(&0), 5);
}
#[test]
fn test_i64_abs() {
assert_eq!((-5i64).abs(), 5);
assert_eq!(5i64.abs(), 5);
assert_eq!(0i64.abs(), 0);
}
#[test]
fn test_wrapping_arithmetic() {
let a = i64::MAX;
let b = 1i64;
let sum = Ring::wrapping_add(&a, &b);
assert_eq!(sum, i64::MIN);
}
#[test]
fn test_ratio_ring_properties() {
assert_eq!(Ratio::<i64>::zero(), Ratio::new(0, 1));
assert_eq!(Ratio::<i64>::one(), Ratio::new(1, 1));
assert!(Ratio::new(0, 1).is_zero());
assert!(Ratio::new(1, 1).is_one());
assert!(!Ratio::new(5, 1).is_zero());
assert!(!Ratio::new(5, 1).is_one());
}
#[test]
fn test_ratio_arithmetic() {
let a = Ratio::new(1, 2);
let b = Ratio::new(1, 3);
let sum = a + b;
assert_eq!(sum, Ratio::new(5, 6));
let diff = a - b;
assert_eq!(diff, Ratio::new(1, 6));
let prod = a * b;
assert_eq!(prod, Ratio::new(1, 6));
let quot = a / b;
assert_eq!(quot, Ratio::new(3, 2));
}
#[test]
fn test_ratio_euclidean_domain() {
let a = Ratio::new(5, 2);
let b = Ratio::new(3, 4);
let (q, r) = a.div_rem(&b);
assert_eq!(q, Ratio::new(10, 3));
assert_eq!(r, Ratio::zero());
}
#[test]
fn test_ratio_abs() {
assert_eq!(Ratio::new(-5, 2).abs(), Ratio::new(5, 2));
assert_eq!(Ratio::new(5, 2).abs(), Ratio::new(5, 2));
assert_eq!(Ratio::new(0, 1).abs(), Ratio::new(0, 1));
}
#[test]
fn test_ratio_field() {
let a = Ratio::new(2, 3);
let inv = a.inv().unwrap();
assert_eq!(inv, Ratio::new(3, 2));
assert_eq!(a * inv, Ratio::one());
assert!(Ratio::zero().inv().is_none());
}
}