#[cfg(test)]
mod tests {
use crate::spec::primitive;
fn cpu(id: &str, input: &[u8]) -> Vec<u8> {
let specs = primitive::specs();
let spec = specs.iter().find(|s| s.id == id).unwrap();
(spec.cpu_fn)(input)
}
fn pair(a: u32, b: u32) -> Vec<u8> {
let mut v = a.to_le_bytes().to_vec();
v.extend_from_slice(&b.to_le_bytes());
v
}
fn unary(a: u32) -> Vec<u8> {
a.to_le_bytes().to_vec()
}
fn result_u32(bytes: &[u8]) -> u32 {
u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
}
#[test]
fn xor_is_commutative() {
for (a, b) in [(0, 0), (1, 2), (0xDEAD, 0xBEEF), (u32::MAX, 0)] {
assert_eq!(
cpu("primitive.bitwise.xor", &pair(a, b)),
cpu("primitive.bitwise.xor", &pair(b, a)),
"xor({a}, {b}) != xor({b}, {a})"
);
}
}
#[test]
fn and_is_commutative() {
for (a, b) in [(0xFF, 0x0F), (u32::MAX, 0), (123, 456)] {
assert_eq!(
cpu("primitive.bitwise.and", &pair(a, b)),
cpu("primitive.bitwise.and", &pair(b, a))
);
}
}
#[test]
fn or_is_commutative() {
for (a, b) in [(0xF0, 0x0F), (0, u32::MAX), (42, 99)] {
assert_eq!(
cpu("primitive.bitwise.or", &pair(a, b)),
cpu("primitive.bitwise.or", &pair(b, a))
);
}
}
#[test]
fn add_is_commutative() {
for (a, b) in [(0, 0), (1, u32::MAX), (0x8000_0000, 1)] {
assert_eq!(
cpu("primitive.math.add", &pair(a, b)),
cpu("primitive.math.add", &pair(b, a))
);
}
}
#[test]
fn mul_is_commutative() {
for (a, b) in [(0, 1), (7, 13), (u32::MAX, 2)] {
assert_eq!(
cpu("primitive.math.mul", &pair(a, b)),
cpu("primitive.math.mul", &pair(b, a))
);
}
}
#[test]
fn min_is_commutative() {
for (a, b) in [(5, 10), (u32::MAX, 0), (42, 42)] {
assert_eq!(
cpu("primitive.math.min", &pair(a, b)),
cpu("primitive.math.min", &pair(b, a))
);
}
}
#[test]
fn max_is_commutative() {
for (a, b) in [(5, 10), (u32::MAX, 0), (42, 42)] {
assert_eq!(
cpu("primitive.math.max", &pair(a, b)),
cpu("primitive.math.max", &pair(b, a))
);
}
}
#[test]
fn eq_is_commutative() {
for (a, b) in [(0, 0), (1, 2), (u32::MAX, u32::MAX)] {
assert_eq!(
cpu("primitive.compare.eq", &pair(a, b)),
cpu("primitive.compare.eq", &pair(b, a))
);
}
}
#[test]
fn ne_is_commutative() {
for (a, b) in [(0, 0), (1, 2), (u32::MAX, 0)] {
assert_eq!(
cpu("primitive.compare.ne", &pair(a, b)),
cpu("primitive.compare.ne", &pair(b, a))
);
}
}
#[test]
fn xor_identity_is_zero() {
for a in [0, 1, 42, u32::MAX, 0x8000_0000] {
assert_eq!(result_u32(&cpu("primitive.bitwise.xor", &pair(a, 0))), a);
}
}
#[test]
fn and_identity_is_max() {
for a in [0, 1, 42, u32::MAX] {
assert_eq!(
result_u32(&cpu("primitive.bitwise.and", &pair(a, u32::MAX))),
a
);
}
}
#[test]
fn or_identity_is_zero() {
for a in [0, 1, 42, u32::MAX] {
assert_eq!(result_u32(&cpu("primitive.bitwise.or", &pair(a, 0))), a);
}
}
#[test]
fn add_identity_is_zero() {
for a in [0, 1, u32::MAX, 0x8000_0000] {
assert_eq!(result_u32(&cpu("primitive.math.add", &pair(a, 0))), a);
}
}
#[test]
fn mul_identity_is_one() {
for a in [0, 1, 42, u32::MAX] {
assert_eq!(result_u32(&cpu("primitive.math.mul", &pair(a, 1))), a);
}
}
#[test]
fn sub_identity_is_zero() {
for a in [0, 1, u32::MAX] {
assert_eq!(result_u32(&cpu("primitive.math.sub", &pair(a, 0))), a);
}
}
#[test]
fn div_identity_is_one() {
for a in [0, 1, 42, u32::MAX] {
assert_eq!(result_u32(&cpu("primitive.math.div", &pair(a, 1))), a);
}
}
#[test]
fn and_annihilator_is_zero() {
for a in [0, 1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.and", &pair(a, 0))), 0);
}
}
#[test]
fn or_annihilator_is_max() {
for a in [0, 1, u32::MAX] {
assert_eq!(
result_u32(&cpu("primitive.bitwise.or", &pair(a, u32::MAX))),
u32::MAX
);
}
}
#[test]
fn mul_annihilator_is_zero() {
for a in [0, 1, u32::MAX] {
assert_eq!(result_u32(&cpu("primitive.math.mul", &pair(a, 0))), 0);
}
}
#[test]
fn xor_self_is_zero() {
for a in [0, 1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.xor", &pair(a, a))), 0);
}
}
#[test]
fn not_is_involution() {
for a in [0, 1, u32::MAX, 0x5555_5555] {
let once = cpu("primitive.bitwise.not", &unary(a));
let twice = cpu("primitive.bitwise.not", &once);
assert_eq!(result_u32(&twice), a, "not(not({a})) != {a}");
}
}
#[test]
fn negate_is_involution() {
for a in [0, 1, u32::MAX, 0x8000_0000] {
let once = cpu("primitive.math.negate", &unary(a));
let twice = cpu("primitive.math.negate", &once);
assert_eq!(result_u32(&twice), a, "negate(negate({a})) != {a}");
}
}
#[test]
fn reverse_bits_is_involution() {
for a in [0, 1, u32::MAX, 0xDEAD_BEEF] {
let once = cpu("primitive.bitwise.reverse_bits", &unary(a));
let twice = cpu("primitive.bitwise.reverse_bits", &once);
assert_eq!(result_u32(&twice), a, "reverse(reverse({a})) != {a}");
}
}
#[test]
fn sub_is_not_commutative() {
let a_minus_b = result_u32(&cpu("primitive.math.sub", &pair(5, 3)));
let b_minus_a = result_u32(&cpu("primitive.math.sub", &pair(3, 5)));
assert_ne!(a_minus_b, b_minus_a, "sub must not be commutative");
}
#[test]
fn eq_and_ne_are_complementary() {
for (a, b) in [(0, 0), (1, 2), (u32::MAX, u32::MAX - 1)] {
let eq = result_u32(&cpu("primitive.compare.eq", &pair(a, b)));
let ne = result_u32(&cpu("primitive.compare.ne", &pair(a, b)));
assert_eq!(eq + ne, 1, "eq({a},{b}) + ne({a},{b}) should be 1");
}
}
#[test]
fn lt_and_ge_are_complementary() {
for (a, b) in [(0, 1), (1, 0), (5, 5), (u32::MAX, 0)] {
let lt = result_u32(&cpu("primitive.compare.lt", &pair(a, b)));
let ge = result_u32(&cpu("primitive.compare.ge", &pair(a, b)));
assert_eq!(lt + ge, 1, "lt({a},{b}) + ge({a},{b}) should be 1");
}
}
#[test]
fn gt_and_le_are_complementary() {
for (a, b) in [(0, 1), (1, 0), (5, 5), (u32::MAX, 0)] {
let gt = result_u32(&cpu("primitive.compare.gt", &pair(a, b)));
let le = result_u32(&cpu("primitive.compare.le", &pair(a, b)));
assert_eq!(gt + le, 1, "gt({a},{b}) + le({a},{b}) should be 1");
}
}
#[test]
fn shl_by_zero_is_identity() {
for a in [0, 1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.shl", &pair(a, 0))), a);
}
}
#[test]
fn shr_by_zero_is_identity() {
for a in [0, 1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.shr", &pair(a, 0))), a);
}
}
#[test]
fn shl_by_32_is_zero() {
for a in [1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.shl", &pair(a, 32))), 0);
}
}
#[test]
fn shr_by_32_is_zero() {
for a in [1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.shr", &pair(a, 32))), 0);
}
}
#[test]
fn popcount_hw_matches_popcount_sw() {
for a in [0, 1, 0xFF, 0x5555_5555, 0xAAAA_AAAA, u32::MAX, 0x8000_0000] {
let hw = cpu("primitive.bitwise.popcount", &unary(a));
let sw = cpu("primitive.bitwise.popcount_sw", &unary(a));
assert_eq!(hw, sw, "popcount HW != SW for {a:#010x}");
}
}
#[test]
fn div_by_zero_returns_zero() {
for a in [0, 1, u32::MAX] {
assert_eq!(result_u32(&cpu("primitive.math.div", &pair(a, 0))), 0);
}
}
#[test]
fn mod_by_zero_returns_zero() {
for a in [0, 1, u32::MAX] {
assert_eq!(result_u32(&cpu("primitive.math.mod", &pair(a, 0))), 0);
}
}
#[test]
fn clz_plus_ctz_plus_popcount_for_power_of_two() {
for shift in 0..32 {
let val = 1u32 << shift;
let clz = result_u32(&cpu("primitive.bitwise.clz", &unary(val)));
let ctz = result_u32(&cpu("primitive.bitwise.ctz", &unary(val)));
let pop = result_u32(&cpu("primitive.bitwise.popcount", &unary(val)));
assert_eq!(
clz + ctz + pop,
32,
"clz({val}) + ctz({val}) + popcount({val}) = {clz}+{ctz}+{pop} != 32"
);
}
}
#[test]
fn rotl_by_zero_is_identity() {
for a in [0, 1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.rotl", &pair(a, 0))), a);
}
}
#[test]
fn rotr_by_zero_is_identity() {
for a in [0, 1, u32::MAX, 0xDEAD_BEEF] {
assert_eq!(result_u32(&cpu("primitive.bitwise.rotr", &pair(a, 0))), a);
}
}
#[test]
fn rotl_then_rotr_is_identity() {
for a in [0xDEAD_BEEF, 0xFF00_00FF, 1] {
for rot in [1, 7, 16, 31] {
let rotated = result_u32(&cpu("primitive.bitwise.rotl", &pair(a, rot)));
let restored = result_u32(&cpu("primitive.bitwise.rotr", &pair(rotated, rot)));
assert_eq!(restored, a, "rotl({a:#x},{rot}) then rotr != identity");
}
}
}
mod declared_laws;
}