520 lines
15 KiB
C++
520 lines
15 KiB
C++
#include <algorithm>
|
|
#include <iostream>
|
|
#include <initializer_list>
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
const int MAX_ALL_FACTORS = 100000;
|
|
const int algorithm = 2;
|
|
int divisions = 0;
|
|
|
|
//Note: Cyclotomic Polynomials have small coefficients. Not appropriate for general polynomial usage.
|
|
class Term {
|
|
private:
|
|
long m_coefficient;
|
|
long m_exponent;
|
|
|
|
public:
|
|
Term(long c, long e) : m_coefficient(c), m_exponent(e) {
|
|
// empty
|
|
}
|
|
|
|
Term(const Term &t) : m_coefficient(t.m_coefficient), m_exponent(t.m_exponent) {
|
|
// empty
|
|
}
|
|
|
|
long coefficient() const {
|
|
return m_coefficient;
|
|
}
|
|
|
|
long degree() const {
|
|
return m_exponent;
|
|
}
|
|
|
|
Term operator -() const {
|
|
return { -m_coefficient, m_exponent };
|
|
}
|
|
|
|
Term operator *(const Term &rhs) const {
|
|
return { m_coefficient * rhs.m_coefficient, m_exponent + rhs.m_exponent };
|
|
}
|
|
|
|
Term operator +(const Term &rhs) const {
|
|
if (m_exponent != rhs.m_exponent) {
|
|
throw std::runtime_error("Exponents not equal");
|
|
}
|
|
return { m_coefficient + rhs.m_coefficient, m_exponent };
|
|
}
|
|
|
|
friend std::ostream &operator<<(std::ostream &, const Term &);
|
|
};
|
|
|
|
std::ostream &operator<<(std::ostream &os, const Term &t) {
|
|
if (t.m_coefficient == 0) {
|
|
return os << '0';
|
|
}
|
|
if (t.m_exponent == 0) {
|
|
return os << t.m_coefficient;
|
|
}
|
|
if (t.m_coefficient == 1) {
|
|
if (t.m_exponent == 1) {
|
|
return os << 'x';
|
|
}
|
|
return os << "x^" << t.m_exponent;
|
|
}
|
|
if (t.m_coefficient == -1) {
|
|
if (t.m_exponent == 1) {
|
|
return os << "-x";
|
|
}
|
|
return os << "-x^" << t.m_exponent;
|
|
}
|
|
if (t.m_exponent == 1) {
|
|
return os << t.m_coefficient << 'x';
|
|
}
|
|
return os << t.m_coefficient << "x^" << t.m_exponent;
|
|
}
|
|
|
|
class Polynomial {
|
|
public:
|
|
std::vector<Term> polynomialTerms;
|
|
|
|
Polynomial() {
|
|
polynomialTerms.push_back({ 0, 0 });
|
|
}
|
|
|
|
Polynomial(std::initializer_list<int> values) {
|
|
if (values.size() % 2 != 0) {
|
|
throw std::runtime_error("Length must be even.");
|
|
}
|
|
|
|
bool ready = false;
|
|
long t;
|
|
for (auto v : values) {
|
|
if (ready) {
|
|
polynomialTerms.push_back({ t, v });
|
|
} else {
|
|
t = v;
|
|
}
|
|
ready = !ready;
|
|
}
|
|
std::sort(
|
|
polynomialTerms.begin(), polynomialTerms.end(),
|
|
[](const Term &t, const Term &u) {
|
|
return u.degree() < t.degree();
|
|
}
|
|
);
|
|
}
|
|
|
|
Polynomial(const std::vector<Term> &termList) {
|
|
if (termList.size() == 0) {
|
|
polynomialTerms.push_back({ 0, 0 });
|
|
} else {
|
|
for (auto t : termList) {
|
|
if (t.coefficient() != 0) {
|
|
polynomialTerms.push_back(t);
|
|
}
|
|
}
|
|
if (polynomialTerms.size() == 0) {
|
|
polynomialTerms.push_back({ 0, 0 });
|
|
}
|
|
std::sort(
|
|
polynomialTerms.begin(), polynomialTerms.end(),
|
|
[](const Term &t, const Term &u) {
|
|
return u.degree() < t.degree();
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
Polynomial(const Polynomial &p) : Polynomial(p.polynomialTerms) {
|
|
// empty
|
|
}
|
|
|
|
long leadingCoefficient() const {
|
|
return polynomialTerms[0].coefficient();
|
|
}
|
|
|
|
long degree() const {
|
|
return polynomialTerms[0].degree();
|
|
}
|
|
|
|
bool hasCoefficientAbs(int coeff) {
|
|
for (auto term : polynomialTerms) {
|
|
if (abs(term.coefficient()) == coeff) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Polynomial operator+(const Term &term) const {
|
|
std::vector<Term> termList;
|
|
bool added = false;
|
|
for (size_t index = 0; index < polynomialTerms.size(); index++) {
|
|
auto currentTerm = polynomialTerms[index];
|
|
if (currentTerm.degree() == term.degree()) {
|
|
added = true;
|
|
if (currentTerm.coefficient() + term.coefficient() != 0) {
|
|
termList.push_back(currentTerm + term);
|
|
}
|
|
} else {
|
|
termList.push_back(currentTerm);
|
|
}
|
|
}
|
|
if (!added) {
|
|
termList.push_back(term);
|
|
}
|
|
return Polynomial(termList);
|
|
}
|
|
|
|
Polynomial operator*(const Term &term) const {
|
|
std::vector<Term> termList;
|
|
for (size_t index = 0; index < polynomialTerms.size(); index++) {
|
|
auto currentTerm = polynomialTerms[index];
|
|
termList.push_back(currentTerm * term);
|
|
}
|
|
return Polynomial(termList);
|
|
}
|
|
|
|
Polynomial operator+(const Polynomial &p) const {
|
|
std::vector<Term> termList;
|
|
int thisCount = polynomialTerms.size();
|
|
int polyCount = p.polynomialTerms.size();
|
|
while (thisCount > 0 || polyCount > 0) {
|
|
if (thisCount == 0) {
|
|
auto polyTerm = p.polynomialTerms[polyCount - 1];
|
|
termList.push_back(polyTerm);
|
|
polyCount--;
|
|
} else if (polyCount == 0) {
|
|
auto thisTerm = polynomialTerms[thisCount - 1];
|
|
termList.push_back(thisTerm);
|
|
thisCount--;
|
|
} else {
|
|
auto polyTerm = p.polynomialTerms[polyCount - 1];
|
|
auto thisTerm = polynomialTerms[thisCount - 1];
|
|
if (thisTerm.degree() == polyTerm.degree()) {
|
|
auto t = thisTerm + polyTerm;
|
|
if (t.coefficient() != 0) {
|
|
termList.push_back(t);
|
|
}
|
|
thisCount--;
|
|
polyCount--;
|
|
} else if (thisTerm.degree() < polyTerm.degree()) {
|
|
termList.push_back(thisTerm);
|
|
thisCount--;
|
|
} else {
|
|
termList.push_back(polyTerm);
|
|
polyCount--;
|
|
}
|
|
}
|
|
}
|
|
return Polynomial(termList);
|
|
}
|
|
|
|
Polynomial operator/(const Polynomial &v) {
|
|
divisions++;
|
|
|
|
Polynomial q;
|
|
Polynomial r(*this);
|
|
long lcv = v.leadingCoefficient();
|
|
long dv = v.degree();
|
|
while (r.degree() >= v.degree()) {
|
|
long lcr = r.leadingCoefficient();
|
|
long s = lcr / lcv;
|
|
Term term(s, r.degree() - dv);
|
|
q = q + term;
|
|
r = r + v * -term;
|
|
}
|
|
|
|
return q;
|
|
}
|
|
|
|
friend std::ostream &operator<<(std::ostream &, const Polynomial &);
|
|
};
|
|
|
|
std::ostream &operator<<(std::ostream &os, const Polynomial &p) {
|
|
auto it = p.polynomialTerms.cbegin();
|
|
auto end = p.polynomialTerms.cend();
|
|
if (it != end) {
|
|
os << *it;
|
|
it = std::next(it);
|
|
}
|
|
while (it != end) {
|
|
if (it->coefficient() > 0) {
|
|
os << " + " << *it;
|
|
} else {
|
|
os << " - " << -*it;
|
|
}
|
|
it = std::next(it);
|
|
}
|
|
return os;
|
|
}
|
|
|
|
std::vector<int> getDivisors(int number) {
|
|
std::vector<int> divisiors;
|
|
long root = (long)sqrt(number);
|
|
for (int i = 1; i <= root; i++) {
|
|
if (number % i == 0) {
|
|
divisiors.push_back(i);
|
|
int div = number / i;
|
|
if (div != i && div != number) {
|
|
divisiors.push_back(div);
|
|
}
|
|
}
|
|
}
|
|
return divisiors;
|
|
}
|
|
|
|
std::map<int, std::map<int, int>> allFactors;
|
|
|
|
std::map<int, int> getFactors(int number) {
|
|
if (allFactors.find(number) != allFactors.end()) {
|
|
return allFactors[number];
|
|
}
|
|
|
|
std::map<int, int> factors;
|
|
if (number % 2 == 0) {
|
|
auto factorsDivTwo = getFactors(number / 2);
|
|
factors.insert(factorsDivTwo.begin(), factorsDivTwo.end());
|
|
if (factors.find(2) != factors.end()) {
|
|
factors[2]++;
|
|
} else {
|
|
factors.insert(std::make_pair(2, 1));
|
|
}
|
|
if (number < MAX_ALL_FACTORS) {
|
|
allFactors.insert(std::make_pair(number, factors));
|
|
}
|
|
return factors;
|
|
}
|
|
long root = (long)sqrt(number);
|
|
long i = 3;
|
|
while (i <= root) {
|
|
if (number % i == 0) {
|
|
auto factorsDivI = getFactors(number / i);
|
|
factors.insert(factorsDivI.begin(), factorsDivI.end());
|
|
if (factors.find(i) != factors.end()) {
|
|
factors[i]++;
|
|
} else {
|
|
factors.insert(std::make_pair(i, 1));
|
|
}
|
|
if (number < MAX_ALL_FACTORS) {
|
|
allFactors.insert(std::make_pair(number, factors));
|
|
}
|
|
return factors;
|
|
}
|
|
i += 2;
|
|
}
|
|
factors.insert(std::make_pair(number, 1));
|
|
if (number < MAX_ALL_FACTORS) {
|
|
allFactors.insert(std::make_pair(number, factors));
|
|
}
|
|
return factors;
|
|
}
|
|
|
|
std::map<int, Polynomial> COMPUTED;
|
|
Polynomial cyclotomicPolynomial(int n) {
|
|
if (COMPUTED.find(n) != COMPUTED.end()) {
|
|
return COMPUTED[n];
|
|
}
|
|
|
|
if (n == 1) {
|
|
// Polynomial: x - 1
|
|
Polynomial p({ 1, 1, -1, 0 });
|
|
COMPUTED.insert(std::make_pair(1, p));
|
|
return p;
|
|
}
|
|
|
|
auto factors = getFactors(n);
|
|
if (factors.find(n) != factors.end()) {
|
|
// n prime
|
|
std::vector<Term> termList;
|
|
for (int index = 0; index < n; index++) {
|
|
termList.push_back({ 1, index });
|
|
}
|
|
|
|
Polynomial cyclo(termList);
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else if (factors.size() == 2 && factors.find(2) != factors.end() && factors[2] == 1 && factors.find(n / 2) != factors.end() && factors[n / 2] == 1) {
|
|
// n = 2p
|
|
int prime = n / 2;
|
|
std::vector<Term> termList;
|
|
int coeff = -1;
|
|
|
|
for (int index = 0; index < prime; index++) {
|
|
coeff *= -1;
|
|
termList.push_back({ coeff, index });
|
|
}
|
|
|
|
Polynomial cyclo(termList);
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else if (factors.size() == 1 && factors.find(2) != factors.end()) {
|
|
// n = 2^h
|
|
int h = factors[2];
|
|
std::vector<Term> termList;
|
|
termList.push_back({ 1, (int)pow(2, h - 1) });
|
|
termList.push_back({ 1, 0 });
|
|
|
|
Polynomial cyclo(termList);
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else if (factors.size() == 1 && factors.find(n) != factors.end()) {
|
|
// n = p^k
|
|
int p = 0;
|
|
int k = 0;
|
|
for (auto iter = factors.begin(); iter != factors.end(); ++iter) {
|
|
p = iter->first;
|
|
k = iter->second;
|
|
}
|
|
std::vector<Term> termList;
|
|
for (int index = 0; index < p; index++) {
|
|
termList.push_back({ 1, index * (int)pow(p, k - 1) });
|
|
}
|
|
|
|
Polynomial cyclo(termList);
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else if (factors.size() == 2 && factors.find(2) != factors.end()) {
|
|
// n = 2^h * p^k
|
|
int p = 0;
|
|
for (auto iter = factors.begin(); iter != factors.end(); ++iter) {
|
|
if (iter->first != 2) {
|
|
p = iter->first;
|
|
}
|
|
}
|
|
|
|
std::vector<Term> termList;
|
|
int coeff = -1;
|
|
int twoExp = (int)pow(2, factors[2] - 1);
|
|
int k = factors[p];
|
|
for (int index = 0; index < p; index++) {
|
|
coeff *= -1;
|
|
termList.push_back({ coeff, index * twoExp * (int)pow(p, k - 1) });
|
|
}
|
|
|
|
Polynomial cyclo(termList);
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else if (factors.find(2) != factors.end() && ((n / 2) % 2 == 1) && (n / 2) > 1) {
|
|
// CP(2m)[x] = CP(-m)[x], n odd integer > 1
|
|
auto cycloDiv2 = cyclotomicPolynomial(n / 2);
|
|
std::vector<Term> termList;
|
|
for (auto term : cycloDiv2.polynomialTerms) {
|
|
if (term.degree() % 2 == 0) {
|
|
termList.push_back(term);
|
|
} else {
|
|
termList.push_back(-term);
|
|
}
|
|
}
|
|
|
|
Polynomial cyclo(termList);
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
}
|
|
|
|
// General Case
|
|
|
|
if (algorithm == 0) {
|
|
// slow - uses basic definition
|
|
auto divisors = getDivisors(n);
|
|
// Polynomial: (x^n - 1)
|
|
Polynomial cyclo({ 1, n, -1, 0 });
|
|
for (auto i : divisors) {
|
|
auto p = cyclotomicPolynomial(i);
|
|
cyclo = cyclo / p;
|
|
}
|
|
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else if (algorithm == 1) {
|
|
// Faster. Remove Max divisor (and all divisors of max divisor) - only one divide for all divisors of Max Divisor
|
|
auto divisors = getDivisors(n);
|
|
int maxDivisor = INT_MIN;
|
|
for (auto div : divisors) {
|
|
maxDivisor = std::max(maxDivisor, div);
|
|
}
|
|
std::vector<int> divisorExceptMax;
|
|
for (auto div : divisors) {
|
|
if (maxDivisor % div != 0) {
|
|
divisorExceptMax.push_back(div);
|
|
}
|
|
}
|
|
|
|
// Polynomial: ( x^n - 1 ) / ( x^m - 1 ), where m is the max divisor
|
|
auto cyclo = Polynomial({ 1, n, -1, 0 }) / Polynomial({ 1, maxDivisor, -1, 0 });
|
|
for (int i : divisorExceptMax) {
|
|
auto p = cyclotomicPolynomial(i);
|
|
cyclo = cyclo / p;
|
|
}
|
|
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else if (algorithm == 2) {
|
|
// Fastest
|
|
// Let p ; q be primes such that p does not divide n, and q q divides n.
|
|
// Then CP(np)[x] = CP(n)[x^p] / CP(n)[x]
|
|
int m = 1;
|
|
auto cyclo = cyclotomicPolynomial(m);
|
|
std::vector<int> primes;
|
|
for (auto iter = factors.begin(); iter != factors.end(); ++iter) {
|
|
primes.push_back(iter->first);
|
|
}
|
|
std::sort(primes.begin(), primes.end());
|
|
for (auto prime : primes) {
|
|
// CP(m)[x]
|
|
auto cycloM = cyclo;
|
|
// Compute CP(m)[x^p].
|
|
std::vector<Term> termList;
|
|
for (auto t : cycloM.polynomialTerms) {
|
|
termList.push_back({ t.coefficient(), t.degree() * prime });
|
|
}
|
|
cyclo = Polynomial(termList) / cycloM;
|
|
m = m * prime;
|
|
}
|
|
// Now, m is the largest square free divisor of n
|
|
int s = n / m;
|
|
// Compute CP(n)[x] = CP(m)[x^s]
|
|
std::vector<Term> termList;
|
|
for (auto t : cyclo.polynomialTerms) {
|
|
termList.push_back({ t.coefficient(), t.degree() * s });
|
|
}
|
|
|
|
cyclo = Polynomial(termList);
|
|
COMPUTED.insert(std::make_pair(n, cyclo));
|
|
return cyclo;
|
|
} else {
|
|
throw std::runtime_error("Invalid algorithm");
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
// initialization
|
|
std::map<int, int> factors;
|
|
factors.insert(std::make_pair(2, 1));
|
|
allFactors.insert(std::make_pair(2, factors));
|
|
|
|
// rest of main
|
|
std::cout << "Task 1: cyclotomic polynomials for n <= 30:\n";
|
|
for (int i = 1; i <= 30; i++) {
|
|
auto p = cyclotomicPolynomial(i);
|
|
std::cout << "CP[" << i << "] = " << p << '\n';
|
|
}
|
|
|
|
std::cout << "Task 2: Smallest cyclotomic polynomial with n or -n as a coefficient:\n";
|
|
int n = 0;
|
|
for (int i = 1; i <= 10; i++) {
|
|
while (true) {
|
|
n++;
|
|
auto cyclo = cyclotomicPolynomial(n);
|
|
if (cyclo.hasCoefficientAbs(i)) {
|
|
std::cout << "CP[" << n << "] has coefficient with magnitude = " << i << '\n';
|
|
n--;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|