#include <cmath>
#include <limits>
#include <functional>

struct DoubleInterval 
{
    long double from = 0.l;
    long double to = 0.l;
    double original = 0.;

    DoubleInterval() = default;
    constexpr DoubleInterval(long double a, long double b, double x) {
        from = a;
        to = b;
        original = x;
    }
    DoubleInterval(double x) {
        from = x;
        to = std::nextafter<double>(x, std::numeric_limits<double>::max());
        original = x;
    }
    
    //
    //     see interval addition
    //
    DoubleInterval& operator+=(const DoubleInterval& rhs) {
        from = static_cast<double>(this->from) + static_cast<double>(rhs.from);
        to = static_cast<double>(this->to) + static_cast<double>(rhs.to);
        original = original + rhs.original;
        return *this;
    }
    friend DoubleInterval operator+(DoubleInterval lhs, const DoubleInterval& rhs) {
        lhs += rhs;
        return lhs;
    }

    DoubleInterval& operator-=(const DoubleInterval& rhs) {
        from = static_cast<double>(this->from) - static_cast<double>(rhs.to);
        to = static_cast<double>(this->to) - static_cast<double>(rhs.from);
        original = original - rhs.original;
        return *this;
    }
    friend DoubleInterval operator-(DoubleInterval lhs, const DoubleInterval& rhs) {
        lhs -= rhs;
        return lhs;
    }

    //
    //    see interval multiplication
    //
    DoubleInterval& operator*=(const DoubleInterval& rhs) {
        long double aa = static_cast<double>(this->from) * static_cast<double>(rhs.from);
        long double ab = static_cast<double>(this->from) * static_cast<double>(rhs.to);
        long double ba = static_cast<double>(this->to) * static_cast<double>(rhs.from);
        long double bb = static_cast<double>(this->to) * static_cast<double>(rhs.to);
        from = std::min(aa, std::min(ab, std::min(ba, bb)));
        to = std::max(aa, std::max(ab, std::max(ba, bb)));
        original = original * rhs.original;
        return *this;
    }
    friend DoubleInterval operator*(DoubleInterval lhs, const DoubleInterval& rhs) {
        lhs *= rhs;
        return lhs;
    }

    DoubleInterval& operator/=(const DoubleInterval& rhs) {
        long double aa = static_cast<double>(this->from) / static_cast<double>(rhs.from);
        long double ab = static_cast<double>(this->from) / static_cast<double>(rhs.to);
        long double ba = static_cast<double>(this->to) / static_cast<double>(rhs.from);
        long double bb = static_cast<double>(this->to) / static_cast<double>(rhs.to);
        from = std::min(aa, std::min(ab, std::min(ba, bb)));
        to = std::max(aa, std::max(ab, std::max(ba, bb)));
        original = original / rhs.original;
        return *this;
    }
    friend DoubleInterval operator/(DoubleInterval lhs, const DoubleInterval& rhs) {
        lhs /= rhs;
        return lhs;
    }


    DoubleInterval operator+=(const double rhs) {
        return *this + DoubleInterval(rhs);
    }
    DoubleInterval operator-=(const double rhs) {
        return *this - DoubleInterval(rhs);
    }
    DoubleInterval operator*=(const double rhs) {
        return *this * DoubleInterval(rhs);
    }
    DoubleInterval operator/=(const double rhs) {
        return *this / DoubleInterval(rhs);
    }  
    
    operator double() {
        return original;
    }
};

DoubleInterval operator+(DoubleInterval lhs, const double& rhs) {
    return lhs + DoubleInterval(rhs);
}
DoubleInterval operator-(DoubleInterval lhs, const double& rhs) {
    return lhs - DoubleInterval(rhs);
}
DoubleInterval operator*(DoubleInterval lhs, const double& rhs) {
    return lhs * DoubleInterval(rhs);
}
DoubleInterval operator/(DoubleInterval lhs, const double& rhs) {
    return lhs / DoubleInterval(rhs);
}

DoubleInterval operator+(double lhs, const DoubleInterval& rhs) {
    return DoubleInterval(lhs) + rhs;
}
DoubleInterval operator-(double lhs, const DoubleInterval& rhs) {
    return DoubleInterval(lhs) - rhs;
}
DoubleInterval operator*(double lhs, const DoubleInterval& rhs) {
    return DoubleInterval(lhs) * rhs;
}
DoubleInterval operator/(double lhs, const DoubleInterval& rhs) {
    return DoubleInterval(lhs) / rhs;
}

//
// see interval comparison
//
bool operator<(const DoubleInterval& a, const DoubleInterval& b) {
    return a.original < b.original;
}
bool operator<(const DoubleInterval& a, const double b) {
    return a.original < b;
}  
bool operator<(const double a, const DoubleInterval& b) {
    return a < b.original;
}

namespace {
    // this selects a minimum and a maximum on an interval divided into a number of segments
    static const unsigned int g_num_of_segments = 10; // 10 is arbitrary

    DoubleInterval run_std(DoubleInterval x, std::function<double(double)> fun) {
        long double a_min = fun(static_cast<double>(x.from));
        long double a_max = a_min;
        for (auto i = 0u; i < g_num_of_segments; ++i) {
           auto xi = x.from + (i + 1) * (x.to - x.from) / g_num_of_segments;
           long double ai = fun(static_cast<double>(xi));

           if (std::isnan(a_min))
               a_min = ai;
           else
               if (! std::isnan(ai))
                   a_min = std::min(a_min, ai);

           if (std::isnan(a_max))
               a_max = ai;
           else
               if (! std::isnan(ai))
                   a_max = std::max(a_max, ai);
        }
        double original = fun(x);
        return DoubleInterval(a_min, a_max, original);
    }
}

#define std_monadic_function(name) \
    DoubleInterval name (DoubleInterval x) { \
        return run_std(x, [](double x) -> double {return std:: name(x);}); \
    }

namespace std {
    std_monadic_function(sqrt);
    std_monadic_function(sin);
    std_monadic_function(cos);
    std_monadic_function(tan);
    std_monadic_function(ctg);
    std_monadic_function(asin);
    std_monadic_function(acos);
    std_monadic_function(atan);
    std_monadic_function(actg);
    std_monadic_function(abs);

    template <>
    constexpr DoubleInterval numeric_limits<DoubleInterval>::quiet_NaN() noexcept
    {
        return DoubleInterval(
            std::numeric_limits<long double>::quiet_NaN(),
            std::numeric_limits<long double>::quiet_NaN(),
            std::numeric_limits<double>::quiet_NaN());
    }
}