Trevor Gross e754ecb6d9 Add missing functions to the macro list
Now that we are using rustdoc output to locate public functions, the
test is indicating a few that were missed since they don't have their
own function. Update everything to now include the following routines:

* `erfc`
* `erfcf`
* `y0`
* `y0f`
* `y1`
* `y1f`
* `yn`
* `ynf`
2025-01-01 11:15:48 +00:00

190 lines
5.4 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.

//! Traits and operations related to bounds of a function.
use std::fmt;
use std::ops::{self, Bound};
use crate::{Float, FloatExt};
/// Representation of a function's domain.
#[derive(Clone, Debug)]
pub struct Domain<T> {
/// Start of the region for which a function is defined (ignoring poles).
pub start: Bound<T>,
/// Endof the region for which a function is defined (ignoring poles).
pub end: Bound<T>,
/// Additional points to check closer around. These can be e.g. undefined asymptotes or
/// inflection points.
pub check_points: Option<fn() -> BoxIter<T>>,
}
type BoxIter<T> = Box<dyn Iterator<Item = T>>;
impl<F: FloatExt> Domain<F> {
/// The start of this domain, saturating at negative infinity.
pub fn range_start(&self) -> F {
match self.start {
Bound::Included(v) => v,
Bound::Excluded(v) => v.next_up(),
Bound::Unbounded => F::NEG_INFINITY,
}
}
/// The end of this domain, saturating at infinity.
pub fn range_end(&self) -> F {
match self.end {
Bound::Included(v) => v,
Bound::Excluded(v) => v.next_down(),
Bound::Unbounded => F::INFINITY,
}
}
}
impl<F: Float> Domain<F> {
/// x ∈
pub const UNBOUNDED: Self =
Self { start: Bound::Unbounded, end: Bound::Unbounded, check_points: None };
/// x ∈ >= 0
pub const POSITIVE: Self =
Self { start: Bound::Included(F::ZERO), end: Bound::Unbounded, check_points: None };
/// x ∈ > 0
pub const STRICTLY_POSITIVE: Self =
Self { start: Bound::Excluded(F::ZERO), end: Bound::Unbounded, check_points: None };
/// Used for versions of `asin` and `acos`.
pub const INVERSE_TRIG_PERIODIC: Self = Self {
start: Bound::Included(F::NEG_ONE),
end: Bound::Included(F::ONE),
check_points: None,
};
/// Domain for `acosh`
pub const ACOSH: Self =
Self { start: Bound::Included(F::ONE), end: Bound::Unbounded, check_points: None };
/// Domain for `atanh`
pub const ATANH: Self = Self {
start: Bound::Excluded(F::NEG_ONE),
end: Bound::Excluded(F::ONE),
check_points: None,
};
/// Domain for `sin`, `cos`, and `tan`
pub const TRIG: Self = Self {
// TODO
check_points: Some(|| Box::new([-F::PI, -F::FRAC_PI_2, F::FRAC_PI_2, F::PI].into_iter())),
..Self::UNBOUNDED
};
/// Domain for `log` in various bases
pub const LOG: Self = Self::STRICTLY_POSITIVE;
/// Domain for `log1p` i.e. `log(1 + x)`
pub const LOG1P: Self =
Self { start: Bound::Excluded(F::NEG_ONE), end: Bound::Unbounded, check_points: None };
/// Domain for `sqrt`
pub const SQRT: Self = Self::POSITIVE;
/// Domain for `gamma`
pub const GAMMA: Self = Self {
check_points: Some(|| {
// Negative integers are asymptotes
Box::new((0..u8::MAX).map(|scale| {
let mut base = F::ZERO;
for _ in 0..scale {
base = base - F::ONE;
}
base
}))
}),
// Whether or not gamma is defined for negative numbers is implementation dependent
..Self::UNBOUNDED
};
/// Domain for `loggamma`
pub const LGAMMA: Self = Self::STRICTLY_POSITIVE;
}
/// Implement on `op::*` types to indicate how they are bounded.
pub trait HasDomain<T>
where
T: Copy + fmt::Debug + ops::Add<Output = T> + ops::Sub<Output = T> + PartialOrd + 'static,
{
const DOMAIN: Domain<T>;
}
/// Implement [`HasDomain`] for both the `f32` and `f64` variants of a function.
macro_rules! impl_has_domain {
($($fn_name:ident => $domain:expr;)*) => {
paste::paste! {
$(
// Implement for f64 functions
impl HasDomain<f64> for $crate::op::$fn_name::Routine {
const DOMAIN: Domain<f64> = Domain::<f64>::$domain;
}
// Implement for f32 functions
impl HasDomain<f32> for $crate::op::[< $fn_name f >]::Routine {
const DOMAIN: Domain<f32> = Domain::<f32>::$domain;
}
)*
}
};
}
// Tie functions together with their domains.
impl_has_domain! {
acos => INVERSE_TRIG_PERIODIC;
acosh => ACOSH;
asin => INVERSE_TRIG_PERIODIC;
asinh => UNBOUNDED;
atan => UNBOUNDED;
atanh => ATANH;
cbrt => UNBOUNDED;
ceil => UNBOUNDED;
cos => TRIG;
cosh => UNBOUNDED;
erf => UNBOUNDED;
erfc => UNBOUNDED;
exp => UNBOUNDED;
exp10 => UNBOUNDED;
exp2 => UNBOUNDED;
expm1 => UNBOUNDED;
fabs => UNBOUNDED;
floor => UNBOUNDED;
frexp => UNBOUNDED;
ilogb => UNBOUNDED;
j0 => UNBOUNDED;
j1 => UNBOUNDED;
lgamma => LGAMMA;
log => LOG;
log10 => LOG;
log1p => LOG1P;
log2 => LOG;
modf => UNBOUNDED;
rint => UNBOUNDED;
round => UNBOUNDED;
sin => TRIG;
sincos => TRIG;
sinh => UNBOUNDED;
sqrt => SQRT;
tan => TRIG;
tanh => UNBOUNDED;
tgamma => GAMMA;
trunc => UNBOUNDED;
y0 => UNBOUNDED;
y1 => UNBOUNDED;
}
/* Manual implementations, these functions don't follow `foo`->`foof` naming */
impl HasDomain<f32> for crate::op::lgammaf_r::Routine {
const DOMAIN: Domain<f32> = Domain::<f32>::LGAMMA;
}
impl HasDomain<f64> for crate::op::lgamma_r::Routine {
const DOMAIN: Domain<f64> = Domain::<f64>::LGAMMA;
}