/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file alternating_iterator.hpp Iterator adaptor that takes items alternating from a middle position. */ #ifndef ALTERNATING_ITERATOR_HPP #define ALTERNATING_ITERATOR_HPP #include /** * Iterator that alternately takes from the "middle" of a range. * @tparam Titer Type of iterator. */ template class AlternatingIterator { public: using value_type = typename Titer::value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; using pointer = typename Titer::pointer; using reference = typename Titer::reference; AlternatingIterator() = default; /** * Construct an AlternatingIterator. * @param first Iterator to first element. * @param last Iterator to last element. * @param middle Iterator to "middle" element, from where to start. * @param begin Whether this iterator points to the first or last elements. */ AlternatingIterator(Titer first, Titer last, Titer middle, bool begin) : first(first), last(last), middle(middle) { /* Starting from the end is not supported, unless the range is empty. */ assert(first == last || middle != last); this->position = begin ? 0 : std::distance(this->first, this->last); this->before = middle; this->after = middle; this->next_state = this->before == this->first; this->state = this->next_state; } bool operator==(const AlternatingIterator &rhs) const { assert(this->first == rhs.first); assert(this->last == rhs.last); assert(this->middle == rhs.middle); return this->position == rhs.position; } std::strong_ordering operator<=>(const AlternatingIterator &rhs) const { assert(this->first == rhs.first); assert(this->last == rhs.last); assert(this->middle == rhs.middle); return this->position <=> rhs.position; } inline reference operator*() const { return *this->Base(); } AlternatingIterator &operator++() { size_t size = static_cast(std::distance(this->first, this->last)); assert(this->position < size); ++this->position; if (this->position < size) this->Next(); return *this; } AlternatingIterator operator++(int) { AlternatingIterator result = *this; ++*this; return result; } inline Titer Base() const { return this->state ? this->after : this->before; } private: Titer first; ///< Initial first iterator. Titer last; ///< Initial last iterator. Titer middle; ///< Initial middle iterator. Titer after; ///< Current iterator after the middle. Titer before; ///< Current iterator before the middle. size_t position; ///< Position within the entire range. bool next_state; ///< Next state for advancing iterator. If true take from after middle, otherwise take from before middle. bool state; ///< Current state for reading iterator. If true take from after middle, otherwise take from before middle. void Next() { this->state = this->next_state; if (this->next_state) { assert(this->after != this->last); ++this->after; this->next_state = this->before == this->first; } else { assert(this->before != this->first); --this->before; this->next_state = std::next(this->after) != this->last; } } }; template class AlternatingView : public std::ranges::view_interface> { public: AlternatingView(std::ranges::viewable_range auto &&range, Titer middle) : first(std::ranges::begin(range)), last(std::ranges::end(range)), middle(middle) { } auto begin() const { return AlternatingIterator{first, last, middle, true}; } auto end() const { return AlternatingIterator{first, last, middle, false}; } private: Titer first; ///< Iterator to first element. Titer last; ///< Iterator to last element. Titer middle; ///< Iterator to middle element. }; #endif /* ALTERNATING_ITERATOR_HPP */