zip.h 4.91 KB
Newer Older
Jan Koniarik's avatar
Jan Koniarik committed
1
#include "emlabcpp/iterators/numeric.h"
Jan Koniarik's avatar
Jan Koniarik committed
2
#include "emlabcpp/types.h"
Jan Koniarik's avatar
Jan Koniarik committed
3
#include "emlabcpp/view.h"
4
5
6
7
#include <tuple>

#pragma once

Jan Koniarik's avatar
Jan Koniarik committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace emlabcpp {
template <typename...>
class zip_iterator;
}

template <typename... Iterators>
struct std::iterator_traits<emlabcpp::zip_iterator<Iterators...>> {
        using value_type      = std::tuple<typename std::iterator_traits<Iterators>::reference...>;
        using difference_type = std::ptrdiff_t;
        using pointer         = void;
        using reference       = value_type;
        using iterator_category = std::bidirectional_iterator_tag;
};

22
23
namespace emlabcpp {

24
25
26
27
28
/// zip_ierator iterates over a group of iterators, where value is a tuple of references to value
/// for each iterator.
///
/// The design expects that all ranges of iterators are of same size.
///
29
30
31
32
33
34
35
template <typename... Iterators>
class zip_iterator {
        std::tuple<Iterators...> iters_;

      public:
        constexpr zip_iterator(Iterators... iters) : iters_(std::move(iters)...) {}

36
        /// Increases each iterator
37
38
39
40
41
42
43
44
45
46
        constexpr zip_iterator operator++() {
                std::apply(
                    [](auto &&... it) { //
                            (++it, ...);
                    },
                    iters_);

                return *this;
        }

47
        /// Decreases each iterator
48
49
50
51
52
53
54
55
56
57
        constexpr zip_iterator operator--() {
                std::apply(
                    [](auto &&... it) { //
                            (++it, ...);
                    },
                    iters_);

                return *this;
        }

58
59
60
61
62
63
64
65
66
        constexpr zip_iterator &operator+=(std::ptrdiff_t m) {
                for_each(iters_, [&](auto &iter) { iter += m; });
                return *this;
        }

        constexpr std::ptrdiff_t operator-(const zip_iterator<Iterators...> &other) const {
                return std::get<0>(iters_) - std::get<0>(other.iters_);
        }

67
68
        /// Dereference of each iterator, returns tuple of references to the
        /// operator* of iterators.
69
70
71
72
73
74
75
76
        constexpr auto operator*() {
                return std::apply(
                    [](auto &&... it) { //
                            return std::forward_as_tuple((*it)...);
                    },
                    iters_);
        }

77
        /// Two zip iterators are equal if all of their iterators are equal
78
79
80
81
82
83
84
85
86
87
88
89
90
        constexpr bool operator==(const zip_iterator<Iterators...> &other) const {
                return equals(other, std::index_sequence_for<Iterators...>{});
        }

      private:
        template <typename std::size_t... Idx>
        constexpr bool equals(const zip_iterator<Iterators...> &other,
                              std::index_sequence<Idx...>) const {
                return ((std::get<Idx>(iters_) == std::get<Idx>(other.iters_)) || ...);
        }
};

template <typename... Iterators>
91
92
93
94
95
96
constexpr zip_iterator<Iterators...> operator+(zip_iterator<Iterators...> lh, std::ptrdiff_t m) {
        lh += m;
        return lh;
}

template <typename... Iterators>
97
98
99
100
101
constexpr bool operator!=(const zip_iterator<Iterators...> &lh,
                          const zip_iterator<Iterators...> &rh) {
        return !(lh == rh);
}

102
103
104
105
106
107
/// Creates a view of zip iterators for specified containers.
///
/// Beware that the function does not check that containers have same size of
/// ranges. If the size differs, increments of begin iterator will never be same
/// as end iterator.
//
108
template <range_container... Ts>
109
110
111
112
inline auto zip(Ts &&... cont) {
        return view(zip_iterator(std::begin(cont)...), zip_iterator(std::end(cont)...));
}

Jan Koniarik's avatar
Jan Koniarik committed
113
114
115
116
117
template <typename Container>
inline auto enumerate(Container &&cont) {
        return zip(range(cont.size()), cont);
}

Jan Koniarik's avatar
Jan Koniarik committed
118
119
120
121
122
123
124
125
126
127
128
template <typename TuplesTuple, std::size_t... ItemIndexes, std::size_t... TupleIndexes>
inline auto tuple_zip_impl(TuplesTuple &&tpls, std::index_sequence<ItemIndexes...>,
                           std::index_sequence<TupleIndexes...>) {
        auto f = [&](auto index) { //
                return std::make_tuple(
                    std::get<decltype(index)::value>(std::get<TupleIndexes>(tpls))...);
        };

        return std::make_tuple(f(std::integral_constant<std::size_t, ItemIndexes>{})...);
}

129
130
131
132
/// Zips a set of tuples of same size into a new tuple.
///
/// zip(tuple<A,B>(), tuple<C,D>()) -> tuple<tuple<A,C>, <tuple<B,d>>;
///
Jan Koniarik's avatar
Jan Koniarik committed
133
134
135
136
137
138
139
140
141
142
template <typename Tuple, typename... Tuples, std::enable_if_t<is_std_tuple_v<Tuple>> * = nullptr,
          std::enable_if_t<std::conjunction_v<is_std_tuple<std::decay_t<Tuples>>...>> * = nullptr>
inline auto zip(Tuple &&frst, Tuples &&... tpls) {
        static_assert(((static_size_v<Tuple> == static_size_v<Tuples>)&&...),
                      "All tuples has to be of same size in zip");
        return tuple_zip_impl(std::make_tuple(frst, tpls...),
                              std::make_index_sequence<static_size_v<Tuple>>{},
                              std::make_index_sequence<sizeof...(Tuples) + 1>{});
}

143
} // namespace emlabcpp