//! Population count (Hamming weight) for [`U256`].
use super::U256;

impl U256 {
    /// Returns the number of ones in the binary representation.
    ///
    /// Delegates to hardware `u64::count_ones` (POPCNT instruction) on each
    /// limb. Useful for Hamming weight/distance in cryptographic analysis.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    ///
    /// assert_eq!(U256::ZERO.count_ones(), 0);
    /// assert_eq!(U256::ONE.count_ones(), 1);
    /// assert_eq!(U256::MAX.count_ones(), 256);
    /// ```
    #[inline]
    pub const fn count_ones(&self) -> u32 {
        self.0[0].count_ones()
            + self.0[1].count_ones()
            + self.0[2].count_ones()
            + self.0[3].count_ones()
    }
}

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

    /// Zero has no bits set.
    #[test]
    fn zero() {
        assert_eq!(U256::ZERO.count_ones(), 0);
    }

    /// One has exactly one bit set.
    #[test]
    fn one() {
        assert_eq!(U256::ONE.count_ones(), 1);
    }

    /// MAX has all 256 bits set.
    #[test]
    fn max() {
        assert_eq!(U256::MAX.count_ones(), 256);
    }

    /// Each limb contributes independently.
    #[test]
    fn per_limb() {
        let val = U256::from_be_limbs([0b111, 0b11, 0b1, 0b0]);
        assert_eq!(val.count_ones(), 3 + 2 + 1 + 0);
    }

    /// Powers of two have exactly one bit.
    #[test]
    fn powers_of_two() {
        for shift in 0..64 {
            let val = U256::from_be_limbs([0, 0, 0, 1u64 << shift]);
            assert_eq!(val.count_ones(), 1);
        }
    }

    /// Known bit pattern across multiple limbs.
    #[test]
    fn mixed_pattern() {
        let val = U256::from_be_limbs([u64::MAX, 0, u64::MAX, 0]);
        assert_eq!(val.count_ones(), 128);
    }
}
