/*
Copyright (C) 2011 Fredrik Johansson
This file is part of FLINT.
FLINT is free software: you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License (LGPL) as published
by the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version. See .
*/
#include
#include "arith.h"
#define MAX_32BIT 58
static const int lookup_table[MAX_32BIT][28] =
{
{-1, 1}, {1, 1}, {1, 2}, {0, 1}, {-1, 2, 4}, {-1, 2},
{-1, -4, 4, 8}, {-1, 0, 2}, {1, -6, 0, 8}, {-1, -2, 4},
{1, 6, -12, -32, 16, 32}, {-3, 0, 4}, {-1, 6, 24, -32, -80, 32, 64},
{1, -4, -4, 8}, {1, 8, -16, -8, 16}, {1, 0, -8, 0, 8},
{1, -8, -40, 80, 240, -192, -448, 128, 256}, {-1, -6, 0, 8},
{1, 10, -40, -160, 240, 672, -448, -1024, 256, 512}, {5, 0, -20, 0, 16},
{1, -16, 32, 48, -96, -32, 64}, {-1, 6, 12, -32, -16, 32},
{-1, -12, 60, 280, -560, -1792, 1792, 4608, -2304, -5120, 1024, 2048},
{1, 0, -16, 0, 16}, {-1, 10, 100, -40, -800, 32, 2240, 0, -2560, 0,
1024}, {-1, -6, 24, 32, -80, -32, 64},
{1, 18, 0, -240, 0, 864, 0, -1152, 0, 512}, {-7, 0, 56, 0, -112, 0, 64},
{-1, 14, 112, -448, -2016, 4032, 13440, -15360, -42240, 28160, 67584,
-24576, -53248, 8192, 16384}, {1, -8, -16, 8, 16},
{-1, -16, 112, 672, -2016, -8064, 13440, 42240, -42240, -112640, 67584,
159744, -53248, -114688, 16384, 32768},
{1, 0, -32, 0, 160, 0, -256, 0, 128},
{1, -24, 48, 344, -688, -1088, 2176, 1280, -2560, -512, 1024},
{1, 8, -40, -80, 240, 192, -448, -128, 256},
{1, 16, -160, -368, 1760, 2272, -7232, -5504, 13824, 5632, -12288,
-2048, 4096}, {-3, 0, 36, 0, -96, 0, 64},
{-1, 18, 180, -960, -5280, 14784, 59136, -101376, -329472, 366080,
1025024, -745472, -1863680, 860160, 1966080, -524288, -1114112, 131072,
262144}, {-1, 10, 40, -160, -240, 672, 448, -1024, -256, 512},
{1, 24, -48, -632, 1264, 3296, -6592, -6784, 13568, 6144, -12288, -2048,
4096}, {1, 0, -48, 0, 304, 0, -512, 0, 256},
{1, -20, -220, 1320, 7920, -25344, -109824, 219648, 768768, -1025024,
-3075072, 2795520, 7454720, -4587520, -11141120, 4456448, 10027008,
-2359296, -4980736, 524288, 1048576}, {1, 16, 32, -48, -96, 32, 64},
{1, 22, -220, -1760, 7920, 41184, -109824, -439296, 768768, 2562560,
-3075072, -8945664, 7454720, 19496960, -11141120, -26738688, 10027008,
22413312, -4980736, -10485760, 1048576, 2097152},
{-11, 0, 220, 0, -1232, 0, 2816, 0, -2816, 0, 1024},
{1, -24, -144, 248, 1680, -864, -7168, 1152, 13824, -512, -12288, 0,
4096}, {1, -12, -60, 280, 560, -1792, -1792, 4608, 2304, -5120, -1024,
2048}, {-1, -24, 264, 2288, -11440, -64064, 192192, 823680, -1647360,
-5857280, 8200192, 25346048, -25346048, -70189056, 50135040, 127008768,
-63504384, -149422080, 49807360, 110100480, -22020096, -46137344,
4194304, 8388608}, {1, 0, -64, 0, 320, 0, -512, 0, 256},
{-1, 28, 196, -2968, -3136, 66304, 18816, -658816, -53760, 3587584,
78848, -11741184, -57344, 24084480, 16384, -31195136, 0, 24772608, 0,
-11010048, 0, 2097152}, {-1, -10, 100, 40, -800, -32, 2240, 0, -2560,
0, 1024}, {1, 32, -64, -1504, 3008, 16832, -33664, -76288, 152576,
173568, -347136, -210944, 421888, 131072, -262144, -32768, 65536},
{13, 0, -364, 0, 2912, 0, -9984, 0, 16640, 0, -13312, 0, 4096},
{-1, 26, 364, -2912, -21840, 96096, 512512, -1464320, -6223360,
12446720, 44808192, -65175552, -206389248, 222265344, 635043840,
-508035072, -1333592064, 784465920, 1917583360, -807403520,
-1857028096, 530579456, 1157627904, -201326592, -419430400, 33554432,
67108864}, {-1, 18, 0, -240, 0, 864, 0, -1152, 0, 512},
{1, 24, -432, -1208, 15216, 28064, -185024, -263424, 1149184, 1250304,
-4177920, -3356672, 9375744, 5324800, -13123584, -4947968, 11141120,
2490368, -5242880, -524288, 1048576},
{1, 0, -96, 0, 1376, 0, -6656, 0, 13568, 0, -12288, 0, 4096},
{1, -40, 80, 2120, -4240, -31648, 63296, 194432, -388864, -613376,
1226752, 1087488, -2174976, -1097728, 2195456, 589824, -1179648,
-131072, 262144}, {-1, -14, 112, 448, -2016, -4032, 13440, 15360,
-42240, -28160, 67584, 24576, -53248, -8192, 16384}
};
/* The coefficients in 2^d * \prod_{i=1}^d (x - cos(a_i)) are
easily bounded using the binomial theorem. */
static slong
magnitude_bound(slong d)
{
slong res;
fmpz_t t;
fmpz_init(t);
fmpz_bin_uiui(t, d, d / 2);
res = fmpz_bits(t);
fmpz_clear(t);
return FLINT_ABS(res) + d;
}
static void
fmpz_mul_or_div_2exp(fmpz_t x, fmpz_t y, slong s)
{
if (s >= 0)
fmpz_mul_2exp(x, y, s);
else
fmpz_fdiv_q_2exp(x, y, -s);
}
/* Balanced product of linear factors (x+alpha_i) using
fixed-point arithmetic with prec bits */
static void
balanced_product(fmpz * c, fmpz * alpha, slong len, slong prec)
{
if (len == 1)
{
fmpz_one(c + 1);
fmpz_mul_2exp(c + 1, c + 1, prec);
fmpz_set(c, alpha);
}
else if (len == 2)
{
fmpz_mul(c, alpha, alpha + 1);
fmpz_fdiv_q_2exp(c, c, prec);
fmpz_add(c + 1, alpha, alpha + 1);
fmpz_one(c + 2);
fmpz_mul_2exp(c + 2, c + 2, prec);
}
else
{
fmpz *L, *R;
slong i, m;
m = len / 2;
L = _fmpz_vec_init(len + 2);
R = L + m + 1;
balanced_product(L, alpha, m, prec);
balanced_product(R, alpha + m, len - m, prec);
_fmpz_poly_mul(c, R, len - m + 1, L, m + 1);
for (i = 0; i < len + 1; i++)
fmpz_fdiv_q_2exp(c + i, c + i, prec);
_fmpz_vec_clear(L, len + 2);
}
}
void
_arith_cos_minpoly(fmpz * coeffs, slong d, ulong n)
{
slong i, j;
fmpz * alpha;
fmpz_t half;
mpfr_t t, u;
flint_bitcnt_t prec;
slong exp;
if (n <= MAX_32BIT)
{
for (i = 0; i <= d; i++)
fmpz_set_si(coeffs + i, lookup_table[n - 1][i]);
return;
}
/* Direct formula for odd primes > 3 */
if (n_is_prime(n))
{
slong s = (n - 1) / 2;
switch (s % 4)
{
case 0:
fmpz_set_si(coeffs, WORD(1));
fmpz_set_si(coeffs + 1, -s);
break;
case 1:
fmpz_set_si(coeffs, WORD(1));
fmpz_set_si(coeffs + 1, s + 1);
break;
case 2:
fmpz_set_si(coeffs, WORD(-1));
fmpz_set_si(coeffs + 1, s);
break;
case 3:
fmpz_set_si(coeffs, WORD(-1));
fmpz_set_si(coeffs + 1, -s - 1);
break;
}
for (i = 2; i <= s; i++)
{
slong b = (s - i) % 2;
fmpz_mul2_uiui(coeffs + i, coeffs + i - 2, s+i-b, s+2-b-i);
fmpz_divexact2_uiui(coeffs + i, coeffs + i, i, i-1);
fmpz_neg(coeffs + i, coeffs + i);
}
return;
}
prec = magnitude_bound(d) + 5 + FLINT_BIT_COUNT(d);
alpha = _fmpz_vec_init(d);
fmpz_init(half);
mpfr_init2(t, prec);
mpfr_init2(u, prec);
fmpz_one(half);
fmpz_mul_2exp(half, half, prec - 1);
mpfr_const_pi(t, prec);
mpfr_div_ui(t, t, n, MPFR_RNDN);
for (i = j = 0; j < d; i++)
{
if (n_gcd(n, i) == 1)
{
mpfr_mul_ui(u, t, 2 * i, MPFR_RNDN);
mpfr_cos(u, u, MPFR_RNDN);
mpfr_neg(u, u, MPFR_RNDN);
exp = mpfr_get_z_2exp(_fmpz_promote(alpha + j), u);
_fmpz_demote_val(alpha + j);
fmpz_mul_or_div_2exp(alpha + j, alpha + j, exp + prec);
j++;
}
}
balanced_product(coeffs, alpha, d, prec);
/* Scale and round */
for (i = 0; i < d + 1; i++)
{
slong r = d;
if ((n & (n - 1)) == 0)
r--;
fmpz_mul_2exp(coeffs + i, coeffs + i, r);
fmpz_add(coeffs + i, coeffs + i, half);
fmpz_fdiv_q_2exp(coeffs + i, coeffs + i, prec);
}
fmpz_clear(half);
mpfr_clear(t);
mpfr_clear(u);
_fmpz_vec_clear(alpha, d);
}
void
arith_cos_minpoly(fmpz_poly_t poly, ulong n)
{
if (n == 0)
{
fmpz_poly_set_ui(poly, UWORD(1));
}
else
{
slong d = (n <= 2) ? 1 : n_euler_phi(n) / 2;
fmpz_poly_fit_length(poly, d + 1);
_arith_cos_minpoly(poly->coeffs, d, n);
_fmpz_poly_set_length(poly, d + 1);
}
}