/*************************************************************************
* ModernUO *
* Copyright 2019-2026 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: StableRandom.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
*************************************************************************/
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Server.Random;
public static class StableRandom
{
// Returns the first long of a new Rng. Used for stable values given a specific set of inputs.
public static long First(ulong seed, long maxValue)
{
Span state = stackalloc ulong[4];
var x = seed;
state[0] = NextSplitMix(ref x);
state[1] = NextSplitMix(ref x);
state[2] = NextSplitMix(ref x);
state[3] = NextSplitMix(ref x);
return (long)NextUInt64(state, (ulong)maxValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong NextUInt64(Span state, ulong maxValue)
{
var randomProduct = Math.BigMul(maxValue, NextUInt64(ref state), out var lowPart);
if (lowPart < maxValue)
{
var remainder = (0ul - maxValue) % maxValue;
while (lowPart < remainder)
{
randomProduct = Math.BigMul(maxValue, NextUInt64(ref state), out lowPart);
}
}
return randomProduct;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong NextUInt64(ref Span state)
{
var result = BitOperations.RotateLeft(state[1] * 5, 7) * 9;
var t = state[1] << 17;
state[2] ^= state[0];
state[3] ^= state[1];
state[1] ^= state[2];
state[0] ^= state[3];
state[2] ^= t;
state[3] = BitOperations.RotateLeft(state[3], 45);
return result;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ulong NextSplitMix(ref ulong x)
{
var z = x += 0x9e3779b97f4a7c15;
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
return z ^ (z >> 31);
}
}