use crate::interval::{
intersect, is_empty, is_finite, scale, size, space_between, translate, TBLInterval,
};
use crate::{Block, Bound, TBLError};
use itertools::Itertools;
use std::fmt::Debug;
use std::iter;
#[derive(Clone)]
pub(crate) enum TBLBlock<L>
where
L: Clone + Debug,
{
Space(TBLInterval<L>),
Segment(TBLInterval<L>),
}
impl<L> Default for TBLBlock<L>
where
L: Clone + Debug,
{
fn default() -> Self {
TBLBlock::Space(TBLInterval::new((0., 1.), None::<L>))
}
}
impl<L> From<TBLBlock<L>> for Block<L>
where
L: Clone + Debug,
{
fn from(b: TBLBlock<L>) -> Self {
match b {
TBLBlock::Space(interval) => Block::Space(size(&interval)),
TBLBlock::Segment(interval) => Block::Segment(size(&interval), interval.label),
}
}
}
pub(crate) fn build_blocks<L>(
intervals: &[TBLInterval<L>],
length: usize,
boundaries: Option<Bound>,
) -> Result<Vec<TBLBlock<L>>, TBLError<L>>
where
L: Clone + Debug,
{
let intervals: Vec<&TBLInterval<L>> = intervals
.iter()
.filter(|interval| is_finite(interval))
.filter(|interval| !is_empty(interval))
.sorted()
.collect();
if let Some(intersection) = intervals
.iter()
.tuple_windows::<(_, _)>()
.find(|(left, right)| intersect(left, right))
{
let (left, right) = intersection;
return Err(TBLError::Intersection(
left.label.clone(),
right.label.clone(),
));
}
let blocks: Vec<TBLBlock<L>> = match intervals.as_slice() {
[] => vec![TBLBlock::default()],
[interval] => vec![TBLBlock::Segment(TBLInterval::new(
interval.bounds,
interval.label.clone(),
))],
_ => {
let none_delimited = intervals.iter().map(Some).chain(iter::once(None));
let windowed = none_delimited.tuple_windows::<(_, _)>();
windowed
.map(|(left, right)| match (left, right) {
(Some(&left_interval), Some(&right_interval)) => {
iter::once(TBLBlock::Segment(left_interval.clone()))
.chain(iter::once(TBLBlock::Space(space_between(
&left_interval,
&right_interval,
))))
.collect()
}
(Some(&left_interval), _) => {
iter::once(TBLBlock::Segment(left_interval.clone())).collect()
}
_ => iter::empty().collect::<Vec<TBLBlock<L>>>(),
})
.flatten()
.collect()
}
};
let intervals_boundaries = crate::interval::boundaries_ref(intervals.as_slice());
let padded_blocks = match padding(intervals_boundaries, boundaries) {
(Some(left), Some(right)) => iter::once(TBLBlock::Space(TBLInterval::new(left, None)))
.chain(blocks)
.chain(iter::once(TBLBlock::Space(TBLInterval::new(right, None))))
.collect(),
(Some(left), None) => iter::once(TBLBlock::Space(TBLInterval::new(left, None)))
.chain(blocks)
.collect(),
(None, Some(right)) => blocks
.into_iter()
.chain(iter::once(TBLBlock::Space(TBLInterval::new(right, None))))
.collect(),
(None, None) => blocks,
};
let padded_blocks_boundaries = blocks_boundaries(padded_blocks.as_slice());
let (min_start, max_end) = (padded_blocks_boundaries.ok_or(TBLError::NoBoundaries))?;
let translation = -min_start;
let ratio = (length as f64) / (max_end - min_start);
let adjusted: Vec<TBLBlock<L>> = padded_blocks
.iter()
.map(|b| match b {
TBLBlock::Space(interval) => TBLBlock::Space(translate(interval, translation)),
TBLBlock::Segment(interval) => TBLBlock::Segment(translate(interval, translation)),
})
.map(|b| match b {
TBLBlock::Space(interval) => TBLBlock::Space(scale(&interval, ratio)),
TBLBlock::Segment(interval) => TBLBlock::Segment(scale(&interval, ratio)),
})
.collect();
Ok(adjusted)
}
fn padding(
intervals_boundaries: Option<Bound>,
boundaries: Option<Bound>,
) -> (Option<Bound>, Option<Bound>) {
match (intervals_boundaries, boundaries) {
(Some((a0, b0)), Some((a1, b1))) => {
let left = if a1 < a0 { Some((a1, a0)) } else { None };
let right = if b0 < b1 { Some((b0, b1)) } else { None };
(left, right)
}
_ => (None, None),
}
}
fn blocks_boundaries<L: Clone + Debug>(blocks: &[TBLBlock<L>]) -> Option<Bound> {
let intervals: Vec<&TBLInterval<L>> = blocks
.iter()
.map(|b| match b {
TBLBlock::Space(interval) => interval,
TBLBlock::Segment(interval) => interval,
})
.collect();
crate::interval::boundaries_ref(intervals.as_slice())
}