use std::{cell::RefCell, rc::Rc};
#[derive(Clone)]
pub enum Chunk<A> {
Empty,
Single(A),
Concat(Rc<Chunk<A>>, Rc<Chunk<A>>),
Collect(Rc<RefCell<Vec<A>>>),
TransformFlatten(Rc<Chunk<A>>, Rc<dyn Fn(A) -> Chunk<A>>),
}
impl<A> Default for Chunk<A> {
fn default() -> Self {
Chunk::Empty
}
}
impl<A> Chunk<A> {
pub fn new(a: A) -> Self {
Chunk::Single(a)
}
pub fn is_null(&self) -> bool {
match self {
Chunk::Empty => true,
Chunk::Collect(vec) => vec.borrow().is_empty(),
_ => false,
}
}
pub fn append(self, a: A) -> Self {
self.concat(Chunk::new(a))
}
pub fn prepend(self, a: A) -> Self {
if self.is_null() {
Chunk::new(a)
} else {
Chunk::new(a).concat(self)
}
}
pub fn concat(self, other: Chunk<A>) -> Chunk<A> {
match (self, other) {
(Chunk::Empty, other) => other,
(this, Chunk::Empty) => this,
(Chunk::Single(a), Chunk::Single(b)) => {
Chunk::Collect(Rc::new(RefCell::new(vec![a, b])))
}
(Chunk::Collect(vec), Chunk::Single(a)) => {
if Rc::strong_count(&vec) == 1 {
vec.borrow_mut().push(a);
Chunk::Collect(vec)
} else {
Chunk::Concat(Rc::new(Chunk::Collect(vec)), Rc::new(Chunk::Single(a)))
}
}
(this, that) => Chunk::Concat(Rc::new(this), Rc::new(that)),
}
}
pub fn transform(self, f: impl Fn(A) -> A + 'static) -> Self {
self.transform_flatten(move |a| Chunk::new(f(a)))
}
pub fn materialize(self) -> Chunk<A>
where
A: Clone,
{
Chunk::Collect(Rc::new(RefCell::new(self.as_vec())))
}
pub fn transform_flatten(self, f: impl Fn(A) -> Chunk<A> + 'static) -> Self {
Chunk::TransformFlatten(Rc::new(self), Rc::new(f))
}
pub fn as_vec(&self) -> Vec<A>
where
A: Clone,
{
let mut vec = Vec::new();
self.as_vec_mut(&mut vec);
vec
}
pub fn as_vec_mut(&self, buf: &mut Vec<A>)
where
A: Clone,
{
match self {
Chunk::Empty => {}
Chunk::Single(a) => {
buf.push(a.clone());
}
Chunk::Concat(a, b) => {
a.as_vec_mut(buf);
b.as_vec_mut(buf);
}
Chunk::TransformFlatten(a, f) => {
let mut tmp = Vec::new();
a.as_vec_mut(&mut tmp);
for elem in tmp.into_iter() {
f(elem).as_vec_mut(buf);
}
}
Chunk::Collect(vec) => {
buf.extend(vec.borrow().iter().cloned());
}
}
}
}
impl<A> FromIterator<A> for Chunk<A> {
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
let vec: Vec<_> = iter.into_iter().collect();
Chunk::Collect(Rc::new(RefCell::new(vec)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let chunk: Chunk<i32> = Chunk::default();
assert!(chunk.is_null());
}
#[test]
fn test_default() {
let chunk: Chunk<i32> = Chunk::default();
assert!(chunk.is_null());
}
#[test]
fn test_is_null() {
let empty: Chunk<i32> = Chunk::default();
assert!(empty.is_null());
let non_empty = empty.append(1);
assert!(!non_empty.is_null());
}
#[test]
fn test_append() {
let chunk = Chunk::default().append(1).append(2).append(3);
assert_eq!(chunk.as_vec(), vec![1, 2, 3]);
let chunk1 = Chunk::default().append(1);
let chunk2 = chunk1.clone().append(2);
assert_eq!(chunk1.as_vec(), vec![1]);
assert_eq!(chunk2.as_vec(), vec![1, 2]);
}
#[test]
fn test_concat() {
let chunk1 = Chunk::default().append(1).append(2);
let chunk2 = Chunk::default().append(3).append(4);
let combined = chunk1.clone().concat(chunk2.clone());
assert_eq!(combined.as_vec(), vec![1, 2, 3, 4]);
let empty = Chunk::default();
assert_eq!(
empty.clone().concat(chunk1.clone()).as_vec(),
chunk1.as_vec()
);
assert_eq!(
chunk1.clone().concat(empty.clone()).as_vec(),
chunk1.as_vec()
);
assert_eq!(empty.clone().concat(empty).as_vec(), Vec::<i32>::new());
}
#[test]
fn test_as_vec() {
let empty: Chunk<i32> = Chunk::default();
assert_eq!(empty.as_vec(), Vec::<i32>::new());
let single = Chunk::default().append(42);
assert_eq!(single.as_vec(), vec![42]);
let multiple = Chunk::default().append(1).append(2).append(3);
assert_eq!(multiple.as_vec(), vec![1, 2, 3]);
let chunk1 = Chunk::default().append(1).append(2);
let chunk2 = Chunk::default().append(3).append(4);
let complex = chunk1.concat(chunk2);
assert_eq!(complex.as_vec(), vec![1, 2, 3, 4]);
}
#[test]
fn test_structural_sharing() {
let chunk1 = Chunk::default().append(1).append(2);
let chunk2 = chunk1.clone().append(3);
let chunk3 = chunk1.clone().append(4);
assert_eq!(chunk1.as_vec(), vec![1, 2]);
assert_eq!(chunk2.as_vec(), vec![1, 2, 3]);
assert_eq!(chunk3.as_vec(), vec![1, 2, 4]);
}
#[test]
fn test_with_different_types() {
let string_chunk = Chunk::default()
.append(String::from("hello"))
.append(String::from("world"));
assert_eq!(string_chunk.as_vec().len(), 2);
let float_chunk = Chunk::default()
.append(std::f64::consts::PI)
.append(std::f64::consts::E);
assert_eq!(
float_chunk.as_vec(),
vec![std::f64::consts::PI, std::f64::consts::E]
);
let bool_chunk = Chunk::default().append(true).append(false).append(true);
assert_eq!(bool_chunk.as_vec(), vec![true, false, true]);
}
#[test]
fn test_transform() {
let empty: Chunk<i32> = Chunk::default();
let transformed_empty = empty.transform(|x| x * 2);
assert_eq!(transformed_empty.as_vec(), Vec::<i32>::new());
let single = Chunk::default().append(5);
let doubled = single.transform(|x| x * 2);
assert_eq!(doubled.as_vec(), vec![10]);
let multiple = Chunk::default().append(1).append(2).append(3);
let doubled = multiple.transform(|x| x * 2);
assert_eq!(doubled.as_vec(), vec![2, 4, 6]);
let string_chunk = Chunk::default()
.append(String::from("hello"))
.append(String::from("world"));
let uppercase = string_chunk.transform(|s| s.to_uppercase());
assert_eq!(uppercase.as_vec(), vec!["HELLO", "WORLD"]);
let numbers = Chunk::default().append(1).append(2).append(3);
let result = numbers
.transform(|x| x * 2)
.transform(|x| x + 1)
.transform(|x| x * 3);
assert_eq!(result.as_vec(), vec![9, 15, 21]);
}
#[test]
fn test_transform_flatten() {
let empty: Chunk<i32> = Chunk::default();
let transformed_empty = empty.transform_flatten(|x| Chunk::new(x * 2));
assert_eq!(transformed_empty.as_vec(), Vec::<i32>::new());
let single = Chunk::default().append(5);
let doubled = single.transform_flatten(|x| Chunk::new(x * 2));
assert_eq!(doubled.as_vec(), vec![10]);
let numbers = Chunk::default().append(1).append(2);
let expanded = numbers.transform_flatten(|x| Chunk::default().append(x + 1).append(x));
assert_eq!(expanded.as_vec(), vec![2, 1, 3, 2]);
let chunk = Chunk::default().append(1).append(2).append(3);
let nested = chunk.transform_flatten(|x| {
if x % 2 == 0 {
Chunk::default().append(x).append(x + 1)
} else {
Chunk::new(x)
}
});
assert_eq!(nested.as_vec(), vec![1, 2, 3, 3]);
let numbers = Chunk::default().append(1).append(2);
let result = numbers
.transform_flatten(|x| Chunk::default().append(x).append(x))
.transform_flatten(|x| Chunk::default().append(x).append(x + 1));
assert_eq!(result.as_vec(), vec![1, 2, 1, 2, 2, 3, 2, 3]);
let chunk = Chunk::default().append(1).append(2);
let filtered = chunk.transform_flatten(|x| {
if x % 2 == 0 {
Chunk::new(x)
} else {
Chunk::default() }
});
assert_eq!(filtered.as_vec(), vec![2]);
}
#[test]
fn test_prepend() {
let chunk = Chunk::default().prepend(1).prepend(2).prepend(3);
assert_eq!(chunk.as_vec(), vec![3, 2, 1]);
let chunk1 = Chunk::default().prepend(1);
let chunk2 = chunk1.clone().prepend(2);
assert_eq!(chunk1.as_vec(), vec![1]);
assert_eq!(chunk2.as_vec(), vec![2, 1]);
let mixed = Chunk::default()
.prepend(1) .append(2) .prepend(3); assert_eq!(mixed.as_vec(), vec![3, 1, 2]);
}
#[test]
fn test_from_iterator() {
let empty_vec: Vec<i32> = vec![];
let empty_chunk: Chunk<i32> = empty_vec.into_iter().collect();
assert!(empty_chunk.is_null());
let vec = vec![1, 2, 3];
let chunk: Chunk<_> = vec.into_iter().collect();
assert_eq!(chunk.as_vec(), vec![1, 2, 3]);
let range_chunk: Chunk<_> = (1..=5).collect();
assert_eq!(range_chunk.as_vec(), vec![1, 2, 3, 4, 5]);
let doubled: Chunk<_> = vec![1, 2, 3].into_iter().map(|x| x * 2).collect();
assert_eq!(doubled.as_vec(), vec![2, 4, 6]);
}
#[test]
fn test_concat_optimization() {
let collected: Chunk<i32> = vec![1, 2, 3].into_iter().collect();
let result = collected.concat(Chunk::Single(4));
assert_eq!(result.as_vec(), vec![1, 2, 3, 4]);
match result {
Chunk::Collect(_) => (), _ => panic!("Expected Collect variant after optimization"),
}
}
}