/*
    Copyright (C) 2012 Fredrik Johansson

    This file is part of Arb.

    Arb 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 <http://www.gnu.org/licenses/>.
*/

#include "arb.h"

void
arb_get_interval_fmpz_2exp(fmpz_t a, fmpz_t b, fmpz_t exp, const arb_t x)
{
    if (!arb_is_finite(x))
    {
        printf("arb_get_interval_fmpz_2exp: expected finite input\n");
        flint_abort();
    }
    else if (arb_is_exact(x))
    {
        arf_get_fmpz_2exp(a, exp, arb_midref(x));
        fmpz_set(b, a);
    }
    else if (arf_is_zero(arb_midref(x)))
    {
        arf_t t;
        arf_init_set_mag_shallow(t, arb_radref(x));
        arf_get_fmpz_2exp(b, exp, t);
        fmpz_neg(a, b);
    }
    else
    {
        arf_t rad;
        fmpz_t tmp;
        slong shift;
        flint_bitcnt_t aval, bval;

        fmpz_init(tmp);

        arf_get_fmpz_2exp(a, exp, arb_midref(x));
        arf_init_set_mag_shallow(rad, arb_radref(x));
        arf_get_fmpz_2exp(b, tmp, rad);

        shift = _fmpz_sub_small(exp, tmp);

        if (FLINT_ABS(shift) >= WORD_MAX / 2)
        {
            printf("arb_get_interval_fmpz_2exp: too large shift\n");
            flint_abort();
        }

        if (shift >= 0)
        {
            fmpz_mul_2exp(a, a, shift);
            fmpz_set(exp, tmp);
        }
        else
            fmpz_mul_2exp(b, b, -shift);

        fmpz_sub(tmp, a, b);
        fmpz_add(b, a, b);
        fmpz_swap(tmp, a);

        if (fmpz_is_zero(a))
        {
            aval = fmpz_val2(b);
        }
        else if (fmpz_is_zero(b))
        {
            aval = fmpz_val2(a);
        }
        else
        {
            aval = fmpz_val2(a);
            bval = fmpz_val2(b);
            aval = FLINT_MIN(aval, bval);
        }

        if (aval > 0)
        {
            fmpz_add_ui(exp, exp, aval);
            fmpz_tdiv_q_2exp(a, a, aval);
            fmpz_tdiv_q_2exp(b, b, aval);
        }

        fmpz_clear(tmp);
    }
}

