use super::*;
pub trait Offset: Sized + Copy + Clone + Deref<Target = u32> + From<u32> {
type Quantum: Quantum;
fn to_absolute(&self, dim: QDim<Self>, power: u8) -> QAbs<Self>;
fn to_quantum(&self, power: u8) -> Self::Quantum;
fn from_absolute_rounded(loc: Loc, dim: QDim<Self>, power: u8) -> Self;
}
pub(crate) type QAbs<O> = <<O as Offset>::Quantum as Quantum>::Absolute;
pub(crate) type QDim<O> = <<O as Offset>::Quantum as Quantum>::Dim;
#[derive(
Copy,
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
derive_more::Add,
derive_more::Sub,
derive_more::Mul,
derive_more::Div,
derive_more::Deref,
derive_more::DerefMut,
derive_more::From,
derive_more::Into,
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(
feature = "fuzzing",
derive(arbitrary::Arbitrary, proptest_derive::Arbitrary)
)]
#[serde(transparent)]
pub struct SpaceOffset(pub u32);
#[derive(
Copy,
Clone,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
derive_more::Add,
derive_more::Sub,
derive_more::Mul,
derive_more::Div,
derive_more::Deref,
derive_more::DerefMut,
derive_more::From,
derive_more::Into,
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(
feature = "fuzzing",
derive(arbitrary::Arbitrary, proptest_derive::Arbitrary)
)]
#[serde(transparent)]
pub struct TimeOffset(pub u32);
impl Offset for SpaceOffset {
type Quantum = SpaceQuantum;
fn to_absolute(&self, dim: SpaceDimension, power: u8) -> Loc {
self.wrapping_mul(dim.quantum)
.wrapping_mul(pow2(power))
.into()
}
fn to_quantum(&self, power: u8) -> Self::Quantum {
self.wrapping_mul(pow2(power)).into()
}
fn from_absolute_rounded(loc: Loc, dim: SpaceDimension, power: u8) -> Self {
(loc.as_u32() / dim.quantum / pow2(power)).into()
}
}
impl Offset for TimeOffset {
type Quantum = TimeQuantum;
fn to_absolute(&self, dim: TimeDimension, power: u8) -> Timestamp {
Timestamp::from_micros(self.wrapping_mul(dim.quantum).wrapping_mul(pow2(power)) as i64)
}
fn to_quantum(&self, power: u8) -> Self::Quantum {
self.wrapping_mul(pow2(power)).into()
}
fn from_absolute_rounded(loc: Loc, dim: TimeDimension, power: u8) -> Self {
(loc.as_u32() / dim.quantum / pow2(power)).into()
}
}
#[derive(
Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize,
)]
#[cfg_attr(
feature = "fuzzing",
derive(arbitrary::Arbitrary, proptest_derive::Arbitrary)
)]
pub struct Segment<O: Offset> {
pub power: u8,
pub offset: O,
}
impl<O: Offset> Segment<O> {
pub fn new<OO: Into<O>>(power: u8, offset: OO) -> Self {
Self {
power,
offset: offset.into(),
}
}
pub fn num_quanta(&self) -> u64 {
2u64.pow(self.power.into())
}
pub fn absolute_length(&self, dim: QDim<O>) -> u64 {
let q = dim.into().quantum as u64;
self.num_quanta() * q
}
pub fn quantum_bounds(&self, dim: QDim<O>) -> (O::Quantum, O::Quantum) {
let n = self.num_quanta();
let a = (n * u64::from(*self.offset)) as u32;
(
O::Quantum::from(a).normalized(dim),
O::Quantum::from(a.wrapping_add(n as u32).wrapping_sub(1)).normalized(dim),
)
}
pub fn contains_quantum(&self, dim: QDim<O>, coord: O::Quantum) -> bool {
let (lo, hi) = self.quantum_bounds(dim);
let coord = coord.normalized(dim);
if lo <= hi {
lo <= coord && coord <= hi
} else {
lo <= coord || coord <= hi
}
}
pub fn bisect(&self) -> Option<[Self; 2]> {
if self.power == 0 {
None
} else {
let power = self.power - 1;
Some([
Segment::new(power, O::from(self.offset.wrapping_mul(2))),
Segment::new(power, O::from(self.offset.wrapping_mul(2).wrapping_add(1))),
])
}
}
}
impl SpaceSegment {
pub fn loc_bounds(&self, dim: impl SpaceDim) -> (Loc, Loc) {
let (a, b): (u32, u32) = bounds(dim.into().into(), self.power, self.offset, 1);
(Loc::from(a), Loc::from(b))
}
}
impl TimeSegment {
pub fn timestamp_bounds(&self, topo: &Topology) -> (Timestamp, Timestamp) {
let (a, b): (i64, i64) = bounds64(topo.time.into(), self.power, self.offset, 1);
let o = topo.time_origin.as_micros();
(Timestamp::from_micros(a + o), Timestamp::from_micros(b + o))
}
}
pub type SpaceSegment = Segment<SpaceOffset>;
pub type TimeSegment = Segment<TimeOffset>;
pub(super) fn bounds<N: From<u32>>(
dim: Dimension,
power: u8,
offset: SpaceOffset,
count: u32,
) -> (N, N) {
debug_assert_ne!(dim.quantum, 0);
debug_assert_ne!(count, 0);
let q = dim.quantum.wrapping_mul(pow2(power));
let start = offset.wrapping_mul(q);
let len = count.wrapping_mul(q);
(start.into(), start.wrapping_add(len).wrapping_sub(1).into())
}
pub(super) fn bounds64<N: From<i64>>(
dim: Dimension,
power: u8,
offset: TimeOffset,
count: u32,
) -> (N, N) {
debug_assert_ne!(dim.quantum, 0);
debug_assert_ne!(count, 0);
let q = dim.quantum as i64 * 2i64.pow(power.into());
let start = (*offset as i64).wrapping_mul(q);
let len = (count as i64).wrapping_mul(q);
(start.into(), start.wrapping_add(len).wrapping_sub(1).into())
}