function value_iteration!(mdp::AbstractMDP{Int, Int}, γ::Float64, horizon::Int; ϵ::Float64=0.01, q::Matrix{Float64}, v::Vector{Float64}, T::Array{Float64, 3}, R::Array{Float64, 3})::Nothing
    nstates::Int = length(state_space(mdp))
    nactions::Int = length(action_space(mdp))

    @assert size(q) == (nactions, nstates)
    @assert length(v) == nstates
    @assert size(T) == (nstates, nactions, nstates)
    @assert size(R) == (nstates, nactions, nstates)
    for s::Int in 1:nstates
        for a::Int in 1:nactions
            for s′::Int in 1:nstates
                @inbounds T[s′, a, s] = transition_probability(mdp, s, a, s′)
                @inbounds R[s′, a, s] = reward(mdp, s, a, s′)
            end
        end
    end

    i::Int = 0
    while i < horizon
        δ =bellman_backup_synchronous!(q, v, R, T, γ) 
        for s::Int in 1:nstates
            @inbounds v[s] = @views maximum(q[:, s])
        end
        i += 1
        δ < ϵ && break
    end

    return nothing
end

function bellman_backup_synchronous!(q::Matrix{Float64}, v::Vector{Float64}, R::Array{Float64, 3}, T::Array{Float64, 3}, γ::Float64)::Float64
    nactions::Int, nstates::Int = size(q)
    δ::Float64 = 0
    for s::Int in 1:nstates
        @inbounds for a::Int in 1:nactions
            qᵢ₊₁::Float64 = 0
            @simd for s′ in 1:nstates
                qᵢ₊₁ += T[s′, a, s]*(R[s′, a, s] + γ * v[s′])
            end
            δₐₛ::Float64 = abs(qᵢ₊₁ - q[a, s])
            if δₐₛ > δ
                δ = δₐₛ
            end
            q[a, s] = qᵢ₊₁
        end
    end
    return δ
end