#include <math.h>
#include "ulong_extras.h"
#include "arf.h"
#include "fmpq.h"
#include "acb_poly.h"
#include "gr.h"
#include "gr_vec.h"
#include "gr_poly.h"
#include "nfloat.h"
static slong
_acb_get_mid_mag(const acb_t z)
{
slong rm, im;
rm = arf_abs_bound_lt_2exp_si(arb_midref(acb_realref(z)));
im = arf_abs_bound_lt_2exp_si(arb_midref(acb_imagref(z)));
return FLINT_MAX(rm, im);
}
static slong convex_hull(slong * res, const double * y, slong len)
{
slong i, n = 0;
for (i = 0; i < len; i++)
{
while (n >= 2 && (res[n - 2] - res[n - 1]) * (y[i] - y[res[n - 1]])
<= (i - res[n - 1]) * (y[res[n - 2]] - y[res[n - 1]]))
n--;
res[n] = i;
n++;
}
return n;
}
static void
_acb_poly_roots_initial_values(acb_ptr roots, acb_srcptr poly, slong deg, slong prec)
{
double * alog;
mag_ptr amag;
mag_t r;
arf_t ar;
slong i, j;
slong * ki, num, m, total;
double theta;
acb_t cmid;
acb_ptr ri;
amag = _mag_vec_init(deg + 1);
alog = flint_malloc(sizeof(double) * (deg + 1));
ki = flint_malloc((deg + 1) * sizeof(slong));
mag_init(r);
arf_init(ar);
acb_init(cmid);
for (i = 0; i <= deg; i++)
{
*arb_midref(acb_realref(cmid)) = *arb_midref(acb_realref(poly + i));
*arb_midref(acb_imagref(cmid)) = *arb_midref(acb_imagref(poly + i));
acb_get_mag(amag + i, cmid);
alog[i] = mag_get_d_log2_approx(amag + i);
}
num = convex_hull(ki, alog, deg + 1);
total = 0;
for (i = 1; i < num; i++)
{
m = ki[i] - ki[i - 1];
mag_div(r, amag + ki[i - 1], amag + ki[i]);
mag_root(r, r, m);
if (mag_is_zero(r))
mag_set_ui_2exp_si(r, 1, -prec);
if (mag_is_inf(r))
mag_set_ui_2exp_si(r, 1, prec);
arf_set_mag(ar, r);
#if 0#endif
for (j = 0; j < m; j++)
{
double irrational_factor = 0.99071828182845905;
theta = 6.283185307179586 * (irrational_factor * (j + 1.0) / m + (double) i / deg) + 0.577216;
ri = roots + total;
acb_zero(ri);
arf_set_d(arb_midref(acb_realref(ri)), cos(theta));
arf_set_d(arb_midref(acb_imagref(ri)), sin(theta));
arf_mul(arb_midref(acb_realref(ri)), arb_midref(acb_realref(ri)), ar, MAG_BITS, ARF_RND_DOWN);
arf_mul(arb_midref(acb_imagref(ri)), arb_midref(acb_imagref(ri)), ar, MAG_BITS, ARF_RND_DOWN);
total++;
}
}
if (total != deg)
flint_abort();
_mag_vec_clear(amag, deg + 1);
flint_free(alog);
flint_free(ki);
mag_clear(r);
arf_clear(ar);
}
#define USE_ABERTH 0
static int
_acb_poly_find_roots_iter(gr_ptr w, gr_ptr z, gr_srcptr f, gr_srcptr f_prime, slong deg, slong maxiter, gr_ctx_t fp_ctx, gr_ctx_t acb_ctx, slong prec)
{
slong iter, i;
slong rootmag, max_rootmag, correction, max_correction;
slong sz = fp_ctx->sizeof_elem;
int status = GR_SUCCESS;
acb_t t;
acb_init(t);
for (iter = 0; iter < maxiter; iter++)
{
{
max_rootmag = -WORD_MAX;
for (i = 0; i < deg; i++)
{
status |= gr_set_other(t, GR_ENTRY(z, i, sz), fp_ctx, acb_ctx);
rootmag = _acb_get_mid_mag(t);
max_rootmag = FLINT_MAX(rootmag, max_rootmag);
}
}
#if USE_ABERTH
status |= _gr_poly_refine_roots_aberth(w, f, fprime, deg, z, 1, fp_ctx);
#else
status |= _gr_poly_refine_roots_wdk(w, f, deg, z, 1, fp_ctx);
#endif
max_correction = -ARF_PREC_EXACT;
for (i = 0; i < deg; i++)
{
status |= gr_set_other(t, GR_ENTRY(w, i, sz), fp_ctx, acb_ctx);
correction = _acb_get_mid_mag(t);
max_correction = FLINT_MAX(correction, max_correction);
}
status |= _gr_vec_sub(z, z, w, deg, fp_ctx);
max_correction -= max_rootmag;
if (max_correction < -prec / 2)
maxiter = FLINT_MIN(maxiter, iter + 2);
else if (max_correction < -prec / 3)
maxiter = FLINT_MIN(maxiter, iter + 3);
else if (max_correction < -prec / 4)
maxiter = FLINT_MIN(maxiter, iter + 4);
}
acb_clear(t);
return status;
}
static slong convex_hull_upper(slong * res, const double * y, const slong * x, slong len)
{
slong i, n = 0;
for (i = 0; i < len; i++)
{
while (n >= 2 && (x[res[n - 2]] - x[res[n - 1]]) * (y[i] - y[res[n - 1]])
<= (x[i] - x[res[n - 1]]) * (y[res[n - 2]] - y[res[n - 1]]))
n--;
res[n] = i;
n++;
}
return n;
}
static double
_minimax_line(double * res_a, double * res_b, const double * y, const slong * x, slong len)
{
slong * hU;
slong nU, pL, pU;
slong i, j;
double dyL, dyU;
slong dxL, dxU;
double a, b, rL, rU;
int last_side = 0;
if (len < 2)
{
*res_a = 0.0;
*res_b = (len == 1) ? y[0] : 0.0;
return 0.0;
}
hU = flint_malloc((len + 1) * sizeof(slong));
nU = convex_hull_upper(hU + 1, y, x, len);
hU[0] = 0;
slong hL[3] = {0, len-1, 0};
pL = 0;
pU = nU;
dyL = y[hL[1]] - y[hL[0]];
dxL = x[hL[1]] - x[hL[0]];
dyU = y[hU[pU]] - y[hU[pU - 1]];
dxU = x[hU[pU]] - x[hU[pU - 1]];
while (x[hL[pL]] < x[hU[pU]])
{
if (dyL * dxU < dyU * dxL)
{
pL++;
dyL = y[hL[pL + 1]] - y[hL[pL]];
dxL = x[hL[pL + 1]] - x[hL[pL]];
last_side = 0;
}
else
{
pU--;
dyU = y[hU[pU]] - y[hU[pU - 1]];
dxU = x[hU[pU]] - x[hU[pU - 1]];
last_side = 1;
}
}
i = last_side ? hU[pU + 1] : hL[pL - 1];
j = last_side ? hU[pU] : hL[pL];
a = (y[j] - y[i]) / (x[j] - x[i]);
rL = y[hL[pL]] - a * x[hL[pL]];
rU = y[hU[pU]] - a * x[hU[pU]];
b = (rU + rL) * 0.5;
*res_a = a;
*res_b = b;
flint_free(hU);
return (rU - rL) * 0.5;
}
static double
_best_fit_double(arb_t scale, double * cdp, const acb_srcptr poly, slong len)
{
double *alog;
double a, b, mdist;
slong * nonzero;
slong i, j, prec, prec2;
mag_t m;
acb_t cmid, cscale;
arb_t c, log2;
arb_init(log2);
arb_init(c);
acb_init(cmid);
acb_init(cscale);
mag_init(m);
prec = 53;
arb_log_ui(log2, 2, prec);
prec2 = prec + 2*ceil(log(len)/log(2));
alog = flint_malloc(sizeof(double) * len);
nonzero = flint_malloc(sizeof(slong) * len);
j = 0;
for (i = 0; i < len; i++)
{
*arb_midref(acb_realref(cmid)) = *arb_midref(acb_realref(poly + i));
*arb_midref(acb_imagref(cmid)) = *arb_midref(acb_imagref(poly + i));
acb_get_mag(m, cmid);
if( !mag_is_zero(m) ) {
alog[j] = mag_get_d_log2_approx(m);
nonzero[j] = i;
j++;
}
}
mdist = _minimax_line(&a, &b, alog, nonzero, j);
if(mdist > 1022) {
b += mdist - 1022;
}
arb_set_d(scale, -a);
arb_mul (scale, scale, log2, prec);
arb_exp (scale, scale, prec);
arb_set_d(c, -b);
arb_mul (c, c, log2, prec);
arb_exp (c, c, prec);
for(i=0; i<len; i++) {
acb_mul_arb(cscale, poly+i, c, prec);
cdp[2*i] = arf_get_d(arb_midref(acb_realref(cscale)), ARF_RND_NEAR);
cdp[2*i+1] = arf_get_d(arb_midref(acb_imagref(cscale)), ARF_RND_NEAR);
arb_mul(c, c, scale, prec2);
}
flint_free(alog);
flint_free(nonzero);
mag_clear(m);
acb_clear(cscale);
arb_clear(c);
arb_clear(log2);
return mdist;
}
double _acb_poly_find_roots_double(acb_ptr roots, acb_srcptr poly, acb_srcptr initial, slong len, slong maxiter, slong prec)
{
double *cdz, *cdp, *cdi;
double max_correction;
arb_t scale;
slong i;
arb_init(scale);
cdp = flint_malloc(2*len*sizeof(double));
cdz = flint_malloc(2*(len - 1)*sizeof(double));
_best_fit_double(scale, cdp, poly, len);
if(initial != NULL) {
cdi = cdz;
for(i=0; i<len-1; i++) {
acb_div_arb(roots + i, initial + i, scale, prec);
cdi[2*i] = arf_get_d(arb_midref(acb_realref(roots + i)), ARF_RND_NEAR);
cdi[2*i+1] = arf_get_d(arb_midref(acb_imagref(roots + i)), ARF_RND_NEAR);
}
} else {
cdi = NULL;
}
max_correction = cd_poly_find_roots(cdz, cdp, cdi, len, maxiter, ldexp(1,-prec));
for(i=0; i<len-1; i++) {
acb_set_d_d(roots + i, cdz[2*i], cdz[2*i+1]);
acb_mul_arb(roots + i, roots + i, scale, prec);
}
flint_free(cdz);
flint_free(cdp);
arb_clear(scale);
return max_correction;
}
slong
_acb_poly_find_roots(acb_ptr roots,
acb_srcptr poly,
acb_srcptr initial, slong len, slong maxiter, slong prec)
{
slong i, deg;
gr_ptr w, z, f, fprime;
gr_ctx_t acb_ctx, fp_ctx;
slong sz;
acb_t t;
int status = GR_SUCCESS;
int attempt;
int success_double = 0;
deg = len - 1;
if (deg == 0)
{
return 0;
}
else if (acb_contains_zero(poly + len - 1))
{
for (i = 0; i < deg; i++)
{
arb_zero_pm_inf(acb_realref(roots + i));
arb_zero_pm_inf(acb_imagref(roots + i));
}
return 0;
}
else if (deg == 1)
{
acb_inv(roots + 0, poly + 1, prec);
acb_mul(roots + 0, roots + 0, poly + 0, prec);
acb_neg(roots + 0, roots + 0);
return 1;
}
if (prec <= 53) {
slong fmaxiter = FLINT_MAX(150,maxiter);
double max_correction = _acb_poly_find_roots_double(roots, poly, initial, len, fmaxiter, prec);
success_double = (max_correction < ldexp(1, -3));
}
if(!success_double) {
if (initial == NULL)
_acb_poly_roots_initial_values(roots, poly, deg, prec);
else
_acb_vec_set(roots, initial, deg);
if (maxiter == 0)
maxiter = 2 * deg + n_sqrt(prec);
gr_ctx_init_complex_acb(acb_ctx, prec + 64);
acb_init(t);
for (attempt = 0; attempt <= 1; attempt++)
{
if (attempt == 0)
{
if (nfloat_complex_ctx_init(fp_ctx, prec, 0) != GR_SUCCESS)
continue;
}
else
{
#if 0#endif
gr_ctx_init_complex_float_acf(fp_ctx, prec);
}
status = GR_SUCCESS;
sz = fp_ctx->sizeof_elem;
z = gr_heap_init_vec(4 * deg + 1, fp_ctx);
w = GR_ENTRY(z, deg, sz);
fprime = GR_ENTRY(z, 2 * deg, sz);
f = GR_ENTRY(z, 3 * deg, sz);
for (i = 0; i <= deg; i++)
status |= gr_set_other(GR_ENTRY(f, i, sz), poly + i, acb_ctx, fp_ctx);
for (i = 0; i < deg; i++)
status |= gr_set_other(GR_ENTRY(z, i, sz), roots + i, acb_ctx, fp_ctx);
#if USE_ABERTH
status |= _gr_poly_derivative(fprime, f, deg + 1, fp_ctx);
#endif
if (status == GR_SUCCESS)
status = _acb_poly_find_roots_iter(w, z, f, fprime, deg, maxiter, fp_ctx, acb_ctx, prec);
if (status == GR_SUCCESS)
{
for (i = 0; i < deg; i++)
{
GR_MUST_SUCCEED(gr_set_other(roots + i, GR_ENTRY(z, i, sz), fp_ctx, acb_ctx));
mag_zero(arb_radref(acb_realref(roots + i)));
mag_zero(arb_radref(acb_imagref(roots + i)));
}
}
gr_heap_clear_vec(z, 4 * deg + 1, fp_ctx);
gr_ctx_clear(fp_ctx);
if (status == GR_SUCCESS)
break;
}
acb_clear(t);
gr_ctx_clear(acb_ctx);
}
return _acb_poly_validate_roots(roots, poly, len, prec);
}
slong
acb_poly_find_roots(acb_ptr roots,
const acb_poly_t poly, acb_srcptr initial,
slong maxiter, slong prec)
{
slong len = poly->length;
if (len == 0)
{
flint_throw(FLINT_ERROR, "find_roots: expected a nonzero polynomial");
}
return _acb_poly_find_roots(roots, poly->coeffs, initial, len, maxiter, prec);
}