using SmallCollections: default, bitsize, getindex0

using Base.FastMath: eq_fast
using LinearAlgebra: LinearAlgebra, dot

const VS = (SmallVector, MutableSmallVector)

@testset "SmallVector" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        u = rand(T, m)
        v = @inferred V{N,T}(u)
        if ismutabletype(V)
            @test v == @inferred(copy(v)) !== v
        else
            @test v === @inferred copy(v)
        end
        @test_inferred capacity(v) N Int
        @test_inferred fixedvector(v) [i <= length(v) ? v[i] : default(T) for i in 1:N] FixedVector{N,T}
        @test_inferred fixedvector(v, Val(N)) fixedvector(v)
        @test_inferred fixedvector(v, Val(N+3)) [i <= length(v) ? v[i] : default(T) for i in 1:N+3] FixedVector{N+3,T}
        @test_inferred v == u true
        @test isvalid(v)
        @test_inferred collect(v) u Vector{T}
        v2 = V{N,T}(u)
        @test_inferred v == v2 true
        v3 = V{2*N,T}(u)
        @test_inferred v == v3 true
        if T <: Number
            @test_inferred eq_fast(v, v2) true
            @test_inferred eq_fast(v, v3) true
            numerictype(T) <: Integer && bitsize(T) >= bitsize(Float64) && continue
            v4 = V{N,setnumerictype(T, Float64)}(u)
            @test_inferred v == v4 true
            @test_inferred eq_fast(v, v4) true
        end
        if !isempty(u)
            i = rand(1:m)
            x = rand(T)
            while x == v[i]
                x = rand(T)
            end
            v5 = setindex(v, x, i)
            @test_inferred v == v5 false
            if T <: Number
                @test_inferred eq_fast(v, v5) false
            end
        end
        v6 = V{N+2,T}(push!(copy(u), rand(T)))
        @test_inferred v == v6 false
        if T <: Number
            @test_inferred eq_fast(v, v6) false
        end
        if !isempty(u)
            u7 = copy(u)
            pop!(u7)
            v7 = V{N+2,T}(u7)
            @test_inferred v == v7 false
            if T <: Number
                @test_inferred eq_fast(v, v7) false
            end
        end
        @test_inferred hash(v) hash(u) UInt
        v8 = V{2*N,T}(u)
        @test_inferred fasthash(v) fasthash(v8) UInt
        @test_inferred length(v) length(u) Int
        @test_inferred V{N,T}() V{N,T}(())
        @test_inferred empty(v) V{N,T}()
        @test_inferred empty(v, Char) V{N,Char}()
    end

    for V in VS, W in (FixedVector, MutableFixedVector), N in (9, 16), T in test_types
        w = rand(W{N,T})
        @test_inferred V(w) w V{N,T}
    end
end

@testset "SmallVector indices" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        u = rand(T, m)
        v = V{N,T}(u)
        if isempty(u)
            @test_throws Exception first(v)
            @test_throws Exception last(v)
        else
            @test_inferred first(v) first(u) T
            @test_inferred last(v) last(u) T
        end
        x = rand(T)
        for i in -1:length(u)+1
            if 1 <= i <= length(u)
                @test_inferred v[i] u[i] T
                w = @test_inferred setindex(v, x, i) setindex!(copy(u), x, i) SmallVector(v)
                @test isvalid(w)
                if T <: Number
                    w = @test_inferred addindex(v, x, i) setindex!(copy(u), u[i]+x, i) SmallVector(v)
                    @test isvalid(w)
                end
                if ismutabletype(V) && isbitstype(T)
                    w = @test_inferred setindex!(copy(v), x, i) setindex!(copy(u), x, i) v
                    @test isvalid(w)
                end
            else
                @test_throws Exception v[i]
                @test_throws Exception setindex(v, x, i)
                T <: Number && @test_throws Exception addindex(v, x, i)
                if ismutabletype(V) && isbitstype(T)
                    @test_throws Exception setindex!(v, x, i)
                end
            end
        end
        for i in 0:m, j in i-1:m+1
            if checkbounds(Bool, u, i:j)
                w = @test_inferred v[i:j] u[i:j] SmallVector(v)
                @test isvalid(w)
            else
                @test_throws Exception v[i:j]
            end
        end
    end
end

@testset "SmallVector bool inds" begin
    for N in (1, 2, 9, 16), T in (Bool, Int16), V in (SmallVector, MutableSmallVector), m in Set((0, 1, N÷2, N))
        u = rand(T, m)
        v = V{N,T}(u)

        if m > 0
            jj = rand(Bool, m-1)
            iis = Any[SmallVector{N+2}(jj), PackedVector{UInt32,1,Bool}(jj)]
            m > 1 && pushfirst!(iis, FixedVector{m-1}(jj))
            for ii in iis
                @test_throws Exception v[ii]
            end
        end

        jj = rand(Bool, m)
        iis = Any[SmallVector{N+2}(jj), PackedVector{UInt32,1,Bool}(jj)]
        m > 0 && pushfirst!(iis, FixedVector{m}(jj))
        for ii in iis
            @test_inferred v[ii] u[jj] SmallVector{N,T}
        end
    end
end

@testset "SmallVector vec inds" begin
    for N in (1, 2, 8, 11, 32, 72, 128), T in (Int8, Char), V in (SmallVector, MutableSmallVector), m in Set((0, 1, N÷2, N))
        N*sizeof(T) <= 256 || continue
        u = rand(T, m)
        v = V{N,T}(u)

        ii = 1:max(0, m-2)
        @test_inferred v[ii] u[ii] SmallVector{N,T}
        ii = collect(ii)
        @test_inferred v[ii] u[ii] MutableSmallVector{N,T}  # type chosen in Base via `similar`

        m > 0 || continue

        M = 7
        for jj in [rand(UInt8(1):UInt8(m), M), Int16[]]
            ii = SmallVector{M+1}(jj)
            @test_inferred v[ii] u[ii] SmallVector{M+1,T}
            ii0 = SmallVector{M}(jj .- UInt8(1))
            @test_inferred getindex0(v, ii0) u[ii] SmallVector{M,T}
            ii = PackedVector{UInt128,12,Int16}(jj)
            @test_inferred v[ii] u[ii] u
            ii = 2:min(m, 5)
            @test_inferred v[ii] u[ii] SmallVector{N,T}
            ii = 1:2:min(m, 7)
            @test_inferred v[ii] u[ii] SmallVector{N,T}
            isempty(jj) && continue
            ii = FixedVector{M}(jj)
            @test_inferred v[ii] u[ii] FixedVector{M,T}
            ii0 = FixedVector{M}(jj .- UInt8(1))
            @test_inferred getindex0(v, ii0) u[ii] FixedVector{M,T}
        end
    end
end

@testset "SmallVector zeros" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        T <: Number || continue
        u = zeros(T, m)
        v = V{N,T}(u)
        @test_inferred iszero(v) true
        w = @test_inferred zero(v) u v
        @test isvalid(w)
        w = @test_inferred zeros(V{N,T}, m) v
        @test isvalid(w)
        w = @test_inferred ones(V{N,T}, m) ones(T, m) V{N,T}
        @test isvalid(w)
    end
end

@testset "SmallVector reverse" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        u = rand(T, m)
        v = V{N,T}(u)
        @test_inferred reverse(v) reverse(u) SmallVector(v)
        for i in 0:m+2
            if 1 <= i <= m+1
                @test_inferred reverse(v, i) reverse(u, i) SmallVector(v)
            else
                @test_throws Exception reverse(v, i)
            end
            for j in i-2:m+1
                if 1 <= i <= m+1 && i-1 <= j <= m
                    @test_inferred reverse(v, i, j) reverse(u, i, j) SmallVector(v)
                else
                    @test_throws Exception reverse(v, i, j)
                end
            end
        end
        (V <: MutableFixedVector && isbitstype(T)) || continue
        w = @test_inferred reverse!(v) reverse(u) v
        @test w === v
    end
end

@testset "SmallVector push/pop" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        u = rand(T, m)
        v = V{N,T}(u)
        x = rand(T)
        y = rand(T)
        @test_inferred push(v) SmallVector(v)
        @test_inferred pushfirst(v) SmallVector(v)
        if length(u) == N
            @test_throws Exception push(v, x)
            @test_throws Exception push(v, x, y)
            @test_throws Exception pushfirst(v, x)
            @test_throws Exception pushfirst(v, x, y)
        else
            w = @test_inferred push(v, x) push!(copy(u), x) SmallVector(v)
            @test isvalid(w)
            w = @test_inferred pushfirst(v, x) pushfirst!(copy(u), x) SmallVector(v)
            @test isvalid(w)
            if length(u) <= N-2
                w = @test_inferred push(v, x, y) push(push(v, x), y)
                @test isvalid(w)
                w = @test_inferred pushfirst(v, x, y) pushfirst(pushfirst(v, y), x)
                @test isvalid(w)
            end
        end
        if isempty(u)
            @test_throws Exception pop(v)
            @test_throws Exception popfirst(v)
        else
            w, _ = @test_inferred pop(v) (deleteat!(copy(u), length(u)), last(u)) (SmallVector(v), last(v))
            @test isvalid(w)
            w, _ = @test_inferred popfirst(v) (deleteat!(copy(u), 1), first(u)) (SmallVector(v), first(v))
            @test isvalid(w)
        end
        for i in (-1, 0, 1, 2, length(u), length(u)+1)
            if 1 <= i <= length(u)+1 && length(u) < N
                w = @test_inferred insert(v, i, x) insert!(copy(u), i, x) SmallVector(v)
                @test isvalid(w)
                if i <= length(u)
                    w = @test_inferred duplicate(v, i) insert(v, i, v[i])
                    @test isvalid(w)
                else
                    @test_throws Exception duplicate(v, i)
                end
            else
                @test_throws Exception insert(v, i, x)
                @test_throws Exception duplicate(v, i)
            end
            if 1 <= i <= length(u)
                w = @test_inferred deleteat(v, i) deleteat!(copy(u), i) SmallVector(v)
                @test isvalid(w)
            else
                @test_throws Exception deleteat(v, i)
            end
        end
        @test_inferred append(v) SmallVector(v)
        @test_inferred prepend(v) SmallVector(v)
        xy = [x, y]
        if length(u) <= N-2
            w = @test_inferred append(v, SmallVector{4}(xy)) push(v, x, y)
            @test isvalid(w)
            w = @test_inferred append(v, xy[i] for i in 1:2) push(v, x, y)
            @test isvalid(w)
            w = @test_inferred append(v, (x,), [y]) push(v, x, y)
            @test isvalid(w)
            w = @test_inferred prepend(v, SmallVector{4}(xy)) pushfirst(v, x, y)
            @test isvalid(w)
            w = @test_inferred prepend(v, xy[i] for i in 1:2) pushfirst(v, x, y)
            @test isvalid(w)
            w = @test_inferred prepend(v, (x,), [y]) pushfirst(v, x, y)
            @test isvalid(w)
        else
            @test_throws Exception append(v, SmallVector{4}(xy))
            @test_throws Exception append(v, xy[i] for i in 1:2)
            @test_throws Exception append(v, (x,), [y])
            @test_throws Exception prepend(v, SmallVector{4}(xy))
            @test_throws Exception prepend(v, xy[i] for i in 1:2)
            @test_throws Exception prepend(v, (x,), [y])
        end
        if numerictype(T) <: Integer
            @test_inferred filter(isodd, v) filter(isodd, u) SmallVector(v)
        end
    end
end

@testset "SmallVector push!/pop!" begin
    for V in (MutableSmallVector,), N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        isbitstype(T) || continue
        u = rand(T, m)
        v = V{N,T}(u)
        x = rand(T)
        y = rand(T)
        @test_inferred push!(copy(v)) v
        @test_inferred pushfirst!(copy(v)) v
        if length(u) == N
            @test_throws Exception push!(copy(v), x)
            @test_throws Exception push!(copy(v), x, y)
            @test_throws Exception pushfirst!(copy(v), x)
            @test_throws Exception pushfirst!(copy(v), x, y)
        else
            w = @test_inferred push!(copy(v), x) push!(copy(u), x) v
            @test isvalid(w)
            w = @test_inferred pushfirst!(copy(v), x) pushfirst!(copy(u), x) v
            @test isvalid(w)
            if length(u) <= N-2
                w = @test_inferred push!(copy(v), x, y) push!(push!(copy(v), x), y)
                @test isvalid(w)
                w = @test_inferred pushfirst!(copy(v), x, y) pushfirst!(pushfirst!(copy(v), y), x)
                @test isvalid(w)
            end
        end
        if isempty(u)
            @test_throws Exception pop!(copy(v))
            @test_throws Exception popfirst!(copy(v))
        else
            @test_inferred pop!(copy(v)) pop!(copy(u)) T
            @test_inferred popfirst!(copy(v)) popfirst!(copy(u)) T
        end
        for i in (-1, 0, 1, 2, length(u), length(u)+1)
            if 1 <= i <= length(u)+1 && length(u) < N
                w = @test_inferred insert!(copy(v), i, x) insert!(copy(u), i, x) v
                @test isvalid(w)
                if i <= length(u)
                    w = @test_inferred duplicate!(copy(v), i) insert!(copy(v), i, v[i])
                    @test isvalid(w)
                else
                    @test_throws Exception duplicate!(copy(v), i)
                end
            else
                @test_throws Exception insert!(copy(v), i, x)
                @test_throws Exception duplicate!(copy(v), i)
            end
            if 1 <= i <= length(u)
                w = @test_inferred deleteat!(copy(v), i) deleteat!(copy(u), i) v
                @test isvalid(w)
            else
                @test_throws Exception deleteat!(copy(v), i)
            end
        end
        @test_inferred append!(v) v
        @test_inferred prepend!(v) v
        xy = [x, y]
        if length(u) <= N-2
            w = @test_inferred append!(copy(v), SmallVector{4}(xy)) push!(copy(v), x, y)
            @test isvalid(w)
            w = @test_inferred append!(copy(v), xy[i] for i in 1:2) push!(copy(v), x, y)
            @test isvalid(w)
            w = @test_inferred append!(copy(v), (x,), [y]) push!(copy(v), x, y)
            @test isvalid(w)
            w = @test_inferred prepend!(copy(v), SmallVector{4}(xy)) pushfirst!(copy(v), x, y)
            @test isvalid(w)
            w = @test_inferred prepend!(copy(v), xy[i] for i in 1:2) pushfirst!(copy(v), x, y)
            @test isvalid(w)
            w = @test_inferred prepend!(copy(v), (x,), [y]) pushfirst!(copy(v), x, y)
            @test isvalid(w)
        else
            @test_throws Exception append!(copy(v), SmallVector{4}(xy))
            @test_throws Exception append!(copy(v), xy[i] for i in 1:2)
            @test_throws Exception append!(copy(v), (x,), [y])
            @test_throws Exception prepend!(copy(v), SmallVector{4}(xy))
            @test_throws Exception prepend!(copy(v), xy[i] for i in 1:2)
            @test_throws Exception prepend!(copy(v), (x,), [y])
        end
        if numerictype(T) <: Integer
            @test_inferred filter!(isodd, copy(v)) filter!(isodd, copy(u)) v
        end
    end
end

@testset "SmallVector cmp" begin
    base_isless(v, w) = invoke(isless, Tuple{AbstractVector{UInt8},AbstractVector{UInt8}}, v, w)
    base_le(v::AbstractVector{T}, w::AbstractVector{T}) where T = invoke(<=, Tuple{AbstractVector{T},AbstractVector{T}}, v, w)

    for N in [8, 16], _ in 1:250
        v = SmallVector{N,UInt8}(rand((0, 255), rand(0:N)))
        w = MutableSmallVector{N,UInt8}(rand((0, 255), rand(0:N)))
        @test_inferred isless(v, w) base_isless(v, w)
        @test_inferred isless(v, v) base_isless(v, v)
    end

    for T in [UInt8, Int32, Char], N in [8, 13]
        v = rand(SmallVector{N,T})
        w = rand(SmallVector{N+2,T})
        @test_inferred v <= w base_le(v, w)
        @test_inferred v <= v base_le(v, v)
    end
end

@testset "SmallVector circshift" begin
    for V in VS, N in [1, 2, 9, 16, 128], T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        N == 128 && !(T in (Int8, UInt8, Bool)) && continue
        u = rand(T, m)
        v = V{N,T}(u)
        for k in (-2*N, -2*N+1, -3, -1, 0, 1, 7, N+5, 2*N+7)
            @test_inferred circshift(v, k) circshift(u, k) SmallVector(v)
            @test isvalid(circshift(v, k))
            V <: MutableSmallVector || continue
            v2 = copy(v)
            v3 = @test_inferred circshift!(v2, k) circshift(u, k) v2
            @test v3 === v2
        end
    end
end

@testset "SmallVector shuffle 256" begin
    T = Int8
    U = UInt8
    for N in [128, 127, 256, 255], n in N-2:N
        v = SmallVector{N}(rand(T, n))
        vv = collect(v)
        for k in [-n, -n+1, -n+2, -2, -1, 0, 1, 2, n-2, n-1, n]
            @test circshift(v, k) == circshift(vv, k)
            @test circshift!(MutableSmallVector(v), k) == circshift(vv, k)
        end
        for k in [1, 2, 3, n-1, n, n+1]
            @test reverse(v, k) == reverse(vv, k)
            @test reverse!(MutableSmallVector(v), k) == reverse(vv, k)
        end
        @test first(popfirst(v)) == deleteat!(copy(vv), 1)
        @test (w = MutableSmallVector(v); popfirst!(w); w) == deleteat!(copy(vv), 1)
        for k in [1, 2, 3, n-2, n-1, n]
            n < N || continue
            @test duplicate(v, k) == [vv[1:k]; vv[k:n]]
            @test first(popat(v, k)) == deleteat!(copy(vv), k)
            @test duplicate!(MutableSmallVector(v), k) == [vv[1:k]; vv[k:n]]
            @test deleteat!(MutableSmallVector(v), k) == deleteat!(copy(vv), k)
        end
        for k in [1, 2, 3, n-2, n-1, n], l in [k-1, k, n-1, n]
            @test v[k:l] == vv[k:l]
        end
        n < typemax(U) || continue
        for M in [128, 127, 256, 255], m in [0, 1, 2, M-2, M-1, M]
            ii = SmallVector{M,U}(rand(0:n-1, m))
            @test v[ii .+ U(1)] == vv[ii .+ 1]
        end
    end
end

using Base.FastMath: mul_fast

@testset "SmallVector add/mul" begin
    for V in VS, N in (1, 2, 9, 16), T1 in test_types, m in (1, round(Int, 0.7*N), N-1, N)
        T1 <: Number || continue
        if T1 <: Unsigned
            u1 = rand(unitrange(T1(1), T1(9)), m)
        else
            u1 = rand(unitrange(T1(-9), T1(9)), m)
        end
        v1 = V{N}(u1)
        w = @test_inferred +v1 u1 SmallVector(v1)
        @test isvalid(w)
        w = @test_inferred -v1 -u1 SmallVector(v1)
        @test isvalid(w)
        for T2 in test_types
            T2 <: Number || continue
            if numerictype(T2) <: Unsigned
                u2 = rand(unitrange(T2(1), T2(9)), m)
                c = rand(unitrange(T2(1), T2(9)))
            else
                u2 = rand(unitrange(T2(-9), T2(9)), m)
                c = rand(unitrange(T2(-9), T2(9)))
            end
            v2 = V{N}(u2)
            T1T2 = eltype(c*u1)
            w = @test_inferred c*v1 c*u1 SmallVector{N,T1T2}
            @test isvalid(w)
            if numerictype(T1T2) <: Unsigned && (minimum(v1; init = zero(T1)) < zero(T1) ||
                    (c < zero(c) && !(isempty(v1) && SmallCollections.MapStyle(mul_fast, T2, T1) isa SmallCollections.LazyStyle)))
                # Julia bug, see julia#58188
                @test_broken mul_fast(c, v1) == mul_fast.(c, u1)
            else
                w = @inferred mul_fast(c, v1)
                @test isapprox(w, mul_fast.(c, u1)) && w isa SmallVector{N,T1T2}
                @test isvalid(w)
            end
            @test_inferred v1*c c*v1
            dimension(T1) == dimension(T2) || continue
            T = promote_type(T1, T2)
            for op in (+, -)
                w = @test_inferred op(v1, v2) op(u1, u2) SmallVector{N,T}
                @test isvalid(w)
                v3 = V{N+4}(u2)
                w = @test_inferred op(v1, v3) op(u1, u2) SmallVector{N,T}
                @test isvalid(w)
                w = @test_inferred op(v3, v1) op(u2, u1) SmallVector{N,T}
                @test isvalid(w)
            end
        end
    end

    N = 8
    T = Float64
    u = T[-Inf, -1, 0, 1, Inf, NaN]
    for V in VS
        v = V{8,Float64}(u)
        for c in (Inf, -Inf, NaN)
            w = @test_inferred c*v c*u SmallVector{N,T}
            @test isvalid(w)
        end
    end
end

@testset "SmallVector sum/max" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        T <: Number || continue
        r = T <: Unsigned ? unitrange(T(1), T(9)) : unitrange(T(-9), T(9))
        u = rand(r, m)
        v = V{N}(u)
        for f in (maximum, minimum)
            if isempty(u)
                @test_throws Exception f(v)
                @test_inferred f(v; init = zero(T)) f(u; init = zero(T))
            else
                @test_inferred f(v) f(u)
            end
        end
        if isempty(u)
            @test_throws Exception extrema(v)
            @test_inferred extrema(v; init = (oneunit(T), zero(T))) extrema(u; init = (oneunit(T), zero(T)))
        else
            @test_inferred extrema(v) extrema(u)
        end
        @test_inferred sum(v) sum(u)
        s = @inferred sum_fast(v)
        @test s ≈ sum(u)
        if T <: SmallCollections.AbstractBitInteger
            @test_inferred sum_fast(v; init = Int32(0)) sum(v) promote_type(T, Int32)
        end
        if T <: Quantity
            @test prod(v) == prod(u)
        else
            @test_inferred prod(v) prod(u)
        end

        u2 = rand(r, m) * im
        v2 = V{N}(u2)
        du = dot(u2, u)
        if (VERSION < v"1.11" || pkgversion(LinearAlgebra) < v"1.12") &&
                m == 0 && (numerictype(T) <: Union{AbstractFloat, Complex{<:AbstractFloat}})
            # dot for empty vectors is not always the zero element (w.r.t. isequal)
            # see LinearAlgebra.jl#1483
            d = @inferred dot(v2, v)
            @test d ≈ du && typeof(d) == typeof(du)
            @test_broken isequal(dot(v2, v), du)
        else
            @test_inferred dot(v2, v) du
        end
        d = @inferred dot_fast(v2, v)
        @test d ≈ du && typeof(d) == typeof(du)

        T <: AbstractFloat || continue
        u = fill(-T(0), m)
        v = V{N}(u)
        @test_inferred sum(v) sum(u)
        @test_inferred prod(-v) prod(-u)
    end
    for V in VS, N in (5, 16), T in (Float32, Float64), x in (Inf, -Inf, NaN)
        u = T[x, -1, 0, 1]
        v = V{N}(u)
        @test_inferred maximum(v) maximum(u)
        @test_inferred minimum(v) minimum(u)
        @test_inferred sum(v) sum(u)
        if isnan(prod(u))
            @test_inferred isnan(prod(v)) true
        else
            @test_inferred prod(v) prod(u)
        end
    end
    for V in VS, N in (5, 16), T in (Float32, Float64)
        u = T[NaN, -1, 0, 1]
        v = V{N}(u)
        @test_inferred isnan(maximum(v)) true
        @test_inferred isnan(minimum(v)) true
        @test_inferred isnan(sum(v)) true
        @test_inferred isnan(prod(v)) true
    end
end

@testset "SmallVector overflow" begin
    for N in (1, 8, 13, 16, 25, 32), T in (Int8, UInt16, Int32), m in [0, 1, N-1, N]
        S = smallbitsettype(Val(N))
        vv = rand(T, m)
        if m > 0
            vv[1] = rand((typemin(T), zero(T)))
        end
        v = MutableSmallVector{N}(vv)
        w = SmallVector{N}(rand(T, m))
        ww = SmallVector{N+2}(w)

        u = -v
        uu = -collect(Int, v)
        s = S(i for i in 1:m if u[i] != uu[i])
        if isempty(s)
            @test_inferred Base.Checked.checked_neg(v) u
        else
            @test_throws OverflowError Base.Checked.checked_neg(v)
        end

        u = v+w
        uu = collect(Int, v) + collect(Int, w)
        s = S(i for i in 1:m if u[i] != uu[i])
        @test_inferred Base.Checked.add_with_overflow(v, w) (u, s)
        if isempty(s)
            @test_inferred Base.Checked.checked_add(v, ww) u
        else
            @test_throws OverflowError Base.Checked.checked_add(v, ww)
        end
        if m < N
            www = push(w, zero(T))
            @test_throws DimensionMismatch Base.Checked.add_with_overflow(v, www)
        end
        www = push(ww, zero(T))
        @test_throws DimensionMismatch Base.Checked.checked_add(v, www)

        u = v+w+v
        uu = collect(Int, v) + collect(Int, w) + collect(Int, v)
        s = S(i for i in 1:m if u[i] != uu[i])
        if isempty(s)
            @test_inferred Base.Checked.checked_add(v, ww, v) u
        else
            @test_throws OverflowError Base.Checked.checked_add(v, ww, v)
        end

        u = v-w
        uu = collect(Int, v) - collect(Int, w)
        s = S(i for i in 1:m if u[i] != uu[i])
        @test_inferred Base.Checked.sub_with_overflow(v, w) (u, s)
        if isempty(s)
            @test_inferred Base.Checked.checked_sub(v, ww) u
        else
            @test_throws OverflowError Base.Checked.checked_sub(v, ww)
        end
        if m < N
            www = push(w, zero(T))
            @test_throws DimensionMismatch Base.Checked.sub_with_overflow(v, www)
        end
        www = push(ww, zero(T))
        @test_throws DimensionMismatch Base.Checked.checked_sub(v, www)

        c = T(2)
        u = c*v
        uu = Int(c) * v
        s = S(i for i in 1:m if u[i] != uu[i])
        @test_inferred Base.Checked.mul_with_overflow(c, v) (u, s)
        @test_inferred Base.Checked.mul_with_overflow(v, c) (u, s)
        if isempty(s)
            @test_inferred Base.Checked.checked_mul(c, v) u
            @test_inferred Base.Checked.checked_mul(v, c) u
        else
            @test_throws OverflowError Base.Checked.checked_mul(c, v)
            @test_throws OverflowError Base.Checked.checked_mul(v, c)
        end
    end
end

@testset "SmallVector map" begin
    f(x) = Int32(2)*x
    f(x, y) = 2*x + y
    g(x) = iszero(x) ? 1 : 1.0
    for V in VS, N in (1, 2, 9, 16), T1 in test_types, m in (0, 1, round(Int, 0.7*N), N-1)
        T1 <: Number || continue
        u1 = rand(T1, m)
        v1 = V{N}(u1)
        u3 = map(f, u1)
        w = @test_inferred map(f, v1) u3 SmallVector{N,eltype(u3)}
        @test isvalid(w)
        for T2 in test_types
            (T2 <: Number && dimension(T1) == dimension(T2)) || continue
            u2 = rand(T2, m+1)
            v2 = V{N+2}(u2)
            u4 = map(f, u1, u2)
            w = @test_inferred map(f, v1, v2) u4 SmallVector{N,eltype(u4)}
            @test isvalid(w)
        end
    end
    for V in VS,  m in (0, 1, 3)
        v = V{8}(1:m)
        u = collect(v)
        u5 = map(g, u)
        w = map(g, v)
        @test w == u5
        @test eltype(w) == eltype(u5)
    end
end

@testset "SmallVector find" begin
    for N in (1, 2, 9, 16), T in [Bool, test_types...], V in (SmallVector, MutableSmallVector), m in (0, 1, N-1, N)
        T <: Number || continue
        k = max(m ÷ 2, 1)
        for u in (rand(T(0):oneunit(T):(T == Bool ? true : T(2)), m), zeros(T, m), ones(T, m))
            v = V{N,T}(u)
            for style in (LazyStyle(), EagerStyle(), RigidStyle(), StrictStyle())
                @test_inferred support(!iszero, v; style) Set(findall(!iszero, u)) SmallBitSet
                i = @inferred Nothing findfirst(!iszero, v; style)
                @test i == findfirst(!iszero, u)
                i = @inferred Nothing findlast(!iszero, v; style)
                @test i == findlast(!iszero, u)
                if !isempty(v)
                    i = @inferred Nothing findnext(!iszero, v, k; style)
                    @test i == findnext(!iszero, u, k)
                    i = @inferred Nothing findprev(!iszero, v, k; style)
                    @test i == findprev(!iszero, u, k)
                end
            end
            if !isempty(v)
                @test_inferred findmin(v) findmin(u) Tuple{T, Int}
                @test_inferred findmax(v) findmax(u) Tuple{T, Int}
                if T != Bool
                    @test_inferred findmin(-, v) findmin(-, u) Tuple{T, Int}
                    @test_inferred findmax(-, v) findmax(-, u) Tuple{T, Int}
                end
            end
            @test_inferred allequal(v) allequal(u) Bool
            VERSION >= v"1.11" && @test_inferred allequal(isodd, v) allequal(isodd, u) Bool
            @test_inferred allunique(v) allunique(u) Bool
            VERSION >= v"1.11" && @test_inferred allunique(-, v) allunique(-, u) Bool
            @test_inferred any(isodd, v) any(isodd, u) Bool
            @test_inferred all(isodd, v) all(isodd, u) Bool
            @test_inferred findall(!iszero, v) findall(!iszero, u) SmallVector{N,SmallLength}
            if VERSION > v"1.13-" && SmallCollections.MapStyle(!iszero, T) == SmallCollections.LazyStyle()
                # work around type instability for `count`, see julia#59869
                try
                    @inferred count(!iszero, v; init = Int32(0))
                    @test_broken true
                catch
                    @test_broken false
                end
            else
                @test_inferred count(!iszero, v; init = Int32(0)) count(!iszero, u; init = Int32(0)) Int32
            end
            if T == Bool
                @test_inferred support(v) Set(findall(u)) SmallBitSet
                @test_inferred findall(v) findall(u) SmallVector{N,SmallLength}
                @test_inferred any(v) any(u) Bool
                @test_inferred all(v) all(u) Bool
                @test_inferred count(v) count(u) Int
            end
        end
    end
end

@testset "SmallVector issorted" begin
    for T in [Int8, Float32, BigInt, Char], u in [Int[], [1], [1,2,3], [1,2,2], [1,2,1], [3,2,1], [3,2,2], [1,1,1,1]],
            N in max(length(u), 1):length(u)+2
        v = SmallVector{N,T}(u)
        w = collect(T, u)
        bys = T == Char ? (identity, uppercase) : (identity, -, x -> 2.0*x)
        for lt in (isless, >), by in bys, rev in (false, true), strict in (false, true)
            if strict
                @test_inferred issorted(v; lt, by, rev, strict) issorted(w; lt, by, rev) & allunique(w)
            else
                @test_inferred issorted(v; lt, by, rev, strict) issorted(w; lt, by, rev)
            end
        end
    end
end

@testset "SmallVector rest" begin
    for V in VS
        v = V{8}(1:2)
        W = typeof(SmallVector(v))
        w..., = v
        @test w == v && w isa W && isvalid(w)
        x1, w... = v
        @test w == v[2:end] && w isa W && isvalid(w)
        x1, x2, w... = v
        @test w == v[3:end] && w isa W && isvalid(w)
        @test_throws Exception x1, x2, x3, w... = v

        if VERSION >= v"1.9"
            v = V{8,UInt8}(1:3)
            W = typeof(SmallVector(v))
            w..., y1 = v
            @test w == v[1:end-1] && w isa W && isvalid(w) && y1 === v[end]
            x1, w..., y1 = v
            @test w == v[2:end-1] && w isa W && isvalid(w) && y1 === v[end]
            x1, x2, w..., y1 = v
            @test w == v[3:end-1] && w isa W && isvalid(w) && y1 === v[end]
            @test_throws Exception x1, x2, x3, w..., y1 = v

            v = SmallVector{8,Int16}(1:4)
            W = typeof(SmallVector(v))
            w..., y1, y2 = v
            @test w == v[1:end-2] && w isa W && isvalid(w) && y1 === v[end-1] && y2 === v[end]
            x1, w..., y1, y2 = v
            @test w == v[2:end-2] && w isa W && isvalid(w) && y1 === v[end-1] && y2 === v[end]
            x1, x2, w..., y1, y2 = v
            @test w == v[3:end-2] && w isa W && isvalid(w) && y1 === v[end-1] && y2 === v[end]
            @test_throws Exception x1, x2, x3, w..., y1, y2 = v
        end
    end
end

@testset "SmallVector support" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        T <: Number || continue
        u = rand(unitrange(T(0), T(2)), m)
        v = V{N,T}(u)
        @test_inferred support(v) Set{Int}(i for i in 1:m if !iszero(u[i])) SmallBitSet
    end
end

@testset "SmallVector rand" begin
    for V in VS, N in (1, 2, 9, 16), T in test_types, m in (0, 1, round(Int, 0.7*N), N-1, N)
        v = @inferred rand(V{N,T})
        @test v isa V{N,T} && isvalid(v)
    end
end

@testset "broadcast" begin
    N = 8
    for V in VS, T in (Int, Float64), m in (0, 1, 3, 8)
        u = collect(T, 1:m)
        v = V{N}(u)
        c = T(2)
        uu = m < N ? push!(copy(u), c) : copy(u)
        vv = SmallVector{N}(uu)
        w = @test_inferred v .+ v u .+ u SmallVector{N,T}
        @test isvalid(w)
        w = @test_inferred v .- v u .- u SmallVector{N,T}
        @test isvalid(w)
        w = @test_inferred v .* v u .* u SmallVector{N,T}
        @test isvalid(w)
        w = @test_inferred (c .* v) (c .* u) SmallVector{N,T}
        @test isvalid(w)

        w = abs.(-v)
        @test w == v && w isa SmallVector{N,T} && isvalid(w)

        f(x, y) = x + 2*y
        # @test_inferred map(f, v, v) map(f, u, u) SmallVector{N,T}
        # @test_inferred map(f, vv, v) map(f, uu, u) SmallVector{N,T}
        w = f.(v, v)
        @test w == f.(u, u) && w isa SmallVector{N,T}
        w = f.(v, c)
        @test w == f.(u, c) && w isa SmallVector{N,T}

        w = @test_inferred (v .^ 2) (u .^ 2) SmallVector{N,T}
        @test isvalid(w)
        @test v .^ -2 == u .^ -2  # TODO: @test_inferred doesn't work
        @test isvalid(v .^ -2)

        w = @test_inferred (v .* Ref(v)) (u .* Ref(u)) SmallVector{N,SmallVector{N,T}}
        @test isvalid(w)
    end

    for T in (Int16, Float32)
        if T <: Integer
            u1 = T[2, -1, 4, -3, 7]
            u2 = T[-3, 9, 4, -1, -5, 6]
        else
            u1 = 10 .* rand(T, 5) .- 5
            u2 = 10 .* rand(T, 6) .- 5
        end
        v1 = SmallVector{8,T}(u1)
        v2 = SmallVector{8,T}(u2)
        for f in (+, -, *, /, ==, !=, <, >, <=, >=, ===, isequal)
            ww = map(f, u1, u2)
            w = @test_inferred map(f, v1, v2) ww SmallVector{8,eltype(ww)}
            @test isvalid(w)
        end
        for f in (round, floor, ceil, trunc, abs, abs2, sign, sqrt, signbit)
            if f === sqrt
                uu = map(abs, u1)
                vv = map(abs, v1)
                ww = map(f, uu)
                w = @test_inferred map(f, vv) ww SmallVector{8,eltype(ww)}
                @test isvalid(w)
            else
                ww = map(f, u1)
                w = @test_inferred map(f, v1) ww SmallVector{8,eltype(ww)}
                @test isvalid(w)
            end
        end
        T <: Integer || continue
        for f in (&, |, xor, nand, nor)
            w = @test_inferred map(f, v1, v2) map(f, u1, u2) SmallVector{8,T}
            @test isvalid(w)
        end
        for f in (~,)
            w = @test_inferred map(f, v1) map(f, u1) SmallVector{8,T}
            @test isvalid(w)
        end
    end

    for V in VS
        u = [7, 8]
        v = V{3}(u)
        a = [1 2; 3 4]
        @test a .+ v == a .+ u

        u = ['a', 'b', 'c']
        v = V{5}(u)
        w = v .* 'x'
        @test w == u .* 'x' && w isa SmallVector{5,String}
    end
end
