ark_ff/fields/models/small_fp/
small_fp_backend.rs1use crate::{AdditiveGroup, BigInt, FftField, One, PrimeField, SqrtPrecomputation, Zero};
2use ark_std::{
3 cmp::*,
4 fmt::{Display, Formatter, Result as FmtResult},
5 hash::Hash,
6 marker::PhantomData,
7 str::FromStr,
8};
9use educe::Educe;
10use num_traits::Unsigned;
11
12pub use ark_ff_macros::SmallFpConfig;
13
14pub trait SmallFpConfig: Send + Sync + 'static + Sized {
21 type T: Copy
22 + Default
23 + PartialEq
24 + Eq
25 + Hash
26 + Sync
27 + Send
28 + PartialOrd
29 + Display
30 + Unsigned
31 + core::fmt::Debug
32 + core::ops::Add<Output = Self::T>
33 + core::ops::Sub<Output = Self::T>
34 + core::ops::Mul<Output = Self::T>
35 + core::ops::Div<Output = Self::T>
36 + core::ops::Rem<Output = Self::T>
37 + Into<u128>
38 + TryFrom<u128>;
39
40 const MODULUS: Self::T;
42 const MODULUS_U128: u128;
43
44 const NUM_BIG_INT_LIMBS: usize = 1;
47
48 const GENERATOR: SmallFp<Self>;
52
53 const ZERO: SmallFp<Self>;
56
57 const ONE: SmallFp<Self>;
60
61 const NEG_ONE: SmallFp<Self>;
63
64 const TWO_ADICITY: u32;
68
69 const TWO_ADIC_ROOT_OF_UNITY: SmallFp<Self>;
71
72 const SMALL_SUBGROUP_BASE: Option<u32> = None;
75
76 const SMALL_SUBGROUP_BASE_ADICITY: Option<u32> = None;
79
80 const LARGE_SUBGROUP_ROOT_OF_UNITY: Option<SmallFp<Self>> = None;
84
85 const SQRT_PRECOMP: Option<SqrtPrecomputation<SmallFp<Self>>>;
89
90 fn add_assign(a: &mut SmallFp<Self>, b: &SmallFp<Self>);
92
93 fn sub_assign(a: &mut SmallFp<Self>, b: &SmallFp<Self>);
95
96 fn double_in_place(a: &mut SmallFp<Self>);
98
99 fn neg_in_place(a: &mut SmallFp<Self>);
101
102 fn mul_assign(a: &mut SmallFp<Self>, b: &SmallFp<Self>);
104
105 fn sum_of_products<const T: usize>(
107 a: &[SmallFp<Self>; T],
108 b: &[SmallFp<Self>; T],
109 ) -> SmallFp<Self>;
110
111 fn square_in_place(a: &mut SmallFp<Self>);
113
114 fn inverse(a: &SmallFp<Self>) -> Option<SmallFp<Self>>;
116
117 fn new(value: Self::T) -> SmallFp<Self>;
119
120 fn from_bigint(other: BigInt<1>) -> Option<SmallFp<Self>>;
124
125 fn into_bigint(other: SmallFp<Self>) -> BigInt<1>;
128}
129
130#[derive(Educe)]
136#[educe(Default, Hash, Clone, Copy, PartialEq, Eq)]
137pub struct SmallFp<P: SmallFpConfig> {
138 pub value: P::T,
139 _phantom: PhantomData<P>,
140}
141
142impl<P: SmallFpConfig> SmallFp<P> {
143 #[doc(hidden)]
144 #[inline]
145 pub fn is_geq_modulus(&self) -> bool {
146 self.value >= P::MODULUS
147 }
148
149 pub const fn from_raw(value: P::T) -> Self {
151 Self {
152 value,
153 _phantom: PhantomData,
154 }
155 }
156
157 #[inline]
159 pub fn new(value: P::T) -> Self {
160 P::new(value)
161 }
162
163 pub const fn num_bits_to_shave() -> usize {
164 primitive_type_bit_size(P::MODULUS_U128) - (Self::MODULUS_BIT_SIZE as usize)
165 }
166}
167
168impl<P: SmallFpConfig> ark_std::fmt::Debug for SmallFp<P> {
169 fn fmt(&self, f: &mut Formatter<'_>) -> ark_std::fmt::Result {
170 ark_std::fmt::Debug::fmt(&self.into_bigint(), f)
171 }
172}
173
174impl<P: SmallFpConfig> Zero for SmallFp<P> {
175 #[inline]
176 fn zero() -> Self {
177 P::ZERO
178 }
179
180 #[inline]
181 fn is_zero(&self) -> bool {
182 *self == P::ZERO
183 }
184}
185
186impl<P: SmallFpConfig> One for SmallFp<P> {
187 #[inline]
188 fn one() -> Self {
189 P::ONE
190 }
191
192 #[inline]
193 fn is_one(&self) -> bool {
194 *self == P::ONE
195 }
196}
197
198impl<P: SmallFpConfig> AdditiveGroup for SmallFp<P> {
199 type Scalar = Self;
200 const ZERO: Self = P::ZERO;
201
202 #[inline]
203 fn double(&self) -> Self {
204 let mut temp = *self;
205 AdditiveGroup::double_in_place(&mut temp);
206 temp
207 }
208
209 #[inline]
210 fn double_in_place(&mut self) -> &mut Self {
211 P::double_in_place(self);
212 self
213 }
214
215 #[inline]
216 fn neg_in_place(&mut self) -> &mut Self {
217 P::neg_in_place(self);
218 self
219 }
220}
221
222const fn const_to_bigint(value: u128) -> BigInt<1> {
223 BigInt::<1>::new([value as u64])
224}
225
226const fn const_num_bits_u128(value: u128) -> u32 {
227 if value == 0 {
228 0
229 } else {
230 128 - value.leading_zeros()
231 }
232}
233
234const fn primitive_type_bit_size(modulus_u128: u128) -> usize {
235 match modulus_u128 {
236 x if x <= u8::MAX as u128 => 8,
237 x if x <= u16::MAX as u128 => 16,
238 x if x <= u32::MAX as u128 => 32,
239 _ => 64,
240 }
241}
242
243impl<P: SmallFpConfig> PrimeField for SmallFp<P> {
244 type BigInt = BigInt<1>;
245
246 const MODULUS: Self::BigInt = const_to_bigint(P::MODULUS_U128);
247 const MODULUS_MINUS_ONE_DIV_TWO: Self::BigInt = Self::MODULUS.divide_by_2_round_down();
248 const MODULUS_BIT_SIZE: u32 = const_num_bits_u128(P::MODULUS_U128);
249 const TRACE: Self::BigInt = Self::MODULUS.two_adic_coefficient();
250 const TRACE_MINUS_ONE_DIV_TWO: Self::BigInt = Self::TRACE.divide_by_2_round_down();
251
252 #[inline]
253 fn from_bigint(r: BigInt<1>) -> Option<Self> {
254 P::from_bigint(r)
255 }
256
257 fn into_bigint(self) -> BigInt<1> {
258 P::into_bigint(self)
259 }
260}
261
262impl<P: SmallFpConfig> FftField for SmallFp<P> {
263 const GENERATOR: Self = P::GENERATOR;
264 const TWO_ADICITY: u32 = P::TWO_ADICITY;
265 const TWO_ADIC_ROOT_OF_UNITY: Self = P::TWO_ADIC_ROOT_OF_UNITY;
266 const SMALL_SUBGROUP_BASE: Option<u32> = P::SMALL_SUBGROUP_BASE;
267 const SMALL_SUBGROUP_BASE_ADICITY: Option<u32> = P::SMALL_SUBGROUP_BASE_ADICITY;
268 const LARGE_SUBGROUP_ROOT_OF_UNITY: Option<Self> = P::LARGE_SUBGROUP_ROOT_OF_UNITY;
269}
270
271impl<P: SmallFpConfig> Ord for SmallFp<P> {
278 #[inline(always)]
279 fn cmp(&self, other: &Self) -> Ordering {
280 self.into_bigint().cmp(&other.into_bigint())
281 }
282}
283
284impl<P: SmallFpConfig> PartialOrd for SmallFp<P> {
291 #[inline(always)]
292 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
293 Some(self.cmp(other))
294 }
295}
296
297impl<P: SmallFpConfig> ark_std::rand::distributions::Distribution<SmallFp<P>>
298 for ark_std::rand::distributions::Standard
299{
300 #[inline]
301 fn sample<R: ark_std::rand::Rng + ?Sized>(&self, rng: &mut R) -> SmallFp<P> {
302 macro_rules! sample_loop {
303 ($ty:ty) => {
304 loop {
305 let mut val: $ty = rng.sample(ark_std::rand::distributions::Standard);
306 let shave_bits = SmallFp::<P>::num_bits_to_shave();
307 let mask = <$ty>::MAX >> shave_bits;
308 val &= mask;
309 if val > 0 && u128::from(val) < P::MODULUS_U128 {
310 return SmallFp::from(val);
311 }
312 }
313 };
314 }
315
316 match P::MODULUS_U128 {
317 modulus if modulus <= u8::MAX as u128 => sample_loop!(u8),
318 modulus if modulus <= u16::MAX as u128 => sample_loop!(u16),
319 modulus if modulus <= u32::MAX as u128 => sample_loop!(u32),
320 _ => sample_loop!(u64),
321 }
322 }
323}
324
325#[derive(Debug)]
326pub enum ParseSmallFpError {
327 Empty,
328 InvalidFormat,
329 InvalidLeadingZero,
330}
331
332impl<P: SmallFpConfig> FromStr for SmallFp<P> {
333 type Err = ParseSmallFpError;
334
335 fn from_str(s: &str) -> Result<Self, Self::Err> {
338 if s.is_empty() {
339 return Err(ParseSmallFpError::Empty);
340 }
341 if s.starts_with('0') && s.len() > 1 {
342 return Err(ParseSmallFpError::InvalidLeadingZero);
343 }
344
345 match s.parse::<u128>() {
346 Ok(val) => Ok(SmallFp::from(val)),
347 Err(_) => Err(ParseSmallFpError::InvalidFormat),
348 }
349 }
350}
351
352impl<P: SmallFpConfig> Display for SmallFp<P> {
355 #[inline]
356 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
357 let bigint = P::into_bigint(*self);
359 write!(f, "{}", bigint)
360 }
361}