1use alloc::sync::Arc;
2
3use p3_field::extension::{
4 BinomialExtensionField, CubicTrinomialExtensionField, QuinticTrinomialExtensionField,
5};
6use p3_field::{Algebra, ExtensionField, Field, PrimeCharacteristicRing};
7
8use crate::symbolic::expression::BaseLeaf;
9use crate::symbolic::variable::SymbolicVariableExt;
10use crate::symbolic::{SymLeaf, SymbolicExpr, SymbolicExpression, SymbolicVariable};
11
12#[derive(Clone, Debug)]
17pub enum ExtLeaf<F, EF> {
18 Base(SymbolicExpression<F>),
20
21 ExtVariable(SymbolicVariableExt<F, EF>),
23
24 ExtConstant(EF),
26}
27
28pub type SymbolicExpressionExt<F, EF> = SymbolicExpr<ExtLeaf<F, EF>>;
33
34impl<F: Field, EF: ExtensionField<F>> SymLeaf for ExtLeaf<F, EF> {
35 type F = F;
36
37 const ZERO: Self = Self::Base(SymbolicExpression::ZERO);
38 const ONE: Self = Self::Base(SymbolicExpression::ONE);
39 const TWO: Self = Self::Base(SymbolicExpression::TWO);
40 const NEG_ONE: Self = Self::Base(SymbolicExpression::NEG_ONE);
41
42 fn degree_multiple(&self) -> usize {
43 match self {
44 Self::Base(e) => e.degree_multiple(),
45 Self::ExtVariable(v) => v.degree_multiple(),
46 Self::ExtConstant(_) => 0,
47 }
48 }
49
50 fn as_const(&self) -> Option<&F> {
51 match self {
52 Self::Base(SymbolicExpression::Leaf(BaseLeaf::Constant(c))) => Some(c),
53 Self::ExtConstant(ef) if ef.is_in_basefield() => {
54 Some(&ef.as_basis_coefficients_slice()[0])
55 }
56 _ => None,
57 }
58 }
59
60 fn from_const(c: F) -> Self {
61 Self::Base(SymbolicExpression::from(c))
62 }
63}
64
65impl<F: Field, EF> SymbolicExpressionExt<F, EF> {
66 pub fn to_base(&self) -> Option<SymbolicExpression<F>> {
71 match self {
72 Self::Leaf(ExtLeaf::Base(e)) => Some(e.clone()),
73 Self::Leaf(ExtLeaf::ExtVariable(_) | ExtLeaf::ExtConstant(_)) => None,
74 Self::Add {
75 x,
76 y,
77 degree_multiple,
78 } => Some(SymbolicExpr::Add {
79 x: Arc::new(x.to_base()?),
80 y: Arc::new(y.to_base()?),
81 degree_multiple: *degree_multiple,
82 }),
83 Self::Sub {
84 x,
85 y,
86 degree_multiple,
87 } => Some(SymbolicExpr::Sub {
88 x: Arc::new(x.to_base()?),
89 y: Arc::new(y.to_base()?),
90 degree_multiple: *degree_multiple,
91 }),
92 Self::Neg { x, degree_multiple } => Some(SymbolicExpr::Neg {
93 x: Arc::new(x.to_base()?),
94 degree_multiple: *degree_multiple,
95 }),
96 Self::Mul {
97 x,
98 y,
99 degree_multiple,
100 } => Some(SymbolicExpr::Mul {
101 x: Arc::new(x.to_base()?),
102 y: Arc::new(y.to_base()?),
103 degree_multiple: *degree_multiple,
104 }),
105 }
106 }
107}
108
109impl<F: Field, EF> From<SymbolicExpression<F>> for SymbolicExpressionExt<F, EF> {
110 fn from(expr: SymbolicExpression<F>) -> Self {
111 Self::Leaf(ExtLeaf::Base(expr))
112 }
113}
114
115impl<F: Field, EF> From<SymbolicVariable<F>> for SymbolicExpressionExt<F, EF> {
116 fn from(var: SymbolicVariable<F>) -> Self {
117 Self::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Variable(var))))
118 }
119}
120
121impl<F, EF> From<SymbolicVariableExt<F, EF>> for SymbolicExpressionExt<F, EF> {
122 fn from(var: SymbolicVariableExt<F, EF>) -> Self {
123 Self::Leaf(ExtLeaf::ExtVariable(var))
124 }
125}
126
127impl<F: Field, EF> From<F> for SymbolicExpressionExt<F, EF> {
128 fn from(f: F) -> Self {
129 Self::Leaf(ExtLeaf::Base(SymbolicExpression::from(f)))
130 }
131}
132
133impl<F, const D: usize> From<BinomialExtensionField<F, D>>
138 for SymbolicExpressionExt<F, BinomialExtensionField<F, D>>
139where
140 F: Field,
141 BinomialExtensionField<F, D>: ExtensionField<F>,
142{
143 fn from(ef: BinomialExtensionField<F, D>) -> Self {
144 Self::Leaf(ExtLeaf::ExtConstant(ef))
145 }
146}
147
148impl<F: Field, EF: ExtensionField<F>> Algebra<F> for SymbolicExpressionExt<F, EF> {}
149
150impl<F: Field, EF: ExtensionField<F>> Algebra<SymbolicExpression<F>>
151 for SymbolicExpressionExt<F, EF>
152{
153}
154
155impl<F: Field, EF: ExtensionField<F>> Algebra<SymbolicVariable<F>>
156 for SymbolicExpressionExt<F, EF>
157{
158}
159
160impl<F: Field, EF: ExtensionField<F>> Algebra<SymbolicVariableExt<F, EF>>
161 for SymbolicExpressionExt<F, EF>
162{
163}
164
165impl<F: Field, const D: usize> Algebra<BinomialExtensionField<F, D>>
167 for SymbolicExpressionExt<F, BinomialExtensionField<F, D>>
168where
169 BinomialExtensionField<F, D>: ExtensionField<F>,
170{
171}
172
173impl<F: Field> From<CubicTrinomialExtensionField<F>>
174 for SymbolicExpressionExt<F, CubicTrinomialExtensionField<F>>
175where
176 CubicTrinomialExtensionField<F>: ExtensionField<F>,
177{
178 fn from(ef: CubicTrinomialExtensionField<F>) -> Self {
179 Self::Leaf(ExtLeaf::ExtConstant(ef))
180 }
181}
182
183impl<F: Field> From<QuinticTrinomialExtensionField<F>>
184 for SymbolicExpressionExt<F, QuinticTrinomialExtensionField<F>>
185where
186 QuinticTrinomialExtensionField<F>: ExtensionField<F>,
187{
188 fn from(ef: QuinticTrinomialExtensionField<F>) -> Self {
189 Self::Leaf(ExtLeaf::ExtConstant(ef))
190 }
191}
192
193impl<F: Field> Algebra<CubicTrinomialExtensionField<F>>
195 for SymbolicExpressionExt<F, CubicTrinomialExtensionField<F>>
196where
197 CubicTrinomialExtensionField<F>: ExtensionField<F>,
198{
199}
200
201impl<F: Field> Algebra<QuinticTrinomialExtensionField<F>>
203 for SymbolicExpressionExt<F, QuinticTrinomialExtensionField<F>>
204where
205 QuinticTrinomialExtensionField<F>: ExtensionField<F>,
206{
207}
208
209#[cfg(test)]
210mod tests {
211 use p3_baby_bear::BabyBear;
212 use p3_field::extension::BinomialExtensionField;
213 use p3_field::{BasedVectorSpace, PrimeCharacteristicRing};
214
215 use super::*;
216 use crate::symbolic::SymbolicExpr;
217 use crate::symbolic::variable::{BaseEntry, ExtEntry};
218
219 type F = BabyBear;
220 type EF = BinomialExtensionField<BabyBear, 4>;
221
222 #[test]
223 fn ext_leaf_degree_multiple_base_variable() {
224 let var = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 0 }, 0);
226 let leaf = ExtLeaf::<F, EF>::Base(SymbolicExpression::from(var));
227 assert_eq!(leaf.degree_multiple(), 1);
228 }
229
230 #[test]
231 fn ext_leaf_degree_multiple_base_constant() {
232 let leaf = ExtLeaf::<F, EF>::Base(SymbolicExpression::from(F::new(42)));
234 assert_eq!(leaf.degree_multiple(), 0);
235 }
236
237 #[test]
238 fn ext_leaf_degree_multiple_ext_variable() {
239 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 0 }, 0);
241 let leaf = ExtLeaf::ExtVariable(var);
242 assert_eq!(leaf.degree_multiple(), 1);
243 }
244
245 #[test]
246 fn ext_leaf_degree_multiple_ext_variable_challenge() {
247 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Challenge, 0);
249 let leaf = ExtLeaf::ExtVariable(var);
250 assert_eq!(leaf.degree_multiple(), 0);
251 }
252
253 #[test]
254 fn ext_leaf_degree_multiple_ext_constant() {
255 let leaf = ExtLeaf::<F, EF>::ExtConstant(EF::ONE);
257 assert_eq!(leaf.degree_multiple(), 0);
258 }
259
260 #[test]
261 fn ext_leaf_as_const_base_constant() {
262 let leaf = ExtLeaf::<F, EF>::Base(SymbolicExpression::from(F::new(7)));
264 assert_eq!(leaf.as_const(), Some(&F::new(7)));
265 }
266
267 #[test]
268 fn ext_leaf_as_const_base_variable() {
269 let var = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 0 }, 0);
271 let leaf = ExtLeaf::<F, EF>::Base(SymbolicExpression::from(var));
272 assert!(leaf.as_const().is_none());
273 }
274
275 #[test]
276 fn ext_leaf_as_const_ext_variable() {
277 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 0 }, 0);
279 let leaf = ExtLeaf::ExtVariable(var);
280 assert!(leaf.as_const().is_none());
281 }
282
283 #[test]
284 fn ext_leaf_as_const_ext_constant_in_basefield() {
285 let leaf = ExtLeaf::<F, EF>::ExtConstant(EF::ONE);
287 assert_eq!(leaf.as_const(), Some(&F::ONE));
288 }
289
290 #[test]
291 fn ext_leaf_as_const_ext_constant_zero() {
292 let leaf = ExtLeaf::<F, EF>::ExtConstant(EF::ZERO);
294 assert_eq!(leaf.as_const(), Some(&F::ZERO));
295 }
296
297 #[test]
298 fn ext_leaf_as_const_ext_constant_not_in_basefield() {
299 let ef_val = EF::from_basis_coefficients_fn(|i| if i == 1 { F::ONE } else { F::ZERO });
301 let leaf = ExtLeaf::<F, EF>::ExtConstant(ef_val);
302 assert!(leaf.as_const().is_none());
303 }
304
305 #[test]
306 fn ext_leaf_from_const() {
307 let leaf = ExtLeaf::<F, EF>::from_const(F::new(13));
309 assert_eq!(leaf.as_const(), Some(&F::new(13)));
310 }
311
312 #[test]
313 fn to_base_leaf_base() {
314 let base_expr = SymbolicExpression::from(F::new(5));
316 let ext_expr = SymbolicExpressionExt::<F, EF>::from(base_expr);
317 let lowered = ext_expr.to_base();
318
319 assert!(lowered.is_some());
320 assert!(matches!(
321 lowered.unwrap(),
322 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == F::new(5)
323 ));
324 }
325
326 #[test]
327 fn to_base_leaf_ext_variable() {
328 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 0 }, 0);
330 let ext_expr = SymbolicExpressionExt::<F, EF>::from(var);
331 assert!(ext_expr.to_base().is_none());
332 }
333
334 #[test]
335 fn to_base_leaf_ext_constant() {
336 let ext_expr = SymbolicExpressionExt::<F, EF>::Leaf(ExtLeaf::ExtConstant(EF::TWO));
338 assert!(ext_expr.to_base().is_none());
339 }
340
341 #[test]
342 fn to_base_add_of_base_exprs() {
343 let a = SymbolicExpressionExt::<F, EF>::from(F::new(3));
345 let b = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
346 BaseEntry::Main { offset: 0 },
347 0,
348 ));
349 let sum = a + b;
350 let lowered = sum.to_base();
351
352 match lowered {
353 Some(SymbolicExpr::Add {
354 x,
355 y,
356 degree_multiple,
357 }) => {
358 assert_eq!(degree_multiple, 1);
359 assert!(matches!(
360 x.as_ref(),
361 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if *c == F::new(3)
362 ));
363 assert!(matches!(
364 y.as_ref(),
365 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
366 if v.index == 0 && v.entry == BaseEntry::Main { offset: 0 }
367 ));
368 }
369 _ => panic!("Expected a lowered Add node"),
370 }
371 }
372
373 #[test]
374 fn to_base_add_with_ext_child_returns_none() {
375 let base = SymbolicExpressionExt::<F, EF>::from(F::new(3));
377 let ext_var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
378 ExtEntry::Permutation { offset: 0 },
379 0,
380 ));
381 let sum = base + ext_var;
382 assert!(sum.to_base().is_none());
383 }
384
385 #[test]
386 fn to_base_sub_of_base_exprs() {
387 let a = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
389 BaseEntry::Main { offset: 0 },
390 0,
391 ));
392 let b = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
393 BaseEntry::Main { offset: 0 },
394 1,
395 ));
396 let diff = a - b;
397 let lowered = diff.to_base();
398
399 match lowered {
400 Some(SymbolicExpr::Sub {
401 x,
402 y,
403 degree_multiple,
404 }) => {
405 assert_eq!(degree_multiple, 1);
406 assert!(matches!(
407 x.as_ref(),
408 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
409 if v.index == 0 && v.entry == BaseEntry::Main { offset: 0 }
410 ));
411 assert!(matches!(
412 y.as_ref(),
413 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
414 if v.index == 1 && v.entry == BaseEntry::Main { offset: 0 }
415 ));
416 }
417 _ => panic!("Expected a lowered Sub node"),
418 }
419 }
420
421 #[test]
422 fn to_base_sub_with_ext_child_returns_none() {
423 let base = SymbolicExpressionExt::<F, EF>::from(F::new(5));
425 let ext_var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
426 ExtEntry::Challenge,
427 0,
428 ));
429 let diff = base - ext_var;
430 assert!(diff.to_base().is_none());
431 }
432
433 #[test]
434 fn to_base_neg_of_base_expr() {
435 let var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
437 BaseEntry::Main { offset: 0 },
438 0,
439 ));
440 let neg = -var;
441 let lowered = neg.to_base();
442
443 match lowered {
444 Some(SymbolicExpr::Neg { x, degree_multiple }) => {
445 assert_eq!(degree_multiple, 1);
446 assert!(matches!(
447 x.as_ref(),
448 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
449 if v.index == 0 && v.entry == BaseEntry::Main { offset: 0 }
450 ));
451 }
452 _ => panic!("Expected a lowered Neg node"),
453 }
454 }
455
456 #[test]
457 fn to_base_mul_of_base_exprs() {
458 let a = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
460 BaseEntry::Main { offset: 0 },
461 0,
462 ));
463 let b = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
464 BaseEntry::Main { offset: 0 },
465 1,
466 ));
467 let prod = a * b;
468 let lowered = prod.to_base();
469
470 match lowered {
471 Some(SymbolicExpr::Mul {
472 x,
473 y,
474 degree_multiple,
475 }) => {
476 assert_eq!(degree_multiple, 2);
477 assert!(matches!(
478 x.as_ref(),
479 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
480 if v.index == 0 && v.entry == BaseEntry::Main { offset: 0 }
481 ));
482 assert!(matches!(
483 y.as_ref(),
484 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
485 if v.index == 1 && v.entry == BaseEntry::Main { offset: 0 }
486 ));
487 }
488 _ => panic!("Expected a lowered Mul node"),
489 }
490 }
491
492 #[test]
493 fn to_base_mul_with_ext_child_returns_none() {
494 let base = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
496 BaseEntry::Main { offset: 0 },
497 0,
498 ));
499 let ext_var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
500 ExtEntry::Permutation { offset: 0 },
501 0,
502 ));
503 let prod = base * ext_var;
504 assert!(prod.to_base().is_none());
505 }
506
507 #[test]
508 fn from_symbolic_expression() {
509 let base_expr = SymbolicExpression::from(F::new(99));
511 let ext_expr = SymbolicExpressionExt::<F, EF>::from(base_expr);
512 assert!(matches!(
513 ext_expr,
514 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Constant(c)))) if c == F::new(99)
515 ));
516 }
517
518 #[test]
519 fn from_symbolic_variable() {
520 let var = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 0 }, 2);
522 let ext_expr = SymbolicExpressionExt::<F, EF>::from(var);
523 assert!(matches!(
524 ext_expr,
525 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Variable(v))))
526 if v.index == 2 && v.entry == BaseEntry::Main { offset: 0 }
527 ));
528 }
529
530 #[test]
531 fn from_symbolic_variable_ext() {
532 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 1 }, 3);
534 let ext_expr = SymbolicExpressionExt::<F, EF>::from(var);
535 assert!(matches!(
536 ext_expr,
537 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
538 if v.index == 3 && v.entry == ExtEntry::Permutation { offset: 1 }
539 ));
540 }
541
542 #[test]
543 fn from_base_field() {
544 let ext_expr = SymbolicExpressionExt::<F, EF>::from(F::new(42));
546 assert!(matches!(
547 ext_expr,
548 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Constant(c)))) if c == F::new(42)
549 ));
550 }
551
552 #[test]
553 fn from_binomial_extension_field() {
554 let ef_val = EF::ONE + EF::ONE;
556 let ext_expr = SymbolicExpressionExt::<F, EF>::from(ef_val);
557 assert!(matches!(
558 ext_expr,
559 SymbolicExpr::Leaf(ExtLeaf::ExtConstant(c)) if c == ef_val
560 ));
561 }
562
563 #[test]
564 fn ext_add_constant_folding() {
565 let a = SymbolicExpressionExt::<F, EF>::from(F::new(3));
567 let b = SymbolicExpressionExt::<F, EF>::from(F::new(4));
568 let result = a + b;
569 assert!(matches!(
570 result,
571 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Constant(c)))) if c == F::new(7)
572 ));
573 }
574
575 #[test]
576 fn ext_sub_constant_folding() {
577 let a = SymbolicExpressionExt::<F, EF>::from(F::new(10));
579 let b = SymbolicExpressionExt::<F, EF>::from(F::new(4));
580 let result = a - b;
581 assert!(matches!(
582 result,
583 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Constant(c)))) if c == F::new(6)
584 ));
585 }
586
587 #[test]
588 fn ext_mul_constant_folding() {
589 let a = SymbolicExpressionExt::<F, EF>::from(F::new(3));
591 let b = SymbolicExpressionExt::<F, EF>::from(F::new(5));
592 let result = a * b;
593 assert!(matches!(
594 result,
595 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Constant(c)))) if c == F::new(15)
596 ));
597 }
598
599 #[test]
600 fn ext_add_variables_degree_tracking() {
601 let a = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
603 ExtEntry::Permutation { offset: 0 },
604 0,
605 ));
606 let b = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
607 ExtEntry::Permutation { offset: 0 },
608 1,
609 ));
610 let result = a + b;
611
612 match result {
613 SymbolicExpr::Add {
614 x,
615 y,
616 degree_multiple,
617 } => {
618 assert_eq!(degree_multiple, 1);
619 assert!(matches!(
620 x.as_ref(),
621 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
622 if v.index == 0 && v.entry == ExtEntry::Permutation { offset: 0 }
623 ));
624 assert!(matches!(
625 y.as_ref(),
626 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
627 if v.index == 1 && v.entry == ExtEntry::Permutation { offset: 0 }
628 ));
629 }
630 _ => panic!("Expected an Add node"),
631 }
632 }
633
634 #[test]
635 fn ext_mul_variables_degree_tracking() {
636 let a = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
638 ExtEntry::Permutation { offset: 0 },
639 0,
640 ));
641 let b = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
642 ExtEntry::Permutation { offset: 0 },
643 1,
644 ));
645 let result = a * b;
646
647 match result {
648 SymbolicExpr::Mul {
649 x,
650 y,
651 degree_multiple,
652 } => {
653 assert_eq!(degree_multiple, 2);
654 assert!(matches!(
655 x.as_ref(),
656 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
657 if v.index == 0 && v.entry == ExtEntry::Permutation { offset: 0 }
658 ));
659 assert!(matches!(
660 y.as_ref(),
661 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
662 if v.index == 1 && v.entry == ExtEntry::Permutation { offset: 0 }
663 ));
664 }
665 _ => panic!("Expected a Mul node"),
666 }
667 }
668
669 #[test]
670 fn ext_constant_zero_mul_folds_to_zero() {
671 let var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
673 ExtEntry::Permutation { offset: 0 },
674 0,
675 ));
676 let zero = SymbolicExpressionExt::<F, EF>::from(EF::ZERO);
677 let result = var * zero;
678 assert!(matches!(
679 result,
680 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Constant(c)))) if c == F::ZERO
681 ));
682 }
683
684 #[test]
685 fn ext_constant_one_mul_folds_to_identity() {
686 let var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
688 ExtEntry::Permutation { offset: 0 },
689 0,
690 ));
691 let one = SymbolicExpressionExt::<F, EF>::from(EF::ONE);
692 let result = var * one;
693 assert!(matches!(
694 result,
695 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
696 if v.index == 0 && v.entry == ExtEntry::Permutation { offset: 0 }
697 ));
698 }
699
700 #[test]
701 fn ext_constant_zero_add_folds_to_identity() {
702 let var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
704 ExtEntry::Permutation { offset: 0 },
705 0,
706 ));
707 let zero = SymbolicExpressionExt::<F, EF>::from(EF::ZERO);
708 let result = zero + var;
709 assert!(matches!(
710 result,
711 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
712 if v.index == 0 && v.entry == ExtEntry::Permutation { offset: 0 }
713 ));
714 }
715
716 #[test]
717 fn ext_constant_zero_sub_folds_to_neg() {
718 let var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
720 ExtEntry::Permutation { offset: 0 },
721 0,
722 ));
723 let zero = SymbolicExpressionExt::<F, EF>::from(EF::ZERO);
724 let result = zero - var;
725 match result {
726 SymbolicExpr::Neg { x, degree_multiple } => {
727 assert_eq!(degree_multiple, 1);
728 assert!(matches!(
729 x.as_ref(),
730 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
731 if v.index == 0 && v.entry == ExtEntry::Permutation { offset: 0 }
732 ));
733 }
734 _ => panic!("Expected a Neg node"),
735 }
736 }
737
738 #[test]
739 fn ext_constant_not_in_basefield_no_folding() {
740 let var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
742 ExtEntry::Permutation { offset: 0 },
743 0,
744 ));
745 let non_base = SymbolicExpressionExt::<F, EF>::from(EF::from_basis_coefficients_fn(|i| {
746 if i == 1 { F::ONE } else { F::ZERO }
747 }));
748 let result = var * non_base;
749 match result {
750 SymbolicExpr::Mul {
751 x,
752 y,
753 degree_multiple,
754 } => {
755 assert_eq!(degree_multiple, 1);
756 assert!(matches!(
757 x.as_ref(),
758 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
759 if v.index == 0 && v.entry == ExtEntry::Permutation { offset: 0 }
760 ));
761 assert!(matches!(
762 y.as_ref(),
763 SymbolicExpr::Leaf(ExtLeaf::ExtConstant(_))
764 ));
765 }
766 _ => panic!("Expected a Mul node since the constant is not in the base field"),
767 }
768 }
769
770 #[test]
771 fn ext_mixed_base_and_ext_arithmetic() {
772 let base_var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariable::<F>::new(
774 BaseEntry::Main { offset: 0 },
775 0,
776 ));
777 let ext_var = SymbolicExpressionExt::<F, EF>::from(SymbolicVariableExt::<F, EF>::new(
778 ExtEntry::Permutation { offset: 0 },
779 0,
780 ));
781 let result = base_var + ext_var;
782
783 match &result {
784 SymbolicExpr::Add {
785 x,
786 y,
787 degree_multiple,
788 } => {
789 assert_eq!(*degree_multiple, 1);
790 assert!(matches!(
791 x.as_ref(),
792 SymbolicExpr::Leaf(ExtLeaf::Base(SymbolicExpr::Leaf(BaseLeaf::Variable(v))))
793 if v.index == 0 && v.entry == BaseEntry::Main { offset: 0 }
794 ));
795 assert!(matches!(
796 y.as_ref(),
797 SymbolicExpr::Leaf(ExtLeaf::ExtVariable(v))
798 if v.index == 0 && v.entry == ExtEntry::Permutation { offset: 0 }
799 ));
800 }
801 _ => panic!("Expected an Add node"),
802 }
803
804 assert!(result.to_base().is_none());
806 }
807}