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 }
291 }
292}
293
294#[derive(Clone, Debug, PartialEq)]
296pub struct ComplexityMetrics {
297 pub max_depth: usize,
299 pub avg_depth: f64,
301 pub node_count: usize,
303 pub leaf_count: usize,
305 pub branching_factor: f64,
307 pub cyclomatic_complexity: usize,
309 pub quantifier_depth: usize,
311 pub modal_depth: usize,
313 pub temporal_depth: usize,
315}
316
317impl ComplexityMetrics {
318 pub fn from_expr(expr: &TLExpr) -> Self {
320 let mut metrics = Self {
321 max_depth: 0,
322 avg_depth: 0.0,
323 node_count: 0,
324 leaf_count: 0,
325 branching_factor: 0.0,
326 cyclomatic_complexity: 1, quantifier_depth: 0,
328 modal_depth: 0,
329 temporal_depth: 0,
330 };
331
332 let mut depth_sum = 0;
333 let mut non_leaf_count = 0;
334 let mut child_count = 0;
335
336 metrics.compute_recursive(
337 expr,
338 0,
339 &mut depth_sum,
340 &mut non_leaf_count,
341 &mut child_count,
342 0,
343 0,
344 0,
345 );
346
347 if metrics.leaf_count > 0 {
349 metrics.avg_depth = depth_sum as f64 / metrics.leaf_count as f64;
350 }
351 if non_leaf_count > 0 {
352 metrics.branching_factor = child_count as f64 / non_leaf_count as f64;
353 }
354
355 metrics
356 }
357
358 #[allow(clippy::too_many_arguments)]
359 fn compute_recursive(
360 &mut self,
361 expr: &TLExpr,
362 depth: usize,
363 depth_sum: &mut usize,
364 non_leaf_count: &mut usize,
365 child_count: &mut usize,
366 quantifier_depth: usize,
367 modal_depth: usize,
368 temporal_depth: usize,
369 ) {
370 self.node_count += 1;
371 self.max_depth = self.max_depth.max(depth);
372 self.quantifier_depth = self.quantifier_depth.max(quantifier_depth);
373 self.modal_depth = self.modal_depth.max(modal_depth);
374 self.temporal_depth = self.temporal_depth.max(temporal_depth);
375
376 match expr {
377 TLExpr::Pred { .. } | TLExpr::Constant(_) => {
379 self.leaf_count += 1;
380 *depth_sum += depth;
381 }
382
383 TLExpr::IfThenElse {
385 condition,
386 then_branch,
387 else_branch,
388 } => {
389 self.cyclomatic_complexity += 1; *non_leaf_count += 1;
391 *child_count += 3;
392 self.compute_recursive(
393 condition,
394 depth + 1,
395 depth_sum,
396 non_leaf_count,
397 child_count,
398 quantifier_depth,
399 modal_depth,
400 temporal_depth,
401 );
402 self.compute_recursive(
403 then_branch,
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 else_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 }
423
424 TLExpr::And(l, r) | TLExpr::Or(l, r) => {
426 self.cyclomatic_complexity += 1; *non_leaf_count += 1;
428 *child_count += 2;
429 self.compute_recursive(
430 l,
431 depth + 1,
432 depth_sum,
433 non_leaf_count,
434 child_count,
435 quantifier_depth,
436 modal_depth,
437 temporal_depth,
438 );
439 self.compute_recursive(
440 r,
441 depth + 1,
442 depth_sum,
443 non_leaf_count,
444 child_count,
445 quantifier_depth,
446 modal_depth,
447 temporal_depth,
448 );
449 }
450
451 TLExpr::Exists { body, .. }
453 | TLExpr::ForAll { body, .. }
454 | TLExpr::SoftExists { body, .. }
455 | TLExpr::SoftForAll { body, .. } => {
456 *non_leaf_count += 1;
457 *child_count += 1;
458 self.compute_recursive(
459 body,
460 depth + 1,
461 depth_sum,
462 non_leaf_count,
463 child_count,
464 quantifier_depth + 1,
465 modal_depth,
466 temporal_depth,
467 );
468 }
469
470 TLExpr::Box(e) | TLExpr::Diamond(e) => {
472 *non_leaf_count += 1;
473 *child_count += 1;
474 self.compute_recursive(
475 e,
476 depth + 1,
477 depth_sum,
478 non_leaf_count,
479 child_count,
480 quantifier_depth,
481 modal_depth + 1,
482 temporal_depth,
483 );
484 }
485
486 TLExpr::Next(e) | TLExpr::Eventually(e) | TLExpr::Always(e) => {
488 *non_leaf_count += 1;
489 *child_count += 1;
490 self.compute_recursive(
491 e,
492 depth + 1,
493 depth_sum,
494 non_leaf_count,
495 child_count,
496 quantifier_depth,
497 modal_depth,
498 temporal_depth + 1,
499 );
500 }
501
502 TLExpr::Until { before, after }
504 | TLExpr::Release {
505 released: before,
506 releaser: after,
507 }
508 | TLExpr::WeakUntil { before, after }
509 | TLExpr::StrongRelease {
510 released: before,
511 releaser: after,
512 } => {
513 *non_leaf_count += 1;
514 *child_count += 2;
515 self.compute_recursive(
516 before,
517 depth + 1,
518 depth_sum,
519 non_leaf_count,
520 child_count,
521 quantifier_depth,
522 modal_depth,
523 temporal_depth + 1,
524 );
525 self.compute_recursive(
526 after,
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 }
536
537 TLExpr::Not(e)
539 | TLExpr::Score(e)
540 | TLExpr::Abs(e)
541 | TLExpr::Floor(e)
542 | TLExpr::Ceil(e)
543 | TLExpr::Round(e)
544 | TLExpr::Sqrt(e)
545 | TLExpr::Exp(e)
546 | TLExpr::Log(e)
547 | TLExpr::Sin(e)
548 | TLExpr::Cos(e)
549 | TLExpr::Tan(e) => {
550 *non_leaf_count += 1;
551 *child_count += 1;
552 self.compute_recursive(
553 e,
554 depth + 1,
555 depth_sum,
556 non_leaf_count,
557 child_count,
558 quantifier_depth,
559 modal_depth,
560 temporal_depth,
561 );
562 }
563
564 TLExpr::Imply(l, r)
566 | TLExpr::Add(l, r)
567 | TLExpr::Sub(l, r)
568 | TLExpr::Mul(l, r)
569 | TLExpr::Div(l, r)
570 | TLExpr::Pow(l, r)
571 | TLExpr::Mod(l, r)
572 | TLExpr::Min(l, r)
573 | TLExpr::Max(l, r)
574 | TLExpr::Eq(l, r)
575 | TLExpr::Lt(l, r)
576 | TLExpr::Gt(l, r)
577 | TLExpr::Lte(l, r)
578 | TLExpr::Gte(l, r) => {
579 *non_leaf_count += 1;
580 *child_count += 2;
581 self.compute_recursive(
582 l,
583 depth + 1,
584 depth_sum,
585 non_leaf_count,
586 child_count,
587 quantifier_depth,
588 modal_depth,
589 temporal_depth,
590 );
591 self.compute_recursive(
592 r,
593 depth + 1,
594 depth_sum,
595 non_leaf_count,
596 child_count,
597 quantifier_depth,
598 modal_depth,
599 temporal_depth,
600 );
601 }
602
603 TLExpr::TNorm { left, right, .. }
605 | TLExpr::TCoNorm { left, right, .. }
606 | TLExpr::FuzzyImplication {
607 premise: left,
608 conclusion: right,
609 ..
610 } => {
611 *non_leaf_count += 1;
612 *child_count += 2;
613 self.compute_recursive(
614 left,
615 depth + 1,
616 depth_sum,
617 non_leaf_count,
618 child_count,
619 quantifier_depth,
620 modal_depth,
621 temporal_depth,
622 );
623 self.compute_recursive(
624 right,
625 depth + 1,
626 depth_sum,
627 non_leaf_count,
628 child_count,
629 quantifier_depth,
630 modal_depth,
631 temporal_depth,
632 );
633 }
634
635 TLExpr::FuzzyNot { expr, .. } => {
636 *non_leaf_count += 1;
637 *child_count += 1;
638 self.compute_recursive(
639 expr,
640 depth + 1,
641 depth_sum,
642 non_leaf_count,
643 child_count,
644 quantifier_depth,
645 modal_depth,
646 temporal_depth,
647 );
648 }
649
650 TLExpr::WeightedRule { rule, .. } => {
652 *non_leaf_count += 1;
653 *child_count += 1;
654 self.compute_recursive(
655 rule,
656 depth + 1,
657 depth_sum,
658 non_leaf_count,
659 child_count,
660 quantifier_depth,
661 modal_depth,
662 temporal_depth,
663 );
664 }
665
666 TLExpr::ProbabilisticChoice { alternatives } => {
667 self.cyclomatic_complexity += alternatives.len(); *non_leaf_count += 1;
669 *child_count += alternatives.len();
670 for (_, e) in alternatives {
671 self.compute_recursive(
672 e,
673 depth + 1,
674 depth_sum,
675 non_leaf_count,
676 child_count,
677 quantifier_depth,
678 modal_depth,
679 temporal_depth,
680 );
681 }
682 }
683
684 TLExpr::Aggregate { body, .. } => {
686 *non_leaf_count += 1;
687 *child_count += 1;
688 self.compute_recursive(
689 body,
690 depth + 1,
691 depth_sum,
692 non_leaf_count,
693 child_count,
694 quantifier_depth,
695 modal_depth,
696 temporal_depth,
697 );
698 }
699
700 TLExpr::Let { value, body, .. } => {
702 *non_leaf_count += 1;
703 *child_count += 2;
704 self.compute_recursive(
705 value,
706 depth + 1,
707 depth_sum,
708 non_leaf_count,
709 child_count,
710 quantifier_depth,
711 modal_depth,
712 temporal_depth,
713 );
714 self.compute_recursive(
715 body,
716 depth + 1,
717 depth_sum,
718 non_leaf_count,
719 child_count,
720 quantifier_depth,
721 modal_depth,
722 temporal_depth,
723 );
724 }
725
726 TLExpr::Lambda { body, .. }
728 | TLExpr::SetCardinality { set: body }
729 | TLExpr::SetComprehension {
730 condition: body, ..
731 }
732 | TLExpr::CountingExists { body, .. }
733 | TLExpr::CountingForAll { body, .. }
734 | TLExpr::ExactCount { body, .. }
735 | TLExpr::Majority { body, .. }
736 | TLExpr::LeastFixpoint { body, .. }
737 | TLExpr::GreatestFixpoint { body, .. }
738 | TLExpr::At { formula: body, .. }
739 | TLExpr::Somewhere { formula: body }
740 | TLExpr::Everywhere { formula: body }
741 | TLExpr::Explain { formula: body } => {
742 *non_leaf_count += 1;
743 *child_count += 1;
744 self.compute_recursive(
745 body,
746 depth + 1,
747 depth_sum,
748 non_leaf_count,
749 child_count,
750 quantifier_depth + 1,
751 modal_depth,
752 temporal_depth,
753 );
754 }
755 TLExpr::Apply { function, argument }
756 | TLExpr::SetMembership {
757 element: function,
758 set: argument,
759 }
760 | TLExpr::SetUnion {
761 left: function,
762 right: argument,
763 }
764 | TLExpr::SetIntersection {
765 left: function,
766 right: argument,
767 }
768 | TLExpr::SetDifference {
769 left: function,
770 right: argument,
771 } => {
772 *non_leaf_count += 1;
773 *child_count += 2;
774 self.compute_recursive(
775 function,
776 depth + 1,
777 depth_sum,
778 non_leaf_count,
779 child_count,
780 quantifier_depth,
781 modal_depth,
782 temporal_depth,
783 );
784 self.compute_recursive(
785 argument,
786 depth + 1,
787 depth_sum,
788 non_leaf_count,
789 child_count,
790 quantifier_depth,
791 modal_depth,
792 temporal_depth,
793 );
794 }
795 TLExpr::GlobalCardinality { values, .. } => {
796 *non_leaf_count += 1;
797 *child_count += values.len();
798 for val in values {
799 self.compute_recursive(
800 val,
801 depth + 1,
802 depth_sum,
803 non_leaf_count,
804 child_count,
805 quantifier_depth,
806 modal_depth,
807 temporal_depth,
808 );
809 }
810 }
811 TLExpr::EmptySet
812 | TLExpr::Nominal { .. }
813 | TLExpr::AllDifferent { .. }
814 | TLExpr::Abducible { .. } => {
815 self.leaf_count += 1;
816 *depth_sum += depth;
817 }
818 }
819 }
820}
821
822#[derive(Clone, Debug, PartialEq, Eq)]
824pub struct PatternAnalysis {
825 pub de_morgan_patterns: usize,
827 pub double_negation: usize,
829 pub modal_duality: usize,
831 pub temporal_duality: usize,
833 pub redundant_quantifiers: usize,
835 pub tautologies: usize,
837 pub contradictions: usize,
839}
840
841impl PatternAnalysis {
842 pub fn from_expr(expr: &TLExpr) -> Self {
844 let mut analysis = Self {
845 de_morgan_patterns: 0,
846 double_negation: 0,
847 modal_duality: 0,
848 temporal_duality: 0,
849 redundant_quantifiers: 0,
850 tautologies: 0,
851 contradictions: 0,
852 };
853
854 analysis.detect_recursive(expr, &HashMap::new());
855 analysis
856 }
857
858 fn detect_recursive(&mut self, expr: &TLExpr, _context: &HashMap<String, TLExpr>) {
859 match expr {
860 TLExpr::Not(e) => {
862 if matches!(**e, TLExpr::Not(_)) {
864 self.double_negation += 1;
865 }
866 if matches!(**e, TLExpr::And(_, _) | TLExpr::Or(_, _)) {
868 self.de_morgan_patterns += 1;
869 }
870 self.detect_recursive(e, _context);
871 }
872
873 TLExpr::Or(l, r) => {
875 if let (TLExpr::Not(inner), other) | (other, TLExpr::Not(inner)) = (&**l, &**r) {
876 if **inner == *other {
877 self.tautologies += 1;
878 }
879 }
880 if matches!(**l, TLExpr::Constant(v) if v >= 1.0)
881 || matches!(**r, TLExpr::Constant(v) if v >= 1.0)
882 {
883 self.tautologies += 1;
884 }
885 self.detect_recursive(l, _context);
886 self.detect_recursive(r, _context);
887 }
888
889 TLExpr::Imply(l, r) => {
890 if l == r {
891 self.tautologies += 1;
892 }
893 self.detect_recursive(l, _context);
894 self.detect_recursive(r, _context);
895 }
896
897 TLExpr::And(l, r) => {
899 if let (TLExpr::Not(inner), other) | (other, TLExpr::Not(inner)) = (&**l, &**r) {
900 if **inner == *other {
901 self.contradictions += 1;
902 }
903 }
904 if matches!(**l, TLExpr::Constant(v) if v <= 0.0)
905 || matches!(**r, TLExpr::Constant(v) if v <= 0.0)
906 {
907 self.contradictions += 1;
908 }
909 self.detect_recursive(l, _context);
910 self.detect_recursive(r, _context);
911 }
912
913 TLExpr::Exists { var, body, .. } => {
915 if let TLExpr::Exists { var: inner_var, .. } = &**body {
916 if var == inner_var {
917 self.redundant_quantifiers += 1;
918 }
919 }
920 self.detect_recursive(body, _context);
921 }
922
923 TLExpr::ForAll { var, body, .. } => {
924 if let TLExpr::ForAll { var: inner_var, .. } = &**body {
925 if var == inner_var {
926 self.redundant_quantifiers += 1;
927 }
928 }
929 self.detect_recursive(body, _context);
930 }
931
932 TLExpr::Diamond(e) => {
934 if let TLExpr::Not(inner) = &**e {
935 if let TLExpr::Box(inner_inner) = &**inner {
936 if matches!(**inner_inner, TLExpr::Not(_)) {
937 self.modal_duality += 1;
938 }
939 }
940 }
941 self.detect_recursive(e, _context);
942 }
943
944 TLExpr::Eventually(e) => {
946 if let TLExpr::Not(inner) = &**e {
947 if let TLExpr::Always(inner_inner) = &**inner {
948 if matches!(**inner_inner, TLExpr::Not(_)) {
949 self.temporal_duality += 1;
950 }
951 }
952 }
953 self.detect_recursive(e, _context);
954 }
955
956 TLExpr::Add(l, r)
958 | TLExpr::Sub(l, r)
959 | TLExpr::Mul(l, r)
960 | TLExpr::Div(l, r)
961 | TLExpr::Pow(l, r)
962 | TLExpr::Mod(l, r)
963 | TLExpr::Min(l, r)
964 | TLExpr::Max(l, r)
965 | TLExpr::Eq(l, r)
966 | TLExpr::Lt(l, r)
967 | TLExpr::Gt(l, r)
968 | TLExpr::Lte(l, r)
969 | TLExpr::Gte(l, r) => {
970 self.detect_recursive(l, _context);
971 self.detect_recursive(r, _context);
972 }
973
974 TLExpr::Score(e)
975 | TLExpr::Abs(e)
976 | TLExpr::Floor(e)
977 | TLExpr::Ceil(e)
978 | TLExpr::Round(e)
979 | TLExpr::Sqrt(e)
980 | TLExpr::Exp(e)
981 | TLExpr::Log(e)
982 | TLExpr::Sin(e)
983 | TLExpr::Cos(e)
984 | TLExpr::Tan(e)
985 | TLExpr::Box(e)
986 | TLExpr::Next(e)
987 | TLExpr::Always(e) => {
988 self.detect_recursive(e, _context);
989 }
990
991 TLExpr::Until { before, after }
992 | TLExpr::Release {
993 released: before,
994 releaser: after,
995 }
996 | TLExpr::WeakUntil { before, after }
997 | TLExpr::StrongRelease {
998 released: before,
999 releaser: after,
1000 } => {
1001 self.detect_recursive(before, _context);
1002 self.detect_recursive(after, _context);
1003 }
1004
1005 TLExpr::TNorm { left, right, .. }
1006 | TLExpr::TCoNorm { left, right, .. }
1007 | TLExpr::FuzzyImplication {
1008 premise: left,
1009 conclusion: right,
1010 ..
1011 } => {
1012 self.detect_recursive(left, _context);
1013 self.detect_recursive(right, _context);
1014 }
1015
1016 TLExpr::FuzzyNot { expr, .. } => {
1017 self.detect_recursive(expr, _context);
1018 }
1019
1020 TLExpr::SoftExists { body, .. }
1021 | TLExpr::SoftForAll { body, .. }
1022 | TLExpr::Aggregate { body, .. } => {
1023 self.detect_recursive(body, _context);
1024 }
1025
1026 TLExpr::WeightedRule { rule, .. } => {
1027 self.detect_recursive(rule, _context);
1028 }
1029
1030 TLExpr::ProbabilisticChoice { alternatives } => {
1031 for (_, e) in alternatives {
1032 self.detect_recursive(e, _context);
1033 }
1034 }
1035
1036 TLExpr::IfThenElse {
1037 condition,
1038 then_branch,
1039 else_branch,
1040 } => {
1041 self.detect_recursive(condition, _context);
1042 self.detect_recursive(then_branch, _context);
1043 self.detect_recursive(else_branch, _context);
1044 }
1045
1046 TLExpr::Let { value, body, .. } => {
1047 self.detect_recursive(value, _context);
1048 self.detect_recursive(body, _context);
1049 }
1050
1051 TLExpr::Lambda { body, .. }
1053 | TLExpr::SetCardinality { set: body }
1054 | TLExpr::SetComprehension {
1055 condition: body, ..
1056 }
1057 | TLExpr::CountingExists { body, .. }
1058 | TLExpr::CountingForAll { body, .. }
1059 | TLExpr::ExactCount { body, .. }
1060 | TLExpr::Majority { body, .. }
1061 | TLExpr::LeastFixpoint { body, .. }
1062 | TLExpr::GreatestFixpoint { body, .. }
1063 | TLExpr::At { formula: body, .. }
1064 | TLExpr::Somewhere { formula: body }
1065 | TLExpr::Everywhere { formula: body }
1066 | TLExpr::Explain { formula: body } => {
1067 self.detect_recursive(body, _context);
1068 }
1069 TLExpr::Apply { function, argument }
1070 | TLExpr::SetMembership {
1071 element: function,
1072 set: argument,
1073 }
1074 | TLExpr::SetUnion {
1075 left: function,
1076 right: argument,
1077 }
1078 | TLExpr::SetIntersection {
1079 left: function,
1080 right: argument,
1081 }
1082 | TLExpr::SetDifference {
1083 left: function,
1084 right: argument,
1085 } => {
1086 self.detect_recursive(function, _context);
1087 self.detect_recursive(argument, _context);
1088 }
1089 TLExpr::GlobalCardinality { values, .. } => {
1090 for val in values {
1091 self.detect_recursive(val, _context);
1092 }
1093 }
1094 TLExpr::EmptySet
1095 | TLExpr::Nominal { .. }
1096 | TLExpr::AllDifferent { .. }
1097 | TLExpr::Abducible { .. } => {}
1098
1099 TLExpr::Pred { .. } | TLExpr::Constant(_) => {}
1101 }
1102 }
1103}
1104
1105#[cfg(test)]
1106mod tests {
1107 use super::*;
1108 use crate::Term;
1109
1110 #[test]
1111 fn test_operator_counts_basic() {
1112 let expr = TLExpr::and(
1114 TLExpr::pred("P", vec![Term::var("x")]),
1115 TLExpr::pred("Q", vec![Term::var("x")]),
1116 );
1117
1118 let counts = OperatorCounts::from_expr(&expr);
1119 assert_eq!(counts.total, 3); assert_eq!(counts.logical, 1);
1121 assert_eq!(counts.predicates, 2);
1122 }
1123
1124 #[test]
1125 fn test_operator_counts_modal() {
1126 let expr = TLExpr::modal_box(TLExpr::pred("P", vec![Term::var("x")]));
1128
1129 let counts = OperatorCounts::from_expr(&expr);
1130 assert_eq!(counts.modal, 1);
1131 assert_eq!(counts.predicates, 1);
1132 }
1133
1134 #[test]
1135 fn test_operator_counts_temporal() {
1136 let expr = TLExpr::eventually(TLExpr::pred("P", vec![Term::var("x")]));
1138
1139 let counts = OperatorCounts::from_expr(&expr);
1140 assert_eq!(counts.temporal, 1);
1141 assert_eq!(counts.predicates, 1);
1142 }
1143
1144 #[test]
1145 fn test_operator_counts_fuzzy() {
1146 let expr = TLExpr::fuzzy_and(
1148 TLExpr::pred("P", vec![Term::var("x")]),
1149 TLExpr::pred("Q", vec![Term::var("x")]),
1150 );
1151
1152 let counts = OperatorCounts::from_expr(&expr);
1153 assert_eq!(counts.fuzzy, 1);
1154 assert_eq!(counts.predicates, 2);
1155 }
1156
1157 #[test]
1158 fn test_complexity_metrics_simple() {
1159 let expr = TLExpr::pred("P", vec![Term::var("x")]);
1161
1162 let metrics = ComplexityMetrics::from_expr(&expr);
1163 assert_eq!(metrics.max_depth, 0);
1164 assert_eq!(metrics.node_count, 1);
1165 assert_eq!(metrics.leaf_count, 1);
1166 }
1167
1168 #[test]
1169 fn test_complexity_metrics_nested() {
1170 let expr = TLExpr::or(
1172 TLExpr::and(
1173 TLExpr::pred("P", vec![Term::var("x")]),
1174 TLExpr::pred("Q", vec![Term::var("x")]),
1175 ),
1176 TLExpr::and(
1177 TLExpr::pred("R", vec![Term::var("x")]),
1178 TLExpr::pred("S", vec![Term::var("x")]),
1179 ),
1180 );
1181
1182 let metrics = ComplexityMetrics::from_expr(&expr);
1183 assert_eq!(metrics.max_depth, 2); assert_eq!(metrics.node_count, 7); assert_eq!(metrics.leaf_count, 4);
1186 assert_eq!(metrics.cyclomatic_complexity, 4); }
1188
1189 #[test]
1190 fn test_complexity_quantifier_depth() {
1191 let expr = TLExpr::exists(
1193 "x",
1194 "D",
1195 TLExpr::forall(
1196 "y",
1197 "D",
1198 TLExpr::pred("P", vec![Term::var("x"), Term::var("y")]),
1199 ),
1200 );
1201
1202 let metrics = ComplexityMetrics::from_expr(&expr);
1203 assert_eq!(metrics.quantifier_depth, 2);
1204 }
1205
1206 #[test]
1207 fn test_complexity_modal_depth() {
1208 let expr = TLExpr::modal_box(TLExpr::modal_diamond(TLExpr::pred(
1210 "P",
1211 vec![Term::var("x")],
1212 )));
1213
1214 let metrics = ComplexityMetrics::from_expr(&expr);
1215 assert_eq!(metrics.modal_depth, 2);
1216 }
1217
1218 #[test]
1219 fn test_pattern_double_negation() {
1220 let expr = TLExpr::negate(TLExpr::negate(TLExpr::pred("P", vec![Term::var("x")])));
1222
1223 let patterns = PatternAnalysis::from_expr(&expr);
1224 assert_eq!(patterns.double_negation, 1);
1225 }
1226
1227 #[test]
1228 fn test_pattern_de_morgan() {
1229 let expr = TLExpr::negate(TLExpr::and(
1231 TLExpr::pred("P", vec![Term::var("x")]),
1232 TLExpr::pred("Q", vec![Term::var("x")]),
1233 ));
1234
1235 let patterns = PatternAnalysis::from_expr(&expr);
1236 assert_eq!(patterns.de_morgan_patterns, 1);
1237 }
1238
1239 #[test]
1240 fn test_pattern_tautology() {
1241 let p = TLExpr::pred("P", vec![Term::var("x")]);
1243 let expr = TLExpr::or(p.clone(), TLExpr::negate(p));
1244
1245 let patterns = PatternAnalysis::from_expr(&expr);
1246 assert_eq!(patterns.tautologies, 1);
1247 }
1248
1249 #[test]
1250 fn test_pattern_contradiction() {
1251 let p = TLExpr::pred("P", vec![Term::var("x")]);
1253 let expr = TLExpr::and(p.clone(), TLExpr::negate(p));
1254
1255 let patterns = PatternAnalysis::from_expr(&expr);
1256 assert_eq!(patterns.contradictions, 1);
1257 }
1258
1259 #[test]
1260 fn test_pattern_redundant_quantifier() {
1261 let expr = TLExpr::exists(
1263 "x",
1264 "D",
1265 TLExpr::exists("x", "D", TLExpr::pred("P", vec![Term::var("x")])),
1266 );
1267
1268 let patterns = PatternAnalysis::from_expr(&expr);
1269 assert_eq!(patterns.redundant_quantifiers, 1);
1270 }
1271}