-
Notifications
You must be signed in to change notification settings - Fork 1k
[Add] RandomNumberFactory Class
#3987
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fea075b
7ff6fde
dd579ae
65612cc
032f07a
9326f47
baa42e5
b0a2709
82ee255
c5047d3
b894c97
2a3d96b
efd70cc
bb9b48d
33a84f7
a2ac484
7dfa3b2
56cbd58
03ff6da
aac2d66
1f2972e
f8a8b7a
4bc5a15
94a787f
6f442da
87d5c9c
d73b03d
1b6be40
50aab5d
236135a
b08aa7a
03774ba
20499d8
40ebce0
a318423
56f7ffb
8d5ba86
22b5083
367ffa6
1fc0721
9528170
dcf1096
bc64eea
6cfcc63
f04e730
bd8cf38
82df24d
217e7b6
a5c57df
d9bf7be
1fa07ea
4a10323
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,291 @@ | ||
| // Copyright (C) 2015-2025 The Neo Project. | ||
| // | ||
| // RandomNumberFactory.cs file belongs to the neo project and is free | ||
| // software distributed under the MIT software license, see the | ||
| // accompanying file LICENSE in the main directory of the | ||
| // repository or http://www.opensource.org/licenses/mit-license.php | ||
| // for more details. | ||
| // | ||
| // Redistribution and use in source and binary forms with or without | ||
| // modifications are permitted. | ||
|
|
||
| using System; | ||
| using System.Buffers.Binary; | ||
| using System.Numerics; | ||
| using System.Security.Cryptography; | ||
|
|
||
| namespace Neo.Extensions.Factories | ||
| { | ||
| public static class RandomNumberFactory | ||
| { | ||
| public static sbyte NextSByte() => | ||
| NextSByte(0, sbyte.MaxValue); | ||
|
|
||
| public static sbyte NextSByte(sbyte maxValue) | ||
| { | ||
| if (maxValue < 0) | ||
| throw new ArgumentOutOfRangeException(nameof(maxValue)); | ||
|
|
||
| return NextSByte(0, maxValue); | ||
| } | ||
|
|
||
| public static sbyte NextSByte(sbyte minValue, sbyte maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return (sbyte)(NextUInt32((uint)(maxValue - minValue)) + minValue); | ||
| } | ||
|
|
||
| public static byte NextByte() => | ||
| NextByte(0, byte.MaxValue); | ||
|
|
||
| public static byte NextByte(byte maxValue) => | ||
| NextByte(0, maxValue); | ||
|
|
||
| public static byte NextByte(byte minValue, byte maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return (byte)(NextUInt32((uint)(maxValue - minValue)) + minValue); | ||
| } | ||
|
|
||
| public static short NextInt16() => | ||
| NextInt16(0, short.MaxValue); | ||
|
|
||
| public static short NextInt16(short maxValue) | ||
| { | ||
| if (maxValue < 0) | ||
| throw new ArgumentOutOfRangeException(nameof(maxValue)); | ||
|
|
||
| return NextInt16(0, maxValue); | ||
| } | ||
|
|
||
cschuchardt88 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| public static short NextInt16(short minValue, short maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return (short)(NextUInt32((uint)(maxValue - minValue)) + minValue); | ||
| } | ||
|
|
||
| public static ushort NextUInt16() => | ||
| NextUInt16(0, ushort.MaxValue); | ||
|
|
||
| public static ushort NextUInt16(ushort maxValue) => | ||
| NextUInt16(0, maxValue); | ||
|
|
||
| public static ushort NextUInt16(ushort minValue, ushort maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return (ushort)(NextUInt32((uint)(maxValue - minValue)) + minValue); | ||
| } | ||
|
|
||
| public static int NextInt32() => | ||
| NextInt32(0, int.MaxValue); | ||
|
|
||
| public static int NextInt32(int maxValue) | ||
| { | ||
| if (maxValue < 0) | ||
| throw new ArgumentOutOfRangeException(nameof(maxValue)); | ||
|
|
||
| return NextInt32(0, maxValue); | ||
| } | ||
|
|
||
| public static int NextInt32(int minValue, int maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return (int)NextUInt32((uint)(maxValue - minValue)) + minValue; | ||
| } | ||
|
|
||
| public static uint NextUInt32() | ||
| { | ||
| Span<byte> longBytes = stackalloc byte[4]; | ||
| RandomNumberGenerator.Fill(longBytes); | ||
| return BinaryPrimitives.ReadUInt32LittleEndian(longBytes); | ||
| } | ||
|
|
||
| public static uint NextUInt32(uint maxValue) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Usually this method is inclusive in the upper bound, not exclusive
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It does inclusive, you can check the unit tests. They check for that.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if maxValue == 0 we fall into … % maxValue , which implied divide-by-zero. like NextInt32(5, 5)) crashes.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same happens for other Next*
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests do just that NextInt32(MaxValue, MaxValue). It will never be divide-by-zero. When you take |
||
| { | ||
| var randomProduct = (ulong)maxValue * NextUInt32(); | ||
| var lowPart = (uint)randomProduct; | ||
|
|
||
| if (lowPart < maxValue) | ||
| { | ||
| var remainder = (0u - maxValue) % maxValue; | ||
|
|
||
| while (lowPart < remainder) | ||
| { | ||
| randomProduct = (ulong)maxValue * NextUInt32(); | ||
| lowPart = (uint)randomProduct; | ||
| } | ||
| } | ||
|
|
||
| return (uint)(randomProduct >> 32); | ||
| } | ||
|
|
||
| public static uint NextUInt32(uint minValue, uint maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return NextUInt32(maxValue - minValue) + minValue; | ||
| } | ||
|
|
||
| public static long NextInt64() => | ||
| NextInt64(0L, long.MaxValue); | ||
|
|
||
| public static long NextInt64(long maxValue) | ||
| { | ||
| return NextInt64(0L, maxValue); | ||
| } | ||
|
|
||
| public static long NextInt64(long minValue, long maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return (long)NextUInt64((ulong)(maxValue - minValue)) + minValue; | ||
| } | ||
|
|
||
| public static ulong NextUInt64() | ||
| { | ||
| Span<byte> longBytes = stackalloc byte[8]; | ||
| RandomNumberGenerator.Fill(longBytes); | ||
| return BinaryPrimitives.ReadUInt64LittleEndian(longBytes); | ||
| } | ||
|
|
||
| public static ulong NextUInt64(ulong maxValue) | ||
| { | ||
| var randomProduct = BigMul(maxValue, NextUInt64(), out var lowPart); | ||
|
|
||
| if (lowPart < maxValue) | ||
| { | ||
| var remainder = (0ul - maxValue) % maxValue; | ||
|
|
||
| while (lowPart < remainder) | ||
| { | ||
| randomProduct = BigMul(maxValue, NextUInt64(), out lowPart); | ||
| } | ||
| } | ||
|
|
||
| return randomProduct; | ||
| } | ||
|
|
||
| public static ulong NextUInt64(ulong minValue, ulong maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return NextUInt64(maxValue - minValue) + minValue; | ||
| } | ||
|
|
||
| public static BigInteger NextBigInteger(BigInteger minValue, BigInteger maxValue) | ||
| { | ||
| if (minValue == maxValue) return maxValue; | ||
|
|
||
| if (minValue > maxValue) | ||
| throw new ArgumentOutOfRangeException(nameof(minValue)); | ||
|
|
||
| return NextBigInteger(maxValue - minValue) + minValue; | ||
| } | ||
|
|
||
| public static BigInteger NextBigInteger(BigInteger maxValue) | ||
| { | ||
| if (maxValue.Sign < 0) | ||
| throw new ArgumentOutOfRangeException(nameof(maxValue)); | ||
|
|
||
| var maxValueBits = maxValue.GetByteCount() * 8; | ||
| var maxValueSize = BigInteger.Pow(2, maxValueBits); | ||
|
|
||
| var randomProduct = maxValue * NextBigInteger(maxValueBits); | ||
| var randomProductBits = randomProduct.GetByteCount() * 8; | ||
|
|
||
| var lowPart = randomProduct.GetLowPart(maxValueBits); | ||
|
|
||
| if (lowPart < maxValue) | ||
| { | ||
| var remainder = (maxValueSize - maxValue) % maxValue; | ||
|
|
||
| while (lowPart < remainder) | ||
| { | ||
| randomProduct = maxValue * NextBigInteger(maxValueBits); | ||
| randomProductBits = randomProduct.GetByteCount() * 8; | ||
| lowPart = randomProduct.GetLowPart(maxValueBits); | ||
| } | ||
| } | ||
|
|
||
| var result = randomProduct >> (randomProductBits - maxValueBits); | ||
|
|
||
| // Since BigInteger doesn't have a max value or bit size | ||
| // anything over 'maxValue' return zero | ||
| if (result >= maxValue) | ||
| return BigInteger.Zero; | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| public static BigInteger NextBigInteger(int sizeInBits) | ||
| { | ||
| if (sizeInBits < 0) | ||
| throw new ArgumentException("sizeInBits must be non-negative."); | ||
|
|
||
| if (sizeInBits == 0) | ||
| return BigInteger.Zero; | ||
|
|
||
| Span<byte> b = stackalloc byte[sizeInBits / 8 + 1]; | ||
| RandomNumberGenerator.Fill(b); | ||
|
|
||
| if (sizeInBits % 8 == 0) | ||
| b[^1] = 0; | ||
| else | ||
| b[^1] &= (byte)((1 << sizeInBits % 8) - 1); | ||
|
|
||
| return new BigInteger(b); | ||
| } | ||
|
|
||
| private static ulong BigMul(ulong a, ulong b, out ulong low) | ||
| { | ||
| // Adaptation of algorithm for multiplication | ||
| // of 32-bit unsigned integers described | ||
| // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8 | ||
| // Basically, it's an optimized version of FOIL method applied to | ||
| // low and high dwords of each operand | ||
|
|
||
| // Use 32-bit uints to optimize the fallback for 32-bit platforms. | ||
| var al = (uint)a; | ||
| var ah = (uint)(a >> 32); | ||
| var bl = (uint)b; | ||
| var bh = (uint)(b >> 32); | ||
|
|
||
| var mull = ((ulong)al) * bl; | ||
| var t = ((ulong)ah) * bl + (mull >> 32); | ||
| var tl = ((ulong)al) * bh + (uint)t; | ||
|
|
||
| low = (tl << 32) | (uint)mull; | ||
|
|
||
| return ((ulong)ah) * bh + (t >> 32) + (tl >> 32); | ||
| } | ||
| } | ||
| } | ||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.