Skip to content

Commit

Permalink
initial pass at a ComplexViewConstIterator
Browse files Browse the repository at this point in the history
  • Loading branch information
JDanielSmith committed Jan 14, 2023
1 parent 6352388 commit b9329e4
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 7 deletions.
131 changes: 124 additions & 7 deletions modules/c++/mem/include/mem/ComplexView.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,28 @@
#pragma once

#include <assert.h>
#include <stdint.h>

#include <vector>
#include <complex>
#include <stdexcept>
#include <functional>
#include <iterator>

#include "coda_oss/span.h"

namespace mem
{

template<typename T>
struct ComplexViewConstIterator; // forward

// Not begin() as TView (or even TView<T>) would "suck up" too much.
template <typename TView, typename axis_type = typename TView::axis_type>
ComplexViewConstIterator<axis_type> ComplexView_begin(TView);
template <typename TView, typename axis_type = typename TView::axis_type>
ComplexViewConstIterator<axis_type> ComplexView_end(TView);

/*!
* \class ComplexView
* \brief These classes class provide read-only views onto a collection of complex
Expand All @@ -45,7 +57,7 @@ namespace mem
* const float* imags;
* This classes have (almost) the same access routines regardless of how the underlying data is actually stored.
*
* Thing are intentinally kept very simple (for now), e.g., no iterators, because it's not yet clear
* Thing are intentinally kept very simple (for now), because it's not yet clear
* what functionality will be needed; YAGNI.
*/

Expand All @@ -55,6 +67,7 @@ struct ComplexSpanView final
using size_type = size_t;
using value_type = std::complex<T>;
using cxvalue_type = value_type;
using axis_type = typename cxvalue_type::value_type; // i.e., float

ComplexSpanView() = delete;
~ComplexSpanView() = default;
Expand All @@ -81,10 +94,18 @@ struct ComplexSpanView final
return data_[idx].imag();
}

constexpr const cxvalue_type& operator[](size_type idx) const noexcept
// const& vs. value makes little difference here as cxvalue_type is
// std::complex<>. But this provides a (subtle) way for clients to know
// what "view style" they're using, should that be needed. This
// also more closely matches std::span<>.
constexpr const cxvalue_type& index(size_type idx) const noexcept
{
return data_[idx];
}
constexpr const cxvalue_type& operator[](size_type idx) const noexcept
{
return index(idx);
}

constexpr size_type size() const noexcept
{
Expand All @@ -95,8 +116,6 @@ struct ComplexSpanView final
template <typename TAxisFunc>
auto copy_axis(TAxisFunc axis) const
{
// real/imag are the two axes in the complex plane
using axis_type = typename value_type::value_type; // i.e., float
std::vector<axis_type> retval(size());
for (size_t i = 0; i < size(); i++)
{
Expand All @@ -119,6 +138,15 @@ struct ComplexSpanView final
return std::vector<cxvalue_type>(data_.begin(), data_.end());
}

ComplexViewConstIterator<axis_type> begin() const
{
return ComplexView_begin(*this);
}
ComplexViewConstIterator<axis_type> end() const
{
return ComplexView_begin(*this);
}

private:
span_t data_;
};
Expand All @@ -134,6 +162,7 @@ struct ComplexArrayView final
using size_type = size_t;
using value_type = typename TVectorLike::value_type;
using cxvalue_type = value_type;
using axis_type = typename cxvalue_type::value_type; // i.e., float

ComplexArrayView() = delete;
~ComplexArrayView() = default;
Expand All @@ -156,10 +185,14 @@ struct ComplexArrayView final
return view.imag(idx);
}

constexpr const cxvalue_type& operator[](size_type idx) const noexcept
constexpr const cxvalue_type& index(size_type idx) const noexcept
{
return view[idx];
}
constexpr const cxvalue_type& operator[](size_type idx) const noexcept
{
return index(idx);
}

constexpr size_type size() const noexcept
{
Expand Down Expand Up @@ -194,6 +227,7 @@ struct ComplexSpansView final // "Span_s_,", i.e., two spans. Avoiding "parallel
using size_type = size_t;
using value_type = T;
using cxvalue_type = std::complex<value_type>;
using axis_type = typename cxvalue_type::value_type; // i.e., float

ComplexSpansView() = delete;
~ComplexSpansView() = default;
Expand Down Expand Up @@ -223,12 +257,16 @@ struct ComplexSpansView final // "Span_s_,", i.e., two spans. Avoiding "parallel
return imags_[idx];
}

constexpr cxvalue_type operator[](size_type idx) const noexcept
constexpr cxvalue_type index(size_type idx) const noexcept
{
// Note that this is a COPY because the "real" and "imag" parts MUST be
// next to each other. https://en.cppreference.com/w/cpp/numeric/complex
return cxvalue_type(real(idx), imag(idx));
}
constexpr cxvalue_type operator[](size_type idx) const noexcept
{
return index(idx);
}

constexpr size_type size() const noexcept
{
Expand Down Expand Up @@ -269,6 +307,7 @@ struct ComplexArraysView final // "Array_s_,", i.e., two arrays. Avoiding "paral
using size_type = size_t;
using value_type = typename TVectorLike::value_type; // i.e., float
using cxvalue_type = std::complex<value_type>;
using axis_type = typename cxvalue_type::value_type; // i.e., float

ComplexArraysView() = delete;
~ComplexArraysView() = default;
Expand All @@ -292,10 +331,14 @@ struct ComplexArraysView final // "Array_s_,", i.e., two arrays. Avoiding "paral
return view.imag(idx);
}

constexpr cxvalue_type operator[](size_type idx) const noexcept
constexpr cxvalue_type index(size_type idx) const noexcept
{
return view[idx];
}
constexpr cxvalue_type operator[](size_type idx) const noexcept
{
return index(idx);
}

constexpr size_type size() const noexcept
{
Expand Down Expand Up @@ -324,6 +367,80 @@ inline auto make_ComplexArraysView(const TVectorLike& reals, const TVectorLike&
return ComplexArraysView<TVectorLike>(reals, imags);
}

template<typename TAxis>
struct ComplexViewConstIterator final
{
// https://stackoverflow.com/questions/8054273/how-to-implement-an-stl-style-iterator-and-avoid-common-pitfalls
using iterator_category = std::random_access_iterator_tag;
using value_type = std::complex<TAxis>;
using difference_type = ptrdiff_t;
using pointer = const value_type*;
using reference = const value_type&;

template <typename TView>
ComplexViewConstIterator(TView view, size_t index) : index_(index)
{
index_f_ = [&](size_t i) { return view[i]; };
}
template<typename TView>
explicit ComplexViewConstIterator(TView view) : ComplexViewConstIterator(view, 0)
{
}

ComplexViewConstIterator(const ComplexViewConstIterator&) = default;
ComplexViewConstIterator& operator=(const ComplexViewConstIterator&) = default;
ComplexViewConstIterator(ComplexViewConstIterator&&) = default;
ComplexViewConstIterator& operator=(ComplexViewConstIterator&&) = default;

bool operator==(const ComplexViewConstIterator& rhs) const
{
// Checking the target() helps ensure the same type of view is used.
return (index_ == rhs.index_) &&
(index_f_.target_type() == rhs.index_f_.target_type());
}
bool operator!=(const ComplexViewConstIterator& rhs) const
{
return !(*this == rhs);
}

ComplexViewConstIterator& operator++()
{
++index_;
return *this;
}
ComplexViewConstIterator operator++(int)
{
auto retval = *this;
++(*this);
return retval;
}

difference_type operator-(const ComplexViewConstIterator& other) const
{
return index_ - other.index_;
}

reference operator*() const
{
return index_f_(index_);
}

private:
size_t index_ = 0;
std::function<value_type(size_t)> index_f_;
};

template <typename TView, typename axis_type>
inline ComplexViewConstIterator<axis_type> ComplexView_begin(TView view)
{
return ComplexViewConstIterator<axis_type>(view);
}
template <typename TView, typename axis_type>
inline ComplexViewConstIterator<axis_type> ComplexView_end(TView view)
{
return ComplexViewConstIterator<axis_type>(view, view.size());
}

}

#endif // CODA_OSS_mem_ComplexView_h_INCLUDED_
54 changes: 54 additions & 0 deletions modules/c++/mem/unittests/test_vector_pointers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,64 @@ TEST_CASE(testComplexViewFloat)
}
}

template <typename TIter>
static void test_mem_ComplexViewConstIterator(const std::string& testName, TIter begin, TIter end)
{
TEST_ASSERT(begin != end);

const auto distance = std::distance(begin, end);
TEST_ASSERT_EQ(4, distance);

//TEST_ASSERT_EQ(view[0].real(), 1.0f);
//TEST_ASSERT_EQ(view[0].imag(), 2.0f);
//TEST_ASSERT_EQ(view[1].real(), 3.0f);
//TEST_ASSERT_EQ(view[1].imag(), 4.0f);
//TEST_ASSERT_EQ(view[2].real(), 5.0f);
//TEST_ASSERT_EQ(view[2].imag(), 6.0f);
//TEST_ASSERT_EQ(view[3].real(), 7.0f);
//TEST_ASSERT_EQ(view[3].imag(), 8.0f);
}

TEST_CASE(testComplexViewFloatIterator)
{
{
const std::span<const cx_float> data(cx_data().data(), cx_data().size());
const auto view = mem::make_ComplexSpanView(data);

test_mem_ComplexViewConstIterator(testName, view.begin(), view.end());
}
{
const auto view = mem::make_ComplexArrayView(cx_data());
TEST_ASSERT_EQ(cx_data().size(), view.size());
test_mem_ComplexView(testName, view);
test_cx_view(testName, view.values());
}

const std::vector<float> reals{1, 3, 5, 7};
const std::vector<float> imags{2, 4, 6, 8};
TEST_ASSERT_EQ(imags.size(), reals.size());
{
const std::span<const float> reals_(reals.data(), reals.size());
const std::span<const float> imags_(imags.data(), imags.size());
const auto view = mem::make_ComplexSpansView(reals_, imags_);

TEST_ASSERT_EQ(reals.size(), view.size());
test_mem_ComplexView(testName, view);
test_cx_view(testName, view.values());
}
{
const auto view = mem::make_ComplexArraysView(reals, imags);
TEST_ASSERT_EQ(reals.size(), view.size());
test_mem_ComplexView(testName, view);
test_cx_view(testName, view.values());
}
}

TEST_MAIN(
TEST_CHECK(testVecOfRawPointers);
TEST_CHECK(testVecOfSharedPointers);

TEST_CHECK(testSpanCxFloat);
TEST_CHECK(testComplexViewFloat);
TEST_CHECK(testComplexViewFloatIterator);
)

0 comments on commit b9329e4

Please sign in to comment.