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

507 lines
12 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 _
}
}
#[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 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 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;
/// Speed of light in a vacuum; m/s.
/// Also equal to 1/sqrt(epsilon_0 mu_0)
fn c() -> Self;
/// Vaccum Permittivity
fn eps0() -> 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)
}
/// Speed of light in a vacuum; m/s.
/// Also equal to 1/sqrt(epsilon_0 mu_0)
fn c() -> Self {
$wrap(299792458.0)
}
/// Vaccum Permittivity
fn eps0() -> Self {
$wrap(8.854187812813e-12) // F⋅m1
}
fn twice_eps0() -> Self {
$wrap(1.7708375625626e-11) // F⋅m1
}
/// 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))
}
}
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 c() -> Self {
Self::from_primitive(T::c())
}
fn eps0() -> Self {
Self::from_primitive(T::eps0())
}
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 _
}
}