use std::ops::Bound::{Excluded, Included, Unbounded};
use std::{fmt, marker::PhantomData, ops::RangeBounds};
use thiserror::Error;
use crate::Index;
#[allow(missing_docs)]
#[derive(Debug, Error)]
pub enum Error {
#[error(
"Cannot build JaggedArray: `ends` represents the end of each row in `data`, \
it should be monotonically increasing. \
Found `end` at position {i} lower than `end` at position {}", .i - 1
)]
BadEnd { i: usize },
#[error(
"Cannot build JaggedArray: `ends` represents the end of each row in `data`, \
Yet, `end` at position {i} ({end}) is larger than the length of data ({len})"
)]
TooLongEnd { i: usize, len: usize, end: usize },
}
#[derive(PartialEq, Eq, Clone)]
pub struct JaggedArray<V, I: Index = u32, E: AsRef<[I]> = Box<[I]>, VS: AsRef<[V]> = Box<[V]>> {
ends: E,
data: VS,
_i: PhantomData<fn([I], [V])>,
}
impl<V, I: Index, E: AsRef<[I]>, VS: AsRef<[V]>> JaggedArray<V, I, E, VS> {
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.data.as_ref().len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.as_ref().is_empty()
}
#[inline]
#[must_use]
pub fn height(&self) -> usize {
self.ends.as_ref().len() + 1
}
pub fn new(ends: E, data: VS) -> Result<Self, Error> {
let mut previous_end = I::new(0);
let last_end = data.as_ref().len();
for (i, end) in ends.as_ref().iter().enumerate() {
if end.get() > last_end {
return Err(Error::TooLongEnd { i, len: last_end, end: end.get() });
}
if end.get() < previous_end.get() {
return Err(Error::BadEnd { i });
}
previous_end = I::new(end.get());
}
Ok(Self { ends, data, _i: PhantomData })
}
#[inline]
#[must_use]
pub fn get(&self, direct_index: usize) -> Option<&V> {
self.data.as_ref().get(direct_index)
}
#[must_use]
pub fn row(&self, index: usize) -> &[V] {
self.get_row(index).unwrap()
}
#[must_use]
pub fn get_row(&self, index: usize) -> Option<&[V]> {
self.get_rows(index..=index)
}
#[must_use]
pub fn rows(&self, range: impl RangeBounds<usize>) -> &[V] {
self.get_rows(range).unwrap()
}
#[inline]
#[must_use]
pub fn get_rows(&self, range: impl RangeBounds<usize>) -> Option<&[V]> {
let ends = self.ends.as_ref();
let get_end = |i| match i {
n if n == ends.len() => Some(self.len()),
n if n >= ends.len() => None,
n => ends.get(n).map(I::get),
};
let start = match range.start_bound() {
Included(0) | Unbounded => 0,
Included(&start) => get_end(start - 1)?,
Excluded(&start) => get_end(start)?,
};
let end = match range.end_bound() {
Excluded(0) => 0,
Excluded(&end) => get_end(end - 1)?,
Included(&end) => get_end(end)?,
Unbounded => self.len(),
};
if start > end {
return None;
}
self.data.as_ref().get(start..end)
}
pub const fn rows_iter(&self) -> JaggedArrayRows<V, I, E, VS> {
JaggedArrayRows { array: self, row: 0 }
}
}
impl<V, I: Index, E: AsRef<[I]>> JaggedArray<V, I, E> {
#[must_use]
pub fn into_vecs(self) -> Vec<Vec<V>> {
let Self { ends, data, .. } = self;
let ends = ends.as_ref();
let mut data = data.into_vec();
let mut iliffe = Vec::with_capacity(ends.len());
let mut last_end = 0;
for end in ends {
let size = end.get() - last_end;
iliffe.push(data.drain(..size).collect());
last_end = end.get();
}
iliffe.push(data);
iliffe
}
}
impl<V: fmt::Debug, I: Index, E: AsRef<[I]>> fmt::Debug for JaggedArray<V, I, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut full_array = f.debug_list();
for row in self.rows_iter() {
full_array.entry(&row);
}
full_array.finish()
}
}
pub struct JaggedArrayRows<
'j,
V,
I: Index = u32,
E: AsRef<[I]> = Box<[I]>,
VS: AsRef<[V]> = Box<[V]>,
> {
array: &'j JaggedArray<V, I, E, VS>,
row: usize,
}
impl<'j, V, I: Index, E: AsRef<[I]>, VS: AsRef<[V]>> Clone for JaggedArrayRows<'j, V, I, E, VS> {
fn clone(&self) -> Self {
Self { array: self.array, row: self.row }
}
}
impl<'j, V, I: Index, E: AsRef<[I]>> Iterator for JaggedArrayRows<'j, V, I, E> {
type Item = &'j [V];
fn next(&mut self) -> Option<Self::Item> {
self.row += 1;
self.array.get_row(self.row - 1)
}
}
pub struct Builder<V, I = u32> {
last_end: Option<I>,
ends: Vec<I>,
data: Vec<V>,
}
impl<V, I: Index> Default for Builder<V, I> {
fn default() -> Self {
Builder { last_end: None, ends: Vec::new(), data: Vec::new() }
}
}
impl<V, I: Index> Builder<V, I> {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn new_with_capacity(row_count: usize, data_len: usize) -> Self {
Builder {
last_end: None,
ends: Vec::with_capacity(row_count),
data: Vec::with_capacity(data_len),
}
}
pub fn add_elem(&mut self, elem: V) -> &mut Self {
self.data.push(elem);
self
}
pub fn add_row(&mut self, row: impl IntoIterator<Item = V>) -> &mut Self {
self.data.extend(row);
if let Some(last_end) = self.last_end.replace(I::new(self.data.len())) {
self.ends.push(last_end);
}
self
}
#[must_use]
pub fn build(&mut self) -> JaggedArray<V, I> {
let ends = std::mem::take(&mut self.ends);
let data = std::mem::take(&mut self.data);
JaggedArray {
ends: ends.into(),
data: data.into(),
_i: PhantomData,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_row() {
let array = Builder::<i64>::new()
.add_row([1, 2, 3])
.add_row([4, 5, 6])
.add_row([])
.add_row([7, 8, 9])
.add_row([])
.build();
assert_eq!(array.get_row(0), Some(&[1, 2, 3][..]));
assert_eq!(array.get_row(1), Some(&[4, 5, 6][..]));
assert_eq!(array.get_row(2), Some(&[][..]));
assert_eq!(array.get_row(3), Some(&[7, 8, 9][..]));
assert_eq!(array.get_row(4), Some(&[][..]));
assert_eq!(array.get_row(5), None);
}
#[test]
fn test_iter_rows() {
let array = Builder::<i64>::new()
.add_row([])
.add_row([1, 2, 3])
.add_row([4, 5, 6])
.add_row([])
.add_row([7, 8, 9])
.add_row([])
.build();
let mut iter = array.rows_iter();
assert_eq!(iter.next(), Some(&[][..]));
assert_eq!(iter.next(), Some(&[1, 2, 3][..]));
assert_eq!(iter.next(), Some(&[4, 5, 6][..]));
assert_eq!(iter.next(), Some(&[][..]));
assert_eq!(iter.next(), Some(&[7, 8, 9][..]));
assert_eq!(iter.next(), Some(&[][..]));
assert_eq!(iter.next(), None);
}
#[test]
fn test_get_rows() {
let array = Builder::<i64>::new()
.add_row([])
.add_row([1, 2, 3])
.add_row([4, 5, 6])
.add_row([])
.add_row([7, 8, 9])
.add_row([])
.build();
println!("{array:?}");
assert_eq!(array.get_rows(0..1), Some(&[][..]));
assert_eq!(array.get_rows(0..2), Some(&[1, 2, 3][..]));
assert_eq!(array.get_rows(2..2), Some(&[][..]));
assert_eq!(array.get_rows(2..3), Some(&[4, 5, 6][..]));
assert_eq!(array.get_rows(2..4), Some(&[4, 5, 6][..]));
assert_eq!(array.get_rows(2..5), Some(&[4, 5, 6, 7, 8, 9][..]));
assert_eq!(array.get_rows(..), Some(&[1, 2, 3, 4, 5, 6, 7, 8, 9][..]));
}
}