-
-
Notifications
You must be signed in to change notification settings - Fork 503
Add Alphabetic distribution
#1587
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a6cf8f6
69cc117
9e8c041
5bc7b40
dc0de22
0d679b9
1e3aec1
df2fe26
3d88c67
4148dc7
be743d5
192ddd8
e6ac021
b1f9b05
2f42f42
f86462e
e5e59ba
baca091
c9093cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,35 @@ use serde::{Deserialize, Serialize}; | |
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | ||
| pub struct Alphanumeric; | ||
|
|
||
| /// Sample a [`u8`], uniformly distributed over letters: | ||
| /// a-z and A-Z. | ||
| /// | ||
| /// # Example | ||
| /// | ||
| /// You're able to generate random Alphabetic characters via mapping or via the | ||
| /// [`SampleString::sample_string`] method like so: | ||
| /// | ||
| /// ``` | ||
| /// use rand::Rng; | ||
| /// use rand::distr::{Alphabetic, SampleString}; | ||
| /// | ||
| /// // Manual mapping | ||
| /// let mut rng = rand::rng(); | ||
| /// let chars: String = (0..7).map(|_| rng.sample(Alphabetic) as char).collect(); | ||
| /// println!("Random chars: {}", chars); | ||
| /// | ||
| /// // Using [`SampleString::sample_string`] | ||
| /// let string = Alphabetic.sample_string(&mut rand::rng(), 16); | ||
| /// println!("Random string: {}", string); | ||
| /// ``` | ||
| /// | ||
| /// # Passwords | ||
| /// | ||
| /// Refer to [`Alphanumeric#Passwords`]. | ||
| #[derive(Debug, Clone, Copy, Default)] | ||
| #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] | ||
| pub struct Alphabetic; | ||
|
|
||
| // ----- Implementations of distributions ----- | ||
|
|
||
| impl Distribution<char> for StandardUniform { | ||
|
|
@@ -123,6 +152,17 @@ impl Distribution<u8> for Alphanumeric { | |
| } | ||
| } | ||
|
|
||
| impl Distribution<u8> for Alphabetic { | ||
| fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u8 { | ||
| const RANGE: u8 = 26 + 26; | ||
|
|
||
| let offset = rng.random_range(0..RANGE) + b'A'; | ||
|
|
||
| // Account for upper-cases | ||
| offset + (offset > b'Z') as u8 * (b'a' - b'Z' - 1) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(feature = "alloc")] | ||
| impl SampleString for Alphanumeric { | ||
| fn append_string<R: Rng + ?Sized>(&self, rng: &mut R, string: &mut String, len: usize) { | ||
|
|
@@ -133,6 +173,20 @@ impl SampleString for Alphanumeric { | |
| } | ||
| } | ||
|
|
||
| #[cfg(feature = "alloc")] | ||
| impl SampleString for Alphabetic { | ||
| fn append_string<R: Rng + ?Sized>(&self, rng: &mut R, string: &mut String, len: usize) { | ||
| // SAFETY: With this distribution we guarantee that we're working with valid ASCII | ||
| // characters. | ||
| // See [#1590](https://github.com/rust-random/rand/issues/1590). | ||
| unsafe { | ||
| let v = string.as_mut_vec(); | ||
| v.reserve_exact(len); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe we don't need to use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Thought the same but on my 1 million markdown headings project from earlier project it yields noticeably better performance. With Without And its not like we'd need more than we use when the method itself takes When opening fn extend<I: IntoIterator<Item = char>>(&mut self, iter: I) {
let iterator = iter.into_iter();
let (lower_bound, _) = iterator.size_hint();
self.reserve(lower_bound);
iterator.for_each(move |c| self.push(c));
}Yet using a Alphabetic.sample_string(rng, 128)Honestly, I'm unsure why it's that much more performant, but it's been consistently like that. Feel free to also test it out if you want: |
||
| v.extend(self.sample_iter(rng).take(len)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Distribution<bool> for StandardUniform { | ||
| #[inline] | ||
| fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> bool { | ||
|
|
@@ -294,6 +348,20 @@ mod tests { | |
| assert!(!incorrect); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_alphabetic() { | ||
| let mut rng = crate::test::rng(806); | ||
|
|
||
| // Test by generating a relatively large number of chars, so we also | ||
| // take the rejection sampling path. | ||
| let mut incorrect = false; | ||
| for _ in 0..100 { | ||
| let c: char = rng.sample(Alphabetic).into(); | ||
| incorrect |= !c.is_ascii_alphabetic(); | ||
| } | ||
| assert!(!incorrect); | ||
| } | ||
|
|
||
| #[test] | ||
| fn value_stability() { | ||
| fn test_samples<T: Copy + core::fmt::Debug + PartialEq, D: Distribution<T>>( | ||
|
|
@@ -321,6 +389,7 @@ mod tests { | |
| ], | ||
| ); | ||
| test_samples(&Alphanumeric, 0, &[104, 109, 101, 51, 77]); | ||
| test_samples(&Alphabetic, 0, &[97, 102, 89, 116, 75]); | ||
| test_samples(&StandardUniform, false, &[true, true, false, true, false]); | ||
| test_samples( | ||
| &StandardUniform, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.