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

impl U512 {
    /// Extracts bits at odd positions (1, 3, 5, ..., 511) and compacts them
    /// into a [`U256`].
    ///
    /// Bit `2i+1` of the [`U512`] maps to bit `i` of the result. This is the
    /// inverse of [`U256::dilate_odd`]:
    /// `v.dilate_odd().undilate_odd() == v`.
    ///
    /// Internally shifts each limb right by 1 to move odd positions into even
    /// positions, then delegates to [`undilate_even`](U512::undilate_even).
    ///
    /// # 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_odd().undilate_odd(), v);
    /// ```
    #[inline]
    pub const fn undilate_odd(&self) -> U256 {
        let shifted = U512([
            self.0[0] >> 1,
            self.0[1] >> 1,
            self.0[2] >> 1,
            self.0[3] >> 1,
            self.0[4] >> 1,
            self.0[5] >> 1,
            self.0[6] >> 1,
            self.0[7] >> 1,
        ]);
        shifted.undilate_even()
    }
}

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

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

    /// Round-trip: dilate odd then undilate odd recovers the original.
    #[test]
    fn round_trip_one() {
        let v = U256::from_be_limbs([0, 0, 0, 1]);
        assert_eq!(v.dilate_odd().undilate_odd(), 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_odd().undilate_odd(), v);
    }

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

    /// Even-dilated value undilates to zero from odd positions.
    #[test]
    fn even_input_gives_zero() {
        let v = U256::MAX.dilate_even();
        assert_eq!(v.undilate_odd(), U256::ZERO);
    }

    /// Bit 511 (odd) → bit 255 of U256.
    #[test]
    fn bit_511_to_255() {
        let v = U256::from_be_limbs([1u64 << 63, 0, 0, 0]);
        assert_eq!(v.dilate_odd().undilate_odd(), v);
    }
}
