Skip to content

Commit f177980

Browse files
authored
Improve consistency of arithmetic with Bool (#153)
This only fixes return types which are clearly unreasonable. Other inconsistent return types are leaved for backward compatibility for now.
1 parent 32373c7 commit f177980

File tree

2 files changed

+127
-11
lines changed

2 files changed

+127
-11
lines changed

src/ColorVectorSpace.jl

Lines changed: 34 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,9 @@ 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
188+
_div(x::Bool, y::FixedPoint) = (T = divtype(typeof(x), typeof(y)); T(_div(T(x), T(y)))) # FIXME
171189
_div(x, y) = (T = divtype(typeof(x), typeof(y)); _div(T(x), T(y)))
172190

173191
@inline _mapc(::Type{C}, f, c) where {C<:MathTypes} = C(f.(channels(c))...)
@@ -241,10 +259,10 @@ function (/)(c::C, f::AbstractFloat) where {C<:Union{AbstractRGB, TransparentRGB
241259
r = oneunit(divtype(eltype(c), typeof(f))) / f
242260
_mapc(rettype(/, c, f), v -> v * r, c)
243261
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))
262+
(+)(a::AbstractRGB, b::AbstractRGB) = _mapc(rettype(+, a, b), +, a, b)
263+
(-)(a::AbstractRGB, b::AbstractRGB) = _mapc(rettype(-, a, b), -, a, b)
264+
(+)(a::TransparentRGB, b::TransparentRGB) = _mapc(rettype(+, a, b), +, a, b)
265+
(-)(a::TransparentRGB, b::TransparentRGB) = _mapc(rettype(-, a, b), -, a, b)
248266

249267
# New multiplication operators
250268
function ()(x::AbstractRGB, y::AbstractRGB)
@@ -290,18 +308,26 @@ middle(c::AbstractGray) = arith_colorant_type(c)(middle(gray(c)))
290308
middle(x::C, y::C) where {C<:AbstractGray} = arith_colorant_type(C)(middle(gray(x), gray(y)))
291309

292310
(/)(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))
311+
(+)(a::AbstractGray, b::AbstractGray) = _mapc(rettype(+, a, b), +, a, b)
312+
(+)(a::AbstractGray{Bool}, b::AbstractGray{Bool}) = _mapc(rettype(+, a, b), , a, b)
313+
(+)(a::TransparentGray, b::TransparentGray) = _mapc(rettype(+, a, b), +, a, b)
314+
(-)(a::AbstractGray, b::AbstractGray) = _mapc(rettype(-, a, b), -, a, b)
315+
(-)(a::AbstractGray{Bool}, b::AbstractGray{Bool}) = _mapc(rettype(-, a, b), , a, b)
316+
(-)(a::TransparentGray, b::TransparentGray) = _mapc(rettype(-, a, b), -, a, b)
297317
(*)(a::AbstractGray, b::AbstractGray) = a b
298318
(^)(a::AbstractGray, b::Integer) = rettype(^, a, b)(gray(a)^convert(Int,b))
299319
(^)(a::AbstractGray, b::Real) = rettype(^, a, b)(gray(a)^b)
300320
(/)(a::AbstractGray, b::AbstractGray) = rettype(/, a, b)(_div(gray(a), gray(b)))
301321
(+)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)+b)
322+
(+)(a::AbstractGray{Bool}, b::Number) = arith_colorant_type(a){sumtype(Bool, typeof(b))}(gray(a) + b)
323+
(+)(a::AbstractGray{Bool}, b::Bool) = a + N0f8(b) # FIXME
302324
(+)(a::Number, b::AbstractGray) = b+a
303325
(-)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)-b)
304326
(-)(a::Number, b::AbstractGray) = base_color_type(b)(a-gray(b))
327+
(-)(a::AbstractGray{Bool}, b::Number) = arith_colorant_type(a){sumtype(Bool, typeof(b))}(gray(a) - b)
328+
(-)(a::Number, b::AbstractGray{Bool}) = arith_colorant_type(b){sumtype(typeof(a), Bool)}(a - gray(b))
329+
(-)(a::AbstractGray{Bool}, b::Bool) = a - N0f8(b) # FIXME
330+
(-)(a::Bool, b::AbstractGray{Bool}) = N0f8(a) - b # FIXME
305331

306332
()(x::C, y::C) where {C<:AbstractGray} = _mul(gray(x), gray(y))
307333
()(x::C, y::C) where {C<:AbstractGray} = x y

test/runtests.jl

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -607,13 +607,103 @@ 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_broken @inferred(cb + 2) === @inferred(2 + cb) === Gray{Float32}(3)
640+
@test_broken @inferred(cb - 2) === Gray{Float32}(-1)
641+
@test_broken @inferred(2 - cb) === Gray{Float32}(1)
642+
@test @inferred(cb + 0) === @inferred(0 + cb) === Gray{N0f8}(1) # v0.9 behavior
643+
@test @inferred(cb - 1) === Gray{N0f8}(0) # v0.9 behavior
644+
@test @inferred(2 - cb) === Gray{N0f8}(1) # v0.9 behavior
645+
@test @inferred(cb * 2) === @inferred(2 * cb) === Gray{Float32}(2)
646+
@test @inferred(cb / 2) === Gray{Float32}(0.5)
647+
@test @inferred(2 / cb) === Gray{Float32}(2)
648+
@test @inferred(cb^1) === cb
649+
end
650+
# vs. Float32 and Gray{Float32}
651+
@testset "vs. $(typeof(x))" for x in (0.5f0, Gray(0.5f0))
652+
@test @inferred(cb + x) === @inferred(x + cb) === Gray{Float32}(1.5)
653+
@test @inferred(cb - x) === Gray{Float32}(0.5)
654+
@test @inferred(x - cb) === Gray{Float32}(-0.5)
655+
@test @inferred(cb * x) === @inferred(x * cb) === Gray{Float32}(0.5)
656+
@test @inferred(cb / x) === Gray{Float32}(2)
657+
@test @inferred(x / cb) === Gray{Float32}(0.5)
658+
if x isa Real
659+
@test @inferred(cb^x) === Gray{Float32}(1)
660+
else
661+
@test @inferred(x^true) === Gray{Float32}(0.5)
662+
end
663+
end
664+
# vs. N0f8 and Gray{N0f8}
665+
@testset "vs. $(typeof(x))" for x in (0.6N0f8, Gray(0.6N0f8))
666+
@test @inferred(cb + x) === @inferred(x + cb) === Gray{N0f8}(1N0f8 + 0.6N0f8)
667+
@test @inferred(cb - x) === Gray{N0f8}(1N0f8 - 0.6N0f8)
668+
@test @inferred(x - cb) === Gray{N0f8}(0.6N0f8 - 1N0f8)
669+
@test @inferred(cb * x) === @inferred(x * cb) === Gray{N0f8}(0.6)
670+
@test_broken @inferred(cb / x) === Gray{Float32}(1 / 0.6)
671+
@test_broken @inferred(x / cb) === Gray{Float32}(0.6)
672+
@test @inferred(cb / oneunit(x)) === Gray{N0f8}(1) # v0.9 behavior
673+
@test @inferred(x / cb) === Gray{N0f8}(0.6) # v0.9 behavior
674+
if x isa Gray
675+
@test_broken @inferred(true / x) === Gray{Float32}(1 / 0.6)
676+
@test @inferred(true / Gray(1)) === Gray{N0f8}(1.0) # v0.9 behavior
677+
@test @inferred(x^true) === Gray{N0f8}(0.6)
678+
end
679+
end
680+
681+
@testset "vs. $(typeof(c)) multiplications" for c in (Gray(true), Gray(0.5f0), Gray(0.6N0f8))
682+
@test @inferred(cb c) === @inferred(c cb) === gray(c)
683+
@test @inferred(cb c) === @inferred(c cb) === c
684+
@test @inferred(cb c) === @inferred(c cb) === c
685+
end
686+
cf = RGB{Float32}(0.1, 0.2, 0.3)
687+
@test @inferred(cf + cb) === @inferred(cb + cf) === RGB{Float32}(1.1, 1.2, 1.3)
688+
@test @inferred(cf - cb) === RGB{Float32}(-0.9, -0.8, -0.7)
689+
@test @inferred(cb - cf) === RGB{Float32}(0.9, 0.8, 0.7)
690+
cu = RGB{N0f8}(0.1, 0.2, 0.3)
691+
@test @inferred(cu + cb) === @inferred(cb + cu) === mapc(v -> v + 1N0f8, cu) # wrapped around
692+
@test @inferred(cu - cb) === mapc(v -> v - 1N0f8, cu) # wrapped around
693+
@test @inferred(cb - cu) === mapc(v -> 1N0f8 - v, cu)
694+
@testset "vs. $(typeof(c))" for c in (cf, cu)
695+
@test @inferred(c * true) === @inferred(true * c) === c
696+
if c === cu
697+
@test_broken @inferred(c / true) === c / 1
698+
@test @inferred(c / true) == c # v0.9 behavior
699+
else
700+
@test @inferred(c / true) === c / 1
701+
end
702+
@test @inferred(cb c) === @inferred(c cb) === Gray(1) c
703+
@test @inferred(cb c) === @inferred(c cb) === c
704+
@test @inferred(cb c) === Gray(1) c
705+
@test @inferred(c cb) === c Gray(1)
706+
end
617707
end
618708

619709
@testset "Complement" begin

0 commit comments

Comments
 (0)