use core::cmp::Ordering;
use miden_core::{Felt, Word};
use miden_utils_testing::rand;
use num::Integer;
use rstest::rstest;
#[rstest]
#[case::gt("gt", &[Ordering::Greater])]
#[case::gte("gte", &[Ordering::Greater, Ordering::Equal])]
#[case::eq("eq", &[Ordering::Equal])]
#[case::lt("lt", &[Ordering::Less])]
#[case::lte("lte", &[Ordering::Less, Ordering::Equal])]
fn test_word_comparison(#[case] proc_name: &str, #[case] valid_ords: &[Ordering]) {
let source = format!(
"
use miden::core::word
begin
exec.word::{proc_name}
end
"
);
let test = build_test!(&source);
let mut seed = 0xfacade;
for i in 0..1000 {
let lhs = rand::seeded_word(&mut seed);
let rhs = if i.is_even() { rand::seeded_word(&mut seed) } else { lhs };
let expected_cmp = lhs.cmp(&rhs);
let mut operand_stack: Vec<u64> = Default::default();
prepend_word(&mut operand_stack, lhs);
prepend_word(&mut operand_stack, rhs);
let expected = u64::from(valid_ords.contains(&expected_cmp));
test.expect_stack_with_inputs(&operand_stack, &[expected]);
}
}
#[test]
fn test_reverse() {
const SOURCE: &str = "
begin
reversew
end
";
let test = build_test!(SOURCE);
let mut seed = 0xfacade;
for _ in 0..1000 {
let word = rand::seeded_word(&mut seed);
let mut operand_stack: Vec<u64> = Default::default();
prepend_word(&mut operand_stack, word);
let expected: Vec<u64> = word.iter().rev().map(Felt::as_canonical_u64).collect();
test.expect_stack_with_inputs(&operand_stack, &expected);
}
}
#[test]
fn test_eqz() {
const SOURCE: &str = "
use miden::core::word
begin
exec.word::eqz
end
";
build_test!(SOURCE, &[0, 0, 0, 0]).expect_stack(&[1]);
build_test!(SOURCE, &[0, 1, 2, 3]).expect_stack(&[0]);
}
#[test]
fn test_preserving_eqz() {
const SOURCE: &str = "
use miden::core::word
use miden::core::sys
begin
exec.word::testz
exec.sys::truncate_stack
end
";
build_test!(SOURCE, &[0, 0, 0, 0]).expect_stack(&[1, 0, 0, 0, 0]);
build_test!(SOURCE, &[0, 1, 2, 3]).expect_stack(&[0, 0, 1, 2, 3]);
}
#[test]
fn test_preserving_eq() {
const SOURCE: &str = "
use miden::core::word
use miden::core::sys
begin
exec.word::test_eq
exec.sys::truncate_stack
end
";
let test = build_test!(SOURCE);
let mut seed = 0xfacade;
for i in 0..1000 {
let lhs = rand::seeded_word(&mut seed);
let rhs = if i.is_even() { rand::seeded_word(&mut seed) } else { lhs };
let is_equal = lhs == rhs;
let mut operand_stack: Vec<u64> = Default::default();
prepend_word(&mut operand_stack, lhs);
prepend_word(&mut operand_stack, rhs);
let mut expected: Vec<u64> = vec![is_equal.into()];
expected.extend(operand_stack.iter());
test.expect_stack_with_inputs(&operand_stack, &expected);
}
}
#[test]
fn store_word_u32s_le_stores_limbs() {
const PTR: u32 = 256;
const W0: u64 = 0x1234567890abcdef;
const W1: u64 = 0x0000000200000001;
const W2: u64 = 0xffffffff00000000;
const W3: u64 = 0x00000000ffffffff;
fn limbs(value: u64) -> (u64, u64) {
(value & 0xffff_ffff, value >> 32)
}
let (w0_lo, w0_hi) = limbs(W0);
let (w1_lo, w1_hi) = limbs(W1);
let (w2_lo, w2_hi) = limbs(W2);
let (w3_lo, w3_hi) = limbs(W3);
let source = format!(
"
use miden::core::word
begin
push.{PTR}
push.{W3}
push.{W2}
push.{W1}
push.{W0}
exec.word::store_word_u32s_le
end
",
);
let expected_mem = [w0_lo, w0_hi, w1_lo, w1_hi, w2_lo, w2_hi, w3_lo, w3_hi];
build_test!(&source).expect_stack_and_memory(&[], PTR, &expected_mem);
}
fn prepend_word(target: &mut Vec<u64>, word: Word) {
let _iterator = target.splice(0..0, word.iter().map(Felt::as_canonical_u64));
}