Phi
integer.hpp
1 // This file is heavily inspired by Jonathan Müllers <jonathanmueller.dev@gmail.com> type_safe library https://github.com/foonathan/type_safe
2 // licensed under the MIT license https://github.com/foonathan/type_safe/blob/master/LICENSE
3 // Original file at https://github.com/foonathan/type_safe/blob/master/include/type_safe/integer.hpp
4 /* MIT License
5 
6 Copyright (c) 2016-2020 Jonathan Müller
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25 */
26 
27 #ifndef INCG_PHI_CORE_INTEGER_HPP
28 #define INCG_PHI_CORE_INTEGER_HPP
29 
30 #include "phi/phi_config.hpp"
31 
32 #if PHI_HAS_EXTENSION_PRAGMA_ONCE()
33 # pragma once
34 #endif
35 
36 #include "phi/algorithm/swap.hpp"
37 #include "phi/compiler_support/constexpr.hpp"
38 #include "phi/compiler_support/inline.hpp"
39 #include "phi/compiler_support/nodiscard.hpp"
40 #include "phi/compiler_support/noexcept.hpp"
41 #include "phi/compiler_support/standard_library.hpp"
42 #include "phi/compiler_support/warning.hpp"
43 #include "phi/core/assert.hpp"
44 #include "phi/core/boolean.hpp"
45 #include "phi/forward/std/hash.hpp"
46 #include "phi/type_traits/conditional.hpp"
47 #include "phi/type_traits/enable_if.hpp"
48 #include "phi/type_traits/integral_constant.hpp"
49 #include "phi/type_traits/is_signed.hpp"
50 #include "phi/type_traits/is_unsafe_integer.hpp"
51 #include "phi/type_traits/is_unsigned.hpp"
52 #include <iosfwd>
53 #include <limits>
54 
55 DETAIL_PHI_BEGIN_NAMESPACE()
56 
57 template <typename IntegerT>
58 class integer;
59 
61 namespace detail
62 {
63  // integer conversion
64  template <typename FromT, typename ToT>
65  struct is_safe_integer_conversion
66  : public integral_constant<
67  bool, is_unsafe_integer<FromT>::value && is_unsafe_integer<ToT>::value &&
68  ((sizeof(FromT) <= sizeof(ToT) &&
69  is_signed<FromT>::value == is_signed<ToT>::value) ||
70  (sizeof(FromT) < sizeof(ToT) && is_unsigned<FromT>::value &&
71  is_signed<ToT>::value))>
72  {};
73 
74  template <typename FromT, typename ToT>
75  using enable_safe_integer_conversion =
76  enable_if_t<is_safe_integer_conversion<FromT, ToT>::value>;
77 
78  template <typename FromT, typename ToT>
79  using fallback_safe_integer_conversion =
80  enable_if_t<!is_safe_integer_conversion<FromT, ToT>::value>;
81 
82  // integer comparison
83  template <typename LhsT, typename RhsT>
84  struct is_safe_integer_comparison
85  : public integral_constant<bool, is_safe_integer_conversion<LhsT, RhsT>::value ||
86  is_safe_integer_conversion<RhsT, LhsT>::value>
87  {};
88 
89  template <typename LhsT, typename RhsT>
90  using enable_safe_integer_comparison =
91  enable_if_t<is_safe_integer_comparison<LhsT, RhsT>::value>;
92 
93  template <typename LhsT, typename RhsT>
94  using fallback_safe_integer_comparison =
95  enable_if_t<!is_safe_integer_comparison<LhsT, RhsT>::value>;
96 
97  // integer operation
98  template <typename LhsT, typename RhsT>
99  struct is_safe_integer_operation
100  : public integral_constant<bool, is_unsafe_integer<LhsT>::value &&
101  is_unsafe_integer<RhsT>::value &&
102  is_signed<LhsT>::value == is_signed<RhsT>::value>
103  {};
104 
105  template <typename LhsT, typename RhsT>
106  struct integer_result_type
107  : public enable_if<is_safe_integer_operation<LhsT, RhsT>::value,
108  conditional_t<sizeof(LhsT) < sizeof(RhsT), RhsT, LhsT>>
109  {};
110 
111  template <typename LhsT, typename RhsT>
112  using integer_result_t = typename integer_result_type<LhsT, RhsT>::type;
113 
114  template <typename LhsT, typename RhsT>
115  using fallback_integer_result =
116  enable_if_t<is_unsafe_integer<LhsT>::value && is_unsafe_integer<RhsT>::value &&
117  is_signed<LhsT>::value != is_signed<RhsT>::value>;
118 
119  // Error detection
120  struct signed_integer_tag
121  {};
122 
123  struct unsigned_integer_tag
124  {};
125 
126  template <typename TypeT>
127  using arithmetic_tag_for =
128  conditional_t<is_signed<TypeT>::value, signed_integer_tag, unsigned_integer_tag>;
129 
130  template <typename TypeT>
131  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_addition_error(signed_integer_tag /*tag*/, TypeT lhs,
132  TypeT rhs) PHI_NOEXCEPT
133  {
134  return rhs > TypeT(0) ? lhs > std::numeric_limits<TypeT>::max() - rhs :
135  lhs < std::numeric_limits<TypeT>::min() - rhs;
136  }
137 
138  template <typename TypeT>
139  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_addition_error(unsigned_integer_tag /*tag*/,
140  TypeT lhs, TypeT rhs) PHI_NOEXCEPT
141  {
142  return std::numeric_limits<TypeT>::max() - rhs < lhs;
143  }
144 
145  template <typename TypeT>
146  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_subtraction_error(signed_integer_tag /*tag*/,
147  TypeT lhs, TypeT rhs) PHI_NOEXCEPT
148  {
149  return rhs > TypeT(0) ? lhs < std::numeric_limits<TypeT>::min() + rhs :
150  lhs > std::numeric_limits<TypeT>::max() + rhs;
151  }
152 
153  template <typename TypeT>
154  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_subtraction_error(unsigned_integer_tag /*tag*/,
155  TypeT lhs, TypeT rhs) PHI_NOEXCEPT
156  {
157  return lhs < rhs;
158  }
159 
160  template <typename TypeT>
161  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_multiplication_error(signed_integer_tag /*tag*/,
162  TypeT lhs,
163  TypeT rhs) PHI_NOEXCEPT
164  {
165  return lhs > TypeT(0) ?
166  (rhs > TypeT(0) ?
167  lhs > std::numeric_limits<TypeT>::max() / rhs : // lhs, rhs > 0
168  rhs < std::numeric_limits<TypeT>::min() /
169  lhs) : // lhs > 0, rhs <= 0
170  (rhs > TypeT(0) ?
171  lhs < std::numeric_limits<TypeT>::min() / rhs : // lhs <= 0, rhs > 0
172  lhs != TypeT(0) && rhs < std::numeric_limits<TypeT>::max() /
173  lhs); // lhs, rhs <= 0
174  }
175 
176  template <typename TypeT>
177  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_multiplication_error(unsigned_integer_tag /*tag*/,
178  TypeT lhs,
179  TypeT rhs) PHI_NOEXCEPT
180  {
181  return rhs != TypeT(0) && lhs > std::numeric_limits<TypeT>::max() / rhs;
182  }
183 
184  template <typename TypeT>
185  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_division_error(signed_integer_tag /*tag*/, TypeT lhs,
186  TypeT rhs) PHI_NOEXCEPT
187  {
188  return rhs == TypeT(0) || (rhs == TypeT(-1) && lhs == std::numeric_limits<TypeT>::min());
189  }
190 
191  template <typename TypeT>
192  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_division_error(unsigned_integer_tag /*tag*/,
193  TypeT /*lhs*/, TypeT rhs) PHI_NOEXCEPT
194  {
195  return rhs == TypeT(0);
196  }
197 
198  template <typename TypeT>
199  PHI_ALWAYS_INLINE PHI_CONSTEXPR bool will_modulo_error(TypeT /*lhs*/, TypeT rhs) PHI_NOEXCEPT
200  {
201  return rhs == TypeT(0);
202  }
203 } // namespace detail
205 
206 PHI_GCC_SUPPRESS_WARNING_PUSH()
207 #if PHI_COMPILER_IS_ATLEAST(GCC, 10, 0, 0)
208 PHI_GCC_SUPPRESS_WARNING("-Warith-conversion")
209 #else
210 PHI_GCC_SUPPRESS_WARNING("-Wconversion")
211 #endif
212 
226 template <typename IntegerT>
227 class integer
228 {
229  static_assert(is_unsafe_integer<IntegerT>::value,
230  "[phi::integer] IntegerT must be a real integer type");
231 
232 public:
233  using this_type = integer<IntegerT>;
234  using value_type = IntegerT;
235  using limits_type = std::numeric_limits<IntegerT>;
236 
237  //=== constructors ===//
238 
239  integer() = delete;
240 
241  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
242  PHI_CONSTEXPR integer(const TypeT& val) PHI_NOEXCEPT : m_Value(val)
243  {}
244 
245  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
246  PHI_ALWAYS_INLINE PHI_CONSTEXPR integer(const integer<TypeT>& val) PHI_NOEXCEPT
247  : m_Value(static_cast<TypeT>(val))
248  {}
249 
250  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
251  PHI_CONSTEXPR integer(TypeT) = delete;
252 
253  //=== assignment ===//
254 
255  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
256  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator=(const TypeT& val) PHI_NOEXCEPT
257  {
258  m_Value = val;
259  return *this;
260  }
261 
262  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
263  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator=(const integer<TypeT>& val)
264  PHI_NOEXCEPT
265  {
266  m_Value = static_cast<TypeT>(val);
267  return *this;
268  }
269 
270  //=== conversion back ===//
271 
272  PHI_NODISCARD PHI_ALWAYS_INLINE explicit PHI_CONSTEXPR operator IntegerT() const PHI_NOEXCEPT
273  {
274  return m_Value;
275  }
276 
277  PHI_NODISCARD PHI_ALWAYS_INLINE PHI_CONSTEXPR IntegerT unsafe() const PHI_NOEXCEPT
278  {
279  return m_Value;
280  }
281 
282  PHI_NODISCARD PHI_ALWAYS_INLINE PHI_CONSTEXPR static integer<IntegerT> min() PHI_NOEXCEPT
283  {
284  return limits_type::min();
285  }
286 
287  PHI_NODISCARD PHI_ALWAYS_INLINE PHI_CONSTEXPR static integer<IntegerT> max() PHI_NOEXCEPT
288  {
289  return limits_type::max();
290  }
291 
292  PHI_EXTENDED_CONSTEXPR void swap(integer<IntegerT>& other) PHI_NOEXCEPT
293  {
294  phi::swap(m_Value, other.m_Value);
295  }
296 
297  //=== unary operators ===//
298  PHI_ALWAYS_INLINE PHI_CONSTEXPR integer operator+() const PHI_NOEXCEPT
299  {
300  return *this;
301  }
302 
303  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer operator-() const PHI_NOEXCEPT
304  {
305  static_assert(is_signed<IntegerT>::value, "Cannot call unary minus on unsigned integer");
306 
307  PHI_ASSERT(m_Value != limits_type::min(), "Unary minus will overflow. Args {}", m_Value);
308 
309  return integer(-m_Value);
310  }
311 
312  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator++() PHI_NOEXCEPT
313  {
314  PHI_ASSERT(!detail::will_addition_error(detail::arithmetic_tag_for<IntegerT>{}, m_Value,
315  IntegerT(1)),
316  "Operator++ will result in overflow. Args {}", m_Value);
317 
318  m_Value += 1;
319  return *this;
320  }
321 
322  // cppcheck-suppress functionConst; NOLINTNEXTLINE(readability-const-return-type)
323  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR const integer operator++(int) PHI_NOEXCEPT
324  {
325  auto res = *this;
326  ++*this;
327  return res;
328  }
329 
330  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator--() PHI_NOEXCEPT
331  {
332  PHI_ASSERT(!detail::will_subtraction_error(detail::arithmetic_tag_for<IntegerT>{}, m_Value,
333  IntegerT(1)),
334  "Operator-- will result in underflow. Args {}", m_Value);
335 
336  m_Value -= 1;
337  return *this;
338  }
339 
340  // cppcheck-suppress functionConst; NOLINTNEXTLINE(readability-const-return-type)
341  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR const integer operator--(int) PHI_NOEXCEPT
342  {
343  auto res = *this;
344  --*this;
345  return res;
346  }
347 
348  //=== compound assignment ====//
349 
350  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
351  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator+=(const integer<TypeT>& other)
352  PHI_NOEXCEPT
353  {
354  PHI_ASSERT(!detail::will_addition_error<IntegerT>(detail::arithmetic_tag_for<IntegerT>{},
355  m_Value, other.unsafe()),
356  "Addition will result in overflow. Args {} + {}", m_Value, other.unsafe());
357 
358  m_Value += other.unsafe();
359  return *this;
360  }
361 
362  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
363  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator+=(const TypeT& other) PHI_NOEXCEPT
364  {
365  return *this += integer<TypeT>(other);
366  }
367 
368  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
369  integer& operator+=(integer<TypeT>) = delete;
370 
371  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
372  integer& operator+=(TypeT) = delete;
373 
374  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
375  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator-=(const integer<TypeT>& other)
376  PHI_NOEXCEPT
377  {
378  PHI_ASSERT(!detail::will_subtraction_error<IntegerT>(detail::arithmetic_tag_for<IntegerT>{},
379  m_Value, other.unsafe()),
380  "Subtraction will result in underflow. Args {} - {}", m_Value, other.unsafe());
381 
382  m_Value -= other.unsafe();
383  return *this;
384  }
385 
386  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
387  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator-=(const TypeT& other) PHI_NOEXCEPT
388  {
389  return *this -= integer<TypeT>(other);
390  }
391 
392  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
393  integer& operator-=(integer<TypeT>) = delete;
394 
395  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
396  integer& operator-=(TypeT) = delete;
397 
398  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
399  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator*=(const integer<TypeT>& other)
400  PHI_NOEXCEPT
401  {
402  PHI_ASSERT(!detail::will_multiplication_error<IntegerT>(
403  detail::arithmetic_tag_for<IntegerT>{}, m_Value, other.unsafe()),
404  "Multiplication will result in overflow. Args {} * {}", m_Value, other.unsafe());
405 
406  m_Value *= other.unsafe();
407  return *this;
408  }
409 
410  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
411  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator*=(const TypeT& other) PHI_NOEXCEPT
412  {
413  return *this *= integer<TypeT>(other);
414  }
415 
416  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
417  integer& operator*=(integer<TypeT>) = delete;
418 
419  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
420  integer& operator*=(TypeT) = delete;
421 
422  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
423  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator/=(const integer<TypeT>& other)
424  PHI_NOEXCEPT
425  {
426  PHI_ASSERT(!detail::will_division_error<IntegerT>(detail::arithmetic_tag_for<IntegerT>{},
427  m_Value, other.unsafe()),
428  "Division error. Args {} / {}", m_Value, other.unsafe());
429 
430  m_Value /= other.unsafe();
431  return *this;
432  }
433 
434  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
435  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator/=(const TypeT& other) PHI_NOEXCEPT
436  {
437  return *this /= integer<TypeT>(other);
438  }
439 
440  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
441  integer& operator/=(integer<TypeT>) = delete;
442 
443  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
444  integer& operator/=(TypeT) = delete;
445 
446  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
447  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator%=(const integer<TypeT>& other)
448  PHI_NOEXCEPT
449  {
450  PHI_ASSERT(!detail::will_modulo_error<IntegerT>(m_Value, other.unsafe()),
451  "Modulo error. Args {} % {}", m_Value, other.unsafe());
452 
453  m_Value %= other.unsafe();
454  return *this;
455  }
456 
457  template <typename TypeT, typename = detail::enable_safe_integer_conversion<TypeT, IntegerT>>
458  PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR integer& operator%=(const TypeT& other) PHI_NOEXCEPT
459  {
460  return *this %= integer<TypeT>(other);
461  }
462 
463  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
464  integer& operator%=(integer<TypeT>) = delete;
465 
466  template <typename TypeT, typename = detail::fallback_safe_integer_conversion<TypeT, IntegerT>>
467  integer& operator%=(TypeT) = delete;
468 
469 private:
470  IntegerT m_Value;
471 };
472 
473 PHI_GCC_SUPPRESS_WARNING_POP()
474 
475 //=== comparison ===//
476 
477 template <typename LhsT, typename RhsT,
478  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
479 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator==(const integer<LhsT>& lhs,
480  const integer<RhsT>& rhs) PHI_NOEXCEPT
481 {
482  return static_cast<LhsT>(lhs) == static_cast<RhsT>(rhs);
483 }
484 
485 template <typename LhsT, typename RhsT,
486  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
487 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator==(const LhsT& lhs,
488  const integer<RhsT>& rhs) PHI_NOEXCEPT
489 {
490  return integer<LhsT>(lhs) == rhs;
491 }
492 
493 template <typename LhsT, typename RhsT,
494  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
495 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator==(const integer<LhsT>& lhs,
496  const RhsT& rhs) PHI_NOEXCEPT
497 {
498  return lhs == integer<RhsT>(rhs);
499 }
500 
501 template <typename LhsT, typename RhsT,
502  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
503 PHI_CONSTEXPR boolean operator==(integer<LhsT>, integer<RhsT>) = delete;
504 
505 template <typename LhsT, typename RhsT,
506  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
507 PHI_CONSTEXPR boolean operator==(LhsT, integer<RhsT>) = delete;
508 
509 template <typename LhsT, typename RhsT,
510  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
511 PHI_CONSTEXPR boolean operator==(integer<LhsT>, RhsT) = delete;
512 
513 template <typename LhsT, typename RhsT,
514  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
515 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator!=(const integer<LhsT>& lhs,
516  const integer<RhsT>& rhs) PHI_NOEXCEPT
517 {
518  return static_cast<LhsT>(lhs) != static_cast<RhsT>(rhs);
519 }
520 
521 template <typename LhsT, typename RhsT,
522  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
523 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator!=(const LhsT& lhs,
524  const integer<RhsT>& rhs) PHI_NOEXCEPT
525 {
526  return integer<LhsT>(lhs) != rhs;
527 }
528 
529 template <typename LhsT, typename RhsT,
530  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
531 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator!=(const integer<LhsT>& lhs,
532  const RhsT& rhs) PHI_NOEXCEPT
533 {
534  return lhs != integer<RhsT>(rhs);
535 }
536 
537 template <typename LhsT, typename RhsT,
538  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
539 PHI_CONSTEXPR boolean operator!=(integer<LhsT>, integer<RhsT>) = delete;
540 
541 template <typename LhsT, typename RhsT,
542  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
543 PHI_CONSTEXPR boolean operator!=(LhsT, integer<RhsT>) = delete;
544 
545 template <typename LhsT, typename RhsT,
546  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
547 PHI_CONSTEXPR boolean operator!=(integer<LhsT>, RhsT) = delete;
548 
549 template <typename LhsT, typename RhsT,
550  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
551 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator<(const integer<LhsT>& lhs,
552  const integer<RhsT>& rhs) PHI_NOEXCEPT
553 {
554  return static_cast<LhsT>(lhs) < static_cast<RhsT>(rhs);
555 }
556 
557 template <typename LhsT, typename RhsT,
558  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
559 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator<(const LhsT& lhs,
560  const integer<RhsT>& rhs) PHI_NOEXCEPT
561 {
562  return integer<LhsT>(lhs) < rhs;
563 }
564 
565 template <typename LhsT, typename RhsT,
566  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
567 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator<(const integer<LhsT>& lhs,
568  const RhsT& rhs) PHI_NOEXCEPT
569 {
570  return lhs < integer<RhsT>(rhs);
571 }
572 
573 template <typename LhsT, typename RhsT,
574  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
575 PHI_CONSTEXPR boolean operator<(integer<LhsT>, integer<RhsT>) = delete;
576 
577 template <typename LhsT, typename RhsT,
578  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
579 PHI_CONSTEXPR boolean operator<(LhsT, integer<RhsT>) = delete;
580 
581 template <typename LhsT, typename RhsT,
582  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
583 PHI_CONSTEXPR boolean operator<(integer<LhsT>, RhsT) = delete;
584 
585 template <typename LhsT, typename RhsT,
586  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
587 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator<=(const integer<LhsT>& lhs,
588  const integer<RhsT>& rhs) PHI_NOEXCEPT
589 {
590  return static_cast<LhsT>(lhs) <= static_cast<RhsT>(rhs);
591 }
592 
593 template <typename LhsT, typename RhsT,
594  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
595 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator<=(const LhsT& lhs,
596  const integer<RhsT>& rhs) PHI_NOEXCEPT
597 {
598  return integer<LhsT>(lhs) <= rhs;
599 }
600 
601 template <typename LhsT, typename RhsT,
602  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
603 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator<=(const integer<LhsT>& lhs,
604  const RhsT& rhs) PHI_NOEXCEPT
605 {
606  return lhs <= integer<RhsT>(rhs);
607 }
608 
609 template <typename LhsT, typename RhsT,
610  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
611 PHI_CONSTEXPR boolean operator<=(integer<LhsT>, integer<RhsT>) = delete;
612 
613 template <typename LhsT, typename RhsT,
614  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
615 PHI_CONSTEXPR boolean operator<=(LhsT, integer<RhsT>) = delete;
616 
617 template <typename LhsT, typename RhsT,
618  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
619 PHI_CONSTEXPR boolean operator<=(integer<LhsT>, RhsT) = delete;
620 
621 template <typename LhsT, typename RhsT,
622  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
623 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator>(const integer<LhsT>& lhs,
624  const integer<RhsT>& rhs) PHI_NOEXCEPT
625 {
626  return static_cast<LhsT>(lhs) > static_cast<RhsT>(rhs);
627 }
628 
629 template <typename LhsT, typename RhsT,
630  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
631 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator>(const LhsT& lhs,
632  const integer<RhsT>& rhs) PHI_NOEXCEPT
633 {
634  return integer<LhsT>(lhs) > rhs;
635 }
636 
637 template <typename LhsT, typename RhsT,
638  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
639 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator>(const integer<LhsT>& lhs,
640  const RhsT& rhs) PHI_NOEXCEPT
641 {
642  return lhs > integer<RhsT>(rhs);
643 }
644 
645 template <typename LhsT, typename RhsT,
646  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
647 PHI_CONSTEXPR boolean operator>(integer<LhsT>, integer<RhsT>) = delete;
648 
649 template <typename LhsT, typename RhsT,
650  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
651 PHI_CONSTEXPR boolean operator>(LhsT, integer<RhsT>) = delete;
652 
653 template <typename LhsT, typename RhsT,
654  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
655 PHI_CONSTEXPR boolean operator>(integer<LhsT>, RhsT) = delete;
656 
657 template <typename LhsT, typename RhsT,
658  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
659 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator>=(const integer<LhsT>& lhs,
660  const integer<RhsT>& rhs) PHI_NOEXCEPT
661 {
662  return static_cast<LhsT>(lhs) >= static_cast<RhsT>(rhs);
663 }
664 
665 template <typename LhsT, typename RhsT,
666  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
667 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator>=(const LhsT& lhs,
668  const integer<RhsT>& rhs) PHI_NOEXCEPT
669 {
670  return integer<LhsT>(lhs) >= rhs;
671 }
672 
673 template <typename LhsT, typename RhsT,
674  typename = detail::enable_safe_integer_comparison<LhsT, RhsT>>
675 PHI_ALWAYS_INLINE PHI_CONSTEXPR boolean operator>=(const integer<LhsT>& lhs,
676  const RhsT& rhs) PHI_NOEXCEPT
677 {
678  return lhs >= integer<RhsT>(rhs);
679 }
680 
681 template <typename LhsT, typename RhsT,
682  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
683 PHI_CONSTEXPR boolean operator>=(integer<LhsT>, integer<RhsT>) = delete;
684 
685 template <typename LhsT, typename RhsT,
686  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
687 PHI_CONSTEXPR boolean operator>=(LhsT, integer<RhsT>) = delete;
688 
689 template <typename LhsT, typename RhsT,
690  typename = detail::fallback_safe_integer_comparison<LhsT, RhsT>>
691 PHI_CONSTEXPR boolean operator>=(integer<LhsT>, RhsT) = delete;
692 
693 //=== binary operations ===//
694 
695 template <typename LhsT, typename RhsT>
696 PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR auto operator+(const integer<LhsT>& lhs,
697  const integer<RhsT>& rhs)
698  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
699 {
700  using type = detail::integer_result_t<LhsT, RhsT>;
701  PHI_ASSERT(!detail::will_addition_error(detail::arithmetic_tag_for<type>{},
702  static_cast<type>(lhs.unsafe()),
703  static_cast<type>(rhs.unsafe())),
704  "Addition will result in overflow. Args {} + {}", lhs.unsafe(), rhs.unsafe());
705 
706  return integer<type>(static_cast<type>(static_cast<LhsT>(lhs) + static_cast<RhsT>(rhs)));
707 }
708 
709 template <typename LhsT, typename RhsT>
710 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator+(const LhsT& lhs, const integer<RhsT>& rhs)
711  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
712 {
713  return integer<LhsT>(lhs) + rhs;
714 }
715 
716 template <typename LhsT, typename RhsT>
717 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator+(const integer<LhsT>& lhs, const RhsT& rhs)
718  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
719 {
720  return lhs + integer<RhsT>(rhs);
721 }
722 
723 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
724 PHI_CONSTEXPR int operator+(integer<LhsT>, integer<RhsT>) = delete;
725 
726 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
727 PHI_CONSTEXPR int operator+(LhsT, integer<RhsT>) = delete;
728 
729 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
730 PHI_CONSTEXPR int operator+(integer<LhsT>, RhsT) = delete;
731 
732 template <typename LhsT, typename RhsT>
733 PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR auto operator-(const integer<LhsT>& lhs,
734  const integer<RhsT>& rhs)
735  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
736 {
737  using type = detail::integer_result_t<LhsT, RhsT>;
738  PHI_ASSERT(!detail::will_subtraction_error(detail::arithmetic_tag_for<type>{},
739  static_cast<type>(lhs.unsafe()),
740  static_cast<type>(rhs.unsafe())),
741  "Subtraction will result in underflow. Args {} - {}", lhs.unsafe(), rhs.unsafe());
742 
743  return integer<type>(static_cast<type>(static_cast<LhsT>(lhs) - static_cast<RhsT>(rhs)));
744 }
745 
746 template <typename LhsT, typename RhsT>
747 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator-(const LhsT& lhs, const integer<RhsT>& rhs)
748  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
749 {
750  return integer<LhsT>(lhs) - rhs;
751 }
752 
753 template <typename LhsT, typename RhsT>
754 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator-(const integer<LhsT>& lhs, const RhsT& rhs)
755  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
756 {
757  return lhs - integer<RhsT>(rhs);
758 }
759 
760 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
761 PHI_CONSTEXPR int operator-(integer<LhsT>, integer<RhsT>) = delete;
762 
763 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
764 PHI_CONSTEXPR int operator-(LhsT, integer<RhsT>) = delete;
765 
766 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
767 PHI_CONSTEXPR int operator-(integer<LhsT>, RhsT) = delete;
768 
769 template <typename LhsT, typename RhsT>
770 PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR auto operator*(const integer<LhsT>& lhs,
771  const integer<RhsT>& rhs)
772  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
773 {
774  using type = detail::integer_result_t<LhsT, RhsT>;
775  PHI_ASSERT(!detail::will_multiplication_error(detail::arithmetic_tag_for<type>{},
776  static_cast<type>(lhs.unsafe()),
777  static_cast<type>(rhs.unsafe())),
778  "Multiplication will result in overflow. Args {} * {}", lhs.unsafe(), rhs.unsafe());
779 
780  return integer<type>(static_cast<type>(static_cast<LhsT>(lhs) * static_cast<RhsT>(rhs)));
781 }
782 
783 template <typename LhsT, typename RhsT>
784 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator*(const LhsT& lhs, const integer<RhsT>& rhs)
785  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
786 {
787  return integer<LhsT>(lhs) * rhs;
788 }
789 
790 template <typename LhsT, typename RhsT>
791 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator*(const integer<LhsT>& lhs, const RhsT& rhs)
792  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
793 {
794  return lhs * integer<RhsT>(rhs);
795 }
796 
797 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
798 PHI_CONSTEXPR int operator*(integer<LhsT>, integer<RhsT>) = delete;
799 
800 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
801 PHI_CONSTEXPR int operator*(LhsT, integer<RhsT>) = delete;
802 
803 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
804 PHI_CONSTEXPR int operator*(integer<LhsT>, RhsT) = delete;
805 
806 template <typename LhsT, typename RhsT>
807 PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR auto operator/(const integer<LhsT>& lhs,
808  const integer<RhsT>& rhs)
809  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
810 {
811  using type = detail::integer_result_t<LhsT, RhsT>;
812  PHI_ASSERT(!detail::will_division_error(detail::arithmetic_tag_for<type>{},
813  static_cast<type>(lhs.unsafe()),
814  static_cast<type>(rhs.unsafe())),
815  "Division by zero/overflow. Args {} / {}", lhs.unsafe(), rhs.unsafe());
816 
817  return integer<type>(static_cast<type>(static_cast<LhsT>(lhs) / static_cast<RhsT>(rhs)));
818 }
819 
820 template <typename LhsT, typename RhsT>
821 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator/(const LhsT& lhs, const integer<RhsT>& rhs)
822  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
823 {
824  return integer<LhsT>(lhs) / rhs;
825 }
826 
827 template <typename LhsT, typename RhsT>
828 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator/(const integer<LhsT>& lhs, const RhsT& rhs)
829  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
830 {
831  return lhs / integer<RhsT>(rhs);
832 }
833 
834 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
835 PHI_CONSTEXPR int operator/(integer<LhsT>, integer<RhsT>) = delete;
836 
837 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
838 PHI_CONSTEXPR int operator/(LhsT, integer<RhsT>) = delete;
839 
840 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
841 PHI_CONSTEXPR int operator/(integer<LhsT>, RhsT) = delete;
842 
843 template <typename LhsT, typename RhsT>
844 PHI_ALWAYS_INLINE PHI_EXTENDED_CONSTEXPR auto operator%(const integer<LhsT>& lhs,
845  const integer<RhsT>& rhs)
846  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
847 {
848  using type = detail::integer_result_t<LhsT, RhsT>;
849  PHI_ASSERT(!detail::will_modulo_error(static_cast<type>(lhs.unsafe()),
850  static_cast<type>(rhs.unsafe())),
851  "Modulo by zero. Args {} % {}", lhs.unsafe(), rhs.unsafe());
852 
853  return integer<type>(static_cast<type>(static_cast<LhsT>(lhs) % static_cast<RhsT>(rhs)));
854 }
855 
856 template <typename LhsT, typename RhsT>
857 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator%(const LhsT& lhs, const integer<RhsT>& rhs)
858  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
859 {
860  return integer<LhsT>(lhs) % rhs;
861 }
862 
863 template <typename LhsT, typename RhsT>
864 PHI_ALWAYS_INLINE PHI_CONSTEXPR auto operator%(const integer<LhsT>& lhs, const RhsT& rhs)
865  PHI_NOEXCEPT->integer<detail::integer_result_t<LhsT, RhsT>>
866 {
867  return lhs % integer<RhsT>(rhs);
868 }
869 
870 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
871 PHI_CONSTEXPR int operator%(integer<LhsT>, integer<RhsT>) = delete;
872 
873 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
874 PHI_CONSTEXPR int operator%(LhsT, integer<RhsT>) = delete;
875 
876 template <typename LhsT, typename RhsT, typename = detail::fallback_integer_result<LhsT, RhsT>>
877 PHI_CONSTEXPR int operator%(integer<LhsT>, RhsT) = delete;
878 
879 //=== input/output ===/
880 
881 template <typename CharT, typename CharTraitsT, typename IntegerT>
882 std::basic_istream<CharT, CharTraitsT>& operator>>(std::basic_istream<CharT, CharTraitsT>& stream,
883  integer<IntegerT>& integer)
884 {
885  IntegerT val{IntegerT(0)};
886  stream >> val;
887  integer = val;
888  return stream;
889 }
890 
891 template <typename CharT, typename CharTraitsT, typename IntegerT>
892 std::basic_ostream<CharT, CharTraitsT>& operator<<(std::basic_ostream<CharT, CharTraitsT>& stream,
893  const integer<IntegerT>& val)
894 {
895  return stream << static_cast<IntegerT>(val);
896 }
897 
898 template <typename IntegerT>
899 PHI_EXTENDED_CONSTEXPR_OR_INLINE void swap(integer<IntegerT>& lhs,
900  integer<IntegerT>& rhs) PHI_NOEXCEPT
901 {
902  lhs.swap(rhs);
903 }
904 
905 DETAIL_PHI_END_NAMESPACE()
906 
907 DETAIL_PHI_BEGIN_STD_NAMESPACE()
908 
909 template <typename IntegerT>
911 struct hash<phi::integer<IntegerT>>
912 {
913  phi::size_t operator()(const phi::integer<IntegerT>& val) const PHI_NOEXCEPT
914  {
915  return std::hash<IntegerT>()(static_cast<IntegerT>(val));
916  }
917 };
918 
919 template <typename IntegerT>
920 struct numeric_limits<phi::integer<IntegerT>> : public std::numeric_limits<IntegerT>
921 {};
922 
923 DETAIL_PHI_END_STD_NAMESPACE()
924 
925 #endif // INCG_PHI_CORE_INTEGER_HPP
Definition: integral_constant.hpp:19
A type safe integer class.
Definition: integer.hpp:58
Definition: swap.hpp:23
Definition: hash.hpp:15
Definition: common_type.test.cpp:195