Skip to content

Commit f25c24e

Browse files
authored
Merge pull request #427 from sfackler/duration-sample
Add a SampleUniform implementation for Duration
2 parents aaaf03f + 561e2ce commit f25c24e

File tree

1 file changed

+107
-0
lines changed

1 file changed

+107
-0
lines changed

src/distributions/uniform.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
// except according to those terms.
1010

1111
//! A distribution uniformly generating numbers within a given range.
12+
#[cfg(feature = "std")]
13+
use std::time::Duration;
1214

1315
use Rng;
1416
use distributions::Distribution;
@@ -494,6 +496,91 @@ macro_rules! uniform_float_impl {
494496
uniform_float_impl! { f32, 32 - 23, next_u32 }
495497
uniform_float_impl! { f64, 64 - 52, next_u64 }
496498

499+
/// Implementation of `UniformImpl` for `Duration`.
500+
///
501+
/// Unless you are implementing `UniformImpl` for your own types, this type should
502+
/// not be used directly, use `Uniform` instead.
503+
#[cfg(feature = "std")]
504+
#[derive(Clone, Copy, Debug)]
505+
pub struct UniformDuration {
506+
offset: Duration,
507+
mode: UniformDurationMode,
508+
}
509+
510+
#[cfg(feature = "std")]
511+
#[derive(Debug, Copy, Clone)]
512+
enum UniformDurationMode {
513+
Small {
514+
nanos: Uniform<u64>,
515+
},
516+
Large {
517+
size: Duration,
518+
secs: Uniform<u64>,
519+
}
520+
}
521+
522+
#[cfg(feature = "std")]
523+
impl SampleUniform for Duration {
524+
type Impl = UniformDuration;
525+
}
526+
527+
#[cfg(feature = "std")]
528+
impl UniformImpl for UniformDuration {
529+
type X = Duration;
530+
531+
#[inline]
532+
fn new(low: Duration, high: Duration) -> UniformDuration {
533+
UniformDuration::new_inclusive(low, high - Duration::new(0, 1))
534+
}
535+
536+
#[inline]
537+
fn new_inclusive(low: Duration, high: Duration) -> UniformDuration {
538+
let size = high - low;
539+
let nanos = size
540+
.as_secs()
541+
.checked_mul(1_000_000_000)
542+
.and_then(|n| n.checked_add(size.subsec_nanos() as u64));
543+
544+
let mode = match nanos {
545+
Some(nanos) => {
546+
UniformDurationMode::Small {
547+
nanos: Uniform::new_inclusive(0, nanos),
548+
}
549+
}
550+
None => {
551+
UniformDurationMode::Large {
552+
size: size,
553+
secs: Uniform::new_inclusive(0, size.as_secs()),
554+
}
555+
}
556+
};
557+
558+
UniformDuration {
559+
mode,
560+
offset: low,
561+
}
562+
}
563+
564+
#[inline]
565+
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Duration {
566+
let d = match self.mode {
567+
UniformDurationMode::Small { nanos } => {
568+
let nanos = nanos.sample(rng);
569+
Duration::new(nanos / 1_000_000_000, (nanos % 1_000_000_000) as u32)
570+
}
571+
UniformDurationMode::Large { size, secs } => {
572+
loop {
573+
let d = Duration::new(secs.sample(rng), rng.gen_range(0, 1_000_000_000));
574+
if d <= size {
575+
break d;
576+
}
577+
}
578+
}
579+
};
580+
581+
self.offset + d
582+
}
583+
}
497584

498585
#[cfg(test)]
499586
mod tests {
@@ -601,6 +688,26 @@ mod tests {
601688

602689
t!(f32, f64)
603690
}
691+
692+
#[test]
693+
#[cfg(feature = "std")]
694+
fn test_durations() {
695+
use std::time::Duration;
696+
697+
let mut rng = ::test::rng(253);
698+
699+
let v = &[(Duration::new(10, 50000), Duration::new(100, 1234)),
700+
(Duration::new(0, 100), Duration::new(1, 50)),
701+
(Duration::new(0, 0), Duration::new(u64::max_value(), 999_999_999))];
702+
for &(low, high) in v.iter() {
703+
let my_uniform = Uniform::new(low, high);
704+
for _ in 0..1000 {
705+
let v = rng.sample(my_uniform);
706+
assert!(low <= v && v < high);
707+
}
708+
}
709+
}
710+
604711
#[test]
605712
fn test_custom_uniform() {
606713
#[derive(Clone, Copy, PartialEq, PartialOrd)]

0 commit comments

Comments
 (0)