1use crate::distribution::{Continuous, ContinuousCDF};
2use crate::statistics::*;
3use std::f64;
4
5#[derive(Copy, Clone, PartialEq, Debug)]
20pub struct Pareto {
21 scale: f64,
22 shape: f64,
23}
24
25#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
27#[non_exhaustive]
28pub enum ParetoError {
29 ScaleInvalid,
31
32 ShapeInvalid,
34}
35
36impl std::fmt::Display for ParetoError {
37 #[cfg_attr(coverage_nightly, coverage(off))]
38 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
39 match self {
40 ParetoError::ScaleInvalid => write!(f, "Scale is NaN, zero, or less than zero"),
41 ParetoError::ShapeInvalid => write!(f, "Shape is NaN, zero, or less than zero"),
42 }
43 }
44}
45
46impl std::error::Error for ParetoError {}
47
48impl Pareto {
49 pub fn new(scale: f64, shape: f64) -> Result<Pareto, ParetoError> {
69 if scale.is_nan() || scale <= 0.0 {
70 return Err(ParetoError::ScaleInvalid);
71 }
72
73 if shape.is_nan() || shape <= 0.0 {
74 return Err(ParetoError::ShapeInvalid);
75 }
76
77 Ok(Pareto { scale, shape })
78 }
79
80 pub fn scale(&self) -> f64 {
91 self.scale
92 }
93
94 pub fn shape(&self) -> f64 {
105 self.shape
106 }
107}
108
109impl std::fmt::Display for Pareto {
110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111 write!(f, "Pareto({},{})", self.scale, self.shape)
112 }
113}
114
115#[cfg(feature = "rand")]
116#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
117impl ::rand::distributions::Distribution<f64> for Pareto {
118 fn sample<R: ::rand::Rng + ?Sized>(&self, rng: &mut R) -> f64 {
119 use rand::distributions::OpenClosed01;
120
121 let u: f64 = rng.sample(OpenClosed01);
123 self.scale * u.powf(-1.0 / self.shape)
124 }
125}
126
127impl ContinuousCDF<f64, f64> for Pareto {
128 fn cdf(&self, x: f64) -> f64 {
143 if x < self.scale {
144 0.0
145 } else {
146 1.0 - (self.scale / x).powf(self.shape)
147 }
148 }
149
150 fn sf(&self, x: f64) -> f64 {
165 if x < self.scale {
166 1.0
167 } else {
168 (self.scale / x).powf(self.shape)
169 }
170 }
171
172 fn inverse_cdf(&self, p: f64) -> f64 {
183 if !(0.0..=1.0).contains(&p) {
184 panic!("x must be in [0, 1]");
185 } else {
186 self.scale * (1.0 - p).powf(-1.0 / self.shape)
187 }
188 }
189}
190
191impl Min<f64> for Pareto {
192 fn min(&self) -> f64 {
203 self.scale
204 }
205}
206
207impl Max<f64> for Pareto {
208 fn max(&self) -> f64 {
217 f64::INFINITY
218 }
219}
220
221impl Distribution<f64> for Pareto {
222 fn mean(&self) -> Option<f64> {
236 if self.shape <= 1.0 {
237 None
238 } else {
239 Some((self.shape * self.scale) / (self.shape - 1.0))
240 }
241 }
242
243 fn variance(&self) -> Option<f64> {
257 if self.shape <= 2.0 {
258 None
259 } else {
260 let a = self.scale / (self.shape - 1.0); Some(a * a * self.shape / (self.shape - 2.0))
262 }
263 }
264
265 fn entropy(&self) -> Option<f64> {
275 Some(self.shape.ln() - self.scale.ln() - (1.0 / self.shape) - 1.0)
276 }
277
278 fn skewness(&self) -> Option<f64> {
294 if self.shape <= 3.0 {
295 None
296 } else {
297 Some(
298 (2.0 * (self.shape + 1.0) / (self.shape - 3.0))
299 * ((self.shape - 2.0) / self.shape).sqrt(),
300 )
301 }
302 }
303}
304
305impl Median<f64> for Pareto {
306 fn median(&self) -> f64 {
316 self.scale * (2f64.powf(1.0 / self.shape))
317 }
318}
319
320impl Mode<Option<f64>> for Pareto {
321 fn mode(&self) -> Option<f64> {
331 Some(self.scale)
332 }
333}
334
335impl Continuous<f64, f64> for Pareto {
336 fn pdf(&self, x: f64) -> f64 {
351 if x < self.scale {
352 0.0
353 } else {
354 (self.shape * self.scale.powf(self.shape)) / x.powf(self.shape + 1.0)
355 }
356 }
357
358 fn ln_pdf(&self, x: f64) -> f64 {
373 if x < self.scale {
374 f64::NEG_INFINITY
375 } else {
376 self.shape.ln() + self.shape * self.scale.ln() - (self.shape + 1.0) * x.ln()
377 }
378 }
379}
380
381#[rustfmt::skip]
382#[cfg(test)]
383mod tests {
384 use super::*;
385 use crate::distribution::internal::*;
386 use crate::testing_boiler;
387
388 testing_boiler!(scale: f64, shape: f64; Pareto; ParetoError);
389
390 #[test]
391 fn test_create() {
392 create_ok(10.0, 0.1);
393 create_ok(5.0, 1.0);
394 create_ok(0.1, 10.0);
395 create_ok(10.0, 100.0);
396 create_ok(1.0, f64::INFINITY);
397 create_ok(f64::INFINITY, f64::INFINITY);
398 }
399
400 #[test]
401 fn test_bad_create() {
402 test_create_err(1.0, -1.0, ParetoError::ShapeInvalid);
403 test_create_err(-1.0, 1.0, ParetoError::ScaleInvalid);
404 create_err(0.0, 0.0);
405 create_err(-1.0, -1.0);
406 create_err(f64::NAN, 1.0);
407 create_err(1.0, f64::NAN);
408 create_err(f64::NAN, f64::NAN);
409 }
410
411 #[test]
412 fn test_variance() {
413 let variance = |x: Pareto| x.variance().unwrap();
414 test_exact(1.0, 3.0, 0.75, variance);
415 test_absolute(10.0, 10.0, 125.0 / 81.0, 1e-13, variance);
416 }
417
418 #[test]
419 fn test_variance_degen() {
420 test_none(1.0, 1.0, |dist| dist.variance()); }
422
423 #[test]
424 fn test_entropy() {
425 let entropy = |x: Pareto| x.entropy().unwrap();
426 test_exact(0.1, 0.1, -11.0, entropy);
427 test_exact(1.0, 1.0, -2.0, entropy);
428 test_exact(10.0, 10.0, -1.1, entropy);
429 test_exact(3.0, 1.0, -2.0 - 3f64.ln(), entropy);
430 test_exact(1.0, 3.0, -4.0/3.0 + 3f64.ln(), entropy);
431 }
432
433 #[test]
434 fn test_skewness() {
435 let skewness = |x: Pareto| x.skewness().unwrap();
436 test_exact(1.0, 4.0, 5.0*2f64.sqrt(), skewness);
437 test_exact(1.0, 100.0, (707.0/485.0)*2f64.sqrt(), skewness);
438 }
439
440 #[test]
441 fn test_skewness_invalid_shape() {
442 test_none(1.0, 3.0, |dist| dist.skewness());
443 }
444
445 #[test]
446 fn test_mode() {
447 let mode = |x: Pareto| x.mode().unwrap();
448 test_exact(0.1, 1.0, 0.1, mode);
449 test_exact(2.0, 1.0, 2.0, mode);
450 test_exact(10.0, f64::INFINITY, 10.0, mode);
451 test_exact(f64::INFINITY, 1.0, f64::INFINITY, mode);
452 }
453
454 #[test]
455 fn test_median() {
456 let median = |x: Pareto| x.median();
457 test_exact(0.1, 0.1, 102.4, median);
458 test_exact(1.0, 1.0, 2.0, median);
459 test_exact(10.0, 10.0, 10.0*2f64.powf(0.1), median);
460 test_exact(3.0, 0.5, 12.0, median);
461 test_exact(10.0, f64::INFINITY, 10.0, median);
462 }
463
464 #[test]
465 fn test_min_max() {
466 let min = |x: Pareto| x.min();
467 let max = |x: Pareto| x.max();
468 test_exact(0.2, f64::INFINITY, 0.2, min);
469 test_exact(10.0, f64::INFINITY, 10.0, min);
470 test_exact(f64::INFINITY, 1.0, f64::INFINITY, min);
471 test_exact(1.0, 0.1, f64::INFINITY, max);
472 test_exact(3.0, 10.0, f64::INFINITY, max);
473 }
474
475 #[test]
476 fn test_pdf() {
477 let pdf = |arg: f64| move |x: Pareto| x.pdf(arg);
478 test_exact(1.0, 1.0, 0.0, pdf(0.1));
479 test_exact(1.0, 1.0, 1.0, pdf(1.0));
480 test_exact(1.0, 1.0, 4.0/9.0, pdf(1.5));
481 test_exact(1.0, 1.0, 1.0/25.0, pdf(5.0));
482 test_exact(1.0, 1.0, 1.0/2500.0, pdf(50.0));
483 test_exact(1.0, 4.0, 4.0, pdf(1.0));
484 test_exact(1.0, 4.0, 128.0/243.0, pdf(1.5));
485 test_exact(1.0, 4.0, 1.0/78125000.0, pdf(50.0));
486 test_exact(3.0, 2.0, 2.0/3.0, pdf(3.0));
487 test_exact(3.0, 2.0, 18.0/125.0, pdf(5.0));
488 test_absolute(25.0, 100.0, 1.5777218104420236e-30, 1e-50, pdf(50.0));
489 test_absolute(100.0, 25.0, 6.6003546737276816e-6, 1e-16, pdf(150.0));
490 test_exact(1.0, 2.0, 0.0, pdf(f64::INFINITY));
491 }
492
493 #[test]
494 fn test_ln_pdf() {
495 let ln_pdf = |arg: f64| move |x: Pareto| x.ln_pdf(arg);
496 test_exact(1.0, 1.0, f64::NEG_INFINITY, ln_pdf(0.1));
497 test_exact(1.0, 1.0, 0.0, ln_pdf(1.0));
498 test_absolute(1.0, 1.0, 4f64.ln() - 9f64.ln(), 1e-14, ln_pdf(1.5));
499 test_absolute(1.0, 1.0, -(25f64.ln()), 1e-14, ln_pdf(5.0));
500 test_absolute(1.0, 1.0, -(2500f64.ln()), 1e-14, ln_pdf(50.0));
501 test_absolute(1.0, 4.0, 4f64.ln(), 1e-14, ln_pdf(1.0));
502 test_absolute(1.0, 4.0, 128f64.ln() - 243f64.ln(), 1e-14, ln_pdf(1.5));
503 test_absolute(1.0, 4.0, -(78125000f64.ln()), 1e-14, ln_pdf(50.0));
504 test_absolute(3.0, 2.0, 2f64.ln() - 3f64.ln(), 1e-14, ln_pdf(3.0));
505 test_absolute(3.0, 2.0, 18f64.ln() - 125f64.ln(), 1e-14, ln_pdf(5.0));
506 test_absolute(25.0, 100.0, 1.5777218104420236e-30f64.ln(), 1e-12, ln_pdf(50.0));
507 test_absolute(100.0, 25.0, 6.6003546737276816e-6f64.ln(), 1e-12, ln_pdf(150.0));
508 test_exact(1.0, 2.0, f64::NEG_INFINITY, ln_pdf(f64::INFINITY));
509 }
510
511 #[test]
512 fn test_cdf() {
513 let cdf = |arg: f64| move |x: Pareto| x.cdf(arg);
514 test_exact(0.1, 0.1, 0.0, cdf(0.1));
515 test_exact(1.0, 1.0, 0.0, cdf(1.0));
516 test_exact(5.0, 5.0, 0.0, cdf(2.0));
517 test_exact(7.0, 7.0, 0.9176457, cdf(10.0));
518 test_exact(10.0, 10.0, 50700551.0/60466176.0, cdf(12.0));
519 test_exact(5.0, 1.0, 0.5, cdf(10.0));
520 test_exact(3.0, 10.0, 1023.0/1024.0, cdf(6.0));
521 test_exact(1.0, 1.0, 1.0, cdf(f64::INFINITY));
522 }
523
524 #[test]
525 fn test_sf() {
526 let sf = |arg: f64| move |x: Pareto| x.sf(arg);
527 test_exact(0.1, 0.1, 1.0, sf(0.1));
528 test_exact(1.0, 1.0, 1.0, sf(1.0));
529 test_exact(5.0, 5.0, 1.0, sf(2.0));
530 test_absolute(7.0, 7.0, 0.08235429999999999, 1e-14, sf(10.0));
531 test_absolute(10.0, 10.0, 0.16150558288984573, 1e-14, sf(12.0));
532 test_exact(5.0, 1.0, 0.5, sf(10.0));
533 test_absolute(3.0, 10.0, 0.0009765625, 1e-14, sf(6.0));
534 test_exact(1.0, 1.0, 0.0, sf(f64::INFINITY));
535 }
536
537 #[test]
538 fn test_inverse_cdf() {
539 let func = |arg: f64| move |x: Pareto| x.inverse_cdf(x.cdf(arg));
540 test_exact(0.1, 0.1, 0.1, func(0.1));
541 test_exact(1.0, 1.0, 1.0, func(1.0));
542 test_exact(7.0, 7.0, 10.0, func(10.0));
543 test_exact(10.0, 10.0, 12.0, func(12.0));
544 test_exact(5.0, 1.0, 10.0, func(10.0));
545 test_exact(3.0, 10.0, 6.0, func(6.0));
546 }
547
548 #[test]
549 fn test_continuous() {
550 test::check_continuous_distribution(&create_ok(1.0, 10.0), 1.0, 10.0);
551 test::check_continuous_distribution(&create_ok(0.1, 2.0), 0.1, 100.0);
552 }
553}