use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
#[derive(Clone, PartialEq, Eq)]
pub struct Pattern<V> {
pub value: V,
pub elements: Vec<Pattern<V>>,
}
#[derive(Debug, Clone, Default)]
pub struct ValidationRules {
pub max_depth: Option<usize>,
pub max_elements: Option<usize>,
pub required_fields: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidationError {
pub message: String,
pub rule_violated: String,
pub location: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct StructureAnalysis {
pub depth_distribution: Vec<usize>,
pub element_counts: Vec<usize>,
pub nesting_patterns: Vec<String>,
pub summary: String,
}
impl<V: fmt::Debug> Pattern<V> {
fn fmt_debug_with_depth(
&self,
f: &mut fmt::Formatter<'_>,
depth: usize,
max_depth: usize,
) -> fmt::Result {
if depth > max_depth {
return write!(f, "...");
}
f.debug_struct("Pattern")
.field("value", &self.value)
.field(
"elements",
&DebugElements {
elements: &self.elements,
depth,
max_depth,
},
)
.finish()
}
}
impl<V: fmt::Debug> fmt::Debug for Pattern<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_debug_with_depth(f, 0, 10) }
}
struct DebugElements<'a, V> {
elements: &'a Vec<Pattern<V>>,
depth: usize,
max_depth: usize,
}
impl<'a, V: fmt::Debug> fmt::Debug for DebugElements<'a, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.depth > self.max_depth {
return write!(f, "[...]");
}
let mut list = f.debug_list();
for (i, elem) in self.elements.iter().enumerate() {
if i >= 5 && self.elements.len() > 10 {
list.entry(&format_args!("... ({} more)", self.elements.len() - 5));
break;
}
list.entry(&DebugPattern {
pattern: elem,
depth: self.depth + 1,
max_depth: self.max_depth,
});
}
list.finish()
}
}
struct DebugPattern<'a, V> {
pattern: &'a Pattern<V>,
depth: usize,
max_depth: usize,
}
impl<'a, V: fmt::Debug> fmt::Debug for DebugPattern<'a, V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.pattern
.fmt_debug_with_depth(f, self.depth, self.max_depth)
}
}
impl<V: fmt::Display> Pattern<V> {
fn fmt_display_with_depth(
&self,
f: &mut fmt::Formatter<'_>,
depth: usize,
max_depth: usize,
) -> fmt::Result {
if depth > max_depth {
return write!(f, "...");
}
write!(f, "(")?;
write!(f, "{}", self.value)?;
if !self.elements.is_empty() {
write!(f, " ")?;
for (i, elem) in self.elements.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
if i >= 5 && self.elements.len() > 10 {
write!(f, "... ({} more)", self.elements.len() - 5)?;
break;
}
elem.fmt_display_with_depth(f, depth + 1, max_depth)?;
}
}
write!(f, ")")
}
}
impl<V: fmt::Display> fmt::Display for Pattern<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.fmt_display_with_depth(f, 0, 10) }
}
impl<V> Pattern<V> {
pub fn point(value: V) -> Self {
Pattern {
value,
elements: vec![],
}
}
#[allow(clippy::self_named_constructors)]
pub fn pattern(value: V, elements: Vec<Pattern<V>>) -> Self {
Pattern { value, elements }
}
pub fn from_list(value: V, values: Vec<V>) -> Self {
Pattern {
value,
elements: values.into_iter().map(Pattern::point).collect(),
}
}
pub fn value(&self) -> &V {
&self.value
}
pub fn elements(&self) -> &[Pattern<V>] {
&self.elements
}
pub fn length(&self) -> usize {
self.elements.len()
}
pub fn size(&self) -> usize {
1 + self.elements.iter().map(|e| e.size()).sum::<usize>()
}
pub fn depth(&self) -> usize {
if self.elements.is_empty() {
0
} else {
1 + self.elements.iter().map(|e| e.depth()).max().unwrap_or(0)
}
}
pub fn is_atomic(&self) -> bool {
self.elements.is_empty()
}
pub fn any_value<F>(&self, predicate: F) -> bool
where
F: Fn(&V) -> bool,
{
self.any_value_recursive(&predicate)
}
fn any_value_recursive<F>(&self, predicate: &F) -> bool
where
F: Fn(&V) -> bool,
{
if predicate(&self.value) {
return true;
}
for element in &self.elements {
if element.any_value_recursive(predicate) {
return true;
}
}
false
}
pub fn all_values<F>(&self, predicate: F) -> bool
where
F: Fn(&V) -> bool,
{
self.all_values_recursive(&predicate)
}
fn all_values_recursive<F>(&self, predicate: &F) -> bool
where
F: Fn(&V) -> bool,
{
if !predicate(&self.value) {
return false;
}
for element in &self.elements {
if !element.all_values_recursive(predicate) {
return false;
}
}
true
}
pub fn filter<F>(&self, predicate: F) -> Vec<&Pattern<V>>
where
F: Fn(&Pattern<V>) -> bool,
{
let mut result = Vec::new();
self.filter_recursive(&predicate, &mut result);
result
}
fn filter_recursive<'a, F>(&'a self, predicate: &F, result: &mut Vec<&'a Pattern<V>>)
where
F: Fn(&Pattern<V>) -> bool,
{
if predicate(self) {
result.push(self);
}
for element in &self.elements {
element.filter_recursive(predicate, result);
}
}
pub fn find_first<F>(&self, predicate: F) -> Option<&Pattern<V>>
where
F: Fn(&Pattern<V>) -> bool,
{
self.find_first_recursive(&predicate)
}
fn find_first_recursive<'a, F>(&'a self, predicate: &F) -> Option<&'a Pattern<V>>
where
F: Fn(&Pattern<V>) -> bool,
{
if predicate(self) {
return Some(self);
}
for element in &self.elements {
if let Some(found) = element.find_first_recursive(predicate) {
return Some(found);
}
}
None
}
pub fn matches(&self, other: &Pattern<V>) -> bool
where
V: PartialEq,
{
if self.value != other.value {
return false;
}
if self.elements.len() != other.elements.len() {
return false;
}
self.elements
.iter()
.zip(other.elements.iter())
.all(|(e1, e2)| e1.matches(e2))
}
pub fn contains(&self, subpattern: &Pattern<V>) -> bool
where
V: PartialEq,
{
if self.matches(subpattern) {
return true;
}
self.elements
.iter()
.any(|element| element.contains(subpattern))
}
pub fn map<W, F>(self, f: F) -> Pattern<W>
where
F: Fn(&V) -> W,
{
self.map_with(&f)
}
fn map_with<W, F>(self, f: &F) -> Pattern<W>
where
F: Fn(&V) -> W,
{
Pattern {
value: f(&self.value),
elements: self
.elements
.into_iter()
.map(|elem| elem.map_with(f))
.collect(),
}
}
pub fn fold<B, F>(&self, init: B, f: F) -> B
where
F: Fn(B, &V) -> B,
{
self.fold_with(init, &f)
}
fn fold_with<B, F>(&self, acc: B, f: &F) -> B
where
F: Fn(B, &V) -> B,
{
let acc = f(acc, &self.value);
self.elements
.iter()
.fold(acc, |acc, elem| elem.fold_with(acc, f))
}
pub fn values(&self) -> Vec<&V> {
let mut result = Vec::with_capacity(self.size());
self.collect_values(&mut result);
result
}
fn collect_values<'a>(&'a self, result: &mut Vec<&'a V>) {
result.push(&self.value);
for elem in &self.elements {
elem.collect_values(result);
}
}
pub fn para<R, F>(&self, f: F) -> R
where
F: Fn(&Pattern<V>, &[R]) -> R,
{
self.para_with(&f)
}
fn para_with<R, F>(&self, f: &F) -> R
where
F: Fn(&Pattern<V>, &[R]) -> R,
{
let child_results: Vec<R> = self
.elements
.iter()
.map(|child| child.para_with(f))
.collect();
f(self, &child_results)
}
pub fn validate(&self, rules: &ValidationRules) -> Result<(), ValidationError> {
self.validate_recursive(rules, 0, &mut Vec::new())
}
fn validate_recursive(
&self,
rules: &ValidationRules,
current_depth: usize,
location: &mut Vec<String>,
) -> Result<(), ValidationError> {
if let Some(max_depth) = rules.max_depth {
if current_depth > max_depth {
return Err(ValidationError {
message: format!(
"Pattern depth {} exceeds maximum allowed depth {}",
current_depth, max_depth
),
rule_violated: "max_depth".to_string(),
location: location.clone(),
});
}
}
if let Some(max_elements) = rules.max_elements {
if self.elements.len() > max_elements {
return Err(ValidationError {
message: format!(
"Pattern has {} elements, exceeding maximum allowed {}",
self.elements.len(),
max_elements
),
rule_violated: "max_elements".to_string(),
location: location.clone(),
});
}
}
for (index, element) in self.elements.iter().enumerate() {
location.push("elements".to_string());
location.push(index.to_string());
element.validate_recursive(rules, current_depth + 1, location)?;
location.pop(); location.pop(); }
Ok(())
}
pub fn analyze_structure(&self) -> StructureAnalysis {
let mut depth_distribution = Vec::new();
let mut element_counts = Vec::new();
self.analyze_recursive(0, &mut depth_distribution, &mut element_counts);
while let Some(&0) = element_counts.last() {
element_counts.pop();
}
let nesting_patterns = self.identify_nesting_patterns(&depth_distribution, &element_counts);
let summary =
self.generate_summary(&depth_distribution, &element_counts, &nesting_patterns);
StructureAnalysis {
depth_distribution,
element_counts,
nesting_patterns,
summary,
}
}
fn analyze_recursive(
&self,
current_depth: usize,
depth_distribution: &mut Vec<usize>,
element_counts: &mut Vec<usize>,
) {
while depth_distribution.len() <= current_depth {
depth_distribution.push(0);
}
while element_counts.len() <= current_depth {
element_counts.push(0);
}
depth_distribution[current_depth] += 1;
let current_count = self.elements.len();
if current_count > element_counts[current_depth] {
element_counts[current_depth] = current_count;
}
for element in &self.elements {
element.analyze_recursive(current_depth + 1, depth_distribution, element_counts);
}
}
fn identify_nesting_patterns(
&self,
depth_distribution: &[usize],
element_counts: &[usize],
) -> Vec<String> {
let mut patterns = Vec::new();
if depth_distribution.len() <= 1 {
patterns.push("atomic".to_string());
return patterns;
}
let is_linear = element_counts.iter().all(|&count| count <= 1);
if is_linear {
patterns.push("linear".to_string());
}
let has_branching = element_counts.iter().any(|&count| count > 1);
if has_branching {
patterns.push("tree".to_string());
}
if element_counts.len() >= 2 {
let first_count = element_counts[0];
if first_count > 0 {
let similar_counts = element_counts.iter().skip(1).all(|&count| {
let ratio = count as f64 / first_count as f64;
(0.5..=2.0).contains(&ratio)
});
if similar_counts && first_count > 1 {
patterns.push("balanced".to_string());
}
}
}
if patterns.is_empty() {
patterns.push("irregular".to_string());
}
patterns
}
fn generate_summary(
&self,
depth_distribution: &[usize],
_element_counts: &[usize], nesting_patterns: &[String],
) -> String {
let total_nodes: usize = depth_distribution.iter().sum();
let max_depth = depth_distribution.len().saturating_sub(1);
let pattern_desc = if nesting_patterns.is_empty() {
"unknown"
} else {
&nesting_patterns[0]
};
format!(
"Pattern with {} level{}, {} node{}, {}-like structure",
max_depth + 1,
if max_depth == 0 { "" } else { "s" },
total_nodes,
if total_nodes == 1 { "" } else { "s" },
pattern_desc
)
}
pub fn traverse_option<W, F>(&self, f: F) -> Option<Pattern<W>>
where
F: Fn(&V) -> Option<W>,
{
self.traverse_option_with(&f)
}
fn traverse_option_with<W, F>(&self, f: &F) -> Option<Pattern<W>>
where
F: Fn(&V) -> Option<W>,
{
let new_value = f(&self.value)?;
let new_elements: Option<Vec<Pattern<W>>> = self
.elements
.iter()
.map(|elem| elem.traverse_option_with(f))
.collect();
Some(Pattern {
value: new_value,
elements: new_elements?,
})
}
pub fn traverse_result<W, E, F>(&self, f: F) -> Result<Pattern<W>, E>
where
F: Fn(&V) -> Result<W, E>,
{
self.traverse_result_with(&f)
}
fn traverse_result_with<W, E, F>(&self, f: &F) -> Result<Pattern<W>, E>
where
F: Fn(&V) -> Result<W, E>,
{
let new_value = f(&self.value)?;
let new_elements: Result<Vec<Pattern<W>>, E> = self
.elements
.iter()
.map(|elem| elem.traverse_result_with(f))
.collect();
Ok(Pattern {
value: new_value,
elements: new_elements?,
})
}
}
impl<V: Clone> Pattern<Option<V>> {
pub fn sequence_option(self) -> Option<Pattern<V>> {
self.traverse_option(|opt| opt.as_ref().cloned())
}
}
impl<V, E> Pattern<Result<V, E>> {
pub fn sequence_result(self) -> Result<Pattern<V>, E>
where
V: Clone,
E: Clone,
{
self.traverse_result(|res| res.clone())
}
}
impl<V> Pattern<V> {
pub fn validate_all<W, E, F>(&self, f: F) -> Result<Pattern<W>, Vec<E>>
where
F: Fn(&V) -> Result<W, E>,
{
self.validate_all_with(&f)
}
fn validate_all_with<W, E, F>(&self, f: &F) -> Result<Pattern<W>, Vec<E>>
where
F: Fn(&V) -> Result<W, E>,
{
fn collect_results<V, W, E, F>(
pattern: &Pattern<V>,
f: &F,
successes: &mut Vec<W>,
errors: &mut Vec<E>,
) where
F: Fn(&V) -> Result<W, E>,
{
match f(&pattern.value) {
Ok(w) => successes.push(w),
Err(e) => errors.push(e),
}
for elem in &pattern.elements {
collect_results(elem, f, successes, errors);
}
}
let mut successes = Vec::new();
let mut errors = Vec::new();
collect_results(self, f, &mut successes, &mut errors);
if !errors.is_empty() {
return Err(errors);
}
fn rebuild<V, W>(pattern: &Pattern<V>, values: &mut impl Iterator<Item = W>) -> Pattern<W> {
let value = values
.next()
.expect("validate_all: insufficient transformed values");
let elements = pattern
.elements
.iter()
.map(|elem| rebuild(elem, values))
.collect();
Pattern { value, elements }
}
Ok(rebuild(self, &mut successes.into_iter()))
}
}
impl<V: PartialOrd> PartialOrd for Pattern<V> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.value.partial_cmp(&other.value) {
Some(Ordering::Equal) => self.elements.partial_cmp(&other.elements),
other => other,
}
}
}
impl<V: Ord> Ord for Pattern<V> {
fn cmp(&self, other: &Self) -> Ordering {
match self.value.cmp(&other.value) {
Ordering::Equal => self.elements.cmp(&other.elements),
non_equal => non_equal,
}
}
}
impl<V: crate::Combinable> Pattern<V> {
pub fn combine(self, other: Self) -> Self {
let combined_value = self.value.combine(other.value);
let mut combined_elements = self.elements;
combined_elements.extend(other.elements);
Pattern {
value: combined_value,
elements: combined_elements,
}
}
pub fn zip3(left: Vec<Pattern<V>>, right: Vec<Pattern<V>>, values: Vec<V>) -> Vec<Pattern<V>> {
left.into_iter()
.zip(right)
.zip(values)
.map(|((l, r), v)| Pattern::pattern(v, vec![l, r]))
.collect()
}
pub fn zip_with<F>(
left: Vec<Pattern<V>>,
right: Vec<Pattern<V>>,
value_fn: F,
) -> Vec<Pattern<V>>
where
F: Fn(&Pattern<V>, &Pattern<V>) -> V,
{
left.into_iter()
.zip(right)
.map(|(l, r)| {
let value = value_fn(&l, &r);
Pattern::pattern(value, vec![l, r])
})
.collect()
}
}
impl<V> Default for Pattern<V>
where
V: Default,
{
fn default() -> Self {
Pattern::point(V::default())
}
}
impl<V: Hash> Hash for Pattern<V> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value.hash(state);
self.elements.hash(state);
}
}
#[cfg(test)]
mod para_tests {
use super::*;
#[test]
fn para_atomic_pattern_receives_empty_slice() {
let atom = Pattern::point(42);
let result = atom.para(|p, rs| {
assert!(rs.is_empty(), "Atomic pattern should receive empty slice");
assert_eq!(
p.elements().len(),
0,
"Atomic pattern should have no elements"
);
*p.value()
});
assert_eq!(result, 42);
}
#[test]
fn para_preserves_element_order() {
let p = Pattern::pattern(
10,
vec![Pattern::point(5), Pattern::point(3), Pattern::point(7)],
);
let values: Vec<i32> = p.para(|pat, child_results| {
let mut result = vec![*pat.value()];
for child_vec in child_results {
result.extend(child_vec);
}
result
});
assert_eq!(values, vec![10, 5, 3, 7]);
}
#[test]
fn para_preserves_element_order_nested() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3), Pattern::point(4)]),
Pattern::point(5),
],
);
let values: Vec<i32> = p.para(|pat, child_results| {
let mut result = vec![*pat.value()];
for child_vec in child_results {
result.extend(child_vec);
}
result
});
assert_eq!(values, vec![1, 2, 3, 4, 5]);
}
#[test]
fn para_provides_structure_access() {
let p = Pattern::pattern(
10,
vec![
Pattern::pattern(20, vec![Pattern::point(30)]),
Pattern::point(40),
],
);
let depth_at_root = p.para(|pat, _| pat.depth());
assert_eq!(depth_at_root, 2, "Root should have depth 2 (max nesting)");
let element_count_at_root = p.para(|pat, _| pat.elements().len());
assert_eq!(element_count_at_root, 2, "Root should have 2 elements");
assert_eq!(p.depth(), depth_at_root);
assert_eq!(p.elements().len(), element_count_at_root);
}
#[test]
fn para_structure_access_nested() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3), Pattern::point(4)]),
Pattern::point(5),
],
);
type Info = (i32, usize, usize);
let info: Info = p.para(|pat, child_infos: &[Info]| {
let value = *pat.value();
let depth = pat.depth();
let elem_count = pat.elements().len();
if value == 1 {
assert_eq!(child_infos.len(), 2);
assert_eq!(child_infos[0].0, 2); assert_eq!(child_infos[1].0, 5); } else if value == 2 {
assert_eq!(child_infos.len(), 2);
assert_eq!(child_infos[0].0, 3);
assert_eq!(child_infos[1].0, 4);
} else {
assert_eq!(child_infos.len(), 0);
}
(value, depth, elem_count)
});
assert_eq!(info.0, 1); assert_eq!(info.1, 2); assert_eq!(info.2, 2); }
#[test]
fn para_element_count_weighted_computation() {
let p = Pattern::pattern(10, vec![Pattern::point(5), Pattern::point(3)]);
let result: i32 = p.para(|pat, rs| {
let value = *pat.value();
let elem_count = pat.elements().len() as i32;
let element_sum: i32 = rs.iter().sum();
value * elem_count + element_sum
});
assert_eq!(result, 20, "Root: 10*2 + (0+0) = 20");
}
#[test]
fn para_element_count_weighted_nested() {
let p = Pattern::pattern(
10,
vec![
Pattern::pattern(5, vec![Pattern::point(2)]),
Pattern::point(3),
],
);
let result: i32 = p.para(|pat, rs| {
let value = *pat.value();
let elem_count = pat.elements().len() as i32;
let element_sum: i32 = rs.iter().sum();
value * elem_count + element_sum
});
assert_eq!(result, 25);
}
#[test]
fn para_varying_element_counts() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3), Pattern::point(4)]),
Pattern::pattern(5, vec![Pattern::point(6)]),
Pattern::point(7),
],
);
type CountInfo = (i32, usize); let result: CountInfo = p.para(|pat, rs: &[CountInfo]| {
let value = *pat.value();
let elem_count = pat.elements().len();
let subtree_count: usize = rs.iter().map(|(_, count)| count).sum();
(value, elem_count + subtree_count)
});
assert_eq!(result.0, 1, "Root value should be 1");
assert_eq!(
result.1, 6,
"Total element count across all levels should be 6"
);
}
#[test]
fn para_element_count_by_depth() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3), Pattern::point(4)]),
Pattern::point(5),
],
);
type DepthCounts = Vec<usize>; let result: DepthCounts = p.para(|pat, rs: &[DepthCounts]| {
let elem_count = pat.elements().len();
let max_depth = rs.iter().map(|v| v.len()).max().unwrap_or(0);
let mut counts = vec![0; max_depth + 1];
counts[0] = elem_count;
for child_counts in rs {
for (depth, &count) in child_counts.iter().enumerate() {
counts[depth + 1] += count;
}
}
counts
});
assert_eq!(result[0], 2, "Root has 2 elements");
assert_eq!(result[1], 2, "Second level has 2 elements");
}
#[test]
fn para_atomic_nesting_statistics() {
let atom = Pattern::point(42);
type Stats = (i32, usize, usize);
let stats: Stats = atom.para(|pat, rs: &[Stats]| {
let value = *pat.value();
let (child_sum, child_count, child_max_depth) = rs
.iter()
.fold((0_i32, 0_usize, 0_usize), |(s, c, d), (s2, c2, d2)| {
(s + s2, c + c2, d.max(*d2))
});
(
value + child_sum,
1 + child_count,
pat.depth().max(child_max_depth),
)
});
assert_eq!(stats.0, 42, "Sum should be the value itself");
assert_eq!(stats.1, 1, "Count should be 1 (single node)");
assert_eq!(stats.2, 0, "Max depth should be 0 (atomic pattern)");
}
#[test]
fn para_nested_nesting_statistics() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3)]),
Pattern::point(4),
],
);
type Stats = (i32, usize, usize);
let stats: Stats = p.para(|pat, rs: &[Stats]| {
let value = *pat.value();
let (child_sum, child_count, child_max_depth) = rs
.iter()
.fold((0_i32, 0_usize, 0_usize), |(s, c, d), (s2, c2, d2)| {
(s + s2, c + c2, d.max(*d2))
});
(
value + child_sum,
1 + child_count,
pat.depth().max(child_max_depth),
)
});
assert_eq!(stats.0, 10, "Sum: 1 + 2 + 3 + 4 = 10");
assert_eq!(stats.1, 4, "Count: 4 nodes total");
assert_eq!(stats.2, 2, "Max depth: 2 (root -> pattern(2) -> point(3))");
}
#[test]
fn para_complex_nesting_statistics() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3), Pattern::point(4)]),
Pattern::pattern(5, vec![Pattern::pattern(6, vec![Pattern::point(7)])]),
Pattern::point(8),
],
);
type Stats = (i32, usize, usize);
let stats: Stats = p.para(|pat, rs: &[Stats]| {
let value = *pat.value();
let (child_sum, child_count, child_max_depth) = rs
.iter()
.fold((0_i32, 0_usize, 0_usize), |(s, c, d), (s2, c2, d2)| {
(s + s2, c + c2, d.max(*d2))
});
(
value + child_sum,
1 + child_count,
pat.depth().max(child_max_depth),
)
});
assert_eq!(stats.0, 36, "Sum of all values");
assert_eq!(stats.1, 8, "Total node count");
assert_eq!(stats.2, 3, "Maximum nesting depth");
}
#[test]
fn para_single_traversal_statistics() {
let p = Pattern::pattern(
10,
vec![
Pattern::pattern(20, vec![Pattern::point(30), Pattern::point(40)]),
Pattern::point(50),
],
);
use std::cell::RefCell;
let visit_count = RefCell::new(0);
type Stats = (i32, usize, usize);
let stats: Stats = p.para(|pat, rs: &[Stats]| {
*visit_count.borrow_mut() += 1;
let value = *pat.value();
let (child_sum, child_count, child_max_depth) = rs
.iter()
.fold((0_i32, 0_usize, 0_usize), |(s, c, d), (s2, c2, d2)| {
(s + s2, c + c2, d.max(*d2))
});
(
value + child_sum,
1 + child_count,
pat.depth().max(child_max_depth),
)
});
assert_eq!(stats.0, 150, "Sum: 10+20+30+40+50");
assert_eq!(stats.1, 5, "Count: 5 nodes");
assert_eq!(stats.2, 2, "Max depth: 2");
assert_eq!(
*visit_count.borrow(),
5,
"Should visit each node exactly once"
);
}
#[test]
fn para_simulates_fold() {
let p = Pattern::pattern(
10,
vec![
Pattern::pattern(20, vec![Pattern::point(30), Pattern::point(40)]),
Pattern::point(50),
],
);
let para_sum: i32 = p.para(|pat, rs| *pat.value() + rs.iter().sum::<i32>());
let fold_sum: i32 = p.fold(0, |acc, v| acc + v);
assert_eq!(para_sum, fold_sum, "Para should produce same sum as fold");
assert_eq!(para_sum, 150, "Sum: 10+20+30+40+50 = 150");
}
#[test]
fn para_simulates_fold_product() {
let p = Pattern::pattern(
2,
vec![
Pattern::pattern(3, vec![Pattern::point(4)]),
Pattern::point(5),
],
);
let para_product: i32 = p.para(|pat, rs| {
let element_product: i32 = rs.iter().product();
if element_product == 0 {
*pat.value() } else {
*pat.value() * element_product
}
});
let fold_product: i32 = p.fold(1, |acc, v| acc * v);
assert_eq!(
para_product, fold_product,
"Para should produce same product as fold"
);
assert_eq!(para_product, 120, "Product: 2*3*4*5 = 120");
}
#[test]
fn para_preserves_order_tolist() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3), Pattern::point(4)]),
Pattern::point(5),
],
);
let para_values: Vec<i32> = p.para(|pat, rs: &[Vec<i32>]| {
let mut result = vec![*pat.value()];
for child_vec in rs {
result.extend(child_vec);
}
result
});
assert_eq!(
para_values,
vec![1, 2, 3, 4, 5],
"Para should preserve pre-order"
);
let values_method: Vec<i32> = p.values().into_iter().copied().collect();
assert_eq!(
para_values, values_method,
"Para toList should match values()"
);
}
#[test]
fn para_order_preservation_wide() {
let p = Pattern::pattern(
0,
vec![
Pattern::point(1),
Pattern::point(2),
Pattern::point(3),
Pattern::point(4),
],
);
let para_values: Vec<i32> = p.para(|pat, rs: &[Vec<i32>]| {
let mut result = vec![*pat.value()];
for child_vec in rs {
result.extend(child_vec);
}
result
});
assert_eq!(
para_values,
vec![0, 1, 2, 3, 4],
"Should preserve left-to-right order"
);
}
#[test]
fn para_custom_structure_aware_folding() {
let p = Pattern::pattern(
"root",
vec![
Pattern::pattern(
"branch",
vec![Pattern::point("leaf1"), Pattern::point("leaf2")],
),
Pattern::point("leaf3"),
],
);
type Description = String;
let description: Description = p.para(|pat, rs: &[Description]| {
let value = *pat.value();
let elem_count = pat.elements().len();
let depth = pat.depth();
if elem_count == 0 {
format!("Leaf({})", value)
} else {
let children_desc = rs.join(", ");
format!(
"Node({}, depth={}, elements=[{}])",
value, depth, children_desc
)
}
});
assert!(description.contains("root"), "Should mention root");
assert!(description.contains("branch"), "Should mention branch");
assert!(description.contains("leaf1"), "Should mention leaf1");
assert!(description.contains("depth="), "Should include depth info");
}
#[test]
fn para_structure_preserving_transformation() {
let p = Pattern::pattern(
1,
vec![
Pattern::pattern(2, vec![Pattern::point(3)]),
Pattern::point(4),
],
);
let transformed: Pattern<i32> = p.para(|pat, rs: &[Pattern<i32>]| {
let value = *pat.value();
let depth = pat.depth();
let new_value = value * (depth as i32 + 1);
Pattern::pattern(new_value, rs.to_vec())
});
assert_eq!(transformed.size(), p.size(), "Size should be preserved");
assert_eq!(transformed.depth(), p.depth(), "Depth should be preserved");
assert_eq!(
transformed.elements().len(),
p.elements().len(),
"Element count should be preserved"
);
assert_eq!(*transformed.value(), 3);
}
#[test]
fn para_complex_custom_logic() {
let p = Pattern::pattern(
10,
vec![
Pattern::pattern(20, vec![Pattern::point(30)]),
Pattern::point(40),
],
);
type WeightedSum = (i32, usize); let result: WeightedSum = p.para(|pat, rs: &[WeightedSum]| {
let value = *pat.value();
let (child_sum, child_descendants): (i32, usize) =
rs.iter().fold((0, 0), |(s, d), (s2, d2)| (s + s2, d + d2));
let my_descendants = child_descendants + pat.elements().len();
let my_weighted_value = value * (1 + my_descendants) as i32;
(my_weighted_value + child_sum, my_descendants)
});
assert_eq!(result.0, 150, "Weighted sum should be 150");
assert_eq!(result.1, 3, "Root should have 3 descendants");
}
}