use super::ball::ComplexBall;
use super::error::ThetaError;
use super::{backend, evaluate_at, Precision, MAX_GENUS};
pub fn theta_characteristic_index(a: &[u8], b: &[u8]) -> Result<u64, ThetaError> {
if a.len() != b.len() {
return Err(ThetaError::DimensionMismatch {
what: "theta characteristic halves",
expected: a.len(),
got: b.len(),
});
}
let g = a.len();
check_genus(g)?;
let mut ab: u64 = 0;
for &bit in a {
ab = (ab << 1) | u64::from(bit != 0);
}
for &bit in b {
ab = (ab << 1) | u64::from(bit != 0);
}
Ok(ab)
}
pub fn theta_characteristic_bits(ab: u64, genus: usize) -> Result<(Vec<u8>, Vec<u8>), ThetaError> {
check_genus(genus)?;
check_characteristic(ab, genus)?;
let mut a = vec![0u8; genus];
let mut b = vec![0u8; genus];
for k in 0..genus {
a[k] = u8::from((ab >> (2 * genus - 1 - k)) & 1 == 1);
b[k] = u8::from((ab >> (genus - 1 - k)) & 1 == 1);
}
Ok((a, b))
}
pub fn theta_characteristic_is_even(ab: u64, genus: usize) -> Result<bool, ThetaError> {
check_genus(genus)?;
check_characteristic(ab, genus)?;
let mask = (1u64 << genus) - 1;
let a = (ab >> genus) & mask;
let b = ab & mask;
Ok((a & b).count_ones() % 2 == 0)
}
fn check_genus(genus: usize) -> Result<(), ThetaError> {
if genus == 0 || genus > MAX_GENUS {
return Err(ThetaError::GenusOutOfRange {
genus,
max: MAX_GENUS,
});
}
Ok(())
}
fn check_characteristic(ab: u64, genus: usize) -> Result<(), ThetaError> {
if ab >= 1u64 << (2 * genus) {
return Err(ThetaError::CharacteristicOutOfRange { ab, genus });
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct SiegelMatrix {
genus: usize,
entries: Vec<ComplexBall>,
}
impl SiegelMatrix {
pub fn new(genus: usize, entries: Vec<ComplexBall>) -> Result<Self, ThetaError> {
check_genus(genus)?;
if entries.len() != genus * genus {
return Err(ThetaError::DimensionMismatch {
what: "period matrix",
expected: genus * genus,
got: entries.len(),
});
}
for i in 0..genus {
for j in (i + 1)..genus {
if !same_ball(&entries[i * genus + j], &entries[j * genus + i]) {
return Err(ThetaError::NotSymmetric { i, j });
}
}
}
Ok(SiegelMatrix { genus, entries })
}
pub fn from_upper_triangle(genus: usize, upper: Vec<ComplexBall>) -> Result<Self, ThetaError> {
check_genus(genus)?;
let want = genus * (genus + 1) / 2;
if upper.len() != want {
return Err(ThetaError::DimensionMismatch {
what: "period matrix upper triangle",
expected: want,
got: upper.len(),
});
}
let mut entries: Vec<Option<ComplexBall>> = vec![None; genus * genus];
let mut k = 0;
for i in 0..genus {
for j in i..genus {
entries[i * genus + j] = Some(upper[k].clone());
entries[j * genus + i] = Some(upper[k].clone());
k += 1;
}
}
Ok(SiegelMatrix {
genus,
entries: entries
.into_iter()
.map(|e| e.expect("filled above"))
.collect(),
})
}
pub fn genus_one(tau: ComplexBall) -> Self {
SiegelMatrix {
genus: 1,
entries: vec![tau],
}
}
pub fn genus(&self) -> usize {
self.genus
}
pub fn entry(&self, i: usize, j: usize) -> Option<&ComplexBall> {
if i >= self.genus || j >= self.genus {
return None;
}
Some(&self.entries[i * self.genus + j])
}
pub fn entries(&self) -> &[ComplexBall] {
&self.entries
}
pub fn is_certainly_in_siegel_upper_half_space(&self, prec: u32) -> Result<bool, ThetaError> {
super::arith::check_precision(prec)?;
backend::siegel_positive_definite(&self.entries, self.genus, prec)
}
}
fn same_ball(x: &ComplexBall, y: &ComplexBall) -> bool {
x.midpoint_re() == y.midpoint_re()
&& x.radius_re() == y.radius_re()
&& x.midpoint_im() == y.midpoint_im()
&& x.radius_im() == y.radius_im()
}
#[derive(Clone, Debug)]
pub struct ThetaValues {
genus: usize,
squared: bool,
values: Vec<ComplexBall>,
}
impl ThetaValues {
pub fn genus(&self) -> usize {
self.genus
}
pub fn is_squared(&self) -> bool {
self.squared
}
pub fn len(&self) -> usize {
self.values.len()
}
pub fn is_empty(&self) -> bool {
self.values.is_empty()
}
pub fn get(&self, ab: u64) -> Option<&ComplexBall> {
usize::try_from(ab).ok().and_then(|i| self.values.get(i))
}
pub fn get_characteristic(&self, a: &[u8], b: &[u8]) -> Result<&ComplexBall, ThetaError> {
if a.len() != self.genus {
return Err(ThetaError::DimensionMismatch {
what: "theta characteristic",
expected: self.genus,
got: a.len(),
});
}
let ab = theta_characteristic_index(a, b)?;
check_characteristic(ab, self.genus)?;
self.get(ab).ok_or(ThetaError::CharacteristicOutOfRange {
ab,
genus: self.genus,
})
}
pub fn values(&self) -> &[ComplexBall] {
&self.values
}
pub fn into_values(self) -> Vec<ComplexBall> {
self.values
}
pub fn worst_accuracy_bits(&self) -> i64 {
self.values
.iter()
.map(ComplexBall::accuracy_bits)
.min()
.unwrap_or(i64::MAX)
}
}
fn prepare(
function: &'static str,
z: &[ComplexBall],
tau: &SiegelMatrix,
bits: u32,
) -> Result<(), ThetaError> {
if z.len() != tau.genus() {
return Err(ThetaError::DimensionMismatch {
what: "theta argument z",
expected: tau.genus(),
got: z.len(),
});
}
let _ = function;
if !tau.is_certainly_in_siegel_upper_half_space(bits)? {
return Err(ThetaError::NotInSiegelUpperHalfSpace {
genus: tau.genus(),
prec: bits,
});
}
Ok(())
}
fn theta_vector(
function: &'static str,
z: &[ComplexBall],
tau: &SiegelMatrix,
sqr: bool,
precision: Precision,
) -> Result<ThetaValues, ThetaError> {
check_genus(tau.genus())?;
let values = evaluate_at(function, precision, |bits| {
prepare(function, z, tau, bits)?;
backend::theta_all(z, tau.entries(), tau.genus(), sqr, bits)
})?;
Ok(ThetaValues {
genus: tau.genus(),
squared: sqr,
values,
})
}
pub fn riemann_theta(
z: &[ComplexBall],
tau: &SiegelMatrix,
precision: Precision,
) -> Result<ThetaValues, ThetaError> {
theta_vector("riemann_theta", z, tau, false, precision)
}
pub fn riemann_theta_squared(
z: &[ComplexBall],
tau: &SiegelMatrix,
precision: Precision,
) -> Result<ThetaValues, ThetaError> {
theta_vector("riemann_theta_squared", z, tau, true, precision)
}
pub fn riemann_theta_characteristic(
z: &[ComplexBall],
tau: &SiegelMatrix,
ab: u64,
precision: Precision,
) -> Result<ComplexBall, ThetaError> {
let g = tau.genus();
check_genus(g)?;
check_characteristic(ab, g)?;
evaluate_at("riemann_theta_characteristic", precision, |bits| {
prepare("riemann_theta_characteristic", z, tau, bits)?;
backend::theta_one(z, tau.entries(), g, ab, bits)
})
}
#[derive(Clone, Debug)]
pub struct SiegelReduction {
genus: usize,
symplectic: Vec<rug::Integer>,
reduced: SiegelMatrix,
}
impl SiegelReduction {
pub fn genus(&self) -> usize {
self.genus
}
pub fn symplectic(&self) -> &[rug::Integer] {
&self.symplectic
}
pub fn symplectic_entry(&self, i: usize, j: usize) -> Option<&rug::Integer> {
let n = 2 * self.genus;
if i >= n || j >= n {
return None;
}
Some(&self.symplectic[i * n + j])
}
pub fn reduced(&self) -> &SiegelMatrix {
&self.reduced
}
}
pub fn siegel_reduce(tau: &SiegelMatrix, prec: u32) -> Result<SiegelReduction, ThetaError> {
super::arith::check_precision(prec)?;
let g = tau.genus();
check_genus(g)?;
let (symplectic, reduced) = backend::siegel_reduce(tau.entries(), g, prec)?;
Ok(SiegelReduction {
genus: g,
symplectic,
reduced: SiegelMatrix {
genus: g,
entries: reduced,
},
})
}
pub fn siegel_is_reduced(tau: &SiegelMatrix, tol_exp: i64, prec: u32) -> Result<bool, ThetaError> {
super::arith::check_precision(prec)?;
check_genus(tau.genus())?;
backend::siegel_is_reduced(tau.entries(), tau.genus(), tol_exp, prec)
}