Skip to content

Commit 8a08f22

Browse files
committed
Provide separate at(), at_view()
This fixes an inconsistency where at(view, i) could return any prefix slice, but at(iter, i) only returned elements; moreover, at(view, i) could be a lot slower than at(start(view), i) because the rank of the result depended on the length of i, which can be dynamic. * ra/small.hh (ViewSmall::at): Do not support slicing; require size(i)==rank(a). * ra/big.hh (ViewBig::at): Idem. * ra/ra.hh (at_view): Redefine, including rank extension. (at): Define rank extension of expr.at(). * docs/ra-ra.texi (at): Update. (at_view): New defun. * test/at.cc: Examples from doc. Move misc at() tests here from fromu.cc. * test/at.cc, test/ra-0.cc, test/ra-11.cc, test/small-1.cc, test/operators.cc: Fix tests that assumed the old behavior of at() to use at_view() instead.
1 parent 0c6e24a commit 8a08f22

File tree

13 files changed

+244
-129
lines changed

13 files changed

+244
-129
lines changed

bench/bench-at.cc

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,17 +128,17 @@ int main(int argc, char * * argv)
128128
test1(smola, smoli, reps, "small/small");
129129
test1(bigsa, smoli, reps, "bigs/small");
130130
test1(bigda, smoli, reps, "bigd/small");
131+
test1(iotav, smoli, reps, "iotv/small");
132+
test1(iotai, smoli, reps, "ioti/small");
131133
test1(smola, bigsi, reps, "small/bigs");
132134
test1(bigsa, bigsi, reps, "bigs/bigs");
133135
test1(bigda, bigsi, reps, "bigd/bigs");
136+
test1(iotav, bigsi, reps, "iotv/bigs");
137+
test1(iotai, bigsi, reps, "ioti/bigs");
134138
test1(smola, bigdi, reps, "small/bigd");
135139
test1(bigsa, bigdi, reps, "bigs/bigd");
136140
test1(bigda, bigdi, reps, "bigd/bigd");
137-
test1(iotav, smoli, reps, "iotv/small");
138-
test1(iotav, bigsi, reps, "iotv/bigs");
139141
test1(iotav, bigdi, reps, "iotv/bigd");
140-
test1(iotai, smoli, reps, "ioti/small");
141-
test1(iotai, bigsi, reps, "ioti/bigs");
142142
test1(iotai, bigdi, reps, "ioti/bigd");
143143
}
144144
return tr.summary();

docs/index.html

Lines changed: 50 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/ra-ra.texi

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,7 +1390,7 @@ cout << fmt(ra::pstyle, A) << endl;
13901390
@end verbatim
13911391
@end example
13921392

1393-
@code{jstyle} is the only style parsed by @code{ra::operator>>(std::istream &)}. See also @ref{x-fmt,fmt}.
1393+
@code{jstyle} is the only style parsed by @code{ra::operator>>(std::istream &)}. See also @ref{x-fmt,@code{fmt}}.
13941394

13951395
For @code{std::format}, the following specifiers are available:
13961396

@@ -2191,16 +2191,73 @@ This differs from @ref{x-agree,@code{agree}} when @var{op} has non-zero argument
21912191
Look up @var{expr} at each element of @var{indices}, which shall be a multi-index into @var{expr}.
21922192

21932193
This can be used for sparse subscripting. For example:
2194-
@example @c [ra30]
2194+
@example @c [ma30]
21952195
@verbatim
21962196
ra::Big<int, 2> A = {{100, 101}, {110, 111}, {120, 121}};
21972197
ra::Big<ra::Small<int, 2>, 2> i = {{{0, 1}, {2, 0}}, {{1, 0}, {2, 1}}};
21982198
ra::Big<int, 2> B = at(A, i);
21992199
@end verbatim
22002200
@result{} B = @{@{101, 120@}, @{110, 121@}@}
22012201
@end example
2202+
2203+
See also @ref{x-at-view,@code{at_view}}.
22022204
@end defun
22032205

2206+
@cindex @code{at_view}
2207+
@anchor{x-at-view} @defun at_view slice indices
2208+
Like @ref{x-at,@code{at}}, but the indices can be shorter than the rank of @var{slice}; in that case, return the corresponding prefix slice.
2209+
2210+
@example @c [ma31]
2211+
@verbatim
2212+
ra::Big<int, 2> A = {{100, 101}, {110, 111}, {120, 121}};
2213+
cout << at_view(A, ra::Small<int, 0> {}) << endl;
2214+
@end verbatim
2215+
@print{} @{@{100, 101@}, @{110, 111@}, @{120, 121@}@}
2216+
@verbatim
2217+
cout << at_view(A, ra::Small<int, 1> {2}) << endl;
2218+
@end verbatim
2219+
@print{} @{120, 121@}
2220+
@verbatim
2221+
cout << at_view(A, ra::Small<int, 2> {2, 0}) << endl;
2222+
@end verbatim
2223+
@print{} 120
2224+
@end example
2225+
2226+
In principle, this function makes @ref{x-at,@code{at}} redundant. We provide both because 1) @code{at_view} only works on slices, 2) if the length of @var{indices} is dynamic, @code{at_view} is forced to return a dynamic rank slice, which is a lot slower.
2227+
2228+
@example @c [ma32]
2229+
@verbatim
2230+
ra::Big<int, 2> A = {{100, 101}, {110, 111}, {120, 121}};
2231+
ra::Big<int, 2> I = {{0, 1}, {2, 0}, {1, 0}};
2232+
2233+
// error if 2!=I.len(1). Iterates over scalars
2234+
cout << at(A, I.iter<1>()) << endl;
2235+
@end verbatim
2236+
@print{}
2237+
3
2238+
101 120 110
2239+
@verbatim
2240+
// L=I.len(1) can be 0, 1, 2. Iterates over dynamic rank (2-L)-views
2241+
cout << at_view(A, I.iter<1>()) << endl;
2242+
@end verbatim
2243+
@print{}
2244+
3
2245+
0
2246+
2247+
101 0
2248+
2249+
120 0
2250+
2251+
110
2252+
@end example
2253+
2254+
This shows the default printing of dynamic rank arrays, which is to print the rank (0), then the shape (), then the contents. The first (3) is the shape of the overall expression, which is that of @code{I.iter<1>()} and has static rank 1.
2255+
2256+
See also @ref{x-at,@code{at}}.
2257+
2258+
@end defun
2259+
2260+
22042261
@cindex @code{begin}
22052262
@anchor{x-begin} @defun begin expr
22062263
Create STL iterator from @var{expr}.

ra/big.hh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ struct ViewBig
124124
constexpr decltype(auto) back() const { dim_t s=size(); RA_CK(s>0, "Bad back()."); return cp[s-1]; }
125125
constexpr decltype(auto) operator()(this auto && self, auto && ... i) { return from(RA_FW(self), RA_FW(i) ...); }
126126
constexpr decltype(auto) operator[](this auto && self, auto && ... i) { return from(RA_FW(self), RA_FW(i) ...); }
127-
constexpr decltype(auto) at(auto const & i) const { return at_view(*this, i); }
127+
constexpr decltype(auto) at(auto const & i) const { return *indexer(*this, cp, start(i)); }
128128
constexpr operator decltype(*cp) () const { return to_scalar(*this); }
129129
// conversion to const, used by Container::view(). FIXME cf Small
130130
constexpr operator ViewBig<reconst<P>, RANK> const & () const requires (!std::is_void_v<reconst<P>>)
@@ -323,6 +323,7 @@ struct Container: public ViewBig<typename storage_traits<Store>::T *, RANK>
323323
constexpr auto begin(this auto && self) { assert(is_c_order(self.view())); return self.view().data(); }
324324
constexpr auto end(this auto && self) { return self.view().data()+self.size(); }
325325
template <rank_t c=0> constexpr auto iter(this auto && self) { if constexpr (1==RANK && 0==c) { return ptr(self.data(), self.size()); } else { return RA_FW(self).view().template iter<c>(); } }
326+
constexpr auto iter(this auto && self, rank_t c) { return RA_FW(self).view().iter(c); }
326327
constexpr operator T & () { return view(); }
327328
constexpr operator T const & () const { return view(); }
328329
};

ra/ra.hh

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,31 @@ pack(auto && ... a)
289289
return map([](auto && ... a){ return T { RA_FW(a) ... }; }, RA_FW(a) ...);
290290
}
291291

292-
// needs nested array for I, but one can use iter<-1> with rank 2
293292
template <class A>
294-
constexpr auto
295-
at(A && a, auto && i)
293+
constexpr decltype(auto)
294+
at(A && a, auto const & i) requires (Slice<std::decay_t<A>> || Iterator<std::decay_t<A>>)
295+
{
296+
if constexpr (0==rank_s<decltype(VAL(i))>()) {
297+
return a.at(i);
298+
} else {
299+
return map([a=std::tuple<A>(RA_FW(a))](auto && i) -> decltype(auto) { return at(std::get<0>(a), i); }, RA_FW(i));
300+
}
301+
}
302+
303+
template <Slice A>
304+
constexpr decltype(auto)
305+
at_view(A && a, auto && i)
296306
{
297-
return map([a=std::tuple<A>(RA_FW(a))](auto && i) -> decltype(auto) { return std::get<0>(a).at(i); }, RA_FW(i));
307+
if constexpr (0==rank_s<decltype(VAL(i))>()) {
308+
// can't say 'frame rank 0' so -size wouldn't work. FIXME What about ra::len
309+
if constexpr (constexpr rank_t cr = rank_diff(rank_s(a), ra::size_s(i)); ANY==cr) {
310+
return a.template iter(rank(a)-ra::size(i)).at(i);
311+
} else {
312+
return a.template iter<cr>().at(i);
313+
}
314+
} else {
315+
return map([a=std::tuple<A>(RA_FW(a))](auto && i) -> decltype(auto) { return at_view(std::get<0>(a), i); }, RA_FW(i));
316+
}
298317
}
299318

300319

@@ -304,42 +323,28 @@ at(A && a, auto && i)
304323

305324
template <class T, class F> requires (toreduce<T, F>)
306325
constexpr decltype(auto)
307-
where(bool const w, T && t, F && f)
308-
{
309-
return w ? VAL(t) : VAL(f);
310-
}
326+
where(bool const w, T && t, F && f) { return w ? VAL(t) : VAL(f); }
327+
311328
template <class W, class T, class F> requires (tomap<W, T, F>)
312329
constexpr auto
313-
where(W && w, T && t, F && f)
314-
{
315-
return pick(cast<bool>(RA_FW(w)), RA_FW(f), RA_FW(t));
316-
}
330+
where(W && w, T && t, F && f) { return pick(cast<bool>(RA_FW(w)), RA_FW(f), RA_FW(t)); }
331+
317332
// catch all for non-ra types.
318333
template <class T, class F> requires (!(tomap<T, F>) && !(toreduce<T, F>))
319334
constexpr decltype(auto)
320-
where(bool const w, T && t, F && f)
321-
{
322-
return w ? t : f;
323-
}
335+
where(bool const w, T && t, F && f) { return w ? t : f; }
324336

325337
template <class A, class B> requires (tomap<A, B>)
326338
constexpr auto
327-
operator &&(A && a, B && b)
328-
{
329-
return where(RA_FW(a), cast<bool>(RA_FW(b)), false);
330-
}
339+
operator &&(A && a, B && b) { return where(RA_FW(a), cast<bool>(RA_FW(b)), false); }
340+
331341
template <class A, class B> requires (tomap<A, B>)
332342
constexpr auto
333-
operator ||(A && a, B && b)
334-
{
335-
return where(RA_FW(a), true, cast<bool>(RA_FW(b)));
336-
}
343+
operator ||(A && a, B && b) { return where(RA_FW(a), true, cast<bool>(RA_FW(b))); }
344+
337345
#define DEF_SHORTCIRCUIT_BINARY_OP(OP) \
338346
template <class A, class B> requires (toreduce<A, B>) \
339-
constexpr auto operator OP(A && a, B && b) \
340-
{ \
341-
return VAL(a) OP VAL(b); \
342-
}
347+
constexpr auto operator OP(A && a, B && b) { return VAL(a) OP VAL(b); }
343348
FOR_EACH(DEF_SHORTCIRCUIT_BINARY_OP, &&, ||)
344349
#undef DEF_SHORTCIRCUIT_BINARY_OP
345350

ra/small.hh

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -337,17 +337,6 @@ from(A && a, auto && ... i)
337337
}
338338
}
339339

340-
constexpr decltype(auto)
341-
at_view(auto const & a, auto const & i)
342-
{
343-
// can't say 'frame rank 0' so -size wouldn't work. FIXME What about ra::len
344-
if constexpr (constexpr rank_t cr = rank_diff(rank_s(a), ra::size_s(i)); ANY==cr) {
345-
return a.template iter(rank(a)-ra::size(i)).at(i);
346-
} else {
347-
return a.template iter<cr>().at(i);
348-
}
349-
}
350-
351340

352341
// ---------------------
353342
// Small view and container
@@ -452,7 +441,7 @@ struct ViewSmall
452441
constexpr decltype(auto) back() const { static_assert(size()>0, "Bad back()."); return cp[size()-1]; }
453442
constexpr decltype(auto) operator()(this auto && self, auto && ... i) { return from(RA_FW(self), RA_FW(i) ...); }
454443
constexpr decltype(auto) operator[](this auto && self, auto && ... i) { return from(RA_FW(self), RA_FW(i) ...); }
455-
constexpr decltype(auto) at(auto const & i) const { return at_view(*this, i); }
444+
constexpr decltype(auto) at(auto const & i) const { return *indexer(*this, cp, start(i)); }
456445
constexpr operator decltype(*cp) () const { return to_scalar(*this); }
457446
// conversion to const
458447
constexpr operator ViewSmall<reconst<P>, Dimv> () const requires (!std::is_void_v<reconst<P>>)

test/at.cc

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ int main()
5353
tr.test_eq(35, sum(at(A, I2)));
5454
tr.test_eq(276, prod(at(A, I2)));
5555

56-
at(A, I1) = std::array { 10, 20 };
56+
at_view(A, I1) = std::array { 10, 20 };
5757
tr.test_eq(std::array { 20, 12, 10, 0 }, A(ra::all, 1));
5858
tr.test_eq(std::array { 20, 0, 10, 0 }, A(ra::all, 3));
5959
};
@@ -72,9 +72,9 @@ int main()
7272
ra::Small<int, 2> i2 { 1, 1 };
7373
ra::Small<int, 1> i1 { 1 };
7474
ra::Small<int, 0> i0 { };
75-
tr.test_eq(2, ra::rank_s<decltype(s.at(i0))>());
76-
tr.test_eq(1, ra::rank_s<decltype(s.at(i1))>());
77-
tr.test_eq(0, ra::rank_s<decltype(s.at(i2))>());
75+
tr.test_eq(2, ra::rank_s<decltype(at_view(s, i0))>());
76+
tr.test_eq(1, ra::rank_s<decltype(at_view(s, i1))>());
77+
tr.test_eq(0, ra::rank_s<decltype(at_view(s, i2))>());
7878
}
7979
tr.section("Small ops");
8080
{
@@ -93,11 +93,51 @@ int main()
9393
ra::Big<ra::Small<int, 2>, 1> I2 = { {1, 1}, {2, 2} };
9494
test(C, A, 10*ra::_0 + ra::_1, I1, I2);
9595
}
96-
tr.section("Misc");
96+
tr.section("Misc I");
9797
{
9898
ra::Small<int, 4, 4> A = ra::_0 - ra::_1;
9999
int i[2] = {1, 2};
100100
tr.test_eq(-1, A.iter().at(i));
101101
}
102+
tr.section("Misc II");
103+
{
104+
ra::Big<int, 2> A({4, 4}, 0), B({4, 4}, 10*ra::_0 + ra::_1);
105+
using coord = ra::Small<int, 2>;
106+
ra::Big<coord, 1> I = { {1, 1}, {2, 2} };
107+
108+
at(A, I) = at(B, I);
109+
tr.test_eq(ra::Big<int>({4, 4}, {0, 0, 0, 0, /**/ 0, 11, 0, 0, /**/ 0, 0, 22, 0, /**/ 0, 0, 0, 0}), A);
110+
111+
// TODO this is why we need ops to have explicit rank.
112+
at(A, ra::scalar(coord{3, 2})) = 99.;
113+
tr.test_eq(ra::Big<int>({4, 4}, {0, 0, 0, 0, /**/ 0, 11, 0, 0, /**/ 0, 0, 22, 0, /**/ 0, 0, 99, 0}), A);
114+
}
115+
tr.section("Misc III"); // [ma30]
116+
{
117+
ra::Big<int, 2> A = {{100, 101}, {110, 111}, {120, 121}};
118+
ra::Big<ra::Small<int, 2>, 2> i = {{{0, 1}, {2, 0}}, {{1, 0}, {2, 1}}};
119+
ra::Big<int, 2> B = at(A, i);
120+
tr.test_eq(ra::Big<int, 2> {{101, 120}, {110, 121}}, at(A, i));
121+
}
122+
tr.section("Misc IV"); // [ma31]
123+
{
124+
ra::Big<int, 2> A = {{100, 101}, {110, 111}, {120, 121}};
125+
tr.strict().test_eq(A, at_view(A, ra::Small<int, 0> {}));
126+
tr.strict().test_eq(A(2), at_view(A, ra::Small<int, 1> {2}));
127+
tr.strict().test_eq(A(2, 0), at_view(A, ra::Small<int, 2> {2, 0}));
128+
}
129+
tr.section("Misc V"); // [ma32]
130+
{
131+
ra::Big<int, 2> A = {{100, 101}, {110, 111}, {120, 121}};
132+
ra::Big<int, 2> I = {{0, 1}, {2, 0}, {1, 0}};
133+
134+
// error if 2!=I.len(1). Iterates over scalars
135+
println(cout, "{}", at(A, I.iter<1>()));
136+
tr.strict().test_eq(ra::Small<int, 3> {101, 120, 110}, at(A, I.iter<1>()));
137+
138+
// L=I.len(1) can be 0, 1, 2. Iterates over dynamic rank (2-L)-views
139+
println(cout, "{}", at_view(A, I.iter<1>()));
140+
tr.strict().test_eq(ra::Small<int, 3> {101, 120, 110}, at_view(A, I.iter<1>()));
141+
}
102142
return tr.summary();
103143
}

0 commit comments

Comments
 (0)