use num_traits::Zero;
use tycho_vm_proc::vm_module;

use crate::error::VmResult;
use crate::gas::GasConsumer;
use crate::saferc::SafeRc;
use crate::stack::{RcStackValue, Stack, StackValue, StackValueType};
use crate::state::VmState;

pub struct TupleOps;

#[vm_module]
impl TupleOps {
    #[op(code = "6d", fmt = "PUSHNULL")]
    fn exec_push_null(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        ok!(stack.push_null());
        Ok(0)
    }

    #[op(code = "6e", fmt = "ISNULL")]
    fn exec_is_null(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let top = ok!(stack.pop());
        ok!(stack.push_bool(top.is_null()));
        Ok(0)
    }

    #[op(code = "6f0i", fmt = "TUPLE {i}")]
    fn exec_mktuple(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        make_tuple_impl(stack, i as _, &st.gas)
    }

    #[op(code = "6f1i", fmt = "INDEX {i}")]
    fn exec_tuple_index(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        tuple_index_impl(stack, i as _)
    }

    #[op(code = "6f2i", fmt = "UNTUPLE {i}")]
    fn exec_untuple(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        untuple_impl(stack, i, &st.gas)
    }

    #[op(code = "6f3i", fmt = "UNPACKFIRST {i}")]
    fn exec_untuple_first(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        untuple_first_impl(stack, i, &st.gas)
    }

    #[op(code = "6f4i", fmt = "EXPLODE {i}")]
    fn exec_explode_tuple(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        explode_tuple_impl(stack, i, &st.gas)
    }

    #[op(code = "6f5i", fmt = "SETINDEX {i}")]
    fn exec_tuple_set_index(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        tuple_set_index_impl(stack, i as _, &st.gas)
    }

    #[op(code = "6f6i", fmt = "INDEXQ {i}")]
    fn exec_tuple_quiet_index(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        tuple_index_quiet_impl(stack, i as _)
    }

    #[op(code = "6f7i", fmt = "SETINDEXQ {i}")]
    fn exec_tuple_quiet_set_index(st: &mut VmState, i: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        tuple_set_index_quiet_impl(stack, i as _, &st.gas)
    }

    #[op(code = "6f80", fmt = "TUPLEVAR")]
    fn exec_mktuple_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let n = ok!(stack.pop_smallint_range(0, 255));
        make_tuple_impl(stack, n as _, &st.gas)
    }

    #[op(code = "6f81", fmt = "INDEXVAR")]
    fn exec_tuple_index_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let i = ok!(stack.pop_smallint_range(0, 254));
        tuple_index_impl(stack, i as _)
    }

    #[op(code = "6f82", fmt = "UNTUPLEVAR")]
    fn exec_untuple_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let n = ok!(stack.pop_smallint_range(0, 255));
        untuple_impl(stack, n as _, &st.gas)
    }

    #[op(code = "6f83", fmt = "UNPACKFIRSTVAR")]
    fn exec_untuple_first_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let n = ok!(stack.pop_smallint_range(0, 255));
        untuple_first_impl(stack, n, &st.gas)
    }

    #[op(code = "6f84", fmt = "EXPLODEVAR")]
    fn exec_explode_tuple_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let n = ok!(stack.pop_smallint_range(0, 255));
        explode_tuple_impl(stack, n, &st.gas)
    }

    #[op(code = "6f85", fmt = "SETINDEXVAR")]
    fn exec_tuple_set_index_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let i = ok!(stack.pop_smallint_range(0, 254));
        tuple_set_index_impl(stack, i as usize, &st.gas)
    }

    #[op(code = "6f86", fmt = "INDEXVARQ")]
    fn exec_tuple_quiet_index_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let i = ok!(stack.pop_smallint_range(0, 254));
        tuple_index_quiet_impl(stack, i as _)
    }

    #[op(code = "6f87", fmt = "SETINDEXVARQ")]
    fn exec_tuple_quiet_set_index_var(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let i = ok!(stack.pop_smallint_range(0, 254));
        tuple_set_index_quiet_impl(stack, i as _, &st.gas)
    }

    #[op(code = "6f88", fmt = "TLEN")]
    fn exec_tuple_length(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let tuple = ok!(stack.pop_tuple_range(0, 255));
        ok!(stack.push_int(tuple.len()));
        Ok(0)
    }

    #[op(code = "6f89", fmt = "QTLEN")]
    fn exec_tuple_length_quiet(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let value = ok!(stack.pop());
        ok!(if let Some(value) = value.as_tuple() {
            stack.push_int(value.len())
        } else {
            stack.push_int(-1)
        });
        Ok(0)
    }

    #[op(code = "6f8a", fmt = "ISTUPLE")]
    fn exec_is_tuple(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let is_tuple = ok!(stack.pop()).is_tuple();
        ok!(stack.push_bool(is_tuple));
        Ok(0)
    }

    #[op(code = "6f8b", fmt = "LAST")]
    fn exec_tuple_last(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let tuple = ok!(stack.pop_tuple_range(1, 255));
        ok!(stack.push_raw(SafeRc::clone(
            tuple.last().expect("Should not fail due to range check")
        )));
        Ok(0)
    }

    #[op(code = "6f8c", fmt = "TPUSH")]
    fn exec_tuple_push(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let x = ok!(stack.pop());
        let mut tuple = ok!(stack.pop_tuple_range(0, 254));
        SafeRc::make_mut(&mut tuple).push(x);
        st.gas.try_consume_tuple_gas(tuple.len() as u64)?;
        ok!(stack.push_raw(tuple));
        Ok(0)
    }

    #[op(code = "6f8d", fmt = "TPOP")]
    fn exec_tuple_pop(st: &mut VmState) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let mut tuple = ok!(stack.pop_tuple_range(1, 255));
        let x = SafeRc::make_mut(&mut tuple)
            .pop()
            .expect("Should not fail due to range check");
        st.gas.try_consume_tuple_gas(tuple.len() as u64)?;
        ok!(stack.push_raw(tuple));
        ok!(stack.push_raw(x));
        Ok(0)
    }

    #[op(code = "6f90", fmt = "ZEROSWAPIF", args(c = true, d = false))]
    #[op(code = "6f91", fmt = "ZEROSWAPIFNOT", args(c = false, d = false))]
    #[op(code = "6f92", fmt = "ZEROROTRIF", args(c = true, d = true))]
    #[op(code = "6f93", fmt = "ZEROROTRIFNOT", args(c = false, d = true))]
    fn exec_zero_swap_if(st: &mut VmState, c: bool, d: bool) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let x = ok!(stack.pop_int());
        if x.is_zero() != c {
            ok!(stack.push_zero());
            if d {
                ok!(stack.swap(0, 1));
            }
        }
        ok!(stack.push_raw(x));
        Ok(0)
    }

    #[op(code = "6f94", fmt = "ZEROSWAPIF2", args(c = true, d = false))]
    #[op(code = "6f95", fmt = "ZEROSWAPIFNOT2", args(c = false, d = false))]
    #[op(code = "6f96", fmt = "ZEROROTRIF2", args(c = true, d = true))]
    #[op(code = "6f97", fmt = "ZEROROTRIFNOT2", args(c = false, d = true))]
    fn exec_zero_swap_if2(st: &mut VmState, c: bool, d: bool) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let x = ok!(stack.pop_int());
        if x.is_zero() != c {
            ok!(stack.push_zero());
            ok!(stack.push_zero());
            if d {
                ok!(stack.swap(0, 2));
            }
        }
        ok!(stack.push_raw(x));
        Ok(0)
    }

    #[op(code = "6fa0", fmt = "NULLSWAPIF", args(c = true, d = false))]
    #[op(code = "6fa1", fmt = "NULLSWAPIFNOT", args(c = false, d = false))]
    #[op(code = "6fa2", fmt = "NULLROTRIF", args(c = true, d = true))]
    #[op(code = "6fa3", fmt = "NULLROTRIFNOT", args(c = false, d = true))]
    fn exec_null_swap_if(st: &mut VmState, c: bool, d: bool) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let x = ok!(stack.pop_int());
        if x.is_zero() != c {
            ok!(stack.push_null());
            if d {
                ok!(stack.swap(0, 1));
            }
        }
        ok!(stack.push_raw(x));
        Ok(0)
    }

    #[op(code = "6fa4", fmt = "NULLSWAPIF2", args(c = true, d = false))]
    #[op(code = "6fa5", fmt = "NULLSWAPIFNOT2", args(c = false, d = false))]
    #[op(code = "6fa6", fmt = "NULLROTRIF2", args(c = true, d = true))]
    #[op(code = "6fa7", fmt = "NULLROTRIFNOT2", args(c = false, d = true))]
    fn exec_null_swap_if2(st: &mut VmState, c: bool, d: bool) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let x = ok!(stack.pop_int());
        if x.is_zero() != c {
            ok!(stack.push_null());
            ok!(stack.push_null());
            if d {
                ok!(stack.swap(0, 2));
            }
        }
        ok!(stack.push_raw(x));
        Ok(0)
    }

    #[op(code = "6fb$iijj", fmt = "INDEX2 {i},{j}")]
    fn exec_tuple_index2(st: &mut VmState, i: u32, j: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let tuple = ok!(stack.pop_tuple_range(0, 255));

        let Some(value) = tuple.get(i as usize) else {
            vm_bail!(IntegerOutOfRange {
                actual: i.to_string(),
                min: 0,
                max: tuple.len() as _,
            });
        };

        let value = ok!(index_stack_value_as_tuple(value.as_ref(), j as usize));
        ok!(stack.push_raw(value.clone()));
        Ok(0)
    }

    #[op(code = "6f$11iijjkk", fmt = "INDEX3 {i},{j},{k}")]
    fn exec_tuple_index3(st: &mut VmState, i: u32, j: u32, k: u32) -> VmResult<i32> {
        let stack = SafeRc::make_mut(&mut st.stack);
        let tuple = ok!(stack.pop_tuple_range(0, 255));

        let Some(value) = tuple.get(i as usize) else {
            vm_bail!(IntegerOutOfRange {
                actual: i.to_string(),
                min: 0,
                max: tuple.len() as _,
            });
        };

        let value = ok!(index_stack_value_as_tuple(value.as_ref(), j as usize));
        let value = ok!(index_stack_value_as_tuple(value.as_ref(), k as usize));
        ok!(stack.push_raw(value.clone()));
        Ok(0)
    }
}

fn make_tuple_impl(stack: &mut Stack, n: usize, gas: &GasConsumer) -> VmResult<i32> {
    let Some(offset) = stack.depth().checked_sub(n) else {
        vm_bail!(StackUnderflow(n));
    };
    let tuple = SafeRc::new(stack.items.drain(offset..offset + n).collect::<Vec<_>>());
    gas.try_consume_tuple_gas(tuple.len() as u64)?;
    ok!(stack.push_raw(tuple));
    Ok(0)
}

fn tuple_index_impl(stack: &mut Stack, i: usize) -> VmResult<i32> {
    let tuple = ok!(stack.pop_tuple_range(0, 255));
    vm_ensure!(i < tuple.len(), IntegerOutOfRange {
        actual: i.to_string(),
        min: 0,
        max: tuple.len() as _,
    });
    ok!(stack.push_raw(tuple[i].clone()));
    Ok(0)
}

fn tuple_index_quiet_impl(stack: &mut Stack, i: usize) -> VmResult<i32> {
    let value = ok!(stack.pop_opt_tuple_range(0, 255)).and_then(|t| t.get(i).cloned());
    ok!(stack.push_opt_raw(value));
    Ok(0)
}

fn untuple_impl(stack: &mut Stack, n: u32, gas: &GasConsumer) -> VmResult<i32> {
    let tuple = ok!(stack.pop_tuple_range(n, n));
    ok!(do_explode_tuple(stack, tuple, n as _, gas));
    Ok(0)
}

fn untuple_first_impl(stack: &mut Stack, n: u32, gas: &GasConsumer) -> VmResult<i32> {
    let tuple = ok!(stack.pop_tuple_range(n, 255));
    ok!(do_explode_tuple(stack, tuple, n as _, gas));
    Ok(0)
}

fn explode_tuple_impl(stack: &mut Stack, n: u32, gas: &GasConsumer) -> VmResult<i32> {
    let tuple = ok!(stack.pop_tuple_range(0, n));
    let tuple_len = tuple.len();
    ok!(do_explode_tuple(stack, tuple, tuple_len, gas));
    ok!(stack.push_int(tuple_len));
    Ok(0)
}

fn do_explode_tuple(
    stack: &mut Stack,
    tuple: SafeRc<Vec<RcStackValue>>,
    n: usize,
    gas: &GasConsumer,
) -> VmResult<()> {
    stack.reserve(n);
    match SafeRc::try_unwrap(tuple) {
        Ok(tuple) => {
            for item in tuple.into_iter().take(n) {
                ok!(stack.push_raw(item));
            }
        }
        Err(tuple) => {
            for item in tuple.iter().take(n) {
                ok!(stack.push_raw(item.clone()));
            }
        }
    }
    gas.try_consume_tuple_gas(n as u64)?;
    Ok(())
}

fn tuple_set_index_impl(stack: &mut Stack, i: usize, gas: &GasConsumer) -> VmResult<i32> {
    let x = ok!(stack.pop());
    let mut tuple = ok!(stack.pop_tuple_range(0, 255));
    vm_ensure!(i < tuple.len(), IntegerOutOfRange {
        min: 0,
        max: tuple.len() as _,
        actual: i.to_string()
    });
    gas.try_consume_tuple_gas(tuple.len() as u64)?;
    SafeRc::make_mut(&mut tuple)[i] = x;
    ok!(stack.push_raw(tuple));
    Ok(0)
}

fn tuple_set_index_quiet_impl(stack: &mut Stack, i: usize, gas: &GasConsumer) -> VmResult<i32> {
    let x = ok!(stack.pop());
    let mut tuple = ok!(stack.pop_opt_tuple_range(0, 255));
    vm_ensure!(i < 255, IntegerOutOfRange {
        min: 0,
        max: 255,
        actual: i.to_string()
    });

    let updated_items = match &mut tuple {
        None if x.is_null() => 0,
        None => {
            let mut items = vec![Stack::make_null(); i + 1];
            items[i] = x;
            tuple = Some(SafeRc::new(items));
            i + 1
        }
        Some(items) if i < items.len() => {
            SafeRc::make_mut(items)[i] = x;
            items.len()
        }
        Some(_) if x.is_null() => 0,
        Some(items) => {
            let items = SafeRc::make_mut(items);
            items.resize(i + 1, Stack::make_null());
            items[i] = x;
            i + 1
        }
    };

    gas.try_consume_tuple_gas(updated_items as u64)?;

    ok!(match tuple {
        None => stack.push_null(),
        Some(value) => stack.push_raw(value),
    });

    Ok(0)
}

fn index_stack_value_as_tuple(value: &dyn StackValue, i: usize) -> VmResult<&RcStackValue> {
    let Some(tuple) = value.as_tuple_range(0, 255) else {
        vm_bail!(InvalidType {
            expected: StackValueType::Tuple as _,
            actual: value.raw_ty(),
        });
    };

    let Some(value) = tuple.get(i) else {
        vm_bail!(IntegerOutOfRange {
            actual: i.to_string(),
            min: 0,
            max: tuple.len() as _,
        });
    };

    Ok(value)
}

#[cfg(test)]
mod tests {
    use num_bigint::BigInt;
    use tracing_test::traced_test;

    use crate::saferc::SafeRc;

    #[test]
    #[traced_test]
    fn swap_if_null() {
        assert_run_vm!("NULLSWAPIF", [int 0] => [int 0]);
        assert_run_vm!("NULLSWAPIF", [int -1] => [null, int -1]);

        assert_run_vm!("NULLSWAPIFNOT", [int 0] => [null, int 0]);
        assert_run_vm!("NULLSWAPIFNOT", [int -1] => [int -1]);

        assert_run_vm!("NULLROTRIF", [int 1, int 0] => [int 1, int 0]);
        assert_run_vm!("NULLROTRIF", [int 1, int -1] => [null, int 1, int -1]);

        assert_run_vm!("NULLROTRIFNOT", [int 1, int 0] => [null, int 1, int 0]);
        assert_run_vm!("NULLROTRIFNOT", [int 1, int -1] => [int 1, int -1]);
    }

    #[test]
    #[traced_test]
    fn swap_if_null_2() {
        assert_run_vm!("NULLSWAPIF2", [int 0] => [int 0]);
        assert_run_vm!("NULLSWAPIF NULLSWAPIF", [int 0] => [int 0]);
        assert_run_vm!("NULLSWAPIF2", [int -1] => [null, null, int -1]);
        assert_run_vm!("NULLSWAPIF NULLSWAPIF", [int -1] => [null, null, int -1]);

        assert_run_vm!("NULLSWAPIFNOT2", [int 0] => [null, null, int 0]);
        assert_run_vm!("NULLSWAPIFNOT NULLSWAPIFNOT", [int 0] => [null, null, int 0]);
        assert_run_vm!("NULLSWAPIFNOT2", [int -1] => [int -1]);
        assert_run_vm!("NULLSWAPIFNOT NULLSWAPIFNOT", [int -1] => [int -1]);

        assert_run_vm!("NULLROTRIF2", [int 1, int 0] => [int 1, int 0]);
        assert_run_vm!("NULLROTRIF NULLROTRIF", [int 1, int 0] => [int 1, int 0]);
        assert_run_vm!("NULLROTRIF2", [int 1, int -1] => [null, null, int 1, int -1]);
        assert_run_vm!("NULLROTRIF NULLROTRIF", [int 1, int -1] => [null, null, int 1, int -1]);

        assert_run_vm!("NULLROTRIFNOT2", [int 1, int 0] => [null, null, int 1, int 0]);
        assert_run_vm!("NULLROTRIFNOT NULLROTRIFNOT", [int 1, int 0] => [null, null, int 1, int 0]);
        assert_run_vm!("NULLROTRIFNOT2", [int 1, int -1] => [int 1, int -1]);
        assert_run_vm!("NULLROTRIFNOT NULLROTRIFNOT", [int 1, int -1] => [int 1, int -1]);
    }

    #[test]
    #[traced_test]
    fn index2() {
        let outer_tuple = SafeRc::new(tuple![
            [int 1, int 2, int 3, int 4],
            [int 5, int 6, int 7, int 8],
        ]);

        assert_run_vm!("INDEX2 0, 0", [raw outer_tuple.clone()] => [int 1]);
        assert_run_vm!("INDEX 0 INDEX 0", [raw outer_tuple.clone()] => [int 1]);
        assert_run_vm!("INDEX2 0, 1", [raw outer_tuple.clone()] => [int 2]);
        assert_run_vm!("INDEX 0 INDEX 1", [raw outer_tuple.clone()] => [int 2]);
        assert_run_vm!("INDEX2 0, 2", [raw outer_tuple.clone()] => [int 3]);
        assert_run_vm!("INDEX 0 INDEX 2", [raw outer_tuple.clone()] => [int 3]);
        assert_run_vm!("INDEX2 0, 3", [raw outer_tuple.clone()] => [int 4]);
        assert_run_vm!("INDEX 0 INDEX 3", [raw outer_tuple.clone()] => [int 4]);

        assert_run_vm!("INDEX2 1, 0", [raw outer_tuple.clone()] => [int 5]);
        assert_run_vm!("INDEX 1 INDEX 0", [raw outer_tuple.clone()] => [int 5]);
        assert_run_vm!("INDEX2 1, 1", [raw outer_tuple.clone()] => [int 6]);
        assert_run_vm!("INDEX 1 INDEX 1", [raw outer_tuple.clone()] => [int 6]);
        assert_run_vm!("INDEX2 1, 2", [raw outer_tuple.clone()] => [int 7]);
        assert_run_vm!("INDEX 1 INDEX 2", [raw outer_tuple.clone()] => [int 7]);
        assert_run_vm!("INDEX2 1, 3", [raw outer_tuple.clone()] => [int 8]);
        assert_run_vm!("INDEX 1 INDEX 3", [raw outer_tuple.clone()] => [int 8]);

        assert_run_vm!("INDEX2 0, 2", [[[int 1], [int 5]]] => [int 0], exit_code: 5);
        assert_run_vm!("INDEX2 2, 2", [[[int 1], [int 5]]] => [int 0], exit_code: 5);
    }

    #[test]
    #[traced_test]
    fn index3() {
        let tuple = {
            let mut tuple = Vec::new();
            let mut n = 0;
            for _ in 0..4 {
                let mut row = Vec::new();
                for _ in 0..4 {
                    let mut col = Vec::new();
                    for _ in 0..4 {
                        n += 1;
                        col.push(SafeRc::new_dyn_value(BigInt::from(n)));
                    }
                    row.push(SafeRc::new_dyn_value(col));
                }
                tuple.push(SafeRc::new_dyn_value(row));
            }
            SafeRc::new_dyn_value(tuple)
        };

        assert_run_vm!("INDEX3 0, 0, 0", [raw tuple.clone()] => [int 1]);
        assert_run_vm!("INDEX3 0, 0, 1", [raw tuple.clone()] => [int 2]);
        assert_run_vm!("INDEX3 0, 0, 2", [raw tuple.clone()] => [int 3]);
        assert_run_vm!("INDEX 0 INDEX 0 INDEX 2", [raw tuple.clone()] => [int 3]);
        assert_run_vm!("INDEX2 0, 0 INDEX 2", [raw tuple.clone()] => [int 3]);

        let t = 1 + 3 + 2 * 4 + 4 * 4;
        assert_run_vm!("INDEX3 1, 2, 3", [raw tuple.clone()] => [int t]);
        assert_run_vm!("INDEX 1 INDEX 2 INDEX 3", [raw tuple.clone()] => [int t]);
        assert_run_vm!("INDEX2 1, 2 INDEX 3", [raw tuple.clone()] => [int t]);

        assert_run_vm!("INDEX3 3, 3, 3", [raw tuple.clone()] => [int 64]);
        assert_run_vm!("INDEX2 3, 3 INDEX 3", [raw tuple.clone()] => [int 64]);
    }
}
