use fusevm::awk_host::{awk_canon_nan, awk_compl, awk_lshift, awk_rand, awk_rshift, awk_srand};
use fusevm::value::Value;
#[test]
fn awk_rand_first_value_from_seed_one_matches_glibc_lcg() {
let mut seed: u64 = 1;
let v = awk_rand(&mut seed);
let expected_seed = 1_u64.wrapping_mul(1103515245).wrapping_add(12345);
let expected = f64::from((expected_seed >> 16) as u32 & 0x7fff) / 32768.0;
assert_eq!(v, expected, "first rand() from seed=1 must match glibc LCG");
}
#[test]
fn awk_rand_is_deterministic_across_independent_seeds() {
let mut s1: u64 = 42;
let mut s2: u64 = 42;
for i in 0..16 {
let a = awk_rand(&mut s1);
let b = awk_rand(&mut s2);
assert_eq!(a, b, "rand call #{i}: same seed must give same value");
}
}
#[test]
fn awk_rand_result_is_in_unit_interval() {
let mut seed: u64 = 0xDEAD_BEEF;
for _ in 0..512 {
let v = awk_rand(&mut seed);
assert!(
(0.0..1.0).contains(&v),
"rand result {v} outside [0, 1) — mask or divisor regression"
);
}
}
#[test]
fn awk_srand_returns_low_32_bits_of_previous_seed() {
let mut seed: u64 = 0xDEAD_BEEF_CAFE_BABE;
let prev = awk_srand(&mut seed, Some(0));
assert_eq!(
prev, 0xCAFE_BABE_u32 as f64,
"srand must return low 32 bits of previous seed, not full 64"
);
assert_eq!(seed, 0, "explicit Some(n) must replace seed wholesale");
}
#[test]
fn awk_srand_none_path_replaces_seed_with_nonzero_from_clock() {
let mut seed: u64 = 7; let prev = awk_srand(&mut seed, None);
assert_eq!(prev, 7.0);
assert_ne!(seed, 7, "clock-seed path must replace seed");
assert_ne!(seed, 0, "clock-seed should not be zero on a sane test host");
}
#[test]
fn awk_compl_truncates_float_operand_toward_zero() {
assert_eq!(awk_compl(&Value::Float(3.7)), -4);
assert_eq!(awk_compl(&Value::Float(-3.7)), 2);
assert_eq!(
awk_compl(&Value::str("3.7")),
-4,
"compl(\"3.7\") must coerce via to_float, not to_int (which yields 0)"
);
}
#[test]
fn awk_lshift_truncates_float_shift_count_toward_zero() {
assert_eq!(awk_lshift(&Value::Int(1), &Value::Float(2.9)), 4);
assert_eq!(awk_lshift(&Value::Int(42), &Value::Float(-0.5)), 42);
}
#[test]
fn awk_rshift_truncates_float_operand_then_shifts() {
assert_eq!(awk_rshift(&Value::Float(16.9), &Value::Int(2)), 4);
assert_eq!(awk_rshift(&Value::Float(-1.0), &Value::Int(63)), 1);
}
#[test]
fn awk_canon_nan_passes_finite_values_unchanged() {
for v in [0.0, -0.0, 1.5, -1.5, f64::MIN, f64::MAX, 1e300, -1e300] {
assert_eq!(
awk_canon_nan(v),
v,
"finite {v} must survive canon_nan unchanged"
);
}
}
#[test]
fn awk_canon_nan_preserves_infinities() {
assert_eq!(awk_canon_nan(f64::INFINITY), f64::INFINITY);
assert_eq!(awk_canon_nan(f64::NEG_INFINITY), f64::NEG_INFINITY);
}
#[test]
fn awk_canon_nan_normalizes_negative_nan_to_positive() {
let neg_nan = f64::from_bits(f64::NAN.to_bits() | (1 << 63));
assert!(neg_nan.is_nan(), "constructed input must still be NaN");
let canon = awk_canon_nan(neg_nan);
assert!(canon.is_nan(), "result must be NaN");
assert_eq!(
canon.to_bits(),
f64::NAN.to_bits(),
"canon_nan must return the standard positive NaN bit pattern"
);
}