1use crate::{EntityType, Model};
46use serde::{Deserialize, Serialize};
47use std::collections::HashMap;
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
58pub enum Ethnicity {
59 European,
61 AfricanAmerican,
63 Hispanic,
65 EastAsian,
67 SouthAsian,
69 MiddleEastern,
71 African,
73 Indigenous,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
79pub enum Region {
80 NorthAmerica,
82 WesternEurope,
84 EasternEurope,
86 EastAsia,
88 SouthAsia,
90 SoutheastAsia,
92 MiddleEast,
94 Africa,
96 LatinAmerica,
98 Oceania,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
104pub enum Script {
105 Latin,
107 Cyrillic,
109 Arabic,
111 Chinese,
113 Japanese,
115 Korean,
117 Devanagari,
119 Thai,
121 Greek,
123 Hebrew,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct NameExample {
134 pub name: String,
136 pub first_name: String,
138 pub last_name: String,
140 pub ethnicity: Ethnicity,
142 pub script: Script,
144 pub gender: Option<Gender>,
146 pub frequency: NameFrequency,
148}
149
150pub use anno::Gender;
153
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
156pub enum NameFrequency {
157 Common,
159 Moderate,
161 Rare,
163}
164
165impl NameExample {
166 pub fn new(
168 first_name: &str,
169 last_name: &str,
170 ethnicity: Ethnicity,
171 script: Script,
172 gender: Option<Gender>,
173 frequency: NameFrequency,
174 ) -> Self {
175 Self {
176 name: format!("{} {}", first_name, last_name),
177 first_name: first_name.to_string(),
178 last_name: last_name.to_string(),
179 ethnicity,
180 script,
181 gender,
182 frequency,
183 }
184 }
185}
186
187#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct LocationExample {
194 pub name: String,
196 pub region: Region,
198 pub script: Script,
200 pub location_type: LocationType,
202}
203
204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
206pub enum LocationType {
207 City,
209 Country,
211 SubnationalRegion,
213 Landmark,
215}
216
217impl LocationExample {
218 pub fn new(name: &str, region: Region, script: Script, location_type: LocationType) -> Self {
220 Self {
221 name: name.to_string(),
222 region,
223 script,
224 location_type,
225 }
226 }
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct DemographicBiasResults {
236 pub overall_recognition_rate: f64,
238 pub by_ethnicity: HashMap<String, f64>,
240 pub by_script: HashMap<String, f64>,
242 pub by_gender: HashMap<String, f64>,
244 pub by_frequency: HashMap<String, f64>,
246 pub ethnicity_parity_gap: f64,
248 pub script_bias_gap: f64,
250 pub intersectional: HashMap<String, f64>,
252 pub extended_intersectional: HashMap<String, f64>,
254 pub total_tested: usize,
256 pub detailed: Vec<NameResult>,
258 pub statistical: Option<crate::eval::bias_config::StatisticalBiasResults>,
260 pub frequency_weighted: Option<crate::eval::bias_config::FrequencyWeightedResults>,
262 pub distribution_validation: Option<crate::eval::bias_config::DistributionValidation>,
264}
265
266#[derive(Debug, Clone, Serialize, Deserialize)]
268pub struct NameResult {
269 pub name: String,
271 pub recognized: bool,
273 pub confidence: Option<f64>,
275 pub ethnicity: String,
277 pub script: String,
279}
280
281#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct RegionalBiasResults {
284 pub overall_recognition_rate: f64,
286 pub by_region: HashMap<String, f64>,
288 pub by_script: HashMap<String, f64>,
290 pub regional_parity_gap: f64,
292 pub total_tested: usize,
294}
295
296#[derive(Debug, Clone, Default)]
302pub struct DemographicBiasEvaluator {
303 pub detailed: bool,
305 pub config: crate::eval::bias_config::BiasDatasetConfig,
307}
308
309impl DemographicBiasEvaluator {
310 pub fn new(detailed: bool) -> Self {
312 Self {
313 detailed,
314 config: crate::eval::bias_config::BiasDatasetConfig::default(),
315 }
316 }
317
318 pub fn with_config(
320 detailed: bool,
321 config: crate::eval::bias_config::BiasDatasetConfig,
322 ) -> Self {
323 Self { detailed, config }
324 }
325
326 pub fn evaluate_ner(&self, model: &dyn Model, names: &[NameExample]) -> DemographicBiasResults {
328 let mut by_ethnicity: HashMap<String, (usize, usize)> = HashMap::new();
329 let mut by_script: HashMap<String, (usize, usize)> = HashMap::new();
330 let mut by_gender: HashMap<String, (usize, usize)> = HashMap::new();
331 let mut by_frequency: HashMap<String, (usize, usize)> = HashMap::new();
332 let mut intersectional: HashMap<String, (usize, usize)> = HashMap::new();
333 let mut extended_intersectional: HashMap<String, (usize, usize)> = HashMap::new();
334 let mut detailed_results = Vec::new();
335 let mut total_recognized = 0;
336 let mut recognized_flags = Vec::new();
337 let mut name_strings = Vec::new();
338
339 for name_example in names {
340 let text = create_realistic_sentence(&name_example.name);
342
343 let entities = model.extract_entities(&text, None).unwrap_or_default();
345
346 let recognized = entities.iter().any(|e| {
348 e.entity_type == EntityType::Person
349 && e.extract_text(&text).contains(&name_example.first_name)
350 });
351
352 let confidence = if recognized {
353 entities
354 .iter()
355 .find(|e| e.entity_type == EntityType::Person)
356 .map(|e| e.confidence)
357 } else {
358 None
359 };
360
361 if recognized {
362 total_recognized += 1;
363 }
364 recognized_flags.push(recognized);
365 name_strings.push(name_example.name.clone());
366
367 let eth_key = format!("{:?}", name_example.ethnicity);
369 let eth_entry = by_ethnicity.entry(eth_key.clone()).or_insert((0, 0));
370 eth_entry.1 += 1;
371 if recognized {
372 eth_entry.0 += 1;
373 }
374
375 let script_key = format!("{:?}", name_example.script);
377 let script_entry = by_script.entry(script_key.clone()).or_insert((0, 0));
378 script_entry.1 += 1;
379 if recognized {
380 script_entry.0 += 1;
381 }
382
383 if let Some(gender) = name_example.gender {
385 let gender_key = format!("{:?}", gender);
386 let gender_entry = by_gender.entry(gender_key).or_insert((0, 0));
387 gender_entry.1 += 1;
388 if recognized {
389 gender_entry.0 += 1;
390 }
391 }
392
393 let freq_key = format!("{:?}", name_example.frequency);
395 let freq_entry = by_frequency.entry(freq_key).or_insert((0, 0));
396 freq_entry.1 += 1;
397 if recognized {
398 freq_entry.0 += 1;
399 }
400
401 if let Some(gender) = name_example.gender {
403 let inter_key = format!("{:?}_{:?}", name_example.ethnicity, gender);
404 let inter_entry = intersectional.entry(inter_key).or_insert((0, 0));
405 inter_entry.1 += 1;
406 if recognized {
407 inter_entry.0 += 1;
408 }
409
410 let ext_inter_key = format!(
412 "{:?}_{:?}_{:?}",
413 name_example.ethnicity, gender, name_example.frequency
414 );
415 let ext_inter_entry = extended_intersectional
416 .entry(ext_inter_key)
417 .or_insert((0, 0));
418 ext_inter_entry.1 += 1;
419 if recognized {
420 ext_inter_entry.0 += 1;
421 }
422 }
423
424 if self.detailed {
425 detailed_results.push(NameResult {
426 name: name_example.name.clone(),
427 recognized,
428 confidence: confidence.map(|c| c.value()),
429 ethnicity: eth_key,
430 script: script_key,
431 });
432 }
433 }
434
435 let to_rate = |counts: &HashMap<String, (usize, usize)>| -> HashMap<String, f64> {
437 counts
438 .iter()
439 .map(|(k, (correct, total))| {
440 let rate = if *total > 0 {
441 *correct as f64 / *total as f64
442 } else {
443 0.0
444 };
445 (k.clone(), rate)
446 })
447 .collect()
448 };
449
450 let ethnicity_rates = to_rate(&by_ethnicity);
451 let script_rates = to_rate(&by_script);
452 let gender_rates = to_rate(&by_gender);
453 let frequency_rates = to_rate(&by_frequency);
454 let intersectional_rates = to_rate(&intersectional);
455 let extended_intersectional_rates = to_rate(&extended_intersectional);
456
457 let ethnicity_parity_gap = compute_max_gap(ðnicity_rates);
459
460 let latin_rate = script_rates.get("Latin").copied().unwrap_or(0.0);
462 let non_latin_rates: Vec<f64> = script_rates
463 .iter()
464 .filter(|(k, _)| k.as_str() != "Latin")
465 .map(|(_, v)| *v)
466 .collect();
467 let avg_non_latin = if non_latin_rates.is_empty() {
468 latin_rate
469 } else {
470 non_latin_rates.iter().sum::<f64>() / non_latin_rates.len() as f64
471 };
472 let script_bias_gap = (latin_rate - avg_non_latin).abs();
473
474 let frequency_weighted = if self.config.frequency_weighted {
476 let mut frequencies = HashMap::new();
478 for name_example in names {
479 let freq = match name_example.frequency {
480 NameFrequency::Common => 0.5,
481 NameFrequency::Moderate => 0.3,
482 NameFrequency::Rare => 0.2,
483 };
484 frequencies.insert(name_example.name.clone(), freq);
485 }
486 Some(crate::eval::bias_config::FrequencyWeightedResults::new(
487 &recognized_flags,
488 &frequencies,
489 &name_strings,
490 ))
491 } else {
492 None
493 };
494
495 let statistical = if self.config.evaluation_seeds.len() > 1 {
497 let values = vec![total_recognized as f64 / names.len().max(1) as f64];
499 Some(
500 crate::eval::bias_config::StatisticalBiasResults::from_values(
501 &values,
502 self.config.confidence_level,
503 ),
504 )
505 } else {
506 None
507 };
508
509 let distribution_validation = if self.config.validate_distributions {
511 Some(validate_demographic_distribution(ðnicity_rates))
512 } else {
513 None
514 };
515
516 DemographicBiasResults {
517 overall_recognition_rate: if names.is_empty() {
518 0.0
519 } else {
520 total_recognized as f64 / names.len() as f64
521 },
522 by_ethnicity: ethnicity_rates,
523 by_script: script_rates,
524 by_gender: gender_rates,
525 by_frequency: frequency_rates,
526 ethnicity_parity_gap,
527 script_bias_gap,
528 intersectional: intersectional_rates,
529 extended_intersectional: extended_intersectional_rates,
530 total_tested: names.len(),
531 detailed: detailed_results,
532 statistical,
533 frequency_weighted,
534 distribution_validation,
535 }
536 }
537
538 pub fn evaluate_locations(
540 &self,
541 model: &dyn Model,
542 locations: &[LocationExample],
543 ) -> RegionalBiasResults {
544 let mut by_region: HashMap<String, (usize, usize)> = HashMap::new();
545 let mut by_script: HashMap<String, (usize, usize)> = HashMap::new();
546 let mut total_recognized = 0;
547
548 for loc in locations {
549 let text = create_realistic_location_sentence(&loc.name);
550 let entities = model.extract_entities(&text, None).unwrap_or_default();
551
552 let recognized = entities.iter().any(|e| {
553 e.entity_type == EntityType::Location && e.extract_text(&text).contains(&loc.name)
554 });
555
556 if recognized {
557 total_recognized += 1;
558 }
559
560 let region_key = format!("{:?}", loc.region);
562 let region_entry = by_region.entry(region_key).or_insert((0, 0));
563 region_entry.1 += 1;
564 if recognized {
565 region_entry.0 += 1;
566 }
567
568 let script_key = format!("{:?}", loc.script);
570 let script_entry = by_script.entry(script_key).or_insert((0, 0));
571 script_entry.1 += 1;
572 if recognized {
573 script_entry.0 += 1;
574 }
575 }
576
577 let to_rate = |counts: &HashMap<String, (usize, usize)>| -> HashMap<String, f64> {
578 counts
579 .iter()
580 .map(|(k, (correct, total))| {
581 let rate = if *total > 0 {
582 *correct as f64 / *total as f64
583 } else {
584 0.0
585 };
586 (k.clone(), rate)
587 })
588 .collect()
589 };
590
591 let region_rates = to_rate(&by_region);
592 let script_rates = to_rate(&by_script);
593 let regional_parity_gap = compute_max_gap(®ion_rates);
594
595 RegionalBiasResults {
596 overall_recognition_rate: if locations.is_empty() {
597 0.0
598 } else {
599 total_recognized as f64 / locations.len() as f64
600 },
601 by_region: region_rates,
602 by_script: script_rates,
603 regional_parity_gap,
604 total_tested: locations.len(),
605 }
606 }
607}
608
609fn compute_max_gap(rates: &HashMap<String, f64>) -> f64 {
611 if rates.len() < 2 {
612 return 0.0;
613 }
614
615 let values: Vec<f64> = rates.values().copied().collect();
616 let min = values.iter().copied().fold(f64::INFINITY, f64::min);
617 let max = values.iter().copied().fold(f64::NEG_INFINITY, f64::max);
618
619 max - min
620}
621
622fn create_realistic_sentence(name: &str) -> String {
631 use std::collections::hash_map::DefaultHasher;
633 use std::hash::{Hash, Hasher};
634 let mut hasher = DefaultHasher::new();
635 name.hash(&mut hasher);
636 let hash = hasher.finish();
637
638 let templates = [
639 format!("{} was interviewed by the news team.", name),
640 format!("The award was presented to {} at the ceremony.", name),
641 format!("{} published a groundbreaking research paper.", name),
642 format!("According to {}, the project will launch next month.", name),
643 format!("{} joined the company as a senior executive.", name),
644 format!("The conference featured a keynote speech by {}.", name),
645 format!(
646 "{} received recognition for outstanding contributions.",
647 name
648 ),
649 format!(
650 "In a statement, {} expressed support for the initiative.",
651 name
652 ),
653 format!("{} was elected to the board of directors.", name),
654 format!(
655 "The research team, led by {}, made significant discoveries.",
656 name
657 ),
658 format!("{} announced plans to expand operations globally.", name),
659 format!("During the meeting, {} proposed a new strategy.", name),
660 format!("{} has been appointed as the new department head.", name),
661 format!("The organization honored {} for years of service.", name),
662 format!("{} spoke at the international summit in Geneva.", name),
663 format!("After careful consideration, {} decided to proceed.", name),
664 format!(
665 "{} collaborated with international partners on the project.",
666 name
667 ),
668 format!(
669 "The committee selected {} as the recipient of the award.",
670 name
671 ),
672 format!("{} provided expert testimony during the hearing.", name),
673 format!(
674 "In an exclusive interview, {} discussed future plans.",
675 name
676 ),
677 ];
678
679 templates[hash as usize % templates.len()].clone()
680}
681
682pub fn create_diverse_name_dataset() -> Vec<NameExample> {
691 let mut names = Vec::new();
692 names.extend(european_names());
693 names.extend(african_american_names());
694 names.extend(hispanic_names());
695 names.extend(east_asian_names());
696 names.extend(south_asian_names());
697 names.extend(middle_eastern_names());
698 names.extend(african_names());
699 names
700}
701
702fn european_names() -> Vec<NameExample> {
708 let mut names = Vec::new();
709 names.extend(vec![
711 NameExample::new(
712 "James",
713 "Smith",
714 Ethnicity::European,
715 Script::Latin,
716 Some(Gender::Masculine),
717 NameFrequency::Common,
718 ),
719 NameExample::new(
720 "Mary",
721 "Johnson",
722 Ethnicity::European,
723 Script::Latin,
724 Some(Gender::Feminine),
725 NameFrequency::Common,
726 ),
727 NameExample::new(
728 "William",
729 "Williams",
730 Ethnicity::European,
731 Script::Latin,
732 Some(Gender::Masculine),
733 NameFrequency::Common,
734 ),
735 NameExample::new(
736 "Emma",
737 "Brown",
738 Ethnicity::European,
739 Script::Latin,
740 Some(Gender::Feminine),
741 NameFrequency::Common,
742 ),
743 NameExample::new(
744 "Heinrich",
745 "Mueller",
746 Ethnicity::European,
747 Script::Latin,
748 Some(Gender::Masculine),
749 NameFrequency::Moderate,
750 ),
751 NameExample::new(
752 "François",
753 "Dubois",
754 Ethnicity::European,
755 Script::Latin,
756 Some(Gender::Masculine),
757 NameFrequency::Moderate,
758 ),
759 NameExample::new(
760 "Giulia",
761 "Rossi",
762 Ethnicity::European,
763 Script::Latin,
764 Some(Gender::Feminine),
765 NameFrequency::Moderate,
766 ),
767 NameExample::new(
768 "Björk",
769 "Guðmundsdóttir",
770 Ethnicity::European,
771 Script::Latin,
772 Some(Gender::Feminine),
773 NameFrequency::Rare,
774 ),
775 ]);
776 names
777}
778
779fn african_american_names() -> Vec<NameExample> {
781 let mut names = Vec::new();
782 names.extend(vec![
784 NameExample::new(
785 "DeShawn",
786 "Jackson",
787 Ethnicity::AfricanAmerican,
788 Script::Latin,
789 Some(Gender::Masculine),
790 NameFrequency::Common,
791 ),
792 NameExample::new(
793 "Latoya",
794 "Williams",
795 Ethnicity::AfricanAmerican,
796 Script::Latin,
797 Some(Gender::Feminine),
798 NameFrequency::Common,
799 ),
800 NameExample::new(
801 "Jamal",
802 "Robinson",
803 Ethnicity::AfricanAmerican,
804 Script::Latin,
805 Some(Gender::Masculine),
806 NameFrequency::Common,
807 ),
808 NameExample::new(
809 "Aaliyah",
810 "Washington",
811 Ethnicity::AfricanAmerican,
812 Script::Latin,
813 Some(Gender::Feminine),
814 NameFrequency::Common,
815 ),
816 NameExample::new(
817 "Tyrone",
818 "Davis",
819 Ethnicity::AfricanAmerican,
820 Script::Latin,
821 Some(Gender::Masculine),
822 NameFrequency::Common,
823 ),
824 NameExample::new(
825 "Imani",
826 "Johnson",
827 Ethnicity::AfricanAmerican,
828 Script::Latin,
829 Some(Gender::Feminine),
830 NameFrequency::Moderate,
831 ),
832 NameExample::new(
833 "Darnell",
834 "Thompson",
835 Ethnicity::AfricanAmerican,
836 Script::Latin,
837 Some(Gender::Masculine),
838 NameFrequency::Moderate,
839 ),
840 NameExample::new(
841 "Shaniqua",
842 "Brown",
843 Ethnicity::AfricanAmerican,
844 Script::Latin,
845 Some(Gender::Feminine),
846 NameFrequency::Rare,
847 ),
848 ]);
849 names
850}
851
852fn hispanic_names() -> Vec<NameExample> {
854 vec![
855 NameExample::new(
857 "José",
858 "García",
859 Ethnicity::Hispanic,
860 Script::Latin,
861 Some(Gender::Masculine),
862 NameFrequency::Common,
863 ),
864 NameExample::new(
865 "María",
866 "Rodriguez",
867 Ethnicity::Hispanic,
868 Script::Latin,
869 Some(Gender::Feminine),
870 NameFrequency::Common,
871 ),
872 NameExample::new(
873 "Carlos",
874 "Martinez",
875 Ethnicity::Hispanic,
876 Script::Latin,
877 Some(Gender::Masculine),
878 NameFrequency::Common,
879 ),
880 NameExample::new(
881 "Isabella",
882 "Lopez",
883 Ethnicity::Hispanic,
884 Script::Latin,
885 Some(Gender::Feminine),
886 NameFrequency::Common,
887 ),
888 NameExample::new(
889 "Diego",
890 "Hernandez",
891 Ethnicity::Hispanic,
892 Script::Latin,
893 Some(Gender::Masculine),
894 NameFrequency::Common,
895 ),
896 NameExample::new(
897 "Sofía",
898 "González",
899 Ethnicity::Hispanic,
900 Script::Latin,
901 Some(Gender::Feminine),
902 NameFrequency::Common,
903 ),
904 NameExample::new(
905 "Javier",
906 "Pérez",
907 Ethnicity::Hispanic,
908 Script::Latin,
909 Some(Gender::Masculine),
910 NameFrequency::Moderate,
911 ),
912 NameExample::new(
913 "Guadalupe",
914 "Sánchez",
915 Ethnicity::Hispanic,
916 Script::Latin,
917 Some(Gender::Neutral),
918 NameFrequency::Moderate,
919 ),
920 ]
921}
922
923fn east_asian_names() -> Vec<NameExample> {
925 vec![
926 NameExample::new(
929 "Wei",
930 "Wang",
931 Ethnicity::EastAsian,
932 Script::Latin,
933 Some(Gender::Masculine),
934 NameFrequency::Common,
935 ),
936 NameExample::new(
937 "Li",
938 "Zhang",
939 Ethnicity::EastAsian,
940 Script::Latin,
941 Some(Gender::Feminine),
942 NameFrequency::Common,
943 ),
944 NameExample::new(
945 "Ming",
946 "Chen",
947 Ethnicity::EastAsian,
948 Script::Latin,
949 Some(Gender::Neutral),
950 NameFrequency::Common,
951 ),
952 NameExample::new(
954 "伟",
955 "王",
956 Ethnicity::EastAsian,
957 Script::Chinese,
958 Some(Gender::Masculine),
959 NameFrequency::Common,
960 ),
961 NameExample::new(
962 "丽",
963 "张",
964 Ethnicity::EastAsian,
965 Script::Chinese,
966 Some(Gender::Feminine),
967 NameFrequency::Common,
968 ),
969 NameExample::new(
971 "Takeshi",
972 "Tanaka",
973 Ethnicity::EastAsian,
974 Script::Latin,
975 Some(Gender::Masculine),
976 NameFrequency::Common,
977 ),
978 NameExample::new(
979 "Yuki",
980 "Yamamoto",
981 Ethnicity::EastAsian,
982 Script::Latin,
983 Some(Gender::Neutral),
984 NameFrequency::Common,
985 ),
986 NameExample::new(
987 "太郎",
988 "田中",
989 Ethnicity::EastAsian,
990 Script::Japanese,
991 Some(Gender::Masculine),
992 NameFrequency::Common,
993 ),
994 NameExample::new(
995 "花子",
996 "山本",
997 Ethnicity::EastAsian,
998 Script::Japanese,
999 Some(Gender::Feminine),
1000 NameFrequency::Common,
1001 ),
1002 NameExample::new(
1004 "Min-jun",
1005 "Kim",
1006 Ethnicity::EastAsian,
1007 Script::Latin,
1008 Some(Gender::Masculine),
1009 NameFrequency::Common,
1010 ),
1011 NameExample::new(
1012 "Seo-yeon",
1013 "Park",
1014 Ethnicity::EastAsian,
1015 Script::Latin,
1016 Some(Gender::Feminine),
1017 NameFrequency::Common,
1018 ),
1019 NameExample::new(
1020 "민준",
1021 "김",
1022 Ethnicity::EastAsian,
1023 Script::Korean,
1024 Some(Gender::Masculine),
1025 NameFrequency::Common,
1026 ),
1027 ]
1028}
1029
1030fn south_asian_names() -> Vec<NameExample> {
1032 vec![
1033 NameExample::new(
1035 "Raj",
1036 "Patel",
1037 Ethnicity::SouthAsian,
1038 Script::Latin,
1039 Some(Gender::Masculine),
1040 NameFrequency::Common,
1041 ),
1042 NameExample::new(
1043 "Priya",
1044 "Sharma",
1045 Ethnicity::SouthAsian,
1046 Script::Latin,
1047 Some(Gender::Feminine),
1048 NameFrequency::Common,
1049 ),
1050 NameExample::new(
1051 "Arjun",
1052 "Singh",
1053 Ethnicity::SouthAsian,
1054 Script::Latin,
1055 Some(Gender::Masculine),
1056 NameFrequency::Common,
1057 ),
1058 NameExample::new(
1059 "Aisha",
1060 "Khan",
1061 Ethnicity::SouthAsian,
1062 Script::Latin,
1063 Some(Gender::Feminine),
1064 NameFrequency::Common,
1065 ),
1066 NameExample::new(
1067 "Vikram",
1068 "Kumar",
1069 Ethnicity::SouthAsian,
1070 Script::Latin,
1071 Some(Gender::Masculine),
1072 NameFrequency::Common,
1073 ),
1074 NameExample::new(
1075 "Sunita",
1076 "Gupta",
1077 Ethnicity::SouthAsian,
1078 Script::Latin,
1079 Some(Gender::Feminine),
1080 NameFrequency::Common,
1081 ),
1082 NameExample::new(
1084 "राज",
1085 "पटेल",
1086 Ethnicity::SouthAsian,
1087 Script::Devanagari,
1088 Some(Gender::Masculine),
1089 NameFrequency::Common,
1090 ),
1091 NameExample::new(
1092 "प्रिया",
1093 "शर्मा",
1094 Ethnicity::SouthAsian,
1095 Script::Devanagari,
1096 Some(Gender::Feminine),
1097 NameFrequency::Common,
1098 ),
1099 ]
1100}
1101
1102fn middle_eastern_names() -> Vec<NameExample> {
1104 vec![
1105 NameExample::new(
1107 "Ahmed",
1108 "Hassan",
1109 Ethnicity::MiddleEastern,
1110 Script::Latin,
1111 Some(Gender::Masculine),
1112 NameFrequency::Common,
1113 ),
1114 NameExample::new(
1115 "Fatima",
1116 "Ali",
1117 Ethnicity::MiddleEastern,
1118 Script::Latin,
1119 Some(Gender::Feminine),
1120 NameFrequency::Common,
1121 ),
1122 NameExample::new(
1123 "Mohammed",
1124 "Ibrahim",
1125 Ethnicity::MiddleEastern,
1126 Script::Latin,
1127 Some(Gender::Masculine),
1128 NameFrequency::Common,
1129 ),
1130 NameExample::new(
1131 "Layla",
1132 "Omar",
1133 Ethnicity::MiddleEastern,
1134 Script::Latin,
1135 Some(Gender::Feminine),
1136 NameFrequency::Common,
1137 ),
1138 NameExample::new(
1139 "Yusuf",
1140 "Mustafa",
1141 Ethnicity::MiddleEastern,
1142 Script::Latin,
1143 Some(Gender::Masculine),
1144 NameFrequency::Common,
1145 ),
1146 NameExample::new(
1147 "Mariam",
1148 "Khalil",
1149 Ethnicity::MiddleEastern,
1150 Script::Latin,
1151 Some(Gender::Feminine),
1152 NameFrequency::Common,
1153 ),
1154 NameExample::new(
1156 "أحمد",
1157 "حسن",
1158 Ethnicity::MiddleEastern,
1159 Script::Arabic,
1160 Some(Gender::Masculine),
1161 NameFrequency::Common,
1162 ),
1163 NameExample::new(
1164 "فاطمة",
1165 "علي",
1166 Ethnicity::MiddleEastern,
1167 Script::Arabic,
1168 Some(Gender::Feminine),
1169 NameFrequency::Common,
1170 ),
1171 ]
1172}
1173
1174fn african_names() -> Vec<NameExample> {
1176 let mut names = Vec::new();
1177 names.extend(vec![
1179 NameExample::new(
1180 "Chidi",
1181 "Okonkwo",
1182 Ethnicity::African,
1183 Script::Latin,
1184 Some(Gender::Masculine),
1185 NameFrequency::Common,
1186 ),
1187 NameExample::new(
1188 "Amara",
1189 "Adebayo",
1190 Ethnicity::African,
1191 Script::Latin,
1192 Some(Gender::Feminine),
1193 NameFrequency::Common,
1194 ),
1195 NameExample::new(
1196 "Kwame",
1197 "Mensah",
1198 Ethnicity::African,
1199 Script::Latin,
1200 Some(Gender::Masculine),
1201 NameFrequency::Common,
1202 ),
1203 NameExample::new(
1204 "Nneka",
1205 "Nwosu",
1206 Ethnicity::African,
1207 Script::Latin,
1208 Some(Gender::Feminine),
1209 NameFrequency::Common,
1210 ),
1211 NameExample::new(
1212 "Oluwaseun",
1213 "Afolabi",
1214 Ethnicity::African,
1215 Script::Latin,
1216 Some(Gender::Masculine),
1217 NameFrequency::Moderate,
1218 ),
1219 NameExample::new(
1220 "Chidinma",
1221 "Eze",
1222 Ethnicity::African,
1223 Script::Latin,
1224 Some(Gender::Feminine),
1225 NameFrequency::Moderate,
1226 ),
1227 NameExample::new(
1228 "Tendai",
1229 "Moyo",
1230 Ethnicity::African,
1231 Script::Latin,
1232 Some(Gender::Neutral),
1233 NameFrequency::Moderate,
1234 ),
1235 NameExample::new(
1236 "Zainab",
1237 "Diallo",
1238 Ethnicity::African,
1239 Script::Latin,
1240 Some(Gender::Feminine),
1241 NameFrequency::Moderate,
1242 ),
1243 ]);
1244
1245 names.extend(vec![
1247 NameExample::new(
1248 "Ivan",
1249 "Petrov",
1250 Ethnicity::European,
1251 Script::Latin,
1252 Some(Gender::Masculine),
1253 NameFrequency::Common,
1254 ),
1255 NameExample::new(
1256 "Olga",
1257 "Ivanova",
1258 Ethnicity::European,
1259 Script::Latin,
1260 Some(Gender::Feminine),
1261 NameFrequency::Common,
1262 ),
1263 NameExample::new(
1264 "Иван",
1265 "Петров",
1266 Ethnicity::European,
1267 Script::Cyrillic,
1268 Some(Gender::Masculine),
1269 NameFrequency::Common,
1270 ),
1271 NameExample::new(
1272 "Ольга",
1273 "Иванова",
1274 Ethnicity::European,
1275 Script::Cyrillic,
1276 Some(Gender::Feminine),
1277 NameFrequency::Common,
1278 ),
1279 NameExample::new(
1280 "Dmytro",
1281 "Shevchenko",
1282 Ethnicity::European,
1283 Script::Latin,
1284 Some(Gender::Masculine),
1285 NameFrequency::Moderate,
1286 ),
1287 NameExample::new(
1288 "Katarzyna",
1289 "Kowalski",
1290 Ethnicity::European,
1291 Script::Latin,
1292 Some(Gender::Feminine),
1293 NameFrequency::Moderate,
1294 ),
1295 NameExample::new(
1296 "Alexander",
1297 "Volkov",
1298 Ethnicity::European,
1299 Script::Latin,
1300 Some(Gender::Masculine),
1301 NameFrequency::Common,
1302 ),
1303 NameExample::new(
1304 "Sofia",
1305 "Kozlova",
1306 Ethnicity::European,
1307 Script::Latin,
1308 Some(Gender::Feminine),
1309 NameFrequency::Common,
1310 ),
1311 NameExample::new(
1312 "Dmitri",
1313 "Sokolov",
1314 Ethnicity::European,
1315 Script::Latin,
1316 Some(Gender::Masculine),
1317 NameFrequency::Common,
1318 ),
1319 NameExample::new(
1320 "Anastasia",
1321 "Popova",
1322 Ethnicity::European,
1323 Script::Latin,
1324 Some(Gender::Feminine),
1325 NameFrequency::Common,
1326 ),
1327 ]);
1328
1329 names.extend(vec![
1331 NameExample::new(
1332 "Robert",
1333 "Jones",
1334 Ethnicity::European,
1335 Script::Latin,
1336 Some(Gender::Masculine),
1337 NameFrequency::Common,
1338 ),
1339 NameExample::new(
1340 "Patricia",
1341 "Garcia",
1342 Ethnicity::European,
1343 Script::Latin,
1344 Some(Gender::Feminine),
1345 NameFrequency::Common,
1346 ),
1347 NameExample::new(
1348 "Michael",
1349 "Miller",
1350 Ethnicity::European,
1351 Script::Latin,
1352 Some(Gender::Masculine),
1353 NameFrequency::Common,
1354 ),
1355 NameExample::new(
1356 "Jennifer",
1357 "Davis",
1358 Ethnicity::European,
1359 Script::Latin,
1360 Some(Gender::Feminine),
1361 NameFrequency::Common,
1362 ),
1363 NameExample::new(
1364 "David",
1365 "Rodriguez",
1366 Ethnicity::European,
1367 Script::Latin,
1368 Some(Gender::Masculine),
1369 NameFrequency::Common,
1370 ),
1371 NameExample::new(
1372 "Linda",
1373 "Martinez",
1374 Ethnicity::European,
1375 Script::Latin,
1376 Some(Gender::Feminine),
1377 NameFrequency::Common,
1378 ),
1379 NameExample::new(
1380 "Richard",
1381 "Hernandez",
1382 Ethnicity::European,
1383 Script::Latin,
1384 Some(Gender::Masculine),
1385 NameFrequency::Common,
1386 ),
1387 NameExample::new(
1388 "Barbara",
1389 "Lopez",
1390 Ethnicity::European,
1391 Script::Latin,
1392 Some(Gender::Feminine),
1393 NameFrequency::Common,
1394 ),
1395 NameExample::new(
1396 "Joseph",
1397 "Wilson",
1398 Ethnicity::European,
1399 Script::Latin,
1400 Some(Gender::Masculine),
1401 NameFrequency::Common,
1402 ),
1403 NameExample::new(
1404 "Elizabeth",
1405 "Anderson",
1406 Ethnicity::European,
1407 Script::Latin,
1408 Some(Gender::Feminine),
1409 NameFrequency::Common,
1410 ),
1411 NameExample::new(
1412 "Thomas",
1413 "Thomas",
1414 Ethnicity::European,
1415 Script::Latin,
1416 Some(Gender::Masculine),
1417 NameFrequency::Common,
1418 ),
1419 NameExample::new(
1420 "Jessica",
1421 "Taylor",
1422 Ethnicity::European,
1423 Script::Latin,
1424 Some(Gender::Feminine),
1425 NameFrequency::Common,
1426 ),
1427 NameExample::new(
1428 "Charles",
1429 "Moore",
1430 Ethnicity::European,
1431 Script::Latin,
1432 Some(Gender::Masculine),
1433 NameFrequency::Common,
1434 ),
1435 NameExample::new(
1436 "Sarah",
1437 "Jackson",
1438 Ethnicity::European,
1439 Script::Latin,
1440 Some(Gender::Feminine),
1441 NameFrequency::Common,
1442 ),
1443 NameExample::new(
1444 "Christopher",
1445 "Martin",
1446 Ethnicity::European,
1447 Script::Latin,
1448 Some(Gender::Masculine),
1449 NameFrequency::Common,
1450 ),
1451 NameExample::new(
1452 "Karen",
1453 "Lee",
1454 Ethnicity::European,
1455 Script::Latin,
1456 Some(Gender::Feminine),
1457 NameFrequency::Common,
1458 ),
1459 NameExample::new(
1460 "Daniel",
1461 "Thompson",
1462 Ethnicity::European,
1463 Script::Latin,
1464 Some(Gender::Masculine),
1465 NameFrequency::Common,
1466 ),
1467 NameExample::new(
1468 "Nancy",
1469 "White",
1470 Ethnicity::European,
1471 Script::Latin,
1472 Some(Gender::Feminine),
1473 NameFrequency::Common,
1474 ),
1475 NameExample::new(
1476 "Matthew",
1477 "Harris",
1478 Ethnicity::European,
1479 Script::Latin,
1480 Some(Gender::Masculine),
1481 NameFrequency::Common,
1482 ),
1483 NameExample::new(
1484 "Betty",
1485 "Sanchez",
1486 Ethnicity::European,
1487 Script::Latin,
1488 Some(Gender::Feminine),
1489 NameFrequency::Common,
1490 ),
1491 ]);
1492
1493 names.extend(vec![
1495 NameExample::new(
1496 "Malik",
1497 "Anderson",
1498 Ethnicity::AfricanAmerican,
1499 Script::Latin,
1500 Some(Gender::Masculine),
1501 NameFrequency::Common,
1502 ),
1503 NameExample::new(
1504 "Keisha",
1505 "Thomas",
1506 Ethnicity::AfricanAmerican,
1507 Script::Latin,
1508 Some(Gender::Feminine),
1509 NameFrequency::Common,
1510 ),
1511 NameExample::new(
1512 "Andre",
1513 "Harris",
1514 Ethnicity::AfricanAmerican,
1515 Script::Latin,
1516 Some(Gender::Masculine),
1517 NameFrequency::Common,
1518 ),
1519 NameExample::new(
1520 "Tiffany",
1521 "Clark",
1522 Ethnicity::AfricanAmerican,
1523 Script::Latin,
1524 Some(Gender::Feminine),
1525 NameFrequency::Common,
1526 ),
1527 NameExample::new(
1528 "Marcus",
1529 "Lewis",
1530 Ethnicity::AfricanAmerican,
1531 Script::Latin,
1532 Some(Gender::Masculine),
1533 NameFrequency::Common,
1534 ),
1535 NameExample::new(
1536 "Nicole",
1537 "Walker",
1538 Ethnicity::AfricanAmerican,
1539 Script::Latin,
1540 Some(Gender::Feminine),
1541 NameFrequency::Common,
1542 ),
1543 NameExample::new(
1544 "Darius",
1545 "Hall",
1546 Ethnicity::AfricanAmerican,
1547 Script::Latin,
1548 Some(Gender::Masculine),
1549 NameFrequency::Common,
1550 ),
1551 NameExample::new(
1552 "Monique",
1553 "Allen",
1554 Ethnicity::AfricanAmerican,
1555 Script::Latin,
1556 Some(Gender::Feminine),
1557 NameFrequency::Common,
1558 ),
1559 NameExample::new(
1560 "Terrell",
1561 "Young",
1562 Ethnicity::AfricanAmerican,
1563 Script::Latin,
1564 Some(Gender::Masculine),
1565 NameFrequency::Common,
1566 ),
1567 NameExample::new(
1568 "Danielle",
1569 "King",
1570 Ethnicity::AfricanAmerican,
1571 Script::Latin,
1572 Some(Gender::Feminine),
1573 NameFrequency::Common,
1574 ),
1575 NameExample::new(
1576 "Kendrick",
1577 "Wright",
1578 Ethnicity::AfricanAmerican,
1579 Script::Latin,
1580 Some(Gender::Masculine),
1581 NameFrequency::Common,
1582 ),
1583 NameExample::new(
1584 "Brittany",
1585 "Lopez",
1586 Ethnicity::AfricanAmerican,
1587 Script::Latin,
1588 Some(Gender::Feminine),
1589 NameFrequency::Common,
1590 ),
1591 NameExample::new(
1592 "Jermaine",
1593 "Hill",
1594 Ethnicity::AfricanAmerican,
1595 Script::Latin,
1596 Some(Gender::Masculine),
1597 NameFrequency::Common,
1598 ),
1599 NameExample::new(
1600 "Crystal",
1601 "Scott",
1602 Ethnicity::AfricanAmerican,
1603 Script::Latin,
1604 Some(Gender::Feminine),
1605 NameFrequency::Common,
1606 ),
1607 NameExample::new(
1608 "Antoine",
1609 "Green",
1610 Ethnicity::AfricanAmerican,
1611 Script::Latin,
1612 Some(Gender::Masculine),
1613 NameFrequency::Common,
1614 ),
1615 NameExample::new(
1616 "Ebony",
1617 "Adams",
1618 Ethnicity::AfricanAmerican,
1619 Script::Latin,
1620 Some(Gender::Feminine),
1621 NameFrequency::Common,
1622 ),
1623 NameExample::new(
1624 "Reginald",
1625 "Baker",
1626 Ethnicity::AfricanAmerican,
1627 Script::Latin,
1628 Some(Gender::Masculine),
1629 NameFrequency::Common,
1630 ),
1631 NameExample::new(
1632 "Jasmine",
1633 "Nelson",
1634 Ethnicity::AfricanAmerican,
1635 Script::Latin,
1636 Some(Gender::Feminine),
1637 NameFrequency::Common,
1638 ),
1639 NameExample::new(
1640 "Darnell",
1641 "Carter",
1642 Ethnicity::AfricanAmerican,
1643 Script::Latin,
1644 Some(Gender::Masculine),
1645 NameFrequency::Common,
1646 ),
1647 NameExample::new(
1648 "LaTasha",
1649 "Mitchell",
1650 Ethnicity::AfricanAmerican,
1651 Script::Latin,
1652 Some(Gender::Feminine),
1653 NameFrequency::Common,
1654 ),
1655 ]);
1656
1657 names.extend(vec![
1659 NameExample::new(
1660 "Alejandro",
1661 "Fernandez",
1662 Ethnicity::Hispanic,
1663 Script::Latin,
1664 Some(Gender::Masculine),
1665 NameFrequency::Common,
1666 ),
1667 NameExample::new(
1668 "Valentina",
1669 "Ramirez",
1670 Ethnicity::Hispanic,
1671 Script::Latin,
1672 Some(Gender::Feminine),
1673 NameFrequency::Common,
1674 ),
1675 NameExample::new(
1676 "Sebastian",
1677 "Torres",
1678 Ethnicity::Hispanic,
1679 Script::Latin,
1680 Some(Gender::Masculine),
1681 NameFrequency::Common,
1682 ),
1683 NameExample::new(
1684 "Camila",
1685 "Flores",
1686 Ethnicity::Hispanic,
1687 Script::Latin,
1688 Some(Gender::Feminine),
1689 NameFrequency::Common,
1690 ),
1691 NameExample::new(
1692 "Mateo",
1693 "Rivera",
1694 Ethnicity::Hispanic,
1695 Script::Latin,
1696 Some(Gender::Masculine),
1697 NameFrequency::Common,
1698 ),
1699 NameExample::new(
1700 "Lucia",
1701 "Gomez",
1702 Ethnicity::Hispanic,
1703 Script::Latin,
1704 Some(Gender::Feminine),
1705 NameFrequency::Common,
1706 ),
1707 NameExample::new(
1708 "Nicolas",
1709 "Diaz",
1710 Ethnicity::Hispanic,
1711 Script::Latin,
1712 Some(Gender::Masculine),
1713 NameFrequency::Common,
1714 ),
1715 NameExample::new(
1716 "Elena",
1717 "Reyes",
1718 Ethnicity::Hispanic,
1719 Script::Latin,
1720 Some(Gender::Feminine),
1721 NameFrequency::Common,
1722 ),
1723 NameExample::new(
1724 "Gabriel",
1725 "Morales",
1726 Ethnicity::Hispanic,
1727 Script::Latin,
1728 Some(Gender::Masculine),
1729 NameFrequency::Common,
1730 ),
1731 NameExample::new(
1732 "Sofia",
1733 "Ortiz",
1734 Ethnicity::Hispanic,
1735 Script::Latin,
1736 Some(Gender::Feminine),
1737 NameFrequency::Common,
1738 ),
1739 NameExample::new(
1740 "Adrian",
1741 "Gutierrez",
1742 Ethnicity::Hispanic,
1743 Script::Latin,
1744 Some(Gender::Masculine),
1745 NameFrequency::Common,
1746 ),
1747 NameExample::new(
1748 "Isabella",
1749 "Chavez",
1750 Ethnicity::Hispanic,
1751 Script::Latin,
1752 Some(Gender::Feminine),
1753 NameFrequency::Common,
1754 ),
1755 NameExample::new(
1756 "Luis",
1757 "Jimenez",
1758 Ethnicity::Hispanic,
1759 Script::Latin,
1760 Some(Gender::Masculine),
1761 NameFrequency::Common,
1762 ),
1763 NameExample::new(
1764 "Gabriela",
1765 "Moreno",
1766 Ethnicity::Hispanic,
1767 Script::Latin,
1768 Some(Gender::Feminine),
1769 NameFrequency::Common,
1770 ),
1771 NameExample::new(
1772 "Fernando",
1773 "Alvarez",
1774 Ethnicity::Hispanic,
1775 Script::Latin,
1776 Some(Gender::Masculine),
1777 NameFrequency::Common,
1778 ),
1779 NameExample::new(
1780 "Valeria",
1781 "Ruiz",
1782 Ethnicity::Hispanic,
1783 Script::Latin,
1784 Some(Gender::Feminine),
1785 NameFrequency::Common,
1786 ),
1787 NameExample::new(
1788 "Ricardo",
1789 "Vargas",
1790 Ethnicity::Hispanic,
1791 Script::Latin,
1792 Some(Gender::Masculine),
1793 NameFrequency::Common,
1794 ),
1795 NameExample::new(
1796 "Andrea",
1797 "Mendoza",
1798 Ethnicity::Hispanic,
1799 Script::Latin,
1800 Some(Gender::Feminine),
1801 NameFrequency::Common,
1802 ),
1803 NameExample::new(
1804 "Eduardo",
1805 "Castillo",
1806 Ethnicity::Hispanic,
1807 Script::Latin,
1808 Some(Gender::Masculine),
1809 NameFrequency::Common,
1810 ),
1811 NameExample::new(
1812 "Natalia",
1813 "Ramos",
1814 Ethnicity::Hispanic,
1815 Script::Latin,
1816 Some(Gender::Feminine),
1817 NameFrequency::Common,
1818 ),
1819 ]);
1820
1821 names.extend(vec![
1823 NameExample::new(
1824 "Hiroshi",
1825 "Suzuki",
1826 Ethnicity::EastAsian,
1827 Script::Latin,
1828 Some(Gender::Masculine),
1829 NameFrequency::Common,
1830 ),
1831 NameExample::new(
1832 "Yuki",
1833 "Takahashi",
1834 Ethnicity::EastAsian,
1835 Script::Latin,
1836 Some(Gender::Neutral),
1837 NameFrequency::Common,
1838 ),
1839 NameExample::new(
1840 "Kenji",
1841 "Tanaka",
1842 Ethnicity::EastAsian,
1843 Script::Latin,
1844 Some(Gender::Masculine),
1845 NameFrequency::Common,
1846 ),
1847 NameExample::new(
1848 "Sakura",
1849 "Watanabe",
1850 Ethnicity::EastAsian,
1851 Script::Latin,
1852 Some(Gender::Feminine),
1853 NameFrequency::Common,
1854 ),
1855 NameExample::new(
1856 "Jun",
1857 "Ito",
1858 Ethnicity::EastAsian,
1859 Script::Latin,
1860 Some(Gender::Neutral),
1861 NameFrequency::Common,
1862 ),
1863 NameExample::new(
1864 "Mei",
1865 "Nakamura",
1866 Ethnicity::EastAsian,
1867 Script::Latin,
1868 Some(Gender::Feminine),
1869 NameFrequency::Common,
1870 ),
1871 NameExample::new(
1872 "Xiaoming",
1873 "Li",
1874 Ethnicity::EastAsian,
1875 Script::Latin,
1876 Some(Gender::Masculine),
1877 NameFrequency::Common,
1878 ),
1879 NameExample::new(
1880 "Xiaoli",
1881 "Wang",
1882 Ethnicity::EastAsian,
1883 Script::Latin,
1884 Some(Gender::Feminine),
1885 NameFrequency::Common,
1886 ),
1887 NameExample::new(
1888 "Jian",
1889 "Liu",
1890 Ethnicity::EastAsian,
1891 Script::Latin,
1892 Some(Gender::Masculine),
1893 NameFrequency::Common,
1894 ),
1895 NameExample::new(
1896 "Yan",
1897 "Zhang",
1898 Ethnicity::EastAsian,
1899 Script::Latin,
1900 Some(Gender::Feminine),
1901 NameFrequency::Common,
1902 ),
1903 NameExample::new(
1904 "Hye-jin",
1905 "Park",
1906 Ethnicity::EastAsian,
1907 Script::Latin,
1908 Some(Gender::Feminine),
1909 NameFrequency::Common,
1910 ),
1911 NameExample::new(
1912 "Seung-ho",
1913 "Kim",
1914 Ethnicity::EastAsian,
1915 Script::Latin,
1916 Some(Gender::Masculine),
1917 NameFrequency::Common,
1918 ),
1919 NameExample::new(
1920 "Ji-woo",
1921 "Lee",
1922 Ethnicity::EastAsian,
1923 Script::Latin,
1924 Some(Gender::Neutral),
1925 NameFrequency::Common,
1926 ),
1927 NameExample::new(
1928 "Soo-jin",
1929 "Choi",
1930 Ethnicity::EastAsian,
1931 Script::Latin,
1932 Some(Gender::Feminine),
1933 NameFrequency::Common,
1934 ),
1935 NameExample::new(
1936 "Min-ho",
1937 "Jung",
1938 Ethnicity::EastAsian,
1939 Script::Latin,
1940 Some(Gender::Masculine),
1941 NameFrequency::Common,
1942 ),
1943 NameExample::new(
1944 "明",
1945 "王",
1946 Ethnicity::EastAsian,
1947 Script::Chinese,
1948 Some(Gender::Masculine),
1949 NameFrequency::Common,
1950 ),
1951 NameExample::new(
1952 "美",
1953 "李",
1954 Ethnicity::EastAsian,
1955 Script::Chinese,
1956 Some(Gender::Feminine),
1957 NameFrequency::Common,
1958 ),
1959 NameExample::new(
1960 "健",
1961 "张",
1962 Ethnicity::EastAsian,
1963 Script::Chinese,
1964 Some(Gender::Masculine),
1965 NameFrequency::Common,
1966 ),
1967 NameExample::new(
1968 "花子",
1969 "佐藤",
1970 Ethnicity::EastAsian,
1971 Script::Japanese,
1972 Some(Gender::Feminine),
1973 NameFrequency::Common,
1974 ),
1975 NameExample::new(
1976 "太郎",
1977 "鈴木",
1978 Ethnicity::EastAsian,
1979 Script::Japanese,
1980 Some(Gender::Masculine),
1981 NameFrequency::Common,
1982 ),
1983 ]);
1984
1985 names.extend(vec![
1987 NameExample::new(
1988 "Amit",
1989 "Patel",
1990 Ethnicity::SouthAsian,
1991 Script::Latin,
1992 Some(Gender::Masculine),
1993 NameFrequency::Common,
1994 ),
1995 NameExample::new(
1996 "Kavita",
1997 "Sharma",
1998 Ethnicity::SouthAsian,
1999 Script::Latin,
2000 Some(Gender::Feminine),
2001 NameFrequency::Common,
2002 ),
2003 NameExample::new(
2004 "Rahul",
2005 "Singh",
2006 Ethnicity::SouthAsian,
2007 Script::Latin,
2008 Some(Gender::Masculine),
2009 NameFrequency::Common,
2010 ),
2011 NameExample::new(
2012 "Deepika",
2013 "Kumar",
2014 Ethnicity::SouthAsian,
2015 Script::Latin,
2016 Some(Gender::Feminine),
2017 NameFrequency::Common,
2018 ),
2019 NameExample::new(
2020 "Vikram",
2021 "Gupta",
2022 Ethnicity::SouthAsian,
2023 Script::Latin,
2024 Some(Gender::Masculine),
2025 NameFrequency::Common,
2026 ),
2027 NameExample::new(
2028 "Anjali",
2029 "Mehta",
2030 Ethnicity::SouthAsian,
2031 Script::Latin,
2032 Some(Gender::Feminine),
2033 NameFrequency::Common,
2034 ),
2035 NameExample::new(
2036 "Rohan",
2037 "Desai",
2038 Ethnicity::SouthAsian,
2039 Script::Latin,
2040 Some(Gender::Masculine),
2041 NameFrequency::Common,
2042 ),
2043 NameExample::new(
2044 "Meera",
2045 "Joshi",
2046 Ethnicity::SouthAsian,
2047 Script::Latin,
2048 Some(Gender::Feminine),
2049 NameFrequency::Common,
2050 ),
2051 NameExample::new(
2052 "Siddharth",
2053 "Reddy",
2054 Ethnicity::SouthAsian,
2055 Script::Latin,
2056 Some(Gender::Masculine),
2057 NameFrequency::Common,
2058 ),
2059 NameExample::new(
2060 "Kiran",
2061 "Nair",
2062 Ethnicity::SouthAsian,
2063 Script::Latin,
2064 Some(Gender::Neutral),
2065 NameFrequency::Common,
2066 ),
2067 NameExample::new(
2068 "Arjun",
2069 "Iyer",
2070 Ethnicity::SouthAsian,
2071 Script::Latin,
2072 Some(Gender::Masculine),
2073 NameFrequency::Common,
2074 ),
2075 NameExample::new(
2076 "Divya",
2077 "Menon",
2078 Ethnicity::SouthAsian,
2079 Script::Latin,
2080 Some(Gender::Feminine),
2081 NameFrequency::Common,
2082 ),
2083 NameExample::new(
2084 "Nikhil",
2085 "Rao",
2086 Ethnicity::SouthAsian,
2087 Script::Latin,
2088 Some(Gender::Masculine),
2089 NameFrequency::Common,
2090 ),
2091 NameExample::new(
2092 "Shreya",
2093 "Malhotra",
2094 Ethnicity::SouthAsian,
2095 Script::Latin,
2096 Some(Gender::Feminine),
2097 NameFrequency::Common,
2098 ),
2099 NameExample::new(
2100 "Aditya",
2101 "Kapoor",
2102 Ethnicity::SouthAsian,
2103 Script::Latin,
2104 Some(Gender::Masculine),
2105 NameFrequency::Common,
2106 ),
2107 NameExample::new(
2108 "Pooja",
2109 "Agarwal",
2110 Ethnicity::SouthAsian,
2111 Script::Latin,
2112 Some(Gender::Feminine),
2113 NameFrequency::Common,
2114 ),
2115 NameExample::new(
2116 "Ravi",
2117 "Bhatt",
2118 Ethnicity::SouthAsian,
2119 Script::Latin,
2120 Some(Gender::Masculine),
2121 NameFrequency::Common,
2122 ),
2123 NameExample::new(
2124 "Neha",
2125 "Chopra",
2126 Ethnicity::SouthAsian,
2127 Script::Latin,
2128 Some(Gender::Feminine),
2129 NameFrequency::Common,
2130 ),
2131 NameExample::new(
2132 "Karan",
2133 "Verma",
2134 Ethnicity::SouthAsian,
2135 Script::Latin,
2136 Some(Gender::Masculine),
2137 NameFrequency::Common,
2138 ),
2139 NameExample::new(
2140 "Sanjana",
2141 "Saxena",
2142 Ethnicity::SouthAsian,
2143 Script::Latin,
2144 Some(Gender::Feminine),
2145 NameFrequency::Common,
2146 ),
2147 ]);
2148
2149 names.extend(vec![
2151 NameExample::new(
2152 "Omar",
2153 "Hassan",
2154 Ethnicity::MiddleEastern,
2155 Script::Latin,
2156 Some(Gender::Masculine),
2157 NameFrequency::Common,
2158 ),
2159 NameExample::new(
2160 "Zara",
2161 "Ali",
2162 Ethnicity::MiddleEastern,
2163 Script::Latin,
2164 Some(Gender::Feminine),
2165 NameFrequency::Common,
2166 ),
2167 NameExample::new(
2168 "Tariq",
2169 "Ibrahim",
2170 Ethnicity::MiddleEastern,
2171 Script::Latin,
2172 Some(Gender::Masculine),
2173 NameFrequency::Common,
2174 ),
2175 NameExample::new(
2176 "Amina",
2177 "Omar",
2178 Ethnicity::MiddleEastern,
2179 Script::Latin,
2180 Some(Gender::Feminine),
2181 NameFrequency::Common,
2182 ),
2183 NameExample::new(
2184 "Khalil",
2185 "Mustafa",
2186 Ethnicity::MiddleEastern,
2187 Script::Latin,
2188 Some(Gender::Masculine),
2189 NameFrequency::Common,
2190 ),
2191 NameExample::new(
2192 "Noor",
2193 "Khalil",
2194 Ethnicity::MiddleEastern,
2195 Script::Latin,
2196 Some(Gender::Feminine),
2197 NameFrequency::Common,
2198 ),
2199 NameExample::new(
2200 "Rashid",
2201 "Mahmoud",
2202 Ethnicity::MiddleEastern,
2203 Script::Latin,
2204 Some(Gender::Masculine),
2205 NameFrequency::Common,
2206 ),
2207 NameExample::new(
2208 "Samira",
2209 "Haddad",
2210 Ethnicity::MiddleEastern,
2211 Script::Latin,
2212 Some(Gender::Feminine),
2213 NameFrequency::Common,
2214 ),
2215 NameExample::new(
2216 "Bashir",
2217 "Nasser",
2218 Ethnicity::MiddleEastern,
2219 Script::Latin,
2220 Some(Gender::Masculine),
2221 NameFrequency::Common,
2222 ),
2223 NameExample::new(
2224 "Leila",
2225 "Fadel",
2226 Ethnicity::MiddleEastern,
2227 Script::Latin,
2228 Some(Gender::Feminine),
2229 NameFrequency::Common,
2230 ),
2231 NameExample::new(
2232 "Karim",
2233 "Said",
2234 Ethnicity::MiddleEastern,
2235 Script::Latin,
2236 Some(Gender::Masculine),
2237 NameFrequency::Common,
2238 ),
2239 NameExample::new(
2240 "Yasmin",
2241 "Malik",
2242 Ethnicity::MiddleEastern,
2243 Script::Latin,
2244 Some(Gender::Feminine),
2245 NameFrequency::Common,
2246 ),
2247 NameExample::new(
2248 "Jamal",
2249 "Rahman",
2250 Ethnicity::MiddleEastern,
2251 Script::Latin,
2252 Some(Gender::Masculine),
2253 NameFrequency::Common,
2254 ),
2255 NameExample::new(
2256 "Soraya",
2257 "Abbas",
2258 Ethnicity::MiddleEastern,
2259 Script::Latin,
2260 Some(Gender::Feminine),
2261 NameFrequency::Common,
2262 ),
2263 NameExample::new(
2264 "Nabil",
2265 "Hakim",
2266 Ethnicity::MiddleEastern,
2267 Script::Latin,
2268 Some(Gender::Masculine),
2269 NameFrequency::Common,
2270 ),
2271 NameExample::new(
2272 "Rania",
2273 "Farid",
2274 Ethnicity::MiddleEastern,
2275 Script::Latin,
2276 Some(Gender::Feminine),
2277 NameFrequency::Common,
2278 ),
2279 NameExample::new(
2280 "Tariq",
2281 "Zaki",
2282 Ethnicity::MiddleEastern,
2283 Script::Latin,
2284 Some(Gender::Masculine),
2285 NameFrequency::Common,
2286 ),
2287 NameExample::new(
2288 "Dina",
2289 "Salem",
2290 Ethnicity::MiddleEastern,
2291 Script::Latin,
2292 Some(Gender::Feminine),
2293 NameFrequency::Common,
2294 ),
2295 NameExample::new(
2296 "Malik",
2297 "Nasir",
2298 Ethnicity::MiddleEastern,
2299 Script::Latin,
2300 Some(Gender::Masculine),
2301 NameFrequency::Common,
2302 ),
2303 NameExample::new(
2304 "Hala",
2305 "Qureshi",
2306 Ethnicity::MiddleEastern,
2307 Script::Latin,
2308 Some(Gender::Feminine),
2309 NameFrequency::Common,
2310 ),
2311 ]);
2312
2313 names.extend(vec![
2315 NameExample::new(
2316 "Kofi",
2317 "Mensah",
2318 Ethnicity::African,
2319 Script::Latin,
2320 Some(Gender::Masculine),
2321 NameFrequency::Common,
2322 ),
2323 NameExample::new(
2324 "Amina",
2325 "Diallo",
2326 Ethnicity::African,
2327 Script::Latin,
2328 Some(Gender::Feminine),
2329 NameFrequency::Common,
2330 ),
2331 NameExample::new(
2332 "Kwame",
2333 "Asante",
2334 Ethnicity::African,
2335 Script::Latin,
2336 Some(Gender::Masculine),
2337 NameFrequency::Common,
2338 ),
2339 NameExample::new(
2340 "Fatou",
2341 "Ndiaye",
2342 Ethnicity::African,
2343 Script::Latin,
2344 Some(Gender::Feminine),
2345 NameFrequency::Common,
2346 ),
2347 NameExample::new(
2348 "Bakary",
2349 "Traore",
2350 Ethnicity::African,
2351 Script::Latin,
2352 Some(Gender::Masculine),
2353 NameFrequency::Common,
2354 ),
2355 NameExample::new(
2356 "Aissatou",
2357 "Ba",
2358 Ethnicity::African,
2359 Script::Latin,
2360 Some(Gender::Feminine),
2361 NameFrequency::Common,
2362 ),
2363 NameExample::new(
2364 "Ibrahim",
2365 "Sow",
2366 Ethnicity::African,
2367 Script::Latin,
2368 Some(Gender::Masculine),
2369 NameFrequency::Common,
2370 ),
2371 NameExample::new(
2372 "Mariama",
2373 "Diallo",
2374 Ethnicity::African,
2375 Script::Latin,
2376 Some(Gender::Feminine),
2377 NameFrequency::Common,
2378 ),
2379 NameExample::new(
2380 "Sekou",
2381 "Keita",
2382 Ethnicity::African,
2383 Script::Latin,
2384 Some(Gender::Masculine),
2385 NameFrequency::Common,
2386 ),
2387 NameExample::new(
2388 "Awa",
2389 "Cisse",
2390 Ethnicity::African,
2391 Script::Latin,
2392 Some(Gender::Feminine),
2393 NameFrequency::Common,
2394 ),
2395 NameExample::new(
2396 "Moussa",
2397 "Toure",
2398 Ethnicity::African,
2399 Script::Latin,
2400 Some(Gender::Masculine),
2401 NameFrequency::Common,
2402 ),
2403 NameExample::new(
2404 "Kadiatou",
2405 "Sangare",
2406 Ethnicity::African,
2407 Script::Latin,
2408 Some(Gender::Feminine),
2409 NameFrequency::Common,
2410 ),
2411 NameExample::new(
2412 "Youssouf",
2413 "Kone",
2414 Ethnicity::African,
2415 Script::Latin,
2416 Some(Gender::Masculine),
2417 NameFrequency::Common,
2418 ),
2419 NameExample::new(
2420 "Aminata",
2421 "Diop",
2422 Ethnicity::African,
2423 Script::Latin,
2424 Some(Gender::Feminine),
2425 NameFrequency::Common,
2426 ),
2427 NameExample::new(
2428 "Boubacar",
2429 "Sall",
2430 Ethnicity::African,
2431 Script::Latin,
2432 Some(Gender::Masculine),
2433 NameFrequency::Common,
2434 ),
2435 NameExample::new(
2436 "Hawa",
2437 "Ba",
2438 Ethnicity::African,
2439 Script::Latin,
2440 Some(Gender::Feminine),
2441 NameFrequency::Common,
2442 ),
2443 NameExample::new(
2444 "Mamadou",
2445 "Diallo",
2446 Ethnicity::African,
2447 Script::Latin,
2448 Some(Gender::Masculine),
2449 NameFrequency::Common,
2450 ),
2451 NameExample::new(
2452 "Ramatoulaye",
2453 "Ndiaye",
2454 Ethnicity::African,
2455 Script::Latin,
2456 Some(Gender::Feminine),
2457 NameFrequency::Common,
2458 ),
2459 NameExample::new(
2460 "Amadou",
2461 "Sow",
2462 Ethnicity::African,
2463 Script::Latin,
2464 Some(Gender::Masculine),
2465 NameFrequency::Common,
2466 ),
2467 NameExample::new(
2468 "Aissata",
2469 "Traore",
2470 Ethnicity::African,
2471 Script::Latin,
2472 Some(Gender::Feminine),
2473 NameFrequency::Common,
2474 ),
2475 ]);
2476 names
2477}
2478
2479fn validate_demographic_distribution(
2484 observed: &HashMap<String, f64>,
2485) -> crate::eval::bias_config::DistributionValidation {
2486 let mut reference = HashMap::new();
2489 reference.insert("European".to_string(), 0.60);
2490 reference.insert("Hispanic".to_string(), 0.19);
2491 reference.insert("AfricanAmerican".to_string(), 0.13);
2492 reference.insert("EastAsian".to_string(), 0.06);
2493 reference.insert("SouthAsian".to_string(), 0.02);
2494 reference.insert("MiddleEastern".to_string(), 0.01);
2495 reference.insert("African".to_string(), 0.01);
2496 reference.insert("Indigenous".to_string(), 0.01);
2497
2498 let total: f64 = observed.values().sum();
2500 let normalized_observed: HashMap<String, f64> = if total > 0.0 {
2501 observed
2502 .iter()
2503 .map(|(k, v)| (k.clone(), v / total))
2504 .collect()
2505 } else {
2506 observed.clone()
2507 };
2508
2509 crate::eval::bias_config::DistributionValidation::validate(
2510 &normalized_observed,
2511 &reference,
2512 0.10, )
2514}
2515
2516fn create_realistic_location_sentence(location: &str) -> String {
2518 use std::collections::hash_map::DefaultHasher;
2519 use std::hash::{Hash, Hasher};
2520 let mut hasher = DefaultHasher::new();
2521 location.hash(&mut hasher);
2522 let hash = hasher.finish();
2523
2524 let templates = [
2525 format!("The summit was held in {} last month.", location),
2526 format!("{} has become a major tech hub in recent years.", location),
2527 format!("Tourists flock to {} during the summer months.", location),
2528 format!(
2529 "The conference in {} attracted thousands of attendees.",
2530 location
2531 ),
2532 format!("{} is known for its vibrant cultural scene.", location),
2533 format!(
2534 "Business leaders met in {} to discuss trade policies.",
2535 location
2536 ),
2537 format!(
2538 "{} hosted the international competition this year.",
2539 location
2540 ),
2541 format!("The economic growth in {} has been remarkable.", location),
2542 format!(
2543 "{} is home to several world-renowned universities.",
2544 location
2545 ),
2546 format!(
2547 "The climate summit in {} addressed global challenges.",
2548 location
2549 ),
2550 ];
2551
2552 templates[hash as usize % templates.len()].clone()
2553}
2554
2555pub fn create_diverse_location_dataset() -> Vec<LocationExample> {
2557 vec![
2558 LocationExample::new(
2560 "New York",
2561 Region::NorthAmerica,
2562 Script::Latin,
2563 LocationType::City,
2564 ),
2565 LocationExample::new(
2566 "Los Angeles",
2567 Region::NorthAmerica,
2568 Script::Latin,
2569 LocationType::City,
2570 ),
2571 LocationExample::new(
2572 "Toronto",
2573 Region::NorthAmerica,
2574 Script::Latin,
2575 LocationType::City,
2576 ),
2577 LocationExample::new(
2578 "Mexico City",
2579 Region::NorthAmerica,
2580 Script::Latin,
2581 LocationType::City,
2582 ),
2583 LocationExample::new(
2585 "London",
2586 Region::WesternEurope,
2587 Script::Latin,
2588 LocationType::City,
2589 ),
2590 LocationExample::new(
2591 "Paris",
2592 Region::WesternEurope,
2593 Script::Latin,
2594 LocationType::City,
2595 ),
2596 LocationExample::new(
2597 "Berlin",
2598 Region::WesternEurope,
2599 Script::Latin,
2600 LocationType::City,
2601 ),
2602 LocationExample::new(
2603 "Amsterdam",
2604 Region::WesternEurope,
2605 Script::Latin,
2606 LocationType::City,
2607 ),
2608 LocationExample::new(
2610 "Moscow",
2611 Region::EasternEurope,
2612 Script::Latin,
2613 LocationType::City,
2614 ),
2615 LocationExample::new(
2616 "Москва",
2617 Region::EasternEurope,
2618 Script::Cyrillic,
2619 LocationType::City,
2620 ),
2621 LocationExample::new(
2622 "Warsaw",
2623 Region::EasternEurope,
2624 Script::Latin,
2625 LocationType::City,
2626 ),
2627 LocationExample::new(
2628 "Kyiv",
2629 Region::EasternEurope,
2630 Script::Latin,
2631 LocationType::City,
2632 ),
2633 LocationExample::new("Tokyo", Region::EastAsia, Script::Latin, LocationType::City),
2635 LocationExample::new(
2636 "東京",
2637 Region::EastAsia,
2638 Script::Japanese,
2639 LocationType::City,
2640 ),
2641 LocationExample::new(
2642 "Beijing",
2643 Region::EastAsia,
2644 Script::Latin,
2645 LocationType::City,
2646 ),
2647 LocationExample::new(
2648 "北京",
2649 Region::EastAsia,
2650 Script::Chinese,
2651 LocationType::City,
2652 ),
2653 LocationExample::new("Seoul", Region::EastAsia, Script::Latin, LocationType::City),
2654 LocationExample::new("서울", Region::EastAsia, Script::Korean, LocationType::City),
2655 LocationExample::new(
2657 "Mumbai",
2658 Region::SouthAsia,
2659 Script::Latin,
2660 LocationType::City,
2661 ),
2662 LocationExample::new(
2663 "Delhi",
2664 Region::SouthAsia,
2665 Script::Latin,
2666 LocationType::City,
2667 ),
2668 LocationExample::new(
2669 "Dhaka",
2670 Region::SouthAsia,
2671 Script::Latin,
2672 LocationType::City,
2673 ),
2674 LocationExample::new(
2675 "Karachi",
2676 Region::SouthAsia,
2677 Script::Latin,
2678 LocationType::City,
2679 ),
2680 LocationExample::new(
2682 "Bangkok",
2683 Region::SoutheastAsia,
2684 Script::Latin,
2685 LocationType::City,
2686 ),
2687 LocationExample::new(
2688 "Singapore",
2689 Region::SoutheastAsia,
2690 Script::Latin,
2691 LocationType::City,
2692 ),
2693 LocationExample::new(
2694 "Jakarta",
2695 Region::SoutheastAsia,
2696 Script::Latin,
2697 LocationType::City,
2698 ),
2699 LocationExample::new(
2700 "Ho Chi Minh City",
2701 Region::SoutheastAsia,
2702 Script::Latin,
2703 LocationType::City,
2704 ),
2705 LocationExample::new(
2707 "Dubai",
2708 Region::MiddleEast,
2709 Script::Latin,
2710 LocationType::City,
2711 ),
2712 LocationExample::new(
2713 "دبي",
2714 Region::MiddleEast,
2715 Script::Arabic,
2716 LocationType::City,
2717 ),
2718 LocationExample::new(
2719 "Tehran",
2720 Region::MiddleEast,
2721 Script::Latin,
2722 LocationType::City,
2723 ),
2724 LocationExample::new(
2725 "Riyadh",
2726 Region::MiddleEast,
2727 Script::Latin,
2728 LocationType::City,
2729 ),
2730 LocationExample::new("Lagos", Region::Africa, Script::Latin, LocationType::City),
2732 LocationExample::new("Nairobi", Region::Africa, Script::Latin, LocationType::City),
2733 LocationExample::new("Cairo", Region::Africa, Script::Latin, LocationType::City),
2734 LocationExample::new(
2735 "Johannesburg",
2736 Region::Africa,
2737 Script::Latin,
2738 LocationType::City,
2739 ),
2740 LocationExample::new(
2741 "Addis Ababa",
2742 Region::Africa,
2743 Script::Latin,
2744 LocationType::City,
2745 ),
2746 LocationExample::new(
2748 "São Paulo",
2749 Region::LatinAmerica,
2750 Script::Latin,
2751 LocationType::City,
2752 ),
2753 LocationExample::new(
2754 "Buenos Aires",
2755 Region::LatinAmerica,
2756 Script::Latin,
2757 LocationType::City,
2758 ),
2759 LocationExample::new(
2760 "Bogotá",
2761 Region::LatinAmerica,
2762 Script::Latin,
2763 LocationType::City,
2764 ),
2765 LocationExample::new(
2766 "Lima",
2767 Region::LatinAmerica,
2768 Script::Latin,
2769 LocationType::City,
2770 ),
2771 LocationExample::new("Sydney", Region::Oceania, Script::Latin, LocationType::City),
2773 LocationExample::new(
2774 "Melbourne",
2775 Region::Oceania,
2776 Script::Latin,
2777 LocationType::City,
2778 ),
2779 LocationExample::new(
2780 "Auckland",
2781 Region::Oceania,
2782 Script::Latin,
2783 LocationType::City,
2784 ),
2785 ]
2786}
2787
2788#[cfg(test)]
2793mod tests {
2794 use super::*;
2795
2796 #[test]
2797 fn test_create_diverse_names() {
2798 let names = create_diverse_name_dataset();
2799
2800 let ethnicities: std::collections::HashSet<_> =
2802 names.iter().map(|n| format!("{:?}", n.ethnicity)).collect();
2803
2804 assert!(
2805 ethnicities.contains("European"),
2806 "Should have European names"
2807 );
2808 assert!(
2809 ethnicities.contains("AfricanAmerican"),
2810 "Should have African-American names"
2811 );
2812 assert!(
2813 ethnicities.contains("Hispanic"),
2814 "Should have Hispanic names"
2815 );
2816 assert!(
2817 ethnicities.contains("EastAsian"),
2818 "Should have East Asian names"
2819 );
2820 assert!(
2821 ethnicities.contains("SouthAsian"),
2822 "Should have South Asian names"
2823 );
2824 assert!(
2825 ethnicities.contains("MiddleEastern"),
2826 "Should have Middle Eastern names"
2827 );
2828 assert!(ethnicities.contains("African"), "Should have African names");
2829 }
2830
2831 #[test]
2832 fn test_multiple_scripts() {
2833 let names = create_diverse_name_dataset();
2834
2835 let scripts: std::collections::HashSet<_> =
2836 names.iter().map(|n| format!("{:?}", n.script)).collect();
2837
2838 assert!(scripts.contains("Latin"), "Should have Latin script");
2839 assert!(scripts.contains("Chinese"), "Should have Chinese script");
2840 assert!(scripts.contains("Japanese"), "Should have Japanese script");
2841 assert!(scripts.contains("Arabic"), "Should have Arabic script");
2842 assert!(scripts.contains("Cyrillic"), "Should have Cyrillic script");
2843 }
2844
2845 #[test]
2846 fn test_gender_balance() {
2847 let names = create_diverse_name_dataset();
2848
2849 let masculine = names
2850 .iter()
2851 .filter(|n| n.gender == Some(Gender::Masculine))
2852 .count();
2853 let feminine = names
2854 .iter()
2855 .filter(|n| n.gender == Some(Gender::Feminine))
2856 .count();
2857
2858 let ratio = masculine as f64 / feminine.max(1) as f64;
2860 assert!(
2861 (0.7..=1.3).contains(&ratio),
2862 "Gender ratio should be roughly balanced, got {:.2}",
2863 ratio
2864 );
2865 }
2866
2867 #[test]
2868 fn test_diverse_locations() {
2869 let locations = create_diverse_location_dataset();
2870
2871 let regions: std::collections::HashSet<_> = locations
2872 .iter()
2873 .map(|l| format!("{:?}", l.region))
2874 .collect();
2875
2876 assert!(regions.len() >= 8, "Should cover at least 8 regions");
2877 assert!(regions.contains("Africa"), "Should have African locations");
2878 assert!(
2879 regions.contains("LatinAmerica"),
2880 "Should have Latin American locations"
2881 );
2882 assert!(
2883 regions.contains("MiddleEast"),
2884 "Should have Middle Eastern locations"
2885 );
2886 }
2887
2888 #[test]
2889 fn test_parity_gap_computation() {
2890 let mut rates = HashMap::new();
2891 rates.insert("A".to_string(), 0.9);
2892 rates.insert("B".to_string(), 0.7);
2893 rates.insert("C".to_string(), 0.8);
2894
2895 let gap = compute_max_gap(&rates);
2896 assert!((gap - 0.2).abs() < 0.001, "Gap should be 0.2, got {}", gap);
2897 }
2898}