Skip to content

Commit 65dd629

Browse files
committed
Rename files following naming convention; Add demonstration for C#, Java, Python implementation
1 parent 5dcce9a commit 65dd629

File tree

5 files changed

+325
-150
lines changed

5 files changed

+325
-150
lines changed

primalityTest/millerRabinMethod/AlgorithmsTools.cs

-90
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System.Numerics;
2+
using System.Security.Cryptography;
3+
4+
namespace Algorithms.primalityTest
5+
{
6+
7+
public static partial class PrimalityTest
8+
{
9+
10+
/// <summary>
11+
/// Generate a random BigInteger between <paramref name="min"/> and <paramref name="max"/>, inclusive.
12+
/// </summary>
13+
/// <remarks>
14+
/// Random BigInteger implementation from <see href="https://stackoverflow.com/a/48855115/12771343">this StackOverflow answer</see>
15+
/// Uses an offset on the <see cref="RandomInRangeFromZeroToPositive(BigInteger)"/> function to return a random BigInteger in a range.
16+
/// </remarks>
17+
/// <param name="min">BigInter lower bound.</param>
18+
/// <param name="max">BigInteger upper bound.</param>
19+
/// <returns>
20+
/// A random BigInteger in [<paramref name="min"/>, <paramref name="max"/>].
21+
/// </returns>
22+
public static BigInteger RandomInRange(BigInteger min, BigInteger max)
23+
{
24+
25+
// If min > max we swap the values
26+
if (min > max)
27+
{
28+
max += min;
29+
min = max - min;
30+
max -= min;
31+
}
32+
33+
// Offset to set min = 0
34+
max -= min;
35+
36+
// Retrieve the random number and offset to get the desired interval
37+
BigInteger value = RandomInRangeFromZeroToPositive(max) + min;
38+
return value;
39+
}
40+
41+
/// <summary>
42+
/// Generate a random BigInteger smaller or equal to <paramref name="max"/>.
43+
/// </summary>
44+
/// <remarks>
45+
/// Random BigInteger implementation from <see href="https://stackoverflow.com/a/48855115/12771343">this StackOverflow answer</see>
46+
/// Returns a random BigInteger lower than <paramref name="max"/> derived from a random byte array" />
47+
/// </remarks>
48+
/// <param name="max">BigInteger upper bound.</param>
49+
/// <returns>
50+
/// A random BigInteger in [0, <paramref name="max"/>].
51+
/// </returns>
52+
private static BigInteger RandomInRangeFromZeroToPositive(BigInteger max)
53+
{
54+
using RandomNumberGenerator rng = RandomNumberGenerator.Create();
55+
BigInteger value;
56+
byte[] bytes = max.ToByteArray();
57+
58+
// NOTE: sign bit is always 0 because `max` must always be positive
59+
byte zeroBitsMask = 0b00000000;
60+
61+
// Count how many bits of the most significant byte are 0
62+
var mostSignificantByte = bytes[bytes.Length - 1];
63+
64+
// Try to set to 0 as many bits as there are in the most significant byte, starting from the left (most significant bits first)
65+
// NOTE: `i` starts from 7 because the sign bit is always 0
66+
for (var i = 7; i >= 0; i--)
67+
{
68+
// Keep iterating until the most significant non-zero bit
69+
if ((mostSignificantByte & (0b1 << i)) != 0)
70+
{
71+
var zeroBits = 7 - i;
72+
zeroBitsMask = (byte)(0b11111111 >> zeroBits);
73+
break;
74+
}
75+
}
76+
77+
do
78+
{
79+
rng.GetBytes(bytes);
80+
81+
// Set most significant bits to 0 (because if any of these bits is 1 `value > max`)
82+
bytes[bytes.Length - 1] &= zeroBitsMask;
83+
84+
value = new BigInteger(bytes);
85+
86+
// `value > max` 50% of the times, in which case the fastest way to keep the distribution uniform is to try again
87+
} while (value > max);
88+
89+
return value;
90+
}
91+
92+
/// <summary>
93+
/// Miller-Rabin primality test
94+
/// </summary>
95+
/// <remarks>
96+
/// Test if <paramref name="number"/> could be prime using the Miller-Rabin Primality Test with <paramref name="rounds"/> rounds.
97+
/// A return value of false means <paramref name="number"/> is definitely composite, while true means it is probably prime.
98+
/// The higher <paramref name="rounds"/> is, the more accurate the test is.
99+
/// </remarks>
100+
/// <param name="number">The number to be tested for primality.</param>
101+
/// <param name="rounds">How many rounds to use in the test.</param>
102+
/// <returns>A bool indicating if the number could be prime or not.</returns>
103+
public static bool MillerRabin(BigInteger number, int rounds = 40)
104+
{
105+
// Handle corner cases
106+
if (number == 1)
107+
return false;
108+
if (number == 2)
109+
return true;
110+
111+
// Factor out the powers of 2 from {number - 1} and save the result
112+
BigInteger d = number - 1;
113+
int r = 0;
114+
while (d % 2 == 0)
115+
{
116+
d /= 2;
117+
++r;
118+
}
119+
120+
BigInteger x, a;
121+
// Cycle at most {round} times
122+
for (; rounds > 0; --rounds)
123+
{
124+
a = RandomInRange(2, (number - 1));
125+
x = BigInteger.ModPow(a, d, number);
126+
if (x == 1 || x == number - 1)
127+
continue;
128+
// Cycle at most {r - 1} times
129+
for (int tmp = 0; tmp < r - 1; ++tmp)
130+
{
131+
x = BigInteger.ModPow(x, 2, number);
132+
if (x == number - 1)
133+
break;
134+
}
135+
if (x == number - 1)
136+
continue;
137+
return false;
138+
}
139+
return true;
140+
}
141+
}
142+
143+
public static class MillerRabinMethod
144+
{
145+
static void Main(string[] args)
146+
{
147+
int count = 0;
148+
int upper_bound = 1000;
149+
Console.WriteLine($"Prime numbers lower than {upper_bound}:");
150+
for (int i = 1; i < upper_bound; ++i)
151+
{
152+
if (PrimalityTest.MillerRabin(i))
153+
{
154+
Console.WriteLine($"\t{i}");
155+
++count;
156+
}
157+
}
158+
Console.WriteLine($"Total: {count}");
159+
}
160+
}
161+
}

0 commit comments

Comments
 (0)