diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2025-02-26 17:12:06 +0100 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2025-02-26 17:12:06 +0100 |
| commit | 7f1b8a358515b45d717c3c4b5baf2fbc0f568170 (patch) | |
| tree | 3e51ad948d132ccad19da053bb083d8b760b4ec2 /code | |
| download | ecm-7f1b8a358515b45d717c3c4b5baf2fbc0f568170.tar.gz ecm-7f1b8a358515b45d717c3c4b5baf2fbc0f568170.zip | |
Initial commit
Diffstat (limited to 'code')
| -rw-r--r-- | code/cpp/a.out | bin | 0 -> 43664 bytes | |||
| -rw-r--r-- | code/cpp/bigint.h | 266 | ||||
| -rw-r--r-- | code/cpp/ecm.cpp | 91 | ||||
| -rw-r--r-- | code/cpp/naive.cpp | 23 | ||||
| -rw-r--r-- | code/cpp/zmodn.h | 79 | ||||
| -rwxr-xr-x | code/python/ecm.py | 75 | ||||
| -rwxr-xr-x | code/python/naive.py | 18 |
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 | |||
| 9 | constexpr uint64_t abs64(int64_t); | ||
| 10 | constexpr 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 | |||
| 16 | template<uint64_t N = 50, uint64_t E = 9> | ||
| 17 | requires (E < 10) | ||
| 18 | class BigInt { | ||
| 19 | public: | ||
| 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 | |||
| 159 | private: | ||
| 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 | |||
| 255 | constexpr uint64_t abs64(int64_t x) { | ||
| 256 | return static_cast<uint64_t>(x > 0 ? x : -x); | ||
| 257 | } | ||
| 258 | |||
| 259 | constexpr 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 | |||
| 8 | constexpr BigInt N(NUMBER); | ||
| 9 | |||
| 10 | class Point { | ||
| 11 | public: | ||
| 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 | |||
| 20 | std::variant<Point, BigInt<>> | ||
| 21 | sum_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 | |||
| 44 | std::variant<Point, BigInt<>> | ||
| 45 | product_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 | |||
| 65 | BigInt<> 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 | |||
| 83 | int 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 | |||
| 6 | constexpr BigInt N(NUMBER); | ||
| 7 | |||
| 8 | BigInt<> 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 | |||
| 15 | int 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 | |||
| 10 | template<typename T> | ||
| 11 | concept 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 | |||
| 26 | template<Integer T> | ||
| 27 | std::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 | |||
| 33 | template<Integer auto N> | ||
| 34 | requires(N > 1) | ||
| 35 | class Zmod { | ||
| 36 | public: | ||
| 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 | } | ||
| 75 | private: | ||
| 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 | |||
| 3 | from sys import argv | ||
| 4 | from random import randint | ||
| 5 | from math import sqrt | ||
| 6 | from dataclasses import dataclass | ||
| 7 | |||
| 8 | @dataclass | ||
| 9 | class Point: | ||
| 10 | x: int = 0 | ||
| 11 | y: int = 0 | ||
| 12 | is_zero: bool = False | ||
| 13 | |||
| 14 | @dataclass | ||
| 15 | class FactorFound(Exception): | ||
| 16 | factor: int = 0 | ||
| 17 | |||
| 18 | # Returns gcd(a, b) and x, y such that ax + by = gcd(a,b) | ||
| 19 | def 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 | |||
| 25 | def 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 | |||
| 31 | def 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 | |||
| 49 | def 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 | ||
| 58 | def 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 | |||
| 73 | N = int(argv[-1]) | ||
| 74 | f = find_factor(N) | ||
| 75 | print(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 | |||
| 3 | from sys import argv | ||
| 4 | from math import floor, sqrt | ||
| 5 | |||
| 6 | def 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 | |||
| 13 | N = int(argv[-1]) | ||
| 14 | f = naive_factor(N) | ||
| 15 | if f == -1: | ||
| 16 | print(N, "is prime") | ||
| 17 | else: | ||
| 18 | print(N, "=", f, "*", N//f) | ||
