Skip to main content

midnight_circuits/field/native/
native_gadget.rs

1// This file is part of MIDNIGHT-ZK.
2// Copyright (C) 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//! A gadget that implement all basic basic operations in the native field, i.e.
15//! basic field operations, decompositions and comparisons
16
17use std::{cell::RefCell, cmp::min, collections::HashMap, marker::PhantomData, rc::Rc};
18
19use midnight_proofs::{
20    circuit::{Layouter, Value},
21    plonk::Error,
22};
23use num_bigint::BigUint;
24use num_traits::Zero;
25#[cfg(any(test, feature = "testing"))]
26use {
27    crate::field::decomposition::chip::P2RDecompositionConfig,
28    crate::field::decomposition::pow2range::Pow2RangeChip,
29    crate::field::native::{NB_ARITH_COLS, NB_ARITH_FIXED_COLS},
30    crate::testing_utils::FromScratch,
31    crate::testing_utils::Sampleable,
32    crate::utils::ComposableChip,
33    midnight_proofs::plonk::{Advice, Column, ConstraintSystem, Fixed, Instance},
34    rand::Rng,
35    rand::RngCore,
36};
37
38use crate::{
39    field::{
40        decomposition::{chip::P2RDecompositionChip, instructions::CoreDecompositionInstructions},
41        NativeChip,
42    },
43    instructions::{
44        public_input::CommittedInstanceInstructions, ArithInstructions, AssertionInstructions,
45        AssignmentInstructions, BinaryInstructions, BitwiseInstructions, CanonicityInstructions,
46        ComparisonInstructions, ControlFlowInstructions, ConversionInstructions,
47        DecompositionInstructions, DivisionInstructions, EqualityInstructions, FieldInstructions,
48        NativeInstructions, PublicInputInstructions, RangeCheckInstructions,
49        ScalarFieldInstructions, UnsafeConversionInstructions, ZeroInstructions,
50    },
51    types::{AssignedBit, AssignedNative, InnerValue, Instantiable},
52    utils::util::big_to_fe,
53    CircuitField,
54};
55
56#[derive(Debug, Clone)]
57/// A gadget that implements all basic operations on the Native field:
58/// - Assignments
59/// - Assertions
60/// - Arithmetic
61/// - Binary
62/// - Comparison
63/// - ControlFlow
64/// - Conversions
65/// - Decomposition
66/// - Equality
67pub struct NativeGadget<F, CoreDecomposition, NativeArith>
68where
69    F: CircuitField,
70    CoreDecomposition: CoreDecompositionInstructions<F>,
71    NativeArith: ArithInstructions<F, AssignedNative<F>>,
72{
73    core_decomposition_chip: CoreDecomposition,
74    /// The underlying native field arithmetic chip.
75    pub native_chip: NativeArith,
76    constrained_cells: Rc<RefCell<HashMap<AssignedNative<F>, BigUint>>>,
77    _marker: PhantomData<F>,
78}
79
80impl<F, CoreDecomposition, NativeArith> NativeGadget<F, CoreDecomposition, NativeArith>
81where
82    F: CircuitField,
83    CoreDecomposition: CoreDecompositionInstructions<F>,
84    NativeArith: ArithInstructions<F, AssignedNative<F>>,
85{
86    /// Create a new gadget.
87    pub fn new(core_decomposition_chip: CoreDecomposition, native_chip: NativeArith) -> Self {
88        Self {
89            core_decomposition_chip,
90            native_chip,
91            constrained_cells: Rc::new(RefCell::new(HashMap::new())),
92            _marker: PhantomData,
93        }
94    }
95
96    /// Updates the (strict) upper-bound of the given assigned cell to the
97    /// minimum between its current bound and the given `bound`.
98    fn update_bound(&self, x: &AssignedNative<F>, bound: BigUint) {
99        let mut map = self.constrained_cells.borrow_mut();
100        map.entry(x.clone())
101            .and_modify(|v| *v = min(v.clone(), bound.clone()))
102            .or_insert(bound);
103    }
104}
105
106impl<F: CircuitField> Instantiable<F> for AssignedByte<F> {
107    fn as_public_input(element: &u8) -> Vec<F> {
108        vec![F::from(*element as u64)]
109    }
110}
111
112/// This wrapper type on `AssignedNative<F>` is designed to enforce type safety
113/// on assigned bytes. It prevents the user from creating an `AssignedByte`
114/// without using the designated entry points, which guarantee (with
115/// constraints) that the assigned value is indeed in the range [0, 256).
116#[derive(Clone, Debug)]
117#[must_use]
118pub struct AssignedByte<F: CircuitField>(AssignedNative<F>);
119
120impl<F: CircuitField> InnerValue for AssignedByte<F> {
121    type Element = u8;
122
123    fn value(&self) -> Value<u8> {
124        self.0.value().map(|v| {
125            let bi_v = v.to_biguint();
126            #[cfg(not(test))]
127            assert!(bi_v <= BigUint::from(255u8));
128            bi_v.to_bytes_le().first().copied().unwrap_or(0u8)
129        })
130    }
131}
132
133impl<F: CircuitField> From<AssignedByte<F>> for AssignedNative<F> {
134    fn from(value: AssignedByte<F>) -> Self {
135        value.0
136    }
137}
138
139impl<F: CircuitField> From<&AssignedByte<F>> for AssignedNative<F> {
140    fn from(value: &AssignedByte<F>) -> Self {
141        value.clone().0
142    }
143}
144
145impl<F: CircuitField> From<AssignedBit<F>> for AssignedByte<F> {
146    fn from(value: AssignedBit<F>) -> Self {
147        AssignedByte(value.0)
148    }
149}
150
151#[cfg(any(test, feature = "testing"))]
152impl<F: CircuitField> Sampleable for AssignedByte<F> {
153    fn sample_inner(mut rng: impl RngCore) -> Self::Element {
154        rng.r#gen()
155    }
156}
157
158/// Struct representing bounded elements, i.e. 0 <= value < 2^bound.
159#[derive(Clone, Debug)]
160pub struct BoundedElement<F: CircuitField> {
161    value: F,
162    bound: u32,
163}
164
165impl<F: CircuitField> BoundedElement<F> {
166    /// Creates a new bounded element
167    pub fn new(value: F, bound: u32) -> Self {
168        #[cfg(not(test))]
169        {
170            use num_traits::One;
171
172            let v_as_bint = value.to_biguint();
173            let bound_as_bint = BigUint::one() << bound;
174            assert!(
175                v_as_bint < bound_as_bint,
176                "Trying to convert {:?} to an AssignedBounded less than 2^{:?}!",
177                value,
178                bound
179            );
180        }
181        BoundedElement { value, bound }
182    }
183
184    /// gets the field value of a BoundedElement
185    pub fn field_value(&self) -> F {
186        self.value
187    }
188
189    /// gets the bound of a BoundedElement
190    pub fn bound(&self) -> u32 {
191        self.bound
192    }
193}
194
195/// This type is designed to enforce type safety on assigned "small" values.
196/// It prevents the user from creating an `AssignedBounded` without using the
197/// designated entry points, which guarantee (with constraints) that the
198/// assigned value is in the desired range, `[0, 2^bound)`.
199#[derive(Clone, Debug)]
200pub struct AssignedBounded<F: CircuitField> {
201    value: AssignedNative<F>,
202    bound: u32,
203}
204
205impl<F: CircuitField> AssignedBounded<F> {
206    /// CAUTION: use only if you know what you are doing!
207    ///
208    /// This function converts an `AssignedNative` to an `AssignedBounded`
209    /// *without* adding any constraint to guarantee the number respects the
210    /// bound.
211    ///
212    /// *It should be used only when the input x is already rangechecked
213    pub(crate) fn to_assigned_bounded_unsafe(x: &AssignedNative<F>, bound: u32) -> Self {
214        // we create the element to enforce the runtime assertions
215        let _new = x.value().map(|&x| BoundedElement::new(x, bound));
216        AssignedBounded {
217            value: x.clone(),
218            bound,
219        }
220    }
221
222    /// gets the bound of an AssignedBounded
223    pub fn bound(&self) -> u32 {
224        self.bound
225    }
226}
227
228impl<F: CircuitField> InnerValue for AssignedBounded<F> {
229    type Element = BoundedElement<F>;
230
231    fn value(&self) -> Value<BoundedElement<F>> {
232        let (assigned_value, bound) = (self.value.clone(), self.bound());
233        assigned_value.value().map(|&value| BoundedElement { value, bound })
234    }
235}
236
237impl<F, CoreDecomposition, NativeArith> RangeCheckInstructions<F, AssignedNative<F>>
238    for NativeGadget<F, CoreDecomposition, NativeArith>
239where
240    F: CircuitField,
241    CoreDecomposition: CoreDecompositionInstructions<F>,
242    NativeArith: ArithInstructions<F, AssignedNative<F>>
243        + AssignmentInstructions<F, AssignedBit<F>>
244        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
245        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
246        + BinaryInstructions<F>
247        + EqualityInstructions<F, AssignedNative<F>>
248        + ControlFlowInstructions<F, AssignedNative<F>>,
249{
250    fn assign_lower_than_fixed(
251        &self,
252        layouter: &mut impl Layouter<F>,
253        value: Value<F>,
254        bound: &BigUint,
255    ) -> Result<AssignedNative<F>, Error> {
256        if bound.is_zero() {
257            return self.assign_fixed(layouter, F::ZERO);
258        }
259
260        // compute largest k such that 2^k <= bound
261        let k = (bound.bits() - 1) as usize;
262        if *bound == BigUint::from(1u8) << k {
263            return self.core_decomposition_chip.assign_less_than_pow2(layouter, value, k);
264        }
265        let x = self.assign(layouter, value)?;
266        self.assert_lower_than_fixed(layouter, &x, bound)?;
267        Ok(x)
268    }
269
270    fn assert_lower_than_fixed(
271        &self,
272        layouter: &mut impl Layouter<F>,
273        x: &AssignedNative<F>,
274        bound: &BigUint,
275    ) -> Result<(), Error> {
276        if let Some(current_bound) = self.constrained_cells.borrow().get(x) {
277            if current_bound <= bound {
278                return Ok(());
279            }
280        }
281        self.update_bound(x, bound.clone());
282
283        // compute largest k such that 2^k <= bound
284        let k = (bound.bits() - 1) as usize;
285        let two_pow_k = BigUint::from(1u8) << k; // 2^k
286
287        // if the bound is a power of 2, the check is easier
288        if two_pow_k == *bound {
289            return self.core_decomposition_chip.assert_less_than_pow2(layouter, x, k);
290        }
291
292        // b := x in [0, 2^k)
293        let b_value = x.value().map(|x| x.to_biguint() < two_pow_k);
294        let b: AssignedBit<F> = self.assign(layouter, b_value)?;
295
296        let diff: F = big_to_fe(bound - two_pow_k);
297
298        // x in [0, bound) <=> x in [0, 2^k) or (x - diff) in [0, 2^k)
299        let shifted_x = self.add_constant(layouter, x, -diff)?;
300        let y = self.select(layouter, &b, x, &shifted_x)?;
301        self.core_decomposition_chip.assert_less_than_pow2(layouter, &y, k)
302    }
303}
304
305impl<F, CoreDecomposition, NativeArith> ComparisonInstructions<F, AssignedNative<F>>
306    for NativeGadget<F, CoreDecomposition, NativeArith>
307where
308    F: CircuitField,
309    CoreDecomposition: CoreDecompositionInstructions<F>,
310    NativeArith: ArithInstructions<F, AssignedNative<F>>
311        + AssignmentInstructions<F, AssignedBit<F>>
312        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
313        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
314        + BinaryInstructions<F>
315        + EqualityInstructions<F, AssignedNative<F>>
316        + ControlFlowInstructions<F, AssignedNative<F>>,
317{
318    // This constant must not exceed F::NUM_BITS - 2. This restriction derives from
319    // the assumption that the following condition holds true:
320    //
321    //     x <  y    ==> 0 < y - x < 2^MAX_BOUND_IN_BITS
322    //
323    // This implies that the difference x - y should not wrap around in the field,
324    // ensuring it remains less than 2^MAX_BOUND_IN_BITS.
325    const MAX_BOUND_IN_BITS: u32 = F::NUM_BITS - 2;
326
327    fn bounded_of_element(
328        &self,
329        layouter: &mut impl Layouter<F>,
330        n: usize,
331        x: &AssignedNative<F>,
332    ) -> Result<AssignedBounded<F>, Error> {
333        #[cfg(not(test))]
334        assert!(
335            n <= Self::MAX_BOUND_IN_BITS as usize,
336            "Cannot bound an element with a bound {} > {} = MAX_BOUND",
337            n,
338            Self::MAX_BOUND_IN_BITS,
339        );
340
341        self.assert_lower_than_fixed(layouter, x, &(BigUint::from(1u32) << n))?;
342        Ok(AssignedBounded::to_assigned_bounded_unsafe(x, n as u32))
343    }
344
345    fn element_of_bounded(
346        &self,
347        _layouter: &mut impl Layouter<F>,
348        bounded: &AssignedBounded<F>,
349    ) -> Result<AssignedNative<F>, Error> {
350        Ok(bounded.value.clone())
351    }
352
353    /// Returns `true` iff the given assigned element is strictly lower than the
354    /// given bound.
355    fn lower_than_fixed(
356        &self,
357        layouter: &mut impl Layouter<F>,
358        x: &AssignedBounded<F>,
359        y: F,
360    ) -> Result<AssignedBit<F>, Error> {
361        if let Some(current_bound) = self.constrained_cells.borrow().get(&x.value) {
362            if *current_bound <= y.to_biguint() {
363                return self.assign_fixed(layouter, true);
364            }
365        }
366
367        let x_as_bint = x.value.value().map(|x| x.to_biguint());
368        let y_as_bint = y.to_biguint();
369
370        // check that we try to make a meaningful comparison, i.e. y < 2^p-1
371        #[cfg(not(test))]
372        assert!(y_as_bint < BigUint::from(1u8) << Self::MAX_BOUND_IN_BITS);
373
374        // x is already bounded by the type system so x < bound for some fixed bound.
375        // If we want to show that x < bound <= y this relation automatically holds
376        if y_as_bint >= (BigUint::from(1u8) << x.bound()) {
377            return self.assign_fixed(layouter, true);
378        }
379
380        // we will now assert the equation
381        // we know 0 <= x,y < 2^bound. There are two cases:
382        //  1. x <  y    ==>    0 < y - x < 2^bound    ==>    0 <= y - x - 1 < 2^bound
383        //  2. x >= y    ==>                                  0 <=  x - y < 2^bound
384
385        // define z = b(2y-1) + x + bx(-2) - y
386        //   - if b = 0 ==> z = x - y
387        //   - if b = 1 ==> z = 2y-1 + x -2x - y =  y - 1 - x
388
389        // assign b
390        let result_bit = x_as_bint.map(|x_as_bint| x_as_bint < y_as_bint);
391        let assigned_result = self.assign(layouter, result_bit)?;
392
393        // assign z: z is of the form "f1 a1 + f2 a2 + f a_1 a_2 + c"
394        // TODO: This can be done in a single row but the interface prevents it...
395        // Expose some more complex function maybe?
396        let b_el: AssignedNative<F> = self.convert(layouter, &assigned_result)?;
397        let x_el = self.element_of_bounded(layouter, x)?;
398        let bx = self.mul(layouter, &x_el, &b_el, None)?;
399        let terms = vec![
400            (F::from(2) * y - F::ONE, b_el),
401            (F::ONE, x_el),
402            (-F::from(2), bx),
403        ];
404        let z = self.linear_combination(layouter, terms.as_slice(), -y)?;
405
406        self.core_decomposition_chip
407            .assert_less_than_pow2(layouter, &z, x.bound() as usize)?;
408        Ok(assigned_result)
409    }
410
411    fn lower_than(
412        &self,
413        layouter: &mut impl Layouter<F>,
414        x: &AssignedBounded<F>,
415        y: &AssignedBounded<F>,
416    ) -> Result<AssignedBit<F>, Error> {
417        let x_as_bint = x.value.value().map(|x| x.to_biguint());
418        let y_as_bint = y.value.value().map(|x| x.to_biguint());
419
420        // we will now assert the equation
421        // we know 0 <= x,y < 2^bound. There are two cases:
422        //  1. x <  y    ==>    0 < y - x < 2^bound    ==>    0 <= y - x - 1 < 2^bound
423        //  2. x >= y    ==>                                  0 <=  x - y < 2^bound
424
425        // define z = 2by - b + x + bx(-2) - y
426        //   - if b = 0 ==> z = x - y
427        //   - if b = 1 ==> z = 2y-1 + x -2x - y =  y - 1 - x
428
429        // assign b
430        let result_bit =
431            x_as_bint.zip(y_as_bint).map(|(x_as_bint, y_as_bint)| x_as_bint < y_as_bint);
432        let assigned_result = self.assign(layouter, result_bit)?;
433
434        // assign z: z is of the form "f1 a1 + f2 a2 + f a_1 a_2 + c"
435        let b_el: AssignedNative<F> = self.convert(layouter, &assigned_result)?;
436        let x_el = self.element_of_bounded(layouter, x)?;
437        let y_el = self.element_of_bounded(layouter, y)?;
438
439        let bx = self.mul(layouter, &x_el, &b_el, None)?;
440        let by = self.mul(layouter, &y_el, &b_el, None)?;
441        let terms = vec![
442            (F::from(2), by),
443            (-F::ONE, b_el),
444            (F::ONE, x_el),
445            (-F::from(2), bx),
446            (-F::ONE, y_el),
447        ];
448        let z = self.linear_combination(layouter, terms.as_slice(), F::ZERO)?;
449
450        let max_bound = x.bound().max(y.bound());
451        self.core_decomposition_chip
452            .assert_less_than_pow2(layouter, &z, max_bound as usize)?;
453        Ok(assigned_result)
454    }
455
456    fn leq(
457        &self,
458        layouter: &mut impl Layouter<F>,
459        x: &AssignedBounded<F>,
460        y: &AssignedBounded<F>,
461    ) -> Result<AssignedBit<F>, Error> {
462        // This is reimplemented this way because doing x < y + 1 might break things in
463        // some weird edge case
464        let b1 = self.lower_than(layouter, x, y)?;
465        let x_el = self.element_of_bounded(layouter, x)?;
466        let y_el = self.element_of_bounded(layouter, y)?;
467        let b2 = self.is_equal(layouter, &x_el, &y_el)?;
468        self.or(layouter, &[b1, b2])
469    }
470
471    /// Returns `true` iff the given assigned element is greater than or equal
472    /// to the given bound.
473    fn geq(
474        &self,
475        layouter: &mut impl Layouter<F>,
476        x: &AssignedBounded<F>,
477        y: &AssignedBounded<F>,
478    ) -> Result<AssignedBit<F>, Error> {
479        let b = self.lower_than(layouter, x, y)?;
480        self.not(layouter, &b)
481    }
482}
483
484impl<F, CoreDecomposition, NativeArith> DivisionInstructions<F, AssignedNative<F>>
485    for NativeGadget<F, CoreDecomposition, NativeArith>
486where
487    F: CircuitField,
488    CoreDecomposition: CoreDecompositionInstructions<F>,
489    NativeArith: ArithInstructions<F, AssignedNative<F>>
490        + AssignmentInstructions<F, AssignedBit<F>>
491        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
492        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
493        + BinaryInstructions<F>
494        + EqualityInstructions<F, AssignedNative<F>>
495        + ControlFlowInstructions<F, AssignedNative<F>>,
496{
497}
498
499/// Instructions for constraining bytes as public inputs.
500impl<F, CoreDecomposition, NativeArith> PublicInputInstructions<F, AssignedByte<F>>
501    for NativeGadget<F, CoreDecomposition, NativeArith>
502where
503    F: CircuitField,
504    CoreDecomposition: CoreDecompositionInstructions<F>,
505    NativeArith:
506        PublicInputInstructions<F, AssignedNative<F>> + ArithInstructions<F, AssignedNative<F>>,
507{
508    fn as_public_input(
509        &self,
510        _layouter: &mut impl Layouter<F>,
511        assigned: &AssignedByte<F>,
512    ) -> Result<Vec<AssignedNative<F>>, Error> {
513        Ok(vec![assigned.clone().into()])
514    }
515
516    fn constrain_as_public_input(
517        &self,
518        layouter: &mut impl Layouter<F>,
519        assigned: &AssignedByte<F>,
520    ) -> Result<(), Error> {
521        let assigned_as_native: AssignedNative<F> = assigned.clone().into();
522        self.constrain_as_public_input(layouter, &assigned_as_native)
523    }
524
525    fn assign_as_public_input(
526        &self,
527        layouter: &mut impl Layouter<F>,
528        value: Value<u8>,
529    ) -> Result<AssignedByte<F>, Error> {
530        // We can skip the in-circuit [0, 255]-range-check as this condition will
531        // be enforced through the public inputs bind anyway.
532        let assigned_native = self
533            .native_chip
534            .assign_as_public_input(layouter, value.map(|byte| F::from(byte as u64)))?;
535        self.convert_unsafe(layouter, &assigned_native)
536    }
537}
538
539impl<F, CD, NA, Assigned> CommittedInstanceInstructions<F, Assigned> for NativeGadget<F, CD, NA>
540where
541    F: CircuitField,
542    CD: CoreDecompositionInstructions<F>,
543
544    NA: CommittedInstanceInstructions<F, AssignedNative<F>>
545        + ArithInstructions<F, AssignedNative<F>>,
546    Assigned: Instantiable<F> + Into<AssignedNative<F>>,
547{
548    fn constrain_as_committed_public_input(
549        &self,
550        layouter: &mut impl Layouter<F>,
551        assigned: &Assigned,
552    ) -> Result<(), Error> {
553        let assigned_as_native = assigned.clone().into();
554        self.native_chip
555            .constrain_as_committed_public_input(layouter, &assigned_as_native)
556    }
557}
558
559/// The set of circuit instructions for assignment of bytes.
560impl<F, CoreDecomposition, NativeArith> AssignmentInstructions<F, AssignedByte<F>>
561    for NativeGadget<F, CoreDecomposition, NativeArith>
562where
563    F: CircuitField,
564    CoreDecomposition: CoreDecompositionInstructions<F>,
565    NativeArith: ArithInstructions<F, AssignedNative<F>>,
566{
567    fn assign(
568        &self,
569        layouter: &mut impl Layouter<F>,
570        byte: Value<u8>,
571    ) -> Result<AssignedByte<F>, Error> {
572        let byte_as_f = byte.map(|b| F::from(b as u64));
573        let assigned =
574            self.core_decomposition_chip.assign_less_than_pow2(layouter, byte_as_f, 8)?;
575        Ok(AssignedByte(assigned))
576    }
577
578    fn assign_fixed(
579        &self,
580        layouter: &mut impl Layouter<F>,
581        constant: u8,
582    ) -> Result<AssignedByte<F>, Error> {
583        let assigned = self.assign_fixed(layouter, F::from(constant as u64))?;
584        Ok(AssignedByte(assigned))
585    }
586
587    fn assign_many(
588        &self,
589        layouter: &mut impl Layouter<F>,
590        values: &[Value<u8>],
591    ) -> Result<Vec<AssignedByte<F>>, Error> {
592        let values_as_f: Vec<_> = values.iter().map(|v| v.map(|b| F::from(b as u64))).collect();
593
594        self.core_decomposition_chip
595            .assign_many_small(layouter, &values_as_f, 8)?
596            .iter()
597            .map(|assigned_native| self.convert_unsafe(layouter, assigned_native))
598            .collect()
599    }
600}
601
602/// The set of AssertionInstructions for bytes.
603impl<F, CoreDecomposition, NativeArith> AssertionInstructions<F, AssignedByte<F>>
604    for NativeGadget<F, CoreDecomposition, NativeArith>
605where
606    F: CircuitField,
607    CoreDecomposition: CoreDecompositionInstructions<F>,
608    NativeArith: ArithInstructions<F, AssignedNative<F>>,
609{
610    fn assert_equal(
611        &self,
612        layouter: &mut impl Layouter<F>,
613        byte1: &AssignedByte<F>,
614        byte2: &AssignedByte<F>,
615    ) -> Result<(), Error> {
616        let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
617        let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
618        self.assert_equal(layouter, &x1, &x2)
619    }
620
621    fn assert_not_equal(
622        &self,
623        layouter: &mut impl Layouter<F>,
624        byte1: &AssignedByte<F>,
625        byte2: &AssignedByte<F>,
626    ) -> Result<(), Error> {
627        let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
628        let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
629        self.assert_not_equal(layouter, &x1, &x2)
630    }
631
632    fn assert_equal_to_fixed(
633        &self,
634        layouter: &mut impl Layouter<F>,
635        byte: &AssignedByte<F>,
636        constant: u8,
637    ) -> Result<(), Error> {
638        let x: AssignedNative<F> = self.convert(layouter, byte)?;
639        self.assert_equal_to_fixed(layouter, &x, F::from(constant as u64))
640    }
641
642    fn assert_not_equal_to_fixed(
643        &self,
644        layouter: &mut impl Layouter<F>,
645        byte: &AssignedByte<F>,
646        constant: u8,
647    ) -> Result<(), Error> {
648        let x: AssignedNative<F> = self.convert(layouter, byte)?;
649        self.assert_not_equal_to_fixed(layouter, &x, F::from(constant as u64))
650    }
651}
652
653/// The set of EqualityInstructions for bytes.
654impl<F, CoreDecomposition, NativeArith> EqualityInstructions<F, AssignedByte<F>>
655    for NativeGadget<F, CoreDecomposition, NativeArith>
656where
657    F: CircuitField,
658    CoreDecomposition: CoreDecompositionInstructions<F>,
659    NativeArith:
660        ArithInstructions<F, AssignedNative<F>> + EqualityInstructions<F, AssignedNative<F>>,
661{
662    fn is_equal(
663        &self,
664        layouter: &mut impl Layouter<F>,
665        byte1: &AssignedByte<F>,
666        byte2: &AssignedByte<F>,
667    ) -> Result<AssignedBit<F>, Error> {
668        let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
669        let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
670        self.native_chip.is_equal(layouter, &x1, &x2)
671    }
672
673    fn is_not_equal(
674        &self,
675        layouter: &mut impl Layouter<F>,
676        byte1: &AssignedByte<F>,
677        byte2: &AssignedByte<F>,
678    ) -> Result<AssignedBit<F>, Error> {
679        let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
680        let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
681        self.native_chip.is_not_equal(layouter, &x1, &x2)
682    }
683
684    fn is_equal_to_fixed(
685        &self,
686        layouter: &mut impl Layouter<F>,
687        byte: &AssignedByte<F>,
688        constant: u8,
689    ) -> Result<AssignedBit<F>, Error> {
690        let x: AssignedNative<F> = self.convert(layouter, byte)?;
691        self.native_chip.is_equal_to_fixed(layouter, &x, F::from(constant as u64))
692    }
693
694    fn is_not_equal_to_fixed(
695        &self,
696        layouter: &mut impl Layouter<F>,
697        byte: &AssignedByte<F>,
698        constant: u8,
699    ) -> Result<AssignedBit<F>, Error> {
700        let x: AssignedNative<F> = self.convert(layouter, byte)?;
701        self.native_chip.is_not_equal_to_fixed(layouter, &x, F::from(constant as u64))
702    }
703}
704
705impl<F: CircuitField, const N: usize> AssertionInstructions<F, [AssignedByte<F>; N]>
706    for NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>
707{
708    fn assert_equal(
709        &self,
710        layouter: &mut impl Layouter<F>,
711        x: &[AssignedByte<F>; N],
712        y: &[AssignedByte<F>; N],
713    ) -> Result<(), Error> {
714        x.iter().zip(y.iter()).try_for_each(|(x, y)| self.assert_equal(layouter, x, y))
715    }
716
717    fn assert_not_equal(
718        &self,
719        layouter: &mut impl Layouter<F>,
720        x: &[AssignedByte<F>; N],
721        y: &[AssignedByte<F>; N],
722    ) -> Result<(), Error> {
723        // TODO: This can be optimized by first aggregating as many bytes as possible in
724        // a single AssignedNative and only then comparing chunk-wise.
725        let xi_eq_yi = (x.iter())
726            .zip(y.iter())
727            .map(|(x, y)| self.is_equal(layouter, x, y))
728            .collect::<Result<Vec<_>, Error>>()?;
729        let all_equal = self.and(layouter, &xi_eq_yi)?;
730        self.assert_equal_to_fixed(layouter, &all_equal, false)
731    }
732
733    fn assert_equal_to_fixed(
734        &self,
735        layouter: &mut impl Layouter<F>,
736        x: &[AssignedByte<F>; N],
737        constant: [u8; N],
738    ) -> Result<(), Error> {
739        x.iter()
740            .zip(constant.iter())
741            .try_for_each(|(x, y)| self.assert_equal_to_fixed(layouter, x, *y))
742    }
743
744    fn assert_not_equal_to_fixed(
745        &self,
746        layouter: &mut impl Layouter<F>,
747        x: &[AssignedByte<F>; N],
748        constant: [u8; N],
749    ) -> Result<(), Error> {
750        // TODO: This can be optimized by first aggregating as many bytes as possible in
751        // a single AssignedNative and only then comparing chunk-wise.
752        let xi_eq_ci = (x.iter())
753            .zip(constant.iter())
754            .map(|(x, c)| self.is_equal_to_fixed(layouter, x, *c))
755            .collect::<Result<Vec<_>, Error>>()?;
756        let all_equal = self.and(layouter, &xi_eq_ci)?;
757        self.assert_equal_to_fixed(layouter, &all_equal, false)
758    }
759}
760
761/// Conversion from AssignedNative to AssignedByte.
762impl<F, CoreDecomposition, NativeArith>
763    ConversionInstructions<F, AssignedNative<F>, AssignedByte<F>>
764    for NativeGadget<F, CoreDecomposition, NativeArith>
765where
766    F: CircuitField,
767    CoreDecomposition: CoreDecompositionInstructions<F>,
768    NativeArith: ArithInstructions<F, AssignedNative<F>>,
769{
770    fn convert_value(&self, x: &F) -> Option<u8> {
771        let b_as_bn = x.to_biguint();
772        #[cfg(not(test))]
773        assert!(
774            b_as_bn <= BigUint::from(255u8),
775            "Trying to convert {:?} to AssignedByte in-circuit",
776            x
777        );
778        b_as_bn.to_bytes_le().first().cloned()
779    }
780
781    fn convert(
782        &self,
783        layouter: &mut impl Layouter<F>,
784        x: &AssignedNative<F>,
785    ) -> Result<AssignedByte<F>, Error> {
786        if let Some(current_bound) = self.constrained_cells.borrow().get(x) {
787            if *current_bound <= BigUint::from(256u32) {
788                return self.convert_unsafe(layouter, x);
789            }
790        }
791        self.update_bound(x, BigUint::from(256u32));
792        let b_value = x.value().map(|x| {
793            <Self as ConversionInstructions<_, _, AssignedByte<F>>>::convert_value(self, x)
794                .unwrap_or(0u8)
795        });
796        let b: AssignedByte<F> = self.assign(layouter, b_value)?;
797        self.assert_equal(layouter, x, &b.0)?;
798        Ok(b)
799    }
800}
801
802/// Conversion from AssignedByte to AssignedNative.
803impl<F, CoreDecomposition, NativeArith>
804    ConversionInstructions<F, AssignedByte<F>, AssignedNative<F>>
805    for NativeGadget<F, CoreDecomposition, NativeArith>
806where
807    F: CircuitField,
808    CoreDecomposition: CoreDecompositionInstructions<F>,
809    NativeArith: ArithInstructions<F, AssignedNative<F>>,
810{
811    fn convert_value(&self, x: &u8) -> Option<F> {
812        Some(F::from(*x as u64))
813    }
814
815    fn convert(
816        &self,
817        _layouter: &mut impl Layouter<F>,
818        byte: &AssignedByte<F>,
819    ) -> Result<AssignedNative<F>, Error> {
820        self.update_bound(&byte.0, BigUint::from(256u32));
821        Ok(byte.0.clone())
822    }
823}
824
825/// Unsafe conversion from AssignedNative to AssignedByte.
826impl<F, CoreDecomposition, NativeArith>
827    UnsafeConversionInstructions<F, AssignedNative<F>, AssignedByte<F>>
828    for NativeGadget<F, CoreDecomposition, NativeArith>
829where
830    F: CircuitField,
831    CoreDecomposition: CoreDecompositionInstructions<F>,
832    NativeArith: ArithInstructions<F, AssignedNative<F>>,
833{
834    /// CAUTION: use only if you know what you are doing!
835    ///
836    /// This function converts an `AssignedNative` to an `AssignedByte`
837    /// *without* adding any constraint to guarantee the "byteness" of the
838    /// assigned value.
839    ///
840    /// *It should be used only when the input x is already guaranteed to be a
841    /// byte*
842    fn convert_unsafe(
843        &self,
844        _layouter: &mut impl Layouter<F>,
845        x: &AssignedNative<F>,
846    ) -> Result<AssignedByte<F>, Error> {
847        #[cfg(not(test))]
848        x.value().map(|&x| {
849            let x = x.to_biguint();
850            assert!(
851                x <= BigUint::from(255u8),
852                "Trying to convert {:?} to an AssignedByte!",
853                x
854            );
855        });
856        Ok(AssignedByte(x.clone()))
857    }
858}
859
860/// The set of circuit instructions for decomposition operations.
861impl<F, CoreDecomposition, NativeArith> DecompositionInstructions<F, AssignedNative<F>>
862    for NativeGadget<F, CoreDecomposition, NativeArith>
863where
864    F: CircuitField,
865    CoreDecomposition: CoreDecompositionInstructions<F>,
866    NativeArith: ArithInstructions<F, AssignedNative<F>>
867        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
868        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
869        + AssignmentInstructions<F, AssignedBit<F>>
870        + BinaryInstructions<F>
871        + EqualityInstructions<F, AssignedNative<F>>
872        + ControlFlowInstructions<F, AssignedNative<F>>,
873    NativeGadget<F, CoreDecomposition, NativeArith>: CanonicityInstructions<F, AssignedNative<F>>
874        + AssertionInstructions<F, AssignedBit<F>>
875        + AssignmentInstructions<F, AssignedBit<F>>
876        + BinaryInstructions<F>
877        + EqualityInstructions<F, AssignedNative<F>>
878        + ControlFlowInstructions<F, AssignedNative<F>>,
879{
880    fn assigned_to_le_bits(
881        &self,
882        layouter: &mut impl Layouter<F>,
883        x: &AssignedNative<F>,
884        nb_bits: Option<usize>,
885        enforce_canonical: bool,
886    ) -> Result<Vec<AssignedBit<F>>, Error> {
887        let nb_bits = nb_bits.unwrap_or(F::NUM_BITS as usize);
888
889        assert!(
890            nb_bits <= F::NUM_BITS as usize,
891            "assigned_to_le_bits: why do you need the output to have more bits than necessary?"
892        );
893
894        let limbs = self
895            .core_decomposition_chip
896            .decompose_fixed_limb_size(layouter, x, nb_bits, 1)?;
897        let bits = limbs
898            .iter()
899            .map(|x| self.native_chip.convert_unsafe(layouter, x))
900            .collect::<Result<Vec<_>, Error>>()?;
901        if enforce_canonical && nb_bits == F::NUM_BITS as usize {
902            debug_assert_eq!(F::modulus().bits(), F::NUM_BITS as u64);
903            // To enforce canonicity, we leverage the fact that `F::NUM_BITS` is tight:
904            // field elements have at most 2 representations as bitstrings of length
905            // `F::NUM_BITS`, and when 2 representations exist, they differ in the LSB
906            // (since the modulus is a large prime, which is odd).
907            // Thus, we can enforce canonicity by checking that the least significant bit of
908            // our output matches `sgn0(x)`.
909            // NB: `self.sgn0` is significantly more efficient than `self.is_canonical`.
910            let b0 = self.sgn0(layouter, x)?;
911            self.assert_equal(layouter, &bits[0], &b0)?;
912        }
913        Ok(bits)
914    }
915
916    fn assigned_to_le_bytes(
917        &self,
918        layouter: &mut impl Layouter<F>,
919        x: &AssignedNative<F>,
920        nb_bytes: Option<usize>,
921    ) -> Result<Vec<AssignedByte<F>>, Error> {
922        let f_num_bytes = F::NUM_BITS.div_ceil(8);
923        let nb_bytes = nb_bytes.unwrap_or(f_num_bytes as usize);
924        if nb_bytes > f_num_bytes as usize {
925            panic!("assigned_to_le_bytes: why do you need the output to have more bytes than necessary?");
926        }
927        // If nb_bytes equals ⌈F::NUM_BITS / 8⌉, we need extra care to
928        // guarantee that the output is canonical: we split in bits enforcing canonicity
929        // and then group the bits in bytes.
930        if nb_bytes == f_num_bytes as usize {
931            let bits = self.assigned_to_le_bits(layouter, x, Some(F::NUM_BITS as usize), true)?;
932            bits.chunks(8)
933                .map(|chunk| {
934                    let terms = chunk
935                        .iter()
936                        .enumerate()
937                        .map(|(i, bit)| (F::from(1 << i), bit.clone().into()))
938                        .collect::<Vec<_>>();
939                    let byte = self.linear_combination(layouter, &terms, F::ZERO)?;
940                    self.convert_unsafe(layouter, &byte)
941                })
942                .collect::<Result<Vec<AssignedByte<F>>, Error>>()
943        }
944        // If nb_bytes < ⌈F::NUM_BITS / 8⌉, wrap-arounds are not possible, so canonicity is always
945        // guaranteed. In this case we can split in bytes more efficiently.
946        else {
947            let limbs = self.core_decomposition_chip.decompose_fixed_limb_size(
948                layouter,
949                x,
950                8 * nb_bytes,
951                8,
952            )?;
953            limbs
954                .iter()
955                .map(|x| self.convert_unsafe(layouter, x))
956                .collect::<Result<Vec<_>, Error>>()
957        }
958    }
959
960    fn assigned_to_le_chunks(
961        &self,
962        layouter: &mut impl Layouter<F>,
963        x: &AssignedNative<F>,
964        nb_bits_per_chunk: usize,
965        nb_chunks: Option<usize>,
966    ) -> Result<Vec<AssignedNative<F>>, Error> {
967        assert!(nb_bits_per_chunk < F::NUM_BITS as usize);
968        let nb_chunks = nb_chunks.unwrap_or((F::NUM_BITS as usize).div_ceil(nb_bits_per_chunk));
969        self.core_decomposition_chip.decompose_fixed_limb_size(
970            layouter,
971            x,
972            nb_bits_per_chunk * nb_chunks,
973            nb_bits_per_chunk,
974        )
975    }
976
977    fn sgn0(
978        &self,
979        layouter: &mut impl Layouter<F>,
980        x: &AssignedNative<F>,
981    ) -> Result<AssignedBit<F>, Error> {
982        // Any element in Zp can be uniquely represented as 2 * w + e, where
983        // w in [0, (p-1)/2] and e in {0, 1}, with the exception of zero, which
984        // admits two representations: (w = 0, e = 0) and (w = (p-1)/2, e = 1).
985        let x_val = x.value().copied().map(|v| v.to_biguint());
986        let w_val = x_val.clone().map(|x| &x / BigUint::from(2u8));
987        let e_val = x_val.clone().map(|x| x.bit(0));
988
989        let e: AssignedBit<F> = self.assign(layouter, e_val)?;
990        let w = self.assign_lower_than_fixed(
991            layouter,
992            w_val.map(big_to_fe::<F>),
993            &(&(F::modulus() + BigUint::from(1u8)) / BigUint::from(2u8)),
994        )?;
995        let must_be_x = self.linear_combination(
996            layouter,
997            &[(F::ONE, e.clone().into()), (F::from(2), w.clone())],
998            F::ZERO,
999        )?;
1000        self.assert_equal(layouter, x, &must_be_x)?;
1001        // The edge case x = 0 is no problem because `x_is_not_zero` is false in that
1002        // case and we will still assign sgn0(0) = 0.
1003        let x_is_zero: AssignedBit<F> = self.is_zero(layouter, x)?;
1004        let x_is_not_zero: AssignedBit<F> = self.not(layouter, &x_is_zero)?;
1005        let sgn0 = self.and(layouter, &[x_is_not_zero, e])?;
1006
1007        Ok(sgn0)
1008    }
1009}
1010
1011// Inherit F Public Input Instructions.
1012impl<F, CoreDecomposition, NativeArith> PublicInputInstructions<F, AssignedNative<F>>
1013    for NativeGadget<F, CoreDecomposition, NativeArith>
1014where
1015    F: CircuitField,
1016    CoreDecomposition: CoreDecompositionInstructions<F>,
1017    NativeArith:
1018        PublicInputInstructions<F, AssignedNative<F>> + ArithInstructions<F, AssignedNative<F>>,
1019{
1020    fn as_public_input(
1021        &self,
1022        layouter: &mut impl Layouter<F>,
1023        assigned: &AssignedNative<F>,
1024    ) -> Result<Vec<AssignedNative<F>>, Error> {
1025        self.native_chip.as_public_input(layouter, assigned)
1026    }
1027
1028    fn constrain_as_public_input(
1029        &self,
1030        layouter: &mut impl Layouter<F>,
1031        assigned: &AssignedNative<F>,
1032    ) -> Result<(), Error> {
1033        self.native_chip.constrain_as_public_input(layouter, assigned)
1034    }
1035
1036    fn assign_as_public_input(
1037        &self,
1038        layouter: &mut impl Layouter<F>,
1039        value: Value<F>,
1040    ) -> Result<AssignedNative<F>, Error> {
1041        self.native_chip.assign_as_public_input(layouter, value)
1042    }
1043}
1044
1045// Inherit F Assignment Instructions.
1046impl<F, CoreDecomposition, NativeArith> AssignmentInstructions<F, AssignedNative<F>>
1047    for NativeGadget<F, CoreDecomposition, NativeArith>
1048where
1049    F: CircuitField,
1050    CoreDecomposition: CoreDecompositionInstructions<F>,
1051    NativeArith: ArithInstructions<F, AssignedNative<F>>,
1052{
1053    fn assign(
1054        &self,
1055        layouter: &mut impl Layouter<F>,
1056        value: Value<F>,
1057    ) -> Result<AssignedNative<F>, Error> {
1058        self.native_chip.assign(layouter, value)
1059    }
1060
1061    fn assign_fixed(
1062        &self,
1063        layouter: &mut impl Layouter<F>,
1064        constant: F,
1065    ) -> Result<AssignedNative<F>, Error> {
1066        self.native_chip.assign_fixed(layouter, constant)
1067    }
1068
1069    fn assign_many(
1070        &self,
1071        layouter: &mut impl Layouter<F>,
1072        value: &[Value<F>],
1073    ) -> Result<Vec<AssignedNative<F>>, Error> {
1074        self.native_chip.assign_many(layouter, value)
1075    }
1076}
1077
1078// Inherit Bit Public Input Instructions.
1079impl<F, CoreDecomposition, NativeArith> PublicInputInstructions<F, AssignedBit<F>>
1080    for NativeGadget<F, CoreDecomposition, NativeArith>
1081where
1082    F: CircuitField,
1083    CoreDecomposition: CoreDecompositionInstructions<F>,
1084    NativeArith:
1085        PublicInputInstructions<F, AssignedBit<F>> + ArithInstructions<F, AssignedNative<F>>,
1086{
1087    fn as_public_input(
1088        &self,
1089        layouter: &mut impl Layouter<F>,
1090        assigned: &AssignedBit<F>,
1091    ) -> Result<Vec<AssignedNative<F>>, Error> {
1092        self.native_chip.as_public_input(layouter, assigned)
1093    }
1094
1095    fn constrain_as_public_input(
1096        &self,
1097        layouter: &mut impl Layouter<F>,
1098        assigned: &AssignedBit<F>,
1099    ) -> Result<(), Error> {
1100        self.native_chip.constrain_as_public_input(layouter, assigned)
1101    }
1102
1103    fn assign_as_public_input(
1104        &self,
1105        layouter: &mut impl Layouter<F>,
1106        value: Value<bool>,
1107    ) -> Result<AssignedBit<F>, Error> {
1108        self.native_chip.assign_as_public_input(layouter, value)
1109    }
1110}
1111
1112// Inherit Bit Assignment Instructions.
1113impl<F, CoreDecomposition, NativeArith> AssignmentInstructions<F, AssignedBit<F>>
1114    for NativeGadget<F, CoreDecomposition, NativeArith>
1115where
1116    F: CircuitField,
1117    CoreDecomposition: CoreDecompositionInstructions<F>,
1118    NativeArith: ArithInstructions<F, AssignedNative<F>>
1119        + AssignmentInstructions<F, AssignedBit<F>>
1120        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>,
1121{
1122    fn assign(
1123        &self,
1124        layouter: &mut impl Layouter<F>,
1125        value: Value<bool>,
1126    ) -> Result<AssignedBit<F>, Error> {
1127        self.native_chip.assign(layouter, value)
1128    }
1129
1130    fn assign_fixed(
1131        &self,
1132        layouter: &mut impl Layouter<F>,
1133        constant: bool,
1134    ) -> Result<AssignedBit<F>, Error> {
1135        self.native_chip.assign_fixed(layouter, constant)
1136    }
1137
1138    fn assign_many(
1139        &self,
1140        layouter: &mut impl Layouter<F>,
1141        values: &[Value<bool>],
1142    ) -> Result<Vec<AssignedBit<F>>, Error> {
1143        let values_as_f: Vec<_> = values.iter().map(|v| v.map(|b| F::from(b as u64))).collect();
1144
1145        self.core_decomposition_chip
1146            .assign_many_small(layouter, &values_as_f, 1)?
1147            .iter()
1148            .map(|assigned_native| self.native_chip.convert_unsafe(layouter, assigned_native))
1149            .collect()
1150    }
1151}
1152
1153// Inherit F Assertion Instructions.
1154impl<F, CoreDecomposition, NativeArith> AssertionInstructions<F, AssignedNative<F>>
1155    for NativeGadget<F, CoreDecomposition, NativeArith>
1156where
1157    F: CircuitField,
1158    CoreDecomposition: CoreDecompositionInstructions<F>,
1159    NativeArith: ArithInstructions<F, AssignedNative<F>>,
1160{
1161    fn assert_equal(
1162        &self,
1163        layouter: &mut impl Layouter<F>,
1164        x: &AssignedNative<F>,
1165        y: &AssignedNative<F>,
1166    ) -> Result<(), Error> {
1167        let x_bound_opt = self.constrained_cells.borrow().get(x).cloned();
1168        if let Some(x_bound) = x_bound_opt {
1169            self.update_bound(y, x_bound.clone());
1170        }
1171
1172        let y_bound_opt = self.constrained_cells.borrow().get(y).cloned();
1173        if let Some(y_bound) = y_bound_opt {
1174            self.update_bound(x, y_bound.clone());
1175        }
1176
1177        self.native_chip.assert_equal(layouter, x, y)
1178    }
1179
1180    fn assert_not_equal(
1181        &self,
1182        layouter: &mut impl Layouter<F>,
1183        x: &AssignedNative<F>,
1184        y: &AssignedNative<F>,
1185    ) -> Result<(), Error> {
1186        self.native_chip.assert_not_equal(layouter, x, y)
1187    }
1188
1189    fn assert_equal_to_fixed(
1190        &self,
1191        layouter: &mut impl Layouter<F>,
1192        x: &AssignedNative<F>,
1193        constant: F,
1194    ) -> Result<(), Error> {
1195        self.native_chip.assert_equal_to_fixed(layouter, x, constant)
1196    }
1197
1198    fn assert_not_equal_to_fixed(
1199        &self,
1200        layouter: &mut impl Layouter<F>,
1201        x: &AssignedNative<F>,
1202        constant: F,
1203    ) -> Result<(), Error> {
1204        self.native_chip.assert_not_equal_to_fixed(layouter, x, constant)
1205    }
1206}
1207
1208// Inherit Bit Assertion Instructions.
1209impl<F, CoreDecomposition, NativeArith> AssertionInstructions<F, AssignedBit<F>>
1210    for NativeGadget<F, CoreDecomposition, NativeArith>
1211where
1212    F: CircuitField,
1213    CoreDecomposition: CoreDecompositionInstructions<F>,
1214    NativeArith: ArithInstructions<F, AssignedNative<F>> + AssertionInstructions<F, AssignedBit<F>>,
1215{
1216    fn assert_equal(
1217        &self,
1218        layouter: &mut impl Layouter<F>,
1219        x: &AssignedBit<F>,
1220        y: &AssignedBit<F>,
1221    ) -> Result<(), Error> {
1222        self.native_chip.assert_equal(layouter, x, y)
1223    }
1224
1225    fn assert_not_equal(
1226        &self,
1227        layouter: &mut impl Layouter<F>,
1228        x: &AssignedBit<F>,
1229        y: &AssignedBit<F>,
1230    ) -> Result<(), Error> {
1231        self.native_chip.assert_not_equal(layouter, x, y)
1232    }
1233
1234    fn assert_equal_to_fixed(
1235        &self,
1236        layouter: &mut impl Layouter<F>,
1237        x: &AssignedBit<F>,
1238        constant: bool,
1239    ) -> Result<(), Error> {
1240        self.native_chip.assert_equal_to_fixed(layouter, x, constant)
1241    }
1242
1243    fn assert_not_equal_to_fixed(
1244        &self,
1245        layouter: &mut impl Layouter<F>,
1246        x: &AssignedBit<F>,
1247        constant: bool,
1248    ) -> Result<(), Error> {
1249        self.native_chip.assert_not_equal_to_fixed(layouter, x, constant)
1250    }
1251}
1252
1253// Inherit Arith Instructions.
1254impl<F, CoreDecomposition, NativeArith> ArithInstructions<F, AssignedNative<F>>
1255    for NativeGadget<F, CoreDecomposition, NativeArith>
1256where
1257    F: CircuitField,
1258    CoreDecomposition: CoreDecompositionInstructions<F>,
1259    NativeArith: ArithInstructions<F, AssignedNative<F>>,
1260{
1261    fn linear_combination(
1262        &self,
1263        layouter: &mut impl Layouter<F>,
1264        terms: &[(F, AssignedNative<F>)],
1265        constant: F,
1266    ) -> Result<AssignedNative<F>, Error> {
1267        self.native_chip.linear_combination(layouter, terms, constant)
1268    }
1269
1270    fn mul(
1271        &self,
1272        layouter: &mut impl Layouter<F>,
1273        x: &AssignedNative<F>,
1274        y: &AssignedNative<F>,
1275        multiplying_constant: Option<F>,
1276    ) -> Result<AssignedNative<F>, Error> {
1277        self.native_chip.mul(layouter, x, y, multiplying_constant)
1278    }
1279
1280    fn div(
1281        &self,
1282        layouter: &mut impl Layouter<F>,
1283        x: &AssignedNative<F>,
1284        y: &AssignedNative<F>,
1285    ) -> Result<AssignedNative<F>, Error> {
1286        self.native_chip.div(layouter, x, y)
1287    }
1288
1289    fn inv(
1290        &self,
1291        layouter: &mut impl Layouter<F>,
1292        x: &AssignedNative<F>,
1293    ) -> Result<AssignedNative<F>, Error> {
1294        self.native_chip.inv(layouter, x)
1295    }
1296
1297    fn inv0(
1298        &self,
1299        layouter: &mut impl Layouter<F>,
1300        x: &AssignedNative<F>,
1301    ) -> Result<AssignedNative<F>, Error> {
1302        self.native_chip.inv0(layouter, x)
1303    }
1304
1305    fn add_and_mul(
1306        &self,
1307        layouter: &mut impl Layouter<F>,
1308        a_and_x: (F, &AssignedNative<F>),
1309        b_and_y: (F, &AssignedNative<F>),
1310        c_and_z: (F, &AssignedNative<F>),
1311        k: F,
1312        m: F,
1313    ) -> Result<AssignedNative<F>, Error> {
1314        self.native_chip.add_and_mul(layouter, a_and_x, b_and_y, c_and_z, k, m)
1315    }
1316
1317    fn add_constants(
1318        &self,
1319        layouter: &mut impl Layouter<F>,
1320        xs: &[AssignedNative<F>],
1321        constants: &[F],
1322    ) -> Result<Vec<AssignedNative<F>>, Error> {
1323        self.native_chip.add_constants(layouter, xs, constants)
1324    }
1325}
1326
1327// Inherit Conversion Instructions to AssignedBit.
1328impl<F, CoreDecomposition, NativeArith> ConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1329    for NativeGadget<F, CoreDecomposition, NativeArith>
1330where
1331    F: CircuitField,
1332    CoreDecomposition: CoreDecompositionInstructions<F>,
1333    NativeArith: ArithInstructions<F, AssignedNative<F>>
1334        + ConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1335        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>,
1336{
1337    fn convert_value(&self, x: &F) -> Option<bool> {
1338        self.native_chip.convert_value(x)
1339    }
1340
1341    fn convert(
1342        &self,
1343        layouter: &mut impl Layouter<F>,
1344        x: &AssignedNative<F>,
1345    ) -> Result<AssignedBit<F>, Error> {
1346        if let Some(current_bound) = self.constrained_cells.borrow().get(x) {
1347            if *current_bound <= BigUint::from(2u32) {
1348                return self.native_chip.convert_unsafe(layouter, x);
1349            }
1350        }
1351        self.update_bound(x, BigUint::from(2u32));
1352        self.native_chip.convert(layouter, x)
1353    }
1354}
1355
1356// Inherit Conversion Instructions from AssignedBit.
1357impl<F, CoreDecomposition, NativeArith> ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
1358    for NativeGadget<F, CoreDecomposition, NativeArith>
1359where
1360    F: CircuitField,
1361    CoreDecomposition: CoreDecompositionInstructions<F>,
1362    NativeArith: ArithInstructions<F, AssignedNative<F>>
1363        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>,
1364{
1365    fn convert_value(&self, x: &bool) -> Option<F> {
1366        self.native_chip.convert_value(x)
1367    }
1368
1369    fn convert(
1370        &self,
1371        layouter: &mut impl Layouter<F>,
1372        x: &AssignedBit<F>,
1373    ) -> Result<AssignedNative<F>, Error> {
1374        let x = self.native_chip.convert(layouter, x)?;
1375        self.update_bound(&x, BigUint::from(2u32));
1376        Ok(x)
1377    }
1378}
1379
1380// Inherit Binary Instructions.
1381impl<F, CoreDecomposition, NativeArith> BinaryInstructions<F>
1382    for NativeGadget<F, CoreDecomposition, NativeArith>
1383where
1384    F: CircuitField,
1385    CoreDecomposition: CoreDecompositionInstructions<F>,
1386    NativeArith: ArithInstructions<F, AssignedNative<F>> + BinaryInstructions<F>,
1387{
1388    fn and(
1389        &self,
1390        layouter: &mut impl Layouter<F>,
1391        bits: &[AssignedBit<F>],
1392    ) -> Result<AssignedBit<F>, Error> {
1393        self.native_chip.and(layouter, bits)
1394    }
1395
1396    fn or(
1397        &self,
1398        layouter: &mut impl Layouter<F>,
1399        bits: &[AssignedBit<F>],
1400    ) -> Result<AssignedBit<F>, Error> {
1401        self.native_chip.or(layouter, bits)
1402    }
1403
1404    fn xor(
1405        &self,
1406        layouter: &mut impl Layouter<F>,
1407        bits: &[AssignedBit<F>],
1408    ) -> Result<AssignedBit<F>, Error> {
1409        self.native_chip.xor(layouter, bits)
1410    }
1411
1412    fn not(
1413        &self,
1414        layouter: &mut impl Layouter<F>,
1415        b: &AssignedBit<F>,
1416    ) -> Result<AssignedBit<F>, Error> {
1417        self.native_chip.not(layouter, b)
1418    }
1419}
1420
1421// Inherit F Equality Instructions.
1422impl<F, CoreDecomposition, NativeArith> EqualityInstructions<F, AssignedNative<F>>
1423    for NativeGadget<F, CoreDecomposition, NativeArith>
1424where
1425    F: CircuitField,
1426    CoreDecomposition: CoreDecompositionInstructions<F>,
1427    NativeArith:
1428        ArithInstructions<F, AssignedNative<F>> + EqualityInstructions<F, AssignedNative<F>>,
1429{
1430    fn is_equal(
1431        &self,
1432        layouter: &mut impl Layouter<F>,
1433        x: &AssignedNative<F>,
1434        y: &AssignedNative<F>,
1435    ) -> Result<AssignedBit<F>, Error> {
1436        self.native_chip.is_equal(layouter, x, y)
1437    }
1438
1439    fn is_not_equal(
1440        &self,
1441        layouter: &mut impl Layouter<F>,
1442        x: &AssignedNative<F>,
1443        y: &AssignedNative<F>,
1444    ) -> Result<AssignedBit<F>, Error> {
1445        self.native_chip.is_not_equal(layouter, x, y)
1446    }
1447
1448    fn is_equal_to_fixed(
1449        &self,
1450        layouter: &mut impl Layouter<F>,
1451        x: &AssignedNative<F>,
1452        constant: F,
1453    ) -> Result<AssignedBit<F>, Error> {
1454        self.native_chip.is_equal_to_fixed(layouter, x, constant)
1455    }
1456
1457    fn is_not_equal_to_fixed(
1458        &self,
1459        layouter: &mut impl Layouter<F>,
1460        x: &AssignedNative<F>,
1461        constant: F,
1462    ) -> Result<AssignedBit<F>, Error> {
1463        self.native_chip.is_not_equal_to_fixed(layouter, x, constant)
1464    }
1465}
1466
1467// Implement Bit Equality Instructions.
1468impl<F, CoreDecomposition, NativeArith> EqualityInstructions<F, AssignedBit<F>>
1469    for NativeGadget<F, CoreDecomposition, NativeArith>
1470where
1471    F: CircuitField,
1472    CoreDecomposition: CoreDecompositionInstructions<F>,
1473    NativeArith: ArithInstructions<F, AssignedNative<F>> + EqualityInstructions<F, AssignedBit<F>>,
1474{
1475    fn is_equal(
1476        &self,
1477        layouter: &mut impl Layouter<F>,
1478        x: &AssignedBit<F>,
1479        y: &AssignedBit<F>,
1480    ) -> Result<AssignedBit<F>, Error> {
1481        self.native_chip.is_equal(layouter, x, y)
1482    }
1483
1484    fn is_not_equal(
1485        &self,
1486        layouter: &mut impl Layouter<F>,
1487        x: &AssignedBit<F>,
1488        y: &AssignedBit<F>,
1489    ) -> Result<AssignedBit<F>, Error> {
1490        self.native_chip.is_not_equal(layouter, x, y)
1491    }
1492
1493    fn is_equal_to_fixed(
1494        &self,
1495        layouter: &mut impl Layouter<F>,
1496        x: &AssignedBit<F>,
1497        constant: bool,
1498    ) -> Result<AssignedBit<F>, Error> {
1499        self.native_chip.is_equal_to_fixed(layouter, x, constant)
1500    }
1501
1502    fn is_not_equal_to_fixed(
1503        &self,
1504        layouter: &mut impl Layouter<F>,
1505        x: &AssignedBit<F>,
1506        constant: bool,
1507    ) -> Result<AssignedBit<F>, Error> {
1508        self.native_chip.is_not_equal_to_fixed(layouter, x, constant)
1509    }
1510}
1511
1512// Inherit Zero Instructions.
1513impl<F, CoreDecomposition, NativeArith> ZeroInstructions<F, AssignedNative<F>>
1514    for NativeGadget<F, CoreDecomposition, NativeArith>
1515where
1516    F: CircuitField,
1517    CoreDecomposition: CoreDecompositionInstructions<F>,
1518    NativeArith: ArithInstructions<F, AssignedNative<F>>
1519        + AssertionInstructions<F, AssignedNative<F>>
1520        + EqualityInstructions<F, AssignedNative<F>>,
1521{
1522}
1523
1524// Inherit F ControlFlow Instructions.
1525impl<F, CoreDecomposition, NativeArith> ControlFlowInstructions<F, AssignedNative<F>>
1526    for NativeGadget<F, CoreDecomposition, NativeArith>
1527where
1528    F: CircuitField,
1529    CoreDecomposition: CoreDecompositionInstructions<F>,
1530    NativeArith:
1531        ArithInstructions<F, AssignedNative<F>> + ControlFlowInstructions<F, AssignedNative<F>>,
1532{
1533    fn select(
1534        &self,
1535        layouter: &mut impl Layouter<F>,
1536        bit: &AssignedBit<F>,
1537        x: &AssignedNative<F>,
1538        y: &AssignedNative<F>,
1539    ) -> Result<AssignedNative<F>, Error> {
1540        self.native_chip.select(layouter, bit, x, y)
1541    }
1542
1543    fn cond_swap(
1544        &self,
1545        layouter: &mut impl Layouter<F>,
1546        cond: &AssignedBit<F>,
1547        x: &AssignedNative<F>,
1548        y: &AssignedNative<F>,
1549    ) -> Result<(AssignedNative<F>, AssignedNative<F>), Error> {
1550        self.native_chip.cond_swap(layouter, cond, x, y)
1551    }
1552}
1553
1554// Inherit Bit ControlFlow Instructions.
1555impl<F, CoreDecomposition, NativeArith> ControlFlowInstructions<F, AssignedBit<F>>
1556    for NativeGadget<F, CoreDecomposition, NativeArith>
1557where
1558    F: CircuitField,
1559    CoreDecomposition: CoreDecompositionInstructions<F>,
1560    NativeArith:
1561        ArithInstructions<F, AssignedNative<F>> + ControlFlowInstructions<F, AssignedBit<F>>,
1562{
1563    fn select(
1564        &self,
1565        layouter: &mut impl Layouter<F>,
1566        bit: &AssignedBit<F>,
1567        x: &AssignedBit<F>,
1568        y: &AssignedBit<F>,
1569    ) -> Result<AssignedBit<F>, Error> {
1570        self.native_chip.select(layouter, bit, x, y)
1571    }
1572
1573    fn cond_swap(
1574        &self,
1575        layouter: &mut impl Layouter<F>,
1576        bit: &AssignedBit<F>,
1577        x: &AssignedBit<F>,
1578        y: &AssignedBit<F>,
1579    ) -> Result<(AssignedBit<F>, AssignedBit<F>), Error> {
1580        self.native_chip.cond_swap(layouter, bit, x, y)
1581    }
1582}
1583
1584// Implement Byte ControlFlow Instructions.
1585impl<F, CoreDecomposition, NativeArith> ControlFlowInstructions<F, AssignedByte<F>>
1586    for NativeGadget<F, CoreDecomposition, NativeArith>
1587where
1588    F: CircuitField,
1589    CoreDecomposition: CoreDecompositionInstructions<F>,
1590    NativeArith:
1591        ArithInstructions<F, AssignedNative<F>> + ControlFlowInstructions<F, AssignedNative<F>>,
1592{
1593    fn select(
1594        &self,
1595        layouter: &mut impl Layouter<F>,
1596        bit: &AssignedBit<F>,
1597        x: &AssignedByte<F>,
1598        y: &AssignedByte<F>,
1599    ) -> Result<AssignedByte<F>, Error> {
1600        let byte = self.native_chip.select(layouter, bit, &x.into(), &y.into())?;
1601        self.convert_unsafe(layouter, &byte)
1602    }
1603
1604    fn cond_swap(
1605        &self,
1606        layouter: &mut impl Layouter<F>,
1607        cond: &AssignedBit<F>,
1608        x: &AssignedByte<F>,
1609        y: &AssignedByte<F>,
1610    ) -> Result<(AssignedByte<F>, AssignedByte<F>), Error> {
1611        let (fst, snd) = (self.native_chip).cond_swap(layouter, cond, &x.into(), &y.into())?;
1612        let fst = self.convert_unsafe(layouter, &fst)?;
1613        let snd = self.convert_unsafe(layouter, &snd)?;
1614        Ok((fst, snd))
1615    }
1616}
1617
1618// Inherit Field Instructions.
1619impl<F, CoreDecomposition, NativeArith> FieldInstructions<F, AssignedNative<F>>
1620    for NativeGadget<F, CoreDecomposition, NativeArith>
1621where
1622    F: CircuitField,
1623    CoreDecomposition: CoreDecompositionInstructions<F>,
1624    NativeArith: FieldInstructions<F, AssignedNative<F>>
1625        + AssertionInstructions<F, AssignedBit<F>>
1626        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1627        + EqualityInstructions<F, AssignedBit<F>>,
1628{
1629    fn order(&self) -> BigUint {
1630        self.native_chip.order()
1631    }
1632}
1633
1634// Implement Scalar Field Instructions.
1635impl<F, CoreDecomposition, NativeArith> ScalarFieldInstructions<F>
1636    for NativeGadget<F, CoreDecomposition, NativeArith>
1637where
1638    F: CircuitField,
1639    CoreDecomposition: CoreDecompositionInstructions<F>,
1640    NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1641        + AssertionInstructions<F, AssignedBit<F>>
1642        + EqualityInstructions<F, AssignedBit<F>>
1643        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1644        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
1645        + AssignmentInstructions<F, AssignedBit<F>>
1646        + BinaryInstructions<F>,
1647{
1648    type Scalar = AssignedNative<F>;
1649}
1650
1651// Inherit Canonicity Instructions.
1652impl<F, CoreDecomposition, NativeArith> CanonicityInstructions<F, AssignedNative<F>>
1653    for NativeGadget<F, CoreDecomposition, NativeArith>
1654where
1655    F: CircuitField,
1656    CoreDecomposition: CoreDecompositionInstructions<F>,
1657    NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1658        + AssertionInstructions<F, AssignedBit<F>>
1659        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1660        + EqualityInstructions<F, AssignedBit<F>>,
1661{
1662    fn le_bits_lower_than(
1663        &self,
1664        layouter: &mut impl Layouter<F>,
1665        bits: &[AssignedBit<F>],
1666        bound: BigUint,
1667    ) -> Result<AssignedBit<F>, Error> {
1668        self.native_chip.le_bits_lower_than(layouter, bits, bound)
1669    }
1670
1671    fn le_bits_geq_than(
1672        &self,
1673        layouter: &mut impl Layouter<F>,
1674        bits: &[AssignedBit<F>],
1675        bound: BigUint,
1676    ) -> Result<AssignedBit<F>, Error> {
1677        self.native_chip.le_bits_geq_than(layouter, bits, bound)
1678    }
1679}
1680
1681// Implement Native Instructions.
1682impl<F, CoreDecomposition, NativeArith> NativeInstructions<F>
1683    for NativeGadget<F, CoreDecomposition, NativeArith>
1684where
1685    F: CircuitField,
1686    CoreDecomposition: CoreDecompositionInstructions<F>,
1687    NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1688        + AssertionInstructions<F, AssignedBit<F>>
1689        + EqualityInstructions<F, AssignedBit<F>>
1690        + ControlFlowInstructions<F, AssignedBit<F>>
1691        + BinaryInstructions<F>
1692        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1693        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>,
1694{
1695}
1696
1697// Implement Bitwise Instructions.
1698impl<F, CoreDecomposition, NativeArith> BitwiseInstructions<F, AssignedNative<F>>
1699    for NativeGadget<F, CoreDecomposition, NativeArith>
1700where
1701    F: CircuitField,
1702    CoreDecomposition: CoreDecompositionInstructions<F>,
1703    NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1704        + AssertionInstructions<F, AssignedBit<F>>
1705        + EqualityInstructions<F, AssignedBit<F>>
1706        + ControlFlowInstructions<F, AssignedBit<F>>
1707        + BinaryInstructions<F>
1708        + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1709        + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>,
1710{
1711}
1712
1713// Circuit implementation for NativeGadget based on the
1714// P2RDecompositionChip and the NativeChip
1715#[cfg(any(test, feature = "testing"))]
1716impl<F: CircuitField> FromScratch<F> for NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>> {
1717    // The circuit config is simply the P2RDecompositionConfig since this contains a
1718    // NativeConfig as well
1719    type Config = P2RDecompositionConfig;
1720
1721    fn new_from_scratch(config: &Self::Config) -> Self {
1722        let max_bit_len = 8;
1723        let native_chip = NativeChip::new_from_scratch(&config.native_config);
1724        let core_decomposition_chip = P2RDecompositionChip::new(config, &max_bit_len);
1725        NativeGadget::new(core_decomposition_chip, native_chip)
1726    }
1727
1728    fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1729        self.native_chip.load_from_scratch(layouter)?;
1730        self.core_decomposition_chip.load(layouter)
1731    }
1732
1733    fn configure_from_scratch(
1734        meta: &mut ConstraintSystem<F>,
1735        advice_columns: &mut Vec<Column<Advice>>,
1736        fixed_columns: &mut Vec<Column<Fixed>>,
1737        instance_columns: &[Column<Instance>; 2],
1738    ) -> Self::Config {
1739        while advice_columns.len() < NB_ARITH_COLS {
1740            advice_columns.push(meta.advice_column());
1741        }
1742        while fixed_columns.len() < NB_ARITH_FIXED_COLS {
1743            fixed_columns.push(meta.fixed_column());
1744        }
1745        let advice_cols: [_; NB_ARITH_COLS] = advice_columns[..NB_ARITH_COLS].try_into().unwrap();
1746        let fixed_cols: [_; NB_ARITH_FIXED_COLS] =
1747            fixed_columns[..NB_ARITH_FIXED_COLS].try_into().unwrap();
1748
1749        let native_config =
1750            NativeChip::configure(meta, &(advice_cols, fixed_cols, *instance_columns));
1751        // Use hard-coded value for nr of range check cols in test
1752        let pow2range_config = Pow2RangeChip::configure(meta, &advice_cols[1..=4]);
1753
1754        P2RDecompositionConfig {
1755            native_config,
1756            pow2range_config,
1757        }
1758    }
1759}
1760
1761#[cfg(test)]
1762mod tests {
1763    use midnight_curves::Fq as BlsScalar;
1764
1765    use super::*;
1766    use crate::instructions::{bitwise, comparison, decomposition, division, range_check};
1767
1768    macro_rules! test {
1769        ($module:ident, $operation:ident) => {
1770            #[test]
1771            fn $operation() {
1772                $module::tests::$operation::<
1773                    BlsScalar,
1774                    AssignedNative<BlsScalar>,
1775                    NativeGadget<BlsScalar, P2RDecompositionChip<BlsScalar>, NativeChip<BlsScalar>>,
1776                    NativeGadget<BlsScalar, P2RDecompositionChip<BlsScalar>, NativeChip<BlsScalar>>,
1777                >("native_gadget_bls");
1778            }
1779        };
1780    }
1781
1782    test!(decomposition, test_bit_decomposition);
1783    test!(decomposition, test_byte_decomposition);
1784    test!(decomposition, test_sgn0);
1785
1786    macro_rules! test {
1787        ($module:ident, $operation:ident) => {
1788            #[test]
1789            fn $operation() {
1790                $module::tests::$operation::<
1791                    BlsScalar,
1792                    AssignedNative<BlsScalar>,
1793                    NativeGadget<BlsScalar, P2RDecompositionChip<BlsScalar>, NativeChip<BlsScalar>>,
1794                >("native_gadget_bls");
1795            }
1796        };
1797    }
1798
1799    test!(comparison, test_lower_and_greater);
1800    test!(comparison, test_assert_bounded_element);
1801    test!(range_check, test_assert_lower_than_fixed);
1802    test!(division, test_div_rem);
1803
1804    test!(bitwise, test_band);
1805    test!(bitwise, test_bor);
1806    test!(bitwise, test_bxor);
1807    test!(bitwise, test_bnot);
1808}