diff --git a/src/Utilities/CleverDicts.jl b/src/Utilities/CleverDicts.jl index e7aa70c417..db571d5308 100644 --- a/src/Utilities/CleverDicts.jl +++ b/src/Utilities/CleverDicts.jl @@ -5,6 +5,22 @@ module CleverDicts # solvers using `CleverDicts` to implement it themselves. import MathOptInterface +import OrderedCollections + +""" + index_to_key(::Type{K}, index::Int) + +Create a new key associated with the integer value `index`. +""" +function index_to_key end + +""" + key_to_index(key::K) + +Map `key` to an integer valued index, assuming that there have been no +deletions. +""" +function key_to_index end function index_to_key(::Type{MathOptInterface.VariableIndex}, index::Int64) return MathOptInterface.VariableIndex(index) @@ -21,8 +37,6 @@ key_to_index(key::MathOptInterface.Index) = key.value # Now, on with `CleverDicts`. -import OrderedCollections - """ CleverDict{K, V} @@ -88,32 +102,19 @@ mutable struct CleverDict{K,V,F<:Function,I<:Function} <: AbstractDict{K,V} ) end end + function CleverDict{K,V}(n::Integer = 0) where {K,V} return CleverDict{K,V}(key_to_index, index_to_key, n) end -""" - index_to_key(::Type{K}, index::Int) - -Create a new key associated with the integer value `index`. -""" -function index_to_key end - -""" - key_to_index(key::K) - -Map `key` to an integer valued index, assuming that there have been no -deletions. -""" -function key_to_index end - _is_dense(c::CleverDict) = c.is_dense function _inverse_hash(c::CleverDict{K}, index::Integer) where {K} return c.inverse_hash(Int64(index))::K end + function _inverse_hash( - c::CleverDict{K,V,F,typeof(index_to_key)}, + ::CleverDict{K,V,F,typeof(index_to_key)}, index::Integer, ) where {K,V,F<:Function} return index_to_key(K, Int64(index))::K @@ -158,11 +159,10 @@ function Base.haskey(c::CleverDict{K}, key::K) where {K} end function Base.keys(c::CleverDict) - return if _is_dense(c) - [_inverse_hash(c, index) for index in c.set] - else - collect(keys(c.dict)) + if _is_dense(c) + return [_inverse_hash(c, index) for index in c.set] end + return collect(keys(c.dict)) end function Base.get(c::CleverDict, key, default) @@ -171,9 +171,8 @@ function Base.get(c::CleverDict, key, default) return default end return c.vector[c.hash(key)::Int64] - else - return get(c.dict, key, default) end + return get(c.dict, key, default) end function Base.getindex(c::CleverDict, key) @@ -182,9 +181,8 @@ function Base.getindex(c::CleverDict, key) throw(KeyError(key)) end return c.vector[c.hash(key)::Int64] - else - return c.dict[key] end + return c.dict[key] end function Base.setindex!(c::CleverDict{K,V}, value::V, key::K)::V where {K,V} @@ -237,7 +235,7 @@ function Base.delete!(c::CleverDict{K}, k::K) where {K} if !isempty(c.vector) empty!(c.vector) end - return + return c end struct LinearIndex @@ -276,51 +274,48 @@ end Base.haskey(::CleverDict, key) = false # Here, we implement the iterate functions for our `CleverDict`. For either -# backend (`OrderedDict` or `Vector`+`BitSet`) we return the same State type +# backend (`OrderedDict` or `Vector`+`BitSet`) we return the same _State type # so that `iterate` returns the same # type regardless of the backing datastructure. To help inference, we convert # the return type. Don't use a type-assertion because this doesn't support `V` # being an abstract type. -struct State +struct _State int::Int64 uint::UInt64 # ::Integer is needed for 32-bit machines. - State(i::Integer) = new(Int64(i), UInt64(0)) - State(i::Integer, j::UInt64) = new(Int64(i), j) + _State(i::Integer) = new(Int64(i), UInt64(0)) + _State(i::Integer, j::UInt64) = new(Int64(i), j) end function Base.iterate( c::CleverDict{K,V}, -)::Union{Nothing,Tuple{Pair{K,V},State}} where {K,V} +)::Union{Nothing,Tuple{Pair{K,V},_State}} where {K,V} if _is_dense(c) itr = iterate(c.set) if itr === nothing - return nothing - else - el, i = itr - new_el = _inverse_hash(c, el) => c.vector[el]::V - @static if VERSION >= v"1.4.0" - return new_el, State(i[2], i[1]) - else - return new_el, State(i) - end + return end - else - itr = iterate(c.dict) - if itr === nothing - return nothing + el, i = itr + new_el = _inverse_hash(c, el) => c.vector[el]::V + @static if VERSION >= v"1.4.0" + return new_el, _State(i[2], i[1]) else - el, i = itr - return convert(Pair{K,V}, el), State(i) + return new_el, _State(i) end end + itr = iterate(c.dict) + if itr === nothing + return + end + el, i = itr + return convert(Pair{K,V}, el), _State(i) end function Base.iterate( c::CleverDict{K,V}, - s::State, -)::Union{Nothing,Tuple{Pair{K,V},State}} where {K,V} + s::_State, +)::Union{Nothing,Tuple{Pair{K,V},_State}} where {K,V} # Note that BitSet is defined by machine Int, so we need to cast any `.int` # fields to `Int` for 32-bit machines. if _is_dense(c) @@ -330,25 +325,22 @@ function Base.iterate( itr = iterate(c.set, Int(s.int)) end if itr === nothing - return nothing - else - el, i = itr - new_el = _inverse_hash(c, el) => c.vector[el]::V - @static if VERSION >= v"1.4.0" - return new_el, State(i[2], i[1]) - else - return new_el, State(i) - end + return end - else - itr = iterate(c.dict, Int(s.int)) - if itr === nothing - return nothing + el, i = itr + new_el = _inverse_hash(c, el) => c.vector[el]::V + @static if VERSION >= v"1.4.0" + return new_el, _State(i[2], i[1]) else - el, i = itr - return convert(Pair{K,V}, el), State(i) + return new_el, _State(i) end end + itr = iterate(c.dict, Int(s.int)) + if itr === nothing + return + end + el, i = itr + return convert(Pair{K,V}, el), _State(i) end # we do not have to implement values and keys functions because they can rely @@ -400,6 +392,7 @@ function Base.convert( ) where {K,V} return d end + function Base.convert( ::Type{CleverDict{K,V,typeof(key_to_index),typeof(index_to_key)}}, src::AbstractDict{K,V}, diff --git a/test/Utilities/CleverDicts.jl b/test/Utilities/CleverDicts.jl index d264d08d65..ff515983be 100644 --- a/test/Utilities/CleverDicts.jl +++ b/test/Utilities/CleverDicts.jl @@ -1,287 +1,306 @@ -using MathOptInterface, Test +module TestCleverDicts + +using MathOptInterface +using Test const CleverDicts = MathOptInterface.Utilities.CleverDicts +const MOI = MathOptInterface + +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end # Note: `MyKey` is just for testing. You wouldn't want to use it in practice # because the key type of the dictionary isn't a concrete type. struct MyKey{X} MyKey(x) = new{Int64(x)}() end + CleverDicts.key_to_index(::MyKey{X}) where {X} = X CleverDicts.index_to_key(::Type{MyKey}, index::Int64) = MyKey(index) -@testset "CleverDict" begin - @testset "MyKey type" begin - d = CleverDicts.CleverDict{MyKey,String}() - key = CleverDicts.add_item(d, "first") - @test key == MyKey(1) - @test d[MyKey(1)] == "first" - end +function test_MyKey() + d = CleverDicts.CleverDict{MyKey,String}() + key = CleverDicts.add_item(d, "first") + @test key == MyKey(1) + @test d[MyKey(1)] == "first" +end - @testset "Abstract Value" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,Any}() - key = CleverDicts.add_item(d, :a) - @test key == MathOptInterface.VariableIndex(1) - @test d[MathOptInterface.VariableIndex(1)] == :a - key = CleverDicts.add_item(d, "b") - @test key == MathOptInterface.VariableIndex(2) - @test d[MathOptInterface.VariableIndex(2)] == "b" - for (k, v) in d - if k.value == 1 - @test v == :a - else - @test k.value == 2 - @test v == "b" - end +function test_Abstract_Value() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,Any}() + key = CleverDicts.add_item(d, :a) + @test key == MathOptInterface.VariableIndex(1) + @test d[MathOptInterface.VariableIndex(1)] == :a + key = CleverDicts.add_item(d, "b") + @test key == MathOptInterface.VariableIndex(2) + @test d[MathOptInterface.VariableIndex(2)] == "b" + for (k, v) in d + if k.value == 1 + @test v == :a + else + @test k.value == 2 + @test v == "b" end end + return +end - @testset "get/set" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - key = CleverDicts.add_item(d, "first") - @test key == MathOptInterface.VariableIndex(1) - @test get(d, key, nothing) == "first" - @test get(d, MathOptInterface.VariableIndex(2), nothing) === nothing - @test Dict(key => "first") == d - @test Dict(key => "second") != d - sizehint!(d, 1) - @test d[key] == "first" - @test haskey(d, key) == true - @test_throws KeyError d[MathOptInterface.VariableIndex(2)] - delete!(d, key) - sizehint!(d, 2) - @test_throws KeyError d[key] - # set index is valid now - # @test_throws KeyError d[key] = "key" - @test haskey(d, key) == false - key2 = CleverDicts.add_item(d, "second") - @test key2 == MathOptInterface.VariableIndex(2) - @test d[key2] == "second" - d[key2] = "third" - @test d[key2] == "third" - @test get(d, key, nothing) === nothing - @test get(d, key2, nothing) === "third" - @test Dict(key2 => "second") != d - @test Dict(key2 => "third") == d +function test_get_set() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + key = CleverDicts.add_item(d, "first") + @test key == MathOptInterface.VariableIndex(1) + @test get(d, key, nothing) == "first" + @test get(d, MathOptInterface.VariableIndex(2), nothing) === nothing + @test Dict(key => "first") == d + @test Dict(key => "second") != d + sizehint!(d, 1) + @test d[key] == "first" + @test haskey(d, key) == true + @test_throws KeyError d[MathOptInterface.VariableIndex(2)] + delete!(d, key) + sizehint!(d, 2) + @test_throws KeyError d[key] + # set index is valid now + # @test_throws KeyError d[key] = "key" + @test haskey(d, key) == false + key2 = CleverDicts.add_item(d, "second") + @test key2 == MathOptInterface.VariableIndex(2) + @test d[key2] == "second" + d[key2] = "third" + @test d[key2] == "third" + @test get(d, key, nothing) === nothing + @test get(d, key2, nothing) === "third" + @test Dict(key2 => "second") != d + @test Dict(key2 => "third") == d - empty!(d) + empty!(d) - key = CleverDicts.add_item(d, "first") - @test key == MathOptInterface.VariableIndex(1) - @test d[key] == "first" - d[key] = "zeroth" - @test d[key] == "zeroth" - @test haskey(d, key) == true - @test_throws KeyError d[MathOptInterface.VariableIndex(2)] - delete!(d, key) - @test_throws KeyError d[key] - # set index is valid now - # @test_throws KeyError d[key] = "key" - @test haskey(d, key) == false - key2 = CleverDicts.add_item(d, "second") - @test key2 == MathOptInterface.VariableIndex(2) - @test d[key2] == "second" - end + key = CleverDicts.add_item(d, "first") + @test key == MathOptInterface.VariableIndex(1) + @test d[key] == "first" + d[key] = "zeroth" + @test d[key] == "zeroth" + @test haskey(d, key) == true + @test_throws KeyError d[MathOptInterface.VariableIndex(2)] + delete!(d, key) + @test_throws KeyError d[key] + # set index is valid now + # @test_throws KeyError d[key] = "key" + @test haskey(d, key) == false + key2 = CleverDicts.add_item(d, "second") + @test key2 == MathOptInterface.VariableIndex(2) + @test d[key2] == "second" + return +end - @testset "LinearIndex" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - key = CleverDicts.add_item(d, "first") - @test d[CleverDicts.LinearIndex(1)] == "first" - key2 = CleverDicts.add_item(d, "second") - @test d[CleverDicts.LinearIndex(2)] == "second" - @test length(d) == 2 - delete!(d, key) - @test d[CleverDicts.LinearIndex(1)] == "second" - @test_throws KeyError d[CleverDicts.LinearIndex(2)] - @test length(d) == 1 - end +function test_LinearIndex() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + key = CleverDicts.add_item(d, "first") + @test d[CleverDicts.LinearIndex(1)] == "first" + key2 = CleverDicts.add_item(d, "second") + @test d[CleverDicts.LinearIndex(2)] == "second" + @test length(d) == 2 + delete!(d, key) + @test d[CleverDicts.LinearIndex(1)] == "second" + @test_throws KeyError d[CleverDicts.LinearIndex(2)] + @test length(d) == 1 + return +end - @testset "keys/values" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - key = CleverDicts.add_item(d, "first") - key2 = CleverDicts.add_item(d, "second") - @test collect(keys(d)) == [ - MathOptInterface.VariableIndex(1), - MathOptInterface.VariableIndex(2), - ] - @test collect(values(d)) == ["first", "second"] - delete!(d, key) - key3 = CleverDicts.add_item(d, "third") - @test collect(keys(d)) == [ - MathOptInterface.VariableIndex(2), - MathOptInterface.VariableIndex(3), - ] - @test collect(values(d)) == ["second", "third"] - end +function test_keys_values() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + key = CleverDicts.add_item(d, "first") + key2 = CleverDicts.add_item(d, "second") + @test collect(keys(d)) == + [MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(2)] + @test collect(values(d)) == ["first", "second"] + delete!(d, key) + key3 = CleverDicts.add_item(d, "third") + @test collect(keys(d)) == + [MathOptInterface.VariableIndex(2), MathOptInterface.VariableIndex(3)] + @test collect(values(d)) == ["second", "third"] + return +end - @testset "iterate" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - key = CleverDicts.add_item(d, "first") - key2 = CleverDicts.add_item(d, "second") - my_keys = MathOptInterface.VariableIndex[] - my_values = String[] - for (k, v) in d - push!(my_keys, k) - push!(my_values, v) - end - @test my_keys == [ - MathOptInterface.VariableIndex(1), - MathOptInterface.VariableIndex(2), - ] - @test my_values == ["first", "second"] - delete!(d, key) - key3 = CleverDicts.add_item(d, "third") - my_keys = MathOptInterface.VariableIndex[] - my_values = String[] - for (k, v) in d - push!(my_keys, k) - push!(my_values, v) - end - @test my_keys == [ - MathOptInterface.VariableIndex(2), - MathOptInterface.VariableIndex(3), - ] - @test my_values == ["second", "third"] +function test_iterate() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + key = CleverDicts.add_item(d, "first") + key2 = CleverDicts.add_item(d, "second") + my_keys = MathOptInterface.VariableIndex[] + my_values = String[] + for (k, v) in d + push!(my_keys, k) + push!(my_values, v) end - - @testset "iterate ii" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - key = CleverDicts.add_item(d, "first") - key2 = CleverDicts.add_item(d, "second") - my_keys = MathOptInterface.VariableIndex[] - my_values = String[] - for (k, v) in d - push!(my_keys, k) - push!(my_values, v) - end - @test my_keys == [ - MathOptInterface.VariableIndex(1), - MathOptInterface.VariableIndex(2), - ] - @test my_values == ["first", "second"] - delete!(d, key) - @test d[CleverDicts.LinearIndex(1)] == "second" - key3 = CleverDicts.add_item(d, "third") - my_keys = MathOptInterface.VariableIndex[] - my_values = String[] - for (k, v) in d - push!(my_keys, k) - push!(my_values, v) - end - @test my_keys == [ - MathOptInterface.VariableIndex(2), - MathOptInterface.VariableIndex(3), - ] - @test my_values == ["second", "third"] + @test my_keys == + [MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(2)] + @test my_values == ["first", "second"] + delete!(d, key) + key3 = CleverDicts.add_item(d, "third") + my_keys = MathOptInterface.VariableIndex[] + my_values = String[] + for (k, v) in d + push!(my_keys, k) + push!(my_values, v) end + @test my_keys == + [MathOptInterface.VariableIndex(2), MathOptInterface.VariableIndex(3)] + @test my_values == ["second", "third"] + return +end - @testset "iterate iii" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - y = 0 - for (k, v) in d - y += 1 - end - @test y == 0 +function test_iterate_ii() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + key = CleverDicts.add_item(d, "first") + key2 = CleverDicts.add_item(d, "second") + my_keys = MathOptInterface.VariableIndex[] + my_values = String[] + for (k, v) in d + push!(my_keys, k) + push!(my_values, v) end - - @testset "haskey" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - @test !haskey(d, 1) - k = CleverDicts.add_item(d, "a") - @test haskey(d, k) - j = CleverDicts.add_item(d, "b") - @test haskey(d, j) - delete!(d, k) - @test !haskey(d, k) - @test haskey(d, j) + @test my_keys == + [MathOptInterface.VariableIndex(1), MathOptInterface.VariableIndex(2)] + @test my_values == ["first", "second"] + delete!(d, key) + @test d[CleverDicts.LinearIndex(1)] == "second" + key3 = CleverDicts.add_item(d, "third") + my_keys = MathOptInterface.VariableIndex[] + my_values = String[] + for (k, v) in d + push!(my_keys, k) + push!(my_values, v) end + @test my_keys == + [MathOptInterface.VariableIndex(2), MathOptInterface.VariableIndex(3)] + @test my_values == ["second", "third"] + return +end - @testset "haskey" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - @test isempty(d) == true - k = CleverDicts.add_item(d, "a") - @test isempty(d) == false - delete!(d, k) - @test isempty(d) == true - j = CleverDicts.add_item(d, "b") - @test isempty(d) == false +function test_iterate_iii() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + y = 0 + for (k, v) in d + y += 1 end + @test y == 0 + return +end - @testset "delete!" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - @test length(d) == 0 - @test delete!(d, MathOptInterface.VariableIndex(0)) == nothing - k1 = CleverDicts.add_item(d, "a") - k2 = CleverDicts.add_item(d, "b") - d[CleverDicts.LinearIndex(2)] == "b" - delete!(d, k1) - d[CleverDicts.LinearIndex(1)] == "b" - k3 = CleverDicts.add_item(d, "c") - @test d[k3] == "c" - @test d[CleverDicts.LinearIndex(1)] == "b" - @test d[CleverDicts.LinearIndex(2)] == "c" - end +function test_haskey() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + @test !haskey(d, 1) + k = CleverDicts.add_item(d, "a") + @test haskey(d, k) + j = CleverDicts.add_item(d, "b") + @test haskey(d, j) + delete!(d, k) + @test !haskey(d, k) + @test haskey(d, j) + return +end + +function test_isempty() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + @test isempty(d) == true + k = CleverDicts.add_item(d, "a") + @test isempty(d) == false + delete!(d, k) + @test isempty(d) == true + j = CleverDicts.add_item(d, "b") + @test isempty(d) == false + return +end - @testset "Dense Operations" begin - # inverse_hash :: Int64 -> Int - inverse_hash(x::Int64) = Int(x * 2) - # hash :: Int -> Int64 - hash(x::Int) = Int64(div(x, 2)) - d = CleverDicts.CleverDict{Int,Float64}(hash, inverse_hash, 3) +function test_delete!() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + @test length(d) == 0 + @test delete!(d, MathOptInterface.VariableIndex(0)) == d + k1 = CleverDicts.add_item(d, "a") + k2 = CleverDicts.add_item(d, "b") + d[CleverDicts.LinearIndex(2)] == "b" + delete!(d, k1) + d[CleverDicts.LinearIndex(1)] == "b" + k3 = CleverDicts.add_item(d, "c") + @test d[k3] == "c" + @test d[CleverDicts.LinearIndex(1)] == "b" + @test d[CleverDicts.LinearIndex(2)] == "c" + return +end - d[4] = 0.25 - @test !haskey(d, 2) - @test haskey(d, 4) - @test !haskey(d, 6) - @test length(d) == 1 - @test collect(d) == [4 => 0.25] - @test d[4] == 0.25 +function test_dense_operations() + # inverse_hash :: Int64 -> Int + inverse_hash(x::Int64) = Int(x * 2) + # hash :: Int -> Int64 + hash(x::Int) = Int64(div(x, 2)) + d = CleverDicts.CleverDict{Int,Float64}(hash, inverse_hash, 3) - d[2] = 1.5 - @test haskey(d, 2) - @test haskey(d, 4) - @test !haskey(d, 6) - @test length(d) == 2 - @test collect(d) == [2 => 1.5, 4 => 0.25] - @test d[2] == 1.5 - @test d[4] == 0.25 + d[4] = 0.25 + @test !haskey(d, 2) + @test haskey(d, 4) + @test !haskey(d, 6) + @test length(d) == 1 + @test collect(d) == [4 => 0.25] + @test d[4] == 0.25 - d[6] = 0.75 - @test haskey(d, 2) - @test haskey(d, 4) - @test haskey(d, 6) - @test length(d) == 3 - @test collect(d) == [2 => 1.5, 4 => 0.25, 6 => 0.75] - @test d[2] == 1.5 - @test d[4] == 0.25 - @test d[6] == 0.75 - end + d[2] = 1.5 + @test haskey(d, 2) + @test haskey(d, 4) + @test !haskey(d, 6) + @test length(d) == 2 + @test collect(d) == [2 => 1.5, 4 => 0.25] + @test d[2] == 1.5 + @test d[4] == 0.25 - @testset "Negative index" begin - d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() - d[MathOptInterface.VariableIndex(-3)] = "a" - @test d[MathOptInterface.VariableIndex(-3)] == "a" - @test_throws ErrorException CleverDicts.add_item(d, "b") - d[MathOptInterface.VariableIndex(0)] = "b" - @test d[MathOptInterface.VariableIndex(-3)] == "a" - @test d[MathOptInterface.VariableIndex(0)] == "b" - @test_throws ErrorException CleverDicts.add_item(d, "c") - end + d[6] = 0.75 + @test haskey(d, 2) + @test haskey(d, 4) + @test haskey(d, 6) + @test length(d) == 3 + @test collect(d) == [2 => 1.5, 4 => 0.25, 6 => 0.75] + @test d[2] == 1.5 + @test d[4] == 0.25 + @test d[6] == 0.75 + return +end - @testset "convert" begin - vals = [MathOptInterface.VariableIndex(-i) for i in 1:10] - d = Dict(MathOptInterface.VariableIndex(i) => vals[i] for i in 1:10) - T = CleverDicts.CleverDict{ - MOI.VariableIndex, - MOI.VariableIndex, - typeof(CleverDicts.key_to_index), - typeof(CleverDicts.index_to_key), - } - c = convert(T, d) - @test c isa T - @test c.is_dense - @test c.last_index == 10 - @test c.vector == vals - @test c === convert(T, c) - end +function test_negative_index() + d = CleverDicts.CleverDict{MathOptInterface.VariableIndex,String}() + d[MathOptInterface.VariableIndex(-3)] = "a" + @test d[MathOptInterface.VariableIndex(-3)] == "a" + @test_throws ErrorException CleverDicts.add_item(d, "b") + d[MathOptInterface.VariableIndex(0)] = "b" + @test d[MathOptInterface.VariableIndex(-3)] == "a" + @test d[MathOptInterface.VariableIndex(0)] == "b" + @test_throws ErrorException CleverDicts.add_item(d, "c") + return +end + +function test_convert() + vals = [MathOptInterface.VariableIndex(-i) for i in 1:10] + d = Dict(MathOptInterface.VariableIndex(i) => vals[i] for i in 1:10) + T = CleverDicts.CleverDict{ + MOI.VariableIndex, + MOI.VariableIndex, + typeof(CleverDicts.key_to_index), + typeof(CleverDicts.index_to_key), + } + c = convert(T, d) + @test c isa T + @test c.is_dense + @test c.last_index == 10 + @test c.vector == vals + @test c === convert(T, c) + return end + +end # module + +TestCleverDicts.runtests()