5656#ifndef BOOST_MULTI_ALGORITHMS_FFT_HPP
5757#define BOOST_MULTI_ALGORITHMS_FFT_HPP
5858
59+ #include < boost/multi/array_ref.hpp> // for layout_t and subarray (cursor -> strided view reconstruction)
60+
5961#include < algorithm> // for copy, fill, min, max, find_if
6062#include < array> // for plan sizes
6163#include < cassert> // for assert
@@ -1129,6 +1131,56 @@ void fft_apply_last(ViewND&& view, fft_engine<T> const& eng) { // NOLINT(cppcor
11291131 }
11301132}
11311133
1134+ // A Multi cursor (`.home()`) is base + strides with no extents. These helpers
1135+ // recover its rank and rebuild a full strided view once the extents (which the
1136+ // plan owns) are supplied -- the "extents (plan) + cursor (target)" split.
1137+ template <class Cursor >
1138+ using fft_cursor_strides_t = std::decay_t <decltype (std::declval<Cursor>().strides())>;
1139+
1140+ // A cursor exposes base()/strides() but, unlike an array/subarray, no sizes().
1141+ template <class C , class = void > struct fft_is_cursor_like : std::false_type {};
1142+ template <class C >
1143+ struct fft_is_cursor_like <C, std::void_t <decltype (std::declval<C>().base()), decltype (std::declval<C>().strides()), std::enable_if_t <!fft_is_multi_like<C>::value>>> : std::true_type {};
1144+
1145+ template <class Cursor >
1146+ inline constexpr std::ptrdiff_t fft_cursor_rank = static_cast <std::ptrdiff_t >(std::tuple_size_v<fft_cursor_strides_t <Cursor>>);
1147+
1148+ // Build layout_t<D> from extents and strides (all offsets 0, as for a home
1149+ // cursor): each level carries nelems = size*stride, which is the invariant
1150+ // layout_t uses to recover size() = nelems/stride and extent() bounds.
1151+ template <std::ptrdiff_t D>
1152+ auto fft_layout_from (std::array<std::size_t , static_cast <std::size_t >(D)> const & ext, std::array<std::ptrdiff_t, static_cast<std::size_t>(D)> const & str) -> multi::layout_t<D> {
1153+ if constexpr (D == 0 ) {
1154+ return multi::layout_t <0 >{};
1155+ } else {
1156+ std::array<std::size_t , static_cast <std::size_t >(D) - 1 > sub_ext{};
1157+ std::array<std::ptrdiff_t , static_cast <std::size_t >(D) - 1 > sub_str{};
1158+ for (std::size_t i = 0 ; i != static_cast <std::size_t >(D) - 1 ; ++i) {
1159+ sub_ext.at (i) = ext.at (i + 1 );
1160+ sub_str.at (i) = str.at (i + 1 );
1161+ }
1162+ return multi::layout_t <D>{
1163+ fft_layout_from<D - 1 >(sub_ext, sub_str),
1164+ str[0 ], 0 ,
1165+ static_cast <std::ptrdiff_t >(ext[0 ]) * str[0 ]
1166+ }; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) [0] is valid for D>=1
1167+ }
1168+ }
1169+
1170+ template <class Cursor , std::size_t ... Is>
1171+ auto fft_strides_array (Cursor const & cur, std::index_sequence<Is...> /* unused*/ )
1172+ -> std::array<std::ptrdiff_t, sizeof...(Is)> {
1173+ return {{static_cast <std::ptrdiff_t >(cur.template stride <static_cast <multi::dimensionality_type>(Is)>())...}};
1174+ }
1175+
1176+ // (cursor, extents) -> strided subarray sharing the cursor's memory.
1177+ template <class T , std::ptrdiff_t D, class Cursor >
1178+ auto fft_view_from_cursor (Cursor const & cur, std::array<std::size_t , static_cast <std::size_t >(D)> const & ext) {
1179+ auto const str = fft_strides_array (cur, std::make_index_sequence<static_cast <std::size_t >(D)>{});
1180+ using ptr_type = typename Cursor::element_ptr;
1181+ return multi::subarray<T, D, ptr_type>{fft_layout_from<D>(ext, str), cur.base ()};
1182+ }
1183+
11321184} // end namespace detail
11331185
11341186// Reusable multidimensional FFT plan: precomputes twiddle tables, stage
@@ -1202,32 +1254,57 @@ class fft_plan {
12021254 explicit fft_plan (MultiSubArray const & arr, int sign = fft_forward)
12031255 : sizes_{to_sizes_ (arr.sizes (), std::make_index_sequence<static_cast <std::size_t >(D)>{})}, sign_{sign} { init_ (); }
12041256
1205- auto sign () const -> int { return sign_; }
1206-
1207- // Execute the plan on `arr` in place. `arr` must have the planned sizes;
1208- // its layout (strides, subarray-ness) is free to differ between calls.
1209- template <class MultiSubArray >
1210- auto operator ()(MultiSubArray&& arr) const -> MultiSubArray&& { // NOLINT(cppcoreguidelines-missing-std-forward)
1211- static_assert (std::decay_t <MultiSubArray>::dimensionality == D, " array rank must match the plan" );
1212- assert (matches_ (arr.sizes (), std::make_index_sequence<static_cast <std::size_t >(D)>{}));
1257+ private:
1258+ // The axis walk, shared by the cursor and array entry points. `view` is a
1259+ // strided view of the planned shape; transformed in place.
1260+ template <class View >
1261+ void apply_ (View&& view) const { // NOLINT(cppcoreguidelines-missing-std-forward)
12131262 if constexpr (D == 1 ) {
1214- detail::fft_apply_last (arr () , engine_<0 >());
1263+ detail::fft_apply_last (view , engine_<0 >());
12151264 } else {
12161265 // Transform the last two axes together, slab by slab (cache
12171266 // locality), then the remaining axes 0 .. D-3 by static recursion
12181267 // over rotated views (each axis bound to its engine at compile
12191268 // time; no runtime axis parameter anywhere).
1220- detail::fft_apply_last_pair (arr () , engine_<D - 1 >(), engine_<D - 2 >());
1269+ detail::fft_apply_last_pair (view , engine_<D - 1 >(), engine_<D - 2 >());
12211270 if constexpr (D >= 3 ) {
1222- transform_middle_<1 >(arr () .rotated ());
1271+ transform_middle_<1 >(view .rotated ());
12231272 }
12241273 }
1274+ }
1275+
1276+ public:
1277+ auto sign () const -> int { return sign_; }
1278+
1279+ auto sizes () const -> std::array<std::size_t, static_cast<std::size_t>(D)> const & { return sizes_; }
1280+
1281+ // Apply the plan on a cursor (`A.home()`) -- base + strides, no extents.
1282+ // The plan supplies the extents, so the cursor's rank must match and its
1283+ // shape is *trusted* to be the planned one (a cursor carries no sizes to
1284+ // check). This is the primitive execution form; the array overload below
1285+ // delegates to it. Transforms in place; returns the cursor.
1286+ template <class Cursor , std::enable_if_t <detail::fft_is_cursor_like<std::decay_t <Cursor>>::value, int > = 0 > // NOLINT(modernize-use-constraints) C++17
1287+ auto operator ()(Cursor const & home) const -> Cursor {
1288+ static_assert (detail::fft_cursor_rank<std::decay_t <Cursor>> == D, " cursor rank must match the plan" );
1289+ auto view = detail::fft_view_from_cursor<T, D>(home, sizes_);
1290+ apply_ (view);
1291+ return home; // cursors are value types (base + strides); returned by value
1292+ }
1293+
1294+ // Execute the plan on `arr` in place. `arr` must have the planned sizes;
1295+ // its layout (strides, subarray-ness) is free to differ between calls.
1296+ // Delegates to the cursor form via `arr.home()`.
1297+ template <class MultiSubArray , std::enable_if_t <detail::fft_is_multi_like<std::decay_t <MultiSubArray>>::value, int > = 0 > // NOLINT(modernize-use-constraints) C++17
1298+ auto operator ()(MultiSubArray&& arr) const -> MultiSubArray&& { // NOLINT(cppcoreguidelines-missing-std-forward)
1299+ static_assert (std::decay_t <MultiSubArray>::dimensionality == D, " array rank must match the plan" );
1300+ assert (matches_ (arr.sizes (), std::make_index_sequence<static_cast <std::size_t >(D)>{}));
1301+ operator ()(arr.home ());
12251302 return std::forward<MultiSubArray>(arr);
12261303 }
12271304
1228- template <class MultiSubArray >
1229- auto execute (MultiSubArray && arr ) const -> MultiSubArray&& {
1230- return operator ()(std::forward<MultiSubArray>(arr ));
1305+ template <class Target >
1306+ auto execute (Target && tgt ) const -> decltype( auto ) {
1307+ return operator ()(std::forward<Target>(tgt ));
12311308 }
12321309};
12331310
@@ -1241,20 +1318,20 @@ fft_plan(MultiSubArray const&) -> fft_plan<typename MultiSubArray::element, Mult
12411318// throw-away plan; for repeated transforms of the same shape, build an
12421319// fft_plan once and execute it).
12431320template <class MultiSubArray >
1244- auto fft_inplace (MultiSubArray&& A , int sign = fft_forward) -> MultiSubArray&& {
1321+ auto fft_inplace (MultiSubArray&& arr , int sign = fft_forward) -> MultiSubArray&& {
12451322 using array_type = std::decay_t <MultiSubArray>;
1246- fft_plan<typename array_type::element, array_type::dimensionality> const plan{A , sign};
1247- return plan (std::forward<MultiSubArray>(A ));
1323+ fft_plan<typename array_type::element, array_type::dimensionality> const plan{arr , sign};
1324+ return plan (std::forward<MultiSubArray>(arr ));
12481325}
12491326
12501327template <class MultiSubArray >
1251- auto fft_inplace_forward (MultiSubArray&& A ) -> MultiSubArray&& {
1252- return fft_inplace (std::forward<MultiSubArray>(A ), fft_forward);
1328+ auto fft_inplace_forward (MultiSubArray&& arr ) -> MultiSubArray&& {
1329+ return fft_inplace (std::forward<MultiSubArray>(arr ), fft_forward);
12531330}
12541331
12551332template <class MultiSubArray >
1256- auto fft_inplace_backward (MultiSubArray&& A ) -> MultiSubArray&& {
1257- return fft_inplace (std::forward<MultiSubArray>(A ), fft_backward);
1333+ auto fft_inplace_backward (MultiSubArray&& arr ) -> MultiSubArray&& {
1334+ return fft_inplace (std::forward<MultiSubArray>(arr ), fft_backward);
12581335}
12591336
12601337} // end namespace boost::multi
0 commit comments