#[inline]
pub fn nodes<const N: usize>() -> [f64; N] {
let mut out = [0.0_f64; N];
let n = N as f64;
for (k, x) in out.iter_mut().enumerate() {
let arg = std::f64::consts::PI * (2.0 * k as f64 + 1.0) / (2.0 * n);
*x = arg.cos();
}
out
}
#[inline]
pub fn nodes_mapped<const N: usize>(start: f64, end: f64) -> [f64; N] {
let mid = 0.5 * (start + end);
let half = 0.5 * (end - start);
let unit = nodes::<N>();
let mut out = [0.0_f64; N];
for (x, &u) in out.iter_mut().zip(unit.iter()) {
*x = mid + half * u;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nodes_count() {
let n3: [f64; 3] = nodes();
assert_eq!(n3.len(), 3);
let n9: [f64; 9] = nodes();
assert_eq!(n9.len(), 9);
}
#[test]
fn test_nodes_in_range() {
let xi: [f64; 9] = nodes();
for &x in &xi {
assert!(x > -1.0 && x < 1.0, "node {x} out of (-1, 1)");
}
}
#[test]
fn test_nodes_descending() {
let xi: [f64; 9] = nodes();
for w in xi.windows(2) {
assert!(w[0] > w[1], "nodes not descending: {} <= {}", w[0], w[1]);
}
}
#[test]
fn test_nodes_mapped_range() {
let t: [f64; 9] = nodes_mapped(10.0, 20.0);
for &x in &t {
assert!(
(10.0..=20.0).contains(&x),
"mapped node {x} out of [10, 20]"
);
}
}
#[test]
fn test_nodes_mapped_midpoint() {
let t: [f64; 9] = nodes_mapped(0.0, 4.0);
let mean: f64 = t.iter().sum::<f64>() / 9.0;
assert!((mean - 2.0).abs() < 1e-10);
}
}