OpenKalman
replicate.hpp
Go to the documentation of this file.
1 /* This file is part of OpenKalman, a header-only C++ library for
2  * Kalman filters and other recursive filters.
3  *
4  * Copyright (c) 2025 Christopher Lee Ogden <ogden@gatech.edu>
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at https://mozilla.org/MPL/2.0/.
9  */
10 
16 #ifndef OPENKALMAN_COLLECTIONS_VIEWS_REPLICATE_HPP
17 #define OPENKALMAN_COLLECTIONS_VIEWS_REPLICATE_HPP
18 
19 #include "values/values.hpp"
24 #include "all.hpp"
25 
27 {
41 #ifdef __cpp_lib_ranges
42  template<collection V, values::index Factor> requires std::same_as<std::decay_t<Factor>, Factor>
43 #else
44  template<typename V, typename Factor>
45 #endif
46  struct replicate_view : stdex::ranges::view_interface<replicate_view<V, Factor>>
47  {
48  private:
49 
50  template<bool Const, typename T>
51  using maybe_const = std::conditional_t<Const, const T, T>;
52 
53  public:
54 
58  template<bool Const>
59  struct iterator
60  {
61  using iterator_concept = std::random_access_iterator_tag;
62  using iterator_category = std::random_access_iterator_tag;
63  using value_type = stdex::ranges::range_value_t<V>;
64  using reference = stdex::ranges::range_reference_t<V>;
65  using difference_type = std::ptrdiff_t;
66  using pointer = void;
67 
68  private:
69 
70  using Parent = maybe_const<Const, replicate_view>;
71 
72  constexpr decltype(auto)
73  get_parent_elem(difference_type ix)
74  {
75  if constexpr (values::fixed_value_compares_with<size_of<V>, 0_uz>) return value_type{};
76  else return get_element(parent_->v_, static_cast<std::size_t>(std::move(ix)));
77  }
78 
79  constexpr decltype(auto)
80  get_parent_elem(difference_type ix) const
81  {
82  if constexpr (values::fixed_value_compares_with<size_of<V>, 0_uz>) return value_type{};
83  else return get_element(parent_->v_, static_cast<std::size_t>(std::move(ix)));
84  }
85 
86  public:
87 
88  constexpr iterator() = default;
89 
90  constexpr iterator(Parent& parent, difference_type p) : parent_ {std::addressof(parent)}, current_{p} {}
91 
92  constexpr iterator(iterator<not Const> i) : parent_ {std::move(i.parent_)}, current_ {std::move(i.current_)} {}
93 
94  constexpr decltype(auto) operator*() { return get_parent_elem(current_ % get_size(parent_->v_)); }
95  constexpr decltype(auto) operator*() const { return get_parent_elem(current_ % get_size(parent_->v_)); }
96  constexpr decltype(auto) operator[](difference_type offset) { return get_parent_elem((current_ + offset) % get_size(parent_->v_)); }
97  constexpr decltype(auto) operator[](difference_type offset) const { return get_parent_elem((current_ + offset) % get_size(parent_->v_)); }
98 
99  constexpr auto& operator++() noexcept { ++current_; return *this; }
100  constexpr auto operator++(int) noexcept { auto temp = *this; ++*this; return temp; }
101  constexpr auto& operator--() noexcept { --current_; return *this; }
102  constexpr auto operator--(int) noexcept { auto temp = *this; --*this; return temp; }
103  constexpr auto& operator+=(const difference_type diff) noexcept { current_ += diff; return *this; }
104  constexpr auto& operator-=(const difference_type diff) noexcept { current_ -= diff; return *this; }
105  friend constexpr auto operator+(const iterator& it, const difference_type diff) noexcept
106  { return iterator {*it.parent_, it.current_ + diff}; }
107  friend constexpr auto operator+(const difference_type diff, const iterator& it) noexcept
108  { return iterator {*it.parent_, diff + it.current_}; }
109  friend constexpr auto operator-(const iterator& it, const difference_type diff)
110  { return iterator {*it.parent_, it.current_ - diff}; }
111  friend constexpr difference_type operator-(const iterator& it, const iterator& other) noexcept
112  { return it.current_ - other.current_; }
113  friend constexpr bool operator==(const iterator& it, const iterator& other) noexcept
114  { return it.current_ == other.current_; }
115 #ifdef __cpp_impl_three_way_comparison
116  constexpr auto operator<=>(const iterator& other) const noexcept { return current_ <=> other.current_; }
117 #else
118  constexpr bool operator!=(const iterator& other) const noexcept { return current_ != other.current_; }
119  constexpr bool operator<(const iterator& other) const noexcept { return current_ < other.current_; }
120  constexpr bool operator>(const iterator& other) const noexcept { return current_ > other.current_; }
121  constexpr bool operator<=(const iterator& other) const noexcept { return current_ <= other.current_; }
122  constexpr bool operator>=(const iterator& other) const noexcept { return current_ >= other.current_; }
123 #endif
124 
125  private:
126 
127  Parent * parent_;
128  difference_type current_;
129 
130  };
131 
132 
136 #ifdef __cpp_concepts
137  constexpr
138  replicate_view() = default;
139 #else
140  template<bool Enable = true, std::enable_if_t<Enable and
141  stdex::default_initializable<V> and stdex::default_initializable<Factor>, int> = 0>
142  constexpr
144 #endif
145 
146 
150  constexpr
151  replicate_view(V& v, Factor f) : v_ {v}, f_ {std::move(f)} {}
152 
153 
155  constexpr
156  replicate_view(V&& v, Factor f) : v_ {std::move(v)}, f_ {std::move(f)} {}
157 
158 
162 #ifdef __cpp_explicit_this_parameter
163  constexpr decltype(auto)
164  base(this auto&& self) noexcept { return std::forward<decltype(self)>(self).v_; }
165 #else
166  constexpr V& base() & { return this->v_; }
167  constexpr const V& base() const & { return this->v_; }
168  constexpr V&& base() && noexcept { return std::move(*this).v_; }
169  constexpr const V&& base() const && noexcept { return std::move(*this).v_; }
170 #endif
171 
172 
176 #ifdef __cpp_concepts
177  constexpr auto
178  begin() requires stdex::ranges::range<const V>
179 #else
180  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
181  constexpr auto begin()
182 #endif
183  { return iterator<false> {*this, 0_uz}; }
184 
185 
186 #ifdef __cpp_concepts
187  constexpr auto
188  begin() const requires stdex::ranges::range<const V>
189 #else
190  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
191  constexpr auto begin() const
192 #endif
193  { return iterator<true> {*this, 0_uz}; }
194 
195 
199 #ifdef __cpp_concepts
200  constexpr auto
201  end() requires stdex::ranges::range<const V>
202 #else
203  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
204  constexpr auto
205  end()
206 #endif
207  {
208  return iterator<false> {*this, static_cast<std::ptrdiff_t>(values::to_value_type(size()))};
209  }
210 
211 
213 #ifdef __cpp_concepts
214  constexpr auto
215  end() const requires stdex::ranges::range<const V>
216 #else
217  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
218  constexpr auto
219  end() const
220 #endif
221  {
222  return iterator<true> {*this, static_cast<std::ptrdiff_t>(values::to_value_type(size()))};
223  }
224 
225 
229 #ifdef __cpp_concepts
230  constexpr values::index auto size() const
231 #else
232  constexpr auto size() const
233 #endif
234  {
235  return values::operation(std::multiplies{}, get_size(v_), f_);
236  }
237 
238 
242 #ifdef __cpp_explicit_this_parameter
243  template<std::size_t i>
244  constexpr decltype(auto)
245  get(this auto&& self) noexcept
246  {
247  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
248  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
249  return collections::get_element(std::forward<decltype(self)>(self).v_,
250  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(self.v_)));
251  }
252 #else
253  template<std::size_t i>
254  constexpr decltype(auto)
255  get() &
256  {
257  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
258  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
259  return collections::get_element(v_,
260  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(v_)));
261  }
262 
263  template<std::size_t i>
264  constexpr decltype(auto)
265  get() const &
266  {
267  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
268  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
269  return collections::get_element(v_,
270  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(v_)));
271  }
272 
273  template<std::size_t i>
274  constexpr decltype(auto)
275  get() && noexcept
276  {
277  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
278  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
279  return collections::get_element(std::move(*this).v_,
280  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(std::move(*this).v_)));
281  }
282 
283  template<std::size_t i>
284  constexpr decltype(auto)
285  get() const && noexcept
286  {
287  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
288  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
289  return collections::get_element(std::move(*this).v_,
290  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(std::move(*this).v_)));
291  }
292 #endif
293 
294  private:
295 
296  V v_;
297  Factor f_;
298  };
299 
300 
304  template<typename V, typename F>
305  replicate_view(const V&, const F&) -> replicate_view<V, F>;
306 
307 
308 }
309 
310 
311 #ifdef __cpp_lib_ranges
312 namespace std::ranges
313 #else
314 namespace OpenKalman::stdex::ranges
315 #endif
316 {
317  template<typename V, typename F>
318  constexpr bool enable_borrowed_range<OpenKalman::collections::replicate_view<V, F>> = enable_borrowed_range<V>;
319 }
320 
321 
322 #ifndef __cpp_lib_ranges
324 {
325  template<typename V, typename F, typename = void>
327 
328  template<typename V, typename F>
329  struct replicate_tuple_size<V, F, std::enable_if_t<values::fixed<F> and (size_of<V>::value != stdex::dynamic_extent)>>
330  : std::integral_constant<std::size_t, size_of_v<V> * values::fixed_value_of_v<F>> {};
331 
332 
333  template<std::size_t i, typename V, typename = void>
335  {
336  using type = stdex::ranges::range_value_t<V>;
337  };
338 
339  template<std::size_t i, typename V>
340  struct replicate_tuple_element<i, V, std::enable_if_t<size_of<V>::value != stdex::dynamic_extent>>
341  : collection_element<i % size_of_v<V>, std::decay_t<V>> {};
342 
343 }
344 #endif
345 
346 
347 namespace std
348 {
349 #ifdef __cpp_lib_ranges
350  template<typename V, OpenKalman::values::fixed F> requires (OpenKalman::collections::size_of_v<V> != OpenKalman::stdex::dynamic_extent)
351  struct tuple_size<OpenKalman::collections::replicate_view<V, F>>
352  : integral_constant<std::size_t, OpenKalman::collections::size_of_v<V> * OpenKalman::values::fixed_value_of_v<F>> {};
353 #else
354  template<typename V, typename F>
355  struct tuple_size<OpenKalman::collections::replicate_view<V, F>>
357 #endif
358 
359 
360 #ifdef __cpp_lib_ranges
361  template<size_t i, typename V, typename F> requires (OpenKalman::collections::size_of_v<V> != OpenKalman::stdex::dynamic_extent)
362  struct tuple_element<i, OpenKalman::collections::replicate_view<V, F>>
363  : tuple_element<i % OpenKalman::collections::size_of_v<V>, decay_t<V>> {};
364 
365  template<size_t i, typename V, typename F> requires (OpenKalman::collections::size_of_v<V> == OpenKalman::stdex::dynamic_extent)
366  struct tuple_element<i, OpenKalman::collections::replicate_view<V, F>>
367  {
368  using type = OpenKalman::stdex::ranges::range_value_t<V>;
369  };
370 #else
371  template<size_t i, typename V, typename F>
372  struct tuple_element<i, OpenKalman::collections::replicate_view<V, F>>
374 #endif
375 
376 }
377 
378 
380 {
381  namespace detail
382  {
383  template<typename Factor>
384  struct replicate_closure : stdex::ranges::range_adaptor_closure<replicate_closure<Factor>>
385  {
386  constexpr replicate_closure(Factor f) : factor_ {std::move(f)} {};
387 
388 #ifdef __cpp_concepts
389  template<viewable_collection R>
390 #else
391  template<typename R, std::enable_if_t<viewable_collection<R>, int> = 0>
392 #endif
393  constexpr auto
394  operator() (R&& r) const
395  {
396  return replicate_view {all(std::forward<R>(r)), factor_};
397  }
398 
399  private:
400  Factor factor_;
401  };
402 
403 
405  {
406 #ifdef __cpp_concepts
407  template<values::index Factor>
408 #else
409  template<typename Factor, std::enable_if_t<values::index<Factor>, int> = 0>
410 #endif
411  constexpr auto
412  operator() (Factor factor) const
413  {
414  return replicate_closure<Factor> {std::move(factor)};
415  }
416 
417 
418 #ifdef __cpp_concepts
419  template<viewable_collection R, values::index Factor>
420 #else
421  template<typename R, typename Factor, std::enable_if_t<viewable_collection<R> and values::index<Factor>, int> = 0>
422 #endif
423  constexpr auto
424  operator() (R&& r, Factor factor) const
425  {
426  return replicate_view {all(std::forward<R>(r)), std::move(factor)};
427  }
428 
429  };
430 
431  }
432 
433 
441 
442 }
443 
444 
445 #endif
constexpr auto begin()
Definition: replicate.hpp:181
Namespace for collections.
Definition: collections.hpp:27
Definition for collections::get.
Definition for collections::collection.
constexpr detail::replicate_adaptor replicate
a std::ranges::range_adaptor_closure for a set of replicated pattern objects.
Definition: replicate.hpp:86
constexpr replicate_view()
Default constructor.
Definition: replicate.hpp:143
constexpr auto end()
Definition: replicate.hpp:205
constexpr replicate_view(V &v, Factor f)
Construct from a collection.
Definition: replicate.hpp:151
Definition of lexicographical_compare_three_way for collections.
Header file for code relating to values (e.g., scalars and indices)
Iterator for replicate_view.
Definition: replicate.hpp:59
decltype(auto) constexpr to_value_type(Arg &&arg)
Convert, if necessary, a fixed or dynamic value to its underlying base type.
Definition: to_value_type.hpp:28
The size of a sized object (including a collection).
Definition: size_of.hpp:33
constexpr V & base() &
The base view.
Definition: replicate.hpp:166
constexpr replicate_view(V &&v, Factor f)
Definition: replicate.hpp:156
Definition: view_interface.hpp:32
Definition for collections::get_size.
A view that replicates a collection some number of times.
Definition: replicate.hpp:46
decltype(auto) constexpr get() &
Get element i.
Definition: replicate.hpp:255
constexpr auto size() const
Definition: replicate.hpp:232
Namespace for generalized views.
Definition: collections.hpp:33
Definition: range_adaptor_closure.hpp:34
The type of the element at a given index, if it can be determined at compile time.
Definition: collection_element.hpp:48
constexpr auto end() const
Definition: replicate.hpp:219
constexpr bool fixed_value_compares_with
T has a fixed value that compares with N in a particular way based on parameter comp.
Definition: fixed_value_compares_with.hpp:74
constexpr bool index
T is an index value.
Definition: index.hpp:62
decltype(auto) constexpr get_element(Arg &&arg, I i)
A generalization of std::get and the range subscript operator.
Definition: get_element.hpp:122
Definition: gettable.hpp:24
constexpr auto get_size(Arg &&arg)
Get the size of a sized object (e.g, a collection)
Definition: get_size.hpp:188
replicate_view(const V &, const F &) -> replicate_view< V, F >
Deduction guide.
constexpr auto operation(Operation &&op, Args &&...args)
A potentially constant-evaluated operation involving some number of values.
Definition: operation.hpp:98