pub mod algebraic {
pub mod field;
pub mod lattice;
pub mod section2;
pub mod units;
pub mod window;
}
pub mod backend;
pub mod certificate;
pub mod classical;
pub mod error;
mod numeric;
pub mod utils;
use crate::algebraic::field::MultiQuadraticField;
use crate::algebraic::section2::generate_native_multiquadratic_section2;
pub use crate::certificate::{
CertificateVerificationReport, CertifiedPointSet, ClassicalCertificate,
ClassicalConstructionKind, ConstructionCertificate, FloatingAuditReport,
MultiquadraticCertificate, Point2,
};
use crate::classical::{generate_moser_spindle, generate_square_grid, generate_triangular_grid};
pub use crate::error::{GenerationError, VerificationError};
const DEFAULT_MAX_PRIME_EXPONENT: usize = 3;
const DEFAULT_MAX_RADIUS_ATTEMPTS: usize = 15;
const DEFAULT_INITIAL_RADIUS: f64 = 2.0;
const DEFAULT_RADIUS_GROWTH: f64 = 1.5;
const DEFAULT_CANDIDATE_MULTIPLIER: usize = 10;
const DEFAULT_PROJECTION_TOLERANCE: f64 = 1e-7;
const DEFAULT_UNIT_DISTANCE_TOLERANCE: f64 = 1e-4;
#[derive(Clone, Debug)]
pub struct MultiquadraticConfig {
generators: Vec<i64>,
split_prime: i64,
k: usize,
max_prime_exponent: usize,
max_radius_attempts: usize,
initial_radius: f64,
radius_growth: f64,
candidate_multiplier: usize,
projection_tolerance: f64,
unit_distance_tolerance: f64,
}
impl MultiquadraticConfig {
pub fn builder(
generators: Vec<i64>,
split_prime: i64,
k: usize,
) -> MultiquadraticConfigBuilder {
MultiquadraticConfigBuilder {
generators,
split_prime,
k,
max_prime_exponent: DEFAULT_MAX_PRIME_EXPONENT,
max_radius_attempts: DEFAULT_MAX_RADIUS_ATTEMPTS,
initial_radius: DEFAULT_INITIAL_RADIUS,
radius_growth: DEFAULT_RADIUS_GROWTH,
candidate_multiplier: DEFAULT_CANDIDATE_MULTIPLIER,
projection_tolerance: DEFAULT_PROJECTION_TOLERANCE,
unit_distance_tolerance: DEFAULT_UNIT_DISTANCE_TOLERANCE,
}
}
pub fn new(generators: Vec<i64>, split_prime: i64, k: usize) -> Result<Self, GenerationError> {
let mut generators = generators;
if !generators.contains(&-1) {
generators.push(-1);
}
MultiQuadraticField::try_new(&generators)?;
validate_split_prime(split_prime)?;
validate_complete_splitting(&generators, split_prime)?;
Ok(Self {
generators,
split_prime,
k,
max_prime_exponent: DEFAULT_MAX_PRIME_EXPONENT,
max_radius_attempts: DEFAULT_MAX_RADIUS_ATTEMPTS,
initial_radius: DEFAULT_INITIAL_RADIUS,
radius_growth: DEFAULT_RADIUS_GROWTH,
candidate_multiplier: DEFAULT_CANDIDATE_MULTIPLIER,
projection_tolerance: DEFAULT_PROJECTION_TOLERANCE,
unit_distance_tolerance: DEFAULT_UNIT_DISTANCE_TOLERANCE,
})
}
pub fn generators(&self) -> &[i64] {
&self.generators
}
pub fn split_prime(&self) -> i64 {
self.split_prime
}
pub fn k(&self) -> usize {
self.k
}
pub fn max_prime_exponent(&self) -> usize {
self.max_prime_exponent
}
pub fn max_radius_attempts(&self) -> usize {
self.max_radius_attempts
}
pub fn initial_radius(&self) -> f64 {
self.initial_radius
}
pub fn radius_growth(&self) -> f64 {
self.radius_growth
}
pub fn candidate_multiplier(&self) -> usize {
self.candidate_multiplier
}
pub fn projection_tolerance(&self) -> f64 {
self.projection_tolerance
}
pub fn unit_distance_tolerance(&self) -> f64 {
self.unit_distance_tolerance
}
pub fn with_prime_search_limit(
mut self,
max_prime_exponent: usize,
) -> Result<Self, GenerationError> {
validate_nonzero_usize("max_prime_exponent", max_prime_exponent)?;
self.max_prime_exponent = max_prime_exponent;
Ok(self)
}
pub fn with_radius_search(
mut self,
max_radius_attempts: usize,
initial_radius: f64,
radius_growth: f64,
) -> Result<Self, GenerationError> {
validate_nonzero_usize("max_radius_attempts", max_radius_attempts)?;
validate_positive_f64("initial_radius", initial_radius)?;
if !radius_growth.is_finite() || radius_growth <= 1.0 {
return Err(GenerationError::InvalidSearchParameter {
parameter: "radius_growth",
reason: "expected a finite value greater than 1.0",
});
}
self.max_radius_attempts = max_radius_attempts;
self.initial_radius = initial_radius;
self.radius_growth = radius_growth;
Ok(self)
}
pub fn with_candidate_multiplier(
mut self,
candidate_multiplier: usize,
) -> Result<Self, GenerationError> {
validate_nonzero_usize("candidate_multiplier", candidate_multiplier)?;
self.candidate_multiplier = candidate_multiplier;
Ok(self)
}
pub fn with_tolerances(
mut self,
projection_tolerance: f64,
unit_distance_tolerance: f64,
) -> Result<Self, GenerationError> {
validate_positive_f64("projection_tolerance", projection_tolerance)?;
validate_positive_f64("unit_distance_tolerance", unit_distance_tolerance)?;
self.projection_tolerance = projection_tolerance;
self.unit_distance_tolerance = unit_distance_tolerance;
Ok(self)
}
}
#[derive(Clone, Debug)]
pub struct MultiquadraticConfigBuilder {
generators: Vec<i64>,
split_prime: i64,
k: usize,
max_prime_exponent: usize,
max_radius_attempts: usize,
initial_radius: f64,
radius_growth: f64,
candidate_multiplier: usize,
projection_tolerance: f64,
unit_distance_tolerance: f64,
}
impl MultiquadraticConfigBuilder {
pub fn max_prime_exponent(mut self, value: usize) -> Self {
self.max_prime_exponent = value;
self
}
pub fn radius_search(mut self, attempts: usize, initial_radius: f64, growth: f64) -> Self {
self.max_radius_attempts = attempts;
self.initial_radius = initial_radius;
self.radius_growth = growth;
self
}
pub fn candidate_multiplier(mut self, value: usize) -> Self {
self.candidate_multiplier = value;
self
}
pub fn tolerances(mut self, projection_tolerance: f64, unit_distance_tolerance: f64) -> Self {
self.projection_tolerance = projection_tolerance;
self.unit_distance_tolerance = unit_distance_tolerance;
self
}
pub fn build(self) -> Result<MultiquadraticConfig, GenerationError> {
MultiquadraticConfig::new(self.generators, self.split_prime, self.k)?
.with_prime_search_limit(self.max_prime_exponent)?
.with_radius_search(
self.max_radius_attempts,
self.initial_radius,
self.radius_growth,
)?
.with_candidate_multiplier(self.candidate_multiplier)?
.with_tolerances(self.projection_tolerance, self.unit_distance_tolerance)
}
}
#[derive(Clone, Debug)]
pub enum ConstructionType {
SquareGrid { rows: usize, cols: usize },
TriangularGrid,
MoserSpindle,
Multiquadratic(MultiquadraticConfig),
}
#[derive(Clone, Debug)]
pub struct UnitDistanceSet {
construction: ConstructionType,
}
impl UnitDistanceSet {
pub fn square_grid(rows: usize, cols: usize) -> Self {
UnitDistanceSet {
construction: ConstructionType::SquareGrid { rows, cols },
}
}
pub fn triangular_grid() -> Self {
UnitDistanceSet {
construction: ConstructionType::TriangularGrid,
}
}
pub fn moser_spindle() -> Self {
UnitDistanceSet {
construction: ConstructionType::MoserSpindle,
}
}
pub fn try_new_multiquadratic(
generators: Vec<i64>,
split_prime: i64,
k: usize,
) -> Result<Self, GenerationError> {
Ok(UnitDistanceSet {
construction: ConstructionType::Multiquadratic(MultiquadraticConfig::new(
generators,
split_prime,
k,
)?),
})
}
pub fn multiquadratic(config: MultiquadraticConfig) -> Self {
UnitDistanceSet {
construction: ConstructionType::Multiquadratic(config),
}
}
pub fn construction(&self) -> &ConstructionType {
&self.construction
}
pub fn generate(&self, n_target: usize) -> Result<Vec<[f64; 2]>, GenerationError> {
match &self.construction {
ConstructionType::SquareGrid { rows, cols } => Ok(generate_square_grid(*rows, *cols)),
ConstructionType::TriangularGrid => Ok(generate_triangular_grid(n_target)),
ConstructionType::MoserSpindle => Ok(generate_moser_spindle()),
ConstructionType::Multiquadratic(config) => {
Ok(generate_native_multiquadratic_section2(config, n_target)?.projected_points)
}
}
}
pub fn generate_points(&self, n_target: usize) -> Result<Vec<Point2>, GenerationError> {
Ok(self
.generate(n_target)?
.into_iter()
.map(Point2::from)
.collect())
}
pub fn generate_certified(
&self,
n_target: usize,
) -> Result<CertifiedPointSet, GenerationError> {
Ok(match &self.construction {
ConstructionType::SquareGrid { rows, cols } => {
let points = generate_square_grid(*rows, *cols);
CertifiedPointSet::new_classical(
ClassicalConstructionKind::SquareGrid {
rows: *rows,
cols: *cols,
},
n_target,
points,
DEFAULT_UNIT_DISTANCE_TOLERANCE,
)
}
ConstructionType::TriangularGrid => {
let points = generate_triangular_grid(n_target);
CertifiedPointSet::new_classical(
ClassicalConstructionKind::TriangularGrid,
n_target,
points,
DEFAULT_UNIT_DISTANCE_TOLERANCE,
)
}
ConstructionType::MoserSpindle => {
let points = generate_moser_spindle();
CertifiedPointSet::new_classical(
ClassicalConstructionKind::MoserSpindle,
n_target,
points,
DEFAULT_UNIT_DISTANCE_TOLERANCE,
)
}
ConstructionType::Multiquadratic(config) => {
CertifiedPointSet::new_algebraic_from_section2(
generate_native_multiquadratic_section2(config, n_target)?,
)?
}
})
}
}
fn validate_split_prime(split_prime: i64) -> Result<(), GenerationError> {
if split_prime <= 2 || !is_prime(split_prime) {
return Err(GenerationError::InvalidSplitPrime { split_prime });
}
Ok(())
}
fn validate_nonzero_usize(parameter: &'static str, value: usize) -> Result<(), GenerationError> {
if value == 0 {
return Err(GenerationError::InvalidSearchParameter {
parameter,
reason: "expected a value greater than zero",
});
}
Ok(())
}
fn validate_positive_f64(parameter: &'static str, value: f64) -> Result<(), GenerationError> {
if !value.is_finite() || value <= 0.0 {
return Err(GenerationError::InvalidSearchParameter {
parameter,
reason: "expected a finite value greater than zero",
});
}
Ok(())
}
fn validate_complete_splitting(
generators: &[i64],
split_prime: i64,
) -> Result<(), GenerationError> {
generators.iter().try_for_each(|&generator| {
if legendre_symbol(generator, split_prime) == 1 {
Ok(())
} else {
Err(GenerationError::PrimeNotSplit {
split_prime,
generator,
})
}
})
}
fn is_prime(n: i64) -> bool {
if n < 2 {
return false;
}
if n == 2 {
return true;
}
if n % 2 == 0 {
return false;
}
!(3..)
.step_by(2)
.take_while(|d| d * d <= n)
.any(|d| n % d == 0)
}
fn legendre_symbol(a: i64, p: i64) -> i64 {
let a = a.rem_euclid(p);
if a == 0 {
return 0;
}
let value = mod_pow(a, (p - 1) / 2, p);
if value == p - 1 { -1 } else { value }
}
fn mod_pow(mut base: i64, exp: i64, modulus: i64) -> i64 {
base = base.rem_euclid(modulus);
std::iter::successors(Some((base, exp)), |&(base, exp)| {
(exp / 2 > 0).then_some(((base * base).rem_euclid(modulus), exp / 2))
})
.filter(|&(_, exp)| exp % 2 == 1)
.fold(1, |result, (base, _)| (result * base).rem_euclid(modulus))
}