use crate::error::{SpecialError, SpecialResult};
use crate::q_analogs::{q_binomial, q_pochhammer};
const MAX_TERMS: usize = 300;
const TOL: f64 = 1e-14;
fn phi2_1_terminating(a1: f64, a2: f64, b1: f64, q: f64, z: f64, n: usize) -> SpecialResult<f64> {
let mut sum = 0.0f64;
let mut a1_k = 1.0f64; let mut a2_k = 1.0f64; let mut b1_k = 1.0f64; let mut qq_k = 1.0f64; let mut z_pow = 1.0f64; let mut q_pow = 1.0f64;
for k in 0..=n {
if k > 0 {
a1_k *= 1.0 - a1 * q.powi((k - 1) as i32);
a2_k *= 1.0 - a2 * q.powi((k - 1) as i32);
b1_k *= 1.0 - b1 * q.powi((k - 1) as i32);
q_pow *= q;
qq_k *= 1.0 - q_pow;
z_pow *= z;
}
let denom = b1_k * qq_k;
if denom.abs() < 1e-300 {
return Err(SpecialError::ComputationError(format!(
"phi2_1_terminating: denominator vanished at k = {k}"
)));
}
let term = a1_k * a2_k / denom * z_pow;
sum += term;
if !sum.is_finite() {
return Err(SpecialError::OverflowError(format!(
"phi2_1_terminating: overflow at k = {k}"
)));
}
}
Ok(sum)
}
fn phi3_2_terminating(
a1: f64,
a2: f64,
a3: f64,
b1: f64,
b2: f64,
q: f64,
z: f64,
n: usize,
) -> SpecialResult<f64> {
let mut sum = 0.0f64;
let mut a1_k = 1.0f64;
let mut a2_k = 1.0f64;
let mut a3_k = 1.0f64;
let mut b1_k = 1.0f64;
let mut b2_k = 1.0f64;
let mut qq_k = 1.0f64;
let mut z_pow = 1.0f64;
let mut q_pow = 1.0f64;
for k in 0..=n {
if k > 0 {
let qk1 = q.powi((k - 1) as i32);
a1_k *= 1.0 - a1 * qk1;
a2_k *= 1.0 - a2 * qk1;
a3_k *= 1.0 - a3 * qk1;
b1_k *= 1.0 - b1 * qk1;
b2_k *= 1.0 - b2 * qk1;
q_pow *= q;
qq_k *= 1.0 - q_pow;
z_pow *= z;
}
let denom = b1_k * b2_k * qq_k;
if denom.abs() < 1e-300 {
return Err(SpecialError::ComputationError(format!(
"phi3_2_terminating: denominator vanished at k = {k}"
)));
}
let term = a1_k * a2_k * a3_k / denom * z_pow;
sum += term;
if !sum.is_finite() {
return Err(SpecialError::OverflowError(format!(
"phi3_2_terminating: overflow at k = {k}"
)));
}
}
Ok(sum)
}
fn phi1_1(a1: f64, b1: f64, q: f64, z: f64) -> SpecialResult<f64> {
let mut sum = 0.0f64;
let mut a1_k = 1.0f64;
let mut b1_k = 1.0f64;
let mut qq_k = 1.0f64;
let mut z_pow = 1.0f64;
let mut q_pow = 1.0f64;
for k in 0..MAX_TERMS {
if k > 0 {
let qk1 = q.powi((k - 1) as i32);
a1_k *= 1.0 - a1 * qk1;
b1_k *= 1.0 - b1 * qk1;
q_pow *= q;
qq_k *= 1.0 - q_pow;
z_pow *= z;
}
let denom = b1_k * qq_k;
if denom.abs() < 1e-300 {
break;
}
let term = a1_k / denom * z_pow;
sum += term;
if term.abs() < TOL * sum.abs().max(1e-300) && k > 3 {
return Ok(sum);
}
if !sum.is_finite() {
return Err(SpecialError::OverflowError(
"phi1_1: series diverged".to_string(),
));
}
}
if sum.is_finite() {
Ok(sum)
} else {
Err(SpecialError::ConvergenceError(
"phi1_1: series did not converge".to_string(),
))
}
}
pub fn little_q_jacobi(n: usize, a: f64, b: f64, x: f64, q: f64) -> SpecialResult<f64> {
if q <= 0.0 || q >= 1.0 {
return Err(SpecialError::DomainError(format!(
"little_q_jacobi: q must satisfy 0 < q < 1, got q = {q}"
)));
}
if n == 0 {
return Ok(1.0);
}
let a1 = q.powi(-(n as i32)); let a2 = a * b * q.powi((n + 1) as i32); let b1 = a * q;
phi2_1_terminating(a1, a2, b1, q, q * x, n)
}
pub fn big_q_jacobi(n: usize, a: f64, b: f64, c: f64, x: f64, q: f64) -> SpecialResult<f64> {
if q <= 0.0 || q >= 1.0 {
return Err(SpecialError::DomainError(format!(
"big_q_jacobi: q must satisfy 0 < q < 1, got q = {q}"
)));
}
if n == 0 {
return Ok(1.0);
}
let a1 = q.powi(-(n as i32));
let a2 = a * b * q.powi((n + 1) as i32);
let a3 = x;
let b1 = a * q;
let b2 = c * q;
phi3_2_terminating(a1, a2, a3, b1, b2, q, q, n)
}
pub fn q_laguerre(n: usize, alpha: f64, x: f64, q: f64) -> SpecialResult<f64> {
if q <= 0.0 || q >= 1.0 {
return Err(SpecialError::DomainError(format!(
"q_laguerre: q must satisfy 0 < q < 1, got q = {q}"
)));
}
if n == 0 {
return Ok(1.0);
}
let qa1 = q.powf(alpha + 1.0); let poch_qa1_n = q_pochhammer(qa1, q, n)?;
let poch_q_n = q_pochhammer(q, q, n)?;
if poch_q_n.abs() < 1e-300 {
return Err(SpecialError::ComputationError(
"q_laguerre: (q;q)_n is too close to zero".to_string(),
));
}
let prefactor = poch_qa1_n / poch_q_n;
let a1 = q.powi(-(n as i32)); let b1 = qa1; let z = -q.powf(alpha + (n as f64) + 1.0) * x;
let phi_val = phi1_1_terminating(a1, b1, q, z, n)?;
Ok(prefactor * phi_val)
}
fn phi1_1_terminating(a1: f64, b1: f64, q: f64, z: f64, n: usize) -> SpecialResult<f64> {
let mut sum = 0.0f64;
let mut a1_k = 1.0f64;
let mut b1_k = 1.0f64;
let mut qq_k = 1.0f64;
let mut z_pow = 1.0f64;
let mut q_pow = 1.0f64;
for k in 0..=n {
if k > 0 {
let qk1 = q.powi((k - 1) as i32);
a1_k *= 1.0 - a1 * qk1;
b1_k *= 1.0 - b1 * qk1;
q_pow *= q;
qq_k *= 1.0 - q_pow;
z_pow *= z;
}
let denom = b1_k * qq_k;
if denom.abs() < 1e-300 {
return Err(SpecialError::ComputationError(format!(
"phi1_1_terminating: denominator vanished at k = {k}"
)));
}
let term = a1_k / denom * z_pow;
sum += term;
if !sum.is_finite() {
return Err(SpecialError::OverflowError(format!(
"phi1_1_terminating: overflow at k = {k}"
)));
}
}
Ok(sum)
}
pub fn al_salam_carlitz(n: usize, a: f64, x: f64, q: f64) -> SpecialResult<f64> {
if q <= 0.0 || q >= 1.0 {
return Err(SpecialError::DomainError(format!(
"al_salam_carlitz: q must satisfy 0 < q < 1, got q = {q}"
)));
}
if n == 0 {
return Ok(1.0);
}
if n == 1 {
return Ok(x - (1.0 + a));
}
let mut u_prev = 1.0f64; let mut u_curr = x - (1.0 + a);
for k in 1..n {
let q_k = q.powi(k as i32); let coeff_curr = x - (1.0 + a * q_k);
let coeff_prev = a * q.powi((k - 1) as i32) * (1.0 - q_k);
let u_next = coeff_curr * u_curr - coeff_prev * u_prev;
u_prev = u_curr;
u_curr = u_next;
if !u_curr.is_finite() {
return Err(SpecialError::OverflowError(format!(
"al_salam_carlitz: overflow at k = {k}"
)));
}
}
Ok(u_curr)
}
pub fn discrete_q_hermite(n: usize, x: f64, q: f64) -> SpecialResult<f64> {
if q <= 0.0 || q >= 1.0 {
return Err(SpecialError::DomainError(format!(
"discrete_q_hermite: q must satisfy 0 < q < 1, got q = {q}"
)));
}
if n == 0 {
return Ok(1.0);
}
if n == 1 {
return Ok(x);
}
let mut h_prev = 1.0f64; let mut h_curr = x;
for k in 1..n {
let q_km1 = q.powi((k - 1) as i32);
let q_k = q.powi(k as i32);
let coeff = q_km1 * (1.0 - q_k);
let h_next = x * h_curr - coeff * h_prev;
h_prev = h_curr;
h_curr = h_next;
if !h_curr.is_finite() {
return Err(SpecialError::OverflowError(format!(
"discrete_q_hermite: overflow at k = {k}"
)));
}
}
Ok(h_curr)
}
pub fn q_charlier(n: usize, a: f64, x: f64, q: f64) -> SpecialResult<f64> {
if q <= 0.0 || q >= 1.0 {
return Err(SpecialError::DomainError(format!(
"q_charlier: q must satisfy 0 < q < 1, got q = {q}"
)));
}
if a <= 0.0 {
return Err(SpecialError::DomainError(format!(
"q_charlier: a must be positive, got a = {a}"
)));
}
if n == 0 {
return Ok(1.0);
}
let mut sum = 0.0f64;
let mut q_kk1_2 = 1.0f64; let mut q_nk = 1.0f64; let mut a_pow = 1.0f64; let q_n = q.powi(n as i32);
for k in 0..=n {
if k > 0 {
q_kk1_2 *= q.powi((k - 1) as i32);
q_nk *= q_n;
a_pow *= a;
}
let binom = q_binomial(n, k, q)?;
let poch_x_k = q_pochhammer(x, q, k)?;
let sign = if k % 2 == 0 { 1.0 } else { -1.0 };
let term = binom * poch_x_k * sign * q_kk1_2 * q_nk / a_pow;
sum += term;
if !sum.is_finite() {
return Err(SpecialError::OverflowError(format!(
"q_charlier: overflow at k = {k}"
)));
}
}
Ok(sum)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_little_q_jacobi_degree_zero() {
let val = little_q_jacobi(0, 0.5, 0.5, 0.3, 0.7).expect("little_q_jacobi n=0");
assert!((val - 1.0).abs() < 1e-12, "val = {val}");
}
#[test]
fn test_little_q_jacobi_degree_one() {
let q = 0.6f64;
let a = 0.5f64;
let b = 0.4f64;
let x = 0.3f64;
let val = little_q_jacobi(1, a, b, x, q).expect("little_q_jacobi n=1");
let a1 = q.powi(-1);
let a2 = a * b * q * q;
let b1 = a * q;
let k1_num = (1.0 - a1) * (1.0 - a2);
let k1_den = (1.0 - b1) * (1.0 - q);
let expected = 1.0 + k1_num / k1_den * (q * x);
assert!((val - expected).abs() < 1e-10, "val = {val}, expected = {expected}");
}
#[test]
fn test_little_q_jacobi_symmetry_at_x0() {
let val = little_q_jacobi(3, 0.5, 0.5, 0.0, 0.7).expect("little_q_jacobi at x=0");
assert!((val - 1.0).abs() < 1e-12, "val = {val}");
}
#[test]
fn test_big_q_jacobi_degree_zero() {
let val = big_q_jacobi(0, 0.5, 0.5, 0.1, 0.4, 0.7).expect("big_q_jacobi n=0");
assert!((val - 1.0).abs() < 1e-12);
}
#[test]
fn test_big_q_jacobi_finite() {
let val = big_q_jacobi(2, 0.5, 0.3, 0.1, 0.4, 0.7).expect("big_q_jacobi n=2");
assert!(val.is_finite());
}
#[test]
fn test_q_laguerre_degree_zero() {
let val = q_laguerre(0, 0.5, 1.0, 0.7).expect("q_laguerre n=0");
assert!((val - 1.0).abs() < 1e-12);
}
#[test]
fn test_q_laguerre_finite() {
let val = q_laguerre(3, 0.5, 0.5, 0.7).expect("q_laguerre n=3");
assert!(val.is_finite());
}
#[test]
fn test_al_salam_carlitz_degree_zero() {
let val = al_salam_carlitz(0, -1.0, 0.5, 0.7).expect("al_salam_carlitz n=0");
assert!((val - 1.0).abs() < 1e-12);
}
#[test]
fn test_al_salam_carlitz_degree_one() {
let a = -0.5f64;
let x = 0.3f64;
let val = al_salam_carlitz(1, a, x, 0.7).expect("al_salam_carlitz n=1");
let expected = x - (1.0 + a);
assert!((val - expected).abs() < 1e-10, "val = {val}, expected = {expected}");
}
#[test]
fn test_al_salam_carlitz_recurrence() {
let a = -0.3f64;
let x = 0.5f64;
let q = 0.6f64;
let u0 = al_salam_carlitz(0, a, x, q).expect("U_0");
let u1 = al_salam_carlitz(1, a, x, q).expect("U_1");
let u2 = al_salam_carlitz(2, a, x, q).expect("U_2");
let expected = (x - (1.0 + a * q)) * u1 - a * (1.0 - q) * u0;
assert!((u2 - expected).abs() < 1e-8, "u2 = {u2}, expected = {expected}");
}
#[test]
fn test_discrete_q_hermite_degree_zero() {
let val = discrete_q_hermite(0, 1.0, 0.7).expect("dqh n=0");
assert!((val - 1.0).abs() < 1e-12);
}
#[test]
fn test_discrete_q_hermite_degree_one() {
let x = 0.5f64;
let val = discrete_q_hermite(1, x, 0.7).expect("dqh n=1");
assert!((val - x).abs() < 1e-12);
}
#[test]
fn test_discrete_q_hermite_recurrence() {
let x = 0.6f64;
let q = 0.5f64;
let h0 = discrete_q_hermite(0, x, q).expect("h0");
let h1 = discrete_q_hermite(1, x, q).expect("h1");
let h2 = discrete_q_hermite(2, x, q).expect("h2");
let expected = x * h1 - (1.0 - q) * h0;
assert!((h2 - expected).abs() < 1e-10, "h2 = {h2}, expected = {expected}");
}
#[test]
fn test_q_charlier_degree_zero() {
let val = q_charlier(0, 2.0, 0.5, 0.7).expect("q_charlier n=0");
assert!((val - 1.0).abs() < 1e-12);
}
#[test]
fn test_q_charlier_finite() {
let val = q_charlier(3, 1.5, 0.4, 0.6).expect("q_charlier n=3");
assert!(val.is_finite());
}
#[test]
fn test_q_charlier_domain_error() {
assert!(q_charlier(2, -1.0, 0.5, 0.7).is_err()); assert!(q_charlier(2, 1.0, 0.5, 1.5).is_err()); }
}