1use halo2_base::{
2 gates::flex_gate::GateInstructions,
3 utils::{biguint_to_fe, decompose_biguint, fe_to_biguint, BigPrimeField, ScalarField},
4 AssignedValue, Context,
5 QuantumCell::Constant,
6};
7use num_bigint::{BigInt, BigUint};
8use num_traits::Zero;
9
10pub mod add_no_carry;
11pub mod big_is_equal;
12pub mod big_is_zero;
13pub mod big_less_than;
14pub mod carry_mod;
15pub mod check_carry_mod_to_zero;
16pub mod check_carry_to_zero;
17pub mod mul_no_carry;
18pub mod negative;
19pub mod scalar_mul_and_add_no_carry;
20pub mod scalar_mul_no_carry;
21pub mod select;
22pub mod select_by_indicator;
23pub mod sub;
24pub mod sub_no_carry;
25
26#[derive(Clone, Debug, PartialEq, Default)]
27pub enum BigIntStrategy {
28 #[default]
30 Simple,
31 }
35
36#[derive(Clone, Debug)]
37pub struct OverflowInteger<F: ScalarField> {
38 pub limbs: Vec<AssignedValue<F>>,
39 pub max_limb_bits: usize,
41 }
44
45impl<F: ScalarField> OverflowInteger<F> {
46 pub fn new(limbs: Vec<AssignedValue<F>>, max_limb_bits: usize) -> Self {
47 Self { limbs, max_limb_bits }
48 }
49
50 #[cfg(test)]
52 pub fn to_bigint(&self, limb_bits: usize) -> BigInt
53 where
54 F: BigPrimeField,
55 {
56 use halo2_base::utils::fe_to_bigint;
57
58 self.limbs
59 .iter()
60 .rev()
61 .fold(BigInt::zero(), |acc, acell| (acc << limb_bits) + fe_to_bigint(acell.value()))
62 }
63
64 pub fn evaluate_native(
67 ctx: &mut Context<F>,
68 gate: &impl GateInstructions<F>,
69 limbs: impl IntoIterator<Item = AssignedValue<F>>,
70 limb_bases: &[F],
71 ) -> AssignedValue<F> {
72 gate.inner_product(ctx, limbs, limb_bases.iter().map(|c| Constant(*c)))
74 }
75}
76
77#[repr(transparent)]
83#[derive(Clone, Debug)]
84pub struct ProperUint<F: ScalarField>(pub(crate) Vec<AssignedValue<F>>);
85
86impl<F: ScalarField> ProperUint<F> {
87 pub fn limbs(&self) -> &[AssignedValue<F>] {
88 self.0.as_slice()
89 }
90
91 pub fn into_overflow(self, limb_bits: usize) -> OverflowInteger<F> {
92 OverflowInteger::new(self.0, limb_bits)
93 }
94
95 pub fn into_crt(
100 self,
101 ctx: &mut Context<F>,
102 gate: &impl GateInstructions<F>,
103 value: BigUint,
104 limb_bases: &[F],
105 limb_bits: usize,
106 ) -> ProperCrtUint<F> {
107 let native =
109 OverflowInteger::evaluate_native(ctx, gate, self.0.iter().copied(), limb_bases);
110 ProperCrtUint(CRTInteger::new(self.into_overflow(limb_bits), native, value.into()))
111 }
112}
113
114#[repr(transparent)]
115#[derive(Clone, Debug)]
116pub struct FixedOverflowInteger<F: ScalarField> {
117 pub limbs: Vec<F>,
118}
119
120impl<F: BigPrimeField> FixedOverflowInteger<F> {
121 pub fn construct(limbs: Vec<F>) -> Self {
122 Self { limbs }
123 }
124
125 pub fn from_native(value: &BigUint, num_limbs: usize, limb_bits: usize) -> Self {
129 let limbs = decompose_biguint(value, num_limbs, limb_bits);
130 Self { limbs }
131 }
132
133 pub fn to_bigint(&self, limb_bits: usize) -> BigUint {
134 self.limbs
135 .iter()
136 .rev()
137 .fold(BigUint::zero(), |acc, x| (acc << limb_bits) + fe_to_biguint(x))
138 }
139
140 pub fn assign(self, ctx: &mut Context<F>) -> ProperUint<F> {
141 let assigned_limbs = self.limbs.into_iter().map(|limb| ctx.load_constant(limb)).collect();
142 ProperUint(assigned_limbs)
143 }
144
145 pub fn select_by_indicator(
147 gate: &impl GateInstructions<F>,
148 ctx: &mut Context<F>,
149 a: &[Self],
150 coeffs: &[AssignedValue<F>],
151 limb_bits: usize,
152 ) -> OverflowInteger<F> {
153 let k = a[0].limbs.len();
154
155 let out_limbs = (0..k)
156 .map(|idx| {
157 let int_limbs = a.iter().map(|a| Constant(a.limbs[idx]));
158 gate.select_by_indicator(ctx, int_limbs, coeffs.iter().copied())
159 })
160 .collect();
161
162 OverflowInteger::new(out_limbs, limb_bits)
163 }
164}
165
166#[derive(Clone, Debug)]
167pub struct CRTInteger<F: ScalarField> {
168 pub truncation: OverflowInteger<F>,
180 pub native: AssignedValue<F>,
181 pub value: BigInt,
182}
183
184impl<F: ScalarField> AsRef<CRTInteger<F>> for CRTInteger<F> {
185 fn as_ref(&self) -> &CRTInteger<F> {
186 self
187 }
188}
189
190impl<'a, F: ScalarField> From<&'a CRTInteger<F>> for CRTInteger<F> {
192 fn from(x: &'a CRTInteger<F>) -> Self {
193 x.clone()
194 }
195}
196
197impl<F: ScalarField> CRTInteger<F> {
198 pub fn new(truncation: OverflowInteger<F>, native: AssignedValue<F>, value: BigInt) -> Self {
199 Self { truncation, native, value }
200 }
201
202 pub fn native(&self) -> &AssignedValue<F> {
203 &self.native
204 }
205
206 pub fn limbs(&self) -> &[AssignedValue<F>] {
207 self.truncation.limbs.as_slice()
208 }
209}
210
211#[repr(transparent)]
219#[derive(Clone, Debug)]
220pub struct ProperCrtUint<F: ScalarField>(pub(crate) CRTInteger<F>);
221
222impl<F: ScalarField> AsRef<CRTInteger<F>> for ProperCrtUint<F> {
223 fn as_ref(&self) -> &CRTInteger<F> {
224 &self.0
225 }
226}
227
228impl<'a, F: ScalarField> From<&'a ProperCrtUint<F>> for ProperCrtUint<F> {
229 fn from(x: &'a ProperCrtUint<F>) -> Self {
230 x.clone()
231 }
232}
233
234impl<F: ScalarField> From<ProperCrtUint<F>> for CRTInteger<F> {
236 fn from(x: ProperCrtUint<F>) -> Self {
237 x.0
238 }
239}
240
241impl<'a, F: ScalarField> From<&'a ProperCrtUint<F>> for CRTInteger<F> {
242 fn from(x: &'a ProperCrtUint<F>) -> Self {
243 x.0.clone()
244 }
245}
246
247impl<F: ScalarField> From<ProperCrtUint<F>> for ProperUint<F> {
248 fn from(x: ProperCrtUint<F>) -> Self {
249 ProperUint(x.0.truncation.limbs)
250 }
251}
252
253impl<F: ScalarField> ProperCrtUint<F> {
254 pub fn limbs(&self) -> &[AssignedValue<F>] {
255 self.0.limbs()
256 }
257
258 pub fn native(&self) -> &AssignedValue<F> {
259 self.0.native()
260 }
261
262 pub fn value(&self) -> BigUint {
263 self.0.value.to_biguint().expect("Value of proper uint should not be negative")
264 }
265}
266
267#[derive(Clone, Debug)]
268pub struct FixedCRTInteger<F: ScalarField> {
269 pub truncation: FixedOverflowInteger<F>,
281 pub value: BigUint,
282}
283
284impl<F: BigPrimeField> FixedCRTInteger<F> {
285 pub fn new(truncation: FixedOverflowInteger<F>, value: BigUint) -> Self {
286 Self { truncation, value }
287 }
288
289 pub fn from_native(value: BigUint, num_limbs: usize, limb_bits: usize) -> Self {
292 let truncation = FixedOverflowInteger::from_native(&value, num_limbs, limb_bits);
293 Self { truncation, value }
294 }
295
296 pub fn assign(
297 self,
298 ctx: &mut Context<F>,
299 limb_bits: usize,
300 native_modulus: &BigUint,
301 ) -> ProperCrtUint<F> {
302 let assigned_truncation = self.truncation.assign(ctx).into_overflow(limb_bits);
303 let assigned_native = ctx.load_constant(biguint_to_fe(&(&self.value % native_modulus)));
304 ProperCrtUint(CRTInteger::new(assigned_truncation, assigned_native, self.value.into()))
305 }
306}