use num_traits::{
Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub,
FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Zero,
};
use crate::core_type::{I128, ParseDecimalError};
impl<const SCALE: u32> Zero for I128<SCALE> {
#[inline]
fn zero() -> Self {
Self::ZERO
}
#[inline]
fn is_zero(&self) -> bool {
self.0 == 0
}
}
impl<const SCALE: u32> One for I128<SCALE> {
#[inline]
fn one() -> Self {
Self::ONE
}
#[inline]
fn is_one(&self) -> bool {
self.0 == Self::multiplier()
}
}
impl<const SCALE: u32> Num for I128<SCALE> {
type FromStrRadixErr = ParseDecimalError;
fn from_str_radix(s: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
if radix != 10 {
return Err(ParseDecimalError::InvalidChar);
}
s.parse::<Self>()
}
}
impl<const SCALE: u32> Bounded for I128<SCALE> {
#[inline]
fn min_value() -> Self {
Self::MIN
}
#[inline]
fn max_value() -> Self {
Self::MAX
}
}
impl<const SCALE: u32> Signed for I128<SCALE> {
#[inline]
fn abs(&self) -> Self {
I128::abs(*self)
}
#[inline]
fn signum(&self) -> Self {
I128::signum(*self)
}
#[inline]
fn is_positive(&self) -> bool {
self.0 > 0
}
#[inline]
fn is_negative(&self) -> bool {
self.0 < 0
}
#[inline]
fn abs_sub(&self, other: &Self) -> Self {
if *self <= *other {
Self::ZERO
} else {
*self - *other
}
}
}
impl<const SCALE: u32> FromPrimitive for I128<SCALE> {
#[inline]
fn from_i64(n: i64) -> Option<Self> {
(n as i128)
.checked_mul(Self::multiplier())
.map(Self)
}
#[inline]
fn from_u64(n: u64) -> Option<Self> {
(n as i128)
.checked_mul(Self::multiplier())
.map(Self)
}
#[inline]
fn from_i128(n: i128) -> Option<Self> {
Self::try_from(n).ok()
}
#[inline]
fn from_u128(n: u128) -> Option<Self> {
Self::try_from(n).ok()
}
#[inline]
fn from_f32(n: f32) -> Option<Self> {
Self::try_from(n).ok()
}
#[inline]
fn from_f64(n: f64) -> Option<Self> {
Self::try_from(n).ok()
}
}
impl<const SCALE: u32> ToPrimitive for I128<SCALE> {
#[inline]
fn to_i64(&self) -> Option<i64> {
let raw = self.0 / Self::multiplier();
i64::try_from(raw).ok()
}
#[inline]
fn to_u64(&self) -> Option<u64> {
if self.0 < 0 {
return None;
}
let raw = self.0 / Self::multiplier();
u64::try_from(raw).ok()
}
#[inline]
fn to_i128(&self) -> Option<i128> {
Some(self.0 / Self::multiplier())
}
#[inline]
fn to_u128(&self) -> Option<u128> {
if self.0 < 0 {
return None;
}
u128::try_from(self.0 / Self::multiplier()).ok()
}
#[inline]
fn to_f32(&self) -> Option<f32> {
Some((*self).to_f32_lossy())
}
#[inline]
fn to_f64(&self) -> Option<f64> {
Some((*self).to_f64_lossy())
}
}
impl<const SCALE: u32> NumCast for I128<SCALE> {
#[inline]
fn from<T: ToPrimitive>(n: T) -> Option<Self> {
let f = n.to_f64();
if let Some(int) = n.to_i128() {
let take_int_path = match f {
None => true,
Some(fv) => fv.is_finite() && ((int as f64) == fv),
};
if take_int_path {
return <Self as FromPrimitive>::from_i128(int);
}
}
if let Some(fv) = f {
return <Self as FromPrimitive>::from_f64(fv);
}
None
}
}
impl<const SCALE: u32> CheckedAdd for I128<SCALE> {
#[inline]
fn checked_add(&self, rhs: &Self) -> Option<Self> {
self.0.checked_add(rhs.0).map(Self)
}
}
impl<const SCALE: u32> CheckedSub for I128<SCALE> {
#[inline]
fn checked_sub(&self, rhs: &Self) -> Option<Self> {
self.0.checked_sub(rhs.0).map(Self)
}
}
impl<const SCALE: u32> CheckedMul for I128<SCALE> {
#[inline]
fn checked_mul(&self, v: &Self) -> Option<Self> {
(*self).checked_mul(*v)
}
}
impl<const SCALE: u32> CheckedDiv for I128<SCALE> {
#[inline]
fn checked_div(&self, v: &Self) -> Option<Self> {
(*self).checked_div(*v)
}
}
impl<const SCALE: u32> CheckedRem for I128<SCALE> {
#[inline]
fn checked_rem(&self, rhs: &Self) -> Option<Self> {
self.0.checked_rem(rhs.0).map(Self)
}
}
impl<const SCALE: u32> CheckedNeg for I128<SCALE> {
#[inline]
fn checked_neg(&self) -> Option<Self> {
self.0.checked_neg().map(Self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core_type::{I128, I128s12};
#[test]
fn zero_is_zero_const() {
assert_eq!(<I128s12 as Zero>::zero(), I128s12::ZERO);
}
#[test]
fn zero_is_zero_predicate() {
assert!(<I128s12 as Zero>::is_zero(&I128s12::ZERO));
assert!(!<I128s12 as Zero>::is_zero(&I128s12::ONE));
assert!(!<I128s12 as Zero>::is_zero(&I128s12::from_bits(1)));
}
#[test]
fn one_is_one_const() {
assert_eq!(<I128s12 as One>::one(), I128s12::ONE);
}
#[test]
fn one_is_one_predicate() {
assert!(<I128s12 as One>::is_one(&I128s12::ONE));
assert!(!<I128s12 as One>::is_one(&I128s12::ZERO));
assert!(!<I128s12 as One>::is_one(&I128s12::from_bits(1)));
}
#[test]
fn bounded_min_max() {
assert_eq!(<I128s12 as Bounded>::min_value(), I128s12::MIN);
assert_eq!(<I128s12 as Bounded>::max_value(), I128s12::MAX);
}
#[test]
fn signed_abs_basic() {
let pos = I128s12::from_bits(1_500_000_000_000);
let neg = I128s12::from_bits(-1_500_000_000_000);
assert_eq!(<I128s12 as Signed>::abs(&pos), pos);
assert_eq!(<I128s12 as Signed>::abs(&neg), pos);
assert_eq!(<I128s12 as Signed>::abs(&I128s12::ZERO), I128s12::ZERO);
}
#[test]
fn signed_signum_basic() {
let pos = I128s12::from_bits(1_500_000_000_000);
let neg = I128s12::from_bits(-1_500_000_000_000);
assert_eq!(<I128s12 as Signed>::signum(&pos), I128s12::ONE);
assert_eq!(<I128s12 as Signed>::signum(&neg), -I128s12::ONE);
assert_eq!(<I128s12 as Signed>::signum(&I128s12::ZERO), I128s12::ZERO);
}
#[test]
fn signed_is_positive_negative() {
let pos = I128s12::from_bits(1_500_000_000_000);
let neg = I128s12::from_bits(-1_500_000_000_000);
assert!(<I128s12 as Signed>::is_positive(&pos));
assert!(!<I128s12 as Signed>::is_positive(&neg));
assert!(!<I128s12 as Signed>::is_positive(&I128s12::ZERO));
assert!(!<I128s12 as Signed>::is_negative(&pos));
assert!(<I128s12 as Signed>::is_negative(&neg));
assert!(!<I128s12 as Signed>::is_negative(&I128s12::ZERO));
}
#[test]
fn signed_abs_sub_clamps_to_zero() {
let two = I128s12::from_bits(2_000_000_000_000);
let five = I128s12::from_bits(5_000_000_000_000);
let three = I128s12::from_bits(3_000_000_000_000);
assert_eq!(<I128s12 as Signed>::abs_sub(&five, &two), three);
assert_eq!(<I128s12 as Signed>::abs_sub(&two, &five), I128s12::ZERO);
assert_eq!(<I128s12 as Signed>::abs_sub(&five, &five), I128s12::ZERO);
}
#[test]
fn from_primitive_i64_in_range() {
assert_eq!(
<I128s12 as FromPrimitive>::from_i64(0),
Some(I128s12::ZERO)
);
assert_eq!(
<I128s12 as FromPrimitive>::from_i64(1),
Some(I128s12::ONE)
);
assert_eq!(
<I128s12 as FromPrimitive>::from_i64(42),
Some(I128s12::from_bits(42_000_000_000_000))
);
assert_eq!(
<I128s12 as FromPrimitive>::from_i64(-42),
Some(I128s12::from_bits(-42_000_000_000_000))
);
}
#[test]
fn from_primitive_u64_in_range() {
assert_eq!(
<I128s12 as FromPrimitive>::from_u64(0),
Some(I128s12::ZERO)
);
assert_eq!(
<I128s12 as FromPrimitive>::from_u64(42),
Some(I128s12::from_bits(42_000_000_000_000))
);
let large = <I128s12 as FromPrimitive>::from_u64(u64::MAX);
assert!(large.is_some());
}
#[test]
fn from_primitive_i128_overflow_returns_none() {
assert_eq!(<I128s12 as FromPrimitive>::from_i128(i128::MAX), None);
assert_eq!(<I128s12 as FromPrimitive>::from_i128(i128::MIN), None);
assert_eq!(
<I128s12 as FromPrimitive>::from_i128(7),
Some(I128s12::from_bits(7_000_000_000_000))
);
}
#[test]
fn from_primitive_u128_overflow_returns_none() {
assert_eq!(<I128s12 as FromPrimitive>::from_u128(u128::MAX), None);
assert_eq!(
<I128s12 as FromPrimitive>::from_u128(99),
Some(I128s12::from_bits(99_000_000_000_000))
);
}
#[test]
fn from_primitive_f32_basic() {
assert_eq!(
<I128s12 as FromPrimitive>::from_f32(0.0),
Some(I128s12::ZERO)
);
assert_eq!(
<I128s12 as FromPrimitive>::from_f32(1.0),
Some(I128s12::ONE)
);
assert_eq!(<I128s12 as FromPrimitive>::from_f32(f32::NAN), None);
assert_eq!(<I128s12 as FromPrimitive>::from_f32(f32::INFINITY), None);
assert_eq!(<I128s12 as FromPrimitive>::from_f32(f32::NEG_INFINITY), None);
}
#[test]
fn from_primitive_f64_basic() {
assert_eq!(
<I128s12 as FromPrimitive>::from_f64(0.0),
Some(I128s12::ZERO)
);
assert_eq!(
<I128s12 as FromPrimitive>::from_f64(1.0),
Some(I128s12::ONE)
);
let v = <I128s12 as FromPrimitive>::from_f64(1.234567890123_f64);
assert!(v.is_some());
assert_eq!(<I128s12 as FromPrimitive>::from_f64(f64::NAN), None);
assert_eq!(<I128s12 as FromPrimitive>::from_f64(f64::INFINITY), None);
assert_eq!(<I128s12 as FromPrimitive>::from_f64(1e30), None);
}
#[test]
fn from_primitive_smaller_int_types_via_default_impl() {
assert_eq!(
<I128s12 as FromPrimitive>::from_i32(7),
Some(I128s12::from_bits(7_000_000_000_000))
);
assert_eq!(
<I128s12 as FromPrimitive>::from_i16(-3),
Some(I128s12::from_bits(-3_000_000_000_000))
);
assert_eq!(
<I128s12 as FromPrimitive>::from_i8(0),
Some(I128s12::ZERO)
);
assert_eq!(
<I128s12 as FromPrimitive>::from_u32(7),
Some(I128s12::from_bits(7_000_000_000_000))
);
assert_eq!(
<I128s12 as FromPrimitive>::from_u16(3),
Some(I128s12::from_bits(3_000_000_000_000))
);
assert_eq!(
<I128s12 as FromPrimitive>::from_u8(255),
Some(I128s12::from_bits(255_000_000_000_000))
);
}
#[test]
fn to_primitive_i64_in_range() {
let v = I128s12::from_bits(42_000_000_000_000);
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&v), Some(42_i64));
let neg = I128s12::from_bits(-42_000_000_000_000);
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&neg), Some(-42_i64));
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&I128s12::ZERO), Some(0_i64));
}
#[test]
fn to_primitive_i64_truncates_toward_zero() {
let v = I128s12::from_bits(2_500_000_000_000);
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&v), Some(2_i64));
let neg = I128s12::from_bits(-2_500_000_000_000);
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&neg), Some(-2_i64));
}
#[test]
fn to_primitive_i64_out_of_range_returns_none() {
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&I128s12::MAX), None);
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&I128s12::MIN), None);
}
#[test]
fn to_primitive_u64_negative_returns_none() {
let neg = I128s12::from_bits(-1_000_000_000_000);
assert_eq!(<I128s12 as ToPrimitive>::to_u64(&neg), None);
}
#[test]
fn to_primitive_u64_in_range() {
let v = I128s12::from_bits(42_000_000_000_000);
assert_eq!(<I128s12 as ToPrimitive>::to_u64(&v), Some(42_u64));
assert_eq!(<I128s12 as ToPrimitive>::to_u64(&I128s12::ZERO), Some(0_u64));
}
#[test]
fn to_primitive_i128_always_succeeds() {
assert!(<I128s12 as ToPrimitive>::to_i128(&I128s12::MAX).is_some());
assert!(<I128s12 as ToPrimitive>::to_i128(&I128s12::MIN).is_some());
assert_eq!(
<I128s12 as ToPrimitive>::to_i128(&I128s12::ZERO),
Some(0_i128)
);
assert_eq!(
<I128s12 as ToPrimitive>::to_i128(&I128s12::from_bits(42_000_000_000_000)),
Some(42_i128)
);
}
#[test]
fn to_primitive_u128_negative_returns_none() {
assert_eq!(
<I128s12 as ToPrimitive>::to_u128(&I128s12::from_bits(-1)),
None
);
}
#[test]
fn to_primitive_u128_in_range() {
assert_eq!(
<I128s12 as ToPrimitive>::to_u128(&I128s12::ZERO),
Some(0_u128)
);
assert_eq!(
<I128s12 as ToPrimitive>::to_u128(&I128s12::from_bits(99_000_000_000_000)),
Some(99_u128)
);
}
#[test]
fn to_primitive_f64_round_trip_within_lsb() {
let lsb = 1.0 / (I128s12::multiplier() as f64);
let v = I128s12::from_f64_lossy(1.234567890123_f64);
let back = <I128s12 as ToPrimitive>::to_f64(&v).expect("to_f64 always returns Some");
assert!(
(back - 1.234567890123_f64).abs() <= lsb * 2.0,
"round-trip exceeded 2 LSB: back = {back}, lsb = {lsb}"
);
}
#[test]
fn to_primitive_f32_matches_to_f32_lossy() {
let v = I128s12::from_bits(1_500_000_000_000);
assert_eq!(
<I128s12 as ToPrimitive>::to_f32(&v),
Some(v.to_f32_lossy())
);
}
#[test]
fn to_primitive_smaller_int_types_via_default_impl() {
let v = I128s12::from_bits(42_000_000_000_000);
assert_eq!(<I128s12 as ToPrimitive>::to_i32(&v), Some(42_i32));
assert_eq!(<I128s12 as ToPrimitive>::to_u32(&v), Some(42_u32));
assert_eq!(<I128s12 as ToPrimitive>::to_i16(&v), Some(42_i16));
assert_eq!(<I128s12 as ToPrimitive>::to_u16(&v), Some(42_u16));
assert_eq!(<I128s12 as ToPrimitive>::to_i8(&v), Some(42_i8));
assert_eq!(<I128s12 as ToPrimitive>::to_u8(&v), Some(42_u8));
let big = I128s12::from_bits(40_000_000_000_000_000); assert_eq!(<I128s12 as ToPrimitive>::to_i8(&big), None);
assert_eq!(<I128s12 as ToPrimitive>::to_u8(&big), None);
}
#[test]
fn checked_add_basic() {
let one = I128s12::ONE;
let two = I128s12::from_bits(2_000_000_000_000);
assert_eq!(
<I128s12 as CheckedAdd>::checked_add(&one, &one),
Some(two)
);
}
#[test]
fn checked_add_overflow_returns_none() {
assert_eq!(
<I128s12 as CheckedAdd>::checked_add(&I128s12::MAX, &I128s12::ONE),
None
);
assert_eq!(
<I128s12 as CheckedAdd>::checked_add(&I128s12::MAX, &I128s12::ZERO),
Some(I128s12::MAX)
);
}
#[test]
fn checked_sub_basic() {
let three = I128s12::from_bits(3_000_000_000_000);
let two = I128s12::from_bits(2_000_000_000_000);
assert_eq!(
<I128s12 as CheckedSub>::checked_sub(&three, &two),
Some(I128s12::ONE)
);
}
#[test]
fn checked_sub_underflow_returns_none() {
assert_eq!(
<I128s12 as CheckedSub>::checked_sub(&I128s12::MIN, &I128s12::ONE),
None
);
}
#[test]
fn checked_mul_basic() {
let half = I128s12::from_bits(500_000_000_000); let quarter = I128s12::from_bits(250_000_000_000); assert_eq!(
<I128s12 as CheckedMul>::checked_mul(&half, &half),
Some(quarter)
);
}
#[test]
fn checked_mul_overflow_returns_none() {
let two = I128s12::from_bits(2_000_000_000_000);
assert_eq!(
<I128s12 as CheckedMul>::checked_mul(&I128s12::MAX, &two),
None
);
}
#[test]
fn checked_div_basic() {
let half = I128s12::from_bits(500_000_000_000); let quarter = I128s12::from_bits(250_000_000_000); let two = I128s12::from_bits(2_000_000_000_000); assert_eq!(
<I128s12 as CheckedDiv>::checked_div(&half, &two),
Some(quarter)
);
}
#[test]
fn checked_div_by_zero_returns_none() {
assert_eq!(
<I128s12 as CheckedDiv>::checked_div(&I128s12::ONE, &I128s12::ZERO),
None
);
}
#[test]
fn checked_div_overflow_returns_none() {
let neg_one = -I128s12::ONE;
assert_eq!(
<I128s12 as CheckedDiv>::checked_div(&I128s12::MIN, &neg_one),
None
);
assert_eq!(
<I128s12 as CheckedDiv>::checked_div(&I128s12::MAX, &I128s12::ONE),
Some(I128s12::MAX)
);
}
#[test]
fn checked_rem_basic() {
let a = I128s12::from_bits(5_500_000_000_000); let b = I128s12::from_bits(2_000_000_000_000); let expected = I128s12::from_bits(1_500_000_000_000); assert_eq!(
<I128s12 as CheckedRem>::checked_rem(&a, &b),
Some(expected)
);
}
#[test]
fn checked_rem_by_zero_returns_none() {
assert_eq!(
<I128s12 as CheckedRem>::checked_rem(&I128s12::ONE, &I128s12::ZERO),
None
);
}
#[test]
fn checked_neg_basic() {
let one = I128s12::ONE;
let neg_one = -I128s12::ONE;
assert_eq!(
<I128s12 as CheckedNeg>::checked_neg(&one),
Some(neg_one)
);
assert_eq!(
<I128s12 as CheckedNeg>::checked_neg(&I128s12::ZERO),
Some(I128s12::ZERO)
);
}
#[test]
fn checked_neg_min_returns_none() {
assert_eq!(
<I128s12 as CheckedNeg>::checked_neg(&I128s12::MIN),
None
);
}
fn lcg_i128_seq(seed: i128, n: usize) -> Vec<i128> {
let mut state: i128 = seed;
let mut out = Vec::with_capacity(n);
for _ in 0..n {
state = state
.wrapping_mul(6_364_136_223_846_793_005_i128)
.wrapping_add(1_442_695_040_888_963_407_i128);
out.push(state);
}
out
}
#[test]
fn checked_mul_trait_matches_inherent_256_pairs() {
let seeds = lcg_i128_seq(0x1234_5678_9ABC_DEF0, 512);
for pair in seeds.chunks_exact(2) {
let a = I128s12::from_bits(pair[0]);
let b = I128s12::from_bits(pair[1]);
let trait_result = <I128s12 as CheckedMul>::checked_mul(&a, &b);
let inherent_result = a.checked_mul(b);
assert_eq!(
trait_result, inherent_result,
"CheckedMul trait != inherent for a={a:?} b={b:?}"
);
}
}
#[test]
fn checked_div_trait_matches_inherent_256_pairs() {
let seeds = lcg_i128_seq(0xDEAD_BEEF_CAFE_0001, 512);
for pair in seeds.chunks_exact(2) {
let a = I128s12::from_bits(pair[0]);
let b_bits = if pair[1] == 0 { I128s12::multiplier() } else { pair[1] };
let b = I128s12::from_bits(b_bits);
let trait_result = <I128s12 as CheckedDiv>::checked_div(&a, &b);
let inherent_result = a.checked_div(b);
assert_eq!(
trait_result, inherent_result,
"CheckedDiv trait != inherent for a={a:?} b={b:?}"
);
}
}
#[test]
fn checked_mul_trait_matches_inherent_boundary() {
let cases: &[(I128s12, I128s12)] = &[
(I128s12::MAX, I128s12::ZERO),
(I128s12::MIN, I128s12::ZERO),
(I128s12::MAX, I128s12::ONE),
(I128s12::MIN, I128s12::ONE),
(I128s12::MAX, I128s12::MAX),
(I128s12::MIN, I128s12::MIN),
(I128s12::from_bits(0), I128s12::from_bits(0)),
(I128s12::from_bits(1), I128s12::from_bits(1)),
(I128s12::from_bits(-1), I128s12::from_bits(1)),
(I128s12::from_bits(1), I128s12::from_bits(-1)),
(I128s12::from_bits(-1), I128s12::from_bits(-1)),
];
for &(a, b) in cases {
let trait_result = <I128s12 as CheckedMul>::checked_mul(&a, &b);
let inherent_result = a.checked_mul(b);
assert_eq!(
trait_result, inherent_result,
"CheckedMul trait != inherent at boundary a={a:?} b={b:?}"
);
}
}
#[test]
fn checked_div_trait_matches_inherent_boundary() {
let neg_one = -I128s12::ONE;
let cases: &[(I128s12, I128s12)] = &[
(I128s12::MAX, I128s12::ONE),
(I128s12::MIN, I128s12::ONE),
(I128s12::MAX, I128s12::MAX),
(I128s12::MIN, I128s12::MIN),
(I128s12::ZERO, I128s12::ONE),
(I128s12::ONE, I128s12::MAX),
(I128s12::ONE, I128s12::ZERO),
(I128s12::MAX, I128s12::ZERO),
(I128s12::MIN, neg_one),
(I128s12::from_bits(1), I128s12::from_bits(1)),
(I128s12::from_bits(-1), I128s12::from_bits(1)),
(I128s12::from_bits(1), I128s12::from_bits(-1)),
(I128s12::from_bits(-1), I128s12::from_bits(-1)),
];
for &(a, b) in cases {
let trait_result = <I128s12 as CheckedDiv>::checked_div(&a, &b);
let inherent_result = a.checked_div(b);
assert_eq!(
trait_result, inherent_result,
"CheckedDiv trait != inherent at boundary a={a:?} b={b:?}"
);
}
}
#[test]
fn from_str_radix_non_ten_returns_invalid() {
let result = <I128s12 as Num>::from_str_radix("1", 16);
assert!(result.is_err());
let result_2 = <I128s12 as Num>::from_str_radix("1", 2);
assert!(result_2.is_err());
}
#[test]
fn from_str_radix_base_ten_delegates_to_from_str() {
let parsed = <I128s12 as Num>::from_str_radix("1", 10).expect("parse 1");
assert_eq!(parsed, I128s12::ONE);
}
#[test]
fn traits_compile_at_scale_6() {
type D6 = I128<6>;
assert_eq!(<D6 as Zero>::zero(), D6::ZERO);
assert_eq!(<D6 as One>::one(), D6::ONE);
assert_eq!(<D6 as Bounded>::min_value(), D6::MIN);
assert_eq!(<D6 as Bounded>::max_value(), D6::MAX);
let v: D6 = <D6 as FromPrimitive>::from_i64(42).unwrap();
assert_eq!(<D6 as ToPrimitive>::to_i64(&v), Some(42_i64));
}
#[test]
fn numcast_from_i32() {
let v: I128s12 = <I128s12 as NumCast>::from(42_i32).expect("in-range");
assert_eq!(v, <I128s12 as From<i32>>::from(42_i32));
}
#[test]
fn numcast_from_f64_preserves_fractional() {
let v: I128s12 = <I128s12 as NumCast>::from(1.5_f64).expect("in-range");
assert_eq!(v, I128s12::from_f64_lossy(1.5_f64));
}
#[test]
fn numcast_from_f64_nan_returns_none() {
assert!(<I128s12 as NumCast>::from(f64::NAN).is_none());
}
#[test]
fn numcast_from_f64_out_of_range_returns_none() {
assert!(<I128s12 as NumCast>::from(1e30_f64).is_none());
}
#[test]
fn numcast_from_i64_above_f64_mantissa_is_exact() {
let v: i64 = 1_i64 << 54;
let d: I128s12 = <I128s12 as NumCast>::from(v).expect("in-range");
assert_eq!(<I128s12 as ToPrimitive>::to_i64(&d), Some(v));
}
}