]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/safe_numerics/safe_base_operations.hpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / boost / safe_numerics / safe_base_operations.hpp
CommitLineData
92f5a8d4
TL
1#ifndef BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP
2#define BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP
3
4// Copyright (c) 2012 Robert Ramey
5//
6// Distributed under the Boost Software License, Version 1.0. (See
7// accompanying file LICENSE_1_0.txt or copy at
8// http://www.boost.org/LICENSE_1_0.txt)
9
10#include <limits>
11#include <type_traits> // is_base_of, is_same, is_floating_point, conditional
12#include <algorithm> // max
20effc67
TL
13#include <istream>
14#include <ostream>
1e59de90 15#include <utility> // declval
92f5a8d4
TL
16
17#include <boost/config.hpp>
18
19#include <boost/core/enable_if.hpp> // lazy_enable_if
20#include <boost/integer.hpp>
21#include <boost/logic/tribool.hpp>
22
1e59de90
TL
23#include "concept/numeric.hpp"
24
92f5a8d4
TL
25#include "checked_integer.hpp"
26#include "checked_result.hpp"
27#include "safe_base.hpp"
28
29#include "interval.hpp"
30#include "utility.hpp"
31
1e59de90
TL
32#include <boost/mp11/utility.hpp> // mp_valid
33#include <boost/mp11/function.hpp> // mp_and, mp_or
34
92f5a8d4
TL
35namespace boost {
36namespace safe_numerics {
37
1e59de90
TL
38////////////////////////////////////////////////////////////////////////////////
39// compile time error dispatcher
40
41// note slightly baroque implementation of a compile time switch statement
42// which instatiates only those cases which are actually invoked. This is
43// motivated to implement the "trap" functionality which will generate a syntax
44// error if and only a function which might fail is called.
45
46namespace dispatch_switch {
47
48 template<class EP, safe_numerics_actions>
49 struct dispatch_case {};
50
51 template<class EP>
52 struct dispatch_case<EP, safe_numerics_actions::uninitialized_value> {
53 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
54 EP::on_uninitialized_value(e, msg);
55 }
56 };
57 template<class EP>
58 struct dispatch_case<EP, safe_numerics_actions::arithmetic_error> {
59 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
60 EP::on_arithmetic_error(e, msg);
61 }
62 };
63 template<class EP>
64 struct dispatch_case<EP, safe_numerics_actions::implementation_defined_behavior> {
65 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
66 EP::on_implementation_defined_behavior(e, msg);
67 }
68 };
69 template<class EP>
70 struct dispatch_case<EP, safe_numerics_actions::undefined_behavior> {
71 constexpr static void invoke(const safe_numerics_error & e, const char * msg){
72 EP::on_undefined_behavior(e, msg);
73 }
74 };
75
76} // dispatch_switch
77
78template<class EP, safe_numerics_error E>
79constexpr inline void
80dispatch(const char * msg){
81 constexpr safe_numerics_actions a = make_safe_numerics_action(E);
82 dispatch_switch::dispatch_case<EP, a>::invoke(E, msg);
83}
84
85template<class EP, class R>
86class dispatch_and_return {
87public:
88 template<safe_numerics_error E>
89 constexpr static checked_result<R> invoke(
90 char const * const & msg
91 ) {
92 dispatch<EP, E>(msg);
93 return checked_result<R>(E, msg);
94 }
95};
96
92f5a8d4
TL
97/////////////////////////////////////////////////////////////////
98// validation
99
f67539c2 100template<typename R, R Min, R Max, typename E>
92f5a8d4
TL
101struct validate_detail {
102 using r_type = checked_result<R>;
103
104 struct exception_possible {
f67539c2 105 template<typename T>
92f5a8d4
TL
106 constexpr static R return_value(
107 const T & t
108 ){
109 // INT08-C
110 const r_type rx = heterogeneous_checked_operation<
111 R,
f67539c2
TL
112 Min,
113 Max,
92f5a8d4
TL
114 typename base_type<T>::type,
115 dispatch_and_return<E, R>
116 >::cast(t);
f67539c2
TL
117
118 return rx;
92f5a8d4
TL
119 }
120 };
121 struct exception_not_possible {
f67539c2 122 template<typename T>
92f5a8d4
TL
123 constexpr static R return_value(
124 const T & t
125 ){
126 return static_cast<R>(base_value(t));
127 }
128 };
129
f67539c2 130 template<typename T>
92f5a8d4
TL
131 constexpr static R return_value(const T & t){
132 constexpr const interval<r_type> t_interval{
133 checked::cast<R>(base_value(std::numeric_limits<T>::min())),
134 checked::cast<R>(base_value(std::numeric_limits<T>::max()))
135 };
136 constexpr const interval<r_type> r_interval{r_type(Min), r_type(Max)};
137
92f5a8d4 138 static_assert(
f67539c2
TL
139 true != static_cast<bool>(r_interval.excludes(t_interval)),
140 "can't cast from ranges that don't overlap"
92f5a8d4 141 );
92f5a8d4
TL
142 return std::conditional<
143 static_cast<bool>(r_interval.includes(t_interval)),
144 exception_not_possible,
145 exception_possible
146 >::type::return_value(t);
147 }
148};
149
150template<class Stored, Stored Min, Stored Max, class P, class E>
151template<class T>
1e59de90 152constexpr inline Stored safe_base<Stored, Min, Max, P, E>::
92f5a8d4 153validated_cast(const T & t) const {
f67539c2 154 return validate_detail<Stored,Min,Max,E>::return_value(t);
92f5a8d4
TL
155}
156
92f5a8d4
TL
157/////////////////////////////////////////////////////////////////
158// constructors
159
20effc67 160// default constructor
92f5a8d4 161template<class Stored, Stored Min, Stored Max, class P, class E>
1e59de90
TL
162constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(){
163 static_assert(
164 std::is_arithmetic<Stored>(),
165 "currently, safe numeric base types must currently be arithmetic types"
166 );
92f5a8d4
TL
167 dispatch<E, safe_numerics_error::uninitialized_value>(
168 "safe values must be initialized"
169 );
170}
20effc67 171// construct an instance of a safe type from an instance of a convertible underlying type.
92f5a8d4 172template<class Stored, Stored Min, Stored Max, class P, class E>
1e59de90 173constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
20effc67
TL
174 const Stored & rhs,
175 skip_validation
92f5a8d4 176) :
20effc67 177 m_t(rhs)
1e59de90
TL
178{
179 static_assert(
180 std::is_arithmetic<Stored>(),
181 "currently, safe numeric base types must currently be arithmetic types"
182 );
183}
92f5a8d4 184
20effc67 185// construct an instance from an instance of a convertible underlying type.
92f5a8d4 186template<class Stored, Stored Min, Stored Max, class P, class E>
20effc67
TL
187 template<
188 class T,
189 typename std::enable_if<
190 std::is_convertible<T, Stored>::value,
191 bool
192 >::type
193 >
1e59de90 194constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(const T &t) :
92f5a8d4 195 m_t(validated_cast(t))
1e59de90
TL
196{
197 static_assert(
198 std::is_arithmetic<Stored>(),
199 "currently, safe numeric base types must currently be arithmetic types"
200 );
201}
92f5a8d4 202
20effc67 203// construct an instance of a safe type from a literal value
92f5a8d4 204template<class Stored, Stored Min, Stored Max, class P, class E>
20effc67 205template<typename T, T N, class Px, class Ex>
1e59de90 206constexpr inline /*explicit*/ safe_base<Stored, Min, Max, P, E>::safe_base(
20effc67 207 const safe_literal_impl<T, N, Px, Ex> & t
92f5a8d4 208) :
20effc67 209 m_t(validated_cast(t))
1e59de90
TL
210{ static_assert(
211 std::is_arithmetic<Stored>(),
212 "currently, safe numeric base types must currently be arithmetic types"
213 );
214}
92f5a8d4
TL
215
216/////////////////////////////////////////////////////////////////
217// casting operators
218
219// cast to a builtin type from a safe type
220template< class Stored, Stored Min, Stored Max, class P, class E>
221template<
222 class R,
223 typename std::enable_if<
224 ! boost::safe_numerics::is_safe<R>::value,
225 int
226 >::type
227>
1e59de90 228constexpr inline safe_base<Stored, Min, Max, P, E>::
92f5a8d4 229operator R () const {
92f5a8d4 230 // if static values don't overlap, the program can never function
92f5a8d4
TL
231 constexpr const interval<R> r_interval;
232 constexpr const interval<Stored> this_interval(Min, Max);
233 static_assert(
234 ! r_interval.excludes(this_interval),
235 "safe type cannot be constructed with this type"
236 );
92f5a8d4
TL
237 return validate_detail<
238 R,
239 std::numeric_limits<R>::min(),
240 std::numeric_limits<R>::max(),
92f5a8d4 241 E
20effc67 242 >::return_value(m_t);
92f5a8d4
TL
243}
244
245/////////////////////////////////////////////////////////////////
246// binary operators
247
248template<class T, class U>
249struct common_exception_policy {
250 static_assert(is_safe<T>::value || is_safe<U>::value,
251 "at least one type must be a safe type"
252 );
253
254 using t_exception_policy = typename get_exception_policy<T>::type;
255 using u_exception_policy = typename get_exception_policy<U>::type;
256
257 static_assert(
258 std::is_same<t_exception_policy, u_exception_policy>::value
259 || std::is_same<t_exception_policy, void>::value
260 || std::is_same<void, u_exception_policy>::value,
261 "if the exception policies are different, one must be void!"
262 );
263
264 static_assert(
265 ! (std::is_same<t_exception_policy, void>::value
266 && std::is_same<void, u_exception_policy>::value),
267 "at least one exception policy must not be void"
268 );
269
270 using type =
271 typename std::conditional<
272 !std::is_same<void, u_exception_policy>::value,
273 u_exception_policy,
274 typename std::conditional<
275 !std::is_same<void, t_exception_policy>::value,
276 t_exception_policy,
277 //
278 void
279 >::type >::type;
280
281 static_assert(
282 !std::is_same<void, type>::value,
283 "exception_policy is void"
284 );
285};
286
287template<class T, class U>
288struct common_promotion_policy {
289 static_assert(is_safe<T>::value || is_safe<U>::value,
290 "at least one type must be a safe type"
291 );
292 using t_promotion_policy = typename get_promotion_policy<T>::type;
293 using u_promotion_policy = typename get_promotion_policy<U>::type;
294 static_assert(
295 std::is_same<t_promotion_policy, u_promotion_policy>::value
296 ||std::is_same<t_promotion_policy, void>::value
297 ||std::is_same<void, u_promotion_policy>::value,
298 "if the promotion policies are different, one must be void!"
299 );
300 static_assert(
301 ! (std::is_same<t_promotion_policy, void>::value
302 && std::is_same<void, u_promotion_policy>::value),
303 "at least one promotion policy must not be void"
304 );
305
306 using type =
307 typename std::conditional<
308 ! std::is_same<void, u_promotion_policy>::value,
309 u_promotion_policy,
310 typename std::conditional<
311 ! std::is_same<void, t_promotion_policy>::value,
312 t_promotion_policy,
313 //
314 void
315 >::type >::type;
316
317 static_assert(
318 ! std::is_same<void, type>::value,
319 "promotion_policy is void"
320 );
321
322};
323
324// give the resultant base type, figure out what the final result
325// type will be. Note we currently need this because we support
326// return of only safe integer types. Someday ..., we'll support
327// all other safe types including float and user defined ones.
92f5a8d4
TL
328
329// helper - cast arguments to binary operators to a specified
330// result type
331
332template<class EP, class R, class T, class U>
1e59de90 333constexpr inline static std::pair<R, R> casting_helper(const T & t, const U & u){
92f5a8d4
TL
334 using r_type = checked_result<R>;
335 const r_type tx = heterogeneous_checked_operation<
336 R,
f67539c2
TL
337 std::numeric_limits<R>::min(),
338 std::numeric_limits<R>::max(),
92f5a8d4
TL
339 typename base_type<T>::type,
340 dispatch_and_return<EP, R>
341 >::cast(base_value(t));
342 const R tr = tx.exception()
343 ? static_cast<R>(t)
1e59de90 344 : tx.m_contents.m_r;
92f5a8d4
TL
345
346 const r_type ux = heterogeneous_checked_operation<
347 R,
f67539c2
TL
348 std::numeric_limits<R>::min(),
349 std::numeric_limits<R>::max(),
92f5a8d4
TL
350 typename base_type<U>::type,
351 dispatch_and_return<EP, R>
352 >::cast(base_value(u));
353 const R ur = ux.exception()
354 ? static_cast<R>(u)
1e59de90 355 : ux.m_contents.m_r;
92f5a8d4
TL
356 return std::pair<R, R>(tr, ur);
357}
358
359// Note: the following global operators will be found via
360// argument dependent lookup.
1e59de90
TL
361namespace {
362template<template<class...> class F, class T, class U >
363using legal_overload =
364 boost::mp11::mp_and<
365 boost::mp11::mp_or< is_safe<T>, is_safe<U> >,
366 boost::mp11::mp_valid<
367 F,
368 typename base_type<T>::type,
369 typename base_type<U>::type
370 >
371 >;
372} // anon
92f5a8d4
TL
373
374/////////////////////////////////////////////////////////////////
375// addition
376
377template<class T, class U>
378struct addition_result {
379private:
380 using promotion_policy = typename common_promotion_policy<T, U>::type;
381 using result_base_type =
382 typename promotion_policy::template addition_result<T,U>::type;
383
384 // if exception not possible
385 constexpr static result_base_type
386 return_value(const T & t, const U & u, std::false_type){
387 return
388 static_cast<result_base_type>(base_value(t))
389 + static_cast<result_base_type>(base_value(u));
390 }
391
392 // if exception possible
393 using exception_policy = typename common_exception_policy<T, U>::type;
394
395 using r_type = checked_result<result_base_type>;
396
397 constexpr static result_base_type
398 return_value(const T & t, const U & u, std::true_type){
399 const std::pair<result_base_type, result_base_type> r = casting_helper<
400 exception_policy,
401 result_base_type
402 >(t, u);
403
404 const r_type rx = checked_operation<
405 result_base_type,
406 dispatch_and_return<exception_policy, result_base_type>
407 >::add(r.first, r.second);
408
409 return
410 rx.exception()
411 ? r.first + r.second
1e59de90 412 : rx.m_contents.m_r;
92f5a8d4
TL
413 }
414
415 using r_type_interval_t = interval<r_type>;
416
417 constexpr static const r_type_interval_t get_r_type_interval(){
418 constexpr const r_type_interval_t t_interval{
419 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
420 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
421 };
422 constexpr const r_type_interval_t u_interval{
423 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
424 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
425 };
426 return t_interval + u_interval;
427 }
428 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
429
430 constexpr static const interval<result_base_type> return_interval{
431 r_type_interval.l.exception()
432 ? std::numeric_limits<result_base_type>::min()
433 : static_cast<result_base_type>(r_type_interval.l),
434 r_type_interval.u.exception()
435 ? std::numeric_limits<result_base_type>::max()
436 : static_cast<result_base_type>(r_type_interval.u)
437 };
438
439 constexpr static bool exception_possible(){
440 if(r_type_interval.l.exception())
441 return true;
442 if(r_type_interval.u.exception())
443 return true;
444 if(! return_interval.includes(r_type_interval))
445 return true;
446 return false;
447 }
448
449 constexpr static auto rl = return_interval.l;
450 constexpr static auto ru = return_interval.u;
451
452public:
453 using type =
454 safe_base<
455 result_base_type,
456 rl,
457 ru,
458 promotion_policy,
459 exception_policy
460 >;
461
462 constexpr static type return_value(const T & t, const U & u){
463 return type(
464 return_value(
465 t,
466 u,
467 std::integral_constant<bool, exception_possible()>()
468 ),
469 typename type::skip_validation()
470 );
471 }
472};
473
1e59de90
TL
474template<class T, class U> using addition_operator
475 = decltype( std::declval<T const&>() + std::declval<U const&>() );
476
92f5a8d4
TL
477template<class T, class U>
478typename boost::lazy_enable_if_c<
1e59de90 479 legal_overload<addition_operator, T, U>::value,
92f5a8d4
TL
480 addition_result<T, U>
481>::type
1e59de90 482constexpr inline operator+(const T & t, const U & u){
92f5a8d4
TL
483 return addition_result<T, U>::return_value(t, u);
484}
485
486template<class T, class U>
487typename std::enable_if<
1e59de90 488 legal_overload<addition_operator, T, U>::value,
92f5a8d4
TL
489 T
490>::type
1e59de90 491constexpr inline operator+=(T & t, const U & u){
92f5a8d4
TL
492 t = static_cast<T>(t + u);
493 return t;
494}
495
496/////////////////////////////////////////////////////////////////
497// subtraction
498
499template<class T, class U>
500struct subtraction_result {
501private:
502 using promotion_policy = typename common_promotion_policy<T, U>::type;
503 using result_base_type =
504 typename promotion_policy::template subtraction_result<T, U>::type;
505
506 // if exception not possible
507 constexpr static result_base_type
508 return_value(const T & t, const U & u, std::false_type){
509 return
510 static_cast<result_base_type>(base_value(t))
511 - static_cast<result_base_type>(base_value(u));
512 }
513
514 // if exception possible
515 using exception_policy = typename common_exception_policy<T, U>::type;
516
517 using r_type = checked_result<result_base_type>;
518
519 constexpr static result_base_type
520 return_value(const T & t, const U & u, std::true_type){
521 const std::pair<result_base_type, result_base_type> r = casting_helper<
522 exception_policy,
523 result_base_type
524 >(t, u);
525
526 const r_type rx = checked_operation<
527 result_base_type,
528 dispatch_and_return<exception_policy, result_base_type>
529 >::subtract(r.first, r.second);
530
531 return
532 rx.exception()
533 ? r.first + r.second
1e59de90 534 : rx.m_contents.m_r;
92f5a8d4
TL
535 }
536 using r_type_interval_t = interval<r_type>;
537
538 constexpr static const r_type_interval_t get_r_type_interval(){
539 constexpr const r_type_interval_t t_interval{
540 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
541 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
542 };
543
544 constexpr const r_type_interval_t u_interval{
545 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
546 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
547 };
548
549 return t_interval - u_interval;
550 }
20effc67 551 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
92f5a8d4
TL
552
553 constexpr static const interval<result_base_type> return_interval{
554 r_type_interval.l.exception()
555 ? std::numeric_limits<result_base_type>::min()
556 : static_cast<result_base_type>(r_type_interval.l),
557 r_type_interval.u.exception()
558 ? std::numeric_limits<result_base_type>::max()
559 : static_cast<result_base_type>(r_type_interval.u)
560 };
561
562 constexpr static bool exception_possible(){
563 if(r_type_interval.l.exception())
564 return true;
565 if(r_type_interval.u.exception())
566 return true;
567 if(! return_interval.includes(r_type_interval))
568 return true;
569 return false;
570 }
571
572public:
573 constexpr static auto rl = return_interval.l;
574 constexpr static auto ru = return_interval.u;
575
576 using type =
577 safe_base<
578 result_base_type,
579 rl,
580 ru,
581 promotion_policy,
582 exception_policy
583 >;
584
585 constexpr static type return_value(const T & t, const U & u){
586 return type(
587 return_value(
588 t,
589 u,
590 std::integral_constant<bool, exception_possible()>()
591 ),
592 typename type::skip_validation()
593 );
594 }
595};
596
1e59de90
TL
597template<class T, class U> using subtraction_operator
598 = decltype( std::declval<T const&>() - std::declval<U const&>() );
599
92f5a8d4
TL
600template<class T, class U>
601typename boost::lazy_enable_if_c<
1e59de90 602 legal_overload<subtraction_operator, T, U>::value,
92f5a8d4
TL
603 subtraction_result<T, U>
604>::type
1e59de90 605constexpr inline operator-(const T & t, const U & u){
92f5a8d4
TL
606 return subtraction_result<T, U>::return_value(t, u);
607}
608
609template<class T, class U>
610typename std::enable_if<
1e59de90 611 legal_overload<subtraction_operator, T, U>::value,
92f5a8d4
TL
612 T
613>::type
1e59de90 614constexpr inline operator-=(T & t, const U & u){
92f5a8d4
TL
615 t = static_cast<T>(t - u);
616 return t;
617}
618
619/////////////////////////////////////////////////////////////////
620// multiplication
621
622template<class T, class U>
623struct multiplication_result {
624private:
625 using promotion_policy = typename common_promotion_policy<T, U>::type;
626 using result_base_type =
627 typename promotion_policy::template multiplication_result<T, U>::type;
628
629 // if exception not possible
630 constexpr static result_base_type
631 return_value(const T & t, const U & u, std::false_type){
632 return
633 static_cast<result_base_type>(base_value(t))
634 * static_cast<result_base_type>(base_value(u));
635 }
636
637 // if exception possible
638 using exception_policy = typename common_exception_policy<T, U>::type;
639
640 using r_type = checked_result<result_base_type>;
641
642 constexpr static result_base_type
643 return_value(const T & t, const U & u, std::true_type){
644 const std::pair<result_base_type, result_base_type> r = casting_helper<
645 exception_policy,
646 result_base_type
647 >(t, u);
648
649 const r_type rx = checked_operation<
650 result_base_type,
651 dispatch_and_return<exception_policy, result_base_type>
652 >::multiply(r.first, r.second);
653
654 return
655 rx.exception()
656 ? r.first * r.second
1e59de90 657 : rx.m_contents.m_r;
92f5a8d4
TL
658 }
659
660 using r_type_interval_t = interval<r_type>;
661
662 constexpr static r_type_interval_t get_r_type_interval(){
663 constexpr const r_type_interval_t t_interval{
664 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
665 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
666 };
667
668 constexpr const r_type_interval_t u_interval{
669 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
670 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
671 };
672
673 return t_interval * u_interval;
674 }
675
20effc67 676 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
92f5a8d4
TL
677
678 constexpr static const interval<result_base_type> return_interval{
679 r_type_interval.l.exception()
680 ? std::numeric_limits<result_base_type>::min()
681 : static_cast<result_base_type>(r_type_interval.l),
682 r_type_interval.u.exception()
683 ? std::numeric_limits<result_base_type>::max()
684 : static_cast<result_base_type>(r_type_interval.u)
685 };
686
687 constexpr static bool exception_possible(){
688 if(r_type_interval.l.exception())
689 return true;
690 if(r_type_interval.u.exception())
691 return true;
692 if(! return_interval.includes(r_type_interval))
693 return true;
694 return false;
695 }
696
697 constexpr static auto rl = return_interval.l;
698 constexpr static auto ru = return_interval.u;
699
700public:
701 using type =
702 safe_base<
703 result_base_type,
704 rl,
705 ru,
706 promotion_policy,
707 exception_policy
708 >;
709
710 constexpr static type return_value(const T & t, const U & u){
711 return type(
712 return_value(
713 t,
714 u,
715 std::integral_constant<bool, exception_possible()>()
716 ),
717 typename type::skip_validation()
718 );
719 }
720};
721
1e59de90
TL
722template<class T, class U> using multiplication_operator
723 = decltype( std::declval<T const&>() * std::declval<U const&>() );
724
92f5a8d4
TL
725template<class T, class U>
726typename boost::lazy_enable_if_c<
1e59de90 727 legal_overload<multiplication_operator, T, U>::value,
92f5a8d4
TL
728 multiplication_result<T, U>
729>::type
1e59de90 730constexpr inline operator*(const T & t, const U & u){
92f5a8d4
TL
731 return multiplication_result<T, U>::return_value(t, u);
732}
733
734template<class T, class U>
735typename std::enable_if<
1e59de90 736 legal_overload<multiplication_operator, T, U>::value,
92f5a8d4
TL
737 T
738>::type
1e59de90 739constexpr inline operator*=(T & t, const U & u){
92f5a8d4
TL
740 t = static_cast<T>(t * u);
741 return t;
742}
743
744/////////////////////////////////////////////////////////////////
745// division
746
747// key idea here - result will never be larger than T
748template<class T, class U>
749struct division_result {
750private:
751 using promotion_policy = typename common_promotion_policy<T, U>::type;
752 using result_base_type =
753 typename promotion_policy::template division_result<T, U>::type;
754
755 // if exception not possible
756 constexpr static result_base_type
757 return_value(const T & t, const U & u, std::false_type){
758 return
759 static_cast<result_base_type>(base_value(t))
760 / static_cast<result_base_type>(base_value(u));
761 }
762
763 // if exception possible
764 using exception_policy = typename common_exception_policy<T, U>::type;
765
20effc67 766 constexpr static const int bits = std::min(
92f5a8d4
TL
767 std::numeric_limits<std::uintmax_t>::digits,
768 std::max(std::initializer_list<int>{
769 std::numeric_limits<result_base_type>::digits,
770 std::numeric_limits<typename base_type<T>::type>::digits,
771 std::numeric_limits<typename base_type<U>::type>::digits
772 }) + (std::numeric_limits<result_base_type>::is_signed ? 1 : 0)
773 );
774
775 using r_type = checked_result<result_base_type>;
776
777 constexpr static result_base_type
778 return_value(const T & t, const U & u, std::true_type){
779 using temp_base = typename std::conditional<
780 std::numeric_limits<result_base_type>::is_signed,
781 typename boost::int_t<bits>::least,
782 typename boost::uint_t<bits>::least
783 >::type;
784 using t_type = checked_result<temp_base>;
785
786 const std::pair<t_type, t_type> r = casting_helper<
787 exception_policy,
788 temp_base
789 >(t, u);
790
791 const t_type rx = checked_operation<
792 temp_base,
793 dispatch_and_return<exception_policy, temp_base>
794 >::divide(r.first, r.second);
795
796 return
797 rx.exception()
798 ? r.first / r.second
799 : rx;
800 }
801 using r_type_interval_t = interval<r_type>;
802
803 constexpr static r_type_interval_t t_interval(){
804 return r_type_interval_t{
805 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
806 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
807 };
808 };
809
810 constexpr static r_type_interval_t u_interval(){
811 return r_type_interval_t{
812 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
813 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
814 };
815 };
816
817 constexpr static r_type_interval_t get_r_type_interval(){
818 constexpr const r_type_interval_t t = t_interval();
819 constexpr const r_type_interval_t u = u_interval();
820
821 if(u.u < r_type(0) || u.l > r_type(0))
822 return t / u;
823 return utility::minmax(
824 std::initializer_list<r_type> {
825 t.l / u.l,
826 t.l / r_type(-1),
827 t.l / r_type(1),
828 t.l / u.u,
829 t.u / u.l,
830 t.u / r_type(-1),
831 t.u / r_type(1),
832 t.u / u.u,
833 }
834 );
835 }
836
20effc67 837 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
92f5a8d4
TL
838
839 constexpr static const interval<result_base_type> return_interval{
840 r_type_interval.l.exception()
841 ? std::numeric_limits<result_base_type>::min()
842 : static_cast<result_base_type>(r_type_interval.l),
843 r_type_interval.u.exception()
844 ? std::numeric_limits<result_base_type>::max()
845 : static_cast<result_base_type>(r_type_interval.u)
846 };
847
848 constexpr static bool exception_possible(){
849 constexpr const r_type_interval_t ri = get_r_type_interval();
850 constexpr const r_type_interval_t ui = u_interval();
851 return
852 static_cast<bool>(ui.includes(r_type(0)))
853 || ri.l.exception()
854 || ri.u.exception();
855 }
856
857 constexpr static auto rl = return_interval.l;
858 constexpr static auto ru = return_interval.u;
859
860public:
861 using type =
862 safe_base<
863 result_base_type,
864 rl,
865 ru,
866 promotion_policy,
867 exception_policy
868 >;
869
870 constexpr static type return_value(const T & t, const U & u){
871 return type(
872 return_value(
873 t,
874 u,
875 std::integral_constant<bool, exception_possible()>()
876 ),
877 typename type::skip_validation()
878 );
879 }
880};
881
1e59de90
TL
882template<class T, class U> using division_operator
883 = decltype( std::declval<T const&>() / std::declval<U const&>() );
884
92f5a8d4
TL
885template<class T, class U>
886typename boost::lazy_enable_if_c<
1e59de90 887 legal_overload<division_operator, T, U>::value,
92f5a8d4
TL
888 division_result<T, U>
889>::type
1e59de90 890constexpr inline operator/(const T & t, const U & u){
92f5a8d4
TL
891 return division_result<T, U>::return_value(t, u);
892}
893
894template<class T, class U>
895typename std::enable_if<
1e59de90 896 legal_overload<division_operator, T, U>::value,
92f5a8d4
TL
897 T
898>::type
1e59de90 899constexpr inline operator/=(T & t, const U & u){
92f5a8d4
TL
900 t = static_cast<T>(t / u);
901 return t;
902}
903
904/////////////////////////////////////////////////////////////////
905// modulus
906
907template<class T, class U>
908struct modulus_result {
909private:
910 using promotion_policy = typename common_promotion_policy<T, U>::type;
911 using result_base_type = typename promotion_policy::template modulus_result<T, U>::type;
912
913 // if exception not possible
914 constexpr static result_base_type
915 return_value(const T & t, const U & u, std::false_type){
916 return
917 static_cast<result_base_type>(base_value(t))
918 % static_cast<result_base_type>(base_value(u));
919 }
920
921 // if exception possible
922 using exception_policy = typename common_exception_policy<T, U>::type;
923
20effc67 924 constexpr static const int bits = std::min(
92f5a8d4
TL
925 std::numeric_limits<std::uintmax_t>::digits,
926 std::max(std::initializer_list<int>{
927 std::numeric_limits<result_base_type>::digits,
928 std::numeric_limits<typename base_type<T>::type>::digits,
929 std::numeric_limits<typename base_type<U>::type>::digits
930 }) + (std::numeric_limits<result_base_type>::is_signed ? 1 : 0)
931 );
932
933 using r_type = checked_result<result_base_type>;
934
935 constexpr static result_base_type
936 return_value(const T & t, const U & u, std::true_type){
937 using temp_base = typename std::conditional<
938 std::numeric_limits<result_base_type>::is_signed,
939 typename boost::int_t<bits>::least,
940 typename boost::uint_t<bits>::least
941 >::type;
942 using t_type = checked_result<temp_base>;
943
944 const std::pair<t_type, t_type> r = casting_helper<
945 exception_policy,
946 temp_base
947 >(t, u);
948
949 const t_type rx = checked_operation<
950 temp_base,
951 dispatch_and_return<exception_policy, temp_base>
952 >::modulus(r.first, r.second);
953
954 return
955 rx.exception()
956 ? r.first % r.second
957 : rx;
958 }
959
960 using r_type_interval_t = interval<r_type>;
961
962 constexpr static const r_type_interval_t t_interval(){
963 return r_type_interval_t{
964 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
965 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
966 };
967 };
968
969 constexpr static const r_type_interval_t u_interval(){
970 return r_type_interval_t{
971 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
972 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
973 };
974 };
975
976 constexpr static const r_type_interval_t get_r_type_interval(){
977 constexpr const r_type_interval_t t = t_interval();
978 constexpr const r_type_interval_t u = u_interval();
979
980 if(u.u < r_type(0)
981 || u.l > r_type(0))
982 return t % u;
983 return utility::minmax(
984 std::initializer_list<r_type> {
985 t.l % u.l,
986 t.l % r_type(-1),
987 t.l % r_type(1),
988 t.l % u.u,
989 t.u % u.l,
990 t.u % r_type(-1),
991 t.u % r_type(1),
992 t.u % u.u,
993 }
994 );
995 }
996
20effc67 997 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
92f5a8d4
TL
998
999 constexpr static const interval<result_base_type> return_interval{
1000 r_type_interval.l.exception()
1001 ? std::numeric_limits<result_base_type>::min()
1002 : static_cast<result_base_type>(r_type_interval.l),
1003 r_type_interval.u.exception()
1004 ? std::numeric_limits<result_base_type>::max()
1005 : static_cast<result_base_type>(r_type_interval.u)
1006 };
1007
1008 constexpr static bool exception_possible(){
1009 constexpr const r_type_interval_t ri = get_r_type_interval();
1010 constexpr const r_type_interval_t ui = u_interval();
1011 return
1012 static_cast<bool>(ui.includes(r_type(0)))
1013 || ri.l.exception()
1014 || ri.u.exception();
1015 }
1016
1017 constexpr static auto rl = return_interval.l;
1018 constexpr static auto ru = return_interval.u;
1019
1020public:
1021 using type =
1022 safe_base<
1023 result_base_type,
1024 rl,
1025 ru,
1026 promotion_policy,
1027 exception_policy
1028 >;
1029
1030 constexpr static type return_value(const T & t, const U & u){
1031 return type(
1032 return_value(
1033 t,
1034 u,
1035 std::integral_constant<bool, exception_possible()>()
1036 ),
1037 typename type::skip_validation()
1038 );
1039 }
1040};
1041
1e59de90
TL
1042template<class T, class U> using modulus_operator
1043 = decltype( std::declval<T const&>() % std::declval<U const&>() );
1044
92f5a8d4
TL
1045template<class T, class U>
1046typename boost::lazy_enable_if_c<
1e59de90 1047 legal_overload<modulus_operator, T, U>::value,
92f5a8d4
TL
1048 modulus_result<T, U>
1049>::type
1e59de90 1050constexpr inline operator%(const T & t, const U & u){
92f5a8d4
TL
1051 // see https://en.wikipedia.org/wiki/Modulo_operation
1052 return modulus_result<T, U>::return_value(t, u);
1053}
1054
1055template<class T, class U>
1056typename std::enable_if<
1e59de90 1057 legal_overload<modulus_operator, T, U>::value,
92f5a8d4
TL
1058 T
1059>::type
1e59de90 1060constexpr inline operator%=(T & t, const U & u){
92f5a8d4
TL
1061 t = static_cast<T>(t % u);
1062 return t;
1063}
1064
1065/////////////////////////////////////////////////////////////////
1066// comparison
1067
1068// less than
1069
1070template<class T, class U>
1071struct less_than_result {
1072private:
1073 using promotion_policy = typename common_promotion_policy<T, U>::type;
1074
1075 using result_base_type =
1076 typename promotion_policy::template comparison_result<T, U>::type;
1077
1078 // if exception not possible
1079 constexpr static bool
1080 return_value(const T & t, const U & u, std::false_type){
1081 return
1082 static_cast<result_base_type>(base_value(t))
1083 < static_cast<result_base_type>(base_value(u));
1084 }
1085
1086 using exception_policy = typename common_exception_policy<T, U>::type;
1087
1088 using r_type = checked_result<result_base_type>;
1089
1090 // if exception possible
1091 constexpr static bool
1092 return_value(const T & t, const U & u, std::true_type){
1093 const std::pair<result_base_type, result_base_type> r = casting_helper<
1094 exception_policy,
1095 result_base_type
1096 >(t, u);
1097
1098 return safe_compare::less_than(r.first, r.second);
1099 }
1100
1101 using r_type_interval_t = interval<r_type>;
1102
1103 constexpr static bool interval_open(const r_type_interval_t & t){
1104 return t.l.exception() || t.u.exception();
1105 }
1106
1107public:
1108 constexpr static bool
1109 return_value(const T & t, const U & u){
1110 constexpr const r_type_interval_t t_interval{
1111 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1112 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1113 };
1114 constexpr const r_type_interval_t u_interval{
1115 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1116 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1117 };
1118
1119 if(t_interval < u_interval)
1120 return true;
1121 if(t_interval > u_interval)
1122 return false;
1123
1124 constexpr bool exception_possible
1125 = interval_open(t_interval) || interval_open(u_interval);
1126
1127 return return_value(
1128 t,
1129 u,
1130 std::integral_constant<bool, exception_possible>()
1131 );
1132 }
1133};
1134
1e59de90
TL
1135template<class T, class U> using less_than_operator
1136 = decltype( std::declval<T const&>() < std::declval<U const&>() );
1137template<class T, class U> using greater_than_operator
1138 = decltype( std::declval<T const&>() > std::declval<U const&>() );
1139template<class T, class U> using less_than_or_equal_operator
1140 = decltype( std::declval<T const&>() <= std::declval<U const&>() );
1141template<class T, class U> using greater_than_or_equal_operator
1142 = decltype( std::declval<T const&>() >= std::declval<U const&>() );
1143
92f5a8d4
TL
1144template<class T, class U>
1145typename std::enable_if<
1e59de90 1146 legal_overload<less_than_operator, T, U>::value,
92f5a8d4
TL
1147 bool
1148>::type
1e59de90 1149constexpr inline operator<(const T & lhs, const U & rhs) {
92f5a8d4
TL
1150 return less_than_result<T, U>::return_value(lhs, rhs);
1151}
1152
1153template<class T, class U>
1154typename std::enable_if<
1e59de90 1155 legal_overload<greater_than_operator, T, U>::value,
92f5a8d4
TL
1156 bool
1157>::type
1e59de90 1158constexpr inline operator>(const T & lhs, const U & rhs) {
92f5a8d4
TL
1159 return rhs < lhs;
1160}
1161
1162template<class T, class U>
1163typename std::enable_if<
1e59de90 1164 legal_overload<greater_than_or_equal_operator, T, U>::value,
92f5a8d4
TL
1165 bool
1166>::type
1e59de90 1167constexpr inline operator>=(const T & lhs, const U & rhs) {
92f5a8d4
TL
1168 return ! ( lhs < rhs );
1169}
1170
1171template<class T, class U>
1172typename std::enable_if<
1e59de90 1173 legal_overload<less_than_or_equal_operator, T, U>::value,
92f5a8d4
TL
1174 bool
1175>::type
1e59de90 1176constexpr inline operator<=(const T & lhs, const U & rhs) {
92f5a8d4
TL
1177 return ! ( lhs > rhs );
1178}
1179
1180// equal
1181
1182template<class T, class U>
1183struct equal_result {
1184private:
1185 using promotion_policy = typename common_promotion_policy<T, U>::type;
1186
1187 using result_base_type =
1188 typename promotion_policy::template comparison_result<T, U>::type;
1189
1190 // if exception not possible
1191 constexpr static bool
1192 return_value(const T & t, const U & u, std::false_type){
1193 return
1194 static_cast<result_base_type>(base_value(t))
1195 == static_cast<result_base_type>(base_value(u));
1196 }
1197
1198 using exception_policy = typename common_exception_policy<T, U>::type;
1199
1200 using r_type = checked_result<result_base_type>;
1201
1202 // exception possible
1203 constexpr static bool
1204 return_value(const T & t, const U & u, std::true_type){
1205 const std::pair<result_base_type, result_base_type> r = casting_helper<
1206 exception_policy,
1207 result_base_type
1208 >(t, u);
1209
1210 return safe_compare::equal(r.first, r.second);
1211 }
1212
1213 using r_type_interval = interval<r_type>;
1214
1215 constexpr static bool interval_open(const r_type_interval & t){
1216 return t.l.exception() || t.u.exception();
1217 }
1218
1219public:
1220 constexpr static bool
1221 return_value(const T & t, const U & u){
1222 constexpr const r_type_interval t_interval{
1223 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1224 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1225 };
1226
1227 constexpr const r_type_interval u_interval{
1228 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1229 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1230 };
1231
1232 if(! intersect(t_interval, u_interval))
1233 return false;
1234
1235 constexpr bool exception_possible
1236 = interval_open(t_interval) || interval_open(u_interval);
1237
1238 return return_value(
1239 t,
1240 u,
1241 std::integral_constant<bool, exception_possible>()
1242 );
1243 }
1244};
1245
1e59de90
TL
1246template<class T, class U> using equal_to_operator
1247 = decltype( std::declval<T const&>() == std::declval<U const&>() );
1248template<class T, class U> using not_equal_to_operator
1249 = decltype( std::declval<T const&>() != std::declval<U const&>() );
1250
92f5a8d4
TL
1251template<class T, class U>
1252typename std::enable_if<
1e59de90 1253 legal_overload<equal_to_operator, T, U>::value,
92f5a8d4
TL
1254 bool
1255>::type
1e59de90 1256constexpr inline operator==(const T & lhs, const U & rhs) {
92f5a8d4
TL
1257 return equal_result<T, U>::return_value(lhs, rhs);
1258}
1259
1260template<class T, class U>
1261typename std::enable_if<
1e59de90 1262 legal_overload<not_equal_to_operator, T, U>::value,
92f5a8d4
TL
1263 bool
1264>::type
1e59de90 1265constexpr inline operator!=(const T & lhs, const U & rhs) {
92f5a8d4
TL
1266 return ! (lhs == rhs);
1267}
1268
20effc67
TL
1269/////////////////////////////////////////////////////////////////////////
1270// The following operators only make sense when applied to integet types
1271
1272/////////////////////////////////////////////////////////////////////////
92f5a8d4
TL
1273// shift operators
1274
1275// left shift
1276template<class T, class U>
1277struct left_shift_result {
1278private:
1279 using promotion_policy = typename common_promotion_policy<T, U>::type;
1280 using result_base_type =
1281 typename promotion_policy::template left_shift_result<T, U>::type;
1282
1283 // if exception not possible
1284 constexpr static result_base_type
1285 return_value(const T & t, const U & u, std::false_type){
1286 return
1287 static_cast<result_base_type>(base_value(t))
1288 << static_cast<result_base_type>(base_value(u));
1289 }
1290
1291 // exception possible
1292 using exception_policy = typename common_exception_policy<T, U>::type;
1293
1294 using r_type = checked_result<result_base_type>;
1295
1296 constexpr static result_base_type
1297 return_value(const T & t, const U & u, std::true_type){
1298 const std::pair<result_base_type, result_base_type> r = casting_helper<
1299 exception_policy,
1300 result_base_type
1301 >(t, u);
1302
1303 const r_type rx = checked_operation<
1304 result_base_type,
1305 dispatch_and_return<exception_policy, result_base_type>
1306 >::left_shift(r.first, r.second);
1307
1308 return
1309 rx.exception()
1310 ? r.first << r.second
1e59de90 1311 : rx.m_contents.m_r;
92f5a8d4
TL
1312 }
1313
1314 using r_type_interval_t = interval<r_type>;
1315
1316 constexpr static r_type_interval_t get_r_type_interval(){
1317 constexpr const r_type_interval_t t_interval{
1318 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1319 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1320 };
1321
1322 constexpr const r_type_interval_t u_interval{
1323 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1324 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1325 };
1326 return (t_interval << u_interval);
1327 }
1328
20effc67 1329 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
92f5a8d4
TL
1330
1331 constexpr static const interval<result_base_type> return_interval{
1332 r_type_interval.l.exception()
1333 ? std::numeric_limits<result_base_type>::min()
1334 : static_cast<result_base_type>(r_type_interval.l),
1335 r_type_interval.u.exception()
1336 ? std::numeric_limits<result_base_type>::max()
1337 : static_cast<result_base_type>(r_type_interval.u)
1338 };
1339
1340 constexpr static bool exception_possible(){
1341 if(r_type_interval.l.exception())
1342 return true;
1343 if(r_type_interval.u.exception())
1344 return true;
1345 if(! return_interval.includes(r_type_interval))
1346 return true;
1347 return false;
1348 }
1349
20effc67
TL
1350 constexpr static const auto rl = return_interval.l;
1351 constexpr static const auto ru = return_interval.u;
92f5a8d4
TL
1352
1353public:
1354 using type =
1355 safe_base<
1356 result_base_type,
1357 rl,
1358 ru,
1359 promotion_policy,
1360 exception_policy
1361 >;
1362
1363 constexpr static type return_value(const T & t, const U & u){
1364 return type(
1365 return_value(
1366 t,
1367 u,
1368 std::integral_constant<bool, exception_possible()>()
1369 ),
1370 typename type::skip_validation()
1371 );
1372 }
1373};
1374
1e59de90
TL
1375template<class T, class U> using left_shift_operator
1376 = decltype( std::declval<T const&>() << std::declval<U const&>() );
1377
92f5a8d4
TL
1378template<class T, class U>
1379typename boost::lazy_enable_if_c<
1e59de90
TL
1380 // exclude usage of << for file input here
1381 boost::safe_numerics::Numeric<T>()
1382 && legal_overload<left_shift_operator, T, U>::value,
92f5a8d4
TL
1383 left_shift_result<T, U>
1384>::type
1e59de90 1385constexpr inline operator<<(const T & t, const U & u){
92f5a8d4
TL
1386 // INT13-CPP
1387 // C++ standards document N4618 & 5.8.2
92f5a8d4
TL
1388 return left_shift_result<T, U>::return_value(t, u);
1389}
1390
1391template<class T, class U>
1392typename std::enable_if<
1e59de90
TL
1393 // exclude usage of << for file output here
1394 boost::safe_numerics::Numeric<T>()
1395 && legal_overload<left_shift_operator, T, U>::value,
92f5a8d4
TL
1396 T
1397>::type
1e59de90 1398constexpr inline operator<<=(T & t, const U & u){
92f5a8d4
TL
1399 t = static_cast<T>(t << u);
1400 return t;
1401}
1402
1e59de90
TL
1403template<class T, class CharT, class Traits> using stream_output_operator
1404 = decltype( std::declval<std::basic_ostream<CharT, Traits> &>() >> std::declval<T const&>() );
1405
1406template<class T, class CharT, class Traits>
1407typename boost::lazy_enable_if_c<
1408 boost::mp11::mp_valid< stream_output_operator, T, CharT, Traits>::value,
1409 std::basic_ostream<CharT, Traits> &
1410>::type
1411constexpr inline operator>>(
1412 std::basic_ostream<CharT, Traits> & os,
1413 const T & t
1414){
1415 // INT13-CPP
1416 // C++ standards document N4618 & 5.8.2
1417 t.output(os);
1418 return os;
1419}
1420
1421/////////////////////////////////////////////////////////////////
92f5a8d4
TL
1422// right shift
1423template<class T, class U>
1424struct right_shift_result {
1425 using promotion_policy = typename common_promotion_policy<T, U>::type;
1426 using result_base_type =
1427 typename promotion_policy::template right_shift_result<T, U>::type;
1428
1429 // if exception not possible
1430 constexpr static result_base_type
1431 return_value(const T & t, const U & u, std::false_type){
1432 return
1433 static_cast<result_base_type>(base_value(t))
1434 >> static_cast<result_base_type>(base_value(u));
1435 }
1436
1437 // exception possible
1438 using exception_policy = typename common_exception_policy<T, U>::type;
1439
1440 using r_type = checked_result<result_base_type>;
1441
1442 constexpr static result_base_type
1443 return_value(const T & t, const U & u, std::true_type){
1444 const std::pair<result_base_type, result_base_type> r = casting_helper<
1445 exception_policy,
1446 result_base_type
1447 >(t, u);
1448
1449 const r_type rx = checked_operation<
1450 result_base_type,
1451 dispatch_and_return<exception_policy, result_base_type>
1452 >::right_shift(r.first, r.second);
1453
1454 return
1455 rx.exception()
1456 ? r.first >> r.second
1e59de90 1457 : rx.m_contents.m_r;
92f5a8d4
TL
1458 }
1459
1460 using r_type_interval_t = interval<r_type>;
1461
1462 constexpr static r_type_interval_t t_interval(){
1463 return r_type_interval_t(
1464 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::min())),
1465 checked::cast<result_base_type>(base_value(std::numeric_limits<T>::max()))
1466 );
1467 };
1468
1469 constexpr static r_type_interval_t u_interval(){
1470 return r_type_interval_t(
1471 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::min())),
1472 checked::cast<result_base_type>(base_value(std::numeric_limits<U>::max()))
1473 );
1474 }
1475 constexpr static r_type_interval_t get_r_type_interval(){;
1476 return (t_interval() >> u_interval());
1477 }
1478
20effc67 1479 constexpr static const r_type_interval_t r_type_interval = get_r_type_interval();
92f5a8d4
TL
1480
1481 constexpr static const interval<result_base_type> return_interval{
1482 r_type_interval.l.exception()
1483 ? std::numeric_limits<result_base_type>::min()
1484 : static_cast<result_base_type>(r_type_interval.l),
1485 r_type_interval.u.exception()
1486 ? std::numeric_limits<result_base_type>::max()
1487 : static_cast<result_base_type>(r_type_interval.u)
1488 };
1489
1490 constexpr static bool exception_possible(){
1491 constexpr const r_type_interval_t ri = r_type_interval;
1492 constexpr const r_type_interval_t ti = t_interval();
1493 constexpr const r_type_interval_t ui = u_interval();
1494 return static_cast<bool>(
1495 // note undesirable coupling with checked::shift right here !
1496 ui.u > checked_result<result_base_type>(
1497 std::numeric_limits<result_base_type>::digits
1498 )
1499 || ti.l < checked_result<result_base_type>(0)
1500 || ui.l < checked_result<result_base_type>(0)
1501 || ri.l.exception()
1502 || ri.u.exception()
1503 );
1504 }
1505
1506 constexpr static auto rl = return_interval.l;
1507 constexpr static auto ru = return_interval.u;
1508
1509public:
1510 using type =
1511 safe_base<
1512 result_base_type,
1513 rl,
1514 ru,
1515 promotion_policy,
1516 exception_policy
1517 >;
1518
1519 constexpr static type return_value(const T & t, const U & u){
1520 return type(
1521 return_value(
1522 t,
1523 u,
1524 std::integral_constant<bool, exception_possible()>()
1525 ),
1526 typename type::skip_validation()
1527 );
1528 }
1529};
1530
1e59de90
TL
1531template<class T, class U> using right_shift_operator
1532 = decltype( std::declval<T const&>() >> std::declval<U const&>() );
1533
92f5a8d4
TL
1534template<class T, class U>
1535typename boost::lazy_enable_if_c<
1e59de90
TL
1536 // exclude usage of >> for file input here
1537 boost::safe_numerics::Numeric<T>()
1538 && legal_overload<right_shift_operator, T, U>::value,
92f5a8d4
TL
1539 right_shift_result<T, U>
1540>::type
1e59de90 1541constexpr inline operator>>(const T & t, const U & u){
92f5a8d4 1542 // INT13-CPP
1e59de90 1543 // C++ standards document N4618 & 5.8.2
92f5a8d4
TL
1544 return right_shift_result<T, U>::return_value(t, u);
1545}
1546
1547template<class T, class U>
1548typename std::enable_if<
1e59de90
TL
1549 // exclude usage of << for file output here
1550 boost::safe_numerics::Numeric<T>()
1551 && legal_overload<right_shift_operator, T, U>::value,
92f5a8d4
TL
1552 T
1553>::type
1e59de90 1554constexpr inline operator>>=(T & t, const U & u){
92f5a8d4
TL
1555 t = static_cast<T>(t >> u);
1556 return t;
1557}
1558
1e59de90
TL
1559template<class T, class CharT, class Traits> using stream_input_operator
1560 = decltype( std::declval<std::basic_istream<CharT, Traits> &>() >> std::declval<T const&>() );
1561
1562template<class T, class CharT, class Traits>
1563typename boost::lazy_enable_if_c<
1564 boost::mp11::mp_valid< stream_input_operator, T, CharT, Traits>::value,
1565 std::basic_istream<CharT, Traits> &
1566>::type
1567constexpr inline operator>>(
1568 std::basic_istream<CharT, Traits> & is,
1569 const T & t
1570){
1571 // INT13-CPP
1572 // C++ standards document N4618 & 5.8.2
1573 t.input(is);
1574 return is;
1575}
1576
92f5a8d4
TL
1577/////////////////////////////////////////////////////////////////
1578// bitwise operators
1579
1580// operator |
1581template<class T, class U>
1582struct bitwise_or_result {
1583private:
1584 using promotion_policy = typename common_promotion_policy<T, U>::type;
1585 using result_base_type =
1586 typename promotion_policy::template bitwise_or_result<T, U>::type;
1587
1588 // according to the C++ standard, the bitwise operators are executed as if
1589 // the operands are consider a logical array of bits. That is, there is no
1590 // sense that these are signed numbers.
1591
1592 using r_type = typename std::make_unsigned<result_base_type>::type;
1593 using r_type_interval_t = interval<r_type>;
92f5a8d4
TL
1594 using exception_policy = typename common_exception_policy<T, U>::type;
1595
1596public:
1597 // lazy_enable_if_c depends on this
1598 using type = safe_base<
1599 result_base_type,
1600 //r_interval.l,
1601 r_type(0),
1602 //r_interval.u,
1603 utility::round_out(
1604 std::max(
1605 static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
1606 static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
1607 )
1608 ),
1609 promotion_policy,
1610 exception_policy
1611 >;
1612
1613 constexpr static type return_value(const T & t, const U & u){
1614 return type(
1615 static_cast<result_base_type>(base_value(t))
1616 | static_cast<result_base_type>(base_value(u)),
1617 typename type::skip_validation()
1618 );
1619 }
1620};
1621
1e59de90
TL
1622template<class T, class U> using bitwise_or_operator
1623 = decltype( std::declval<T const&>() | std::declval<U const&>() );
1624
92f5a8d4
TL
1625template<class T, class U>
1626typename boost::lazy_enable_if_c<
1e59de90 1627 legal_overload<bitwise_or_operator, T, U>::value,
92f5a8d4
TL
1628 bitwise_or_result<T, U>
1629>::type
1e59de90 1630constexpr inline operator|(const T & t, const U & u){
92f5a8d4
TL
1631 return bitwise_or_result<T, U>::return_value(t, u);
1632}
1633
1634template<class T, class U>
1635typename std::enable_if<
1e59de90 1636 legal_overload<bitwise_or_operator, T, U>::value,
92f5a8d4
TL
1637 T
1638>::type
1e59de90 1639constexpr inline operator|=(T & t, const U & u){
92f5a8d4
TL
1640 t = static_cast<T>(t | u);
1641 return t;
1642}
1643
1644// operator &
1645template<class T, class U>
1646struct bitwise_and_result {
1647private:
1648 using promotion_policy = typename common_promotion_policy<T, U>::type;
1649 using result_base_type =
1650 typename promotion_policy::template bitwise_and_result<T, U>::type;
1651
1652 // according to the C++ standard, the bitwise operators are executed as if
1653 // the operands are consider a logical array of bits. That is, there is no
1654 // sense that these are signed numbers.
1655
1656 using r_type = typename std::make_unsigned<result_base_type>::type;
1657 using r_type_interval_t = interval<r_type>;
92f5a8d4
TL
1658 using exception_policy = typename common_exception_policy<T, U>::type;
1659
1660public:
1661 // lazy_enable_if_c depends on this
1662 using type = safe_base<
1663 result_base_type,
1664 //r_interval.l,
1665 r_type(0),
1666 //r_interval.u,
1667 utility::round_out(
1668 std::min(
1669 static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
1670 static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
1671 )
1672 ),
1673 promotion_policy,
1674 exception_policy
1675 >;
1676
1677 constexpr static type return_value(const T & t, const U & u){
1678 return type(
1679 static_cast<result_base_type>(base_value(t))
1680 & static_cast<result_base_type>(base_value(u)),
1681 typename type::skip_validation()
1682 );
1683 }
1684};
1e59de90
TL
1685
1686template<class T, class U> using bitwise_and_operator
1687 = decltype( std::declval<T const&>() & std::declval<U const&>() );
1688
92f5a8d4
TL
1689template<class T, class U>
1690typename boost::lazy_enable_if_c<
1e59de90 1691 legal_overload<bitwise_and_operator, T, U>::value,
92f5a8d4
TL
1692 bitwise_and_result<T, U>
1693>::type
1e59de90 1694constexpr inline operator&(const T & t, const U & u){
92f5a8d4
TL
1695 return bitwise_and_result<T, U>::return_value(t, u);
1696}
1697
1698template<class T, class U>
1699typename std::enable_if<
1e59de90 1700 legal_overload<bitwise_and_operator, T, U>::value,
92f5a8d4
TL
1701 T
1702>::type
1e59de90 1703constexpr inline operator&=(T & t, const U & u){
92f5a8d4
TL
1704 t = static_cast<T>(t & u);
1705 return t;
1706}
1707
1708// operator ^
1709template<class T, class U>
1710struct bitwise_xor_result {
1711 using promotion_policy = typename common_promotion_policy<T, U>::type;
1712 using result_base_type =
1713 typename promotion_policy::template bitwise_xor_result<T, U>::type;
1714
1715 // according to the C++ standard, the bitwise operators are executed as if
1716 // the operands are consider a logical array of bits. That is, there is no
1717 // sense that these are signed numbers.
1718
1719 using r_type = typename std::make_unsigned<result_base_type>::type;
1720 using r_type_interval_t = interval<r_type>;
92f5a8d4
TL
1721 using exception_policy = typename common_exception_policy<T, U>::type;
1722
1723public:
1724 // lazy_enable_if_c depends on this
1725 using type = safe_base<
1726 result_base_type,
1727 //r_interval.l,
1728 r_type(0),
1729 //r_interval.u,
1730 utility::round_out(
1731 std::max(
1732 static_cast<r_type>(base_value(std::numeric_limits<T>::max())),
1733 static_cast<r_type>(base_value(std::numeric_limits<U>::max()))
1734 )
1735 ),
1736 promotion_policy,
1737 exception_policy
1738 >;
1739
1740 constexpr static type return_value(const T & t, const U & u){
1741 return type(
1742 static_cast<result_base_type>(base_value(t))
1743 ^ static_cast<result_base_type>(base_value(u)),
1744 typename type::skip_validation()
1745 );
1746 }
1747};
1748
1e59de90
TL
1749template<class T, class U> using bitwise_xor_operator
1750 = decltype( std::declval<T const&>() ^ std::declval<U const&>() );
1751
92f5a8d4
TL
1752template<class T, class U>
1753typename boost::lazy_enable_if_c<
1e59de90 1754 legal_overload<bitwise_xor_operator, T, U>::value,
92f5a8d4
TL
1755 bitwise_xor_result<T, U>
1756>::type
1e59de90 1757constexpr inline operator^(const T & t, const U & u){
92f5a8d4
TL
1758 return bitwise_xor_result<T, U>::return_value(t, u);
1759}
1760
1761template<class T, class U>
1762typename std::enable_if<
1e59de90 1763 legal_overload<bitwise_xor_operator, T, U>::value,
92f5a8d4
TL
1764 T
1765>::type
1e59de90 1766constexpr inline operator^=(T & t, const U & u){
92f5a8d4
TL
1767 t = static_cast<T>(t ^ u);
1768 return t;
1769}
1770
1771/////////////////////////////////////////////////////////////////
1772// stream helpers
1773
1774template<
1775 class T,
1776 T Min,
1777 T Max,
1778 class P, // promotion polic
1779 class E // exception policy
1780>
1781template<
1782 class CharT,
1783 class Traits
1784>
1e59de90 1785inline void safe_base<T, Min, Max, P, E>::output(
92f5a8d4
TL
1786 std::basic_ostream<CharT, Traits> & os
1787) const {
1788 os << (
1789 (std::is_same<T, signed char>::value
1790 || std::is_same<T, unsigned char>::value
1791 || std::is_same<T, wchar_t>::value
1792 ) ?
1793 static_cast<int>(m_t)
1794 :
1795 m_t
1796 );
1797}
1798
1799template<
1800 class T,
1801 T Min,
1802 T Max,
1803 class P, // promotion polic
1804 class E // exception policy
1805>
1806template<
1807 class CharT,
1808 class Traits
1809>
1e59de90 1810inline void safe_base<T, Min, Max, P, E>::input(
92f5a8d4
TL
1811 std::basic_istream<CharT, Traits> & is
1812){
1813 if(std::is_same<T, signed char>::value
1814 || std::is_same<T, unsigned char>::value
1815 || std::is_same<T, wchar_t>::value
1816 ){
1817 int x;
1818 is >> x;
1819 m_t = validated_cast(x);
1820 }
1821 else{
20effc67
TL
1822 if(std::is_unsigned<T>::value){
1823 // reading a negative number into an unsigned variable cannot result in
1824 // a correct result. But, C++ reads the absolute value, multiplies
1825 // it by -1 and stores the resulting value. This is crazy - but there
1826 // it is! Oh, and it doesn't set the failbit. We fix this behavior here
1827 is >> std::ws;
1828 int x = is.peek();
1829 // if the input string starts with a '-', we know its an error
1830 if(x == '-'){
1831 // set fail bit
1832 is.setstate(std::ios_base::failbit);
1833 }
1834 }
92f5a8d4 1835 is >> m_t;
20effc67
TL
1836 if(is.fail()){
1837 boost::safe_numerics::dispatch<
1838 E,
1839 boost::safe_numerics::safe_numerics_error::domain_error
1840 >(
1841 "error in file input"
1842 );
1843 }
1844 else
1845 validated_cast(m_t);
92f5a8d4
TL
1846 }
1847}
1848
1849} // safe_numerics
1850} // boost
1851
1852#endif // BOOST_NUMERIC_SAFE_BASE_OPERATIONS_HPP