shogo-library

競技プログラミング用の C++ ライブラリ

View the Project on GitHub shogo314/shogo-library

:heavy_check_mark: inf を持つ整数型
(src/math/infint.hpp)

正の無限大・負の無限大・NaN を持つ符号付き整数型 infint<T = long long> です。オーバーフローする演算は無限大に飽和します。最短路の距離など「到達不能」を表したい値に使えます。

using I = infint<>;
I a = I::Inf(), b = 5;
I c = a + b;               // inf
I d = I::Max() + b;        // inf (オーバーフローは飽和する)
I e = a - a;               // nan
std::cout << c << ' ' << -c << ' ' << e << '\n';  // "inf -inf nan"

内部では T の最大値を正の無限大、最小値 + 1 を負の無限大、最小値を NaN として表します。

値の作り方

書き方 値
infint<T>(x) 整数 x
infint<T>("inf")、"plusinf" / "minusinf" / "nan" 正の無限大 / 負の無限大 / NaN (大文字小文字は問わない)。それ以外の文字列は整数として読む
Inf()、PlusInf() / MinusInf() / NaN() 正の無限大 / 負の無限大 / NaN
Max() / Min() 無限大でない最大値 / 最小値

演算

演算 動作
+、-、* (と複合代入) 有限の値どうしでオーバーフローすれば ±無限大。$\infty - \infty$、$0 \times \infty$ は NaN
/、% 0 で割ると NaN。無限大どうしは NaN。無限大 ÷ 有限は ±無限大、無限大 % 有限は NaN。有限 ÷ 無限大は 0、有限 % 無限大は元の値
単項 - 符号反転 (無限大は反対の無限大に)
<、<=、>、>= NaN を含むと常に false
==、!= 内部表現の比較 (NaN どうしは等しい)
NaN を含む演算 結果は NaN

メンバ関数

関数 説明
val() 内部表現の値
isfinite() 有限か
isPlusInf()、isInf() / isMinusInf() / isNaN() 正の無限大か (isInf も正の無限大のみ) / 負の無限大か / NaN か
to_string()、os << x 文字列にする。無限大などの表記は set_str_plusinf(s)、set_str_minusinf(s)、set_str_nan(s) で変えられる (初期値 inf、-inf、nan)
repr() infint<>{"plusinf"} のような、コンストラクタにそのまま使える文字列

Verified with

Code

#pragma once

/**
 * @file infint.hpp
 * @brief inf を持つ整数型
 */

#include <algorithm>
#include <iostream>
#include <limits>
#include <ostream>
#include <string>
#include <string_view>
#include <type_traits>

/**
 * infをもつ整数型
 * Tは符号付き整数であり、2の補数表現で表されることを期待する
 */
template <typename T = long long>
struct infint {
   private:
    static_assert(std::is_integral_v<T> and std::is_signed_v<T>);
    static_assert(std::numeric_limits<T>::min() + 1 == -std::numeric_limits<T>::max());

    T _v;

    inline constexpr static T _PlusInf = std::numeric_limits<T>::max();       //!< 正の無限大
    inline constexpr static T _MinusInf = std::numeric_limits<T>::min() + 1;  //!< 負の無限大
    inline constexpr static T _NaN = std::numeric_limits<T>::min();           //!< Not a Number
    static std::string str_plusinf;
    static std::string str_minusinf;
    static std::string str_nan;

   public:
    constexpr infint() noexcept : _v(0) {}
    constexpr infint(const T& t) noexcept : _v(t) {}
    constexpr infint(const infint& o) noexcept : _v(o._v) {}
    infint(std::string_view sv) noexcept(false) {
        std::string s(sv);
        std::for_each(s.begin(), s.end(), [](char& c) {
            if ('A' <= c and c <= 'Z') {
                c = c - 'A' + 'a';
            }
        });
        if (s == "plusinf") {
            _v = _PlusInf;
        } else if (s == "inf") {
            _v = _PlusInf;
        } else if (s == "minusinf") {
            _v = _MinusInf;
        } else if (s == "nan") {
            _v = _NaN;
        } else {
            _v = std::stoll(s);
        }
    }

    /**
     * 内部表現をそのまま返す
     */
    constexpr T val() const noexcept { return _v; }

    constexpr bool isNaN() const noexcept { return _v == _NaN; }
    constexpr bool isPlusInf() const noexcept { return _v == _PlusInf; }
    constexpr bool isMinusInf() const noexcept { return _v == _MinusInf; }
    constexpr bool isInf() const noexcept { return _v == _PlusInf; }
    constexpr bool isfinite() const noexcept { return _MinusInf < _v and _v < _PlusInf; }

    constexpr infint& operator=(const infint& o) noexcept {
        _v = o._v;
        return *this;
    }
    constexpr infint& operator=(const T& t) noexcept {
        _v = t;
        return *this;
    }

    constexpr infint operator+() const noexcept { return _v; }
    constexpr infint operator-() const noexcept {
        if (_v == _NaN) return _v;
        return -_v;
    }
    constexpr infint& operator*=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() or not o.isfinite()) {
            if (_v == 0 or o._v == 0) {
                _v = _NaN;
            } else if ((_v > 0) ^ (o._v > 0)) {
                _v = _MinusInf;
            } else {
                _v = _PlusInf;
            }
            return *this;
        }
        if (_v == 0 or o._v == 0) {
            _v = 0;
            return *this;
        }
        // 有限の値の最大は _PlusInf - 1
        if (std::abs(_v) > (_PlusInf - 1) / std::abs(o._v)) {
            if ((_v > 0) ^ (o._v > 0)) {
                _v = _MinusInf;
            } else {
                _v = _PlusInf;
            }
            return *this;
        }
        _v *= o._v;
        return *this;
    }
    constexpr infint& operator/=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() and not o.isfinite()) {
            _v = _NaN;
            return *this;
        }
        if (o._v == 0) {
            _v = _NaN;
            return *this;
        }
        if (not o.isfinite()) {
            _v = 0;
            return *this;
        }
        if (not isfinite()) {
            // ±inf / 有限 = ±inf (符号は商の符号)
            if ((_v > 0) ^ (o._v > 0)) {
                _v = _MinusInf;
            } else {
                _v = _PlusInf;
            }
            return *this;
        }
        _v /= o._v;
        return *this;
    }
    constexpr infint& operator%=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() and not o.isfinite()) {
            _v = _NaN;
            return *this;
        }
        if (o._v == 0) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite()) {
            // ±inf % 有限 は定まらない (浮動小数点数の fmod と同じく NaN)
            _v = _NaN;
            return *this;
        }
        if (not o.isfinite()) {
            return *this;
        }
        _v %= o._v;
        return *this;
    }
    constexpr infint& operator+=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() or not o.isfinite()) {
            if (not isfinite() and not o.isfinite()) {
                if (isPlusInf() ^ o.isPlusInf()) {
                    _v = _NaN;
                }
            } else if (not o.isfinite()) {
                _v = o._v;
            }
            return *this;
        }
        if (o._v > 0) {
            if (_v >= _PlusInf - o._v) {
                _v = _PlusInf;
                return *this;
            }
        } else if (o._v < 0) {
            if (_v <= _MinusInf - o._v) {
                _v = _MinusInf;
                return *this;
            }
        }
        _v += o._v;
        return *this;
    }
    constexpr infint& operator-=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() or not o.isfinite()) {
            if (not isfinite() and not o.isfinite()) {
                if (isPlusInf() == o.isPlusInf()) {
                    _v = _NaN;
                }
            } else if (not o.isfinite()) {
                _v = -o._v;
            }
            return *this;
        }
        if (o._v > 0) {
            if (_v <= _MinusInf + o._v) {
                _v = _MinusInf;
                return *this;
            }
        } else if (o._v < 0) {
            if (_v >= _PlusInf + o._v) {
                _v = _PlusInf;
                return *this;
            }
        }
        _v -= o._v;
        return *this;
    }
    static std::string type_str() { return "infint<>"; }
    std::string initializer_str() const {
        if (_v == _PlusInf) {
            return "{\"plusinf\"}";
        } else if (_v == _MinusInf) {
            return "{\"minusinf\"}";
        } else if (_v == _NaN) {
            return "{\"nan\"}";
        } else {
            return "{" + std::to_string(_v) + "}";
        }
    }
    std::string repr() const {
        return this->type_str() + this->initializer_str();
    }
    std::string to_string() const {
        if (_v == _PlusInf) {
            return str_plusinf;
        } else if (_v == _MinusInf) {
            return str_minusinf;
        } else if (_v == _NaN) {
            return str_nan;
        } else {
            return std::to_string(_v);
        }
    }
    template <class CharT, class Traits>
    friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const infint& rhs) {
        os << rhs.to_string();
        return os;
    }
    static void set_str_plusinf(std::string s) {
        str_plusinf = s;
    }
    static void set_str_minusinf(std::string s) {
        str_minusinf = s;
    }
    static void set_str_nan(std::string s) {
        str_nan = s;
    }

    /**
     * 正の無限大
     */
    constexpr static infint PlusInf() noexcept {
        return _PlusInf;
    }
    /**
     * 負の無限大
     */
    constexpr static infint MinusInf() noexcept {
        return _MinusInf;
    }
    /**
     * Not a Number
     */
    constexpr static infint NaN() noexcept {
        return _NaN;
    }
    /**
     * 正の無限大(PlusInfと同じ)
     */
    constexpr static infint Inf() noexcept {
        return _PlusInf;
    }
    /**
     * 正の無限大でない最大値
     */
    constexpr static infint Max() noexcept {
        return _PlusInf - 1;
    }
    /**
     * 負の無限大でない最小値
     */
    constexpr static infint Min() noexcept {
        return _MinusInf + 1;
    }

    friend constexpr infint operator*(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res *= t2;
    }
    friend constexpr infint operator/(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res /= t2;
    }
    friend constexpr infint operator%(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res %= t2;
    }
    friend constexpr infint operator+(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res += t2;
    }
    friend constexpr infint operator-(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res -= t2;
    }
    friend constexpr bool operator<(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v < t2._v;
    }
    friend constexpr bool operator>(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v > t2._v;
    }
    friend constexpr bool operator<=(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v <= t2._v;
    }
    friend constexpr bool operator>=(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v >= t2._v;
    }
    friend constexpr bool operator==(const infint& t1, const infint& t2) noexcept {
        return t1._v == t2._v;
    }
    friend constexpr bool operator!=(const infint& t1, const infint& t2) noexcept {
        return t1._v != t2._v;
    }
};
template <typename T>
std::string infint<T>::str_plusinf = "inf";
template <typename T>
std::string infint<T>::str_minusinf = "-inf";
template <typename T>
std::string infint<T>::str_nan = "nan";
#line 2 "src/math/infint.hpp"

/**
 * @file infint.hpp
 * @brief inf を持つ整数型
 */

#include <algorithm>
#include <iostream>
#include <limits>
#include <ostream>
#include <string>
#include <string_view>
#include <type_traits>

/**
 * infをもつ整数型
 * Tは符号付き整数であり、2の補数表現で表されることを期待する
 */
template <typename T = long long>
struct infint {
   private:
    static_assert(std::is_integral_v<T> and std::is_signed_v<T>);
    static_assert(std::numeric_limits<T>::min() + 1 == -std::numeric_limits<T>::max());

    T _v;

    inline constexpr static T _PlusInf = std::numeric_limits<T>::max();       //!< 正の無限大
    inline constexpr static T _MinusInf = std::numeric_limits<T>::min() + 1;  //!< 負の無限大
    inline constexpr static T _NaN = std::numeric_limits<T>::min();           //!< Not a Number
    static std::string str_plusinf;
    static std::string str_minusinf;
    static std::string str_nan;

   public:
    constexpr infint() noexcept : _v(0) {}
    constexpr infint(const T& t) noexcept : _v(t) {}
    constexpr infint(const infint& o) noexcept : _v(o._v) {}
    infint(std::string_view sv) noexcept(false) {
        std::string s(sv);
        std::for_each(s.begin(), s.end(), [](char& c) {
            if ('A' <= c and c <= 'Z') {
                c = c - 'A' + 'a';
            }
        });
        if (s == "plusinf") {
            _v = _PlusInf;
        } else if (s == "inf") {
            _v = _PlusInf;
        } else if (s == "minusinf") {
            _v = _MinusInf;
        } else if (s == "nan") {
            _v = _NaN;
        } else {
            _v = std::stoll(s);
        }
    }

    /**
     * 内部表現をそのまま返す
     */
    constexpr T val() const noexcept { return _v; }

    constexpr bool isNaN() const noexcept { return _v == _NaN; }
    constexpr bool isPlusInf() const noexcept { return _v == _PlusInf; }
    constexpr bool isMinusInf() const noexcept { return _v == _MinusInf; }
    constexpr bool isInf() const noexcept { return _v == _PlusInf; }
    constexpr bool isfinite() const noexcept { return _MinusInf < _v and _v < _PlusInf; }

    constexpr infint& operator=(const infint& o) noexcept {
        _v = o._v;
        return *this;
    }
    constexpr infint& operator=(const T& t) noexcept {
        _v = t;
        return *this;
    }

    constexpr infint operator+() const noexcept { return _v; }
    constexpr infint operator-() const noexcept {
        if (_v == _NaN) return _v;
        return -_v;
    }
    constexpr infint& operator*=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() or not o.isfinite()) {
            if (_v == 0 or o._v == 0) {
                _v = _NaN;
            } else if ((_v > 0) ^ (o._v > 0)) {
                _v = _MinusInf;
            } else {
                _v = _PlusInf;
            }
            return *this;
        }
        if (_v == 0 or o._v == 0) {
            _v = 0;
            return *this;
        }
        // 有限の値の最大は _PlusInf - 1
        if (std::abs(_v) > (_PlusInf - 1) / std::abs(o._v)) {
            if ((_v > 0) ^ (o._v > 0)) {
                _v = _MinusInf;
            } else {
                _v = _PlusInf;
            }
            return *this;
        }
        _v *= o._v;
        return *this;
    }
    constexpr infint& operator/=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() and not o.isfinite()) {
            _v = _NaN;
            return *this;
        }
        if (o._v == 0) {
            _v = _NaN;
            return *this;
        }
        if (not o.isfinite()) {
            _v = 0;
            return *this;
        }
        if (not isfinite()) {
            // ±inf / 有限 = ±inf (符号は商の符号)
            if ((_v > 0) ^ (o._v > 0)) {
                _v = _MinusInf;
            } else {
                _v = _PlusInf;
            }
            return *this;
        }
        _v /= o._v;
        return *this;
    }
    constexpr infint& operator%=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() and not o.isfinite()) {
            _v = _NaN;
            return *this;
        }
        if (o._v == 0) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite()) {
            // ±inf % 有限 は定まらない (浮動小数点数の fmod と同じく NaN)
            _v = _NaN;
            return *this;
        }
        if (not o.isfinite()) {
            return *this;
        }
        _v %= o._v;
        return *this;
    }
    constexpr infint& operator+=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() or not o.isfinite()) {
            if (not isfinite() and not o.isfinite()) {
                if (isPlusInf() ^ o.isPlusInf()) {
                    _v = _NaN;
                }
            } else if (not o.isfinite()) {
                _v = o._v;
            }
            return *this;
        }
        if (o._v > 0) {
            if (_v >= _PlusInf - o._v) {
                _v = _PlusInf;
                return *this;
            }
        } else if (o._v < 0) {
            if (_v <= _MinusInf - o._v) {
                _v = _MinusInf;
                return *this;
            }
        }
        _v += o._v;
        return *this;
    }
    constexpr infint& operator-=(const infint& o) noexcept {
        if (isNaN() or o.isNaN()) {
            _v = _NaN;
            return *this;
        }
        if (not isfinite() or not o.isfinite()) {
            if (not isfinite() and not o.isfinite()) {
                if (isPlusInf() == o.isPlusInf()) {
                    _v = _NaN;
                }
            } else if (not o.isfinite()) {
                _v = -o._v;
            }
            return *this;
        }
        if (o._v > 0) {
            if (_v <= _MinusInf + o._v) {
                _v = _MinusInf;
                return *this;
            }
        } else if (o._v < 0) {
            if (_v >= _PlusInf + o._v) {
                _v = _PlusInf;
                return *this;
            }
        }
        _v -= o._v;
        return *this;
    }
    static std::string type_str() { return "infint<>"; }
    std::string initializer_str() const {
        if (_v == _PlusInf) {
            return "{\"plusinf\"}";
        } else if (_v == _MinusInf) {
            return "{\"minusinf\"}";
        } else if (_v == _NaN) {
            return "{\"nan\"}";
        } else {
            return "{" + std::to_string(_v) + "}";
        }
    }
    std::string repr() const {
        return this->type_str() + this->initializer_str();
    }
    std::string to_string() const {
        if (_v == _PlusInf) {
            return str_plusinf;
        } else if (_v == _MinusInf) {
            return str_minusinf;
        } else if (_v == _NaN) {
            return str_nan;
        } else {
            return std::to_string(_v);
        }
    }
    template <class CharT, class Traits>
    friend std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, const infint& rhs) {
        os << rhs.to_string();
        return os;
    }
    static void set_str_plusinf(std::string s) {
        str_plusinf = s;
    }
    static void set_str_minusinf(std::string s) {
        str_minusinf = s;
    }
    static void set_str_nan(std::string s) {
        str_nan = s;
    }

    /**
     * 正の無限大
     */
    constexpr static infint PlusInf() noexcept {
        return _PlusInf;
    }
    /**
     * 負の無限大
     */
    constexpr static infint MinusInf() noexcept {
        return _MinusInf;
    }
    /**
     * Not a Number
     */
    constexpr static infint NaN() noexcept {
        return _NaN;
    }
    /**
     * 正の無限大(PlusInfと同じ)
     */
    constexpr static infint Inf() noexcept {
        return _PlusInf;
    }
    /**
     * 正の無限大でない最大値
     */
    constexpr static infint Max() noexcept {
        return _PlusInf - 1;
    }
    /**
     * 負の無限大でない最小値
     */
    constexpr static infint Min() noexcept {
        return _MinusInf + 1;
    }

    friend constexpr infint operator*(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res *= t2;
    }
    friend constexpr infint operator/(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res /= t2;
    }
    friend constexpr infint operator%(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res %= t2;
    }
    friend constexpr infint operator+(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res += t2;
    }
    friend constexpr infint operator-(const infint& t1, const infint& t2) noexcept {
        infint res(t1);
        return res -= t2;
    }
    friend constexpr bool operator<(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v < t2._v;
    }
    friend constexpr bool operator>(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v > t2._v;
    }
    friend constexpr bool operator<=(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v <= t2._v;
    }
    friend constexpr bool operator>=(const infint& t1, const infint& t2) noexcept {
        if (t1.isNaN() or t2.isNaN()) return false;
        return t1._v >= t2._v;
    }
    friend constexpr bool operator==(const infint& t1, const infint& t2) noexcept {
        return t1._v == t2._v;
    }
    friend constexpr bool operator!=(const infint& t1, const infint& t2) noexcept {
        return t1._v != t2._v;
    }
};
template <typename T>
std::string infint<T>::str_plusinf = "inf";
template <typename T>
std::string infint<T>::str_minusinf = "-inf";
template <typename T>
std::string infint<T>::str_nan = "nan";
Back to top page