use std::sync::Arc;
use vyre_foundation::ir::model::expr::Ident;
use vyre_foundation::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
pub const OP_ID: &str = "vyre-primitives::graph::path_reconstruct";
pub const BATCHED_OP_ID: &str = "vyre-primitives::graph::batched_path_reconstruct";
pub const BATCHED_WORKGROUP_SIZE: u32 = 256;
pub const PATH_PARENT_BUFFER: &str = "path_reconstruct parent";
pub const PATH_TARGET_BUFFER: &str = "path_reconstruct target";
pub const PATH_OUT_BUFFER: &str = "path_reconstruct path_out";
pub const PATH_LEN_BUFFER: &str = "path_reconstruct path_len";
pub const BATCHED_PATH_TARGETS_BUFFER: &str = "batched_path_reconstruct targets";
pub const BATCHED_PATHS_BUFFER: &str = "batched_path_reconstruct paths";
pub const BATCHED_LENS_BUFFER: &str = "batched_path_reconstruct lens";
pub const PATH_RECONSTRUCT_DISPATCH_GRID: [u32; 3] = [1, 1, 1];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BatchedPathReconstructLayout {
pub target_count: u32,
pub path_words: usize,
pub path_words_u32: u32,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PathReconstructDispatchPlan {
pub parent_words: usize,
pub target_words: usize,
pub path_words: usize,
pub len_words: usize,
pub max_depth: u32,
pub grid: [u32; 3],
}
impl PathReconstructDispatchPlan {
#[must_use]
pub fn program(&self) -> Program {
path_reconstruct(
PATH_PARENT_BUFFER,
PATH_TARGET_BUFFER,
PATH_OUT_BUFFER,
PATH_LEN_BUFFER,
self.max_depth,
)
}
pub fn static_input_key(
&self,
parent: &[u32],
) -> Result<PathReconstructStaticInputKey, String> {
if parent.len() != self.parent_words {
return Err(format!(
"Fix: path_reconstruct static key expected {} parent word(s), got {}.",
self.parent_words,
parent.len()
));
}
Ok(PathReconstructStaticInputKey {
parent_words: self.parent_words,
parent_hash: path_reconstruct_u32_slice_fingerprint(parent),
target_count: 1,
max_depth: self.max_depth,
batched: false,
})
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BatchedPathReconstructDispatchPlan {
pub layout: BatchedPathReconstructLayout,
pub parent_words: usize,
pub target_words: usize,
pub path_words: usize,
pub len_words: usize,
pub max_depth: u32,
pub grid: [u32; 3],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PathReconstructStaticInputKey {
pub parent_words: usize,
pub parent_hash: u64,
pub target_count: u32,
pub max_depth: u32,
pub batched: bool,
}
impl BatchedPathReconstructDispatchPlan {
#[must_use]
pub fn program(&self) -> Program {
batched_path_reconstruct(self.layout.target_count, self.max_depth)
}
pub fn static_input_key(
&self,
parent: &[u32],
) -> Result<PathReconstructStaticInputKey, String> {
if parent.len() != self.parent_words {
return Err(format!(
"Fix: batched_path_reconstruct static key expected {} parent word(s), got {}.",
self.parent_words,
parent.len()
));
}
Ok(PathReconstructStaticInputKey {
parent_words: self.parent_words,
parent_hash: path_reconstruct_u32_slice_fingerprint(parent),
target_count: self.layout.target_count,
max_depth: self.max_depth,
batched: true,
})
}
}
fn path_reconstruct_u32_slice_fingerprint(values: &[u32]) -> u64 {
const FNV_OFFSET: u64 = 0xcbf29ce484222325;
const FNV_PRIME: u64 = 0x100000001b3;
let mut hash = FNV_OFFSET;
for byte in (values.len() as u64).to_le_bytes() {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
for value in values {
for byte in value.to_le_bytes() {
hash ^= u64::from(byte);
hash = hash.wrapping_mul(FNV_PRIME);
}
}
hash
}
#[must_use]
pub fn path_reconstruct(
parent: &str,
target: &str,
path_out: &str,
path_len: &str,
max_depth: u32,
) -> Program {
if max_depth == 0 {
return crate::invalid_output_program(
OP_ID,
path_out,
DataType::U32,
"Fix: path_reconstruct max_depth must be >= 1.".to_string(),
);
}
let body = vec![
Node::let_bind("current", Expr::load(target, Expr::u32(0))),
Node::let_bind("len", Expr::u32(0)),
Node::let_bind("done", Expr::u32(0)),
Node::loop_for(
"step",
Expr::u32(0),
Expr::u32(max_depth),
vec![Node::if_then(
Expr::eq(Expr::var("done"), Expr::u32(0)),
vec![
Node::store(path_out, Expr::var("len"), Expr::var("current")),
Node::assign("len", Expr::add(Expr::var("len"), Expr::u32(1))),
Node::let_bind(
"next",
Expr::select(
Expr::lt(Expr::var("current"), Expr::buf_len(parent)),
Expr::load(parent, Expr::var("current")),
Expr::var("current"),
),
),
Node::if_then(
Expr::eq(Expr::var("next"), Expr::var("current")),
vec![Node::assign("done", Expr::u32(1))],
),
Node::assign("current", Expr::var("next")),
],
)],
),
Node::loop_for(
"pad",
Expr::var("len"),
Expr::u32(max_depth),
vec![Node::store(path_out, Expr::var("pad"), Expr::u32(0))],
),
Node::store(path_len, Expr::u32(0), Expr::var("len")),
];
Program::wrapped(
vec![
BufferDecl::storage(parent, 0, BufferAccess::ReadOnly, DataType::U32),
BufferDecl::storage(target, 1, BufferAccess::ReadOnly, DataType::U32).with_count(1),
BufferDecl::storage(path_out, 2, BufferAccess::ReadWrite, DataType::U32)
.with_count(max_depth),
BufferDecl::storage(path_len, 3, BufferAccess::ReadWrite, DataType::U32).with_count(1),
],
[1, 1, 1],
vec![Node::Region {
generator: Ident::from(OP_ID),
source_region: None,
body: Arc::new(vec![Node::if_then(
Expr::eq(Expr::InvocationId { axis: 0 }, Expr::u32(0)),
body,
)]),
}],
)
}
#[must_use]
pub fn batched_path_reconstruct(target_count: u32, max_depth: u32) -> Program {
let layout = match validate_batched_path_reconstruct_layout(target_count as usize, max_depth) {
Ok(layout) => layout,
Err(error) => {
return crate::invalid_output_program(BATCHED_OP_ID, "paths", DataType::U32, error);
}
};
let path_words = layout.path_words_u32;
let body = vec![
Node::let_bind("idx", Expr::InvocationId { axis: 0 }),
Node::if_then(
Expr::lt(Expr::var("idx"), Expr::u32(target_count)),
vec![
Node::let_bind("base", Expr::mul(Expr::var("idx"), Expr::u32(max_depth))),
Node::let_bind("current", Expr::load("targets", Expr::var("idx"))),
Node::let_bind("len", Expr::u32(0)),
Node::let_bind("done", Expr::u32(0)),
Node::loop_for(
"step",
Expr::u32(0),
Expr::u32(max_depth),
vec![Node::if_then(
Expr::eq(Expr::var("done"), Expr::u32(0)),
vec![
Node::store(
"paths",
Expr::add(Expr::var("base"), Expr::var("len")),
Expr::var("current"),
),
Node::assign("len", Expr::add(Expr::var("len"), Expr::u32(1))),
Node::let_bind(
"next",
Expr::select(
Expr::lt(Expr::var("current"), Expr::buf_len("parent")),
Expr::load("parent", Expr::var("current")),
Expr::var("current"),
),
),
Node::if_then(
Expr::eq(Expr::var("next"), Expr::var("current")),
vec![Node::assign("done", Expr::u32(1))],
),
Node::assign("current", Expr::var("next")),
],
)],
),
Node::loop_for(
"pad",
Expr::var("len"),
Expr::u32(max_depth),
vec![Node::store(
"paths",
Expr::add(Expr::var("base"), Expr::var("pad")),
Expr::u32(0),
)],
),
Node::store("lens", Expr::var("idx"), Expr::var("len")),
],
),
];
Program::wrapped(
vec![
BufferDecl::storage("parent", 0, BufferAccess::ReadOnly, DataType::U32),
BufferDecl::storage("targets", 1, BufferAccess::ReadOnly, DataType::U32)
.with_count(target_count),
BufferDecl::storage("paths", 2, BufferAccess::ReadWrite, DataType::U32)
.with_count(path_words),
BufferDecl::storage("lens", 3, BufferAccess::ReadWrite, DataType::U32)
.with_count(target_count),
],
[BATCHED_WORKGROUP_SIZE, 1, 1],
vec![Node::Region {
generator: Ident::from(BATCHED_OP_ID),
source_region: None,
body: Arc::new(body),
}],
)
}
pub fn validate_batched_path_reconstruct_layout(
target_len: usize,
max_depth: u32,
) -> Result<BatchedPathReconstructLayout, String> {
if max_depth == 0 {
return Err("Fix: batched_path_reconstruct max_depth must be >= 1.".to_string());
}
let target_count = u32::try_from(target_len).map_err(|_| {
format!(
"Fix: batched_path_reconstruct target count {target_len} exceeds the primitive u32 lane limit."
)
})?;
let path_words_u32 = target_count.checked_mul(max_depth).ok_or_else(|| {
format!(
"Fix: batched_path_reconstruct target_count*max_depth overflows u32 for target_count={target_count}, max_depth={max_depth}."
)
})?;
let path_words = usize::try_from(path_words_u32).map_err(|_| {
format!("Fix: batched_path_reconstruct path word count {path_words_u32} exceeds usize.")
})?;
Ok(BatchedPathReconstructLayout {
target_count,
path_words,
path_words_u32,
})
}
pub fn plan_path_reconstruct_dispatch(
parent_len: usize,
max_depth: u32,
) -> Result<PathReconstructDispatchPlan, String> {
if max_depth == 0 {
return Err("Fix: path_reconstruct max_depth must be >= 1.".to_string());
}
Ok(PathReconstructDispatchPlan {
parent_words: parent_len,
target_words: 1,
path_words: max_depth as usize,
len_words: 1,
max_depth,
grid: PATH_RECONSTRUCT_DISPATCH_GRID,
})
}
pub fn plan_batched_path_reconstruct_dispatch(
parent_len: usize,
target_len: usize,
max_depth: u32,
) -> Result<BatchedPathReconstructDispatchPlan, String> {
let layout = validate_batched_path_reconstruct_layout(target_len, max_depth)?;
Ok(BatchedPathReconstructDispatchPlan {
parent_words: parent_len,
target_words: target_len,
path_words: layout.path_words,
len_words: target_len,
max_depth,
grid: [
ceil_div_u32(layout.target_count, BATCHED_WORKGROUP_SIZE),
1,
1,
],
layout,
})
}
fn ceil_div_u32(value: u32, divisor: u32) -> u32 {
if value == 0 {
0
} else {
((value - 1) / divisor) + 1
}
}
pub fn validate_path_reconstruct_readback(
plan: &PathReconstructDispatchPlan,
len: u32,
) -> Result<usize, String> {
let len_usize = usize::try_from(len).map_err(|_| {
format!("Fix: path_reconstruct returned length {len}, which cannot fit this host usize.")
})?;
if len_usize > plan.path_words {
return Err(format!(
"Fix: path_reconstruct returned length {len}, exceeding max_depth {}. Treat this as malformed GPU readback or a backend bug.",
plan.max_depth
));
}
Ok(len_usize)
}
pub fn validate_batched_path_reconstruct_readback(
plan: &BatchedPathReconstructDispatchPlan,
path_words: usize,
len_words: usize,
lens: &[u32],
) -> Result<(), String> {
if path_words != plan.path_words || len_words != plan.len_words {
return Err(format!(
"Fix: batched_path_reconstruct returned {path_words} path word(s) and {len_words} len word(s), expected {} and {}.",
plan.path_words, plan.len_words
));
}
for (target_index, &len) in lens.iter().enumerate() {
let len_usize = usize::try_from(len).map_err(|_| {
format!(
"Fix: batched_path_reconstruct target {target_index} returned length {len}, which cannot fit this host usize."
)
})?;
if len_usize > plan.max_depth as usize {
return Err(format!(
"Fix: batched_path_reconstruct target {target_index} returned length {len}, exceeding max_depth {}. Treat this as malformed GPU readback or a backend bug.",
plan.max_depth
));
}
}
Ok(())
}
#[cfg(test)]
mod dispatch_plan_tests {
use super::*;
#[test]
fn single_path_dispatch_plan_owns_outputs_and_grid() {
let plan = plan_path_reconstruct_dispatch(4, 8)
.expect("Fix: nonzero max_depth should plan single reconstruction");
assert_eq!(plan.parent_words, 4);
assert_eq!(plan.target_words, 1);
assert_eq!(plan.path_words, 8);
assert_eq!(plan.len_words, 1);
assert_eq!(plan.grid, PATH_RECONSTRUCT_DISPATCH_GRID);
}
#[test]
fn single_path_dispatch_plan_rejects_zero_depth() {
let err = plan_path_reconstruct_dispatch(4, 0).unwrap_err();
assert!(err.contains("max_depth"));
}
#[test]
fn batched_path_dispatch_plan_owns_layout_and_grid() {
let plan = plan_batched_path_reconstruct_dispatch(4, 513, 3)
.expect("Fix: valid batched reconstruction should plan");
assert_eq!(plan.parent_words, 4);
assert_eq!(plan.target_words, 513);
assert_eq!(plan.path_words, 1539);
assert_eq!(plan.len_words, 513);
assert_eq!(plan.grid, [3, 1, 1]);
assert_eq!(plan.layout.target_count, 513);
}
#[test]
fn static_input_key_tracks_parent_content_and_dispatch_shape() {
let single = plan_path_reconstruct_dispatch(4, 8)
.expect("Fix: nonzero max_depth should plan single reconstruction");
let batched = plan_batched_path_reconstruct_dispatch(4, 2, 8)
.expect("Fix: valid batched reconstruction should plan");
let first = single
.static_input_key(&[0, 0, 1, 2])
.expect("Fix: matching parent slice should key");
let same = single
.static_input_key(&[0, 0, 1, 2])
.expect("Fix: matching parent slice should key");
let changed = single
.static_input_key(&[0, 0, 0, 2])
.expect("Fix: same-shape parent content should key");
let batched_key = batched
.static_input_key(&[0, 0, 1, 2])
.expect("Fix: matching batched parent slice should key");
assert_eq!(first, same);
assert_ne!(first, changed);
assert_ne!(first, batched_key);
assert_eq!(first.parent_words, 4);
assert_eq!(first.target_count, 1);
assert!(!first.batched);
assert_eq!(batched_key.target_count, 2);
assert!(batched_key.batched);
}
#[test]
fn static_input_key_rejects_parent_length_drift() {
let single = plan_path_reconstruct_dispatch(4, 8)
.expect("Fix: nonzero max_depth should plan single reconstruction");
let batched = plan_batched_path_reconstruct_dispatch(4, 2, 8)
.expect("Fix: valid batched reconstruction should plan");
let err = single.static_input_key(&[0, 0, 1]).unwrap_err();
assert!(err.contains("expected 4 parent word"));
let err = batched.static_input_key(&[0, 0, 1]).unwrap_err();
assert!(err.contains("expected 4 parent word"));
}
#[test]
fn single_path_readback_validation_rejects_impossible_len() {
let plan = plan_path_reconstruct_dispatch(4, 4)
.expect("Fix: nonzero max_depth should plan single reconstruction");
assert_eq!(validate_path_reconstruct_readback(&plan, 4), Ok(4));
let err = validate_path_reconstruct_readback(&plan, 5).unwrap_err();
assert!(err.contains("exceeding max_depth 4"));
}
#[test]
fn batched_path_readback_validation_rejects_shape_and_len_drift() {
let plan = plan_batched_path_reconstruct_dispatch(4, 2, 4)
.expect("Fix: valid batched reconstruction should plan");
validate_batched_path_reconstruct_readback(&plan, 8, 2, &[4, 1]).unwrap();
let err = validate_batched_path_reconstruct_readback(&plan, 7, 2, &[4, 1]).unwrap_err();
assert!(err.contains("expected 8 and 2"));
let err = validate_batched_path_reconstruct_readback(&plan, 8, 2, &[4, 5]).unwrap_err();
assert!(err.contains("target 1"));
assert!(err.contains("exceeding max_depth 4"));
}
}
#[must_use]
#[cfg(any(test, feature = "cpu-parity"))]
pub fn cpu_ref(parent: &[u32], target: u32, max_depth: u32, scratch: &mut Vec<u32>) -> u32 {
scratch.clear();
let mut current = target;
let mut len = 0u32;
let cap = max_depth as usize;
while (len as usize) < cap {
scratch.push(current);
len += 1;
let next = parent.get(current as usize).copied().unwrap_or(current);
if next == current {
break;
}
current = next;
}
while scratch.len() < cap {
scratch.push(0);
}
len
}
#[cfg(any(test, feature = "cpu-parity"))]
pub fn try_cpu_ref_batched(
parent: &[u32],
targets: &[u32],
max_depth: u32,
paths: &mut Vec<u32>,
lens: &mut Vec<u32>,
) -> Result<(), String> {
let mut scratch = Vec::new();
try_cpu_ref_batched_with_scratch(parent, targets, max_depth, paths, lens, &mut scratch)
}
#[cfg(any(test, feature = "cpu-parity"))]
pub fn try_cpu_ref_batched_with_scratch(
parent: &[u32],
targets: &[u32],
max_depth: u32,
paths: &mut Vec<u32>,
lens: &mut Vec<u32>,
scratch: &mut Vec<u32>,
) -> Result<(), String> {
let layout = validate_batched_path_reconstruct_layout(targets.len(), max_depth)?;
let depth = max_depth as usize;
scratch.clear();
crate::graph::scratch::reserve_graph_items(
paths,
layout.path_words,
"path reconstruction CPU oracle",
"batched path output",
)?;
crate::graph::scratch::reserve_graph_items(
lens,
layout.target_count as usize,
"path reconstruction CPU oracle",
"batched length output",
)?;
crate::graph::scratch::reserve_graph_items(
scratch,
depth,
"path reconstruction CPU oracle",
"per-target path scratch",
)?;
paths.clear();
lens.clear();
for &target in targets {
let len = cpu_ref(parent, target, max_depth, scratch);
paths.extend_from_slice(&scratch);
lens.push(len);
}
Ok(())
}
#[cfg(any(test, feature = "cpu-parity"))]
pub fn cpu_ref_batched(
parent: &[u32],
targets: &[u32],
max_depth: u32,
paths: &mut Vec<u32>,
lens: &mut Vec<u32>,
) {
try_cpu_ref_batched(parent, targets, max_depth, paths, lens)
.expect("Fix: batched path reconstruction CPU oracle allocation failed");
}
#[cfg(feature = "inventory-registry")]
inventory::submit! {
crate::harness::OpEntry::new(
OP_ID,
|| path_reconstruct("parent", "target", "path_out", "path_len", 4),
Some(|| {
let to_bytes = |w: &[u32]| crate::wire::pack_u32_slice(w);
vec![vec![
to_bytes(&[0, 0, 1, 2]),
to_bytes(&[3]),
to_bytes(&[0, 0, 0, 0]),
to_bytes(&[0]),
]]
}),
Some(|| {
let to_bytes = |w: &[u32]| crate::wire::pack_u32_slice(w);
vec![vec![
to_bytes(&[3, 2, 1, 0]),
to_bytes(&[4]),
]]
}),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn walks_parent_chain_to_root() {
let mut scratch = Vec::with_capacity(4);
let len = cpu_ref(&[0, 0, 1, 2], 3, 4, &mut scratch);
assert_eq!(len, 4);
assert_eq!(&scratch[0..4], &[3, 2, 1, 0]);
}
#[test]
fn terminates_on_max_depth() {
let mut scratch = Vec::with_capacity(8);
let len = cpu_ref(&[1, 0], 0, 8, &mut scratch);
assert_eq!(len, 8);
assert_eq!(&scratch[..], &[0, 1, 0, 1, 0, 1, 0, 1]);
}
#[test]
fn tail_is_zero_padded_when_root_reached_before_cap() {
let mut scratch = Vec::with_capacity(8);
let len = cpu_ref(&[0, 0, 1, 2], 3, 8, &mut scratch);
assert_eq!(len, 4);
assert_eq!(&scratch[..4], &[3, 2, 1, 0]);
assert_eq!(&scratch[4..], &[0, 0, 0, 0]);
}
#[test]
fn parent_self_loops_terminate_immediately() {
let mut scratch = Vec::with_capacity(4);
let len = cpu_ref(&[0, 1], 1, 4, &mut scratch);
assert_eq!(len, 1);
assert_eq!(scratch[0], 1);
assert_eq!(&scratch[1..], &[0, 0, 0]);
}
#[test]
fn deep_chain_within_max_depth() {
let parent = &[0, 0, 1, 2, 3];
let mut scratch = Vec::with_capacity(8);
let len = cpu_ref(parent, 4, 8, &mut scratch);
assert_eq!(len, 5);
assert_eq!(&scratch[..5], &[4, 3, 2, 1, 0]);
assert_eq!(&scratch[5..], &[0, 0, 0]);
}
#[test]
fn target_not_in_parent_array_terminates_at_target() {
let mut scratch = Vec::with_capacity(4);
let len = cpu_ref(&[0, 0, 1], 5, 4, &mut scratch);
assert_eq!(len, 1);
assert_eq!(scratch[0], 5);
assert_eq!(&scratch[1..], &[0, 0, 0]);
}
#[test]
fn max_depth_zero_returns_empty_path() {
let mut scratch = Vec::with_capacity(4);
let len = cpu_ref(&[0, 0, 1, 2], 3, 0, &mut scratch);
assert_eq!(len, 0);
assert!(scratch.is_empty());
}
#[test]
fn max_depth_one_returns_only_target() {
let mut scratch = Vec::with_capacity(4);
let len = cpu_ref(&[0, 0, 1, 2], 3, 1, &mut scratch);
assert_eq!(len, 1);
assert_eq!(scratch[0], 3);
assert_eq!(scratch.len(), 1, "cap == max_depth == 1, no padding needed");
}
#[test]
fn program_builder_max_depth_zero_emits_trap() {
let p = path_reconstruct("parent", "target", "out", "len", 0);
let entry = p.entry();
let has_trap = entry.iter().any(|n| {
if let Node::Region { body, .. } = n {
body.iter().any(|inner| matches!(inner, Node::Trap { .. }))
} else {
matches!(n, Node::Trap { .. })
}
});
assert!(
has_trap,
"max_depth == 0 must produce a trap program, not panic"
);
}
#[test]
fn batched_program_has_expected_buffers_and_workgroup() {
let p = batched_path_reconstruct(3, 4);
assert_eq!(p.workgroup_size, [BATCHED_WORKGROUP_SIZE, 1, 1]);
let names: Vec<&str> = p.buffers.iter().map(|b| b.name()).collect();
assert_eq!(names, vec!["parent", "targets", "paths", "lens"]);
assert_eq!(p.buffers[1].count(), 3);
assert_eq!(p.buffers[2].count(), 12);
assert_eq!(p.buffers[3].count(), 3);
}
#[test]
fn batched_layout_validator_accepts_empty_and_canonical_batches() {
assert_eq!(
validate_batched_path_reconstruct_layout(0, 4).unwrap(),
BatchedPathReconstructLayout {
target_count: 0,
path_words: 0,
path_words_u32: 0,
}
);
assert_eq!(
validate_batched_path_reconstruct_layout(3, 4).unwrap(),
BatchedPathReconstructLayout {
target_count: 3,
path_words: 12,
path_words_u32: 12,
}
);
}
#[test]
fn batched_layout_validator_rejects_zero_depth_and_overflow() {
let err = validate_batched_path_reconstruct_layout(3, 0).unwrap_err();
assert!(err.contains("max_depth must be >= 1"));
let err = validate_batched_path_reconstruct_layout(u32::MAX as usize + 1, 1).unwrap_err();
assert!(err.contains("target count"));
let err = validate_batched_path_reconstruct_layout(u32::MAX as usize, 2).unwrap_err();
assert!(err.contains("target_count*max_depth"));
}
#[test]
fn batched_cpu_ref_matches_single_target_segments() {
let mut paths = Vec::new();
let mut lens = Vec::new();
cpu_ref_batched(&[0, 0, 1, 2], &[3, 0, 2], 4, &mut paths, &mut lens);
assert_eq!(lens, vec![4, 1, 3]);
assert_eq!(&paths[0..4], &[3, 2, 1, 0]);
assert_eq!(&paths[4..8], &[0, 0, 0, 0]);
assert_eq!(&paths[8..12], &[2, 1, 0, 0]);
}
#[test]
fn batched_cpu_ref_with_scratch_reuses_all_storage() {
let mut paths = Vec::with_capacity(32);
let mut lens = Vec::with_capacity(8);
let mut scratch = Vec::with_capacity(8);
paths.extend_from_slice(&[0xDEAD_BEEF; 5]);
lens.extend_from_slice(&[0xCAFE_BABE; 3]);
scratch.extend_from_slice(&[0xFEED_FACE; 6]);
let paths_capacity = paths.capacity();
let lens_capacity = lens.capacity();
let scratch_capacity = scratch.capacity();
try_cpu_ref_batched_with_scratch(
&[0, 0, 1, 2],
&[3, 0, 2],
4,
&mut paths,
&mut lens,
&mut scratch,
)
.expect("Fix: valid batched path reconstruction must evaluate.");
assert_eq!(lens, vec![4, 1, 3]);
assert_eq!(&paths[0..4], &[3, 2, 1, 0]);
assert_eq!(&paths[4..8], &[0, 0, 0, 0]);
assert_eq!(&paths[8..12], &[2, 1, 0, 0]);
assert_eq!(scratch, vec![2, 1, 0, 0]);
assert_eq!(paths.capacity(), paths_capacity);
assert_eq!(lens.capacity(), lens_capacity);
assert_eq!(scratch.capacity(), scratch_capacity);
try_cpu_ref_batched_with_scratch(
&[0, 0, 1, 2],
&[1],
2,
&mut paths,
&mut lens,
&mut scratch,
)
.expect("Fix: second valid batch must reuse and truncate buffers.");
assert_eq!(lens, vec![2]);
assert_eq!(paths, vec![1, 0]);
assert_eq!(scratch, vec![1, 0]);
assert_eq!(paths.capacity(), paths_capacity);
assert_eq!(lens.capacity(), lens_capacity);
assert_eq!(scratch.capacity(), scratch_capacity);
}
#[test]
fn batched_cpu_ref_rejects_zero_depth_like_dispatch_planner() {
let mut paths = vec![0xDEAD_BEEF];
let mut lens = vec![0xCAFE_BABE];
let err = try_cpu_ref_batched(&[0], &[0], 0, &mut paths, &mut lens).unwrap_err();
assert!(err.contains("max_depth must be >= 1"));
assert_eq!(paths, vec![0xDEAD_BEEF]);
assert_eq!(lens, vec![0xCAFE_BABE]);
}
#[test]
fn generated_batched_cpu_ref_matches_single_target_oracle_shapes() {
for target_count in 0usize..64 {
for depth in 1u32..65 {
let parent: Vec<u32> = (0..128u32)
.map(|node| if node == 0 { 0 } else { node - 1 })
.collect();
let targets: Vec<u32> = (0..target_count)
.map(|index| ((index * 17 + depth as usize * 3) % parent.len()) as u32)
.collect();
let mut paths = vec![0xDEAD_BEEFu32; 3];
let mut lens = vec![0xCAFE_BABEu32; 2];
try_cpu_ref_batched(&parent, &targets, depth, &mut paths, &mut lens).unwrap();
assert_eq!(lens.len(), targets.len());
assert_eq!(paths.len(), targets.len() * depth as usize);
let mut single = Vec::new();
for (target_index, &target) in targets.iter().enumerate() {
let expected_len = cpu_ref(&parent, target, depth, &mut single);
assert_eq!(lens[target_index], expected_len);
let start = target_index * depth as usize;
let end = start + depth as usize;
assert_eq!(&paths[start..end], &single[..]);
}
}
}
}
#[test]
fn batched_program_zero_depth_emits_trap() {
let p = batched_path_reconstruct(3, 0);
let entry = p.entry();
let has_trap = entry.iter().any(|n| {
if let Node::Region { body, .. } = n {
body.iter().any(|inner| matches!(inner, Node::Trap { .. }))
} else {
matches!(n, Node::Trap { .. })
}
});
assert!(has_trap, "zero-depth batched path reconstruction must trap");
}
}