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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
use necsim_core::{
    cogs::{Backup, Habitat, MathsCore},
    landscape::IndexedLocation,
};
use necsim_core_bond::{ClosedUnitF64, PositiveF64};

use super::EmigrationChoice;

#[allow(clippy::module_name_repetitions)]
#[derive(Debug)]
pub struct ProbabilisticEmigrationChoice {
    probability: ClosedUnitF64,
}

impl ProbabilisticEmigrationChoice {
    #[must_use]
    pub fn new(probability: ClosedUnitF64) -> Self {
        Self { probability }
    }
}

#[contract_trait]
impl Backup for ProbabilisticEmigrationChoice {
    unsafe fn backup_unchecked(&self) -> Self {
        Self {
            probability: self.probability,
        }
    }
}

#[contract_trait]
impl<M: MathsCore, H: Habitat<M>> EmigrationChoice<M, H> for ProbabilisticEmigrationChoice {
    fn should_lineage_emigrate(
        &self,
        _indexed_location: &IndexedLocation,
        time: PositiveF64,
        _habitat: &H,
    ) -> bool {
        let hash = diffuse(time.get().to_bits());

        // http://prng.di.unimi.it -> Generating uniform doubles in the unit interval
        #[allow(clippy::cast_precision_loss)]
        let u01 = ((hash >> 11) as f64) * f64::from_bits(0x3CA0_0000_0000_0000_u64); // 0x1.0p-53

        u01 <= self.probability.get()
    }
}

// https://docs.rs/seahash/4.0.1/src/seahash/helper.rs.html#72-89
#[inline]
const fn diffuse(mut x: u64) -> u64 {
    // These are derived from the PCG RNG's round. Thanks to @Veedrac for proposing
    // this. The basic idea is that we use dynamic shifts, which are determined
    // by the input itself. The shift is chosen by the higher bits, which means
    // that changing those flips the lower bits, which scatters upwards because
    // of the multiplication.

    x = x.wrapping_mul(0x6eed_0e9d_a4d9_4a4f);

    let a = x >> 32;
    let b = x >> 60;

    x ^= a >> b;

    x = x.wrapping_mul(0x6eed_0e9d_a4d9_4a4f);

    x
}