tailcall_chunk/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
//! A Rust implementation of a persistent data structure that provides O(1) append and concatenation operations
//! through structural sharing.
//!
//! # Overview
//! `Chunk` is a persistent data structure that offers:
//! - **O(1) Append Operations**: Add elements to your chunk in constant time
//! - **O(1) Concatenation**: Combine two chunks efficiently
//! - **Immutable/Persistent**: All operations create new versions while preserving the original
//! - **Memory Efficient**: Uses structural sharing via reference counting
//! - **Safe Rust**: Implemented using 100% safe Rust
//!
//! # Theoretical Background
//!
//! This implementation is inspired by the concepts presented in Hinze and Paterson's work on
//! [Finger Trees](https://en.wikipedia.org/wiki/Finger_tree), though simplified for our specific use case.
//! While our implementation differs in structure, it shares similar performance goals and theoretical foundations.
//!
//! ## Relationship to Finger Trees
//!
//! Finger Trees are a functional data structure that supports:
//! - Access to both ends in amortized constant time
//! - Concatenation in logarithmic time
//! - Persistence through structural sharing
//!
//! Our `Chunk` implementation achieves similar goals through a simplified approach:
//! - We use `Append` nodes for constant-time additions
//! - The `Concat` variant enables efficient concatenation
//! - `Rc` (Reference Counting) provides persistence and structural sharing
//!
//! # Example Usage
//! ```rust
//! use tailcall_chunk::Chunk;
//!
//! // Create a new chunk and append some elements
//! let chunk1 = Chunk::default()
//!     .append(1)
//!     .append(2);
//!
//! // Create another chunk
//! let chunk2 = Chunk::default()
//!     .append(3)
//!     .append(4);
//!
//! // Concatenate chunks in O(1) time
//! let combined = chunk1.concat(chunk2);
//!
//! // Convert to vector when needed
//! assert_eq!(combined.as_vec(), vec![1, 2, 3, 4]);
//! ```
//!
//! # Performance Characteristics
//!
//! ## Time Complexity Analysis
//!
//! | Operation             | Worst Case | Amortized    | Space        |
//! | --------------------- | ---------- | ------------ | ------------ |
//! | `new()`               | O(1)       | O(1)         | O(1)         |
//! | `append()`            | O(1)       | O(1)         | O(1)         |
//! | `concat()`            | O(1)       | O(1)         | O(1)         |
//! | `transform()`         | O(1)       | O(1)         | O(1)         |
//! | `transform_flatten()` | O(1)       | O(1)         | O(1)         |
//! | `as_vec()`            | O(n)       | O(n)         | O(n)         |
//! | `clone()`             | O(1)       | O(1)         | O(1)         |
//!
//! ## Amortized Analysis Details
//!
//! ### Append Operation
//! The `append` operation is O(1) amortized because:
//! - The actual append is always O(1) as it only creates a new `Append` node
//! - No rebalancing is required
//! - Memory allocation is constant time
//!
//! ### Concat Operation
//! The `concat` operation achieves O(1) amortized time through:
//! - Lazy evaluation: immediate concatenation is O(1)
//! - The actual work is deferred until `as_vec()` is called
//! - No immediate copying or restructuring of data
//!
//! ### Transform Operations
//! Both `transform` and `transform_flatten` are O(1) amortized because:
//! - They create a new node with a transformation function
//! - Actual transformation is deferred until materialization
//! - No immediate computation is performed on elements
//!
//! ### as_vec Operation
//! The `as_vec` operation is O(n) because:
//! - It must process all elements to create the final vector
//! - For a chunk with n elements:
//!   - Basic traversal: O(n)
//!   - Applying deferred transformations: O(n)
//!   - Memory allocation and copying: O(n)
//!
//! ### Memory Usage Patterns
//!
//! The space complexity has interesting properties:
//! - Immediate space usage for operations is O(1)
//! - Deferred space cost accumulates with operations
//! - Final materialization requires O(n) space
//! - Structural sharing reduces memory overhead for clones and versions
//!
//! ```rust
//! use tailcall_chunk::Chunk;
//!
//! // Each operation has O(1) immediate cost
//! let chunk = Chunk::default()
//!     .append(1)    // O(1) time and space
//!     .append(2)    // O(1) time and space
//!     .transform(|x| x + 1);  // O(1) time and space
//!
//! // O(n) cost is paid here
//! let vec = chunk.as_vec();
//! ```
//!
//! # Implementation Details
//!
//! The `Chunk<A>` type is implemented as an enum with four variants:
//! - `Empty`: Represents an empty chunk
//! - `Append`: Represents a single element appended to another chunk
//! - `Concat`: Represents the concatenation of two chunks
//! - `TransformFlatten`: Represents a lazy transformation and flattening of elements
//!
//! The data structure achieves its performance characteristics through:
//! - Structural sharing using `Rc`
//! - Lazy evaluation of concatenation and transformations
//! - Immutable operations that preserve previous versions
//!
//! # Memory Efficiency
//!
//! The `Chunk` type uses structural sharing through reference counting (`Rc`), which means:
//! - Appending or concatenating chunks doesn't copy the existing elements
//! - Memory is automatically freed when no references remain
//! - Multiple versions of the data structure can coexist efficiently
//!
//! ```rust
//! use tailcall_chunk::Chunk;
//!
//! let original = Chunk::default().append(1).append(2);
//! let version1 = original.clone().append(3);  // Efficient cloning
//! let version2 = original.clone().append(4);  // Both versions share data
//! ```
//!
//! # References
//!
//! 1. Ralf Hinze and Ross Paterson. "Finger Trees: A Simple General-purpose Data Structure",
//!    Journal of Functional Programming 16(2):197-217, 2006.
//! 2. Chris Okasaki. "Purely Functional Data Structures", Cambridge University Press, 1998.

mod chunk;
pub use chunk::*;