@marlonwq/primality 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +70 -0
- package/dist/index.d.mts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +106 -0
- package/dist/index.mjs +78 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Márlon C.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Primality
|
|
2
|
+
A high-performance TypeScript library for primality testing using Miller-Rabin and Fermat algorithms. Designed with BigInt support for cryptographic-grade calculations.
|
|
3
|
+
|
|
4
|
+
[](https://www.npmjs.com/package/@marlonwq/primality)
|
|
5
|
+
[](https://www.npmjs.com/package/@marlonwq/primality)
|
|
6
|
+

|
|
7
|
+
[](https://github.com/marlonwq/primality/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
- **Miller-Rabin Test**: Robust probabilistic test.
|
|
11
|
+
- **Fermat Primality Test**: Fast probabilistic test based on Fermat's Little Theorem.
|
|
12
|
+
- **BigInt Support**: Test extremely large numbers without precision loss.
|
|
13
|
+
- **Zero Dependencies**: Lightweight and optimized for performance.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add @marlonwq/primality
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { isPrimeMillerRabin, isPrimeFermat } from '@marlonwq/primality';
|
|
24
|
+
|
|
25
|
+
// Using Miller-Rabin (Recommended for high accuracy)
|
|
26
|
+
// Use the 'n' suffix for BigInt literals
|
|
27
|
+
const num1 = 104729n;
|
|
28
|
+
console.log(isPrimeMillerRabin(num1)); // true
|
|
29
|
+
|
|
30
|
+
// Using Fermat (Faster, but watch out for Carmichael numbers)
|
|
31
|
+
const num2 = 561n;
|
|
32
|
+
console.log(isPrimeFermat(num2)); // true (Fermat's false positive)
|
|
33
|
+
console.log(isPrimeMillerRabin(num2)); // false (Miller-Rabin's correct answer)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## How it Works
|
|
37
|
+
|
|
38
|
+
<details>
|
|
39
|
+
<summary>Click to expand the mathematical background</summary>
|
|
40
|
+
|
|
41
|
+
The Miller-Rabin test is an evolution of **Fermat's Little Theorem**. Fermat states that if $p$ is prime, then for any integer $a$, $a^{p-1} \equiv 1 \pmod p$. However, some composite numbers (known as **Carmichael numbers**) also satisfy this condition, leading to false positives.
|
|
42
|
+
|
|
43
|
+
### The Miller-Rabin Secret
|
|
44
|
+
The core of Miller-Rabin is the fact that in a prime field $\mathbb{Z}_p$, the equation $x^2 \equiv 1 \pmod p$ has only two solutions: $x = 1$ and $x = p - 1$ (or $-1$).
|
|
45
|
+
|
|
46
|
+
**The Algorithm Steps:**
|
|
47
|
+
1. Write $n - 1$ as $2^s \cdot d$ by repeatedly factoring out powers of 2.
|
|
48
|
+
2. Pick a random base $a$ in the range $[2, n - 2]$.
|
|
49
|
+
3. Compute $x = a^d \pmod n$. If $x = 1$ or $x = n - 1$, the number is a **probable prime**.
|
|
50
|
+
4. Otherwise, square $x$ repeatedly ($x = x^2 \pmod n$) up to $s - 1$ times.
|
|
51
|
+
5. If at any point $x = n - 1$, the number is a **probable prime**.
|
|
52
|
+
6. If the loop finishes without ever hitting $n - 1$, the number is **definitely composite**.
|
|
53
|
+
|
|
54
|
+
### Accuracy
|
|
55
|
+
Miller-Rabin is a probabilistic algorithm. Each iteration ($k$) reduces the probability of a composite number being declared prime to less than $1/4$. With $k=40$, the error probability is less than $4^{-40}$ — a value so infinitesimal that it is statistically more likely for a meteor to strike your computer than for the test to provide a wrong answer.
|
|
56
|
+
|
|
57
|
+
</details>
|
|
58
|
+
|
|
59
|
+
## Contributing
|
|
60
|
+
We use `pnpm` as our package manager. To get started:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
pnpm install
|
|
64
|
+
pnpm build
|
|
65
|
+
pnpm test
|
|
66
|
+
```
|
|
67
|
+
If you find this project helpful, please consider contributing in the following ways: Submitting a [pull request](https://github.com/marlonwq/primality/pulls), opening an [issue](https://github.com/marlonwq/primality/issues), giving the project a star or [buying me a coffee!](https://ko-fi.com/marlonwq)!
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
[MIT](https://github.com/marlonwq/primality/blob/main/LICENSE)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fermat Primality Test
|
|
3
|
+
* @param n - The number to test (BigInt)
|
|
4
|
+
* @param iterations - Number of trials (higher is more accurate)
|
|
5
|
+
* @returns boolean - True if probably prime, false if composite
|
|
6
|
+
*/
|
|
7
|
+
declare function isPrimeFermat(n: bigint, iterations?: number): boolean;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Miller-Rabin Primality Test
|
|
11
|
+
* @param n - The number to test (BigInt)
|
|
12
|
+
* @param k - Number of iterations (accuracy). Default is 40 for high confidence.
|
|
13
|
+
*/
|
|
14
|
+
declare function isPrimeMillerRabin(n: bigint, k?: number): boolean;
|
|
15
|
+
|
|
16
|
+
export { isPrimeFermat, isPrimeMillerRabin };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fermat Primality Test
|
|
3
|
+
* @param n - The number to test (BigInt)
|
|
4
|
+
* @param iterations - Number of trials (higher is more accurate)
|
|
5
|
+
* @returns boolean - True if probably prime, false if composite
|
|
6
|
+
*/
|
|
7
|
+
declare function isPrimeFermat(n: bigint, iterations?: number): boolean;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Miller-Rabin Primality Test
|
|
11
|
+
* @param n - The number to test (BigInt)
|
|
12
|
+
* @param k - Number of iterations (accuracy). Default is 40 for high confidence.
|
|
13
|
+
*/
|
|
14
|
+
declare function isPrimeMillerRabin(n: bigint, k?: number): boolean;
|
|
15
|
+
|
|
16
|
+
export { isPrimeFermat, isPrimeMillerRabin };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
isPrimeFermat: () => isPrimeFermat,
|
|
24
|
+
isPrimeMillerRabin: () => isPrimeMillerRabin
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/fermat.ts
|
|
29
|
+
function isPrimeFermat(n, iterations = 5) {
|
|
30
|
+
if (n <= 1n) return false;
|
|
31
|
+
if (n <= 3n) return true;
|
|
32
|
+
if (n % 2n === 0n) return false;
|
|
33
|
+
for (let i = 0; i < iterations; i++) {
|
|
34
|
+
const a = BigInt(Math.floor(Math.random() * (Number(n) - 4))) + 2n;
|
|
35
|
+
if (powerModular(a, n - 1n, n) !== 1n) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
function powerModular(base, exp, mod) {
|
|
42
|
+
let res = 1n;
|
|
43
|
+
base = base % mod;
|
|
44
|
+
while (exp > 0n) {
|
|
45
|
+
if (exp % 2n === 1n) res = res * base % mod;
|
|
46
|
+
base = base * base % mod;
|
|
47
|
+
exp = exp / 2n;
|
|
48
|
+
}
|
|
49
|
+
return res;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// src/miller-rabin.ts
|
|
53
|
+
function getRandomBigInt(min, max) {
|
|
54
|
+
const range = max - min;
|
|
55
|
+
const bits = range.toString(2).length;
|
|
56
|
+
let res;
|
|
57
|
+
do {
|
|
58
|
+
let randStr = "";
|
|
59
|
+
for (let i = 0; i < bits; i++) {
|
|
60
|
+
randStr += Math.random() > 0.5 ? "1" : "0";
|
|
61
|
+
}
|
|
62
|
+
res = BigInt("0b" + randStr);
|
|
63
|
+
} while (res > range);
|
|
64
|
+
return res + min;
|
|
65
|
+
}
|
|
66
|
+
function isPrimeMillerRabin(n, k = 40) {
|
|
67
|
+
if (n <= 1n) return false;
|
|
68
|
+
if (n <= 3n) return true;
|
|
69
|
+
if (n % 2n === 0n) return false;
|
|
70
|
+
let d = n - 1n;
|
|
71
|
+
let s = 0n;
|
|
72
|
+
while (d % 2n === 0n) {
|
|
73
|
+
d /= 2n;
|
|
74
|
+
s++;
|
|
75
|
+
}
|
|
76
|
+
for (let i = 0; i < k; i++) {
|
|
77
|
+
const a = getRandomBigInt(2n, n - 2n);
|
|
78
|
+
let x = powerModular2(a, d, n);
|
|
79
|
+
if (x === 1n || x === n - 1n) continue;
|
|
80
|
+
let composite = true;
|
|
81
|
+
for (let r = 1n; r < s; r++) {
|
|
82
|
+
x = x * x % n;
|
|
83
|
+
if (x === n - 1n) {
|
|
84
|
+
composite = false;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (composite) return false;
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
function powerModular2(base, exp, mod) {
|
|
93
|
+
let res = 1n;
|
|
94
|
+
base = base % mod;
|
|
95
|
+
while (exp > 0n) {
|
|
96
|
+
if (exp % 2n === 1n) res = res * base % mod;
|
|
97
|
+
base = base * base % mod;
|
|
98
|
+
exp = exp / 2n;
|
|
99
|
+
}
|
|
100
|
+
return res;
|
|
101
|
+
}
|
|
102
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
103
|
+
0 && (module.exports = {
|
|
104
|
+
isPrimeFermat,
|
|
105
|
+
isPrimeMillerRabin
|
|
106
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/fermat.ts
|
|
2
|
+
function isPrimeFermat(n, iterations = 5) {
|
|
3
|
+
if (n <= 1n) return false;
|
|
4
|
+
if (n <= 3n) return true;
|
|
5
|
+
if (n % 2n === 0n) return false;
|
|
6
|
+
for (let i = 0; i < iterations; i++) {
|
|
7
|
+
const a = BigInt(Math.floor(Math.random() * (Number(n) - 4))) + 2n;
|
|
8
|
+
if (powerModular(a, n - 1n, n) !== 1n) {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
function powerModular(base, exp, mod) {
|
|
15
|
+
let res = 1n;
|
|
16
|
+
base = base % mod;
|
|
17
|
+
while (exp > 0n) {
|
|
18
|
+
if (exp % 2n === 1n) res = res * base % mod;
|
|
19
|
+
base = base * base % mod;
|
|
20
|
+
exp = exp / 2n;
|
|
21
|
+
}
|
|
22
|
+
return res;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/miller-rabin.ts
|
|
26
|
+
function getRandomBigInt(min, max) {
|
|
27
|
+
const range = max - min;
|
|
28
|
+
const bits = range.toString(2).length;
|
|
29
|
+
let res;
|
|
30
|
+
do {
|
|
31
|
+
let randStr = "";
|
|
32
|
+
for (let i = 0; i < bits; i++) {
|
|
33
|
+
randStr += Math.random() > 0.5 ? "1" : "0";
|
|
34
|
+
}
|
|
35
|
+
res = BigInt("0b" + randStr);
|
|
36
|
+
} while (res > range);
|
|
37
|
+
return res + min;
|
|
38
|
+
}
|
|
39
|
+
function isPrimeMillerRabin(n, k = 40) {
|
|
40
|
+
if (n <= 1n) return false;
|
|
41
|
+
if (n <= 3n) return true;
|
|
42
|
+
if (n % 2n === 0n) return false;
|
|
43
|
+
let d = n - 1n;
|
|
44
|
+
let s = 0n;
|
|
45
|
+
while (d % 2n === 0n) {
|
|
46
|
+
d /= 2n;
|
|
47
|
+
s++;
|
|
48
|
+
}
|
|
49
|
+
for (let i = 0; i < k; i++) {
|
|
50
|
+
const a = getRandomBigInt(2n, n - 2n);
|
|
51
|
+
let x = powerModular2(a, d, n);
|
|
52
|
+
if (x === 1n || x === n - 1n) continue;
|
|
53
|
+
let composite = true;
|
|
54
|
+
for (let r = 1n; r < s; r++) {
|
|
55
|
+
x = x * x % n;
|
|
56
|
+
if (x === n - 1n) {
|
|
57
|
+
composite = false;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (composite) return false;
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
function powerModular2(base, exp, mod) {
|
|
66
|
+
let res = 1n;
|
|
67
|
+
base = base % mod;
|
|
68
|
+
while (exp > 0n) {
|
|
69
|
+
if (exp % 2n === 1n) res = res * base % mod;
|
|
70
|
+
base = base * base % mod;
|
|
71
|
+
exp = exp / 2n;
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
isPrimeFermat,
|
|
77
|
+
isPrimeMillerRabin
|
|
78
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@marlonwq/primality",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A high-performance TypeScript library for primality testing using Miller-Rabin and Fermat algorithms.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --out-dir dist --clean",
|
|
13
|
+
"test": "vitest"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"math",
|
|
17
|
+
"primality",
|
|
18
|
+
"primes",
|
|
19
|
+
"miller-rabin",
|
|
20
|
+
"fermat",
|
|
21
|
+
"number-theory",
|
|
22
|
+
"cryptography",
|
|
23
|
+
"RSA",
|
|
24
|
+
"RSA cryptography",
|
|
25
|
+
"performance"
|
|
26
|
+
],
|
|
27
|
+
"author": "Márlon",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"packageManager": "pnpm@10.28.0",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"tsup": "^8.5.1",
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vitest": "^4.0.18"
|
|
34
|
+
}
|
|
35
|
+
}
|