diff --git a/include/cartesian_product.hpp b/include/cartesian_product.hpp index 81fdcbd..59aaf84 100644 --- a/include/cartesian_product.hpp +++ b/include/cartesian_product.hpp @@ -1197,12 +1197,15 @@ namespace tl { class cursor { template using constify = std::conditional_t; - + template + using intify = std::conditional_t; // Instead of storing a pointer to the views, we'll store the sentinels: // constify>* bases_; std::tuple>...> currents_{}; std::tuple>...> begins_{}; std::tuple>...> ends_{}; + std::tuple...> counts_{}; + int idx_; public: using reference = @@ -1217,6 +1220,8 @@ namespace tl { : currents_( tl::tuple_transform(std::ranges::begin, *bases) ) , begins_( currents_ ) , ends_( tl::tuple_transform(std::ranges::end, *bases) ) + , counts_( tl::tuple_transform(std::ranges::size, *bases) ) + , idx_(0) {} //If the underlying ranges are common, we can get to the end by assigning from end @@ -1246,66 +1251,36 @@ namespace tl { return tuple_transform([](auto& i) -> decltype(auto) { return *i; }, currents_); } + template + void update(int idx) { + if constexpr(N == 0) + std::get(currents_) = idx + std::get(begins_); + else + std::get(currents_) = idx % std::get(counts_) + std::get(begins_); + if constexpr (N > 0) { + idx /= std::get(counts_); + update(idx); + } + } + //Increment the iterator at std::get(currents_) //If that iterator hits its end, recurse to std::get template void next() { - auto& it = std::get(currents_); - ++it; - if (it == std::get(ends_)) { - if constexpr (N > 0) { - it = std::get(begins_); - next(); - } - } + advance(1); } //Decrement the iterator at std::get(currents_) //If that iterator was at its begin, cycle it to end and recurse to std::get template void prev() requires (am_bidirectional...>) { - auto& it = std::get(currents_); - if (it == std::get(begins_)) { - std::ranges::advance(it, std::get(ends_)); - if constexpr (N > 0) { - prev(); - } - } - --it; + advance(-1); } template void advance(difference_type n) requires (am_random_access...>) { - auto& it = std::get(currents_); - auto begin = std::get(begins_); - auto end = std::get(ends_); - auto size = end - begin; - - auto distance_from_begin = it - begin; - - //Calculate where in the iterator cycle we should end up - auto offset = (distance_from_begin + n) % static_cast(size); - - //Calculate how many times incrementing this iterator would cause it to cycle round - //This will be negative if we cycled by decrementing - auto times_cycled = (distance_from_begin + n) / size - (offset < 0 ? 1 : 0); - - //Set the iterator to the correct new position - it = begin + (offset < 0 ? offset + static_cast(size) : offset); - - if constexpr (N > 0) { - //If this iterator cycled, then we need to advance the N-1th iterator - //by the number of times it cycled - if (times_cycled != 0) { - advance(times_cycled); - } - } - else { - //If we're the 0th iterator, then cycling should set the iterator to the end - if (times_cycled > 0) { - std::ranges::advance(it, end); - } - } + idx_ += n; + update(idx_); } constexpr bool equal(const cursor& rhs) const