pattern_core/pattern/comonad_helpers.rs
1//! Helper functions demonstrating practical applications of Comonad operations.
2//!
3//! This module provides position-aware helper functions that compute structural
4//! metadata at every position in a pattern:
5//!
6//! - `depth_at()`: Decorate each position with its depth (maximum nesting level)
7//! - `size_at()`: Decorate each position with its subtree size (total node count)
8//! - `indices_at()`: Decorate each position with its path from root (sequence of indices)
9//!
10//! These helpers demonstrate how `extend` enables natural expression of context-aware
11//! computation, where functions have access to the full subpattern at each position.
12
13use crate::Pattern;
14
15impl<V> Pattern<V> {
16 /// Decorates each position with its depth (maximum nesting level).
17 ///
18 /// This uses `extend` to compute the depth at every position in the pattern.
19 /// Depth is defined as:
20 /// - Atomic pattern (no elements): depth 0
21 /// - Pattern with elements: depth = 1 + max(child depths)
22 ///
23 /// # Returns
24 ///
25 /// A pattern where each position's value is the depth of that subpattern.
26 ///
27 /// # Complexity
28 ///
29 /// Time: O(n), Space: O(n)
30 ///
31 /// # Examples
32 ///
33 /// ```
34 /// use pattern_core::Pattern;
35 ///
36 /// let p = Pattern::point("x");
37 /// assert_eq!(p.depth_at().extract(), &0);
38 ///
39 /// let p = Pattern::pattern("root", vec![
40 /// Pattern::pattern("a", vec![Pattern::point("x")]),
41 /// Pattern::point("b")
42 /// ]);
43 /// let depths = p.depth_at();
44 /// assert_eq!(depths.extract(), &2); // root has depth 2
45 /// assert_eq!(depths.elements()[0].extract(), &1); // "a" has depth 1
46 /// assert_eq!(depths.elements()[1].extract(), &0); // "b" has depth 0
47 /// ```
48 pub fn depth_at(&self) -> Pattern<usize> {
49 self.extend(&|subpattern| subpattern.depth())
50 }
51
52 /// Decorates each position with the total node count of its subtree.
53 ///
54 /// This uses `extend` to compute the size at every position in the pattern.
55 /// Size is defined as: 1 (self) + sum of child sizes.
56 ///
57 /// # Returns
58 ///
59 /// A pattern where each position's value is the size of that subpattern.
60 ///
61 /// # Complexity
62 ///
63 /// Time: O(n), Space: O(n)
64 ///
65 /// # Examples
66 ///
67 /// ```
68 /// use pattern_core::Pattern;
69 ///
70 /// let p = Pattern::point("x");
71 /// assert_eq!(p.size_at().extract(), &1);
72 ///
73 /// let p = Pattern::pattern("root", vec![
74 /// Pattern::point("a"),
75 /// Pattern::point("b")
76 /// ]);
77 /// let sizes = p.size_at();
78 /// assert_eq!(sizes.extract(), &3); // root + 2 children
79 /// assert_eq!(sizes.elements()[0].extract(), &1);
80 /// assert_eq!(sizes.elements()[1].extract(), &1);
81 /// ```
82 pub fn size_at(&self) -> Pattern<usize> {
83 self.extend(&|subpattern| subpattern.size())
84 }
85
86 /// Decorates each position with its path from root (sequence of element indices).
87 ///
88 /// Unlike `depth_at` and `size_at`, this cannot use `extend` because it requires
89 /// tracking the path during traversal. The function passed to `extend` only sees
90 /// the local subpattern, not the path from the root.
91 ///
92 /// Path representation:
93 /// - Root: empty vector `[]`
94 /// - Child at index i: parent_path + `[i]`
95 ///
96 /// # Returns
97 ///
98 /// A pattern where each position's value is its path from root.
99 ///
100 /// # Complexity
101 ///
102 /// Time: O(n), Space: O(n * depth) due to path vectors
103 ///
104 /// # Examples
105 ///
106 /// ```
107 /// use pattern_core::Pattern;
108 ///
109 /// let p = Pattern::point("x");
110 /// let paths = p.indices_at();
111 /// let expected: Vec<usize> = vec![];
112 /// assert_eq!(paths.extract(), &expected);
113 ///
114 /// let p = Pattern::pattern("root", vec![
115 /// Pattern::pattern("a", vec![Pattern::point("x")]),
116 /// Pattern::point("b")
117 /// ]);
118 /// let paths = p.indices_at();
119 /// let expected: Vec<usize> = vec![];
120 /// assert_eq!(paths.extract(), &expected); // root path
121 /// assert_eq!(paths.elements()[0].extract(), &vec![0]); // first child path
122 /// assert_eq!(paths.elements()[0].elements()[0].extract(), &vec![0, 0]); // nested child path
123 /// assert_eq!(paths.elements()[1].extract(), &vec![1]); // second child path
124 /// ```
125 pub fn indices_at(&self) -> Pattern<Vec<usize>> {
126 fn go<V>(path: Vec<usize>, pattern: &Pattern<V>) -> Pattern<Vec<usize>> {
127 Pattern {
128 value: path.clone(),
129 elements: pattern
130 .elements()
131 .iter()
132 .enumerate()
133 .map(|(i, elem)| {
134 let mut new_path = path.clone();
135 new_path.push(i);
136 go(new_path, elem)
137 })
138 .collect(),
139 }
140 }
141 go(vec![], self)
142 }
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn depth_at_atomic_pattern() {
151 let p = Pattern::point("x");
152 let depths = p.depth_at();
153 assert_eq!(depths.extract(), &0);
154 }
155
156 #[test]
157 fn depth_at_with_children() {
158 let p = Pattern::pattern("root", vec![Pattern::point("a"), Pattern::point("b")]);
159 let depths = p.depth_at();
160 assert_eq!(depths.extract(), &1); // has children (even though atomic), so depth 1
161 }
162
163 #[test]
164 fn depth_at_nested() {
165 let p = Pattern::pattern(
166 "root",
167 vec![Pattern::pattern("a", vec![Pattern::point("x")])],
168 );
169 let depths = p.depth_at();
170 assert_eq!(depths.extract(), &2); // depth through "a" to "x"
171 assert_eq!(depths.elements()[0].extract(), &1);
172 }
173
174 #[test]
175 fn size_at_atomic_pattern() {
176 let p = Pattern::point("x");
177 let sizes = p.size_at();
178 assert_eq!(sizes.extract(), &1);
179 }
180
181 #[test]
182 fn size_at_with_children() {
183 let p = Pattern::pattern(
184 "root",
185 vec![
186 Pattern::point("a"),
187 Pattern::point("b"),
188 Pattern::point("c"),
189 ],
190 );
191 let sizes = p.size_at();
192 assert_eq!(sizes.extract(), &4); // 1 root + 3 children
193 assert_eq!(sizes.elements()[0].extract(), &1);
194 }
195
196 #[test]
197 fn indices_at_atomic_pattern() {
198 let p = Pattern::point("x");
199 let paths = p.indices_at();
200 assert_eq!(paths.extract(), &Vec::<usize>::new());
201 }
202
203 #[test]
204 fn indices_at_with_children() {
205 let p = Pattern::pattern("root", vec![Pattern::point("a"), Pattern::point("b")]);
206 let paths = p.indices_at();
207 assert_eq!(paths.extract(), &Vec::<usize>::new());
208 assert_eq!(paths.elements()[0].extract(), &vec![0]);
209 assert_eq!(paths.elements()[1].extract(), &vec![1]);
210 }
211
212 #[test]
213 fn indices_at_nested() {
214 let p = Pattern::pattern(
215 "root",
216 vec![
217 Pattern::pattern("a", vec![Pattern::point("x")]),
218 Pattern::point("b"),
219 ],
220 );
221 let paths = p.indices_at();
222 assert_eq!(paths.extract(), &Vec::<usize>::new());
223 assert_eq!(paths.elements()[0].extract(), &vec![0]);
224 assert_eq!(paths.elements()[0].elements()[0].extract(), &vec![0, 0]);
225 assert_eq!(paths.elements()[1].extract(), &vec![1]);
226 }
227}