use noether::{
AssociativeAddition, AssociativeMultiplication, CommutativeAddition, CommutativeMultiplication,
Distributive, FiniteField,
};
use num_traits::{Euclid, Inv, One, Zero};
use std::fmt::{Debug, Display};
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Fp<const P: u64> {
value: u64,
}
impl<const P: u64> Fp<P> {
pub fn new(value: u64) -> Self {
assert!(P > 1, "P must be at least 2");
Self { value: value % P }
}
pub fn value(&self) -> u64 {
self.value
}
fn inv_value(&self) -> Option<u64> {
if self.value == 0 {
return None;
}
let mut a = self.value as i128;
let mut m = P as i128;
let mut x = 1i128;
let mut y = 0i128;
while a > 1 {
let q = a / m;
let temp = m;
m = a % m;
a = temp;
let temp = y;
y = x - q * y;
x = temp;
}
if x < 0 {
x += P as i128;
}
Some(x as u64)
}
pub fn characteristic() -> u64 {
P
}
pub fn order() -> u64 {
P
}
pub fn pow(&self, exp: u64) -> Self {
if exp == 0 {
return Self::one();
}
let mut result = Self::one();
let mut base = *self;
let mut exp = exp;
while exp > 0 {
if exp % 2 == 1 {
result *= base;
}
base = base * base;
exp /= 2;
}
result
}
}
impl<const P: u64> Add for Fp<P> {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self::new(self.value + rhs.value)
}
}
impl<const P: u64> AddAssign for Fp<P> {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<const P: u64> Sub for Fp<P> {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self::new(self.value + P - rhs.value)
}
}
impl<const P: u64> SubAssign for Fp<P> {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<const P: u64> Mul for Fp<P> {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
Self::new(self.value * rhs.value)
}
}
impl<const P: u64> MulAssign for Fp<P> {
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<const P: u64> Div for Fp<P> {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
if rhs.value == 0 {
panic!("Division by zero in finite field");
}
self * rhs.inv()
}
}
impl<const P: u64> DivAssign for Fp<P> {
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl<const P: u64> Neg for Fp<P> {
type Output = Self;
fn neg(self) -> Self::Output {
if self.value == 0 {
self } else {
Self::new(P - self.value)
}
}
}
impl<const P: u64> Rem for Fp<P> {
type Output = Self;
fn rem(self, rhs: Self) -> Self::Output {
if rhs.value == 0 {
panic!("Remainder by zero in finite field");
}
Self::new(self.value % rhs.value)
}
}
impl<const P: u64> Zero for Fp<P> {
fn zero() -> Self {
Self::new(0)
}
fn is_zero(&self) -> bool {
self.value == 0
}
}
impl<const P: u64> One for Fp<P> {
fn one() -> Self {
Self::new(1)
}
}
impl<const P: u64> Inv for Fp<P> {
type Output = Self;
fn inv(self) -> Self::Output {
if self.value == 0 {
panic!("Attempt to invert zero in finite field");
}
Self::new(self.inv_value().unwrap())
}
}
impl<const P: u64> Euclid for Fp<P> {
fn div_euclid(&self, v: &Self) -> Self {
if v.value == 0 {
panic!("Division by zero in finite field");
}
*self / *v
}
fn rem_euclid(&self, v: &Self) -> Self {
if v.value == 0 {
panic!("Remainder by zero in finite field");
}
*self % *v
}
}
impl<const P: u64> Display for Fp<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
impl<const P: u64> CommutativeAddition for Fp<P> {}
impl<const P: u64> AssociativeAddition for Fp<P> {}
impl<const P: u64> CommutativeMultiplication for Fp<P> {}
impl<const P: u64> AssociativeMultiplication for Fp<P> {}
impl<const P: u64> Distributive for Fp<P> {}
impl<const P: u64> FiniteField for Fp<P> {
type ScalarType = u64;
fn characteristic() -> u64 {
P
}
fn order() -> u64 {
P
}
}
fn verify_field_axioms<const P: u64>(elements: &[Fp<P>]) {
println!("Verifying field axioms for GF({}):", P);
let zero = Fp::<P>::zero();
for &a in elements {
assert_eq!(a + zero, a, "Additive identity failed for {}", a);
}
println!("✓ Additive identity verified");
for &a in elements {
if a != zero {
assert_eq!(a + (-a), zero, "Additive inverse failed for {}", a);
}
}
println!("✓ Additive inverse verified");
let one = Fp::<P>::one();
for &a in elements {
assert_eq!(a * one, a, "Multiplicative identity failed for {}", a);
}
println!("✓ Multiplicative identity verified");
for &a in elements {
if a != zero {
let inv = a.inv();
assert_eq!(a * inv, one, "Multiplicative inverse failed for {}", a);
}
}
println!("✓ Multiplicative inverse verified");
for &a in elements {
for &b in elements {
let sum1 = a + b;
let sum2 = b + a;
assert_eq!(
sum1, sum2,
"Additive commutativity failed for {} and {}",
a, b
);
assert_eq!(
a * b,
b * a,
"Multiplicative commutativity failed for {} and {}",
a,
b
);
}
}
println!("✓ Commutativity verified");
let test_count = if elements.len() <= 5 {
elements.len()
} else {
5
};
for &a in elements.iter().take(test_count) {
for &b in elements.iter().take(test_count) {
for &c in elements.iter().take(test_count) {
assert_eq!((a + b) + c, a + (b + c), "Additive associativity failed");
assert_eq!(
(a * b) * c,
a * (b * c),
"Multiplicative associativity failed"
);
}
}
}
println!("✓ Associativity verified (sampled)");
for &a in elements.iter().take(test_count) {
for &b in elements.iter().take(test_count) {
for &c in elements.iter().take(test_count) {
assert_eq!(
a * (b + c),
(a * b) + (a * c),
"Distributivity failed for {}, {}, {}",
a,
b,
c
);
}
}
}
println!("✓ Distributivity verified (sampled)");
}
fn print_multiplication_table<const P: u64>() {
println!("\nMultiplication Table for GF({}):", P);
print!("× | ");
for i in 0..P {
print!("{:2} ", i);
}
println!("\n--+-{}", "-".repeat(3 * P as usize));
for i in 0..P {
print!("{:1} | ", i);
for j in 0..P {
let product = Fp::<P>::new(i) * Fp::<P>::new(j);
print!("{:2} ", product.value());
}
println!();
}
println!();
}
fn diffie_hellman<const P: u64>(g: Fp<P>, a: u64, b: u64) -> (Fp<P>, Fp<P>, Fp<P>) {
let alice_public = g.pow(a);
let bob_public = g.pow(b);
let alice_secret = bob_public.pow(a);
let bob_secret = alice_public.pow(b);
assert_eq!(alice_secret, bob_secret, "Shared secrets must match");
(alice_public, bob_public, alice_secret)
}
fn main() {
println!("Finite Field Operations Example");
println!("===============================\n");
const P7: u64 = 7;
println!("Example 1: Operations in GF({})", P7);
let elements_gf7: Vec<Fp<P7>> = (0..P7).map(Fp::<P7>::new).collect();
verify_field_axioms(&elements_gf7);
print_multiplication_table::<P7>();
println!("\nDemonstrating field properties:");
println!("Field characteristic: {}", P7); println!("Field order: {}", P7);
let a = Fp::<P7>::new(3);
let b = Fp::<P7>::new(5);
println!("Let a = {} and b = {} in GF({})", a, b, P7);
println!("a + b = {}", a + b);
println!("a - b = {}", a - b);
println!("a * b = {}", a * b);
println!("a / b = {}", a / b);
println!("-a = {}", -a);
println!("a^{} = {}", 3, a.pow(3));
println!("1/a = {}", a.inv());
println!("\nExample 2: Diffie-Hellman Key Exchange in GF({})", P7);
let g = Fp::<P7>::new(3); let alice_private = 4; let bob_private = 2;
let (alice_public, bob_public, shared_secret) = diffie_hellman(g, alice_private, bob_private);
println!("Generator g = {}", g);
println!("Alice's private key = {}", alice_private);
println!("Bob's private key = {}", bob_private);
println!("Alice's public key = g^a = {}", alice_public);
println!("Bob's public key = g^b = {}", bob_public);
println!("Shared secret = g^(ab) = {}", shared_secret);
const P17: u64 = 17;
println!("\nExample 3: Operations in GF({})", P17);
let c = Fp::<P17>::new(7);
let d = Fp::<P17>::new(11);
println!("Let c = {} and d = {} in GF({})", c, d, P17);
println!("c + d = {}", c + d);
println!("c - d = {}", c - d);
println!("c * d = {}", c * d);
println!("c / d = {}", c / d);
println!("-c = {}", -c);
println!("c^{} = {}", 5, c.pow(5));
println!("1/c = {}", c.inv());
println!("\nFinding a primitive element (generator) in GF({}):", P17);
let mut found_generator = false;
for i in 2..P17 {
let g = Fp::<P17>::new(i);
let mut powers = vec![Fp::<P17>::one()];
let mut current = g;
for _ in 1..(P17 - 1) {
powers.push(current);
current *= g;
}
if powers.len() == (P17 - 1) as usize {
let mut unique_powers = powers.clone();
unique_powers.sort_by_key(|e| e.value());
unique_powers.dedup();
if unique_powers.len() == (P17 - 1) as usize {
println!("Found generator {} in GF({})!", i, P17);
found_generator = true;
break;
}
}
}
if !found_generator {
println!("No generator found in GF({}).", P17);
}
println!("\nExample 4: Discrete Logarithm in GF({}):", P7);
let g = Fp::<P7>::new(3);
let y = Fp::<P7>::new(6);
let mut found_x = false;
for x in 1..P7 {
if g.pow(x) == y {
println!("Found discrete logarithm: {}^{} = {} (mod {})", g, x, y, P7);
found_x = true;
break;
}
}
if !found_x {
println!("No solution found for discrete logarithm.");
}
println!("\nThis example demonstrates how to implement and work with finite fields");
println!("using the algebraic structure traits provided by the Noether library.");
}