1#![allow(clippy::arithmetic_side_effects)] mod generate_trace;
3
4pub use generate_trace::*;
5
6use crypto_primitives::{ConstSemiring, FixedConfig, Semiring, SemiringConfig, boolean::Boolean};
7use num_traits::Zero;
8use rand::{
9 distr::{Distribution, StandardUniform},
10 prelude::*,
11};
12use std::marker::PhantomData;
13use zinc_poly::{
14 EvaluatablePolynomial,
15 mle::{DenseMultilinearExtension, MultilinearExtensionRand},
16 univariate::{
17 binary::BinaryPoly,
18 dense::DensePolynomial,
19 dynamic::{DynamicPolynomial, HasDynamicPolynomialConfig},
20 },
21};
22use zinc_uair::{
23 AffineVirtualSpec, AffineVirtualTerm, BitOp, BitOpSpec, ConstraintBuilder, PublicColumnLayout,
24 ShiftSpec, TotalColumnLayout, TraceRow, Uair, UairSignature, UairTrace,
25 ideal::{DegreeOneIdeal, ImpossibleIdeal},
26};
27use zinc_utils::from_ref::FromRef;
28
29#[derive(Clone, Debug)]
30pub struct TestUairSimpleMultiplication<R, P>(PhantomData<(R, P)>);
31
32impl<R, P> Uair for TestUairSimpleMultiplication<R, P>
33where
34 R: Semiring + 'static,
35 P: Semiring + 'static,
36{
37 type Ideal = ImpossibleIdeal; type FqIdeal = ImpossibleIdeal;
39 type Scalar = DensePolynomial<R, 32>;
40 type Prime = P;
41
42 fn signature() -> UairSignature<Self::Prime> {
43 let total = TotalColumnLayout::new(0, 3, 0);
44 let shifts = (0..3).map(|i| ShiftSpec::new(i, 1)).collect();
45 UairSignature::new(total, PublicColumnLayout::default(), shifts, vec![])
46 }
47
48 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
49 b: &mut B,
50 expr_cfg: &C,
51 up: TraceRow<C::Element>,
52 down: TraceRow<C::Element>,
53 _from_ref: FromR,
54 _mbs: MulByScalar,
55 _ideal_from_ref: IFromR,
56 _fq_ideal_from_ref: IFqFromR,
57 ) where
58 C: SemiringConfig,
59 B: ConstraintBuilder<Expr = C::Element>,
60 {
61 let up = up.arbitrary_poly;
62 let down = down.arbitrary_poly;
63
64 b.assert_zero(expr_cfg.sub(&expr_cfg.mul(&up[0], &up[1]), &down[0]));
65 b.assert_zero(expr_cfg.sub(&expr_cfg.mul(&up[1], &up[2]), &down[1]));
66 b.assert_zero(expr_cfg.sub(&expr_cfg.mul(&up[0], &up[2]), &down[2]));
67 }
68}
69
70impl<R, P> GenerateRandomTrace<32> for TestUairSimpleMultiplication<R, P>
71where
72 R: Semiring + From<i8> + 'static,
73 P: Semiring + 'static,
74 StandardUniform: Distribution<R>,
75{
76 type PolyCoeff = R;
77 type Int = R;
78
79 fn generate_random_trace<G: Rng + ?Sized>(
80 num_vars: usize,
81 rng: &mut G,
82 ) -> UairTrace<'static, R, R, 32, 32> {
83 let ring_cfg = FixedConfig::<R>::default();
84 let poly_cfg = ring_cfg.dyn_poly_cfg();
85
86 let mut a: Vec<DynamicPolynomial<R>> =
87 vec![DynamicPolynomial::new(vec![R::from(rng.random::<i8>())])];
88 let mut b: Vec<DynamicPolynomial<R>> = vec![DynamicPolynomial::new(vec![
89 R::zero(),
90 R::from(rng.random::<i8>()),
91 ])];
92 let mut c: Vec<DynamicPolynomial<R>> = vec![DynamicPolynomial::new(vec![
93 R::zero(),
94 R::from(rng.random::<i8>()),
95 ])];
96
97 for i in 1..1 << num_vars {
98 let prev_a = a[i - 1].clone();
99 let prev_b = b[i - 1].clone();
100 let prev_c = c[i - 1].clone();
101
102 a.push(poly_cfg.mul(&prev_a, &prev_b));
103 b.push(poly_cfg.mul(&prev_b, &prev_c));
104 c.push(poly_cfg.mul(&prev_a, &prev_c));
105 }
106
107 let arbitrary_poly = vec![
108 a.into_iter()
109 .map(|x| {
110 let deg = poly_cfg.degree(&x);
111 assert!(
112 deg < Some(32),
113 "degree bound exceeded: {}",
114 deg.expect("if the degree is large it's not None")
115 );
116 DensePolynomial::new_with_zero(x.coeffs, R::zero())
117 })
118 .collect(),
119 b.into_iter()
120 .map(|x| {
121 let deg = poly_cfg.degree(&x);
122 assert!(
123 deg < Some(32),
124 "degree bound exceeded: {}",
125 deg.expect("if the degree is large it's not None"),
126 );
127 DensePolynomial::new_with_zero(x.coeffs, R::zero())
128 })
129 .collect(),
130 c.into_iter()
131 .map(|x| {
132 let deg = poly_cfg.degree(&x);
133 assert!(
134 deg < Some(32),
135 "degree bound exceeded: {}",
136 deg.expect("if the degree is large it's not None"),
137 );
138 DensePolynomial::new_with_zero(x.coeffs, R::zero())
139 })
140 .collect(),
141 ]
142 .into();
143 UairTrace {
144 arbitrary_poly,
145 ..Default::default()
146 }
147 }
148}
149
150#[derive(Clone, Debug)]
151pub struct TestUairNoMultiplication<R, P>(PhantomData<(R, P)>);
152
153impl<R, P> Uair for TestUairNoMultiplication<R, P>
154where
155 R: ConstSemiring + From<i32> + 'static,
156 P: Semiring + 'static,
157{
158 type Ideal = DegreeOneIdeal<R>;
159 type FqIdeal = ImpossibleIdeal;
160 type Scalar = DensePolynomial<R, 32>;
161 type Prime = P;
162
163 fn signature() -> UairSignature<Self::Prime> {
164 let total = TotalColumnLayout::new(0, 3, 0);
165 UairSignature::new(total, PublicColumnLayout::default(), vec![], vec![])
166 }
167
168 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
169 b: &mut B,
170 expr_cfg: &C,
171 up: TraceRow<C::Element>,
172 _down: TraceRow<C::Element>,
173 _from_ref: FromR,
174 _mbs: MulByScalar,
175 ideal_from_ref: IFromR,
176 _fq_ideal_from_ref: IFqFromR,
177 ) where
178 C: SemiringConfig,
179 B: ConstraintBuilder<Expr = C::Element>,
180 IFromR: Fn(&Self::Ideal) -> B::Ideal,
181 {
182 let up = up.arbitrary_poly;
183
184 b.assert_in_ideal(
185 expr_cfg.sub(&expr_cfg.add(&up[0], &up[1]), &up[2]),
186 &ideal_from_ref(&DegreeOneIdeal::new(R::from(2))),
187 );
188 }
189}
190
191impl<R, P> GenerateRandomTrace<32> for TestUairNoMultiplication<R, P>
192where
193 R: ConstSemiring + From<i32> + 'static,
194 P: Semiring + 'static,
195{
196 type PolyCoeff = R;
197 type Int = R;
198
199 fn generate_random_trace<G: Rng + ?Sized>(
200 num_vars: usize,
201 rng: &mut G,
202 ) -> UairTrace<'static, R, R, 32, 32> {
203 let a: DenseMultilinearExtension<DensePolynomial<R, 32>> =
204 DenseMultilinearExtension::rand(num_vars, rng)
205 .into_iter()
206 .map(|x: u32| {
207 DensePolynomial::from_ref(&DensePolynomial::<Boolean, _>::from(
208 BinaryPoly::<32>::from(x),
209 ))
210 })
211 .collect();
212
213 let b: DenseMultilinearExtension<_> = DenseMultilinearExtension::rand(num_vars, rng)
214 .into_iter()
215 .map(|x: u32| {
216 DensePolynomial::from_ref(&DensePolynomial::<Boolean, _>::from(
217 BinaryPoly::<32>::from(x),
218 ))
219 })
220 .collect();
221
222 let c = a.clone() + b.clone();
223
224 UairTrace {
225 arbitrary_poly: vec![a, b, c].into(),
226 ..Default::default()
227 }
228 }
229}
230
231#[derive(Clone, Debug)]
232pub struct TestUairScalarMultiplications<R, P>(PhantomData<(R, P)>);
233
234impl<R, P> Uair for TestUairScalarMultiplications<R, P>
235where
236 R: ConstSemiring + From<i8> + 'static,
237 P: Semiring + 'static,
238{
239 type Ideal = DegreeOneIdeal<R>;
240 type FqIdeal = ImpossibleIdeal;
241 type Scalar = DensePolynomial<R, 32>;
242 type Prime = P;
243
244 fn signature() -> UairSignature<Self::Prime> {
245 let total = TotalColumnLayout::new(0, 3, 0);
246 UairSignature::new(total, PublicColumnLayout::default(), vec![], vec![])
247 }
248
249 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
250 b: &mut B,
251 expr_cfg: &C,
252 up: TraceRow<C::Element>,
253 _down: TraceRow<C::Element>,
254 from_ref: FromR,
255 mbs: MulByScalar,
256 ideal_from_ref: IFromR,
257 _fq_ideal_from_ref: IFqFromR,
258 ) where
259 C: SemiringConfig,
260 B: ConstraintBuilder<Expr = C::Element>,
261 IFromR: Fn(&Self::Ideal) -> B::Ideal,
262 FromR: Fn(&DensePolynomial<R, 32>) -> C::Element,
263 MulByScalar: Fn(&C::Element, &DensePolynomial<R, 32>) -> Option<C::Element>,
264 {
265 let up = up.arbitrary_poly;
266
267 let scaled = mbs(
268 &up[0],
269 &DensePolynomial::new_with_zero([R::from(-1), R::from(0), R::from(1)], R::zero()),
270 )
271 .expect("arithmetic overflow");
272 let constant = from_ref(&DensePolynomial::new_with_zero(
273 [R::from(1), R::from(2), R::from(3), R::from(4)],
274 R::zero(),
275 ));
276 b.assert_in_ideal(
278 expr_cfg.add(
279 &expr_cfg.sub(&expr_cfg.add(&scaled, &up[1]), &up[2]),
280 &constant,
281 ),
282 &ideal_from_ref(&DegreeOneIdeal::new(R::from(2))),
283 );
284 }
285}
286
287#[derive(Clone, Debug)]
288pub struct BinaryDecompositionUair<R, P>(PhantomData<(R, P)>);
289
290impl<R, P> Uair for BinaryDecompositionUair<R, P>
291where
292 R: ConstSemiring + From<u32> + 'static,
293 P: Semiring + 'static,
294{
295 type Ideal = DegreeOneIdeal<R>;
296 type FqIdeal = ImpossibleIdeal;
297 type Scalar = DensePolynomial<R, 32>;
298 type Prime = P;
299
300 fn signature() -> UairSignature<Self::Prime> {
301 let total = TotalColumnLayout::new(1, 0, 1);
302 UairSignature::new(total, PublicColumnLayout::default(), vec![], vec![])
303 }
304
305 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
306 b: &mut B,
307 expr_cfg: &C,
308 up: TraceRow<C::Element>,
309 _down: TraceRow<C::Element>,
310 _from_ref: FromR,
311 _mbs: MulByScalar,
312 ideal_from_ref: IFromR,
313 _fq_ideal_from_ref: IFqFromR,
314 ) where
315 C: SemiringConfig,
316 B: ConstraintBuilder<Expr = C::Element>,
317 FromR: Fn(&Self::Scalar) -> C::Element,
318 MulByScalar: Fn(&C::Element, &Self::Scalar) -> Option<C::Element>,
319 IFromR: Fn(&Self::Ideal) -> B::Ideal,
320 {
321 let int_col = &up.int[0];
322 let binary_poly_col = &up.binary_poly[0];
323
324 b.assert_in_ideal(
325 expr_cfg.sub(binary_poly_col, int_col),
326 &ideal_from_ref(&DegreeOneIdeal::new(R::from(2))),
327 );
328 }
329}
330
331impl<R, P> GenerateRandomTrace<32> for BinaryDecompositionUair<R, P>
332where
333 R: ConstSemiring + From<u32> + 'static,
334 P: Semiring + 'static,
335{
336 type PolyCoeff = R;
337 type Int = R;
338
339 fn generate_random_trace<G: Rng + ?Sized>(
340 num_vars: usize,
341 rng: &mut G,
342 ) -> UairTrace<'static, R, R, 32, 32> {
343 let int_col_u32: DenseMultilinearExtension<u32> =
344 DenseMultilinearExtension::rand(num_vars, rng);
345
346 let binary_poly_col: DenseMultilinearExtension<BinaryPoly<32>> =
347 int_col_u32.iter().map(|i| BinaryPoly::from(*i)).collect();
348
349 let int_col = int_col_u32.into_iter().map(R::from).collect();
350
351 UairTrace {
352 binary_poly: vec![binary_poly_col].into(),
353 arbitrary_poly: vec![].into(),
354 int: vec![int_col].into(),
355 }
356 }
357}
358
359#[derive(Clone, Debug)]
360pub struct BigLinearUair<R, P>(PhantomData<(R, P)>);
361
362impl<R, P> Uair for BigLinearUair<R, P>
363where
364 R: ConstSemiring + From<u32> + 'static,
365 P: Semiring + 'static,
366{
367 type Ideal = DegreeOneIdeal<R>;
368 type FqIdeal = ImpossibleIdeal;
369 type Scalar = DensePolynomial<R, 32>;
370 type Prime = P;
371
372 fn signature() -> UairSignature<Self::Prime> {
373 let total = TotalColumnLayout::new(16, 0, 1);
374 let shifts = (0..16).map(|i| ShiftSpec::new(i, 1)).collect();
375 UairSignature::new(total, PublicColumnLayout::default(), shifts, vec![])
376 }
377
378 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
379 b: &mut B,
380 expr_cfg: &C,
381 up: TraceRow<C::Element>,
382 down: TraceRow<C::Element>,
383 _from_ref: FromR,
384 _mbs: MulByScalar,
385 ideal_from_ref: IFromR,
386 _fq_ideal_from_ref: IFqFromR,
387 ) where
388 C: SemiringConfig,
389 B: ConstraintBuilder<Expr = C::Element>,
390 FromR: Fn(&Self::Scalar) -> C::Element,
391 MulByScalar: Fn(&C::Element, &Self::Scalar) -> Option<C::Element>,
392 IFromR: Fn(&Self::Ideal) -> B::Ideal,
393 {
394 let one_ideal = DegreeOneIdeal::new(R::from(1));
395 let two_ideal = DegreeOneIdeal::new(R::from(2));
396
397 let sum_of_binary_polys = up.binary_poly[1..]
398 .iter()
399 .fold(up.binary_poly[0].clone(), |acc, next| {
400 expr_cfg.add(&acc, next)
401 });
402
403 b.assert_in_ideal(
406 expr_cfg.sub(&sum_of_binary_polys, &up.int[0]),
407 &ideal_from_ref(&one_ideal),
408 );
409
410 b.assert_in_ideal(
412 expr_cfg.sub(&down.binary_poly[0], &up.int[0]),
413 &ideal_from_ref(&two_ideal),
414 );
415
416 up.binary_poly[1..]
419 .iter()
420 .zip(&down.binary_poly[1..])
421 .for_each(|(up, down)| {
422 b.assert_in_ideal(expr_cfg.sub(up, down), &ideal_from_ref(&one_ideal));
423 });
424 }
425}
426
427impl<R, P> GenerateRandomTrace<32> for BigLinearUair<R, P>
428where
429 R: ConstSemiring + From<u32> + 'static,
430 P: Semiring + 'static,
431{
432 type PolyCoeff = R;
433 type Int = R;
434
435 fn generate_random_trace<G: Rng + ?Sized>(
436 num_vars: usize,
437 rng: &mut G,
438 ) -> UairTrace<'static, R, R, 32, 32> {
439 fn random_binary_poly_with_popcount(
441 popcount: u32,
442 rng: &mut (impl rand::Rng + ?Sized),
443 ) -> BinaryPoly<32> {
444 let mut positions: [u8; 32] =
445 core::array::from_fn(|i| u8::try_from(i).expect("can't fail"));
446 for i in 0..popcount as usize {
447 let j = i + rng.next_u32() as usize % (32 - i);
448 positions.swap(i, j);
449 }
450 let mut value: u32 = 0;
451 for &pos in &positions[..popcount as usize] {
452 value |= 1u32 << pos;
453 }
454 BinaryPoly::from(value)
455 }
456
457 let mut binary_poly_cols: Vec<DenseMultilinearExtension<BinaryPoly<32>>> =
458 vec![(0..(1 << num_vars)).map(|_| BinaryPoly::zero()).collect(); 16];
459 let mut int_col: DenseMultilinearExtension<Self::Int> =
460 (0..(1 << num_vars)).map(|_| R::ZERO).collect();
461
462 binary_poly_cols.iter_mut().for_each(|col| {
463 col[0] = rng.random();
464 });
465
466 for i in 0..(1 << num_vars) - 1 {
467 let int: u32 = binary_poly_cols
468 .iter()
469 .map(|col| col[i].evaluate_at_point(&1_u32).expect("should be fine"))
470 .sum();
471 int_col[i] = R::from(int);
472
473 binary_poly_cols[0][i + 1] = BinaryPoly::from(int);
474 binary_poly_cols[1..].iter_mut().for_each(|col| {
475 let popcount = col[i].evaluate_at_point(&1_u32).expect("should be fine");
476 col[i + 1] = random_binary_poly_with_popcount(popcount, rng);
477 });
478 }
479
480 let len = int_col.len();
481
482 int_col[len - 1] = R::from(
483 binary_poly_cols
484 .iter()
485 .map(|col| {
486 col[len - 1]
487 .evaluate_at_point(&1_u32)
488 .expect("should be fine")
489 })
490 .sum::<u32>(),
491 );
492
493 UairTrace {
494 binary_poly: binary_poly_cols.into(),
495 arbitrary_poly: vec![].into(),
496 int: vec![int_col].into(),
497 }
498 }
499}
500
501#[derive(Clone, Debug)]
502pub struct BigLinearUairWithPublicInput<R, P>(PhantomData<(R, P)>);
503
504impl<R, P> Uair for BigLinearUairWithPublicInput<R, P>
505where
506 R: ConstSemiring + From<u32> + 'static,
507 P: Semiring + 'static,
508{
509 type Ideal = <BigLinearUair<R, P> as Uair>::Ideal;
510 type FqIdeal = <BigLinearUair<R, P> as Uair>::FqIdeal;
511 type Scalar = <BigLinearUair<R, P> as Uair>::Scalar;
512 type Prime = <BigLinearUair<R, P> as Uair>::Prime;
513
514 fn signature() -> UairSignature<Self::Prime> {
515 let total = TotalColumnLayout::new(16, 0, 1);
516 let public = PublicColumnLayout::new(4, 0, 0);
517 let shifts = (0..16).map(|i| ShiftSpec::new(i, 1)).collect();
518 UairSignature::new(total, public, shifts, vec![])
519 }
520
521 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
522 b: &mut B,
523 expr_cfg: &C,
524 up: TraceRow<C::Element>,
525 down: TraceRow<C::Element>,
526 from_ref: FromR,
527 mbs: MulByScalar,
528 ideal_from_ref: IFromR,
529 fq_ideal_from_ref: IFqFromR,
530 ) where
531 C: SemiringConfig,
532 B: ConstraintBuilder<Expr = C::Element>,
533 FromR: Fn(&Self::Scalar) -> C::Element,
534 MulByScalar: Fn(&C::Element, &Self::Scalar) -> Option<C::Element>,
535 IFromR: Fn(&Self::Ideal) -> B::Ideal,
536 IFqFromR: Fn(&Self::FqIdeal) -> B::FqIdeal,
537 {
538 BigLinearUair::<R, P>::constrain_general(
539 b,
540 expr_cfg,
541 up,
542 down,
543 from_ref,
544 mbs,
545 ideal_from_ref,
546 fq_ideal_from_ref,
547 )
548 }
549}
550
551impl<R, P> GenerateRandomTrace<32> for BigLinearUairWithPublicInput<R, P>
552where
553 R: ConstSemiring + From<u32> + 'static,
554 P: Semiring + 'static,
555{
556 type PolyCoeff = <BigLinearUair<R, P> as GenerateRandomTrace<32>>::PolyCoeff;
557 type Int = <BigLinearUair<R, P> as GenerateRandomTrace<32>>::Int;
558
559 fn generate_random_trace<G: Rng + ?Sized>(
560 num_vars: usize,
561 rng: &mut G,
562 ) -> UairTrace<'static, Self::PolyCoeff, Self::Int, 32, 32> {
563 BigLinearUair::<R, P>::generate_random_trace(num_vars, rng)
564 }
565}
566
567#[derive(Clone, Debug)]
582pub struct ShaProxy<R, P>(PhantomData<(R, P)>);
583
584impl<R, P> Uair for ShaProxy<R, P>
585where
586 R: ConstSemiring + From<u32> + 'static,
587 P: Semiring + 'static,
588{
589 type Ideal = DegreeOneIdeal<R>;
590 type FqIdeal = ImpossibleIdeal;
591 type Scalar = DensePolynomial<R, 32>;
592 type Prime = P;
593
594 fn signature() -> UairSignature<Self::Prime> {
595 let total = TotalColumnLayout::new(14, 0, 4);
597 let shifts = vec![ShiftSpec::new(0, 1), ShiftSpec::new(4, 4)];
600 UairSignature::new(total, PublicColumnLayout::default(), shifts, vec![])
601 }
602
603 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
604 b: &mut B,
605 expr_cfg: &C,
606 up: TraceRow<C::Element>,
607 down: TraceRow<C::Element>,
608 _from_ref: FromR,
609 mbs: MulByScalar,
610 ideal_from_ref: IFromR,
611 _fq_ideal_from_ref: IFqFromR,
612 ) where
613 C: SemiringConfig,
614 B: ConstraintBuilder<Expr = C::Element>,
615 FromR: Fn(&Self::Scalar) -> C::Element,
616 MulByScalar: Fn(&C::Element, &Self::Scalar) -> Option<C::Element>,
617 IFromR: Fn(&Self::Ideal) -> B::Ideal,
618 {
619 let one_ideal = ideal_from_ref(&DegreeOneIdeal::new(R::ONE));
620 let two_ideal = ideal_from_ref(&DegreeOneIdeal::new(R::from(2)));
621 let x_scalar = DensePolynomial::<R, 32>::new_with_zero([R::ZERO, R::from(1)], R::zero());
623
624 let c1 = [
630 &up.binary_poly[1],
631 &up.binary_poly[2],
632 &up.binary_poly[3],
633 &up.int[0],
634 &up.int[1],
635 &up.int[2],
636 ]
637 .into_iter()
638 .fold(down.binary_poly[0].clone(), |acc, term| {
639 expr_cfg.sub(&acc, term)
640 });
641 b.assert_in_ideal(c1, &two_ideal);
642
643 let c2 = [
645 &up.binary_poly[5],
646 &up.binary_poly[6],
647 &up.binary_poly[7],
648 &up.int[1],
649 &up.int[2],
650 &up.int[3],
651 ]
652 .into_iter()
653 .fold(down.binary_poly[1].clone(), |acc, term| {
654 expr_cfg.sub(&acc, term)
655 });
656 b.assert_in_ideal(c2, &two_ideal);
657
658 b.assert_in_ideal(expr_cfg.sub(&up.binary_poly[8], &up.int[0]), &two_ideal);
660
661 b.assert_in_ideal(expr_cfg.sub(&up.binary_poly[9], &up.int[1]), &two_ideal);
663
664 b.assert_in_ideal(
666 expr_cfg.sub(
667 &up.binary_poly[10],
668 &mbs(&up.binary_poly[11], &x_scalar).expect("mul-by-X overflow"),
669 ),
670 &one_ideal,
671 );
672
673 b.assert_in_ideal(
675 expr_cfg.sub(
676 &up.binary_poly[12],
677 &mbs(&up.binary_poly[13], &x_scalar).expect("mul-by-X overflow"),
678 ),
679 &one_ideal,
680 );
681 }
682}
683
684impl<R, P> GenerateRandomTrace<32> for ShaProxy<R, P>
685where
686 R: ConstSemiring + From<u32> + 'static,
687 P: Semiring + 'static,
688{
689 type PolyCoeff = R;
690 type Int = R;
691
692 #[allow(clippy::needless_range_loop)]
693 fn generate_random_trace<G: Rng + ?Sized>(
694 num_vars: usize,
695 rng: &mut G,
696 ) -> UairTrace<'static, R, R, 32, 32> {
697 fn random_binary_poly_with_popcount(
699 popcount: u32,
700 rng: &mut (impl rand::Rng + ?Sized),
701 ) -> BinaryPoly<32> {
702 let mut positions: [u8; 32] =
703 core::array::from_fn(|i| u8::try_from(i).expect("can't fail"));
704 for i in 0..popcount as usize {
705 let j = i + rng.next_u32() as usize % (32 - i);
706 positions.swap(i, j);
707 }
708 let mut value: u32 = 0;
709 for &pos in &positions[..popcount as usize] {
710 value |= 1_u32 << pos;
711 }
712 BinaryPoly::from(value)
713 }
714
715 const SMALL_MASK: u32 = (1 << 28) - 1;
720 const INT_MAX_EXCL: u32 = 32;
722
723 let len = 1 << num_vars;
724
725 let mut bp_cols: Vec<DenseMultilinearExtension<BinaryPoly<32>>> =
726 vec![(0..len).map(|_| BinaryPoly::zero()).collect(); 14];
727 let mut int_cols: Vec<DenseMultilinearExtension<R>> =
728 vec![(0..len).map(|_| R::ZERO).collect(); 4];
729
730 bp_cols[0][0] = BinaryPoly::from(rng.next_u32() & SMALL_MASK);
737 for k in 0..4.min(len) {
738 bp_cols[4][k] = BinaryPoly::from(rng.next_u32() & SMALL_MASK);
739 }
740
741 for i in 0..len {
742 for k in 1..=3 {
744 bp_cols[k][i] = BinaryPoly::from(rng.next_u32() & SMALL_MASK);
745 }
746
747 let c2_target_oob = i + 4 >= len;
756
757 for k in 5..=7 {
758 bp_cols[k][i] = if c2_target_oob {
759 BinaryPoly::zero()
760 } else {
761 BinaryPoly::from(rng.next_u32() & SMALL_MASK)
762 };
763 }
764
765 let int_vals: [u32; 4] = if c2_target_oob {
769 [rng.next_u32() % INT_MAX_EXCL, 0, 0, 0]
770 } else {
771 [
772 rng.next_u32() % INT_MAX_EXCL,
773 rng.next_u32() % INT_MAX_EXCL,
774 rng.next_u32() % INT_MAX_EXCL,
775 rng.next_u32() % INT_MAX_EXCL,
776 ]
777 };
778 for (k, v) in int_vals.iter().enumerate() {
779 int_cols[k][i] = R::from(*v);
780 }
781
782 bp_cols[8][i] = BinaryPoly::from(int_vals[0]);
787 bp_cols[9][i] = BinaryPoly::from(int_vals[1]);
788
789 let bp11: BinaryPoly<32> = rng.random();
791 let popcount11 = bp11
792 .evaluate_at_point(&1_u32)
793 .expect("popcount eval should fit in u32");
794 bp_cols[11][i] = bp11;
795 bp_cols[10][i] = random_binary_poly_with_popcount(popcount11, rng);
796
797 let bp13: BinaryPoly<32> = rng.random();
799 let popcount13 = bp13
800 .evaluate_at_point(&1_u32)
801 .expect("popcount eval should fit in u32");
802 bp_cols[13][i] = bp13;
803 bp_cols[12][i] = random_binary_poly_with_popcount(popcount13, rng);
804
805 let eval_at_2 = |bp: &BinaryPoly<32>| -> u32 {
811 bp.evaluate_at_point(&2_u32)
812 .expect("28-bit binary poly eval at 2 fits in u32")
813 };
814
815 if i + 1 < len {
817 let s1 = eval_at_2(&bp_cols[1][i])
818 + eval_at_2(&bp_cols[2][i])
819 + eval_at_2(&bp_cols[3][i])
820 + int_vals[0]
821 + int_vals[1]
822 + int_vals[2];
823 bp_cols[0][i + 1] = BinaryPoly::from(s1);
824 }
825
826 if i + 4 < len {
828 let s2 = eval_at_2(&bp_cols[5][i])
829 + eval_at_2(&bp_cols[6][i])
830 + eval_at_2(&bp_cols[7][i])
831 + int_vals[1]
832 + int_vals[2]
833 + int_vals[3];
834 bp_cols[4][i + 4] = BinaryPoly::from(s2);
835 }
836 }
837
838 UairTrace {
839 binary_poly: bp_cols.into(),
840 arbitrary_poly: vec![].into(),
841 int: int_cols.into(),
842 }
843 }
844}
845
846#[derive(Clone, Debug)]
850pub struct TestUairMixedShifts<R, P>(PhantomData<(R, P)>);
851
852impl<R, P> Uair for TestUairMixedShifts<R, P>
853where
854 R: Semiring + 'static,
855 P: Semiring + 'static,
856{
857 type Ideal = ImpossibleIdeal;
858 type FqIdeal = ImpossibleIdeal;
859 type Scalar = DensePolynomial<R, 32>;
860 type Prime = P;
861
862 fn signature() -> UairSignature<Self::Prime> {
863 let total = TotalColumnLayout::new(0, 3, 0);
864 let shifts = vec![
865 ShiftSpec::new(0, 1), ShiftSpec::new(1, 2), ];
868 UairSignature::new(total, PublicColumnLayout::default(), shifts, vec![])
869 }
870
871 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
875 builder: &mut B,
876 expr_cfg: &C,
877 up: TraceRow<C::Element>,
878 down: TraceRow<C::Element>,
879 _from_ref: FromR,
880 _mbs: MulByScalar,
881 _ideal_from_ref: IFromR,
882 _fq_ideal_from_ref: IFqFromR,
883 ) where
884 C: SemiringConfig,
885 B: ConstraintBuilder<Expr = C::Element>,
886 {
887 let up = up.arbitrary_poly;
888 let down = down.arbitrary_poly;
889
890 builder.assert_zero(expr_cfg.sub(&expr_cfg.sub(&down[0], &up[0]), &up[1]));
891 builder.assert_zero(expr_cfg.sub(&up[2], &down[1]));
892 }
893}
894
895impl<R, P> GenerateRandomTrace<32> for TestUairMixedShifts<R, P>
896where
897 R: Semiring + From<i8> + 'static,
898 P: Semiring + 'static,
899 StandardUniform: Distribution<R>,
900{
901 type PolyCoeff = R;
902 type Int = R;
903
904 fn generate_random_trace<G: Rng + ?Sized>(
906 num_vars: usize,
907 rng: &mut G,
908 ) -> UairTrace<'static, R, R, 32, 32> {
909 let n = 1 << num_vars;
910
911 let ring_cfg = FixedConfig::<R>::default();
913 let poly_cfg = ring_cfg.dyn_poly_cfg();
914
915 let b_col: Vec<DynamicPolynomial<R>> = (0..n)
916 .map(|_| DynamicPolynomial::new(vec![R::from(rng.random::<i8>())]))
917 .collect();
918
919 let mut a_col: Vec<DynamicPolynomial<R>> =
921 vec![DynamicPolynomial::new(vec![R::from(rng.random::<i8>())])];
922 for i in 0..n - 1 {
923 a_col.push(poly_cfg.add(&a_col[i], &b_col[i]));
924 }
925
926 let mut c_col: Vec<DynamicPolynomial<R>> = Vec::with_capacity(n);
928 for i in 0..n {
929 if i + 2 < n {
930 c_col.push(b_col[i + 2].clone());
931 } else {
932 c_col.push(DynamicPolynomial::ZERO);
933 }
934 }
935
936 let to_mle =
937 |col: Vec<DynamicPolynomial<R>>| -> DenseMultilinearExtension<DensePolynomial<R, 32>> {
938 col.into_iter()
939 .map(|x| DensePolynomial::new_with_zero(x.coeffs, R::zero()))
940 .collect()
941 };
942
943 UairTrace {
944 arbitrary_poly: vec![to_mle(a_col), to_mle(b_col), to_mle(c_col)].into(),
945 ..Default::default()
946 }
947 }
948}
949
950#[derive(Clone, Debug)]
957pub struct TestUairBitOpsMixedSplice<R, P>(PhantomData<(R, P)>);
958
959impl<R, P> Uair for TestUairBitOpsMixedSplice<R, P>
960where
961 R: ConstSemiring + 'static,
962 P: Semiring + 'static,
963{
964 type Ideal = DegreeOneIdeal<R>;
965 type FqIdeal = ImpossibleIdeal;
966 type Scalar = DensePolynomial<R, 32>;
967 type Prime = P;
968
969 fn signature() -> UairSignature<Self::Prime> {
970 let total = TotalColumnLayout::new(3, 2, 0);
971 let shifts = vec![ShiftSpec::new(0, 1), ShiftSpec::new(3, 1)];
972 let bit_op_specs = vec![BitOpSpec::new(0, BitOp::ShR(3))];
973 let sig = UairSignature::new(total, PublicColumnLayout::default(), shifts, vec![])
974 .with_bit_op_specs(bit_op_specs);
975 debug_assert_eq!(sig.down_cols().num_binary_poly_cols(), 2);
976 debug_assert_eq!(sig.down_cols().num_arbitrary_poly_cols(), 1);
977 debug_assert_eq!(sig.down_cols().num_int_cols(), 0);
978 sig
979 }
980
981 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
982 b: &mut B,
983 expr_cfg: &C,
984 up: TraceRow<C::Element>,
985 down: TraceRow<C::Element>,
986 _from_ref: FromR,
987 _mbs: MulByScalar,
988 ideal_from_ref: IFromR,
989 _fq_ideal_from_ref: IFqFromR,
990 ) where
991 C: SemiringConfig,
992 B: ConstraintBuilder<Expr = C::Element>,
993 IFromR: Fn(&Self::Ideal) -> B::Ideal,
994 {
995 let one_ideal = ideal_from_ref(&DegreeOneIdeal::new(R::ONE));
996 b.assert_in_ideal(
997 expr_cfg.sub(&down.binary_poly[0], &up.binary_poly[2]),
998 &one_ideal,
999 );
1000 b.assert_in_ideal(
1001 expr_cfg.sub(&down.binary_poly[1], &up.binary_poly[1]),
1002 &one_ideal,
1003 );
1004 b.assert_in_ideal(
1005 expr_cfg.sub(&down.arbitrary_poly[0], &up.arbitrary_poly[1]),
1006 &one_ideal,
1007 );
1008 }
1009}
1010
1011impl<R, P> GenerateRandomTrace<32> for TestUairBitOpsMixedSplice<R, P>
1012where
1013 R: ConstSemiring + From<i8> + 'static,
1014 P: Semiring + 'static,
1015 StandardUniform: Distribution<R>,
1016{
1017 type PolyCoeff = R;
1018 type Int = R;
1019
1020 fn generate_random_trace<G: Rng + ?Sized>(
1021 num_vars: usize,
1022 rng: &mut G,
1023 ) -> UairTrace<'static, R, R, 32, 32> {
1024 let n = 1usize << num_vars;
1025
1026 let w_u32: Vec<u32> = (0..n).map(|_| rng.next_u32()).collect();
1027 let w_col: DenseMultilinearExtension<BinaryPoly<32>> =
1028 w_u32.iter().map(|w| BinaryPoly::from(*w)).collect();
1029 let s_shr_col: DenseMultilinearExtension<BinaryPoly<32>> =
1030 w_u32.iter().map(|w| BinaryPoly::from(w >> 3)).collect();
1031 let t_col: DenseMultilinearExtension<BinaryPoly<32>> = (0..n)
1032 .map(|i| {
1033 if i + 1 < n {
1034 BinaryPoly::from(w_u32[i + 1])
1035 } else {
1036 BinaryPoly::from(0u32)
1037 }
1038 })
1039 .collect();
1040
1041 let a_cells: Vec<DensePolynomial<R, 32>> = (0..n)
1042 .map(|_| DensePolynomial::new_with_zero([R::from(rng.random::<i8>())], R::zero()))
1043 .collect();
1044 let a_next_cells: Vec<DensePolynomial<R, 32>> = (0..n)
1045 .map(|i| {
1046 if i + 1 < n {
1047 a_cells[i + 1].clone()
1048 } else {
1049 DensePolynomial::<R, 32>::zero()
1050 }
1051 })
1052 .collect();
1053
1054 UairTrace {
1055 binary_poly: vec![w_col, s_shr_col, t_col].into(),
1056 arbitrary_poly: vec![
1057 a_cells.into_iter().collect(),
1058 a_next_cells.into_iter().collect(),
1059 ]
1060 .into(),
1061 int: vec![].into(),
1062 }
1063 }
1064}
1065
1066#[derive(Clone, Debug)]
1073pub struct TestUairBitOpsFqFamily<R, P>(PhantomData<(R, P)>);
1074
1075impl<R, P> Uair for TestUairBitOpsFqFamily<R, P>
1076where
1077 R: ConstSemiring + 'static,
1078 P: Semiring + From<u64> + 'static,
1079{
1080 type Ideal = DegreeOneIdeal<R>;
1081 type FqIdeal = DegreeOneIdeal<R>;
1082 type Scalar = DensePolynomial<R, 32>;
1083 type Prime = P;
1084
1085 fn signature() -> UairSignature<Self::Prime> {
1086 let total = TotalColumnLayout::new(2, 0, 0);
1087 UairSignature::new(total, PublicColumnLayout::default(), vec![], vec![])
1088 .with_bit_op_specs(vec![BitOpSpec::new(0, BitOp::ShR(3))])
1089 .with_primes(vec![P::from(MERSENNE_61_PRIME)])
1090 }
1091
1092 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
1093 b: &mut B,
1094 expr_cfg: &C,
1095 up: TraceRow<C::Element>,
1096 down: TraceRow<C::Element>,
1097 _from_ref: FromR,
1098 _mbs: MulByScalar,
1099 ideal_from_ref: IFromR,
1100 fq_ideal_from_ref: IFqFromR,
1101 ) where
1102 C: SemiringConfig,
1103 B: ConstraintBuilder<Expr = C::Element>,
1104 IFromR: Fn(&Self::Ideal) -> B::Ideal,
1105 IFqFromR: Fn(&Self::FqIdeal) -> B::FqIdeal,
1106 {
1107 let q_ideal = ideal_from_ref(&DegreeOneIdeal::new(R::ONE));
1108 let fq_ideal = fq_ideal_from_ref(&DegreeOneIdeal::new(R::ONE));
1109 let bit_op_matches_expected = expr_cfg.sub(&down.binary_poly[0], &up.binary_poly[1]);
1110
1111 b.assert_in_ideal(bit_op_matches_expected.clone(), &q_ideal);
1112 b.assert_in_fq_ideal(0, bit_op_matches_expected, &fq_ideal);
1113 }
1114}
1115
1116impl<R, P> GenerateRandomTrace<32> for TestUairBitOpsFqFamily<R, P>
1117where
1118 R: ConstSemiring + 'static,
1119 P: Semiring + From<u64> + 'static,
1120{
1121 type PolyCoeff = R;
1122 type Int = R;
1123
1124 fn generate_random_trace<G: Rng + ?Sized>(
1125 num_vars: usize,
1126 rng: &mut G,
1127 ) -> UairTrace<'static, R, R, 32, 32> {
1128 let n = 1usize << num_vars;
1129 let w_u32: Vec<u32> = (0..n).map(|_| rng.next_u32()).collect();
1130 let w_col: DenseMultilinearExtension<BinaryPoly<32>> =
1131 w_u32.iter().map(|w| BinaryPoly::from(*w)).collect();
1132 let s_shr_col: DenseMultilinearExtension<BinaryPoly<32>> =
1133 w_u32.iter().map(|w| BinaryPoly::from(w >> 3)).collect();
1134
1135 UairTrace {
1136 binary_poly: vec![w_col, s_shr_col].into(),
1137 ..Default::default()
1138 }
1139 }
1140}
1141
1142#[derive(Clone, Debug)]
1154pub struct TestUairAffineVirtual<R, P, const NUM_PUBLIC_BINARY: usize>(PhantomData<(R, P)>);
1155
1156pub type TestUairAffineVirtualUnshifted<R, P> = TestUairAffineVirtual<R, P, 1>;
1158
1159pub type TestUairAffineVirtualPublicOnly<R, P> = TestUairAffineVirtual<R, P, 4>;
1161
1162impl<R, P, const NUM_PUBLIC_BINARY: usize> Uair for TestUairAffineVirtual<R, P, NUM_PUBLIC_BINARY>
1163where
1164 R: ConstSemiring + 'static,
1165 P: Semiring + From<u64> + 'static,
1166{
1167 type Ideal = DegreeOneIdeal<R>;
1168 type FqIdeal = DegreeOneIdeal<R>;
1169 type Scalar = DensePolynomial<R, 32>;
1170 type Prime = P;
1171
1172 fn signature() -> UairSignature<Self::Prime> {
1173 UairSignature::new(
1174 TotalColumnLayout::new(4, 0, 0),
1175 PublicColumnLayout::new(NUM_PUBLIC_BINARY, 0, 0),
1176 vec![],
1177 vec![],
1178 )
1179 .with_affine_virtual_specs(vec![
1180 AffineVirtualSpec::with_ones_coefficient(
1181 vec![
1182 AffineVirtualTerm::new(0, -1),
1183 AffineVirtualTerm::new(1, 1),
1184 AffineVirtualTerm::new(2, -2),
1185 ],
1186 1,
1187 ),
1188 AffineVirtualSpec::new(vec![
1189 AffineVirtualTerm::new(0, 1),
1190 AffineVirtualTerm::new(1, 1),
1191 AffineVirtualTerm::new(3, -2),
1192 ]),
1193 ])
1194 .with_primes(vec![P::from(MERSENNE_61_PRIME)])
1195 }
1196
1197 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
1198 b: &mut B,
1199 expr_cfg: &C,
1200 up: TraceRow<C::Element>,
1201 _down: TraceRow<C::Element>,
1202 _from_ref: FromR,
1203 _mbs: MulByScalar,
1204 ideal_from_ref: IFromR,
1205 fq_ideal_from_ref: IFqFromR,
1206 ) where
1207 C: SemiringConfig,
1208 B: ConstraintBuilder<Expr = C::Element>,
1209 IFromR: Fn(&Self::Ideal) -> B::Ideal,
1210 IFqFromR: Fn(&Self::FqIdeal) -> B::FqIdeal,
1211 {
1212 let identity = expr_cfg.sub(&up.binary_poly[1], &up.binary_poly[1]);
1216 b.assert_in_ideal(
1217 identity.clone(),
1218 &ideal_from_ref(&DegreeOneIdeal::new(R::ONE)),
1219 );
1220 b.assert_in_fq_ideal(
1221 0,
1222 identity,
1223 &fq_ideal_from_ref(&DegreeOneIdeal::new(R::ONE)),
1224 );
1225 }
1226}
1227
1228impl<R, P, const NUM_PUBLIC_BINARY: usize> GenerateRandomTrace<32>
1229 for TestUairAffineVirtual<R, P, NUM_PUBLIC_BINARY>
1230where
1231 R: ConstSemiring + 'static,
1232 P: Semiring + From<u64> + 'static,
1233{
1234 type PolyCoeff = R;
1235 type Int = R;
1236
1237 fn generate_random_trace<G: Rng + ?Sized>(
1238 num_vars: usize,
1239 rng: &mut G,
1240 ) -> UairTrace<'static, R, R, 32, 32> {
1241 let n = 1usize << num_vars;
1242 let e: Vec<u32> = (0..n).map(|_| rng.next_u32()).collect();
1243 let g: Vec<u32> = (0..n).map(|_| rng.next_u32()).collect();
1244 let u_not: Vec<u32> = e.iter().zip(&g).map(|(e, g)| !e & g).collect();
1245 let u_and: Vec<u32> = e.iter().zip(&g).map(|(e, g)| e & g).collect();
1246
1247 UairTrace {
1248 binary_poly: vec![
1249 e.into_iter().map(BinaryPoly::from).collect(),
1250 g.into_iter().map(BinaryPoly::from).collect(),
1251 u_not.into_iter().map(BinaryPoly::from).collect(),
1252 u_and.into_iter().map(BinaryPoly::from).collect(),
1253 ]
1254 .into(),
1255 ..Default::default()
1256 }
1257 }
1258}
1259
1260#[derive(Clone, Debug)]
1281pub struct TestUairFqLargePrime<R, P>(PhantomData<(R, P)>);
1282
1283pub const MERSENNE_61_PRIME: u64 = (1 << 61) - 1;
1285
1286#[allow(clippy::cast_possible_truncation)]
1289pub const GOLDILOCKS_PRIME: u64 = ((1_u128 << 64) - (1 << 32) + 1) as u64;
1290
1291impl<R, P> Uair for TestUairFqLargePrime<R, P>
1292where
1293 R: ConstSemiring + From<i32> + 'static,
1294 P: Semiring + From<u64> + 'static,
1295{
1296 type Ideal = ImpossibleIdeal;
1297 type FqIdeal = DegreeOneIdeal<R>;
1298 type Scalar = DensePolynomial<R, 32>;
1299 type Prime = P;
1300
1301 fn signature() -> UairSignature<Self::Prime> {
1302 let total = TotalColumnLayout::new(0, 1, 0);
1304 UairSignature::new(total, PublicColumnLayout::default(), vec![], vec![])
1305 .with_primes(vec![P::from(MERSENNE_61_PRIME), P::from(GOLDILOCKS_PRIME)])
1306 }
1307
1308 fn constrain_general<C, B, FromR, MulByScalar, IFromR, IFqFromR>(
1309 b: &mut B,
1310 _expr_cfg: &C,
1311 up: TraceRow<C::Element>,
1312 _down: TraceRow<C::Element>,
1313 _from_ref: FromR,
1314 _mbs: MulByScalar,
1315 _ideal_from_ref: IFromR,
1316 fq_ideal_from_ref: IFqFromR,
1317 ) where
1318 C: SemiringConfig,
1319 B: ConstraintBuilder<Expr = C::Element>,
1320 FromR: Fn(&Self::Scalar) -> C::Element,
1321 MulByScalar: Fn(&C::Element, &Self::Scalar) -> Option<C::Element>,
1322 IFqFromR: Fn(&Self::FqIdeal) -> B::FqIdeal,
1323 {
1324 let ideal = fq_ideal_from_ref(&DegreeOneIdeal::<R>::new(R::ZERO));
1326 b.assert_in_fq_ideal(
1327 0,
1328 up.arbitrary_poly[0].clone(),
1329 &ideal,
1330 );
1331 b.assert_in_fq_ideal(
1332 1,
1333 up.arbitrary_poly[0].clone(),
1334 &ideal,
1335 );
1336 }
1337}
1338
1339impl<R, P> GenerateRandomTrace<32> for TestUairFqLargePrime<R, P>
1340where
1341 R: ConstSemiring + From<i32> + 'static,
1342 P: Semiring + From<u64> + 'static,
1343{
1344 type PolyCoeff = R;
1345 type Int = R;
1346
1347 fn generate_random_trace<G: Rng + ?Sized>(
1348 num_vars: usize,
1349 rng: &mut G,
1350 ) -> UairTrace<'static, R, R, 32, 32> {
1351 let a: DenseMultilinearExtension<DensePolynomial<R, 32>> =
1354 DenseMultilinearExtension::rand(num_vars, rng)
1355 .into_iter()
1356 .map(|x: u32| {
1357 let poly = DensePolynomial::from_ref(&DensePolynomial::<Boolean, _>::from(
1358 BinaryPoly::<32>::from(x),
1359 ));
1360 let mut coeffs = [R::ZERO; 32];
1362 coeffs[1..].clone_from_slice(&poly.coeffs[1..]);
1363 DensePolynomial::new(coeffs)
1364 })
1365 .collect();
1366
1367 UairTrace {
1368 arbitrary_poly: vec![a].into(),
1369 ..Default::default()
1370 }
1371 }
1372}
1373
1374#[cfg(test)]
1375mod tests {
1376 use super::*;
1377 use crypto_primitives::crypto_bigint_int::Int;
1378 use num_traits::ConstZero;
1379 use zinc_uair::{
1380 collect_scalars::collect_scalars,
1381 constraint_counter::count_constraints,
1382 degree_counter::{count_constraint_degrees_flattened, count_max_degree},
1383 };
1384
1385 const LIMBS: usize = 4;
1386
1387 #[test]
1388 fn test_constraint_degrees() {
1389 fn assert_uair_shape<U: Uair>(expected_degrees: &[usize]) {
1390 assert_eq!(count_constraints::<U>().total(), expected_degrees.len());
1391 assert_eq!(count_constraint_degrees_flattened::<U>(), expected_degrees);
1392 assert_eq!(
1393 count_max_degree::<U>(),
1394 *expected_degrees.iter().max().unwrap()
1395 );
1396 }
1397
1398 assert_uair_shape::<TestUairSimpleMultiplication<Int<LIMBS>, u64>>(&[2, 2, 2]);
1399 assert_uair_shape::<TestUairNoMultiplication<Int<LIMBS>, u64>>(&[1]);
1400 assert_uair_shape::<TestUairScalarMultiplications<Int<LIMBS>, u64>>(&[1]);
1401 assert_uair_shape::<BinaryDecompositionUair<u32, u64>>(&[1]);
1402 assert_uair_shape::<BigLinearUair<u32, u64>>(&[1; 17]);
1403 assert_uair_shape::<TestUairMixedShifts<Int<LIMBS>, u64>>(&[1, 1]);
1404 assert_uair_shape::<TestUairBitOpsMixedSplice<Int<LIMBS>, u64>>(&[1, 1, 1]);
1405 assert_uair_shape::<TestUairBitOpsFqFamily<Int<LIMBS>, u64>>(&[1, 1]);
1406 assert_uair_shape::<TestUairAffineVirtualUnshifted<Int<LIMBS>, u64>>(&[1, 1]);
1407 assert_uair_shape::<TestUairAffineVirtualPublicOnly<Int<LIMBS>, u64>>(&[1, 1]);
1408 assert_uair_shape::<TestUairFqLargePrime<Int<LIMBS>, u64>>(&[1, 1]);
1411 }
1412
1413 #[test]
1414 fn test_air_scalar_multiplications_correct_collect_scalars() {
1415 assert_eq!(
1416 collect_scalars::<TestUairScalarMultiplications<Int<LIMBS>, u64>>(),
1417 (vec![
1418 DensePolynomial::new_with_zero(
1419 [Int::from_i8(-1), Int::from_i8(0), Int::from_i8(1)],
1420 Int::ZERO
1421 ),
1422 DensePolynomial::new_with_zero(
1423 [
1424 Int::from_i8(1),
1425 Int::from_i8(2),
1426 Int::from_i8(3),
1427 Int::from_i8(4),
1428 ],
1429 Int::ZERO
1430 )
1431 ]
1432 .into_iter()
1433 .collect())
1434 );
1435 }
1436}