devela/data/list/array/vec/chunk/mod.rs
1// devela::data::list::array::vec::chunk
2//
3//
4// IMPROVE: bring benchmarks
5
6use crate::{vec_ as vec, Rc, RefCell, Vec};
7
8#[cfg(test)]
9mod tests;
10
11#[doc = crate::TAG_DATA_STRUCTURE!()]
12/// A persistent data structure with efficient append and concatenation operations.
13///
14/// # Overview
15/// `VecChunk<A>` is an immutable data structure that allows O(1) complexity for append and
16/// concatenation operations through structural sharing. It uses [`Rc`] (Reference Counting)
17/// for efficient memory management.
18///
19/// # Performance
20/// - Append operation: O(1)
21/// - Concatenation operation: O(1)
22/// - Converting to Vec: O(n)
23///
24/// # Implementation Details
25/// The data structure is implemented as an enum with three variants:
26/// - `Empty`: Represents an empty chunk
27/// - `Append`: Represents a single element appended to another chunk
28/// - `Concat`: Represents the concatenation of two chunks
29///
30/// # Example
31/// ```
32/// # use devela::VecChunk;
33/// let mut chunk = VecChunk::default();
34/// chunk = chunk.append(1);
35/// chunk = chunk.append(2);
36///
37/// let other_chunk = VecChunk::default().append(3).append(4);
38/// let combined = chunk.concat(other_chunk);
39///
40/// assert_eq!(combined.as_vec(), vec![1, 2, 3, 4]);
41/// ```
42///
43/// # References
44/// - [Persistent Data Structures](https://en.wikipedia.org/wiki/Persistent_data_structure)
45/// - [Structural Sharing](https://hypirion.com/musings/understanding-persistent-vector-pt-1)
46///
47#[doc = crate::doc_!(vendor: "tailcall-chunk")]
48#[must_use]
49#[derive(Clone)]
50pub enum VecChunk<A> {
51 /// Represents an empty chunk with no elements
52 Empty,
53 /// Represents a chunk containing exactly one element
54 Single(A),
55 /// Represents the concatenation of two chunks, enabling O(1) concatenation
56 Concat(Rc<VecChunk<A>>, Rc<VecChunk<A>>),
57 /// Represents a collection of elements
58 Collect(Rc<RefCell<Vec<A>>>),
59 /// Represents a lazy transformation that flattens elements
60 TransformFlatten(Rc<VecChunk<A>>, Rc<dyn Fn(A) -> VecChunk<A>>),
61}
62
63impl<A> Default for VecChunk<A> {
64 /// Creates a new empty chunk.
65 ///
66 /// This is equivalent to using [`VecChunk::Empty`].
67 fn default() -> Self {
68 VecChunk::Empty
69 }
70}
71
72impl<A> VecChunk<A> {
73 /// Creates a new chunk containing a single element.
74 ///
75 /// # Arguments
76 /// * `a` - The element to store in the chunk
77 ///
78 /// # Example
79 /// ```
80 /// # use devela::VecChunk;
81 /// let chunk: VecChunk<i32> = VecChunk::new(100);
82 /// assert!(!chunk.is_null());
83 /// ```
84 pub fn new(a: A) -> Self {
85 VecChunk::Single(a)
86 }
87
88 /// Returns `true` if the chunk is empty.
89 ///
90 /// # Example
91 /// ```
92 /// # use devela::VecChunk;
93 /// let chunk: VecChunk<i32> = VecChunk::default();
94 /// assert!(chunk.is_null());
95 ///
96 /// let non_empty = chunk.append(42);
97 /// assert!(!non_empty.is_null());
98 /// ```
99 pub fn is_null(&self) -> bool {
100 match self {
101 VecChunk::Empty => true,
102 VecChunk::Collect(vec) => vec.borrow().is_empty(),
103 _ => false,
104 }
105 }
106
107 /// Append a new element to the chunk.
108 ///
109 /// This operation has O(1) complexity as it creates a new `Append` variant
110 /// that references the existing chunk through an [`Rc`].
111 ///
112 /// # Example
113 /// ```
114 /// # use devela::VecChunk;
115 /// let chunk = VecChunk::default().append(1).append(2);
116 /// assert_eq!(chunk.as_vec(), vec![1, 2]);
117 /// ```
118 pub fn append(self, a: A) -> Self {
119 self.concat(VecChunk::new(a))
120 }
121
122 /// Prepend a new element to the beginning of the chunk.
123 ///
124 /// This operation has O(1) complexity as it creates a new `Concat` variant
125 /// that references the existing chunk through an [`Rc`].
126 ///
127 /// # Example
128 /// ```
129 /// # use devela::VecChunk;
130 /// let chunk = VecChunk::default().prepend(1).prepend(2);
131 /// assert_eq!(chunk.as_vec(), vec![2, 1]);
132 /// ```
133 pub fn prepend(self, a: A) -> Self {
134 if self.is_null() {
135 VecChunk::new(a)
136 } else {
137 VecChunk::new(a).concat(self)
138 }
139 }
140
141 /// Concatenates this chunk with another chunk.
142 ///
143 /// This operation has O(1) complexity as it creates a new `Concat` variant
144 /// that references both chunks through [`Rc`]s.
145 ///
146 /// # Performance Optimization
147 /// If either chunk is empty, returns the other chunk instead of creating
148 /// a new `Concat` variant.
149 ///
150 /// # Example
151 /// ```
152 /// # use devela::VecChunk;
153 /// let chunk1 = VecChunk::default().append(1).append(2);
154 /// let chunk2 = VecChunk::default().append(3).append(4);
155 /// let combined = chunk1.concat(chunk2);
156 /// assert_eq!(combined.as_vec(), vec![1, 2, 3, 4]);
157 /// ```
158 pub fn concat(self, other: VecChunk<A>) -> VecChunk<A> {
159 match (self, other) {
160 // Handle null cases
161 (VecChunk::Empty, other) => other,
162 (this, VecChunk::Empty) => this,
163 (VecChunk::Single(a), VecChunk::Single(b)) => {
164 VecChunk::Collect(Rc::new(RefCell::new(vec![a, b])))
165 }
166 (VecChunk::Collect(vec), VecChunk::Single(a)) => {
167 if Rc::strong_count(&vec) == 1 {
168 // Only clone if there are no other references
169 vec.borrow_mut().push(a);
170 VecChunk::Collect(vec)
171 } else {
172 VecChunk::Concat(Rc::new(VecChunk::Collect(vec)), Rc::new(VecChunk::Single(a)))
173 }
174 }
175 // Handle all other cases with Concat
176 (this, that) => VecChunk::Concat(Rc::new(this), Rc::new(that)),
177 }
178 }
179
180 /// Transforms each element in the chunk using the provided function.
181 ///
182 /// This method creates a lazy representation of the transformation without actually
183 /// performing it. The transformation is only executed when [`as_vec`](VecChunk::as_vec)
184 /// or [`as_vec_mut`](VecChunk::as_vec_mut) is called.
185 ///
186 /// # Performance
187 /// - Creating the transformation: O(1)
188 /// - Executing the transformation (during [`as_vec`](VecChunk::as_vec)): O(n)
189 ///
190 /// # Example
191 /// ```
192 /// # use devela::VecChunk;
193 /// let chunk = VecChunk::default().append(1).append(2).append(3);
194 /// // This operation is O(1) and doesn't actually transform the elements
195 /// let doubled = chunk.transform(|x| x * 2);
196 /// // The transformation happens here, when we call as_vec()
197 /// assert_eq!(doubled.as_vec(), vec![2, 4, 6]);
198 /// ```
199 pub fn transform(self, f: impl Fn(A) -> A + 'static) -> Self {
200 self.transform_flatten(move |a| VecChunk::new(f(a)))
201 }
202
203 /// Materializes a chunk by converting it into a collected form.
204 ///
205 /// This method evaluates any lazy transformations and creates a new chunk containing
206 /// all elements in a `Collect` variant. This can be useful for performance when you
207 /// plan to reuse the chunk multiple times, as it prevents re-evaluation of transformations.
208 ///
209 /// # Performance
210 /// - Time complexity: O(n) where n is the number of elements
211 /// - Space complexity: O(n) as it creates a new vector containing all elements
212 ///
213 /// # Example
214 /// ```
215 /// # use devela::VecChunk;
216 /// let chunk = VecChunk::default()
217 /// .append(1)
218 /// .append(2)
219 /// .transform(|x| x * 2); // Lazy transformation
220 ///
221 /// // Materialize the chunk to evaluate the transformation once
222 /// let materialized = chunk.materialize();
223 ///
224 /// assert_eq!(materialized.as_vec(), vec![2, 4]);
225 /// ```
226 pub fn materialize(self) -> VecChunk<A>
227 where
228 A: Clone,
229 {
230 VecChunk::Collect(Rc::new(RefCell::new(self.as_vec())))
231 }
232
233 /// Transforms each element in the chunk into a new chunk and flattens the result.
234 ///
235 /// This method creates a lazy representation of the transformation without actually
236 /// performing it. The transformation is only executed when [`as_vec`](VecChunk::as_vec)
237 /// or [`as_vec_mut`](VecChunk::as_vec_mut) is called.
238 ///
239 /// # Performance
240 /// - Creating the transformation: O(1)
241 /// - Executing the transformation (during [`as_vec`](VecChunk::as_vec)): O(n)
242 ///
243 /// # Example
244 /// ```
245 /// # use devela::VecChunk;
246 /// let chunk = VecChunk::default().append(1).append(2);
247 /// // Transform each number x into a chunk containing [x, x+1]
248 /// let expanded = chunk.transform_flatten(|x| {
249 /// VecChunk::default().append(x).append(x + 1)
250 /// });
251 /// assert_eq!(expanded.as_vec(), vec![1, 2, 2, 3]);
252 /// ```
253 pub fn transform_flatten(self, f: impl Fn(A) -> VecChunk<A> + 'static) -> Self {
254 VecChunk::TransformFlatten(Rc::new(self), Rc::new(f))
255 }
256
257 /// Converts the chunk into a vector of references to its elements.
258 ///
259 /// This operation has O(n) complexity where n is the number of elements
260 /// in the chunk.
261 ///
262 /// # Example
263 /// ```
264 /// # use devela::VecChunk;
265 /// let chunk = VecChunk::default().append(1).append(2).append(3);
266 /// assert_eq!(chunk.as_vec(), vec![1, 2, 3]);
267 /// ```
268 pub fn as_vec(&self) -> Vec<A>
269 where
270 A: Clone,
271 {
272 let mut vec = Vec::new();
273 self.as_vec_mut(&mut vec);
274 vec
275 }
276
277 /// Helper method that populates a vector with references to the chunk's elements.
278 ///
279 /// This method is used internally by [`as_vec`](VecChunk::as_vec) to avoid
280 /// allocating multiple vectors during the traversal.
281 pub fn as_vec_mut(&self, buf: &mut Vec<A>)
282 where
283 A: Clone,
284 {
285 match self {
286 VecChunk::Empty => {}
287 VecChunk::Single(a) => {
288 buf.push(a.clone());
289 }
290 VecChunk::Concat(a, b) => {
291 a.as_vec_mut(buf);
292 b.as_vec_mut(buf);
293 }
294 VecChunk::TransformFlatten(a, f) => {
295 let mut tmp = Vec::new();
296 a.as_vec_mut(&mut tmp);
297 for elem in tmp.into_iter() {
298 f(elem).as_vec_mut(buf);
299 }
300 }
301 VecChunk::Collect(vec) => {
302 buf.extend(vec.borrow().iter().cloned());
303 }
304 }
305 }
306}
307
308impl<A> FromIterator<A> for VecChunk<A> {
309 /// Creates a chunk from an iterator.
310 ///
311 /// # Example
312 /// ```
313 /// # use devela::VecChunk;
314 /// let vec = vec![1, 2, 3];
315 /// let chunk: VecChunk<_> = vec.into_iter().collect();
316 /// assert_eq!(chunk.as_vec(), vec![1, 2, 3]);
317 /// ```
318 fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
319 let vec: Vec<_> = iter.into_iter().collect();
320 VecChunk::Collect(Rc::new(RefCell::new(vec)))
321 }
322}