devela 0.28.0

A development substrate of coherence.
Documentation
// devela::num::dom::_helper
//
//! Defines: `_num_dom_impl_arith`, `_num_dom_upcast_arith`, `_num_dom_upcasted_mul_add`.
//
// IMPROVE:MAYBE try to unify _num_dom_upcast_arith! with _num_dom_upcasted_mul_add!.

#![allow(unused, non_camel_case_types)]

/// Implement the arithmetic operators for a unit struct wrapper, based on the inner type.
///
/// # Arguments:
/// $W:   the outer wrapper
/// $T:   the inner type
/// $cap: the (optional) capability feature that enables the given implementation. E.g "_int_i8".
///
/// # Invoked from:
/// - int/wrapper/namespace.rs
/// - real/float/wrapper/definition.rs
#[doc(hidden)]
#[macro_export]
#[allow(clippy::crate_in_macro_def, reason = "paste! must be in the root of invoking crate")]
macro_rules! _num_dom_impl_arith· {
    ($W:ident: $($T:ty $( : $cap:literal)? ),+) => { $(
        $crate::_num_dom_impl_arith![@common $W($T $(:$cap)? )];
        $crate::_num_dom_impl_arith![@neg $W($T $(:$cap)? )];
    )+ };
    ($W:ident: (no_neg) $($T:ty $(: $cap:literal)? ),+) => { $(
        $crate::_num_dom_impl_arith![@common $W($T $(:$cap)? )];
    )+ };

    (@common $W:ident($T:ty $(: $cap:literal)? )) => {
        $crate::_num_dom_impl_arith![@op $W($T $(:$cap)? ), Add, add];
        $crate::_num_dom_impl_arith![@op $W($T $(:$cap)? ), Sub, sub];
        $crate::_num_dom_impl_arith![@op $W($T $(:$cap)? ), Mul, mul];
        $crate::_num_dom_impl_arith![@op $W($T $(:$cap)? ), Div, div];
        $crate::_num_dom_impl_arith![@op $W($T $(:$cap)? ), Rem, rem];
        $crate::_num_dom_impl_arith![@op_assign $W($T $(:$cap)? ), AddAssign, add_assign];
        $crate::_num_dom_impl_arith![@op_assign $W($T $(:$cap)? ), SubAssign, sub_assign];
        $crate::_num_dom_impl_arith![@op_assign $W($T $(:$cap)? ), MulAssign, mul_assign];
        $crate::_num_dom_impl_arith![@op_assign $W($T $(:$cap)? ), DivAssign, div_assign];
        $crate::_num_dom_impl_arith![@op_assign $W($T $(:$cap)? ), RemAssign, rem_assign];
    };
    (@neg $W:ident($T:ty $(: $cap:literal)? )) => {
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl core::ops::Neg for $W<$T> {
            type Output = $W<$T>;
            fn neg(self) -> $W<$T> { $W(self.0.neg()) }
        }
    };

    (
    // $wrap:  the wrapper type
    // $T:     the inner type
    // $trait: the trait to implement
    // $fn:    the name of the method
        @op $W:ident($T:ty $(: $cap:literal)? ), $trait:ident, $fn:ident) => {
        /* $W<$T> op $W<$T> -> $W<$T> */
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl core::ops::$trait for $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, $W<$T>, 0];
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'s> core::ops::$trait<$W<$T>> for &'s $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, $W<$T>, 0];
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'o> core::ops::$trait<&'o $W<$T>> for $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, &'o $W<$T>, 0];
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'s, 'o> core::ops::$trait<&'o $W<$T>> for &'s $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, &'o $W<$T>, 0];
        }
        /* $W<$T> op $T -> $W<$T> */
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl core::ops::$trait<$T> for $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, $T];
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'s> core::ops::$trait<$T> for &'s $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, $T];
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'o> core::ops::$trait<&'o $T> for $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, &'o $T];
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'s, 'o> core::ops::$trait<&'o $T> for &'s $W<$T> {
            $crate::_num_dom_impl_arith![@op_body $W($T), $fn, &'o $T];
        }
    };
    (@op_body $W:ident($T:ty), $fn:ident, $other:ty $(, $other_field:tt)?) => {
        type Output = $W<$T>;
        fn $fn(self, other: $other) -> $W<$T> { $W(self.0.$fn(other$(. $other_field)?)) }
    };

    (@op_assign $W:ident($T:ty $(: $cap:literal)?), $trait:ident, $fn:ident) => { crate::paste! {
        /* $W<$T> op_assign $W<$T> */
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl core::ops::$trait for $W<$T> {
            fn $fn(&mut self, other: $W<$T>) { self.0.$fn(other.0); }
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'o> core::ops::$trait<&'o $W<$T>> for $W<$T> {
            fn $fn(&mut self, other: &'o $W<$T>) { self.0.$fn(other.0); }
        }
        /* $W<$T> op_assign $T -> $W<$T> */
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl core::ops::$trait<$T> for $W<$T> {
            fn $fn(&mut self, other: $T) { self.0.$fn(other); }
        }
        $( #[cfg(feature = $cap )] #[cfg_attr(nightly_doc, doc(cfg(feature = $cap)))] )?
        impl<'o> core::ops::$trait<&'o $T> for $W<$T> {
            fn $fn(&mut self, other: &'o $T) { self.0.$fn(other); }
        }
    }};
}
#[doc(hidden)]
pub use _num_dom_impl_arith· as _num_dom_impl_arith;

/// Helper macro to deal with the case when we can't upcast (i.e. for 128-bits).
///
/// # Arguments
/// $op:  an overloadable operator (+, -, *, /)
/// $fn:  the corresponding function (add, sub, mul, div)
/// $lhs: the left hand side operator
/// $rhs: the right hand side operator
/// $is_up: whether we've upcasted (Y) or not (N), known at compile-time
///
/// # Invoked from:
/// - /src/num/dom/int/wrapper/impl_modulo.rs
///
/// # Features
/// Uses `unsafe_hint` for performance optimizations with upcasted arithmetic.
#[doc(hidden)]
#[macro_export]
#[rustfmt::skip]
macro_rules! _num_dom_upcast_arith· {
    // this is used for checked versions
    (err $op:tt $fn:ident($lhs:expr, $rhs:expr) $is_up:ident) => { paste! {
        if cif!(same($is_up, Y)) { // can't overflow if upcasted
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                // SAFETY: can't overflow if upcasted
                unsafe { $lhs.[<unchecked_ $fn>]($rhs) }
            } _ => { $lhs $op $rhs }}

        } else { // otherwise do the checked operation:
            if let Some(result) = $lhs.[<checked_ $fn>]($rhs) {
                result } else { return Err(Overflow(None));
            }
        }
    }};
    // this is used for checked versions that don't need to calculate cycles
    (reduce_err $op:tt $fn:ident($lhs:expr, $rhs:expr) % $modulus:expr, $is_up:ident) => { paste! {
        if cif!(same($is_up, Y)) { // can't overflow if upcasted
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                // SAFETY: can't overflow if upcasted
                unsafe { $lhs.[<unchecked_ $fn>]($rhs) }
            } _ => { $lhs $op $rhs }}

        } else { // otherwise reduce each operand before the checked operation:
            if let Some(result) = ($lhs % $modulus).[<checked_ $fn>]($rhs % $modulus) {
                result } else { return Err(Overflow(None));
            }
        }
    }};
    // this is used for unchecked versions that don't need to calculate cycles
    (reduce $op:tt $fn:ident($lhs:expr, $rhs:expr) % $modulus:expr, $is_up:ident) => { paste! {
        if cif!(same($is_up, Y)) { // can't overflow if upcasted
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                // SAFETY: can't overflow if upcasted
                unsafe { $lhs.[<unchecked_ $fn>]($rhs) }
            } _ => { $lhs $op $rhs }}
        } else { // otherwise reduce each operand before the unchecked operation:
            ($lhs % $modulus) $op ($rhs % $modulus)
        }
    }};
}
#[doc(hidden)]
pub use _num_dom_upcast_arith· as _num_dom_upcast_arith;

/// Helper macro to only do checked operations when we can't upcast (i.e. for 128-bits).
///
/// Performs checked operations only if the upcasted type is the same
/// as the non-upcasted one. It is compatible with const functions.
///
/// # Arguments
/// `$lhs`:      the left hand side operator
/// `$rhs`:      the right hand side operator
/// `$modulus`:  the modulus (for reduced_* ops)
/// `$ba`:       the base type
/// `$up`:       the upcasted type
///
/// # Invoked from:
/// - /src/num/dom/int/wrapper/impl_root.rs
///
/// # Examples
/// ```ignore
/// let sum = _num_dom_upcasted_mul_add![add_err(v, m) i32 => i64];
/// let sum = _num_dom_upcasted_mul_add![reduced_add_err(v, m) % 40; i32 => i64];
/// ```
/// # Features
/// It makes use of `unsafe_hint` to optimize arithmetic ops when able to upcast.
//
#[doc(hidden)]
#[macro_export]
#[rustfmt::skip]
macro_rules! _num_dom_upcasted_mul_add· {
    (
    /* basic arithmetic ops */
    // if we've not upcasted, do checked operation and return err on overflow
    add_err($lhs:expr, $rhs:expr) $ba:ty => $up:ty) => {
        if $crate::cif!(diff($ba, $up)) {
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                unsafe { $lhs.unchecked_add($rhs) } // SAFETY: can't overflow if upcasted
            } _ => { $lhs + $rhs }}
        } else {
            if let Some(sum) = $lhs.checked_add($rhs) { sum }
            else { return Err($crate::IntError::Overflow(None)); }
        }
    };
    (mul_err($lhs:expr, $rhs:expr) $ba:ty => $up:ty) => {
        if $crate::cif!(diff($ba, $up)) {
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                unsafe { $lhs.unchecked_mul($rhs) } // SAFETY: can't overflow if upcasted
            } _ => { $lhs * $rhs }}
        } else {
            if let Some(product) = $lhs.checked_mul($rhs) { product }
            else { return Err($crate::IntError::Overflow(None)); }
        }
    };
    (
    /* reduced (modulo) ops */

    // if we've not upcasted, first reduce the sumands with the given modulus,
    // then do checked operation and return err on overflow
    reduced_add_err($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
        if $crate::cif!(diff($ba, $up)) {
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                unsafe { $lhs.unchecked_add($rhs) } // SAFETY: can't overflow if upcasted
            } _ => { $lhs * $rhs }}
        } else {
            // reduce each sumand before checked operation
            if let Some(sum) = ($lhs % $modulus).checked_add($rhs % $modulus) { sum }
            else { return Err($crate::IntError::Overflow(None)); }
        }
    };
    (
    // if we've not upcasted, just reduce the sumands with the given $modulus
    reduced_add($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
        if $crate::cif!(diff($ba, $up)) {
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                unsafe { $lhs.unchecked_add($rhs) } // SAFETY: can't overflow if upcasted
            } _ => { $lhs + $rhs }}
        } else {
            // reduce each operand before the operation that could panic
            ($lhs % $modulus) + ($rhs % $modulus)
        }
    };
    (reduced_mul_err($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
        if $crate::cif!(diff($ba, $up)) {
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                unsafe { $lhs.unchecked_mul($rhs) } // SAFETY: can't overflow if upcasted
            } _ => { $lhs * $rhs }}
        } else {
            // reduce each factor before checked operation
            if let Some(product) = ($lhs % $modulus).checked_mul($rhs % $modulus) { product }
            else { return Err($crate::IntError::Overflow(None)); }
        }
    };
    (reduced_mul($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
        if $crate::cif!(diff($ba, $up)) {
            cfg_select! { all(feature = "unsafe_hint", not(feature = "safe_num")) => {
                unsafe { $lhs.unchecked_mul($rhs) } // SAFETY: can't overflow if upcasted
            } _ => { $lhs * $rhs }}
        } else {
            // reduce each operand before the operation that could panic
            ($lhs % $modulus) + ($rhs % $modulus)
        }
    };
}
#[doc(hidden)]
pub use _num_dom_upcasted_mul_add· as _num_dom_upcasted_mul_add;