use super::chronosystem::{ChronosystemContext, CulturalShift};
use crate::enums::{MacrosystemPath, RelationshipSchema};
use crate::types::Timestamp;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub struct CulturalOrientation {
pub individualism_collectivism: f64,
pub power_distance: f64,
pub uncertainty_avoidance: f64,
}
impl Default for CulturalOrientation {
fn default() -> Self {
CulturalOrientation {
individualism_collectivism: 0.0, power_distance: 0.5,
uncertainty_avoidance: 0.5,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct InstitutionalStructure {
pub rule_of_law: f64,
pub social_mobility: f64,
pub corruption_level: f64,
}
impl Default for InstitutionalStructure {
fn default() -> Self {
InstitutionalStructure {
rule_of_law: 0.6,
social_mobility: 0.5,
corruption_level: 0.3,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MacrosystemConstraintSet {
pub allowed_relationship_schemas: Vec<RelationshipSchema>,
pub hierarchy_violation_penalty: f64,
}
impl MacrosystemConstraintSet {
#[must_use]
pub fn allows_schema(&self, schema: RelationshipSchema) -> bool {
self.allowed_relationship_schemas.contains(&schema)
}
}
impl Default for MacrosystemConstraintSet {
fn default() -> Self {
MacrosystemConstraintSet {
allowed_relationship_schemas: RelationshipSchema::all().to_vec(),
hierarchy_violation_penalty: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct MacrosystemModifier {
pub individualism_collectivism: Option<f64>,
pub power_distance: Option<f64>,
pub uncertainty_avoidance: Option<f64>,
pub rule_of_law: Option<f64>,
pub social_mobility: Option<f64>,
pub corruption_level: Option<f64>,
pub cultural_stress: Option<f64>,
pub collective_trauma: Option<f64>,
pub economic_inequality: Option<f64>,
pub cultural_restrictiveness: Option<f64>,
}
impl MacrosystemModifier {
#[must_use]
pub fn apply_to(&self, base: &MacrosystemContext) -> MacrosystemContext {
let mut modified = base.clone();
if let Some(value) = self.individualism_collectivism {
modified.cultural_orientation.individualism_collectivism = value.clamp(-1.0, 1.0);
}
if let Some(value) = self.power_distance {
modified.cultural_orientation.power_distance = value.clamp(0.0, 1.0);
}
if let Some(value) = self.uncertainty_avoidance {
modified.cultural_orientation.uncertainty_avoidance = value.clamp(0.0, 1.0);
}
if let Some(value) = self.rule_of_law {
modified.institutional_structure.rule_of_law = value.clamp(0.0, 1.0);
}
if let Some(value) = self.social_mobility {
modified.institutional_structure.social_mobility = value.clamp(0.0, 1.0);
}
if let Some(value) = self.corruption_level {
modified.institutional_structure.corruption_level = value.clamp(0.0, 1.0);
}
if let Some(value) = self.cultural_stress {
modified.cultural_stress = value.clamp(0.0, 1.0);
}
if let Some(value) = self.collective_trauma {
modified.collective_trauma = value.clamp(0.0, 1.0);
}
if let Some(value) = self.economic_inequality {
modified.economic_inequality = value.clamp(0.0, 1.0);
}
if let Some(value) = self.cultural_restrictiveness {
modified.cultural_restrictiveness = value.clamp(0.0, 1.0);
}
modified
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MacrosystemContext {
pub cultural_orientation: CulturalOrientation,
pub institutional_structure: InstitutionalStructure,
pub cultural_stress: f64,
pub collective_trauma: f64,
pub economic_inequality: f64,
pub cultural_restrictiveness: f64,
pub subculture_overrides: HashMap<String, MacrosystemModifier>,
}
impl Default for MacrosystemContext {
fn default() -> Self {
MacrosystemContext {
cultural_orientation: CulturalOrientation::default(),
institutional_structure: InstitutionalStructure::default(),
cultural_stress: 0.2,
collective_trauma: 0.1,
economic_inequality: 0.4,
cultural_restrictiveness: 0.2,
subculture_overrides: HashMap::new(),
}
}
}
impl MacrosystemContext {
#[must_use]
pub fn new() -> Self {
MacrosystemContext::default()
}
#[must_use]
pub fn for_subculture(&self, key: &str) -> MacrosystemContext {
self.subculture_overrides
.get(key)
.map(|modifier| modifier.apply_to(self))
.unwrap_or_else(|| self.clone())
}
#[must_use]
pub fn get_value(&self, path: &MacrosystemPath) -> f64 {
match path {
MacrosystemPath::IndividualismCollectivism => {
self.cultural_orientation.individualism_collectivism
}
MacrosystemPath::PowerDistance => self.cultural_orientation.power_distance,
MacrosystemPath::UncertaintyAvoidance => {
self.cultural_orientation.uncertainty_avoidance
}
MacrosystemPath::CulturalStress => self.cultural_stress,
MacrosystemPath::CollectiveTrauma => self.collective_trauma,
MacrosystemPath::EconomicInequality => self.economic_inequality,
MacrosystemPath::CulturalRestrictiveness => self.cultural_restrictiveness,
MacrosystemPath::RuleOfLaw => self.institutional_structure.rule_of_law,
MacrosystemPath::SocialMobility => self.institutional_structure.social_mobility,
MacrosystemPath::CorruptionLevel => self.institutional_structure.corruption_level,
}
}
pub fn set_value(&mut self, path: &MacrosystemPath, value: f64) {
match path {
MacrosystemPath::IndividualismCollectivism => {
self.cultural_orientation.individualism_collectivism = value.clamp(-1.0, 1.0);
}
MacrosystemPath::PowerDistance => {
self.cultural_orientation.power_distance = value.clamp(0.0, 1.0);
}
MacrosystemPath::UncertaintyAvoidance => {
self.cultural_orientation.uncertainty_avoidance = value.clamp(0.0, 1.0);
}
MacrosystemPath::CulturalStress => {
self.cultural_stress = value.clamp(0.0, 1.0);
}
MacrosystemPath::CollectiveTrauma => {
self.collective_trauma = value.clamp(0.0, 1.0);
}
MacrosystemPath::EconomicInequality => {
self.economic_inequality = value.clamp(0.0, 1.0);
}
MacrosystemPath::CulturalRestrictiveness => {
self.cultural_restrictiveness = value.clamp(0.0, 1.0);
}
MacrosystemPath::RuleOfLaw => {
self.institutional_structure.rule_of_law = value.clamp(0.0, 1.0);
}
MacrosystemPath::SocialMobility => {
self.institutional_structure.social_mobility = value.clamp(0.0, 1.0);
}
MacrosystemPath::CorruptionLevel => {
self.institutional_structure.corruption_level = value.clamp(0.0, 1.0);
}
}
}
#[must_use]
pub fn interpolate_for_timestamp(
&self,
chronosystem: &ChronosystemContext,
timestamp: Timestamp,
) -> MacrosystemContext {
let mut snapshot = self.clone();
let mut shifts: Vec<&CulturalShift> = chronosystem.cultural_shifts().iter().collect();
shifts.sort_by_key(|shift| shift.start_timestamp);
for shift in shifts {
if timestamp < shift.start_timestamp {
continue;
}
let elapsed_days = (timestamp - shift.start_timestamp).as_days() as f64;
let progress = (elapsed_days / 1825.0).clamp(0.0, 1.0);
for (path, target) in shift.target_values.iter() {
let base_value = snapshot.get_value(path);
let interpolated = base_value + (target - base_value) * progress;
snapshot.set_value(path, interpolated);
}
}
snapshot
}
#[must_use]
pub fn is_individualist(&self) -> bool {
self.cultural_orientation.individualism_collectivism > 0.3
}
#[must_use]
pub fn is_collectivist(&self) -> bool {
self.cultural_orientation.individualism_collectivism < -0.3
}
#[must_use]
pub fn is_high_power_distance(&self) -> bool {
self.cultural_orientation.power_distance > 0.6
}
#[must_use]
pub fn constraint_set(&self) -> MacrosystemConstraintSet {
let power_distance = self.cultural_orientation.power_distance;
if power_distance > 0.6 {
MacrosystemConstraintSet {
allowed_relationship_schemas: vec![
RelationshipSchema::Mentor,
RelationshipSchema::Subordinate,
RelationshipSchema::Family,
RelationshipSchema::Nuclear,
RelationshipSchema::Extended,
],
hierarchy_violation_penalty: power_distance * 0.2,
}
} else {
MacrosystemConstraintSet {
allowed_relationship_schemas: vec![
RelationshipSchema::Peer,
RelationshipSchema::Mentor,
RelationshipSchema::Romantic,
RelationshipSchema::Family,
RelationshipSchema::Nuclear,
RelationshipSchema::Extended,
],
hierarchy_violation_penalty: 0.0,
}
}
}
#[must_use]
pub fn autonomy_need_weight(&self) -> f64 {
let ic = self.cultural_orientation.individualism_collectivism;
if ic > 0.3 {
1.0 + ic * 0.3
} else {
1.0 + ic * 0.2
}
}
#[must_use]
pub fn belonging_need_weight(&self) -> f64 {
let ic = self.cultural_orientation.individualism_collectivism;
if ic > 0.3 {
1.0 - ic * 0.2
} else {
1.0 - ic * 0.3
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::{ChronosystemContext, CulturalShift};
use crate::enums::MacrosystemPath;
use crate::types::{Duration, Timestamp};
#[test]
fn cultural_orientation_default() {
let co = CulturalOrientation::default();
assert!((co.individualism_collectivism - 0.0).abs() < f64::EPSILON);
assert!((co.power_distance - 0.5).abs() < f64::EPSILON);
assert!((co.uncertainty_avoidance - 0.5).abs() < f64::EPSILON);
}
#[test]
fn cultural_orientation_clone_eq() {
let co1 = CulturalOrientation::default();
let co2 = co1.clone();
assert_eq!(co1, co2);
}
#[test]
fn institutional_structure_default() {
let is = InstitutionalStructure::default();
assert!((is.rule_of_law - 0.6).abs() < f64::EPSILON);
assert!((is.social_mobility - 0.5).abs() < f64::EPSILON);
assert!((is.corruption_level - 0.3).abs() < f64::EPSILON);
}
#[test]
fn institutional_structure_clone_eq() {
let is1 = InstitutionalStructure::default();
let is2 = is1.clone();
assert_eq!(is1, is2);
}
#[test]
fn macrosystem_context_default() {
let macro_ctx = MacrosystemContext::default();
assert!((macro_ctx.cultural_stress - 0.2).abs() < f64::EPSILON);
assert!((macro_ctx.collective_trauma - 0.1).abs() < f64::EPSILON);
assert!((macro_ctx.economic_inequality - 0.4).abs() < f64::EPSILON);
assert!((macro_ctx.cultural_restrictiveness - 0.2).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_context_new() {
let macro_ctx = MacrosystemContext::new();
assert!((macro_ctx.cultural_stress - 0.2).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_interpolate_skips_future_shifts() {
let macro_ctx = MacrosystemContext::default();
let mut chronosystem = ChronosystemContext::default();
let timestamp = Timestamp::from_ymd_hms(2024, 1, 1, 0, 0, 0);
let shift_start = timestamp + Duration::days(30);
let shift = CulturalShift::new(shift_start)
.with_target(MacrosystemPath::CulturalStress, 0.9);
chronosystem.add_cultural_shift(shift);
let baseline = macro_ctx.get_value(&MacrosystemPath::CulturalStress);
let snapshot = macro_ctx.interpolate_for_timestamp(&chronosystem, timestamp);
assert!((snapshot.get_value(&MacrosystemPath::CulturalStress) - baseline).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_subculture_override_applies() {
let mut macro_ctx = MacrosystemContext::default();
let mut modifier = MacrosystemModifier::default();
modifier.power_distance = Some(0.9);
modifier.cultural_stress = Some(0.75);
macro_ctx
.subculture_overrides
.insert("subculture_a".to_string(), modifier);
let adjusted = macro_ctx.for_subculture("subculture_a");
assert!((adjusted.cultural_orientation.power_distance - 0.9).abs() < f64::EPSILON);
assert!((adjusted.cultural_stress - 0.75).abs() < f64::EPSILON);
let baseline = macro_ctx.for_subculture("missing");
assert_eq!(baseline, macro_ctx);
}
#[test]
fn macrosystem_get_value_all_paths() {
let macro_ctx = MacrosystemContext::default();
for path in MacrosystemPath::all() {
let value = macro_ctx.get_value(&path);
if matches!(path, MacrosystemPath::IndividualismCollectivism) {
assert!(value >= -1.0 && value <= 1.0);
} else {
assert!(value >= 0.0 && value <= 1.0);
}
}
}
#[test]
fn macrosystem_set_value() {
let mut macro_ctx = MacrosystemContext::default();
macro_ctx.set_value(&MacrosystemPath::CulturalStress, 0.8);
assert!((macro_ctx.cultural_stress - 0.8).abs() < f64::EPSILON);
macro_ctx.set_value(&MacrosystemPath::CulturalRestrictiveness, 0.65);
assert!((macro_ctx.cultural_restrictiveness - 0.65).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_clamped() {
let mut macro_ctx = MacrosystemContext::default();
macro_ctx.set_value(&MacrosystemPath::CulturalStress, 1.5);
assert!((macro_ctx.cultural_stress - 1.0).abs() < f64::EPSILON);
macro_ctx.set_value(&MacrosystemPath::CulturalStress, -0.5);
assert!((macro_ctx.cultural_stress - 0.0).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_individualism_clamped() {
let mut macro_ctx = MacrosystemContext::default();
macro_ctx.set_value(&MacrosystemPath::IndividualismCollectivism, 1.5);
assert!(
(macro_ctx.cultural_orientation.individualism_collectivism - 1.0).abs() < f64::EPSILON
);
macro_ctx.set_value(&MacrosystemPath::IndividualismCollectivism, -1.5);
assert!(
(macro_ctx.cultural_orientation.individualism_collectivism - (-1.0)).abs()
< f64::EPSILON
);
}
#[test]
fn macrosystem_set_all_paths() {
let mut macro_ctx = MacrosystemContext::default();
for path in MacrosystemPath::all() {
macro_ctx.set_value(&path, 0.75);
}
assert!((macro_ctx.cultural_stress - 0.75).abs() < f64::EPSILON);
assert!((macro_ctx.collective_trauma - 0.75).abs() < f64::EPSILON);
assert!(
(macro_ctx.cultural_orientation.individualism_collectivism - 0.75).abs() < f64::EPSILON
);
assert!((macro_ctx.cultural_orientation.power_distance - 0.75).abs() < f64::EPSILON);
assert!((macro_ctx.cultural_restrictiveness - 0.75).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_is_individualist() {
let mut macro_ctx = MacrosystemContext::default();
assert!(!macro_ctx.is_individualist());
macro_ctx.cultural_orientation.individualism_collectivism = 0.5;
assert!(macro_ctx.is_individualist());
macro_ctx.cultural_orientation.individualism_collectivism = -0.5;
assert!(!macro_ctx.is_individualist());
}
#[test]
fn macrosystem_is_collectivist() {
let mut macro_ctx = MacrosystemContext::default();
assert!(!macro_ctx.is_collectivist());
macro_ctx.cultural_orientation.individualism_collectivism = -0.5;
assert!(macro_ctx.is_collectivist());
macro_ctx.cultural_orientation.individualism_collectivism = 0.5;
assert!(!macro_ctx.is_collectivist());
}
#[test]
fn macrosystem_is_high_power_distance() {
let mut macro_ctx = MacrosystemContext::default();
assert!(!macro_ctx.is_high_power_distance());
macro_ctx.cultural_orientation.power_distance = 0.8;
assert!(macro_ctx.is_high_power_distance());
}
#[test]
fn macrosystem_autonomy_need_weight_individualist() {
let mut macro_ctx = MacrosystemContext::default();
macro_ctx.cultural_orientation.individualism_collectivism = 0.7;
let weight = macro_ctx.autonomy_need_weight();
assert!(weight > 1.2);
}
#[test]
fn macrosystem_autonomy_need_weight_collectivist() {
let mut macro_ctx = MacrosystemContext::default();
macro_ctx.cultural_orientation.individualism_collectivism = -0.7;
let weight = macro_ctx.autonomy_need_weight();
assert!(weight < 0.9);
}
#[test]
fn macrosystem_belonging_need_weight_individualist() {
let mut macro_ctx = MacrosystemContext::default();
macro_ctx.cultural_orientation.individualism_collectivism = 0.7;
let weight = macro_ctx.belonging_need_weight();
assert!(weight < 0.9);
}
#[test]
fn macrosystem_belonging_need_weight_collectivist() {
let mut macro_ctx = MacrosystemContext::default();
macro_ctx.cultural_orientation.individualism_collectivism = -0.7;
let weight = macro_ctx.belonging_need_weight();
assert!(weight > 1.2);
}
#[test]
fn macrosystem_clone_eq() {
let macro1 = MacrosystemContext::default();
let macro2 = macro1.clone();
assert_eq!(macro1, macro2);
}
#[test]
fn macrosystem_debug() {
let macro_ctx = MacrosystemContext::default();
let debug = format!("{:?}", macro_ctx);
assert!(debug.contains("MacrosystemContext"));
}
#[test]
fn macrosystem_path_get_cultural_orientation() {
let macro_ctx = MacrosystemContext::default();
assert!(
(macro_ctx.get_value(&MacrosystemPath::IndividualismCollectivism) - 0.0).abs()
< f64::EPSILON
);
assert!((macro_ctx.get_value(&MacrosystemPath::PowerDistance) - 0.5).abs() < f64::EPSILON);
assert!(
(macro_ctx.get_value(&MacrosystemPath::UncertaintyAvoidance) - 0.5).abs()
< f64::EPSILON
);
}
#[test]
fn macrosystem_path_get_institutional() {
let macro_ctx = MacrosystemContext::default();
assert!((macro_ctx.get_value(&MacrosystemPath::RuleOfLaw) - 0.6).abs() < f64::EPSILON);
assert!((macro_ctx.get_value(&MacrosystemPath::SocialMobility) - 0.5).abs() < f64::EPSILON);
assert!(
(macro_ctx.get_value(&MacrosystemPath::CorruptionLevel) - 0.3).abs() < f64::EPSILON
);
}
#[test]
fn macrosystem_path_get_societal() {
let macro_ctx = MacrosystemContext::default();
assert!((macro_ctx.get_value(&MacrosystemPath::CulturalStress) - 0.2).abs() < f64::EPSILON);
assert!(
(macro_ctx.get_value(&MacrosystemPath::CollectiveTrauma) - 0.1).abs() < f64::EPSILON
);
assert!(
(macro_ctx.get_value(&MacrosystemPath::EconomicInequality) - 0.4).abs() < f64::EPSILON
);
assert!(
(macro_ctx.get_value(&MacrosystemPath::CulturalRestrictiveness) - 0.2).abs()
< f64::EPSILON
);
}
#[test]
fn macrosystem_creation_default() {
let macro_ctx = MacrosystemContext::default();
assert!(
(macro_ctx.cultural_orientation.individualism_collectivism - 0.0).abs() < f64::EPSILON
);
assert!((macro_ctx.cultural_orientation.power_distance - 0.5).abs() < f64::EPSILON);
assert!((macro_ctx.cultural_orientation.uncertainty_avoidance - 0.5).abs() < f64::EPSILON);
assert!((macro_ctx.institutional_structure.rule_of_law - 0.6).abs() < f64::EPSILON);
assert!((macro_ctx.institutional_structure.social_mobility - 0.5).abs() < f64::EPSILON);
assert!((macro_ctx.institutional_structure.corruption_level - 0.3).abs() < f64::EPSILON);
assert!((macro_ctx.cultural_stress - 0.2).abs() < f64::EPSILON);
assert!((macro_ctx.collective_trauma - 0.1).abs() < f64::EPSILON);
assert!((macro_ctx.economic_inequality - 0.4).abs() < f64::EPSILON);
assert!((macro_ctx.cultural_restrictiveness - 0.2).abs() < f64::EPSILON);
let macro_new = MacrosystemContext::new();
assert_eq!(macro_ctx, macro_new);
}
#[test]
fn macrosystem_cultural_constraint_application() {
let mut high_pd = MacrosystemContext::default();
high_pd.cultural_orientation.power_distance = 0.9;
let mut low_pd = MacrosystemContext::default();
low_pd.cultural_orientation.power_distance = 0.2;
assert!(high_pd.is_high_power_distance());
assert!(!low_pd.is_high_power_distance());
assert!(high_pd.cultural_orientation.power_distance > 0.6);
assert!(low_pd.cultural_orientation.power_distance < 0.6);
let mut individualist = MacrosystemContext::default();
individualist
.cultural_orientation
.individualism_collectivism = 0.8;
let mut collectivist = MacrosystemContext::default();
collectivist.cultural_orientation.individualism_collectivism = -0.8;
assert!(individualist.is_individualist());
assert!(!individualist.is_collectivist());
assert!(collectivist.is_collectivist());
assert!(!collectivist.is_individualist());
}
#[test]
fn macrosystem_constraint_set_respects_power_distance() {
let mut high_pd = MacrosystemContext::default();
high_pd.cultural_orientation.power_distance = 0.9;
let high_constraints = high_pd.constraint_set();
assert!(high_constraints.allows_schema(RelationshipSchema::Subordinate));
assert!(!high_constraints.allows_schema(RelationshipSchema::Romantic));
assert!(high_constraints.hierarchy_violation_penalty > 0.0);
let mut low_pd = MacrosystemContext::default();
low_pd.cultural_orientation.power_distance = 0.2;
let low_constraints = low_pd.constraint_set();
assert!(low_constraints.allows_schema(RelationshipSchema::Romantic));
assert!(!low_constraints.allows_schema(RelationshipSchema::Subordinate));
assert!((low_constraints.hierarchy_violation_penalty - 0.0).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_power_distance_modifies_compliance() {
let mut high_pd = MacrosystemContext::default();
high_pd.cultural_orientation.power_distance = 0.85;
let mut low_pd = MacrosystemContext::default();
low_pd.cultural_orientation.power_distance = 0.15;
assert!(high_pd.is_high_power_distance());
assert!(!low_pd.is_high_power_distance());
let high_pd_value = high_pd.get_value(&MacrosystemPath::PowerDistance);
let low_pd_value = low_pd.get_value(&MacrosystemPath::PowerDistance);
assert!((high_pd_value - 0.85).abs() < f64::EPSILON);
assert!((low_pd_value - 0.15).abs() < f64::EPSILON);
assert!(high_pd_value - low_pd_value > 0.5);
}
#[test]
fn macrosystem_collectivism_modifies_belonging_weight() {
let mut individualist = MacrosystemContext::default();
individualist
.cultural_orientation
.individualism_collectivism = 0.7;
let mut collectivist = MacrosystemContext::default();
collectivist.cultural_orientation.individualism_collectivism = -0.7;
let individualist_belonging = individualist.belonging_need_weight();
let collectivist_belonging = collectivist.belonging_need_weight();
assert!(collectivist_belonging > individualist_belonging);
assert!((individualist_belonging - 0.86).abs() < 0.01);
assert!((collectivist_belonging - 1.21).abs() < 0.01);
let individualist_autonomy = individualist.autonomy_need_weight();
let collectivist_autonomy = collectivist.autonomy_need_weight();
assert!(individualist_autonomy > collectivist_autonomy);
assert!((individualist_autonomy - 1.21).abs() < 0.01);
assert!((collectivist_autonomy - 0.86).abs() < 0.01);
}
#[test]
fn macrosystem_constraint_set_allows_schema() {
let constraint = MacrosystemConstraintSet {
allowed_relationship_schemas: vec![
RelationshipSchema::Peer,
RelationshipSchema::Mentor,
],
hierarchy_violation_penalty: 0.1,
};
assert!(constraint.allows_schema(RelationshipSchema::Peer));
assert!(constraint.allows_schema(RelationshipSchema::Mentor));
assert!(!constraint.allows_schema(RelationshipSchema::Romantic));
}
#[test]
fn macrosystem_constraint_set_default() {
let constraint = MacrosystemConstraintSet::default();
assert!(constraint.allows_schema(RelationshipSchema::Peer));
assert!(constraint.allows_schema(RelationshipSchema::Mentor));
assert!(constraint.allows_schema(RelationshipSchema::Romantic));
assert!(constraint.allows_schema(RelationshipSchema::Family));
assert!(constraint.allows_schema(RelationshipSchema::Nuclear));
assert!(constraint.allows_schema(RelationshipSchema::Extended));
assert!(constraint.allows_schema(RelationshipSchema::Subordinate));
assert!((constraint.hierarchy_violation_penalty - 0.0).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_modifier_apply_to_partial_overrides() {
let base = MacrosystemContext::default();
let mut modifier = MacrosystemModifier::default();
modifier.power_distance = Some(0.8);
modifier.cultural_stress = Some(0.75);
let modified = modifier.apply_to(&base);
assert!((modified.cultural_orientation.power_distance - 0.8).abs() < f64::EPSILON);
assert!((modified.cultural_stress - 0.75).abs() < f64::EPSILON);
assert_eq!(
modified.cultural_orientation.individualism_collectivism,
base.cultural_orientation.individualism_collectivism
);
assert_eq!(modified.collective_trauma, base.collective_trauma);
}
#[test]
fn macrosystem_modifier_preserves_power_distance_when_none() {
let base = MacrosystemContext::default();
let mut modifier = MacrosystemModifier::default();
modifier.cultural_stress = Some(0.42);
let modified = modifier.apply_to(&base);
assert_eq!(
modified.cultural_orientation.power_distance,
base.cultural_orientation.power_distance
);
}
#[test]
fn macrosystem_modifier_apply_to_all_fields() {
let base = MacrosystemContext::default();
let mut modifier = MacrosystemModifier::default();
modifier.individualism_collectivism = Some(0.5);
modifier.power_distance = Some(0.8);
modifier.uncertainty_avoidance = Some(0.7);
modifier.rule_of_law = Some(0.9);
modifier.social_mobility = Some(0.4);
modifier.corruption_level = Some(0.2);
modifier.cultural_stress = Some(0.6);
modifier.collective_trauma = Some(0.3);
modifier.economic_inequality = Some(0.5);
modifier.cultural_restrictiveness = Some(0.7);
let modified = modifier.apply_to(&base);
assert!(
(modified.cultural_orientation.individualism_collectivism - 0.5).abs() < f64::EPSILON
);
assert!((modified.cultural_orientation.power_distance - 0.8).abs() < f64::EPSILON);
assert!((modified.cultural_orientation.uncertainty_avoidance - 0.7).abs() < f64::EPSILON);
assert!((modified.institutional_structure.rule_of_law - 0.9).abs() < f64::EPSILON);
assert!((modified.institutional_structure.social_mobility - 0.4).abs() < f64::EPSILON);
assert!((modified.institutional_structure.corruption_level - 0.2).abs() < f64::EPSILON);
assert!((modified.cultural_stress - 0.6).abs() < f64::EPSILON);
assert!((modified.collective_trauma - 0.3).abs() < f64::EPSILON);
assert!((modified.economic_inequality - 0.5).abs() < f64::EPSILON);
assert!((modified.cultural_restrictiveness - 0.7).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_modifier_apply_to_with_clamping() {
let base = MacrosystemContext::default();
let mut modifier = MacrosystemModifier::default();
modifier.individualism_collectivism = Some(1.5); modifier.power_distance = Some(-0.5); modifier.cultural_stress = Some(2.0);
let modified = modifier.apply_to(&base);
assert!(
(modified.cultural_orientation.individualism_collectivism - 1.0).abs() < f64::EPSILON
);
assert!((modified.cultural_orientation.power_distance - 0.0).abs() < f64::EPSILON);
assert!((modified.cultural_stress - 1.0).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_constraint_set_low_power_distance_allows_peer() {
let mut low_pd = MacrosystemContext::default();
low_pd.cultural_orientation.power_distance = 0.4;
let constraint = low_pd.constraint_set();
assert!(constraint.allows_schema(RelationshipSchema::Peer));
assert!((constraint.hierarchy_violation_penalty - 0.0).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_constraint_set_high_power_distance_no_peer() {
let mut high_pd = MacrosystemContext::default();
high_pd.cultural_orientation.power_distance = 0.75;
let constraint = high_pd.constraint_set();
assert!(!constraint.allows_schema(RelationshipSchema::Peer));
assert!(constraint.hierarchy_violation_penalty > 0.0);
assert!((constraint.hierarchy_violation_penalty - 0.15).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_autonomy_need_weight_neutral_individualism() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.0;
let weight = ctx.autonomy_need_weight();
assert!((weight - 1.0).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_autonomy_need_weight_slight_collectivist() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = -0.5;
let weight = ctx.autonomy_need_weight();
assert!((weight - 0.9).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_autonomy_need_weight_individualist_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.3;
let weight = ctx.autonomy_need_weight();
assert!(weight > 1.0);
}
#[test]
fn macrosystem_belonging_need_weight_neutral_individualism() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.0;
let weight = ctx.belonging_need_weight();
assert!((weight - 1.0).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_belonging_need_weight_slight_collectivist() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = -0.5;
let weight = ctx.belonging_need_weight();
assert!((weight - 1.15).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_belonging_need_weight_individualist_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.3;
let weight = ctx.belonging_need_weight();
assert!(weight > 0.9);
}
#[test]
fn macrosystem_belonging_need_weight_positive_below_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.2;
let weight = ctx.belonging_need_weight();
assert!((weight - 0.94).abs() < f64::EPSILON);
}
#[test]
fn for_subculture_with_nonexistent_key_returns_clone() {
let ctx = MacrosystemContext::default();
let result = ctx.for_subculture("nonexistent_key");
assert_eq!(result, ctx);
}
#[test]
fn macrosystem_set_value_power_distance() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::PowerDistance, 0.85);
assert!((ctx.cultural_orientation.power_distance - 0.85).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_uncertainty_avoidance() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::UncertaintyAvoidance, 0.65);
assert!((ctx.cultural_orientation.uncertainty_avoidance - 0.65).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_rule_of_law() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::RuleOfLaw, 0.85);
assert!((ctx.institutional_structure.rule_of_law - 0.85).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_social_mobility() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::SocialMobility, 0.75);
assert!((ctx.institutional_structure.social_mobility - 0.75).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_corruption_level() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::CorruptionLevel, 0.55);
assert!((ctx.institutional_structure.corruption_level - 0.55).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_collective_trauma() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::CollectiveTrauma, 0.65);
assert!((ctx.collective_trauma - 0.65).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_economic_inequality() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::EconomicInequality, 0.75);
assert!((ctx.economic_inequality - 0.75).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_set_value_cultural_restrictiveness() {
let mut ctx = MacrosystemContext::default();
ctx.set_value(&MacrosystemPath::CulturalRestrictiveness, 0.7);
assert!((ctx.cultural_restrictiveness - 0.7).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_is_individualist_boundary_above_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.31;
assert!(ctx.is_individualist());
}
#[test]
fn macrosystem_is_individualist_boundary_below_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.29;
assert!(!ctx.is_individualist());
}
#[test]
fn macrosystem_is_collectivist_boundary_below_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = -0.31;
assert!(ctx.is_collectivist());
}
#[test]
fn macrosystem_is_collectivist_boundary_above_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = -0.29;
assert!(!ctx.is_collectivist());
}
#[test]
fn macrosystem_is_high_power_distance_boundary_above() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.power_distance = 0.61;
assert!(ctx.is_high_power_distance());
}
#[test]
fn macrosystem_is_high_power_distance_boundary_below() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.power_distance = 0.59;
assert!(!ctx.is_high_power_distance());
}
#[test]
fn macrosystem_modifier_default_all_none() {
let modifier = MacrosystemModifier::default();
assert!(modifier.individualism_collectivism.is_none());
assert!(modifier.power_distance.is_none());
assert!(modifier.uncertainty_avoidance.is_none());
assert!(modifier.rule_of_law.is_none());
assert!(modifier.social_mobility.is_none());
assert!(modifier.corruption_level.is_none());
assert!(modifier.cultural_stress.is_none());
assert!(modifier.collective_trauma.is_none());
assert!(modifier.economic_inequality.is_none());
}
#[test]
fn macrosystem_constraint_set_clone_eq() {
let constraint1 = MacrosystemConstraintSet::default();
let constraint2 = constraint1.clone();
assert_eq!(constraint1, constraint2);
}
#[test]
fn macrosystem_constraint_set_debug() {
let constraint = MacrosystemConstraintSet::default();
let debug_str = format!("{:?}", constraint);
assert!(debug_str.contains("MacrosystemConstraintSet"));
}
#[test]
fn macrosystem_modifier_clone_eq() {
let modifier1 = MacrosystemModifier::default();
let modifier2 = modifier1.clone();
assert_eq!(modifier1, modifier2);
}
#[test]
fn macrosystem_modifier_debug() {
let modifier = MacrosystemModifier::default();
let debug_str = format!("{:?}", modifier);
assert!(debug_str.contains("MacrosystemModifier"));
}
#[test]
fn macrosystem_subculture_none_exists() {
let ctx = MacrosystemContext::default();
let result = ctx.for_subculture("nonexistent");
assert_eq!(result, ctx);
}
#[test]
fn macrosystem_subculture_multiple_overrides() {
let mut ctx = MacrosystemContext::default();
let mut modifier1 = MacrosystemModifier::default();
modifier1.power_distance = Some(0.9);
modifier1.cultural_stress = Some(0.8);
let mut modifier2 = MacrosystemModifier::default();
modifier2.power_distance = Some(0.2);
modifier2.economic_inequality = Some(0.1);
ctx.subculture_overrides
.insert("elite".to_string(), modifier1);
ctx.subculture_overrides
.insert("marginalized".to_string(), modifier2);
let elite = ctx.for_subculture("elite");
assert!((elite.cultural_orientation.power_distance - 0.9).abs() < f64::EPSILON);
assert!((elite.cultural_stress - 0.8).abs() < f64::EPSILON);
let marginalized = ctx.for_subculture("marginalized");
assert!((marginalized.cultural_orientation.power_distance - 0.2).abs() < f64::EPSILON);
assert!((marginalized.economic_inequality - 0.1).abs() < f64::EPSILON);
}
#[test]
fn belonging_need_weight_above_threshold() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.individualism_collectivism = 0.5;
let weight = ctx.belonging_need_weight();
assert!((weight - (1.0 - 0.5 * 0.2)).abs() < f64::EPSILON);
}
#[test]
fn macrosystem_subculture_override_applied() {
let mut ctx = MacrosystemContext::default();
ctx.cultural_orientation.power_distance = 0.3;
let mut modifier = MacrosystemModifier::default();
modifier.power_distance = Some(0.9);
ctx.subculture_overrides
.insert("override".to_string(), modifier);
let overridden = ctx.for_subculture("override");
assert!((overridden.cultural_orientation.power_distance - 0.9).abs() < f64::EPSILON);
}
#[test]
fn cultural_shift_interpolates_from_current_values() {
let base = MacrosystemContext::default();
let start = Timestamp::from_ymd_hms(2000, 1, 1, 0, 0, 0);
let shift = CulturalShift::new(start)
.with_target(MacrosystemPath::IndividualismCollectivism, 0.8)
.with_target(MacrosystemPath::PowerDistance, 0.2);
let mut chrono = ChronosystemContext::default();
chrono.add_cultural_shift(shift);
let immediate = base.interpolate_for_timestamp(&chrono, start);
assert!(
(immediate.get_value(&MacrosystemPath::IndividualismCollectivism)
- base.get_value(&MacrosystemPath::IndividualismCollectivism))
.abs()
< f64::EPSILON
);
assert!(
(immediate.get_value(&MacrosystemPath::PowerDistance)
- base.get_value(&MacrosystemPath::PowerDistance))
.abs()
< f64::EPSILON
);
}
#[test]
fn cultural_shift_midpoint_interpolation() {
let base = MacrosystemContext::default();
let start = Timestamp::from_ymd_hms(2000, 1, 1, 0, 0, 0);
let shift = CulturalShift::new(start)
.with_target(MacrosystemPath::IndividualismCollectivism, 0.8)
.with_target(MacrosystemPath::PowerDistance, 0.2);
let mut chrono = ChronosystemContext::default();
chrono.add_cultural_shift(shift);
let midpoint = start + Duration::days(913);
let snapshot = base.interpolate_for_timestamp(&chrono, midpoint);
let progress = 913.0 / 1825.0;
let expected_ic = base.get_value(&MacrosystemPath::IndividualismCollectivism)
+ (0.8 - base.get_value(&MacrosystemPath::IndividualismCollectivism)) * progress;
let expected_pd = base.get_value(&MacrosystemPath::PowerDistance)
+ (0.2 - base.get_value(&MacrosystemPath::PowerDistance)) * progress;
assert!(
(snapshot.get_value(&MacrosystemPath::IndividualismCollectivism) - expected_ic).abs()
< 0.01
);
assert!(
(snapshot.get_value(&MacrosystemPath::PowerDistance) - expected_pd).abs() < 0.01
);
}
#[test]
fn cultural_shift_reaches_targets_after_five_years() {
let base = MacrosystemContext::default();
let start = Timestamp::from_ymd_hms(2000, 1, 1, 0, 0, 0);
let shift = CulturalShift::new(start)
.with_target(MacrosystemPath::IndividualismCollectivism, 0.8)
.with_target(MacrosystemPath::PowerDistance, 0.2);
let mut chrono = ChronosystemContext::default();
chrono.add_cultural_shift(shift);
let end = start + Duration::days(1825);
let snapshot = base.interpolate_for_timestamp(&chrono, end);
assert!(
(snapshot.get_value(&MacrosystemPath::IndividualismCollectivism) - 0.8).abs()
< 0.0001
);
assert!(
(snapshot.get_value(&MacrosystemPath::PowerDistance) - 0.2).abs() < 0.0001
);
}
}