use crate::groups::MultiplicativeAbelianGroup;
use crate::rings::PrincipalIdealDomain;
use crate::spaces::VectorSpace;
use num_traits::{Euclid, One, Zero};
pub trait EuclideanDomain: PrincipalIdealDomain + Euclid {}
pub trait Field: EuclideanDomain + MultiplicativeAbelianGroup {}
pub trait FiniteField: Field {
type ScalarType: Clone + PartialOrd + Zero + One;
fn characteristic() -> u64;
fn order() -> u64;
}
pub trait OrderedField: Field + PartialOrd {}
pub trait RealField: OrderedField {}
pub trait FieldExtension: Field + VectorSpace<Self::BaseField> {
type BaseField: Field;
fn degree() -> usize;
fn trace(&self) -> Self::BaseField;
fn norm(&self) -> Self::BaseField;
}
pub trait FieldExtensionTower: FieldExtension {
fn tower_height() -> usize;
fn extension_degree(i: usize) -> usize;
}
impl<T: PrincipalIdealDomain + Euclid> EuclideanDomain for T {}
impl<T: EuclideanDomain + MultiplicativeAbelianGroup> Field for T {}
impl<T: Field + PartialOrd> OrderedField for T {}
#[cfg(test)]
mod tests {
use super::*;
use crate::operations::{
AssociativeAddition, AssociativeMultiplication, CommutativeAddition,
CommutativeMultiplication, Distributive,
};
use num_traits::{Inv, One, Zero};
use std::cmp::Ordering;
use std::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct GF5(u8);
impl GF5 {
fn new(value: u8) -> Self {
Self(value % 5)
}
}
impl Add for GF5 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.0 + rhs.0)
}
}
impl AddAssign for GF5 {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Sub for GF5 {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.0 + 5 - rhs.0)
}
}
impl SubAssign for GF5 {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Neg for GF5 {
type Output = Self;
fn neg(self) -> Self::Output {
if self.0 == 0 {
Self(0) } else {
Self(5 - self.0)
}
}
}
impl Zero for GF5 {
fn zero() -> Self {
Self(0)
}
fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl Mul for GF5 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.0 * rhs.0)
}
}
impl MulAssign for GF5 {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl One for GF5 {
fn one() -> Self {
Self(1)
}
}
impl Inv for GF5 {
type Output = Self;
fn inv(self) -> Self::Output {
match self.0 {
0 => panic!("Division by zero in GF(5)"),
1 => Self(1), 2 => Self(3), 3 => Self(2), 4 => Self(4), _ => unreachable!("All values should be normalized to 0-4"),
}
}
}
impl Div for GF5 {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
if rhs.0 == 0 {
panic!("Division by zero in GF(5)");
}
self * rhs.inv()
}
}
impl DivAssign for GF5 {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl Rem for GF5 {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
if rhs.0 == 0 {
panic!("Remainder by zero in GF(5)");
}
Self(self.0 % rhs.0)
}
}
impl RemAssign for GF5 {
fn rem_assign(&mut self, rhs: Self) {
if rhs.0 == 0 {
panic!("Remainder by zero in GF(5)");
}
*self = Self(self.0 % rhs.0);
}
}
impl Euclid for GF5 {
fn div_euclid(&self, v: &Self) -> Self {
if v.0 == 0 {
panic!("Division by zero in GF(5)");
}
*self / *v
}
fn rem_euclid(&self, v: &Self) -> Self {
if v.0 == 0 {
panic!("Remainder by zero in GF(5)");
}
Self(self.0 % v.0)
}
}
impl CommutativeAddition for GF5 {}
impl AssociativeAddition for GF5 {}
impl CommutativeMultiplication for GF5 {}
impl AssociativeMultiplication for GF5 {}
impl Distributive for GF5 {}
impl PartialOrd for GF5 {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl FiniteField for GF5 {
type ScalarType = u8;
fn characteristic() -> u64 {
5
}
fn order() -> u64 {
5
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
struct Rational {
num: i32,
den: i32,
}
impl Rational {
fn new(num: i32, den: i32) -> Self {
if den == 0 {
panic!("Denominator cannot be zero");
}
let (num, den) = if den < 0 { (-num, -den) } else { (num, den) };
let gcd = Self::gcd(num.abs(), den);
Self {
num: num / gcd,
den: den / gcd,
}
}
fn gcd(a: i32, b: i32) -> i32 {
if b == 0 {
a
} else {
Self::gcd(b, a % b)
}
}
}
impl Add for Rational {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.num * rhs.den + rhs.num * self.den, self.den * rhs.den)
}
}
impl AddAssign for Rational {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Sub for Rational {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.num * rhs.den - rhs.num * self.den, self.den * rhs.den)
}
}
impl SubAssign for Rational {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Neg for Rational {
type Output = Self;
fn neg(self) -> Self::Output {
Self::new(-self.num, self.den)
}
}
impl Zero for Rational {
fn zero() -> Self {
Self::new(0, 1)
}
fn is_zero(&self) -> bool {
self.num == 0
}
}
impl Mul for Rational {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.num * rhs.num, self.den * rhs.den)
}
}
impl MulAssign for Rational {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl One for Rational {
fn one() -> Self {
Self::new(1, 1)
}
}
impl Inv for Rational {
type Output = Self;
fn inv(self) -> Self::Output {
if self.num == 0 {
panic!("Division by zero");
}
Self::new(self.den, self.num)
}
}
impl Div for Rational {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
if rhs.num == 0 {
panic!("Division by zero");
}
Self::new(self.num * rhs.den, self.den * rhs.num)
}
}
impl DivAssign for Rational {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl Rem for Rational {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
if rhs.num == 0 {
panic!("Remainder by zero");
}
let quotient = (self.num as f64 * rhs.den as f64) / (self.den as f64 * rhs.num as f64);
let whole_part = Self::new(quotient.floor() as i32, 1);
self - whole_part * rhs
}
}
impl RemAssign for Rational {
fn rem_assign(&mut self, rhs: Self) {
*self = *self % rhs;
}
}
impl Euclid for Rational {
fn div_euclid(&self, v: &Self) -> Self {
if v.num == 0 {
panic!("Division by zero");
}
let quotient = (self.num as f64 * v.den as f64) / (self.den as f64 * v.num as f64);
Self::new(quotient.floor() as i32, 1)
}
fn rem_euclid(&self, v: &Self) -> Self {
if v.num == 0 {
panic!("Remainder by zero");
}
let quotient = self.div_euclid(v);
*self - quotient * *v
}
}
impl PartialOrd for Rational {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let left = self.num as i64 * other.den as i64;
let right = self.den as i64 * other.num as i64;
left.partial_cmp(&right)
}
}
impl CommutativeAddition for Rational {}
impl AssociativeAddition for Rational {}
impl CommutativeMultiplication for Rational {}
impl AssociativeMultiplication for Rational {}
impl Distributive for Rational {}
#[test]
fn test_euclidean_domain() {
fn assert_is_euclidean_domain<T: EuclideanDomain>(_: &T) {}
assert_is_euclidean_domain(&GF5::new(2));
assert_is_euclidean_domain(&Rational::new(3, 4));
assert_is_euclidean_domain(&std::f64::consts::PI);
let a = GF5::new(7); let b = GF5::new(3);
assert_eq!(a.div_euclid(&b), GF5::new(4));
let _ = a.rem_euclid(&b);
let p = Rational::new(7, 2); let q = Rational::new(2, 1);
assert_eq!(p.div_euclid(&q), Rational::new(1, 1));
assert_eq!(p.rem_euclid(&q), Rational::new(3, 2));
}
#[test]
fn test_field() {
fn assert_is_field<T: Field>(_: &T) {}
assert_is_field(&GF5::new(2));
assert_is_field(&Rational::new(3, 4));
assert_is_field(&std::f64::consts::PI);
let a = GF5::new(2);
let b = GF5::new(3);
assert_eq!(a + b, b + a);
assert_eq!(a * b, b * a);
let c = GF5::new(4);
assert_eq!(a * (b + c), a * b + a * c);
assert_eq!(a * a.inv(), GF5::one());
assert_eq!(b * b.inv(), GF5::one());
assert_eq!(a / b, a * b.inv());
let p = Rational::new(3, 4); let q = Rational::new(2, 5);
assert_eq!(p + q, q + p);
assert_eq!(p * q, q * p);
let r = Rational::new(1, 2); assert_eq!(p * (q + r), p * q + p * r);
assert_eq!(p * p.inv(), Rational::one());
assert_eq!(q * q.inv(), Rational::one());
assert_eq!(p / q, p * q.inv());
}
#[test]
fn test_finite_field() {
fn assert_is_finite_field<T: FiniteField>(_: &T) {}
assert_is_finite_field(&GF5::new(2));
assert_eq!(GF5::characteristic(), 5);
assert_eq!(GF5::order(), 5);
let elements: Vec<GF5> = (0..5).map(GF5::new).collect();
for &a in &elements {
for &b in &elements {
if !b.is_zero() {
let sum = a + b;
let product = a * b;
let quotient = a / b;
assert!(elements.contains(&sum));
assert!(elements.contains(&product));
assert!(elements.contains("ient));
}
}
}
let mut seen_inverses = Vec::new();
for &a in &elements {
if !a.is_zero() {
let a_inv = a.inv();
assert!(!seen_inverses.contains(&a_inv));
seen_inverses.push(a_inv);
assert_eq!(a * a_inv, GF5::one());
}
}
}
#[test]
fn test_ordered_field() {
fn assert_is_ordered_field<T: OrderedField>(_: &T) {}
assert_is_ordered_field(&Rational::new(3, 4));
assert_is_ordered_field(&std::f64::consts::PI);
let a = Rational::new(3, 4); let b = Rational::new(2, 3); let c = Rational::new(1, 2);
assert!(a > b && b > c);
assert!(a > c);
let d = Rational::new(1, 10); assert!(a > b);
assert!(a + d > b + d);
let pos = Rational::new(2, 1); assert!(a > c);
assert!(a * pos > c * pos);
let neg = Rational::new(-2, 1); assert!(a * neg < c * neg); }
#[test]
fn test_field_axioms() {
let elements: Vec<GF5> = (0..5).map(GF5::new).collect();
let non_zero: Vec<GF5> = elements.iter().filter(|e| !e.is_zero()).cloned().collect();
let zero = GF5::zero();
let one = GF5::one();
for &a in &elements {
assert_eq!(a + zero, a);
}
for &a in &elements {
assert_eq!(a + (-a), zero);
}
for &a in &elements {
assert_eq!(a * one, a);
}
for &a in &non_zero {
assert_eq!(a * a.inv(), one);
}
for &a in &elements {
for &b in &elements {
for &c in &elements {
assert_eq!((a + b) + c, a + (b + c));
}
}
}
for &a in &elements {
for &b in &elements {
assert_eq!(a + b, b + a);
}
}
for &a in &elements {
for &b in &elements {
for &c in &elements {
assert_eq!((a * b) * c, a * (b * c));
}
}
}
for &a in &elements {
for &b in &elements {
assert_eq!(a * b, b * a);
}
}
for &a in &elements {
for &b in &elements {
for &c in &elements {
assert_eq!(a * (b + c), a * b + a * c);
}
}
}
}
}