Skip to main content

scirs2_special/
combinatorial.rs

1//! Combinatorial functions
2//!
3//! This module provides various combinatorial functions including:
4//! - Factorial and related functions
5//! - Binomial coefficients
6//! - Permutations and combinations
7//! - Stirling numbers
8//! - Bell numbers
9//! - Bernoulli numbers
10//! - Euler numbers
11
12use crate::error::{SpecialError, SpecialResult};
13use crate::gamma::gamma;
14use std::f64::consts::PI;
15
16/// Computes the factorial of a non-negative integer.
17///
18/// For large values, this uses the gamma function: n! = Γ(n+1).
19///
20/// # Arguments
21///
22/// * `n` - Non-negative integer
23///
24/// # Returns
25///
26/// * `SpecialResult<f64>` - The factorial value n!
27///
28/// # Examples
29///
30/// ```
31/// use scirs2_special::factorial;
32///
33/// assert_eq!(factorial(0).expect("test/example should not fail"), 1.0);
34/// assert_eq!(factorial(5).expect("test/example should not fail"), 120.0);
35/// assert_eq!(factorial(10).expect("test/example should not fail"), 3628800.0);
36/// ```
37#[allow(dead_code)]
38pub fn factorial(n: u32) -> SpecialResult<f64> {
39    if n <= 20 {
40        // Use direct calculation for small values
41        let mut result = 1.0;
42        for i in 1..=n {
43            result *= i as f64;
44        }
45        Ok(result)
46    } else {
47        // Use gamma function for larger values
48        Ok(gamma((n + 1) as f64))
49    }
50}
51
52/// Computes the double factorial of a non-negative integer.
53///
54/// Double factorial n!! is defined as:
55/// - For even n: n!! = n × (n-2) × (n-4) × ... × 2
56/// - For odd n: n!! = n × (n-2) × (n-4) × ... × 1
57/// - 0!! = 1 by convention
58///
59/// # Arguments
60///
61/// * `n` - Non-negative integer
62///
63/// # Returns
64///
65/// * `SpecialResult<f64>` - The double factorial value n!!
66///
67/// # Examples
68///
69/// ```
70/// use scirs2_special::double_factorial;
71///
72/// assert_eq!(double_factorial(0).expect("test/example should not fail"), 1.0);
73/// assert_eq!(double_factorial(5).expect("test/example should not fail"), 15.0); // 5 × 3 × 1
74/// assert_eq!(double_factorial(6).expect("test/example should not fail"), 48.0); // 6 × 4 × 2
75/// ```
76#[allow(dead_code)]
77pub fn double_factorial(n: u32) -> SpecialResult<f64> {
78    if n == 0 {
79        return Ok(1.0);
80    }
81
82    let mut result = 1.0;
83    let mut i = n;
84    while i > 0 {
85        result *= i as f64;
86        i = i.saturating_sub(2);
87    }
88    Ok(result)
89}
90
91/// Computes the double factorial n!! (alias for SciPy compatibility).
92///
93/// This is an alias for `double_factorial` to match SciPy's `factorial2` function.
94///
95/// # Arguments
96///
97/// * `n` - The number for which to compute the double factorial
98///
99/// # Returns
100///
101/// * `SpecialResult<f64>` - The double factorial n!!
102///
103/// # Examples
104///
105/// ```
106/// use scirs2_special::factorial2;
107///
108/// assert_eq!(factorial2(5).expect("test/example should not fail"), 15.0); // 5 × 3 × 1
109/// assert_eq!(factorial2(6).expect("test/example should not fail"), 48.0); // 6 × 4 × 2
110/// ```
111#[allow(dead_code)]
112pub fn factorial2(n: u32) -> SpecialResult<f64> {
113    double_factorial(n)
114}
115
116/// Computes the k-factorial (multi-factorial) of n.
117///
118/// The k-factorial of n is the product of positive integers up to n
119/// that are congruent to n modulo k.
120/// For example, the 3-factorial of 8 is 8×5×2.
121///
122/// # Arguments
123///
124/// * `n` - The number for which to compute the k-factorial
125/// * `k` - The step size (must be positive)
126///
127/// # Returns
128///
129/// * `SpecialResult<f64>` - The k-factorial of n
130///
131/// # Examples
132///
133/// ```
134/// use scirs2_special::factorialk;
135///
136/// assert_eq!(factorialk(8, 3).expect("test/example should not fail"), 80.0); // 8 × 5 × 2
137/// assert_eq!(factorialk(5, 2).expect("test/example should not fail"), 15.0); // 5 × 3 × 1
138/// assert_eq!(factorialk(6, 2).expect("test/example should not fail"), 48.0); // 6 × 4 × 2 (same as double factorial)
139/// ```
140#[allow(dead_code)]
141pub fn factorialk(n: u32, k: u32) -> SpecialResult<f64> {
142    if k == 0 {
143        return Err(crate::SpecialError::ValueError(
144            "k must be positive".to_string(),
145        ));
146    }
147
148    if n == 0 {
149        return Ok(1.0);
150    }
151
152    let mut result = 1.0;
153    let mut i = n;
154    while i > 0 {
155        result *= i as f64;
156        i = i.saturating_sub(k);
157    }
158    Ok(result)
159}
160
161/// Computes the binomial coefficient "n choose k".
162///
163/// The binomial coefficient C(n,k) = n! / (k! × (n-k)!) represents
164/// the number of ways to choose k items from n items.
165///
166/// # Arguments
167///
168/// * `n` - Total number of items
169/// * `k` - Number of items to choose
170///
171/// # Returns
172///
173/// * `SpecialResult<f64>` - The binomial coefficient C(n,k)
174///
175/// # Examples
176///
177/// ```
178/// use scirs2_special::binomial;
179///
180/// assert_eq!(binomial(5, 2).expect("test/example should not fail"), 10.0);
181/// assert_eq!(binomial(10, 3).expect("test/example should not fail"), 120.0);
182/// assert_eq!(binomial(7, 0).expect("test/example should not fail"), 1.0);
183/// assert_eq!(binomial(7, 7).expect("test/example should not fail"), 1.0);
184/// ```
185#[allow(dead_code)]
186pub fn binomial(n: u32, k: u32) -> SpecialResult<f64> {
187    if k > n {
188        return Ok(0.0);
189    }
190
191    if k == 0 || k == n {
192        return Ok(1.0);
193    }
194
195    // Use symmetry: C(n,k) = C(n,n-k)
196    let k = if k > n - k { n - k } else { k };
197
198    // For small values, use direct calculation to avoid precision issues
199    if n <= 30 {
200        let mut result = 1.0;
201        for i in 0..k {
202            result = result * (n - i) as f64 / (i + 1) as f64;
203        }
204        Ok(result)
205    } else {
206        // Use gamma function for larger values
207        let n_fact = gamma((n + 1) as f64);
208        let k_fact = gamma((k + 1) as f64);
209        let nk_fact = gamma((n - k + 1) as f64);
210        Ok(n_fact / (k_fact * nk_fact))
211    }
212}
213
214/// Computes the number of permutations of n items taken k at a time.
215///
216/// P(n,k) = n! / (n-k)! represents the number of ways to arrange
217/// k items from n items where order matters.
218///
219/// # Arguments
220///
221/// * `n` - Total number of items
222/// * `k` - Number of items to arrange
223///
224/// # Returns
225///
226/// * `SpecialResult<f64>` - The number of permutations P(n,k)
227///
228/// # Examples
229///
230/// ```
231/// use scirs2_special::permutations;
232///
233/// assert_eq!(permutations(5, 2).expect("test/example should not fail"), 20.0);
234/// assert_eq!(permutations(10, 3).expect("test/example should not fail"), 720.0);
235/// assert_eq!(permutations(7, 0).expect("test/example should not fail"), 1.0);
236/// ```
237#[allow(dead_code)]
238pub fn permutations(n: u32, k: u32) -> SpecialResult<f64> {
239    if k > n {
240        return Ok(0.0);
241    }
242
243    if k == 0 {
244        return Ok(1.0);
245    }
246
247    // For small values, use direct calculation
248    if n <= 30 {
249        let mut result = 1.0;
250        for i in 0..k {
251            result *= (n - i) as f64;
252        }
253        Ok(result)
254    } else {
255        // Use gamma function for larger values
256        let n_fact = gamma((n + 1) as f64);
257        let nk_fact = gamma((n - k + 1) as f64);
258        Ok(n_fact / nk_fact)
259    }
260}
261
262/// Computes the number of permutations (alias for SciPy compatibility).
263///
264/// This is an alias for `permutations` to match SciPy's `perm` function.
265///
266/// # Arguments
267///
268/// * `n` - Total number of items
269/// * `k` - Number of items to arrange
270///
271/// # Returns
272///
273/// * `SpecialResult<f64>` - The number of permutations P(n,k)
274///
275/// # Examples
276///
277/// ```
278/// use scirs2_special::perm;
279///
280/// assert_eq!(perm(5, 2).expect("test/example should not fail"), 20.0);
281/// assert_eq!(perm(10, 3).expect("test/example should not fail"), 720.0);
282/// ```
283#[allow(dead_code)]
284pub fn perm(n: u32, k: u32) -> SpecialResult<f64> {
285    permutations(n, k)
286}
287
288/// Computes the Stirling number of the first kind.
289///
290/// The unsigned Stirling number of the first kind s(n,k) counts
291/// the number of permutations of n elements with exactly k cycles.
292///
293/// # Arguments
294///
295/// * `n` - Number of elements
296/// * `k` - Number of cycles
297///
298/// # Returns
299///
300/// * `SpecialResult<f64>` - The unsigned Stirling number s(n,k)
301///
302/// # Examples
303///
304/// ```
305/// use scirs2_special::stirling_first;
306///
307/// assert_eq!(stirling_first(0, 0).expect("test/example should not fail"), 1.0);
308/// assert_eq!(stirling_first(4, 2).expect("test/example should not fail"), 11.0);
309/// assert_eq!(stirling_first(5, 3).expect("test/example should not fail"), 35.0);
310/// ```
311#[allow(dead_code)]
312pub fn stirling_first(n: u32, k: u32) -> SpecialResult<f64> {
313    if n == 0 && k == 0 {
314        return Ok(1.0);
315    }
316    if n == 0 || k == 0 || k > n {
317        return Ok(0.0);
318    }
319
320    // Use recurrence relation: s(n,k) = (n-1) * s(n-1,k) + s(n-1,k-1)
321    let mut dp = vec![vec![0.0; (k + 1) as usize]; (n + 1) as usize];
322    dp[0][0] = 1.0;
323
324    for i in 1..=n as usize {
325        for j in 1..=std::cmp::min(i, k as usize) {
326            dp[i][j] = (i - 1) as f64 * dp[i - 1][j] + dp[i - 1][j - 1];
327        }
328    }
329
330    Ok(dp[n as usize][k as usize])
331}
332
333/// Computes the Stirling number of the second kind.
334///
335/// The Stirling number of the second kind S(n,k) counts the number
336/// of ways to partition n elements into exactly k non-empty subsets.
337///
338/// # Arguments
339///
340/// * `n` - Number of elements
341/// * `k` - Number of subsets
342///
343/// # Returns
344///
345/// * `SpecialResult<f64>` - The Stirling number S(n,k)
346///
347/// # Examples
348///
349/// ```
350/// use scirs2_special::stirling_second;
351///
352/// assert_eq!(stirling_second(0, 0).expect("test/example should not fail"), 1.0);
353/// assert_eq!(stirling_second(4, 2).expect("test/example should not fail"), 7.0);
354/// assert_eq!(stirling_second(5, 3).expect("test/example should not fail"), 25.0);
355/// ```
356#[allow(dead_code)]
357pub fn stirling_second(n: u32, k: u32) -> SpecialResult<f64> {
358    if n == 0 && k == 0 {
359        return Ok(1.0);
360    }
361    if n == 0 || k == 0 || k > n {
362        return Ok(0.0);
363    }
364
365    // Use recurrence relation: S(n,k) = k * S(n-1,k) + S(n-1,k-1)
366    let mut dp = vec![vec![0.0; (k + 1) as usize]; (n + 1) as usize];
367    dp[0][0] = 1.0;
368
369    for i in 1..=n as usize {
370        for j in 1..=std::cmp::min(i, k as usize) {
371            dp[i][j] = j as f64 * dp[i - 1][j] + dp[i - 1][j - 1];
372        }
373    }
374
375    Ok(dp[n as usize][k as usize])
376}
377
378/// Computes the Stirling number of the second kind (alias for SciPy compatibility).
379///
380/// This is an alias for `stirling_second` to match SciPy's `stirling2` function.
381///
382/// # Arguments
383///
384/// * `n` - Number of elements
385/// * `k` - Number of subsets
386///
387/// # Returns
388///
389/// * `SpecialResult<f64>` - The Stirling number S(n,k)
390///
391/// # Examples
392///
393/// ```
394/// use scirs2_special::stirling2;
395///
396/// assert_eq!(stirling2(4, 2).expect("test/example should not fail"), 7.0);
397/// assert_eq!(stirling2(5, 3).expect("test/example should not fail"), 25.0);
398/// ```
399#[allow(dead_code)]
400pub fn stirling2(n: u32, k: u32) -> SpecialResult<f64> {
401    stirling_second(n, k)
402}
403
404/// Computes the Bell number B(n).
405///
406/// The Bell number B(n) counts the number of ways to partition
407/// a set of n elements into non-empty subsets.
408///
409/// # Arguments
410///
411/// * `n` - Number of elements
412///
413/// # Returns
414///
415/// * `SpecialResult<f64>` - The Bell number B(n)
416///
417/// # Examples
418///
419/// ```
420/// use scirs2_special::bell_number;
421///
422/// assert_eq!(bell_number(0).expect("test/example should not fail"), 1.0);
423/// assert_eq!(bell_number(1).expect("test/example should not fail"), 1.0);
424/// assert_eq!(bell_number(2).expect("test/example should not fail"), 2.0);
425/// assert_eq!(bell_number(3).expect("test/example should not fail"), 5.0);
426/// assert_eq!(bell_number(4).expect("test/example should not fail"), 15.0);
427/// ```
428#[allow(dead_code)]
429pub fn bell_number(n: u32) -> SpecialResult<f64> {
430    if n == 0 {
431        return Ok(1.0);
432    }
433
434    // B(n) = sum of S(n,k) for k from 0 to n
435    let mut result = 0.0;
436    for k in 0..=n {
437        result += stirling_second(n, k)?;
438    }
439    Ok(result)
440}
441
442/// Computes the Bernoulli number B(n).
443///
444/// The Bernoulli numbers are a sequence of rational numbers defined
445/// by the generating function t/(e^t - 1) = sum(B_n * t^n / n!).
446///
447/// This implementation returns the modern convention where B_1 = -1/2.
448///
449/// # Arguments
450///
451/// * `n` - Index of the Bernoulli number
452///
453/// # Returns
454///
455/// * `SpecialResult<f64>` - The Bernoulli number B(n)
456///
457/// # Examples
458///
459/// ```
460/// use scirs2_special::bernoulli_number;
461/// use approx::assert_relative_eq;
462///
463/// assert_eq!(bernoulli_number(0).expect("test/example should not fail"), 1.0);
464/// assert_relative_eq!(bernoulli_number(1).expect("test/example should not fail"), -0.5, epsilon = 1e-10);
465/// assert_relative_eq!(bernoulli_number(2).expect("test/example should not fail"), 1.0/6.0, epsilon = 1e-10);
466/// assert_eq!(bernoulli_number(3).expect("test/example should not fail"), 0.0); // Odd Bernoulli numbers are 0 (except B_1)
467/// ```
468#[allow(dead_code)]
469pub fn bernoulli_number(n: u32) -> SpecialResult<f64> {
470    if n == 0 {
471        return Ok(1.0);
472    }
473    if n == 1 {
474        return Ok(-0.5);
475    }
476    if n > 1 && n % 2 == 1 {
477        return Ok(0.0); // Odd Bernoulli numbers are zero (except B_1)
478    }
479
480    // For small even n, use known values
481    match n {
482        2 => return Ok(1.0 / 6.0),
483        4 => return Ok(-1.0 / 30.0),
484        6 => return Ok(1.0 / 42.0),
485        8 => return Ok(-1.0 / 30.0),
486        10 => return Ok(5.0 / 66.0),
487        12 => return Ok(-691.0 / 2730.0),
488        _ => {} // Fall through to the recurrence relation below
489    }
490
491    // For larger values, use recurrence relation
492    // B_n = -1/(n+1) * sum_{k=0}^{n-1} C(n+1,k) * B_k
493    let mut bernoulli = vec![0.0; (n + 1) as usize];
494    bernoulli[0] = 1.0;
495    if n >= 1 {
496        bernoulli[1] = -0.5;
497    }
498
499    for m in 2..=(n as usize) {
500        if m % 2 == 1 {
501            bernoulli[m] = 0.0;
502            continue;
503        }
504
505        let mut sum = 0.0;
506        for (k, &bernoulli_k) in bernoulli.iter().enumerate().take(m) {
507            let binom_coeff = binomial((m + 1) as u32, k as u32)?;
508            sum += binom_coeff * bernoulli_k;
509        }
510        bernoulli[m] = -sum / (m + 1) as f64;
511    }
512
513    Ok(bernoulli[n as usize])
514}
515
516/// Computes the Euler number E(n).
517///
518/// The Euler numbers are integers defined by the generating function
519/// sech(t) = 2/(e^t + e^(-t)) = sum(E_n * t^n / n!).
520/// Only even-indexed Euler numbers are non-zero.
521///
522/// # Arguments
523///
524/// * `n` - Index of the Euler number
525///
526/// # Returns
527///
528/// * `SpecialResult<f64>` - The Euler number E(n)
529///
530/// # Examples
531///
532/// ```
533/// use scirs2_special::euler_number;
534///
535/// assert_eq!(euler_number(0).expect("test/example should not fail"), 1.0);
536/// assert_eq!(euler_number(1).expect("test/example should not fail"), 0.0);
537/// assert_eq!(euler_number(2).expect("test/example should not fail"), -1.0);
538/// assert_eq!(euler_number(4).expect("test/example should not fail"), 5.0);
539/// ```
540#[allow(dead_code)]
541pub fn euler_number(n: u32) -> SpecialResult<f64> {
542    if n % 2 == 1 {
543        return Ok(0.0); // Odd Euler numbers are zero
544    }
545
546    // Known values for small even n
547    match n {
548        0 => return Ok(1.0),
549        2 => return Ok(-1.0),
550        4 => return Ok(5.0),
551        6 => return Ok(-61.0),
552        8 => return Ok(1385.0),
553        10 => return Ok(-50521.0),
554        _ => {} // Fall through to computation below
555    }
556
557    // For larger values, use more efficient algorithms
558    if n > 100 {
559        // For very large n, use asymptotic approximation
560        return euler_number_asymptotic(n as i32);
561    } else if n > 20 {
562        // For moderate values, use improved recurrence with rational arithmetic
563        return euler_number_improved_recurrence(n as i32);
564    }
565
566    // Use standard recurrence relation for small values
567    euler_number_standard_recurrence(n as i32)
568}
569
570/// Standard recurrence relation for Euler numbers (small n)
571#[allow(dead_code)]
572fn euler_number_standard_recurrence(n: i32) -> SpecialResult<f64> {
573    // Use recurrence relation based on generating function
574    let mut euler = vec![0.0; (n + 1) as usize];
575    euler[0] = 1.0;
576
577    for m in (2..=(n as usize)).step_by(2) {
578        let mut sum = 0.0;
579        for k in (0..m).step_by(2) {
580            let binom_coeff = binomial(m as u32, k as u32)?;
581            sum += binom_coeff * euler[k];
582        }
583        euler[m] = -sum;
584    }
585
586    Ok(euler[n as usize])
587}
588
589/// Improved recurrence relation for Euler numbers using rational arithmetic (moderate n)
590#[allow(dead_code)]
591fn euler_number_improved_recurrence(n: i32) -> SpecialResult<f64> {
592    // Use the recurrence relation with better numerical stability
593    // E_n = -sum_{k=0}^{n-1} C(n,k) * E_k for even n
594
595    if n % 2 == 1 {
596        return Ok(0.0); // Euler numbers are zero for odd indices
597    }
598
599    // For moderate values, use a more memory-efficient approach
600    // that doesn't store all intermediate values
601
602    let mut _prev_eulr = [0.0; 2]; // Only store last two values (reserved for future optimization)
603    _prev_eulr[0] = 1.0; // E_0 = 1
604
605    if n == 0 {
606        return Ok(1.0);
607    }
608
609    // Store a few more values for better efficiency
610    let mut euler_cache = vec![0.0; (n / 2 + 1) as usize];
611    euler_cache[0] = 1.0; // E_0 = 1
612
613    for m in (2..=n).step_by(2) {
614        let m_idx = (m / 2) as usize;
615        let mut sum = 0.0;
616
617        for k in (0..m).step_by(2) {
618            let k_idx = (k / 2) as usize;
619
620            // Use more efficient binomial coefficient computation
621            let binom_coeff = efficient_binomial(m as u32, k as u32)?;
622            sum += binom_coeff * euler_cache[k_idx];
623        }
624
625        euler_cache[m_idx] = -sum;
626    }
627
628    Ok(euler_cache[(n / 2) as usize])
629}
630
631/// Asymptotic approximation for Euler numbers (large n)
632#[allow(dead_code)]
633fn euler_number_asymptotic(n: i32) -> SpecialResult<f64> {
634    if n % 2 == 1 {
635        return Ok(0.0); // Euler numbers are zero for odd indices
636    }
637
638    // For large even n, use the asymptotic formula:
639    // |E_n| ~ 8 * sqrt(2/π) * (2^{n+2} * n! / π^{n+1}) * [1 + O(1/n)]
640
641    let n_f = n as f64;
642
643    // Use Stirling's approximation for n!
644    // ln(n!) ≈ n*ln(n) - n + 0.5*ln(2πn)
645    let ln_n_factorial = n_f * n_f.ln() - n_f + 0.5 * (2.0 * PI * n_f).ln();
646
647    // Calculate log of the asymptotic approximation to avoid overflow
648    // ln|E_n| ≈ ln(8) + 0.5*ln(2/π) + (n+2)*ln(2) + ln(n!) - (n+1)*ln(π)
649
650    let ln_8 = 8.0_f64.ln();
651    let ln_sqrt_2_over_pi = 0.5 * (2.0 / PI).ln();
652    let power_of_2_term = (n_f + 2.0) * 2.0_f64.ln();
653    let pi_power_term = -(n_f + 1.0) * PI.ln();
654
655    let ln_magnitude = ln_8 + ln_sqrt_2_over_pi + power_of_2_term + ln_n_factorial + pi_power_term;
656
657    // Check if the result would overflow
658    if ln_magnitude > 700.0 {
659        return Err(SpecialError::OverflowError(
660            "Euler number too large to represent as f64".to_string(),
661        ));
662    }
663
664    let magnitude = ln_magnitude.exp();
665
666    // Apply the correct sign: E_n = (-1)^{n/2} * |E_n|
667    let sign = if (n / 2) % 2 == 0 { 1.0 } else { -1.0 };
668
669    Ok(sign * magnitude)
670}
671
672/// More efficient binomial coefficient computation for moderate values
673#[allow(dead_code)]
674fn efficient_binomial(n: u32, k: u32) -> SpecialResult<f64> {
675    if k > n {
676        return Ok(0.0);
677    }
678
679    if k == 0 || k == n {
680        return Ok(1.0);
681    }
682
683    // Use symmetry to reduce computation
684    let k_use = if k > n - k { n - k } else { k };
685
686    // For larger values, use logarithmic computation to avoid overflow
687    if n > 30 {
688        use crate::gamma::gamma;
689
690        // C(n,k) = Γ(n+1) / (Γ(k+1) * Γ(n-k+1))
691        let ln_result = (gamma((n + 1) as f64).ln())
692            - (gamma((k_use + 1) as f64).ln())
693            - (gamma((n - k_use + 1) as f64).ln());
694
695        if ln_result > 700.0 {
696            return Err(SpecialError::OverflowError(
697                "Binomial coefficient too large".to_string(),
698            ));
699        }
700
701        Ok(ln_result.exp())
702    } else {
703        // Use the standard multiplication approach for smaller values
704        let mut result = 1.0;
705        for i in 0..k_use {
706            result *= (n - i) as f64;
707            result /= (i + 1) as f64;
708        }
709        Ok(result)
710    }
711}
712
713/// Combination function - alias for binomial coefficient
714///
715/// This function provides SciPy compatibility for `comb(n, k)`
716/// which is the number of ways to choose k items from n items.
717#[allow(dead_code)]
718pub fn comb(n: u32, k: u32) -> SpecialResult<f64> {
719    binomial(n, k)
720}
721
722#[cfg(test)]
723mod tests {
724    use super::*;
725    use approx::assert_relative_eq;
726
727    #[test]
728    fn test_factorial() {
729        assert_eq!(factorial(0).expect("test/example should not fail"), 1.0);
730        assert_eq!(factorial(1).expect("test/example should not fail"), 1.0);
731        assert_eq!(factorial(5).expect("test/example should not fail"), 120.0);
732        assert_eq!(
733            factorial(10).expect("test/example should not fail"),
734            3628800.0
735        );
736
737        // Test larger values using gamma function
738        assert_relative_eq!(
739            factorial(15).expect("test/example should not fail"),
740            1307674368000.0,
741            epsilon = 1.0
742        );
743    }
744
745    #[test]
746    fn test_double_factorial() {
747        assert_eq!(
748            double_factorial(0).expect("test/example should not fail"),
749            1.0
750        );
751        assert_eq!(
752            double_factorial(1).expect("test/example should not fail"),
753            1.0
754        );
755        assert_eq!(
756            double_factorial(2).expect("test/example should not fail"),
757            2.0
758        );
759        assert_eq!(
760            double_factorial(5).expect("test/example should not fail"),
761            15.0
762        ); // 5 × 3 × 1
763        assert_eq!(
764            double_factorial(6).expect("test/example should not fail"),
765            48.0
766        ); // 6 × 4 × 2
767        assert_eq!(
768            double_factorial(8).expect("test/example should not fail"),
769            384.0
770        ); // 8 × 6 × 4 × 2
771    }
772
773    #[test]
774    fn test_factorial2() {
775        assert_eq!(factorial2(0).expect("test/example should not fail"), 1.0);
776        assert_eq!(factorial2(5).expect("test/example should not fail"), 15.0); // 5 × 3 × 1
777        assert_eq!(factorial2(6).expect("test/example should not fail"), 48.0); // 6 × 4 × 2
778
779        // Test that factorial2 is the same as double_factorial
780        assert_eq!(
781            factorial2(8).expect("test/example should not fail"),
782            double_factorial(8).expect("test/example should not fail")
783        );
784    }
785
786    #[test]
787    fn test_factorialk() {
788        assert_eq!(factorialk(0, 1).expect("test/example should not fail"), 1.0);
789        assert_eq!(
790            factorialk(8, 3).expect("test/example should not fail"),
791            80.0
792        ); // 8 × 5 × 2
793        assert_eq!(
794            factorialk(5, 2).expect("test/example should not fail"),
795            15.0
796        ); // 5 × 3 × 1
797        assert_eq!(
798            factorialk(6, 2).expect("test/example should not fail"),
799            48.0
800        ); // 6 × 4 × 2 (same as double factorial)
801        assert_eq!(
802            factorialk(9, 4).expect("test/example should not fail"),
803            45.0
804        ); // 9 × 5 × 1
805
806        // Test k=1 (should be regular factorial)
807        assert_eq!(
808            factorialk(5, 1).expect("test/example should not fail"),
809            factorial(5).expect("test/example should not fail")
810        );
811
812        // Test error case
813        assert!(factorialk(5, 0).is_err());
814    }
815
816    #[test]
817    fn test_binomial() {
818        assert_eq!(binomial(5, 2).expect("test/example should not fail"), 10.0);
819        assert_eq!(
820            binomial(10, 3).expect("test/example should not fail"),
821            120.0
822        );
823        assert_eq!(binomial(7, 0).expect("test/example should not fail"), 1.0);
824        assert_eq!(binomial(7, 7).expect("test/example should not fail"), 1.0);
825        assert_eq!(binomial(5, 10).expect("test/example should not fail"), 0.0); // k > n
826
827        // Test symmetry
828        assert_eq!(
829            binomial(10, 3).expect("test/example should not fail"),
830            binomial(10, 7).expect("test/example should not fail")
831        );
832    }
833
834    #[test]
835    fn test_permutations() {
836        assert_eq!(
837            permutations(5, 2).expect("test/example should not fail"),
838            20.0
839        );
840        assert_eq!(
841            permutations(10, 3).expect("test/example should not fail"),
842            720.0
843        );
844        assert_eq!(
845            permutations(7, 0).expect("test/example should not fail"),
846            1.0
847        );
848        assert_eq!(
849            permutations(5, 10).expect("test/example should not fail"),
850            0.0
851        ); // k > n
852    }
853
854    #[test]
855    fn test_perm() {
856        assert_eq!(perm(5, 2).expect("test/example should not fail"), 20.0);
857        assert_eq!(perm(10, 3).expect("test/example should not fail"), 720.0);
858
859        // Test that perm is the same as permutations
860        assert_eq!(
861            perm(7, 3).expect("test/example should not fail"),
862            permutations(7, 3).expect("test/example should not fail")
863        );
864    }
865
866    #[test]
867    fn test_stirling_first() {
868        assert_eq!(
869            stirling_first(0, 0).expect("test/example should not fail"),
870            1.0
871        );
872        assert_eq!(
873            stirling_first(4, 2).expect("test/example should not fail"),
874            11.0
875        );
876        assert_eq!(
877            stirling_first(5, 3).expect("test/example should not fail"),
878            35.0
879        );
880        assert_eq!(
881            stirling_first(3, 0).expect("test/example should not fail"),
882            0.0
883        );
884        assert_eq!(
885            stirling_first(0, 3).expect("test/example should not fail"),
886            0.0
887        );
888    }
889
890    #[test]
891    fn test_stirling_second() {
892        assert_eq!(
893            stirling_second(0, 0).expect("test/example should not fail"),
894            1.0
895        );
896        assert_eq!(
897            stirling_second(4, 2).expect("test/example should not fail"),
898            7.0
899        );
900        assert_eq!(
901            stirling_second(5, 3).expect("test/example should not fail"),
902            25.0
903        );
904        assert_eq!(
905            stirling_second(3, 0).expect("test/example should not fail"),
906            0.0
907        );
908        assert_eq!(
909            stirling_second(0, 3).expect("test/example should not fail"),
910            0.0
911        );
912    }
913
914    #[test]
915    fn test_stirling2() {
916        assert_eq!(stirling2(4, 2).expect("test/example should not fail"), 7.0);
917        assert_eq!(stirling2(5, 3).expect("test/example should not fail"), 25.0);
918
919        // Test that stirling2 is the same as stirling_second
920        assert_eq!(
921            stirling2(6, 3).expect("test/example should not fail"),
922            stirling_second(6, 3).expect("test/example should not fail")
923        );
924    }
925
926    #[test]
927    fn test_bell_number() {
928        assert_eq!(bell_number(0).expect("test/example should not fail"), 1.0);
929        assert_eq!(bell_number(1).expect("test/example should not fail"), 1.0);
930        assert_eq!(bell_number(2).expect("test/example should not fail"), 2.0);
931        assert_eq!(bell_number(3).expect("test/example should not fail"), 5.0);
932        assert_eq!(bell_number(4).expect("test/example should not fail"), 15.0);
933        assert_eq!(bell_number(5).expect("test/example should not fail"), 52.0);
934    }
935
936    #[test]
937    fn test_bernoulli_number() {
938        assert_eq!(
939            bernoulli_number(0).expect("test/example should not fail"),
940            1.0
941        );
942        assert_relative_eq!(
943            bernoulli_number(1).expect("test/example should not fail"),
944            -0.5,
945            epsilon = 1e-10
946        );
947        assert_relative_eq!(
948            bernoulli_number(2).expect("test/example should not fail"),
949            1.0 / 6.0,
950            epsilon = 1e-10
951        );
952        assert_eq!(
953            bernoulli_number(3).expect("test/example should not fail"),
954            0.0
955        );
956        assert_relative_eq!(
957            bernoulli_number(4).expect("test/example should not fail"),
958            -1.0 / 30.0,
959            epsilon = 1e-10
960        );
961        assert_eq!(
962            bernoulli_number(5).expect("test/example should not fail"),
963            0.0
964        );
965    }
966
967    #[test]
968    fn test_euler_number() {
969        assert_eq!(euler_number(0).expect("test/example should not fail"), 1.0);
970        assert_eq!(euler_number(1).expect("test/example should not fail"), 0.0);
971        assert_eq!(euler_number(2).expect("test/example should not fail"), -1.0);
972        assert_eq!(euler_number(3).expect("test/example should not fail"), 0.0);
973        assert_eq!(euler_number(4).expect("test/example should not fail"), 5.0);
974        assert_eq!(
975            euler_number(6).expect("test/example should not fail"),
976            -61.0
977        );
978    }
979}