245 lines
6 KiB
C
245 lines
6 KiB
C
![]() |
#include <vector>
|
||
|
#include <deque>
|
||
|
#include <iostream>
|
||
|
#include <algorithm>
|
||
|
using namespace std;
|
||
|
|
||
|
template<unsigned N, long long i>
|
||
|
struct is_prime_ {
|
||
|
static const bool value = !(N % i == 0) && is_prime_<N, (i + 1) * (i + 1) <= N ? (N % i == 0 ? 1 : i + 1) : -1>::value;
|
||
|
};
|
||
|
|
||
|
template<unsigned N>
|
||
|
struct is_prime_<N, 1LL> {
|
||
|
static const bool value = false;
|
||
|
};
|
||
|
|
||
|
template<unsigned N>
|
||
|
struct is_prime_<N, -1LL> {
|
||
|
static const bool value = true;
|
||
|
};
|
||
|
|
||
|
template<unsigned N>
|
||
|
struct is_prime {
|
||
|
static const bool value = (is_prime_<N, 2>::value && (N != 1)) || (N == 2);
|
||
|
};
|
||
|
|
||
|
template<unsigned N>
|
||
|
static const bool is_prime_v = is_prime<N>::value;
|
||
|
|
||
|
template<unsigned N, unsigned long long i, bool find_divider>
|
||
|
struct is_power_of_prime_ {
|
||
|
static const bool value = is_power_of_prime_<N % i == 0 ? N / i : (find_divider ? 0 : (i * i >= N ? 1 : N)), N % i == 0 ? i : i + 2, N % i == 0>::value;
|
||
|
};
|
||
|
|
||
|
template<unsigned long long i, bool find_divider>
|
||
|
struct is_power_of_prime_<1, i, find_divider> {
|
||
|
static const bool value = true;
|
||
|
};
|
||
|
|
||
|
template<unsigned long long i, bool find_divider>
|
||
|
struct is_power_of_prime_<0, i, find_divider> {
|
||
|
static const bool value = false;
|
||
|
};
|
||
|
|
||
|
template<unsigned N>
|
||
|
struct is_power_of_prime {
|
||
|
static const bool value = is_power_of_prime_<N, 3, false>::value;
|
||
|
};
|
||
|
|
||
|
template<unsigned N>
|
||
|
struct has_primitive_root {
|
||
|
static const bool value = is_power_of_prime<(N == 4 || N == 2 ? 1 : (N % 4 == 0 ? 0 : (N % 2 == 0 ? N / 2 : N)))>::value;
|
||
|
};
|
||
|
|
||
|
template<unsigned N>
|
||
|
static const bool has_primitive_root_v = has_primitive_root<N>::value;
|
||
|
|
||
|
template<bool T>
|
||
|
void my_static_assert() {
|
||
|
int* a = new int [T ? 1 : -1];
|
||
|
delete[] a;
|
||
|
}
|
||
|
|
||
|
unsigned phi(unsigned n) {
|
||
|
unsigned result = n;
|
||
|
for (int i = 2; i * i <= n; ++i) {
|
||
|
if (n % i == 0) {
|
||
|
while (n % i == 0) {
|
||
|
n /= i;
|
||
|
}
|
||
|
result -= result / i;
|
||
|
}
|
||
|
}
|
||
|
if (n > 1)
|
||
|
result -= result / n;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
class Residue {
|
||
|
public:
|
||
|
Residue() = default;
|
||
|
Residue(const Residue<Modulus>& another) = default;
|
||
|
explicit Residue(long long x) : x(x) { Normilize(); }
|
||
|
explicit operator int() const { return x; }
|
||
|
|
||
|
bool operator==(const Residue<Modulus>& another) {
|
||
|
return x == another.x;
|
||
|
}
|
||
|
|
||
|
bool operator!=(const Residue<Modulus>& another) {
|
||
|
return !(*this == another);
|
||
|
}
|
||
|
|
||
|
Residue& operator+=(const Residue<Modulus>& another);
|
||
|
Residue& operator-=(const Residue<Modulus>& another);
|
||
|
Residue& operator*=(const Residue<Modulus>& another);
|
||
|
Residue& operator/=(const Residue<Modulus>& another);
|
||
|
|
||
|
Residue getInverse() const {
|
||
|
cerr << "getInverse " << x << " " << Modulus << endl;
|
||
|
my_static_assert<is_prime_v<Modulus>>();
|
||
|
return Residue(pow(x, Modulus - 2));
|
||
|
}
|
||
|
|
||
|
unsigned order() const {
|
||
|
cerr << "order " << x << " " << Modulus << endl;
|
||
|
|
||
|
unsigned phi = ::phi(Modulus);
|
||
|
unsigned copy_phi = phi;
|
||
|
std::vector<unsigned> first = {1}, second = {phi};
|
||
|
for (unsigned i = 2; i * i <= phi; ++i) {
|
||
|
if (copy_phi % i == 0) {
|
||
|
first.push_back(i);
|
||
|
second.push_back(phi / i);
|
||
|
}
|
||
|
}
|
||
|
std::reverse(second.begin(), second.end());
|
||
|
first.insert(first.end(), second.begin(), second.end());
|
||
|
|
||
|
for (unsigned i: first) {
|
||
|
if (pow(i) == Residue(1)) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static Residue getPrimitiveRoot() {
|
||
|
cerr << "getPrimitiveRoot " << Modulus << endl;
|
||
|
my_static_assert<has_primitive_root_v<Modulus>>();
|
||
|
if (Modulus == 2) {
|
||
|
return Residue(1);
|
||
|
} else if (Modulus == 4) {
|
||
|
return Residue(3);
|
||
|
}
|
||
|
unsigned phi = ::phi(Modulus);
|
||
|
unsigned copy_phi = phi;
|
||
|
std::vector<unsigned> deviders;
|
||
|
for (unsigned i = 2; i * i <= phi; ++i) {
|
||
|
if (copy_phi % i == 0) {
|
||
|
deviders.push_back(i);
|
||
|
deviders.push_back(phi / i);
|
||
|
while (copy_phi % i == 0) copy_phi /= i;
|
||
|
}
|
||
|
}
|
||
|
if (copy_phi > 1)
|
||
|
deviders.push_back(copy_phi);
|
||
|
|
||
|
for (unsigned res = 2; res < phi; ++res) {
|
||
|
bool ok = pow(res, phi) == 1;
|
||
|
for (unsigned i: deviders) {
|
||
|
ok &= pow(res, i) != 1;
|
||
|
}
|
||
|
if (ok) {
|
||
|
return Residue(res);
|
||
|
}
|
||
|
}
|
||
|
return Residue(phi);
|
||
|
}
|
||
|
|
||
|
Residue<Modulus> pow(unsigned degree) const;
|
||
|
private:
|
||
|
static int pow(int x, unsigned degree);
|
||
|
|
||
|
void Normilize() {
|
||
|
x %= static_cast<long long>(Modulus);
|
||
|
if (x < 0)
|
||
|
x += Modulus;
|
||
|
}
|
||
|
long long x;
|
||
|
};
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus>& Residue<Modulus>::operator+=(const Residue<Modulus>& another) {
|
||
|
x += another.x;
|
||
|
Normilize();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus>& Residue<Modulus>::operator-=(const Residue<Modulus>& another) {
|
||
|
x -= another.x;
|
||
|
Normilize();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus>& Residue<Modulus>::operator*=(const Residue<Modulus>& another) {
|
||
|
x *= another.x;
|
||
|
Normilize();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus>& Residue<Modulus>::operator/=(const Residue<Modulus>& another) {
|
||
|
*this *= another.getInverse();
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus> operator+(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||
|
Residue<Modulus> tmp(a);
|
||
|
tmp += b;
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus> operator-(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||
|
Residue<Modulus> tmp(a);
|
||
|
tmp -= b;
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus> operator*(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||
|
Residue<Modulus> tmp(a);
|
||
|
tmp *= b;
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus> operator/(const Residue<Modulus>& a, const Residue<Modulus>& b) {
|
||
|
Residue<Modulus> tmp(a);
|
||
|
tmp /= b;
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
Residue<Modulus> Residue<Modulus>::pow(unsigned degree) const {
|
||
|
|
||
|
return Residue(pow(x, degree));
|
||
|
}
|
||
|
|
||
|
template<unsigned Modulus>
|
||
|
int Residue<Modulus>::pow(int x, unsigned degree) {
|
||
|
|
||
|
if (degree == 0) {
|
||
|
return 1;
|
||
|
} else {
|
||
|
long long t = pow(x, degree / 2);
|
||
|
return (((t * t) % static_cast<long long>(Modulus)) * (degree % 2 == 0 ? 1 : x)) % static_cast<long long>(Modulus);
|
||
|
}
|
||
|
}
|