/*
Copyright (C) 2010, 2016, 2020 William Hart
Copyright (C) 2010 Andy Novocin
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
#include
#include
#include "flint.h"
#include "fmpz.h"
#include "fmpz_poly.h"
#define CLD_EPS 0.00000001
static double _log2(double n)
{
return log(n)/log(2);
}
static int _d_cmp_2exp(double a, slong a_exp, double b, slong b_exp)
{
long t;
if (a_exp == 0)
{
if (b_exp == 0)
{
if (a > 1.5*b)
return 2;
else if (b > 1.5*a)
return -2;
else if (a >= b)
return 1;
else
return -1;
}
t = 1 + (long) _log2(a);
if (t >= b_exp + 2) /* a, a_exp >= 2*(b, b_exp) */
return 2;
else if (b_exp >= t + 2) /* b, b_exp >= 2*(a, a_exp) */
return -2;
else /* divide both values by 4 and try again */
return _d_cmp_2exp(a/4, 0, b*pow(2.0, (double) b_exp - 2.0), 0);
} else if (b_exp == 0)
return -_d_cmp_2exp(b, b_exp, a, a_exp);
else /* neither a_exp not b_exp is zero */
{
if (a_exp >= b_exp + 2) /* a, a_exp >= 2*(b, b_exp) */
return 2;
else if (b_exp >= a_exp + 2) /* b, b_exp >= 2*(a, a_exp) */
return -2;
else if (a_exp >= b_exp) /* shift exponents so one is 0 */
return _d_cmp_2exp(a, a_exp - b_exp, b, 0);
else
return -_d_cmp_2exp(b, b_exp - a_exp, a, 0);
}
}
void fmpz_poly_CLD_bound(fmpz_t res, const fmpz_poly_t f, slong n)
{
/*
Given: f = a_0 + ... + a_N x^N and n in {0, 1, ..., N - 1}
Let B_1(r) = 1/(r^{n+1})(|a_0| + ... + |a_n|r^n)
Let B_2(r) = 1/(r^{n+1})(|a_{n + 1}|r^{n+1} + ... + |a_N|r^N)
Find r > 0 such that Max{B_1(r), B_2(r)} is minimized
Return N*Max{B_1(r), B_2(r)}
*/
/*
We let hi(x) = 1/x^(n+1) * (|a_{n+1}|*x^(n+1) + ... + |a_N|*x^N)
so that hi(r) = |a_{n+1}| + ... + |a_N|*r^(N-n-1) = B_2(r)
and let lo(x) = |a_n|*x + ... + |a_0|*x^(n+1)
so that lo(1/r) = |a_n|/r + ... + |a_0|/r^{n+1} = B_1(r)
*/
/*
We notionally begin by letting lo_eval = B_1(0) which has the
value +INF. And we let hi_eval = B_2(0) which has the value
|a_{n+1}|.
We see that lo_eval is too large, so we set step = 1 and add
step to the power of the evaluation point for B_1. We keep adding
step until lo_eval is not too large. We then halve step and switch
its direction. We keep doing this until we have refined the value
sufficiently.
*/
fmpz_poly_t lo, hi;
double rpow = 0, hi_eval, lo_eval, max_eval;
double step = 1.0;
slong size_f = FLINT_ABS(fmpz_poly_max_bits(f));
slong hi_exp = 0, lo_exp = 0;
slong rbits, max_exp, rexp = 0;
int too_much = 0;
fmpz_poly_init(lo);
fmpz_poly_init(hi);
/* compute lo(x) and hi(x) */
fmpz_poly_set_trunc(lo, f, n + 1);
fmpz_poly_scalar_abs(lo, lo);
fmpz_poly_reverse(lo, lo, n + 2);
fmpz_poly_shift_right(hi, f, n + 1);
fmpz_poly_scalar_abs(hi, hi);
/* refine guess */
while (1)
{
/* compute approx max_exp that evaluation will use */
rbits = fabs(rpow);
max_exp = rbits*hi->length + size_f + 1;
if (rbits > 200 || rexp != 0) /* r itself has a large exponent */
{
/* r is really 2^rpow * 2^rexp */
double r = pow(2.0, rpow);
hi_eval = fmpz_poly_evaluate_horner_d_2exp2(&hi_exp, hi, r, rexp);
lo_eval = fmpz_poly_evaluate_horner_d_2exp2(&lo_exp, lo, 1/r, -rexp);
} /* if max exponent may overwhelm a double (with safety margin) */
else if (max_exp > 950 || too_much) /* result of eval has large exponent */
{
double r = pow(2.0, rpow);
hi_eval = fmpz_poly_evaluate_horner_d_2exp(&hi_exp, hi, r);
lo_eval = fmpz_poly_evaluate_horner_d_2exp(&lo_exp, lo, 1/r);
}
else /* everything can be handled with doubles */
{
double r = pow(2.0, rpow);
hi_eval = fmpz_poly_evaluate_horner_d(hi, r);
lo_eval = fmpz_poly_evaluate_horner_d(lo, 1/r);
hi_exp = lo_exp = 0;
}
/* if doubles suffice */
if (hi_exp == 0 && lo_exp == 0)
{
if (1.5*lo_eval < hi_eval)
{
/* lo_eval is too small */
if (step >= 0.0)
step = -step/2.0;
rpow += step;
} else if (lo_eval > 1.5*hi_eval)
{
/* lo_eval is too big */
if (step < 0.0)
step = -step/2.0;
rpow += step;
} else if (hi_eval != hi_eval || lo_eval != lo_eval)
{
/* doubles were insufficient after all */
too_much = 1;
} else /* we are done */
{
if (hi_eval > lo_eval)
max_eval = hi_eval;
else
max_eval = lo_eval;
/* we adjust due to rounding errors in horner's evaluation */
fmpz_set_d(res, max_eval*(f->length - 1)*(1.0 + f->length*ldexp(2.0, -51)));
goto cleanup;
}
} else
{
/*
too big for doubles alone, so we represent in d*2^exp format
_d_cmp_2exp will return 2 when 2*lo < hi, -2 when 2*hi < lo
1 when lo <= hi and -1 when lo > hi
*/
int cmp;
/* just compare exponents, because it's probably not going to converge otherwise */
if (rbits > 200 || rexp != 0)
{
if ((hi_exp + _log2(hi_eval)) > 1.01 * (lo_exp + _log2(lo_eval)))
cmp = 2;
else if ((lo_exp + _log2(lo_eval)) > 1.01 * (hi_exp + _log2(hi_eval)))
cmp = -2;
else if ((hi_exp + _log2(hi_eval)) >= (lo_exp + _log2(lo_eval)))
cmp = 1;
else
cmp = -1;
}
else
{
cmp = _d_cmp_2exp(hi_eval, hi_exp, lo_eval, lo_exp);
}
if (fabs(step) < CLD_EPS) /* give up if step is now very small */
{
cmp = cmp == 2 ? 1 : -1;
}
switch (cmp)
{
case 2:
if (step >= 0.0)
step = -step/2.0;
rpow += step;
break;
case -2:
if (step < 0.0)
step = -step/2.0;
rpow += step;
break;
case 1:
/* we adjust due to rounding errors in horner's evaluation */
fmpz_set_d_2exp(res, hi_eval*(f->length - 1)*(1.0 + f->length*ldexp(2.0, -51)), hi_exp);
goto cleanup;
case -1:
/* we adjust due to rounding errors in horner's evaluation */
fmpz_set_d_2exp(res, lo_eval*(f->length - 1)*(1.0 + f->length*ldexp(2.0, -51)), lo_exp);
goto cleanup;
}
}
if (rpow > 1000.0)
{
rpow -= 1000.0;
rexp += 1000;
} else if (rpow < -1000.0)
{
rpow += 1000.0;
rexp -= 1000;
}
}
cleanup:
fmpz_poly_clear(lo);
fmpz_poly_clear(hi);
}