use cubecl::prelude::*;
use dspk_core::compute::{row_position, row_share};
pub const RADIX_BITS: u32 = 8;
pub const RADIX_BINS: u32 = 1 << RADIX_BITS;
pub const KEY_ABS: u32 = 0;
pub const KEY_SIGNED: u32 = 1;
pub const KEY_ABS_DEV: u32 = 2;
#[cube]
fn order_key<F: Float, K: Int>(x: F, #[comptime] key_bits: u32) -> K {
let bits = K::reinterpret(x);
let top = K::cast_from(1u32) << K::cast_from(comptime!(key_bits - 1));
let flip = if (bits & top) == top { !K::cast_from(0u32) } else { top };
bits ^ flip
}
#[cube]
fn key_value<F: Float, K: Int>(key: K, #[comptime] key_bits: u32) -> F {
let top = K::cast_from(1u32) << K::cast_from(comptime!(key_bits - 1));
let flip = if (key & top) == top { top } else { !K::cast_from(0u32) };
F::reinterpret(key ^ flip)
}
#[cube]
fn select_key<F: Float, K: Int>(x: F, center: F, #[comptime] key_bits: u32, #[comptime] mode: u32) -> K {
if comptime!(mode == KEY_SIGNED) {
order_key::<F, K>(x, key_bits)
} else if comptime!(mode == KEY_ABS_DEV) {
K::reinterpret(F::abs(x - center))
} else {
K::reinterpret(F::abs(x))
}
}
#[cube(launch)]
#[allow(clippy::too_many_arguments)]
pub fn row_kth_radix_kernel<F: Float, K: Int>(
input: &[F],
center: &[F],
out: &mut [F],
rows: u32,
row_stride: u32,
col_start: u32,
cols: u32,
k: u32,
#[comptime] key_bits: u32,
#[comptime] units: u32,
#[comptime] mode: u32,
) {
let row = row_position();
if row < rows {
let unit = UNIT_POS_X;
let base = row * row_stride + col_start;
let c = if comptime!(mode == KEY_ABS_DEV) { center[row as usize] } else { F::new(0.0f32) };
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 share = row_share(cols, units);
let mut col = share.start;
while col < share.end {
let key = select_key::<F, K>(input[(base + col) as usize], c, key_bits, mode);
if (key & mask) == prefix {
let digit = u32::cast_from((key >> shift) & digit_mask);
Atomic::fetch_add(&hist[digit as usize], 1u32);
}
col += share.step;
}
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] = if comptime!(mode == KEY_SIGNED) { key_value::<F, K>(fixed[0], key_bits) } else { F::reinterpret(fixed[0]) };
}
}
}