Skip to content

Commit 299c42d

Browse files
committed
Improve consistency of arithmetic with Bool
1 parent 85b8759 commit 299c42d

File tree

2 files changed

+112
-8
lines changed

2 files changed

+112
-8
lines changed

src/ColorVectorSpace.jl

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ divtype(::Type{A}, ::Type{B}) where {A,B} = coltype(typeof(zero(A)/oneunit(B)))
6161
powtype(::Type{A}, ::Type{B}) where {A,B} = coltype(typeof(zero(A)^zero(B)))
6262
sumtype(a::Colorant, b::Colorant) = coltype(sumtype(eltype(a),eltype(b)))
6363

64+
multype(::Type{Bool}, ::Type{B}) where {B} = B
65+
multype(::Type{A}, ::Type{Bool}) where {A} = A <: Integer ? multype(A, N0f8) : A
66+
multype(::Type{Bool}, ::Type{Bool}) = Bool
67+
sumtype(::Type{Bool}, ::Type{B}) where {B} = sumtype(B <: Integer ? N0f8 : UInt8, B)
68+
sumtype(::Type{A}, ::Type{Bool}) where {A} = sumtype(Bool, A)
69+
sumtype(::Type{Bool}, ::Type{Bool}) = Bool
70+
divtype(::Type{Bool}, ::Type{B}) where {B} = divtype(B <: Integer ? N0f8 : UInt8, B)
71+
divtype(::Type{A}, ::Type{Bool}) where {A} = divtype(A, N0f8)
72+
divtype(::Type{Bool}, ::Type{Bool}) = divtype(Bool, N0f8)
73+
powtype(::Type{Bool}, ::Type{B}) where {B} = B <: Integer ? Bool : B
74+
powtype(::Type{A}, ::Type{Bool}) where {A} = A
75+
powtype(::Type{Bool}, ::Type{Bool}) = Bool
76+
6477
coltype(::Type{T}) where {T<:Fractional} = T
6578
coltype(::Type{T}) where {T<:Number} = floattype(T)
6679

@@ -140,12 +153,15 @@ dotc(x::Real, y::Real) = dotc(promote(x, y)...)
140153
## Math on Colors. These implementations encourage inlining and,
141154
## for the case of Normed types, nearly halve the number of multiplications (for RGB)
142155

156+
_neg(x) = -x
157+
_neg(x::Bool) = x # GF(2)
158+
143159
# Scalar RGB
144160
copy(c::AbstractRGB) = c
145161
(+)(c::AbstractRGB) = mapc(+, c)
146162
(+)(c::TransparentRGB) = mapc(+, c)
147-
(-)(c::AbstractRGB) = mapc(-, c)
148-
(-)(c::TransparentRGB) = mapc(-, c)
163+
(-)(c::AbstractRGB) = mapc(_neg, c)
164+
(-)(c::TransparentRGB) = mapc(_neg, c)
149165
(*)(f::Real, c::AbstractRGB) = arith_colorant_type(c){multype(typeof(f),eltype(c))}(f*red(c), f*green(c), f*blue(c))
150166
(*)(f::Real, c::TransparentRGB) = arith_colorant_type(c){multype(typeof(f),eltype(c))}(f*red(c), f*green(c), f*blue(c), f*alpha(c))
151167
function (*)(f::Real, c::AbstractRGB{T}) where T<:Normed
@@ -250,28 +266,42 @@ end
250266
(*)(f::Real, c::TransparentGray) = arith_colorant_type(c){multype(typeof(f),eltype(c))}(f*gray(c), f*alpha(c))
251267
(*)(c::AbstractGray, f::Real) = (*)(f, c)
252268
(*)(c::TransparentGray, f::Real) = (*)(f, c)
253-
(/)(c::AbstractGray, f::Real) = (one(f)/f)*c
254-
(/)(n::Number, c::AbstractGray) = base_color_type(c)(n/gray(c))
255-
(/)(c::TransparentGray, f::Real) = (one(f)/f)*c
256-
(/)(c::AbstractGray, f::Integer) = (one(eltype(c))/f)*c
269+
(/)(c::AbstractGray, f::Real) = (oneunit(divtype(eltype(c), typeof(f)))/f)*c
270+
(/)(c::AbstractGray{Bool}, f::Real) = arith_colorant_type(c){divtype(Bool,typeof(f))}(gray(c))/f
271+
(/)(n::Number, c::AbstractGray) = arith_colorant_type(c){divtype(typeof(real(n)),eltype(c))}(n/gray(c))
272+
(/)(c::TransparentGray, f::Real) = (oneunit(divtype(eltype(c), typeof(f)))/f)*c
257273
(/)(c::TransparentGray, f::Integer) = (one(eltype(c))/f)*c
258274
(+)(a::AbstractGray{S}, b::AbstractGray{T}) where {S,T} = parametric(color_rettype(a,b), sumtype(S,T))(gray(a)+gray(b))
275+
(+)(a::AbstractGray{Bool}, b::AbstractGray{T}) where {T} = parametric(color_rettype(a,b), sumtype(Bool,T))(gray(a)) + b
276+
(+)(a::AbstractGray{S}, b::AbstractGray{Bool}) where {S} = b + a
277+
(+)(a::AbstractGray{Bool}, b::AbstractGray{Bool}) = parametric(color_rettype(a,b), Bool)(gray(a) gray(b))
259278
(+)(a::TransparentGray, b::TransparentGray) = parametric(color_rettype(a,b), sumtype(eltype(a),eltype(b)))(gray(a)+gray(b),alpha(a)+alpha(b))
260279
(-)(a::AbstractGray{S}, b::AbstractGray{T}) where {S,T} = parametric(color_rettype(a,b), sumtype(S,T))(gray(a)-gray(b))
280+
(-)(a::AbstractGray{Bool}, b::AbstractGray{T}) where {T} = parametric(color_rettype(a,b), sumtype(Bool,T))(gray(a)) - b
281+
(-)(a::AbstractGray{S}, b::AbstractGray{Bool}) where {S} = a - parametric(color_rettype(a,b), sumtype(S,Bool))(gray(b))
282+
(-)(a::AbstractGray{Bool}, b::AbstractGray{Bool}) = a + b # GF(2)
261283
(-)(a::TransparentGray, b::TransparentGray) = parametric(color_rettype(a,b), sumtype(eltype(a),eltype(b)))(gray(a)-gray(b),alpha(a)-alpha(b))
262284
(*)(a::AbstractGray{S}, b::AbstractGray{T}) where {S,T} = parametric(color_rettype(a,b), multype(S,T))(gray(a)*gray(b))
263285
(^)(a::AbstractGray{S}, b::Integer) where {S} = arith_colorant_type(a){powtype(S,Int)}(gray(a)^convert(Int,b))
264286
(^)(a::AbstractGray{S}, b::Real) where {S} = arith_colorant_type(a){powtype(S,typeof(b))}(gray(a)^b)
265287
(+)(c::AbstractGray) = c
266288
(+)(c::TransparentGray) = c
267-
(-)(c::AbstractGray) = typeof(c)(-gray(c))
268-
(-)(c::TransparentGray) = typeof(c)(-gray(c),-alpha(c))
289+
(-)(c::AbstractGray) = typeof(c)(_neg(gray(c)))
290+
(-)(c::TransparentGray) = typeof(c)(_neg(gray(c)),_neg(alpha(c)))
269291
(/)(a::C, b::C) where C<:AbstractGray = base_color_type(C)(gray(a)/gray(b))
270292
(/)(a::AbstractGray, b::AbstractGray) = /(promote(a, b)...)
293+
(/)(a::AbstractGray{Bool}, b::AbstractGray) = /(UInt8(gray(a)), b)
294+
(/)(a::AbstractGray{Bool}, b::AbstractGray{Bool}) = gray(b) ? a : throw(DivideError())
271295
(+)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)+b)
296+
(+)(a::AbstractGray{Bool}, b::Number) = arith_colorant_type(a){sumtype(Bool, typeof(b))}(gray(a)) + b
297+
(+)(a::AbstractGray{Bool}, b::Bool) = a + UInt8(b)
272298
(+)(a::Number, b::AbstractGray) = b+a
273299
(-)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)-b)
274300
(-)(a::Number, b::AbstractGray) = base_color_type(b)(a-gray(b))
301+
(-)(a::AbstractGray{Bool}, b::Number) = arith_colorant_type(a){sumtype(Bool, typeof(b))}(gray(a)) - b
302+
(-)(a::Number, b::AbstractGray{Bool}) = a - arith_colorant_type(b){sumtype(typeof(a), Bool)}(gray(b))
303+
(-)(a::AbstractGray{Bool}, b::Bool) = a - UInt8(b)
304+
(-)(a::Bool, b::AbstractGray{Bool}) = UInt8(a) - b
275305

276306
()(x::AbstractGray, y::AbstractGray) = gray(x)*gray(y)
277307
()(x::C, y::C) where C<:AbstractGray = base_color_type(C)(gray(x)*gray(y))

test/runtests.jl

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ using ColorVectorSpace, Colors, FixedPointNumbers
33

44
using Test
55

6+
@test isempty(detect_ambiguities(ColorVectorSpace))
7+
68
n8sum(x,y) = Float64(N0f8(x)) + Float64(N0f8(y))
79

810
macro test_colortype_approx_eq(a, b)
@@ -480,6 +482,78 @@ ColorTypes.blue(c::RatRGB) = c.b
480482
@test cf cf === (Float64(red(cf))^2 + Float64(green(cf))^2 + Float64(blue(cf))^2)/3
481483
end
482484

485+
@testset "arithmetic with Bool" begin # issue 148
486+
cb = Gray{Bool}(1)
487+
@test @inferred(+cb) === cb
488+
@test @inferred(-cb) === cb # wrapped around
489+
@test @inferred(one(cb)*cb) === cb
490+
@test oneunit(cb) === Gray(true)
491+
# vs. Bool
492+
@test @inferred(cb + true) === @inferred(true + cb) === Gray{Float32}(2)
493+
@test @inferred(cb - true) === Gray{Float32}(0)
494+
@test @inferred(true - cb) === Gray{Float32}(0)
495+
@test @inferred(cb * true) === @inferred(true * cb) === Gray{Bool}(1)
496+
@test @inferred(cb / true) === Gray{Float32}(1)
497+
@test @inferred(true / cb) === Gray{Float32}(1)
498+
@test @inferred(cb^true) === cb
499+
# vs. Gray{Bool}
500+
@test @inferred(cb + Gray(true)) === @inferred(Gray(true) + cb) === Gray{Bool}(0) # wrapped around
501+
@test @inferred(cb - Gray(true)) === Gray{Bool}(0)
502+
@test @inferred(Gray(false) - cb) === Gray{Bool}(1) # wrapped around
503+
@test @inferred(cb * Gray(true)) === @inferred(Gray(true) * cb) === Gray{Bool}(1)
504+
@test @inferred(cb / Gray(true)) === Gray{Bool}(1)
505+
@test_throws Exception cb / Gray(false)
506+
# vs. Int
507+
@test @inferred(cb + 2) === @inferred(2 + cb) === Gray{Float32}(3)
508+
@test @inferred(cb - 2) === Gray{Float32}(-1)
509+
@test @inferred(2 - cb) === Gray{Float32}(1)
510+
@test @inferred(cb * 2) === @inferred(2 * cb) === Gray{Float32}(2)
511+
@test @inferred(cb / 2) === Gray{Float32}(0.5)
512+
@test @inferred(2 / cb) === Gray{Float32}(2)
513+
@test @inferred(cb^1) === cb
514+
# vs. Float32 and Gray{Float32}
515+
@testset "vs. $(typeof(x))" for x in (0.5f0, Gray(0.5f0))
516+
@test @inferred(cb + x) === @inferred(x + cb) === Gray{Float32}(1.5)
517+
@test @inferred(cb - x) === Gray{Float32}(0.5)
518+
@test @inferred(x - cb) === Gray{Float32}(-0.5)
519+
@test @inferred(cb * x) === @inferred(x * cb) === Gray{Float32}(0.5)
520+
@test @inferred(cb / x) === Gray{Float32}(2)
521+
@test @inferred(x / cb) === Gray{Float32}(0.5)
522+
end
523+
@test @inferred(cb^0.5f0) === Gray{Float32}(1)
524+
# vs. N0f8 and Gray{N0f8}
525+
@testset "vs. $(typeof(x))" for x in (0.6N0f8, Gray(0.6N0f8))
526+
@test @inferred(cb + x) === @inferred(x + cb) === Gray{Float32}(1.6)
527+
@test @inferred(cb - x) === Gray{Float32}(1.0f0 - 0.6N0f8)
528+
@test @inferred(x - cb) === Gray{Float32}(0.6N0f8 - 1.0f0)
529+
@test @inferred(cb * x) === @inferred(x * cb) === Gray{N0f8}(0.6)
530+
@test @inferred(cb / x) === Gray{Float32}(1 / 0.6)
531+
@test @inferred(x / cb) === Gray{N0f8}(0.6)
532+
end
533+
534+
@testset "vs. $(typeof(c)) multiplications" for c in (Gray(true), Gray(0.5f0), Gray(0.6N0f8))
535+
@test @inferred(cb c) === @inferred(c cb) === gray(c)
536+
@test @inferred(cb c) === @inferred(c cb) === c
537+
@test @inferred(cb c) === @inferred(c cb) === c
538+
end
539+
cf = RGB{Float32}(0.1, 0.2, 0.3)
540+
@test @inferred(cf + cb) === @inferred(cb + cf) === RGB{Float32}(1.1, 1.2, 1.3)
541+
@test @inferred(cf - cb) === RGB{Float32}(-0.9, -0.8, -0.7)
542+
@test @inferred(cb - cf) === RGB{Float32}(0.9, 0.8, 0.7)
543+
cu = RGB{N0f8}(0.1, 0.2, 0.3)
544+
@test @inferred(cu + cb) === @inferred(cb + cu) === mapc(v -> v + 1N0f8, cu) # wrapped around
545+
@test @inferred(cu - cb) === mapc(v -> v - 1N0f8, cu) # wrapped around
546+
@test @inferred(cb - cu) === mapc(v -> 1N0f8 - v, cu)
547+
@testset "vs. $(typeof(c))" for c in (cf, cu)
548+
@test @inferred(c * true) === @inferred(true * c) === c
549+
@test @inferred(c / true) === c / 1
550+
@test @inferred(cb c) === @inferred(c cb) === Gray(1) c
551+
@test @inferred(cb c) === @inferred(c cb) === c
552+
@test @inferred(cb c) === Gray(1) c
553+
@test @inferred(c cb) === c Gray(1)
554+
end
555+
end
556+
483557
@testset "Complement" begin
484558
@test complement(Gray(0.2)) === Gray(0.8)
485559
@test complement(AGray(0.2f0, 0.7f0)) === AGray(0.8f0, 0.7f0)

0 commit comments

Comments
 (0)