@@ -433,7 +433,7 @@ pub struct UniformInt<X> {
433
433
}
434
434
435
435
macro_rules! uniform_int_impl {
436
- ( $ty: ty, $unsigned: ident, $u_large: ident) => {
436
+ ( $ty: ty, $unsigned: ident, $u_large: ident, $u_extra_large : ident ) => {
437
437
impl SampleUniform for $ty {
438
438
type Sampler = UniformInt <$ty>;
439
439
}
@@ -536,9 +536,8 @@ macro_rules! uniform_int_impl {
536
536
"UniformSampler::sample_single_inclusive: low > high"
537
537
) ;
538
538
let range = high. wrapping_sub( low) . wrapping_add( 1 ) as $unsigned as $u_large;
539
- // If the above resulted in wrap-around to 0, the range is $ty::MIN..=$ty::MAX,
540
- // and any integer will do.
541
539
if range == 0 {
540
+ // Range is MAX+1 (unrepresentable), so we need a special case
542
541
return rng. gen ( ) ;
543
542
}
544
543
@@ -564,21 +563,188 @@ macro_rules! uniform_int_impl {
564
563
}
565
564
}
566
565
}
566
+
567
+ impl UniformInt <$ty> {
568
+ /// Sample single inclusive, using ONeill's method
569
+ #[ inline]
570
+ pub fn sample_single_inclusive_oneill<R : Rng + ?Sized , B1 , B2 >(
571
+ low_b: B1 , high_b: B2 , rng: & mut R ,
572
+ ) -> $ty
573
+ where
574
+ B1 : SampleBorrow <$ty> + Sized ,
575
+ B2 : SampleBorrow <$ty> + Sized ,
576
+ {
577
+ let low = * low_b. borrow( ) ;
578
+ let high = * high_b. borrow( ) ;
579
+ assert!(
580
+ low <= high,
581
+ "UniformSampler::sample_single_inclusive: low > high"
582
+ ) ;
583
+ let range = high. wrapping_sub( low) . wrapping_add( 1 ) as $unsigned as $u_large;
584
+ if range == 0 {
585
+ // Range is MAX+1 (unrepresentable), so we need a special case
586
+ return rng. gen ( ) ;
587
+ }
588
+
589
+ // we use the "Debiased Int Mult (t-opt, m-opt)" rejection sampling method
590
+ // described here https://www.pcg-random.org/posts/bounded-rands.html
591
+ // and here https://github.com/imneme/bounded-rands
592
+
593
+ let ( mut hi, mut lo) = rng. gen :: <$u_large>( ) . wmul( range) ;
594
+ if lo < range {
595
+ let mut threshold = range. wrapping_neg( ) ;
596
+ // this shortcut works best with large ranges
597
+ if threshold >= range {
598
+ threshold -= range;
599
+ if threshold >= range {
600
+ threshold %= range;
601
+ }
602
+ }
603
+ while lo < threshold {
604
+ let ( new_hi, new_lo) = rng. gen :: <$u_large>( ) . wmul( range) ;
605
+ hi = new_hi;
606
+ lo = new_lo;
607
+ }
608
+ }
609
+ low. wrapping_add( hi as $ty)
610
+ }
611
+
612
+ /// Sample single inclusive, using Canon's method
613
+ #[ inline]
614
+ pub fn sample_single_inclusive_canon<R : Rng + ?Sized , B1 , B2 >(
615
+ low_b: B1 , high_b: B2 , rng: & mut R ,
616
+ ) -> $ty
617
+ where
618
+ B1 : SampleBorrow <$ty> + Sized ,
619
+ B2 : SampleBorrow <$ty> + Sized ,
620
+ {
621
+ let low = * low_b. borrow( ) ;
622
+ let high = * high_b. borrow( ) ;
623
+ assert!(
624
+ low <= high,
625
+ "UniformSampler::sample_single_inclusive: low > high"
626
+ ) ;
627
+ let range = high. wrapping_sub( low) . wrapping_add( 1 ) as $unsigned as $u_extra_large;
628
+ if range == 0 {
629
+ // Range is MAX+1 (unrepresentable), so we need a special case
630
+ return rng. gen ( ) ;
631
+ }
632
+
633
+ // generate a sample using a sensible integer type
634
+ let ( mut result, lo_order) = rng. gen :: <$u_extra_large>( ) . wmul( range) ;
635
+
636
+ // if the sample is biased...
637
+ if lo_order > range. wrapping_neg( ) {
638
+ // ...generate a new sample with 64 more bits, enough that bias is undetectable
639
+ let ( new_hi_order, _) =
640
+ ( rng. gen :: <$u_extra_large>( ) ) . wmul( range as $u_extra_large) ;
641
+ // and adjust if needed
642
+ result += lo_order
643
+ . checked_add( new_hi_order as $u_extra_large)
644
+ . is_none( ) as $u_extra_large;
645
+ }
646
+
647
+ low. wrapping_add( result as $ty)
648
+ }
649
+
650
+ /// Sample single inclusive, using Canon's method with Lemire's early-out
651
+ #[ inline]
652
+ pub fn sample_inclusive_canon_lemire<R : Rng + ?Sized , B1 , B2 >(
653
+ low_b: B1 , high_b: B2 , rng: & mut R ,
654
+ ) -> $ty
655
+ where
656
+ B1 : SampleBorrow <$ty> + Sized ,
657
+ B2 : SampleBorrow <$ty> + Sized ,
658
+ {
659
+ let low = * low_b. borrow( ) ;
660
+ let high = * high_b. borrow( ) ;
661
+ assert!(
662
+ low <= high,
663
+ "UniformSampler::sample_single_inclusive: low > high"
664
+ ) ;
665
+ let range = high. wrapping_sub( low) . wrapping_add( 1 ) as $unsigned as $u_extra_large;
666
+ if range == 0 {
667
+ // Range is MAX+1 (unrepresentable), so we need a special case
668
+ return rng. gen ( ) ;
669
+ }
670
+
671
+ // generate a sample using a sensible integer type
672
+ let ( mut result, lo_order) = rng. gen :: <$u_extra_large>( ) . wmul( range) ;
673
+
674
+ // if the sample is biased... (since range won't be changing we can further
675
+ // improve this check with a modulo)
676
+ if lo_order < range. wrapping_neg( ) % range {
677
+ // ...generate a new sample with 64 more bits, enough that bias is undetectable
678
+ let ( new_hi_order, _) =
679
+ ( rng. gen :: <$u_extra_large>( ) ) . wmul( range as $u_extra_large) ;
680
+ // and adjust if needed
681
+ result += lo_order
682
+ . checked_add( new_hi_order as $u_extra_large)
683
+ . is_none( ) as $u_extra_large;
684
+ }
685
+
686
+ low. wrapping_add( result as $ty)
687
+ }
688
+
689
+ /// Sample single inclusive, using the Bitmask method
690
+ #[ inline]
691
+ pub fn sample_single_inclusive_bitmask<R : Rng + ?Sized , B1 , B2 >(
692
+ low_b: B1 , high_b: B2 , rng: & mut R ,
693
+ ) -> $ty
694
+ where
695
+ B1 : SampleBorrow <$ty> + Sized ,
696
+ B2 : SampleBorrow <$ty> + Sized ,
697
+ {
698
+ let low = * low_b. borrow( ) ;
699
+ let high = * high_b. borrow( ) ;
700
+ assert!(
701
+ low <= high,
702
+ "UniformSampler::sample_single_inclusive: low > high"
703
+ ) ;
704
+ let mut range = high. wrapping_sub( low) . wrapping_add( 1 ) as $unsigned as $u_large;
705
+ if range == 0 {
706
+ // Range is MAX+1 (unrepresentable), so we need a special case
707
+ return rng. gen ( ) ;
708
+ }
709
+
710
+ // the old impl use a mix of methods for different integer sizes, we only use
711
+ // the lz method here for a better comparison.
712
+
713
+ let mut mask = $u_large:: max_value( ) ;
714
+ range -= 1 ;
715
+ mask >>= ( range | 1 ) . leading_zeros( ) ;
716
+ loop {
717
+ let x = rng. gen :: <$u_large>( ) & mask;
718
+ if x <= range {
719
+ return low. wrapping_add( x as $ty) ;
720
+ }
721
+ }
722
+ }
723
+ }
567
724
} ;
568
725
}
569
-
570
- uniform_int_impl ! { i8 , u8 , u32 }
571
- uniform_int_impl ! { i16 , u16 , u32 }
572
- uniform_int_impl ! { i32 , u32 , u32 }
573
- uniform_int_impl ! { i64 , u64 , u64 }
574
- uniform_int_impl ! { i128 , u128 , u128 }
575
- uniform_int_impl ! { isize , usize , usize }
576
- uniform_int_impl ! { u8 , u8 , u32 }
577
- uniform_int_impl ! { u16 , u16 , u32 }
578
- uniform_int_impl ! { u32 , u32 , u32 }
579
- uniform_int_impl ! { u64 , u64 , u64 }
580
- uniform_int_impl ! { usize , usize , usize }
581
- uniform_int_impl ! { u128 , u128 , u128 }
726
+ uniform_int_impl ! { i8 , u8 , u32 , u64 }
727
+ uniform_int_impl ! { i16 , u16 , u32 , u64 }
728
+ uniform_int_impl ! { i32 , u32 , u32 , u64 }
729
+ uniform_int_impl ! { i64 , u64 , u64 , u64 }
730
+ uniform_int_impl ! { i128 , u128 , u128 , u128 }
731
+ uniform_int_impl ! { u8 , u8 , u32 , u64 }
732
+ uniform_int_impl ! { u16 , u16 , u32 , u64 }
733
+ uniform_int_impl ! { u32 , u32 , u32 , u64 }
734
+ uniform_int_impl ! { u64 , u64 , u64 , u64 }
735
+ uniform_int_impl ! { u128 , u128 , u128 , u128 }
736
+ #[ cfg( any( target_pointer_width = "16" , target_pointer_width = "32" , ) ) ]
737
+ mod isize_int_impls {
738
+ use super :: * ;
739
+ uniform_int_impl ! { isize , usize , usize , u64 }
740
+ uniform_int_impl ! { usize , usize , usize , u64 }
741
+ }
742
+ #[ cfg( not( any( target_pointer_width = "16" , target_pointer_width = "32" , ) ) ) ]
743
+ mod isize_int_impls {
744
+ use super :: * ;
745
+ uniform_int_impl ! { isize , usize , usize , usize }
746
+ uniform_int_impl ! { usize , usize , usize , usize }
747
+ }
582
748
583
749
#[ cfg( feature = "simd_support" ) ]
584
750
macro_rules! uniform_simd_int_impl {
0 commit comments