1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::statistics::*;
3use std::f64;
4
5#[derive(Copy, Clone, PartialEq, Debug)]
22pub struct Exp {
23 rate: f64,
24}
25
26#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
28#[non_exhaustive]
29pub enum ExpError {
30 RateInvalid,
32}
33
34impl std::fmt::Display for ExpError {
35 #[cfg_attr(coverage_nightly, coverage(off))]
36 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
37 match self {
38 ExpError::RateInvalid => write!(f, "Rate is NaN, zero or less than zero"),
39 }
40 }
41}
42
43impl std::error::Error for ExpError {}
44
45impl Exp {
46 pub fn new(rate: f64) -> Result<Exp, ExpError> {
65 if rate.is_nan() || rate <= 0.0 {
66 Err(ExpError::RateInvalid)
67 } else {
68 Ok(Exp { rate })
69 }
70 }
71
72 pub fn rate(&self) -> f64 {
83 self.rate
84 }
85}
86
87impl std::fmt::Display for Exp {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(f, "Exp({})", self.rate)
90 }
91}
92
93#[cfg(feature = "rand")]
94#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
95impl ::rand::distributions::Distribution<f64> for Exp {
96 fn sample<R: ::rand::Rng + ?Sized>(&self, r: &mut R) -> f64 {
97 use crate::distribution::ziggurat;
98
99 ziggurat::sample_exp_1(r) / self.rate
100 }
101}
102
103impl ContinuousCDF<f64, f64> for Exp {
104 fn cdf(&self, x: f64) -> f64 {
115 if x < 0.0 {
116 0.0
117 } else {
118 1.0 - (-self.rate * x).exp()
119 }
120 }
121
122 fn sf(&self, x: f64) -> f64 {
133 if x < 0.0 {
134 1.0
135 } else {
136 (-self.rate * x).exp()
137 }
138 }
139
140 fn inverse_cdf(&self, p: f64) -> f64 {
150 -(-p).ln_1p() / self.rate
151 }
152}
153
154impl Min<f64> for Exp {
155 fn min(&self) -> f64 {
164 0.0
165 }
166}
167
168impl Max<f64> for Exp {
169 fn max(&self) -> f64 {
178 f64::INFINITY
179 }
180}
181
182impl Distribution<f64> for Exp {
183 fn mean(&self) -> Option<f64> {
193 Some(1.0 / self.rate)
194 }
195
196 fn variance(&self) -> Option<f64> {
206 Some(1.0 / (self.rate * self.rate))
207 }
208
209 fn entropy(&self) -> Option<f64> {
219 Some(1.0 - self.rate.ln())
220 }
221
222 fn skewness(&self) -> Option<f64> {
230 Some(2.0)
231 }
232}
233
234impl Median<f64> for Exp {
235 fn median(&self) -> f64 {
245 f64::consts::LN_2 / self.rate
246 }
247}
248
249impl Mode<Option<f64>> for Exp {
250 fn mode(&self) -> Option<f64> {
258 Some(0.0)
259 }
260}
261
262impl Continuous<f64, f64> for Exp {
263 fn pdf(&self, x: f64) -> f64 {
274 if x < 0.0 {
275 0.0
276 } else {
277 self.rate * (-self.rate * x).exp()
278 }
279 }
280
281 fn ln_pdf(&self, x: f64) -> f64 {
292 if x < 0.0 {
293 f64::NEG_INFINITY
294 } else {
295 self.rate.ln() - self.rate * x
296 }
297 }
298}
299
300#[rustfmt::skip]
301#[cfg(test)]
302mod tests {
303 use super::*;
304 use crate::distribution::internal::*;
305 use crate::testing_boiler;
306
307 testing_boiler!(rate: f64; Exp; ExpError);
308
309 #[test]
310 fn test_create() {
311 create_ok(0.1);
312 create_ok(1.0);
313 create_ok(10.0);
314 }
315
316 #[test]
317 fn test_bad_create() {
318 create_err(f64::NAN);
319 create_err(0.0);
320 create_err(-1.0);
321 create_err(-10.0);
322 }
323
324 #[test]
325 fn test_mean() {
326 let mean = |x: Exp| x.mean().unwrap();
327 test_exact(0.1, 10.0, mean);
328 test_exact(1.0, 1.0, mean);
329 test_exact(10.0, 0.1, mean);
330 }
331
332 #[test]
333 fn test_variance() {
334 let variance = |x: Exp| x.variance().unwrap();
335 test_absolute(0.1, 100.0, 1e-13, variance);
336 test_exact(1.0, 1.0, variance);
337 test_exact(10.0, 0.01, variance);
338 }
339
340 #[test]
341 fn test_entropy() {
342 let entropy = |x: Exp| x.entropy().unwrap();
343 test_absolute(0.1, 3.302585092994045684018, 1e-15, entropy);
344 test_exact(1.0, 1.0, entropy);
345 test_absolute(10.0, -1.302585092994045684018, 1e-15, entropy);
346 }
347
348 #[test]
349 fn test_skewness() {
350 let skewness = |x: Exp| x.skewness().unwrap();
351 test_exact(0.1, 2.0, skewness);
352 test_exact(1.0, 2.0, skewness);
353 test_exact(10.0, 2.0, skewness);
354 }
355
356 #[test]
357 fn test_median() {
358 let median = |x: Exp| x.median();
359 test_absolute(0.1, 6.931471805599453094172, 1e-15, median);
360 test_exact(1.0, f64::consts::LN_2, median);
361 test_exact(10.0, 0.06931471805599453094172, median);
362 }
363
364 #[test]
365 fn test_mode() {
366 let mode = |x: Exp| x.mode().unwrap();
367 test_exact(0.1, 0.0, mode);
368 test_exact(1.0, 0.0, mode);
369 test_exact(10.0, 0.0, mode);
370 }
371
372 #[test]
373 fn test_min_max() {
374 let min = |x: Exp| x.min();
375 let max = |x: Exp| x.max();
376 test_exact(0.1, 0.0, min);
377 test_exact(1.0, 0.0, min);
378 test_exact(10.0, 0.0, min);
379 test_exact(0.1, f64::INFINITY, max);
380 test_exact(1.0, f64::INFINITY, max);
381 test_exact(10.0, f64::INFINITY, max);
382 }
383
384 #[test]
385 fn test_pdf() {
386 let pdf = |arg: f64| move |x: Exp| x.pdf(arg);
387 test_exact(0.1, 0.1, pdf(0.0));
388 test_exact(1.0, 1.0, pdf(0.0));
389 test_exact(10.0, 10.0, pdf(0.0));
390 test_is_nan(f64::INFINITY, pdf(0.0));
391 test_exact(0.1, 0.09900498337491680535739, pdf(0.1));
392 test_absolute(1.0, 0.9048374180359595731642, 1e-15, pdf(0.1));
393 test_exact(10.0, 3.678794411714423215955, pdf(0.1));
394 test_is_nan(f64::INFINITY, pdf(0.1));
395 test_exact(0.1, 0.09048374180359595731642, pdf(1.0));
396 test_exact(1.0, 0.3678794411714423215955, pdf(1.0));
397 test_absolute(10.0, 4.539992976248485153559e-4, 1e-19, pdf(1.0));
398 test_is_nan(f64::INFINITY, pdf(1.0));
399 test_exact(0.1, 0.0, pdf(f64::INFINITY));
400 test_exact(1.0, 0.0, pdf(f64::INFINITY));
401 test_exact(10.0, 0.0, pdf(f64::INFINITY));
402 test_is_nan(f64::INFINITY, pdf(f64::INFINITY));
403 }
404
405 #[test]
406 fn test_neg_pdf() {
407 let pdf = |arg: f64| move |x: Exp| x.pdf(arg);
408 test_exact(0.1, 0.0, pdf(-1.0));
409 }
410
411 #[test]
412 fn test_ln_pdf() {
413 let ln_pdf = |arg: f64| move |x: Exp| x.ln_pdf(arg);
414 test_absolute(0.1, -2.302585092994045684018, 1e-15, ln_pdf(0.0));
415 test_exact(1.0, 0.0, ln_pdf(0.0));
416 test_exact(10.0, 2.302585092994045684018, ln_pdf(0.0));
417 test_is_nan(f64::INFINITY, ln_pdf(0.0));
418 test_absolute(0.1, -2.312585092994045684018, 1e-15, ln_pdf(0.1));
419 test_exact(1.0, -0.1, ln_pdf(0.1));
420 test_absolute(10.0, 1.302585092994045684018, 1e-15, ln_pdf(0.1));
421 test_is_nan(f64::INFINITY, ln_pdf(0.1));
422 test_exact(0.1, -2.402585092994045684018, ln_pdf(1.0));
423 test_exact(1.0, -1.0, ln_pdf(1.0));
424 test_exact(10.0, -7.697414907005954315982, ln_pdf(1.0));
425 test_is_nan(f64::INFINITY, ln_pdf(1.0));
426 test_exact(0.1, f64::NEG_INFINITY, ln_pdf(f64::INFINITY));
427 test_exact(1.0, f64::NEG_INFINITY, ln_pdf(f64::INFINITY));
428 test_exact(10.0, f64::NEG_INFINITY, ln_pdf(f64::INFINITY));
429 test_is_nan(f64::INFINITY, ln_pdf(f64::INFINITY));
430 }
431
432 #[test]
433 fn test_neg_ln_pdf() {
434 let ln_pdf = |arg: f64| move |x: Exp| x.ln_pdf(arg);
435 test_exact(0.1, f64::NEG_INFINITY, ln_pdf(-1.0));
436 }
437
438 #[test]
439 fn test_cdf() {
440 let cdf = |arg: f64| move |x: Exp| x.cdf(arg);
441 test_exact(0.1, 0.0, cdf(0.0));
442 test_exact(1.0, 0.0, cdf(0.0));
443 test_exact(10.0, 0.0, cdf(0.0));
444 test_is_nan(f64::INFINITY, cdf(0.0));
445 test_absolute(0.1, 0.009950166250831946426094, 1e-16, cdf(0.1));
446 test_absolute(1.0, 0.0951625819640404268358, 1e-16, cdf(0.1));
447 test_exact(10.0, 0.6321205588285576784045, cdf(0.1));
448 test_exact(f64::INFINITY, 1.0, cdf(0.1));
449 test_absolute(0.1, 0.0951625819640404268358, 1e-16, cdf(1.0));
450 test_exact(1.0, 0.6321205588285576784045, cdf(1.0));
451 test_exact(10.0, 0.9999546000702375151485, cdf(1.0));
452 test_exact(f64::INFINITY, 1.0, cdf(1.0));
453 test_exact(0.1, 1.0, cdf(f64::INFINITY));
454 test_exact(1.0, 1.0, cdf(f64::INFINITY));
455 test_exact(10.0, 1.0, cdf(f64::INFINITY));
456 test_exact(f64::INFINITY, 1.0, cdf(f64::INFINITY));
457 }
458
459 #[test]
460 fn test_inverse_cdf() {
461 let distribution = Exp::new(0.42).unwrap();
462 assert_eq!(distribution.median(), distribution.inverse_cdf(0.5));
463
464 let distribution = Exp::new(0.042).unwrap();
465 assert_eq!(distribution.median(), distribution.inverse_cdf(0.5));
466
467 let distribution = Exp::new(0.0042).unwrap();
468 assert_eq!(distribution.median(), distribution.inverse_cdf(0.5));
469
470 let distribution = Exp::new(0.33).unwrap();
471 assert_eq!(distribution.median(), distribution.inverse_cdf(0.5));
472
473 let distribution = Exp::new(0.033).unwrap();
474 assert_eq!(distribution.median(), distribution.inverse_cdf(0.5));
475
476 let distribution = Exp::new(0.0033).unwrap();
477 assert_eq!(distribution.median(), distribution.inverse_cdf(0.5));
478 }
479
480 #[test]
481 fn test_sf() {
482 let sf = |arg: f64| move |x: Exp| x.sf(arg);
483 test_exact(0.1, 1.0, sf(0.0));
484 test_exact(1.0, 1.0, sf(0.0));
485 test_exact(10.0, 1.0, sf(0.0));
486 test_is_nan(f64::INFINITY, sf(0.0));
487 test_absolute(0.1, 0.9900498337491681, 1e-16, sf(0.1));
488 test_absolute(1.0, 0.9048374180359595, 1e-16, sf(0.1));
489 test_absolute(10.0, 0.36787944117144233, 1e-15, sf(0.1));
490 test_exact(f64::INFINITY, 0.0, sf(0.1));
491 }
492
493 #[test]
494 fn test_neg_cdf() {
495 let cdf = |arg: f64| move |x: Exp| x.cdf(arg);
496 test_exact(0.1, 0.0, cdf(-1.0));
497 }
498
499 #[test]
500 fn test_neg_sf() {
501 let sf = |arg: f64| move |x: Exp| x.sf(arg);
502 test_exact(0.1, 1.0, sf(-1.0));
503 }
504
505 #[test]
506 fn test_continuous() {
507 test::check_continuous_distribution(&create_ok(0.5), 0.0, 10.0);
508 test::check_continuous_distribution(&create_ok(1.5), 0.0, 20.0);
509 test::check_continuous_distribution(&create_ok(2.5), 0.0, 50.0);
510 }
511}