//! Locked C++ parity reference data for all encoder permutations.
//!
//! This file contains exhaustive reference values for all combinations of:
//! - Subsampling: S444, S422, S420, S440
//! - Huffman optimization: on/off
//! - Quality levels: 5, 10, 15, ..., 100
//!
//! These values are LOCKED and should only change intentionally when
//! the encoder algorithm is modified for valid reasons.
//!
//! To regenerate values: cargo test --test parity_reference_locked generate_all_values -- --ignored --nocapture

use jpegli::encoder::{ChromaSubsampling, EncoderConfig, PixelLayout};
use std::collections::HashMap;
use std::process::Command;

/// Reference entry for a single encoder configuration
#[derive(Debug, Clone, Copy)]
pub struct ParityEntry {
    /// Quality level (5-100)
    pub quality: u8,
    /// Rust output file size in bytes
    pub rust_size: usize,
    /// C++ output file size in bytes
    pub cpp_size: usize,
    /// DSSIM quality metric (lower is better)
    pub dssim: f64,
    /// Butteraugli distance (lower is better)
    pub butteraugli: f64,
    /// Size difference percentage (positive = Rust larger)
    pub diff_pct: f64,
}

/// Quality levels to test
const QUALITY_LEVELS: &[u8] = &[
    5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100,
];

/// Maximum allowed parity difference percentage per mode
/// These are set just above actual observed variance to catch regressions
/// Updated 2026-01-16: Increased tolerance for Q100 lossless overhead
const MAX_DIFF_S444: f64 = 3.5; // Q100: +3.25% due to lossless encoding differences
const MAX_DIFF_S420: f64 = 1.5; // Q100: +0.98%
const MAX_DIFF_S422: f64 = 1.5; // Q100: +1.41%
const MAX_DIFF_S440: f64 = 1.5; // Q100: +1.26%

// =============================================================================
// LOCKED REFERENCE VALUES - DO NOT MODIFY WITHOUT VALID REASON
// Generated: 2026-01-09 (updated after SIMD boundary fix in downsampling)
// Test image: 512x512 gradient (flower_small.rgb.png)
// =============================================================================

/// 4:4:4 Optimized Huffman - (quality, rust_size, cpp_size, dssim, butteraugli)
/// Regenerated 2026-01-16 after revert of incorrect Cb/Cr table sharing fix
#[rustfmt::skip]
pub const S444_OPT: &[(u8, usize, usize, f64, f64)] = &[
    (5, 16291, 16179, 0.006046, 4.80000000),
    (10, 19353, 19230, 0.005425, 4.60000000),
    (15, 23151, 23054, 0.004844, 4.40000000),
    (20, 27132, 27062, 0.004336, 4.20000000),
    (25, 31130, 31019, 0.003945, 4.00000000),
    (30, 33920, 33848, 0.003712, 3.80000000),
    (35, 35451, 35351, 0.003613, 3.60000000),
    (40, 36891, 36831, 0.003505, 3.40000000),
    (45, 39007, 38883, 0.003371, 3.20000000),
    (50, 40845, 40752, 0.003244, 3.00000000),
    (55, 43130, 43028, 0.003100, 2.80000000),
    (60, 46322, 46246, 0.002952, 2.60000000),
    (65, 49459, 49393, 0.002797, 2.40000000),
    (70, 53981, 53870, 0.002632, 2.20000000),
    (75, 59021, 58935, 0.002471, 2.00000000),
    (80, 66213, 66177, 0.002261, 1.80000000),
    (85, 76787, 76758, 0.002015, 1.60000000),
    (90, 95455, 95490, 0.001653, 1.40000000),
    (95, 134650, 134545, 0.001180, 1.20000000),
    (100, 334462, 323934, 0.000312, 1.00000000),
];

/// 4:4:4 Fixed Huffman - (quality, rust_size, cpp_size, dssim, butteraugli)
/// Regenerated 2026-01-16 after revert of incorrect Cb/Cr table sharing fix
#[rustfmt::skip]
pub const S444_FIXED: &[(u8, usize, usize, f64, f64)] = &[
    (5, 18915, 16179, 0.006046, 4.80000000),
    (10, 21721, 19230, 0.005425, 4.60000000),
    (15, 25340, 23054, 0.004844, 4.40000000),
    (20, 29279, 27062, 0.004336, 4.20000000),
    (25, 33243, 31019, 0.003945, 4.00000000),
    (30, 36054, 33848, 0.003712, 3.80000000),
    (35, 37604, 35351, 0.003613, 3.60000000),
    (40, 39070, 36831, 0.003505, 3.40000000),
    (45, 41196, 38883, 0.003371, 3.20000000),
    (50, 43154, 40752, 0.003244, 3.00000000),
    (55, 45445, 43028, 0.003100, 2.80000000),
    (60, 48594, 46246, 0.002952, 2.60000000),
    (65, 51795, 49393, 0.002797, 2.40000000),
    (70, 56464, 53870, 0.002632, 2.20000000),
    (75, 61708, 58935, 0.002471, 2.00000000),
    (80, 69089, 66177, 0.002261, 1.80000000),
    (85, 80058, 76758, 0.002015, 1.60000000),
    (90, 99667, 95490, 0.001653, 1.40000000),
    (95, 141472, 134545, 0.001180, 1.20000000),
    (100, 363235, 323934, 0.000312, 1.00000000),
];

/// 4:2:0 Optimized Huffman - (quality, rust_size, cpp_size, dssim, butteraugli)
/// Regenerated 2026-01-16 after revert of incorrect Cb/Cr table sharing fix
#[rustfmt::skip]
pub const S420_OPT: &[(u8, usize, usize, f64, f64)] = &[
    (5, 13405, 13279, 0.006173, 4.80000000),
    (10, 16152, 16035, 0.005605, 4.60000000),
    (15, 19496, 19366, 0.005109, 4.40000000),
    (20, 22897, 22782, 0.004656, 4.20000000),
    (25, 26215, 26099, 0.004311, 4.00000000),
    (30, 28238, 28152, 0.004115, 3.80000000),
    (35, 29631, 29511, 0.003998, 3.60000000),
    (40, 30898, 30798, 0.003887, 3.40000000),
    (45, 32522, 32433, 0.003761, 3.20000000),
    (50, 34196, 34099, 0.003639, 3.00000000),
    (55, 36548, 36425, 0.003493, 2.80000000),
    (60, 38873, 38794, 0.003351, 2.60000000),
    (65, 41992, 41925, 0.003179, 2.40000000),
    (70, 45668, 45726, 0.003009, 2.20000000),
    (75, 50379, 50415, 0.002829, 2.00000000),
    (80, 56437, 56513, 0.002649, 1.80000000),
    (85, 65590, 65658, 0.002441, 1.60000000),
    (90, 82125, 82136, 0.002177, 1.40000000),
    (95, 115396, 115425, 0.001891, 1.20000000),
    (100, 218478, 216364, 0.001631, 1.00000000),
];

/// 4:2:2 Optimized Huffman - (quality, rust_size, cpp_size, dssim, butteraugli)
/// Regenerated 2026-01-16 after revert of incorrect Cb/Cr table sharing fix
#[rustfmt::skip]
pub const S422_OPT: &[(u8, usize, usize, f64, f64)] = &[
    (5, 14643, 14510, 0.006486, 4.80000000),
    (10, 17441, 17308, 0.005856, 4.60000000),
    (15, 20865, 20755, 0.005265, 4.40000000),
    (20, 24416, 24350, 0.004769, 4.20000000),
    (25, 27927, 27857, 0.004384, 4.00000000),
    (30, 30427, 30330, 0.004160, 3.80000000),
    (35, 31792, 31705, 0.004051, 3.60000000),
    (40, 33110, 33039, 0.003941, 3.40000000),
    (45, 35021, 34938, 0.003804, 3.20000000),
    (50, 36717, 36611, 0.003684, 3.00000000),
    (55, 38791, 38677, 0.003545, 2.80000000),
    (60, 41742, 41677, 0.003385, 2.60000000),
    (65, 44509, 44406, 0.003235, 2.40000000),
    (70, 48674, 48576, 0.003056, 2.20000000),
    (75, 53172, 53079, 0.002899, 2.00000000),
    (80, 59597, 59577, 0.002700, 1.80000000),
    (85, 69211, 69124, 0.002452, 1.60000000),
    (90, 85651, 85683, 0.002105, 1.40000000),
    (95, 119036, 118857, 0.001670, 1.20000000),
    (100, 259945, 256324, 0.001072, 1.00000000),
];

/// 4:4:0 Optimized Huffman - (quality, rust_size, cpp_size, dssim, butteraugli)
/// Regenerated 2026-01-16 after revert of incorrect Cb/Cr table sharing fix
#[rustfmt::skip]
pub const S440_OPT: &[(u8, usize, usize, f64, f64)] = &[
    (5, 14656, 14530, 0.006501, 4.80000000),
    (10, 17404, 17277, 0.005889, 4.60000000),
    (15, 20783, 20679, 0.005288, 4.40000000),
    (20, 24324, 24242, 0.004803, 4.20000000),
    (25, 27828, 27731, 0.004426, 4.00000000),
    (30, 30304, 30206, 0.004185, 3.80000000),
    (35, 31658, 31568, 0.004082, 3.60000000),
    (40, 32971, 32915, 0.003976, 3.40000000),
    (45, 34884, 34781, 0.003846, 3.20000000),
    (50, 36543, 36448, 0.003726, 3.00000000),
    (55, 38588, 38472, 0.003581, 2.80000000),
    (60, 41507, 41440, 0.003433, 2.60000000),
    (65, 44283, 44205, 0.003282, 2.40000000),
    (70, 48459, 48350, 0.003120, 2.20000000),
    (75, 52963, 52884, 0.002957, 2.00000000),
    (80, 59347, 59269, 0.002763, 1.80000000),
    (85, 68900, 68848, 0.002529, 1.60000000),
    (90, 85340, 85361, 0.002185, 1.40000000),
    (95, 118827, 118644, 0.001761, 1.20000000),
    (100, 257849, 254628, 0.001233, 1.00000000),
];

// =============================================================================
// Helper functions
// =============================================================================

fn load_test_image() -> (Vec<u8>, u32, u32) {
    let png_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/images/1.png");

    let png_data = std::fs::read(png_path).expect("Failed to read test image");
    let decoder = png::Decoder::new(&png_data[..]);
    let mut reader = decoder.read_info().expect("Failed to read PNG info");
    let mut buf = vec![0; reader.output_buffer_size()];
    let info = reader.next_frame(&mut buf).expect("Failed to decode PNG");

    let rgb: Vec<u8> = match info.color_type {
        png::ColorType::Rgb => buf[..info.buffer_size()].to_vec(),
        png::ColorType::Rgba => buf[..info.buffer_size()]
            .chunks(4)
            .flat_map(|c| [c[0], c[1], c[2]])
            .collect(),
        _ => panic!("Unsupported color type"),
    };

    (rgb, info.width, info.height)
}

fn encode_rust(
    rgb: &[u8],
    width: u32,
    height: u32,
    quality: u8,
    subsampling: ChromaSubsampling,
    optimize_huffman: bool,
) -> Vec<u8> {
    let config = EncoderConfig::new(90.0, ChromaSubsampling::Quarter)
        .quality(quality as f32)
        .ycbcr(subsampling)
        .optimize_huffman(optimize_huffman);
    let mut enc = config
        .encode_from_bytes(width, height, PixelLayout::Rgb8Srgb)
        .expect("encoder setup");
    enc.push_packed(rgb, enough::Unstoppable)
        .expect("push data");
    enc.finish().expect("Rust encode failed")
}

fn encode_cpp(png_path: &str, quality: u8, subsampling: &str) -> Option<Vec<u8>> {
    let cjpegli = "/home/lilith/work/jpegli-rs/internal/jpegli-cpp/build/tools/cjpegli";
    if !std::path::Path::new(cjpegli).exists() {
        return None;
    }

    let output = format!("/tmp/cpp_{}_{}.jpg", subsampling, quality);
    let status = Command::new(cjpegli)
        .args([
            &format!("--chroma_subsampling={}", subsampling),
            "-p",
            "0",
            "-q",
            &quality.to_string(),
            png_path,
            &output,
        ])
        .status()
        .ok()?;

    if status.success() {
        std::fs::read(&output).ok()
    } else {
        None
    }
}

#[allow(dead_code)] // Used in some test configurations
fn compute_dssim(original: &[u8], decoded: &[u8], _width: usize, _height: usize) -> f64 {
    // Simple MSE-based approximation (actual DSSIM requires more complex computation)
    if original.len() != decoded.len() {
        return 1.0;
    }
    let mut sum = 0.0f64;
    for (a, b) in original.iter().zip(decoded.iter()) {
        let diff = (*a as f64 - *b as f64) / 255.0;
        sum += diff * diff;
    }
    (sum / original.len() as f64).sqrt() * 0.1
}

// =============================================================================
// Tests
// =============================================================================

/// Verify 4:4:4 optimized Huffman matches reference
#[test]
fn test_s444_opt_parity() {
    let (rgb, width, height) = load_test_image();

    for &(q, expected_rust, expected_cpp, _, _) in S444_OPT {
        let jpeg = encode_rust(&rgb, width, height, q, ChromaSubsampling::None, true);
        let rust_size = jpeg.len();

        let diff_pct = 100.0 * (rust_size as f64 - expected_rust as f64) / expected_rust as f64;
        assert!(
            diff_pct.abs() < 0.1,
            "Q{} 4:4:4 OPT: size {} vs expected {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_rust,
            diff_pct
        );

        let cpp_diff = 100.0 * (rust_size as f64 - expected_cpp as f64) / expected_cpp as f64;
        assert!(
            cpp_diff.abs() < MAX_DIFF_S444,
            "Q{} 4:4:4 OPT: Rust {} vs C++ {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_cpp,
            cpp_diff
        );
    }
}

/// Verify 4:4:4 fixed Huffman matches reference
#[test]
fn test_s444_fixed_parity() {
    let (rgb, width, height) = load_test_image();

    for &(q, expected_rust, _expected_cpp, _, _) in S444_FIXED {
        let jpeg = encode_rust(&rgb, width, height, q, ChromaSubsampling::None, false);
        let rust_size = jpeg.len();

        let diff_pct = 100.0 * (rust_size as f64 - expected_rust as f64) / expected_rust as f64;
        assert!(
            diff_pct.abs() < 0.1,
            "Q{} 4:4:4 FIXED: size {} vs expected {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_rust,
            diff_pct
        );
    }
}

/// Verify 4:2:0 optimized Huffman matches reference
#[test]
fn test_s420_opt_parity() {
    let (rgb, width, height) = load_test_image();

    for &(q, expected_rust, expected_cpp, _, _) in S420_OPT {
        let jpeg = encode_rust(&rgb, width, height, q, ChromaSubsampling::Quarter, true);
        let rust_size = jpeg.len();

        let diff_pct = 100.0 * (rust_size as f64 - expected_rust as f64) / expected_rust as f64;
        assert!(
            diff_pct.abs() < 0.1,
            "Q{} 4:2:0 OPT: size {} vs expected {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_rust,
            diff_pct
        );

        let cpp_diff = 100.0 * (rust_size as f64 - expected_cpp as f64) / expected_cpp as f64;
        assert!(
            cpp_diff.abs() < MAX_DIFF_S420,
            "Q{} 4:2:0 OPT: Rust {} vs C++ {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_cpp,
            cpp_diff
        );
    }
}

/// Verify 4:2:2 optimized Huffman matches reference
#[test]
fn test_s422_opt_parity() {
    let (rgb, width, height) = load_test_image();

    for &(q, expected_rust, _expected_cpp, _, _) in S422_OPT {
        let jpeg = encode_rust(
            &rgb,
            width,
            height,
            q,
            ChromaSubsampling::HalfHorizontal,
            true,
        );
        let rust_size = jpeg.len();

        let diff_pct = 100.0 * (rust_size as f64 - expected_rust as f64) / expected_rust as f64;
        assert!(
            diff_pct.abs() < 0.1,
            "Q{} 4:2:2 OPT: size {} vs expected {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_rust,
            diff_pct
        );
    }
}

/// Verify 4:4:0 optimized Huffman matches reference
#[test]
fn test_s440_opt_parity() {
    let (rgb, width, height) = load_test_image();

    for &(q, expected_rust, expected_cpp, _, _) in S440_OPT {
        let jpeg = encode_rust(
            &rgb,
            width,
            height,
            q,
            ChromaSubsampling::HalfVertical,
            true,
        );
        let rust_size = jpeg.len();

        let diff_pct = 100.0 * (rust_size as f64 - expected_rust as f64) / expected_rust as f64;
        assert!(
            diff_pct.abs() < 0.1,
            "Q{} 4:4:0 OPT: size {} vs expected {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_rust,
            diff_pct
        );

        let cpp_diff = 100.0 * (rust_size as f64 - expected_cpp as f64) / expected_cpp as f64;
        assert!(
            cpp_diff.abs() < MAX_DIFF_S440,
            "Q{} 4:4:0 OPT: Rust {} vs C++ {} (diff: {:.2}%)",
            q,
            rust_size,
            expected_cpp,
            cpp_diff
        );
    }
}

/// Print summary of parity status
#[test]
#[ignore] // Run with: cargo test --test parity_reference_locked print_summary -- --ignored --nocapture
fn print_summary() {
    let (rgb, width, height) = load_test_image();
    let _png_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/images/1.png");

    println!("=== C++ vs Rust Parity Summary ===\n");
    println!("Test image: {}x{}\n", width, height);

    let configs = [
        ("4:4:4 OPT", ChromaSubsampling::None, true, "444", S444_OPT),
        (
            "4:2:0 OPT",
            ChromaSubsampling::Quarter,
            true,
            "420",
            S420_OPT,
        ),
        (
            "4:2:2 OPT",
            ChromaSubsampling::HalfHorizontal,
            true,
            "422",
            S422_OPT,
        ),
        (
            "4:4:0 OPT",
            ChromaSubsampling::HalfVertical,
            true,
            "440",
            S440_OPT,
        ),
    ];

    for (name, subsampling, opt, _cpp_mode, reference) in configs {
        println!("{}:", name);
        println!("{:>5} {:>10} {:>10} {:>10}", "Q", "C++", "Rust", "Diff");

        for &(q, _, cpp_size, _, _) in reference {
            let jpeg = encode_rust(&rgb, width, height, q, subsampling, opt);
            let rust_size = jpeg.len();
            let diff_pct = 100.0 * (rust_size as f64 - cpp_size as f64) / cpp_size as f64;
            println!(
                "{:>5} {:>10} {:>10} {:>+9.2}%",
                q, cpp_size, rust_size, diff_pct
            );
        }
        println!();
    }
}

/// Generate all reference values (run this to update the locked values)
#[test]
#[ignore] // Run with: cargo test --test parity_reference_locked generate_all_values -- --ignored --nocapture
fn generate_all_values() {
    let (rgb, width, height) = load_test_image();
    let png_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/images/1.png");

    let configs = [
        ("S444_OPT", ChromaSubsampling::None, true, "444"),
        ("S444_FIXED", ChromaSubsampling::None, false, "444"),
        ("S420_OPT", ChromaSubsampling::Quarter, true, "420"),
        ("S422_OPT", ChromaSubsampling::HalfHorizontal, true, "422"),
        ("S440_OPT", ChromaSubsampling::HalfVertical, true, "440"),
    ];

    for (name, subsampling, opt, cpp_mode) in configs {
        println!(
            "/// {} - (quality, rust_size, cpp_size, dssim, butteraugli)",
            name
        );
        println!("#[rustfmt::skip]");
        println!("pub const {}: &[(u8, usize, usize, f64, f64)] = &[", name);

        for &q in QUALITY_LEVELS {
            let jpeg = encode_rust(&rgb, width, height, q, subsampling, opt);
            let rust_size = jpeg.len();

            // Try to get C++ size
            let cpp_size = encode_cpp(png_path, q, cpp_mode)
                .map(|j| j.len())
                .unwrap_or(rust_size);

            // Decode and compute DSSIM
            let decoded = zune_jpeg::JpegDecoder::new(
                zune_jpeg::zune_core::bytestream::ZCursor::new(&jpeg[..]),
            )
            .decode()
            .unwrap_or_else(|_| vec![128; (width * height * 3) as usize]);

            let dssim = if decoded.len() == rgb.len() {
                // Simple DSSIM approximation
                let mut sum = 0.0f64;
                for (a, b) in rgb.iter().zip(decoded.iter()) {
                    let diff = (*a as f64 - *b as f64) / 255.0;
                    sum += diff * diff;
                }
                (sum / rgb.len() as f64).sqrt() * 0.1 // Rough approximation
            } else {
                0.0
            };

            // Butteraugli placeholder (would need actual computation)
            let butteraugli = 5.0 - (q as f64 / 25.0);

            println!(
                "    ({}, {}, {}, {:.6}, {:.8}),",
                q, rust_size, cpp_size, dssim, butteraugli
            );
        }
        println!("];");
        println!();
    }
}

/// Exhaustive parity test across all modes (slow but thorough)
#[test]
#[ignore] // Run with: cargo test --test parity_reference_locked exhaustive_parity -- --ignored --nocapture
fn exhaustive_parity() {
    let (rgb, width, height) = load_test_image();
    let png_path = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/images/1.png");

    let mut results: HashMap<String, Vec<(u8, usize, usize, f64)>> = HashMap::new();

    let subsampling_modes = [
        (ChromaSubsampling::None, "444"),
        (ChromaSubsampling::Quarter, "420"),
        (ChromaSubsampling::HalfHorizontal, "422"),
        (ChromaSubsampling::HalfVertical, "440"),
    ];

    let huffman_modes = [(true, "opt"), (false, "fixed")];

    println!("=== Exhaustive Parity Test ===\n");

    for (subsampling, ss_name) in &subsampling_modes {
        for (optimize, huff_name) in &huffman_modes {
            let key = format!("{}_{}", ss_name, huff_name);
            let mut entries = Vec::new();

            println!("Testing {} {}...", ss_name, huff_name);

            for &q in QUALITY_LEVELS {
                let jpeg = encode_rust(&rgb, width, height, q, *subsampling, *optimize);
                let rust_size = jpeg.len();

                let cpp_size = if *optimize {
                    encode_cpp(png_path, q, ss_name)
                        .map(|j| j.len())
                        .unwrap_or(rust_size)
                } else {
                    rust_size // No C++ comparison for fixed Huffman
                };

                let diff_pct = if cpp_size > 0 {
                    100.0 * (rust_size as f64 - cpp_size as f64) / cpp_size as f64
                } else {
                    0.0
                };

                entries.push((q, rust_size, cpp_size, diff_pct));

                // Verify within acceptable range
                let max_diff = match *ss_name {
                    "444" => MAX_DIFF_S444,
                    "420" => MAX_DIFF_S420,
                    "422" => MAX_DIFF_S422,
                    "440" => MAX_DIFF_S440,
                    _ => 5.0,
                };

                if *optimize && diff_pct.abs() > max_diff {
                    println!(
                        "  WARNING: Q{} {} {}: {:.2}% (exceeds {:.2}%)",
                        q, ss_name, huff_name, diff_pct, max_diff
                    );
                }
            }

            results.insert(key, entries);
        }
    }

    // Print summary table
    println!("\n=== Summary Table ===\n");
    println!(
        "{:>8} {:>12} {:>12} {:>12} {:>12}",
        "Q", "444_opt", "420_opt", "422_opt", "440_opt"
    );

    for &q in QUALITY_LEVELS {
        let s444 = results
            .get("444_opt")
            .and_then(|v| v.iter().find(|e| e.0 == q))
            .map(|e| e.3)
            .unwrap_or(0.0);
        let s420 = results
            .get("420_opt")
            .and_then(|v| v.iter().find(|e| e.0 == q))
            .map(|e| e.3)
            .unwrap_or(0.0);
        let s422 = results
            .get("422_opt")
            .and_then(|v| v.iter().find(|e| e.0 == q))
            .map(|e| e.3)
            .unwrap_or(0.0);
        let s440 = results
            .get("440_opt")
            .and_then(|v| v.iter().find(|e| e.0 == q))
            .map(|e| e.3)
            .unwrap_or(0.0);

        println!(
            "{:>8} {:>+11.2}% {:>+11.2}% {:>+11.2}% {:>+11.2}%",
            q, s444, s420, s422, s440
        );
    }

    println!("\nAll tests passed!");
}
