#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <assert.h>
#include <stdarg.h>
#include <vector>
#include <utility>
#include <numeric>
// [CONF] 中間結果表示
#define SHOW_ITM
#ifdef _WIN32
int fopen_s_wrp(FILE** const p_fp, const char* const fpath, const char* const mode)
{
return (int)fopen_s(p_fp, fpath, mode);
}
int sprintf_s_wrp(char buf[], const size_t bufsz, const char* const fmt, ...)
{
va_list ap;
int nRet;
va_start(ap, fmt);
nRet = vsprintf_s(buf, bufsz, fmt, ap);
va_end(ap);
return nRet;
}
#else
int fopen_s_wrp(FILE** const p_fp, const char* const fpath, const char* const mode)
{
*p_fp = fopen(fpath, mode);
return (*p_fp == NULL);
}
int sprintf_s_wrp(char buf[], const size_t bufsz, const char* const fmt, ...)
{
va_list ap;
int nRet;
va_start(ap, fmt);
nRet = vsprintf(buf, fmt, ap);
va_end(ap);
return nRet;
}
#define _countof(arr) (sizeof(arr) / sizeof(arr[0]))
#endif
namespace priroot
{
typedef int64_t pow_t;
/// 1の原始n乗根を表す。
/// nの剰余計算を高速化するため、nは2^pに制限する。
class nth_root
{
const pow_t t90;
const pow_t m360;
std::vector<double> m_sin;
public:
nth_root(const int p)
: t90(((pow_t)1 << (p - 2))), m360((t90 << 2) - 1), m_sin((size_t)t90 + 1)
{
const double k = 0.5 * M_PI;
for (pow_t i = 1; i < t90; i++) {
m_sin[i] = sin((k * (double)i) / (double)t90);
}
m_sin[0] = 0.0;
m_sin[t90] = 1.0;
}
pow_t n() const { return 4 * t90; }
/// 1の原始n乗根のk乗を取得する。
void pow(const pow_t k_, double& re, double& im) const {
pow_t k = (k_ < 0) ? 4 * t90 - k_ : k_;
k &= m360; // nの剰余
assert(0 <= k && k < 4 * t90);
if (k <= t90) {
re = m_sin[t90 - k];
im = m_sin[k];
}
else if (k <= 2 * t90) {
re = -m_sin[k - t90]; // = t90 - (2 * t90 - k)
im = m_sin[2 * t90 - k];
}
else if (k <= 3 * t90) {
re = -m_sin[3 * t90 - k]; // = t90 - (k - 2 * t90)
im = -m_sin[k - 2 * t90];
}
else {
re = m_sin[k - 3 * t90]; // = t90 - (4 * t90 - k)
im = -m_sin[4 * t90 - k];
}
}
};
} // namespace priroot
namespace dftn
{
using namespace priroot;
/// フーリエ変換
/// DFTの原理式
/// X(k) = Σ[j=0..t360-1]{ x(j) * w^(j*k) } (k=0..t360-1)
/// に従いDFTを計算し、X(k)の実部をfbgn[k..]、虚部をfbgn[(k + t360)..]に格納する。
void fouri(
std::vector<double>::const_iterator xbgn,
std::vector<double>::const_iterator xend,
std::vector<double>::iterator fbgn,
std::vector<double>::iterator fend,
const int p,
const nth_root& w)
{
const pow_t t360 = (pow_t)1 << p;
assert(xend - xbgn == (ptrdiff_t)(2 * t360));
assert(fend - fbgn == (ptrdiff_t)(2 * t360));
assert(w.n() == t360);
for (pow_t k = 0; k < t360; k++) {
double sum_re = 0.0;
double sum_im = 0.0;
for (pow_t j = 0; j < t360; j++) {
const double a = xbgn[j];
const double b = xbgn[j + t360];
double wjk_re, wjk_im;
w.pow(j * k, wjk_re, wjk_im);
sum_re += a * wjk_re - b * wjk_im;
sum_im += a * wjk_im + b * wjk_re;
}
fbgn[k] = sum_re;
fbgn[k + t360] = sum_im;
}
}
/// フーリエ逆変換
/// IDFTの原理式
/// x(j) = Σ[k=0..t360-1]{ X(k) * w^(-j*k) } (j=0..t360-1)
/// に従いIDFTを計算し、x(k)の実部をxbgn[k..]、虚部をxbgn[(k + t360)..]に格納する。
void fouriinv(
std::vector<double>::const_iterator fbgn,
std::vector<double>::const_iterator fend,
std::vector<double>::iterator xbgn,
std::vector<double>::iterator xend,
const int p,
const nth_root& w)
{
const pow_t t360 = (pow_t)1 << p;
assert(fend - fbgn == (ptrdiff_t)(2 * t360));
assert(xend - xbgn == (ptrdiff_t)(2 * t360));
assert(w.n() == t360);
for (pow_t j = 0; j < t360; j++) {
double sum_re = 0.0;
double sum_im = 0.0;
for (pow_t k = 0; k < t360; k++) {
const double a = fbgn[k];
const double b = fbgn[k + t360];
double wjk_re, wjk_im;
w.pow(j * k, wjk_re, wjk_im);
sum_re += a * wjk_re + b * wjk_im;
sum_im += b * wjk_re - a * wjk_im;
}
xbgn[j] = sum_re / (double)t360;
xbgn[j + t360] = sum_im / (double)t360;
}
}
} // namespace dftn
namespace dft4n
{
using namespace priroot;
/// 4n乗根によるフーリエ変換
/// x(j) (j=0..(2*t360)-1)から
/// x'(j) = (x(j) + i * x(j+t360)) * u^(j/4) (j=0..t360-1)
/// を構成し、x'(j)をDFTする。ここで、uは1の原始t360乗根。
void fouri4n(
std::vector<double>::iterator xbgn,
std::vector<double>::iterator xend,
std::vector<double>::iterator fbgn,
std::vector<double>::iterator fend,
const int p,
const nth_root& w)
{
const pow_t t360 = (pow_t)1 << p;
assert(xend - xbgn == (ptrdiff_t)(2 * t360));
assert(fend - fbgn == (ptrdiff_t)(2 * t360));
assert(w.n() == (t360 << 2));
// { x'(j) }構成
// { x(j) + i * x(j+t360) } にu^(j/4)を掛ける。
for (pow_t j = 0; j < t360; j++) {
const double a = xbgn[j];
const double b = xbgn[j + t360];
// u = w^4としてu^(j/4) = w^(j)
double wj_re, wj_im;
w.pow(j, wj_re, wj_im);
xbgn[j] = a * wj_re - b * wj_im;
xbgn[j + t360] = a * wj_im + b * wj_re;
}
// { x'(j) }のフーリエ変換
std::vector<double> fr((size_t)t360);
for (pow_t k = 0; k < t360; k++) {
double sum_re = 0.0;
double sum_im = 0.0;
for (pow_t j = 0; j < t360; j++) {
const double a = xbgn[j];
const double b = xbgn[j + t360];
// u = w^4としてu^(j*k)を掛けて足す
// u^(j*k) = w^(4*j*k)
double wjk_re, wjk_im;
w.pow(4 * j * k, wjk_re, wjk_im);
sum_re += a * wjk_re - b * wjk_im;
sum_im += a * wjk_im + b * wjk_re;
}
fbgn[k] = sum_re;
fbgn[k + t360] = sum_im;
}
}
/// 4n乗根による逆フーリエ変換
void fouri4ninv(
std::vector<double>::const_iterator fbgn,
std::vector<double>::const_iterator fend,
std::vector<double>::iterator xbgn,
std::vector<double>::iterator xend,
const int p,
const nth_root& w)
{
const pow_t t360 = (pow_t)1 << p;
assert(fend - fbgn == (ptrdiff_t)(2 * t360));
assert(xend - xbgn == (ptrdiff_t)(2 * t360));
assert(w.n() == (t360 << 2));
// { x'(j) }の逆フーリエ変換
for (pow_t j = 0; j < t360; j++) {
double sum_re = 0.0;
double sum_im = 0.0;
for (pow_t k = 0; k < t360; k++) {
const double a = fbgn[k];
const double b = fbgn[k + t360];
// u = w^4としてu^(j*(-k))を掛けて足す
// u^(j*(-k)) = w^(-4*j*k)
double wjk_re, wjk_im;
w.pow(4 * j * k, wjk_re, wjk_im);
sum_re += a * wjk_re + b * wjk_im;
sum_im += b * wjk_re - a * wjk_im;
}
xbgn[j] = sum_re / (double)t360;
xbgn[j + t360] = sum_im / (double)t360;
}
// { x(j) }復元
for (pow_t j = 0; j < t360; j++) {
const double a = xbgn[j];
const double b = xbgn[j + t360];
// u = w^4としてu^(-j/4) = w^(-j)を掛ける
double wj_re, wj_im;
w.pow(j, wj_re, wj_im);
xbgn[j] = a * wj_re + b * wj_im;
xbgn[j + t360] = b * wj_re - a * wj_im;
}
}
} // namespace dftn
namespace utest
{
using namespace priroot;
void nth_rootTest()
{
const int p = 4;
nth_root w(p);
const pow_t t360 = (pow_t)1 << p;
for (pow_t k = 0; k <= 2 * t360; k++) {
double re, im;
w.pow(k, re, im);
const double expn = (k % t360 == 0) ? 0.0 : (2 * M_PI) / atan2(im, re);
printf("rt.pow(%2lld) = % f + i * % f (w^% 10f=1)\n", k, re, im, expn);
}
}
void show_complex_seq(const std::vector<double>& s)
{
const size_t hsz = s.size() / 2;
assert(2 * hsz == s.size());
printf("Re:");
for (size_t i = 0; i < hsz; i++) {
printf(" % 12f", s[i]);
}
printf("\n");
printf("Im:");
for (size_t i = 0; i < hsz; i++) {
printf(" % 12f", s[i + hsz]);
}
printf("\n");
}
void fouriTest()
{
const int p = 4;
const pow_t t360 = (pow_t)1 << p;
nth_root w(p);
// 入力列準備
std::vector<double> x((size_t)(2 * t360));
std::iota(x.begin(), x.begin() + t360, 0.0);
printf("input seq:\n");
show_complex_seq(x);
printf("\n");
// フーリエ変換
std::vector<double> f((size_t)(2 * t360));
dftn::fouri(x.begin(), x.end(), f.begin(), f.end(), p, w);
x.clear();
printf("dftn::fouri() result:\n");
show_complex_seq(f);
printf("\n");
// 逆フーリエ変換
std::vector<double> r((size_t)(2 * t360));
dftn::fouriinv(f.begin(), f.end(), r.begin(), r.end(), p, w);
f.clear();
printf("dftn::fouriinv() result:\n");
show_complex_seq(r);
printf("\n");
// 照合
const size_t sz = r.size();
if (sz != (size_t)(2 * t360)) { abort(); }
for (size_t i = 0; i < sz / 2; i++) {
const double actv = r[i];
const double expv = (double)i;
const double err = std::abs(actv - expv);
if (err >= 0.1) { abort(); }
}
for (size_t i = sz / 2; i < sz; i++) {
const double expv = 0.0;
const double actv = r[i];
const double err = std::abs(actv - expv);
if (err >= 0.1) { abort(); }
}
}
void fouri4nTest()
{
const int p = 4;
const pow_t t360 = (pow_t)1 << (p - 1);
nth_root w(p + 1);
// 入力列準備
std::vector<double> x((size_t)2 * t360);
std::iota(x.begin(), x.end(), 0.0);
printf("input seq:\n");
show_complex_seq(x);
printf("\n");
// フーリエ変換
std::vector<double> f((size_t)2 * t360);
dft4n::fouri4n(x.begin(), x.end(), f.begin(), f.end(), p - 1, w);
x.clear();
printf("dft4n::fouri4n() result:\n");
show_complex_seq(f);
printf("\n");
// 逆フーリエ変換
std::vector<double> r((size_t)2 * t360);
dft4n::fouri4ninv(f.begin(), f.end(), r.begin(), r.end(), p - 1, w);
f.clear();
printf("dft4n::fouri4ninv() result:\n");
show_complex_seq(r);
printf("\n");
// 照合
const size_t sz = r.size();
if (sz != (size_t)(2 * t360)) { abort(); }
for (size_t i = 0; i < sz; i++) {
const double actv = r[i];
const double expv = (double)i;
const double err = std::abs(actv - expv);
if (err >= 0.1) { abort(); }
}
}
} // namespace utest
int main()
{
#ifdef SHOW_ITM
utest::nth_rootTest();
utest::fouriTest();
utest::fouri4nTest();
#endif
}