spirix 0.1.0

Two's complement floating-point arithmetic library
Documentation
use crate::core::integer::*;
use crate::core::undefined::*;
use crate::{Circle, CircleConstants, Integer, Scalar, ScalarConstants};
use core::ops::*;
use i256::I256;
use num_traits::{AsPrimitive, WrappingAdd, WrappingMul, WrappingNeg, WrappingSub};
#[allow(private_bounds)]
impl<
        F: Integer
            + FullInt
            + Shl<isize, Output = F>
            + Shr<isize, Output = F>
            + Shl<F, Output = F>
            + Shr<F, Output = F>
            + Shl<E, Output = F>
            + Shr<E, Output = F>
            + WrappingNeg
            + WrappingAdd
            + WrappingMul
            + WrappingSub,
        E: Integer
            + FullInt
            + Shl<isize, Output = E>
            + Shr<isize, Output = E>
            + Shl<E, Output = E>
            + Shr<E, Output = E>
            + Shl<F, Output = E>
            + Shr<F, Output = E>
            + WrappingNeg
            + WrappingAdd
            + WrappingMul
            + WrappingSub,
    > Scalar<F, E>
where
    Circle<F, E>: CircleConstants,
    Scalar<F, E>: ScalarConstants,
    u8: AsPrimitive<F>,
    u16: AsPrimitive<F>,
    u32: AsPrimitive<F>,
    u64: AsPrimitive<F>,
    u128: AsPrimitive<F>,
    usize: AsPrimitive<F>,
    i8: AsPrimitive<F>,
    i16: AsPrimitive<F>,
    i32: AsPrimitive<F>,
    i64: AsPrimitive<F>,
    i128: AsPrimitive<F>,
    isize: AsPrimitive<F>,
    I256: From<F>,
    u8: AsPrimitive<E>,
    u16: AsPrimitive<E>,
    u32: AsPrimitive<E>,
    u64: AsPrimitive<E>,
    u128: AsPrimitive<E>,
    usize: AsPrimitive<E>,
    i8: AsPrimitive<E>,
    i16: AsPrimitive<E>,
    i32: AsPrimitive<E>,
    i64: AsPrimitive<E>,
    i128: AsPrimitive<E>,
    isize: AsPrimitive<E>,
    I256: From<E>,
{
    /// Adds this Scalar to a Circle
    ///
    /// # Description
    ///
    /// Performs addition between a Scalar and a Circle, adding the Scalar value to the real component of the Circle while leaving the imaginary component unchanged (except for normalization). Returns a finite Circle unless the result exceeds representable range, in which case it may return an exploded or vanished Circle.
    ///
    /// Addition process:
    /// 0. Checks for any escaped (vanished, exploded, infinity, or undefined) values and handles these first
    /// 1. Checks for Zeros and returns the appropriate result
    /// 2. Aligns fractions by shifting the smaller value right based on exponent difference
    /// 3. Adds the Scalar to the real component of the Circle
    /// 4. Normalizes result and adjusts exponent, considering both components
    /// 5. Escapes for underflow if necessary, overflow is naturally handled by escaped exponent alignment
    ///
    /// # Returns
    ///
    /// - `[℘ ]` ➔ `[℘ ]` First undefined state encountered
    /// - `[∞]` + `[∞]` ➔ `[℘ ↑+↑]` Undefined exploded plus exploded state
    /// - `[∞]` + `[#]` ➔ `[℘ ↑+]` Undefined exploded plus finite state
    /// - `[#]` + `[∞]` ➔ `[℘ +↑]` Undefined finite plus exploded state
    /// - `[↑]` + `[↑]` ➔ `[℘ ↑+↑]` Undefined exploded plus exploded state
    /// - `[↑]` + `[#]` ➔ `[℘ ↑+]` Undefined exploded plus finite state
    /// - `[#]` + `[↑]` ➔ `[℘ +↑]` Undefined finite plus exploded state
    /// - `[↓]` + `[↓]` ➔ `[℘ ↓+↓]` Undefined vanished plus vanished state
    /// - `[↓]` + `[#]` ➔ `[#]` The Circle unchanged
    /// - `[#]` + `[↓]` ➔ `[#]` The Scalar as real part, unchanged imaginary
    /// - `[0]` + `[#]` ➔ `[#]` The Circle unchanged
    /// - `[#]` + `[0]` ➔ `[#, 0i]` The Scalar as real part, zero imaginary
    /// - `[#]` + `[#]` ➔ `[#]` or `[↑]` or `[↓]` A finite, exploded or vanished Circle
    ///
    /// # Examples
    ///
    /// ```rust
    /// use spirix::{Scalar, Circle, ScalarF5E3, CircleF5E3};
    ///
    /// // Adding a Scalar to a Circle let a = Scalar::<i32, i8>::from(3_i32); let b = CircleF5E3::from((4_i32, 2_i32)); let sum = a + b; assert!(sum.r() == 7_i32); assert!(sum.i() == 2_i32);
    ///
    /// // Adding with Zero assert!(ScalarF5E3::ZERO + b == b);
    ///
    /// // Adding with infinity stays infinite let infinity: ScalarF5E3 = ScalarF5E3::ONE / 0_i32; assert!((infinity + b).is_infinite());
    ///
    /// // Addition with vanished values let tiny: ScalarF5E3 = ScalarF5E3::MIN_POS / 5_i32; assert!(tiny.vanished()); assert!((tiny + b) == b);
    ///
    /// // Addition with exploded values let huge: ScalarF5E3 = ScalarF5E3::MAX * 5_i32; assert!(huge.exploded()); assert!((huge + b).is_undefined());
    ///
    /// // Adding two exploded values let huge_circle: CircleF5E3 = CircleF5E3::MAX * 3_i32; assert!((huge + huge_circle).is_undefined());
    /// ```
    pub(crate) fn scalar_add_circle(&self, circle: &Circle<F, E>) -> Circle<F, E> {
        if self.is_normal() && circle.is_normal() {
            // AMBIG=0 unified pipeline. self is Scalar (N0); convert fraction to N1 before sign_extend; Scalar's imaginary contribution is 0.
            let self_is_big = self.exponent.into_unsigned() > circle.exponent.into_unsigned();
            let (big_exp, small_exp) = if self_is_big {
                (self.exponent, circle.exponent)
            } else {
                (circle.exponent, self.exponent)
            };
            let exp_diff = big_exp.wrapping_sub(&small_exp);
            let frac_bits_e: E = Self::fraction_bits().as_();
            if exp_diff.into_unsigned() >= frac_bits_e.into_unsigned() {
                return if self_is_big {
                    Circle {
                        real: (self.fraction >> 1isize) ^ F::min_value(),
                        imaginary: F::zero(),
                        exponent: self.exponent,
                    }
                } else {
                    *circle
                };
            }

            let shift: isize = exp_diff.saturate();
            let scalar_n1 = (self.fraction >> 1isize) ^ F::min_value();
            let (big_r, big_i, small_r, small_i) = if self_is_big {
                (
                    scalar_n1.sign_extend().w_shl(shift),
                    F::zero().sign_extend(),
                    circle.real.sign_extend(),
                    circle.imaginary.sign_extend(),
                )
            } else {
                (
                    circle.real.sign_extend().w_shl(shift),
                    circle.imaginary.sign_extend().w_shl(shift),
                    scalar_n1.sign_extend(),
                    F::zero().sign_extend(),
                )
            };
            let result_r = big_r.w_add(small_r);
            let result_i = big_i.w_add(small_i);

            if result_r.w_is_zero() && result_i.w_is_zero() {
                return Circle::<F, E>::ZERO;
            }

            let leading_r = result_r.leading_same();
            let leading_i = result_i.leading_same();
            let leading = leading_r.min(leading_i);

            let fb = Self::fraction_bits();
            let delta: isize = fb.wrapping_sub(leading).wrapping_add(1);
            let delta_e: E = delta.as_();
            let offset = small_exp.wrapping_add(&delta_e);

            let shl_amount = leading.wrapping_sub(fb).wrapping_sub(1);
            let canonical_r = if shl_amount >= 0 {
                result_r.w_shl(shl_amount)
            } else {
                result_r.w_shr(shl_amount.wrapping_neg())
            };
            let canonical_i = if shl_amount >= 0 {
                result_i.w_shl(shl_amount)
            } else {
                result_i.w_shr(shl_amount.wrapping_neg())
            };
            return Circle::<F, E> {
                real: canonical_r.deflate(),
                imaginary: canonical_i.deflate(),
                exponent: offset,
            };
        }

        // Escape-class handling (at least one operand is non-normal). Mirrors scalar_add_scalar's order: undefined → INFINITY-absorbs → zero identity → escape combinations.
        {
            if self.is_undefined() {
                return Circle {
                    real: self.fraction,
                    imaginary: self.fraction,
                    exponent: self.exponent,
                };
            }
            if circle.is_undefined() {
                return *circle;
            }
            // [∞] absorbs.
            if self.is_infinite() || circle.is_infinite() {
                return Circle::<F, E>::INFINITY;
            }
            // Zero identity.
            if self.is_zero() {
                return *circle;
            }
            if circle.is_zero() {
                return Circle::<F, E>::from_ri(*self, Scalar::<F, E>::ZERO);
            }
            if self.exploded() && circle.exploded() {
                return Circle {
                    real: TRANSFINITE_PLUS_TRANSFINITE.prefix.sa(),
                    imaginary: TRANSFINITE_PLUS_TRANSFINITE.prefix.sa(),
                    exponent: Self::ambiguous_exponent(),
                };
            }
            if self.vanished() && circle.vanished() {
                return Circle {
                    real: VANISHED_PLUS_VANISHED.prefix.sa(),
                    imaginary: VANISHED_PLUS_VANISHED.prefix.sa(),
                    exponent: Self::ambiguous_exponent(),
                };
            }
            // Self exploded: exp + van = exp; exp + normal = [℘].
            if self.exploded() {
                if circle.vanished() {
                    return Circle::<F, E>::from_ri(*self, Scalar::<F, E>::ZERO);
                }
                return Circle {
                    real: TRANSFINITE_PLUS_FINITE.prefix.sa(),
                    imaginary: TRANSFINITE_PLUS_FINITE.prefix.sa(),
                    exponent: Self::ambiguous_exponent(),
                };
            }
            // Circle exploded: van + exp = exp; normal + exp = [℘].
            if circle.exploded() {
                if self.vanished() {
                    return *circle;
                }
                return Circle {
                    real: FINITE_PLUS_TRANSFINITE.prefix.sa(),
                    imaginary: FINITE_PLUS_TRANSFINITE.prefix.sa(),
                    exponent: Self::ambiguous_exponent(),
                };
            }
            // Single vanished (other is normal — zero/exp/inf/undef already handled).
            if self.vanished() {
                return *circle;
            }
            if circle.vanished() {
                return Circle::<F, E>::from_ri(*self, Scalar::<F, E>::ZERO);
            }
            // Fallthrough — at least one non-normal but no class matched (shouldn't reach).
            Circle::<F, E>::from_ri(*self, Scalar::<F, E>::ZERO)
        }
    }
}