#[must_use]
pub fn double_factorial(n: i64) -> f64 {
let mut k = n;
let mut acc = 1.0_f64;
while k > 1 {
acc *= k as f64;
k -= 2;
}
acc
}
#[must_use]
pub fn cart_norm(alpha: f64, lx: usize, ly: usize, lz: usize) -> f64 {
let l = (lx + ly + lz) as i32;
let df = double_factorial(2 * lx as i64 - 1)
* double_factorial(2 * ly as i64 - 1)
* double_factorial(2 * lz as i64 - 1);
let two_alpha_over_pi = 2.0 * alpha / std::f64::consts::PI;
two_alpha_over_pi.powf(0.75) * (4.0 * alpha).powi(l).sqrt() / df.sqrt()
}
#[must_use]
pub fn shell_norm(alpha: f64, l: usize) -> f64 {
cart_norm(alpha, l, 0, 0)
}
#[must_use]
pub fn cart_unit_factor(lx: usize, ly: usize, lz: usize) -> f64 {
let l = lx + ly + lz;
let df = double_factorial(2 * lx as i64 - 1)
* double_factorial(2 * ly as i64 - 1)
* double_factorial(2 * lz as i64 - 1);
(double_factorial(2 * l as i64 - 1) / df).sqrt()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn double_factorial_small_values() {
assert_eq!(double_factorial(-1), 1.0);
assert_eq!(double_factorial(0), 1.0);
assert_eq!(double_factorial(1), 1.0);
assert_eq!(double_factorial(3), 3.0);
assert_eq!(double_factorial(5), 15.0);
assert_eq!(double_factorial(7), 105.0);
}
#[test]
fn normalized_primitive_has_unit_self_overlap() {
let raw_self_overlap = |alpha: f64, lx: usize, ly: usize, lz: usize| {
let l = (lx + ly + lz) as i32;
let df = double_factorial(2 * lx as i64 - 1)
* double_factorial(2 * ly as i64 - 1)
* double_factorial(2 * lz as i64 - 1);
df / (4.0 * alpha).powi(l) * (std::f64::consts::PI / (2.0 * alpha)).powf(1.5)
};
for &alpha in &[0.1, 0.8, 1.0, 3.3, 25.0] {
for &(lx, ly, lz) in &[(0, 0, 0), (1, 0, 0), (0, 1, 1), (2, 0, 0), (1, 1, 1)] {
let n = cart_norm(alpha, lx, ly, lz);
let s = n * n * raw_self_overlap(alpha, lx, ly, lz);
assert!(
(s - 1.0).abs() < 1e-13,
"alpha={alpha} l=({lx}{ly}{lz}) s={s}"
);
}
}
}
}