DDC 0.0.0

a discrete domain computation library

discrete_vector.hpp
1// Copyright (C) The DDC development team, see COPYRIGHT.md file
2//
3// SPDX-License-Identifier: MIT
4
5#pragma once
6
7#include <array>
8#include <cstddef>
9#include <ostream>
10#include <type_traits>
11#include <utility>
12
13#include <Kokkos_Core.hpp>
14
15#include "ddc/detail/macros.hpp"
16#include "ddc/detail/type_seq.hpp"
17
18namespace ddc {
19
20template <class...>
21class DiscreteVector;
22
23template <class T>
24struct is_discrete_vector : std::false_type
25{
26};
27
28template <class... Tags>
29struct is_discrete_vector<DiscreteVector<Tags...>> : std::true_type
30{
31};
32
33template <class T>
34inline constexpr bool is_discrete_vector_v = is_discrete_vector<T>::value;
35
36
37namespace detail {
38
39template <class... Tags>
40struct ToTypeSeq<DiscreteVector<Tags...>>
41{
42 using type = TypeSeq<Tags...>;
43};
44
45} // namespace detail
46
47/** A DiscreteVectorElement is a scalar that represents the difference between two coordinates.
48 */
49using DiscreteVectorElement = std::ptrdiff_t;
50
51template <class QueryTag, class... Tags>
52KOKKOS_FUNCTION constexpr DiscreteVectorElement const& get(
53 DiscreteVector<Tags...> const& tuple) noexcept
54{
55 return tuple.template get<QueryTag>();
56}
57
58template <class QueryTag, class... Tags>
59KOKKOS_FUNCTION constexpr DiscreteVectorElement& get(DiscreteVector<Tags...>& tuple) noexcept
60{
61 return tuple.template get<QueryTag>();
62}
63
64template <class QueryTag, class... Tags>
65KOKKOS_FUNCTION constexpr DiscreteVectorElement const& get_or(
66 DiscreteVector<Tags...> const& tuple,
67 DiscreteVectorElement const& default_value) noexcept
68{
69 return tuple.template get_or<QueryTag>(default_value);
70}
71
72/// Unary operators: +, -
73
74template <class... Tags>
75KOKKOS_FUNCTION constexpr DiscreteVector<Tags...> operator+(DiscreteVector<Tags...> const& x)
76{
77 return x;
78}
79
80template <class... Tags>
81KOKKOS_FUNCTION constexpr DiscreteVector<Tags...> operator-(DiscreteVector<Tags...> const& x)
82{
83 return DiscreteVector<Tags...>((-get<Tags>(x))...);
84}
85
86/// Internal binary operators: +, -
87
88template <class... Tags, class... OTags>
89KOKKOS_FUNCTION constexpr auto operator+(
90 DiscreteVector<Tags...> const& lhs,
91 DiscreteVector<OTags...> const& rhs)
92{
93 using detail::TypeSeq;
94 if constexpr (sizeof...(Tags) >= sizeof...(OTags)) {
95 static_assert(((type_seq_contains_v<TypeSeq<OTags>, TypeSeq<Tags...>>)&&...));
96 DiscreteVector<Tags...> result(lhs);
97 result += rhs;
98 return result;
99 } else {
100 static_assert(((type_seq_contains_v<TypeSeq<Tags>, TypeSeq<OTags...>>)&&...));
101 DiscreteVector<OTags...> result(rhs);
102 result += lhs;
103 return result;
104 }
105}
106
107template <class... Tags, class... OTags>
108KOKKOS_FUNCTION constexpr auto operator-(
109 DiscreteVector<Tags...> const& lhs,
110 DiscreteVector<OTags...> const& rhs)
111{
112 using detail::TypeSeq;
113 if constexpr (sizeof...(Tags) >= sizeof...(OTags)) {
114 static_assert(((type_seq_contains_v<TypeSeq<OTags>, TypeSeq<Tags...>>)&&...));
115 DiscreteVector<Tags...> result(lhs);
116 result -= rhs;
117 return result;
118 } else {
119 static_assert(((type_seq_contains_v<TypeSeq<Tags>, TypeSeq<OTags...>>)&&...));
120 DiscreteVector<OTags...> result(-rhs);
121 result += lhs;
122 return result;
123 }
124}
125
126template <class Tag, class IntegralType, class = std::enable_if_t<std::is_integral_v<IntegralType>>>
127KOKKOS_FUNCTION constexpr DiscreteVector<Tag> operator+(
128 DiscreteVector<Tag> const& lhs,
129 IntegralType const& rhs)
130{
131 return DiscreteVector<Tag>(get<Tag>(lhs) + rhs);
132}
133
134template <class IntegralType, class Tag, class = std::enable_if_t<std::is_integral_v<IntegralType>>>
135KOKKOS_FUNCTION constexpr DiscreteVector<Tag> operator+(
136 IntegralType const& lhs,
137 DiscreteVector<Tag> const& rhs)
138{
139 return DiscreteVector<Tag>(lhs + get<Tag>(rhs));
140}
141
142template <class Tag, class IntegralType, class = std::enable_if_t<std::is_integral_v<IntegralType>>>
143KOKKOS_FUNCTION constexpr DiscreteVector<Tag> operator-(
144 DiscreteVector<Tag> const& lhs,
145 IntegralType const& rhs)
146{
147 return DiscreteVector<Tag>(get<Tag>(lhs) - rhs);
148}
149
150template <class IntegralType, class Tag, class = std::enable_if_t<std::is_integral_v<IntegralType>>>
151KOKKOS_FUNCTION constexpr DiscreteVector<Tag> operator-(
152 IntegralType const& lhs,
153 DiscreteVector<Tag> const& rhs)
154{
155 return DiscreteVector<Tag>(lhs - get<Tag>(rhs));
156}
157
158/// external left binary operator: *
159
160template <
161 class IntegralType,
162 class... Tags,
163 class = std::enable_if_t<std::is_integral_v<IntegralType>>>
164KOKKOS_FUNCTION constexpr auto operator*(
165 IntegralType const& lhs,
166 DiscreteVector<Tags...> const& rhs)
167{
168 return DiscreteVector<Tags...>((lhs * get<Tags>(rhs))...);
169}
170
171template <class... QueryTags, class... Tags>
172KOKKOS_FUNCTION constexpr DiscreteVector<QueryTags...> select(
173 DiscreteVector<Tags...> const& arr) noexcept
174{
175 return DiscreteVector<QueryTags...>(arr);
176}
177
178template <class... QueryTags, class... Tags>
179KOKKOS_FUNCTION constexpr DiscreteVector<QueryTags...> select(
180 DiscreteVector<Tags...>&& arr) noexcept
181{
182 return DiscreteVector<QueryTags...>(std::move(arr));
183}
184
185/// Returns a reference towards the DiscreteVector that contains the QueryTag
186template <
187 class QueryTag,
188 class HeadDVect,
189 class... TailDVects,
190 std::enable_if_t<
191 is_discrete_vector_v<HeadDVect> && (is_discrete_vector_v<TailDVects> && ...),
192 int> = 1>
193KOKKOS_FUNCTION constexpr auto const& take(HeadDVect const& head, TailDVects const&... tail)
194{
195 DDC_IF_NVCC_THEN_PUSH_AND_SUPPRESS(implicit_return_from_non_void_function)
196 if constexpr (type_seq_contains_v<detail::TypeSeq<QueryTag>, to_type_seq_t<HeadDVect>>) {
197 static_assert(
198 (!type_seq_contains_v<detail::TypeSeq<QueryTag>, to_type_seq_t<TailDVects>> && ...),
199 "ERROR: tag redundant");
200 return head;
201 } else {
202 static_assert(sizeof...(TailDVects) > 0, "ERROR: tag not found");
203 return take<QueryTag>(tail...);
204 }
205 DDC_IF_NVCC_THEN_POP
206}
207
208namespace detail {
209
210template <class T>
211class DiscreteVectorConversionOperators
212{
213};
214
215template <class Tag>
216class DiscreteVectorConversionOperators<DiscreteVector<Tag>>
217{
218public:
219 KOKKOS_FUNCTION constexpr operator DiscreteVectorElement const &() const noexcept
220 {
221 return static_cast<DiscreteVector<Tag> const*>(this)->m_values[0];
222 }
223
224 KOKKOS_FUNCTION constexpr operator DiscreteVectorElement&() noexcept
225 {
226 return static_cast<DiscreteVector<Tag>*>(this)->m_values[0];
227 }
228};
229
230/// Returns a reference to the underlying `std::array`
231template <class... Tags>
232KOKKOS_FUNCTION constexpr std::array<DiscreteVectorElement, sizeof...(Tags)>& array(
233 DiscreteVector<Tags...>& v) noexcept
234{
235 return v.m_values;
236}
237
238/// Returns a reference to the underlying `std::array`
239template <class... Tags>
240KOKKOS_FUNCTION constexpr std::array<DiscreteVectorElement, sizeof...(Tags)> const& array(
241 DiscreteVector<Tags...> const& v) noexcept
242{
243 return v.m_values;
244}
245
246} // namespace detail
247
248/** A DiscreteVector is a vector in the discrete dimension
249 *
250 * Each is tagged by its associated dimensions.
251 */
252template <class... Tags>
253class DiscreteVector : public detail::DiscreteVectorConversionOperators<DiscreteVector<Tags...>>
254{
255 friend class detail::DiscreteVectorConversionOperators<DiscreteVector<Tags...>>;
256
257 friend KOKKOS_FUNCTION constexpr std::array<DiscreteVectorElement, sizeof...(Tags)>& detail::
258 array<Tags...>(DiscreteVector<Tags...>& v) noexcept;
259 friend KOKKOS_FUNCTION constexpr std::array<DiscreteVectorElement, sizeof...(Tags)> const&
260 detail::array<Tags...>(DiscreteVector<Tags...> const& v) noexcept;
261
262 using tags_seq = detail::TypeSeq<Tags...>;
263
264private:
265 std::array<DiscreteVectorElement, sizeof...(Tags)> m_values;
266
267public:
268 static KOKKOS_FUNCTION constexpr std::size_t size() noexcept
269 {
270 return sizeof...(Tags);
271 }
272
273public:
274 KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector() = default;
275
276 KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector(DiscreteVector const&) = default;
277
278 KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector(DiscreteVector&&) = default;
279
280 template <class... DVects, class = std::enable_if_t<(is_discrete_vector_v<DVects> && ...)>>
281 explicit KOKKOS_FUNCTION constexpr DiscreteVector(DVects const&... delems) noexcept
282 : m_values {take<Tags>(delems...).template get<Tags>()...}
283 {
284 }
285
286 template <
287 class... Params,
288 class = std::enable_if_t<(!is_discrete_vector_v<Params> && ...)>,
289 class = std::enable_if_t<(std::is_convertible_v<Params, DiscreteVectorElement> && ...)>,
290 class = std::enable_if_t<sizeof...(Params) == sizeof...(Tags)>>
291 explicit KOKKOS_FUNCTION constexpr DiscreteVector(Params const&... params) noexcept
292 : m_values {static_cast<DiscreteVectorElement>(params)...}
293 {
294 }
295
296 KOKKOS_DEFAULTED_FUNCTION ~DiscreteVector() = default;
297
298 KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector& operator=(DiscreteVector const& other)
299 = default;
300
301 KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector& operator=(DiscreteVector&& other) = default;
302
303 template <class... OTags>
304 KOKKOS_FUNCTION constexpr DiscreteVector& operator=(
305 DiscreteVector<OTags...> const& other) noexcept
306 {
307 m_values = other.m_values;
308 return *this;
309 }
310
311 template <class... OTags>
312 KOKKOS_FUNCTION constexpr DiscreteVector& operator=(DiscreteVector<OTags...>&& other) noexcept
313 {
314 m_values = std::move(other.m_values);
315 return *this;
316 }
317
318 template <class... OTags>
319 KOKKOS_FUNCTION constexpr bool operator==(DiscreteVector<OTags...> const& rhs) const noexcept
320 {
321 return ((m_values[type_seq_rank_v<Tags, tags_seq>] == rhs.template get<Tags>()) && ...);
322 }
323
324 template <class... OTags>
325 KOKKOS_FUNCTION constexpr bool operator!=(DiscreteVector<OTags...> const& rhs) const noexcept
326 {
327 return !(*this == rhs);
328 }
329
330 template <class QueryTag>
331 KOKKOS_FUNCTION constexpr DiscreteVectorElement& get() noexcept
332 {
333 using namespace detail;
334 static_assert(in_tags_v<QueryTag, tags_seq>, "requested Tag absent from DiscreteVector");
335 return m_values[type_seq_rank_v<QueryTag, tags_seq>];
336 }
337
338 template <class QueryTag>
339 KOKKOS_FUNCTION constexpr DiscreteVectorElement const& get() const noexcept
340 {
341 using namespace detail;
342 static_assert(in_tags_v<QueryTag, tags_seq>, "requested Tag absent from DiscreteVector");
343 return m_values[type_seq_rank_v<QueryTag, tags_seq>];
344 }
345
346 template <class QueryTag>
347 KOKKOS_FUNCTION constexpr DiscreteVectorElement const& get_or(
348 DiscreteVectorElement const& default_value) const&
349 {
350 DDC_IF_NVCC_THEN_PUSH_AND_SUPPRESS(implicit_return_from_non_void_function)
351 if constexpr (in_tags_v<QueryTag, tags_seq>) {
352 return m_values[type_seq_rank_v<QueryTag, tags_seq>];
353 } else {
354 return default_value;
355 }
356 DDC_IF_NVCC_THEN_POP
357 }
358
359 template <std::size_t N = sizeof...(Tags)>
360 KOKKOS_FUNCTION constexpr std::enable_if_t<N == 1, DiscreteVectorElement const&> value()
361 const noexcept
362 {
363 return m_values[0];
364 }
365
366 template <std::size_t N = sizeof...(Tags), class = std::enable_if_t<N == 1>>
367 KOKKOS_FUNCTION constexpr DiscreteVector& operator++()
368 {
369 ++m_values[0];
370 return *this;
371 }
372
373 template <std::size_t N = sizeof...(Tags), class = std::enable_if_t<N == 1>>
374 KOKKOS_FUNCTION constexpr DiscreteVector operator++(int)
375 {
376 DiscreteVector const tmp = *this;
377 ++m_values[0];
378 return tmp;
379 }
380
381 template <std::size_t N = sizeof...(Tags), class = std::enable_if_t<N == 1>>
382 KOKKOS_FUNCTION constexpr DiscreteVector& operator--()
383 {
384 ++m_values[0];
385 return *this;
386 }
387
388 template <std::size_t N = sizeof...(Tags), class = std::enable_if_t<N == 1>>
389 KOKKOS_FUNCTION constexpr DiscreteVector operator--(int)
390 {
391 DiscreteVector const tmp = *this;
392 ++m_values[0];
393 return tmp;
394 }
395
396 template <class... OTags>
397 KOKKOS_FUNCTION constexpr DiscreteVector& operator+=(DiscreteVector<OTags...> const& rhs)
398 {
399 static_assert(((type_seq_contains_v<detail::TypeSeq<OTags>, tags_seq>)&&...));
400 ((m_values[type_seq_rank_v<OTags, tags_seq>] += rhs.template get<OTags>()), ...);
401 return *this;
402 }
403
404 template <
405 class IntegralType,
406 std::size_t N = sizeof...(Tags),
407 class = std::enable_if_t<N == 1>,
408 class = std::enable_if_t<std::is_integral_v<IntegralType>>>
409 KOKKOS_FUNCTION constexpr DiscreteVector& operator+=(IntegralType const& rhs)
410 {
411 m_values[0] += rhs;
412 return *this;
413 }
414
415 template <class... OTags>
416 KOKKOS_FUNCTION constexpr DiscreteVector& operator-=(DiscreteVector<OTags...> const& rhs)
417 {
418 static_assert(((type_seq_contains_v<detail::TypeSeq<OTags>, tags_seq>)&&...));
419 ((m_values[type_seq_rank_v<OTags, tags_seq>] -= rhs.template get<OTags>()), ...);
420 return *this;
421 }
422
423 template <
424 class IntegralType,
425 std::size_t N = sizeof...(Tags),
426 class = std::enable_if_t<N == 1>,
427 class = std::enable_if_t<std::is_integral_v<IntegralType>>>
428 KOKKOS_FUNCTION constexpr DiscreteVector& operator-=(IntegralType const& rhs)
429 {
430 m_values[0] -= rhs;
431 return *this;
432 }
433
434 template <class... OTags>
435 KOKKOS_FUNCTION constexpr DiscreteVector& operator*=(DiscreteVector<OTags...> const& rhs)
436 {
437 static_assert(((type_seq_contains_v<detail::TypeSeq<OTags>, tags_seq>)&&...));
438 ((m_values[type_seq_rank_v<OTags, tags_seq>] *= rhs.template get<OTags>()), ...);
439 return *this;
440 }
441};
442
443template <class Tag>
444KOKKOS_FUNCTION constexpr bool operator<(
445 DiscreteVector<Tag> const& lhs,
446 DiscreteVector<Tag> const& rhs)
447{
448 return lhs.value() < rhs.value();
449}
450
451template <class Tag, class IntegralType>
452KOKKOS_FUNCTION constexpr bool operator<(DiscreteVector<Tag> const& lhs, IntegralType const& rhs)
453{
454 return lhs.value() < rhs;
455}
456
457inline std::ostream& operator<<(std::ostream& out, DiscreteVector<> const&)
458{
459 out << "()";
460 return out;
461}
462
463template <class Head, class... Tags>
464std::ostream& operator<<(std::ostream& out, DiscreteVector<Head, Tags...> const& arr)
465{
466 out << "(";
467 out << get<Head>(arr);
468 ((out << ", " << get<Tags>(arr)), ...);
469 out << ")";
470 return out;
471}
472
473} // namespace ddc
KOKKOS_FUNCTION constexpr DiscreteVector & operator--()
Definition: discrete_vector.hpp:382
KOKKOS_FUNCTION constexpr DiscreteVectorElement const & get_or(DiscreteVectorElement const &default_value) const &
Definition: discrete_vector.hpp:347
KOKKOS_FUNCTION constexpr std::enable_if_t< N==1, DiscreteVectorElement const & > value() const noexcept
Definition: discrete_vector.hpp:360
KOKKOS_FUNCTION constexpr DiscreteVector & operator+=(IntegralType const &rhs)
Definition: discrete_vector.hpp:409
KOKKOS_DEFAULTED_FUNCTION ~DiscreteVector()=default
KOKKOS_FUNCTION constexpr DiscreteVectorElement const & get() const noexcept
Definition: discrete_vector.hpp:339
KOKKOS_FUNCTION constexpr DiscreteVector & operator=(DiscreteVector< OTags... > const &other) noexcept
Definition: discrete_vector.hpp:304
KOKKOS_FUNCTION constexpr DiscreteVector operator--(int)
Definition: discrete_vector.hpp:389
KOKKOS_FUNCTION constexpr DiscreteVector & operator-=(DiscreteVector< OTags... > const &rhs)
Definition: discrete_vector.hpp:416
KOKKOS_FUNCTION constexpr DiscreteVector & operator-=(IntegralType const &rhs)
Definition: discrete_vector.hpp:428
KOKKOS_FUNCTION constexpr DiscreteVector operator++(int)
Definition: discrete_vector.hpp:374
KOKKOS_FUNCTION constexpr bool operator!=(DiscreteVector< OTags... > const &rhs) const noexcept
Definition: discrete_vector.hpp:325
KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector & operator=(DiscreteVector &&other)=default
KOKKOS_FUNCTION constexpr DiscreteVector & operator++()
Definition: discrete_vector.hpp:367
static KOKKOS_FUNCTION constexpr std::size_t size() noexcept
Definition: discrete_vector.hpp:268
KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector()=default
KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector(DiscreteVector const &)=default
KOKKOS_FUNCTION constexpr DiscreteVector(DVects const &... delems) noexcept
Definition: discrete_vector.hpp:281
KOKKOS_FUNCTION constexpr DiscreteVectorElement & get() noexcept
Definition: discrete_vector.hpp:331
KOKKOS_FUNCTION constexpr DiscreteVector & operator=(DiscreteVector< OTags... > &&other) noexcept
Definition: discrete_vector.hpp:312
KOKKOS_FUNCTION constexpr bool operator==(DiscreteVector< OTags... > const &rhs) const noexcept
Definition: discrete_vector.hpp:319
KOKKOS_FUNCTION constexpr DiscreteVector & operator*=(DiscreteVector< OTags... > const &rhs)
Definition: discrete_vector.hpp:435
KOKKOS_FUNCTION constexpr DiscreteVector(Params const &... params) noexcept
Definition: discrete_vector.hpp:291
KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector(DiscreteVector &&)=default
KOKKOS_DEFAULTED_FUNCTION constexpr DiscreteVector & operator=(DiscreteVector const &other)=default
KOKKOS_FUNCTION constexpr DiscreteVector & operator+=(DiscreteVector< OTags... > const &rhs)
Definition: discrete_vector.hpp:397
Definition: aligned_allocator.hpp:11
KOKKOS_FUNCTION constexpr DiscreteVector< Tags... > operator+(DiscreteVector< Tags... > const &x)
Unary operators: +, -.
Definition: discrete_vector.hpp:75
KOKKOS_FUNCTION constexpr auto operator*(IntegralType const &lhs, DiscreteVector< Tags... > const &rhs)
external left binary operator: *
Definition: discrete_vector.hpp:164
KOKKOS_FUNCTION constexpr bool operator<(DiscreteVector< Tag > const &lhs, IntegralType const &rhs)
Definition: discrete_vector.hpp:452
KOKKOS_FUNCTION constexpr DiscreteVectorElement const & get_or(DiscreteVector< Tags... > const &tuple, DiscreteVectorElement const &default_value) noexcept
Definition: discrete_vector.hpp:65
KOKKOS_FUNCTION constexpr auto operator-(DiscreteVector< Tags... > const &lhs, DiscreteVector< OTags... > const &rhs)
Definition: discrete_vector.hpp:108
KOKKOS_FUNCTION constexpr DiscreteVectorElement & get(DiscreteVector< Tags... > &tuple) noexcept
Definition: discrete_vector.hpp:59
KOKKOS_FUNCTION constexpr DiscreteVector< Tag > operator-(DiscreteVector< Tag > const &lhs, IntegralType const &rhs)
Definition: discrete_vector.hpp:143
constexpr bool is_discrete_vector_v
Definition: discrete_vector.hpp:34
KOKKOS_FUNCTION constexpr DiscreteVectorElement const & get(DiscreteVector< Tags... > const &tuple) noexcept
Definition: discrete_vector.hpp:52
KOKKOS_FUNCTION constexpr DiscreteVector< Tags... > operator-(DiscreteVector< Tags... > const &x)
Definition: discrete_vector.hpp:81
KOKKOS_FUNCTION constexpr DiscreteVector< QueryTags... > select(DiscreteVector< Tags... > const &arr) noexcept
Definition: discrete_vector.hpp:172
KOKKOS_FUNCTION constexpr DiscreteVector< Tag > operator+(IntegralType const &lhs, DiscreteVector< Tag > const &rhs)
Definition: discrete_vector.hpp:135
KOKKOS_FUNCTION constexpr auto operator+(DiscreteVector< Tags... > const &lhs, DiscreteVector< OTags... > const &rhs)
Internal binary operators: +, -.
Definition: discrete_vector.hpp:89
KOKKOS_FUNCTION constexpr auto const & take(HeadDVect const &head, TailDVects const &... tail)
Returns a reference towards the DiscreteVector that contains the QueryTag.
Definition: discrete_vector.hpp:193
KOKKOS_FUNCTION constexpr bool operator<(DiscreteVector< Tag > const &lhs, DiscreteVector< Tag > const &rhs)
Definition: discrete_vector.hpp:444
KOKKOS_FUNCTION constexpr DiscreteVector< Tag > operator-(IntegralType const &lhs, DiscreteVector< Tag > const &rhs)
Definition: discrete_vector.hpp:151
KOKKOS_FUNCTION constexpr DiscreteVector< Tag > operator+(DiscreteVector< Tag > const &lhs, IntegralType const &rhs)
Definition: discrete_vector.hpp:127
KOKKOS_FUNCTION constexpr DiscreteVector< QueryTags... > select(DiscreteVector< Tags... > &&arr) noexcept
Definition: discrete_vector.hpp:179
Definition: discrete_vector.hpp:25