clock-hash 1.0.0

ClockHash-256: Consensus hash function for ClockinChain
Documentation
//! Scalar fallback implementations for ClockHash operations
//!
//! This module provides scalar (non-SIMD) implementations of ClockMix operations
//! that serve as fallbacks when SIMD is not available or not requested.

/// Scalar fallback implementation of ClockMix
#[inline]
pub fn scalar_clock_mix(message: &mut [u64; 16]) {
    use crate::constants::{ROTATION_SCHEDULE, SBOX};
    use crate::utils::rotl64;

    // Prefetch S-box table for better cache performance
    #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
    unsafe {
        use core::arch::x86_64::_mm_prefetch;
        _mm_prefetch(SBOX.as_ptr() as *const i8, core::arch::x86_64::_MM_HINT_T0);
    }

    // Step 1: XOR with rotated neighbor
    // Process in order to minimize cache misses and improve pipeline utilization
    for i in 0..16 {
        let next_idx = (i + 1) % 16;
        let rotation = ROTATION_SCHEDULE[i];
        message[i] ^= rotl64(message[next_idx], rotation);
    }

    // Step 2: Add S-box value
    // Process in order with prefetching to minimize cache misses
    for i in 0..16 {
        let sbox_idx = (message[i] & 0xFF) as usize;
        message[i] = message[i].wrapping_add(SBOX[sbox_idx] as u64);
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::constants::SBOX;

    #[test]
    fn test_scalar_clock_mix_basic() {
        let mut message = [0u64; 16];
        for i in 0..16 {
            message[i] = i as u64;
        }

        scalar_clock_mix(&mut message);

        // Verify that the message was modified
        assert_ne!(message, [0u64; 16]);
        let identity_array: [u64; 16] = (0..16)
            .map(|i| i as u64)
            .collect::<Vec<_>>()
            .try_into()
            .unwrap();
        assert_ne!(message, identity_array);
    }

    #[test]
    fn test_scalar_clock_mix_invertibility() {
        // Test that scalar_clock_mix is not trivially invertible
        let original = [0x123456789ABCDEF0u64; 16];
        let mut modified = original.clone();

        scalar_clock_mix(&mut modified);

        // Should be different from original
        assert_ne!(original, modified);

        // And different from all-zeros
        assert_ne!(modified, [0u64; 16]);
    }

    #[test]
    fn test_scalar_clock_mix_deterministic() {
        let mut msg1 = [0xFEDCBA9876543210u64; 16];
        let mut msg2 = [0xFEDCBA9876543210u64; 16];

        scalar_clock_mix(&mut msg1);
        scalar_clock_mix(&mut msg2);

        assert_eq!(msg1, msg2);
    }

    #[test]
    fn test_scalar_clock_mix_edge_cases() {
        // Test with all zeros - should be transformed by S-box
        let mut msg1 = [0u64; 16];
        scalar_clock_mix(&mut msg1);
        // All zeros will become SBOX[0] values (73) due to S-box addition
        assert_ne!(msg1, [0u64; 16]);
        for &val in &msg1 {
            assert_eq!(val, SBOX[0] as u64);
        }

        // Test with all ones
        let mut msg1 = [u64::MAX; 16];
        let mut msg2 = [u64::MAX; 16];
        scalar_clock_mix(&mut msg1);
        scalar_clock_mix(&mut msg2);
        assert_eq!(msg1, msg2);

        // Test with alternating pattern
        let mut msg1 = [0u64; 16];
        let mut msg2 = [0u64; 16];
        for i in 0..16 {
            let val = if i % 2 == 0 {
                0xAAAAAAAAAAAAAAAA
            } else {
                0x5555555555555555
            };
            msg1[i] = val;
            msg2[i] = val;
        }
        scalar_clock_mix(&mut msg1);
        scalar_clock_mix(&mut msg2);
        assert_eq!(msg1, msg2);
    }

    #[test]
    fn test_large_value_handling() {
        // Test with values that might cause overflow issues
        let mut msg1 = [u64::MAX; 16];
        let mut msg2 = [u64::MAX; 16];

        scalar_clock_mix(&mut msg1);
        scalar_clock_mix(&mut msg2);

        assert_eq!(msg1, msg2, "Should handle u64::MAX correctly");
    }

    #[test]
    fn test_rotation_schedule_coverage() {
        use crate::constants::ROTATION_SCHEDULE;

        // Verify that all rotation values are within reasonable bounds (0-63 for u64)
        for &rotation in ROTATION_SCHEDULE.iter() {
            assert!(rotation <= 63, "Rotation {} is out of bounds", rotation);
        }

        // Verify we have the expected number of rotations
        assert_eq!(
            ROTATION_SCHEDULE.len(),
            16,
            "Expected 16 rotation values, got {}",
            ROTATION_SCHEDULE.len()
        );
    }

    #[test]
    fn test_sbox_table_properties() {
        use crate::constants::SBOX;

        // Verify SBOX has correct size (256 entries)
        assert_eq!(SBOX.len(), 256, "SBOX should have 256 entries");

        // SBOX values are guaranteed to be within u8 range by the type system

        // Verify SBOX has some variation (not all zeros or all same value)
        let first_value = SBOX[0];
        let all_same = SBOX.iter().all(|&v| v == first_value);
        assert!(!all_same, "SBOX should not contain all identical values");

        let all_zero = SBOX.iter().all(|&v| v == 0);
        assert!(!all_zero, "SBOX should not contain all zeros");
    }
}