Commit 427beebe authored by LEFEBVREJP email's avatar LEFEBVREJP email
Browse files

Added Greg Davidson's ranges implementation.

parent 166d2cfd
Pipeline #158067 passed with stages
in 17 minutes and 57 seconds
...@@ -8,6 +8,8 @@ json.hh ...@@ -8,6 +8,8 @@ json.hh
system.hh system.hh
stringfunctions.i.hh stringfunctions.i.hh
stringfunctions.hh stringfunctions.hh
range.hh
range.i.hh
value.hh value.hh
) )
SET(SOURCES SET(SOURCES
......
/*
* @file: range.hh
* @author: Gregory G. Davidson
*
* Created on July 28, 2021
*
* Provides the ability to iterate over ranges in a ranged-for construct
*/
#ifndef RADIX_RADIXCORE_RANGE_HH_
#define RADIX_RADIXCORE_RANGE_HH_
#include <iterator>
#include <type_traits>
namespace radix
{
namespace detail
{
// Forward declaration of iterator classes
template <class T>
class RangeIterator;
template <class T>
class StridedRangeIterator;
} // namespace detail
/**
* @brief Standard range class for iterating over a range
*/
template <class T>
class Range
{
public:
using value_type = T;
using iterator = detail::RangeIterator<T>;
using size_type = std::size_t;
// >>> CONSTRUCTORS
// Default constructor for an empty range
inline Range();
// Construct with a range ending (begins at zero)
inline explicit Range(value_type end);
// Construct from a given beginning and ending
inline Range(value_type begin, value_type end);
// >>> BASIC CONTAINER FUNCTIONALITY
//! Return beginning of range
iterator begin() const { return d_begin; }
//! Return ending of range
iterator end() const { return d_end; }
//! Return size of range
size_type size() const { return *d_end - *d_begin; }
//! Return whether range is empty
bool empty() const { return d_begin == d_end; }
private:
iterator d_begin;
iterator d_end;
};
/**
* @brief Strided range class for iterating over a range by some stride
*/
template <class T>
class StridedRange
{
public:
using value_type = T;
using iterator = detail::StridedRangeIterator<T>;
using size_type = std::size_t;
// >>> CONSTRUCTORS
// Construct with a range ending and stride
inline StridedRange(value_type end, value_type stride);
// Construct with a range beginning, ending, and stride
inline StridedRange(value_type begin, value_type end, value_type stride);
// >>> BASIC CONTAINER FUNCTIONALITY
//! Return beginning of range
iterator begin() const { return d_begin; }
//! Return ending of range
iterator end() const { return d_end; }
//! Return size of range
size_type size() const { return *d_end - *d_begin; }
//! Return whether range is empty
bool empty() const { return d_begin == d_end; }
private:
iterator d_begin;
iterator d_end;
};
//*****************************************************************************
// HELPER FUNCTIONS
//*****************************************************************************
// Build an iterable range with a beginning and ending
template <class T>
Range<T> range(T begin, T end);
// Build an iterable range with just an ending
template <class T>
Range<T> range(T begin, T end);
// Build a strided range with a beginning, ending, and stride
template <class T>
StridedRange<T> range(T begin, T end, T stride);
namespace detail
{
/**
* @brief Range iterator class
*/
template <class T>
class RangeIterator
{
public:
//@{
//! Public types
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = typename std::make_signed<T>::type;
using pointer = const T*;
using reference = const T&;
using This = RangeIterator<T>;
//@}
public:
// Construct with value
inline explicit RangeIterator(value_type value);
// >>> ITERATOR INTERFACE
//! Dereference operator
value_type operator*() const { return d_value; }
//! Indirection operator
pointer operator->() const { return &d_value; }
// Prefix increment operator
inline This& operator++();
// Postfix increment operator
inline This operator++(int);
//! Equality operator
inline bool operator==(const This& iter) const;
//! Inequality operator
inline bool operator!=(const This& iter) const;
protected:
value_type d_value;
};
//*****************************************************************************
/**
* @brief Strided range iterator class
*/
template <class T>
class StridedRangeIterator : public RangeIterator<T>
{
using Base = RangeIterator<T>;
public:
using typename Base::value_type;
using This = StridedRangeIterator<T>;
public:
// Construct with value and stride
inline StridedRangeIterator(value_type value, value_type stride);
// >>> OVERWRITTEN ITERATOR INTERFACE
// Prefix increment operator
inline This& operator++();
//! Equality operator
inline bool operator==(const This& iter) const;
// >>> CLASS INTERFACE
value_type stride() const { return d_stride; }
private:
value_type d_stride;
};
} // namespace detail
} // namespace radix
//*****************************************************************************
// Inline function definitions
#include "range.i.hh"
//*****************************************************************************
#endif /** RADIX_RADIXCORE_RANGE_HH_ */
/*
* @file: range.i.hh
* @author: Gregory G. Davidson
*
* Created on July 28, 2021
*/
#include <cmath>
#include "radixbug/bug.hh"
namespace
{
// Return 1 or -1 matching the sign of the argument
template <class T>
T sign(T value)
{
return std::signbit(value) ? -1 : 1;
}
} // namespace
namespace radix
{
//*****************************************************************************
// RANGE
//*****************************************************************************
/*!
* \brief Default constructor for empty range
*/
template <class T>
Range<T>::Range()
: Range(value_type(0), value_type(0))
{
radix_ensure(this->empty());
}
//*****************************************************************************
/*!
* \brief Constructor with range beginning at zero
*/
template <class T>
Range<T>::Range(value_type end)
: Range(T(0), end)
{
/* * */
}
//*****************************************************************************
/*!
* \brief Constructor with a beginnning and ending range
*/
template <class T>
Range<T>::Range(value_type begin, value_type end)
: d_begin(begin)
, d_end(end)
{
radix_require(begin <= end);
}
//*****************************************************************************
// STRIDEDRANGE
//*****************************************************************************
/*!
* \brief Construct with a range ending and stride
*/
template <class T>
StridedRange<T>::StridedRange(value_type end, value_type stride)
: StridedRange(value_type(0), end, stride)
{
/* * */
}
//*****************************************************************************
/*!
* \brief Construct with a range begining, ending, and stride
*/
template <class T>
StridedRange<T>::StridedRange(value_type begin, value_type end,
value_type stride)
: d_begin(begin, stride)
, d_end(end, stride)
{
radix_require(end >= sign(stride) * begin);
}
//*****************************************************************************
// HELPER FUNCTIONS
//*****************************************************************************
/*!
* \brief Build an iterable range with a beginning and ending
*/
template <class T>
Range<T> range(T begin, T end)
{
return Range<T>(begin, end);
}
//*****************************************************************************
/*!
* \brief Build an iterable range with just an ending
*/
template <class T>
Range<T> range(T end)
{
return Range<T>(end);
}
//*****************************************************************************
/*!
* \brief Build a strided, iterable range with a beginning, ending, and stride
*/
template <class T>
StridedRange<T> range(T begin, T end, T stride)
{
return StridedRange<T>(begin, end, stride);
}
namespace detail
{
//*****************************************************************************
// RANGEITERATOR
//*****************************************************************************
/*!
* \brief Constructor
*/
template <class T>
RangeIterator<T>::RangeIterator(value_type value)
: d_value(value)
{
/* * */
}
//*****************************************************************************
/*!
* \brief Prefix increment operator
*/
template <class T>
auto RangeIterator<T>::operator++() -> This&
{
++d_value;
return *this;
}
//*****************************************************************************
/*!
* \brief Postfix increment operator
*/
template <class T>
auto RangeIterator<T>::operator++(int) -> This
{
This copy = *this;
++(*this);
return copy;
}
//*****************************************************************************
/*!
* \brief Equality operator
*/
template <class T>
bool RangeIterator<T>::operator==(const This& iter) const
{
return d_value == iter.d_value;
}
//*****************************************************************************
/*!
* \brief Inequality operator
*/
template <class T>
bool RangeIterator<T>::operator!=(const This& iter) const
{
return !this->operator==(iter);
}
//*****************************************************************************
// STRIDEDRANGEITERATOR
//*****************************************************************************
/*!
* \brief Constructor taking a value and stride
*/
template <class T>
StridedRangeIterator<T>::StridedRangeIterator(value_type value,
value_type stride)
: Base(value)
, d_stride(stride)
{
radix_require(stride != 0);
}
/*!
* \brief Prefix increment
* operator
*/
template <class T>
auto StridedRangeIterator<T>::operator++() -> This&
{
Base::d_value += d_stride;
return *this;
}
//*****************************************************************************
/*!
* \brief Equality operator
*
* Only iterators of equal stride
* should be compared
*/
template <class T>
bool StridedRangeIterator<T>::operator==(const This& iter) const
{
radix_require(iter.stride() == this->stride());
return Base::d_value == iter.d_value;
}
} // namespace detail
} // namespace radix
INCLUDE(GoogleTest) INCLUDE(GoogleTest)
ADD_GOOGLE_TEST(tstJson.cc NP 1) ADD_GOOGLE_TEST(tstJson.cc NP 1)
ADD_GOOGLE_TEST(tstRange.cc NP 1)
ADD_GOOGLE_TEST(tstString.cc NP 1) ADD_GOOGLE_TEST(tstString.cc NP 1)
ADD_GOOGLE_TEST(tstSystem.cc NP 1) ADD_GOOGLE_TEST(tstSystem.cc NP 1)
ADD_GOOGLE_TEST(tstValue.cc NP 1) ADD_GOOGLE_TEST(tstValue.cc NP 1)
......
#include "gtest/gtest.h"
#include <vector>
#include "radixcore/range.hh"
// Tests simple range starting from zero
TEST(RangeTest, simple_range)
{
std::vector<int> v;
for (auto i : radix::range(10))
{
v.push_back(i);
}
std::vector<int> v_ref = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
EXPECT_EQ(v_ref.size(), v.size());
auto v_ref_iter = v_ref.begin();
for (int v_elem : v)
{
EXPECT_DOUBLE_EQ(*v_ref_iter++, v_elem);
}
}
// Tests range with a non-zero beginning
TEST(RangeTest, nonzero_begin_range)
{
std::vector<int> v;
for (auto i : radix::range(5, 10))
{
v.push_back(i);
}
std::vector<int> v_ref = {5, 6, 7, 8, 9};
EXPECT_EQ(v_ref.size(), v.size());
auto v_ref_iter = v_ref.begin();
for (int v_elem : v)
{
EXPECT_DOUBLE_EQ(*v_ref_iter++, v_elem);
}
}
// Tests range with a positive stride
TEST(RangeTest, pos_stride_test)
{
std::vector<int> v;
for (auto i : radix::range(0, 10, 2))
{
v.push_back(i);
}
std::vector<int> v_ref = {0, 2, 4, 6, 8};
EXPECT_EQ(v_ref.size(), v.size());
auto v_ref_iter = v_ref.begin();
for (int v_elem : v)
{
EXPECT_DOUBLE_EQ(*v_ref_iter++, v_elem);
}
}
// Tests range with a negative stride
TEST(RangeTest, neg_stride_test)
{
std::vector<int> v;
for (auto i : radix::range(9, -1, -1))
{
v.push_back(i);
}
std::vector<int> v_ref = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
EXPECT_EQ(v_ref.size(), v.size());
auto v_ref_iter = v_ref.begin();
for (int v_elem : v)
{
EXPECT_DOUBLE_EQ(*v_ref_iter++, v_elem);
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment