1use num_bigint::{BigInt, BigUint, Sign};
19use num_traits::Zero;
20
21use crate::basis::Basis;
22use crate::primes::mod_inverse;
23use crate::RAYON_CHANNEL_THRESHOLD;
24
25#[derive(Clone, Debug)]
27pub struct RnsInt {
28 pub residues: Vec<u32>,
30 pub basis: Basis,
31}
32
33impl RnsInt {
34 pub fn from_bigint(n: &BigInt, basis: Basis) -> Self {
36 let residues = basis
37 .moduli()
38 .iter()
39 .map(|&m| {
40 let mm = BigInt::from(m);
41 let r = ((n % &mm) + &mm) % &mm;
42 r.to_biguint().unwrap().try_into().unwrap()
43 })
44 .collect();
45 RnsInt { residues, basis }
46 }
47
48 pub fn from_i64(n: i64, basis: Basis) -> Self {
50 Self::from_bigint(&BigInt::from(n), basis)
51 }
52
53 pub fn zero(basis: Basis) -> Self {
55 RnsInt { residues: vec![0; basis.len()], basis }
56 }
57
58 pub fn from_residues(residues: Vec<u32>, basis: Basis) -> Self {
60 RnsInt { residues, basis }
61 }
62
63 pub fn to_bigint(&self) -> BigInt {
65 let u = garner_crt(&self.residues, self.basis.moduli());
66 let m = self.basis.modulus_product();
67 if &u * 2u8 > m {
68 BigInt::from_biguint(Sign::Plus, u) - BigInt::from_biguint(Sign::Plus, m)
69 } else {
70 BigInt::from_biguint(Sign::Plus, u)
71 }
72 }
73
74 pub fn is_zero(&self) -> bool {
76 self.residues.iter().all(|&r| r == 0)
77 }
78
79 pub fn add(&self, other: &Self) -> Self {
81 let out = channel_map(&self.residues, &other.residues, self.basis.moduli(), add_channel);
82 Self::from_residues(out, self.basis.clone())
83 }
84
85 pub fn sub(&self, other: &Self) -> Self {
87 let out = channel_map(&self.residues, &other.residues, self.basis.moduli(), sub_channel);
88 Self::from_residues(out, self.basis.clone())
89 }
90
91 pub fn mul(&self, other: &Self) -> Self {
93 let out = channel_map(&self.residues, &other.residues, self.basis.moduli(), mul_channel);
94 Self::from_residues(out, self.basis.clone())
95 }
96
97 pub fn neg(&self) -> Self {
99 let out: Vec<u32> = self
100 .residues
101 .iter()
102 .zip(self.basis.moduli())
103 .map(|(&r, &m)| (m - r) % m)
104 .collect();
105 Self::from_residues(out, self.basis.clone())
106 }
107}
108
109fn channel_map(
111 a: &[u32],
112 b: &[u32],
113 moduli: &[u32],
114 f: impl Fn(u32, u32, u32) -> u32 + Sync + Send,
115) -> Vec<u32> {
116 use rayon::prelude::*;
117 if a.len() >= RAYON_CHANNEL_THRESHOLD {
118 a.par_iter()
119 .zip(b.par_iter())
120 .zip(moduli.par_iter())
121 .map(|((&av, &bv), &m)| f(av, bv, m))
122 .collect()
123 } else {
124 a.iter()
125 .zip(b.iter())
126 .zip(moduli.iter())
127 .map(|((&av, &bv), &m)| f(av, bv, m))
128 .collect()
129 }
130}
131
132#[inline]
134pub fn add_channel(a: u32, b: u32, m: u32) -> u32 {
135 (a + b) % m
136}
137
138#[inline]
140pub fn sub_channel(a: u32, b: u32, m: u32) -> u32 {
141 (a + m - b) % m
142}
143
144#[inline]
146pub fn mul_channel(a: u32, b: u32, m: u32) -> u32 {
147 (a * b) % m
148}
149
150pub fn garner_crt(residues: &[u32], moduli: &[u32]) -> BigUint {
156 let k = residues.len();
157 assert_eq!(k, moduli.len(), "residue/moduli length mismatch");
158 if k == 0 {
159 return BigUint::zero();
160 }
161
162 let mut c: Vec<u64> = residues.iter().map(|&r| r as u64).collect();
163 for i in 0..k {
164 let mi = moduli[i] as u64;
165 for j in 0..i {
166 let inv = mod_inverse(moduli[j] as u64 % mi, mi)
167 .expect("basis primes must be pairwise coprime for CRT");
168 let diff = (c[i] + mi - (c[j] % mi)) % mi;
169 c[i] = (diff * inv) % mi;
170 }
171 }
172
173 let mut result = BigUint::from(c[k - 1]);
174 for i in (0..k - 1).rev() {
175 result = result * BigUint::from(moduli[i]) + BigUint::from(c[i]);
176 }
177 result
178}
179
180pub fn crt_balanced(residues: &[u32], moduli: &[u32]) -> BigInt {
182 let u = garner_crt(residues, moduli);
183 let m: BigUint = moduli.iter().map(|&p| BigUint::from(p)).product();
184 if &u * 2u8 > m {
185 BigInt::from_biguint(Sign::Plus, u) - BigInt::from_biguint(Sign::Plus, m)
186 } else {
187 BigInt::from_biguint(Sign::Plus, u)
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 fn b() -> Basis {
196 Basis::standard()
197 }
198
199 #[test]
200 fn roundtrip_positive() {
201 let a = RnsInt::from_i64(123_456_789, b());
202 assert_eq!(a.to_bigint(), BigInt::from(123_456_789));
203 }
204
205 #[test]
206 fn roundtrip_negative() {
207 let a = RnsInt::from_i64(-42, b());
208 assert_eq!(a.to_bigint(), BigInt::from(-42));
209 }
210
211 #[test]
212 fn add_sub_mul() {
213 let a = RnsInt::from_i64(1000, b());
214 let bb = RnsInt::from_i64(337, b());
215 assert_eq!(a.add(&bb).to_bigint(), BigInt::from(1337));
216 assert_eq!(a.sub(&bb).to_bigint(), BigInt::from(663));
217 assert_eq!(bb.sub(&a).to_bigint(), BigInt::from(-663));
218 assert_eq!(a.mul(&bb).to_bigint(), BigInt::from(337_000));
219 }
220
221 #[test]
222 fn garner_classic() {
223 assert_eq!(garner_crt(&[2, 3, 2], &[3, 5, 7]), BigUint::from(23u8));
225 assert_eq!(garner_crt(&[0, 1, 0], &[2, 3, 5]), BigUint::from(10u8));
227 }
228
229 #[test]
230 fn is_zero_works() {
231 assert!(RnsInt::zero(b()).is_zero());
232 assert!(!RnsInt::from_i64(1, b()).is_zero());
233 }
234}