number_encoding/
lib.rs

1// Copyright 2019-2022 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Number systems
16//!
17//! This crate provides number systems for combinations, factorials, multinomials, and sequences of
18//! bits.
19
20#![no_std]
21#![warn(unused_results, missing_docs)]
22
23#[cfg(feature = "alloc")]
24extern crate alloc;
25#[cfg(feature = "std")]
26extern crate std;
27
28pub mod combinadics;
29pub mod factoradics;
30pub mod multinadics;
31pub mod sequences;
32
33/// Returns the greatest common divisor of `a` and `b`.
34///
35/// See [wikipedia] for more information.
36///
37/// # Panics
38///
39/// Panics in debug mode if `a == 0 && b == 0`.
40///
41/// # Examples
42///
43/// ```rust
44/// # use number_encoding::greatest_common_divisor;
45/// assert_eq!(greatest_common_divisor(2, 3), 1);
46/// assert_eq!(greatest_common_divisor(5, 1), 1);
47/// assert_eq!(greatest_common_divisor(5, 5), 5);
48/// assert_eq!(greatest_common_divisor(12, 8), 4);
49/// assert_eq!(greatest_common_divisor(12, 18), 6);
50/// ```
51///
52/// [wikipedia]: https://en.wikipedia.org/wiki/Greatest_common_divisor
53pub fn greatest_common_divisor(mut a: usize, mut b: usize) -> usize {
54    debug_assert!(a > 0 || b > 0, "Failed precondition");
55    while b > 0 {
56        a %= b;
57        core::mem::swap(&mut a, &mut b);
58    }
59    a
60}
61
62#[test]
63fn greatest_common_divisor_ok() {
64    fn spec(a: usize, b: usize) -> usize {
65        let mut r = 0;
66        for i in 1 ..= std::cmp::max(a, b) {
67            if a % i == 0 && b % i == 0 {
68                r = std::cmp::max(r, i);
69            }
70        }
71        r
72    }
73    for a in 0 .. 20 {
74        for b in 0 .. 20 {
75            if a == 0 && b == 0 {
76                continue;
77            }
78            assert_eq!(greatest_common_divisor(a, b), spec(a, b), "a={a} b={b}");
79        }
80    }
81}
82
83/// Returns the factorial of `n`.
84///
85/// See [wikipedia] for more information.
86///
87/// # Examples
88///
89/// ```rust
90/// # use number_encoding::factorial;
91/// assert_eq!(factorial(0), 1);
92/// assert_eq!(factorial(1), 1);
93/// assert_eq!(factorial(2), 2);
94/// assert_eq!(factorial(3), 6);
95/// assert_eq!(factorial(4), 24);
96/// ```
97///
98/// [wikipedia]: https://en.wikipedia.org/wiki/Factorial
99pub fn factorial(mut n: usize) -> usize {
100    let mut r = 1;
101    while n > 0 {
102        r *= n;
103        n -= 1;
104    }
105    r
106}
107
108#[test]
109fn factorial_ok() {
110    fn spec(n: usize) -> usize {
111        if n == 0 {
112            1
113        } else {
114            n * spec(n - 1)
115        }
116    }
117    for n in 0 .. 10 {
118        assert_eq!(factorial(n), spec(n), "n={n}");
119    }
120}
121
122/// Returns the number of `k`-combinations of a set of `n` elements.
123///
124/// See [wikipedia] for more information.
125///
126/// # Examples
127///
128/// ```rust
129/// # use number_encoding::combination;
130/// assert_eq!(combination(4, 0), 1);
131/// assert_eq!(combination(4, 1), 4);
132/// assert_eq!(combination(4, 2), 6);
133/// assert_eq!(combination(4, 3), 4);
134/// assert_eq!(combination(4, 4), 1);
135/// assert_eq!(combination(4, 5), 0);
136/// ```
137///
138/// [wikipedia]: https://en.wikipedia.org/wiki/Combination
139pub fn combination(n: usize, k: usize) -> usize {
140    if n < k {
141        return 0;
142    }
143    let mut r = 1;
144    let mut d = factorial(k);
145    for i in 0 .. k {
146        let mut m = n - i;
147        if d > 1 {
148            let g = greatest_common_divisor(m, d);
149            m /= g;
150            d /= g;
151        }
152        r *= m;
153    }
154    debug_assert_eq!(d, 1);
155    r
156}
157
158#[test]
159fn combination_ok() {
160    fn spec(n: usize, k: usize) -> usize {
161        if k > n {
162            0
163        } else {
164            factorial(n) / (factorial(n - k) * factorial(k))
165        }
166    }
167    for n in 0 .. 5 {
168        for k in 0 .. 5 {
169            assert_eq!(combination(n, k), spec(n, k), "n={n} k={k}");
170        }
171    }
172}
173
174/// Returns the number of permutations of a multiset.
175///
176/// See [wikipedia] for more information.
177///
178/// # Examples
179///
180/// ```rust
181/// # use number_encoding::multinomial;
182/// assert_eq!(multinomial(&[2, 0, 1]), 6);
183/// assert_eq!(multinomial(&[0, 1, 0]), 3);
184/// assert_eq!(multinomial(&[0, 1, 1, 0, 2, 0]), 60);
185/// ```
186///
187/// [wikipedia]: https://en.wikipedia.org/wiki/Multinomial_theorem#Number_of_unique_permutations_of_words
188pub fn multinomial<T: Ord>(xs: &[T]) -> usize {
189    let mut n = xs.len();
190    let mut r = 1;
191    for i in 0 .. xs.len() {
192        if xs[.. i].contains(&xs[i]) {
193            continue;
194        }
195        let k = xs[i ..].iter().filter(|&x| x == &xs[i]).count();
196        r *= combination(n, k);
197        n -= k;
198    }
199    r
200}
201
202#[test]
203fn multinomial_ok() {
204    fn test(xs: &[usize], r: usize) {
205        assert_eq!(multinomial(xs), r, "xs={xs:?}");
206    }
207    test(&[], 1);
208    test(&[0], 1);
209    test(&[0, 0], 1);
210    test(&[0, 1], 2);
211    test(&[0, 1, 0], 3);
212    test(&[0, 1, 0, 1], 6);
213    test(&[0, 1, 1, 0, 2, 0], 60);
214}
215
216fn is_ordered_set<T: Ord>(xs: &[T]) -> bool {
217    xs.windows(2).all(|w| w[0] < w[1])
218}
219
220#[test]
221fn is_ordered_set_ok() {
222    fn test(xs: &[usize], r: bool) {
223        assert_eq!(is_ordered_set(xs), r, "xs={xs:?}");
224    }
225    test(&[0, 1], true);
226    test(&[0, 0], false);
227    test(&[1, 0], false);
228}
229
230// TODO(https://github.com/rust-lang/rust/issues/53485): Use is_sorted.
231fn is_ordered_multiset<T: Ord>(xs: &[T]) -> bool {
232    xs.windows(2).all(|w| w[0] <= w[1])
233}
234
235#[test]
236fn is_ordered_multiset_ok() {
237    fn test(xs: &[usize], r: bool) {
238        assert_eq!(is_ordered_multiset(xs), r, "xs={xs:?}");
239    }
240    test(&[0, 1, 1], true);
241    test(&[0, 0, 1], true);
242    test(&[1, 0], false);
243}
244
245fn is_unordered_set<T: Ord>(xs: &[T]) -> bool {
246    xs.iter().all(|x| xs.iter().filter(|&y| x == y).count() == 1)
247}
248
249#[test]
250fn is_unordered_set_ok() {
251    fn test(xs: &[usize], r: bool) {
252        assert_eq!(is_unordered_set(xs), r, "xs={xs:?}");
253    }
254    test(&[0, 1], true);
255    test(&[0, 0], false);
256    test(&[1, 0], true);
257}