1use 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)]
57pub 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 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 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 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#[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#[derive(Clone, Debug)]
160pub struct BoundedElement<F: CircuitField> {
161 value: F,
162 bound: u32,
163}
164
165impl<F: CircuitField> BoundedElement<F> {
166 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 pub fn field_value(&self) -> F {
186 self.value
187 }
188
189 pub fn bound(&self) -> u32 {
191 self.bound
192 }
193}
194
195#[derive(Clone, Debug)]
200pub struct AssignedBounded<F: CircuitField> {
201 value: AssignedNative<F>,
202 bound: u32,
203}
204
205impl<F: CircuitField> AssignedBounded<F> {
206 pub(crate) fn to_assigned_bounded_unsafe(x: &AssignedNative<F>, bound: u32) -> Self {
214 let _new = x.value().map(|&x| BoundedElement::new(x, bound));
216 AssignedBounded {
217 value: x.clone(),
218 bound,
219 }
220 }
221
222 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 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 let k = (bound.bits() - 1) as usize;
285 let two_pow_k = BigUint::from(1u8) << k; if two_pow_k == *bound {
289 return self.core_decomposition_chip.assert_less_than_pow2(layouter, x, k);
290 }
291
292 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 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 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 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 #[cfg(not(test))]
372 assert!(y_as_bint < BigUint::from(1u8) << Self::MAX_BOUND_IN_BITS);
373
374 if y_as_bint >= (BigUint::from(1u8) << x.bound()) {
377 return self.assign_fixed(layouter, true);
378 }
379
380 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 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 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 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 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 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
499impl<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 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
559impl<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
602impl<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
653impl<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 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 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
761impl<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
802impl<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
825impl<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 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
860impl<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 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 == 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 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 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 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
1011impl<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
1045impl<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
1078impl<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
1112impl<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
1153impl<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
1208impl<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
1253impl<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
1327impl<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
1356impl<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
1380impl<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
1421impl<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
1467impl<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
1512impl<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
1524impl<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
1554impl<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
1584impl<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
1618impl<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
1634impl<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
1651impl<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
1681impl<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
1697impl<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#[cfg(any(test, feature = "testing"))]
1716impl<F: CircuitField> FromScratch<F> for NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>> {
1717 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 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}