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 }
215}
216
217#[derive(Clone, Debug, PartialEq)]
219pub struct ComplexityMetrics {
220 pub max_depth: usize,
222 pub avg_depth: f64,
224 pub node_count: usize,
226 pub leaf_count: usize,
228 pub branching_factor: f64,
230 pub cyclomatic_complexity: usize,
232 pub quantifier_depth: usize,
234 pub modal_depth: usize,
236 pub temporal_depth: usize,
238}
239
240impl ComplexityMetrics {
241 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, 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 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 TLExpr::Pred { .. } | TLExpr::Constant(_) => {
302 self.leaf_count += 1;
303 *depth_sum += depth;
304 }
305
306 TLExpr::IfThenElse {
308 condition,
309 then_branch,
310 else_branch,
311 } => {
312 self.cyclomatic_complexity += 1; *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 TLExpr::And(l, r) | TLExpr::Or(l, r) => {
349 self.cyclomatic_complexity += 1; *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 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 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 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 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 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 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 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 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(); *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 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 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#[derive(Clone, Debug, PartialEq, Eq)]
654pub struct PatternAnalysis {
655 pub de_morgan_patterns: usize,
657 pub double_negation: usize,
659 pub modal_duality: usize,
661 pub temporal_duality: usize,
663 pub redundant_quantifiers: usize,
665 pub tautologies: usize,
667 pub contradictions: usize,
669}
670
671impl PatternAnalysis {
672 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 TLExpr::Not(e) => {
692 if matches!(**e, TLExpr::Not(_)) {
694 self.double_negation += 1;
695 }
696 if matches!(**e, TLExpr::And(_, _) | TLExpr::Or(_, _)) {
698 self.de_morgan_patterns += 1;
699 }
700 self.detect_recursive(e, _context);
701 }
702
703 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 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 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 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 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 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 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 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); assert_eq!(counts.logical, 1);
903 assert_eq!(counts.predicates, 2);
904 }
905
906 #[test]
907 fn test_operator_counts_modal() {
908 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 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 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 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 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); assert_eq!(metrics.node_count, 7); assert_eq!(metrics.leaf_count, 4);
968 assert_eq!(metrics.cyclomatic_complexity, 4); }
970
971 #[test]
972 fn test_complexity_quantifier_depth() {
973 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 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 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 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 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 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 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}