//! Morton/Z-order interleaving of two [`U256`] values into a [`U512`].
use super::U512;
use crate::u256::U256;

impl U512 {
    /// Interleaves two [`U256`] coordinates into a single [`U512`] Morton code.
    ///
    /// Bits of `x` occupy even positions (0, 2, 4, ..., 510) and bits of `y`
    /// occupy odd positions (1, 3, 5, ..., 511). This is equivalent to
    /// `x.dilate_even() | y.dilate_odd()`.
    ///
    /// The OR is safe because the two dilations occupy disjoint bit positions.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    /// use cnfy_uint::u512::U512;
    ///
    /// let x = U256::from_be_limbs([0, 0, 0, 0b11]);
    /// let y = U256::from_be_limbs([0, 0, 0, 0b10]);
    /// let z = U512::interleave(&x, &y);
    /// assert_eq!(z, U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 0b1101]));
    /// ```
    #[inline]
    pub const fn interleave(x: &U256, y: &U256) -> U512 {
        let even = x.dilate_even();
        let odd = y.dilate_odd();

        U512([
            even.0[0] | odd.0[0],
            even.0[1] | odd.0[1],
            even.0[2] | odd.0[2],
            even.0[3] | odd.0[3],
            even.0[4] | odd.0[4],
            even.0[5] | odd.0[5],
            even.0[6] | odd.0[6],
            even.0[7] | odd.0[7],
        ])
    }
}

#[cfg(test)]
mod ai_tests {
    use super::*;

    /// Interleaving two zeros produces U512::ZERO.
    #[test]
    fn zero_zero() {
        assert_eq!(U512::interleave(&U256::ZERO, &U256::ZERO), U512::ZERO);
    }

    /// Interleaving x=1, y=0 places bit at position 0.
    #[test]
    fn x_one_y_zero() {
        let x = U256::from_be_limbs([0, 0, 0, 1]);
        assert_eq!(
            U512::interleave(&x, &U256::ZERO),
            x.dilate_even(),
        );
    }

    /// Interleaving x=0, y=1 places bit at position 1.
    #[test]
    fn x_zero_y_one() {
        let y = U256::from_be_limbs([0, 0, 0, 1]);
        assert_eq!(
            U512::interleave(&U256::ZERO, &y),
            y.dilate_odd(),
        );
    }

    /// Manual verification: x=0b11, y=0b10 → 0b1101.
    #[test]
    fn manual_bits() {
        let x = U256::from_be_limbs([0, 0, 0, 0b11]);
        let y = U256::from_be_limbs([0, 0, 0, 0b10]);
        let z = U512::interleave(&x, &y);
        assert_eq!(z, U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 0b1101]));
    }

    /// Interleaving MAX values fills all 512 bits.
    #[test]
    fn max_max() {
        assert_eq!(U512::interleave(&U256::MAX, &U256::MAX), U512::MAX);
    }

    /// Equivalence with explicit dilate-then-OR.
    #[test]
    fn matches_dilate_or() {
        let x = U256::from_be_limbs([0xABCD, 0x1234, 0x5678, 0x9ABC]);
        let y = U256::from_be_limbs([0xDEF0, 0xCAFE, 0xBABE, 0xDEAD]);
        let interleaved = U512::interleave(&x, &y);
        let manual = x.dilate_even() | y.dilate_odd();
        assert_eq!(interleaved, manual);
    }
}
