33
44#include < algorithm>
55#include < functional>
6+ #include < limits>
67#include < ranges>
78#include < set>
89#include < unordered_set>
@@ -320,39 +321,76 @@ namespace dice::template_library {
320321
321322
322323namespace dice ::template_library {
324+
325+ template <typename S, typename T>
326+ concept step_for = std::is_default_constructible_v<S> && requires (T start, T const stop, S step) {
327+ { start <= stop } -> std::convertible_to<bool >;
328+ { start >= stop } -> std::convertible_to<bool >;
329+
330+ start += step;
331+ };
332+
323333 namespace ranges_algo_detail {
324334
325335 // The view that represents the generated sequence.
326336 // range generator view (python-like iota with step)
327- template <std::integral T >
328- struct range_generator_view : std::ranges::view_interface<range_generator_view<T>> {
337+ template <typename T, step_for<T> S >
338+ struct range_generator_view : std::ranges::view_interface<range_generator_view<T, S >> {
329339 struct iterator ;
330340 using sentinel = std::default_sentinel_t ;
331341
332342 private:
333343 T start_;
334344 T stop_;
335- std:: make_signed_t <T> step_;
345+ S step_;
336346
337347 public:
338- explicit constexpr range_generator_view (T start, T stop, std:: make_signed_t <T> step) noexcept
348+ explicit constexpr range_generator_view (T start, T stop, S step)
339349 : start_{start},
340350 stop_{stop},
341351 step_{step} {
352+
353+ if (step == S{}) [[unlikely]] {
354+ throw std::invalid_argument{" range: step must not be the zero element/the additive identity" };
355+ }
342356 }
343357
344358 constexpr iterator begin () const noexcept {
345359 return iterator{*this , start_};
346360 }
347361
348- constexpr sentinel end () const noexcept {
362+ static constexpr sentinel end () noexcept {
349363 return std::default_sentinel;
350364 }
365+
366+ constexpr size_t size () const noexcept requires (std::integral<T> && std::integral<S>) {
367+ // Math for calculating result: ceil(|stop_ - start_| / |step_|)
368+ // For integers we need to adjust the formula to avoid overflows, conversions and inefficient operations.
369+ // Note: ceil(a / b) = (a + b - 1) / b
370+
371+ if (start_ <= stop_) {
372+ // forward
373+ if (step_ < 0 ) {
374+ // wrong direction
375+ return 0 ;
376+ }
377+
378+ return (stop_ - start_ + step_ - 1 ) / step_;
379+ } else {
380+ // backward
381+ if (step_ > 0 ) {
382+ // wrong direction
383+ return 0 ;
384+ }
385+
386+ return (start_ - stop_ + -step_ - 1 ) / -step_;
387+ }
388+ }
351389 };
352390
353391 // The iterator that generates the numbers on the fly.
354- template <std::integral T >
355- struct range_generator_view <T>::iterator {
392+ template <typename T, step_for<T> S >
393+ struct range_generator_view <T, S >::iterator {
356394 using iterator_category = std::input_iterator_tag;
357395 using value_type = T;
358396 using difference_type = std::ptrdiff_t ;
@@ -382,7 +420,7 @@ namespace dice::template_library {
382420 friend constexpr bool operator ==(iterator const &self, std::default_sentinel_t ) noexcept {
383421 // The end is reached if the step is positive and value is >= stop,
384422 // or if the step is negative and value is <= stop.
385- if (self.parent_ ->step_ > 0 ) {
423+ if (self.parent_ ->step_ > S{} ) {
386424 return self.value_ >= self.parent_ ->stop_ ;
387425 }
388426 return self.value_ <= self.parent_ ->stop_ ;
@@ -402,20 +440,19 @@ namespace dice::template_library {
402440 * - `range<T>(start, stop)`: Generates [start,stop).
403441 * - `range<T>(start, stop, step)`: Generates numbers from start, incrementing by step, until stop is met or passed.
404442 */
405- template <std::integral T, typename S = T >
443+ template <typename T, typename S>
406444 constexpr auto range (T start, T stop, S step) noexcept {
407- static_assert (std::is_integral_v<S>, " Step must be an integral type." );
408- return ranges_algo_detail::range_generator_view<T>(start, stop, static_cast <std::make_signed_t <T>>(step));
445+ return ranges_algo_detail::range_generator_view<T, S>(start, stop, step);
409446 }
410447
411- template <std::integral T>
448+ template <typename T> requires ( std::is_constructible_v<T, int > && step_for<T, T>)
412449 constexpr auto range(T start, T stop) noexcept {
413- return ranges_algo_detail::range_generator_view<T>(start, stop, 1 );
450+ return ranges_algo_detail::range_generator_view<T, T >(start, stop, T ( 1 ) );
414451 }
415452
416- template <std::integral T>
453+ template <typename T> requires ( std::is_default_constructible_v<T> && std::is_constructible_v<T, int >)
417454 constexpr auto range(T stop) noexcept {
418- return ranges_algo_detail::range_generator_view<T>( static_cast < T>(0 ) , stop, 1 );
455+ return ranges_algo_detail::range_generator_view<T, T>(T{} , stop, T ( 1 ) );
419456 }
420457
421458}// namespace dice::template_library
0 commit comments