mod chronosystem;
mod effects;
mod exosystem;
mod macrosystem;
mod mesosystem;
mod microsystem;
pub use chronosystem::{
ChronosystemContext, CohortEffects, CriticalPeriod, CulturalShift, HistoricalPeriod,
NonNormativeEvent, NormativeTransition, TurningPoint, TurningPointDomain,
};
pub(crate) use effects::apply_context_effects;
pub use exosystem::{ExosystemContext, ParentWorkQuality};
pub use macrosystem::{
CulturalOrientation, InstitutionalStructure, MacrosystemConstraintSet, MacrosystemContext,
};
pub use mesosystem::{
passes_proximal_process_gate, passes_proximal_process_gate_with_reciprocity, MesosystemCache,
MesosystemLinkage, MesosystemState, ProximalProcessGateError,
INTERACTION_COMPLEXITY_THRESHOLD, INTERACTION_FREQUENCY_THRESHOLD,
INTERACTION_RECIPROCITY_THRESHOLD,
};
pub use microsystem::{
EducationContext, FamilyContext, FamilyRole, HealthcareContext, InteractionProfile,
Microsystem, MicrosystemType, NeighborhoodContext, ReligiousContext, SocialContext,
WorkContext,
};
use crate::enums::{ApparentGender, ApparentRace, ContextPath, VisibleTrait};
use crate::state::DemandCharacteristics;
use crate::types::MicrosystemId;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) struct PadBounds {
pub valence_min: f32,
pub valence_max: f32,
pub arousal_min: f32,
pub arousal_max: f32,
pub dominance_min: f32,
pub dominance_max: f32,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EcologicalContext {
microsystems: HashMap<MicrosystemId, Microsystem>,
exosystem: ExosystemContext,
macrosystem: MacrosystemContext,
chronosystem: ChronosystemContext,
mesosystem_cache: MesosystemCache,
mesosystem_state: MesosystemState,
}
impl EcologicalContext {
#[must_use]
pub fn new() -> Self {
EcologicalContext {
microsystems: HashMap::new(),
exosystem: ExosystemContext::default(),
macrosystem: MacrosystemContext::default(),
chronosystem: ChronosystemContext::default(),
mesosystem_cache: MesosystemCache::new(),
mesosystem_state: MesosystemState::default(),
}
}
pub fn add_microsystem(&mut self, id: MicrosystemId, microsystem: Microsystem) {
self.microsystems.insert(id, microsystem);
self.mesosystem_cache.invalidate();
}
#[must_use]
pub fn get_microsystem(&self, id: &MicrosystemId) -> Option<&Microsystem> {
self.microsystems.get(id)
}
pub fn get_microsystem_mut(&mut self, id: &MicrosystemId) -> Option<&mut Microsystem> {
self.microsystems.get_mut(id)
}
pub fn remove_microsystem(&mut self, id: &MicrosystemId) -> Option<Microsystem> {
let result = self.microsystems.remove(id);
if result.is_some() {
self.mesosystem_cache.invalidate();
}
result
}
#[must_use]
pub fn list_microsystems(&self, microsystem_type: MicrosystemType) -> Vec<MicrosystemId> {
self.microsystems
.iter()
.filter(|(_, m)| m.microsystem_type() == microsystem_type)
.map(|(id, _)| id.clone())
.collect()
}
#[must_use]
pub fn microsystem_count(&self) -> usize {
self.microsystems.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.microsystems.is_empty()
}
pub fn microsystems_iter(&self) -> impl Iterator<Item = (&MicrosystemId, &Microsystem)> {
self.microsystems.iter()
}
#[must_use]
pub fn exosystem(&self) -> &ExosystemContext {
&self.exosystem
}
pub fn exosystem_mut(&mut self) -> &mut ExosystemContext {
&mut self.exosystem
}
#[must_use]
pub fn macrosystem(&self) -> &MacrosystemContext {
&self.macrosystem
}
pub fn macrosystem_mut(&mut self) -> &mut MacrosystemContext {
&mut self.macrosystem
}
#[must_use]
pub fn chronosystem(&self) -> &ChronosystemContext {
&self.chronosystem
}
pub fn chronosystem_mut(&mut self) -> &mut ChronosystemContext {
&mut self.chronosystem
}
#[must_use]
pub fn mesosystem_cache(&self) -> &MesosystemCache {
&self.mesosystem_cache
}
#[must_use]
pub fn mesosystem_state(&mut self) -> &MesosystemState {
self.mesosystem_state = MesosystemState::compute(&self.microsystems);
&self.mesosystem_state
}
#[must_use]
pub fn mesosystem_state_cached(&self) -> &MesosystemState {
&self.mesosystem_state
}
pub fn invalidate_mesosystem_cache(&mut self) {
self.mesosystem_cache.invalidate();
}
#[must_use]
pub fn get_spillover(&self, from: &MicrosystemId, to: &MicrosystemId) -> f64 {
self.mesosystem_cache
.get_spillover(from, to, &self.microsystems)
}
#[must_use]
pub fn get_role_conflict(&self, context_a: &MicrosystemId, context_b: &MicrosystemId) -> f64 {
self.mesosystem_cache
.get_role_conflict(context_a, context_b, &self.microsystems)
}
#[must_use]
pub fn list_linkages(&self) -> Vec<(MicrosystemId, MicrosystemId)> {
self.mesosystem_cache.list_linkages(&self.microsystems)
}
#[must_use]
pub fn aggregate_social_warmth(&self) -> f64 {
let social_contexts: Vec<_> = self
.microsystems
.values()
.filter_map(|m| m.social())
.collect();
if social_contexts.is_empty() {
0.5
} else {
let sum: f64 = social_contexts.iter().map(|s| s.warmth).sum();
sum / social_contexts.len() as f64
}
}
#[must_use]
pub fn aggregate_stress(&self) -> f64 {
if self.microsystems.is_empty() {
return 0.0;
}
let total_stress: f64 = self.microsystems.values().map(|m| m.stress_level()).sum();
total_stress / self.microsystems.len() as f64
}
#[must_use]
pub fn aggregate_hostility(&self) -> f64 {
if self.microsystems.is_empty() {
return 0.0;
}
let total_hostility: f64 = self.microsystems.values().map(|m| m.hostility()).sum();
total_hostility / self.microsystems.len() as f64
}
#[must_use]
fn average_family_climate(&self) -> f64 {
let family_contexts: Vec<_> = self
.microsystems
.values()
.filter_map(|m| m.family())
.collect();
if family_contexts.is_empty() {
return 0.5;
}
let sum: f64 = family_contexts
.iter()
.map(|family| {
let supportive = (family.warmth
+ (1.0 - family.hostility)
+ family.stability
+ family.predictability
+ family.cohesion)
/ 5.0;
supportive.clamp(0.0, 1.0)
})
.sum();
sum / family_contexts.len() as f64
}
#[must_use]
pub(crate) fn compute_pad_bounds(&self) -> PadBounds {
let threat_level = self.exosystem.threat_level;
let cultural_restrictiveness = self.macrosystem.cultural_restrictiveness;
let health_access = self.exosystem.health_system_access;
let family_climate = self.average_family_climate();
let valence_limit =
((1.0 - 0.6 * cultural_restrictiveness) * (0.8 + 0.2 * family_climate))
.clamp(0.4, 1.0);
let arousal_max = (1.0 - 0.2 * threat_level - 0.4 * (1.0 - health_access)).clamp(0.4, 1.0);
let dominance_max =
(1.0 - 0.7 * threat_level - 0.3 * (1.0 - family_climate)).clamp(0.3, 1.0);
PadBounds {
valence_min: -(valence_limit as f32),
valence_max: valence_limit as f32,
arousal_min: -1.0,
arousal_max: arousal_max as f32,
dominance_min: -1.0,
dominance_max: dominance_max as f32,
}
}
pub fn apply_person_to_context_shaping(
&mut self,
extraversion: f32,
conscientiousness: f32,
agreeableness: f32,
neuroticism: f32,
grievance: f32,
) {
if extraversion > 0.3 {
let boost = f64::from(extraversion - 0.3) * 0.1;
for microsystem in self.microsystems.values_mut() {
if let Some(social) = microsystem.social_mut() {
social.warmth = (social.warmth + boost).min(1.0);
}
}
}
if conscientiousness > 0.3 {
let boost = f64::from(conscientiousness - 0.3) * 0.1;
for microsystem in self.microsystems.values_mut() {
if let Some(work) = microsystem.work_mut() {
work.role_clarity = (work.role_clarity + boost).min(1.0);
work.predictability = (work.predictability + boost * 0.5).min(1.0);
}
}
}
if agreeableness > 0.3 {
let boost = f64::from(agreeableness - 0.3) * 0.1;
for microsystem in self.microsystems.values_mut() {
if let Some(family) = microsystem.family_mut() {
family.warmth = (family.warmth + boost).min(1.0);
}
}
}
if neuroticism > 0.3 {
let penalty = f64::from(neuroticism - 0.3) * 0.1;
for microsystem in self.microsystems.values_mut() {
if let Some(work) = microsystem.work_mut() {
work.stability = (work.stability - penalty).max(0.0);
work.predictability = (work.predictability - penalty).max(0.0);
}
if let Some(family) = microsystem.family_mut() {
family.stability = (family.stability - penalty).max(0.0);
family.predictability = (family.predictability - penalty).max(0.0);
}
}
}
if grievance > 0.3 {
let boost = f64::from(grievance - 0.3) * 0.1;
for microsystem in self.microsystems.values_mut() {
if let Some(work) = microsystem.work_mut() {
work.hostility = (work.hostility + boost).min(1.0);
}
if let Some(family) = microsystem.family_mut() {
family.hostility = (family.hostility + boost).min(1.0);
}
if let Some(social) = microsystem.social_mut() {
social.hostility = (social.hostility + boost).min(1.0);
}
}
}
}
pub fn apply_person_to_context_modulation(
&mut self,
boldness: f32,
sensitivity: f32,
sociability: f32,
) {
let boldness = boldness.clamp(0.0, 1.0);
let sensitivity = sensitivity.clamp(0.0, 1.0);
let sociability = sociability.clamp(0.0, 1.0);
if boldness > 0.3 {
let boost = f64::from(boldness - 0.3) * 0.08;
for microsystem in self.microsystems.values_mut() {
if let Some(social) = microsystem.social_mut() {
social.interaction_profile.interaction_frequency =
(social.interaction_profile.interaction_frequency + boost).min(1.0);
social.group_standing = (social.group_standing + boost * 0.5).min(1.0);
}
if let Some(work) = microsystem.work_mut() {
work.cognitive_stimulation =
(work.cognitive_stimulation + boost * 0.6).min(1.0);
}
}
}
if sociability > 0.3 {
let boost = f64::from(sociability - 0.3) * 0.06;
for microsystem in self.microsystems.values_mut() {
if let Some(social) = microsystem.social_mut() {
social.warmth = (social.warmth + boost).min(1.0);
social.interaction_profile.reciprocity_balance =
(social.interaction_profile.reciprocity_balance + boost * 0.5).min(1.0);
}
}
}
if sensitivity > 0.3 {
let dampening = f64::from(sensitivity - 0.3) * 0.08;
for microsystem in self.microsystems.values_mut() {
if let Some(social) = microsystem.social_mut() {
social.hostility = (social.hostility - dampening).max(0.0);
social.interaction_profile.interaction_frequency =
(social.interaction_profile.interaction_frequency - dampening).max(0.0);
}
if let Some(work) = microsystem.work_mut() {
work.cognitive_stimulation =
(work.cognitive_stimulation - dampening).max(0.0);
work.predictability = (work.predictability + dampening * 0.4).min(1.0);
}
if let Some(family) = microsystem.family_mut() {
family.hostility = (family.hostility - dampening * 0.6).max(0.0);
family.predictability = (family.predictability + dampening * 0.4).min(1.0);
}
}
}
}
pub fn apply_demand_characteristics_shaping(&mut self, demand: &DemandCharacteristics) {
let mut bias: f64 = 0.0;
match demand.apparent_race() {
ApparentRace::Marginalized => bias -= 0.15,
ApparentRace::Privileged => bias += 0.05,
ApparentRace::Unknown => {}
}
match demand.apparent_gender() {
ApparentGender::Female => bias -= 0.04,
ApparentGender::NonBinary => bias -= 0.06,
ApparentGender::Male => {}
ApparentGender::Unknown => {}
}
for trait_marker in demand.visible_traits() {
match trait_marker {
VisibleTrait::StigmatizedAppearance => bias -= 0.18,
VisibleTrait::AttractivePresentation => bias += 0.12,
VisibleTrait::VisibleDisability => bias -= 0.1,
VisibleTrait::ApparentPoverty => bias -= 0.08,
}
}
let bias = bias.clamp(-0.4, 0.2);
if bias.abs() <= f64::EPSILON {
return;
}
for microsystem in self.microsystems.values_mut() {
if let Some(social) = microsystem.social_mut() {
if bias < 0.0 {
social.hostility = (social.hostility + bias.abs()).min(1.0);
social.warmth = (social.warmth - bias.abs() * 0.6).max(0.0);
social.group_standing = (social.group_standing - bias.abs() * 0.5).max(0.0);
} else {
social.warmth = (social.warmth + bias).min(1.0);
social.group_standing = (social.group_standing + bias * 0.6).min(1.0);
}
}
if let Some(work) = microsystem.work_mut() {
if bias < 0.0 {
work.hostility = (work.hostility + bias.abs() * 0.6).min(1.0);
work.warmth = (work.warmth - bias.abs() * 0.4).max(0.0);
} else {
work.warmth = (work.warmth + bias * 0.3).min(1.0);
}
}
if let Some(neighborhood) = microsystem.neighborhood_mut() {
if bias < 0.0 {
neighborhood.hostility =
(neighborhood.hostility + bias.abs() * 0.5).min(1.0);
neighborhood.warmth =
(neighborhood.warmth - bias.abs() * 0.4).max(0.0);
}
}
}
}
#[must_use]
pub fn compute_context_to_person_effects(&self, relationship_quality: f64) -> (f32, f32) {
let aggregate_stress = self.aggregate_stress();
let social_warmth = self.aggregate_social_warmth();
let aggregate_hostility = self.aggregate_hostility();
let mut stress_adjustment = (aggregate_stress * 0.1) as f32;
let loneliness_factor = (1.0 - social_warmth) * (1.0 - relationship_quality);
let loneliness_adjustment = (loneliness_factor * 0.1) as f32;
let hostility_factor = aggregate_hostility * (1.0 - relationship_quality);
stress_adjustment += (hostility_factor * 0.05) as f32;
(stress_adjustment, loneliness_adjustment)
}
#[must_use]
pub fn get(&self, path: &ContextPath) -> Option<f64> {
match path {
ContextPath::Microsystem(id, mpath) => {
self.microsystems.get(id).map(|m| m.get_value(mpath))
}
ContextPath::Exosystem(epath) => Some(self.exosystem.get_value(epath)),
ContextPath::Macrosystem(mpath) => Some(self.macrosystem.get_value(mpath)),
ContextPath::Chronosystem(cpath) => Some(self.chronosystem.get_value(cpath)),
}
}
pub fn set(&mut self, path: &ContextPath, value: f64) -> bool {
match path {
ContextPath::Microsystem(id, mpath) => {
if let Some(m) = self.microsystems.get_mut(id) {
m.set_value(mpath, value);
true
} else {
false
}
}
ContextPath::Exosystem(epath) => {
self.exosystem.set_value(epath, value);
true
}
ContextPath::Macrosystem(mpath) => {
self.macrosystem.set_value(mpath, value);
true
}
ContextPath::Chronosystem(cpath) => {
self.chronosystem.set_value(cpath, value);
true
}
}
}
}
impl Default for EcologicalContext {
fn default() -> Self {
EcologicalContext::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::enums::{
ApparentGender, ApparentRace, ChronosystemPath, ExosystemPath, MacrosystemPath,
MicrosystemPath, VisibleTrait,
};
use crate::state::DemandCharacteristics;
#[test]
fn ecological_context_creation_default() {
let context = EcologicalContext::default();
assert!(context.is_empty());
assert_eq!(context.microsystem_count(), 0);
}
#[test]
fn ecological_context_new() {
let context = EcologicalContext::new();
assert!(context.is_empty());
}
#[test]
fn add_microsystem() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
let work = Microsystem::new_work(WorkContext::default());
context.add_microsystem(work_id.clone(), work);
assert_eq!(context.microsystem_count(), 1);
assert!(context.get_microsystem(&work_id).is_some());
}
#[test]
fn add_multiple_microsystems() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
let family_id = MicrosystemId::new("family_primary").unwrap();
context.add_microsystem(
work_id.clone(),
Microsystem::new_work(WorkContext::default()),
);
context.add_microsystem(
family_id.clone(),
Microsystem::new_family(FamilyContext::default()),
);
assert_eq!(context.microsystem_count(), 2);
assert!(context.get_microsystem(&work_id).is_some());
assert!(context.get_microsystem(&family_id).is_some());
}
#[test]
fn remove_microsystem() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
context.add_microsystem(
work_id.clone(),
Microsystem::new_work(WorkContext::default()),
);
assert_eq!(context.microsystem_count(), 1);
let removed = context.remove_microsystem(&work_id);
assert!(removed.is_some());
assert_eq!(context.microsystem_count(), 0);
}
#[test]
fn remove_nonexistent_microsystem() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
let removed = context.remove_microsystem(&work_id);
assert!(removed.is_none());
}
#[test]
fn list_microsystems_by_type() {
let mut context = EcologicalContext::default();
let work1 = MicrosystemId::new("work_acme").unwrap();
let work2 = MicrosystemId::new("work_other").unwrap();
let family_id = MicrosystemId::new("family_primary").unwrap();
context.add_microsystem(work1.clone(), Microsystem::new_work(WorkContext::default()));
context.add_microsystem(work2.clone(), Microsystem::new_work(WorkContext::default()));
context.add_microsystem(
family_id.clone(),
Microsystem::new_family(FamilyContext::default()),
);
let work_ids = context.list_microsystems(MicrosystemType::Work);
assert_eq!(work_ids.len(), 2);
let family_ids = context.list_microsystems(MicrosystemType::Family);
assert_eq!(family_ids.len(), 1);
}
#[test]
fn get_microsystem_mut() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.3;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let microsystem = context.get_microsystem_mut(&work_id).unwrap();
let work_ref = microsystem.work_mut().unwrap();
work_ref.workload_stress = 0.8;
let retrieved = context.get_microsystem(&work_id).unwrap();
assert!((retrieved.work().unwrap().workload_stress - 0.8).abs() < f64::EPSILON);
}
#[test]
fn microsystems_iter() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
let family_id = MicrosystemId::new("family_primary").unwrap();
context.add_microsystem(work_id, Microsystem::new_work(WorkContext::default()));
context.add_microsystem(family_id, Microsystem::new_family(FamilyContext::default()));
let count = context.microsystems_iter().count();
assert_eq!(count, 2);
}
#[test]
fn exosystem_accessor() {
let context = EcologicalContext::default();
let exo = context.exosystem();
assert!(exo.resource_availability >= 0.0 && exo.resource_availability <= 1.0);
}
#[test]
fn exosystem_mut_accessor() {
let mut context = EcologicalContext::default();
context.exosystem_mut().resource_availability = 0.9;
assert!((context.exosystem().resource_availability - 0.9).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_accessor() {
let context = EcologicalContext::default();
let macro_ctx = context.macrosystem();
let _ = macro_ctx.cultural_orientation;
}
#[test]
fn macrosystem_mut_accessor() {
let mut context = EcologicalContext::default();
context.macrosystem_mut().cultural_stress = 0.4;
assert!((context.macrosystem().cultural_stress - 0.4).abs() < f64::EPSILON);
}
#[test]
fn chronosystem_accessor() {
let context = EcologicalContext::default();
let chrono = context.chronosystem();
let _ = chrono.historical_period();
}
#[test]
fn chronosystem_mut_accessor() {
let mut context = EcologicalContext::default();
context
.chronosystem_mut()
.historical_period_mut()
.stability_level = 0.3;
assert!(
(context.chronosystem().historical_period().stability_level - 0.3).abs() < f64::EPSILON
);
}
#[test]
fn invalidate_mesosystem_cache() {
let mut context = EcologicalContext::default();
context.invalidate_mesosystem_cache();
}
#[test]
fn context_path_macrosystem_query() {
let context = EcologicalContext::default();
let path = ContextPath::Macrosystem(MacrosystemPath::PowerDistance);
let value = context.get(&path);
assert!(value.is_some());
assert!(value.unwrap() >= 0.0 && value.unwrap() <= 1.0);
}
#[test]
fn context_path_exosystem_query() {
let context = EcologicalContext::default();
let path = ContextPath::Exosystem(ExosystemPath::ResourceAvailability);
let value = context.get(&path);
assert!(value.is_some());
}
#[test]
fn context_path_chronosystem_query() {
let context = EcologicalContext::default();
let path = ContextPath::Chronosystem(ChronosystemPath::StabilityLevel);
let value = context.get(&path);
assert!(value.is_some());
}
#[test]
fn context_path_microsystem_query() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.7;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let path = ContextPath::Microsystem(
work_id,
MicrosystemPath::Work(crate::enums::WorkPath::WorkloadStress),
);
let value = context.get(&path);
assert!(value.is_some());
assert!((value.unwrap() - 0.7).abs() < f64::EPSILON);
}
#[test]
fn context_path_microsystem_query_nonexistent() {
let context = EcologicalContext::default();
let work_id = MicrosystemId::new("nonexistent").unwrap();
let path = ContextPath::Microsystem(
work_id,
MicrosystemPath::Work(crate::enums::WorkPath::WorkloadStress),
);
let value = context.get(&path);
assert!(value.is_none());
}
#[test]
fn context_set_macrosystem() {
let mut context = EcologicalContext::default();
let path = ContextPath::Macrosystem(MacrosystemPath::CulturalStress);
let result = context.set(&path, 0.6);
assert!(result);
let value = context.get(&path).unwrap();
assert!((value - 0.6).abs() < f64::EPSILON);
}
#[test]
fn context_set_exosystem() {
let mut context = EcologicalContext::default();
let path = ContextPath::Exosystem(ExosystemPath::InstitutionalSupport);
let result = context.set(&path, 0.8);
assert!(result);
let value = context.get(&path).unwrap();
assert!((value - 0.8).abs() < f64::EPSILON);
}
#[test]
fn context_set_chronosystem() {
let mut context = EcologicalContext::default();
let path = ContextPath::Chronosystem(ChronosystemPath::ResourceScarcity);
let result = context.set(&path, 0.4);
assert!(result);
let value = context.get(&path).unwrap();
assert!((value - 0.4).abs() < f64::EPSILON);
}
#[test]
fn context_set_microsystem() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_acme").unwrap();
context.add_microsystem(
work_id.clone(),
Microsystem::new_work(WorkContext::default()),
);
let path = ContextPath::Microsystem(
work_id.clone(),
MicrosystemPath::Work(crate::enums::WorkPath::WorkloadStress),
);
let result = context.set(&path, 0.9);
assert!(result);
let value = context.get(&path).unwrap();
assert!((value - 0.9).abs() < f64::EPSILON);
}
#[test]
fn context_set_microsystem_nonexistent() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("nonexistent").unwrap();
let path = ContextPath::Microsystem(
work_id,
MicrosystemPath::Work(crate::enums::WorkPath::WorkloadStress),
);
let result = context.set(&path, 0.9);
assert!(!result);
}
#[test]
fn clone_and_equality() {
let context1 = EcologicalContext::default();
let context2 = context1.clone();
assert_eq!(context1, context2);
}
#[test]
fn debug_format() {
let context = EcologicalContext::default();
let debug = format!("{:?}", context);
assert!(debug.contains("EcologicalContext"));
}
#[test]
fn microsystem_multiple_contexts_aggregate() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work_primary").unwrap();
let family_id = MicrosystemId::new("family_primary").unwrap();
let social_id = MicrosystemId::new("social_friends").unwrap();
context.add_microsystem(work_id, Microsystem::new_work(WorkContext::default()));
context.add_microsystem(family_id, Microsystem::new_family(FamilyContext::default()));
context.add_microsystem(social_id, Microsystem::new_social(SocialContext::default()));
assert_eq!(context.microsystem_count(), 3);
}
#[test]
fn microsystem_id_identifies_instance_path_identifies_property() {
let mut context = EcologicalContext::default();
let work1_id = MicrosystemId::new("work_job1").unwrap();
let work2_id = MicrosystemId::new("work_job2").unwrap();
let mut work1 = WorkContext::default();
work1.workload_stress = 0.3;
let mut work2 = WorkContext::default();
work2.workload_stress = 0.8;
context.add_microsystem(work1_id.clone(), Microsystem::new_work(work1));
context.add_microsystem(work2_id.clone(), Microsystem::new_work(work2));
let path1 = ContextPath::Microsystem(
work1_id,
MicrosystemPath::Work(crate::enums::WorkPath::WorkloadStress),
);
let path2 = ContextPath::Microsystem(
work2_id,
MicrosystemPath::Work(crate::enums::WorkPath::WorkloadStress),
);
let value1 = context.get(&path1).unwrap();
let value2 = context.get(&path2).unwrap();
assert!((value1 - 0.3).abs() < f64::EPSILON);
assert!((value2 - 0.8).abs() < f64::EPSILON);
}
#[test]
fn mesosystem_cache_accessor() {
let context = EcologicalContext::default();
let cache = context.mesosystem_cache();
assert!(!cache.is_valid());
}
#[test]
fn mesosystem_state_accessor_recomputes() {
let mut context = EcologicalContext::default();
assert_eq!(
context.mesosystem_state_cached(),
&MesosystemState::default()
);
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.interaction_profile.interaction_frequency = 0.8;
context.add_microsystem(
MicrosystemId::new("work").unwrap(),
Microsystem::new_work(work),
);
let mut social = SocialContext::default();
social.warmth = 0.8;
social.predictability = 0.9;
social.hostility = 0.1;
social.interaction_profile.interaction_frequency = 0.6;
context.add_microsystem(
MicrosystemId::new("social").unwrap(),
Microsystem::new_social(social),
);
let state = context.mesosystem_state();
assert!((state.work_social_conflict - 0.3).abs() < f64::EPSILON);
assert!(
(context.mesosystem_state_cached().work_social_conflict - 0.3).abs() < f64::EPSILON
);
}
#[test]
fn ecological_context_get_spillover() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.7;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.predictability = 0.3;
family.stability = 0.3;
context.add_microsystem(family_id.clone(), Microsystem::new_family(family));
let spillover = context.get_spillover(&work_id, &family_id);
assert!(spillover >= 0.0 && spillover <= 1.0);
}
#[test]
fn ecological_context_get_role_conflict() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
work.interaction_profile.interaction_frequency = 0.8;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.caregiving_burden = 0.8;
family.interaction_profile.interaction_frequency = 0.8;
context.add_microsystem(family_id.clone(), Microsystem::new_family(family));
let conflict = context.get_role_conflict(&work_id, &family_id);
assert!(conflict >= 0.0 && conflict <= 1.0);
}
#[test]
fn ecological_context_list_linkages() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
context.add_microsystem(
work_id.clone(),
Microsystem::new_work(WorkContext::default()),
);
context.add_microsystem(
family_id.clone(),
Microsystem::new_family(FamilyContext::default()),
);
let linkages = context.list_linkages();
assert_eq!(linkages.len(), 1);
}
#[test]
fn aggregate_social_warmth_no_social_microsystems() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
context.add_microsystem(work_id, Microsystem::new_work(WorkContext::default()));
let warmth = context.aggregate_social_warmth();
assert!((warmth - 0.5).abs() < f64::EPSILON);
}
#[test]
fn aggregate_social_warmth_with_social_microsystems() {
let mut context = EcologicalContext::default();
let social1_id = MicrosystemId::new("social1").unwrap();
let social2_id = MicrosystemId::new("social2").unwrap();
let mut social1 = SocialContext::default();
social1.warmth = 0.8;
let mut social2 = SocialContext::default();
social2.warmth = 0.4;
context.add_microsystem(social1_id, Microsystem::new_social(social1));
context.add_microsystem(social2_id, Microsystem::new_social(social2));
let warmth = context.aggregate_social_warmth();
assert!((warmth - 0.6).abs() < f64::EPSILON);
}
#[test]
fn aggregate_stress_empty_context() {
let context = EcologicalContext::default();
let stress = context.aggregate_stress();
assert!((stress - 0.0).abs() < f64::EPSILON);
}
#[test]
fn aggregate_stress_with_microsystems() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.8;
context.add_microsystem(work_id, Microsystem::new_work(work));
let stress = context.aggregate_stress();
assert!(stress > 0.0 && stress <= 1.0);
}
#[test]
fn aggregate_hostility_empty_context() {
let context = EcologicalContext::default();
let hostility = context.aggregate_hostility();
assert!((hostility - 0.0).abs() < f64::EPSILON);
}
#[test]
fn aggregate_hostility_with_microsystems() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.hostility = 0.7;
context.add_microsystem(work_id, Microsystem::new_work(work));
let hostility = context.aggregate_hostility();
assert!((hostility - 0.7).abs() < f64::EPSILON);
}
#[test]
fn compute_pad_bounds_defaults_without_family() {
let context = EcologicalContext::default();
let bounds = context.compute_pad_bounds();
assert!(bounds.valence_max <= 1.0 && bounds.valence_max >= 0.4);
assert!(bounds.arousal_max <= 1.0 && bounds.arousal_max >= 0.4);
assert!(bounds.dominance_max <= 1.0 && bounds.dominance_max >= 0.3);
}
#[test]
fn compute_pad_bounds_clamps_for_threat_and_restrictiveness() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.warmth = 0.1;
family.hostility = 0.9;
family.stability = 0.1;
family.predictability = 0.1;
context.add_microsystem(family_id, Microsystem::new_family(family));
context.exosystem.threat_level = 1.0;
context.exosystem.health_system_access = 0.0;
context.macrosystem.cultural_restrictiveness = 1.0;
let bounds = context.compute_pad_bounds();
assert!((bounds.dominance_max - 0.3).abs() < f32::EPSILON);
assert!((bounds.valence_max - 0.4).abs() < f32::EPSILON);
assert!((bounds.arousal_max - 0.4).abs() < f32::EPSILON);
}
#[test]
fn apply_person_to_context_shaping_high_extraversion() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.warmth = 0.5;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
context.apply_person_to_context_shaping(0.7, 0.0, 0.0, 0.0, 0.0);
let updated = context
.get_microsystem(&social_id)
.unwrap()
.social()
.unwrap();
assert!(updated.warmth > 0.5);
}
#[test]
fn apply_person_to_context_shaping_low_extraversion() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.warmth = 0.5;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
context.apply_person_to_context_shaping(0.2, 0.0, 0.0, 0.0, 0.0);
let updated = context
.get_microsystem(&social_id)
.unwrap()
.social()
.unwrap();
assert!((updated.warmth - 0.5).abs() < f64::EPSILON);
}
#[test]
fn apply_person_to_context_shaping_skips_non_social_microsystems() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
context.add_microsystem(work_id.clone(), Microsystem::new_work(WorkContext::default()));
context.apply_person_to_context_shaping(0.8, 0.0, 0.0, 0.0, 0.0);
let work = context.get_microsystem(&work_id).unwrap();
assert!(work.social().is_none());
}
#[test]
fn apply_person_to_context_modulation_boldness_increases_social_frequency() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.interaction_profile.interaction_frequency = 0.4;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
context.apply_person_to_context_modulation(0.8, 0.0, 0.0);
let updated = context
.get_microsystem(&social_id)
.unwrap()
.social()
.unwrap();
assert!(updated.interaction_profile.interaction_frequency > 0.4);
}
#[test]
fn apply_person_to_context_modulation_sensitivity_dampens_stimulation() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.cognitive_stimulation = 0.7;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
context.apply_person_to_context_modulation(0.0, 0.8, 0.0);
let updated = context
.get_microsystem(&work_id)
.unwrap()
.work()
.unwrap();
assert!(updated.cognitive_stimulation < 0.7);
}
#[test]
fn apply_demand_characteristics_shaping_increases_hostility_for_stigma() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.hostility = 0.3;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
let demand = DemandCharacteristics::new(
22,
ApparentGender::Female,
ApparentRace::Marginalized,
vec![VisibleTrait::StigmatizedAppearance],
);
context.apply_demand_characteristics_shaping(&demand);
let updated = context
.get_microsystem(&social_id)
.unwrap()
.social()
.unwrap();
assert!(updated.hostility > 0.3);
}
#[test]
fn high_conscientiousness_increases_work_clarity() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.role_clarity = 0.4;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
context.apply_person_to_context_shaping(0.0, 0.7, 0.0, 0.0, 0.0);
let updated = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!(updated.role_clarity > 0.4);
}
#[test]
fn conscientiousness_ignores_non_work_microsystems() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.warmth = 0.6;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
context.apply_person_to_context_shaping(0.0, 0.7, 0.0, 0.0, 0.0);
let updated = context
.get_microsystem(&social_id)
.unwrap()
.social()
.unwrap();
assert!((updated.warmth - 0.6).abs() < f64::EPSILON);
}
#[test]
fn high_neuroticism_reduces_stability_tolerance() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.stability = 0.8;
work.predictability = 0.8;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
context.apply_person_to_context_shaping(0.0, 0.0, 0.0, 0.7, 0.0);
let updated = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!(updated.stability < 0.8);
assert!(updated.predictability < 0.8);
}
#[test]
fn neuroticism_and_grievance_affect_family_context() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.stability = 0.8;
family.predictability = 0.8;
family.hostility = 0.2;
context.add_microsystem(family_id.clone(), Microsystem::new_family(family));
context.apply_person_to_context_shaping(0.0, 0.0, 0.0, 0.7, 0.8);
let updated = context
.get_microsystem(&family_id)
.unwrap()
.family()
.unwrap();
assert!(updated.stability < 0.8);
assert!(updated.predictability < 0.8);
assert!(updated.hostility > 0.2);
}
#[test]
fn high_agreeableness_increases_family_warmth() {
let mut context = EcologicalContext::default();
let family_id = MicrosystemId::new("family").unwrap();
let mut family = FamilyContext::default();
family.warmth = 0.4;
context.add_microsystem(family_id.clone(), Microsystem::new_family(family));
context.apply_person_to_context_shaping(0.0, 0.0, 0.7, 0.0, 0.0);
let updated = context
.get_microsystem(&family_id)
.unwrap()
.family()
.unwrap();
assert!(updated.warmth > 0.4);
}
#[test]
fn agreeableness_ignores_non_family_microsystems() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.role_clarity = 0.4;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
context.apply_person_to_context_shaping(0.0, 0.0, 0.7, 0.0, 0.0);
let updated = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!((updated.role_clarity - 0.4).abs() < f64::EPSILON);
}
#[test]
fn high_grievance_increases_perceived_hostility() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let social_id = MicrosystemId::new("social").unwrap();
let mut work = WorkContext::default();
work.hostility = 0.2;
let mut social = SocialContext::default();
social.hostility = 0.2;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
context.apply_person_to_context_shaping(0.0, 0.0, 0.0, 0.0, 0.8);
let work_updated = context.get_microsystem(&work_id).unwrap().work().unwrap();
let social_updated = context
.get_microsystem(&social_id)
.unwrap()
.social()
.unwrap();
assert!(work_updated.hostility > 0.2);
assert!(social_updated.hostility > 0.2);
}
#[test]
fn compute_context_to_person_effects_high_stress() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.workload_stress = 0.9;
context.add_microsystem(work_id, Microsystem::new_work(work));
let (stress_adj, loneliness_adj) = context.compute_context_to_person_effects(0.5);
assert!(stress_adj > 0.0);
assert!(loneliness_adj >= 0.0);
}
#[test]
fn low_relationship_quality_increases_context_hostility() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.hostility = 0.8;
context.add_microsystem(work_id, Microsystem::new_work(work));
let (high_quality_stress, _) = context.compute_context_to_person_effects(0.9);
let (low_quality_stress, _) = context.compute_context_to_person_effects(0.2);
assert!(low_quality_stress > high_quality_stress);
}
#[test]
fn compute_context_to_person_effects_empty_context() {
let context = EcologicalContext::default();
let (stress_adj, loneliness_adj) = context.compute_context_to_person_effects(0.5);
assert!((stress_adj - 0.0).abs() < 0.01);
assert!(loneliness_adj < 0.05);
}
#[test]
fn ecological_context_entity_integration() {
use crate::entity::EntityBuilder;
use crate::enums::Species;
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let context = entity.context();
assert!(context.is_empty()); assert_eq!(context.microsystem_count(), 0);
let _ = context.exosystem();
let _ = context.macrosystem();
let _ = context.chronosystem();
let _ = context.mesosystem_cache();
let mut entity = entity;
let work_id = MicrosystemId::new("work_test").unwrap();
entity.context_mut().add_microsystem(
work_id.clone(),
Microsystem::new_work(WorkContext::default()),
);
assert_eq!(entity.context().microsystem_count(), 1);
assert!(entity.context().get_microsystem(&work_id).is_some());
}
#[test]
fn person_modulation_boosts_social_and_work_with_boldness() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let work_id = MicrosystemId::new("work").unwrap();
let mut social = SocialContext::default();
social.interaction_profile.interaction_frequency = 0.4;
social.group_standing = 0.4;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
let mut work = WorkContext::default();
work.cognitive_stimulation = 0.4;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
context.apply_person_to_context_modulation(0.8, 0.0, 0.0);
let updated_social = context.get_microsystem(&social_id).unwrap().social().unwrap();
assert!(updated_social.interaction_profile.interaction_frequency > 0.4);
assert!(updated_social.group_standing > 0.4);
let updated_work = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!(updated_work.cognitive_stimulation > 0.4);
}
#[test]
fn person_modulation_increases_warmth_and_reciprocity_with_sociability() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let mut social = SocialContext::default();
social.warmth = 0.4;
social.interaction_profile.reciprocity_balance = 0.4;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
context.apply_person_to_context_modulation(0.0, 0.0, 0.8);
let updated_social = context.get_microsystem(&social_id).unwrap().social().unwrap();
assert!(updated_social.warmth > 0.4);
assert!(updated_social.interaction_profile.reciprocity_balance > 0.4);
}
#[test]
fn person_modulation_sociability_ignores_non_social_microsystems() {
let mut context = EcologicalContext::default();
let work_id = MicrosystemId::new("work").unwrap();
let mut work = WorkContext::default();
work.predictability = 0.6;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
context.apply_person_to_context_modulation(0.0, 0.0, 0.8);
let updated_work = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!((updated_work.predictability - 0.6).abs() < f64::EPSILON);
}
#[test]
fn person_modulation_sensitivity_dampens_social_work_and_family() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let work_id = MicrosystemId::new("work").unwrap();
let family_id = MicrosystemId::new("family").unwrap();
let mut social = SocialContext::default();
social.hostility = 0.6;
social.interaction_profile.interaction_frequency = 0.6;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
let mut work = WorkContext::default();
work.cognitive_stimulation = 0.7;
work.predictability = 0.4;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let mut family = FamilyContext::default();
family.hostility = 0.6;
family.predictability = 0.4;
context.add_microsystem(family_id.clone(), Microsystem::new_family(family));
context.apply_person_to_context_modulation(0.0, 0.8, 0.0);
let updated_social = context.get_microsystem(&social_id).unwrap().social().unwrap();
assert!(updated_social.hostility < 0.6);
assert!(updated_social.interaction_profile.interaction_frequency < 0.6);
let updated_work = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!(updated_work.cognitive_stimulation < 0.7);
assert!(updated_work.predictability > 0.4);
let updated_family = context.get_microsystem(&family_id).unwrap().family().unwrap();
assert!(updated_family.hostility < 0.6);
assert!(updated_family.predictability > 0.4);
}
#[test]
fn demand_characteristics_negative_bias_applies() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let work_id = MicrosystemId::new("work").unwrap();
let neighborhood_id = MicrosystemId::new("neighborhood").unwrap();
let mut social = SocialContext::default();
social.hostility = 0.2;
social.warmth = 0.6;
social.group_standing = 0.5;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
let mut work = WorkContext::default();
work.hostility = 0.1;
work.warmth = 0.6;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let mut neighborhood = NeighborhoodContext::default();
neighborhood.hostility = 0.1;
neighborhood.warmth = 0.7;
context.add_microsystem(neighborhood_id.clone(), Microsystem::new_neighborhood(neighborhood));
let demand = DemandCharacteristics::new(
28,
ApparentGender::NonBinary,
ApparentRace::Marginalized,
vec![
VisibleTrait::StigmatizedAppearance,
VisibleTrait::ApparentPoverty,
VisibleTrait::VisibleDisability,
],
);
context.apply_demand_characteristics_shaping(&demand);
let updated_social = context.get_microsystem(&social_id).unwrap().social().unwrap();
assert!(updated_social.hostility > 0.2);
assert!(updated_social.warmth < 0.6);
assert!(updated_social.group_standing < 0.5);
let updated_work = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!(updated_work.hostility > 0.1);
assert!(updated_work.warmth < 0.6);
let updated_neighborhood = context
.get_microsystem(&neighborhood_id)
.unwrap()
.neighborhood()
.unwrap();
assert!(updated_neighborhood.hostility > 0.1);
assert!(updated_neighborhood.warmth < 0.7);
}
#[test]
fn demand_characteristics_positive_bias_applies() {
let mut context = EcologicalContext::default();
let social_id = MicrosystemId::new("social").unwrap();
let work_id = MicrosystemId::new("work").unwrap();
let mut social = SocialContext::default();
social.warmth = 0.4;
social.group_standing = 0.4;
context.add_microsystem(social_id.clone(), Microsystem::new_social(social));
let mut work = WorkContext::default();
work.warmth = 0.4;
context.add_microsystem(work_id.clone(), Microsystem::new_work(work));
let demand = DemandCharacteristics::new(
35,
ApparentGender::Male,
ApparentRace::Privileged,
vec![VisibleTrait::AttractivePresentation],
);
context.apply_demand_characteristics_shaping(&demand);
let updated_social = context.get_microsystem(&social_id).unwrap().social().unwrap();
assert!(updated_social.warmth > 0.4);
assert!(updated_social.group_standing > 0.4);
let updated_work = context.get_microsystem(&work_id).unwrap().work().unwrap();
assert!(updated_work.warmth > 0.4);
}
#[test]
fn demand_characteristics_positive_bias_skips_neighborhood_changes() {
let mut context = EcologicalContext::default();
let neighborhood_id = MicrosystemId::new("neighborhood").unwrap();
let mut neighborhood = NeighborhoodContext::default();
neighborhood.hostility = 0.2;
neighborhood.warmth = 0.7;
context.add_microsystem(neighborhood_id.clone(), Microsystem::new_neighborhood(neighborhood));
let demand = DemandCharacteristics::new(
40,
ApparentGender::Male,
ApparentRace::Privileged,
vec![VisibleTrait::AttractivePresentation],
);
context.apply_demand_characteristics_shaping(&demand);
let updated_neighborhood = context
.get_microsystem(&neighborhood_id)
.unwrap()
.neighborhood()
.unwrap();
assert!((updated_neighborhood.hostility - 0.2).abs() < f64::EPSILON);
assert!((updated_neighborhood.warmth - 0.7).abs() < f64::EPSILON);
}
}