diff --git a/Cargo.toml b/Cargo.toml
index 8625bd99..e1c03e45 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -56,3 +56,9 @@ features = ["macros"]
level = "warn"
# Set by cargo-llvm-cov when running on nightly
check-cfg = ['cfg(coverage_nightly)']
+
+[package.metadata.docs.rs]
+rustdoc-args = [
+ "--html-in-header",
+ "./katex-header.html"
+]
diff --git a/Makefile.toml b/Makefile.toml
new file mode 100644
index 00000000..985672ed
--- /dev/null
+++ b/Makefile.toml
@@ -0,0 +1,4 @@
+[tasks.doc-katex]
+env = { "RUSTDOCFLAGS" = "--html-in-header katex-header.html" }
+command = "cargo"
+args = ["doc", "--no-deps"]
diff --git a/katex-header.html b/katex-header.html
new file mode 100644
index 00000000..578dbeaa
--- /dev/null
+++ b/katex-header.html
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/src/distribution/geometric.rs b/src/distribution/geometric.rs
index 82af5eef..1833abce 100644
--- a/src/distribution/geometric.rs
+++ b/src/distribution/geometric.rs
@@ -93,7 +93,7 @@ impl std::fmt::Display for Geometric {
#[cfg(feature = "rand")]
impl ::rand::distributions::Distribution for Geometric {
fn sample(&self, r: &mut R) -> f64 {
- use ::rand::distributions::OpenClosed01;
+ use rand::distributions::OpenClosed01;
if ulps_eq!(self.p, 1.0) {
1.0
diff --git a/src/distribution/laplace.rs b/src/distribution/laplace.rs
index 168859ea..06bae5de 100644
--- a/src/distribution/laplace.rs
+++ b/src/distribution/laplace.rs
@@ -553,8 +553,8 @@ mod tests {
#[cfg(feature = "rand")]
#[test]
fn test_sample() {
- use ::rand::distributions::Distribution;
- use ::rand::thread_rng;
+ use rand::distributions::Distribution;
+ use rand::thread_rng;
let l = create_ok(0.1, 0.5);
l.sample(&mut thread_rng());
@@ -563,9 +563,9 @@ mod tests {
#[cfg(feature = "rand")]
#[test]
fn test_sample_distribution() {
- use ::rand::distributions::Distribution;
- use ::rand::rngs::StdRng;
- use ::rand::SeedableRng;
+ use rand::distributions::Distribution;
+ use rand::rngs::StdRng;
+ use rand::SeedableRng;
// sanity check sampling
let location = 0.0;
diff --git a/src/distribution/mod.rs b/src/distribution/mod.rs
index e8c9574c..d927adbd 100644
--- a/src/distribution/mod.rs
+++ b/src/distribution/mod.rs
@@ -1,9 +1,8 @@
//! Defines common interfaces for interacting with statistical distributions
//! and provides
//! concrete implementations for a variety of distributions.
-use super::statistics::{Max, Min};
-use ::num_traits::{Float, Num};
-use num_traits::NumAssignOps;
+use crate::statistics::{Max, Min};
+use num_traits::{Float, Num, NumAssignOps};
pub use self::bernoulli::Bernoulli;
pub use self::beta::{Beta, BetaError};
@@ -41,6 +40,8 @@ pub use self::triangular::{Triangular, TriangularError};
pub use self::uniform::{Uniform, UniformError};
pub use self::weibull::{Weibull, WeibullError};
+pub use self::noncentral_students_t::{NoncentralStudentsT, NoncentralStudentsTError};
+
mod bernoulli;
mod beta;
mod binomial;
@@ -83,6 +84,8 @@ mod ziggurat;
#[cfg(feature = "rand")]
mod ziggurat_tables;
+mod noncentral_students_t;
+
/// The `ContinuousCDF` trait is used to specify an interface for univariate
/// distributions for which cdf float arguments are sensible.
pub trait ContinuousCDF: Min + Max {
diff --git a/src/distribution/noncentral_students_t.rs b/src/distribution/noncentral_students_t.rs
new file mode 100644
index 00000000..a9c1df97
--- /dev/null
+++ b/src/distribution/noncentral_students_t.rs
@@ -0,0 +1,190 @@
+use crate::distribution::{Continuous, ContinuousCDF};
+use crate::function::{beta, gamma};
+use crate::statistics::{Max, Min};
+
+/// the non-central students T distribution
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub struct NoncentralStudentsT {
+ location: f64,
+ scale: f64,
+ freedom: f64,
+}
+
+/// Represents the errors that can occur when creating a [`NoncentralStudentsT`].
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+#[non_exhaustive]
+pub enum NoncentralStudentsTError {
+ /// The location is NaN.
+ LocationInvalid,
+
+ /// The scale is NaN, zero or less than zero.
+ ScaleInvalid,
+
+ /// The degrees of freedom are NaN, zero or less than zero.
+ FreedomInvalid,
+}
+
+impl std::fmt::Display for NoncentralStudentsTError {
+ #[cfg_attr(coverage_nightly, coverage(off))]
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ match self {
+ NoncentralStudentsTError::LocationInvalid => write!(f, "Location is NaN"),
+ NoncentralStudentsTError::ScaleInvalid => {
+ write!(f, "Scale is NaN, zero or less than zero")
+ }
+ NoncentralStudentsTError::FreedomInvalid => {
+ write!(f, "Degrees of freedom are NaN, zero or less than zero")
+ }
+ }
+ }
+}
+
+impl std::error::Error for NoncentralStudentsTError {}
+
+impl NoncentralStudentsT {
+ /// Constructs a new student's t-distribution with location `location`,
+ /// scale `scale`, and `freedom` freedom.
+ ///
+ /// # Errors
+ ///
+ /// Returns an error if any of `location`, `scale`, or `freedom` are `NaN`.
+ /// Returns an error if `scale <= 0.0` or `freedom <= 0.0`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use statrs::distribution::NoncentralStudentsT;
+ ///
+ /// let mut result = NoncentralStudentsT::new(0.0, 1.0, 2.0);
+ /// assert!(result.is_ok());
+ ///
+ /// result = NoncentralStudentsT::new(0.0, 0.0, 0.0);
+ /// assert!(result.is_err());
+ /// ```
+ pub fn new(
+ location: f64,
+ scale: f64,
+ freedom: f64,
+ ) -> Result {
+ if location.is_nan() {
+ return Err(NoncentralStudentsTError::LocationInvalid);
+ }
+
+ if scale.is_nan() || scale <= 0.0 {
+ return Err(NoncentralStudentsTError::ScaleInvalid);
+ }
+
+ if freedom.is_nan() || freedom <= 0.0 {
+ return Err(NoncentralStudentsTError::FreedomInvalid);
+ }
+
+ Ok(NoncentralStudentsT {
+ location,
+ scale,
+ freedom,
+ })
+ }
+
+ /// Returns the location of the student's t-distribution
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use statrs::distribution::NoncentralStudentsT;
+ ///
+ /// let n = NoncentralStudentsT::new(0.0, 1.0, 2.0).unwrap();
+ /// assert_eq!(n.location(), 0.0);
+ /// ```
+ pub fn location(&self) -> f64 {
+ self.location
+ }
+
+ /// Returns the scale of the student's t-distribution
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use statrs::distribution::NoncentralStudentsT;
+ ///
+ /// let n = NoncentralStudentsT::new(0.0, 1.0, 2.0).unwrap();
+ /// assert_eq!(n.scale(), 1.0);
+ /// ```
+ pub fn scale(&self) -> f64 {
+ self.scale
+ }
+
+ /// Returns the freedom of the student's t-distribution
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use statrs::distribution::NoncentralStudentsT;
+ ///
+ /// let n = NoncentralStudentsT::new(0.0, 1.0, 2.0).unwrap();
+ /// assert_eq!(n.freedom(), 2.0);
+ /// ```
+ pub fn freedom(&self) -> f64 {
+ self.freedom
+ }
+}
+
+impl std::fmt::Display for NoncentralStudentsT {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "t_{}({},{})", self.freedom, self.location, self.scale)
+ }
+}
+
+impl ContinuousCDF for NoncentralStudentsT {
+ /// Calculates the cumulative distribution function for the noncentral students t distribution at `t`
+ ///
+ /// # Definition
+ /// \\(F(t;\nu,\delta) = \textrm{Prob}(t_\nu(\delta) < t)\\)
+ /// \\[
+ /// \Phi(-\delta) + \frac{1}{2}\sum\_{i=0}^\infty{[P\_i I\_x(i+1/2,n/2) + Q\_i I\_x(i+1/2,n/2)]}\textrm{ where}\\\\\[1.5em\]
+ /// x = \frac{t^2}{\nu + t^2}, \quad
+ /// P_i = e^{-\delta^2/2}\\,\frac{(\delta^2/2)^i}{i!}, \quad
+ /// Q_i = e^{-\delta^2/2}\\,\frac{(\delta^2/2)^i}{\Gamma(i + 3/2)}
+ /// \\]
+ /// where \\(I_x\\) denotes the incomplete regularized beta function, same as \\(I_x(a,b)\\) [here](https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function), unregularized is implemented as [`beta_inc`](crate::function::beta::beta_inc)
+ fn cdf(&self, t: f64) -> f64 {
+ unimplemented!()
+ }
+}
+
+impl Continuous for NoncentralStudentsT {
+ fn pdf(&self, x: f64) -> f64 {
+ unimplemented!()
+ }
+
+ fn ln_pdf(&self, x: f64) -> f64 {
+ self.pdf(x).ln()
+ }
+}
+
+impl Min for NoncentralStudentsT {
+ /// Returns the minimum value in the domain of the student's t-distribution
+ /// representable by a double precision float
+ ///
+ /// # Formula
+ ///
+ /// ```text
+ /// f64::NEG_INFINITY
+ /// ```
+ fn min(&self) -> f64 {
+ f64::NEG_INFINITY
+ }
+}
+
+impl Max for NoncentralStudentsT {
+ /// Returns the maximum value in the domain of the student's t-distribution
+ /// representable by a double precision float
+ ///
+ /// # Formula
+ ///
+ /// ```text
+ /// f64::INFINITY
+ /// ```
+ fn max(&self) -> f64 {
+ f64::INFINITY
+ }
+}
diff --git a/src/function/beta.rs b/src/function/beta.rs
index 794a8504..0a4f4b33 100644
--- a/src/function/beta.rs
+++ b/src/function/beta.rs
@@ -43,6 +43,11 @@ pub fn checked_ln_beta(a: f64, b: f64) -> Result {
/// where `a` is the first beta parameter
/// and `b` is the second beta parameter.
///
+/// # Definition
+/// $$
+/// B(a,b) = \int\_0^1{t^{a-1}(1-t)^{b-1}}\textrm{d}t
+/// $$
+///
///
/// # Panics
///
@@ -64,10 +69,14 @@ pub fn checked_beta(a: f64, b: f64) -> Result {
}
/// Computes the lower incomplete (unregularized) beta function
-/// `B(a,b,x) = int(t^(a-1)*(1-t)^(b-1),t=0..x)` for `a > 0, b > 0, 1 >= x >= 0`
/// where `a` is the first beta parameter, `b` is the second beta parameter, and
/// `x` is the upper limit of the integral
///
+/// # Definition
+/// ```math
+/// B(x;a,b) = B\_x(a,b) = \int\_0^x t^{a-1}*(1-t)^{b-1} \textrm{d}t
+/// x \in [0,1] \textrm{ and } a,b > 0
+/// ```
/// # Panics
///
/// If `a <= 0.0`, `b <= 0.0`, `x < 0.0`, or `x > 1.0`
diff --git a/src/statistics/traits.rs b/src/statistics/traits.rs
index 9140eab4..e4b5c38b 100644
--- a/src/statistics/traits.rs
+++ b/src/statistics/traits.rs
@@ -1,4 +1,4 @@
-use ::num_traits::float::Float;
+use num_traits::float::Float;
/// The `Min` trait specifies than an object has a minimum value
pub trait Min {