use cubecl::prelude::*;
use dspk_core::compute::{channel_position, sample_position};
pub const POLARITY_POSITIVE: u32 = 0;
pub const POLARITY_NEGATIVE: u32 = 1;
pub const POLARITY_BOTH: u32 = 2;
#[cube]
fn first_candidate_sample(scan_start: u32, b: u32, lanes: u32, block: u32) -> u32 {
let group = b / lanes;
scan_start + group * lanes * block + (b - group * lanes)
}
#[cube]
fn is_candidate<F: Float>(trace: &[F], row: u32, t: u32, height: F, polarity: u32) -> bool {
let v = trace[(row + t) as usize];
let prev = trace[(row + t - 1u32) as usize];
let next = trace[(row + t + 1u32) as usize];
let neg_height = F::new(0.0f32) - height;
let is_max = v >= height && prev < v && v >= next;
let is_min = v <= neg_height && prev > v && v <= next;
(polarity != POLARITY_NEGATIVE && is_max) || (polarity != POLARITY_POSITIVE && is_min)
}
#[cube(launch)]
pub fn count_peak_candidates_kernel<F: Float>(
trace: &[F],
heights: &[F],
block_counts: &mut [u32],
num_channels: u32,
num_samples: u32,
scan_start: u32,
scan_end: u32,
num_blocks: u32,
lanes: u32,
block: u32,
polarity: u32,
) {
let b = sample_position();
let ch = channel_position();
if ch < num_channels && b < num_blocks {
let height = heights[ch as usize];
let row = ch * num_samples;
let mut n = 0u32;
let mut t = first_candidate_sample(scan_start, b, lanes, block);
let mut i = 0u32;
while i < block && t < scan_end {
if is_candidate::<F>(trace, row, t, height, polarity) {
n += 1u32;
}
i += 1u32;
t += lanes;
}
block_counts[(ch * num_blocks + b) as usize] = n;
}
}
#[cube(launch)]
pub fn scan_candidate_counts_kernel(
block_counts: &mut [u32],
channel_totals: &mut [u32],
num_channels: u32,
num_blocks: u32,
) {
let ch = ABSOLUTE_POS_X;
if ch < num_channels {
let row = ch * num_blocks;
let mut acc = 0u32;
let mut b = 0u32;
while b < num_blocks {
let n = block_counts[(row + b) as usize];
block_counts[(row + b) as usize] = acc;
acc += n;
b += 1u32;
}
channel_totals[ch as usize] = acc;
}
}
#[cube(launch)]
pub fn write_peak_candidates_kernel<F: Float>(
trace: &[F],
heights: &[F],
block_offsets: &[u32],
channel_bases: &[u32],
out_indices: &mut [u32],
out_values: &mut [F],
out_rows: &mut [u32],
num_channels: u32,
num_samples: u32,
scan_start: u32,
scan_end: u32,
num_blocks: u32,
lanes: u32,
block: u32,
polarity: u32,
) {
let b = sample_position();
let ch = channel_position();
if ch < num_channels && b < num_blocks {
let offset = block_offsets[(ch * num_blocks + b) as usize];
let base = channel_bases[ch as usize];
let next = if b + 1u32 < num_blocks {
block_offsets[(ch * num_blocks + b + 1u32) as usize]
} else {
channel_bases[(ch + 1u32) as usize] - base
};
if next > offset {
let height = heights[ch as usize];
let row = ch * num_samples;
let mut slot = base + offset;
let mut t = first_candidate_sample(scan_start, b, lanes, block);
let mut i = 0u32;
while i < block && t < scan_end {
if is_candidate::<F>(trace, row, t, height, polarity) {
out_indices[slot as usize] = t;
out_values[slot as usize] = trace[(row + t) as usize];
out_rows[slot as usize] = ch;
slot += 1u32;
}
i += 1u32;
t += lanes;
}
}
}
}