#![allow(dead_code)]
use std::ops::Range;
use polydat::iteration::source::DataSource;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Realizability {
Dynamic,
Countable,
Rangeable,
Realizable,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Child {
Node(usize),
Ordinals(Range<u64>),
}
pub trait ChildSource {
fn poll_next(&mut self) -> Option<Child>;
fn realizability(&self) -> Realizability;
fn extent(&self) -> Option<u64> {
None
}
}
pub struct CountedSource {
next: usize,
len: usize,
}
impl CountedSource {
pub fn new(len: usize) -> Self {
Self { next: 0, len }
}
}
impl ChildSource for CountedSource {
fn poll_next(&mut self) -> Option<Child> {
if self.next >= self.len {
return None;
}
let i = self.next;
self.next += 1;
Some(Child::Node(i))
}
fn realizability(&self) -> Realizability {
Realizability::Realizable
}
fn extent(&self) -> Option<u64> {
Some(self.len as u64)
}
}
pub struct CursorSource {
source: Box<dyn DataSource>,
stride: usize,
}
impl CursorSource {
pub fn new(source: Box<dyn DataSource>, stride: usize) -> Self {
Self { source, stride }
}
#[inline]
pub fn render(&self, ordinal: u64) -> polydat::iteration::source::SourceItem {
self.source.render_item(ordinal)
}
}
impl ChildSource for CursorSource {
fn poll_next(&mut self) -> Option<Child> {
self.source.reserve(self.stride).map(Child::Ordinals)
}
fn realizability(&self) -> Realizability {
if self.source.extent().is_some() {
Realizability::Rangeable
} else {
Realizability::Dynamic
}
}
fn extent(&self) -> Option<u64> {
self.source.extent()
}
}
pub struct StanzaRuns<'a> {
per_pos_rows: &'a [usize],
pos: usize,
base: u64,
end: u64,
}
impl<'a> StanzaRuns<'a> {
pub fn new(range: Range<u64>, per_pos_rows: &'a [usize]) -> Self {
Self {
per_pos_rows,
pos: 0,
base: range.start,
end: range.end,
}
}
}
impl<'a> Iterator for StanzaRuns<'a> {
type Item = (usize, u64, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.per_pos_rows.len() || self.base >= self.end {
return None;
}
let rows = self.per_pos_rows[self.pos].max(1) as u64;
let sub_end = (self.base + rows).min(self.end);
let item = (self.pos, self.base, (sub_end - self.base) as usize);
self.pos += 1;
self.base = sub_end;
Some(item)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Drive {
BoundedSpawn,
CursorReserve,
}
pub fn select_drive(realizability: Realizability) -> Drive {
match realizability {
Realizability::Realizable | Realizability::Countable => Drive::BoundedSpawn,
Realizability::Rangeable | Realizability::Dynamic => Drive::CursorReserve,
}
}
#[cfg(test)]
mod tests {
use super::*;
use polydat::iteration::source::{DataSourceFactory, RangeSourceFactory};
#[test]
fn counted_source_is_realizable_node_indices() {
let mut s = CountedSource::new(3);
assert_eq!(s.realizability(), Realizability::Realizable);
assert_eq!(s.extent(), Some(3));
let mut got = vec![];
while let Some(Child::Node(i)) = s.poll_next() {
got.push(i);
}
assert_eq!(got, vec![0, 1, 2]);
}
#[test]
fn cursor_source_over_range_is_rangeable_and_yields_ordinals() {
let f = RangeSourceFactory::new(0, 5);
let mut s = CursorSource::new(f.create_reader(), 2);
assert_eq!(s.realizability(), Realizability::Rangeable);
assert_eq!(s.extent(), Some(5));
let mut ranges = vec![];
while let Some(Child::Ordinals(r)) = s.poll_next() {
ranges.push(r);
}
assert_eq!(ranges, vec![0..2, 2..4, 4..5]);
}
#[test]
fn realizability_ladder_is_ordered() {
assert!(Realizability::Dynamic < Realizability::Countable);
assert!(Realizability::Countable < Realizability::Rangeable);
assert!(Realizability::Rangeable < Realizability::Realizable);
}
#[test]
fn select_drive_routes_by_level() {
assert_eq!(select_drive(Realizability::Realizable), Drive::BoundedSpawn);
assert_eq!(select_drive(Realizability::Countable), Drive::BoundedSpawn);
assert_eq!(select_drive(Realizability::Rangeable), Drive::CursorReserve);
assert_eq!(select_drive(Realizability::Dynamic), Drive::CursorReserve);
}
#[test]
fn select_drive_aligns_with_source_kinds() {
assert_eq!(
select_drive(CountedSource::new(2).realizability()),
Drive::BoundedSpawn
);
let cursor = CursorSource::new(RangeSourceFactory::new(0, 4).create_reader(), 2);
assert_eq!(select_drive(cursor.realizability()), Drive::CursorReserve);
}
#[test]
fn stanza_runs_cover_once_over_cursor_with_partial_tail() {
const M: u64 = 1000;
const N: usize = 300;
let per_pos_rows = [N]; let stanza_stride = N;
let mut source =
CursorSource::new(RangeSourceFactory::new(0, M).create_reader(), stanza_stride);
let mut covered: Vec<(u64, usize)> = Vec::new(); let mut next_expected: u64 = 0;
while let Some(Child::Ordinals(range)) = source.poll_next() {
for (pos, base, run_len) in StanzaRuns::new(range, &per_pos_rows) {
assert_eq!(pos, 0, "single-op stanza is always position 0");
assert_eq!(base, next_expected, "gap or overlap at ordinal {base}");
next_expected = base + run_len as u64;
covered.push((base, run_len));
}
}
assert_eq!(covered, vec![(0, 300), (300, 300), (600, 300), (900, 100)]);
assert_eq!(covered.last().unwrap().1, (M % N as u64) as usize);
let total: usize = covered.iter().map(|&(_, len)| len).sum();
assert_eq!(total as u64, M);
}
#[test]
fn stanza_runs_multi_op_stanza_tiles_reserved_stride() {
let per_pos_rows = [1, 4];
let runs: Vec<_> = StanzaRuns::new(10..15, &per_pos_rows).collect();
assert_eq!(runs, vec![(0, 10, 1), (1, 11, 4)]);
assert_eq!(runs.iter().map(|&(_, _, l)| l).sum::<usize>(), 5);
}
#[test]
fn stanza_runs_truncates_short_tail_mid_stanza() {
let per_pos_rows = [1, 4];
let runs: Vec<_> = StanzaRuns::new(12..15, &per_pos_rows).collect();
assert_eq!(runs, vec![(0, 12, 1), (1, 13, 2)]);
assert_eq!(runs.iter().map(|&(_, _, l)| l).sum::<usize>(), 3);
let runs2: Vec<_> = StanzaRuns::new(14..15, &per_pos_rows).collect();
assert_eq!(runs2, vec![(0, 14, 1)]);
}
}