aboutsummaryrefslogtreecommitdiff
path: root/code
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-02-26 17:12:06 +0100
committerSebastiano Tronto <sebastiano@tronto.net>2025-02-26 17:12:06 +0100
commit7f1b8a358515b45d717c3c4b5baf2fbc0f568170 (patch)
tree3e51ad948d132ccad19da053bb083d8b760b4ec2 /code
downloadecm-7f1b8a358515b45d717c3c4b5baf2fbc0f568170.tar.gz
ecm-7f1b8a358515b45d717c3c4b5baf2fbc0f568170.zip
Initial commit
Diffstat (limited to '')
-rw-r--r--code/cpp/a.outbin0 -> 43664 bytes
-rw-r--r--code/cpp/bigint.h266
-rw-r--r--code/cpp/ecm.cpp91
-rw-r--r--code/cpp/naive.cpp23
-rw-r--r--code/cpp/zmodn.h79
-rwxr-xr-xcode/python/ecm.py75
-rwxr-xr-xcode/python/naive.py18
7 files changed, 552 insertions, 0 deletions
diff --git a/code/cpp/a.out b/code/cpp/a.out
new file mode 100644
index 0000000..b52f011
--- /dev/null
+++ b/code/cpp/a.out
Binary files differ
diff --git a/code/cpp/bigint.h b/code/cpp/bigint.h
new file mode 100644
index 0000000..096ed9c
--- /dev/null
+++ b/code/cpp/bigint.h
@@ -0,0 +1,266 @@
1#ifndef BIGUNSIGNED_H
2#define BIGUNSIGNED_H
3
4#include <cstdint>
5#include <iostream>
6#include <random>
7#include <string_view>
8
9constexpr uint64_t abs64(int64_t);
10constexpr uint64_t pow10(uint64_t);
11
12// Big integer class for numbers of at most N decimal digits.
13// The number E is used to tune the size of each digit, mostly for
14// testing purposes.
15
16template<uint64_t N = 50, uint64_t E = 9>
17requires (E < 10)
18class BigInt {
19public:
20 // The member variables sign and digits are declared public so that
21 // BigInt becomes a structural type and can be used in templates.
22
23 static constexpr uint64_t M = pow10(E);
24 static constexpr uint64_t D = (N / E) + 1;
25
26 bool sign;
27 uint64_t digits[D];
28
29 constexpr BigInt() : sign{true} {
30 std::fill(digits, digits+D, 0);
31 }
32
33 constexpr BigInt(int64_t n) : sign{n >= 0} {
34 std::fill(digits, digits+D, 0);
35 digits[0] = abs64(n);
36 carryover();
37 }
38
39 constexpr BigInt(const std::string_view s) : sign{true} {
40 std::fill(digits, digits+D, 0);
41 if (s.size() == 0)
42 return;
43 for (int i = s.size()-1, j = 0; i >= 0; i--, j++) {
44 if (s[i] == '\'')
45 continue;
46 if (i == 0 && s[i] == '-') {
47 sign = false;
48 break;
49 }
50 digits[j/E] += (pow10(j % E))
51 * static_cast<uint64_t>(s[i] - '0');
52 }
53 }
54
55 constexpr auto operator<=>(const BigInt& other) const {
56 if (sign != other.sign)
57 return sign <=> other.sign;
58
59 for (int i = D-1; i >= 0; i--)
60 if (digits[i] != other.digits[i])
61 return sign ?
62 digits[i] <=> other.digits[i] :
63 other.digits[i] <=> digits[i];
64
65 return 0 <=> 0;
66 }
67
68 constexpr bool operator==(const BigInt& other) const = default;
69
70 constexpr BigInt abs() const {
71 BigInt ret = *this;
72 ret.sign = true;
73 return ret;
74 }
75
76 constexpr BigInt operator-() const {
77 if (*this == 0)
78 return 0;
79 BigInt ret = *this;
80 ret.sign = !ret.sign;
81 return ret;
82 }
83
84 constexpr BigInt operator+(const BigInt& z) const {
85 if (sign && z.sign)
86 return positive_sum(*this, z);
87 else if (sign && !z.sign)
88 return positive_diff(*this, -z);
89 else if (!sign && z.sign)
90 return positive_diff(z, -*this);
91 else
92 return -positive_sum(-*this, -z);
93 }
94
95 constexpr BigInt operator-(const BigInt& z) const {
96 return *this + (-z);
97 }
98
99 constexpr BigInt operator*(const BigInt& z) const {
100 BigInt ret;
101 ret.sign = !(sign ^ z.sign);
102 for (int i = 0; i < D; i++)
103 for (int j = 0; i+j < D; j++)
104 ret.digits[i+j] += digits[i] * z.digits[j];
105 ret.carryover();
106 return ret;
107 }
108
109 constexpr BigInt operator/(const BigInt& z) const {
110 auto [q, r] = euclidean_division(*this, z);
111 return q;
112 }
113
114 constexpr BigInt operator%(const BigInt& z) const {
115 auto [q, r] = euclidean_division(*this, z);
116 return r;
117 }
118
119 constexpr BigInt operator+=(const BigInt& z) { return *this = *this + z; }
120 constexpr BigInt operator++() { return *this += 1; }
121 constexpr BigInt operator-=(const BigInt& z) { return *this = *this - z; }
122 constexpr BigInt operator--() { return *this -= 1; }
123 constexpr BigInt operator*=(const BigInt& z) { return *this = *this * z; }
124 constexpr BigInt operator/=(const BigInt& z) { return *this = *this / z; }
125 constexpr BigInt operator%=(const BigInt& z) { return *this = *this % z; }
126
127 static BigInt random(BigInt r) {
128 std::random_device rd;
129 std::default_random_engine rng(rd());
130 std::uniform_int_distribution<int> distribution(0, M-1);
131
132 BigInt ret;
133 for (uint64_t i = 0; i < D; i++)
134 ret.digits[i] = distribution(rng);
135
136 return ret % r;
137 }
138
139 friend std::ostream& operator<<(std::ostream& os, const BigInt<N, E>& z) {
140 if (z == 0) {
141 os << "0";
142 return os;
143 }
144
145 if (!z.sign)
146 os << "-";
147
148 int j;
149 for (j = z.D-1; z.digits[j] == 0; j--) ;
150 os << z.digits[j]; // Top digit is not padded
151
152 for (int i = j-1; i >= 0; i--) {
153 std::string num = std::to_string(z.digits[i]);
154 os << std::string(E - num.length(), '0') << num;
155 }
156 return os;
157 }
158
159private:
160 constexpr void carryover() {
161 for (int i = 1; i < D; i++) {
162 auto c = digits[i-1] / M;
163 digits[i-1] -= c * M;
164 digits[i] += c;
165 }
166 }
167
168 constexpr BigInt half() const {
169 BigInt ret;
170 uint64_t carry = 0;
171 for (int i = D-1; i >= 0; i--) {
172 ret.digits[i] += (digits[i] + M * carry) / 2;
173 carry = digits[i] % 2;
174 }
175 return ret;
176 }
177
178 static constexpr BigInt powM(uint64_t e) {
179 BigInt ret;
180 ret.digits[e] = 1;
181 return ret;
182 }
183
184 // Sum of non-negative integers
185 static constexpr BigInt positive_sum(const BigInt& x, const BigInt& y) {
186 BigInt ret;
187 for (int i = 0; i < D; i++)
188 ret.digits[i] = x.digits[i] + y.digits[i];
189 ret.carryover();
190 return ret;
191 }
192
193 // Difference of non-negative integers (result may be negative)
194 static constexpr BigInt positive_diff(const BigInt& x, const BigInt& y) {
195 if (y > x)
196 return -positive_diff(y, x);
197
198 BigInt ret;
199 uint64_t carry = 0;
200 for (int i = 0; i < D; i++) {
201 uint64_t oldcarry = carry;
202 if (x.digits[i] < y.digits[i] + oldcarry) {
203 ret.digits[i] = M;
204 carry = 1;
205 } else {
206 carry = 0;
207 }
208 ret.digits[i] += x.digits[i];
209 ret.digits[i] -= y.digits[i] + oldcarry;
210 }
211 ret.carryover();
212 return ret;
213 }
214
215 // Division with remainder, UB if y == 0
216 static constexpr std::pair<BigInt, BigInt>
217 euclidean_division(const BigInt& x, const BigInt& y) {
218 auto [q, r] = positive_div(x.abs(), y.abs());
219 if (x.sign && y.sign)
220 return std::pair(q, r);
221 else if (x.sign && !y.sign)
222 return r == 0 ? std::pair(-q, 0) : std::pair(-q-1, y+r);
223 else if (!x.sign && y.sign)
224 return r == 0 ? std::pair(-q, r) : std::pair(-q-1, y-r);
225 else
226 return std::pair(q, -r);
227 }
228
229 // Division with remainder of non-negative integers, UB if y == 0
230 // This method is inefficient (O(log(x/y)) BigInt multiplications)
231 static constexpr std::pair<BigInt, BigInt>
232 positive_div(const BigInt& x, const BigInt& y) {
233 BigInt q = 0;
234 BigInt r = x;
235
236 if (y > x)
237 return std::pair(q, r);
238
239 BigInt lb = 0;
240 BigInt ub = x;
241 while (true) {
242 BigInt q = (ub + lb).half();
243 BigInt r = x - y*q;
244
245 if (r < 0)
246 ub = q;
247 else if (r >= y)
248 lb = q+1;
249 else
250 return std::pair(q, r);
251 }
252 }
253};
254
255constexpr uint64_t abs64(int64_t x) {
256 return static_cast<uint64_t>(x > 0 ? x : -x);
257}
258
259constexpr uint64_t pow10(uint64_t e) {
260 if (e == 0)
261 return 1;
262 else
263 return 10 * pow10(e-1);
264}
265
266#endif
diff --git a/code/cpp/ecm.cpp b/code/cpp/ecm.cpp
new file mode 100644
index 0000000..64dcb6b
--- /dev/null
+++ b/code/cpp/ecm.cpp
@@ -0,0 +1,91 @@
1#include "bigint.h"
2#include "zmodn.h"
3
4#include <cstdint>
5#include <iostream>
6#include <variant>
7
8constexpr BigInt N(NUMBER);
9
10class Point {
11public:
12 bool is_infinity;
13 Zmod<N> x;
14 Zmod<N> y;
15
16 Point(Zmod<N> a, Zmod<N> b) : is_infinity{false}, x{a}, y{b} {}
17 Point(bool inf) : is_infinity{inf}, x{0}, y{0} {}
18};
19
20std::variant<Point, BigInt<>>
21sum_or_factor(BigInt<> a, Point p, Point q) {
22 if (p.is_infinity) return q;
23 if (q.is_infinity) return p;
24 if (p.x == q.x && p.y + q.y == Zmod<N>(0)) return Point(true);
25
26 Zmod<N> l(0);
27 if (p.x != q.x)
28 if (auto inv_x = (p.x-q.x).inverse(); inv_x.has_value())
29 l = (p.y-q.y) * inv_x.value();
30 else
31 return std::get<0>(extended_gcd(N, (p.x-q.x).toint()));
32 else
33 if (auto inv_y = (p.y+q.y).inverse(); inv_y.has_value())
34 l = (Zmod<N>(3) * p.x * p.x + a) * inv_y.value();
35 else
36 return std::get<0>(extended_gcd(N, (p.y+q.y).toint()));
37
38 auto x = l*l - p.x - q.x;
39 auto y = l*(p.x-x) - p.y;
40
41 return Point(x, y);
42}
43
44std::variant<Point, BigInt<>>
45product_or_factor(BigInt<> a, std::variant<Point, BigInt<>> pi, BigInt<> m) {
46 if (std::holds_alternative<BigInt<>>(pi))
47 return std::get<BigInt<>>(pi);
48
49 auto p = std::get<Point>(pi);
50
51 if (m == 0)
52 return Point(true); // Anything multiplied by 0 is 0
53
54 // Divide-and-conquer multiplication (power) algorithm
55 if (m % 2 == 0) {
56 return product_or_factor(a, sum_or_factor(a, p, p), m/2);
57 } else {
58 auto pp = product_or_factor(a, p, m-1);
59 if (std::holds_alternative<BigInt<>>(pp))
60 return std::get<BigInt<>>(pp);
61 return sum_or_factor(a, p, std::get<Point>(pp));
62 }
63}
64
65BigInt<> find_factor_ecm() {
66 // Find a factor of the integer N.
67 // If N is prime, this method goes into an infinite loop.
68
69 while (true) {
70 BigInt a = BigInt<>::random(N);
71 Point p(BigInt<>::random(N), BigInt<>::random(N));
72 for (BigInt m = 2;
73 (m < 256 || m*m*m*m*m*m*m*m < N) && !p.is_infinity; m += 1) {
74 auto x = product_or_factor(a, p, m);
75 if (std::holds_alternative<BigInt<>>(x))
76 return std::get<BigInt<>>(x);
77 else
78 p = std::get<Point>(x);
79 }
80 }
81}
82
83int main() {
84 // N is a compile-time constant
85 if (BigInt f = find_factor_ecm(); f > 1 && f < N)
86 std::cout << N << " = " << f << " * " << N/f << std::endl;
87 else
88 std::cout << N << " is prime" << std::endl;
89
90 return 0;
91}
diff --git a/code/cpp/naive.cpp b/code/cpp/naive.cpp
new file mode 100644
index 0000000..ed741e6
--- /dev/null
+++ b/code/cpp/naive.cpp
@@ -0,0 +1,23 @@
1#include "bigint.h"
2
3#include <cstdint>
4#include <iostream>
5
6constexpr BigInt N(NUMBER);
7
8BigInt<> find_factor() {
9 for (BigInt i = 2; i*i < N; i += 1)
10 if (N % i == 0)
11 return i;
12 return -1;
13}
14
15int main() {
16 // N is a compile-time constant
17 if (auto f = find_factor(); f > 1 && f < N)
18 std::cout << N << " = " << f << " * " << N/f << std::endl;
19 else
20 std::cout << N << " is prime" << std::endl;
21
22 return 0;
23}
diff --git a/code/cpp/zmodn.h b/code/cpp/zmodn.h
new file mode 100644
index 0000000..a24f293
--- /dev/null
+++ b/code/cpp/zmodn.h
@@ -0,0 +1,79 @@
1#ifndef ZMODN_H
2#define ZMODN_H
3
4#include <cstdint>
5#include <iostream>
6#include <optional>
7#include <tuple>
8#include <type_traits>
9
10template<typename T>
11concept Integer = requires(T a, T b, int i, std::ostream& os) {
12 {T(i)};
13
14 {a + b} -> std::same_as<T>;
15 {a - b} -> std::same_as<T>;
16 {a * b} -> std::same_as<T>;
17 {a / b} -> std::same_as<T>;
18 {a % b} -> std::same_as<T>;
19
20 {a == b} -> std::same_as<bool>;
21 {a != b} -> std::same_as<bool>;
22
23 {os << a} -> std::same_as<std::ostream&>;
24};
25
26template<Integer T>
27std::tuple<T, T, T> extended_gcd(T a, T b) {
28 if (b == 0) return {a, 1, 0};
29 auto [g, x, y] = extended_gcd(b, a%b);
30 return {g, y, x - y*(a/b)};
31}
32
33template<Integer auto N>
34requires(N > 1)
35class Zmod {
36public:
37 Zmod(decltype(N) z) : value{(z%N + N) % N} {}
38 decltype(N) toint() const { return value; }
39
40 Zmod operator+(const Zmod& z) const { return value + z.value; }
41 Zmod operator-(const Zmod& z) const { return value - z.value; }
42 Zmod operator*(const Zmod& z) const { return value * z.value; }
43
44 Zmod operator+=(const Zmod& z) { return (*this) = value + z.value; }
45 Zmod operator-=(const Zmod& z) { return (*this) = value - z.value; }
46 Zmod operator*=(const Zmod& z) { return (*this) = value * z.value; }
47
48 Zmod operator^(decltype(N) z) const {
49 if (z == 0) return 1;
50 if (z % 2 == 0) return (((*this) * (*this)) ^ (z/2));
51 return (*this) * ((*this) ^ (z-1));
52 }
53
54 bool operator==(const Zmod& z) const { return value == z.value; }
55 bool operator!=(const Zmod& z) const { return value != z.value; }
56
57 std::optional<Zmod> inverse() const {
58 auto [g, a, _] = extended_gcd(value, N);
59 return g == 1 ? Zmod(a) : std::optional<Zmod>{};
60 }
61
62 std::optional<Zmod> operator/(const Zmod& d) const {
63 auto i = d.inverse();
64 return i ? (*this) * i.value() : i;
65 }
66
67 std::optional<Zmod> operator/=(const Zmod& d) {
68 auto q = *this / d;
69 return q ? (*this = q.value()) : q;
70 }
71
72 friend std::ostream& operator<<(std::ostream& os, const Zmod<N>& z) {
73 return os << "(" << z.value << " mod " << N << ")";
74 }
75private:
76 decltype(N) value;
77};
78
79#endif
diff --git a/code/python/ecm.py b/code/python/ecm.py
new file mode 100755
index 0000000..662facf
--- /dev/null
+++ b/code/python/ecm.py
@@ -0,0 +1,75 @@
1#! /bin/env python
2
3from sys import argv
4from random import randint
5from math import sqrt
6from dataclasses import dataclass
7
8@dataclass
9class Point:
10 x: int = 0
11 y: int = 0
12 is_zero: bool = False
13
14@dataclass
15class FactorFound(Exception):
16 factor: int = 0
17
18# Returns gcd(a, b) and x, y such that ax + by = gcd(a,b)
19def extended_gcd(a: int, b: int) -> (int, int, int):
20 if b == 0:
21 return a, 1, 0
22 g, x, y = extended_gcd(b, a % b)
23 return g, y, x - y * (a // b)
24
25def inverse_modulo(a: int, n: int) -> int:
26 g, x, y = extended_gcd(a, n)
27 if g != 1:
28 raise FactorFound(g)
29 return x
30
31def ec_sum(p: Point, q: Point, A: int, n: int) -> Point:
32 if p.is_zero:
33 return q
34 if q.is_zero:
35 return p
36 if (p.x - q.x) % n == 0 and (p.y + q.y) % n == 0:
37 return Point(is_zero = True)
38
39 if (p.x - q.x) % n != 0:
40 k = ((p.y - q.y) * inverse_modulo(p.x - q.x, n)) % n
41 else:
42 k = ((3 * p.x**2 + A) * inverse_modulo(p.y + q.y, n)) % n
43
44 x = (k**2 - p.x - q.x) % n
45 y = (k * (p.x - x) - p.y) % n
46
47 return Point(x = x, y = y)
48
49def ec_mul(M: int, p: Point, A: int, n: int) -> Point:
50 if M == 0:
51 return Point(is_zero = True)
52 if M % 2 == 0:
53 return ec_mul(M // 2, ec_sum(p, p, A, n), A, n)
54 return ec_sum(p, ec_mul(M - 1, p, A, n), A, n)
55
56# Elliptic curve factorization method
57# If n is prime, this method goes into an infinite loop
58def find_factor(n: int) -> int:
59 bound = max(int(sqrt(sqrt(sqrt(n)))), 256)
60
61 while True:
62 A = randint(0,n)
63 p = Point(x = randint(0,n), y = randint(0,n))
64 M = 2
65 while M < bound and not p.is_zero:
66 try:
67 p = ec_mul(M, p, A, n)
68 except FactorFound as ff:
69 #print("Found with A =", A, "M =", M, "and P =", p)
70 return ff.factor
71 M += 1
72
73N = int(argv[-1])
74f = find_factor(N)
75print(N, "=", f, "*", N//f)
diff --git a/code/python/naive.py b/code/python/naive.py
new file mode 100755
index 0000000..7f6ec00
--- /dev/null
+++ b/code/python/naive.py
@@ -0,0 +1,18 @@
1#!/bin/env python
2
3from sys import argv
4from math import floor, sqrt
5
6def naive_factor(n: int) -> int:
7 for i in range(2,floor(sqrt(n))+1):
8 if n%i == 0:
9 return i
10 else:
11 return -1
12
13N = int(argv[-1])
14f = naive_factor(N)
15if f == -1:
16 print(N, "is prime")
17else:
18 print(N, "=", f, "*", N//f)

Generated with cgit - Back to sebastiano.tronto.net