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 <stdexcept>
14#include <type_traits>
15#include <utility>
16
17namespace CppClingo::Util {
18
21
22// Casting
23
25template <std::signed_integral T, std::signed_integral S> auto check_cast(S in) -> bool {
26 return std::in_range<T>(std::move(in));
27}
28
30template <std::integral T, std::integral S> auto safe_cast(S in) -> T {
31 if (std::in_range<T, S>(in)) {
32 return static_cast<T>(std::move(in));
33 }
34 throw std::range_error("invalid cast");
35}
36
37// Addition
38
42template <std::signed_integral S> auto check_add(S a, S b) -> std::optional<S> {
43#ifdef __GNUC__
44 if (S c; !__builtin_add_overflow(a, b, &c)) {
45 return c;
46 }
47 return std::nullopt;
48#else
49 if constexpr (std::is_same_v<S, int32_t>) {
50 int64_t tmp = static_cast<int64_t>(a) + b;
51 if (check_cast<int32_t>(tmp)) {
52 return static_cast<int32_t>(tmp);
53 }
54 return std::nullopt;
55 } else {
56 using U = std::make_unsigned_t<S>;
57 S tmp = static_cast<S>(static_cast<U>(a) + static_cast<U>(b));
58 if ((a >= 0 && b >= 0 && tmp < a) || (a < 0 && b < 0 && tmp > a)) {
59 return std::nullopt;
60 }
61 return tmp;
62 }
63#endif
64}
65
66// Subtraction
67
71template <std::signed_integral S> auto check_sub(S a, S b) -> std::optional<S> {
72#ifdef __GNUC__
73 if (S c; !__builtin_sub_overflow(a, b, &c)) {
74 return c;
75 }
76 return std::nullopt;
77#else
78 if constexpr (std::is_same_v<S, int32_t>) {
79 int64_t tmp = static_cast<int64_t>(a) - b;
80 if (check_cast<int32_t>(tmp)) {
81 return static_cast<int32_t>(tmp);
82 }
83 return std::nullopt;
84 } else {
85 using U = std::make_unsigned_t<S>;
86 S tmp = static_cast<S>(static_cast<U>(a) - static_cast<U>(b));
87 if ((a >= 0 && b < 0 && tmp < a) || (b >= 0 && tmp > a)) {
88 return std::nullopt;
89 }
90 return tmp;
91 }
92#endif
93}
94
95// Unary Minus
96
98template <std::signed_integral S> auto check_neg(S a) -> std::optional<S> {
99 if (a == std::numeric_limits<S>::min()) {
100 return std::nullopt;
101 }
102 return -a;
103}
104
105// Absolute
106
108template <std::signed_integral S> auto check_abs(S a) -> std::optional<S> {
109 if (a == std::numeric_limits<S>::min()) {
110 return std::nullopt;
111 }
112 return a < 0 ? -a : a;
113}
114
115// Multiplication
116
120template <std::signed_integral S> auto check_mul(S a, S b) -> std::optional<S> {
121#ifdef __GNUC__
122 if (S c; !__builtin_mul_overflow(a, b, &c)) {
123 return c;
124 }
125 return std::nullopt;
126#else
127 if constexpr (std::is_same_v<S, int32_t>) {
128 int64_t tmp = static_cast<int64_t>(a) * b;
129 if (check_cast<int32_t>(tmp)) {
130 return static_cast<int32_t>(tmp);
131 }
132 return std::nullopt;
133 } else {
134 if (a > 0 && b > 0 && a > std::numeric_limits<S>::max() / b) {
135 return std::nullopt;
136 }
137 if (a > 0 && b < 0 && b < std::numeric_limits<S>::min() / a) {
138 return std::nullopt;
139 }
140 if (a < 0 && b > 0 && a < std::numeric_limits<S>::min() / b) {
141 return std::nullopt;
142 }
143 if (a < 0 && b < 0 && b < std::numeric_limits<S>::max() / a) {
144 return std::nullopt;
145 }
146 return a * b;
147 }
148#endif
149}
150
151// Division
152
154template <std::signed_integral S> auto check_div(S a, S b) -> std::optional<S> {
155 if (b == 0 || (b == -1 && a == std::numeric_limits<S>::min())) {
156 return std::nullopt;
157 }
158 int d = a / b;
159 int r = a % b;
160 // The remainder is zero if |b| is 1, so we can always subtract 1.
161 if ((r > 0 && b < 0) || (r < 0 && b > 0)) {
162 --d;
163 }
164 return d;
165}
166
167// Modulo
168
170template <std::signed_integral S> auto check_mod(S a, S b) -> std::optional<S> {
171 if (b == 0) {
172 return std::nullopt;
173 }
174 if (b == -1) {
175 return 0;
176 }
177 int r = a % b;
178 // This never overflows.
179 if ((r > 0 && b < 0) || (r < 0 && b > 0)) {
180 r += b;
181 }
182 return r;
183}
184
185// Power
186
190template <std::signed_integral S> auto check_pow(S a, S b) -> std::optional<S> {
191 if (b < 0) {
192 return std::nullopt;
193 }
194 int r = 1;
195 while (b > 0) {
196 if ((b & 1) != 0) {
197 auto tmp = check_mul(r, a);
198 if (!tmp.has_value()) {
199 return std::nullopt;
200 }
201 r = tmp.value();
202 }
203 b >>= 1;
204 if (b > 0) {
205 auto tmp = check_mul(a, a);
206 if (!tmp.has_value()) {
207 return std::nullopt;
208 }
209 a = tmp.value();
210 }
211 }
212 return r;
213}
214
216
217} // namespace CppClingo::Util
auto check_neg(S a) -> std::optional< S >
Negate an integer checking overflows.
Definition checked_math.hh:98
auto check_pow(S a, S b) -> std::optional< S >
Power of the given integers checking overflows.
Definition checked_math.hh:190
auto check_mul(S a, S b) -> std::optional< S >
Multiply two integers checking overflows.
Definition checked_math.hh:120
auto safe_cast(S in) -> T
Cast S to T if possible.
Definition checked_math.hh:30
auto check_mod(S a, S b) -> std::optional< S >
Modulo of two integers checking overflows (truncating toward negative infinity).
Definition checked_math.hh:170
auto check_sub(S a, S b) -> std::optional< S >
Subtract two integers checking overflows.
Definition checked_math.hh:71
auto check_abs(S a) -> std::optional< S >
The absolute of an integer checking overflows.
Definition checked_math.hh:108
auto check_div(S a, S b) -> std::optional< S >
Divide two integers checking overflows (truncating toward negative infinity).
Definition checked_math.hh:154
auto check_add(S a, S b) -> std::optional< S >
Add two integers checking overflows.
Definition checked_math.hh:42
auto check_cast(S in) -> bool
Check if s of type S can be casted to T without loss.
Definition checked_math.hh:25