#[must_use]
pub fn compute_regge_action(
vertices: u32,
edges: u32,
triangles: u32,
coupling_0: f64,
coupling_2: f64,
cosmological_constant: f64,
) -> f64 {
let n_0 = f64::from(vertices);
let n_1 = f64::from(edges);
let n_2 = f64::from(triangles);
cosmological_constant.mul_add(n_1, (-coupling_0).mul_add(n_0, -(coupling_2 * n_2)))
}
#[derive(Debug, Clone)]
pub struct ActionConfig {
pub coupling_0: f64,
pub coupling_2: f64,
pub cosmological_constant: f64,
}
impl Default for ActionConfig {
fn default() -> Self {
Self {
coupling_0: 1.0,
coupling_2: 1.0,
cosmological_constant: 0.1,
}
}
}
impl ActionConfig {
#[must_use]
pub const fn new(coupling_0: f64, coupling_2: f64, cosmological_constant: f64) -> Self {
Self {
coupling_0,
coupling_2,
cosmological_constant,
}
}
#[must_use]
pub fn calculate_action(&self, vertices: u32, edges: u32, triangles: u32) -> f64 {
compute_regge_action(
vertices,
edges,
triangles,
self.coupling_0,
self.coupling_2,
self.cosmological_constant,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
#[test]
fn test_regge_action_calculation() {
let vertices = 10;
let edges = 20;
let triangles = 15;
let coupling_0 = 1.0;
let coupling_2 = 1.0;
let cosmological_constant = 0.1;
let action = compute_regge_action(
vertices,
edges,
triangles,
coupling_0,
coupling_2,
cosmological_constant,
);
let expected = -23.0;
assert_relative_eq!(action, expected);
}
#[test]
fn test_action_config_default() {
let config = ActionConfig::default();
assert_relative_eq!(config.coupling_0, 1.0);
assert_relative_eq!(config.coupling_2, 1.0);
assert_relative_eq!(config.cosmological_constant, 0.1);
}
#[test]
fn test_action_config_calculate() {
let config = ActionConfig::new(2.0, 1.5, 0.2);
let action = config.calculate_action(5, 10, 8);
let expected = -20.0;
assert_relative_eq!(action, expected);
}
}
#[cfg(test)]
mod prop_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn action_always_finite(
vertices in 0u32..100,
edges in 0u32..500,
triangles in 0u32..300,
coupling_0 in -10.0f64..10.0,
coupling_2 in -10.0f64..10.0,
cosmological_constant in -5.0f64..5.0
) {
let action = compute_regge_action(
vertices, edges, triangles,
coupling_0, coupling_2, cosmological_constant
);
prop_assert!(action.is_finite(), "Action must always be finite, got: {}", action);
prop_assert!(!action.is_nan(), "Action must not be NaN");
}
#[test]
fn action_config_consistency(
vertices in 0u32..50,
edges in 0u32..150,
triangles in 0u32..100,
coupling_0 in -5.0f64..5.0,
coupling_2 in -5.0f64..5.0,
cosmological_constant in -2.0f64..2.0
) {
let config = ActionConfig::new(coupling_0, coupling_2, cosmological_constant);
prop_assert!((config.coupling_0 - coupling_0).abs() < f64::EPSILON);
prop_assert!((config.coupling_2 - coupling_2).abs() < f64::EPSILON);
prop_assert!((config.cosmological_constant - cosmological_constant).abs() < f64::EPSILON);
let action_config = config.calculate_action(vertices, edges, triangles);
let action_direct = compute_regge_action(
vertices, edges, triangles,
coupling_0, coupling_2, cosmological_constant
);
prop_assert!(
(action_config - action_direct).abs() < f64::EPSILON,
"Config-based and direct calculations should match: {} vs {}",
action_config, action_direct
);
}
}
}