diff --git a/Redcraft.Utility/Source/Private/Numeric/Random.cpp b/Redcraft.Utility/Source/Private/Numeric/Random.cpp new file mode 100644 index 0000000..1d76103 --- /dev/null +++ b/Redcraft.Utility/Source/Private/Numeric/Random.cpp @@ -0,0 +1,51 @@ +#include "Numeric/Random.h" + +#include "Templates/Atomic.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_BEGIN(Math) + +NAMESPACE_UNNAMED_BEGIN + +TAtomic RandState = 586103306; + +NAMESPACE_UNNAMED_END + +uint32 Seed(uint32 InSeed) +{ + uint32 OldSeed = RandState.Load(EMemoryOrder::Relaxed); + + if (InSeed != 0) RandState.Store(InSeed, EMemoryOrder::Relaxed); + + return OldSeed; +} + +uint32 Rand() +{ + uint32 Result; + + RandState.FetchFn( + [&Result](uint32 Value) + { + Result = Value; + + Result ^= Result << 13; + Result ^= Result >> 17; + Result ^= Result << 5; + + return Result; + }, + EMemoryOrder::Relaxed + ); + + return Result % 0x7FFFFFFF; +} + +NAMESPACE_END(Math) + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END diff --git a/Redcraft.Utility/Source/Public/Numeric/Numeric.h b/Redcraft.Utility/Source/Public/Numeric/Numeric.h index 36a422c..3bc8003 100644 --- a/Redcraft.Utility/Source/Public/Numeric/Numeric.h +++ b/Redcraft.Utility/Source/Public/Numeric/Numeric.h @@ -6,3 +6,4 @@ #include "Numeric/Numbers.h" #include "Numeric/Bit.h" #include "Numeric/Math.h" +#include "Numeric/Random.h" diff --git a/Redcraft.Utility/Source/Public/Numeric/Random.h b/Redcraft.Utility/Source/Public/Numeric/Random.h new file mode 100644 index 0000000..328d5e5 --- /dev/null +++ b/Redcraft.Utility/Source/Public/Numeric/Random.h @@ -0,0 +1,88 @@ +#pragma once + +#include "CoreTypes.h" +#include "Numeric/Bit.h" +#include "Numeric/Math.h" + +NAMESPACE_REDCRAFT_BEGIN +NAMESPACE_MODULE_BEGIN(Redcraft) +NAMESPACE_MODULE_BEGIN(Utility) + +NAMESPACE_BEGIN(Math) + +/** Seeds the random number generator. Return the previous seed. */ +NODISCARD REDCRAFTUTILITY_API uint32 Seed(uint32 InSeed = 0); + +/** @return The generated random number within the range of [0, 0x7FFFFFFF). */ +NODISCARD REDCRAFTUTILITY_API uint32 Rand(); + +/** @return The generated random number within the range of [0, A). */ +template +NODISCARD FORCEINLINE T Rand(T A) +{ + constexpr uint32 RandStateNum = 0x7FFFFFFF; + + if (A <= 0) return 0; + + if (A <= RandStateNum) return Rand() % A; + + constexpr uint32 BlockSize = Math::BitFloor(RandStateNum); + constexpr uint BlockWidth = Math::CountRightZero(BlockSize); + + const T BlockNum = Math::DivAndCeil(A, BlockSize); + + T Result = 0; + + for (T I = 0; I < BlockNum; ++I) + { + Result ^= Rand(); + + Result <<= BlockWidth; + } + + return Math::Abs(Result) % A; +} + +/** @return The generated random number within the range of [0, A). */ +template +NODISCARD FORCEINLINE T Rand(T A) +{ + constexpr uint32 RandStateNum = 0x7FFFFFFF; + + if (Math::IsNegative(A)) return TNumericLimits::QuietNaN(); + + constexpr size_t BlockNum = Math::DivAndCeil(sizeof(T), 4); + + T Multiplier = A; + + Multiplier /= BlockNum; + Multiplier /= RandStateNum; + + T Result = 0; + + for (size_t I = 0; I < BlockNum; ++I) + { + Result += Rand() * Multiplier; + } + + return Result; +} + +/** @return The generated random number within the range of [A, B). */ +template requires (CCommonType) +NODISCARD FORCEINLINE auto RandWithin(T A, U B) +{ + using FCommonT = TCommonType; + + if (A == B) return static_cast(A); + + if (A > B) return Math::RandWithin(B, A); + + return static_cast(A + Math::Rand(B - A)); +} + +NAMESPACE_END(Math) + +NAMESPACE_MODULE_END(Utility) +NAMESPACE_MODULE_END(Redcraft) +NAMESPACE_REDCRAFT_END