ecgen/perm.rs
1use genawaiter::sync::{Gen, GenBoxed};
2
3// /// For future rust version:
4// const fn factorial2<const N: usize>() -> usize {
5// if N < 2 {
6// 1
7// } else {
8// const M: usize = N - 1;
9// N * factorial2::<M>()
10// }
11// }
12
13/// The `factorial` function calculates the factorial of a given number.
14///
15/// Arguments:
16///
17/// * `n`: The parameter `n` represents the number for which we want to calculate the factorial.
18///
19/// Returns:
20///
21/// The `factorial` function returns the factorial of the input number `n`.
22///
23/// # Examples
24///
25/// ```
26/// use ecgen::factorial;
27///
28/// assert_eq!(factorial(5), 120);
29/// assert_eq!(factorial(1), 1);
30/// ```
31pub const fn factorial(n: usize) -> usize {
32 if n < 2 {
33 1
34 } else {
35 n * factorial(n - 1)
36 }
37}
38
39/// Generate all permutations by adjacent transposition
40///
41/// The `sjt_gen` function in Rust generates all permutations of a given length using the
42/// Steinhaus-Johnson-Trotter algorithm.
43///
44/// Arguments:
45///
46/// * `n`: The parameter `n` represents the number of elements in the permutation.
47///
48/// Returns:
49///
50/// The function `sjt_gen` returns a boxed generator that yields permutations of indices.
51///
52/// # Examples
53///
54/// ```
55/// use ecgen::sjt_gen;
56///
57/// let mut perm = ["🍉", "🍌", "🍇", "🍏"];
58/// let mut cnt = 0;
59/// for n in sjt_gen(perm.len()) {
60/// println!("{}", perm.concat());
61/// cnt += 1;
62/// perm.swap(n, n + 1);
63/// }
64///
65/// assert_eq!(cnt, 24);
66/// assert_eq!(perm, ["🍉", "🍌", "🍇", "🍏"]); // Hamilton cycle
67/// ```
68pub fn sjt_gen(n: usize) -> GenBoxed<usize> {
69 Gen::new_boxed(|co| {
70 async move {
71 /* Generate the swaps for the Steinhaus-Johnson-Trotter algorithm. */
72 if n == 2 {
73 co.yield_(0).await;
74 co.yield_(0).await; // tricky part: return to the original list
75 return;
76 }
77 let mut even = true;
78 for j in sjt_gen(n - 1) {
79 if even {
80 // downward
81 for i in (0..n - 1).rev() {
82 co.yield_(i).await;
83 }
84 co.yield_(1 + j).await;
85 even = false;
86 } else {
87 // upward
88 for i in 0..n - 1 {
89 co.yield_(i).await;
90 }
91 co.yield_(j).await;
92 even = true;
93 }
94 }
95 }
96 })
97}
98
99/// Generate all permutations by star transposition
100///
101/// The `ehr_gen` function generates all permutations of a given length using the star transposition
102/// algorithm.
103///
104/// Arguments:
105///
106/// * `n`: The parameter `n` represents the number of elements in the permutation. In the given example,
107/// `n` is set to 4, so it generates permutations of 4 elements.
108///
109/// Returns:
110///
111/// The function `ehr_gen` returns a `GenBoxed<usize>`, which is a boxed generator that yields `usize`
112/// values.
113///
114/// # Examples
115///
116/// ```
117/// use ecgen::ehr_gen;
118///
119/// let mut perm = ["🍉", "🍌", "🍇", "🍏"];
120/// let mut cnt = 1;
121/// println!("{}", perm.concat());
122/// for n in ehr_gen(perm.len()) {
123/// perm.swap(0, n);
124/// println!("{}", perm.concat());
125/// cnt += 1;
126/// }
127///
128/// assert_eq!(cnt, 24);
129/// assert_eq!(perm, ["🍏", "🍌", "🍇", "🍉"]);
130/// ```
131pub fn ehr_gen(n: usize) -> GenBoxed<usize> {
132 Gen::new_boxed(|co| {
133 async move {
134 let mut c = vec![0; n + 1]; // c[0] is never used
135 let mut b: Vec<usize> = (0..n).collect(); // 0, ... n-1
136 loop {
137 let mut k: usize = 1;
138 loop {
139 if c[k] == k {
140 c[k] = 0;
141 k += 1;
142 }
143 if c[k] < k {
144 break;
145 }
146 }
147 if k == n {
148 break;
149 }
150 c[k] += 1;
151 co.yield_(b[k]).await;
152 b[1..k].reverse();
153 }
154 }
155 })
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_sjt() {
164 let mut cnt = 0;
165 for _n in sjt_gen(4) {
166 cnt += 1;
167 }
168 assert_eq!(cnt, factorial(4));
169 }
170
171 #[test]
172 fn test_ehr() {
173 let mut cnt = 1;
174 for _n in ehr_gen(4) {
175 cnt += 1;
176 }
177 assert_eq!(cnt, factorial(4));
178 }
179}