text:
using ColorTypes, FixedPointNumbers
using Test

@testset "ColorTypes" begin

@test isempty(detect_ambiguities(ColorTypes, Base, Core))

# Support pre- and post- julia #20288
tformat(x...) = join(string.(x), ", ")

@test ColorTypes.to_top(AGray32(.8)) == ColorTypes.Colorant{FixedPointNumbers.Normed{UInt8,8},2}
@test @inferred(eltype(Color{N0f8})) == N0f8
@test @inferred(eltype(RGB{Float32})) == Float32
@test @inferred(eltype(RGBA{Float64})) == Float64
# @test eltype(RGB) == TypeVar(:T, Fractional)
@inferred(eltype(RGB))      # just test that it doesn't error

@test length(RGB)    == 3
@test length(RGB1)   == 3
@test length(Gray)   == 1
@test length(ARGB)   == 4
@test length(RGB24)  == 3
@test length(ARGB32) == 4
@test length(AGray{Float32}) == 2

# coloralpha/alphacolor for `TransparentColor`s (issue #126)
@test @inferred(coloralpha(RGBA)) == RGBA
@test @inferred(coloralpha(ARGB)) == RGBA
@test @inferred(coloralpha(RGBA{Float32})) == RGBA
@test @inferred(coloralpha(ARGB{Float32})) == RGBA
@test @inferred(coloralpha(AGray)) == GrayA
@test @inferred(coloralpha(GrayA)) == GrayA
@test @inferred(coloralpha(AGray{Float32})) == GrayA
@test @inferred(coloralpha(GrayA{Float32})) == GrayA
@test_throws MethodError coloralpha(ARGB32)
@test @inferred(alphacolor(RGBA)) == ARGB
@test @inferred(alphacolor(ARGB)) == ARGB
@test @inferred(alphacolor(RGBA{Float32})) == ARGB
@test @inferred(alphacolor(ARGB{Float32})) == ARGB
@test @inferred(alphacolor(AGray)) == AGray
@test @inferred(alphacolor(GrayA)) == AGray
@test @inferred(alphacolor(AGray{Float32})) == AGray
@test @inferred(alphacolor(GrayA{Float32})) == AGray
@test @inferred(alphacolor(ARGB32)) == ARGB32

@test @inferred(color_type(RGB{N0f8})) == RGB{N0f8}
@test @inferred(color_type(RGB)) == RGB
@test @inferred(color_type(RGBA{Float32})) == RGB{Float32}
@test @inferred(color_type(GrayA{N0f8})) == Gray{N0f8}
@test @inferred(color_type(RGBA)) == RGB
@test @inferred(color_type(RGB24) ) == RGB24
@test @inferred(color_type(ARGB32)) == RGB24
@test @inferred(color_type(TransparentColor{RGB})) == RGB
@test @inferred(color_type(TransparentColor{RGB,Float64})) == RGB
@test @inferred(color_type(TransparentColor{RGB{Float64},Float64})) == RGB{Float64}
@test color_type(TransparentColor) <: Color
@test Color <: color_type(TransparentColor)
@test_throws MethodError color_type(Colorant{N0f8})

@test @inferred(base_color_type(RGBA{Float32})) == RGB
@test @inferred(base_color_type(ARGB{Float32})) == RGB
@test @inferred(base_color_type(BGR{N0f8})      ) == BGR
@test @inferred(base_color_type(HSV) ) == HSV
@test @inferred(base_color_type(HSVA)) == HSV
@test @inferred(base_color_type(TransparentColor{RGB{Float64},Float64})) == RGB

@test @inferred(base_colorant_type(RGBA{Float32})) == RGBA
@test @inferred(base_colorant_type(ARGB{Float32})) == ARGB
@test @inferred(base_colorant_type(BGR{N0f8})      ) == BGR
@test @inferred(base_colorant_type(HSV) ) == HSV
@test @inferred(base_colorant_type(HSVA)) == HSVA

@testset "floattype" begin
    @test @inferred(floattype(RGBA{Float32})) == RGBA{Float32}
    @test @inferred(floattype(BGR{N0f8})    ) == BGR{Float32}
    @test @inferred(floattype(Gray{N0f8})   ) == Gray{Float32}
    @test @inferred(floattype(N0f8)         ) == Float32
    @test @inferred(floattype(Bool)         ) == Float32
    @test @inferred(floattype(Float32)      ) == Float32
    @test @inferred(floattype(Float64)      ) == Float64

    @test @inferred(floattype(ARGB32)) == ARGB{Float32}
    @test @inferred(floattype(AGray32)) == AGray{Float32}
    @test @inferred(floattype(RGB24)) == RGB{Float32}
    @test @inferred(floattype(Gray24)) == Gray{Float32}
end

@test @inferred(ccolor(Colorant{N0f8,3}, BGR{N0f8})) == BGR{N0f8}

@test @inferred(ccolor(RGB{Float32}, HSV{Float32})) == RGB{Float32}
@test @inferred(ccolor(RGB{N0f8},      HSV{Float32})) == RGB{N0f8}
@test @inferred(ccolor(RGB,          HSV{Float32})) == RGB{Float32}
@test @inferred(ccolor(ARGB{Float32}, HSV{Float32})) == ARGB{Float32}
@test @inferred(ccolor(ARGB{N0f8},      HSV{Float32})) == ARGB{N0f8}
@test @inferred(ccolor(ARGB,          HSV{Float32})) == ARGB{Float32}

@test @inferred(ccolor(Gray{N0f8}, Bool)) === Gray{N0f8}
@test @inferred(ccolor(Gray,     Bool)) === Gray{Bool}
@test @inferred(ccolor(Gray,     Int))  === Gray{N0f8}
# This tests the same thing as the last, but in a user-observable way
let a = Array{Gray}(undef, 1)
    a[1] = Gray(0)
    a[1] = 1
    @test a[1] === Gray(1)
end

@test @inferred(ccolor(RGB,  RGB))  == RGB
@test @inferred(ccolor(Gray, Gray)) == Gray

# Traits for instances (and their constructors)
@test @inferred(eltype(RGB{N0f8}(1,0,0))) == N0f8
@test @inferred(eltype(RGB(1.0,0,0))) == Float64
@test @inferred(eltype(ARGB(1.0,0.8,0.6,0.4))) == Float64
@test @inferred(eltype(RGBA{Float32}(1.0,0.8,0.6,0.4))) == Float32
@test @inferred(eltype(RGB(0x01,0x00,0x00))) == N0f8

@test length(ARGB(1.0,0.8,0.6,0.4)) == 4

@test @inferred(color_type(RGB{N0f8}(1,0,0))) == RGB{N0f8}
@test @inferred(color_type(ARGB(1.0,0.8,0.6,0.4))) == RGB{Float64}
@test @inferred(color_type(RGBA{Float32}(1.0,0.8,0.6,0.4))) == RGB{Float32}

@test @inferred(base_color_type(RGB{N0f8}(1,0,0))) == RGB
@test @inferred(base_color_type(ARGB(1.0,0.8,0.6,0.4))) == RGB
@test @inferred(base_color_type(RGBA{Float32}(1.0,0.8,0.6,0.4))) == RGB

@test @inferred(base_colorant_type(RGB{N0f8}(1,0,0))) == RGB
@test @inferred(base_colorant_type(ARGB(1.0,0.8,0.6,0.4))) == ARGB
@test @inferred(base_colorant_type(RGBA{Float32}(1.0,0.8,0.6,0.4))) == RGBA

@test N0f8 <: ColorTypes.eltypes_supported(RGB(1,0,0))

# Constructors
for val in (0.2, 0.2f0, N0f8(0.2), N4f12(0.2), N0f16(0.2),
            Gray{N0f8}(0.2), Gray{N4f12}(0.2), Gray24(0.2))
    @test isa(Gray(val), Gray)
    @test Gray{N0f8}(val) === Gray{N0f8}(0.2)
    @test Gray{N0f16}(val) === Gray{N0f16}(0.2)
    @test Gray24(val) === Gray24(0.2)
    @test AGray32(val) === AGray32(0.2, 1)
    @test AGray32(val, 0.8) === AGray32(0.2, 0.8)
end
for val in (1.2, 1.2f0, N4f12(1.2), Gray{N4f12}(1.2), 2)
    !isa(val, Int) && @test isa(Gray(val), Gray)
    @test_throws ArgumentError Gray{N0f8}(val)
    @test_throws ArgumentError Gray{N0f16}(val)
    @test_throws ArgumentError Gray24(val) == Gray24(0.2)
    @test_throws ArgumentError GrayA{N0f8}(val)
    @test_throws ArgumentError AGray{N0f8}(val)
    @test_throws ArgumentError AGray32(val)
    @test_throws ArgumentError AGray32(val, 0.8)
end
@test eltype(Gray()) == N0f8
@test Gray(Gray()) == Gray()  # no StackOverflowError
@test eltype(broadcast(Gray, rand(5))) == Gray{Float64}
@test eltype(broadcast(Gray, rand(Float32,5))) == Gray{Float32}

for C in ColorTypes.parametric3
    @test eltype(C{Float32}) == Float32
    et = (C <: AbstractRGB) ? N0f8 : Float32
    @test eltype(C(1,0,0)) == et
    @test color_type(C(1,0,0)) == C{et}
    @test color_type(C) == C
    @test color_type(C{Float32}) == C{Float32}
    @test eltype(C{Float32}(1,0,0)) == Float32
    @test C(C()) == C()  # no StackOverflowError
end

# Specifically test the AbstractRGB types
# This checks that the constructor order is the same, even if the
# storage order is not
for C in filter(T -> T <: AbstractRGB, ColorTypes.color3types)
    c = C(1, 0.5, 0)
    C == RGB24 && continue
    @test red(c)   == c.r == 1
    @test green(c) == c.g == 0.5
    @test blue(c)  == c.b == 0
end

# #80
z = N0f8(0)
@test HSV(z, z, z) === HSV{Float32}(0, 0, 0)

# Check various input types and values
for C in filter(T -> T <: AbstractRGB, ColorTypes.color3types)
    for val1 in (0.2, 0.2f0, N0f8(0.2), N4f12(0.2), N0f16(0.2))
        for val2 in (0.2, 0.2f0, N0f8(0.2), N4f12(0.2), N0f16(0.2))
            c = C(val1,val2,val1)
            @test isa(c, C)
            @test isa(alphacolor(C)(val1,val2,val1), alphacolor(C))
            @test isa(alphacolor(C)(val1,val2,val1,0.2), alphacolor(C))
            @test alphacolor(C)(c) === alphacolor(C)(val1,val2,val1,convert(eltype(c), 1))
            @test alphacolor(C)(c, 0.2) === alphacolor(C)(val1,val2,val1,convert(eltype(c), 0.2))
            @test alphacolor(c, 0.2) === alphacolor(C)(val1,val2,val1,convert(eltype(c), 0.2))
            if C !== RGB24
                @test isa(coloralpha(C)(val1,val2,val1), coloralpha(C))
                @test isa(coloralpha(C)(val1,val2,val1,0.2), coloralpha(C))
                @test C{N0f8}(val1,val2,val1) === C{N0f8}(0.2,0.2,0.2)
                @test C{N0f16}(val1,val2,val1) === C{N0f16}(0.2,0.2,0.2)
                @test alphacolor(C){N0f8}(val1,val2,val1) === alphacolor(C){N0f8}(0.2,0.2,0.2,1)
                @test alphacolor(C){N0f8}(val1,val2,val1,0.2) === alphacolor(C){N0f8}(0.2,0.2,0.2,0.2)
                @test coloralpha(C){N0f8}(val1,val2,val1) === coloralpha(C){N0f8}(0.2,0.2,0.2,1)
                @test coloralpha(C){N0f8}(val1,val2,val1,0.2) === coloralpha(C){N0f8}(0.2,0.2,0.2,0.2)
                @test coloralpha(C)(c) === coloralpha(C){eltype(c)}(val1,val2,val1,1)
                @test coloralpha(C)(c, 0.2) === coloralpha(C)(val1,val2,val1,convert(eltype(c), 0.2))
                @test coloralpha(c, 0.2) === coloralpha(C)(val1,val2,val1,convert(eltype(c), 0.2))
            end
        end
    end
    @test isa(C(1,0,1), C)
    @test C() === C(0,0,0)
    @test isa(alphacolor(C)(1,0,1), alphacolor(C))
    @test alphacolor(C)() === alphacolor(C)(0,0,0,1)
    if C != RGB24
        @test C(1,0,1) === C{N0f8}(1,0,1)
        @test alphacolor(C)(1,0,1) === alphacolor(C){N0f8}(1,0,1,1)
        @test alphacolor(C)(1,0,1,0.2) === alphacolor(C){Float64}(1,0,1,0.2)
        @test coloralpha(C)(1,0,1) === coloralpha(C){N0f8}(1,0,1,1)
        @test coloralpha(C)(1,0,1,0.2) === coloralpha(C){Float64}(1,0,1,0.2)
        @test coloralpha(C)() === coloralpha(C)(0,0,0,1)
    end
    for val in (1.2, 1.2f0, N4f12(1.2), 2)
        if C !== RGB24
            @test_throws ArgumentError C{N0f8}(val,val,val)
            @test_throws ArgumentError C{N0f16}(val,val,val)
            @test_throws ArgumentError alphacolor(C){N0f8}(val,val,val)
            @test_throws ArgumentError coloralpha(C){N0f8}(val,val,val)
            if val != 2
                @test isa(C(val,val,val), C)
                c = C(val,val,val)
                @test_throws ArgumentError alphacolor(C){N0f8}(c)
                @test_throws ArgumentError coloralpha(C){N0f8}(c)
                if C != RGB1 && C != RGB4
                    @test_throws ArgumentError alphacolor(C){N0f8}(c, 0.2)
                    @test_throws ArgumentError coloralpha(C){N0f8}(c, 0.2)
                end
            end
        end
        if C == RGB24 || isa(val, Int)
            @test_throws ArgumentError C(val,val,val)
            @test_throws ArgumentError alphacolor(C)(val,val,val)
        end
    end
end

ret = @test_throws ArgumentError RGB(255, 17, 48)
@test occursin(tformat(255,17,48), ret.value.msg)
@test occursin("0-255", ret.value.msg)
ret = @test_throws ArgumentError RGB(256, 17, 48)
@test occursin(tformat(256,17,48), ret.value.msg)
@test !occursin("0-255", ret.value.msg)

@testset "Test some Gray stuff" begin
    c = Gray(0.8)
    @test gray(c) == real(c) == 0.8
    @test gray(0.8) == 0.8
    c = convert(Gray, 0.8)
    @test c === Gray{Float64}(0.8)

    ac = convert(AGray, c)
    @test ac === AGray{Float64}(0.8, 1.0)
    ac = AGray(c)
    @test ac === AGray{Float64}(0.8, 1.0)
    ac = AGray{Float64}(c)
    @test ac === AGray{Float64}(0.8, 1.0)
    ca = GrayA{Float64}(ac)
    @test ca === GrayA{Float64}(0.8, 1.0)

    c = AGray(0.8)
    @test gray(c) == 0.8
    @test color(c) == Gray(0.8)

    c = convert(Gray, true)
    @test c === Gray{Bool}(true)
    @test gray(c) === true
    @test gray(false) === false
end

# Transparency
@test alphacolor(Gray24(.2), .8) == AGray32(.2,.8)
@test alphacolor(RGB24(1,0,0), .8) == ARGB32(1,0,0,.8)
@test alphacolor(RGB(1,0,0), .8) == ARGB{N0f8}(1,0,0,.8)
@test coloralpha(RGB(1,0,0), .8) == RGBA{N0f8}(1,0,0,.8)
@test alphacolor(RGBA(1,0,0,.8)) == ARGB{Float64}(1,0,0,.8)
@test coloralpha(ARGB(1,0,0,.8)) == RGBA{Float64}(1,0,0,.8)
@test alphacolor(RGBA(1,0,0,.8)) == ARGB{Float64}(1,0,0,.8)
@test coloralpha(ARGB(1,0,0,.8)) == RGBA{Float64}(1,0,0,.8)
for C in setdiff(ColorTypes.parametric3, [RGB1,RGB4])
    for A in (alphacolor(C), coloralpha(C))
        @test eltype(A{Float32}) == Float32
        @test color_type(A) == C
        @test color_type(A{Float32}) == C{Float32}
        @test eltype(A(1,0.8,0.6,0.4)) == Float64
        c = A{Float64}(1,0.8,0.6,0.4)
        @test color_type(c) == C{Float64}
        cc = color(c)
        @test cc == C{Float64}(1,0.8,0.6)
        @test A(cc) == A{Float64}(1,0.8,0.6,1)
        @test A(cc, 0.4)  == c
        @test A(cc, 0x01) == A{Float64}(1,0.8,0.6,1)
        @test A{Float32}(cc, 0x01) == A{Float32}(1,0.8,0.6,1)
        @test C(c         ) == C{Float64}(1,0.8,0.6)
        @test C{Float32}(c) == C{Float32}(1,0.8,0.6)
        @test convert(A, cc) == A{Float64}(1,0.8,0.6,1)
        @test A(cc) === A{Float64}(1,0.8,0.6,1)
        @test A{Float64}(cc) === A{Float64}(1,0.8,0.6,1)
        @test convert(A, cc, 0.4)  == c
        @test convert(A, cc, 0x01) == A{Float64}(1,0.8,0.6,1)
        @test convert(A{Float32}, cc, 0x01) == A{Float32}(1,0.8,0.6,1)
        @test convert(C,          c) == C{Float64}(1,0.8,0.6)
        @test convert(C{Float32}, c) == C{Float32}(1,0.8,0.6)
        @test C{Float32}(c) === C{Float32}(1,0.8,0.6)
    end
    AC, CA = alphacolor(C), coloralpha(C)
    @test AC(CA{Float64}(1,0.8,0.6,0.4)) == AC{Float64}(1,0.8,0.6,0.4)
    @test AC{Float64}(CA{Float64}(1,0.8,0.6,0.4)) == AC{Float64}(1,0.8,0.6,0.4)
    @test CA(AC{Float64}(1,0.8,0.6,0.4)) == CA{Float64}(1,0.8,0.6,0.4)
    @test CA{Float64}(AC{Float64}(1,0.8,0.6,0.4)) == CA{Float64}(1,0.8,0.6,0.4)
end
ac = reinterpret(ARGB32, rand(UInt32))
@test convert(ARGB32, ac) == ac
c = convert(RGB24, ac)
@test convert(RGB24, c) == c

crgb   = convert(RGB, c)
acargb = convert(ARGB, ac)
@test convert(Colorant,       crgb) === crgb
@test convert(Colorant{N0f8},   crgb) === crgb
@test convert(Colorant{Float32},       crgb) === convert(RGB{Float32}, crgb)
@test convert(Color,          crgb) === crgb
@test convert(Color{N0f8},      crgb) === crgb
@test convert(Color{Float32}, crgb) === convert(RGB{Float32}, crgb)
@test_throws ErrorException convert(TransparentColor, crgb)
@test_throws ErrorException convert(TransparentColor{RGB{N0f8}}, crgb)
@test_throws ErrorException convert(TransparentColor{RGB{N0f8},N0f8}, crgb)
@test_throws ErrorException convert(TransparentColor{RGB{N0f8},N0f8,2}, crgb)
@test convert(AlphaColor, crgb) === alphacolor(crgb)
@test convert(AlphaColor{RGB{N0f8}}, crgb) === alphacolor(crgb)
@test convert(AlphaColor{RGB{N0f8},N0f8}, crgb) === alphacolor(crgb)
@test convert(AlphaColor{RGB{N0f8},N0f8,4}, crgb) === alphacolor(crgb)
@test convert(ColorAlpha, crgb) === coloralpha(crgb)
@test convert(ColorAlpha{RGB{N0f8}}, crgb) === coloralpha(crgb)
@test convert(ColorAlpha{RGB{N0f8},N0f8}, crgb) === coloralpha(crgb)
@test convert(ColorAlpha{RGB{N0f8},N0f8,4}, crgb) === coloralpha(crgb)

@test convert(Colorant, acargb) === acargb
@test convert(Colorant{N0f8},   acargb) === acargb
@test convert(Colorant{N0f8,3}, acargb) === crgb
@test convert(TransparentColor, acargb) == acargb
@test convert(Color, acargb) == crgb
@test convert(AlphaColor, acargb) === acargb # issue #126
@test convert(ColorAlpha, acargb) == coloralpha(acargb) # issue #126

h = N0f8(0.5)
@test Gray24(0.5) == Gray24(h)
@test convert(Gray24, 0.5) == Gray24(h)
@test convert(AGray, Gray24(h)) === AGray{N0f8}(h, 1)
@test convert(AGray, Gray24(h), 0.8)  === AGray{N0f8}(h, 0.8)
@test convert(AGray, AGray32(h, 0.8)) === AGray{N0f8}(h, 0.8)
@test AGray32(0.5) == AGray32(h, 1)
@test convert(AGray32, 0.5, 0.8) == AGray32(h, N0f8(0.8))
@test RGB24(0.5) == RGB24(h, h, h)
@test convert(RGB24, 0.5) == RGB24(h, h, h)
@test ARGB32(0.5) == ARGB32(h, h, h, 1)

@test red(c)   == red(ac)
@test green(c) == green(ac)
@test blue(c)  == blue(ac)
ac2 = convert(ARGB32, c)
@test reinterpret(UInt32, ac2) == (c.color | 0xff000000)
@test color(c) == c
@test color(ac) == c
@test alpha(c) == N0f8(1)
@test alpha(ac) == N0f8(ac.color>>24, 0)
@test alpha(ac2) == N0f8(1)
@test reinterpret(UInt32, reinterpret(RGB24,  0xff020304)) == 0xff020304
@test reinterpret(UInt32, reinterpret(ARGB32, 0x01020304)) == 0x01020304
ac3 = convert(RGBA, ac)
@test convert(RGB24, ac3) == c
ac4 = AGray32(.2,.8)
@test alpha(ac4) == .8
@test gray(ac4) == .2

for C in filter(T -> T <: AbstractRGB, ColorTypes.color3types)
    rgb = convert(C, c)
    C == RGB24 && continue
    @test ccolor(Gray24, C) == Gray24
    @test ccolor(AGray32, C) == AGray32
    @test convert(AbstractRGB, c) == c
    @test convert(AbstractRGB{Float64}, rgb) === convert(C{Float64}, c)
    argb = convert(alphacolor(C), ac)
    rgba = convert(coloralpha(C), ac)
    @test rgb.r == red(c)
    @test rgb.g == green(c)
    @test rgb.b == blue(c)
    @test argb.alpha == alpha(ac)
    @test argb.r == red(ac)
    @test argb.g == green(ac)
    @test argb.b == blue(ac)
    @test rgba.alpha == alpha(ac)
    @test rgba.r == red(ac)
    @test rgba.g == green(ac)
    @test rgba.b == blue(ac)
end

@test_throws ErrorException convert(HSV, RGB(1,0,1))
@test_throws ErrorException convert(AHSV, RGB(1,0,1), 0.5)

@test convert(Float64, Gray(.3)) === .3
@test Float64(Gray(0.3)) === 0.3
@test float(Gray(0.3))   === 0.3
x = N0f8(0.3)
@test convert(N0f8, Gray24(0.3)) === x
@test convert(GrayA{N0f8}, .2) == GrayA{N0f8}(.2)
@test convert(AGray{N0f8}, .2) == AGray{N0f8}(.2)
@test Gray{N0f8}(0.37).val           == N0f8(0.37)
@test convert(Gray{N0f8}, 0.37).val  == N0f8(0.37)
@test reinterpret(UInt32, Gray24(reinterpret(N0f8, 0x0d)))           == 0x000d0d0d
@test reinterpret(UInt32, convert(Gray24, reinterpret(N0f8, 0x0d)))  == 0x000d0d0d
@test reinterpret(UInt32, AGray32(reinterpret(N0f8, 0x0d)))          == 0xff0d0d0d
@test reinterpret(UInt32, convert(AGray32, reinterpret(N0f8, 0x0d))) == 0xff0d0d0d
@test reinterpret(UInt32, AGray32(reinterpret(N0f8, 0x0d), reinterpret(N0f8, 0x80))) == 0x800d0d0d
@test convert(Gray{N0f16}, Gray24(reinterpret(N0f8, 0x0d))) == Gray{N0f16}(0.05098)

@test promote(Gray{N0f8}(0.2), Gray24(0.3)) === (Gray{N0f8}(0.2), Gray{N0f8}(0.3))
@test promote(Gray(0.2f0), Gray24(0.3)) === (Gray{Float32}(0.2), Gray{Float32}(N0f8(0.3)))
@test promote(RGB{N0f8}(0.2,0.3,0.4), RGB24(0.3,0.8,0.1)) === (RGB{N0f8}(0.2,0.3,0.4), RGB{N0f8}(0.3,0.8,0.1))
@test promote(RGB{Float32}(0.2,0.3,0.4), RGB24(0.3,0.8,0.1)) === (RGB{Float32}(0.2,0.3,0.4), RGB{Float32}(N0f8(0.3),N0f8(0.8),N0f8(0.1)))

iob = IOBuffer()
cf = RGB{Float32}(0.32218,0.14983,0.87819)
c  = convert(RGB{N0f8}, cf)
show(iob, c)
@test String(take!(iob)) == "RGB{N0f8}(0.322,0.149,0.878)"
c = RGB{N0f16}(0.32218,0.14983,0.87819)
show(iob, c)
@test String(take!(iob)) == "RGB{N0f16}(0.32218,0.14983,0.87819)"
c = RGBA{N0f8}(0.32218,0.14983,0.87819,0.99241)
show(iob, c)
@test String(take!(iob)) == "RGBA{N0f8}(0.322,0.149,0.878,0.992)"
show(IOContext(iob, :compact => true), c)
@test String(take!(iob)) == "RGBA{N0f8}(0.322,0.149,0.878,0.992)"
show(iob, cf)
@test String(take!(iob)) == "RGB{Float32}(0.32218f0,0.14983f0,0.87819f0)"
show(IOContext(iob, :compact => true), cf)
@test String(take!(iob)) == "RGB{Float32}(0.32218,0.14983,0.87819)"
show(iob, Gray24(0.4))
@test String(take!(iob)) == "Gray24(0.4N0f8)"
show(iob, RGB24(0.4,0.2,0.8))
@test String(take!(iob)) == "RGB24(0.4N0f8,0.2N0f8,0.8N0f8)"
show(iob, ARGB32(0.4,0.2,0.8,1.0))
@test String(take!(iob)) == "ARGB32(0.4N0f8,0.2N0f8,0.8N0f8,1.0N0f8)"
summary(iob, Gray{N0f8}[0.2, 0.4, 0.6])
@test String(take!(iob)) == "3-element Array{Gray{N0f8},1} with eltype Gray{Normed{UInt8,8}}"
@test ColorTypes.colorant_string(Union{})   == "Union{}"
@test ColorTypes.colorant_string(RGB{N0f8}) == "RGB"
@test ColorTypes.colorant_string(RGB24)     == "RGB24"
@test ColorTypes.colorant_string(ARGB32)    == "ARGB32"
@test ColorTypes.colorant_string(Gray24)    == "Gray24"
@test ColorTypes.colorant_string(AGray32)   == "AGray32"
@test ColorTypes.colorant_string_with_eltype(Union{})   == "Union{}"
@test ColorTypes.colorant_string_with_eltype(RGB{N0f8}) == "RGB{N0f8}"
@test ColorTypes.colorant_string_with_eltype(RGB24)     == "RGB24"
@test ColorTypes.colorant_string_with_eltype(ARGB32)    == "ARGB32"
@test ColorTypes.colorant_string_with_eltype(Gray24)    == "Gray24"
@test ColorTypes.colorant_string_with_eltype(AGray32)   == "AGray32"

@test oneunit(Gray{N0f8}) == Gray{N0f8}(1)
@test zero(Gray{N0f8}) == Gray{N0f8}(0)

c = Gray(0.8)
@test c == 0.8
show(iob, c)
@test String(take!(iob)) == "Gray{Float64}(0.8)"
show(iob, AGray(0.8))
@test String(take!(iob)) == "AGray{Float64}(0.8,1.0)"
show(iob, AGray32(0.8))
@test String(take!(iob)) == "AGray32{N0f8}(0.8,1.0)"

# if the test below fails, please extend the list of types at the call to
# make_alpha in types.jl (this is the price of making that list explicit)
@test Set(ColorTypes.ctypes) ==
Set([DIN99d, DIN99o, DIN99, HSI, HSL, HSV, LCHab, LCHuv,
     LMS, Lab, Luv, XYZ, YCbCr, YIQ, xyY, BGR, RGB, Gray])

## operations
for T in (Gray{N0f8}, Gray{N2f6}, Gray{N0f16}, Gray{N2f14}, Gray{N0f32}, Gray{N2f30},
          Gray{Float16}, Gray{Float32}, Gray{Float64},
          RGB{N0f8}, RGB{N2f6}, RGB{N0f16}, RGB{N2f14}, RGB{N0f32}, RGB{N2f30},
          RGB{Float16}, RGB{Float32}, RGB{Float64},
          AGray{Float32}, GrayA{Float64},
          RGBA{Float32}, ARGB{N0f16}, RGB1{N0f8}, RGB4{Float64},
          BGR{Float16}, ABGR{N0f32}, BGRA{N2f14},
          Gray, AGray, GrayA,
          RGB, ARGB, RGBA, BGR, ABGR, BGRA, RGB1, RGB4,
          HSV, HSL, Lab, LCHab, YIQ)
    a = rand(T)
    @test all(x->x[2]<=getfield(a,x[1])<=x[3],
                zip(ColorTypes.colorfields(T),gamutmin(T),gamutmax(T)))
    @test isa(a, T)
    a = rand(T, (3, 5))
    if isconcretetype(T)
        @test isa(a, Array{T,2})
    end
    for el in a
        @test all(x->x[2]<=getfield(el,x[1])<=x[3],
                    zip(ColorTypes.colorfields(T),gamutmin(T),gamutmax(T)))
    end
    @test eltype(a) <: T
    @test size(a) == (3,5)
    ap = a'
    @test ap[1,1] == a[1,1]
end
for T in (Gray24, AGray32)
    a = rand(T)
    b = a.color
    @test b&0xff == (b>>8)&0xff == (b>>16)&0xff
    @test isa(a, T)
    a = rand(T,3,5)
    for el in a
        b = el.color
        @test b&0xff == (b>>8)&0xff == (b>>16)&0xff
    end
    @test eltype(a) <: T
    @test size(a) == (3,5)
end

@test eltype(broadcast(RGB, [BGR(1,0,0)])) == RGB{N0f8}
addred(x1::AbstractRGB, x2::AbstractRGB) = red(x1) + red(x2)
@test addred.([RGB(1,0,0)], RGB(1.0,0,0)) == [2]

# colorfields
@test ColorTypes.colorfields(AGray32(.2)) == (:color,:alpha)
@test ColorTypes.colorfields(Gray) == (:val,)
@test ColorTypes.colorfields(RGB1) == (:r, :g, :b)
@test ColorTypes.colorfields(RGB4) == (:r, :g, :b)
@test ColorTypes.colorfields(BGR) == (:r, :g, :b)

# UInt32 comparison
@test reinterpret(UInt32, Gray24()) == 0x00000000
@test reinterpret(UInt32, Gray24(.2)) == 0x00333333
@test reinterpret(UInt32, Gray24(reinterpret(N0f8, 0x23))) == 0x00232323
@test reinterpret(UInt32, AGray32()) == 0xff000000
@test reinterpret(UInt32, AGray32(.2)) == 0xff333333
@test reinterpret(UInt32, convert(AGray32, .2, 0.)) == 0x00333333
@test reinterpret(UInt32, AGray32(reinterpret(N0f8, 0x23))) == 0xff232323
@test reinterpret(UInt32, RGB24()) == 0x00000000
@test reinterpret(UInt32, ARGB32()) == 0xff000000
@test reinterpret(UInt32, ARGB32(1,.2,.3)) == 0xffff334c

@test @inferred(mapc(sqrt, Gray{N0f8}(0.04))) == Gray(sqrt(N0f8(0.04)))
@test @inferred(mapc(sqrt, AGray{N0f8}(0.04, 0.4))) == AGray(sqrt(N0f8(0.04)), sqrt(N0f8(0.4)))
@test @inferred(mapc(sqrt, GrayA{N0f8}(0.04, 0.4))) == GrayA(sqrt(N0f8(0.04)), sqrt(N0f8(0.4)))
@test @inferred(mapc(x->2x, RGB{N0f8}(0.04,0.2,0.3))) == RGB(map(x->2*N0f8(x), (0.04,0.2,0.3))...)
@test @inferred(mapc(sqrt, RGBA{N0f8}(0.04,0.2,0.3,0.7))) == RGBA(map(x->sqrt(N0f8(x)), (0.04,0.2,0.3,0.7))...)
@test @inferred(mapc(x->1.5f0x, RGBA{N0f8}(0.04,0.2,0.3,0.4))) == RGBA(map(x->1.5f0*N0f8(x), (0.04,0.2,0.3,0.4))...)

@test @inferred(mapc(max, Gray{N0f8}(0.2), Gray{N0f8}(0.3))) == Gray{N0f8}(0.3)
@test @inferred(mapc(-, AGray{Float32}(0.3), AGray{Float32}(0.2))) == AGray{Float32}(0.3f0-0.2f0,0.0)
@test @inferred(mapc(min, RGB{N0f8}(0.2,0.8,0.7), RGB{N0f8}(0.5,0.2,0.99))) == RGB{N0f8}(0.2,0.2,0.7)
@test @inferred(mapc(+, RGBA{N0f8}(0.2,0.8,0.7,0.3), RGBA{Float32}(0.5,0.2,0.99,0.5))) == RGBA(0.5f0+N0f8(0.2),0.2f0+N0f8(0.8),0.99f0+N0f8(0.7),0.5f0+N0f8(0.3))
@test @inferred(mapc(+, HSVA(0.1,0.8,0.3,0.5), HSVA(0.5,0.5,0.5,0.3))) == HSVA(0.1+0.5,0.8+0.5,0.3+0.5,0.5+0.3)
@test_throws ArgumentError mapc(min, RGB{N0f8}(0.2,0.8,0.7), BGR{N0f8}(0.5,0.2,0.99))
@test @inferred(mapc(abs, -2)) == 2

@test @inferred(reducec(+, 0.0, Gray(0.3))) === 0.3
@test @inferred(reducec(+, 1.0, Gray(0.3))) === 1.3
@test @inferred(reducec(+, 0, Gray(0.3))) === 0.3
@test @inferred(reducec(+, 0.0, AGray(0.3, 0.8))) === 0.3 + 0.8
@test @inferred(reducec(+, 0.0, RGB(0.3, 0.8, 0.5))) === (0.3 + 0.8) + 0.5
@test @inferred(reducec(+, 0.0, RGBA(0.3, 0.8, 0.5, 0.7))) === ((0.3 + 0.8) + 0.5) + 0.7
@test @inferred(reducec(&, true, Gray(true)))
@test !(@inferred(reducec(&, false, Gray(true))))
@test !(@inferred(reducec(&, true, Gray(false))))

@test @inferred(reducec(+, 0.0, 0.3)) === 0.3
@test @inferred(reducec(+, 0, 0.3)) === 0.3
@test @inferred(reducec(&, true, true))
@test !(@inferred(reducec(&, false, true)))
@test !(@inferred(reducec(&, true, false)))

@test @inferred(mapreducec(x->x^2, +, 0.0, Gray(0.3))) === 0.3^2
@test @inferred(mapreducec(x->x^2, +, 1.0, Gray(0.3))) === 1 + 0.3^2
@test @inferred(mapreducec(x->x^2, +, 0, Gray(0.3))) === 0.3^2
@test @inferred(mapreducec(x->x^2, +, 0.0, AGray(0.3, 0.8))) === 0.3^2 + 0.8^2
@test @inferred(mapreducec(x->x^2, +, 0.0, RGB(0.3, 0.8, 0.5))) === (0.3^2 + 0.8^2) + 0.5^2
@test @inferred(mapreducec(x->x^2, +, 0.0, RGBA(0.3, 0.8, 0.5, 0.7))) === ((0.3^2 + 0.8^2) + 0.5^2) + 0.7^2
@test !(@inferred(mapreducec(x->!x, &, true, Gray(true))))
@test !(@inferred(mapreducec(x->!x, &, false, Gray(true))))
@test @inferred(mapreducec(x->!x, &, true, Gray(false)))
@test !@inferred(mapreducec(x->!x, &, false, Gray(false)))

@test @inferred(mapreducec(x->x^2, +, 0.0, 0.3)) === 0.3^2
@test @inferred(mapreducec(x->x^2, +, 1.0, 0.3)) === 1 + 0.3^2
@test @inferred(mapreducec(x->x^2, +, 0, 0.3)) === 0.3^2
@test !(@inferred(mapreducec(x->!x, &, true, true)))
@test !(@inferred(mapreducec(x->!x, &, false, true)))
@test @inferred(mapreducec(x->!x, &, true, false))
@test !@inferred(mapreducec(x->!x, &, false, false))

@test Gray(0.8) ≈ Gray(0.8 + eps())
@test Gray(0.8) ≈ 0.8 + eps()
@test 0.8 + eps() ≈ Gray(0.8)
@test GrayA(0.8, 0.4) ≈ GrayA(0.8 + eps(), 0.4)
@test RGB(0.2, 0.8, 0.4) ≈ RGB(0.2, 0.8 + eps(), 0.4)
@test RGBA(0.2, 0.8, 0.4, 0.2) ≈ RGBA(0.2, 0.8 + eps(), 0.4, 0.2 - eps())
@test !(Gray(0.8) ≈ Gray(0.6))
@test !(RGB(0.2, 0.8, 0.4) ≈ RGB(0.2, 0.8 + eps(), 0.5))
@test Gray(0.8f0) ≈ Gray(Float64(0.8f0 + eps(0.8f0)))
c = RGB{N0f8}(0.2, 0.8, 0.4)
c1 = mapc(Float32, c)
@test c == c1 && c ≈ c1
c2 = RGB(red(c1), green(c1)-0.1, blue(c1))
@test c ≈ c2 atol=0.11
@test !isapprox(c, c2; atol=0.09)
@test c ≈ convert(RGB4, c)
@test !(c ≈ HSV{Float32}(140.0f0,0.75f0,0.8f0))  # the latter comes from convert when using Colors
@test Gray(0.8N0f8) == Gray24(0.8) && Gray(0.8N0f8) ≈ Gray24(0.8)

# issue #52
@test AGray{BigFloat}(0.5,0.25) == AGray{BigFloat}(0.5,0.25)
@test RGBA{BigFloat}(0.5, 0.25, 0.5, 0.5) == RGBA{BigFloat}(0.5, 0.25, 0.5, 0.5)

for (a, b) in ((Gray(1.0), Gray(1)),
               (GrayA(0.8, 0.6), AGray(0.8, 0.6)),
               (RGB(1, 0.5, 0), BGR(1, 0.5, 0)),
               (RGBA(1, 0.5, 0, 0.8), ABGR(1, 0.5, 0, 0.8)))
    local a, b
    @test a == b
    @test hash(a) == hash(b)
end
for (a, b) in ((RGB(1, 0.5, 0), RGBA(1, 0.5, 0, 0.9)),)
    local a, b
    @test a != b
    @test hash(a) != hash(b)
end
# It's not obvious whether we want these to compare as equal, but
# whatever happens, you want hashing and equality-testing to yield the
# same result
for (a, b) in ((RGB(1, 0.5, 0), RGBA(1, 0.5, 0, 1)),)
    local a, b
    @test (a == b) == (hash(a) == hash(b))
end

### Prevent ambiguous definitions

# Certain types, like Gray24, reinterpret a UInt32 as having a
# particular color meaning. The problem with defining `convert`
# methods is that a UInt32 has a value, e.g., 0 = black and 1 = white.
# Users who want to interpret a UInt32 as a bit pattern should
# explicitly use `reinterpret`.
@testset "bit pattern ambiguities" begin
    @test_throws MethodError convert(UInt32, RGB24(1,1,1))
    @test_throws MethodError convert(UInt32, ARGB32(1,1,1,0))
    @test convert(UInt32, Gray24(1)) == 1
    @test_throws InexactError convert(UInt32, Gray24(0.5))
    @test_throws MethodError convert(UInt32, AGray32(1,0))
    @test !(RGB24(1,1,1) == 0x00ffffff)
    @test !(ARGB32(1,1,1,0) == 0x00ffffff)
    @test Gray24(1) == 1
    @test !(Gray24(1) == 0x00ffffff)
    @test !(AGray32(1,0) == 0x00ffffff)
    @test_throws ArgumentError RGB24(0x00ffffff)
    @test_throws ArgumentError ARGB32(0x00ffffff)
    @test_throws ArgumentError Gray24(0x00ffffff)
    @test_throws ArgumentError AGray32(0x00ffffff)
    @test_throws ArgumentError RGB24[0x00000000,0x00808080]
    @test_throws ArgumentError RGB24[0x00000000,0x00808080,0x00ffffff]
    @test_throws ArgumentError RGB24[0x00000000,0x00808080,0x00ffffff,0x000000ff]
end

### Prevent future commits from unexporting abstract types
@testset "abstract type exports" begin
    dispatcher(::AbstractGray) = 1
    dispatcher(::Color3) = 2
    dispatcher(::TransparentGray) = 3
    dispatcher(Transparent3) = 4
    dispatcher(::TransparentRGB) = 5
    normeddispatcher(::ColorantNormed) = true
    normeddispatcher(::Colorant) = false

    @test dispatcher(Gray(0.2)) == 1
    @test dispatcher(RGB(1.0,1.0,1.0)) == 2
    @test dispatcher(AGray(0.2,0.5)) == 3
    @test dispatcher(alphacolor(rand(HSV))) == 4
    @test dispatcher(ARGB(0.5,1.0,1.0,1.0)) == 5
    @test normeddispatcher(RGB(1.0,1.0,1.0)) == false
    @test normeddispatcher(RGB{N0f8}(1.0,1.0,1.0)) == true
end

@testset "color construction from grayscale" begin
    @test RGB(Gray(0.2), 0.3, 0.4) == RGB(0.2, 0.3, 0.4)
    @test RGB(0.2, Gray(0.3), 0.4) == RGB(0.2, 0.3, 0.4)
    @test RGB(0.2, 0.3, Gray(0.4)) == RGB(0.2, 0.3, 0.4)
    @test RGB(Gray(0.2), Gray(0.3), Gray(0.4)) == RGB(0.2, 0.3, 0.4)
end

end # ColorTypes

nothing


text:
@testset "Unblock" begin
    @test string(unblock(striplines(f))) == "f(x, y = 3; a, b = 5) = 50"
end

ext: jl


text:
module TestGridapMakie

using GridapMakie
using Gridap
using GLMakie
using Test

import FileIO

const OUTDIR = joinpath(@__DIR__, "output")
rm(OUTDIR, force=true, recursive=true)
mkpath(OUTDIR)

function savefig(f, suffix::String)
    fig = f()
    println("*"^80)
    filename = "$(suffix).png"
    @show filename
    path = joinpath(OUTDIR, filename)
    FileIO.save(path, fig)
    return true
end

@testset "Tests 2D" begin

    domain = (0,1,0,1)
    cell_nums = (10,10)
    model = CartesianDiscreteModel(domain,cell_nums) |> simplexify
    Ω = Triangulation(model)
    Γ = BoundaryTriangulation(model)
    Λ = SkeletonTriangulation(model)
    n_Λ = get_normal_vector(Λ)
    reffe = ReferenceFE(lagrangian,Float64,1)
    V = FESpace(model,reffe)
    uh = interpolate(x->sin(π*(x[1]+x[2])),V)
    celldata = rand(num_cells(Ω))

    @test savefig("2d_Fig1") do
        fig = plot(Ω)
        wireframe!(Ω, linestyle=:dash, color=:black)
        scatter!(Ω, markersize=10, marker=:diamond, color=:brown2)
        fig
    end
    @test savefig("2d_Fig11") do
        fig, _ , sc = plot(Ω, uh)
        Colorbar(fig[1,2], sc)
        fig
    end
    @test savefig("2d_Fig111") do
        fig,_,sc = plot(Γ, uh, colormap=:algae, linewidth=10)
        Colorbar(fig[1,2],sc)
        fig
    end
    @test savefig("2d_Fig12") do
        fig = plot(Ω, color=:green)
        wireframe!(Ω, color=:red, linewidth=2.5)
        fig
    end
    @test savefig("2d_Fig13") do
        fig, _ , plt = plot(Ω, color=3*celldata, colormap=:heat)
        Colorbar(fig[1,2], plt)
        fig
    end
    @test savefig("2d_Fig14") do
        fig, _ , plt = plot(Λ, jump(n_Λ⋅∇(uh)),linewidth=4)
        Colorbar(fig[1,2], plt)
        fig
    end
    @test savefig("2d_fig15") do
        fig, _ , plt = plot(uh, colormap=:Spectral)
        Colorbar(fig[1,2], plt)
        fig
    end
end

@testset "Tests 3D" begin

    domain = (0,1,0,1,0,1)
    cell_nums = (10,10,10)
    model = CartesianDiscreteModel(domain,cell_nums) |> simplexify
    Ω = Triangulation(model)
    Γ = BoundaryTriangulation(model)
    Λ = SkeletonTriangulation(model)
    n_Λ = get_normal_vector(Λ)
    reffe = ReferenceFE(lagrangian,Float64,1)
    V = FESpace(model,reffe)
    uh = interpolate(x->sin(π*(x[1]+x[2]+x[3])),V)
    celldata = rand(num_cells(Ω))

    @test savefig("3d_Fig1") do
        fig = plot(Ω)
        wireframe!(Ω)
        scatter!(Ω)
        fig
    end
    @test savefig("3d_Fig11") do
        fig, _ , sc = plot(Ω, uh, colorrange=(0,1))
        Colorbar(fig[1,2],sc)
        fig
    end
    @test savefig("3d_Fig111") do
        fig, _ , sc = plot(Γ, uh, colormap=:algae)
        Colorbar(fig[1,2],sc)
        fig
    end
    @test savefig("3d_Fig12") do
        fig = plot(Ω, color=:green)
        wireframe!(Ω, color=:red, linewidth=2.5)
        fig
    end
    @test savefig("3d_Fig13") do
      fig, _ , plt = plot(Ω, color=3*celldata, colormap=:heat)
        Colorbar(fig[1,2], plt)
        fig
    end
    @test savefig("3d_Fig14") do
        fig, _ , plt = plot(Λ, jump(n_Λ⋅∇(uh)))
        Colorbar(fig[1,2],plt)
        fig
    end
    @test savefig("3d_fig15") do
      fig, _ , plt = plot(uh, colormap=:Spectral, colorrange=(0,1))
        Colorbar(fig[1,2], plt)
        fig
    end
end

end #module


text:
using Nextion
using Test

@testset "raw_cmd_valid" begin

    nexSerial = NexSerial("/dev/ttyUSB0")

    send(nexSerial, "page 0")

    send(nexSerial, "cls WHITE")
    # send(nexSerial, "rest")

    sleep(0.5)

    send(nexSerial, "t0.txt=\"Hello\"")

    nexSerial.backlight = Backlight(50)
    #@test nexSerial.backlight == 50  #

    #send(nexSerial, "dim=1")  # 1% backlight
    #send(nexSerial, "dim=100")  # 100% backlight
    # dim / dims (s=save)

    sleep(0.5)

    send(nexSerial, "t0.txt=\"1\"")

    sleep(0.5)

    send(nexSerial, "t0.txt=\"2\"")

    sleep(0.5)

    send(nexSerial, "t0.txt=\"3\"")

    sleep(0.5)

    nexSerial.backlight = Backlight(100)
    send(nexSerial, "t0.txt=\"Bye\"")

    close(nexSerial)

    @test true

end


text:
module ADMMModule

import Base: iterate
using ..ObjectAgentModule
using ..ConvAnalysisModule
using Printf

export admm

mutable struct ADMMIterable{T<:Real}
    λ::T;
    ρ::T;
    agents::Array{<:ObjectAgent, 1};
    method::Symbol; # either :freedir or :fixdir
    maxiter::Int;
    elapsed::T;

    function ADMMIterable(λ::T, ρ::T, agents::Array{<:ObjectAgent, 1}, method::Symbol, maxiter::Int) where T<:Real
        @assert (method == :fixdir || method == :freedir) ":fixdir and :freedir are the only currently supported methods"
        new{T}(λ, ρ, agents, method, maxiter, 0)
    end
end

function iterate(it::ADMMIterable{<:Real}, iteration::Int=0)
    if iteration >= it.maxiter return nothing end

    it.elapsed += @elapsed begin
        Threads.@threads for k = 1:length(it.agents)
            if it.method == :freedir
                updatex!(it.agents[k], it.λ);
            else
                updatex_dirFixed!(it.agents[k], it.λ);
            end
        end
        for agent in it.agents
            broadcastx!(agent);
        end
        for agent in it.agents
            updateu!(agent, it.ρ);
        end
    end

    return (iteration, iteration+1);
end

function admm(agents::Array{<:ObjectAgent, 1};
    λ::T=1.0, ρ::T=1/λ,
    method::Symbol = :fixdir,
    log::Bool = false,
    verbose::Bool = false,
    maxiter::Int = 25,
    stoping_criteria = x -> false,
    ) where T<:Real

    if log
         history = ConvAnalysis_Data();
    end

    admm_it = ADMMIterable(λ, ρ, agents, method, maxiter);

    for (iteration, item) = enumerate(admm_it)
        if log
            push!(history, agents);
        end

        if verbose
            verbose && @printf("%3d\n", iteration)
        end

        if iteration > 1 && stoping_criteria(history)
            break;
        end
    end

    log ? (admm_it.elapsed, history) : (admm_it.elapsed, nothing)
end

end


text:
# # PointNet Classification
# Classification of PointCloud structure using PointNet model
#
# !!!note
#     For visualization purpose we will require to install Makie and
#     compatible backend (GLMakie or WGLMakie). To install it simply run
#     `] add AbstractPlotting GLMakie` in the julia prompt.

using Flux3D, Flux, Makie, CUDA
using Flux: onehotbatch, onecold, onehot, crossentropy
using Statistics: mean
using Base.Iterators: partition

Makie.AbstractPlotting.inline!(false)
Makie.AbstractPlotting.set_theme!(show_axis = false)

# ## Defining arguments for use in training.
# * batch_size - batch size of of training data to be passed while training.
# * lr - learing rate for the optimization.
# * epochs - number of episodes for training the classification model.
# * num_classes - number of classes in labels of dataset.
# * npoints - number of points in each PointCloud to be returned by dataset.

batch_size = 32
lr = 3e-4
epochs = 5
num_classes = 10 #possible values {10,40}
npoints = 1024

# ## ModelNet10 Dataset
# This package has dataset wrapper for ModelNet10/40 which makes it easy to load
# and preprocess ModelNet dataset. In this example we will using ModelNet10
# but we can also use ModelNet40 with minor tweak in num_classes args.
#
# We can construct ModelNet10 dataset by passing:
# * `mode=:pointcloud` - for returning PointCloud variant of dataset
# * `npoints` - no. of points in each PointCloud.
# * `transforms`  - Transforms to be applied before return specified PointCloud.
# * `train`   - Bool to indicate training or testing split.
#
# Detailed list of available arguments can be found in ModelNet section.

dset = ModelNet10.dataset(;
    mode = :pointcloud,
    npoints = npoints,
    transform = NormalizePointCloud(),
)
val_dset = ModelNet10.dataset(;
    mode = :pointcloud,
    train = false,
    npoints = npoints,
    transform = NormalizePointCloud(),
)

# ## Visualizing the dataset
# we can access the dataset by correspond index like `dset[1]` which will return
# a `ModelNet10 DataPoint` and following information
# `idx: 1, data: PointCloud{Float32}, ground_truth: 1 (bathtub)`.
#
# For the visulizing the corresponding datapoint we can use `visulize`

visualize(dset[11], markersize = 0.1)

# ```@raw html
# <p align="center">
#     <img width=256 height=256 src="../../assets/chair.png">
# </p>
# ```

# ## Preparing Dataloader for training.

data = [dset[i].data.points for i = 1:length(dset)]
labels = onehotbatch([dset[i].ground_truth for i = 1:length(dset)], 1:num_classes)

valX = cat([val_dset[i].data.points for i = 1:length(val_dset)]..., dims = 3)
valY = onehotbatch([val_dset[i].ground_truth for i = 1:length(val_dset)], 1:num_classes)

TRAIN = [
    (cat(data[i]..., dims = 3), labels[:, i]) for i in partition(1:length(data), batch_size)
]
VAL = (valX, valY)

# ## Defining 3D model
# Flux3D has predefined PointNet classification model which can be used to train
# PointCloud dataset

m = PointNet(num_classes)

# ## Defining loss and validating objectives

loss(x, y) = crossentropy(m(x), y)
accuracy(x, y) = mean(onecold(cpu(m(x)), 1:num_classes) .== onecold(cpu(y), 1:num_classes))

# ## Defining learning rate and optimizer
opt = Flux.ADAM(lr)

# ## Using GPU for fast training [**Optional**]
# We can convert the 3D model to GPU or CPU using`gpu` and `cpu`,
# and also changing the dataloader using same function

m = m |> gpu
TRAIN = TRAIN |> gpu
VAL = VAL |> gpu

# ## Training the 3D model

ps = params(m)
for epoch = 1:epochs
    running_loss = 0
    for d in TRAIN
        gs = gradient(ps) do
            training_loss = loss(d...)
            running_loss += training_loss
            return training_loss
        end
        Flux.update!(opt, ps, gs)
    end
    print("Epoch: $(epoch), epoch_loss: $(running_loss), accuracy: $(accuracy(VAL...))\n")
end
@show accuracy(VAL...)


text:
const DS = DataStructure101

export Heap, MaxHeap, MinHeap
export level, root, parent, left_child, right_child, heapify!, build!

abstract type Heap end

mutable struct MaxHeap <: Heap
    data::Vector{Int64}
    heapified::Bool

    MaxHeap() = new(Int64[], true)
end

mutable struct MinHeap <: Heap
    data::Vector{Int64}
    heapified::Bool

    MinHeap() = new(Int64[], true)
end

Base.getindex(heap::Heap, i::Int64) = heap.data[i]

Base.setindex!(heap::Heap, v::Int64, i::Int64) = (heap.data[i] = v)

Base.length(heap::Heap) = length(heap.data)

level(heap::Heap, i::Int64) = floor(Int, log2(i)) + 1

root(heap::Heap) = 1

parent(heap::Heap, i::Int64) = (i > length(heap) || i < 1) ? -1 : floor(Int, i/2)

left_child(heap::Heap, i::Int64) = 2 * i

right_child(heap::Heap, i::Int64) = 2 * i + 1

heapified(heap::Heap) = heap.heapified

function tree_repr(heap::Heap, i::Int64, n::Int64; tree_str::String="", level::Int64=0)
    right_index = right_child(heap, i)
    left_index = left_child(heap, i)
    length = Base.length(heap)

    (right_index <= n) && (tree_str = tree_repr(
        heap,
        right_index,
        length,
        tree_str=tree_str,
        level=level+1
    ))

    tree_str = "$(tree_str)\n$("\t"^level)HeapNode($(heap[i]))"

    (left_index <= n) && (tree_str = tree_repr(
        heap,
        left_index,
        length,
        tree_str=tree_str,
        level=level+1
    ))

    return tree_str
end

function Base.show(io::IO, heap::Heap)
    print(io, "Heap(\n$(tree_repr(heap, root(heap), length(heap)))\n)")
end

function heapify!(heap::MaxHeap, i::Int64, n::Int64)
    largest_index = i
    right_index = right_child(heap, i)
    left_index = left_child(heap, i)

    (right_index <= n && heap[i] < heap[right_index]) &&
        (largest_index = right_index)
    (left_index <= n && heap[largest_index] < heap[left_index]) &&
        (largest_index = left_index)

    if largest_index != i
        heap[i], heap[largest_index] = heap[largest_index], heap[i]
        heapify!(heap, largest_index, n)
    end
end

function heapify!(heap::MinHeap, i::Int64, n::Int64)
    smallest_index = i
    right_index = right_child(heap, i)
    left_index = left_child(heap, i)

    (right_index <= n && heap[i] > heap[right_index]) &&
        (smallest_index = right_index)
    (left_index <= n && heap[smallest_index] > heap[left_index]) &&
        (smallest_index = left_index)

    if smallest_index != i
        heap[i], heap[smallest_index] = heap[smallest_index], heap[i]
        heapify!(heap, smallest_index, n)
    end
end

function build!(heap::Heap)
    heapified(heap) && return

    len = length(heap)
    p = parent(heap, len)
    for i = p:-1:1
        heapify!(heap, i, len)
    end

    heap.heapified = true
end

function Base.push!(heap::Heap, v::Int64)
    push!(heap.data, v)
    heap.heapified = false
end

function Base.pop!(heap::Heap)
    !heapified(heap) && throw("Not Heap")

    len = length(heap)
    heap[1], heap[len] = heap[len], heap[1]
    value = pop!(heap.data)
    heapify!(heap, 1, len-1)

    return value
end

function Base.sort!(heap::Heap)
    !heapified(heap) && throw("Not Heap")

    sorted = Int64[]
    len = length(heap)
    for i=1:len
        push!(sorted, pop!(heap))
    end

    return sorted
end


text:
"""
    RNNCell(in_dims => out_dims, activation=tanh; bias::Bool=true, init_bias=zeros32, init_weight=glorot_uniform, init_state=ones32)

An Elman RNNCell cell with `activation` (typically set to `tanh` or `relu`).

``h_{new} = activation(weight_{ih} \\times x + weight_{hh} \\times h_{prev} + bias)``

## Arguments

* `in_dims`: Input Dimension
* `out_dims`: Output (Hidden State) Dimension
* `activation`: Activation function
* `bias`: Set to false to deactivate bias
* `init_bias`: Initializer for bias
* `init_weight`: Initializer for weight
* `init_state`: Initializer for hidden state

## Inputs

* Case 1: Only a single input `x` of shape `(in_dims, batch_size)` - Creates a hidden state using `init_state` and proceeds to Case 2.
* Case 2: Tuple (`x`, `h`) is provided, then the updated hidden state is returned.

## Returns

* New hidden state ``h_{new}`` of shape `(out_dims, batch_size)`
* Updated model state

## Parameters

* `weight_ih`: Maps the input to the hidden state.
* `weight_hh`: Maps the hidden state to the hidden state.
* `bias`: Bias vector (not present if `bias=false`)

## States

* `rng`: Controls the randomness (if any) in the initial state generation
"""
struct RNNCell{bias, A, B, W, S} <: AbstractExplicitLayer
    activation::A
    in_dims::Int
    out_dims::Int
    init_bias::B
    init_weight::W
    init_state::S
end

function RNNCell((in_dims, out_dims)::Pair{<:Int, <:Int},
                 activation=tanh;
                 bias::Bool=true,
                 init_bias=zeros32,
                 init_weight=glorot_uniform,
                 init_state=ones32)
    return RNNCell{bias, typeof(activation), typeof(init_bias), typeof(init_weight),
                   typeof(init_state)}(activation, in_dims, out_dims, init_bias,
                                       init_weight, init_state)
end

function initialparameters(rng::AbstractRNG, rnn::RNNCell{bias}) where {bias}
    ps = (weight_ih=rnn.init_weight(rng, rnn.out_dims, rnn.in_dims),
          weight_hh=rnn.init_weight(rng, rnn.out_dims, rnn.out_dims))
    if bias
        ps = merge(ps, (bias=rnn.init_bias(rng, rnn.out_dims),))
    end
    return ps
end

function initialstates(rng::AbstractRNG, ::RNNCell)
    # FIXME: Take PRNGs seriously
    randn(rng, 1)
    return (rng=replicate(rng),)
end

function (rnn::RNNCell)(x::AbstractMatrix, ps::Union{ComponentArray, NamedTuple},
                        st::NamedTuple)
    rng = replicate(st.rng)
    @set! st.rng = rng
    hidden_state = rnn.init_state(rng, rnn.out_dims, size(x, 2))
    return rnn((x, hidden_state), ps, st)
end

function (rnn::RNNCell{true})((x, hidden_state)::Tuple{<:AbstractMatrix, <:AbstractMatrix},
                              ps::Union{ComponentArray, NamedTuple}, st::NamedTuple)
    h_new = rnn.activation.(ps.weight_ih * x .+ ps.weight_hh * hidden_state .+ ps.bias)
    return h_new, st
end

function (rnn::RNNCell{true, typeof(identity)})((x,
                                                 hidden_state)::Tuple{<:AbstractMatrix,
                                                                      <:AbstractMatrix},
                                                ps::Union{ComponentArray, NamedTuple},
                                                st::NamedTuple)
    h_new = ps.weight_ih * x .+ ps.weight_hh * hidden_state .+ ps.bias
    return h_new, st
end

function (rnn::RNNCell{false})((x, hidden_state)::Tuple{<:AbstractMatrix, <:AbstractMatrix},
                               ps::Union{ComponentArray, NamedTuple}, st::NamedTuple)
    h_new = rnn.activation.(ps.weight_ih * x .+ ps.weight_hh * hidden_state)
    return h_new, st
end

function (rnn::RNNCell{false, typeof(identity)})((x,
                                                  hidden_state)::Tuple{<:AbstractMatrix,
                                                                       <:AbstractMatrix},
                                                 ps::Union{ComponentArray, NamedTuple},
                                                 st::NamedTuple)
    h_new = ps.weight_ih * x .+ ps.weight_hh * hidden_state
    return h_new, st
end

function Base.show(io::IO, r::RNNCell{bias}) where {bias}
    print(io, "RNNCell($(r.in_dims) => $(r.out_dims)")
    (r.activation == identity) || print(io, ", $(r.activation)")
    bias || print(io, ", bias=false")
    return print(io, ")")
end

"""
    LSTMCell(in_dims => out_dims; init_weight=(glorot_uniform, glorot_uniform, glorot_uniform, glorot_uniform), init_bias=(zeros32, zeros32, ones32, zeros32), init_state=zeros32)

Long Short-Term (LSTM) Cell

``i = \\sigma(W_{ii} \\times x + W_{hi} \\times h_{prev} + b_{i})``

``f = \\sigma(W_{if} \\times x + W_{hf} \\times h_{prev} + b_{f})``

``g = tanh(W_{ig} \\times x + W_{hg} \\times h_{prev} + b_{g})``

``o = \\sigma(W_{io} \\times x + W_{ho} \\times h_{prev} + b_{o})``

``c_{new} = f \\cdot c_{prev} + i \\cdot g``

``h_{new} = o \\cdot tanh(c_{new})``

## Arguments

* `in_dims`: Input Dimension
* `out_dims`: Output (Hidden State & Memory) Dimension
* `init_bias`: Initializer for bias. Must be a tuple containing 4 functions
* `init_weight`: Initializer for weight. Must be a tuple containing 4 functions
* `init_state`: Initializer for hidden state and memory

## Inputs

* Case 1: Only a single input `x` of shape `(in_dims, batch_size)` - Creates a hidden state and memory using `init_state` and proceeds to Case 2.
* Case 2: Tuple (`x`, `h`, `c`) is provided, then the updated hidden state and memory is returned.

## Returns

* Tuple Containing
    * New hidden state ``h_{new}`` of shape `(out_dims, batch_size)`
    * Updated Memory ``c_{new}`` of shape `(out_dims, batch_size)`
* Updated model state

## Parameters

* `weight_i`: Concatenated Weights to map from input space ``\\left\\{ W_{ii}, W_{if}, W_{ig}, W_{io} \\right\\}``.
* `weight_h`: Concatenated Weights to map from hidden space ``\\left\\{ W_{hi}, W_{hf}, W_{hg}, W_{ho} \\right\\}``
* `bias`: Bias vector

## States

* `rng`: Controls the randomness (if any) in the initial state generation
"""
struct LSTMCell{B, W, S} <: AbstractExplicitLayer
    in_dims::Int
    out_dims::Int
    init_bias::B
    init_weight::W
    init_state::S
end

function LSTMCell((in_dims, out_dims)::Pair{<:Int, <:Int};
                  init_weight::Tuple{Function, Function, Function, Function}=(glorot_uniform,
                                                                              glorot_uniform,
                                                                              glorot_uniform,
                                                                              glorot_uniform),
                  init_bias::Tuple{Function, Function, Function, Function}=(zeros32,
                                                                            zeros32,
                                                                            ones32,
                                                                            zeros32),
                  init_state::Function=zeros32)
    return LSTMCell(in_dims, out_dims, init_bias, init_weight, init_state)
end

function initialparameters(rng::AbstractRNG, lstm::LSTMCell)
    weight_i = vcat([init_weight(rng, lstm.out_dims, lstm.in_dims)
                     for init_weight in lstm.init_weight]...)
    weight_h = vcat([init_weight(rng, lstm.out_dims, lstm.out_dims)
                     for init_weight in lstm.init_weight]...)
    bias = vcat([init_bias(rng, lstm.out_dims, 1) for init_bias in lstm.init_bias]...)
    return (weight_i=weight_i, weight_h=weight_h, bias=bias)
end

function initialstates(rng::AbstractRNG, ::LSTMCell)
    # FIXME: Take PRNGs seriously
    randn(rng, 1)
    return (rng=replicate(rng),)
end

function (lstm::LSTMCell)(x::AbstractMatrix, ps::Union{ComponentArray, NamedTuple},
                          st::NamedTuple)
    rng = replicate(st.rng)
    @set! st.rng = rng
    hidden_state = lstm.init_state(rng, lstm.out_dims, size(x, 2))
    memory = lstm.init_state(rng, lstm.out_dims, size(x, 2))
    return lstm((x, hidden_state, memory), ps, st)
end

function (lstm::LSTMCell)((x, hidden_state,
                           memory)::Tuple{<:AbstractMatrix, <:AbstractMatrix,
                                          <:AbstractMatrix},
                          ps::Union{ComponentArray, NamedTuple},
                          st::NamedTuple)
    g = ps.weight_i * x .+ ps.weight_h * hidden_state .+ ps.bias
    input, forget, cell, output = multigate(g, Val(4))
    memory_new = @. sigmoid_fast(forget) * memory + sigmoid_fast(input) * tanh_fast(cell)
    hidden_state_new = @. sigmoid_fast(output) * tanh_fast(memory_new)
    return (hidden_state_new, memory_new), st
end

Base.show(io::IO, l::LSTMCell) = print(io, "LSTMCell($(l.in_dims) => $(l.out_dims))")

"""
    GRUCell((in_dims, out_dims)::Pair{<:Int,<:Int}; init_weight::Tuple{Function,Function,Function}=(glorot_uniform, glorot_uniform, glorot_uniform), init_bias::Tuple{Function,Function,Function}=(zeros32, zeros32, zeros32), init_state::Function=zeros32)

Gated Recurrent Unit (GRU) Cell

``r = \\sigma(W_{ir} \\times x + W_{hr} \\times h_{prev} + b_{hr})``

``z = \\sigma(W_{iz} \\times x + W_{hz} \\times h_{prev} + b_{hz})``

``n = \\sigma(W_{in} \\times x + b_{in} + r \\cdot (W_{hn} \\times h_{prev} + b_{hn}))``

``h_{new} = (1 - z) \\cdot n + z \\cdot h_{prev}``

## Arguments

* `in_dims`: Input Dimension
* `out_dims`: Output (Hidden State) Dimension
* `init_bias`: Initializer for bias. Must be a tuple containing 3 functions
* `init_weight`: Initializer for weight. Must be a tuple containing 3 functions
* `init_state`: Initializer for hidden state

## Inputs

* Case 1: Only a single input `x` of shape `(in_dims, batch_size)` - Creates a hidden state using `init_state` and proceeds to Case 2.
* Case 2: Tuple (`x`, `h`) is provided, then the updated hidden state is returned.

## Returns

* New hidden state ``h_{new}`` of shape `(out_dims, batch_size)`
* Updated model state

## Parameters

* `weight_i`: Concatenated Weights to map from input space ``\\left\\{ W_{ir}, W_{iz}, W_{in} \\right\\}``.
* `weight_h`: Concatenated Weights to map from hidden space ``\\left\\{ W_{hr}, W_{hz}, W_{hn} \\right\\}``
* `bias_i`: Bias vector (``b_{in}``)
* `bias_h`: Concatenated Bias vector for the hidden space ``\\left\\{ b_{hr}, b_{hz}, b_{hn} \\right\\}``

## States

* `rng`: Controls the randomness (if any) in the initial state generation
"""
struct GRUCell{W, B, S} <: AbstractExplicitLayer
    in_dims::Int
    out_dims::Int
    init_weight::W
    init_bias::B
    init_state::S
end

function GRUCell((in_dims, out_dims)::Pair{<:Int, <:Int};
                 init_weight::Tuple{Function, Function, Function}=(glorot_uniform,
                                                                   glorot_uniform,
                                                                   glorot_uniform),
                 init_bias::Tuple{Function, Function, Function}=(zeros32, zeros32,
                                                                 zeros32),
                 init_state::Function=zeros32)
    return GRUCell(in_dims, out_dims, init_weight, init_bias, init_state)
end

function initialparameters(rng::AbstractRNG, gru::GRUCell)
    weight_i = vcat([init_weight(rng, gru.out_dims, gru.in_dims)
                     for init_weight in gru.init_weight]...)
    weight_h = vcat([init_weight(rng, gru.out_dims, gru.out_dims)
                     for init_weight in gru.init_weight]...)
    bias_i = gru.init_bias[1](rng, gru.out_dims, 1)
    bias_h = vcat([init_bias(rng, gru.out_dims, 1) for init_bias in gru.init_bias]...)
    return (weight_i=weight_i, weight_h=weight_h, bias_i=bias_i, bias_h=bias_h)
end

function initialstates(rng::AbstractRNG, ::GRUCell)
    # FIXME: Take PRNGs seriously
    randn(rng, 1)
    return (rng=replicate(rng),)
end

function (gru::GRUCell)(x::AbstractMatrix, ps::Union{ComponentArray, NamedTuple},
                        st::NamedTuple)
    rng = replicate(st.rng)
    @set! st.rng = rng
    hidden_state = gru.init_state(rng, gru.out_dims, size(x, 2))
    return gru((x, hidden_state), ps, st)
end

function (gru::GRUCell)((x, hidden_state)::Tuple{<:AbstractMatrix, <:AbstractMatrix},
                        ps::Union{ComponentArray, NamedTuple}, st::NamedTuple)
    gxs = multigate(ps.weight_i * x, Val(3))
    ghbs = multigate(ps.weight_h * hidden_state .+ ps.bias_h, Val(3))

    r = @. sigmoid_fast(gxs[1] + ghbs[1])
    z = @. sigmoid_fast(gxs[2] + ghbs[2])
    n = @. tanh_fast(gxs[3] + ps.bias_i + r * ghbs[3])
    hidden_state_new = @. (1 - z) * n + z * hidden_state

    return hidden_state_new, st
end

Base.show(io::IO, g::GRUCell) = print(io, "GRUCell($(g.in_dims) => $(g.out_dims))")


text:
using URIParser
using NumericalDataManipulation.DataIO.FileSystem
using NumericalDataManipulation.Interpolation
using NumericalDataManipulation.Data

function load_numerical_table(resource::FileResource; has_title::Bool = false)
    @assert(exists(resource), "The resource $resource does not exist.")
    lines_to_skip = if has_title 1 else 0 end
    datatable = load_datatable(resource.file_path; skip = lines_to_skip)
    numerical_table = NumericalTable(datatable)
    return numerical_table
end

function save_numerical_table(resource::FileResource, table::NumericalTable)
end


text:
function get_offsets(obj::UtcTaiOffsetLoader)
    return jcall(obj, "getOffsets", List, ())
end

function load_data(obj::UtcTaiOffsetLoader, arg0::InputStream, arg1::JString)
    return jcall(obj, "loadData", void, (InputStream, JString), arg0, arg1)
end

function still_accepts_data(obj::UtcTaiOffsetLoader)
    return jcall(obj, "stillAcceptsData", jboolean, ())
end


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

@doc raw"""DEPRECATED.

    IoK8sApiExtensionsV1beta1RollbackConfig(;
        revision=nothing,
    )

    - revision::Int64 : The revision to rollback to. If set to 0, rollback to the last revision.
"""
mutable struct IoK8sApiExtensionsV1beta1RollbackConfig <: SwaggerModel
    revision::Any # spec type: Union{ Nothing, Int64 } # spec name: revision

    function IoK8sApiExtensionsV1beta1RollbackConfig(;revision=nothing)
        o = new()
        validate_property(IoK8sApiExtensionsV1beta1RollbackConfig, Symbol("revision"), revision)
        setfield!(o, Symbol("revision"), revision)
        o
    end
end # type IoK8sApiExtensionsV1beta1RollbackConfig

const _property_map_IoK8sApiExtensionsV1beta1RollbackConfig = Dict{Symbol,Symbol}(Symbol("revision")=>Symbol("revision"))
const _property_types_IoK8sApiExtensionsV1beta1RollbackConfig = Dict{Symbol,String}(Symbol("revision")=>"Int64")
Base.propertynames(::Type{ IoK8sApiExtensionsV1beta1RollbackConfig }) = collect(keys(_property_map_IoK8sApiExtensionsV1beta1RollbackConfig))
Swagger.property_type(::Type{ IoK8sApiExtensionsV1beta1RollbackConfig }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_IoK8sApiExtensionsV1beta1RollbackConfig[name]))}
Swagger.field_name(::Type{ IoK8sApiExtensionsV1beta1RollbackConfig }, property_name::Symbol) =  _property_map_IoK8sApiExtensionsV1beta1RollbackConfig[property_name]

function check_required(o::IoK8sApiExtensionsV1beta1RollbackConfig)
    true
end

function validate_property(::Type{ IoK8sApiExtensionsV1beta1RollbackConfig }, name::Symbol, val)
end


text:
using LinearAlgebra
using Statistics
using Random

function abs_val(d)
	return abs(d)
end

function obj(X, y, w, lam, tau)
    n, ncol = size(X);
    pen = 0;
    obj_fun = 0;
    r = y - X * w;
    rw = zeros(n);
    for q_i in 1:n
        if r[q_i] < 0
            rw[q_i] = r[q_i] * r[q_i] * (1 - tau);
        else
            rw[q_i] = r[q_i] * r[q_i] * tau;
        end
    end
    pen = lam * sum(broadcast(abs, w));
    obj_fun = sum(rw) + pen;
    return obj_fun;
end

function median_index(vector)
    med = median(vector);
    abs_demed_vector = broadcast(abs, vector .- med);
    idx = argmin(abs_demed_vector);
    return idx;
end

function buckets(K_buckets, nrow)
    tmp_buckets_id = range(0, stop=K_buckets-1, length=K_buckets);
    tmp2buckets_id = repeat(tmp_buckets_id, 1, Int(ceil(nrow/K_buckets)));
    buckets_id = sort(vec(tmp2buckets_id)[1:nrow]);
    return buckets_id;
end

function cvfolds(nfolds, nrow)
    tmp_foldsid = range(0, stop=nfolds-1, length=nfolds);
    tmp2_foldsid = repeat(tmp_foldsid, 1, Int(ceil(nrow/nfolds)));
    foldsid = sort(vec(tmp2_foldsid)[1:nrow]);
    return foldsid;
end

function block_X(X, which_idx)
    return X[which_idx,:];
end

function block_y(y, which_idx)
    return y[which_idx];
end

function mom_obj(X, y, w, w_prime, lam, tau)
    loss = obj(X, y, w, lam, tau);
    loss_prime = obj(X, y, w_prime, lam, tau);
    loss_mom = loss - loss_prime;
    return loss_mom;
end

function f_grad(X, y, w, tau)
    n, ncol = size(X);
    r = y - X * w;
    grad_tmp = zeros(n);
    for q_i in 1:n
        if r[q_i] < 0
            grad_tmp[q_i] = 2 * r[q_i] * (1 - tau);
        else
            grad_tmp[q_i] = 2 * r[q_i] * tau;
        end
    end
    grad = -transpose(X) * grad_tmp;
    return grad;
end

function block(X, y, w, w_prime, K, tau, is_random)
	vect_means = zeros(K);
	n = size(y)[1];
	buckets_id = buckets(K, n);
	if is_random == true
		buckets_id = shuffle(buckets_id);
	end
	for k in 1:K
		Xk = X[findall(x -> x==(k-1), buckets_id), :];
		yk = y[findall(x -> x==(k-1), buckets_id)];
		excess_loss_k = obj(Xk, yk, w, 0, tau) - obj(Xk, yk, w_prime, 0, tau);
		vect_means[k] = excess_loss_k;
	end
	idx = median_index(vect_means);
	which_idx = findall(x -> x==(idx-1), buckets_id);
	return which_idx;
end

function block_cv(X_test, y_test, w, K_prime, tau)
	vect_means = zeros(K_prime);
	n = size(y_test)[1];
	buckets_id = buckets(K_prime, n);
	for k in 1:K_prime
		Xk = X_test[findall(x -> x==(k-1), buckets_id), :];
		yk = y_test[findall(x -> x==(k-1), buckets_id)];
		excess_loss_k = obj(Xk, yk, w, 0, tau);
		vect_means[k] = excess_loss_k;
	end
  idx = median_index(vect_means);
  which_idx = findall(x -> x==(idx-1), buckets_id);
  return which_idx;
end

function soft_threshold(w, mu)
	p = size(w)[1];
	max_tmp = zeros(p);
	for i in 1:p
		if abs_val(w[i]) > mu
			max_tmp[i] = abs_val(w[i]) - mu;
		else
			max_tmp[i] = 0.0;
		end
	end
	st = sign.(w) .* max_tmp;
	return st;
end

function pg_als(X, y, beta_0, tau, max_iter, lam)
	w = beta_0;
	w_prev = w;
	obj_pg_als = zeros(max_iter+1);
	tmp = obj(X, y, w, lam, tau);
	obj_pg_als[1] = tmp;

	beta = 1.2;
	delta = 1.0;
	LL = 1.0;
	for j in 1:max_iter
		w_prev = w;
		delta = 1.0;
		obj_val = obj(X, y, w, lam, tau);
		while (delta > 0.001)
			gamma = 1.0 / LL;
			w = w_prev - gamma * f_grad(X, y, w_prev, tau);
			w = soft_threshold(w, lam * gamma);
			obj_val_new = obj(X, y, w, lam, tau);
			f_grad_w = sum(transpose(f_grad(X, y, w_prev, tau)) * (w - w_prev));
			delta = obj_val_new - obj_val - f_grad_w - (LL / 2) * norm(w-w_prev, 2) ^ 2;
			LL = LL * beta;
		end
		LL = LL / beta;
		tmp = obj(X, y, w, lam, tau);
		obj_pg_als[j+1] = tmp;
	end
	out = Dict("fitted_values" => obj_pg_als, "beta" => w);
	return out;
end

function pg_mom_als(X, y, beta_0, tau, K, max_iter, lam, epsil, is_random)
	w = beta_0;
	w_prime = beta_0;
	mom_obj_pg_als = zeros(max_iter+1);
	tmp = mom_obj(X, y, w, w_prime, lam, tau);
	mom_obj_pg_als[1] = tmp;
	beta = 1.2;
	stopping = false;
	counter = 0.0;
	tmp_obj = 0.0;
	tmp_obj_prev = 0.0;
	while (counter < max_iter && stopping == false)
		counter = counter + 1.0;
		LL = 1.0;
		w_prev = w;
		delta = ones(1);
		idx_k = block(X, y, w, w_prime, K, tau, is_random);
		Xk = block_X(X, idx_k);
		yk = block_y(y, idx_k);
		obj_val = obj(Xk, yk, w, lam, tau);
		while (delta[1] > 0.001)
			gamma = 1 / LL;
			w = w_prev - gamma * f_grad(Xk, yk, w_prev, tau);
			w = soft_threshold(w, lam * gamma);
			delta = obj(Xk, yk, w, lam, tau) - obj_val - transpose(Array(f_grad(Xk, yk, w_prev, tau))) * (w - w_prev) - (LL / 2) * norm(w-w_prev, 2) ^ 2;
			LL = LL * beta;
		end
		LL_prime = 1.0;
		w_prime_prev = w_prime;
		delta_prime = ones(1);
		idx_k = block(X, y, w, w_prime, K, tau, is_random);
		Xk = block_X(X, idx_k);
		yk = block_y(y, idx_k);
		obj_val = obj(Xk, yk, w_prime, lam, tau);
		while (delta_prime[1] > 0.001)
			gamma = 1 / LL_prime;
			w_prime = w_prime_prev - gamma * f_grad(Xk, yk, w_prime_prev, tau);
			w_prime = soft_threshold(w_prime, lam * gamma);
			delta_prime = obj(Xk, yk, w_prime, lam, tau) - obj_val - transpose(Array(f_grad(Xk, yk, w_prime_prev, tau))) * (w_prime - w_prime_prev) - (LL_prime / 2) * norm(w_prime - w_prime_prev, 2) ^ 2;
			LL_prime = LL_prime * beta;
		end
		tmp_obj_prev = tmp_obj;
		tmp_obj = mom_obj(X, y, w, w_prime, lam, tau);
		mom_obj_pg_als[Int(counter)+1] = tmp_obj;
		#if (abs_val(tmp_obj) <= epsil)
		#	if (abs_val(tmp_obj) + abs_val(tmp_obj_prev) <= 2 * epsil)
		#		stopping = true;
		#		counter = counter + 1.0;
		#	end
		#end
	end
	out = Dict("fitted_values" => mom_obj_pg_als, "beta" => w, "beta_prime" => w_prime, "counter" => counter, "stopping" => stopping);
	return out
end

function pg_mom_als_w(X, y, beta_0, tau, K, max_iter, lam, epsil, is_random)
	w = beta_0;
	w_prime = beta_0;
	mom_obj_pg_als = zeros(max_iter+1);
	tmp = mom_obj(X, y, w, w_prime, lam, tau);
	mom_obj_pg_als[1] = tmp;
	beta = 1.2;
    stopping = false;
	counter = 0.0;
	tmp_obj = 0.0;
	tmp_obj_prev = 0.0;
	while (counter < max_iter && stopping == false)
		counter = counter + 1.0;
		LL = 1.0;
		w_prev = w;
		delta = ones(1);
		idx_k = block(X, y, w, w_prime, K, tau, is_random);
		Xk = block_X(X, idx_k);
		yk = block_y(y, idx_k);
		obj_val = obj(Xk, yk, w, lam, tau);
		while (delta[1] > 0.001)
			gamma = 1 / LL;
			w = w_prev - gamma * f_grad(Xk, yk, w_prev, tau);
			w = soft_threshold(w, lam * gamma);
			delta = obj(Xk, yk, w, lam, tau) - obj_val - transpose(Array(f_grad(Xk, yk, w_prev, tau)))*(w - w_prev)-(LL/2)* norm(w-w_prev, 2) ^ 2;
			LL = LL*beta;
		end
		LL_prime = 1.0;
		w_prime_prev = w_prime;
		delta_prime = ones(1);
		idx_k = block(X, y, w, w_prime, K, tau, is_random);
		Xk = block_X(X, idx_k);
		yk = block_y(y, idx_k);
		obj_val = obj(Xk, yk, w_prime, lam, tau);
		while (delta_prime[1] > 0.001)
			gamma = 1 / LL_prime;
			w_prime = w_prime_prev - gamma * f_grad(Xk, yk, w_prime_prev, tau);
			w_prime = soft_threshold(w_prime, lam * gamma);
			delta_prime = obj(Xk, yk, w_prime, lam, tau) - obj_val - transpose(Array(f_grad(Xk, yk, w_prime_prev, tau)))*(w_prime - w_prime_prev)-(LL_prime/2)*norm(w_prime - w_prime_prev, 2) ^ 2;
			LL_prime = LL_prime*beta;
		end
		tmp_obj_prev = tmp_obj;
		tmp_obj = mom_obj(X, y, w, w_prime, lam, tau);
		mom_obj_pg_als[Int(counter)+1] = tmp_obj;
		if (abs_val(tmp_obj) <= epsil)
			if (abs_val(tmp_obj)+abs_val(tmp_obj_prev) <= 2*epsil)
				stopping = true;
				counter = counter + 1.0;
			end
		end
	end
	return w;
end

"""

X = [1 2 3; 4 5 6; 7 8 9];
y = [2, -4, 6];
w = [1, 2, 5];
w_prime = [1, 2, 3];
tau = 0.5;
lam = 2.0;
K = 3;
K_buckets = 3;
nrow = 10;
max_iter = 10;
epsil = 0.001;
is_random = false;

println(obj(X, y, w, lam, tau))
println(median_index(y));
println(buckets(K_buckets, nrow));
println(cvfolds(K_buckets, nrow));
println(block_X(X, [1,2]));
println(block_y(X, [1,2]));
println(mom_obj(X, y, w, w_prime, lam, tau));
println(f_grad(X, y, w, tau));
println(block(X, y, w, w_prime, K, tau, is_random))
println(pg_als(X, y, w, tau, max_iter, lam))
println(pg_mom_als(X, y, w, tau, K, max_iter, lam, epsil, is_random))
println(pg_mom_als_w(X, y, w, tau, K, max_iter, lam, epsil, is_random))

"""

max_stars_repo_name: jstriaukas/robustml

max_issues_repo_name: jstriaukas/robustml

max_forks_repo_name: jstriaukas/robustml


text:
using Documenter, ReverseDiff

makedocs(modules=[ReverseDiff])