tensorlogic_ir/expr/
advanced_analysis.rs

1//! Advanced expression analysis for operator counting and complexity metrics.
2//!
3//! This module provides detailed analysis capabilities for TensorLogic expressions,
4//! including operator type classification, complexity metrics, and pattern detection.
5
6use std::collections::HashMap;
7
8use super::TLExpr;
9
10/// Detailed operator counts categorized by type.
11#[derive(Clone, Debug, Default, PartialEq, Eq)]
12pub struct OperatorCounts {
13    /// Total number of operators
14    pub total: usize,
15    /// Basic logical operators (And, Or, Not, Imply)
16    pub logical: usize,
17    /// Arithmetic operators (Add, Sub, Mul, Div, Pow, Mod, Min, Max)
18    pub arithmetic: usize,
19    /// Comparison operators (Eq, Lt, Gt, Lte, Gte)
20    pub comparison: usize,
21    /// Unary mathematical functions (Abs, Floor, Ceil, Round, Sqrt, Exp, Log, Sin, Cos, Tan)
22    pub mathematical: usize,
23    /// Quantifiers (Exists, ForAll, SoftExists, SoftForAll)
24    pub quantifiers: usize,
25    /// Modal logic operators (Box, Diamond)
26    pub modal: usize,
27    /// Temporal logic operators (Next, Eventually, Always, Until, Release, WeakUntil, StrongRelease)
28    pub temporal: usize,
29    /// Fuzzy logic operators (TNorm, TCoNorm, FuzzyNot, FuzzyImplication)
30    pub fuzzy: usize,
31    /// Probabilistic operators (WeightedRule, ProbabilisticChoice)
32    pub probabilistic: usize,
33    /// Aggregation operators
34    pub aggregation: usize,
35    /// Control flow (IfThenElse, Let)
36    pub control_flow: usize,
37    /// Predicates
38    pub predicates: usize,
39    /// Constants
40    pub constants: usize,
41}
42
43impl OperatorCounts {
44    /// Create a new empty operator count.
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// Compute operator counts from an expression.
50    pub fn from_expr(expr: &TLExpr) -> Self {
51        let mut counts = Self::new();
52        counts.count_recursive(expr);
53        counts
54    }
55
56    /// Recursively count operators in an expression.
57    fn count_recursive(&mut self, expr: &TLExpr) {
58        self.total += 1;
59
60        match expr {
61            // Predicates
62            TLExpr::Pred { .. } => {
63                self.predicates += 1;
64            }
65
66            // Constants
67            TLExpr::Constant(_) => {
68                self.constants += 1;
69            }
70
71            // Basic logical operators
72            TLExpr::And(l, r) | TLExpr::Or(l, r) | TLExpr::Imply(l, r) => {
73                self.logical += 1;
74                self.count_recursive(l);
75                self.count_recursive(r);
76            }
77            TLExpr::Not(e) | TLExpr::Score(e) => {
78                self.logical += 1;
79                self.count_recursive(e);
80            }
81
82            // Arithmetic operators
83            TLExpr::Add(l, r)
84            | TLExpr::Sub(l, r)
85            | TLExpr::Mul(l, r)
86            | TLExpr::Div(l, r)
87            | TLExpr::Pow(l, r)
88            | TLExpr::Mod(l, r)
89            | TLExpr::Min(l, r)
90            | TLExpr::Max(l, r) => {
91                self.arithmetic += 1;
92                self.count_recursive(l);
93                self.count_recursive(r);
94            }
95
96            // Comparison operators
97            TLExpr::Eq(l, r)
98            | TLExpr::Lt(l, r)
99            | TLExpr::Gt(l, r)
100            | TLExpr::Lte(l, r)
101            | TLExpr::Gte(l, r) => {
102                self.comparison += 1;
103                self.count_recursive(l);
104                self.count_recursive(r);
105            }
106
107            // Mathematical functions
108            TLExpr::Abs(e)
109            | TLExpr::Floor(e)
110            | TLExpr::Ceil(e)
111            | TLExpr::Round(e)
112            | TLExpr::Sqrt(e)
113            | TLExpr::Exp(e)
114            | TLExpr::Log(e)
115            | TLExpr::Sin(e)
116            | TLExpr::Cos(e)
117            | TLExpr::Tan(e) => {
118                self.mathematical += 1;
119                self.count_recursive(e);
120            }
121
122            // Quantifiers
123            TLExpr::Exists { body, .. } | TLExpr::ForAll { body, .. } => {
124                self.quantifiers += 1;
125                self.count_recursive(body);
126            }
127            TLExpr::SoftExists { body, .. } | TLExpr::SoftForAll { body, .. } => {
128                self.quantifiers += 1;
129                self.probabilistic += 1; // Also counts as probabilistic
130                self.count_recursive(body);
131            }
132
133            // Modal logic
134            TLExpr::Box(e) | TLExpr::Diamond(e) => {
135                self.modal += 1;
136                self.count_recursive(e);
137            }
138
139            // Temporal logic
140            TLExpr::Next(e) | TLExpr::Eventually(e) | TLExpr::Always(e) => {
141                self.temporal += 1;
142                self.count_recursive(e);
143            }
144            TLExpr::Until { before, after }
145            | TLExpr::Release {
146                released: before,
147                releaser: after,
148            }
149            | TLExpr::WeakUntil { before, after }
150            | TLExpr::StrongRelease {
151                released: before,
152                releaser: after,
153            } => {
154                self.temporal += 1;
155                self.count_recursive(before);
156                self.count_recursive(after);
157            }
158
159            // Fuzzy logic
160            TLExpr::TNorm { left, right, .. } | TLExpr::TCoNorm { left, right, .. } => {
161                self.fuzzy += 1;
162                self.count_recursive(left);
163                self.count_recursive(right);
164            }
165            TLExpr::FuzzyNot { expr, .. } => {
166                self.fuzzy += 1;
167                self.count_recursive(expr);
168            }
169            TLExpr::FuzzyImplication {
170                premise,
171                conclusion,
172                ..
173            } => {
174                self.fuzzy += 1;
175                self.count_recursive(premise);
176                self.count_recursive(conclusion);
177            }
178
179            // Probabilistic operators
180            TLExpr::WeightedRule { rule, .. } => {
181                self.probabilistic += 1;
182                self.count_recursive(rule);
183            }
184            TLExpr::ProbabilisticChoice { alternatives } => {
185                self.probabilistic += 1;
186                for (_, e) in alternatives {
187                    self.count_recursive(e);
188                }
189            }
190
191            // Aggregation
192            TLExpr::Aggregate { body, .. } => {
193                self.aggregation += 1;
194                self.count_recursive(body);
195            }
196
197            // Control flow
198            TLExpr::IfThenElse {
199                condition,
200                then_branch,
201                else_branch,
202            } => {
203                self.control_flow += 1;
204                self.count_recursive(condition);
205                self.count_recursive(then_branch);
206                self.count_recursive(else_branch);
207            }
208            TLExpr::Let { value, body, .. } => {
209                self.control_flow += 1;
210                self.count_recursive(value);
211                self.count_recursive(body);
212            }
213        }
214    }
215}
216
217/// Complexity metrics for an expression.
218#[derive(Clone, Debug, PartialEq)]
219pub struct ComplexityMetrics {
220    /// Maximum depth of the expression tree
221    pub max_depth: usize,
222    /// Average depth of leaf nodes
223    pub avg_depth: f64,
224    /// Total number of nodes
225    pub node_count: usize,
226    /// Number of leaf nodes (predicates and constants)
227    pub leaf_count: usize,
228    /// Branching factor (average number of children per non-leaf node)
229    pub branching_factor: f64,
230    /// Cyclomatic complexity (number of decision points + 1)
231    pub cyclomatic_complexity: usize,
232    /// Quantifier depth (maximum nesting level of quantifiers)
233    pub quantifier_depth: usize,
234    /// Modal depth (maximum nesting level of modal operators)
235    pub modal_depth: usize,
236    /// Temporal depth (maximum nesting level of temporal operators)
237    pub temporal_depth: usize,
238}
239
240impl ComplexityMetrics {
241    /// Compute complexity metrics from an expression.
242    pub fn from_expr(expr: &TLExpr) -> Self {
243        let mut metrics = Self {
244            max_depth: 0,
245            avg_depth: 0.0,
246            node_count: 0,
247            leaf_count: 0,
248            branching_factor: 0.0,
249            cyclomatic_complexity: 1, // Start with 1
250            quantifier_depth: 0,
251            modal_depth: 0,
252            temporal_depth: 0,
253        };
254
255        let mut depth_sum = 0;
256        let mut non_leaf_count = 0;
257        let mut child_count = 0;
258
259        metrics.compute_recursive(
260            expr,
261            0,
262            &mut depth_sum,
263            &mut non_leaf_count,
264            &mut child_count,
265            0,
266            0,
267            0,
268        );
269
270        // Compute averages
271        if metrics.leaf_count > 0 {
272            metrics.avg_depth = depth_sum as f64 / metrics.leaf_count as f64;
273        }
274        if non_leaf_count > 0 {
275            metrics.branching_factor = child_count as f64 / non_leaf_count as f64;
276        }
277
278        metrics
279    }
280
281    #[allow(clippy::too_many_arguments)]
282    fn compute_recursive(
283        &mut self,
284        expr: &TLExpr,
285        depth: usize,
286        depth_sum: &mut usize,
287        non_leaf_count: &mut usize,
288        child_count: &mut usize,
289        quantifier_depth: usize,
290        modal_depth: usize,
291        temporal_depth: usize,
292    ) {
293        self.node_count += 1;
294        self.max_depth = self.max_depth.max(depth);
295        self.quantifier_depth = self.quantifier_depth.max(quantifier_depth);
296        self.modal_depth = self.modal_depth.max(modal_depth);
297        self.temporal_depth = self.temporal_depth.max(temporal_depth);
298
299        match expr {
300            // Leaves
301            TLExpr::Pred { .. } | TLExpr::Constant(_) => {
302                self.leaf_count += 1;
303                *depth_sum += depth;
304            }
305
306            // Decision points (add to cyclomatic complexity)
307            TLExpr::IfThenElse {
308                condition,
309                then_branch,
310                else_branch,
311            } => {
312                self.cyclomatic_complexity += 1; // Decision point
313                *non_leaf_count += 1;
314                *child_count += 3;
315                self.compute_recursive(
316                    condition,
317                    depth + 1,
318                    depth_sum,
319                    non_leaf_count,
320                    child_count,
321                    quantifier_depth,
322                    modal_depth,
323                    temporal_depth,
324                );
325                self.compute_recursive(
326                    then_branch,
327                    depth + 1,
328                    depth_sum,
329                    non_leaf_count,
330                    child_count,
331                    quantifier_depth,
332                    modal_depth,
333                    temporal_depth,
334                );
335                self.compute_recursive(
336                    else_branch,
337                    depth + 1,
338                    depth_sum,
339                    non_leaf_count,
340                    child_count,
341                    quantifier_depth,
342                    modal_depth,
343                    temporal_depth,
344                );
345            }
346
347            // Logical operators (decision points)
348            TLExpr::And(l, r) | TLExpr::Or(l, r) => {
349                self.cyclomatic_complexity += 1; // Decision point
350                *non_leaf_count += 1;
351                *child_count += 2;
352                self.compute_recursive(
353                    l,
354                    depth + 1,
355                    depth_sum,
356                    non_leaf_count,
357                    child_count,
358                    quantifier_depth,
359                    modal_depth,
360                    temporal_depth,
361                );
362                self.compute_recursive(
363                    r,
364                    depth + 1,
365                    depth_sum,
366                    non_leaf_count,
367                    child_count,
368                    quantifier_depth,
369                    modal_depth,
370                    temporal_depth,
371                );
372            }
373
374            // Quantifiers (increase quantifier depth)
375            TLExpr::Exists { body, .. }
376            | TLExpr::ForAll { body, .. }
377            | TLExpr::SoftExists { body, .. }
378            | TLExpr::SoftForAll { body, .. } => {
379                *non_leaf_count += 1;
380                *child_count += 1;
381                self.compute_recursive(
382                    body,
383                    depth + 1,
384                    depth_sum,
385                    non_leaf_count,
386                    child_count,
387                    quantifier_depth + 1,
388                    modal_depth,
389                    temporal_depth,
390                );
391            }
392
393            // Modal operators (increase modal depth)
394            TLExpr::Box(e) | TLExpr::Diamond(e) => {
395                *non_leaf_count += 1;
396                *child_count += 1;
397                self.compute_recursive(
398                    e,
399                    depth + 1,
400                    depth_sum,
401                    non_leaf_count,
402                    child_count,
403                    quantifier_depth,
404                    modal_depth + 1,
405                    temporal_depth,
406                );
407            }
408
409            // Temporal operators (increase temporal depth)
410            TLExpr::Next(e) | TLExpr::Eventually(e) | TLExpr::Always(e) => {
411                *non_leaf_count += 1;
412                *child_count += 1;
413                self.compute_recursive(
414                    e,
415                    depth + 1,
416                    depth_sum,
417                    non_leaf_count,
418                    child_count,
419                    quantifier_depth,
420                    modal_depth,
421                    temporal_depth + 1,
422                );
423            }
424
425            // Binary temporal operators
426            TLExpr::Until { before, after }
427            | TLExpr::Release {
428                released: before,
429                releaser: after,
430            }
431            | TLExpr::WeakUntil { before, after }
432            | TLExpr::StrongRelease {
433                released: before,
434                releaser: after,
435            } => {
436                *non_leaf_count += 1;
437                *child_count += 2;
438                self.compute_recursive(
439                    before,
440                    depth + 1,
441                    depth_sum,
442                    non_leaf_count,
443                    child_count,
444                    quantifier_depth,
445                    modal_depth,
446                    temporal_depth + 1,
447                );
448                self.compute_recursive(
449                    after,
450                    depth + 1,
451                    depth_sum,
452                    non_leaf_count,
453                    child_count,
454                    quantifier_depth,
455                    modal_depth,
456                    temporal_depth + 1,
457                );
458            }
459
460            // Unary operators
461            TLExpr::Not(e)
462            | TLExpr::Score(e)
463            | TLExpr::Abs(e)
464            | TLExpr::Floor(e)
465            | TLExpr::Ceil(e)
466            | TLExpr::Round(e)
467            | TLExpr::Sqrt(e)
468            | TLExpr::Exp(e)
469            | TLExpr::Log(e)
470            | TLExpr::Sin(e)
471            | TLExpr::Cos(e)
472            | TLExpr::Tan(e) => {
473                *non_leaf_count += 1;
474                *child_count += 1;
475                self.compute_recursive(
476                    e,
477                    depth + 1,
478                    depth_sum,
479                    non_leaf_count,
480                    child_count,
481                    quantifier_depth,
482                    modal_depth,
483                    temporal_depth,
484                );
485            }
486
487            // Binary operators
488            TLExpr::Imply(l, r)
489            | TLExpr::Add(l, r)
490            | TLExpr::Sub(l, r)
491            | TLExpr::Mul(l, r)
492            | TLExpr::Div(l, r)
493            | TLExpr::Pow(l, r)
494            | TLExpr::Mod(l, r)
495            | TLExpr::Min(l, r)
496            | TLExpr::Max(l, r)
497            | TLExpr::Eq(l, r)
498            | TLExpr::Lt(l, r)
499            | TLExpr::Gt(l, r)
500            | TLExpr::Lte(l, r)
501            | TLExpr::Gte(l, r) => {
502                *non_leaf_count += 1;
503                *child_count += 2;
504                self.compute_recursive(
505                    l,
506                    depth + 1,
507                    depth_sum,
508                    non_leaf_count,
509                    child_count,
510                    quantifier_depth,
511                    modal_depth,
512                    temporal_depth,
513                );
514                self.compute_recursive(
515                    r,
516                    depth + 1,
517                    depth_sum,
518                    non_leaf_count,
519                    child_count,
520                    quantifier_depth,
521                    modal_depth,
522                    temporal_depth,
523                );
524            }
525
526            // Fuzzy operators
527            TLExpr::TNorm { left, right, .. }
528            | TLExpr::TCoNorm { left, right, .. }
529            | TLExpr::FuzzyImplication {
530                premise: left,
531                conclusion: right,
532                ..
533            } => {
534                *non_leaf_count += 1;
535                *child_count += 2;
536                self.compute_recursive(
537                    left,
538                    depth + 1,
539                    depth_sum,
540                    non_leaf_count,
541                    child_count,
542                    quantifier_depth,
543                    modal_depth,
544                    temporal_depth,
545                );
546                self.compute_recursive(
547                    right,
548                    depth + 1,
549                    depth_sum,
550                    non_leaf_count,
551                    child_count,
552                    quantifier_depth,
553                    modal_depth,
554                    temporal_depth,
555                );
556            }
557
558            TLExpr::FuzzyNot { expr, .. } => {
559                *non_leaf_count += 1;
560                *child_count += 1;
561                self.compute_recursive(
562                    expr,
563                    depth + 1,
564                    depth_sum,
565                    non_leaf_count,
566                    child_count,
567                    quantifier_depth,
568                    modal_depth,
569                    temporal_depth,
570                );
571            }
572
573            // Probabilistic operators
574            TLExpr::WeightedRule { rule, .. } => {
575                *non_leaf_count += 1;
576                *child_count += 1;
577                self.compute_recursive(
578                    rule,
579                    depth + 1,
580                    depth_sum,
581                    non_leaf_count,
582                    child_count,
583                    quantifier_depth,
584                    modal_depth,
585                    temporal_depth,
586                );
587            }
588
589            TLExpr::ProbabilisticChoice { alternatives } => {
590                self.cyclomatic_complexity += alternatives.len(); // Multiple decision points
591                *non_leaf_count += 1;
592                *child_count += alternatives.len();
593                for (_, e) in alternatives {
594                    self.compute_recursive(
595                        e,
596                        depth + 1,
597                        depth_sum,
598                        non_leaf_count,
599                        child_count,
600                        quantifier_depth,
601                        modal_depth,
602                        temporal_depth,
603                    );
604                }
605            }
606
607            // Aggregation
608            TLExpr::Aggregate { body, .. } => {
609                *non_leaf_count += 1;
610                *child_count += 1;
611                self.compute_recursive(
612                    body,
613                    depth + 1,
614                    depth_sum,
615                    non_leaf_count,
616                    child_count,
617                    quantifier_depth,
618                    modal_depth,
619                    temporal_depth,
620                );
621            }
622
623            // Let binding
624            TLExpr::Let { value, body, .. } => {
625                *non_leaf_count += 1;
626                *child_count += 2;
627                self.compute_recursive(
628                    value,
629                    depth + 1,
630                    depth_sum,
631                    non_leaf_count,
632                    child_count,
633                    quantifier_depth,
634                    modal_depth,
635                    temporal_depth,
636                );
637                self.compute_recursive(
638                    body,
639                    depth + 1,
640                    depth_sum,
641                    non_leaf_count,
642                    child_count,
643                    quantifier_depth,
644                    modal_depth,
645                    temporal_depth,
646                );
647            }
648        }
649    }
650}
651
652/// Pattern detection results.
653#[derive(Clone, Debug, PartialEq, Eq)]
654pub struct PatternAnalysis {
655    /// Detected De Morgan's law patterns: ¬(A ∧ B) or ¬(A ∨ B)
656    pub de_morgan_patterns: usize,
657    /// Double negation patterns: ¬¬A
658    pub double_negation: usize,
659    /// Modal duality patterns: ◇P with ¬□¬P nearby
660    pub modal_duality: usize,
661    /// Temporal duality patterns: FP with ¬G¬P nearby
662    pub temporal_duality: usize,
663    /// Redundant quantifier patterns (nested same quantifiers)
664    pub redundant_quantifiers: usize,
665    /// Tautologies (always true expressions)
666    pub tautologies: usize,
667    /// Contradictions (always false expressions)
668    pub contradictions: usize,
669}
670
671impl PatternAnalysis {
672    /// Detect patterns in an expression.
673    pub fn from_expr(expr: &TLExpr) -> Self {
674        let mut analysis = Self {
675            de_morgan_patterns: 0,
676            double_negation: 0,
677            modal_duality: 0,
678            temporal_duality: 0,
679            redundant_quantifiers: 0,
680            tautologies: 0,
681            contradictions: 0,
682        };
683
684        analysis.detect_recursive(expr, &HashMap::new());
685        analysis
686    }
687
688    fn detect_recursive(&mut self, expr: &TLExpr, _context: &HashMap<String, TLExpr>) {
689        match expr {
690            // Double negation and De Morgan's patterns
691            TLExpr::Not(e) => {
692                // Double negation: ¬¬A
693                if matches!(**e, TLExpr::Not(_)) {
694                    self.double_negation += 1;
695                }
696                // De Morgan's patterns: ¬(A ∧ B) or ¬(A ∨ B)
697                if matches!(**e, TLExpr::And(_, _) | TLExpr::Or(_, _)) {
698                    self.de_morgan_patterns += 1;
699                }
700                self.detect_recursive(e, _context);
701            }
702
703            // Tautologies: A ∨ ¬A, A → A, A ∨ TRUE
704            TLExpr::Or(l, r) => {
705                if let (TLExpr::Not(inner), other) | (other, TLExpr::Not(inner)) = (&**l, &**r) {
706                    if **inner == *other {
707                        self.tautologies += 1;
708                    }
709                }
710                if matches!(**l, TLExpr::Constant(v) if v >= 1.0)
711                    || matches!(**r, TLExpr::Constant(v) if v >= 1.0)
712                {
713                    self.tautologies += 1;
714                }
715                self.detect_recursive(l, _context);
716                self.detect_recursive(r, _context);
717            }
718
719            TLExpr::Imply(l, r) => {
720                if l == r {
721                    self.tautologies += 1;
722                }
723                self.detect_recursive(l, _context);
724                self.detect_recursive(r, _context);
725            }
726
727            // Contradictions: A ∧ ¬A, A ∧ FALSE
728            TLExpr::And(l, r) => {
729                if let (TLExpr::Not(inner), other) | (other, TLExpr::Not(inner)) = (&**l, &**r) {
730                    if **inner == *other {
731                        self.contradictions += 1;
732                    }
733                }
734                if matches!(**l, TLExpr::Constant(v) if v <= 0.0)
735                    || matches!(**r, TLExpr::Constant(v) if v <= 0.0)
736                {
737                    self.contradictions += 1;
738                }
739                self.detect_recursive(l, _context);
740                self.detect_recursive(r, _context);
741            }
742
743            // Redundant quantifiers: ∃x.∃x.P or ∀x.∀x.P
744            TLExpr::Exists { var, body, .. } => {
745                if let TLExpr::Exists { var: inner_var, .. } = &**body {
746                    if var == inner_var {
747                        self.redundant_quantifiers += 1;
748                    }
749                }
750                self.detect_recursive(body, _context);
751            }
752
753            TLExpr::ForAll { var, body, .. } => {
754                if let TLExpr::ForAll { var: inner_var, .. } = &**body {
755                    if var == inner_var {
756                        self.redundant_quantifiers += 1;
757                    }
758                }
759                self.detect_recursive(body, _context);
760            }
761
762            // Modal duality: ◇P when we see ¬□¬P pattern
763            TLExpr::Diamond(e) => {
764                if let TLExpr::Not(inner) = &**e {
765                    if let TLExpr::Box(inner_inner) = &**inner {
766                        if matches!(**inner_inner, TLExpr::Not(_)) {
767                            self.modal_duality += 1;
768                        }
769                    }
770                }
771                self.detect_recursive(e, _context);
772            }
773
774            // Temporal duality: FP when we see ¬G¬P pattern
775            TLExpr::Eventually(e) => {
776                if let TLExpr::Not(inner) = &**e {
777                    if let TLExpr::Always(inner_inner) = &**inner {
778                        if matches!(**inner_inner, TLExpr::Not(_)) {
779                            self.temporal_duality += 1;
780                        }
781                    }
782                }
783                self.detect_recursive(e, _context);
784            }
785
786            // Recursive cases (And and Or are handled above for pattern detection)
787            TLExpr::Add(l, r)
788            | TLExpr::Sub(l, r)
789            | TLExpr::Mul(l, r)
790            | TLExpr::Div(l, r)
791            | TLExpr::Pow(l, r)
792            | TLExpr::Mod(l, r)
793            | TLExpr::Min(l, r)
794            | TLExpr::Max(l, r)
795            | TLExpr::Eq(l, r)
796            | TLExpr::Lt(l, r)
797            | TLExpr::Gt(l, r)
798            | TLExpr::Lte(l, r)
799            | TLExpr::Gte(l, r) => {
800                self.detect_recursive(l, _context);
801                self.detect_recursive(r, _context);
802            }
803
804            TLExpr::Score(e)
805            | TLExpr::Abs(e)
806            | TLExpr::Floor(e)
807            | TLExpr::Ceil(e)
808            | TLExpr::Round(e)
809            | TLExpr::Sqrt(e)
810            | TLExpr::Exp(e)
811            | TLExpr::Log(e)
812            | TLExpr::Sin(e)
813            | TLExpr::Cos(e)
814            | TLExpr::Tan(e)
815            | TLExpr::Box(e)
816            | TLExpr::Next(e)
817            | TLExpr::Always(e) => {
818                self.detect_recursive(e, _context);
819            }
820
821            TLExpr::Until { before, after }
822            | TLExpr::Release {
823                released: before,
824                releaser: after,
825            }
826            | TLExpr::WeakUntil { before, after }
827            | TLExpr::StrongRelease {
828                released: before,
829                releaser: after,
830            } => {
831                self.detect_recursive(before, _context);
832                self.detect_recursive(after, _context);
833            }
834
835            TLExpr::TNorm { left, right, .. }
836            | TLExpr::TCoNorm { left, right, .. }
837            | TLExpr::FuzzyImplication {
838                premise: left,
839                conclusion: right,
840                ..
841            } => {
842                self.detect_recursive(left, _context);
843                self.detect_recursive(right, _context);
844            }
845
846            TLExpr::FuzzyNot { expr, .. } => {
847                self.detect_recursive(expr, _context);
848            }
849
850            TLExpr::SoftExists { body, .. }
851            | TLExpr::SoftForAll { body, .. }
852            | TLExpr::Aggregate { body, .. } => {
853                self.detect_recursive(body, _context);
854            }
855
856            TLExpr::WeightedRule { rule, .. } => {
857                self.detect_recursive(rule, _context);
858            }
859
860            TLExpr::ProbabilisticChoice { alternatives } => {
861                for (_, e) in alternatives {
862                    self.detect_recursive(e, _context);
863                }
864            }
865
866            TLExpr::IfThenElse {
867                condition,
868                then_branch,
869                else_branch,
870            } => {
871                self.detect_recursive(condition, _context);
872                self.detect_recursive(then_branch, _context);
873                self.detect_recursive(else_branch, _context);
874            }
875
876            TLExpr::Let { value, body, .. } => {
877                self.detect_recursive(value, _context);
878                self.detect_recursive(body, _context);
879            }
880
881            // Leaves
882            TLExpr::Pred { .. } | TLExpr::Constant(_) => {}
883        }
884    }
885}
886
887#[cfg(test)]
888mod tests {
889    use super::*;
890    use crate::Term;
891
892    #[test]
893    fn test_operator_counts_basic() {
894        // Simple: P(x) AND Q(x)
895        let expr = TLExpr::and(
896            TLExpr::pred("P", vec![Term::var("x")]),
897            TLExpr::pred("Q", vec![Term::var("x")]),
898        );
899
900        let counts = OperatorCounts::from_expr(&expr);
901        assert_eq!(counts.total, 3); // AND + 2 predicates
902        assert_eq!(counts.logical, 1);
903        assert_eq!(counts.predicates, 2);
904    }
905
906    #[test]
907    fn test_operator_counts_modal() {
908        // □(P(x))
909        let expr = TLExpr::modal_box(TLExpr::pred("P", vec![Term::var("x")]));
910
911        let counts = OperatorCounts::from_expr(&expr);
912        assert_eq!(counts.modal, 1);
913        assert_eq!(counts.predicates, 1);
914    }
915
916    #[test]
917    fn test_operator_counts_temporal() {
918        // F(P(x))
919        let expr = TLExpr::eventually(TLExpr::pred("P", vec![Term::var("x")]));
920
921        let counts = OperatorCounts::from_expr(&expr);
922        assert_eq!(counts.temporal, 1);
923        assert_eq!(counts.predicates, 1);
924    }
925
926    #[test]
927    fn test_operator_counts_fuzzy() {
928        // P(x) ⊤_min Q(x)
929        let expr = TLExpr::fuzzy_and(
930            TLExpr::pred("P", vec![Term::var("x")]),
931            TLExpr::pred("Q", vec![Term::var("x")]),
932        );
933
934        let counts = OperatorCounts::from_expr(&expr);
935        assert_eq!(counts.fuzzy, 1);
936        assert_eq!(counts.predicates, 2);
937    }
938
939    #[test]
940    fn test_complexity_metrics_simple() {
941        // P(x)
942        let expr = TLExpr::pred("P", vec![Term::var("x")]);
943
944        let metrics = ComplexityMetrics::from_expr(&expr);
945        assert_eq!(metrics.max_depth, 0);
946        assert_eq!(metrics.node_count, 1);
947        assert_eq!(metrics.leaf_count, 1);
948    }
949
950    #[test]
951    fn test_complexity_metrics_nested() {
952        // ((P AND Q) OR (R AND S))
953        let expr = TLExpr::or(
954            TLExpr::and(
955                TLExpr::pred("P", vec![Term::var("x")]),
956                TLExpr::pred("Q", vec![Term::var("x")]),
957            ),
958            TLExpr::and(
959                TLExpr::pred("R", vec![Term::var("x")]),
960                TLExpr::pred("S", vec![Term::var("x")]),
961            ),
962        );
963
964        let metrics = ComplexityMetrics::from_expr(&expr);
965        assert_eq!(metrics.max_depth, 2); // OR at 0, AND at 1, predicates at 2
966        assert_eq!(metrics.node_count, 7); // 1 OR + 2 AND + 4 predicates
967        assert_eq!(metrics.leaf_count, 4);
968        assert_eq!(metrics.cyclomatic_complexity, 4); // 1 + 3 decision points (OR, 2 ANDs)
969    }
970
971    #[test]
972    fn test_complexity_quantifier_depth() {
973        // ∃x.∀y.P(x,y)
974        let expr = TLExpr::exists(
975            "x",
976            "D",
977            TLExpr::forall(
978                "y",
979                "D",
980                TLExpr::pred("P", vec![Term::var("x"), Term::var("y")]),
981            ),
982        );
983
984        let metrics = ComplexityMetrics::from_expr(&expr);
985        assert_eq!(metrics.quantifier_depth, 2);
986    }
987
988    #[test]
989    fn test_complexity_modal_depth() {
990        // □◇P(x)
991        let expr = TLExpr::modal_box(TLExpr::modal_diamond(TLExpr::pred(
992            "P",
993            vec![Term::var("x")],
994        )));
995
996        let metrics = ComplexityMetrics::from_expr(&expr);
997        assert_eq!(metrics.modal_depth, 2);
998    }
999
1000    #[test]
1001    fn test_pattern_double_negation() {
1002        // ¬¬P(x)
1003        let expr = TLExpr::negate(TLExpr::negate(TLExpr::pred("P", vec![Term::var("x")])));
1004
1005        let patterns = PatternAnalysis::from_expr(&expr);
1006        assert_eq!(patterns.double_negation, 1);
1007    }
1008
1009    #[test]
1010    fn test_pattern_de_morgan() {
1011        // ¬(P ∧ Q)
1012        let expr = TLExpr::negate(TLExpr::and(
1013            TLExpr::pred("P", vec![Term::var("x")]),
1014            TLExpr::pred("Q", vec![Term::var("x")]),
1015        ));
1016
1017        let patterns = PatternAnalysis::from_expr(&expr);
1018        assert_eq!(patterns.de_morgan_patterns, 1);
1019    }
1020
1021    #[test]
1022    fn test_pattern_tautology() {
1023        // P(x) ∨ ¬P(x)
1024        let p = TLExpr::pred("P", vec![Term::var("x")]);
1025        let expr = TLExpr::or(p.clone(), TLExpr::negate(p));
1026
1027        let patterns = PatternAnalysis::from_expr(&expr);
1028        assert_eq!(patterns.tautologies, 1);
1029    }
1030
1031    #[test]
1032    fn test_pattern_contradiction() {
1033        // P(x) ∧ ¬P(x)
1034        let p = TLExpr::pred("P", vec![Term::var("x")]);
1035        let expr = TLExpr::and(p.clone(), TLExpr::negate(p));
1036
1037        let patterns = PatternAnalysis::from_expr(&expr);
1038        assert_eq!(patterns.contradictions, 1);
1039    }
1040
1041    #[test]
1042    fn test_pattern_redundant_quantifier() {
1043        // ∃x.∃x.P(x) - redundant nesting of same variable
1044        let expr = TLExpr::exists(
1045            "x",
1046            "D",
1047            TLExpr::exists("x", "D", TLExpr::pred("P", vec![Term::var("x")])),
1048        );
1049
1050        let patterns = PatternAnalysis::from_expr(&expr);
1051        assert_eq!(patterns.redundant_quantifiers, 1);
1052    }
1053}