cnfy-uint 0.2.3

Zero-dependency 256-bit unsigned integer arithmetic for cryptographic applications
Documentation
//! Morton dilation of a [`U256`] into odd bit positions of a [`U512`].
use super::U256;
use crate::u512::U512;

impl U256 {
    /// Dilates this [`U256`] into the odd bit positions (1, 3, 5, ..., 511)
    /// of a [`U512`].
    ///
    /// Each bit `i` of the input maps to bit `2i+1` of the result. Even bit
    /// positions in the output are all zero. Combined with
    /// [`dilate_even`](U256::dilate_even), this enables Morton/Z-order
    /// interleaving at the U256→U512 level.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    /// use cnfy_uint::u512::U512;
    ///
    /// let v = U256::from_be_limbs([0, 0, 0, 1]);
    /// let dilated = v.dilate_odd();
    /// assert_eq!(dilated, U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 2]));
    /// ```
    #[inline]
    pub const fn dilate_odd(&self) -> U512 {
        let w0 = Self::dilate_u32_to_u64(self.0[0] as u32) << 1;
        let w1 = Self::dilate_u32_to_u64((self.0[0] >> 32) as u32) << 1;
        let w2 = Self::dilate_u32_to_u64(self.0[1] as u32) << 1;
        let w3 = Self::dilate_u32_to_u64((self.0[1] >> 32) as u32) << 1;
        let w4 = Self::dilate_u32_to_u64(self.0[2] as u32) << 1;
        let w5 = Self::dilate_u32_to_u64((self.0[2] >> 32) as u32) << 1;
        let w6 = Self::dilate_u32_to_u64(self.0[3] as u32) << 1;
        let w7 = Self::dilate_u32_to_u64((self.0[3] >> 32) as u32) << 1;

        U512([w0, w1, w2, w3, w4, w5, w6, w7])
    }
}

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

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

    /// Bit 0 maps to position 1.
    #[test]
    fn bit_0() {
        let v = U256::from_be_limbs([0, 0, 0, 1]);
        assert_eq!(v.dilate_odd(), U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 2]));
    }

    /// Bit 31 maps to position 63 (U512 limb 0, bit 63).
    #[test]
    fn bit_31() {
        let v = U256::from_be_limbs([0, 0, 0, 1u64 << 31]);
        assert_eq!(
            v.dilate_odd(),
            U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 1u64 << 63]),
        );
    }

    /// Bit 32 maps to position 65 (U512 limb 1, bit 1).
    #[test]
    fn bit_32() {
        let v = U256::from_be_limbs([0, 0, 0, 1u64 << 32]);
        assert_eq!(
            v.dilate_odd(),
            U512::from_be_limbs([0, 0, 0, 0, 0, 0, 2, 0]),
        );
    }

    /// Bit 255 maps to position 511 (U512 limb 7, bit 63).
    #[test]
    fn bit_255() {
        let v = U256::from_be_limbs([1u64 << 63, 0, 0, 0]);
        assert_eq!(
            v.dilate_odd(),
            U512::from_be_limbs([1u64 << 63, 0, 0, 0, 0, 0, 0, 0]),
        );
    }

    /// All bits set: every odd position in U512 should be 1.
    #[test]
    fn all_bits_set() {
        let v = U256::MAX.dilate_odd();
        for i in 0..8 {
            assert_eq!(v.0[i], 0xAAAA_AAAA_AAAA_AAAA);
        }
    }

    /// Even and odd dilations are disjoint.
    #[test]
    fn orthogonal_to_even() {
        let even = U256::MAX.dilate_even();
        let odd = U256::MAX.dilate_odd();
        assert_eq!(even & odd, U512::ZERO);
    }

    /// Even | odd fills all 512 bits.
    #[test]
    fn even_or_odd_is_max() {
        let even = U256::MAX.dilate_even();
        let odd = U256::MAX.dilate_odd();
        assert_eq!(even | odd, U512::MAX);
    }
}