use rand;
use std::fmt;
use std::collections::BTreeMap;
pub fn run(flips_per_iteration: usize, iterations: usize) -> CoinFlipResult {
let outcomes = get_all_outcomes(flips_per_iteration);
let mut results: BTreeMap<String, usize> = outcomes
.into_iter()
.map(|outcome| (outcome, 0))
.collect();
for _ in 0..iterations {
let mut flips = String::new();
for _ in 0..flips_per_iteration {
flips.push_str(&Coin::flip().to_string());
}
*results.entry(flips).or_insert(0) += 1;
}
let results = results
.into_iter()
.map(|(key, count)|{
(key, EmpiricalResult::new(count, iterations))
})
.collect();
let expected = EmpiricalResult::expected(flips_per_iteration, iterations);
CoinFlipResult::new(iterations, expected, results)
}
pub fn get_all_outcomes(flips_per_iteration: usize) -> Vec<String> {
let num_outcomes = get_num_outcomes(flips_per_iteration);
let mut results = Vec::with_capacity(num_outcomes);
let base: usize = 2;
for i in 0..num_outcomes {
let mut outcome = String::with_capacity(flips_per_iteration);
for j in 1..=flips_per_iteration {
let d = num_outcomes / base.pow(j as u32);
outcome.push_str(
if (i / d) % base == 0 { "H" } else { "T" }
);
}
results.push(outcome);
}
results
}
pub fn get_num_outcomes(flips_per_iteration: usize) -> usize {
let base: usize = 2;
base.pow(flips_per_iteration as u32)
}
pub struct CoinFlipResult {
pub iterations: usize,
pub expected: EmpiricalResult,
pub results: BTreeMap<String, EmpiricalResult>,
}
impl CoinFlipResult {
fn new(iterations: usize, expected: EmpiricalResult, results: BTreeMap<String, EmpiricalResult>) -> Self {
CoinFlipResult {
iterations,
expected,
results,
}
}
pub fn to_json_string(&self) -> String {
let indent = " ";
let mut json = format!(
"{{\n{indent}iterations: {}\n{indent}expected: {:.5}\n{indent}actual: {{\n",
self.iterations,
self.expected,
);
for (k, v) in self.results.iter() {
let entry = format!(
"{indent}{indent}{k}: {v}\n"
);
json.push_str(&entry);
}
let close = format!("{indent}}}\n}}");
json.push_str(&close);
json
}
}
impl fmt::Display for CoinFlipResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_json_string())
}
}
pub struct EmpiricalResult {
pub count: usize,
pub probability: f64,
}
impl EmpiricalResult {
fn new(count: usize, iterations: usize) -> Self {
let probability: f64 = (count as f64) / (iterations as f64);
EmpiricalResult {
count,
probability,
}
}
pub fn expected(flips_per_iteration: usize, iterations: usize) -> Self {
let num_outcomes = get_num_outcomes(flips_per_iteration);
let count = iterations / num_outcomes;
EmpiricalResult::new(count, iterations)
}
}
impl fmt::Display for EmpiricalResult {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{count: {}, probability: {:.5}}}", self.count, self.probability)
}
}
#[derive(PartialEq)]
enum Coin {
Heads,
Tails,
}
impl Coin {
fn flip() -> Self {
if rand::random() {
Coin::Heads
} else {
Coin::Tails
}
}
}
impl fmt::Display for Coin {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use Coin::{Heads, Tails};
match self {
Heads => write!(f, "H"),
Tails => write!(f, "T"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_all_outcomes_4() {
use Coin::{Heads, Tails};
let outcomes = get_all_outcomes(4);
let mut expected = Vec::new();
for i in [Heads, Tails] {
for j in [Heads, Tails] {
for k in [Heads, Tails] {
for l in [Heads, Tails] {
expected.push(format!("{i}{j}{k}{l}"));
}
}
}
}
assert_eq!(outcomes, expected);
}
#[test]
fn test_get_all_outcomes_6() {
use Coin::{Heads, Tails};
let outcomes = get_all_outcomes(6);
let mut expected = Vec::new();
for i in [Heads, Tails] {
for j in [Heads, Tails] {
for k in [Heads, Tails] {
for l in [Heads, Tails] {
for m in [Heads, Tails] {
for n in [Heads, Tails] {
expected.push(format!("{i}{j}{k}{l}{m}{n}"));
}
}
}
}
}
}
assert_eq!(outcomes, expected);
}
#[test]
fn test_coin_display() {
let h = Coin::Heads;
let t = Coin::Tails;
assert_eq!(
format!("{h}{t}"),
"HT"
);
}
#[test]
fn test_coin_to_string() {
let h = Coin::Heads;
assert_eq!(
h.to_string(),
"H"
);
}
#[test]
fn test_coin_eq() {
let c1 = Coin::Heads;
let c2 = Coin::Heads;
assert!(c1 == c2);
}
#[test]
fn test_coin_neq() {
let c1 = Coin::Heads;
let c2 = Coin::Tails;
assert!(c1 != c2);
}
}