//! Morton undilation extracting even bit positions from a [`U512`] into a [`U256`].
use super::U512;
use crate::u256::U256;

impl U512 {
    /// Extracts bits at even positions (0, 2, 4, ..., 510) and compacts them
    /// into a [`U256`].
    ///
    /// Bit `2i` of the [`U512`] maps to bit `i` of the result. This is the
    /// inverse of [`U256::dilate_even`]:
    /// `v.dilate_even().undilate_even() == v`.
    ///
    /// Each pair of adjacent `u64` limbs undilates into one `u64` limb of the
    /// resulting [`U256`]: the even limb contributes the low `u32` and the odd
    /// limb contributes the high `u32`.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    /// use cnfy_uint::u512::U512;
    ///
    /// let v = U256::from_be_limbs([0, 0, 0, 0xDEAD_BEEF]);
    /// assert_eq!(v.dilate_even().undilate_even(), v);
    /// ```
    #[inline]
    pub const fn undilate_even(&self) -> U256 {
        let q0 = U256::undilate_u64_to_u32(self.0[0]);
        let q1 = U256::undilate_u64_to_u32(self.0[1]);
        let q2 = U256::undilate_u64_to_u32(self.0[2]);
        let q3 = U256::undilate_u64_to_u32(self.0[3]);
        let q4 = U256::undilate_u64_to_u32(self.0[4]);
        let q5 = U256::undilate_u64_to_u32(self.0[5]);
        let q6 = U256::undilate_u64_to_u32(self.0[6]);
        let q7 = U256::undilate_u64_to_u32(self.0[7]);

        // U512 limb[2i] low u32 and limb[2i+1] high u32 → U256 limb[i]
        let w0 = (q0 as u64) | ((q1 as u64) << 32);
        let w1 = (q2 as u64) | ((q3 as u64) << 32);
        let w2 = (q4 as u64) | ((q5 as u64) << 32);
        let w3 = (q6 as u64) | ((q7 as u64) << 32);

        U256::from_be_limbs([w3, w2, w1, w0])
    }
}

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

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

    /// Round-trip: dilate then undilate recovers the original.
    #[test]
    fn round_trip_one() {
        let v = U256::from_be_limbs([0, 0, 0, 1]);
        assert_eq!(v.dilate_even().undilate_even(), v);
    }

    /// Round-trip with a value spanning all four limbs.
    #[test]
    fn round_trip_full() {
        let v = U256::from_be_limbs([
            0xDEAD_BEEF_CAFE_BABE,
            0x1234_5678_9ABC_DEF0,
            0x0123_4567_89AB_CDEF,
            0xFEDC_BA98_7654_3210,
        ]);
        assert_eq!(v.dilate_even().undilate_even(), v);
    }

    /// Round-trip with U256::MAX.
    #[test]
    fn round_trip_max() {
        assert_eq!(U256::MAX.dilate_even().undilate_even(), U256::MAX);
    }

    /// Odd-dilated value undilates to zero from even positions.
    #[test]
    fn odd_input_gives_zero() {
        let v = U256::MAX.dilate_odd();
        assert_eq!(v.undilate_even(), U256::ZERO);
    }

    /// Bit 510 (even) → bit 255 of U256.
    #[test]
    fn bit_510_to_255() {
        let v = U256::from_be_limbs([1u64 << 63, 0, 0, 0]);
        assert_eq!(v.dilate_even().undilate_even(), v);
    }
}
