mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-02 18:13:39 +00:00
refactor(algo): route the splitmix64 copies through one function (#7603)
* route the splitmix64 copies through one function * document the splitmix64 generators --------- Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
@@ -45,12 +45,9 @@ pub const PATCH_RADIUS: usize = 15;
|
||||
|
||||
struct Rng(u64);
|
||||
impl Rng {
|
||||
/// The next value of the SplitMix64 sequence.
|
||||
fn next(&mut self) -> u64 {
|
||||
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
|
||||
let mut z = self.0;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
|
||||
z ^ (z >> 31)
|
||||
crate::photo_util::splitmix64(&mut self.0)
|
||||
}
|
||||
fn unit(&mut self) -> f64 {
|
||||
(self.next() >> 11) as f64 / (1u64 << 53) as f64
|
||||
|
||||
@@ -65,15 +65,13 @@ const SOURCE_MOMENTS_MAX: usize = 1 << 20;
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Rng(u64);
|
||||
impl Rng {
|
||||
/// A generator whose sequence is fixed by `seed`.
|
||||
pub(crate) fn new(seed: u64) -> Self {
|
||||
Rng(seed ^ 0x9E37_79B9_7F4A_7C15)
|
||||
}
|
||||
/// The next value of the SplitMix64 sequence.
|
||||
pub(crate) fn next(&mut self) -> u64 {
|
||||
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
|
||||
let mut z = self.0;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
|
||||
z ^ (z >> 31)
|
||||
crate::photo_util::splitmix64(&mut self.0)
|
||||
}
|
||||
/// Skip `n` numbers (the same state as after `n` calls of [`Rng::next`]).
|
||||
pub(crate) fn skip(&mut self, n: u64) {
|
||||
|
||||
@@ -159,15 +159,21 @@ pub(crate) fn par_map<T: Send>(n: usize, f: impl Fn(usize) -> T + Sync + Send) -
|
||||
(0..n).map(f).collect()
|
||||
}
|
||||
|
||||
/// Advances `state` one SplitMix64 step and returns the mixed output.
|
||||
pub(crate) fn splitmix64(state: &mut u64) -> u64 {
|
||||
*state = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
|
||||
let mut z = *state;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
|
||||
z ^ (z >> 31)
|
||||
}
|
||||
|
||||
/// Deterministic 64-bit generator (SplitMix64).
|
||||
pub(crate) struct Rng(pub u64);
|
||||
impl Rng {
|
||||
/// The next value of the SplitMix64 sequence.
|
||||
pub fn next(&mut self) -> u64 {
|
||||
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
|
||||
let mut z = self.0;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
|
||||
z ^ (z >> 31)
|
||||
splitmix64(&mut self.0)
|
||||
}
|
||||
pub fn below(&mut self, n: usize) -> usize {
|
||||
(self.next() % n.max(1) as u64) as usize
|
||||
@@ -209,6 +215,29 @@ pub(crate) fn catmull_rom(t: f32) -> [f32; 4] {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// The shared step and [`Rng`] reproduce the inlined copies they replaced, bit for bit.
|
||||
#[test]
|
||||
fn splitmix64_output_is_unchanged() {
|
||||
/// The SplitMix64 step as each generator inlined it before.
|
||||
fn inlined(state: &mut u64) -> u64 {
|
||||
*state = state.wrapping_add(0x9E37_79B9_7F4A_7C15);
|
||||
let mut z = *state;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
|
||||
z ^ (z >> 31)
|
||||
}
|
||||
let mut zero = 0;
|
||||
assert_eq!([0; 3].map(|_| splitmix64(&mut zero)), [0xE220_A839_7B1D_CDAF, 0x6E78_9E6A_A1B9_65F4, 0x06C4_5D18_8009_454F]);
|
||||
for seed in [0, 1, 42, 0x2545_F491_4F6C_DD1D, u64::MAX] {
|
||||
let (mut shared, mut copy, mut rng) = (seed, seed, Rng(seed));
|
||||
for _ in 0..10_000 {
|
||||
let want = inlined(&mut copy);
|
||||
assert_eq!(splitmix64(&mut shared), want);
|
||||
assert_eq!(rng.next(), want);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The shared sRGB functions match the removed copies bit for bit.
|
||||
#[test]
|
||||
fn srgb_transfer_output_is_unchanged() {
|
||||
|
||||
@@ -274,15 +274,13 @@ pub fn composite(surface: &mut Surface, prims: &[Prim], clip: Rect, selection: O
|
||||
pub struct Rng(u64);
|
||||
|
||||
impl Rng {
|
||||
/// A generator whose sequence is fixed by `seed` and `salt`.
|
||||
pub fn new(seed: u32, salt: u64) -> Rng {
|
||||
Rng(((seed as u64) << 32) ^ salt.wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ 0x2545_F491_4F6C_DD1D)
|
||||
}
|
||||
/// The next value of the SplitMix64 sequence.
|
||||
pub fn next_u64(&mut self) -> u64 {
|
||||
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
|
||||
let mut z = self.0;
|
||||
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
|
||||
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
|
||||
z ^ (z >> 31)
|
||||
crate::photo_util::splitmix64(&mut self.0)
|
||||
}
|
||||
/// Uniform in [0, 1).
|
||||
pub fn f(&mut self) -> f32 {
|
||||
|
||||
Reference in New Issue
Block a user