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