#include <iostream>
#include <vector>
// Возведение a в степень b по модулю p
int powmod (int a, int b, int p)
{
int res = 1;
while (b)
if (b & 1)
res = int ((long long) res * a % p), --b;
else
a = int ((long long) a * a % p), b >>= 1;
return res;
}
// Вычисление обратного к a элемента по модулю n
int inverse(int a, int n)
{
int b0 = n, t, q;
int x0 = 0, x1 = 1;
if (n == 1) return 1;
while (a > 1) {
q = a / n;
t = n, n = a % n, a = t;
t = x0, x0 = x1 - q * x0, x1 = t;
}
if (x1 < 0) x1 += b0;
return x1;
}
// Вычисление примитивного корня числового поля
// (Стандартный DFT использует комплексные числа, поэтому изменим алгоритм для работы в Zp)
// Ограничение: p - простое
int generator (int p)
{
std::vector<int> fact;
int phi = p - 1, n = phi;
for (int i = 2; i * i <= n; ++i)
if (n % i == 0)
{
fact.push_back (i);
while (n % i == 0)
n /= i;
}
if (n > 1)
fact.push_back (n);
for (int res = 2; res <= p; ++res) {
bool ok = true;
for (size_t i = 0; i < fact.size() && ok; ++i)
ok &= powmod (res, phi / fact[i], p) != 1;
if (ok) return res;
}
return -1;
}
// Реализация прямого DFT над Zp
// Принимает вектор длины n, эта длина используется при вычислении
// примитивного корня поля
// Вычисления приводятся по mod (n + 1), т.к. длина вектора на 1 меньше p,
// где p: Zp
std::vector<int> forward_dft (std::vector<int>& a)
{
std::vector<int> result;
int prim_root = generator (a.size()); // Корень степени n из единицы
for (int i = 0; i < a.size(); i++)
{
int sum = 0;
int power = 0;
for (int j = 0; j < a.size(); j++)
{
int power_of_root = powmod (prim_root, power, a.size() + 1);
sum += power_of_root * a[j]; // Накапливаем сумму произведений элементов строки матрицы на вектор
power += i; // Увеличиваем степень, в которую возводится примитивный корень из единицы
}
result.push_back(sum % (a.size() + 1)); // Набрали сумму, приводим по mod p, p = длина вектора + 1
}
return result;
}
std::vector<int> inversed_dft (std::vector<int>& a)
{
std::vector<int> result;
int prim_root = generator (a.size()); // Корень степени n из единицы
int inv_prim_root = inverse (prim_root, a.size() + 1); // Обратный к найденному примитивному корню
int inv_n = inverse(a.size(), a.size() + 1);
for (int i = 0; i < a.size(); i++)
{
int sum = 0;
int power = 0;
for (int j = 0; j < a.size(); j++)
{
int power_of_inv_root = powmod (inv_prim_root, power, a.size() + 1);
sum += inv_n * power_of_inv_root * a[j]; // Накапливаем сумму произведений элементов строки матрицы на вектор
power += i; // Увеличиваем степень, в которую возводится примитивный корень из единицы
}
result.push_back(sum % (a.size() + 1)); // Набрали сумму, приводим по mod p, p = длина вектора + 1
}
return result;
}
// Вывод сообщений
void debug(const std::string& str, std::vector<int>& v)
{
std::cout << "[DEBUG]:" << str << std::endl;
for(int i = 0; i < v.size(); i++)
std::cout << v[i] << " ";
std::cout << std::endl;
}
int main()
{
int a[] = {4, 3, 2, 1};
std::vector<int> v(std::begin(a), std::end(a));
debug("Source vector", v);
std::vector<int> result = forward_dft(v);
debug("Forward DFT", result);
result = inversed_dft(result);
debug("Inversed DFT", result);
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8dmVjdG9yPgoKLy8g0JLQvtC30LLQtdC00LXQvdC40LUgYSDQsiDRgdGC0LXQv9C10L3RjCBiINC/0L4g0LzQvtC00YPQu9GOIHAKaW50IHBvd21vZCAoaW50IGEsIGludCBiLCBpbnQgcCkKewoJaW50IHJlcyA9IDE7Cgl3aGlsZSAoYikKCQlpZiAoYiAmIDEpCgkJCXJlcyA9IGludCAoKGxvbmcgbG9uZykgcmVzICogYSAlIHApLCAgLS1iOwoJCWVsc2UKCQkJYSA9IGludCAoKGxvbmcgbG9uZykgYSAqIGEgJSBwKSwgIGIgPj49IDE7CglyZXR1cm4gcmVzOwp9CgovLyDQktGL0YfQuNGB0LvQtdC90LjQtSDQvtCx0YDQsNGC0L3QvtCz0L4g0LogYSDRjdC70LXQvNC10L3RgtCwINC/0L4g0LzQvtC00YPQu9GOIG4KaW50IGludmVyc2UoaW50IGEsIGludCBuKQp7CglpbnQgYjAgPSBuLCB0LCBxOwoJaW50IHgwID0gMCwgeDEgPSAxOwoJaWYgKG4gPT0gMSkgcmV0dXJuIDE7Cgl3aGlsZSAoYSA+IDEpIHsKCQlxID0gYSAvIG47CgkJdCA9IG4sIG4gPSBhICUgbiwgYSA9IHQ7CgkJdCA9IHgwLCB4MCA9IHgxIC0gcSAqIHgwLCB4MSA9IHQ7Cgl9CglpZiAoeDEgPCAwKSB4MSArPSBiMDsKCXJldHVybiB4MTsKfQoKLy8g0JLRi9GH0LjRgdC70LXQvdC40LUg0L/RgNC40LzQuNGC0LjQstC90L7Qs9C+INC60L7RgNC90Y8g0YfQuNGB0LvQvtCy0L7Qs9C+INC/0L7Qu9GPCi8vICjQodGC0LDQvdC00LDRgNGC0L3Ri9C5IERGVCDQuNGB0L/QvtC70YzQt9GD0LXRgiDQutC+0LzQv9C70LXQutGB0L3Ri9C1INGH0LjRgdC70LAsINC/0L7RjdGC0L7QvNGDINC40LfQvNC10L3QuNC8INCw0LvQs9C+0YDQuNGC0Lwg0LTQu9GPINGA0LDQsdC+0YLRiyDQsiBacCkKLy8g0J7Qs9GA0LDQvdC40YfQtdC90LjQtTogcCAtINC/0YDQvtGB0YLQvtC1CmludCBnZW5lcmF0b3IgKGludCBwKQp7CglzdGQ6OnZlY3RvcjxpbnQ+IGZhY3Q7CglpbnQgcGhpID0gcCAtIDEsICBuID0gcGhpOwoJZm9yIChpbnQgaSA9IDI7IGkgKiBpIDw9IG47ICsraSkKCQlpZiAobiAlIGkgPT0gMCkKCQl7CgkJCWZhY3QucHVzaF9iYWNrIChpKTsKCQkJd2hpbGUgKG4gJSBpID09IDApCgkJCQluIC89IGk7CgkJfQoJaWYgKG4gPiAxKQoJCWZhY3QucHVzaF9iYWNrIChuKTsKIAoJZm9yIChpbnQgcmVzID0gMjsgcmVzIDw9IHA7ICsrcmVzKSB7CgkJYm9vbCBvayA9IHRydWU7CgkJZm9yIChzaXplX3QgaSA9IDA7IGkgPCBmYWN0LnNpemUoKSAmJiBvazsgKytpKQoJCQlvayAmPSBwb3dtb2QgKHJlcywgcGhpIC8gZmFjdFtpXSwgcCkgIT0gMTsKCQlpZiAob2spICByZXR1cm4gcmVzOwoJfQoJcmV0dXJuIC0xOwp9CgovLyDQoNC10LDQu9C40LfQsNGG0LjRjyDQv9GA0Y/QvNC+0LPQviBERlQg0L3QsNC0IFpwCi8vINCf0YDQuNC90LjQvNCw0LXRgiDQstC10LrRgtC+0YAg0LTQu9C40L3RiyBuLCDRjdGC0LAg0LTQu9C40L3QsCDQuNGB0L/QvtC70YzQt9GD0LXRgtGB0Y8g0L/RgNC4INCy0YvRh9C40YHQu9C10L3QuNC4Ci8vINC/0YDQuNC80LjRgtC40LLQvdC+0LPQviDQutC+0YDQvdGPINC/0L7Qu9GPCi8vINCS0YvRh9C40YHQu9C10L3QuNGPINC/0YDQuNCy0L7QtNGP0YLRgdGPINC/0L4gbW9kIChuICsgMSksINGCLtC6LiDQtNC70LjQvdCwINCy0LXQutGC0L7RgNCwINC90LAgMSDQvNC10L3RjNGI0LUgcCwKLy8g0LPQtNC1IHA6IFpwCnN0ZDo6dmVjdG9yPGludD4gZm9yd2FyZF9kZnQgKHN0ZDo6dmVjdG9yPGludD4mIGEpCnsKCXN0ZDo6dmVjdG9yPGludD4gcmVzdWx0OwoJaW50IHByaW1fcm9vdCA9IGdlbmVyYXRvciAoYS5zaXplKCkpOwkvLyDQmtC+0YDQtdC90Ywg0YHRgtC10L/QtdC90LggbiDQuNC3INC10LTQuNC90LjRhtGLCglmb3IgKGludCBpID0gMDsgaSA8IGEuc2l6ZSgpOyBpKyspCgl7CgkJaW50IHN1bSAgID0gMDsKCQlpbnQgcG93ZXIgPSAwOwoJCWZvciAoaW50IGogPSAwOyBqIDwgYS5zaXplKCk7IGorKykKCQl7CgkJCWludCBwb3dlcl9vZl9yb290ID0gcG93bW9kIChwcmltX3Jvb3QsIHBvd2VyLCBhLnNpemUoKSArIDEpOwoJCQlzdW0JICArPSBwb3dlcl9vZl9yb290ICogYVtqXTsJLy8g0J3QsNC60LDQv9C70LjQstCw0LXQvCDRgdGD0LzQvNGDINC/0YDQvtC40LfQstC10LTQtdC90LjQuSDRjdC70LXQvNC10L3RgtC+0LIg0YHRgtGA0L7QutC4INC80LDRgtGA0LjRhtGLINC90LAg0LLQtdC60YLQvtGACgkJCXBvd2VyICs9IGk7CQkJCQkJLy8g0KPQstC10LvQuNGH0LjQstCw0LXQvCDRgdGC0LXQv9C10L3RjCwg0LIg0LrQvtGC0L7RgNGD0Y4g0LLQvtC30LLQvtC00LjRgtGB0Y8g0L/RgNC40LzQuNGC0LjQstC90YvQuSDQutC+0YDQtdC90Ywg0LjQtyDQtdC00LjQvdC40YbRiwoJCX0KCQlyZXN1bHQucHVzaF9iYWNrKHN1bSAlIChhLnNpemUoKSArIDEpKTsgLy8g0J3QsNCx0YDQsNC70Lgg0YHRg9C80LzRgywg0L/RgNC40LLQvtC00LjQvCDQv9C+IG1vZCBwLCBwID0g0LTQu9C40L3QsCDQstC10LrRgtC+0YDQsCArIDEKCX0KCXJldHVybiByZXN1bHQ7Cn0KCgpzdGQ6OnZlY3RvcjxpbnQ+IGludmVyc2VkX2RmdCAoc3RkOjp2ZWN0b3I8aW50PiYgYSkKewoJc3RkOjp2ZWN0b3I8aW50PiByZXN1bHQ7CglpbnQgcHJpbV9yb290ID0gZ2VuZXJhdG9yIChhLnNpemUoKSk7CS8vINCa0L7RgNC10L3RjCDRgdGC0LXQv9C10L3QuCBuINC40Lcg0LXQtNC40L3QuNGG0YsKCWludCBpbnZfcHJpbV9yb290ID0gaW52ZXJzZSAocHJpbV9yb290LCBhLnNpemUoKSArIDEpOwkvLyDQntCx0YDQsNGC0L3Ri9C5INC6INC90LDQudC00LXQvdC90L7QvNGDINC/0YDQuNC80LjRgtC40LLQvdC+0LzRgyDQutC+0YDQvdGOCglpbnQgaW52X24gPSBpbnZlcnNlKGEuc2l6ZSgpLCBhLnNpemUoKSArIDEpOwoJZm9yIChpbnQgaSA9IDA7IGkgPCBhLnNpemUoKTsgaSsrKQoJewoJCWludCBzdW0gICA9IDA7CgkJaW50IHBvd2VyID0gMDsKCQlmb3IgKGludCBqID0gMDsgaiA8IGEuc2l6ZSgpOyBqKyspCgkJewoJCQlpbnQgcG93ZXJfb2ZfaW52X3Jvb3QgPSBwb3dtb2QgKGludl9wcmltX3Jvb3QsIHBvd2VyLCBhLnNpemUoKSArIDEpOwoJCQlzdW0JICArPSBpbnZfbiAqIHBvd2VyX29mX2ludl9yb290ICogYVtqXTsJLy8g0J3QsNC60LDQv9C70LjQstCw0LXQvCDRgdGD0LzQvNGDINC/0YDQvtC40LfQstC10LTQtdC90LjQuSDRjdC70LXQvNC10L3RgtC+0LIg0YHRgtGA0L7QutC4INC80LDRgtGA0LjRhtGLINC90LAg0LLQtdC60YLQvtGACgkJCXBvd2VyICs9IGk7CQkJCQkJCS8vINCj0LLQtdC70LjRh9C40LLQsNC10Lwg0YHRgtC10L/QtdC90YwsINCyINC60L7RgtC+0YDRg9GOINCy0L7Qt9Cy0L7QtNC40YLRgdGPINC/0YDQuNC80LjRgtC40LLQvdGL0Lkg0LrQvtGA0LXQvdGMINC40Lcg0LXQtNC40L3QuNGG0YsKCQl9CgkJcmVzdWx0LnB1c2hfYmFjayhzdW0gJSAoYS5zaXplKCkgKyAxKSk7IC8vINCd0LDQsdGA0LDQu9C4INGB0YPQvNC80YMsINC/0YDQuNCy0L7QtNC40Lwg0L/QviBtb2QgcCwgcCA9INC00LvQuNC90LAg0LLQtdC60YLQvtGA0LAgKyAxCgl9CglyZXR1cm4gcmVzdWx0Owp9CgovLyDQktGL0LLQvtC0INGB0L7QvtCx0YnQtdC90LjQuQp2b2lkIGRlYnVnKGNvbnN0IHN0ZDo6c3RyaW5nJiBzdHIsIHN0ZDo6dmVjdG9yPGludD4mIHYpCnsKCXN0ZDo6Y291dCA8PCAiW0RFQlVHXToiIDw8IHN0ciA8PCBzdGQ6OmVuZGw7Cglmb3IoaW50IGkgPSAwOyBpIDwgdi5zaXplKCk7IGkrKykKCQlzdGQ6OmNvdXQgPDwgdltpXSA8PCAiICI7CglzdGQ6OmNvdXQgPDwgc3RkOjplbmRsOwp9CgppbnQgbWFpbigpCnsKCWludCBhW10gPSB7NCwgMywgMiwgMX07CglzdGQ6OnZlY3RvcjxpbnQ+IHYoc3RkOjpiZWdpbihhKSwgc3RkOjplbmQoYSkpOwogCglkZWJ1ZygiU291cmNlIHZlY3RvciIsIHYpOwogCglzdGQ6OnZlY3RvcjxpbnQ+IHJlc3VsdCA9IGZvcndhcmRfZGZ0KHYpOwogCglkZWJ1ZygiRm9yd2FyZCBERlQiLCByZXN1bHQpOwogCglyZXN1bHQgPSBpbnZlcnNlZF9kZnQocmVzdWx0KTsKIAoJZGVidWcoIkludmVyc2VkIERGVCIsIHJlc3VsdCk7Cn0=