import more fidget

This commit is contained in:
2025-06-26 19:36:49 +01:00
parent 4a60649429
commit 8a371de244
8 changed files with 1083 additions and 187 deletions
+365
View File
@@ -0,0 +1,365 @@
use glam::Vec4;
/// A point in space with associated partial derivatives.
#[derive(Copy, Clone, Debug, Default, PartialEq)]
#[repr(C)]
pub struct Grad {
/// Value of the distance field at this point
pub v: f32,
/// Partial derivative with respect to `x`
pub dx: f32,
/// Partial derivative with respect to `y`
pub dy: f32,
/// Partial derivative with respect to `z`
pub dz: f32,
}
impl std::fmt::Display for Grad {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({}, {}, {}, {})", self.v, self.dx, self.dy, self.dz)
}
}
impl Grad {
/// Constructs a new gradient
#[inline]
pub fn new(v: f32, dx: f32, dy: f32, dz: f32) -> Self {
Self { v, dx, dy, dz }
}
/// Looks up a gradient by index (0 = x, 1 = y, 2 = z)
///
/// # Panics
/// If the index is not in the 0-2 range
#[inline]
pub fn d(&self, i: usize) -> f32 {
match i {
0 => self.dx,
1 => self.dy,
2 => self.dz,
_ => panic!("invalid index {i}"),
}
}
/// Absolute value
#[inline]
pub fn abs(self) -> Self {
if self.v < 0.0 {
Grad {
v: -self.v,
dx: -self.dx,
dy: -self.dy,
dz: -self.dz,
}
} else {
self
}
}
/// Square root
#[inline]
pub fn sqrt(self) -> Self {
let v = self.v.sqrt();
Grad {
v,
dx: self.dx / (2.0 * v),
dy: self.dy / (2.0 * v),
dz: self.dz / (2.0 * v),
}
}
/// Sine
#[inline]
pub fn sin(self) -> Self {
let c = self.v.cos();
Grad {
v: self.v.sin(),
dx: self.dx * c,
dy: self.dy * c,
dz: self.dz * c,
}
}
/// Cosine
#[inline]
pub fn cos(self) -> Self {
let s = -self.v.sin();
Grad {
v: self.v.cos(),
dx: self.dx * s,
dy: self.dy * s,
dz: self.dz * s,
}
}
/// Tangent
#[inline]
pub fn tan(self) -> Self {
let c = self.v.cos().powi(2);
Grad {
v: self.v.tan(),
dx: self.dx / c,
dy: self.dy / c,
dz: self.dz / c,
}
}
/// Arcsin
#[inline]
pub fn asin(self) -> Self {
let r = (1.0 - self.v.powi(2)).sqrt();
Grad {
v: self.v.asin(),
dx: self.dx / r,
dy: self.dy / r,
dz: self.dz / r,
}
}
/// Arccos
#[inline]
pub fn acos(self) -> Self {
let r = (1.0 - self.v.powi(2)).sqrt();
Grad {
v: self.v.acos(),
dx: -self.dx / r,
dy: -self.dy / r,
dz: -self.dz / r,
}
}
/// Arctangent
#[inline]
pub fn atan(self) -> Self {
let r = self.v.powi(2) + 1.0;
Grad {
v: self.v.atan(),
dx: self.dx / r,
dy: self.dy / r,
dz: self.dz / r,
}
}
/// Exponential function
#[inline]
pub fn exp(self) -> Self {
let v = self.v.exp();
Grad {
v,
dx: v * self.dx,
dy: v * self.dy,
dz: v * self.dz,
}
}
/// Natural log
#[inline]
pub fn ln(self) -> Self {
Grad {
v: self.v.ln(),
dx: self.dx / self.v,
dy: self.dy / self.v,
dz: self.dz / self.v,
}
}
/// Reciprocal
#[inline]
pub fn recip(self) -> Self {
let v2 = -self.v.powi(2);
Grad {
v: 1.0 / self.v,
dx: self.dx / v2,
dy: self.dy / v2,
dz: self.dz / v2,
}
}
/// Minimum of two values
#[inline]
pub fn min(self, rhs: Self) -> Self {
if self.v < rhs.v { self } else { rhs }
}
/// Maximum of two values
#[inline]
pub fn max(self, rhs: Self) -> Self {
if self.v > rhs.v { self } else { rhs }
}
/// Least non-negative remainder
#[inline]
pub fn rem_euclid(&self, rhs: Grad) -> Self {
let e = self.v.div_euclid(rhs.v);
Grad {
v: self.v.rem_euclid(rhs.v),
dx: self.dx - rhs.dx * e,
dy: self.dy - rhs.dy * e,
dz: self.dz - rhs.dz * e,
}
}
/// Snap to the largest less-than-or-equal value
#[inline]
pub fn floor(&self) -> Self {
Grad {
v: self.v.floor(),
dx: 0.0,
dy: 0.0,
dz: 0.0,
}
}
/// Snap to the smallest greater-than-or-equal value
#[inline]
pub fn ceil(&self) -> Self {
Grad {
v: self.v.ceil(),
dx: 0.0,
dy: 0.0,
dz: 0.0,
}
}
/// Rounds to the nearest integer
#[inline]
pub fn round(&self) -> Self {
Grad {
v: self.v.round(),
dx: 0.0,
dy: 0.0,
dz: 0.0,
}
}
/// Four-quadrant arctangent
#[inline]
pub fn atan2(self, x: Self) -> Self {
let y = self;
let d = x.v.powi(2) + y.v.powi(2);
Grad {
v: y.v.atan2(x.v),
dx: (x.v * y.dx - y.v * x.dx) / d,
dy: (x.v * y.dy - y.v * x.dy) / d,
dz: (x.v * y.dz - y.v * x.dz) / d,
}
}
/// Checks that the two values are roughly equal, panicking otherwise
#[cfg(test)]
pub(crate) fn compare_eq(&self, other: Self) {
let d = (self.v - other.v)
.abs()
.max((self.dx - other.dx).abs())
.max((self.dy - other.dy).abs())
.max((self.dz - other.dz).abs());
if d >= 1e-6 {
panic!("lhs != rhs ({self:?} != {other:?})");
}
}
}
impl From<f32> for Grad {
#[inline]
fn from(v: f32) -> Self {
Grad {
v,
dx: 0.0,
dy: 0.0,
dz: 0.0,
}
}
}
impl From<Grad> for Vec4 {
#[inline]
fn from(g: Grad) -> Self {
Vec4::new(g.dx, g.dy, g.dz, g.v)
}
}
impl std::ops::Add<Grad> for Grad {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Grad {
v: self.v + rhs.v,
dx: self.dx + rhs.dx,
dy: self.dy + rhs.dy,
dz: self.dz + rhs.dz,
}
}
}
impl std::ops::Mul<Grad> for Grad {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
Self {
v: self.v * rhs.v,
dx: self.v * rhs.dx + rhs.v * self.dx,
dy: self.v * rhs.dy + rhs.v * self.dy,
dz: self.v * rhs.dz + rhs.v * self.dz,
}
}
}
impl std::ops::Mul<f32> for Grad {
type Output = Self;
#[inline]
fn mul(self, rhs: f32) -> Self {
Self {
v: self.v * rhs,
dx: self.dx * rhs,
dy: self.dy * rhs,
dz: self.dz * rhs,
}
}
}
impl std::ops::Div<Grad> for Grad {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
let d = rhs.v.powi(2);
Self {
v: self.v / rhs.v,
dx: (rhs.v * self.dx - self.v * rhs.dx) / d,
dy: (rhs.v * self.dy - self.v * rhs.dy) / d,
dz: (rhs.v * self.dz - self.v * rhs.dz) / d,
}
}
}
impl std::ops::Sub<Grad> for Grad {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Self {
v: self.v - rhs.v,
dx: self.dx - rhs.dx,
dy: self.dy - rhs.dy,
dz: self.dz - rhs.dz,
}
}
}
impl std::ops::Neg for Grad {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self {
v: -self.v,
dx: -self.dx,
dy: -self.dy,
dz: -self.dz,
}
}
}
+720
View File
@@ -0,0 +1,720 @@
use std::simd::{
StdFloat,
cmp::{SimdPartialEq, SimdPartialOrd},
num::SimdFloat,
u32x8,
};
use crate::{
interpreter::{
Mask, VALUE_0, VALUE_1, VALUE_2, VALUE_05, VALUE_M1, VALUE_NAN, VALUE_PI, Value, glfract,
glsign,
},
vm::choice::{Choice, VChoice},
};
/// Stores a range, with conservative calculations to guarantee that it always
/// contains the actual value.
///
/// # Warning
/// This implementation does not set rounding modes, so it may not be _perfect_.
#[derive(Copy, Clone, PartialEq)]
#[repr(C)]
pub struct Interval {
lower: Value,
upper: Value,
}
impl std::fmt::Debug for Interval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.debug_tuple("")
.field(&self.lower)
.field(&self.upper)
.finish()
}
}
impl Interval {
pub const HALF: Self = Self::const_splat(0.5);
pub const MONE: Self = Self::const_splat(-1.0);
pub const NAN: Self = Self::const_splat(core::f32::NAN);
pub const ONE: Self = Self::const_splat(1.0);
pub const PI: Self = Self::const_splat(core::f32::consts::PI);
pub const ZERO: Self = Self::const_splat(0.0);
/// Builds a new interval
///
/// There are two kinds of valid interval:
/// - `[lower, upper]` where `lower <= upper`
/// - `[NaN, NaN]`
///
/// # Panics
/// Panics if the resulting interval would be invalid
#[inline]
pub fn new(lower: Value, upper: Value) -> Self {
assert!(
(upper.simd_ge(lower) | (lower.is_nan() & upper.is_nan())).all(),
"invalid interval [{lower:?}, {upper:?}]"
);
Self { lower, upper }
}
pub fn splat(value: f32) -> Interval {
Interval::new(Value::splat(value), Value::splat(value))
}
pub fn splat2(lower: f32, upper: f32) -> Interval {
Interval::new(Value::splat(lower), Value::splat(upper))
}
const fn const_splat(value: f32) -> Interval {
Interval {
lower: Value::splat(value),
upper: Value::splat(value),
}
}
/// Returns the lower bound of the interval
#[inline]
pub fn lower(&self) -> Value {
self.lower
}
/// Returns the upper bound of the interval
#[inline]
pub fn upper(&self) -> Value {
self.upper
}
/// Checks whether the given value is (strictly) contained in the interval
#[inline]
pub fn contains(&self, v: Value) -> Mask {
v.simd_ge(self.lower) & v.simd_le(self.upper)
}
/// Returns `true` if either bound of the interval is `NaN`
#[inline]
pub fn has_nan(&self) -> Mask {
self.lower.is_nan() | self.upper.is_nan()
}
/// Calculates the absolute value of the interval
#[inline]
pub fn abs(self) -> Self {
let llt = self.lower.simd_lt(VALUE_0);
let ugt = self.upper.simd_gt(VALUE_0);
let lower = llt.select(ugt.select(VALUE_0, -self.upper), self.lower);
let upper = llt.select(
ugt.select(self.upper.simd_max(-self.lower), -self.lower),
self.upper,
);
Interval::new(lower, upper)
}
/// Squares the interval
///
/// Note that this has tighter bounds than multiplication, because we know
/// that both sides of the multiplication are the same value.
#[inline]
pub fn square(self) -> Self {
let ult = self.upper.simd_lt(VALUE_0);
let lgt = self.lower.simd_gt(VALUE_0);
let has_nan = self.has_nan();
let lower = ult.select(
self.upper * self.upper,
lgt.select(self.lower * self.lower, has_nan.select(VALUE_NAN, VALUE_0)),
);
let upper = ult.select(
self.lower * self.lower,
lgt.select(
self.upper * self.upper,
has_nan.select(VALUE_NAN, {
let k = self.lower.abs().simd_max(self.upper.abs());
k * k
}),
),
);
Interval::new(lower, upper)
}
/// Cubes the interval
///
/// Note that this has tighter bounds than multiplication, because we know
/// that both sides of the multiplication are the same value.
#[inline]
pub fn cube(self) -> Self {
let has_nan = self.has_nan();
let lower = has_nan.select(VALUE_NAN, self.lower * self.lower * self.lower);
let upper = has_nan.select(VALUE_NAN, self.upper * self.upper * self.upper);
Interval::new(lower, upper)
}
/// Computes the sine of the interval
#[inline]
pub fn sin(self) -> Self {
let same_cycle = ((self.lower / VALUE_PI) + VALUE_05)
.floor()
.simd_eq(((self.upper / VALUE_PI) + VALUE_05).floor());
let up = (((self.upper / VALUE_PI) + VALUE_05).floor()) % (VALUE_2);
let whole_cycle = (((self.upper / VALUE_PI) + VALUE_05).floor()
- ((self.lower / VALUE_PI) + VALUE_05).floor())
.simd_gt(VALUE_1);
let temp0 = self.lower.sin();
let temp1 = self.upper.sin();
let lower = self.has_nan().select(
VALUE_NAN,
((!whole_cycle & (up.simd_eq(VALUE_1))) | same_cycle)
.select(temp0.simd_min(temp1), VALUE_M1),
);
let upper = self.has_nan().select(
VALUE_NAN,
((!whole_cycle & (up.simd_eq(VALUE_0))) | same_cycle)
.select(temp0.simd_max(temp1), VALUE_1),
);
Interval::new(lower, upper)
}
/// Computes the cosine of the interval
#[inline]
pub fn cos(self) -> Self {
let same_cycle = (self.lower / VALUE_PI)
.floor()
.simd_eq((self.upper / VALUE_PI).floor());
let up = ((self.upper / VALUE_PI).floor()) % (VALUE_2);
let whole_cycle =
((self.upper / VALUE_PI).floor() - (self.lower / VALUE_PI).floor()).simd_gt(VALUE_1);
let temp0 = self.lower.cos();
let temp1 = self.upper.cos();
let lower = self.has_nan().select(
VALUE_NAN,
((!whole_cycle & (up.simd_eq(VALUE_0))) | same_cycle)
.select(temp0.simd_min(temp1), VALUE_M1),
);
let upper = self.has_nan().select(
VALUE_NAN,
((!whole_cycle & (up.simd_eq(VALUE_1))) | same_cycle)
.select(temp0.simd_max(temp1), VALUE_1),
);
Interval::new(lower, upper)
}
/// Computes the tangent of the interval
///
/// Returns the `NAN` interval if the result contains a undefined point
#[inline]
pub fn tan(self) -> Self {
let size = self.upper - self.lower;
let lower_tmp = Value::from_array(self.lower.to_array().map(|f| f.tan()));
let upper_tmp = Value::from_array(self.upper.to_array().map(|f| f.tan()));
let lower =
(size.simd_lt(VALUE_PI) & upper_tmp.simd_ge(lower_tmp)).select(lower_tmp, VALUE_NAN);
let upper =
(size.simd_lt(VALUE_PI) & upper_tmp.simd_ge(lower_tmp)).select(upper_tmp, VALUE_NAN);
Interval::new(lower, upper)
}
/// Computes the arcsine of the interval
///
/// Returns the `NAN` interval if the input is invalid
#[inline]
pub fn asin(self) -> Self {
let lower = (self.lower.simd_lt(VALUE_M1) | self.upper.simd_gt(VALUE_1)).select(
VALUE_NAN,
Value::from_array(self.lower.to_array().map(|f| f.asin())),
);
let upper = (self.lower.simd_lt(VALUE_M1) | self.upper.simd_gt(VALUE_1)).select(
VALUE_NAN,
Value::from_array(self.upper.to_array().map(|f| f.asin())),
);
Interval::new(lower, upper)
}
/// Computes the arccosine of the interval
///
/// Returns the `NAN` interval if the input is invalid
#[inline]
pub fn acos(self) -> Self {
let lower = (self.lower.simd_lt(VALUE_M1) | self.upper.simd_gt(VALUE_1)).select(
VALUE_NAN,
Value::from_array(self.upper.to_array().map(|f| f.asin())),
);
let upper = (self.lower.simd_lt(VALUE_M1) | self.upper.simd_gt(VALUE_1)).select(
VALUE_NAN,
Value::from_array(self.lower.to_array().map(|f| f.asin())),
);
Interval::new(lower, upper)
}
/// Computes the arctangent of the interval
#[inline]
pub fn atan(self) -> Self {
let lower = Value::from_array(self.lower.to_array().map(|f| f.asin()));
let upper = Value::from_array(self.upper.to_array().map(|f| f.asin()));
Interval::new(lower, upper)
}
/// Computes the exponent function applied to the interval
#[inline]
pub fn exp(self) -> Self {
Interval::new(self.lower.exp(), self.upper.exp())
}
/// Computes the natural log of the input interval
///
/// Returns the `NAN` interval if the input contains zero
#[inline]
pub fn ln(self) -> Self {
let lower = (self.has_nan()).select(VALUE_NAN, self.lower.ln());
let upper = (self.has_nan()).select(VALUE_NAN, self.upper.ln());
Interval::new(lower, upper)
}
/// Calculates the square root of the interval
///
/// If the interval contains values below 0, returns a `NAN` interval.
#[inline]
pub fn sqrt(self) -> Self {
let lower = (self.lower.simd_lt(VALUE_0)).select(VALUE_NAN, self.lower.sqrt());
let upper = (self.lower.simd_lt(VALUE_0)).select(VALUE_NAN, self.upper.sqrt());
Interval::new(lower, upper)
}
/// Calculates the reciprocal of the interval
///
/// If the interval includes 0, returns the `NAN` interval
#[inline]
pub fn recip(self) -> Self {
let lower = (self.lower.simd_le(VALUE_0) & self.upper.simd_ge(VALUE_0))
.select(VALUE_NAN, self.upper.recip());
let upper = (self.lower.simd_le(VALUE_0) & self.upper.simd_ge(VALUE_0))
.select(VALUE_NAN, self.lower.recip());
Interval::new(lower, upper)
}
/// Calculates the minimum of two intervals
///
/// Returns both the result and a [`VChoice`] indicating whether one side is
/// always less than the other.
///
/// If either side is `NAN`, returns the `NAN` interval and `VChoice::Both`.
#[inline]
pub fn min_choice(self, rhs: Self) -> (Self, VChoice) {
let has_nan = self.has_nan() | rhs.has_nan();
let choice = has_nan.select(
VChoice::BOTH.0,
self.upper.simd_lt(rhs.lower).select(
VChoice::LEFT.0,
rhs.upper
.simd_lt(self.lower)
.select(VChoice::RIGHT.0, VChoice::BOTH.0),
),
);
(
Interval::new(
has_nan.select(VALUE_NAN, self.lower.simd_min(rhs.lower)),
has_nan.select(VALUE_NAN, self.upper.simd_min(rhs.upper)),
),
VChoice(choice),
)
}
/// Calculates the maximum of two intervals
///
/// Returns both the result and a [`VChoice`] indicating whether one side is
/// always greater than the other.
///
/// If either side is `NAN`, returns the `NAN` interval and `VChoice::Both`.
#[inline]
pub fn max_choice(self, rhs: Self) -> (Self, VChoice) {
let has_nan = self.has_nan() | rhs.has_nan();
let choice = has_nan.select(
VChoice::BOTH.0,
self.lower.simd_gt(rhs.upper).select(
VChoice::LEFT.0,
rhs.lower
.simd_gt(self.upper)
.select(VChoice::RIGHT.0, VChoice::BOTH.0),
),
);
(
Interval::new(
has_nan.select(VALUE_NAN, self.lower.simd_min(rhs.lower)),
has_nan.select(VALUE_NAN, self.upper.simd_min(rhs.upper)),
),
VChoice(choice),
)
}
/// Calculates the short-circuiting `AND` of two intervals
///
/// Returns both the result and a [`VChoice`] indicating whether one side is
/// always selected. An unambiguous 0 in `self` selects itself; an
/// unambiguous 1 selects the opposite branch.
#[inline]
pub fn and_choice(self, rhs: Self) -> (Self, VChoice) {
let has_nan = self.has_nan() | rhs.has_nan();
let choice = has_nan.select(
VChoice::BOTH.0,
(self.lower.simd_eq(VALUE_0) & self.upper.simd_eq(VALUE_0)).select(
VChoice::LEFT.0,
self.contains(VALUE_0)
.select(VChoice::BOTH.0, VChoice::RIGHT.0),
),
);
(
Interval::new(
has_nan.select(
VALUE_NAN,
(self.lower.simd_eq(VALUE_0) & self.upper.simd_eq(VALUE_0)).select(
VALUE_0,
self.contains(VALUE_0)
.select(rhs.lower.simd_min(VALUE_0), rhs.lower),
),
),
has_nan.select(
VALUE_NAN,
(self.lower.simd_eq(VALUE_0) & self.upper.simd_eq(VALUE_0)).select(
VALUE_0,
self.contains(VALUE_0)
.select(rhs.upper.simd_max(VALUE_0), rhs.upper),
),
),
),
VChoice(choice),
)
}
/// Calculates the short-circuiting `OR` of two intervals
///
/// Returns both the result and a [`VChoice`] indicating whether one side is
/// always selected. An unambiguous 0 in `self` selects the opposite
/// branch; an unambiguous 1 selects itself.
#[inline]
pub fn or_choice(self, rhs: Self) -> (Self, VChoice) {
let has_nan = self.has_nan() | rhs.has_nan();
let choice = has_nan.select(
VChoice::BOTH.0,
self.contains(VALUE_0).select(
(self.lower.simd_eq(VALUE_0) & self.upper.simd_eq(VALUE_0))
.select(VChoice::RIGHT.0, VChoice::BOTH.0),
VChoice::LEFT.0,
),
);
(
Interval::new(
has_nan.select(
VALUE_NAN,
self.contains(VALUE_0).select(
(self.lower.simd_eq(VALUE_0) & self.upper.simd_eq(VALUE_0))
.select(rhs.lower, rhs.lower.simd_min(self.lower)),
self.lower,
),
),
has_nan.select(
VALUE_NAN,
self.contains(VALUE_0).select(
(self.lower.simd_eq(VALUE_0) & self.upper.simd_eq(VALUE_0))
.select(rhs.upper, rhs.upper.simd_max(self.upper)),
self.upper,
),
),
),
VChoice(choice),
)
}
/// Returns the midpoint of the interval
#[inline]
pub fn midpoint(self) -> Value {
(self.lower + self.upper) / VALUE_2
}
/// Splits the interval at the midpoint
///
/// ```
/// # use fidget::types::Interval;
/// let a = Interval::new(0.0, 1.0);
/// let (lo, hi) = a.split();
/// assert_eq!(lo, Interval::new(0.0, 0.5));
/// assert_eq!(hi, Interval::new(0.5, 1.0));
/// ```
#[inline]
pub fn split(self) -> (Self, Self) {
let mid = self.midpoint();
(
Interval::new(self.lower, mid),
Interval::new(mid, self.upper),
)
}
/// Linear interpolation from `lower` to `upper`
///
/// ```
/// # use fidget::types::Interval;
/// let a = Interval::new(0.0, 2.0);
/// assert_eq!(a.lerp(0.5), 1.0);
/// assert_eq!(a.lerp(0.75), 1.5);
/// assert_eq!(a.lerp(2.0), 4.0);
/// ```
#[inline]
pub fn lerp(self, frac: Value) -> Value {
self.lower * (VALUE_1 - frac) + self.upper * frac
}
/// Calculates the width of the interval
///
/// ```
/// # use fidget::types::Interval;
/// let a = Interval::new(2.0, 3.0);
/// assert_eq!(a.width(), 1.0);
/// let b = Interval::new(2.0, 5.0);
/// assert_eq!(b.width(), 3.0);
/// ```
#[inline]
pub fn width(self) -> Value {
self.upper - self.lower
}
/// Checks that the two values are roughly equal, panicking otherwise
#[cfg(test)]
pub(crate) fn compare_eq(&self, other: Self) {
let d = (self.lower - other.lower)
.abs()
.simd_max((self.upper - other.upper).abs());
if d.simd_ge(Value::splat(1e-6)).any() {
panic!("lhs != rhs ({self:?} != {other:?})");
}
}
/// Largest value that is less-than-or-equal to this value
#[inline]
pub fn floor(&self) -> Self {
Interval::new(self.lower.floor(), self.upper.floor())
}
/// Smallest value that is greater-than-or-equal to this value
#[inline]
pub fn ceil(&self) -> Self {
Interval::new(self.lower.ceil(), self.upper.ceil())
}
/// Rounded value
#[inline]
pub fn round(&self) -> Self {
Interval::new(self.lower.round(), self.upper.round())
}
/// Four-quadrant arctangent
#[inline]
pub fn atan2(self, x: Self) -> Self {
let has_nan = self.has_nan() | x.has_nan();
// TODO optimize this further
Interval::new(
has_nan.select(VALUE_NAN, -VALUE_PI),
has_nan.select(VALUE_NAN, VALUE_PI),
)
}
#[inline]
pub fn sign(self) -> Interval {
Interval::new(glsign(self.lower), glsign(self.upper))
}
#[inline]
pub fn fract(self) -> Interval {
let ge1 = (self.upper - self.lower).simd_ge(VALUE_1);
Interval::new(
ge1.select(VALUE_0, glfract(self.lower)),
ge1.select(VALUE_1, glfract(self.upper)),
)
}
pub fn compare(self, other: Interval) -> Interval {
let has_nan = self.has_nan() | other.has_nan();
let check1 = self.upper.simd_lt(other.lower);
let check2 = other.upper.simd_lt(self.lower);
let check3 = other.upper.simd_eq(self.lower);
let check4 = self.upper.simd_eq(other.lower);
let lower = has_nan.select(
VALUE_NAN,
check1.select(
VALUE_M1,
check2.select(
VALUE_1,
check3.select(VALUE_0, check4.select(VALUE_M1, VALUE_M1)),
),
),
);
let upper = has_nan.select(
VALUE_NAN,
check1.select(
VALUE_M1,
check2.select(
VALUE_1,
check3.select(VALUE_1, check4.select(VALUE_0, VALUE_1)),
),
),
);
Interval::new(lower, upper)
}
pub fn clamp(self, min: Interval, max: Interval) -> Interval {
Interval::new(
self.lower.simd_clamp(min.lower, max.lower),
self.upper.simd_clamp(min.upper, max.upper),
)
}
}
impl std::fmt::Display for Interval {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({:?}, {:?})", self.lower, self.upper)
}
}
impl From<[Value; 2]> for Interval {
#[inline]
fn from(i: [Value; 2]) -> Interval {
Interval::new(i[0], i[1])
}
}
impl From<Value> for Interval {
#[inline]
fn from(f: Value) -> Self {
Interval::new(f, f)
}
}
impl std::ops::Not for Interval {
type Output = Self;
fn not(self) -> Self::Output {
let has_nan = self.has_nan();
let is_zero = self.lower.simd_eq(VALUE_0) & self.upper.simd_eq(VALUE_0);
let crosses_zero = self.lower.simd_le(VALUE_0) & self.upper.simd_ge(VALUE_0);
let lower = has_nan.select(VALUE_NAN, is_zero.select(VALUE_1, VALUE_0));
let upper = has_nan.select(VALUE_NAN, crosses_zero.select(VALUE_1, VALUE_0));
Interval::new(lower, upper)
}
}
impl std::ops::Rem<Interval> for Interval {
type Output = Self;
#[inline]
fn rem(self, rhs: Interval) -> Self::Output {
// TODO optimize this more?
let has_nan = self.has_nan() | rhs.has_nan() | rhs.contains(VALUE_0);
let other_constant = rhs.lower.simd_eq(rhs.upper) & rhs.lower.simd_gt(VALUE_0);
let a = self.lower / rhs.lower;
let b = self.upper / rhs.lower;
let floors = a.simd_ne(a.floor()) & a.floor().simd_eq(b.floor());
let lower = has_nan.select(
VALUE_NAN,
(other_constant & floors).select(self.lower % rhs.lower, VALUE_0),
);
let upper = has_nan.select(
VALUE_NAN,
(other_constant & floors).select(self.upper % rhs.lower, rhs.upper.abs()),
);
Interval::new(lower, upper)
}
}
impl std::ops::Add<Interval> for Interval {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
Interval::new(self.lower + rhs.lower, self.upper + rhs.upper)
}
}
impl std::ops::Mul<Interval> for Interval {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
let has_nan = self.has_nan() | rhs.has_nan();
let mut out = [VALUE_0; 4];
let mut k = 0;
for i in [self.lower, self.upper] {
for j in [rhs.lower, rhs.upper] {
out[k] = i * j;
k += 1;
}
}
let mut lower = out[0];
let mut upper = out[0];
for &v in &out[1..] {
lower = lower.simd_min(v);
upper = upper.simd_max(v);
}
Interval::new(
has_nan.select(VALUE_NAN, lower),
has_nan.select(VALUE_NAN, upper),
)
}
}
impl std::ops::Mul<Value> for Interval {
type Output = Self;
#[inline]
fn mul(self, rhs: Value) -> Self {
let has_nan = self.has_nan() | rhs.is_nan();
let rlt = rhs.simd_lt(VALUE_0);
Interval::new(
has_nan.select(VALUE_NAN, rlt.select(self.upper * rhs, self.lower * rhs)),
has_nan.select(VALUE_NAN, rlt.select(self.lower * rhs, self.upper * rhs)),
)
}
}
impl std::ops::Div<Interval> for Interval {
type Output = Self;
#[inline]
fn div(self, rhs: Self) -> Self {
let has_nan = self.has_nan() | (rhs.lower.simd_lt(VALUE_0) & rhs.upper.simd_gt(VALUE_0));
let mut out = [VALUE_0; 4];
let mut k = 0;
for i in [self.lower, self.upper] {
for j in [rhs.lower, rhs.upper] {
out[k] = i / j;
k += 1;
}
}
let mut lower = out[0];
let mut upper = out[0];
for &v in &out[1..] {
lower = lower.simd_min(v);
upper = upper.simd_max(v);
}
Interval::new(
has_nan.select(VALUE_NAN, lower),
has_nan.select(VALUE_NAN, upper),
)
}
}
impl std::ops::Sub<Interval> for Interval {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
Interval::new(self.lower - rhs.upper, self.upper - rhs.lower)
}
}
impl std::ops::Neg for Interval {
type Output = Self;
#[inline]
fn neg(self) -> Self {
Interval::new(-self.upper, -self.lower)
}
}
+6
View File
@@ -0,0 +1,6 @@
//! Custom types used during evaluation
mod grad;
mod interval;
pub use grad::Grad;
pub use interval::Interval;