1use binius_core::word::Word;
4use binius_frontend::{CircuitBuilder, Wire};
5
6use super::{
7 BigUint, BigUintDivideHint, ModDivideHint, ModInverseHint, PseudoMersenneModReduce,
8 addsub::{add_with_carry_out, sub_with_borrow_out},
9 biguint_lt, num_biguint_from_u64_limbs, sub, textbook_mul, textbook_square,
10};
11
12pub struct PseudoMersennePrimeField {
17 modulus: BigUint,
18 modulus_po2: usize,
19 modulus_subtrahend: BigUint,
20}
21
22impl PseudoMersennePrimeField {
23 pub fn new(b: &CircuitBuilder, modulus_po2: usize, modulus_subtrahend: &[u64]) -> Self {
27 let modulus_subtrahend = num_biguint_from_u64_limbs(modulus_subtrahend);
28 let po2 = num_bigint::BigUint::from(2usize).pow(modulus_po2 as u32);
29 assert!(po2 > modulus_subtrahend, "2^modulus_po2 - modulus_subtrahend > 0");
30
31 let modulus = BigUint::new_constant(b, &(po2 - &modulus_subtrahend));
32 let modulus_subtrahend = BigUint::new_constant(b, &modulus_subtrahend);
33
34 Self {
35 modulus,
36 modulus_po2,
37 modulus_subtrahend,
38 }
39 }
40
41 pub const fn limbs_len(&self) -> usize {
43 self.modulus.limbs.len()
44 }
45
46 pub const fn modulus(&self) -> &BigUint {
48 &self.modulus
49 }
50
51 pub fn add(&self, b: &CircuitBuilder, fe1: &BigUint, fe2: &BigUint) -> BigUint {
55 let l = self.limbs_len();
56 assert!(fe1.limbs.len() == l && fe2.limbs.len() == l);
57
58 let (sum, carry) = add_with_carry_out(b, fe1, fe2);
62
63 let need_reduction = b.bxor(carry, b.bnot(biguint_lt(b, &sum, &self.modulus)));
69
70 let (reduced, _borrow) =
73 sub_with_borrow_out(b, &sum, &self.modulus.zero_unless(b, need_reduction));
74
75 reduced
76 }
77
78 pub fn sub(&self, b: &CircuitBuilder, fe1: &BigUint, fe2: &BigUint) -> BigUint {
82 assert!(fe1.limbs.len() == self.limbs_len() && fe2.limbs.len() == self.limbs_len());
83 let fe2_add_inv = sub(b, &self.modulus, fe2);
85 self.add(b, fe1, &fe2_add_inv)
86 }
87
88 pub fn square(&self, b: &CircuitBuilder, fe: &BigUint) -> BigUint {
92 assert!(fe.limbs.len() == self.limbs_len());
93 self.reduce_product(b, &textbook_square(b, fe))
94 }
95
96 pub fn mul(&self, b: &CircuitBuilder, fe1: &BigUint, fe2: &BigUint) -> BigUint {
101 assert!(fe1.limbs.len() == self.limbs_len() && fe2.limbs.len() == self.limbs_len());
102 self.reduce_product(b, &textbook_mul(b, fe1, fe2))
103 }
104
105 fn reduce_product(&self, b: &CircuitBuilder, product: &BigUint) -> BigUint {
106 let (quotient, remainder) = BigUintDivideHint::call(b, &product.limbs, &self.modulus.limbs);
107
108 let zero = b.add_constant(Word::ZERO);
109
110 let quotient = BigUint { limbs: quotient };
111 let remainder = BigUint { limbs: remainder }.pad_limbs_to(self.limbs_len(), zero);
112
113 b.assert_true("remainder < modulus", biguint_lt(b, &remainder, &self.modulus));
114
115 PseudoMersenneModReduce::new(
117 b,
118 product,
119 self.modulus_po2,
120 &self.modulus_subtrahend,
121 "ient,
122 &remainder,
123 )
124 .constrain(b);
125
126 remainder
127 }
128
129 pub fn inverse(&self, b: &CircuitBuilder, fe: &BigUint, exists: Wire) -> BigUint {
136 assert!(fe.limbs.len() == self.limbs_len());
137 let (quotient, inverse) = ModInverseHint::call(b, &fe.limbs, &self.modulus.limbs);
138
139 let zero = b.add_constant(Word::ZERO);
140
141 let quotient = BigUint { limbs: quotient };
142 let inverse = BigUint { limbs: inverse }.pad_limbs_to(self.limbs_len(), zero);
143 let one = BigUint::new_constant(b, &num_bigint::BigUint::from(1usize))
144 .pad_limbs_to(self.limbs_len(), zero);
145
146 let product = textbook_mul(b, &inverse, fe);
147
148 b.assert_true("inverse < modulus", biguint_lt(b, &inverse, &self.modulus));
149
150 PseudoMersenneModReduce::new(
152 b,
153 &product,
154 self.modulus_po2,
155 &self.modulus_subtrahend,
156 "ient,
157 &one,
158 )
159 .constrain_cond(b, exists);
160
161 inverse
162 }
163
164 pub fn div(
192 &self,
193 b: &CircuitBuilder,
194 dividend: &BigUint,
195 divisor: &BigUint,
196 exists: Wire,
197 ) -> BigUint {
198 assert!(
199 dividend.limbs.len() == self.limbs_len() && divisor.limbs.len() == self.limbs_len()
200 );
201 let (quotient, slope) =
202 ModDivideHint::call(b, ÷nd.limbs, &divisor.limbs, &self.modulus.limbs);
203
204 let zero = b.add_constant(Word::ZERO);
205
206 let quotient = BigUint { limbs: quotient };
207 let slope = BigUint { limbs: slope }.pad_limbs_to(self.limbs_len(), zero);
208
209 let product = textbook_mul(b, &slope, divisor);
210
211 b.assert_true("slope < modulus", biguint_lt(b, &slope, &self.modulus));
212
213 PseudoMersenneModReduce::new(
215 b,
216 &product,
217 self.modulus_po2,
218 &self.modulus_subtrahend,
219 "ient,
220 dividend,
221 )
222 .constrain_cond(b, exists);
223
224 slope
225 }
226}