fix incorrect folder name for julia-0.6.x
Former-commit-id: ef2c7401e0876f22d2f7762d182cfbcd5a7d9c70
This commit is contained in:
129
julia-0.6.3/share/julia/base/pkg/resolve/fieldvalue.jl
Normal file
129
julia-0.6.3/share/julia/base/pkg/resolve/fieldvalue.jl
Normal file
@@ -0,0 +1,129 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module FieldValues
|
||||
|
||||
using ...VersionWeights
|
||||
importall .....Base.Operators
|
||||
|
||||
export FieldValue, Field, validmax, secondmax
|
||||
|
||||
# FieldValue is a hierarchical numeric type with 6 levels.
|
||||
# When summing two FieldValues, the levels are summed independently.
|
||||
# When comparing them, lower levels take precedence.
|
||||
# The levels are used as such:
|
||||
# l0 : for hard constraints (dependencies and requirements)
|
||||
# l1 : for favoring higher versions of the explicitly required
|
||||
# packages
|
||||
# l2 : for favoring higher versions of all other packages
|
||||
# l3 : for favoring uninstallation of non-needed packages
|
||||
# l4 : for favoring dependants over dependencies
|
||||
# l5 : for symmetry-breaking random noise
|
||||
#
|
||||
struct FieldValue
|
||||
l0::Int
|
||||
l1::VersionWeight
|
||||
l2::VersionWeight
|
||||
l3::Int
|
||||
l4::Int
|
||||
l5::Int128
|
||||
end
|
||||
FieldValue(l0::Integer, l1::VersionWeight, l2::VersionWeight, l3::Integer, l4::Integer) = FieldValue(l0, l1, l2, l3, l4, Int128(0))
|
||||
FieldValue(l0::Integer, l1::VersionWeight, l2::VersionWeight, l3::Integer) = FieldValue(l0, l1, l2, l3, 0)
|
||||
FieldValue(l0::Integer, l1::VersionWeight, l2::VersionWeight) = FieldValue(l0, l1, l2, 0)
|
||||
FieldValue(l0::Integer, l1::VersionWeight) = FieldValue(l0, l1, zero(VersionWeight))
|
||||
FieldValue(l0::Integer) = FieldValue(l0, zero(VersionWeight))
|
||||
FieldValue() = FieldValue(0)
|
||||
|
||||
# This isn't nice, but it's for debugging only anyway
|
||||
function Base.show(io::IO, a::FieldValue)
|
||||
print(io, a.l0)
|
||||
a == FieldValue(a.l0) && return
|
||||
print(io, ".", a.l1)
|
||||
a == FieldValue(a.l0, a.l1) && return
|
||||
print(io, ".", a.l2)
|
||||
a == FieldValue(a.l0, a.l1, a.l2) && return
|
||||
print(io, ".", a.l3)
|
||||
a == FieldValue(a.l0, a.l1, a.l2, a.l3) && return
|
||||
print(io, ".", a.l4)
|
||||
a == FieldValue(a.l0, a.l1, a.l2, a.l3, a.l4) && return
|
||||
print(io, ".", a.l5)
|
||||
return
|
||||
end
|
||||
|
||||
const Field = Vector{FieldValue}
|
||||
|
||||
Base.zero(::Type{FieldValue}) = FieldValue()
|
||||
|
||||
Base.typemin(::Type{FieldValue}) = (x=typemin(Int); y=typemin(VersionWeight); FieldValue(x, y, y, x, x, typemin(Int128)))
|
||||
|
||||
Base.:-(a::FieldValue, b::FieldValue) = FieldValue(a.l0-b.l0, a.l1-b.l1, a.l2-b.l2, a.l3-b.l3, a.l4-b.l4, a.l5-b.l5)
|
||||
Base.:+(a::FieldValue, b::FieldValue) = FieldValue(a.l0+b.l0, a.l1+b.l1, a.l2+b.l2, a.l3+b.l3, a.l4+b.l4, a.l5+b.l5)
|
||||
|
||||
function Base.isless(a::FieldValue, b::FieldValue)
|
||||
a.l0 < b.l0 && return true
|
||||
a.l0 > b.l0 && return false
|
||||
c = cmp(a.l1, b.l1)
|
||||
c < 0 && return true
|
||||
c > 0 && return false
|
||||
c = cmp(a.l2, b.l2)
|
||||
c < 0 && return true
|
||||
c > 0 && return false
|
||||
a.l3 < b.l3 && return true
|
||||
a.l3 > b.l3 && return false
|
||||
a.l4 < b.l4 && return true
|
||||
a.l4 > b.l4 && return false
|
||||
a.l5 < b.l5 && return true
|
||||
return false
|
||||
end
|
||||
|
||||
Base.:(==)(a::FieldValue, b::FieldValue) =
|
||||
a.l0 == b.l0 && a.l1 == b.l1 && a.l2 == b.l2 && a.l3 == b.l3 && a.l4 == b.l4 && a.l5 == b.l5
|
||||
|
||||
Base.abs(a::FieldValue) = FieldValue(abs(a.l0), abs(a.l1), abs(a.l2), abs(a.l3), abs(a.l4), abs(a.l5))
|
||||
|
||||
Base.copy(a::FieldValue) = FieldValue(a.l0, copy(a.l1), copy(a.l2), a.l3, a.l4, a.l5)
|
||||
|
||||
function Base.unsafe_copy!(dest::Field, doffs, src::Field, soffs, n)
|
||||
for i = 1:n
|
||||
dest[doffs+i-1] = copy(src[soffs+i-1])
|
||||
end
|
||||
return dest
|
||||
end
|
||||
|
||||
# if the maximum field has l0 < 0, it means that
|
||||
# some hard constraint is being violated
|
||||
validmax(a::FieldValue) = a.l0 >= 0
|
||||
|
||||
# like usual indmax, but favors the highest indices
|
||||
# in case of a tie
|
||||
function Base.indmax(f::Field)
|
||||
m = typemin(FieldValue)
|
||||
mi = 0
|
||||
for j = length(f):-1:1
|
||||
if f[j] > m
|
||||
m = f[j]
|
||||
mi = j
|
||||
end
|
||||
end
|
||||
@assert mi != 0
|
||||
return mi
|
||||
end
|
||||
|
||||
# secondmax returns the (normalized) value of the second maximum in a
|
||||
# field. It's used to determine the most polarized field.
|
||||
function secondmax(f::Field)
|
||||
m = typemin(FieldValue)
|
||||
m2 = typemin(FieldValue)
|
||||
for i = 1:length(f)
|
||||
a = f[i]
|
||||
if a > m
|
||||
m2 = m
|
||||
m = a
|
||||
elseif a > m2
|
||||
m2 = a
|
||||
end
|
||||
end
|
||||
return m2 - m
|
||||
end
|
||||
|
||||
end
|
||||
364
julia-0.6.3/share/julia/base/pkg/resolve/interface.jl
Normal file
364
julia-0.6.3/share/julia/base/pkg/resolve/interface.jl
Normal file
@@ -0,0 +1,364 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module PkgToMaxSumInterface
|
||||
|
||||
using ...Types, ...Query, ..VersionWeights
|
||||
|
||||
export Interface, compute_output_dict, greedysolver,
|
||||
verify_solution, enforce_optimality!
|
||||
|
||||
# A collection of objects which allow interfacing external (Pkg) and
|
||||
# internal (MaxSum) representation
|
||||
mutable struct Interface
|
||||
# requirements and dependencies, in external representation
|
||||
reqs::Requires
|
||||
deps::Dict{String,Dict{VersionNumber,Available}}
|
||||
|
||||
# packages list
|
||||
pkgs::Vector{String}
|
||||
|
||||
# number of packages
|
||||
np::Int
|
||||
|
||||
# states per package: one per version + uninstalled
|
||||
spp::Vector{Int}
|
||||
|
||||
# pakage dict: associates an index to each package name
|
||||
pdict::Dict{String,Int}
|
||||
|
||||
# package versions: for each package, keep the list of the
|
||||
# possible version numbers; this defines a
|
||||
# mapping from version numbers of a package
|
||||
# to indices
|
||||
pvers::Vector{Vector{VersionNumber}}
|
||||
|
||||
# versions dict: associates a version index to each package
|
||||
# version; such that
|
||||
# pvers[p0][vdict[p0][vn]] = vn
|
||||
vdict::Vector{Dict{VersionNumber,Int}}
|
||||
|
||||
# version weights: the weight for each version of each package
|
||||
# (versions include the uninstalled state; the
|
||||
# higher the weight, the more favored the version)
|
||||
vweight::Vector{Vector{VersionWeight}}
|
||||
|
||||
function Interface(reqs::Requires, deps::Dict{String,Dict{VersionNumber,Available}})
|
||||
# generate pkgs
|
||||
pkgs = sort!(String[keys(deps)...])
|
||||
|
||||
np = length(pkgs)
|
||||
|
||||
# generate pdict
|
||||
pdict = Dict{String,Int}(pkgs[i] => i for i = 1:np)
|
||||
|
||||
# generate spp and pvers
|
||||
spp = Vector{Int}(np)
|
||||
|
||||
pvers = [VersionNumber[] for i = 1:np]
|
||||
|
||||
for (p,depsp) in deps, vn in keys(depsp)
|
||||
p0 = pdict[p]
|
||||
push!(pvers[p0], vn)
|
||||
end
|
||||
for p0 = 1:np
|
||||
sort!(pvers[p0])
|
||||
spp[p0] = length(pvers[p0]) + 1
|
||||
end
|
||||
|
||||
# generate vdict
|
||||
vdict = [Dict{VersionNumber,Int}() for p0 = 1:np]
|
||||
for (p,depsp) in deps
|
||||
p0 = pdict[p]
|
||||
vdict0 = vdict[p0]
|
||||
pvers0 = pvers[p0]
|
||||
for vn in keys(depsp)
|
||||
for v0 in 1:length(pvers0)
|
||||
if pvers0[v0] == vn
|
||||
vdict0[vn] = v0
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
## generate wveights:
|
||||
vweight = Vector{Vector{VersionWeight}}(np)
|
||||
for p0 = 1:np
|
||||
pvers0 = pvers[p0]
|
||||
spp0 = spp[p0]
|
||||
vweight0 = vweight[p0] = Vector{VersionWeight}(spp0)
|
||||
for v0 = 1:spp0-1
|
||||
vweight0[v0] = VersionWeight(pvers0[v0])
|
||||
end
|
||||
vweight0[spp0] = VersionWeight(v"0") # last version means uninstalled
|
||||
end
|
||||
|
||||
return new(reqs, deps, pkgs, np, spp, pdict, pvers, vdict, vweight)
|
||||
end
|
||||
end
|
||||
|
||||
# The output format is a Dict which associates a VersionNumber to each installed package name
|
||||
function compute_output_dict(sol::Vector{Int}, interface::Interface)
|
||||
pkgs = interface.pkgs
|
||||
np = interface.np
|
||||
pvers = interface.pvers
|
||||
spp = interface.spp
|
||||
|
||||
want = Dict{String,VersionNumber}()
|
||||
for p0 = 1:np
|
||||
p = pkgs[p0]
|
||||
s = sol[p0]
|
||||
if s != spp[p0]
|
||||
v = pvers[p0][s]
|
||||
want[p] = v
|
||||
end
|
||||
end
|
||||
|
||||
return want
|
||||
end
|
||||
|
||||
# Produce a trivial solution: try to maximize each version;
|
||||
# bail out as soon as some non-trivial requirements are detected.
|
||||
function greedysolver(interface::Interface)
|
||||
reqs = interface.reqs
|
||||
deps = interface.deps
|
||||
spp = interface.spp
|
||||
pdict = interface.pdict
|
||||
pvers = interface.pvers
|
||||
np = interface.np
|
||||
|
||||
# initialize solution: all uninstalled
|
||||
sol = [spp[p0] for p0 = 1:np]
|
||||
|
||||
# set up required packages to their highest allowed versions
|
||||
for (rp,rvs) in reqs
|
||||
rp0 = pdict[rp]
|
||||
# look for the highest version which satisfies the requirements
|
||||
rv = spp[rp0] - 1
|
||||
while rv > 0
|
||||
rvn = pvers[rp0][rv]
|
||||
rvn ∈ rvs && break
|
||||
rv -= 1
|
||||
end
|
||||
@assert rv > 0
|
||||
sol[rp0] = rv
|
||||
end
|
||||
|
||||
# we start from required packages and explore the graph
|
||||
# following dependencies
|
||||
staged = Set{String}(keys(reqs))
|
||||
seen = copy(staged)
|
||||
|
||||
while !isempty(staged)
|
||||
staged_next = Set{String}()
|
||||
for p in staged
|
||||
p0 = pdict[p]
|
||||
@assert sol[p0] < spp[p0]
|
||||
vn = pvers[p0][sol[p0]]
|
||||
a = deps[p][vn]
|
||||
|
||||
# scan dependencies
|
||||
for (rp,rvs) in a.requires
|
||||
rp0 = pdict[rp]
|
||||
# look for the highest version which satisfies the requirements
|
||||
rv = spp[rp0] - 1
|
||||
while rv > 0
|
||||
rvn = pvers[rp0][rv]
|
||||
rvn ∈ rvs && break
|
||||
rv -= 1
|
||||
end
|
||||
# if we found a version, and the package was uninstalled
|
||||
# or the same version was already selected, we're ok;
|
||||
# otherwise we can't be sure what the optimal configuration is
|
||||
# and we bail out
|
||||
if rv > 0 && (sol[rp0] == spp[rp0] || sol[rp0] == rv)
|
||||
sol[rp0] = rv
|
||||
else
|
||||
return (false, Int[])
|
||||
end
|
||||
|
||||
rp ∈ seen || push!(staged_next, rp)
|
||||
end
|
||||
end
|
||||
union!(seen, staged_next)
|
||||
staged = staged_next
|
||||
end
|
||||
|
||||
@assert verify_solution(sol, interface)
|
||||
|
||||
return true, sol
|
||||
end
|
||||
|
||||
# verifies that the solution fulfills all hard constraints
|
||||
# (requirements and dependencies)
|
||||
function verify_solution(sol::Vector{Int}, interface::Interface)
|
||||
reqs = interface.reqs
|
||||
deps = interface.deps
|
||||
spp = interface.spp
|
||||
pdict = interface.pdict
|
||||
pvers = interface.pvers
|
||||
vdict = interface.vdict
|
||||
|
||||
# verify requirements
|
||||
for (p,vs) in reqs
|
||||
p0 = pdict[p]
|
||||
sol[p0] != spp[p0] || return false
|
||||
vn = pvers[p0][sol[p0]]
|
||||
vn ∈ vs || return false
|
||||
end
|
||||
|
||||
# verify dependencies
|
||||
for (p,d) in deps
|
||||
p0 = pdict[p]
|
||||
vdict0 = vdict[p0]
|
||||
for (vn,a) in d
|
||||
v0 = vdict0[vn]
|
||||
if sol[p0] == v0
|
||||
for (rp, rvs) in a.requires
|
||||
p1 = pdict[rp]
|
||||
if sol[p1] == spp[p1]
|
||||
println("""
|
||||
VERIFICATION ERROR: REQUIRED DEPENDENCY NOT INSTALLED
|
||||
package p=$p (p0=$p0) version=$vn (v0=$v0) requires package rp=$rp in version set rvs=$rvs
|
||||
but package $rp is not being installed (p1=$p1 sol[p1]=$(sol[p1]) == spp[p1]=$(spp[p1]))
|
||||
""")
|
||||
return false
|
||||
end
|
||||
vn1 = pvers[p1][sol[p1]]
|
||||
if vn1 ∉ rvs
|
||||
println("""
|
||||
VERIFICATION ERROR: INVALID VERSION
|
||||
package p=$p (p0=$p0) version=$vn (v0=$v0) requires package rp=$rp in version set rvs=$rvs
|
||||
but package $rp version is being set to $vn1 (p1=$p1 sol[p1]=$(sol[p1]) spp[p1]=$(spp[p1]))
|
||||
""")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
# Push the given solution to a local optimium if needed
|
||||
function enforce_optimality!(sol::Vector{Int}, interface::Interface)
|
||||
np = interface.np
|
||||
|
||||
reqs = interface.reqs
|
||||
deps = interface.deps
|
||||
pkgs = interface.pkgs
|
||||
spp = interface.spp
|
||||
pdict = interface.pdict
|
||||
pvers = interface.pvers
|
||||
vdict = interface.vdict
|
||||
|
||||
# prepare some useful structures
|
||||
# pdeps[p0][v0] has all dependencies of package p0 version v0
|
||||
pdeps = [Vector{Requires}(spp[p0]-1) for p0 = 1:np]
|
||||
# prevdeps[p1][p0][v0] is the VersionSet of package p1 which package p0 version v0
|
||||
# depends upon
|
||||
prevdeps = [Dict{Int,Dict{Int,VersionSet}}() for p0 = 1:np]
|
||||
|
||||
for (p,d) in deps
|
||||
p0 = pdict[p]
|
||||
vdict0 = vdict[p0]
|
||||
for (vn,a) in d
|
||||
v0 = vdict0[vn]
|
||||
pdeps[p0][v0] = a.requires
|
||||
for (rp, rvs) in a.requires
|
||||
p1 = pdict[rp]
|
||||
if !haskey(prevdeps[p1], p0)
|
||||
prevdeps[p1][p0] = Dict{Int,VersionSet}()
|
||||
end
|
||||
prevdeps[p1][p0][v0] = rvs
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
restart = true
|
||||
while restart
|
||||
restart = false
|
||||
for p0 = 1:np
|
||||
s0 = sol[p0]
|
||||
if s0 >= spp[p0] - 1
|
||||
# either the package is not installed,
|
||||
# or it's already at the maximum version
|
||||
continue
|
||||
end
|
||||
viol = false
|
||||
# check if the higher version has a depencency which
|
||||
# would be violated by the state of the remaining packages
|
||||
for (p,vs) in pdeps[p0][s0+1]
|
||||
p1 = pdict[p]
|
||||
if sol[p1] == spp[p1]
|
||||
# the dependency is violated because
|
||||
# the other package is not being installed
|
||||
viol = true
|
||||
break
|
||||
end
|
||||
vn = pvers[p1][sol[p1]]
|
||||
if vn ∉ vs
|
||||
# the dependency is violated because
|
||||
# the other package version is invalid
|
||||
viol = true
|
||||
break
|
||||
end
|
||||
end
|
||||
viol && continue
|
||||
|
||||
# check if bumping the version would violate some
|
||||
# dependency of another package
|
||||
for (p1,d) in prevdeps[p0]
|
||||
vs = get(d, sol[p1], nothing)
|
||||
vs === nothing && continue
|
||||
vn = pvers[p0][s0+1]
|
||||
if vn ∉ vs
|
||||
# bumping the version would violate
|
||||
# the dependency
|
||||
viol = true
|
||||
break
|
||||
end
|
||||
end
|
||||
viol && continue
|
||||
# So the solution is non-optimal: we bump it manually
|
||||
#warn("nonoptimal solution for package $(interface.pkgs[p0]): sol=$s0")
|
||||
sol[p0] += 1
|
||||
restart = true
|
||||
end
|
||||
end
|
||||
|
||||
# Finally uninstall unneeded packages:
|
||||
# start from the required ones and keep only
|
||||
# the packages reachable from them along the graph
|
||||
uninst = trues(np)
|
||||
staged = Set{String}(keys(reqs))
|
||||
seen = copy(staged)
|
||||
|
||||
while !isempty(staged)
|
||||
staged_next = Set{String}()
|
||||
for p in staged
|
||||
p0 = pdict[p]
|
||||
uninst[p0] = false
|
||||
@assert sol[p0] < spp[p0]
|
||||
vn = pvers[p0][sol[p0]]
|
||||
a = deps[p][vn]
|
||||
|
||||
# scan dependencies
|
||||
for (rp,rvs) in a.requires
|
||||
rp0 = pdict[rp]
|
||||
@assert sol[rp0] < spp[rp0] && pvers[rp0][sol[rp0]] ∈ rvs
|
||||
rp ∈ seen || push!(staged_next, rp)
|
||||
end
|
||||
end
|
||||
union!(seen, staged_next)
|
||||
staged = staged_next
|
||||
end
|
||||
|
||||
for p0 in find(uninst)
|
||||
sol[p0] = spp[p0]
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
524
julia-0.6.3/share/julia/base/pkg/resolve/maxsum.jl
Normal file
524
julia-0.6.3/share/julia/base/pkg/resolve/maxsum.jl
Normal file
@@ -0,0 +1,524 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module MaxSum
|
||||
|
||||
include("fieldvalue.jl")
|
||||
|
||||
using .FieldValues, ..VersionWeights, ..PkgToMaxSumInterface
|
||||
|
||||
export UnsatError, Graph, Messages, maxsum
|
||||
|
||||
# An exception type used internally to signal that an unsatisfiable
|
||||
# constraint was detected
|
||||
mutable struct UnsatError <: Exception
|
||||
info
|
||||
end
|
||||
|
||||
# Some parameters to drive the decimation process
|
||||
mutable struct MaxSumParams
|
||||
nondec_iterations # number of initial iterations before starting
|
||||
# decimation
|
||||
dec_interval # number of iterations between decimations
|
||||
dec_fraction # fraction of nodes to decimate at every decimation
|
||||
# step
|
||||
|
||||
function MaxSumParams()
|
||||
accuracy = parse(Int, get(ENV, "JULIA_PKGRESOLVE_ACCURACY", "1"))
|
||||
if accuracy <= 0
|
||||
error("JULIA_PKGRESOLVE_ACCURACY must be > 0")
|
||||
end
|
||||
nondec_iterations = accuracy * 20
|
||||
dec_interval = accuracy * 10
|
||||
dec_fraction = 0.05 / accuracy
|
||||
return new(nondec_iterations, dec_interval, dec_fraction)
|
||||
end
|
||||
end
|
||||
|
||||
# Graph holds the graph structure onto which max-sum is run, in
|
||||
# sparse format
|
||||
mutable struct Graph
|
||||
# adjacency matrix:
|
||||
# for each package, has the list of neighbors
|
||||
# indices (both dependencies and dependants)
|
||||
gadj::Vector{Vector{Int}}
|
||||
|
||||
# compatibility mask:
|
||||
# for each package p0 has a list of bool masks.
|
||||
# Each entry in the list gmsk[p0] is relative to the
|
||||
# package p1 as read from gadj[p0].
|
||||
# Each mask has dimension spp1 x spp0, where
|
||||
# spp0 is the number of states of p0, and
|
||||
# spp1 is the number of states of p1.
|
||||
gmsk::Vector{Vector{BitMatrix}}
|
||||
|
||||
# dependency direction:
|
||||
# keeps track of which direction the dependency goes
|
||||
# takes 3 values:
|
||||
# 1 = dependant
|
||||
# -1 = dependency
|
||||
# 0 = both
|
||||
# Used to break symmetry between dependants and
|
||||
# dependencies (introduces a FieldValue at level l3).
|
||||
# The "both" case is for when there are dependency
|
||||
# relations which go both ways, in which case the
|
||||
# noise is left to discriminate in case of ties
|
||||
gdir::Vector{Vector{Int}}
|
||||
|
||||
# adjacency dict:
|
||||
# allows one to retrieve the indices in gadj, so that
|
||||
# gadj[p0][adjdict[p1][p0]] = p1
|
||||
# ("At which index does package p1 appear in gadj[p0]?")
|
||||
adjdict::Vector{Dict{Int,Int}}
|
||||
|
||||
# states per package: same as in Interface
|
||||
spp::Vector{Int}
|
||||
|
||||
# update order: shuffled at each iteration
|
||||
perm::Vector{Int}
|
||||
|
||||
# number of packages (all Vectors above have this length)
|
||||
np::Int
|
||||
|
||||
function Graph(interface::Interface)
|
||||
deps = interface.deps
|
||||
np = interface.np
|
||||
|
||||
spp = interface.spp
|
||||
pdict = interface.pdict
|
||||
pvers = interface.pvers
|
||||
vdict = interface.vdict
|
||||
|
||||
gadj = [Int[] for i = 1:np]
|
||||
gmsk = [BitMatrix[] for i = 1:np]
|
||||
gdir = [Int[] for i = 1:np]
|
||||
adjdict = [Dict{Int,Int}() for i = 1:np]
|
||||
|
||||
for (p,d) in deps
|
||||
p0 = pdict[p]
|
||||
vdict0 = vdict[p0]
|
||||
for (vn,a) in d
|
||||
v0 = vdict0[vn]
|
||||
for (rp, rvs) in a.requires
|
||||
p1 = pdict[rp]
|
||||
|
||||
j0 = 1
|
||||
while j0 <= length(gadj[p0]) && gadj[p0][j0] != p1
|
||||
j0 += 1
|
||||
end
|
||||
j1 = 1
|
||||
while j1 <= length(gadj[p1]) && gadj[p1][j1] != p0
|
||||
j1 += 1
|
||||
end
|
||||
@assert (j0 > length(gadj[p0]) && j1 > length(gadj[p1])) ||
|
||||
(j0 <= length(gadj[p0]) && j1 <= length(gadj[p1]))
|
||||
|
||||
if j0 > length(gadj[p0])
|
||||
push!(gadj[p0], p1)
|
||||
push!(gadj[p1], p0)
|
||||
j0 = length(gadj[p0])
|
||||
j1 = length(gadj[p1])
|
||||
|
||||
adjdict[p1][p0] = j0
|
||||
adjdict[p0][p1] = j1
|
||||
|
||||
bm = trues(spp[p1], spp[p0])
|
||||
bmt = bm'
|
||||
|
||||
push!(gmsk[p0], bm)
|
||||
push!(gmsk[p1], bmt)
|
||||
|
||||
push!(gdir[p0], 1)
|
||||
push!(gdir[p1], -1)
|
||||
else
|
||||
bm = gmsk[p0][j0]
|
||||
bmt = gmsk[p1][j1]
|
||||
if gdir[p0][j0] == -1
|
||||
gdir[p0][j0] = 0
|
||||
gdir[p1][j1] = 0
|
||||
end
|
||||
end
|
||||
|
||||
for v1 = 1:length(pvers[p1])
|
||||
if pvers[p1][v1] ∉ rvs
|
||||
bm[v1, v0] = false
|
||||
bmt[v0, v1] = false
|
||||
end
|
||||
end
|
||||
bm[end,v0] = false
|
||||
bmt[v0,end] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
perm = [1:np;]
|
||||
|
||||
return new(gadj, gmsk, gdir, adjdict, spp, perm, np)
|
||||
end
|
||||
end
|
||||
|
||||
# Messages has the cavity messages and the total fields, and
|
||||
# gets updated iteratively (and occasionally decimated) until
|
||||
# convergence
|
||||
mutable struct Messages
|
||||
# cavity incoming messages: for each package p0,
|
||||
# for each neighbor p1 of p0,
|
||||
# msg[p0][p1] is a vector of length spp[p0]
|
||||
# messages are normalized (i.e. the max is always 0)
|
||||
msg::Vector{Vector{Field}}
|
||||
|
||||
# overall fields: for each package p0,
|
||||
# fld[p0] is a vector of length spp[p0]
|
||||
# fields are not normalized
|
||||
fld::Vector{Field}
|
||||
|
||||
# backup of the initial value of fld, to be used when resetting
|
||||
initial_fld::Vector{Field}
|
||||
|
||||
# keep track of which variables have been decimated
|
||||
decimated::BitVector
|
||||
num_nondecimated::Int
|
||||
|
||||
function Messages(interface::Interface, graph::Graph)
|
||||
reqs = interface.reqs
|
||||
pkgs = interface.pkgs
|
||||
np = interface.np
|
||||
spp = interface.spp
|
||||
pvers = interface.pvers
|
||||
pdict = interface.pdict
|
||||
vweight = interface.vweight
|
||||
|
||||
# a "deterministic noise" function based on hashes
|
||||
function noise(p0::Int, v0::Int)
|
||||
s = pkgs[p0] * string(v0 == spp[p0] ? "UNINST" : pvers[p0][v0])
|
||||
Int128(hash(s))
|
||||
end
|
||||
|
||||
# external fields: there are 2 terms, a noise to break potential symmetries
|
||||
# and one to favor newest versions over older, and no-version over all
|
||||
fld = [[FieldValue(0, zero(VersionWeight), vweight[p0][v0], (v0==spp[p0]), 0, noise(p0,v0)) for v0 = 1:spp[p0]] for p0 = 1:np]
|
||||
|
||||
# enforce requirements
|
||||
for (rp, rvs) in reqs
|
||||
p0 = pdict[rp]
|
||||
pvers0 = pvers[p0]
|
||||
fld0 = fld[p0]
|
||||
for v0 = 1:spp[p0]-1
|
||||
vn = pvers0[v0]
|
||||
if !in(vn, rvs)
|
||||
# the state is forbidden by requirements
|
||||
fld0[v0] = FieldValue(-1)
|
||||
else
|
||||
# the state is one of those explicitly requested:
|
||||
# favor it at a higer level than normal (upgrade
|
||||
# FieldValue from l2 to l1)
|
||||
fld0[v0] += FieldValue(0, vweight[p0][v0], -vweight[p0][v0])
|
||||
end
|
||||
end
|
||||
# the uninstalled state is forbidden by requirements
|
||||
fld0[spp[p0]] = FieldValue(-1)
|
||||
end
|
||||
# normalize fields
|
||||
for p0 = 1:np
|
||||
m = maximum(fld[p0])
|
||||
for v0 = 1:spp[p0]
|
||||
fld[p0][v0] -= m
|
||||
end
|
||||
end
|
||||
|
||||
initial_fld = deepcopy(fld)
|
||||
|
||||
# initialize cavity messages to 0
|
||||
gadj = graph.gadj
|
||||
msg = [[zeros(FieldValue, spp[p0]) for p1 = 1:length(gadj[p0])] for p0 = 1:np]
|
||||
|
||||
return new(msg, fld, initial_fld, falses(np), np)
|
||||
end
|
||||
end
|
||||
|
||||
function getsolution(msgs::Messages)
|
||||
# the solution is just the location of the maximum in
|
||||
# each field
|
||||
|
||||
fld = msgs.fld
|
||||
np = length(fld)
|
||||
sol = Vector{Int}(np)
|
||||
for p0 = 1:np
|
||||
fld0 = fld[p0]
|
||||
s0 = indmax(fld0)
|
||||
if !validmax(fld0[s0])
|
||||
throw(UnsatError(p0))
|
||||
end
|
||||
sol[p0] = s0
|
||||
end
|
||||
return sol
|
||||
end
|
||||
|
||||
# This is the core of the max-sum solver:
|
||||
# for a given node p0 (i.e. a package) updates all
|
||||
# input cavity messages and fields of its neighbors
|
||||
function update(p0::Int, graph::Graph, msgs::Messages)
|
||||
gadj = graph.gadj
|
||||
gmsk = graph.gmsk
|
||||
gdir = graph.gdir
|
||||
adjdict = graph.adjdict
|
||||
spp = graph.spp
|
||||
np = graph.np
|
||||
msg = msgs.msg
|
||||
fld = msgs.fld
|
||||
decimated = msgs.decimated
|
||||
|
||||
maxdiff = zero(FieldValue)
|
||||
|
||||
gadj0 = gadj[p0]
|
||||
msg0 = msg[p0]
|
||||
fld0 = fld[p0]
|
||||
spp0 = spp[p0]
|
||||
adjdict0 = adjdict[p0]
|
||||
|
||||
# iterate over all neighbors of p0
|
||||
for j0 in 1:length(gadj0)
|
||||
|
||||
p1 = gadj0[j0]
|
||||
decimated[p1] && continue
|
||||
j1 = adjdict0[p1]
|
||||
#@assert j0 == adjdict[p1][p0]
|
||||
bm1 = gmsk[p1][j1]
|
||||
dir1 = gdir[p1][j1]
|
||||
spp1 = spp[p1]
|
||||
msg1 = msg[p1]
|
||||
|
||||
# compute the output cavity message p0->p1
|
||||
cavmsg = fld0 - msg0[j0]
|
||||
|
||||
if dir1 == -1
|
||||
# p0 depends on p1
|
||||
for v0 = 1:spp0-1
|
||||
cavmsg[v0] += FieldValue(0, VersionWeight(0), VersionWeight(0), 0, v0)
|
||||
end
|
||||
end
|
||||
|
||||
# keep the old input cavity message p0->p1
|
||||
oldmsg = msg1[j1]
|
||||
|
||||
# init the new message to minus infinity
|
||||
newmsg = [FieldValue(-1) for v1 = 1:spp1]
|
||||
|
||||
# compute the new message by passing cavmsg
|
||||
# through the constraint encoded in the bitmask
|
||||
# (nearly equivalent to:
|
||||
# newmsg = [maximum(cavmsg[bm1[:,v1]]) for v1 = 1:spp1]
|
||||
# except for the gnrg term)
|
||||
m = FieldValue(-1)
|
||||
for v1 = 1:spp1
|
||||
for v0 = 1:spp0
|
||||
if bm1[v0, v1]
|
||||
newmsg[v1] = max(newmsg[v1], cavmsg[v0])
|
||||
end
|
||||
end
|
||||
if dir1 == 1 && v1 != spp1
|
||||
# p1 depends on p0
|
||||
newmsg[v1] += FieldValue(0, VersionWeight(0), VersionWeight(0), 0, v1)
|
||||
end
|
||||
m = max(m, newmsg[v1])
|
||||
end
|
||||
if !validmax(m)
|
||||
# No state available without violating some
|
||||
# hard constraint
|
||||
throw(UnsatError(p1))
|
||||
end
|
||||
|
||||
# normalize the new message
|
||||
for v1 = 1:spp1
|
||||
newmsg[v1] -= m
|
||||
end
|
||||
|
||||
diff = newmsg - oldmsg
|
||||
maxdiff = max(maxdiff, maximum(abs.(diff)))
|
||||
|
||||
# update the field of p1
|
||||
fld1 = fld[p1]
|
||||
for v1 = 1:spp1
|
||||
fld1[v1] += diff[v1]
|
||||
end
|
||||
|
||||
# put the newly computed message in place
|
||||
msg1[j1] = newmsg
|
||||
end
|
||||
return maxdiff
|
||||
end
|
||||
|
||||
# A simple shuffling machinery for the update order in iterate()
|
||||
# (woulnd't pass any random quality test but it's arguably enough)
|
||||
let step=1
|
||||
global shuffleperm, shuffleperminit
|
||||
shuffleperminit() = (step = 1)
|
||||
function shuffleperm(graph::Graph)
|
||||
perm = graph.perm
|
||||
np = graph.np
|
||||
for j = np:-1:2
|
||||
k = mod(step,j)+1
|
||||
perm[j], perm[k] = perm[k], perm[j]
|
||||
step += isodd(j) ? 1 : k
|
||||
end
|
||||
#@assert isperm(perm)
|
||||
end
|
||||
end
|
||||
|
||||
# Call update for all nodes (i.e. packages) in
|
||||
# random order
|
||||
function iterate(graph::Graph, msgs::Messages)
|
||||
np = graph.np
|
||||
|
||||
maxdiff = zero(FieldValue)
|
||||
shuffleperm(graph)
|
||||
perm = graph.perm
|
||||
for p0 in perm
|
||||
maxdiff0 = update(p0, graph, msgs)
|
||||
maxdiff = max(maxdiff, maxdiff0)
|
||||
end
|
||||
return maxdiff
|
||||
end
|
||||
|
||||
function decimate1(p0::Int, graph::Graph, msgs::Messages)
|
||||
decimated = msgs.decimated
|
||||
fld = msgs.fld
|
||||
adjdict = graph.adjdict
|
||||
gmsk = graph.gmsk
|
||||
|
||||
@assert !decimated[p0]
|
||||
fld0 = fld[p0]
|
||||
s0 = indmax(fld0)
|
||||
# only do the decimation if it is consistent with
|
||||
# the previously decimated nodes
|
||||
for p1 in find(decimated)
|
||||
haskey(adjdict[p0], p1) || continue
|
||||
s1 = indmax(fld[p1])
|
||||
j1 = adjdict[p0][p1]
|
||||
gmsk[p1][j1][s0,s1] || return false
|
||||
end
|
||||
#println("DECIMATING $p0 (s0=$s0 fld=$fld0)")
|
||||
for v0 = 1:length(fld0)
|
||||
v0 == s0 && continue
|
||||
fld0[v0] = FieldValue(-1)
|
||||
end
|
||||
msgs.decimated[p0] = true
|
||||
msgs.num_nondecimated -= 1
|
||||
return true
|
||||
end
|
||||
|
||||
function reset_messages!(msgs::Messages)
|
||||
msg = msgs.msg
|
||||
fld = msgs.fld
|
||||
initial_fld = msgs.initial_fld
|
||||
decimated = msgs.decimated
|
||||
np = length(fld)
|
||||
for p0 = 1:np
|
||||
map(m->fill!(m, zero(FieldValue)), msg[p0])
|
||||
decimated[p0] && continue
|
||||
fld[p0] = copy(initial_fld[p0])
|
||||
end
|
||||
return msgs
|
||||
end
|
||||
|
||||
# If normal convergence fails (or is too slow) fix the most
|
||||
# polarized packages by adding extra infinite fields on every state
|
||||
# but the maximum
|
||||
function decimate(n::Int, graph::Graph, msgs::Messages)
|
||||
#println("DECIMATING $n NODES")
|
||||
adjdict = graph.adjdict
|
||||
fld = msgs.fld
|
||||
decimated = msgs.decimated
|
||||
fldorder = sortperm(fld, by=secondmax)
|
||||
did_dec = false
|
||||
for p0 in fldorder
|
||||
decimated[p0] && continue
|
||||
did_dec |= decimate1(p0, graph, msgs)
|
||||
n -= 1
|
||||
n == 0 && break
|
||||
end
|
||||
@assert n == 0
|
||||
if !did_dec
|
||||
# did not succeed in decimating anything;
|
||||
# try to decimate at least one node
|
||||
for p0 in fldorder
|
||||
decimated[p0] && continue
|
||||
if decimate1(p0, graph, msgs)
|
||||
did_dec = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if !did_dec
|
||||
# still didn't succeed, give up
|
||||
p0 = first(fldorder[.~(decimated)])
|
||||
throw(UnsatError(p0))
|
||||
end
|
||||
|
||||
reset_messages!(msgs)
|
||||
return
|
||||
end
|
||||
|
||||
# In case ties still exist at convergence, break them and
|
||||
# keep converging
|
||||
function break_ties(msgs::Messages)
|
||||
fld = msgs.fld
|
||||
unbroken_ties = Int[]
|
||||
for p0 = 1:length(fld)
|
||||
fld0 = fld[p0]
|
||||
z = 0
|
||||
m = typemin(FieldValue)
|
||||
for v0 = 1:length(fld0)
|
||||
if fld0[v0] > m
|
||||
m = fld0[v0]
|
||||
z = 1
|
||||
elseif fld0[v0] == m
|
||||
z += 1
|
||||
end
|
||||
end
|
||||
if z > 1
|
||||
#println("TIE! p0=$p0")
|
||||
decimate1(p0, msgs) && return false
|
||||
push!(unbroken_ties, p0)
|
||||
end
|
||||
end
|
||||
# If there were ties, but none were broken, bail out
|
||||
isempty(unbroken_ties) || throw(PkgError(first(unbroken_ties)))
|
||||
return true
|
||||
end
|
||||
|
||||
# Iterative solver: run iterate() until convergence
|
||||
# (occasionally calling decimate())
|
||||
function maxsum(graph::Graph, msgs::Messages)
|
||||
params = MaxSumParams()
|
||||
|
||||
it = 0
|
||||
shuffleperminit()
|
||||
while true
|
||||
it += 1
|
||||
maxdiff = iterate(graph, msgs)
|
||||
#println("it = $it maxdiff = $maxdiff")
|
||||
|
||||
if maxdiff == zero(FieldValue)
|
||||
break_ties(msgs) && break
|
||||
continue
|
||||
end
|
||||
if it >= params.nondec_iterations &&
|
||||
(it - params.nondec_iterations) % params.dec_interval == 0
|
||||
numdec = clamp(floor(Int, params.dec_fraction * graph.np), 1, msgs.num_nondecimated)
|
||||
decimate(numdec, graph, msgs)
|
||||
msgs.num_nondecimated == 0 && break
|
||||
end
|
||||
end
|
||||
|
||||
# Finally, decimate everything just to
|
||||
# check against inconsistencies
|
||||
# (old_numnondec is saved just to prevent
|
||||
# wrong messages about accuracy)
|
||||
old_numnondec = msgs.num_nondecimated
|
||||
decimate(msgs.num_nondecimated, graph, msgs)
|
||||
msgs.num_nondecimated = old_numnondec
|
||||
|
||||
return getsolution(msgs)
|
||||
end
|
||||
|
||||
end
|
||||
230
julia-0.6.3/share/julia/base/pkg/resolve/versionweight.jl
Normal file
230
julia-0.6.3/share/julia/base/pkg/resolve/versionweight.jl
Normal file
@@ -0,0 +1,230 @@
|
||||
# This file is a part of Julia. License is MIT: https://julialang.org/license
|
||||
|
||||
module VersionWeights
|
||||
|
||||
importall ....Base.Operators
|
||||
|
||||
export VersionWeight
|
||||
|
||||
struct HierarchicalValue{T}
|
||||
v::Vector{T}
|
||||
rest::T
|
||||
end
|
||||
|
||||
HierarchicalValue{T}(v::Vector{T}) = HierarchicalValue{T}(v, zero(T))
|
||||
HierarchicalValue(T::Type) = HierarchicalValue(T[])
|
||||
|
||||
Base.zero(::Type{HierarchicalValue{T}}) where {T} = HierarchicalValue(T)
|
||||
|
||||
Base.typemin(::Type{HierarchicalValue{T}}) where {T} = HierarchicalValue(T[], typemin(T))
|
||||
|
||||
for f in (:-, :+)
|
||||
@eval function Base.$f(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where T
|
||||
av = a.v
|
||||
bv = b.v
|
||||
la = length(a.v)
|
||||
lb = length(b.v)
|
||||
l0 = min(la, lb)
|
||||
l1 = max(la, lb)
|
||||
ld = la - lb
|
||||
rv = Vector{T}(l1)
|
||||
rf = ($f)(a.rest, b.rest)
|
||||
@inbounds for i = 1:l0
|
||||
rv[i] = ($f)(av[i], bv[i])
|
||||
end
|
||||
@inbounds for i = l0+1:l0+ld
|
||||
rv[i] = ($f)(av[i], b.rest)
|
||||
end
|
||||
@inbounds for i = l0+1:l0-ld
|
||||
rv[i] = ($f)(a.rest, bv[i])
|
||||
end
|
||||
return HierarchicalValue(rv, rf)
|
||||
end
|
||||
end
|
||||
|
||||
Base.:-(a::HierarchicalValue) = HierarchicalValue(-a.v, -a.rest)
|
||||
|
||||
function Base.cmp(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where T
|
||||
av = a.v
|
||||
bv = b.v
|
||||
la = length(a.v)
|
||||
lb = length(b.v)
|
||||
l0 = min(la, lb)
|
||||
l1 = max(la, lb)
|
||||
ld = la - lb
|
||||
@inbounds for i = 1:l0
|
||||
c = cmp(av[i], bv[i]); c != 0 && return c
|
||||
end
|
||||
@inbounds for i = l0+1:l0+ld
|
||||
c = cmp(av[i], b.rest); c != 0 && return c
|
||||
end
|
||||
@inbounds for i = l0+1:l0-ld
|
||||
c = cmp(a.rest, bv[i]); c != 0 && return c
|
||||
end
|
||||
return cmp(a.rest, b.rest)
|
||||
end
|
||||
Base.isless(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where {T} = cmp(a,b) < 0
|
||||
Base.:(==)(a::HierarchicalValue{T}, b::HierarchicalValue{T}) where {T} = cmp(a,b) == 0
|
||||
|
||||
Base.abs(a::HierarchicalValue{T}) where {T} = HierarchicalValue(T[abs(x) for x in a.v], abs(a.rest))
|
||||
|
||||
Base.copy(a::HierarchicalValue{T}) where {T} = HierarchicalValue(T[copy(x) for x in a.v], copy(a.rest))
|
||||
|
||||
struct VWPreBuildItem
|
||||
nonempty::Int
|
||||
s::HierarchicalValue{Int}
|
||||
i::Int
|
||||
end
|
||||
VWPreBuildItem() = VWPreBuildItem(0, HierarchicalValue(Int), 0)
|
||||
VWPreBuildItem(i::Int) = VWPreBuildItem(1, HierarchicalValue(Int), i)
|
||||
VWPreBuildItem(s::String) = VWPreBuildItem(1, HierarchicalValue(Int[s...]), 0)
|
||||
|
||||
Base.zero(::Type{VWPreBuildItem}) = VWPreBuildItem()
|
||||
|
||||
Base.typemin(::Type{VWPreBuildItem}) = (x=typemin(Int); VWPreBuildItem(x, typemin(HierarchicalValue{Int}), x))
|
||||
|
||||
Base.:-(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty-b.nonempty, a.s-b.s, a.i-b.i)
|
||||
Base.:+(a::VWPreBuildItem, b::VWPreBuildItem) = VWPreBuildItem(a.nonempty+b.nonempty, a.s+b.s, a.i+b.i)
|
||||
|
||||
Base.:-(a::VWPreBuildItem) = VWPreBuildItem(-a.nonempty, -a.s, -a.i)
|
||||
|
||||
function Base.cmp(a::VWPreBuildItem, b::VWPreBuildItem)
|
||||
c = cmp(a.nonempty, b.nonempty); c != 0 && return c
|
||||
c = cmp(a.s, b.s); c != 0 && return c
|
||||
return cmp(a.i, b.i)
|
||||
end
|
||||
Base.isless(a::VWPreBuildItem, b::VWPreBuildItem) = cmp(a,b) < 0
|
||||
Base.:(==)(a::VWPreBuildItem, b::VWPreBuildItem) = cmp(a,b) == 0
|
||||
|
||||
Base.abs(a::VWPreBuildItem) = VWPreBuildItem(abs(a.nonempty), abs(a.s), abs(a.i))
|
||||
|
||||
Base.copy(a::VWPreBuildItem) = VWPreBuildItem(a.nonempty, copy(a.s), a.i)
|
||||
|
||||
struct VWPreBuild
|
||||
nonempty::Int
|
||||
w::HierarchicalValue{VWPreBuildItem}
|
||||
end
|
||||
|
||||
const _vwprebuild_zero = VWPreBuild(0, HierarchicalValue(VWPreBuildItem))
|
||||
|
||||
function VWPreBuild(ispre::Bool, desc::Tuple{Vararg{Union{Int,String}}})
|
||||
isempty(desc) && return _vwprebuild_zero
|
||||
desc == ("",) && return VWPreBuild(ispre ? -1 : 1, HierarchicalValue(VWPreBuildItem[]))
|
||||
hv = HierarchicalValue([VWPreBuildItem(item) for item in desc])
|
||||
return VWPreBuild(ispre ? -1 : 0, hv)
|
||||
end
|
||||
VWPreBuild() = _vwprebuild_zero
|
||||
|
||||
Base.zero(::Type{VWPreBuild}) = VWPreBuild()
|
||||
|
||||
const _vwprebuild_min = VWPreBuild(typemin(Int), typemin(HierarchicalValue{VWPreBuildItem}))
|
||||
Base.typemin(::Type{VWPreBuild}) = _vwprebuild_min
|
||||
|
||||
function Base.:(-)(a::VWPreBuild, b::VWPreBuild)
|
||||
b === _vwprebuild_zero && return a
|
||||
a === _vwprebuild_zero && return -b
|
||||
VWPreBuild(a.nonempty-b.nonempty, a.w-b.w)
|
||||
end
|
||||
function Base.:(+)(a::VWPreBuild, b::VWPreBuild)
|
||||
b === _vwprebuild_zero && return a
|
||||
a === _vwprebuild_zero && return b
|
||||
VWPreBuild(a.nonempty+b.nonempty, a.w+b.w)
|
||||
end
|
||||
|
||||
function Base.:(-)(a::VWPreBuild)
|
||||
a === _vwprebuild_zero && return a
|
||||
VWPreBuild(-a.nonempty, -a.w)
|
||||
end
|
||||
|
||||
@inline function Base.cmp(a::VWPreBuild, b::VWPreBuild)
|
||||
a === _vwprebuild_zero && b === _vwprebuild_zero && return 0
|
||||
c = cmp(a.nonempty, b.nonempty); c != 0 && return c
|
||||
return cmp(a.w, b.w)
|
||||
end
|
||||
Base.isless(a::VWPreBuild, b::VWPreBuild) = cmp(a,b) < 0
|
||||
Base.:(==)(a::VWPreBuild, b::VWPreBuild) = cmp(a,b) == 0
|
||||
|
||||
function Base.abs(a::VWPreBuild)
|
||||
a === _vwprebuild_zero && return a
|
||||
VWPreBuild(abs(a.nonempty), abs(a.w))
|
||||
end
|
||||
|
||||
function Base.copy(a::VWPreBuild)
|
||||
a === _vwprebuild_zero && return a
|
||||
VWPreBuild(a.nonempty, copy(a.w))
|
||||
end
|
||||
|
||||
function Base.deepcopy_internal(a::VWPreBuild, dict::ObjectIdDict)
|
||||
haskey(dict, a) && return dict[a]
|
||||
b = (a === _vwprebuild_zero) ? _vwprebuild_zero : VWPreBuild(a.nonempty, Base.deepcopy_internal(a.w, dict))
|
||||
dict[a] = b
|
||||
return b
|
||||
end
|
||||
|
||||
# The numeric type used to determine how the different
|
||||
# versions of a package should be weighed
|
||||
struct VersionWeight
|
||||
major::Int
|
||||
minor::Int
|
||||
patch::Int
|
||||
prerelease::VWPreBuild
|
||||
build::VWPreBuild
|
||||
end
|
||||
VersionWeight(major::Int, minor::Int, patch::Int, prerelease::VWPreBuild) = VersionWeight(major, minor, patch, prerelease, zero(VWPreBuild))
|
||||
VersionWeight(major::Int, minor::Int, patch::Int) = VersionWeight(major, minor, patch, zero(VWPreBuild))
|
||||
VersionWeight(major::Int, minor::Int) = VersionWeight(major, minor, 0)
|
||||
VersionWeight(major::Int) = VersionWeight(major, 0)
|
||||
VersionWeight() = VersionWeight(0)
|
||||
|
||||
VersionWeight(vn::VersionNumber) =
|
||||
VersionWeight(vn.major, vn.minor, vn.patch,
|
||||
VWPreBuild(true, vn.prerelease), VWPreBuild(false, vn.build))
|
||||
|
||||
Base.zero(::Type{VersionWeight}) = VersionWeight()
|
||||
|
||||
Base.typemin(::Type{VersionWeight}) = (x=typemin(Int); y=typemin(VWPreBuild); VersionWeight(x, x, x, y, y))
|
||||
|
||||
Base.:(-)(a::VersionWeight, b::VersionWeight) =
|
||||
VersionWeight(a.major-b.major, a.minor-b.minor, a.patch-b.patch,
|
||||
a.prerelease-b.prerelease, a.build-b.build)
|
||||
|
||||
Base.:(+)(a::VersionWeight, b::VersionWeight) =
|
||||
VersionWeight(a.major+b.major, a.minor+b.minor, a.patch+b.patch,
|
||||
a.prerelease+b.prerelease, a.build+b.build)
|
||||
|
||||
Base.:(-)(a::VersionWeight) =
|
||||
VersionWeight(-a.major, -a.minor, -a.patch,
|
||||
-a.prerelease, -a.build)
|
||||
|
||||
function Base.cmp(a::VersionWeight, b::VersionWeight)
|
||||
c = cmp(a.major, b.major); c != 0 && return c
|
||||
c = cmp(a.minor, b.minor); c != 0 && return c
|
||||
c = cmp(a.patch, b.patch); c != 0 && return c
|
||||
c = cmp(a.prerelease, b.prerelease); c != 0 && return c
|
||||
return cmp(a.build, b.build)
|
||||
end
|
||||
Base.isless(a::VersionWeight, b::VersionWeight) = cmp(a,b) < 0
|
||||
Base.:(==)(a::VersionWeight, b::VersionWeight) = cmp(a,b) == 0
|
||||
|
||||
Base.abs(a::VersionWeight) =
|
||||
VersionWeight(abs(a.major), abs(a.minor), abs(a.patch),
|
||||
abs(a.prerelease), abs(a.build))
|
||||
|
||||
Base.copy(a::VersionWeight) =
|
||||
VersionWeight(a.major, a.minor, a.patch,
|
||||
copy(a.prerelease), copy(a.build))
|
||||
|
||||
# This isn't nice, but it's for debugging only anyway
|
||||
function Base.show(io::IO, a::VersionWeight)
|
||||
print(io, "(", a.major)
|
||||
a == VersionWeight(a.major) && @goto done
|
||||
print(io, ".", a.minor)
|
||||
a == VersionWeight(a.major, a.minor) && @goto done
|
||||
print(io, ".", a.patch)
|
||||
a.prerelease ≠ _vwprebuild_zero && print(io, "-", a.prerelease)
|
||||
a.build ≠ _vwprebuild_zero && print(io, "+", a.build)
|
||||
@label done
|
||||
print(io, ")")
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user