use std::sync::Arc;
use vyre::ir::Program;
use vyre_foundation::ir::model::expr::Ident;
use vyre_foundation::ir::{BufferAccess, BufferDecl, DataType, Expr, Node};
pub(crate) const OP_ID: &str = "weir::range_check";
#[must_use]
pub fn range_check(node_count: u32, interval_hi: &str, bound: &str, out: &str) -> Program {
let domain = crate::graph_layout::LinearDomain::new(node_count);
let t = Expr::InvocationId { axis: 0 };
let body = vec![Node::store(
out,
t.clone(),
Expr::select(
Expr::lt(
Expr::load(interval_hi, t.clone()),
Expr::load(bound, t.clone()),
),
Expr::u32(1),
Expr::u32(0),
),
)];
Program::wrapped(
vec![
BufferDecl::storage(interval_hi, 0, BufferAccess::ReadOnly, DataType::U32)
.with_count(domain.element_count()),
BufferDecl::storage(bound, 1, BufferAccess::ReadOnly, DataType::U32)
.with_count(domain.element_count()),
BufferDecl::storage(out, 2, BufferAccess::ReadWrite, DataType::U32)
.with_count(domain.element_count()),
],
[256, 1, 1],
vec![Node::Region {
generator: Ident::from(OP_ID),
source_region: None,
body: Arc::new(vec![Node::if_then(
Expr::lt(t.clone(), Expr::u32(node_count)),
body,
)]),
}],
)
}
#[must_use]
#[cfg(any(test, feature = "cpu-parity"))]
#[deprecated(
note = "reference oracle only; production code must dispatch the Weir Program on a concrete GPU backend or use weir::oracle for parity evidence"
)]
pub(crate) fn cpu_ref(interval_hi: &[u32], bound: &[u32]) -> Vec<u32> {
let n = interval_hi.len().min(bound.len());
let mut out = crate::staging_reserve::reserved_vec(n, "range-check CPU oracle output")
.unwrap_or_else(|error| {
panic!("range-check CPU oracle output reservation failed: {error}")
});
out.extend((0..n).map(|i| u32::from(interval_hi[i] < bound[i])));
out
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RangeCheck;
impl super::soundness::SoundnessTagged for RangeCheck {
fn soundness(&self) -> super::soundness::Soundness {
super::soundness::Soundness::Exact
}
}
inventory::submit! {
vyre_harness::OpEntry::new(
OP_ID,
|| range_check(4, "hi", "bound", "out"),
Some(|| {
let u32s = crate::dispatch_decode::pack_u32;
vec![vec![
u32s(&[5, 10, 11, 0]),
u32s(&[6, 10, 9, 1]),
u32s(&[0; 4]),
]]
}),
Some(|| {
let u32s = crate::dispatch_decode::pack_u32;
vec![vec![u32s(&[1, 0, 0, 1])]]
}),
)
}
#[cfg(test)]
#[allow(deprecated)]
mod tests {
use super::*;
#[test]
fn within_bound_returns_one() {
assert_eq!(cpu_ref(&[5, 10, 15], &[10, 20, 30]), vec![1, 1, 1]);
}
#[test]
fn at_bound_returns_zero() {
assert_eq!(cpu_ref(&[10, 20], &[10, 20]), vec![0, 0]);
}
#[test]
fn over_bound_returns_zero() {
assert_eq!(cpu_ref(&[100], &[10]), vec![0]);
}
#[test]
fn mixed_bounds() {
assert_eq!(cpu_ref(&[1, 100, 5], &[10, 10, 100]), vec![1, 0, 1]);
}
}