use std::sync::{Arc, OnceLock};
use num_bigint::{BigInt, BigUint};
use num_traits::One;
use crate::primes::primes_up_to;
pub const PRIME_MIN: u32 = 1 << 15;
pub const PRIME_MAX: u32 = 1 << 16;
fn gpu_primes() -> &'static Vec<u32> {
static PRIMES: OnceLock<Vec<u32>> = OnceLock::new();
PRIMES.get_or_init(|| {
primes_up_to((PRIME_MAX - 1) as usize)
.into_iter()
.map(|p| p as u32)
.filter(|&p| p > PRIME_MIN)
.collect()
})
}
#[derive(Clone, Debug)]
pub struct Basis {
primes: Arc<Vec<u32>>,
}
impl Basis {
pub fn from_count(n: usize) -> Self {
let all = gpu_primes();
assert!(n <= all.len(), "requested more primes than are GPU-eligible");
Basis { primes: Arc::new(all[..n].to_vec()) }
}
pub fn standard() -> Self {
Self::with_bits(512)
}
pub fn with_bits(bits: u64) -> Self {
let all = gpu_primes();
let mut product = BigUint::one();
let target = BigUint::one() << bits as usize;
let mut take = 0;
for &p in all.iter() {
if product > target {
break;
}
product *= BigUint::from(p);
take += 1;
}
Basis { primes: Arc::new(all[..take].to_vec()) }
}
#[inline]
pub fn len(&self) -> usize {
self.primes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.primes.is_empty()
}
#[inline]
pub fn modulus(&self, i: usize) -> u32 {
self.primes[i]
}
#[inline]
pub fn moduli(&self) -> &[u32] {
&self.primes
}
pub fn capacity_bits(&self) -> u64 {
let product = self.modulus_product();
if product <= BigUint::one() {
0
} else {
product.bits() - 1
}
}
pub fn modulus_product(&self) -> BigUint {
self.primes.iter().map(|&p| BigUint::from(p)).product()
}
pub fn capacity(&self) -> BigUint {
self.modulus_product()
}
pub fn signed_capacity(&self) -> BigInt {
BigInt::from(self.modulus_product() / BigUint::from(2u8))
}
pub fn extend_to_bits(&self, bits: u64) -> Basis {
if self.capacity_bits() >= bits {
return self.clone();
}
Basis::with_bits(bits)
}
}
impl PartialEq for Basis {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.primes, &other.primes) || self.primes == other.primes
}
}
impl Eq for Basis {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn primes_are_gpu_eligible() {
let b = Basis::from_count(10);
for i in 0..b.len() {
let p = b.modulus(i);
assert!(p > PRIME_MIN && p < PRIME_MAX);
}
}
#[test]
fn with_bits_exceeds_target() {
let b = Basis::with_bits(166); assert!(b.capacity_bits() >= 166);
assert!(b.len() <= 12 && b.len() >= 10);
}
#[test]
fn extend_grows() {
let small = Basis::with_bits(50);
let big = small.extend_to_bits(500);
assert!(big.capacity_bits() >= 500);
assert!(big.len() > small.len());
}
}