1use crate::{
4 CombError, canonical_cycles, combination_rank, combination_unrank, longest_only,
5 permutation_rank, permutation_unrank, word_rank, word_to_digits, words,
6};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct RankableValuesDemo {
11 pub combination: Vec<usize>,
13 pub combination_rank: String,
15 pub combination_unranked: Vec<usize>,
17 pub permutation: Vec<usize>,
19 pub permutation_rank: String,
21 pub permutation_unranked: Vec<usize>,
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct FiniteEnumerationDemo {
28 pub alphabet: Vec<String>,
30 pub ordered_alphabet: Vec<String>,
32 pub length: usize,
34 pub limit: usize,
36 pub total_words: String,
38 pub first_words: Vec<Vec<String>>,
40 pub first_digits: Vec<Vec<u64>>,
42 pub first_ranks: Vec<String>,
44 pub canonical_cycles: Vec<Vec<String>>,
46 pub longest_words: Vec<Vec<String>>,
48}
49
50pub fn rankable_values_demo() -> Result<RankableValuesDemo, CombError> {
52 let combination = vec![0, 2, 4];
53 let combination_rank = combination_rank(&combination, 5)?;
54 let combination_unranked = combination_unrank(&combination_rank, 5, 3)?;
55
56 let permutation = vec![2, 0, 1];
57 let permutation_rank = permutation_rank(&permutation)?;
58 let permutation_unranked = permutation_unrank(&permutation_rank, 3)?;
59
60 Ok(RankableValuesDemo {
61 combination,
62 combination_rank: combination_rank.to_string(),
63 combination_unranked,
64 permutation,
65 permutation_rank: permutation_rank.to_string(),
66 permutation_unranked,
67 })
68}
69
70pub fn finite_enumeration_demo(
72 alphabet: Vec<String>,
73 length: usize,
74 order: Vec<usize>,
75 limit: usize,
76) -> Result<FiniteEnumerationDemo, CombError> {
77 let ordered_alphabet = ordered_alphabet(&alphabet, &order)?;
78 let iterator = words(&ordered_alphabet, length);
79 let total_words = iterator.total_ordinals().to_string();
80 let first_words = iterator.take(limit).collect::<Vec<_>>();
81 let first_digits = first_words
82 .iter()
83 .map(|word| word_to_digits(&ordered_alphabet, word))
84 .collect::<Result<Vec<_>, _>>()?;
85 let first_ranks = first_words
86 .iter()
87 .map(|word| word_rank(&ordered_alphabet, word).map(|rank| rank.to_string()))
88 .collect::<Result<Vec<_>, _>>()?;
89 let cycle_source = first_words
90 .iter()
91 .find(|word| word.windows(2).any(|window| window[0] != window[1]))
92 .or_else(|| first_words.first());
93 let canonical_cycles = cycle_source
94 .map(|word| canonical_cycles(word))
95 .unwrap_or_else(Vec::new);
96 let longest_words = longest_only(first_words.clone(), |word| word.len());
97
98 Ok(FiniteEnumerationDemo {
99 alphabet,
100 ordered_alphabet,
101 length,
102 limit,
103 total_words,
104 first_words,
105 first_digits,
106 first_ranks,
107 canonical_cycles,
108 longest_words,
109 })
110}
111
112fn ordered_alphabet(alphabet: &[String], order: &[usize]) -> Result<Vec<String>, CombError> {
113 if order.len() != alphabet.len() {
114 return Err(CombError::InvalidParameters(
115 "order length must match alphabet length".to_string(),
116 ));
117 }
118 let mut seen = vec![false; alphabet.len()];
119 let mut ordered = Vec::with_capacity(alphabet.len());
120 for &index in order {
121 if index >= alphabet.len() {
122 return Err(CombError::OutOfRange {
123 value: index.to_string(),
124 bound: alphabet.len().to_string(),
125 });
126 }
127 if seen[index] {
128 return Err(CombError::InvalidParameters(
129 "order must be a permutation".to_string(),
130 ));
131 }
132 seen[index] = true;
133 ordered.push(alphabet[index].clone());
134 }
135 Ok(ordered)
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn rankable_values_round_trip_ordinals() {
144 let demo = rankable_values_demo().expect("valid rankable values demo");
145
146 assert_eq!(demo.combination_unranked, demo.combination);
147 assert_eq!(demo.permutation_unranked, demo.permutation);
148 assert_eq!(demo.combination_rank, "4");
149 assert_eq!(demo.permutation_rank, "4");
150 }
151
152 #[test]
153 fn finite_enumeration_demo_uses_supplied_data() {
154 let demo = finite_enumeration_demo(
155 vec!["C".to_string(), "D".to_string(), "E".to_string()],
156 2,
157 vec![2, 0, 1],
158 5,
159 )
160 .expect("valid finite enumeration demo");
161
162 assert_eq!(demo.ordered_alphabet, vec!["E", "C", "D"]);
163 assert_eq!(demo.total_words, "9");
164 assert_eq!(
165 demo.first_words,
166 vec![
167 vec!["E", "E"],
168 vec!["E", "C"],
169 vec!["E", "D"],
170 vec!["C", "E"],
171 vec!["C", "C"],
172 ]
173 );
174 assert_eq!(
175 demo.first_digits,
176 vec![vec![0, 0], vec![0, 1], vec![0, 2], vec![1, 0], vec![1, 1]]
177 );
178 assert_eq!(demo.first_ranks, vec!["0", "1", "2", "3", "4"]);
179 assert_eq!(
180 demo.canonical_cycles,
181 vec![
182 vec!["C".to_string(), "E".to_string()],
183 vec!["E".to_string(), "C".to_string()]
184 ]
185 );
186 assert_eq!(demo.longest_words, demo.first_words);
187 }
188}