use ccf_core::qac::{
qac_update_3x3, reject_arithmetic_interpolation_regression,
reject_missing_positive_diagonal_gauge, reject_scalar_accumulator_update, Matrix3,
PositiveDiagonal3, QacInputs3, ARITHMETIC_INTERPOLATION_OLD_WRONG_PATH,
MISSING_POSITIVE_DIAGONAL_GAUGE_OLD_WRONG_PATH, QAC_JOURNEY_ID, QAC_STORY_ID,
SCALAR_ACCUMULATOR_OLD_WRONG_PATH,
};
const EXPECTED_QAC: Matrix3 = Matrix3::new([
[0.98190658848882018, 1.2947637401235284, 0.71866568469348002],
[0.83870014968076223, 1.1484222048498280, 0.90773678135167934],
[0.92507475466515798, 1.2991347723221549, 0.99724185998841852],
]);
fn fixture() -> QacInputs3 {
QacInputs3 {
prior_a_t: Matrix3::new([[0.82, 1.15, 0.64], [1.04, 0.91, 1.33], [0.77, 1.28, 1.09]]),
reference_r_t: Matrix3::new([[1.21, 0.73, 1.08], [0.88, 1.42, 0.96], [1.31, 0.69, 1.17]]),
left_l_t: PositiveDiagonal3::try_new([1.10, 0.90, 1.05]).unwrap(),
right_c_t: PositiveDiagonal3::try_new([0.95, 1.20, 0.85]).unwrap(),
alpha_t: 0.35,
epsilon_floor: 1.0e-12,
}
}
fn max_abs_error(actual: Matrix3, expected: Matrix3) -> f64 {
let mut max_error = 0.0_f64;
for row in 0..3 {
for col in 0..3 {
max_error =
max_error.max((actual.entries[row][col] - expected.entries[row][col]).abs());
}
}
max_error
}
#[test]
fn qac_update_matches_hand_computed_3x3_fixture() {
eprintln!("journey={QAC_JOURNEY_ID} story={QAC_STORY_ID}");
let actual = qac_update_3x3(&fixture()).expect("QAC update implementation is required");
let max_matrix_error = max_abs_error(actual, EXPECTED_QAC);
eprintln!("max_matrix_error={max_matrix_error:.16e}");
assert!(
max_matrix_error <= 1.0e-12,
"max matrix error {max_matrix_error:.16e} exceeds 1e-12"
);
}
#[test]
fn arithmetic_interpolation_negative_fixture_reports_kappa_excursion() {
eprintln!("journey={QAC_JOURNEY_ID} story={QAC_STORY_ID}");
let report = reject_arithmetic_interpolation_regression(&fixture())
.expect("arithmetic interpolation negative guard is required");
assert_eq!(
report.old_wrong_path,
ARITHMETIC_INTERPOLATION_OLD_WRONG_PATH
);
assert!(report.rejected);
assert!(
report.kappa_excursion > 0.0,
"arithmetic interpolation must produce a positive kappa excursion"
);
eprintln!(
"arithmetic_interpolation_negative=PASS kappa_excursion={:.16e}",
report.kappa_excursion
);
}
#[test]
fn missing_positive_diagonal_gauge_negative_fixture_is_rejected() {
eprintln!("journey={QAC_JOURNEY_ID} story={QAC_STORY_ID}");
let report = reject_missing_positive_diagonal_gauge(&fixture())
.expect("missing positive diagonal gauge negative guard is required");
assert_eq!(
report.old_wrong_path,
MISSING_POSITIVE_DIAGONAL_GAUGE_OLD_WRONG_PATH
);
assert!(report.rejected);
eprintln!("missing_positive_diagonal_gauge_negative=PASS");
}
#[test]
fn scalar_accumulator_negative_fixture_reports_kappa_excursion() {
eprintln!("journey={QAC_JOURNEY_ID} story={QAC_STORY_ID}");
let report = reject_scalar_accumulator_update(&fixture())
.expect("scalar accumulator negative guard is required");
assert_eq!(report.old_wrong_path, SCALAR_ACCUMULATOR_OLD_WRONG_PATH);
assert!(report.rejected);
assert!(
report.kappa_excursion > 0.0,
"scalar accumulator update must produce a positive kappa excursion"
);
eprintln!(
"scalar_accumulator_negative=PASS kappa_excursion={:.16e}",
report.kappa_excursion
);
}