smooth_numbers/
lib.rs

1/*! Algorithms to generate smooth numbers.
2
3See the definition of *smooth number* on
4[Wikipedia](https://en.wikipedia.org/wiki/Smooth_number) and
5[MathWorld](https://mathworld.wolfram.com/SmoothNumber.html).
6
7# Examples
8
9Generate the first 10 3-smooth numbers, i.e. numbers of the form `2^i * 3^j`:
10```
11use smooth_numbers::smooth;
12assert_eq!(smooth(3, 10), [1, 2, 3, 4, 6, 8, 9, 12, 16, 18]);
13```
14
15Generate the first 10 5-smooth numbers, i.e. numbers of the form `2^i * 3^j * 5^k`:
16```
17use smooth_numbers::smooth;
18assert_eq!(smooth(5, 10), [1, 2, 3, 4, 5, 6, 8, 9, 10, 12]);
19```
20
21Generate the first 10 numbers of the form `2^i * 5^j`:
22```
23use smooth_numbers::with_primes;
24assert_eq!(with_primes(&[2, 5], 10), [1, 2, 4, 5, 8, 10, 16, 20, 25, 32]);
25```
26*/
27
28#![cfg_attr(docsrs, feature(doc_auto_cfg))]
29
30/** Generates the first `n` numbers in the Pratt's sequence.
31
32Pratt's sequence is the sequence of numbers of the form `2^i * 3^j`,
33with `i` and `j` natural numbers. These are also called [3-smooth numbers] and
34they are indexed in OEIS as [A003586](https://oeis.org/A003586).
35
36They provide the best-known sequence of gaps for [Shellsort] (best worst-case time complexity).
37
38[`pratt`]`(n)` is equivalent to [`smooth`]`(3, n)`, but faster.
39
40[3-smooth numbers]: https://mathworld.wolfram.com/SmoothNumber.html
41[Shellsort]: https://en.wikipedia.org/wiki/Shellsort#Gap_sequences
42*/
43pub fn pratt(n: usize) -> Vec<u64> {
44    if n == 0 {
45        return Vec::new();
46    }
47
48    let mut v = Vec::with_capacity(n);
49    v.push(1);
50
51    let mut two = 0;
52    let mut three = 0;
53
54    for _ in 1..n {
55        let times_two = 2 * v[two];
56        let times_three = 3 * v[three];
57        match (times_two).cmp(&times_three) {
58            std::cmp::Ordering::Less => {
59                v.push(times_two);
60                two += 1;
61            }
62            std::cmp::Ordering::Equal => {
63                v.push(times_two);
64                two += 1;
65                three += 1;
66            }
67            std::cmp::Ordering::Greater => {
68                v.push(times_three);
69                three += 1;
70            }
71        }
72    }
73    v
74}
75
76/** Generates the first `n` `k`-smooth numbers, i.e. numbers whose prime factors
77  are smaller than or equal to `k`.
78
79See the definition of *smooth number* on
80[Wikipedia](https://en.wikipedia.org/wiki/Smooth_number) and
81[MathWorld](https://mathworld.wolfram.com/SmoothNumber.html).
82
83# Examples
84
85With `k < 2`, the numbers cannot have any prime factor,
86hence the only smooth number in this case is `1`.
87
88```
89# use smooth_numbers::smooth;
90assert_eq!(smooth(0, 0), []);
91assert_eq!(smooth(0, 1), [1]);
92assert_eq!(smooth(0, 10), [1]);
93assert_eq!(smooth(1, 10), [1]);
94```
95
96With `k == 2`, the numbers can only have the prime factor `2`,
97hence we obtain the sequence of the powers of `2`.
98
99```
100# use smooth_numbers::smooth;
101assert_eq!(smooth(2, 10), [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]);
102```
103
104With `k == 3`, the numbers can only have the prime factors `2` and `3`,
105hence we obtain the sequence of the numbers of the form `2^i * 3^j`,
106also known as Pratt's sequence.
107See the [`pratt`] function for a specialized algorithm to generate this sequence.
108
109```
110# use smooth_numbers::smooth;
111assert_eq!(smooth(3, 10), [1, 2, 3, 4, 6, 8, 9, 12, 16, 18]);
112```
113*/
114pub fn smooth(k: usize, n: usize) -> Vec<u64> {
115    if n == 0 {
116        return Vec::new();
117    }
118
119    if k < 2 {
120        return vec![1];
121    }
122
123    if k == 2 {
124        let mut v = Vec::with_capacity(n);
125        v.push(1);
126        let mut x = 1;
127        for _ in 1..n {
128            x *= 2;
129            v.push(x);
130        }
131        return v;
132    }
133
134    let sieve = primal::Sieve::new(k);
135    let primes: Vec<u64> = sieve
136        .primes_from(2)
137        .take_while(|&p| p <= k)
138        .map(|p| p as u64)
139        .collect();
140
141    with_primes(&primes, n)
142}
143
144/** Generates the first `n` smooth numbers whose prime factors are among `primes`.
145
146# Examples
147
148If `primes` is empty, the numbers cannot have any prime factor,
149hence the only smooth number in this case is `1`.
150
151```
152# use smooth_numbers::with_primes;
153assert_eq!(with_primes(&[], 0), []);
154assert_eq!(with_primes(&[], 1), [1]);
155assert_eq!(with_primes(&[], 10), [1]);
156```
157
158If `primes` contains a single element, the numbers can only have the prime factor `primes[0]`,
159hence we obtain the sequence of the powers of `primes[0]`.
160
161```
162# use smooth_numbers::with_primes;
163assert_eq!(with_primes(&[2], 10), [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]);
164```
165
166If `primes == &[2, 3]`, the numbers can only have the prime factors `2` and `3`,
167hence we obtain the sequence of the numbers of the form `2^i * 3^j`,
168also known as Pratt's sequence.
169See the [`pratt`] function for a specialized algorithm to generate this sequence.
170
171```
172# use smooth_numbers::with_primes;
173assert_eq!(with_primes(&[2, 3], 10), [1, 2, 3, 4, 6, 8, 9, 12, 16, 18]);
174```
175*/
176pub fn with_primes(primes: &[u64], n: usize) -> Vec<u64> {
177    if n == 0 {
178        return Vec::new();
179    }
180
181    let num_primes = primes.len();
182
183    if num_primes == 0 {
184        return vec![1];
185    }
186
187    if num_primes == 1 {
188        let mut v = Vec::with_capacity(n);
189        v.push(1);
190        let mut x = 1;
191        for _ in 1..n {
192            x *= primes[0];
193            v.push(x);
194        }
195        return v;
196    }
197
198    let mut indices: Vec<usize> = vec![0; num_primes];
199    let mut v = Vec::with_capacity(n);
200    v.push(1);
201
202    for _ in 1..n {
203        let new = primes
204            .iter()
205            .zip(&indices)
206            .map(|(p, &j)| p * v[j])
207            .min()
208            .expect("cannot find next smooth number");
209        v.push(new);
210        for j in 0..num_primes {
211            if primes[j] * v[indices[j]] == new {
212                indices[j] += 1;
213            }
214        }
215    }
216    v
217}
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222
223    #[test]
224    fn pratt_has_correct_length() {
225        for n in [0, 1, 10, 100] {
226            assert_eq!(pratt(n).len(), n);
227        }
228    }
229
230    #[test]
231    fn pratt_has_correct_values() {
232        assert_eq!(pratt(10), [1, 2, 3, 4, 6, 8, 9, 12, 16, 18]);
233        assert_eq!(pratt(100).last(), Some(&93312));
234        // this is the largest possible
235        assert_eq!(pratt(1343).last(), Some(&17748888853923495936));
236        assert_eq!(pratt(1344).last(), Some(&17991041643939889152));
237    }
238
239    #[test]
240    #[should_panic(expected = "attempt to multiply with overflow")]
241    // #[should_panic(expected = "index out of bounds")] // with `overflow-checks = false`
242    fn pratt_first_to_overflow() {
243        let _ = pratt(1345).last();
244    }
245
246    #[test]
247    fn smooth_has_correct_length() {
248        for n in [0, 1, 10, 64] {
249            assert_eq!(smooth(2, n).len(), n);
250        }
251        for n in [0, 1, 10, 100] {
252            assert_eq!(smooth(3, n).len(), n);
253            assert_eq!(smooth(5, n).len(), n);
254        }
255    }
256
257    #[test]
258    fn smooth_has_correct_values() {
259        assert_eq!(smooth(2, 10), [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]);
260        assert_eq!(smooth(3, 10), [1, 2, 3, 4, 6, 8, 9, 12, 16, 18]);
261        assert_eq!(smooth(3, 100).last(), Some(&93312));
262        // this is the largest possible
263        assert_eq!(smooth(3, 1343).last(), Some(&17748888853923495936));
264        assert_eq!(smooth(3, 1344).last(), Some(&17991041643939889152));
265    }
266
267    #[test]
268    #[should_panic(expected = "attempt to multiply with overflow")]
269    fn smooth_1_first_to_overflow() {
270        let _ = smooth(2, 65).last();
271    }
272
273    #[test]
274    #[should_panic(expected = "attempt to multiply with overflow")]
275    fn smooth_2_first_to_overflow() {
276        let _ = smooth(3, 1345).last();
277    }
278
279    #[test]
280    fn with_primes_has_correct_length() {
281        for n in [0, 1, 10, 64] {
282            assert_eq!(with_primes(&[2], n).len(), n);
283        }
284        for n in [0, 1, 10, 100] {
285            assert_eq!(with_primes(&[2, 3], n).len(), n);
286            assert_eq!(with_primes(&[2, 3, 5], n).len(), n);
287        }
288    }
289
290    #[test]
291    fn with_primes_has_correct_values() {
292        assert_eq!(
293            with_primes(&[2], 10),
294            [1, 2, 4, 8, 16, 32, 64, 128, 256, 512]
295        );
296        assert_eq!(with_primes(&[2, 3], 10), [1, 2, 3, 4, 6, 8, 9, 12, 16, 18]);
297        assert_eq!(with_primes(&[2, 3], 100).last(), Some(&93312));
298        // this is the largest possible
299        assert_eq!(
300            with_primes(&[2, 3], 1343).last(),
301            Some(&17748888853923495936)
302        );
303        assert_eq!(
304            with_primes(&[2, 3], 1344).last(),
305            Some(&17991041643939889152)
306        );
307        assert_eq!(
308            with_primes(&[2, 5], 10),
309            [1, 2, 4, 5, 8, 10, 16, 20, 25, 32]
310        );
311    }
312
313    #[test]
314    #[should_panic(expected = "attempt to multiply with overflow")]
315    fn with_primes_1_first_to_overflow() {
316        let _ = with_primes(&[2], 65).last();
317    }
318
319    #[test]
320    #[should_panic(expected = "attempt to multiply with overflow")]
321    fn with_primes_2_first_to_overflow() {
322        let _ = with_primes(&[2, 3], 1345).last();
323    }
324}