fdtd-coremem/crates/cross/src/real.rs

554 lines
12 KiB
Rust

use core::intrinsics;
use core::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign,
};
use core::cmp::{Eq, Ord, Ordering, PartialOrd};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "fmt")]
use core::fmt;
pub trait ToFloat {
// TODO: make these by-value
fn to_f32(&self) -> f32 {
self.to_f64() as _
}
fn to_f64(&self) -> f64 {
self.to_f32() as _
}
fn to_r32(&self) -> R32 {
R32::new(self.to_f32())
}
fn to_r64(&self) -> R64 {
R64::new(self.to_f64())
}
}
#[cfg(all(not(feature = "fmt"), not(feature = "serde")))]
pub trait RealFeatures {}
#[cfg(all(not(feature = "fmt"), feature = "serde"))]
pub trait RealFeatures: Serialize + for<'a> Deserialize<'a> {}
#[cfg(all(feature = "fmt", not(feature = "serde")))]
pub trait RealFeatures: fmt::LowerExp + fmt::Display + fmt::Debug {}
#[cfg(all(feature = "fmt", feature = "serde"))]
pub trait RealFeatures: fmt::LowerExp + fmt::Display + fmt::Debug + Serialize + for<'a> Deserialize<'a> {}
/// This exists to allow configuration over # of bits (f32 v.s. f64) as well as
/// constraints.
pub trait Real:
ToFloat
+ RealFeatures
+ PartialOrd
+ Add<Output=Self>
+ Sub<Output=Self>
+ Mul<Output=Self>
+ Div<Output=Self>
+ AddAssign
+ MulAssign
+ SubAssign
+ DivAssign
+ Neg<Output=Self>
+ Copy
+ Clone
+ Default
+ Send
+ Sync
+ 'static {
// TODO: fold with from_<blah>
fn from_primitive<P: ToFloat>(p: P) -> Self {
Self::from_f64(p.to_f64())
}
fn from_f32(f: f32) -> Self {
Self::from_f64(f as _)
}
fn from_f64(f: f64) -> Self {
Self::from_f32(f as _)
}
fn from_u64(u: u64) -> Self {
Self::from_primitive(u)
}
fn from_u32(u: u32) -> Self {
Self::from_primitive(u)
}
// TODO: make this by-value
fn cast<R: Real>(&self) -> R {
R::from_primitive(*self)
}
fn is_finite(self) -> bool;
fn floor(self) -> Self;
fn ceil(self) -> Self;
fn round(self) -> Self;
fn abs(self) -> Self;
fn exp(self) -> Self;
fn powf(self, p: Self) -> Self;
fn sqrt(self) -> Self;
fn sin_cos(self) -> (Self, Self);
fn sin(self) -> Self {
self.sin_cos().0
}
fn cos(self) -> Self {
self.sin_cos().1
}
fn min_max_or_undefined(self, other: Self) -> (Self, Self) {
match self.partial_cmp(&other) {
Some(ordering) => match ordering {
Ordering::Less | Ordering::Equal => (self, other),
_ => (other, self),
},
_ => unreachable!(),
}
}
fn min_or_undefined(self, other: Self) -> Self {
self.min_max_or_undefined(other).0
}
fn max_or_undefined(self, other: Self) -> Self {
self.min_max_or_undefined(other).1
}
fn is_zero(self) -> bool {
self == Self::zero()
}
fn inv(self) -> Self {
Self::one() / self
}
fn zero() -> Self;
fn one() -> Self;
fn two() -> Self;
fn three() -> Self;
fn ten() -> Self;
fn tenth() -> Self;
fn third() -> Self;
fn half() -> Self;
fn pi() -> Self;
fn two_pi() -> Self;
fn ln2() -> Self;
/// Speed of light in a vacuum; m/s.
/// Also equal to 1/sqrt(epsilon_0 mu_0)
fn c() -> Self;
fn c_inv() -> Self;
/// Vaccum Permittivity
fn eps0() -> Self;
fn eps0_inv() -> Self;
fn twice_eps0() -> Self;
/// Vacuum Permeability
fn mu0() -> Self;
fn mu0_inv() -> Self;
}
macro_rules! decl_consts {
($wrap:expr) => {
fn zero() -> Self {
$wrap(0.0)
}
fn one() -> Self {
$wrap(1.0)
}
fn two() -> Self {
$wrap(2.0)
}
fn three() -> Self {
$wrap(3.0)
}
fn ten() -> Self {
$wrap(10.0)
}
fn tenth() -> Self {
$wrap(0.1)
}
fn third() -> Self {
$wrap(0.3333333333333333)
}
fn half() -> Self {
$wrap(0.5)
}
fn pi() -> Self {
$wrap(3.141592653589793)
}
fn two_pi() -> Self {
$wrap(6.283185307179586)
}
fn ln2() -> Self {
$wrap(0.6931471805599453)
}
/// Speed of light in a vacuum; m/s.
/// Also equal to 1/sqrt(epsilon_0 mu_0)
fn c() -> Self {
$wrap(299792458.0)
}
fn c_inv() -> Self {
$wrap(3.3356409519815204e-09)
}
/// Vaccum Permittivity
fn eps0() -> Self {
$wrap(8.854187812813e-12) // F/m
}
fn eps0_inv() -> Self {
$wrap(112940906737.1361) // m/F
}
fn twice_eps0() -> Self {
$wrap(1.7708375625626e-11) // F/m
}
/// Vacuum Permeability
fn mu0() -> Self {
$wrap(1.2566370621219e-6) // H/m
}
fn mu0_inv() -> Self {
$wrap(795774.715025073) // m/H
}
};
}
impl ToFloat for f32 {
fn to_f32(&self) -> f32 {
*self
}
}
impl RealFeatures for f32 {}
impl Real for f32 {
decl_consts!(Real::from_f32);
fn from_primitive<P: ToFloat>(p: P) -> Self {
Self::from_f32(p.to_f32())
}
fn from_f32(f: f32) -> Self {
f
}
fn is_finite(self) -> bool {
f32::is_finite(self)
}
fn floor(self) -> Self {
unsafe { intrinsics::floorf32(self) }
}
fn ceil(self) -> Self {
unsafe { intrinsics::ceilf32(self) }
}
fn round(self) -> Self {
unsafe { intrinsics::roundf32(self) }
}
fn abs(self) -> Self {
unsafe { intrinsics::fabsf32(self) }
}
fn exp(self) -> Self {
unsafe { intrinsics::expf32(self) }
}
fn sqrt(self) -> Self {
unsafe { intrinsics::sqrtf32(self) }
}
fn powf(self, p: Self) -> Self {
unsafe { intrinsics::powf32(self, p) }
}
fn sin_cos(self) -> (Self, Self) {
unsafe {(
intrinsics::sinf32(self),
intrinsics::cosf32(self),
)}
}
}
impl ToFloat for f64 {
fn to_f64(&self) -> f64 {
*self
}
}
impl RealFeatures for f64 {}
impl Real for f64 {
decl_consts!(Real::from_f64);
fn from_f64(f: f64) -> Self {
f
}
fn is_finite(self) -> bool {
f64::is_finite(self)
}
fn floor(self) -> Self {
unsafe { intrinsics::floorf64(self) }
}
fn ceil(self) -> Self {
unsafe { intrinsics::ceilf64(self) }
}
fn round(self) -> Self {
unsafe { intrinsics::roundf64(self) }
}
fn abs(self) -> Self {
unsafe { intrinsics::fabsf64(self) }
}
fn exp(self) -> Self {
unsafe { intrinsics::expf64(self) }
}
fn sqrt(self) -> Self {
unsafe { intrinsics::sqrtf64(self) }
}
fn powf(self, p: Self) -> Self {
unsafe { intrinsics::powf64(self, p) }
}
fn sin_cos(self) -> (Self, Self) {
unsafe {(
intrinsics::sinf64(self),
intrinsics::cosf64(self),
)}
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "fmt", derive(Debug))]
#[derive(Default, Copy, Clone, PartialEq, PartialOrd)]
pub struct Finite<T>(T);
pub type R32 = Finite<f32>;
pub type R64 = Finite<f64>;
impl<T: Real> Finite<T> {
fn new(inner: T) -> Self {
if !inner.is_finite() {
Self::handle_non_finite(inner);
}
Self(inner)
}
#[cfg(feature = "fmt")]
fn handle_non_finite(inner: T) -> ! {
panic!("{} is not finite", inner);
}
#[cfg(not(feature = "fmt"))]
fn handle_non_finite(_inner: T) -> ! {
panic!(); // expected a finite real
}
}
impl<T: Real> Eq for Finite<T> {}
impl<T: Real> Ord for Finite<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
#[cfg(feature = "fmt")]
impl<T: fmt::Display> fmt::Display for Finite<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "fmt")]
impl<T: fmt::LowerExp> fmt::LowerExp for Finite<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: Real> Neg for Finite<T> {
type Output = Self;
fn neg(self) -> Self {
Self::new(self.0.neg())
}
}
impl<T: Real> AddAssign for Finite<T> {
fn add_assign(&mut self, other: Self) {
*self = Self::new(self.0.add(other.0))
}
}
impl<T: Real> SubAssign for Finite<T> {
fn sub_assign(&mut self, other: Self) {
*self = Self::new(self.0.sub(other.0))
}
}
impl<T: Real> MulAssign for Finite<T> {
fn mul_assign(&mut self, other: Self) {
*self = Self::new(self.0.mul(other.0))
}
}
impl<T: Real> DivAssign for Finite<T> {
fn div_assign(&mut self, other: Self) {
*self = Self::new(self.0.div(other.0))
}
}
impl<T: Real> Add for Finite<T> {
type Output = Self;
fn add(self, other: Self) -> Self {
Self::new(self.0.add(other.0))
}
}
impl<T: Real> Sub for Finite<T> {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self::new(self.0.sub(other.0))
}
}
impl<T: Real> Mul for Finite<T> {
type Output = Self;
fn mul(self, other: Self) -> Self {
Self::new(self.0.mul(other.0))
}
}
impl<T: Real> Div for Finite<T> {
type Output = Self;
fn div(self, other: Self) -> Self {
Self::new(self.0.div(other.0))
}
}
#[cfg(feature = "iter")]
impl<T: Real> std::iter::Sum for Finite<T> {
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = Self>
{
iter.fold(Self::zero(), |a, b| a + b)
}
}
impl<T: ToFloat> ToFloat for Finite<T> {
fn to_f32(&self) -> f32 {
self.0.to_f32()
}
fn to_f64(&self) -> f64 {
self.0.to_f64()
}
}
impl<T: RealFeatures> RealFeatures for Finite<T> {}
impl<T: Real> Real for Finite<T> {
fn from_primitive<P: ToFloat>(p: P) -> Self {
Self::new(T::from_primitive(p))
}
fn from_f32(f: f32) -> Self {
Self::new(T::from_f32(f))
}
fn from_f64(f: f64) -> Self {
Self::new(T::from_f64(f))
}
fn is_finite(self) -> bool {
true
}
fn floor(self) -> Self {
Self::new(self.0.floor())
}
fn ceil(self) -> Self {
Self::new(self.0.ceil())
}
fn round(self) -> Self {
Self::new(self.0.round())
}
fn abs(self) -> Self {
Self::new(self.0.abs())
}
fn exp(self) -> Self {
Self::new(self.0.exp())
}
fn sqrt(self) -> Self {
Self::new(self.0.sqrt())
}
fn powf(self, p: Self) -> Self {
Self::new(self.0.powf(p.0))
}
fn sin_cos(self) -> (Self, Self) {
let (s, c) = self.0.sin_cos();
(Self::new(s), Self::new(c))
}
// we would ideally use `decl_consts` here, but that produces f64 -> f32 casts for R32 code.
fn zero() -> Self {
Self::from_primitive(T::zero())
}
fn one() -> Self {
Self::from_primitive(T::one())
}
fn two() -> Self {
Self::from_primitive(T::two())
}
fn three() -> Self {
Self::from_primitive(T::three())
}
fn ten() -> Self {
Self::from_primitive(T::ten())
}
fn tenth() -> Self {
Self::from_primitive(T::tenth())
}
fn third() -> Self {
Self::from_primitive(T::third())
}
fn half() -> Self {
Self::from_primitive(T::half())
}
fn pi() -> Self {
Self::from_primitive(T::pi())
}
fn two_pi() -> Self {
Self::from_primitive(T::two_pi())
}
fn ln2() -> Self {
Self::from_primitive(T::ln2())
}
fn c() -> Self {
Self::from_primitive(T::c())
}
fn c_inv() -> Self {
Self::from_primitive(T::c_inv())
}
fn eps0() -> Self {
Self::from_primitive(T::eps0())
}
fn eps0_inv() -> Self {
Self::from_primitive(T::eps0_inv())
}
fn twice_eps0() -> Self {
Self::from_primitive(T::twice_eps0())
}
fn mu0() -> Self {
Self::from_primitive(T::mu0())
}
fn mu0_inv() -> Self {
Self::from_primitive(T::mu0_inv())
}
}
impl ToFloat for i32 {
fn to_f32(&self) -> f32 {
*self as _
}
fn to_f64(&self) -> f64 {
*self as _
}
}
impl ToFloat for u32 {
fn to_f32(&self) -> f32 {
*self as _
}
fn to_f64(&self) -> f64 {
*self as _
}
}
impl ToFloat for u64 {
fn to_f32(&self) -> f32 {
*self as _
}
fn to_f64(&self) -> f64 {
*self as _
}
}