#include "reference_tolerance_policy.h" #include #include #include namespace fesa::test { namespace { bool IsFiniteFamily(const ReferenceToleranceFamily& family) { if (family.identity.empty() || family.components.empty() || family.rows.empty()) { return false; } return std::all_of(family.rows.begin(), family.rows.end(), [](const ReferenceToleranceRow& row) { return std::isfinite(row.fesa_value) && std::isfinite(row.reference_value); }); } } ReferenceToleranceEvaluation ReferenceTolerancePolicy::Evaluate( const std::vector& families) { ReferenceToleranceEvaluation evaluation{true, {}, true, {}}; evaluation.families.reserve(families.size()); for (const auto& family : families) { if (!IsFiniteFamily(family)) { return {false, "nonfinite-comparison-value", false, {}}; } ReferenceToleranceFamilyDecision decision{}; decision.identity = family.identity; decision.components = family.components; decision.blocking = family.blocking; decision.row_count = family.rows.size(); decision.rows.reserve(family.rows.size()); for (const auto& row : family.rows) { decision.reference_scale = (std::max)(decision.reference_scale, std::abs(row.reference_value)); } decision.near_zero_band = kNearZeroRatio * decision.reference_scale; double error_norm = 0.0; for (std::size_t index = 0U; index < family.rows.size(); ++index) { const auto& row = family.rows[index]; const double absolute_error = std::abs(row.fesa_value - row.reference_value); if (!std::isfinite(absolute_error)) { return {false, "nonfinite-comparison-value", false, {}}; } error_norm = std::hypot(error_norm, absolute_error); if (!std::isfinite(error_norm)) { return {false, "nonfinite-comparison-value", false, {}}; } if (absolute_error > decision.maximum_absolute_error) { decision.maximum_absolute_error = absolute_error; decision.worst_row = index; } ReferenceToleranceRowDecision row_decision{}; row_decision.absolute_error = absolute_error; if (decision.reference_scale == 0.0) { ++decision.near_zero_count; row_decision.threshold = 0.0; row_decision.branch = ReferenceToleranceBranch::kZeroScaleExact; row_decision.passed = absolute_error == 0.0; } else if (std::abs(row.reference_value) <= decision.near_zero_band) { ++decision.near_zero_count; row_decision.threshold = decision.near_zero_band; row_decision.branch = ReferenceToleranceBranch::kNearZero; row_decision.passed = absolute_error <= row_decision.threshold; } else { row_decision.threshold = kRelativeTolerance * std::abs(row.reference_value); row_decision.relative_error = absolute_error / std::abs(row.reference_value); row_decision.relative_error_applicable = true; row_decision.branch = ReferenceToleranceBranch::kRelative; row_decision.passed = row_decision.relative_error <= kRelativeTolerance; } decision.rows.push_back(row_decision); } const bool rows_passed = std::all_of( decision.rows.begin(), decision.rows.end(), [](const ReferenceToleranceRowDecision& row) { return row.passed; }); if (decision.reference_scale == 0.0) { decision.relative_rms = 0.0; decision.rms_passed = rows_passed; if (!rows_passed) { decision.diagnostic_code = "zero-reference-scale-nonzero-error"; } } else { const double rms = error_norm / std::sqrt(static_cast(family.rows.size())); decision.relative_rms = rms / decision.reference_scale; if (!std::isfinite(decision.relative_rms)) { return {false, "nonfinite-comparison-value", false, {}}; } decision.rms_passed = decision.relative_rms <= kRelativeRmsTolerance; } decision.passed = rows_passed && decision.rms_passed; if (decision.blocking && !decision.passed) { evaluation.passed = false; } evaluation.families.push_back(std::move(decision)); } return evaluation; } }