1use std::fmt;
2
3#[derive(Debug, Copy, Clone, PartialEq, Eq)]
5pub enum Side {
6 Heads = 0,
7 Tails = 1,
8}
9
10impl fmt::Display for Side {
12 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13 match self {
14 Side::Heads => write!(f, "heads"),
15 Side::Tails => write!(f, "tails"),
16 }
17 }
18}
19
20impl Side {
21 pub fn is_heads(&self) -> bool {
22 match self {
23 Side::Heads => true,
24 Side::Tails => false,
25 }
26 }
27
28 pub fn is_tails(&self) -> bool {
29 !self.is_heads()
30 }
31}
32
33pub fn coinflip(randomness: [u8; 32]) -> Side {
53 if randomness[0] % 2 == 0 {
54 Side::Heads
55 } else {
56 Side::Tails
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63
64 const RANDOMNESS1: [u8; 32] = [
65 88, 85, 86, 91, 61, 64, 60, 71, 234, 24, 246, 200, 35, 73, 38, 187, 54, 59, 96, 9, 237, 27,
66 215, 103, 148, 230, 28, 48, 51, 114, 203, 219,
67 ];
68 const RANDOMNESS2: [u8; 32] = [
69 207, 251, 10, 105, 100, 223, 244, 6, 207, 231, 253, 206, 157, 68, 143, 184, 209, 222, 70,
70 249, 114, 160, 213, 73, 147, 94, 136, 191, 94, 98, 99, 170,
71 ];
72 const RANDOMNESS3: [u8; 32] = [
73 43, 140, 160, 0, 187, 41, 212, 6, 218, 53, 58, 198, 80, 209, 171, 239, 222, 247, 30, 23,
74 184, 79, 79, 221, 192, 225, 217, 142, 135, 164, 169, 255,
75 ];
76 const RANDOMNESS4: [u8; 32] = [
77 52, 187, 72, 255, 102, 110, 115, 233, 50, 165, 124, 255, 217, 131, 112, 209, 253, 176, 108,
78 99, 102, 225, 12, 36, 82, 107, 106, 207, 99, 107, 197, 84,
79 ];
80
81 #[test]
82 fn side_is_heads_and_is_tails_works() {
83 assert!(Side::Heads.is_heads());
84 assert!(!Side::Heads.is_tails());
85
86 assert!(Side::Tails.is_tails());
87 assert!(!Side::Tails.is_heads());
88 }
89
90 #[test]
91 fn side_implements_display() {
92 let heads = Side::Heads;
93 let embedded = format!("Side: {}", heads);
94 assert_eq!(embedded, "Side: heads");
95 assert_eq!(heads.to_string(), "heads");
96
97 let tails = Side::Tails;
98 let embedded = format!("Side: {}", tails);
99 assert_eq!(embedded, "Side: tails");
100 assert_eq!(tails.to_string(), "tails");
101 }
102
103 #[test]
104 fn coinflip_works() {
105 let result = coinflip(RANDOMNESS1);
106 assert_eq!(result, Side::Heads);
107
108 let result = coinflip(RANDOMNESS2);
109 assert_eq!(result, Side::Tails);
110
111 let result = coinflip(RANDOMNESS3);
112 assert_eq!(result, Side::Tails);
113
114 let result = coinflip(RANDOMNESS4);
115 assert_eq!(result, Side::Heads);
116 }
117 #[test]
118 fn coinflip_distribution_is_uniform() {
119 use crate::sub_randomness::sub_randomness;
123 use std::collections::HashMap;
124
125 const TEST_SAMPLE_SIZE: usize = 300_000;
126 const ACCURACY: f32 = 0.01;
127
128 let mut result = vec![];
129
130 for subrand in sub_randomness(RANDOMNESS1).take(TEST_SAMPLE_SIZE) {
131 result.push(coinflip(subrand).is_heads());
132 }
133
134 let mut histogram = HashMap::new();
135
136 for element in result {
137 let count = histogram.entry(element).or_insert(0);
138 *count += 1;
139 }
140
141 let estimated_count_for_uniform_distribution = (TEST_SAMPLE_SIZE / 2) as f32;
142 let estimation_min: i32 =
143 (estimated_count_for_uniform_distribution * (1_f32 - ACCURACY)) as i32;
144 let estimation_max: i32 =
145 (estimated_count_for_uniform_distribution * (1_f32 + ACCURACY)) as i32;
146 println!(
147 "estimation {}, max: {}, min: {}",
148 estimated_count_for_uniform_distribution, estimation_max, estimation_min
149 );
150 for (bin, count) in histogram {
152 println!("{}: {}", bin, count);
153 assert!(count >= estimation_min && count <= estimation_max);
154 }
155 }
156}