From c55dbc2dc221c91fe6378ae6f3ef45d9d5931504 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 17 Dec 2021 11:17:56 -0500 Subject: [PATCH] fixup! promote ranges to the largest of the start, step, or length (as applicable) --- base/broadcast.jl | 4 ++ base/math.jl | 4 +- base/range.jl | 61 ++++++++++++---------------- stdlib/Dates/test/periods.jl | 6 +-- stdlib/LinearAlgebra/test/special.jl | 18 +++++--- test/ranges.jl | 14 ++++--- test/testhelpers/Furlongs.jl | 17 +++++++- 7 files changed, 70 insertions(+), 54 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 971727768a4bec..bd28831be0e910 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -1130,6 +1130,7 @@ broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::LinRange, x::Number) = LinRa broadcasted(::DefaultArrayStyle{1}, ::typeof(*), r::OrdinalRange, x::AbstractFloat) = Base.range_start_step_length(first(r)*x, step(r)*x, length(r)) +#broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::AbstractRange, x::Number) = range(first(r)/x, last(r)/x, length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::AbstractRange, x::Number) = range(first(r)/x, step=step(r)/x, length=length(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(/), r::StepRangeLen{T}, x::Number) where {T} = StepRangeLen{typeof(T(r.ref)/x)}(r.ref/x, r.step/x, length(r), r.offset) @@ -1139,6 +1140,9 @@ broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::AbstractRange) = broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::StepRangeLen) = StepRangeLen(x\r.ref, x\r.step, length(r), r.offset) broadcasted(::DefaultArrayStyle{1}, ::typeof(\), x::Number, r::LinRange) = LinRange(x \ r.start, x \ r.stop, r.len) +broadcasted(::DefaultArrayStyle{1}, ::typeof(÷), r::AbstractRange, x::Number) = range(first(r) ÷ x, last(r) ÷ x, length=length(r)) +broadcasted(::DefaultArrayStyle{1}, ::typeof(÷), r::LinRange, x::Number) = LinRange(r.start ÷ x, r.stop ÷ x, r.len) + broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::UnitRange) = big(r.start):big(last(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::StepRange) = big(r.start):big(r.step):big(last(r)) broadcasted(::DefaultArrayStyle{1}, ::typeof(big), r::StepRangeLen) = StepRangeLen(big(r.ref), big(r.step), length(r), r.offset) diff --git a/base/math.jl b/base/math.jl index 9a5e5377cf6132..cf3274060eb966 100644 --- a/base/math.jl +++ b/base/math.jl @@ -682,7 +682,7 @@ function _hypot(x, y) # Return Inf if either or both inputs is Inf (Compliance with IEEE754) if isinf(ax) || isinf(ay) - return oftype(axu, Inf) + return typeof(axu)(Inf) end # Order the operands @@ -745,7 +745,7 @@ _hypot(x::ComplexF16, y::ComplexF16) = Float16(_hypot(ComplexF32(x), ComplexF32( function _hypot(x...) maxabs = maximum(abs, x) if isnan(maxabs) && any(isinf, x) - return oftype(maxabs, Inf) + return typeof(maxabs)(Inf) elseif (iszero(maxabs) || isinf(maxabs)) return maxabs else diff --git a/base/range.jl b/base/range.jl index be72b0b6bdbaab..83cbd43eaa81ef 100644 --- a/base/range.jl +++ b/base/range.jl @@ -167,46 +167,38 @@ range_length(len::Integer) = OneTo(len) range_stop(stop) = range_start_stop(oftype(stop, 1), stop) range_stop(stop::Integer) = range_length(stop) -# Stop and length as the only argument -range_stop_length(a::Real, len::Integer) = UnitRange(promote(a - (len - oneunit(len)), a)...) -range_stop_length(a::AbstractFloat, len::Integer) = range_step_stop_length(oftype(a, 1), a, len) -range_stop_length(a, len::Integer) = range_step_stop_length(oftype(a - a, 1), a, len) - range_step_stop_length(step, stop, length) = reverse(range_start_step_length(stop, -step, length)) -range_start_length(a::Real, len::Integer) = UnitRange(promote(a, a + (len - oneunit(len)))...) -range_start_length(a::AbstractFloat, len::Integer) = range_start_step_length(a, oftype(a, 1), len) -range_start_length(a, len::Integer) = range_start_step_length(a, oftype(a - a, 1), len) - -range_start_stop(start, stop) = start:stop - -function range_start_step_length(a::AbstractFloat, step::AbstractFloat, len::Integer) - range_start_step_length(promote(a, step)..., len) -end - -function range_start_step_length(a::Real, step::AbstractFloat, len::Integer) - range_start_step_length(float(a), step, len) -end - -function range_start_step_length(a::AbstractFloat, step::Real, len::Integer) - range_start_step_length(a, float(step), len) +# Stop and length as the only argument +function range_stop_length(a, len::Integer) + step = oftype(a - a, 1) # assert that step is representable + start = a - (len - oneunit(len)) + if start isa Signed + # overflow in recomputing length from stop is okay + return UnitRange(start, oftype(start, a)) + end + return range_step_stop_length(oftype(a - a, 1), a, len) end -function range_start_step_length(a::T, step::T, len::Integer) where {T <: AbstractFloat} - _rangestyle(OrderStyle(T), ArithmeticStyle(T), a, step, len) +# Start and length as the only argument +function range_start_length(a, len::Integer) + step = oftype(a - a, 1) # assert that step is representable + stop = a + (len - oneunit(len)) + if stop isa Signed + # overflow in recomputing length from stop is okay + return UnitRange(oftype(stop, a), stop) + end + return range_start_step_length(a, oftype(a - a, 1), len) end -function range_start_step_length(a::T, step, len::Integer) where {T} - _rangestyle(OrderStyle(T), ArithmeticStyle(T), a, step, len) -end +range_start_stop(start, stop) = start:stop -function _rangestyle(::Ordered, ::ArithmeticWraps, a, step, len::Integer) - stop = a + step * (len - oneunit(len)) - T = typeof(stop) - return StepRange{T,typeof(step)}(convert(T, a), step, stop) -end -function _rangestyle(::Any, ::Any, a, step, len::Integer) +function range_start_step_length(a, step, len::Integer) stop = a + step * (len - oneunit(len)) + if stop isa Signed + # overflow in recomputing length from stop is okay + return StepRange{typeof(stop),typeof(step)}(convert(typeof(stop), a), step, stop) + end return StepRangeLen{typeof(stop),typeof(a),typeof(step)}(a, step, len) end @@ -952,8 +944,9 @@ end function lerpi(j::Integer, d::Integer, a::T, b::T) where T @inline - t = j/d - T((1-t)*a + t*b) + t = j/d # ∈ [0,1] + # compute approximately fma(t, b, -fma(t, a, a)) + return T((1-t)*a + t*b) end getindex(r::AbstractRange, ::Colon) = copy(r) diff --git a/stdlib/Dates/test/periods.jl b/stdlib/Dates/test/periods.jl index 3bb310be4ee84a..0467841fb6261c 100644 --- a/stdlib/Dates/test/periods.jl +++ b/stdlib/Dates/test/periods.jl @@ -179,10 +179,8 @@ end @test_throws InexactError y * 3//4 @test (1:1:5)*Second(5) === Second(5)*(1:1:5) === Second(5):Second(5):Second(25) === (1:5)*Second(5) @test collect(1:1:5)*Second(5) == Second(5)*collect(1:1:5) == (1:5)*Second(5) - @test (Second(2):Second(2):Second(10))/Second(2) === 1.0:1.0:5.0 - @test collect(Second(2):Second(2):Second(10))/Second(2) == 1:1:5 - @test (Second(2):Second(2):Second(10)) / 2 === Second(1):Second(1):Second(5) - @test collect(Second(2):Second(2):Second(10)) / 2 == Second(1):Second(1):Second(5) + @test (Second(2):Second(2):Second(10))/Second(2) === 1.0:1.0:5.0 == collect(Second(2):Second(2):Second(10))/Second(2) + @test (Second(2):Second(2):Second(10)) / 2 == Second(1):Second(1):Second(5) == collect(Second(2):Second(2):Second(10)) / 2 @test Dates.Year(4) / 2 == Dates.Year(2) @test Dates.Year(4) / 2f0 == Dates.Year(2) @test Dates.Year(4) / 0.5 == Dates.Year(8) diff --git a/stdlib/LinearAlgebra/test/special.jl b/stdlib/LinearAlgebra/test/special.jl index e0c5f87111b07a..fa78249043323e 100644 --- a/stdlib/LinearAlgebra/test/special.jl +++ b/stdlib/LinearAlgebra/test/special.jl @@ -395,12 +395,18 @@ using .Main.Furlongs @test one(S) isa SymTridiagonal # eltype with dimensions - D = Diagonal{Furlong{2, Int64}}([1, 2, 3, 4]) - Bu = Bidiagonal{Furlong{2, Int64}}([1, 2, 3, 4], [1, 2, 3], 'U') - Bl = Bidiagonal{Furlong{2, Int64}}([1, 2, 3, 4], [1, 2, 3], 'L') - T = Tridiagonal{Furlong{2, Int64}}([1, 2, 3], [1, 2, 3, 4], [1, 2, 3]) - S = SymTridiagonal{Furlong{2, Int64}}([1, 2, 3, 4], [1, 2, 3]) - mats = [D, Bu, Bl, T, S] + D0 = Diagonal{Furlong{0, Int64}}([1, 2, 3, 4]) + Bu0 = Bidiagonal{Furlong{0, Int64}}([1, 2, 3, 4], [1, 2, 3], 'U') + Bl0 = Bidiagonal{Furlong{0, Int64}}([1, 2, 3, 4], [1, 2, 3], 'L') + T0 = Tridiagonal{Furlong{0, Int64}}([1, 2, 3], [1, 2, 3, 4], [1, 2, 3]) + S0 = SymTridiagonal{Furlong{0, Int64}}([1, 2, 3, 4], [1, 2, 3]) + F2 = Furlongs.Furlong{2}(1) + D2 = Diagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2) + Bu2 = Bidiagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2, [1, 2, 3].*F2, 'U') + Bl2 = Bidiagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2, [1, 2, 3].*F2, 'L') + T2 = Tridiagonal{Furlong{2, Int64}}([1, 2, 3].*F2, [1, 2, 3, 4].*F2, [1, 2, 3].*F2) + S2 = SymTridiagonal{Furlong{2, Int64}}([1, 2, 3, 4].*F2, [1, 2, 3].*F2) + mats = Any[D0, Bu0, Bl0, T0, S0, D2, Bu2, Bl2, T2, S2] for A in mats @test iszero(zero(A)) @test isone(one(A)) diff --git a/test/ranges.jl b/test/ranges.jl index 47d18c53bacd38..03f6fb04fbfd0a 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -1517,8 +1517,10 @@ isdefined(Main, :Furlongs) || @eval Main include("testhelpers/Furlongs.jl") using .Main.Furlongs @testset "dimensional correctness" begin - @test length(Vector(Furlong(2):Furlong(10))) == 9 - @test length(range(Furlong(2), length=9)) == checked_length(range(Furlong(2), length=9)) == 9 + @test_throws TypeError Furlong(2):Furlong(10) + @test_throws TypeError range(Furlong(2), length=9) + @test length(Vector(Furlong(2):Furlong(1):Furlong(10))) == 9 + @test length(range(Furlong(2), step=Furlong(1), length=9)) == checked_length(range(Furlong(2), step=Furlong(1), length=9)) == 9 @test @inferred(length(StepRange(Furlong(2), Furlong(1), Furlong(1)))) == 0 @test Vector(Furlong(2):Furlong(1):Furlong(10)) == Vector(range(Furlong(2), step=Furlong(1), length=9)) == Furlong.(2:10) @test Vector(Furlong(1.0):Furlong(0.5):Furlong(10.0)) == @@ -2260,16 +2262,16 @@ end # test behavior of wrap-around and promotion of empty ranges (#35711) @test length(range(0, length=UInt(0))) === UInt(0) -@test !isempty(range(0, length=UInt(0))) +@test isempty(range(0, length=UInt(0))) @test length(range(typemax(Int), length=UInt(0))) === UInt(0) @test isempty(range(typemax(Int), length=UInt(0))) -@test length(range(0, length=UInt(0), step=UInt(2))) == typemin(Int) % UInt -@test !isempty(range(0, length=UInt(0), step=UInt(2))) +@test length(range(0, length=UInt(0), step=UInt(2))) == UInt(0) +@test isempty(range(0, length=UInt(0), step=UInt(2))) @test length(range(typemax(Int), length=UInt(0), step=UInt(2))) === UInt(0) @test isempty(range(typemax(Int), length=UInt(0), step=UInt(2))) @test length(range(typemax(Int), length=UInt(0), step=2)) === UInt(0) @test isempty(range(typemax(Int), length=UInt(0), step=2)) -@test length(range(typemax(Int), length=0, step=UInt(2))) === UInt(0) +@test length(range(typemax(Int), length=0, step=UInt(2))) === 0 @test isempty(range(typemax(Int), length=0, step=UInt(2))) @test length(range(1, length=typemax(Int128))) === typemax(Int128) diff --git a/test/testhelpers/Furlongs.jl b/test/testhelpers/Furlongs.jl index 67c2023a0bc843..8ac22c6244cd33 100644 --- a/test/testhelpers/Furlongs.jl +++ b/test/testhelpers/Furlongs.jl @@ -15,13 +15,26 @@ end Furlong(x::T) where {T<:Number} = Furlong{1,T}(x) Furlong(x::Furlong) = x (::Type{T})(x::Furlong{0}) where {T<:Number} = T(x.val)::T +(::Type{T})(x::Furlong{0}) where {T<:Furlong{0}} = T(x.val)::T +(::Type{T})(x::Furlong{0}) where {T<:Furlong} = typeassert(x, T) Furlong{p}(v::Number) where {p} = Furlong{p,typeof(v)}(v) -Furlong{p}(x::Furlong{q}) where {p,q} = (@assert(p==q); Furlong{p,typeof(x.val)}(x.val)) -Furlong{p,T}(x::Furlong{q}) where {T,p,q} = (@assert(p==q); Furlong{p,T}(T(x.val))) +Furlong{p}(x::Furlong{q}) where {p,q} = (typeassert(x, Furlong{p}); Furlong{p,typeof(x.val)}(x.val)) +Furlong{p,T}(x::Furlong{q}) where {T,p,q} = (typeassert(x, Furlong{p}); Furlong{p,T}(T(x.val))) Base.promote_type(::Type{Furlong{p,T}}, ::Type{Furlong{p,S}}) where {p,T,S} = Furlong{p,promote_type(T,S)} +# only Furlong{0} forms a ring and isa Number +Base.convert(::Type{T}, y::Number) where {T<:Furlong{0}} = T(y) +Base.convert(::Type{Furlong}, y::Number) = Furlong{0}(y) +Base.convert(::Type{Furlong{<:Any,T}}, y::Number) where {T<:Number} = Furlong{0,T}(y) +Base.convert(::Type{T}, y::Number) where {T<:Furlong} = typeassert(y, T) # throws, since cannot convert a Furlong{0} to a Furlong{p} +# other Furlong{p} form a group +Base.convert(::Type{T}, y::Furlong) where {T<:Furlong{0}} = T(y) +Base.convert(::Type{Furlong}, y::Furlong) = y +Base.convert(::Type{Furlong{<:Any,T}}, y::Furlong{p}) where {p,T<:Number} = Furlong{p,T}(y) +Base.convert(::Type{T}, y::Furlong) where {T<:Furlong} = T(y) + Base.one(x::Furlong{p,T}) where {p,T} = one(T) Base.one(::Type{Furlong{p,T}}) where {p,T} = one(T) Base.oneunit(x::Furlong{p,T}) where {p,T} = Furlong{p,T}(one(T))