Skip to main content

yui_core/conc/num/
ff.rs

1//! The finite field š”½_p = ℤ/pℤ for prime `p`.
2//!
3//! Primality of `p` is **not** checked; the [`Field`] impl is only valid when
4//! `p` is prime. For `p = 2`, prefer [`FF2`](super::FF2).
5//!
6//! See: <https://en.wikipedia.org/wiki/Finite_field>
7
8#![allow(non_upper_case_globals)]
9
10use std::ops::{Add, Neg, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, RemAssign};
11use std::str::FromStr;
12use derive_more::{Display, Debug};
13use num_traits::{Zero, One};
14use auto_impl_ops::auto_ops;
15
16use crate::abst::{MathType, AddMonOps, AddGrpOps, MonOps, RingOps, FieldOps, EucRingOps, AddMon, AddGrp, Mon, Ring, EucRing, Field};
17use crate::util::parse_err::ParseErr;
18
19type I = i32;
20
21/// An element of š”½_p, stored as a representative in `0 .. p`.
22#[derive(Clone, Copy, PartialEq, Eq, Default, Display, Debug)]
23#[display( "{}", _0)]
24#[debug( "{}", _0)]
25pub struct FF<const p: I>(I);
26
27impl<const p: I> FF<p> {
28    pub fn new(a: I) -> Self {
29        assert!(p > 0);
30        Self(a.rem_euclid(p))
31    }
32
33    pub fn rep(&self) -> &I {
34        &self.0
35    }
36}
37
38impl<const p: I> From<I> for FF<p> {
39    fn from(a: I) -> Self {
40        Self::new(a)
41    }
42}
43
44impl<const p: I> FromStr for FF<p> {
45    type Err = ParseErr;
46    fn from_str(s: &str) -> Result<Self, Self::Err> {
47        let a = s.parse::<I>().map_err(|_| ParseErr::invalid(s, &Self::math_symbol()))?;
48        Ok(Self::from(a))
49    }
50}
51
52impl<const p: I> Zero for FF<p> {
53    fn zero() -> Self {
54        Self(0)
55    }
56
57    fn is_zero(&self) -> bool {
58        self.0.is_zero()
59    }
60}
61
62impl<const p: I> One for FF<p> {
63    fn one() -> Self {
64        Self(1)
65    }
66
67    fn is_one(&self) -> bool {
68        self.0.is_one()
69    }
70}
71
72macro_rules! impl_unop {
73    ($trait:ident, $method:ident) => {
74        impl<const p: I> $trait for FF<p> {
75            type Output = Self;
76            fn $method(self) -> Self::Output {
77                Self::new(self.0.$method())
78            }
79        }
80
81        impl<const p: I> $trait for &FF<p> {
82            type Output = FF<p>;
83            #[inline]
84            fn $method(self) -> Self::Output {
85                FF::new(self.0.$method())
86            }
87        }
88    };
89}
90
91impl_unop!(Neg, neg);
92
93macro_rules! impl_binop {
94    ($trait:ident, $method:ident) => {
95        #[auto_ops]
96        impl<const p: I> $trait<&FF<p>> for &FF<p> {
97            type Output = FF<p>;
98            fn $method(self, rhs: &FF<p>) -> Self::Output {
99                FF::new(self.0.$method(&rhs.0))
100            }
101        }
102    }
103}
104
105impl_binop!(Add, add);
106impl_binop!(Sub, sub);
107impl_binop!(Mul, mul);
108
109#[auto_ops]
110impl<const p: I> Div<&FF<p>> for &FF<p> {
111    type Output = FF<p>;
112    fn div(self, rhs: &FF<p>) -> Self::Output {
113        assert!(!rhs.is_zero());
114        self * rhs.inv().unwrap()
115    }
116}
117
118#[auto_ops]
119impl<const p: I> Rem<&FF<p>> for &FF<p> {
120    type Output = FF<p>;
121    fn rem(self, rhs: &FF<p>) -> Self::Output {
122        assert!(!rhs.is_zero());
123        FF::zero() // MEMO: FF<p> is a field.
124    }
125}
126
127macro_rules! impl_alg_ops {
128    ($trait:ident) => {
129        impl<const p: I> $trait for FF<p> {}
130        impl<const p: I> $trait<FF<p>> for &FF<p> {}
131    };
132}
133
134impl_alg_ops!(AddMonOps);
135impl_alg_ops!(AddGrpOps);
136impl_alg_ops!(MonOps);
137impl_alg_ops!(RingOps);
138impl_alg_ops!(EucRingOps);
139impl_alg_ops!(FieldOps);
140
141impl<const p: I> MathType for FF<p> {
142    fn math_symbol() -> String {
143        use crate::util::format::subscript;
144        format!("F{}", subscript(p as isize))
145    }
146}
147
148impl<const p: I> AddMon for FF<p> {}
149impl<const p: I> AddGrp for FF<p> {}
150impl<const p: I> Mon for FF<p> {}
151
152impl<const p: I> Ring for FF<p> {
153    fn inv(&self) -> Option<Self> {
154        if self.is_zero() {
155            None
156        } else {
157            // 1 = ax + py  ->  ax = 1 mod p.
158            let (d, x, _y) = I::gcdx(&self.0, &p);
159
160            assert!(d.is_one());
161
162            let inv = Self::new(x);
163            Some(inv)
164        }
165    }
166
167    fn is_unit(&self) -> bool {
168        !self.is_zero()
169    }
170
171    fn normalizing_unit(&self) -> Self {
172        if self.is_zero() {
173            Self::one()
174        } else {
175            self.inv().unwrap()
176        }
177    }
178}
179
180impl<const p: I> EucRing for FF<p> {}
181impl<const p: I> Field for FF<p> {}
182
183mod tex {
184    use crate::util::tex::TeX;
185    use super::*;
186
187    impl<const p: I> TeX for FF<p> {
188        fn tex_math_symbol() -> String {
189            format!("\\mathbb{{F}}_{p}")
190        }
191        fn tex_string(&self) -> String {
192            self.to_string()
193        }
194    }
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200
201    type F3 = FF<3>;
202    type F5 = FF<5>;
203
204    #[test]
205    fn init() {
206        let a = F3::new(-7);
207        assert_eq!(a.0, 2);
208
209        let a = F5::new(-7);
210        assert_eq!(a.0, 3);
211    }
212
213    #[test]
214    fn display() {
215        let a = F3::new(-7);
216        assert_eq!(format!("{}", a), "2");
217
218        let a = F5::new(-7);
219        assert_eq!(format!("{}", a), "3");
220    }
221
222    #[test]
223    fn debug() {
224        let a = F3::new(-7);
225        assert_eq!(format!("{:?}", a), "2");
226
227        let a = F5::new(-7);
228        assert_eq!(format!("{:?}", a), "3");
229    }
230
231    #[test]
232    fn add() {
233        let a = F5::new(3);
234        let b = F5::new(4);
235
236        assert_eq!(a + b, F5::new(2));
237    }
238
239    #[test]
240    fn add_assign() {
241        let mut a = F5::new(3);
242        a += F5::new(4);
243        assert_eq!(a, F5::new(2));
244    }
245
246    #[test]
247    fn neg() {
248        let a = F5::new(3);
249        assert_eq!(-a, F5::new(2));
250    }
251
252    #[test]
253    fn sub() {
254        let a = F5::new(3);
255        let b = F5::new(4);
256
257        assert_eq!(a - b, F5::new(4));
258    }
259
260    #[test]
261    fn sub_assign() {
262        let mut a = F5::new(3);
263        a -= F5::new(4);
264        assert_eq!(a, F5::new(4));
265    }
266
267    #[test]
268    fn mul() {
269        let a = F5::new(3);
270        let b = F5::new(4);
271        assert_eq!(a * b, F5::new(2));
272    }
273
274    #[test]
275    fn mul_assign() {
276        let mut a = F5::new(3);
277        a *= F5::new(4);
278        assert_eq!(a, F5::new(2));
279    }
280
281    #[test]
282    fn div() {
283        let a = F5::new(4);
284        let b = F5::new(3);
285        assert_eq!(a / b, F5::new(3));
286    }
287
288    #[test]
289    fn div_assign() {
290        let mut a = F5::new(4);
291        a /= F5::new(3);
292        assert_eq!(a, F5::new(3));
293    }
294
295
296    #[test]
297    fn rem() {
298        let a = F5::new(4);
299        let b = F5::new(3);
300        assert_eq!(a % b, F5::zero());
301    }
302
303    #[test]
304    fn rem_assign() {
305        let mut a = F5::new(4);
306        a %= F5::new(3);
307        assert_eq!(a, F5::zero());
308    }
309
310    #[test]
311    fn tex() {
312        use crate::util::tex::TeX;
313        assert_eq!(F3::tex_math_symbol(), "\\mathbb{F}_3");
314        assert_eq!(F3::from(5).tex_string(), "2");
315    }
316}