1use super::{Continuous, ContinuousCDF};
2use crate::consts::EULER_MASCHERONI;
3use crate::statistics::*;
4use std::f64::{self, consts::PI};
5
6#[derive(Copy, Clone, PartialEq, Debug)]
21pub struct Gumbel {
22 location: f64,
23 scale: f64,
24}
25
26#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
28#[non_exhaustive]
29pub enum GumbelError {
30 LocationInvalid,
32
33 ScaleInvalid,
35}
36
37impl std::fmt::Display for GumbelError {
38 #[cfg_attr(coverage_nightly, coverage(off))]
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 match self {
41 GumbelError::LocationInvalid => write!(f, "Location is NAN"),
42 GumbelError::ScaleInvalid => write!(f, "Scale is NAN, zero or less than zero"),
43 }
44 }
45}
46
47impl std::error::Error for GumbelError {}
48
49impl Gumbel {
50 pub fn new(location: f64, scale: f64) -> Result<Self, GumbelError> {
69 if location.is_nan() {
70 return Err(GumbelError::LocationInvalid);
71 }
72
73 if scale.is_nan() || scale <= 0.0 {
74 return Err(GumbelError::ScaleInvalid);
75 }
76
77 Ok(Self { location, scale })
78 }
79
80 pub fn location(&self) -> f64 {
91 self.location
92 }
93
94 pub fn scale(&self) -> f64 {
105 self.scale
106 }
107}
108
109impl std::fmt::Display for Gumbel {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 write!(f, "Gumbel({:?}, {:?})", self.location, self.scale)
112 }
113}
114
115#[cfg(feature = "rand")]
116impl ::rand::distributions::Distribution<f64> for Gumbel {
117 fn sample<R: rand::Rng + ?Sized>(&self, r: &mut R) -> f64 {
118 self.location - self.scale * ((-(r.gen::<f64>())).ln()).ln()
119 }
120}
121
122impl ContinuousCDF<f64, f64> for Gumbel {
123 fn cdf(&self, x: f64) -> f64 {
134 (-(-(x - self.location) / self.scale).exp()).exp()
135 }
136
137 fn inverse_cdf(&self, p: f64) -> f64 {
150 if p <= 0.0 {
151 f64::NEG_INFINITY
152 } else if p >= 1.0 {
153 f64::INFINITY
154 } else {
155 self.location - self.scale * ((-(p.ln())).ln())
156 }
157 }
158
159 fn sf(&self, x: f64) -> f64 {
170 -(-(-(x - self.location) / self.scale).exp()).exp_m1()
171 }
172}
173
174impl Min<f64> for Gumbel {
175 fn min(&self) -> f64 {
184 f64::NEG_INFINITY
185 }
186}
187
188impl Max<f64> for Gumbel {
189 fn max(&self) -> f64 {
198 f64::INFINITY
199 }
200}
201
202impl Distribution<f64> for Gumbel {
203 fn entropy(&self) -> Option<f64> {
214 Some(1.0 + EULER_MASCHERONI + (self.scale).ln())
215 }
216
217 fn mean(&self) -> Option<f64> {
228 Some(self.location + (EULER_MASCHERONI * self.scale))
229 }
230
231 fn skewness(&self) -> Option<f64> {
243 Some(1.13955)
244 }
245
246 fn variance(&self) -> Option<f64> {
256 Some(((PI * PI) / 6.0) * self.scale * self.scale)
257 }
258
259 fn std_dev(&self) -> Option<f64> {
269 Some(self.scale * PI / 6.0_f64.sqrt())
270 }
271}
272
273impl Median<f64> for Gumbel {
274 fn median(&self) -> f64 {
284 self.location - self.scale * (((2.0_f64).ln()).ln())
285 }
286}
287
288impl Mode<f64> for Gumbel {
289 fn mode(&self) -> f64 {
299 self.location
300 }
301}
302
303impl Continuous<f64, f64> for Gumbel {
304 fn pdf(&self, x: f64) -> f64 {
315 (1.0_f64 / self.scale)
316 * (-(x - self.location) / (self.scale)).exp()
317 * (-((-(x - self.location) / self.scale).exp())).exp()
318 }
319
320 fn ln_pdf(&self, x: f64) -> f64 {
331 ((1.0_f64 / self.scale)
332 * (-(x - self.location) / (self.scale)).exp()
333 * (-((-(x - self.location) / self.scale).exp())).exp())
334 .ln()
335 }
336}
337
338#[rustfmt::skip]
339#[cfg(test)]
340mod tests {
341 use super::*;
342 use crate::testing_boiler;
343
344 testing_boiler!(location: f64, scale: f64; Gumbel; GumbelError);
345
346 #[test]
347 fn test_create() {
348 create_ok(0.0, 0.1);
349 create_ok(0.0, 1.0);
350 create_ok(0.0, 10.0);
351 create_ok(10.0, 11.0);
352 create_ok(-5.0, 100.0);
353 create_ok(0.0, f64::INFINITY);
354 }
355
356 #[test]
357 fn test_bad_create() {
358 let invalid = [
359 (f64::NAN, 1.0, GumbelError::LocationInvalid),
360 (1.0, f64::NAN, GumbelError::ScaleInvalid),
361 (f64::NAN, f64::NAN, GumbelError::LocationInvalid),
362 (1.0, 0.0, GumbelError::ScaleInvalid),
363 (0.0, f64::NEG_INFINITY, GumbelError::ScaleInvalid)
364 ];
365
366 for (location, scale, err) in invalid {
367 test_create_err(location, scale, err);
368 }
369 }
370
371 #[test]
372 fn test_min_max() {
373 let min = |x: Gumbel| x.min();
374 let max = |x:Gumbel| x.max();
375
376 test_exact(0.0, 1.0, f64::NEG_INFINITY, min);
377 test_exact(0.0, 1.0, f64::INFINITY, max);
378 }
379
380 #[test]
381 fn test_entropy() {
382 let entropy = |x: Gumbel| x.entropy().unwrap();
383 test_exact(0.0, 2.0, 2.270362845461478, entropy);
384 test_exact(0.1, 4.0, 2.9635100260214235, entropy);
385 test_exact(1.0, 10.0, 3.8798007578955787, entropy);
386 test_exact(10.0, 11.0, 3.9751109376999034, entropy);
387 }
388
389 #[test]
390 fn test_mean() {
391 let mean = |x: Gumbel| x.mean().unwrap();
392 test_exact(0.0, 2.0, 1.1544313298030658, mean);
393 test_exact(0.1, 4.0, 2.4088626596061316, mean);
394 test_exact(1.0, 10.0, 6.772156649015328, mean);
395 test_exact(10.0, 11.0, 16.34937231391686, mean);
396 test_exact(10.0, f64::INFINITY, f64::INFINITY, mean);
397 }
398
399 #[test]
400 fn test_skewness() {
401 let skewness = |x: Gumbel| x.skewness().unwrap();
402 test_exact(0.0, 2.0, 1.13955, skewness);
403 test_exact(0.1, 4.0, 1.13955, skewness);
404 test_exact(1.0, 10.0, 1.13955, skewness);
405 test_exact(10.0, 11.0, 1.13955, skewness);
406 test_exact(10.0, f64::INFINITY, 1.13955, skewness);
407 }
408
409 #[test]
410 fn test_variance() {
411 let variance = |x: Gumbel| x.variance().unwrap();
412 test_exact(0.0, 2.0, 6.579736267392906, variance);
413 test_exact(0.1, 4.0, 26.318945069571624, variance);
414 test_exact(1.0, 10.0, 164.49340668482265, variance);
415 test_exact(10.0, 11.0, 199.03702208863538, variance);
416 }
417
418 #[test]
419 fn test_std_dev() {
420 let std_dev = |x: Gumbel| x.std_dev().unwrap();
421 test_exact(0.0, 2.0, 2.565099660323728, std_dev);
422 test_exact(0.1, 4.0, 5.130199320647456, std_dev);
423 test_exact(1.0, 10.0, 12.82549830161864, std_dev);
424 test_exact(10.0, 11.0, 14.108048131780505, std_dev);
425 }
426
427 #[test]
428 fn test_median() {
429 let median = |x: Gumbel| x.median();
430 test_exact(0.0, 2.0, 0.7330258411633287, median);
431 test_exact(0.1, 4.0, 1.5660516823266574, median);
432 test_exact(1.0, 10.0, 4.665129205816644, median);
433 test_exact(10.0, 11.0, 14.031642126398307, median);
434 test_exact(10.0, f64::INFINITY, f64::INFINITY, median);
435 }
436
437 #[test]
438 fn test_mode() {
439 let mode = |x: Gumbel| x.mode();
440 test_exact(0.0, 2.0, 0.0, mode);
441 test_exact(0.1, 4.0, 0.1, mode);
442 test_exact(1.0, 10.0, 1.0, mode);
443 test_exact(10.0, 11.0, 10.0, mode);
444 test_exact(10.0, f64::INFINITY, 10.0, mode);
445 }
446
447 #[test]
448 fn test_cdf() {
449 let cdf = |a: f64| move |x: Gumbel| x.cdf(a);
450 test_exact(0.0, 0.1, 0.0, cdf(-5.0));
451 test_exact(0.0, 0.1, 0.0, cdf(-1.0));
452 test_exact(0.0, 0.1, 0.36787944117144233, cdf(0.0));
453 test_exact(0.0, 0.1, 0.9999546011007987, cdf(1.0));
454 test_absolute(0.0, 0.1, 0.99999999999999999, 1e-12, cdf(5.0));
455 test_absolute(0.0, 1.0, 0.06598803584531253, 1e-12, cdf(-1.0));
456 test_exact(0.0, 1.0, 0.36787944117144233, cdf(0.0));
457 test_absolute(0.0, 10.0, 0.192295645547964928, 1e-12, cdf(-5.0));
458 test_absolute(0.0, 10.0, 0.3311542771529088, 1e-12, cdf(-1.0));
459 test_exact(0.0, 10.0, 0.36787944117144233, cdf(0.0));
460 test_absolute(0.0, 10.0, 0.4046076616641318, 1e-12, cdf(1.0));
461 test_absolute(0.0, 10.0, 0.545239211892605, 1e-12, cdf(5.0));
462 test_exact(-2.0, f64::INFINITY, 0.36787944117144233, cdf(-5.0));
463 test_exact(-2.0, f64::INFINITY, 0.36787944117144233, cdf(-1.0));
464 test_exact(-2.0, f64::INFINITY, 0.36787944117144233, cdf(0.0));
465 test_exact(-2.0, f64::INFINITY, 0.36787944117144233, cdf(1.0));
466 test_exact(-2.0, f64::INFINITY, 0.36787944117144233, cdf(5.0));
467 test_exact(f64::INFINITY, 1.0, 0.0, cdf(-5.0));
468 test_exact(f64::INFINITY, 1.0, 0.0, cdf(-1.0));
469 test_exact(f64::INFINITY, 1.0, 0.0, cdf(0.0));
470 test_exact(f64::INFINITY, 1.0, 0.0, cdf(1.0));
471 test_exact(f64::INFINITY, 1.0, 0.0, cdf(5.0));
472 }
473
474 #[test]
475 fn test_inverse_cdf() {
476 let inv_cdf = |a: f64| move |x: Gumbel| x.inverse_cdf(a);
477 test_exact(0.0, 0.1, f64::NEG_INFINITY, inv_cdf(-5.0));
478 test_exact(0.0, 0.1, f64::NEG_INFINITY, inv_cdf(-1.0));
479 test_exact(0.0, 0.1, f64::NEG_INFINITY, inv_cdf(0.0));
480 test_exact(0.0, 0.1, f64::INFINITY, inv_cdf(1.0));
481 test_exact(0.0, 0.1, f64::INFINITY, inv_cdf(5.0));
482 test_absolute(0.0, 1.0, -0.8340324452479557, 1e-12, inv_cdf(0.1));
483 test_absolute(0.0, 10.0, 3.6651292058166436, 1e-12, inv_cdf(0.5));
484 test_absolute(0.0, 10.0, 22.503673273124456, 1e-12, inv_cdf(0.9));
485 test_exact(2.0, f64::INFINITY, f64::NEG_INFINITY, inv_cdf(0.1));
486 test_exact(-2.0, f64::INFINITY, f64::INFINITY, inv_cdf(0.5));
487 test_exact(f64::INFINITY, 1.0, f64::INFINITY, inv_cdf(0.1));
488 }
489
490 #[test]
491 fn test_sf() {
492 let sf = |a: f64| move |x: Gumbel| x.sf(a);
493 test_exact(0.0, 0.1, 1.0, sf(-5.0));
494 test_exact(0.0, 0.1, 1.0, sf(-1.0));
495 test_absolute(0.0, 0.1, 0.632120558828557678, 1e-12, sf(0.0));
496 test_absolute(0.0, 0.1, 0.000045398899201269, 1e-12, sf(1.0));
497 test_absolute(0.0, 1.0, 0.934011964154687462, 1e-12, sf(-1.0));
498 test_absolute(0.0, 1.0, 0.632120558828557678, 1e-12, sf(0.0));
499 test_absolute(0.0, 1.0, 0.3077993724446536, 1e-12, sf(1.0));
500 test_absolute(0.0, 10.0, 0.66884572284709110, 1e-12, sf(-1.0));
501 test_absolute(0.0, 10.0, 0.632120558828557678, 1e-12, sf(0.0));
502 test_absolute(0.0, 10.0, 0.595392338335868174, 1e-12, sf(1.0));
503 test_exact(-2.0, f64::INFINITY, 0.6321205588285576784, sf(-5.0));
504 test_exact(-2.0, f64::INFINITY, 0.6321205588285576784, sf(-1.0));
505 test_exact(-2.0, f64::INFINITY, 0.6321205588285576784, sf(0.0));
506 test_exact(-2.0, f64::INFINITY, 0.6321205588285576784, sf(1.0));
507 test_exact(-2.0, f64::INFINITY, 0.6321205588285576784, sf(5.0));
508 test_exact(f64::INFINITY, 1.0, 1.0, sf(-5.0));
509 test_exact(f64::INFINITY, 1.0, 1.0, sf(-1.0));
510 test_exact(f64::INFINITY, 1.0, 1.0, sf(0.0));
511 test_exact(f64::INFINITY, 1.0, 1.0, sf(1.0));
512 test_exact(f64::INFINITY, 1.0, 1.0, sf(5.0));
513 test_absolute(0.0, 1.0, 4.248354255291589e-18, 1e-32, sf(40.0));
514 test_absolute(0.0, 1.0, 1.804851387845415e-35, 1e-50, sf(80.0));
515 }
516
517 #[test]
518 fn test_pdf() {
519 let pdf = |a: f64| move |x: Gumbel| x.pdf(a);
520 test_exact(0.0, 0.1, 0.0, pdf(-5.0));
521 test_exact(0.0, 0.1, 3.678794411714423215, pdf(0.0));
522 test_absolute(0.0, 0.1, 0.0004539786865564, 1e-12, pdf(1.0));
523 test_absolute(0.0, 1.0, 0.1793740787340171, 1e-12, pdf(-1.0));
524 test_exact(0.0, 1.0, 0.36787944117144233, pdf(0.0));
525 test_absolute(0.0, 1.0, 0.25464638004358249, 1e-12, pdf(1.0));
526 test_absolute(0.0, 10.0, 0.031704192107794217, 1e-12, pdf(-5.0));
527 test_absolute(0.0, 10.0, 0.0365982076505757, 1e-12, pdf(-1.0));
528 test_exact(0.0, 10.0, 0.036787944117144233, pdf(0.0));
529 test_absolute(0.0, 10.0, 0.03661041518977401428, 1e-12, pdf(1.0));
530 test_absolute(0.0, 10.0, 0.033070429889041, 1e-12, pdf(5.0));
531 test_exact(-2.0, f64::INFINITY, 0.0, pdf(-5.0));
532 test_exact(-2.0, f64::INFINITY, 0.0, pdf(-1.0));
533 test_exact(-2.0, f64::INFINITY, 0.0, pdf(0.0));
534 test_exact(-2.0, f64::INFINITY, 0.0, pdf(1.0));
535 test_exact(-2.0, f64::INFINITY, 0.0, pdf(5.0));
536 }
537
538 #[test]
539 fn test_ln_pdf() {
540 let ln_pdf = |a: f64| move |x: Gumbel| x.ln_pdf(a);
541 test_exact(0.0, 0.1, 0.0_f64.ln(), ln_pdf(-5.0));
542 test_exact(0.0, 0.1, 3.678794411714423215_f64.ln(), ln_pdf(0.0));
543 test_absolute(0.0, 0.1, 0.0004539786865564_f64.ln(), 1e-12, ln_pdf(1.0));
544 test_absolute(0.0, 1.0, 0.1793740787340171_f64.ln(), 1e-12, ln_pdf(-1.0));
545 test_exact(0.0, 1.0, 0.36787944117144233_f64.ln(), ln_pdf(0.0));
546 test_absolute(0.0, 1.0, 0.25464638004358249_f64.ln(), 1e-12, ln_pdf(1.0));
547 test_absolute(0.0, 10.0, 0.031704192107794217_f64.ln(), 1e-12, ln_pdf(-5.0));
548 test_absolute(0.0, 10.0, 0.0365982076505757_f64.ln(), 1e-12, ln_pdf(-1.0));
549 test_exact(0.0, 10.0, 0.036787944117144233_f64.ln(), ln_pdf(0.0));
550 test_absolute(0.0, 10.0, 0.03661041518977401428_f64.ln(), 1e-12, ln_pdf(1.0));
551 test_absolute(0.0, 10.0, 0.033070429889041_f64.ln(), 1e-12, ln_pdf(5.0));
552 test_exact(-2.0, f64::INFINITY, 0.0_f64.ln(), ln_pdf(-5.0));
553 test_exact(-2.0, f64::INFINITY, 0.0_f64.ln(), ln_pdf(-1.0));
554 test_exact(-2.0, f64::INFINITY, 0.0_f64.ln(), ln_pdf(0.0));
555 test_exact(-2.0, f64::INFINITY, 0.0_f64.ln(), ln_pdf(1.0));
556 test_exact(-2.0, f64::INFINITY, 0.0_f64.ln(), ln_pdf(5.0));
557 }
558}