mirror of
https://github.com/Noratrieb/simple-std.git
synced 2026-01-14 16:35:06 +01:00
input and random
This commit is contained in:
commit
5d6a2d668a
4 changed files with 259 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
/target
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
[package]
|
||||||
|
name = "simple-std"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
description = "A simple extension to the Rust standard library for exercises"
|
||||||
|
keywords = ["beginner", "help"]
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
45
README.md
Normal file
45
README.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
# note: this is for practicing rust only, do not use this in actual production programs!
|
||||||
|
|
||||||
|
simple-std is a little extension to the standard library,
|
||||||
|
providing additional helpers for getting input or creating random numbers.
|
||||||
|
|
||||||
|
`std` is very useful, but it's lacking for little beginner exercises
|
||||||
|
(for a good reason), so I made this library to help with that.
|
||||||
|
|
||||||
|
Every function from this library has a little section on why this function isn't in `std`, to help you understand
|
||||||
|
the reasoning behind including something in `std`.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
Greeting
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use simple_std::input;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("What is your name?");
|
||||||
|
let name = input();
|
||||||
|
println!("Hello {}!", name)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Guessing game
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use simple_std::{input, random_int_range};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let number = random_int_range(0..100);
|
||||||
|
loop {
|
||||||
|
let input = input().parse::<i32>().expect("not a number");
|
||||||
|
if input < number {
|
||||||
|
println!("Higher");
|
||||||
|
} else if input > number {
|
||||||
|
println!("Lower");
|
||||||
|
} else {
|
||||||
|
println!("Correct!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
199
src/lib.rs
Normal file
199
src/lib.rs
Normal file
|
|
@ -0,0 +1,199 @@
|
||||||
|
//!
|
||||||
|
//! # Example: guessing game
|
||||||
|
//! ```
|
||||||
|
//! use simple_std::{input, random_int_range};
|
||||||
|
//!
|
||||||
|
//! fn main() {
|
||||||
|
//! let number = random_int_range(0..100);
|
||||||
|
//! loop {
|
||||||
|
//!# // hack the input function for this to work in the doc test
|
||||||
|
//!# fn input() -> String {
|
||||||
|
//!# random_int_range(0..100).to_string()
|
||||||
|
//!# }
|
||||||
|
//! let input = input().parse::<i32>().expect("not a number");
|
||||||
|
//! if input < number {
|
||||||
|
//! println!("Higher");
|
||||||
|
//! } else if input > number {
|
||||||
|
//! println!("Lower");
|
||||||
|
//! } else {
|
||||||
|
//! println!("Correct!");
|
||||||
|
//! break;
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
pub use io::input;
|
||||||
|
pub use random::{random_float, random_int_range};
|
||||||
|
|
||||||
|
mod io {
|
||||||
|
///
|
||||||
|
/// Reads a single line of input, similar to Pythons `input` function
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use simple_std::input;
|
||||||
|
///
|
||||||
|
/// println!("What is your name?");
|
||||||
|
/// let name = input();
|
||||||
|
/// println!("Hello {}!", name)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Why is this not in std?
|
||||||
|
///
|
||||||
|
/// The implementation is fairly simple, just 2 lines, but it has a little complexity to it,
|
||||||
|
/// that's why there is the simplified version here.
|
||||||
|
pub fn input() -> String {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
std::io::stdin().read_line(&mut buffer).unwrap();
|
||||||
|
buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod random {
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns a random number from 0 to 1, like Javascript `Math.random`
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use simple_std::random_float;
|
||||||
|
///
|
||||||
|
/// let number = random_float();
|
||||||
|
///
|
||||||
|
/// println!("Number between 0 and 1: {}", number);
|
||||||
|
///
|
||||||
|
/// assert!(number < 1.0);
|
||||||
|
/// assert!(number >= 0.0);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Why is this not in std?
|
||||||
|
///
|
||||||
|
/// Rust aims to be correct, that's why it's major random number library is cryptographically secure,
|
||||||
|
/// meaning it's randomness can't easily be guessed. And cryptographically secure random number generation
|
||||||
|
/// is a big task, that's why it has it's own crate.
|
||||||
|
pub fn random_float() -> f64 {
|
||||||
|
((random_u64() >> 11) as f64) / ((1u64 << 53) as f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns an integer number contained in the range
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
/// ```
|
||||||
|
/// use simple_std::random_int_range;
|
||||||
|
///
|
||||||
|
/// let number = random_int_range(0..100);
|
||||||
|
///
|
||||||
|
/// println!("Number between 0 and 100: {}", number);
|
||||||
|
///
|
||||||
|
/// assert!(number < 100);
|
||||||
|
/// assert!(number >= 0);
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Why is this not in std?
|
||||||
|
///
|
||||||
|
/// See [`random_float`]
|
||||||
|
///
|
||||||
|
pub fn random_int_range(range: Range<i32>) -> i32 {
|
||||||
|
let difference = range.end - range.start;
|
||||||
|
range.start + ((random_u64() as i32).abs() % difference)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generates a pseudo-random u32
|
||||||
|
fn random_u64() -> u64 {
|
||||||
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
|
static STATE0: AtomicU64 = AtomicU64::new(0);
|
||||||
|
static STATE1: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
|
if STATE0.load(Ordering::SeqCst) == 0 {
|
||||||
|
// more or less random initial state
|
||||||
|
STATE0.store((system_time_random()) as u64, Ordering::SeqCst);
|
||||||
|
STATE1.store((system_time_random()) as u64, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use xorshift128+ because it's easy https://v8.dev/blog/math-random
|
||||||
|
|
||||||
|
// not a bug
|
||||||
|
let mut s1 = STATE0.load(Ordering::SeqCst);
|
||||||
|
let s0 = STATE1.load(Ordering::SeqCst);
|
||||||
|
|
||||||
|
STATE0.store(s0, Ordering::SeqCst);
|
||||||
|
|
||||||
|
s1 ^= s1 << 23;
|
||||||
|
s1 ^= s1 >> 17;
|
||||||
|
s1 ^= s0;
|
||||||
|
s1 ^= s0 >> 26;
|
||||||
|
|
||||||
|
STATE1.store(s1, Ordering::SeqCst);
|
||||||
|
|
||||||
|
s0.wrapping_add(s1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_time_random() -> u128 {
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_micros()
|
||||||
|
^ SystemTime::now()
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_nanos()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{random_float, random_int_range};
|
||||||
|
use std::iter::repeat_with;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn not_equal() {
|
||||||
|
repeat_with(random_float)
|
||||||
|
.take(100)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.windows(2)
|
||||||
|
.for_each(|win| assert_ne!(win[0], win[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn between_0_1() {
|
||||||
|
assert!(repeat_with(random_float)
|
||||||
|
.take(100000)
|
||||||
|
.all(|n| n >= 0.0 && n < 1.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn distributed() {
|
||||||
|
assert!(repeat_with(random_float).take(100000).any(|n| n > 0.999));
|
||||||
|
assert!(repeat_with(random_float).take(100000).any(|n| n < 0.001));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range_in_range() {
|
||||||
|
[0..10, 5..15, 1000..1004, (-5)..5, (-10)..(-5)]
|
||||||
|
.iter()
|
||||||
|
.for_each(|range| {
|
||||||
|
assert!(repeat_with(|| random_int_range(range.clone()))
|
||||||
|
.take(10000)
|
||||||
|
.all(|n| n < range.end && n >= range.start));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn distributed_range() {
|
||||||
|
[0..10, 5..15, 1000..1004, (-5)..5, (-10)..(-5)]
|
||||||
|
.iter()
|
||||||
|
.for_each(|range| {
|
||||||
|
range.clone().for_each(|expected| {
|
||||||
|
assert!(repeat_with(|| random_int_range(range.clone()))
|
||||||
|
.take(100000)
|
||||||
|
.any(|n| n == expected));
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue