1use super::interner::CodeGenInterner;
2use crate::pallas_primitives::conway::{BigInt, PlutusData};
3use crate::uplc::{
4 ast::{Constant, Data, Name, NamedDeBruijn, Program, Term, Type},
5 builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER, INDICES_CONVERTER},
6 builtins::DefaultFunction,
7 machine::{cost_model::ExBudget, runtime::Compressable, value::from_pallas_bigint},
8};
9use alloc::{
10 rc::Rc,
11 string::{String, ToString},
12 vec::Vec,
13};
14use blst::{blst_p1, blst_p2};
15use core::{cmp::Ordering, iter, ops::Neg};
16use hashbrown::HashMap;
17use itertools::{FoldWhile, Itertools};
18use strum::IntoEnumIterator;
19
20#[derive(Eq, Hash, PartialEq, Clone, Debug, PartialOrd)]
21pub enum ScopePath {
22 FUNC,
23 ARG,
24}
25
26#[derive(Eq, Hash, PartialEq, Clone, Debug, Default, PartialOrd)]
27pub struct Scope {
28 scope: Vec<ScopePath>,
29}
30
31impl Scope {
32 pub fn new() -> Self {
33 Self { scope: vec![] }
34 }
35
36 pub fn push(&self, path: ScopePath) -> Self {
37 let mut new_scope = self.scope.clone();
38 new_scope.push(path);
39 Self { scope: new_scope }
40 }
41
42 pub fn pop(&self) -> Self {
43 let mut new_scope = self.scope.clone();
44 new_scope.pop();
45 Self { scope: new_scope }
46 }
47
48 pub fn common_ancestor(&self, other: &Scope) -> Self {
49 Self {
50 scope: self
51 .scope
52 .iter()
53 .zip(other.scope.iter())
54 .map_while(|(a, b)| if a == b { Some(a) } else { None })
55 .cloned()
56 .collect_vec(),
57 }
58 }
59
60 pub fn is_common_ancestor(&self, other: &Scope) -> bool {
61 self == &self.common_ancestor(other)
62 }
63
64 pub fn len(&self) -> usize {
65 self.scope.len()
66 }
67
68 pub fn is_empty(&self) -> bool {
69 self.len() == 0
70 }
71}
72
73pub struct IdGen {
74 id: usize,
75}
76
77impl IdGen {
78 pub fn new() -> Self {
79 Self { id: 0 }
80 }
81
82 pub fn next_id(&mut self) -> usize {
83 self.id += 1;
84 self.id
85 }
86}
87
88impl Default for IdGen {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94pub const NO_INLINE: &str = "__no_inline__";
95
96#[derive(PartialEq, PartialOrd, Default, Debug, Clone)]
97pub struct VarLookup {
98 found: bool,
99 occurrences: usize,
100 delays: usize,
101 no_inline: bool,
102}
103
104impl VarLookup {
105 pub fn new() -> Self {
106 Self {
107 found: false,
108 occurrences: 0,
109 delays: 0,
110 no_inline: false,
111 }
112 }
113
114 pub fn new_found() -> Self {
115 Self {
116 found: true,
117 occurrences: 1,
118 delays: 0,
119 no_inline: false,
120 }
121 }
122
123 pub fn combine(self, other: Self) -> Self {
124 Self {
125 found: self.found || other.found,
126 occurrences: self.occurrences + other.occurrences,
127 delays: self.delays + other.delays,
128 no_inline: self.no_inline || other.no_inline,
129 }
130 }
131
132 pub fn delay_if_found(self, delay_amount: usize) -> Self {
133 if self.found {
134 Self {
135 found: self.found,
136 occurrences: self.occurrences,
137 delays: self.delays + delay_amount,
138 no_inline: self.no_inline,
139 }
140 } else {
141 self
142 }
143 }
144
145 pub fn no_inline_if_found(self) -> Self {
146 if self.found {
147 Self {
148 found: self.found,
149 occurrences: self.occurrences,
150 delays: self.delays,
151 no_inline: true,
152 }
153 } else {
154 self
155 }
156 }
157}
158
159impl DefaultFunction {
160 pub fn is_order_agnostic_builtin(self) -> bool {
161 matches!(
162 self,
163 DefaultFunction::AddInteger
164 | DefaultFunction::MultiplyInteger
165 | DefaultFunction::EqualsInteger
166 | DefaultFunction::EqualsByteString
167 | DefaultFunction::EqualsString
168 | DefaultFunction::EqualsData
169 | DefaultFunction::Bls12_381_G1_Equal
170 | DefaultFunction::Bls12_381_G2_Equal
171 | DefaultFunction::Bls12_381_G1_Add
172 | DefaultFunction::Bls12_381_G2_Add
173 )
174 }
175 pub fn can_curry_builtin(self) -> bool {
178 matches!(
179 self,
180 DefaultFunction::AddInteger
181 | DefaultFunction::SubtractInteger
182 | DefaultFunction::MultiplyInteger
183 | DefaultFunction::DivideInteger
184 | DefaultFunction::ModInteger
185 | DefaultFunction::QuotientInteger
186 | DefaultFunction::RemainderInteger
187 | DefaultFunction::EqualsInteger
188 | DefaultFunction::EqualsByteString
189 | DefaultFunction::EqualsString
190 | DefaultFunction::EqualsData
191 | DefaultFunction::Bls12_381_G1_Equal
192 | DefaultFunction::Bls12_381_G2_Equal
193 | DefaultFunction::LessThanInteger
194 | DefaultFunction::LessThanEqualsInteger
195 | DefaultFunction::AppendByteString
196 | DefaultFunction::ConsByteString
197 | DefaultFunction::SliceByteString
198 | DefaultFunction::IndexByteString
199 | DefaultFunction::LessThanEqualsByteString
200 | DefaultFunction::LessThanByteString
201 | DefaultFunction::AppendString
202 | DefaultFunction::Bls12_381_G1_Add
203 | DefaultFunction::Bls12_381_G2_Add
204 | DefaultFunction::ConstrData
205 )
206 }
207
208 pub fn is_error_safe(self, arg_stack: &[&Term<Name>]) -> bool {
209 match self {
210 DefaultFunction::AddInteger
211 | DefaultFunction::SubtractInteger
212 | DefaultFunction::MultiplyInteger
213 | DefaultFunction::EqualsInteger
214 | DefaultFunction::LessThanInteger
215 | DefaultFunction::LessThanEqualsInteger
216 | DefaultFunction::IData => arg_stack.iter().all(|arg| {
217 if let Term::Constant(c) = arg {
218 matches!(c.as_ref(), Constant::Integer(_))
219 } else {
220 false
221 }
222 }),
223 DefaultFunction::DivideInteger
224 | DefaultFunction::ModInteger
225 | DefaultFunction::QuotientInteger
226 | DefaultFunction::RemainderInteger => arg_stack.iter().all(|arg| {
227 if let Term::Constant(c) = arg {
228 if let Constant::Integer(i) = c.as_ref() {
229 *i != 0.into()
230 } else {
231 false
232 }
233 } else {
234 false
235 }
236 }),
237 DefaultFunction::LengthOfByteString
238 | DefaultFunction::EqualsByteString
239 | DefaultFunction::AppendByteString
240 | DefaultFunction::LessThanEqualsByteString
241 | DefaultFunction::LessThanByteString
242 | DefaultFunction::BData => arg_stack.iter().all(|arg| {
243 if let Term::Constant(c) = arg {
244 matches!(c.as_ref(), Constant::ByteString(_))
245 } else {
246 false
247 }
248 }),
249
250 DefaultFunction::ConsByteString => {
251 if let (Term::Constant(c), Term::Constant(c2)) = (&arg_stack[0], &arg_stack[1]) {
252 if let (Constant::Integer(i), Constant::ByteString(_)) =
253 (c.as_ref(), c2.as_ref())
254 {
255 i >= &0.into() && i < &256.into()
256 } else {
257 false
258 }
259 } else {
260 false
261 }
262 }
263
264 DefaultFunction::SliceByteString => {
265 if let (Term::Constant(c), Term::Constant(c2), Term::Constant(c3)) =
266 (&arg_stack[0], &arg_stack[1], &arg_stack[2])
267 {
268 matches!(
269 (c.as_ref(), c2.as_ref(), c3.as_ref()),
270 (
271 Constant::Integer(_),
272 Constant::Integer(_),
273 Constant::ByteString(_)
274 )
275 )
276 } else {
277 false
278 }
279 }
280
281 DefaultFunction::IndexByteString => {
282 if let (Term::Constant(c), Term::Constant(c2)) = (&arg_stack[0], &arg_stack[1]) {
283 if let (Constant::ByteString(bs), Constant::Integer(i)) =
284 (c.as_ref(), c2.as_ref())
285 {
286 i >= &0.into() && i < &bs.len().into()
287 } else {
288 false
289 }
290 } else {
291 false
292 }
293 }
294
295 DefaultFunction::EqualsString
296 | DefaultFunction::AppendString
297 | DefaultFunction::EncodeUtf8 => arg_stack.iter().all(|arg| {
298 if let Term::Constant(c) = arg {
299 matches!(c.as_ref(), Constant::String(_))
300 } else {
301 false
302 }
303 }),
304
305 DefaultFunction::EqualsData | DefaultFunction::SerialiseData => {
306 arg_stack.iter().all(|arg| {
307 if let Term::Constant(c) = arg {
308 matches!(c.as_ref(), Constant::Data(_))
309 } else {
310 false
311 }
312 })
313 }
314
315 DefaultFunction::Bls12_381_G1_Equal | DefaultFunction::Bls12_381_G1_Add => {
316 arg_stack.iter().all(|arg| {
317 if let Term::Constant(c) = arg {
318 matches!(c.as_ref(), Constant::Bls12_381G1Element(_))
319 } else {
320 false
321 }
322 })
323 }
324
325 DefaultFunction::Bls12_381_G2_Equal | DefaultFunction::Bls12_381_G2_Add => {
326 arg_stack.iter().all(|arg| {
327 if let Term::Constant(c) = arg {
328 matches!(c.as_ref(), Constant::Bls12_381G2Element(_))
329 } else {
330 false
331 }
332 })
333 }
334
335 DefaultFunction::ConstrData => {
336 if let (Term::Constant(c), Term::Constant(c2)) = (&arg_stack[0], &arg_stack[1]) {
337 if let (Constant::Integer(i), Constant::ProtoList(Type::Data, _)) =
338 (c.as_ref(), c2.as_ref())
339 {
340 i >= &0.into()
341 } else {
342 false
343 }
344 } else {
345 false
346 }
347 }
348
349 _ => false,
350 }
351 }
352
353 pub fn wrapped_name(self) -> String {
354 format!("__{}_wrapped", self.aiken_name())
355 }
356}
357pub fn forceable_wrapped_names() -> Vec<String> {
358 DefaultFunction::iter()
359 .filter(|df| df.force_count() > 0)
360 .map(|df| df.wrapped_name())
361 .collect_vec()
362}
363
364#[derive(PartialEq, Clone, Debug)]
365pub enum BuiltinArgs {
366 TwoArgs {
367 fst: (usize, Term<Name>),
368 snd: Option<(usize, Term<Name>)>,
369 },
370 ThreeArgs {
371 fst: (usize, Term<Name>),
372 snd: Option<(usize, Term<Name>)>,
373 thd: Option<(usize, Term<Name>)>,
374 },
375 TwoArgsAnyOrder {
376 fst: (usize, Term<Name>),
377 snd: Option<(usize, Term<Name>)>,
378 },
379}
380
381impl BuiltinArgs {
382 fn args_from_arg_stack(stack: Vec<(usize, Term<Name>)>, func: DefaultFunction) -> Self {
383 let error_safe = false;
384
385 let mut ordered_arg_stack = stack.into_iter().sorted_by(|(_, arg1), (_, arg2)| {
386 if func.is_order_agnostic_builtin() {
388 if matches!(arg1, Term::Constant(_)) == matches!(arg2, Term::Constant(_)) {
389 Ordering::Equal
390 } else if matches!(arg1, Term::Constant(_)) {
391 Ordering::Less
392 } else {
393 Ordering::Greater
394 }
395 } else {
396 Ordering::Equal
397 }
398 });
399
400 if ordered_arg_stack.len() == 2 && func.is_order_agnostic_builtin() {
401 BuiltinArgs::TwoArgsAnyOrder {
404 fst: ordered_arg_stack.next().unwrap(),
405 snd: if error_safe {
406 ordered_arg_stack.next()
407 } else {
408 None
409 },
410 }
411 } else if ordered_arg_stack.len() == 2 {
412 BuiltinArgs::TwoArgs {
413 fst: ordered_arg_stack.next().unwrap(),
414 snd: if error_safe {
415 ordered_arg_stack.next()
416 } else {
417 None
418 },
419 }
420 } else {
421 BuiltinArgs::ThreeArgs {
422 fst: ordered_arg_stack.next().unwrap(),
423 snd: ordered_arg_stack.next(),
424 thd: if error_safe {
425 ordered_arg_stack.next()
426 } else {
427 None
428 },
429 }
430 }
431 }
432
433 fn args_to_curried_args(self, builtin: DefaultFunction) -> CurriedBuiltin {
434 let args = match self {
435 BuiltinArgs::TwoArgs { fst, snd } | BuiltinArgs::TwoArgsAnyOrder { fst, snd } => {
436 CurriedArgs::TwoArgs {
437 fst_args: vec![CurriedNode {
438 id: fst.0,
439 term: fst.1,
440 }],
441 snd_args: snd
442 .into_iter()
443 .map(|item| CurriedNode {
444 id: item.0,
445 term: item.1,
446 })
447 .collect_vec(),
448 }
449 }
450 BuiltinArgs::ThreeArgs { fst, snd, thd } => CurriedArgs::ThreeArgs {
451 fst_args: vec![CurriedNode {
452 id: fst.0,
453 term: fst.1,
454 }],
455 snd_args: snd
456 .into_iter()
457 .map(|item| CurriedNode {
458 id: item.0,
459 term: item.1,
460 })
461 .collect_vec(),
462 thd_args: thd
463 .into_iter()
464 .map(|item| CurriedNode {
465 id: item.0,
466 term: item.1,
467 })
468 .collect_vec(),
469 },
470 };
471
472 CurriedBuiltin {
473 func: builtin,
474 args,
475 }
476 }
477
478 pub fn get_id_args(self) -> Vec<UplcNode> {
479 match self {
480 BuiltinArgs::TwoArgs { fst, snd } | BuiltinArgs::TwoArgsAnyOrder { fst, snd } => {
481 iter::once(fst)
482 .chain(snd)
483 .map(|item| UplcNode {
484 applied_id: item.0,
485 curried_id: item.0,
486 term: item.1,
487 })
488 .collect_vec()
489 }
490 BuiltinArgs::ThreeArgs { fst, snd, thd } => iter::once(fst)
491 .chain(snd)
492 .chain(thd)
493 .map(|item| UplcNode {
494 applied_id: item.0,
495 curried_id: item.0,
496 term: item.1,
497 })
498 .collect_vec(),
499 }
500 }
501}
502
503#[derive(PartialEq, Clone, Debug)]
504pub struct CurriedNode {
505 id: usize,
506 term: Term<Name>,
507}
508
509#[derive(PartialEq, Clone, Debug)]
510pub struct UplcNode {
511 applied_id: usize,
512 curried_id: usize,
513 term: Term<Name>,
514}
515
516#[derive(Eq, Hash, PartialEq, Clone, Debug)]
517pub struct CurriedName {
518 func_name: String,
519 id_vec: Vec<usize>,
520}
521
522impl CurriedName {
523 pub fn len(&self) -> usize {
524 self.id_vec.len()
525 }
526
527 pub fn is_empty(&self) -> bool {
528 self.len() == 0
529 }
530}
531
532#[derive(PartialEq, Clone, Debug)]
533pub enum CurriedArgs {
534 TwoArgs {
535 fst_args: Vec<CurriedNode>,
536 snd_args: Vec<CurriedNode>,
537 },
538 ThreeArgs {
539 fst_args: Vec<CurriedNode>,
540 snd_args: Vec<CurriedNode>,
541 thd_args: Vec<CurriedNode>,
542 },
543}
544
545impl CurriedArgs {
546 pub fn merge_node_by_path(self, path: BuiltinArgs) -> Self {
547 match (self, path) {
548 (
549 CurriedArgs::TwoArgs {
550 mut fst_args,
551 mut snd_args,
552 },
553 BuiltinArgs::TwoArgs { fst, snd },
554 ) => {
555 let fst_args = match fst_args.iter_mut().find(|item| item.term == fst.1) {
556 None => {
557 fst_args.push(CurriedNode {
558 id: fst.0,
559 term: fst.1,
560 });
561 fst_args
562 }
563 _ => fst_args,
564 };
565
566 let snd_args = match snd_args.iter_mut().find(|item| match &snd {
567 Some(snd) => item.term == snd.1,
568 None => false,
569 }) {
570 None => snd_args
571 .into_iter()
572 .chain(snd.into_iter().map(|item| CurriedNode {
573 id: item.0,
574 term: item.1,
575 }))
576 .collect_vec(),
577 _ => snd_args,
578 };
579
580 CurriedArgs::TwoArgs { fst_args, snd_args }
581 }
582 (
583 CurriedArgs::TwoArgs {
584 mut fst_args,
585 mut snd_args,
586 },
587 BuiltinArgs::TwoArgsAnyOrder { mut fst, snd },
588 ) => {
589 let (switched, fst_args) = if fst_args.iter_mut().any(|item| item.term == fst.1) {
590 (false, fst_args)
591 } else if fst_args.iter_mut().any(|item| match &snd {
592 Some(snd) => item.term == snd.1,
593 None => false,
594 }) {
595 (true, fst_args)
596 } else {
597 fst_args.push(CurriedNode {
598 id: fst.0,
599 term: core::mem::replace(&mut fst.1, Term::Error.force()),
604 });
605
606 (false, fst_args)
607 };
608
609 let snd_args = if switched {
611 assert!(fst.1 != Term::Error.force());
612
613 if snd_args.iter_mut().any(|item| item.term == fst.1) {
614 snd_args
615 } else {
616 snd_args.push(CurriedNode {
617 id: fst.0,
618 term: fst.1,
619 });
620 snd_args
621 }
622 } else if snd_args.iter_mut().any(|item| match &snd {
623 Some(snd) => item.term == snd.1,
624 None => false,
625 }) {
626 snd_args
627 } else {
628 snd_args
629 .into_iter()
630 .chain(snd.into_iter().map(|item| CurriedNode {
631 id: item.0,
632 term: item.1,
633 }))
634 .collect_vec()
635 };
636
637 CurriedArgs::TwoArgs { fst_args, snd_args }
638 }
639 (
640 CurriedArgs::ThreeArgs {
641 mut fst_args,
642 mut snd_args,
643 mut thd_args,
644 },
645 BuiltinArgs::ThreeArgs { fst, snd, thd },
646 ) => {
647 let fst_args = match fst_args.iter_mut().find(|item| item.term == fst.1) {
648 None => {
649 fst_args.push(CurriedNode {
650 id: fst.0,
651 term: fst.1,
652 });
653 fst_args
654 }
655 _ => fst_args,
656 };
657
658 let snd_args = match snd_args.iter_mut().find(|item| match &snd {
659 Some(snd) => item.term == snd.1,
660 None => false,
661 }) {
662 None => snd_args
663 .into_iter()
664 .chain(snd.into_iter().map(|item| CurriedNode {
665 id: item.0,
666 term: item.1,
667 }))
668 .collect_vec(),
669
670 _ => snd_args,
671 };
672
673 let thd_args = match thd_args.iter_mut().find(|item| match &thd {
674 Some(thd) => item.term == thd.1,
675 None => false,
676 }) {
677 None => thd_args
678 .into_iter()
679 .chain(thd.into_iter().map(|item| CurriedNode {
680 id: item.0,
681 term: item.1,
682 }))
683 .collect_vec(),
684
685 _ => thd_args,
686 };
687
688 CurriedArgs::ThreeArgs {
689 fst_args,
690 snd_args,
691 thd_args,
692 }
693 }
694 _ => unreachable!(),
695 }
696 }
697
698 fn get_id_args(&self, path: BuiltinArgs) -> Option<Vec<UplcNode>> {
700 match (self, path) {
701 (CurriedArgs::TwoArgs { fst_args, snd_args }, BuiltinArgs::TwoArgs { fst, snd }) => {
702 let arg = fst_args.iter().find(|item| fst.1 == item.term)?;
703
704 let Some(arg2) = snd_args.iter().find(|item| match &snd {
705 Some(snd) => item.term == snd.1,
706 None => false,
707 }) else {
708 return Some(vec![UplcNode {
709 applied_id: fst.0,
710 curried_id: arg.id,
711 term: arg.term.clone(),
712 }]);
713 };
714
715 Some(vec![
716 UplcNode {
717 applied_id: fst.0,
718 curried_id: arg.id,
719 term: arg.term.clone(),
720 },
721 UplcNode {
722 applied_id: snd.as_ref().unwrap().0,
723 curried_id: arg2.id,
724 term: arg2.term.clone(),
725 },
726 ])
727 }
728 (
729 CurriedArgs::TwoArgs { fst_args, snd_args },
730 BuiltinArgs::TwoArgsAnyOrder { fst, snd },
731 ) => {
732 let mut id_vec = vec![];
733
734 if let Some(arg) = fst_args.iter().find(|item| item.term == fst.1) {
735 id_vec.push(UplcNode {
736 applied_id: fst.0,
737 curried_id: arg.id,
738 term: arg.term.clone(),
739 });
740
741 let Some(arg2) = snd_args.iter().find(|item| match &snd {
742 Some(snd) => snd.1 == item.term,
743 None => false,
744 }) else {
745 return Some(id_vec);
746 };
747
748 id_vec.push(UplcNode {
749 applied_id: snd.as_ref().unwrap().0,
750 curried_id: arg2.id,
751 term: arg2.term.clone(),
752 });
753
754 Some(id_vec)
755 } else if let Some(arg) = fst_args.iter().find(|item| match &snd {
756 Some(snd) => item.term == snd.1,
757 None => false,
758 }) {
759 id_vec.push(UplcNode {
760 applied_id: snd.as_ref().unwrap().0,
761 curried_id: arg.id,
762 term: arg.term.clone(),
763 });
764
765 let Some(arg2) = snd_args.iter().find(|item| item.term == fst.1) else {
766 return Some(id_vec);
767 };
768
769 id_vec.push(UplcNode {
770 applied_id: fst.0,
771 curried_id: arg2.id,
772 term: arg2.term.clone(),
773 });
774
775 Some(id_vec)
776 } else {
777 None
778 }
779 }
780
781 (
782 CurriedArgs::ThreeArgs {
783 fst_args,
784 snd_args,
785 thd_args,
786 },
787 BuiltinArgs::ThreeArgs { fst, snd, thd },
788 ) => {
789 let arg = fst_args.iter().find(|item| fst.1 == item.term)?;
790
791 let Some(arg2) = snd_args.iter().find(|item| match &snd {
792 Some(snd) => item.term == snd.1,
793 None => false,
794 }) else {
795 return Some(vec![UplcNode {
796 applied_id: fst.0,
797 curried_id: arg.id,
798 term: arg.term.clone(),
799 }]);
800 };
801
802 let Some(arg3) = thd_args.iter().find(|item| match &thd {
803 Some(thd) => item.term == thd.1,
804 None => false,
805 }) else {
806 return Some(vec![
807 UplcNode {
808 applied_id: fst.0,
809 curried_id: arg.id,
810 term: arg.term.clone(),
811 },
812 UplcNode {
813 applied_id: snd.as_ref().unwrap().0,
814 curried_id: arg2.id,
815 term: arg2.term.clone(),
816 },
817 ]);
818 };
819
820 Some(vec![
821 UplcNode {
822 applied_id: fst.0,
823 curried_id: arg.id,
824 term: arg.term.clone(),
825 },
826 UplcNode {
827 applied_id: snd.as_ref().unwrap().0,
828 curried_id: arg2.id,
829 term: arg2.term.clone(),
830 },
831 UplcNode {
832 applied_id: thd.as_ref().unwrap().0,
833 curried_id: arg3.id,
834 term: arg3.term.clone(),
835 },
836 ])
837 }
838 _ => unreachable!(),
839 }
840 }
841
842 fn is_flipped(&self, path: &BuiltinArgs) -> bool {
843 match (self, path) {
844 (CurriedArgs::TwoArgs { fst_args, .. }, BuiltinArgs::TwoArgsAnyOrder { fst, snd }) => {
845 if fst_args.iter().any(|item| item.term == fst.1) {
846 false
847 } else {
848 fst_args.iter().any(|item| match &snd {
849 Some(snd) => item.term == snd.1,
850 None => false,
851 })
852 }
853 }
854 _ => false,
855 }
856 }
857}
858#[derive(PartialEq, Clone, Debug)]
859pub struct CurriedBuiltin {
860 pub func: DefaultFunction,
861 pub args: CurriedArgs,
864}
865
866impl CurriedBuiltin {
867 pub fn merge_node_by_path(self, path: BuiltinArgs) -> Self {
868 Self {
869 func: self.func,
870 args: self.args.merge_node_by_path(path),
871 }
872 }
873
874 pub fn get_id_args(&self, path: BuiltinArgs) -> Option<Vec<UplcNode>> {
875 self.args.get_id_args(path)
876 }
877
878 pub fn is_flipped(&self, path: &BuiltinArgs) -> bool {
879 self.args.is_flipped(path)
880 }
881}
882
883#[derive(Debug, Clone)]
884pub struct Context {
885 pub inlined_apply_ids: Vec<usize>,
886 pub constants_to_flip: Vec<usize>,
887 pub write_bits_indices_arg: Vec<usize>,
888 pub builtins_map: HashMap<DefaultFunction, ()>,
889 pub blst_p1_list: Vec<blst_p1>,
890 pub blst_p2_list: Vec<blst_p2>,
891 pub write_bits_convert: bool,
892 pub node_count: usize,
893}
894
895#[derive(Clone, Debug)]
896pub enum Args {
897 Force(usize),
898 Apply(usize, Term<Name>),
899}
900
901impl Term<Name> {
902 fn traverse_uplc_with_helper(
903 &mut self,
904 scope: &Scope,
905 mut arg_stack: Vec<Args>,
906 id_gen: &mut IdGen,
907 with: &mut impl FnMut(Option<usize>, &mut Term<Name>, Vec<Args>, &Scope, &mut Context),
908 context: &mut Context,
909 inline_lambda: bool,
910 ) {
911 match self {
912 Term::Apply { function, argument } => {
913 let arg = Rc::make_mut(argument);
914
915 arg.traverse_uplc_with_helper(
916 &scope.push(ScopePath::ARG),
917 vec![],
918 id_gen,
919 with,
920 context,
921 inline_lambda,
922 );
923 let apply_id = id_gen.next_id();
924
925 arg_stack.push(Args::Apply(apply_id, arg.clone()));
927
928 let func = Rc::make_mut(function);
929
930 func.traverse_uplc_with_helper(
931 &scope.push(ScopePath::FUNC),
932 arg_stack,
933 id_gen,
934 with,
935 context,
936 inline_lambda,
937 );
938
939 with(Some(apply_id), self, vec![], scope, context);
940 }
941 Term::Force(f) => {
942 let f = Rc::make_mut(f);
943 let force_id = id_gen.next_id();
944
945 arg_stack.push(Args::Force(force_id));
946
947 f.traverse_uplc_with_helper(scope, arg_stack, id_gen, with, context, inline_lambda);
948
949 with(Some(force_id), self, vec![], scope, context);
950 }
951 Term::Delay(d) => {
952 let d = Rc::make_mut(d);
953 let delay_arg = arg_stack
954 .pop()
955 .map(|arg| {
956 assert!(matches!(arg, Args::Force(_)));
957 vec![arg]
958 })
959 .unwrap_or_default();
960
961 d.traverse_uplc_with_helper(scope, arg_stack, id_gen, with, context, inline_lambda);
962
963 with(None, self, delay_arg, scope, context);
964 }
965 Term::Lambda {
966 parameter_name,
967 body,
968 } => {
969 let p = parameter_name.clone();
970
971 let args = if parameter_name.text == NO_INLINE {
974 vec![]
975 } else {
976 arg_stack
977 .pop()
978 .map(|arg| {
979 assert!(matches!(arg, Args::Apply(_, _)));
980 vec![arg]
981 })
982 .unwrap_or_default()
983 };
984
985 if inline_lambda {
986 with(None, self, args, scope, context);
990
991 match self {
992 Term::Lambda {
993 parameter_name,
994 body,
995 } if *parameter_name == p => {
996 let body = Rc::make_mut(body);
997 body.traverse_uplc_with_helper(
998 scope,
999 arg_stack,
1000 id_gen,
1001 with,
1002 context,
1003 inline_lambda,
1004 );
1005 }
1006 other => other.traverse_uplc_with_helper(
1007 scope,
1008 arg_stack,
1009 id_gen,
1010 with,
1011 context,
1012 inline_lambda,
1013 ),
1014 }
1015 } else {
1016 let body = Rc::make_mut(body);
1017
1018 body.traverse_uplc_with_helper(
1019 scope,
1020 arg_stack,
1021 id_gen,
1022 with,
1023 context,
1024 inline_lambda,
1025 );
1026
1027 with(None, self, args, scope, context);
1028 }
1029 }
1030
1031 Term::Case { constr, branches } => {
1032 let constr = Rc::make_mut(constr);
1033 constr.traverse_uplc_with_helper(
1034 scope,
1035 vec![],
1036 id_gen,
1037 with,
1038 context,
1039 inline_lambda,
1040 );
1041
1042 if branches.len() == 1 {
1043 branches[0].traverse_uplc_with_helper(
1046 scope,
1047 arg_stack,
1048 id_gen,
1049 with,
1050 context,
1051 inline_lambda,
1052 );
1053 } else {
1054 for branch in branches {
1055 branch.traverse_uplc_with_helper(
1056 scope,
1057 arg_stack.clone(),
1058 id_gen,
1059 with,
1060 context,
1061 inline_lambda,
1062 );
1063 }
1064 }
1065 }
1066 Term::Constr { fields, .. } => {
1067 for field in fields {
1068 field.traverse_uplc_with_helper(
1069 scope,
1070 vec![],
1071 id_gen,
1072 with,
1073 context,
1074 inline_lambda,
1075 );
1076 }
1077 }
1078
1079 Term::Builtin(func) => {
1080 let mut args = vec![];
1081
1082 for _ in 0..(func.arity() + usize::try_from(func.force_count()).unwrap()) {
1083 if let Some(arg) = arg_stack.pop() {
1084 args.push(arg);
1085 }
1086 }
1087 with(None, self, args, scope, context);
1089 }
1090 term => {
1091 with(None, term, vec![], scope, context);
1092 }
1093 }
1094 context.node_count += 1;
1095 }
1096
1097 fn substitute_var(&mut self, original: Rc<Name>, replace_with: &Term<Name>) {
1098 match self {
1099 Term::Var(name) if *name == original => {
1100 *self = replace_with.clone();
1101 }
1102 Term::Delay(body) => Rc::make_mut(body).substitute_var(original, replace_with),
1103 Term::Lambda {
1104 parameter_name,
1105 body,
1106 } if *parameter_name != original => {
1107 Rc::make_mut(body).substitute_var(original, replace_with);
1108 }
1109 Term::Apply { function, argument } => {
1110 Rc::make_mut(function).substitute_var(original.clone(), replace_with);
1111 Rc::make_mut(argument).substitute_var(original, replace_with);
1112 }
1113 Term::Force(f) => {
1114 Rc::make_mut(f).substitute_var(original, replace_with);
1115 }
1116 Term::Case { .. } => todo!(),
1117 Term::Constr { .. } => todo!(),
1118 _ => (),
1119 }
1120 }
1121
1122 fn replace_identity_usage(&mut self, original: Rc<Name>) {
1123 match self {
1124 Term::Delay(body) => {
1125 Rc::make_mut(body).replace_identity_usage(original.clone());
1126 }
1127 Term::Lambda {
1128 parameter_name,
1129 body,
1130 } => {
1131 if *parameter_name != original {
1132 Rc::make_mut(body).replace_identity_usage(original.clone());
1133 }
1134 }
1135 Term::Apply { function, argument } => {
1136 let func = Rc::make_mut(function);
1137 let arg = Rc::make_mut(argument);
1138
1139 func.replace_identity_usage(original.clone());
1140 arg.replace_identity_usage(original.clone());
1141
1142 let Term::Var(name) = &func else {
1143 return;
1144 };
1145
1146 if *name == original {
1147 *self = core::mem::replace(arg, Term::Error.force());
1148 }
1149 }
1150 Term::Force(f) => {
1151 Rc::make_mut(f).replace_identity_usage(original.clone());
1152 }
1153 Term::Case { .. } => todo!(),
1154 Term::Constr { .. } => todo!(),
1155 _ => (),
1156 }
1157 }
1158
1159 fn var_occurrences(
1160 &self,
1161 search_for: Rc<Name>,
1162 mut arg_stack: Vec<()>,
1163 mut force_stack: Vec<()>,
1164 ) -> VarLookup {
1165 match self {
1166 Term::Var(name) => {
1167 if *name == search_for {
1168 VarLookup::new_found()
1169 } else {
1170 VarLookup::new()
1171 }
1172 }
1173 Term::Delay(body) => {
1174 let not_forced = usize::from(force_stack.pop().is_none());
1175
1176 body.var_occurrences(search_for, arg_stack, force_stack)
1177 .delay_if_found(not_forced)
1178 }
1179 Term::Lambda {
1180 parameter_name,
1181 body,
1182 } => {
1183 if parameter_name.text == NO_INLINE {
1184 body.var_occurrences(search_for, arg_stack, force_stack)
1185 .no_inline_if_found()
1186 } else if *parameter_name == search_for {
1187 VarLookup::new()
1188 } else {
1189 let not_applied = usize::from(arg_stack.pop().is_none());
1190 body.var_occurrences(search_for, arg_stack, force_stack)
1191 .delay_if_found(not_applied)
1192 }
1193 }
1194 Term::Apply { function, argument } => {
1195 arg_stack.push(());
1197
1198 let apply_var_occurrence_stack = |term: &Term<Name>, arg_stack: Vec<()>| {
1199 term.var_occurrences(search_for.clone(), arg_stack, force_stack)
1200 };
1201
1202 let apply_var_occurrence_no_stack =
1203 |term: &Term<Name>| term.var_occurrences(search_for.clone(), vec![], vec![]);
1204
1205 if let Term::Apply {
1206 function: next_func,
1207 argument: next_arg,
1208 } = function.as_ref()
1209 {
1210 arg_stack.push(());
1212 next_func.carry_args_to_branch(
1213 next_arg,
1214 argument,
1215 arg_stack,
1216 apply_var_occurrence_stack,
1217 apply_var_occurrence_no_stack,
1218 )
1219 } else {
1220 apply_var_occurrence_stack(function, arg_stack)
1221 .combine(apply_var_occurrence_no_stack(argument))
1222 }
1223 }
1224 Term::Force(x) => {
1225 force_stack.push(());
1226 x.var_occurrences(search_for, arg_stack, force_stack)
1227 }
1228 Term::Case { .. } => todo!(),
1229 Term::Constr { .. } => todo!(),
1230 _ => VarLookup::new(),
1231 }
1232 }
1233
1234 fn carry_args_to_branch(
1239 &self,
1240 then_arg: &Rc<Term<Name>>,
1241 else_arg: &Rc<Term<Name>>,
1242 mut arg_stack: Vec<()>,
1243 var_occurrence_stack: impl FnOnce(&Term<Name>, Vec<()>) -> VarLookup,
1244 var_occurrence_no_stack: impl Fn(&Term<Name>) -> VarLookup,
1245 ) -> VarLookup {
1246 let Term::Apply {
1247 function: builtin,
1248 argument: condition,
1249 } = self
1250 else {
1251 return var_occurrence_stack(self, arg_stack)
1252 .combine(var_occurrence_no_stack(then_arg))
1253 .combine(var_occurrence_no_stack(else_arg));
1254 };
1255
1256 arg_stack.push(());
1258
1259 let Term::Delay(else_arg) = else_arg.as_ref() else {
1260 return var_occurrence_stack(builtin, arg_stack)
1261 .combine(var_occurrence_no_stack(condition))
1262 .combine(var_occurrence_no_stack(then_arg))
1263 .combine(var_occurrence_no_stack(else_arg));
1264 };
1265
1266 let Term::Delay(then_arg) = then_arg.as_ref() else {
1267 return var_occurrence_stack(builtin, arg_stack)
1268 .combine(var_occurrence_no_stack(condition))
1269 .combine(var_occurrence_no_stack(then_arg))
1270 .combine(var_occurrence_no_stack(else_arg));
1271 };
1272
1273 match builtin.as_ref() {
1274 Term::Var(a)
1275 if a.text == DefaultFunction::IfThenElse.wrapped_name()
1276 || a.text == DefaultFunction::ChooseList.wrapped_name() =>
1277 {
1278 if matches!(else_arg.as_ref(), Term::Error) {
1279 arg_stack.pop();
1281 arg_stack.pop();
1282 arg_stack.pop();
1283
1284 var_occurrence_no_stack(condition)
1285 .combine(var_occurrence_stack(then_arg, arg_stack))
1286 } else if matches!(then_arg.as_ref(), Term::Error) {
1287 arg_stack.pop();
1289 arg_stack.pop();
1290 arg_stack.pop();
1291
1292 var_occurrence_no_stack(condition)
1293 .combine(var_occurrence_stack(else_arg, arg_stack))
1294 } else {
1295 var_occurrence_stack(builtin, arg_stack)
1296 .combine(var_occurrence_no_stack(condition))
1297 .combine(var_occurrence_no_stack(then_arg))
1298 .combine(var_occurrence_no_stack(else_arg))
1299 }
1300 }
1301
1302 _ => var_occurrence_stack(builtin, arg_stack)
1303 .combine(var_occurrence_no_stack(condition))
1304 .combine(var_occurrence_no_stack(then_arg))
1305 .combine(var_occurrence_no_stack(else_arg)),
1306 }
1307 }
1308
1309 fn lambda_reducer(
1310 &mut self,
1311 _id: Option<usize>,
1312 mut arg_stack: Vec<Args>,
1313 _scope: &Scope,
1314 context: &mut Context,
1315 ) -> bool {
1316 let mut changed = false;
1317 match self {
1318 Term::Lambda {
1319 parameter_name,
1320 body,
1321 } => {
1322 if let Some(Args::Apply(arg_id, arg_term)) = arg_stack.pop() {
1324 let replace = match &arg_term {
1325 Term::Constant(c) if matches!(c.as_ref(), Constant::String(_)) => false,
1327 Term::Delay(e) if matches!(e.as_ref(), Term::Error) => true,
1330 Term::Lambda { .. } => arg_term.is_a_builtin_wrapper(),
1332 Term::Constant(_) | Term::Var(_) | Term::Builtin(_) => true,
1334
1335 _ => false,
1336 };
1337 changed = replace;
1338
1339 if replace {
1340 let body = Rc::make_mut(body);
1341 context.inlined_apply_ids.push(arg_id);
1342
1343 body.substitute_var(
1344 parameter_name.clone(),
1345 arg_term.pierce_no_inlines_ref(),
1346 );
1347 *self = core::mem::replace(body, Term::Error.force());
1349 }
1350 }
1351 }
1352
1353 Term::Case { .. } => todo!(),
1354 Term::Constr { .. } => todo!(),
1355 _ => (),
1356 };
1357
1358 changed
1359 }
1360
1361 fn builtin_force_reducer(
1363 &mut self,
1364 _id: Option<usize>,
1365 mut arg_stack: Vec<Args>,
1366 _scope: &Scope,
1367 context: &mut Context,
1368 ) {
1369 if let Term::Builtin(func) = self {
1370 arg_stack.reverse();
1371 let has_forces = func.force_count() > 0;
1372 while let Some(Args::Force(id)) = arg_stack.pop() {
1373 context.inlined_apply_ids.push(id);
1374 }
1375
1376 if has_forces {
1377 context.builtins_map.insert(*func, ());
1378 *self = Term::var(func.wrapped_name());
1379 }
1380 }
1381 }
1382
1383 fn bls381_compressor(
1385 &mut self,
1386 _id: Option<usize>,
1387 _arg_stack: Vec<Args>,
1388 _scope: &Scope,
1389 context: &mut Context,
1390 ) {
1391 if let Term::Constant(con) = self {
1392 match con.as_ref() {
1393 Constant::Bls12_381G1Element(blst_p1) => {
1394 if let Some(index) = context
1395 .blst_p1_list
1396 .iter()
1397 .position(|item| item == blst_p1.as_ref())
1398 {
1399 *self = Term::var(format!("blst_p1_index_{}", index));
1400 } else {
1401 context.blst_p1_list.push(*blst_p1.as_ref());
1402 *self =
1403 Term::var(format!("blst_p1_index_{}", context.blst_p1_list.len() - 1));
1404 }
1405 }
1406 Constant::Bls12_381G2Element(blst_p2) => {
1407 if let Some(index) = context
1408 .blst_p2_list
1409 .iter()
1410 .position(|item| item == blst_p2.as_ref())
1411 {
1412 *self = Term::var(format!("blst_p2_index_{}", index));
1413 } else {
1414 context.blst_p2_list.push(*blst_p2.as_ref());
1415 *self =
1416 Term::var(format!("blst_p2_index_{}", context.blst_p2_list.len() - 1));
1417 }
1418 }
1419 _ => (),
1420 }
1421 }
1422 }
1423
1424 fn split_body_lambda(&mut self) {
1429 let mut arg_stack = vec![];
1430 let mut current_term = &mut core::mem::replace(self, Term::Error.force());
1431 let mut unsat_lams = vec![];
1432
1433 let mut function_groups = vec![vec![]];
1434 let mut function_dependencies = vec![vec![]];
1435
1436 loop {
1437 match current_term {
1438 Term::Apply { function, argument } => {
1439 current_term = Rc::make_mut(function);
1440
1441 let arg = Rc::make_mut(argument);
1442
1443 arg.split_body_lambda();
1444
1445 arg_stack.push(Args::Apply(0, core::mem::replace(arg, Term::Error.force())));
1446 }
1447 Term::Lambda {
1448 parameter_name,
1449 body,
1450 } => {
1451 current_term = Rc::make_mut(body);
1452
1453 if let Some(Args::Apply(_, arg)) = arg_stack.pop() {
1454 let names = arg.get_var_names();
1455
1456 let func = (parameter_name.clone(), arg);
1457
1458 if let Some((position, _)) =
1459 function_groups.iter().enumerate().rfind(|named_functions| {
1460 named_functions
1461 .1
1462 .iter()
1463 .any(|(name, _)| names.contains(name))
1464 })
1465 {
1466 let insert_position = position + 1;
1467 if insert_position == function_groups.len() {
1468 function_groups.push(vec![func]);
1469 function_dependencies.push(names);
1470 } else {
1471 function_groups[insert_position].push(func);
1472 function_dependencies[insert_position].extend(names);
1473 }
1474 } else {
1475 function_groups[0].push(func);
1476 function_dependencies[0].extend(names);
1477 }
1478 } else {
1479 unsat_lams.push(parameter_name.clone());
1480 }
1481 }
1482 Term::Force(term) => {
1483 current_term = Rc::make_mut(term);
1484
1485 arg_stack.push(Args::Force(0));
1486 }
1487 Term::Delay(term) => {
1488 Rc::make_mut(term).split_body_lambda();
1489 break;
1490 }
1491 Term::Case { .. } => todo!(),
1492 Term::Constr { .. } => todo!(),
1493 _ => break,
1494 }
1495 }
1496 let mut swap_postions = vec![];
1497
1498 function_groups
1499 .iter()
1500 .enumerate()
1501 .for_each(|(group_index, group)| {
1502 if group.len() <= 3 {
1503 group
1504 .iter()
1505 .enumerate()
1506 .rev()
1507 .for_each(|(item_index, (item_name, _))| {
1508 let current_eligible_position = function_dependencies
1509 .iter()
1510 .enumerate()
1511 .fold_while(group_index, |acc, (new_position, dependencies)| {
1512 if dependencies.contains(item_name) {
1513 FoldWhile::Done(acc)
1514 } else {
1515 FoldWhile::Continue(new_position)
1516 }
1517 })
1518 .into_inner();
1519
1520 if current_eligible_position > group_index {
1521 swap_postions.push((
1522 group_index,
1523 item_index,
1524 current_eligible_position,
1525 ));
1526 }
1527 });
1528 }
1529 });
1530
1531 for (group_index, item_index, swap_index) in swap_postions {
1532 let item = function_groups[group_index].remove(item_index);
1533
1534 function_groups[swap_index].push(item);
1535 }
1536
1537 let term_to_build_on = core::mem::replace(current_term, Term::Error.force());
1538
1539 let term = arg_stack
1541 .into_iter()
1542 .rfold(term_to_build_on, |term, arg| match arg {
1543 Args::Force(_) => term.force(),
1544 Args::Apply(_, arg) => term.apply(arg),
1545 });
1546
1547 let term = function_groups.into_iter().rfold(term, |term, group| {
1548 let term = group.iter().rfold(term, |term, (name, _)| Term::Lambda {
1549 parameter_name: name.clone(),
1550 body: term.into(),
1551 });
1552
1553 group
1554 .into_iter()
1555 .fold(term, |term, (_, arg)| term.apply(arg))
1556 });
1557
1558 let term = unsat_lams
1559 .into_iter()
1560 .rfold(term, |term, name| Term::Lambda {
1561 parameter_name: name.clone(),
1562 body: term.into(),
1563 });
1564
1565 *self = term;
1566 }
1567
1568 fn get_var_names(&self) -> Vec<Rc<Name>> {
1569 let mut names = vec![];
1570
1571 let mut term = self;
1572
1573 loop {
1574 match term {
1575 Term::Apply { function, argument } => {
1576 let arg_names = argument.get_var_names();
1577
1578 names.extend(arg_names);
1579
1580 term = function;
1581 }
1582 Term::Var(name) => {
1583 names.push(name.clone());
1584 break;
1585 }
1586 Term::Delay(t) => {
1587 term = t;
1588 }
1589 Term::Lambda { body, .. } => {
1590 term = body;
1591 }
1592 Term::Constant(_) | Term::Error | Term::Builtin(_) => {
1593 break;
1594 }
1595 Term::Force(t) => {
1596 term = t;
1597 }
1598 Term::Constr { .. } => todo!(),
1599 Term::Case { .. } => todo!(),
1600 }
1601 }
1602
1603 names
1604 }
1605
1606 fn case_constr_apply_reducer(
1608 &mut self,
1609 _id: Option<usize>,
1610 _arg_stack: Vec<Args>,
1611 _scope: &Scope,
1612 _context: &mut Context,
1613 ) {
1614 let mut term = &mut core::mem::replace(self, Term::Error.force());
1615
1616 let mut arg_vec = vec![];
1617
1618 while let Term::Apply { function, argument } = term {
1619 arg_vec.push(Rc::make_mut(argument));
1620
1621 term = Rc::make_mut(function);
1622 }
1623
1624 arg_vec.reverse();
1625
1626 match term {
1627 Term::Case { constr, branches }
1628 if branches.len() == 1 && matches!(constr.as_ref(), Term::Constr { .. }) =>
1629 {
1630 let Term::Constr { fields, .. } = Rc::make_mut(constr) else {
1631 unreachable!();
1632 };
1633
1634 for arg in arg_vec {
1635 fields.push(core::mem::replace(arg, Term::Error.force()));
1636 }
1637
1638 *self = core::mem::replace(term, Term::Error.force());
1639 }
1640 _ => {
1641 if arg_vec.len() > 2 {
1642 let mut fields = vec![];
1643
1644 for arg in arg_vec {
1645 fields.push(core::mem::replace(arg, Term::Error.force()));
1646 }
1647
1648 *self = Term::constr(0, fields)
1649 .case(vec![core::mem::replace(term, Term::Error.force())]);
1650 } else {
1651 for arg in arg_vec {
1652 *term = (core::mem::replace(term, Term::Error.force()))
1653 .apply(core::mem::replace(arg, Term::Error.force()));
1654 }
1655
1656 *self = core::mem::replace(term, Term::Error.force());
1657 }
1658 }
1659 }
1660 }
1661 fn write_bits_convert_arg(
1665 &mut self,
1666 id: Option<usize>,
1667 mut arg_stack: Vec<Args>,
1668 _scope: &Scope,
1669 context: &mut Context,
1670 ) {
1671 match self {
1672 Term::Apply { argument, .. } => {
1673 let id = id.unwrap();
1674
1675 if context.write_bits_indices_arg.contains(&id) {
1676 match Rc::make_mut(argument) {
1677 Term::Constant(constant) => {
1678 let Constant::ProtoList(tipo, items) = Rc::make_mut(constant) else {
1679 unreachable!();
1680 };
1681
1682 assert!(*tipo == Type::Data);
1683 *tipo = Type::Integer;
1684
1685 for item in items {
1686 let Constant::Data(PlutusData::BigInt(i)) = item else {
1687 unreachable!();
1688 };
1689
1690 *item = Constant::Integer(from_pallas_bigint(i));
1691 }
1692 }
1693 arg => {
1694 context.write_bits_convert = true;
1695
1696 *arg = Term::var(INDICES_CONVERTER)
1697 .apply(core::mem::replace(arg, Term::Error.force()));
1698 }
1699 }
1700 }
1701 }
1702
1703 Term::Builtin(DefaultFunction::WriteBits) => {
1704 if arg_stack.is_empty() {
1705 context.write_bits_convert = true;
1706
1707 *self = Term::write_bits()
1708 .apply(Term::var("__arg_1"))
1709 .apply(Term::var(INDICES_CONVERTER).apply(Term::var("__arg_2")))
1710 .apply(Term::var("__arg_3"))
1711 .lambda("__arg_3")
1712 .lambda("__arg_2")
1713 .lambda("__arg_1")
1714 } else {
1715 arg_stack.pop();
1717
1718 let Some(Args::Apply(arg_id, _)) = arg_stack.pop() else {
1719 return;
1720 };
1721
1722 context.write_bits_indices_arg.push(arg_id);
1723 }
1724 }
1725 _ => (),
1726 }
1727 }
1728
1729 fn identity_reducer(
1730 &mut self,
1731 _id: Option<usize>,
1732 mut arg_stack: Vec<Args>,
1733 _scope: &Scope,
1734 context: &mut Context,
1735 ) -> bool {
1736 let mut changed = false;
1737
1738 match self {
1739 Term::Lambda {
1740 parameter_name,
1741 body,
1742 } => {
1743 let body = Rc::make_mut(body);
1744 let Some(Args::Apply(arg_id, identity_func)) = arg_stack.pop() else {
1747 return false;
1748 };
1749
1750 let Term::Lambda {
1751 parameter_name: identity_name,
1752 body: identity_body,
1753 } = identity_func.pierce_no_inlines()
1754 else {
1755 return false;
1756 };
1757
1758 let Term::Var(identity_var) = identity_body.as_ref() else {
1759 return false;
1760 };
1761
1762 if *identity_var == identity_name {
1763 body.replace_identity_usage(parameter_name.clone());
1765 if !body
1768 .var_occurrences(parameter_name.clone(), vec![], vec![])
1769 .found
1770 {
1771 changed = true;
1772 context.inlined_apply_ids.push(arg_id);
1773 *self = core::mem::replace(body, Term::Error.force());
1774 }
1775 }
1776 }
1777 Term::Constr { .. } => todo!(),
1778 Term::Case { .. } => todo!(),
1779 _ => (),
1780 };
1781
1782 changed
1783 }
1784
1785 fn inline_reducer(
1786 &mut self,
1787 _id: Option<usize>,
1788 mut arg_stack: Vec<Args>,
1789 _scope: &Scope,
1790 context: &mut Context,
1791 ) -> bool {
1792 let mut changed = false;
1793
1794 match self {
1795 Term::Lambda {
1796 parameter_name,
1797 body,
1798 } => {
1799 let Some(Args::Apply(arg_id, arg_term)) = arg_stack.pop() else {
1801 return false;
1802 };
1803
1804 let arg_term = arg_term.pierce_no_inlines_ref();
1805
1806 let body = Rc::make_mut(body);
1807
1808 let var_lookup = body.var_occurrences(parameter_name.clone(), vec![], vec![]);
1809
1810 let must_execute_condition = var_lookup.delays == 0 && !var_lookup.no_inline;
1811
1812 let cant_throw_condition = matches!(
1813 arg_term,
1814 Term::Var(_)
1815 | Term::Constant(_)
1816 | Term::Delay(_)
1817 | Term::Lambda { .. }
1818 | Term::Builtin(_),
1819 );
1820
1821 let force_wrapped_builtin = context
1822 .builtins_map
1823 .keys()
1824 .any(|b| b.wrapped_name() == parameter_name.text);
1825
1826 if !force_wrapped_builtin
1829 && var_lookup.occurrences == 1
1830 && (must_execute_condition || cant_throw_condition)
1831 {
1832 changed = true;
1833 body.substitute_var(parameter_name.clone(), arg_term);
1834
1835 context.inlined_apply_ids.push(arg_id);
1836 *self = core::mem::replace(body, Term::Error.force());
1837
1838 } else if !var_lookup.found && (cant_throw_condition || force_wrapped_builtin) {
1840 changed = true;
1841 context.inlined_apply_ids.push(arg_id);
1842 *self = core::mem::replace(body, Term::Error.force());
1843 }
1844 }
1845
1846 Term::Constr { .. } => todo!(),
1847 Term::Case { .. } => todo!(),
1848 _ => {}
1849 };
1850 changed
1851 }
1852
1853 fn force_delay_reducer(
1854 &mut self,
1855 _id: Option<usize>,
1856 mut arg_stack: Vec<Args>,
1857 _scope: &Scope,
1858 context: &mut Context,
1859 ) -> bool {
1860 let mut changed = false;
1861 if let Term::Delay(d) = self {
1862 if let Some(Args::Force(id)) = arg_stack.pop() {
1863 changed = true;
1864 context.inlined_apply_ids.push(id);
1865 *self = core::mem::replace(Rc::make_mut(d), Term::Error.force())
1866 } else if let Term::Force(var) = d.as_ref() {
1867 if let Term::Var(_) = var.as_ref() {
1868 changed = true;
1869 *self = var.as_ref().clone();
1870 }
1871 }
1872 }
1873 changed
1874 }
1875
1876 fn remove_no_inlines(
1877 &mut self,
1878 _id: Option<usize>,
1879 _arg_stack: Vec<Args>,
1880 _scope: &Scope,
1881 _context: &mut Context,
1882 ) {
1883 match self {
1884 Term::Lambda {
1885 parameter_name,
1886 body,
1887 } if parameter_name.text == NO_INLINE => {
1888 *self = core::mem::replace(Rc::make_mut(body), Term::Error.force());
1889 }
1890 _ => (),
1891 }
1892 }
1893
1894 fn inline_constr_ops(
1896 &mut self,
1897 _id: Option<usize>,
1898 _arg_stack: Vec<Args>,
1899 _scope: &Scope,
1900 _context: &mut Context,
1901 ) {
1902 if let Term::Apply { function, argument } = self {
1903 if let Term::Var(name) = function.as_ref() {
1904 let arg = Rc::make_mut(argument);
1905 if name.text == CONSTR_FIELDS_EXPOSER {
1906 *self = Term::snd_pair().apply(
1907 Term::unconstr_data().apply(core::mem::replace(arg, Term::Error.force())),
1908 )
1909 } else if name.text == CONSTR_INDEX_EXPOSER {
1910 *self = Term::fst_pair().apply(
1911 Term::unconstr_data().apply(core::mem::replace(arg, Term::Error.force())),
1912 )
1913 }
1914 } else if let Term::Lambda {
1915 parameter_name,
1916 body,
1917 } = Rc::make_mut(function)
1918 {
1919 if parameter_name.text == CONSTR_INDEX_EXPOSER
1920 || parameter_name.text == CONSTR_FIELDS_EXPOSER
1921 {
1922 let body = Rc::make_mut(body);
1923 *self = core::mem::replace(body, Term::Error)
1924 }
1925 }
1926 }
1927 }
1928
1929 fn cast_data_reducer(
1930 &mut self,
1931 _id: Option<usize>,
1932 mut arg_stack: Vec<Args>,
1933 _scope: &Scope,
1934 context: &mut Context,
1935 ) -> bool {
1936 let mut changed = false;
1937
1938 match self {
1939 Term::Builtin(first_function) => {
1940 let Some(Args::Apply(arg_id, mut arg_term)) = arg_stack.pop() else {
1941 return false;
1942 };
1943
1944 match &mut arg_term {
1945 Term::Apply { function, argument } => {
1946 if let Term::Builtin(second_function) = function.as_ref() {
1947 match (first_function, second_function) {
1948 (DefaultFunction::UnIData, DefaultFunction::IData)
1949 | (DefaultFunction::IData, DefaultFunction::UnIData)
1950 | (DefaultFunction::BData, DefaultFunction::UnBData)
1951 | (DefaultFunction::UnBData, DefaultFunction::BData)
1952 | (DefaultFunction::ListData, DefaultFunction::UnListData)
1953 | (DefaultFunction::UnListData, DefaultFunction::ListData)
1954 | (DefaultFunction::MapData, DefaultFunction::UnMapData)
1955 | (DefaultFunction::UnMapData, DefaultFunction::MapData) => {
1956 changed = true;
1957 context.inlined_apply_ids.push(arg_id);
1958 *self = core::mem::replace(
1959 Rc::make_mut(argument),
1960 Term::Error.force(),
1961 );
1962 }
1963 _ => {}
1964 }
1965 }
1966 }
1967 Term::Constant(c) => match (first_function, c.as_ref()) {
1968 (
1969 DefaultFunction::UnIData,
1970 Constant::Data(PlutusData::BigInt(BigInt::Int(i))),
1971 ) => {
1972 changed = true;
1973 context.inlined_apply_ids.push(arg_id);
1974 *self = Term::integer(i128::from(*i).into());
1975 }
1976 (DefaultFunction::IData, Constant::Integer(i)) => {
1977 changed = true;
1978 context.inlined_apply_ids.push(arg_id);
1979 *self = Term::data(Data::integer(i.clone()));
1980 }
1981 (DefaultFunction::UnBData, Constant::Data(PlutusData::BoundedBytes(b))) => {
1982 changed = true;
1983 context.inlined_apply_ids.push(arg_id);
1984 *self = Term::byte_string(b.clone().into());
1985 }
1986 (DefaultFunction::BData, Constant::ByteString(b)) => {
1987 changed = true;
1988 context.inlined_apply_ids.push(arg_id);
1989 *self = Term::data(Data::bytestring(b.clone()));
1990 }
1991 (DefaultFunction::UnListData, Constant::Data(PlutusData::Array(l))) => {
1992 changed = true;
1993 context.inlined_apply_ids.push(arg_id);
1994 *self = Term::list_values(
1995 l.iter()
1996 .map(|item| Constant::Data(item.clone()))
1997 .collect_vec(),
1998 );
1999 }
2000 (DefaultFunction::ListData, Constant::ProtoList(_, l)) => {
2001 changed = true;
2002 context.inlined_apply_ids.push(arg_id);
2003 *self = Term::data(Data::list(
2004 l.iter()
2005 .map(|item| match item {
2006 Constant::Data(d) => d.clone(),
2007 _ => unreachable!(),
2008 })
2009 .collect_vec(),
2010 ));
2011 }
2012 (DefaultFunction::MapData, Constant::ProtoList(_, m)) => {
2013 changed = true;
2014 context.inlined_apply_ids.push(arg_id);
2015 *self = Term::data(Data::map(
2016 m.iter()
2017 .map(|m| match m {
2018 Constant::ProtoPair(_, _, f, s) => {
2019 match (f.as_ref(), s.as_ref()) {
2020 (Constant::Data(d), Constant::Data(d2)) => {
2021 (d.clone(), d2.clone())
2022 }
2023 _ => unreachable!(),
2024 }
2025 }
2026 _ => unreachable!(),
2027 })
2028 .collect_vec(),
2029 ));
2030 }
2031 (DefaultFunction::UnMapData, Constant::Data(PlutusData::Map(m))) => {
2032 changed = true;
2033 context.inlined_apply_ids.push(arg_id);
2034 *self = Term::map_values(
2035 m.iter()
2036 .map(|item| {
2037 Constant::ProtoPair(
2038 Type::Data,
2039 Type::Data,
2040 Constant::Data(item.0.clone()).into(),
2041 Constant::Data(item.1.clone()).into(),
2042 )
2043 })
2044 .collect_vec(),
2045 );
2046 }
2047 _ => {}
2048 },
2049 _ => {}
2050 }
2051 }
2052 Term::Constr { .. } => todo!(),
2053 Term::Case { .. } => todo!(),
2054 _ => {}
2055 }
2056 changed
2057 }
2058
2059 fn convert_arithmetic_ops(
2061 &mut self,
2062 _id: Option<usize>,
2063 arg_stack: Vec<Args>,
2064 _scope: &Scope,
2065 context: &mut Context,
2066 ) -> bool {
2067 let mut changed = false;
2068 match self {
2069 Term::Builtin(d @ DefaultFunction::SubtractInteger) => {
2070 if arg_stack.len() == d.arity() {
2071 let Some(Args::Apply(apply_id, Term::Constant(_))) = arg_stack.last() else {
2072 return false;
2073 };
2074 changed = true;
2075 context.constants_to_flip.push(*apply_id);
2076
2077 *self = Term::Builtin(DefaultFunction::AddInteger);
2078 }
2079 }
2080 Term::Constr { .. } => todo!(),
2081 Term::Case { .. } => todo!(),
2082 _ => {}
2083 }
2084 changed
2085 }
2086
2087 fn builtin_eval_reducer(
2088 &mut self,
2089 _id: Option<usize>,
2090 mut arg_stack: Vec<Args>,
2091 _scope: &Scope,
2092 context: &mut Context,
2093 ) -> bool {
2094 let mut changed = false;
2095
2096 match self {
2097 Term::Builtin(func) => {
2098 arg_stack = arg_stack
2099 .into_iter()
2100 .filter(|args| matches!(args, Args::Apply(_, _)))
2101 .collect_vec();
2102
2103 let args = arg_stack
2104 .iter()
2105 .map(|args| {
2106 let Args::Apply(_, term) = args else {
2107 unreachable!()
2108 };
2109
2110 term.pierce_no_inlines_ref()
2111 })
2112 .collect_vec();
2113 if arg_stack.len() == func.arity() && func.is_error_safe(&args) {
2114 changed = true;
2115 let applied_term =
2116 arg_stack
2117 .into_iter()
2118 .fold(Term::Builtin(*func), |acc, item| {
2119 let Args::Apply(arg_id, arg) = item else {
2120 unreachable!()
2121 };
2122
2123 context.inlined_apply_ids.push(arg_id);
2124 acc.apply(arg.pierce_no_inlines().clone())
2125 });
2126
2127 let eval_term: Term<Name> = Program {
2129 version: (1, 0, 0),
2130 term: applied_term,
2131 }
2132 .to_named_debruijn()
2133 .unwrap()
2134 .eval(ExBudget::default())
2135 .result()
2136 .unwrap()
2137 .try_into()
2138 .unwrap();
2139
2140 *self = eval_term;
2141 }
2142 }
2143 Term::Constr { .. } => todo!(),
2144 Term::Case { .. } => todo!(),
2145 _ => (),
2146 }
2147 changed
2148 }
2149
2150 fn remove_inlined_ids(
2151 &mut self,
2152 id: Option<usize>,
2153 _arg_stack: Vec<Args>,
2154 _scope: &Scope,
2155 context: &mut Context,
2156 ) {
2157 match self {
2158 Term::Apply { function, .. } | Term::Force(function) => {
2159 let Some(id) = id else {
2161 return;
2162 };
2163
2164 if context.inlined_apply_ids.contains(&id) {
2165 let func = Rc::make_mut(function);
2166 *self = core::mem::replace(func, Term::Error.force());
2167 }
2168 }
2169 _ => (),
2170 }
2171 }
2172
2173 fn flip_constants(
2174 &mut self,
2175 id: Option<usize>,
2176 _arg_stack: Vec<Args>,
2177 _scope: &Scope,
2178 context: &mut Context,
2179 ) {
2180 if let Term::Apply { argument, .. } = self {
2181 let Some(id) = id else {
2182 return;
2183 };
2184
2185 if context.constants_to_flip.contains(&id) {
2186 let Term::Constant(c) = Rc::make_mut(argument) else {
2187 unreachable!();
2188 };
2189
2190 let Constant::Integer(i) = c.as_ref() else {
2191 unreachable!();
2192 };
2193
2194 *c = Constant::Integer(i.neg()).into();
2195 }
2196 }
2197 }
2198
2199 pub fn pierce_no_inlines_ref(&self) -> &Self {
2200 let mut term = self;
2201
2202 while let Term::Lambda {
2203 parameter_name,
2204 body,
2205 } = term
2206 {
2207 if parameter_name.as_ref().text == NO_INLINE {
2208 term = body;
2209 } else {
2210 break;
2211 }
2212 }
2213
2214 term
2215 }
2216
2217 fn pierce_no_inlines(mut self) -> Self {
2218 let term = &mut self;
2219
2220 while let Term::Lambda {
2221 parameter_name,
2222 body,
2223 } = term
2224 {
2225 if parameter_name.as_ref().text == NO_INLINE {
2226 *term = core::mem::replace(Rc::make_mut(body), Term::Error.force());
2227 } else {
2228 break;
2229 }
2230 }
2231
2232 core::mem::replace(term, Term::Error.force())
2233 }
2234
2235 fn is_a_builtin_wrapper(&self) -> bool {
2236 let (names, term) = self.pop_lambdas_and_get_names();
2237
2238 let mut arg_names = vec![];
2239
2240 let mut term = term;
2241
2242 while let Term::Apply { function, argument } = term {
2243 match argument.as_ref() {
2244 Term::Var(name) => arg_names.push(format!("{}_{}", name.text, name.unique)),
2245
2246 Term::Constant(_) => {}
2247 _ => {
2248 return false;
2250 }
2251 }
2252 term = function.as_ref();
2253 }
2254
2255 let func_is_builtin = match term {
2256 Term::Var(name) => forceable_wrapped_names().contains(&name.text),
2257 Term::Builtin(_) => true,
2258 _ => false,
2259 };
2260
2261 arg_names.iter().all(|item| names.contains(item)) && func_is_builtin
2262 }
2263
2264 fn pop_lambdas_and_get_names(&self) -> (Vec<String>, &Term<Name>) {
2265 let mut names = vec![];
2266
2267 let mut term = self;
2268
2269 while let Term::Lambda {
2270 parameter_name,
2271 body,
2272 } = term
2273 {
2274 if parameter_name.text != NO_INLINE {
2275 names.push(format!("{}_{}", parameter_name.text, parameter_name.unique));
2276 }
2277 term = body.as_ref();
2278 }
2279
2280 (names, term)
2281 }
2282}
2283
2284impl Program<Name> {
2285 fn traverse_uplc_with(
2286 self,
2287 inline_lambda: bool,
2288 with: &mut impl FnMut(Option<usize>, &mut Term<Name>, Vec<Args>, &Scope, &mut Context),
2289 ) -> (Self, Context) {
2290 let mut term = self.term;
2291 let scope = Scope { scope: vec![] };
2292 let arg_stack = vec![];
2293 let mut id_gen = IdGen::new();
2294
2295 let mut context = Context {
2296 inlined_apply_ids: vec![],
2297 constants_to_flip: vec![],
2298 write_bits_indices_arg: vec![],
2299 builtins_map: HashMap::new(),
2300 blst_p1_list: vec![],
2301 blst_p2_list: vec![],
2302 write_bits_convert: false,
2303 node_count: 0,
2304 };
2305
2306 term.traverse_uplc_with_helper(
2307 &scope,
2308 arg_stack,
2309 &mut id_gen,
2310 with,
2311 &mut context,
2312 inline_lambda,
2313 );
2314 (
2315 Program {
2316 version: self.version,
2317 term,
2318 },
2319 context,
2320 )
2321 }
2322 pub fn run_once_pass(self) -> Self {
2324 let (program, context) = self
2327 .traverse_uplc_with(false, &mut |id, term, _arg_stack, scope, context| {
2328 term.inline_constr_ops(id, vec![], scope, context);
2329 })
2330 .0
2331 .traverse_uplc_with(false, &mut |id, term, arg_stack, scope, context| {
2332 term.bls381_compressor(id, vec![], scope, context);
2333 term.builtin_force_reducer(id, arg_stack, scope, context);
2334 term.remove_inlined_ids(id, vec![], scope, context);
2335 });
2336
2337 let mut term = program.term;
2338
2339 for (index, blst_p1) in context.blst_p1_list.into_iter().enumerate() {
2340 let compressed = blst_p1.compress();
2341
2342 term = term
2343 .lambda(format!("blst_p1_index_{}", index))
2344 .apply(Term::bls12_381_g1_uncompress().apply(Term::byte_string(compressed)));
2345 }
2346
2347 for (index, blst_p2) in context.blst_p2_list.into_iter().enumerate() {
2348 let compressed = blst_p2.compress();
2349
2350 term = term
2351 .lambda(format!("blst_p2_index_{}", index))
2352 .apply(Term::bls12_381_g2_uncompress().apply(Term::byte_string(compressed)));
2353 }
2354
2355 for default_func in context.builtins_map.keys().sorted().cloned() {
2356 term = term.lambda(default_func.wrapped_name());
2357 }
2358
2359 for default_func in context.builtins_map.keys().sorted().cloned().rev() {
2360 term = term.apply(if default_func.force_count() == 1 {
2361 Term::Builtin(default_func).force()
2362 } else {
2363 Term::Builtin(default_func).force().force()
2364 });
2365 }
2366
2367 let mut program = Program {
2368 version: program.version,
2369 term,
2370 };
2371
2372 let mut interner = CodeGenInterner::new();
2373
2374 interner.program(&mut program);
2375
2376 let program = Program::<NamedDeBruijn>::try_from(program).unwrap();
2377
2378 Program::<Name>::try_from(program).unwrap()
2379 }
2380
2381 pub fn multi_pass(self) -> (Self, Context) {
2382 self.traverse_uplc_with(true, &mut |id, term, arg_stack, scope, context| {
2383 let false = term.lambda_reducer(id, arg_stack.clone(), scope, context) else {
2384 term.remove_inlined_ids(id, vec![], scope, context);
2385 return;
2386 };
2387
2388 let false = term.identity_reducer(id, arg_stack.clone(), scope, context) else {
2389 term.remove_inlined_ids(id, vec![], scope, context);
2390 return;
2391 };
2392
2393 let false = term.inline_reducer(id, arg_stack.clone(), scope, context) else {
2394 term.remove_inlined_ids(id, vec![], scope, context);
2395 return;
2396 };
2397
2398 let false = term.force_delay_reducer(id, arg_stack.clone(), scope, context) else {
2399 term.remove_inlined_ids(id, vec![], scope, context);
2400 return;
2401 };
2402
2403 let false = term.cast_data_reducer(id, arg_stack.clone(), scope, context) else {
2404 term.remove_inlined_ids(id, vec![], scope, context);
2405 return;
2406 };
2407
2408 let false = term.builtin_eval_reducer(id, arg_stack.clone(), scope, context) else {
2409 term.remove_inlined_ids(id, vec![], scope, context);
2410 return;
2411 };
2412
2413 term.convert_arithmetic_ops(id, arg_stack, scope, context);
2414 term.flip_constants(id, vec![], scope, context);
2415 term.remove_inlined_ids(id, vec![], scope, context);
2416 })
2417 }
2418
2419 pub fn run_one_opt(
2420 self,
2421 inline_lambda: bool,
2422 with: &mut impl FnMut(Option<usize>, &mut Term<Name>, Vec<Args>, &Scope, &mut Context),
2423 ) -> Self {
2424 let (mut program, context) =
2425 self.traverse_uplc_with(inline_lambda, &mut |id, term, arg_stack, scope, context| {
2426 with(id, term, arg_stack, scope, context);
2427 term.flip_constants(id, vec![], scope, context);
2428 term.remove_inlined_ids(id, vec![], scope, context);
2429 });
2430
2431 if context.write_bits_convert {
2432 program.term = program.term.data_list_to_integer_list();
2433 }
2434
2435 program
2436 }
2437
2438 pub fn clean_up_no_inlines(self) -> Self {
2439 self.traverse_uplc_with(true, &mut |id, term, _arg_stack, scope, context| {
2440 term.remove_no_inlines(id, vec![], scope, context);
2441 })
2442 .0
2443 }
2444
2445 pub fn afterwards(self) -> Self {
2446 let (mut program, context) =
2447 self.traverse_uplc_with(true, &mut |id, term, arg_stack, scope, context| {
2448 term.write_bits_convert_arg(id, arg_stack, scope, context);
2449 });
2450
2451 program = program
2452 .split_body_lambda_reducer()
2453 .traverse_uplc_with(true, &mut |id, term, _arg_stack, scope, context| {
2454 term.case_constr_apply_reducer(id, vec![], scope, context);
2455 })
2456 .0;
2457
2458 if context.write_bits_convert {
2459 program.term = program.term.data_list_to_integer_list();
2460 }
2461
2462 let mut interner = CodeGenInterner::new();
2463
2464 interner.program(&mut program);
2465
2466 let program = Program::<NamedDeBruijn>::try_from(program).unwrap();
2467
2468 Program::<Name>::try_from(program).unwrap()
2469 }
2470
2471 pub fn builtin_curry_reducer(self) -> Self {
2473 let mut curried_terms = vec![];
2474 let mut id_mapped_curry_terms: HashMap<CurriedName, (Scope, Term<Name>, usize)> =
2475 HashMap::new();
2476 let mut curry_applied_ids = vec![];
2477 let mut scope_mapped_to_term: HashMap<Scope, Vec<(CurriedName, Term<Name>)>> =
2478 HashMap::new();
2479
2480 let mut flipped_terms: HashMap<Scope, bool> = HashMap::new();
2481
2482 let mut final_ids: HashMap<Vec<usize>, ()> = HashMap::new();
2483
2484 let (step_a, _) = self.traverse_uplc_with(
2485 false,
2486 &mut |_id, term, arg_stack, scope, _context| match term {
2487 Term::Builtin(func) => {
2488 if func.can_curry_builtin() && arg_stack.len() == func.arity() {
2489 let arg_stack = arg_stack
2490 .into_iter()
2491 .map(|item| {
2492 let Args::Apply(arg_id, arg) = item else {
2493 unreachable!()
2494 };
2495 (arg_id, arg)
2496 })
2497 .collect_vec();
2498 let builtin_args = BuiltinArgs::args_from_arg_stack(arg_stack, *func);
2502
2503 let mut id_vec = if let Some((index, _)) =
2505 curried_terms.iter_mut().find_position(
2506 |curried_term: &&mut CurriedBuiltin| curried_term.func == *func,
2507 ) {
2508 let curried_builtin = curried_terms.swap_remove(index);
2511
2512 let curried_builtin =
2513 curried_builtin.merge_node_by_path(builtin_args.clone());
2514
2515 flipped_terms
2516 .insert(scope.clone(), curried_builtin.is_flipped(&builtin_args));
2517
2518 let Some(id_vec) = curried_builtin.get_id_args(builtin_args) else {
2519 unreachable!();
2520 };
2521
2522 curried_terms.push(curried_builtin);
2523
2524 id_vec
2525 } else {
2526 let curried_builtin = builtin_args.clone().args_to_curried_args(*func);
2528
2529 let Some(id_vec) = curried_builtin.get_id_args(builtin_args) else {
2530 unreachable!();
2531 };
2532
2533 curried_terms.push(curried_builtin);
2534
2535 id_vec
2536 };
2537
2538 while let Some(node) = id_vec.pop() {
2539 let mut id_only_vec =
2540 id_vec.iter().map(|item| item.curried_id).collect_vec();
2541
2542 id_only_vec.push(node.curried_id);
2543
2544 let curry_name = CurriedName {
2545 func_name: func.aiken_name(),
2546 id_vec: id_only_vec,
2547 };
2548
2549 if let Some((map_scope, _, occurrences)) =
2550 id_mapped_curry_terms.get_mut(&curry_name)
2551 {
2552 *map_scope = map_scope.common_ancestor(scope);
2553 *occurrences += 1;
2554 } else if id_vec.is_empty() {
2555 id_mapped_curry_terms.insert(
2556 curry_name,
2557 (scope.clone(), Term::Builtin(*func).apply(node.term), 1),
2558 );
2559 } else {
2560 let var_name = id_vec_function_to_var(
2561 &func.aiken_name(),
2562 &id_vec.iter().map(|item| item.curried_id).collect_vec(),
2563 );
2564
2565 id_mapped_curry_terms.insert(
2566 curry_name,
2567 (scope.clone(), Term::var(var_name).apply(node.term), 1),
2568 );
2569 }
2570 }
2571 }
2572 }
2573 Term::Constr { .. } => todo!(),
2574 Term::Case { .. } => todo!(),
2575 _ => {}
2576 },
2577 );
2578
2579 id_mapped_curry_terms
2580 .into_iter()
2581 .filter(|(_, (_, _, occurrences))| *occurrences > 2)
2583 .for_each(|(key, val)| {
2584 final_ids.insert(key.id_vec.clone(), ());
2585
2586 match scope_mapped_to_term.get_mut(&val.0) {
2587 Some(list) => {
2588 let insert_position = list
2589 .iter()
2590 .position(|(list_key, _)| key.len() <= list_key.len())
2591 .unwrap_or(list.len());
2592
2593 list.insert(insert_position, (key, val.1));
2594 }
2595 None => {
2596 scope_mapped_to_term.insert(val.0, vec![(key, val.1)]);
2597 }
2598 }
2599 });
2600
2601 let (mut step_b, _) = step_a.traverse_uplc_with(
2602 false,
2603 &mut |id, term, arg_stack, scope, _context| match term {
2604 Term::Builtin(func) => {
2605 if func.can_curry_builtin() && arg_stack.len() == func.arity() {
2606 let mut arg_stack = arg_stack
2607 .into_iter()
2608 .map(|item| {
2609 let Args::Apply(arg_id, arg) = item else {
2610 unreachable!()
2611 };
2612 (arg_id, arg)
2613 })
2614 .collect_vec();
2615
2616 let Some(curried_builtin) =
2617 curried_terms.iter().find(|curry| curry.func == *func)
2618 else {
2619 return;
2620 };
2621
2622 if let Some(true) = flipped_terms.get(scope) {
2623 arg_stack.reverse();
2624 }
2625
2626 let builtin_args = BuiltinArgs::args_from_arg_stack(arg_stack, *func);
2627
2628 let Some(mut id_vec) = curried_builtin.get_id_args(builtin_args) else {
2629 return;
2630 };
2631
2632 while !id_vec.is_empty() {
2633 let id_lookup = id_vec.iter().map(|item| item.curried_id).collect_vec();
2634
2635 if final_ids.contains_key(&id_lookup) {
2636 break;
2637 }
2638 id_vec.pop();
2639 }
2640
2641 if id_vec.is_empty() {
2642 return;
2643 }
2644
2645 let name = id_vec_function_to_var(
2646 &func.aiken_name(),
2647 &id_vec.iter().map(|item| item.curried_id).collect_vec(),
2648 );
2649
2650 id_vec.iter().for_each(|item| {
2651 curry_applied_ids.push(item.applied_id);
2652 });
2653
2654 *term = Term::var(name);
2655 }
2656 }
2657 Term::Apply { function, .. } => {
2658 let id = id.unwrap();
2659
2660 if curry_applied_ids.contains(&id) {
2661 *term = function.as_ref().clone();
2662 }
2663
2664 if let Some(insert_list) = scope_mapped_to_term.remove(scope) {
2665 for (key, val) in insert_list.into_iter().rev() {
2666 let name = id_vec_function_to_var(&key.func_name, &key.id_vec);
2667
2668 if term
2669 .var_occurrences(Name::text(&name).into(), vec![], vec![])
2670 .found
2671 {
2672 *term = term.clone().lambda(name).apply(val);
2673 }
2674 }
2675 }
2676 }
2677 Term::Constr { .. } => todo!(),
2678 Term::Case { .. } => todo!(),
2679 _ => {
2680 if let Some(insert_list) = scope_mapped_to_term.remove(scope) {
2681 for (key, val) in insert_list.into_iter().rev() {
2682 let name = id_vec_function_to_var(&key.func_name, &key.id_vec);
2683
2684 if term
2685 .var_occurrences(Name::text(&name).into(), vec![], vec![])
2686 .found
2687 {
2688 *term = term.clone().lambda(name).apply(val);
2689 }
2690 }
2691 }
2692 }
2693 },
2694 );
2695
2696 let mut interner = CodeGenInterner::new();
2697
2698 interner.program(&mut step_b);
2699
2700 step_b
2701 }
2702
2703 pub fn split_body_lambda_reducer(mut self) -> Self {
2704 self.term.split_body_lambda();
2705
2706 self
2707 }
2708}
2709
2710fn id_vec_function_to_var(func_name: &str, id_vec: &[usize]) -> String {
2711 format!(
2712 "__{}_{}_curried",
2713 func_name,
2714 id_vec
2715 .iter()
2716 .map(|item| item.to_string())
2717 .collect::<Vec<String>>()
2718 .join("_")
2719 )
2720}
2721
2722#[cfg(test)]
2723mod tests {
2724 use super::NO_INLINE;
2725 use crate::pallas_primitives::conway::{BigInt, PlutusData};
2726 use crate::uplc::{
2727 ast::{Constant, Data, Name, NamedDeBruijn, Program, Term},
2728 builder::{CONSTR_FIELDS_EXPOSER, CONSTR_INDEX_EXPOSER},
2729 builtins::DefaultFunction,
2730 optimize::interner::CodeGenInterner,
2731 };
2732 use pretty_assertions::assert_eq;
2733
2734 fn compare_optimization(
2735 mut expected: Program<Name>,
2736 mut program: Program<Name>,
2737 optimization: fn(Program<Name>) -> Program<Name>,
2738 ) {
2739 let mut interner = CodeGenInterner::new();
2740
2741 interner.program(&mut program);
2742
2743 let mut interner = CodeGenInterner::new();
2744
2745 interner.program(&mut expected);
2746
2747 let expected: Program<NamedDeBruijn> = expected.try_into().unwrap();
2748 let actual = optimization(program);
2749
2750 let actual: Program<NamedDeBruijn> = actual.try_into().unwrap();
2751
2752 assert_eq!(actual, expected);
2753 }
2754
2755 #[test]
2756 fn lambda_reduce_var() {
2757 let program = Program {
2758 version: (1, 0, 0),
2759 term: Term::var("bar")
2760 .lambda("bar")
2761 .apply(Term::var("foo"))
2762 .lambda("foo")
2763 .apply(
2764 Term::constr_data()
2765 .apply(Term::integer(3.into()))
2766 .apply(Term::list_values(vec![])),
2767 ),
2768 };
2769
2770 let expected = Program {
2771 version: (1, 0, 0),
2772 term: Term::var("foo").lambda("foo").apply(
2773 Term::constr_data()
2774 .apply(Term::integer(3.into()))
2775 .apply(Term::list_values(vec![])),
2776 ),
2777 };
2778
2779 compare_optimization(expected, program, |p| {
2780 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
2781 term.lambda_reducer(id, arg_stack, scope, context);
2782 })
2783 });
2784 }
2785
2786 #[test]
2787 fn lambda_reduce_constant() {
2788 let program = Program {
2789 version: (1, 0, 0),
2790 term: Term::var("foo")
2791 .lambda("foo")
2792 .apply(Term::integer(6.into())),
2793 };
2794
2795 let expected: Program<Name> = Program {
2796 version: (1, 0, 0),
2797 term: Term::integer(6.into()),
2798 };
2799
2800 compare_optimization(expected, program, |p| {
2801 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
2802 term.lambda_reducer(id, arg_stack, scope, context);
2803 })
2804 });
2805 }
2806
2807 #[test]
2808 fn lambda_reduce_builtin() {
2809 let program = Program {
2810 version: (1, 0, 0),
2811 term: Term::var("foo").lambda("foo").apply(Term::add_integer()),
2812 };
2813
2814 let expected: Program<Name> = Program {
2815 version: (1, 0, 0),
2816 term: Term::add_integer(),
2817 };
2818
2819 compare_optimization(expected, program, |p| {
2820 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
2821 term.lambda_reducer(id, arg_stack, scope, context);
2822 })
2823 });
2824 }
2825
2826 #[test]
2827 fn lambda_reduce_force_delay_error_lam() {
2828 let program: Program<Name> = Program {
2829 version: (1, 0, 0),
2830 term: Term::var("foo")
2831 .apply(Term::var("bar"))
2832 .apply(Term::var("baz"))
2833 .apply(Term::var("bat"))
2834 .lambda("foo")
2835 .apply(Term::snd_pair())
2836 .lambda("bar")
2837 .apply(Term::integer(1.into()).delay())
2838 .lambda("baz")
2839 .apply(Term::Error)
2840 .lambda("bat")
2841 .apply(Term::bool(false).lambda("x")),
2842 };
2843
2844 let expected = Program {
2845 version: (1, 0, 0),
2846 term: Term::var("foo")
2847 .apply(Term::var("bar"))
2848 .apply(Term::var("baz"))
2849 .apply(Term::var("bat"))
2850 .lambda("foo")
2851 .apply(Term::snd_pair())
2852 .lambda("bar")
2853 .apply(Term::integer(1.into()).delay())
2854 .lambda("baz")
2855 .apply(Term::Error)
2856 .lambda("bat")
2857 .apply(Term::bool(false).lambda("x")),
2858 };
2859
2860 compare_optimization(expected, program, |p| {
2861 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
2862 term.lambda_reducer(id, arg_stack, scope, context);
2863 })
2864 });
2865 }
2866
2867 #[test]
2868 fn builtin_force_reduce_list_builtins() {
2869 let program: Program<Name> = Program {
2870 version: (1, 0, 0),
2871 term: Term::mk_cons()
2872 .apply(Term::var("x"))
2873 .apply(Term::tail_list().apply(Term::head_list().apply(Term::var("y"))))
2874 .lambda("x")
2875 .lambda("y"),
2876 };
2877
2878 let expected = Program {
2879 version: (1, 0, 0),
2880 term: Term::var("__cons_list_wrapped")
2881 .apply(Term::var("x"))
2882 .apply(
2883 Term::var("__tail_list_wrapped")
2884 .apply(Term::var("__head_list_wrapped").apply(Term::var("y"))),
2885 )
2886 .lambda("x")
2887 .lambda("y")
2888 .lambda("__cons_list_wrapped")
2890 .lambda("__head_list_wrapped")
2891 .lambda("__tail_list_wrapped")
2892 .apply(Term::tail_list())
2893 .apply(Term::head_list())
2894 .apply(Term::mk_cons()),
2895 };
2896
2897 compare_optimization(expected, program, |p| p.run_once_pass());
2898 }
2899
2900 #[test]
2901 fn builtin_force_reduce_if_builtin() {
2902 let program: Program<Name> = Program {
2903 version: (1, 0, 0),
2904 term: Term::equals_integer()
2905 .apply(Term::var("x"))
2906 .apply(
2907 Term::add_integer()
2908 .apply(Term::integer(2.into()))
2909 .apply(Term::var("y")),
2910 )
2911 .delayed_if_then_else(
2912 Term::length_of_bytearray().apply(Term::byte_string(vec![])),
2913 Term::Error,
2914 )
2915 .lambda("x")
2916 .lambda("y"),
2917 };
2918
2919 let expected = Program {
2920 version: (1, 0, 0),
2921 term: Term::var("__if_then_else_wrapped")
2922 .apply(
2923 Term::equals_integer().apply(Term::var("x")).apply(
2924 Term::add_integer()
2925 .apply(Term::integer(2.into()))
2926 .apply(Term::var("y")),
2927 ),
2928 )
2929 .apply(
2930 Term::length_of_bytearray()
2931 .apply(Term::byte_string(vec![]))
2932 .delay(),
2933 )
2934 .apply(Term::Error.delay())
2935 .force()
2936 .lambda("x")
2937 .lambda("y")
2938 .lambda("__if_then_else_wrapped")
2939 .apply(Term::Builtin(DefaultFunction::IfThenElse).force()),
2940 };
2941
2942 compare_optimization(expected, program, |p| p.run_once_pass());
2943 }
2944
2945 #[test]
2946 fn builtin_force_reduce_pair_builtins() {
2947 let program: Program<Name> = Program {
2948 version: (1, 0, 0),
2949 term: Term::add_integer()
2950 .apply(Term::un_i_data().apply(Term::fst_pair().apply(Term::var("__pair"))))
2951 .apply(Term::un_i_data().apply(Term::snd_pair().apply(Term::var("__pair"))))
2952 .lambda("__pair")
2953 .apply(
2954 Term::mk_pair_data()
2955 .apply(Term::data(Data::integer(1.into())))
2956 .apply(Term::data(Data::integer(5.into()))),
2957 ),
2958 };
2959
2960 let expected = Program {
2961 version: (1, 0, 0),
2962 term: Term::add_integer()
2963 .apply(
2964 Term::un_i_data()
2965 .apply(Term::var("__fst_pair_wrapped").apply(Term::var("__pair"))),
2966 )
2967 .apply(
2968 Term::un_i_data()
2969 .apply(Term::var("__snd_pair_wrapped").apply(Term::var("__pair"))),
2970 )
2971 .lambda("__pair")
2972 .apply(
2973 Term::mk_pair_data()
2974 .apply(Term::data(Data::integer(1.into())))
2975 .apply(Term::data(Data::integer(5.into()))),
2976 )
2977 .lambda("__fst_pair_wrapped")
2978 .lambda("__snd_pair_wrapped")
2979 .apply(Term::snd_pair())
2980 .apply(Term::fst_pair()),
2981 };
2982
2983 compare_optimization(expected, program, |p| p.run_once_pass());
2984 }
2985
2986 #[test]
2987 fn identity_reduce_usage() {
2988 let program: Program<Name> = Program {
2989 version: (1, 0, 0),
2990 term: Term::sha2_256()
2991 .apply(Term::var("identity").apply(Term::var("x")))
2992 .lambda("x")
2993 .apply(Term::byte_string(vec![]).delay())
2994 .lambda("identity")
2995 .apply(Term::var("y").lambda("y")),
2996 };
2997
2998 let expected = Program {
2999 version: (1, 0, 0),
3000 term: Term::sha2_256()
3001 .apply(Term::var("x"))
3002 .lambda("x")
3003 .apply(Term::byte_string(vec![]).delay()),
3004 };
3005
3006 compare_optimization(expected, program, |p| {
3007 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3008 term.identity_reducer(id, arg_stack, scope, context);
3009 })
3010 });
3011 }
3012
3013 #[test]
3014 fn identity_reduce_0_occurrence() {
3015 let program: Program<Name> = Program {
3016 version: (1, 0, 0),
3017 term: Term::sha2_256()
3018 .apply(Term::var("x"))
3019 .lambda("x")
3020 .apply(Term::byte_string(vec![]).delay())
3021 .lambda("identity")
3022 .apply(Term::var("y").lambda("y")),
3023 };
3024
3025 let expected = Program {
3026 version: (1, 0, 0),
3027 term: Term::sha2_256()
3028 .apply(Term::var("x"))
3029 .lambda("x")
3030 .apply(Term::byte_string(vec![]).delay()),
3031 };
3032
3033 compare_optimization(expected, program, |p| {
3034 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3035 term.identity_reducer(id, arg_stack, scope, context);
3036 })
3037 });
3038 }
3039
3040 #[test]
3041 fn identity_reduce_param() {
3042 let program: Program<Name> = Program {
3043 version: (1, 0, 0),
3044 term: Term::sha2_256()
3045 .apply(
3046 Term::var("f")
3047 .apply(Term::var("x"))
3048 .apply(Term::var("identity")),
3049 )
3050 .lambda("x")
3051 .apply(Term::byte_string(vec![]).delay())
3052 .lambda("identity")
3053 .apply(Term::var("y").lambda("y"))
3054 .lambda("f")
3055 .apply(
3056 Term::var("with")
3057 .apply(Term::var("x"))
3058 .lambda("with")
3059 .lambda("x"),
3060 ),
3061 };
3062
3063 let expected = Program {
3064 version: (1, 0, 0),
3065 term: Term::sha2_256()
3066 .apply(
3067 Term::var("f")
3068 .apply(Term::var("x"))
3069 .apply(Term::var("identity")),
3070 )
3071 .lambda("x")
3072 .apply(Term::byte_string(vec![]).delay())
3073 .lambda("identity")
3074 .apply(Term::var("y").lambda("y"))
3075 .lambda("f")
3076 .apply(
3077 Term::var("with")
3078 .apply(Term::var("x"))
3079 .lambda("with")
3080 .lambda("x"),
3081 ),
3082 };
3083
3084 compare_optimization(expected, program, |p| {
3085 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3086 term.identity_reducer(id, arg_stack, scope, context);
3087 })
3088 });
3089 }
3090
3091 #[test]
3092 fn identity_reduce_no_inline() {
3093 let program: Program<Name> = Program {
3094 version: (1, 0, 0),
3095 term: Term::sha2_256()
3096 .apply(Term::var("identity").apply(Term::var("x")))
3097 .lambda("x")
3098 .apply(Term::byte_string(vec![]).delay())
3099 .lambda("identity")
3100 .apply(Term::var("y").lambda("y").lambda(NO_INLINE)),
3101 };
3102
3103 let expected = Program {
3104 version: (1, 0, 0),
3105 term: Term::sha2_256()
3106 .apply(Term::var("x"))
3107 .lambda("x")
3108 .apply(Term::byte_string(vec![]).delay()),
3109 };
3110
3111 compare_optimization(expected, program, |p| {
3112 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3113 term.identity_reducer(id, arg_stack, scope, context);
3114 })
3115 });
3116 }
3117
3118 #[test]
3119 fn identity_reduce_no_inline_2() {
3120 let program: Program<Name> = Program {
3121 version: (1, 0, 0),
3122 term: Term::sha2_256()
3123 .apply(Term::var("identity").apply(Term::var("x")))
3124 .lambda("x")
3125 .apply(Term::byte_string(vec![]).delay())
3126 .lambda(NO_INLINE)
3127 .lambda("identity")
3128 .apply(Term::var("y").lambda("y").lambda(NO_INLINE)),
3129 };
3130
3131 let expected = Program {
3132 version: (1, 0, 0),
3133 term: Term::sha2_256()
3134 .apply(Term::var("x"))
3135 .lambda("x")
3136 .apply(Term::byte_string(vec![]).delay())
3137 .lambda(NO_INLINE),
3138 };
3139
3140 compare_optimization(expected, program, |p| {
3141 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3142 term.identity_reducer(id, arg_stack, scope, context);
3143 })
3144 });
3145 }
3146
3147 #[test]
3148 fn inline_reduce_delay_sha() {
3149 let program: Program<Name> = Program {
3150 version: (1, 0, 0),
3151 term: Term::sha2_256()
3152 .apply(Term::var("x"))
3153 .lambda("x")
3154 .apply(Term::byte_string(vec![]).delay()),
3155 };
3156
3157 let expected = Program {
3158 version: (1, 0, 0),
3159 term: Term::sha2_256().apply(Term::byte_string(vec![]).delay()),
3160 };
3161
3162 compare_optimization(expected, program, |p| {
3163 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3164 term.inline_reducer(id, arg_stack, scope, context);
3165 })
3166 });
3167 }
3168
3169 #[test]
3170 fn inline_reduce_if_then_else_then() {
3171 let program: Program<Name> = Program {
3172 version: (1, 0, 0),
3173 term: Term::var("__if_then_else_wrapped")
3174 .apply(Term::bool(true))
3175 .apply(Term::sha3_256().apply(Term::var("x")).delay())
3176 .apply(Term::Error.delay())
3177 .force()
3178 .lambda("x")
3179 .apply(Term::sha3_256().apply(Term::byte_string(vec![])))
3180 .lambda("__if_then_else_wrapped")
3181 .apply(Term::Builtin(DefaultFunction::IfThenElse).force()),
3182 };
3183
3184 let expected = Program {
3185 version: (1, 0, 0),
3186 term: Term::var("__if_then_else_wrapped")
3187 .apply(Term::bool(true))
3188 .apply(
3189 Term::sha3_256()
3190 .apply(Term::sha3_256().apply(Term::byte_string(vec![])))
3191 .delay(),
3192 )
3193 .apply(Term::Error.delay())
3194 .force()
3195 .lambda("__if_then_else_wrapped")
3196 .apply(Term::Builtin(DefaultFunction::IfThenElse).force()),
3197 };
3198
3199 compare_optimization(expected, program, |p| {
3200 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3201 term.inline_reducer(id, arg_stack, scope, context);
3202 })
3203 });
3204 }
3205
3206 #[test]
3207 fn inline_reduce_if_then_else_else() {
3208 let program: Program<Name> = Program {
3209 version: (1, 0, 0),
3210 term: Term::var("__if_then_else_wrapped")
3211 .apply(Term::bool(true))
3212 .apply(Term::Error.delay())
3213 .apply(Term::sha3_256().apply(Term::var("x")).delay())
3214 .force()
3215 .lambda("x")
3216 .apply(Term::sha3_256().apply(Term::byte_string(vec![])))
3217 .lambda("__if_then_else_wrapped")
3218 .apply(Term::Builtin(DefaultFunction::IfThenElse).force()),
3219 };
3220
3221 let expected = Program {
3222 version: (1, 0, 0),
3223 term: Term::var("__if_then_else_wrapped")
3224 .apply(Term::bool(true))
3225 .apply(Term::Error.delay())
3226 .apply(
3227 Term::sha3_256()
3228 .apply(Term::sha3_256().apply(Term::byte_string(vec![])))
3229 .delay(),
3230 )
3231 .force()
3232 .lambda("__if_then_else_wrapped")
3233 .apply(Term::Builtin(DefaultFunction::IfThenElse).force()),
3234 };
3235
3236 compare_optimization(expected, program, |p| {
3237 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3238 term.inline_reducer(id, arg_stack, scope, context);
3239 })
3240 });
3241 }
3242
3243 #[test]
3244 fn inline_reduce_0_occurrence() {
3245 let program: Program<Name> = Program {
3246 version: (1, 0, 0),
3247 term: Term::sha2_256()
3248 .lambda("x")
3249 .apply(Term::byte_string(vec![]).delay()),
3250 };
3251
3252 let expected = Program {
3253 version: (1, 0, 0),
3254 term: Term::sha2_256(),
3255 };
3256
3257 compare_optimization(expected, program, |p| {
3258 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3259 term.inline_reducer(id, arg_stack, scope, context);
3260 })
3261 });
3262 }
3263
3264 #[test]
3265 fn wrap_data_reduce_i_data() {
3266 let program: Program<Name> = Program {
3267 version: (1, 0, 0),
3268 term: Term::equals_data()
3269 .apply(Term::i_data().apply(Term::un_i_data().apply(Term::Constant(
3270 Constant::Data(PlutusData::BigInt(BigInt::Int(5.into()))).into(),
3271 ))))
3272 .apply(Term::i_data().apply(Term::integer(1.into())))
3273 .lambda("x"),
3274 };
3275
3276 let expected = Program {
3277 version: (1, 0, 0),
3278 term: Term::equals_data()
3279 .apply(Term::Constant(
3280 Constant::Data(PlutusData::BigInt(BigInt::Int(5.into()))).into(),
3281 ))
3282 .apply(Term::data(Data::integer(1.into())))
3283 .lambda("x"),
3284 };
3285
3286 compare_optimization(expected, program, |p| {
3287 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3288 term.cast_data_reducer(id, arg_stack, scope, context);
3289 })
3290 });
3291 }
3292
3293 #[test]
3294 fn wrap_data_reduce_un_i_data() {
3295 let program: Program<Name> = Program {
3296 version: (1, 0, 0),
3297 term: Term::equals_integer()
3298 .apply(Term::un_i_data().apply(Term::i_data().apply(Term::integer(1.into()))))
3299 .apply(Term::un_i_data().apply(Term::Constant(
3300 Constant::Data(PlutusData::BigInt(BigInt::Int(5.into()))).into(),
3301 )))
3302 .lambda("x"),
3303 };
3304
3305 let expected = Program {
3306 version: (1, 0, 0),
3307 term: Term::equals_integer()
3308 .apply(Term::integer(1.into()))
3309 .apply(Term::integer(5.into()))
3310 .lambda("x"),
3311 };
3312
3313 compare_optimization(expected, program, |p| {
3314 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3315 term.cast_data_reducer(id, arg_stack, scope, context);
3316 })
3317 });
3318 }
3319
3320 #[test]
3321 fn curry_reducer_test_1() {
3322 let program: Program<Name> = Program {
3323 version: (1, 0, 0),
3324 term: Term::add_integer()
3325 .apply(Term::var("x"))
3326 .apply(Term::integer(1.into()))
3327 .lambda("x")
3328 .apply(
3329 Term::add_integer()
3330 .apply(Term::integer(1.into()))
3331 .apply(Term::var("y")),
3332 )
3333 .lambda("y")
3334 .apply(
3335 Term::add_integer()
3336 .apply(Term::var("g"))
3337 .apply(Term::integer(1.into())),
3338 )
3339 .lambda("g"),
3340 };
3341
3342 let expected = Program {
3343 version: (1, 0, 0),
3344 term: Term::var("add_one_curried")
3345 .apply(Term::var("x"))
3346 .lambda("x")
3347 .apply(Term::var("add_one_curried").apply(Term::var("y")))
3348 .lambda("y")
3349 .apply(Term::var("add_one_curried").apply(Term::var("g")))
3350 .lambda("add_one_curried")
3351 .apply(Term::add_integer().apply(Term::integer(1.into())))
3352 .lambda("g"),
3353 };
3354
3355 compare_optimization(expected, program, |p| p.builtin_curry_reducer());
3356 }
3357
3358 #[test]
3359 fn curry_reducer_test_2() {
3360 let program: Program<Name> = Program {
3361 version: (1, 0, 0),
3362 term: Term::add_integer()
3363 .apply(Term::var("x"))
3364 .apply(Term::integer(1.into()))
3365 .lambda("x")
3366 .apply(
3367 Term::add_integer()
3368 .apply(Term::integer(1.into()))
3369 .apply(Term::var("y")),
3370 )
3371 .lambda("y")
3372 .apply(Term::integer(5.into())),
3373 };
3374
3375 let expected = Program {
3376 version: (1, 0, 0),
3377 term: Term::add_integer()
3378 .apply(Term::var("x"))
3379 .apply(Term::integer(1.into()))
3380 .lambda("x")
3381 .apply(
3382 Term::add_integer()
3383 .apply(Term::integer(1.into()))
3384 .apply(Term::var("y")),
3385 )
3386 .lambda("y")
3387 .apply(Term::integer(5.into())),
3388 };
3389
3390 compare_optimization(expected, program, |p| p.builtin_curry_reducer());
3391 }
3392
3393 #[test]
3394 fn curry_reducer_test_3() {
3395 let program: Program<Name> = Program {
3396 version: (1, 0, 0),
3397 term: Term::var("equivalence")
3398 .lambda("equivalence")
3399 .apply(
3400 Term::equals_integer()
3401 .apply(Term::integer(0.into()))
3402 .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("tuple_index_0")))
3403 .if_then_else(
3404 Term::equals_integer()
3405 .apply(Term::integer(0.into()))
3406 .apply(
3407 Term::var(CONSTR_INDEX_EXPOSER)
3408 .apply(Term::var("tuple_index_1")),
3409 )
3410 .if_then_else(
3411 Term::equals_integer()
3412 .apply(
3413 Term::subtract_integer()
3414 .apply(Term::var("x2"))
3415 .apply(Term::var("x1")),
3416 )
3417 .apply(Term::integer(0.into()))
3418 .delayed_if_then_else(
3419 Term::equals_integer()
3420 .apply(
3421 Term::subtract_integer()
3422 .apply(Term::var("y2"))
3423 .apply(Term::var("y1")),
3424 )
3425 .apply(Term::integer(0.into())),
3426 Term::bool(false),
3427 )
3428 .lambda("x2")
3429 .apply(Term::un_i_data().apply(
3430 Term::fst_pair().apply(Term::var("field_0_pair")),
3431 ))
3432 .lambda("y2")
3433 .apply(Term::un_i_data().apply(
3434 Term::snd_pair().apply(Term::var("field_0_pair")),
3435 ))
3436 .lambda("field_0_pair")
3437 .apply(
3438 Term::mk_pair_data()
3439 .apply(
3440 Term::head_list()
3441 .apply(Term::var("__list_data")),
3442 )
3443 .apply(Term::head_list().apply(Term::var("__tail")))
3444 .lambda("__tail")
3445 .apply(
3446 Term::tail_list()
3447 .apply(Term::var("__list_data")),
3448 )
3449 .lambda("__list_data")
3450 .apply(
3451 Term::unlist_data().apply(
3452 Term::head_list().apply(Term::var(
3453 "tuple_index_1_fields",
3454 )),
3455 ),
3456 ),
3457 )
3458 .lambda("tuple_index_1_fields")
3459 .apply(
3460 Term::var(CONSTR_FIELDS_EXPOSER)
3461 .apply(Term::var("tuple_index_1")),
3462 )
3463 .delay(),
3464 Term::var("clauses_delayed"),
3465 )
3466 .force()
3467 .lambda("x1")
3468 .apply(
3469 Term::un_i_data()
3470 .apply(Term::fst_pair().apply(Term::var("field_0_pair"))),
3471 )
3472 .lambda("y1")
3473 .apply(
3474 Term::un_i_data()
3475 .apply(Term::snd_pair().apply(Term::var("field_0_pair"))),
3476 )
3477 .lambda("field_0_pair")
3478 .apply(
3479 Term::mk_pair_data()
3480 .apply(Term::head_list().apply(Term::var("__list_data")))
3481 .apply(Term::head_list().apply(Term::var("__tail")))
3482 .lambda("__tail")
3483 .apply(Term::tail_list().apply(Term::var("__list_data")))
3484 .lambda("__list_data")
3485 .apply(
3486 Term::unlist_data().apply(
3487 Term::head_list()
3488 .apply(Term::var("tuple_index_0_fields")),
3489 ),
3490 ),
3491 )
3492 .lambda("tuple_index_0_fields")
3493 .apply(
3494 Term::var(CONSTR_FIELDS_EXPOSER)
3495 .apply(Term::var("tuple_index_0")),
3496 )
3497 .delay(),
3498 Term::var("clauses_delayed"),
3499 )
3500 .force()
3501 .lambda("clauses_delayed")
3502 .apply(
3503 Term::equals_integer()
3504 .apply(Term::integer(1.into()))
3505 .apply(
3506 Term::var(CONSTR_INDEX_EXPOSER)
3507 .apply(Term::var("tuple_index_0")),
3508 )
3509 .if_then_else(
3510 Term::equals_integer()
3511 .apply(Term::integer(1.into()))
3512 .apply(
3513 Term::var(CONSTR_INDEX_EXPOSER)
3514 .apply(Term::var("tuple_index_1")),
3515 )
3516 .if_then_else(
3517 Term::bool(true).delay(),
3518 Term::var("clauses_delayed"),
3519 )
3520 .force()
3521 .delay(),
3522 Term::var("clauses_delayed"),
3523 )
3524 .force()
3525 .lambda("clauses_delayed")
3526 .apply(
3527 Term::equals_integer()
3528 .apply(Term::integer(1.into()))
3529 .apply(
3530 Term::var(CONSTR_INDEX_EXPOSER)
3531 .apply(Term::var("tuple_index_0")),
3532 )
3533 .if_then_else(
3534 Term::equals_integer()
3535 .apply(Term::integer(0.into()))
3536 .apply(
3537 Term::var(CONSTR_INDEX_EXPOSER)
3538 .apply(Term::var("tuple_index_1")),
3539 )
3540 .if_then_else(
3541 Term::bool(false).delay(),
3542 Term::var("clauses_delayed"),
3543 )
3544 .force()
3545 .delay(),
3546 Term::var("clauses_delayed"),
3547 )
3548 .force()
3549 .lambda("clauses_delayed")
3550 .apply(Term::bool(false).delay())
3551 .delay(),
3552 )
3553 .delay(),
3554 )
3555 .lambda("tuple_index_0")
3556 .apply(Term::fst_pair().apply(Term::var("input")))
3557 .lambda("tuple_index_1")
3558 .apply(Term::snd_pair().apply(Term::var("input")))
3559 .lambda("input")
3560 .apply(
3561 Term::mk_pair_data()
3562 .apply(Term::var("ec1"))
3563 .apply(Term::var("ec2")),
3564 )
3565 .lambda("ec2")
3566 .lambda("ec1"),
3567 )
3568 .apply(Term::data(Data::constr(1, vec![])))
3569 .apply(Term::data(Data::constr(1, vec![])))
3570 .delayed_if_then_else(
3571 Term::bool(true),
3572 Term::bool(true).if_then_else(Term::bool(false), Term::bool(true)),
3573 )
3574 .constr_index_exposer()
3575 .constr_fields_exposer(),
3576 };
3577
3578 let expected = Program {
3579 version: (1, 0, 0),
3580 term: Term::var("equivalence")
3581 .lambda("equivalence")
3582 .apply(
3583 Term::var("equals_integer_0_curried")
3584 .apply(Term::var(CONSTR_INDEX_EXPOSER).apply(Term::var("tuple_index_0")))
3585 .if_then_else(
3586 Term::var("equals_integer_0_curried")
3587 .apply(
3588 Term::var(CONSTR_INDEX_EXPOSER)
3589 .apply(Term::var("tuple_index_1")),
3590 )
3591 .if_then_else(
3592 Term::var("equals_integer_0_curried")
3593 .apply(
3594 Term::subtract_integer()
3595 .apply(Term::var("x2"))
3596 .apply(Term::var("x1")),
3597 )
3598 .delayed_if_then_else(
3599 Term::var("equals_integer_0_curried").apply(
3600 Term::subtract_integer()
3601 .apply(Term::var("y2"))
3602 .apply(Term::var("y1")),
3603 ),
3604 Term::bool(false),
3605 )
3606 .lambda("x2")
3607 .apply(Term::un_i_data().apply(
3608 Term::fst_pair().apply(Term::var("field_0_pair")),
3609 ))
3610 .lambda("y2")
3611 .apply(Term::un_i_data().apply(
3612 Term::snd_pair().apply(Term::var("field_0_pair")),
3613 ))
3614 .lambda("field_0_pair")
3615 .apply(
3616 Term::mk_pair_data()
3617 .apply(
3618 Term::head_list()
3619 .apply(Term::var("__list_data")),
3620 )
3621 .apply(Term::head_list().apply(Term::var("__tail")))
3622 .lambda("__tail")
3623 .apply(
3624 Term::tail_list()
3625 .apply(Term::var("__list_data")),
3626 )
3627 .lambda("__list_data")
3628 .apply(
3629 Term::unlist_data().apply(
3630 Term::head_list().apply(Term::var(
3631 "tuple_index_1_fields",
3632 )),
3633 ),
3634 ),
3635 )
3636 .lambda("tuple_index_1_fields")
3637 .apply(
3638 Term::var(CONSTR_FIELDS_EXPOSER)
3639 .apply(Term::var("tuple_index_1")),
3640 )
3641 .delay(),
3642 Term::var("clauses_delayed"),
3643 )
3644 .force()
3645 .lambda("x1")
3646 .apply(
3647 Term::un_i_data()
3648 .apply(Term::fst_pair().apply(Term::var("field_0_pair"))),
3649 )
3650 .lambda("y1")
3651 .apply(
3652 Term::un_i_data()
3653 .apply(Term::snd_pair().apply(Term::var("field_0_pair"))),
3654 )
3655 .lambda("field_0_pair")
3656 .apply(
3657 Term::mk_pair_data()
3658 .apply(Term::head_list().apply(Term::var("__list_data")))
3659 .apply(Term::head_list().apply(Term::var("__tail")))
3660 .lambda("__tail")
3661 .apply(Term::tail_list().apply(Term::var("__list_data")))
3662 .lambda("__list_data")
3663 .apply(
3664 Term::unlist_data().apply(
3665 Term::head_list()
3666 .apply(Term::var("tuple_index_0_fields")),
3667 ),
3668 ),
3669 )
3670 .lambda("tuple_index_0_fields")
3671 .apply(
3672 Term::var(CONSTR_FIELDS_EXPOSER)
3673 .apply(Term::var("tuple_index_0")),
3674 )
3675 .delay(),
3676 Term::var("clauses_delayed"),
3677 )
3678 .force()
3679 .lambda("clauses_delayed")
3680 .apply(
3681 Term::var("equals_integer_1_curried")
3682 .apply(
3683 Term::var(CONSTR_INDEX_EXPOSER)
3684 .apply(Term::var("tuple_index_0")),
3685 )
3686 .if_then_else(
3687 Term::var("equals_integer_1_curried")
3688 .apply(
3689 Term::var(CONSTR_INDEX_EXPOSER)
3690 .apply(Term::var("tuple_index_1")),
3691 )
3692 .if_then_else(
3693 Term::bool(true).delay(),
3694 Term::var("clauses_delayed"),
3695 )
3696 .force()
3697 .delay(),
3698 Term::var("clauses_delayed"),
3699 )
3700 .force()
3701 .lambda("clauses_delayed")
3702 .apply(
3703 Term::var("equals_integer_1_curried")
3704 .apply(
3705 Term::var(CONSTR_INDEX_EXPOSER)
3706 .apply(Term::var("tuple_index_0")),
3707 )
3708 .if_then_else(
3709 Term::var("equals_integer_0_curried")
3710 .apply(
3711 Term::var(CONSTR_INDEX_EXPOSER)
3712 .apply(Term::var("tuple_index_1")),
3713 )
3714 .if_then_else(
3715 Term::bool(false).delay(),
3716 Term::var("clauses_delayed"),
3717 )
3718 .force()
3719 .delay(),
3720 Term::var("clauses_delayed"),
3721 )
3722 .force()
3723 .lambda("clauses_delayed")
3724 .apply(Term::bool(false).delay())
3725 .delay(),
3726 )
3727 .lambda("equals_integer_1_curried")
3728 .apply(Term::equals_integer().apply(Term::integer(1.into())))
3729 .delay(),
3730 )
3731 .lambda("equals_integer_0_curried")
3732 .apply(Term::equals_integer().apply(Term::integer(0.into())))
3733 .lambda("tuple_index_0")
3734 .apply(Term::fst_pair().apply(Term::var("input")))
3735 .lambda("tuple_index_1")
3736 .apply(Term::snd_pair().apply(Term::var("input")))
3737 .lambda("input")
3738 .apply(
3739 Term::mk_pair_data()
3740 .apply(Term::var("ec1"))
3741 .apply(Term::var("ec2")),
3742 )
3743 .lambda("ec2")
3744 .lambda("ec1"),
3745 )
3746 .apply(Term::data(Data::constr(1, vec![])))
3747 .apply(Term::data(Data::constr(1, vec![])))
3748 .delayed_if_then_else(
3749 Term::bool(true),
3750 Term::bool(true).if_then_else(Term::bool(false), Term::bool(true)),
3751 )
3752 .constr_index_exposer()
3753 .constr_fields_exposer(),
3754 };
3755
3756 compare_optimization(expected, program, |p| p.builtin_curry_reducer());
3757 }
3758
3759 #[test]
3760 fn curry_reducer_test_4() {
3761 let program: Program<Name> = Program {
3762 version: (1, 0, 0),
3763 term: Term::bool(true)
3764 .delayed_if_then_else(
3765 Term::integer(2.into()),
3766 Term::add_integer()
3767 .apply(
3768 Term::add_integer()
3769 .apply(Term::var("x"))
3770 .apply(Term::var("y")),
3771 )
3772 .apply(Term::var("z"))
3773 .lambda("z")
3774 .apply(
3775 Term::index_bytearray()
3776 .apply(Term::byte_string(vec![
3777 1, 2, 4, 8, 16, 32, 64, 128, 255, 255,
3778 ]))
3779 .apply(Term::integer(35.into())),
3780 )
3781 .lambda("y")
3782 .apply(
3783 Term::index_bytearray()
3784 .apply(Term::byte_string(vec![
3785 1, 2, 4, 8, 16, 32, 64, 128, 255, 255,
3786 ]))
3787 .apply(Term::integer(35.into())),
3788 ),
3789 )
3790 .lambda("x")
3791 .apply(
3792 Term::bool(true).delayed_if_then_else(
3793 Term::integer(1.into()),
3794 Term::index_bytearray()
3795 .apply(Term::byte_string(vec![
3796 1, 2, 4, 8, 16, 32, 64, 128, 255, 255,
3797 ]))
3798 .apply(Term::integer(35.into())),
3799 ),
3800 ),
3801 };
3802
3803 let expected = Program {
3804 version: (1, 0, 0),
3805 term: Term::bool(true)
3806 .delayed_if_then_else(
3807 Term::integer(2.into()),
3808 Term::add_integer()
3809 .apply(
3810 Term::add_integer()
3811 .apply(Term::var("x"))
3812 .apply(Term::var("y")),
3813 )
3814 .apply(Term::var("z"))
3815 .lambda("z")
3816 .apply(Term::var("good_curry").apply(Term::integer(35.into())))
3817 .lambda("y")
3818 .apply(Term::var("good_curry").apply(Term::integer(35.into()))),
3819 )
3820 .lambda("x")
3821 .apply(Term::bool(true).delayed_if_then_else(
3822 Term::integer(1.into()),
3823 Term::var("good_curry").apply(Term::integer(35.into())),
3824 ))
3825 .lambda("good_curry")
3826 .apply(Term::index_bytearray().apply(Term::byte_string(vec![
3827 1, 2, 4, 8, 16, 32, 64, 128, 255, 255,
3828 ]))),
3829 };
3830
3831 compare_optimization(expected, program, |p| p.builtin_curry_reducer());
3832 }
3833
3834 #[test]
3835 fn case_constr_apply_test_1() {
3836 let program: Program<Name> = Program {
3837 version: (1, 1, 0),
3838 term: Term::add_integer()
3839 .apply(Term::integer(0.into()))
3840 .apply(Term::integer(0.into()))
3841 .apply(Term::integer(0.into()))
3842 .apply(Term::integer(0.into()))
3843 .apply(Term::integer(0.into()))
3844 .apply(Term::integer(0.into())),
3845 };
3846
3847 let expected = Program {
3848 version: (1, 1, 0),
3849 term: Term::constr(
3850 0,
3851 vec![
3852 Term::integer(0.into()),
3853 Term::integer(0.into()),
3854 Term::integer(0.into()),
3855 Term::integer(0.into()),
3856 Term::integer(0.into()),
3857 Term::integer(0.into()),
3858 ],
3859 )
3860 .case(vec![Term::add_integer()]),
3861 };
3862
3863 compare_optimization(expected, program, |p| {
3864 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3865 term.case_constr_apply_reducer(id, arg_stack, scope, context);
3866 })
3867 });
3868 }
3869
3870 #[test]
3871 fn case_constr_apply_test_2() {
3872 let program: Program<Name> = Program {
3873 version: (1, 1, 0),
3874 term: Term::add_integer()
3875 .apply(Term::integer(0.into()))
3876 .apply(Term::integer(0.into())),
3877 };
3878
3879 let expected = Program {
3880 version: (1, 1, 0),
3881 term: Term::add_integer()
3882 .apply(Term::integer(0.into()))
3883 .apply(Term::integer(0.into())),
3884 };
3885
3886 compare_optimization(expected, program, |p| {
3887 p.run_one_opt(true, &mut |id, term, arg_stack, scope, context| {
3888 term.case_constr_apply_reducer(id, arg_stack, scope, context);
3889 })
3890 });
3891 }
3892}