text:
module ExtremalOptimization
using Random, StatsBase, LinearAlgebra

struct EOState{vP,vC,vO,tW,tN}
    P::Vector{vP}
    C::Vector{vC}
    order::Vector{vO}
    W::tW
    N::tN
end

struct EOProblem{tF,tS}
    f::tF
    s::tS
end

function approxequality(a, b, atol, rtol)
    Δ = norm(a - b)
    M = hypot(norm(a), norm(b))
    Δ <= max(atol, M * rtol)
end

function hasconverged(state::EOState, atol, rtol, f_atol, f_rtol)
    f_a = state.C[state.order[1]]
    f_b = state.C[state.order[2]]
    sol_a = state.P[state.order[1]]
    sol_b = state.P[state.order[2]]
    approxequality(f_a, f_b, f_atol, f_rtol) || approxequality(sol_a, sol_b, atol, rtol)
end

function eostate(problem::EOProblem, N; τ, kw...)
    P = [problem.s(i) for i = 1:N]
    C = [problem.f(P[i]) for i = 1:N]
    for i in eachindex(P)
        isfinite(C[i]) && continue
        error("The cost function has produced non-finite values for the initial point: $(P[i])")
    end
    order = sortperm(C)
    wS = [i^(-τ) for i = N:-1:1]
    wS[end] += wS[1]
    wS[1] = 0
    W = Weights(wS)
    best = order[1]
    P[best], C[best], EOState(P, C, order, W, N)
end

function sample_distinct(rng, N, set, notset)
    N <= 0 && return ()
    @label resample
    s = rand(rng, set)
    (s ∈ notset) && @goto resample
    return (s, sample_distinct(rng, N - 1, set, (notset..., s))...)
end

function eostate(problem::EOProblem, S::EOState; β, rng, verbose, kw...)
    i = S.order[sample(rng, S.W)]
    @label regen
    j, k, q = sample_distinct(rng, 3, 1:S.N, (i,))
    nP = S.P[j] + (β/sqrt(2)) * randn(rng) * (S.P[k] - S.P[q])
    nC = problem.f(nP)
    isfinite(nC) || begin
        verbose && println("Failed function evaluation at $nP")
        @goto regen
    end
    S.P[i] = nP
    S.C[i] = nC
    sortperm!(S.order, S.C)
    best = S.order[1]
    S.P[best], S.C[best], EOState(S.P, S.C, S.order, S.W, S.N)
end

"""
```julia
function optimize(
    f,
    s,
    N;
    reps_per_particle = 100,
    β = 1.5,
    A = 1.0,
    atol = 0.0,
    rtol = sqrt(eps(1.0)),
    f_atol = 0.0,
    f_rtol = sqrt(eps(1.0)),
    verbose = false,
    rng = Random.GLOBAL_RNG,
    callback = state -> nothing,
)
```

- `f` : cost function to minimize, whose argument is either a scalar or a vector, must returns a scalar value.
- `s` : function whose input is the particle number and output is a random initial point to be ranked by `f`.
- `N` : number of particles to use, choose a number greater than `d+4` where `d` is the number of dimensions.
- `reps_per_particle` : maximum number of iterations per particle.

## Usage example:

```julia
using ExtremalOptimization
rosenbrock2d(x) = (x[1] - 1)^2 + 100*(x[2] - x[1]^2)^2
initpoint(i) = randn(2)
optimize(rosenbrock2d, initpoint, 20)
```
output
```
(x = [1.0000008, 1.0000016], fx = 6.50e-13, f_nevals = 1109)
```
as expected the algorithm has found the optimum at `(1, 1)`, up to the specified tolerance.
"""
function optimize(
    f,
    s,
    N;
    reps_per_particle = 100,
    β = 1.5,
    A = 1.0,
    atol = 0.0,
    rtol = sqrt(eps(1.0)),
    f_atol = 0.0,
    f_rtol = sqrt(eps(1.0)),
    verbose = false,
    rng = Random.GLOBAL_RNG,
    callback = state -> nothing,
)
    problem = EOProblem(f, s)
    τ = 1 + A / log(N)
    best, cost, state = eostate(problem, N; τ)
    callback(state)
    fnevals = N
    for i = 1:(N*reps_per_particle)
        best, cost, state = eostate(problem, state; β, rng, verbose)
        fnevals += 1
        callback(state)
        verbose && println(i, " ", cost)
        hasconverged(state, atol, rtol, f_atol, f_rtol) && break
    end
    (x = best, fx = cost, f_nevals = fnevals)
end

export optimize
end


text:
using Revise; using BenchmarkTools; using FunctionWrappers:FunctionWrapper;

function afunk()
    @eval return(2*$x)
end

afunk()

# ######## commented all below

# # https://discourse.julialang.org/t/how-to-bypass-the-world-age-problem/7012/22
# # https://discourse.julialang.org/t/running-in-world-age-x-while-current-world-is-y-errors/5871/2

# function h(t,e)
#    @eval f=(x->$e)
#    print(f(t))
# end

# function h1(t,e)
#    @eval f= (x->$e) |> FunctionWrapper{Float64,Tuple{Float64}}
#    print(f(t))
# end

# # try h(1.,:(x+1)) and h1(1.,:(x+1))

# using BenchmarkTools

# e0=:(exp(x+1))
# #e0=:(x+1)

# function g(e)
#    @eval return( (x->$e)  )
# end

# fe=g(e0)

# # ja(1., e0) should work
# function ja(t,e)
#    print(fe(t))
# end

# # jb(1., e0) should trigger world age problem
# function jb(t,e)
#    f=g(e)
#    print(f(t))
# end

# function gw(e)
#    @eval return( (x->$e) |> FunctionWrapper{Float64,Tuple{Float64}} )
# end

# # Now this should be fine again
# function jc(t,e)
#    f=gw(e)
#    print(f(t))
# end

# # jd(1., e0) should trigger world age problem
# function jd(t,e)
#    f=g(e)
#    print(Base.invokelatest(f,t))
# end

# f1(x,a)=exp(x+a)
# f0=x->f1(x,1.)
# # f2=g(e0)
# f2 = x->exp(x+1)
# # f2 = gw(e0)

# function jt(e)
#    print("With FunctionWrapper")
#    f=gw(e)
#    @btime for i=0.:0.01:10.; $f(i); end
#    #println(f(-1.))

#    print("Using invokelatest")
#    fl=g(e)
#    @btime for i=0.:0.01:10.; Base.invokelatest($fl,i); end
#    #println(Base.invokelatest(fl,0.))

#    print("Nested function calls")
#    @btime for i=0.:0.01:10.; f0(i); end
#    #println(f0(0.))

#    print("Single function calls")
#    @btime for i=0.:0.01:10.; f2(i); end
#    #println(f2(0.))

#    print("Optimum - in loop expression")
#    @btime for i=0.:0.01:10.; exp(i+1); end
# end

# # e0 = :(x+1)

# # julia> jt(e0)
# # With FunctionWrapper  6.244 μs (0 allocations: 0 bytes)
# # Using invokelatest  51.916 μs (3003 allocations: 46.92 KiB)
# # Nested function calls  18.857 μs (2002 allocations: 31.28 KiB)
# # Optimum - in loop expression  503.855 ns (0 allocations: 0 bytes)

# # e0 = :(exp(x+1))

# # julia> jt(e0)
# # With FunctionWrapper  18.099 μs (0 allocations: 0 bytes)
# # 2.718281828459045
# # Using invokelatest  73.238 μs (3003 allocations: 46.92 KiB)
# # 2.718281828459045
# # Nested function calls  36.860 μs (2002 allocations: 31.28 KiB)
# # 2.718281828459045
# # Single function calls  38.826 μs (2002 allocations: 31.28 KiB)     Why is that so big???
# # 2.718281828459045
# # Optimum - in loop expression  13.058 μs (0 allocations: 0 bytes)
# # 2.718281828459045

# @eval f=(x->$e)

# f(t)=t

# function h0(t)
#    f(i)=3*i
#    print(f(t))
# end

# function h1(t)
#    @eval f(i)=3*i
#    print(f(t))
# end
# function h2(t)
#    @eval f(i)=3*i
#    g=Base.invokelatest(f,t)
# end

ext: jl

max_stars_repo_path: 20190605_functionwrappers simpler.jl

max_stars_repo_name: JuliaRheology/RheoBenchAndTest

max_issues_repo_path: 20190605_functionwrappers simpler.jl

max_issues_repo_name: JuliaRheology/RheoBenchAndTest

max_forks_repo_path: 20190605_functionwrappers simpler.jl

max_forks_repo_name: JuliaRheology/RheoBenchAndTest


text:
#===============================================================================

    Updating rule

    Based on code on Ben Moll's webite:
            www.princton.edu/~moll/HACTproject.htm

===============================================================================#

#Need to figure out best way to transfer inputs in julia
include("Huggett_initial.jl")
include("Huggett_terminal.jl")

# Get values for g, r, A from initial
g0, gg0, r, A = initial_val()

r00 = r
A0=A
AT0 = A'

# Get values for g_st, v_st, r_st from terminal
g_st, v_st, r_st = terminal()

plot(a,v_st)

plot(a, g)
plot!(a,g0, xlims=(-0.15,1))

# Create time and time increment variables
T = 20
N = 100
H =1000
dt = T/N

# Create and initial guess of the interest rate sequence
r0 = r_st*ones(N,1)

# Set up space to save variables
S = zeros(N,1)

v = zeros(H,2,N)

v[:,:,N] = v_st

r_new = r0
r_t =r0
A_t = []

maxit=1000
r_it =zeros(N,maxit)
Sdist = zeros(maxit,1)
dVf, dVb, c = [zeros(H,2) for i in 1:3]
dS_it=[]
SS_it=[]
gg=zeros(N+1,2*H,1)

convergence_criterion = 10^(-5)

# Speed of updating the interest rate
ξ = 20*(exp.(-0.05*(1:N))) .- exp(-0.05*N)

# main loop

for it in 1:maxit
    global r_t = r_new
    r_it[:,it] = r_t
    V = v_st

    for n in N:-1:1
        v[:,:,n]=V
        #forward difference
        dVf[1:H-1,:] = (V[2:H,:]-V[1:H-1,:])/da
        dVf[H,:] = (z .+ r_t[n].*amax).^(-s) #will never be used, but impose state constraint a<=amax just in case
        #backward difference
        dVb[2:H,:] = (V[2:H,:]-V[1:H-1,:])/da
        dVb[1,:] = (z .+ r_t[n].*amin).^(-s) #state constraint boundary condition

        I_concave = dVb .> dVf #indicator whether value function is concave (problems arise if this is not the case)

        # consumption and savings with forward difference
        cf = dVf.^(-1/s)
        ssf = zz + r_t[n].*aa - cf
        # consumption and savings with backward difference
        cb = dVb.^(-1/s)
        ssb = zz + r_t[n].*aa - cb
        # consumption and derivative of value function at steady state
        c0 = zz + r_t[n].*aa
        dV0 = c0.^(-s)

        #= dV_upwind makes a choice of forward or backward differences based on
         the sign of the drift    =#
        If = ssf .> 0 #positive drift → forward difference
        Ib = ssb .< 0 #negative drift → backward difference
        I0 = (1.0 .-If-Ib) #at steady state

        dV_Upwind = dVf.*If + dVb.*Ib + dV0.*I0  #important to include third term
        c = dV_Upwind.^(-1/s)
        u = c.^(1-s)/(1-s)

        # CONSTRUCT MATRIX
        X = - min.(ssb,0)/da
        Y = - max.(ssf,0)/da + min.(ssb,0)/da
        Z = max.(ssf,0)/da

        A1=spdiagm(0 => Y[:,1], -1 => X[2:H,1], 1 =>Z[1:H-1,1])
        A2=spdiagm(0=> Y[:,2], -1 =>X[2:H,2], 1 => Z[1:H-1,2])

        global A = [A1 spzeros(H,H);spzeros(H,H) A2] + Aswitch

        push!(A_t,A)
        B = (1/dt + ρ)*sparse(I, 2*H, 2*H) - A

        u_stacked = [u[:,1];u[:,2]]
        V_stacked = [V[:,1];V[:,2]]

        b = u_stacked + V_stacked/dt
        V_stacked = B\b #SOLVE SYSTEM OF EQUATIONS

        V = [V_stacked[1:H] V_stacked[H+1:2*H]]

        global ss = zz+ r_t[n].*aa -c
    end

    gg[1,:,:] = gg0

    #loop for KFE, solve forward
    global SS = zeros(N,1)
    global dS = zeros(N,1)

    for q in 1:N
        global SS, dS = SS, dS
        AT=A_t[end-(q-1)]' #the indexing is backward from original code.
        gg[q+1,:,:] = (sparse(I,2*H,2*H)-AT*dt)\gg[q,:,:]
        global SS[q] = (gg[q,:,:]'*aa[:].*da)[1]
        global dS[q] = (gg[q+1,:,:]'*aa[:]*da - gg[q,:,:]'*aa[:]*da)[1]
    end

    push!(dS_it,dS)
    push!(SS_it, SS)

    global r_new = r_t - ξ.*dS

    Sdist[it] = findmax(abs.(dS))[1]
    println("Iteration = $(it)")
    println("Convergence Criterion= $(Sdist[it])")

    if Sdist[it] < convergence_criterion
        break
    end
end

plot(Sdist[:])

# Correct graph, previously had some scoping issues with Julia
plot(1:N, SS_it[1], ylabel="Excess Supply", xlabel="Excess Demand", label="First iteration" )
plot!(1:N, SS_it[end], label="Last Iternation")

plot(r_t[:], legend=false, xlabel="Period", ylabel="r")
plot!(1:N, r_st.*ones(N,1), line=:dash)

# More plots

N1 =4
T1=-N1*dt
time = (1:N)'*dt
time1 = T1 .+ (1:N1)'*dt
time2 = [time1'; time']
r_t2 = [r00*ones(N1,1); r_t]

plot(time2, r_t2, xlims=(T1, 10), ylims=(-0.05, 0.035), legend=:false)
plot!(time2, r_st*ones(N1+N,1), line=:dash,
        xlabel="Year", title="Equilibrium Interest Rate")

amax1 = 0.5
gmax=3
n=2

p1 = plot(a, gg[n,1:H,:],xlims=(amin,amax1),
        ylims=(0, gmax), xlabel="Wealth", ylabel="Densities",legend=false)
plot!(a, gg[n,H+1:end,:], title="\$t=0.1\$")

# set up new index
t=2
n=convert(Int64, t/dt)

p2 = plot(a, gg[n,1:H,:],xlims=(amin,amax1),
        ylims=(0, gmax), xlabel="Wealth", ylabel="Densities",legend=false)
plot!(a, gg[n,H+1:end,:], title="\$t=2\$")

t=5
n=convert(Int64, t/dt)

p3 = plot(a, gg[n,1:H,:],xlims=(amin,amax1),
        ylims=(0, gmax), xlabel="Wealth", ylabel="Densities",legend=false)
plot!(a, gg[n,H+1:end,:], title="\$t=5\$")

t=T
n=convert(Int64, t/dt)

p4 = plot(a, gg[n,1:H,:],xlims=(amin,amax1),
        ylims=(0, gmax), xlabel="Wealth", ylabel="Densities",legend=false)
plot!(a, gg[n,H+1:end,:], title="\$t=\\infty\$")

plot(p1,p2,p3,p4,legend=false,layout = (2,2))


text:
using WhereTraits
using Test

using Suppressor
using StringDistances  # TODO make this a submodule for test only
using InteractiveUtils

# Test README.md example
# ======================

isTr(_) = false

fn(x::Integer) = 1
@traits fn(x::X) where {X<:AbstractFloat, isTr(X)} = 2
@traits_show_implementation fn

@traits fn(x::AbstractFloat) = 3
@traits_show_implementation fn

if !isTr(Float32)  # if the test is reexecuted, the definition already exists
  @test fn(Float32(5)) == 3 # -> 3; dispatch through traits, but isTr not yet defined, hence using default case
end

isTr(::Type{Float32}) = true
isTr(::Type{Int}) = true

@test fn(5) == 1 # -> 1; dispatch only happens on the type
@test fn(Float32(5)) == 2 # -> 2; dispatch through traits
@test fn(Float64(5)) == 3 # -> 3; default dispatch through traits

isTr2(_) = false
isTr2(::Type{Float16}) = true
@traits fn(x::X) where {X <: AbstractFloat, isTr2(X)} = 4
@traits_show_implementation fn

@test fn(Float16(5)) == 4 # -> 4; dispatch through traits
@test fn(Float32(5)) == 2 # -> 2; NO MethodError; nothing is overwritten, everything works like you would hope for

println("@code_llvm fn(5) --------------------------------------------")
@code_llvm fn(5)
@code_warntype fn(5)

println("@code_llvm fn(Float16(5)) --------------------------------------------")
@code_llvm fn(Float16(5))
@code_warntype fn(Float16(5))

out1 = @capture_out begin
  @code_llvm fn(5)
end
out2 = @capture_out begin
  @code_llvm fn(Float16(5))
end
# string distance should be small
if evaluate(Levenshtein(), out1, out2) > 10
  @warn """
  Both code should be the same, however compile to massively different machine code.
  You may want to take a look at the above printed code_llvm and code_warntype.

  The result my strongly vary from julia version to julia version
  """
end


text:
module FindPeaks

import Base.isempty, Base.length, Base.getindex, Base.sort

export findindices, findprominences, findwidthbounds
export findpeaks

export PeakResults
export peakheights, peaklocations, peakprominences, peakwidths
export isempty, length, getindex, sort

include("functions.jl")
include("PeakResults.jl")

end  # module

max_stars_repo_name: vkumpost/stoosc

max_issues_repo_name: vkumpost/stoosc

max_forks_repo_name: vkumpost/stoosc


text:
let
    # roundtrip test
    # TODO this could be done with Quickcheck if we had a way of
    # generating "interesting" HTML documents
    doc = open("$testdir/fixtures/example.html") do example
        read(example, String) |> parsehtml
    end
    io = IOBuffer()
    print(io, doc)
    seek(io, 0)
    newdoc = read(io, String) |> parsehtml
    @test newdoc == doc
end

tests = [
    "30",  # regression test for issue #30
    "multitext",  # regression test for multiple HTMLText in one HTMLElement
    "varied",  # relatively complex example
    "whitespace",  # whitespace sensitive
]
@testset for test in tests
    let
        doc = open("$testdir/fixtures/$(test)_input.html") do example
            parsehtml(read(example, String), preserve_whitespace = (test == "whitespace"))
        end
        io = IOBuffer()
        print(io, doc.root, pretty = (test != "whitespace"))
        seek(io, 0)
        ground_truth = read(open("$testdir/fixtures/$(test)_output.html"), String)
        # Eliminate possible line ending issues
        ground_truth = replace(ground_truth, "\r\n" => "\n")
        @test read(io, String) == ground_truth
    end
end

@testset "xml entities" begin
    io = IOBuffer()

    orig = """
<faketag>
"""
    print(io, parsehtml(orig))
    @test occursin(orig, String(take!(io)))
end


text:
using Base.Test
import WaterData

@testset "Utility functions" begin
    @testset "Small utility functions" begin
        # maprows
        A = [1 2;3 4]
        @test vec(WaterData.maprows(sum, A)) == [3, 7]
        @test vec(WaterData.maprows(mean, A)) == [1.5, 3.5]

        # adjacent pairs
        B = [1, 7, 2, 8]
        @test collect(WaterData.adjacentpairs(B)) == [(1,7), (7,2), (2,8)]

        # 2D cross product
        @test WaterData.crossprod2d([1, 1], [1, 1]) == 0
        @test WaterData.crossprod2d([1,-1], [3, 4]) == 7
        @test WaterData.crossprod2d([1, 2], [3, 4]) == -2

        # isoutside
        @test !WaterData.isoutside(3, 2, 4)
        @test WaterData.isoutside(2., 3., 4.)
        @test !WaterData.isoutside(3., 3., 4.)

        # intersection testing
        c1 = ([1, 5], [8, 5])
        c2 = ([1, 1], [8, 8])
        c3 = ([3, 1], [3, 8])
        c4 = ([8, 5], [8, 8])
        c5 = ([4, 1], [7, 4])
        yeses = [(c1,c2), (c1,c3), (c1,c4), (c2,c3), (c2,c4)]
        noes  = [(c1,c5), (c2,c5), (c3,c4), (c3,c5), (c4,c5)]
        for lines in yeses
            @test WaterData.intersects(lines...)
        end
        for lines in noes
            @test !WaterData.intersects(lines...)
        end
    end

    @testset "Duplicate finding" begin
        @test WaterData.unique_indices([1,2,3,4,5]) == [1,2,3,4,5]
        @test WaterData.unique_indices([1,1,1,2]) == [3,4]
        @test WaterData.unique_indices([1,1,2,3,4,5,4,5]) == [2,3,4,7,8]
        @test length(WaterData.unique_indices([1,1,1,1,1])) == 1
        @test length(WaterData.unique_indices([1,2,3,1,2,3,1,2])) == 3

        dups = [1,2,3,2,1,2,3]
        uniq = WaterData.unique_indices(dups)
        @test dups[uniq] == [1,2,3]
    end
end


text:
# Load the Interpolator in the FEM module
FEM.lintriginterpmod()
using FEM.LinTrigInterpMod

module LinTrigMod

using FEM.LinTrigInterpMod

import FEM: createinterp, creategps, createstorage, get_field,
            Bmatrix, doftypes, get_ndofs, get_geotype, get_ref_area, dNdxmatrix

import FEM: AbstractMaterialStatus, AbstractElemStorage, AbstractFElement, FENode2
import FEM: Vertex3, Point2, GaussPoint2, Du, Dv, GeoTrig

export LinTrig

type LinTrigStorage <: AbstractElemStorage
    B::Matrix{Float64}
    DeBe::Matrix{Float64}
    Ke::Matrix{Float64}
    ɛ::Vector{Float64}
    f_int::Vector{Float64}
    u_field::Vector{Float64}
end

LinTrigStorage() = LinTrigStorage(zeros(4, 6), zeros(4,6), zeros(6,6),
                                  zeros(4), zeros(6), zeros(6))

type LinTrig{T <: AbstractMaterialStatus} <: AbstractFElement{T}
    vertices::Vertex3
    gps::Vector{GaussPoint2}
    n::Int
    interp::LinTrigInterp
    storage::LinTrigStorage
    matstats::Vector{T}
    temp_matstats::Vector{T}
end

# Constructors
function LinTrig{T <: AbstractMaterialStatus}(vertices::Vertex3, n, interp::LinTrigInterp,
                 lts::LinTrigStorage, gps::Vector{GaussPoint2}, matstat::T)
    matstats = Array(T, length(gps))
    temp_matstats = Array(T, length(gps))
    for i in 1:length(gps)
        matstats[i] = copy(matstat)
        temp_matstats[i] = copy(matstat)
    end

    LinTrig(vertices, gps, n, interp, lts, matstats, temp_matstats)
end

function LinTrig(v::Vector{Int}, n, interp::LinTrigInterp,
                 lts::LinTrigStorage, gps::Vector{GaussPoint2})
    LinTrig(Vertex3(v[1], v[2], v[3]), gps, n, interp, lts)
end

get_ref_area(::LinTrig) = 0.5
get_geoelem(ele::LinTrig) = GeoTrig(ele.n, ele.vertices)
get_geotype(::LinTrig) = GeoTrig
createstorage(::Type{LinTrig}) = LinTrigStorage()
createinterp(::Type{LinTrig}) = LinTrigInterp()
creategps(::Type{LinTrig}) = [GaussPoint2(Point2(1/3, 1/3), 0.5)]
get_ndofs(::LinTrig) = 6

const dofsa = [Du, Dv]
function doftypes(::LinTrig, ::Int)
    return dofsa
end

function Bmatrix(elem::LinTrig, gp::GaussPoint2, nodes::Vector{FENode2})
    dNdx = dNdxmatrix(elem.interp, gp.local_coords, elem.vertices, nodes)

    B = elem.storage.B

    B[1, 1] = dNdx[1, 1]
    B[1, 3] = dNdx[2, 1]
    B[1, 5] = dNdx[3, 1]

    B[2, 2] = dNdx[1, 2]
    B[2, 4] = dNdx[2, 2]
    B[2, 6] = dNdx[3, 2]

    B[4, 1] = dNdx[1, 2]
    B[4, 2] = dNdx[1, 1]
    B[4, 3] = dNdx[2, 2]
    B[4, 4] = dNdx[2, 1]
    B[4, 5] = dNdx[3, 2]
    B[4, 6] = dNdx[3, 1]

    return B
end

end # module


text:
using SimpleValueGraphs: hasedgekey_or_throw, hasvertexkey_or_throw, hasgraphkey_or_throw, AbstractValGraph, ValEdgeIter

@testset "abstractvaluegraph" begin

@testset "eltype" begin

    @test eltype(DummyValGraph{Int8}) == Int8
    @test eltype(DummyValGraph(ValGraph{UInt16}(3))) == UInt16
end

@testset "vertexvals_type" begin

    @test vertexvals_type(DummyValGraph{Int64, Tuple{Integer, Union{Nothing, String}}}) ==
                Tuple{Integer, Union{Nothing, String}}

    g = DummyValGraph(ValDiGraph(
        0;
        vertexval_types=(a=Char, b=Vector{Bool}),
        vertexval_init=undef
    ))

    expected_type = NamedTuple{(:a, :b), Tuple{Char, Vector{Bool}}}
    @assert g isa AbstractValGraph{V, expected_type, E_VALS} where {V, E_VALS}
    @test vertexvals_type(g) == expected_type
end

@testset "vertexvals_type" begin

    @test edgevals_type(DummyValGraph{Int, Tuple{}, Tuple{Integer, Union{Nothing, String}}}) ==
                Tuple{Integer, Union{Nothing, String}}

    g = DummyValGraph(ValOutDiGraph(
        0;
        edgeval_types=(String,)
    ))
    expected_type = Tuple{String}
    @assert g isa AbstractValGraph{V, V_VALS, expected_type} where {V, V_VALS}
    @test edgevals_type(g) == expected_type
end

@testset "hasedgekey" begin

    g1 = DummyValGraph(ValGraph(0; edgeval_types=(a=Int, b=String)))
    g2 = DummyValGraph(ValDiGraph(0; edgeval_types=(Int, )))

    @test hasedgekey(g1, :a)
    @test hasedgekey(typeof(g1), :a)
    @test hasedgekey(g1, :b)
    @test hasedgekey(typeof(g1), :b)
    @test !hasedgekey(g1, :c)
    @test !hasedgekey(typeof(g1), :c)
    @test hasedgekey(g1, 1)
    @test hasedgekey(typeof(g1), 1)
    @test hasedgekey(g1, 2)
    @test hasedgekey(typeof(g1), 2)
    @test !hasedgekey(g1, 0)
    @test !hasedgekey(typeof(g1), 0)
    @test !hasedgekey(g1, 3)
    @test !hasedgekey(typeof(g1), 3)

    @test hasedgekey(g2, 1)
    @test hasedgekey(typeof(g2), 1)
    @test !hasedgekey(g2, 0)
    @test !hasedgekey(typeof(g2), 2)
end

@testset "hasedgekey_or_throw" begin

    g0 = DummyValGraph(ValOutDiGraph(Int8(0)))
    g1 = DummyValGraph(ValGraph(1; edgeval_types=(a=Int, b=String)))
    g2 = DummyValGraph(ValDiGraph(2; edgeval_types=(Int, )))

    G0 = typeof(g0)
    G1 = typeof(g1)
    G2 = typeof(g2)

    @test_throws Exception hasedgekey_or_throw(g0, 1)
    @test_throws Exception hasedgekey_or_throw(G0, 1)
    @test_throws Exception hasedgekey_or_throw(g0, 0)
    @test_throws Exception hasedgekey_or_throw(G0, 0)

    @test hasedgekey_or_throw(g1, :a) == nothing
    @test hasedgekey_or_throw(G1, :a) == nothing
    @test hasedgekey_or_throw(g1, :b) == nothing
    @test hasedgekey_or_throw(G1, :b) == nothing
    @test_throws Exception hasedgekey_or_throw(g1, :c)
    @test_throws Exception hasedgekey_or_throw(G1, :c)
    @test hasedgekey_or_throw(g1, 1) == nothing
    @test hasedgekey_or_throw(G1, 1) == nothing
    @test hasedgekey_or_throw(g1, 2) == nothing
    @test hasedgekey_or_throw(G1, 2) == nothing
    @test_throws Exception hasedgekey_or_throw(g1, 3)
    @test_throws Exception hasedgekey_or_throw(G1, 3)

    @test hasedgekey_or_throw(g2, 1) == nothing
    @test hasedgekey_or_throw(G2, 1) == nothing
    @test_throws Exception hasedgekey_or_throw(g2, 0)
    @test_throws Exception hasedgekey_or_throw(G2, 2)
end

@testset "hasvertexkey" begin

    g1 = DummyValGraph(ValGraph(0; vertexval_types=(xy=Bool, ), vertexval_init=undef))
    g2 = DummyValGraph(ValDiGraph(0; vertexval_types=(Int, Int), vertexval_init=undef))

    @test hasvertexkey(g1, :xy)
    @test hasvertexkey(typeof(g1), :xy)
    @test !hasvertexkey(g1, :uv)
    @test !hasvertexkey(typeof(g1), :uv)
    @test hasvertexkey(g1, 1)
    @test hasvertexkey(typeof(g1), 1)
    @test !hasvertexkey(g1, 0)
    @test !hasvertexkey(typeof(g1), 0)
    @test !hasvertexkey(g1, 2)
    @test !hasvertexkey(typeof(g1), 2)

    @test hasvertexkey(g2, Int8(1))
    @test hasvertexkey(typeof(g2), Int8(1))
    @test hasvertexkey(g2, UInt8(2))
    @test hasvertexkey(typeof(g2), UInt8(2))
    @test !hasvertexkey(g2, 0)
    @test !hasvertexkey(typeof(g2), UInt8(0))
    @test !hasvertexkey(g2, 3)
    @test !hasvertexkey(typeof(g2), 3)
end

@testset "hasvertexkey_or_throw" begin

    g0 = DummyValGraph(ValOutDiGraph(Int8(0)))
    g1 = DummyValGraph(ValGraph(0; vertexval_types=(xy=Bool, ), vertexval_init=undef))
    g2 = DummyValGraph(ValDiGraph(0; vertexval_types=(Int, Int), vertexval_init=undef))

    G0 = typeof(g0)
    G1 = typeof(g1)
    G2 = typeof(g2)

    @test_throws Exception hasvertexkey_or_throw(g0, 1)
    @test_throws Exception hasvertexkey_or_throw(G0, 1)
    @test_throws Exception hasvertexkey_or_throw(g0, 0)
    @test_throws Exception hasvertexkey_or_throw(G0, 0)

    @test hasvertexkey_or_throw(g1, :xy) == nothing
    @test hasvertexkey_or_throw(G1, :xy) == nothing
    @test_throws Exception hasvertexkey_or_throw(g1, :uv)
    @test_throws Exception hasvertexkey_or_throw(G1, :uv)
    @test hasvertexkey_or_throw(g1, 1) == nothing
    @test hasvertexkey_or_throw(G1, 1) == nothing
    @test_throws Exception hasvertexkey_or_throw(g1, 0)
    @test_throws Exception hasvertexkey_or_throw(G1, 0)
    @test_throws Exception hasvertexkey_or_throw(g1, 2)
    @test_throws Exception hasvertexkey_or_throw(G1, 2)

    @test hasvertexkey_or_throw(g2, Int8(1)) == nothing
    @test hasvertexkey_or_throw(G2, Int8(1)) == nothing
    @test hasvertexkey_or_throw(g2, UInt8(2)) == nothing
    @test hasvertexkey_or_throw(G2, UInt8(2)) == nothing
    @test_throws Exception hasvertexkey_or_throw(g2, 0)
    @test_throws Exception hasvertexkey_or_throw(G2, UInt8(0))
    @test_throws Exception hasvertexkey_or_throw(g2, 3)
    @test_throws Exception hasvertexkey_or_throw(G2, 3)
end

@testset "hasgraphkey" begin

    g0 = DummyValGraph(ValOutDiGraph(Int8(0)))
    g1 = DummyValGraph(ValGraph(0; graphvals=(xy=Bool, )))
    g2 = DummyValGraph(ValDiGraph(0; graphvals=(10, -20)))

    G0 = typeof(g0)
    G1 = typeof(g1)
    G2 = typeof(g2)

    @test !hasgraphkey(g0, 1)
    @test !hasgraphkey(G0, 1)
    @test !hasgraphkey(g0, 0)
    @test !hasgraphkey(G0, 0)

    @test hasgraphkey(g1, :xy)
    @test hasgraphkey(G1, :xy)
    @test !hasgraphkey(g1, :uv)
    @test !hasgraphkey(G1, :uv)
    @test hasgraphkey(g1, 1)
    @test hasgraphkey(G1, 1)
    @test !hasgraphkey(g1, 0)
    @test !hasgraphkey(G1, 0)
    @test !hasgraphkey(g1, 2)
    @test !hasgraphkey(G1, 2)

    @test hasgraphkey(g2, Int8(1))
    @test hasgraphkey(G2, Int8(1))
    @test hasgraphkey(g2, UInt8(2))
    @test hasgraphkey(G2, UInt8(2))
    @test !hasgraphkey(g2, 0)
    @test !hasgraphkey(G2, UInt8(0))
    @test !hasgraphkey(g2, 3)
    @test !hasgraphkey(G2, 3)
end

@testset "hasgraphkey_or_throw" begin

    g0 = DummyValGraph(ValOutDiGraph(Int8(0)))
    g1 = DummyValGraph(ValGraph(0; graphvals=(xy=Bool, )))
    g2 = DummyValGraph(ValDiGraph(0; graphvals=(10, -20)))

    G0 = typeof(g0)
    G1 = typeof(g1)
    G2 = typeof(g2)

    @test_throws Exception hasgraphkey_or_throw(g0, 1)
    @test_throws Exception hasgraphkey_or_throw(G0, 1)
    @test_throws Exception hasgraphkey_or_throw(g0, 0)
    @test_throws Exception hasgraphkey_or_throw(G0, 0)

    @test hasgraphkey_or_throw(g1, :xy) == nothing
    @test hasgraphkey_or_throw(G1, :xy) == nothing
    @test_throws Exception hasgraphkey_or_throw(g1, :uv)
    @test_throws Exception hasgraphkey_or_throw(G1, :uv)
    @test hasgraphkey_or_throw(g1, 1) == nothing
    @test hasgraphkey_or_throw(G1, 1) == nothing
    @test_throws Exception hasgraphkey_or_throw(g1, 0)
    @test_throws Exception hasgraphkey_or_throw(G1, 0)
    @test_throws Exception hasgraphkey_or_throw(g1, 2)
    @test_throws Exception hasgraphkey_or_throw(G1, 2)

    @test hasgraphkey_or_throw(g2, Int8(1)) == nothing
    @test hasgraphkey_or_throw(G2, Int8(1)) == nothing
    @test hasgraphkey_or_throw(g2, UInt8(2)) == nothing
    @test hasgraphkey_or_throw(G2, UInt8(2)) == nothing
    @test_throws Exception hasgraphkey_or_throw(g2, 0)
    @test_throws Exception hasgraphkey_or_throw(G2, UInt8(0))
    @test_throws Exception hasgraphkey_or_throw(g2, 3)
    @test_throws Exception hasgraphkey_or_throw(G2, 3)
end

# TODO test show

@testset "vertices" begin

    g1 = DummyValGraph(ValGraph{UInt8}(2))
    g2 = DummyValGraph(ValOutDiGraph{Int16}(3))

    @test allunique(vertices(g1))
    @test issetequal(vertices(g1), 1:2)
    @test allunique(vertices(g2))
    @test issetequal(vertices(g2), 1:3)
end

@testset "has_vertex" begin

    g = DummyValGraph(ValGraph{UInt8}(2))

    @test has_vertex(g, UInt8(1))
    @test has_vertex(g, 2)
    @test !has_vertex(g, 0)
    @test !has_vertex(g, 3)
    @test !has_vertex(g, -1)
end

@testset "edges" begin

    g_simple = SimpleGraph(Edge.([(1, 1), (1, 2), (4, 5)]))
    g_simple_di = SimpleDiGraph(Edge.([(1, 1), (1, 2), (4, 5), (5, 4)]))

    g1 = DummyValGraph(ValGraph{UInt16}(g_simple;
        edgeval_types=(a=Int64, b=Float64),
        edgeval_init=(s, d) -> (a=s, b=d)
    ))
    g2 = DummyValGraph(ValDiGraph{Int16}(g_simple_di;
        edgeval_types=(Int64, Float64),
        edgeval_init=(s, d) -> (s, d)
    ))

    @test edges(g1, :) isa ValEdgeIter{typeof(g1)}
    @test edges(g2, :) isa ValEdgeIter{typeof(g2)}

    @test eltype(edges(g1, :)) == ValEdge{UInt16, NamedTuple{(:a, :b), Tuple{Int64, Float64}}}
    @test eltype(edges(g2, :)) == ValDiEdge{Int16, Tuple{Int64, Float64}}

    @test collect(edges(g1, :)) == [
        ValEdge{UInt16, NamedTuple{(:a, :b), Tuple{Int64, Float64}}}(1, 1, (a=1, b=1.0)),
        ValEdge{UInt16, NamedTuple{(:a, :b), Tuple{Int64, Float64}}}(1, 2, (a=1, b=2.0)),
        ValEdge{UInt16, NamedTuple{(:a, :b), Tuple{Int64, Float64}}}(4, 5, (a=4, b=5.0))
    ]
    @test collect(edges(g2, :)) == [
        ValDiEdge{Int16, Tuple{Int64, Float64}}(1, 1, (1, 1.0)),
        ValDiEdge{Int16, Tuple{Int64, Float64}}(1, 2, (1, 2.0)),
        ValDiEdge{Int16, Tuple{Int64, Float64}}(4, 5, (4, 5.0)),
        ValDiEdge{Int16, Tuple{Int64, Float64}}(5, 4, (5, 4.0)),
    ]
end

@testset "edgetype" begin

    g1 = DummyValGraph(ValGraph{Int8}(0; edgeval_types=(Int16, Int32)))
    g2 = DummyValGraph(ValDiGraph{UInt8}(0; edgeval_types=(a=UInt16, b=UInt32)))

    @test edgetype(g1) == ValEdge{Int8, Tuple{}}
    @test edgetype(g2) == ValDiEdge{UInt8, Tuple{}}
end

@testset "ne" begin

    g_simple = SimpleGraph(Edge.([(1, 1), (1, 2), (4, 5)]))
    g_simple_di = SimpleDiGraph(Edge.([(1, 1), (1, 2), (4, 5), (5, 4)]))

    g1 = DummyValGraph(ValGraph(g_simple))
    g2 = DummyValGraph(ValDiGraph(g_simple_di))

    @test ne(g1) == 3
    @test ne(g2) == 4
end

@testset "outneightbors" begin

    g_simple = SimpleGraph(Edge.([(1, 1), (1, 2), (4, 5)]))
    g_simple_di = SimpleDiGraph(Edge.([(1, 1), (1, 2), (4, 5), (5, 4)]))

    g1 = DummyValGraph(ValGraph(g_simple))
    g2 = DummyValGraph(ValDiGraph(g_simple_di))

    @test outneighbors(g1, 1) == [1, 2]
    @test outneighbors(g1, 2) == [1]
    @test outneighbors(g1, 3) == []
    @test outneighbors(g1, 4) == [5]
    @test outneighbors(g1, 5) == [4]

    @test outneighbors(g2, 1) == [1, 2]
    @test outneighbors(g2, 2) == []
    @test outneighbors(g2, 3) == []
    @test outneighbors(g2, 4) == [5]
    @test outneighbors(g2, 5) == [4]
end

@testset "inneightbors" begin

    g_simple = SimpleGraph(Edge.([(1, 1), (1, 2), (4, 5)]))
    g_simple_di = SimpleDiGraph(Edge.([(1, 1), (1, 2), (4, 5), (5, 4)]))

    g1 = DummyValGraph(ValGraph(g_simple))
    g2 = DummyValGraph(ValDiGraph(g_simple_di))

    @test inneighbors(g1, 1) == [1, 2]
    @test inneighbors(g1, 2) == [1]
    @test inneighbors(g1, 3) == []
    @test inneighbors(g1, 4) == [5]
    @test inneighbors(g1, 5) == [4]

    @test inneighbors(g2, 1) == [1]
    @test inneighbors(g2, 2) == [1]
    @test inneighbors(g2, 3) == []
    @test inneighbors(g2, 4) == [5]
    @test inneighbors(g2, 5) == [4]
end

@testset "outedgevals" begin

    g4 = ValOutDiGraph{Int16}(4, edgeval_types=(a=String,))
    add_edge!(g4, 1, 2, (a="12",))
    add_edge!(g4, 2, 2, (a="22",))
    add_edge!(g4, 1, 4, (a="14",))
    add_edge!(g4, 4, 1, (a="41",))
    g4 = DummyValGraph(g4)

    @test outedgevals(g4, 1) == ["12", "14"]
    @test outedgevals(g4, 2) == ["22"]
    @test outedgevals(g4, 3) == []
    @test outedgevals(g4, 4) == ["41"]

    @test outedgevals(g4, 1, 1) == ["12", "14"]
    @test outedgevals(g4, 2, 1) == ["22"]
    @test outedgevals(g4, 3, 1) == []
    @test outedgevals(g4, 4, 1) == ["41"]

    @test outedgevals(g4, 1, :a) == ["12", "14"]
    @test outedgevals(g4, 2, :a) == ["22"]
    @test outedgevals(g4, 3, :a) == []
    @test outedgevals(g4, 4, :a) == ["41"]

    @test outedgevals(g4, 1, :) == [(a="12",), (a="14",)]
    @test outedgevals(g4, 2, :) == [(a="22",)]
    @test outedgevals(g4, 3, :) == []
    @test outedgevals(g4, 4, :) == [(a="41",)]

    g3 = ValGraph{UInt64}(3, edgeval_types=(x=String, y=Tuple{Int, Int}))
    add_edge!(g3, 1, 2, (x="12", y=(1,2)))
    add_edge!(g3, 2, 3, (x="23", y=(2,3)))
    add_edge!(g3, 3, 3, (x="33", y=(3,3)))
    g3 = DummyValGraph(g3)

    @test outedgevals(g3, 1, 1) == ["12"]
    @test outedgevals(g3, 2, 1) == ["12", "23"]
    @test outedgevals(g3, 3, 1) == ["23", "33"]
    @test outedgevals(g3, 1, 2) == [(1,2)]
    @test outedgevals(g3, 2, 2) == [(1,2), (2,3)]
    @test outedgevals(g3, 3, 2) == [(2,3), (3,3)]

    @test outedgevals(g3, 1, :x) == ["12"]
    @test outedgevals(g3, 2, :x) == ["12", "23"]
    @test outedgevals(g3, 3, :x) == ["23", "33"]
    @test outedgevals(g3, 1, :y) == [(1,2)]
    @test outedgevals(g3, 2, :y) == [(1,2), (2,3)]
    @test outedgevals(g3, 3, :y) == [(2,3), (3,3)]

    @test outedgevals(g3, 1, :) == [(x="12", y=(1,2))]
    @test outedgevals(g3, 2, :) == [(x="12", y=(1,2)), (x="23", y=(2,3))]
    @test outedgevals(g3, 3, :) == [(x="23", y=(2,3)), (x="33", y=(3,3))]
end

@testset "inedgevals" begin

    g4 = ValDiGraph(4, edgeval_types=(a=String,))
    add_edge!(g4, 1, 2, (a="12",))
    add_edge!(g4, 2, 2, (a="22",))
    add_edge!(g4, 1, 4, (a="14",))
    add_edge!(g4, 4, 1, (a="41",))
    g4 = DummyValGraph(g4)

    @test inedgevals(g4, 1) == ["41"]
    @test inedgevals(g4, 2) == ["12", "22"]
    @test inedgevals(g4, 3) == []
    @test inedgevals(g4, 4) == ["14"]

    @test inedgevals(g4, 1, 1) == ["41"]
    @test inedgevals(g4, 2, 1) == ["12", "22"]
    @test inedgevals(g4, 3, 1) == []
    @test inedgevals(g4, 4, 1) == ["14"]

    @test inedgevals(g4, 1, :a) == ["41"]
    @test inedgevals(g4, 2, :a) == ["12", "22"]
    @test inedgevals(g4, 3, :a) == []
    @test inedgevals(g4, 4, :a) == ["14"]

    @test inedgevals(g4, 1, :) == [(a="41",)]
    @test inedgevals(g4, 2, :) == [(a="12",), (a="22",)]
    @test inedgevals(g4, 3, :) == []
    @test inedgevals(g4, 4, :) == [(a="14",)]

    g3 = ValDiGraph{UInt8}(3, edgeval_types=(x=String, y=Tuple{Int, Int}))
    add_edge!(g3, 1, 2, (x="12", y=(1,2)))
    add_edge!(g3, 2, 3, (x="23", y=(2,3)))
    add_edge!(g3, 3, 2, (x="32", y=(3,2)))
    add_edge!(g3, 3, 3, (x="33", y=(3,3)))
    g3 = DummyValGraph(g3)

    @test inedgevals(g3, 1, 1) == []
    @test inedgevals(g3, 2, 1) == ["12", "32"]
    @test inedgevals(g3, 3, 1) == ["23", "33"]
    @test inedgevals(g3, 1, 2) == []
    @test inedgevals(g3, 2, 2) == [(1,2), (3,2)]
    @test inedgevals(g3, 3, 2) == [(2,3), (3,3)]

    @test inedgevals(g3, 1, :x) == []
    @test inedgevals(g3, 2, :x) == ["12", "32"]
    @test inedgevals(g3, 3, :x) == ["23", "33"]
    @test inedgevals(g3, 1, :y) == []
    @test inedgevals(g3, 2, :y) == [(1,2), (3,2)]
    @test inedgevals(g3, 3, :y) == [(2,3), (3,3)]

    @test inedgevals(g3, 1, :) == []
    @test inedgevals(g3, 2, :) == [(x="12", y=(1,2)), (x="32", y=(3,2))]
    @test inedgevals(g3, 3, :) == [(x="23", y=(2,3)), (x="33", y=(3,3))]
end

@testset "get_vertexval" begin

    g0 = DummyValGraph(ValDiGraph(2));

    g1 = DummyValGraph(ValGraph(2;
        vertexval_types=(Int64, ),
        vertexval_init=v -> (v, )
    ))

    g2 = DummyValGraph(ValOutDiGraph(2;
        vertexval_types=(a=Int64, b=String),
        vertexval_init=v -> (a=v, b="$v")
    ))

    @test get_vertexval(g0, 1, :) == ()
    @test get_vertexval(g0, 2, :) == ()
    @test get_vertexval(g1, 1, :) == (1,)
    @test get_vertexval(g1, 2, :) == (2,)
    @test get_vertexval(g2, 1, :) == (a=1, b="1")
    @test get_vertexval(g2, 2, :) == (a=2, b="2")

    @test get_vertexval(g1, 1, 1) == 1
    @test get_vertexval(g1, 2, 1) == 2
    @test get_vertexval(g2, 1, 1) == 1
    @test get_vertexval(g2, 2, 1) == 2
    @test get_vertexval(g2, 1, 2) == "1"
    @test get_vertexval(g2, 2, 2) == "2"

    @test get_vertexval(g2, 1, :a) == 1
    @test get_vertexval(g2, 2, :a) == 2
    @test get_vertexval(g2, 1, :b) == "1"
    @test get_vertexval(g2, 2, :b) == "2"

    @test get_vertexval(g1, 1) == 1
    @test get_vertexval(g1, 2) == 2
end

@testset "set_vertexval!" begin

    g0 = DummyValGraph(ValDiGraph(2));

    g1 = DummyValGraph(ValGraph(Int8(2);
        vertexval_types=(Int64, ),
        vertexval_init=v -> (v, )
    ))

    g2 = DummyValGraph(ValOutDiGraph(UInt16(2);
        vertexval_types=(a=Int64, b=String),
        vertexval_init=v -> (a=v, b="$v")
    ))

    @test set_vertexval!(g0, 2, :, ()) == true
    @test set_vertexval!(g0, 0, :, ()) == false

    @test set_vertexval!(g1, 1, 11) == true
    @test get_vertexval(g1, 1) == 11
    @test get_vertexval(g1, 2) == 2
    @test set_vertexval!(g1, 2, 22) == true
    @test get_vertexval(g1, 1) == 11
    @test get_vertexval(g1, 2) == 22
    @test set_vertexval!(g1, 3, 33) == false
    @test get_vertexval(g1, 1) == 11
    @test get_vertexval(g1, 2) == 22

    @test set_vertexval!(g1, 1, :, (111,)) == true
    @test get_vertexval(g1, 1) == 111
    @test get_vertexval(g1, 2) == 22
    @test set_vertexval!(g1, 0, :, (44,)) == false
    @test get_vertexval(g1, 1) == 111
    @test get_vertexval(g1, 2) == 22

    @test set_vertexval!(g2, 1, :a, 11) == true
    @test get_vertexval(g2, 1, :) == (a=11, b="1")
    @test get_vertexval(g2, 2, :) == (a=2, b="2")
    @test set_vertexval!(g2, 2, :b, "22") == true
    @test get_vertexval(g2, 1, :) == (a=11, b="1")
    @test get_vertexval(g2, 2, :) == (a=2, b="22")
    @test set_vertexval!(g2, 3, :b, "33") == false
    @test get_vertexval(g2, 1, :) == (a=11, b="1")
    @test get_vertexval(g2, 2, :) == (a=2, b="22")
    @test set_vertexval!(g2, 1, :, (a=111, b="111")) == true
    @test get_vertexval(g2, 1, :) == (a=111, b="111")
    @test get_vertexval(g2, 2, :) == (a=2, b="22")
end

@testset "get_edgeval" begin

    g_simple = SimpleGraph(Edge.([(1, 1), (2, 3)]))
    g_simple_di = SimpleDiGraph(Edge.([(1, 1), (1, 2), (3, 4), (4, 3)]))

    g0 = DummyValGraph(ValGraph(g_simple))
    g1 = DummyValGraph(
        ValGraph(g_simple;
                 edgeval_types=(a=Int64, b=String),
                 edgeval_init=(s, d) ->(a=s, b="$d")
    ))
    g2 = DummyValGraph(
        ValDiGraph(g_simple_di;
                 edgeval_types=(Int64,),
                 edgeval_init=(s, d) ->(s,)
    ))

    @test get_edgeval(g0, 1, 1, :) == ()
    @test get_edgeval(g0, 2, 3, :) == ()
    @test get_edgeval(g0, 3, 2, :) == ()
    @test get_edgeval(g1, 1, 1, :) == (a=1, b="1")
    @test get_edgeval(g1, 2, 3, :) == (a=2, b="3")
    @test get_edgeval(g1, 3, 2, :) == (a=2, b="3")
    @test get_edgeval(g2, 1, 1, :) == (1,)
    @test get_edgeval(g2, 1, 2, :) == (1,)
    @test get_edgeval(g2, 3, 4, :) == (3,)
    @test get_edgeval(g2, 4, 3, :) == (4,)

    @test get_edgeval(g1, 1, 1, 1) == 1
    @test get_edgeval(g1, 2, 3, 1) == 2
    @test get_edgeval(g1, 3, 2, 1) == 2
    @test get_edgeval(g1, 1, 1, 2) == "1"
    @test get_edgeval(g1, 2, 3, 2) == "3"
    @test get_edgeval(g1, 3, 2, 2) == "3"
    @test get_edgeval(g2, 1, 1, 1) == 1
    @test get_edgeval(g2, 1, 2, 1) == 1
    @test get_edgeval(g2, 3, 4, 1) == 3
    @test get_edgeval(g2, 4, 3, 1) == 4

    @test get_edgeval(g1, 1, 1, :a) == 1
    @test get_edgeval(g1, 2, 3, :a) == 2
    @test get_edgeval(g1, 3, 2, :a) == 2
    @test get_edgeval(g1, 1, 1, :b) == "1"
    @test get_edgeval(g1, 2, 3, :b) == "3"
    @test get_edgeval(g1, 3, 2, :b) == "3"

    @test get_edgeval(g2, 1, 1) == 1
    @test get_edgeval(g2, 1, 2) == 1
    @test get_edgeval(g2, 3, 4) == 3
    @test get_edgeval(g2, 4, 3) == 4
end

@testset "get_edgeval_or" begin

    g_simple = SimpleGraph(Edge.([(1, 1), (2, 3)]))
    g_simple_di = SimpleDiGraph(Edge.([(1, 1), (1, 2), (3, 4), (4, 3)]))

    g0 = DummyValGraph(ValGraph(g_simple))
    g1 = DummyValGraph(
        ValGraph(g_simple;
                 edgeval_types=(a=Int64, b=String),
                 edgeval_init=(s, d) ->(a=s, b="$d")
    ))
    g2 = DummyValGraph(
        ValDiGraph(g_simple_di;
                 edgeval_types=(Int64,),
                 edgeval_init=(s, d) ->(s,)
    ))

    @test get_edgeval_or(g0, 1, 1, :, nothing) == ()
    @test get_edgeval_or(g0, 2, 3, :, nothing) == ()
    @test get_edgeval_or(g0, 3, 2, :, "xyz") == ()
    @test get_edgeval_or(g0, 2, 2, :, nothing) == nothing
    @test get_edgeval_or(g0, 1, 3, :, "xyz") == "xyz"
    @test get_edgeval_or(g0, 1, 10, :, "xyz") == "xyz"

    @test get_edgeval_or(g1, 1, 1, :, nothing) == (a=1, b="1")
    @test get_edgeval_or(g1, 2, 3, :, nothing) == (a=2, b="3")
    @test get_edgeval_or(g1, 3, 2, :, "xyz") == (a=2, b="3")
    @test get_edgeval_or(g1, 3, 3, :, "xyz") == "xyz"
    @test get_edgeval_or(g1, 1, 3, :, nothing) == nothing
    @test get_edgeval_or(g1, 0, 3, :, nothing) == nothing

    @test get_edgeval_or(g2, 1, 1, :, nothing) == (1,)
    @test get_edgeval_or(g2, 1, 2, :, nothing) == (1,)
    @test get_edgeval_or(g2, 3, 4, :, "xyz") == (3,)
    @test get_edgeval_or(g2, 4, 3, :, "xyz") == (4,)
    @test get_edgeval_or(g2, 3, 4, :, nothing) == (3,)
    @test get_edgeval_or(g2, 4, 3, :, nothing) == (4,)
    @test get_edgeval_or(g2, 2, 1, :, nothing) == nothing
    @test get_edgeval_or(g2, 2, 2, :, "xyz") == "xyz"
    @test get_edgeval_or(g2, 2, 10, :, "xyz") == "xyz"

    @test get_edgeval_or(g1, 1, 1, 1, nothing) == 1
    @test get_edgeval_or(g1, 2, 3, 1, nothing) == 2
    @test get_edgeval_or(g1, 3, 2, 1, "xyz") == 2
    @test get_edgeval_or(g1, 2, 2, 1, nothing) == nothing
    @test get_edgeval_or(g1, 3, 1, 1, "xyz") == "xyz"
    @test get_edgeval_or(g1, 3, 10, 1, "xyz") == "xyz"

    @test get_edgeval_or(g1, 1, 1, 2, nothing) == "1"
    @test get_edgeval_or(g1, 2, 3, 2, "xyz") == "3"
    @test get_edgeval_or(g1, 3, 2, 2, "xyz") == "3"
    @test get_edgeval_or(g1, 3, 3, 2, nothing) == nothing
    @test get_edgeval_or(g1, 1, 3, 2, "xyz") == "xyz"
    @test get_edgeval_or(g1, 0, 3, 2, "xyz") == "xyz"

    @test get_edgeval_or(g2, 1, 1, 1, nothing) == 1
    @test get_edgeval_or(g2, 1, 2, 1, nothing) == 1
    @test get_edgeval_or(g2, 3, 4, 1, "xyz") == 3
    @test get_edgeval_or(g2, 4, 3, 1, "xyz") == 4
    @test get_edgeval_or(g2, 2, 2, 1, nothing) == nothing
    @test get_edgeval_or(g2, 3, 3, 1, "xyz") == "xyz"
    @test get_edgeval_or(g2, 0, 10, 1, "xyz") == "xyz"

    @test get_edgeval_or(g1, 1, 1, :a, nothing) == 1
    @test get_edgeval_or(g1, 2, 3, :a, nothing) == 2
    @test get_edgeval_or(g1, 3, 2, :a, nothing) == 2
    @test get_edgeval_or(g1, 1, 1, :b, "xyz") == "1"
    @test get_edgeval_or(g1, 2, 3, :b, "xyz") == "3"
    @test get_edgeval_or(g1, 3, 2, :b, "xyz") == "3"
    @test get_edgeval_or(g1, 2, 2, :b, nothing) == nothing
    @test get_edgeval_or(g1, 1, 2, :b, "xyz") == "xyz"
    @test get_edgeval_or(g1, 0, 0, :b, "xyz") == "xyz"

    @test get_edgeval_or(g2, 1, 1, nothing) == 1
    @test get_edgeval_or(g2, 1, 2, nothing) == 1
    @test get_edgeval_or(g2, 3, 4, "xyz") == 3
    @test get_edgeval_or(g2, 4, 3, "xyz") == 4
    @test get_edgeval_or(g2, 2, 1, nothing) == nothing
    @test get_edgeval_or(g2, 1, 3, "xyz") == "xyz"
    @test get_edgeval_or(g2, 10, 10, "xyz") == "xyz"
end

@testset "set_edgeval!" begin

    g_simple = SimpleGraph(Edge.([(1, 1), (2, 3)]))
    g_simple_di = SimpleDiGraph(Edge.([(1, 1), (1, 2), (3, 4), (4, 3)]))

    g0 = DummyValGraph(ValGraph(g_simple))
    g1 = DummyValGraph(
        ValGraph(g_simple;
                 edgeval_types=(a=Int64, b=String),
                 edgeval_init=(s, d) ->(a=s, b="$d")
    ))
    g2 = DummyValGraph(
        ValDiGraph(g_simple_di;
                 edgeval_types=(Int64,),
                 edgeval_init=(s, d) ->(s,)
    ))

    @test set_edgeval!(g0, 1, 1, :, ()) == true
    @test set_edgeval!(g0, 2, 3, :, ()) == true
    @test set_edgeval!(g0, 3, 2, :, ()) == true
    @test set_edgeval!(g0, 1, 3, :, ()) == false
    @test set_edgeval!(g0, 3, 3, :, ()) == false

    @test set_edgeval!(g1, 1, 1, :a, 11) == true
    @test outedgevals(g1, 1, :) == [(a=11, b="1")]
    @test outedgevals(g1, 2, :) == [(a=2, b="3")]
    @test outedgevals(g1, 3, :) == [(a=2, b="3")]
    @test set_edgeval!(g1, 2, 3, :b, "33") == true
    @test outedgevals(g1, 1, :) == [(a=11, b="1")]
    @test outedgevals(g1, 2, :) == [(a=2, b="33")]
    @test outedgevals(g1, 3, :) == [(a=2, b="33")]
    @test set_edgeval!(g1, 1, 2, :b, "22") == false
    @test outedgevals(g1, 1, :) == [(a=11, b="1")]
    @test outedgevals(g1, 2, :) == [(a=2, b="33")]
    @test outedgevals(g1, 3, :) == [(a=2, b="33")]
    @test set_edgeval!(g1, 3, 2, :, (a=222, b="333")) == true
    @test outedgevals(g1, 1, :) == [(a=11, b="1")]
    @test outedgevals(g1, 2, :) == [(a=222, b="333")]
    @test outedgevals(g1, 3, :) == [(a=222, b="333")]

    @test set_edgeval!(g2, 3, 4, 33) == true
    @test outedgevals(g2, 1) == [1, 1]
    @test outedgevals(g2, 2) == []
    @test outedgevals(g2, 3) == [33]
    @test outedgevals(g2, 4) == [4]
    @test set_edgeval!(g2, 1, 3, 11) == false
    @test outedgevals(g2, 1) == [1, 1]
    @test outedgevals(g2, 2) == []
    @test outedgevals(g2, 3) == [33]
    @test outedgevals(g2, 4) == [4]

end

@testset "get_graphval" begin

    g1 = DummyValGraph(ValGraph(1, graphvals=(1, "2")))
    g2 = DummyValGraph(ValDiGraph(2, graphvals=(x=1, y="2")))
    g3 = DummyValGraph(ValOutDiGraph(3, graphvals=(a="1", )))
    g4 = DummyValGraph(ValOutDiGraph(Int(4)))

    @test get_graphval(g1, :) == (1, "2")

    @test get_graphval(g2, :x) == 1
    @test get_graphval(g2, :y) == "2"
    @test get_graphval(g2, :) == (x=1, y="2")

    @test get_graphval(g3) == "1"
    @test get_graphval(g3, :a) == "1"
    @test get_graphval(g3, :) == (a="1",)

    @test get_graphval(g4, :) == ()
end

@testset "set_graphval!" begin

    g1 = DummyValGraph(ValGraph(1, graphvals=(1, "2")))
    g2 = DummyValGraph(ValDiGraph(2, graphvals=(x=1, y="2")))
    g3 = DummyValGraph(ValOutDiGraph(3, graphvals=(a="1", )))
    g4 = DummyValGraph(ValOutDiGraph(Int(4)))

    set_graphval!(g1, :, (10, "20"))
    @test get_graphval(g1, :) == (10, "20")

    set_graphval!(g2, :x , 10)
    @test get_graphval(g2, :) == (x=10, y="2")
    set_graphval!(g2, :y, "20")
    @test get_graphval(g2, :) == (x=10, y="20")
    set_graphval!(g2, :, (x=100, y="200"))
    @test get_graphval(g2, :) == (x=100, y="200")

    set_graphval!(g3, "10")
    @test get_graphval(g3) == "10"
    set_graphval!(g3, :, (a="100",))
    @test get_graphval(g3) == "100"
    set_graphval!(g3, :a, "1000")
    @test get_graphval(g3) == "1000"

    set_graphval!(g4, :, ())
    @test get_graphval(g4, :) == ()
end

@testset "add_edge!(g, s, d, val=...)" begin

    ga = DummyValGraph(ValGraph{UInt64}(4, edgeval_types=(a=Int64,)))
    gb = DummyValGraph(ValDiGraph{Int8}(2, edgeval_types=(String,)))

    add_edge!(ga, 1, 2, val=100)
    @test ne(ga) == 1
    @test get_edgeval(ga, 1, 2) == 100
    add_edge!(ga, 3, 3, val=200)
    @test ne(ga) == 2
    @test get_edgeval(ga, 3, 3) == 200

    add_edge!(gb, 1, 2, val="abc")
    @test ne(gb) == 1
    @test get_edgeval(gb, 1, 2) == "abc"
    add_edge!(gb, 2, 1, val="xyz")
    @test ne(gb) == 2
    @test get_edgeval(gb, 2, 1) == "xyz"
end

@testset "add_vertex!" begin

    ga = DummyValGraph(ValGraph{UInt64}(0, edgeval_types=(a=Int64, b=Char)))
    gb = DummyValGraph(ValDiGraph{Int8}(0))

    add_vertex!(ga)
    @test nv(ga) == 1
    add_vertex!(ga)
    @test nv(ga) == 2

    add_vertex!(gb)
    @test nv(gb) == 1
    add_vertex!(gb)
    @test nv(gb) == 2
end

@testset "add_vertex!(g, val=...)" begin

    ga = DummyValGraph(ValGraph{UInt64}(0, vertexval_types=(a=Int64,), vertexval_init=undef))
    gb = DummyValGraph(ValDiGraph{Int8}(0, vertexval_types=(String,), vertexval_init=undef))

    add_vertex!(ga, val=100)
    @test nv(ga) == 1
    @test get_vertexval(ga, 1) == 100
    add_vertex!(ga, val=200)
    @test nv(ga) == 2
    @test get_vertexval(ga, 2) == 200

    add_vertex!(gb, val="abc")
    @test nv(gb) == 1
    @test get_vertexval(gb, 1) == "abc"
    add_vertex!(gb, val="xyz")
    @test nv(gb) == 2
    @test get_vertexval(gb, 2) == "xyz"
end

end # testset


text: println("Hello from Binder!")

max_stars_repo_name: paradocs/my-first-binder

max_issues_repo_name: mattiasvillani/BLnotebooks

max_forks_repo_name: mattiasvillani/BLnotebooks


text:
import Base.inv

"""
`inv(b::Bijection)` creates a new `Bijection` that is the
inverse of `b`.
Subsequence changes to `b` will not affect `inv(b)`.

See also `active_inv`.
"""
function inv(b::Bijection{S,T}) where {S,T}
    bb = Bijection{T,S}()
    for (x,y) in b
        bb[y] = x
    end
    return bb
end

export active_inv

"""
`active_inv(b::Bijection)` creates a `Bijection` that is the
inverse of `b`. The original `b` and the new `Bijection` returned
are tied together so that changes to one immediately affect the
other. In this way, the two `Bijection`s remain inverses in
perpetuity.

See also `inv`.
"""
function active_inv(b::Bijection{S,T}) where {S,T}
    return Bijection{T,S}(b.range, b.domain, b.finv, b.f)
end