use nalgebra::Complex;
use num::ToPrimitive;
use num::rational::Ratio;
use crate::arithmetic_utils::{dedekind_sum, euler_function_coeff};
use super::modular_def::{ModularError, ModularForm, ModularTransformationGroup};
#[allow(dead_code)]
pub struct EtaTransformationGroup {
a: i128,
b: i128,
c: i128,
d: i128,
}
impl ModularTransformationGroup<Complex<f64>> for EtaTransformationGroup {
fn new(raw_matrix: [[Complex<f64>; 2]; 2]) -> Result<Self, ModularError> {
const EPSILON: f64 = 1e-9;
let [[a, b], [c, d]] = raw_matrix;
if [a, b, c, d]
.into_iter()
.any(|x| x.im.abs() > EPSILON || (x.re - x.re.round()).abs() > EPSILON)
{
return Err(ModularError::NonIntegerEntry);
}
#[allow(clippy::cast_possible_truncation)]
let (a, b, c, d) = (
a.re.round() as i128,
b.re.round() as i128,
c.re.round() as i128,
d.re.round() as i128,
);
if a * d - b * c != 1 {
return Err(ModularError::NotInGroup);
}
Ok(Self { a, b, c, d })
}
fn transform_q(&self, q: &Complex<f64>) -> Complex<f64> {
*q
}
fn multiplier_system(&self, tau: &Complex<f64>) -> Complex<f64> {
let Self { a, b, c, d } = *self;
if c < 0 || (c == 0 && a == -1) {
let flipped = Self {
a: -a,
b: -b,
c: -c,
d: -d,
};
let chi_flipped = flipped.multiplier_system(tau);
#[allow(clippy::cast_precision_loss)]
let z = Complex::new(c as f64, 0.0) * tau + Complex::new(d as f64, 0.0);
return chi_flipped * (-z).sqrt() / z.sqrt();
}
if c == 0 {
#[allow(clippy::cast_precision_loss)]
let angle = std::f64::consts::PI * b as f64 / 12.0;
return Complex::new(angle.cos(), angle.sin());
}
let x = Ratio::new(a + d, 12 * c) - dedekind_sum(d, c) - Ratio::new(1, 4);
let angle = std::f64::consts::PI * x.to_f64().expect("Ratio<i128> fits in f64");
Complex::new(angle.cos(), angle.sin())
}
#[allow(clippy::cast_precision_loss)]
fn raw_a(&self) -> Complex<f64> {
Complex::new(self.a as f64, 0.0)
}
#[allow(clippy::cast_precision_loss)]
fn raw_b(&self) -> Complex<f64> {
Complex::new(self.b as f64, 0.0)
}
#[allow(clippy::cast_precision_loss)]
fn raw_c(&self) -> Complex<f64> {
Complex::new(self.c as f64, 0.0)
}
#[allow(clippy::cast_precision_loss)]
fn raw_d(&self) -> Complex<f64> {
Complex::new(self.d as f64, 0.0)
}
fn is_trivial_multiplier_system() -> bool {
false
}
}
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub struct DedekindEta;
impl ModularForm<1, Complex<f64>> for DedekindEta {
type TransformationGroup = EtaTransformationGroup;
#[allow(clippy::cast_precision_loss)]
fn extract_coeffs(&self, which_coeff: usize) -> Result<Complex<f64>, ModularError> {
Ok(Complex::new(euler_function_coeff(which_coeff) as f64, 0.0))
}
fn evaluate_at(&self, _q: &Complex<f64>) -> Result<Complex<f64>, ModularError> {
Err(ModularError::Unavailable)
}
}