Clingo
Loading...
Searching...
No Matches
checked_math.hh
1#pragma once
2
3// The code below took inspiration from <https://github.com/dcleblanc/SafeInt>
4// and the paper "Division and Modulus for Computer Scientists". In principle,
5// one could also just use the SafeInt class as a third party library.
6// Currently, it is not included because it is a bit heavy (7k loc) and we need
7// only a few functions here. I still have to make up my mind here.
8
9#include <concepts>
10#include <cstdint>
11#include <limits>
12#include <optional>
13#include <type_traits>
14#include <utility>
15
16namespace CppClingo::Util {
17
20
21// Casting
22
24template <std::signed_integral T, std::signed_integral S> auto check_cast(S in) -> bool {
25 return std::in_range<T>(std::move(in));
26}
27
29template <std::integral T, std::integral S> auto safe_cast(S in) -> T {
30 if (std::in_range<T, S>(in)) {
31 return static_cast<T>(std::move(in));
32 }
33 throw std::range_error("invalid cast");
34}
35
36// Addition
37
41template <std::signed_integral S> auto check_add(S a, S b) -> std::optional<S> {
42#ifdef __GNUC__
43 if (S c; !__builtin_add_overflow(a, b, &c)) {
44 return c;
45 }
46 return std::nullopt;
47#else
48 if constexpr (std::is_same_v<S, int32_t>) {
49 int64_t tmp = static_cast<int64_t>(a) + b;
50 if (check_cast<int32_t>(tmp)) {
51 return static_cast<int32_t>(tmp);
52 }
53 return std::nullopt;
54 } else {
55 using U = std::make_unsigned_t<S>;
56 S tmp = static_cast<S>(static_cast<U>(a) + static_cast<U>(b));
57 if ((a >= 0 && b >= 0 && tmp < a) || (a < 0 && b < 0 && tmp > a)) {
58 return std::nullopt;
59 }
60 return tmp;
61 }
62#endif
63}
64
65// Subtraction
66
70template <std::signed_integral S> auto check_sub(S a, S b) -> std::optional<S> {
71#ifdef __GNUC__
72 if (S c; !__builtin_sub_overflow(a, b, &c)) {
73 return c;
74 }
75 return std::nullopt;
76#else
77 if constexpr (std::is_same_v<S, int32_t>) {
78 int64_t tmp = static_cast<int64_t>(a) - b;
79 if (check_cast<int32_t>(tmp)) {
80 return static_cast<int32_t>(tmp);
81 }
82 return std::nullopt;
83 } else {
84 using U = std::make_unsigned_t<S>;
85 S tmp = static_cast<S>(static_cast<U>(a) - static_cast<U>(b));
86 if ((a >= 0 && b < 0 && tmp < a) || (b >= 0 && tmp > a)) {
87 return std::nullopt;
88 }
89 return tmp;
90 }
91#endif
92}
93
94// Unary Minus
95
97template <std::signed_integral S> auto check_neg(S a) -> std::optional<S> {
98 if (a == std::numeric_limits<S>::min()) {
99 return std::nullopt;
100 }
101 return -a;
102}
103
104// Absolute
105
107template <std::signed_integral S> auto check_abs(S a) -> std::optional<S> {
108 if (a == std::numeric_limits<S>::min()) {
109 return std::nullopt;
110 }
111 return a < 0 ? -a : a;
112}
113
114// Multiplication
115
119template <std::signed_integral S> auto check_mul(S a, S b) -> std::optional<S> {
120#ifdef __GNUC__
121 if (S c; !__builtin_mul_overflow(a, b, &c)) {
122 return c;
123 }
124 return std::nullopt;
125#else
126 if constexpr (std::is_same_v<S, int32_t>) {
127 int64_t tmp = static_cast<int64_t>(a) * b;
128 if (check_cast<int32_t>(tmp)) {
129 return static_cast<int32_t>(tmp);
130 }
131 return std::nullopt;
132 } else {
133 if (a > 0 && b > 0 && a > std::numeric_limits<S>::max() / b) {
134 return std::nullopt;
135 }
136 if (a > 0 && b < 0 && b < std::numeric_limits<S>::min() / a) {
137 return std::nullopt;
138 }
139 if (a < 0 && b > 0 && a < std::numeric_limits<S>::min() / b) {
140 return std::nullopt;
141 }
142 if (a < 0 && b < 0 && b < std::numeric_limits<S>::max() / a) {
143 return std::nullopt;
144 }
145 return a * b;
146 }
147#endif
148}
149
150// Division
151
153template <std::signed_integral S> auto check_div(S a, S b) -> std::optional<S> {
154 if (b == 0 || (b == -1 && a == std::numeric_limits<S>::min())) {
155 return std::nullopt;
156 }
157 int d = a / b;
158 int r = a % b;
159 // The remainder is zero if |b| is 1, so we can always subtract 1.
160 if ((r > 0 && b < 0) || (r < 0 && b > 0)) {
161 --d;
162 }
163 return d;
164}
165
166// Modulo
167
169template <std::signed_integral S> auto check_mod(S a, S b) -> std::optional<S> {
170 if (b == 0) {
171 return std::nullopt;
172 }
173 if (b == -1) {
174 return 0;
175 }
176 int r = a % b;
177 // This never overflows.
178 if ((r > 0 && b < 0) || (r < 0 && b > 0)) {
179 r += b;
180 }
181 return r;
182}
183
184// Power
185
189template <std::signed_integral S> auto check_pow(S a, S b) -> std::optional<S> {
190 if (b < 0) {
191 return std::nullopt;
192 }
193 int r = 1;
194 while (b > 0) {
195 if ((b & 1) != 0) {
196 auto tmp = check_mul(r, a);
197 if (!tmp.has_value()) {
198 return std::nullopt;
199 }
200 r = tmp.value();
201 }
202 b >>= 1;
203 if (b > 0) {
204 auto tmp = check_mul(a, a);
205 if (!tmp.has_value()) {
206 return std::nullopt;
207 }
208 a = tmp.value();
209 }
210 }
211 return r;
212}
213
215
216} // namespace CppClingo::Util
auto check_neg(S a) -> std::optional< S >
Negate an integer checking overflows.
Definition checked_math.hh:97
auto check_pow(S a, S b) -> std::optional< S >
Power of the given integers checking overflows.
Definition checked_math.hh:189
auto check_mul(S a, S b) -> std::optional< S >
Multiply two integers checking overflows.
Definition checked_math.hh:119
auto safe_cast(S in) -> T
Cast S to T if possible.
Definition checked_math.hh:29
auto check_mod(S a, S b) -> std::optional< S >
Modulo of two integers checking overflows (truncating toward negative infinity).
Definition checked_math.hh:169
auto check_sub(S a, S b) -> std::optional< S >
Subtract two integers checking overflows.
Definition checked_math.hh:70
auto check_abs(S a) -> std::optional< S >
The absolute of an integer checking overflows.
Definition checked_math.hh:107
auto check_div(S a, S b) -> std::optional< S >
Divide two integers checking overflows (truncating toward negative infinity).
Definition checked_math.hh:153
auto check_add(S a, S b) -> std::optional< S >
Add two integers checking overflows.
Definition checked_math.hh:41
auto check_cast(S in) -> bool
Check if s of type S can be casted to T without loss.
Definition checked_math.hh:24