language_distribution/
random_language.rs1crate::ix!();
2
3pub trait RandomLanguage {
4 fn random_language(&self) -> Option<Language>;
5}
6
7impl RandomLanguage for Country {
8 fn random_language(&self) -> Option<Language> {
9 let dist = self.language_distribution();
10 if dist.is_empty() {
11 return None;
12 }
13
14 let langs: Vec<Language> = dist.keys().cloned().collect();
15 let weights: Vec<f64> = langs.iter().map(|l| dist[l]).collect();
16
17 let weighted_dist = WeightedIndex::new(&weights).ok()?;
19 let mut rng = thread_rng();
20
21 Some(langs[weighted_dist.sample(&mut rng)].clone())
23 }
24}
25
26#[cfg(test)]
27mod random_language_tests {
28 use super::*;
29
30 #[test]
31 fn test_random_language_exists() {
32 let c = Country::Canada; let dist = c.language_distribution();
34 assert!(dist.contains_key(&Language::English));
35 assert!(dist.contains_key(&Language::French));
36
37 for _ in 0..10 {
39 let lang = c.random_language();
40 assert!(lang.is_some(), "Random language should not be None for Canada");
41 let chosen = lang.unwrap();
42 assert!(chosen == Language::English || chosen == Language::French, "Canada should yield English or French only");
43 }
44 }
45
46 #[test]
47 fn test_country_with_no_languages() {
48 struct EmptyCountry;
53 impl LanguageDistribution for EmptyCountry {
55 fn language_distribution(&self) -> HashMap<Language, f64> {
56 HashMap::new()
57 }
58 }
59
60 impl RandomLanguage for EmptyCountry {
61 fn random_language(&self) -> Option<Language> {
62 let dist = self.language_distribution();
63 if dist.is_empty() {
64 return None;
65 }
66 None
67 }
68 }
69
70 let e = EmptyCountry;
71 assert!(e.language_distribution().is_empty());
72 assert!(e.random_language().is_none(), "No languages means no random language should be returned");
73 }
74
75 #[test]
76 fn test_random_language_distribution_proportions() {
77 let c = Country::Belgium; let samples = 10_000;
84 let mut counts = HashMap::new();
85 counts.insert(Language::Dutch, 0);
86 counts.insert(Language::French, 0);
87 counts.insert(Language::German, 0);
88
89 for _ in 0..samples {
90 let lang = c.random_language().unwrap();
91 *counts.get_mut(&lang).unwrap() += 1;
92 }
93
94 let dutch_ratio = counts[&Language::Dutch] as f64 / samples as f64;
95 let french_ratio = counts[&Language::French] as f64 / samples as f64;
96 let german_ratio = counts[&Language::German] as f64 / samples as f64;
97
98 assert!((dutch_ratio - 0.55).abs() < 0.05, "Dutch ratio should be close to 0.55");
101 assert!((french_ratio - 0.44).abs() < 0.05, "French ratio should be close to 0.44");
102 assert!((german_ratio - 0.01).abs() < 0.01, "German ratio should be close to 0.01");
103 }
104}
105