clock-curve-math 1.1.3

High-performance, constant-time, cryptography-grade number theory library for ClockCurve ecosystem
Documentation
//! Comprehensive tests for SIMD preparation utilities.
//!
//! This module tests the SIMD infrastructure and data structures
//! for future vectorized cryptographic operations.

#[cfg(feature = "simd")]
mod tests {
    use clock_curve_math::simd::*;

    #[test]
    fn test_simd_limbs_creation_and_conversion() {
        // Test with zeros
        let zero_limbs = [0u64; 4];
        let zero_simd = SimdLimbs::from_limbs(&zero_limbs);
        assert_eq!(zero_simd.to_limbs(), zero_limbs);

        // Test with various values
        let test_limbs = [1u64, 2u64, 3u64, 4u64];
        let test_simd = SimdLimbs::from_limbs(&test_limbs);
        assert_eq!(test_simd.to_limbs(), test_limbs);

        // Test with maximum values
        let max_limbs = [u64::MAX; 4];
        let max_simd = SimdLimbs::from_limbs(&max_limbs);
        assert_eq!(max_simd.to_limbs(), max_limbs);
    }

    #[test]
    fn test_simd_limbs_debug_and_clone() {
        let limbs = [42u64, 123u64, 999u64, 0u64];
        let original = SimdLimbs::from_limbs(&limbs);
        let cloned = original.clone();

        // Test Clone
        assert_eq!(original, cloned);
        assert_eq!(original.to_limbs(), cloned.to_limbs());

        // Test Debug formatting (should not panic)
        let debug_str = format!("{:?}", original);
        assert!(debug_str.contains("SimdLimbs"));
        assert!(debug_str.contains("limbs"));
    }

    #[test]
    fn test_simd_limbs_equality() {
        let limbs1 = [1u64, 2, 3, 4];
        let limbs2 = [1u64, 2, 3, 4];
        let limbs3 = [1u64, 2, 3, 5];

        let simd1 = SimdLimbs::from_limbs(&limbs1);
        let simd2 = SimdLimbs::from_limbs(&limbs2);
        let simd3 = SimdLimbs::from_limbs(&limbs3);

        // Test equality
        assert_eq!(simd1, simd2);
        assert_eq!(simd1.to_limbs(), simd2.to_limbs());

        // Test inequality
        assert_ne!(simd1, simd3);
        assert_ne!(simd1.to_limbs(), simd3.to_limbs());
    }

    #[test]
    fn test_simd_support_enum_variants() {
        // Test all enum variants
        assert_eq!(SimdSupport::None, SimdSupport::None);
        assert_eq!(SimdSupport::Sse41, SimdSupport::Sse41);
        assert_eq!(SimdSupport::Avx256, SimdSupport::Avx256);
        assert_eq!(SimdSupport::Avx512, SimdSupport::Avx512);
        assert_eq!(SimdSupport::Neon, SimdSupport::Neon);

        // Test Debug formatting
        assert!(format!("{:?}", SimdSupport::None).contains("None"));
        assert!(format!("{:?}", SimdSupport::Avx256).contains("Avx256"));
    }

    #[test]
    fn test_simd_support_detection_safety() {
        // Test that detection doesn't panic and returns valid values
        let support = SimdLimbs::detect_simd_support();

        // Should be one of the valid enum variants
        match support {
            SimdSupport::None
            | SimdSupport::Sse41
            | SimdSupport::Avx256
            | SimdSupport::Avx512
            | SimdSupport::Neon => {
                // Valid variant
            }
        }

        // Test multiple calls return consistent results
        let support2 = SimdLimbs::detect_simd_support();
        assert_eq!(support, support2);
    }

    #[test]
    fn test_simd_support_clone_and_copy() {
        let support = SimdSupport::Avx256;
        let cloned = support.clone();
        let copied = support;

        assert_eq!(support, cloned);
        assert_eq!(support, copied);
        assert_eq!(cloned, copied);
    }

    #[cfg(feature = "alloc")]
    mod alloc_tests {
        use super::*;
        use clock_curve_math::FieldElement;

        #[test]
        fn test_simd_batch_creation() {
            let batch = SimdBatch::<u64>::new();
            assert_eq!(batch.as_slice().len(), 0);
            assert!(matches!(
                batch.simd_support(),
                SimdSupport::None
                    | SimdSupport::Sse41
                    | SimdSupport::Avx256
                    | SimdSupport::Avx512
                    | SimdSupport::Neon
            ));
        }

        #[test]
        fn test_simd_batch_with_capacity() {
            let capacity = 100;
            let batch = SimdBatch::<u64>::with_capacity(capacity);
            assert_eq!(batch.as_slice().len(), 0);
            // Note: We can't directly test the capacity, but the batch should be usable
        }

        #[test]
        fn test_simd_batch_push_and_access() {
            let mut batch = SimdBatch::new();

            // Push some elements
            batch.push(42u64);
            batch.push(123u64);
            batch.push(999u64);

            // Check contents
            let slice = batch.as_slice();
            assert_eq!(slice.len(), 3);
            assert_eq!(slice[0], 42u64);
            assert_eq!(slice[1], 123u64);
            assert_eq!(slice[2], 999u64);
        }

        #[test]
        fn test_simd_batch_with_field_elements() {
            let mut batch = SimdBatch::new();

            let elem1 = FieldElement::from_u64(1);
            let elem2 = FieldElement::from_u64(2);
            let elem3 = FieldElement::from_u64(3);

            batch.push(elem1.clone());
            batch.push(elem2.clone());
            batch.push(elem3.clone());

            let slice = batch.as_slice();
            assert_eq!(slice.len(), 3);
            assert_eq!(slice[0], elem1);
            assert_eq!(slice[1], elem2);
            assert_eq!(slice[2], elem3);
        }

        #[test]
        fn test_simd_batch_clone() {
            let mut original = SimdBatch::new();
            original.push(42u64);
            original.push(24u64);

            let cloned = original.clone();

            // Should have same contents
            assert_eq!(original.as_slice(), cloned.as_slice());
            assert_eq!(original.simd_support(), cloned.simd_support());

            // Modifying original shouldn't affect clone
            original.push(100u64);
            assert_ne!(original.as_slice().len(), cloned.as_slice().len());
        }

        #[test]
        fn test_simd_batch_debug_formatting() {
            let mut batch = SimdBatch::new();
            batch.push(1u64);
            batch.push(2u64);

            let debug_str = format!("{:?}", batch);
            assert!(debug_str.contains("SimdBatch"));
            // Note: Exact format may vary, just ensure it doesn't panic
        }
    }

    #[test]
    fn test_prepare_montgomery_mul_simd() {
        use clock_curve_math::{BigInt, montgomery};

        let a = BigInt::from_u64(12345);
        let b = BigInt::from_u64(67890);
        let modulus = BigInt::from_u64(7919); // Prime modulus
        let n_prime = montgomery::compute_n_prime(&modulus);

        // Test that the function doesn't panic and returns a result
        let result = prepare_montgomery_mul_simd(&a, &b, &modulus, &n_prime);

        // Should produce some result (exact value depends on implementation)
        assert!(result >= BigInt::from_u64(0));
        assert!(result < modulus);

        // Compare with direct montgomery_mul call
        let expected = montgomery::montgomery_mul(&a, &b, &modulus, &n_prime);
        assert_eq!(result, expected);
    }

    #[cfg(feature = "alloc")]
    #[test]
    fn test_prepare_batch_inverse_simd() {
        use clock_curve_math::FieldElement;

        let elements = vec![
            FieldElement::from_u64(1),
            FieldElement::from_u64(2),
            FieldElement::from_u64(3),
        ];

        // Test that the function doesn't panic
        let result = prepare_batch_inverse_simd(&elements);

        // Should return Some result for valid inputs
        assert!(result.is_some());

        let inverses = result.unwrap();
        assert_eq!(inverses.inverses.len(), 3);

        // Verify correctness: element * inverse = 1
        for (i, inv) in inverses.inverses.iter().enumerate() {
            assert_eq!(elements[i].mul(inv), FieldElement::from_u64(1));
        }

        // Compare with direct batch_inverse call
        let expected = clock_curve_math::field::batch_inverse(&elements);
        assert!(expected.is_some());
        let expected_inverses = expected.unwrap();
        assert_eq!(inverses.inverses, expected_inverses.inverses);
    }

    #[cfg(feature = "alloc")]
    mod perf_tests {
        use super::*;

        #[test]
        fn test_perf_result_creation() {
            use core::time::Duration;

            let duration = Duration::from_nanos(1000);
            let operation = "test_operation";
            let result = perf::PerfResult::new(duration, operation);

            assert_eq!(result.duration, duration);
            assert_eq!(result.operation, operation);
            assert!(matches!(
                result.simd_level,
                SimdSupport::None
                    | SimdSupport::Sse41
                    | SimdSupport::Avx256
                    | SimdSupport::Avx512
                    | SimdSupport::Neon
            ));
        }

        #[test]
        fn test_perf_result_debug() {
            use core::time::Duration;

            let result = perf::PerfResult::new(Duration::from_micros(500), "test");
            let debug_str = format!("{:?}", result);
            assert!(debug_str.contains("PerfResult"));
            assert!(debug_str.contains("test"));
        }

        #[test]
        fn test_simd_benchmark_creation() {
            let benchmark = perf::SimdBenchmark::new();
            // Should not panic and have empty results
        }

        #[test]
        fn test_simd_benchmark_operations() {
            use core::time::Duration;

            let mut benchmark = perf::SimdBenchmark::new();

            let result1 = perf::PerfResult::new(Duration::from_nanos(1000), "scalar");
            let result2 = perf::PerfResult::new(Duration::from_nanos(500), "simd");

            benchmark.record(result1);
            benchmark.record(result2);

            // Test improvement ratio calculation
            let ratio = benchmark.improvement_ratio("scalar", "simd");
            assert!(ratio.is_some());
            assert_eq!(ratio.unwrap(), 2.0); // 1000ns / 500ns = 2.0
        }

        #[test]
        fn test_simd_benchmark_missing_operations() {
            use core::time::Duration;

            let mut benchmark = perf::SimdBenchmark::new();
            benchmark.record(perf::PerfResult::new(Duration::from_nanos(1000), "scalar"));

            // Should return None when SIMD operation is missing
            let ratio = benchmark.improvement_ratio("scalar", "missing_simd");
            assert!(ratio.is_none());

            // Should return None when scalar operation is missing
            let ratio2 = benchmark.improvement_ratio("missing_scalar", "simd");
            assert!(ratio2.is_none());
        }

        #[test]
        fn test_simd_benchmark_clone() {
            use core::time::Duration;

            let mut original = perf::SimdBenchmark::new();
            original.record(perf::PerfResult::new(Duration::from_nanos(1000), "test"));

            let cloned = original.clone();
            // Should have same structure (though we can't easily test internal state)
        }
    }

    #[test]
    fn test_simd_alignment() {
        let limbs = [1u64, 2, 3, 4];
        let simd = SimdLimbs::from_limbs(&limbs);

        // Test that the struct is properly aligned
        // We can't directly test alignment at compile time, but we can ensure
        // the functionality works as expected
        let converted_back = simd.to_limbs();
        assert_eq!(limbs, converted_back);
    }

    #[cfg(feature = "alloc")]
    #[test]
    fn test_simd_batch_empty_operations() {
        let batch = SimdBatch::<u64>::new();

        // Should handle empty batch gracefully
        assert_eq!(batch.as_slice().len(), 0);

        let empty_slice = batch.as_slice();
        assert_eq!(empty_slice, &[]);
    }

    #[test]
    fn test_simd_constant_time_considerations() {
        // Test that SIMD detection is consistent (important for constant-time)
        let support1 = SimdLimbs::detect_simd_support();
        let support2 = SimdLimbs::detect_simd_support();
        let support3 = SimdLimbs::detect_simd_support();

        // Should be consistent across calls
        assert_eq!(support1, support2);
        assert_eq!(support2, support3);
    }
}