1use crate::core::Expression;
34use crate::educational::step_by_step::Step;
35use crate::functions::intelligence::StepGenerator;
36use crate::functions::properties::{
37 AsymptoticData, DifferentialEquation, FunctionProperties, RecurrenceRule, SpecialProperties,
38 SpecialValue,
39};
40
41pub struct SpecialIntelligence;
49
50impl SpecialIntelligence {
51 pub fn new() -> Self {
53 Self
54 }
55
56 pub fn get_all_properties(&self) -> Vec<(String, FunctionProperties)> {
61 vec![
62 ("gamma".to_owned(), Self::gamma_properties()),
63 ("beta".to_owned(), Self::beta_properties()),
64 ("digamma".to_owned(), Self::digamma_properties()),
65 ("polygamma".to_owned(), Self::polygamma_properties()),
66 ("bessel_j".to_owned(), Self::bessel_j_properties()),
67 ("bessel_y".to_owned(), Self::bessel_y_properties()),
68 ("zeta".to_owned(), Self::zeta_properties()),
69 ]
70 }
71
72 fn gamma_properties() -> FunctionProperties {
83 FunctionProperties::Special(Box::new(SpecialProperties {
84 has_derivative: true,
85 has_antiderivative: false,
86 antiderivative_rule: None,
87 recurrence_relations: vec![RecurrenceRule {
88 name: "Functional equation".to_owned(),
89 relation: r"\Gamma(z+1) = z \cdot \Gamma(z)".to_owned(),
90 coefficients: vec![],
91 }],
92 differential_equation: None,
93 special_values: vec![
94 SpecialValue {
95 input: "1".to_owned(),
96 output: Expression::integer(1),
97 latex_explanation: r"\Gamma(1) = 1".to_owned(),
98 },
99 SpecialValue {
100 input: "2".to_owned(),
101 output: Expression::integer(1),
102 latex_explanation: r"\Gamma(2) = 1".to_owned(),
103 },
104 SpecialValue {
105 input: "1/2".to_owned(),
106 output: Expression::mul(vec![Expression::sqrt(Expression::pi())]),
107 latex_explanation: r"\Gamma(1/2) = \sqrt{\pi}".to_owned(),
108 },
109 SpecialValue {
110 input: "3/2".to_owned(),
111 output: Expression::div(
112 Expression::sqrt(Expression::pi()),
113 Expression::integer(2),
114 ),
115 latex_explanation: r"\Gamma(3/2) = \frac{\sqrt{\pi}}{2}".to_owned(),
116 },
117 SpecialValue {
118 input: "5/2".to_owned(),
119 output: Expression::div(
120 Expression::mul(vec![
121 Expression::integer(3),
122 Expression::sqrt(Expression::pi()),
123 ]),
124 Expression::integer(4),
125 ),
126 latex_explanation: r"\Gamma(5/2) = \frac{3\sqrt{\pi}}{4}".to_owned(),
127 },
128 ],
129 asymptotic_behavior: Some(AsymptoticData {
130 as_x_to_infinity:
131 r"\Gamma(z) \sim \sqrt{2\pi/z} (z/e)^z \text{ (Stirling's approximation)}"
132 .to_owned(),
133 as_x_to_zero: r"\Gamma(z) \sim 1/z \text{ for } z \to 0^+".to_owned(),
134 leading_coefficient: Expression::integer(1),
135 }),
136 wolfram_name: Some("Gamma"),
137 }))
138 }
139
140 fn beta_properties() -> FunctionProperties {
155 FunctionProperties::Special(Box::new(SpecialProperties {
156 has_derivative: true,
157 has_antiderivative: false,
158 antiderivative_rule: None,
159 recurrence_relations: vec![RecurrenceRule {
160 name: "Symmetry".to_owned(),
161 relation: r"B(a,b) = B(b,a)".to_owned(),
162 coefficients: vec![],
163 }],
164 differential_equation: None,
165 special_values: vec![SpecialValue {
166 input: "1, 1".to_owned(),
167 output: Expression::integer(1),
168 latex_explanation: r"B(1,1) = 1".to_owned(),
169 }],
170 asymptotic_behavior: None,
171 wolfram_name: Some("Beta"),
172 }))
173 }
174
175 fn digamma_properties() -> FunctionProperties {
177 FunctionProperties::Special(Box::new(SpecialProperties {
178 has_derivative: true,
179 has_antiderivative: false,
180 antiderivative_rule: None,
181 recurrence_relations: vec![RecurrenceRule {
182 name: "Recurrence relation".to_owned(),
183 relation: r"\psi(z+1) = \psi(z) + \frac{1}{z}".to_owned(),
184 coefficients: vec![],
185 }],
186 differential_equation: None,
187 special_values: vec![],
188 asymptotic_behavior: None,
189 wolfram_name: None, }))
191 }
192
193 fn polygamma_properties() -> FunctionProperties {
195 FunctionProperties::Special(Box::new(SpecialProperties {
196 has_derivative: true,
197 has_antiderivative: false,
198 antiderivative_rule: None,
199 recurrence_relations: vec![],
200 differential_equation: None,
201 special_values: vec![],
202 asymptotic_behavior: None,
203 wolfram_name: None, }))
205 }
206
207 fn bessel_j_properties() -> FunctionProperties {
217 FunctionProperties::Special(Box::new(SpecialProperties {
218
219 has_derivative: true,
220 has_antiderivative: false,
221 antiderivative_rule: None,
222 recurrence_relations: vec![
223 RecurrenceRule {
224 name: "Sum relation".to_owned(),
225 relation: r"J_{n-1}(z) + J_{n+1}(z) = \frac{2n}{z}J_n(z)".to_owned(),
226 coefficients: vec![],
227 },
228 RecurrenceRule {
229 name: "Difference relation".to_owned(),
230 relation: r"J_{n-1}(z) - J_{n+1}(z) = 2J'_n(z)".to_owned(),
231 coefficients: vec![],
232 },
233 RecurrenceRule {
234 name: "Reflection formula".to_owned(),
235 relation: r"J_{-n}(z) = (-1)^n J_n(z)".to_owned(),
236 coefficients: vec![],
237 },
238 ],
239 differential_equation: Some(DifferentialEquation {
240 order: 2,
241 equation: r"z^2 y'' + z y' + (z^2 - n^2)y = 0".to_owned(),
242 coefficients: vec![],
243 }),
244 special_values: vec![
245 SpecialValue {
246 input: "0, 0".to_owned(),
247 output: Expression::integer(1),
248 latex_explanation: r"J_0(0) = 1".to_owned(),
249 },
250 SpecialValue {
251 input: "n, 0".to_owned(),
252 output: Expression::integer(0),
253 latex_explanation: r"J_n(0) = 0 \text{ for } n > 0".to_owned(),
254 },
255 ],
256 asymptotic_behavior: Some(AsymptoticData {
257 as_x_to_infinity: r"J_n(z) \sim \sqrt{\frac{2}{\pi z}} \cos\left(z - \frac{n\pi}{2} - \frac{\pi}{4}\right)".to_owned(),
258 as_x_to_zero: r"J_0(0) = 1, J_n(0) = 0 \text{ for } n > 0".to_owned(),
259 leading_coefficient: Expression::integer(1),
260 }),
261 wolfram_name: None, }))
263 }
264
265 fn bessel_y_properties() -> FunctionProperties {
274 FunctionProperties::Special(Box::new(SpecialProperties {
275
276 has_derivative: true,
277 has_antiderivative: false,
278 antiderivative_rule: None,
279 recurrence_relations: vec![
280 RecurrenceRule {
281 name: "Sum relation".to_owned(),
282 relation: r"Y_{n-1}(z) + Y_{n+1}(z) = \frac{2n}{z}Y_n(z)".to_owned(),
283 coefficients: vec![],
284 },
285 RecurrenceRule {
286 name: "Difference relation".to_owned(),
287 relation: r"Y_{n-1}(z) - Y_{n+1}(z) = 2Y'_n(z)".to_owned(),
288 coefficients: vec![],
289 },
290 RecurrenceRule {
291 name: "Reflection formula".to_owned(),
292 relation: r"Y_{-n}(z) = (-1)^n Y_n(z)".to_owned(),
293 coefficients: vec![],
294 },
295 ],
296 differential_equation: Some(DifferentialEquation {
297 order: 2,
298 equation: r"z^2 y'' + z y' + (z^2 - n^2)y = 0".to_owned(),
299 coefficients: vec![],
300 }),
301 special_values: vec![SpecialValue {
302 input: "n, 0".to_owned(),
303 output: Expression::integer(-1),
304 latex_explanation: r"Y_n(0) = -\infty \text{ (logarithmic singularity)}".to_owned(),
305 }],
306 asymptotic_behavior: Some(AsymptoticData {
307 as_x_to_infinity: r"Y_n(z) \sim \sqrt{\frac{2}{\pi z}} \sin\left(z - \frac{n\pi}{2} - \frac{\pi}{4}\right)".to_owned(),
308 as_x_to_zero: r"Y_n(0) = -\infty \text{ (logarithmic singularity)}".to_owned(),
309 leading_coefficient: Expression::integer(1),
310 }),
311 wolfram_name: None, }))
313 }
314
315 fn zeta_properties() -> FunctionProperties {
329 FunctionProperties::Special(Box::new(SpecialProperties {
330
331 has_derivative: true,
332 has_antiderivative: false,
333 antiderivative_rule: None,
334 recurrence_relations: vec![RecurrenceRule {
335 name: "Functional equation".to_owned(),
336 relation: r"\zeta(s) = 2^s \pi^{s-1} \sin\left(\frac{\pi s}{2}\right) \Gamma(1-s) \zeta(1-s)".to_owned(),
337 coefficients: vec![],
338 }],
339 differential_equation: None,
340 special_values: vec![
341 SpecialValue {
342 input: "0".to_owned(),
343 output: Expression::rational(-1, 2),
344 latex_explanation: r"\zeta(0) = -\frac{1}{2}".to_owned(),
345 },
346 SpecialValue {
347 input: "-1".to_owned(),
348 output: Expression::rational(-1, 12),
349 latex_explanation: r"\zeta(-1) = -\frac{1}{12}".to_owned(),
350 },
351 SpecialValue {
352 input: "2".to_owned(),
353 output: Expression::div(
354 Expression::pow(Expression::pi(), Expression::integer(2)),
355 Expression::integer(6),
356 ),
357 latex_explanation: r"\zeta(2) = \frac{\pi^2}{6}".to_owned(),
358 },
359 SpecialValue {
360 input: "4".to_owned(),
361 output: Expression::div(
362 Expression::pow(Expression::pi(), Expression::integer(4)),
363 Expression::integer(90),
364 ),
365 latex_explanation: r"\zeta(4) = \frac{\pi^4}{90}".to_owned(),
366 },
367 SpecialValue {
368 input: "6".to_owned(),
369 output: Expression::div(
370 Expression::pow(Expression::pi(), Expression::integer(6)),
371 Expression::integer(945),
372 ),
373 latex_explanation: r"\zeta(6) = \frac{\pi^6}{945}".to_owned(),
374 },
375 SpecialValue {
376 input: "8".to_owned(),
377 output: Expression::div(
378 Expression::pow(Expression::pi(), Expression::integer(8)),
379 Expression::integer(9450),
380 ),
381 latex_explanation: r"\zeta(8) = \frac{\pi^8}{9450}".to_owned(),
382 },
383 SpecialValue {
384 input: "10".to_owned(),
385 output: Expression::div(
386 Expression::pow(Expression::pi(), Expression::integer(10)),
387 Expression::integer(93555),
388 ),
389 latex_explanation: r"\zeta(10) = \frac{\pi^{10}}{93555}".to_owned(),
390 },
391 SpecialValue {
392 input: "-5".to_owned(),
393 output: Expression::rational(-1, 252),
394 latex_explanation: r"\zeta(-5) = -\frac{1}{252}".to_owned(),
395 },
396 SpecialValue {
397 input: "-7".to_owned(),
398 output: Expression::rational(1, 240),
399 latex_explanation: r"\zeta(-7) = \frac{1}{240}".to_owned(),
400 },
401 ],
402 asymptotic_behavior: Some(AsymptoticData {
403 as_x_to_infinity: r"\zeta(s) \to 1 \text{ as } \text{Re}(s) \to \infty".to_owned(),
404 as_x_to_zero: r"\zeta(s) \text{ has pole at } s=1 \text{ with residue } 1".to_owned(),
405 leading_coefficient: Expression::integer(1),
406 }),
407 wolfram_name: Some("Zeta"),
408 }))
409 }
410}
411
412impl Default for SpecialIntelligence {
413 fn default() -> Self {
414 Self::new()
415 }
416}
417
418pub struct SpecialStepGenerator;
420
421impl StepGenerator for SpecialStepGenerator {
422 fn generate_steps(&self, name: &str, args: &[Expression]) -> Vec<Step> {
423 match name {
424 "gamma" => self.gamma_steps(args),
425 "beta" => self.beta_steps(args),
426 "digamma" => self.digamma_steps(args),
427 "polygamma" => self.polygamma_steps(args),
428 "bessel_j" => self.bessel_j_steps(args),
429 "bessel_y" => self.bessel_y_steps(args),
430 "zeta" => self.zeta_steps(args),
431 _ => vec![],
432 }
433 }
434
435 fn generate_latex_explanation(&self, name: &str, args: &[Expression]) -> String {
436 match name {
437 "gamma" => format!(
438 r"\Gamma({})",
439 args.first().map(|e| e.to_string()).unwrap_or_default()
440 ),
441 "beta" => format!(
442 r"B({}, {})",
443 args.first().map(|e| e.to_string()).unwrap_or_default(),
444 args.get(1).map(|e| e.to_string()).unwrap_or_default()
445 ),
446 "digamma" => format!(
447 r"\psi({})",
448 args.first().map(|e| e.to_string()).unwrap_or_default()
449 ),
450 "polygamma" => format!(
451 r"\psi^{{({})}}({})",
452 args.first().map(|e| e.to_string()).unwrap_or_default(),
453 args.get(1).map(|e| e.to_string()).unwrap_or_default()
454 ),
455 "bessel_j" => format!(
456 r"J_{{{}}}({})",
457 args.first().map(|e| e.to_string()).unwrap_or_default(),
458 args.get(1).map(|e| e.to_string()).unwrap_or_default()
459 ),
460 "bessel_y" => format!(
461 r"Y_{{{}}}({})",
462 args.first().map(|e| e.to_string()).unwrap_or_default(),
463 args.get(1).map(|e| e.to_string()).unwrap_or_default()
464 ),
465 "zeta" => format!(
466 r"\zeta({})",
467 args.first().map(|e| e.to_string()).unwrap_or_default()
468 ),
469 _ => String::new(),
470 }
471 }
472}
473
474impl SpecialStepGenerator {
475 fn gamma_steps(&self, args: &[Expression]) -> Vec<Step> {
476 if args.is_empty() {
477 return vec![];
478 }
479
480 vec![Step::new(
481 "Gamma Function",
482 format!("Evaluating Γ({})", args[0]),
483 )]
484 }
485
486 fn beta_steps(&self, args: &[Expression]) -> Vec<Step> {
487 if args.len() < 2 {
488 return vec![];
489 }
490
491 vec![Step::new(
492 "Beta Function",
493 format!("Evaluating B({}, {})", args[0], args[1]),
494 )]
495 }
496
497 fn digamma_steps(&self, args: &[Expression]) -> Vec<Step> {
498 if args.is_empty() {
499 return vec![];
500 }
501
502 vec![Step::new(
503 "Digamma Function",
504 format!("Evaluating ψ({})", args[0]),
505 )]
506 }
507
508 fn polygamma_steps(&self, args: &[Expression]) -> Vec<Step> {
509 if args.len() < 2 {
510 return vec![];
511 }
512
513 vec![Step::new(
514 "Polygamma Function",
515 format!("Evaluating ψ^({})({})", args[0], args[1]),
516 )]
517 }
518
519 fn bessel_j_steps(&self, args: &[Expression]) -> Vec<Step> {
520 if args.len() < 2 {
521 return vec![];
522 }
523
524 let mut steps = vec![Step::new(
525 "Bessel Function (First Kind)",
526 format!("Evaluating J_{{{}}}({})", args[0], args[1]),
527 )];
528
529 steps.push(Step::new(
530 "Mathematical Context",
531 "Bessel J functions solve: z²y'' + zy' + (z² - n²)y = 0".to_owned(),
532 ));
533
534 steps.push(Step::new(
535 "Properties",
536 "J functions are finite at origin; used in wave propagation and heat conduction"
537 .to_owned(),
538 ));
539
540 steps
541 }
542
543 fn bessel_y_steps(&self, args: &[Expression]) -> Vec<Step> {
544 if args.len() < 2 {
545 return vec![];
546 }
547
548 let mut steps = vec![Step::new(
549 "Bessel Function (Second Kind)",
550 format!("Evaluating Y_{{{}}}({})", args[0], args[1]),
551 )];
552
553 steps.push(Step::new(
554 "Mathematical Context",
555 "Bessel Y functions solve: z²y'' + zy' + (z² - n²)y = 0".to_owned(),
556 ));
557
558 steps.push(Step::new(
559 "Warning",
560 "Y functions have logarithmic singularity at z = 0".to_owned(),
561 ));
562
563 steps
564 }
565
566 fn zeta_steps(&self, args: &[Expression]) -> Vec<Step> {
567 if args.is_empty() {
568 return vec![];
569 }
570
571 let mut steps = vec![Step::new(
572 "Riemann Zeta Function",
573 format!("Evaluating ζ({})", args[0]),
574 )];
575
576 steps.push(Step::new(
577 "Definition",
578 "For Re(s) > 1: ζ(s) = Σ(n=1 to ∞) 1/n^s".to_owned(),
579 ));
580
581 steps.push(Step::new(
582 "Special Values",
583 "The Riemann zeta function has known values: ζ(2)=π²/6, ζ(4)=π⁴/90, ζ(0)=-1/2, ζ(-1)=-1/12".to_owned(),
584 ));
585
586 steps.push(Step::new(
587 "Pole at s=1",
588 "Note: ζ(s) has a simple pole at s=1 with residue 1".to_owned(),
589 ));
590
591 steps.push(Step::new(
592 "Functional Equation",
593 "ζ(s) = 2^s π^(s-1) sin(πs/2) Γ(1-s) ζ(1-s) extends ζ to entire complex plane"
594 .to_owned(),
595 ));
596
597 steps
598 }
599}
600
601#[cfg(test)]
602mod tests {
603 use super::*;
604
605 #[test]
606 fn test_gamma_properties() {
607 let props = SpecialIntelligence::gamma_properties();
608 match props {
609 FunctionProperties::Special(sp) => {
610 assert!(sp.has_derivative);
611 assert!(
612 sp.special_values.len() >= 5,
613 "Should have at least 5 special values including half-integers"
614 );
615 }
616 _ => panic!("Expected Special properties"),
617 }
618 }
619
620 #[test]
621 fn test_special_intelligence_get_all() {
622 let intelligence = SpecialIntelligence::new();
623 let all_props = intelligence.get_all_properties();
624 assert!(
625 all_props.len() >= 7,
626 "Should have at least 7 functions (gamma, beta, digamma, polygamma, bessel_j, bessel_y, zeta)"
627 );
628 }
629
630 #[test]
631 fn test_step_generator() {
632 let generator = SpecialStepGenerator;
633 let arg = Expression::integer(5);
634 let steps = generator.generate_steps("gamma", &[arg]);
635 assert!(!steps.is_empty());
636 }
637
638 #[test]
639 fn test_bessel_j_properties() {
640 let props = SpecialIntelligence::bessel_j_properties();
641 match props {
642 FunctionProperties::Special(sp) => {
643 assert!(sp.has_derivative);
644 assert!(
645 !sp.recurrence_relations.is_empty(),
646 "Should have recurrence relations"
647 );
648 assert!(
649 sp.differential_equation.is_some(),
650 "Should have differential equation"
651 );
652 assert!(!sp.special_values.is_empty(), "Should have special values");
653 assert!(
654 sp.asymptotic_behavior.is_some(),
655 "Should have asymptotic behavior"
656 );
657 }
658 _ => panic!("Expected Special properties"),
659 }
660 }
661
662 #[test]
663 fn test_bessel_y_properties() {
664 let props = SpecialIntelligence::bessel_y_properties();
665 match props {
666 FunctionProperties::Special(sp) => {
667 assert!(sp.has_derivative);
668 assert!(
669 !sp.recurrence_relations.is_empty(),
670 "Should have recurrence relations"
671 );
672 assert!(
673 sp.differential_equation.is_some(),
674 "Should have differential equation"
675 );
676 assert!(
677 sp.asymptotic_behavior.is_some(),
678 "Should have asymptotic behavior"
679 );
680 }
681 _ => panic!("Expected Special properties"),
682 }
683 }
684
685 #[test]
686 fn test_bessel_j_step_generation() {
687 let generator = SpecialStepGenerator;
688 let n = Expression::integer(0);
689 let x = Expression::integer(1);
690 let steps = generator.generate_steps("bessel_j", &[n, x]);
691 assert!(!steps.is_empty(), "Should generate steps");
692 assert!(
693 steps.len() >= 3,
694 "Should have at least 3 steps (evaluation, context, properties)"
695 );
696 }
697
698 #[test]
699 fn test_bessel_y_step_generation() {
700 let generator = SpecialStepGenerator;
701 let n = Expression::integer(0);
702 let x = Expression::integer(1);
703 let steps = generator.generate_steps("bessel_y", &[n, x]);
704 assert!(!steps.is_empty(), "Should generate steps");
705 assert!(
706 steps.len() >= 3,
707 "Should have at least 3 steps (evaluation, context, warning)"
708 );
709 }
710
711 #[test]
712 fn test_bessel_j_latex_explanation() {
713 let generator = SpecialStepGenerator;
714 let n = Expression::integer(2);
715 let x = Expression::integer(5);
716 let latex = generator.generate_latex_explanation("bessel_j", &[n, x]);
717 assert!(latex.contains("J_"), "Should contain J_");
718 assert!(latex.contains("2"), "Should contain order");
719 assert!(latex.contains("5"), "Should contain argument");
720 }
721
722 #[test]
723 fn test_bessel_y_latex_explanation() {
724 let generator = SpecialStepGenerator;
725 let n = Expression::integer(1);
726 let x = Expression::integer(3);
727 let latex = generator.generate_latex_explanation("bessel_y", &[n, x]);
728 assert!(latex.contains("Y_"), "Should contain Y_");
729 assert!(latex.contains("1"), "Should contain order");
730 assert!(latex.contains("3"), "Should contain argument");
731 }
732
733 #[test]
734 fn test_all_functions_have_properties() {
735 let intelligence = SpecialIntelligence::new();
736 let all_props = intelligence.get_all_properties();
737
738 let function_names: Vec<String> = all_props.iter().map(|(name, _)| name.clone()).collect();
739
740 assert!(
741 function_names.contains(&"bessel_j".to_string()),
742 "bessel_j should be registered"
743 );
744 assert!(
745 function_names.contains(&"bessel_y".to_string()),
746 "bessel_y should be registered"
747 );
748 assert!(
749 function_names.contains(&"zeta".to_string()),
750 "zeta should be registered"
751 );
752 }
753
754 #[test]
755 fn test_zeta_properties() {
756 let props = SpecialIntelligence::zeta_properties();
757 match props {
758 FunctionProperties::Special(sp) => {
759 assert!(sp.has_derivative, "Zeta should have derivative");
760 assert!(
761 sp.special_values.len() >= 9,
762 "Zeta should have at least 9 special values (including ζ(8), ζ(10), ζ(-5), ζ(-7))"
763 );
764 assert!(
765 sp.asymptotic_behavior.is_some(),
766 "Zeta should have asymptotic behavior"
767 );
768 assert!(
769 !sp.recurrence_relations.is_empty(),
770 "Zeta should have functional equation"
771 );
772 }
773 _ => panic!("Expected Special properties"),
774 }
775 }
776
777 #[test]
778 fn test_zeta_step_generation() {
779 let generator = SpecialStepGenerator;
780 let s = Expression::integer(2);
781 let steps = generator.generate_steps("zeta", &[s]);
782 assert!(!steps.is_empty(), "Should generate steps for zeta");
783 assert!(
784 steps.len() >= 4,
785 "Should have at least 4 steps (evaluation, definition, special values, pole)"
786 );
787 }
788
789 #[test]
790 fn test_zeta_latex_explanation() {
791 let generator = SpecialStepGenerator;
792 let s = Expression::integer(3);
793 let latex = generator.generate_latex_explanation("zeta", &[s]);
794 assert!(latex.contains(r"\zeta"), "Should contain zeta symbol");
795 assert!(latex.contains("3"), "Should contain argument");
796 }
797
798 #[test]
799 fn test_gamma_half_integer_special_values() {
800 let props = SpecialIntelligence::gamma_properties();
801 match props {
802 FunctionProperties::Special(sp) => {
803 let half_integer_values: Vec<_> = sp
804 .special_values
805 .iter()
806 .filter(|v| v.input.contains("/"))
807 .collect();
808 assert!(
809 half_integer_values.len() >= 3,
810 "Should have at least 3 half-integer special values"
811 );
812 }
813 _ => panic!("Expected Special properties"),
814 }
815 }
816
817 #[test]
818 fn test_beta_gamma_relationship_documented() {
819 let props = SpecialIntelligence::beta_properties();
820 match props {
821 FunctionProperties::Special(sp) => {
822 assert!(
823 !sp.recurrence_relations.is_empty(),
824 "Beta should document relationship with Gamma"
825 );
826 }
827 _ => panic!("Expected Special properties"),
828 }
829 }
830}