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

impl U512 {
    /// Deinterleaves a [`U512`] Morton code into two [`U256`] coordinates.
    ///
    /// Returns `(x, y)` where `x` contains the bits from even positions
    /// (0, 2, 4, ..., 510) and `y` contains the bits from odd positions
    /// (1, 3, 5, ..., 511). This is the inverse of
    /// [`interleave`](U512::interleave):
    /// `U512::interleave(&x, &y).deinterleave() == (x, y)`.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    /// use cnfy_uint::u512::U512;
    ///
    /// let x = U256::from_be_limbs([0, 0, 0, 42]);
    /// let y = U256::from_be_limbs([0, 0, 0, 99]);
    /// let (rx, ry) = U512::interleave(&x, &y).deinterleave();
    /// assert_eq!(rx, x);
    /// assert_eq!(ry, y);
    /// ```
    #[inline]
    pub const fn deinterleave(&self) -> (U256, U256) {
        (self.undilate_even(), self.undilate_odd())
    }
}

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

    /// Deinterleaving zero produces (ZERO, ZERO).
    #[test]
    fn zero() {
        assert_eq!(U512::ZERO.deinterleave(), (U256::ZERO, U256::ZERO));
    }

    /// Round-trip: interleave then deinterleave recovers both coordinates.
    #[test]
    fn round_trip() {
        let x = U256::from_be_limbs([
            0xDEAD_BEEF_CAFE_BABE,
            0x1234_5678_9ABC_DEF0,
            0x0123_4567_89AB_CDEF,
            0xFEDC_BA98_7654_3210,
        ]);
        let y = U256::from_be_limbs([
            0x0011_2233_4455_6677,
            0x8899_AABB_CCDD_EEFF,
            0xFFEE_DDCC_BBAA_9988,
            0x7766_5544_3322_1100,
        ]);
        let (rx, ry) = U512::interleave(&x, &y).deinterleave();
        assert_eq!(rx, x);
        assert_eq!(ry, y);
    }

    /// Deinterleaving an even-only value returns (x, ZERO).
    #[test]
    fn even_only() {
        let x = U256::from_be_limbs([0, 0, 0, 0xCAFE_BABE]);
        let v = x.dilate_even();
        assert_eq!(v.deinterleave(), (x, U256::ZERO));
    }

    /// Deinterleaving an odd-only value returns (ZERO, y).
    #[test]
    fn odd_only() {
        let y = U256::from_be_limbs([0, 0, 0, 0xDEAD_BEEF]);
        let v = y.dilate_odd();
        assert_eq!(v.deinterleave(), (U256::ZERO, y));
    }

    /// Deinterleaving MAX gives (MAX, MAX).
    #[test]
    fn max() {
        assert_eq!(U512::MAX.deinterleave(), (U256::MAX, U256::MAX));
    }

    /// Round-trip with both coordinates at maximum.
    #[test]
    fn round_trip_max() {
        let (rx, ry) = U512::interleave(&U256::MAX, &U256::MAX).deinterleave();
        assert_eq!(rx, U256::MAX);
        assert_eq!(ry, U256::MAX);
    }
}
