text:
using PrintTypesTersely
using Test

struct A{T}
end

struct B{C,D}
	data::D
end

const B{C} = B{C, D} where {D}

@testset "PrintTypesTersely.jl" begin

	@testset "testing modes" begin
		PrintTypesTersely.on()
		@test PrintTypesTersely._terseprint[] == true
		PrintTypesTersely.off()
		@test PrintTypesTersely._terseprint[] == false
        PrintTypesTersely.with_state(true) do
			@test PrintTypesTersely._terseprint[] == true
        end
		PrintTypesTersely.with_state(false) do
			@test PrintTypesTersely._terseprint[] == false
        end
    end

    @testset "testing terseprint on" begin
        PrintTypesTersely.with_state(true) do
            @test repr(A{Vector{Int}}) == "A{…}"
            @test repr(A{Union{Int, Missing}}) == "A{…}"
            @test repr(B{Int, Float32}) == "B{…}"
            @test repr(B{Int}) == "B{…}"
            @test repr(DenseMatrix{Int}) == "DenseArray{…}"
            @test repr(StridedVector{Int}) == "Union{DenseArray{…}, ReinterpretArray{…}, ReshapedArray{…}, SubArray{…}}"
            @test repr(StridedVecOrMat{Int}) == "Union{DenseArray{…}, DenseArray{…}, ReinterpretArray{…}, ReinterpretArray{…}, ReshapedArray{…}, ReshapedArray{…}, SubArray{…}, SubArray{…}}"
            @test repr(Union{StridedVector{Int}, Int}) == "Union{Int64{…}, DenseArray{…}, ReinterpretArray{…}, ReshapedArray{…}, SubArray{…}}"
            @test repr(UnionAll(TypeVar(:T,Integer), Array)) == "Array{…}"
            @test repr(Union{StridedMatrix{Int}, StridedArray{Int,3}}) == "Union{DenseArray{…}, DenseArray{…}, ReinterpretArray{…}, ReinterpretArray{…}, ReshapedArray{…}, ReshapedArray{…}, SubArray{…}, SubArray{…}}"
			t_ = TypeVar(:_,Integer)
			@test repr(UnionAll(t_, Array{t_})) == "Array{…}"
		    u_ = TypeVar(:_,Complex)
			@test repr(UnionAll(u_, Array{UnionAll(t_, Array{t_, u_})})) == "Array{…}"
        end
    end

    @testset "testing terseprint off" begin
        PrintTypesTersely.with_state(false) do
            @test repr(A{Vector{Int}}) == (VERSION < v"1.6.0-" ? "A{Array{Int64,1}}" : "A{Vector{Int64}}")
			@test repr(A{Union{Int, Missing}}) == "A{Union{Missing, Int64}}"
			@test repr(B{Int, Float32}) == (VERSION < v"1.6.0-" ? "B{Int64,Float32}" : "B{Int64, Float32}")
            @test repr(B{Int}) == (VERSION < v"1.6.0-" ? "B{Int64,D} where D" : "B{Int64, D} where D")
            @test repr(DenseMatrix{Int}) == (VERSION < v"1.6.0-" ? "DenseArray{Int64,2}" : "DenseMatrix{Int64}")
            @test repr(StridedVector{Int}) == (VERSION < v"1.6.0-" ? "StridedArray{Int64, 1}" : "StridedVector{Int64}")
            @test repr(StridedVecOrMat{Int}) == "StridedVecOrMat{Int64}"
			expected_1_3 = "Union{Int64, DenseArray{Int64,1}, Base.ReinterpretArray{Int64,1,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,1,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,1,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_4 = "Union{Int64, DenseArray{Int64,1}, Base.ReinterpretArray{Int64,1,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,1,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,1,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_5 = "Union{Int64, DenseArray{Int64,1}, Base.ReinterpretArray{Int64,1,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,1,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,1,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex, Base.ReshapedArray{T,N,A,Tuple{}} where A<:AbstractUnitRange where N where T},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_6 = "Union{Int64, StridedVector{Int64}}"
			expected = VERSION < v"1.6.0-" ? VERSION < v"1.5" ? VERSION < v"1.4" ? expected_1_3 : expected_1_4 : expected_1_5 : expected_1_6
			@test repr(Union{StridedVector{Int}, Int}) == expected
			@test repr(UnionAll(TypeVar(:T,Integer), Array)) == "Array"
			expected_1_3_linux = "Union{StridedArray{Int64, 2}, DenseArray{Int64,3}, Base.ReinterpretArray{Int64,3,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,3,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,3,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_3_win = "Union{StridedArray{Int64, 3}, DenseArray{Int64,2}, Base.ReinterpretArray{Int64,2,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,2,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,2,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_4 = "Union{StridedArray{Int64, 2}, DenseArray{Int64,3}, Base.ReinterpretArray{Int64,3,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,3,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,3,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_5_linux = "Union{StridedArray{Int64, 2}, DenseArray{Int64,3}, Base.ReinterpretArray{Int64,3,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,3,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,3,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex, Base.ReshapedArray{T,N,A,Tuple{}} where A<:AbstractUnitRange where N where T},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_5_win = "Union{StridedArray{Int64, 3}, DenseArray{Int64,2}, Base.ReinterpretArray{Int64,2,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, Base.ReshapedArray{Int64,2,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray}, SubArray{Int64,2,A,I,L} where L where I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex, Base.ReshapedArray{T,N,A,Tuple{}} where A<:AbstractUnitRange where N where T},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, Base.ReshapedArray{T,N,A,MI} where MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64},N} where N} where A<:Union{Base.ReinterpretArray{T,N,S,A} where S where A<:Union{SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, SubArray{T,N,A,I,true} where I<:Union{Tuple{Vararg{Real,N} where N}, Tuple{AbstractUnitRange,Vararg{Any,N} where N}} where A<:DenseArray where N where T, DenseArray} where N where T, DenseArray}}"
			expected_1_6_linux = "Union{StridedMatrix{Int64}, StridedArray{Int64, 3}}"
			expected_1_6_win = "Union{DenseArray{Int64, 3}, Base.ReinterpretArray{Int64, 3, S, A, IsReshaped} where {A<:Union{SubArray{T, N, A, I, true} where {T, N, A<:DenseArray, I<:Union{Tuple{Vararg{Real, N} where N}, Tuple{AbstractUnitRange, Vararg{Any, N} where N}}}, DenseArray}, IsReshaped, S}, Base.ReshapedArray{Int64, 3, A, MI} where {A<:Union{Base.ReinterpretArray{T, N, S, A, IsReshaped} where {T, N, A<:Union{SubArray{T, N, A, I, true} where {T, N, A<:DenseArray, I<:Union{Tuple{Vararg{Real, N} where N}, Tuple{AbstractUnitRange, Vararg{Any, N} where N}}}, DenseArray}, IsReshaped, S}, SubArray{T, N, A, I, true} where {T, N, A<:DenseArray, I<:Union{Tuple{Vararg{Real, N} where N}, Tuple{AbstractUnitRange, Vararg{Any, N} where N}}}, DenseArray}, MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64}, N} where N}}, SubArray{Int64, 3, A, I, L} where {A<:Union{Base.ReinterpretArray{T, N, S, A, IsReshaped} where {T, N, A<:Union{SubArray{T, N, A, I, true} where {T, N, A<:DenseArray, I<:Union{Tuple{Vararg{Real, N} where N}, Tuple{AbstractUnitRange, Vararg{Any, N} where N}}}, DenseArray}, IsReshaped, S}, Base.ReshapedArray{T, N, A, MI} where {T, N, A<:Union{Base.ReinterpretArray{T, N, S, A, IsReshaped} where {T, N, A<:Union{SubArray{T, N, A, I, true} where {T, N, A<:DenseArray, I<:Union{Tuple{Vararg{Real, N} where N}, Tuple{AbstractUnitRange, Vararg{Any, N} where N}}}, DenseArray}, IsReshaped, S}, SubArray{T, N, A, I, true} where {T, N, A<:DenseArray, I<:Union{Tuple{Vararg{Real, N} where N}, Tuple{AbstractUnitRange, Vararg{Any, N} where N}}}, DenseArray}, MI<:Tuple{Vararg{Base.MultiplicativeInverses.SignedMultiplicativeInverse{Int64}, N} where N}}, DenseArray}, I<:Tuple{Vararg{Union{Int64, AbstractRange{Int64}, Base.AbstractCartesianIndex, Base.ReshapedArray{T, N, A, Tuple{}} where {T, N, A<:AbstractUnitRange}}, N} where N}, L}, StridedMatrix{Int64}}"
			expected = VERSION < v"1.6.0-" ? VERSION < v"1.5" ? VERSION < v"1.4" ? Sys.iswindows() ? expected_1_3_win : expected_1_3_linux : expected_1_4 : (Sys.iswindows() ? expected_1_5_win : expected_1_5_linux) : (Sys.iswindows() ? expected_1_6_win : expected_1_6_linux)
			@test repr(Union{StridedMatrix{Int}, StridedArray{Int,3}}) == expected
			t_ = TypeVar(:_,Integer)
			@test repr(UnionAll(t_, Array{t_})) == (VERSION < v"1.6.0-" ? "Array{_1,N} where N where _1<:Integer" : "Array{_1, N} where {_1<:Integer, N}")
			u_ = TypeVar(:_,Complex)
			@test repr(UnionAll(u_, Array{UnionAll(t_, Array{t_, u_})})) == (VERSION < v"1.6.0-" ?
				"Array{Array{_2,_1} where _2<:Integer,N} where N where _1<:Complex" :
				"Array{Array{_2, _1} where _2<:Integer, N} where {_1<:Complex, N}")
        end
    end

	# make it run Mill tests only for Julia 1.5 and higher because that's where Mil 2 works
	@static if VERSION >= v"1.5.0"
		include("mill_jsongrinder_integration.jl")
	end
end


text:
using ConditionalMean
using Base.Test

# use all(isapprox.(x,y)) elementwise isapprox of arrays

A=zeros(5,5)
A[:]=1:25
# all finite values

# write your own tests here
@time condmean(A[:],isfinite)[1] == mean(A)
@test condmean(A[:],isfinite)[1] == mean(A)
@time condmean(A[:],isfinite)[1] == mean(A)

f(x)=2x
c(x)=x>5
@test condmean(f,A,c)[2:5] == condmean(f,A,c,1)[2:5]
@test isnan(condmean(f,A,c)[1])
@test condmean(f,A,c,2) == mean(2*A,2)+5 # this fails for

# use a missing value
mv=-999.0
B=zeros(5,5)
B[:]=A[:]
B[3,:]=mv
@test condmean(B,mv,1) == mean(A,1)
B[:]=A[:]
B[2,:]=mv
@test condmean(B,mv,1) == mean(A,1)+0.25

# test using NaN and mapping the function through the mean
B[:]=A[:]
B[3,:]=NaN
# easy to predict for a linear operator
@test all(isapprox.(condmean(x->2*x,B,isfinite,1), 2*[3.0 8.0 13.0 18.0 23.0]))

# test the offsetting version
@show condmean(A,isfinite,2)
@show mean(A,2)
@test condmean(A,isfinite,2) == mean(A,2)

# test precision issues; first no missing values
B=Array{Float64}(A)
B[:]=A[:]+1e6
Bs=Array{Float32}(B)

# first single precision instance requires compilation
@time residual32 = (condmean(Bs[:],NaN)[1] - 1e6 - 13.0)
@time residual32 = (condmean(Bs[:],NaN)[1] - 1e6 - 13.0)
@time residual32 = (condmean(Bs[:],NaN)[1] - 1e6 - 13.0)
@show residual32

@time residual64 = (condmean(B[:],NaN)[1] - 1e6 - 13.0)
@time residual64 = (condmean(B[:],NaN)[1] - 1e6 - 13.0)
@time residual64 = (condmean(B[:],NaN)[1] - 1e6 - 13.0)
@show residual64

if     !(residual64 ≈ Float64(0.0))
  warn("ConditionalMean: condmean imprecise for double precision.")
elseif !(residual32 ≈ Float32(0.0))
  warn("ConditionalMean: condmean imprecise for single precision.")
end
# require to be precise at double precision
@test residual64 ≈ Float64(0.0)

# test precision with missing value
B[3,:]=Float32(mv)
@test condmean(B[:],mv)[1] - 1e6 ≈ 13.0
@test all(isapprox.(condmean(B,mv,1) - 1e6, [3.0 8.0 13.0 18.0 23.0]))
# with NaN
B[3,:]=Float32(NaN)
@test all(isapprox.(condmean(B,isfinite,1) - 1e6, [3.0 8.0 13.0 18.0 23.0]))

ext: jl


text:
module Telegram
using HTTP
using JSON3

using Base.CoreLogging:
    global_logger,
    LogLevel, BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel

import Base.CoreLogging:
    AbstractLogger,
    handle_message, shouldlog, min_enabled_level, catch_exceptions

export TelegramClient, useglobally!, TelegramLogger, run_bot

include("client.jl")
include("api.jl")
using .API

include("logging.jl")
include("bot.jl")

end # module


text:
import DiffRules: diffrule, diffrules

@inline function merge_systems(newval, x::DerivedVar{T}, y::DerivedVar{U}, ∂g_∂x, ∂g_∂y) where {T,U}
    return DerivedVar(newval, linear_combine(∂g_∂x, ∂g_∂y, x.grads, y.grads))
end

for i ∈ diffrules()

    pkg, f, arity = i

    # TODO: Rules for functions outside Base
    if pkg ≠ :Base continue end

    df(expr...) = diffrule(pkg, f, expr...)

    # Unitary functions
    if arity == 1
        quote
            function $pkg.$f(x::DerivedVar)
                return DerivedVar($f(val(x)), mul($(df(:(val(x)))), x.grads))
            end

            function $pkg.$f(x::CovariantVar{T,AV}) where {T,AV}
                return DerivedVar($f(val(x)), Gradients(x.system, onehot(AV, $(df(:(val(x)))),
                                                                         x.index, length(system(x).vals))))
            end
        end |> eval

    # Binary functions
    elseif arity == 2
        # Add explicit definitions for Integers to avoid ambiguities with Base
        for numtype in (:Real, :Integer)
            quote
                function $pkg.$f(x::DerivedVar, y::$numtype)
                    return DerivedVar($f(val(x), y),
                                      mul($(df(:(val(x)), :y)[1]), x.grads))
                end

                function $pkg.$f(x::$numtype, y::DerivedVar)
                    return DerivedVar($f(x, val(y)),
                                      mul($(df(:x, :(val(y)))[2]), y.grads))
                end

                $pkg.$f(x::CovariantVar, y::$numtype) = $f(DerivedVar(x), y)
                $pkg.$f(x::$numtype, y::CovariantVar) = $f(x, DerivedVar(y))
            end |> eval
        end

        quote
            # More of a hack, some ranges don't work otherwise
            $pkg.$f(x::CorrelatedVar, y::Base.TwicePrecision) = $f(x, convert(AbstractFloat, y))
            $pkg.$f(x::Base.TwicePrecision, y::CorrelatedVar) = $f(convert(AbstractFloat, x), y)

            function $pkg.$f(x::DerivedVar{T}, y::DerivedVar{U}) where {T,U}
                return merge_systems($f(val(x), val(y)), x, y,
                                     $(df(:(val(x)), :(val(y)))[1]),
                                     $(df(:(val(x)), :(val(y)))[2]))
            end

            $pkg.$f(x::CovariantVar, y::DerivedVar) = $f(DerivedVar(x), y)
            $pkg.$f(x::DerivedVar, y::CovariantVar) = $f(x, DerivedVar(y))

            function $pkg.$f(x::CovariantVar{T,AV1}, y::CovariantVar{U,AV2}) where {T,AV1,U,AV2}
                if system(x) === system(y)
                    return DerivedVar($f(val(x), val(y)),
                                      Gradients(system(x),
                                        onehot(AV1, $(df(:(val(x)), :(val(y)))[1]),
                                               x.index, length(system(x).vals)) .+
                                        onehot(AV2, $(df(:(val(x)), :(val(y)))[2]),
                                               y.index, length(system(y).vals))
                                      ))
                else
                    return DerivedVar($f(val(x), val(y)), Gradients(
                                    Gradients(system(x),
                                        onehot(AV1, $(df(:(val(x)), :(val(y)))[1]),
                                               x.index, length(system(x).vals)),
                                        ),
                                    system(y),
                                    onehot(AV2, $(df(:(val(x)), :(val(y)))[2]),
                                           y.index, length(system(y).vals))
                                    ))
                end
            end
        end |> eval
    else
        @warn "$i not handled"
    end
end

Base.sincos(x::CorrelatedVar) = (sin(x), cos(x))


text:
include("indicesarray.jl")
include("iarray.jl")
include("similar.jl")
include("getindex.jl")
include("broadcasting.jl")
include("setindex.jl")
include("operators.jl")
include("rebuild.jl")
include("reduce.jl")
include("dimensions.jl")
include("appends.jl")
include("push_pop.jl")
include("math.jl")
include("stats.jl")
include("show.jl")


text:
import Base: +, -, ==, hash
import Plots: plot, bar

struct Hand
    cards::Vector{Int}
    Hand(cards) = new(remove_trailing_zeros(cards))
end

Hand() = Hand([])

function _rtz(a, len=length(a))
    counter = 0
    for i = len:-1:1
        if a[i] == 0
            counter += 1
        else
            break
        end
    end
    return counter
end

remove_trailing_zeros(a) =  a[1:length(a) - _rtz(a, length(a))]

function remove_trailing_zeros!(a)
    len = length(a)
    deleteat!(a, len-_rtz(a, len)+1:len)
end

function +(hand::Hand, card::Int)
    newcards = copy(hand.cards)
    len = length(hand.cards)
    if len < card
        @inbounds for _ = len+1:card
            push!(newcards, 0)
        end
    end
    newcards[card] += 1
    return Hand(newcards)
end

function -(hand::Hand, card::Int)
    newcards = copy(hand.cards)
    len = length(hand.cards)
    if len < card
        for i = len+1:card
            push!(newcards, 0)
        end
    end
    if newcards[card] > 0
        newcards[card] -= 1
    end
    return Hand(newcards)
end

==(hand1::Hand, hand2::Hand) = all(.==(hand1.cards, hand2.cards))

function hash(hand::Hand, h::UInt)
    h = hash(hand.cards, h)
end

function evaluate(hand::Hand)
    cards = hand.cards
    best = 0
    tot = sum(cards)
    for (number, count) in enumerate(cards)
        best = max(best, number * count)
    end
    return best // tot
end

function add_optimal(hand::Hand)
    candidate_cards = 1:length(remove_trailing_zeros(hand.cards))+1
    card_to_add = findmin([evaluate(hand + i) for i in candidate_cards])[2]
    return hand + card_to_add
end

plot(hand::Hand; kwargs...) = bar(hand::Hand; kwargs...)
bar(hand::Hand; kwargs...) = bar(1:length(hand.cards), hand.cards, legend = :none; kwargs...)

function get_parents(hand::Hand)
    # nparents = length(filter(a -> a != 0, hand.cards))
    parents = Hand[]
    for i = 1:length(hand.cards)
        newcards = (hand - i).cards
        remove_trailing_zeros!(newcards)
        if Hand(newcards) != hand
            push!(parents, Hand(newcards))
        end
    end
    parents
end

function get_children(hand::Hand)
    children = Hand[]
    for i = 1:length(hand.cards)+1
        newcards = (hand + i).cards
        push!(children, Hand(newcards))
    end
    children
end

function isparent(candidate::Hand, child::Hand)
    all(child.cards .- candidate.cards .>= 0)
end

ischild(candidate::Hand, parent::Hand) = isparent(parent, candidate)

function distance(hand1::Hand, hand2::Hand)
    h1 = hand1.cards
    h2 = hand2.cards
    if length(h1) > length(h2)
        push!(h2, [0 for _ = 1:length(h1)-length(h2)]...)
    end
    if length(h2) > length(h1)
        push!(h1, [0 for _ = 1:length(h2)-length(h1)]...)
    end
    sum(h1 - h2 .!= 0)
end

DAG = Set{Hand}()
root = Hand([])
push!(DAG, root)
push!(DAG, Hand())
push!(DAG, Hand([1, 1]))

for hand in DAG
    for child in get_children(hand)
        push!(children, get_children(hand)...)
    end
end

function add_children(DAG::Set{Hand})
    children = Set{Hand}()
    for hand in DAG
        for child in get_children(hand)
            push!(children, child)
        end
    end
    union(DAG, children)
end

function add_children(DAG::Set{Hand}, level)
    children = Set{Hand}()
    tempDAG = copy(DAG)
    nhands = length(DAG)
    counter = 0
    while counter < nhands
        for hand in DAG
            if sum(hand.cards) <= level
                push!(children, get_children(hand)...)
                nelements += 1
            end
        end
        tempDAG = union(tempDAG, children)
        counter += 1
    end
end


text:
include("bpMCSetup.jl")

function makeBiasedLTD(ys::Vector{Float64}, N::Int64)
  rpio::RPIO{Int64Particle} = RPIO{Int64Particle}(N, length(ys), 1)
  function ltd(in::SVector{d, Float64})
    θ::BPθ = toBPθ(in)
    lp::Float64 = lglogprior(θ)
    if lp == -Inf return -Inf end
    model::Model = makeBPModel(θ, ys)
    return lp + biasedEstimateDiscrete(model, rpio)
  end
end

logtargetBiased = makeBiasedLTD(ys, N)
PBiased = makeAMKernel(logtargetBiased, d)

Random.seed!(12345)
setRNGs(54321)

biasedElapsed = @elapsed (chainBiased = simulateChainProgress(PBiased, t0, chainLength))
bar = PBiased(:acceptanceRate)
biasedCovEstimate = PBiased(:covEstimate)

@save "data/bpMCBiased.jld" chainBiased bar biasedCovEstimate biasedElapsed


text:
immutable Uniform <: ContinuousUnivariateDistribution
    a::Float64
    b::Float64
    function Uniform(a::Real, b::Real)
	    if a < b
	    	new(float64(a), float64(b))
	    else
	    	error("a < b required for range [a, b]")
	    end
	end
end

Uniform() = Uniform(0.0, 1.0)

@_jl_dist_2p Uniform unif

entropy(d::Uniform) = log(d.b - d.a + 1.0)

insupport(d::Uniform, x::Number) = isreal(x) && d.a <= x <= d.b

kurtosis(d::Uniform) = -6.0 / 5.0

mean(d::Uniform) = (d.a + d.b) / 2.0

median(d::Uniform) = (d.a + d.b) / 2.0

function mgf(d::Uniform, t::Real)
	a, b = d.a, d.b
	return (exp(t * b) - exp(t * a)) / (t * (b - a))
end

function cf(d::Uniform, t::Real)
	a, b = d.a, d.b
	return (exp(im * t * b) - exp(im * t * a)) / (im * t * (b - a))
end

modes(d::Uniform) = error("The uniform distribution has no modes")

rand(d::Uniform) = d.a + (d.b - d.a) * rand()

skewness(d::Uniform) = 0.0

function var(d::Uniform)
	w = d.b - d.a
	return w * w / 12.0
end


text:
const EMPTYSTRING = ""

# Implementation of MOI for AbstractModel
abstract type AbstractModelLike{T} <: MOI.ModelLike end
abstract type AbstractOptimizer{T} <: MOI.AbstractOptimizer end
const AbstractModel{T} = Union{AbstractModelLike{T},AbstractOptimizer{T}}

# Variables
function MOI.get(model::AbstractModel, ::MOI.NumberOfVariables)::Int64
    if model.variable_indices === nothing
        return model.num_variables_created
    else
        return length(model.variable_indices)
    end
end

function MOI.add_variable(model::AbstractModel{T}) where {T}
    vi = VI(model.num_variables_created += 1)
    push!(model.single_variable_mask, 0x0)
    push!(model.lower_bound, zero(T))
    push!(model.upper_bound, zero(T))
    if model.variable_indices !== nothing
        push!(model.variable_indices, vi)
    end
    return vi
end

function MOI.add_variables(model::AbstractModel, n::Integer)
    return [MOI.add_variable(model) for i in 1:n]
end

"""
    remove_variable(f::MOI.AbstractFunction, s::MOI.AbstractSet, vi::MOI.VariableIndex)

Return a tuple `(g, t)` representing the constraint `f`-in-`s` with the
variable `vi` removed. That is, the terms containing the variable `vi` in the
function `f` are removed and the dimension of the set `s` is updated if
needed (e.g. when `f` is a `VectorOfVariables` with `vi` being one of the
variables).
"""
remove_variable(f, s, vi::VI) = remove_variable(f, vi), s
function remove_variable(f::MOI.VectorOfVariables, s, vi::VI)
    g = remove_variable(f, vi)
    if length(g.variables) != length(f.variables)
        t = MOI.update_dimension(s, length(g.variables))
    else
        t = s
    end
    return g, t
end

function filter_variables(keep::F, f, s) where {F<:Function}
    return filter_variables(keep, f), s
end
function filter_variables(
    keep::F,
    f::MOI.VectorOfVariables,
    s,
) where {F<:Function}
    g = filter_variables(keep, f)
    if length(g.variables) != length(f.variables)
        t = MOI.update_dimension(s, length(g.variables))
    else
        t = s
    end
    return g, t
end
function _delete_variable(
    model::AbstractModel{T},
    vi::MOI.VariableIndex,
) where {T}
    MOI.throw_if_not_valid(model, vi)
    model.single_variable_mask[vi.value] = 0x0
    if model.variable_indices === nothing
        model.variable_indices =
            Set(MOI.get(model, MOI.ListOfVariableIndices()))
    end
    delete!(model.variable_indices, vi)
    model.name_to_var = nothing
    delete!(model.var_to_name, vi)
    model.name_to_con = nothing
    delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.EqualTo{T}}(vi.value),
    )
    delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.GreaterThan{T}}(vi.value),
    )
    delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.LessThan{T}}(vi.value),
    )
    delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.Interval{T}}(vi.value),
    )
    delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.Integer}(vi.value),
    )
    delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.ZeroOne}(vi.value),
    )
    delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.Semicontinuous{T}}(vi.value),
    )
    return delete!(
        model.con_to_name,
        MOI.ConstraintIndex{MOI.SingleVariable,MOI.Semiinteger{T}}(vi.value),
    )
end
function MOI.delete(model::AbstractModel, vi::MOI.VariableIndex)
    vis = [vi]
    _throw_if_cannot_delete(model.constraints, vis, vis)
    _delete_variable(model, vi)
    _deleted_constraints(model.constraints, vi) do ci
        return delete!(model.con_to_name, ci)
    end
    model.objective = remove_variable(model.objective, vi)
    model.name_to_con = nothing
    return
end

function MOI.delete(model::AbstractModel, vis::Vector{MOI.VariableIndex})
    if isempty(vis)
        # In `keep`, we assume that `model.variable_indices !== nothing` so
        # at least one variable need to be deleted.
        return
    end
    _throw_if_cannot_delete(model.constraints, vis, Set(vis))
    _deleted_constraints(model.constraints, vis) do ci
        return delete!(model.con_to_name, ci)
    end
    for vi in vis
        _delete_variable(model, vi)
    end
    keep(vi::MOI.VariableIndex) = vi in model.variable_indices
    model.objective = filter_variables(keep, model.objective)
    model.name_to_con = nothing
    return
end

function MOI.is_valid(
    model::AbstractModel,
    ci::CI{MOI.SingleVariable,S},
) where {S}
    return 1 ≤ ci.value ≤ length(model.single_variable_mask) &&
           !iszero(
        model.single_variable_mask[ci.value] & single_variable_flag(S),
    )
end
function MOI.is_valid(model::AbstractModel, ci::MOI.ConstraintIndex)
    return MOI.is_valid(model.constraints, ci)
end

function MOI.is_valid(model::AbstractModel, vi::VI)
    if model.variable_indices === nothing
        return 1 ≤ vi.value ≤ model.num_variables_created
    else
        return in(vi, model.variable_indices)
    end
end

function MOI.get(model::AbstractModel, ::MOI.ListOfVariableIndices)
    if model.variable_indices === nothing
        return VI.(1:model.num_variables_created)
    else
        vis = collect(model.variable_indices)
        sort!(vis, by = vi -> vi.value) # It needs to be sorted by order of creation
        return vis
    end
end

# Names
MOI.supports(::AbstractModel, ::MOI.Name) = true
function MOI.set(model::AbstractModel, ::MOI.Name, name::String)
    return model.name = name
end
MOI.get(model::AbstractModel, ::MOI.Name) = model.name

MOI.supports(::AbstractModel, ::MOI.VariableName, vi::Type{VI}) = true
function MOI.set(model::AbstractModel, ::MOI.VariableName, vi::VI, name::String)
    model.var_to_name[vi] = name
    model.name_to_var = nothing # Invalidate the name map.
    return
end

function MOI.get(model::AbstractModel, ::MOI.VariableName, vi::VI)
    return get(model.var_to_name, vi, EMPTYSTRING)
end

"""
    build_name_to_var_map(con_to_name::Dict{MOI.VariableIndex, String})

Create and return a reverse map from name to variable index, given a map from
variable index to name. The special value `MOI.VariableIndex(0)` is used to
indicate that multiple variables have the same name.
"""
function build_name_to_var_map(var_to_name::Dict{VI,String})
    name_to_var = Dict{String,VI}()
    for (var, var_name) in var_to_name
        if haskey(name_to_var, var_name)
            # 0 is a special value that means this string does not map to
            # a unique variable name.
            name_to_var[var_name] = VI(0)
        else
            name_to_var[var_name] = var
        end
    end
    return name_to_var
end

function throw_multiple_name_error(::Type{MOI.VariableIndex}, name::String)
    return error("Multiple variables have the name $name.")
end
function throw_multiple_name_error(::Type{<:MOI.ConstraintIndex}, name::String)
    return error("Multiple constraints have the name $name.")
end
function throw_if_multiple_with_name(::Nothing, ::String) end
function throw_if_multiple_with_name(index::MOI.Index, name::String)
    if iszero(index.value)
        throw_multiple_name_error(typeof(index), name)
    end
end

function MOI.get(model::AbstractModel, ::Type{VI}, name::String)
    if model.name_to_var === nothing
        # Rebuild the map.
        model.name_to_var = build_name_to_var_map(model.var_to_name)
    end
    result = get(model.name_to_var, name, nothing)
    throw_if_multiple_with_name(result, name)
    return result
end

function MOI.get(
    model::AbstractModel,
    ::MOI.ListOfVariableAttributesSet,
)::Vector{MOI.AbstractVariableAttribute}
    return isempty(model.var_to_name) ? [] : [MOI.VariableName()]
end

MOI.supports(model::AbstractModel, ::MOI.ConstraintName, ::Type{<:CI}) = true
function MOI.set(
    model::AbstractModel,
    ::MOI.ConstraintName,
    ci::CI,
    name::String,
)
    model.con_to_name[ci] = name
    model.name_to_con = nothing # Invalidate the name map.
    return
end

function MOI.get(model::AbstractModel, ::MOI.ConstraintName, ci::CI)
    return get(model.con_to_name, ci, EMPTYSTRING)
end

"""
    build_name_to_con_map(con_to_name::Dict{MOI.ConstraintIndex, String})

Create and return a reverse map from name to constraint index, given a map from
constraint index to name. The special value
`MOI.ConstraintIndex{Nothing, Nothing}(0)` is used to indicate that multiple
constraints have the same name.
"""
function build_name_to_con_map(con_to_name::Dict{CI,String})
    name_to_con = Dict{String,CI}()
    for (con, con_name) in con_to_name
        if haskey(name_to_con, con_name)
            name_to_con[con_name] = CI{Nothing,Nothing}(0)
        else
            name_to_con[con_name] = con
        end
    end
    return name_to_con
end

function MOI.get(model::AbstractModel, ConType::Type{<:CI}, name::String)
    if model.name_to_con === nothing
        # Rebuild the map.
        model.name_to_con = build_name_to_con_map(model.con_to_name)
    end
    ci = get(model.name_to_con, name, nothing)
    throw_if_multiple_with_name(ci, name)
    return ci isa ConType ? ci : nothing
end

function MOI.get(
    model::AbstractModel,
    ::MOI.ListOfConstraintAttributesSet,
)::Vector{MOI.AbstractConstraintAttribute}
    return isempty(model.con_to_name) ? [] : [MOI.ConstraintName()]
end

# Objective
MOI.get(model::AbstractModel, ::MOI.ObjectiveSense) = model.sense
MOI.supports(model::AbstractModel, ::MOI.ObjectiveSense) = true
function MOI.set(
    model::AbstractModel{T},
    ::MOI.ObjectiveSense,
    sense::MOI.OptimizationSense,
) where {T}
    if sense == MOI.FEASIBILITY_SENSE
        model.objectiveset = false
        model.objective = zero(MOI.ScalarAffineFunction{T})
    end
    model.senseset = true
    model.sense = sense
    return
end

function MOI.get(model::AbstractModel, ::MOI.ObjectiveFunctionType)
    return MOI.typeof(model.objective)
end
function MOI.get(model::AbstractModel, ::MOI.ObjectiveFunction{T})::T where {T}
    return model.objective
end
function MOI.supports(
    model::AbstractModel{T},
    ::MOI.ObjectiveFunction{
        <:Union{
            MOI.SingleVariable,
            MOI.ScalarAffineFunction{T},
            MOI.ScalarQuadraticFunction{T},
        },
    },
) where {T}
    return true
end
function MOI.set(
    model::AbstractModel,
    attr::MOI.ObjectiveFunction{F},
    f::F,
) where {F<:MOI.AbstractFunction}
    if !MOI.supports(model, attr)
        throw(MOI.UnsupportedAttribute(attr))
    end
    model.objectiveset = true
    # f needs to be copied, see #2
    model.objective = copy(f)
    return
end

function MOI.modify(
    model::AbstractModel,
    ::MOI.ObjectiveFunction,
    change::MOI.AbstractFunctionModification,
)
    model.objective = modify_function(model.objective, change)
    model.objectiveset = true
    return
end

function MOI.get(::AbstractModel, ::MOI.ListOfOptimizerAttributesSet)
    return MOI.AbstractOptimizerAttribute[]
end
function MOI.get(
    model::AbstractModel,
    ::MOI.ListOfModelAttributesSet,
)::Vector{MOI.AbstractModelAttribute}
    listattr = MOI.AbstractModelAttribute[]
    if model.senseset
        push!(listattr, MOI.ObjectiveSense())
    end
    if model.objectiveset
        push!(listattr, MOI.ObjectiveFunction{typeof(model.objective)}())
    end
    if !isempty(model.name)
        push!(listattr, MOI.Name())
    end
    return listattr
end

# Constraints
single_variable_flag(::Type{<:MOI.EqualTo}) = 0x1
single_variable_flag(::Type{<:MOI.GreaterThan}) = 0x2
single_variable_flag(::Type{<:MOI.LessThan}) = 0x4
single_variable_flag(::Type{<:MOI.Interval}) = 0x8
single_variable_flag(::Type{MOI.Integer}) = 0x10
single_variable_flag(::Type{MOI.ZeroOne}) = 0x20
single_variable_flag(::Type{<:MOI.Semicontinuous}) = 0x40
single_variable_flag(::Type{<:MOI.Semiinteger}) = 0x80
# If a set is added here, a line should be added in
# `MOI.delete(::AbstractModel, ::MOI.VariableIndex)`

function flag_to_set_type(flag::UInt8, ::Type{T}) where {T}
    if flag == 0x1
        return MOI.EqualTo{T}
    elseif flag == 0x2
        return MOI.GreaterThan{T}
    elseif flag == 0x4
        return MOI.LessThan{T}
    elseif flag == 0x8
        return MOI.Interval{T}
    elseif flag == 0x10
        return MOI.Integer
    elseif flag == 0x20
        return MOI.ZeroOne
    elseif flag == 0x40
        return MOI.Semicontinuous{T}
    else
        @assert flag == 0x80
        return MOI.Semiinteger{T}
    end
end

# Julia doesn't infer `S1` correctly, so we use a function barrier to improve
# inference.
function _throw_if_lower_bound_set(variable, S2, mask, T)
    S1 = flag_to_set_type(mask, T)
    throw(MOI.LowerBoundAlreadySet{S1,S2}(variable))
    return
end

function throw_if_lower_bound_set(variable, S2, mask, T)
    lower_mask = mask & LOWER_BOUND_MASK
    if iszero(lower_mask)
        return  # No lower bound set.
    elseif iszero(single_variable_flag(S2) & LOWER_BOUND_MASK)
        return  # S2 isn't related to the lower bound.
    end
    return _throw_if_lower_bound_set(variable, S2, lower_mask, T)
end

# Julia doesn't infer `S1` correctly, so we use a function barrier to improve
# inference.
function _throw_if_upper_bound_set(variable, S2, mask, T)
    S1 = flag_to_set_type(mask, T)
    throw(MOI.UpperBoundAlreadySet{S1,S2}(variable))
    return
end

function throw_if_upper_bound_set(variable, S2, mask, T)
    upper_mask = mask & UPPER_BOUND_MASK
    if iszero(upper_mask)
        return  # No upper bound set.
    elseif iszero(single_variable_flag(S2) & UPPER_BOUND_MASK)
        return  # S2 isn't related to the upper bound.
    end
    return _throw_if_upper_bound_set(variable, S2, upper_mask, T)
end

# Sets setting lower bound:
extract_lower_bound(set::MOI.EqualTo) = set.value
function extract_lower_bound(
    set::Union{MOI.GreaterThan,MOI.Interval,MOI.Semicontinuous,MOI.Semiinteger},
)
    return set.lower
end
# 0xcb = 0x80 | 0x40 | 0x8 | 0x2 | 0x1
const LOWER_BOUND_MASK = 0xcb

# Sets setting upper bound:
extract_upper_bound(set::MOI.EqualTo) = set.value
function extract_upper_bound(
    set::Union{MOI.LessThan,MOI.Interval,MOI.Semicontinuous,MOI.Semiinteger},
)
    return set.upper
end
# 0xcd = 0x80 | 0x40 | 0x8 | 0x4 | 0x1
const UPPER_BOUND_MASK = 0xcd

const SUPPORTED_VARIABLE_SCALAR_SETS{T} = Union{
    MOI.EqualTo{T},
    MOI.GreaterThan{T},
    MOI.LessThan{T},
    MOI.Interval{T},
    MOI.Integer,
    MOI.ZeroOne,
    MOI.Semicontinuous{T},
    MOI.Semiinteger{T},
}
function MOI.supports_constraint(
    ::AbstractModel{T},
    ::Type{MOI.SingleVariable},
    ::Type{<:SUPPORTED_VARIABLE_SCALAR_SETS{T}},
) where {T}
    return true
end
function MOI.supports_constraint(
    model::AbstractModel,
    ::Type{F},
    ::Type{S},
) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet}
    return MOI.supports_constraint(model.constraints, F, S)
end

function MOI.add_constraint(
    model::AbstractModel{T},
    f::MOI.SingleVariable,
    s::SUPPORTED_VARIABLE_SCALAR_SETS{T},
) where {T}
    flag = single_variable_flag(typeof(s))
    index = f.variable.value
    mask = model.single_variable_mask[index]
    throw_if_lower_bound_set(f.variable, typeof(s), mask, T)
    throw_if_upper_bound_set(f.variable, typeof(s), mask, T)
    # No error should be thrown now, we can modify `model`.
    if !iszero(flag & LOWER_BOUND_MASK)
        model.lower_bound[index] = extract_lower_bound(s)
    end
    if !iszero(flag & UPPER_BOUND_MASK)
        model.upper_bound[index] = extract_upper_bound(s)
    end
    model.single_variable_mask[index] = mask | flag
    return CI{MOI.SingleVariable,typeof(s)}(index)
end

function MOI.add_constraint(
    model::AbstractModel,
    func::MOI.AbstractFunction,
    set::MOI.AbstractSet,
)
    return MOI.add_constraint(model.constraints, func, set)
end

function MOI.get(
    model::AbstractModel,
    attr::Union{MOI.AbstractFunction,MOI.AbstractSet},
    ci::MOI.ConstraintIndex,
)
    return MOI.get(model.constraints, attr, ci)
end

function _delete_constraint(
    model::AbstractModel,
    ci::MOI.ConstraintIndex{MOI.SingleVariable,S},
) where {S}
    MOI.throw_if_not_valid(model, ci)
    model.single_variable_mask[ci.value] &= ~single_variable_flag(S)
    return
end

function _delete_constraint(model::AbstractModel, ci::MOI.ConstraintIndex)
    return MOI.delete(model.constraints, ci)
end

function MOI.delete(model::AbstractModel, ci::MOI.ConstraintIndex)
    _delete_constraint(model, ci)
    model.name_to_con = nothing
    delete!(model.con_to_name, ci)
    return
end

function MOI.modify(
    model::AbstractModel,
    ci::MOI.ConstraintIndex,
    change::MOI.AbstractFunctionModification,
)
    MOI.modify(model.constraints, ci, change)
    return
end

function MOI.set(
    ::AbstractModel,
    ::MOI.ConstraintFunction,
    ::MOI.ConstraintIndex{MOI.SingleVariable,<:MOI.AbstractScalarSet},
    ::MOI.SingleVariable,
)
    return throw(MOI.SettingSingleVariableFunctionNotAllowed())
end
function MOI.set(
    model::AbstractModel{T},
    ::MOI.ConstraintSet,
    ci::MOI.ConstraintIndex{MOI.SingleVariable,S},
    set::S,
) where {T,S<:SUPPORTED_VARIABLE_SCALAR_SETS{T}}
    MOI.throw_if_not_valid(model, ci)
    flag = single_variable_flag(typeof(set))
    if !iszero(flag & LOWER_BOUND_MASK)
        model.lower_bound[ci.value] = extract_lower_bound(set)
    end
    if !iszero(flag & UPPER_BOUND_MASK)
        model.upper_bound[ci.value] = extract_upper_bound(set)
    end
    return
end

function MOI.set(
    model::AbstractModel,
    attr::MOI.ConstraintSet,
    ci::MOI.ConstraintIndex{<:MOI.AbstractFunction,S},
    set::S,
) where {S<:MOI.AbstractSet}
    MOI.set(model.constraints, attr, ci, set)
    return
end

function MOI.set(
    model::AbstractModel,
    attr::MOI.ConstraintFunction,
    ci::MOI.ConstraintIndex{F,<:MOI.AbstractSet},
    func::F,
) where {F<:MOI.AbstractFunction}
    MOI.set(model.constraints, attr, ci, func)
    return
end

function MOI.get(
    model::AbstractModel,
    ::MOI.NumberOfConstraints{MOI.SingleVariable,S},
) where {S}
    flag = single_variable_flag(S)
    return count(mask -> !iszero(flag & mask), model.single_variable_mask)
end
function MOI.get(
    model::AbstractModel,
    noc::MOI.NumberOfConstraints{F,S},
) where {F,S}
    return MOI.get(model.constraints, noc)
end

function _add_contraint_type(
    list,
    model::AbstractModel,
    S::Type{<:MOI.AbstractScalarSet},
)
    flag = single_variable_flag(S)
    if any(mask -> !iszero(flag & mask), model.single_variable_mask)
        push!(list, (MOI.SingleVariable, S))
    end
    return
end
function MOI.get(model::AbstractModel{T}, loc::MOI.ListOfConstraints) where {T}
    list = copy(MOI.get(model.constraints, loc))
    for S in (
        MOI.EqualTo{T},
        MOI.GreaterThan{T},
        MOI.LessThan{T},
        MOI.Interval{T},
        MOI.Semicontinuous{T},
        MOI.Semiinteger{T},
        MOI.Integer,
        MOI.ZeroOne,
    )
        _add_contraint_type(list, model, S)
    end
    return list
end

function MOI.get(
    model::AbstractModel,
    ::MOI.ListOfConstraintIndices{MOI.SingleVariable,S},
) where {S}
    list = CI{MOI.SingleVariable,S}[]
    flag = single_variable_flag(S)
    for (index, mask) in enumerate(model.single_variable_mask)
        if !iszero(mask & flag)
            push!(list, CI{MOI.SingleVariable,S}(index))
        end
    end
    return list
end

function MOI.get(
    model::AbstractModel,
    loc::MOI.ListOfConstraintIndices{F,S},
) where {F,S}
    return MOI.get(model.constraints, loc)
end

function MOI.get(
    model::AbstractModel,
    ::MOI.ConstraintFunction,
    ci::CI{MOI.SingleVariable},
)
    MOI.throw_if_not_valid(model, ci)
    return MOI.SingleVariable(MOI.VariableIndex(ci.value))
end
function MOI.get(
    model::AbstractModel,
    attr::Union{MOI.ConstraintFunction,MOI.ConstraintSet},
    ci::MOI.ConstraintIndex,
)
    return MOI.get(model.constraints, attr, ci)
end

function _get_single_variable_set(
    model::AbstractModel,
    ::Type{<:MOI.EqualTo},
    index,
)
    return MOI.EqualTo(model.lower_bound[index])
end
function _get_single_variable_set(
    model::AbstractModel,
    S::Type{<:Union{MOI.GreaterThan,MOI.EqualTo}},
    index,
)
    # Lower and upper bounds are equal for `EqualTo`, we can take either of them.
    return S(model.lower_bound[index])
end
function _get_single_variable_set(
    model::AbstractModel,
    S::Type{<:MOI.LessThan},
    index,
)
    return S(model.upper_bound[index])
end
function _get_single_variable_set(
    model::AbstractModel,
    S::Type{<:Union{MOI.Interval,MOI.Semicontinuous,MOI.Semiinteger}},
    index,
)
    return S(model.lower_bound[index], model.upper_bound[index])
end
function _get_single_variable_set(
    ::AbstractModel,
    S::Type{<:Union{MOI.Integer,MOI.ZeroOne}},
    index,
)
    return S()
end
function MOI.get(
    model::AbstractModel,
    ::MOI.ConstraintSet,
    ci::CI{MOI.SingleVariable,S},
) where {S}
    MOI.throw_if_not_valid(model, ci)
    return _get_single_variable_set(model, S, ci.value)
end

function MOI.is_empty(model::AbstractModel)
    return isempty(model.name) &&
           !model.senseset &&
           !model.objectiveset &&
           isempty(model.objective.terms) &&
           iszero(model.objective.constant) &&
           iszero(model.num_variables_created) &&
           MOI.is_empty(model.constraints)
end
function MOI.empty!(model::AbstractModel{T}) where {T}
    model.name = ""
    model.senseset = false
    model.sense = MOI.FEASIBILITY_SENSE
    model.objectiveset = false
    model.objective = zero(MOI.ScalarAffineFunction{T})
    model.num_variables_created = 0
    model.variable_indices = nothing
    model.single_variable_mask = UInt8[]
    model.lower_bound = T[]
    model.upper_bound = T[]
    empty!(model.var_to_name)
    model.name_to_var = nothing
    empty!(model.con_to_name)
    model.name_to_con = nothing
    MOI.empty!(model.constraints)
    return
end

function pass_nonvariable_constraints(
    dest::AbstractModel,
    src::MOI.ModelLike,
    idxmap::IndexMap,
    constraint_types,
    pass_cons;
    filter_constraints::Union{Nothing,Function} = nothing,
)
    return pass_nonvariable_constraints(
        dest.constraints,
        src,
        idxmap,
        constraint_types,
        pass_cons;
        filter_constraints = filter_constraints,
    )
end

function MOI.copy_to(dest::AbstractModel, src::MOI.ModelLike; kws...)
    return automatic_copy_to(dest, src; kws...)
end
supports_default_copy_to(model::AbstractModel, copy_names::Bool) = true

# Allocate-Load Interface
# Even if the model does not need it and use default_copy_to, it could be used
# by a layer that needs it
supports_allocate_load(model::AbstractModel, copy_names::Bool) = true

function allocate_variables(model::AbstractModel, nvars)
    return MOI.add_variables(model, nvars)
end
allocate(model::AbstractModel, attr...) = MOI.set(model, attr...)
function allocate_constraint(
    model::AbstractModel,
    f::MOI.AbstractFunction,
    s::MOI.AbstractSet,
)
    return MOI.add_constraint(model, f, s)
end

function load_variables(::AbstractModel, nvars) end
function load(::AbstractModel, attr...) end
function load_constraint(
    ::AbstractModel,
    ::CI,
    ::MOI.AbstractFunction,
    ::MOI.AbstractSet,
) end

# Macro to generate Model

function _struct_of_constraints_type(name, subtypes, parametrized_type)
    if length(subtypes) == 1
        # Only one type, no need for a `StructOfConstraints`.
        return subtypes[1]
    else
        T = esc(:T)
        t = :($name{$T})
        if parametrized_type
            append!(t.args, subtypes)
        end
        return t
    end
end

# This macro is for expert/internal use only. Prefer the concrete Model type
# instantiated below.
"""
    macro model(
        model_name,
        scalar_sets,
        typed_scalar_sets,
        vector_sets,
        typed_vector_sets,
        scalar_functions,
        typed_scalar_functions,
        vector_functions,
        typed_vector_functions,
        is_optimizer = false
    )

Creates a type `model_name` implementing the MOI model interface and containing
`scalar_sets` scalar sets `typed_scalar_sets` typed scalar sets, `vector_sets`
vector sets, `typed_vector_sets` typed vector sets, `scalar_functions` scalar
functions, `typed_scalar_functions` typed scalar functions, `vector_functions`
vector functions and `typed_vector_functions` typed vector functions.
To give no set/function, write `()`, to give one set `S`, write `(S,)`.

The function [`MathOptInterface.SingleVariable`](@ref) should not be given in
`scalar_functions`. The model supports [`MathOptInterface.SingleVariable`](@ref)-in-`S`
constraints where `S` is [`MathOptInterface.EqualTo`](@ref),
[`MathOptInterface.GreaterThan`](@ref), [`MathOptInterface.LessThan`](@ref),
[`MathOptInterface.Interval`](@ref), [`MathOptInterface.Integer`](@ref),
[`MathOptInterface.ZeroOne`](@ref), [`MathOptInterface.Semicontinuous`](@ref)
or [`MathOptInterface.Semiinteger`](@ref). The sets supported
with the [`MathOptInterface.SingleVariable`](@ref) cannot be controlled from the
macro, use the [`UniversalFallback`](@ref) to support more sets.

This macro creates a model specialized for specific types of constraint,
by defining specialized structures and methods. To create a model that,
in addition to be optimized for specific constraints, also support arbitrary
constraints and attributes, use [`UniversalFallback`](@ref).

If `is_optimizer = true`, the resulting struct is a
of [`GenericOptimizer`](@ref), which is a subtype of
[`MathOptInterface.AbstractOptimizer`](@ref), otherwise, it is a
[`GenericModel`](@ref), which is a subtype of
[`MathOptInterface.ModelLike`](@ref).

### Examples

The model describing an linear program would be:
```julia
@model(LPModel,                                                   # Name of model
      (),                                                         # untyped scalar sets
      (MOI.EqualTo, MOI.GreaterThan, MOI.LessThan, MOI.Interval), #   typed scalar sets
      (MOI.Zeros, MOI.Nonnegatives, MOI.Nonpositives),            # untyped vector sets
      (),                                                         #   typed vector sets
      (),                                                         # untyped scalar functions
      (MOI.ScalarAffineFunction,),                                #   typed scalar functions
      (MOI.VectorOfVariables,),                                   # untyped vector functions
      (MOI.VectorAffineFunction,),                                #   typed vector functions
      false
    )
```

Let `MOI` denote `MathOptInterface`, `MOIU` denote `MOI.Utilities`.
The macro would create the following types with
[`struct_of_constraint_code`](@ref):
```julia
struct LPModelScalarConstraints{T, C1, C2, C3, C4} <: MOIU.StructOfConstraints
    moi_equalto::C1
    moi_greaterthan::C2
    moi_lessthan::C3
    moi_interval::C4
end
struct LPModelVectorConstraints{T, C1, C2, C3} <: MOIU.StructOfConstraints
    moi_zeros::C1
    moi_nonnegatives::C2
    moi_nonpositives::C3
end
struct LPModelFunctionConstraints{T} <: MOIU.StructOfConstraints
    moi_scalaraffinefunction::LPModelScalarConstraints{
        T,
        MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.EqualTo{T}},
        MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}},
        MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.LessThan{T}},
        MOIU.VectorOfConstraints{MOI.ScalarAffineFunction{T}, MOI.Interval{T}}
    }
    moi_vectorofvariables::LPModelVectorConstraints{
        T,
        MOIU.VectorOfConstraints{MOI.VectorOfVariables, MOI.Zeros},
        MOIU.VectorOfConstraints{MOI.VectorOfVariables, MOI.Nonnegatives},
        MOIU.VectorOfConstraints{MOI.VectorOfVariables, MOI.Nonpositives}
    }
    moi_vectoraffinefunction::LPModelVectorConstraints{
        T,
        MOIU.VectorOfConstraints{MOI.VectorAffineFunction{T}, MOI.Zeros},
        MOIU.VectorOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonnegatives},
        MOIU.VectorOfConstraints{MOI.VectorAffineFunction{T}, MOI.Nonpositives}
    }
end
const LPModel{T} = MOIU.GenericModel{T, LPModelFunctionConstraints{T}}
```
The type `LPModel` implements the MathOptInterface API except methods specific
to optimizers like `optimize!` or `get` with `VariablePrimal`.
"""
macro model(
    model_name,
    ss,
    sst,
    vs,
    vst,
    sf,
    sft,
    vf,
    vft,
    is_optimizer = false,
)
    scalar_sets = [SymbolSet.(ss.args, false); SymbolSet.(sst.args, true)]
    vector_sets = [SymbolSet.(vs.args, false); SymbolSet.(vst.args, true)]

    scname = esc(Symbol(string(model_name) * "ScalarConstraints"))
    vcname = esc(Symbol(string(model_name) * "VectorConstraints"))

    esc_model_name = esc(model_name)
    # TODO if there is only one function or one set, remove the layer

    scalar_funs = [
        SymbolFun.(sf.args, false)
        SymbolFun.(sft.args, true)
    ]
    vector_funs = [
        SymbolFun.(vf.args, false)
        SymbolFun.(vft.args, true)
    ]
    funs = [scalar_funs; vector_funs]
    set_struct_types = map(eachindex(funs)) do i
        if i <= length(scalar_funs)
            cname = scname
            sets = scalar_sets
        else
            cname = vcname
            sets = vector_sets
        end
        voc = map(sets) do set
            return :(VectorOfConstraints{$(_typedfun(funs[i])),$(_typedset(set))})
        end
        return _struct_of_constraints_type(cname, voc, true)
    end
    func_name = esc(Symbol(string(model_name) * "FunctionConstraints"))
    func_typed = _struct_of_constraints_type(func_name, set_struct_types, false)
    T = esc(:T)
    generic = if is_optimizer
        :(GenericOptimizer{$T,$func_typed})
    else
        :(GenericModel{$T,$func_typed})
    end
    model_code = if is_optimizer
        :(const $esc_model_name{$T} = $generic)
    else
        :(const $esc_model_name{$T} = $generic)
    end
    expr = Expr(:block)
    if length(scalar_sets) >= 2
        push!(expr.args, struct_of_constraint_code(scname, scalar_sets))
    end
    if length(vector_sets) >= 2
        push!(expr.args, struct_of_constraint_code(vcname, vector_sets))
    end
    if length(funs) != 1
        push!(
            expr.args,
            struct_of_constraint_code(func_name, funs, set_struct_types),
        )
    end
    push!(expr.args, model_code)
    return expr
end

for (loop_name, loop_super_type) in [
    (:GenericModel, :AbstractModelLike),
    (:GenericOptimizer, :AbstractOptimizer),
]
    global name = loop_name
    global super_type = loop_super_type
    @eval begin
        """
            mutable struct $name{T,C} <: $super_type{T}

        Implements a models supporting
        * an objective function of type
          `MOI.SingleVariable`, `MOI.ScalarAffineFunction{T}` and
          `MOI.ScalarQuadraticFunction{T}`,
        * [`MathOptInterface.SingleVariable`](@ref)-in-`S`
          constraints where `S` is [`MathOptInterface.EqualTo`](@ref),
          [`MathOptInterface.GreaterThan`](@ref), [`MathOptInterface.LessThan`](@ref),
          [`MathOptInterface.Interval`](@ref), [`MathOptInterface.Integer`](@ref),
          [`MathOptInterface.ZeroOne`](@ref), [`MathOptInterface.Semicontinuous`](@ref)
          or [`MathOptInterface.Semiinteger`](@ref).
        * `F`-in-`S` constraints that are supported by `C`.
        """
        mutable struct $name{T,C} <: $super_type{T}
            name::String
            senseset::Bool
            sense::MOI.OptimizationSense
            objectiveset::Bool
            objective::Union{
                MOI.SingleVariable,
                MOI.ScalarAffineFunction{T},
                MOI.ScalarQuadraticFunction{T},
            }
            num_variables_created::Int64
            # If nothing, no variable has been deleted so the indices of the
            # variables are VI.(1:num_variables_created)
            variable_indices::Union{Nothing,Set{VI}}
            # Union of flags of `S` such that a `SingleVariable`-in-`S`
            # constraint was added to the model and not deleted yet.
            single_variable_mask::Vector{UInt8}
            # Lower bound set by `SingleVariable`-in-`S` where `S`is
            # `GreaterThan{T}`, `EqualTo{T}` or `Interval{T}`.
            lower_bound::Vector{T}
            # Lower bound set by `SingleVariable`-in-`S` where `S`is
            # `LessThan{T}`, `EqualTo{T}` or `Interval{T}`.
            upper_bound::Vector{T}
            constraints::C
            var_to_name::Dict{MOI.VariableIndex,String}
            # If `nothing`, the dictionary hasn't been constructed yet.
            name_to_var::Union{Dict{String,MOI.VariableIndex},Nothing}
            con_to_name::Dict{MOI.ConstraintIndex,String}
            name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing}
            # A useful dictionary for extensions to store things. These are
            # _not_ copied between models!
            ext::Dict{Symbol,Any}
            function $name{T,C}() where {T,C}
                return new{T,C}(
                    EMPTYSTRING,
                    false,
                    MOI.FEASIBILITY_SENSE,
                    false,
                    zero(MOI.ScalarAffineFunction{T}),
                    0,
                    nothing,
                    UInt8[],
                    T[],
                    T[],
                    C(),
                    Dict{MOI.VariableIndex,String}(),
                    nothing,
                    Dict{MOI.ConstraintIndex,String}(),
                    nothing,
                    Dict{Symbol,Any}(),
                )
            end
        end
    end
end

const LessThanIndicatorSetOne{T} =
    MOI.IndicatorSet{MOI.ACTIVATE_ON_ONE,MOI.LessThan{T}}
const LessThanIndicatorSetZero{T} =
    MOI.IndicatorSet{MOI.ACTIVATE_ON_ZERO,MOI.LessThan{T}}

@model(
    Model,
    (MOI.ZeroOne, MOI.Integer),
    (
        MOI.EqualTo,
        MOI.GreaterThan,
        MOI.LessThan,
        MOI.Interval,
        MOI.Semicontinuous,
        MOI.Semiinteger,
    ),
    (
        MOI.Reals,
        MOI.Zeros,
        MOI.Nonnegatives,
        MOI.Nonpositives,
        MOI.Complements,
        MOI.NormInfinityCone,
        MOI.NormOneCone,
        MOI.SecondOrderCone,
        MOI.RotatedSecondOrderCone,
        MOI.GeometricMeanCone,
        MOI.ExponentialCone,
        MOI.DualExponentialCone,
        MOI.RelativeEntropyCone,
        MOI.NormSpectralCone,
        MOI.NormNuclearCone,
        MOI.PositiveSemidefiniteConeTriangle,
        MOI.PositiveSemidefiniteConeSquare,
        MOI.RootDetConeTriangle,
        MOI.RootDetConeSquare,
        MOI.LogDetConeTriangle,
        MOI.LogDetConeSquare,
    ),
    (
        MOI.PowerCone,
        MOI.DualPowerCone,
        MOI.SOS1,
        MOI.SOS2,
        LessThanIndicatorSetOne,
        LessThanIndicatorSetZero,
    ),
    (),
    (MOI.ScalarAffineFunction, MOI.ScalarQuadraticFunction),
    (MOI.VectorOfVariables,),
    (MOI.VectorAffineFunction, MOI.VectorQuadraticFunction)
)

@doc raw"""

An implementation of `ModelLike` that supports all functions and sets defined
in MOI. It is parameterized by the coefficient type.

# Examples
```jl
model = Model{Float64}()
x = add_variable(model)
```
"""
Model


text:
# Context management

export
    CuContext, CuCurrentContext, activate,
    synchronize, CuCurrentDevice

## construction and destruction

"""
    CuContext(dev::CuDevice, flags=CTX_SCHED_AUTO)
    CuContext(f::Function, ...)

Create a CUDA context for device. A context on the GPU is analogous to a process on the CPU,
with its own distinct address space and allocated resources. When a context is destroyed,
the system cleans up the resources allocated to it.

When you are done using the context, call [`CUDA.unsafe_destroy!`](@ref) to mark it for
deletion, or use do-block syntax with this constructor.

"""
mutable struct CuContext
    handle::CUcontext

    function CuContext(handle::CUcontext)
        handle == C_NULL && return new(C_NULL)
        return Base.@lock context_lock get!(valid_contexts, handle) do
            new(handle)
        end
    end
end

# NOTE: we don't implement `isequal` or `hash` in order to fall back to `===` and `objectid`
#       as contexts are unique, and with primary device contexts identical handles might be
#       returned after resetting the context (device) and all associated resources.

# the `valid` bit serves two purposes: make sure we don't double-free a context (in case we
# early-freed it ourselves before the GC kicked in), and to make sure we don't free derived
# resources after the owning context has been destroyed (which can happen due to
# out-of-order finalizer execution)
const valid_contexts = Dict{CUcontext,CuContext}()
const context_lock = ReentrantLock()
isvalid(ctx::CuContext) = any(x->x==ctx, values(valid_contexts))
# NOTE: we can't just look up by the handle, because contexts derived from a primary one
#       have the same handle even though they might have been destroyed in the meantime.
function invalidate!(ctx::CuContext)
    delete!(valid_contexts, ctx.handle)
    return
end

function Base.show(io::IO, ctx::CuContext)
    if ctx.handle != C_NULL
        fields = [@sprintf("%p", ctx.handle), @sprintf("instance %x", objectid(ctx))]
        if !isvalid(ctx)
            push!(fields, "invalidated")
        end

        print(io, "CuContext(", join(fields, ", "), ")")
    else
        print(io, "CuContext(NULL)")
    end
end

"""
    unsafe_destroy!(ctx::CuContext)

Immediately destroy a context, freeing up all resources associated with it. This does not
respect any users of the context, and might make other objects unusable.
"""
function unsafe_destroy!(ctx::CuContext)
    if isvalid(ctx)
        cuCtxDestroy_v2(ctx)
        invalidate!(ctx)
    end
end

Base.unsafe_convert(::Type{CUcontext}, ctx::CuContext) = ctx.handle

@enum_without_prefix CUctx_flags CU_

function CuContext(dev::CuDevice, flags=0)
    handle_ref = Ref{CUcontext}()
    cuCtxCreate_v2(handle_ref, flags, dev)
    return CuContext(handle_ref[])
end

"""
    CuCurrentContext()

Return the current context, or `nothing` if there is no active context.
"""
function CuCurrentContext()
    handle_ref = Ref{CUcontext}()
    cuCtxGetCurrent(handle_ref)
    if handle_ref[] == C_NULL
        return nothing
    else
        return CuContext(handle_ref[])
    end
end

"""
    push!(CuContext, ctx::CuContext)

Pushes a context on the current CPU thread.
"""
Base.push!(::Type{CuContext}, ctx::CuContext) = cuCtxPushCurrent_v2(ctx)

"""
    pop!(CuContext)

Pops the current CUDA context from the current CPU thread, and returns that context.
"""
function Base.pop!(::Type{CuContext})
    handle_ref = Ref{CUcontext}()
    cuCtxPopCurrent_v2(handle_ref)
    CuContext(handle_ref[])
end

"""
    activate(ctx::CuContext)

Binds the specified CUDA context to the calling CPU thread.
"""
activate(ctx::CuContext) = cuCtxSetCurrent(ctx)

function CuContext(f::Function, dev::CuDevice, args...)
    ctx = CuContext(dev, args...)    # implicitly pushes
    try
        f(ctx)
    finally
        @assert pop!(CuContext) == ctx
        unsafe_destroy!(ctx)
    end
end

## context properties

"""
    CuCurrentDevice()

Returns the current device, or `nothing` if there is no active device.
"""
function CuCurrentDevice()
    device_ref = Ref{CUdevice}()
    res = unsafe_cuCtxGetDevice(device_ref)
    if res == ERROR_INVALID_CONTEXT
        return nothing
    elseif res != SUCCESS
        throw_api_error(res)
    end
    return CuDevice(Bool, device_ref[])
end

"""
    CuDevice(::CuContext)

Returns the device for a context.
"""
function CuDevice(ctx::CuContext)
    push!(CuContext, ctx)
    dev = CuCurrentDevice()
    pop!(CuContext)
    return dev
end

"""
    synchronize()

Block for the current context's tasks to complete.
"""
synchronize() = cuCtxSynchronize()

## cache config

export cache_config, cache_config!

@enum_without_prefix CUfunc_cache CU_

function cache_config()
    config = Ref{CUfunc_cache}()
    cuCtxGetCacheConfig(config)
    return config[]
end

function cache_config!(config::CUfunc_cache)
    cuCtxSetCacheConfig(config)
end

## shared memory config

export shmem_config, shmem_config!

@enum_without_prefix CUsharedconfig CU_

function shmem_config()
    config = Ref{CUsharedconfig}()
    cuCtxGetSharedMemConfig(config)
    return config[]
end

function shmem_config!(config::CUsharedconfig)
    cuCtxSetSharedMemConfig(config)
end

## limits

export limit, limit!

@enum_without_prefix CUlimit CU_

function limit(lim::CUlimit)
    val = Ref{Csize_t}()
    cuCtxGetLimit(val, lim)
    return Int(val[])
end

limit!(lim::CUlimit, val) = cuCtxSetLimit(lim, val)


text:
using FlightMechanicsUtils
using Test

@testset "Translate forces and moments" begin
    # No traslation
    m2 = translate_forces_moments([10, 20, 30], [50, 60, 90], [1, 1, 1], [1, 1, 1])
    @test isapprox(m2, [50, 60, 90])

    # Simple translation 1
    m2 = translate_forces_moments([1, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 1])
    @test isapprox(m2, [0, -1, 0])

    # Simple translation 2
    m2 = translate_forces_moments([1, 0, 0], [1, 0, 0], [0, 0, 0], [0, 0, 1])
    @test isapprox(m2, [1, -1, 0])

    # Simple translation 3
    m2 = translate_forces_moments(
        [10, 20, 30],
        [50, 60, 90],
        [1, 1, 1],
        [1, 1, 1] + [0, 1, 0],
    )
    @test isapprox(m2, [50, 60, 90] + [-30, 0, 10])

    # Reciprocal
    m2 = translate_forces_moments([10, 20, 30], [50, 60, 90], [1, 1, 1], [10, 20, 30])
    m1_ = translate_forces_moments([10, 20, 30], m2, [10, 20, 30], [1, 1, 1])
    @test isapprox([50, 60, 90], m1_)
end

@testset "Steiner" begin
    # No point translation
    p1 = [0, 0, 0]
    p2 = p1
    inertia = [
        1 0 0
        0 2 0
        0 0 3
    ]
    mass = 10
    @test isapprox(steiner_inertia(p1, inertia, mass, p2), inertia)

    # Point with no inertia
    p1 = [0, 0, 0]
    p2 = [10, 0, 0]
    inertia = zeros(3, 3)
    p2 = [10, 0, 0]
    inertia = zeros(3, 3)
    mass = 10

    exp_inertia = [
        0 0 0
        0 1000 0
        0 0 1000
    ]
    @test isapprox(steiner_inertia(p1, inertia, mass, p2), exp_inertia)

    # Point with with inertia
    p1 = [0, 0, 0]
    p2 = [10, 0, 0]
    inertia = [
        1 0 0
        0 2 0
        0 0 3
    ]
    mass = 10

    exp_inertia = [
        0 0 0
        0 1000 0
        0 0 1000
    ] + inertia

    @test isapprox(steiner_inertia(p1, inertia, mass, p2), exp_inertia)

end

@testset "Coordinated turn bank angle constrain" begin
    # Check case:  Stevens, B. L., Lewis, F. L., (1992).
    # Aircraft control and simulation: dynamics, controls design, and autonomous systems.
    # John Wiley & Sons. (Section 3.6, figure 3.6-2, page 192)
    ϕ = coordinated_turn_bank(0.3, deg2rad(13.7), deg2rad(0.0292), 502*0.3048, 0.0)
    # Take into account that this check case is a full trim not just the kinematic realtionship
    @test isapprox(rad2deg(ϕ), 78.3, atol=0.05)

    # Opposite turn
    ϕ = coordinated_turn_bank(-0.3, deg2rad(13.7), deg2rad(0.0292), 502*0.3048, 0.0)
    @test isapprox(rad2deg(ϕ), -78.3, atol=0.05)

    # Null turn rate
    ϕ = coordinated_turn_bank(0, deg2rad(13.7), deg2rad(0.0292), 502*0.3048, 0.0)
    @test isapprox(rad2deg(ϕ), 0)

    # For regression testing (no check values found in Stevens)
    ϕ = coordinated_turn_bank(0.1, deg2rad(13.7), deg2rad(0.0292), 502*0.3048, 10)
    @test isapprox(rad2deg(ϕ), 51.16941949883969)
end


text:
module VortexHelperColorSwitchTrigger
using ..Ahorn, Maple

const colorNames = Dict{String, Int}(
    "Blue" => 0,
    "Rose" => 1,
    "Orange" => 2,
    "Lime" => 3
)

@mapdef Trigger "VortexHelper/ColorSwitchTrigger" ColorSwitchTrigger(
					x::Integer, y::Integer,
					width::Integer = Maple.defaultTriggerWidth,
					height::Integer = Maple.defaultTriggerHeight,
					index::Integer = 0,
					oneUse::Bool = false, silent::Bool = false)

const placements = Ahorn.PlacementDict(
	"Color Switch Trigger (Vortex Helper)" => Ahorn.EntityPlacement(
		ColorSwitchTrigger,
		"rectangle"
	)
)

Ahorn.editingOptions(trigger::ColorSwitchTrigger) = Dict{String, Any}(
    "index" => colorNames
)

end

max_stars_repo_name: catapillie/VortexHelper

max_issues_repo_name: catapillie/VortexHelper

max_forks_repo_name: catapillie/VortexHelper


text:
import Pkg
import UUIDs

function get_latest_version_from_registries!(dep_to_latest_version::Dict{Package, Union{VersionNumber, Nothing}},
                                             registry_list::Vector{Pkg.Types.RegistrySpec})
      original_directory = pwd()
      num_registries = length(registry_list)
      registry_temp_dirs = Vector{String}(undef, num_registries)
      for i = 1:num_registries
          tmp_dir = mktempdir()
          atexit(() -> rm(tmp_dir; force = true, recursive = true))
          registry_temp_dirs[i] = tmp_dir
          name = registry_list[i].name
          url = registry_list[i].url
          previous_directory = pwd()
          cd(tmp_dir)
          run(`git clone $(url) $(name)`)
          cd(previous_directory)
      end
      for i = 1:num_registries
          previous_directory = pwd()
          registry_temp_dir = registry_temp_dirs[i]
          name = registry_list[i].name
          registry_path = joinpath(registry_temp_dir, name)
          cd(registry_path)
          registry_parsed = Pkg.TOML.parsefile(joinpath(registry_path, "Registry.toml"))
          packages = registry_parsed["packages"]
          for p in packages
              name = p[2]["name"]
              uuid = UUIDs.UUID(p[1])
              package = Package(name, uuid)
              path = p[2]["path"]
              if package in keys(dep_to_latest_version)
                  versions = VersionNumber.(collect(keys(Pkg.TOML.parsefile(joinpath(registry_path, path, "Versions.toml")))))
                  old_value = dep_to_latest_version[package]
                  if isnothing(old_value)
                      dep_to_latest_version[package] = maximum(versions)
                  else
                      dep_to_latest_version[package] = max(old_value, maximum(versions))
                  end
              end
          end
          cd(previous_directory)
      end
      cd(original_directory)
      for tmp_dir in registry_temp_dirs
          rm(tmp_dir; force = true, recursive = true)
      end
      return dep_to_latest_version
end


text:
@testset "Simple simulated scanner" begin
  scannerName_ = "TestSimpleSimulatedScanner"
  scanner = MPIScanner(scannerName_)

  path = normpath(string(@__DIR__), "TestConfigs/TestSimpleSimulatedScanner/Sequences/Sequence.toml")
  # sequence = sequenceFromTOML(path)
  # setupSequence(scanner, sequence)

  # uMeas, uRef = readData(getDAQ(scanner), 1, 1)

  close(scanner)
end


text:
# the following large scale terms have not been implemented, as their increases are not linear.
const _large_scales = Number[
    10^153,     10^183,     10^213,     10^243,     10^273,     10^303,     10^306,     10^333,     10^336,     10^363,
    10^366,     10^393,     10^423,     10^453,     10^483,     10^513,     10^543,     10^573,     10^603,     10^903,
    10^1203,    10^1503,    10^1803,    10^2103,    10^2403,    10^2703,    10^3003
    ]

const _large_scale_modern = String[
    "quinquagintillion",    "sexagintillion",       "septuagintillion",       "octogintillion",         "nonagintillion",
    "centillion",           "uncentillion",         "decicentillion",         "undecicentillion",       "viginticentillion",
    "unviginticentillion",  "trigintacentillion",   "quadragintacentillion",  "quinquagintacentillion", "sexagintacentillion",
    "septuagintacentillion","octogintacentillion",  "onagintacentillion",     "ducentillion",           "trecentillion",
    "quadringentillion",    "quingentillion",       "sescentillion",          "septingentillion",       "octingentillion",
    "nongentillion",        "millinillion"
    ]
const _large_scale_traditional_british = String[
    "thousand quinvigintillion",            "thousand trigintillion",                   "thousand quintrigintillion",
    "thousand quadragintillion",            "thousand quinquadragintillion",            "thousand quinquagintillion",
    "unquinquagintillion",                  "thousand quinquinquagintillion",           "sesquinquagintillion",
    "thousand sexagintillion",              "unsexagintillion",                         "thousand quinsexagintillion",
    "thousand septuagintillion",            "thousand quinseptuagintillion",            "thousand octogintillion",
    "thousand quinoctogintillion",          "thousand nonagintillion",                  "thousand quinnonagintillion", "
    thousand centillion",                   "thousand quinquagintacentillion",          "thousand ducentillion",
    "thousand quinquagintaducentillion",    "thousand trecentillion",                   "thousand quinquagintatrecentillion",
    "thousand quadringentillion",           "thousand quinquagintaquadringentillion",   "thousand quingentillion"
    ]
const _large_scale_traditional_european = String[
    "quinvigintilliard",        "trigintilliard",                   "quintrigintilliard",   "quadragintilliard",
    "quinquadragintilliard",    "quinquagintilliard",               "unquinquagintillion",  "quinquinquagintilliard",
    "sesquinquagintillion",     "sexagintilliard",                  "unsexagintillion",     "quinsexagintilliard",
    "septuagintilliard",        "quinseptuagintilliard",            "octogintilliard",      "quinoctogintilliard",
    "nonagintilliard",          "quinnonagintilliard",              "centilliard",          "quinquagintacentilliard",
    "ducentilliard",            "quinquagintaducentilliard",        "trecentilliard",       "quinquagintatrecentilliard",
    "quadringentilliard",       "quinquagintaquadringentilliard",   "quingentilliard"
]


text:
struct VisualizationGrid{Dc,Dp,T} <: Grid{Dc,Dp}
  sub_grid::UnstructuredGrid{Dc,Dp,T,false}
  sub_cell_to_cell::Vector{Int}
  cell_to_refpoints::CompressedArray{Vector{Point{Dc,T}},1,Vector{Vector{Point{Dc,T}}},Vector{Int8}}
end

get_reffes(g::VisualizationGrid) = get_reffes(g.sub_grid)

get_cell_type(g::VisualizationGrid) = get_cell_type(g.sub_grid)

get_node_coordinates(g::VisualizationGrid) = get_node_coordinates(g.sub_grid)

get_cell_nodes(g::VisualizationGrid) = get_cell_nodes(g.sub_grid)

function VisualizationGrid(trian::Triangulation, ref_grids::Vector{<:UnstructuredGrid})

  cell_to_ctype = collect1d(get_cell_type(trian))
  ctype_to_refpoints = map(get_node_coordinates, ref_grids)
  cell_to_refpoints = CompressedArray(ctype_to_refpoints,cell_to_ctype)
  cell_map = get_cell_map(trian)
  cell_to_points = evaluate(cell_map, cell_to_refpoints)

  node_to_coords, cell_to_offset = _prepare_node_to_coords(cell_to_points)

  ctype_to_scell_to_snodes = map(get_cell_nodes,ref_grids)

  sub_cell_to_nodes, sub_cell_to_cell = _prepare_sub_cell_to_nodes(
    cell_to_ctype,ctype_to_scell_to_snodes,cell_to_offset)

  ctype_to_reffes = map(get_reffes,ref_grids)
  ctype_to_scell_type = map(get_cell_type,ref_grids)
  sctype_to_reffe, sub_cell_to_sctype = _prepare_sctype_to_reffe(
    ctype_to_reffes,ctype_to_scell_type,cell_to_ctype)

  sub_grid = UnstructuredGrid(
    node_to_coords,
    sub_cell_to_nodes,
    sctype_to_reffe,
    sub_cell_to_sctype,
    Val{false}())

  VisualizationGrid(sub_grid,sub_cell_to_cell,cell_to_refpoints)

end

struct VisualizationData
  grid::VisualizationGrid
  celldata
  nodaldata
end

"""
"""
function visualization_data(trian::Triangulation; order=-1, nsubcells=-1, celldata=Dict(), cellfields=Dict())

  if order == -1 && nsubcells == -1
    # Use the given cells as visualization cells
    f = (reffe) -> UnstructuredGrid(reffe)
  elseif order != -1 && nsubcells == -1
    # Use cells of given order as visualization cells
    f = (reffe) -> UnstructuredGrid(LagrangianRefFE(Float64,get_polytope(reffe),order))
  elseif order == -1 && nsubcells != -1
    # Use use linear sub-cells with nsubcells per direction
    f = (reffe) -> UnstructuredGrid(compute_reference_grid(reffe,nsubcells))
  else
    @unreachable "order and nsubcells kw-arguments can not be given at the same time"
  end

  ref_grids = map(f, get_reffes(trian))
  visgrid = VisualizationGrid(trian,ref_grids)

  cdata = _prepare_cdata(celldata,visgrid.sub_cell_to_cell)
  pdata = _prepare_pdata(trian,cellfields,visgrid.cell_to_refpoints)

  return VisualizationData(visgrid, cdata, pdata)
end

function _prepare_node_to_coords(cell_to_points)
  cell_to_offset = zeros(Int,length(cell_to_points))
  P = eltype(eltype(cell_to_points))
  node_to_coords = P[]
  cache = array_cache(cell_to_points)
  _prepare_node_to_coords_fill!(node_to_coords,cell_to_offset,cache,cell_to_points)
  (node_to_coords, cell_to_offset)
end

function _prepare_node_to_coords_fill!(node_to_coords,cell_to_offset,cache,cell_to_points)
  offset = 0
  for cell in 1:length(cell_to_points)
    cell_to_offset[cell] = offset
    points = getindex!(cache,cell_to_points,cell)
    for point in points
      push!(node_to_coords, point)
      offset += 1
    end
  end
end

function _prepare_sub_cell_to_nodes(
  cell_to_ctype,ctype_to_scell_to_snodes,cell_to_offset)

  sub_cell_to_nodes = Table(Int[],Int32[1])
  sub_cell_to_cell = Int[]

  for (cell, ctype) in enumerate(cell_to_ctype)
    scell_to_snodes = ctype_to_scell_to_snodes[ctype]
    offset = cell_to_offset[cell]
    for (scell, snodes) in enumerate(scell_to_snodes)
      push!(sub_cell_to_cell,cell)
      push!(sub_cell_to_nodes.ptrs,length(snodes))
      for snode in snodes
        node = snode+offset
        push!(sub_cell_to_nodes.data,node)
      end
    end
  end

  length_to_ptrs!(sub_cell_to_nodes.ptrs)

  (sub_cell_to_nodes, sub_cell_to_cell)
end

function _prepare_sctype_to_reffe(ctype_to_r_to_reffe,ctype_to_scell_to_r,cell_to_ctype)

  i_to_reffe = vcat(ctype_to_r_to_reffe...)
  u_to_reffe, i_to_u = _find_unique_with_indices(i_to_reffe)
  ctype_to_nreffes = map(length,ctype_to_r_to_reffe)
  sub_cell_to_u = _prepare_sub_cell_to_u(
    ctype_to_r_to_reffe,ctype_to_nreffes,ctype_to_scell_to_r,cell_to_ctype,i_to_u)

  (u_to_reffe, sub_cell_to_u)
end

function _prepare_sub_cell_to_u(
  ctype_to_r_to_reffe,ctype_to_nreffes,ctype_to_scell_to_r,cell_to_ctype,i_to_u)

  i = 1
  ctype_to_r_to_u = Vector{Int}[]
  for (ctype, r_to_reffes) in enumerate(ctype_to_r_to_reffe)
    nreffes = ctype_to_nreffes[ctype]
    r_to_u = zeros(Int,nreffes)
    for r in 1:nreffes
      r_to_u[r] = i_to_u[i]
      i +=1
    end
    push!(ctype_to_r_to_u,r_to_u)
  end

  sub_cell_to_u = Int8[]
  for ctype in cell_to_ctype
    scell_to_r = ctype_to_scell_to_r[ctype]
    r_to_u = ctype_to_r_to_u[ctype]
    for r in scell_to_r
      push!(sub_cell_to_u,r_to_u[r])
    end
  end

  sub_cell_to_u

end

function _prepare_pdata(trian,cellfields,samplingpoints)
  pdata = Dict()
  for (k,v) in cellfields
    _v = CellField(v,trian)
    pdata[k], = _prepare_node_to_coords(evaluate(_v,samplingpoints))
  end
  pdata
end

function _prepare_cdata(celldata,sub_cell_to_cell)
  cdata = Dict()
  for (k,v) in celldata
    cell_to_a = collect(v)
    T = eltype(cell_to_a)
    sub_cell_to_a = zeros(T,length(sub_cell_to_cell))
    _fill_sub_cell_to_a!(sub_cell_to_a,cell_to_a,sub_cell_to_cell)
    cdata[k] = sub_cell_to_a
  end
  k2 = "cell"
  @assert ! haskey(cdata,k2) "cell is a reserved key"
  cdata[k2] = sub_cell_to_cell
  cdata
end

function _fill_sub_cell_to_a!(sub_cell_to_a,cell_to_a,sub_cell_to_cell)
  for (sub_cell, cell) in enumerate(sub_cell_to_cell)
    sub_cell_to_a[sub_cell] = cell_to_a[cell]
  end
end


text:
# make predicted postag file
# args: gold-postag file with vectors, predicted postag file without vectors, output file

using HDF5,JLD,KUparser
a=load(ARGS[1])
b=load(ARGS[2])
@show a["deprel"]==b["deprel"]
@show a["postag"]==b["postag"]
aform=map(x->x.form, a["corpus"]); bform=map(x->x.form, b["corpus"])
@show aform==bform
for i=1:length(a["corpus"])
    a["corpus"][i].postag = b["corpus"][i].postag
end
save(ARGS[3], "corpus", a["corpus"], "postag", a["postag"], "deprel", a["deprel"])


text:
include("helpers.jl")

function laff_scal(alpha::T, x::T) where T <: Matrix{Number}
    # Como alpha é um escalar
    # apesar de estar representado como uma matriz
    # pegamos apenas o primeiro, e único, elemento da matriz
    x .* alpha[1]

end

## Testar ##
x = Matrix([1,2,3], 1, 3)
alpha = Matrix([0.5], 1, 1)

laff_scal(alpha, x)

max_stars_repo_path: LAFF/Semana 1/laff_scal.jl

max_issues_repo_path: LAFF/Semana 1/laff_scal.jl

max_forks_repo_path: LAFF/Semana 1/laff_scal.jl


text:
# A BallTree (also called Metric tree) is a tree that is created
# from successively splitting points into surrounding hyper spheres
# which radius are determined from the given metric.
# The tree uses the triangle inequality to prune the search space
# when finding the neighbors to a point,
immutable BallTree{V <: AbstractVector, N, T, M <: Metric} <: NNTree{V, M}
    data::Vector{V}
    hyper_spheres::Vector{HyperSphere{N, T}} # Each hyper sphere bounds its children
    indices::Vector{Int}                  # Translates from tree index -> point index
    metric::M                             # Metric used for tree
    tree_data::TreeData                   # Some constants needed
    reordered::Bool                       # If the data has been reordered
end

# When we create the bounding spheres we need some temporary arrays.
# We create a type to hold them to not allocate these arrays at every
# function call and to reduce the number of parameters in the tree builder.
immutable ArrayBuffers{N, T <: AbstractFloat}
    center::MVector{N, T}
end

function ArrayBuffers{N, T}(::Type{Val{N}}, ::Type{T})
    ArrayBuffers(zeros(MVector{N, T}))
end

"""
    BallTree(data [, metric = Euclidean(), leafsize = 10]) -> balltree

Creates a `BallTree` from the data using the given `metric` and `leafsize`.
"""
function BallTree{V <: AbstractArray, M <: Metric}(data::Vector{V},
                                                   metric::M = Euclidean();
                                                   leafsize::Int = 10,
                                                   reorder::Bool = true,
                                                   storedata::Bool = true,
                                                   reorderbuffer::Vector{V} = Vector{V}(),
                                                   indicesfor::Symbol = :data)

    reorder = !isempty(reorderbuffer) || (storedata ? reorder : false)

    tree_data = TreeData(data, leafsize)
    n_d = length(V)
    n_p = length(data)

    array_buffs = ArrayBuffers(Val{length(V)}, get_T(eltype(V)))
    indices = collect(1:n_p)

    # Bottom up creation of hyper spheres so need spheres even for leafs)
    hyper_spheres = Array(HyperSphere{length(V), eltype(V)}, tree_data.n_internal_nodes + tree_data.n_leafs)

        if reorder
        indices_reordered = Vector{Int}(n_p)
        if isempty(reorderbuffer)
            data_reordered = Vector{V}(n_p)
        else
            data_reordered = reorderbuffer
        end
    else
        # Dummy variables
        indices_reordered = Vector{Int}()
        data_reordered = Vector{V}()
    end

    # Call the recursive BallTree builder
    build_BallTree(1, data, data_reordered, hyper_spheres, metric, indices, indices_reordered,
                   1,  length(data), tree_data, array_buffs, reorder)

    if reorder
       data = data_reordered
       indices = indicesfor == :data ? indices_reordered : collect(1:n_p)
    end

    BallTree(storedata ? data : similar(data,0), hyper_spheres, indices, metric, tree_data, reorder)
end

function BallTree{T <: AbstractFloat, M <: Metric}(data::Matrix{T},
                                                   metric::M = Euclidean();
                                                   leafsize::Int = 10,
                                                   storedata::Bool = true,
                                                   reorder::Bool = true,
                                                   reorderbuffer::Matrix{T} = Matrix{T}(),
                                                   indicesfor::Symbol = :data)
    dim = size(data, 1)
    npoints = size(data, 2)
    points = reinterpret(SVector{dim, T}, data, (length(data) ÷ dim, ))
    if isempty(reorderbuffer)
        reorderbuffer_points = Vector{SVector{dim, T}}()
    else
        reorderbuffer_points = reinterpret(SVector{dim, T}, reorderbuffer, (length(reorderbuffer) ÷ dim, ))
    end
    BallTree(points ,metric, leafsize = leafsize, storedata = storedata, reorder = reorder,
            reorderbuffer = reorderbuffer_points, indicesfor = indicesfor)
end

# Recursive function to build the tree.
function build_BallTree{V <: AbstractVector, N, T}(index::Int,
                                                   data::Vector{V},
                                                   data_reordered::Vector{V},
                                                   hyper_spheres::Vector{HyperSphere{N, T}},
                                                   metric::Metric,
                                                   indices::Vector{Int},
                                                   indices_reordered::Vector{Int},
                                                   low::Int,
                                                   high::Int,
                                                   tree_data::TreeData,
                                                   array_buffs::ArrayBuffers{N, T},
                                                   reorder::Bool)

    n_points = high - low + 1 # Points left
    if n_points <= tree_data.leafsize
        if reorder
            reorder_data!(data_reordered, data, index, indices, indices_reordered, tree_data)
        end
        # Create bounding sphere of points in leaf node by brute force
        hyper_spheres[index] = create_bsphere(data, metric, indices, low, high, array_buffs)
        return
    end

    # Find split such that one of the sub trees has 2^p points
    # and the left sub tree has more points
    mid_idx = find_split(low, tree_data.leafsize, n_points)

    # Brute force to find the dimension with the largest spread
    split_dim = find_largest_spread(data, indices, low, high)

    # Sort the data at the mid_idx boundary using the split_dim
    # to compare
    select_spec!(indices, mid_idx, low, high, data, split_dim)

    build_BallTree(getleft(index), data, data_reordered, hyper_spheres, metric,
                   indices, indices_reordered, low, mid_idx - 1,
                   tree_data, array_buffs, reorder)

    build_BallTree(getright(index), data, data_reordered, hyper_spheres, metric,
                  indices, indices_reordered, mid_idx, high,
                  tree_data, array_buffs, reorder)

    # Finally create bounding hyper sphere from the two children's hyper spheres
    hyper_spheres[index]  =  create_bsphere(metric, hyper_spheres[getleft(index)],
                                            hyper_spheres[getright(index)],
                                            array_buffs)
end

function _knn(tree::BallTree,
              point::AbstractVector,
              best_idxs::Vector{Int},
              best_dists::Vector,
              skip::Function)

    knn_kernel!(tree, 1, point, best_idxs, best_dists, skip)
    return
end

function knn_kernel!{V, F}(tree::BallTree{V},
                           index::Int,
                           point::AbstractArray,
                           best_idxs ::Vector{Int},
                           best_dists::Vector,
                           skip::F)
    @NODE 1
    if isleaf(tree.tree_data.n_internal_nodes, index)
        add_points_knn!(best_dists, best_idxs, tree, index, point, true, skip)
        return
    end

    left_sphere = tree.hyper_spheres[getleft(index)]
    right_sphere = tree.hyper_spheres[getright(index)]

    left_dist = max(zero(eltype(V)), evaluate(tree.metric, point, left_sphere.center) - left_sphere.r)
    right_dist = max(zero(eltype(V)), evaluate(tree.metric, point, right_sphere.center) - right_sphere.r)

    if left_dist <= best_dists[1] || right_dist <= best_dists[1]
        if left_dist < right_dist
            knn_kernel!(tree, getleft(index), point, best_idxs, best_dists, skip)
            if right_dist <=  best_dists[1]
                 knn_kernel!(tree, getright(index), point, best_idxs, best_dists, skip)
             end
        else
            knn_kernel!(tree, getright(index), point, best_idxs, best_dists, skip)
            if left_dist <=  best_dists[1]
                 knn_kernel!(tree, getleft(index), point, best_idxs, best_dists, skip)
            end
        end
    end
    return
end

function _inrange{V}(tree::BallTree{V},
                     point::AbstractVector,
                     radius::Number,
                     idx_in_ball::Vector{Int})
    ball = HyperSphere(convert(V, point), convert(eltype(V), radius))  # The "query ball"
    inrange_kernel!(tree, 1, point, ball, idx_in_ball)  # Call the recursive range finder
    return
end

function inrange_kernel!(tree::BallTree,
                         index::Int,
                         point::AbstractVector,
                         query_ball::HyperSphere,
                         idx_in_ball::Vector{Int})
    @NODE 1
    sphere = tree.hyper_spheres[index]

    # If the query ball in the bounding sphere for the current sub tree
    # do not intersect we can disrecard the whole subtree
    if !intersects(tree.metric, sphere, query_ball)
        return
    end

    # At a leaf node, check all points in the leaf node
    if isleaf(tree.tree_data.n_internal_nodes, index)
        add_points_inrange!(idx_in_ball, tree, index, point, query_ball.r, true)
        return
    end

    # The query ball encloses the sub tree bounding sphere. Add all points in the
    # sub tree without checking the distance function.
    if encloses(tree.metric, sphere, query_ball)
         addall(tree, index, idx_in_ball)
    else
        # Recursively call the left and right sub tree.
        inrange_kernel!(tree,  getleft(index), point, query_ball, idx_in_ball)
        inrange_kernel!(tree, getright(index), point, query_ball, idx_in_ball)
    end
end