Skip to main content

midnight_circuits/field/foreign/
field_chip.rs

1// This file is part of MIDNIGHT-ZK.
2// Copyright (C) 2025 Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14//! `field_chip` is a chip for performing arithmetic over emulated fields.
15//!  See [here](https://github.com/midnightntwrk/midnight-circuits/wiki/Foreign-Field-Arithmetic)
16//!  for a description of the techniques that we use in this implementation.
17
18use std::{
19    cmp::{max, min},
20    fmt::Debug,
21    hash::{Hash, Hasher},
22    marker::PhantomData,
23};
24
25use ff::PrimeField;
26use midnight_proofs::{
27    circuit::{Chip, Layouter, Value},
28    plonk::{Advice, Column, ConstraintSystem, Error},
29};
30use num_bigint::{BigInt as BI, BigUint, ToBigInt};
31use num_integer::Integer;
32use num_traits::{One, Signed, Zero};
33#[cfg(any(test, feature = "testing"))]
34use {
35    crate::testing_utils::{FromScratch, Sampleable},
36    midnight_proofs::plonk::Instance,
37    rand::RngCore,
38};
39
40use super::gates::{
41    mul::{self, MulConfig},
42    norm::{self, NormConfig},
43};
44use crate::{
45    field::foreign::{
46        params::{check_params, FieldEmulationParams},
47        util::{bi_from_limbs, bi_to_limbs},
48    },
49    instructions::{
50        ArithInstructions, AssertionInstructions, AssignmentInstructions, CanonicityInstructions,
51        ControlFlowInstructions, ConversionInstructions, DecompositionInstructions,
52        EqualityInstructions, FieldInstructions, NativeInstructions, PublicInputInstructions,
53        ScalarFieldInstructions, ZeroInstructions,
54    },
55    types::{AssignedBit, AssignedByte, AssignedNative, InnerConstants, InnerValue, Instantiable},
56    utils::util::{bigint_to_fe, fe_to_bigint, modulus},
57};
58
59/// Type for assigned emulated field elements of K over native field F.
60//  - `limb_values` is a vector of assigned cells representing the emulated element in base `base`.
61//  - `limb_bounds` is a vector of BigInt pairs containing a lower bound and an upper bound on the
62//    values of every limb in `limb_values`. Both bounds are inclusive. The lower bound can be
63//    negative; if that is the case, the limb value may have wrapped-around the native modulus below
64//    zero, this will be corrected later in the identities.
65//
66// The integer x represented by limbs [x0, ..., x_{n-1}] is defined as
67//   x := 1 + sum_i base^i xi
68//
69// The +1 shift is introduced so that integer 0 has a unique representation in
70// limbs form, this greatly simplifies comparisons with zero.
71//
72// An AssignedField is well-formed if limb_bounds = (0, base-1).
73//
74// We will perform additions or subtractions with AssignedField even if they
75// are not well-formed, by operating limb-wise and updating the bounds
76// accordingly.
77//
78// However, for multiplication, well-formedness of inputs is a requirement.
79// We can use the `normalize` function to make a AssignedField well-formed as long as the
80// `limb_bounds` have a moderate size.
81#[derive(Clone, Debug)]
82#[must_use]
83pub struct AssignedField<F, K, P>
84where
85    F: PrimeField,
86    K: PrimeField,
87    P: FieldEmulationParams<F, K>,
88{
89    limb_values: Vec<AssignedNative<F>>,
90    limb_bounds: Vec<(BI, BI)>,
91    _marker: PhantomData<(K, P)>,
92}
93
94impl<F, K, P> PartialEq for AssignedField<F, K, P>
95where
96    F: PrimeField,
97    K: PrimeField,
98    P: FieldEmulationParams<F, K>,
99{
100    fn eq(&self, other: &Self) -> bool {
101        self.limb_values
102            .iter()
103            .zip(other.limb_values.iter())
104            .all(|(s, o)| s.cell() == o.cell())
105    }
106}
107
108impl<F: PrimeField, K: PrimeField, P: FieldEmulationParams<F, K>> Eq for AssignedField<F, K, P> {}
109
110impl<F, K, P> Hash for AssignedField<F, K, P>
111where
112    F: PrimeField,
113    K: PrimeField,
114    P: FieldEmulationParams<F, K>,
115{
116    fn hash<H: Hasher>(&self, state: &mut H) {
117        self.limb_values.iter().for_each(|elem| elem.hash(state));
118    }
119}
120
121impl<F, K, P> Instantiable<F> for AssignedField<F, K, P>
122where
123    F: PrimeField,
124    K: PrimeField,
125    P: FieldEmulationParams<F, K>,
126{
127    fn as_public_input(element: &K) -> Vec<F> {
128        // We shift the value of x by 1 for the unique-zero representation.
129        let element_as_bi = fe_to_bigint(&(*element - K::ONE));
130        let base = BI::from(2).pow(P::LOG2_BASE);
131        bi_to_limbs(P::NB_LIMBS, &base, &element_as_bi)
132            .iter()
133            .map(|x| bigint_to_fe::<F>(x))
134            .collect()
135    }
136
137    fn from_public_input(fields: &[F]) -> Option<K> {
138        let base = BI::from(2).pow(P::LOG2_BASE);
139
140        if fields.len() != P::NB_LIMBS as usize {
141            return None;
142        }
143
144        let limbs_as_bi = fields.iter().map(|f| fe_to_bigint(f)).collect::<Vec<_>>();
145        let element_as_bi = bi_from_limbs(&base, &limbs_as_bi) + BI::one();
146        Some(bigint_to_fe::<K>(&element_as_bi))
147    }
148}
149
150impl<F, K, P> InnerValue for AssignedField<F, K, P>
151where
152    F: PrimeField,
153    K: PrimeField,
154    P: FieldEmulationParams<F, K>,
155{
156    type Element = K;
157
158    fn value(&self) -> Value<K> {
159        let bi_limbs = self
160            .limb_values
161            .iter()
162            .zip(self.limb_bounds.iter())
163            .map(|(xi, (lower_bound, _))| {
164                // We add a shift of |lbound| to correct possible wrap-arounds below 0, and
165                // shift back after the conversion from F to BigInt
166                let shift = BI::abs(lower_bound);
167                let fe_shift = bigint_to_fe::<F>(&shift);
168                xi.value().map(|xv| fe_to_bigint::<F>(&(*xv + fe_shift)) - &shift)
169            })
170            .collect::<Vec<_>>();
171        let bi_limbs: Value<Vec<BI>> = Value::from_iter(bi_limbs);
172        let base = BI::from(2).pow(P::LOG2_BASE);
173        bi_limbs.map(|limbs| bigint_to_fe::<K>(&(BI::one() + bi_from_limbs(&base, &limbs))))
174    }
175}
176
177impl<F: PrimeField, K: PrimeField, P> InnerConstants for AssignedField<F, K, P>
178where
179    F: PrimeField,
180    K: PrimeField,
181    P: FieldEmulationParams<F, K>,
182{
183    fn inner_zero() -> K {
184        K::ZERO
185    }
186
187    fn inner_one() -> K {
188        K::ONE
189    }
190}
191
192impl<F, K, P> AssignedField<F, K, P>
193where
194    F: PrimeField,
195    K: PrimeField,
196    P: FieldEmulationParams<F, K>,
197{
198    /// Create an assigned value with well-formed bounds given its limbs.
199    /// This function does not guarantee that the limbs actually meet the
200    /// claimed bounds, it is the responsibility of the caller to make sure
201    /// that was asserted elsewhere.
202    /// DO NOT use this function unless you know what you are doing.
203    pub(crate) fn from_limbs_unsafe(limb_values: Vec<AssignedNative<F>>) -> Self {
204        debug_assert!(limb_values.len() as u32 == P::NB_LIMBS);
205        Self {
206            limb_values,
207            limb_bounds: well_formed_bounds::<F, K, P>(),
208            _marker: PhantomData,
209        }
210    }
211}
212
213#[cfg(any(test, feature = "testing"))]
214impl<F, K, P> Sampleable for AssignedField<F, K, P>
215where
216    F: PrimeField,
217    K: PrimeField,
218    P: FieldEmulationParams<F, K>,
219{
220    fn sample_inner(rng: impl RngCore) -> K {
221        K::random(rng)
222    }
223}
224
225/// Number of columns required by this chip.
226pub fn nb_field_chip_columns<F, K, P>() -> usize
227where
228    F: PrimeField,
229    K: PrimeField,
230    P: FieldEmulationParams<F, K>,
231{
232    P::NB_LIMBS as usize + max(P::NB_LIMBS as usize, 1 + P::moduli().len())
233}
234
235/// Creates a vector of upper-bounds (one per limb), specifiying the maximum
236/// size (log2) that each limb should take for the emulated field element to be
237/// considered well-formed.
238/// All such bounds will be equal to the base, except possibly the bound for
239/// the most significant limb, which may be smaller in order to guarantee that
240/// 0 has a unique representation.
241pub fn well_formed_log2_bounds<F, K, P>() -> Vec<u32>
242where
243    F: PrimeField,
244    K: PrimeField,
245    P: FieldEmulationParams<F, K>,
246{
247    // Let m be the emulated modulus.
248    // We want that m <= base^(nb_limbs - 1) * msl_bound < 2m,
249    // therefore msl_bound must be the first power of 2 higher than or equal to
250    // m / base^(nb_limbs - 1).
251    let m = &modulus::<K>().to_bigint().unwrap();
252    let log2_msl_bound = m.bits() as u32 - (P::NB_LIMBS - 1) * P::LOG2_BASE;
253    let mut bounds = vec![log2_msl_bound];
254    bounds.resize(P::NB_LIMBS as usize, P::LOG2_BASE);
255    bounds.into_iter().rev().collect::<Vec<_>>()
256}
257
258/// Foreign Field Chip configuration.
259// - q_mul is the se to enable the emulated multiplication gate.
260// - q_norm is the selector to enable the normalization gate.
261// - x and y are the inputs (in limbs form).
262// - z is the output (in limbs form).
263// - u, u_mul_bounds, u_norm_bounds, v, vs_mul_bounds and vs_norm_bounds parameters involved in the
264//   identities, refer to [mul_bounds] and [normalization_bounds] for more details.
265#[derive(Clone, Debug)]
266pub struct FieldChipConfig {
267    mul_config: mul::MulConfig,
268    norm_config: norm::NormConfig,
269    /// Column for input x
270    pub x_cols: Vec<Column<Advice>>,
271    /// Column for input y
272    pub y_cols: Vec<Column<Advice>>,
273    /// Column for input/output z
274    pub z_cols: Vec<Column<Advice>>,
275    /// Column for auxiliary value u (quotient by the emulated modulus)
276    pub u_col: Column<Advice>,
277    /// Column for auxiliary values vj (quotients by the auxiliary moduli)
278    pub v_cols: Vec<Column<Advice>>,
279}
280
281/// ['FieldChip'] for operations on field K emulated over native field F.
282#[derive(Clone, Debug)]
283pub struct FieldChip<F, K, P, N>
284where
285    F: PrimeField,
286    K: PrimeField,
287    P: FieldEmulationParams<F, K>,
288    N: NativeInstructions<F>,
289{
290    config: FieldChipConfig,
291    pub(crate) native_gadget: N,
292    _marker: PhantomData<(F, K, P, N)>,
293}
294
295impl<F, K, P> AssignedField<F, K, P>
296where
297    F: PrimeField,
298    K: PrimeField,
299    P: FieldEmulationParams<F, K>,
300{
301    /// The modulus defining the domain of this emulated field element.
302    pub fn modulus(&self) -> BI {
303        modulus::<K>().to_bigint().unwrap().clone()
304    }
305
306    /// Tells whether the given emulated field element is well-formed, i.e., the
307    /// limb_bounds match expected range.
308    ///
309    /// AssignedField whose range is more restricted but included in the
310    /// expected one are also considered well-formed.
311    pub fn is_well_formed(&self) -> bool {
312        self.limb_bounds.iter().zip(well_formed_log2_bounds::<F, K, P>()).all(
313            |((lower, upper), expected_upper)| {
314                assert!(lower <= upper);
315                !BI::is_negative(lower) && upper.bits() <= expected_upper as u64
316            },
317        )
318    }
319
320    /// The limb values associated to the given AssignedField.
321    /// Recall that the integer represented by limbs [x0, ..., x_{n-1}] is
322    /// x := 1 + sum_i base^i xi
323    pub fn limb_values(&self) -> Vec<AssignedNative<F>> {
324        self.limb_values.clone()
325    }
326
327    /// The limb values (in BigInt form) associated to the given AssignedField.
328    pub fn bigint_limbs(&self) -> Value<Vec<BI>> {
329        let limbs = self
330            .limb_values
331            .iter()
332            .zip(self.limb_bounds.iter())
333            .map(|(xi, (lbound, _))| {
334                // We add a shift of |lbound| to correct possible wrap-arounds below 0, and
335                // shift back after the conversion from F to BigInt
336                let shift = BI::abs(lbound);
337                let fe_shift = bigint_to_fe::<F>(&shift);
338                xi.value().map(|xv| fe_to_bigint::<F>(&(*xv + fe_shift)) - &shift)
339            })
340            .collect::<Vec<_>>();
341        Value::from_iter(limbs)
342    }
343}
344
345/// A vector of `NB_LIMBS` bounds of the form [0, base), except for possibly the
346/// most significant limb, which may be of the form [0, 2^k) with 2^k <= base.
347/// This is so that there exist emulated field elements with a unique
348/// representation (even if some of them have two representations).
349fn well_formed_bounds<F, K, P>() -> Vec<(BI, BI)>
350where
351    F: PrimeField,
352    K: PrimeField,
353    P: FieldEmulationParams<F, K>,
354{
355    well_formed_log2_bounds::<F, K, P>()
356        .into_iter()
357        .map(|log2_base| (BI::zero(), BI::from(2).pow(log2_base) - BI::one()))
358        .collect()
359}
360
361// The limbs of emulated zero.
362fn limbs_of_zero<F, K, P>() -> Vec<BI>
363where
364    F: PrimeField,
365    K: PrimeField,
366    P: FieldEmulationParams<F, K>,
367{
368    bi_to_limbs(
369        P::NB_LIMBS,
370        &BI::from(2).pow(P::LOG2_BASE),
371        &(modulus::<K>().to_bigint().unwrap() - BI::one()),
372    )
373}
374
375impl<F, K, P, N> Chip<F> for FieldChip<F, K, P, N>
376where
377    F: PrimeField,
378    K: PrimeField,
379    P: FieldEmulationParams<F, K>,
380    N: NativeInstructions<F>,
381{
382    type Config = FieldChipConfig;
383    type Loaded = ();
384    fn config(&self) -> &Self::Config {
385        &self.config
386    }
387    fn loaded(&self) -> &Self::Loaded {
388        &()
389    }
390}
391
392impl<F, K, P, N> AssignmentInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
393where
394    F: PrimeField,
395    K: PrimeField,
396    P: FieldEmulationParams<F, K>,
397    N: NativeInstructions<F>,
398{
399    fn assign(
400        &self,
401        layouter: &mut impl Layouter<F>,
402        x: Value<K>,
403    ) -> Result<AssignedField<F, K, P>, Error> {
404        let base = BI::from(2).pow(P::LOG2_BASE);
405        // We shift the value of x by 1, remember that limbs {xi}_i represent integer
406        //   1 + sum_i base^i xi
407        let x = x.map(|v| {
408            let bi = fe_to_bigint(&(v - K::ONE));
409            bi_to_limbs(P::NB_LIMBS, &base, &bi)
410        });
411
412        // Range-check the cells in the range [0, base)
413        let x_cells = (0..P::NB_LIMBS)
414            .map(|i| x.clone().map(|limbs| bigint_to_fe::<F>(&limbs[i as usize])))
415            .zip(well_formed_log2_bounds::<F, K, P>().iter())
416            .map(|(xi_value, log2_bound)| {
417                self.native_gadget.assign_lower_than_fixed(
418                    layouter,
419                    xi_value,
420                    &(BigUint::one() << *log2_bound),
421                )
422            })
423            .collect::<Result<Vec<_>, Error>>()?;
424
425        Ok(AssignedField::<F, K, P> {
426            limb_values: x_cells,
427            limb_bounds: well_formed_bounds::<F, K, P>(),
428            _marker: PhantomData,
429        })
430    }
431
432    fn assign_fixed(
433        &self,
434        layouter: &mut impl Layouter<F>,
435        constant: K,
436    ) -> Result<AssignedField<F, K, P>, Error> {
437        let base = BI::from(2).pow(P::LOG2_BASE);
438        // We shift the value of x by 1, remember that limbs {xi}_i represent integer
439        //   1 + sum_i base^i xi
440        let constant = fe_to_bigint(&(constant - K::ONE));
441        let constant_limbs = bi_to_limbs(P::NB_LIMBS, &base, &constant);
442        let constant_cells = constant_limbs
443            .iter()
444            .map(|x| self.native_gadget.assign_fixed(layouter, bigint_to_fe::<F>(x)))
445            .collect::<Result<Vec<_>, _>>()?;
446
447        // All limbs will be in the range [0, base) by construction, no range-checks are
448        // needed.
449        // WARNING: We use "loose" bounds (`well_formed_bounds::<F, K, P>()`) here even
450        // if we know for certain that the cells contain a constant. This is to
451        // avoid a potential completeness issue when calling `assert_equal`.
452        // (Using tight bounds could result in an unsatisfiable equal assertion between
453        // two equal field elements: one in canonical form and the other one in the
454        // non-canonical (but well-formed) form, making the assertion fail when it
455        // should not.)
456        Ok(AssignedField::<F, K, P> {
457            limb_values: constant_cells,
458            limb_bounds: well_formed_bounds::<F, K, P>(),
459            _marker: PhantomData,
460        })
461    }
462}
463
464// This conversion should be treated as opaque, it is useful for dealing with
465// public inputs.
466impl<F, K, P> From<AssignedField<F, K, P>> for Vec<AssignedNative<F>>
467where
468    F: PrimeField,
469    K: PrimeField,
470    P: FieldEmulationParams<F, K>,
471{
472    fn from(x: AssignedField<F, K, P>) -> Self {
473        x.limb_values()
474    }
475}
476
477impl<F, K, P, N> PublicInputInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
478where
479    F: PrimeField,
480    K: PrimeField,
481    P: FieldEmulationParams<F, K>,
482    N: NativeInstructions<F>,
483{
484    fn as_public_input(
485        &self,
486        layouter: &mut impl Layouter<F>,
487        assigned: &AssignedField<F, K, P>,
488    ) -> Result<Vec<AssignedNative<F>>, Error> {
489        let assigned = self.normalize(layouter, assigned)?;
490        Ok(assigned.limb_values)
491    }
492
493    fn constrain_as_public_input(
494        &self,
495        layouter: &mut impl Layouter<F>,
496        assigned: &AssignedField<F, K, P>,
497    ) -> Result<(), Error> {
498        self.as_public_input(layouter, assigned)?
499            .iter()
500            .try_for_each(|c| self.native_gadget.constrain_as_public_input(layouter, c))
501    }
502
503    fn assign_as_public_input(
504        &self,
505        layouter: &mut impl Layouter<F>,
506        value: Value<K>,
507    ) -> Result<AssignedField<F, K, P>, Error> {
508        let base = BI::from(2).pow(P::LOG2_BASE);
509        // We subtract one due to the unique-zero representation.
510        let x = value.map(|v| bi_to_limbs(P::NB_LIMBS, &base, &fe_to_bigint(&(v - K::ONE))));
511        let limbs = (0..P::NB_LIMBS)
512            .map(|i| x.clone().map(|limbs| bigint_to_fe::<F>(&limbs[i as usize])))
513            .collect::<Vec<_>>();
514        // We can skip all range-checks given that the assigned field element will be
515        // constrained with public inputs, thus that structure will be enforced anyway.
516        let assigned_limbs = self.native_gadget.assign_many(layouter, &limbs)?;
517        let assigned_field = AssignedField::<F, K, P> {
518            limb_values: assigned_limbs,
519            limb_bounds: well_formed_bounds::<F, K, P>(),
520            _marker: PhantomData,
521        };
522        self.constrain_as_public_input(layouter, &assigned_field)?;
523        Ok(assigned_field)
524    }
525}
526
527impl<F, K, P, N> AssertionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
528where
529    F: PrimeField,
530    K: PrimeField,
531    P: FieldEmulationParams<F, K>,
532    N: NativeInstructions<F>,
533{
534    fn assert_equal(
535        &self,
536        layouter: &mut impl Layouter<F>,
537        x: &AssignedField<F, K, P>,
538        y: &AssignedField<F, K, P>,
539    ) -> Result<(), Error> {
540        // We normalize the x and y before comparing them.
541        // Even though field elements may admit several representations in
542        // well-formed limbs form, an honest prover will use the canonical one,
543        // which allows them to always pass the following equality assertion if the two
544        // emulated field elements are indeed equal.
545        let x = self.normalize(layouter, x)?;
546        let y = self.normalize(layouter, y)?;
547        x.limb_values
548            .iter()
549            .zip(y.limb_values.iter())
550            .map(|(xi, yi)| self.native_gadget.assert_equal(layouter, xi, yi))
551            .collect::<Result<Vec<_>, _>>()?;
552        Ok(())
553    }
554
555    fn assert_not_equal(
556        &self,
557        layouter: &mut impl Layouter<F>,
558        x: &AssignedField<F, K, P>,
559        y: &AssignedField<F, K, P>,
560    ) -> Result<(), Error> {
561        let diff = self.sub(layouter, x, y)?;
562        self.assert_non_zero(layouter, &diff)
563    }
564
565    fn assert_equal_to_fixed(
566        &self,
567        layouter: &mut impl Layouter<F>,
568        x: &AssignedField<F, K, P>,
569        constant: K,
570    ) -> Result<(), Error> {
571        // We normalize x before comparing it to the constant.
572        // Even though field elements may admit several representations in
573        // well-formed limbs form, an honest prover will use the canonical one,
574        // which allows them to always pass the following equality assertion if the x
575        // is indeed equal to the given constant.
576        let x = self.normalize(layouter, x)?;
577        let constant_limbs = {
578            let constant = fe_to_bigint(&(constant - K::ONE));
579            let base = BI::from(2).pow(P::LOG2_BASE);
580            bi_to_limbs(P::NB_LIMBS, &base, &constant)
581        };
582        x.limb_values
583            .iter()
584            .zip(constant_limbs.iter())
585            .map(|(xi, ki)| {
586                self.native_gadget.assert_equal_to_fixed(layouter, xi, bigint_to_fe::<F>(ki))
587            })
588            .collect::<Result<Vec<_>, _>>()?;
589        Ok(())
590    }
591
592    fn assert_not_equal_to_fixed(
593        &self,
594        layouter: &mut impl Layouter<F>,
595        x: &AssignedField<F, K, P>,
596        constant: K,
597    ) -> Result<(), Error> {
598        let diff = self.add_constant(layouter, x, -constant)?;
599        self.assert_non_zero(layouter, &diff)
600    }
601}
602
603impl<F, K, P, N> EqualityInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
604where
605    F: PrimeField,
606    K: PrimeField,
607    P: FieldEmulationParams<F, K>,
608    N: NativeInstructions<F>,
609{
610    fn is_equal(
611        &self,
612        layouter: &mut impl Layouter<F>,
613        x: &AssignedField<F, K, P>,
614        y: &AssignedField<F, K, P>,
615    ) -> Result<AssignedBit<F>, Error> {
616        let diff = self.sub(layouter, x, y)?;
617        self.is_zero(layouter, &diff)
618    }
619
620    fn is_not_equal(
621        &self,
622        layouter: &mut impl Layouter<F>,
623        x: &AssignedField<F, K, P>,
624        y: &AssignedField<F, K, P>,
625    ) -> Result<AssignedBit<F>, Error> {
626        let b = self.is_equal(layouter, x, y)?;
627        self.native_gadget.not(layouter, &b)
628    }
629
630    fn is_equal_to_fixed(
631        &self,
632        layouter: &mut impl Layouter<F>,
633        x: &AssignedField<F, K, P>,
634        constant: <AssignedField<F, K, P> as InnerValue>::Element,
635    ) -> Result<AssignedBit<F>, Error> {
636        let diff = self.add_constant(layouter, x, -constant)?;
637        self.is_zero(layouter, &diff)
638    }
639
640    fn is_not_equal_to_fixed(
641        &self,
642        layouter: &mut impl Layouter<F>,
643        x: &AssignedField<F, K, P>,
644        constant: <AssignedField<F, K, P> as InnerValue>::Element,
645    ) -> Result<AssignedBit<F>, Error> {
646        let b = self.is_equal_to_fixed(layouter, x, constant)?;
647        self.native_gadget.not(layouter, &b)
648    }
649}
650
651impl<F, K, P, N> ZeroInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
652where
653    F: PrimeField,
654    K: PrimeField,
655    P: FieldEmulationParams<F, K>,
656    N: NativeInstructions<F>,
657{
658    fn assert_non_zero(
659        &self,
660        layouter: &mut impl Layouter<F>,
661        x: &AssignedField<F, K, P>,
662    ) -> Result<(), Error> {
663        let b = self.is_zero(layouter, x)?;
664        self.native_gadget.assert_equal_to_fixed(layouter, &b, false)
665    }
666
667    fn is_zero(
668        &self,
669        layouter: &mut impl Layouter<F>,
670        x: &AssignedField<F, K, P>,
671    ) -> Result<AssignedBit<F>, Error> {
672        // Zero has a unique representation in limbs form, we can simply make sure that
673        // the limbs of x are all equal to the limbs of zero.
674        let x = self.normalize(layouter, x)?;
675        let bs = x
676            .limb_values
677            .iter()
678            .zip(limbs_of_zero::<F, K, P>().iter())
679            .map(|(xi, ci)| {
680                self.native_gadget.is_equal_to_fixed(layouter, xi, bigint_to_fe::<F>(ci))
681            })
682            .collect::<Result<Vec<_>, _>>()?;
683        self.native_gadget.and(layouter, &bs)
684    }
685}
686
687impl<F, K, P, N> ControlFlowInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
688where
689    F: PrimeField,
690    K: PrimeField,
691    P: FieldEmulationParams<F, K>,
692    N: NativeInstructions<F>,
693{
694    fn select(
695        &self,
696        layouter: &mut impl Layouter<F>,
697        cond: &AssignedBit<F>,
698        x: &AssignedField<F, K, P>,
699        y: &AssignedField<F, K, P>,
700    ) -> Result<AssignedField<F, K, P>, Error> {
701        let z_limb_values = x
702            .limb_values
703            .iter()
704            .zip(y.limb_values.iter())
705            .map(|(xi, yi)| self.native_gadget.select(layouter, cond, xi, yi))
706            .collect::<Result<Vec<_>, _>>()?;
707        let z_limb_bounds = x
708            .limb_bounds
709            .iter()
710            .zip(y.limb_bounds.iter())
711            .map(|(xi_bounds, yi_bounds)| {
712                (
713                    min(&xi_bounds.0, &yi_bounds.0).clone(),
714                    max(&xi_bounds.1, &yi_bounds.1).clone(),
715                )
716            })
717            .collect::<Vec<_>>();
718        Ok(AssignedField::<F, K, P> {
719            limb_values: z_limb_values,
720            limb_bounds: z_limb_bounds,
721            _marker: PhantomData,
722        })
723    }
724}
725
726impl<F, K, P, N> ArithInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
727where
728    F: PrimeField,
729    K: PrimeField,
730    P: FieldEmulationParams<F, K>,
731    N: NativeInstructions<F>,
732{
733    fn linear_combination(
734        &self,
735        layouter: &mut impl Layouter<F>,
736        terms: &[(K, AssignedField<F, K, P>)],
737        constant: K,
738    ) -> Result<AssignedField<F, K, P>, Error> {
739        // We fold over mul_by_constant and add and only normalize at the end.
740        let init: AssignedField<F, K, P> = self.assign_fixed(layouter, constant)?;
741        let res = terms.iter().try_fold(init, |acc, (c, x)| {
742            let prod = self.mul_by_constant(layouter, x, *c)?;
743            self.add(layouter, &acc, &prod)
744        })?;
745        self.normalize_if_approaching_limit(layouter, &res)
746    }
747
748    fn add(
749        &self,
750        layouter: &mut impl Layouter<F>,
751        x: &AssignedField<F, K, P>,
752        y: &AssignedField<F, K, P>,
753    ) -> Result<AssignedField<F, K, P>, Error> {
754        let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
755
756        if x == &zero {
757            return Ok(y.clone());
758        }
759
760        if y == &zero {
761            return Ok(x.clone());
762        }
763
764        // Note that x := 1 + sum_i base^i xi and y := 1 + sum_i base^i yi.
765        // Thus z = (x + y) is equal to 2 + sum_i base^i (xi + yi).
766        // Observe there is a 2 instead of the implicit 1, thus we cannot simply add the
767        // limbs of x and y pair-wise. We also need to add a factor of +1 to the
768        // least-significant limb to account for this difference.
769
770        let mut constants = vec![BI::one()];
771        constants.resize(P::NB_LIMBS as usize, BI::zero());
772
773        let z_limb_values = x
774            .limb_values
775            .iter()
776            .zip(y.limb_values.iter())
777            .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
778            .map(|((xi, yi), ci)| {
779                self.native_gadget.linear_combination(
780                    layouter,
781                    &[(F::ONE, xi.clone()), (F::ONE, yi.clone())],
782                    ci,
783                )
784            })
785            .collect::<Result<Vec<_>, _>>()?;
786
787        let z = AssignedField::<F, K, P> {
788            limb_values: z_limb_values,
789            limb_bounds: x
790                .limb_bounds
791                .iter()
792                .zip(y.limb_bounds.iter())
793                .zip(constants.iter())
794                .map(|((xi_bounds, yi_bounds), ci)| {
795                    (
796                        &xi_bounds.0 + &yi_bounds.0 + ci,
797                        &xi_bounds.1 + &yi_bounds.1 + ci,
798                    )
799                })
800                .collect(),
801            _marker: PhantomData,
802        };
803        self.normalize_if_approaching_limit(layouter, &z)
804    }
805
806    fn sub(
807        &self,
808        layouter: &mut impl Layouter<F>,
809        x: &AssignedField<F, K, P>,
810        y: &AssignedField<F, K, P>,
811    ) -> Result<AssignedField<F, K, P>, Error> {
812        let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
813
814        if y == &zero {
815            return Ok(x.clone());
816        }
817
818        // Note that x := 1 + sum_i base^i xi and y := 1 + sum_i base^i yi.
819        // Thus z = (x - y) is equal to 0 + sum_i base^i (xi + yi).
820        // Observe there is a 0 instead of the implicit 1, thus we cannot simply
821        // subtract the limbs of x and y pair-wise. We also need to add a factor
822        // of -1 to the least-significant limb to account for this difference.
823
824        let mut constants = vec![BI::from(-1)];
825        constants.resize(P::NB_LIMBS as usize, BI::zero());
826
827        let z_limb_values = x
828            .limb_values
829            .iter()
830            .zip(y.limb_values.iter())
831            .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
832            .map(|((xi, yi), ci)| {
833                self.native_gadget.linear_combination(
834                    layouter,
835                    &[(F::ONE, xi.clone()), (-F::ONE, yi.clone())],
836                    ci,
837                )
838            })
839            .collect::<Result<Vec<_>, _>>()?;
840
841        let z = AssignedField::<F, K, P> {
842            limb_values: z_limb_values,
843            limb_bounds: x
844                .limb_bounds
845                .iter()
846                .zip(y.limb_bounds.iter())
847                .zip(constants.iter())
848                .map(|((xi_bounds, yi_bounds), ci)| {
849                    (
850                        &xi_bounds.0 - &yi_bounds.1 + ci,
851                        &xi_bounds.1 - &yi_bounds.0 + ci,
852                    )
853                })
854                .collect(),
855            _marker: PhantomData,
856        };
857        self.normalize_if_approaching_limit(layouter, &z)
858    }
859
860    fn mul(
861        &self,
862        layouter: &mut impl Layouter<F>,
863        x: &AssignedField<F, K, P>,
864        y: &AssignedField<F, K, P>,
865        multiplying_constant: Option<K>,
866    ) -> Result<AssignedField<F, K, P>, Error> {
867        let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
868        let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
869
870        if x == &zero || y == &zero {
871            return Ok(zero);
872        }
873
874        if x == &one {
875            return Ok(y.clone());
876        }
877
878        if y == &one {
879            return Ok(x.clone());
880        }
881
882        let y = match multiplying_constant {
883            None => y.clone(),
884            Some(k) => self.mul_by_constant(layouter, y, k)?,
885        };
886        self.assign_mul(layouter, x, &y, false)
887    }
888
889    fn div(
890        &self,
891        layouter: &mut impl Layouter<F>,
892        x: &AssignedField<F, K, P>,
893        y: &AssignedField<F, K, P>,
894    ) -> Result<AssignedField<F, K, P>, Error> {
895        let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
896        if y == &one {
897            return Ok(x.clone());
898        }
899
900        let y = self.normalize(layouter, y)?;
901        self.assert_non_zero(layouter, &y)?;
902        self.assign_mul(layouter, x, &y, true)
903    }
904
905    fn neg(
906        &self,
907        layouter: &mut impl Layouter<F>,
908        x: &AssignedField<F, K, P>,
909    ) -> Result<AssignedField<F, K, P>, Error> {
910        let zero: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ZERO)?;
911
912        if x == &zero {
913            return Ok(zero);
914        }
915
916        // Note that x := 1 + sum_i base^i xi.
917        // Thus z = -x is equal to -1 + sum_i base^i (xi + yi).
918        // Observe there is a -1 instead of the implicit 1, thus we cannot simply negate
919        // the limbs of x. We also need to add a factor of -2 to the least-significant
920        // limb to account for this difference.
921
922        let mut constants = vec![BI::from(-2)];
923        constants.resize(P::NB_LIMBS as usize, BI::zero());
924
925        let z_limb_values = x
926            .limb_values
927            .iter()
928            .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
929            .map(|(xi, ci)| {
930                self.native_gadget.linear_combination(layouter, &[(-F::ONE, xi.clone())], ci)
931            })
932            .collect::<Result<Vec<_>, _>>()?;
933
934        let z = AssignedField::<F, K, P> {
935            limb_values: z_limb_values,
936            limb_bounds: x
937                .limb_bounds
938                .iter()
939                .zip(constants.iter())
940                .map(|(xi_bounds, ci)| (-&xi_bounds.1 + ci, -&xi_bounds.0 + ci))
941                .collect(),
942            _marker: PhantomData,
943        };
944        self.normalize_if_approaching_limit(layouter, &z)
945    }
946
947    fn inv(
948        &self,
949        layouter: &mut impl Layouter<F>,
950        x: &AssignedField<F, K, P>,
951    ) -> Result<AssignedField<F, K, P>, Error> {
952        let one: AssignedField<F, K, P> = self.assign_fixed(layouter, K::ONE)?;
953
954        if x == &one {
955            return Ok(one);
956        }
957
958        // We do not need to assert that x != 0 because the equation enforced by
959        // [assign_mul] will be 1 = z * x, which is unsatisfiable if x = 0.
960        self.assign_mul(layouter, &one, x, true)
961    }
962
963    fn inv0(
964        &self,
965        layouter: &mut impl Layouter<F>,
966        x: &AssignedField<F, K, P>,
967    ) -> Result<AssignedField<F, K, P>, Error> {
968        let is_zero = self.is_zero(layouter, x)?;
969        let zero = self.assign_fixed(layouter, K::ZERO)?;
970        let one = self.assign_fixed(layouter, K::ONE)?;
971        let invertible = self.select(layouter, &is_zero, &one, x)?;
972        let inverse = self.assign_mul(layouter, &one, &invertible, true)?;
973        self.select(layouter, &is_zero, &zero, &inverse)
974    }
975
976    fn add_constant(
977        &self,
978        layouter: &mut impl Layouter<F>,
979        x: &AssignedField<F, K, P>,
980        k: K,
981    ) -> Result<AssignedField<F, K, P>, Error> {
982        // The following is more efficient than simply:
983        //
984        //   let constant = self.assign_fixed(layouter, k)?;
985        //   self.add(layouter, x, &assign_fixed)
986        //
987        // as we do not create cells for the constant limbs.
988
989        if k.is_zero().into() {
990            return Ok(x.clone());
991        }
992
993        let base = BI::from(2).pow(P::LOG2_BASE);
994        let k_limbs = bi_to_limbs(P::NB_LIMBS, &base, &fe_to_bigint(&k));
995
996        let z_limb_values = {
997            self.native_gadget.add_constants(
998                layouter,
999                &x.limb_values,
1000                &k_limbs.iter().map(bigint_to_fe::<F>).collect::<Vec<_>>(),
1001            )?
1002        };
1003
1004        let z = AssignedField::<F, K, P> {
1005            limb_values: z_limb_values,
1006            limb_bounds: x
1007                .limb_bounds
1008                .iter()
1009                .zip(k_limbs.iter())
1010                .map(|(xi_bound, ki)| (&xi_bound.0 + ki, &xi_bound.1 + ki))
1011                .collect(),
1012            _marker: PhantomData,
1013        };
1014        self.normalize_if_approaching_limit(layouter, &z)
1015    }
1016
1017    fn mul_by_constant(
1018        &self,
1019        layouter: &mut impl Layouter<F>,
1020        x: &AssignedField<F, K, P>,
1021        k: K,
1022    ) -> Result<AssignedField<F, K, P>, Error> {
1023        if k.is_zero().into() {
1024            return self.assign_fixed(layouter, K::ZERO);
1025        }
1026
1027        if k == K::ONE {
1028            return Ok(x.clone());
1029        }
1030
1031        // If the constant is too big, we should multiply normally instead.
1032        // This threshold is just a heuristic, it will allow us to perform about 1000
1033        // sums after this multiplication without normalization.
1034        // We will get an error when compiling the circuit if the max_limb_bound is
1035        // violated, so the choice of this threshold is not critical for soundness.
1036        let threshold =
1037            P::max_limb_bound().div_floor(&(BI::from(1000) * BI::from(2).pow(P::LOG2_BASE)));
1038        if fe_to_bigint(&k) > threshold {
1039            let assigned_k = self.assign_fixed(layouter, k)?;
1040            return self.assign_mul(layouter, x, &assigned_k, false);
1041        }
1042
1043        // At this point we know that k is small enough (k <= threshold) thus we can
1044        // proceed by multiplying the constant by every limb.
1045
1046        // Note that x := 1 + sum_i base^i xi.
1047        // Thus z = k * x is equal to k + sum_i base^i (k * xi).
1048        // Observe there is a k instead of the implicit 1, thus we cannot simply
1049        // multiply the limbs of x by k. We also need to add a factor of (k-1)
1050        // to the least-significant limb to account for this difference.
1051
1052        // Yes, we convert it to F (the wrong - but native - field), but this is fine
1053        // because the constant has been verified to be small.
1054        let k_as_bigint = fe_to_bigint(&k);
1055        let kv = bigint_to_fe::<F>(&k_as_bigint);
1056        // We've also checked k != 0 and k != 1, so it is fine to subtract one here.
1057        // (for the unique-zero representation).
1058        let mut constants = vec![k_as_bigint.clone() - BI::one()];
1059        constants.resize(P::NB_LIMBS as usize, BI::zero());
1060
1061        let z_limb_values = x
1062            .limb_values
1063            .iter()
1064            .zip(constants.iter().map(|ci| bigint_to_fe::<F>(ci)))
1065            .map(|(xi, ci)| {
1066                self.native_gadget.linear_combination(layouter, &[(kv, xi.clone())], ci)
1067            })
1068            .collect::<Result<Vec<_>, _>>()?;
1069
1070        let limb_bounds = x
1071            .limb_bounds
1072            .iter()
1073            .zip(constants.iter())
1074            .map(|(xi_bounds, ci)| {
1075                (
1076                    &xi_bounds.0 * k_as_bigint.clone() + ci,
1077                    &xi_bounds.1 * k_as_bigint.clone() + ci,
1078                )
1079            })
1080            .collect();
1081
1082        let z = AssignedField::<F, K, P> {
1083            limb_values: z_limb_values,
1084            limb_bounds,
1085            _marker: PhantomData,
1086        };
1087        self.normalize_if_approaching_limit(layouter, &z)
1088    }
1089}
1090
1091impl<F, K, P, N> FieldInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1092where
1093    F: PrimeField,
1094    K: PrimeField,
1095    P: FieldEmulationParams<F, K>,
1096    N: NativeInstructions<F>,
1097{
1098    fn order(&self) -> BigUint {
1099        modulus::<K>()
1100    }
1101}
1102
1103impl<F, K, P, N> ScalarFieldInstructions<F> for FieldChip<F, K, P, N>
1104where
1105    F: PrimeField,
1106    K: PrimeField,
1107    P: FieldEmulationParams<F, K>,
1108    N: NativeInstructions<F>,
1109{
1110    type Scalar = AssignedField<F, K, P>;
1111}
1112
1113impl<F, K, P, N> DecompositionInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1114where
1115    F: PrimeField,
1116    K: PrimeField,
1117    P: FieldEmulationParams<F, K>,
1118    N: NativeInstructions<F>,
1119{
1120    fn assigned_to_le_bits(
1121        &self,
1122        layouter: &mut impl Layouter<F>,
1123        x: &AssignedField<F, K, P>,
1124        nb_bits: Option<usize>,
1125        enforce_canonical: bool,
1126    ) -> Result<Vec<AssignedBit<F>>, Error> {
1127        // Add one to account for the extra +1 in the unique-zero representation.
1128        let mut x = self.add_constant(layouter, x, K::ONE)?;
1129        if enforce_canonical {
1130            x = self.make_canonical(layouter, &x)?;
1131        };
1132        let mut bits = vec![];
1133        x.limb_values
1134            .iter()
1135            .zip(well_formed_log2_bounds::<F, K, P>().iter())
1136            .map(|(cell, log2_bound)| {
1137                self.native_gadget.assigned_to_le_bits(
1138                    layouter,
1139                    cell,
1140                    Some(*log2_bound as usize),
1141                    true,
1142                )
1143            })
1144            .collect::<Result<Vec<_>, _>>()?
1145            .into_iter()
1146            .for_each(|new_bits| bits.extend(new_bits));
1147
1148        // Drop the most significant bits up to the desired length, but make sure
1149        // they encode 0.
1150        let nb_bits = nb_bits.unwrap_or(K::NUM_BITS as usize);
1151        bits[nb_bits..]
1152            .iter()
1153            .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, false))?;
1154        let bits = bits[0..nb_bits].to_vec();
1155        if enforce_canonical && nb_bits >= K::NUM_BITS as usize {
1156            let canonical = self.is_canonical(layouter, &bits)?;
1157            self.assert_equal_to_fixed(layouter, &canonical, true)?;
1158        }
1159        Ok(bits)
1160    }
1161
1162    fn assigned_to_le_bytes(
1163        &self,
1164        layouter: &mut impl Layouter<F>,
1165        x: &AssignedField<F, K, P>,
1166        nb_bytes: Option<usize>,
1167    ) -> Result<Vec<AssignedByte<F>>, Error> {
1168        let nb_bytes = nb_bytes.unwrap_or(K::NUM_BITS.div_ceil(8) as usize);
1169        // The following could be further optimzed when 8 divides LOG2_BASE.
1170        let bits = self.assigned_to_le_bits(layouter, x, Some(nb_bytes * 8), true)?;
1171        let bytes = bits
1172            .chunks(8)
1173            .map(|chunk| {
1174                let terms = chunk
1175                    .iter()
1176                    .enumerate()
1177                    .map(|(i, bit)| (F::from(1 << i), bit.clone().into()))
1178                    .collect::<Vec<_>>();
1179                let byte = self.native_gadget.linear_combination(layouter, &terms, F::ZERO)?;
1180                self.native_gadget.convert_unsafe(layouter, &byte)
1181            })
1182            .collect::<Result<Vec<AssignedByte<F>>, Error>>()?;
1183
1184        // Drop the most significant bytes up to the desired length, but make sure
1185        // they encode 0.
1186        bytes[nb_bytes..]
1187            .iter()
1188            .try_for_each(|byte| self.native_gadget.assert_equal_to_fixed(layouter, byte, 0u8))?;
1189        Ok(bytes[0..nb_bytes].to_vec())
1190    }
1191
1192    fn assigned_from_le_bits(
1193        &self,
1194        layouter: &mut impl Layouter<F>,
1195        bits: &[AssignedBit<F>],
1196    ) -> Result<AssignedField<F, K, P>, Error> {
1197        let mut coeff = K::ONE;
1198        let mut terms = vec![];
1199        for chunk in bits.chunks(P::LOG2_BASE as usize) {
1200            let mut native_coeff = F::ONE;
1201            let mut native_terms = vec![];
1202            for b in chunk.iter() {
1203                let bit: AssignedNative<F> = b.clone().into();
1204                native_terms.push((native_coeff, bit));
1205                native_coeff = native_coeff + native_coeff;
1206            }
1207            let term = {
1208                let limb =
1209                    self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1210                self.assigned_field_from_limb(layouter, &limb)?
1211            };
1212            terms.push((coeff, term));
1213            coeff = bigint_to_fe::<K>(&BI::from(2).pow(P::LOG2_BASE)) * coeff;
1214        }
1215        let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1216        self.normalize(layouter, &x)
1217    }
1218
1219    fn assigned_from_le_bytes(
1220        &self,
1221        layouter: &mut impl Layouter<F>,
1222        bytes: &[AssignedByte<F>],
1223    ) -> Result<AssignedField<F, K, P>, Error> {
1224        let mut coeff = K::ONE;
1225        let mut terms = vec![];
1226        let nb_bytes_per_chunk = P::LOG2_BASE / 8;
1227        for chunk in bytes.chunks(nb_bytes_per_chunk as usize) {
1228            let mut native_coeff = F::ONE;
1229            let mut native_terms = vec![];
1230            for b in chunk.iter() {
1231                let byte: AssignedNative<F> = b.clone().into();
1232                native_terms.push((native_coeff, byte));
1233                native_coeff = F::from(256) * native_coeff;
1234            }
1235            let term = {
1236                let limb =
1237                    self.native_gadget.linear_combination(layouter, &native_terms, F::ZERO)?;
1238                self.assigned_field_from_limb(layouter, &limb)?
1239            };
1240            terms.push((coeff, term));
1241            coeff = bigint_to_fe::<K>(&BI::from(2).pow(8 * nb_bytes_per_chunk)) * coeff;
1242        }
1243        let x = self.linear_combination(layouter, &terms, K::ZERO)?;
1244        self.normalize(layouter, &x)
1245    }
1246
1247    fn assigned_to_le_chunks(
1248        &self,
1249        layouter: &mut impl Layouter<F>,
1250        x: &AssignedField<F, K, P>,
1251        nb_bits_per_chunk: usize,
1252        nb_chunks: Option<usize>,
1253    ) -> Result<Vec<AssignedNative<F>>, Error> {
1254        assert!(nb_bits_per_chunk < F::NUM_BITS as usize);
1255        if P::LOG2_BASE % (nb_bits_per_chunk as u32) == 0 {
1256            let nb_chunks_per_limb = (P::LOG2_BASE / (nb_bits_per_chunk as u32)) as usize;
1257            let mut nb_missing_chunks =
1258                nb_chunks.unwrap_or(nb_chunks_per_limb * P::NB_LIMBS as usize);
1259            // Add one to account for the extra +1 in the unique-zero representation.
1260            let x = self.add_constant(layouter, x, K::ONE)?;
1261            let x = self.normalize(layouter, &x)?;
1262            let chunks = x
1263                .limb_values
1264                .iter()
1265                .map(|limb| {
1266                    let nb_chunks_on_this_limb = min(nb_missing_chunks, nb_chunks_per_limb);
1267                    nb_missing_chunks -= nb_chunks_on_this_limb;
1268                    self.native_gadget.assigned_to_le_chunks(
1269                        layouter,
1270                        limb,
1271                        nb_bits_per_chunk,
1272                        Some(nb_chunks_on_this_limb),
1273                    )
1274                })
1275                .collect::<Result<Vec<_>, Error>>()?
1276                .concat();
1277            assert_eq!(nb_missing_chunks, 0);
1278            Ok(chunks)
1279        }
1280        // When nb_bits_per_chunk does not divide P::LOG2_BASE we cannot proceed as above,
1281        // let's split in bits and then aggregate chunks, this is a bit less efficient.
1282        else {
1283            let bits = self.assigned_to_le_bits(layouter, x, None, false)?;
1284            bits.chunks(nb_bits_per_chunk)
1285                .map(|bits_of_chunk| {
1286                    self.native_gadget.assigned_from_le_bits(layouter, bits_of_chunk)
1287                })
1288                .collect::<Result<Vec<_>, Error>>()
1289        }
1290    }
1291}
1292
1293impl<F, K, P, N> FieldChip<F, K, P, N>
1294where
1295    F: PrimeField,
1296    K: PrimeField,
1297    P: FieldEmulationParams<F, K>,
1298    N: NativeInstructions<F>,
1299{
1300    /// Given config creates new emulated field chip.
1301    pub fn new(config: &FieldChipConfig, native_gadget: &N) -> Self {
1302        Self {
1303            config: config.clone(),
1304            native_gadget: native_gadget.clone(),
1305            _marker: PhantomData,
1306        }
1307    }
1308
1309    /// Configures the emulated field chip.
1310    /// `advice_columns` should contain at least as many columns as this chip
1311    /// requires, namely `nb_field_chip_columns::<P>()`.
1312    pub fn configure(
1313        meta: &mut ConstraintSystem<F>,
1314        advice_columns: &[Column<Advice>],
1315    ) -> FieldChipConfig {
1316        check_params::<F, K, P>();
1317
1318        let nb_limbs = P::NB_LIMBS;
1319        let x_cols = advice_columns[..(nb_limbs as usize)].to_vec();
1320        let y_cols = x_cols.clone();
1321        let z_cols = advice_columns[(nb_limbs as usize)..(2 * nb_limbs as usize)].to_vec();
1322
1323        x_cols.iter().chain(z_cols.iter()).for_each(|&col| meta.enable_equality(col));
1324
1325        let u_col = advice_columns[nb_limbs as usize];
1326        let v_cols = advice_columns
1327            [(nb_limbs as usize + 1)..(nb_limbs as usize + 1 + P::moduli().len())]
1328            .to_vec();
1329
1330        let mul_config = MulConfig::configure::<F, K, P>(meta, &x_cols, &z_cols);
1331        let norm_config = NormConfig::configure::<F, K, P>(meta, &x_cols, &z_cols);
1332
1333        FieldChipConfig {
1334            mul_config,
1335            norm_config,
1336            x_cols,
1337            y_cols,
1338            z_cols,
1339            u_col,
1340            v_cols,
1341        }
1342    }
1343
1344    // Creates a new AssignedField z, asserted to be well-formed and satisfy
1345    // the emulated field relation x * y = z.
1346    // If the [division] flag is set, the relation becomes z * y = x, in order to
1347    // model the division (z = x / y).
1348    // WARNING: When used for division, we must independently assert that y != 0
1349    // (note that when y = 0, any value for z would satisfy the relation if x = 0).
1350    fn assign_mul(
1351        &self,
1352        layouter: &mut impl Layouter<F>,
1353        x: &AssignedField<F, K, P>,
1354        y: &AssignedField<F, K, P>,
1355        division: bool,
1356    ) -> Result<AssignedField<F, K, P>, Error> {
1357        let base = BI::from(2).pow(P::LOG2_BASE);
1358        let nb_limbs = P::NB_LIMBS;
1359
1360        let x = self.normalize(layouter, x)?;
1361        let y = self.normalize(layouter, y)?;
1362
1363        y.value().error_if_known_and(|yv| division && K::is_zero(yv).into())?;
1364
1365        let zv = x
1366            .value()
1367            .zip(y.value())
1368            .map(|(xv, yv)| {
1369                if division {
1370                    xv * yv.invert().unwrap()
1371                } else {
1372                    xv * yv
1373                }
1374            })
1375            .map(|z| bi_to_limbs(nb_limbs, &base, &fe_to_bigint(&(z - K::ONE))));
1376        let z_values = (0..nb_limbs)
1377            .map(|i| zv.clone().map(|zs| bigint_to_fe::<F>(&zs[i as usize])))
1378            .collect::<Vec<_>>();
1379
1380        // Assign and range-check the z limbs
1381        let z_limbs = z_values
1382            .iter()
1383            .zip(well_formed_log2_bounds::<F, K, P>().iter())
1384            .map(|(&z_value, &log2_bound)| {
1385                self.native_gadget.assign_lower_than_fixed(
1386                    layouter,
1387                    z_value,
1388                    &(BigUint::one() << log2_bound),
1389                )
1390            })
1391            .collect::<Result<Vec<_>, Error>>()?;
1392
1393        let z = AssignedField::<F, K, P> {
1394            limb_values: z_limbs,
1395            limb_bounds: well_formed_bounds::<F, K, P>(),
1396            _marker: PhantomData,
1397        };
1398
1399        // Divisions z = x / y are modeled as multiplications z * y = x.
1400        // We swap x and z when division = true.
1401        let (l, r) = if !division { (&x, &z) } else { (&z, &x) };
1402        mul::assert_mul::<F, K, P, N>(
1403            layouter,
1404            l,
1405            &y,
1406            r,
1407            &self.config.mul_config,
1408            &self.native_gadget,
1409        )?;
1410
1411        Ok(z)
1412    }
1413}
1414
1415impl<F, K, P, N> FieldChip<F, K, P, N>
1416where
1417    F: PrimeField,
1418    K: PrimeField,
1419    P: FieldEmulationParams<F, K>,
1420    N: NativeInstructions<F>,
1421{
1422    /// Normalizes the given assigned field element, but only if its bounds
1423    /// exceed the limits of the well-formed bounds.
1424    pub(crate) fn normalize(
1425        &self,
1426        layouter: &mut impl Layouter<F>,
1427        x: &AssignedField<F, K, P>,
1428    ) -> Result<AssignedField<F, K, P>, Error> {
1429        if x.is_well_formed() {
1430            Ok(x.clone())
1431        } else {
1432            self.make_canonical(layouter, x)
1433        }
1434    }
1435
1436    /// Normalizes the given assigned field element, but only if its bounds
1437    /// are approaching the limits of the well-formed bounds.
1438    pub(crate) fn normalize_if_approaching_limit(
1439        &self,
1440        layouter: &mut impl Layouter<F>,
1441        x: &AssignedField<F, K, P>,
1442    ) -> Result<AssignedField<F, K, P>, Error> {
1443        // This threshold was chosen empirically.
1444        let threshold: BI = P::max_limb_bound() / 10;
1445        let dangerous_lower_bounds = x.limb_bounds.iter().any(|b| b.0 < -threshold.clone());
1446        let dangerous_upper_bounds = x.limb_bounds.iter().any(|b| b.1 > threshold);
1447        if dangerous_lower_bounds || dangerous_upper_bounds {
1448            self.make_canonical(layouter, x)
1449        } else {
1450            Ok(x.clone())
1451        }
1452    }
1453
1454    /// Converts the given assigned field element into an equivalent one
1455    /// represented in canonical form, through the normalization procedure.
1456    fn make_canonical(
1457        &self,
1458        layouter: &mut impl Layouter<F>,
1459        x: &AssignedField<F, K, P>,
1460    ) -> Result<AssignedField<F, K, P>, Error> {
1461        let max_limb_bound = P::max_limb_bound();
1462        x.limb_bounds.iter().for_each(|(lower, upper)| {
1463            if lower < &(-&max_limb_bound) || upper > &max_limb_bound {
1464                panic!(
1465                    "make_canonical: the limb bounds of the input: [{}, {}] exceed the
1466                     maximum limb bound value {}; consider applying a normalization
1467                     earlier, when the bounds are still within the permited range;
1468                     increasing the [max_limb_bound] of your FieldEmulationParams could also
1469                     help, if possible.",
1470                    lower, upper, max_limb_bound
1471                );
1472            }
1473        });
1474        let z_limbs = norm::normalize::<F, K, P, N>(
1475            layouter,
1476            x,
1477            &self.config.norm_config,
1478            &self.native_gadget,
1479        )?;
1480        let z = AssignedField::<F, K, P> {
1481            limb_values: z_limbs,
1482            limb_bounds: well_formed_bounds::<F, K, P>(),
1483            _marker: PhantomData,
1484        };
1485        Ok(z)
1486    }
1487
1488    /// This function should only be called once the limb has been asserted to
1489    /// be in the range [0, base).
1490    fn assigned_field_from_limb(
1491        &self,
1492        layouter: &mut impl Layouter<F>,
1493        limb: &AssignedNative<F>,
1494    ) -> Result<AssignedField<F, K, P>, Error> {
1495        // Subtract one for the unique-zero representation.
1496        let least_significant_limb = self.native_gadget.add_constant(layouter, limb, -F::ONE)?;
1497        let mut limb_values = vec![least_significant_limb];
1498        let mut limb_bounds = well_formed_bounds::<F, K, P>();
1499        let zero = self.native_gadget.assign_fixed(layouter, F::ZERO)?;
1500        limb_values.resize(P::NB_LIMBS as usize, zero);
1501        limb_bounds[0] = (limb_bounds[0].clone().0 - 1, limb_bounds[0].clone().1 - 1);
1502        Ok(AssignedField::<F, K, P> {
1503            limb_values,
1504            limb_bounds,
1505            _marker: PhantomData,
1506        })
1507    }
1508}
1509
1510// Inherit Bit Assignment Instructions from NativeGadget.
1511impl<F, K, P, N> AssignmentInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1512where
1513    F: PrimeField,
1514    K: PrimeField,
1515    P: FieldEmulationParams<F, K>,
1516    N: NativeInstructions<F>,
1517{
1518    fn assign(
1519        &self,
1520        layouter: &mut impl Layouter<F>,
1521        value: Value<bool>,
1522    ) -> Result<AssignedBit<F>, Error> {
1523        self.native_gadget.assign(layouter, value)
1524    }
1525
1526    fn assign_fixed(
1527        &self,
1528        layouter: &mut impl Layouter<F>,
1529        constant: bool,
1530    ) -> Result<AssignedBit<F>, Error> {
1531        self.native_gadget.assign_fixed(layouter, constant)
1532    }
1533}
1534
1535// Inherit Bit Assertion Instructions from NativeGadget.
1536impl<F, K, P, N> AssertionInstructions<F, AssignedBit<F>> for FieldChip<F, K, P, N>
1537where
1538    F: PrimeField,
1539    K: PrimeField,
1540    P: FieldEmulationParams<F, K>,
1541    N: NativeInstructions<F>,
1542{
1543    fn assert_equal(
1544        &self,
1545        layouter: &mut impl Layouter<F>,
1546        x: &AssignedBit<F>,
1547        y: &AssignedBit<F>,
1548    ) -> Result<(), Error> {
1549        self.native_gadget.assert_equal(layouter, x, y)
1550    }
1551
1552    fn assert_not_equal(
1553        &self,
1554        layouter: &mut impl Layouter<F>,
1555        x: &AssignedBit<F>,
1556        y: &AssignedBit<F>,
1557    ) -> Result<(), Error> {
1558        self.native_gadget.assert_not_equal(layouter, x, y)
1559    }
1560
1561    fn assert_equal_to_fixed(
1562        &self,
1563        layouter: &mut impl Layouter<F>,
1564        x: &AssignedBit<F>,
1565        constant: bool,
1566    ) -> Result<(), Error> {
1567        self.native_gadget.assert_equal_to_fixed(layouter, x, constant)
1568    }
1569
1570    fn assert_not_equal_to_fixed(
1571        &self,
1572        layouter: &mut impl Layouter<F>,
1573        x: &AssignedBit<F>,
1574        constant: bool,
1575    ) -> Result<(), Error> {
1576        self.native_gadget.assert_not_equal_to_fixed(layouter, x, constant)
1577    }
1578}
1579
1580impl<F, K, P, N> ConversionInstructions<F, AssignedBit<F>, AssignedField<F, K, P>>
1581    for FieldChip<F, K, P, N>
1582where
1583    F: PrimeField,
1584    K: PrimeField,
1585    P: FieldEmulationParams<F, K>,
1586    N: NativeInstructions<F>,
1587{
1588    fn convert_value(&self, x: &bool) -> Option<K> {
1589        Some(if *x { K::ONE } else { K::ZERO })
1590    }
1591
1592    fn convert(
1593        &self,
1594        layouter: &mut impl Layouter<F>,
1595        x: &AssignedBit<F>,
1596    ) -> Result<AssignedField<F, K, P>, Error> {
1597        let x: AssignedNative<F> = x.clone().into();
1598        self.assigned_field_from_limb(layouter, &x)
1599    }
1600}
1601
1602impl<F, K, P, N> ConversionInstructions<F, AssignedByte<F>, AssignedField<F, K, P>>
1603    for FieldChip<F, K, P, N>
1604where
1605    F: PrimeField,
1606    K: PrimeField,
1607    P: FieldEmulationParams<F, K>,
1608    N: NativeInstructions<F>,
1609{
1610    fn convert_value(&self, x: &u8) -> Option<K> {
1611        Some(K::from(*x as u64))
1612    }
1613
1614    fn convert(
1615        &self,
1616        layouter: &mut impl Layouter<F>,
1617        x: &AssignedByte<F>,
1618    ) -> Result<AssignedField<F, K, P>, Error> {
1619        let x: AssignedNative<F> = x.clone().into();
1620        self.assigned_field_from_limb(layouter, &x)
1621    }
1622}
1623
1624// Inherit Canonicity Instructions from NativeGadget.
1625impl<F, K, P, N> CanonicityInstructions<F, AssignedField<F, K, P>> for FieldChip<F, K, P, N>
1626where
1627    F: PrimeField,
1628    K: PrimeField,
1629    P: FieldEmulationParams<F, K>,
1630    N: NativeInstructions<F>,
1631{
1632    fn le_bits_lower_than(
1633        &self,
1634        layouter: &mut impl Layouter<F>,
1635        bits: &[AssignedBit<F>],
1636        bound: BigUint,
1637    ) -> Result<AssignedBit<F>, Error> {
1638        self.native_gadget.le_bits_lower_than(layouter, bits, bound)
1639    }
1640
1641    fn le_bits_geq_than(
1642        &self,
1643        layouter: &mut impl Layouter<F>,
1644        bits: &[AssignedBit<F>],
1645        bound: BigUint,
1646    ) -> Result<AssignedBit<F>, Error> {
1647        self.native_gadget.le_bits_geq_than(layouter, bits, bound)
1648    }
1649}
1650
1651#[derive(Clone, Debug)]
1652#[cfg(any(test, feature = "testing"))]
1653/// Configuration used to implement `FromScratch` for the Foreign field chip.
1654/// This should only be used for testing.
1655pub struct FieldChipConfigForTests<F, N>
1656where
1657    F: PrimeField,
1658    N: NativeInstructions<F> + FromScratch<F>,
1659{
1660    native_gadget_config: <N as FromScratch<F>>::Config,
1661    field_chip_config: FieldChipConfig,
1662}
1663
1664#[cfg(any(test, feature = "testing"))]
1665impl<F, K, P, N> FromScratch<F> for FieldChip<F, K, P, N>
1666where
1667    F: PrimeField,
1668    K: PrimeField,
1669    P: FieldEmulationParams<F, K>,
1670    N: NativeInstructions<F> + FromScratch<F>,
1671{
1672    type Config = FieldChipConfigForTests<F, N>;
1673
1674    fn new_from_scratch(config: &FieldChipConfigForTests<F, N>) -> Self {
1675        let native_gadget = <N as FromScratch<F>>::new_from_scratch(&config.native_gadget_config);
1676        FieldChip::new(&config.field_chip_config, &native_gadget)
1677    }
1678
1679    fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1680        self.native_gadget.load_from_scratch(layouter)
1681    }
1682
1683    fn configure_from_scratch(
1684        meta: &mut ConstraintSystem<F>,
1685        instance_columns: &[Column<Instance>; 2],
1686    ) -> FieldChipConfigForTests<F, N> {
1687        let native_gadget_config =
1688            <N as FromScratch<F>>::configure_from_scratch(meta, instance_columns);
1689        let field_chip_config = {
1690            let advice_cols = (0..nb_field_chip_columns::<F, K, P>())
1691                .map(|_| meta.advice_column())
1692                .collect::<Vec<_>>();
1693            FieldChip::<F, K, P, N>::configure(meta, &advice_cols)
1694        };
1695        FieldChipConfigForTests {
1696            native_gadget_config,
1697            field_chip_config,
1698        }
1699    }
1700}
1701
1702#[cfg(test)]
1703mod tests {
1704    use midnight_curves::{
1705        secp256k1::{Fp as secp256k1Base, Fq as secp256k1Scalar},
1706        Fq as BlsScalar,
1707    };
1708
1709    use super::*;
1710    use crate::{
1711        field::{
1712            decomposition::chip::P2RDecompositionChip, foreign::params::MultiEmulationParams,
1713            NativeChip, NativeGadget,
1714        },
1715        instructions::{
1716            arithmetic, assertions, control_flow, decomposition, equality, public_input, zero,
1717        },
1718    };
1719
1720    macro_rules! test_generic {
1721        ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1722            $mod::tests::$op::<
1723                $native,
1724                AssignedField<$native, $emulated, MultiEmulationParams>,
1725                FieldChip<
1726                    $native,
1727                    $emulated,
1728                    MultiEmulationParams,
1729                    NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1730                >,
1731            >($name);
1732        };
1733    }
1734
1735    macro_rules! test {
1736        ($mod:ident, $op:ident) => {
1737            #[test]
1738            fn $op() {
1739                test_generic!($mod, $op, BlsScalar, secp256k1Base, "field_chip_secp_base");
1740                test_generic!(
1741                    $mod,
1742                    $op,
1743                    BlsScalar,
1744                    secp256k1Scalar,
1745                    "field_chip_secp_scalar"
1746                );
1747            }
1748        };
1749    }
1750
1751    test!(assertions, test_assertions);
1752
1753    test!(public_input, test_public_inputs);
1754
1755    test!(equality, test_is_equal);
1756
1757    test!(zero, test_zero_assertions);
1758    test!(zero, test_is_zero);
1759
1760    test!(control_flow, test_select);
1761    test!(control_flow, test_cond_assert_equal);
1762    test!(control_flow, test_cond_swap);
1763
1764    test!(arithmetic, test_add);
1765    test!(arithmetic, test_sub);
1766    test!(arithmetic, test_mul);
1767    test!(arithmetic, test_div);
1768    test!(arithmetic, test_neg);
1769    test!(arithmetic, test_inv);
1770    test!(arithmetic, test_pow);
1771    test!(arithmetic, test_linear_combination);
1772    test!(arithmetic, test_add_and_mul);
1773
1774    macro_rules! test_generic {
1775        ($mod:ident, $op:ident, $native:ident, $emulated:ident, $name:expr) => {
1776            $mod::tests::$op::<
1777                $native,
1778                AssignedField<$native, $emulated, MultiEmulationParams>,
1779                FieldChip<
1780                    $native,
1781                    $emulated,
1782                    MultiEmulationParams,
1783                    NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1784                >,
1785                NativeGadget<$native, P2RDecompositionChip<$native>, NativeChip<$native>>,
1786            >($name);
1787        };
1788    }
1789
1790    macro_rules! test {
1791        ($mod:ident, $op:ident) => {
1792            #[test]
1793            fn $op() {
1794                test_generic!($mod, $op, BlsScalar, secp256k1Base, "field_chip_secp_base");
1795                test_generic!(
1796                    $mod,
1797                    $op,
1798                    BlsScalar,
1799                    secp256k1Scalar,
1800                    "field_chip_secp_scalar"
1801                );
1802            }
1803        };
1804    }
1805
1806    test!(decomposition, test_bit_decomposition);
1807    test!(decomposition, test_byte_decomposition);
1808    test!(decomposition, test_sgn0);
1809}