use crate::context::microsystem::Microsystem;
use crate::types::MicrosystemId;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
pub const INTERACTION_FREQUENCY_THRESHOLD: f64 = 0.3;
pub const INTERACTION_COMPLEXITY_THRESHOLD: f64 = 0.3;
pub const INTERACTION_RECIPROCITY_THRESHOLD: f64 = 0.6;
#[derive(Debug, Clone, PartialEq)]
pub enum ProximalProcessGateError {
FrequencyBelowThreshold {
actual: f64,
threshold: f64,
},
ComplexityBelowThreshold {
actual: f64,
threshold: f64,
},
BothBelowThreshold {
actual_frequency: f64,
frequency_threshold: f64,
actual_complexity: f64,
complexity_threshold: f64,
},
ReciprocityBelowThreshold {
actual: f64,
threshold: f64,
},
}
impl std::fmt::Display for ProximalProcessGateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ProximalProcessGateError::FrequencyBelowThreshold { actual, threshold } => {
write!(
f,
"Interaction frequency {} is below threshold {}",
actual, threshold
)
}
ProximalProcessGateError::ComplexityBelowThreshold { actual, threshold } => {
write!(
f,
"Interaction complexity {} is below threshold {}",
actual, threshold
)
}
ProximalProcessGateError::BothBelowThreshold {
actual_frequency,
frequency_threshold,
actual_complexity,
complexity_threshold,
} => {
write!(
f,
"Interaction frequency {} is below threshold {} and complexity {} is below threshold {}",
actual_frequency, frequency_threshold, actual_complexity, complexity_threshold
)
}
ProximalProcessGateError::ReciprocityBelowThreshold { actual, threshold } => {
write!(
f,
"Interaction reciprocity {} is below threshold {}",
actual, threshold
)
}
}
}
}
impl std::error::Error for ProximalProcessGateError {}
pub fn passes_proximal_process_gate(
frequency: f64,
complexity: f64,
frequency_threshold: f64,
complexity_threshold: f64,
) -> Result<(), ProximalProcessGateError> {
let freq_ok = frequency >= frequency_threshold;
let complex_ok = complexity >= complexity_threshold;
if !freq_ok && !complex_ok {
Err(ProximalProcessGateError::BothBelowThreshold {
actual_frequency: frequency,
frequency_threshold,
actual_complexity: complexity,
complexity_threshold,
})
} else if !freq_ok {
Err(ProximalProcessGateError::FrequencyBelowThreshold {
actual: frequency,
threshold: frequency_threshold,
})
} else if !complex_ok {
Err(ProximalProcessGateError::ComplexityBelowThreshold {
actual: complexity,
threshold: complexity_threshold,
})
} else {
Ok(())
}
}
pub fn passes_proximal_process_gate_with_reciprocity(
frequency: f64,
complexity: f64,
reciprocity: f64,
frequency_threshold: f64,
complexity_threshold: f64,
reciprocity_threshold: f64,
) -> Result<(), ProximalProcessGateError> {
passes_proximal_process_gate(frequency, complexity, frequency_threshold, complexity_threshold)?;
if reciprocity < reciprocity_threshold {
return Err(ProximalProcessGateError::ReciprocityBelowThreshold {
actual: reciprocity,
threshold: reciprocity_threshold,
});
}
Ok(())
}
#[derive(Debug, Clone, PartialEq)]
pub struct MesosystemLinkage {
pub from: MicrosystemId,
pub to: MicrosystemId,
pub spillover: f64,
pub role_conflict: f64,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MesosystemState {
pub work_family_conflict: f64,
pub family_social_support: f64,
pub work_social_conflict: f64,
pub value_alignment_consistency: f64,
pub autonomy_norm_consistency: f64,
pub mesosystem_consistency: f64,
pub shared_membership_strength: f64,
pub microsystem_influence_weight: f64,
}
impl Default for MesosystemState {
fn default() -> Self {
MesosystemState {
work_family_conflict: 0.0,
family_social_support: 0.0,
work_social_conflict: 0.0,
value_alignment_consistency: 1.0,
autonomy_norm_consistency: 1.0,
mesosystem_consistency: 1.0,
shared_membership_strength: 0.0,
microsystem_influence_weight: 0.0,
}
}
}
impl MesosystemState {
#[must_use]
pub fn compute(microsystems: &HashMap<MicrosystemId, Microsystem>) -> Self {
let mut work_ids = Vec::new();
let mut family_ids = Vec::new();
let mut work_role_clarity = Vec::new();
let mut family_role_clarity = Vec::new();
let mut social_predictability = Vec::new();
let mut work_predictability = Vec::new();
let mut family_predictability = Vec::new();
let mut family_support = Vec::new();
let mut work_low_predictability = false;
let mut social_high_warmth = false;
let mut total_frequency = 0.0;
let mut total_complexity = 0.0;
let mut count = 0.0;
for (id, micro) in microsystems {
total_frequency += micro.interaction_frequency();
total_complexity += micro.interaction_complexity();
count += 1.0;
if let Some(work) = micro.work() {
work_ids.push(id.clone());
work_role_clarity.push(work.role_clarity);
work_predictability.push(work.predictability);
if work.predictability < 0.4 {
work_low_predictability = true;
}
}
if let Some(family) = micro.family() {
family_ids.push(id.clone());
family_role_clarity.push(family.role_clarity);
family_predictability.push(family.predictability);
family_support.push((family.warmth - family.hostility).max(0.0));
}
if let Some(social) = micro.social() {
social_predictability.push(social.predictability);
if social.warmth > 0.7 {
social_high_warmth = true;
}
}
}
let work_family_conflict = if work_ids.is_empty() || family_ids.is_empty() {
0.0
} else {
let cache = MesosystemCache::new();
let mut total = 0.0;
let count = work_ids.len() * family_ids.len();
for work_id in &work_ids {
for family_id in &family_ids {
total += cache.get_role_conflict(work_id, family_id, microsystems);
}
}
total / count as f64
};
let family_social_support = average(&family_support).unwrap_or(0.0);
let work_social_conflict = if work_low_predictability && social_high_warmth {
0.3
} else {
0.0
};
let value_alignment_consistency = consistency_from_values(&[
average(&work_role_clarity),
average(&family_role_clarity),
average(&social_predictability),
]);
let autonomy_norm_consistency = consistency_from_values(&[
average(&work_predictability),
average(&family_predictability),
average(&social_predictability),
]);
let mesosystem_consistency = value_alignment_consistency;
let shared_membership_strength =
MesosystemCache::compute_shared_membership_strength(microsystems);
let (avg_frequency, avg_complexity) = if count > 0.0 {
(total_frequency / count, total_complexity / count)
} else {
(0.0, 0.0)
};
let microsystem_influence_weight = match passes_proximal_process_gate(
avg_frequency,
avg_complexity,
INTERACTION_FREQUENCY_THRESHOLD,
INTERACTION_COMPLEXITY_THRESHOLD,
) {
Ok(()) => 1.0,
Err(_) => 0.0,
};
MesosystemState {
work_family_conflict,
family_social_support,
work_social_conflict,
value_alignment_consistency,
autonomy_norm_consistency,
mesosystem_consistency,
shared_membership_strength,
microsystem_influence_weight,
}
}
}
fn average(values: &[f64]) -> Option<f64> {
if values.is_empty() {
None
} else {
Some(values.iter().sum::<f64>() / values.len() as f64)
}
}
fn consistency_from_values(values: &[Option<f64>]) -> f64 {
let filtered: Vec<f64> = values.iter().copied().flatten().collect();
if filtered.len() < 2 {
return 1.0;
}
let mean = filtered.iter().sum::<f64>() / filtered.len() as f64;
let variance = filtered
.iter()
.map(|&value| (value - mean).powi(2))
.sum::<f64>()
/ filtered.len() as f64;
1.0 - (variance * 2.0).clamp(0.0, 1.0)
}
#[derive(Debug, Clone, PartialEq)]
pub struct MesosystemCache {
spillover_cache: RefCell<HashMap<(MicrosystemId, MicrosystemId), f64>>,
role_conflict_cache: RefCell<HashMap<(MicrosystemId, MicrosystemId), f64>>,
valid: Cell<bool>,
}
impl MesosystemCache {
#[must_use]
pub fn new() -> Self {
MesosystemCache {
spillover_cache: RefCell::new(HashMap::new()),
role_conflict_cache: RefCell::new(HashMap::new()),
valid: Cell::new(false),
}
}
pub fn invalidate(&mut self) {
self.spillover_cache.get_mut().clear();
self.role_conflict_cache.get_mut().clear();
self.valid.set(false);
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.valid.get()
}
pub fn get_spillover(
&self,
from: &MicrosystemId,
to: &MicrosystemId,
microsystems: &HashMap<MicrosystemId, Microsystem>,
) -> f64 {
let key = (from.clone(), to.clone());
if let Some(&cached) = self.spillover_cache.borrow().get(&key) {
return cached;
}
let source = match microsystems.get(from) {
Some(m) => m,
None => return 0.0,
};
if microsystems.get(to).is_none() {
return 0.0;
}
let source_stress = source.stress_level();
let spillover = if source_stress > 0.5 {
(source_stress - 0.5) * 0.3
} else {
0.0
};
let spillover = spillover.clamp(0.0, 1.0);
self.spillover_cache
.borrow_mut()
.insert((from.clone(), to.clone()), spillover);
self.valid.set(true);
spillover
}
pub fn get_role_conflict(
&self,
context_a: &MicrosystemId,
context_b: &MicrosystemId,
microsystems: &HashMap<MicrosystemId, Microsystem>,
) -> f64 {
let key = if context_a.as_str() < context_b.as_str() {
(context_a.clone(), context_b.clone())
} else {
(context_b.clone(), context_a.clone())
};
if let Some(&cached) = self.role_conflict_cache.borrow().get(&key) {
return cached;
}
let micro_a = match microsystems.get(context_a) {
Some(m) => m,
None => return 0.0,
};
let micro_b = match microsystems.get(context_b) {
Some(m) => m,
None => return 0.0,
};
let stress_a = micro_a.stress_level();
let stress_b = micro_b.stress_level();
let freq_a = micro_a.interaction_frequency();
let freq_b = micro_b.interaction_frequency();
let time_conflict = if freq_a > 0.5 && freq_b > 0.5 {
(freq_a + freq_b - 1.0) * 0.5
} else {
0.0
};
let stress_conflict = if stress_a > 0.5 && stress_b > 0.5 {
(stress_a + stress_b - 1.0) * 0.4
} else {
0.0
};
let warmth_diff = (micro_a.warmth() - micro_b.warmth()).abs();
let hostility_diff = (micro_a.hostility() - micro_b.hostility()).abs();
let treatment_conflict = (warmth_diff + hostility_diff) * 0.3;
let total = time_conflict + stress_conflict + treatment_conflict;
let total = total.clamp(0.0, 1.0);
self.role_conflict_cache.borrow_mut().insert(key, total);
self.valid.set(true);
total
}
pub fn list_linkages(
&self,
microsystems: &HashMap<MicrosystemId, Microsystem>,
) -> Vec<(MicrosystemId, MicrosystemId)> {
let ids: Vec<_> = microsystems.keys().cloned().collect();
let mut linkages = Vec::new();
for i in 0..ids.len() {
for j in (i + 1)..ids.len() {
let a = &ids[i];
let b = &ids[j];
let micro_a = µsystems[a];
let micro_b = µsystems[b];
if micro_a.interaction_frequency() > 0.1 && micro_b.interaction_frequency() > 0.1 {
linkages.push((a.clone(), b.clone()));
}
}
}
linkages
}
#[must_use]
pub fn compute_consistency(microsystems: &HashMap<MicrosystemId, Microsystem>) -> f64 {
if microsystems.len() < 2 {
return 1.0; }
let mut work_role_clarity = Vec::new();
let mut family_role_clarity = Vec::new();
let mut social_predictability = Vec::new();
for micro in microsystems.values() {
match micro {
Microsystem::Work(work) => work_role_clarity.push(work.role_clarity),
Microsystem::Family(family) => family_role_clarity.push(family.role_clarity),
Microsystem::Social(social) => social_predictability.push(social.predictability),
_ => {}
}
}
consistency_from_values(&[
average(&work_role_clarity),
average(&family_role_clarity),
average(&social_predictability),
])
}
#[must_use]
pub fn compute_shared_membership_strength(
microsystems: &HashMap<MicrosystemId, Microsystem>,
) -> f64 {
use std::collections::HashSet;
let mut all_ids: HashSet<String> = HashSet::new();
let mut id_to_context_count: HashMap<String, usize> = HashMap::new();
for micro in microsystems.values() {
let member_ids: Vec<&crate::types::EntityId> = match micro {
Microsystem::Work(w) => {
let mut ids: Vec<_> = w.peer_ids.iter().collect();
if let Some(ref sup) = w.supervisor_id {
ids.push(sup);
}
ids
}
Microsystem::Family(f) => f.family_unit.iter().collect(),
Microsystem::Social(s) => s.close_friends.iter().collect(),
Microsystem::Education(e) => {
let mut ids: Vec<_> = e.peer_ids.iter().collect();
ids.extend(e.instructors.iter());
ids
}
Microsystem::Healthcare(h) => {
if let Some(ref provider) = h.primary_provider_id {
vec![provider]
} else {
vec![]
}
}
Microsystem::Religious(r) => {
if let Some(ref leader) = r.leader_id {
vec![leader]
} else {
vec![]
}
}
Microsystem::Neighborhood(n) => n.proximity_network.iter().collect(),
};
for id in member_ids {
let id_str = id.as_str().to_string();
all_ids.insert(id_str.clone());
*id_to_context_count.entry(id_str).or_insert(0) += 1;
}
}
if all_ids.is_empty() {
return 0.0;
}
let overlap_count = id_to_context_count
.values()
.filter(|&&count| count >= 2)
.count();
(overlap_count as f64 / all_ids.len() as f64).clamp(0.0, 1.0)
}
}
impl Default for MesosystemCache {
fn default() -> Self {
MesosystemCache::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::microsystem::{
EducationContext, FamilyContext, SocialContext, WorkContext,
};
#[test]
fn proximal_process_gate_allows_sufficient_thresholds() {
let result = passes_proximal_process_gate(0.5, 0.5, 0.3, 0.3);
assert!(result.is_ok());
}
#[test]
fn proximal_process_gate_with_reciprocity_blocks_low_reciprocity() {
let result = passes_proximal_process_gate_with_reciprocity(0.6, 0.6, 0.4, 0.3, 0.3, 0.6);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(
err,
ProximalProcessGateError::ReciprocityBelowThreshold { actual, threshold }
if (actual - 0.4).abs() < f64::EPSILON
&& (threshold - 0.6).abs() < f64::EPSILON
));
}
#[test]
fn proximal_process_frequency_gate_blocks_low_frequency() {
let result = passes_proximal_process_gate(0.2, 0.5, 0.3, 0.3);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(
err,
ProximalProcessGateError::FrequencyBelowThreshold { actual, threshold }
if (actual - 0.2).abs() < f64::EPSILON
&& (threshold - 0.3).abs() < f64::EPSILON
));
}
#[test]
fn proximal_process_complexity_gate_blocks_low_complexity() {
let result = passes_proximal_process_gate(0.5, 0.2, 0.3, 0.3);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(
err,
ProximalProcessGateError::ComplexityBelowThreshold { actual, threshold }
if (actual - 0.2).abs() < f64::EPSILON
&& (threshold - 0.3).abs() < f64::EPSILON
));
}
#[test]
fn proximal_process_gate_returns_error_with_reason() {
let result = passes_proximal_process_gate(0.1, 0.1, 0.3, 0.3);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(matches!(
err,
ProximalProcessGateError::BothBelowThreshold {
actual_frequency,
frequency_threshold,
actual_complexity,
complexity_threshold,
} if (actual_frequency - 0.1).abs() < f64::EPSILON
&& (frequency_threshold - 0.3).abs() < f64::EPSILON
&& (actual_complexity - 0.1).abs() < f64::EPSILON
&& (complexity_threshold - 0.3).abs() < f64::EPSILON
));
}
#[test]
fn proximal_process_gate_error_display() {
let err = ProximalProcessGateError::FrequencyBelowThreshold {
actual: 0.2,
threshold: 0.3,
};
let display = format!("{}", err);
assert!(display.contains("0.2"));
assert!(display.contains("0.3"));
let err = ProximalProcessGateError::ComplexityBelowThreshold {
actual: 0.2,
threshold: 0.3,
};
let display = format!("{}", err);
assert!(display.contains("complexity"));
let err = ProximalProcessGateError::BothBelowThreshold {
actual_frequency: 0.1,
frequency_threshold: 0.3,
actual_complexity: 0.2,
complexity_threshold: 0.4,
};
let display = format!("{}", err);
assert!(display.contains("frequency"));
assert!(display.contains("complexity"));
let err = ProximalProcessGateError::ReciprocityBelowThreshold {
actual: 0.2,
threshold: 0.6,
};
let display = format!("{}", err);
assert!(display.contains("reciprocity"));
}
#[test]
fn mesosystem_cache_new() {
let cache = MesosystemCache::new();
assert!(!cache.is_valid());
}
#[test]
fn mesosystem_cache_invalidate() {
let mut cache = MesosystemCache::new();
cache.valid.set(true);
cache.invalidate();
assert!(!cache.is_valid());
}
#[test]
fn mesosystem_cache_marks_valid_after_compute() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(
family_id.clone(),
Microsystem::new_family(FamilyContext::default()),
);
let _ = cache.get_spillover(&work_id, &family_id, µsystems);
assert!(cache.is_valid());
}
#[test]
fn mesosystem_work_stress_spills_to_home() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.predictability = 0.3;
family.stability = 0.3;
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
let expected = (0.8 - 0.5) * 0.3;
assert!((spillover - expected).abs() < 1e-6);
}
#[test]
fn mesosystem_role_conflict_computation() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let conflict = cache.get_role_conflict(&work_id, &family_id, µsystems);
assert!(conflict > 0.2);
assert!(conflict <= 1.0);
}
#[test]
fn mesosystem_role_conflict_symmetric() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(
work_id.clone(),
Microsystem::new_work(WorkContext::default()),
);
microsystems.insert(
family_id.clone(),
Microsystem::new_family(FamilyContext::default()),
);
let conflict_ab = cache.get_role_conflict(&work_id, &family_id, µsystems);
let conflict_ba = cache.get_role_conflict(&family_id, &work_id, µsystems);
assert!((conflict_ab - conflict_ba).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_spillover_nonexistent_source() {
let cache = MesosystemCache::new();
let microsystems = HashMap::new();
let from = MicrosystemId::new("nonexistent").unwrap();
let to = MicrosystemId::new("also_nonexistent").unwrap();
let spillover = cache.get_spillover(&from, &to, µsystems);
assert!((spillover - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_role_conflict_nonexistent() {
let cache = MesosystemCache::new();
let microsystems = HashMap::new();
let a = MicrosystemId::new("nonexistent").unwrap();
let b = MicrosystemId::new("also_nonexistent").unwrap();
let conflict = cache.get_role_conflict(&a, &b, µsystems);
assert!((conflict - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_list_linkages() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let social_id = MicrosystemId::new("social").unwrap();
microsystems.insert(
work_id.clone(),
Microsystem::new_work(WorkContext::default()),
);
microsystems.insert(
family_id.clone(),
Microsystem::new_family(FamilyContext::default()),
);
microsystems.insert(
social_id.clone(),
Microsystem::new_social(SocialContext::default()),
);
let linkages = cache.list_linkages(µsystems);
assert_eq!(linkages.len(), 3);
let work = microsystems
.get_mut(&work_id)
.and_then(Microsystem::work_mut)
.expect("work microsystem missing");
work.interaction_profile.interaction_frequency = 0.0;
let linkages = cache.list_linkages(µsystems);
assert_eq!(linkages.len(), 1);
}
#[test]
fn mesosystem_cache_retains_values_until_invalidated() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.5;
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
microsystems.insert(
family_id.clone(),
Microsystem::new_family(FamilyContext::default()),
);
let spillover1 = cache.get_spillover(&work_id, &family_id, µsystems);
let work = microsystems
.get_mut(&work_id)
.and_then(Microsystem::work_mut)
.expect("work microsystem missing");
work.workload_stress = 0.9;
let spillover2 = cache.get_spillover(&work_id, &family_id, µsystems);
assert!((spillover1 - spillover2).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_recomputes_on_microsystem_change() {
let mut cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.3;
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.predictability = 0.5;
family.stability = 0.5;
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let spillover1 = cache.get_spillover(&work_id, &family_id, µsystems);
cache.invalidate();
let work = microsystems
.get_mut(&work_id)
.and_then(Microsystem::work_mut)
.expect("work microsystem missing");
work.workload_stress = 0.9;
let spillover2 = cache.get_spillover(&work_id, &family_id, µsystems);
assert!(spillover2 > spillover1);
}
#[test]
fn mesosystem_consistency() {
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.role_clarity = 0.7;
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut family = FamilyContext::default();
family.role_clarity = 0.7;
microsystems.insert(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
let consistency = MesosystemCache::compute_consistency(µsystems);
assert!(consistency > 0.8);
}
#[test]
fn mesosystem_consistency_low_variance() {
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.role_clarity = 1.0;
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut family = FamilyContext::default();
family.role_clarity = 0.0;
microsystems.insert(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
let consistency = MesosystemCache::compute_consistency(µsystems);
assert!((consistency - 0.5).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_consistency_single_microsystem() {
let mut microsystems = HashMap::new();
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(WorkContext::default()),
);
let consistency = MesosystemCache::compute_consistency(µsystems);
assert!((consistency - 1.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_consistency_empty() {
let microsystems = HashMap::new();
let consistency = MesosystemCache::compute_consistency(µsystems);
assert!((consistency - 1.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_consistency_includes_social_and_ignores_other_types() {
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.role_clarity = 0.6;
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut family = FamilyContext::default();
family.role_clarity = 0.4;
microsystems.insert(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
let mut social = SocialContext::default();
social.predictability = 0.7;
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
microsystems.insert(
MicrosystemId::new("education").unwrap(),
Microsystem::new_education(EducationContext::default()),
);
let consistency = MesosystemCache::compute_consistency(µsystems);
assert!((0.0..=1.0).contains(&consistency));
}
#[test]
fn mesosystem_shared_membership_empty() {
let microsystems = HashMap::new();
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!((strength - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_shared_membership_no_overlap() {
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.peer_ids = vec![
crate::types::EntityId::new("alice").unwrap(),
crate::types::EntityId::new("bob").unwrap(),
];
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut family = FamilyContext::default();
family.family_unit = vec![
crate::types::EntityId::new("charlie").unwrap(),
crate::types::EntityId::new("diana").unwrap(),
];
microsystems.insert(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!((strength - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_shared_membership_with_overlap() {
let mut microsystems = HashMap::new();
let shared_id = crate::types::EntityId::new("shared_person").unwrap();
let mut work = WorkContext::default();
work.peer_ids = vec![
shared_id.clone(),
crate::types::EntityId::new("bob").unwrap(),
];
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut social = SocialContext::default();
social.close_friends = vec![shared_id, crate::types::EntityId::new("charlie").unwrap()];
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!(strength > 0.3 && strength < 0.4);
}
#[test]
fn mesosystem_state_defaults_when_empty() {
let microsystems = HashMap::new();
let state = MesosystemState::compute(µsystems);
assert_eq!(state, MesosystemState::default());
}
#[test]
fn mesosystem_state_computes_cross_context_metrics() {
use crate::types::EntityId;
let mut microsystems = HashMap::new();
let shared_id = EntityId::new("shared_person").unwrap();
let mut work = WorkContext::default();
work.role_clarity = 0.2;
work.predictability = 0.3;
work.warmth = 0.2;
work.hostility = 0.1;
work.workload_stress = 0.8;
work.peer_ids = vec![shared_id.clone()];
work.interaction_profile.interaction_frequency = 0.8;
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut family = FamilyContext::default();
family.role_clarity = 0.8;
family.predictability = 0.9;
family.warmth = 0.7;
family.hostility = 0.1;
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
microsystems.insert(
MicrosystemId::new("family").unwrap(),
Microsystem::new_family(family),
);
let mut social = SocialContext::default();
social.predictability = 0.9;
social.warmth = 0.8;
social.hostility = 0.1;
social.close_friends = vec![shared_id];
social.interaction_profile.interaction_frequency = 0.6;
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let state = MesosystemState::compute(µsystems);
assert!(state.work_family_conflict > 0.0);
assert!((state.family_social_support - 0.6).abs() < 1e-6);
assert!((state.work_social_conflict - 0.3).abs() < f64::EPSILON);
assert!((state.shared_membership_strength - 1.0).abs() < f64::EPSILON);
assert!(state.microsystem_influence_weight > 0.9);
let consistency = |values: &[f64]| {
let mean = values.iter().sum::<f64>() / values.len() as f64;
let variance = values
.iter()
.map(|&value| (value - mean).powi(2))
.sum::<f64>()
/ values.len() as f64;
1.0 - (variance * 2.0).clamp(0.0, 1.0)
};
let expected_value_alignment = consistency(&[0.2, 0.8, 0.9]);
let expected_autonomy = consistency(&[0.3, 0.9, 0.9]);
let expected_meso = expected_value_alignment;
assert!((state.value_alignment_consistency - expected_value_alignment).abs() < 1e-6);
assert!((state.autonomy_norm_consistency - expected_autonomy).abs() < 1e-6);
assert!((state.mesosystem_consistency - expected_meso).abs() < 1e-6);
}
#[test]
fn mesosystem_linkage_creation() {
let linkage = MesosystemLinkage {
from: MicrosystemId::new("work").unwrap(),
to: MicrosystemId::new("family").unwrap(),
spillover: 0.3,
role_conflict: 0.2,
};
assert_eq!(linkage.from.as_str(), "work");
assert_eq!(linkage.to.as_str(), "family");
}
#[test]
fn mesosystem_linkage_clone_eq() {
let linkage1 = MesosystemLinkage {
from: MicrosystemId::new("work").unwrap(),
to: MicrosystemId::new("family").unwrap(),
spillover: 0.3,
role_conflict: 0.2,
};
let linkage2 = linkage1.clone();
assert_eq!(linkage1, linkage2);
}
#[test]
fn mesosystem_cache_clone_eq() {
let cache1 = MesosystemCache::new();
let cache2 = cache1.clone();
assert_eq!(cache1, cache2);
}
#[test]
fn mesosystem_cache_default() {
let cache = MesosystemCache::default();
assert!(!cache.is_valid());
}
#[test]
fn proximal_process_gate_error_clone_eq() {
let err1 = ProximalProcessGateError::FrequencyBelowThreshold {
actual: 0.2,
threshold: 0.3,
};
let err2 = err1.clone();
assert_eq!(err1, err2);
}
#[test]
fn mesosystem_spillover_to_social_target() {
use crate::context::microsystem::SocialContext;
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let social = SocialContext::default();
let social_id = MicrosystemId::new("social").unwrap();
microsystems.insert(social_id.clone(), Microsystem::new_social(social));
let spillover = cache.get_spillover(&work_id, &social_id, µsystems);
assert!(spillover >= 0.0 && spillover <= 1.0);
}
#[test]
fn mesosystem_spillover_to_education_target() {
use crate::context::microsystem::EducationContext;
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let edu = EducationContext::default();
let edu_id = MicrosystemId::new("education").unwrap();
microsystems.insert(edu_id.clone(), Microsystem::new_education(edu));
let spillover = cache.get_spillover(&work_id, &edu_id, µsystems);
assert!(spillover >= 0.0 && spillover <= 1.0);
}
#[test]
fn mesosystem_spillover_to_healthcare_target() {
use crate::context::microsystem::HealthcareContext;
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let hc = HealthcareContext::default();
let hc_id = MicrosystemId::new("healthcare").unwrap();
microsystems.insert(hc_id.clone(), Microsystem::new_healthcare(hc));
let spillover = cache.get_spillover(&work_id, &hc_id, µsystems);
assert!(spillover >= 0.0 && spillover <= 1.0);
}
#[test]
fn mesosystem_spillover_to_religious_target() {
use crate::context::microsystem::ReligiousContext;
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let rel = ReligiousContext::default();
let rel_id = MicrosystemId::new("religious").unwrap();
microsystems.insert(rel_id.clone(), Microsystem::new_religious(rel));
let spillover = cache.get_spillover(&work_id, &rel_id, µsystems);
assert!(spillover >= 0.0 && spillover <= 1.0);
}
#[test]
fn mesosystem_spillover_to_neighborhood_target() {
use crate::context::microsystem::NeighborhoodContext;
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let nb = NeighborhoodContext::default();
let nb_id = MicrosystemId::new("neighborhood").unwrap();
microsystems.insert(nb_id.clone(), Microsystem::new_neighborhood(nb));
let spillover = cache.get_spillover(&work_id, &nb_id, µsystems);
assert!(spillover >= 0.0 && spillover <= 1.0);
}
#[test]
fn mesosystem_shared_membership_with_education() {
use crate::context::microsystem::EducationContext;
use crate::types::EntityId;
let mut microsystems = HashMap::new();
let shared_id = EntityId::new("shared_person").unwrap();
let mut edu = EducationContext::default();
edu.peer_ids = vec![shared_id.clone()];
edu.instructors = vec![EntityId::new("instructor").unwrap()];
microsystems.insert(
MicrosystemId::new("education").unwrap(),
Microsystem::new_education(edu),
);
let mut social = SocialContext::default();
social.close_friends = vec![shared_id.clone()];
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!(strength > 0.0);
}
#[test]
fn mesosystem_shared_membership_with_healthcare() {
use crate::context::microsystem::HealthcareContext;
use crate::types::EntityId;
let mut microsystems = HashMap::new();
let shared_id = EntityId::new("shared_person").unwrap();
let mut hc = HealthcareContext::default();
hc.primary_provider_id = Some(shared_id.clone());
microsystems.insert(
MicrosystemId::new("healthcare").unwrap(),
Microsystem::new_healthcare(hc),
);
let mut social = SocialContext::default();
social.close_friends = vec![shared_id.clone()];
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!(strength > 0.0);
}
#[test]
fn mesosystem_shared_membership_with_religious() {
use crate::context::microsystem::ReligiousContext;
use crate::types::EntityId;
let mut microsystems = HashMap::new();
let shared_id = EntityId::new("shared_person").unwrap();
let mut rel = ReligiousContext::default();
rel.leader_id = Some(shared_id.clone());
microsystems.insert(
MicrosystemId::new("religious").unwrap(),
Microsystem::new_religious(rel),
);
let mut social = SocialContext::default();
social.close_friends = vec![shared_id.clone()];
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!(strength > 0.0);
}
#[test]
fn mesosystem_shared_membership_with_neighborhood() {
use crate::context::microsystem::NeighborhoodContext;
use crate::types::EntityId;
let mut microsystems = HashMap::new();
let shared_id = EntityId::new("shared_person").unwrap();
let mut nb = NeighborhoodContext::default();
nb.proximity_network = vec![shared_id.clone()];
microsystems.insert(
MicrosystemId::new("neighborhood").unwrap(),
Microsystem::new_neighborhood(nb),
);
let mut social = SocialContext::default();
social.close_friends = vec![shared_id.clone()];
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!(strength > 0.0);
}
#[test]
fn mesosystem_shared_membership_healthcare_no_provider() {
use crate::context::microsystem::HealthcareContext;
let mut microsystems = HashMap::new();
let hc = HealthcareContext::default(); microsystems.insert(
MicrosystemId::new("healthcare").unwrap(),
Microsystem::new_healthcare(hc),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!((strength - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_shared_membership_religious_no_leader() {
use crate::context::microsystem::ReligiousContext;
let mut microsystems = HashMap::new();
let rel = ReligiousContext::default(); microsystems.insert(
MicrosystemId::new("religious").unwrap(),
Microsystem::new_religious(rel),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!((strength - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_shared_membership_with_supervisor() {
use crate::types::EntityId;
let mut microsystems = HashMap::new();
let shared_id = EntityId::new("supervisor_friend").unwrap();
let mut work = WorkContext::default();
work.peer_ids = vec![EntityId::new("coworker").unwrap()];
work.supervisor_id = Some(shared_id.clone());
microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut social = SocialContext::default();
social.close_friends = vec![shared_id.clone()];
microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let strength = MesosystemCache::compute_shared_membership_strength(µsystems);
assert!(strength > 0.0);
}
#[test]
fn mesosystem_spillover_target_missing() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let family_id = MicrosystemId::new("family").unwrap();
let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
assert!((spillover - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_role_conflict_uses_cache() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let conflict1 = cache.get_role_conflict(&work_id, &family_id, µsystems);
let conflict2 = cache.get_role_conflict(&work_id, &family_id, µsystems);
assert!((conflict1 - conflict2).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_spillover_to_work_target() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.hostility = 0.3;
family.interaction_profile.interaction_frequency = 0.7;
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let work = WorkContext::default();
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let spillover = cache.get_spillover(&family_id, &work_id, µsystems);
assert!(spillover >= 0.0 && spillover <= 1.0);
}
#[test]
fn mesosystem_role_conflict_missing_context_b() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let work = WorkContext::default();
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let family_id = MicrosystemId::new("family").unwrap();
let conflict = cache.get_role_conflict(&work_id, &family_id, µsystems);
assert!((conflict - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_spillover_uses_cache() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let family = FamilyContext::default();
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let spillover1 = cache.get_spillover(&work_id, &family_id, µsystems);
let spillover2 = cache.get_spillover(&work_id, &family_id, µsystems);
assert!((spillover1 - spillover2).abs() < f64::EPSILON);
assert!(cache.spillover_cache.borrow().contains_key(&(work_id, family_id)));
}
#[test]
fn mesosystem_role_conflict_uses_cache_properly() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let conflict1 = cache.get_role_conflict(&work_id, &family_id, µsystems);
let conflict2 = cache.get_role_conflict(&work_id, &family_id, µsystems);
assert!((conflict1 - conflict2).abs() < f64::EPSILON);
assert!(cache.role_conflict_cache.borrow().len() >= 1);
}
#[test]
fn mesosystem_shared_member_linkage() {
use crate::types::EntityId;
let mut microsystems = HashMap::new();
let shared_member = EntityId::new("shared_person").unwrap();
let mut work = WorkContext::default();
work.peer_ids = vec![shared_member.clone(), EntityId::new("coworker1").unwrap()];
work.interaction_profile.interaction_frequency = 0.7;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let mut social = SocialContext::default();
social.close_friends = vec![
shared_member.clone(),
EntityId::new("other_friend").unwrap(),
];
social.interaction_profile.interaction_frequency = 0.6;
let social_id = MicrosystemId::new("social").unwrap();
microsystems.insert(social_id.clone(), Microsystem::new_social(social));
let cache = MesosystemCache::new();
let linkages = cache.list_linkages(µsystems);
assert_eq!(linkages.len(), 1);
let membership_strength =
MesosystemCache::compute_shared_membership_strength(µsystems);
assert!(membership_strength > 0.3);
let mut no_overlap_microsystems = HashMap::new();
let mut work2 = WorkContext::default();
work2.peer_ids = vec![EntityId::new("alice").unwrap()];
work2.interaction_profile.interaction_frequency = 0.7;
no_overlap_microsystems.insert(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work2),
);
let mut social2 = SocialContext::default();
social2.close_friends = vec![EntityId::new("bob").unwrap()];
social2.interaction_profile.interaction_frequency = 0.6;
no_overlap_microsystems.insert(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social2),
);
let no_overlap_strength =
MesosystemCache::compute_shared_membership_strength(&no_overlap_microsystems);
assert!((no_overlap_strength - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_spillover_from_work_missing_family() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let family_id = MicrosystemId::new("family").unwrap();
let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
assert!((spillover - 0.0).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_work_family_conflict_with_non_zero_count() {
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.caregiving_burden = 0.7;
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let state = MesosystemState::compute(µsystems);
let conflict = state.work_family_conflict;
assert!(conflict >= 0.0);
}
#[test]
fn mesosystem_work_family_conflict_with_zero_count() {
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let state = MesosystemState::compute(µsystems);
assert_eq!(state.work_family_conflict, 0.0);
}
#[test]
fn mesosystem_spillover_below_threshold_returns_zero() {
let cache = MesosystemCache::new();
let mut microsystems = HashMap::new();
let mut work = WorkContext::default();
work.workload_stress = 0.3; let work_id = MicrosystemId::new("work").unwrap();
microsystems.insert(work_id.clone(), Microsystem::new_work(work));
let family = FamilyContext::default();
let family_id = MicrosystemId::new("family").unwrap();
microsystems.insert(family_id.clone(), Microsystem::new_family(family));
let spillover = cache.get_spillover(&work_id, &family_id, µsystems);
assert!((spillover - 0.0).abs() < f64::EPSILON);
}
}