NuriKit v0.1.0b2
Loading...
Searching...
No Matches
utils.h
Go to the documentation of this file.
1//
2// Project NuriKit - Copyright 2023 SNU Compbio Lab.
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#ifndef NURI_UTILS_H_
7#define NURI_UTILS_H_
8
10#include <cstddef>
11#include <cstring>
12#include <filesystem>
13#include <functional>
14#include <limits>
15#include <memory>
16#include <numeric>
17#include <string_view>
18#include <type_traits>
19#include <utility>
20#include <vector>
21
22#include <absl/base/nullability.h>
23#include <absl/base/optimization.h>
24#include <absl/log/absl_check.h>
25#include <absl/numeric/bits.h>
26#include <absl/strings/ascii.h>
27#include <Eigen/Dense>
29
30#include "nuri/eigen_config.h"
31#include "nuri/meta.h"
32
33namespace nuri {
34template <class T, std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0>
35constexpr T min(T a, T b) {
36 return std::min(a, b);
37}
38
39template <
40 class L, class R,
41 std::enable_if_t<
42 std::is_same_v<internal::remove_cvref_t<L>, internal::remove_cvref_t<R>>
43 && !std::is_trivially_copyable_v<internal::remove_cvref_t<L>>
44 && std::is_lvalue_reference_v<L> && std::is_lvalue_reference_v<R>,
45 int> = 0>
46constexpr const std::remove_reference_t<L> &min(L &&a, R &&b) {
47 return std::min(std::forward<L>(a), std::forward<R>(b));
48}
49
50template <class T, std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0>
51constexpr T max(T a, T b) {
52 return std::max(a, b);
53}
54
55template <
56 class L, class R,
57 std::enable_if_t<
58 std::is_same_v<internal::remove_cvref_t<L>, internal::remove_cvref_t<R>>
59 && !std::is_trivially_copyable_v<internal::remove_cvref_t<L>>
60 && std::is_lvalue_reference_v<L> && std::is_lvalue_reference_v<R>,
61 int> = 0>
62constexpr const std::remove_reference_t<L> &max(L &&a, R &&b) {
63 return std::max(std::forward<L>(a), std::forward<R>(b));
64}
65
66template <class T, class Comp = std::less<>,
67 std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0>
68constexpr std::pair<T, T> minmax(T a, T b, Comp &&comp = {}) {
69 return std::minmax(a, b, std::forward<Comp>(comp));
70}
71
72template <
73 class L, class R, class Comp = std::less<>,
74 std::enable_if_t<
75 std::is_same_v<internal::remove_cvref_t<L>, internal::remove_cvref_t<R>>
76 && !std::is_trivially_copyable_v<internal::remove_cvref_t<L>>
77 && std::is_lvalue_reference_v<L> && std::is_lvalue_reference_v<R>,
78 int> = 0>
79constexpr std::pair<const std::remove_reference_t<L> &,
80 const std::remove_reference_t<L> &>
81minmax(L &&a, R &&b, Comp &&comp = {}) {
82 return std::minmax(std::forward<L>(a), std::forward<R>(b),
83 std::forward<Comp>(comp));
84}
85
86template <class T, std::enable_if_t<std::is_trivially_copyable_v<T>, int> = 0>
87constexpr T clamp(T v, T l, T h) {
88 return std::clamp(v, l, h);
89}
90
91template <
92 class T, class L, class H,
93 std::enable_if_t<
94 std::is_same_v<internal::remove_cvref_t<T>, internal::remove_cvref_t<L>>
95 && std::is_same_v<internal::remove_cvref_t<L>,
96 internal::remove_cvref_t<H>>
97 && !std::is_trivially_copyable_v<internal::remove_cvref_t<T>>
98 && std::is_lvalue_reference_v<T> && std::is_lvalue_reference_v<L>
99 && std::is_lvalue_reference_v<H>,
100 int> = 0>
101constexpr const std::remove_reference_t<T> &clamp(T &&v, L &&l, H &&h) {
102 return std::clamp(std::forward<T>(v), std::forward<L>(l), std::forward<H>(h));
103}
104
105namespace internal {
106 class PowersetStream {
107 public:
108 explicit PowersetStream(int n): n_(n), r_(0), r_max_(0), state_(0) { }
109
110 PowersetStream &next() {
111 if (state_ >= r_max_) {
112 ++r_;
113
114 if (ABSL_PREDICT_FALSE(!*this))
115 return *this;
116
117 r_max_ |= 1U << (n_ - r_);
118 state_ = (1U << r_) - 1;
119 return *this;
120 }
121
122 constexpr auto nbits = std::numeric_limits<decltype(state_)>::digits;
123 unsigned int shifted = state_ << (nbits - n_);
124
125 unsigned int leading_ones = absl::countl_one(shifted);
126 unsigned int stripped = (shifted << leading_ones) >> leading_ones;
127
128 int leading_zeros = absl::countl_zero(stripped);
129 int next_one_bit = nuri::max(n_ - leading_zeros - 1, 0);
130
131 unsigned int mask = (1U << next_one_bit) - 1;
132 state_ = ((1U << (leading_ones + 1)) - 1) << (next_one_bit + 1)
133 | (mask & state_);
134 return *this;
135 }
136
137 unsigned int state() const { return state_; }
138
139 operator bool() const { return r_ <= n_; }
140
141 private:
142 int n_;
143 int r_;
144 unsigned int r_max_;
145 unsigned int state_;
146 };
147
148 inline PowersetStream &operator>>(PowersetStream &ps, unsigned int &state) {
149 ps.next();
150 state = ps.state();
151 return ps;
152 }
153} // namespace internal
154
156
157template <class E, class U = internal::underlying_type_t<E>, U = 0>
158constexpr E operator|(E lhs, E rhs) {
159 return static_cast<E>(static_cast<U>(lhs) | static_cast<U>(rhs));
160}
161
162template <class E, class U = internal::underlying_type_t<E>, U = 0>
163constexpr E &operator|=(E &self, E rhs) {
164 return self = self | rhs;
165}
166
167template <class E, class U = internal::underlying_type_t<E>, U = 0>
168constexpr E operator&(E lhs, E rhs) {
169 return static_cast<E>(static_cast<U>(lhs) & static_cast<U>(rhs));
170}
171
172template <class E, class U = internal::underlying_type_t<E>, U = 0>
173constexpr E &operator&=(E &self, E rhs) {
174 return self = self & rhs;
175}
176
177template <class E, class U = internal::underlying_type_t<E>, U = 0>
178constexpr E operator^(E lhs, E rhs) {
179 return static_cast<E>(static_cast<U>(lhs) ^ static_cast<U>(rhs));
180}
181
182template <class E, class U = internal::underlying_type_t<E>, U = 0>
183constexpr E &operator^=(E &self, E rhs) {
184 return self = self ^ rhs;
185}
186
187template <class E, class U = internal::underlying_type_t<E>, U = 0>
188constexpr E operator~(E val) {
189 return static_cast<E>(~static_cast<U>(val));
190}
191
192template <class E, class U = internal::underlying_type_t<E>, U = 0,
193 std::enable_if_t<std::is_unsigned_v<U>, int> = 0>
194constexpr E operator-(E val) {
195 return static_cast<E>(-static_cast<U>(val));
196}
197
198namespace internal {
199 template <class E>
200 constexpr bool check_flag(E flags, E flag) {
201 // NOLINTNEXTLINE(bugprone-non-zero-enum-to-bool-conversion)
202 return static_cast<bool>(flags & flag);
203 }
204
205 template <class E, class U = extract_if_enum_t<E>,
206 std::enable_if_t<std::is_unsigned_v<U>, U> = 0>
207 constexpr E &update_flag(E &flags, bool cond, E flag) {
208 E mask = -static_cast<E>(cond);
209 flags = (flags & ~flag) | (mask & flag);
210 return flags;
211 }
212
213 template <class E, class U = extract_if_enum_t<E>,
214 std::enable_if_t<std::is_unsigned_v<U>, U> = 0>
215 constexpr E &set_flag_if(E &flags, bool cond, E flag) {
216 E mask = -static_cast<E>(cond);
217 flags |= mask & flag;
218 return flags;
219 }
220
221 template <class E, class U = extract_if_enum_t<E>,
222 std::enable_if_t<std::is_unsigned_v<U>, U> = 0>
223 constexpr E &unset_flag_if(E &flags, bool cond, E flag) {
224 E mask = -static_cast<E>(cond);
225 flags &= ~(mask & flag);
226 return flags;
227 }
228
229 template <class F>
230 int iround(F x) {
231 return static_cast<int>(std::lround(x));
232 }
233
234 constexpr int negate_if_false(bool cond) {
235 int ret = (static_cast<int>(cond) << 1) - 1;
236 ABSL_ASSUME(ret == 1 || ret == -1);
237 return ret;
238 }
239} // namespace internal
240
241template <int N = Eigen::Dynamic, int... Extra>
242auto generate_index(Eigen::Index size) {
243 Array<int, N, 1, 0, Extra...> result(size);
244 std::iota(result.begin(), result.end(), 0);
245 return result;
246}
247
248template <int N = Eigen::Dynamic, int... Extra, class Container,
249 class Comp = std::less<>>
250auto argsort(const Container &container, Comp op = {}) {
251 auto idxs = generate_index<N, Extra...>(std::size(container));
252 std::sort(idxs.begin(), idxs.end(),
253 [&](int i, int j) { return op(container[i], container[j]); });
254 return idxs;
255}
256
257template <int N = Eigen::Dynamic, int... Extra, class Container,
258 class Comp = std::less<>>
259auto argpartition(const Container &container, int count, Comp op = {}) {
260 auto idxs = generate_index<N, Extra...>(std::size(container));
261 std::nth_element(idxs.begin(), idxs.begin() + count - 1, idxs.end(),
262 [&](int i, int j) {
263 return op(container[i], container[j]);
264 });
265 return idxs;
266}
267
268template <class Container>
269void mask_to_map(Container &mask) {
270 typename Container::value_type idx = 0;
271 for (int i = 0; i < mask.size(); ++i) {
272 mask[i] = mask[i] ? idx++ : -1;
273 }
274}
275
276template <typename Derived, typename Base>
277std::unique_ptr<Derived>
278static_unique_ptr_cast(std::unique_ptr<Base> &&p) noexcept {
279 auto d = static_cast<Derived *>(p.release());
280 return std::unique_ptr<Derived>(d);
281}
282
283inline std::string_view extension_no_dot(const std::filesystem::path &ext) {
284 const std::string_view ext_view = ext.native();
285 if (ABSL_PREDICT_TRUE(!ext_view.empty())) {
286 return ext_view.substr(1);
287 }
288 return ext_view;
289}
290
291constexpr std::string_view slice(std::string_view str, std::size_t begin,
292 std::size_t end) {
293 return str.substr(begin, end - begin);
294}
295
296inline std::string_view slice_strip(std::string_view str, std::size_t begin,
297 std::size_t end) {
298 return absl::StripAsciiWhitespace(slice(str, begin, end));
299}
300
301inline std::string_view slice_rstrip(std::string_view str, std::size_t begin,
302 std::size_t end) {
303 return absl::StripTrailingAsciiWhitespace(slice(str, begin, end));
304}
305
306constexpr std::string_view safe_substr(std::string_view str, size_t begin,
307 size_t count = std::string_view::npos) {
308 if (ABSL_PREDICT_FALSE(begin > str.size()))
309 return "";
310
311 return str.substr(begin, count);
312}
313
314constexpr std::string_view safe_slice(std::string_view str, size_t begin,
315 size_t end) {
316 if (ABSL_PREDICT_FALSE(begin > str.size()))
317 return "";
318
319 return slice(str, begin, end);
320}
321
322inline std::string_view safe_slice_strip(std::string_view str, size_t begin,
323 size_t end) {
324 return absl::StripAsciiWhitespace(safe_slice(str, begin, end));
325}
326
327inline std::string_view safe_slice_rstrip(std::string_view str, size_t begin,
328 size_t end) {
329 return absl::StripTrailingAsciiWhitespace(safe_slice(str, begin, end));
330}
331
332namespace internal {
333 template <bool = (sizeof(double) * 3) % alignof(Vector3d) == 0>
334 inline void stack_impl(Matrix3Xd &m, const std::vector<Vector3d> &vs) {
335 for (int i = 0; i < vs.size(); ++i)
336 m.col(i) = vs[i];
337 }
338
339 template <>
340 inline void stack_impl<true>(Matrix3Xd &m, const std::vector<Vector3d> &vs) {
341 ABSL_DCHECK(reinterpret_cast<ptrdiff_t>(vs[0].data()) + sizeof(double) * 3
342 == reinterpret_cast<ptrdiff_t>(vs[1].data()))
343 << "Bad alignment";
344 std::memcpy(m.data(), vs[0].data(), vs.size() * sizeof(double) * 3);
345 }
346} // namespace internal
347
348inline Matrix3Xd stack(const std::vector<Vector3d> &vs) {
349 if (ABSL_PREDICT_FALSE(vs.empty()))
350 return Matrix3Xd(3, 0);
351
352 Matrix3Xd m(3, vs.size());
353 internal::stack_impl(m, vs);
354 return m;
355}
356
357template <class T = int, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
358constexpr T value_if(bool cond, T val = 1) {
359 return static_cast<T>(static_cast<int>(cond)) * val;
360}
361
362template <class Scalar, std::enable_if_t<std::is_arithmetic_v<Scalar>
363 && (!std::is_integral_v<Scalar>
364 || std::is_signed_v<Scalar>),
365 int> = 0>
366constexpr Scalar nonnegative(Scalar x) {
367 return nuri::max(x, static_cast<Scalar>(0));
368}
369
370template <class UInt,
371 std::enable_if_t<std::is_integral_v<UInt> && !std::is_signed_v<UInt>,
372 int> = 0>
373constexpr UInt nonnegative(UInt x) {
374 return x;
375}
376
377template <class UInt,
378 std::enable_if_t<std::is_same_v<UInt, unsigned int>, int> = 0>
379constexpr int log_base10(UInt x) {
380 int lg = (x >= 1000000000) ? 9
381 : (x >= 100000000) ? 8
382 : (x >= 10000000) ? 7
383 : (x >= 1000000) ? 6
384 : (x >= 100000) ? 5
385 : (x >= 10000) ? 4
386 : (x >= 1000) ? 3
387 : (x >= 100) ? 2
388 : (x >= 10) ? 1
389 : 0;
390 return lg;
391}
392
393template <class Int, std::enable_if_t<std::is_same_v<Int, int>, int> = 0>
394// NOLINTNEXTLINE(readability-function-cognitive-complexity)
395constexpr int log_base10(Int x) {
396 int lg = value_if(x < 0);
397 lg += log_base10(static_cast<unsigned int>(x < 0 ? -x : x));
398 return lg;
399}
400
401namespace internal {
402#ifdef absl_nullable
403 // These 3 are deprecated as of v20250512
404 template <class T>
405 using Nonnull = T absl_nonnull;
406 template <class T>
407 using Nullable = T absl_nullable;
408 template <class T>
409 using NullabilityUnknown = T absl_nullability_unknown;
410#else
411 using absl::Nonnull;
412 using absl::NullabilityUnknown;
413 using absl::Nullable;
414#endif
415} // namespace internal
416} // namespace nuri
417
418#endif /* NURI_UTILS_H_ */
Definition crdgen.h:16
constexpr T min(T a, T b)
Definition utils.h:35
constexpr std::pair< T, T > minmax(T a, T b, Comp &&comp={})
Definition utils.h:68
constexpr T clamp(T v, T l, T h)
Definition utils.h:87
constexpr T max(T a, T b)
Definition utils.h:51