1#[cfg(feature = "serde1")]
2use serde::{Deserialize, Serialize};
3
4use crate::impl_display;
5use crate::misc::ln_gammafn;
6use crate::traits::*;
7use rand::Rng;
8use std::f64::consts::PI;
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq)]
14#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
15#[cfg_attr(feature = "serde1", serde(rename_all = "snake_case"))]
16pub struct StudentsT {
17 v: f64,
19}
20
21impl Parameterized for StudentsT {
22 type Parameters = f64;
23
24 fn emit_params(&self) -> Self::Parameters {
25 self.v()
26 }
27
28 fn from_params(v: Self::Parameters) -> Self {
29 Self::new_unchecked(v)
30 }
31}
32
33#[derive(Debug, Clone, PartialEq)]
34#[cfg_attr(feature = "serde1", derive(Serialize, Deserialize))]
35#[cfg_attr(feature = "serde1", serde(rename_all = "snake_case"))]
36pub enum StudentsTError {
37 VNotFinite { v: f64 },
39 VTooLow { v: f64 },
41}
42
43impl StudentsT {
44 #[inline]
46 pub fn new(v: f64) -> Result<Self, StudentsTError> {
47 if v <= 0.0 {
48 Err(StudentsTError::VTooLow { v })
49 } else if !v.is_finite() {
50 Err(StudentsTError::VNotFinite { v })
51 } else {
52 Ok(StudentsT { v })
53 }
54 }
55
56 #[inline]
59 pub fn new_unchecked(v: f64) -> Self {
60 StudentsT { v }
61 }
62
63 #[inline]
65 pub fn v(&self) -> f64 {
66 self.v
67 }
68
69 #[inline]
100 pub fn set_v(&mut self, v: f64) -> Result<(), StudentsTError> {
101 if !v.is_finite() {
102 Err(StudentsTError::VNotFinite { v })
103 } else if v <= 0.0 {
104 Err(StudentsTError::VTooLow { v })
105 } else {
106 self.set_v_unchecked(v);
107 Ok(())
108 }
109 }
110
111 #[inline]
113 pub fn set_v_unchecked(&mut self, v: f64) {
114 self.v = v;
115 }
116}
117
118impl Default for StudentsT {
119 fn default() -> Self {
120 StudentsT { v: 2.0 }
121 }
122}
123
124impl From<&StudentsT> for String {
125 fn from(t: &StudentsT) -> String {
126 format!("Student's({})", t.v)
127 }
128}
129
130impl_display!(StudentsT);
131
132macro_rules! impl_traits {
133 ($kind:ty) => {
134 impl HasDensity<$kind> for StudentsT {
135 fn ln_f(&self, x: &$kind) -> f64 {
136 let vp1 = (self.v + 1.0) / 2.0;
138 let xf = f64::from(*x);
139 let xterm = -vp1 * (xf * xf / self.v).ln_1p();
140 let zterm = 0.5_f64.mul_add(
141 -(self.v * PI).ln(),
142 ln_gammafn(vp1) - ln_gammafn(self.v / 2.0),
143 );
144 zterm + xterm
145 }
146 }
147
148 impl Sampleable<$kind> for StudentsT {
149 fn draw<R: Rng>(&self, rng: &mut R) -> $kind {
150 let t = rand_distr::StudentT::new(self.v).unwrap();
151 rng.sample(t) as $kind
152 }
153
154 fn sample<R: Rng>(&self, n: usize, rng: &mut R) -> Vec<$kind> {
155 let t = rand_distr::StudentT::new(self.v).unwrap();
156 (0..n).map(|_| rng.sample(t) as $kind).collect()
157 }
158 }
159
160 impl Support<$kind> for StudentsT {
161 fn supports(&self, x: &$kind) -> bool {
162 x.is_finite()
163 }
164 }
165
166 impl ContinuousDistr<$kind> for StudentsT {}
167
168 impl Mean<$kind> for StudentsT {
169 fn mean(&self) -> Option<$kind> {
170 if self.v > 1.0 {
171 Some(0.0)
172 } else {
173 None
174 }
175 }
176 }
177
178 impl Median<$kind> for StudentsT {
179 fn median(&self) -> Option<$kind> {
180 Some(0.0)
181 }
182 }
183
184 impl Mode<$kind> for StudentsT {
185 fn mode(&self) -> Option<$kind> {
186 Some(0.0)
187 }
188 }
189
190 impl Variance<$kind> for StudentsT {
191 fn variance(&self) -> Option<$kind> {
192 if self.v > 2.0 {
193 Some((self.v / (self.v - 2.0)) as $kind)
194 } else {
195 None
196 }
197 }
198 }
199 };
200}
201
202impl Skewness for StudentsT {
203 fn skewness(&self) -> Option<f64> {
204 if self.v > 3.0 {
205 Some(0.0)
206 } else {
207 None
208 }
209 }
210}
211
212impl Kurtosis for StudentsT {
213 fn kurtosis(&self) -> Option<f64> {
214 if self.v > 4.0 {
215 Some(6.0 / (self.v - 4.0))
216 } else if self.v > 2.0 {
217 Some(f64::INFINITY)
218 } else {
219 None
220 }
221 }
222}
223
224impl_traits!(f64);
225impl_traits!(f32);
226
227impl std::error::Error for StudentsTError {}
228
229impl fmt::Display for StudentsTError {
230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231 match self {
232 Self::VNotFinite { v } => write!(f, "non-finite v: {}", v),
233 Self::VTooLow { v } => {
234 write!(f, "v ({}) must be greater than zero", v)
235 }
236 }
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243 use crate::test_basic_impls;
244 use std::f64;
245
246 const TOL: f64 = 1E-12;
247
248 test_basic_impls!(f64, StudentsT);
249
250 #[test]
251 fn new() {
252 let t = StudentsT::new(2.3).unwrap();
253 assert::close(t.v, 2.3, TOL);
254 }
255
256 #[test]
257 fn new_should_reject_v_leq_zero() {
258 assert!(StudentsT::new(f64::MIN_POSITIVE).is_ok());
259 assert!(StudentsT::new(0.0).is_err());
260 assert!(StudentsT::new(-f64::MIN_POSITIVE).is_err());
261 assert!(StudentsT::new(-1.0).is_err());
262 }
263
264 #[test]
265 fn new_should_reject_non_finite_v() {
266 assert!(StudentsT::new(f64::INFINITY).is_err());
267 assert!(StudentsT::new(-f64::NAN).is_err());
268 assert!(StudentsT::new(f64::NEG_INFINITY).is_err());
269 }
270
271 #[test]
272 fn ln_pdf() {
273 let t = StudentsT::new(2.3).unwrap();
274 assert::close(t.ln_pdf(&0.0_f64), -1.024_744_023_893_756_6, TOL);
275 assert::close(t.ln_pdf(&1.0_f64), -1.620_416_044_030_352, TOL);
276 assert::close(t.ln_pdf(&2.5_f64), -3.191_230_587_916_138, TOL);
277 assert::close(t.ln_pdf(&-2.5_f64), -3.191_230_587_916_138, TOL);
278 }
279
280 #[test]
281 fn variance() {
282 let v: f64 = StudentsT::new(2.3).unwrap().variance().unwrap();
283 assert::close(v, 7.666_666_666_666_670_5, TOL);
284 }
285
286 #[test]
287 fn median() {
288 let m: f64 = StudentsT::new(2.3).unwrap().median().unwrap();
289 assert::close(m, 0.0, TOL);
290 }
291
292 #[test]
293 fn mode() {
294 let m: f64 = StudentsT::new(2.3).unwrap().mode().unwrap();
295 assert::close(m, 0.0, TOL);
296 }
297}