Skip to content

Commit be28239

Browse files
authored
Merge pull request #41 from mstoeckl/zipf-edge-case
Avoid hangs and debug asserts on invalid parameters for Zipf
2 parents bfcb948 + 9a8000b commit be28239

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- Use `direct-minimal-versions` (#38)
2222
- Fix panic in `FisherF::new` on almost zero parameters (#39)
2323
- Fix panic in `NormalInverseGaussian::new` with very large `alpha`; this is a Value-breaking change (#40)
24+
- Fix hang and debug assertion in `Zipf::new` on invalid parameters (#41)
2425
- Fix panic in `Binomial::sample` with `n ≥ 2^63`; this is a Value-breaking change (#43)
2526
- Error instead of producing `-inf` output for `Exp` when `lambda` is `-0.0` (#44)
2627

src/zipf.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,20 @@ where
6666
/// Error type returned from [`Zipf::new`].
6767
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
6868
pub enum Error {
69-
/// `s < 0` or `nan`.
69+
/// `s < 0` or `s` is `nan`
7070
STooSmall,
71-
/// `n < 1`.
71+
/// `n < 1` or `n` is `nan`
7272
NTooSmall,
73+
/// `n = inf` and `s <= 1`
74+
IllDefined,
7375
}
7476

7577
impl fmt::Display for Error {
7678
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7779
f.write_str(match self {
7880
Error::STooSmall => "s < 0 or is NaN in Zipf distribution",
79-
Error::NTooSmall => "n < 1 in Zipf distribution",
81+
Error::NTooSmall => "n < 1 or is NaN in Zipf distribution",
82+
Error::IllDefined => "n = inf and s <= 1 in Zipf distribution",
8083
})
8184
}
8285
}
@@ -100,17 +103,22 @@ where
100103
if !(s >= F::zero()) {
101104
return Err(Error::STooSmall);
102105
}
103-
if n < F::one() {
106+
if !(n >= F::one()) {
104107
return Err(Error::NTooSmall);
105108
}
109+
if n.is_infinite() && s <= F::one() {
110+
return Err(Error::IllDefined);
111+
}
106112
let q = if s != F::one() {
107113
// Make sure to calculate the division only once.
108114
F::one() / (F::one() - s)
109115
} else {
110116
// This value is never used.
111117
F::zero()
112118
};
113-
let t = if s != F::one() {
119+
let t = if s == F::infinity() {
120+
F::one()
121+
} else if s != F::one() {
114122
(n.powf(F::one() - s) - s) * q
115123
} else {
116124
F::one() + n.ln()
@@ -220,6 +228,16 @@ mod tests {
220228
// TODO: verify that this is a uniform distribution
221229
}
222230

231+
#[test]
232+
fn zipf_sample_s_inf() {
233+
let d = Zipf::new(10., f64::infinity()).unwrap();
234+
let mut rng = crate::test::rng(2);
235+
for _ in 0..1000 {
236+
let r = d.sample(&mut rng);
237+
assert!(r == 1.);
238+
}
239+
}
240+
223241
#[test]
224242
fn zipf_sample_large_n() {
225243
let d = Zipf::new(f64::MAX, 1.5).unwrap();

0 commit comments

Comments
 (0)