use rlx_ir::Dim;
use crate::Tensor;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SliceAxis {
All,
Index(i64),
Range { start: i64, end: i64 },
From(i64),
}
#[inline]
pub fn ax() -> SliceAxis {
SliceAxis::All
}
#[inline]
pub fn ix(i: i64) -> SliceAxis {
SliceAxis::Index(i)
}
#[inline]
pub fn rg(start: i64, end: i64) -> SliceAxis {
SliceAxis::Range { start, end }
}
#[inline]
pub fn tail(start: i64) -> SliceAxis {
SliceAxis::From(start)
}
pub(crate) fn resolve_index(idx: i64, dim: Option<usize>, axis: usize) -> usize {
if idx >= 0 {
return idx as usize;
}
let n = dim
.unwrap_or_else(|| panic!("negative index requires a static dimension on axis {axis}"))
as i64;
let resolved = n + idx;
assert!(
resolved >= 0,
"index {idx} out of bounds for axis {axis} of size {n}"
);
resolved as usize
}
#[derive(Debug, Clone, Default)]
pub struct SliceSpec {
axes: Vec<SliceAxis>,
}
impl SliceSpec {
pub fn new(axes: Vec<SliceAxis>) -> Self {
Self { axes }
}
pub fn apply(&self, tensor: &Tensor) -> Tensor {
let mut out = tensor.clone();
let shape = tensor.shape();
for (axis, spec) in self.axes.iter().enumerate() {
let dim = match shape.dims().get(axis) {
Some(Dim::Static(n)) => Some(*n),
Some(Dim::Dynamic(_)) => None,
None => panic!("slice axis {axis} out of bounds (rank {})", shape.rank()),
};
out = match spec {
SliceAxis::All => out,
SliceAxis::Index(i) => out.narrow(axis, resolve_index(*i, dim, axis), 1),
SliceAxis::Range { start, end } => {
let s = resolve_index(*start, dim, axis);
let e = resolve_index(*end, dim, axis);
assert!(e > s, "slice range end must exceed start (axis {axis})");
out.narrow(axis, s, e - s)
}
SliceAxis::From(start) => {
let s = resolve_index(*start, dim, axis);
let n = dim.unwrap_or_else(|| {
panic!("slice `start..` requires a static dimension on axis {axis}")
});
out.narrow(axis, s, n - s)
}
};
}
out
}
}
#[macro_export]
macro_rules! s {
( $( $axis:expr ),* $(,)? ) => {
$crate::SliceSpec::new(vec![ $( $axis ),* ])
};
}