1use std::collections::HashMap;
7
8use super::TLExpr;
9
10#[derive(Clone, Debug, Default, PartialEq, Eq)]
12pub struct OperatorCounts {
13 pub total: usize,
15 pub logical: usize,
17 pub arithmetic: usize,
19 pub comparison: usize,
21 pub mathematical: usize,
23 pub quantifiers: usize,
25 pub modal: usize,
27 pub temporal: usize,
29 pub fuzzy: usize,
31 pub probabilistic: usize,
33 pub aggregation: usize,
35 pub control_flow: usize,
37 pub predicates: usize,
39 pub constants: usize,
41}
42
43impl OperatorCounts {
44 pub fn new() -> Self {
46 Self::default()
47 }
48
49 pub fn from_expr(expr: &TLExpr) -> Self {
51 let mut counts = Self::new();
52 counts.count_recursive(expr);
53 counts
54 }
55
56 fn count_recursive(&mut self, expr: &TLExpr) {
58 self.total += 1;
59
60 match expr {
61 TLExpr::Pred { .. } => {
63 self.predicates += 1;
64 }
65
66 TLExpr::Constant(_) => {
68 self.constants += 1;
69 }
70
71 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 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 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 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 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; self.count_recursive(body);
131 }
132
133 TLExpr::Box(e) | TLExpr::Diamond(e) => {
135 self.modal += 1;
136 self.count_recursive(e);
137 }
138
139 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 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 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 TLExpr::Aggregate { body, .. } => {
193 self.aggregation += 1;
194 self.count_recursive(body);
195 }
196
197 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 TLExpr::Lambda { body, .. } => {
216 self.control_flow += 1;
217 self.count_recursive(body);
218 }
219 TLExpr::Apply { function, argument } => {
220 self.control_flow += 1;
221 self.count_recursive(function);
222 self.count_recursive(argument);
223 }
224 TLExpr::SetMembership { element, set }
225 | TLExpr::SetUnion {
226 left: element,
227 right: set,
228 }
229 | TLExpr::SetIntersection {
230 left: element,
231 right: set,
232 }
233 | TLExpr::SetDifference {
234 left: element,
235 right: set,
236 } => {
237 self.logical += 1;
238 self.count_recursive(element);
239 self.count_recursive(set);
240 }
241 TLExpr::SetCardinality { set } => {
242 self.arithmetic += 1;
243 self.count_recursive(set);
244 }
245 TLExpr::EmptySet => {
246 self.constants += 1;
247 }
248 TLExpr::SetComprehension { condition, .. } => {
249 self.quantifiers += 1;
250 self.count_recursive(condition);
251 }
252 TLExpr::CountingExists { body, .. }
253 | TLExpr::CountingForAll { body, .. }
254 | TLExpr::ExactCount { body, .. }
255 | TLExpr::Majority { body, .. } => {
256 self.quantifiers += 1;
257 self.count_recursive(body);
258 }
259 TLExpr::LeastFixpoint { body, .. } | TLExpr::GreatestFixpoint { body, .. } => {
260 self.control_flow += 1;
261 self.count_recursive(body);
262 }
263 TLExpr::Nominal { .. } => {
264 self.constants += 1;
265 }
266 TLExpr::At { formula, .. } => {
267 self.modal += 1;
268 self.count_recursive(formula);
269 }
270 TLExpr::Somewhere { formula } | TLExpr::Everywhere { formula } => {
271 self.modal += 1;
272 self.count_recursive(formula);
273 }
274 TLExpr::AllDifferent { .. } => {
275 self.logical += 1;
276 }
277 TLExpr::GlobalCardinality { values, .. } => {
278 self.logical += 1;
279 for val in values {
280 self.count_recursive(val);
281 }
282 }
283 TLExpr::Abducible { .. } => {
284 self.predicates += 1;
285 }
286 TLExpr::Explain { formula } => {
287 self.control_flow += 1;
288 self.count_recursive(formula);
289 }
290 TLExpr::SymbolLiteral(_) => {
291 self.predicates += 1;
292 }
293 TLExpr::Match { scrutinee, arms } => {
294 self.control_flow += 1;
295 self.count_recursive(scrutinee);
296 for (_, body) in arms {
297 self.count_recursive(body);
298 }
299 }
300 }
301 }
302}
303
304#[derive(Clone, Debug, PartialEq)]
306pub struct ComplexityMetrics {
307 pub max_depth: usize,
309 pub avg_depth: f64,
311 pub node_count: usize,
313 pub leaf_count: usize,
315 pub branching_factor: f64,
317 pub cyclomatic_complexity: usize,
319 pub quantifier_depth: usize,
321 pub modal_depth: usize,
323 pub temporal_depth: usize,
325}
326
327impl ComplexityMetrics {
328 pub fn from_expr(expr: &TLExpr) -> Self {
330 let mut metrics = Self {
331 max_depth: 0,
332 avg_depth: 0.0,
333 node_count: 0,
334 leaf_count: 0,
335 branching_factor: 0.0,
336 cyclomatic_complexity: 1, quantifier_depth: 0,
338 modal_depth: 0,
339 temporal_depth: 0,
340 };
341
342 let mut depth_sum = 0;
343 let mut non_leaf_count = 0;
344 let mut child_count = 0;
345
346 metrics.compute_recursive(
347 expr,
348 0,
349 &mut depth_sum,
350 &mut non_leaf_count,
351 &mut child_count,
352 0,
353 0,
354 0,
355 );
356
357 if metrics.leaf_count > 0 {
359 metrics.avg_depth = depth_sum as f64 / metrics.leaf_count as f64;
360 }
361 if non_leaf_count > 0 {
362 metrics.branching_factor = child_count as f64 / non_leaf_count as f64;
363 }
364
365 metrics
366 }
367
368 #[allow(clippy::too_many_arguments)]
369 fn compute_recursive(
370 &mut self,
371 expr: &TLExpr,
372 depth: usize,
373 depth_sum: &mut usize,
374 non_leaf_count: &mut usize,
375 child_count: &mut usize,
376 quantifier_depth: usize,
377 modal_depth: usize,
378 temporal_depth: usize,
379 ) {
380 self.node_count += 1;
381 self.max_depth = self.max_depth.max(depth);
382 self.quantifier_depth = self.quantifier_depth.max(quantifier_depth);
383 self.modal_depth = self.modal_depth.max(modal_depth);
384 self.temporal_depth = self.temporal_depth.max(temporal_depth);
385
386 match expr {
387 TLExpr::Pred { .. } | TLExpr::Constant(_) => {
389 self.leaf_count += 1;
390 *depth_sum += depth;
391 }
392
393 TLExpr::IfThenElse {
395 condition,
396 then_branch,
397 else_branch,
398 } => {
399 self.cyclomatic_complexity += 1; *non_leaf_count += 1;
401 *child_count += 3;
402 self.compute_recursive(
403 condition,
404 depth + 1,
405 depth_sum,
406 non_leaf_count,
407 child_count,
408 quantifier_depth,
409 modal_depth,
410 temporal_depth,
411 );
412 self.compute_recursive(
413 then_branch,
414 depth + 1,
415 depth_sum,
416 non_leaf_count,
417 child_count,
418 quantifier_depth,
419 modal_depth,
420 temporal_depth,
421 );
422 self.compute_recursive(
423 else_branch,
424 depth + 1,
425 depth_sum,
426 non_leaf_count,
427 child_count,
428 quantifier_depth,
429 modal_depth,
430 temporal_depth,
431 );
432 }
433
434 TLExpr::And(l, r) | TLExpr::Or(l, r) => {
436 self.cyclomatic_complexity += 1; *non_leaf_count += 1;
438 *child_count += 2;
439 self.compute_recursive(
440 l,
441 depth + 1,
442 depth_sum,
443 non_leaf_count,
444 child_count,
445 quantifier_depth,
446 modal_depth,
447 temporal_depth,
448 );
449 self.compute_recursive(
450 r,
451 depth + 1,
452 depth_sum,
453 non_leaf_count,
454 child_count,
455 quantifier_depth,
456 modal_depth,
457 temporal_depth,
458 );
459 }
460
461 TLExpr::Exists { body, .. }
463 | TLExpr::ForAll { body, .. }
464 | TLExpr::SoftExists { body, .. }
465 | TLExpr::SoftForAll { body, .. } => {
466 *non_leaf_count += 1;
467 *child_count += 1;
468 self.compute_recursive(
469 body,
470 depth + 1,
471 depth_sum,
472 non_leaf_count,
473 child_count,
474 quantifier_depth + 1,
475 modal_depth,
476 temporal_depth,
477 );
478 }
479
480 TLExpr::Box(e) | TLExpr::Diamond(e) => {
482 *non_leaf_count += 1;
483 *child_count += 1;
484 self.compute_recursive(
485 e,
486 depth + 1,
487 depth_sum,
488 non_leaf_count,
489 child_count,
490 quantifier_depth,
491 modal_depth + 1,
492 temporal_depth,
493 );
494 }
495
496 TLExpr::Next(e) | TLExpr::Eventually(e) | TLExpr::Always(e) => {
498 *non_leaf_count += 1;
499 *child_count += 1;
500 self.compute_recursive(
501 e,
502 depth + 1,
503 depth_sum,
504 non_leaf_count,
505 child_count,
506 quantifier_depth,
507 modal_depth,
508 temporal_depth + 1,
509 );
510 }
511
512 TLExpr::Until { before, after }
514 | TLExpr::Release {
515 released: before,
516 releaser: after,
517 }
518 | TLExpr::WeakUntil { before, after }
519 | TLExpr::StrongRelease {
520 released: before,
521 releaser: after,
522 } => {
523 *non_leaf_count += 1;
524 *child_count += 2;
525 self.compute_recursive(
526 before,
527 depth + 1,
528 depth_sum,
529 non_leaf_count,
530 child_count,
531 quantifier_depth,
532 modal_depth,
533 temporal_depth + 1,
534 );
535 self.compute_recursive(
536 after,
537 depth + 1,
538 depth_sum,
539 non_leaf_count,
540 child_count,
541 quantifier_depth,
542 modal_depth,
543 temporal_depth + 1,
544 );
545 }
546
547 TLExpr::Not(e)
549 | TLExpr::Score(e)
550 | TLExpr::Abs(e)
551 | TLExpr::Floor(e)
552 | TLExpr::Ceil(e)
553 | TLExpr::Round(e)
554 | TLExpr::Sqrt(e)
555 | TLExpr::Exp(e)
556 | TLExpr::Log(e)
557 | TLExpr::Sin(e)
558 | TLExpr::Cos(e)
559 | TLExpr::Tan(e) => {
560 *non_leaf_count += 1;
561 *child_count += 1;
562 self.compute_recursive(
563 e,
564 depth + 1,
565 depth_sum,
566 non_leaf_count,
567 child_count,
568 quantifier_depth,
569 modal_depth,
570 temporal_depth,
571 );
572 }
573
574 TLExpr::Imply(l, r)
576 | TLExpr::Add(l, r)
577 | TLExpr::Sub(l, r)
578 | TLExpr::Mul(l, r)
579 | TLExpr::Div(l, r)
580 | TLExpr::Pow(l, r)
581 | TLExpr::Mod(l, r)
582 | TLExpr::Min(l, r)
583 | TLExpr::Max(l, r)
584 | TLExpr::Eq(l, r)
585 | TLExpr::Lt(l, r)
586 | TLExpr::Gt(l, r)
587 | TLExpr::Lte(l, r)
588 | TLExpr::Gte(l, r) => {
589 *non_leaf_count += 1;
590 *child_count += 2;
591 self.compute_recursive(
592 l,
593 depth + 1,
594 depth_sum,
595 non_leaf_count,
596 child_count,
597 quantifier_depth,
598 modal_depth,
599 temporal_depth,
600 );
601 self.compute_recursive(
602 r,
603 depth + 1,
604 depth_sum,
605 non_leaf_count,
606 child_count,
607 quantifier_depth,
608 modal_depth,
609 temporal_depth,
610 );
611 }
612
613 TLExpr::TNorm { left, right, .. }
615 | TLExpr::TCoNorm { left, right, .. }
616 | TLExpr::FuzzyImplication {
617 premise: left,
618 conclusion: right,
619 ..
620 } => {
621 *non_leaf_count += 1;
622 *child_count += 2;
623 self.compute_recursive(
624 left,
625 depth + 1,
626 depth_sum,
627 non_leaf_count,
628 child_count,
629 quantifier_depth,
630 modal_depth,
631 temporal_depth,
632 );
633 self.compute_recursive(
634 right,
635 depth + 1,
636 depth_sum,
637 non_leaf_count,
638 child_count,
639 quantifier_depth,
640 modal_depth,
641 temporal_depth,
642 );
643 }
644
645 TLExpr::FuzzyNot { expr, .. } => {
646 *non_leaf_count += 1;
647 *child_count += 1;
648 self.compute_recursive(
649 expr,
650 depth + 1,
651 depth_sum,
652 non_leaf_count,
653 child_count,
654 quantifier_depth,
655 modal_depth,
656 temporal_depth,
657 );
658 }
659
660 TLExpr::WeightedRule { rule, .. } => {
662 *non_leaf_count += 1;
663 *child_count += 1;
664 self.compute_recursive(
665 rule,
666 depth + 1,
667 depth_sum,
668 non_leaf_count,
669 child_count,
670 quantifier_depth,
671 modal_depth,
672 temporal_depth,
673 );
674 }
675
676 TLExpr::ProbabilisticChoice { alternatives } => {
677 self.cyclomatic_complexity += alternatives.len(); *non_leaf_count += 1;
679 *child_count += alternatives.len();
680 for (_, e) in alternatives {
681 self.compute_recursive(
682 e,
683 depth + 1,
684 depth_sum,
685 non_leaf_count,
686 child_count,
687 quantifier_depth,
688 modal_depth,
689 temporal_depth,
690 );
691 }
692 }
693
694 TLExpr::Aggregate { body, .. } => {
696 *non_leaf_count += 1;
697 *child_count += 1;
698 self.compute_recursive(
699 body,
700 depth + 1,
701 depth_sum,
702 non_leaf_count,
703 child_count,
704 quantifier_depth,
705 modal_depth,
706 temporal_depth,
707 );
708 }
709
710 TLExpr::Let { value, body, .. } => {
712 *non_leaf_count += 1;
713 *child_count += 2;
714 self.compute_recursive(
715 value,
716 depth + 1,
717 depth_sum,
718 non_leaf_count,
719 child_count,
720 quantifier_depth,
721 modal_depth,
722 temporal_depth,
723 );
724 self.compute_recursive(
725 body,
726 depth + 1,
727 depth_sum,
728 non_leaf_count,
729 child_count,
730 quantifier_depth,
731 modal_depth,
732 temporal_depth,
733 );
734 }
735
736 TLExpr::Lambda { body, .. }
738 | TLExpr::SetCardinality { set: body }
739 | TLExpr::SetComprehension {
740 condition: body, ..
741 }
742 | TLExpr::CountingExists { body, .. }
743 | TLExpr::CountingForAll { body, .. }
744 | TLExpr::ExactCount { body, .. }
745 | TLExpr::Majority { body, .. }
746 | TLExpr::LeastFixpoint { body, .. }
747 | TLExpr::GreatestFixpoint { body, .. }
748 | TLExpr::At { formula: body, .. }
749 | TLExpr::Somewhere { formula: body }
750 | TLExpr::Everywhere { formula: body }
751 | TLExpr::Explain { formula: body } => {
752 *non_leaf_count += 1;
753 *child_count += 1;
754 self.compute_recursive(
755 body,
756 depth + 1,
757 depth_sum,
758 non_leaf_count,
759 child_count,
760 quantifier_depth + 1,
761 modal_depth,
762 temporal_depth,
763 );
764 }
765 TLExpr::Apply { function, argument }
766 | TLExpr::SetMembership {
767 element: function,
768 set: argument,
769 }
770 | TLExpr::SetUnion {
771 left: function,
772 right: argument,
773 }
774 | TLExpr::SetIntersection {
775 left: function,
776 right: argument,
777 }
778 | TLExpr::SetDifference {
779 left: function,
780 right: argument,
781 } => {
782 *non_leaf_count += 1;
783 *child_count += 2;
784 self.compute_recursive(
785 function,
786 depth + 1,
787 depth_sum,
788 non_leaf_count,
789 child_count,
790 quantifier_depth,
791 modal_depth,
792 temporal_depth,
793 );
794 self.compute_recursive(
795 argument,
796 depth + 1,
797 depth_sum,
798 non_leaf_count,
799 child_count,
800 quantifier_depth,
801 modal_depth,
802 temporal_depth,
803 );
804 }
805 TLExpr::GlobalCardinality { values, .. } => {
806 *non_leaf_count += 1;
807 *child_count += values.len();
808 for val in values {
809 self.compute_recursive(
810 val,
811 depth + 1,
812 depth_sum,
813 non_leaf_count,
814 child_count,
815 quantifier_depth,
816 modal_depth,
817 temporal_depth,
818 );
819 }
820 }
821 TLExpr::EmptySet
822 | TLExpr::Nominal { .. }
823 | TLExpr::AllDifferent { .. }
824 | TLExpr::Abducible { .. }
825 | TLExpr::SymbolLiteral(_) => {
826 self.leaf_count += 1;
827 *depth_sum += depth;
828 }
829 TLExpr::Match { scrutinee, arms } => {
830 *non_leaf_count += 1;
831 *child_count += 1 + arms.len();
832 self.compute_recursive(
833 scrutinee,
834 depth + 1,
835 depth_sum,
836 non_leaf_count,
837 child_count,
838 quantifier_depth,
839 modal_depth,
840 temporal_depth,
841 );
842 for (_, body) in arms {
843 self.compute_recursive(
844 body,
845 depth + 1,
846 depth_sum,
847 non_leaf_count,
848 child_count,
849 quantifier_depth,
850 modal_depth,
851 temporal_depth,
852 );
853 }
854 }
855 }
856 }
857}
858
859#[derive(Clone, Debug, PartialEq, Eq)]
861pub struct PatternAnalysis {
862 pub de_morgan_patterns: usize,
864 pub double_negation: usize,
866 pub modal_duality: usize,
868 pub temporal_duality: usize,
870 pub redundant_quantifiers: usize,
872 pub tautologies: usize,
874 pub contradictions: usize,
876}
877
878impl PatternAnalysis {
879 pub fn from_expr(expr: &TLExpr) -> Self {
881 let mut analysis = Self {
882 de_morgan_patterns: 0,
883 double_negation: 0,
884 modal_duality: 0,
885 temporal_duality: 0,
886 redundant_quantifiers: 0,
887 tautologies: 0,
888 contradictions: 0,
889 };
890
891 analysis.detect_recursive(expr, &HashMap::new());
892 analysis
893 }
894
895 fn detect_recursive(&mut self, expr: &TLExpr, _context: &HashMap<String, TLExpr>) {
896 match expr {
897 TLExpr::Not(e) => {
899 if matches!(**e, TLExpr::Not(_)) {
901 self.double_negation += 1;
902 }
903 if matches!(**e, TLExpr::And(_, _) | TLExpr::Or(_, _)) {
905 self.de_morgan_patterns += 1;
906 }
907 self.detect_recursive(e, _context);
908 }
909
910 TLExpr::Or(l, r) => {
912 if let (TLExpr::Not(inner), other) | (other, TLExpr::Not(inner)) = (&**l, &**r) {
913 if **inner == *other {
914 self.tautologies += 1;
915 }
916 }
917 if matches!(**l, TLExpr::Constant(v) if v >= 1.0)
918 || matches!(**r, TLExpr::Constant(v) if v >= 1.0)
919 {
920 self.tautologies += 1;
921 }
922 self.detect_recursive(l, _context);
923 self.detect_recursive(r, _context);
924 }
925
926 TLExpr::Imply(l, r) => {
927 if l == r {
928 self.tautologies += 1;
929 }
930 self.detect_recursive(l, _context);
931 self.detect_recursive(r, _context);
932 }
933
934 TLExpr::And(l, r) => {
936 if let (TLExpr::Not(inner), other) | (other, TLExpr::Not(inner)) = (&**l, &**r) {
937 if **inner == *other {
938 self.contradictions += 1;
939 }
940 }
941 if matches!(**l, TLExpr::Constant(v) if v <= 0.0)
942 || matches!(**r, TLExpr::Constant(v) if v <= 0.0)
943 {
944 self.contradictions += 1;
945 }
946 self.detect_recursive(l, _context);
947 self.detect_recursive(r, _context);
948 }
949
950 TLExpr::Exists { var, body, .. } => {
952 if let TLExpr::Exists { var: inner_var, .. } = &**body {
953 if var == inner_var {
954 self.redundant_quantifiers += 1;
955 }
956 }
957 self.detect_recursive(body, _context);
958 }
959
960 TLExpr::ForAll { var, body, .. } => {
961 if let TLExpr::ForAll { var: inner_var, .. } = &**body {
962 if var == inner_var {
963 self.redundant_quantifiers += 1;
964 }
965 }
966 self.detect_recursive(body, _context);
967 }
968
969 TLExpr::Diamond(e) => {
971 if let TLExpr::Not(inner) = &**e {
972 if let TLExpr::Box(inner_inner) = &**inner {
973 if matches!(**inner_inner, TLExpr::Not(_)) {
974 self.modal_duality += 1;
975 }
976 }
977 }
978 self.detect_recursive(e, _context);
979 }
980
981 TLExpr::Eventually(e) => {
983 if let TLExpr::Not(inner) = &**e {
984 if let TLExpr::Always(inner_inner) = &**inner {
985 if matches!(**inner_inner, TLExpr::Not(_)) {
986 self.temporal_duality += 1;
987 }
988 }
989 }
990 self.detect_recursive(e, _context);
991 }
992
993 TLExpr::Add(l, r)
995 | TLExpr::Sub(l, r)
996 | TLExpr::Mul(l, r)
997 | TLExpr::Div(l, r)
998 | TLExpr::Pow(l, r)
999 | TLExpr::Mod(l, r)
1000 | TLExpr::Min(l, r)
1001 | TLExpr::Max(l, r)
1002 | TLExpr::Eq(l, r)
1003 | TLExpr::Lt(l, r)
1004 | TLExpr::Gt(l, r)
1005 | TLExpr::Lte(l, r)
1006 | TLExpr::Gte(l, r) => {
1007 self.detect_recursive(l, _context);
1008 self.detect_recursive(r, _context);
1009 }
1010
1011 TLExpr::Score(e)
1012 | TLExpr::Abs(e)
1013 | TLExpr::Floor(e)
1014 | TLExpr::Ceil(e)
1015 | TLExpr::Round(e)
1016 | TLExpr::Sqrt(e)
1017 | TLExpr::Exp(e)
1018 | TLExpr::Log(e)
1019 | TLExpr::Sin(e)
1020 | TLExpr::Cos(e)
1021 | TLExpr::Tan(e)
1022 | TLExpr::Box(e)
1023 | TLExpr::Next(e)
1024 | TLExpr::Always(e) => {
1025 self.detect_recursive(e, _context);
1026 }
1027
1028 TLExpr::Until { before, after }
1029 | TLExpr::Release {
1030 released: before,
1031 releaser: after,
1032 }
1033 | TLExpr::WeakUntil { before, after }
1034 | TLExpr::StrongRelease {
1035 released: before,
1036 releaser: after,
1037 } => {
1038 self.detect_recursive(before, _context);
1039 self.detect_recursive(after, _context);
1040 }
1041
1042 TLExpr::TNorm { left, right, .. }
1043 | TLExpr::TCoNorm { left, right, .. }
1044 | TLExpr::FuzzyImplication {
1045 premise: left,
1046 conclusion: right,
1047 ..
1048 } => {
1049 self.detect_recursive(left, _context);
1050 self.detect_recursive(right, _context);
1051 }
1052
1053 TLExpr::FuzzyNot { expr, .. } => {
1054 self.detect_recursive(expr, _context);
1055 }
1056
1057 TLExpr::SoftExists { body, .. }
1058 | TLExpr::SoftForAll { body, .. }
1059 | TLExpr::Aggregate { body, .. } => {
1060 self.detect_recursive(body, _context);
1061 }
1062
1063 TLExpr::WeightedRule { rule, .. } => {
1064 self.detect_recursive(rule, _context);
1065 }
1066
1067 TLExpr::ProbabilisticChoice { alternatives } => {
1068 for (_, e) in alternatives {
1069 self.detect_recursive(e, _context);
1070 }
1071 }
1072
1073 TLExpr::IfThenElse {
1074 condition,
1075 then_branch,
1076 else_branch,
1077 } => {
1078 self.detect_recursive(condition, _context);
1079 self.detect_recursive(then_branch, _context);
1080 self.detect_recursive(else_branch, _context);
1081 }
1082
1083 TLExpr::Let { value, body, .. } => {
1084 self.detect_recursive(value, _context);
1085 self.detect_recursive(body, _context);
1086 }
1087
1088 TLExpr::Lambda { body, .. }
1090 | TLExpr::SetCardinality { set: body }
1091 | TLExpr::SetComprehension {
1092 condition: body, ..
1093 }
1094 | TLExpr::CountingExists { body, .. }
1095 | TLExpr::CountingForAll { body, .. }
1096 | TLExpr::ExactCount { body, .. }
1097 | TLExpr::Majority { body, .. }
1098 | TLExpr::LeastFixpoint { body, .. }
1099 | TLExpr::GreatestFixpoint { body, .. }
1100 | TLExpr::At { formula: body, .. }
1101 | TLExpr::Somewhere { formula: body }
1102 | TLExpr::Everywhere { formula: body }
1103 | TLExpr::Explain { formula: body } => {
1104 self.detect_recursive(body, _context);
1105 }
1106 TLExpr::Apply { function, argument }
1107 | TLExpr::SetMembership {
1108 element: function,
1109 set: argument,
1110 }
1111 | TLExpr::SetUnion {
1112 left: function,
1113 right: argument,
1114 }
1115 | TLExpr::SetIntersection {
1116 left: function,
1117 right: argument,
1118 }
1119 | TLExpr::SetDifference {
1120 left: function,
1121 right: argument,
1122 } => {
1123 self.detect_recursive(function, _context);
1124 self.detect_recursive(argument, _context);
1125 }
1126 TLExpr::GlobalCardinality { values, .. } => {
1127 for val in values {
1128 self.detect_recursive(val, _context);
1129 }
1130 }
1131 TLExpr::EmptySet
1132 | TLExpr::Nominal { .. }
1133 | TLExpr::AllDifferent { .. }
1134 | TLExpr::Abducible { .. }
1135 | TLExpr::SymbolLiteral(_) => {}
1136
1137 TLExpr::Match { scrutinee, arms } => {
1138 self.detect_recursive(scrutinee, _context);
1139 for (_, body) in arms {
1140 self.detect_recursive(body, _context);
1141 }
1142 }
1143
1144 TLExpr::Pred { .. } | TLExpr::Constant(_) => {}
1146 }
1147 }
1148}
1149
1150#[cfg(test)]
1151mod tests {
1152 use super::*;
1153 use crate::Term;
1154
1155 #[test]
1156 fn test_operator_counts_basic() {
1157 let expr = TLExpr::and(
1159 TLExpr::pred("P", vec![Term::var("x")]),
1160 TLExpr::pred("Q", vec![Term::var("x")]),
1161 );
1162
1163 let counts = OperatorCounts::from_expr(&expr);
1164 assert_eq!(counts.total, 3); assert_eq!(counts.logical, 1);
1166 assert_eq!(counts.predicates, 2);
1167 }
1168
1169 #[test]
1170 fn test_operator_counts_modal() {
1171 let expr = TLExpr::modal_box(TLExpr::pred("P", vec![Term::var("x")]));
1173
1174 let counts = OperatorCounts::from_expr(&expr);
1175 assert_eq!(counts.modal, 1);
1176 assert_eq!(counts.predicates, 1);
1177 }
1178
1179 #[test]
1180 fn test_operator_counts_temporal() {
1181 let expr = TLExpr::eventually(TLExpr::pred("P", vec![Term::var("x")]));
1183
1184 let counts = OperatorCounts::from_expr(&expr);
1185 assert_eq!(counts.temporal, 1);
1186 assert_eq!(counts.predicates, 1);
1187 }
1188
1189 #[test]
1190 fn test_operator_counts_fuzzy() {
1191 let expr = TLExpr::fuzzy_and(
1193 TLExpr::pred("P", vec![Term::var("x")]),
1194 TLExpr::pred("Q", vec![Term::var("x")]),
1195 );
1196
1197 let counts = OperatorCounts::from_expr(&expr);
1198 assert_eq!(counts.fuzzy, 1);
1199 assert_eq!(counts.predicates, 2);
1200 }
1201
1202 #[test]
1203 fn test_complexity_metrics_simple() {
1204 let expr = TLExpr::pred("P", vec![Term::var("x")]);
1206
1207 let metrics = ComplexityMetrics::from_expr(&expr);
1208 assert_eq!(metrics.max_depth, 0);
1209 assert_eq!(metrics.node_count, 1);
1210 assert_eq!(metrics.leaf_count, 1);
1211 }
1212
1213 #[test]
1214 fn test_complexity_metrics_nested() {
1215 let expr = TLExpr::or(
1217 TLExpr::and(
1218 TLExpr::pred("P", vec![Term::var("x")]),
1219 TLExpr::pred("Q", vec![Term::var("x")]),
1220 ),
1221 TLExpr::and(
1222 TLExpr::pred("R", vec![Term::var("x")]),
1223 TLExpr::pred("S", vec![Term::var("x")]),
1224 ),
1225 );
1226
1227 let metrics = ComplexityMetrics::from_expr(&expr);
1228 assert_eq!(metrics.max_depth, 2); assert_eq!(metrics.node_count, 7); assert_eq!(metrics.leaf_count, 4);
1231 assert_eq!(metrics.cyclomatic_complexity, 4); }
1233
1234 #[test]
1235 fn test_complexity_quantifier_depth() {
1236 let expr = TLExpr::exists(
1238 "x",
1239 "D",
1240 TLExpr::forall(
1241 "y",
1242 "D",
1243 TLExpr::pred("P", vec![Term::var("x"), Term::var("y")]),
1244 ),
1245 );
1246
1247 let metrics = ComplexityMetrics::from_expr(&expr);
1248 assert_eq!(metrics.quantifier_depth, 2);
1249 }
1250
1251 #[test]
1252 fn test_complexity_modal_depth() {
1253 let expr = TLExpr::modal_box(TLExpr::modal_diamond(TLExpr::pred(
1255 "P",
1256 vec![Term::var("x")],
1257 )));
1258
1259 let metrics = ComplexityMetrics::from_expr(&expr);
1260 assert_eq!(metrics.modal_depth, 2);
1261 }
1262
1263 #[test]
1264 fn test_pattern_double_negation() {
1265 let expr = TLExpr::negate(TLExpr::negate(TLExpr::pred("P", vec![Term::var("x")])));
1267
1268 let patterns = PatternAnalysis::from_expr(&expr);
1269 assert_eq!(patterns.double_negation, 1);
1270 }
1271
1272 #[test]
1273 fn test_pattern_de_morgan() {
1274 let expr = TLExpr::negate(TLExpr::and(
1276 TLExpr::pred("P", vec![Term::var("x")]),
1277 TLExpr::pred("Q", vec![Term::var("x")]),
1278 ));
1279
1280 let patterns = PatternAnalysis::from_expr(&expr);
1281 assert_eq!(patterns.de_morgan_patterns, 1);
1282 }
1283
1284 #[test]
1285 fn test_pattern_tautology() {
1286 let p = TLExpr::pred("P", vec![Term::var("x")]);
1288 let expr = TLExpr::or(p.clone(), TLExpr::negate(p));
1289
1290 let patterns = PatternAnalysis::from_expr(&expr);
1291 assert_eq!(patterns.tautologies, 1);
1292 }
1293
1294 #[test]
1295 fn test_pattern_contradiction() {
1296 let p = TLExpr::pred("P", vec![Term::var("x")]);
1298 let expr = TLExpr::and(p.clone(), TLExpr::negate(p));
1299
1300 let patterns = PatternAnalysis::from_expr(&expr);
1301 assert_eq!(patterns.contradictions, 1);
1302 }
1303
1304 #[test]
1305 fn test_pattern_redundant_quantifier() {
1306 let expr = TLExpr::exists(
1308 "x",
1309 "D",
1310 TLExpr::exists("x", "D", TLExpr::pred("P", vec![Term::var("x")])),
1311 );
1312
1313 let patterns = PatternAnalysis::from_expr(&expr);
1314 assert_eq!(patterns.redundant_quantifiers, 1);
1315 }
1316}