//! Checked addition for [`U512`].
use super::U512;

impl U512 {
    /// Checked 512-bit addition, returning `None` on overflow.
    ///
    /// Computes `self + other` via [`overflowing_add`](U512::overflowing_add).
    /// Returns `Some(result)` if the sum fits in 512 bits, or `None` if
    /// the addition would overflow.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u512::U512;
    ///
    /// let a = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 10]);
    /// let b = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 20]);
    /// assert_eq!(a.checked_add(&b), Some(U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 30])));
    /// assert_eq!(U512::MAX.checked_add(&U512::ONE), None);
    /// ```
    #[inline]
    pub const fn checked_add(&self, other: &U512) -> Option<U512> {
        let (result, overflow) = self.overflowing_add(other);
        if overflow {
            None
        } else {
            Some(result)
        }
    }
}

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

    /// Small values add without overflow.
    #[test]
    fn small_add() {
        let a = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 100]);
        let b = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 200]);
        assert_eq!(
            a.checked_add(&b),
            Some(U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 300])),
        );
    }

    /// MAX + 1 overflows to None.
    #[test]
    fn max_plus_one() {
        assert_eq!(U512::MAX.checked_add(&U512::ONE), None);
    }

    /// MAX + MAX overflows to None.
    #[test]
    fn max_plus_max() {
        assert_eq!(U512::MAX.checked_add(&U512::MAX), None);
    }

    /// Zero is the additive identity.
    #[test]
    fn additive_identity() {
        let a = U512::from_be_limbs([0x1234, 0x5678, 0x9ABC, 0xDEF0, 1, 2, 3, 4]);
        assert_eq!(a.checked_add(&U512::ZERO), Some(a));
    }

    /// Carry across all limbs without overflow.
    #[test]
    fn carry_no_overflow() {
        let a = U512::from_be_limbs([0, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
        let b = U512::ONE;
        assert_eq!(
            a.checked_add(&b),
            Some(U512::from_be_limbs([1, 0, 0, 0, 0, 0, 0, 0])),
        );
    }

    /// Just below overflow boundary succeeds.
    #[test]
    fn just_below_overflow() {
        let a = U512::from_be_limbs([u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX, u64::MAX - 1]);
        assert_eq!(a.checked_add(&U512::ONE), Some(U512::MAX));
    }
}
