use bytemuck::Pod;
use crate::byte_storage::CpuStorageBytes;
use fuel_ir::{Error, Result};
pub trait CompareOp<T: Copy> {
fn apply(a: T, b: T) -> u8;
}
pub trait CompareOpCore {
fn f32(a: f32, b: f32) -> bool;
fn f64(a: f64, b: f64) -> bool;
}
impl<O: CompareOpCore> CompareOp<f32> for O {
fn apply(a: f32, b: f32) -> u8 {
if <O as CompareOpCore>::f32(a, b) {
1
} else {
0
}
}
}
impl<O: CompareOpCore> CompareOp<f64> for O {
fn apply(a: f64, b: f64) -> u8 {
if <O as CompareOpCore>::f64(a, b) {
1
} else {
0
}
}
}
impl<O: CompareOpCore> CompareOp<half::bf16> for O {
fn apply(a: half::bf16, b: half::bf16) -> u8 {
if <O as CompareOpCore>::f32(a.to_f32(), b.to_f32()) {
1
} else {
0
}
}
}
impl<O: CompareOpCore> CompareOp<half::f16> for O {
fn apply(a: half::f16, b: half::f16) -> u8 {
if <O as CompareOpCore>::f32(a.to_f32(), b.to_f32()) {
1
} else {
0
}
}
}
pub fn compare<T, U>(
name: &str,
lhs: &CpuStorageBytes,
rhs: &CpuStorageBytes,
output: &mut CpuStorageBytes,
) -> Result<()>
where
T: Copy + Pod,
U: CompareOp<T>,
{
let lhs_view: &[T] = lhs.as_slice()?;
let rhs_view: &[T] = rhs.as_slice()?;
let out_view: &mut [u8] = output.as_slice_mut()?;
if lhs_view.len() != rhs_view.len() || lhs_view.len() != out_view.len() {
return Err(Error::Msg(format!(
"{name}: element count mismatch (lhs={}, rhs={}, out={})",
lhs_view.len(),
rhs_view.len(),
out_view.len(),
))
.bt());
}
for (i, slot) in out_view.iter_mut().enumerate() {
*slot = U::apply(lhs_view[i], rhs_view[i]);
}
Ok(())
}
pub struct Eq;
impl CompareOpCore for Eq {
fn f32(a: f32, b: f32) -> bool {
a == b
}
fn f64(a: f64, b: f64) -> bool {
a == b
}
}
pub struct Ne;
impl CompareOpCore for Ne {
fn f32(a: f32, b: f32) -> bool {
a != b
}
fn f64(a: f64, b: f64) -> bool {
a != b
}
}
pub struct Lt;
impl CompareOpCore for Lt {
fn f32(a: f32, b: f32) -> bool {
a < b
}
fn f64(a: f64, b: f64) -> bool {
a < b
}
}
pub struct Le;
impl CompareOpCore for Le {
fn f32(a: f32, b: f32) -> bool {
a <= b
}
fn f64(a: f64, b: f64) -> bool {
a <= b
}
}
pub struct Gt;
impl CompareOpCore for Gt {
fn f32(a: f32, b: f32) -> bool {
a > b
}
fn f64(a: f64, b: f64) -> bool {
a > b
}
}
pub struct Ge;
impl CompareOpCore for Ge {
fn f32(a: f32, b: f32) -> bool {
a >= b
}
fn f64(a: f64, b: f64) -> bool {
a >= b
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compare_op_eq_f32_returns_u8_mask() {
assert_eq!(<Eq as CompareOp<f32>>::apply(2.5, 2.5), 1);
assert_eq!(<Eq as CompareOp<f32>>::apply(2.5, 1.5), 0);
}
#[test]
fn compare_op_lt_f32_nan_yields_zero() {
assert_eq!(<Lt as CompareOp<f32>>::apply(f32::NAN, 1.0), 0);
assert_eq!(<Lt as CompareOp<f32>>::apply(1.0, f32::NAN), 0);
assert_eq!(<Lt as CompareOp<f32>>::apply(f32::NAN, f32::NAN), 0);
}
#[test]
fn compare_op_ne_f32_nan_yields_one() {
assert_eq!(<Ne as CompareOp<f32>>::apply(f32::NAN, f32::NAN), 1);
assert_eq!(<Ne as CompareOp<f32>>::apply(1.0, 1.0), 0);
assert_eq!(<Ne as CompareOp<f32>>::apply(1.0, 2.0), 1);
}
#[test]
fn compare_op_ge_le_at_equality() {
assert_eq!(<Ge as CompareOp<f32>>::apply(2.0, 2.0), 1);
assert_eq!(<Le as CompareOp<f32>>::apply(2.0, 2.0), 1);
assert_eq!(<Gt as CompareOp<f32>>::apply(2.0, 2.0), 0);
assert_eq!(<Lt as CompareOp<f32>>::apply(2.0, 2.0), 0);
}
#[test]
fn compare_op_bf16_blanket_routes_through_f32() {
let a = half::bf16::from_f32(2.5);
let b = half::bf16::from_f32(2.5);
assert_eq!(<Eq as CompareOp<half::bf16>>::apply(a, b), 1);
let c = half::bf16::from_f32(1.5);
assert_eq!(<Lt as CompareOp<half::bf16>>::apply(c, a), 1);
assert_eq!(<Gt as CompareOp<half::bf16>>::apply(c, a), 0);
}
#[test]
fn compare_chassis_element_count_mismatch_errors() {
let lhs = CpuStorageBytes::from_slice(&[1.0_f32, 2.0]);
let rhs = CpuStorageBytes::from_slice(&[1.0_f32, 2.0, 3.0]); let mut out = CpuStorageBytes::from_zero_bytes(2); let r = compare::<f32, Eq>("test", &lhs, &rhs, &mut out);
assert!(r.is_err());
}
#[test]
fn compare_chassis_walks_all_elements() {
let lhs = CpuStorageBytes::from_slice(&[1.0_f32, 2.0, 3.0, 4.0]);
let rhs = CpuStorageBytes::from_slice(&[1.0_f32, 1.0, 3.0, 5.0]);
let mut out = CpuStorageBytes::from_zero_bytes(4); compare::<f32, Eq>("test", &lhs, &rhs, &mut out).expect("compare eq_f32");
let r: &[u8] = out.as_slice().unwrap();
assert_eq!(r, &[1, 0, 1, 0]);
}
}