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}