use core::fmt::{Debug, Display};
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, Sub, SubAssign};
mod private {
pub trait Sealed {}
impl Sealed for f64 {}
impl Sealed for f32 {}
impl Sealed for i8 {}
impl Sealed for i16 {}
impl Sealed for i32 {}
impl Sealed for i64 {}
impl Sealed for i128 {}
#[cfg(feature = "scalar-rational")]
impl Sealed for num_rational::Rational64 {}
#[cfg(feature = "scalar-rational")]
impl Sealed for num_rational::Rational32 {}
}
pub trait Scalar:
private::Sealed
+ Copy
+ Clone
+ Debug
+ PartialEq
+ PartialOrd
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
+ AddAssign
+ SubAssign
+ MulAssign
+ DivAssign
+ Neg<Output = Self>
+ Sized
+ 'static
{
const ZERO: Self;
const ONE: Self;
fn abs(self) -> Self;
fn min(self, other: Self) -> Self;
fn max(self, other: Self) -> Self;
fn rem_euclid(self, rhs: Self) -> Self;
}
pub trait Real: Scalar + Display + Rem<Output = Self> {
const PI: Self;
const TAU: Self;
const E: Self;
const INFINITY: Self;
const NEG_INFINITY: Self;
const NAN: Self;
fn from_f64(value: f64) -> Self;
fn to_f64(self) -> f64;
fn signum(self) -> Self;
fn is_nan(self) -> bool;
fn is_infinite(self) -> bool;
fn is_finite(self) -> bool;
fn mul_add(self, a: Self, b: Self) -> Self;
fn floor(self) -> Self;
fn ceil(self) -> Self;
fn round(self) -> Self;
fn trunc(self) -> Self;
fn fract(self) -> Self;
fn powf(self, exp: Self) -> Self;
fn powi(self, exp: i32) -> Self;
fn sqrt(self) -> Self;
fn cbrt(self) -> Self;
fn ln(self) -> Self;
fn log10(self) -> Self;
fn log2(self) -> Self;
fn log(self, base: Self) -> Self;
fn exp(self) -> Self;
fn exp2(self) -> Self;
fn hypot(self, other: Self) -> Self;
}
pub trait Transcendental: Real {
fn sin(self) -> Self;
fn cos(self) -> Self;
fn tan(self) -> Self;
fn sin_cos(self) -> (Self, Self);
fn asin(self) -> Self;
fn acos(self) -> Self;
fn atan(self) -> Self;
fn atan2(self, other: Self) -> Self;
fn sinh(self) -> Self;
fn cosh(self) -> Self;
fn tanh(self) -> Self;
fn asinh(self) -> Self;
fn acosh(self) -> Self;
fn atanh(self) -> Self;
}
pub trait Exact: Scalar {
fn to_f64_approx(self) -> f64;
fn from_f64_approx(value: f64) -> Self;
fn checked_from_f64(value: f64) -> Option<Self>;
}
pub trait IntegerScalar: Exact + Display {}
impl Scalar for f64 {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
#[inline]
fn abs(self) -> Self {
#[cfg(feature = "std")]
{
f64::abs(self)
}
#[cfg(not(feature = "std"))]
{
libm::fabs(self)
}
}
#[inline]
fn min(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f64::min(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::fmin(self, other)
}
}
#[inline]
fn max(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f64::max(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::fmax(self, other)
}
}
#[inline]
fn rem_euclid(self, rhs: Self) -> Self {
#[cfg(feature = "std")]
{
f64::rem_euclid(self, rhs)
}
#[cfg(not(feature = "std"))]
{
let r = libm::fmod(self, rhs);
if r < 0.0 {
r + libm::fabs(rhs)
} else {
r
}
}
}
}
impl Real for f64 {
const PI: Self = core::f64::consts::PI;
const TAU: Self = core::f64::consts::TAU;
const E: Self = core::f64::consts::E;
const INFINITY: Self = f64::INFINITY;
const NEG_INFINITY: Self = f64::NEG_INFINITY;
const NAN: Self = f64::NAN;
#[inline]
fn from_f64(value: f64) -> Self {
value
}
#[inline]
fn to_f64(self) -> f64 {
self
}
#[inline]
fn signum(self) -> Self {
f64::signum(self)
}
#[inline]
fn is_nan(self) -> bool {
f64::is_nan(self)
}
#[inline]
fn is_infinite(self) -> bool {
f64::is_infinite(self)
}
#[inline]
fn is_finite(self) -> bool {
f64::is_finite(self)
}
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self {
#[cfg(feature = "std")]
{
f64::mul_add(self, a, b)
}
#[cfg(not(feature = "std"))]
{
libm::fma(self, a, b)
}
}
#[inline]
fn floor(self) -> Self {
#[cfg(feature = "std")]
{
f64::floor(self)
}
#[cfg(not(feature = "std"))]
{
libm::floor(self)
}
}
#[inline]
fn ceil(self) -> Self {
#[cfg(feature = "std")]
{
f64::ceil(self)
}
#[cfg(not(feature = "std"))]
{
libm::ceil(self)
}
}
#[inline]
fn round(self) -> Self {
#[cfg(feature = "std")]
{
f64::round(self)
}
#[cfg(not(feature = "std"))]
{
libm::round(self)
}
}
#[inline]
fn trunc(self) -> Self {
#[cfg(feature = "std")]
{
f64::trunc(self)
}
#[cfg(not(feature = "std"))]
{
libm::trunc(self)
}
}
#[inline]
fn fract(self) -> Self {
self - self.trunc()
}
#[inline]
fn powf(self, exp: Self) -> Self {
#[cfg(feature = "std")]
{
f64::powf(self, exp)
}
#[cfg(not(feature = "std"))]
{
libm::pow(self, exp)
}
}
#[inline]
fn powi(self, exp: i32) -> Self {
#[cfg(feature = "std")]
{
f64::powi(self, exp)
}
#[cfg(not(feature = "std"))]
{
libm::pow(self, exp as f64)
}
}
#[inline]
fn sqrt(self) -> Self {
#[cfg(feature = "std")]
{
f64::sqrt(self)
}
#[cfg(not(feature = "std"))]
{
libm::sqrt(self)
}
}
#[inline]
fn cbrt(self) -> Self {
#[cfg(feature = "std")]
{
f64::cbrt(self)
}
#[cfg(not(feature = "std"))]
{
libm::cbrt(self)
}
}
#[inline]
fn ln(self) -> Self {
#[cfg(feature = "std")]
{
f64::ln(self)
}
#[cfg(not(feature = "std"))]
{
libm::log(self)
}
}
#[inline]
fn log10(self) -> Self {
#[cfg(feature = "std")]
{
f64::log10(self)
}
#[cfg(not(feature = "std"))]
{
libm::log10(self)
}
}
#[inline]
fn log2(self) -> Self {
#[cfg(feature = "std")]
{
f64::log2(self)
}
#[cfg(not(feature = "std"))]
{
libm::log2(self)
}
}
#[inline]
fn log(self, base: Self) -> Self {
#[cfg(feature = "std")]
{
f64::log(self, base)
}
#[cfg(not(feature = "std"))]
{
libm::log(self) / libm::log(base)
}
}
#[inline]
fn exp(self) -> Self {
#[cfg(feature = "std")]
{
f64::exp(self)
}
#[cfg(not(feature = "std"))]
{
libm::exp(self)
}
}
#[inline]
fn exp2(self) -> Self {
#[cfg(feature = "std")]
{
f64::exp2(self)
}
#[cfg(not(feature = "std"))]
{
libm::exp2(self)
}
}
#[inline]
fn hypot(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f64::hypot(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::hypot(self, other)
}
}
}
impl Transcendental for f64 {
#[inline]
fn sin(self) -> Self {
#[cfg(feature = "std")]
{
f64::sin(self)
}
#[cfg(not(feature = "std"))]
{
libm::sin(self)
}
}
#[inline]
fn cos(self) -> Self {
#[cfg(feature = "std")]
{
f64::cos(self)
}
#[cfg(not(feature = "std"))]
{
libm::cos(self)
}
}
#[inline]
fn tan(self) -> Self {
#[cfg(feature = "std")]
{
f64::tan(self)
}
#[cfg(not(feature = "std"))]
{
libm::tan(self)
}
}
#[inline]
fn sin_cos(self) -> (Self, Self) {
#[cfg(feature = "std")]
{
f64::sin_cos(self)
}
#[cfg(not(feature = "std"))]
{
libm::sincos(self)
}
}
#[inline]
fn asin(self) -> Self {
#[cfg(feature = "std")]
{
f64::asin(self)
}
#[cfg(not(feature = "std"))]
{
libm::asin(self)
}
}
#[inline]
fn acos(self) -> Self {
#[cfg(feature = "std")]
{
f64::acos(self)
}
#[cfg(not(feature = "std"))]
{
libm::acos(self)
}
}
#[inline]
fn atan(self) -> Self {
#[cfg(feature = "std")]
{
f64::atan(self)
}
#[cfg(not(feature = "std"))]
{
libm::atan(self)
}
}
#[inline]
fn atan2(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f64::atan2(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::atan2(self, other)
}
}
#[inline]
fn sinh(self) -> Self {
#[cfg(feature = "std")]
{
f64::sinh(self)
}
#[cfg(not(feature = "std"))]
{
libm::sinh(self)
}
}
#[inline]
fn cosh(self) -> Self {
#[cfg(feature = "std")]
{
f64::cosh(self)
}
#[cfg(not(feature = "std"))]
{
libm::cosh(self)
}
}
#[inline]
fn tanh(self) -> Self {
#[cfg(feature = "std")]
{
f64::tanh(self)
}
#[cfg(not(feature = "std"))]
{
libm::tanh(self)
}
}
#[inline]
fn asinh(self) -> Self {
#[cfg(feature = "std")]
{
f64::asinh(self)
}
#[cfg(not(feature = "std"))]
{
libm::asinh(self)
}
}
#[inline]
fn acosh(self) -> Self {
#[cfg(feature = "std")]
{
f64::acosh(self)
}
#[cfg(not(feature = "std"))]
{
libm::acosh(self)
}
}
#[inline]
fn atanh(self) -> Self {
#[cfg(feature = "std")]
{
f64::atanh(self)
}
#[cfg(not(feature = "std"))]
{
libm::atanh(self)
}
}
}
impl Scalar for f32 {
const ZERO: Self = 0.0;
const ONE: Self = 1.0;
#[inline]
fn abs(self) -> Self {
#[cfg(feature = "std")]
{
f32::abs(self)
}
#[cfg(not(feature = "std"))]
{
libm::fabsf(self)
}
}
#[inline]
fn min(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f32::min(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::fminf(self, other)
}
}
#[inline]
fn max(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f32::max(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::fmaxf(self, other)
}
}
#[inline]
fn rem_euclid(self, rhs: Self) -> Self {
#[cfg(feature = "std")]
{
f32::rem_euclid(self, rhs)
}
#[cfg(not(feature = "std"))]
{
let r = libm::fmodf(self, rhs);
if r < 0.0 {
r + libm::fabsf(rhs)
} else {
r
}
}
}
}
impl Real for f32 {
const PI: Self = core::f32::consts::PI;
const TAU: Self = core::f32::consts::TAU;
const E: Self = core::f32::consts::E;
const INFINITY: Self = f32::INFINITY;
const NEG_INFINITY: Self = f32::NEG_INFINITY;
const NAN: Self = f32::NAN;
#[inline]
fn from_f64(value: f64) -> Self {
value as f32
}
#[inline]
fn to_f64(self) -> f64 {
self as f64
}
#[inline]
fn signum(self) -> Self {
f32::signum(self)
}
#[inline]
fn is_nan(self) -> bool {
f32::is_nan(self)
}
#[inline]
fn is_infinite(self) -> bool {
f32::is_infinite(self)
}
#[inline]
fn is_finite(self) -> bool {
f32::is_finite(self)
}
#[inline]
fn mul_add(self, a: Self, b: Self) -> Self {
#[cfg(feature = "std")]
{
f32::mul_add(self, a, b)
}
#[cfg(not(feature = "std"))]
{
libm::fmaf(self, a, b)
}
}
#[inline]
fn floor(self) -> Self {
#[cfg(feature = "std")]
{
f32::floor(self)
}
#[cfg(not(feature = "std"))]
{
libm::floorf(self)
}
}
#[inline]
fn ceil(self) -> Self {
#[cfg(feature = "std")]
{
f32::ceil(self)
}
#[cfg(not(feature = "std"))]
{
libm::ceilf(self)
}
}
#[inline]
fn round(self) -> Self {
#[cfg(feature = "std")]
{
f32::round(self)
}
#[cfg(not(feature = "std"))]
{
libm::roundf(self)
}
}
#[inline]
fn trunc(self) -> Self {
#[cfg(feature = "std")]
{
f32::trunc(self)
}
#[cfg(not(feature = "std"))]
{
libm::truncf(self)
}
}
#[inline]
fn fract(self) -> Self {
self - self.trunc()
}
#[inline]
fn powf(self, exp: Self) -> Self {
#[cfg(feature = "std")]
{
f32::powf(self, exp)
}
#[cfg(not(feature = "std"))]
{
libm::powf(self, exp)
}
}
#[inline]
fn powi(self, exp: i32) -> Self {
#[cfg(feature = "std")]
{
f32::powi(self, exp)
}
#[cfg(not(feature = "std"))]
{
libm::powf(self, exp as f32)
}
}
#[inline]
fn sqrt(self) -> Self {
#[cfg(feature = "std")]
{
f32::sqrt(self)
}
#[cfg(not(feature = "std"))]
{
libm::sqrtf(self)
}
}
#[inline]
fn cbrt(self) -> Self {
#[cfg(feature = "std")]
{
f32::cbrt(self)
}
#[cfg(not(feature = "std"))]
{
libm::cbrtf(self)
}
}
#[inline]
fn ln(self) -> Self {
#[cfg(feature = "std")]
{
f32::ln(self)
}
#[cfg(not(feature = "std"))]
{
libm::logf(self)
}
}
#[inline]
fn log10(self) -> Self {
#[cfg(feature = "std")]
{
f32::log10(self)
}
#[cfg(not(feature = "std"))]
{
libm::log10f(self)
}
}
#[inline]
fn log2(self) -> Self {
#[cfg(feature = "std")]
{
f32::log2(self)
}
#[cfg(not(feature = "std"))]
{
libm::log2f(self)
}
}
#[inline]
fn log(self, base: Self) -> Self {
#[cfg(feature = "std")]
{
f32::log(self, base)
}
#[cfg(not(feature = "std"))]
{
libm::logf(self) / libm::logf(base)
}
}
#[inline]
fn exp(self) -> Self {
#[cfg(feature = "std")]
{
f32::exp(self)
}
#[cfg(not(feature = "std"))]
{
libm::expf(self)
}
}
#[inline]
fn exp2(self) -> Self {
#[cfg(feature = "std")]
{
f32::exp2(self)
}
#[cfg(not(feature = "std"))]
{
libm::exp2f(self)
}
}
#[inline]
fn hypot(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f32::hypot(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::hypotf(self, other)
}
}
}
impl Transcendental for f32 {
#[inline]
fn sin(self) -> Self {
#[cfg(feature = "std")]
{
f32::sin(self)
}
#[cfg(not(feature = "std"))]
{
libm::sinf(self)
}
}
#[inline]
fn cos(self) -> Self {
#[cfg(feature = "std")]
{
f32::cos(self)
}
#[cfg(not(feature = "std"))]
{
libm::cosf(self)
}
}
#[inline]
fn tan(self) -> Self {
#[cfg(feature = "std")]
{
f32::tan(self)
}
#[cfg(not(feature = "std"))]
{
libm::tanf(self)
}
}
#[inline]
fn sin_cos(self) -> (Self, Self) {
#[cfg(feature = "std")]
{
f32::sin_cos(self)
}
#[cfg(not(feature = "std"))]
{
libm::sincosf(self)
}
}
#[inline]
fn asin(self) -> Self {
#[cfg(feature = "std")]
{
f32::asin(self)
}
#[cfg(not(feature = "std"))]
{
libm::asinf(self)
}
}
#[inline]
fn acos(self) -> Self {
#[cfg(feature = "std")]
{
f32::acos(self)
}
#[cfg(not(feature = "std"))]
{
libm::acosf(self)
}
}
#[inline]
fn atan(self) -> Self {
#[cfg(feature = "std")]
{
f32::atan(self)
}
#[cfg(not(feature = "std"))]
{
libm::atanf(self)
}
}
#[inline]
fn atan2(self, other: Self) -> Self {
#[cfg(feature = "std")]
{
f32::atan2(self, other)
}
#[cfg(not(feature = "std"))]
{
libm::atan2f(self, other)
}
}
#[inline]
fn sinh(self) -> Self {
#[cfg(feature = "std")]
{
f32::sinh(self)
}
#[cfg(not(feature = "std"))]
{
libm::sinhf(self)
}
}
#[inline]
fn cosh(self) -> Self {
#[cfg(feature = "std")]
{
f32::cosh(self)
}
#[cfg(not(feature = "std"))]
{
libm::coshf(self)
}
}
#[inline]
fn tanh(self) -> Self {
#[cfg(feature = "std")]
{
f32::tanh(self)
}
#[cfg(not(feature = "std"))]
{
libm::tanhf(self)
}
}
#[inline]
fn asinh(self) -> Self {
#[cfg(feature = "std")]
{
f32::asinh(self)
}
#[cfg(not(feature = "std"))]
{
libm::asinhf(self)
}
}
#[inline]
fn acosh(self) -> Self {
#[cfg(feature = "std")]
{
f32::acosh(self)
}
#[cfg(not(feature = "std"))]
{
libm::acoshf(self)
}
}
#[inline]
fn atanh(self) -> Self {
#[cfg(feature = "std")]
{
f32::atanh(self)
}
#[cfg(not(feature = "std"))]
{
libm::atanhf(self)
}
}
}
#[cfg(feature = "scalar-rational")]
mod rational_impl {
use super::*;
use num_rational::{Rational32, Rational64};
impl Scalar for Rational64 {
const ZERO: Self = Rational64::new_raw(0, 1);
const ONE: Self = Rational64::new_raw(1, 1);
#[inline]
fn abs(self) -> Self {
if self < Self::ZERO {
-self
} else {
self
}
}
#[inline]
fn min(self, other: Self) -> Self {
if self < other {
self
} else {
other
}
}
#[inline]
fn max(self, other: Self) -> Self {
if self > other {
self
} else {
other
}
}
#[inline]
fn rem_euclid(self, rhs: Self) -> Self {
let r = self - (self / rhs).trunc() * rhs;
if r < Self::ZERO {
r + rhs.abs()
} else {
r
}
}
}
impl Exact for Rational64 {
#[inline]
fn to_f64_approx(self) -> f64 {
*self.numer() as f64 / *self.denom() as f64
}
#[inline]
fn from_f64_approx(value: f64) -> Self {
Rational64::approximate_float(value).unwrap_or_else(|| {
if value.is_nan() {
Rational64::new_raw(0, 1)
} else if value > 0.0 {
Rational64::new_raw(i64::MAX, 1)
} else {
Rational64::new_raw(i64::MIN, 1)
}
})
}
#[inline]
fn checked_from_f64(value: f64) -> Option<Self> {
if !value.is_finite() {
return None;
}
Rational64::approximate_float(value)
}
}
impl Scalar for Rational32 {
const ZERO: Self = Rational32::new_raw(0, 1);
const ONE: Self = Rational32::new_raw(1, 1);
#[inline]
fn abs(self) -> Self {
if self < Self::ZERO {
-self
} else {
self
}
}
#[inline]
fn min(self, other: Self) -> Self {
if self < other {
self
} else {
other
}
}
#[inline]
fn max(self, other: Self) -> Self {
if self > other {
self
} else {
other
}
}
#[inline]
fn rem_euclid(self, rhs: Self) -> Self {
let r = self - (self / rhs).trunc() * rhs;
if r < Self::ZERO {
r + rhs.abs()
} else {
r
}
}
}
impl Exact for Rational32 {
#[inline]
fn to_f64_approx(self) -> f64 {
*self.numer() as f64 / *self.denom() as f64
}
#[inline]
fn from_f64_approx(value: f64) -> Self {
Rational32::approximate_float(value).unwrap_or_else(|| {
if value.is_nan() {
Rational32::new_raw(0, 1)
} else if value > 0.0 {
Rational32::new_raw(i32::MAX, 1)
} else {
Rational32::new_raw(i32::MIN, 1)
}
})
}
#[inline]
fn checked_from_f64(value: f64) -> Option<Self> {
if !value.is_finite() {
return None;
}
Rational32::approximate_float(value)
}
}
}
macro_rules! impl_scalar_for_signed_int {
($($t:ty),*) => { $(
impl Scalar for $t {
const ZERO: Self = 0;
const ONE: Self = 1;
#[inline]
fn abs(self) -> Self {
self.saturating_abs()
}
#[inline]
fn min(self, other: Self) -> Self {
Ord::min(self, other)
}
#[inline]
fn max(self, other: Self) -> Self {
Ord::max(self, other)
}
#[inline]
fn rem_euclid(self, rhs: Self) -> Self {
self.rem_euclid(rhs)
}
}
impl Exact for $t {
#[inline]
fn to_f64_approx(self) -> f64 {
self as f64
}
#[inline]
fn from_f64_approx(value: f64) -> Self {
value as Self
}
#[inline]
fn checked_from_f64(value: f64) -> Option<Self> {
if !value.is_finite() {
return None;
}
const F_MIN: f64 = <$t>::MIN as f64;
const F_MAX_EXCL: f64 = <$t>::MAX as f64 + 1.0;
if !(F_MIN..F_MAX_EXCL).contains(&value) {
return None;
}
Some(value as Self)
}
}
impl IntegerScalar for $t {}
)* };
}
impl_scalar_for_signed_int!(i8, i16, i32, i64, i128);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_f64_scalar_basic() {
assert_eq!(f64::ZERO, 0.0);
assert_eq!(f64::ONE, 1.0);
assert_eq!((-5.0_f64).abs(), 5.0);
assert_eq!(3.0_f64.min(5.0), 3.0);
assert_eq!(3.0_f64.max(5.0), 5.0);
}
#[test]
fn test_f64_real() {
assert!((f64::PI - core::f64::consts::PI).abs() < 1e-15);
assert_eq!(f64::from_f64(42.5), 42.5);
assert_eq!(42.5_f64.to_f64(), 42.5);
assert!(f64::NAN.is_nan());
assert!(f64::INFINITY.is_infinite());
}
#[test]
fn test_f64_transcendental() {
let angle = core::f64::consts::FRAC_PI_2;
assert!((angle.sin() - 1.0).abs() < 1e-15);
assert!(angle.cos().abs() < 1e-15);
}
#[test]
fn test_f32_scalar_basic() {
assert_eq!(f32::ZERO, 0.0);
assert_eq!(f32::ONE, 1.0);
assert_eq!((-5.0_f32).abs(), 5.0);
}
#[test]
fn test_f32_real() {
assert!((f32::PI - core::f32::consts::PI).abs() < 1e-6);
assert_eq!(f32::from_f64(42.5), 42.5);
}
#[test]
fn test_f32_transcendental() {
let angle = core::f32::consts::FRAC_PI_2;
assert!((angle.sin() - 1.0).abs() < 1e-6);
}
#[test]
fn test_i32_scalar_basic() {
assert_eq!(i32::ZERO, 0);
assert_eq!(i32::ONE, 1);
assert_eq!((-5_i32).abs(), 5);
assert_eq!(Scalar::min(3_i32, 5), 3);
assert_eq!(Scalar::max(3_i32, 5), 5);
assert_eq!(7_i32.rem_euclid(4), 3);
assert_eq!((-7_i32).rem_euclid(4), 1);
}
#[test]
fn test_i64_scalar_basic() {
assert_eq!(i64::ZERO, 0);
assert_eq!(i64::ONE, 1);
assert_eq!((-100_i64).abs(), 100);
assert_eq!(Scalar::min(10_i64, 20), 10);
assert_eq!(Scalar::max(10_i64, 20), 20);
}
#[test]
fn test_i8_scalar_basic() {
assert_eq!(i8::ZERO, 0);
assert_eq!(i8::ONE, 1);
assert_eq!((-5_i8).abs(), 5);
assert_eq!(Scalar::max(127_i8, -128), 127);
}
#[test]
fn test_i16_scalar_basic() {
assert_eq!(i16::ZERO, 0);
assert_eq!(i16::ONE, 1);
assert_eq!((-1000_i16).abs(), 1000);
}
#[test]
fn test_i128_scalar_basic() {
assert_eq!(i128::ZERO, 0);
assert_eq!(i128::ONE, 1);
assert_eq!((-42_i128).abs(), 42);
}
#[test]
fn test_integer_exact_conversions() {
assert_eq!(42_i32.to_f64_approx(), 42.0);
assert_eq!(i32::from_f64_approx(42.9), 42); assert_eq!(i32::from_f64_approx(-3.7), -3);
assert_eq!(1000_i64.to_f64_approx(), 1000.0);
assert_eq!(i64::from_f64_approx(1500.0), 1500);
assert_eq!(i8::from_f64_approx(100.0), 100);
}
#[test]
fn checked_from_f64_i8_boundaries() {
assert_eq!(i8::checked_from_f64(127.0), Some(127));
assert_eq!(i8::checked_from_f64(127.9), Some(127)); assert_eq!(i8::checked_from_f64(128.0), None);
assert_eq!(i8::checked_from_f64(-128.0), Some(-128));
assert_eq!(i8::checked_from_f64(-128.9), None);
assert_eq!(i8::checked_from_f64(f64::INFINITY), None);
assert_eq!(i8::checked_from_f64(f64::NAN), None);
}
#[test]
fn checked_from_f64_i32_boundaries() {
assert_eq!(i32::checked_from_f64(2147483647.0), Some(i32::MAX));
assert_eq!(i32::checked_from_f64(2147483648.0), None);
assert_eq!(i32::checked_from_f64(-2147483648.0), Some(i32::MIN));
assert_eq!(i32::checked_from_f64(-2147483649.0), None);
}
#[test]
fn checked_from_f64_i64_no_false_success_near_max() {
let near_max_f64 = (i64::MAX - 1) as f64; assert_eq!(i64::checked_from_f64(near_max_f64), None);
assert_eq!(i64::checked_from_f64(i64::MAX as f64), None);
let safe_max_f64 = 9_223_372_036_854_774_784.0_f64; assert_eq!(
i64::checked_from_f64(safe_max_f64),
Some(9_223_372_036_854_774_784_i64)
);
assert_eq!(i64::checked_from_f64(i64::MIN as f64), Some(i64::MIN));
}
}