From 82f9a210aad41d81636b82618ac7f8923ade6c79 Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Wed, 28 Oct 2015 10:07:03 -0500 Subject: [PATCH] Use promote_op in broadcasting and matrix multiplication This makes it easier to support these operations on AbstractArrays with generic eltypes --- base/abstractarray.jl | 9 +++++++++ base/bool.jl | 2 ++ base/broadcast.jl | 34 +++++++++++---------------------- base/complex.jl | 7 +++++++ base/functors.jl | 4 ++++ base/int.jl | 2 ++ base/linalg.jl | 1 + base/linalg/matmul.jl | 38 ++++++++++++++++++------------------- base/promotion.jl | 3 +++ base/sparse/sparsematrix.jl | 2 +- test/arrayops.jl | 8 ++++++-- 11 files changed, 65 insertions(+), 45 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 690cc80629273..3020240b38a85 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1269,6 +1269,15 @@ function map_promote(f, A::AbstractArray) return promote_to!(f, 2, dest, A) end +# These are needed because map(eltype, As) is not inferrable +promote_eltype_op(::Any) = (@_pure_meta; Bottom) +promote_eltype_op{T}(op, ::AbstractArray{T}) = (@_pure_meta; promote_op(op, T)) +promote_eltype_op{T}(op, ::T ) = (@_pure_meta; promote_op(op, T)) +promote_eltype_op{R,S}(op, ::AbstractArray{R}, ::AbstractArray{S}) = (@_pure_meta; promote_op(op, R, S)) +promote_eltype_op{R,S}(op, ::AbstractArray{R}, ::S) = (@_pure_meta; promote_op(op, R, S)) +promote_eltype_op{R,S}(op, ::R, ::AbstractArray{S}) = (@_pure_meta; promote_op(op, R, S)) +promote_eltype_op(op, A, B, C, D...) = (@_pure_meta; promote_op(op, eltype(A), promote_eltype_op(op, B, C, D...))) + ## 1 argument map!{F}(f::F, A::AbstractArray) = map!(f, A, A) function map!{F}(f::F, dest::AbstractArray, A::AbstractArray) diff --git a/base/bool.jl b/base/bool.jl index 95df1e3494cb3..915d67a97bb3f 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -59,3 +59,5 @@ fld(x::Bool, y::Bool) = div(x,y) cld(x::Bool, y::Bool) = div(x,y) rem(x::Bool, y::Bool) = y ? false : throw(DivideError()) mod(x::Bool, y::Bool) = rem(x,y) + +promote_op(op, ::Type{Bool}, ::Type{Bool}) = typeof(op(true, true)) diff --git a/base/broadcast.jl b/base/broadcast.jl index de593d46e0c47..32666ed6a32c8 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -3,9 +3,8 @@ module Broadcast using ..Cartesian -import Base.promote_eltype -import Base.@get! -import Base.num_bit_chunks, Base._msk_end, Base.unsafe_bitgetindex +using Base: promote_op, promote_eltype, promote_eltype_op, @get!, _msk_end, unsafe_bitgetindex +using Base: AddFun, SubFun, MulFun, LDivFun, RDivFun, PowFun import Base: .+, .-, .*, ./, .\, .//, .==, .<, .!=, .<=, .%, .<<, .>>, .^ export broadcast, broadcast!, broadcast_function, broadcast!_function, bitbroadcast export broadcast_getindex, broadcast_setindex! @@ -232,7 +231,7 @@ for (Bsig, Asig, gbf, gbb) in end -broadcast(f, As...) = broadcast!(f, Array(promote_eltype(As...), broadcast_shape(As...)), As...) +broadcast(f, As...) = broadcast!(f, Array(promote_eltype_op(f, As...), broadcast_shape(As...)), As...) bitbroadcast(f, As...) = broadcast!(f, BitArray(broadcast_shape(As...)), As...) @@ -286,35 +285,28 @@ end ## elementwise operators ## -.*(As::AbstractArray...) = broadcast(*, As...) .%(A::AbstractArray, B::AbstractArray) = broadcast(%, A, B) .<<(A::AbstractArray, B::AbstractArray) = broadcast(<<, A, B) .>>(A::AbstractArray, B::AbstractArray) = broadcast(>>, A, B) -eltype_plus(As::AbstractArray...) = promote_eltype(As...) -eltype_plus(As::AbstractArray{Bool}...) = typeof(true+true) +eltype_plus(As::AbstractArray...) = promote_eltype_op(AddFun(), As...) .+(As::AbstractArray...) = broadcast!(+, Array(eltype_plus(As...), broadcast_shape(As...)), As...) -type_minus(T, S) = promote_type(T, S) -type_minus(::Type{Bool}, ::Type{Bool}) = typeof(true-true) - function .-(A::AbstractArray, B::AbstractArray) - broadcast!(-, Array(type_minus(eltype(A), eltype(B)), broadcast_shape(A,B)), A, B) + broadcast!(-, Array(promote_op(SubFun(), eltype(A), eltype(B)), broadcast_shape(A,B)), A, B) end -type_div(T,S) = promote_type(T,S) -type_div{T<:Integer,S<:Integer}(::Type{T},::Type{S}) = typeof(one(T)/one(S)) -type_div{T,S}(::Type{Complex{T}},::Type{Complex{S}}) = Complex{type_div(T,S)} -type_div{T,S}(::Type{Complex{T}},::Type{S}) = Complex{type_div(T,S)} -type_div{T,S}(::Type{T},::Type{Complex{S}}) = Complex{type_div(T,S)} +eltype_mul(As::AbstractArray...) = promote_eltype_op(MulFun(), As...) + +.*(As::AbstractArray...) = broadcast!(*, Array(eltype_mul(As...), broadcast_shape(As...)), As...) function ./(A::AbstractArray, B::AbstractArray) - broadcast!(/, Array(type_div(eltype(A), eltype(B)), broadcast_shape(A, B)), A, B) + broadcast!(/, Array(promote_op(RDivFun(), eltype(A), eltype(B)), broadcast_shape(A, B)), A, B) end function .\(A::AbstractArray, B::AbstractArray) - broadcast!(\, Array(type_div(eltype(A), eltype(B)), broadcast_shape(A, B)), A, B) + broadcast!(\, Array(promote_op(LDivFun(), eltype(A), eltype(B)), broadcast_shape(A, B)), A, B) end typealias RatIntT{T<:Integer} Union{Type{Rational{T}},Type{T}} @@ -327,12 +319,8 @@ function .//(A::AbstractArray, B::AbstractArray) broadcast!(//, Array(type_rdiv(eltype(A), eltype(B)), broadcast_shape(A, B)), A, B) end -type_pow(T,S) = promote_type(T,S) -type_pow{S<:Integer}(::Type{Bool},::Type{S}) = Bool -type_pow{S}(T,::Type{Rational{S}}) = type_pow(T, type_div(S, S)) - function .^(A::AbstractArray, B::AbstractArray) - broadcast!(^, Array(type_pow(eltype(A), eltype(B)), broadcast_shape(A, B)), A, B) + broadcast!(^, Array(promote_op(PowFun(), eltype(A), eltype(B)), broadcast_shape(A, B)), A, B) end ## element-wise comparison operators returning BitArray ## diff --git a/base/complex.jl b/base/complex.jl index b005d728ef87c..8d98ae0c7aabf 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -26,6 +26,13 @@ promote_rule{T<:Real,S<:Real}(::Type{Complex{T}}, ::Type{S}) = promote_rule{T<:Real,S<:Real}(::Type{Complex{T}}, ::Type{Complex{S}}) = Complex{promote_type(T,S)} +promote_op{T<:Real,S<:Real}(op, ::Type{Complex{T}}, ::Type{Complex{S}}) = + Complex{promote_op(op,T,S)} +promote_op{T<:Real,S<:Real}(op, ::Type{Complex{T}}, ::Type{S}) = + Complex{promote_op(op,T,S)} +promote_op{T<:Real,S<:Real}(op, ::Type{T}, ::Type{Complex{S}}) = + Complex{promote_op(op,T,S)} + widen{T}(::Type{Complex{T}}) = Complex{widen(T)} real(z::Complex) = z.re diff --git a/base/functors.jl b/base/functors.jl index 613698192b7ba..ba8aa4e354344 100644 --- a/base/functors.jl +++ b/base/functors.jl @@ -120,6 +120,10 @@ EqX{T}(x::T) = EqX{T}(x) call(f::EqX, y) = f.x == y +# More promote_op rules + +promote_op{T<:Integer}(::PowFun, ::Type{Bool}, ::Type{T}) = Bool + #### Bitwise operators #### # BitFunctors are functions that behave in the same bit-wise manner when applied diff --git a/base/int.jl b/base/int.jl index 2ace89965c3dd..0ca47800f043c 100644 --- a/base/int.jl +++ b/base/int.jl @@ -340,6 +340,8 @@ promote_rule(::Type{UInt128}, ::Type{Int32} ) = UInt128 promote_rule(::Type{UInt128}, ::Type{Int64} ) = UInt128 promote_rule(::Type{UInt128}, ::Type{Int128}) = UInt128 +promote_op{R<:Integer,S<:Integer}(op, ::Type{R}, ::Type{S}) = typeof(op(one(R), one(S))) + ## traits ## typemin(::Type{Int8 }) = Int8(-128) diff --git a/base/linalg.jl b/base/linalg.jl index 0a081cb0bff0b..c0c57ae2ca5e7 100644 --- a/base/linalg.jl +++ b/base/linalg.jl @@ -10,6 +10,7 @@ import Base: USE_BLAS64, abs, big, ceil, conj, convert, copy, copy!, copy_transp imag, inv, isapprox, kron, ndims, power_by_squaring, print_matrix, promote_rule, real, round, setindex!, show, similar, size, transpose, transpose!, trunc, unsafe_getindex, unsafe_setindex! +using Base: promote_op, MulFun export # Modules diff --git a/base/linalg/matmul.jl b/base/linalg/matmul.jl index cb7d87cd27cb3..5362b00d7cdd1 100644 --- a/base/linalg/matmul.jl +++ b/base/linalg/matmul.jl @@ -38,8 +38,8 @@ function scale!(C::AbstractMatrix, b::AbstractVector, A::AbstractMatrix) end C end -scale(A::Matrix, b::Vector) = scale!(similar(A, promote_type(eltype(A),eltype(b))), A, b) -scale(b::Vector, A::Matrix) = scale!(similar(b, promote_type(eltype(A),eltype(b)), size(A)), b, A) +scale(A::Matrix, b::Vector) = scale!(similar(A, promote_op(MulFun(),eltype(A),eltype(b))), A, b) +scale(b::Vector, A::Matrix) = scale!(similar(b, promote_op(MulFun(),eltype(b),eltype(A)), size(A)), b, A) # Dot products @@ -78,11 +78,11 @@ At_mul_B{T<:BlasComplex}(x::StridedVector{T}, y::StridedVector{T}) = [BLAS.dotu( # Matrix-vector multiplication function (*){T<:BlasFloat,S}(A::StridedMatrix{T}, x::StridedVector{S}) - TS = promote_type(arithtype(T),arithtype(S)) + TS = promote_op(MulFun(),arithtype(T),arithtype(S)) A_mul_B!(similar(x, TS, size(A,1)), A, convert(AbstractVector{TS}, x)) end function (*){T,S}(A::AbstractMatrix{T}, x::AbstractVector{S}) - TS = promote_type(arithtype(T),arithtype(S)) + TS = promote_op(MulFun(),arithtype(T),arithtype(S)) A_mul_B!(similar(x,TS,size(A,1)),A,x) end (*)(A::AbstractVector, B::AbstractMatrix) = reshape(A,length(A),1)*B @@ -101,22 +101,22 @@ end A_mul_B!(y::StridedVector, A::StridedVecOrMat, x::StridedVector) = generic_matvecmul!(y, 'N', A, x) function At_mul_B{T<:BlasFloat,S}(A::StridedMatrix{T}, x::StridedVector{S}) - TS = promote_type(arithtype(T),arithtype(S)) + TS = promote_op(MulFun(),arithtype(T),arithtype(S)) At_mul_B!(similar(x,TS,size(A,2)), A, convert(AbstractVector{TS}, x)) end function At_mul_B{T,S}(A::StridedMatrix{T}, x::StridedVector{S}) - TS = promote_type(arithtype(T),arithtype(S)) + TS = promote_op(MulFun(),arithtype(T),arithtype(S)) At_mul_B!(similar(x,TS,size(A,2)), A, x) end At_mul_B!{T<:BlasFloat}(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}) = gemv!(y, 'T', A, x) At_mul_B!(y::StridedVector, A::StridedVecOrMat, x::StridedVector) = generic_matvecmul!(y, 'T', A, x) function Ac_mul_B{T<:BlasFloat,S}(A::StridedMatrix{T}, x::StridedVector{S}) - TS = promote_type(arithtype(T),arithtype(S)) + TS = promote_op(MulFun(),arithtype(T),arithtype(S)) Ac_mul_B!(similar(x,TS,size(A,2)),A,convert(AbstractVector{TS},x)) end function Ac_mul_B{T,S}(A::StridedMatrix{T}, x::StridedVector{S}) - TS = promote_type(arithtype(T),arithtype(S)) + TS = promote_op(MulFun(),arithtype(T),arithtype(S)) Ac_mul_B!(similar(x,TS,size(A,2)), A, x) end @@ -127,7 +127,7 @@ Ac_mul_B!(y::StridedVector, A::StridedVecOrMat, x::StridedVector) = generic_matv # Matrix-matrix multiplication function (*){T,S}(A::AbstractMatrix{T}, B::StridedMatrix{S}) - TS = promote_type(arithtype(T), arithtype(S)) + TS = promote_op(MulFun(), arithtype(T), arithtype(S)) A_mul_B!(similar(B, TS, (size(A,1), size(B,2))), A, B) end A_mul_B!{T<:BlasFloat}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = gemm_wrapper!(C, 'N', 'N', A, B) @@ -144,14 +144,14 @@ end A_mul_B!(C::StridedMatrix, A::StridedVecOrMat, B::StridedVecOrMat) = generic_matmatmul!(C, 'N', 'N', A, B) function At_mul_B{T,S}(A::AbstractMatrix{T}, B::StridedMatrix{S}) - TS = promote_type(arithtype(T), arithtype(S)) + TS = promote_op(MulFun(),arithtype(T), arithtype(S)) At_mul_B!(similar(B, TS, (size(A,2), size(B,2))), A, B) end At_mul_B!{T<:BlasFloat}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = is(A,B) ? syrk_wrapper!(C, 'T', A) : gemm_wrapper!(C, 'T', 'N', A, B) At_mul_B!(C::StridedMatrix, A::StridedVecOrMat, B::StridedVecOrMat) = generic_matmatmul!(C, 'T', 'N', A, B) function A_mul_Bt{T,S}(A::AbstractMatrix{T}, B::StridedMatrix{S}) - TS = promote_type(arithtype(T), arithtype(S)) + TS = promote_op(MulFun(),arithtype(T), arithtype(S)) A_mul_Bt!(similar(B, TS, (size(A,1), size(B,1))), A, B) end A_mul_Bt!{T<:BlasFloat}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = is(A,B) ? syrk_wrapper!(C, 'N', A) : gemm_wrapper!(C, 'N', 'T', A, B) @@ -168,7 +168,7 @@ end A_mul_Bt!(C::StridedVecOrMat, A::StridedVecOrMat, B::StridedVecOrMat) = generic_matmatmul!(C, 'N', 'T', A, B) function At_mul_Bt{T,S}(A::AbstractMatrix{T}, B::StridedVecOrMat{S}) - TS = promote_type(arithtype(T), arithtype(S)) + TS = promote_op(MulFun(),arithtype(T), arithtype(S)) At_mul_Bt!(similar(B, TS, (size(A,2), size(B,1))), A, B) end At_mul_Bt!{T<:BlasFloat}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = gemm_wrapper!(C, 'T', 'T', A, B) @@ -177,7 +177,7 @@ At_mul_Bt!(C::StridedMatrix, A::StridedVecOrMat, B::StridedVecOrMat) = generic_m Ac_mul_B{T<:BlasReal}(A::StridedMatrix{T}, B::StridedMatrix{T}) = At_mul_B(A, B) Ac_mul_B!{T<:BlasReal}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = At_mul_B!(C, A, B) function Ac_mul_B{T,S}(A::StridedMatrix{T}, B::StridedMatrix{S}) - TS = promote_type(arithtype(T), arithtype(S)) + TS = promote_op(MulFun(),arithtype(T), arithtype(S)) Ac_mul_B!(similar(B, TS, (size(A,2), size(B,2))), A, B) end Ac_mul_B!{T<:BlasComplex}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = is(A,B) ? herk_wrapper!(C,'C',A) : gemm_wrapper!(C,'C', 'N', A, B) @@ -186,16 +186,16 @@ Ac_mul_B!(C::StridedMatrix, A::StridedVecOrMat, B::StridedVecOrMat) = generic_ma A_mul_Bc{T<:BlasFloat,S<:BlasReal}(A::StridedMatrix{T}, B::StridedMatrix{S}) = A_mul_Bt(A, B) A_mul_Bc!{T<:BlasFloat,S<:BlasReal}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{S}) = A_mul_Bt!(C, A, B) function A_mul_Bc{T,S}(A::StridedMatrix{T}, B::StridedMatrix{S}) - TS = promote_type(arithtype(T),arithtype(S)) + TS = promote_op(MulFun(),arithtype(T),arithtype(S)) A_mul_Bc!(similar(B,TS,(size(A,1),size(B,1))),A,B) end A_mul_Bc!{T<:BlasComplex}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = is(A,B) ? herk_wrapper!(C, 'N', A) : gemm_wrapper!(C, 'N', 'C', A, B) A_mul_Bc!(C::StridedMatrix, A::StridedVecOrMat, B::StridedVecOrMat) = generic_matmatmul!(C, 'N', 'C', A, B) -Ac_mul_Bc{T,S}(A::AbstractMatrix{T}, B::StridedMatrix{S}) = Ac_mul_Bc!(similar(B, promote_type(arithtype(T), arithtype(S)), (size(A,2), size(B,1))), A, B) +Ac_mul_Bc{T,S}(A::AbstractMatrix{T}, B::StridedMatrix{S}) = Ac_mul_Bc!(similar(B, promote_op(MulFun(),arithtype(T), arithtype(S)), (size(A,2), size(B,1))), A, B) Ac_mul_Bc!{T<:BlasFloat}(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}) = gemm_wrapper!(C, 'C', 'C', A, B) Ac_mul_Bc!(C::StridedMatrix, A::StridedVecOrMat, B::StridedVecOrMat) = generic_matmatmul!(C, 'C', 'C', A, B) -Ac_mul_Bt{T,S}(A::AbstractMatrix{T}, B::StridedMatrix{S}) = Ac_mul_Bt(similar(B, promote_type(arithtype(A), arithtype(B)), (size(A,2), size(B,1))), A, B) +Ac_mul_Bt{T,S}(A::AbstractMatrix{T}, B::StridedMatrix{S}) = Ac_mul_Bt(similar(B, promote_op(MulFun(),arithtype(A), arithtype(B)), (size(A,2), size(B,1))), A, B) Ac_mul_Bt!(C::StridedMatrix, A::StridedVecOrMat, B::StridedVecOrMat) = generic_matmatmul!(C, 'C', 'T', A, B) # Supporting functions for matrix multiplication @@ -425,7 +425,7 @@ end function generic_matmatmul{T,S}(tA, tB, A::AbstractVecOrMat{T}, B::AbstractMatrix{S}) mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) - C = similar(B, promote_type(arithtype(T),arithtype(S)), mA, nB) + C = similar(B, promote_op(MulFun(),arithtype(T),arithtype(S)), mA, nB) generic_matmatmul!(C, tA, tB, A, B) end @@ -630,7 +630,7 @@ end # multiply 2x2 matrices function matmul2x2{T,S}(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) - matmul2x2!(similar(B, promote_type(T,S), 2, 2), tA, tB, A, B) + matmul2x2!(similar(B, promote_op(MulFun(),T,S), 2, 2), tA, tB, A, B) end function matmul2x2!{T,S,R}(C::AbstractMatrix{R}, tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) @@ -659,7 +659,7 @@ end # Multiply 3x3 matrices function matmul3x3{T,S}(tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) - matmul3x3!(similar(B, promote_type(T,S), 3, 3), tA, tB, A, B) + matmul3x3!(similar(B, promote_op(MulFun(),T,S), 3, 3), tA, tB, A, B) end function matmul3x3!{T,S,R}(C::AbstractMatrix{R}, tA, tB, A::AbstractMatrix{T}, B::AbstractMatrix{S}) diff --git a/base/promotion.jl b/base/promotion.jl index 92fe23d5a70ff..3162b446f9806 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -208,7 +208,10 @@ checked_mul(x::Integer, y::Integer) = checked_mul(promote(x,y)...) # as needed. For example, if you need to provide a custom result type # for the multiplication of two types, # promote_op{R<:MyType,S<:MyType}(::MulFun, ::Type{R}, ::Type{S}) = MyType{multype(R,S)} +promote_op(::Any) = (@_pure_meta; Bottom) +promote_op(::Any, T) = (@_pure_meta; T) promote_op{R,S}(::Any, ::Type{R}, ::Type{S}) = (@_pure_meta; promote_type(R, S)) +promote_op(op, T, S, U, V...) = (@_pure_meta; promote_op(op, T, promote_op(op, S, U, V...))) ## catch-alls to prevent infinite recursion when definitions are missing ## diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 027012dea94e2..857c935e8642a 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -972,7 +972,7 @@ end # macro (.-)(A::Number, B::SparseMatrixCSC) = A .- full(B) ( -)(A::Array , B::SparseMatrixCSC) = A - full(B) -(.*)(A::AbstractArray, B::AbstractArray) = broadcast_zpreserving(*, A, B) +(.*)(A::AbstractArray, B::AbstractArray) = broadcast_zpreserving(MulFun(), A, B) (.*)(A::SparseMatrixCSC, B::Number) = SparseMatrixCSC(A.m, A.n, copy(A.colptr), copy(A.rowval), A.nzval .* B) (.*)(A::Number, B::SparseMatrixCSC) = SparseMatrixCSC(B.m, B.n, copy(B.colptr), copy(B.rowval), A .* B.nzval) diff --git a/test/arrayops.jl b/test/arrayops.jl index 019095a05b7aa..cf8053401f6b3 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1233,7 +1233,7 @@ b = rand(6,7) # return type declarations (promote_op) module RetTypeDecl using Base.Test - import Base: +, *, .* + import Base: +, *, .*, zero immutable MeterUnits{T,P} <: Number val::T @@ -1243,9 +1243,11 @@ module RetTypeDecl m = MeterUnits(1.0, 1) # 1.0 meter, i.e. units of length m2 = MeterUnits(1.0, 2) # 1.0 meter^2, i.e. units of area - (+){T}(x::MeterUnits{T,1}, y::MeterUnits{T,1}) = MeterUnits{T,1}(x.val+y.val) + (+){T,pow}(x::MeterUnits{T,pow}, y::MeterUnits{T,pow}) = MeterUnits{T,pow}(x.val+y.val) + (*){T,pow}(x::Int, y::MeterUnits{T,pow}) = MeterUnits{typeof(x*one(T)),pow}(x*y.val) (*){T}(x::MeterUnits{T,1}, y::MeterUnits{T,1}) = MeterUnits{T,2}(x.val*y.val) (.*){T}(x::MeterUnits{T,1}, y::MeterUnits{T,1}) = MeterUnits{T,2}(x.val*y.val) + zero{T,pow}(x::MeterUnits{T,pow}) = MeterUnits{T,pow}(zero(T)) Base.promote_op{R,S}(::Base.AddFun, ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),1} Base.promote_op{R,S}(::Base.MulFun, ::Type{MeterUnits{R,1}}, ::Type{MeterUnits{S,1}}) = MeterUnits{promote_type(R,S),2} @@ -1255,6 +1257,8 @@ module RetTypeDecl @test @inferred([m,m]+m) == [m+m,m+m] @test @inferred(m.*[m,m]) == [m2,m2] @test @inferred([m,m].*m) == [m2,m2] + @test @inferred([m 2m; m m]*[m,m]) == [3m2,2m2] + @test @inferred([m m].*[m,m]) == [m2 m2; m2 m2] end # range, range ops