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-2026 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"
23 #include "all.hpp"
24 
26 {
40 #ifdef __cpp_lib_ranges
41  template<collection V, values::index Factor> requires std::same_as<std::decay_t<Factor>, Factor>
42 #else
43  template<typename V, typename Factor>
44 #endif
45  struct replicate_view : stdex::ranges::view_interface<replicate_view<V, Factor>>
46  {
47  private:
48 
49  template<bool Const, typename T>
50  using maybe_const = std::conditional_t<Const, const T, T>;
51 
52  public:
53 
57  template<bool Const>
58  struct iterator
59  {
60  using iterator_concept = std::random_access_iterator_tag;
61  using iterator_category = std::random_access_iterator_tag;
62  using value_type = stdex::ranges::range_value_t<V>;
63  using reference = stdex::ranges::range_reference_t<V>;
64  using difference_type = std::ptrdiff_t;
65  using pointer = void;
66 
67  private:
68 
69  using Parent = maybe_const<Const, replicate_view>;
70 
71  constexpr decltype(auto)
72  get_parent_elem(difference_type ix)
73  {
74  if constexpr (values::fixed_value_compares_with<size_of<V>, 0_uz>) return value_type{};
75  else return get_element(parent_->v_, static_cast<std::size_t>(std::move(ix)));
76  }
77 
78  constexpr decltype(auto)
79  get_parent_elem(difference_type ix) const
80  {
81  if constexpr (values::fixed_value_compares_with<size_of<V>, 0_uz>) return value_type{};
82  else return get_element(parent_->v_, static_cast<std::size_t>(std::move(ix)));
83  }
84 
85  public:
86 
87  constexpr iterator() = default;
88 
89  constexpr iterator(Parent& parent, difference_type p) : parent_ {std::addressof(parent)}, current_{p} {}
90 
91  constexpr iterator(iterator<not Const> i) : parent_ {std::move(i.parent_)}, current_ {std::move(i.current_)} {}
92 
93  constexpr decltype(auto) operator*() { return get_parent_elem(current_ % get_size(parent_->v_)); }
94  constexpr decltype(auto) operator*() const { return get_parent_elem(current_ % get_size(parent_->v_)); }
95  constexpr decltype(auto) operator[](difference_type offset) { return get_parent_elem((current_ + offset) % get_size(parent_->v_)); }
96  constexpr decltype(auto) operator[](difference_type offset) const { return get_parent_elem((current_ + offset) % get_size(parent_->v_)); }
97 
98  constexpr auto& operator++() noexcept { ++current_; return *this; }
99  constexpr auto operator++(int) noexcept { auto temp = *this; ++*this; return temp; }
100  constexpr auto& operator--() noexcept { --current_; return *this; }
101  constexpr auto operator--(int) noexcept { auto temp = *this; --*this; return temp; }
102  constexpr auto& operator+=(const difference_type diff) noexcept { current_ += diff; return *this; }
103  constexpr auto& operator-=(const difference_type diff) noexcept { current_ -= diff; return *this; }
104  friend constexpr auto operator+(const iterator& it, const difference_type diff) noexcept
105  { return iterator {*it.parent_, it.current_ + diff}; }
106  friend constexpr auto operator+(const difference_type diff, const iterator& it) noexcept
107  { return iterator {*it.parent_, diff + it.current_}; }
108  friend constexpr auto operator-(const iterator& it, const difference_type diff)
109  { return iterator {*it.parent_, it.current_ - diff}; }
110  friend constexpr difference_type operator-(const iterator& it, const iterator& other) noexcept
111  { return it.current_ - other.current_; }
112  friend constexpr bool operator==(const iterator& it, const iterator& other) noexcept
113  { return it.current_ == other.current_; }
114 #ifdef __cpp_impl_three_way_comparison
115  constexpr auto operator<=>(const iterator& other) const noexcept { return current_ <=> other.current_; }
116 #else
117  constexpr bool operator!=(const iterator& other) const noexcept { return current_ != other.current_; }
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 #endif
123 
124  private:
125 
126  Parent * parent_;
127  difference_type current_;
128 
129  };
130 
131 
135 #ifdef __cpp_concepts
136  constexpr
137  replicate_view() = default;
138 #else
139  template<bool Enable = true, std::enable_if_t<Enable and
140  stdex::default_initializable<V> and stdex::default_initializable<Factor>, int> = 0>
141  constexpr
143 #endif
144 
145 
149  constexpr
150  replicate_view(V& v, Factor f) : v_ {v}, f_ {std::move(f)} {}
151 
152 
154  constexpr
155  replicate_view(V&& v, Factor f) : v_ {std::move(v)}, f_ {std::move(f)} {}
156 
157 
161 #ifdef __cpp_explicit_this_parameter
162  constexpr decltype(auto)
163  base(this auto&& self) noexcept { return std::forward<decltype(self)>(self).v_; }
164 #else
165  constexpr V& base() & { return this->v_; }
166  constexpr const V& base() const & { return this->v_; }
167  constexpr V&& base() && noexcept { return std::move(*this).v_; }
168  constexpr const V&& base() const && noexcept { return std::move(*this).v_; }
169 #endif
170 
171 
175 #ifdef __cpp_concepts
176  constexpr auto
177  begin() requires stdex::ranges::range<const V>
178 #else
179  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
180  constexpr auto begin()
181 #endif
182  { return iterator<false> {*this, 0_uz}; }
183 
184 
185 #ifdef __cpp_concepts
186  constexpr auto
187  begin() const requires stdex::ranges::range<const V>
188 #else
189  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
190  constexpr auto begin() const
191 #endif
192  { return iterator<true> {*this, 0_uz}; }
193 
194 
198 #ifdef __cpp_concepts
199  constexpr auto
200  end() requires stdex::ranges::range<const V>
201 #else
202  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
203  constexpr auto
204  end()
205 #endif
206  {
207  return iterator<false> {*this, static_cast<std::ptrdiff_t>(values::to_value_type(size()))};
208  }
209 
210 
212 #ifdef __cpp_concepts
213  constexpr auto
214  end() const requires stdex::ranges::range<const V>
215 #else
216  template<bool Enable = true, std::enable_if_t<Enable and stdex::ranges::range<const V>, int> = 0>
217  constexpr auto
218  end() const
219 #endif
220  {
221  return iterator<true> {*this, static_cast<std::ptrdiff_t>(values::to_value_type(size()))};
222  }
223 
224 
228 #ifdef __cpp_concepts
229  constexpr values::index auto size() const
230 #else
231  constexpr auto size() const
232 #endif
233  {
234  return values::operation(std::multiplies{}, get_size(v_), f_);
235  }
236 
237 
241 #ifdef __cpp_explicit_this_parameter
242  template<std::size_t i>
243  constexpr decltype(auto)
244  get(this auto&& self) noexcept
245  {
246  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
247  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
248  return collections::get_element(std::forward<decltype(self)>(self).v_,
249  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(self.v_)));
250  }
251 #else
252  template<std::size_t i>
253  constexpr decltype(auto)
254  get() &
255  {
256  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
257  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
258  return collections::get_element(v_,
259  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(v_)));
260  }
261 
262  template<std::size_t i>
263  constexpr decltype(auto)
264  get() const &
265  {
266  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
267  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
268  return collections::get_element(v_,
269  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(v_)));
270  }
271 
272  template<std::size_t i>
273  constexpr decltype(auto)
274  get() && noexcept
275  {
276  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
277  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
278  return collections::get_element(std::move(*this).v_,
279  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(std::move(*this).v_)));
280  }
281 
282  template<std::size_t i>
283  constexpr decltype(auto)
284  get() const && noexcept
285  {
286  if constexpr (size_of_v<V> != stdex::dynamic_extent and values::fixed<Factor>)
287  static_assert(i < size_of_v<V> * values::fixed_value_of_v<Factor>, "Index out of range");
288  return collections::get_element(std::move(*this).v_,
289  values::operation(std::modulus{}, std::integral_constant<std::size_t, i>{}, get_size(std::move(*this).v_)));
290  }
291 #endif
292 
293  private:
294 
295  V v_;
296  Factor f_;
297  };
298 
299 
303  template<typename V, typename F>
304  replicate_view(const V&, const F&) -> replicate_view<V, F>;
305 
306 
307 }
308 
309 
310 #ifdef __cpp_lib_ranges
311 namespace std::ranges
312 #else
313 namespace OpenKalman::stdex::ranges
314 #endif
315 {
316  template<typename V, typename F>
317  constexpr bool enable_borrowed_range<OpenKalman::collections::replicate_view<V, F>> = enable_borrowed_range<V>;
318 }
319 
320 
321 #ifndef __cpp_lib_ranges
323 {
324  template<typename V, typename F, typename = void>
326 
327  template<typename V, typename F>
328  struct replicate_tuple_size<V, F, std::enable_if_t<values::fixed<F> and (size_of<V>::value != stdex::dynamic_extent)>>
329  : std::integral_constant<std::size_t, size_of_v<V> * values::fixed_value_of_v<F>> {};
330 
331 
332  template<std::size_t i, typename V, typename = void>
334  {
335  using type = stdex::ranges::range_value_t<V>;
336  };
337 
338  template<std::size_t i, typename V>
339  struct replicate_tuple_element<i, V, std::enable_if_t<size_of<V>::value != stdex::dynamic_extent>>
340  : collection_element<i % size_of_v<V>, std::decay_t<V>> {};
341 
342 }
343 #endif
344 
345 
346 namespace std
347 {
348 #ifdef __cpp_lib_ranges
349  template<typename V, OpenKalman::values::fixed F> requires (OpenKalman::collections::size_of_v<V> != OpenKalman::stdex::dynamic_extent)
350  struct tuple_size<OpenKalman::collections::replicate_view<V, F>>
351  : integral_constant<std::size_t, OpenKalman::collections::size_of_v<V> * OpenKalman::values::fixed_value_of_v<F>> {};
352 #else
353  template<typename V, typename F>
354  struct tuple_size<OpenKalman::collections::replicate_view<V, F>>
356 #endif
357 
358 
359 #ifdef __cpp_lib_ranges
360  template<size_t i, typename V, typename F> requires (OpenKalman::collections::size_of_v<V> != OpenKalman::stdex::dynamic_extent)
361  struct tuple_element<i, OpenKalman::collections::replicate_view<V, F>>
362  : tuple_element<i % OpenKalman::collections::size_of_v<V>, decay_t<V>> {};
363 
364  template<size_t i, typename V, typename F> requires (OpenKalman::collections::size_of_v<V> == OpenKalman::stdex::dynamic_extent)
365  struct tuple_element<i, OpenKalman::collections::replicate_view<V, F>>
366  {
367  using type = OpenKalman::stdex::ranges::range_value_t<V>;
368  };
369 #else
370  template<size_t i, typename V, typename F>
371  struct tuple_element<i, OpenKalman::collections::replicate_view<V, F>>
373 #endif
374 
375 }
376 
377 
379 {
380  namespace detail
381  {
382  template<typename Factor>
383  struct replicate_closure : stdex::ranges::range_adaptor_closure<replicate_closure<Factor>>
384  {
385  constexpr replicate_closure(Factor f) : factor_ {std::move(f)} {};
386 
387 #ifdef __cpp_concepts
388  template<collection R>
389 #else
390  template<typename R, std::enable_if_t<collection<R>, int> = 0>
391 #endif
392  constexpr auto
393  operator() (R&& r) const
394  {
395  if constexpr (viewable_collection<R>)
396  return replicate_view {all(std::forward<R>(r)), factor_};
397  else
398  return replicate_view {std::forward<R>(r), factor_};
399  }
400 
401  private:
402 
403  Factor factor_;
404  };
405 
406 
408  {
409 #ifdef __cpp_concepts
410  template<values::index Factor>
411 #else
412  template<typename Factor, std::enable_if_t<values::index<Factor>, int> = 0>
413 #endif
414  constexpr auto
415  operator() (Factor factor) const
416  {
417  return replicate_closure<Factor> {std::move(factor)};
418  }
419 
420 
421 #ifdef __cpp_concepts
422  template<collection R, values::index Factor>
423 #else
424  template<typename R, typename Factor, std::enable_if_t<collection<R> and values::index<Factor>, int> = 0>
425 #endif
426  constexpr auto
427  operator() (R&& r, Factor factor) const
428  {
429  if constexpr (viewable_collection<R>)
430  return replicate_view {all(std::forward<R>(r)), std::move(factor)};
431  else
432  return replicate_view {std::forward<R>(r), std::move(factor)};
433  }
434 
435  };
436 
437  }
438 
439 
447 
448 }
449 
450 
451 #endif
constexpr auto begin()
Definition: replicate.hpp:180
Namespace for collections.
Definition: collections.hpp:27
Definition for collections::collection.
constexpr replicate_view()
Default constructor.
Definition: replicate.hpp:142
constexpr auto end()
Definition: replicate.hpp:204
constexpr replicate_view(V &v, Factor f)
Construct from a collection.
Definition: replicate.hpp:150
constexpr auto get_size(Arg &&arg)
Get the size of a sized object (e.g, a collection)
Definition: get_size.hpp:224
Header file for code relating to values (e.g., scalars and indices)
Iterator for replicate_view.
Definition: replicate.hpp:58
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:165
constexpr replicate_view(V &&v, Factor f)
Definition: replicate.hpp:155
Definition: view_interface.hpp:32
Definition for collections::get_size.
Definition for collections::get_element.
constexpr detail::replicate_adaptor replicate
a std::ranges::range_adaptor_closure for a set of replicated pattern objects.
Definition: replicate.hpp:86
A view that replicates a collection some number of times.
Definition: replicate.hpp:45
decltype(auto) constexpr get() &
Get element i.
Definition: replicate.hpp:254
constexpr auto size() const
Definition: replicate.hpp:231
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:218
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:124
Definition: gettable.hpp:24
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