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 fn from_public_input(fields: &[F]) -> Option<u8> {
112 let [f] = fields else { return None };
113 u8::try_from(f.to_biguint()).ok()
114 }
115}
116
117#[derive(Clone, Debug)]
122#[must_use]
123pub struct AssignedByte<F: CircuitField>(AssignedNative<F>);
124
125impl<F: CircuitField> InnerValue for AssignedByte<F> {
126 type Element = u8;
127
128 fn value(&self) -> Value<u8> {
129 self.0.value().map(|v| {
130 let bi_v = v.to_biguint();
131 #[cfg(not(test))]
132 assert!(bi_v <= BigUint::from(255u8));
133 bi_v.to_bytes_le().first().copied().unwrap_or(0u8)
134 })
135 }
136}
137
138impl<F: CircuitField> From<AssignedByte<F>> for AssignedNative<F> {
139 fn from(value: AssignedByte<F>) -> Self {
140 value.0
141 }
142}
143
144impl<F: CircuitField> From<&AssignedByte<F>> for AssignedNative<F> {
145 fn from(value: &AssignedByte<F>) -> Self {
146 value.clone().0
147 }
148}
149
150impl<F: CircuitField> From<AssignedBit<F>> for AssignedByte<F> {
151 fn from(value: AssignedBit<F>) -> Self {
152 AssignedByte(value.0)
153 }
154}
155
156#[cfg(any(test, feature = "testing"))]
157impl<F: CircuitField> Sampleable for AssignedByte<F> {
158 fn sample_inner(mut rng: impl RngCore) -> Self::Element {
159 rng.r#gen()
160 }
161}
162
163#[derive(Clone, Debug)]
165pub struct BoundedElement<F: CircuitField> {
166 value: F,
167 bound: u32,
168}
169
170impl<F: CircuitField> BoundedElement<F> {
171 pub fn new(value: F, bound: u32) -> Self {
173 #[cfg(not(test))]
174 {
175 use num_traits::One;
176
177 let v_as_bint = value.to_biguint();
178 let bound_as_bint = BigUint::one() << bound;
179 assert!(
180 v_as_bint < bound_as_bint,
181 "Trying to convert {:?} to an AssignedBounded less than 2^{:?}!",
182 value,
183 bound
184 );
185 }
186 BoundedElement { value, bound }
187 }
188
189 pub fn field_value(&self) -> F {
191 self.value
192 }
193
194 pub fn bound(&self) -> u32 {
196 self.bound
197 }
198}
199
200#[derive(Clone, Debug)]
205pub struct AssignedBounded<F: CircuitField> {
206 value: AssignedNative<F>,
207 bound: u32,
208}
209
210impl<F: CircuitField> AssignedBounded<F> {
211 pub(crate) fn to_assigned_bounded_unsafe(x: &AssignedNative<F>, bound: u32) -> Self {
219 let _new = x.value().map(|&x| BoundedElement::new(x, bound));
221 AssignedBounded {
222 value: x.clone(),
223 bound,
224 }
225 }
226
227 pub fn bound(&self) -> u32 {
229 self.bound
230 }
231}
232
233impl<F: CircuitField> InnerValue for AssignedBounded<F> {
234 type Element = BoundedElement<F>;
235
236 fn value(&self) -> Value<BoundedElement<F>> {
237 let (assigned_value, bound) = (self.value.clone(), self.bound());
238 assigned_value.value().map(|&value| BoundedElement { value, bound })
239 }
240}
241
242impl<F, CoreDecomposition, NativeArith> RangeCheckInstructions<F, AssignedNative<F>>
243 for NativeGadget<F, CoreDecomposition, NativeArith>
244where
245 F: CircuitField,
246 CoreDecomposition: CoreDecompositionInstructions<F>,
247 NativeArith: ArithInstructions<F, AssignedNative<F>>
248 + AssignmentInstructions<F, AssignedBit<F>>
249 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
250 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
251 + BinaryInstructions<F>
252 + EqualityInstructions<F, AssignedNative<F>>
253 + ControlFlowInstructions<F, AssignedNative<F>>,
254{
255 fn assign_lower_than_fixed(
256 &self,
257 layouter: &mut impl Layouter<F>,
258 value: Value<F>,
259 bound: &BigUint,
260 ) -> Result<AssignedNative<F>, Error> {
261 if bound.is_zero() {
262 return self.assign_fixed(layouter, F::ZERO);
263 }
264
265 let k = (bound.bits() - 1) as usize;
267 if *bound == BigUint::from(1u8) << k {
268 return self.core_decomposition_chip.assign_less_than_pow2(layouter, value, k);
269 }
270 let x = self.assign(layouter, value)?;
271 self.assert_lower_than_fixed(layouter, &x, bound)?;
272 Ok(x)
273 }
274
275 fn assert_lower_than_fixed(
276 &self,
277 layouter: &mut impl Layouter<F>,
278 x: &AssignedNative<F>,
279 bound: &BigUint,
280 ) -> Result<(), Error> {
281 if let Some(current_bound) = self.constrained_cells.borrow().get(x) {
282 if current_bound <= bound {
283 return Ok(());
284 }
285 }
286 self.update_bound(x, bound.clone());
287
288 let k = (bound.bits() - 1) as usize;
290 let two_pow_k = BigUint::from(1u8) << k; if two_pow_k == *bound {
294 return self.core_decomposition_chip.assert_less_than_pow2(layouter, x, k);
295 }
296
297 let b_value = x.value().map(|x| x.to_biguint() < two_pow_k);
299 let b: AssignedBit<F> = self.assign(layouter, b_value)?;
300
301 let diff: F = big_to_fe(bound - two_pow_k);
302
303 let shifted_x = self.add_constant(layouter, x, -diff)?;
305 let y = self.select(layouter, &b, x, &shifted_x)?;
306 self.core_decomposition_chip.assert_less_than_pow2(layouter, &y, k)
307 }
308}
309
310impl<F, CoreDecomposition, NativeArith> ComparisonInstructions<F, AssignedNative<F>>
311 for NativeGadget<F, CoreDecomposition, NativeArith>
312where
313 F: CircuitField,
314 CoreDecomposition: CoreDecompositionInstructions<F>,
315 NativeArith: ArithInstructions<F, AssignedNative<F>>
316 + AssignmentInstructions<F, AssignedBit<F>>
317 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
318 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
319 + BinaryInstructions<F>
320 + EqualityInstructions<F, AssignedNative<F>>
321 + ControlFlowInstructions<F, AssignedNative<F>>,
322{
323 const MAX_BOUND_IN_BITS: u32 = F::NUM_BITS - 2;
331
332 fn bounded_of_element(
333 &self,
334 layouter: &mut impl Layouter<F>,
335 n: usize,
336 x: &AssignedNative<F>,
337 ) -> Result<AssignedBounded<F>, Error> {
338 #[cfg(not(test))]
339 assert!(
340 n <= Self::MAX_BOUND_IN_BITS as usize,
341 "Cannot bound an element with a bound {} > {} = MAX_BOUND",
342 n,
343 Self::MAX_BOUND_IN_BITS,
344 );
345
346 self.assert_lower_than_fixed(layouter, x, &(BigUint::from(1u32) << n))?;
347 Ok(AssignedBounded::to_assigned_bounded_unsafe(x, n as u32))
348 }
349
350 fn element_of_bounded(
351 &self,
352 _layouter: &mut impl Layouter<F>,
353 bounded: &AssignedBounded<F>,
354 ) -> Result<AssignedNative<F>, Error> {
355 Ok(bounded.value.clone())
356 }
357
358 fn lower_than_fixed(
361 &self,
362 layouter: &mut impl Layouter<F>,
363 x: &AssignedBounded<F>,
364 y: F,
365 ) -> Result<AssignedBit<F>, Error> {
366 if let Some(current_bound) = self.constrained_cells.borrow().get(&x.value) {
367 if *current_bound <= y.to_biguint() {
368 return self.assign_fixed(layouter, true);
369 }
370 }
371
372 let x_as_bint = x.value.value().map(|x| x.to_biguint());
373 let y_as_bint = y.to_biguint();
374
375 #[cfg(not(test))]
377 assert!(y_as_bint < BigUint::from(1u8) << Self::MAX_BOUND_IN_BITS);
378
379 if y_as_bint >= (BigUint::from(1u8) << x.bound()) {
382 return self.assign_fixed(layouter, true);
383 }
384
385 let result_bit = x_as_bint.map(|x_as_bint| x_as_bint < y_as_bint);
396 let assigned_result = self.assign(layouter, result_bit)?;
397
398 let b_el: AssignedNative<F> = self.convert(layouter, &assigned_result)?;
402 let x_el = self.element_of_bounded(layouter, x)?;
403 let bx = self.mul(layouter, &x_el, &b_el, None)?;
404 let terms = vec![
405 (F::from(2) * y - F::ONE, b_el),
406 (F::ONE, x_el),
407 (-F::from(2), bx),
408 ];
409 let z = self.linear_combination(layouter, terms.as_slice(), -y)?;
410
411 self.core_decomposition_chip
412 .assert_less_than_pow2(layouter, &z, x.bound() as usize)?;
413 Ok(assigned_result)
414 }
415
416 fn lower_than(
417 &self,
418 layouter: &mut impl Layouter<F>,
419 x: &AssignedBounded<F>,
420 y: &AssignedBounded<F>,
421 ) -> Result<AssignedBit<F>, Error> {
422 let x_as_bint = x.value.value().map(|x| x.to_biguint());
423 let y_as_bint = y.value.value().map(|x| x.to_biguint());
424
425 let result_bit =
436 x_as_bint.zip(y_as_bint).map(|(x_as_bint, y_as_bint)| x_as_bint < y_as_bint);
437 let assigned_result = self.assign(layouter, result_bit)?;
438
439 let b_el: AssignedNative<F> = self.convert(layouter, &assigned_result)?;
441 let x_el = self.element_of_bounded(layouter, x)?;
442 let y_el = self.element_of_bounded(layouter, y)?;
443
444 let bx = self.mul(layouter, &x_el, &b_el, None)?;
445 let by = self.mul(layouter, &y_el, &b_el, None)?;
446 let terms = vec![
447 (F::from(2), by),
448 (-F::ONE, b_el),
449 (F::ONE, x_el),
450 (-F::from(2), bx),
451 (-F::ONE, y_el),
452 ];
453 let z = self.linear_combination(layouter, terms.as_slice(), F::ZERO)?;
454
455 let max_bound = x.bound().max(y.bound());
456 self.core_decomposition_chip
457 .assert_less_than_pow2(layouter, &z, max_bound as usize)?;
458 Ok(assigned_result)
459 }
460
461 fn leq(
462 &self,
463 layouter: &mut impl Layouter<F>,
464 x: &AssignedBounded<F>,
465 y: &AssignedBounded<F>,
466 ) -> Result<AssignedBit<F>, Error> {
467 let b1 = self.lower_than(layouter, x, y)?;
470 let x_el = self.element_of_bounded(layouter, x)?;
471 let y_el = self.element_of_bounded(layouter, y)?;
472 let b2 = self.is_equal(layouter, &x_el, &y_el)?;
473 self.or(layouter, &[b1, b2])
474 }
475
476 fn geq(
479 &self,
480 layouter: &mut impl Layouter<F>,
481 x: &AssignedBounded<F>,
482 y: &AssignedBounded<F>,
483 ) -> Result<AssignedBit<F>, Error> {
484 let b = self.lower_than(layouter, x, y)?;
485 self.not(layouter, &b)
486 }
487}
488
489impl<F, CoreDecomposition, NativeArith> DivisionInstructions<F, AssignedNative<F>>
490 for NativeGadget<F, CoreDecomposition, NativeArith>
491where
492 F: CircuitField,
493 CoreDecomposition: CoreDecompositionInstructions<F>,
494 NativeArith: ArithInstructions<F, AssignedNative<F>>
495 + AssignmentInstructions<F, AssignedBit<F>>
496 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
497 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
498 + BinaryInstructions<F>
499 + EqualityInstructions<F, AssignedNative<F>>
500 + ControlFlowInstructions<F, AssignedNative<F>>,
501{
502}
503
504impl<F, CoreDecomposition, NativeArith> PublicInputInstructions<F, AssignedByte<F>>
506 for NativeGadget<F, CoreDecomposition, NativeArith>
507where
508 F: CircuitField,
509 CoreDecomposition: CoreDecompositionInstructions<F>,
510 NativeArith:
511 PublicInputInstructions<F, AssignedNative<F>> + ArithInstructions<F, AssignedNative<F>>,
512{
513 fn as_public_input(
514 &self,
515 _layouter: &mut impl Layouter<F>,
516 assigned: &AssignedByte<F>,
517 ) -> Result<Vec<AssignedNative<F>>, Error> {
518 Ok(vec![assigned.clone().into()])
519 }
520
521 fn constrain_as_public_input(
522 &self,
523 layouter: &mut impl Layouter<F>,
524 assigned: &AssignedByte<F>,
525 ) -> Result<(), Error> {
526 let assigned_as_native: AssignedNative<F> = assigned.clone().into();
527 self.constrain_as_public_input(layouter, &assigned_as_native)
528 }
529
530 fn assign_as_public_input(
531 &self,
532 layouter: &mut impl Layouter<F>,
533 value: Value<u8>,
534 ) -> Result<AssignedByte<F>, Error> {
535 let assigned_native = self
538 .native_chip
539 .assign_as_public_input(layouter, value.map(|byte| F::from(byte as u64)))?;
540 self.convert_unsafe(layouter, &assigned_native)
541 }
542}
543
544impl<F, CD, NA, Assigned> CommittedInstanceInstructions<F, Assigned> for NativeGadget<F, CD, NA>
545where
546 F: CircuitField,
547 CD: CoreDecompositionInstructions<F>,
548
549 NA: CommittedInstanceInstructions<F, AssignedNative<F>>
550 + ArithInstructions<F, AssignedNative<F>>,
551 Assigned: Instantiable<F> + Into<AssignedNative<F>>,
552{
553 fn constrain_as_committed_public_input(
554 &self,
555 layouter: &mut impl Layouter<F>,
556 assigned: &Assigned,
557 ) -> Result<(), Error> {
558 let assigned_as_native = assigned.clone().into();
559 self.native_chip
560 .constrain_as_committed_public_input(layouter, &assigned_as_native)
561 }
562}
563
564impl<F, CoreDecomposition, NativeArith> AssignmentInstructions<F, AssignedByte<F>>
566 for NativeGadget<F, CoreDecomposition, NativeArith>
567where
568 F: CircuitField,
569 CoreDecomposition: CoreDecompositionInstructions<F>,
570 NativeArith: ArithInstructions<F, AssignedNative<F>>,
571{
572 fn assign(
573 &self,
574 layouter: &mut impl Layouter<F>,
575 byte: Value<u8>,
576 ) -> Result<AssignedByte<F>, Error> {
577 let byte_as_f = byte.map(|b| F::from(b as u64));
578 let assigned =
579 self.core_decomposition_chip.assign_less_than_pow2(layouter, byte_as_f, 8)?;
580 Ok(AssignedByte(assigned))
581 }
582
583 fn assign_fixed(
584 &self,
585 layouter: &mut impl Layouter<F>,
586 constant: u8,
587 ) -> Result<AssignedByte<F>, Error> {
588 let assigned = self.assign_fixed(layouter, F::from(constant as u64))?;
589 Ok(AssignedByte(assigned))
590 }
591
592 fn assign_many(
593 &self,
594 layouter: &mut impl Layouter<F>,
595 values: &[Value<u8>],
596 ) -> Result<Vec<AssignedByte<F>>, Error> {
597 let values_as_f: Vec<_> = values.iter().map(|v| v.map(|b| F::from(b as u64))).collect();
598
599 self.core_decomposition_chip
600 .assign_many_small(layouter, &values_as_f, 8)?
601 .iter()
602 .map(|assigned_native| self.convert_unsafe(layouter, assigned_native))
603 .collect()
604 }
605}
606
607impl<F, CoreDecomposition, NativeArith> AssertionInstructions<F, AssignedByte<F>>
609 for NativeGadget<F, CoreDecomposition, NativeArith>
610where
611 F: CircuitField,
612 CoreDecomposition: CoreDecompositionInstructions<F>,
613 NativeArith: ArithInstructions<F, AssignedNative<F>>,
614{
615 fn assert_equal(
616 &self,
617 layouter: &mut impl Layouter<F>,
618 byte1: &AssignedByte<F>,
619 byte2: &AssignedByte<F>,
620 ) -> Result<(), Error> {
621 let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
622 let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
623 self.assert_equal(layouter, &x1, &x2)
624 }
625
626 fn assert_not_equal(
627 &self,
628 layouter: &mut impl Layouter<F>,
629 byte1: &AssignedByte<F>,
630 byte2: &AssignedByte<F>,
631 ) -> Result<(), Error> {
632 let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
633 let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
634 self.assert_not_equal(layouter, &x1, &x2)
635 }
636
637 fn assert_equal_to_fixed(
638 &self,
639 layouter: &mut impl Layouter<F>,
640 byte: &AssignedByte<F>,
641 constant: u8,
642 ) -> Result<(), Error> {
643 let x: AssignedNative<F> = self.convert(layouter, byte)?;
644 self.assert_equal_to_fixed(layouter, &x, F::from(constant as u64))
645 }
646
647 fn assert_not_equal_to_fixed(
648 &self,
649 layouter: &mut impl Layouter<F>,
650 byte: &AssignedByte<F>,
651 constant: u8,
652 ) -> Result<(), Error> {
653 let x: AssignedNative<F> = self.convert(layouter, byte)?;
654 self.assert_not_equal_to_fixed(layouter, &x, F::from(constant as u64))
655 }
656}
657
658impl<F, CoreDecomposition, NativeArith> EqualityInstructions<F, AssignedByte<F>>
660 for NativeGadget<F, CoreDecomposition, NativeArith>
661where
662 F: CircuitField,
663 CoreDecomposition: CoreDecompositionInstructions<F>,
664 NativeArith:
665 ArithInstructions<F, AssignedNative<F>> + EqualityInstructions<F, AssignedNative<F>>,
666{
667 fn is_equal(
668 &self,
669 layouter: &mut impl Layouter<F>,
670 byte1: &AssignedByte<F>,
671 byte2: &AssignedByte<F>,
672 ) -> Result<AssignedBit<F>, Error> {
673 let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
674 let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
675 self.native_chip.is_equal(layouter, &x1, &x2)
676 }
677
678 fn is_not_equal(
679 &self,
680 layouter: &mut impl Layouter<F>,
681 byte1: &AssignedByte<F>,
682 byte2: &AssignedByte<F>,
683 ) -> Result<AssignedBit<F>, Error> {
684 let x1: AssignedNative<F> = self.convert(layouter, byte1)?;
685 let x2: AssignedNative<F> = self.convert(layouter, byte2)?;
686 self.native_chip.is_not_equal(layouter, &x1, &x2)
687 }
688
689 fn is_equal_to_fixed(
690 &self,
691 layouter: &mut impl Layouter<F>,
692 byte: &AssignedByte<F>,
693 constant: u8,
694 ) -> Result<AssignedBit<F>, Error> {
695 let x: AssignedNative<F> = self.convert(layouter, byte)?;
696 self.native_chip.is_equal_to_fixed(layouter, &x, F::from(constant as u64))
697 }
698
699 fn is_not_equal_to_fixed(
700 &self,
701 layouter: &mut impl Layouter<F>,
702 byte: &AssignedByte<F>,
703 constant: u8,
704 ) -> Result<AssignedBit<F>, Error> {
705 let x: AssignedNative<F> = self.convert(layouter, byte)?;
706 self.native_chip.is_not_equal_to_fixed(layouter, &x, F::from(constant as u64))
707 }
708}
709
710impl<F: CircuitField, const N: usize> AssertionInstructions<F, [AssignedByte<F>; N]>
711 for NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>>
712{
713 fn assert_equal(
714 &self,
715 layouter: &mut impl Layouter<F>,
716 x: &[AssignedByte<F>; N],
717 y: &[AssignedByte<F>; N],
718 ) -> Result<(), Error> {
719 x.iter().zip(y.iter()).try_for_each(|(x, y)| self.assert_equal(layouter, x, y))
720 }
721
722 fn assert_not_equal(
723 &self,
724 layouter: &mut impl Layouter<F>,
725 x: &[AssignedByte<F>; N],
726 y: &[AssignedByte<F>; N],
727 ) -> Result<(), Error> {
728 let xi_eq_yi = (x.iter())
731 .zip(y.iter())
732 .map(|(x, y)| self.is_equal(layouter, x, y))
733 .collect::<Result<Vec<_>, Error>>()?;
734 let all_equal = self.and(layouter, &xi_eq_yi)?;
735 self.assert_equal_to_fixed(layouter, &all_equal, false)
736 }
737
738 fn assert_equal_to_fixed(
739 &self,
740 layouter: &mut impl Layouter<F>,
741 x: &[AssignedByte<F>; N],
742 constant: [u8; N],
743 ) -> Result<(), Error> {
744 x.iter()
745 .zip(constant.iter())
746 .try_for_each(|(x, y)| self.assert_equal_to_fixed(layouter, x, *y))
747 }
748
749 fn assert_not_equal_to_fixed(
750 &self,
751 layouter: &mut impl Layouter<F>,
752 x: &[AssignedByte<F>; N],
753 constant: [u8; N],
754 ) -> Result<(), Error> {
755 let xi_eq_ci = (x.iter())
758 .zip(constant.iter())
759 .map(|(x, c)| self.is_equal_to_fixed(layouter, x, *c))
760 .collect::<Result<Vec<_>, Error>>()?;
761 let all_equal = self.and(layouter, &xi_eq_ci)?;
762 self.assert_equal_to_fixed(layouter, &all_equal, false)
763 }
764}
765
766impl<F, CoreDecomposition, NativeArith>
768 ConversionInstructions<F, AssignedNative<F>, AssignedByte<F>>
769 for NativeGadget<F, CoreDecomposition, NativeArith>
770where
771 F: CircuitField,
772 CoreDecomposition: CoreDecompositionInstructions<F>,
773 NativeArith: ArithInstructions<F, AssignedNative<F>>,
774{
775 fn convert_value(&self, x: &F) -> Option<u8> {
776 let b_as_bn = x.to_biguint();
777 #[cfg(not(test))]
778 assert!(
779 b_as_bn <= BigUint::from(255u8),
780 "Trying to convert {:?} to AssignedByte in-circuit",
781 x
782 );
783 b_as_bn.to_bytes_le().first().cloned()
784 }
785
786 fn convert(
787 &self,
788 layouter: &mut impl Layouter<F>,
789 x: &AssignedNative<F>,
790 ) -> Result<AssignedByte<F>, Error> {
791 if let Some(current_bound) = self.constrained_cells.borrow().get(x) {
792 if *current_bound <= BigUint::from(256u32) {
793 return self.convert_unsafe(layouter, x);
794 }
795 }
796 self.update_bound(x, BigUint::from(256u32));
797 let b_value = x.value().map(|x| {
798 <Self as ConversionInstructions<_, _, AssignedByte<F>>>::convert_value(self, x)
799 .unwrap_or(0u8)
800 });
801 let b: AssignedByte<F> = self.assign(layouter, b_value)?;
802 self.assert_equal(layouter, x, &b.0)?;
803 Ok(b)
804 }
805}
806
807impl<F, CoreDecomposition, NativeArith>
809 ConversionInstructions<F, AssignedByte<F>, AssignedNative<F>>
810 for NativeGadget<F, CoreDecomposition, NativeArith>
811where
812 F: CircuitField,
813 CoreDecomposition: CoreDecompositionInstructions<F>,
814 NativeArith: ArithInstructions<F, AssignedNative<F>>,
815{
816 fn convert_value(&self, x: &u8) -> Option<F> {
817 Some(F::from(*x as u64))
818 }
819
820 fn convert(
821 &self,
822 _layouter: &mut impl Layouter<F>,
823 byte: &AssignedByte<F>,
824 ) -> Result<AssignedNative<F>, Error> {
825 self.update_bound(&byte.0, BigUint::from(256u32));
826 Ok(byte.0.clone())
827 }
828}
829
830impl<F, CoreDecomposition, NativeArith>
832 UnsafeConversionInstructions<F, AssignedNative<F>, AssignedByte<F>>
833 for NativeGadget<F, CoreDecomposition, NativeArith>
834where
835 F: CircuitField,
836 CoreDecomposition: CoreDecompositionInstructions<F>,
837 NativeArith: ArithInstructions<F, AssignedNative<F>>,
838{
839 fn convert_unsafe(
848 &self,
849 _layouter: &mut impl Layouter<F>,
850 x: &AssignedNative<F>,
851 ) -> Result<AssignedByte<F>, Error> {
852 #[cfg(not(test))]
853 x.value().map(|&x| {
854 let x = x.to_biguint();
855 assert!(
856 x <= BigUint::from(255u8),
857 "Trying to convert {:?} to an AssignedByte!",
858 x
859 );
860 });
861 Ok(AssignedByte(x.clone()))
862 }
863}
864
865impl<F, CoreDecomposition, NativeArith> DecompositionInstructions<F, AssignedNative<F>>
867 for NativeGadget<F, CoreDecomposition, NativeArith>
868where
869 F: CircuitField,
870 CoreDecomposition: CoreDecompositionInstructions<F>,
871 NativeArith: ArithInstructions<F, AssignedNative<F>>
872 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
873 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
874 + AssignmentInstructions<F, AssignedBit<F>>
875 + BinaryInstructions<F>
876 + EqualityInstructions<F, AssignedNative<F>>
877 + ControlFlowInstructions<F, AssignedNative<F>>,
878 NativeGadget<F, CoreDecomposition, NativeArith>: CanonicityInstructions<F, AssignedNative<F>>
879 + AssertionInstructions<F, AssignedBit<F>>
880 + AssignmentInstructions<F, AssignedBit<F>>
881 + BinaryInstructions<F>
882 + EqualityInstructions<F, AssignedNative<F>>
883 + ControlFlowInstructions<F, AssignedNative<F>>,
884{
885 fn assigned_to_le_bits(
886 &self,
887 layouter: &mut impl Layouter<F>,
888 x: &AssignedNative<F>,
889 nb_bits: Option<usize>,
890 enforce_canonical: bool,
891 ) -> Result<Vec<AssignedBit<F>>, Error> {
892 let nb_bits = nb_bits.unwrap_or(F::NUM_BITS as usize);
893
894 assert!(
895 nb_bits <= F::NUM_BITS as usize,
896 "assigned_to_le_bits: why do you need the output to have more bits than necessary?"
897 );
898
899 let limbs = self
900 .core_decomposition_chip
901 .decompose_fixed_limb_size(layouter, x, nb_bits, 1)?;
902 let bits = limbs
903 .iter()
904 .map(|x| self.native_chip.convert_unsafe(layouter, x))
905 .collect::<Result<Vec<_>, Error>>()?;
906 if enforce_canonical && nb_bits == F::NUM_BITS as usize {
907 debug_assert_eq!(F::modulus().bits(), F::NUM_BITS as u64);
908 let b0 = self.sgn0(layouter, x)?;
916 self.assert_equal(layouter, &bits[0], &b0)?;
917 }
918 Ok(bits)
919 }
920
921 fn assigned_to_le_bytes(
922 &self,
923 layouter: &mut impl Layouter<F>,
924 x: &AssignedNative<F>,
925 nb_bytes: Option<usize>,
926 ) -> Result<Vec<AssignedByte<F>>, Error> {
927 let f_num_bytes = F::NUM_BITS.div_ceil(8);
928 let nb_bytes = nb_bytes.unwrap_or(f_num_bytes as usize);
929 if nb_bytes > f_num_bytes as usize {
930 panic!("assigned_to_le_bytes: why do you need the output to have more bytes than necessary?");
931 }
932 if nb_bytes == f_num_bytes as usize {
936 let bits = self.assigned_to_le_bits(layouter, x, Some(F::NUM_BITS as usize), true)?;
937 bits.chunks(8)
938 .map(|chunk| {
939 let terms = chunk
940 .iter()
941 .enumerate()
942 .map(|(i, bit)| (F::from(1 << i), bit.clone().into()))
943 .collect::<Vec<_>>();
944 let byte = self.linear_combination(layouter, &terms, F::ZERO)?;
945 self.convert_unsafe(layouter, &byte)
946 })
947 .collect::<Result<Vec<AssignedByte<F>>, Error>>()
948 }
949 else {
952 let limbs = self.core_decomposition_chip.decompose_fixed_limb_size(
953 layouter,
954 x,
955 8 * nb_bytes,
956 8,
957 )?;
958 limbs
959 .iter()
960 .map(|x| self.convert_unsafe(layouter, x))
961 .collect::<Result<Vec<_>, Error>>()
962 }
963 }
964
965 fn assigned_to_le_chunks(
966 &self,
967 layouter: &mut impl Layouter<F>,
968 x: &AssignedNative<F>,
969 nb_bits_per_chunk: usize,
970 nb_chunks: Option<usize>,
971 ) -> Result<Vec<AssignedNative<F>>, Error> {
972 assert!(nb_bits_per_chunk < F::NUM_BITS as usize);
973 let nb_chunks = nb_chunks.unwrap_or((F::NUM_BITS as usize).div_ceil(nb_bits_per_chunk));
974 self.core_decomposition_chip.decompose_fixed_limb_size(
975 layouter,
976 x,
977 nb_bits_per_chunk * nb_chunks,
978 nb_bits_per_chunk,
979 )
980 }
981
982 fn sgn0(
983 &self,
984 layouter: &mut impl Layouter<F>,
985 x: &AssignedNative<F>,
986 ) -> Result<AssignedBit<F>, Error> {
987 let x_val = x.value().copied().map(|v| v.to_biguint());
991 let w_val = x_val.clone().map(|x| &x / BigUint::from(2u8));
992 let e_val = x_val.clone().map(|x| x.bit(0));
993
994 let e: AssignedBit<F> = self.assign(layouter, e_val)?;
995 let w = self.assign_lower_than_fixed(
996 layouter,
997 w_val.map(big_to_fe::<F>),
998 &(&(F::modulus() + BigUint::from(1u8)) / BigUint::from(2u8)),
999 )?;
1000 let must_be_x = self.linear_combination(
1001 layouter,
1002 &[(F::ONE, e.clone().into()), (F::from(2), w.clone())],
1003 F::ZERO,
1004 )?;
1005 self.assert_equal(layouter, x, &must_be_x)?;
1006 let x_is_zero: AssignedBit<F> = self.is_zero(layouter, x)?;
1009 let x_is_not_zero: AssignedBit<F> = self.not(layouter, &x_is_zero)?;
1010 let sgn0 = self.and(layouter, &[x_is_not_zero, e])?;
1011
1012 Ok(sgn0)
1013 }
1014}
1015
1016impl<F, CoreDecomposition, NativeArith> PublicInputInstructions<F, AssignedNative<F>>
1018 for NativeGadget<F, CoreDecomposition, NativeArith>
1019where
1020 F: CircuitField,
1021 CoreDecomposition: CoreDecompositionInstructions<F>,
1022 NativeArith:
1023 PublicInputInstructions<F, AssignedNative<F>> + ArithInstructions<F, AssignedNative<F>>,
1024{
1025 fn as_public_input(
1026 &self,
1027 layouter: &mut impl Layouter<F>,
1028 assigned: &AssignedNative<F>,
1029 ) -> Result<Vec<AssignedNative<F>>, Error> {
1030 self.native_chip.as_public_input(layouter, assigned)
1031 }
1032
1033 fn constrain_as_public_input(
1034 &self,
1035 layouter: &mut impl Layouter<F>,
1036 assigned: &AssignedNative<F>,
1037 ) -> Result<(), Error> {
1038 self.native_chip.constrain_as_public_input(layouter, assigned)
1039 }
1040
1041 fn assign_as_public_input(
1042 &self,
1043 layouter: &mut impl Layouter<F>,
1044 value: Value<F>,
1045 ) -> Result<AssignedNative<F>, Error> {
1046 self.native_chip.assign_as_public_input(layouter, value)
1047 }
1048}
1049
1050impl<F, CoreDecomposition, NativeArith> AssignmentInstructions<F, AssignedNative<F>>
1052 for NativeGadget<F, CoreDecomposition, NativeArith>
1053where
1054 F: CircuitField,
1055 CoreDecomposition: CoreDecompositionInstructions<F>,
1056 NativeArith: ArithInstructions<F, AssignedNative<F>>,
1057{
1058 fn assign(
1059 &self,
1060 layouter: &mut impl Layouter<F>,
1061 value: Value<F>,
1062 ) -> Result<AssignedNative<F>, Error> {
1063 self.native_chip.assign(layouter, value)
1064 }
1065
1066 fn assign_fixed(
1067 &self,
1068 layouter: &mut impl Layouter<F>,
1069 constant: F,
1070 ) -> Result<AssignedNative<F>, Error> {
1071 self.native_chip.assign_fixed(layouter, constant)
1072 }
1073
1074 fn assign_many(
1075 &self,
1076 layouter: &mut impl Layouter<F>,
1077 value: &[Value<F>],
1078 ) -> Result<Vec<AssignedNative<F>>, Error> {
1079 self.native_chip.assign_many(layouter, value)
1080 }
1081}
1082
1083impl<F, CoreDecomposition, NativeArith> PublicInputInstructions<F, AssignedBit<F>>
1085 for NativeGadget<F, CoreDecomposition, NativeArith>
1086where
1087 F: CircuitField,
1088 CoreDecomposition: CoreDecompositionInstructions<F>,
1089 NativeArith:
1090 PublicInputInstructions<F, AssignedBit<F>> + ArithInstructions<F, AssignedNative<F>>,
1091{
1092 fn as_public_input(
1093 &self,
1094 layouter: &mut impl Layouter<F>,
1095 assigned: &AssignedBit<F>,
1096 ) -> Result<Vec<AssignedNative<F>>, Error> {
1097 self.native_chip.as_public_input(layouter, assigned)
1098 }
1099
1100 fn constrain_as_public_input(
1101 &self,
1102 layouter: &mut impl Layouter<F>,
1103 assigned: &AssignedBit<F>,
1104 ) -> Result<(), Error> {
1105 self.native_chip.constrain_as_public_input(layouter, assigned)
1106 }
1107
1108 fn assign_as_public_input(
1109 &self,
1110 layouter: &mut impl Layouter<F>,
1111 value: Value<bool>,
1112 ) -> Result<AssignedBit<F>, Error> {
1113 self.native_chip.assign_as_public_input(layouter, value)
1114 }
1115}
1116
1117impl<F, CoreDecomposition, NativeArith> AssignmentInstructions<F, AssignedBit<F>>
1119 for NativeGadget<F, CoreDecomposition, NativeArith>
1120where
1121 F: CircuitField,
1122 CoreDecomposition: CoreDecompositionInstructions<F>,
1123 NativeArith: ArithInstructions<F, AssignedNative<F>>
1124 + AssignmentInstructions<F, AssignedBit<F>>
1125 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>,
1126{
1127 fn assign(
1128 &self,
1129 layouter: &mut impl Layouter<F>,
1130 value: Value<bool>,
1131 ) -> Result<AssignedBit<F>, Error> {
1132 self.native_chip.assign(layouter, value)
1133 }
1134
1135 fn assign_fixed(
1136 &self,
1137 layouter: &mut impl Layouter<F>,
1138 constant: bool,
1139 ) -> Result<AssignedBit<F>, Error> {
1140 self.native_chip.assign_fixed(layouter, constant)
1141 }
1142
1143 fn assign_many(
1144 &self,
1145 layouter: &mut impl Layouter<F>,
1146 values: &[Value<bool>],
1147 ) -> Result<Vec<AssignedBit<F>>, Error> {
1148 let values_as_f: Vec<_> = values.iter().map(|v| v.map(|b| F::from(b as u64))).collect();
1149
1150 self.core_decomposition_chip
1151 .assign_many_small(layouter, &values_as_f, 1)?
1152 .iter()
1153 .map(|assigned_native| self.native_chip.convert_unsafe(layouter, assigned_native))
1154 .collect()
1155 }
1156}
1157
1158impl<F, CoreDecomposition, NativeArith> AssertionInstructions<F, AssignedNative<F>>
1160 for NativeGadget<F, CoreDecomposition, NativeArith>
1161where
1162 F: CircuitField,
1163 CoreDecomposition: CoreDecompositionInstructions<F>,
1164 NativeArith: ArithInstructions<F, AssignedNative<F>>,
1165{
1166 fn assert_equal(
1167 &self,
1168 layouter: &mut impl Layouter<F>,
1169 x: &AssignedNative<F>,
1170 y: &AssignedNative<F>,
1171 ) -> Result<(), Error> {
1172 let x_bound_opt = self.constrained_cells.borrow().get(x).cloned();
1173 if let Some(x_bound) = x_bound_opt {
1174 self.update_bound(y, x_bound.clone());
1175 }
1176
1177 let y_bound_opt = self.constrained_cells.borrow().get(y).cloned();
1178 if let Some(y_bound) = y_bound_opt {
1179 self.update_bound(x, y_bound.clone());
1180 }
1181
1182 self.native_chip.assert_equal(layouter, x, y)
1183 }
1184
1185 fn assert_not_equal(
1186 &self,
1187 layouter: &mut impl Layouter<F>,
1188 x: &AssignedNative<F>,
1189 y: &AssignedNative<F>,
1190 ) -> Result<(), Error> {
1191 self.native_chip.assert_not_equal(layouter, x, y)
1192 }
1193
1194 fn assert_equal_to_fixed(
1195 &self,
1196 layouter: &mut impl Layouter<F>,
1197 x: &AssignedNative<F>,
1198 constant: F,
1199 ) -> Result<(), Error> {
1200 self.native_chip.assert_equal_to_fixed(layouter, x, constant)
1201 }
1202
1203 fn assert_not_equal_to_fixed(
1204 &self,
1205 layouter: &mut impl Layouter<F>,
1206 x: &AssignedNative<F>,
1207 constant: F,
1208 ) -> Result<(), Error> {
1209 self.native_chip.assert_not_equal_to_fixed(layouter, x, constant)
1210 }
1211}
1212
1213impl<F, CoreDecomposition, NativeArith> AssertionInstructions<F, AssignedBit<F>>
1215 for NativeGadget<F, CoreDecomposition, NativeArith>
1216where
1217 F: CircuitField,
1218 CoreDecomposition: CoreDecompositionInstructions<F>,
1219 NativeArith: ArithInstructions<F, AssignedNative<F>> + AssertionInstructions<F, AssignedBit<F>>,
1220{
1221 fn assert_equal(
1222 &self,
1223 layouter: &mut impl Layouter<F>,
1224 x: &AssignedBit<F>,
1225 y: &AssignedBit<F>,
1226 ) -> Result<(), Error> {
1227 self.native_chip.assert_equal(layouter, x, y)
1228 }
1229
1230 fn assert_not_equal(
1231 &self,
1232 layouter: &mut impl Layouter<F>,
1233 x: &AssignedBit<F>,
1234 y: &AssignedBit<F>,
1235 ) -> Result<(), Error> {
1236 self.native_chip.assert_not_equal(layouter, x, y)
1237 }
1238
1239 fn assert_equal_to_fixed(
1240 &self,
1241 layouter: &mut impl Layouter<F>,
1242 x: &AssignedBit<F>,
1243 constant: bool,
1244 ) -> Result<(), Error> {
1245 self.native_chip.assert_equal_to_fixed(layouter, x, constant)
1246 }
1247
1248 fn assert_not_equal_to_fixed(
1249 &self,
1250 layouter: &mut impl Layouter<F>,
1251 x: &AssignedBit<F>,
1252 constant: bool,
1253 ) -> Result<(), Error> {
1254 self.native_chip.assert_not_equal_to_fixed(layouter, x, constant)
1255 }
1256}
1257
1258impl<F, CoreDecomposition, NativeArith> ArithInstructions<F, AssignedNative<F>>
1260 for NativeGadget<F, CoreDecomposition, NativeArith>
1261where
1262 F: CircuitField,
1263 CoreDecomposition: CoreDecompositionInstructions<F>,
1264 NativeArith: ArithInstructions<F, AssignedNative<F>>,
1265{
1266 fn linear_combination(
1267 &self,
1268 layouter: &mut impl Layouter<F>,
1269 terms: &[(F, AssignedNative<F>)],
1270 constant: F,
1271 ) -> Result<AssignedNative<F>, Error> {
1272 self.native_chip.linear_combination(layouter, terms, constant)
1273 }
1274
1275 fn mul(
1276 &self,
1277 layouter: &mut impl Layouter<F>,
1278 x: &AssignedNative<F>,
1279 y: &AssignedNative<F>,
1280 multiplying_constant: Option<F>,
1281 ) -> Result<AssignedNative<F>, Error> {
1282 self.native_chip.mul(layouter, x, y, multiplying_constant)
1283 }
1284
1285 fn div(
1286 &self,
1287 layouter: &mut impl Layouter<F>,
1288 x: &AssignedNative<F>,
1289 y: &AssignedNative<F>,
1290 ) -> Result<AssignedNative<F>, Error> {
1291 self.native_chip.div(layouter, x, y)
1292 }
1293
1294 fn inv(
1295 &self,
1296 layouter: &mut impl Layouter<F>,
1297 x: &AssignedNative<F>,
1298 ) -> Result<AssignedNative<F>, Error> {
1299 self.native_chip.inv(layouter, x)
1300 }
1301
1302 fn inv0(
1303 &self,
1304 layouter: &mut impl Layouter<F>,
1305 x: &AssignedNative<F>,
1306 ) -> Result<AssignedNative<F>, Error> {
1307 self.native_chip.inv0(layouter, x)
1308 }
1309
1310 fn add_and_mul(
1311 &self,
1312 layouter: &mut impl Layouter<F>,
1313 a_and_x: (F, &AssignedNative<F>),
1314 b_and_y: (F, &AssignedNative<F>),
1315 c_and_z: (F, &AssignedNative<F>),
1316 k: F,
1317 m: F,
1318 ) -> Result<AssignedNative<F>, Error> {
1319 self.native_chip.add_and_mul(layouter, a_and_x, b_and_y, c_and_z, k, m)
1320 }
1321
1322 fn add_constants(
1323 &self,
1324 layouter: &mut impl Layouter<F>,
1325 xs: &[AssignedNative<F>],
1326 constants: &[F],
1327 ) -> Result<Vec<AssignedNative<F>>, Error> {
1328 self.native_chip.add_constants(layouter, xs, constants)
1329 }
1330}
1331
1332impl<F, CoreDecomposition, NativeArith> ConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1334 for NativeGadget<F, CoreDecomposition, NativeArith>
1335where
1336 F: CircuitField,
1337 CoreDecomposition: CoreDecompositionInstructions<F>,
1338 NativeArith: ArithInstructions<F, AssignedNative<F>>
1339 + ConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1340 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>,
1341{
1342 fn convert_value(&self, x: &F) -> Option<bool> {
1343 self.native_chip.convert_value(x)
1344 }
1345
1346 fn convert(
1347 &self,
1348 layouter: &mut impl Layouter<F>,
1349 x: &AssignedNative<F>,
1350 ) -> Result<AssignedBit<F>, Error> {
1351 if let Some(current_bound) = self.constrained_cells.borrow().get(x) {
1352 if *current_bound <= BigUint::from(2u32) {
1353 return self.native_chip.convert_unsafe(layouter, x);
1354 }
1355 }
1356 self.update_bound(x, BigUint::from(2u32));
1357 self.native_chip.convert(layouter, x)
1358 }
1359}
1360
1361impl<F, CoreDecomposition, NativeArith> ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
1363 for NativeGadget<F, CoreDecomposition, NativeArith>
1364where
1365 F: CircuitField,
1366 CoreDecomposition: CoreDecompositionInstructions<F>,
1367 NativeArith: ArithInstructions<F, AssignedNative<F>>
1368 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>,
1369{
1370 fn convert_value(&self, x: &bool) -> Option<F> {
1371 self.native_chip.convert_value(x)
1372 }
1373
1374 fn convert(
1375 &self,
1376 layouter: &mut impl Layouter<F>,
1377 x: &AssignedBit<F>,
1378 ) -> Result<AssignedNative<F>, Error> {
1379 let x = self.native_chip.convert(layouter, x)?;
1380 self.update_bound(&x, BigUint::from(2u32));
1381 Ok(x)
1382 }
1383}
1384
1385impl<F, CoreDecomposition, NativeArith> BinaryInstructions<F>
1387 for NativeGadget<F, CoreDecomposition, NativeArith>
1388where
1389 F: CircuitField,
1390 CoreDecomposition: CoreDecompositionInstructions<F>,
1391 NativeArith: ArithInstructions<F, AssignedNative<F>> + BinaryInstructions<F>,
1392{
1393 fn and(
1394 &self,
1395 layouter: &mut impl Layouter<F>,
1396 bits: &[AssignedBit<F>],
1397 ) -> Result<AssignedBit<F>, Error> {
1398 self.native_chip.and(layouter, bits)
1399 }
1400
1401 fn or(
1402 &self,
1403 layouter: &mut impl Layouter<F>,
1404 bits: &[AssignedBit<F>],
1405 ) -> Result<AssignedBit<F>, Error> {
1406 self.native_chip.or(layouter, bits)
1407 }
1408
1409 fn xor(
1410 &self,
1411 layouter: &mut impl Layouter<F>,
1412 bits: &[AssignedBit<F>],
1413 ) -> Result<AssignedBit<F>, Error> {
1414 self.native_chip.xor(layouter, bits)
1415 }
1416
1417 fn not(
1418 &self,
1419 layouter: &mut impl Layouter<F>,
1420 b: &AssignedBit<F>,
1421 ) -> Result<AssignedBit<F>, Error> {
1422 self.native_chip.not(layouter, b)
1423 }
1424}
1425
1426impl<F, CoreDecomposition, NativeArith> EqualityInstructions<F, AssignedNative<F>>
1428 for NativeGadget<F, CoreDecomposition, NativeArith>
1429where
1430 F: CircuitField,
1431 CoreDecomposition: CoreDecompositionInstructions<F>,
1432 NativeArith:
1433 ArithInstructions<F, AssignedNative<F>> + EqualityInstructions<F, AssignedNative<F>>,
1434{
1435 fn is_equal(
1436 &self,
1437 layouter: &mut impl Layouter<F>,
1438 x: &AssignedNative<F>,
1439 y: &AssignedNative<F>,
1440 ) -> Result<AssignedBit<F>, Error> {
1441 self.native_chip.is_equal(layouter, x, y)
1442 }
1443
1444 fn is_not_equal(
1445 &self,
1446 layouter: &mut impl Layouter<F>,
1447 x: &AssignedNative<F>,
1448 y: &AssignedNative<F>,
1449 ) -> Result<AssignedBit<F>, Error> {
1450 self.native_chip.is_not_equal(layouter, x, y)
1451 }
1452
1453 fn is_equal_to_fixed(
1454 &self,
1455 layouter: &mut impl Layouter<F>,
1456 x: &AssignedNative<F>,
1457 constant: F,
1458 ) -> Result<AssignedBit<F>, Error> {
1459 self.native_chip.is_equal_to_fixed(layouter, x, constant)
1460 }
1461
1462 fn is_not_equal_to_fixed(
1463 &self,
1464 layouter: &mut impl Layouter<F>,
1465 x: &AssignedNative<F>,
1466 constant: F,
1467 ) -> Result<AssignedBit<F>, Error> {
1468 self.native_chip.is_not_equal_to_fixed(layouter, x, constant)
1469 }
1470}
1471
1472impl<F, CoreDecomposition, NativeArith> EqualityInstructions<F, AssignedBit<F>>
1474 for NativeGadget<F, CoreDecomposition, NativeArith>
1475where
1476 F: CircuitField,
1477 CoreDecomposition: CoreDecompositionInstructions<F>,
1478 NativeArith: ArithInstructions<F, AssignedNative<F>> + EqualityInstructions<F, AssignedBit<F>>,
1479{
1480 fn is_equal(
1481 &self,
1482 layouter: &mut impl Layouter<F>,
1483 x: &AssignedBit<F>,
1484 y: &AssignedBit<F>,
1485 ) -> Result<AssignedBit<F>, Error> {
1486 self.native_chip.is_equal(layouter, x, y)
1487 }
1488
1489 fn is_not_equal(
1490 &self,
1491 layouter: &mut impl Layouter<F>,
1492 x: &AssignedBit<F>,
1493 y: &AssignedBit<F>,
1494 ) -> Result<AssignedBit<F>, Error> {
1495 self.native_chip.is_not_equal(layouter, x, y)
1496 }
1497
1498 fn is_equal_to_fixed(
1499 &self,
1500 layouter: &mut impl Layouter<F>,
1501 x: &AssignedBit<F>,
1502 constant: bool,
1503 ) -> Result<AssignedBit<F>, Error> {
1504 self.native_chip.is_equal_to_fixed(layouter, x, constant)
1505 }
1506
1507 fn is_not_equal_to_fixed(
1508 &self,
1509 layouter: &mut impl Layouter<F>,
1510 x: &AssignedBit<F>,
1511 constant: bool,
1512 ) -> Result<AssignedBit<F>, Error> {
1513 self.native_chip.is_not_equal_to_fixed(layouter, x, constant)
1514 }
1515}
1516
1517impl<F, CoreDecomposition, NativeArith> ZeroInstructions<F, AssignedNative<F>>
1519 for NativeGadget<F, CoreDecomposition, NativeArith>
1520where
1521 F: CircuitField,
1522 CoreDecomposition: CoreDecompositionInstructions<F>,
1523 NativeArith: ArithInstructions<F, AssignedNative<F>>
1524 + AssertionInstructions<F, AssignedNative<F>>
1525 + EqualityInstructions<F, AssignedNative<F>>,
1526{
1527}
1528
1529impl<F, CoreDecomposition, NativeArith> ControlFlowInstructions<F, AssignedNative<F>>
1531 for NativeGadget<F, CoreDecomposition, NativeArith>
1532where
1533 F: CircuitField,
1534 CoreDecomposition: CoreDecompositionInstructions<F>,
1535 NativeArith:
1536 ArithInstructions<F, AssignedNative<F>> + ControlFlowInstructions<F, AssignedNative<F>>,
1537{
1538 fn select(
1539 &self,
1540 layouter: &mut impl Layouter<F>,
1541 bit: &AssignedBit<F>,
1542 x: &AssignedNative<F>,
1543 y: &AssignedNative<F>,
1544 ) -> Result<AssignedNative<F>, Error> {
1545 self.native_chip.select(layouter, bit, x, y)
1546 }
1547
1548 fn cond_swap(
1549 &self,
1550 layouter: &mut impl Layouter<F>,
1551 cond: &AssignedBit<F>,
1552 x: &AssignedNative<F>,
1553 y: &AssignedNative<F>,
1554 ) -> Result<(AssignedNative<F>, AssignedNative<F>), Error> {
1555 self.native_chip.cond_swap(layouter, cond, x, y)
1556 }
1557}
1558
1559impl<F, CoreDecomposition, NativeArith> ControlFlowInstructions<F, AssignedBit<F>>
1561 for NativeGadget<F, CoreDecomposition, NativeArith>
1562where
1563 F: CircuitField,
1564 CoreDecomposition: CoreDecompositionInstructions<F>,
1565 NativeArith:
1566 ArithInstructions<F, AssignedNative<F>> + ControlFlowInstructions<F, AssignedBit<F>>,
1567{
1568 fn select(
1569 &self,
1570 layouter: &mut impl Layouter<F>,
1571 bit: &AssignedBit<F>,
1572 x: &AssignedBit<F>,
1573 y: &AssignedBit<F>,
1574 ) -> Result<AssignedBit<F>, Error> {
1575 self.native_chip.select(layouter, bit, x, y)
1576 }
1577
1578 fn cond_swap(
1579 &self,
1580 layouter: &mut impl Layouter<F>,
1581 bit: &AssignedBit<F>,
1582 x: &AssignedBit<F>,
1583 y: &AssignedBit<F>,
1584 ) -> Result<(AssignedBit<F>, AssignedBit<F>), Error> {
1585 self.native_chip.cond_swap(layouter, bit, x, y)
1586 }
1587}
1588
1589impl<F, CoreDecomposition, NativeArith> ControlFlowInstructions<F, AssignedByte<F>>
1591 for NativeGadget<F, CoreDecomposition, NativeArith>
1592where
1593 F: CircuitField,
1594 CoreDecomposition: CoreDecompositionInstructions<F>,
1595 NativeArith:
1596 ArithInstructions<F, AssignedNative<F>> + ControlFlowInstructions<F, AssignedNative<F>>,
1597{
1598 fn select(
1599 &self,
1600 layouter: &mut impl Layouter<F>,
1601 bit: &AssignedBit<F>,
1602 x: &AssignedByte<F>,
1603 y: &AssignedByte<F>,
1604 ) -> Result<AssignedByte<F>, Error> {
1605 let byte = self.native_chip.select(layouter, bit, &x.into(), &y.into())?;
1606 self.convert_unsafe(layouter, &byte)
1607 }
1608
1609 fn cond_swap(
1610 &self,
1611 layouter: &mut impl Layouter<F>,
1612 cond: &AssignedBit<F>,
1613 x: &AssignedByte<F>,
1614 y: &AssignedByte<F>,
1615 ) -> Result<(AssignedByte<F>, AssignedByte<F>), Error> {
1616 let (fst, snd) = (self.native_chip).cond_swap(layouter, cond, &x.into(), &y.into())?;
1617 let fst = self.convert_unsafe(layouter, &fst)?;
1618 let snd = self.convert_unsafe(layouter, &snd)?;
1619 Ok((fst, snd))
1620 }
1621}
1622
1623impl<F, CoreDecomposition, NativeArith> FieldInstructions<F, AssignedNative<F>>
1625 for NativeGadget<F, CoreDecomposition, NativeArith>
1626where
1627 F: CircuitField,
1628 CoreDecomposition: CoreDecompositionInstructions<F>,
1629 NativeArith: FieldInstructions<F, AssignedNative<F>>
1630 + AssertionInstructions<F, AssignedBit<F>>
1631 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1632 + EqualityInstructions<F, AssignedBit<F>>,
1633{
1634 fn order(&self) -> BigUint {
1635 self.native_chip.order()
1636 }
1637}
1638
1639impl<F, CoreDecomposition, NativeArith> ScalarFieldInstructions<F>
1641 for NativeGadget<F, CoreDecomposition, NativeArith>
1642where
1643 F: CircuitField,
1644 CoreDecomposition: CoreDecompositionInstructions<F>,
1645 NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1646 + AssertionInstructions<F, AssignedBit<F>>
1647 + EqualityInstructions<F, AssignedBit<F>>
1648 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1649 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>
1650 + AssignmentInstructions<F, AssignedBit<F>>
1651 + BinaryInstructions<F>,
1652{
1653 type Scalar = AssignedNative<F>;
1654}
1655
1656impl<F, CoreDecomposition, NativeArith> CanonicityInstructions<F, AssignedNative<F>>
1658 for NativeGadget<F, CoreDecomposition, NativeArith>
1659where
1660 F: CircuitField,
1661 CoreDecomposition: CoreDecompositionInstructions<F>,
1662 NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1663 + AssertionInstructions<F, AssignedBit<F>>
1664 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1665 + EqualityInstructions<F, AssignedBit<F>>,
1666{
1667 fn le_bits_lower_than(
1668 &self,
1669 layouter: &mut impl Layouter<F>,
1670 bits: &[AssignedBit<F>],
1671 bound: BigUint,
1672 ) -> Result<AssignedBit<F>, Error> {
1673 self.native_chip.le_bits_lower_than(layouter, bits, bound)
1674 }
1675
1676 fn le_bits_geq_than(
1677 &self,
1678 layouter: &mut impl Layouter<F>,
1679 bits: &[AssignedBit<F>],
1680 bound: BigUint,
1681 ) -> Result<AssignedBit<F>, Error> {
1682 self.native_chip.le_bits_geq_than(layouter, bits, bound)
1683 }
1684}
1685
1686impl<F, CoreDecomposition, NativeArith> NativeInstructions<F>
1688 for NativeGadget<F, CoreDecomposition, NativeArith>
1689where
1690 F: CircuitField,
1691 CoreDecomposition: CoreDecompositionInstructions<F>,
1692 NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1693 + AssertionInstructions<F, AssignedBit<F>>
1694 + EqualityInstructions<F, AssignedBit<F>>
1695 + ControlFlowInstructions<F, AssignedBit<F>>
1696 + BinaryInstructions<F>
1697 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1698 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>,
1699{
1700}
1701
1702impl<F, CoreDecomposition, NativeArith> BitwiseInstructions<F, AssignedNative<F>>
1704 for NativeGadget<F, CoreDecomposition, NativeArith>
1705where
1706 F: CircuitField,
1707 CoreDecomposition: CoreDecompositionInstructions<F>,
1708 NativeArith: CanonicityInstructions<F, AssignedNative<F>>
1709 + AssertionInstructions<F, AssignedBit<F>>
1710 + EqualityInstructions<F, AssignedBit<F>>
1711 + ControlFlowInstructions<F, AssignedBit<F>>
1712 + BinaryInstructions<F>
1713 + UnsafeConversionInstructions<F, AssignedNative<F>, AssignedBit<F>>
1714 + ConversionInstructions<F, AssignedBit<F>, AssignedNative<F>>,
1715{
1716}
1717
1718#[cfg(any(test, feature = "testing"))]
1721impl<F: CircuitField> FromScratch<F> for NativeGadget<F, P2RDecompositionChip<F>, NativeChip<F>> {
1722 type Config = P2RDecompositionConfig;
1725
1726 fn new_from_scratch(config: &Self::Config) -> Self {
1727 let max_bit_len = 8;
1728 let native_chip = NativeChip::new_from_scratch(&config.native_config);
1729 let core_decomposition_chip = P2RDecompositionChip::new(config, &max_bit_len);
1730 NativeGadget::new(core_decomposition_chip, native_chip)
1731 }
1732
1733 fn load_from_scratch(&self, layouter: &mut impl Layouter<F>) -> Result<(), Error> {
1734 self.native_chip.load_from_scratch(layouter)?;
1735 self.core_decomposition_chip.load(layouter)
1736 }
1737
1738 fn configure_from_scratch(
1739 meta: &mut ConstraintSystem<F>,
1740 advice_columns: &mut Vec<Column<Advice>>,
1741 fixed_columns: &mut Vec<Column<Fixed>>,
1742 instance_columns: &[Column<Instance>; 2],
1743 ) -> Self::Config {
1744 while advice_columns.len() < NB_ARITH_COLS {
1745 advice_columns.push(meta.advice_column());
1746 }
1747 while fixed_columns.len() < NB_ARITH_FIXED_COLS {
1748 fixed_columns.push(meta.fixed_column());
1749 }
1750 let advice_cols: [_; NB_ARITH_COLS] = advice_columns[..NB_ARITH_COLS].try_into().unwrap();
1751 let fixed_cols: [_; NB_ARITH_FIXED_COLS] =
1752 fixed_columns[..NB_ARITH_FIXED_COLS].try_into().unwrap();
1753
1754 let native_config =
1755 NativeChip::configure(meta, &(advice_cols, fixed_cols, *instance_columns));
1756 let pow2range_config = Pow2RangeChip::configure(meta, &advice_cols[1..=4]);
1758
1759 P2RDecompositionConfig {
1760 native_config,
1761 pow2range_config,
1762 }
1763 }
1764}
1765
1766#[cfg(test)]
1767mod tests {
1768 use midnight_curves::Fq as BlsScalar;
1769
1770 use super::*;
1771 use crate::instructions::{bitwise, comparison, decomposition, division, range_check};
1772
1773 macro_rules! test {
1774 ($module:ident, $operation:ident) => {
1775 #[test]
1776 fn $operation() {
1777 $module::tests::$operation::<
1778 BlsScalar,
1779 AssignedNative<BlsScalar>,
1780 NativeGadget<BlsScalar, P2RDecompositionChip<BlsScalar>, NativeChip<BlsScalar>>,
1781 NativeGadget<BlsScalar, P2RDecompositionChip<BlsScalar>, NativeChip<BlsScalar>>,
1782 >("native_gadget_bls");
1783 }
1784 };
1785 }
1786
1787 test!(decomposition, test_bit_decomposition);
1788 test!(decomposition, test_byte_decomposition);
1789 test!(decomposition, test_sgn0);
1790
1791 macro_rules! test {
1792 ($module:ident, $operation:ident) => {
1793 #[test]
1794 fn $operation() {
1795 $module::tests::$operation::<
1796 BlsScalar,
1797 AssignedNative<BlsScalar>,
1798 NativeGadget<BlsScalar, P2RDecompositionChip<BlsScalar>, NativeChip<BlsScalar>>,
1799 >("native_gadget_bls");
1800 }
1801 };
1802 }
1803
1804 test!(comparison, test_lower_and_greater);
1805 test!(comparison, test_assert_bounded_element);
1806 test!(range_check, test_assert_lower_than_fixed);
1807 test!(division, test_div_rem);
1808
1809 test!(bitwise, test_band);
1810 test!(bitwise, test_bor);
1811 test!(bitwise, test_bxor);
1812 test!(bitwise, test_bnot);
1813}