use core::marker::PhantomData;
use necsim_core::cogs::{MathsCore, PrimeableRng, RngCore};
use serde::{Deserialize, Serialize};
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Serialize, Deserialize, TypeLayout)]
#[serde(deny_unknown_fields)]
#[layout(free = "M")]
pub struct SeaHash<M: MathsCore> {
seed: u64,
location: u64,
time: u64,
offset: u64,
#[serde(skip)]
marker: PhantomData<M>,
}
impl<M: MathsCore> Clone for SeaHash<M> {
fn clone(&self) -> Self {
Self {
seed: self.seed,
location: self.location,
time: self.time,
offset: self.offset,
marker: PhantomData::<M>,
}
}
}
impl<M: MathsCore> RngCore<M> for SeaHash<M> {
type Seed = [u8; 8];
#[must_use]
#[inline]
fn from_seed(seed: Self::Seed) -> Self {
let seed = u64::from_le_bytes(seed);
Self {
seed,
location: 0_u64,
time: 0_u64,
offset: 0_u64,
marker: PhantomData::<M>,
}
}
#[must_use]
#[inline]
fn sample_u64(&mut self) -> u64 {
let sample =
seahash_diffuse(seahash_diffuse(self.time ^ self.offset) ^ self.location ^ self.seed);
self.offset += 1;
sample
}
}
impl<M: MathsCore> PrimeableRng<M> for SeaHash<M> {
fn prime_with(&mut self, location_index: u64, time_index: u64) {
self.location = location_index;
self.time = time_index;
self.offset = 0_u64;
}
}
#[inline]
const fn seahash_diffuse(mut x: u64) -> u64 {
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
}