1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
use rand_core::{RngCore, SeedableRng};
use rand_pcg::Pcg64Mcg as Pcg;

pub fn get_random_proposer_index(seed: u64, weights: &[u64], weight_sum: u64) -> usize {
    let tmp = u64::max_value() / weight_sum;
    let mut rng = Pcg::seed_from_u64(seed);
    let mut acc = 0u64;
    let mut random_digit = rng.next_u64();
    while random_digit >= weight_sum * tmp {
        random_digit = rng.next_u64();
    }

    for (index, weight) in weights.iter().enumerate() {
        acc += *weight;
        if random_digit < acc * tmp {
            return index;
        }
    }
    0
}

#[cfg(test)]
mod test {
    use super::get_random_proposer_index;

    #[test]
    fn test_rand_proposer() {
        let weights = vec![1u64, 1u64, 1u64, 1u64];
        let ans = vec![3, 2, 0, 0, 3, 1, 2, 2, 0];

        for seed in 1..10 {
            let res = get_random_proposer_index(seed as u64, &weights, 4);
            assert_eq!(res, ans[(seed - 1) as usize]);
        }
    }
}