use crate::domain::{Domain, EuclideanDomain};
use crate::rational::RationalDomain;
type PolyCoeffs<D> = Vec<<D as Domain>::Element>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AlgebraicElement<E> {
coeffs: Vec<E>,
}
impl<E> AlgebraicElement<E> {
pub fn coeffs(&self) -> &[E] {
&self.coeffs
}
}
impl<E: std::fmt::Display> std::fmt::Display for AlgebraicElement<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.coeffs.is_empty() {
return write!(f, "0");
}
for (i, c) in self.coeffs.iter().enumerate().rev() {
if i < self.coeffs.len() - 1 {
write!(f, " + ")?;
}
match i {
0 => write!(f, "{c}")?,
1 => write!(f, "({c})·α")?,
_ => write!(f, "({c})·α^{i}")?,
}
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AlgebraicExtension<D: Domain> {
base: D,
min_poly: Vec<D::Element>,
}
impl<D: Domain> AlgebraicExtension<D> {
pub fn new(base: D, min_poly: Vec<D::Element>) -> Self {
debug_assert!(
min_poly.len() >= 2,
"minimal polynomial must have degree at least 1"
);
debug_assert!(
min_poly.last() == Some(&base.one()),
"minimal polynomial must be monic"
);
Self { base, min_poly }
}
pub fn base_domain(&self) -> &D {
&self.base
}
pub fn min_poly(&self) -> &[D::Element] {
&self.min_poly
}
pub fn extension_degree(&self) -> usize {
self.min_poly.len() - 1
}
pub fn from_base(&self, c: D::Element) -> AlgebraicElement<D::Element> {
let mut coeffs = vec![c];
self.trim(&mut coeffs);
AlgebraicElement { coeffs }
}
pub fn alpha(&self) -> AlgebraicElement<D::Element> {
AlgebraicElement {
coeffs: vec![self.base.zero(), self.base.one()],
}
}
pub fn element(&self, mut coeffs: Vec<D::Element>) -> AlgebraicElement<D::Element> {
self.reduce(&mut coeffs);
AlgebraicElement { coeffs }
}
fn trim(&self, v: &mut Vec<D::Element>) {
while let Some(last) = v.last() {
if self.base.is_zero(last) {
v.pop();
} else {
break;
}
}
}
fn reduce(&self, v: &mut Vec<D::Element>) {
let d = self.extension_degree();
while v.len() > d {
let c = v.pop().expect("nonempty while reducing");
if self.base.is_zero(&c) {
continue;
}
let offset = v.len() - d;
for (i, mc) in self.min_poly.iter().take(d).enumerate() {
let t = self.base.mul(&c, mc);
let slot = &mut v[offset + i];
*slot = self.base.sub(slot, &t);
}
}
self.trim(v);
}
fn poly_add(&self, a: &[D::Element], b: &[D::Element]) -> Vec<D::Element> {
let mut out = Vec::with_capacity(a.len().max(b.len()));
for i in 0..a.len().max(b.len()) {
let x = a.get(i);
let y = b.get(i);
let c = match (x, y) {
(Some(x), Some(y)) => self.base.add(x, y),
(Some(x), None) => x.clone(),
(None, Some(y)) => y.clone(),
(None, None) => unreachable!(),
};
out.push(c);
}
self.trim(&mut out);
out
}
fn poly_sub(&self, a: &[D::Element], b: &[D::Element]) -> Vec<D::Element> {
let mut out = Vec::with_capacity(a.len().max(b.len()));
for i in 0..a.len().max(b.len()) {
let x = a.get(i);
let y = b.get(i);
let c = match (x, y) {
(Some(x), Some(y)) => self.base.sub(x, y),
(Some(x), None) => x.clone(),
(None, Some(y)) => self.base.neg(y),
(None, None) => unreachable!(),
};
out.push(c);
}
self.trim(&mut out);
out
}
fn poly_mul(&self, a: &[D::Element], b: &[D::Element]) -> Vec<D::Element> {
if a.is_empty() || b.is_empty() {
return Vec::new();
}
let mut out = vec![self.base.zero(); a.len() + b.len() - 1];
for (i, x) in a.iter().enumerate() {
if self.base.is_zero(x) {
continue;
}
for (j, y) in b.iter().enumerate() {
let t = self.base.mul(x, y);
let slot = &mut out[i + j];
*slot = self.base.add(slot, &t);
}
}
self.trim(&mut out);
out
}
fn poly_quot_rem(
&self,
a: &[D::Element],
b: &[D::Element],
) -> Option<(PolyCoeffs<D>, PolyCoeffs<D>)> {
if b.is_empty() {
return None;
}
let mut rem = a.to_vec();
self.trim(&mut rem);
let mut quot = vec![self.base.zero(); a.len().max(b.len()) - b.len() + 1];
let deg_b = b.len() - 1;
let lc_b = b.last().expect("nonempty divisor");
while rem.len() > deg_b && !rem.is_empty() {
let k = rem.len() - 1 - deg_b;
let c = self
.base
.div(rem.last().expect("nonempty remainder"), lc_b)?;
if !self.base.is_zero(&c) {
quot[k] = self.base.add("[k], &c);
for (i, bc) in b.iter().enumerate() {
let t = self.base.mul(&c, bc);
let slot = &mut rem[k + i];
*slot = self.base.sub(slot, &t);
}
}
self.trim(&mut rem);
}
self.trim(&mut quot);
Some((quot, rem))
}
fn poly_extended_gcd(
&self,
a: &[D::Element],
b: &[D::Element],
) -> Option<(PolyCoeffs<D>, PolyCoeffs<D>, PolyCoeffs<D>)> {
let mut old_r = a.to_vec();
let mut r = b.to_vec();
let mut old_s = vec![self.base.one()];
let mut s: Vec<D::Element> = Vec::new();
while !r.is_empty() {
let (q, rem) = self.poly_quot_rem(&old_r, &r)?;
old_r = r;
r = rem;
let qs = self.poly_mul(&q, &s);
let new_s = self.poly_sub(&old_s, &qs);
old_s = s;
s = new_s;
}
let lc = old_r.last()?.clone();
let lc_inv = self.base.inv(&lc)?;
if !self.base.is_one(&lc) {
for c in old_r.iter_mut().chain(old_s.iter_mut()) {
*c = self.base.mul(c, &lc_inv);
}
}
Some((old_r, old_s, Vec::new()))
}
}
impl<D: Domain> Domain for AlgebraicExtension<D> {
type Element = AlgebraicElement<D::Element>;
fn zero(&self) -> Self::Element {
AlgebraicElement { coeffs: Vec::new() }
}
fn one(&self) -> Self::Element {
self.from_base(self.base.one())
}
fn add(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
AlgebraicElement {
coeffs: self.poly_add(&a.coeffs, &b.coeffs),
}
}
fn sub(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
AlgebraicElement {
coeffs: self.poly_sub(&a.coeffs, &b.coeffs),
}
}
fn neg(&self, a: &Self::Element) -> Self::Element {
AlgebraicElement {
coeffs: a.coeffs.iter().map(|c| self.base.neg(c)).collect(),
}
}
fn mul(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
let mut coeffs = self.poly_mul(&a.coeffs, &b.coeffs);
self.reduce(&mut coeffs);
AlgebraicElement { coeffs }
}
fn div(&self, a: &Self::Element, b: &Self::Element) -> Option<Self::Element> {
self.inv(b).map(|inv| self.mul(a, &inv))
}
fn inv(&self, a: &Self::Element) -> Option<Self::Element> {
if self.is_zero(a) {
return None;
}
let (g, s, _) = self.poly_extended_gcd(&a.coeffs, &self.min_poly)?;
if !g.is_empty() && g.len() == 1 {
let mut coeffs = s;
self.reduce(&mut coeffs);
Some(AlgebraicElement { coeffs })
} else {
None
}
}
fn is_zero(&self, a: &Self::Element) -> bool {
a.coeffs.is_empty()
}
fn cast_u64(&self, n: u64) -> Self::Element {
self.from_base(self.base.cast_u64(n))
}
}
impl<D: Domain> EuclideanDomain for AlgebraicExtension<D> {
fn div_rem(
&self,
a: &Self::Element,
b: &Self::Element,
) -> Option<(Self::Element, Self::Element)> {
self.div(a, b).map(|q| (q, self.zero()))
}
fn gcd(&self, a: &Self::Element, b: &Self::Element) -> Self::Element {
if self.is_zero(a) && self.is_zero(b) {
self.zero()
} else {
self.one()
}
}
}
pub type AlgebraicNumberField = AlgebraicExtension<RationalDomain>;
#[cfg(test)]
mod tests {
use super::*;
use crate::finite_field::FiniteField;
use crate::rational::Rational;
use num_bigint::BigInt;
fn r(n: i64, d: i64) -> Rational {
Rational::new(n, d)
}
fn q_ext_sqrt(c: i64) -> AlgebraicNumberField {
AlgebraicNumberField::new(RationalDomain, vec![r(-c, 1), r(0, 1), r(1, 1)])
}
#[test]
fn sqrt2_arithmetic() {
let field = q_ext_sqrt(2);
let alpha = field.alpha();
assert_eq!(field.mul(&alpha, &alpha), field.from_base(r(2, 1)));
let one = field.one();
let a = field.add(&one, &alpha);
let b = field.sub(&one, &alpha);
assert_eq!(field.mul(&a, &b), field.from_base(r(-1, 1)));
}
#[test]
fn sqrt2_inverse() {
let field = q_ext_sqrt(2);
let alpha = field.alpha();
let inv = field.inv(&alpha).expect("α is a unit");
assert_eq!(inv.coeffs(), &[r(0, 1), r(1, 2)], "1/√2 = √2/2");
let a = field.element(vec![r(1, 1), r(2, 1)]);
let a_inv = field.inv(&a).expect("unit");
assert_eq!(field.mul(&a, &a_inv), field.one());
}
#[test]
fn gaussian_rationals() {
let field = AlgebraicNumberField::new(RationalDomain, vec![r(1, 1), r(0, 1), r(1, 1)]);
let i = field.alpha();
assert_eq!(field.mul(&i, &i), field.from_base(r(-1, 1)));
let one_plus_i = field.add(&field.one(), &i);
let sq = field.mul(&one_plus_i, &one_plus_i);
assert_eq!(sq.coeffs(), &[r(0, 1), r(2, 1)]);
let inv = field.inv(&one_plus_i).expect("unit");
assert_eq!(inv.coeffs(), &[r(1, 2), r(-1, 2)]);
}
#[test]
fn cbrt2_inverse() {
let field =
AlgebraicNumberField::new(RationalDomain, vec![r(-2, 1), r(0, 1), r(0, 1), r(1, 1)]);
let alpha = field.alpha();
let inv = field.inv(&alpha).expect("unit");
assert_eq!(inv.coeffs(), &[r(0, 1), r(0, 1), r(1, 2)]);
let a = field.element(vec![r(1, 1), r(1, 1), r(1, 1)]);
let a_inv = field.inv(&a).expect("unit");
assert_eq!(field.mul(&a, &a_inv), field.one());
}
#[test]
fn galois_field_gf9() {
let base = FiniteField::new(BigInt::from(3));
let field = AlgebraicExtension::new(
base.clone(),
vec![base.element(1), base.element(0), base.element(1)],
);
let alpha = field.alpha();
assert_eq!(field.mul(&alpha, &alpha), field.from_base(base.element(2)));
let a = field.add(&field.one(), &alpha);
let a_inv = field.inv(&a).expect("unit in GF(9)");
assert_eq!(field.mul(&a, &a_inv), field.one());
assert_eq!(field.pow(&a, 8), field.one());
}
#[test]
fn reducible_modulus_has_zero_divisors() {
let field = q_ext_sqrt(1);
let alpha = field.alpha();
let a = field.sub(&alpha, &field.one());
assert!(field.inv(&a).is_none(), "α − 1 is not a unit");
}
#[test]
fn degenerate_gcd_and_div_rem() {
let field = q_ext_sqrt(2);
let a = field.alpha();
let z = field.zero();
assert_eq!(field.gcd(&a, &z), field.one());
assert_eq!(field.gcd(&z, &z), field.zero());
let (q, rem) = field.div_rem(&a, &a).expect("division by a unit");
assert_eq!(q, field.one());
assert_eq!(rem, field.zero());
assert!(field.div_rem(&a, &z).is_none());
}
#[test]
fn cast_and_element_reduction() {
let field = q_ext_sqrt(2);
assert_eq!(field.cast_u64(3), field.from_base(r(3, 1)));
let e = field.element(vec![r(0, 1), r(0, 1), r(1, 1)]); assert_eq!(e, field.from_base(r(2, 1)));
assert!(field.is_zero(&field.element(vec![r(0, 1), r(0, 1)])));
}
}