perfectset/src/f3.rs
2025-02-15 10:17:13 +01:00

237 lines
4.9 KiB
Rust

use num::traits::{One, Zero};
use rand::distr::{Distribution, StandardUniform};
use rand::Rng;
use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct F3 {
s: i8,
}
impl F3 {
pub const ELEMENTS: [F3; 3] = [F3 { s: 0 }, F3 { s: 1 }, F3 { s: 2 }];
}
impl From<F3> for usize {
fn from(f: F3) -> usize {
f.s as usize
}
}
impl TryFrom<u8> for F3 {
type Error = &'static str;
fn try_from(i: u8) -> Result<Self, Self::Error> {
if i > 2 {
Err("out of range")
} else {
Ok(F3 { s: i as i8 })
}
}
}
impl Add for F3 {
type Output = F3;
fn add(self, rhs: Self) -> Self::Output {
F3 {
s: (self.s + rhs.s).rem_euclid(3),
}
}
}
impl AddAssign for F3 {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl Zero for F3 {
fn zero() -> Self {
F3 { s: 0 }
}
fn is_zero(&self) -> bool {
*self == Self::zero()
}
}
impl Sub for F3 {
type Output = F3;
fn sub(self, rhs: Self) -> Self::Output {
F3 {
s: (self.s - rhs.s).rem_euclid(3),
}
}
}
impl SubAssign for F3 {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl Mul for F3 {
type Output = F3;
fn mul(self, rhs: Self) -> Self::Output {
F3 {
s: (self.s * rhs.s).rem_euclid(3),
}
}
}
impl MulAssign for F3 {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl One for F3 {
fn one() -> Self {
F3 { s: 1 }
}
fn is_one(&self) -> bool {
*self == Self::one()
}
}
impl Distribution<F3> for StandardUniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> F3 {
F3 {
s: rng.random_range(0..3),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_associative() {
for a in F3::ELEMENTS {
for b in F3::ELEMENTS {
for c in F3::ELEMENTS {
assert_eq!((a + b) + c, a + (b + c));
}
}
}
}
#[test]
fn add_identity() {
for a in F3::ELEMENTS {
assert_eq!(F3::zero() + a, a);
assert_eq!(a + F3::zero(), a);
}
}
#[test]
fn add_inverse() {
for a in F3::ELEMENTS {
let mut has_inverse = false;
for b in F3::ELEMENTS {
if a + b == F3::zero() {
assert!(!has_inverse);
has_inverse = true;
}
}
assert!(has_inverse);
}
}
#[test]
fn sub_is_add_inverse() {
for a in F3::ELEMENTS {
for b in F3::ELEMENTS {
assert_eq!(a + b - b, a);
assert_eq!(a - b + b, a);
}
}
}
#[test]
fn add_commutative() {
for a in F3::ELEMENTS {
for b in F3::ELEMENTS {
assert_eq!(a + b, b + a);
}
}
}
#[test]
fn mul_associative() {
for a in F3::ELEMENTS {
for b in F3::ELEMENTS {
for c in F3::ELEMENTS {
assert_eq!((a * b) * c, a * (b * c));
}
}
}
}
#[test]
fn mul_identity() {
for a in F3::ELEMENTS {
assert_eq!(F3::one() * a, a);
assert_eq!(a * F3::one(), a);
}
}
#[test]
fn mul_inverse() {
for a in F3::ELEMENTS {
if !a.is_zero() {
let mut has_inverse = false;
for b in F3::ELEMENTS {
if a * b == F3::one() {
assert!(!has_inverse);
has_inverse = true;
}
}
assert!(has_inverse);
}
}
}
#[test]
fn mul_commutative() {
for a in F3::ELEMENTS {
for b in F3::ELEMENTS {
assert_eq!(a * b, b * a);
}
}
}
#[test]
fn distributive() {
for a in F3::ELEMENTS {
for b in F3::ELEMENTS {
for c in F3::ELEMENTS {
assert_eq!(a * (b + c), a * b + a * c);
}
}
}
}
#[test]
fn add_is_like_set() {
let e = F3::ELEMENTS;
assert_eq!(e[0] + e[0] + e[0], F3::zero());
assert_eq!(e[1] + e[1] + e[1], F3::zero());
assert_eq!(e[2] + e[2] + e[2], F3::zero());
assert_eq!(e[0] + e[1] + e[2], F3::zero());
assert_ne!(e[0] + e[0] + e[1], F3::zero());
assert_ne!(e[0] + e[0] + e[2], F3::zero());
assert_ne!(e[1] + e[1] + e[0], F3::zero());
assert_ne!(e[1] + e[1] + e[2], F3::zero());
assert_ne!(e[2] + e[2] + e[0], F3::zero());
assert_ne!(e[2] + e[2] + e[1], F3::zero());
}
}