tasm_lib/list/higher_order/
map.rsuse std::collections::HashMap;
use itertools::Itertools;
use rand::random;
use rand::rngs::StdRng;
use rand::Rng;
use rand::SeedableRng;
use strum::EnumCount;
use tasm_lib::list::higher_order::inner_function::InnerFunction;
use tasm_lib::structure::tasm_object::DEFAULT_MAX_DYN_FIELD_SIZE;
use triton_vm::isa;
use triton_vm::isa::op_stack::OpStackElement;
use triton_vm::isa::parser::tokenize;
use triton_vm::isa::triton_asm;
use triton_vm::prelude::*;
use crate::data_type::DataType;
use crate::library::Library;
use crate::list::new::New;
use crate::list::push::Push;
use crate::prelude::BasicSnippet;
use crate::rust_shadowing_helper_functions::dyn_malloc::dynamic_allocator;
use crate::rust_shadowing_helper_functions::list::insert_random_list;
use crate::rust_shadowing_helper_functions::list::list_get;
use crate::rust_shadowing_helper_functions::list::list_get_length;
use crate::rust_shadowing_helper_functions::list::list_pointer_to_elem_pointer;
use crate::rust_shadowing_helper_functions::list::list_set;
use crate::rust_shadowing_helper_functions::list::list_set_length;
use crate::snippet_bencher::BenchmarkCase;
use crate::traits::deprecated_snippet::DeprecatedSnippet;
use crate::traits::function::Function;
use crate::traits::function::FunctionInitialState;
const INNER_FN_INCORRECT_NUM_INPUTS: &str = "Inner function in `map` only works with *one* input. \
Use a tuple as a workaround.";
const INNER_FN_INCORRECT_INPUT_DYN_LEN: &str = "An input type of dynamic length to `map`s inner \
function must be a tuple of form `(bfe, _)`.";
pub type Map = ChainMap<1>;
pub struct ChainMap<const NUM_INPUT_LISTS: usize> {
f: InnerFunction,
}
impl<const NUM_INPUT_LISTS: usize> ChainMap<NUM_INPUT_LISTS> {
pub const NUM_INTERNAL_REGISTERS: usize = {
assert!(NUM_INPUT_LISTS <= Self::MAX_NUM_INPUT_LISTS);
3 + NUM_INPUT_LISTS
};
const MAX_NUM_INPUT_LISTS: usize = OpStackElement::COUNT - 1;
pub fn new(f: InnerFunction) -> Self {
if let Some(input_len) = f.domain().static_length() {
assert!(input_len < OpStackElement::COUNT);
} else {
let DataType::Tuple(tuple) = f.domain() else {
panic!("{INNER_FN_INCORRECT_INPUT_DYN_LEN}");
};
let [_, DataType::Bfe] = tuple[..] else {
panic!("{INNER_FN_INCORRECT_INPUT_DYN_LEN}");
};
}
let output_len = f
.range()
.static_length()
.expect("output type's encoding length must be static");
assert!(output_len + 1 < OpStackElement::COUNT);
Self { f }
}
}
impl<const NUM_INPUT_LISTS: usize> BasicSnippet for ChainMap<NUM_INPUT_LISTS> {
fn inputs(&self) -> Vec<(DataType, String)> {
let list_type = DataType::List(Box::new(self.f.domain()));
(0..NUM_INPUT_LISTS)
.map(|i| (list_type.clone(), format!("*input_list_{i}")))
.collect_vec()
}
fn outputs(&self) -> Vec<(DataType, String)> {
let list_type = DataType::List(Box::new(self.f.range()));
vec![(list_type, "*output_list".to_string())]
}
fn entrypoint(&self) -> String {
let maybe_chain_surely_map = if NUM_INPUT_LISTS == 1 {
"map".to_string()
} else {
format!("chain_map_{NUM_INPUT_LISTS}")
};
let f_label = self.f.entrypoint();
format!("tasmlib_list_higher_order_u32_{maybe_chain_surely_map}_{f_label}")
}
fn code(&self, library: &mut Library) -> Vec<LabelledInstruction> {
if self.f.domain().static_length().is_some() {
self.code_for_static_len_input_type(library)
} else {
self.code_for_dyn_len_input_type(library)
}
}
}
struct DecomposedInnerFunction<'body> {
exec_or_call: Vec<LabelledInstruction>,
fn_body: Option<&'body [LabelledInstruction]>,
}
impl<const NUM_INPUT_LISTS: usize> ChainMap<NUM_INPUT_LISTS> {
fn code_for_static_len_input_type(&self, library: &mut Library) -> Vec<LabelledInstruction> {
let input_type = self.f.domain();
let output_type = self.f.range();
assert!(input_type.static_length().is_some());
let new_list = library.import(Box::new(New::new(output_type.clone())));
let inner_fn = self.decompose_inner_fn(library);
let entrypoint = self.entrypoint();
let main_loop_fn = format!("{entrypoint}_loop");
let mul_elem_size = |n| match n {
1 => triton_asm!(),
n => triton_asm!(push {n} mul),
};
let adjust_output_list_pointer = match output_type.stack_size() {
0 | 1 => triton_asm!(),
n => triton_asm!(addi {-(n as i32 - 1)}),
};
let main_loop_body = triton_asm! {
dup 2 dup 1 eq
skiz return
{&input_type.read_value_from_memory_leave_pointer()}
place {input_type.stack_size()}
{&inner_fn.exec_or_call}
pick {output_type.stack_size() + 1}
{&output_type.write_value_to_memory_leave_pointer()}
addi {-2 * output_type.stack_size() as i32}
place 1 recurse
};
let map_one_list = triton_asm! {
read_mem 1
addi 1 pick 2
read_mem 1
addi 1 dup 1
{&mul_elem_size(input_type.stack_size())}
dup 1
add pick 2
pick 4 add dup 0
pick 4
write_mem 1
addi -1 dup 0
place 4 pick 1
{&mul_elem_size(output_type.stack_size())}
add {&adjust_output_list_pointer}
place 1 call {main_loop_fn}
hint used_list: Pointer = stack[2]
pop 2
place {NUM_INPUT_LISTS}
};
let map_all_lists = vec![map_one_list; NUM_INPUT_LISTS].concat();
triton_asm! {
{entrypoint}:
call {new_list}
hint chain_map_output_list: Pointer = stack[0]
{&map_all_lists}
place {NUM_INPUT_LISTS}
{&Self::pop_input_lists()}
return
{main_loop_fn}:
{&main_loop_body}
{&inner_fn.fn_body.unwrap_or_default()}
}
}
fn code_for_dyn_len_input_type(&self, library: &mut Library) -> Vec<LabelledInstruction> {
let input_type = self.f.domain();
let output_type = self.f.range();
assert!(input_type.static_length().is_none());
let new_list = library.import(Box::new(New::new(output_type.clone())));
let push = library.import(Box::new(Push::new(output_type.clone())));
let inner_fn = self.decompose_inner_fn(library);
let entrypoint = self.entrypoint();
let main_loop_fn = format!("{entrypoint}_loop");
let main_loop_body = triton_asm! {
dup 3
dup 3
eq
skiz return
read_mem 1 hint item_len = stack[0]
addi 2 push {DEFAULT_MAX_DYN_FIELD_SIZE}
hint default_max_dyn_field_size = stack[0]
dup 2 lt
assert dup 1
dup 1
add
place 2
place 1 {&inner_fn.exec_or_call}
dup {output_type.stack_size() + 1}
place {output_type.stack_size()}
call {push}
pick 3
addi 1
place 3
recurse
};
let map_one_list = triton_asm! {
pick 1
read_mem 1 hint in_list_len = stack[1]
addi 2 pick 2
place 1
push 0 hint filler = stack[0]
place 3
push 0 hint index = stack[0]
place 3
call {main_loop_fn}
pick 1
place 4
pop 3
place {NUM_INPUT_LISTS}
};
let map_all_lists = vec![map_one_list; NUM_INPUT_LISTS].concat();
triton_asm! {
{entrypoint}:
call {new_list}
hint chain_map_output_list: Pointer = stack[0]
{&map_all_lists}
place {NUM_INPUT_LISTS}
{&Self::pop_input_lists()}
return
{main_loop_fn}:
{&main_loop_body}
{&inner_fn.fn_body.unwrap_or_default()}
}
}
fn decompose_inner_fn(&self, library: &mut Library) -> DecomposedInnerFunction {
let exec_or_call = match &self.f {
InnerFunction::RawCode(code) => {
code.inlined_body()
.unwrap_or(triton_asm!(call {code.entrypoint()}))
}
InnerFunction::DeprecatedSnippet(sn) => {
assert_eq!(1, sn.input_types().len(), "{INNER_FN_INCORRECT_NUM_INPUTS}");
let fn_body = sn.function_code(library);
let (_, instructions) = tokenize(&fn_body).unwrap();
let labelled_instructions = isa::parser::to_labelled_instructions(&instructions);
let label = library.explicit_import(&sn.entrypoint_name(), &labelled_instructions);
triton_asm!(call { label })
}
InnerFunction::BasicSnippet(snippet) => {
assert_eq!(1, snippet.inputs().len(), "{INNER_FN_INCORRECT_NUM_INPUTS}");
let labelled_instructions = snippet.annotated_code(library);
let label = library.explicit_import(&snippet.entrypoint(), &labelled_instructions);
triton_asm!(call { label })
}
InnerFunction::NoFunctionBody(lnat) => {
triton_asm!(call { lnat.label_name })
}
};
let fn_body = if let InnerFunction::RawCode(c) = &self.f {
c.inlined_body().is_none().then_some(c.function.as_slice())
} else {
None
};
DecomposedInnerFunction {
exec_or_call,
fn_body,
}
}
fn pop_input_lists() -> Vec<LabelledInstruction> {
match NUM_INPUT_LISTS {
0 => triton_asm!(),
i @ 1..=5 => triton_asm!(pop { i }),
i @ 6..=10 => triton_asm!(pop 5 pop { i - 5 }),
i @ 11..=15 => triton_asm!(pop 5 pop 5 pop { i - 10 }),
_ => unreachable!("see compile time checks for `NUM_INPUT_LISTS`"),
}
}
fn init_state(
&self,
environment_args: impl IntoIterator<Item = BFieldElement>,
list_lengths: [u16; NUM_INPUT_LISTS],
) -> FunctionInitialState {
let input_type = self.f.domain();
let mut stack = self.init_stack_for_isolated_run();
let mut memory = HashMap::default();
stack.extend(environment_args);
for list_length in list_lengths {
let list_length = usize::from(list_length);
let list_pointer = dynamic_allocator(&mut memory);
insert_random_list(&input_type, list_pointer, list_length, &mut memory);
stack.push(list_pointer);
}
FunctionInitialState { stack, memory }
}
}
impl<const NUM_INPUT_LISTS: usize> Function for ChainMap<NUM_INPUT_LISTS> {
fn rust_shadow(
&self,
stack: &mut Vec<BFieldElement>,
memory: &mut HashMap<BFieldElement, BFieldElement>,
) {
let input_type = self.f.domain();
let output_type = self.f.range();
New::new(output_type.clone()).rust_shadowing(stack, vec![], vec![], memory);
let output_list_pointer = stack.pop().unwrap();
let input_list_pointers = (0..NUM_INPUT_LISTS)
.map(|_| stack.pop().unwrap())
.collect_vec();
let buffer = (0..Self::NUM_INTERNAL_REGISTERS).map(|_| random::<BFieldElement>());
stack.extend(buffer);
let mut total_output_len = 0;
for input_list_pointer in input_list_pointers {
let input_list_len = list_get_length(input_list_pointer, memory);
for i in (0..input_list_len).rev() {
if input_type.static_length().is_some() {
let elem = list_get(input_list_pointer, i, memory, input_type.stack_size());
stack.extend(elem.into_iter().rev());
} else {
let (len, ptr) =
list_pointer_to_elem_pointer(input_list_pointer, i, memory, &input_type);
stack.push(ptr);
stack.push(bfe!(len as u64));
};
self.f.apply(stack, memory);
let elem = (0..output_type.stack_size())
.map(|_| stack.pop().unwrap())
.collect();
list_set(output_list_pointer, total_output_len + i, elem, memory);
}
total_output_len += input_list_len;
}
for _ in 0..Self::NUM_INTERNAL_REGISTERS {
stack.pop();
}
stack.push(output_list_pointer);
list_set_length(output_list_pointer, total_output_len, memory);
}
fn pseudorandom_initial_state(
&self,
seed: [u8; 32],
bench: Option<BenchmarkCase>,
) -> FunctionInitialState {
let mut rng = StdRng::from_seed(seed);
let environment_args = rng.gen::<[BFieldElement; OpStackElement::COUNT]>();
let list_lengths = match bench {
None => rng.gen::<[u8; NUM_INPUT_LISTS]>(),
Some(BenchmarkCase::CommonCase) => [10; NUM_INPUT_LISTS],
Some(BenchmarkCase::WorstCase) => [100; NUM_INPUT_LISTS],
};
let list_lengths = list_lengths.map(Into::into);
self.init_state(environment_args, list_lengths)
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use num_traits::Zero;
use proptest_arbitrary_interop::arb;
use test_strategy::proptest;
use triton_vm::twenty_first::math::other::random_elements;
use triton_vm::twenty_first::prelude::*;
use super::*;
use crate::arithmetic;
use crate::data_type::DataType;
use crate::library::Library;
use crate::list::higher_order::inner_function::InnerFunction;
use crate::list::higher_order::inner_function::RawCode;
use crate::neptune::mutator_set::get_swbf_indices::u32_to_u128_add_another_u128;
use crate::test_helpers::test_rust_equivalence_given_execution_state;
use crate::traits::function::ShadowedFunction;
use crate::traits::rust_shadow::RustShadow;
use crate::twenty_first::prelude::x_field_element::EXTENSION_DEGREE;
use crate::InitVmState;
use crate::VmHasher;
#[derive(Debug, Clone)]
pub(crate) struct TestHashXFieldElement;
impl DeprecatedSnippet for TestHashXFieldElement {
fn entrypoint_name(&self) -> String {
"test_hash_xfield_element".to_string()
}
fn input_field_names(&self) -> Vec<String>
where
Self: Sized,
{
vec![
"elem2".to_string(),
"elem1".to_string(),
"elem0".to_string(),
]
}
fn input_types(&self) -> Vec<DataType> {
vec![DataType::Xfe]
}
fn output_field_names(&self) -> Vec<String>
where
Self: Sized,
{
vec![
"digelem4".to_string(),
"digelem3".to_string(),
"digelem2".to_string(),
"digelem1".to_string(),
"digelem0".to_string(),
]
}
fn output_types(&self) -> Vec<DataType> {
vec![DataType::Digest]
}
fn stack_diff(&self) -> isize
where
Self: Sized,
{
2
}
fn function_code(&self, library: &mut Library) -> String {
let entrypoint = self.entrypoint_name();
let unused_import = library.import(Box::new(arithmetic::u32::safeadd::Safeadd));
triton_asm!(
{entrypoint}:
push 0 push 0
push 0 push 0
push 0 push 0 push 1 pick 9 pick 9 pick 9 push 0
push 0
call {unused_import}
pop 1
sponge_init
sponge_absorb
sponge_squeeze pick 9 pick 9 pick 9 pick 9 pick 9 pop 5 return
)
.iter()
.join("\n")
}
fn crash_conditions(&self) -> Vec<String>
where
Self: Sized,
{
vec![]
}
fn gen_input_states(&self) -> Vec<InitVmState>
where
Self: Sized,
{
vec![InitVmState::with_stack(
[
vec![BFieldElement::zero(); 16],
random_elements::<BFieldElement>(3),
]
.concat(),
)]
}
fn common_case_input_state(&self) -> InitVmState
where
Self: Sized,
{
InitVmState::with_stack(
[
vec![BFieldElement::zero(); 16],
random_elements::<BFieldElement>(3),
]
.concat(),
)
}
fn worst_case_input_state(&self) -> InitVmState
where
Self: Sized,
{
InitVmState::with_stack(
[
vec![BFieldElement::zero(); 16],
random_elements::<BFieldElement>(3),
]
.concat(),
)
}
fn rust_shadowing(
&self,
stack: &mut Vec<BFieldElement>,
_std_in: Vec<BFieldElement>,
_secret_in: Vec<BFieldElement>,
_memory: &mut HashMap<BFieldElement, BFieldElement>,
) where
Self: Sized,
{
let mut xfield_element = vec![];
for _ in 0..EXTENSION_DEGREE {
xfield_element.push(stack.pop().unwrap());
}
let digest = VmHasher::hash_varlen(&xfield_element);
stack.extend(digest.reversed().values());
}
}
fn test_chain_map_with_different_num_input_lists(f: impl Fn() -> InnerFunction) {
ShadowedFunction::new(ChainMap::<0>::new(f())).test();
ShadowedFunction::new(ChainMap::<1>::new(f())).test();
ShadowedFunction::new(ChainMap::<2>::new(f())).test();
ShadowedFunction::new(ChainMap::<3>::new(f())).test();
ShadowedFunction::new(ChainMap::<4>::new(f())).test();
ShadowedFunction::new(ChainMap::<5>::new(f())).test();
ShadowedFunction::new(ChainMap::<7>::new(f())).test();
ShadowedFunction::new(ChainMap::<11>::new(f())).test();
ShadowedFunction::new(ChainMap::<15>::new(f())).test();
}
#[test]
fn prop_test() {
let f = || InnerFunction::DeprecatedSnippet(Box::new(TestHashXFieldElement));
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_identity_on_bfe() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(identity_bfe: return),
DataType::Bfe,
DataType::Bfe,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_bfe_lift() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(bfe_lift: push 0 push 0 pick 2 return),
DataType::Bfe,
DataType::Xfe,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_xfe_get_coeff_0() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(get_0: place 2 pop 2 return),
DataType::Xfe,
DataType::Bfe,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_square_on_bfe() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(square_bfe: dup 0 mul return),
DataType::Bfe,
DataType::Bfe,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_square_plus_n_on_bfe() {
fn test_case<const N: usize>() {
let raw_code = InnerFunction::RawCode(RawCode::new(
triton_asm!(square_plus_n_bfe: dup 0 mul dup {5 + N} add return),
DataType::Bfe,
DataType::Bfe,
));
ShadowedFunction::new(ChainMap::<N>::new(raw_code)).test();
}
test_case::<0>();
test_case::<1>();
test_case::<2>();
test_case::<3>();
test_case::<4>();
test_case::<5>();
test_case::<7>();
test_case::<9>();
test_case::<10>();
}
#[test]
fn test_with_raw_function_square_on_xfe() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(square_xfe: dup 2 dup 2 dup 2 xx_mul return),
DataType::Xfe,
DataType::Xfe,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_xfe_to_digest() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(xfe_to_digest: push 0 push 0 return),
DataType::Xfe,
DataType::Digest,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_digest_to_xfe() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(xfe_to_digest: pop 2 return),
DataType::Digest,
DataType::Xfe,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_with_raw_function_square_on_xfe_plus_another_xfe() {
fn test_case<const N: usize>() {
let offset = ChainMap::<{ N }>::NUM_INTERNAL_REGISTERS;
let raw_code = InnerFunction::RawCode(RawCode::new(
triton_asm!(
square_xfe_plus_another_xfe:
dup 2 dup 2 dup 2 xx_mul
dup {5 + offset}
dup {5 + offset}
dup {5 + offset}
xx_add
return
),
DataType::Xfe,
DataType::Xfe,
));
ShadowedFunction::new(ChainMap::<N>::new(raw_code)).test();
}
test_case::<0>();
test_case::<1>();
test_case::<2>();
test_case::<3>();
test_case::<5>();
test_case::<4>();
test_case::<6>();
test_case::<7>();
}
#[test]
fn test_u32_list_to_unit_list() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(remove_elements: pop 1 return),
DataType::U32,
DataType::Tuple(vec![]),
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_u32_list_to_u64_list() {
let f = || {
InnerFunction::RawCode(RawCode::new(
triton_asm!(duplicate_u32: dup 0 return),
DataType::U32,
DataType::U64,
))
};
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn test_u32_list_to_u128_list_plus_x() {
let raw_code = InnerFunction::RawCode(u32_to_u128_add_another_u128());
let snippet = Map::new(raw_code);
let encoded_u128 = random::<u128>().encode();
let input_list_len = rand::thread_rng().gen_range(0u16..200);
let initial_state = snippet.init_state(encoded_u128, [input_list_len]);
test_rust_equivalence_given_execution_state(
&ShadowedFunction::new(snippet),
initial_state.into(),
);
}
#[proptest(cases = 10)]
fn num_internal_registers_is_correct(#[strategy(arb())] guard: BFieldElement) {
fn test_case<const N: usize>(guard: BFieldElement) {
let offset = ChainMap::<{ N }>::NUM_INTERNAL_REGISTERS;
let raw_code = InnerFunction::RawCode(RawCode::new(
triton_asm! { check_env: dup {offset} push {guard} eq assert return },
DataType::Tuple(vec![]),
DataType::Tuple(vec![]),
));
let snippet = ChainMap::<N>::new(raw_code);
let initial_state = snippet.init_state(vec![guard], [1; N]);
test_rust_equivalence_given_execution_state(
&ShadowedFunction::new(snippet),
initial_state.into(),
);
}
test_case::<0>(guard);
test_case::<1>(guard);
test_case::<2>(guard);
test_case::<3>(guard);
test_case::<4>(guard);
test_case::<5>(guard);
test_case::<6>(guard);
test_case::<7>(guard);
test_case::<8>(guard);
test_case::<9>(guard);
test_case::<10>(guard);
test_case::<11>(guard);
test_case::<12>(guard);
}
#[test]
fn mapping_over_dynamic_length_items_works() {
let f = || {
let list_type = DataType::List(Box::new(DataType::Bfe));
InnerFunction::RawCode(RawCode::new(
triton_asm!(just_forty_twos: pop 2 push 42 return),
DataType::Tuple(vec![list_type, DataType::Bfe]),
DataType::Bfe,
))
};
assert!(f().domain().static_length().is_none());
test_chain_map_with_different_num_input_lists(f);
}
#[test]
fn mapping_over_list_of_lists_writing_their_lengths_works() {
let f = || {
let list_type = DataType::List(Box::new(DataType::Bfe));
InnerFunction::RawCode(RawCode::new(
triton_asm!(write_list_length: pop 1 read_mem 1 pop 1 return),
DataType::Tuple(vec![list_type, DataType::Bfe]),
DataType::Bfe,
))
};
assert!(f().domain().static_length().is_none());
test_chain_map_with_different_num_input_lists(f);
}
}
#[cfg(test)]
mod benches {
use super::tests::TestHashXFieldElement;
use super::*;
use crate::list::higher_order::inner_function::InnerFunction;
use crate::list::higher_order::inner_function::RawCode;
use crate::traits::function::ShadowedFunction;
use crate::traits::rust_shadow::RustShadow;
#[test]
fn map_benchmark() {
let f = InnerFunction::DeprecatedSnippet(Box::new(TestHashXFieldElement));
ShadowedFunction::new(Map::new(f)).bench();
}
#[test]
fn map_with_dyn_items_benchmark() {
let list_type = DataType::List(Box::new(DataType::Bfe));
let f = InnerFunction::RawCode(RawCode::new(
triton_asm!(dyn_length_elements: pop 2 push 42 return),
DataType::Tuple(vec![list_type, DataType::Bfe]),
DataType::Bfe,
));
ShadowedFunction::new(Map::new(f)).bench();
}
}