use crate::non_empty_vec::NonEmptyVec;
use std::mem;
pub type DepthList<L, T> = Vec<DepthItem<L, T>>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DepthItem<L, T> {
Item(T),
List(L, DepthList<L, T>),
}
#[derive(Debug)]
struct DepthStack<L, T> {
finished: Vec<(L, DepthList<L, T>)>,
stack: NonEmptyVec<(L, Vec<DepthItem<L, T>>)>,
}
impl<L, T> DepthStack<L, T>
where
L: Copy,
{
#[inline]
pub fn new(top_ltype: L) -> Self {
DepthStack {
finished: Vec::new(),
stack: NonEmptyVec::new((top_ltype, Vec::new())),
}
}
pub fn increase_depth(&mut self, ltype: L) {
self.stack.push((ltype, Vec::new()));
}
pub fn decrease_depth(&mut self) {
let (ltype, list) = self.stack.pop().expect("No depth to pop off!");
self.push(DepthItem::List(ltype, list));
}
pub fn new_list(&mut self, ltype: L) {
if self.stack.is_single() {
self.finish_depth_list(Some(ltype));
} else {
self.decrease_depth();
self.increase_depth(ltype);
}
}
fn push(&mut self, item: DepthItem<L, T>) {
self.stack.last_mut().1.push(item);
}
#[inline]
pub fn push_item(&mut self, item: T) {
self.push(DepthItem::Item(item));
}
#[inline]
pub fn last_type(&self) -> L {
self.stack.last().0
}
fn finish_depth_list(&mut self, new_ltype: Option<L>) {
for _ in 1..self.stack.len() {
self.decrease_depth();
}
debug_assert!(
self.stack.is_single(),
"Open layers remain after collapsing",
);
let (ltype, list) = {
let new_ltype = new_ltype.unwrap_or(self.stack.first().0);
mem::replace(self.stack.first_mut(), (new_ltype, Vec::new()))
};
if !list.is_empty() {
self.finished.push((ltype, list));
}
}
pub fn into_trees(mut self) -> Vec<(L, DepthList<L, T>)> {
self.finish_depth_list(None);
self.finished
}
}
pub fn process_depths<I, L, T>(top_ltype: L, items: I) -> Vec<(L, DepthList<L, T>)>
where
I: IntoIterator<Item = (usize, L, T)>,
L: Copy + PartialEq,
{
let mut stack = DepthStack::new(top_ltype);
let mut previous = 0;
for (depth, ltype, item) in items {
for _ in previous..depth {
stack.increase_depth(ltype);
}
for _ in depth..previous {
stack.decrease_depth();
}
if stack.last_type() != ltype {
stack.new_list(ltype);
}
stack.push_item(item);
previous = depth;
}
stack.into_trees()
}
#[test]
fn depth() {
macro_rules! test {
($depths:expr, $list:expr $(,)?) => {{
let depths: Vec<_> = $depths
.into_iter()
.map(|(depth, item)| (depth, (), item))
.collect();
let list: Vec<DepthItem<(), char>> = $list;
let expected = ((), list);
let actual = process_depths((), depths);
assert_eq!(
actual.len(),
1,
"Actual produced finished list doesn't have exactly one element",
);
assert_eq!(
&actual[0], &expected,
"Actual produced depth list doesn't match expected",
);
}};
}
macro_rules! item {
($item:expr) => {
DepthItem::Item($item)
};
}
macro_rules! list {
() => {
DepthItem::List((), vec![])
};
($($x:expr),+ $(,)?) => {
DepthItem::List((), vec![$($x),+])
};
}
test!(
vec![(0, 'a')], vec![item!('a')],
);
test!(
vec![(0, 'a'), (0, 'b')], vec![item!('a'), item!('b')],
);
test!(
vec![(0, 'a'), (0, 'b'), (1, 'c')],
vec![item!('a'), item!('b'), list![item!('c')]]
);
test!(
vec![(0, 'a'), (0, 'b'), (2, 'c')],
vec![item!('a'), item!('b'), list![list![item!('c')]]],
);
test!(
vec![(1, 'a'), (1, 'b')],
vec![list![item!('a'), item!('b')]],
);
test!(
vec![(2, 'a'), (2, 'b')],
vec![list![list![item!('a'), item!('b')]]],
);
test!(
vec![(2, 'a'), (1, 'b')],
vec![list![list![item!('a')], item!('b')]],
);
test!(
vec![(5, 'a')],
vec![list![list![list![list![list![item!('a')]]]]]],
);
test!(
vec![(2, 'a'), (3, 'b'), (1, 'c'), (1, 'd'), (2, 'e'), (0, 'f')],
vec![
list![
list![item!('a'), list![item!('b')]],
item!('c'),
item!('d'),
list![item!('e')],
],
item!('f'),
],
);
}
#[test]
fn depth_types() {
macro_rules! test {
($depths:expr, $list:expr $(,)?) => {{
let expected: Vec<(char, Vec<DepthItem<char, char>>)> = $list;
let actual = process_depths(' ', $depths);
assert_eq!(
actual, expected,
"Actual produced depth list doesn't match expected",
);
}};
}
macro_rules! item {
($item:expr) => {
DepthItem::Item($item)
};
}
macro_rules! list {
() => {
DepthItem::List((), vec![])
};
($ltype:expr, $($x:expr),+ $(,)?) => {
DepthItem::List($ltype, vec![$($x),+])
};
}
test!(vec![], vec![]);
test!(
vec![(0, '*', 'a')], vec![('*', vec![item!('a')])],
);
test!(
vec![(0, '*', 'a'), (0, '*', 'b')], vec![('*', vec![item!('a'), item!('b')])],
);
test!(
vec![(0, '*', 'a'), (0, '#', 'b')], vec![('*', vec![item!('a')]), ('#', vec![item!('b')])],
);
test!(
vec![(1, '*', 'a'), (1, '#', 'b')],
vec![(' ', vec![list!['*', item!('a')], list!['#', item!('b')]])],
);
test!(
vec![(1, '*', 'a'), (1, '#', 'b'), (0, '*', 'c')],
vec![
(' ', vec![list!['*', item!('a')], list!['#', item!('b')]]),
('*', vec![item!('c')]),
],
);
test!(
vec![(2, '*', 'a'), (2, '#', 'b')],
vec![(
' ',
vec![list!['*', list!['*', item!('a')], list!['#', item!('b')]]]
)],
);
test!(
vec![(0, '#', 'a'), (2, '*', 'b'), (2, '*', 'c'), (1, '*', 'd')],
vec![(
'#',
vec![
item!('a'),
list!['*', list!['*', item!('b'), item!('c')], item!('d')],
],
)],
);
test!(
vec![(0, '#', 'a'), (0, '#', 'b'), (0, '*', 'c'), (1, '#', 'd')],
vec![
('#', vec![item!('a'), item!('b')]),
('*', vec![item!('c'), list!['#', item!('d')]]),
],
);
}
#[cfg(test)]
mod test {
use super::*;
use proptest::prelude::*;
macro_rules! test {
($depths:expr) => {{
let depths_input = $depths;
let expected_length = depths_input.len();
let depth_list = process_depths(900, depths_input);
fn count(list: &DepthList<i32, String>) -> usize {
list.iter()
.map(|item| match item {
DepthItem::Item(_) => 1,
DepthItem::List(_, list) => count(&list),
})
.sum()
}
let actual_length: usize =
depth_list.iter().map(|(_, list)| count(list)).sum();
assert_eq!(
actual_length, expected_length,
"Actual depth list output doesn't match input",
);
}};
}
fn arb_depth(max_depth: usize) -> impl Strategy<Value = Vec<(usize, i32, String)>> {
prop::collection::vec((0..max_depth, 901..902, "[a-z]{3}"), 0..100)
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(4096))]
#[test]
#[ignore = "slow test"]
fn deep_depth_prop(depths in arb_depth(128)) {
test!(depths);
}
#[test]
#[ignore = "slow test"]
fn shallow_depth_prop(depths in arb_depth(4)) {
test!(depths);
}
}
}