585585
586586# # interface implementations
587587
588+ length (r:: AbstractRange ) = error (" length implementation missing" ) # catch mistakes
588589size (r:: AbstractRange ) = (length (r),)
589590
590591isempty (r:: StepRange ) =
592+ # steprange_last_empty(r.start, r.step, r.stop) == r.stop
591593 (r. start != r. stop) & ((r. step > zero (r. step)) != (r. stop > r. start))
592594isempty (r:: AbstractUnitRange ) = first (r) > last (r)
593595isempty (r:: StepRangeLen ) = length (r) == 0
@@ -614,68 +616,135 @@ julia> step(range(2.5, stop=10.9, length=85))
614616```
615617"""
616618step (r:: StepRange ) = r. step
617- step (r:: AbstractUnitRange{T} ) where {T} = oneunit (T) - zero (T)
619+ step (r:: AbstractUnitRange{T} ) where {T} = oneunit (T) - zero (T)
618620step (r:: StepRangeLen ) = r. step
619621step (r:: StepRangeLen{T} ) where {T<: AbstractFloat } = T (r. step)
620622step (r:: LinRange ) = (last (r)- first (r))/ r. lendiv
621623
622624step_hp (r:: StepRangeLen ) = r. step
623625step_hp (r:: AbstractRange ) = step (r)
624626
625- unsafe_length (r:: AbstractRange ) = length (r) # generic fallback
626-
627- function unsafe_length (r:: StepRange )
628- n = Integer (div ((r. stop - r. start) + r. step, r. step))
629- isempty (r) ? zero (n) : n
630- end
631- length (r:: StepRange ) = unsafe_length (r)
632- unsafe_length (r:: AbstractUnitRange ) = Integer (last (r) - first (r) + step (r))
633- unsafe_length (r:: OneTo ) = Integer (r. stop - zero (r. stop))
634- length (r:: AbstractUnitRange ) = unsafe_length (r)
635- length (r:: OneTo ) = unsafe_length (r)
636- length (r:: StepRangeLen ) = r. len
637- length (r:: LinRange ) = r. len
627+ axes (r:: AbstractRange ) = (oneto (length (r)),)
638628
639629# Needed to fold the `firstindex` call in SimdLoop.simd_index
640630firstindex (:: UnitRange ) = 1
641631firstindex (:: StepRange ) = 1
642632firstindex (:: LinRange ) = 1
643633
644- function length (r:: StepRange{T} ) where T<: Union{Int,UInt,Int64,UInt64,Int128,UInt128}
645- isempty (r) && return zero (T)
646- if r. step > 1
647- return checked_add (convert (T, div (unsigned (r. stop - r. start), r. step)), one (T))
648- elseif r. step < - 1
649- return checked_add (convert (T, div (unsigned (r. start - r. stop), - r. step)), one (T))
650- elseif r. step > 0
651- return checked_add (div (checked_sub (r. stop, r. start), r. step), one (T))
634+ # n.b. checked_length for these is defined iff checked_add and checked_sub are
635+ # defined between the relevant types
636+ function checked_length (r:: OrdinalRange{T} ) where T
637+ s = step (r)
638+ # s != 0, by construction, but avoids the division error later
639+ start = first (r)
640+ if s == zero (s) || isempty (r)
641+ return Integer (start - start + zero (s))
642+ end
643+ stop = last (r)
644+ if isless (s, zero (s))
645+ diff = checked_sub (start, stop)
646+ s = - s
652647 else
653- return checked_add ( div ( checked_sub (r . start, r . stop), - r . step), one (T) )
648+ diff = checked_sub (stop, start )
654649 end
650+ a = Integer (div (diff, s))
651+ return checked_add (a, one (a))
655652end
656653
657- function length (r:: AbstractUnitRange{T} ) where T<: Union{Int,Int64,Int128}
654+ function checked_length (r:: AbstractUnitRange{T} ) where T
655+ # compiler optimization: remove dead cases from above
656+ if isempty (r)
657+ return Integer (first (r) - first (r))
658+ end
659+ a = Integer (checked_add (checked_sub (last (r), first (r))))
660+ return checked_add (a, one (a))
661+ end
662+
663+ function length (r:: OrdinalRange{T} ) where T
664+ s = step (r)
665+ # s != 0, by construction, but avoids the division error later
666+ start = first (r)
667+ if s == zero (s) || isempty (r)
668+ return Integer (start - start + zero (s))
669+ end
670+ stop = last (r)
671+ if isless (s, zero (s))
672+ diff = start - stop
673+ s = - s
674+ else
675+ diff = stop - start
676+ end
677+ a = Integer (div (diff, s))
678+ return a + one (a)
679+ end
680+
681+
682+ function length (r:: AbstractUnitRange{T} ) where T
658683 @_inline_meta
659- checked_add (checked_sub (last (r), first (r)), one (T))
684+ a = Integer (last (r) - first (r)) # even when isempty, by construction (with overflow)
685+ return a + one (a)
660686end
661- length (r:: OneTo{T} ) where {T<: Union{Int,Int64} } = T (r. stop)
662687
663- length (r:: AbstractUnitRange{T} ) where {T<: Union{UInt,UInt64,UInt128} } =
664- r. stop < r. start ? zero (T) : checked_add (last (r) - first (r), one (T))
688+ length (r:: OneTo ) = Integer (r. stop - zero (r. stop))
689+ length (r:: StepRangeLen ) = r. len
690+ length (r:: LinRange ) = r. len
665691
666- # some special cases to favor default Int type
667- let smallint = (Int === Int64 ?
668- Union{Int8,UInt8,Int16,UInt16,Int32,UInt32} :
669- Union{Int8,UInt8,Int16,UInt16})
670- global length
671-
672- function length (r:: StepRange{<:smallint} )
673- isempty (r) && return Int (0 )
674- div (Int (r. stop)+ Int (r. step) - Int (r. start), Int (r. step))
692+ let bigints = Union{Int, UInt, Int64, UInt64, Int128, UInt128}
693+ global length, checked_length
694+ # compile optimization for which promote_type(T, Int) == T
695+ length (r:: OneTo{T} ) where {T<: bigints } = r. stop
696+ # slightly more accurate length and checked_length in extreme cases
697+ # (near typemax) for types with known `unsigned` functions
698+ function length (r:: OrdinalRange{T} ) where T<: bigints
699+ s = step (r)
700+ s == zero (s) && return zero (T) # unreachable, by construction, but avoids the error case here later
701+ isempty (r) && return zero (T)
702+ diff = last (r) - first (r)
703+ # if |s| > 1, diff might have overflowed, but unsigned(diff)÷s should
704+ # therefore still be valid (if the result is representable at all)
705+ # n.b. !(s isa T)
706+ if s isa Unsigned || - 1 <= s <= 1 || s == - s
707+ a = div (diff, s)
708+ elseif s < 0
709+ a = div (unsigned (- diff), - s) % typeof (diff)
710+ else
711+ a = div (unsigned (diff), s) % typeof (diff)
712+ end
713+ return Integer (a) + one (a)
714+ end
715+ function checked_length (r:: OrdinalRange{T} ) where T<: bigints
716+ s = step (r)
717+ s == zero (s) && return zero (T) # unreachable, by construction, but avoids the error case here later
718+ isempty (r) && return zero (T)
719+ stop, start = last (r), first (r)
720+ # n.b. !(s isa T)
721+ if s > 1
722+ diff = stop - start
723+ a = convert (T, div (unsigned (diff), s))
724+ elseif s < - 1
725+ diff = start - stop
726+ a = convert (T, div (unsigned (diff), - s))
727+ elseif s > 0
728+ a = div (checked_sub (stop, start), s)
729+ else
730+ a = div (checked_sub (start, stop), - s)
731+ end
732+ return checked_add (a, one (a))
675733 end
734+ end
676735
677- length (r:: AbstractUnitRange{<:smallint} ) = Int (last (r)) - Int (first (r)) + 1
678- length (r:: OneTo{<:smallint} ) = Int (r. stop)
736+ # some special cases to favor default Int type
737+ let smallints = (Int === Int64 ?
738+ Union{Int8, UInt8, Int16, UInt16, Int32, UInt32} :
739+ Union{Int8, UInt8, Int16, UInt16})
740+ global length, checked_length
741+ # n.b. !(step isa T)
742+ length (r:: OrdinalRange{<:smallints} ) = div (Int (last (r)) - Int (first (r)), step (r)) + 1
743+ length (r:: AbstractUnitRange{<:smallints} ) = Int (last (r)) - Int (first (r)) + 1
744+ length (r:: OneTo{<:smallints} ) = Int (r. stop)
745+ checked_length (r:: OrdinalRange{<:smallints} ) = length (r)
746+ checked_length (r:: AbstractUnitRange{<:smallints} ) = length (r)
747+ checked_length (r:: OneTo{<:smallints} ) = length (r)
679748end
680749
681750first (r:: OrdinalRange{T} ) where {T} = convert (T, r. start)
0 commit comments