@@ -174,3 +174,70 @@ impl Drop for TestRng {
174174 println ! ( "Called TestRng with seed {} {} times" , self . seed, self . calls) ;
175175 }
176176}
177+
178+ /// This impl is Lemire's method with approximate zone. It reproduces rand 0.8's
179+ /// `rng.gen_range(low..=high)` for `usize` on 64-bit. It mustn't be modified
180+ /// to maintain backwards compatibility. It is a direct reimplementation of
181+ /// `sample_single_inclusive` for a concrete type (`usize`) an inlined widening multiply
182+ /// from https://github.com/rust-random/rand/blob/937320c/src/distributions/uniform.rs.
183+ pub fn gen_range_inclusive_legacy ( low : usize , high : usize , rng : & mut impl Rng ) -> usize {
184+ debug_assert ! ( low <= high) ;
185+
186+ let range = high. wrapping_sub ( low) . wrapping_add ( 1 ) ;
187+ // The range is 0..=usize::MAX.
188+ if range == 0 {
189+ return rng. random :: < u64 > ( ) as usize ;
190+ }
191+
192+ // Approximate zone: conservative but avoids division.
193+ let zone = ( range << range. leading_zeros ( ) ) . wrapping_sub ( 1 ) ;
194+
195+ loop {
196+ let v = rng. next_u64 ( ) as usize ;
197+ // Widening multiply: v * range as u128, split into (hi, lo).
198+ let wide = ( v as u128 ) * ( range as u128 ) ;
199+ let hi = ( wide >> 64 ) as usize ;
200+ let lo = wide as usize ;
201+ if lo <= zone {
202+ return low. wrapping_add ( hi) ;
203+ }
204+ }
205+ }
206+
207+ /// This impl reproduces rand 0.8's `slice.choose_weighted(rng, weight_fn)` for u16 weights.
208+ /// It mustn't be modified to maintain backwards compatibility. It is a direct, "collapsed"
209+ /// reimplementation of `WeightedIndex::new(weights).sample(rng)` specifically for `u16` weights
210+ /// from https://github.com/rust-random/rand/blob/937320c/src/distributions/weighted_index.rs.
211+ pub fn choose_weighted_legacy < ' a , T , R : Rng > ( slice : & ' a [ T ] , weight_fn : impl Fn ( & T ) -> u16 , rng : & mut R ) -> & ' a T {
212+ // WeightedIndex::new.
213+ let mut iter = slice. iter ( ) ;
214+ let first = iter. next ( ) . unwrap ( ) ;
215+ let mut total: u16 = weight_fn ( first) ;
216+ let mut cumulative: Vec < u16 > = Vec :: with_capacity ( slice. len ( ) - 1 ) ;
217+ for item in iter {
218+ cumulative. push ( total) ;
219+ total += weight_fn ( item) ;
220+ }
221+ assert ! ( total > 0 ) ;
222+
223+ // Uniform::new(0u16, total) -> new_inclusive(0, total - 1)
224+ // range as u16, then promoted to u32 for zone math.
225+ let range = total as u32 ;
226+ let ints_to_reject = ( u32:: MAX - range + 1 ) % range;
227+ let zone = u32:: MAX - ints_to_reject;
228+
229+ // Uniform::sample (exact Lemire, u32 sample space).
230+ let chosen: u16 = loop {
231+ let v = rng. next_u32 ( ) ;
232+ let wide = ( v as u64 ) * ( range as u64 ) ;
233+ let hi = ( wide >> 32 ) as u32 ;
234+ let lo = wide as u32 ;
235+ if lo <= zone {
236+ break hi as u16 ;
237+ }
238+ } ;
239+
240+ // Binary search (partition_point).
241+ let idx = cumulative. partition_point ( |w| * w <= chosen) ;
242+ & slice[ idx]
243+ }
0 commit comments