use serde::{Deserialize, Serialize};
use tracing::{instrument, warn};
use crate::constants::{ALPHA, ALPHA_S_MZ, M_Z_GEV};
use crate::cosmology::friedmann::CosmologicalParameters;
use crate::error::{MimamsaError, ensure_finite, require_finite};
use crate::quantum_field::coupling::{
running_coupling_qcd_analytic, running_coupling_qed_analytic,
};
use crate::error::require_all_finite;
use super::fixed_point::manifestation_intensity;
const SIGN_ELEMENT: [usize; 12] = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3];
const SIGN_MODALITY: [usize; 12] = [0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2];
const PLANET_WEIGHTS: [f64; 10] = [2.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
const ASPECT_ORB: f64 = 8.0;
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct BridgeOutput {
pub intensity: f64,
pub phase: f64,
pub unity_param: f64,
pub convergence_rate: f64,
}
impl BridgeOutput {
#[instrument(level = "debug", skip(params), ret)]
pub fn at_redshift(params: &CosmologicalParameters, z: f64) -> Result<Self, MimamsaError> {
let intensity = bridge_scale_6(params, z)?;
let phase = bridge_scale_7(params, z)?;
let unity_param = 1.0 - intensity;
let dz = 0.01;
let intensity_plus = bridge_scale_6(params, z + dz)?;
let convergence_rate = ensure_finite(
((intensity - intensity_plus) / dz).abs(),
"BridgeOutput::convergence_rate",
)?;
Ok(Self {
intensity,
phase,
unity_param,
convergence_rate,
})
}
}
#[instrument(level = "trace")]
#[inline]
pub fn scale_coupling_qed(mu_gev: f64) -> Result<f64, MimamsaError> {
require_finite(mu_gev, "scale_coupling_qed")?;
if mu_gev <= 0.0 {
warn!(mu_gev, "scale_coupling_qed: scale must be positive");
return Err(MimamsaError::Computation(
"scale_coupling_qed: scale must be positive".to_string(),
));
}
running_coupling_qed_analytic(ALPHA, M_Z_GEV, mu_gev)
}
#[instrument(level = "trace")]
#[inline]
pub fn scale_coupling_qcd(mu_gev: f64, n_f: u8) -> Result<f64, MimamsaError> {
require_finite(mu_gev, "scale_coupling_qcd")?;
if mu_gev <= 0.0 {
warn!(mu_gev, "scale_coupling_qcd: scale must be positive");
return Err(MimamsaError::Computation(
"scale_coupling_qcd: scale must be positive".to_string(),
));
}
running_coupling_qcd_analytic(ALPHA_S_MZ, M_Z_GEV, mu_gev, n_f)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlanetaryField {
pub element_balance: [f64; 4],
pub modality_balance: [f64; 3],
pub aspect_tension: f64,
pub aspect_harmony: f64,
pub house_emphasis: [f64; 12],
pub retrograde_fraction: f64,
pub dominant_element: u8,
pub dominant_modality: u8,
}
#[inline]
fn sign_index(longitude_deg: f64) -> usize {
let normalized = ((longitude_deg % 360.0) + 360.0) % 360.0;
(normalized / 30.0) as usize % 12
}
#[must_use = "element balance distribution should be used"]
#[instrument(level = "trace", skip(longitudes_deg))]
pub fn element_balance(longitudes_deg: &[f64; 10]) -> Result<[f64; 4], MimamsaError> {
require_all_finite(longitudes_deg, "element_balance")?;
let mut counts = [0.0_f64; 4];
for (i, &lon) in longitudes_deg.iter().enumerate() {
let sign = sign_index(lon);
counts[SIGN_ELEMENT[sign]] += PLANET_WEIGHTS[i];
}
let total: f64 = counts.iter().sum();
if total <= 0.0 {
warn!(total, "element_balance: zero total weight");
return Err(MimamsaError::Computation(
"element_balance: zero total weight".to_string(),
));
}
for c in &mut counts {
*c /= total;
}
Ok(counts)
}
#[must_use = "modality balance distribution should be used"]
#[instrument(level = "trace", skip(longitudes_deg))]
pub fn modality_balance(longitudes_deg: &[f64; 10]) -> Result<[f64; 3], MimamsaError> {
require_all_finite(longitudes_deg, "modality_balance")?;
let mut counts = [0.0_f64; 3];
for (i, &lon) in longitudes_deg.iter().enumerate() {
let sign = sign_index(lon);
counts[SIGN_MODALITY[sign]] += PLANET_WEIGHTS[i];
}
let total: f64 = counts.iter().sum();
if total <= 0.0 {
warn!(total, "modality_balance: zero total weight");
return Err(MimamsaError::Computation(
"modality_balance: zero total weight".to_string(),
));
}
for c in &mut counts {
*c /= total;
}
Ok(counts)
}
#[must_use = "aspect tension/harmony values should be used"]
#[instrument(level = "trace", skip(aspects))]
pub fn aspect_tension_harmony(aspects: &[(f64, f64)]) -> Result<(f64, f64), MimamsaError> {
if aspects.is_empty() {
return Ok((0.0, 0.0));
}
let mut hard_sum = 0.0_f64;
let mut soft_sum = 0.0_f64;
let mut total_strength = 0.0_f64;
for &(angle, strength) in aspects {
require_finite(angle, "aspect_tension_harmony")?;
require_finite(strength, "aspect_tension_harmony")?;
let s = strength.clamp(0.0, 1.0);
total_strength += s;
if (angle - 90.0).abs() < ASPECT_ORB || (angle - 180.0).abs() < ASPECT_ORB {
hard_sum += s;
}
if (angle - 60.0).abs() < ASPECT_ORB || (angle - 120.0).abs() < ASPECT_ORB {
soft_sum += s;
}
}
if total_strength <= 0.0 {
return Ok((0.0, 0.0));
}
let tension = ensure_finite(hard_sum / total_strength, "aspect_tension")?;
let harmony = ensure_finite(soft_sum / total_strength, "aspect_harmony")?;
Ok((tension.clamp(0.0, 1.0), harmony.clamp(0.0, 1.0)))
}
#[must_use = "house emphasis distribution should be used"]
#[instrument(level = "trace", skip(planet_longitudes, house_cusps))]
pub fn house_emphasis(
planet_longitudes: &[f64; 10],
house_cusps: &[f64; 12],
) -> Result<[f64; 12], MimamsaError> {
require_all_finite(planet_longitudes, "house_emphasis")?;
require_all_finite(house_cusps, "house_emphasis")?;
let mut emphasis = [0.0_f64; 12];
for (p_idx, &p_lon) in planet_longitudes.iter().enumerate() {
let p = ((p_lon % 360.0) + 360.0) % 360.0;
let mut placed = false;
for h in 0..12 {
let cusp_start = ((house_cusps[h] % 360.0) + 360.0) % 360.0;
let cusp_end = ((house_cusps[(h + 1) % 12] % 360.0) + 360.0) % 360.0;
let in_house = if cusp_start < cusp_end {
p >= cusp_start && p < cusp_end
} else {
p >= cusp_start || p < cusp_end
};
if in_house {
emphasis[h] += PLANET_WEIGHTS[p_idx];
placed = true;
break;
}
}
if !placed {
emphasis[0] += PLANET_WEIGHTS[p_idx]; }
}
let total: f64 = emphasis.iter().sum();
if total <= 0.0 {
warn!(total, "house_emphasis: zero total weight");
return Err(MimamsaError::Computation(
"house_emphasis: zero total weight".to_string(),
));
}
for e in &mut emphasis {
*e /= total;
}
Ok(emphasis)
}
#[must_use = "retrograde fraction should be used"]
pub fn retrograde_fraction(daily_motions: &[f64]) -> Result<f64, MimamsaError> {
if daily_motions.is_empty() {
return Ok(0.0);
}
for &m in daily_motions {
require_finite(m, "retrograde_fraction")?;
}
let retro_count = daily_motions.iter().filter(|&&m| m < 0.0).count();
ensure_finite(
retro_count as f64 / daily_motions.len() as f64,
"retrograde_fraction",
)
}
#[instrument(level = "debug", skip_all)]
pub fn bridge_scale_3(
planet_longitudes: &[f64; 10],
house_cusps: &[f64; 12],
aspects: &[(f64, f64)],
daily_motions: &[f64],
) -> Result<PlanetaryField, MimamsaError> {
let elem = element_balance(planet_longitudes)?;
let modal = modality_balance(planet_longitudes)?;
let (tension, harmony) = aspect_tension_harmony(aspects)?;
let houses = house_emphasis(planet_longitudes, house_cusps)?;
let retro = retrograde_fraction(daily_motions)?;
let dominant_element = elem
.iter()
.enumerate()
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i as u8)
.unwrap_or(0);
let dominant_modality = modal
.iter()
.enumerate()
.max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
.map(|(i, _)| i as u8)
.unwrap_or(0);
Ok(PlanetaryField {
element_balance: elem,
modality_balance: modal,
aspect_tension: tension,
aspect_harmony: harmony,
house_emphasis: houses,
retrograde_fraction: retro,
dominant_element,
dominant_modality,
})
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct StellarField {
pub lifecycle_fraction: f64,
pub luminosity_intensity: f64,
pub thermal_temperament: f64,
pub compositional_complexity: f64,
pub evolutionary_urgency: f64,
pub spectral_archetype: u8,
}
fn spectral_archetype_from_temperature(temperature_k: f64) -> u8 {
match temperature_k {
t if t >= 30000.0 => 0, t if t >= 10000.0 => 1, t if t >= 7500.0 => 2, t if t >= 6000.0 => 3, t if t >= 5200.0 => 4, t if t >= 3700.0 => 5, _ => 6, }
}
#[instrument(level = "debug", skip_all)]
pub fn bridge_scale_4(
temperature_k: f64,
luminosity_solar: f64,
age_years: f64,
main_sequence_lifetime_years: f64,
metallicity_feh: f64,
) -> Result<StellarField, MimamsaError> {
require_all_finite(
&[
temperature_k,
luminosity_solar,
age_years,
main_sequence_lifetime_years,
metallicity_feh,
],
"bridge_scale_4",
)?;
if temperature_k <= 0.0 || luminosity_solar <= 0.0 || main_sequence_lifetime_years <= 0.0 {
warn!(
temperature_k,
luminosity_solar,
main_sequence_lifetime_years,
"bridge_scale_4: temperature, luminosity, and lifetime must be positive"
);
return Err(MimamsaError::Computation(
"bridge_scale_4: temperature, luminosity, and lifetime must be positive".to_string(),
));
}
let tau = (age_years / main_sequence_lifetime_years).clamp(0.0, 1.0);
let log_l = luminosity_solar.max(0.01).log10(); let luminosity_intensity = ((log_l + 2.0) / 8.0).clamp(0.0, 1.0);
let log_t = temperature_k.log10(); let thermal_temperament =
((log_t - 2400.0_f64.log10()) / (50000.0_f64.log10() - 2400.0_f64.log10())).clamp(0.0, 1.0);
let compositional_complexity = ((metallicity_feh + 2.0) / 2.5).clamp(0.0, 1.0);
let evolutionary_urgency = (tau * tau * tau).clamp(0.0, 1.0);
let spectral_archetype = spectral_archetype_from_temperature(temperature_k);
Ok(StellarField {
lifecycle_fraction: tau,
luminosity_intensity,
thermal_temperament,
compositional_complexity,
evolutionary_urgency,
spectral_archetype,
})
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct GalacticField {
pub concentration: f64,
pub density_pressure: f64,
pub structure_growth: f64,
pub cosmic_activity: f64,
pub chemical_complexity: f64,
pub filamentarity: f64,
pub web_environment: u8,
}
fn web_environment_from_eigenvalues(
lambda1: f64,
lambda2: f64,
lambda3: f64,
threshold: f64,
) -> u8 {
let count = [lambda1, lambda2, lambda3]
.iter()
.filter(|&&l| l > threshold)
.count();
count as u8 }
#[instrument(level = "debug", skip_all)]
pub fn bridge_scale_5(
halo_concentration: f64,
density_contrast: f64,
growth_factor: f64,
sfr_density: f64,
metallicity_12log_oh: f64,
tidal_eigenvalues: &[f64; 3],
tidal_threshold: f64,
) -> Result<GalacticField, MimamsaError> {
require_all_finite(
&[
halo_concentration,
density_contrast,
growth_factor,
sfr_density,
metallicity_12log_oh,
tidal_eigenvalues[0],
tidal_eigenvalues[1],
tidal_eigenvalues[2],
tidal_threshold,
],
"bridge_scale_5",
)?;
let concentration = ((halo_concentration - 3.0) / 17.0).clamp(0.0, 1.0);
let density_pressure = (1.0 / (1.0 + (-density_contrast).exp())).clamp(0.0, 1.0);
let structure_growth = growth_factor.clamp(0.0, 1.0);
let cosmic_activity = (sfr_density / 0.1).clamp(0.0, 1.0);
let chemical_complexity = ((metallicity_12log_oh - 7.5) / 1.8).clamp(0.0, 1.0);
let [l1, l2, l3] = *tidal_eigenvalues;
let eigen_range = l1 - l3;
let filamentarity = if eigen_range.abs() > 1e-15 {
((l2 - l3) / eigen_range).clamp(0.0, 1.0)
} else {
0.5 };
let web_environment = web_environment_from_eigenvalues(l1, l2, l3, tidal_threshold);
Ok(GalacticField {
concentration,
density_pressure,
structure_growth,
cosmic_activity,
chemical_complexity,
filamentarity,
web_environment,
})
}
#[instrument(level = "trace", skip(params))]
pub fn bridge_scale_6(params: &CosmologicalParameters, z: f64) -> Result<f64, MimamsaError> {
manifestation_intensity(params, z)
}
#[instrument(level = "trace", skip(params))]
pub fn bridge_scale_7(params: &CosmologicalParameters, z: f64) -> Result<f64, MimamsaError> {
manifestation_intensity(params, z)
}
#[cfg(test)]
mod tests {
use super::*;
fn planck() -> CosmologicalParameters {
CosmologicalParameters::planck2018()
}
#[test]
fn test_scale_coupling_qed_at_mz() {
let a = scale_coupling_qed(M_Z_GEV).unwrap();
assert!((a - ALPHA).abs() / ALPHA < 1e-6);
}
#[test]
fn test_scale_coupling_qed_increases() {
let a_200 = scale_coupling_qed(200.0).unwrap();
assert!(a_200 > ALPHA, "QED coupling should increase with energy");
}
#[test]
fn test_scale_coupling_qcd_at_mz() {
let a = scale_coupling_qcd(M_Z_GEV, 6).unwrap();
assert!((a - ALPHA_S_MZ).abs() / ALPHA_S_MZ < 1e-6);
}
#[test]
fn test_scale_coupling_qcd_decreases() {
let a_1000 = scale_coupling_qcd(1000.0, 6).unwrap();
assert!(
a_1000 < ALPHA_S_MZ,
"QCD coupling should decrease with energy"
);
}
#[test]
fn test_bridge_scale_6_bounds() {
let p = planck();
for z in [0.0, 0.5, 1.0, 5.0, 100.0] {
let i = bridge_scale_6(&p, z).unwrap();
assert!(
(0.0..=1.0).contains(&i),
"bridge_scale_6({z}) = {i} out of [0,1]"
);
}
}
#[test]
fn test_bridge_scale_7_bounds() {
let p = planck();
for z in [0.0, 0.5, 1.0, 5.0, 100.0] {
let phase = bridge_scale_7(&p, z).unwrap();
assert!(
(0.0..=1.0).contains(&phase),
"bridge_scale_7({z}) = {phase} out of [0,1]"
);
}
}
#[test]
fn test_bridge_output_consistency() {
let out = BridgeOutput::at_redshift(&planck(), 0.0).unwrap();
assert!((out.intensity + out.unity_param - 1.0).abs() < 1e-12);
assert!(out.convergence_rate >= 0.0);
}
#[test]
fn test_bridge_output_serde() {
let out = BridgeOutput::at_redshift(&planck(), 0.5).unwrap();
let json = serde_json::to_string(&out).unwrap();
let _back: BridgeOutput = serde_json::from_str(&json).unwrap();
}
fn all_aries() -> [f64; 10] {
[0.0; 10]
}
fn spread_signs() -> [f64; 10] {
[
5.0, 35.0, 65.0, 95.0, 125.0, 155.0, 185.0, 215.0, 245.0, 275.0,
]
}
fn equal_cusps() -> [f64; 12] {
let mut cusps = [0.0; 12];
for (i, cusp) in cusps.iter_mut().enumerate() {
*cusp = i as f64 * 30.0;
}
cusps
}
#[test]
fn test_element_balance_all_fire() {
let elem = element_balance(&all_aries()).unwrap();
assert!(
(elem[0] - 1.0).abs() < 1e-10,
"all-Aries should be 100% Fire"
);
assert!(elem[1].abs() < 1e-10);
}
#[test]
fn test_element_balance_sums_to_one() {
let elem = element_balance(&spread_signs()).unwrap();
let sum: f64 = elem.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
}
#[test]
fn test_modality_balance_sums_to_one() {
let modal = modality_balance(&spread_signs()).unwrap();
let sum: f64 = modal.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
}
#[test]
fn test_aspect_tension_hard() {
let aspects = vec![(90.0, 1.0), (90.0, 1.0), (180.0, 1.0)];
let (tension, harmony) = aspect_tension_harmony(&aspects).unwrap();
assert!((tension - 1.0).abs() < 1e-10);
assert!(harmony.abs() < 1e-10);
}
#[test]
fn test_aspect_harmony_soft() {
let aspects = vec![(120.0, 1.0), (60.0, 1.0)];
let (tension, harmony) = aspect_tension_harmony(&aspects).unwrap();
assert!(tension.abs() < 1e-10);
assert!((harmony - 1.0).abs() < 1e-10);
}
#[test]
fn test_aspect_empty() {
let (t, h) = aspect_tension_harmony(&[]).unwrap();
assert!(t.abs() < 1e-10);
assert!(h.abs() < 1e-10);
}
#[test]
fn test_house_emphasis_sums_to_one() {
let houses = house_emphasis(&spread_signs(), &equal_cusps()).unwrap();
let sum: f64 = houses.iter().sum();
assert!((sum - 1.0).abs() < 1e-10);
}
#[test]
fn test_retrograde_fraction_none() {
let motions = [1.0, 13.0, 1.2, 0.6, 0.5, 0.08, 0.03, 0.01, 0.01, 0.01];
let frac = retrograde_fraction(&motions).unwrap();
assert!(frac.abs() < 1e-10);
}
#[test]
fn test_retrograde_fraction_some() {
let motions = [1.0, 13.0, -0.5, 0.6, -0.3, 0.08, 0.03, 0.01, 0.01, 0.01];
let frac = retrograde_fraction(&motions).unwrap();
assert!((frac - 0.2).abs() < 1e-10); }
#[test]
fn test_bridge_scale_3_complete() {
let field = bridge_scale_3(
&spread_signs(),
&equal_cusps(),
&[(90.0, 0.8), (120.0, 0.9), (60.0, 0.5)],
&[1.0, 13.0, -0.5, 0.6, 0.5, 0.08, 0.03, 0.01, 0.01, 0.01],
)
.unwrap();
assert!((field.element_balance.iter().sum::<f64>() - 1.0).abs() < 1e-10);
assert!((field.modality_balance.iter().sum::<f64>() - 1.0).abs() < 1e-10);
assert!((field.house_emphasis.iter().sum::<f64>() - 1.0).abs() < 1e-10);
assert!(field.aspect_tension >= 0.0 && field.aspect_tension <= 1.0);
assert!(field.aspect_harmony >= 0.0 && field.aspect_harmony <= 1.0);
assert!(field.retrograde_fraction >= 0.0 && field.retrograde_fraction <= 1.0);
assert!(field.dominant_element <= 3);
assert!(field.dominant_modality <= 2);
}
#[test]
fn test_bridge_scale_3_serde() {
let field =
bridge_scale_3(&spread_signs(), &equal_cusps(), &[(90.0, 0.8)], &[1.0; 10]).unwrap();
let json = serde_json::to_string(&field).unwrap();
let _back: PlanetaryField = serde_json::from_str(&json).unwrap();
}
#[test]
fn test_bridge_scale_4_sun() {
let field = bridge_scale_4(5772.0, 1.0, 4.6e9, 1.0e10, 0.0).unwrap();
assert!(field.lifecycle_fraction > 0.4 && field.lifecycle_fraction < 0.5);
assert!(field.luminosity_intensity > 0.0 && field.luminosity_intensity < 1.0);
assert!(field.thermal_temperament > 0.0 && field.thermal_temperament < 1.0);
assert!((field.compositional_complexity - 0.8).abs() < 0.01); assert_eq!(field.spectral_archetype, 4); }
#[test]
fn test_bridge_scale_4_hot_star() {
let field = bridge_scale_4(40000.0, 1e5, 1e6, 3e6, -0.5).unwrap();
assert!(field.thermal_temperament > 0.9);
assert!(field.luminosity_intensity > 0.8);
assert_eq!(field.spectral_archetype, 0); }
#[test]
fn test_bridge_scale_4_cool_star() {
let field = bridge_scale_4(3000.0, 0.01, 1e9, 1e12, -1.0).unwrap();
assert!(field.thermal_temperament < 0.15);
assert!(field.luminosity_intensity < 0.01);
assert_eq!(field.spectral_archetype, 6); }
#[test]
fn test_bridge_scale_4_bounds() {
let field = bridge_scale_4(5772.0, 1.0, 4.6e9, 1.0e10, 0.0).unwrap();
assert!((0.0..=1.0).contains(&field.lifecycle_fraction));
assert!((0.0..=1.0).contains(&field.luminosity_intensity));
assert!((0.0..=1.0).contains(&field.thermal_temperament));
assert!((0.0..=1.0).contains(&field.compositional_complexity));
assert!((0.0..=1.0).contains(&field.evolutionary_urgency));
assert!(field.spectral_archetype <= 6);
}
#[test]
fn test_bridge_scale_4_invalid_rejected() {
assert!(bridge_scale_4(-1.0, 1.0, 1e9, 1e10, 0.0).is_err());
assert!(bridge_scale_4(5772.0, -1.0, 1e9, 1e10, 0.0).is_err());
assert!(bridge_scale_4(5772.0, 1.0, 1e9, -1.0, 0.0).is_err());
}
#[test]
fn test_bridge_scale_4_serde() {
let field = bridge_scale_4(5772.0, 1.0, 4.6e9, 1.0e10, 0.0).unwrap();
let json = serde_json::to_string(&field).unwrap();
let _back: StellarField = serde_json::from_str(&json).unwrap();
}
#[test]
fn test_bridge_scale_4_evolutionary_urgency() {
let young = bridge_scale_4(5772.0, 1.0, 1e8, 1e10, 0.0).unwrap();
let old = bridge_scale_4(5772.0, 1.0, 9.5e9, 1e10, 0.0).unwrap();
assert!(old.evolutionary_urgency > young.evolutionary_urgency);
}
fn milky_way_scale_5() -> GalacticField {
bridge_scale_5(10.0, 0.0, 1.0, 0.01, 8.7, &[1.0, 0.3, -0.5], 0.0).unwrap()
}
#[test]
fn test_bridge_scale_5_milky_way() {
let field = milky_way_scale_5();
assert!(field.concentration > 0.3 && field.concentration < 0.5);
assert!((field.density_pressure - 0.5).abs() < 0.01);
assert!((field.structure_growth - 1.0).abs() < 1e-10);
assert!(field.chemical_complexity > 0.6 && field.chemical_complexity < 0.7);
assert_eq!(field.web_environment, 2);
}
#[test]
fn test_bridge_scale_5_void() {
let field = bridge_scale_5(5.0, -0.8, 0.3, 0.001, 7.8, &[-0.1, -0.3, -0.5], 0.0).unwrap();
assert!(field.concentration < 0.2);
assert!(field.density_pressure < 0.35);
assert!(field.structure_growth < 0.35);
assert_eq!(field.web_environment, 0); }
#[test]
fn test_bridge_scale_5_cluster_node() {
let field = bridge_scale_5(15.0, 5.0, 1.0, 0.05, 9.0, &[2.0, 1.5, 0.5], 0.0).unwrap();
assert!(field.concentration > 0.6);
assert!(field.density_pressure > 0.9);
assert_eq!(field.web_environment, 3); }
#[test]
fn test_bridge_scale_5_cosmic_noon() {
let field = bridge_scale_5(8.0, 1.0, 0.5, 0.1, 8.5, &[0.5, 0.1, -0.2], 0.0).unwrap();
assert!((field.cosmic_activity - 1.0).abs() < 0.01); }
#[test]
fn test_bridge_scale_5_bounds() {
let field = milky_way_scale_5();
assert!((0.0..=1.0).contains(&field.concentration));
assert!((0.0..=1.0).contains(&field.density_pressure));
assert!((0.0..=1.0).contains(&field.structure_growth));
assert!((0.0..=1.0).contains(&field.cosmic_activity));
assert!((0.0..=1.0).contains(&field.chemical_complexity));
assert!((0.0..=1.0).contains(&field.filamentarity));
assert!(field.web_environment <= 3);
}
#[test]
fn test_bridge_scale_5_serde() {
let field = milky_way_scale_5();
let json = serde_json::to_string(&field).unwrap();
let _back: GalacticField = serde_json::from_str(&json).unwrap();
}
#[test]
fn test_negative_scale_rejected() {
assert!(scale_coupling_qed(-1.0).is_err());
assert!(scale_coupling_qcd(-1.0, 6).is_err());
}
}