// 2^(2^34 - 1) = 2^17179869183 を計算する。
#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>
// [DBGSW] 中間結果表示
//#define SHOW_ITM
// [DBGSW] I表示
//#define SHOW_I
// [DBGSW] SIN_TABLE作成状況表示
//#define SHOW_SIN_TABLE
typedef uint32_t I_TYPE;
// [CONF] 基本SINテーブルの最大要素数
#define BASIC_SINTBL_P_MAX 29
// [CONF] 多倍長バッファ1要素の10進数での桁数
const int FIG = 5;
// [CONF] 10^FIG
const I_TYPE RADIX = 100000;
#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
/// 実部と虚部が後退に並ぶ形式の複素数リストを表示する。
void show_re_im_seq(const std::vector<double>& s, const int p)
{
const size_t hsz = (size_t)1 << (p - 1);
printf("Re:");
for (size_t i = 0; i < hsz; i++) {
printf(" % 12f", s[2 * i]);
}
printf("\n");
printf("Im:");
for (size_t i = 0; i < hsz; i++) {
printf(" % 12f", s[2 * i + 1]);
}
printf("\n");
}
/// 実部と虚部が後退に並ぶ形式の複素数リストを表示する。
void show_re_im_seq(const std::vector<I_TYPE>& s, const int p)
{
char fmt[128];
sprintf_s_wrp(fmt, _countof(fmt), "%%0%dlu", FIG);
const size_t hsz = (size_t)1 << (p - 1);
printf("Re:");
for (size_t i = 0; i < hsz; i++) {
printf(fmt, s[2 * i]);
}
printf("\n");
printf("Im:");
for (size_t i = 0; i < hsz; i++) {
printf(fmt, s[2 * i + 1]);
}
printf("\n");
}
namespace priroot
{
// データの桁数の桁数を表す型
typedef int expn_t;
// データの桁数を表す型
typedef int64_t pow_t;
#ifdef SHOW_SIN_TABLE
size_t g_sin_tblent_cnt = 0;
#endif
/// 1の原始n乗根を表す。
/// nの剰余計算を高速化するため、nは2^pに制限する。
class basic_nth_root
{
/*const*/ expn_t sv_p;
/*const*/ pow_t t360;
/*const*/ pow_t m360;
/*const*/ pow_t t090;
std::vector<double> m_sin;
public:
basic_nth_root() : sv_p(0), t090(0), m360(0) { }
basic_nth_root(const expn_t p)
: sv_p(p), t360((pow_t)1 << p), m360(t360 - 1), t090(t360 / 4), m_sin((size_t)(t360 / 4 + 1))
{
if (p >= 2) {
assert(t360 >= 4);
t090 = t360 / 4;
const double k = 0.5 * M_PI;
for (pow_t i = 1; i < t090; i++) {
m_sin[i] = sin((k * (double)i) / (double)t090);
}
#ifdef SHOW_SIN_TABLE
g_sin_tblent_cnt += t090;
printf("******* %s: p=%d table made. (+%zu, %zu)\n", __FUNCTION__, p, t090, g_sin_tblent_cnt);
#endif
m_sin[0] = 0.0;
m_sin[t090] = 1.0;
}
}
expn_t p() const { return sv_p; }
pow_t n() const { return t360; }
/// 1の原始n乗根のk乗を取得する。
void pow(const pow_t k_, double& re, double& im) const {
assert(k_ >= 0);
pow_t k = k_;
k &= m360; // nの剰余
if (sv_p < 2) {
assert(k == 0 || k == 1);
im = 0.0;
re = (k == 0) ? 1.0 : -1.0;
return;
}
assert(0 <= k && k < 4 * t090);
if (k <= t090) {
re = m_sin[t090 - k];
im = m_sin[k];
}
else if (k <= 2 * t090) {
re = -m_sin[k - t090]; // = t090 - (2 * t090 - k)
im = m_sin[2 * t090 - k];
}
else if (k <= 3 * t090) {
re = -m_sin[3 * t090 - k]; // = t090 - (k - 2 * t090)
im = -m_sin[k - 2 * t090];
}
else {
re = m_sin[k - 3 * t090]; // = t090 - (4 * t090 - k)
im = -m_sin[4 * t090 - k];
}
}
};
/// 1の原始n乗根を表す。
class nth_root
{
expn_t max_p;
// 1の原始2^p乗根のr乗を収めたテーブルのベクタ
// basic_tables[p]が1の原始2^p乗根のテーブルを表す。
std::vector<basic_nth_root> basic_tables;
// 1の原始2^(p+m)乗根のみを収めたベクタ
// high_order_root_tables[m]が1の原始2^(p+m)乗根の値。(こちらはベクタ要素がテーブルではなく値を表す。)
std::vector<std::pair<double, double> > high_order_root_values;
/*
(NOTE)
1の原始n乗根をw_nと書くことにすると、{ b_n }を0か1からなる数列、rを適当な整数として
k = b_0 + 2 * b_1 + (2^2) * b_2 + ... * (2^(m-1)) * b_(m-1) + { (2^m) * r }
であるとき、
(w_(2^(p+m)))^k = w_(2^(p+m))^b_0 * w_(2^(p+(m-1)))^b_1 * w_(2^(p+(m-2))^b_2 * ... * w_(2^(p+1))^b_(m-1) * { w_p^(rの2^pに対する剰余) }
と書けるので、
w_p^r (r=0..(2^p-1)) の値
w_2^(p+q) (q=1..m) の値
があれば、w^(p+m)を計算できる。
前者は任意の指数rについて計算できねばならないため、テーブルで実現する場合はO(2^p)オーダーのメモリを要するが、
後者はm個の値だけ記憶しておけば良いため、O(m)のオーダーで済む。そのかわりkが与えられる都度値の掛け算が発生する。
*/
public:
nth_root() : max_p(0) { }
/// 1の原始2^p乗根のテーブルを用意する。
void alloc_table(const expn_t p) {
if (p > max_p) {
// (テーブル生成済のpより大きいp)
// まずテーブル生成を検討する。
if (p <= BASIC_SINTBL_P_MAX) {
// (pが制限値以下)
// [max_p+1..p]のテーブルを新規生成
basic_tables.resize((size_t)(p + 1));
for (expn_t i = max_p + 1; i <= p; i++) {
basic_tables[i] = basic_nth_root(i);
}
max_p = p;
}
}
if (p > max_p) {
// (任意指数に対するテーブル化が不能な大きさのp)
// 指数1の値のみ記憶する。
const expn_t m = p - max_p;
if ((size_t)m >= high_order_root_values.size()) {
high_order_root_values.resize((size_t)(m + 1));
std::pair<double, double>& ent = high_order_root_values[m]; // Alias
const pow_t big_n = (pow_t)1 << p;
const double arg = (2 * M_PI) / big_n;
ent.first = cos(arg); // 実部
ent.second = sin(arg); // 虚部
}
}
}
/// 1の原始2^p乗根のk乗を取得する。
void pow(const expn_t p, const pow_t k, double& re, double& im) const {
if (p <= max_p) {
// (w_(2^p))^k算出
basic_tables[p].pow(k, re, im);
}
else {
const expn_t m = p - max_p;
const pow_t r = k >> m;
const pow_t b = k & (((pow_t)1 << m) - 1);
// (w_(2^max_p))^r算出
basic_tables[max_p].pow(r, re, im);
// w_(2^(max_p + i))を掛け合わせる
pow_t mask = (pow_t)1 << (m - 1);
for (expn_t i = 1; i <= m; i++) {
assert(mask > 0);
if ((b & mask) != 0) {
const std::pair<double, double>& ent = high_order_root_values[i]; // Alias
const double tmp_re = re * ent.first - im * ent.second;
const double tmp_im = re * ent.second + im * ent.first;
re = tmp_re;
im = tmp_im;
}
mask >>= 1;
}
assert(mask == 0);
}
}
};
} // namespace priroot
namespace fft4n
{
using namespace priroot;
/// ビット数bitwのBTR演算
pow_t btr(const pow_t x, const expn_t bitw) {
pow_t y = 0;
for (expn_t i = 0; i <= (bitw - 1) / 2; i++) {
const pow_t b = ((pow_t)1 << i) & x;
y |= (b << ((bitw - 1) - 2 * i));
}
for (expn_t i = (bitw - 1) / 2 + 1; i < bitw; i++) {
const pow_t b = ((pow_t)1 << i) & x;
y |= (b >> (2 * i - (bitw - 1)));
}
return y;
}
/// フーリエ変換
/// DFTの原理式
/// X(k) = Σ[j=0..t360-1]{ x(j) * w^(j*k) } (k=0..t360-1)
/// より
/// X(k) = Σ[j=0..t180-1]{ x(2*j) * w^(2*j*k) } + Σ[j=0..t180-1]{ x(2*j+1) * w^((2*j+1)*k) }
/// = Σ[j=0..t180-1]{ x(2*j) * w^(2*j*k) } + w^k * Σ[j=0..t180-1]{ x(2*j+1) * w^(2*j*k) }
/// さらに、kをk+t180に置き換えると以下が成立する。
/// X(k+t180) = Σ[j=0..t180-1]{ x(2*j) * w^(2*j*(k + t180) } + w^(k+t180) * Σ[j=0..t180-1]{ x(2*j+1) * w^(2*j*(k+t180) }
/// = Σ[j=0..t180-1]{ x(2*j) * w^(2*j*k) } - w^k * Σ[j=0..t180-1]{ x(2*j+1) * w^(2*j*k) }
/// よって、
/// X_even(k) = Σ[j=0..t180-1]{ x(2*j ) * w^(2*j*k) } (k=0..t180-1)
/// X_odd(k) = Σ[j=0..t180-1]{ x(2*j+1) * w^(2*j*k) } (k=0..t180-1)
/// として、X(k) (k=0..t360)は以下のバタフライ演算で表せる。
/// X(k ) = X_even(k) + w^k * X_odd(k) (k=0..t180-1)
/// X(k+t180) = X_even(k) - w^k * X_odd(k) (k=0..t180-1)
/// これを、大元の時間領域データx(j)の実部がbuf[2*btr_j]、虚部がbuf[2*btr_j + 1]に格納されている前提で行う。
/// ここで、t360=2^pとして、btr_jはjのビット幅pのbit reverse order。
void fouri(
std::vector<double>::iterator buf,
const expn_t p,
nth_root& w)
{
// 再帰終端判定
if (p == 0) {
// (要素数2^0のフーリエ変換)
// 単一要素x(j)のフーリエ変換はx(j)なので何もしない。
return;
}
const pow_t t360 = (pow_t)1 << p;
const pow_t t180 = (pow_t)1 << (p - 1);
w.alloc_table(p);
// X_even(k)取得
// [0..t180-1]に対し、w^2kを回転因子とするフーリエ変換をin-placeで実行する。
fouri(buf, p - 1, w);
// X_odd(k)取得
// [t180..t360-1]に対し、w^2kを回転因子とするフーリエ変換をin-placeで実行する。
// t180に2を掛けているのは、実部と虚部の2要素セットでt001であることによる。
fouri(buf + 2 * t180, p - 1, w);
// バタフライ演算
// X(k ) = X_even(k) + w^k * X_odd(k)
// X(k+t180) = X_even(k) - w^k * X_odd(k)
for (pow_t k = 0; k < t180; k++) {
const pow_t ofs_even = k << 1;
const pow_t ofs_odd = (k + t180) << 1;
// X_even(k)
const double X_even_k_re = buf[ofs_even];
const double X_even_k_im = buf[ofs_even + 1];
// X_odd(k)
double X_odd_k_re = buf[ofs_odd];
double X_odd_k_im = buf[ofs_odd + 1];
// X_odd(k) * w^k 算出、ここでwは1の原始t360乗根
double wk_re, wk_im;
w.pow(p, k, wk_re, wk_im);
const double re = X_odd_k_re * wk_re - X_odd_k_im * wk_im;
const double im = X_odd_k_re * wk_im + X_odd_k_im * wk_re;
// X(k ) = X_even(k) + w^k * X_odd(k) = X_even(k) + (re + i * im)
buf[ofs_even] = X_even_k_re + re;
buf[ofs_even + 1] = X_even_k_im + im;
// X(k+t180) = X_even(k) - w^k * X_odd(k) = X_even(k) - (re + i * im)
buf[ofs_odd] = X_even_k_re - re;
buf[ofs_odd + 1] = X_even_k_im - im;
}
}
/// Bit reverse orderにin-placeで並べ替える。
void sort_by_btr(
std::vector<double>::iterator buf,
const expn_t p)
{
const pow_t t360 = (pow_t)1 << p;
for (pow_t i = 0; i < t360; i++) {
const pow_t btr_i = btr(i, p);
// (btr_i, i)と(i, btr_i)の両方についてswapすると元に戻ってしまうので、
// 片方のみswap対象とする。
if (btr_i < i) {
std::swap(buf[2 * btr_i], buf[2 * i]); // 実部
std::swap(buf[2 * btr_i + 1], buf[2 * i + 1]); // 虚部
}
}
}
/// 逆フーリエ変換
/// フーリエ変換とは逆の以下のバタフライ演算を行う。
/// x(k ) = (x_even(k) + w^(-k) * x_odd(k)) / t360 (k=0..t180-1)
/// x(k+t180) = (x_even(k) - w^(-k) * x_odd(k)) / t360 (k=0..t180-1)
/// これを、大元の周波数領域データX(k)の実部がbuf[2*k]、虚部がbuf[2*k+1]に格納されている前提で行う。
void fouriinv(
std::vector<double>::iterator buf,
const expn_t p,
nth_root& w)
{
// 再帰終端判定
if (p == 0) {
// (要素数2^0のフーリエ変換)
// 単一要素x(j)のフーリエ変換はx(j)なので何もしない。
return;
}
const pow_t t360 = (pow_t)1 << p;
const pow_t t180 = (pow_t)1 << (p - 1);
w.alloc_table(p);
// x_even(k)取得
// [0..t180-1]に対し、w^2kを回転因子とするフーリエ変換をin-placeで実行する。
fouriinv(buf, p - 1, w);
// x_odd(k)取得
// [t180..t360-1]に対し、w^2kを回転因子とするフーリエ変換をin-placeで実行する。
// t180に2を掛けているのは、実部と虚部の2要素セットでt001であることによる。
fouriinv(buf + 2 * t180, p - 1, w);
// バタフライ演算
// x(k ) = x_even(k) + w^k * x_odd(k)
// x(k+t180) = x_even(k) - w^k * x_odd(k)
for (pow_t k = 0; k < t180; k++) {
const pow_t ofs_even = k << 1;
const pow_t ofs_odd = (k + t180) << 1;
// x_even(k)
const double x_even_k_re = buf[ofs_even];
const double x_even_k_im = buf[ofs_even + 1];
// x_odd(k)
double x_odd_k_re = buf[ofs_odd];
double x_odd_k_im = buf[ofs_odd + 1];
// x_odd(k) * w^(-k) 算出、ここでwは1の原始t360乗根
double wk_re, wk_im;
w.pow(p, k, wk_re, wk_im);
const double re = x_odd_k_re * wk_re + x_odd_k_im * wk_im;
const double im = x_odd_k_im * wk_re - x_odd_k_re * wk_im;
// X(k ) = x_even(k) + w^k * x_odd(k) = x_even(k) + (re + i * im)
buf[ofs_even] = (x_even_k_re + re) / 2.0;
buf[ofs_even + 1] = (x_even_k_im + im) / 2.0;
// X(k+t180) = x_even(k) - w^k * x_odd(k) = x_even(k) - (re + i * im)
buf[ofs_odd] = (x_even_k_re - re) / 2.0;
buf[ofs_odd + 1] = (x_even_k_im - im) / 2.0;
}
}
/// 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の原始(4 * mag * t360)乗根 (mag ≧ 1)。
void fouri4n(
std::vector<double>::iterator buf,
const expn_t p,
nth_root& w)
{
const pow_t t360 = (pow_t)1 << p;
w.alloc_table(p + 2);
// { x'(j) }構成
// { x(j) + i * x(j+t360) } にu^(j/4)を掛ける。
const expn_t p2 = p + 2;
for (pow_t j = 0; j < t360; j++) {
const double a = buf[2 * j];
const double b = buf[2 * j + 1];
// u = w^4としてu^(j/4) = w^(j)
double wj_re, wj_im;
w.pow(p2, j, wj_re, wj_im);
buf[2 * j] = a * wj_re - b * wj_im;
buf[2 * j + 1] = a * wj_im + b * wj_re;
}
// { x'(j) }をbit reverse orderに変換
sort_by_btr(buf, p);
// フーリエ変換
fouri(buf, p, w);
}
/// 4n乗根による逆フーリエ変換
void fouri4ninv(
std::vector<double>::iterator buf,
const expn_t p,
nth_root& w)
{
const pow_t t360 = (pow_t)1 << p;
w.alloc_table(p + 2);
// { X'(k) }をbit reverse orderに変換
sort_by_btr(buf, p);
// 逆フーリエ変換
fouriinv(buf, p, w);
// { x(j) }復元
const expn_t p2 = p + 2;
for (pow_t j = 0; j < t360; j++) {
const double a = buf[2 * j];
const double b = buf[2 * j + 1];
// u = w^4としてu^(-j/4) = w^(-j)を掛ける
double wj_re, wj_im;
w.pow(p2, j, wj_re, wj_im);
buf[2 * j] = a * wj_re + b * wj_im;
buf[2 * j + 1] = b * wj_re - a * wj_im;
}
}
} // namespace fft4n
namespace oper
{
using namespace fft4n;
struct I
{
std::vector<I_TYPE> data; // 基数RADIXの多倍長データ
size_t ndigits; // dataに入っている有効な桁数
I(const size_t cap)
: data(cap), ndigits((size_t)0)
{
/*pass*/
}
};
struct F
{
std::vector<double> data; // 基数[-(RADIX/2), RADIX/2)の多倍長データ
int p; // 2^p == (dataの要素数) となるp
F(const int p_)
: data((size_t)1 << p_), p(0)
{
/*pass*/
}
};
/// 整数のフーリエ変換
void i2f(F& a, const I& b, const int p, priroot::nth_root& w)
{
size_t i;
// 次数2^(p-1) = t360の4n乗根フーリエ変換を行う。
// 結果は実部と虚部を合わせて2^p = (2 * t360)桁分となる。
const pow_t t360 = (pow_t)1 << (p - 1);
assert(b.ndigits <= (size_t)t360);
assert((size_t)(2 * t360) <= a.data.size());
#ifdef SHOW_I
printf("%s: Before pre tr.:\n", __FUNCTION__);
show_re_im_seq(b.data, p);
#endif
// フーリエ変換の前処理
// (bが表す整数) = Σ[i=0..a.ndigits-1]{ b.data[i] * RADIX^i } (0≦b.data[i]<RADIX)
// を、
// (aが表す整数) = Σ[i=0..a.ndigits-1]{ a.data[i] * RADIX^i } (-RADIX / 2≦a.data[i]<RADIX / 2)
// に変換する。(a, bが同じ整数を表すように桁上げも行う。)
a.p = p;
int carry = 0;
for (i = 0; i < b.ndigits - 1; i++) {
// 実部虚部隣接化
// b.data[t360..]を虚部に配置する。
const size_t dst_i = (i < (size_t)t360) ? 2 * i : 2 * (i - (size_t)t360) + 1;
const int32_t t = b.data[i] + carry; // 桁[i]の値
if (t < (int32_t)RADIX / 2) {
// (桁[i]の値∈[0, RADIX / 2) )
// 何もしない
a.data[dst_i] = t;
carry = 0;
}
else {
// (桁[i]の値∈[RADIX / 2, RADIX) )
// 補正実施
// まず桁[i] -= RADIX により桁[i]の値∈[0, RADIX / 2)とする。ただし
// それだけでは(aが表す整数)が RADIX * RADIX^i = RADIX^(i+1)だけ減ってしまうので、
// 減った分をcarryで一つ上の桁に回して補填する。
a.data[dst_i] = (double)(t - (int32_t)RADIX);
carry = 1;
}
}
{
// 実部虚部隣接化
const size_t dst_i = (i < (size_t)t360) ? 2 * i : 2 * (i - (size_t)t360) + 1;
// 最上位桁
a.data[dst_i] = (double)(b.data[i] + (I_TYPE)carry);
i++;
}
for (; i < (size_t)(2 * t360); i++) {
// 実部虚部隣接化
const size_t dst_i = (i < (size_t)t360) ? 2 * i : 2 * (i - (size_t)t360) + 1;
a.data[dst_i] = 0.0;
}
#if defined(SHOW_ITM) || defined(SHOW_I)
printf("Before fouri4n():\n");
show_re_im_seq(a.data, a.p);
#endif
// フーリエ変換実施
fft4n::fouri4n(a.data.begin(), p - 1, w);
#ifdef SHOW_ITM
printf("After fouri4n():\n");
show_re_im_seq(a.data, a.p);
#endif
}
/// 整数のフーリエ変換結果の積(結果は整数)
void mul_f2i(I& a, const F& b, const F& c, F& d, double& err_max, priroot::nth_root& w) {
int64_t carry;
size_t i, t;
const int p = b.p;
// 次数2^(p-1) = t360の4n乗根フーリエ変換結果の積を逆フーリエ変換する。
// 結果は実部と虚部を合わせて2^p = (2 * t360)桁となる。
const pow_t t360 = (size_t)1 << (p - 1);
assert(p == c.p);
assert((size_t)(2 * t360) <= d.data.size());
// 積
for (i = 0; i < (size_t)t360; i++) {
const double b_i_re = b.data[2 * i];
const double b_i_im = b.data[2 * i + 1];
const double c_i_re = c.data[2 * i];
const double c_i_im = c.data[2 * i + 1];
// d[i] = b[i] * c[i]
const double d_i_re = b_i_re * c_i_re - b_i_im * c_i_im;
const double d_i_im = b_i_re * c_i_im + b_i_im * c_i_re;
d.data[2 * i] = d_i_re;
d.data[2 * i + 1] = d_i_im;
}
#ifdef SHOW_ITM
printf("Before fouri4ninv():\n");
show_re_im_seq(d.data, d.p);
#endif
// 逆フーリエ変換
fft4n::fouri4ninv(d.data.begin(), p - 1, w);
#ifdef SHOW_ITM
printf("After fouri4ninv():\n");
show_re_im_seq(d.data, d.p);
#endif
const size_t s = std::min(a.data.size(), (size_t)(2 * t360));
// Carry and fix処理
// ただし対象とする桁の並びd.data[]は、
// 桁の値が[0, RADIX)ではなく、[-RADIX / 2, RADIX / 2)である。
carry = 0;
t = 1;
//const double k = ldexp(1.0, -p + 1); // k = 1.0 * 2^(-p + 1)
double k = 1.0; // fouri4ninv()結果は2^pで割る必要無し。
for (i = 0; i < s; i++) {
// 実部虚部隣接化解除
// b.data[t360..]を虚部に配置する。
const size_t src_i = (i < (size_t)t360) ? 2 * i : 2 * (i - (size_t)t360) + 1;
//printf("t360=%lld, s=%zu, i=%zu, src_i=%zu\n", t360, s, i, src_i);
const double x = d.data[src_i] * k;
int64_t e = (int64_t)floor(x + 0.5);
err_max = std::max(err_max, fabs(x - e));
e += carry;
carry = e / RADIX;
int64_t f = e - carry * RADIX;
if (f < 0) {
f += RADIX;
carry--;
}
a.data[i] = (I_TYPE)f;
if (f != 0) {
t = i + 1;
}
}
a.ndigits = t;
if (carry != 0) {
if (a.ndigits < a.data.size()) {
assert(0 <= carry && carry < RADIX);
a.data[a.ndigits++] = (I_TYPE)carry;
}
else {
// ここに来たら桁あふれ
abort();
}
}
for (; i < s; i++) {
// 実部虚部隣接化解除
const size_t src_i = (i < (size_t)t360) ? 2 * i : 2 * i + 1;
const double x = d.data[src_i] * k;
const int64_t e = (int64_t)floor(x + 0.5);
err_max = std::max(err_max, fabs(x - e));
assert(e == 0);
}
}
/// 整数の和
void add_i(I& a, const I& b_, const I& c_) {
I_TYPE carry, d;
size_t i;
const I& b = (b_.ndigits > c_.ndigits) ? c_ : b_; // Alias
const I& c = (b_.ndigits > c_.ndigits) ? b_ : c_; // Alias
assert(b.ndigits <= c.ndigits);
assert(c.ndigits <= a.data.size());
carry = 0;
for (i = 0; i < b.ndigits; i++) {
d = b.data[i] + c.data[i] + carry;
carry = 0;
if (d >= RADIX) {
d -= RADIX;
carry++;
}
a.data[i] = d;
}
for (; i < c.ndigits; i++) {
d = c.data[i] + carry;
carry = 0;
if (d >= RADIX) {
d -= RADIX;
carry++;
}
a.data[i] = d;
}
a.ndigits = c.ndigits;
if (a.ndigits < a.data.size()) {
if (carry != 0) {
a.data[a.ndigits++] = carry;
}
}
else {
if (carry != 0) {
// ここに来たら桁あふれ
abort();
}
}
}
/// 整数のファイル出力
void print(const char* const filename, const I& a)
{
char format[10];
int nRet;
assert(a.data.size() > (size_t)0);
assert(a.ndigits <= a.data.size());
FILE* fp;
nRet = fopen_s_wrp(&fp, filename, "w");
if (nRet != 0) {
fflush(stdout);
fprintf(stderr, "ERROR: fopen_s_wrp() failed. (nRet=%d)\n", nRet);
abort();
}
sprintf_s_wrp(format, _countof(format), "%%0%du", FIG);
size_t s = a.ndigits;
fprintf(fp, "%lu", a.data[--s]);
while (s != 0) {
fprintf(fp, format, a.data[--s]);
}
fclose(fp);
}
} // namespace oper
namespace
{
// N ≧ log2(s1 * s2)を満たす最小のNを求める。
int s2p(const size_t s1, const size_t s2) {
int i;
for (i = 1; i < 64; i++) {
if (((size_t)1 << i) + 1 >= s1 + s2) {
// (2^i + 1 ≧ (s1の有効桁数) + (s2の有効桁数))
break;
}
}
return i;
}
} // namespace
int main()
{
using namespace oper;
const int n = 34;
nth_root w;
const int max_p = s2p(((size_t)((1LLU << n) * log10(2)) / FIG + 1), 1);
const size_t cap = (size_t)1 << max_p;
I a(cap);
F x(max_p);
a.data[0] = 1;
a.ndigits = 1;
double err_max = 0.0;
for (int i = 0; i < n; i++) {
// 多倍長整数xのフーリエ変換
// p ≧ log2(a * a)を満たす最小のpを求め、
// aの次数2^pのフーリエ変換結果をxに格納する。
const int p = s2p(a.ndigits, a.ndigits);
i2f(x, a, p, w);
// x = x*x、および結果のxを整数に戻してaに格納する
mul_f2i(a, x, x, x, err_max, w);
add_i(a, a, a);
// 途中経過出力
{
char filename[200];
sprintf_s_wrp(filename, _countof(filename), "%d.txt", i + 1);
print(filename, a);
}
printf("%d %f\n", i + 1, err_max);
}
return 0;
}