// Copyright © 2024 Mikhail Hogrefe
//
// This file is part of Malachite.
//
// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
// 3 of the License, or (at your option) any later version. See .
use malachite_base::num::arithmetic::factorial::checked_multifactorial_naive;
use malachite_base::num::arithmetic::traits::Parity;
use malachite_base::num::basic::unsigneds::PrimitiveUnsigned;
use malachite_base::test_util::generators::{
unsigned_gen, unsigned_gen_var_23, unsigned_gen_var_24, unsigned_gen_var_25,
unsigned_pair_gen_var_12, unsigned_pair_gen_var_43,
};
use malachite_base::test_util::num::arithmetic::factorial::{
checked_double_factorial_naive, checked_factorial_naive, checked_subfactorial_naive,
};
use std::panic::catch_unwind;
#[test]
fn test_factorial() {
fn test(n: u64, out: T) {
assert_eq!(T::factorial(n), out);
}
test::(0, 1);
test::(1, 1);
test::(2, 2);
test::(3, 6);
test::(4, 24);
test::(5, 120);
test::(10, 3628800);
}
fn factorial_fail_helper() {
assert_panic!(T::factorial(100));
}
#[test]
fn factorial_fail() {
apply_fn_to_unsigneds!(factorial_fail_helper);
}
#[test]
fn test_checked_factorial() {
fn test(n: u64, out: Option) {
assert_eq!(T::checked_factorial(n), out);
assert_eq!(checked_factorial_naive(n), out);
}
test::(0, Some(1));
test::(1, Some(1));
test::(2, Some(2));
test::(3, Some(6));
test::(4, Some(24));
test::(5, Some(120));
test::(10, Some(3628800));
test::(6, None);
test::(100, None);
}
#[test]
fn test_double_factorial() {
fn test(n: u64, out: T) {
assert_eq!(T::double_factorial(n), out);
}
test::(0, 1);
test::(1, 1);
test::(2, 2);
test::(3, 3);
test::(4, 8);
test::(5, 15);
test::(6, 48);
test::(7, 105);
test::(19, 654729075);
test::(20, 3715891200);
}
fn double_factorial_fail_helper() {
assert_panic!(T::double_factorial(100));
}
#[test]
fn double_factorial_fail() {
apply_fn_to_unsigneds!(double_factorial_fail_helper);
}
#[test]
fn test_checked_double_factorial() {
fn test(n: u64, out: Option) {
assert_eq!(T::checked_double_factorial(n), out);
assert_eq!(checked_double_factorial_naive(n), out);
}
test::(0, Some(1));
test::(1, Some(1));
test::(2, Some(2));
test::(3, Some(3));
test::(4, Some(8));
test::(5, Some(15));
test::(6, Some(48));
test::(7, Some(105));
test::(19, Some(654729075));
test::(20, Some(3715891200));
test::(8, None);
test::(100, None);
}
#[test]
fn test_multifactorial() {
fn test(n: u64, m: u64, out: T) {
assert_eq!(T::multifactorial(n, m), out);
}
test::(0, 1, 1);
test::(1, 1, 1);
test::(2, 1, 2);
test::(3, 1, 6);
test::(4, 1, 24);
test::(5, 1, 120);
test::(0, 2, 1);
test::(1, 2, 1);
test::(2, 2, 2);
test::(3, 2, 3);
test::(4, 2, 8);
test::(5, 2, 15);
test::(6, 2, 48);
test::(7, 2, 105);
test::(0, 3, 1);
test::(1, 3, 1);
test::(2, 3, 2);
test::(3, 3, 3);
test::(4, 3, 4);
test::(5, 3, 10);
test::(6, 3, 18);
test::(7, 3, 28);
test::(8, 3, 80);
test::(9, 3, 162);
test::(10, 1, 3628800);
test::(20, 2, 3715891200);
test::(25, 3, 608608000);
}
fn multifactorial_fail_helper() {
assert_panic!(T::multifactorial(1, 0));
assert_panic!(T::multifactorial(100, 1));
}
#[test]
fn multifactorial_fail() {
apply_fn_to_unsigneds!(multifactorial_fail_helper);
}
#[test]
fn test_checked_multifactorial() {
fn test(n: u64, m: u64, out: Option) {
assert_eq!(T::checked_multifactorial(n, m), out);
assert_eq!(checked_multifactorial_naive(n, m), out);
}
test::(0, 1, Some(1));
test::(1, 1, Some(1));
test::(2, 1, Some(2));
test::(3, 1, Some(6));
test::(4, 1, Some(24));
test::(5, 1, Some(120));
test::(0, 2, Some(1));
test::(1, 2, Some(1));
test::(2, 2, Some(2));
test::(3, 2, Some(3));
test::(4, 2, Some(8));
test::(5, 2, Some(15));
test::(6, 2, Some(48));
test::(7, 2, Some(105));
test::(0, 3, Some(1));
test::(1, 3, Some(1));
test::(2, 3, Some(2));
test::(3, 3, Some(3));
test::(4, 3, Some(4));
test::(5, 3, Some(10));
test::(6, 3, Some(18));
test::(7, 3, Some(28));
test::(8, 3, Some(80));
test::(9, 3, Some(162));
test::(10, 1, Some(3628800));
test::(20, 2, Some(3715891200));
test::(25, 3, Some(608608000));
test::(6, 1, None);
test::(8, 2, None);
test::(10, 3, None);
test::(100, 1, None);
test::(100, 2, None);
test::(100, 3, None);
}
fn checked_multifactorial_fail_helper() {
assert_panic!(T::checked_multifactorial(1, 0));
}
#[test]
fn checked_multifactorial_fail() {
apply_fn_to_unsigneds!(checked_multifactorial_fail_helper);
}
#[test]
fn test_subfactorial() {
fn test(n: u64, out: T) {
assert_eq!(T::subfactorial(n), out);
}
test::(0, 1);
test::(1, 0);
test::(2, 1);
test::(3, 2);
test::(4, 9);
test::(5, 44);
test::(10, 1334961);
}
fn subfactorial_fail_helper() {
assert_panic!(T::subfactorial(100));
}
#[test]
fn subfactorial_fail() {
apply_fn_to_unsigneds!(subfactorial_fail_helper);
}
#[test]
fn test_checked_subfactorial() {
fn test(n: u64, out: Option) {
assert_eq!(T::checked_subfactorial(n), out);
assert_eq!(checked_subfactorial_naive(n), out);
}
test::(0, Some(1));
test::(1, Some(0));
test::(2, Some(1));
test::(3, Some(2));
test::(4, Some(9));
test::(5, Some(44));
test::(10, Some(1334961));
test::(6, None);
test::(100, None);
}
fn factorial_properties_helper() {
unsigned_gen_var_23::().test_properties(|n| {
let f = T::factorial(n);
assert_eq!(T::checked_factorial(n), Some(f));
assert_eq!(T::multifactorial(n, 1), f);
assert_ne!(f, T::ZERO);
if n != 0 {
assert_eq!(f / T::factorial(n - 1), T::exact_from(n));
}
});
}
#[test]
fn factorial_properties() {
apply_fn_to_unsigneds!(factorial_properties_helper);
}
fn checked_factorial_properties_helper() {
unsigned_gen().test_properties(|n| {
let of = T::checked_factorial(n);
assert_eq!(checked_factorial_naive(n), of);
assert_eq!(T::checked_multifactorial(n, 1), of);
assert_ne!(of, Some(T::ZERO));
if let Some(f) = of {
assert_eq!(T::factorial(n), f);
}
if n != u64::MAX && of.is_none() {
assert!(T::checked_factorial(n + 1).is_none());
}
});
}
#[test]
fn checked_factorial_properties() {
apply_fn_to_unsigneds!(checked_factorial_properties_helper);
}
fn double_factorial_properties_helper() {
unsigned_gen_var_24::().test_properties(|n| {
let f = T::double_factorial(n);
assert_eq!(T::checked_double_factorial(n), Some(f));
assert_eq!(T::multifactorial(n, 2), f);
assert_ne!(f, T::ZERO);
if n > 1 {
assert_eq!(f / T::double_factorial(n - 2), T::exact_from(n));
}
});
}
#[test]
fn double_factorial_properties() {
apply_fn_to_unsigneds!(double_factorial_properties_helper);
}
fn checked_double_factorial_properties_helper() {
unsigned_gen().test_properties(|n| {
let of = T::checked_double_factorial(n);
assert_eq!(checked_double_factorial_naive(n), of);
assert_eq!(T::checked_multifactorial(n, 2), of);
assert_ne!(of, Some(T::ZERO));
if let Some(f) = of {
assert_eq!(T::double_factorial(n), f);
}
if n != u64::MAX && of.is_none() {
assert!(T::checked_double_factorial(n + 1).is_none());
}
});
}
#[test]
fn checked_double_factorial_properties() {
apply_fn_to_unsigneds!(checked_double_factorial_properties_helper);
}
fn multifactorial_properties_helper() {
unsigned_pair_gen_var_43::().test_properties(|(n, m)| {
let f = T::multifactorial(n, m);
assert_eq!(T::checked_multifactorial(n, m), Some(f));
assert_ne!(f, T::ZERO);
if n >= m {
assert_eq!(f / T::multifactorial(n - m, m), T::exact_from(n));
}
});
}
#[test]
fn multifactorial_properties() {
apply_fn_to_unsigneds!(multifactorial_properties_helper);
}
fn checked_multifactorial_properties_helper() {
unsigned_pair_gen_var_12::().test_properties(|(n, m)| {
let of = T::checked_multifactorial(n, m);
assert_eq!(checked_multifactorial_naive(n, m), of);
assert_ne!(of, Some(T::ZERO));
if let Some(f) = of {
assert_eq!(T::multifactorial(n, m), f);
}
if n != u64::MAX && of.is_none() {
assert!(T::checked_multifactorial(n + 1, m).is_none());
}
});
}
#[test]
fn checked_multifactorial_properties() {
apply_fn_to_unsigneds!(checked_multifactorial_properties_helper);
}
fn subfactorial_properties_helper() {
unsigned_gen_var_25::().test_properties(|n| {
let f = T::subfactorial(n);
assert_eq!(T::checked_subfactorial(n), Some(f));
if n != 1 {
assert_ne!(f, T::ZERO);
}
if n != 0 && n != 2 {
let g = if n.even() { f - T::ONE } else { f + T::ONE };
assert_eq!(g / T::subfactorial(n - 1), T::exact_from(n));
}
});
}
#[test]
fn subfactorial_properties() {
apply_fn_to_unsigneds!(subfactorial_properties_helper);
}
fn checked_subfactorial_properties_helper() {
unsigned_gen().test_properties(|n| {
let of = T::checked_subfactorial(n);
assert_eq!(checked_subfactorial_naive(n), of);
if n != 1 {
assert_ne!(of, Some(T::ZERO));
}
if let Some(f) = of {
assert_eq!(T::subfactorial(n), f);
}
if n != u64::MAX && of.is_none() {
assert!(T::checked_subfactorial(n + 1).is_none());
}
});
}
#[test]
fn checked_subfactorial_properties() {
apply_fn_to_unsigneds!(checked_subfactorial_properties_helper);
}