/*
 * holefiller.cpp: implementation of methods from holefiller.h.
 */

#include "spigot.h"
#include "funcs.h"
#include "cr.h"
#include "holefiller.h"

MAKE_CLASS_GREETER(HoleFiller);

HoleFiller::HoleFiller(Spigot ax)
    : x(move(ax))
{
    dgreet();
}

Spigot HoleFiller::replace()
{
    Spigot a, diff;
    bigint n, d;
    for (int i = 0; (a = xspecial(i)) != nullptr; i++) {
        diff = spigot_sub(x->clone(), move(a));
        if (diff->is_rational(&n, &d) && n == 0) {
            return yspecial(i);
        }
    }
    return nullptr;
}

bool HoleFiller::combine(bigint *ret_lo, bigint *ret_hi, unsigned *ret_bits,
                         const bigint &xnlo, const bigint &xnhi,
                         const bigint &ynlo, const bigint &ynhi,
                         unsigned dbits, int /*index*/)
{
    bigint deviation = -xnlo;
    if (deviation < xnhi)
        deviation = xnhi;
    if (dbits < 1 || (deviation >> (dbits-1)) != 0)
        return false;
    *ret_bits = dbits;
    *ret_lo = ynlo - deviation;
    *ret_hi = ynhi + deviation;
    return true;
}

void HoleFiller::gen_bin_interval(bigint *ret_lo, bigint *ret_hi,
                                  unsigned *ret_bits)
{
    crBegin;

    /*
     * Set up a BracketingGenerator for x-a, for each special input a.
     */
    {
        Spigot a;
        for (int i = 0; (a = xspecial(i)) != nullptr; i++)
            bgs.push_back(make_unique<BracketingGenerator>
                          (spigot_sub(x->clone(), move(a))));
    }

    /*
     * Loop round and round, fetching more information about each of
     * those, until we narrow to 0 or 1 of them potentially zero.
     *
     * This is a setup phase in which we don't return to the caller at
     * all, because if the input interval doesn't even narrow to less
     * than the distance between our special values (if we have more
     * than one of them), then no useful output was going to be
     * generated anyway.
     */
    while (1) {
        int n = 0;                 // count the still-active bgs
        bigint nlo, nhi;
        unsigned dbits;

        for (int i = 0; i < (int)bgs.size(); i++) {
            bgs[i]->get_bracket_shift(&nlo, &nhi, &dbits);
            dprint("input bracket for specials[", i, "]: (",
                   nlo, ",", nhi, ") / 2^", dbits);

            if (nlo > 0 || nhi < 0) {
                /*
                 * We know the sign of x-a, i.e. we know it's nonzero.
                 * Discard this special value.
                 */
                bgs[i] = nullptr;
            } else if (nlo == 0 && nhi == 0) {
                /*
                 * We know x-a is _exactly_ zero, i.e. this is
                 * precisely a special value of the function.
                 */
                ybg = make_unique<BracketingGenerator>(yspecial(i));
                goto passthrough;  // multilevel break (sorry)
            } else {
                /*
                 * This one is still a possible.
                 */
                n++;
            }
        }

        if (n == 0) {
            /*
             * No remaining possibilities for special values, so we
             * just switch to constructing the general return value.
             */
            ybg = make_unique<BracketingGenerator>(ygeneral(x->clone()));
            goto passthrough;
        }

        if (n == 1) {
            /*
             * There's one remaining possibility for a special value.
             * This is the interesting case.
             */
            for (int i = 0; i < (int)bgs.size(); i++) {
                if (bgs[i]) {
                    specindex = i;
                    xbg = move(bgs[i]);
                    bgs[i] = nullptr;
                    ybg = make_unique<BracketingGenerator>(yspecial(i));
                    goto narrowing;
                }
            }
        }
    }

  narrowing:
    /*
     * Now we've got a single potential special value. Narrow an
     * interval about the SV output for as long as we can't prove the
     * input is not the SV input.
     */
    while (true) {
        {
            bigint xnlo, xnhi, ynlo, ynhi;
            unsigned xdbits, ydbits;

            xbg->get_bracket_shift(&xnlo, &xnhi, &xdbits);
            dprint("narrowing input bracket: (",
                   xnlo, ",", xnhi, ") / 2^", xdbits);

            if (xnlo > 0 || xnhi < 0) {
                /*
                 * Turns out we don't have the special value after
                 * all; reinitialise ybg with the general value, and
                 * go on to the passthrough phase.
                 */
                xbg = nullptr;
                ybg = make_unique<BracketingGenerator>(
                    ygeneral(x->clone()));
                goto passthrough;
            } else if (xnlo == 0 && xnhi == 0) {
                /*
                 * Turns out we have _exactly_ the special value.
                 */
                xbg = nullptr;
                goto passthrough;
            }

            ybg->get_bracket_shift(&ynlo, &ynhi, &ydbits);
            dprint("narrowing output bracket: (",
                   ynlo, ",", ynhi, ") / 2^", ydbits);

            /*
             * Normalise the input and output brackets to the same
             * denominator, which we leave in ydbits.
             */
            ydbits = match_dyadic_rationals(xnlo, xdbits, xnhi, xdbits,
                                            ynlo, ydbits, ynhi, ydbits);
            dprint("narrowing normalised brackets: input (",
                   // (intentionally ydbits in the x bracket too)
                   xnlo, ",", xnhi, ") / 2^", ydbits,
                   ynlo, ",", ynhi, ") / 2^", ydbits);

            /*
             * And let our subclass figure out how closely it can
             * afford to narrow the resulting interval.
             */
            combine(ret_lo, ret_hi, ret_bits,
                    xnlo, xnhi, ynlo, ynhi, ydbits, specindex);
        }

        dprint("combined output bracket: (",
               *ret_lo, ",", *ret_hi, ") / 2^", *ret_bits);
        crReturnV;
    }

  passthrough:
    /*
     * The simple part: now we know what kind of output we're
     * generating, just pass through the results of an ordinary
     * BracketingGenerator.
     */

    // FIXME: here we'd really like to pass through an exact
    // termination, if we're a rational!

    while (1) {
        ybg->get_bracket_shift(ret_lo, ret_hi, ret_bits);
        dprint("passthrough bracket: (",
               *ret_lo, ",", *ret_hi, ") / 2^", *ret_bits);
        crReturnV;
    }

    crEnd;
}
