1extern crate rayon;
37extern crate rug;
38
39#[macro_use]
40extern crate log;
41extern crate env_logger;
42
43mod utils;
44
45use rayon::prelude::*;
46use rug::Integer;
47use std::error::Error;
48use std::fmt;
49use std::fmt::Display;
50use utils::*;
51
52#[derive(Debug, PartialEq)]
54pub enum ComputeError {
55 NotEnoughModuli,
58}
59
60impl Display for ComputeError {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 write!(f, "ComputeError: {}", self.description())
63 }
64}
65
66impl Error for ComputeError {
67 fn description(&self) -> &str {
68 match self {
69 ComputeError::NotEnoughModuli => "Not enough moduli",
70 }
71 }
72}
73
74pub type ComputeResult = Result<Vec<Option<Integer>>, ComputeError>;
75
76struct ProductTree {
77 levels: Vec<Vec<Integer>>,
78}
79
80fn compute_product_tree(moduli: Vec<Integer>) -> ProductTree {
81 if moduli.len() == 1 {
83 return ProductTree {
84 levels: vec![moduli],
85 };
86 }
87
88 let level = (0..(moduli.len() / 2))
90 .into_par_iter()
91 .map(|i| Integer::from(&moduli[i * 2] * &moduli[i * 2 + 1]))
92 .collect();
93
94 let mut res = compute_product_tree(level);
95 res.levels.push(moduli);
96 res
97}
98
99fn compute_remainders(tree: ProductTree) -> Option<Vec<Integer>> {
100 let level_count = tree.levels.len() - 1;
101 trace!("computing remainders for {} levels", level_count);
102
103 tree.levels
104 .into_iter()
105 .enumerate()
106 .fold(None, |maybe_parent, (level, current)| {
107 let parent = match maybe_parent {
108 None => {
109 return Some(current);
110 }
111 Some(parent) => parent,
112 };
113
114 trace!("computing remainder level {}/{}", level, level_count);
115 let remainders = current
116 .into_par_iter()
117 .enumerate()
118 .map(|(i, mut value)| {
119 value.square_mut();
121
122 &parent[i / 2] % value
123 })
124 .collect();
125
126 Some(remainders)
127 })
128}
129
130fn compute_gcds(remainders: &[Integer], moduli: &[Integer]) -> Vec<Integer> {
131 trace!("computing quotients and gcd");
132 remainders
133 .par_iter()
134 .zip(moduli.par_iter())
135 .map(|(remainder, modulo)| {
136 let quotient = Integer::from(remainder / modulo);
137 quotient.gcd(modulo)
138 })
139 .collect()
140}
141
142pub fn compute(moduli: &[Integer]) -> ComputeResult {
187 if moduli.len() < 2 {
188 return Err(ComputeError::NotEnoughModuli);
189 }
190
191 let (padded_moduli, pad_size) = pad_ints(moduli.to_vec());
193 trace!("added {} padding to moduli", pad_size);
194
195 trace!("computing product tree");
196 let tree = compute_product_tree(padded_moduli);
197 let remainders = compute_remainders(tree);
198
199 let gcds = compute_gcds(&unpad_ints(remainders.unwrap(), pad_size), moduli);
200
201 Ok(gcds
202 .into_iter()
203 .map(|gcd| if gcd == 1 { None } else { Some(gcd) })
204 .collect())
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 #[test]
212 fn it_should_fail_on_zero_moduli() {
213 assert!(compute(&[]).is_err());
214 }
215
216 #[test]
217 fn it_should_fail_on_single_moduli() {
218 assert!(compute(&[Integer::new()]).is_err());
219 }
220
221 #[test]
222 fn it_should_return_gcd_of_two_moduli() {
223 let moduli = [Integer::from(6), Integer::from(15)];
224
225 let result = compute(&moduli).unwrap();
226 assert_eq!(
227 result,
228 vec![Some(Integer::from(3)), Some(Integer::from(3)),]
229 );
230 }
231
232 #[test]
233 fn it_should_find_gcd_for_many_moduli() {
234 let moduli = vec![
235 Integer::from(31 * 41),
236 Integer::from(41),
237 Integer::from(61),
238 Integer::from(71 * 31),
239 Integer::from(101 * 131),
240 Integer::from(131 * 151),
241 ];
242
243 let result = compute(&moduli).unwrap();
244
245 assert_eq!(
246 result,
247 vec![
248 Some(Integer::from(31 * 41)),
249 Some(Integer::from(41)),
250 None,
251 Some(Integer::from(31)),
252 Some(Integer::from(131)),
253 Some(Integer::from(131)),
254 ]
255 );
256 }
257}