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,
    > Circle<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>,
{
    pub(crate) fn circle_divide_scalar(&self, other: &Scalar<F, E>) -> Self {
        if self.is_normal() && other.is_normal() {
            // AMBIG=0 unified pipeline. Circle / Scalar: divide each Circle component by the (N0→N1) Scalar fraction. Use div_euclid pattern.
            let a = self.real.sign_extend();
            let b = self.imaginary.sign_extend();
            let s = ((other.fraction >> 1isize) ^ F::min_value()).sign_extend();
            let fb = Self::fraction_bits();

            let real_quotient = a.w_shl(fb).w_div(s);
            let imag_quotient = b.w_shl(fb).w_div(s);

            if real_quotient.w_is_zero() && imag_quotient.w_is_zero() {
                return Self::ZERO;
            }

            // Cross-type div uses `a << fb / s`: a and s are both N1 wide (value × 2^(FRAC-2)), quotient scale is value × 2^FRAC. Shifting left by `leading-1` brings the (renormalized) dominant component to canonical wide N1 (leading_same=1, magnitude bit at 2*FRAC-2); the >> fb then lands at narrow N1 canonical (FRAC-2). Producing canonical form here is required — Circle's class detection in is_n1/is_n2 looks at the magnitude bit of either component, so sub-canonical fractions get misclassified as N-2/vanished.
            let leading_r = real_quotient.leading_same();
            let leading_i = imag_quotient.leading_same();
            let leading = leading_r.min(leading_i);
            let shift = leading.wrapping_sub(1);
            let real = real_quotient.w_shl(shift).w_shr(fb).deflate();
            let imaginary = imag_quotient.w_shl(shift).w_shr(fb).deflate();

            // Exp follows the fraction shift: the canonical-N1 normalization shifted the value by `leading - 1` bits; offset the binade by `FRAC - 1 - leading` so the final stored value matches reality. This collapses to `pa - pb + bo` when leading = FRAC-1 (value lands in [1, 2) naturally), and bumps the binade down for sub-canonical leading (value < 1 → shifted up to canonical → exp drops).
            let pa = self.exponent.cycle_widen();
            let pb = other.exponent.cycle_widen();
            let w_bo = Self::binade_origin().cycle_widen();
            let delta_e: E = fb.wrapping_sub(1).wrapping_sub(leading).as_();
            let w_delta = delta_e.sign_extend();
            let stored_pos = pa.w_sub(pb).w_add(w_bo).w_add(w_delta);
            let max_pos = Self::max_exponent().cycle_widen();
            let min_pos = Self::min_exponent().cycle_widen();

            return if stored_pos > max_pos {
                Self {
                    real,
                    imaginary,
                    exponent: Self::ambiguous_exponent(),
                }
            } else if stored_pos < min_pos {
                Self {
                    real: real >> 1isize,
                    imaginary: imaginary >> 1isize,
                    exponent: Self::ambiguous_exponent(),
                }
            } else {
                Self {
                    real,
                    imaginary,
                    exponent: stored_pos.deflate(),
                }
            };
        }

        // Escape-class handling (at least one operand non-normal).
        {
            if self.is_undefined() {
                return *self;
            }
            if other.is_undefined() {
                return Circle {
                    real: other.fraction,
                    imaginary: other.fraction,
                    exponent: other.exponent,
                };
            }
            if other.is_zero() {
                if self.is_zero() {
                    return Circle {
                        real: NEGLIGIBLE_DIVIDE_NEGLIGIBLE.prefix.sa(),
                        imaginary: NEGLIGIBLE_DIVIDE_NEGLIGIBLE.prefix.sa(),
                        exponent: Self::ambiguous_exponent(),
                    };
                }
                return Circle::<F, E>::INFINITY;
            }
            if self.is_infinite() {
                if other.is_infinite() {
                    return Circle {
                        real: TRANSFINITE_DIVIDE_TRANSFINITE.prefix.sa(),
                        imaginary: TRANSFINITE_DIVIDE_TRANSFINITE.prefix.sa(),
                        exponent: Self::ambiguous_exponent(),
                    };
                }
                return Circle::<F, E>::INFINITY;
            }
            if self.is_zero() || other.is_infinite() {
                return Circle::<F, E>::ZERO;
            }
            if self.exploded() && other.exploded() {
                return Self {
                    real: TRANSFINITE_DIVIDE_TRANSFINITE.prefix.sa(),
                    imaginary: TRANSFINITE_DIVIDE_TRANSFINITE.prefix.sa(),
                    exponent: Self::ambiguous_exponent(),
                };
            }
            if self.vanished() && other.vanished() {
                return Self {
                    real: NEGLIGIBLE_DIVIDE_NEGLIGIBLE.prefix.sa(),
                    imaginary: NEGLIGIBLE_DIVIDE_NEGLIGIBLE.prefix.sa(),
                    exponent: Self::ambiguous_exponent(),
                };
            }
            // Mixed escape: divide each Circle component by the (N0→N1 for normal, pass-thru for escape) Scalar, renormalize to N-2 (numerator vanished or denom exploded) or N-1 (otherwise) at AMBIG exp.
            let n_level: isize = if self.vanished() || other.exploded() {
                -2
            } else {
                -1
            };
            let denom_narrow: F = if other.is_normal() {
                (other.fraction >> 1isize) ^ F::min_value()
            } else {
                other.fraction
            };
            let s = denom_narrow.sign_extend();
            let a = self.real.sign_extend();
            let b = self.imaginary.sign_extend();
            let fb = Self::fraction_bits();
            let quotient_r = a.w_shl(fb).w_div(s);
            let quotient_i = b.w_shl(fb).w_div(s);

            let leading_r = quotient_r.leading_same();
            let leading_i = quotient_i.leading_same();
            let leading = leading_r.min(leading_i);
            let shift = leading.wrapping_add(n_level);
            return Self {
                real: quotient_r.w_shl(shift).w_shr(fb).deflate(),
                imaginary: quotient_i.w_shl(shift).w_shr(fb).deflate(),
                exponent: Self::ambiguous_exponent(),
            };
        }
    }
}