Skip to content

Commit 69a4b58

Browse files
committed
Improve consistency of arithmetic with Bool
This only fixes return types which are clearly unreasonable. Other inconsistent return types are leaved for backward compatibility for now.
1 parent 32373c7 commit 69a4b58

File tree

2 files changed

+119
-11
lines changed

2 files changed

+119
-11
lines changed

src/ColorVectorSpace.jl

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,21 @@ divtype(::Type{A}, ::Type{B}) where {A,B<:FixedPoint} = coltype(typeof(zero(A)/t
9494
powtype(::Type{A}, ::Type{B}) where {A,B} = coltype(typeof(zero(A)^zero(B)))
9595
sumtype(a::Colorant, b::Colorant) = coltype(sumtype(eltype(a),eltype(b)))
9696

97+
multype(::Type{Bool}, ::Type{B}) where {B} = B
98+
multype(::Type{A}, ::Type{Bool}) where {A} = A <: Integer ? multype(A, N0f8) : A
99+
multype(::Type{Bool}, ::Type{Bool}) = Bool
100+
sumtype(::Type{Bool}, ::Type{B}) where {B} = B <: Integer ? N0f8 : sumtype(N0f8, B) # FIXME
101+
sumtype(::Type{A}, ::Type{Bool}) where {A} = sumtype(Bool, A)
102+
sumtype(::Type{Bool}, ::Type{Bool}) = N0f8 # FIXME
103+
divtype(::Type{Bool}, ::Type{B}) where {B} = divtype(B <: Integer ? N0f8 : UInt8, B)
104+
divtype(::Type{Bool}, ::Type{B}) where {B<:FixedPoint} = divtype(N0f8, B) # FIXME
105+
divtype(::Type{A}, ::Type{Bool}) where {A} = divtype(A, A <: FixedPoint ? N0f8 : UInt8) # FIXME
106+
divtype(::Type{A}, ::Type{Bool}) where {A<:Integer} = Float32 # FIXME
107+
divtype(::Type{Bool}, ::Type{Bool}) = divtype(N0f8, UInt8)
108+
powtype(::Type{Bool}, ::Type{B}) where {B} = B <: Integer ? Bool : B
109+
powtype(::Type{A}, ::Type{Bool}) where {A} = A
110+
powtype(::Type{Bool}, ::Type{Bool}) = Bool
111+
97112
coltype(::Type{T}) where {T<:Fractional} = T
98113
coltype(::Type{T}) where {T<:Number} = floattype(T)
99114

@@ -168,6 +183,8 @@ function _div(x::AbstractFloat, y::Normed)
168183
end
169184
_div(x::AbstractFloat, y::Integer) = _mul(x, oneunit(x) / y)
170185
_div(x::T, y::T) where {T} = x / y
186+
_div(x::Bool, y::Bool) = (T = divtype(typeof(x), typeof(y)); _div(T(x), T(y)))
187+
_div(x::FixedPoint, y::Bool) = (T = divtype(typeof(x), typeof(y)); T(_div(T(x), T(y)))) # FIXME
171188
_div(x, y) = (T = divtype(typeof(x), typeof(y)); _div(T(x), T(y)))
172189

173190
@inline _mapc(::Type{C}, f, c) where {C<:MathTypes} = C(f.(channels(c))...)
@@ -241,10 +258,10 @@ function (/)(c::C, f::AbstractFloat) where {C<:Union{AbstractRGB, TransparentRGB
241258
r = oneunit(divtype(eltype(c), typeof(f))) / f
242259
_mapc(rettype(/, c, f), v -> v * r, c)
243260
end
244-
(+)(a::AbstractRGB, b::AbstractRGB) = rettype(+, a, b)(red(a)+red(b), green(a)+green(b), blue(a)+blue(b))
245-
(-)(a::AbstractRGB, b::AbstractRGB) = rettype(-, a, b)(red(a)-red(b), green(a)-green(b), blue(a)-blue(b))
246-
(+)(a::TransparentRGB, b::TransparentRGB) = rettype(+, a, b)(red(a)+red(b), green(a)+green(b), blue(a)+blue(b), alpha(a)+alpha(b))
247-
(-)(a::TransparentRGB, b::TransparentRGB) = rettype(-, a, b)(red(a)-red(b), green(a)-green(b), blue(a)-blue(b), alpha(a)-alpha(b))
261+
(+)(a::AbstractRGB, b::AbstractRGB) = _mapc(rettype(+, a, b), +, a, b)
262+
(-)(a::AbstractRGB, b::AbstractRGB) = _mapc(rettype(-, a, b), -, a, b)
263+
(+)(a::TransparentRGB, b::TransparentRGB) = _mapc(rettype(+, a, b), +, a, b)
264+
(-)(a::TransparentRGB, b::TransparentRGB) = _mapc(rettype(-, a, b), -, a, b)
248265

249266
# New multiplication operators
250267
function ()(x::AbstractRGB, y::AbstractRGB)
@@ -290,18 +307,26 @@ middle(c::AbstractGray) = arith_colorant_type(c)(middle(gray(c)))
290307
middle(x::C, y::C) where {C<:AbstractGray} = arith_colorant_type(C)(middle(gray(x), gray(y)))
291308

292309
(/)(n::Number, c::AbstractGray) = base_color_type(c)(_div(real(n), gray(c)))
293-
(+)(a::AbstractGray, b::AbstractGray) = rettype(+, a, b)(gray(a)+gray(b))
294-
(+)(a::TransparentGray, b::TransparentGray) = rettype(+, a, b)(gray(a)+gray(b), alpha(a)+alpha(b))
295-
(-)(a::AbstractGray, b::AbstractGray) = rettype(-, a, b)(gray(a)-gray(b))
296-
(-)(a::TransparentGray, b::TransparentGray) = rettype(-, a, b)(gray(a)-gray(b), alpha(a)-alpha(b))
310+
(+)(a::AbstractGray, b::AbstractGray) = _mapc(rettype(+, a, b), +, a, b)
311+
(+)(a::AbstractGray{Bool}, b::AbstractGray{Bool}) = _mapc(rettype(+, a, b), , a, b)
312+
(+)(a::TransparentGray, b::TransparentGray) = _mapc(rettype(+, a, b), +, a, b)
313+
(-)(a::AbstractGray, b::AbstractGray) = _mapc(rettype(-, a, b), -, a, b)
314+
(-)(a::AbstractGray{Bool}, b::AbstractGray{Bool}) = _mapc(rettype(-, a, b), , a, b)
315+
(-)(a::TransparentGray, b::TransparentGray) = _mapc(rettype(-, a, b), -, a, b)
297316
(*)(a::AbstractGray, b::AbstractGray) = a b
298317
(^)(a::AbstractGray, b::Integer) = rettype(^, a, b)(gray(a)^convert(Int,b))
299318
(^)(a::AbstractGray, b::Real) = rettype(^, a, b)(gray(a)^b)
300319
(/)(a::AbstractGray, b::AbstractGray) = rettype(/, a, b)(_div(gray(a), gray(b)))
301320
(+)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)+b)
321+
(+)(a::AbstractGray{Bool}, b::Number) = arith_colorant_type(a){sumtype(Bool, typeof(b))}(gray(a)) + b
322+
(+)(a::AbstractGray{Bool}, b::Bool) = a + N0f8(b) # FIXME
302323
(+)(a::Number, b::AbstractGray) = b+a
303324
(-)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)-b)
304325
(-)(a::Number, b::AbstractGray) = base_color_type(b)(a-gray(b))
326+
(-)(a::AbstractGray{Bool}, b::Number) = arith_colorant_type(a){sumtype(Bool, typeof(b))}(gray(a)) - b
327+
(-)(a::Number, b::AbstractGray{Bool}) = a - arith_colorant_type(b){sumtype(typeof(a), Bool)}(gray(b))
328+
(-)(a::AbstractGray{Bool}, b::Bool) = a - N0f8(b) # FIXME
329+
(-)(a::Bool, b::AbstractGray{Bool}) = N0f8(a) - b # FIXME
305330

306331
()(x::C, y::C) where {C<:AbstractGray} = _mul(gray(x), gray(y))
307332
()(x::C, y::C) where {C<:AbstractGray} = x y

test/runtests.jl

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -607,13 +607,96 @@ ColorTypes.comp2(c::RGBA32) = alpha(c)
607607
@test cf cf === (Float64(red(cf))^2 + Float64(green(cf))^2 + Float64(blue(cf))^2)/3
608608
end
609609

610-
@testset "arithmetic with Bool" begin
610+
@testset "arithmetic with Bool" begin # issue 148
611611
cb = Gray{Bool}(1)
612612
@test @inferred(+cb) === cb
613613
@test @inferred(-cb) === cb # wrapped around
614-
@test_broken @inferred(one(cb) * cb) === cb
614+
@test @inferred(one(cb)*cb) === cb
615615
@test oneunit(cb) === Gray(true)
616-
# TODO: add more tests (cf. issue #148)
616+
617+
@testset "vs. Bool" begin
618+
@test_broken @inferred(cb + true) === @inferred(true + cb) === Gray{Float32}(2)
619+
@test_broken @inferred(cb - true) === Gray{Float32}(0)
620+
@test_broken @inferred(true - cb) === Gray{Float32}(0)
621+
@test @inferred(cb + false) === @inferred(false + cb) === Gray{N0f8}(1) # v0.9 behavior
622+
@test @inferred(cb - true) === Gray{N0f8}(0) # v0.9 behavior
623+
@test @inferred(true - cb) === Gray{N0f8}(0) # v0.9 behavior
624+
@test @inferred(cb * true) === @inferred(true * cb) === Gray{Bool}(1)
625+
@test @inferred(cb / true) === Gray{Float32}(1)
626+
@test @inferred(cb / false) === Gray{Float32}(Inf32)
627+
@test @inferred(true / cb) === Gray{Float32}(1)
628+
@test @inferred(cb^true) === cb
629+
end
630+
@testset "vs. Gray{Bool}" begin
631+
@test @inferred(cb + Gray(true)) === @inferred(Gray(true) + cb) === Gray{Bool}(0) # wrapped around
632+
@test @inferred(cb - Gray(true)) === Gray{Bool}(0)
633+
@test @inferred(Gray(false) - cb) === Gray{Bool}(1) # wrapped around
634+
@test @inferred(cb * Gray(true)) === @inferred(Gray(true) * cb) === Gray{Bool}(1)
635+
@test @inferred(cb / Gray(true)) === Gray{Float32}(1)
636+
@test @inferred(cb / Gray(false)) === Gray{Float32}(Inf32)
637+
end
638+
@testset "vs. Int" begin
639+
@test @inferred(cb + 2) === @inferred(2 + cb) === Gray{Float32}(3)
640+
@test @inferred(cb - 2) === Gray{Float32}(-1)
641+
@test @inferred(2 - cb) === Gray{Float32}(1)
642+
@test @inferred(cb * 2) === @inferred(2 * cb) === Gray{Float32}(2)
643+
@test @inferred(cb / 2) === Gray{Float32}(0.5)
644+
@test @inferred(2 / cb) === Gray{Float32}(2)
645+
@test @inferred(cb^1) === cb
646+
end
647+
# vs. Float32 and Gray{Float32}
648+
@testset "vs. $(typeof(x))" for x in (0.5f0, Gray(0.5f0))
649+
@test @inferred(cb + x) === @inferred(x + cb) === Gray{Float32}(1.5)
650+
@test @inferred(cb - x) === Gray{Float32}(0.5)
651+
@test @inferred(x - cb) === Gray{Float32}(-0.5)
652+
@test @inferred(cb * x) === @inferred(x * cb) === Gray{Float32}(0.5)
653+
@test @inferred(cb / x) === Gray{Float32}(2)
654+
@test @inferred(x / cb) === Gray{Float32}(0.5)
655+
if x isa Real
656+
@test @inferred(cb^x) === Gray{Float32}(1)
657+
else
658+
@test @inferred(x^true) === Gray{Float32}(0.5)
659+
end
660+
end
661+
# vs. N0f8 and Gray{N0f8}
662+
@testset "vs. $(typeof(x))" for x in (0.6N0f8, Gray(0.6N0f8))
663+
@test @inferred(cb + x) === @inferred(x + cb) === Gray{N0f8}(1N0f8 + 0.6N0f8)
664+
@test @inferred(cb - x) === Gray{N0f8}(1N0f8 - 0.6N0f8)
665+
@test @inferred(x - cb) === Gray{N0f8}(0.6N0f8 - 1N0f8)
666+
@test @inferred(cb * x) === @inferred(x * cb) === Gray{N0f8}(0.6)
667+
@test_broken @inferred(cb / x) === Gray{Float32}(1 / 0.6)
668+
@test_broken @inferred(x / cb) === Gray{Float32}(0.6)
669+
@test @inferred(cb / oneunit(x)) === Gray{N0f8}(1) # v0.9 behavior
670+
@test @inferred(x / cb) === Gray{N0f8}(0.6) # v0.9 behavior
671+
end
672+
@test @inferred(Gray(0.6N0f8)^true) === Gray{N0f8}(0.6)
673+
674+
@testset "vs. $(typeof(c)) multiplications" for c in (Gray(true), Gray(0.5f0), Gray(0.6N0f8))
675+
@test @inferred(cb c) === @inferred(c cb) === gray(c)
676+
@test @inferred(cb c) === @inferred(c cb) === c
677+
@test @inferred(cb c) === @inferred(c cb) === c
678+
end
679+
cf = RGB{Float32}(0.1, 0.2, 0.3)
680+
@test @inferred(cf + cb) === @inferred(cb + cf) === RGB{Float32}(1.1, 1.2, 1.3)
681+
@test @inferred(cf - cb) === RGB{Float32}(-0.9, -0.8, -0.7)
682+
@test @inferred(cb - cf) === RGB{Float32}(0.9, 0.8, 0.7)
683+
cu = RGB{N0f8}(0.1, 0.2, 0.3)
684+
@test @inferred(cu + cb) === @inferred(cb + cu) === mapc(v -> v + 1N0f8, cu) # wrapped around
685+
@test @inferred(cu - cb) === mapc(v -> v - 1N0f8, cu) # wrapped around
686+
@test @inferred(cb - cu) === mapc(v -> 1N0f8 - v, cu)
687+
@testset "vs. $(typeof(c))" for c in (cf, cu)
688+
@test @inferred(c * true) === @inferred(true * c) === c
689+
if c === cu
690+
@test_broken @inferred(c / true) === c / 1
691+
@test @inferred(c / true) == c # v0.9 behavior
692+
else
693+
@test @inferred(c / true) === c / 1
694+
end
695+
@test @inferred(cb c) === @inferred(c cb) === Gray(1) c
696+
@test @inferred(cb c) === @inferred(c cb) === c
697+
@test @inferred(cb c) === Gray(1) c
698+
@test @inferred(c cb) === c Gray(1)
699+
end
617700
end
618701

619702
@testset "Complement" begin

0 commit comments

Comments
 (0)