From 8bc4e86f2fa69111bb961861796735c2562d3441 Mon Sep 17 00:00:00 2001 From: Taj Date: Mon, 8 Mar 2021 08:19:57 +0000 Subject: [PATCH] feat: added a few algorithms that will be used for rewriting RSA algorithm that currently fails the tests (#288) * feat: added a few algorithms that will be used for rsa algorithm * fix: renamed the directory for better import string organisation * fix: rename of a file * updating DIRECTORY.md * fix: corrected the mistakes in the documetation comments of some code * fix: missplelling * fix: spelling errors * fix: reduced cyclomatic complexity Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> --- DIRECTORY.md | 10 ++- ciphers/polybius/polybius_test.go | 2 +- math/gcd/gcd.go | 6 +- math/gcd/gcd_test.go | 32 ++++++-- math/gcd/gcditerative.go | 9 +++ math/lcm/lcm.go | 12 +++ .../modularexponentiation_test.go | 6 +- math/prime/millerrabinprimalitytest.go | 73 +++++++++++++++++++ .../prime_test.go} | 29 +++++++- math/{primecheck => prime}/primecheck.go | 2 +- .../advanced-aho-corasick/adac.go | 4 +- .../aho-corasick/ac.go | 4 +- strings/multiple-string-matching/sbom/sbom.go | 53 +------------- strings/single-string-matching/bom/bom.go | 10 +-- 14 files changed, 175 insertions(+), 77 deletions(-) create mode 100644 math/gcd/gcditerative.go create mode 100644 math/lcm/lcm.go create mode 100644 math/prime/millerrabinprimalitytest.go rename math/{primecheck/primecheck_test.go => prime/prime_test.go} (63%) rename math/{primecheck => prime}/primecheck.go (97%) diff --git a/DIRECTORY.md b/DIRECTORY.md index 7c5e8ea61..b242a201d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -104,15 +104,19 @@ * Gcd * [Gcd](https://github.com/TheAlgorithms/Go/blob/master/math/gcd/gcd.go) * [Gcd Test](https://github.com/TheAlgorithms/Go/blob/master/math/gcd/gcd_test.go) + * [Gcditerative](https://github.com/TheAlgorithms/Go/blob/master/math/gcd/gcditerative.go) + * Lcm + * [Lcm](https://github.com/TheAlgorithms/Go/blob/master/math/lcm/lcm.go) * Modulararithmetic * [Modularexponentiation](https://github.com/TheAlgorithms/Go/blob/master/math/modulararithmetic/modularexponentiation.go) * [Modularexponentiation Test](https://github.com/TheAlgorithms/Go/blob/master/math/modulararithmetic/modularexponentiation_test.go) * Power * [Fastexponent](https://github.com/TheAlgorithms/Go/blob/master/math/power/fastexponent.go) * [Fastexponent Test](https://github.com/TheAlgorithms/Go/blob/master/math/power/fastexponent_test.go) - * Primecheck - * [Primecheck](https://github.com/TheAlgorithms/Go/blob/master/math/primecheck/primecheck.go) - * [Primecheck Test](https://github.com/TheAlgorithms/Go/blob/master/math/primecheck/primecheck_test.go) + * Prime + * [Millerrabinprimalitytest](https://github.com/TheAlgorithms/Go/blob/master/math/prime/millerrabinprimalitytest.go) + * [Prime Test](https://github.com/TheAlgorithms/Go/blob/master/math/prime/prime_test.go) + * [Primecheck](https://github.com/TheAlgorithms/Go/blob/master/math/prime/primecheck.go) * Pythagoras * [Pythagoras](https://github.com/TheAlgorithms/Go/blob/master/math/pythagoras/pythagoras.go) * [Pythagoras Test](https://github.com/TheAlgorithms/Go/blob/master/math/pythagoras/pythagoras_test.go) diff --git a/ciphers/polybius/polybius_test.go b/ciphers/polybius/polybius_test.go index d75b4ebb3..d607884e3 100644 --- a/ciphers/polybius/polybius_test.go +++ b/ciphers/polybius/polybius_test.go @@ -45,7 +45,7 @@ func TestNewPolybius(t *testing.T) { wantErr string }{ { - name: "correct initalization", size: 5, characters: "HogeF", key: "abcdefghijklmnopqrstuvwxy", wantErr: "", + name: "correct initialization", size: 5, characters: "HogeF", key: "abcdefghijklmnopqrstuvwxy", wantErr: "", }, { name: "truncate characters", size: 5, characters: "HogeFuga", key: "abcdefghijklmnopqrstuvwxy", wantErr: "", diff --git a/math/gcd/gcd.go b/math/gcd/gcd.go index ef22b1589..e76e8396f 100644 --- a/math/gcd/gcd.go +++ b/math/gcd/gcd.go @@ -1,9 +1,9 @@ package gcd -// Gcd finds and returns the greatest common divisor of a given integer. -func Gcd(a, b int) int { +// Recursive finds and returns the greatest common divisor of a given integer. +func Recursive(a, b int64) int64 { if b == 0 { return a } - return Gcd(b, a%b) + return Recursive(b, a%b) } diff --git a/math/gcd/gcd_test.go b/math/gcd/gcd_test.go index ef589fabe..78b5b3155 100644 --- a/math/gcd/gcd_test.go +++ b/math/gcd/gcd_test.go @@ -2,21 +2,23 @@ package gcd import "testing" +type testFunction func(int64, int64) int64 + var testCases = []struct { name string - a int - b int - output int + a int64 + b int64 + output int64 }{ {"gcd of 10 and 0", 10, 0, 10}, {"gcd of 98 and 56", 98, 56, 14}, {"gcd of 0 and 10", 0, 10, 10}, } -func TestGCD(t *testing.T) { +func TemplateTestGCD(t *testing.T, f testFunction) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual := Gcd(tc.a, tc.b) + actual := f(tc.a, tc.b) if actual != tc.output { t.Errorf("Expected GCD of %d and %d to be: %v, but got: %d", tc.a, tc.b, tc.output, actual) } @@ -24,8 +26,24 @@ func TestGCD(t *testing.T) { } } -func BenchmarkGCD(b *testing.B) { +func TestGCDRecursive(t *testing.T) { + TemplateTestGCD(t, Recursive) +} + +func TestGCDIterative(t *testing.T) { + TemplateTestGCD(t, Iterative) +} + +func TemplateBenchmarkGCD(b *testing.B, f testFunction) { for i := 0; i < b.N; i++ { - Gcd(98, 56) + f(98, 56) } } + +func BenchmarkGCDRecursive(b *testing.B) { + TemplateBenchmarkGCD(b, Recursive) +} + +func BenchmarkGCDIterative(b *testing.B) { + TemplateBenchmarkGCD(b, Iterative) +} diff --git a/math/gcd/gcditerative.go b/math/gcd/gcditerative.go new file mode 100644 index 000000000..e87dfce6f --- /dev/null +++ b/math/gcd/gcditerative.go @@ -0,0 +1,9 @@ +package gcd + +// Iterative Faster iterative version of GcdRecursive without holding up too much of the stack +func Iterative(a, b int64) int64 { + for b != 0 { + a, b = b, a%b + } + return a +} diff --git a/math/lcm/lcm.go b/math/lcm/lcm.go new file mode 100644 index 000000000..e1de23e17 --- /dev/null +++ b/math/lcm/lcm.go @@ -0,0 +1,12 @@ +package lcm + +import ( + "math" + + "github.com/TheAlgorithms/Go/math/gcd" +) + +// Lcm returns the lcm of two numbers using the fact that lcm(a,b) * gcd(a,b) = | a * b | +func Lcm(a, b int64) int64 { + return int64(math.Abs(float64(a*b)) / float64(gcd.Iterative(a, b))) +} diff --git a/math/modulararithmetic/modularexponentiation_test.go b/math/modulararithmetic/modularexponentiation_test.go index 531e4045f..9af4d7461 100644 --- a/math/modulararithmetic/modularexponentiation_test.go +++ b/math/modulararithmetic/modularexponentiation_test.go @@ -66,12 +66,12 @@ func TestModularExponentiation(t *testing.T) { result, err := ModularExponentiation(test.base, test.exponent, test.mod) if err != test.expectedError { t.Logf("Test Failed for %s", test.name) - t.Logf("Unexpected error occured") - t.Errorf("Expected error: %v, Recieved error: %v", test.expectedError, err) + t.Logf("Unexpected error occurred") + t.Errorf("Expected error: %v, Received error: %v", test.expectedError, err) } if result != test.expected { t.Logf("Test Failed for %s", test.description) - t.Fatalf("Expected: %d, Recieved: %d", test.expected, result) + t.Fatalf("Expected: %d, Received: %d", test.expected, result) } }) } diff --git a/math/prime/millerrabinprimalitytest.go b/math/prime/millerrabinprimalitytest.go new file mode 100644 index 000000000..f38da3dfa --- /dev/null +++ b/math/prime/millerrabinprimalitytest.go @@ -0,0 +1,73 @@ +package prime + +import ( + "math/rand" + + "github.com/TheAlgorithms/Go/math/modulararithmetic" +) + +// findD accepts a number and returns the +// odd number d such that num = 2^r * d - 1 +func findRD(num int64) (int64, int64) { + r := int64(0) + d := num - 1 + for num%2 == 0 { + d /= 2 + r++ + } + return d, r +} + +// MillerTest This is the intermediate step that repeats within the +// miller rabin primality test for better probabilitic chances of +// receiving the correct result. +func MillerTest(d, num int64) (bool, error) { + random := rand.Int63n(num-1) + 2 + + res, err := modulararithmetic.ModularExponentiation(random, d, num) + + if err != nil { + return false, err + } + // miller conditions checks + if res == 1 || res == num-1 { + return true, nil + } + + for d != num-1 { + res = (res * res) % num + d *= 2 + if res == 1 { + return false, nil + } + if res == num-1 { + return true, nil + } + } + return false, nil +} + +// MillerRabinTest Probabilistic test for primality of an integer based of the algorithm devised by Miller and Rabin. +func MillerRabinTest(num, rounds int64) (bool, error) { + if num <= 4 { + if num == 2 || num == 3 { + return true, nil + } + return false, nil + } + if num%2 == 0 { + return false, nil + } + d, _ := findRD(num) + + for i := int64(0); i < rounds; i++ { + val, err := MillerTest(d, num) + if err != nil { + return false, err + } + if !val { + return false, nil + } + } + return true, nil +} diff --git a/math/primecheck/primecheck_test.go b/math/prime/prime_test.go similarity index 63% rename from math/primecheck/primecheck_test.go rename to math/prime/prime_test.go index 62c8105e0..a8b835f5e 100644 --- a/math/primecheck/primecheck_test.go +++ b/math/prime/prime_test.go @@ -1,4 +1,4 @@ -package primecheck +package prime import ( "testing" @@ -49,6 +49,33 @@ func TestTablePairApproach(t *testing.T) { } +func TestMillerRabinTest(t *testing.T) { + var tests = []struct { + name string + input int64 + expected bool + rounds int64 + err error + }{ + {"smallest prime", 2, true, 5, nil}, + {"random prime", 3, true, 5, nil}, + {"neither prime nor composite", 1, false, 5, nil}, + {"random non-prime", 10, false, 5, nil}, + {"another random prime", 23, true, 5, nil}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + output, err := MillerRabinTest(test.input, test.rounds) + if err != test.err { + t.Errorf("For input: %d, unexpected error: %v, expected error: %v", test.input, err, test.err) + } + if output != test.expected { + t.Errorf("For input: %d, expected: %v", test.input, output) + } + }) + } +} + func BenchmarkNaiveApproach(b *testing.B) { for i := 0; i < b.N; i++ { NaiveApproach(23) diff --git a/math/primecheck/primecheck.go b/math/prime/primecheck.go similarity index 97% rename from math/primecheck/primecheck.go rename to math/prime/primecheck.go index 6f03a5918..e6b4f96c2 100644 --- a/math/primecheck/primecheck.go +++ b/math/prime/primecheck.go @@ -1,4 +1,4 @@ -package primecheck +package prime // A primality test is an algorithm for determining whether an input number is prime.Among other fields of mathematics, it is used for cryptography. //Unlike integer factorization, primality tests do not generally give prime factors, only stating whether the input number is prime or not. diff --git a/strings/multiple-string-matching/advanced-aho-corasick/adac.go b/strings/multiple-string-matching/advanced-aho-corasick/adac.go index 82012451b..dfbb6ae67 100644 --- a/strings/multiple-string-matching/advanced-aho-corasick/adac.go +++ b/strings/multiple-string-matching/advanced-aho-corasick/adac.go @@ -44,7 +44,7 @@ type Result struct { // } // r := ahoCorasick(string(textFile), patterns) // for key, value := range r.occurrences { //prints all occurrences of each pattern (if there was at least one) -// fmt.Printf("\nThere were %d occurences for word: %q at positions: ", len(value), key) +// fmt.Printf("\nThere were %d occurrences for word: %q at positions: ", len(value), key) // for i := range value { // fmt.Printf("%d", value[i]) // if i != len(value)-1 { @@ -77,7 +77,7 @@ func AhoCorasick(t string, p []string) Result { for i := range f[current] { if p[f[current][i]] == GetWord(pos-len(p[f[current][i]])+1, pos, t) { //check for word match // if debugMode == true { - // fmt.Printf("Occurence at position %d, %q = %q\n", pos-len(p[f[current][i]])+1, p[f[current][i]], p[f[current][i]]) + // fmt.Printf("Occurrence at position %d, %q = %q\n", pos-len(p[f[current][i]])+1, p[f[current][i]], p[f[current][i]]) // } newOccurrences := IntArrayCapUp(occurrences[f[current][i]]) occurrences[f[current][i]] = newOccurrences diff --git a/strings/multiple-string-matching/aho-corasick/ac.go b/strings/multiple-string-matching/aho-corasick/ac.go index d6abb0922..903f693fc 100644 --- a/strings/multiple-string-matching/aho-corasick/ac.go +++ b/strings/multiple-string-matching/aho-corasick/ac.go @@ -43,7 +43,7 @@ type Result struct { // } // r := ahoCorasick(string(textFile), patterns) // for key, value := range r.occurrences { //prints all occurrences of each pattern (if there was at least one) -// fmt.Printf("\nThere were %d occurences for word: %q at positions: ", len(value), key) +// fmt.Printf("\nThere were %d occurrences for word: %q at positions: ", len(value), key) // for i := range value { // fmt.Printf("%d", value[i]) // if i != len(value)-1 { @@ -85,7 +85,7 @@ func AhoCorasick(t string, p []string) Result { for i := range f[current] { if p[f[current][i]] == getWord(pos-len(p[f[current][i]])+1, pos, t) { //check for word match // if debugMode == true { - // fmt.Printf("Occurence at position %d, %q = %q\n", pos-len(p[f[current][i]])+1, p[f[current][i]], p[f[current][i]]) + // fmt.Printf("Occurrence at position %d, %q = %q\n", pos-len(p[f[current][i]])+1, p[f[current][i]], p[f[current][i]]) // } newOccurrences := intArrayCapUp(occurrences[f[current][i]]) occurrences[f[current][i]] = newOccurrences diff --git a/strings/multiple-string-matching/sbom/sbom.go b/strings/multiple-string-matching/sbom/sbom.go index 022ec1577..1c0c8904f 100644 --- a/strings/multiple-string-matching/sbom/sbom.go +++ b/strings/multiple-string-matching/sbom/sbom.go @@ -6,11 +6,6 @@ import ( "time" ) -// User defined. -// Set to true to print various extra stuff out (slows down the execution) -// Set to false for quick and quiet execution. -const debugMode bool = true - type Result struct { occurrences map[string][]int } @@ -44,7 +39,7 @@ type Result struct { // } // r := sbom(string(textFile), patterns) // for key, value := range r.occurrences { //prints all occurrences of each pattern (if there was at least one) -// fmt.Printf("\nThere were %d occurences for word: %q at positions: ", len(value), key) +// fmt.Printf("\nThere were %d occurrences for word: %q at positions: ", len(value), key) // for i := range value { // fmt.Printf("%d", value[i]) // if i != len(value)-1 { @@ -63,43 +58,21 @@ func Sbom(t string, p []string) Result { occurrences := make(map[int][]int) lmin := computeMinLength(p) or, f := buildOracleMultiple(reverseAll(trimToLength(p, lmin))) - if debugMode { - fmt.Printf("\n\nSBOM:\n\n") - } pos := 0 for pos <= len(t)-lmin { current := 0 j := lmin - if debugMode { - fmt.Printf("Position: %d, we read: ", pos) - } for j >= 1 && stateExists(current, or) { - if debugMode { - fmt.Printf("%c", t[pos+j-1]) - } current = getTransition(current, t[pos+j-1], or) - if debugMode { - if current == -1 { - fmt.Printf(" (FAIL) ") - } else { - fmt.Printf(", ") - } - } j-- } - if debugMode { - fmt.Printf("in the factor oracle. \n") - } word := getWord(pos, pos+lmin-1, t) if stateExists(current, or) && j == 0 && strings.HasPrefix(word, getCommonPrefix(p, f[current], lmin)) { //check for prefix match for i := range f[current] { if p[f[current][i]] == getWord(pos, pos-1+len(p[f[current][i]]), t) { //check for word match - if debugMode { - fmt.Printf("- Occurence, %q = %q\n", p[f[current][i]], word) - } - newOccurences := intArrayCapUp(occurrences[f[current][i]]) - occurrences[f[current][i]] = newOccurences - occurrences[f[current][i]][len(newOccurences)-1] = pos + newOccurrences := intArrayCapUp(occurrences[f[current][i]]) + occurrences[f[current][i]] = newOccurrences + occurrences[f[current][i]][len(newOccurrences)-1] = pos } } j = 0 @@ -125,9 +98,6 @@ func buildOracleMultiple(p []string) (orToReturn map[int]map[uint8]int, f map[in i := 0 //root of trie orToReturn = orTrie s[i] = -1 - if debugMode { - fmt.Printf("\n\nOracle construction: \n") - } for current := 1; current < len(stateIsTerminal); current++ { o, parent := getParent(current, orTrie) down := s[parent] @@ -150,9 +120,6 @@ func constructTrie(p []string) (trie map[int]map[uint8]int, stateIsTerminal []bo stateIsTerminal = make([]bool, 1) f = make(map[int][]int) state := 1 - if debugMode { - fmt.Printf("\n\nTrie construction: \n") - } createNewState(0, trie) for i := 0; i < len(p); i++ { current := 0 @@ -174,15 +141,9 @@ func constructTrie(p []string) (trie map[int]map[uint8]int, stateIsTerminal []bo newArray := intArrayCapUp(f[current]) newArray[len(newArray)-1] = i f[current] = newArray //F(Current) <- F(Current) union {i} - if debugMode { - fmt.Printf(" and %d", i) - } } else { stateIsTerminal[current] = true f[current] = []int{i} //F(Current) <- {i} - if debugMode { - fmt.Printf("\n%d is terminal for word number %d", current, i) - } } } return trie, stateIsTerminal, f @@ -290,17 +251,11 @@ func getParent(state int, at map[int]map[uint8]int) (uint8, int) { // Automaton function for creating a new state 'state'. func createNewState(state int, at map[int]map[uint8]int) { at[state] = make(map[uint8]int) - if debugMode { - fmt.Printf("\ncreated state %d", state) - } } // Creates a transition for function σ(state,letter) = end. func createTransition(fromState int, overChar uint8, toState int, at map[int]map[uint8]int) { at[fromState][overChar] = toState - if debugMode { - fmt.Printf("\n σ(%d,%c)=%d;", fromState, overChar, toState) - } } // Returns ending state for transition σ(fromState,overChar), '-1' if there is none. diff --git a/strings/single-string-matching/bom/bom.go b/strings/single-string-matching/bom/bom.go index 2d0fd01f4..b237eb05b 100644 --- a/strings/single-string-matching/bom/bom.go +++ b/strings/single-string-matching/bom/bom.go @@ -67,7 +67,7 @@ package bom // n, m := len(t), len(p) // var current, j, pos int // oracle := oracleOnLine(reverse(p)) -// occurences := make([]int, len(t)) +// occurrences := make([]int, len(t)) // currentOcc := 0 // pos = 0 // if debugMode == true { @@ -85,9 +85,9 @@ package bom // } // if stateExists(current, oracle) { // if debugMode == true { -// fmt.Printf(" We got an occurence!") +// fmt.Printf(" We got an occurrence!") // } -// occurences[currentOcc] = pos +// occurrences[currentOcc] = pos // currentOcc++ // } // pos = pos + j + 1 @@ -103,9 +103,9 @@ package bom // if currentOcc > 0 { // fmt.Printf("Word %q was found %d times at positions: ", p, currentOcc) // for k := 0; k < currentOcc-1; k++ { -// fmt.Printf("%d, ", occurences[k]) +// fmt.Printf("%d, ", occurrences[k]) // } -// fmt.Printf("%d", occurences[currentOcc-1]) +// fmt.Printf("%d", occurrences[currentOcc-1]) // fmt.Printf(".\n") // } // if currentOcc == 0 {