diff --git a/src/compile/mod.rs b/src/compile/mod.rs index 9f8dffb4..16d4ca9b 100644 --- a/src/compile/mod.rs +++ b/src/compile/mod.rs @@ -486,26 +486,37 @@ fn list_fold<'brand>( f_array: &ProgNode<'brand>, f_fold: &ProgNode<'brand>, ) -> Result, simplicity::types::Error> { - /* (fold f)_(n + 1) : E<2^(n + 1) × A → A + /* (fold f)_(n + 1) : E^<2^(n + 1) × A → A * (fold f)_(n + 1) := OOH ▵ (OIH ▵ IH); - * case (drop (fold f)_n) - * ((IOH ▵ (OH ▵ IIH; f_n)); (fold f)_n) + * (IOH ▵ case IIH (OH ▵ IIH; f_n)); + * (fold f)_n + * + * Uses f_fold exactly once (no DAG sharing) to avoid incorrect pruning. */ let ctx = f_array.inference_context(); + // Rearrange input: ((1+E^n) × E^ u32 { + let (_, sum): (bool, u32) = jet::add_32(elt, acc); + sum +} +"#; + + // Helper: fold that ignores the accumulator and returns the element. + // When folded left-to-right over a non-empty list, the final result equals the LAST element. + const LAST_FN: &str = r#"fn last(elt: u32, _acc: u32) -> u32 { + elt +} +"#; + + fn make_prog(fns: &str, body: &str) -> String { + format!("{fns}\nfn main() {{\n{body}\n}}\n") + } + + fn run(prog: &str) { + TestCase::program_text(Cow::Owned(prog.to_owned())) + .with_witness_values(WitnessValues::default()) + .assert_run_success(); + } + + // ── bound = 2 (list holds 0 or 1 elements) ────────────────────────── + + #[test] + fn list_fold_empty_bound2() { + // Empty list: fold must return the initial accumulator unchanged. + run(&make_prog( + ADD_FN, + r#" let list: List = list![]; + let result: u32 = fold::(list, 77); + assert!(jet::eq_32(result, 77));"#, + )); + } + + #[test] + fn list_fold_single_element_bound2() { + // One-element list: fold must apply f exactly once. + run(&make_prog( + ADD_FN, + r#" let list: List = list![42]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 42));"#, + )); + } + + // ── bound = 4 (list holds 0–3 elements) ────────────────────────────── + + #[test] + fn list_fold_empty_bound4() { + // Empty list with larger bound: must still return initial accumulator. + run(&make_prog( + ADD_FN, + r#" let list: List = list![]; + let result: u32 = fold::(list, 99); + assert!(jet::eq_32(result, 99));"#, + )); + } + + #[test] + fn list_fold_one_element_bound4() { + // Partition: head-block empty, tail = [5]. + run(&make_prog( + ADD_FN, + r#" let list: List = list![5]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 5));"#, + )); + } + + #[test] + fn list_fold_two_elements_bound4() { + // Partition: head-block = [10, 20], tail empty. + run(&make_prog( + ADD_FN, + r#" let list: List = list![10, 20]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 30));"#, + )); + } + + #[test] + fn list_fold_three_elements_bound4() { + // Full list for bound=4: head=[1,2], tail=[3]. + run(&make_prog( + ADD_FN, + r#" let list: List = list![1, 2, 3]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 6));"#, + )); + } + + // ── bound = 8 (list holds 0–7 elements) ────────────────────────────── + + #[test] + fn list_fold_five_elements_bound8() { + // Partition: outer-head=[1,2,3,4], outer-tail partition [5]. + run(&make_prog( + ADD_FN, + r#" let list: List = list![1, 2, 3, 4, 5]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 15));"#, + )); + } + + #[test] + fn list_fold_seven_elements_bound8() { + // Full list for bound=8. + run(&make_prog( + ADD_FN, + r#" let list: List = list![1, 2, 3, 4, 5, 6, 7]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 28));"#, + )); + } + + // ── Ordering tests ──────────────────────────────────────────────────── + // `last` ignores the accumulator and returns the element itself. + // A correct left-to-right fold over [a, b, c] returns the *last* element c. + // A right-to-left fold would return the *first* element a instead. + + #[test] + fn list_fold_order_bound4() { + run(&make_prog( + LAST_FN, + r#" let list: List = list![1, 2, 3]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 3));"#, + )); + } + + #[test] + fn list_fold_order_bound8() { + run(&make_prog( + LAST_FN, + r#" let list: List = list![1, 2, 3, 4, 5, 6, 7]; + let result: u32 = fold::(list, 0); + assert!(jet::eq_32(result, 7));"#, + )); + } +}