Skip to content

Commit a3146fc

Browse files
committed
add outer class
1 parent b98dcce commit a3146fc

3 files changed

Lines changed: 158 additions & 32 deletions

File tree

Lines changed: 125 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
// https://www.boost.org/LICENSE_1_0.txt
44

55
// #pragma once
6-
#ifndef BOOST_MULTI_DETAIL_EXTENTS_HPP
7-
#define BOOST_MULTI_DETAIL_EXTENTS_HPP
6+
#ifndef BOOST_MULTI_DETAIL_OUTER_HPP
7+
#define BOOST_MULTI_DETAIL_OUTER_HPP
88

99
#include "boost/multi/detail/index_range.hpp" // IWYU pragma: export // for index_extension, extension_t, tuple, intersection, range, operator!=, operator==
1010
#include "boost/multi/detail/interfaces.hpp"
@@ -77,26 +77,93 @@ template<class T, class... Ts> struct tuple_tail<std::tuple<T, Ts...>> {
7777
template<class Tuple> using tuple_type_t = typename tuple_tail<Tuple>::type;
7878
} // end namespace stdx
7979

80-
// namespace detail {
81-
// template<typename... Ts>
82-
// struct TailTuple {};
80+
template<class... Exts>
81+
class outer_t;
8382

84-
// template<typename Head, typename... Tail>
85-
// struct TailTuple<Head, Tail...> : TailTuple<Tail...> {
86-
// using TailType = TailTuple<Tail...>;
87-
// };
83+
template<class Outer>
84+
class outer_elements_t; // lazy random-access range over the flattened coordinate tuples (defined below)
8885

89-
// // Base case for the empty pack
90-
// template<>
91-
// struct TailTuple<> {};
86+
template<>
87+
class outer_t<> : public std::tuple<> { // base case: zero-dimensional extents
88+
public:
89+
static constexpr dimensionality_type dimensionality = 0;
9290

93-
// } // end namespace detail
91+
using index = multi::index;
92+
using indices_type = std::tuple<>;
93+
using element = std::tuple<>;
9494

95-
template<class... Exts>
96-
class extents_t;
95+
outer_t() = default;
96+
97+
constexpr auto num_elements() const noexcept -> multi::ssize_t { return 1; } // NOLINT(readability-convert-member-functions-to-static)
98+
99+
constexpr auto from_linear(index /*n*/) const noexcept -> indices_type { return {}; } // NOLINT(readability-convert-member-functions-to-static)
100+
constexpr auto sizes() const noexcept -> std::tuple<> { return {}; } // NOLINT(readability-convert-member-functions-to-static)
101+
102+
friend constexpr auto operator==(outer_t const& /*self*/, outer_t const& /*other*/) noexcept -> bool { return true; }
103+
friend constexpr auto operator!=(outer_t const& /*self*/, outer_t const& /*other*/) noexcept -> bool { return false; }
104+
};
105+
106+
// A lazy, random-access, sized range over the flattened coordinate tuples of an `outer_t`:
107+
// `outer.elements()[n] == outer.from_linear(n)`. This is what makes `outer` behave like an
108+
// array of (coordinate) tuples -- the cartesian product materialized lazily, element by element.
109+
template<class Outer>
110+
class outer_elements_t {
111+
Outer outer_;
112+
113+
public:
114+
using value_type = typename Outer::indices_type;
115+
using size_type = multi::ssize_t;
116+
using difference_type = std::ptrdiff_t;
117+
118+
explicit constexpr outer_elements_t(Outer outer) : outer_{std::move(outer)} {}
119+
120+
constexpr auto size() const -> size_type { return outer_.num_elements(); }
121+
constexpr auto operator[](difference_type n) const -> value_type { return outer_.from_linear(static_cast<typename Outer::index>(n)); }
122+
123+
class iterator {
124+
Outer outer_;
125+
std::ptrdiff_t n_ = 0;
126+
127+
public:
128+
using value_type = typename Outer::indices_type;
129+
using reference = value_type; // proxy: dereference yields a fresh coordinate tuple by value
130+
using pointer = void;
131+
using difference_type = std::ptrdiff_t;
132+
using iterator_category = std::random_access_iterator_tag;
133+
134+
iterator() = default;
135+
constexpr iterator(Outer outer, difference_type n) : outer_{std::move(outer)}, n_{n} {}
136+
137+
constexpr auto operator*() const -> value_type { return outer_.from_linear(static_cast<typename Outer::index>(n_)); }
138+
constexpr auto operator[](difference_type d) const -> value_type { return *(*this + d); }
139+
140+
constexpr auto operator++() -> iterator& { ++n_; return *this; }
141+
constexpr auto operator--() -> iterator& { --n_; return *this; }
142+
constexpr auto operator++(int) -> iterator { auto ret = *this; ++n_; return ret; } // NOLINT(cert-dcl21-cpp)
143+
constexpr auto operator--(int) -> iterator { auto ret = *this; --n_; return ret; } // NOLINT(cert-dcl21-cpp)
144+
145+
constexpr auto operator+=(difference_type d) -> iterator& { n_ += d; return *this; }
146+
constexpr auto operator-=(difference_type d) -> iterator& { n_ -= d; return *this; }
147+
148+
friend constexpr auto operator+(iterator it, difference_type d) -> iterator { it += d; return it; }
149+
friend constexpr auto operator+(difference_type d, iterator it) -> iterator { it += d; return it; }
150+
friend constexpr auto operator-(iterator it, difference_type d) -> iterator { it -= d; return it; }
151+
friend constexpr auto operator-(iterator const& lhs, iterator const& rhs) -> difference_type { return lhs.n_ - rhs.n_; }
152+
153+
friend constexpr auto operator==(iterator const& lhs, iterator const& rhs) -> bool { return lhs.n_ == rhs.n_; }
154+
friend constexpr auto operator!=(iterator const& lhs, iterator const& rhs) -> bool { return lhs.n_ != rhs.n_; }
155+
friend constexpr auto operator< (iterator const& lhs, iterator const& rhs) -> bool { return lhs.n_ < rhs.n_; }
156+
friend constexpr auto operator> (iterator const& lhs, iterator const& rhs) -> bool { return lhs.n_ > rhs.n_; }
157+
friend constexpr auto operator<=(iterator const& lhs, iterator const& rhs) -> bool { return lhs.n_ <= rhs.n_; }
158+
friend constexpr auto operator>=(iterator const& lhs, iterator const& rhs) -> bool { return lhs.n_ >= rhs.n_; }
159+
};
160+
161+
constexpr auto begin() const -> iterator { return iterator{outer_, 0}; }
162+
constexpr auto end() const -> iterator { return iterator{outer_, size()}; }
163+
};
97164

98165
template<class Ext, class... Exts>
99-
class extents_t<Ext, Exts...> : public std::tuple<Ext, Exts...> { // TODO(correaa) use libcuda++ in the future https://github.com/boostorg/math/blob/develop/include/boost/math/tools/cstdint.hpp
166+
class outer_t<Ext, Exts...> : public std::tuple<Ext, Exts...> { // TODO(correaa) use libcuda++ in the future https://github.com/boostorg/math/blob/develop/include/boost/math/tools/cstdint.hpp
100167
using base_ = std::tuple<Ext, Exts...>;
101168

102169
public:
@@ -111,9 +178,9 @@ class extents_t<Ext, Exts...> : public std::tuple<Ext, Exts...> { // TODO(corre
111178
using size_type = typename extension_type::size_type;
112179
using index = typename extension_type::index;
113180

114-
extents_t() = default;
181+
outer_t() = default;
115182
// using std::tuple<Exts...>::tuple;
116-
explicit extents_t(Ext ext, Exts... xs) : std::tuple<Ext, Exts...>{std::move(ext), std::move(xs)...} {}
183+
explicit outer_t(Ext ext, Exts... xs) : std::tuple<Ext, Exts...>{std::move(ext), std::move(xs)...} {}
117184

118185
auto extension() const -> extension_type {
119186
using std::get;
@@ -126,15 +193,45 @@ class extents_t<Ext, Exts...> : public std::tuple<Ext, Exts...> { // TODO(corre
126193

127194
auto size() const -> size_type { return extension().size(); }
128195

129-
using sub_type = extents_t<>;
196+
auto num_elements() const { return size() * sub().num_elements(); }
197+
198+
using sub_type = outer_t<Exts...>;
199+
200+
constexpr auto sub() const -> sub_type {
201+
return std::apply(
202+
[](auto... xs) noexcept -> sub_type { return sub_type(xs...); },
203+
stdx::tail(static_cast<std::tuple<Ext, Exts...> const&>(*this))
204+
);
205+
}
206+
207+
// the coordinate type produced when fully indexing: a tuple of `dimensionality + 1` indices
208+
using indices_type = decltype(std::tuple_cat(std::declval<std::tuple<index>>(), std::declval<typename sub_type::indices_type>()));
209+
210+
// row-major decomposition of a linear index `n` into the full coordinate tuple
211+
constexpr auto from_linear(index n) const -> indices_type {
212+
auto const sub_num = sub().num_elements();
213+
return std::tuple_cat(
214+
std::make_tuple(static_cast<index>(extension().first() + (n / sub_num))),
215+
sub().from_linear(static_cast<index>(n % sub_num))
216+
);
217+
}
218+
219+
// the structured factors of the cartesian product (the D extensions, as a tuple)
220+
constexpr auto extensions() const -> element { return static_cast<base_ const&>(*this); }
221+
222+
// the per-dimension sizes
223+
constexpr auto sizes() const { return std::tuple_cat(std::make_tuple(size()), sub().sizes()); }
224+
225+
// lazy random-access array of the `num_elements()` coordinate tuples (cartesian product, flattened)
226+
constexpr auto elements() const -> outer_elements_t<outer_t> { return outer_elements_t<outer_t>{*this}; }
130227

131228
class iterator : public detail::forward_iterator_facade<iterator> {
132-
friend class extents_t;
229+
friend class outer_t;
133230

134231
typename extension_type::iterator idx_;
135-
extents_t<Exts...> rest_;
232+
outer_t<Exts...> rest_;
136233

137-
constexpr explicit iterator(typename extension_type::iterator idx, extents_t<Exts...> rest)
234+
constexpr explicit iterator(typename extension_type::iterator idx, outer_t<Exts...> rest)
138235
: idx_{idx}, rest_{std::move(rest)} {}
139236

140237
public:
@@ -204,28 +301,28 @@ class extents_t<Ext, Exts...> : public std::tuple<Ext, Exts...> { // TODO(corre
204301
constexpr auto begin() const noexcept {
205302
return iterator{
206303
stdx::head(static_cast<std::tuple<Ext, Exts...> const&>(*this)).begin(),
207-
std::apply([](auto... xs) noexcept -> auto { return extents_t<Exts...>(xs...); }, stdx::tail(static_cast<std::tuple<Ext, Exts...> const&>(*this)))
304+
std::apply([](auto... xs) noexcept -> auto { return outer_t<Exts...>(xs...); }, stdx::tail(static_cast<std::tuple<Ext, Exts...> const&>(*this)))
208305
};
209306
}
210307

211308
template<std::size_t I>
212-
friend auto get(extents_t const& self) {
309+
friend auto get(outer_t const& self) {
213310
using std::get;
214311
return get<I>(static_cast<std::tuple<Ext, Exts...> const&>(self));
215312
}
216313
};
217314

218-
template<class... Exts> extents_t(Exts...) -> extents_t<decltype(multi::extension_t(std::declval<Exts>()))...>;
315+
template<class... Exts> outer_t(Exts...) -> outer_t<decltype(multi::extension_t(std::declval<Exts>()))...>;
219316

220317
} // end namespace boost::multi
221318

222319
template<class... Exts>
223-
struct std::tuple_size<::boost::multi::extents_t<Exts...>> { // NOLINT(cert-dcl58-cpp) structured binding
320+
struct std::tuple_size<::boost::multi::outer_t<Exts...>> { // NOLINT(cert-dcl58-cpp) structured binding
224321
static constexpr std::size_t value = sizeof...(Exts);
225322
};
226323

227324
template<std::size_t I, class... Exts>
228-
struct std::tuple_element<I, ::boost::multi::extents_t<Exts...>> { // NOLINT(cert-dcl58-cpp) structured binding
325+
struct std::tuple_element<I, ::boost::multi::outer_t<Exts...>> { // NOLINT(cert-dcl58-cpp) structured binding
229326
using type = std::tuple_element_t<I, std::tuple<Exts...>>;
230327
};
231328

@@ -2134,4 +2231,4 @@ struct std::tuple_element<I, ::boost::multi::extents_t<Exts...>> { // NOLINT(ce
21342231

21352232
#undef BOOST_MULTI_HD
21362233

2137-
#endif // BOOST_MULTI_DETAIL_EXTENTS_HPP
2234+
#endif // BOOST_MULTI_DETAIL_OUTER_HPP

test/extents.cpp renamed to test/outer.cpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// https://www.boost.org/LICENSE_1_0.txt
44

55
#include <boost/multi/array.hpp>
6-
#include <boost/multi/detail/extents.hpp>
6+
#include <boost/multi/detail/outer.hpp>
77

88
#include <boost/core/lightweight_test.hpp> // IWYU pragma: keep
99

@@ -17,15 +17,15 @@ auto main() -> int { // NOLINT(bugprone-exception-escape,readability-function-c
1717
{
1818
multi::array<int, 1> const arr1d(3);
1919

20-
auto const x1d = multi::extents_t(arr1d.extension());
20+
auto const x1d = multi::outer_t(arr1d.extension());
2121

2222
BOOST_TEST( x1d.size() == 3 );
2323

24-
auto const y1d = multi::extents_t(3);
24+
auto const y1d = multi::outer_t(3);
2525
BOOST_TEST( y1d.size() == 3 );
2626
}
2727
{
28-
multi::extents_t const x2d(4, 3);
28+
multi::outer_t const x2d(4, 3);
2929
BOOST_TEST( x2d.size() == 4 );
3030
auto [x0, x1] = x2d;
3131

@@ -51,6 +51,32 @@ auto main() -> int { // NOLINT(bugprone-exception-escape,readability-function-c
5151
auto x2d_it3 = x2d_it2 + 1;
5252
BOOST_TEST( x2d_it3 == x2d.begin() + 3 );
5353
}
54+
{
55+
// outer_t behaves like a (lazy) array of coordinate tuples
56+
multi::outer_t const x2d(2, 3); // 2 x 3 structured cartesian product
57+
58+
BOOST_TEST( x2d.num_elements() == 6 );
59+
60+
auto const els = x2d.elements(); // lazy random-access range of (i, j) tuples
61+
BOOST_TEST( els.size() == 6 );
62+
63+
using std::get;
64+
// row-major order: (0,0)(0,1)(0,2)(1,0)(1,1)(1,2)
65+
BOOST_TEST(( els[0] == std::make_tuple(0, 0) ));
66+
BOOST_TEST(( els[1] == std::make_tuple(0, 1) ));
67+
BOOST_TEST(( els[3] == std::make_tuple(1, 0) ));
68+
BOOST_TEST(( els[5] == std::make_tuple(1, 2) ));
69+
70+
// random-access iteration
71+
auto it = els.begin();
72+
BOOST_TEST(( *it == std::make_tuple(0, 0) ));
73+
BOOST_TEST(( it[4] == std::make_tuple(1, 1) ));
74+
BOOST_TEST( els.end() - els.begin() == 6 );
75+
BOOST_TEST(( *(els.begin() + 5) == std::make_tuple(1, 2) ));
76+
77+
// sizes() reports per-dimension lengths
78+
BOOST_TEST(( x2d.sizes() == std::make_tuple(2, 3) ));
79+
}
5480
{
5581
multi::array<int, 3> const arr({2, 3, 5});
5682
auto [is, js, ks] = arr.extents();

test/ranges_zip.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ auto main() -> int { // NOLINT(bugprone-exception-escape)
8383
multi::array<int, 2> arr1({10, 10}, 5);
8484
multi::array<int, 2> arr2({10, 10}, 6);
8585

86+
auto&& arr1ro0 = arr1[0];
87+
BOOST_TEST( arr1ro0[0] == 5 );
88+
8689
auto&& es1 = arr1().elements();
8790
auto&& es2 = arr2().elements();
8891

0 commit comments

Comments
 (0)