Skip to content

Commit

Permalink
Merge pull request #151 from MarcDirven/dev
Browse files Browse the repository at this point in the history
Inclusive scan
  • Loading branch information
Kaaserne authored Mar 12, 2023
2 parents e34c402 + 2e8ba4d commit 2ee4f6e
Show file tree
Hide file tree
Showing 11 changed files with 372 additions and 14 deletions.
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ set(examples
FunctionTools
Generate
GroupBy
InclusiveScan
Join
JoinWhere
Map
Expand Down
23 changes: 23 additions & 0 deletions examples/InclusiveScan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <Lz/InclusiveScan.hpp>

#include <iostream>

int main() {
int array[] = {3, 5, 2, 3, 4, 2, 3};
// start the scan from arr[0] (3)
auto scan = lz::iScan(array);

for (const int& i : scan) {
fmt::print("{} ", i);
}
// prints 3 8 10 13 17 19 22

// essentially it's:
// 3 (3)
// 3 + 5 (8)
// 8 + 2 (10)
// 10 + 3 (13)
// 13 + 4 (17)
// 17 + 2 (19)
// 19 + 3 (22)
}
13 changes: 7 additions & 6 deletions include/Lz/ExclusiveScan.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#ifndef LZ_SCAN_HPP
# define LZ_SCAN_HPP
#ifndef LZ_EXCLUSIVE_SCAN_HPP
# define LZ_EXCLUSIVE_SCAN_HPP

# include "detail/BasicIteratorView.hpp"
# include "detail/ExclusiveScanIterator.hpp"
Expand Down Expand Up @@ -51,9 +51,10 @@ class ExclusiveScan final : public internal::BasicIteratorView<internal::Exclusi
* @param binOp The fold function. Essentially, it is executed as (`init = binOp(std::move(init), *iterator);`)
* @return An exclusive scan view object.
*/
template<LZ_CONCEPT_ITERATOR Iterator, class T, class BinaryOp = MAKE_BIN_OP(std::plus, internal::Decay<T>)>
template<LZ_CONCEPT_ITERATOR Iterator, class T = internal::ValueType<Iterator>,
class BinaryOp = MAKE_BIN_OP(std::plus, internal::Decay<T>)>
LZ_NODISCARD LZ_CONSTEXPR_CXX_14 ExclusiveScan<Iterator, internal::Decay<T>, internal::Decay<BinaryOp>>
eScan(Iterator first, Iterator last, T&& init, BinaryOp&& binOp = {}) {
eScan(Iterator first, Iterator last, T&& init = {}, BinaryOp&& binOp = {}) {
return { std::move(first), std::move(last), std::move(init), std::forward<BinaryOp>(binOp) };
}

Expand All @@ -80,10 +81,10 @@ eScan(Iterator first, Iterator last, T&& init, BinaryOp&& binOp = {}) {
* @return An exclusive scan view object.
*/
// clang-format off
template<LZ_CONCEPT_ITERABLE Iterable, class T, class BinaryOp = MAKE_BIN_OP(std::plus, internal::Decay<T>)>
template<LZ_CONCEPT_ITERABLE Iterable, class T = internal::ValueTypeIterable<Iterable>, class BinaryOp = MAKE_BIN_OP(std::plus, internal::Decay<T>)>
LZ_NODISCARD LZ_CONSTEXPR_CXX_14
ExclusiveScan<internal::IterTypeFromIterable<Iterable>, internal::Decay<T>, internal::Decay<BinaryOp>>
eScan(Iterable&& iterable, T&& init, BinaryOp&& binOp = {}) {
eScan(Iterable&& iterable, T&& init = {}, BinaryOp&& binOp = {}) {
return eScan(internal::begin(std::forward<Iterable>(iterable)), internal::end(std::forward<Iterable>(iterable)),
std::forward<T>(init), std::forward<BinaryOp>(binOp));
}
Expand Down
162 changes: 162 additions & 0 deletions include/Lz/InclusiveScan.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#pragma once

#ifndef LZ_INCLUSIVE_SCAN_HPP
# define LZ_INCLUSIVE_SCAN_HPP

# include "detail/BasicIteratorView.hpp"
# include "detail/InclusiveScanIterator.hpp"

namespace lz {
LZ_MODULE_EXPORT_SCOPE_BEGIN

template<class Iterator, class T, class BinaryOp>
class InclusiveScan final : public internal::BasicIteratorView<internal::InclusiveScanIterator<Iterator, T, BinaryOp>> {
public:
using iterator = internal::InclusiveScanIterator<Iterator, T, BinaryOp>;
using const_iterator = iterator;

constexpr InclusiveScan() = default;

LZ_CONSTEXPR_CXX_14 InclusiveScan(Iterator first, Iterator last, T init, BinaryOp binaryOp) :
internal::BasicIteratorView<iterator>(iterator(std::move(first), init, binaryOp),
iterator(std::move(last), init, binaryOp)) {
}
};

/**
* @addtogroup ItFns
* @{
*/

/**
* @brief Returns an inclusive scan iterator.
* @details Returns an inclusive scan iterator. This iterator begins by returning `binOp(std::move(init), *first)`. It then
* proceeds to the next one, which is essentially the previously calculated value + the current iterator element being handled.
* Example:
* @example
* ```cpp
* int array[] = {3, 5, 2, 3, 4, 2, 3};
* // start the scan from 3 + init (1) = 4
* auto scan = lz::iScanFrom(std::begin(array), std::end(array), 1);
*
* for (const int& i : scan) {
* fmt::print("{} ", i);
* }
* // prints 4 9 11 14 18 20 23
* ```
*
* @param first The iterator denoting the beginning
* @param last The iterator denoting the end
* @param init The value to start with
* @param binOp The fold function. Essentially, it is executed as (`init = binOp(std::move(init), *iterator);`)
* @return An inclusive scan view object.
*/
template<LZ_CONCEPT_ITERATOR Iterator, class T, class BinaryOp = MAKE_BIN_OP(std::plus, internal::Decay<T>)>
LZ_NODISCARD LZ_CONSTEXPR_CXX_14 InclusiveScan<Iterator, internal::Decay<T>, internal::Decay<BinaryOp>>
iScanFrom(Iterator first, Iterator last, T&& init, BinaryOp&& binOp = {}) {
auto tmp = binOp(std::forward<T>(init), *first);
return { std::move(first), std::move(last), std::move(tmp), std::forward<BinaryOp>(binOp) };
}

/**
* @brief Returns an inclusive scan iterator.
* @details Returns an inclusive scan iterator. This iterator begins by returning `binOp(std::move(init), *first)`. It then
* proceeds to the next one, which is essentially the previously calculated value + the current iterator element being handled.
* Example:
* @example
* ```cpp
* int array[] = {3, 5, 2, 3, 4, 2, 3};
* // start the scan from 3 + init (1) = 4
* auto scan = lz::iScanFrom(array, 1);
*
* for (const int& i : scan) {
* fmt::print("{} ", i);
* }
* // prints 4 9 11 14 18 20 23
* ```
*
* @param iterable The iterable to perform the inclusive scan over
* @param init The value to start with
* @param binOp The fold function. Essentially, it is executed as (`init = binOp(std::move(init), *iterator);`)
* @return An inclusive scan view object.
*/
// clang-format off
template<LZ_CONCEPT_ITERABLE Iterable, class T, class BinaryOp = MAKE_BIN_OP(std::plus, internal::Decay<T>)>
LZ_NODISCARD LZ_CONSTEXPR_CXX_14
InclusiveScan<internal::IterTypeFromIterable<Iterable>, internal::Decay<T>, internal::Decay<BinaryOp>>
iScanFrom(Iterable&& iterable, T&& init, BinaryOp&& binOp = {}) {
return iScanFrom(internal::begin(std::forward<Iterable>(iterable)), internal::end(std::forward<Iterable>(iterable)),
std::forward<T>(init), std::forward<BinaryOp>(binOp));
}
// clang-format on

/**
* @brief Returns an inclusive scan iterator.
* @details Returns an inclusive scan iterator. This iterator begins by returning `*first`. It then
* proceeds to the next one, which is essentially the previously calculated value (calculated by `binOp(std::move(current), *it)`
* + the current iterator element being handled. Example:
* @example
* ```cpp
* int array[] = {3, 5, 2, 3, 4, 2, 3};
* // start the scan from 3
* auto scan = lz::iScan(array);
*
* for (const int& i : scan) {
* fmt::print("{} ", i);
* }
* // prints 3 8 10 13 17 19 22
* ```
*
* @param first The iterator denoting the beginning
* @param last The iterator denoting the end
* @param init The value to start with
* @param binOp The fold function. Essentially, it is executed as (`init = binOp(std::move(init), *iterator);`)
* @return An inclusive scan view object.
*/
template<LZ_CONCEPT_ITERATOR Iterator, class BinaryOp = MAKE_BIN_OP(std::plus, internal::ValueType<Iterator>)>
LZ_NODISCARD LZ_CONSTEXPR_CXX_14 InclusiveScan<Iterator, internal::ValueType<Iterator>, internal::Decay<BinaryOp>>
iScan(Iterator first, Iterator last, BinaryOp&& binOp = {}) {
auto tmp = *first;
return { std::move(first), std::move(last), std::move(tmp), std::forward<BinaryOp>(binOp) };
}

/**
* @brief Returns an inclusive scan iterator.
* @details Returns an inclusive scan iterator. This iterator begins by returning `*std::begin(iterable)`. It then
* proceeds to the next one, which is essentially the previously calculated value (calculated by `binOp(std::move(current), *it)`
* + the current iterator element being handled. Example:
* @example
* ```cpp
* int array[] = {3, 5, 2, 3, 4, 2, 3};
* // start the scan from 3
* auto scan = lz::iScan(array);
*
* for (const int& i : scan) {
* fmt::print("{} ", i);
* }
* // prints 3 8 10 13 17 19 22
* ```
*
* @param iterable The iterable to perform the inclusive scan over
* @param init The value to start with
* @param binOp The fold function. Essentially, it is executed as (`init = binOp(std::move(init), *iterator);`)
* @return An inclusive scan view object.
*/
// clang-format off
template<LZ_CONCEPT_ITERABLE Iterable, class BinaryOp = MAKE_BIN_OP(std::plus, internal::ValueTypeIterable<Iterable>)>
LZ_NODISCARD LZ_CONSTEXPR_CXX_14
InclusiveScan<internal::IterTypeFromIterable<Iterable>, internal::ValueTypeIterable<Iterable>, internal::Decay<BinaryOp>>
iScan(Iterable&& iterable, BinaryOp&& binOp = {}) {
return iScan(internal::begin(std::forward<Iterable>(iterable)), internal::end(std::forward<Iterable>(iterable)), std::forward<BinaryOp>(binOp));
}
// clang-format on

// End of group
/**
* @}
*/

LZ_MODULE_EXPORT_SCOPE_END
} // namespace lz

#endif
26 changes: 22 additions & 4 deletions include/Lz/Lz.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
# include "Lz/Enumerate.hpp"
# include "Lz/Except.hpp"
# include "Lz/Exclude.hpp"
# include "Lz/ExclusiveScan.hpp"
# include "Lz/Flatten.hpp"
# include "Lz/FunctionTools.hpp"
# include "Lz/Generate.hpp"
# include "Lz/GroupBy.hpp"
# include "Lz/InclusiveScan.hpp"
# include "Lz/JoinWhere.hpp"
# include "Lz/Loop.hpp"
# include "Lz/Random.hpp"
Expand Down Expand Up @@ -210,7 +212,6 @@ class IterView final : public internal::BasicIteratorView<Iterator> {
}

//! See Loop.hpp for documentation
template<class Iterable>
LZ_NODISCARD LZ_CONSTEXPR_CXX_20 IterView<internal::LoopIterator<Iterator>> loop() const {
return chain(lz::loop(*this));
}
Expand All @@ -220,11 +221,28 @@ class IterView final : public internal::BasicIteratorView<Iterator> {
exclude(const difference_type from, const difference_type to) const {
return chain(lz::exclude(*this, from, to));
}

// clang-format off
//! See InclusiveScan.hpp for documentation.
template<class T = value_type, class BinaryOp = MAKE_BIN_OP(std::plus, internal::ValueType<iterator>)>
LZ_NODISCARD
LZ_CONSTEXPR_CXX_20 IterView<internal::InclusiveScanIterator<Iterator, internal::Decay<T>, internal::Decay<BinaryOp>>>
iScan(T&& init = {}, BinaryOp&& binaryOp = {}) const {
return chain(lz::iScan(*this, std::forward<T>(init), std::forward<BinaryOp>(binaryOp)));
}

//! See ExclusiveScan.hpp for documentation.
template<class T = value_type, class BinaryOp = MAKE_BIN_OP(std::plus, internal::ValueType<iterator>)>
LZ_NODISCARD
LZ_CONSTEXPR_CXX_20 IterView<internal::ExclusiveScanIterator<Iterator, internal::Decay<T>, internal::Decay<BinaryOp>>>
eScan(T&& init = {}, BinaryOp&& binaryOp = {}) const {
return chain(lz::eScan(*this, std::forward<T>(init), std::forward<BinaryOp>(binaryOp)));
}
// clang-format on

template<class Iterable>
LZ_NODISCARD LZ_CONSTEXPR_CXX_20 IterView<internal::RotateIterator<Iterator>>
rotate(const internal::DiffType<iterator> start) const {
return chain(lz::rotate(*this, start));
rotate(iterator start) const {
return chain(lz::rotate(std::move(start), this->begin(), this->end()));
}

//! See FunctionTools.hpp `hasOne` for documentation.
Expand Down
4 changes: 2 additions & 2 deletions include/Lz/detail/ExclusiveScanIterator.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#ifndef LZ_SCAN_ITERATOR
# define LZ_SCAN_ITERATOR
#ifndef LZ_EXCLUSIVE_SCAN_ITERATOR_HPP
# define LZ_EXCLUSIVE_SCAN_ITERATOR_HPP

# include "FunctionContainer.hpp"

Expand Down
64 changes: 64 additions & 0 deletions include/Lz/detail/InclusiveScanIterator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#ifndef LZ_INCLUSIVE_SCAN_ITERATOR_HPP
# define LZ_INCLUSIVE_SCAN_ITERATOR_HPP

# include "FunctionContainer.hpp"

namespace lz {
namespace internal {
template<class Iterator, class T, class BinaryOp>
class InclusiveScanIterator {
T _reducer{};
FunctionContainer<BinaryOp> _binaryOp{};
Iterator _iterator{};

using IterTraits = std::iterator_traits<Iterator>;

public:
using reference = T&;
using value_type = Decay<reference>;
using pointer = FakePointerProxy<reference>;
using difference_type = typename IterTraits::difference_type;
using iterator_category = std::forward_iterator_tag;

constexpr InclusiveScanIterator() = default;

LZ_CONSTEXPR_CXX_14 InclusiveScanIterator(Iterator iterator, T init, BinaryOp binaryOp) :
_reducer(std::move(init)),
_binaryOp(std::move(binaryOp)),
_iterator(std::move(iterator)) {
}

LZ_NODISCARD LZ_CONSTEXPR_CXX_14 typename std::remove_reference<reference>::type const& operator*() const {
return _reducer;
}

LZ_NODISCARD LZ_CONSTEXPR_CXX_14 reference operator*() {
return _reducer;
}

LZ_CONSTEXPR_CXX_20 InclusiveScanIterator& operator++() {
++_iterator;
_reducer = _binaryOp(std::move(_reducer), *_iterator);
return *this;
}

LZ_CONSTEXPR_CXX_20 InclusiveScanIterator operator++(int) {
InclusiveScanIterator tmp(*this);
++*this;
return tmp;
}

LZ_NODISCARD constexpr friend bool operator!=(const InclusiveScanIterator& a, const InclusiveScanIterator& b) {
return a._iterator != b._iterator;
}

LZ_NODISCARD constexpr friend bool operator==(const InclusiveScanIterator& a, const InclusiveScanIterator& b) {
return !(a != b); // NOLINT
}
};
} // namespace internal
} // namespace lz

#endif
2 changes: 1 addition & 1 deletion include/Lz/detail/LzTools.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ template<class Function, class... Args>
using FunctionReturnType = decltype(std::declval<Function>()(std::declval<Args>()...));

template<class Iterable>
using ValueTypeIterable = typename Decay<Iterable>::value_type;
using ValueTypeIterable = typename std::iterator_traits<IterTypeFromIterable<Iterable>>::value_type;

template<class Iterable>
using DiffTypeIterable = typename std::iterator_traits<IterTypeFromIterable<Iterable>>::difference_type;
Expand Down
1 change: 1 addition & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ add_executable(cpp-lazy-tests
generate-tests.cpp
generate-while-tests.cpp
group-by-tests.cpp
inclusive-scan-tests.cpp
init-tests.cpp
join-tests.cpp
join-where-tests.cpp
Expand Down
2 changes: 1 addition & 1 deletion tests/exclusive-scan-tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

TEST_CASE("ExclusiveScan basic functionality", "[ExclusiveScan][Basic functionality]") {
int arr[] = { 3, 1, 4, 1, 5, 9, 2, 6 };
auto scan = lz::eScan(arr, 0);
auto scan = lz::eScan(arr);

CHECK(*scan.begin() == 0);
CHECK(std::distance(std::begin(scan), std::end(scan)) == std::distance(std::begin(arr), std::end(arr)));
Expand Down
Loading

0 comments on commit 2ee4f6e

Please sign in to comment.