1use std::sync::{Arc, OnceLock};
13
14use num_bigint::{BigInt, BigUint};
15use num_traits::One;
16
17use crate::primes::primes_up_to;
18
19pub const PRIME_MIN: u32 = 1 << 15;
21pub const PRIME_MAX: u32 = 1 << 16;
23
24fn gpu_primes() -> &'static Vec<u32> {
26 static PRIMES: OnceLock<Vec<u32>> = OnceLock::new();
27 PRIMES.get_or_init(|| {
28 primes_up_to((PRIME_MAX - 1) as usize)
29 .into_iter()
30 .map(|p| p as u32)
31 .filter(|&p| p > PRIME_MIN)
32 .collect()
33 })
34}
35
36#[derive(Clone, Debug)]
38pub struct Basis {
39 primes: Arc<Vec<u32>>,
40}
41
42impl Basis {
43 pub fn from_count(n: usize) -> Self {
45 let all = gpu_primes();
46 assert!(n <= all.len(), "requested more primes than are GPU-eligible");
47 Basis { primes: Arc::new(all[..n].to_vec()) }
48 }
49
50 pub fn standard() -> Self {
53 Self::with_bits(512)
54 }
55
56 pub fn with_bits(bits: u64) -> Self {
58 let all = gpu_primes();
59 let mut product = BigUint::one();
60 let target = BigUint::one() << bits as usize;
61 let mut take = 0;
62 for &p in all.iter() {
63 if product > target {
64 break;
65 }
66 product *= BigUint::from(p);
67 take += 1;
68 }
69 Basis { primes: Arc::new(all[..take].to_vec()) }
70 }
71
72 #[inline]
74 pub fn len(&self) -> usize {
75 self.primes.len()
76 }
77
78 #[inline]
80 pub fn is_empty(&self) -> bool {
81 self.primes.is_empty()
82 }
83
84 #[inline]
86 pub fn modulus(&self, i: usize) -> u32 {
87 self.primes[i]
88 }
89
90 #[inline]
92 pub fn moduli(&self) -> &[u32] {
93 &self.primes
94 }
95
96 pub fn capacity_bits(&self) -> u64 {
98 let product = self.modulus_product();
99 if product <= BigUint::one() {
100 0
101 } else {
102 product.bits() - 1
103 }
104 }
105
106 pub fn modulus_product(&self) -> BigUint {
108 self.primes.iter().map(|&p| BigUint::from(p)).product()
109 }
110
111 pub fn capacity(&self) -> BigUint {
113 self.modulus_product()
114 }
115
116 pub fn signed_capacity(&self) -> BigInt {
118 BigInt::from(self.modulus_product() / BigUint::from(2u8))
119 }
120
121 pub fn extend_to_bits(&self, bits: u64) -> Basis {
124 if self.capacity_bits() >= bits {
125 return self.clone();
126 }
127 Basis::with_bits(bits)
128 }
129}
130
131impl PartialEq for Basis {
132 fn eq(&self, other: &Self) -> bool {
133 Arc::ptr_eq(&self.primes, &other.primes) || self.primes == other.primes
134 }
135}
136impl Eq for Basis {}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn primes_are_gpu_eligible() {
144 let b = Basis::from_count(10);
145 for i in 0..b.len() {
146 let p = b.modulus(i);
147 assert!(p > PRIME_MIN && p < PRIME_MAX);
148 }
149 }
150
151 #[test]
152 fn with_bits_exceeds_target() {
153 let b = Basis::with_bits(166); assert!(b.capacity_bits() >= 166);
155 assert!(b.len() <= 12 && b.len() >= 10);
157 }
158
159 #[test]
160 fn extend_grows() {
161 let small = Basis::with_bits(50);
162 let big = small.extend_to_bits(500);
163 assert!(big.capacity_bits() >= 500);
164 assert!(big.len() > small.len());
165 }
166}