use std::marker::PhantomData;
use num::Zero;
use super::modular_def::{ModularError, ModularForm, ModularTransformationGroup};
use crate::arithmetic_utils::Field;
pub enum CoerceTransformation<
R: Field,
T1: ModularTransformationGroup<R>,
T2: ModularTransformationGroup<R>,
> {
FORCED(PhantomData<R>, PhantomData<T1>, PhantomData<T2>),
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn sample_points<R: Field + nalgebra::ComplexField>() -> Vec<R> {
let rational = |num: i64, den: i64| -> R {
let magnitude = R::natural_inclusion(num.unsigned_abs() as usize)
* R::natural_inclusion(den.unsigned_abs() as usize).inv();
if num < 0 { -magnitude } else { magnitude }
};
let mut imaginary_unit = (-R::one()).sqrt();
if imaginary_unit.clone().imaginary() < <R as nalgebra::ComplexField>::RealField::zero() {
imaginary_unit = -imaginary_unit;
}
let mut state: u64 = 0x9E37_79B9_7F4A_7C15;
#[allow(clippy::cast_possible_wrap)]
let mut next_i64 = || {
state = state
.wrapping_mul(6_364_136_223_846_793_005)
.wrapping_add(1_442_695_040_888_963_407);
(state >> 33) as i64
};
(0..8)
.map(|_| {
let re_num = next_i64() % 41 - 20;
let re_den = next_i64().rem_euclid(9) + 1;
let im_num = next_i64().rem_euclid(37) + 1; let im_den = next_i64().rem_euclid(9) + 1;
rational(re_num, re_den) + imaginary_unit.clone() * rational(im_num, im_den)
})
.collect()
}
impl<R: Field, T1: ModularTransformationGroup<R>, T2: ModularTransformationGroup<R>>
CoerceTransformation<R, T1, T2>
{
#[allow(clippy::similar_names)]
pub fn validate(
self,
gens_t1: &[T1],
gens_t2: &[T2],
close_enough: fn(&R, &R) -> bool,
) -> Result<Self, ModularError>
where
R: nalgebra::ComplexField,
{
let taus = sample_points::<R>();
for gen_t1 in gens_t1 {
let gen_t2 = T2::new(gen_t1.raw_matrix())
.map_err(|_| ModularError::InequivalentTransformationGroups)?;
for tau in &taus {
let mult_t1 = gen_t1.multiplier_system(tau);
let mult_t2 = gen_t2.multiplier_system(tau);
if !close_enough(&mult_t1, &mult_t2) {
return Err(ModularError::InequivalentTransformationGroups);
}
}
}
for gen_t2 in gens_t2 {
let gen_t1 = T1::new(gen_t2.raw_matrix())
.map_err(|_| ModularError::InequivalentTransformationGroups)?;
for tau in &taus {
let mult_t1 = gen_t1.multiplier_system(tau);
let mult_t2 = gen_t2.multiplier_system(tau);
if !close_enough(&mult_t1, &mult_t2) {
return Err(ModularError::InequivalentTransformationGroups);
}
}
}
Ok(self)
}
}
pub struct EquivalentTransportedMF<
const TWICE_WEIGHT: usize,
R: Field,
M: ModularForm<TWICE_WEIGHT, R>,
TG: ModularTransformationGroup<R>,
> {
#[allow(dead_code)]
coercion: CoerceTransformation<R, M::TransformationGroup, TG>,
underlying: M,
}
impl<
const TWICE_WEIGHT: usize,
R: Field,
M: ModularForm<TWICE_WEIGHT, R>,
TG: ModularTransformationGroup<R>,
> EquivalentTransportedMF<TWICE_WEIGHT, R, M, TG>
{
pub fn coerce(
underlying: M,
coercion: CoerceTransformation<R, M::TransformationGroup, TG>,
) -> Self {
Self {
coercion,
underlying,
}
}
}
impl<
const TWICE_WEIGHT: usize,
R: Field,
M: ModularForm<TWICE_WEIGHT, R>,
TG: ModularTransformationGroup<R>,
> ModularForm<TWICE_WEIGHT, R> for EquivalentTransportedMF<TWICE_WEIGHT, R, M, TG>
{
type TransformationGroup = TG;
fn extract_coeffs(&self, which_coeff: usize) -> Result<R, ModularError> {
self.underlying.extract_coeffs(which_coeff)
}
fn evaluate_at(&self, q: &R) -> Result<R, ModularError> {
self.underlying.evaluate_at(q)
}
}