-
-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathExtensions.cs
More file actions
129 lines (110 loc) · 4.21 KB
/
Extensions.cs
File metadata and controls
129 lines (110 loc) · 4.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
using System;
using System.Linq;
using KeePassLib.Cryptography;
namespace KeePassDiceware
{
internal static class Extensions
{
/// <summary>
/// Generates a pseudorandom value in range [0, <paramref name="maxInclusive"/>] (both ends are inclusive).
/// </summary>
/// <param name="maxInclusive">maximal value (inclusive) of random number.</param>
/// <returns>
/// Pseudorandom value from [0, <paramref name="maxInclusive"/>] range.
/// </returns>
public static ulong AtMost(this CryptoRandomStream random, ulong maxInclusive)
{
const ulong RANDOM_MAX = ulong.MaxValue; // GetRandomUInt64 max return value
if (maxInclusive == RANDOM_MAX)
{
return random.GetRandomUInt64();
}
ulong modulus = maxInclusive + 1;
ulong ceil = RANDOM_MAX - (RANDOM_MAX % modulus);
ulong generated;
do
{
generated = random.GetRandomUInt64();
} while (generated >= ceil);
return generated % modulus;
}
/// <summary>
/// Generates a pseudorandom value in range [0, <paramref name="maxInclusive"/>] (both ends are inclusive).
/// </summary>
/// <param name="maxInclusive">maximal value (inclusive) of random number.</param>
/// <returns>
/// Pseudorandom value from [0, <paramref name="maxInclusive"/>] range.
/// </returns>
public static int AtMost(this CryptoRandomStream random, int maxInclusive)
{
if (maxInclusive < 0)
{
throw new ArgumentOutOfRangeException(nameof(maxInclusive), $"Must be positive");
}
ulong val = random.AtMost(Convert.ToUInt64(maxInclusive));
return checked((int)val); // maxInclusive <= int.MaxValue -> wont throw
}
/// <summary>
/// Generates a pseudorandom value in range [<paramref name="minInclusive"/>, <paramref name="maxInclusive"/>] (both ends are inclusive).
/// </summary>
/// <param name="minInclusive">minimal value (inclusive) of random number.</param>
/// <param name="maxInclusive">maximal value (inclusive) of random number.</param>
/// <returns>
/// Pseudorandom value from [<paramref name="minInclusive"/>, <paramref name="maxInclusive"/>] range.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when the <paramref name="minInclusive"/> is larger than <paramref name="maxInclusive"/>.
/// </exception>
public static int Range(this CryptoRandomStream random, int minInclusive, int maxInclusive)
{
if (minInclusive > maxInclusive)
{
throw new ArgumentOutOfRangeException(nameof(minInclusive), $"Must be smaller or equal to {nameof(maxInclusive)}");
}
int randomValue = AtMost(random, maxInclusive - minInclusive);
return randomValue + minInclusive;
}
/// <summary>
/// Selects a pseudorandom element from <paramref name="array"/>.
/// </summary>
/// <param name="array">array from which to select the random element.</param>
/// <returns>
/// A pseudorandom element from <paramref name="array"/>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="array"/> is empty.
/// </exception>
public static T SelectRandom<T>(this T[] array, CryptoRandomStream random)
{
if (array.Length <= 0)
{
throw new ArgumentException(nameof(array), $"Unable to select a random member of empty object.");
}
int choice = random.AtMost(array.Length - 1);
return array[choice];
}
/// <summary>
/// Return TRUE with 50% probability. Simulates a perfect coin toss.
/// </summary>
/// <returns>
/// TRUE / FALSE with equal probabilities.
/// </returns>
public static bool CoinToss(this CryptoRandomStream random) => (random.GetRandomBytes(1)[0] & 1) == 0;
/// <summary>
/// Shuffles the order of characters in input string <paramref name="str"/>.
/// </summary>
/// <param name="str">String to shuffle.</param>
/// <returns>
/// A new string with the characters in a random order.
/// </returns>
public static string Shuffle(this string str, CryptoRandomStream random)
{
if (string.IsNullOrEmpty(str) || str.Length <= 1)
{
return str;
}
string result = new(str.ToCharArray().OrderBy(s => random.AtMost(ulong.MaxValue)).ToArray());
return result;
}
}
}