text:
using MetaGraphs
import LightGraphs: SimpleGraphs
import Base64:
        stringmime

@testset "MetaGraphs" begin

    # constructors
    @test @inferred(MetaGraph()) == MetaGraph(SimpleGraph())
    @test @inferred(MetaDiGraph()) == MetaDiGraph(SimpleDiGraph())

    @test !is_directed(MetaGraph)
    @test is_directed(MetaDiGraph)

    adjmx1 = [0 1 0; 1 0 1; 0 1 0] # SimpleGraph
    adjmx2 = [0 1 0; 1 0 1; 1 1 0] # SimpleDiGraph
    gx = PathGraph(4)
    dgx = PathDiGraph(4)

    mg = MetaGraph()
    @test add_vertex!(mg, :color, "red") && get_prop(mg, nv(mg), :color) == "red"
    @test add_vertex!(mg, Dict(:color => "red", :prop2 => "prop2")) && props(mg, nv(mg)) == Dict(:color => "red", :prop2 => "prop2")
    @test add_edge!(mg, 1, 2, :color, "blue") && get_prop(mg, 1, 2, :color) ==  "blue"
    @test add_vertex!(mg) && add_edge!(mg, 1, 3, Dict(:color => "red", :prop2 => "prop2")) && props(mg, 1, 3) == Dict(:color => "red", :prop2 => "prop2")

    for g in testgraphs(gx)
        mg = MetaGraph(g)
        g2 = SimpleGraph(mg)
        @test g2 == mg.graph

        @test eltype(@inferred(MetaGraph{UInt8,Float16}(mg))) == UInt8
        @test weighttype(@inferred(MetaGraph{UInt8,Float16}(mg))) == Float16

        @test @inferred(MetaGraphs.fadj(mg, 2)) == LightGraphs.SimpleGraphs.fadj(g, 2)
        @test @inferred(MetaGraphs.badj(mg, 2)) == LightGraphs.SimpleGraphs.badj(g, 2)

        @test @inferred(edgetype(mg)) == edgetype(mg.graph)

        @test @inferred(eltype(mg)) == eltype(g)
        @test @inferred(eltype(MetaGraph(g, 2.0))) == eltype(g)
        @test @inferred(eltype(MetaGraph(g, :cost))) == eltype(g)
        @test @inferred(eltype(MetaGraph(g, :cost, 2.0))) == eltype(g)
        @test @inferred(eltype(MetaGraph{UInt8,Float16}(g))) == UInt8
        @test @inferred(eltype(MetaGraph{UInt8,Float16}(g, :cost))) == UInt8
        @test @inferred(eltype(MetaGraph{UInt8,Float16}(g, 4))) == UInt8
        @test @inferred(ne(mg)) == 3
        @test @inferred(nv(mg)) == 4
        @test @inferred(!is_directed(mg))

        @test @inferred(vertices(mg)) == 1:4
        @test Edge(2, 3) in edges(mg)
        @test @inferred(outneighbors(mg, 2)) == inneighbors(mg, 2) == neighbors(mg, 2)
        @test @inferred(has_edge(mg, 2, 3))
        @test @inferred(has_edge(mg, 3, 2))

        mgc = copy(mg)
        @test @inferred(add_edge!(mgc, 4 => 1)) && mgc == MetaGraph(CycleGraph(4))
        @test @inferred(has_edge(mgc, 4 => 1)) && has_edge(mgc, 0x04 => 0x01)
        mgc = copy(mg)
        @test @inferred(add_edge!(mgc, (4, 1))) && mgc == MetaGraph(CycleGraph(4))
        @test @inferred(has_edge(mgc, (4, 1))) && has_edge(mgc, (0x04, 0x01))
        mgc = copy(mg)
        @test add_edge!(mgc, 4, 1) && mgc == MetaGraph(CycleGraph(4))

        @test @inferred(add_vertex!(mgc))   # out of order, but we want it for issubset
        @test @inferred(mg ⊆ mgc)
        @test @inferred(has_vertex(mgc, 5))
        @test @inferred(rem_edge!(mgc, 1, 2)) && @inferred(!has_edge(mgc, 1, 2))

        mga = @inferred(copy(mg))
        @test @inferred(rem_vertex!(mga, 2)) && ne(mga) == 1
        @test @inferred(!rem_vertex!(mga, 10))

        @test @inferred(zero(mg)) == MetaGraph{eltype(mg),weighttype(mg)}()
        @test @inferred(eltype(mg)) == eltype(outneighbors(mg, 1)) == eltype(nv(mg))
        T = @inferred(eltype(mg))
        U = @inferred(weighttype(mg))
        @test @inferred(nv(MetaGraph{T,U}(6))) == 6

    end

    for g in testdigraphs(dgx)
        mg = MetaDiGraph(g)
        g2 = SimpleDiGraph(mg)
        @test g2 == mg.graph

        @test eltype(@inferred(MetaDiGraph{UInt8,Float16}(mg))) == UInt8
        @test weighttype(@inferred(MetaDiGraph{UInt8,Float16}(mg))) == Float16

        @test @inferred(MetaGraphs.fadj(mg, 2)) == LightGraphs.SimpleGraphs.fadj(g, 2)
        @test @inferred(MetaGraphs.badj(mg, 2)) == LightGraphs.SimpleGraphs.badj(g, 2)

        @test @inferred(edgetype(mg)) == edgetype(mg.graph)

        @test @inferred(eltype(mg)) == eltype(g)
        @test @inferred(eltype(MetaDiGraph(g, 2.0))) == eltype(g)
        @test @inferred(eltype(MetaDiGraph(g, :cost))) == eltype(g)
        @test @inferred(eltype(MetaDiGraph(g, :cost, 2.0))) == eltype(g)
        @test @inferred(eltype(MetaDiGraph{UInt8,Float16}(g))) == UInt8
        @test @inferred(eltype(MetaDiGraph{UInt8,Float16}(g, :cost))) == UInt8
        @test @inferred(eltype(MetaDiGraph{UInt8,Float16}(g, 4))) == UInt8

        @test @inferred(ne(mg)) == 3
        @test @inferred(nv(mg)) == 4
        @test @inferred(is_directed(mg))

        @test @inferred(vertices(mg)) == 1:4
        @test Edge(2, 3) in edges(mg)
        @test @inferred(outneighbors(mg, 2)) == [3]
        @test @inferred(inneighbors(mg, 2)) == [1]
        @test @inferred(has_edge(mg, 2, 3))
        @test @inferred(!has_edge(mg, 3, 2))

        mgc = copy(mg)
        @test @inferred(add_edge!(mgc, 4 => 1)) && mgc == MetaDiGraph(CycleDiGraph(4))
        @test @inferred(has_edge(mgc, 4 => 1)) && has_edge(mgc, 0x04 => 0x01)
        mgc = copy(mg)
        @test @inferred(add_edge!(mgc, (4, 1))) && mgc == MetaDiGraph(CycleDiGraph(4))
        @test @inferred(has_edge(mgc, (4, 1))) && has_edge(mgc, (0x04, 0x01))
        mgc = copy(mg)
        @test add_edge!(mgc, 4, 1) && mgc == MetaDiGraph(CycleDiGraph(4))

        @test @inferred(add_vertex!(mgc))   # out of order, but we want it for issubset
        @test @inferred(mg ⊆ mgc)
        @test @inferred(has_vertex(mgc, 5))
        @test @inferred(rem_edge!(mgc, 1, 2)) && @inferred(!has_edge(mgc, 1, 2))

        mga = @inferred(copy(mg))
        @test @inferred(rem_vertex!(mga, 2)) && ne(mga) == 1
        @test @inferred(!rem_vertex!(mga, 10))

        @test @inferred(zero(mg)) == MetaDiGraph{eltype(mg),weighttype(mg)}()
        @test @inferred(eltype(mg)) == eltype(outneighbors(mg, 1)) == eltype(nv(mg))
        T = @inferred(eltype(mg))
        U = @inferred(weighttype(mg))
        @test @inferred(nv(MetaDiGraph{T,U}(6))) == 6
    end

    for gbig in [SimpleGraph(0xff), SimpleDiGraph(0xff)]
        mg = MetaGraph(gbig)
        @test @inferred(!add_vertex!(mg))    # overflow
        @test @inferred(add_vertices!(mg, 10)) < 10
    end

    gx = SimpleGraph()
    for g in testgraphs(gx)
        mg = MetaGraph(g)
        T = eltype(mg)
        U = weighttype(mg)
        @test sprint(show, mg) == "{0, 0} undirected $T metagraph with $U weights defined by :$(mg.weightfield) (default weight $(mg.defaultweight))"
        @test @inferred(add_vertices!(mg, 5)) == 5
        @test sprint(show, mg) == "{5, 0} undirected $T metagraph with $U weights defined by :$(mg.weightfield) (default weight $(mg.defaultweight))"
    end
    gx = SimpleDiGraph()
    for g in testdigraphs(gx)
        mg = MetaDiGraph(g)
        T = eltype(mg)
        U = weighttype(mg)
        @test sprint(show, mg) == "{0, 0} directed $T metagraph with $U weights defined by :$(mg.weightfield) (default weight $(mg.defaultweight))"
        @test @inferred(add_vertices!(mg, 5)) == 5
        @test sprint(show, mg) == "{5, 0} directed $T metagraph with $U weights defined by :$(mg.weightfield) (default weight $(mg.defaultweight))"
    end

    mg = MetaGraph(CompleteGraph(3), 3.0)
    @test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 3]
    @test set_prop!(mg, 1, 2, :weight, 0.2)
    @test !set_prop!(mg, 1000, 2, :weight, 0.2) # nonexistent edge
    @test set_prop!(mg, 2, 3, :weight, 1)
    @test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 2, 3]

    @test set_prop!(mg, 1, :color, "blue")
    @test set_prop!(mg, 1, :color, "blue")
    @test !set_prop!(mg, 1000, :color, "blue") # nonexistent vertex
    @test set_prop!(mg, 1, :id, 0x5)
    @test set_prop!(mg, 1, :id, 0x5)
    @test set_prop!(mg, :name, "test graph")
    @test set_prop!(mg, :name, "test graph")

    @test length(props(mg)) == 1
    @test length(props(mg, 1)) == 2
    @test length(props(mg, 1, 2)) == 1

    @test get_prop(mg, :name) == "test graph"
    @test get_prop(mg, 1, :color) == "blue"
    @test get_prop(mg, 1, 2, :weight) == 0.2

    set_prop!(mg, :del, 0x2)
    set_prop!(mg, 1, :del, 0x2)
    set_prop!(mg, 1, 2, :del, 0x2)

    rem_prop!(mg, :del)
    rem_prop!(mg, 1, :del)
    rem_prop!(mg, 1, 2, :del)
    @test_throws KeyError get_prop(mg, 1, :del)
    @test get_prop(mg, 1, :id) == 0x5
    clear_props!(mg)
    @test length(props(mg)) == 0
    clear_props!(mg, 1)
    @test length(props(mg, 1)) == 0
    clear_props!(mg, 1, 2)
    @test length(props(mg, 1, 2)) == 0

    # metagraph constructor from metadigraph
    mdg = MetaDiGraph(PathDiGraph(4), 3.0)
    set_prop!(mdg, 1, 2, :name, 12)
    set_prop!(mdg, 2, 3, :name, 23)
    set_prop!(mdg, 3, 4, :name, 34)
    set_indexing_prop!(mdg, 1, :foo, "v1")
    set_indexing_prop!(mdg, 2, :foo, "v2")
    set_indexing_prop!(mdg, 3, :foo, "v3")
    set_indexing_prop!(mdg, 4, :foo, "v4")

    dg = MetaGraph(mdg)
    set_prop!(mdg, 1, 2, :name, 99)
    @test get_prop(mdg, 1, 2, :name) == 99
    @test get_prop(dg, 1, 2, :name) == 12

    set_indexing_prop!(mdg, 1, :foo, "99")
    @test get_prop(mdg, 1, :foo) == "99"
    @test get_prop(dg, 1, :foo) == "v1"
    add_vertex!(mdg)
    @test nv(mdg) == nv(dg) + 1
    add_edge!(mdg, 1, 3)
    @test !has_edge(dg, 1, 3)
    defaultweight!(mdg, 11.11)
    @test defaultweight(dg) == 3.0

    mg = MetaDiGraph(PathDiGraph(3), 3.0)
    add_edge!(mg, 1, 3)
    @test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 3]
    @test set_prop!(mg, 1, 2, :weight, 0.2)
    @test !set_prop!(mg, 1000, 2, :weight, 0.2) # nonexistent edge
    @test set_prop!(mg, 2, 3, :weight, 1)
    @test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 2, 3]

    @test set_prop!(mg, 1, :color, "blue")
    @test !set_prop!(mg, 1000, :color, "blue") # nonexistent vertex
    @test set_prop!(mg, 1, :id, 0x5)
    @test set_prop!(mg, :name, "test graph")

    @test length(props(mg)) == 1
    @test length(props(mg, 1)) == 2
    @test length(props(mg, 1, 2)) == 1

    @test get_prop(mg, :name) == "test graph"
    @test get_prop(mg, 1, :color) == "blue"
    @test get_prop(mg, 1, 2, :weight) == 0.2

    set_prop!(mg, :del, 0x2)
    set_prop!(mg, 1, :del, 0x2)
    set_prop!(mg, 1, 2, :del, 0x2)

    @test has_prop(mg, :del)
    @test has_prop(mg, 1, :del)
    @test has_prop(mg, 1, 2, :del)
    rem_prop!(mg, :del)
    rem_prop!(mg, 1, :del)
    rem_prop!(mg, 1, 2, :del)
    @test !has_prop(mg, :del)
    @test !has_prop(mg, 1, :del)
    @test !has_prop(mg, 1, 2, :del)
    @test_throws KeyError get_prop(mg, 1, :del)
    @test get_prop(mg, 1, :id) == 0x5
    clear_props!(mg)
    @test length(props(mg)) == 0
    clear_props!(mg, 1)
    @test length(props(mg, 1)) == 0
    clear_props!(mg, 1, 2)
    @test length(props(mg, 1, 2)) == 0

    mg = MetaGraph(CompleteGraph(3), 3.0)
    mw = MetaGraphs.MetaWeights(mg)
    @test mw[1, 2] == 3.0
    @test sprint(show, mw) == stringmime("text/plain", mw) == "metaweights"
    @test size(mw) == (3, 3)
    set_prop!(mg, 1, 2, :weight, 0.2)
    set_prop!(mg, 2, 3, :weight, 1)
    @test weightfield!(mg, :cost) == :cost
    @test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 3]
    @test weightfield!(mg, :weight) == :weight
    @test enumerate_paths(dijkstra_shortest_paths(mg, 1), 3) == [1, 2, 3]

    @test set_props!(mg, 1, 2,  Dict(:color => :blue, :action => "knows"))
    @test length(props(mg, 1, 2)) == 3
    @test rem_edge!(mg, 1, 2)
    @test length(props(mg, 1, 2)) == 0
    @test set_props!(mg, Dict(:name => "testgraph", :type => "undirected"))
    @test length(props(mg)) == 2

    mg = MetaGraph(CompleteGraph(3), 3.0)
    set_prop!(mg, 1, :color, "blue")
    set_prop!(mg, 1, :id, 40)
    set_prop!(mg, 2, :color, "red")
    set_prop!(mg, 2, :id, 80)
    set_prop!(mg, 3, :color, "blue")
    set_prop!(mg, 1, 2, :weight, 0.2)
    set_prop!(mg, 2, 3, :weight, 0.6)
    set_prop!(mg, :name, "test metagraph")

    @test length(collect(filter_edges(mg, :weight))) == 2
    @test length(collect(filter_edges(mg, :weight, 0.2))) == 1
    @test length(collect(filter_vertices(mg, :color))) == 3
    @test length(collect(filter_vertices(mg, :color, "blue"))) == 2

    fv1 = filter_vertices(mg, :id)
    fv2 = filter_vertices(mg, :color, "blue")
    fe1 = filter_edges(mg, :weight)
    fe2 = filter_edges(mg, :weight, 0.6)

    i = mg[fv1]
    @test nv(i) == 2 && ne(i) == 1
    @test has_prop(i, 1, :id)
    @test has_prop(i, 2, :id)

    i = mg[fv2]
    @test nv(i) == 2 && ne(i) == 1
    @test get_prop(i, 1, :color) == "blue"
    @test get_prop(i, 2, :color) == "blue"

    i = mg[fe1]
    @test nv(i) == 3 && ne(i) == 2
    @test sum(get_prop(i, e, :weight) for e in edges(i)) == 0.8

    i = mg[fe2]
    @test nv(i) == 2 && ne(i) == 1
    @test get_prop(i, 1, :color) == "red"
    @test get_prop(i, 2, :color) == "blue"
    @test get_prop(i, 1, 2, :weight) == 0.6

    # test for #15
    mga = MetaGraph(Graph(5))
    for v in vertices(mga)
        set_prop!(mga, v, :name, string(v))
    end
    @test get_prop(mga, 1, :name) == "1"
    @test get_prop(mga, 5, :name) == "5"
    @test rem_vertex!(mga, 1)
    @test get_prop(mga, 1, :name) == "5"
    @test isempty(props(mga, 5))

    # test for #22
    mga = MetaGraph(PathGraph(4))
    set_prop!(mga, 1, 2, :name, "1, 2")
    set_prop!(mga, 2, 3, :name, "2, 3")
    set_prop!(mga, 3, 4, :name, "3, 4")
    @test rem_vertex!(mga, 2)
    @test nv(mga) == 3
    @test ne(mga) == 1
    @test length(mga.eprops) == 1  # should only be edge 2=>3
    @test props(mga, 2, 3)[:name] == "3, 4"

    mga = MetaDiGraph(PathDiGraph(4))
    set_prop!(mga, 1, 2, :name, "1, 2")
    set_prop!(mga, 2, 3, :name, "2, 3")
    set_prop!(mga, 3, 4, :name, "3, 4")
    @test rem_vertex!(mga, 2)
    @test nv(mga) == 3
    @test ne(mga) == 1
    @test length(mga.eprops) == 1 # should only be edge 3=>2
    @test props(mga, 3, 2)[:name] == "3, 4"

    # test for #32
    mga = MetaGraph(PathGraph(4))
    for j in 1:4
        set_prop!(mga, j, :prop, "node$j")
    end
    set_indexing_prop!(mga, :prop)
    add_vertex!(mga)
    set_indexing_prop!(mga, nv(mga), :prop, "node5")
    @test mga["node5", :prop] == 5
    @test get_prop(mga, 5, :prop) == "node5"

    # test for #33
    mga = MetaGraph(PathGraph(4))
    set_prop!(mga, 1, 2, :name, "1, 2")
    set_prop!(mga, 2, 3, :name, "2, 3")
    set_prop!(mga, 3, 4, :name, "3, 4")
    set_prop!(mga, 3, :v, 3)
    set_prop!(mga, 4, :v, 4)
    @test rem_vertex!(mga, nv(mga))
    @test has_prop(mga, 2, 3, :name)
    @test !has_prop(mga, 3, 4, :name)
    @test !has_prop(mga, 4, :v)
    @test has_prop(mga, 3, :v)

    # test for setting indexed props with set_prop!
    mga = MetaGraph(PathGraph(4))
    for j in 1:3
        set_indexing_prop!(mga, j, :prop, "node$j")
    end
    add_vertex!(mga)
    set_prop!(mga, nv(mga), :prop, "node5")
    set_prop!(mga, 1, :prop, "newnode1")
    @test get_prop(mga, 4, :prop) == "prop4"
    @test mga["node5", :prop] == 5
    @test get_prop(mga, 5, :prop) == "node5"
    @test get_prop(mga, 1, :prop) == "newnode1"

end

@testset "MetaIndexing" begin
    G = MetaGraph(100)
    dG = MetaDiGraph(100)

    for i in 1:100
        set_prop!(G, i, :name, "gnode_$i")
        set_prop!(dG, i, :name, "dgnode_$i")
    end

    for i in 1:100
        set_prop!(G, i, :not_unique, "$(i % 5)")
        set_prop!(dG, i, :not_unique, "$(i % 5)")
    end

    @test set_indexing_prop!(G, :name) == Set{Symbol}([:name])
    @test set_indexing_prop!(dG, :name) == Set{Symbol}([:name])
    @test_throws ErrorException set_indexing_prop!(G, :not_unique)
    @test_throws ErrorException set_indexing_prop!(dG, :not_unique)

    @test G["gnode_3", :name] == 3
    @test dG["dgnode_99", :name] == 99

    @test_throws ErrorException set_indexing_prop!(G, 4, :name, "gnode_3")
    @test_throws ErrorException set_indexing_prop!(dG, 4, :name, "dgnode_3")
    @test_throws ErrorException set_prop!(G, 4, :name, "gnode_3")
    @test_throws ErrorException set_prop!(dG, 4, :name, "dgnode_3")
    @test_throws ErrorException set_props!(G, 5, Dict(:name => "name", :other_name => "something"))
    @test_throws ErrorException set_props!(dG, 5, Dict(:name => "name", :other_name => "something"))

    @info("Ignore \"'foo1' is already in index\" warnings")
    set_indexing_prop!(G, 50, :name, "another name")
    set_indexing_prop!(G, 50, :name, "another name")

    set_indexing_prop!(G, 43, :foo, "foo1")
    set_indexing_prop!(dG, 43, :foo, "foo1")
    @test G[1, :foo] != "foo1"
    @test dG[1, :foo] != "foo1"

    set_indexing_prop!(G, 42, :foo, "bar")
    set_indexing_prop!(dG, 42, :foo, "bar")

    @test G["foo10", :foo] == 10
    @test G[12, :foo] == "foo12"
    @test G["bar", :foo] == 42
    @test G[42, :foo] == "bar"
    @test isa(G[:foo], Dict{Any,Integer})
    @test isa(dG[:foo], Dict{Any,Integer})

    @test dG["foo30", :foo] == 30
    @test dG[79, :foo] == "foo79"
    @test dG["bar", :foo] == 42
    @test dG[42, :foo] == "bar"

    @test_throws ErrorException G[:not_a_key]
    @test_throws ErrorException dG[:not_a_key]
end

@testset "Overrides" begin
    mga = MetaDiGraph(PathDiGraph(4), 8)
    set_prop!(mga, 1, 2, :name, "1, 2")
    set_prop!(mga, 2, 3, :name, "2, 3")
    set_prop!(mga, 3, 4, :name, "3, 4")
    set_indexing_prop!(mga, 1, :foo, "v1")
    set_indexing_prop!(mga, 2, :foo, "v2")
    set_indexing_prop!(mga, 3, :foo, "v3")
    set_indexing_prop!(mga, 4, :foo, "v4")

    rmg = reverse(mga)
    @test props(rmg, 1)[:foo] == rmg[1, :foo] == mga[4, :foo] == "v4"
    @test props(rmg, 2, 1) == props(mga, 1, 2)
    @test props(rmg, 3, 2) == props(mga, 2, 3)
    @test weightfield(rmg) == weightfield(mga) == :weight
    @test defaultweight(rmg) == defaultweight(mga) == 8
end


text:
using Optim

##
function get_reflectance(ftype, λ, incident, emergent)
    # Raw measured spectrum stored in Utils
    Rexp = SpectraDB.sl1_exp_spectrum(beam.λ)
    # Reference measured spectrum stored in Utils
    Rref = SpectraDB.sl1_ref_spectrum(beam.λ)
    # Theoretical reflectance spectrum for the reference
    Rthe = theoretical_spectrum(ftype, beam, incident, emergent)
    # Calculate the absolute normalised measured spectra to fit
    Rexp_norm = normalize_reflectance(beam.λ, [beam.λ Rexp], [beam.λ Rthe], [beam.λ Rref])
end
##

# Wavelength range [nm]
λ = 250:900
# Angle of incidence [degrees]
θ = [5.]
# Polarisation (1.0 = p, 0.0 = s, between 0.0 and 1.0 = average)
pol = 0.5
beam = PlaneWave(λ, θ; p=pol)

# Refractive indices of incident (0) and substrate (2)
incident = RIdb.air(beam.λ)
emergent = RIdb.silicon(beam.λ)

# Define the RI model to use
layers = [
    LayerTMMO(incident), # 1
    ModelFit(:looyenga; N=(ninc=incident, nhost=emergent)), # 2
    LayerTMMO(emergent), # 3
]

# Get the spectrum to fit
Rexp = get_reflectance(Reflectance(), beam.λ, incident, emergent)

seed = [3300, 0.85]
options = Optim.Options(
    g_abstol=1e-8, g_reltol=1e-8, iterations=10^5, store_trace=true, show_trace=false,
)

solOptim = fit_tmm_optics(
    Reflectance(Rexp), [seed], beam, layers;
    alg=SAMIN(), options=options, lb=[0.5.*seed], ub=[1.5.*seed],
)

solOptim2 = fit_tmm_optics(
    Reflectance(Rexp), [seed], beam, layers; alg=NelderMead(), options=options,
)

ext: jl


text:
function GRASP_local_search(beta::Number, prec::Dict{Int, Array}, task_times::Array{Int, 2}, bj::Array{Int,1}, CTS::Constants)
    tasks_by_w = OperationalShippingProblem(beta, task_times, bj, CTS)
    best_makespan = CTS.H
    best_LS = init_ls(CTS)
    for iter = 1:10000
        makespan = CTS.H
        LS = init_ls(CTS)
        QC = init_qc(CTS)
        TIME = init_timer(CTS)

        while LS.tasks_left > 0 && TIME.period < TIME.horizon_plan
            cranes_status = "idle"
            while cranes_status != "All cranes are busy" && cranes_status != "Next time period" && cranes_status != "LS is completed"
                cranes_status = randomizedConstructionHeuristic("dist", "dist", tasks_by_w, prec, bj, LS, TIME, QC, CTS)
            end
            next_time_period(TIME, QC, CTS);
        end

        if check_solution(prec, LS, CTS) == true
            makespan = total_makespan(LS, CTS)
            if makespan < best_makespan
                best_makespan = makespan
                best_LS = deepcopy(LS)
            end

            #perform local_search
            gamma = rand(35:5:95)/100
            for ind in ["number", "minimal"]
                new_LS = remove_tasks(1-gamma, LS, CTS)
                println(gamma)
                check, TIME, QC = get_current_state(new_LS, CTS)
                while new_LS.tasks_left > 0 && TIME.period < TIME.horizon_plan
                    cranes_status = "idle"
                    while cranes_status != "All cranes are busy" && cranes_status != "Next time period" && cranes_status != "LS is completed"
                        cranes_status = deterministicConstructionHeuristic(ind, "dist", tasks_by_w, prec, bj, new_LS, TIME, QC, CTS)
                    end
                    next_time_period(TIME, QC, CTS);
                end

                if check_solution(prec, new_LS, CTS) == true
                    makespan = total_makespan(new_LS, CTS)
                    if makespan < best_makespan
                        best_makespan = makespan
                        best_LS = deepcopy(new_LS)
                    end
                else
                    println("STOP")
                    global wrong_LS = deepcopy(new_LS)
                    return(total_makespan(LS, CTS), LS)
                    break
                end
            end

        else
            continue
        end
    end

    return(best_makespan, best_LS)
end

max_stars_repo_licenses: Zlib MIT

max_issues_repo_licenses: Zlib MIT

max_forks_repo_licenses: Zlib MIT


text:
"""
Module to define playing cards
"""
module PlayingCards

import Base: parse, string

export Card, createdeck, deal!, parse, string, suit, suitint, rank, rankint

const suits = tuple(
    "♣", "♦", "♥", "♠"
)
const ranks = tuple(
    "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"
)

# Define Card

struct Card
    value :: Int8
    function Card(value::Integer)
        @assert(0 ≤ value < 52, "value is not in 0:51")
        new(value)
    end
end

# Constructors

function Card(suitint::Int64, rankint::Int64)::Card
    @assert(1 ≤ suitint ≤ 4, "suitint is not in 1:4")
    @assert(1 ≤ rankint ≤ 13, "rankint is not in 1:3")
    return Card((suitint-1) * 13 + (rankint-1))
end

function Card(suit::AbstractString, rank::AbstractString)::Card
    suitint = findfirst(x -> x == suit, suits)
    rankint = findfirst(x -> x == rank, ranks)
    return Card(suitint, rankint)
end

function Card(s::AbstractString)::Card
    sarray = collect(s)
    rankstr = join(sarray[1:end-1])
    suitstr = string(sarray[end])
    return Card(suitstr, rankstr)
end

# Functions for a single card

"""
   suitint(card)
Returns the suit of the card as an integer ∈ 1:4
"""
function suitint(card::Card)
    return (card.value ÷ 13) + 1
end

function suit(card::Card)::String
    return suits[suitint(card)]
end

"""
    rankint(card)
Returns the rank of `card` as an integer ∈ 1:13
"""
function rankint(card::Card)
    return (card.value % 13) + 1
end

function rank(card::Card)::String
    return ranks[rankint(card)]
end

function string(card::Card)
    return "$(rank(card))$(suit(card))"
end

function parse(::Type{Card}, s::AbstractString)
    return Card(s)
end

# Functions for multiple cards

function string(cards::Union{Array{Card,1}, Tuple{Vararg{Card}}})
    return join([string(c) for c in cards], ' ')
end

function parse(::Type{Array{Card,1}}, s::AbstractString)::Array{Card,1}
    return [ Card(cardstr) for cardstr in split(s, ' ') ]
end

"""
    createdeck()
Returns an unshuffled deck of cards (as an array)
"""
function createdeck()::Array{Card,1}
    return [Card(value) for value in 0:51]::Array{Card,1}
end

"""
    deal!(deck, n, p)
Deals `n` cards to `p` players from `deck`, removing cards from `deck` in the
process
"""
function deal!(deck::Array{Card,1}, n::Integer, p::Integer)
    @assert(n * p ≤ length(deck), "cards dealt would exceed the number available")
    hands = [Array{Card}(undef, n) for _ in 1:p]
    for cardnum in 1:n, player in 1:p
            hands[player][cardnum] = pop!(deck)
    end
    return hands
end

end  # module PlayingCards


text:
import SHA

@static if VERSION < v"1.4.0-DEV"
    view_read!(io, buf::SubArray{UInt8}) = readbytes!(io, buf, sizeof(buf))
else
    view_read!(io, buf::SubArray{UInt8}) = read!(io, buf)
end

function iterate_headers(
    callback::Function,
    tar::IO;
    raw::Bool = false,
    strict::Bool = !raw,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    eof(tar) && return
    hdr = read_standard_header(tar, buf=buf)
    hdr == nothing && return
    if !raw
        globals = Dict{String,String}()
    end
    if hdr.type == :g && hdr.path == SKELETON_MAGIC
        skip_data(tar, hdr.size)
        skeleton = true
    else
        if !raw
            hdr = read_header(tar, hdr, globals=globals, buf=buf)
        end
        skeleton = false
        @goto loop
    end
    while !eof(tar)
        hdr = if raw
            read_standard_header(tar, buf=buf)
        else
            read_header(tar, globals=globals, buf=buf)
        end
    @label loop
        hdr === nothing && break
        strict && check_header(hdr)
        callback(hdr)
        if !skeleton || hdr.type in (:g, :x) ||
            hdr.path == "././@LongLink" && hdr.type in (:L, :K)
            skip_data(tar, hdr.size)
        end
    end
end

function extract_tarball(
    predicate::Function,
    tar::IO,
    root::String;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    skeleton::IO = devnull,
    copy_symlinks::Bool = false,
)
    paths = read_tarball(predicate, tar; buf=buf, skeleton=skeleton) do hdr, parts
        # get the file system version of the path
        sys_path = reduce(joinpath, init=root, parts)
        # delete anything that's there already
        ispath(sys_path) && rm(sys_path, force=true, recursive=true)
        # ensure dirname(sys_path) is a directory
        dir = dirname(sys_path)
        st = stat(dir)
        if !isdir(st)
            ispath(st) && rm(dir, force=true, recursive=true)
            mkpath(dir)
        end
        # create the path
        if hdr.type == :directory
            mkdir(sys_path)
        elseif hdr.type == :symlink
            copy_symlinks || symlink(hdr.link, sys_path)
        elseif hdr.type == :file
            read_data(tar, sys_path, size=hdr.size, buf=buf)
            exec = 0o100 & hdr.mode != 0
            tar_mode = exec ? 0o755 : 0o644
            sys_mode = filemode(sys_path)
            if exec
                # copy read bits to execute bits with
                # at least the user execute bit on
                sys_mode |= 0o100 | (sys_mode & 0o444) >> 2
                # TODO: would be better to have the system
                # create an executable with default mode but
                # we don't have a way to do that afaik
            end
            chmod(sys_path, tar_mode & sys_mode)
        else # should already be caught by check_header
            error("unsupported tarball entry type: $(hdr.type)")
        end
    end
    copy_symlinks || return

    # resolve the internal targets of symlinks
    for (path, what) in paths
        what isa AbstractString || continue
        target = link_target(paths, path, what)
        paths[path] = something(target, :symlink)
    end

    # follow chains of symlinks
    follow(seen::Vector, what::Symbol) =
        what == :symlink ? what : seen[end]
    follow(seen::Vector, what::String) =
        what in seen ? :symlink : follow(push!(seen, what), paths[what])
    for (path, what) in paths
        what isa AbstractString || continue
        paths[path] = follow([path], what)
    end

    # copies that need to be made
    copies = Pair{String,String}[]
    for (path, what) in paths
        what isa AbstractString || continue
        push!(copies, path => what)
    end
    sort!(copies, by=last)

    while !isempty(copies)
        i = 1
        while i ≤ length(copies)
            path, what = copies[i]
            # check if source is complete yet
            if any(startswith(p, "$what/") for (p, w) in copies)
                # `what` is an incomplete directory
                # need to wait for source to be complete
                i += 1
            else
                # source complete, can copy now
                deleteat!(copies, i)
                src = reduce(joinpath, init=root, split(what, '/'))
                dst = reduce(joinpath, init=root, split(path, '/'))
                cp(src, dst)
                if Sys.iswindows()
                    # our `cp` doesn't copy ACL properties, so manually set them via `chmod`
                    function copy_mode(src::String, dst::String)
                        chmod(dst, filemode(src))
                        isdir(dst) || return
                        for name in readdir(dst)
                            sub_src = joinpath(src, name)
                            sub_dst = joinpath(dst, name)
                            copy_mode(sub_src, sub_dst)
                        end
                    end
                    copy_mode(src, dst)
                end
            end
        end
    end
end

# resolve symlink target or nothing if not valid
function link_target(
    paths::Dict{String,Union{String,Symbol}},
    path::AbstractString,
    link::AbstractString,
)
    first(link) == '/' && return
    path_parts = split(path, r"/+")
    link_parts = split(link, r"/+")
    pop!(path_parts)
    part = nothing # remember the last part
    while !isempty(link_parts)
        part = popfirst!(link_parts)
        part in ("", ".") && continue
        if part == ".."
            isempty(path_parts) && return
            pop!(path_parts)
        else
            push!(path_parts, part)
            prefix = join(path_parts, '/')
            prefix in keys(paths) || return
            isempty(link_parts) && break
            what = paths[prefix]
            if what isa AbstractString
                prefix = link_target(paths, prefix, what)
                path_parts = split(prefix, '/')
            end
        end
    end
    isempty(path_parts) && return
    target = join(path_parts, '/')
    # if link ends in `/` or `.` target must be a directory
    part in ("", ".") && paths[target] != :directory && return
    # can't copy a circular link to a prefix of itself
    (path == target || startswith(path, "$target/")) && return
    return target
end

function git_tree_hash(
    predicate::Function,
    tar::IO,
    HashType::DataType,
    skip_empty::Bool;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    # build tree with leaves for files and symlinks
    tree = Dict{String,Any}()
    read_tarball(predicate, tar; buf=buf) do hdr, parts
        isempty(parts) && return
        name = pop!(parts)
        node = tree
        for part in parts
            node′ = get(node, part, nothing)
            if !(node′ isa Dict)
                node′ = node[part] = Dict{String,Any}()
            end
            node = node′
        end
        if hdr.type == :directory
            node[name] = Dict{String,Any}()
            return
        end
        if hdr.type == :symlink
            mode = "120000"
            hash = git_object_hash("blob", HashType) do io
                write(io, hdr.link)
            end
        elseif hdr.type == :file
            mode = iszero(hdr.mode & 0o100) ? "100644" : "100755"
            hash = git_file_hash(tar, hdr.size, HashType, buf=buf)
        else
            error("unsupported type for git tree hashing: $(hdr.type)")
        end
        node[name] = (mode, hash)
    end

    # prune directories that don't contain any files
    if skip_empty
        prune_empty!(node::Tuple) = true
        function prune_empty!(node::Dict)
            filter!(node) do (name, child)
                prune_empty!(child)
            end
            return !isempty(node)
        end
        prune_empty!(tree)
    end

    # reduce the tree to a single hash value
    hash_tree(node::Tuple) = node
    function hash_tree(node::Dict)
        by((name, child)) = child isa Dict ? "$name/" : name
        hash = git_object_hash("tree", HashType) do io
            for (name, child) in sort!(collect(node), by=by)
                mode, hash = hash_tree(child)
                print(io, mode, ' ', name, '\0')
                write(io, hex2bytes(hash))
            end
        end
        return "40000", hash
    end

    return hash_tree(tree)[end]
end

function git_object_hash(
    emit::Function,
    kind::AbstractString,
    HashType::DataType,
)
    ctx = HashType()
    body = codeunits(sprint(emit))
    SHA.update!(ctx, codeunits("$kind $(length(body))\0"))
    SHA.update!(ctx, body)
    return bytes2hex(SHA.digest!(ctx))
end

function git_file_hash(
    tar::IO,
    size::Integer,
    HashType::DataType;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    ctx = HashType()
    SHA.update!(ctx, codeunits("blob $size\0"))
    # TODO: this largely duplicates the logic of read_data
    # read_data could be used directly if SHA offered an interface
    # where you write data to an IO object and it maintains a hash
    padded_size = round_up(size)
    while padded_size > 0
        max_read_len = min(padded_size, length(buf))
        read_len = readbytes!(tar, buf, max_read_len)
        read_len < max_read_len && eof(tar) && throw(EOFError())
        nonpadded_view = view(buf, 1:min(read_len, size))
        SHA.update!(ctx, nonpadded_view)
        size -= length(nonpadded_view)
        padded_size -= read_len
    end
    @assert size == padded_size == 0
    return bytes2hex(SHA.digest!(ctx))
end

const SKELETON_MAGIC = "%!skeleton:\x83\xe6\xa8\xfe"

function write_skeleton_header(
    skeleton::IO;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    skeleton === devnull || write_extended_header(
        skeleton, type = :g, name = SKELETON_MAGIC, buf = buf,
        ["comment" => "Tar.jl skeleton file", "size" => "0"],
    )
end

function check_skeleton_header(
    skeleton::IO;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
)
    hdr = read_standard_header(skeleton, buf=buf)
    hdr.type == :g && hdr.path == SKELETON_MAGIC ||
        error("not a skeleton file: $skeleton")
    skip_data(skeleton, hdr.size)
end

function read_tarball(
    callback::Function,
    predicate::Function,
    tar::IO;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    skeleton::IO = devnull,
)
    write_skeleton_header(skeleton, buf=buf)
    # symbols for path types except symlinks store the link
    paths = Dict{String,Union{Symbol,String}}()
    globals = Dict{String,String}()
    while !eof(tar)
        hdr = read_header(tar, globals=globals, buf=buf, tee=skeleton)
        hdr === nothing && break
        # check if we should extract or skip
        if !predicate(hdr)
            skip_data(tar, hdr.size)
            continue
        end
        check_header(hdr)
        # normalize path and check for symlink attacks
        path = ""
        for part in split(hdr.path, '/')
            (isempty(part) || part == ".") && continue
            # check_header doesn't allow ".." in path
            get(paths, path, nothing) isa String && error("""
            Refusing to extract path with symlink prefix, possible attack
             * path to extract: $(repr(hdr.path))
             * symlink prefix: $(repr(path))
            """)
            isempty(path) || (paths[path] = :directory)
            path = isempty(path) ? part : "$path/$part"
        end
        paths[path] = hdr.type == :symlink ? hdr.link : hdr.type
        before = applicable(position, tar) ? position(tar) : 0
        callback(hdr, split(path, '/', keepempty=false))
        applicable(position, tar) || continue
        advanced = position(tar) - before
        expected = round_up(hdr.size)
        advanced == expected ||
            error("callback read $advanced bytes instead of $expected")
    end
    return paths
end

function read_header(
    io::IO;
    globals::Dict{String,String} = Dict{String,String}(),
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    tee::IO = devnull,
)
    hdr = read_standard_header(io, buf=buf, tee=tee)
    hdr === nothing && return nothing
    read_header(io, hdr, globals=globals, buf=buf, tee=tee)
end

function read_header(
    io::IO,
    hdr::Header; # initial header
    globals::Dict{String,String} = Dict{String,String}(),
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    tee::IO = devnull,
)
    # process zero or more extended headers
    metadata = copy(globals)
    while true
        if hdr.type in (:g, :x) # POSIX extended headers
            let hdr=hdr         #15276
                read_extended_metadata(io, hdr.size, buf=buf, tee=tee) do key, val
                    if key in ("size", "path", "linkpath")
                        if hdr.type == :g
                            globals[key] = val
                        end
                        metadata[key] = val
                    end
                end
            end
        elseif hdr.path == "././@LongLink" && hdr.type in (:L, :K)
            # GNU long name or link header
            data = read_data(io, size=hdr.size, buf=buf, tee=tee)
            data[end] == 0 ||
                error("malformed GNU long header (trailing `\\0` expected): " *
                      repr(String(data)))
            key = hdr.type == :L ? "path" : "linkpath"
            metadata[key] = String(@view data[1:end-1])
        else
            break # non-extension header block
        end
        hdr = read_standard_header(io, buf=buf, tee=tee)
        hdr === nothing && error("premature end of tar file")
    end
    # determine final values for size, path & link
    size = hdr.size
    if "size" in keys(metadata)
        val = metadata["size"]
        size = tryparse(UInt64, val)
        size === nothing &&
            error("invalid extended header size value: $(repr(val))")
    end
    path = get(metadata, "path", hdr.path)
    link = get(metadata, "linkpath", hdr.link)
    # construct and return Header object
    return Header(path, hdr.type, hdr.mode, size, link)
end

using Base.Checked: mul_with_overflow, add_with_overflow

function read_extended_metadata(
    callback::Function,
    io::IO,
    size::Integer;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    tee::IO = devnull,
)
    data = read_data(io, size=size, buf=buf, tee=tee)
    malformed() = error("malformed extended header metadata: $(repr(String(data)))")
    i = 0
    while i < size
        j, m = i, 0
        while j ≤ size
            byte = data[j += 1]
            byte == UInt8(' ') && break
            UInt8('0') ≤ byte ≤ UInt8('9') || malformed()
            m, fm = mul_with_overflow(m, 10)
            m, fa = add_with_overflow(m, Int(byte - UInt8('0')))
            fm | fa &&
                error("extended header record size too large: $(repr(String(data)))")
        end
        k, l = j, i + m
        while k ≤ l
            byte = data[k += 1]
            byte == UInt8('=') && break
        end
        # data[i+1:j-1] is the length in decimal
        # data[j+1:k-1] is the key string
        # data[k+1:l-1] is the value string
        # data[i] is end of previous
        # data[j] is ` ` (space)
        # data[k] is `=` (equals)
        # data[l] is `\n` (newline)
        i+1 < j < k < l || malformed()
        @assert data[j] == UInt8(' ')
        @assert data[k] == UInt8('=')
        data[l] == UInt('\n') || malformed()
        i = l # next starting point
        # pass key, value back to caller
        key = String(@view data[j+1:k-1])
        val = String(@view data[k+1:l-1])
        callback(key, val)
    end
end

function read_standard_header(
    io::IO;
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    tee::IO = devnull,
)
    header_view = view(buf, 1:512)
    view_read!(io, header_view)
    write(tee, header_view)
    if all(iszero, header_view)
        if tee !== devnull
            while !eof(io)
                r = readbytes!(io, buf)
                write(tee, view(buf, 1:r))
            end
        end
        return nothing
    end
    name    = read_header_str(header_view, 0, 100)
    mode    = read_header_int(header_view, 100, 8)
    size    = header_view[124+1] & 0x80 == 0 ?
              read_header_int(header_view, 124, 12) :
              read_header_bin(header_view, 124, 12)
    chksum  = read_header_int(header_view, 148, 8)
    type    = read_header_chr(header_view, 156)
    link    = read_header_str(header_view, 157, 100)
    version = read_header_str(header_view, 263, 2)
    prefix  = read_header_str(header_view, 345, 155)
    # check various fields
    header_view[index_range(148, 8)] .= ' ' # fill checksum field with spaces
    buf_sum = sum(header_view)
    chksum == buf_sum ||
        error("incorrect header checksum = $chksum; should be $buf_sum\n$(repr(String(header_view)))")
    occursin(r"^0* *$", version) ||
        error("unknown version string for tar file: $(repr(version))")
    path = isempty(prefix) ? name : "$prefix/$name"
    return Header(path, to_symbolic_type(type), mode, size, link)
end

round_up(size) = 512 * ((size + 511) ÷ 512)

function skip_data(tar::IO, size::Integer)
    size < 0 && throw(ArgumentError("[internal error] negative skip: $size"))
    size > 0 && skip(tar, round_up(size))
end

index_range(offset::Int, length::Int) = offset .+ (1:length)

read_header_chr(buf::AbstractVector{UInt8}, offset::Int) = Char(buf[offset+1])

function read_header_str(buf::AbstractVector{UInt8}, offset::Int, length::Int)
    r = index_range(offset, length)
    for i in r
        byte = buf[i]
        byte == 0 && return String(buf[first(r):i-1])
    end
    return String(buf[r])
end

function read_header_int(buf::AbstractVector{UInt8}, offset::Int, length::Int)
    n = UInt64(0)
    for i in index_range(offset, length)
        byte = buf[i]
        byte in (0x00, UInt8(' ')) && break
        UInt8('0') <= byte <= UInt8('7') ||
            error("invalid octal digit: $(repr(Char(byte)))")
        n <<= 3
        n |= byte - 0x30
    end
    return n
end

function read_header_bin(buf::AbstractVector{UInt8}, offset::Int, length::Int)
    n = UInt64(0)
    for i in index_range(offset, length)
        n <<= 8
        n |= buf[i]
    end
    return n
end

function read_data(
    tar::IO,
    file::IO;
    size::Integer,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    tee::IO = devnull,
)::Nothing
    padded_size = round_up(size)
    while padded_size > 0
        max_read_len = min(padded_size, length(buf))
        read_len = readbytes!(tar, buf, max_read_len)
        write(tee, view(buf, 1:read_len))
        read_len < max_read_len && eof(tar) && error("premature end of tar file")
        size -= write(file, view(buf, 1:min(read_len, size)))
        padded_size -= read_len
    end
    @assert size == padded_size == 0
    return
end

function read_data(
    tar::IO,
    file::AbstractString;
    size::Integer,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    tee::IO = devnull,
)::Nothing
    open(file, write=true) do file′
        read_data(tar, file′, size=size, buf=buf, tee=tee)
    end
end

# WARNING: this returns a view into `buf` so you must either use
# the result before making another call using `buf` or make a copy
function read_data(
    tar::IO;
    size::Integer,
    buf::Vector{UInt8} = Vector{UInt8}(undef, DEFAULT_BUFFER_SIZE),
    tee::IO = devnull,
)::AbstractVector{UInt8}
    n = round_up(size)
    length(buf) < n && resize!(buf, nextpow(2, n))
    r = readbytes!(tar, buf, n)
    write(tee, view(buf, 1:r))
    r < n && error("premature end of tar file")
    return view(buf, 1:size)
end


text:
perf_table = DataFrame()

perf_table.TestDescription = [
    "Rollout, 1 ft (s)",
    "60 mph (s)",
    "100 mph (s)",
    "150 mph (s)",
    "Rolling start, 5-60 mph (s)",
    "Top gear, 30-50 mph (s)",
    "Top gear, 50-70 mph (s)",
    "¼-mile time (s)",
    "¼-mile speed (mph)"
]

perf_table.CarAndDriver = [
    0.2,
    2.4,
    6.0,
    15.2,
    2.9,
    1.1,
    1.6,
    10.5,
    130
]

u0 = [0.0, 0, 0.0];
tspan=(0, 20.0);

condition(u,t,integrator) = u[1] - 0.3048; #150 mph in m/s
affect!(integrator) = terminate!(integrator);
cb = ContinuousCallback(condition,affect!);
rollout = solve(ODEProblem(taycan_state, u0, tspan),Rodas4(autodiff=false), saveat=1e-3, reltol=1e-9, callback=cb);
ro_t = rollout.t[end]

condition(u,t,integrator) = u[2] - 67.056; #150 mph in m/s
affect!(integrator) = terminate!(integrator);
cb = ContinuousCallback(condition,affect!);
zeroTo150 = solve(ODEProblem(taycan_state, u0, tspan),Rodas4(autodiff=false), saveat=1e-3, reltol=1e-9, callback=cb);
zeroTo150.t[end]

z60_t = zeroTo60.t[end] - ro_t
t_150 = zeroTo150.t
x_150 = [zeroTo150.u[x][1] for x in 1:length(zeroTo150.t)]
v_150 = [zeroTo150.u[x][2] for x in 1:length(zeroTo150.t)]

z100_t=t_150[findfirst((x)->x>=44.704, v_150)] - ro_t
z150_t=zeroTo150.t[end]-ro_t

z5_t=t_150[findfirst((x)->x>=2.2352, v_150)]
five60_t = zeroTo60.t[end]-z5_t

z30_t=t_150[findfirst((x)->x>=13.4112, v_150)]
z50_t=t_150[findfirst((x)->x>=22.352, v_150)]
z70_t=t_150[findfirst((x)->x>=31.2928, v_150)]

thirty50_t = z50_t-z30_t
fifty70_t = z70_t - z50_t

q_mile_t = t_150[findfirst((x)->x>=402.336, x_150)] - ro_t
q_mile_v = v_150[findfirst((x)->x>=402.336, x_150)]*2.23694

perf_table.SimulationResults = [
    ro_t,
    z60_t,
    z100_t,
    z150_t,
    five60_t,
    thirty50_t,
    fifty70_t,
    q_mile_t,
    q_mile_v
]

perf_table.AbsoluteError = perf_table.SimulationResults- perf_table.CarAndDriver
perf_table.RelativeError = (perf_table.AbsoluteError./perf_table.CarAndDriver)*100

max_stars_repo_name: CrashBurnRepeat/Taycan-Analysis

max_issues_repo_name: CrashBurnRepeat/Taycan-Analysis

max_forks_repo_name: CrashBurnRepeat/Taycan-Analysis


text:
include("euler/euler.jl")
using .Primes: prime_numbers
using Combinatorics: permutations
using BenchmarkTools
BenchmarkTools.DEFAULT_PARAMETERS.samples = 100

function compute(n::Int)::Int
    function get_permutation_numbers(used::Vector{Int}, prime_index::Int)::Int
        sub_result = 0
        for i ∈ setdiff(digits, used)
            if parse(Int, string(i, join(used[1:2]))) % primes[prime_index] == 0
                if prime_index < 7
                    sub_result += get_permutation_numbers(vcat(i, used), prime_index + 1)
                else
                    used_digits = vcat(i, used)
                    sub_result += parse(Int, join(vcat(setdiff(digits, used_digits), used_digits)))
                end
            end
        end
        return sub_result
    end
    digits = collect(0:n)
    primes = reverse(prime_numbers(17))
    result = 0
    for permutation ∈ permutations(digits, 3)
        if parse(Int, join(permutation)) % primes[10 - n] == 0
            result += get_permutation_numbers(permutation, 11 - n)
        end
    end
    return result
end

compute(9)

@benchmark compute(9)

max_stars_repo_name: Dynortice/Project-Euler

max_issues_repo_name: Dynortice/Project-Euler

max_forks_repo_name: Dynortice/Project-Euler


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: timestream_query
using AWS.Compat
using AWS.UUIDs

"""
    CancelQuery()

 Cancels a query that has been issued. Cancellation is guaranteed only if the query has not completed execution before the cancellation request was issued. Because cancellation is an idempotent operation, subsequent cancellation requests will return a CancellationMessage, indicating that the query has already been canceled.

# Required Parameters
- `QueryId`:  The id of the query that needs to be cancelled. QueryID is returned as part of QueryResult.

"""
cancel_query(QueryId; aws_config::AWSConfig=global_aws_config()) = timestream_query("CancelQuery", Dict{String, Any}("QueryId"=>QueryId); aws_config=aws_config)
cancel_query(QueryId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = timestream_query("CancelQuery", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("QueryId"=>QueryId), args)); aws_config=aws_config)

"""
    DescribeEndpoints()

DescribeEndpoints returns a list of available endpoints to make Timestream API calls against. This API is available through both Write and Query. Because Timestream’s SDKs are designed to transparently work with the service’s architecture, including the management and mapping of the service endpoints, it is not recommended that you use this API unless:   Your application uses a programming language that does not yet have SDK support   You require better control over the client-side implementation   For detailed information on how to use DescribeEndpoints, see The Endpoint Discovery Pattern and REST APIs.

"""
describe_endpoints(; aws_config::AWSConfig=global_aws_config()) = timestream_query("DescribeEndpoints"; aws_config=aws_config)
describe_endpoints(args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = timestream_query("DescribeEndpoints", args; aws_config=aws_config)

"""
    Query()

 Query is a synchronous operation that enables you to execute a query. Query will timeout after 60 seconds. You must update the default timeout in the SDK to support a timeout of 60 seconds. The result set will be truncated to 1MB. Service quotas apply. For more information, see Quotas in the Timestream Developer Guide.

# Required Parameters
- `QueryString`:  The query to be executed by Timestream.

# Optional Parameters
- `ClientToken`:  Unique, case-sensitive string of up to 64 ASCII characters that you specify when you make a Query request. Providing a ClientToken makes the call to Query idempotent, meaning that multiple identical calls have the same effect as one single call.  Your query request will fail in the following cases:    If you submit a request with the same client token outside the 5-minute idepotency window.     If you submit a request with the same client token but a change in other parameters within the 5-minute idempotency window.     After 4 hours, any request with the same client token is treated as a new request.
- `MaxRows`:  The total number of rows to return in the output. If the total number of rows available is more than the value specified, a NextToken is provided in the command's output. To resume pagination, provide the NextToken value in the starting-token argument of a subsequent command.
- `NextToken`:  A pagination token passed to get a set of results.
"""
query(QueryString; aws_config::AWSConfig=global_aws_config()) = timestream_query("Query", Dict{String, Any}("QueryString"=>QueryString, "ClientToken"=>string(uuid4())); aws_config=aws_config)
query(QueryString, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = timestream_query("Query", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("QueryString"=>QueryString, "ClientToken"=>string(uuid4())), args)); aws_config=aws_config)


text:
#--------------------------------------------------
# Vector space algebra

# Unary ops
@inline Base.:-(a::TupleVector) = map(-, a)
@inline Base.:-(a::TupleVector{n,<:Number}) where n = map(Base.:-, a)

# Binary ops
# Between arrays
@inline Base.:+(a::TupleVector, b::TupleVector) = map(∑, a, b)
@inline Base.:+(a::AbstractArray, b::TupleVector) = map(∑, a, b)
@inline Base.:+(a::TupleVector, b::AbstractArray) = map(∑, a, b)

@inline Base.:+(a::TupleVector{n,<:Number}, b::TupleVector{n,<:Number}) where n = map(Base.:+, a, b)
@inline Base.:+(a::AbstractArray{<:Number}, b::TupleVector{n,<:Number}) where n = map(Base.:+, a, b)
@inline Base.:+(a::TupleVector{n,<:Number}, b::AbstractArray{<:Number}) where n = map(Base.:+, a, b)

@inline Base.:-(a::TupleVector, b::TupleVector) = map(-, a, b)
@inline Base.:-(a::AbstractArray, b::TupleVector) = map(-, a, b)
@inline Base.:-(a::TupleVector, b::AbstractArray) = map(-, a, b)

@inline Base.:-(a::TupleVector{n,<:Number}, b::TupleVector{n,<:Number}) where n = map(Base.:-, a, b)
@inline Base.:-(a::AbstractArray{<:Number}, b::TupleVector{n,<:Number}) where n = map(Base.:-, a, b)
@inline Base.:-(a::TupleVector{n,<:Number}, b::AbstractArray{<:Number}) where n = map(Base.:-, a, b)

# Scalar-array
@inline Base.:*(a::Number, b::TupleVector{n,<:Number}) where n = broadcast(Base.:*, a, b)
@inline Base.:*(a::TupleVector{n,<:Number}, b::Number) where n = broadcast(Base.:*, a, b)

@inline Base.:*(a, b::TupleVector) = broadcast(∏, a, b)
@inline Base.:*(a::TupleVector, b) = broadcast(∏, a, b)

@inline Base.:*(a::Expr, b::TupleVector) = broadcast(∏, Ref(a), b)
@inline Base.:*(a::TupleVector, b::Expr) = broadcast(∏, a, Ref(b))

@inline Base.:*(a::Symbol, b::TupleVector) = broadcast(∏, Ref(a), b)
@inline Base.:*(a::TupleVector, b::Symbol) = broadcast(∏, a, Ref(b))

@inline Base.:/(a::TupleVector{n,<:Number}, b::Number) where n = broadcast(Base.:/, a, b)
@inline Base.:\(a::Number, b::TupleVector{n,<:Number}) where n = broadcast(Base.:\, a, b)

@inline Base.:/(a::TupleVector, b) = broadcast(/, a, b)
@inline Base.:\(a, b::TupleVector) = broadcast(\, a, b)

#--------------------------------------------------
# Vector products
@inline LinearAlgebra.dot(a::TupleVector, b::TupleVector) = _vecdot(a, b, LinearAlgebra.dot)
@inline bilinear_vecdot(a::TupleVector, b::TupleVector) = _vecdot(a, b, Base.:*)

@inline function _vecdot(a::TupleVector{S}, b::TupleVector{S}, product) where S
    if S == 0
        za, zb = zero(eltype(a)), zero(eltype(b))
    else
        # Use an actual element if there is one, to support e.g. Vector{<:Number}
        # element types for which runtime size information is required to construct
        # a zero element.
        za, zb = zero(a[1]), zero(b[1])
    end
    ret = product(za, zb) + product(za, zb)
    @inbounds @simd for j = 1:S
        ret += product(a[j], b[j])
    end
    return ret
end

#--------------------------------------------------
# Norms
@inline LinearAlgebra.norm_sqr(v::TupleVector) = mapreduce(Base.abs2, Base.:+, v; init=zero(real(eltype(v))))

@inline LinearAlgebra.norm(a::TupleVector) = _norm(a)
@generated function _norm(a::TupleVector{S}) where S
    if S == 0
        return :(zero(real(eltype(a))))
    end
    expr = :(Base.abs2(a[1]))
    for j = 2:S
        expr = :($expr + Base.abs2(a[$j]))
    end
    return quote
        $(Expr(:meta, :inline))
        @inbounds return Base.sqrt($expr)
    end
end

_norm_p0(x) = x == 0 ? zero(x) : one(x)

@inline LinearAlgebra.norm(a::TupleVector, p::Real) = _norm(a, p)
@generated function _norm(a::TupleVector{S,T}, p::Real) where {S,T}
    if S == 0
        return :(zero(real(eltype(a))))
    end
    fun = T<:Number ? :(Base.abs) : :abs
    expr = :($fun(a[1])^p)
    for j = 2:S
        expr = :($expr + $fun(a[$j])^p)
    end
    expr_p1 = :($fun(a[1]))
    for j = 2:S
        expr_p1 = :($expr_p1 + $fun(a[$j]))
    end
    return quote
        $(Expr(:meta, :inline))
        if p == Inf
            return mapreduce(Base.abs, max, a)
        elseif p == 1
            @inbounds return $expr_p1
        elseif p == 2
            return LinearAlgebra.norm(a)
        elseif p == 0
            return mapreduce(_norm_p0, $(T<:Number ? :(Base.:+) : :∑), a)
        else
            @inbounds return $(T<:Number ? :(Base.:^) : :^)($expr,$(T<:Number ? :(Base.inv) : :inv)(p))
        end
    end
end

@inline LinearAlgebra.normalize(a::TupleVector) = ∏(inv(LinearAlgebra.norm(a)),a)
@inline LinearAlgebra.normalize(a::TupleVector, p::Real) = ∏(inv(LinearAlgebra.norm(a, p)),a)

@inline LinearAlgebra.normalize!(a::TupleVector) = (a .*= inv(LinearAlgebra.norm(a)); return a)
@inline LinearAlgebra.normalize!(a::TupleVector, p::Real) = (a .*= inv(LinearAlgebra.norm(a, p)); return a)

@inline LinearAlgebra.normalize(a::TupleVector{n,<:Number}) where n = Base.:*(Base.inv(LinearAlgebra.norm(a)),a)
@inline LinearAlgebra.normalize(a::TupleVector{n,<:Number}, p::Real) where n = Base.:*(Base.inv(LinearAlgebra.norm(a, p)),a)

@inline LinearAlgebra.normalize!(a::TupleVector{n,<:Number}) where n = (a .*= Base.inv(LinearAlgebra.norm(a)); return a)
@inline LinearAlgebra.normalize!(a::TupleVector{n,<:Number}, p::Real) where n = (a .*= Base.inv(LinearAlgebra.norm(a, p)); return a)