Skip to content

Commit

Permalink
feat: Add an iterator selection function (#51)
Browse files Browse the repository at this point in the history
* Add an iterator selection function

* Rebase atop master
  • Loading branch information
notgull authored Mar 3, 2023
1 parent a636da2 commit c1cbe9b
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 12 deletions.
14 changes: 14 additions & 0 deletions src/global_rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ pub fn uppercase() -> char {
with_rng(|r| r.uppercase())
}

/// Choose an item from an iterator at random.
///
/// This function may have an unexpected result if the `len()` property of the
/// iterator does not match the actual number of items in the iterator. If
/// the iterator is empty, this returns `None`.
#[inline]
pub fn choice<I>(iter: I) -> Option<I::Item>
where
I: IntoIterator,
I::IntoIter: ExactSizeIterator,
{
with_rng(|r| r.choice(iter))
}

/// Generates a random digit in the given `base`.
///
/// Digits are represented by `char`s in ranges 0-9 and a-z.
Expand Down
39 changes: 27 additions & 12 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,18 +279,14 @@ impl Rng {
#[inline]
pub fn alphabetic(&mut self) -> char {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
let len = CHARS.len() as u8;
let i = self.u8(..len);
CHARS[i as usize] as char
*self.choice(CHARS).unwrap() as char
}

/// Generates a random `char` in ranges a-z, A-Z and 0-9.
#[inline]
pub fn alphanumeric(&mut self) -> char {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let len = CHARS.len() as u8;
let i = self.u8(..len);
CHARS[i as usize] as char
*self.choice(CHARS).unwrap() as char
}

/// Generates a random `bool`.
Expand Down Expand Up @@ -403,9 +399,7 @@ impl Rng {
#[inline]
pub fn lowercase(&mut self) -> char {
const CHARS: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
let len = CHARS.len() as u8;
let i = self.u8(..len);
CHARS[i as usize] as char
*self.choice(CHARS).unwrap() as char
}

/// Initializes this generator with the given seed.
Expand All @@ -420,6 +414,29 @@ impl Rng {
self.0
}

/// Choose an item from an iterator at random.
///
/// This function may have an unexpected result if the `len()` property of the
/// iterator does not match the actual number of items in the iterator. If
/// the iterator is empty, this returns `None`.
#[inline]
pub fn choice<I>(&mut self, iter: I) -> Option<I::Item>
where
I: IntoIterator,
I::IntoIter: ExactSizeIterator,
{
let mut iter = iter.into_iter();

// Get the item at a random index.
let len = iter.len();
if len == 0 {
return None;
}
let index = self.usize(0..len);

iter.nth(index)
}

/// Shuffles a slice randomly.
#[inline]
pub fn shuffle<T>(&mut self, slice: &mut [T]) {
Expand Down Expand Up @@ -529,9 +546,7 @@ impl Rng {
#[inline]
pub fn uppercase(&mut self) -> char {
const CHARS: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let len = CHARS.len() as u8;
let i = self.u8(..len);
CHARS[i as usize] as char
*self.choice(CHARS).unwrap() as char
}

/// Generates a random `char` in the given range.
Expand Down
10 changes: 10 additions & 0 deletions tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,13 @@ fn with_seed() {
b.seed(7);
assert_eq!(a.u64(..), b.u64(..));
}

#[test]
fn choice() {
let items = [1, 4, 9, 5, 2, 3, 6, 7, 8, 0];
let mut r = fastrand::Rng::new();

for item in &items {
while r.choice(&items).unwrap() != item {}
}
}

0 comments on commit c1cbe9b

Please sign in to comment.