// // libsemigroups - C++ library for semigroups and monoids // Copyright (C) 2019 James D. Mitchell // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // // This file contains functionality for various constant values used in // libsemigroups. // TODO(later) // 1. NegativeInfinity could be comparable with unsigned integers (always <). // 2. specialisation of operator<< for ostringstream for better printing. I // couldn't immediately get this to work. #ifndef LIBSEMIGROUPS_INCLUDE_CONSTANTS_HPP_ #define LIBSEMIGROUPS_INCLUDE_CONSTANTS_HPP_ #include // for int64_t #include // for numeric_limits #include // for is_integral namespace libsemigroups { namespace detail { struct Min { template constexpr T operator()() const noexcept { static_assert(std::is_integral::value, "can only call Min with an integral type"); return std::numeric_limits::min(); } }; struct Max { template constexpr T operator()() const noexcept { static_assert(std::is_integral::value, "can only call Max with an integral type"); return std::numeric_limits::max(); } }; template struct Constant { static_assert(std::is_same::value || std::is_same::value, "template parameter TMaxOrMin must be Max or Min"); Constant() = default; Constant(Constant const&) = default; Constant(Constant&&) = default; Constant& operator=(Constant const&) = default; Constant& operator=(Constant&&) = default; ~Constant() = default; template constexpr operator T() const noexcept { static_assert( std::is_integral::value && (std::is_signed::value || std::is_same::value), "the template parameter T must be an integral type, and either " "unsigned or the template parameter TMaxOrMin must be Max."); return TMaxOrMin().template operator()() + TOffset; } }; } // namespace detail //////////////////////////////////////////////////////////////////////// // Constant values //////////////////////////////////////////////////////////////////////// //! Type for undefined values. using Undefined = detail::Constant<0, detail::Max>; //! Type for positive infinity. using PositiveInfinity = detail::Constant<-1, detail::Max>; //! Type for the maximum value of something. using LimitMax = detail::Constant<-2, detail::Max>; //! Type for negative infinity. using NegativeInfinity = detail::Constant<0, detail::Min>; //! This variable is used to indicate that a value is undefined. UNDEFINED is //! comparable with any integral value (signed or unsigned) or constant via //! `==` and `!=` but not via `<` or `>`. constexpr Undefined UNDEFINED{}; //! This variable represents \f$\infty\f$. POSITIVE_INFINITY is comparable //! via `==`, `!=`, `<`, `>` with any integral value (signed or unsigned) and //! with NEGATIVE_INFINITY, and is comparable to any other constant via `==` //! and `!=`, but not by `<` and `>`. constexpr PositiveInfinity POSITIVE_INFINITY{}; //! This variable represents the maximum value that certain function //! parameters can have. LIMIT_MAX is comparable via `==`, `!=`, `<`, `>` //! with any integral value (signed or unsigned), and is comparable to any //! other constant via `==` and `!=`, but not by `<` and `>`. constexpr LimitMax LIMIT_MAX{}; //! This variable represents \f$-\infty\f$. NEGATIVE_INFINITY is comparable //! via `==`, `!=`, `<`, `>` with any signed integral value and //! with POSITIVE_INFINITY, and is comparable to any other constant via `==` //! and `!=`. constexpr NegativeInfinity NEGATIVE_INFINITY{}; //////////////////////////////////////////////////////////////////////// // Operators for all constants //////////////////////////////////////////////////////////////////////// // Note that for some reason Catch requires that the comparison functions are // in the namespace detail. namespace detail { // operator== // No SFINAE required, since the functions delegated to don't exist. template constexpr bool operator==(Constant const& lhs, T const& rhs) noexcept { return lhs.operator T() == rhs; } template constexpr bool operator==(T const& lhs, Constant const& rhs) noexcept { return rhs.operator T() == lhs; } template constexpr bool operator==(detail::Constant const&, detail::Constant const&) noexcept { return std::is_same::value && R1 == R2; } // operator!= // No SFINAE required, since the functions delegated to don't exist. template constexpr bool operator!=(Constant const& lhs, T const& rhs) noexcept { return !(lhs == rhs); } template constexpr bool operator!=(T const& lhs, Constant const& rhs) noexcept { return !(lhs == rhs); } template constexpr bool operator!=(detail::Constant const& lhs, detail::Constant const& rhs) noexcept { return !(lhs == rhs); } // operator> // No SFINAE required, since the functions delegated to don't exist. template constexpr bool operator>(detail::Constant const& lhs, T const& rhs) noexcept { return rhs < lhs; } template constexpr bool operator>(T const& lhs, detail::Constant const& rhs) noexcept { return rhs < lhs; } template constexpr bool operator>(detail::Constant const&, detail::Constant const&) noexcept { return false; } template constexpr bool operator<(detail::Constant const&, detail::Constant const&) noexcept { return false; } // No further operator< for Constant and Constant unless given explicitly //////////////////////////////////////////////////////////////////////// // Operators for specific constants //////////////////////////////////////////////////////////////////////// // PositiveInfinity is not less than any integral value, or // NegativeInfinity. template constexpr auto operator<(PositiveInfinity const&, T const&) noexcept -> typename std::enable_if::value || std::is_same::value, SFINAE>::type { return false; } // Every integral value, and negative infinity, is less than // PositiveInfinity. template constexpr auto operator<(T const&, PositiveInfinity const&) noexcept -> typename std::enable_if::value || std::is_same::value, SFINAE>::type { return true; } // NegativeInfinity is less than every integral value. template constexpr auto operator<(NegativeInfinity const&, T const&) noexcept -> typename std::enable_if::value, SFINAE>::type { return true; } // No integral value is less than NegativeInfinity. template constexpr auto operator<(T const&, NegativeInfinity const&) noexcept -> typename std::enable_if::value, SFINAE>::type { return false; } // LimitMax is compared by implicit conversion with any integral value. template constexpr auto operator<(LimitMax const& lhs, T const& rhs) noexcept -> typename std::enable_if::value, SFINAE>::type { return lhs.operator T() < rhs; } // LimitMax is compared by implicit conversion with any integral value. template constexpr auto operator<(T const& lhs, LimitMax const& rhs) noexcept -> typename std::enable_if::value, SFINAE>::type { return lhs < rhs.operator T(); } template constexpr auto operator-(LimitMax const& lhs, T const& rhs) noexcept -> typename std::enable_if::value, SFINAE>::type { return lhs.operator T() - rhs; } template constexpr auto operator-(T const& lhs, LimitMax const& rhs) noexcept -> typename std::enable_if::value, SFINAE>::type { return lhs - rhs.operator T(); } } // namespace detail } // namespace libsemigroups #endif // LIBSEMIGROUPS_INCLUDE_CONSTANTS_HPP_