From 9928696f82bb782d2d5b96e883b2d78c3fe5fe16 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 12 Nov 2021 14:46:47 -0500 Subject: [PATCH 1/2] promote ranges to the largest of the start, step, or length (as applicable) Be careful to use `oneunit` instead of `1`, so that arithmetic on user-given types does not promote first to Int. Fixes #35711 Fixes #10554 --- base/range.jl | 65 ++++++++++++++++++++++++++++---------------------- test/ranges.jl | 16 +++++++++++++ 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/base/range.jl b/base/range.jl index f6a4356b16508..be72b0b6bdbaa 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1,21 +1,21 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -(:)(a::Real, b::Real) = (:)(promote(a,b)...) +(:)(a::Real, b::Real) = (:)(promote(a, b)...) (:)(start::T, stop::T) where {T<:Real} = UnitRange{T}(start, stop) (:)(start::T, stop::T) where {T} = (:)(start, oftype(stop >= start ? stop - start : start - stop, 1), stop) # promote start and stop, leaving step alone -(:)(start::A, step, stop::C) where {A<:Real,C<:Real} = - (:)(convert(promote_type(A,C),start), step, convert(promote_type(A,C),stop)) +(:)(start::A, step, stop::C) where {A<:Real, C<:Real} = + (:)(convert(promote_type(A, C), start), step, convert(promote_type(A, C), stop)) # AbstractFloat specializations (:)(a::T, b::T) where {T<:AbstractFloat} = (:)(a, T(1), b) -(:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a,b,c)...) -(:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...) -(:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a,b,c)...) +(:)(a::T, b::AbstractFloat, c::T) where {T<:Real} = (:)(promote(a, b, c)...) +(:)(a::T, b::AbstractFloat, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...) +(:)(a::T, b::Real, c::T) where {T<:AbstractFloat} = (:)(promote(a, b, c)...) (:)(start::T, step::T, stop::T) where {T<:AbstractFloat} = _colon(OrderStyle(T), ArithmeticStyle(T), start, step, stop) @@ -168,15 +168,15 @@ 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{typeof(a)}(oftype(a, a-len+1), a) +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_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{typeof(a)}(a, oftype(a, a+len-1)) +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_length(a, len::Integer) = range_start_step_length(a, oftype(a - a, 1), len) range_start_stop(start, stop) = start:stop @@ -201,15 +201,13 @@ function range_start_step_length(a::T, step, len::Integer) where {T} end function _rangestyle(::Ordered, ::ArithmeticWraps, a, step, len::Integer) - start = a + zero(step) - stop = a + step * (len - 1) - T = typeof(start) - return StepRange{T,typeof(step)}(start, step, convert(T, stop)) + 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) - start = a + zero(step) - T = typeof(a) - return StepRangeLen{typeof(start),T,typeof(step)}(a, step, len) + stop = a + step * (len - oneunit(len)) + return StepRangeLen{typeof(stop),typeof(a),typeof(step)}(a, step, len) end range_start_step_stop(start, step, stop) = start:step:stop @@ -893,7 +891,7 @@ _in_unit_range(v::UnitRange, val, i::Integer) = i > 0 && val <= v.stop && val >= function getindex(v::UnitRange{T}, i::Integer) where T @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) - val = convert(T, v.start + (i - 1)) + val = convert(T, v.start + (i - oneunit(i))) @boundscheck _in_unit_range(v, val, i) || throw_boundserror(v, i) val end @@ -904,7 +902,7 @@ const OverflowSafe = Union{Bool,Int8,Int16,Int32,Int64,Int128, function getindex(v::UnitRange{T}, i::Integer) where {T<:OverflowSafe} @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) - val = v.start + (i - 1) + val = v.start + (i - oneunit(i)) @boundscheck _in_unit_range(v, val, i) || throw_boundserror(v, i) val % T end @@ -919,7 +917,7 @@ end function getindex(v::AbstractRange{T}, i::Integer) where T @inline i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) - ret = convert(T, first(v) + (i - 1)*step_hp(v)) + ret = convert(T, first(v) + (i - oneunit(i))*step_hp(v)) ok = ifelse(step(v) > zero(step(v)), (ret <= last(v)) & (ret >= first(v)), (ret <= first(v)) & (ret >= last(v))) @@ -949,7 +947,7 @@ end function unsafe_getindex(r::LinRange, i::Integer) i isa Bool && throw(ArgumentError("invalid index: $i of type Bool")) - lerpi(i-1, r.lendiv, r.start, r.stop) + lerpi(i-oneunit(i), r.lendiv, r.start, r.stop) end function lerpi(j::Integer, d::Integer, a::T, b::T) where T @@ -968,8 +966,10 @@ function getindex(r::AbstractUnitRange, s::AbstractUnitRange{T}) where {T<:Integ return range(first(s) ? first(r) : last(r), length = last(s)) else f = first(r) - start = oftype(f, f + first(s)-firstindex(r)) - return range(start, length=length(s)) + start = oftype(f, f + first(s) - firstindex(r)) + len = length(s) + stop = oftype(f, start + (len - oneunit(len))) + return range(start, stop) end end @@ -984,11 +984,14 @@ function getindex(r::AbstractUnitRange, s::StepRange{T}) where {T<:Integer} @boundscheck checkbounds(r, s) if T === Bool - return range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length = last(s)) + return range(first(s) ? first(r) : last(r), step=oneunit(eltype(r)), length=last(s)) else f = first(r) - start = oftype(f, f + s.start-firstindex(r)) - return range(start, step=step(s), length=length(s)) + start = oftype(f, f + s.start - firstindex(r)) + st = step(s) + len = length(s) + stop = oftype(f, start + (len - oneunit(len)) * st) + return range(start, stop; step=st) end end @@ -1011,9 +1014,13 @@ function getindex(r::StepRange, s::AbstractRange{T}) where {T<:Integer} return range(start, step=step(r); length=len) else f = r.start + fs = first(s) st = r.step - start = oftype(f, f + (first(s)-oneunit(first(s)))*st) - return range(start; step=st*step(s), length=length(s)) + start = oftype(f, f + (fs - oneunit(fs)) * st) + st = st * step(s) + len = length(s) + stop = oftype(f, start + (len - oneunit(len)) * st) + return range(start, stop; step=st) end end @@ -1042,7 +1049,7 @@ function getindex(r::StepRangeLen{T}, s::OrdinalRange{S}) where {T, S<:Integer} # Find closest approach to offset by s ind = LinearIndices(s) offset = L(max(min(1 + round(L, (r.offset - first(s))/sstep), last(ind)), first(ind))) - ref = _getindex_hiprec(r, first(s) + (offset-1)*sstep) + ref = _getindex_hiprec(r, first(s) + (offset - oneunit(offset)) * sstep) return StepRangeLen{T}(ref, rstep*sstep, len, offset) end end diff --git a/test/ranges.jl b/test/ranges.jl index b0306ac68b33d..47d18c53bacd3 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -2257,3 +2257,19 @@ let r = Ptr{Cvoid}(20):-UInt(2):Ptr{Cvoid}(10) @test step(r) === -UInt(2) @test last(r) === Ptr{Cvoid}(10) 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 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(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 isempty(range(typemax(Int), length=0, step=UInt(2))) + +@test length(range(1, length=typemax(Int128))) === typemax(Int128) From ffda97dd7ba23e2edf07323a5c037ccd40ca9e92 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 17 Dec 2021 11:17:56 -0500 Subject: [PATCH 2/2] fixup! promote ranges to the largest of the start, step, or length (as applicable) --- base/broadcast.jl | 1 + 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, 67 insertions(+), 54 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 971727768a4be..e4577412bff29 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) diff --git a/base/math.jl b/base/math.jl index 9a5e5377cf613..cf3274060eb96 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 be72b0b6bdbaa..83cbd43eaa81e 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 3bb310be4ee84..0467841fb6261 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 e0c5f87111b07..fa78249043323 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 47d18c53bacd3..03f6fb04fbfd0 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 67c2023a0bc84..8ac22c6244cd3 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))