use cubecl::zspace::SmallVec;
use crate::BoundaryPolicy;
use crate::{Axis, Boundary, Projection, Refusal, Space};
pub(crate) struct Boundaries {
pub(crate) modes: SmallVec<[Option<Boundary>; Space::MAX_RANK]>,
}
impl Boundaries {
fn mode(
policy: Option<BoundaryPolicy>,
projection: &Projection,
addressed: &[Axis],
concrete: &Space,
overhangs: &[Axis],
) -> Option<Boundary> {
let boundary = match policy {
Some(BoundaryPolicy::Unchecked) => return None,
Some(BoundaryPolicy::Every(boundary)) => return Some(boundary),
Some(BoundaryPolicy::AnyExtent(boundary)) => boundary,
None => Boundary::Zero,
};
let overhangs = addressed
.iter()
.filter(|&&axis| concrete.contains(axis))
.any(|axis| overhangs.contains(axis));
(overhangs || projection.may_underflow()).then_some(boundary)
}
pub(crate) fn new(
policy: Option<BoundaryPolicy>,
projection: &Projection,
addressed: &[Axis],
concrete: &Space,
overhangs: &[Axis],
width: usize,
) -> Result<Self, Refusal> {
let coord_rank = projection.coordinate_rank();
if coord_rank == 0 {
return Err(Refusal::NoCoordinate);
}
let Some(boundary) = Self::mode(policy, projection, addressed, concrete, overhangs) else {
return Ok(Boundaries {
modes: SmallVec::new(),
});
};
let coords = projection.untiled();
let settled = |pa: usize| {
coords
.physical_axis(pa)
.flattened_axes(concrete)
.is_some_and(|axes| axes.iter().all(|axis| !overhangs.contains(axis)))
};
if width > 1 && !settled(coord_rank - 1) {
return Err(Refusal::UncheckableVectorEdge);
}
let modes: SmallVec<[Option<Boundary>; Space::MAX_RANK]> = (0..coord_rank)
.map(|pa| (!settled(pa)).then_some(boundary))
.collect();
Ok(match modes.iter().any(Option::is_some) {
true => Boundaries { modes },
false => Boundaries {
modes: SmallVec::new(),
},
})
}
}