text:
export InterpretedSystem, InterpretedHomotopy

"""
    InterpretedSystem <: AbstractSystem

An [`AbstractSystem`](@ref) which automatically generates a program for the
fast evaluation of `F` and its Jacobian. The program is however, not compiled
but rather interpreted. See also [`CompiledSystem`](@ref).

    InterpretedSystem(F::System; optimizations = true)

Construct an `InterpretedSystem` from the given [`System`](@ref) `F`.
If `optimizations = true` then [`optimize`](@ref) is called on `F` before compiling.
"""
mutable struct InterpretedSystem{T₁,T₂} <: AbstractSystem
    system::System
    eval_interpreter::Interpreter{T₁}
    eval_interpreter_cache::InterpreterCache{Vector{ComplexF64},Nothing}
    eval_interpreter_cache_ext::InterpreterCache{Vector{ComplexDF64},Nothing}
    jac_interpreter::Interpreter{T₂}
    jac_interpreter_cache::InterpreterCache{Vector{ComplexF64},Nothing}
    taylor_caches::Tuple{
        InterpreterCache{TaylorVector{2,ComplexF64},Nothing},
        InterpreterCache{TaylorVector{3,ComplexF64},Nothing},
        InterpreterCache{TaylorVector{4,ComplexF64},Nothing},
    }
    arb_eval_interpreter_cache::Union{Nothing,InterpreterCache{ArbRefVector,Arb}}
    arb_jac_interpreter_cache::Union{Nothing,InterpreterCache{ArbRefVector,Arb}}
    acb_eval_interpreter_cache::Union{Nothing,InterpreterCache{AcbRefVector,Acb}}
    acb_jac_interpreter_cache::Union{Nothing,InterpreterCache{AcbRefVector,Acb}}
end

function InterpretedSystem(F::System; optimizations::Bool = true)
    F = optimizations ? optimize(F) : F
    eval_interpreter = interpreter(F)
    jac_interpreter = jacobian_interpreter(F)
    N = cache_min_length(eval_interpreter)
    taylor_caches = (
        InterpreterCache(TaylorVector{2}(ComplexF64, N)),
        InterpreterCache(TaylorVector{3}(ComplexF64, N)),
        InterpreterCache(TaylorVector{4}(ComplexF64, N)),
    )

    InterpretedSystem(
        F,
        eval_interpreter,
        InterpreterCache(ComplexF64, eval_interpreter),
        InterpreterCache(ComplexDF64, eval_interpreter),
        jac_interpreter,
        InterpreterCache(ComplexF64, jac_interpreter),
        taylor_caches,
        nothing,
        nothing,
        nothing,
        nothing,
    )
end

Base.size(F::InterpretedSystem) = size(F.system)
variables(F::InterpretedSystem) = variables(F.system)
parameters(F::InterpretedSystem) = parameters(F.system)
variable_groups(F::InterpretedSystem) = variable_groups(F.system)
Base.:(==)(F::InterpretedSystem, G::InterpretedSystem) = F.system == G.system

function Base.show(io::IO, F::InterpretedSystem)
    print(io, "Interpreted: ")
    show(io, F.system)
end

(F::InterpretedSystem)(x, p = nothing) = F.system(x, p)
function evaluate!(u, F::InterpretedSystem, x, p = nothing)
    execute!(u, F.eval_interpreter, x, p, F.eval_interpreter_cache)
end
function evaluate!(u, F::InterpretedSystem, x::AbstractVector{ComplexDF64}, p = nothing)
    execute!(u, F.eval_interpreter, x, p, F.eval_interpreter_cache_ext)
end
function evaluate_and_jacobian!(u, U, F::InterpretedSystem, x, p = nothing)
    execute!(u, U, F.jac_interpreter, x, p, F.jac_interpreter_cache)
    nothing
end
function jacobian!(U, F::InterpretedSystem, x, p = nothing)
    evaluate_and_jacobian!(nothing, U, F, x, p)
    U
end

for M = 1:3
    @eval function taylor!(
        u::AbstractVector,
        Order::Val{$M},
        F::InterpretedSystem,
        x,
        p = nothing,
    )
        execute!(u, Order, F.eval_interpreter, x, p, F.taylor_caches[$M])
        u
    end
end

# Arb
function evaluate!(
    u,
    F::InterpretedSystem,
    x::AbstractArray{<:Union{Arb,ArbRef}},
    p = nothing,
)
    if isnothing(F.arb_eval_interpreter_cache)
        F.arb_eval_interpreter_cache = InterpreterCache(Arb, F.eval_interpreter; prec = 128)
    end
    if u isa AbstractArray{<:Union{Arb,ArbRef}}
        set_arb_precision!(
            F.arb_eval_interpreter_cache,
            max(precision(first(u)), precision(first(x))),
        )
    else
        set_arb_precision!(F.arb_eval_interpreter_cache, precision(first(x)))
    end
    execute!(u, F.eval_interpreter, x, p, F.arb_eval_interpreter_cache)
end
function evaluate!(
    u,
    F::InterpretedSystem,
    x::AbstractArray{<:Union{Acb,AcbRef}},
    p = nothing,
)
    if isnothing(F.acb_eval_interpreter_cache)
        F.acb_eval_interpreter_cache = InterpreterCache(Acb, F.eval_interpreter; prec = 128)
    end
    if u isa AbstractArray{<:Union{Acb,AcbRef}}
        set_arb_precision!(
            F.acb_eval_interpreter_cache,
            max(precision(first(u)), precision(first(x))),
        )
    else
        set_arb_precision!(F.acb_eval_interpreter_cache, precision(first(x)))
    end
    execute!(u, F.eval_interpreter, x, p, F.acb_eval_interpreter_cache)
end

function evaluate_and_jacobian!(
    u,
    U,
    F::InterpretedSystem,
    x::AbstractArray{<:Union{Arb,ArbRef}},
    p = nothing,
)
    if isnothing(F.arb_eval_interpreter_cache)
        F.arb_jac_interpreter_cache = InterpreterCache(Arb, F.jac_interpreter; prec = 128)
    end
    if U isa AbstractArray{<:Union{Arb,ArbRef}}
        set_arb_precision!(
            F.arb_jac_interpreter_cache,
            max(precision(first(U)), precision(first(x))),
        )
    else
        set_arb_precision!(F.arb_jac_interpreter_cache, precision(first(x)))
    end
    execute!(u, U, F.jac_interpreter, x, p, F.arb_jac_interpreter_cache)
end

function evaluate_and_jacobian!(
    u,
    U,
    F::InterpretedSystem,
    x::AbstractArray{<:Union{Acb,AcbRef}},
    p = nothing,
)
    if isnothing(F.acb_eval_interpreter_cache)
        F.acb_jac_interpreter_cache = InterpreterCache(Acb, F.jac_interpreter; prec = 128)
    end
    if U isa AbstractArray{<:Union{Acb,AcbRef}}
        set_arb_precision!(
            F.acb_jac_interpreter_cache,
            max(precision(first(U)), precision(first(x))),
        )
    else
        set_arb_precision!(F.acb_jac_interpreter_cache, precision(first(x)))
    end
    execute!(u, U, F.jac_interpreter, x, p, F.acb_jac_interpreter_cache)
end

function (F::InterpretedSystem)(x::AbstractArray{<:Union{Arb,ArbRef}}, p = nothing)
    u = ArbVector(first(size(F)); prec = precision(first(x)))
    evaluate!(u, F, x, p)
    u
end
function (F::InterpretedSystem)(x::AbstractArray{<:Union{Acb,AcbRef}}, p = nothing)
    u = AcbVector(first(size(F)); prec = precision(first(x)))
    evaluate!(u, F, x, p)
    u
end
(F::System)(x::AbstractVector{<:Union{Arb,ArbRef,Acb,AcbRef}}, p::Nothing = nothing) =
    InterpretedSystem(F)(x, p)
(F::System)(x::AbstractVector{<:Union{Arb,ArbRef,Acb,AcbRef}}, p::AbstractArray) =
    InterpretedSystem(F)(x, p)
(F::System)(x::AbstractMatrix{<:Union{Arb,ArbRef,Acb,AcbRef}}, p = nothing) =
    InterpretedSystem(F)(x, p)

"""
    InterpretedHomotopy <: AbstractHomotopy

An [`AbstractHomotopy`](@ref) which automatically generates a program for the
fast evaluation of `H` and its Jacobian. The program is however, not compiled
but rather interpreted. See also [`CompiledHomotopy`](@ref).

    InterpretedHomotopy(H::Homotopy; optimizations = true)

Construct an `InterpretedHomotopy` from the given [`Homotopy`](@ref) `H`.
If `optimizations = true` then [`optimize`](@ref) is called on `H` before compiling.
"""
struct InterpretedHomotopy{T₁,T₂} <: AbstractHomotopy
    homotopy::Homotopy
    eval_interpreter::Interpreter{T₁}
    eval_interpreter_cache::InterpreterCache{Vector{ComplexF64}}
    eval_interpreter_cache_ext::InterpreterCache{Vector{ComplexDF64}}
    jac_interpreter::Interpreter{T₂}
    jac_interpreter_cache::InterpreterCache{Vector{ComplexF64}}
    taylor_caches::Tuple{
        InterpreterCache{TaylorVector{2,ComplexF64}},
        InterpreterCache{TaylorVector{3,ComplexF64}},
        InterpreterCache{TaylorVector{4,ComplexF64}},
    }
end

function InterpretedHomotopy(H::Homotopy; optimizations::Bool = true)
    H = optimizations ? optimize(H) : H
    eval_interpreter = interpreter(H)
    jac_interpreter = jacobian_interpreter(H)
    N = cache_min_length(eval_interpreter)
    taylor_caches = (
        InterpreterCache(TaylorVector{2}(ComplexF64, N)),
        InterpreterCache(TaylorVector{3}(ComplexF64, N)),
        InterpreterCache(TaylorVector{4}(ComplexF64, N)),
    )

    InterpretedHomotopy(
        H,
        eval_interpreter,
        InterpreterCache(ComplexF64, eval_interpreter),
        InterpreterCache(ComplexDF64, eval_interpreter),
        jac_interpreter,
        InterpreterCache(ComplexF64, jac_interpreter),
        taylor_caches,
    )
end

Base.size(H::InterpretedHomotopy) = size(H.homotopy)
variables(H::InterpretedHomotopy) = variables(H.homotopy)
parameters(H::InterpretedHomotopy) = parameters(H.homotopy)
Homotopy(H::InterpretedHomotopy) = H.homotopy

function Base.show(io::IO, H::InterpretedHomotopy)
    print(io, "Interpreted: ")
    show(io, H.homotopy)
end

(H::InterpretedHomotopy)(x, t, p = nothing) = H.homotopy(x, t, p)
function evaluate!(
    u,
    H::InterpretedHomotopy,
    x,
    t,
    p = nothing,
    cache = H.eval_interpreter_cache,
)
    execute!(u, H.eval_interpreter, x, t, p, cache)
end
function evaluate!(u, H::InterpretedHomotopy, x::AbstractArray{ComplexDF64}, t, p = nothing)
    execute!(u, H.eval_interpreter, x, t, p, H.eval_interpreter_cache_ext)
end
function evaluate_and_jacobian!(
    u,
    U,
    H::InterpretedHomotopy,
    x,
    t,
    p = nothing,
    cache = H.jac_interpreter_cache,
)
    execute!(u, U, H.jac_interpreter, x, t, p, cache)
    nothing
end

function jacobian!(
    U,
    H::InterpretedHomotopy,
    x,
    t,
    p = nothing,
    cache = H.jac_interpreter_cache,
)
    execute!(nothing, U, H.jac_interpreter, x, t, p, cache)
    nothing
end

for M = 1:3
    @eval function taylor!(
        u::AbstractVecOrMat,
        Order::Val{$M},
        H::InterpretedHomotopy,
        x,
        t,
        p::Union{AbstractVector,Nothing} = nothing,
    )
        execute!(u, Order, H.eval_interpreter, x, t, p, H.taylor_caches[$M])
        u
    end
end


text:
using LinearAlgebra;
using Printf;

function linspace(start::Number, stop::Number, n)
	linx = zeros(Float64, n);
	delta = (stop - start) / (n - 1);
	for i = 1:n
		linx[i] = start + (i - 1) * delta;
	end
	return linx;
end

function tdma(n, a, b, c, r)
	ans = zeros(Float64, n);
	g = zeros(Float64, n);
	g[1] = b[1];
	s = zeros(Float64, n);
	s[1] = r[1];

	ans[1] = s[1] / g[1];

	for i = 2:n-1
		g[i] = b[i] - a[i] * c[i - 1] / g[i - 1];
		s[i] = r[i] - a[i] * s[i - 1] / g[i - 1];
		ans[i] = s[i] / g[i];
	end
	for i = n-2:1
		ans[i] = (s[i] - c[i] * ans[i + 1]) / g[i];
	end
	return ans
end

function init()
	global nx = 5;
	global ny = 5;
	global lx = Float64(100.0);
	global ly = Float64(100.0);
	global x = linspace(0, lx, nx);
	global y = linspace(0, ly, ny);
	global dx = lx / (nx - 1);
	global dy = ly / (ny - 1);
	global t = 0.0;
	global tlims = 300.0;;
	global dt = 10.0;
	global kappa = 0.835;
	global beta_x = kappa * dt / 2.0dx^2;
	global beta_y = kappa * dt / 2.0dy^2;
	global u = zeros(Float64, (ny, nx));
	for j in 1:ny
		u[j, 1] = 75.0;
		u[j, end] = 50.0;
	end
	for i in 1:nx
		u[1, i] = 0.0;
		u[end, i] = 100.0;
	end
	u[begin, begin] = (75.0 + 0.0) / 2.0;
	u[end, begin] = (75.0 + 100.0) / 2.0;
	u[begin, end] = (0.0 + 50.0) / 2.0;
	u[end, end] = (100.0 + 50.0) / 2.0;
end

function output(x::Array, y::Array, u::Array, t::Float64)
	filename = @sprintf("adi_cn2d_t=%03d.dat", t);
	open(filename, "w") do io
		for i in 1:ny
			for j in 1:nx
				write(io, @sprintf("%.5f %.5f %.5f\n", x[j], y[i], u[i, j]));
			end
		end
	end
end

function cn_direction_x(u::Array)
	a = zeros(Float64, nx - 2);
	b = zeros(Float64, nx - 2);
	c = zeros(Float64, nx - 2);
	r = zeros(Float64, nx - 2);
	for j = 1:ny-2
		for i = 1:nx-2
			a[i] = -beta_y;
			b[i] = 1.0 + 2.0beta_y;
			c[i] = -beta_y;
			r[i] = u[j+1, i+1] + beta_x*(u[j+2, i+1] - 2u[j+1, i+1] + u[j, i+1]);
		end
		r[begin] = r[begin] - a[begin] * u[j+1, begin];
		r[end] = r[end] - c[end] * u[j+1, end];
		u[j+1, begin+1:end-1] = tdma(nx - 2, a, b, c, r)
	end
end

function cn_direction_y(u::Array)
	a = zeros(Float64, nx - 2);
	b = zeros(Float64, nx - 2);
	c = zeros(Float64, nx - 2);
	r = zeros(Float64, nx - 2);
	for i in 1:nx-2
		for j in 1:ny-2
			a[j] = -beta_x;
			b[j] = 1.0 + 2.0beta_x;
			c[j] = -beta_x;
			r[j] = u[j+1, i+1] + beta_y * (u[j+1, i+2] - 2.0u[j+1, i+1] + u[j+1, i])
		end
		r[begin] = r[begin] - a[begin]*u[begin, i+1];
		r[end] = r[end] - c[begin]*u[end, i+1];
		u[begin+1:end-1, i] = tdma(ny-2, a, b, c, r);
	end
end

function main()
	init();
	@printf("nx = %d, ny = %d, dt = %.4f, dx = %.4f, dy = %.4f\n", nx, ny, dt, dx, dy);
	loop_count = 1;
	while t <= tlims
		@printf("t = %.5f\n", t);
		## 奇数回目はx方向を陰的にする.
		if loop_count % 2 == 1
			## x方向にcrank nicolson
			cn_direction_x(u);
			## y方向にcrank nicolson
			cn_direction_y(u);
			output(x, y, u, t);
		## 偶数回目はy方向を陰的にする.
		else
			## y方向にcrank nicolson
			cn_direction_y(u);
			## x方向にcrank nicolson
			cn_direction_x(u);
			output(x, y, u, t);
		end
		loop_count += 1;
		global t += dt;
	end
end

main()

ext: jl


text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

module Types

using UUIDs
using Random
using Dates
import LibGit2
import REPL
import Base.string
using REPL.TerminalMenus

using TOML
import ..Pkg, ..Registry
import ..Pkg: GitTools, depots, depots1, logdir, set_readonly, safe_realpath, pkg_server, stdlib_dir, stdlib_path, isurl, stderr_f
import Base.BinaryPlatforms: Platform
using ..Pkg.Versions

import Base: SHA1
using SHA

export UUID, SHA1, VersionRange, VersionSpec,
    PackageSpec, PackageEntry, EnvCache, Context, GitRepo, Context!, Manifest, Project, err_rep,
    PkgError, pkgerror, has_name, has_uuid, is_stdlib, stdlib_version, is_unregistered_stdlib, stdlibs, write_env, write_env_usage, parse_toml, find_registered!,
    project_resolve!, project_deps_resolve!, manifest_resolve!, registry_resolve!, stdlib_resolve!, handle_repos_develop!, handle_repos_add!, ensure_resolved,
    registered_name,
    manifest_info,
    read_project, read_package, read_manifest,
    PackageMode, PKGMODE_MANIFEST, PKGMODE_PROJECT, PKGMODE_COMBINED,
    UpgradeLevel, UPLEVEL_FIXED, UPLEVEL_PATCH, UPLEVEL_MINOR, UPLEVEL_MAJOR,
    PreserveLevel, PRESERVE_ALL, PRESERVE_DIRECT, PRESERVE_SEMVER, PRESERVE_TIERED, PRESERVE_NONE,
    projectfile_path, manifestfile_path

# Load in data about historical stdlibs
include("HistoricalStdlibs.jl")

deepcopy_toml(x) = x
function deepcopy_toml(@nospecialize(x::Vector))
    d = similar(x)
    for (i, v) in enumerate(x)
        d[i] = deepcopy_toml(v)
    end
    return d
end
function deepcopy_toml(x::Dict{String, Any})
    d = Dict{String, Any}()
    sizehint!(d, length(x))
    for (k, v) in x
        d[k] = deepcopy_toml(v)
    end
    return d
end

# See loading.jl
const TOML_CACHE = Base.TOMLCache(TOML.Parser(), Dict{String, Dict{String, Any}}())
const TOML_LOCK = ReentrantLock()
# Some functions mutate the returning Dict so return a copy of the cached value here
parse_toml(toml_file::AbstractString) =
    Base.invokelatest(deepcopy_toml, Base.parsed_toml(toml_file, TOML_CACHE, TOML_LOCK))::Dict{String, Any}

#################
# Pkg Error #
#################
struct PkgError <: Exception
    msg::String
end
pkgerror(msg::String...) = throw(PkgError(join(msg)))
Base.showerror(io::IO, err::PkgError) = print(io, err.msg)

###############
# PackageSpec #
###############
@enum(UpgradeLevel, UPLEVEL_FIXED, UPLEVEL_PATCH, UPLEVEL_MINOR, UPLEVEL_MAJOR)
@enum(PreserveLevel, PRESERVE_ALL, PRESERVE_DIRECT, PRESERVE_SEMVER, PRESERVE_TIERED, PRESERVE_NONE)
@enum(PackageMode, PKGMODE_PROJECT, PKGMODE_MANIFEST, PKGMODE_COMBINED)

const VersionTypes = Union{VersionNumber,VersionSpec,UpgradeLevel}

Base.@kwdef mutable struct GitRepo
    source::Union{Nothing,String} = nothing
    rev::Union{Nothing,String} = nothing
    subdir::Union{String, Nothing} = nothing
end

Base.:(==)(r1::GitRepo, r2::GitRepo) =
    r1.source == r2.source && r1.rev == r2.rev && r1.subdir == r2.subdir

mutable struct PackageSpec
    name::Union{Nothing,String}
    uuid::Union{Nothing,UUID}
    version::Union{Nothing,VersionTypes,String}
    tree_hash::Union{Nothing,SHA1}
    repo::GitRepo
    path::Union{Nothing,String}
    pinned::Bool
    # used for input only
    url
    rev
    subdir

end
function PackageSpec(; name::Union{Nothing,AbstractString} = nothing,
                       uuid::Union{Nothing,UUID,AbstractString} = nothing,
                       version::Union{Nothing,VersionTypes,AbstractString} = VersionSpec(),
                       tree_hash::Union{Nothing,SHA1} = nothing,
                       repo::GitRepo = GitRepo(),
                       path::Union{Nothing,AbstractString} = nothing,
                       pinned::Bool = false,
                       url = nothing,
                       rev = nothing,
                       subdir = nothing)
    uuid = uuid === nothing ? nothing : UUID(uuid)
    return PackageSpec(name, uuid, version, tree_hash, repo, path, pinned, url, rev, subdir)
end
PackageSpec(name::AbstractString) = PackageSpec(;name=name)
PackageSpec(name::AbstractString, uuid::UUID) = PackageSpec(;name=name, uuid=uuid)
PackageSpec(name::AbstractString, version::VersionTypes) = PackageSpec(;name=name, version=version)
PackageSpec(n::AbstractString, u::UUID, v::VersionTypes) = PackageSpec(;name=n, uuid=u, version=v)

function Base.:(==)(a::PackageSpec, b::PackageSpec)
    return a.name == b.name && a.uuid == b.uuid && a.version == b.version &&
    a.tree_hash == b.tree_hash && a.repo == b.repo && a.path == b.path &&
    a.pinned == b.pinned
end

function err_rep(pkg::PackageSpec)
    x = pkg.name !== nothing && pkg.uuid !== nothing ? x = "$(pkg.name) [$(string(pkg.uuid)[1:8])]" :
        pkg.name !== nothing ? pkg.name :
        pkg.uuid !== nothing ? string(pkg.uuid)[1:8] :
        pkg.repo.source
    return "`$x`"
end

has_name(pkg::PackageSpec) = pkg.name !== nothing
has_uuid(pkg::PackageSpec) = pkg.uuid !== nothing
isresolved(pkg::PackageSpec) = pkg.uuid !== nothing && pkg.name !== nothing

function Base.show(io::IO, pkg::PackageSpec)
    vstr = repr(pkg.version)
    f = []
    pkg.name !== nothing && push!(f, "name" => pkg.name)
    pkg.uuid !== nothing && push!(f, "uuid" => pkg.uuid)
    pkg.tree_hash !== nothing && push!(f, "tree_hash" => pkg.tree_hash)
    pkg.path !== nothing && push!(f, "path" => pkg.path)
    pkg.url !== nothing && push!(f, "url" => pkg.url)
    pkg.rev !== nothing && push!(f, "rev" => pkg.rev)
    pkg.subdir !== nothing && push!(f, "subdir" => pkg.subdir)
    pkg.pinned && push!(f, "pinned" => pkg.pinned)
    push!(f, "version" => (vstr == "VersionSpec(\"*\")" ? "*" : vstr))
    if pkg.repo.source !== nothing
        push!(f, "repo/source" => string("\"", pkg.repo.source, "\""))
    end
    if pkg.repo.rev !== nothing
        push!(f, "repo/rev" => pkg.repo.rev)
    end
    if pkg.repo.subdir !== nothing
        push!(f, "repo/subdir" => pkg.repo.subdir)
    end
    print(io, "PackageSpec(\n")
    for (field, value) in f
        print(io, "  ", field, " = ", value, "\n")
    end
    print(io, ")")
end

############
# EnvCache #
############

function projectfile_path(env_path::String; strict=false)
    for name in Base.project_names
        maybe_file = joinpath(env_path, name)
        isfile(maybe_file) && return maybe_file
    end
    return strict ? nothing : joinpath(env_path, "Project.toml")
end

function manifestfile_path(env_path::String; strict=false)
    for name in Base.manifest_names
        maybe_file = joinpath(env_path, name)
        isfile(maybe_file) && return maybe_file
    end
    if strict
        return nothing
    else
        project = basename(projectfile_path(env_path)::String)
        idx = findfirst(x -> x == project, Base.project_names)
        @assert idx !== nothing
        return joinpath(env_path, Base.manifest_names[idx])
    end
end

function find_project_file(env::Union{Nothing,String}=nothing)
    project_file = nothing
    if env isa Nothing
        project_file = Base.active_project()
        project_file === nothing && pkgerror("no active project")
    elseif startswith(env, '@')
        project_file = Base.load_path_expand(env)
        project_file === nothing && pkgerror("package environment does not exist: $env")
    elseif env isa String
        if isdir(env)
            isempty(readdir(env)) || pkgerror("environment is a package directory: $env")
            project_file = joinpath(env, Base.project_names[end])
        else
            project_file = endswith(env, ".toml") ? abspath(env) :
                abspath(env, Base.project_names[end])
        end
    end
    @assert project_file isa String &&
        (isfile(project_file) || !ispath(project_file) ||
         isdir(project_file) && isempty(readdir(project_file)))
    return Pkg.safe_realpath(project_file)
end

Base.@kwdef mutable struct Compat
    val::VersionSpec
    str::String
end

Base.@kwdef mutable struct Project
    other::Dict{String,Any} = Dict{String,Any}()
    # Fields
    name::Union{String, Nothing} = nothing
    uuid::Union{UUID, Nothing} = nothing
    version::Union{VersionTypes, Nothing} = nothing
    manifest::Union{String, Nothing} = nothing
    # Sections
    deps::Dict{String,UUID} = Dict{String,UUID}()
    extras::Dict{String,UUID} = Dict{String,UUID}()
    targets::Dict{String,Vector{String}} = Dict{String,Vector{String}}()
    compat::Dict{String,Compat} = Dict{String,Compat}()
end
Base.:(==)(t1::Project, t2::Project) = all(x -> (getfield(t1, x) == getfield(t2, x))::Bool, fieldnames(Project))
Base.hash(x::Project, h::UInt) = foldr(hash, [getfield(t, x) for x in fieldnames(Project)], init=h)

Base.@kwdef mutable struct PackageEntry
    name::Union{String,Nothing} = nothing
    version::Union{VersionNumber,Nothing} = nothing
    path::Union{String,Nothing} = nothing
    pinned::Bool = false
    repo::GitRepo = GitRepo()
    tree_hash::Union{Nothing,SHA1} = nothing
    deps::Dict{String,UUID} = Dict{String,UUID}()
    uuid::Union{Nothing, UUID} = nothing
    other::Union{Dict,Nothing} = nothing
end
Base.:(==)(t1::PackageEntry, t2::PackageEntry) = t1.name == t2.name &&
    t1.version == t2.version &&
    t1.path == t2.path &&
    t1.pinned == t2.pinned &&
    t1.repo == t2.repo &&
    t1.tree_hash == t2.tree_hash &&
    t1.deps == t2.deps &&
    t1.uuid == t2.uuid
    # omits `other`
Base.hash(x::PackageEntry, h::UInt) = foldr(hash, [x.name, x.version, x.path, x.pinned, x.repo, x.tree_hash, x.deps, x.uuid], init=h)  # omits `other`

Base.@kwdef mutable struct Manifest
    julia_version::Union{Nothing,VersionNumber} = nothing # only set to VERSION when resolving
    manifest_format::VersionNumber = v"2.0.0"
    deps::Dict{UUID,PackageEntry} = Dict{UUID,PackageEntry}()
    other::Dict{String,Any} = Dict{String,Any}()
end
Base.:(==)(t1::Manifest, t2::Manifest) = all(x -> (getfield(t1, x) == getfield(t2, x))::Bool, fieldnames(Manifest))
Base.hash(m::Manifest, h::UInt) = foldr(hash, [getfield(m, x) for x in fieldnames(Manifest)], init=h)
Base.getindex(m::Manifest, i_or_key) = getindex(m.deps, i_or_key)
Base.get(m::Manifest, key, default) = get(m.deps, key, default)
Base.setindex!(m::Manifest, i_or_key, value) = setindex!(m.deps, i_or_key, value)
Base.iterate(m::Manifest) = iterate(m.deps)
Base.iterate(m::Manifest, i::Int) = iterate(m.deps, i)
Base.length(m::Manifest) = length(m.deps)
Base.empty!(m::Manifest) = empty!(m.deps)
Base.values(m::Manifest) = values(m.deps)
Base.keys(m::Manifest) = keys(m.deps)
Base.haskey(m::Manifest, key) = haskey(m.deps, key)

function Base.show(io::IO, pkg::PackageEntry)
    f = []
    pkg.name        !== nothing && push!(f, "name"      => pkg.name)
    pkg.version     !== nothing && push!(f, "version"   => pkg.version)
    pkg.tree_hash   !== nothing && push!(f, "tree_hash" => pkg.tree_hash)
    pkg.path        !== nothing && push!(f, "dev/path"  => pkg.path)
    pkg.pinned                  && push!(f, "pinned"    => pkg.pinned)
    pkg.repo.source !== nothing && push!(f, "url/path"  => "`$(pkg.repo.source)`")
    pkg.repo.rev    !== nothing && push!(f, "rev"       => pkg.repo.rev)
    pkg.repo.subdir !== nothing && push!(f, "subdir"    => pkg.repo.subdir)
    print(io, "PackageEntry(\n")
    for (field, value) in f
        print(io, "  ", field, " = ", value, "\n")
    end
    print(io, ")")
end

mutable struct EnvCache
    # environment info:
    env::Union{Nothing,String}
    # paths for files:
    project_file::String
    manifest_file::String
    # name / uuid of the project
    pkg::Union{PackageSpec, Nothing}
    # cache of metadata:
    project::Project
    manifest::Manifest
    # What these where at creation of the EnvCache
    original_project::Project
    original_manifest::Manifest
end

function EnvCache(env::Union{Nothing,String}=nothing)
    project_file = find_project_file(env)
    project_dir = dirname(project_file)
    # read project file
    project = read_project(project_file)
    # initialize project package
    if project.name !== nothing && project.uuid !== nothing
        project_package = PackageSpec(
            name = project.name,
            uuid = project.uuid,
            version = something(project.version, VersionNumber("0.0")),
            path = project_dir,
        )
    else
        project_package = nothing
    end
    # determine manifest file
    dir = abspath(project_dir)
    manifest_file = project.manifest
    manifest_file = manifest_file !== nothing ?
        abspath(manifest_file) : manifestfile_path(dir)::String
    write_env_usage(manifest_file, "manifest_usage.toml")
    manifest = read_manifest(manifest_file)

    env′ = EnvCache(env,
        project_file,
        manifest_file,
        project_package,
        project,
        manifest,
        deepcopy(project),
        deepcopy(manifest),
        )

    return env′
end

include("project.jl")
include("manifest.jl")

# ENV variables to set some of these defaults?
Base.@kwdef mutable struct Context
    env::EnvCache = EnvCache()
    io::IO = stderr_f()
    use_git_for_all_downloads::Bool = false
    use_only_tarballs_for_downloads::Bool = false
    num_concurrent_downloads::Int = 8

    # Registris
    registries::Vector{Registry.RegistryInstance} = Registry.reachable_registries()

    # The Julia Version to resolve with respect to
    julia_version::Union{VersionNumber,Nothing} = VERSION
end

project_uuid(env::EnvCache) = env.pkg === nothing ? nothing : env.pkg.uuid
collides_with_project(env::EnvCache, pkg::PackageSpec) =
    is_project_name(env, pkg.name) || is_project_uuid(env, pkg.uuid)
is_project(env::EnvCache, pkg::PackageSpec) = is_project_uuid(env, pkg.uuid)
is_project_name(env::EnvCache, name::String) =
    env.pkg !== nothing && env.pkg.name == name
is_project_name(env::EnvCache, name::Nothing) = false
is_project_uuid(env::EnvCache, uuid::UUID) = project_uuid(env) == uuid

###########
# Context #
###########

const STDLIB = Ref{Dict{UUID,String}}()
function load_stdlib()
    stdlib = Dict{UUID,String}()
    for name in readdir(stdlib_dir())
        projfile = projectfile_path(stdlib_path(name); strict=true)
        nothing === projfile && continue
        project = parse_toml(projfile)
        uuid = get(project, "uuid", nothing)
        nothing === uuid && continue
        stdlib[UUID(uuid)] = name
    end
    return stdlib
end

function stdlibs()
    if !isassigned(STDLIB)
        STDLIB[] = load_stdlib()
    end
    return STDLIB[]
end
is_stdlib(uuid::UUID) = uuid in keys(stdlibs())

# Find the entry in `STDLIBS_BY_VERSION`
# that corresponds to the requested version, and use that.
# If we can't find one, defaults to `UNREGISTERED_STDLIBS`
function get_last_stdlibs(julia_version::VersionNumber)
    last_stdlibs = UNREGISTERED_STDLIBS
    for (version, stdlibs) in STDLIBS_BY_VERSION
        if VersionNumber(julia_version.major, julia_version.minor, julia_version.patch) < version
            break
        end
        last_stdlibs = stdlibs
    end
    return last_stdlibs
end
# If `julia_version` is set to `nothing`, that means (essentially) treat all registered
# stdlibs as normal packages so that we get the latest versions of everything, ignoring
# julia compat.  So we set the list of stdlibs to that of only the unregistered stdlibs.
get_last_stdlibs(::Nothing) = UNREGISTERED_STDLIBS

# Allow asking if something is an stdlib for a particular version of Julia
function is_stdlib(uuid::UUID, julia_version::Union{VersionNumber, Nothing})
    # Only use the cache if we are asking for stdlibs in a custom Julia version
    if julia_version == VERSION
        return is_stdlib(uuid)
    end

    last_stdlibs = get_last_stdlibs(julia_version)
    # Note that if the user asks for something like `julia_version = 0.7.0`, we'll
    # fall through with an empty `last_stdlibs`, which will always return `false`.
    return uuid in keys(last_stdlibs)
end

# Return the version of a stdlib with respect to a particular Julia version, or
# `nothing` if that stdlib is not versioned.  We only store version numbers for
# stdlibs that are external and thus could be installed from their repositories,
# e.g. things like `GMP_jll`, `Tar`, etc...
function stdlib_version(uuid::UUID, julia_version::Union{VersionNumber,Nothing})
    last_stdlibs = get_last_stdlibs(julia_version)
    if !(uuid in keys(last_stdlibs))
        return nothing
    end
    return last_stdlibs[uuid][2]
end

is_unregistered_stdlib(uuid::UUID) = haskey(UNREGISTERED_STDLIBS, uuid)

Context!(kw_context::Vector{Pair{Symbol,Any}})::Context =
    Context!(Context(); kw_context...)
function Context!(ctx::Context; kwargs...)
    for (k, v) in kwargs
        setfield!(ctx, k, v)
    end
    return ctx
end

function write_env_usage(source_file::AbstractString, usage_filepath::AbstractString)
    # Don't record ghost usage
    !isfile(source_file) && return

    # Ensure that log dir exists
    !ispath(logdir()) && mkpath(logdir())

    usage_file = joinpath(logdir(), usage_filepath)
    timestamp = now()

    ## Atomically write usage file
    while true
        # read existing usage file
        usage = if isfile(usage_file)
            TOML.parsefile(usage_file)
        else
            Dict{String, Any}()
        end

        # record new usage
        usage[source_file] = [Dict("time" => timestamp)]

        # keep only latest usage info
        for k in keys(usage)
            times = map(d -> Dates.DateTime(d["time"]), usage[k])
            usage[k] = [Dict("time" => maximum(times))]
        end

        # Write to a temp file in the same directory as the destination
        temp_usage_file = tempname(logdir())
        open(temp_usage_file, "w") do io
            TOML.print(io, usage, sorted=true)
        end

        # Move the temp file into place, replacing the original
        mv(temp_usage_file, usage_file, force = true)

        # Check that the new file has what we want in it
        new_usage = if isfile(usage_file)
            TOML.parsefile(usage_file)
        else
            Dict{String, Any}()
        end
        if haskey(new_usage, source_file)
            for e in new_usage[source_file]
                if Dates.DateTime(e["time"]) >= timestamp
                    return
                end
            end
        end
        # If not, try again
    end
end

function read_package(path::String)
    project = read_project(path)
    if project.name === nothing
        pkgerror("expected a `name` entry in project file at `$(abspath(path))`")
    end
    if project.uuid === nothing
        pkgerror("expected a `uuid` entry in project file at `$(abspath(path))`")
    end
    name = project.name
    if !isfile(joinpath(dirname(path), "src", "$name.jl"))
        pkgerror("expected the file `src/$name.jl` to exist for package `$name` at `$(dirname(path))`")
    end
    return project
end

const refspecs = ["+refs/*:refs/remotes/cache/*"]

function relative_project_path(project_file::String, path::String)
    # compute path relative the project
    # realpath needed to expand symlinks before taking the relative path
    return relpath(Pkg.safe_realpath(abspath(path)),
                   Pkg.safe_realpath(dirname(project_file)))
end

function devpath(env::EnvCache, name::AbstractString, shared::Bool)
    @assert name != ""
    dev_dir = shared ? abspath(Pkg.devdir()) : joinpath(dirname(env.project_file), "dev")
    return joinpath(dev_dir, name)
end

function handle_repo_develop!(ctx::Context, pkg::PackageSpec, shared::Bool)
    # First, check if we can compute the path easily (which requires a given local path or name)
    is_local_path = pkg.repo.source !== nothing && !isurl(pkg.repo.source)
    if is_local_path || pkg.name !== nothing
        dev_path = is_local_path ? pkg.repo.source : devpath(ctx.env, pkg.name, shared)
        if pkg.repo.subdir !== nothing
            dev_path = joinpath(dev_path, pkg.repo.subdir)
        end
        # If given an explicit local path, that needs to exist
        if is_local_path && !isdir(dev_path)
            if isfile(dev_path)
                pkgerror("Dev path `$(dev_path)` is a file, but a directory is required.")
            else
                pkgerror("Dev path `$(dev_path)` does not exist.")
            end
        end
        if isdir(dev_path)
            resolve_projectfile!(ctx.env, pkg, dev_path)
            if is_local_path
                pkg.path = isabspath(dev_path) ? dev_path : relative_project_path(ctx.env.project_file, dev_path)
            else
                pkg.path = shared ? dev_path : relative_project_path(ctx.env.project_file, dev_path)
            end
            return false
        end
    end
    # If we dev by name and it is in the Project + tracking a repo in the source we can get the repo from the Manifest
    if pkg.name !== nothing && pkg.uuid === nothing
        uuid = get(ctx.env.project.deps, pkg.name, nothing)
        if uuid !== nothing
            entry = manifest_info(ctx.env.manifest, uuid)
            if entry !== nothing
                pkg.repo.source = entry.repo.source
            end
        end
    end

    # Still haven't found the source, try get it from the registry
    if pkg.repo.source === nothing
        set_repo_source_from_registry!(ctx, pkg)
    end
    @assert pkg.repo.source !== nothing

    repo_path = tempname()
    cloned = false
    package_path = pkg.repo.subdir === nothing ? repo_path : joinpath(repo_path, pkg.repo.subdir)
    if !has_name(pkg)
        LibGit2.close(GitTools.ensure_clone(ctx.io, repo_path, pkg.repo.source))
        cloned = true
        resolve_projectfile!(ctx.env, pkg, package_path)
    end
    if pkg.repo.subdir !== nothing
        repo_name = split(pkg.repo.source, '/', keepempty=false)[end]
        # Make the develop path prettier.
        if endswith(repo_name, ".git")
            repo_name = chop(repo_name, tail=4)
        end
        if endswith(repo_name, ".jl")
            repo_name = chop(repo_name, tail=3)
        end
        dev_path = devpath(ctx.env, repo_name, shared)
    else
        dev_path = devpath(ctx.env, pkg.name, shared)
    end
    if isdir(dev_path)
        println(ctx.io, "Path `$(dev_path)` exists and looks like the correct repo. Using existing path.")
        new = false
    else
        mkpath(dirname(dev_path))
        if !cloned
            LibGit2.close(GitTools.ensure_clone(ctx.io, dev_path, pkg.repo.source))
        else
            mv(repo_path, dev_path)
        end
        new = true
    end
    if !has_uuid(pkg)
        resolve_projectfile!(ctx.env, pkg, dev_path)
    end
    pkg.path = shared ? dev_path : relative_project_path(ctx.env.project_file, dev_path)
    if pkg.repo.subdir !== nothing
        pkg.path = joinpath(pkg.path, pkg.repo.subdir)
    end

    return new
end

function handle_repos_develop!(ctx::Context, pkgs::AbstractVector{PackageSpec}, shared::Bool)
    new_uuids = Set{UUID}()
    for pkg in pkgs
        new = handle_repo_develop!(ctx, pkg, shared)
        new && push!(new_uuids, pkg.uuid)
        @assert pkg.path !== nothing
        @assert has_uuid(pkg)
        pkg.repo = GitRepo() # clear repo field, no longer needed
    end
    return new_uuids
end

add_repo_cache_path(url::String) = joinpath(depots1(), "clones", string(hash(url)))

function set_repo_source_from_registry!(ctx, pkg)
    registry_resolve!(ctx.registries, pkg)
    # Didn't find the package in the registry, but maybe it exists in the updated registry
    if !isresolved(pkg)
        Pkg.Operations.update_registries(ctx; force=false)
        registry_resolve!(ctx.registries, pkg)
    end
    ensure_resolved(ctx.env.manifest, [pkg]; registry=true)
    # We might have been given a name / uuid combo that does not have an entry in the registry
    for reg in ctx.registries
        regpkg = get(reg, pkg.uuid, nothing)
        regpkg === nothing && continue
        info = Pkg.Registry.registry_info(regpkg)
        url = info.repo
        url === nothing && continue
        pkg.repo.source = url
        if info.subdir !== nothing
            pkg.repo.subdir = info.subdir
        end
        return
    end
    pkgerror("Repository for package with UUID `$(pkg.uuid)` could not be found in a registry.")
end

function handle_repo_add!(ctx::Context, pkg::PackageSpec)
    # The first goal is to populate pkg.repo.source if that wasn't given explicitly
    if pkg.repo.source === nothing
        @assert pkg.repo.rev !== nothing
        # First, we try resolving against the manifest and current registry to avoid updating registries if at all possible.
        # This also handles the case where we _only_ wish to switch the tracking branch for a package.
        manifest_resolve!(ctx.env.manifest, [pkg]; force=true)
        if isresolved(pkg)
            entry = manifest_info(ctx.env.manifest, pkg.uuid)
            if entry !== nothing && entry.repo.source !== nothing # reuse source in manifest
                pkg.repo.source = entry.repo.source
            end
        end
        if pkg.repo.source === nothing
            set_repo_source_from_registry!(ctx, pkg)
        end
    end
    @assert pkg.repo.source !== nothing

    # We now have the source of the package repo, check if it is a local path and if that exists
    repo_source = pkg.repo.source
    if !isurl(pkg.repo.source)
        if isdir(pkg.repo.source)
            if !isdir(joinpath(pkg.repo.source, ".git"))
                msg = "Did not find a git repository at `$(pkg.repo.source)`"
                if isfile(joinpath(pkg.repo.source, "Project.toml")) || isfile(joinpath(pkg.repo.source, "JuliaProject.toml"))
                    msg *= ", perhaps you meant `Pkg.develop`?"
                end
                pkgerror(msg)
            end
            LibGit2.with(GitTools.check_valid_HEAD, LibGit2.GitRepo(pkg.repo.source)) # check for valid git HEAD
            pkg.repo.source = isabspath(pkg.repo.source) ? safe_realpath(pkg.repo.source) : relative_project_path(ctx.env.project_file, pkg.repo.source)
            repo_source = normpath(joinpath(dirname(ctx.env.project_file), pkg.repo.source))
        else
            pkgerror("Path `$(pkg.repo.source)` does not exist.")
        end
    end

    let repo_source = repo_source
        # The type-assertions below are necessary presumably due to julia#36454
        LibGit2.with(GitTools.ensure_clone(ctx.io, add_repo_cache_path(repo_source::Union{Nothing,String}), repo_source::Union{Nothing,String}; isbare=true)) do repo
            repo_source_typed = repo_source::Union{Nothing,String}
            GitTools.check_valid_HEAD(repo)

            # If the user didn't specify rev, assume they want the default (master) branch if on a branch, otherwise the current commit
            if pkg.repo.rev === nothing
                pkg.repo.rev = LibGit2.isattached(repo) ? LibGit2.branch(repo) : string(LibGit2.GitHash(LibGit2.head(repo)))
            end

            obj_branch = get_object_or_branch(repo, pkg.repo.rev)
            fetched = false
            if obj_branch === nothing
                fetched = true
                GitTools.fetch(ctx.io, repo, repo_source_typed; refspecs=refspecs)
                obj_branch = get_object_or_branch(repo, pkg.repo.rev)
                if obj_branch === nothing
                    pkgerror("Did not find rev $(pkg.repo.rev) in repository")
                end
            end
            gitobject, isbranch = obj_branch

            # If we are tracking a branch and are not pinned we want to update the repo if we haven't done that yet
            innerentry = manifest_info(ctx.env.manifest, pkg.uuid)
            ispinned = innerentry !== nothing && innerentry.pinned
            if isbranch && !fetched && !ispinned
                GitTools.fetch(ctx.io, repo, repo_source_typed; refspecs=refspecs)
                gitobject, isbranch = get_object_or_branch(repo, pkg.repo.rev)
            end

            # Now we have the gitobject for our ref, time to find the tree hash for it
            tree_hash_object = LibGit2.peel(LibGit2.GitTree, gitobject)
            if pkg.repo.subdir !== nothing
                try
                    tree_hash_object = tree_hash_object[pkg.repo.subdir]
                catch e
                    e isa KeyError || rethrow()
                    pkgerror("Did not find subdirectory `$(pkg.repo.subdir)`")
                end
            end
            pkg.tree_hash = SHA1(string(LibGit2.GitHash(tree_hash_object)))

            # If we already resolved a uuid, we can bail early if this package is already installed at the current tree_hash
            if has_uuid(pkg)
                version_path = Pkg.Operations.source_path(ctx.env.project_file, pkg, ctx.julia_version)
                isdir(version_path) && return false
            end

            temp_path = mktempdir()
            GitTools.checkout_tree_to_path(repo, tree_hash_object, temp_path)
            package = resolve_projectfile!(ctx.env, pkg, temp_path)

            # Now that we are fully resolved (name, UUID, tree_hash, repo.source, repo.rev), we can finally
            # check to see if the package exists at its canonical path.
            version_path = Pkg.Operations.source_path(ctx.env.project_file, pkg, ctx.julia_version)
            isdir(version_path) && return false

            # Otherwise, move the temporary path into its correct place and set read only
            mkpath(version_path)
            mv(temp_path, version_path; force=true)
            set_readonly(version_path)
            return true
        end
    end
end

function handle_repos_add!(ctx::Context, pkgs::AbstractVector{PackageSpec})
    new_uuids = Set{UUID}()
    for pkg in pkgs
        handle_repo_add!(ctx, pkg) && push!(new_uuids, pkg.uuid)
        @assert pkg.name !== nothing && pkg.uuid !== nothing && pkg.tree_hash !== nothing
    end
    return new_uuids
end

function resolve_projectfile!(env::EnvCache, pkg, project_path)
    project_file = projectfile_path(project_path; strict=true)
    project_file === nothing && pkgerror(string("could not find project file in package at `",
                    something(pkg.repo.source, pkg.path, project_path), "` maybe `subdir` needs to be specified"))
    project_data = read_package(project_file)
    if pkg.uuid === nothing || pkg.uuid == project_data.uuid
        pkg.uuid = project_data.uuid
    else
        pkgerror("UUID `$(project_data.uuid)` given by project file `$project_file` does not match given UUID `$(pkg.uuid)`")
    end
    if pkg.name === nothing || pkg.name == project_data.name
        pkg.name = project_data.name
    else
        pkgerror("name `$(project_data.name)` given by project file `$project_file` does not match given name `$(pkg.name)`")
    end
end

get_object_or_branch(repo, rev::SHA1) =
    get_object_or_branch(repo, string(rev))

# Returns nothing if rev could not be found in repo
function get_object_or_branch(repo, rev)
    try
        gitobject = LibGit2.GitObject(repo, "remotes/cache/heads/" * rev)
        return gitobject, true
    catch err
        err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow()
    end
    try
        gitobject = LibGit2.GitObject(repo, "remotes/origin/" * rev)
        return gitobject, true
    catch err
        err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow()
    end
    try
        gitobject = LibGit2.GitObject(repo, rev)
        return gitobject, false
    catch err
        err isa LibGit2.GitError && err.code == LibGit2.Error.ENOTFOUND || rethrow()
    end
    return nothing
end

########################################
# Resolving packages from name or uuid #
########################################

function project_resolve!(env::EnvCache, pkgs::AbstractVector{PackageSpec})
    for pkg in pkgs
        if has_uuid(pkg) && !has_name(pkg) && Types.is_project_uuid(env, pkg.uuid)
            pkg.name = env.pkg.name
        end
        if has_name(pkg) && !has_uuid(pkg) && Types.is_project_name(env, pkg.name)
            pkg.uuid = env.pkg.uuid
        end
    end
end

# Disambiguate name/uuid package specifications using project info.
function project_deps_resolve!(env::EnvCache, pkgs::AbstractVector{PackageSpec})
    uuids = env.project.deps
    names = Dict(uuid => name for (name, uuid) in uuids)
    for pkg in pkgs
        if has_name(pkg) && !has_uuid(pkg) && pkg.name in keys(uuids)
            pkg.uuid = uuids[pkg.name]
        end
        if has_uuid(pkg) && !has_name(pkg) && pkg.uuid in keys(names)
            pkg.name = names[pkg.uuid]
        end
    end
end

# Disambiguate name/uuid package specifications using manifest info.
function manifest_resolve!(manifest::Manifest, pkgs::AbstractVector{PackageSpec}; force=false)
    uuids = Dict{String,Vector{UUID}}()
    names = Dict{UUID,String}()
    for (uuid, entry) in manifest
        push!(get!(uuids, entry.name, UUID[]), uuid)
        names[uuid] = entry.name # can be duplicate but doesn't matter
    end
    for pkg in pkgs
        if has_name(pkg) && !has_uuid(pkg) && pkg.name in keys(uuids)
            length(uuids[pkg.name]) == 1 && (pkg.uuid = uuids[pkg.name][1])
        end
        if has_uuid(pkg) && !has_name(pkg) && pkg.uuid in keys(names)
            pkg.name = names[pkg.uuid]
        end
    end
end

# Disambiguate name/uuid package specifications using registry info.
registry_resolve!(registries::Vector{Registry.RegistryInstance}, pkg::PackageSpec) = registry_resolve!(registries, [pkg])
function registry_resolve!(registries::Vector{Registry.RegistryInstance}, pkgs::AbstractVector{PackageSpec})
    # if there are no half-specified packages, return early
    any(pkg -> has_name(pkg) ⊻ has_uuid(pkg), pkgs) || return
    # collect all names and uuids since we're looking anyway
    names = [pkg.name::String for pkg in pkgs if has_name(pkg)]
    uuids = [pkg.uuid::UUID for pkg in pkgs if has_uuid(pkg)]
    for pkg in pkgs
        @assert has_name(pkg) || has_uuid(pkg)
        if has_name(pkg) && !has_uuid(pkg)
            pkg.uuid = registered_uuid(registries, pkg.name)
        end
        if has_uuid(pkg) && !has_name(pkg)
            pkg.name = registered_name(registries, pkg.uuid)
        end
    end
    return pkgs
end

function stdlib_resolve!(pkgs::AbstractVector{PackageSpec})
    for pkg in pkgs
        @assert has_name(pkg) || has_uuid(pkg)
        if has_name(pkg) && !has_uuid(pkg)
            for (uuid, name) in stdlibs()
                name == pkg.name && (pkg.uuid = uuid)
            end
        end
        if !has_name(pkg) && has_uuid(pkg)
            name = get(stdlibs(), pkg.uuid, nothing)
            nothing !== name && (pkg.name = name)
        end
    end
end

# Ensure that all packages are fully resolved
function ensure_resolved(manifest::Manifest,
        pkgs::AbstractVector{PackageSpec};
        registry::Bool=false,)::Nothing
    unresolved_uuids = Dict{String,Vector{UUID}}()
    for pkg in pkgs
        has_uuid(pkg) && continue
        uuids = [uuid for (uuid, entry) in manifest if entry.name == pkg.name]
        sort!(uuids, by=uuid -> uuid.value)
        unresolved_uuids[pkg.name] = uuids
    end
    unresolved_names = UUID[]
    for pkg in pkgs
        has_name(pkg) && continue
        push!(unresolved_names, pkg.uuid)
    end
    isempty(unresolved_uuids) && isempty(unresolved_names) && return
    msg = sprint() do io
        if !isempty(unresolved_uuids)
            println(io, "The following package names could not be resolved:")
            for (name, uuids) in sort!(collect(unresolved_uuids), by=lowercase ∘ first)
                print(io, " * $name (")
                if length(uuids) == 0
                    what = ["project", "manifest"]
                    registry && push!(what, "registry")
                    print(io, "not found in ")
                    join(io, what, ", ", " or ")
                else
                    join(io, uuids, ", ", " or ")
                    print(io, " in manifest but not in project")
                end
                println(io, ")")
            end
        end
        if !isempty(unresolved_names)
            println(io, "The following package uuids could not be resolved:")
            for uuid in unresolved_names
                println(io, " * $uuid")
            end
        end
    end
    pkgerror(msg)
end

function registered_uuids(registries::Vector{Registry.RegistryInstance}, name::String)
    uuids = Set{UUID}()
    for reg in registries
        union!(uuids, Registry.uuids_from_name(reg, name))
    end
    return uuids
end
# Determine a single UUID for a given name, prompting if needed
function registered_uuid(registries::Vector{Registry.RegistryInstance}, name::String)::Union{Nothing,UUID}
    uuids = registered_uuids(registries, name)
    length(uuids) == 0 && return nothing
    length(uuids) == 1 && return first(uuids)
    repo_infos = Tuple{String, String, UUID}[]
    for uuid in uuids
        for reg in registries
            pkg = get(reg, uuid, nothing)
            pkg === nothing && continue
            info = Pkg.Registry.registry_info(pkg)
            info.repo === nothing && continue
            push!(repo_infos, (reg.name, info.repo, uuid))
        end
    end
    unique!(repo_infos)
    if isinteractive()
        # prompt for which UUID was intended:
        menu = RadioMenu(String["Registry: $(value[1]) - Repo: $(value[2]) - UUID: $(value[3])" for value in repo_infos])
        choice = request("There are multiple registered `$name` packages, choose one:", menu)
        choice == -1 && return nothing
        return repo_infos[choice][3]
    else
        pkgerror("there are multiple registered `$name` packages, explicitly set the uuid")
    end
end

# Determine current name for a given package UUID

function registered_name(registries::Vector{Registry.RegistryInstance}, uuid::UUID)::Union{Nothing,String}
    name = nothing
    for reg in registries
        regpkg = get(reg, uuid, nothing)
        regpkg === nothing && continue
        name′ = regpkg.name
        if name !== nothing
            name′ == name || pkgerror("package `$uuid` has multiple registered name values: $name, $name′")
        end
        name = name′
    end
    return name
end

# Find package by UUID in the manifest file
manifest_info(::Manifest, uuid::Nothing) = nothing
function manifest_info(manifest::Manifest, uuid::UUID)::Union{PackageEntry,Nothing}
    return get(manifest, uuid, nothing)
end
function write_env(env::EnvCache; update_undo=true,
                   skip_writing_project::Bool=false)
    if (env.project != env.original_project) && (!skip_writing_project)
        write_project(env)
    end
    if env.manifest != env.original_manifest
        write_manifest(env)
    end
    update_undo && Pkg.API.add_snapshot_to_undo(env)
end

end # module


text:
################
# NShaskey
################
struct NShaskey{T <: AbstNS} <: Function ___NS_ns::T end

Base.getproperty(x::NShaskey, atr::Symbol) =
    hasfield(typeof(x), atr) ? Base.getfield(x, atr) : (atr ∈ x.___NS_ns._keys)

(x::NShaskey)(atr::Symbol) = atr ∈ x.___NS_ns._keys
(x::NShaskey)(atr::Symbol...) =
    (keys = x.___NS_ns._keys; collect(a ∈ keys for a ∈ atr))

################
# NSdel
################

struct NSdel{T <: AbstNS} <: Function ___NS_ns::T end

Base.getproperty(x::NSdel, atr::Symbol) =
    begin
        hasfield(typeof(x), atr) && (return Base.getfield(x, atr))

        x.___NS_ns._fixed && error("this NS is fixed!")
        x.___NS_ns._lcked && error("this NS is locked!")

        delete!(x.___NS_ns.__dict, atr)
        x.___NS_ns
    end

(x::NSdel)(atr::Symbol...) =
    begin
        if length(atr) == 0
            x.___NS_ns._fixed && error("this NS is fixed!")
            x.___NS_ns._lcked && error("this NS is locked!")
            empty!(x.___NS_ns.__dict)
        else
            for a ∈ atr; Base.getproperty(x, a); end
        end
        x.___NS_ns
    end

################
# NScstize, NSdecstize
################

struct NScstize{T <: AbstNS} <: Function ___NS_ns::T end
struct NSdecstize{T <: AbstNS} <: Function ___NS_ns::T end

Base.getproperty(x::NScstize, atr::Symbol) =
    begin
        hasfield(typeof(x), atr) && (return Base.getfield(x, atr))

        x.___NS_ns._fixed && error("this NS is fixed!")

        d = x.___NS_ns.__dict

        haskey(d, atr) ||
            error("""this NS does not have a property named '$(atr)'.""")

        isa(d[atr], NSnoncst_item) && (d[atr] = NScst_item(d[atr].obj))

        x.___NS_ns
    end

Base.getproperty(x::NSdecstize, atr::Symbol) =
    begin
        hasfield(typeof(x), atr) && (return Base.getfield(x, atr))

        x.___NS_ns._fixed && error("this NS is fixed!")

        d = x.___NS_ns.__dict

        haskey(d, atr) ||
            error("""this NS does not have a property named '$(atr)'.""")

        isa(x.___NS_ns.__dict[atr], NScst_item) &&
            (x.___NS_ns.__dict[atr] = NSnoncst_item(x.___NS_ns.__dict[atr].obj))

        x.___NS_ns
    end

(x::Union{NScstize, NSdecstize})(atr::Symbol...) =
    begin
        length(atr) == 0 && (return x(x.___NS_ns._keys...))

        for a ∈ atr; Base.getproperty(x, a); end
        x.___NS_ns
    end

################
# NSTag
################

struct NSTag{T, CST} ___NS_ns::AbstNS end

Base.getproperty(x::NSTag{TAG}, atr::Symbol) where TAG =
    begin
        Base.hasfield(typeof(x), atr) && (return Base.getfield(x, atr))

        ns = x.___NS_ns

        !ns.haskey(atr) &&
            error("""this NS does not have a property named '$(atr)'.""")

        o = ns.__dict[atr].obj
        to = typeof(o)
        to <: NSTagFunc && to.parameters[1] == TAG && (return o.fnc)

        error("""'$(atr)' is not a $(string(typeof(x).parameters[1])).""")
    end

Base.setproperty!(x::NSTag{TAG, TF}, atr::Symbol, f) where TAG where TF =
    begin
        Base.hasfield(typeof(x), atr) && (Base.setproperty!(x, atr, f); return)

        Base.setproperty!(x.___NS_ns, atr,
                          (TF ? NScst_item : NSnoncst_item)(NSTagFunc{TAG}(f)))
        #=
        Base.setproperty!(x.___NS_ns, atr,
                          typeof(x.___NS_ns) == __NSX_CodeMode
                          ? (TF ? NScst_item : NSnoncst_item)(NSTagFunc{TAG}(f))
                          : _MakeItem(x, f))
        =#
    end

Base.setproperty!(x::NSTag{:prp, TF}, atr::Symbol, f) where TF =
    begin
        Base.hasfield(typeof(x), atr) && (Base.setproperty!(x, atr, f); return)

        typeof(x.___NS_ns) != __NSX_CodeMode && x.___NS_ns.haskey(atr) &&
            x.___NS_ns.del(atr)
        Base.setproperty!(x.___NS_ns, atr,
                          (TF ? NScst_item : NSnoncst_item)(NSTagFunc{:prp}(f)))

        #=
        if typeof(x.___NS_ns) == __NSX_CodeMode
            Base.setproperty!(x.___NS_ns, atr,
                              (TF ? NScst_item : NSnoncst_item)(NSTagFunc{:prp}(f)))
        else
            x.___NS_ns.haskey(atr) && x.___NS_ns.del(atr)
            Base.setproperty!(x.___NS_ns, atr, _MakeItem(x, f))
        end
        =#
    end

################
# NScst
################

struct NScst{T <: AbstNS} ___NS_ns::T end

Base.getproperty(x::NScst, atr::Symbol) =
    (Base.hasfield(typeof(x), atr)
     ? Base.getfield(x, atr)
     : NSTag{atr, true}(x.___NS_ns))

Base.setproperty!(x::NScst, atr::Symbol, o) =
        (Base.hasfield(typeof(x), atr)
         ? Base.setproperty!(x, atr, f)
         : Base.setproperty!(x.___NS_ns, atr, NScst_item(o)))
#=
_MakeItem(x::NSTag{T, false}, f) where T = NSnoncst_item(NSTagFunc{T}(f))
_MakeItem(x::NSTag{T, true},  f) where T = NScst_item(NSTagFunc{T}(f))
_MakeItem(x::NSTag{:dfn, false}, f) = NSnoncst_item(f(x.___NS_ns))
_MakeItem(x::NSTag{:dfn, true},  f) = NScst_item(f(x.___NS_ns))
=#

###############################
# NSTagFunc
###############################

struct NSTagFunc{T} <: Function fnc end

NSDfn = NSTagFunc{:dfn}
NSReq = NSTagFunc{:req}
NSMth = NSTagFunc{:mth}
NSFnc = NSTagFunc{:fnc}
NSPrp = NSTagFunc{:prp}

(f::NSTagFunc)(a...; ka...) = f.fnc(a...; ka...)
(mth::NSMth)(self) = (a...; ka...)->mth.fnc(self, a...; ka...)
# (dfn::NSDfn)(self) = dfn.fnc(self)
# (req::NSReq)(self) = req.fnc(self)
# (mth::NSMth)(a...; ka...) = mth.fnc(a...; ka...)
# (fnc::NSFnc)(a...; ka...) = fnc.fnc(a...; ka...)
# (prp::NSPrp)(a...) = prp.fnc(a...)

macro nsdfn(ex) esc(Expr(:call, :(Wild.NSDfn), ex)) end
macro nsprp(ex) esc(Expr(:call, :(Wild.NSPrp), ex)) end
macro nsreq(ex) esc(Expr(:call, :(Wild.NSReq), ex)) end
macro nsmth(ex) esc(Expr(:call, :(Wild.NSMth), ex)) end
macro nsfnc(ex) esc(Expr(:call, :(Wild.NSFnc), ex)) end
macro nscst(ex) esc(Expr(:call, :(Wild.NScst_item), ex)) end


text:
export rmkey, update, mergef

"""
Remove a `key` from named tuple `nt` has been removed

```jldoctest
x = (a = 3, b = 4, c =12)
rmkey(tag, Val{:c})
(a = 3, b = 4)
```
"""
@generated function rmkey(nt::NamedTuple{K, V}, key::Type{Val{T}}) where {K, V, T}
  T isa Symbol || throw(ArgumentError("Usage: `rmkey(x, Val{:X})`"))
  # args = [:($k = nt.$k) for k in K if k != T]
  ks = tuple((k for k in K if k != T)...)
  vs = [:(nt.$k) for k in K if k != T]
  # FIXME: This can be made more efficient, using TAGS
  # Expr(:tuple, args...)
  :(NamedTuple{$ks}(($(vs...),)))
end

@pre rmkey(nt, key) = key in keys(nt) "Key `key` not in `nt`"
@post rmkey(nt, key) = keys(__ret__) == setdiff(keys(nt), key) "Keys of returned nt same as nt minus key"
@post rmkey(nt, key) = all([__ret__[k] == nt[k] for k in __ret__]) "Values of returned nt same as nt minus key"

"""
Update a named tuple

```
update((x = 3, y = 2, z = 1), Val{:x}, 7)
(x = 7, y = 2, z = 1)
```

"""
@generated function update(nt::NamedTuple{K, V}, key::Type{Val{T}}, val) where {K, V, T}
  args = []
  for k in K
    if k == T
      push!(args, :($k = val))
    else
      push!(args, :($k = nt.$k))
    end
  end
  # FIXME, return named tuple
  Expr(:tuple, args...)
end
@post update(nt, key) = keys(__ret__) == keys(nt) "Keys of returned nt same as nt"
@post update(nt, key) = all([__ret__[k] == nt[k] for k in setdiff(keys(nt), key)])
@post update(nt, key) = __ret__[key] == val "Value of key updated in result"

"""
Merge `nt1` with `nt2`.
For each key :key_i which clashes (is in both `n1` and `n2`), returns
`f(:Val{:key_i}, nt1.key_i, nt2.key_i)`

```
nt1 = (x = 2, y = 4)
nt2 = (x = 4, a = 2)
mergef(f, nt1, nt2)
```
"""
@generated function mergef(f, nt1::NamedTuple{K1, V1}, nt2::NamedTuple{K2, V2}) where {K1, K2, V1, V2}
  if isempty(K1 ∩ K2)
    :(merge(nt1, nt2))
  else
    :(f(nt1, nt2))
  end
end

@post mergef(f, nt1, nt2) = keys(__ret__) == keys(nt1) ∪ keys(nt2)
@post mergef(f, nt1, nt2) = all((__ret__[k] == nt1[k] for k in keys if k in nt1 && k ∉ nt2))
@post mergef(f, nt1, nt2) = all((__ret__[k] == nt2[k] for k in keys if k in nt2 && k ∉ nt1))
@post mergef(f, nt1, nt2) = all((__ret__[k] == f(nt1[k], nt2[k]) for k in keys if k in nt1 && k ∉ nt2))

max_stars_repo_name: zenna/expect

max_issues_repo_name: zenna/expect

max_forks_repo_name: zenna/expect


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

mutable struct VirtualMachineScaleSetUpdateVMProfile <: SwaggerModel
    osProfile::Any # spec type: Union{ Nothing, VirtualMachineScaleSetUpdateOSProfile } # spec name: osProfile
    storageProfile::Any # spec type: Union{ Nothing, VirtualMachineScaleSetUpdateStorageProfile } # spec name: storageProfile
    networkProfile::Any # spec type: Union{ Nothing, VirtualMachineScaleSetUpdateNetworkProfile } # spec name: networkProfile
    securityProfile::Any # spec type: Union{ Nothing, SecurityProfile } # spec name: securityProfile
    diagnosticsProfile::Any # spec type: Union{ Nothing, DiagnosticsProfile } # spec name: diagnosticsProfile
    extensionProfile::Any # spec type: Union{ Nothing, VirtualMachineScaleSetExtensionProfile } # spec name: extensionProfile
    licenseType::Any # spec type: Union{ Nothing, String } # spec name: licenseType
    billingProfile::Any # spec type: Union{ Nothing, BillingProfile } # spec name: billingProfile
    scheduledEventsProfile::Any # spec type: Union{ Nothing, ScheduledEventsProfile } # spec name: scheduledEventsProfile

    function VirtualMachineScaleSetUpdateVMProfile(;osProfile=nothing, storageProfile=nothing, networkProfile=nothing, securityProfile=nothing, diagnosticsProfile=nothing, extensionProfile=nothing, licenseType=nothing, billingProfile=nothing, scheduledEventsProfile=nothing)
        o = new()
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("osProfile"), osProfile)
        setfield!(o, Symbol("osProfile"), osProfile)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("storageProfile"), storageProfile)
        setfield!(o, Symbol("storageProfile"), storageProfile)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("networkProfile"), networkProfile)
        setfield!(o, Symbol("networkProfile"), networkProfile)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("securityProfile"), securityProfile)
        setfield!(o, Symbol("securityProfile"), securityProfile)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("diagnosticsProfile"), diagnosticsProfile)
        setfield!(o, Symbol("diagnosticsProfile"), diagnosticsProfile)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("extensionProfile"), extensionProfile)
        setfield!(o, Symbol("extensionProfile"), extensionProfile)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("licenseType"), licenseType)
        setfield!(o, Symbol("licenseType"), licenseType)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("billingProfile"), billingProfile)
        setfield!(o, Symbol("billingProfile"), billingProfile)
        validate_property(VirtualMachineScaleSetUpdateVMProfile, Symbol("scheduledEventsProfile"), scheduledEventsProfile)
        setfield!(o, Symbol("scheduledEventsProfile"), scheduledEventsProfile)
        o
    end
end # type VirtualMachineScaleSetUpdateVMProfile

const _property_map_VirtualMachineScaleSetUpdateVMProfile = Dict{Symbol,Symbol}(Symbol("osProfile")=>Symbol("osProfile"), Symbol("storageProfile")=>Symbol("storageProfile"), Symbol("networkProfile")=>Symbol("networkProfile"), Symbol("securityProfile")=>Symbol("securityProfile"), Symbol("diagnosticsProfile")=>Symbol("diagnosticsProfile"), Symbol("extensionProfile")=>Symbol("extensionProfile"), Symbol("licenseType")=>Symbol("licenseType"), Symbol("billingProfile")=>Symbol("billingProfile"), Symbol("scheduledEventsProfile")=>Symbol("scheduledEventsProfile"))
const _property_types_VirtualMachineScaleSetUpdateVMProfile = Dict{Symbol,String}(Symbol("osProfile")=>"VirtualMachineScaleSetUpdateOSProfile", Symbol("storageProfile")=>"VirtualMachineScaleSetUpdateStorageProfile", Symbol("networkProfile")=>"VirtualMachineScaleSetUpdateNetworkProfile", Symbol("securityProfile")=>"SecurityProfile", Symbol("diagnosticsProfile")=>"DiagnosticsProfile", Symbol("extensionProfile")=>"VirtualMachineScaleSetExtensionProfile", Symbol("licenseType")=>"String", Symbol("billingProfile")=>"BillingProfile", Symbol("scheduledEventsProfile")=>"ScheduledEventsProfile")
Base.propertynames(::Type{ VirtualMachineScaleSetUpdateVMProfile }) = collect(keys(_property_map_VirtualMachineScaleSetUpdateVMProfile))
Swagger.property_type(::Type{ VirtualMachineScaleSetUpdateVMProfile }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_VirtualMachineScaleSetUpdateVMProfile[name]))}
Swagger.field_name(::Type{ VirtualMachineScaleSetUpdateVMProfile }, property_name::Symbol) =  _property_map_VirtualMachineScaleSetUpdateVMProfile[property_name]

function check_required(o::VirtualMachineScaleSetUpdateVMProfile)
    true
end

function validate_property(::Type{ VirtualMachineScaleSetUpdateVMProfile }, name::Symbol, val)
end


text:
# ----------------- #
# Utility functions #
# ----------------- #

"Tells maximum number of arguments for a generic or anonymous function"
function max_args(f::Function)
    if isgeneric(f)
        return methods(f).max_args
    else
        # anonymous function
        # NOTE: This might be quite fragile, but works on 0.3.6 and 0.4-dev
        return length(Base.uncompressed_ast(f.code).args[1])
    end
end

"""
Inplace version of `row_kron`. Sets the `i`th row of `out` equal to
`kron(A[i, :], B[i, :])`
"""
function row_kron!(A::AbstractMatrix, B::AbstractMatrix, out::AbstractMatrix)
    # get input dimensions
    nobsa, na = size(A)
    nobsb, nb = size(B)

    nobsa != nobsb && error("A and B must have same number of rows")

    # fill in each element. To do this we make sure we access each array
    # consistent with its column major memory layout.
    @inbounds for ia=1:na, ib=1:nb, t=1:nobsa
        out[t, nb*(ia-1) + ib] = A[t, ia] * B[t, ib]
    end
    out
end

"""
Compute the row-wise Kronecker product between two matrices `A` and `B`.
The matrices are assumed to be of size `(r, ca)` and `(r, cb)`,
respectively. The output matrix will be `(r, ca*cb)`

The `i`th row of the output matrix will be equal to
`kron(A[i,:], B[i,:])`
"""
:row_kron

function row_kron(A::AbstractMatrix{S}, B::AbstractMatrix{T}) where {S,T}
    nobsa, na = size(A)
    nobsb, nb = size(B)
    out = Array(promote_type(S, T), nobsa, na*nb)
    row_kron!(A, B, out)
    out
end

# specialized version for sparse matrices
function row_kron(A::SparseMatrixCSC{S}, B::SparseMatrixCSC{T}) where {S,T}
    nobsa, na = size(A)
    nobsb, nb = size(B)
    out = spzeros(promote_type(S, T), nobsa, na*nb)
    row_kron!(A, B, out)
    out
end

function gettril(x::Array{Float64, 2})
  n, m = size(x)
  a = Array{eltype(x)}(undef, convert(Int, n.*(n+1)/2))
  k::Int = 1
  @inbounds for i = 1:n
    for j = 1:i
      a[k] = x[i, j]
      k += 1
    end
  end
  return a
end


text:
OP, python, libpython = ARGS

# Special exit codes for this script.
# (see: https://www.tldp.org/LDP/abs/html/exitcodes.html)
code_no_precompile_needed = 113

if VERSION < v"0.7.0"
    error("Unsupported Julia version $VERSION")
end

using Pkg
using InteractiveUtils

@info "Julia version info"
versioninfo(verbose=true)

@info "Julia executable: $(Base.julia_cmd().exec[1])"

pkgid = Base.PkgId(Base.UUID(0x438e738f_606a_5dbb_bf0a_cddfbfd45ab0), "PyCall")
pycall_is_installed = Base.locate_package(pkgid) !== nothing

@info "Trying to import PyCall..."

module DummyPyCall
python = nothing
libpython = nothing
end

try
    # `import PyCall` cannot be caught?
    global PyCall = Base.require(Main, :PyCall)
catch err
    @error "`import PyCall` failed" exception=(err, catch_backtrace())
    global PyCall = DummyPyCall
end

ENV["PYTHON"] = python

# TODO: warn if some relevant JULIA_* environment variables are set
# TODO: use PackageSpec to specify PyCall's UUID

print_logfile = true

function build_pycall()
    if VERSION >= v"1.1.0-rc1"
        @info """Run `Pkg.build("PyCall"; verbose=true)`"""
        Pkg.build("PyCall"; verbose=true)
        global print_logfile = false
    else
        @info """Run `Pkg.build("PyCall")`"""
        Pkg.build("PyCall")
    end
end

if OP == "build"
    build_pycall()
elseif PyCall.python == python || PyCall.libpython == libpython
    @info """
    PyCall is already installed and compatible with Python executable.

    PyCall:
        python: $(PyCall.python)
        libpython: $(PyCall.libpython)
    Python:
        python: $python
        libpython: $libpython
    """
    exit(code_no_precompile_needed)
else
    if PyCall.python !== nothing
        if isempty(libpython)
            @warn """
            PyCall is already installed.  However, you may have trouble using
            this Python executable because it is statically linked to libpython.

            For more information, see:
                https://pyjulia.readthedocs.io/en/latest/troubleshooting.html

            Python executable:
                $python
            Julia executable:
                $(Base.julia_cmd().exec[1])
            """
            exit(code_no_precompile_needed)
        else
            @info """
            PyCall is already installed but not compatible with this Python
            executable.  Re-building PyCall...
            """
            build_pycall()
        end
    elseif pycall_is_installed
        @info """
        PyCall is already installed but importing it failed.
        Re-building PyCall may fix the issue...
        """
        build_pycall()
    else
        @info "Installing PyCall..."
        Pkg.add("PyCall")
    end
end

modpath = Base.locate_package(pkgid)
pkgdir = joinpath(dirname(modpath), "..")

if print_logfile
    logfile = joinpath(pkgdir, "deps", "build.log")
    if isfile(logfile)
        @info "Build log in $logfile"
        print(stderr, read(logfile, String))
    end
end

max_stars_repo_name: grahamgill/pyjulia

max_issues_repo_name: grahamgill/pyjulia

max_forks_repo_name: grahamgill/pyjulia


text:
"""
    get_values(gradient)

Get q-values and b-values
"""
function get_values(gradient)
    nsequence = length(gradient.sequences)
    if gradient.values_type == "q"
        qvalues = repeat(gradient.values, 1, nsequence)
        bvalues =
            gradient.values .^ 2 .* bvalue_no_q.(gradient.sequences)'
    else
        bvalues = repeat(gradient.values, 1, nsequence)
        qvalues = .√(gradient.values ./ bvalue_no_q.(gradient.sequences)')
    end

    qvalues, bvalues
end


text:
import gmsh

gmsh.initialize()

gmsh.model.add("t1")

lc = 1e-2
gmsh.model.geo.addPoint(0, 0, 0, lc, 1)

gmsh.model.geo.addPoint(.1, 0,  0, lc, 2)
gmsh.model.geo.addPoint(.1, .3, 0, lc, 3)

p4 = gmsh.model.geo.addPoint(0, .3, 0, lc)

gmsh.model.geo.addLine(1, 2, 1)
gmsh.model.geo.addLine(3, 2, 2)
gmsh.model.geo.addLine(3, p4, 3)
gmsh.model.geo.addLine(4, 1, p4)

gmsh.model.geo.addCurveLoop([4, 1, -2, 3], 1)

gmsh.model.geo.addPlaneSurface([1], 1)

gmsh.model.geo.synchronize()

gmsh.model.addPhysicalGroup(0, [1, 2], 1)
gmsh.model.addPhysicalGroup(1, [1, 2], 2)
gmsh.model.addPhysicalGroup(2, [1], 6)

gmsh.model.setPhysicalName(2, 6, "My surface")

gmsh.model.mesh.generate(2)

gmsh.write("t1.msh")

gmsh.finalize()

# This file was generated using Literate.jl, https://github.com/fredrikekre/Literate.jl


text:
"""
    download(
        npd :: NASAPrecipitationDataset,
        geo :: GeoRegion = GeoRegion("GLB")
    ) -> nothing

Downloads a NASA Precipitation dataset specified by `npd` for a GeoRegion specified by `geo`

Arguments
=========
- `npd` : a `NASAPrecipitationDataset` specifying the dataset details and date download range
- `geo` : a `GeoRegion` (see [GeoRegions.jl](https://github.com/JuliaClimate/GeoRegions.jl)) that sets the geographic bounds of the data array in lon-lat
"""
function download(
	npd :: IMERGHalfHourly{ST,DT},
	geo :: GeoRegion = GeoRegion("GLB")
) where {ST<:AbstractString, DT<:TimeType}

	@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from $(npd.dtbeg) to $(npd.dtend)"

	if geo.regID == "GLB"
		@info "$(modulelog()) - Global dataset request has been detected, switching to the IMERG GeoRegion"
		addNPDGeoRegions(); geo = GeoRegion("IMERG")
	else
		isinGeoRegion(geo,GeoRegion("IMERG"))
	end

	fnc  = imergrawfiles()
	lon,lat = gpmlonlat(); nlon = length(lon); nlat = length(lat)
	ginfo = RegionGrid(geo,lon,lat)

	@info "$(modulelog()) - Preallocating temporary arrays for extraction of $(npd.lname) data for the $(geo.name) GeoRegion from the original gridded dataset"
	glon = ginfo.glon; nglon = length(glon); iglon = ginfo.ilon
	glat = ginfo.glat; nglat = length(glat); iglat = ginfo.ilat
	tmp0 = zeros(Float32,nglat,nglon)
	var  = zeros(Float32,nglon,nglat,48)

	if typeof(geo) <: PolyRegion
		  msk = ginfo.mask
	else; msk = ones(nglon,nglat)
	end

	if iglon[1] > iglon[end]
		shift = true
		iglon1 = iglon[1] : nlon; niglon1 = length(iglon1)
		iglon2 = 1 : iglon[end];  niglon2 = length(iglon2)
		tmp1 = @view tmp0[:,1:niglon1]
		tmp2 = @view tmp0[:,(niglon1+1):niglon2]
	else
		shift = false
		iglon = iglon[1] : iglon[end]
	end

	if iglat[1] > iglat[end]
		iglat = iglat[1] : -1 : iglat[end]
	else
		iglat = iglat[1] : iglat[end]
	end

	for dt in npd.dtbeg : Day(1) : npd.dtend

		@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from the NASA Earthdata servers using OPeNDAP protocols for $(dt) ..."

		ymdfnc = Dates.format(dt,dateformat"yyyymmdd")
		npddir = joinpath(npd.hroot,"$(year(dt))",@sprintf("%03d",dayofyear(dt)))
		for it = 1 : 48

			@debug "$(modulelog()) - Loading data into temporary array for timestep $(fnc[it])"

			npdfnc = "$(npd.fpref).$ymdfnc-$(fnc[it]).$(npd.fsuff)"
			ds = NCDataset(joinpath(npddir,npdfnc))
			if !shift
				NCDatasets.load!(ds["precipitationCal"].var,tmp0,iglat,iglon,1)
			else
				NCDatasets.load!(ds["precipitationCal"].var,tmp1,iglat,iglon1,1)
				NCDatasets.load!(ds["precipitationCal"].var,tmp2,iglat,iglon2,1)
			end
			close(ds)

			@debug "$(modulelog()) - Extraction of data from temporary array for the $(geo.name) GeoRegion"
			for ilat = 1 : nglat, ilon = 1 : nglon
				varii = tmp0[ilat,ilon]
				mskii = msk[ilon,ilat]
				if (varii != -9999.9f0) && !isnan(mskii)
					  var[ilon,ilat,it] = varii / 3600
				else; var[ilon,ilat,it] = NaN32
				end
			end
		end

		save(var,dt,npd,geo,ginfo)

	end

end

function download(
	npd :: IMERGDaily{ST,DT},
	geo :: GeoRegion = GeoRegion("GLB")
) where {ST<:AbstractString, DT<:TimeType}

	@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from $(npd.dtbeg) to $(npd.dtend)"

	if geo.regID == "GLB"
		@info "$(modulelog()) - Global dataset request has been detected, switching to the IMERG GeoRegion"
		addNPDGeoRegions(); geo = GeoRegion("IMERG")
	else
		isinGeoRegion(geo,GeoRegion("IMERG"))
	end

	lon,lat = gpmlonlat(); nlon = length(lon); nlat = length(lat)
	ginfo = RegionGrid(geo,lon,lat)

	@info "$(modulelog()) - Preallocating temporary arrays for extraction of $(npd.lname) data for the $(geo.name) GeoRegion from the original gridded dataset"
	glon = ginfo.glon; nglon = length(glon); iglon = ginfo.ilon
	glat = ginfo.glat; nglat = length(glat); iglat = ginfo.ilat
	tmp0 = zeros(Float32,nglat,nglon)
	var  = zeros(Float32,nglon,nglat,31)

	if typeof(geo) <: PolyRegion
		  msk = ginfo.mask
	else; msk = ones(nglon,nglat)
	end

	if iglon[1] > iglon[end]
		shift = true
		iglon1 = iglon[1] : nlon; niglon1 = length(iglon1)
		iglon2 = 1 : iglon[end];  niglon2 = length(iglon2)
		tmp1 = @view tmp0[:,1:niglon1]
		tmp2 = @view tmp0[:,(niglon1+1):niglon2]
	else
		shift = false
		iglon = iglon[1] : iglon[end]
	end

	if iglat[1] > iglat[end]
		iglat = iglat[1] : -1 : iglat[end]
	else
		iglat = iglat[1] : iglat[end]
	end

	for dt in npd.dtbeg : Month(1) : npd.dtend

		@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from the NASA Earthdata servers using OPeNDAP protocols for $(year(dt))-$(month(dt)) ..."

		npddir = joinpath(npd.hroot,"$(yrmo2dir(dt))")
		ndy = daysinmonth(dt)

		for dy in 1 : ndy
			dtii   = Date(year(dt),month(dt),dy)
			ymdfnc = "$(ymd2str(dtii))"
			npdfnc = "$(npd.fpref).$ymdfnc-$(npd.fsuff)"
			ds = NCDataset(joinpath(npddir,npdfnc))
			if !shift
				NCDatasets.load!(ds["precipitationCal"].var,tmp0,iglat,iglon,1)
			else
				NCDatasets.load!(ds["precipitationCal"].var,tmp1,iglat,iglon1,1)
				NCDatasets.load!(ds["precipitationCal"].var,tmp2,iglat,iglon2,1)
			end
			close(ds)

			@debug "$(modulelog()) - Extraction of data from temporary array for the $(geo.name) GeoRegion"

			for ilat = 1 : nglat, ilon = 1 : nglon
				varii = tmp0[ilat,ilon]
				mskii = msk[ilon,ilat]
				if (varii != -9999.9f0) && !isnan(mskii)
					  var[ilon,ilat,dy] = varii / 86400
				else; var[ilon,ilat,dy] = NaN32
				end
			end

		end

		save(view(var,:,:,1:ndy),dt,npd,geo,ginfo)

	end

end

function download(
	npd :: IMERGMonthly{ST,DT},
	geo :: GeoRegion = GeoRegion("GLB")
) where {ST<:AbstractString, DT<:TimeType}

	@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from $(npd.dtbeg) to $(npd.dtend)"

	if geo.regID == "GLB"
		@info "$(modulelog()) - Global dataset request has been detected, switching to the IMERG GeoRegion"
		addNPDGeoRegions(); geo = GeoRegion("IMERG")
	else
		isinGeoRegion(geo,GeoRegion("IMERG"))
	end

	lon,lat = gpmlonlat(); nlon = length(lon); nlat = length(lat)
	ginfo = RegionGrid(geo,lon,lat)

	@info "$(modulelog()) - Preallocating temporary arrays for extraction of $(npd.lname) data for the $(geo.name) GeoRegion from the original gridded dataset"
	glon = ginfo.glon; nglon = length(glon); iglon = ginfo.ilon
	glat = ginfo.glat; nglat = length(glat); iglat = ginfo.ilat
	tmp0 = zeros(Float32,nglat,nglon)
	var  = zeros(Float32,nglon,nglat,12)

	if typeof(geo) <: PolyRegion
		  msk = ginfo.mask
	else; msk = ones(nglon,nglat)
	end

	if iglon[1] > iglon[end]
		shift = true
		iglon1 = iglon[1] : nlon; niglon1 = length(iglon1)
		iglon2 = 1 : iglon[end];  niglon2 = length(iglon2)
		tmp1 = @view tmp0[:,1:niglon1]
		tmp2 = @view tmp0[:,(niglon1+1):niglon2]
	else
		shift = false
		iglon = iglon[1] : iglon[end]
	end

	if iglat[1] > iglat[end]
		iglat = iglat[1] : -1 : iglat[end]
	else
		iglat = iglat[1] : iglat[end]
	end

	for dt in npd.dtbeg : Year(1) : npd.dtend

		@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from the NASA Earthdata servers using OPeNDAP protocols for $(year(dt)) ..."

		npddir = joinpath(npd.hroot,"$(year(dt))")

		for mo in 1 : 12
			dtii   = Date(year(dt),mo,1)
			ymdfnc = "$(ymd2str(dtii))-S000000-E235959"
			npdfnc = "$(npd.fpref).$ymdfnc.$(@sprintf("%02d",mo)).$(npd.fsuff)"
			ds = NCDataset(joinpath(npddir,npdfnc))
			if !shift
				NCDatasets.load!(ds["precipitation"].var,tmp0,iglat,iglon,1)
			else
				NCDatasets.load!(ds["precipitation"].var,tmp1,iglat,iglon1,1)
				NCDatasets.load!(ds["precipitation"].var,tmp2,iglat,iglon2,1)
			end
			close(ds)

			@debug "$(modulelog()) - Extraction of data from temporary array for the $(geo.name) GeoRegion"

			for ilat = 1 : nglat, ilon = 1 : nglon
				varii = tmp0[ilat,ilon]
				mskii = msk[ilon,ilat]
				if (varii != -9999.9f0) && !isnan(mskii)
					  var[ilon,ilat,mo] = varii / 3600
				else; var[ilon,ilat,mo] = NaN32
				end
			end

		end

		save(var,dt,npd,geo,ginfo)

	end

end

function download(
	npd :: TRMM3Hourly{ST,DT},
	geo :: GeoRegion = GeoRegion("GLB")
) where {ST<:AbstractString, DT<:TimeType}

	@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from $(npd.dtbeg) to $(npd.dtend)"

	if geo.regID == "GLB"
		@info "$(modulelog()) - Global dataset request has been detected, switching to the TRMM GeoRegion"
		addNPDGeoRegions(); geo = GeoRegion("TRMM")
	else
		isinGeoRegion(geo,GeoRegion("TRMM"))
	end

	lon,lat = trmmlonloat(); nlon = length(lon); nlat = length(lat)
	ginfo = RegionGrid(geo,lon,lat)

	@info "$(modulelog()) - Preallocating temporary arrays for extraction of $(npd.lname) data for the $(geo.name) GeoRegion from the original gridded dataset"
	glon = ginfo.glon; nglon = length(glon); iglon = ginfo.ilon
	glat = ginfo.glat; nglat = length(glat); iglat = ginfo.ilat
	tmp0 = zeros(Float32,nglat,nglon)
	var  = zeros(Float32,nglon,nglat,8)

	if typeof(geo) <: PolyRegion
		  msk = ginfo.mask
	else; msk = ones(nglon,nglat)
	end

	if iglon[1] > iglon[end]
		shift = true
		iglon1 = iglon[1] : nlon; niglon1 = length(iglon1)
		iglon2 = 1 : iglon[end];  niglon2 = length(iglon2)
		tmp1 = @view tmp0[:,1:niglon1]
		tmp2 = @view tmp0[:,(niglon1+1):niglon2]
	else
		shift = false
		iglon = iglon[1] : iglon[end]
	end

	if iglat[1] > iglat[end]
		iglat = iglat[1] : -1 : iglat[end]
	else
		iglat = iglat[1] : iglat[end]
	end

	for dt in npd.dtbeg : Day(1) : npd.dtend

		@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from the NASA Earthdata servers using OPeNDAP protocols for $(dt) ..."

		ymdfnc = Dates.format(dt,dateformat"yyyymmdd")

		for it = 1 : 8

			@debug "$(modulelog()) - Loading data into temporary array for timestep $(fnc[it])"

			if isone(it); di = dt-Day(1)
				  npddir = joinpath(npd.hroot,"$(year(di))",@sprintf("%03d",dayofyear(di)))
			else; npddir = joinpath(npd.hroot,"$(year(dt))",@sprintf("%03d",dayofyear(dt)))
			end

			npdfnc = "$(npd.fpref).$ymdfnc.$((it-1)*3).$(npd.fsuff)"
			ds = NCDataset(joinpath(npddir,npdfnc))
			if !shift
				NCDatasets.load!(ds["precipitation"].var,tmp0,iglat,iglon,1)
			else
				NCDatasets.load!(ds["precipitation"].var,tmp1,iglat,iglon1,1)
				NCDatasets.load!(ds["precipitation"].var,tmp2,iglat,iglon2,1)
			end
			close(ds)

			@debug "$(modulelog()) - Extraction of data from temporary array for the $(geo.name) GeoRegion"
			for ilat = 1 : nglat, ilon = 1 : nglon
				varii = tmp0[ilat,ilon]
				mskii = msk[ilon,ilat]
				if (varii != -9999.9f0) && !isnan(mskii)
					  var[ilon,ilat,it] = varii / 3600
				else; var[ilon,ilat,it] = NaN32
				end
			end
		end

		save(var,dt,npd,geo,ginfo)

	end

end

function download(
	npd :: TRMMDaily{ST,DT},
	geo :: GeoRegion = GeoRegion("GLB")
) where {ST<:AbstractString, DT<:TimeType}

	@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from $(npd.dtbeg) to $(npd.dtend)"

	if geo.regID == "GLB"
		@info "$(modulelog()) - Global dataset request has been detected, switching to the TRMM GeoRegion"
		addNPDGeoRegions(); geo = GeoRegion("TRMM")
	else
		isinGeoRegion(geo,GeoRegion("TRMM"))
	end

	lon,lat = trmmlonlat(); nlon = length(lon); nlat = length(lat)
	ginfo = RegionGrid(geo,lon,lat)

	@info "$(modulelog()) - Preallocating temporary arrays for extraction of $(npd.lname) data for the $(geo.name) GeoRegion from the original gridded dataset"
	glon = ginfo.glon; nglon = length(glon); iglon = ginfo.ilon
	glat = ginfo.glat; nglat = length(glat); iglat = ginfo.ilat
	tmp0 = zeros(Float32,nglat,nglon)
	var  = zeros(Float32,nglon,nglat,31)

	if typeof(geo) <: PolyRegion
		  msk = ginfo.mask
	else; msk = ones(nglon,nglat)
	end

	if iglon[1] > iglon[end]
		shift = true
		iglon1 = iglon[1] : nlon; niglon1 = length(iglon1)
		iglon2 = 1 : iglon[end];  niglon2 = length(iglon2)
		tmp1 = @view tmp0[:,1:niglon1]
		tmp2 = @view tmp0[:,(niglon1+1):niglon2]
	else
		shift = false
		iglon = iglon[1] : iglon[end]
	end

	if iglat[1] > iglat[end]
		iglat = iglat[1] : -1 : iglat[end]
	else
		iglat = iglat[1] : iglat[end]
	end

	for dt in npd.dtbeg : Month(1) : npd.dtend

		@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from the NASA Earthdata servers using OPeNDAP protocols for $(year(dt))-$(month(dt)) ..."

		npddir = joinpath(npd.hroot,"$(yrmo2dir(dt))")
		ndy = daysinmonth(dt)

		for dy in 1 : ndy
			dtii   = Date(year(dt),month(dt),dy)
			ymdfnc = "$(ymd2str(dtii))"
			npdfnc = "$(npd.fpref).$ymdfnc.$(npd.fsuff)"
			ds = NCDataset(joinpath(npddir,npdfnc))
			if !shift
				NCDatasets.load!(ds["precipitation"].var,tmp0,iglat,iglon,1)
			else
				NCDatasets.load!(ds["precipitation"].var,tmp1,iglat,iglon1,1)
				NCDatasets.load!(ds["precipitation"].var,tmp2,iglat,iglon2,1)
			end
			close(ds)

			@debug "$(modulelog()) - Extraction of data from temporary array for the $(geo.name) GeoRegion"

			for ilat = 1 : nglat, ilon = 1 : nglon
				varii = tmp0[ilat,ilon]
				mskii = msk[ilon,ilat]
				if (varii != -9999.9f0) && !isnan(mskii)
					  var[ilon,ilat,dy] = varii / 86400
				else; var[ilon,ilat,dy] = NaN32
				end
			end

		end

		save(view(var,:,:,1:ndy),dt,npd,geo,ginfo)

	end

end

function download(
	npd :: TRMMMonthly{ST,DT},
	geo :: GeoRegion = GeoRegion("GLB")
) where {ST<:AbstractString, DT<:TimeType}

	@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from $(npd.dtbeg) to $(npd.dtend)"

	if geo.regID == "GLB"
		@info "$(modulelog()) - Global dataset request has been detected, switching to the TRMM GeoRegion"
		addNPDGeoRegions(); geo = GeoRegion("TRMM")
	else
		isinGeoRegion(geo,GeoRegion("TRMM"))
	end

	lon,lat = trmmlonlat(); nlon = length(lon); nlat = length(lat)
	ginfo = RegionGrid(geo,lon,lat)

	@info "$(modulelog()) - Preallocating temporary arrays for extraction of $(npd.lname) data for the $(geo.name) GeoRegion from the original gridded dataset"
	glon = ginfo.glon; nglon = length(glon); iglon = ginfo.ilon
	glat = ginfo.glat; nglat = length(glat); iglat = ginfo.ilat
	tmp0 = zeros(Float32,nglat,nglon)
	var  = zeros(Float32,nglon,nglat,12)

	if typeof(geo) <: PolyRegion
		  msk = ginfo.mask
	else; msk = ones(nglon,nglat)
	end

	if iglon[1] > iglon[end]
		shift = true
		iglon1 = iglon[1] : nlon; niglon1 = length(iglon1)
		iglon2 = 1 : iglon[end];  niglon2 = length(iglon2)
		tmp1 = @view tmp0[:,1:niglon1]
		tmp2 = @view tmp0[:,(niglon1+1):niglon2]
	else
		shift = false
		iglon = iglon[1] : iglon[end]
	end

	if iglat[1] > iglat[end]
		iglat = iglat[1] : -1 : iglat[end]
	else
		iglat = iglat[1] : iglat[end]
	end

	for dt in npd.dtbeg : Year(1) : npd.dtend

		@info "$(modulelog()) - Downloading $(npd.lname) data for the $(geo.name) GeoRegion from the NASA Earthdata servers using OPeNDAP protocols for $(year(dt)) ..."

		npddir = joinpath(npd.hroot,"$(year(dt))")

		for mo in 1 : 12
			dtii   = Date(year(dt),mo,1)
			ymdfnc = "$(ymd2str(dtii))"
			npdfnc = "$(npd.fpref).$ymdfnc.$(npd.fsuff)"
			ds = NCDataset(joinpath(npddir,npdfnc))
			if !shift
				NCDatasets.load!(ds["precipitation"].var,tmp0,iglat,iglon,1)
			else
				NCDatasets.load!(ds["precipitation"].var,tmp1,iglat,iglon1,1)
				NCDatasets.load!(ds["precipitation"].var,tmp2,iglat,iglon2,1)
			end
			close(ds)

			@debug "$(modulelog()) - Extraction of data from temporary array for the $(geo.name) GeoRegion"

			for ilat = 1 : nglat, ilon = 1 : nglon
				varii = tmp0[ilat,ilon]
				mskii = msk[ilon,ilat]
				if (varii != -9999.9f0) && !isnan(mskii)
					  var[ilon,ilat,mo] = varii / 3600
				else; var[ilon,ilat,mo] = NaN32
				end
			end

		end

		save(var,dt,npd,geo,ginfo)

	end

end

max_stars_repo_name: natgeo-wong/NASAPrecipitation

max_issues_repo_name: natgeo-wong/NASAPrecipitation

max_forks_repo_name: natgeo-wong/NASAPrecipitation


text:
module TestAqua

using Aqua
using Kaleido
using Test

Aqua.test_all(
    Kaleido;
    ambiguities = false,
    project_extras = true,
    stale_deps = true,
    deps_compat = true,
    project_toml_formatting = true,
)

@testset "Method ambiguity" begin
    # Not including `Base` due to
    # https://github.com/JuliaCollections/DataStructures.jl/pull/511
    Aqua.test_ambiguities(Kaleido)
end

@testset "Compare test/Project.toml and test/environments/main/Project.toml" begin
    @test Text(read(joinpath(@__DIR__, "Project.toml"), String)) ==
          Text(read(joinpath(@__DIR__, "environments", "main", "Project.toml"), String))
end

end  # module


text:
function _add_system_balance_slacks!(
    optimization_container::OptimizationContainer,
    slack_name::String,
    expression::Symbol,
    single_first_axes::Bool = false,
)
    time_steps = model_time_steps(optimization_container)
    expression_array = get_expression(optimization_container, expression)
    var_name_up = make_variable_name(slack_name, SLACK_UP)
    var_name_dn = make_variable_name(slack_name, SLACK_DN)
    single_first_axes && (first_index = [axes(expression_array)[1][1]])
    !single_first_axes && (first_index = axes(expression_array)[1])
    variable_up =
        add_var_container!(optimization_container, var_name_up, first_index, time_steps)
    variable_dn =
        add_var_container!(optimization_container, var_name_dn, first_index, time_steps)
    for ix in first_index, jx in time_steps
        variable_up[ix, jx] = JuMP.@variable(
            optimization_container.JuMPmodel,
            base_name = "$(var_name_up)_{$(ix), $(jx)}",
            lower_bound = 0.0
        )
        variable_dn[ix, jx] = JuMP.@variable(
            optimization_container.JuMPmodel,
            base_name = "$(var_name_dn)_{$(ix), $(jx)}",
            lower_bound = 0.0
        )
        add_to_expression!(expression_array, ix, jx, variable_up[ix, jx], 1.0)
        add_to_expression!(expression_array, ix, jx, variable_dn[ix, jx], -1.0)
        JuMP.add_to_expression!(
            optimization_container.cost_function,
            (variable_dn[ix, jx] + variable_up[ix, jx]) * BALANCE_SLACK_COST,
        )
    end
    return
end

function add_slacks!(
    optimization_container::OptimizationContainer,
    ::Type{CopperPlatePowerModel},
)
    _add_system_balance_slacks!(
        optimization_container,
        ACTIVE_POWER,
        :nodal_balance_active,
        true,
    )
    return
end

function add_slacks!(
    optimization_container::OptimizationContainer,
    ::Type{T},
) where {T <: PM.AbstractActivePowerModel}
    _add_system_balance_slacks!(optimization_container, ACTIVE_POWER, :nodal_balance_active)
    return
end

function add_slacks!(
    optimization_container::OptimizationContainer,
    ::Type{T},
) where {T <: PM.AbstractPowerModel}
    _add_system_balance_slacks!(optimization_container, ACTIVE_POWER, :nodal_balance_active)
    _add_system_balance_slacks!(
        optimization_container,
        REACTIVE_POWER,
        :nodal_balance_reactive,
    )
    return
end