Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DIRECTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@
* [Factorial](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Factorial.js)
* [Factors](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Factors.js)
* [FareyApproximation](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FareyApproximation.js)
* [FermatPrimalityTest](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FermatPrimalityTest.js)
* [Fibonacci](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/Fibonacci.js)
* [FindHcf](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindHcf.js)
* [FindLcm](https://github.com/TheAlgorithms/Javascript/blob/master/Maths/FindLcm.js)
Expand Down
97 changes: 97 additions & 0 deletions Maths/FermatPrimalityTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* The Fermat primality test is a probabilistic test to determine whether a number
* is a probable prime.
*
* It relies on Fermat's Little Theorem, which states that if p is prime and a
* is not divisible by p, then
*
* a^(p - 1) % p = 1
*
* However, there are certain numbers (so called Fermat Liars) that screw things up;
* if a is one of these liars the equation will hold even though p is composite.
*
* But not everything is lost! It's been proven that at least half of all integers
* aren't Fermat Liars (these ones called Fermat Witnesses). Thus, if we keep
* testing the primality with random integers, we can achieve higher reliability.
*
* The interesting about all of this is that since half of all integers are
* Fermat Witnesses, the precision gets really high really fast! Suppose that we
* make the test 50 times: the chance of getting only Fermat Liars in all runs is
*
* 1 / 2^50 = 8.8 * 10^-16 (a pretty small number)
*
* For comparison, the probability of a cosmic ray causing an error to your
* infalible program is around 1.4 * 10^-15. An order of magnitude below!
*
* But because nothing is perfect, there's a major flaw to this algorithm, and
* the cause are the so called Carmichael Numbers. These are composite numbers n
* that hold the equality from Fermat's Little Theorem for every a < n (excluding
* is factors). In other words, if we are trying to determine if a Carmichael Number
* is prime or not, the chances of getting a wrong answer are pretty high! Because
* of that, the Fermat Primality Test is not used is serious applications. :(
*
* You can find more about the Fermat primality test and its flaws here:
* https://en.wikipedia.org/wiki/Fermat_primality_test
*
* And about Carmichael Numbers here:
* https://primes.utm.edu/glossary/xpage/CarmichaelNumber.html
*/

/**
* Faster exponentiation that capitalize on the fact that we are only interested
* in the modulus of the exponentiation.
*
* Find out more about it here: https://en.wikipedia.org/wiki/Modular_exponentiation
*
* @param {number} base
* @param {number} exponent
* @param {number} modulus
*/
const modularExponentiation = (base, exponent, modulus) => {
if (modulus === 1) return 0 // after all, any x % 1 = 0

let result = 1
base %= modulus // make sure that base < modulus

while (exponent > 0) {
// if exponent is odd, multiply the result by the base
if (exponent % 2 === 1) {
result = (result * base) % modulus
exponent--
} else {
exponent = exponent / 2 // exponent is even for sure
base = (base * base) % modulus
}
}

return result
}

/**
* Test if a given number n is prime or not.
*
* @param {number} n The number to check for primality
* @param {number} numberOfIterations The number of times to apply Fermat's Little Theorem
* @returns True if prime, false otherwise
*/
const fermatPrimeCheck = (n, numberOfIterations = 50) => {
// first check for edge cases
if (n <= 1 || n === 4) return false
if (n <= 3) return true // 2 and 3 are included here

for (let i = 0; i < numberOfIterations; i++) {
// pick a random number a, with 2 <= a < n - 2
const randomNumber = Math.floor(Math.random() * (n - 2) + 2)

// if a^(n - 1) % n is different than 1, n is composite
if (modularExponentiation(randomNumber, n - 1, n) !== 1) {
return false
}
}

// if we arrived here without finding a Fermat Witness, this is almost guaranteed
// to be a prime number (or a Carmichael number, if you are unlucky)
return true
}

export { modularExponentiation, fermatPrimeCheck }
19 changes: 19 additions & 0 deletions Maths/test/FermatPrimalityTest.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { fermatPrimeCheck, modularExponentiation } from '../FermatPrimalityTest'

describe('modularExponentiation', () => {
it('should give the correct output for all exponentiations', () => {
expect(modularExponentiation(38, 220, 221)).toBe(1)
expect(modularExponentiation(24, 220, 221)).toBe(81)
})
})

describe('fermatPrimeCheck', () => {
it('should give the correct output for prime and composite numbers', () => {
expect(fermatPrimeCheck(2, 35)).toBe(true)
expect(fermatPrimeCheck(10, 30)).toBe(false)
expect(fermatPrimeCheck(94286167)).toBe(true)
expect(fermatPrimeCheck(83165867)).toBe(true)
expect(fermatPrimeCheck(13268774)).toBe(false)
expect(fermatPrimeCheck(13233852)).toBe(false)
})
})