Skip to content

Commit cfaed5c

Browse files
committed
Two squares decomposition + test
1 parent 4ae7587 commit cfaed5c

File tree

2 files changed

+105
-0
lines changed

2 files changed

+105
-0
lines changed

cp-algo/math/two_squares.hpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#ifndef CP_ALGO_MATH_TWO_SQUARES_HPP
2+
#define CP_ALGO_MATH_TWO_SQUARES_HPP
3+
#include "number_theory.hpp"
4+
#include <complex>
5+
#include <utility>
6+
#include <vector>
7+
#include <map>
8+
namespace cp_algo::math {
9+
using gaussint = std::complex<int64_t>;
10+
gaussint two_squares_prime_any(int64_t p) {
11+
if(p == 2) {
12+
return gaussint(1, 1);
13+
}
14+
assert(p % 4 == 1);
15+
using base = dynamic_modint;
16+
return base::with_mod(p, [&](){
17+
base g = primitive_root(p);
18+
int64_t i = bpow(g, (p - 1) / 4).getr();
19+
int64_t q0 = 1, q1 = 0;
20+
int64_t r = i, m = p;
21+
// TODO: Use library contfrac?
22+
do {
23+
int64_t d = r / m;
24+
q0 = std::exchange(q1, q0 + d * q1);
25+
r = std::exchange(m, r % m);
26+
} while(q1 < p / q1);
27+
return gaussint(q0, (base(i) * base(q0)).rem());
28+
});
29+
}
30+
31+
std::vector<gaussint> two_squares_all(int64_t n) {
32+
if(n == 0) {
33+
return {0};
34+
}
35+
auto primes = factorize(n);
36+
std::map<int64_t, int> cnt;
37+
for(auto p: primes) {
38+
cnt[p]++;
39+
}
40+
// 1, -1, i, -i
41+
std::vector<gaussint> res = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
42+
for(auto [p, c]: cnt) {
43+
std::vector<gaussint> nres;
44+
if(p % 4 == 3) {
45+
if(c % 2 == 0) {
46+
auto mul = bpow(gaussint(p), c / 2);
47+
for(auto p: res) {
48+
nres.push_back(p * mul);
49+
}
50+
}
51+
} else if(p % 4 == 1) {
52+
gaussint base = two_squares_prime_any(p);
53+
for(int i = 0; i <= c; i++) {
54+
auto mul = bpow(base, i) * bpow(conj(base), c - i);
55+
for(auto p: res) {
56+
nres.push_back(p * mul);
57+
}
58+
}
59+
} else if(p % 4 == 2) {
60+
auto mul = bpow(gaussint(1, 1), c);
61+
for(auto p: res) {
62+
nres.push_back(p * mul);
63+
}
64+
}
65+
res = nres;
66+
}
67+
std::vector<gaussint> nres;
68+
for(auto p: res) {
69+
if(p.real() >= 0 && p.imag() >= 0) {
70+
nres.push_back(p);
71+
}
72+
}
73+
return nres;
74+
}
75+
}
76+
#endif // CP_ALGO_MATH_TWO_SQUARES_HPP
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// @brief Represent A Number As Two Square Sum
2+
#define PROBLEM "https://judge.yosupo.jp/problem/two_square_sum"
3+
#pragma GCC optimize("Ofast,unroll-loops")
4+
#include "cp-algo/math/two_squares.hpp"
5+
#include <bits/stdc++.h>
6+
7+
using namespace std;
8+
using namespace cp_algo::math;
9+
10+
void solve() {
11+
int64_t n;
12+
cin >> n;
13+
auto res = two_squares_all(n);
14+
cout << size(res) << "\n";
15+
for(auto p: res) {
16+
cout << p.real() << ' ' << p.imag() << "\n";
17+
}
18+
}
19+
20+
signed main() {
21+
//freopen("input.txt", "r", stdin);
22+
ios::sync_with_stdio(0);
23+
cin.tie(0);
24+
int t = 1;
25+
cin >> t;
26+
while(t--) {
27+
solve();
28+
}
29+
}

0 commit comments

Comments
 (0)