/* -*- c++ -*-
 * holefiller.h: abstract wrapper class that fills in removable
 * singularities in functions.
 */

#include <vector>
using std::vector;

/*
 * This class is a wrapper that sits in front of another spigot,
 * somewhat like enforce.cpp's Enforcer. You provide it with a value
 * x, which might be one of a finite set of special x values; the
 * basic semantics are that if x does turn out to be one of those
 * values then there's a corresponding special y value for each one
 * that should be returned, and if x turns out to be anything else
 * then there's a 'general y' that should be returned instead.
 *
 * You provide the special x and y values and the general y value by
 * means of subclassing HoleFiller itself; they're all given by
 * virtual methods.
 *
 * The point of this is for situations in which you have a means of
 * implementing _most_ of a continuous function f, apart from a small
 * number of 'removable singularities' - points where the function
 * really is continuous, but for some annoying reason, you have to use
 * a different evaluation strategy to compute its value there. But you
 * can't just write the obvious code (if x == this then return that,
 * else do the general thing), because that causes an exactness hazard
 * in the case where x _turns out_ to be the special value but that
 * wasn't obvious up front.
 *
 * Enter HoleFiller. HoleFiller will keep track of which special
 * value(s) the input x has not yet been proved to miss, and it will
 * return intervals narrowing about the corresponding special y value
 * for as long as there's still one of those in play. But if it then
 * turns out that x is not a special value after all, it'll switch to
 * returning intervals about whatever the general fallback evaluator
 * returns.
 *
 * In order to do this correctly, it must not return an interval in
 * the first phase which rules out any value that f(x) might still
 * turn out to have. So another thing your subclass must provide is a
 * function which takes as input the currently known interval about
 * the special value to which we have restricted x, and returns an
 * interval about the corresponding special y which also contains any
 * other value that f(x) might take on the input interval. That way,
 * if we later abandon the special value, we won't have returned any
 * interval that excludes the real answer to f(x).
 */
class HoleFiller : public BinaryIntervalSource {
    vector<unique_ptr<BracketingGenerator>> bgs;
    unique_ptr<BracketingGenerator> xbg, ybg;
    int specindex;
    int crState { -1 };

    virtual void gen_bin_interval(bigint *ret_lo, bigint *ret_hi,
                                  unsigned *ret_bits) override;

  protected:
    Spigot x;

    virtual Spigot xspecial(int) = 0;
    virtual Spigot yspecial(int) = 0;
    virtual Spigot ygeneral(Spigot) = 0;

    /*
     * This function may be reimplemented by a subclass. Its job is:
     * given an interval (xnlo,xnhi)/dbits saying how close x is to
     * the (index)th special input, and an interval (ynlo,ynhi)/dbits
     * bracketing the corresponding special output, return an interval
     * that we can be sure the _real_ output is in.
     *
     * We provide a default implementation here based on the
     * assumption that the function is Lipschitz-continuous within a
     * range of +-1/2 about the target, because that's really easy and
     * quite a weak constraint. If you felt like squeezing more
     * performance out then you could reimplement this to be more
     * aggressive; if you find yourself implementing a function which
     * is really steep at the special point (perhaps even infinitely
     * steep, e.g. if it approaches the point in a sqrt-like way) then
     * you _must_ reimplement this to be more conservative.
     *
     * Return false if we can't get any information at all yet.
     */
    virtual bool 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*/);

  public:
    HoleFiller(Spigot ax);

    /*
     * See if we can _immediately_ detect exact equality to a
     * special-case input. If so, return that y value.
     */
    Spigot replace();
};
