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