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