pub mod coerced;
pub mod eisenstein;
pub mod eta;
pub mod modular_def;
pub mod product;
pub mod sum;
pub use coerced::{CoerceTransformation, EquivalentTransportedMF};
pub use eisenstein::{EisensteinE4, EisensteinE6};
pub use eta::{DedekindEta, EtaTransformationGroup};
pub use modular_def::{
ModularError, ModularForm, ModularTransformationGroup, Sl2Z, UnitModularForm,
};
pub use product::{
CombinedTransformationGroup, ProductModularForm, cube_modular_form, cube_modular_form_trivial,
square_modular_form, square_modular_form_trivial,
};
pub use sum::SumModularForm;
#[cfg(test)]
mod tests {
use std::marker::PhantomData;
use super::EquivalentTransportedMF;
use nalgebra::Complex;
use super::{
DedekindEta, EisensteinE4, EisensteinE6, EtaTransformationGroup, ModularForm,
ModularTransformationGroup, ProductModularForm, Sl2Z, SumModularForm, cube_modular_form,
cube_modular_form_trivial, square_modular_form, square_modular_form_trivial,
};
#[test]
fn e8_theta_series_is_e4_eisenstein_series() {
let basis_of_space: [Box<dyn ModularForm<8, Complex<f64>, TransformationGroup = Sl2Z>>; 1] =
[Box::new(EisensteinE4)];
let constrained_coeffs_values = [Ok((0, Complex { re: 1.0, im: 0.0 }))];
let theta_e8 = SumModularForm::<8, Complex<f64>, Sl2Z>::new_from_some_coeffs(
basis_of_space,
&constrained_coeffs_values,
)
.expect("E4 alone spans a 1-dimensional space pinned down by its constant term");
assert_eq!(theta_e8.summands.len(), 1);
assert_eq!(theta_e8.summands[0].1, Complex { re: 1.0, im: 0.0 });
assert_eq!(
theta_e8.extract_coeffs(1),
Ok(Complex { re: 240.0, im: 0.0 })
);
}
#[test]
fn e4_sum_coeffs() {
const COEFF1: Complex<f64> = Complex { re: 1.0, im: 0.0 };
const COEFF2: Complex<f64> = Complex { re: 5.0, im: 0.0 };
let sum: SumModularForm<8, Complex<f64>, Sl2Z> = SumModularForm {
summands: vec![
(
Box::new(EisensteinE4)
as Box<dyn ModularForm<8, Complex<f64>, TransformationGroup = Sl2Z>>,
COEFF1,
),
(
Box::new(EisensteinE4)
as Box<dyn ModularForm<8, Complex<f64>, TransformationGroup = Sl2Z>>,
COEFF2,
),
],
};
assert_eq!(sum.extract_coeffs(0), Ok(COEFF1 + COEFF2));
assert_eq!(sum.extract_coeffs(1), Ok((COEFF1 + COEFF2) * 240.0));
assert_eq!(sum.extract_coeffs(2), Ok((COEFF1 + COEFF2) * 2160.0));
}
#[test]
fn e4_times_e6_is_e10_eisenstein_series() {
let product =
ProductModularForm::<Complex<f64>, EisensteinE4, EisensteinE6, 8, 12, 20>::new(
EisensteinE4,
EisensteinE6,
);
assert_eq!(product.extract_coeffs(0), Ok(Complex::new(1.0, 0.0)));
assert_eq!(product.extract_coeffs(1), Ok(Complex::new(-264.0, 0.0)));
assert_eq!(product.extract_coeffs(2), Ok(Complex::new(-135_432.0, 0.0)));
assert_eq!(
product.extract_coeffs(3),
Ok(Complex::new(-5_196_576.0, 0.0))
);
}
#[test]
fn e4_cubed_and_e6_squared() {
let e4_cubed = cube_modular_form::<Complex<f64>, 8, 16, 24, EisensteinE4>(EisensteinE4);
let e6_squared = square_modular_form::<Complex<f64>, 12, 24, EisensteinE6>(EisensteinE6);
assert_eq!(e4_cubed.extract_coeffs(0), Ok(Complex::new(1.0, 0.0)));
assert_eq!(e4_cubed.extract_coeffs(1), Ok(Complex::new(720.0, 0.0)));
assert_eq!(e4_cubed.extract_coeffs(2), Ok(Complex::new(179_280.0, 0.0)));
assert_eq!(
e4_cubed.extract_coeffs(3),
Ok(Complex::new(16_954_560.0, 0.0))
);
assert_eq!(
e4_cubed.extract_coeffs(4),
Ok(Complex::new(396_974_160.0, 0.0))
);
let coercion =
super::CoerceTransformation::FORCED(PhantomData, PhantomData, PhantomData::<Sl2Z>);
let e4_cubed_fixed = EquivalentTransportedMF::coerce(e4_cubed, coercion);
assert_eq!(e4_cubed_fixed.extract_coeffs(0), Ok(Complex::new(1.0, 0.0)));
assert_eq!(
e4_cubed_fixed.extract_coeffs(1),
Ok(Complex::new(720.0, 0.0))
);
assert_eq!(
e4_cubed_fixed.extract_coeffs(2),
Ok(Complex::new(179_280.0, 0.0))
);
assert_eq!(
e4_cubed_fixed.extract_coeffs(3),
Ok(Complex::new(16_954_560.0, 0.0))
);
assert_eq!(
e4_cubed_fixed.extract_coeffs(4),
Ok(Complex::new(396_974_160.0, 0.0))
);
assert_eq!(e6_squared.extract_coeffs(0), Ok(Complex::new(1.0, 0.0)));
assert_eq!(e6_squared.extract_coeffs(1), Ok(Complex::new(-1008.0, 0.0)));
assert_eq!(
e6_squared.extract_coeffs(2),
Ok(Complex::new(220_752.0, 0.0))
);
assert_eq!(
e6_squared.extract_coeffs(3),
Ok(Complex::new(16_519_104.0, 0.0))
);
assert_eq!(
e6_squared.extract_coeffs(4),
Ok(Complex::new(399_517_776.0, 0.0))
);
let coercion =
super::CoerceTransformation::FORCED(PhantomData, PhantomData, PhantomData::<Sl2Z>);
let e6_squared_fixed = EquivalentTransportedMF::coerce(e6_squared, coercion);
assert_eq!(
e6_squared_fixed.extract_coeffs(0),
Ok(Complex::new(1.0, 0.0))
);
assert_eq!(
e6_squared_fixed.extract_coeffs(1),
Ok(Complex::new(-1008.0, 0.0))
);
assert_eq!(
e6_squared_fixed.extract_coeffs(2),
Ok(Complex::new(220_752.0, 0.0))
);
assert_eq!(
e6_squared_fixed.extract_coeffs(3),
Ok(Complex::new(16_519_104.0, 0.0))
);
assert_eq!(
e6_squared_fixed.extract_coeffs(4),
Ok(Complex::new(399_517_776.0, 0.0))
);
let sum: SumModularForm<24, Complex<f64>, Sl2Z> = SumModularForm {
summands: vec![
(
Box::new(e4_cubed_fixed)
as Box<dyn ModularForm<24, Complex<f64>, TransformationGroup = Sl2Z>>,
Complex::new(1.0, 0.0),
),
(
Box::new(e6_squared_fixed)
as Box<dyn ModularForm<24, Complex<f64>, TransformationGroup = Sl2Z>>,
Complex::new(-1.0, 0.0),
),
],
};
assert_eq!(sum.extract_coeffs(0), Ok(Complex::new(0.0, 0.0)));
assert_eq!(sum.extract_coeffs(1), Ok(Complex::new(1_728.0, 0.0)));
assert_eq!(sum.extract_coeffs(2), Ok(Complex::new(-41_472.0, 0.0)));
assert_eq!(sum.extract_coeffs(3), Ok(Complex::new(435_456.0, 0.0)));
assert_eq!(sum.extract_coeffs(4), Ok(Complex::new(-2_543_616.0, 0.0)));
}
#[test]
fn e4_cubed_minus_e6_squared_via_trivial_variants_is_1728_delta() {
let e4_cubed =
cube_modular_form_trivial::<Complex<f64>, 8, 16, 24, EisensteinE4>(EisensteinE4);
let e6_squared =
square_modular_form_trivial::<Complex<f64>, 12, 24, EisensteinE6>(EisensteinE6);
let sum: SumModularForm<24, Complex<f64>, Sl2Z> = SumModularForm {
summands: vec![
(
Box::new(e4_cubed)
as Box<dyn ModularForm<24, Complex<f64>, TransformationGroup = Sl2Z>>,
Complex::new(1.0, 0.0),
),
(
Box::new(e6_squared)
as Box<dyn ModularForm<24, Complex<f64>, TransformationGroup = Sl2Z>>,
Complex::new(-1.0, 0.0),
),
],
};
assert_eq!(sum.extract_coeffs(0), Ok(Complex::new(0.0, 0.0)));
assert_eq!(sum.extract_coeffs(1), Ok(Complex::new(1_728.0, 0.0)));
assert_eq!(sum.extract_coeffs(2), Ok(Complex::new(-41_472.0, 0.0)));
assert_eq!(sum.extract_coeffs(3), Ok(Complex::new(435_456.0, 0.0)));
assert_eq!(sum.extract_coeffs(4), Ok(Complex::new(-2_543_616.0, 0.0)));
}
#[test]
#[should_panic(expected = "trivial multiplier system")]
fn square_modular_form_trivial_panics_for_nontrivial_multiplier() {
let _ = square_modular_form_trivial::<Complex<f64>, 1, 2, DedekindEta>(DedekindEta);
}
#[test]
fn new_from_some_coeffs_reconstructs_delta_from_e4_cubed_and_e6_squared() {
let e4_cubed = cube_modular_form::<Complex<f64>, 8, 16, 24, EisensteinE4>(EisensteinE4);
let e6_squared = square_modular_form::<Complex<f64>, 12, 24, EisensteinE6>(EisensteinE6);
let coercion =
super::CoerceTransformation::FORCED(PhantomData, PhantomData, PhantomData::<Sl2Z>);
let e4_cubed_fixed = EquivalentTransportedMF::coerce(e4_cubed, coercion);
let coercion =
super::CoerceTransformation::FORCED(PhantomData, PhantomData, PhantomData::<Sl2Z>);
let e6_squared_fixed = EquivalentTransportedMF::coerce(e6_squared, coercion);
let basis_of_space: [Box<dyn ModularForm<24, Complex<f64>, TransformationGroup = Sl2Z>>;
2] = [Box::new(e4_cubed_fixed), Box::new(e6_squared_fixed)];
let constrained_coeffs_values = [
Ok((0, Complex::new(0.0, 0.0))),
Ok((1, Complex::new(1.0, 0.0))),
];
let delta = SumModularForm::<24, Complex<f64>, Sl2Z>::new_from_some_coeffs(
basis_of_space,
&constrained_coeffs_values,
)
.expect("E4^3 and E6^2 span a 2-dimensional space pinned down by these two constraints");
const ONE_OVER_1728: f64 = 1.0 / 1728.0;
assert_eq!(delta.summands.len(), 2);
assert!((delta.summands[0].1 - ONE_OVER_1728).norm() < 1e-9);
assert!((delta.summands[1].1 + ONE_OVER_1728).norm() < 1e-9);
assert!((delta.extract_coeffs(0).unwrap() - 0.0).norm() < 1e-9);
assert!((delta.extract_coeffs(1).unwrap() - 1.0).norm() < 1e-9);
assert!((delta.extract_coeffs(2).unwrap() - (-24.0)).norm() < 1e-9);
assert!((delta.extract_coeffs(3).unwrap() - 252.0).norm() < 1e-9);
assert!((delta.extract_coeffs(4).unwrap() - (-1472.0)).norm() < 1e-9);
}
#[test]
fn new_from_some_coeffs_scales_eta_to_hit_a_target_coefficient() {
let basis_of_space: [Box<
dyn ModularForm<1, Complex<f64>, TransformationGroup = EtaTransformationGroup>,
>; 1] = [Box::new(DedekindEta)];
let constrained_coeffs_values = [Ok((7, Complex::new(39.0, 0.0)))];
let scaled_eta = SumModularForm::<1, Complex<f64>, EtaTransformationGroup>::new_from_some_coeffs(
basis_of_space,
&constrained_coeffs_values,
)
.expect("DedekindEta alone spans a 1-dimensional space pinned down by a nonzero coefficient");
assert_eq!(scaled_eta.summands.len(), 1);
assert_eq!(scaled_eta.summands[0].1, Complex::new(39.0, 0.0));
assert_eq!(scaled_eta.extract_coeffs(7), Ok(Complex::new(39.0, 0.0)));
assert_eq!(scaled_eta.extract_coeffs(9), Ok(Complex::new(0.0, 0.0)));
}
fn eta_group_from_ints(a: i128, b: i128, c: i128, d: i128) -> EtaTransformationGroup {
let to_complex = |x: i128| Complex::new(x as f64, 0.0);
EtaTransformationGroup::new([
[to_complex(a), to_complex(b)],
[to_complex(c), to_complex(d)],
])
.expect("a*d - b*c == 1")
}
fn generic_taus() -> [Complex<f64>; 2] {
[Complex::new(0.37, 1.62), Complex::new(-1.1, 0.83)]
}
#[test]
fn eta_multiplier_at_t_generator() {
let t = eta_group_from_ints(1, 1, 0, 1);
let expected = Complex::new(0.0, std::f64::consts::PI / 12.0).exp();
for tau in generic_taus() {
let eps_t = t.multiplier_system(&tau);
assert!((eps_t - expected).norm() < 1e-9);
}
}
#[test]
fn eta_multiplier_at_s_generator() {
let s = eta_group_from_ints(0, -1, 1, 0);
let expected = Complex::new(0.0, -std::f64::consts::PI / 4.0).exp();
for tau in generic_taus() {
let eps_s = s.multiplier_system(&tau);
assert!((eps_s - expected).norm() < 1e-9);
}
}
#[test]
fn eta_multiplier_at_negated_s_generator() {
let neg_s = eta_group_from_ints(0, 1, -1, 0);
let expected = Complex::new(0.0, std::f64::consts::PI / 4.0).exp();
for tau in generic_taus() {
let eps = neg_s.multiplier_system(&tau);
assert!((eps - expected).norm() < 1e-9);
}
}
#[test]
fn eta_multiplier_at_negated_t_generator() {
let neg_t = eta_group_from_ints(-1, -1, 0, -1);
let expected = Complex::new(0.0, -5.0 * std::f64::consts::PI / 12.0).exp();
for tau in generic_taus() {
let eps = neg_t.multiplier_system(&tau);
assert!((eps - expected).norm() < 1e-9);
}
}
}