use crate::{error, error::Error};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GridSpec {
dimension: u32,
size: u32,
length: u32,
order: Option<u32>,
bits_per_axis: Option<u32>,
}
impl GridSpec {
pub fn new(dimension: u32, size: u32) -> error::Result<Self> {
if dimension == 0 {
return Err(Error::Shape("dimension must be >= 1".to_string()));
}
if size == 0 {
return Err(Error::Size("size must be >= 1".to_string()));
}
let length = size.checked_pow(dimension).ok_or_else(|| {
Error::Size("curve length (size^dimension) exceeds u32 bounds".to_string())
})?;
Ok(Self {
dimension,
size,
length,
order: None,
bits_per_axis: None,
})
}
pub fn power_of_two(dimension: u32, size: u32) -> error::Result<Self> {
if size == 0 || !size.is_power_of_two() {
return Err(Error::Size(
"size must be a positive power of two".to_string(),
));
}
let mut spec = Self::new(dimension, size)?;
let order = size.trailing_zeros();
spec.order = Some(order);
spec.bits_per_axis = Some(order);
Ok(spec)
}
pub fn require_index_bits_lt(&self, limit: u32) -> error::Result<()> {
if let Some(bits) = self.bits_per_axis {
let total_bits = (bits as u64) * (self.dimension as u64);
if total_bits >= limit as u64 {
return Err(Error::Size(format!(
"index requires {total_bits} bits; must be < {limit} for u32 indices"
)));
}
}
Ok(())
}
pub fn dimension(&self) -> u32 {
self.dimension
}
pub fn size(&self) -> u32 {
self.size
}
pub fn length(&self) -> u32 {
self.length
}
pub fn order(&self) -> Option<u32> {
self.order
}
pub fn bits_per_axis(&self) -> Option<u32> {
self.bits_per_axis
}
}