text:
_dst_eltype(any, default) = default
_dst_eltype(::Type{T}, default) where {T<:Number} = T
_dst_eltype(::Type{Bool}, default) = default

@testset "convertlabelview binary" begin
    @test_throws MethodError convertlabelview(LabelEnc.MarginBased, [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.OneOfK(2), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.Indices(3), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.ZeroOne(), [1,2,3], LabelEnc.Indices(Int,3))
    @test_throws MethodError convertlabelview(LabelEnc.FuzzyBinary(), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.MarginBased(), Any[1.,0,1], LabelEnc.FuzzyBinary())
    @test_throws MethodError convertlabelview(LabelEnc.FuzzyBinary(), [1.,0,1], LabelEnc.ZeroOne())
    @test_throws MethodError convertlabelview(LabelEnc.FuzzyBinary(), [1.,0,1])
    for (src_lm, src_x) in (
            (LabelEnc.TrueFalse(),[true,false,true,false,false,true]),
            (LabelEnc.ZeroOne(Int32),Int32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Int64),Int64[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Float32),Float32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Float64[1,0,1,0,0,1]),
            (LabelEnc.MarginBased(Int32),Int32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Int64),Int64[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Float64[1,-1,1,-1,-1,1]),
            (LabelEnc.Indices(Float64,2),Float64[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:no,:yes,:maybe,:no,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.TrueFalse(),[true,false,true,false,false,true]),
                (LabelEnc.ZeroOne(Int32),Int32[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(Int64),Int64[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(Float32),Float32[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(),Float64[1,0,1,0,0,1]),
                (LabelEnc.MarginBased(Int32),Int32[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Int64),Int64[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(),Float64[1,-1,1,-1,-1,1]),
                (LabelEnc.Indices(Float64,2),Float64[1,2,1,2,2,1]),
                (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
                (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
            )
            c_src_x = copy(src_x)
            res = @inferred(convertlabelview(dst_lm, c_src_x, src_lm))
            @test res == dst_x
            if typeof(src_lm) <: LabelEnc.OneVsRest
                @test typeof(res) <: MappedArrays.ReadonlyMappedArray
            else
                res2 = convertlabelview(dst_lm, c_src_x)
                @test res == res2
                @test typeof(res) <: MappedArrays.MappedArray
                @test c_src_x[1] == poslabel(src_lm)
                res[1] = neglabel(dst_lm)
                @test c_src_x[1] == neglabel(src_lm)
                @test c_src_x[2] == neglabel(src_lm)
                res[2] = poslabel(dst_lm)
                @test c_src_x[2] == poslabel(src_lm)
            end
        end
    end
end

println("<HEARTBEAT>")

@testset "convertlabelview multiclass" begin
    for (src_lm, src_x) in (
            (LabelEnc.Indices(Int32,3),Int32[1,2,3,2,2,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,2,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:b,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.Indices(UInt8,3),UInt8[1,2,3,2,2,1]),
                (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,2,1]),
                (LabelEnc.NativeLabels(["a","b","c"]),["a","b","c","b","b","a"]),
            )
            c_src_x = copy(src_x)
            res = @inferred(convertlabelview(dst_lm, c_src_x, src_lm))
            @test res == dst_x
            res2 = convertlabelview(dst_lm, c_src_x)
            @test res == res2
            @test typeof(res) <: MappedArrays.MappedArray
            @test c_src_x[1] == ind2label(1, src_lm)
            res[1] = ind2label(2, dst_lm)
            @test c_src_x[1] == ind2label(2, src_lm)
            @test c_src_x[3] == ind2label(3, src_lm)
            res[3] = ind2label(1, dst_lm)
            @test c_src_x[3] == ind2label(1, src_lm)
        end
    end
end

println("<HEARTBEAT>")

@testset "convert binary" begin
    @test convertlabel(LabelEnc.MarginBased, UInt8[1,0,1], LabelEnc.ZeroOne()) == [0x1,0xff,0x1]
    for (src_lm, src_x) in (
            (LabelEnc.FuzzyBinary(),Any[true,0,1,-1,false,3]),
            (LabelEnc.FuzzyBinary(),Int32[1,0,1,0,0,1]),
            (LabelEnc.FuzzyBinary(),Float64[1,-1,1,-1,-1,1]),
            (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
            (LabelEnc.ZeroOne(),Int32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Int64[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Float32[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),Float64[1,0,1,0,0,1]),
            (LabelEnc.MarginBased(),Int32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Int64[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),Float64[1,-1,1,-1,-1,1]),
            (LabelEnc.Indices(2),Float64[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:no,:yes,:maybe,:no,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
            ([:a,:b],[:a,:b,:a,:b,:b,:a]),
            (LabelEnc.OneOfK(Bool,2),BitArray(Bool[1 0 1 0 0 1; 0 1 0 1 1 0])),
            (LabelEnc.OneOfK(Int8,2),Int8[1 0 1 0 0 1; 0 1 0 1 1 0]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.TrueFalse,BitArray([true,false,true,false,false,true])),
                (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
                (LabelEnc.ZeroOne,(_dst_eltype(eltype(src_x),Float64))[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(UInt8),UInt8[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(Int),Int[1,0,1,0,0,1]),
                (LabelEnc.ZeroOne(),[1.,0,1,0,0,1]),
                (LabelEnc.MarginBased,(_dst_eltype(eltype(src_x),Float64))[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(Int),Int[1,-1,1,-1,-1,1]),
                (LabelEnc.MarginBased(),[1,-1.,1,-1,-1,1]),
                (LabelEnc.Indices,(_dst_eltype(eltype(src_x),Int))[1,2,1,2,2,1]),
                (LabelEnc.Indices{UInt8},UInt8[1,2,1,2,2,1]),
                (LabelEnc.Indices(2),Int[1,2,1,2,2,1]),
                (LabelEnc.Indices(Float32,2),Float32[1,2,1,2,2,1]),
                (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
                (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
                ([:a,:b],[:a,:b,:a,:b,:b,:a]),
                (LabelEnc.OneOfK,(_dst_eltype(eltype(src_x),Int))[1 0 1 0 0 1; 0 1 0 1 1 0]),
                (LabelEnc.OneOfK{UInt8},UInt8[1 0 1 0 0 1; 0 1 0 1 1 0]),
                (LabelEnc.OneOfK{Float32},Float32[1 0 1 0 0 1; 0 1 0 1 1 0]),
                (LabelEnc.OneOfK(Bool,2),BitArray(Bool[1 0 1 0 0 1; 0 1 0 1 1 0])),
            )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                if !(typeof(dst_lm) <: Vector)
                    @test @inferred(islabelenc(dst_x, dst_lm)) == true
                end
                if !(typeof(src_lm)<:LabelEnc.FuzzyBinary) && !xor((typeof(src_lm) <: LabelEnc.OneVsRest),(typeof(dst_lm) <: LabelEnc.OneVsRest))
                    res = if typeof(dst_lm) <: Type || typeof(dst_lm) <: Array
                        convertlabel(dst_lm, src_x)
                    else
                        @inferred convertlabel(dst_lm, src_x)
                    end
                    @test typeof(res) <: typeof(dst_x)
                    @test res == dst_x
                end
                res = if typeof(src_lm) <: Vector && typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                    # in this situation K can not be inferred
                    # this is because we neither specify in src or dst the number of labels at compile time
                    convertlabel(dst_lm, src_x, src_lm)
                elseif typeof(src_lm) <: Vector && typeof(dst_lm) <: Vector
                    # same here
                    convertlabel(dst_lm, src_x, src_lm)
                else
                    @inferred convertlabel(dst_lm, src_x, src_lm)
                end
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
            end
        end
        println("<HEARTBEAT>")
    end
end

@testset "convert multiclass" begin
    for (src_lm, src_x) in (
            (LabelEnc.Indices(3),[1,2,3,2,3,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,3,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:c,:a]),
            ([:a,:b,:c],[:a,:b,:c,:b,:c,:a]),
            (LabelEnc.OneOfK(Bool,3),BitArray(Bool[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0])),
            (LabelEnc.OneOfK(Int8,3),Int8[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.Indices,(_dst_eltype(eltype(src_x),Int))[1,2,3,2,3,1]),
                (LabelEnc.Indices{UInt8},UInt8[1,2,3,2,3,1]),
                (LabelEnc.Indices(3),Int[1,2,3,2,3,1]),
                (LabelEnc.Indices(Float32,3),Float32[1,2,3,2,3,1]),
                (LabelEnc.NativeLabels([:x,:y,:z]),[:x,:y,:z,:y,:z,:x]),
                ([:x,:y,:z],[:x,:y,:z,:y,:z,:x]),
                (LabelEnc.OneOfK,(_dst_eltype(eltype(src_x),Int))[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
                (LabelEnc.OneOfK{UInt8},UInt8[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
                (LabelEnc.OneOfK{Float32},Float32[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]),
                (LabelEnc.OneOfK(Bool,3),BitArray(Bool[1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0])),
            )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                if !(typeof(dst_lm) <: Vector)
                    @test @inferred(islabelenc(dst_x, dst_lm)) == true
                end
                res = if typeof(dst_lm) <: Type || typeof(dst_lm) <: Array
                    convertlabel(dst_lm, src_x)
                else
                    @inferred convertlabel(dst_lm, src_x)
                end
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = if typeof(src_lm) <: Vector && typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                    # in this situation K can not be inferred
                    # this is because we neither specify in src or dst the number of labels at compile time
                    convertlabel(dst_lm, src_x, src_lm)
                elseif typeof(src_lm) <: Vector && typeof(dst_lm) <: Vector
                    # same here
                    convertlabel(dst_lm, src_x, src_lm)
                else
                    @inferred convertlabel(dst_lm, src_x, src_lm)
                end
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
            end
        end
    end
end

println("<HEARTBEAT>")

@testset "binary OneOfK with and without ObsDim" begin
    x = [1 0 1 0 0 1; 0 1 0 1 1 0]
    xt = x'
    # From OneOfK
    for (dst_lm, dst_x) in (
            (LabelEnc.TrueFalse,BitArray([true,false,true,false,false,true])),
            (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
            (LabelEnc.ZeroOne,(_dst_eltype(eltype(x),Float64))[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(UInt8),UInt8[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Int),Int[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),[1.,0,1,0,0,1]),
            (LabelEnc.MarginBased,(_dst_eltype(eltype(x),Float64))[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Int),Int[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),[1,-1.,1,-1,-1,1]),
            (LabelEnc.Indices,(_dst_eltype(eltype(x),Int))[1,2,1,2,2,1]),
            (LabelEnc.Indices{UInt8},UInt8[1,2,1,2,2,1]),
            (LabelEnc.Indices(2),Int[1,2,1,2,2,1]),
            (LabelEnc.Indices(Float32,2),Float32[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
            ([:a,:b],[:a,:b,:a,:b,:b,:a]),
        )
        @testset "$x -> ($dst_lm) $dst_x" begin
            res = if typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                convertlabel(dst_lm, x)
            else
                @inferred convertlabel(dst_lm, x)
            end
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(2))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # positional obsdim
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(2), ObsDim.Constant(2))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(2), ObsDim.Last())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, xt, LabelEnc.OneOfK(2), ObsDim.First())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # kw obsdim
            res = convertlabel(dst_lm, x, obsdim=2)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, x, LabelEnc.OneOfK(2), obsdim=:last)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, LabelEnc.OneOfK(2), obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
        end
    end
    println("<HEARTBEAT>")
    # To OneOfK
    for (src_lm, src_x) in (
            (LabelEnc.TrueFalse(),BitArray([true,false,true,false,false,true])),
            (LabelEnc.ZeroOne(UInt8),UInt8[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(Int),Int[1,0,1,0,0,1]),
            (LabelEnc.ZeroOne(),[1.,0,1,0,0,1]),
            (LabelEnc.MarginBased(Float32),Float32[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(Int),Int[1,-1,1,-1,-1,1]),
            (LabelEnc.MarginBased(),[1,-1.,1,-1,-1,1]),
            (LabelEnc.Indices(2),Int[1,2,1,2,2,1]),
            (LabelEnc.Indices(Float32,2),Float32[1,2,1,2,2,1]),
            (LabelEnc.OneVsRest(:yes),[:yes,:not_yes,:yes,:not_yes,:not_yes,:yes]),
            (LabelEnc.NativeLabels([:a,:b]),[:a,:b,:a,:b,:b,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.OneOfK,Array{_dst_eltype(eltype(src_x),Int)}(x)),
                (LabelEnc.OneOfK{Float32},Array{Float32}(x)),
                (LabelEnc.OneOfK(Bool,2),BitArray(x)),
             )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                res = @inferred convertlabel(dst_lm, src_x, src_lm)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x

                # positional obsdim
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Constant(2))
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Last())
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.First())
                # TODO in julia 0.7 we take transposition seriously:
                #   typeof(res) == BitArray{2}
                #   typeof(dst_x') == LinearAlgebra.Adjoint{Bool,BitArray{2}}

                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'

                # kw obsdim
                res = convertlabel(dst_lm, src_x, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
            end
        end
    end
end

println("<HEARTBEAT>")

@testset "multiclass OneOfK with and without ObsDim" begin
    x = [1 0 0 0 0 1; 0 1 0 1 0 0; 0 0 1 0 1 0]
    xt = x'
    # From OneOfK
    for (dst_lm, dst_x) in (
            (LabelEnc.Indices(3),[1,2,3,2,3,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,3,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:c,:a]),
            ([:a,:b,:c],[:a,:b,:c,:b,:c,:a]),
        )
        @testset "$x -> ($dst_lm) $dst_x" begin
            res = if typeof(dst_lm) <: Type && (dst_lm <: LabelEnc.Indices || dst_lm <: LabelEnc.OneOfK)
                convertlabel(dst_lm, x)
            else
                @inferred convertlabel(dst_lm, x)
            end
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(3))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # positional obsdim
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(3), ObsDim.Constant(2))
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, x, LabelEnc.OneOfK(3), ObsDim.Last())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = @inferred convertlabel(dst_lm, xt, LabelEnc.OneOfK(3), ObsDim.First())
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x

            # kw obsdim
            res = convertlabel(dst_lm, x, obsdim=2)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, x, LabelEnc.OneOfK(3), obsdim=:last)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
            res = convertlabel(dst_lm, xt, LabelEnc.OneOfK(3), obsdim=1)
            @test typeof(res) <: typeof(dst_x)
            @test res == dst_x
        end
    end
    # To OneOfK
    @testset "implicit NativeLabels" begin
        res = @inferred convertlabel(LabelEnc.OneOfK(Int,3), [:a,:b,:c,:b,:c,:a], [:a,:b,:c], ObsDim.Constant(1))
        @test typeof(res) <: typeof(x)
        @test res == x'
        res = convertlabel(LabelEnc.OneOfK, [:a,:b,:c,:b,:c,:a], [:a,:b,:c], ObsDim.Constant(1))
        @test typeof(res) <: typeof(x)
        @test res == x'
        res = convertlabel(LabelEnc.OneOfK{Float64}, [:a,:b,:c,:b,:c,:a], [:a,:b,:c], ObsDim.Constant(1))
        @test typeof(res) <: Matrix{Float64}
        @test res == x'
        res = convertlabel(LabelEnc.OneOfK{Float64}, [:a,:b,:c,:b,:c,:a], [:a,:b,:c], obsdim=1)
        @test typeof(res) <: Matrix{Float64}
        @test res == x'
    end
    for (src_lm, src_x) in (
            (LabelEnc.Indices(3),[1,2,3,2,3,1]),
            (LabelEnc.Indices(Float64,3),Float64[1,2,3,2,3,1]),
            (LabelEnc.NativeLabels([:a,:b,:c]),[:a,:b,:c,:b,:c,:a]),
        )
        for (dst_lm, dst_x) in (
                (LabelEnc.OneOfK,Array{(_dst_eltype(eltype(src_x),Int))}(x)),
                (LabelEnc.OneOfK{Float32},Array{Float32}(x)),
                (LabelEnc.OneOfK(Bool,3),BitArray(x)),
             )
            @testset "($src_lm) $src_x -> ($dst_lm) $dst_x" begin
                res = @inferred convertlabel(dst_lm, src_x, src_lm)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x

                # positional obsdim
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Constant(2))
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.Last())
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = @inferred convertlabel(dst_lm, src_x, src_lm, ObsDim.First())
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'

                # kw obsdim
                res = convertlabel(dst_lm, src_x, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=:last)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x
                res = convertlabel(dst_lm, src_x, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
                res = convertlabel(dst_lm, src_x, src_lm, obsdim=1)
                @test typeof(res) <: typeof(dst_x)
                @test res == dst_x'
            end
        end
    end
end

@testset "special NativeLabels-Indices examples" begin
    enc = LabelEnc.NativeLabels(["a","b","c","d"])
    @test @inferred(convertlabel(LabelEnc.Indices, "c", enc)) == 3

    csub = SubString("c",1,1)
    @test @inferred(convertlabel(LabelEnc.Indices, csub, enc)) == 3
end


text:
#==============================================================================#
# Utility functions
#==============================================================================#

ordered_pair(i::Integer, j::Integer) = i <= j ? (i, j) : (j, i)

ndims(dims::NTuple{N,<:Integer}) where {N} = sum(dims .> 1)

ksize(dims::NTuple{3,TI}) where {TI <: Integer} = (floor(TI, dims[1]/2+1), dims[2], dims[3])

function squeeze(A::AbstractArray)
    outshape = Tuple(s for s in size(A) if s > 1)
    return reshape(A, outshape)
end

function determine_nthreads(size::Integer, serial_cut::Integer = 20000)
    return min(Threads.nthreads(), 1 + size÷serial_cut)
end

#==============================================================================#

"""
    copydict!(dest, src)

Copy data from arrays in `src` to `dest`, adding keys to `dest` if necessary.
"""
function copydict!(dest::Dict{TK,TV}, src::Dict{TK,TV}) where {TK,TV<:AbstractArray}
    for (key, val) in src
        if haskey(dest, key) && size(val) == size(dest[key])
            dest[key] .= val
        else
            dest[key] = deepcopy(val)
        end
    end
    return nothing
end

"""
    dotdicts(a, b)

Compute the total dot product between arrays with matching keys in the two dictionaries.
"""
function dotdicts(a::Dict{TK,TV}, b::Dict{TK,TV}) where {TK,TV<:AbstractArray}
    # Performs a dot product between all arrays in a and b dictionaries
    d = 0.0
    for (key, val1) in a
        if haskey(b, key) && size(val1) == size(b[key])
            val2 = b[key]
            d += dot(val1, val2)
        end
    end
    return d
end

"""
    interpolate_grid(grid{TF}, outshape) where {TF <: Real}

Interpolate a real-space field or density grid to the shape defined by `outshape`,
using a cubic b-spline method.

If the interpolation is performed to an output shape of different dimension,
the input grid is either copied or sliced, and the output grid may be inaccurate.
"""
function interpolate_grid(grid::FieldGrid{TF}, outshape::NTuple{3,<:Integer}) where {TF <: Real}
    inshape = size(grid)

    sqz = squeeze(grid)
    itp = interpolate(sqz, BSpline(Cubic(Line(OnGrid()))))
    itp_dim = ndims(size(itp))

    in_single = Tuple(i for i = 1:3 if inshape[i] == 1)
    out_single = Tuple(i for i = 1:3 if outshape[i] == 1)
    if in_single != out_single
        @warn "Interpolating to different dimensions -- output array may be inaccurate."
    end

    # Points at which to evaluate the interpolation function
    hooks = Vector{Float64}[]
    for i = 1:3
        stop_pt = outshape[i] == 1 ? 1 : inshape[i]
        push!(hooks, collect(range(1.0, stop_pt; length = outshape[i])))
    end

    # Loop along and interpolate
    itp_grid = zeros(TF, outshape)
    for dx = 1:outshape[1]
        hx = hooks[1][dx]

        for dy = 1:outshape[2]
            hy = hooks[2][dy]

            for dz = 1:outshape[3]
                hz = hooks[3][dz]

                # Choose which dimensions to copy along
                if itp_dim == 3
                    itp_grid[dx, dy, dz] = itp(hx, hy, hz)
                elseif itp_dim == 2
                    itp_grid[dx, dy, dz] = itp(hx, hy)
                else
                    itp_grid[dx, dy, dz] = itp(hx)
                end

            end
        end
    end

    return itp_grid
end

ext: jl


text:
abstract type AbstractDiffusion end
abstract type AbstractStaticDiffusion <: AbstractDiffusion end
abstract type AbstractDynamicDiffusion <: AbstractDiffusion end
isstatic(diffusion::AbstractStaticDiffusion) = true
isdynamic(diffusion::AbstractStaticDiffusion) = false
isstatic(diffusion::AbstractDynamicDiffusion) = false
isdynamic(diffusion::AbstractDynamicDiffusion) = true
initial_diffusion(diffusion::AbstractDiffusion, d, q, Eltype) = one(Eltype)

struct FixedDiffusion <: AbstractStaticDiffusion end
function estimate_diffusion(rule::FixedDiffusion, integ)
    @unpack d, measurement = integ.cache
    diffusions = integ.sol.diffusions

    v, S = measurement.μ, measurement.Σ

    if iszero(v)
        return zero(integ.cache.diffusion)
    end
    if iszero(S)
        return Inf
    end

    diffusion_t = v' * inv(S) * v / d

    if integ.success_iter == 0
        @assert length(diffusions) == 0
        return diffusion_t
    else
        @assert length(diffusions) == integ.success_iter
        diffusion_prev = diffusions[end]
        diffusion = diffusion_prev + (diffusion_t - diffusion_prev) / integ.success_iter
        return diffusion
    end
end

"""Maximum a-posteriori Diffusion estimate when using an InverseGamma(1/2,1/2) prior

The mode of an InverseGamma(α,β) distribution is given by β/(α+1)
To compute this in an on-line basis from the previous Diffusion, we reverse the computation to
get the previous sum of residuals from Diffusion, and then modify that sum and compute the new
Diffusion.
"""
struct MAPFixedDiffusion <: AbstractStaticDiffusion end
function estimate_diffusion(rule::MAPFixedDiffusion, integ)
    @unpack d, measurement = integ.cache
    diffusions = integ.sol.diffusions

    N = integ.success_iter + 1
    v, S = measurement.μ, measurement.Σ
    res_t = v' * inv(S) * v / d

    α, β = 1/2, 1/2
    if integ.success_iter == 0
        @assert length(diffusions) == 0
        diffusion_t = (β + 1/2 * res_t) / (α + N*d/2 + 1)
        return diffusion_t
    else
        @assert length(diffusions) == integ.success_iter
        diffusion_prev = diffusions[end]
        res_prev = (diffusion_prev * (α + (N-1)*d/2 + 1) - β) * 2
        res_sum_t = res_prev + res_t
        diffusion = (β + 1/2 * res_sum_t) / (α + N*d/2 + 1)
        return diffusion
    end
end

struct DynamicDiffusion <: AbstractDynamicDiffusion end
function estimate_diffusion(kind::DynamicDiffusion, integ)
    @unpack dt = integ
    @unpack d, R = integ.cache
    @unpack H, Q, measurement = integ.cache
    # @assert all(R .== 0) "The dynamic-diffusion assumes R==0!"
    z = measurement.μ
    σ² = z' * ((H*Q*H')\z) / d
    return σ²
end

struct MVDynamicDiffusion <: AbstractDynamicDiffusion end
initial_diffusion(diffusion::MVDynamicDiffusion, d, q, Eltype) =
    kron(Diagonal(ones(Eltype, q+1)), Diagonal(ones(Eltype, d)))
function estimate_diffusion(kind::MVDynamicDiffusion, integ)
    @unpack dt = integ
    @unpack d, q, R, Precond, Proj = integ.cache
    @unpack H, Q, measurement = integ.cache
    E1 = Proj(1)
    z = measurement.μ

    # @assert all(R .== 0) "The dynamic-diffusion assumes R==0!"

    # Assert EK0
    PI = inv(Precond(dt))
    @assert all(H .== E1 * PI)

    # More safety checks
    @assert isdiag(H*Q*H')
    @assert length(unique(diag(H*Q*H'))) == 1
    Q0_11 = diag(H*Q*H')[1]

    Σ_ii = z .^ 2 ./ Q0_11
    Σ_ii .= max.(Σ_ii, eps(eltype(Σ_ii)))
    Σ = Diagonal(Σ_ii)

    Σ_out = kron(Diagonal(ones(q+1)), Σ)
    @assert isdiag(Σ_out)
    # @info "MVDynamic diffusion" Σ Σ_out
    return Σ_out
end

struct MVFixedDiffusion <: AbstractStaticDiffusion end
initial_diffusion(diffusion::MVFixedDiffusion, d, q, Eltype) =
    kron(Diagonal(ones(Eltype, q+1)), Diagonal(ones(Eltype, d)))
function estimate_diffusion(kind::MVFixedDiffusion, integ)
    @unpack dt = integ
    @unpack d, q, R, Precond, Proj = integ.cache
    @unpack measurement, H = integ.cache
    @unpack d, measurement = integ.cache
    E1 = Proj(1)
    diffusions = integ.sol.diffusions

    # Assert EK0
    PI = inv(Precond(dt))
    @assert all(H .== E1 * PI)

    @unpack measurement = integ.cache
    v, S = measurement.μ, measurement.Σ

    # More safety checks
    @assert isdiag(S)
    # @assert length(unique(diag(S))) == 1
    S_11 = diag(S)[1]

    Σ_ii = v .^ 2 ./ S_11
    Σ = Diagonal(Σ_ii)
    Σ_out = kron(Diagonal(ones(q+1)), Σ)
    @assert isdiag(Σ_out)
    # @info "MV-MLE-Diffusion" v S Σ Σ_out

    if integ.success_iter == 0
        @assert length(diffusions) == 0
        return Σ_out
    else
        @assert length(diffusions) == integ.success_iter
        diffusion_prev = diffusions[end]
        diffusion = diffusion_prev + (Σ_out - diffusion_prev) / integ.success_iter
        return diffusion
    end
end


text:
function load_data(filename::String, seq_len = 50, batch_size = 64)
    # Load data, split it into an alphabet, then convert
    # the text to a onehot matrix representation.
    text = collect(String(read(filename)))
    alphabet = [unique(text)..., '_']
    text = map(ch -> onehot(ch, alphabet), text)
    stop = onehot('_', alphabet)

    Xs = collect(partition(batchseq(chunk(text[1:end-1], batch_size), stop), seq_len))
    Ys = collect(partition(batchseq(chunk(text[2:end],   batch_size), stop), seq_len))
    return alphabet, Xs, Ys
end

# Sample randomly from model output to generate sentences
function sample(m, alphabet, len)
    Flux.reset!(m)
    buf = IOBuffer()
    c = rand(alphabet)
    for i = 1:len
        write(buf, c)
        c = wsample(alphabet, m(onehot(c, alphabet)))
    end
    return String(take!(buf))
end

max_stars_repo_name: MikeInnes/zygote-paper

max_issues_repo_name: MikeInnes/zygote-paper

max_forks_repo_name: MikeInnes/zygote-paper


text:
## Helper Functions ##

function stringify(set::Set{K})::String where K
    str = ""
    for s in sort!(collect(set))
        str *= string(s) * ","
    end
    return rstrip(str, [',',' '])
end

function stringify(array::Vector{K})::String where K
    str = ""
    for s in sort(array)
        str *= string(s) * ","
    end
    return rstrip(str, [',',' '])
end

## Generic Utility Functions ##

function _two_stage_tree(n::Int, p::Vector{R})::ScenarioTree where R <: Real
    st = ScenarioTree()
    for s in 1:n
        add_leaf(st, root(st), p[s])
    end
    return st
end

"""
    two_stage_tree(n::Int)::ScenarioTree
    two_stage_tree(p::Vector{R})::ScenarioTree where R <: Real

Construct and return a two-stage scenario tree with `n` scenarios with equal probability or a two-stage scenario tree with `length(p)` scenarios where probability of scenario s is `p[s+1]`.
"""
function two_stage_tree(n::Int)::ScenarioTree
    return _two_stage_tree(n, [1.0/n for k in 1:n])
end

function two_stage_tree(p::Vector{R})::ScenarioTree where R <: Real
    if !isapprox(sum(p), 1.0, atol=1e-8)
        @warn("Given probability vector has total probability $(sum(p)). Normalizing to 1.")
        p /= sum(p)
    end
    return _two_stage_tree(length(p), p)
end

function visualize_tree(phd::PHData)
    # TODO: Implement this...
    @warn("Not yet implemented")
    return
end

## Complex PHData Interaction Functions ##

# NOTE: These functions are almost all post-processing functions

"""
    lower_bounds(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with the computed and saved lower bounds. See the `lower_bound` keyword argument on [`solve`](@ref).
"""
function lower_bounds(phd::PHData)::DataFrames.DataFrame

    lb_df = nothing
    lower_bounds = phd.history.lower_bounds

    for iter in sort!(collect(keys(lower_bounds)))
        lb = lower_bounds[iter]
        data = Dict{String,Any}("iteration" => iter,
                                "bound" => lb.lower_bound,
                                "absolute gap" => lb.gap,
                                "relative gap" => lb.rel_gap,
                                )

        if isnothing(lb_df)
            lb_df = DataFrames.DataFrame(data)
        else
            push!(lb_df, data)
        end
    end

    if isnothing(lb_df)
        lb_df = DataFrames.DataFrame()
    end

    return lb_df
end

"""
    residuals(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with absolute and relative residuals along with the components. See the `save_residuals` keyword argument on [`solve`](@ref).
"""
function residuals(phd::PHData)::DataFrames.DataFrame

    res_df = nothing
    residuals = phd.history.residuals

    for iter in sort!(collect(keys(residuals)))
        res = residuals[iter]
        data = Dict{String,Any}("iteration" => iter,
                                "absolute" => res.abs_res,
                                "relative" => res.rel_res,
                                "xhat_sq" => res.xhat_sq,
                                "x_sq" => res.x_sq
                                )

        if isnothing(res_df)
            res_df = DataFrames.DataFrame(data)
        else
            push!(res_df, data)
        end
    end

    if isnothing(res_df)
        res_df = DataFrames.DataFrame()
    end

    return res_df
end

"""
    retrieve_soln(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with the solution values of all consensus and leaf variables.
"""
function retrieve_soln(phd::PHData)::DataFrames.DataFrame

    vars = Vector{String}()
    vals = Vector{Float64}()
    stages = Vector{STAGE_ID}()
    scens = Vector{String}()

    for xid in sort!(collect(keys(phd.xhat)))

        vid = first(convert_to_variable_ids(phd, xid))

        push!(vars, name(phd, vid))
        push!(vals, xhat_value(phd, xid))
        push!(stages, value(stage_id(phd, xid)))
        push!(scens, stringify(value.(scenario_bundle(phd, xid))))

    end

    soln_df = DataFrames.DataFrame(variable=vars, value=vals, stage=stages,
                                   scenarios=scens)

    return soln_df
end

"""
    retrieve_aug_obj_value(phd::PHData)::Float64

Return the current objective value including Lagrange and proximal PH terms of the stochastic program.
"""
function retrieve_aug_obj_value(phd::PHData)::Float64

    obj_value = 0.0

    for (scid, sinfo) in pairs(phd.scenario_map)
        obj_value += sinfo.prob * objective_value(sinfo)
    end

    return obj_value
end

"""
    retrieve_obj_value(phd::PHData)::Float64

Return the current objective value without Lagrange and proximal PH terms.
"""
function retrieve_obj_value(phd::PHData)::Float64

    obj_value = 0.0

    for (scid, sinfo) in pairs(phd.scenario_map)

        obj_s = objective_value(sinfo)

        # Remove PH terms
        for (vid, x_val) in pairs(sinfo.branch_vars)
            w_val = sinfo.w_vars[vid]
            xhat_val = xhat_value(phd, vid)
            r = get_penalty_value(phd.r, convert_to_xhat_id(phd, vid))
            obj_s -= w_val * x_val + 0.5 * r * (x_val - xhat_val)^2
        end

        obj_value += sinfo.prob * obj_s

    end

    return obj_value
end

"""
    retrieve_no_hats(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with the final nonconsensus variable values.
"""
function retrieve_no_hats(phd::PHData)::DataFrames.DataFrame
    vars = Vector{String}()
    vals = Vector{Float64}()
    stage = Vector{STAGE_ID}()
    scenario = Vector{SCENARIO_ID}()
    index = Vector{INDEX}()

    variable_map = Dict{VariableID, Float64}()
    for (scid,sinfo) in phd.scenario_map
        merge!(variable_map, sinfo.branch_vars, sinfo.leaf_vars)
    end

    for vid in sort!(collect(keys(variable_map)))
        push!(vars, name(phd, vid))
        push!(vals, variable_map[vid])
        push!(stage, value(vid.stage))
        push!(scenario, value(vid.scenario))
        push!(index, value(vid.index))
    end

    soln_df = DataFrames.DataFrame(variable=vars, value=vals, stage=stage,
                                   scenario=scenario, index=index)

    return soln_df
end

"""
    retrieve_w(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with the final anticipativity constraint Lagrange multiplier values.
"""
function retrieve_w(phd::PHData)::DataFrames.DataFrame
    vars = Vector{String}()
    vals = Vector{Float64}()
    stage = Vector{STAGE_ID}()
    scenario = Vector{SCENARIO_ID}()
    index = Vector{INDEX}()

    variable_map = Dict{VariableID, Float64}()
    for (scid,sinfo) in phd.scenario_map
        merge!(variable_map, sinfo.w_vars)
    end

    for vid in sort!(collect(keys(variable_map)))

        push!(vars, "W_" * name(phd, vid))
        push!(vals, variable_map[vid])
        push!(stage, value(vid.stage))
        push!(scenario, value(vid.scenario))
        push!(index, value(vid.index))

    end

    soln_df = DataFrames.DataFrame(variable=vars, value=vals, stage=stage,
                                   scenario=scenario, index=index)

    return soln_df
end

"""
    retrieve_xhat_history(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with the saved consensus variable values. See the `save_iterates` keyword argument on [`solve`](@ref).
"""
function retrieve_xhat_history(phd::PHData)::DataFrames.DataFrame

    xhat_df = nothing
    iterates = phd.history.iterates

    for iter in sort!(collect(keys(iterates)))

        data = Dict{String,Any}("iteration" => iter)

        for xhid in sort!(collect(keys(phd.xhat)))
            if !is_leaf(phd, xhid)
                vname = name(phd, xhid) * "_" * stringify(value.(scenario_bundle(phd, xhid)))
                data[vname] = iterates[iter].xhat[xhid]
            end
        end

        if isnothing(xhat_df)
            xhat_df = DataFrames.DataFrame(data)
        else
            push!(xhat_df, data)
        end
    end

    if isnothing(xhat_df)
        xhat_df = DataFrames.DataFrame()
    end

    return xhat_df
end

"""
    retrieve_no_hat_history(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with the saved nonconsensus variable values. See the `save_iterates` keyword argument on [`solve`](@ref).
"""
function retrieve_no_hat_history(phd::PHData)::DataFrames.DataFrame

    x_df = nothing
    iterates = phd.history.iterates

    for iter in sort!(collect(keys(iterates)))

        current_iterate = iterates[iter]
        data = Dict{String,Any}("iteration" => iter)

        for vid in sort!(collect(keys(current_iterate.x)))
            vname = name(phd, vid) * "_$(value(scenario(vid)))"
            data[vname] = current_iterate.x[vid]
        end

        if isnothing(x_df)
            x_df = DataFrames.DataFrame(data)
        else
            push!(x_df, data)
        end
    end

    if isnothing(x_df)
        x_df = DataFrames.DataFrame()
    end

    return x_df
end

"""
    retrieve_w_history(phd::PHData)::DataFrames.DataFrame

Return a `DataFrame` with the saved PH Lagrange variable values. See the `save_iterates` keyword argument on [`solve`](@ref).
"""
function retrieve_w_history(phd::PHData)::DataFrames.DataFrame

    w_df = nothing
    iterates = phd.history.iterates

    for iter in sort!(collect(keys(iterates)))

        current_iterate = iterates[iter]
        data = Dict{String,Any}("iteration" => iter)

        for vid in sort!(collect(keys(current_iterate.w)))
            vname = "W_" * name(phd, vid) * "_$(value(scenario(vid)))"
            data[vname] = current_iterate.w[vid]
        end

        if isnothing(w_df)
            w_df = DataFrames.DataFrame(data)
        else
            push!(w_df, data)
        end
    end

    if isnothing(w_df)
        w_df = DataFrames.DataFrame()
    end

    return w_df
end


text:
module OrbitalFeatureUtils

using SparseArrays
using DataFrames
using ...ChemistryFeaturization.Data: valenceshell_conf_df

# Helper functions used for encoding
#=
return the Index and Value arrays (can be used for building a sparse matrix on their own)
corresponding to a single element given its valence shell configuration (regex-extracted usually)
=#
function _orbitalsparse(valence_shell_config)
    I = map(e -> _orbitalindex(e[1:2]), valence_shell_config)
    V = map(e -> parse(Int, e[3]), valence_shell_config)
    return I, V
end

# faster and more memory efficient than using a dictionary
function _orbitalindex(orbital::SubString{String})
    if orbital == "1s"
        return 1
    elseif orbital == "2s"
        return 2
    elseif orbital == "2p"
        return 3
    elseif orbital == "3s"
        return 4
    elseif orbital == "3p"
        return 5
    elseif orbital == "4s"
        return 6
    elseif orbital == "3d"
        return 7
    elseif orbital == "4p"
        return 8
    elseif orbital == "5s"
        return 9
    elseif orbital == "4d"
        return 10
    elseif orbital == "5p"
        return 11
    elseif orbital == "6s"
        return 12
    elseif orbital == "4f"
        return 13
    elseif orbital == "5d"
        return 14
    elseif orbital == "6p"
        return 15
    elseif orbital == "7s"
        return 16
    elseif orbital == "5f"
        return 17
    elseif orbital == "6d"
        return 18
    elseif orbital == "7p"
        return 19
    elseif orbital == "8s"
        return 20
    elseif orbital == "5g"
        return 21
    elseif orbital == "6f"
        return 22
    elseif orbital == "7d"
        return 23
    elseif orbital == "8p"
        return 24
    else # orbital == "9s"
        return 25
    end
end

# using regex, get the valence shell configuration
function _orbitalregex(element, df::DataFrame = valenceshell_conf_df)
    config_df_row =
        filter(:Symbol => x -> x == element, df; view = true)[!, "Electronic Structure"][1]
    orbital_regex = r"[1-9][s|p|d|f|g][1-9]" # regex which will match individual orbitals
    orbital_config = map(o -> o.match, collect(eachmatch(orbital_regex, config_df_row)))
    return orbital_config
end

# Get the electronic configuration of `element` (equivalent to the findnz() result if the configuration was a SparseVector)
function _name_to_econf(element, df::DataFrame = valenceshell_conf_df)
    valence_shell_config = _orbitalregex(element, valenceshell_conf_df)
    return _orbitalsparse(valence_shell_config)
end

# Get the name of the element which has the electronic configuration `shell_conf_sparse` (as a SparseVector).
function _econf_to_name(
    shell_conf_sparse::SparseVector{Tv,Ti},
    df::DataFrame = valenceshell_conf_df,
) where {Tv,Ti}
    rows, vals = findnz(shell_conf_sparse)
    shell_conf = ""

    # find the configuration for each shell and concatenate it to the `shell_conf`
    for i = 1:length(rows)
        shell_conf = shell_conf * _indexorbital(rows[i]) * string(vals[i]) * "."
    end

    # `chop` the trailing '.', and rearrange order of the shells to match format present in `valenceshell_conf_df`
    shell_conf = join(sort(split(chop(shell_conf), '.'), by = first), '.')

    # find the DataFrame that has the matching `shell_conf` as its Valence Shell Configuration, and get its `Symbol`
    return (filter("Electronic Structure" => x -> x == shell_conf, df; view = true)[
        !,
        :Symbol,
    ])[1]
end

# Helper functions used for decoding

function _indexorbital(orbital::Int16)
    if orbital == 1
        return "1s"
    elseif orbital == 2
        return "2s"
    elseif orbital == 3
        return "2p"
    elseif orbital == 4
        return "3s"
    elseif orbital == 5
        return "3p"
    elseif orbital == 6
        return "4s"
    elseif orbital == 7
        return "3d"
    elseif orbital == 8
        return "4p"
    elseif orbital == 9
        return "5s"
    elseif orbital == 10
        return "4d"
    elseif orbital == 11
        return "5p"
    elseif orbital == 12
        return "6s"
    elseif orbital == 13
        return "4f"
    elseif orbital == 14
        return "5d"
    elseif orbital == 15
        return "6p"
    elseif orbital == 16
        return "7s"
    elseif orbital == 17
        return "5f"
    elseif orbital == 18
        return "6d"
    elseif orbital == 19
        return "7p"
    elseif orbital == 20
        return "8s"
    elseif orbital == 21
        return "5g"
    elseif orbital == 22
        return "6f"
    elseif orbital == 23
        return "7d"
    elseif orbital == 24
        return "8p"
    else
        return "9s"
    end
end

end


text:
#TODO: remove when Graphviz_jll is back
import Conda
Conda.add("graphviz", :cropbox; channel="conda-forge")


text:
module ThesisUtils

import Plots
import PlotUtils
import ColorTypes
import PyCall
  PyCall.@pyimport gc as pygc
import PyPlot

export Margin, Normal, Full
export autofig, placeholder, annotate_color!, seqcolors,
       remove_ticklabels!

## Page layout settings
# To describe the extent of a figure we define these terms
@enum PlotSize Margin Normal Full
const plotsizes = (Margin, Normal, Full)
"Indicates that a figure spans the tufte-latex margin"
Margin
"Indicates that a figure spans the tufte-latex body area"
Normal
"Indicates that a figure spans the whole page"
Full

# DPI settings
const dpi_default = 100
const dpi_print = 300
const scale = dpi_print/dpi_default

"Default plot aspect ratios"
const aspectratio = Dict(
  Margin => 1,
  Normal => 1.5,
  Full => 1.6 )

"Widths of the margin, body text, and full page in inches"
const width_in = Dict(
  Margin => 2,
  Normal => 4 + 3//16,
  Full => 6 + 1//2 )

"Sizes of figures in pixels"
const figsize = let
  pixelsizes = map(plotsizes) do s
    plotsize_in = (1, 1/aspectratio[s]) .* width_in[s]
    pixels_scaled = plotsize_in .* dpi_print
    whole_numbers = round.(Int, pixels_scaled)
  end

  Dict(zip(plotsizes, pixelsizes))
end

## Plot display options
# Font settings
const fontstyle = "fbb"
const fontsize = Dict(
  Margin => 8,
  Normal => 10,
  Full => 10)
const font = Dict((s, Plots.font(fontstyle, fontsize[s])) for s in plotsizes)
const main_font_opts = Dict(
  :tickfont => font[Normal],
  :legendfont => font[Normal],
  :guidefont => font[Normal],
  :titlefont => font[Normal] )
const caption_font_opts = Dict(
  :tickfont => font[Margin],
  :legendfont => font[Margin],
  :guidefont => font[Margin],
  :titlefont => font[Margin] )
PyPlot.rc("mathtext", fontset = "custom", rm = fontstyle,
          it = "$fontstyle:italic", bf = "$fontstyle:bold",
          sf = fontstyle)

# Plot appearance
const plotopts = Dict(
  :linewidth => 1.5,
  :thickness_scaling => scale,
  :grid => true)
Plots.pyplot(;plotopts...)

# Folder to save generated figures to
const figdir = "autofigs"

""" Make a plot to go in the thesis and save it

    plotfunc: The plotting function; should return a Plot object
    name: The file name to save the figure as
    s::PlotSize: One of `Margin`, `Normal` or `Full`. Indicates what size
                 the figure should be saved as

    vscale [optional, default=1]: Scale the plot to create more or less
                                  vertical space
    png [optional, default=1]: Save the plot as a png instead of a pdf """
function autofig(plotfunc, name, s::PlotSize; vscale=1, savepng=false)
  # workaround for python not closing files properly
  pygc.collect()

  # get the figure size and font size
  width = figsize[s][1]
  height = figsize[s][2] * vscale
  if s == Margin
    Plots.default(;caption_font_opts...)
  else
    Plots.default(;main_font_opts...)
  end
  if s == Full
    name *= "_big_fig"
  end

  Plots.default(size = (width, height))

  # plot the figure and save it to file
  p = plotfunc()
  figpath = joinpath(figdir, name)
  if savepng
    Plots.png(figpath)
  else
    Plots.svg(figpath)
    # bit of a workaround because the font doesn't embed
    run(`rsvg-convert -f pdf -o $figpath.pdf $figpath.svg`)
    rm("$figpath.svg")
  end
  p
end

"Make a default plot"
function placeholder()
  Plots.plot([sin, cos], linspace(0, 2π), labels=["sin(x)" "cos(x)"],
    xlabel="This is the x-axis",
    ylabel="This is the y-axis",
    title="Placeholder plot",
    framestyle=:grid)
end

const PlotType = Union{Plots.Plot, Plots.Subplot}

"Annotate a plot with a colored label"
function annotate_color!(x, y, text, color;
                         position=:left, plotsize=Normal, rotation=0)
  basefont = font[plotsize]
  rot = deg2rad(rotation)
  formatted_font = Plots.font(basefont, color, position, rot)
  formatted_text = Plots.text(text, formatted_font)
  Plots.annotate!(x, y, formatted_text)
end
function annotate_color!(p::PlotType, x, y, text, color;
                         position=:left, plotsize=Normal, rotation=0)
  basefont = font[plotsize]
  rot = deg2rad(rotation)
  formatted_font = Plots.font(basefont, color, position, rot)
  formatted_text = Plots.text(text, formatted_font)
  Plots.annotate!(p, x, y, formatted_text)
end

"Remove the tick labels from a plot but leave the ticks and grid"
function remove_ticklabels!(p::PlotType; x=true, y=true)
  x && Plots.plot!(p; xformatter=_->"")
  y && Plots.plot!(p; yformatter=_->"")
  p
end

"Provide a palette of colors drawn sequentially from a color gradient"
function seqcolors(name, N, start=0, stop=1)
  grad = PlotUtils.cgrad(name)
  colors = ColorTypes.RGBA{Float64}[grad[n] for n in linspace(start, stop, N)]
end

end # module ThesisUtils


text:
module RationalFunctionFit
using ..Polynomials
import ..Polynomials: RationalFunction, indeterminate, constructorof
import ..Polynomials: evalpoly
using LinearAlgebra

"""
    fit(::Type{RationalFunction}, xs::AbstractVector{S}, ys::AbstractVector{T}, m, n; var=:x)

Fit a rational function of the form `pq = (a₀ + a₁x¹ + … + aₘxᵐ) / (1 + b₁x¹ + … + bₙxⁿ)` to the data `(x,y)`.

!!! Note:

    This uses a simple implementation of the Gauss-Newton method
    to solve the non-linear least squares problem:
    `minᵦ Σ(yᵢ - pq(xᵢ,β)²`, where `β=(a₀,a₁,…,aₘ,b₁,…,bₙ)`.

    A more rapidly convergent method is used in the `LsqFit.jl`
    package, and if performance is important, re-expressing the
    problem for use with that package is suggested.

    Further, if an accurate rational function fit of adaptive degrees
    is of interest, the `BaryRational.jl` package provides an
    implementation of the AAA algorithm ("which offers speed,
    flexibility, and robustness we have not seen in other algorithms"
    [Nakatsukasa, Sète,
    Trefethen](https://arxiv.org/pdf/1612.00337.pdf)) and one using
    Floater-Hormann weights [Floater,
    Hormann](https://doi.org/10.1007/s00211-007-0093-y) ("that have no
    real poles and arbitrarily high approximation orders on any real
    interval, regardless of the distribution of the points")

    The [RationalApproximations](https://github.com/billmclean/RationalApproximations) package also has implementations of the AAA algorithm.

    A python libary, [polyrat](https://github.com/jeffrey-hokanson/polyrat), has implementations of other algorithms.

## Example
```
julia> x = variable(Polynomial{Float64})
Polynomial(1.0*x)

julia> pq = (1+x)//(1-x)
(1.0 + 1.0*x) // (1.0 - 1.0*x)

julia> xs = 2.0:.1:3;

julia> ys = pq.(xs);

julia> v = fit(RationalFunction, xs, ys, 2, 2)
(1.0 + 1.0*x - 6.82121e-13*x^2) // (1.0 - 1.0*x + 2.84217e-13*x^2)

julia> maximum(abs, v(x)-pq(x) for x ∈ 2.1:0.1:3.0)
1.06314956838105e-12

julia> using BaryRational

julia> u = aaa(xs,ys)
(::BaryRational.AAAapprox{Vector{Float64}}) (generic function with 1 method)

julia> maximum(abs, u(x)-pq(x) for x ∈ 2.1:0.1:3.0)
4.440892098500626e-16

julia> u(variable(pq)) # to see which polynomial is used
(2.68328 + 0.447214*x - 1.78885*x^2 + 0.447214*x^3) // (2.68328 - 4.91935*x + 2.68328*x^2 - 0.447214*x^3)
```

"""
function Polynomials.fit(::Type{PQ}, xs::AbstractVector{S}, ys::AbstractVector{T}, m, n; var=:x) where {T,S, PQ<:RationalFunction}

    β₁,β₂ = gauss_newton(collect(xs), convert(Vector{float(T)}, ys), m, n)
    P = eltype(PQ)
    T′ = Polynomials._eltype(P) == nothing ? eltype(β₁) : eltype(P)
    X = indeterminate(PQ, var)
    P′ = constructorof(P){T′,X}
    p = P′(β₁)
    q = P′(vcat(1, β₂))

    p // q
end

"""
    fit(::Type{RationalFunction}, r::Polynomial, m, n; var=:x)

Fit a Pade approximant ([`pade_fit`](@ref)) to `r`.

Examples:

```jldoctext
julia> using Polynomials, PolynomialRatios

julia> x = variable()
Polynomial(x)

julia> ex = 1 + x + x^2/2 + x^3/6 + x^4/24 + x^5/120 # Taylor polynomial for e^x
Polynomial(1.0 + 1.0*x + 0.5*x^2 + 0.16666666666666666*x^3 + 0.041666666666666664*x^4 + 0.008333333333333333*x^5)

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 1,1)(x) for x ∈ 0:.05:0.5)
0.017945395966538547

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 1,2)(x) for x ∈ 0:.05:0.5)
0.0016624471707165078

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 2,1)(x) for x ∈ 0:.05:0.5)
0.001278729299871717

julia> maximum(abs, exp(x) - fit(RationalFunction, ex, 2,2)(x) for x ∈ 0:.05:0.5)
7.262205147950951e-5
```
"""
function Polynomials.fit(::Type{RationalFunction},r::Polynomial, m::Integer, n::Integer;var=:x)
    p,q = pade_fit(r, m,n, var=var)
    p // q
end

## ---- Pade
## https://mathworld.wolfram.com/PadeApproximant.html
"""
    pade_fit(r::Polynomial, m,n)

For a polynomial `r` of degree `d ≥ m + n`, find a rational function `p/q` with
`degree(p) ≤ m`, `degree(q) ≤ n` and `q*r - p = x^{m+n+1}*s(x)` for some polynomial `s`.

This implementation sets up a system of equations to identify `p` and `q`.
"""
function pade_fit(p::Polynomial{T}, m::Integer, n::Integer; var=:x) where {T}
    d = degree(p)
    @assert (0 <= m) && (1 <= n) && (m + n <= d)

    # could be much more perfomant
    c = convert(LaurentPolynomial, p) # for better indexing
    cs = [c[m+j-i] for j ∈ 1:n, i ∈ 0:n]

    qs′ = cs[:, 2:end] \ cs[:,1]
    qs = vcat(1, -qs′)

    cs = [c[0 + j - i] for j ∈ 0:m, i∈0:n]
    ps = cs * qs

    Polynomial(ps, var), Polynomial(qs,var)
end

## ---- Least Squares
## avoiding dependency on another package, LsqFit

using LinearAlgebra

# return pair ((a₀,...,aₙ), (b₁,...,bₘ))
function initial_guess(xs::Vector{T}, ys::Vector{S}, n, m) where {T, S}
    # p(x) = a₀ + ... + aₙx^n
    # q(x) = 1 + b₁x + ... + bₘx^m = 1 + r(x)
    # yᵢ + yᵢ * r(xᵢ) = p(xᵢ)
    # yᵢ = p(xᵢ) - r(xᵢ) * yᵢ
    k = n+1+m
    A = zeros(T, k, k)
    A[:,1] .= one(T)
    xs′ = xs[1:k]
    xs′′ = copy(xs′)
    for i ∈ 1:n
        A[:,1+i] = xs′
        xs′ .*= xs′′
    end
    xs′ = -copy(xs′′) .* ys[1:k]
    for i ∈ 1:m
        A[:, 1+n+i] = xs′
        xs′ .*= xs′′
    end
    β = pinv(A) *  ys[1:k]

end

function make_model(n)
    (x, β) -> begin
        β₁, β₂ = β[1:n+1], β[n+2:end]
        evalpoly.(x,(β₁,)) ./ evalpoly.(x, (vcat(1, β₂),))
    end
end

function J!(Jᵣ, xs::Vector{T}, β, n) where {T}
    β₁, β₂ = β[1:n+1],β[n+2:end]
    ps = evalpoly.(xs, (β₁,))
    qs = evalpoly.(xs, (vcat(1, β₂),))
    λ = one(T) ./ qs
    for i ∈ eachindex(β₁)
        Jᵣ[:,1] = λ
        λ .*= xs
    end
    λ = xs .* ps ./ (qs .* qs)
    for i ∈ eachindex(β₂)
        Jᵣ[:, n+1+i]=λ
        λ .*= xs
    end
    nothing
end

function gauss_newton(xs, ys::Vector{T}, n, m, tol=sqrt(eps(T))) where {T}

    β = initial_guess(xs, ys, n, m)
    model = make_model(n)

    Jᵣ = zeros(T, length(xs), 1 + n + m)

    Δ = norm(ys, Inf) * tol

    ϵₘ = norm(ys - model(xs, β), Inf)
    βₘ = copy(β)

    no_steps = 0

    while no_steps < 25
        no_steps += 1

        r = ys - model(xs, β)
        ϵ = norm(r, Inf)
        ϵ < Δ && return (β[1:n+1], β[n+2:end])
        if ϵ < ϵₘ
            ϵₘ = ϵ
            βₘ .= β
        end
        J!(Jᵣ, xs, β, n)
        Δᵦ = pinv(Jᵣ' * Jᵣ) * (Jᵣ' * r)
        β .-= Δᵦ

    end

    @warn "no convergence; returning best fit of many steps"
    return (βₘ[1:n+1], βₘ[n+2:end])
end

end


text:
@testset "tapedtask" begin
    # Test case 1: stack allocated objects are deep copied.
    @testset "stack allocated objects" begin
        function f()
            t = 0
            while true
                produce(t)
                t = 1 + t
            end
        end

        ttask = TapedTask(f)
        @test consume(ttask) == 0
        @test consume(ttask) == 1
        a = copy(ttask)
        @test consume(a) == 2
        @test consume(a) == 3
        @test consume(ttask) == 2
        @test consume(ttask) == 3

        @inferred Libtask.TapedFunction(f)
    end

    # Test case 2: heap allocated objects are shallowly copied.
    @testset "heap allocated objects" begin
        function f()
            t = [0 1 2]
            while true
                produce(t[1])
                t[1] = 1 + t[1]
            end
        end

        ttask = TapedTask(f)
        @test consume(ttask) == 0
        @test consume(ttask) == 1
        a = copy(ttask)
        @test consume(a) == 2
        @test consume(a) == 3
        @test consume(ttask) == 4
        @test consume(ttask) == 5
        @test consume(ttask) == 6
        @test consume(ttask) == 7
    end

    @testset "iteration" begin
        function f()
            t = 1
            while true
                produce(t)
                t = 1 + t
            end
        end

        ttask = TapedTask(f)

        next = iterate(ttask)
        @test next === (1, nothing)

        val, state = next
        next = iterate(ttask, state)
        @test next === (2, nothing)

        val, state = next
        next = iterate(ttask, state)
        @test next === (3, nothing)

        a = collect(Iterators.take(ttask, 7))
        @test eltype(a) === Int
        @test a == 4:10
    end

    # Test of `Exception`.
    @testset "Exception" begin
        @testset "method error" begin
            function f()
                t = 0
                while true
                    t[3] = 1
                    produce(t)
                    t = t + 1
                end
            end

            ttask = TapedTask(f)
            try
                consume(ttask)
            catch ex
                @test ex isa MethodError
            end
            if VERSION >= v"1.5"
                @test ttask.task.exception isa MethodError
            end
        end

        @testset "error test" begin
            function f()
                x = 1
                while true
                    error("error test")
                    produce(x)
                    x += 1
                end
            end

            ttask = TapedTask(f)
            try
                consume(ttask)
            catch ex
                @test ex isa ErrorException
            end
            if VERSION >= v"1.5"
                @test ttask.task.exception isa ErrorException
            end
        end

        @testset "OutOfBounds Test Before" begin
            function f()
                x = zeros(2)
                while true
                    x[1] = 1
                    x[2] = 2
                    x[3] = 3
                    produce(x[1])
                end
            end

            ttask = TapedTask(f)
            try
                consume(ttask)
            catch ex
                @test ex isa BoundsError
            end
            if VERSION >= v"1.5"
                @test ttask.task.exception isa BoundsError
            end
        end

        @testset "OutOfBounds Test After `produce`" begin
            function f()
                x = zeros(2)
                while true
                    x[1] = 1
                    x[2] = 2
                    produce(x[2])
                    x[3] = 3
                end
            end

            ttask = TapedTask(f)
            @test consume(ttask) == 2
            try
                consume(ttask)
            catch ex
                @test ex isa BoundsError
            end
            if VERSION >= v"1.5"
                @test ttask.task.exception isa BoundsError
            end
        end

        @testset "OutOfBounds Test After `copy`" begin
            function f()
                x = zeros(2)
                while true
                    x[1] = 1
                    x[2] = 2
                    produce(x[2])
                    x[3] = 3
                end
            end

            ttask = TapedTask(f)
            @test consume(ttask) == 2
            ttask2 = copy(ttask)
            try
                consume(ttask2)
            catch ex
                @test ex isa BoundsError
            end
            @test ttask.task.exception === nothing
            if VERSION >= v"1.5"
                @test ttask2.task.exception isa BoundsError
            end
        end
    end
end

max_stars_repo_name: TuringLang/Libtask

max_issues_repo_name: TuringLang/Libtask

max_forks_repo_name: TuringLang/Libtask


text:
const NULL = 0
const APOSTROPHE = 39
const COMMA = 44
const MINUS = 45
const PERIOD = 46
const SLASH = 47
const ZERO = 48
const ONE = 49
const TWO = 50
const THREE = 51
const FOUR = 52
const FIVE = 53
const SIX = 54
const SEVEN = 55
const EIGHT = 56
const NINE = 57
const SEMICOLON = 59
const EQUAL = 61
const A = 65
const B = 66
const C = 67
const D = 68
const E = 69
const F = 70
const G = 71
const H = 72
const I = 73
const J = 74
const K = 75
const L = 76
const M = 77
const N = 78
const O = 79
const P = 80
const Q = 81
const R = 82
const S = 83
const T = 84
const U = 85
const V = 86
const W = 87
const X = 88
const Y = 89
const Z = 90
const SPACE = 32
const ESCAPE = 256
const ENTER = 257
const TAB = 258
const BACKSPACE = 259
const INSERT = 260
const DELETE = 261
const RIGHT = 262
const LEFT = 263
const DOWN = 264
const UP = 265
const PAGE_UP = 266
const PAGE_DOWN = 267
const HOME = 268
const END = 269
const CAPS_LOCK = 280
const SCROLL_LOCK = 281
const NUM_LOCK = 282
const PRINT_SCREEN = 283
const PAUSE = 284
const F1 = 290
const F2 = 291
const F3 = 292
const F4 = 293
const F5 = 294
const F6 = 295
const F7 = 296
const F8 = 297
const F9 = 298
const F10 = 299
const F11 = 300
const F12 = 301
const LEFT_SHIFT = 340
const LEFT_CONTROL = 341
const LEFT_ALT = 342
const LEFT_SUPER = 343
const RIGHT_SHIFT = 344
const RIGHT_CONTROL = 345
const RIGHT_ALT = 346
const RIGHT_SUPER = 347
const KB_MENU = 348
const LEFT_BRACKET = 91
const BACKSLASH = 92
const RIGHT_BRACKET = 93
const GRAVE = 96
const KP_0 = 320
const KP_1 = 321
const KP_2 = 322
const KP_3 = 323
const KP_4 = 324
const KP_5 = 325
const KP_6 = 326
const KP_7 = 327
const KP_8 = 328
const KP_9 = 329
const KP_DECIMAL = 330
const KP_DIVIDE = 331
const KP_MULTIPLY = 332
const KP_SUBTRACT = 333
const KP_ADD = 334
const KP_ENTER = 335
const KP_EQUAL = 336
const BACK = 4
const MENU = 82
const VOLUME_UP = 24
const VOLUME_DOWN = 25


text: sphere = (Point3D[[0.0, -0.5, 0.0], [0.01903, -0.490393, 0.095671], [0.0, -0.490393, 0.097545], [0.037329, -0.46194, 0.187665], [0.0, -0.46194, 0.191342], [0.054193, -0.415735, 0.272448], [0.0, -0.415735, 0.277785], [0.068975, -0.353553, 0.34676], [0.0, -0.353553, 0.353553], [0.081106, -0.277785, 0.407747], [0.0, -0.277785, 0.415735], [0.09012, -0.191342, 0.453064], [0.0, -0.191342, 0.46194], [0.095671, -0.097545, 0.48097], [0.0, -0.097545, 0.490393], [0.097545, 0.0, 0.490393], [0.0, 0.0, 0.5], [0.095671, 0.097545, 0.48097], [0.0, 0.097545, 0.490393], [0.09012, 0.191342, 0.453064], [0.0, 0.191342, 0.46194], [0.081106, 0.277785, 0.407747], [0.0, 0.277785, 0.415735], [0.068975, 0.353553, 0.34676], [0.0, 0.353553, 0.353553], [0.054193, 0.415735, 0.272447], [0.0, 0.415735, 0.277785], [0.037329, 0.46194, 0.187665], [0.0, 0.46194, 0.191342], [0.01903, 0.490393, 0.095671], [0.0, 0.490393, 0.097545], [0.0, 0.5, 0.0], [0.037329, -0.490393, 0.09012], [0.073223, -0.46194, 0.176777], [0.106304, -0.415735, 0.25664], [0.135299, -0.353553, 0.326641], [0.159095, -0.277785, 0.384089], [0.176777, -0.191342, 0.426777], [0.187665, -0.097545, 0.453064], [0.191342, 0.0, 0.46194], [0.187665, 0.097545, 0.453064], [0.176777, 0.191342, 0.426777], [0.159095, 0.277785, 0.384089], [0.135299, 0.353553, 0.326641], [0.106304, 0.415735, 0.25664], [0.073223, 0.46194, 0.176777], [0.037329, 0.490393, 0.09012], [0.054193, -0.490393, 0.081106], [0.106304, -0.46194, 0.159095], [0.154329, -0.415735, 0.23097], [0.196424, -0.353553, 0.293969], [0.23097, -0.277785, 0.345671], [0.25664, -0.191342, 0.384089], [0.272448, -0.097545, 0.407747], [0.277785, 0.0, 0.415735], [0.272448, 0.097545, 0.407747], [0.25664, 0.191342, 0.384089], [0.23097, 0.277785, 0.345671], [0.196424, 0.353553, 0.293969], [0.154329, 0.415735, 0.23097], [0.106304, 0.46194, 0.159095], [0.054193, 0.490393, 0.081106], [0.068975, -0.490393, 0.068975], [0.135299, -0.46194, 0.135299], [0.196424, -0.415735, 0.196424], [0.25, -0.353553, 0.25], [0.293969, -0.277785, 0.293969], [0.326641, -0.191342, 0.326641], [0.34676, -0.097545, 0.34676], [0.353553, 0.0, 0.353553], [0.34676, 0.097545, 0.34676], [0.326641, 0.191342, 0.326641], [0.293969, 0.277785, 0.293969], [0.25, 0.353553, 0.25], [0.196424, 0.415735, 0.196424], [0.135299, 0.46194, 0.135299], [0.068975, 0.490393, 0.068975], [0.081106, -0.490393, 0.054193], [0.159095, -0.46194, 0.106304], [0.23097, -0.415735, 0.154329], [0.293969, -0.353553, 0.196424], [0.345671, -0.277785, 0.23097], [0.384089, -0.191342, 0.25664], [0.407747, -0.097545, 0.272448], [0.415735, 0.0, 0.277785], [0.407747, 0.097545, 0.272448], [0.384089, 0.191342, 0.25664], [0.345671, 0.277785, 0.23097], [0.293969, 0.353553, 0.196424], [0.23097, 0.415735, 0.154329], [0.159095, 0.46194, 0.106304], [0.081106, 0.490393, 0.054193], [0.09012, -0.490393, 0.037329], [0.176777, -0.46194, 0.073223], [0.25664, -0.415735, 0.106304], [0.326641, -0.353553, 0.135299], [0.384089, -0.277785, 0.159095], [0.426777, -0.191342, 0.176777], [0.453064, -0.097545, 0.187665], [0.46194, 0.0, 0.191342], [0.453064, 0.097545, 0.187665], [0.426777, 0.191342, 0.176777], [0.384089, 0.277785, 0.159095], [0.326641, 0.353553, 0.135299], [0.25664, 0.415735, 0.106304], [0.176777, 0.46194, 0.073223], [0.09012, 0.490393, 0.037329], [0.095671, -0.490393, 0.01903], [0.187665, -0.46194, 0.037329], [0.272448, -0.415735, 0.054193], [0.34676, -0.353553, 0.068975], [0.407747, -0.277785, 0.081106], [0.453064, -0.191342, 0.09012], [0.48097, -0.097545, 0.095671], [0.490393, 0.0, 0.097545], [0.48097, 0.097545, 0.095671], [0.453064, 0.191342, 0.09012], [0.407747, 0.277785, 0.081106], [0.34676, 0.353553, 0.068975], [0.272448, 0.415735, 0.054193], [0.187665, 0.46194, 0.037329], [0.095671, 0.490393, 0.01903], [0.097545, -0.490393, 0.0], [0.191342, -0.46194, 0.0], [0.277785, -0.415735, 0.0], [0.353553, -0.353553, 0.0], [0.415735, -0.277785, 0.0], [0.46194, -0.191342, 0.0], [0.490393, -0.097545, 0.0], [0.5, 0.0, 0.0], [0.490393, 0.097545, 0.0], [0.46194, 0.191342, 0.0], [0.415735, 0.277785, 0.0], [0.353553, 0.353553, 0.0], [0.277785, 0.415735, 0.0], [0.191342, 0.46194, 0.0], [0.097545, 0.490393, 0.0], [0.095671, -0.490393, -0.01903], [0.187665, -0.46194, -0.037329], [0.272448, -0.415735, -0.054193], [0.34676, -0.353553, -0.068975], [0.407747, -0.277785, -0.081106], [0.453064, -0.191342, -0.09012], [0.48097, -0.097545, -0.095671], [0.490393, 0.0, -0.097545], [0.48097, 0.097545, -0.095671], [0.453064, 0.191342, -0.09012], [0.407747, 0.277785, -0.081106], [0.34676, 0.353553, -0.068975], [0.272447, 0.415735, -0.054193], [0.187665, 0.46194, -0.037329], [0.095671, 0.490393, -0.01903], [0.09012, -0.490393, -0.037329], [0.176777, -0.46194, -0.073223], [0.25664, -0.415735, -0.106304], [0.326641, -0.353553, -0.135299], [0.384089, -0.277785, -0.159095], [0.426777, -0.191342, -0.176777], [0.453064, -0.097545, -0.187665], [0.46194, 0.0, -0.191342], [0.453064, 0.097545, -0.187665], [0.426777, 0.191342, -0.176777], [0.384089, 0.277785, -0.159095], [0.326641, 0.353553, -0.135299], [0.25664, 0.415735, -0.106304], [0.176777, 0.46194, -0.073223], [0.09012, 0.490393, -0.037329], [0.081106, -0.490393, -0.054193], [0.159095, -0.46194, -0.106304], [0.23097, -0.415735, -0.154329], [0.293969, -0.353553, -0.196424], [0.345671, -0.277785, -0.23097], [0.384089, -0.191342, -0.25664], [0.407747, -0.097545, -0.272448], [0.415735, 0.0, -0.277785], [0.407747, 0.097545, -0.272448], [0.384089, 0.191342, -0.25664], [0.345671, 0.277785, -0.23097], [0.293969, 0.353553, -0.196424], [0.23097, 0.415735, -0.154329], [0.159095, 0.46194, -0.106304], [0.081106, 0.490393, -0.054193], [0.068975, -0.490393, -0.068975], [0.135299, -0.46194, -0.135299], [0.196424, -0.415735, -0.196424], [0.25, -0.353553, -0.25], [0.293969, -0.277785, -0.293969], [0.326641, -0.191342, -0.326641], [0.34676, -0.097545, -0.34676], [0.353553, 0.0, -0.353553], [0.34676, 0.097545, -0.34676], [0.326641, 0.191342, -0.326641], [0.293969, 0.277785, -0.293969], [0.25, 0.353553, -0.25], [0.196424, 0.415735, -0.196424], [0.135299, 0.46194, -0.135299], [0.068975, 0.490393, -0.068975], [0.054193, -0.490393, -0.081106], [0.106304, -0.46194, -0.159095], [0.154329, -0.415735, -0.23097], [0.196424, -0.353553, -0.293969], [0.23097, -0.277785, -0.345671], [0.25664, -0.191342, -0.384089], [0.272448, -0.097545, -0.407747], [0.277785, 0.0, -0.415735], [0.272448, 0.097545, -0.407747], [0.25664, 0.191342, -0.384089], [0.23097, 0.277785, -0.345671], [0.196424, 0.353553, -0.293969], [0.154329, 0.415735, -0.23097], [0.106304, 0.46194, -0.159095], [0.054193, 0.490393, -0.081106], [0.037329, -0.490393, -0.09012], [0.073223, -0.46194, -0.176777], [0.106304, -0.415735, -0.25664], [0.135299, -0.353553, -0.326641], [0.159095, -0.277785, -0.384089], [0.176777, -0.191342, -0.426777], [0.187665, -0.097545, -0.453064], [0.191342, 0.0, -0.46194], [0.187665, 0.097545, -0.453064], [0.176777, 0.191342, -0.426777], [0.159095, 0.277785, -0.384089], [0.135299, 0.353553, -0.326641], [0.106304, 0.415735, -0.25664], [0.073223, 0.46194, -0.176777], [0.037329, 0.490393, -0.09012], [0.01903, -0.490393, -0.095671], [0.037329, -0.46194, -0.187665], [0.054193, -0.415735, -0.272448], [0.068975, -0.353553, -0.34676], [0.081106, -0.277785, -0.407747], [0.09012, -0.191342, -0.453064], [0.095671, -0.097545, -0.48097], [0.097545, 0.0, -0.490393], [0.095671, 0.097545, -0.48097], [0.09012, 0.191342, -0.453064], [0.081106, 0.277785, -0.407747], [0.068975, 0.353553, -0.34676], [0.054193, 0.415735, -0.272448], [0.037329, 0.46194, -0.187665], [0.01903, 0.490393, -0.095671], [0.0, -0.490393, -0.097545], [0.0, -0.46194, -0.191342], [0.0, -0.415735, -0.277785], [0.0, -0.353553, -0.353553], [0.0, -0.277785, -0.415735], [0.0, -0.191342, -0.46194], [0.0, -0.097545, -0.490393], [0.0, 0.0, -0.5], [0.0, 0.097545, -0.490393], [0.0, 0.191342, -0.46194], [0.0, 0.277785, -0.415735], [0.0, 0.353553, -0.353553], [0.0, 0.415735, -0.277785], [0.0, 0.46194, -0.191342], [0.0, 0.490393, -0.097545], [-0.01903, -0.490393, -0.095671], [-0.037329, -0.46194, -0.187665], [-0.054193, -0.415735, -0.272448], [-0.068975, -0.353553, -0.34676], [-0.081106, -0.277785, -0.407747], [-0.09012, -0.191342, -0.453064], [-0.095671, -0.097545, -0.48097], [-0.097545, 0.0, -0.490393], [-0.095671, 0.097545, -0.48097], [-0.09012, 0.191342, -0.453064], [-0.081106, 0.277785, -0.407747], [-0.068975, 0.353553, -0.34676], [-0.054193, 0.415735, -0.272447], [-0.037329, 0.46194, -0.187665], [-0.01903, 0.490393, -0.095671], [-0.037329, -0.490393, -0.09012], [-0.073223, -0.46194, -0.176777], [-0.106304, -0.415735, -0.25664], [-0.135299, -0.353553, -0.326641], [-0.159095, -0.277785, -0.384089], [-0.176777, -0.191342, -0.426777], [-0.187665, -0.097545, -0.453064], [-0.191342, 0.0, -0.46194], [-0.187665, 0.097545, -0.453064], [-0.176777, 0.191342, -0.426777], [-0.159095, 0.277785, -0.384089], [-0.135299, 0.353553, -0.326641], [-0.106304, 0.415735, -0.25664], [-0.073223, 0.46194, -0.176777], [-0.037329, 0.490393, -0.09012], [-0.054193, -0.490393, -0.081106], [-0.106304, -0.46194, -0.159095], [-0.154329, -0.415735, -0.23097], [-0.196424, -0.353553, -0.293969], [-0.23097, -0.277785, -0.345671], [-0.25664, -0.191342, -0.384089], [-0.272448, -0.097545, -0.407747], [-0.277785, 0.0, -0.415735], [-0.272448, 0.097545, -0.407747], [-0.25664, 0.191342, -0.384089], [-0.23097, 0.277785, -0.345671], [-0.196424, 0.353553, -0.293969], [-0.154329, 0.415735, -0.23097], [-0.106304, 0.46194, -0.159095], [-0.054193, 0.490393, -0.081106], [-0.068975, -0.490393, -0.068975], [-0.135299, -0.46194, -0.135299], [-0.196424, -0.415735, -0.196424], [-0.25, -0.353553, -0.25], [-0.293969, -0.277785, -0.293969], [-0.326641, -0.191342, -0.326641], [-0.34676, -0.097545, -0.34676], [-0.353553, 0.0, -0.353553], [-0.34676, 0.097545, -0.34676], [-0.326641, 0.191342, -0.326641], [-0.293969, 0.277785, -0.293969], [-0.25, 0.353553, -0.25], [-0.196424, 0.415735, -0.196424], [-0.135299, 0.46194, -0.135299], [-0.068975, 0.490393, -0.068975], [-0.081106, -0.490393, -0.054193], [-0.159095, -0.46194, -0.106304], [-0.23097, -0.415735, -0.154329], [-0.293969, -0.353553, -0.196424], [-0.345671, -0.277785, -0.23097], [-0.384089, -0.191342, -0.25664], [-0.407747, -0.097545, -0.272447], [-0.415735, 0.0, -0.277785], [-0.407747, 0.097545, -0.272447], [-0.384089, 0.191342, -0.25664], [-0.345671, 0.277785, -0.23097], [-0.293969, 0.353553, -0.196424], [-0.23097, 0.415735, -0.154329], [-0.159095, 0.46194, -0.106304], [-0.081106, 0.490393, -0.054193], [-0.09012, -0.490393, -0.037329], [-0.176777, -0.46194, -0.073223], [-0.25664, -0.415735, -0.106304], [-0.326641, -0.353553, -0.135299], [-0.384089, -0.277785, -0.159095], [-0.426777, -0.191342, -0.176777], [-0.453064, -0.097545, -0.187665], [-0.46194, 0.0, -0.191342], [-0.453064, 0.097545, -0.187665], [-0.426777, 0.191342, -0.176777], [-0.384089, 0.277785, -0.159095], [-0.326641, 0.353553, -0.135299], [-0.25664, 0.415735, -0.106304], [-0.176777, 0.46194, -0.073223], [-0.09012, 0.490393, -0.037329], [-0.095671, -0.490393, -0.01903], [-0.187665, -0.46194, -0.037329], [-0.272448, -0.415735, -0.054193], [-0.34676, -0.353553, -0.068975], [-0.407747, -0.277785, -0.081106], [-0.453064, -0.191342, -0.09012], [-0.48097, -0.097545, -0.095671], [-0.490393, 0.0, -0.097545], [-0.48097, 0.097545, -0.095671], [-0.453064, 0.191342, -0.09012], [-0.407747, 0.277785, -0.081106], [-0.34676, 0.353553, -0.068975], [-0.272447, 0.415735, -0.054193], [-0.187665, 0.46194, -0.037329], [-0.095671, 0.490393, -0.01903], [-0.097545, -0.490393, 0.0], [-0.191342, -0.46194, 0.0], [-0.277785, -0.415735, 0.0], [-0.353553, -0.353553, 0.0], [-0.415735, -0.277785, 0.0], [-0.46194, -0.191342, 0.0], [-0.490393, -0.097545, 0.0], [-0.5, 0.0, 0.0], [-0.490393, 0.097545, 0.0], [-0.46194, 0.191342, 0.0], [-0.415735, 0.277785, 0.0], [-0.353553, 0.353553, 0.0], [-0.277785, 0.415735, 0.0], [-0.191342, 0.46194, 0.0], [-0.097545, 0.490393, 0.0], [-0.095671, -0.490393, 0.01903], [-0.187665, -0.46194, 0.037329], [-0.272448, -0.415735, 0.054193], [-0.34676, -0.353553, 0.068975], [-0.407747, -0.277785, 0.081106], [-0.453064, -0.191342, 0.09012], [-0.48097, -0.097545, 0.095671], [-0.490393, 0.0, 0.097545], [-0.48097, 0.097545, 0.095671], [-0.453064, 0.191342, 0.09012], [-0.407747, 0.277785, 0.081106], [-0.34676, 0.353553, 0.068975], [-0.272447, 0.415735, 0.054193], [-0.187665, 0.46194, 0.037329], [-0.095671, 0.490393, 0.01903], [-0.09012, -0.490393, 0.037329], [-0.176777, -0.46194, 0.073223], [-0.25664, -0.415735, 0.106304], [-0.326641, -0.353553, 0.135299], [-0.384089, -0.277785, 0.159095], [-0.426777, -0.191342, 0.176777], [-0.453064, -0.097545, 0.187665], [-0.46194, 0.0, 0.191342], [-0.453064, 0.097545, 0.187665], [-0.426777, 0.191342, 0.176777], [-0.384089, 0.277785, 0.159095], [-0.326641, 0.353553, 0.135299], [-0.25664, 0.415735, 0.106304], [-0.176777, 0.46194, 0.073223], [-0.09012, 0.490393, 0.037329], [-0.081106, -0.490393, 0.054193], [-0.159095, -0.46194, 0.106304], [-0.23097, -0.415735, 0.154329], [-0.293969, -0.353553, 0.196424], [-0.345671, -0.277785, 0.23097], [-0.384089, -0.191342, 0.25664], [-0.407747, -0.097545, 0.272448], [-0.415735, 0.0, 0.277785], [-0.407747, 0.097545, 0.272448], [-0.384089, 0.191342, 0.25664], [-0.345671, 0.277785, 0.23097], [-0.293969, 0.353553, 0.196424], [-0.23097, 0.415735, 0.154329], [-0.159095, 0.46194, 0.106304], [-0.081106, 0.490393, 0.054193], [-0.068975, -0.490393, 0.068975], [-0.135299, -0.46194, 0.135299], [-0.196424, -0.415735, 0.196424], [-0.25, -0.353553, 0.25], [-0.293969, -0.277785, 0.293969], [-0.326641, -0.191342, 0.326641], [-0.34676, -0.097545, 0.34676], [-0.353553, 0.0, 0.353554], [-0.34676, 0.097545, 0.34676], [-0.326641, 0.191342, 0.326641], [-0.293969, 0.277785, 0.293969], [-0.25, 0.353553, 0.25], [-0.196424, 0.415735, 0.196424], [-0.135299, 0.46194, 0.135299], [-0.068975, 0.490393, 0.068975], [-0.054193, -0.490393, 0.081106], [-0.106304, -0.46194, 0.159095], [-0.154329, -0.415735, 0.23097], [-0.196424, -0.353553, 0.293969], [-0.23097, -0.277785, 0.345671], [-0.25664, -0.191342, 0.384089], [-0.272448, -0.097545, 0.407747], [-0.277785, 0.0, 0.415735], [-0.272448, 0.097545, 0.407747], [-0.25664, 0.191342, 0.384089], [-0.23097, 0.277785, 0.345671], [-0.196424, 0.353553, 0.293969], [-0.154329, 0.415735, 0.23097], [-0.106304, 0.46194, 0.159095], [-0.054193, 0.490393, 0.081106], [-0.037329, -0.490393, 0.09012], [-0.073223, -0.46194, 0.176777], [-0.106304, -0.415735, 0.25664], [-0.135299, -0.353553, 0.326641], [-0.159095, -0.277785, 0.384089], [-0.176777, -0.191342, 0.426777], [-0.187665, -0.097545, 0.453064], [-0.191342, 0.0, 0.46194], [-0.187665, 0.097545, 0.453064], [-0.176777, 0.191342, 0.426777], [-0.159095, 0.277785, 0.384089], [-0.135299, 0.353553, 0.326641], [-0.106304, 0.415735, 0.25664], [-0.073223, 0.46194, 0.176777], [-0.037329, 0.490393, 0.09012], [-0.01903, -0.490393, 0.095671], [-0.037329, -0.46194, 0.187665], [-0.054193, -0.415735, 0.272448], [-0.068975, -0.353553, 0.34676], [-0.081106, -0.277785, 0.407747], [-0.09012, -0.191342, 0.453064], [-0.095671, -0.097545, 0.48097], [-0.097545, 0.0, 0.490393], [-0.095671, 0.097545, 0.48097], [-0.09012, 0.191342, 0.453064], [-0.081106, 0.277785, 0.407747], [-0.068975, 0.353553, 0.34676], [-0.054193, 0.415735, 0.272448], [-0.037329, 0.46194, 0.187665], [-0.01903, 0.490393, 0.095671]], [[3, 1, 2], [3, 2, 4, 5], [5, 4, 6, 7], [7, 6, 8, 9], [9, 8, 10, 11], [11, 10, 12, 13], [13, 12, 14, 15], [15, 14, 16, 17], [17, 16, 18, 19], [19, 18, 20, 21], [21, 20, 22, 23], [23, 22, 24, 25], [25, 24, 26, 27], [27, 26, 28, 29], [29, 28, 30, 31], [31, 30, 32], [2, 1, 33], [2, 33, 34, 4], [4, 34, 35, 6], [6, 35, 36, 8], [8, 36, 37, 10], [10, 37, 38, 12], [12, 38, 39, 14], [14, 39, 40, 16], [16, 40, 41, 18], [18, 41, 42, 20], [20, 42, 43, 22], [22, 43, 44, 24], [24, 44, 45, 26], [26, 45, 46, 28], [28, 46, 47, 30], [30, 47, 32], [33, 1, 48], [33, 48, 49, 34], [34, 49, 50, 35], [35, 50, 51, 36], [36, 51, 52, 37], [37, 52, 53, 38], [38, 53, 54, 39], [39, 54, 55, 40], [40, 55, 56, 41], [41, 56, 57, 42], [42, 57, 58, 43], [43, 58, 59, 44], [44, 59, 60, 45], [45, 60, 61, 46], [46, 61, 62, 47], [47, 62, 32], [48, 1, 63], [48, 63, 64, 49], [49, 64, 65, 50], [50, 65, 66, 51], [51, 66, 67, 52], [52, 67, 68, 53], [53, 68, 69, 54], [54, 69, 70, 55], [55, 70, 71, 56], [56, 71, 72, 57], [57, 72, 73, 58], [58, 73, 74, 59], [59, 74, 75, 60], [60, 75, 76, 61], [61, 76, 77, 62], [62, 77, 32], [63, 1, 78], [63, 78, 79, 64], [64, 79, 80, 65], [65, 80, 81, 66], [66, 81, 82, 67], [67, 82, 83, 68], [68, 83, 84, 69], [69, 84, 85, 70], [70, 85, 86, 71], [71, 86, 87, 72], [72, 87, 88, 73], [73, 88, 89, 74], [74, 89, 90, 75], [75, 90, 91, 76], [76, 91, 92, 77], [77, 92, 32], [78, 1, 93], [78, 93, 94, 79], [79, 94, 95, 80], [80, 95, 96, 81], [81, 96, 97, 82], [82, 97, 98, 83], [83, 98, 99, 84], [84, 99, 100, 85], [85, 100, 101, 86], [86, 101, 102, 87], [87, 102, 103, 88], [88, 103, 104, 89], [89, 104, 105, 90], [90, 105, 106, 91], [91, 106, 107, 92], [92, 107, 32], [93, 1, 108], [93, 108, 109, 94], [94, 109, 110, 95], [95, 110, 111, 96], [96, 111, 112, 97], [97, 112, 113, 98], [98, 113, 114, 99], [99, 114, 115, 100], [100, 115, 116, 101], [101, 116, 117, 102], [102, 117, 118, 103], [103, 118, 119, 104], [104, 119, 120, 105], [105, 120, 121, 106], [106, 121, 122, 107], [107, 122, 32], [108, 1, 123], [108, 123, 124, 109], [109, 124, 125, 110], [110, 125, 126, 111], [111, 126, 127, 112], [112, 127, 128, 113], [113, 128, 129, 114], [114, 129, 130, 115], [115, 130, 131, 116], [116, 131, 132, 117], [117, 132, 133, 118], [118, 133, 134, 119], [119, 134, 135, 120], [120, 135, 136, 121], [121, 136, 137, 122], [122, 137, 32], [123, 1, 138], [123, 138, 139, 124], [124, 139, 140, 125], [125, 140, 141, 126], [126, 141, 142, 127], [127, 142, 143, 128], [128, 143, 144, 129], [129, 144, 145, 130], [130, 145, 146, 131], [131, 146, 147, 132], [132, 147, 148, 133], [133, 148, 149, 134], [134, 149, 150, 135], [135, 150, 151, 136], [136, 151, 152, 137], [137, 152, 32], [138, 1, 153], [138, 153, 154, 139], [139, 154, 155, 140], [140, 155, 156, 141], [141, 156, 157, 142], [142, 157, 158, 143], [143, 158, 159, 144], [144, 159, 160, 145], [145, 160, 161, 146], [146, 161, 162, 147], [147, 162, 163, 148], [148, 163, 164, 149], [149, 164, 165, 150], [150, 165, 166, 151], [151, 166, 167, 152], [152, 167, 32], [153, 1, 168], [153, 168, 169, 154], [154, 169, 170, 155], [155, 170, 171, 156], [156, 171, 172, 157], [157, 172, 173, 158], [158, 173, 174, 159], [159, 174, 175, 160], [160, 175, 176, 161], [161, 176, 177, 162], [162, 177, 178, 163], [163, 178, 179, 164], [164, 179, 180, 165], [165, 180, 181, 166], [166, 181, 182, 167], [167, 182, 32], [168, 1, 183], [168, 183, 184, 169], [169, 184, 185, 170], [170, 185, 186, 171], [171, 186, 187, 172], [172, 187, 188, 173], [173, 188, 189, 174], [174, 189, 190, 175], [175, 190, 191, 176], [176, 191, 192, 177], [177, 192, 193, 178], [178, 193, 194, 179], [179, 194, 195, 180], [180, 195, 196, 181], [181, 196, 197, 182], [182, 197, 32], [183, 1, 198], [183, 198, 199, 184], [184, 199, 200, 185], [185, 200, 201, 186], [186, 201, 202, 187], [187, 202, 203, 188], [188, 203, 204, 189], [189, 204, 205, 190], [190, 205, 206, 191], [191, 206, 207, 192], [192, 207, 208, 193], [193, 208, 209, 194], [194, 209, 210, 195], [195, 210, 211, 196], [196, 211, 212, 197], [197, 212, 32], [198, 1, 213], [198, 213, 214, 199], [199, 214, 215, 200], [200, 215, 216, 201], [201, 216, 217, 202], [202, 217, 218, 203], [203, 218, 219, 204], [204, 219, 220, 205], [205, 220, 221, 206], [206, 221, 222, 207], [207, 222, 223, 208], [208, 223, 224, 209], [209, 224, 225, 210], [210, 225, 226, 211], [211, 226, 227, 212], [212, 227, 32], [213, 1, 228], [213, 228, 229, 214], [214, 229, 230, 215], [215, 230, 231, 216], [216, 231, 232, 217], [217, 232, 233, 218], [218, 233, 234, 219], [219, 234, 235, 220], [220, 235, 236, 221], [221, 236, 237, 222], [222, 237, 238, 223], [223, 238, 239, 224], [224, 239, 240, 225], [225, 240, 241, 226], [226, 241, 242, 227], [227, 242, 32], [228, 1, 243], [228, 243, 244, 229], [229, 244, 245, 230], [230, 245, 246, 231], [231, 246, 247, 232], [232, 247, 248, 233], [233, 248, 249, 234], [234, 249, 250, 235], [235, 250, 251, 236], [236, 251, 252, 237], [237, 252, 253, 238], [238, 253, 254, 239], [239, 254, 255, 240], [240, 255, 256, 241], [241, 256, 257, 242], [242, 257, 32], [243, 1, 258], [243, 258, 259, 244], [244, 259, 260, 245], [245, 260, 261, 246], [246, 261, 262, 247], [247, 262, 263, 248], [248, 263, 264, 249], [249, 264, 265, 250], [250, 265, 266, 251], [251, 266, 267, 252], [252, 267, 268, 253], [253, 268, 269, 254], [254, 269, 270, 255], [255, 270, 271, 256], [256, 271, 272, 257], [257, 272, 32], [258, 1, 273], [258, 273, 274, 259], [259, 274, 275, 260], [260, 275, 276, 261], [261, 276, 277, 262], [262, 277, 278, 263], [263, 278, 279, 264], [264, 279, 280, 265], [265, 280, 281, 266], [266, 281, 282, 267], [267, 282, 283, 268], [268, 283, 284, 269], [269, 284, 285, 270], [270, 285, 286, 271], [271, 286, 287, 272], [272, 287, 32], [273, 1, 288], [273, 288, 289, 274], [274, 289, 290, 275], [275, 290, 291, 276], [276, 291, 292, 277], [277, 292, 293, 278], [278, 293, 294, 279], [279, 294, 295, 280], [280, 295, 296, 281], [281, 296, 297, 282], [282, 297, 298, 283], [283, 298, 299, 284], [284, 299, 300, 285], [285, 300, 301, 286], [286, 301, 302, 287], [287, 302, 32], [288, 1, 303], [288, 303, 304, 289], [289, 304, 305, 290], [290, 305, 306, 291], [291, 306, 307, 292], [292, 307, 308, 293], [293, 308, 309, 294], [294, 309, 310, 295], [295, 310, 311, 296], [296, 311, 312, 297], [297, 312, 313, 298], [298, 313, 314, 299], [299, 314, 315, 300], [300, 315, 316, 301], [301, 316, 317, 302], [302, 317, 32], [303, 1, 318], [303, 318, 319, 304], [304, 319, 320, 305], [305, 320, 321, 306], [306, 321, 322, 307], [307, 322, 323, 308], [308, 323, 324, 309], [309, 324, 325, 310], [310, 325, 326, 311], [311, 326, 327, 312], [312, 327, 328, 313], [313, 328, 329, 314], [314, 329, 330, 315], [315, 330, 331, 316], [316, 331, 332, 317], [317, 332, 32], [318, 1, 333], [318, 333, 334, 319], [319, 334, 335, 320], [320, 335, 336, 321], [321, 336, 337, 322], [322, 337, 338, 323], [323, 338, 339, 324], [324, 339, 340, 325], [325, 340, 341, 326], [326, 341, 342, 327], [327, 342, 343, 328], [328, 343, 344, 329], [329, 344, 345, 330], [330, 345, 346, 331], [331, 346, 347, 332], [332, 347, 32], [333, 1, 348], [333, 348, 349, 334], [334, 349, 350, 335], [335, 350, 351, 336], [336, 351, 352, 337], [337, 352, 353, 338], [338, 353, 354, 339], [339, 354, 355, 340], [340, 355, 356, 341], [341, 356, 357, 342], [342, 357, 358, 343], [343, 358, 359, 344], [344, 359, 360, 345], [345, 360, 361, 346], [346, 361, 362, 347], [347, 362, 32], [348, 1, 363], [348, 363, 364, 349], [349, 364, 365, 350], [350, 365, 366, 351], [351, 366, 367, 352], [352, 367, 368, 353], [353, 368, 369, 354], [354, 369, 370, 355], [355, 370, 371, 356], [356, 371, 372, 357], [357, 372, 373, 358], [358, 373, 374, 359], [359, 374, 375, 360], [360, 375, 376, 361], [361, 376, 377, 362], [362, 377, 32], [363, 1, 378], [363, 378, 379, 364], [364, 379, 380, 365], [365, 380, 381, 366], [366, 381, 382, 367], [367, 382, 383, 368], [368, 383, 384, 369], [369, 384, 385, 370], [370, 385, 386, 371], [371, 386, 387, 372], [372, 387, 388, 373], [373, 388, 389, 374], [374, 389, 390, 375], [375, 390, 391, 376], [376, 391, 392, 377], [377, 392, 32], [378, 1, 393], [378, 393, 394, 379], [379, 394, 395, 380], [380, 395, 396, 381], [381, 396, 397, 382], [382, 397, 398, 383], [383, 398, 399, 384], [384, 399, 400, 385], [385, 400, 401, 386], [386, 401, 402, 387], [387, 402, 403, 388], [388, 403, 404, 389], [389, 404, 405, 390], [390, 405, 406, 391], [391, 406, 407, 392], [392, 407, 32], [393, 1, 408], [393, 408, 409, 394], [394, 409, 410, 395], [395, 410, 411, 396], [396, 411, 412, 397], [397, 412, 413, 398], [398, 413, 414, 399], [399, 414, 415, 400], [400, 415, 416, 401], [401, 416, 417, 402], [402, 417, 418, 403], [403, 418, 419, 404], [404, 419, 420, 405], [405, 420, 421, 406], [406, 421, 422, 407], [407, 422, 32], [408, 1, 423], [408, 423, 424, 409], [409, 424, 425, 410], [410, 425, 426, 411], [411, 426, 427, 412], [412, 427, 428, 413], [413, 428, 429, 414], [414, 429, 430, 415], [415, 430, 431, 416], [416, 431, 432, 417], [417, 432, 433, 418], [418, 433, 434, 419], [419, 434, 435, 420], [420, 435, 436, 421], [421, 436, 437, 422], [422, 437, 32], [423, 1, 438], [423, 438, 439, 424], [424, 439, 440, 425], [425, 440, 441, 426], [426, 441, 442, 427], [427, 442, 443, 428], [428, 443, 444, 429], [429, 444, 445, 430], [430, 445, 446, 431], [431, 446, 447, 432], [432, 447, 448, 433], [433, 448, 449, 434], [434, 449, 450, 435], [435, 450, 451, 436], [436, 451, 452, 437], [437, 452, 32], [438, 1, 453], [438, 453, 454, 439], [439, 454, 455, 440], [440, 455, 456, 441], [441, 456, 457, 442], [442, 457, 458, 443], [443, 458, 459, 444], [444, 459, 460, 445], [445, 460, 461, 446], [446, 461, 462, 447], [447, 462, 463, 448], [448, 463, 464, 449], [449, 464, 465, 450], [450, 465, 466, 451], [451, 466, 467, 452], [452, 467, 32], [453, 1, 468], [453, 468, 469, 454], [454, 469, 470, 455], [455, 470, 471, 456], [456, 471, 472, 457], [457, 472, 473, 458], [458, 473, 474, 459], [459, 474, 475, 460], [460, 475, 476, 461], [461, 476, 477, 462], [462, 477, 478, 463], [463, 478, 479, 464], [464, 479, 480, 465], [465, 480, 481, 466], [466, 481, 482, 467], [467, 482, 32], [468, 1, 3], [468, 3, 5, 469], [469, 5, 7, 470], [470, 7, 9, 471], [471, 9, 11, 472], [472, 11, 13, 473], [473, 13, 15, 474], [474, 15, 17, 475], [475, 17, 19, 476], [476, 19, 21, 477], [477, 21, 23, 478], [478, 23, 25, 479], [479, 25, 27, 480], [480, 27, 29, 481], [481, 29, 31, 482], [482, 31, 32]])


text:
const pyjuliacallmodule = pynew()
const pyJuliaError = pynew()

function init_juliacall()
    # ensure the 'juliacall' module exists
    # this means that Python code can do "import juliacall" and it will work regardless of
    # whether Python is embedded in Julia or vice-versa
    jl = pyjuliacallmodule
    sys = pysysmodule
    if C.CTX.is_embedded
        # in this case, Julia is being embedded into Python by juliacall, which already exists
        pycopy!(jl, sys.modules["juliacall"])
        @assert pystr_asstring(jl.CONFIG["meta"]) == Deps.meta_file()
        @assert pystr_asstring(jl.__version__) == string(VERSION)
    elseif "juliacall" in sys.modules
        # otherwise, Python is being embedded into Julia by PythonCall, so should not exist
        error("'juliacall' module already exists")
    else
        # create the juliacall module and save it in sys.modules
        pycopy!(jl, pytype(sys)("juliacall"))
        jl.CONFIG = pydict(embedded=true, meta=Deps.meta_file())
        jl.__version__ = string(VERSION)
        jl.__path__ = pylist((joinpath(dirname(dirname(pathof(PythonCall))), "juliacall"),))
        sys.modules["juliacall"] = jl
    end
end

function init_juliacall_2()
    jl = pyjuliacallmodule
    jl.Main = Main
    jl.Core = Core
    jl.Base = Base
    jl.Pkg = Pkg
    if !C.CTX.is_embedded
        pybuiltins.exec(pybuiltins.compile("from .all import *", "$(@__FILE__):$(@__LINE__)", "exec"), jl.__dict__)
    end
    pycopy!(pyJuliaError, jl.JuliaError)
    C.POINTERS.PyExc_JuliaError = incref(getptr(pyJuliaError))
end

function pyconvert_rule_jlas(::Type{T}, x::Py) where {T}
    # get the type
    t = x.type
    if !pyisjl(t)
        pydel!(t)
        return pyconvert_unconverted()
    end
    S = _pyjl_getvalue(t)
    pydel!(t)
    S isa Type || return pyconvert_unconverted()
    # convert x to S, then to T
    r = pytryconvert(S, x)
    if pyconvert_isunconverted(r)
        return pyconvert_unconverted()
    elseif T == Any || S <: T
        return r
    else
        return pyconvert_tryconvert(T, pyconvert_result(r))
    end
end


text:
using MultiAgentPOMDPs
using POMDPs
using Test

mutable struct X <: JointMDP{Vector{Float64},Vector{Bool}} end
abstract type Z <: JointMDP{Vector{Float64},Vector{Int}} end
mutable struct Y <: Z end

@testset "MultiAgentPOMDPs.jl" begin
    @testset "inference" begin
        @test_throws ErrorException statetype(Int)
        @test_throws ErrorException actiontype(Int)
        @test_throws ErrorException obstype(Int)

        @test statetype(X) == Vector{Float64}
        @test statetype(Y) == Vector{Float64}
        @test actiontype(X) == Vector{Bool}
        @test actiontype(Y) == Vector{Int}
    end
end