Skip to content

Commit d9eb54f

Browse files
authored
Merge pull request #414 from hitonanode/gaussian-integer
Gaussian integer
2 parents e67e04a + aeb426a commit d9eb54f

9 files changed

Lines changed: 244 additions & 3 deletions

File tree

.github/workflows/verify.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ jobs:
1414
- uses: actions/checkout@v1
1515

1616
- name: Set up Python
17-
uses: actions/setup-python@v1
18-
17+
uses: actions/setup-python@v5
18+
1919
- name: Install boost
2020
run: sudo apt install -y libboost-dev
2121

2222
- name: Install dependencies
23-
run: pip3 install -U online-judge-verify-helper
23+
run: |
24+
python -m pip install -U pip
25+
python -m pip install -U "setuptools<81" online-judge-verify-helper
2426
2527
- name: Clone AC Library
2628
run: cd ${GITHUB_WORKSPACE}/../ && git clone https://github.com/atcoder/ac-library.git

number/gaussian_integer.hpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#pragma once
2+
#include <cassert>
3+
#include <utility>
4+
5+
struct GaussianInteger {
6+
using Int = long long;
7+
using G = GaussianInteger;
8+
Int x, y; // x + yi
9+
10+
Int re() const noexcept { return x; }
11+
Int im() const noexcept { return y; }
12+
13+
explicit GaussianInteger(Int x_ = 0, Int y_ = 0) : x(x_), y(y_) {}
14+
15+
G &operator+=(const G &n) {
16+
x += n.x, y += n.y;
17+
return *this;
18+
}
19+
G &operator-=(const G &n) {
20+
x -= n.x, y -= n.y;
21+
return *this;
22+
}
23+
G &operator*=(const G &n) {
24+
const Int nx = x * n.x - y * n.y, ny = x * n.y + y * n.x;
25+
x = nx, y = ny;
26+
return *this;
27+
}
28+
29+
// Euclidean division
30+
G &operator/=(const G &n) {
31+
const Int d = n.norm();
32+
assert(d != 0);
33+
const Int nx = x * n.x + y * n.y, ny = y * n.x - x * n.y;
34+
auto div_round = [](Int num, Int den) {
35+
return (num >= 0) ? (num + den / 2) / den : (num - den / 2) / den;
36+
};
37+
x = div_round(nx, d), y = div_round(ny, d);
38+
return *this;
39+
}
40+
41+
G &operator%=(const G &n) {
42+
*this -= (*this / n) * n;
43+
return *this;
44+
}
45+
46+
G operator+(const G &n) const { return G(*this) += n; }
47+
G operator-(const G &n) const { return G(*this) -= n; }
48+
G operator*(const G &n) const { return G(*this) *= n; }
49+
G operator/(const G &n) const { return G(*this) /= n; }
50+
G operator%(const G &n) const { return G(*this) %= n; }
51+
52+
Int norm() const { return x * x + y * y; }
53+
54+
G conj() const { return G{x, -y}; }
55+
56+
static G gcd(G a, G b) {
57+
while (b.x != 0 or b.y != 0) {
58+
a %= b;
59+
std::swap(a, b);
60+
}
61+
return a.canonical();
62+
}
63+
friend G gcd(G a, G b) { return G::gcd(a, b); }
64+
65+
bool operator==(const G &n) const { return x == n.x and y == n.y; }
66+
bool operator!=(const G &n) const { return !(*this == n); }
67+
68+
template <class OStream> friend OStream &operator<<(OStream &os, const G &g) {
69+
return os << g.x << (g.y >= 0 ? "+" : "") << g.y << "i";
70+
}
71+
72+
// move to first quadrant (x > 0, y >= 0)
73+
G canonical() const {
74+
if (x > 0 and y >= 0) return *this;
75+
if (x <= 0 and y > 0) return G{y, -x};
76+
if (x < 0 and y <= 0) return G{-x, -y};
77+
return G{-y, x};
78+
}
79+
80+
std::pair<Int, Int> to_pair() const { return {x, y}; }
81+
};

number/gaussian_integer.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
title: Gaussian Integer (ガウス整数)
3+
documentation_of: ./gaussian_integer.hpp
4+
---
5+
6+
ガウス整数を扱う.
7+
8+
## 問題例
9+
10+
- [Library Checker: Gcd of Gaussian Integers](https://judge.yosupo.jp/problem/gcd_of_gaussian_integers)

number/sqrt_mod.hpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#pragma once
2+
#include <algorithm>
3+
#include <type_traits>
4+
5+
// Solve x^2 == a (MOD p) for prime p
6+
// If no solution exists, return -1
7+
template <class Int> Int sqrt_mod(Int a, Int p) {
8+
using Long =
9+
std::conditional_t<std::is_same_v<Int, int>, long long,
10+
std::conditional_t<std::is_same_v<Int, long long>, __int128, void>>;
11+
12+
auto pow = [&](Int x, long long n) {
13+
Int ans = 1, tmp = x;
14+
while (n) {
15+
if (n & 1) ans = (Long)ans * tmp % p;
16+
tmp = (Long)tmp * tmp % p, n /= 2;
17+
}
18+
return ans;
19+
};
20+
if (a == 0) return 0;
21+
22+
a = (a % p + p) % p;
23+
if (p == 2) return a;
24+
if (pow(a, (p - 1) / 2) != 1) return -1;
25+
26+
int b = 1;
27+
while (pow(b, (p - 1) / 2) == 1) ++b;
28+
29+
int e = 0;
30+
Int m = p - 1;
31+
while (m % 2 == 0) m /= 2, ++e;
32+
33+
Int x = pow(a, (m - 1) / 2), y = (Long)x * x % p * a % p;
34+
x = (Long)x * a % p;
35+
Int z = pow(b, m);
36+
while (y != 1) {
37+
int j = 0;
38+
Int t = y;
39+
while (t != 1) t = (Long)t * t % p, ++j;
40+
z = pow(z, 1LL << (e - j - 1));
41+
x = (Long)x * z % p;
42+
z = (Long)z * z % p;
43+
y = (Long)y * z % p;
44+
e = j;
45+
}
46+
return std::min(x, p - x);
47+
}

number/sqrt_mod.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: Square root modulo prime (平方剰余)
3+
documentation_of: ./sqrt_mod.hpp
4+
---
5+
6+
素数 $p$ と整数 $a$ に対して,$x^2 \equiv a \pmod{p}$ を満たす $x$ を求める.解が存在しない場合は $-1$ を返す.Tonelli-Shanks algorithm に基づく.
7+
8+
## 使用方法
9+
10+
```cpp
11+
int a, p;
12+
int x = sqrt_mod<int>(a, p); // x^2 ≡ a (mod p), or -1
13+
14+
long long al, pl;
15+
long long xl = sqrt_mod<long long>(al, pl); // __int128 を内部で使用
16+
```
17+
18+
`Int``int` のとき内部で `long long``long long` のとき `__int128` を乗算のオーバーフロー回避に使用する.
19+
20+
## 問題例
21+
22+
- [Library Checker: Sqrt Mod](https://judge.yosupo.jp/problem/sqrt_mod)
23+
24+
## リンク
25+
26+
- [Tonelli–Shanks algorithm - Wikipedia](https://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm)

number/square_sums.hpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#pragma once
2+
#include <cmath>
3+
4+
#include "sqrt_mod.hpp"
5+
6+
// Fermat's theorem on sums of two squares
7+
// Solve x^2 + y^2 = p for prime p, where p != 3 (mod 4)
8+
template <class Int> std::pair<Int, Int> SumOfTwoSquaresPrime(Int p) {
9+
if (p == 2) return {1, 1};
10+
if (p % 4 != 1) return {-1, -1};
11+
12+
Int r0 = p, r1 = sqrt_mod<Int>(p - 1, p);
13+
for (const Int limit = std::sqrtl(p); r1 > limit;) {
14+
Int next_r = r0 % r1;
15+
r0 = r1, r1 = next_r;
16+
}
17+
18+
return std::minmax(r1, (Int)std::sqrtl(p - r1 * r1));
19+
}

number/square_sums.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
---
2+
title: Sum of two squares (二つの平方数の和)
3+
documentation_of: ./square_sums.hpp
4+
---
5+
6+
素数 $p$ に対して,$x^2 + y^2 = p$ を満たす非負整数の組 $(x, y)$ ($x \le y$)を求める.Fermat の二平方和定理に基づく.
7+
8+
$p = 2$ のとき $(1, 1)$ を,$p \equiv 1 \pmod{4}$ のとき解を返す.$p \equiv 3 \pmod{4}$ のときは解が存在せず $(-1, -1)$ を返す.
9+
10+
## 使用方法
11+
12+
```cpp
13+
int p; // prime
14+
auto [x, y] = SumOfTwoSquaresPrime<int>(p); // x <= y, x^2 + y^2 = p
15+
```
16+
17+
内部で `sqrt_mod` を呼び出し,$-1$ の平方根 $r$ を求めた後,$p$ と $r$ に対するユークリッドの互除法を $r_i \le \sqrt{p}$ となるまで適用する.
18+
19+
## リンク
20+
21+
- [Fermat's theorem on sums of two squares - Wikipedia](https://en.wikipedia.org/wiki/Fermat%27s_theorem_on_sums_of_two_squares)
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/gcd_of_gaussian_integers"
2+
3+
#include "../gaussian_integer.hpp"
4+
5+
#include <iostream>
6+
using namespace std;
7+
8+
int main() {
9+
cin.tie(nullptr), ios::sync_with_stdio(false);
10+
11+
int T;
12+
cin >> T;
13+
while (T--) {
14+
long long a, b, c, d;
15+
cin >> a >> b >> c >> d;
16+
GaussianInteger g1(a, b), g2(c, d);
17+
const auto g = gcd(g1, g2);
18+
cout << g.x << " " << g.y << "\n";
19+
}
20+
}

number/test/sqrt_mod.test.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#define PROBLEM "https://judge.yosupo.jp/problem/sqrt_mod"
2+
#include "../sqrt_mod.hpp"
3+
4+
#include <iostream>
5+
using namespace std;
6+
7+
int main() {
8+
int T;
9+
cin >> T;
10+
while (T--) {
11+
int Y, P;
12+
cin >> Y >> P;
13+
cout << sqrt_mod<int>(Y, P) << '\n';
14+
}
15+
}

0 commit comments

Comments
 (0)