fp/field/
element.rs

1use std::{
2    hash::Hash,
3    ops::{Add, AddAssign, Deref, Div, Mul, MulAssign, Neg, Sub, SubAssign},
4};
5
6use super::{Field, field_internal::FieldInternal};
7
8/// This just ensures that the containers are "nice enough", in the sense that they are cloneable,
9/// hashable, etc. We may add more custom methods in the future.
10pub trait FieldElementContainer:
11    std::fmt::Debug + std::fmt::Display + Clone + PartialEq + Eq + Hash
12{
13}
14
15/// An element of a field.
16///
17/// This contains the field itself so that it knows how to do arithmetic operations. We want this to
18/// be a _struct_ rather than a trait, which means that we want the _actual_ storage of the value to
19/// be managed by the field itself. Therefore, we have an internal field trait that knows about
20/// arithmetic operations and other implementation details, but these operations are only accessible
21/// from outside the crate using this struct.
22///
23/// It might seem wasteful to handle, say, `FieldElement<Fp<P>>`s rather than `u32` in the API for
24/// `FqVector<Fp<P>>`. However, this gives us type-level guarantees that the invariants of the
25/// elements hold, i.e. in this case that its value is in the range `0..P`. Moreover, this is bigger
26/// than a bare `F::ElementContainer` only when the field has a positive memory footprint. The cases
27/// we care most about, `Fp<P2>`, `Fp<P3>`, `Fp<P5>`, and `Fp<P7>`, are all ZSTs and therefore don't
28/// cause any overhead.
29#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
30pub struct FieldElement<F: FieldInternal> {
31    field: F,
32    pub(super) value: F::ElementContainer,
33}
34
35impl<F: FieldInternal> FieldElement<F> {
36    /// Create a new field element. This is only visible to the `field` module, because the caller
37    /// is responsible for ensuring that the invariants of `value` hold.
38    ///
39    /// Handling `FieldElement`s in the API rather than the containers directly has the advantage of
40    /// being sure at compile-time that the invariants hold.
41    pub(super) fn new(field: F, value: F::ElementContainer) -> Self {
42        Self { field, value }
43    }
44
45    pub fn field(&self) -> F {
46        self.field
47    }
48
49    pub(crate) fn val(self) -> F::ElementContainer {
50        self.value
51    }
52
53    pub fn inv(self) -> Option<Self> {
54        self.field.inv(self)
55    }
56
57    pub fn frobenius(self) -> Self {
58        self.field.frobenius(self)
59    }
60}
61
62// Allows us to access methods on `F::Element` directly
63impl<F: FieldInternal> Deref for FieldElement<F> {
64    type Target = F::ElementContainer;
65
66    fn deref(&self) -> &Self::Target {
67        &self.value
68    }
69}
70
71macro_rules! impl_arith {
72    ($trait:ident, $trait_assign:ident, $method:ident, $method_assign:ident) => {
73        impl<F: Field> $trait for FieldElement<F> {
74            type Output = Self;
75
76            fn $method(self, rhs: Self) -> Self::Output {
77                self.field.$method(self, rhs)
78            }
79        }
80
81        impl<F: Field> $trait_assign for FieldElement<F> {
82            fn $method_assign(&mut self, rhs: Self) {
83                self.field.$method_assign(self, rhs);
84            }
85        }
86    };
87}
88
89impl_arith!(Add, AddAssign, add, add_assign);
90impl_arith!(Sub, SubAssign, sub, sub_assign);
91impl_arith!(Mul, MulAssign, mul, mul_assign);
92
93impl<F: Field> Div for FieldElement<F> {
94    type Output = Option<Self>;
95
96    fn div(self, rhs: Self) -> Self::Output {
97        self.field.div(self, rhs)
98    }
99}
100
101impl<F: Field> Neg for FieldElement<F> {
102    type Output = Self;
103
104    fn neg(self) -> Self::Output {
105        self.field.neg(self)
106    }
107}
108
109impl<F: Field> std::fmt::Display for FieldElement<F> {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        write!(f, "{}", self.value)
112    }
113}