// SPDX-FileCopyrightText: 2026 John Moxley
// SPDX-License-Identifier: MIT OR Apache-2.0

//! The [`WidthLE`] trait — encodes "this storage type can losslessly
//! widen to that target storage type".
//!
//! Marker trait used by the cross-scale-op constructors (`mul_of`,
//! `add_of`, `cmp_of`, etc.) emitted on every decimal width: a source
//! storage `W` may participate in an operation targeted at storage
//! `Target` only when `W: WidthLE<Target>`.
//!
//! The trait is implemented for the reflexive case (`W: WidthLE<W>`)
//! and for every (narrower, wider) storage pair the crate ships. The
//! widening is performed by `Int::resize` / `Int::from_i128` for the
//! wide tier and a plain `as` cast for the narrow primitive tier.
//!
//! `WidthLE` is intentionally not exposed as a public trait method on
//! the decimal types — it is a *bound* used inside the per-width
//! `mul_of` / `cmp_of` / … inherent methods to limit the legal source
//! widths.
//!
//! # Trait surface
//!
//! ```ignore
//! pub trait WidthLE<Target>: Copy {
//!     /// Widens `self` to the `Target` storage type. Always lossless
//!     /// (every value representable in `Self` is representable in
//!     /// `Target`).
//!     fn widen_into(self) -> Target;
//! }
//! ```

/// Trait expressing "this storage type can losslessly widen to the
/// target storage type". Implemented for every (narrower, wider)
/// signed-integer storage pair the crate ships, plus the reflexive
/// (W → W) case.
pub trait WidthLE<Target>: Copy {
    /// Widens `self` to the `Target` storage type. Lossless by
    /// construction — `Target`'s range strictly covers `Self`'s.
    fn widen_into(self) -> Target;
}

// ── Reflexive impls (W → W) for every storage type. ──────────────────

impl WidthLE<i32> for i32 {
    #[inline]
    fn widen_into(self) -> i32 {
        self
    }
}
impl WidthLE<i64> for i64 {
    #[inline]
    fn widen_into(self) -> i64 {
        self
    }
}
impl WidthLE<i128> for i128 {
    #[inline]
    fn widen_into(self) -> i128 {
        self
    }
}

// ── Native widening (primitive → primitive). ─────────────────────────

impl WidthLE<i64> for i32 {
    #[inline]
    fn widen_into(self) -> i64 {
        self as i64
    }
}
impl WidthLE<i128> for i32 {
    #[inline]
    fn widen_into(self) -> i128 {
        self as i128
    }
}
impl WidthLE<i128> for i64 {
    #[inline]
    fn widen_into(self) -> i128 {
        self as i128
    }
}

// ── Wide-tier reflexive + cross impls. ───────────────────────────────
//
// Generated by `decl_width_le_wide_pair!` which expands to one impl per
// (source storage, target storage) ordering. The wide storages are
// `Int<3> < Int<4> < Int<6> < Int<8> < Int<12> < Int<16> < Int<24> <
//  Int<32> < Int<48> < Int<64>`. Primitive sources (`i32`/`i64`/`i128`)
// widen into every wide storage via `Int::from_i128`.

#[cfg(any(
    feature = "d57",
    feature = "d76",
    feature = "d115",
    feature = "d153",
    feature = "d230",
    feature = "d307",
    feature = "d462",
    feature = "d616",
    feature = "d924",
    feature = "d1232",
    feature = "wide",
    feature = "x-wide",
    feature = "xx-wide",
))]
macro_rules! impl_width_le_wide_reflexive {
    ($Storage:ty) => {
        impl $crate::types::traits::width_le::WidthLE<$Storage> for $Storage {
            #[inline]
            fn widen_into(self) -> $Storage {
                self
            }
        }
    };
}

#[cfg(any(
    feature = "d57",
    feature = "d76",
    feature = "d115",
    feature = "d153",
    feature = "d230",
    feature = "d307",
    feature = "d462",
    feature = "d616",
    feature = "d924",
    feature = "d1232",
    feature = "wide",
    feature = "x-wide",
    feature = "xx-wide",
))]
macro_rules! impl_width_le_native_to_wide {
    ($Wide:ty, $Native:ty) => {
        impl $crate::types::traits::width_le::WidthLE<$Wide> for $Native {
            #[inline]
            fn widen_into(self) -> $Wide {
                <$Wide>::from_i128(self as i128)
            }
        }
    };
}

macro_rules! impl_width_le_wide_pair {
    ($Wider:ty, $Narrower:ty) => {
        impl $crate::types::traits::width_le::WidthLE<$Wider> for $Narrower {
            #[inline]
            fn widen_into(self) -> $Wider {
                self.resize::<$Wider>()
            }
        }
    };
}

// Reflexive impls for each wide storage that exists.

#[cfg(any(feature = "d57", feature = "wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<3>);
#[cfg(any(feature = "d76", feature = "wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<4>);
#[cfg(any(feature = "d115", feature = "wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<6>);
#[cfg(any(feature = "d153", feature = "wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<8>);
#[cfg(any(feature = "d230", feature = "wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<12>);
#[cfg(any(feature = "d307", feature = "wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<16>);
#[cfg(any(feature = "d462", feature = "x-wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<24>);
#[cfg(any(feature = "d616", feature = "x-wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<32>);
#[cfg(any(feature = "d924", feature = "xx-wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<48>);
#[cfg(any(feature = "d1232", feature = "xx-wide"))]
impl_width_le_wide_reflexive!(crate::int::types::Int<64>);

// Native (i32/i64/i128) → wide impls.
//
// For every wide storage, allow widening from the three primitive
// integer storages. `from_i128` accepts `i128` sources, so the i32 /
// i64 inputs are upcast to i128 first (always lossless).

#[allow(unused_macros)]
macro_rules! impls_native_to_one_wide {
    ($Wide:ty) => {
        impl_width_le_native_to_wide!($Wide, i32);
        impl_width_le_native_to_wide!($Wide, i64);
        impl_width_le_native_to_wide!($Wide, i128);
    };
}

#[cfg(any(feature = "d57", feature = "wide"))]
impls_native_to_one_wide!(crate::int::types::Int<3>);
#[cfg(any(feature = "d76", feature = "wide"))]
impls_native_to_one_wide!(crate::int::types::Int<4>);
#[cfg(any(feature = "d115", feature = "wide"))]
impls_native_to_one_wide!(crate::int::types::Int<6>);
#[cfg(any(feature = "d153", feature = "wide"))]
impls_native_to_one_wide!(crate::int::types::Int<8>);
#[cfg(any(feature = "d230", feature = "wide"))]
impls_native_to_one_wide!(crate::int::types::Int<12>);
#[cfg(any(feature = "d307", feature = "wide"))]
impls_native_to_one_wide!(crate::int::types::Int<16>);
#[cfg(any(feature = "d462", feature = "x-wide"))]
impls_native_to_one_wide!(crate::int::types::Int<24>);
#[cfg(any(feature = "d616", feature = "x-wide"))]
impls_native_to_one_wide!(crate::int::types::Int<32>);
#[cfg(any(feature = "d924", feature = "xx-wide"))]
impls_native_to_one_wide!(crate::int::types::Int<48>);
#[cfg(any(feature = "d1232", feature = "xx-wide"))]
impls_native_to_one_wide!(crate::int::types::Int<64>);

// ── D38 storage (`Int<2>`) WidthLE web. ──────────────────────────────
//
// D38 is a base type (no feature gate), so the reflexive impl and the
// narrow-source widenings (`i32`/`i64` → `Int<2>`, i.e. D18 → D38)
// are unconditional. Widening `Int<2>` up into the wider tiers (D38 vs
// D57+ cross-width comparisons) is gated on each wider tier's feature.
impl WidthLE<crate::int::types::Int<2>> for crate::int::types::Int<2> {
    #[inline]
    fn widen_into(self) -> crate::int::types::Int<2> {
        self
    }
}
impl WidthLE<crate::int::types::Int<2>> for i32 {
    #[inline]
    fn widen_into(self) -> crate::int::types::Int<2> {
        crate::int::types::Int::<2>::from_i128(self as i128)
    }
}
impl WidthLE<crate::int::types::Int<2>> for i64 {
    #[inline]
    fn widen_into(self) -> crate::int::types::Int<2> {
        crate::int::types::Int::<2>::from_i128(self as i128)
    }
}

#[cfg(any(feature = "d57", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<3>, crate::int::types::Int<2>);
#[cfg(any(feature = "d76", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<4>, crate::int::types::Int<2>);
#[cfg(any(feature = "d115", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<6>, crate::int::types::Int<2>);
#[cfg(any(feature = "d153", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<8>, crate::int::types::Int<2>);
#[cfg(any(feature = "d230", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<12>, crate::int::types::Int<2>);
#[cfg(any(feature = "d307", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<16>, crate::int::types::Int<2>);
#[cfg(any(feature = "d462", feature = "x-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<24>, crate::int::types::Int<2>);
#[cfg(any(feature = "d616", feature = "x-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<32>, crate::int::types::Int<2>);
#[cfg(any(feature = "d924", feature = "xx-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<48>, crate::int::types::Int<2>);
#[cfg(any(feature = "d1232", feature = "xx-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<64>, crate::int::types::Int<2>);

// ── D18 storage (`Int<1>`) WidthLE web. ──────────────────────────────
//
// D18 is the narrowest base type, so the reflexive impl
// and the widening into D38 (`Int<1>` → `Int<2>`) are unconditional.
// Widening `Int<1>` up into the wider tiers is gated on each wider tier's
// feature, exactly as for the `Int<2>` web above.
impl WidthLE<crate::int::types::Int<1>> for crate::int::types::Int<1> {
    #[inline]
    fn widen_into(self) -> crate::int::types::Int<1> {
        self
    }
}
impl_width_le_wide_pair!(crate::int::types::Int<2>, crate::int::types::Int<1>);
#[cfg(any(feature = "d57", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<3>, crate::int::types::Int<1>);
#[cfg(any(feature = "d76", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<4>, crate::int::types::Int<1>);
#[cfg(any(feature = "d115", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<6>, crate::int::types::Int<1>);
#[cfg(any(feature = "d153", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<8>, crate::int::types::Int<1>);
#[cfg(any(feature = "d230", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<12>, crate::int::types::Int<1>);
#[cfg(any(feature = "d307", feature = "wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<16>, crate::int::types::Int<1>);
#[cfg(any(feature = "d462", feature = "x-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<24>, crate::int::types::Int<1>);
#[cfg(any(feature = "d616", feature = "x-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<32>, crate::int::types::Int<1>);
#[cfg(any(feature = "d924", feature = "xx-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<48>, crate::int::types::Int<1>);
#[cfg(any(feature = "d1232", feature = "xx-wide"))]
impl_width_le_wide_pair!(crate::int::types::Int<64>, crate::int::types::Int<1>);

// Wide → wide cross impls. For every ordered pair (narrower, wider).
// Each impl is independently gated on BOTH storages being available.

macro_rules! wide_pair {
    ($Wider:ty, $Narrower:ty, $WiderFeat:literal, $NarrowerFeat:literal,
     $WiderUmbrella:literal, $NarrowerUmbrella:literal) => {
        #[cfg(all(
                    any(feature = $WiderFeat, feature = $WiderUmbrella),
                    any(feature = $NarrowerFeat, feature = $NarrowerUmbrella),
                ))]
        impl_width_le_wide_pair!($Wider, $Narrower);
    };
}

// Int<3> (D57) is widest of nothing narrower (D57 is the narrowest
// wide). Pairs where Int<3> is the wider:
wide_pair!(
    crate::int::types::Int<4>,
    crate::int::types::Int<3>,
    "d76",
    "d57",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<6>,
    crate::int::types::Int<3>,
    "d115",
    "d57",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<8>,
    crate::int::types::Int<3>,
    "d153",
    "d57",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<12>,
    crate::int::types::Int<3>,
    "d230",
    "d57",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<16>,
    crate::int::types::Int<3>,
    "d307",
    "d57",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<24>,
    crate::int::types::Int<3>,
    "d462",
    "d57",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<32>,
    crate::int::types::Int<3>,
    "d616",
    "d57",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<3>,
    "d924",
    "d57",
    "xx-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<3>,
    "d1232",
    "d57",
    "xx-wide",
    "wide"
);

wide_pair!(
    crate::int::types::Int<6>,
    crate::int::types::Int<4>,
    "d115",
    "d76",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<8>,
    crate::int::types::Int<4>,
    "d153",
    "d76",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<12>,
    crate::int::types::Int<4>,
    "d230",
    "d76",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<16>,
    crate::int::types::Int<4>,
    "d307",
    "d76",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<24>,
    crate::int::types::Int<4>,
    "d462",
    "d76",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<32>,
    crate::int::types::Int<4>,
    "d616",
    "d76",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<4>,
    "d924",
    "d76",
    "xx-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<4>,
    "d1232",
    "d76",
    "xx-wide",
    "wide"
);

wide_pair!(
    crate::int::types::Int<8>,
    crate::int::types::Int<6>,
    "d153",
    "d115",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<12>,
    crate::int::types::Int<6>,
    "d230",
    "d115",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<16>,
    crate::int::types::Int<6>,
    "d307",
    "d115",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<24>,
    crate::int::types::Int<6>,
    "d462",
    "d115",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<32>,
    crate::int::types::Int<6>,
    "d616",
    "d115",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<6>,
    "d924",
    "d115",
    "xx-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<6>,
    "d1232",
    "d115",
    "xx-wide",
    "wide"
);

wide_pair!(
    crate::int::types::Int<12>,
    crate::int::types::Int<8>,
    "d230",
    "d153",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<16>,
    crate::int::types::Int<8>,
    "d307",
    "d153",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<24>,
    crate::int::types::Int<8>,
    "d462",
    "d153",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<32>,
    crate::int::types::Int<8>,
    "d616",
    "d153",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<8>,
    "d924",
    "d153",
    "xx-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<8>,
    "d1232",
    "d153",
    "xx-wide",
    "wide"
);

wide_pair!(
    crate::int::types::Int<16>,
    crate::int::types::Int<12>,
    "d307",
    "d230",
    "wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<24>,
    crate::int::types::Int<12>,
    "d462",
    "d230",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<32>,
    crate::int::types::Int<12>,
    "d616",
    "d230",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<12>,
    "d924",
    "d230",
    "xx-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<12>,
    "d1232",
    "d230",
    "xx-wide",
    "wide"
);

wide_pair!(
    crate::int::types::Int<24>,
    crate::int::types::Int<16>,
    "d462",
    "d307",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<32>,
    crate::int::types::Int<16>,
    "d616",
    "d307",
    "x-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<16>,
    "d924",
    "d307",
    "xx-wide",
    "wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<16>,
    "d1232",
    "d307",
    "xx-wide",
    "wide"
);

wide_pair!(
    crate::int::types::Int<32>,
    crate::int::types::Int<24>,
    "d616",
    "d462",
    "x-wide",
    "x-wide"
);
wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<24>,
    "d924",
    "d462",
    "xx-wide",
    "x-wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<24>,
    "d1232",
    "d462",
    "xx-wide",
    "x-wide"
);

wide_pair!(
    crate::int::types::Int<48>,
    crate::int::types::Int<32>,
    "d924",
    "d616",
    "xx-wide",
    "x-wide"
);
wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<32>,
    "d1232",
    "d616",
    "xx-wide",
    "x-wide"
);

wide_pair!(
    crate::int::types::Int<64>,
    crate::int::types::Int<48>,
    "d1232",
    "d924",
    "xx-wide",
    "xx-wide"
);
