use crate::scalar::Real;
use crate::{Error, Result};
const SCALED_RANGE_BITS: i64 = 256;
const UNSCALED_FROM_BITS: i64 = -900;
const GENERAL_DIFFERENCE_FROM: f64 = 0.9;
pub fn wigner_d<T: Real>(m: i32, n: i32, x: T, out: &mut [T]) -> Result<()> {
check_x(x)?;
let (m, n) = (i64::from(m), i64::from(n));
let l_min = match usize::try_from(m.unsigned_abs().max(n.unsigned_abs())) {
Ok(l_min) if l_min < out.len() => l_min,
_ => {
out.fill(T::zero());
return Ok(());
}
};
out[..l_min].fill(T::zero());
let reflect = x < T::zero() && (m == -n || -x >= T::from_f64(GENERAL_DIFFERENCE_FROM));
let (n, x) = if reflect { (-n, -x) } else { (n, x) };
if m == n && x >= T::zero() {
difference_form(m, x, l_min, out);
} else if x >= T::from_f64(GENERAL_DIFFERENCE_FROM) {
general_difference_form(m, n, x, l_min, out);
} else {
three_term(m, n, x, l_min, out);
}
if reflect {
let first_odd = 1 - (l_min as i64 + m).rem_euclid(2) as usize;
for v in out[l_min..].iter_mut().skip(first_odd).step_by(2) {
*v = -*v;
}
}
Ok(())
}
pub fn wigner_d_vec<T: Real>(m: i32, n: i32, l_max: usize, x: T) -> Result<Vec<T>> {
check_x(x)?;
let too_large = || {
Error::InvalidInput(format!(
"wigner_d_vec cannot allocate l_max + 1 values for l_max = {l_max}"
))
};
let len = l_max.checked_add(1).ok_or_else(too_large)?;
let mut out = Vec::new();
out.try_reserve_exact(len).map_err(|_| too_large())?;
out.resize(len, T::zero());
wigner_d(m, n, x, &mut out)?;
Ok(out)
}
fn check_x<T: Real>(x: T) -> Result<()> {
if (-T::one()..=T::one()).contains(&x) {
Ok(())
} else {
Err(Error::InvalidInput(format!(
"wigner_d needs -1 <= x <= 1, got {:?}",
x.approx_f64()
)))
}
}
fn three_term<T: Real>(m: i64, n: i64, x: T, l_min: usize, out: &mut [T]) {
let (a, b) = ((m - n).unsigned_abs(), (m + n).unsigned_abs());
let (magnitude, exp) = start_value(a, b, x);
let start = if n >= m || (m - n) % 2 == 0 {
magnitude
} else {
-magnitude
};
let mut rec = ThreeTerm {
x,
m: m as f64,
n: n as f64,
q: T::zero(),
};
run(&mut rec, l_min, [T::zero(), start], exp, out);
}
fn difference_form<T: Real>(m: i64, x: T, l_min: usize, out: &mut [T]) {
let b = 2 * m.unsigned_abs();
let (start, exp) = start_value(0, b, x);
let w = x - T::one();
let mut rec = Difference { w, m: m as f64 };
if l_min == 0 {
out[0] = start;
if out.len() > 1 {
run(&mut rec, 1, [w, x], exp, out);
}
} else {
run(&mut rec, l_min, [start, start], exp, out);
}
}
fn general_difference_form<T: Real>(m: i64, n: i64, x: T, l_min: usize, out: &mut [T]) {
let (a, b) = ((m - n).unsigned_abs(), (m + n).unsigned_abs());
let (magnitude, exp) = start_value(a, b, x);
let start = if n >= m || (m - n) % 2 == 0 {
magnitude
} else {
-magnitude
};
let (mf, nf, lf) = (m as f64, n as f64, l_min as f64);
let mut rec = GeneralDifference {
w: x - T::one(),
m: mf,
n: nf,
q: T::zero(),
u: T::from_f64(lf * (lf + 1.0)) - mf * nf,
};
run(&mut rec, l_min, [start, start], exp, out);
}
trait Step<T> {
fn step(&mut self, s: usize, state: &mut [T; 2]);
}
fn run<T: Real, R: Step<T>>(
rec: &mut R,
mut s: usize,
mut state: [T; 2],
mut exp: i64,
out: &mut [T],
) {
while exp != 0 {
out[s] = mul_pow2(state[1], exp);
if s + 1 == out.len() {
return;
}
rec.step(s, &mut state);
s += 1;
let k = log2_floor(state[0].abs().max(state[1].abs()));
if k + exp >= UNSCALED_FROM_BITS {
state = state.map(|v| mul_pow2(v, exp));
exp = 0;
} else if k.abs() > SCALED_RANGE_BITS {
state = state.map(|v| mul_pow2(v, -k));
exp += k;
}
}
out[s] = state[1];
while s + 1 < out.len() {
rec.step(s, &mut state);
s += 1;
out[s] = state[1];
}
}
struct ThreeTerm<T> {
x: T,
m: f64,
n: f64,
q: T,
}
impl<T: Real> Step<T> for ThreeTerm<T> {
#[inline]
fn step(&mut self, s: usize, state: &mut [T; 2]) {
let [prev, cur] = *state;
let (sf, mf, nf) = (s as f64, self.m, self.n);
let c_prev = self.q * (sf + 1.0);
let numer = (self.x * (sf * (sf + 1.0)) - mf * nf) * (2.0 * sf + 1.0) * cur - c_prev * prev;
let s1 = sf + 1.0;
self.q =
T::from_f64((s1 - mf) * (s1 + mf)).sqrt() * T::from_f64((s1 - nf) * (s1 + nf)).sqrt();
*state = [cur, numer / (self.q * sf)];
}
}
struct Difference<T> {
w: T,
m: f64,
}
impl<T: Real> Step<T> for Difference<T> {
#[inline]
fn step(&mut self, s: usize, state: &mut [T; 2]) {
let [delta, cur] = *state;
let (sf, mf) = (s as f64, self.m);
let s1 = sf + 1.0;
let k = T::from_f64(2.0 * sf + 1.0) * sf * s1;
let b = T::from_f64(s1) * ((sf - mf) * (sf + mf));
let c = T::from_f64(sf) * ((s1 - mf) * (s1 + mf));
let next = (self.w * k * cur + delta * b) * (T::one() / c);
*state = [next, cur + next];
}
}
struct GeneralDifference<T> {
w: T,
m: f64,
n: f64,
q: T,
u: T,
}
impl<T: Real> Step<T> for GeneralDifference<T> {
#[inline]
fn step(&mut self, s: usize, state: &mut [T; 2]) {
let [delta, cur] = *state;
let (sf, mf, nf) = (s as f64, self.m, self.n);
let s1 = sf + 1.0;
let q1 = (T::from_f64((s1 - mf) * (s1 + mf)) * ((s1 - nf) * (s1 + nf))).sqrt();
let alpha = T::from_f64(sf * s1) - mf * nf;
let dmn = mf - nf;
let numer =
T::from_f64(s1 * s1) * (1.0 + dmn * dmn - 2.0 * s1) + T::from_f64(2.0 * s1) * (mf * nf);
let v = numer / (alpha + q1);
let k = self.w * (T::from_f64(2.0 * sf + 1.0) * sf * s1) + self.u * s1 + v * sf;
let next = (k * cur + delta * (self.q * s1)) * (T::one() / (q1 * sf));
(self.q, self.u) = (q1, v + 2.0 * s1);
*state = [next, cur + next];
}
}
fn start_value<T: Real>(a: u64, b: u64, x: T) -> (T, i64) {
let half_sin = ((T::one() - x) * 0.5).sqrt();
let half_cos = ((T::one() + x) * 0.5).sqrt();
if a >= 2 && half_sin == T::zero() {
if a > 2 {
return (T::zero(), 0);
}
let (b1, b2) = (b as f64 + 1.0, b as f64 + 2.0);
let mut v = (T::one() - x) * 0.5 * (T::from_f64(b1) * b2 * 0.5).sqrt();
for _ in 0..b {
v *= half_cos;
}
return (v, 0);
}
let (low, high) = (pow2(-SCALED_RANGE_BITS), pow2(SCALED_RANGE_BITS));
let mut v = T::one();
let mut e = 0;
for k in 1..=a.max(b) {
if k <= a {
v = v * (T::from_f64((b + k) as f64) / T::from_f64(k as f64)).sqrt() * half_sin;
}
if k <= b {
v *= half_cos;
}
let size = v.abs();
if (size < T::from_f64(low) && size != T::zero()) || size > T::from_f64(high) {
let k = log2_floor(v);
(v, e) = (mul_pow2(v, -k), e + k);
}
}
fold(v, e)
}
fn fold<T: Real>(v: T, e: i64) -> (T, i64) {
if e == 0 || v == T::zero() {
(v, 0)
} else if log2_floor(v) + e >= UNSCALED_FROM_BITS {
(mul_pow2(v, e), 0)
} else {
(v, e)
}
}
fn pow2(k: i64) -> f64 {
debug_assert!((-1022..=1023).contains(&k));
f64::from_bits(((k + 1023) as u64) << 52)
}
fn log2_floor<T: Real>(v: T) -> i64 {
let v = v.approx_f64();
if v == 0.0 || !v.is_finite() {
return 0;
}
let biased = ((v.to_bits() >> 52) & 0x7ff) as i64;
if biased == 0 {
return log2_floor(v * pow2(64)) - 64;
}
biased - 1023
}
fn mul_pow2<T: Real>(v: T, e: i64) -> T {
if v == T::zero() || e == 0 {
return v;
}
let ev = log2_floor(v);
let mut unit = v;
let mut k = -ev;
while k != 0 {
let step = k.clamp(-1022, 1023);
unit *= pow2(step);
k -= step;
}
let t = ev + e;
if t >= -1022 {
unit * pow2(t)
} else {
unit * pow2(-1022) * pow2((t + 1022).max(-62))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::scalar::DoubleDouble as Dd;
fn reference_mul_pow2(v: f64, e: i64) -> f64 {
let bits = v.to_bits();
let (biased, frac) = (((bits >> 52) & 0x7ff) as i64, bits & ((1 << 52) - 1));
let (sig, exp) = if biased == 0 {
(frac, -1074)
} else {
(frac | (1 << 52), biased - 1075)
};
let target = exp + e; let exact_pow2 = |k: i64| {
if k >= -1022 {
f64::from_bits(((k + 1023) as u64) << 52)
} else {
f64::from_bits(1 << (k + 1074))
}
};
let mag = if target >= -1074 {
sig as f64 * exact_pow2(target.min(1023 - 52))
} else {
let shift = (-1074 - target) as u32;
let q = if shift >= 64 { 0 } else { sig >> shift };
let rem = if shift >= 64 { sig } else { sig - (q << shift) };
let half = if shift >= 65 {
u64::MAX
} else {
1u64 << (shift - 1)
};
let q = if rem > half || (rem == half && q % 2 == 1) {
q + 1
} else {
q
};
q as f64 * exact_pow2(-1074)
};
mag.copysign(v)
}
#[test]
fn mul_pow2_is_exact_or_rounds_once() {
let values = [
1.0,
-1.5,
1.0 + f64::EPSILON,
1.75 - f64::EPSILON,
3.0e-300,
2f64.powi(-1070) * 1.25,
f64::MIN_POSITIVE,
-5e-324,
1.0e200,
];
for v in values {
for e in [
-1200, -1130, -1100, -1080, -1075, -1074, -1060, -1023, -1000, -300, -1, 1, 300,
] {
let expected = reference_mul_pow2(v, e);
if expected.abs() >= 2f64.powi(1023) {
continue;
}
let got = mul_pow2(v, e);
assert_eq!(
got.to_bits(),
expected.to_bits(),
"{v:e}·2^{e}: {got:e} vs {expected:e}"
);
}
}
let x = Dd::new(1.0, 2f64.powi(-60));
let y = mul_pow2(x, -500);
assert_eq!((y.hi(), y.lo()), (2f64.powi(-500), 2f64.powi(-560)));
let z = mul_pow2(y, 500);
assert_eq!(z, x);
}
#[test]
fn log2_floor_handles_every_finite_double() {
assert_eq!(log2_floor(1.0), 0);
assert_eq!(log2_floor(1.999), 0);
assert_eq!(log2_floor(-2.0), 1);
assert_eq!(log2_floor(f64::MIN_POSITIVE), -1022);
assert_eq!(log2_floor(5e-324), -1074);
assert_eq!(log2_floor(3.0 * 5e-324), -1073);
assert_eq!(log2_floor(f64::MAX), 1023);
assert_eq!(log2_floor(0.0), 0);
assert_eq!(log2_floor(f64::INFINITY), 0);
}
#[test]
fn difference_form_numerator_identity() {
for m in -12..=12_i128 {
for n in -12..=12_i128 {
let l_min = m.abs().max(n.abs());
for s in l_min.max(1)..l_min + 40 {
let s1 = s + 1;
let alpha = s * s1 - m * n;
let q2 = (s1 * s1 - m * m) * (s1 * s1 - n * n);
let formula = s1 * s1 * (1 + (m - n) * (m - n) - 2 * s1) + 2 * s1 * m * n;
assert_eq!(alpha * alpha - q2, formula, "m={m} n={n} s={s}");
assert_eq!((s + 1) * (s + 2) - m * n, alpha + 2 * s1);
}
}
}
}
}