1use crate::context::microsystem::Microsystem;
21use crate::types::MicrosystemId;
22use std::cell::{Cell, RefCell};
23use std::collections::HashMap;
24
25pub const INTERACTION_FREQUENCY_THRESHOLD: f64 = 0.3;
29
30pub const INTERACTION_COMPLEXITY_THRESHOLD: f64 = 0.3;
34
35pub const INTERACTION_RECIPROCITY_THRESHOLD: f64 = 0.6;
39
40#[derive(Debug, Clone, PartialEq)]
42pub enum ProximalProcessGateError {
43 FrequencyBelowThreshold {
45 actual: f64,
47 threshold: f64,
49 },
50 ComplexityBelowThreshold {
52 actual: f64,
54 threshold: f64,
56 },
57 BothBelowThreshold {
59 actual_frequency: f64,
61 frequency_threshold: f64,
63 actual_complexity: f64,
65 complexity_threshold: f64,
67 },
68 ReciprocityBelowThreshold {
70 actual: f64,
72 threshold: f64,
74 },
75}
76
77impl std::fmt::Display for ProximalProcessGateError {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
80 ProximalProcessGateError::FrequencyBelowThreshold { actual, threshold } => {
81 write!(
82 f,
83 "Interaction frequency {} is below threshold {}",
84 actual, threshold
85 )
86 }
87 ProximalProcessGateError::ComplexityBelowThreshold { actual, threshold } => {
88 write!(
89 f,
90 "Interaction complexity {} is below threshold {}",
91 actual, threshold
92 )
93 }
94 ProximalProcessGateError::BothBelowThreshold {
95 actual_frequency,
96 frequency_threshold,
97 actual_complexity,
98 complexity_threshold,
99 } => {
100 write!(
101 f,
102 "Interaction frequency {} is below threshold {} and complexity {} is below threshold {}",
103 actual_frequency, frequency_threshold, actual_complexity, complexity_threshold
104 )
105 }
106 ProximalProcessGateError::ReciprocityBelowThreshold { actual, threshold } => {
107 write!(
108 f,
109 "Interaction reciprocity {} is below threshold {}",
110 actual, threshold
111 )
112 }
113 }
114 }
115}
116
117impl std::error::Error for ProximalProcessGateError {}
118
119pub fn passes_proximal_process_gate(
132 frequency: f64,
133 complexity: f64,
134 frequency_threshold: f64,
135 complexity_threshold: f64,
136) -> Result<(), ProximalProcessGateError> {
137 let freq_ok = frequency >= frequency_threshold;
138 let complex_ok = complexity >= complexity_threshold;
139
140 if !freq_ok && !complex_ok {
141 Err(ProximalProcessGateError::BothBelowThreshold {
142 actual_frequency: frequency,
143 frequency_threshold,
144 actual_complexity: complexity,
145 complexity_threshold,
146 })
147 } else if !freq_ok {
148 Err(ProximalProcessGateError::FrequencyBelowThreshold {
149 actual: frequency,
150 threshold: frequency_threshold,
151 })
152 } else if !complex_ok {
153 Err(ProximalProcessGateError::ComplexityBelowThreshold {
154 actual: complexity,
155 threshold: complexity_threshold,
156 })
157 } else {
158 Ok(())
159 }
160}
161
162pub fn passes_proximal_process_gate_with_reciprocity(
177 frequency: f64,
178 complexity: f64,
179 reciprocity: f64,
180 frequency_threshold: f64,
181 complexity_threshold: f64,
182 reciprocity_threshold: f64,
183) -> Result<(), ProximalProcessGateError> {
184 passes_proximal_process_gate(frequency, complexity, frequency_threshold, complexity_threshold)?;
185
186 if reciprocity < reciprocity_threshold {
187 return Err(ProximalProcessGateError::ReciprocityBelowThreshold {
188 actual: reciprocity,
189 threshold: reciprocity_threshold,
190 });
191 }
192
193 Ok(())
194}
195
196#[derive(Debug, Clone, PartialEq)]
198pub struct MesosystemLinkage {
199 pub from: MicrosystemId,
201
202 pub to: MicrosystemId,
204
205 pub spillover: f64,
207
208 pub role_conflict: f64,
210}
211
212#[derive(Debug, Clone, PartialEq)]
214pub struct MesosystemState {
215 pub work_family_conflict: f64,
217 pub family_social_support: f64,
219 pub work_social_conflict: f64,
221 pub value_alignment_consistency: f64,
223 pub autonomy_norm_consistency: f64,
225 pub mesosystem_consistency: f64,
227 pub shared_membership_strength: f64,
229 pub microsystem_influence_weight: f64,
231}
232
233impl Default for MesosystemState {
234 fn default() -> Self {
235 MesosystemState {
236 work_family_conflict: 0.0,
237 family_social_support: 0.0,
238 work_social_conflict: 0.0,
239 value_alignment_consistency: 1.0,
240 autonomy_norm_consistency: 1.0,
241 mesosystem_consistency: 1.0,
242 shared_membership_strength: 0.0,
243 microsystem_influence_weight: 0.0,
244 }
245 }
246}
247
248impl MesosystemState {
249 #[must_use]
251 pub fn compute(microsystems: &HashMap<MicrosystemId, Microsystem>) -> Self {
252 let mut work_ids = Vec::new();
253 let mut family_ids = Vec::new();
254
255 let mut work_role_clarity = Vec::new();
256 let mut family_role_clarity = Vec::new();
257 let mut social_predictability = Vec::new();
258
259 let mut work_predictability = Vec::new();
260 let mut family_predictability = Vec::new();
261
262 let mut family_support = Vec::new();
263 let mut work_low_predictability = false;
264 let mut social_high_warmth = false;
265 let mut total_frequency = 0.0;
266 let mut total_complexity = 0.0;
267 let mut count = 0.0;
268
269 for (id, micro) in microsystems {
270 total_frequency += micro.interaction_frequency();
271 total_complexity += micro.interaction_complexity();
272 count += 1.0;
273 if let Some(work) = micro.work() {
274 work_ids.push(id.clone());
275 work_role_clarity.push(work.role_clarity);
276 work_predictability.push(work.predictability);
277 if work.predictability < 0.4 {
278 work_low_predictability = true;
279 }
280 }
281
282 if let Some(family) = micro.family() {
283 family_ids.push(id.clone());
284 family_role_clarity.push(family.role_clarity);
285 family_predictability.push(family.predictability);
286 family_support.push((family.warmth - family.hostility).max(0.0));
287 }
288
289 if let Some(social) = micro.social() {
290 social_predictability.push(social.predictability);
291 if social.warmth > 0.7 {
292 social_high_warmth = true;
293 }
294 }
295 }
296
297 let work_family_conflict = if work_ids.is_empty() || family_ids.is_empty() {
298 0.0
299 } else {
300 let cache = MesosystemCache::new();
301 let mut total = 0.0;
302 let count = work_ids.len() * family_ids.len();
303
304 for work_id in &work_ids {
305 for family_id in &family_ids {
306 total += cache.get_role_conflict(work_id, family_id, microsystems);
307 }
308 }
309
310 total / count as f64
311 };
312
313 let family_social_support = average(&family_support).unwrap_or(0.0);
314 let work_social_conflict = if work_low_predictability && social_high_warmth {
315 0.3
316 } else {
317 0.0
318 };
319
320 let value_alignment_consistency = consistency_from_values(&[
321 average(&work_role_clarity),
322 average(&family_role_clarity),
323 average(&social_predictability),
324 ]);
325
326 let autonomy_norm_consistency = consistency_from_values(&[
327 average(&work_predictability),
328 average(&family_predictability),
329 average(&social_predictability),
330 ]);
331
332 let mesosystem_consistency = value_alignment_consistency;
333 let shared_membership_strength =
334 MesosystemCache::compute_shared_membership_strength(microsystems);
335 let (avg_frequency, avg_complexity) = if count > 0.0 {
336 (total_frequency / count, total_complexity / count)
337 } else {
338 (0.0, 0.0)
339 };
340 let microsystem_influence_weight = match passes_proximal_process_gate(
341 avg_frequency,
342 avg_complexity,
343 INTERACTION_FREQUENCY_THRESHOLD,
344 INTERACTION_COMPLEXITY_THRESHOLD,
345 ) {
346 Ok(()) => 1.0,
347 Err(_) => 0.0,
348 };
349
350 MesosystemState {
351 work_family_conflict,
352 family_social_support,
353 work_social_conflict,
354 value_alignment_consistency,
355 autonomy_norm_consistency,
356 mesosystem_consistency,
357 shared_membership_strength,
358 microsystem_influence_weight,
359 }
360 }
361}
362
363fn average(values: &[f64]) -> Option<f64> {
364 if values.is_empty() {
365 None
366 } else {
367 Some(values.iter().sum::<f64>() / values.len() as f64)
368 }
369}
370
371fn consistency_from_values(values: &[Option<f64>]) -> f64 {
372 let filtered: Vec<f64> = values.iter().copied().flatten().collect();
373 if filtered.len() < 2 {
374 return 1.0;
375 }
376
377 let mean = filtered.iter().sum::<f64>() / filtered.len() as f64;
378 let variance = filtered
379 .iter()
380 .map(|&value| (value - mean).powi(2))
381 .sum::<f64>()
382 / filtered.len() as f64;
383
384 1.0 - (variance * 2.0).clamp(0.0, 1.0)
385}
386
387#[derive(Debug, Clone, PartialEq)]
393pub struct MesosystemCache {
394 spillover_cache: RefCell<HashMap<(MicrosystemId, MicrosystemId), f64>>,
396
397 role_conflict_cache: RefCell<HashMap<(MicrosystemId, MicrosystemId), f64>>,
399
400 valid: Cell<bool>,
402}
403
404impl MesosystemCache {
405 #[must_use]
407 pub fn new() -> Self {
408 MesosystemCache {
409 spillover_cache: RefCell::new(HashMap::new()),
410 role_conflict_cache: RefCell::new(HashMap::new()),
411 valid: Cell::new(false),
412 }
413 }
414
415 pub fn invalidate(&mut self) {
417 self.spillover_cache.get_mut().clear();
418 self.role_conflict_cache.get_mut().clear();
419 self.valid.set(false);
420 }
421
422 #[must_use]
424 pub fn is_valid(&self) -> bool {
425 self.valid.get()
426 }
427
428 pub fn get_spillover(
447 &self,
448 from: &MicrosystemId,
449 to: &MicrosystemId,
450 microsystems: &HashMap<MicrosystemId, Microsystem>,
451 ) -> f64 {
452 let key = (from.clone(), to.clone());
454 if let Some(&cached) = self.spillover_cache.borrow().get(&key) {
455 return cached;
456 }
457
458 let source = match microsystems.get(from) {
460 Some(m) => m,
461 None => return 0.0,
462 };
463
464 if microsystems.get(to).is_none() {
465 return 0.0;
466 }
467
468 let source_stress = source.stress_level();
470 let spillover = if source_stress > 0.5 {
471 (source_stress - 0.5) * 0.3
472 } else {
473 0.0
474 };
475 let spillover = spillover.clamp(0.0, 1.0);
476 self.spillover_cache
477 .borrow_mut()
478 .insert((from.clone(), to.clone()), spillover);
479 self.valid.set(true);
480 spillover
481 }
482
483 pub fn get_role_conflict(
498 &self,
499 context_a: &MicrosystemId,
500 context_b: &MicrosystemId,
501 microsystems: &HashMap<MicrosystemId, Microsystem>,
502 ) -> f64 {
503 let key = if context_a.as_str() < context_b.as_str() {
505 (context_a.clone(), context_b.clone())
506 } else {
507 (context_b.clone(), context_a.clone())
508 };
509
510 if let Some(&cached) = self.role_conflict_cache.borrow().get(&key) {
511 return cached;
512 }
513
514 let micro_a = match microsystems.get(context_a) {
516 Some(m) => m,
517 None => return 0.0,
518 };
519
520 let micro_b = match microsystems.get(context_b) {
521 Some(m) => m,
522 None => return 0.0,
523 };
524
525 let stress_a = micro_a.stress_level();
531 let stress_b = micro_b.stress_level();
532
533 let freq_a = micro_a.interaction_frequency();
535 let freq_b = micro_b.interaction_frequency();
536 let time_conflict = if freq_a > 0.5 && freq_b > 0.5 {
537 (freq_a + freq_b - 1.0) * 0.5
538 } else {
539 0.0
540 };
541
542 let stress_conflict = if stress_a > 0.5 && stress_b > 0.5 {
544 (stress_a + stress_b - 1.0) * 0.4
545 } else {
546 0.0
547 };
548
549 let warmth_diff = (micro_a.warmth() - micro_b.warmth()).abs();
551 let hostility_diff = (micro_a.hostility() - micro_b.hostility()).abs();
552 let treatment_conflict = (warmth_diff + hostility_diff) * 0.3;
553
554 let total = time_conflict + stress_conflict + treatment_conflict;
555 let total = total.clamp(0.0, 1.0);
556 self.role_conflict_cache.borrow_mut().insert(key, total);
557 self.valid.set(true);
558 total
559 }
560
561 pub fn list_linkages(
567 &self,
568 microsystems: &HashMap<MicrosystemId, Microsystem>,
569 ) -> Vec<(MicrosystemId, MicrosystemId)> {
570 let ids: Vec<_> = microsystems.keys().cloned().collect();
571 let mut linkages = Vec::new();
572
573 for i in 0..ids.len() {
574 for j in (i + 1)..ids.len() {
575 let a = &ids[i];
576 let b = &ids[j];
577
578 let micro_a = µsystems[a];
580 let micro_b = µsystems[b];
581
582 if micro_a.interaction_frequency() > 0.1 && micro_b.interaction_frequency() > 0.1 {
583 linkages.push((a.clone(), b.clone()));
584 }
585 }
586 }
587
588 linkages
589 }
590
591 #[must_use]
604 pub fn compute_consistency(microsystems: &HashMap<MicrosystemId, Microsystem>) -> f64 {
605 if microsystems.len() < 2 {
606 return 1.0; }
608
609 let mut work_role_clarity = Vec::new();
610 let mut family_role_clarity = Vec::new();
611 let mut social_predictability = Vec::new();
612
613 for micro in microsystems.values() {
614 match micro {
615 Microsystem::Work(work) => work_role_clarity.push(work.role_clarity),
616 Microsystem::Family(family) => family_role_clarity.push(family.role_clarity),
617 Microsystem::Social(social) => social_predictability.push(social.predictability),
618 _ => {}
619 }
620 }
621
622 consistency_from_values(&[
623 average(&work_role_clarity),
624 average(&family_role_clarity),
625 average(&social_predictability),
626 ])
627 }
628
629 #[must_use]
639 pub fn compute_shared_membership_strength(
640 microsystems: &HashMap<MicrosystemId, Microsystem>,
641 ) -> f64 {
642 use std::collections::HashSet;
643
644 let mut all_ids: HashSet<String> = HashSet::new();
646 let mut id_to_context_count: HashMap<String, usize> = HashMap::new();
647
648 for micro in microsystems.values() {
649 let member_ids: Vec<&crate::types::EntityId> = match micro {
650 Microsystem::Work(w) => {
651 let mut ids: Vec<_> = w.peer_ids.iter().collect();
652 if let Some(ref sup) = w.supervisor_id {
653 ids.push(sup);
654 }
655 ids
656 }
657 Microsystem::Family(f) => f.family_unit.iter().collect(),
658 Microsystem::Social(s) => s.close_friends.iter().collect(),
659 Microsystem::Education(e) => {
660 let mut ids: Vec<_> = e.peer_ids.iter().collect();
661 ids.extend(e.instructors.iter());
662 ids
663 }
664 Microsystem::Healthcare(h) => {
665 if let Some(ref provider) = h.primary_provider_id {
666 vec![provider]
667 } else {
668 vec![]
669 }
670 }
671 Microsystem::Religious(r) => {
672 if let Some(ref leader) = r.leader_id {
673 vec![leader]
674 } else {
675 vec![]
676 }
677 }
678 Microsystem::Neighborhood(n) => n.proximity_network.iter().collect(),
679 };
680
681 for id in member_ids {
682 let id_str = id.as_str().to_string();
683 all_ids.insert(id_str.clone());
684 *id_to_context_count.entry(id_str).or_insert(0) += 1;
685 }
686 }
687
688 if all_ids.is_empty() {
689 return 0.0;
690 }
691
692 let overlap_count = id_to_context_count
694 .values()
695 .filter(|&&count| count >= 2)
696 .count();
697
698 (overlap_count as f64 / all_ids.len() as f64).clamp(0.0, 1.0)
699 }
700}
701
702impl Default for MesosystemCache {
703 fn default() -> Self {
704 MesosystemCache::new()
705 }
706}
707
708#[cfg(test)]
709mod tests {
710 use super::*;
711 use crate::context::microsystem::{
712 EducationContext, FamilyContext, SocialContext, WorkContext,
713 };
714
715 #[test]
718 fn proximal_process_gate_allows_sufficient_thresholds() {
719 let result = passes_proximal_process_gate(0.5, 0.5, 0.3, 0.3);
720 assert!(result.is_ok());
721 }
722
723 #[test]
724 fn proximal_process_gate_with_reciprocity_blocks_low_reciprocity() {
725 let result = passes_proximal_process_gate_with_reciprocity(0.6, 0.6, 0.4, 0.3, 0.3, 0.6);
726 assert!(result.is_err());
727 let err = result.unwrap_err();
728 assert!(matches!(
729 err,
730 ProximalProcessGateError::ReciprocityBelowThreshold { actual, threshold }
731 if (actual - 0.4).abs() < f64::EPSILON
732 && (threshold - 0.6).abs() < f64::EPSILON
733 ));
734 }
735
736 #[test]
737 fn proximal_process_frequency_gate_blocks_low_frequency() {
738 let result = passes_proximal_process_gate(0.2, 0.5, 0.3, 0.3);
739 assert!(result.is_err());
740 let err = result.unwrap_err();
741 assert!(matches!(
742 err,
743 ProximalProcessGateError::FrequencyBelowThreshold { actual, threshold }
744 if (actual - 0.2).abs() < f64::EPSILON
745 && (threshold - 0.3).abs() < f64::EPSILON
746 ));
747 }
748
749 #[test]
750 fn proximal_process_complexity_gate_blocks_low_complexity() {
751 let result = passes_proximal_process_gate(0.5, 0.2, 0.3, 0.3);
752 assert!(result.is_err());
753 let err = result.unwrap_err();
754 assert!(matches!(
755 err,
756 ProximalProcessGateError::ComplexityBelowThreshold { actual, threshold }
757 if (actual - 0.2).abs() < f64::EPSILON
758 && (threshold - 0.3).abs() < f64::EPSILON
759 ));
760 }
761
762 #[test]
763 fn proximal_process_gate_returns_error_with_reason() {
764 let result = passes_proximal_process_gate(0.1, 0.1, 0.3, 0.3);
765 assert!(result.is_err());
766 let err = result.unwrap_err();
767 assert!(matches!(
768 err,
769 ProximalProcessGateError::BothBelowThreshold {
770 actual_frequency,
771 frequency_threshold,
772 actual_complexity,
773 complexity_threshold,
774 } if (actual_frequency - 0.1).abs() < f64::EPSILON
775 && (frequency_threshold - 0.3).abs() < f64::EPSILON
776 && (actual_complexity - 0.1).abs() < f64::EPSILON
777 && (complexity_threshold - 0.3).abs() < f64::EPSILON
778 ));
779 }
780
781 #[test]
782 fn proximal_process_gate_error_display() {
783 let err = ProximalProcessGateError::FrequencyBelowThreshold {
784 actual: 0.2,
785 threshold: 0.3,
786 };
787 let display = format!("{}", err);
788 assert!(display.contains("0.2"));
789 assert!(display.contains("0.3"));
790
791 let err = ProximalProcessGateError::ComplexityBelowThreshold {
792 actual: 0.2,
793 threshold: 0.3,
794 };
795 let display = format!("{}", err);
796 assert!(display.contains("complexity"));
797
798 let err = ProximalProcessGateError::BothBelowThreshold {
799 actual_frequency: 0.1,
800 frequency_threshold: 0.3,
801 actual_complexity: 0.2,
802 complexity_threshold: 0.4,
803 };
804 let display = format!("{}", err);
805 assert!(display.contains("frequency"));
806 assert!(display.contains("complexity"));
807
808 let err = ProximalProcessGateError::ReciprocityBelowThreshold {
809 actual: 0.2,
810 threshold: 0.6,
811 };
812 let display = format!("{}", err);
813 assert!(display.contains("reciprocity"));
814 }
815
816 #[test]
819 fn mesosystem_cache_new() {
820 let cache = MesosystemCache::new();
821 assert!(!cache.is_valid());
822 }
823
824 #[test]
825 fn mesosystem_cache_invalidate() {
826 let mut cache = MesosystemCache::new();
827 cache.valid.set(true);
828 cache.invalidate();
829 assert!(!cache.is_valid());
830 }
831
832 #[test]
833 fn mesosystem_cache_marks_valid_after_compute() {
834 let cache = MesosystemCache::new();
835 let mut microsystems = HashMap::new();
836
837 let mut work = WorkContext::default();
838 work.workload_stress = 0.8;
839 let work_id = MicrosystemId::new("work").unwrap();
840 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
841
842 let family_id = MicrosystemId::new("family").unwrap();
843 microsystems.insert(
844 family_id.clone(),
845 Microsystem::new_family(FamilyContext::default()),
846 );
847
848 let _ = cache.get_spillover(&work_id, &family_id, µsystems);
849 assert!(cache.is_valid());
850 }
851
852 #[test]
853 fn mesosystem_work_stress_spills_to_home() {
854 let cache = MesosystemCache::new();
855 let mut microsystems = HashMap::new();
856
857 let mut work = WorkContext::default();
859 work.workload_stress = 0.8;
860 work.interaction_profile.interaction_frequency = 0.7;
861 let work_id = MicrosystemId::new("work").unwrap();
862 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
863
864 let mut family = FamilyContext::default();
866 family.predictability = 0.3;
867 family.stability = 0.3;
868 let family_id = MicrosystemId::new("family").unwrap();
869 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
870
871 let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
872
873 let expected = (0.8 - 0.5) * 0.3;
875 assert!((spillover - expected).abs() < 1e-6);
876 }
877
878 #[test]
879 fn mesosystem_role_conflict_computation() {
880 let cache = MesosystemCache::new();
881 let mut microsystems = HashMap::new();
882
883 let mut work = WorkContext::default();
885 work.workload_stress = 0.8;
886 work.interaction_profile.interaction_frequency = 0.8;
887 let work_id = MicrosystemId::new("work").unwrap();
888 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
889
890 let mut family = FamilyContext::default();
892 family.caregiving_burden = 0.8;
893 family.interaction_profile.interaction_frequency = 0.8;
894 let family_id = MicrosystemId::new("family").unwrap();
895 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
896
897 let conflict = cache.get_role_conflict(&work_id, &family_id, µsystems);
898
899 assert!(conflict > 0.2);
901 assert!(conflict <= 1.0);
902 }
903
904 #[test]
905 fn mesosystem_role_conflict_symmetric() {
906 let cache = MesosystemCache::new();
907 let mut microsystems = HashMap::new();
908
909 let work_id = MicrosystemId::new("work").unwrap();
910 let family_id = MicrosystemId::new("family").unwrap();
911
912 microsystems.insert(
913 work_id.clone(),
914 Microsystem::new_work(WorkContext::default()),
915 );
916 microsystems.insert(
917 family_id.clone(),
918 Microsystem::new_family(FamilyContext::default()),
919 );
920
921 let conflict_ab = cache.get_role_conflict(&work_id, &family_id, µsystems);
922 let conflict_ba = cache.get_role_conflict(&family_id, &work_id, µsystems);
923
924 assert!((conflict_ab - conflict_ba).abs() < f64::EPSILON);
925 }
926
927 #[test]
928 fn mesosystem_spillover_nonexistent_source() {
929 let cache = MesosystemCache::new();
930 let microsystems = HashMap::new();
931
932 let from = MicrosystemId::new("nonexistent").unwrap();
933 let to = MicrosystemId::new("also_nonexistent").unwrap();
934
935 let spillover = cache.get_spillover(&from, &to, µsystems);
936 assert!((spillover - 0.0).abs() < f64::EPSILON);
937 }
938
939 #[test]
940 fn mesosystem_role_conflict_nonexistent() {
941 let cache = MesosystemCache::new();
942 let microsystems = HashMap::new();
943
944 let a = MicrosystemId::new("nonexistent").unwrap();
945 let b = MicrosystemId::new("also_nonexistent").unwrap();
946
947 let conflict = cache.get_role_conflict(&a, &b, µsystems);
948 assert!((conflict - 0.0).abs() < f64::EPSILON);
949 }
950
951 #[test]
952 fn mesosystem_list_linkages() {
953 let cache = MesosystemCache::new();
954 let mut microsystems = HashMap::new();
955
956 let work_id = MicrosystemId::new("work").unwrap();
957 let family_id = MicrosystemId::new("family").unwrap();
958 let social_id = MicrosystemId::new("social").unwrap();
959
960 microsystems.insert(
961 work_id.clone(),
962 Microsystem::new_work(WorkContext::default()),
963 );
964 microsystems.insert(
965 family_id.clone(),
966 Microsystem::new_family(FamilyContext::default()),
967 );
968 microsystems.insert(
969 social_id.clone(),
970 Microsystem::new_social(SocialContext::default()),
971 );
972
973 let linkages = cache.list_linkages(µsystems);
974
975 assert_eq!(linkages.len(), 3);
977
978 let work = microsystems
979 .get_mut(&work_id)
980 .and_then(Microsystem::work_mut)
981 .expect("work microsystem missing");
982 work.interaction_profile.interaction_frequency = 0.0;
983
984 let linkages = cache.list_linkages(µsystems);
985 assert_eq!(linkages.len(), 1);
986 }
987
988 #[test]
989 fn mesosystem_cache_retains_values_until_invalidated() {
990 let cache = MesosystemCache::new();
991 let mut microsystems = HashMap::new();
992
993 let work_id = MicrosystemId::new("work").unwrap();
994 let family_id = MicrosystemId::new("family").unwrap();
995
996 let mut work = WorkContext::default();
997 work.workload_stress = 0.5;
998 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
999 microsystems.insert(
1000 family_id.clone(),
1001 Microsystem::new_family(FamilyContext::default()),
1002 );
1003
1004 let spillover1 = cache.get_spillover(&work_id, &family_id, µsystems);
1006
1007 let work = microsystems
1009 .get_mut(&work_id)
1010 .and_then(Microsystem::work_mut)
1011 .expect("work microsystem missing");
1012 work.workload_stress = 0.9;
1013
1014 let spillover2 = cache.get_spillover(&work_id, &family_id, µsystems);
1016
1017 assert!((spillover1 - spillover2).abs() < f64::EPSILON);
1018 }
1019
1020 #[test]
1021 fn mesosystem_recomputes_on_microsystem_change() {
1022 let mut cache = MesosystemCache::new();
1023 let mut microsystems = HashMap::new();
1024
1025 let work_id = MicrosystemId::new("work").unwrap();
1026 let family_id = MicrosystemId::new("family").unwrap();
1027
1028 let mut work = WorkContext::default();
1029 work.workload_stress = 0.3;
1030 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1031
1032 let mut family = FamilyContext::default();
1033 family.predictability = 0.5;
1034 family.stability = 0.5;
1035 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
1036
1037 let spillover1 = cache.get_spillover(&work_id, &family_id, µsystems);
1038
1039 cache.invalidate();
1041 let work = microsystems
1042 .get_mut(&work_id)
1043 .and_then(Microsystem::work_mut)
1044 .expect("work microsystem missing");
1045 work.workload_stress = 0.9;
1046
1047 let spillover2 = cache.get_spillover(&work_id, &family_id, µsystems);
1048
1049 assert!(spillover2 > spillover1);
1051 }
1052
1053 #[test]
1054 fn mesosystem_consistency() {
1055 let mut microsystems = HashMap::new();
1056
1057 let mut work = WorkContext::default();
1059 work.role_clarity = 0.7;
1060 microsystems.insert(
1061 MicrosystemId::new("work").unwrap(),
1062 Microsystem::new_work(work),
1063 );
1064
1065 let mut family = FamilyContext::default();
1066 family.role_clarity = 0.7;
1067 microsystems.insert(
1068 MicrosystemId::new("family").unwrap(),
1069 Microsystem::new_family(family),
1070 );
1071
1072 let consistency = MesosystemCache::compute_consistency(µsystems);
1073
1074 assert!(consistency > 0.8);
1076 }
1077
1078 #[test]
1079 fn mesosystem_consistency_low_variance() {
1080 let mut microsystems = HashMap::new();
1081
1082 let mut work = WorkContext::default();
1084 work.role_clarity = 1.0;
1085 microsystems.insert(
1086 MicrosystemId::new("work").unwrap(),
1087 Microsystem::new_work(work),
1088 );
1089
1090 let mut family = FamilyContext::default();
1091 family.role_clarity = 0.0;
1092 microsystems.insert(
1093 MicrosystemId::new("family").unwrap(),
1094 Microsystem::new_family(family),
1095 );
1096
1097 let consistency = MesosystemCache::compute_consistency(µsystems);
1098
1099 assert!((consistency - 0.5).abs() < f64::EPSILON);
1101 }
1102
1103 #[test]
1104 fn mesosystem_consistency_single_microsystem() {
1105 let mut microsystems = HashMap::new();
1106 microsystems.insert(
1107 MicrosystemId::new("work").unwrap(),
1108 Microsystem::new_work(WorkContext::default()),
1109 );
1110
1111 let consistency = MesosystemCache::compute_consistency(µsystems);
1112 assert!((consistency - 1.0).abs() < f64::EPSILON);
1113 }
1114
1115 #[test]
1116 fn mesosystem_consistency_empty() {
1117 let microsystems = HashMap::new();
1118 let consistency = MesosystemCache::compute_consistency(µsystems);
1119 assert!((consistency - 1.0).abs() < f64::EPSILON);
1120 }
1121
1122 #[test]
1123 fn mesosystem_consistency_includes_social_and_ignores_other_types() {
1124 let mut microsystems = HashMap::new();
1125
1126 let mut work = WorkContext::default();
1127 work.role_clarity = 0.6;
1128 microsystems.insert(
1129 MicrosystemId::new("work").unwrap(),
1130 Microsystem::new_work(work),
1131 );
1132
1133 let mut family = FamilyContext::default();
1134 family.role_clarity = 0.4;
1135 microsystems.insert(
1136 MicrosystemId::new("family").unwrap(),
1137 Microsystem::new_family(family),
1138 );
1139
1140 let mut social = SocialContext::default();
1141 social.predictability = 0.7;
1142 microsystems.insert(
1143 MicrosystemId::new("social").unwrap(),
1144 Microsystem::new_social(social),
1145 );
1146
1147 microsystems.insert(
1148 MicrosystemId::new("education").unwrap(),
1149 Microsystem::new_education(EducationContext::default()),
1150 );
1151
1152 let consistency = MesosystemCache::compute_consistency(µsystems);
1153 assert!((0.0..=1.0).contains(&consistency));
1154 }
1155
1156 #[test]
1157 fn mesosystem_shared_membership_empty() {
1158 let microsystems = HashMap::new();
1159 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1160 assert!((strength - 0.0).abs() < f64::EPSILON);
1161 }
1162
1163 #[test]
1164 fn mesosystem_shared_membership_no_overlap() {
1165 let mut microsystems = HashMap::new();
1166
1167 let mut work = WorkContext::default();
1168 work.peer_ids = vec![
1169 crate::types::EntityId::new("alice").unwrap(),
1170 crate::types::EntityId::new("bob").unwrap(),
1171 ];
1172 microsystems.insert(
1173 MicrosystemId::new("work").unwrap(),
1174 Microsystem::new_work(work),
1175 );
1176
1177 let mut family = FamilyContext::default();
1178 family.family_unit = vec![
1179 crate::types::EntityId::new("charlie").unwrap(),
1180 crate::types::EntityId::new("diana").unwrap(),
1181 ];
1182 microsystems.insert(
1183 MicrosystemId::new("family").unwrap(),
1184 Microsystem::new_family(family),
1185 );
1186
1187 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1188 assert!((strength - 0.0).abs() < f64::EPSILON);
1189 }
1190
1191 #[test]
1192 fn mesosystem_shared_membership_with_overlap() {
1193 let mut microsystems = HashMap::new();
1194
1195 let shared_id = crate::types::EntityId::new("shared_person").unwrap();
1196
1197 let mut work = WorkContext::default();
1198 work.peer_ids = vec![
1199 shared_id.clone(),
1200 crate::types::EntityId::new("bob").unwrap(),
1201 ];
1202 microsystems.insert(
1203 MicrosystemId::new("work").unwrap(),
1204 Microsystem::new_work(work),
1205 );
1206
1207 let mut social = SocialContext::default();
1208 social.close_friends = vec![shared_id, crate::types::EntityId::new("charlie").unwrap()];
1209 microsystems.insert(
1210 MicrosystemId::new("social").unwrap(),
1211 Microsystem::new_social(social),
1212 );
1213
1214 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1215
1216 assert!(strength > 0.3 && strength < 0.4);
1218 }
1219
1220 #[test]
1223 fn mesosystem_state_defaults_when_empty() {
1224 let microsystems = HashMap::new();
1225 let state = MesosystemState::compute(µsystems);
1226 assert_eq!(state, MesosystemState::default());
1227 }
1228
1229 #[test]
1230 fn mesosystem_state_computes_cross_context_metrics() {
1231 use crate::types::EntityId;
1232
1233 let mut microsystems = HashMap::new();
1234 let shared_id = EntityId::new("shared_person").unwrap();
1235
1236 let mut work = WorkContext::default();
1237 work.role_clarity = 0.2;
1238 work.predictability = 0.3;
1239 work.warmth = 0.2;
1240 work.hostility = 0.1;
1241 work.workload_stress = 0.8;
1242 work.peer_ids = vec![shared_id.clone()];
1243 work.interaction_profile.interaction_frequency = 0.8;
1244 microsystems.insert(
1245 MicrosystemId::new("work").unwrap(),
1246 Microsystem::new_work(work),
1247 );
1248
1249 let mut family = FamilyContext::default();
1250 family.role_clarity = 0.8;
1251 family.predictability = 0.9;
1252 family.warmth = 0.7;
1253 family.hostility = 0.1;
1254 family.caregiving_burden = 0.8;
1255 family.interaction_profile.interaction_frequency = 0.8;
1256 microsystems.insert(
1257 MicrosystemId::new("family").unwrap(),
1258 Microsystem::new_family(family),
1259 );
1260
1261 let mut social = SocialContext::default();
1262 social.predictability = 0.9;
1263 social.warmth = 0.8;
1264 social.hostility = 0.1;
1265 social.close_friends = vec![shared_id];
1266 social.interaction_profile.interaction_frequency = 0.6;
1267 microsystems.insert(
1268 MicrosystemId::new("social").unwrap(),
1269 Microsystem::new_social(social),
1270 );
1271
1272 let state = MesosystemState::compute(µsystems);
1273
1274 assert!(state.work_family_conflict > 0.0);
1275 assert!((state.family_social_support - 0.6).abs() < 1e-6);
1276 assert!((state.work_social_conflict - 0.3).abs() < f64::EPSILON);
1277 assert!((state.shared_membership_strength - 1.0).abs() < f64::EPSILON);
1278 assert!(state.microsystem_influence_weight > 0.9);
1279
1280 let consistency = |values: &[f64]| {
1281 let mean = values.iter().sum::<f64>() / values.len() as f64;
1282 let variance = values
1283 .iter()
1284 .map(|&value| (value - mean).powi(2))
1285 .sum::<f64>()
1286 / values.len() as f64;
1287 1.0 - (variance * 2.0).clamp(0.0, 1.0)
1288 };
1289
1290 let expected_value_alignment = consistency(&[0.2, 0.8, 0.9]);
1291 let expected_autonomy = consistency(&[0.3, 0.9, 0.9]);
1292 let expected_meso = expected_value_alignment;
1293
1294 assert!((state.value_alignment_consistency - expected_value_alignment).abs() < 1e-6);
1295 assert!((state.autonomy_norm_consistency - expected_autonomy).abs() < 1e-6);
1296 assert!((state.mesosystem_consistency - expected_meso).abs() < 1e-6);
1297 }
1298
1299 #[test]
1302 fn mesosystem_linkage_creation() {
1303 let linkage = MesosystemLinkage {
1304 from: MicrosystemId::new("work").unwrap(),
1305 to: MicrosystemId::new("family").unwrap(),
1306 spillover: 0.3,
1307 role_conflict: 0.2,
1308 };
1309
1310 assert_eq!(linkage.from.as_str(), "work");
1311 assert_eq!(linkage.to.as_str(), "family");
1312 }
1313
1314 #[test]
1315 fn mesosystem_linkage_clone_eq() {
1316 let linkage1 = MesosystemLinkage {
1317 from: MicrosystemId::new("work").unwrap(),
1318 to: MicrosystemId::new("family").unwrap(),
1319 spillover: 0.3,
1320 role_conflict: 0.2,
1321 };
1322 let linkage2 = linkage1.clone();
1323 assert_eq!(linkage1, linkage2);
1324 }
1325
1326 #[test]
1327 fn mesosystem_cache_clone_eq() {
1328 let cache1 = MesosystemCache::new();
1329 let cache2 = cache1.clone();
1330 assert_eq!(cache1, cache2);
1331 }
1332
1333 #[test]
1334 fn mesosystem_cache_default() {
1335 let cache = MesosystemCache::default();
1336 assert!(!cache.is_valid());
1337 }
1338
1339 #[test]
1340 fn proximal_process_gate_error_clone_eq() {
1341 let err1 = ProximalProcessGateError::FrequencyBelowThreshold {
1342 actual: 0.2,
1343 threshold: 0.3,
1344 };
1345 let err2 = err1.clone();
1346 assert_eq!(err1, err2);
1347 }
1348
1349 #[test]
1352 fn mesosystem_spillover_to_social_target() {
1353 use crate::context::microsystem::SocialContext;
1354
1355 let cache = MesosystemCache::new();
1356 let mut microsystems = HashMap::new();
1357
1358 let mut work = WorkContext::default();
1359 work.workload_stress = 0.8;
1360 work.interaction_profile.interaction_frequency = 0.7;
1361 let work_id = MicrosystemId::new("work").unwrap();
1362 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1363
1364 let social = SocialContext::default();
1365 let social_id = MicrosystemId::new("social").unwrap();
1366 microsystems.insert(social_id.clone(), Microsystem::new_social(social));
1367
1368 let spillover = cache.get_spillover(&work_id, &social_id, µsystems);
1369 assert!(spillover >= 0.0 && spillover <= 1.0);
1370 }
1371
1372 #[test]
1373 fn mesosystem_spillover_to_education_target() {
1374 use crate::context::microsystem::EducationContext;
1375
1376 let cache = MesosystemCache::new();
1377 let mut microsystems = HashMap::new();
1378
1379 let mut work = WorkContext::default();
1380 work.workload_stress = 0.8;
1381 work.interaction_profile.interaction_frequency = 0.7;
1382 let work_id = MicrosystemId::new("work").unwrap();
1383 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1384
1385 let edu = EducationContext::default();
1386 let edu_id = MicrosystemId::new("education").unwrap();
1387 microsystems.insert(edu_id.clone(), Microsystem::new_education(edu));
1388
1389 let spillover = cache.get_spillover(&work_id, &edu_id, µsystems);
1390 assert!(spillover >= 0.0 && spillover <= 1.0);
1391 }
1392
1393 #[test]
1394 fn mesosystem_spillover_to_healthcare_target() {
1395 use crate::context::microsystem::HealthcareContext;
1396
1397 let cache = MesosystemCache::new();
1398 let mut microsystems = HashMap::new();
1399
1400 let mut work = WorkContext::default();
1401 work.workload_stress = 0.8;
1402 work.interaction_profile.interaction_frequency = 0.7;
1403 let work_id = MicrosystemId::new("work").unwrap();
1404 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1405
1406 let hc = HealthcareContext::default();
1407 let hc_id = MicrosystemId::new("healthcare").unwrap();
1408 microsystems.insert(hc_id.clone(), Microsystem::new_healthcare(hc));
1409
1410 let spillover = cache.get_spillover(&work_id, &hc_id, µsystems);
1411 assert!(spillover >= 0.0 && spillover <= 1.0);
1412 }
1413
1414 #[test]
1415 fn mesosystem_spillover_to_religious_target() {
1416 use crate::context::microsystem::ReligiousContext;
1417
1418 let cache = MesosystemCache::new();
1419 let mut microsystems = HashMap::new();
1420
1421 let mut work = WorkContext::default();
1422 work.workload_stress = 0.8;
1423 work.interaction_profile.interaction_frequency = 0.7;
1424 let work_id = MicrosystemId::new("work").unwrap();
1425 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1426
1427 let rel = ReligiousContext::default();
1428 let rel_id = MicrosystemId::new("religious").unwrap();
1429 microsystems.insert(rel_id.clone(), Microsystem::new_religious(rel));
1430
1431 let spillover = cache.get_spillover(&work_id, &rel_id, µsystems);
1432 assert!(spillover >= 0.0 && spillover <= 1.0);
1433 }
1434
1435 #[test]
1436 fn mesosystem_spillover_to_neighborhood_target() {
1437 use crate::context::microsystem::NeighborhoodContext;
1438
1439 let cache = MesosystemCache::new();
1440 let mut microsystems = HashMap::new();
1441
1442 let mut work = WorkContext::default();
1443 work.workload_stress = 0.8;
1444 work.interaction_profile.interaction_frequency = 0.7;
1445 let work_id = MicrosystemId::new("work").unwrap();
1446 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1447
1448 let nb = NeighborhoodContext::default();
1449 let nb_id = MicrosystemId::new("neighborhood").unwrap();
1450 microsystems.insert(nb_id.clone(), Microsystem::new_neighborhood(nb));
1451
1452 let spillover = cache.get_spillover(&work_id, &nb_id, µsystems);
1453 assert!(spillover >= 0.0 && spillover <= 1.0);
1454 }
1455
1456 #[test]
1457 fn mesosystem_shared_membership_with_education() {
1458 use crate::context::microsystem::EducationContext;
1459 use crate::types::EntityId;
1460
1461 let mut microsystems = HashMap::new();
1462
1463 let shared_id = EntityId::new("shared_person").unwrap();
1464
1465 let mut edu = EducationContext::default();
1466 edu.peer_ids = vec![shared_id.clone()];
1467 edu.instructors = vec![EntityId::new("instructor").unwrap()];
1468 microsystems.insert(
1469 MicrosystemId::new("education").unwrap(),
1470 Microsystem::new_education(edu),
1471 );
1472
1473 let mut social = SocialContext::default();
1474 social.close_friends = vec![shared_id.clone()];
1475 microsystems.insert(
1476 MicrosystemId::new("social").unwrap(),
1477 Microsystem::new_social(social),
1478 );
1479
1480 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1481 assert!(strength > 0.0);
1482 }
1483
1484 #[test]
1485 fn mesosystem_shared_membership_with_healthcare() {
1486 use crate::context::microsystem::HealthcareContext;
1487 use crate::types::EntityId;
1488
1489 let mut microsystems = HashMap::new();
1490
1491 let shared_id = EntityId::new("shared_person").unwrap();
1492
1493 let mut hc = HealthcareContext::default();
1494 hc.primary_provider_id = Some(shared_id.clone());
1495 microsystems.insert(
1496 MicrosystemId::new("healthcare").unwrap(),
1497 Microsystem::new_healthcare(hc),
1498 );
1499
1500 let mut social = SocialContext::default();
1501 social.close_friends = vec![shared_id.clone()];
1502 microsystems.insert(
1503 MicrosystemId::new("social").unwrap(),
1504 Microsystem::new_social(social),
1505 );
1506
1507 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1508 assert!(strength > 0.0);
1509 }
1510
1511 #[test]
1512 fn mesosystem_shared_membership_with_religious() {
1513 use crate::context::microsystem::ReligiousContext;
1514 use crate::types::EntityId;
1515
1516 let mut microsystems = HashMap::new();
1517
1518 let shared_id = EntityId::new("shared_person").unwrap();
1519
1520 let mut rel = ReligiousContext::default();
1521 rel.leader_id = Some(shared_id.clone());
1522 microsystems.insert(
1523 MicrosystemId::new("religious").unwrap(),
1524 Microsystem::new_religious(rel),
1525 );
1526
1527 let mut social = SocialContext::default();
1528 social.close_friends = vec![shared_id.clone()];
1529 microsystems.insert(
1530 MicrosystemId::new("social").unwrap(),
1531 Microsystem::new_social(social),
1532 );
1533
1534 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1535 assert!(strength > 0.0);
1536 }
1537
1538 #[test]
1539 fn mesosystem_shared_membership_with_neighborhood() {
1540 use crate::context::microsystem::NeighborhoodContext;
1541 use crate::types::EntityId;
1542
1543 let mut microsystems = HashMap::new();
1544
1545 let shared_id = EntityId::new("shared_person").unwrap();
1546
1547 let mut nb = NeighborhoodContext::default();
1548 nb.proximity_network = vec![shared_id.clone()];
1549 microsystems.insert(
1550 MicrosystemId::new("neighborhood").unwrap(),
1551 Microsystem::new_neighborhood(nb),
1552 );
1553
1554 let mut social = SocialContext::default();
1555 social.close_friends = vec![shared_id.clone()];
1556 microsystems.insert(
1557 MicrosystemId::new("social").unwrap(),
1558 Microsystem::new_social(social),
1559 );
1560
1561 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1562 assert!(strength > 0.0);
1563 }
1564
1565 #[test]
1566 fn mesosystem_shared_membership_healthcare_no_provider() {
1567 use crate::context::microsystem::HealthcareContext;
1568
1569 let mut microsystems = HashMap::new();
1570
1571 let hc = HealthcareContext::default(); microsystems.insert(
1573 MicrosystemId::new("healthcare").unwrap(),
1574 Microsystem::new_healthcare(hc),
1575 );
1576
1577 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1579 assert!((strength - 0.0).abs() < f64::EPSILON);
1580 }
1581
1582 #[test]
1583 fn mesosystem_shared_membership_religious_no_leader() {
1584 use crate::context::microsystem::ReligiousContext;
1585
1586 let mut microsystems = HashMap::new();
1587
1588 let rel = ReligiousContext::default(); microsystems.insert(
1590 MicrosystemId::new("religious").unwrap(),
1591 Microsystem::new_religious(rel),
1592 );
1593
1594 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1596 assert!((strength - 0.0).abs() < f64::EPSILON);
1597 }
1598
1599 #[test]
1600 fn mesosystem_shared_membership_with_supervisor() {
1601 use crate::types::EntityId;
1602
1603 let mut microsystems = HashMap::new();
1604
1605 let shared_id = EntityId::new("supervisor_friend").unwrap();
1606
1607 let mut work = WorkContext::default();
1608 work.peer_ids = vec![EntityId::new("coworker").unwrap()];
1609 work.supervisor_id = Some(shared_id.clone());
1610 microsystems.insert(
1611 MicrosystemId::new("work").unwrap(),
1612 Microsystem::new_work(work),
1613 );
1614
1615 let mut social = SocialContext::default();
1616 social.close_friends = vec![shared_id.clone()];
1617 microsystems.insert(
1618 MicrosystemId::new("social").unwrap(),
1619 Microsystem::new_social(social),
1620 );
1621
1622 let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
1623 assert!(strength > 0.0);
1625 }
1626
1627 #[test]
1628 fn mesosystem_spillover_target_missing() {
1629 let cache = MesosystemCache::new();
1630 let mut microsystems = HashMap::new();
1631
1632 let mut work = WorkContext::default();
1633 work.workload_stress = 0.8;
1634 let work_id = MicrosystemId::new("work").unwrap();
1635 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1636
1637 let family_id = MicrosystemId::new("family").unwrap();
1638 let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
1641 assert!((spillover - 0.0).abs() < f64::EPSILON);
1642 }
1643
1644 #[test]
1645 fn mesosystem_role_conflict_uses_cache() {
1646 let cache = MesosystemCache::new();
1647 let mut microsystems = HashMap::new();
1648
1649 let mut work = WorkContext::default();
1650 work.workload_stress = 0.8;
1651 work.interaction_profile.interaction_frequency = 0.8;
1652 let work_id = MicrosystemId::new("work").unwrap();
1653 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1654
1655 let mut family = FamilyContext::default();
1656 family.caregiving_burden = 0.8;
1657 family.interaction_profile.interaction_frequency = 0.8;
1658 let family_id = MicrosystemId::new("family").unwrap();
1659 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
1660
1661 let conflict1 = cache.get_role_conflict(&work_id, &family_id, µsystems);
1663
1664 let conflict2 = cache.get_role_conflict(&work_id, &family_id, µsystems);
1666
1667 assert!((conflict1 - conflict2).abs() < f64::EPSILON);
1668 }
1669
1670 #[test]
1671 fn mesosystem_spillover_to_work_target() {
1672 let cache = MesosystemCache::new();
1674 let mut microsystems = HashMap::new();
1675
1676 let mut family = FamilyContext::default();
1677 family.caregiving_burden = 0.8;
1678 family.hostility = 0.3;
1679 family.interaction_profile.interaction_frequency = 0.7;
1680 let family_id = MicrosystemId::new("family").unwrap();
1681 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
1682
1683 let work = WorkContext::default();
1684 let work_id = MicrosystemId::new("work").unwrap();
1685 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1686
1687 let spillover = cache.get_spillover(&family_id, &work_id, µsystems);
1689 assert!(spillover >= 0.0 && spillover <= 1.0);
1690 }
1691
1692 #[test]
1693 fn mesosystem_role_conflict_missing_context_b() {
1694 let cache = MesosystemCache::new();
1695 let mut microsystems = HashMap::new();
1696
1697 let work = WorkContext::default();
1698 let work_id = MicrosystemId::new("work").unwrap();
1699 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1700
1701 let family_id = MicrosystemId::new("family").unwrap();
1702 let conflict = cache.get_role_conflict(&work_id, &family_id, µsystems);
1705 assert!((conflict - 0.0).abs() < f64::EPSILON);
1706 }
1707
1708 #[test]
1709 fn mesosystem_spillover_uses_cache() {
1710 let cache = MesosystemCache::new();
1711 let mut microsystems = HashMap::new();
1712
1713 let mut work = WorkContext::default();
1714 work.workload_stress = 0.8;
1715 work.interaction_profile.interaction_frequency = 0.7;
1716 let work_id = MicrosystemId::new("work").unwrap();
1717 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1718
1719 let family = FamilyContext::default();
1720 let family_id = MicrosystemId::new("family").unwrap();
1721 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
1722
1723 let spillover1 = cache.get_spillover(&work_id, &family_id, µsystems);
1725
1726 let spillover2 = cache.get_spillover(&work_id, &family_id, µsystems);
1728 assert!((spillover1 - spillover2).abs() < f64::EPSILON);
1729 assert!(cache.spillover_cache.borrow().contains_key(&(work_id, family_id)));
1730 }
1731
1732 #[test]
1733 fn mesosystem_role_conflict_uses_cache_properly() {
1734 let cache = MesosystemCache::new();
1735 let mut microsystems = HashMap::new();
1736
1737 let mut work = WorkContext::default();
1738 work.workload_stress = 0.8;
1739 work.interaction_profile.interaction_frequency = 0.8;
1740 let work_id = MicrosystemId::new("work").unwrap();
1741 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1742
1743 let mut family = FamilyContext::default();
1744 family.caregiving_burden = 0.8;
1745 family.interaction_profile.interaction_frequency = 0.8;
1746 let family_id = MicrosystemId::new("family").unwrap();
1747 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
1748
1749 let conflict1 = cache.get_role_conflict(&work_id, &family_id, µsystems);
1751
1752 let conflict2 = cache.get_role_conflict(&work_id, &family_id, µsystems);
1754 assert!((conflict1 - conflict2).abs() < f64::EPSILON);
1755 assert!(cache.role_conflict_cache.borrow().len() >= 1);
1756 }
1757
1758
1759 #[test]
1760 fn mesosystem_shared_member_linkage() {
1761 use crate::types::EntityId;
1763
1764 let mut microsystems = HashMap::new();
1765
1766 let shared_member = EntityId::new("shared_person").unwrap();
1768
1769 let mut work = WorkContext::default();
1771 work.peer_ids = vec![shared_member.clone(), EntityId::new("coworker1").unwrap()];
1772 work.interaction_profile.interaction_frequency = 0.7;
1773 let work_id = MicrosystemId::new("work").unwrap();
1774 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1775
1776 let mut social = SocialContext::default();
1778 social.close_friends = vec![
1779 shared_member.clone(),
1780 EntityId::new("other_friend").unwrap(),
1781 ];
1782 social.interaction_profile.interaction_frequency = 0.6;
1783 let social_id = MicrosystemId::new("social").unwrap();
1784 microsystems.insert(social_id.clone(), Microsystem::new_social(social));
1785
1786 let cache = MesosystemCache::new();
1788 let linkages = cache.list_linkages(µsystems);
1789
1790 assert_eq!(linkages.len(), 1);
1792
1793 let membership_strength =
1795 MesosystemCache::compute_shared_membership_strength(µsystems);
1796
1797 assert!(membership_strength > 0.3);
1801
1802 let mut no_overlap_microsystems = HashMap::new();
1804 let mut work2 = WorkContext::default();
1805 work2.peer_ids = vec![EntityId::new("alice").unwrap()];
1806 work2.interaction_profile.interaction_frequency = 0.7;
1807 no_overlap_microsystems.insert(
1808 MicrosystemId::new("work").unwrap(),
1809 Microsystem::new_work(work2),
1810 );
1811
1812 let mut social2 = SocialContext::default();
1813 social2.close_friends = vec![EntityId::new("bob").unwrap()];
1814 social2.interaction_profile.interaction_frequency = 0.6;
1815 no_overlap_microsystems.insert(
1816 MicrosystemId::new("social").unwrap(),
1817 Microsystem::new_social(social2),
1818 );
1819
1820 let no_overlap_strength =
1821 MesosystemCache::compute_shared_membership_strength(&no_overlap_microsystems);
1822 assert!((no_overlap_strength - 0.0).abs() < f64::EPSILON);
1823 }
1824
1825 #[test]
1826 fn mesosystem_spillover_from_work_missing_family() {
1827 let cache = MesosystemCache::new();
1828 let mut microsystems = HashMap::new();
1829
1830 let mut work = WorkContext::default();
1831 work.workload_stress = 0.8;
1832 let work_id = MicrosystemId::new("work").unwrap();
1833 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1834
1835 let family_id = MicrosystemId::new("family").unwrap();
1836 let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
1839 assert!((spillover - 0.0).abs() < f64::EPSILON);
1840 }
1841
1842 #[test]
1843 fn mesosystem_work_family_conflict_with_non_zero_count() {
1844 let mut microsystems = HashMap::new();
1845
1846 let mut work = WorkContext::default();
1847 work.workload_stress = 0.8;
1848 let work_id = MicrosystemId::new("work").unwrap();
1849 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1850
1851 let mut family = FamilyContext::default();
1852 family.caregiving_burden = 0.7;
1853 let family_id = MicrosystemId::new("family").unwrap();
1854 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
1855
1856 let state = MesosystemState::compute(µsystems);
1857 let conflict = state.work_family_conflict;
1858 assert!(conflict >= 0.0);
1859 }
1860
1861 #[test]
1862 fn mesosystem_work_family_conflict_with_zero_count() {
1863 let mut microsystems = HashMap::new();
1864
1865 let mut work = WorkContext::default();
1867 work.workload_stress = 0.8;
1868 let work_id = MicrosystemId::new("work").unwrap();
1869 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1870
1871 let state = MesosystemState::compute(µsystems);
1872 assert_eq!(state.work_family_conflict, 0.0);
1874 }
1875
1876 #[test]
1877 fn mesosystem_spillover_below_threshold_returns_zero() {
1878 let cache = MesosystemCache::new();
1879 let mut microsystems = HashMap::new();
1880
1881 let mut work = WorkContext::default();
1882 work.workload_stress = 0.3; let work_id = MicrosystemId::new("work").unwrap();
1884 microsystems.insert(work_id.clone(), Microsystem::new_work(work));
1885
1886 let family = FamilyContext::default();
1887 let family_id = MicrosystemId::new("family").unwrap();
1888 microsystems.insert(family_id.clone(), Microsystem::new_family(family));
1889
1890 let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
1891 assert!((spillover - 0.0).abs() < f64::EPSILON);
1892 }
1893}