1 // 2 // libsemigroups - C++ library for semigroups and monoids 3 // Copyright (C) 2019 James D. Mitchell 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 // 18 // 19 // This file contains functionality for various constant values used in 20 // libsemigroups. 21 22 // TODO(later) 23 // 1. NegativeInfinity could be comparable with unsigned integers (always <). 24 // 2. specialisation of operator<< for ostringstream for better printing. I 25 // couldn't immediately get this to work. 26 27 #ifndef LIBSEMIGROUPS_INCLUDE_CONSTANTS_HPP_ 28 #define LIBSEMIGROUPS_INCLUDE_CONSTANTS_HPP_ 29 30 #include <cinttypes> // for int64_t 31 #include <limits> // for numeric_limits 32 #include <type_traits> // for is_integral 33 34 namespace libsemigroups { 35 namespace detail { 36 37 struct Min { 38 template <typename T> operator ()libsemigroups::detail::Min39 constexpr T operator()() const noexcept { 40 static_assert(std::is_integral<T>::value, 41 "can only call Min with an integral type"); 42 return std::numeric_limits<T>::min(); 43 } 44 }; 45 46 struct Max { 47 template <typename T> operator ()libsemigroups::detail::Max48 constexpr T operator()() const noexcept { 49 static_assert(std::is_integral<T>::value, 50 "can only call Max with an integral type"); 51 return std::numeric_limits<T>::max(); 52 } 53 }; 54 55 template <int64_t TOffset, typename TMaxOrMin> 56 struct Constant { 57 static_assert(std::is_same<TMaxOrMin, Max>::value 58 || std::is_same<TMaxOrMin, Min>::value, 59 "template parameter TMaxOrMin must be Max or Min"); 60 61 Constant() = default; 62 Constant(Constant const&) = default; 63 Constant(Constant&&) = default; 64 Constant& operator=(Constant const&) = default; 65 Constant& operator=(Constant&&) = default; 66 ~Constant() = default; 67 68 template <typename T> operator Tlibsemigroups::detail::Constant69 constexpr operator T() const noexcept { 70 static_assert( 71 std::is_integral<T>::value 72 && (std::is_signed<T>::value 73 || std::is_same<TMaxOrMin, Max>::value), 74 "the template parameter T must be an integral type, and either " 75 "unsigned or the template parameter TMaxOrMin must be Max."); 76 return TMaxOrMin().template operator()<T>() + TOffset; 77 } 78 }; 79 } // namespace detail 80 81 //////////////////////////////////////////////////////////////////////// 82 // Constant values 83 //////////////////////////////////////////////////////////////////////// 84 85 //! Type for undefined values. 86 using Undefined = detail::Constant<0, detail::Max>; 87 88 //! Type for positive infinity. 89 using PositiveInfinity = detail::Constant<-1, detail::Max>; 90 91 //! Type for the maximum value of something. 92 using LimitMax = detail::Constant<-2, detail::Max>; 93 94 //! Type for negative infinity. 95 using NegativeInfinity = detail::Constant<0, detail::Min>; 96 97 //! This variable is used to indicate that a value is undefined. UNDEFINED is 98 //! comparable with any integral value (signed or unsigned) or constant via 99 //! `==` and `!=` but not via `<` or `>`. 100 constexpr Undefined UNDEFINED{}; 101 102 //! This variable represents \f$\infty\f$. POSITIVE_INFINITY is comparable 103 //! via `==`, `!=`, `<`, `>` with any integral value (signed or unsigned) and 104 //! with NEGATIVE_INFINITY, and is comparable to any other constant via `==` 105 //! and `!=`, but not by `<` and `>`. 106 constexpr PositiveInfinity POSITIVE_INFINITY{}; 107 108 //! This variable represents the maximum value that certain function 109 //! parameters can have. LIMIT_MAX is comparable via `==`, `!=`, `<`, `>` 110 //! with any integral value (signed or unsigned), and is comparable to any 111 //! other constant via `==` and `!=`, but not by `<` and `>`. 112 constexpr LimitMax LIMIT_MAX{}; 113 114 //! This variable represents \f$-\infty\f$. NEGATIVE_INFINITY is comparable 115 //! via `==`, `!=`, `<`, `>` with any signed integral value and 116 //! with POSITIVE_INFINITY, and is comparable to any other constant via `==` 117 //! and `!=`. 118 constexpr NegativeInfinity NEGATIVE_INFINITY{}; 119 120 //////////////////////////////////////////////////////////////////////// 121 // Operators for all constants 122 //////////////////////////////////////////////////////////////////////// 123 124 // Note that for some reason Catch requires that the comparison functions are 125 // in the namespace detail. 126 namespace detail { 127 128 // operator== 129 // No SFINAE required, since the functions delegated to don't exist. 130 template <int64_t R, typename S, typename T> operator ==(Constant<R,S> const & lhs,T const & rhs)131 constexpr bool operator==(Constant<R, S> const& lhs, 132 T const& rhs) noexcept { 133 return lhs.operator T() == rhs; 134 } 135 136 template <int64_t R, typename S, typename T> operator ==(T const & lhs,Constant<R,S> const & rhs)137 constexpr bool operator==(T const& lhs, 138 Constant<R, S> const& rhs) noexcept { 139 return rhs.operator T() == lhs; 140 } 141 142 template <int64_t R1, typename S1, int64_t R2, typename S2> operator ==(detail::Constant<R1,S1> const &,detail::Constant<R2,S2> const &)143 constexpr bool operator==(detail::Constant<R1, S1> const&, 144 detail::Constant<R2, S2> const&) noexcept { 145 return std::is_same<S1, S2>::value && R1 == R2; 146 } 147 148 // operator!= 149 // No SFINAE required, since the functions delegated to don't exist. 150 template <int64_t R, typename S, typename T> operator !=(Constant<R,S> const & lhs,T const & rhs)151 constexpr bool operator!=(Constant<R, S> const& lhs, 152 T const& rhs) noexcept { 153 return !(lhs == rhs); 154 } 155 156 template <int64_t R, typename S, typename T> operator !=(T const & lhs,Constant<R,S> const & rhs)157 constexpr bool operator!=(T const& lhs, 158 Constant<R, S> const& rhs) noexcept { 159 return !(lhs == rhs); 160 } 161 162 template <int64_t R1, typename S1, int64_t R2, typename S2> operator !=(detail::Constant<R1,S1> const & lhs,detail::Constant<R2,S2> const & rhs)163 constexpr bool operator!=(detail::Constant<R1, S1> const& lhs, 164 detail::Constant<R2, S2> const& rhs) noexcept { 165 return !(lhs == rhs); 166 } 167 168 // operator> 169 // No SFINAE required, since the functions delegated to don't exist. 170 template <int64_t R, typename S, typename T> operator >(detail::Constant<R,S> const & lhs,T const & rhs)171 constexpr bool operator>(detail::Constant<R, S> const& lhs, 172 T const& rhs) noexcept { 173 return rhs < lhs; 174 } 175 176 template <int64_t R, typename S, typename T> operator >(T const & lhs,detail::Constant<R,S> const & rhs)177 constexpr bool operator>(T const& lhs, 178 detail::Constant<R, S> const& rhs) noexcept { 179 return rhs < lhs; 180 } 181 182 template <int64_t R, typename S> operator >(detail::Constant<R,S> const &,detail::Constant<R,S> const &)183 constexpr bool operator>(detail::Constant<R, S> const&, 184 detail::Constant<R, S> const&) noexcept { 185 return false; 186 } 187 188 template <int64_t R, typename S> operator <(detail::Constant<R,S> const &,detail::Constant<R,S> const &)189 constexpr bool operator<(detail::Constant<R, S> const&, 190 detail::Constant<R, S> const&) noexcept { 191 return false; 192 } 193 194 // No further operator< for Constant and Constant unless given explicitly 195 196 //////////////////////////////////////////////////////////////////////// 197 // Operators for specific constants 198 //////////////////////////////////////////////////////////////////////// 199 200 // PositiveInfinity is not less than any integral value, or 201 // NegativeInfinity. 202 template <typename T, typename SFINAE = bool> operator <(PositiveInfinity const &,T const &)203 constexpr auto operator<(PositiveInfinity const&, T const&) noexcept -> 204 typename std::enable_if<std::is_integral<T>::value 205 || std::is_same<NegativeInfinity, T>::value, 206 SFINAE>::type { 207 return false; 208 } 209 210 // Every integral value, and negative infinity, is less than 211 // PositiveInfinity. 212 template <typename T, typename SFINAE = bool> operator <(T const &,PositiveInfinity const &)213 constexpr auto operator<(T const&, PositiveInfinity const&) noexcept -> 214 typename std::enable_if<std::is_integral<T>::value 215 || std::is_same<NegativeInfinity, T>::value, 216 SFINAE>::type { 217 return true; 218 } 219 220 // NegativeInfinity is less than every integral value. 221 template <typename T, typename SFINAE = bool> operator <(NegativeInfinity const &,T const &)222 constexpr auto operator<(NegativeInfinity const&, T const&) noexcept -> 223 typename std::enable_if<std::is_integral<T>::value, SFINAE>::type { 224 return true; 225 } 226 227 // No integral value is less than NegativeInfinity. 228 template <typename T, typename SFINAE = bool> operator <(T const &,NegativeInfinity const &)229 constexpr auto operator<(T const&, NegativeInfinity const&) noexcept -> 230 typename std::enable_if<std::is_integral<T>::value, SFINAE>::type { 231 return false; 232 } 233 234 // LimitMax is compared by implicit conversion with any integral value. 235 template <typename T, typename SFINAE = bool> operator <(LimitMax const & lhs,T const & rhs)236 constexpr auto operator<(LimitMax const& lhs, T const& rhs) noexcept -> 237 typename std::enable_if<std::is_integral<T>::value, SFINAE>::type { 238 return lhs.operator T() < rhs; 239 } 240 241 // LimitMax is compared by implicit conversion with any integral value. 242 template <typename T, typename SFINAE = bool> operator <(T const & lhs,LimitMax const & rhs)243 constexpr auto operator<(T const& lhs, LimitMax const& rhs) noexcept -> 244 typename std::enable_if<std::is_integral<T>::value, SFINAE>::type { 245 return lhs < rhs.operator T(); 246 } 247 248 template <typename T, typename SFINAE = T> operator -(LimitMax const & lhs,T const & rhs)249 constexpr auto operator-(LimitMax const& lhs, T const& rhs) noexcept -> 250 typename std::enable_if<std::is_integral<T>::value, SFINAE>::type { 251 return lhs.operator T() - rhs; 252 } 253 254 template <typename T, typename SFINAE = T> operator -(T const & lhs,LimitMax const & rhs)255 constexpr auto operator-(T const& lhs, LimitMax const& rhs) noexcept -> 256 typename std::enable_if<std::is_integral<T>::value, SFINAE>::type { 257 return lhs - rhs.operator T(); 258 } 259 } // namespace detail 260 } // namespace libsemigroups 261 #endif // LIBSEMIGROUPS_INCLUDE_CONSTANTS_HPP_ 262