malachite_base/chars/
exhaustive.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::chars::CharType;
10use crate::chars::crement::increment_char;
11use core::ops::RangeInclusive;
12
13/// Generates all ASCII [`char`]s, in ascending order.
14///
15/// For a friendlier order (_e.g_. nonprintable [`char`]s coming last), try
16/// [`exhaustive_ascii_chars`].
17///
18/// The output length is 128.
19///
20/// # Complexity per iteration
21/// Constant time and additional memory.
22///
23/// # Examples
24/// ```
25/// use malachite_base::chars::exhaustive::ascii_chars_increasing;
26///
27/// assert_eq!(
28///     ascii_chars_increasing().collect::<String>(),
29///     "\u{0}\u{1}\u{2}\u{3}\u{4}\u{5}\u{6}\u{7}\u{8}\t\n\u{b}\u{c}\r\u{e}\u{f}\u{10}\u{11}\u{12}\
30///     \u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f} !\"#$%&\'()*\
31///     +,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{7f}"
32/// );
33/// ```
34pub const fn ascii_chars_increasing() -> RangeInclusive<char> {
35    char::MIN..='\u{7f}'
36}
37
38/// Generates all [`char`]s, in ascending order.
39///
40/// For a friendlier order (_e.g_. nonprintable [`char`]s coming last), try [`exhaustive_chars`].
41///
42/// The output length is 1,112,064.
43///
44/// # Complexity per iteration
45/// Constant time and additional memory.
46///
47/// # Examples
48/// ```
49/// use malachite_base::chars::exhaustive::chars_increasing;
50///
51/// assert_eq!(
52///     chars_increasing().take(200).collect::<String>(),
53///     "\u{0}\u{1}\u{2}\u{3}\u{4}\u{5}\u{6}\u{7}\u{8}\t\n\u{b}\u{c}\r\u{e}\u{f}\u{10}\u{11}\u{12}\
54///     \u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f} !\"#$%&\'()*\
55///     +,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u{7f}\
56///     \u{80}\u{81}\u{82}\u{83}\u{84}\u{85}\u{86}\u{87}\u{88}\u{89}\u{8a}\u{8b}\u{8c}\u{8d}\u{8e}\
57///     \u{8f}\u{90}\u{91}\u{92}\u{93}\u{94}\u{95}\u{96}\u{97}\u{98}\u{99}\u{9a}\u{9b}\u{9c}\u{9d}\
58///     \u{9e}\u{9f}\u{a0}¡¢£¤¥¦§¨©ª«¬\u{ad}®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇ"
59/// );
60/// ```
61pub const fn chars_increasing() -> RangeInclusive<char> {
62    char::MIN..=char::MAX
63}
64
65/// Generates all [`char`]s, in a friendly order, so that more familiar [`char`] come first.
66///
67/// The order is
68/// - Lowercase ASCII letters,
69/// - Uppercase ASCII letters,
70/// - ASCII digits,
71/// - Graphic ASCII [`char`]s (not alphanumeric and not control), including `' '` but no other
72///   whitespace,
73/// - (only if `ascii_only` is false) Graphic Non-ASCII [`char`]s; all non-ASCII [`char`]s whose
74///   [`Debug`](std::fmt::Debug) representations don't start with `'\'`,
75/// - All remaining [`char`]s.
76///
77/// This `struct` is created by [`exhaustive_chars`] and [`exhaustive_ascii_chars`]; see their
78/// documentation for more.
79#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
80pub struct ExhaustiveChars {
81    ascii_only: bool,
82    first: bool,
83    c: char,
84    current_type: CharType,
85}
86
87impl Iterator for ExhaustiveChars {
88    type Item = char;
89
90    fn next(&mut self) -> Option<char> {
91        if self.first {
92            self.first = false;
93        } else {
94            match self.current_type {
95                CharType::AsciiLower => {
96                    if self.c == 'z' {
97                        self.current_type = CharType::AsciiUpper;
98                        self.c = 'A';
99                    } else {
100                        increment_char(&mut self.c);
101                    }
102                }
103                CharType::AsciiUpper => {
104                    if self.c == 'Z' {
105                        self.current_type = CharType::AsciiNumeric;
106                        self.c = '0';
107                    } else {
108                        increment_char(&mut self.c);
109                    }
110                }
111                CharType::AsciiNumeric => {
112                    if self.c == '9' {
113                        self.current_type = CharType::AsciiNonAlphanumericGraphic;
114                        self.c = ' ';
115                    } else {
116                        increment_char(&mut self.c);
117                    }
118                }
119                CharType::AsciiNonAlphanumericGraphic => {
120                    if self.c == '~' {
121                        if self.ascii_only {
122                            self.current_type = CharType::NonGraphic;
123                            self.c = '\0';
124                        } else {
125                            self.current_type = CharType::NonAsciiGraphic;
126                            self.c = '\u{a1}';
127                        };
128                    } else {
129                        increment_char(&mut self.c);
130                        // No control chars between ' ' and '~'
131                        while self.c.is_ascii_alphanumeric() {
132                            increment_char(&mut self.c);
133                        }
134                    }
135                }
136                CharType::NonAsciiGraphic => {
137                    if self.c == '\u{323af}' {
138                        self.current_type = CharType::NonGraphic;
139                        self.c = '\0';
140                    } else {
141                        increment_char(&mut self.c);
142                        while !CharType::NonAsciiGraphic.contains(self.c) {
143                            increment_char(&mut self.c);
144                        }
145                    }
146                }
147                CharType::NonGraphic => {
148                    let limit = if self.ascii_only { '\u{7f}' } else { char::MAX };
149                    if self.c == limit {
150                        return None;
151                    }
152                    increment_char(&mut self.c);
153                    while !self.c.is_ascii_control()
154                        && (self.c.is_ascii() || CharType::NonAsciiGraphic.contains(self.c))
155                    {
156                        increment_char(&mut self.c);
157                    }
158                }
159            }
160        }
161        Some(self.c)
162    }
163}
164
165/// Generates all ASCII [`char`]s, in a friendly order, so that more familiar [`char`]s come first.
166///
167/// The order is
168/// - Lowercase ASCII letters,
169/// - Uppercase ASCII letters,
170/// - ASCII digits,
171/// - Graphic ASCII [`char`]s (not alphanumeric and not control), including `' '` but no other
172///   whitespace,
173/// - All remaining ASCII [`char`]s.
174///
175/// Within each group, the [`char`]s are ordered according to their usual order.
176///
177/// If you want to generate ASCII [`char`]s in their usual order, try [`ascii_chars_increasing`].
178///
179/// The output length is 128.
180///
181/// # Complexity per iteration
182/// Constant time and additional memory.
183///
184/// # Examples
185/// ```
186/// use malachite_base::chars::exhaustive::exhaustive_ascii_chars;
187///
188/// assert_eq!(
189///     exhaustive_ascii_chars().collect::<String>(),
190///     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 !\"#$%&\'()*+,-./:;<=>?@[\\\
191///     ]^_`{|}~\u{0}\u{1}\u{2}\u{3}\u{4}\u{5}\u{6}\u{7}\u{8}\t\n\u{b}\u{c}\r\u{e}\u{f}\u{10}\u{11}\
192///     \u{12}\u{13}\u{14}\u{15}\u{16}\u{17}\u{18}\u{19}\u{1a}\u{1b}\u{1c}\u{1d}\u{1e}\u{1f}\u{7f}"
193/// );
194/// ```
195pub const fn exhaustive_ascii_chars() -> ExhaustiveChars {
196    ExhaustiveChars {
197        ascii_only: true,
198        first: true,
199        c: 'a',
200        current_type: CharType::AsciiLower,
201    }
202}
203
204/// Generates all [`char`]s, in a friendly order, so that more familiar [`char`]s come first.
205///
206/// The order is
207/// - Lowercase ASCII letters,
208/// - Uppercase ASCII letters,
209/// - ASCII digits,
210/// - Graphic ASCII [`char`] (not alphanumeric and not control), including `' '` but no other
211///   whitespace,
212/// - Graphic Non-ASCII [`char`]s; all non-ASCII [`char`]s whose [`Debug`](std::fmt::Debug)
213///   representations don't start with `'\'`,
214/// - All remaining [`char`]s.
215///
216/// Within each group, the [`char`]s are ordered according to their usual order.
217///
218/// If you want to generate [`char`]s in their usual order, try [`chars_increasing`].
219///
220/// The output length is 1,112,064.
221///
222/// # Complexity per iteration
223/// Constant time and additional memory.
224///
225/// # Examples
226/// ```
227/// use malachite_base::chars::exhaustive::exhaustive_chars;
228///
229/// assert_eq!(
230///     exhaustive_chars().take(200).collect::<String>(),
231///     "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 !\"#$%&\'()*+,-./:;<=>?@[\\\
232///     ]^_`{|}~¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóô\
233///     õö÷øùúûüýþÿĀāĂ㥹ĆćĈĉĊ"
234/// );
235/// ```
236pub const fn exhaustive_chars() -> ExhaustiveChars {
237    ExhaustiveChars {
238        ascii_only: false,
239        first: true,
240        c: 'a',
241        current_type: CharType::AsciiLower,
242    }
243}