scirs2_stats/distributions/geometric.rs
1//! Geometric distribution functions
2//!
3//! This module provides functionality for the Geometric distribution.
4
5use crate::error::{StatsError, StatsResult};
6use crate::sampling::SampleableDistribution;
7use scirs2_core::numeric::{Float, NumCast};
8use scirs2_core::random::prelude::*;
9use scirs2_core::random::{Distribution, Geometric as RandGeometric};
10
11/// Geometric distribution structure
12///
13/// The Geometric distribution is a discrete probability distribution that models
14/// the number of failures before the first success in a sequence of independent
15/// Bernoulli trials, each with success probability p.
16pub struct Geometric<F: Float> {
17 /// Success probability (0 < p ≤ 1)
18 pub p: F,
19 /// Random number generator
20 rand_distr: RandGeometric,
21}
22
23impl<F: Float + NumCast + std::fmt::Display> Geometric<F> {
24 /// Create a new Geometric distribution with given success probability
25 ///
26 /// # Arguments
27 ///
28 /// * `p` - Success probability (0 < p ≤ 1)
29 ///
30 /// # Returns
31 ///
32 /// * A new Geometric distribution instance
33 ///
34 /// # Examples
35 ///
36 /// ```
37 /// use scirs2_stats::distributions::geometric::Geometric;
38 ///
39 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
40 /// ```
41 pub fn new(p: F) -> StatsResult<Self> {
42 // Validate parameters
43 if p <= F::zero() || p > F::one() {
44 return Err(StatsError::DomainError(
45 "Success probability must be between 0 and 1, exclusive of 0".to_string(),
46 ));
47 }
48
49 // Create RNG for Geometric distribution
50 let p_f64 = <f64 as scirs2_core::numeric::NumCast>::from(p).expect("Operation failed");
51 let rand_distr = match RandGeometric::new(p_f64) {
52 Ok(distr) => distr,
53 Err(_) => {
54 return Err(StatsError::ComputationError(
55 "Failed to create Geometric distribution for sampling".to_string(),
56 ))
57 }
58 };
59
60 Ok(Geometric { p, rand_distr })
61 }
62
63 /// Calculate the probability mass function (PMF) at a given point
64 ///
65 /// # Arguments
66 ///
67 /// * `k` - The point at which to evaluate the PMF (number of failures before first success)
68 ///
69 /// # Returns
70 ///
71 /// * The value of the PMF at the given point
72 ///
73 /// # Examples
74 ///
75 /// ```
76 /// use scirs2_stats::distributions::geometric::Geometric;
77 ///
78 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
79 /// let pmf_at_2 = geom.pmf(2.0);
80 /// assert!((pmf_at_2 - 0.147).abs() < 1e-3);
81 /// ```
82 pub fn pmf(&self, k: F) -> F {
83 let zero = F::zero();
84 let one = F::one();
85
86 // Check if k is a non-negative integer
87 if k < zero || !Self::is_integer(k) {
88 return zero;
89 }
90
91 let k_usize = k.to_usize().expect("Operation failed");
92
93 // PMF = p * (1 - p)^k
94 let q = one - self.p; // q = 1 - p
95 self.p * q.powf(F::from(k_usize).expect("Failed to convert to float"))
96 }
97
98 /// Calculate the log of the probability mass function (log-PMF) at a given point
99 ///
100 /// # Arguments
101 ///
102 /// * `k` - The point at which to evaluate the log-PMF (number of failures before first success)
103 ///
104 /// # Returns
105 ///
106 /// * The value of the log-PMF at the given point
107 ///
108 /// # Examples
109 ///
110 /// ```
111 /// use scirs2_stats::distributions::geometric::Geometric;
112 ///
113 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
114 /// let log_pmf_at_2 = geom.log_pmf(2.0);
115 /// assert!((log_pmf_at_2 - (-1.9164)) < 1e-4);
116 /// ```
117 pub fn log_pmf(&self, k: F) -> F {
118 let zero = F::zero();
119 let neg_infinity = F::neg_infinity();
120
121 // Check if k is a non-negative integer
122 if k < zero || !Self::is_integer(k) {
123 return neg_infinity;
124 }
125
126 let k_usize = k.to_usize().expect("Operation failed");
127 let k_f = F::from(k_usize).expect("Failed to convert to float");
128
129 // log-PMF = ln(p) + k * ln(1 - p)
130 let one = F::one();
131 let q = one - self.p; // q = 1 - p
132
133 self.p.ln() + k_f * q.ln()
134 }
135
136 /// Calculate the cumulative distribution function (CDF) at a given point
137 ///
138 /// # Arguments
139 ///
140 /// * `k` - The point at which to evaluate the CDF
141 ///
142 /// # Returns
143 ///
144 /// * The value of the CDF at the given point
145 ///
146 /// # Examples
147 ///
148 /// ```
149 /// use scirs2_stats::distributions::geometric::Geometric;
150 ///
151 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
152 /// let cdf_at_2 = geom.cdf(2.0);
153 /// assert!((cdf_at_2 - 0.657).abs() < 1e-3);
154 /// ```
155 pub fn cdf(&self, k: F) -> F {
156 let zero = F::zero();
157 let one = F::one();
158
159 // CDF = 0 for k < 0
160 if k < zero {
161 return zero;
162 }
163
164 // Floor k to handle non-integer values
165 let k_floor = k.floor();
166 let k_int = k_floor.to_usize().expect("Operation failed");
167
168 // Calculate CDF = 1 - (1 - p)^(k+1)
169 let q = one - self.p; // q = 1 - p
170 one - q.powf(F::from(k_int + 1).expect("Failed to convert to float"))
171 }
172
173 /// Inverse of the cumulative distribution function (quantile function)
174 ///
175 /// # Arguments
176 ///
177 /// * `p` - Probability value (between 0 and 1)
178 ///
179 /// # Returns
180 ///
181 /// * The value k such that CDF(k) = p
182 ///
183 /// # Examples
184 ///
185 /// ```
186 /// use scirs2_stats::distributions::geometric::Geometric;
187 ///
188 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
189 /// let quant = geom.ppf(0.5).expect("Operation failed");
190 /// assert_eq!(quant, 1.0);
191 /// ```
192 pub fn ppf(&self, pval: F) -> StatsResult<F> {
193 let zero = F::zero();
194 let one = F::one();
195
196 if pval < zero || pval > one {
197 return Err(StatsError::DomainError(
198 "Probability must be between 0 and 1".to_string(),
199 ));
200 }
201
202 // Special cases
203 if pval <= zero {
204 return Ok(zero);
205 }
206 if pval >= one {
207 return Ok(F::infinity());
208 }
209
210 // Handle case when p = 1.0 separately
211 if self.p == one {
212 return Ok(zero); // When p=1, X is always 0
213 }
214
215 // For actual calculation, we need to find the smallest k where CDF(k) >= p_val
216 // We could use the formula: k = ceiling(log(1-p_val) / log(1-p)) - 1
217 // But we'll use a simpler iterative approach instead
218
219 // Calculate the quantile manually to avoid formula issues
220 let mut k = zero;
221 while self.cdf(k) < pval {
222 k = k + one;
223 }
224
225 Ok(k)
226 }
227
228 /// Generate random samples from the distribution
229 ///
230 /// # Arguments
231 ///
232 /// * `size` - Number of samples to generate
233 ///
234 /// # Returns
235 ///
236 /// * Vector of random samples
237 ///
238 /// # Examples
239 ///
240 /// ```
241 /// use scirs2_stats::distributions::geometric::Geometric;
242 ///
243 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
244 /// let samples = geom.rvs(10).expect("Operation failed");
245 /// assert_eq!(samples.len(), 10);
246 /// ```
247 pub fn rvs(&self, size: usize) -> StatsResult<Vec<F>> {
248 let mut rng = thread_rng();
249 let mut samples = Vec::with_capacity(size);
250
251 for _ in 0..size {
252 // Generate random Geometric sample
253 let sample = self.rand_distr.sample(&mut rng);
254 let sample_f = F::from(sample).expect("Failed to convert to float");
255 samples.push(sample_f);
256 }
257
258 Ok(samples)
259 }
260
261 /// Calculate the mean of the distribution
262 ///
263 /// # Returns
264 ///
265 /// * The mean of the distribution
266 ///
267 /// # Examples
268 ///
269 /// ```
270 /// use scirs2_stats::distributions::geometric::Geometric;
271 ///
272 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
273 /// let mean = geom.mean();
274 /// assert!((mean - 2.333333).abs() < 1e-6);
275 /// ```
276 pub fn mean(&self) -> F {
277 // Mean = (1-p)/p
278 let one = F::one();
279 let q = one - self.p; // q = 1 - p
280 q / self.p
281 }
282
283 /// Calculate the variance of the distribution
284 ///
285 /// # Returns
286 ///
287 /// * The variance of the distribution
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// use scirs2_stats::distributions::geometric::Geometric;
293 ///
294 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
295 /// let variance = geom.var();
296 /// assert!((variance - 7.777778).abs() < 1e-6);
297 /// ```
298 pub fn var(&self) -> F {
299 // Variance = (1-p)/p^2
300 let one = F::one();
301 let q = one - self.p; // q = 1 - p
302 q / (self.p * self.p)
303 }
304
305 /// Calculate the standard deviation of the distribution
306 ///
307 /// # Returns
308 ///
309 /// * The standard deviation of the distribution
310 ///
311 /// # Examples
312 ///
313 /// ```
314 /// use scirs2_stats::distributions::geometric::Geometric;
315 ///
316 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
317 /// let std_dev = geom.std();
318 /// assert!((std_dev - 2.788867).abs() < 1e-6);
319 /// ```
320 pub fn std(&self) -> F {
321 // Std = sqrt(variance)
322 self.var().sqrt()
323 }
324
325 /// Calculate the skewness of the distribution
326 ///
327 /// # Returns
328 ///
329 /// * The skewness of the distribution
330 ///
331 /// # Examples
332 ///
333 /// ```
334 /// use scirs2_stats::distributions::geometric::Geometric;
335 ///
336 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
337 /// let skewness = geom.skewness();
338 /// assert!((skewness - 2.0318891).abs() < 1e-6);
339 /// ```
340 pub fn skewness(&self) -> F {
341 // Skewness = (2-p) / sqrt(1-p)
342 let one = F::one();
343 let two = F::from(2.0).expect("Failed to convert constant to float");
344
345 let q = one - self.p; // q = 1 - p
346 (two - self.p) / q.sqrt()
347 }
348
349 /// Calculate the kurtosis of the distribution
350 ///
351 /// # Returns
352 ///
353 /// * The excess kurtosis of the distribution
354 ///
355 /// # Examples
356 ///
357 /// ```
358 /// use scirs2_stats::distributions::geometric::Geometric;
359 ///
360 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
361 /// let kurtosis = geom.kurtosis();
362 /// assert!((kurtosis - 6.9) < 1e-1);
363 /// ```
364 pub fn kurtosis(&self) -> F {
365 // Excess Kurtosis = 6 + p^2/(1-p)
366 let one = F::one();
367 let six = F::from(6.0).expect("Failed to convert constant to float");
368
369 let q = one - self.p; // q = 1 - p
370 six + (self.p * self.p) / q
371 }
372
373 /// Calculate the entropy of the distribution
374 ///
375 /// # Returns
376 ///
377 /// * The entropy value
378 ///
379 /// # Examples
380 ///
381 /// ```
382 /// use scirs2_stats::distributions::geometric::Geometric;
383 ///
384 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
385 /// let entropy = geom.entropy();
386 /// assert!((entropy - 2.937588) < 1e-6);
387 /// ```
388 pub fn entropy(&self) -> F {
389 // Entropy = -[(1-p)*ln(1-p) + p*ln(p)] / p
390 let one = F::one();
391 let q = one - self.p; // q = 1 - p
392
393 -(q * q.ln() + self.p * self.p.ln()) / self.p
394 }
395
396 /// Calculate the median of the distribution
397 ///
398 /// # Returns
399 ///
400 /// * The median of the distribution
401 ///
402 /// # Examples
403 ///
404 /// ```
405 /// use scirs2_stats::distributions::geometric::Geometric;
406 ///
407 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
408 /// let median = geom.median();
409 /// assert_eq!(median, 1.0);
410 /// ```
411 pub fn median(&self) -> F {
412 // Median = ceiling(ln(0.5)/ln(1-p)) - 1
413 let half = F::from(0.5).expect("Failed to convert constant to float");
414 let one = F::one();
415 let q = one - self.p; // q = 1 - p
416
417 (half.ln() / q.ln()).ceil() - one
418 }
419
420 /// Calculate the mode of the distribution
421 ///
422 /// # Returns
423 ///
424 /// * The mode of the distribution
425 ///
426 /// # Examples
427 ///
428 /// ```
429 /// use scirs2_stats::distributions::geometric::Geometric;
430 ///
431 /// let geom = Geometric::new(0.3f64).expect("Operation failed");
432 /// let mode = geom.mode();
433 /// assert_eq!(mode, 0.0);
434 /// ```
435 pub fn mode(&self) -> F {
436 // Mode = 0 (always for geometric distribution)
437 F::zero()
438 }
439
440 // Helper method to check if a value is an integer
441 fn is_integer(value: F) -> bool {
442 value == value.floor()
443 }
444}
445
446/// Create a Geometric distribution with the given parameter.
447///
448/// This is a convenience function to create a Geometric distribution with
449/// the given success probability.
450///
451/// # Arguments
452///
453/// * `p` - Success probability (0 < p ≤ 1)
454///
455/// # Returns
456///
457/// * A Geometric distribution object
458///
459/// # Examples
460///
461/// ```
462/// use scirs2_stats::distributions;
463///
464/// let g = distributions::geom(0.3f64).expect("Operation failed");
465/// let pmf_at_2 = g.pmf(2.0);
466/// assert!((pmf_at_2 - 0.147).abs() < 1e-3);
467/// ```
468#[allow(dead_code)]
469pub fn geom<F>(p: F) -> StatsResult<Geometric<F>>
470where
471 F: Float + NumCast + std::fmt::Display,
472{
473 Geometric::new(p)
474}
475
476/// Implementation of SampleableDistribution for Geometric
477impl<F: Float + NumCast + std::fmt::Display> SampleableDistribution<F> for Geometric<F> {
478 fn rvs(&self, size: usize) -> StatsResult<Vec<F>> {
479 self.rvs(size)
480 }
481}
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486 use approx::assert_relative_eq;
487
488 #[test]
489 fn test_geometric_creation() {
490 // Valid p values
491 let geom1 = Geometric::new(0.3).expect("Operation failed");
492 assert_eq!(geom1.p, 0.3);
493
494 let geom2 = Geometric::new(1.0).expect("Operation failed");
495 assert_eq!(geom2.p, 1.0);
496
497 // Invalid p values
498 assert!(Geometric::<f64>::new(0.0).is_err());
499 assert!(Geometric::<f64>::new(-0.1).is_err());
500 assert!(Geometric::<f64>::new(1.1).is_err());
501 }
502
503 #[test]
504 fn test_geometric_pmf() {
505 // Geometric(0.3)
506 let geom = Geometric::new(0.3).expect("Operation failed");
507
508 // PMF values for different k
509 assert_relative_eq!(geom.pmf(0.0), 0.3, epsilon = 1e-10);
510 assert_relative_eq!(geom.pmf(1.0), 0.3 * 0.7, epsilon = 1e-10);
511 assert_relative_eq!(geom.pmf(2.0), 0.3 * 0.7 * 0.7, epsilon = 1e-10);
512 assert_relative_eq!(geom.pmf(3.0), 0.3 * 0.7 * 0.7 * 0.7, epsilon = 1e-10);
513
514 // PMF should be 0 for negative k
515 assert_eq!(geom.pmf(-1.0), 0.0);
516
517 // PMF should be 0 for non-integer k
518 assert_eq!(geom.pmf(1.5), 0.0);
519
520 // Special case: Geometric(1.0)
521 let geom_p1 = Geometric::new(1.0).expect("Operation failed");
522 assert_eq!(geom_p1.pmf(0.0), 1.0);
523 assert_eq!(geom_p1.pmf(1.0), 0.0);
524 assert_eq!(geom_p1.pmf(2.0), 0.0);
525 }
526
527 #[test]
528 fn test_geometric_log_pmf() {
529 // Geometric(0.3)
530 let geom = Geometric::new(0.3).expect("Operation failed");
531
532 // Check log_pmf against pmf
533 for k in 0..5 {
534 let k_f = k as f64;
535 let pmf = geom.pmf(k_f);
536 let log_pmf = geom.log_pmf(k_f);
537
538 if pmf > 0.0 {
539 assert_relative_eq!(log_pmf, pmf.ln(), epsilon = 1e-10);
540 } else {
541 assert!(log_pmf.is_infinite() && log_pmf.is_sign_negative());
542 }
543 }
544
545 // log_pmf should be -infinity for negative k or non-integer k
546 assert!(geom.log_pmf(-1.0).is_infinite() && geom.log_pmf(-1.0).is_sign_negative());
547 assert!(geom.log_pmf(1.5).is_infinite() && geom.log_pmf(1.5).is_sign_negative());
548 }
549
550 #[test]
551 fn test_geometric_cdf() {
552 // Geometric(0.3)
553 let geom = Geometric::new(0.3).expect("Operation failed");
554
555 // CDF for k = 0 should be 1 - (1-p)^1 = 1 - 0.7^1 = 0.3
556 assert_relative_eq!(geom.cdf(0.0), 0.3, epsilon = 1e-10);
557
558 // CDF for k = 1 should be 1 - (1-p)^2 = 1 - 0.7^2 = 1 - 0.49 = 0.51
559 assert_relative_eq!(geom.cdf(1.0), 0.51, epsilon = 1e-10);
560
561 // CDF for k = 2 should be 1 - (1-p)^3 = 1 - 0.7^3 = 1 - 0.343 = 0.657
562 assert_relative_eq!(geom.cdf(2.0), 0.657, epsilon = 1e-10);
563
564 // CDF for k = 3 should be 1 - (1-p)^4 = 1 - 0.7^4 = 1 - 0.2401 = 0.7599
565 assert_relative_eq!(geom.cdf(3.0), 0.7599, epsilon = 1e-10);
566
567 // CDF should be 0 for k < 0
568 assert_eq!(geom.cdf(-1.0), 0.0);
569
570 // Non-integer k should use floor of k
571 assert_relative_eq!(geom.cdf(2.5), geom.cdf(2.0), epsilon = 1e-10);
572
573 // Special case: Geometric(1.0)
574 let geom_p1 = Geometric::new(1.0).expect("Operation failed");
575 assert_eq!(geom_p1.cdf(0.0), 1.0);
576 assert_eq!(geom_p1.cdf(1.0), 1.0);
577 }
578
579 #[test]
580 fn test_geometric_ppf() {
581 // Geometric(0.3)
582 let geom = Geometric::new(0.3).expect("Operation failed");
583
584 // Verify the ppf against the cdf
585 for k in 0..10 {
586 let k_f = k as f64;
587 let cdf_k = geom.cdf(k_f);
588
589 // For any value p such that CDF(k-1) < p <= CDF(k),
590 // the inverse CDF (ppf) should return k
591 if k > 0 {
592 let cdf_k_minus_1 = geom.cdf(k_f - 1.0);
593 let p_mid = (cdf_k + cdf_k_minus_1) / 2.0; // A value between CDF(k-1) and CDF(k)
594 assert_eq!(geom.ppf(p_mid).expect("Operation failed"), k_f);
595 } else {
596 // For k=0, just test a small value of p
597 assert_eq!(geom.ppf(cdf_k / 2.0).expect("Operation failed"), k_f);
598 }
599 }
600
601 // Test edge cases
602 assert_eq!(geom.ppf(0.0).expect("Operation failed"), 0.0);
603 assert!(geom.ppf(1.0).expect("Operation failed").is_infinite()); // For p=1, the result should be infinity
604
605 // The PPF should be the inverse of the CDF
606 let test_values = vec![0.1, 0.3, 0.5, 0.7, 0.9];
607 for p in test_values {
608 let k = geom.ppf(p).expect("Operation failed");
609
610 // Special case for p = 1.0 which gives infinity
611 if p == 1.0 {
612 continue;
613 }
614
615 // For discrete distributions, we check that F(k-1) < p ≤ F(k)
616 if k > 0.0 {
617 let k_minus_1 = k - 1.0;
618 let cdf_k_minus_1 = geom.cdf(k_minus_1);
619 let cdf_k = geom.cdf(k);
620 assert!(cdf_k_minus_1 < p && p <= cdf_k);
621 } else {
622 // For k = 0, just check that p ≤ F(0)
623 let cdf_k = geom.cdf(k);
624 assert!(p <= cdf_k);
625 }
626 }
627
628 // Invalid probability values
629 assert!(geom.ppf(-0.1).is_err());
630 assert!(geom.ppf(1.1).is_err());
631 }
632
633 #[test]
634 fn test_geometric_rvs() {
635 // Geometric(0.3)
636 let geom = Geometric::new(0.3).expect("Operation failed");
637
638 // Generate samples
639 let samples = geom.rvs(100).expect("Operation failed");
640
641 // Check number of samples
642 assert_eq!(samples.len(), 100);
643
644 // All samples should be non-negative integers
645 for &s in &samples {
646 assert!(s >= 0.0);
647 assert_eq!(s, s.floor());
648 }
649
650 // Mean should be approximately (1-p)/p = 0.7/0.3 = 2.333...
651 let sum: f64 = samples.iter().sum();
652 let mean = sum / samples.len() as f64;
653
654 // The mean could deviate somewhat due to randomness
655 assert!(mean > 1.0 && mean < 4.0);
656 }
657
658 #[test]
659 fn test_geometric_moments() {
660 // Test with p = 0.3
661 let geom = Geometric::new(0.3).expect("Operation failed");
662
663 // Mean = (1-p)/p = 0.7/0.3 = 2.333...
664 assert_relative_eq!(geom.mean(), 2.333333, epsilon = 1e-6);
665
666 // Variance = (1-p)/p^2 = 0.7/(0.3^2) = 0.7/0.09 = 7.777...
667 assert_relative_eq!(geom.var(), 7.777778, epsilon = 1e-6);
668
669 // Standard deviation = sqrt(variance) = sqrt(7.777...) ≈ 2.789...
670 assert_relative_eq!(geom.std(), 2.788867, epsilon = 1e-6);
671
672 // Skewness = (2-p)/sqrt(1-p) = (2-0.3)/sqrt(0.7) = 1.7/sqrt(0.7) ≈ 2.031...
673 assert_relative_eq!(geom.skewness(), 2.031889, epsilon = 1e-6);
674
675 // Kurtosis = 6 + p^2/(1-p) = 6 + 0.3^2/0.7 = 6 + 0.09/0.7 ≈ 6.129...
676 assert_relative_eq!(geom.kurtosis(), 6.128571, epsilon = 1e-6);
677
678 // Entropy is more complex, we'll just check it's positive
679 assert!(geom.entropy() > 0.0);
680
681 // Mode is always 0 for geometric distribution
682 assert_eq!(geom.mode(), 0.0);
683
684 // Median = ceiling(ln(0.5)/ln(1-p)) - 1 = ceiling(ln(0.5)/ln(0.7)) - 1
685 // ln(0.5) ≈ -0.693, ln(0.7) ≈ -0.357, so ln(0.5)/ln(0.7) ≈ 1.94, ceiling(1.94) = 2, 2-1 = 1
686 assert_eq!(geom.median(), 1.0);
687 }
688
689 #[test]
690 fn test_geometric_edge_cases() {
691 // Test with p = 1.0 (special case, only k=0 has non-zero probability)
692 let geom_p1 = Geometric::new(1.0).expect("Operation failed");
693
694 // PMF is 1 for k=0, 0 elsewhere
695 assert_eq!(geom_p1.pmf(0.0), 1.0);
696 assert_eq!(geom_p1.pmf(1.0), 0.0);
697
698 // CDF is 1 for all k >= 0
699 assert_eq!(geom_p1.cdf(0.0), 1.0);
700 assert_eq!(geom_p1.cdf(1.0), 1.0);
701
702 // Mean = (1-p)/p = 0/1 = 0
703 assert_eq!(geom_p1.mean(), 0.0);
704
705 // Variance = (1-p)/p^2 = 0/1 = 0
706 assert_eq!(geom_p1.var(), 0.0);
707
708 // PPF is 0 for all p < 1, infinity for p=1
709 assert_eq!(geom_p1.ppf(0.5).expect("Operation failed"), 0.0);
710 assert!(geom_p1.ppf(1.0).expect("Operation failed").is_infinite());
711 }
712}