use cubecl::prelude::*;
use dspk_core::compute::row_position;
pub const RADIX_BITS: u32 = 8;
pub const RADIX_BINS: u32 = 1 << RADIX_BITS;
#[cube(launch)]
#[allow(clippy::too_many_arguments)]
pub fn row_abs_kth_radix_kernel<F: Float, K: Int>(
input: &[F],
out: &mut [F],
rows: u32,
row_stride: u32,
col_start: u32,
cols: u32,
k: u32,
#[comptime] key_bits: u32,
#[comptime] units: u32,
) {
let row = row_position();
if row < rows {
let unit = UNIT_POS_X;
let base = row * row_stride + col_start;
let passes = comptime!(key_bits / RADIX_BITS);
let digit_mask = K::cast_from(RADIX_BINS - 1);
let hist = Shared::<[Atomic<u32>]>::new_slice(comptime!(RADIX_BINS as usize));
let mut fixed = Shared::<[K]>::new_slice(2usize);
let mut rank = Shared::<[u32]>::new_slice(1usize);
if unit == 0u32 {
fixed[0] = K::cast_from(0u32);
fixed[1] = K::cast_from(0u32);
rank[0] = k;
}
sync_cube();
#[unroll]
for pass in 0..passes {
let shift = K::cast_from(comptime!(key_bits - RADIX_BITS * (pass + 1)));
let mut b = unit;
while b < RADIX_BINS {
Atomic::store(&hist[b as usize], 0u32);
b += units;
}
sync_cube();
let (prefix, mask) = (fixed[0], fixed[1]);
let mut col = unit;
while col < cols {
let key = K::reinterpret(F::abs(input[(base + col) as usize]));
if (key & mask) == prefix {
let digit = u32::cast_from((key >> shift) & digit_mask);
Atomic::fetch_add(&hist[digit as usize], 1u32);
}
col += units;
}
sync_cube();
if unit == 0u32 {
let mut remaining = rank[0];
let mut chosen = 0u32;
let mut found = 0u32;
let mut b = 0u32;
while b < RADIX_BINS {
let count = Atomic::load(&hist[b as usize]);
if found == 0u32 {
if remaining < count {
chosen = b;
found = 1u32;
} else {
remaining -= count;
}
}
b += 1u32;
}
rank[0] = remaining;
fixed[0] = prefix | (K::cast_from(chosen) << shift);
fixed[1] = mask | (digit_mask << shift);
}
sync_cube();
}
if unit == 0u32 {
out[row as usize] = F::reinterpret(fixed[0]);
}
}
}