// 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
#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 fft
{
void fr(
std::vector<double>::iterator s1,
std::vector<double>::iterator s2,
std::vector<double>::iterator end,
const size_t add,
const size_t t90,
const double sin1, const double cos1)
{
for (; s1 < end; (s1 += add, s2 += add)) {
#ifdef SHOW_ITM
printf("%s: end-s1=%lld, add=%zu\n", __FUNCTION__, end - s1, add);
#endif
const double r0 = s1[0];
const double r1 = s2[0];
const double r2 = s1[t90];
const double r3 = s2[t90];
// 1のN乗根wを cos1 + i * sin1 として、次式を計算
// (r2 + i * r3) * w
// = r2 * cos1 + r2 * i * sin1 + r3 * i * cos1 - r3 * sin1
// = (r2 * cos1 - r3 * sin1) + i * (r2 * sin1 + r3 * cos1)
const double m0 = r2 * cos1 - r3 * sin1; // 実部
const double m1 = r2 * sin1 + r3 * cos1; // 虚部
s1[0] = r0 + m0;
s2[0] = r0 - m0;
s1[t90] = -r1 + m1;
s2[t90] = r1 + m1;
}
}
void fri(
std::vector<double>::iterator s1,
std::vector<double>::iterator s2,
std::vector<double>::iterator end,
const size_t add,
const size_t t90,
const double sin1, const double cos1)
{
for (; s1 < end; (s1 += add, s2 += add)) {
const double r0 = s1[0];
const double r1 = s2[0];
const double r2 = s1[t90];
const double r3 = s2[t90];
const double m0 = r0 + r1;
const double m1 = -r2 + r3;
const double m2 = r0 - r1;
const double m3 = r2 + r3;
s1[0] = m0;
s2[0] = m1;
s1[t90] = m2 * cos1 + m3 * sin1;
s2[t90] = -m2 * sin1 + m3 * cos1;
}
}
void fr0(
std::vector<double>::iterator s1,
std::vector<double>::iterator end,
const size_t add,
const size_t t90)
{
for (; s1 < end; s1 += add) {
const double r0 = s1[0];
const double r1 = s1[t90];
s1[0] = r0 + r1;
s1[t90] = r0 - r1;
}
}
void bxchg(
std::vector<double>& s,
const int p)
{
// 2^pを8等分した境界位置
const size_t t045 = (size_t)1 << (p - 3); // 1 * 2^(p-3)
const size_t t090 = t045 + t045; // 2 * 2^(p-3)
const size_t t180 = t090 + t090; // 4 * 2^(p-3)
const size_t t270 = t180 + t090; // 6 * 2^(p-3)
/*
(計算例)
p=8のとき
t045 = 32 = B'00100000
t090 = 64 = B'01000000
t180 = 128 = B'10000000
t270 = 192 = B'11000000
t4 = t090 - 4 = 60
t5 = t4 = 60
*/
size_t t4 = t090 - 4; // 2 * 2^(p-3) - 4 --- p=4のとき0、p=5のとき4、p=6のとき28
size_t t5 = t4; // 2 * 2^(p-3) - 4 --- (同上)
for (;;) {
std::swap(s[t4 + 1], s[t180 + t5]); // 180 degを挟んだ2点を交換
std::swap(s[t4 + 2], s[t090 + t5]);
std::swap(s[t4 + 3], s[t270 + t5]);
std::swap(s[t090 + t4 + 1], s[t180 + t5 + 2]);
std::swap(s[t090 + t4 + 3], s[t270 + t5 + 2]);
std::swap(s[t180 + t4 + 3], s[t270 + t5 + 1]);
if (t4 < t5) {
std::swap(s[t4], s[t5]);
std::swap(s[t090 + t4 + 2], s[t090 + t5 + 2]);
std::swap(s[t180 + t4 + 1], s[t180 + t5 + 1]);
std::swap(s[t270 + t4 + 3], s[t270 + t5 + 3]);
}
if (t4 == 0) {
break;
}
t4 -= 4;
size_t t6 = t045;
for (;;) {
t5 ^= t6;
if ((t5 & t6) == 0) {
break;
}
t6 >>= 1;
}
}
}
void bitexchg(
std::vector<double>& s,
const int p)
{
if (p <= 1) {
/*pass*/
}
else if (p == 2) {
std::swap(s[1], s[2]);
}
else if (p == 3) {
std::swap(s[1], s[4]);
std::swap(s[3], s[6]);
}
else {
bxchg(s, p);
}
}
#ifdef SHOW_SIN_TABLE
size_t g_sin_tblent_cnt = 0;
#endif
/// sin関数のテーブル化
/// SIN_TABLE[p]に以下のリストを設定する。
/// SIN_TABLE[2] = [ sin(0) ]
/// SIN_TABLE[3] = [ sin(0), sin(45 deg) ]
/// SIN_TABLE[4] = [ sin(0), sin(90 / 4 deg), sin(45 deg), sin(90 * 3 / 4 deg) ]
/// ...
void sin_table(
const int p,
std::vector<std::vector<double> >& SIN_TABLE)
{
const size_t sv_sz = SIN_TABLE.size();
if (p >= SIN_TABLE.size()) {
// (pに対するテーブルが未作成)
// テーブルを収める領域を確保
SIN_TABLE.resize((size_t)(p + 1));
}
std::vector<double>& sin_tbl = SIN_TABLE[p]; // Alias
if (p >= 2 && sv_sz <= p) {
// (pが2以上かつpに対するテーブルが未作成だった)
// pに対するテーブルの領域を確保し、数値格納
const size_t t090 = (size_t)1 << (p - 2);
sin_tbl.resize(t090);
const double k = 0.5 * M_PI;
for (size_t i = 0; i < t090; i++) {
sin_tbl[i] = sin((k * i) / t090);
}
#ifdef SHOW_SIN_TABLE
g_sin_tblent_cnt += t090;
printf("******* %s: p=%d table made. (+%zu, %zu; sv_sz=%zu)\n", __FUNCTION__, p, t090, g_sin_tblent_cnt, sv_sz);
#endif
}
}
/// フーリエ変換
void fouri(
std::vector<double>& s,
const int p,
std::vector<std::vector<double> >& SIN_TABLE)
{
#ifdef SHOW_ITM
printf("%s: p=%d: Called.\n", __FUNCTION__, p);
#endif
sin_table(p, SIN_TABLE);
const std::vector<double>& table = SIN_TABLE[p];
const size_t t360 = (size_t)1 << p; // = 2^p
const size_t t090 = t360 >> 2; // = 2^(p-1)
bitexchg(s, p);
for (int i = 1; i <= p; i++) { // i = 1, 2, 3, ..., p
const size_t r360 = (size_t)1 << i; // = 2^i
const size_t r180 = r360 >> 1; // = 2^(i-1)
const size_t r090 = r180 >> 1; // = 2^(i-2)
fr0(s.begin(), s.begin() + t360, r360, r180);
size_t k1 = 1;
size_t k2 = r180 - 1; // = 2^(i-1)-1
const size_t t2 = (size_t)1 << (p - i); // = 2^(p-i) = (2^p) / (2^i) = t360 / r360 (幾何学的意味)
size_t t0 = t2; // = t360 / r360 (幾何学的意味)
size_t t1 = t090 - t2; // = t090 - t360 / r360 (幾何学的意味)
for (; k1 < r090; (k1++, k2--)) { // k1 = 1, 2, 3, ..., 2^(i-2)-1; k2 = 2^(i-1)-1, 2^(i-1)-2, 2^(i-1)-3, ..., 2^(i-2)+1
const double sin1 = table[t0]; // 1の(4*p)乗根の(2^(p-i))乗の虚部
const double cos1 = table[t1]; // 1の(4*p)乗根の(2^(p-i))乗の実部
#ifdef SHOW_ITM
{
const double degree = (2 * M_PI) / atan2(sin1, cos1);
printf("%s: i=%d, k1=%zu, k2=%zu, r090=%zu, w^%f=1, t0=%zu, t1=%zu\n",
__FUNCTION__,
i, k1, k2, r090, degree,
t0, t1);
const double ideg = floor(degree + 0.5);
if (std::abs(degree - ideg) > 0.1) {
printf("*** fraction ***\n");
}
}
#endif
fr(s.begin() + k1, s.begin() + k2, s.begin() + t360, r360, r180, sin1, cos1);
t0 += t2; // t0 = t360/r360, (2*t360)/r360, (3*t360)/r360, ..., ((2^(i-2)-1)*2^p)/2^i, ..., ((2^(i-2)-1)*t360)/r360
t1 -= t2;
}
}
#ifdef SHOW_ITM
printf("%s: p=%d: Done.\n", __FUNCTION__, p);
#endif
}
/// 逆フーリエ変換
void fouriinv(
std::vector<double>& s,
const int p,
std::vector<std::vector<double> >& SIN_TABLE)
{
sin_table(p, SIN_TABLE);
const std::vector<double>& table = SIN_TABLE[p];
const size_t t360 = (size_t)1 << p;
const size_t t090 = t360 >> 2;
for (int i = p; i >= 1; i--) {
const size_t r360 = (size_t)1 << i;
const size_t r180 = r360 >> 1;
const size_t r090 = r180 >> 1;
fr0(s.begin(), s.begin() + t360, r360, r180);
size_t k1 = 1;
size_t k2 = r180 - 1;
size_t t2 = (size_t)1 << (p - i);
size_t t0 = t2;
size_t t1 = t090 - t2;
for (; k1 < r090; k1++, k2--) {
const double sin1 = table[t0];
const double cos1 = table[t1];
fri(s.begin() + k1, s.begin() + k2, s.begin() + t360, r360, r180, sin1, cos1);
t0 += t2;
t1 -= t2;
}
}
bitexchg(s, p);
}
} // namespace fft
typedef uint32_t I_TYPE;
const int FIG = 3;
const I_TYPE RADIX = 1000;
namespace oper
{
using namespace fft;
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, std::vector<std::vector<double> >& SIN_TABLE)
{
size_t i;
const size_t t360 = (size_t)1 << p;
assert(b.ndigits <= t360);
assert(t360 <= a.data.size());
#ifdef SHOW_I
printf("%s: Before pre tr.:\n", __FUNCTION__);
for (size_t ii = 0; ii < b.ndigits; ii++) {
printf(" %03ld", b.data[ii]);
}
printf("\n");
for (size_t ii = b.ndigits; ii < b.data.size(); ii++) {
if (b.data[ii] != 0) {
abort();
}
}
#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++) {
const int32_t t = b.data[i] + carry; // 桁[i]の値
if (t < (int32_t)RADIX / 2) {
// (桁[i]の値∈[0, RADIX / 2) )
// 何もしない
a.data[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[i] = (double)(t - (int32_t)RADIX);
carry = 1;
}
}
// 最上位桁
a.data[i] = (double)(b.data[i] + (I_TYPE)carry);
i++;
for (; i < t360; i++) {
a.data[i] = 0.0;
}
#ifdef SHOW_I
{
size_t n;
for (n = a.data.size(); n > 0; n--) {
if (std::abs(a.data[n - 1]) >= 0.000001) {
break;
}
}
printf("%s: After pre tr.:\n", __FUNCTION__);
for (size_t ii = 0; ii < n; ii++) {
printf(" %03.0f", a.data[ii]);
}
printf("\n");
}
#endif
// フーリエ変換実施
fouri(a.data, p, SIN_TABLE);
}
/// 整数のフーリエ変換結果の積(結果は整数)
void mul_f2i(I& a, const F& b, const F& c, F& d, double& err_max, std::vector<std::vector<double> >& SIN_TABLE)
{
size_t i, t, t180, t360, k1, k2;
int64_t carry;
const int p = b.p;
t180 = (size_t)1 << (p - 1);
t360 = t180 + t180;
assert(p == c.p);
assert(t360 <= b.data.size());
assert(t360 <= c.data.size());
assert(t360 <= d.data.size());
d.data[0] = b.data[0] * c.data[0] * 0.5;
d.data[t180] = b.data[t180] * c.data[t180] * 0.5;
k1 = 1;
k2 = t360 - 1;
for (; k1 < t180; (k1++, k2--)) {
const double re = b.data[k1] * c.data[k1] - b.data[k2] * c.data[k2];
const double im = b.data[k1] * c.data[k2] + b.data[k2] * c.data[k1];
d.data[k1] = re;
d.data[k2] = im;
}
// 逆フーリエ変換
fouriinv(d.data, p, SIN_TABLE);
const size_t s = std::min(a.data.size(), 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)
for (i = 0; i < s; i++) {
const double x = d.data[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 < t360; i++) {
const double x = d.data[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 {
// ここに来たら桁あふれ
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
bool fft_test()
{
using namespace fft;
using namespace oper;
const int max_p = 5;
const size_t cap = (size_t)1 << max_p;
I a(cap);
F x(max_p);
x.p = max_p;
for (size_t i = 0; i < cap; i++) {
x.data[i] = (double)i;
}
// フーリエ変換実施
std::vector<std::vector<double> > SIN_TABLE; // working object for fouri()
fouri(x.data, x.p, SIN_TABLE);
return true;
}
int main()
{
using namespace oper;
#ifdef SHOW_ITM
if (fft_test()) {
return 0;
}
#endif
std::vector<std::vector<double> > SIN_TABLE;
const int n = 34;
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, SIN_TABLE);
// x = x*x、および結果のxを整数に戻してaに格納する
mul_f2i(a, x, x, x, err_max, SIN_TABLE);
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;
}