use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub enum RandomSeed {
Str(String),
Num(f64),
}
impl fmt::Display for RandomSeed {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RandomSeed::Str(s) => write!(f, "{}", s),
RandomSeed::Num(n) => write!(f, "{}", n),
}
}
}
impl From<&str> for RandomSeed {
fn from(s: &str) -> Self {
RandomSeed::Str(s.to_string())
}
}
impl From<String> for RandomSeed {
fn from(s: String) -> Self {
RandomSeed::Str(s)
}
}
impl From<&String> for RandomSeed {
fn from(s: &String) -> Self {
RandomSeed::Str(s.clone())
}
}
impl From<f64> for RandomSeed {
fn from(n: f64) -> Self {
RandomSeed::Num(n)
}
}
impl From<f32> for RandomSeed {
fn from(n: f32) -> Self {
RandomSeed::Num(n as f64)
}
}
impl From<i32> for RandomSeed {
fn from(n: i32) -> Self {
RandomSeed::Num(n as f64)
}
}
impl From<i64> for RandomSeed {
fn from(n: i64) -> Self {
RandomSeed::Num(n as f64)
}
}
impl From<u32> for RandomSeed {
fn from(n: u32) -> Self {
RandomSeed::Num(n as f64)
}
}
impl From<u64> for RandomSeed {
fn from(n: u64) -> Self {
RandomSeed::Num(n as f64)
}
}
impl From<usize> for RandomSeed {
fn from(n: usize) -> Self {
RandomSeed::Num(n as f64)
}
}
pub fn hash_code(s: &str) -> i32 {
let mut hash: i32 = 0;
for code_unit in s.encode_utf16() {
hash = hash.wrapping_mul(31).wrapping_add(code_unit as i32);
}
hash
}
#[inline]
pub fn mulberry32(a: i64) -> f64 {
let a_u32 = a as u32;
let t0 = a_u32.wrapping_add(0x6D2B79F5);
let t1 = (t0 ^ (t0 >> 15)).wrapping_mul(t0 | 1);
let t2 = t1 ^ t1.wrapping_add((t1 ^ (t1 >> 7)).wrapping_mul(t1 | 61));
let result = t2 ^ (t2 >> 14);
(result as f64) / 4294967296.0
}
#[inline]
pub fn random(seed: impl Into<RandomSeed>) -> f64 {
match seed.into() {
RandomSeed::Str(s) => mulberry32(hash_code(&s) as i64),
RandomSeed::Num(n) => mulberry32((n * 10_000_000_000.0) as i64),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_random_deterministic() {
let r1 = random("remotion-seed");
let r2 = random("remotion-seed");
assert_eq!(r1, r2);
assert!((0.0..1.0).contains(&r1));
let n1 = random(42);
let n2 = random(42);
assert_eq!(n1, n2);
assert!((0.0..1.0).contains(&n1));
assert_ne!(r1, n1);
}
#[test]
fn test_hash_code_known_values() {
assert_eq!(hash_code(""), 0);
assert_eq!(hash_code("a"), 97);
assert_eq!(hash_code("hello"), 99162322);
}
}