From 83d4971d0686c6b139fddd3204e01cab0f9aee4d Mon Sep 17 00:00:00 2001 From: Akeem Tyrell Lamont King Date: Fri, 11 May 2018 07:49:19 -0400 Subject: [PATCH 01/12] Adding Little Fermat Theorem --- include/functions/math.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/functions/math.hpp b/include/functions/math.hpp index 9dee74e..3871e44 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -8,6 +8,8 @@ #define BIG_INT_MATH_FUNCTIONS_HPP #include +#include +#include #include "functions/conversion.hpp" @@ -153,6 +155,21 @@ BigInt gcd(const BigInt &num1, const BigInt &num2){ return abs_num1; } +BigInt is_probable_prime(const BigInt& num, size_t k) { + srand(time(NULL)); + BigInt a; + while (k > 0) { + a = rand() % (num.to_long_long()-1) + 1; + if (exp(a, num-1)%num == 1) { + return true; + } else if (exp(a, num-1) == 1%num) { + return true; + } + --k; + } + return false; +} + /* gcd(BigInt, Integer) From 0650ee857288673976a216dcb017bdf874308e77 Mon Sep 17 00:00:00 2001 From: Akeem Tyrell Lamont King Date: Fri, 11 May 2018 08:16:34 -0400 Subject: [PATCH 02/12] Fixed issue with sed command being different on GNU vs. OSX using '.hpp' --- scripts/release.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scripts/release.sh b/scripts/release.sh index 1403878..deb753a 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -46,6 +46,9 @@ do cat "include/$file" >> "$release_file" printf "\n\n" >> "$release_file" done - -# delete includes for non-standard header files from the release file -sed -i "/#include \"*\"/d" "$release_file" +if [[ "$OSTYPE" == "linux-gnu" ]]; then + # delete includes for non-standard header files from the release file + sed -i "/#include \"*\"/d" "$release_file" +else + sed -i .hpp "/#include \"*\"/d" "$release_file" +fi From 58e673a3285e4b99895b2c735153aac419609da3 Mon Sep 17 00:00:00 2001 From: Akeem King Date: Fri, 11 May 2018 16:57:57 -0400 Subject: [PATCH 03/12] Finished implementing Little Fermat's Theorem, Rabin Miller is next --- include/BigInt.hpp | 3 +++ include/functions/math.hpp | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/BigInt.hpp b/include/BigInt.hpp index e3be86c..1cb3774 100644 --- a/include/BigInt.hpp +++ b/include/BigInt.hpp @@ -102,6 +102,9 @@ class BigInt { // Random number generating functions: friend BigInt big_random(size_t); + + //Math + bool is_probable_prime(size_t); }; #endif // BIG_INT_HPP diff --git a/include/functions/math.hpp b/include/functions/math.hpp index 3871e44..ef73fa7 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include "functions/conversion.hpp" @@ -155,14 +157,20 @@ BigInt gcd(const BigInt &num1, const BigInt &num2){ return abs_num1; } -BigInt is_probable_prime(const BigInt& num, size_t k) { +bool BigInt::is_probable_prime(size_t k) { + // Little Fermat's Theorem srand(time(NULL)); + const BigInt ONE = 1; BigInt a; while (k > 0) { - a = rand() % (num.to_long_long()-1) + 1; - if (exp(a, num-1)%num == 1) { - return true; - } else if (exp(a, num-1) == 1%num) { + // 1 <= a < n + a = BigInt(rand()+1) % *this-ONE; + + if (gcd(a, *this) != ONE) { + return false; + } + // a^n-1 % n == 1, then n is prime + if (pow(a, this->to_int()-1) % *this == ONE) { return true; } --k; From 773c6674e6adbc2f99a1eeda0d002183045dbea6 Mon Sep 17 00:00:00 2001 From: Akeem King Date: Tue, 15 May 2018 02:30:36 -0400 Subject: [PATCH 04/12] Adding Rabin-Miller theory --- include/functions/math.hpp | 31 ++++++++++++++++++++++++------- include/functions/utility.hpp | 28 +++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/include/functions/math.hpp b/include/functions/math.hpp index b5edde4..4d60cf1 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -12,8 +12,10 @@ #include #include #include +#include #include "functions/conversion.hpp" +#include "functions/utility.hpp" /* @@ -160,23 +162,38 @@ BigInt gcd(const BigInt &num1, const BigInt &num2){ bool BigInt::is_probable_prime(size_t k) { #define COMPOSITE false #define PRIME true - // Little Fermat's Theorem + if (*this <= BigInt(3) || *this == BigInt(5)) { + return PRIME; + } srand(time(NULL)); const BigInt ONE = 1; + const BigInt TWO = 2; BigInt a; while (k-- > 0) { // 1 <= a < n - a = BigInt(rand()+1) % *this-ONE; + a = BigInt(rand()+2) % *this-TWO; if (gcd(a, *this) != ONE) { return COMPOSITE; } - - // a^n-1 != -1/+1 (mod n), then n is composite - if (pow(a, this->to_int()-1) != ONE (% *this) || (pow(a, this->to_int()-1) != -ONE (% *this)) { - return COMPOSITE; + int s, m; + std::tie(s, m) = calculate_vars(*this); + // x = a^m%n + BigInt x = pow(a, m)%*this; + if (x == ONE || x == *this-ONE) { + continue; + } + for (int i = 0; i < s-1; i++) { + // x = x^2%n + x = pow(x, 2)%*this; + if (x == 1) { + return COMPOSITE; + } else if (x == *this-ONE) { + continue; + } + return COMPOSITE; } } - return COMPOSITE; + return PRIME; } diff --git a/include/functions/utility.hpp b/include/functions/utility.hpp index a26ed45..a45cefd 100644 --- a/include/functions/utility.hpp +++ b/include/functions/utility.hpp @@ -8,7 +8,7 @@ #define BIG_INT_UTILITY_FUNCTIONS_HPP #include - +#include /* is_valid_number @@ -109,5 +109,31 @@ bool is_power_of_10(const std::string& num){ return true; // first digit is 1 and the following digits are all 0 } +/* + calculate_vars + ______________________ + Calculates the s and m variables needed to complete Rabin Miller Primality + test. + + s and m come from the formula n-1 = 2^s*m, where m is odd and s >= 1 +*/ +std::tuple calculate_vars(BigInt& n) { + int s = 1; + int m = 1; + long long sentinel_value = n.to_long_long()-1; + long long calculated_value = 2; + + while (sentinel_value >= calculated_value) { + if (sentinel_value > calculated_value) { + s++; + m = sentinel_value/pow(2, s); + calculated_value = pow(2, s)*m; + } else if (sentinel_value == calculated_value) { + return std::make_tuple(s, m); + } + } + + return std::make_tuple(s, m); +} #endif // BIG_INT_UTILITY_FUNCTIONS_HPP From 59927ec426e5b08d36cee63944a5059b9835d2ce Mon Sep 17 00:00:00 2001 From: Akeem King Date: Tue, 15 May 2018 12:21:16 -0400 Subject: [PATCH 05/12] Finished implementing Rabin-Miller Primality test --- include/functions/math.hpp | 40 +++++++++++++++++++++++++---------- include/functions/utility.hpp | 20 ++++++++++++------ 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/include/functions/math.hpp b/include/functions/math.hpp index 4d60cf1..141931c 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -7,12 +7,10 @@ #ifndef BIG_INT_MATH_FUNCTIONS_HPP #define BIG_INT_MATH_FUNCTIONS_HPP -#include -#include -#include -#include -#include -#include + +#include +#include +#include #include "functions/conversion.hpp" #include "functions/utility.hpp" @@ -159,24 +157,44 @@ BigInt gcd(const BigInt &num1, const BigInt &num2){ return abs_num1; } -bool BigInt::is_probable_prime(size_t k) { +/* + BigInt::is_probable_prime(size_t) + --------------------------------- + Returns boolean representing the primality of the BigInt object that this method refers + to using Rabin-Miller Primality testing. + NOTE: This test is probablisitic so it is not failproof. A composite number passes the test + for at most 1/4 of the possible bases. This is true for each iteration of the test, so the + Probability a composite number passes is 1/4^N or less, where N is equal to certainty + */ + +bool BigInt::is_probable_prime(size_t certainty) { + // The BigInt object that this method refers to will be referred to as n in comments #define COMPOSITE false #define PRIME true + if (*this <= BigInt(3) || *this == BigInt(5)) { return PRIME; } - srand(time(NULL)); + + std::default_random_engine generator; + std::uniform_int_distribution distribution(2, this->to_long_long()-2); + auto random_number = std::bind(distribution, generator); + const BigInt ONE = 1; const BigInt TWO = 2; BigInt a; - while (k-- > 0) { + while (certainty-- > 0) { // 1 <= a < n - a = BigInt(rand()+2) % *this-TWO; + a = random_number(); + // If there exists an x > 1 such that a % x == 0 && n % x == 0 + // then n is composite if (gcd(a, *this) != ONE) { return COMPOSITE; } int s, m; - std::tie(s, m) = calculate_vars(*this); + // Calculates needed variables that fit the equation + // n - 1 = 2^s*m such that s >= 1 and m is odd + std::tie(s, m) = calculate_vars(this->to_int()); // x = a^m%n BigInt x = pow(a, m)%*this; if (x == ONE || x == *this-ONE) { diff --git a/include/functions/utility.hpp b/include/functions/utility.hpp index a45cefd..8d1024b 100644 --- a/include/functions/utility.hpp +++ b/include/functions/utility.hpp @@ -109,30 +109,36 @@ bool is_power_of_10(const std::string& num){ return true; // first digit is 1 and the following digits are all 0 } + + /* calculate_vars - ______________________ + -------------- Calculates the s and m variables needed to complete Rabin Miller Primality test. s and m come from the formula n-1 = 2^s*m, where m is odd and s >= 1 */ -std::tuple calculate_vars(BigInt& n) { + +std::tuple calculate_vars(int n) { int s = 1; int m = 1; - long long sentinel_value = n.to_long_long()-1; - long long calculated_value = 2; + const int sentinel_value = n-1; + int calculated_value = 2; while (sentinel_value >= calculated_value) { if (sentinel_value > calculated_value) { s++; - m = sentinel_value/pow(2, s); - calculated_value = pow(2, s)*m; + long long spower = pow(2, s); + if (spower > sentinel_value) { + return std::make_tuple(0,0); + } + m = sentinel_value/spower; + calculated_value = spower*m; } else if (sentinel_value == calculated_value) { return std::make_tuple(s, m); } } - return std::make_tuple(s, m); } From 0a8eba113542fb91a48a7085cf0ebee01a108c0f Mon Sep 17 00:00:00 2001 From: Akeem King Date: Tue, 15 May 2018 20:58:06 -0400 Subject: [PATCH 06/12] Added test for base case of is_probable_prime --- include/functions/math.hpp | 2 +- include/functions/utility.hpp | 6 +++--- test/functions/math.cpp | 15 +++++++++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/functions/math.hpp b/include/functions/math.hpp index 141931c..4307f21 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -194,7 +194,7 @@ bool BigInt::is_probable_prime(size_t certainty) { int s, m; // Calculates needed variables that fit the equation // n - 1 = 2^s*m such that s >= 1 and m is odd - std::tie(s, m) = calculate_vars(this->to_int()); + std::tie(s, m) = calculate_vars(this->to_long_long()); // x = a^m%n BigInt x = pow(a, m)%*this; if (x == ONE || x == *this-ONE) { diff --git a/include/functions/utility.hpp b/include/functions/utility.hpp index 8d1024b..d30a921 100644 --- a/include/functions/utility.hpp +++ b/include/functions/utility.hpp @@ -120,11 +120,11 @@ bool is_power_of_10(const std::string& num){ s and m come from the formula n-1 = 2^s*m, where m is odd and s >= 1 */ -std::tuple calculate_vars(int n) { +std::tuple calculate_vars(long long n) { int s = 1; int m = 1; - const int sentinel_value = n-1; - int calculated_value = 2; + const long long sentinel_value = n-1; + long long calculated_value = 2; while (sentinel_value >= calculated_value) { if (sentinel_value > calculated_value) { diff --git a/test/functions/math.cpp b/test/functions/math.cpp index c769652..39e50cf 100644 --- a/test/functions/math.cpp +++ b/test/functions/math.cpp @@ -91,8 +91,8 @@ TEST_CASE("Base cases for pow()", "[functions][math][pow]") { } TEST_CASE("pow() with BigInt base", "[functions][math][pow]") { - BigInt num = 11; - REQUIRE(pow(num, 9) == 2357947691); + BigInt num = 11; + REQUIRE(pow(num, 9) == 2357947691); num = -27; REQUIRE(pow(num, 16) == "79766443076872509863361"); num = 174; @@ -382,3 +382,14 @@ TEST_CASE("lcm()of big integers", "[functions][math][lcm][big]") { "533220692127153044311875258011747917053108027629278373174251200266431" "428784066739966"); } + +TEST_CASE("Base cases for is_probable_prime()", "[functions][math][is_probable_prime]") { + BigInt num = 1; + REQUIRE(num.is_probable_prime(25) == 1); + num = 2; + REQUIRE(num.is_probable_prime(25) == 1); + num = 3; + REQUIRE(num.is_probable_prime(25) == 1); + num = 5; + REQUIRE(num.is_probable_prime(25) == 1); +} From d58e8d21ebeb610bd5bc7ee9c96833a947e84dce Mon Sep 17 00:00:00 2001 From: Akeem King Date: Sat, 13 Oct 2018 17:17:39 -0400 Subject: [PATCH 07/12] Renaming vars --- include/functions/math.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/functions/math.hpp b/include/functions/math.hpp index 4307f21..6a89d6b 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -178,14 +178,14 @@ bool BigInt::is_probable_prime(size_t certainty) { std::default_random_engine generator; std::uniform_int_distribution distribution(2, this->to_long_long()-2); - auto random_number = std::bind(distribution, generator); + auto get_random_number = std::bind(distribution, generator); const BigInt ONE = 1; const BigInt TWO = 2; - BigInt a; + BigInt rand_num; while (certainty-- > 0) { // 1 <= a < n - a = random_number(); + rand_num = get_random_number(); // If there exists an x > 1 such that a % x == 0 && n % x == 0 // then n is composite if (gcd(a, *this) != ONE) { From c59028a6a6e35ed171edc57923eb90ee23f5d48d Mon Sep 17 00:00:00 2001 From: Akeem King Date: Sat, 13 Oct 2018 17:22:14 -0400 Subject: [PATCH 08/12] Removing unnecessary define statements --- include/functions/math.hpp | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/include/functions/math.hpp b/include/functions/math.hpp index 6a89d6b..00185cb 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -169,11 +169,9 @@ BigInt gcd(const BigInt &num1, const BigInt &num2){ bool BigInt::is_probable_prime(size_t certainty) { // The BigInt object that this method refers to will be referred to as n in comments - #define COMPOSITE false - #define PRIME true if (*this <= BigInt(3) || *this == BigInt(5)) { - return PRIME; + return true; } std::default_random_engine generator; @@ -189,7 +187,7 @@ bool BigInt::is_probable_prime(size_t certainty) { // If there exists an x > 1 such that a % x == 0 && n % x == 0 // then n is composite if (gcd(a, *this) != ONE) { - return COMPOSITE; + return false; } int s, m; // Calculates needed variables that fit the equation @@ -204,14 +202,14 @@ bool BigInt::is_probable_prime(size_t certainty) { // x = x^2%n x = pow(x, 2)%*this; if (x == 1) { - return COMPOSITE; + return false; } else if (x == *this-ONE) { continue; } - return COMPOSITE; + return false; } } - return PRIME; + return true; } From f6a56ac4fc951c5487988b3b665ea864479a1531 Mon Sep 17 00:00:00 2001 From: Akeem King Date: Sat, 13 Oct 2018 17:26:01 -0400 Subject: [PATCH 09/12] Fixing spacing --- include/functions/utility.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/functions/utility.hpp b/include/functions/utility.hpp index d30a921..33d7d57 100644 --- a/include/functions/utility.hpp +++ b/include/functions/utility.hpp @@ -128,11 +128,11 @@ std::tuple calculate_vars(long long n) { while (sentinel_value >= calculated_value) { if (sentinel_value > calculated_value) { - s++; - long long spower = pow(2, s); - if (spower > sentinel_value) { - return std::make_tuple(0,0); - } + s++; + long long spower = pow(2, s); + if (spower > sentinel_value) { + return std::make_tuple(0,0); + } m = sentinel_value/spower; calculated_value = spower*m; } else if (sentinel_value == calculated_value) { From 5028105618b74448e93bdd7ee26e4e45a898d57c Mon Sep 17 00:00:00 2001 From: Akeem King Date: Sat, 13 Oct 2018 17:27:04 -0400 Subject: [PATCH 10/12] Fixing spacing --- test/functions/math.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functions/math.cpp b/test/functions/math.cpp index 39e50cf..99a4330 100644 --- a/test/functions/math.cpp +++ b/test/functions/math.cpp @@ -92,7 +92,7 @@ TEST_CASE("Base cases for pow()", "[functions][math][pow]") { TEST_CASE("pow() with BigInt base", "[functions][math][pow]") { BigInt num = 11; - REQUIRE(pow(num, 9) == 2357947691); + REQUIRE(pow(num, 9) == 2357947691); num = -27; REQUIRE(pow(num, 16) == "79766443076872509863361"); num = 174; From e1eda6f4570c1a29653eed5e03143096dc825d32 Mon Sep 17 00:00:00 2001 From: Akeem King Date: Wed, 17 Oct 2018 18:21:47 -0400 Subject: [PATCH 11/12] Replacing all occurences of a with rand_num --- include/functions/math.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/functions/math.hpp b/include/functions/math.hpp index 00185cb..ceb6bc4 100644 --- a/include/functions/math.hpp +++ b/include/functions/math.hpp @@ -182,19 +182,19 @@ bool BigInt::is_probable_prime(size_t certainty) { const BigInt TWO = 2; BigInt rand_num; while (certainty-- > 0) { - // 1 <= a < n + // 1 <= rand_num < n rand_num = get_random_number(); - // If there exists an x > 1 such that a % x == 0 && n % x == 0 + // If there exists an x > 1 such that rand_num % x == 0 && n % x == 0 // then n is composite - if (gcd(a, *this) != ONE) { + if (gcd(rand_num, *this) != ONE) { return false; } int s, m; // Calculates needed variables that fit the equation // n - 1 = 2^s*m such that s >= 1 and m is odd std::tie(s, m) = calculate_vars(this->to_long_long()); - // x = a^m%n - BigInt x = pow(a, m)%*this; + // x = rand_num^m%n + BigInt x = pow(rand_num, m)%*this; if (x == ONE || x == *this-ONE) { continue; } From 5b4919a2910fd9564133d61943cd3840ade0eeba Mon Sep 17 00:00:00 2001 From: Akeem King Date: Wed, 17 Oct 2018 18:34:48 -0400 Subject: [PATCH 12/12] Adding more extensive testing for is_probable_prime --- test/functions/math.cpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/functions/math.cpp b/test/functions/math.cpp index 99a4330..3a40dfb 100644 --- a/test/functions/math.cpp +++ b/test/functions/math.cpp @@ -393,3 +393,36 @@ TEST_CASE("Base cases for is_probable_prime()", "[functions][math][is_probable_p num = 5; REQUIRE(num.is_probable_prime(25) == 1); } + +TEST_CASE("is_probable_prime() for big integers true", "[functions][math][is_probable_prime]") { + BigInt num = 4361161843811; + REQUIRE(num.is_probable_prime(25) == 1); + num = 91584398720031; + REQUIRE(num.is_probable_prime(25) == 1); + num = "54362229927468991056799869539182953179604007"; + REQUIRE(num.is_probable_prime(25) == 1); + num = "1141606828476848812192797260322842016771684147"; + REQUIRE(num.is_probable_prime(25) == 1); + num = "237082482904158189833801188468727382999221896206963750677"; + REQUIRE(num.is_probable_prime(25) == 1); + num = "4978732140987321986509824957843275042983659820346238764217"; + REQUIRE(num.is_probable_prime(25) == 1); +} + +TEST_CASE("is_probable_prime() for big integers false", "[functions][math][is_probable_prime]") { + BigInt num = 576308207413; + REQUIRE(num.is_probable_prime(25) == 0); + num = 648273642634986; + REQUIRE(num.is_probable_prime(25) == 0); + num = "328964398726983264982"; + REQUIRE(num.is_probable_prime(25) == 0); + num = "4079327665427094820942557"; + REQUIRE(num.is_probable_prime(25) == 0); + num = "879654387682647825646328764"; + REQUIRE(num.is_probable_prime(25) == 0); + num = "98732243986019286982046325298743"; + REQUIRE(num.is_probable_prime(25) == 0); + num = "589658224987973265974369876397863298796328749"; + REQUIRE(num.is_probable_prime(25) == 0); + +}