text:
using PyPlot

"""
    bushiness_nesdistance()

This function returns a plot showing the multistage distance of trees of different height.

The bushiness of the tree is increased with increasing height of the tree and the multistage
distance is calculated.

These results are from one of the examples we worked on.
"""
function bushiness_nesdistance()
    bushns = [3, 4, 5, 6, 7, 8]
    nstdistance = [ 0.24868 0.16772 0.12339 0.09586 0.07725 0.06408;
                    0.21781 0.12874 0.08541 0.0611 0.04575 0.03653;
                    0.16249 0.08449 0.05089 0.03337 0.02382 0.01724;
                    0.11346 0.05236 0.0289 0.01813 0.01188 0.00855]

    pt = subplot2grid((1,5), (0,0), colspan=5)
    pt.spines["top"].set_visible(false) #remove the line of the box at the top
    pt.spines["right"].set_visible(false) #remove the line of the box at the right
    for i=1:size(nstdistance,1)
        pt.plot(bushns,nstdistance[i,:],linewidth=2,marker = ".",label ="Height = $i")
    end
    xlabel("Branches at each node")
    ylabel("Multistage distance")
    axis("tight") # Fit the axis tightly to the plot
    legend(loc="upper right",fancybox="true") # Create a legend of all the existing plots using their labels as names
end


text:
using LaserTypes
using Test, SafeTestsets

@testset "LaserTypes.jl" begin
    @safetestset "Setup" begin include("setup.jl") end
    @safetestset "Dimensions" begin include("dimensions.jl") end
    @safetestset "Gauss" begin include("gauss.jl") end
    @safetestset "Laguerre-Gauss" begin include("Laguerre-Gauss/laguerre-gauss.jl") end
    @safetestset "Fμν" begin include("faraday.jl") end
    @safetestset "Derived" begin include("derived.jl") end
    @safetestset "Show" begin include("show.jl") end
end

ext: jl


text:
using HDF5
using CRC32c
using Test

@testset "plain" begin

# Create a new file
fn = tempname()
f = h5open(fn, "w")
@test isopen(f)
# Write scalars
f["Float64"] = 3.2
f["Int16"] = Int16(4)
# compression of empty array (issue #246)
f["compressedempty", shuffle=(), compress=4] = Int64[]
# compression of zero-dimensional array (pull request #445)
f["compressed_zerodim", shuffle=(), compress=4] = fill(Int32(42), ())
f["bloscempty", blosc=4] = Int64[]
# Create arrays of different types
A = randn(3, 5)
write(f, "Afloat64", convert(Matrix{Float64}, A))
write(f, "Afloat32", convert(Matrix{Float32}, A))
Ai = rand(1:20, 2, 4)
write(f, "Aint8", convert(Matrix{Int8}, Ai))
f["Aint16"] = convert(Matrix{Int16}, Ai)
write(f, "Aint32", convert(Matrix{Int32}, Ai))
write(f, "Aint64", convert(Matrix{Int64}, Ai))
write(f, "Auint8", convert(Matrix{UInt8}, Ai))
write(f, "Auint16", convert(Matrix{UInt16}, Ai))

# test writing multiple variable (issue #599)
write(f, "Auint32", convert(Matrix{UInt32}, Ai), "Auint64", convert(Matrix{UInt64}, Ai))

# Arrays of bools (pull request #540)
Abool = [false, true, false]
write(f, "Abool", Abool)

salut = "Hi there"
ucode = "uniçº∂e"
write(f, "salut", salut)
write(f, "ucode", ucode)
# Manually write a variable-length string (issue #187)
let
    dtype = HDF5.Datatype(HDF5.h5t_copy(HDF5.H5T_C_S1))
    HDF5.h5t_set_size(dtype, HDF5.H5T_VARIABLE)
    HDF5.h5t_set_cset(dtype, HDF5.cset(typeof(salut)))
    dspace = dataspace(salut)
    dset = create_dataset(f, "salut-vlen", dtype, dspace)
    GC.@preserve salut begin
        HDF5.h5d_write(dset, dtype, HDF5.H5S_ALL, HDF5.H5S_ALL, HDF5.H5P_DEFAULT, [pointer(salut)])
    end
end
# Arrays of strings
salut_split = ["Hi", "there"]
write(f, "salut_split", salut_split)
salut_2d = ["Hi" "there"; "Salut" "friend"]
write(f, "salut_2d", salut_2d)
# Arrays of strings as vlen
vlen = HDF5.VLen(salut_split)
write_dataset(f, "salut_vlen", vlen)
# Arrays of scalars as vlen
vlen_int = [[3], [1], [4]]
vleni = HDF5.VLen(vlen_int)
write_dataset(f, "int_vlen", vleni)
write_attribute(f["int_vlen"], "vlen_attr", vleni)
# Empty arrays
empty = UInt32[]
write(f, "empty", empty)
# Empty strings
empty_string = ""
write(f, "empty_string", empty_string)
# Empty array of strings
empty_string_array = String[]
write(f, "empty_string_array", empty_string_array)
# Array of empty string
empty_array_of_strings = [""]
write(f, "empty_array_of_strings", empty_array_of_strings)
# attributes
species = [["N", "C"]; ["A", "B"]]
attributes(f)["species"] = species
C∞ = 42
attributes(f)["C∞"] = C∞
dset = f["salut"]
@test !isempty(dset)
label = "This is a string"
attributes(dset)["typeinfo"] = label
close(dset)
# Scalar reference values in attributes
attributes(f)["ref_test"] = HDF5.Reference(f, "empty_array_of_strings")
@test read(attributes(f)["ref_test"]) === HDF5.Reference(f, "empty_array_of_strings")
# Group
g = create_group(f, "mygroup")
# Test dataset with compression
R = rand(1:20, 20, 40);
g["CompressedA", chunk=(5, 6), shuffle=(), compress=9] = R
g["BloscA", chunk=(5, 6), shuffle=(), blosc=9] = R
close(g)
# Copy group containing dataset
copy_object(f, "mygroup", f, "mygroup2")
# Copy dataset
g = create_group(f, "mygroup3")
copy_object(f["mygroup/CompressedA"], g, "CompressedA")
copy_object(f["mygroup/BloscA"], g, "BloscA")
close(g)
# Writing hyperslabs
dset = create_dataset(f, "slab", datatype(Float64), dataspace(20, 20, 5), chunk=(5, 5, 1))
Xslab = randn(20, 20, 5)
for i = 1:5
    dset[:,:,i] = Xslab[:,:,i]
end
# More complex hyperslab and assignment with "incorrect" types (issue #34)
d = create_dataset(f, "slab2", datatype(Float64), ((10, 20), (100, 200)), chunk=(1, 1))
d[:,:] = 5
d[1,1] = 4
# 1d indexing
d = create_dataset(f, "slab3", datatype(Int), ((10,), (-1,)), chunk=(5,))
@test d[:] == zeros(Int, 10)
d[3:5] = 3:5
# Create a dataset designed to be deleted
f["deleteme"] = 17.2
close(f)
@test !isopen(f)
# Test the h5read/write interface, with attributes
W = copy(reshape(1:120, 15, 8))
Wa = Dict("a" => 1, "b" => 2)
h5write(fn, "newgroup/W", W)
h5writeattr(fn, "newgroup/W", Wa)

# Read the file back in
fr = h5open(fn)
x = read(fr, "Float64")
@test x == 3.2 && isa(x, Float64)
y = read(fr, "Int16")
@test y == 4 && isa(y, Int16)
zerodim = read(fr, "compressed_zerodim")
@test zerodim == 42 && isa(zerodim, Int32)
bloscempty = read(fr, "bloscempty")
@test bloscempty == Int64[] && isa(bloscempty, Vector{Int64})
Af32 = read(fr, "Afloat32")
@test convert(Matrix{Float32}, A) == Af32
@test eltype(Af32) == Float32
Af64 = read(fr, "Afloat64")
@test convert(Matrix{Float64}, A) == Af64
@test eltype(Af64) == Float64
@test eltype(fr["Afloat64"]) == Float64  # issue 167
Ai8 = read(fr, "Aint8")
@test Ai == Ai8
@test eltype(Ai8) == Int8
Ai16 = read(fr, "Aint16")
@test Ai == Ai16
@test eltype(Ai16) == Int16
Ai32 = read(fr, "Aint32")
@test Ai == Ai32
@test eltype(Ai32) == Int32
Ai64 = read(fr, "Aint64")
@test Ai == Ai64
@test eltype(Ai64) == Int64
Ai8 = read(fr, "Auint8")
@test Ai == Ai8
@test eltype(Ai8) == UInt8
Ai16 = read(fr, "Auint16")
@test Ai == Ai16
@test eltype(Ai16) == UInt16
Ai32 = read(fr, "Auint32")
@test Ai == Ai32
@test eltype(Ai32) == UInt32
Ai64 = read(fr, "Auint64")
@test Ai == Ai64
@test eltype(Ai64) == UInt64

Abool_read = read(fr, "Abool")
@test Abool_read == Abool
@test eltype(Abool_read) == Bool

salutr = read(fr, "salut")
@test salut == salutr
salutr = read(fr, "salut-vlen")
@test salut == salutr
ucoder = read(fr, "ucode")
@test ucode == ucoder
salut_splitr = read(fr, "salut_split")
@test salut_splitr == salut_split
salut_2dr = read(fr, "salut_2d")
@test salut_2d == salut_2dr
salut_vlenr = read(fr, "salut_vlen")
#@test salut_vlenr == salut_split
vlen_intr = read(fr, "int_vlen")
@test vlen_intr == vlen_int
vlen_attrr = read(fr["int_vlen"]["vlen_attr"])
@test vlen_attrr == vlen_int
Rr = read(fr, "mygroup/CompressedA")
@test Rr == R
Rr2 = read(fr, "mygroup2/CompressedA")
@test Rr2 == R
Rr3 = read(fr, "mygroup3/CompressedA")
@test Rr3 == R
Rr4 = read(fr, "mygroup/BloscA")
@test Rr4 == R
Rr5 = read(fr, "mygroup2/BloscA")
@test Rr5 == R
Rr6 = read(fr, "mygroup3/BloscA")
@test Rr6 == R
dset = fr["mygroup/CompressedA"]
@test HDF5.get_chunk(dset) == (5, 6)
@test HDF5.name(dset) == "/mygroup/CompressedA"
dset2 = fr["mygroup/BloscA"]
@test HDF5.get_chunk(dset2) == (5, 6)
@test HDF5.name(dset2) == "/mygroup/BloscA"
Xslabr = read(fr, "slab")
@test Xslabr == Xslab
Xslabr = h5read(fn, "slab", (:, :, :))  # issue #87
@test Xslabr == Xslab
Xslab2r = read(fr, "slab2")
target = fill(5, 10, 20)
target[1] = 4
@test Xslab2r == target
dset = fr["slab3"]
@test dset[3:5] == [3:5;]
emptyr = read(fr, "empty")
@test isempty(emptyr)
empty_stringr = read(fr, "empty_string")
@test empty_stringr == empty_string
empty_string_arrayr = read(fr, "empty_string_array")
@test empty_string_arrayr == empty_string_array
empty_array_of_stringsr = read(fr, "empty_array_of_strings")
@test empty_array_of_stringsr == empty_array_of_strings
@test read_attribute(fr, "species") == species
@test read_attribute(fr, "C∞") == C∞
dset = fr["salut"]
@test read_attribute(dset, "typeinfo") == label
close(dset)
# Test ref-based reading
Aref = fr["Afloat64"]
sel = (2:3, 1:2:5)
Asub = Aref[sel...]
@test Asub == A[sel...]
close(Aref)
# Test iteration, name, and parent
for obj in fr
    @test HDF5.filename(obj) == fn
    n = HDF5.name(obj)
    p = parent(obj)
end
# Test reading multiple vars at once
z = read(fr, "Float64", "Int16")
@test z == (3.2, 4)
@test typeof(z) == Tuple{Float64,Int16}
# Test reading entire file at once
z = read(fr)
@test z["Float64"] == 3.2
close(fr)

# Test object deletion
fr = h5open(fn, "r+")
@test haskey(fr, "deleteme")
delete_object(fr, "deleteme")
@test !haskey(fr, "deleteme")
close(fr)

# Test the h5read interface
Wr = h5read(fn, "newgroup/W")
@test Wr == W
rng = (2:3:15, 3:5)
Wr = h5read(fn, "newgroup/W", rng)
@test Wr == W[rng...]
War = h5readattr(fn, "newgroup/W")
@test War == Wa

# issue #618
# Test that invalid writes treat implicit creation as a transaction, cleaning up the partial
# operation
hid = h5open(fn, "w")
A = rand(3, 3)'
@test !haskey(hid, "A")
@test_throws ArgumentError write(hid, "A", A)
@test !haskey(hid, "A")
dset = create_dataset(hid, "attr", datatype(Int), dataspace(0))
@test !haskey(attributes(dset), "attr")
# broken test - writing attributes does not check that the stride is correct
@test_skip @test_throws ArgumentError write(dset, "attr", A)
@test !haskey(attributes(dset), "attr")
close(hid)

# more do syntax
h5open(fn, "w") do fid
    g = create_group(fid, "mygroup")
    write(g, "x", 3.2)
end

fid = h5open(fn, "r")
@test keys(fid) == ["mygroup"]
g = fid["mygroup"]
@test keys(g) == ["x"]
close(g)
close(fid)
rm(fn)

# more do syntax: atomic rename version
tmpdir = mktempdir()
outfile = joinpath(tmpdir, "test.h5")

# create a new file
h5rewrite(outfile) do fid
    g = create_group(fid, "mygroup")
    write(g, "x", 3.3)
end
@test length(readdir(tmpdir)) == 1
h5open(outfile, "r") do fid
    @test keys(fid) == ["mygroup"]
    @test keys(fid["mygroup"]) == ["x"]
end

# fail to overwrite
@test_throws ErrorException h5rewrite(outfile) do fid
    g = create_group(fid, "mygroup")
    write(g, "oops", 3.3)
    error("failed")
end
@test length(readdir(tmpdir)) == 1
h5open(outfile, "r") do fid
    @test keys(fid) == ["mygroup"]
    @test keys(fid["mygroup"]) == ["x"]
end

# overwrite
h5rewrite(outfile) do fid
    g = create_group(fid, "mygroup")
    write(g, "y", 3.3)
end
@test length(readdir(tmpdir)) == 1
h5open(outfile, "r") do fid
    @test keys(fid) == ["mygroup"]
    @test keys(fid["mygroup"]) == ["y"]
end
rm(tmpdir, recursive=true)

test_files = joinpath(@__DIR__, "test_files")

d = h5read(joinpath(test_files, "compound.h5"), "/data")
@test typeof(d[1]) == NamedTuple{(:wgt, :xyz, :uvw, :E), Tuple{Float64, Array{Float64, 1}, Array{Float64, 1}, Float64}}

# get-datasets
fn = tempname()
fd = h5open(fn, "w")
fd["level_0"] = [1,2,3]
grp = create_group(fd, "mygroup")
fd["mygroup/level_1"] = [4, 5]
grp2 = create_group(grp, "deep_group")
fd["mygroup/deep_group/level_2"] = [6.0, 7.0]
datasets = HDF5.get_datasets(fd)
@test sort(map(HDF5.name, datasets)) ==  sort(["/level_0", "/mygroup/deep_group/level_2", "/mygroup/level_1"])
close(fd)
rm(fn)

# File creation and access property lists
fid = h5open(fn, "w", userblock=1024, libver_bounds=(HDF5.H5F_LIBVER_EARLIEST, HDF5.H5F_LIBVER_LATEST))
write(fid, "intarray", [1, 2, 3])
close(fid)
h5open(fn, "r", libver_bounds=(HDF5.H5F_LIBVER_EARLIEST, HDF5.H5F_LIBVER_LATEST)) do fid
    intarray = read(fid, "intarray")
    @test intarray == [1, 2, 3]
end

# Test null terminated ASCII string (e.g. exported by h5py) #332
h5open(joinpath(test_files, "nullterm_ascii.h5"), "r") do fid
    str = read(fid["test"])
    @test str == "Hello World"
end

@test HDF5.unpad(UInt8[0x43, 0x43, 0x41], 1) == "CCA"

# Test the h5read/write interface with a filename as a first argument, when
# the file does not exist
rm(fn)
h5write(fn, "newgroup/W", W)
Wr = h5read(fn, "newgroup/W")
@test Wr == W
close(f)
rm(fn)

if !isempty(HDF5.libhdf5_hl)
    # Test direct chunk writing
    h5open(fn, "w") do f
      d = create_dataset(f, "dataset", datatype(Int), dataspace(4, 4), chunk=(2, 2))
      raw = HDF5.ChunkStorage(d)
      raw[1,1] = 0, collect(reinterpret(UInt8, [1,2,5,6]))
      raw[3,1] = 0, collect(reinterpret(UInt8, [3,4,7,8]))
      raw[1,3] = 0, collect(reinterpret(UInt8, [9,10,13,14]))
      raw[3,3] = 0, collect(reinterpret(UInt8, [11,12,15,16]))
    end

    @test h5open(fn, "r") do f
      vec(f["dataset"][:,:])
    end == collect(1:16)

    close(f)
    rm(fn)
end

# Test that switching time tracking off results in identical files
fn1 = tempname(); fn2 = tempname()
h5open(fn1, "w") do f
    f["x", track_times=false] = [1, 2, 3]
end
sleep(1)
h5open(fn2, "w") do f
    f["x", track_times=false] = [1, 2, 3]
end
@test open(crc32c, fn1) == open(crc32c, fn2)
rm(fn1); rm(fn2)

end # testset plain

@testset "complex" begin
  HDF5.enable_complex_support()

  fn = tempname()
  f = h5open(fn, "w")

  f["ComplexF64"] = 1.0 + 2.0im
  attributes(f["ComplexF64"])["ComplexInt64"] = 1im

  Acmplx = rand(ComplexF64, 3, 5)
  write(f, "Acmplx64", convert(Matrix{ComplexF64}, Acmplx))
  write(f, "Acmplx32", convert(Matrix{ComplexF32}, Acmplx))

  dset = create_dataset(f, "Acmplx64_hyperslab", datatype(Complex{Float64}), dataspace(Acmplx))
  for i in 1:size(Acmplx, 2)
    dset[:, i] = Acmplx[:,i]
  end

  HDF5.disable_complex_support()
  @test_throws ErrorException f["_ComplexF64"] = 1.0 + 2.0im
  @test_throws ErrorException write(f, "_Acmplx64", convert(Matrix{ComplexF64}, Acmplx))
  @test_throws ErrorException write(f, "_Acmplx32", convert(Matrix{ComplexF32}, Acmplx))
  HDF5.enable_complex_support()

  close(f)

  fr = h5open(fn)
  z = read(fr, "ComplexF64")
  @test z == 1.0 + 2.0im && isa(z, ComplexF64)
  z_attrs = attributes(fr["ComplexF64"])
  @test read(z_attrs["ComplexInt64"]) == 1im

  Acmplx32 = read(fr, "Acmplx32")
  @test convert(Matrix{ComplexF32}, Acmplx) == Acmplx32
  @test eltype(Acmplx32) == ComplexF32
  Acmplx64 = read(fr, "Acmplx64")
  @test convert(Matrix{ComplexF64}, Acmplx) == Acmplx64
  @test eltype(Acmplx64) == ComplexF64

  dset = fr["Acmplx64_hyperslab"]
  Acmplx64_hyperslab = zeros(eltype(dset), size(dset))
  for i in 1:size(dset, 2)
    Acmplx64_hyperslab[:,i] = dset[:,i]
  end
  @test convert(Matrix{ComplexF64}, Acmplx) == Acmplx64_hyperslab

  HDF5.disable_complex_support()
  z = read(fr, "ComplexF64")
  @test isa(z, NamedTuple{(:r, :i), Tuple{Float64, Float64}})

  Acmplx32 = read(fr, "Acmplx32")
  @test eltype(Acmplx32) == NamedTuple{(:r, :i), Tuple{Float32, Float32}}
  Acmplx64 = read(fr, "Acmplx64")
  @test eltype(Acmplx64) == NamedTuple{(:r, :i), Tuple{Float64, Float64}}

  close(fr)

  HDF5.enable_complex_support()
end

# test strings with null and undefined references
@testset "undefined and null" begin
fn = tempname()
f = h5open(fn, "w")

# don't silently truncate data
@test_throws ArgumentError write(f, "test", ["hello","there","\0"])
@test_throws ArgumentError write(f, "trunc1", "\0")
@test_throws ArgumentError write(f, "trunc2", "trunc\0ateme")

# test writing uninitialized string arrays
undefstrarr = similar(Vector(1:3), String) # strs = String[#undef, #undef, #undef]
@test_throws UndefRefError write(f, "undef", undefstrarr)

close(f)
rm(fn)

end # testset null and undefined

# test writing abstract arrays
@testset "abstract arrays" begin

# test writing reinterpreted data
fn = tempname()
try
    h5open(fn, "w") do f
        data = reinterpret(UInt8, [true, false, false])
        write(f, "reinterpret array", data)
    end

    @test h5open(fn, "r") do f
        read(f, "reinterpret array")
    end == UInt8[0x01, 0x00, 0x00]
finally
    rm(fn)
end

# don't silently fail for arrays with a different stride
fn = tempname()
try
    data = rand(UInt16, 2, 3);
    pdv_data = PermutedDimsArray(data, (2, 1))

    @test_throws ArgumentError h5write(fn, "pdv_data", pdv_data)
finally
    rm(fn)
end

# test alignment
fn = tempname()
h5open(fn, "w", alignment=(0, 8)) do fid
    fid["x"] = zeros(10, 10)
end

end # writing abstract arrays

# issue #705
@testset "empty and 0-size arrays" begin
fn = tempname()
hfile = h5open(fn, "w")

# Write datasets with various 0-sizes
write(hfile, "empty", HDF5.EmptyArray{Int64}()) # HDF5 empty
write(hfile, "zerodim", fill(1.0π))   # 0-dimensional
write(hfile, "zerovec", zeros(0))     # 1-dimensional, size 0
write(hfile, "zeromat", zeros(0, 0))  # 2-dimensional, size 0
write(hfile, "zeromat2", zeros(0, 1)) # 2-dimensional, size 0 with non-zero axis
dempty = hfile["empty"]
dzerodim = hfile["zerodim"]
dzerovec = hfile["zerovec"]
dzeromat = hfile["zeromat"]
dzeromat2 = hfile["zeromat2"]

# Test that eltype is preserved (especially for EmptyArray)
@test eltype(dempty) == Int64
@test eltype(dzerodim) == Float64
@test eltype(dzerovec) == Float64
@test eltype(dzeromat) == Float64
@test eltype(dzeromat2) == Float64
# Test sizes are as expected
@test size(dempty) == ()
@test size(dzerovec) == (0,)
@test size(dzeromat) == (0, 0)
@test size(dzeromat2) == (0, 1)
@test HDF5.isnull(dempty)
@test !HDF5.isnull(dzerovec)
@test !HDF5.isnull(dzeromat)
@test !HDF5.isnull(dzeromat2)
# Reading back must preserve emptiness
@test read(dempty) isa HDF5.EmptyArray
# but 0-dimensional Array{T,0} are stored as HDF5 scalar
@test size(dzerodim) == ()
@test !HDF5.isnull(dzerodim)
@test read(dzerodim) == 1.0π

# Similar tests for writing to attributes
write(dempty, "attr", HDF5.EmptyArray{Float64}())
write(dzerodim, "attr", fill(1.0ℯ))
write(dzerovec, "attr", zeros(Int64, 0))
write(dzeromat, "attr", zeros(Int64, 0, 0))
write(dzeromat2, "attr", zeros(Int64, 0, 1))
aempty = dempty["attr"]
azerodim = dzerodim["attr"]
azerovec = dzerovec["attr"]
azeromat = dzeromat["attr"]
azeromat2 = dzeromat2["attr"]
# Test that eltype is preserved (especially for EmptyArray)
@test eltype(aempty) == Float64
@test eltype(azerodim) == Float64
@test eltype(azerovec) == Int64
@test eltype(azeromat) == Int64
@test eltype(azeromat2) == Int64
# Test sizes are as expected
@test size(aempty) == ()
@test size(azerovec) == (0,)
@test size(azeromat) == (0, 0)
@test size(azeromat2) == (0, 1)
@test HDF5.isnull(aempty)
@test !HDF5.isnull(azerovec)
@test !HDF5.isnull(azeromat)
@test !HDF5.isnull(azeromat2)
# Reading back must preserve emptiness
@test read(aempty) isa HDF5.EmptyArray
# but 0-dimensional Array{T,0} are stored as HDF5 scalar
@test size(azerodim) == ()
@test !HDF5.isnull(azerodim)
@test read(azerodim) == 1.0ℯ

# Concatenation of EmptyArrays is not supported
x = HDF5.EmptyArray{Float64}()
@test_throws ErrorException [x x]
@test_throws ErrorException [x; x]
@test_throws ErrorException [x x; x x]

close(hfile)
rm(fn)

# check that printing EmptyArray doesn't error
buf = IOBuffer()
show(buf, HDF5.EmptyArray{Int64}())
@test String(take!(buf)) == "HDF5.EmptyArray{Int64}()"
show(buf, MIME"text/plain"(), HDF5.EmptyArray{Int64}())
@test String(take!(buf)) == "HDF5.EmptyArray{Int64}()"
end # empty and 0-size arrays

@testset "generic read of native types" begin
fn = tempname()
hfile = h5open(fn, "w")

dtype_varstring = HDF5.Datatype(HDF5.h5t_copy(HDF5.H5T_C_S1))
HDF5.h5t_set_size(dtype_varstring, HDF5.H5T_VARIABLE)

write(hfile, "uint8_array", UInt8[(1:8)...])
write(hfile, "bool_scalar", true)

fixstring = "fix"
varstring = "var"
write(hfile, "fixed_string", fixstring)
vardset = create_dataset(hfile, "variable_string", dtype_varstring, dataspace(varstring))
GC.@preserve varstring begin
    HDF5.h5d_write(vardset, dtype_varstring, HDF5.H5S_ALL, HDF5.H5S_ALL, HDF5.H5P_DEFAULT, [pointer(varstring)])
end
flush(hfile)
close(dtype_varstring)

# generic read() handles concrete types with definite sizes transparently
d = read(hfile["uint8_array"], UInt8)
@test d isa Vector{UInt8}
@test d == 1:8
d = read(hfile["bool_scalar"], Bool)
@test d isa Bool
@test d == true
d = read(hfile["fixed_string"], HDF5.FixedString{length(fixstring),0})
@test d isa String
@test d == fixstring
d = read(hfile["variable_string"], Cstring)
@test d isa String
@test d == varstring
# will also accept memory-compatible reinterpretations
d = read(hfile["uint8_array"], Int8)
@test d isa Vector{Int8}
@test d == 1:8
d = read(hfile["bool_scalar"], UInt8)
@test d isa UInt8
@test d == 0x1
# but should throw on non-compatible types
@test_throws ErrorException("""
                            Type size mismatch
                            sizeof(UInt16) = 2
                            sizeof($(sprint(show, datatype(UInt8)))) = 1
                            """) read(hfile["uint8_array"], UInt16)

# Strings are not fixed size, but generic read still handles them if given the correct
# underlying FixedString or Cstring type; a method overload makes String work, too.
d = read(hfile["fixed_string"], String)
@test d isa String
@test d == fixstring
d = read(hfile["variable_string"], String)
@test d isa String
@test d == varstring

close(hfile)
rm(fn)
end # generic read of native types

@testset "show" begin
fn = tempname()

# First create data objects and sure they print useful outputs

hfile = h5open(fn, "w", swmr = true)
@test sprint(show, hfile) == "HDF5.File: (read-write, swmr) $fn"

group = create_group(hfile, "group")
@test sprint(show, group) == "HDF5.Group: /group (file: $fn)"

dset = create_dataset(group, "dset", datatype(Int), dataspace((1,)))
@test sprint(show, dset) == "HDF5.Dataset: /group/dset (file: $fn xfer_mode: 0)"

meta = create_attribute(dset, "meta", datatype(Bool), dataspace((1,)))
@test sprint(show, meta) == "HDF5.Attribute: meta"

dsetattrs = attributes(dset)
@test sprint(show, dsetattrs) == "Attributes of HDF5.Dataset: /group/dset (file: $fn xfer_mode: 0)"

prop = create_property(HDF5.H5P_DATASET_CREATE)
@test sprint(show, prop) == "HDF5.Properties: dataset create class"

dtype = HDF5.Datatype(HDF5.h5t_copy(HDF5.H5T_IEEE_F64LE))
@test sprint(show, dtype) == "HDF5.Datatype: H5T_IEEE_F64LE"
commit_datatype(hfile, "type", dtype)
@test sprint(show, dtype) == "HDF5.Datatype: /type H5T_IEEE_F64LE"

dtypemeta = create_attribute(dtype, "dtypemeta", datatype(Bool), dataspace((1,)))
@test sprint(show, dtypemeta) == "HDF5.Attribute: dtypemeta"

dtypeattrs = attributes(dtype)
@test sprint(show, dtypeattrs) == "Attributes of HDF5.Datatype: /type H5T_IEEE_F64LE"

dspace_null = HDF5.Dataspace(HDF5.h5s_create(HDF5.H5S_NULL))
dspace_scal = HDF5.Dataspace(HDF5.h5s_create(HDF5.H5S_SCALAR))
dspace_norm = dataspace((100, 4))
dspace_maxd = dataspace((100, 4), max_dims = (256, 4))
dspace_slab = HDF5.hyperslab(dataspace((100, 4)), 1:20:100, 1:4)
if HDF5.libversion ≥ v"1.10.7"
dspace_irrg = HDF5.Dataspace(HDF5.h5s_combine_select(
        HDF5.h5s_copy(dspace_slab), HDF5.H5S_SELECT_OR,
        HDF5.hyperslab(dataspace((100, 4)), 2, 2)))
@test sprint(show, dspace_irrg) == "HDF5.Dataspace: (100, 4) [irregular selection]"
end
@test sprint(show, dspace_null) == "HDF5.Dataspace: H5S_NULL"
@test sprint(show, dspace_scal) == "HDF5.Dataspace: H5S_SCALAR"
@test sprint(show, dspace_norm) == "HDF5.Dataspace: (100, 4)"
@test sprint(show, dspace_maxd) == "HDF5.Dataspace: (100, 4) / (256, 4)"
@test sprint(show, dspace_slab) == "HDF5.Dataspace: (1:20:81, 1:4) / (1:100, 1:4)"

# Now test printing after closing each object

close(dspace_null)
@test sprint(show, dspace_null) == "HDF5.Dataspace: (invalid)"

close(dtype)
@test sprint(show, dtype) == "HDF5.Datatype: (invalid)"

close(prop)
@test sprint(show, prop) == "HDF5.Properties: (invalid)"

close(meta)
@test sprint(show, meta) == "HDF5.Attribute: (invalid)"

close(dtypemeta)
@test sprint(show, dtypemeta) == "HDF5.Attribute: (invalid)"

close(dset)
@test sprint(show, dset) == "HDF5.Dataset: (invalid)"
@test sprint(show, dsetattrs) == "Attributes of HDF5.Dataset: (invalid)"

close(group)
@test sprint(show, group) == "HDF5.Group: (invalid)"

close(hfile)
@test sprint(show, hfile) == "HDF5.File: (closed) $fn"

# Go back and check different access modes for file printing
hfile = h5open(fn, "r+", swmr = true)
@test sprint(show, hfile) == "HDF5.File: (read-write, swmr) $fn"
close(hfile)
hfile = h5open(fn, "r", swmr = true)
@test sprint(show, hfile) == "HDF5.File: (read-only, swmr) $fn"
close(hfile)
hfile = h5open(fn, "r")
@test sprint(show, hfile) == "HDF5.File: (read-only) $fn"
close(hfile)
hfile = h5open(fn, "cw")
@test sprint(show, hfile) == "HDF5.File: (read-write) $fn"
close(hfile)

rm(fn)

# Make an interesting file tree
hfile = h5open(fn, "w")
# file level
hfile["version"] = 1.0
attributes(hfile)["creator"] = "HDF5.jl"
# group level
create_group(hfile, "inner")
attributes(hfile["inner"])["dirty"] = true
# dataset level
hfile["inner/data"] = collect(-5:5)
attributes(hfile["inner/data"])["mode"] = 1
# non-trivial committed datatype
# TODO: print more datatype information
tmeta = HDF5.Datatype(HDF5.h5t_create(HDF5.H5T_COMPOUND, sizeof(Int) + sizeof(Float64)))
HDF5.h5t_insert(tmeta, "scale", 0, HDF5.hdf5_type_id(Int))
HDF5.h5t_insert(tmeta, "bias", sizeof(Int), HDF5.hdf5_type_id(Float64))
tstr = datatype("fixed")
t = HDF5.Datatype(HDF5.h5t_create(HDF5.H5T_COMPOUND, sizeof(tmeta) + sizeof(tstr)))
HDF5.h5t_insert(t, "meta", 0, tmeta)
HDF5.h5t_insert(t, "type", sizeof(tmeta), tstr)
commit_datatype(hfile, "dtype", t)

buf = IOBuffer()
iobuf = IOContext(buf, :limit => true, :module => Main)
show3(io::IO, x) = show(IOContext(io, iobuf), MIME"text/plain"(), x)

HDF5.show_tree(iobuf, hfile)
msg = String(take!(buf))
@test occursin(r"""
🗂 HDF5.File: .*$
├─ 🏷 creator
├─ 📄 dtype
├─ 📂 inner
│  ├─ 🏷 dirty
│  └─ 🔢 data
│     └─ 🏷 mode
└─ 🔢 version"""m, msg)
@test sprint(show3, hfile) == msg

HDF5.show_tree(iobuf, hfile, attributes = false)
@test occursin(r"""
🗂 HDF5.File: .*$
├─ 📄 dtype
├─ 📂 inner
│  └─ 🔢 data
└─ 🔢 version"""m, String(take!(buf)))

HDF5.show_tree(iobuf, attributes(hfile))
msg = String(take!(buf))
@test occursin(r"""
🗂 Attributes of HDF5.File: .*$
└─ 🏷 creator"""m, msg)
@test sprint(show3, attributes(hfile)) == msg

HDF5.show_tree(iobuf, hfile["inner"])
msg = String(take!(buf))
@test occursin(r"""
📂 HDF5.Group: /inner .*$
├─ 🏷 dirty
└─ 🔢 data
   └─ 🏷 mode"""m, msg)
@test sprint(show3, hfile["inner"]) == msg

HDF5.show_tree(iobuf, hfile["inner"], attributes = false)
@test occursin(r"""
📂 HDF5.Group: /inner .*$
└─ 🔢 data"""m, String(take!(buf)))

HDF5.show_tree(iobuf, hfile["inner/data"])
msg = String(take!(buf))
@test occursin(r"""
🔢 HDF5.Dataset: /inner/data .*$
└─ 🏷 mode"""m, msg)
# xfer_mode changes between printings, so need regex again
@test occursin(r"""
🔢 HDF5.Dataset: /inner/data .*$
└─ 🏷 mode"""m, sprint(show3, hfile["inner/data"]))

HDF5.show_tree(iobuf, hfile["inner/data"], attributes = false)
@test occursin(r"""
🔢 HDF5.Dataset: /inner/data .*$"""m, String(take!(buf)))

HDF5.show_tree(iobuf, hfile["dtype"])
@test occursin(r"""
📄 HDF5.Datatype: /dtype""", String(take!(buf)))

HDF5.show_tree(iobuf, hfile["inner/data"]["mode"], attributes = true)
@test occursin(r"""
🏷 HDF5.Attribute: mode""", String(take!(buf)))

# configurable options

# no emoji icons
HDF5.SHOW_TREE_ICONS[] = false
@test occursin(r"""
\[F\] HDF5.File: .*$
├─ \[A\] creator
├─ \[T\] dtype
├─ \[G\] inner
│  ├─ \[A\] dirty
│  └─ \[D\] data
│     └─ \[A\] mode
└─ \[D\] version"""m, sprint(show3, hfile))
HDF5.SHOW_TREE_ICONS[] = true

# no tree printing
show(IOContext(iobuf, :compact => true), MIME"text/plain"(), hfile)
msg = String(take!(buf))
@test msg == sprint(show, hfile)

close(hfile)

# Now test the print-limiting heuristics for large/complex datasets

# group with a large number of children; tests child entry truncation heuristic
h5open(fn, "w") do hfile
    dt, ds = datatype(Int), dataspace(())
    opts = Iterators.product('A':'Z', 1:9)
    for ii in opts
        create_dataset(hfile, string(ii...), dt, ds)
    end

    def = HDF5.SHOW_TREE_MAX_CHILDREN[]
    HDF5.SHOW_TREE_MAX_CHILDREN[] = 5

    HDF5.show_tree(iobuf, hfile)
    msg = String(take!(buf))
    @test occursin(r"""
🗂 HDF5.File: .*$
├─ 🔢 A1
├─ 🔢 A2
├─ 🔢 A3
├─ 🔢 A4
├─ 🔢 A5
└─ \(229 more children\)"""m, msg)
    @test sprint(show3, hfile) == msg

    HDF5.SHOW_TREE_MAX_CHILDREN[] = def

    # IOContext can halt limiting
    HDF5.show_tree(IOContext(iobuf, :limit => false), hfile)
    @test countlines(seekstart(buf)) == length(opts) + 1
    truncate(buf, 0)
end

# deeply nested set of elements; test that the tree is truncated
h5open(fn, "w") do hfile
    p = HDF5.root(hfile)::HDF5.Group
    opts = 'A':'Z'
    for ii in opts
        p = create_group(p, string(ii))
    end

    def = HDF5.SHOW_TREE_MAX_DEPTH[]
    HDF5.SHOW_TREE_MAX_DEPTH[] = 5

    HDF5.show_tree(iobuf, hfile)
    msg = String(take!(buf))
    @test occursin(r"""
🗂 HDF5.File: .*$
└─ 📂 A
   └─ 📂 B
      └─ 📂 C
         └─ 📂 D
            └─ 📂 E
               └─ \(1 child\)"""m, msg)
    @test sprint(show3, hfile) == msg

    HDF5.SHOW_TREE_MAX_DEPTH[] = def

    # IOContext can halt limiting
    HDF5.show_tree(IOContext(iobuf, :limit => false), hfile)
    @test countlines(seekstart(buf)) == length(opts) + 1
    truncate(buf, 0)
end

rm(fn)

end # show tests

@testset "split1" begin

@test HDF5.split1("/") == ("/", "")
@test HDF5.split1("a") == ("a", "")
@test HDF5.split1("/a/b/c") == ("/", "a/b/c")
@test HDF5.split1("a/b/c") == ("a", "b/c")
@test HDF5.split1(GenericString("a")) == ("a", "")
@test HDF5.split1(GenericString("/a/b/c")) == ("/", "a/b/c")
@test HDF5.split1(GenericString("a/b/c")) == ("a", "b/c")

# The following two paths have the same graphemes but different code unit structures:
# the first one is
#     <latin small letter a with circumflex> "/" <greek small leter alpha>
# while the second one is
#     "a" <combining circumflex accent> "/" <greek small letter alpha>
circa = "â" # <latin small leter a with circumflex>
acomb = "â" # "a" + <combining circumflex accent>
path1 = circa * "/α"
path2 = acomb * "/α"
# Sanity checks that the two strings are different but equivalent under normalization
@test path1 != path2
@test Base.Unicode.normalize(path1, :NFC) == Base.Unicode.normalize(path2, :NFC)
# Check split1 operates correctly
@test HDF5.split1(path1) == (circa, "α")
@test HDF5.split1(path2) == (acomb, "α")
@test HDF5.split1("/" * path1) == ("/", path1)
@test HDF5.split1("/" * path2) == ("/", path2)

end # split1 tests

# Also tests AbstractString interface
@testset "haskey" begin
fn = tempname()
hfile = h5open(fn, "w")

group1 = create_group(hfile, "group1")
group2 = create_group(group1, "group2")

@test haskey(hfile, "/")
@test haskey(hfile, GenericString("group1"))
@test !haskey(hfile, GenericString("groupna"))
@test haskey(hfile, "group1/group2")
@test !haskey(hfile, "group1/groupna")
@test_throws KeyError hfile["nothing"]

dset1 = create_dataset(hfile, "dset1", datatype(Int), dataspace((1,)))
dset2 = create_dataset(group1, "dset2", datatype(Int), dataspace((1,)))

@test haskey(hfile, "dset1")
@test !haskey(hfile, "dsetna")
@test haskey(hfile, "group1/dset2")
@test !haskey(hfile, "group1/dsetna")

meta1 = create_attribute(dset1, "meta1", datatype(Bool), dataspace((1,)))
@test haskey(dset1, "meta1")
@test !haskey(dset1, "metana")
@test_throws KeyError dset1["nothing"]

attribs = attributes(hfile)
attribs["test1"] = true
attribs["test2"] = "foo"

@test haskey(attribs, "test1")
@test haskey(attribs, "test2")
@test !haskey(attribs, "testna")
@test_throws KeyError attribs["nothing"]

attribs = attributes(dset2)
attribs["attr"] = "foo"
@test haskey(attribs, GenericString("attr"))

close(hfile)
rm(fn)
end # haskey tests

@testset "AbstractString" begin

fn = GenericString(tempname())
hfile = h5open(fn, "w")
close(hfile)
hfile = h5open(fn); close(hfile)
hfile = h5open(fn, "w")

@test_nowarn create_group(hfile, GenericString("group1"))
@test_nowarn create_dataset(hfile, GenericString("dset1"), datatype(Int), dataspace((1,)))
@test_nowarn create_dataset(hfile, GenericString("dset2"), 1)

@test_nowarn hfile[GenericString("group1")]
@test_nowarn hfile[GenericString("dset1")]

dset1 = hfile["dset1"]
@test_nowarn create_attribute(dset1, GenericString("meta1"), datatype(Bool), dataspace((1,)))
@test_nowarn create_attribute(dset1, GenericString("meta2"), 1)
@test_nowarn dset1[GenericString("meta1")]
@test_nowarn dset1[GenericString("x")] = 2

array_of_strings = ["test",]
write(hfile, "array_of_strings", array_of_strings)
@test_nowarn attributes(hfile)[GenericString("ref_test")] = HDF5.Reference(hfile, GenericString("array_of_strings"))
@test read(attributes(hfile)[GenericString("ref_test")]) === HDF5.Reference(hfile, "array_of_strings")

hfile[GenericString("test")] = 17.2
@test_nowarn delete_object(hfile, GenericString("test"))
@test_nowarn delete_attribute(dset1, GenericString("meta1"))

# transient types
memtype_id = HDF5.h5t_copy(HDF5.H5T_NATIVE_DOUBLE)
dt = HDF5.Datatype(memtype_id)
@test !HDF5.h5t_committed(dt)
commit_datatype(hfile, GenericString("dt"), dt)
@test HDF5.h5t_committed(dt)

dt = datatype(Int)
ds = dataspace(0)
d = create_dataset(hfile, GenericString("d"), dt, ds)
g = create_group(hfile, GenericString("g"))
a = create_attribute(hfile, GenericString("a"), dt, ds)

for obj in (d, g)
   @test_nowarn write_attribute(obj, GenericString("a"), 1)
   @test_nowarn read_attribute(obj, GenericString("a"))
   @test_nowarn write(obj, GenericString("aa"), 1)
   @test_nowarn attributes(obj)["attr1"] = GenericString("b")
end
@test_nowarn write(d, "attr2", GenericString("c"))
@test_nowarn write_dataset(g, GenericString("ag"), GenericString("gg"))
@test_nowarn write_dataset(g, GenericString("ag_array"), [GenericString("a1"), GenericString("a2")])

genstrs = GenericString["fee", "fi", "foo"]
@test_nowarn write_attribute(d, GenericString("myattr"), genstrs)
@test genstrs == read(d["myattr"])

for obj in (hfile,)
    @test_nowarn open_dataset(obj, GenericString("d"))
    @test_nowarn write_dataset(obj, GenericString("dd"), 1)
    @test_nowarn read_dataset(obj, GenericString("dd"))
    @test_nowarn read(obj, GenericString("dd"))
    @test_nowarn read(obj, GenericString("dd")=>Int)
end
read(attributes(hfile), GenericString("a"))

write(hfile, GenericString("ASD"), GenericString("Aa"))
write(g, GenericString("ASD"), GenericString("Aa"))
write(g, GenericString("ASD1"), [GenericString("Aa")])

# test writing multiple variable
@test_nowarn write(hfile, GenericString("a1"), rand(2,2), GenericString("a2"), rand(2,2))

# copy methods
d1 = create_dataset(hfile, GenericString("d1"), dt, ds)
d1["x"] = 32
@test_nowarn copy_object(hfile, GenericString("d1"), hfile, GenericString("d1copy1"))
@test_nowarn copy_object(d1, hfile, GenericString("d1copy2"))

fn = GenericString(tempname())
A = Matrix(reshape(1:120, 15, 8))
@test_nowarn h5write(fn, GenericString("A"), A)
@test_nowarn h5read(fn, GenericString("A"))
@test_nowarn h5read(fn, GenericString("A"), (2:3:15, 3:5))

@test_nowarn h5write(fn, GenericString("x"), 1)
@test_nowarn h5read(fn, GenericString("x") => Int)

@test_nowarn h5rewrite(fn) do fid
    g = create_group(fid, "mygroup")
    write(g, "x", 3.3)
end
@test_nowarn h5rewrite(fn) do fid
    g = create_group(fid, "mygroup")
    write(g, "y", 3.3)
end

@test_nowarn h5write(fn, "W", [1 2; 3 4])
@test_nowarn h5writeattr(fn, GenericString("W"), Dict("a" => 1, "b" => 2))
@test_nowarn h5readattr(fn, GenericString("W"))

fn_external = GenericString(tempname())
dset = HDF5.create_external_dataset(hfile, "ext", fn_external, Int, (10,20))

end

@testset "opaque data" begin
    mktemp() do path, io
        close(io)
        fid = h5open(path, "w")

        num   = 1
        olen  = 4
        otype = HDF5.Datatype(HDF5.h5t_create(HDF5.H5T_OPAQUE, olen))
        HDF5.h5t_set_tag(otype, "opaque test")

        # scalar
        dat0 = rand(UInt8, olen)
        create_dataset(fid, "scalar", otype, dataspace(()))
        write_dataset(fid["scalar"], otype, dat0)
        # vector
        dat1 = [rand(UInt8, olen) for _ in 1:4]
        buf1 = reduce(vcat, dat1)
        create_dataset(fid, "vector", otype, dataspace(dat1))
        write_dataset(fid["vector"], otype, buf1)
        # matrix
        dat2 = [rand(UInt8, olen) for _ in 1:4, _ in 1:2]
        buf2 = reduce(vcat, dat2)
        create_dataset(fid, "matrix", otype, dataspace(dat2))
        write_dataset(fid["matrix"], otype, buf2)

        # opaque data within a compound data type
        ctype = HDF5.Datatype(HDF5.h5t_create(HDF5.H5T_COMPOUND, sizeof(num) + sizeof(otype)))
        HDF5.h5t_insert(ctype, "v", 0, datatype(num))
        HDF5.h5t_insert(ctype, "d", sizeof(num), otype)
        cdat = vcat(reinterpret(UInt8, [num]), dat0)
        create_dataset(fid, "compound", ctype, dataspace(()))
        write_dataset(fid["compound"], ctype, cdat)

        opaque0 = read(fid["scalar"])
        @test opaque0.tag == "opaque test"
        @test opaque0.data == dat0
        opaque1 = read(fid["vector"])
        @test opaque1.tag == "opaque test"
        @test opaque1.data == dat1
        opaque2 = read(fid["matrix"])
        @test opaque2.tag == "opaque test"
        @test opaque2.data == dat2

        # Note: opaque tag is lost
        compound = read(fid["compound"])
        @test compound == (v = num, d = dat0)
    end
end

@testset "FixedStrings and FixedArrays" begin
    # properties for FixedString
    fix = HDF5.FixedString{4,0}((b"test"...,))
    @test length(typeof(fix)) == 4
    @test length(fix) == 4
    @test HDF5.pad(typeof(fix)) == 0
    @test HDF5.pad(fix) == 0
    # issue #742, large fixed strings are readable
    mktemp() do path, io
        close(io)
        num = Int64(9)
        ref = join('a':'z') ^ 1000
        fid = h5open(path, "w")
        # long string serialized as FixedString
        fid["longstring"] = ref

        # compound datatype containing a FixedString
        compound_dtype = HDF5.Datatype(HDF5.h5t_create(HDF5.H5T_COMPOUND, sizeof(num) + sizeof(ref)))
        HDF5.h5t_insert(compound_dtype, "n", 0, datatype(num))
        HDF5.h5t_insert(compound_dtype, "a", sizeof(num), datatype(ref))
        c = create_dataset(fid, "compoundlongstring", compound_dtype, dataspace(()))
        # normally this is done with a `struct name{N}; n::Int64; a::NTuple{N,Char}; end`,
        # but we need to not actually instantiate the `NTuple`.
        buf = IOBuffer()
        write(buf, num, ref)
        @assert position(buf) == sizeof(compound_dtype)
        write_dataset(c, compound_dtype, take!(buf))

        # Test reading without stalling
        d = fid["longstring"]
        T = HDF5.get_jl_type(d)
        @test T <: HDF5.FixedString
        @test length(T) == length(ref)
        @test read(d) == ref

        T = HDF5.get_jl_type(c)
        @test T <: NamedTuple
        @test fieldnames(T) == (:n, :a)
        @test read(c) == (n = num, a = ref)
    end

    fix = HDF5.FixedArray{Float64,(2,2),4}((1, 2, 3, 4))
    @test size(typeof(fix)) == (2, 2)
    @test size(fix) == (2, 2)
    @test eltype(typeof(fix)) == Float64
    @test eltype(fix) == Float64
    # large fixed arrays are readable
    mktemp() do path, io
        close(io)
        ref = rand(Float64, 3000)
        t = HDF5.Datatype(HDF5.h5t_array_create(datatype(Float64), ndims(ref), collect(size(ref))))
        scalarspace = dataspace(())

        fid = h5open(path, "w")
        d = create_dataset(fid, "longnums", t, scalarspace)
        write_dataset(d, t, ref)

        T = HDF5.get_jl_type(d)
        @test T <: HDF5.FixedArray
        @test size(T) == size(ref)
        @test eltype(T) == eltype(ref)
        @test read(d) == ref
    end
end

@testset "Object Exists" begin

hfile = h5open(tempname(), "w")
g1 = create_group(hfile, "group1")
@test_throws ErrorException create_group(hfile, "group1")
create_group(g1, "group1a")
@test_throws ErrorException create_group(hfile, "/group1/group1a")
@test_throws ErrorException create_group(g1, "group1a")

create_dataset(hfile, "dset1", 1)
create_dataset(hfile, "/group1/dset1", 1)

@test_throws ErrorException create_dataset(hfile, "dset1", 1)
@test_throws ErrorException create_dataset(hfile, "group1", 1)
@test_throws ErrorException create_dataset(g1, "dset1", 1)

end

@testset "HDF5 existance" begin

fn1 = tempname()
fn2 = tempname()

open(fn1, "w") do f
    write(f, "Hello text file")
end

@test !HDF5.ishdf5(fn1) # check that a non-hdf5 file retuns false
@test !HDF5.ishdf5(fn2) # checks that a file that does not exist returns false

@test_throws ErrorException h5write(fn1, "x", 1) # non hdf5 file throws
h5write(fn2, "x", 1)

@test HDF5.ishdf5(fn2)

end


text:
using JuliaWebAPI
using Test

const apiclnt = APIInvoker("tcp://127.0.0.1:9999")

@info("Open http://localhost:12000/listfiles with a web browser to download files")
run_http(apiclnt, 12000)


text:
using ExprOptimization, ExprRules
using Test

let
    grammar = @grammar begin
        R = R + R
        R = |(1:3)
    end

    p = PPT(0.75)
    ppt = PPTs.PPTNode(p, grammar)

    @test PPTs.nchildren(ppt) == 0

    PPTs.get_child(p, ppt, grammar, 1)
    @test PPTs.nchildren(ppt) == 1
    PPTs.get_child(p, ppt, grammar, 2)
    @test PPTs.nchildren(ppt) == 2

    @test all(isapprox.(PPTs.probabilities(ppt, :R), [0.1, 0.3, 0.3, 0.3]; atol=0.01))

    r = rand(p, ppt, grammar, :R)
    PPTs.probability(p, ppt, grammar, r)
    PPTs.prune!(ppt, grammar, 0.999)
end


text:
if basename(pwd()) == "aoc"
    cd("2017/18")
end

function part1(filename::AbstractString)
    lines = split.(readlines(filename))
    pc = 1
    registers = Dict{Char,Int}(c => 0 for c in 'a':'z')
    fromtoken(token) = isdigit(last(token)) ? parse(Int, token) : registers[token[1]]
    snd = nothing
    rcv = nothing
    while checkbounds(Bool, lines, pc)
        line = lines[pc]
        pc += 1
        instr = line[1]
        if instr == "snd"
            snd = fromtoken(line[2])
        elseif instr == "set"
            registers[line[2][1]] = fromtoken(line[3])
        elseif instr == "add"
            registers[line[2][1]] += fromtoken(line[3])
        elseif instr == "mul"
            registers[line[2][1]] *= fromtoken(line[3])
        elseif instr == "mod"
            registers[line[2][1]] %= fromtoken(line[3])
        elseif instr == "rcv"
            if !iszero(fromtoken(line[2]))
                return rcv = snd
            end
        elseif instr == "jgz"
            if fromtoken(line[2]) > 0
                pc += fromtoken(line[3]) - 1
            end
        end
    end
end

part1("example.txt")

function part2(filename::AbstractString)
    lines = split.(readlines(filename))
    atob = Channel{Int}()
    btoa = Channel{Int}()
    @sync begin
        a = @spawn machine(btoa, atob, lines, 0)
        b = machine(atob, btoa, lines, 1)
    end
end

function machine(input, output, lines, p = 0)
    pc = 1
    registers = Dict{Char,Int}(c => 0 for c in 'a':'z')
    registers['p'] = p
    fromtoken(token) = isdigit(last(token)) ? parse(Int, token) : registers[token[1]]
    while checkbounds(Bool, lines, pc)
        line = lines[pc]
        pc += 1
        instr = line[1]
        if instr == "snd"
            put!(output, fromtoken(line[2]))
        elseif instr == "set"
            registers[line[2][1]] = fromtoken(line[3])
        elseif instr == "add"
            registers[line[2][1]] += fromtoken(line[3])
        elseif instr == "mul"
            registers[line[2][1]] *= fromtoken(line[3])
        elseif instr == "mod"
            registers[line[2][1]] %= fromtoken(line[3])
        elseif instr == "rcv"
            registers[line[2]] = take!(input)
        elseif instr == "jgz"
            if fromtoken(line[2]) > 0
                pc += fromtoken(line[3]) - 1
            end
        end
    end
end

max_stars_repo_name: stellartux/advent-of-code

max_issues_repo_name: stellartux/advent-of-code

max_forks_repo_name: stellartux/advent-of-code


text:
module DuffingVanDerPol

using DiffEqBase: ODEProblem
using Parameters: @with_kw, @unpack
using Setfield: @lens

using ...Bifurcations: BifurcationProblem

@with_kw struct DuffingVanDerPolParam{A, B, D}
    a::A = 1.0
    b::B = 0.0
    d::D = 1.0
end

function f(du, u, p, t)
    @unpack a, b, d = p
    du[1] = u[2]
    du[2] = - a * u[1] - b * u[1]^3 - d * (u[1]^2 - 1) * u[2]
    nothing
end

make_prob(
        p = DuffingVanDerPolParam();
        u0 = [0.0, 0.0],
        tspan = (0.0, 2π),
        ode = ODEProblem(f, u0, tspan, p),
        param_axis = (@lens _.d),
        t_domain = (-2.0, 8.0),
        kwargs...) =
    BifurcationProblem(ode, param_axis, t_domain;
                       kwargs...)

prob = make_prob()
ode = prob.p.de_prob
param_axis = prob.p.param_axis
t_domain = prob.t_domain

end  # module


text:
"""
Basic GUI desktop interactions, such as opening a URL with
a web browser.

# Example:
```julia
using Desktop
if hasdesktop()
   browse_url("https://julialang.org/")
else
   @info("No desktop environment available")
end
```
"""
module Desktop

export hasdesktop, browse_url, open_file

using Base.Filesystem

# some standard Win32 API types and constants, see [MS-DTYP]
const BOOL = Cint
const DWORD = Culong
const LPDWORD = Ref{DWORD}
const HWINSTA = Ptr{Cvoid}
const UOI_NAME = 2

# auxiliary function for the Windows part of hasdesktop()
"""
    windows_station_name()

    Query the name of the “windows station” in which the current
    Win32 process is running. If this is `WinSta0`, the process
    has access to a GUI desktop.
"""
function windows_station_name()
    hwinsta = ccall((:GetProcessWindowStation, "user32.dll"),
                    stdcall, HWINSTA, ())
    Base.windowserror("GetProcessWindowStation", hwinsta == C_NULL)
    buf = zeros(UInt8, 80)
    len = LPDWORD(0)
    r = ccall((:GetUserObjectInformationA, "user32.dll"), stdcall, BOOL,
              (Ptr{Cvoid}, Cint, Ptr{Cvoid}, DWORD, LPDWORD),
              hwinsta, UOI_NAME, buf, sizeof(buf), len)
    Base.windowserror("GetUserObjectInformationA", r == 0)
    buf[end] = 0
    return unsafe_string(pointer(buf))
end

"""
    hasdesktop()

Returns `true` if the current process appears to have access to a
graphical desktop environment and is therefore likely to succeed when
invoking GUI functions or applications.

The algorithm used is a platform-dependent heuristic:

- On Microsoft Windows: tests if the current process is running in a
  “windows station” called `WinSta0`

- On macOS: checks the has-graphic-access bit in the security session
  information of the calling process

- On other platforms: checks if a non-empty environment variable
  `DISPLAY` or `WAYLAND_DISPLAY` exists

It only checks the native GUI interface of the respective platform;
e.g. an available X11 server will be ignored on Windows or macOS.
"""
function hasdesktop()
    if Sys.iswindows()
        return windows_station_name() == "WinSta0"
    elseif Sys.isapple()
        # https://developer.apple.com/documentation/security/1593382-sessiongetinfo
        callerSecuritySession = 0xffffffff
        sessionHasGraphicAccess = 16
        errSessionSuccess = 0
        attrs = Ref{Cuint}(0)
    	r = ccall((:SessionGetInfo,
	           "/System/Library/Frameworks/Security.framework/Versions/A/Security"),
	          stdcall, Cint, (Cuint, Ref{Cuint}, Ref{Cuint}),
	          callerSecuritySession, C_NULL, attrs)
        if r == errSessionSuccess
	  return (attrs[] & sessionHasGraphicAccess) != 0
        else
	  @error r
        end
    else
        return (!isempty(get(ENV, "DISPLAY", "")) ||
                !isempty(get(ENV, "WAYLAND_DISPLAY", "")))
    end
end

"""
    browse_url(url::AbstractString)

Attempts to launch a web browser to display the document available at
the provided URL or filesystem path.

The success of this function depends on access to a GUI desktop
environment.

See also: [`hasdesktop`](@ref), [`open_file`](@ref)
"""
function browse_url(url::AbstractString)
    if Sys.iswindows()
        # https://github.com/LOLBAS-Project/LOLBAS/blob/master/Archive-Old-Version/OSLibraries/Url.dll.md
        return success(`rundll32.exe url.dll,OpenURL $url`)
    elseif Sys.isapple()
    	# currently requests Safari explicitly, as e.g. Google Chrome
	# (if that's the default browser) fails to open the
	# Julia documentation index.html due to that file
	# commonly being installed with xattr com.apple.quarantine
        # https://github.com/JuliaLang/julia/issues/34275
        return success(`/usr/bin/open -a safari $url`)
    else
        for browser in [
            "/usr/bin/xdg-open",
            "/usr/bin/firefox",
            "/usr/bin/google-chrome",
        ]
            if isfile(browser)
                return success(`$browser $url`)
            end
        end
        @error "Cannot find a web browser to display $url"
    end
end

"""
    open_file(path::AbstractString)

Opens a file using a default application that the operating system
or desktop environment associates with this file type.

The success of this function may depend on access to a GUI desktop
environment.

See also: [`hasdesktop`](@ref), [`browse_url`](@ref)
"""
function open_file(path::AbstractString)
    if Sys.iswindows()
        # https://github.com/LOLBAS-Project/LOLBAS/blob/master/Archive-Old-Version/OSLibraries/Url.dll.md
        return success(`rundll32.exe url.dll,FileProtocolHandler $path`)
    elseif Sys.isapple()
        return success(`/usr/bin/open $path`)
    else
        for handler in [
            "/usr/bin/xdg-open",
            "/usr/bin/run-mailcap",
        ]
            if isfile(handler)
                return success(`$handler $path`)
            end
        end
        @error "Cannot find an application to open $path"
    end
end

# TODO: Should Julia have more functions for basic desktop interaction
# (open, print, edit a file), like an equivalent of
# https://docs.oracle.com/javase/9/docs/api/java/awt/Desktop.html
# as a Base.Desktop module?
# See also similar packages:
# http://www.davidc.net/programming/java/browsing-urls-and-opening-files
# https://github.com/GiovineItalia/Gadfly.jl/blob/master/src/open_file.jl

end # module


text:
## a time signature set event
immutable TimeSigSet <: SysMes
	time::Int
	num::Int
	den::Int
	clocks::Int
	sub::Int

	function TimeSigSet(tm::Int, num::Int, den::Int, clocks::Int, sub::Int)
		@assert 0 <= tm ;  @assert 0 < num <= 32 ;  @assert 0 <= den <= 5
		@assert 0 < clocks < 128 ;  @assert 0 < sub <= 128
		new(tm,num,den,clocks,sub)
	end
end

"""
	timeSigSet!(mt::MidiTrack, tm::Rational{Int}, num::Int, den::Int, clocks::Int, sub::Int)

Writes a time signature set event to `mt` at the current track offset plus `tm` note lengths.  `ch` {1..16} is the channel, `num` {0..32} the numerator, 2^`den` {0..5} the denomenator, while `clocks` and `sub` are the clocks and subclocks values.
"""
function timeSigSet!(mt::MidiTrack, tm::Rational{Int}, num::Int, den::Int, clocks::Int, sub::Int)
	push!(mt.ms,TimeSigSet(timm(mt,tm),num,den,clocks,sub))
	timm(mt,tm)
end

## a tempos set event
immutable TempoSet <: SysMes
	time::Int
	bpm::Int

	function TempoSet(tm::Int, bpm::Int)
		@assert 0 <= tm ;  @assert 0<bpm
		new(tm,bpm)
	end
end

"""
	tempoSet!(mt::MidiTrack, tm::Rational{Int}, bpm::Int)

Writes a tempos set event to `mt` at the current track offset plus `tm` note lengths.  `bpm` is the number of beats per minute.
"""
function tempoSet!(mt::MidiTrack, tm::Rational{Int}, bpm::Int)
	push!(mt.ms,TempoSet(timm(mt,tm),bpm))
	timm(mt,tm)
end

## a track end event
immutable TrackEnd <: SysMes
	time::Int

	function TrackEnd(tm::Int)
		@assert 0 <= tm
		new(tm)
	end
end

"""
	trackEnd!(mt::MidiTrack, tm::Rational{Int})

Writes a track end event  to `mt` at the current track offset plus `tm` note lengths.
"""
function trackEnd!(mt::MidiTrack, tm::Rational{Int})
	push!(mt.ms,TrackEnd(timm(mt,tm)))
	timm(mt,tm)
end

## a track marker event
immutable TrackMarker <: SysMes
	time::Int
	mes::ASCIIString

	function TrackMarker(tm::Int, mes::AbstractString)
		@assert 0 <= tm ;  @assert 0<length(mes)<128
		new(tm,ascii(mes))
	end
end

"""
	trackMarker!(mt::MidiTrack, tm::Rational{Int}, mes::AbstractString)

Writes a track marker event to `mt` at the current track offset plus `tm` note lengths. `mes` is the message
"""
function trackMarker!(mt::MidiTrack, tm::Rational{Int}, mes::AbstractString)
	push!(mt.ms,TrackMarker(timm(mt,tm),mes))
	timm(mt,tm)
end

## these convert ChanMes objects to byte sequences in the MIDI format

bytesev(m::TimeSigSet,tm::Int) = vcat(bytesDt(m.time-tm),bytesTimeSig(m.num,m.den,m.clocks,m.sub))

bytesev(m::TempoSet,tm::Int) = vcat(bytesDt(m.time-tm),bytesTempo(m.bpm))

bytesev(m::TrackEnd,tm::Int) = vcat(bytesDt(m.time-tm),bytesTrackEnd())

bytesev(m::TrackMarker,tm::Int) = vcat(bytesDt(m.time-tm),bytesTrackMarker(m.mes))


text:
using GeneralizedDynamicsFromData
using Plots
using HDF5
using OrderedCollections
using Dates

experiment_name = "selkov_grid_$(Dates.month(today()))_$(Dates.day(today()))"

reps = 100
neurons = [2^i for i in 1:10]
layers = [i for i in 1:10]

grid_config = [OrderedDict([
    :inputs => 2,
    :outputs => 1,
    :neurons => N,
    :layers => L,
    :non_lin => tanh,
    :initialization => Flux.glorot_normal
]) for (N, L) in Iterators.product(neurons, layers)]

problem = Dict([
    :equation => selkov,
    :parameters => [0.1 0.6],
    :u0 => Float32[1.0, 1.0],
    :tE => 50.0f0,
    :tspan => (0.0f0, 50.0f0),
    :ts => 0.1,
    :solver => Vern7,
    :loss => mse_loss
])

result = grid_experiment(problem, grid_config, reps)

max_stars_repo_name: pnieters/GeneralizedDynamicsFromData

max_issues_repo_name: pnieters/GeneralizedDynamicsFromData

max_forks_repo_name: pnieters/GeneralizedDynamicsFromData


text:
### A Pluto.jl notebook ###
# v0.17.1

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local iv = try Base.loaded_modules[Base.PkgId(Base.UUID("6e696c72-6542-2067-7265-42206c756150"), "AbstractPlutoDingetjes")].Bonds.initial_value catch; b -> missing; end
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)
        el
    end
end

# ╔═╡ c102e87a-b570-4d86-b087-3506396fc065
begin
	using PlutoUI
	import Triangulate
	import TetGen
	using SimplexGridFactory
	using ExtendableGrids
    using PlutoVista
end

# ╔═╡ 93ca4fd0-8f61-4174-b459-55f5395c0f56
md"""
# Test Notebook for [PlutoVista](https://github.com/j-fu/PlutoVista.jl)
"""

# ╔═╡ 53f55db7-225f-4717-ab62-e024211e98a2
md"""
## 1D Data

1D Data are plotted using the [plotly.js](https://plotly.com/javascript/) javascript plotting library as backend.

### plot
"""

# ╔═╡ b8a976e3-7fef-4527-ae6a-4da31c93a04f
X=0:0.01:10

# ╔═╡ 133d0c31-eea9-4200-88d2-4afdc61a16bd
md"""
The simplest way to plot 1D data is to just plot:
"""

# ╔═╡ 2e92c42c-2923-4f35-9d43-6e38eee3f8f6
plot(X,cos.(X))

# ╔═╡ 09cfb361-b897-40b1-8792-23b00151b995
md"""
More complicated plots can be combined.
"""

# ╔═╡ f44deb76-e715-477c-9e8a-dcf5cd68577f
md"""
xscale $(@bind xscale Slider(1:0.1:10,show_value=true))

yscale: $(@bind yscale Slider(1:0.1:10,show_value=true)),
"""

# ╔═╡ 60dcfcf5-391e-418f-8e7c-3a0fe94f1e0d
let
    p=PlutoVistaPlot(resolution=(500,300),titlefontsize=20)
	plot!(p,X,yscale*sin.(X*xscale);label="sin",color=:red,linestyle=:dashdot)
	plot!(p,X,cos.(X*xscale);label="cos",color=:green,linewidth=1,markertype=:star5)
	plot!(p,X,X./X[end];color=:blue,linestyle=:dash,title="test",legend=:rt,xlabel="x")
end

# ╔═╡ 76d7a50d-40da-4367-a84f-3e2324e0c78d
md"""
For changing data, we can create the plot first and fill it afterwards.
"""

# ╔═╡ c3b719cd-5134-4395-8f36-4daab6ee3bf0
p1=plot(resolution=(600,300),axisfontsize=20)

# ╔═╡ bdde2452-cf28-4d56-935c-693e6a1ca8a9
md"""
 xscale1: $(@bind xscale1 Slider(1:0.1:10,show_value=true))
"""

# ╔═╡ cf9f4d1a-ebcd-4b59-bb0c-cc0d6b3239ca
begin
	plot!(p1,X,sin.(X*xscale1);label="sin",color=:red,linestyle=:dashdot,clear=true)
	plot!(p1,X,cos.(X*xscale1);label="cos",color=:green,linestyle=:dot,clear=false,show=true,xlabel="x",ylabel="y")
end

# ╔═╡ d5240960-7759-48b8-94fd-3436f7b1573d
md"""
### Discontinuous function
"""

# ╔═╡ b4afef75-450c-45a9-af8b-87224c3591e9
begin
	Xm=collect(-1:0.01:0)
	Xp=collect(0:0.01:1)
	Xpm=vcat(Xp,[NaN],Xm)
	F=vcat(map(x->sin(4π*x),Xm),[NaN],map(x->cos(4π*x),Xp))

end

# ╔═╡ 0c8bc23a-cee1-4d6b-a63e-8f2391fc3f3c
PlutoVista.plot(Xpm,F,markertype=:star5,markercount=20)

# ╔═╡ d0ea4e21-9d8c-416e-bf68-d7522e2ece20
md"""
## 2D Data

For 2D data, the package uses  [vtk.js](https://kitware.github.io/vtk-js/index.html)
as a backend and ths us uses GPU acceleration via the WebGL interface.
"""

# ╔═╡ f15a91a4-a03b-4125-ba58-e44d9792a7e3
md"""
Let us make a triangulation using the Triangulate.jl package, plot it, define a piecewise linear function on it and plot this function.
"""

# ╔═╡ ab232244-4fe2-4ab0-a0bf-d1d9510802d2
function maketriangulation(maxarea)
     triin=Triangulate.TriangulateIO()
    triin.pointlist=Matrix{Cdouble}([-1.0 -1.0 ; 1.0 -1.0 ; 1.0 2 ; -1.0 1.0]')
    triin.segmentlist=Matrix{Cint}([1 2 ; 2 3 ; 3 4 ; 4 1; 4 2 ]')
    triin.segmentmarkerlist=Vector{Int32}([1, 2, 3, 4, 6])
    triin.regionlist=Matrix{Cdouble}([-0.9 0.9; -0.9 0.9; 1 2 ; 2.0*maxarea maxarea])
    (triout, vorout)=Triangulate.triangulate("paADQ", triin)
    triout.pointlist, triout.trianglelist,Int.(vec(triout.triangleattributelist)),triout.segmentlist,triout.segmentmarkerlist
end;

# ╔═╡ 724495e1-d501-4a03-be88-16b644938afd
md"""
Change grid resolution: $(@bind resolution Slider(10:1:100))
"""

# ╔═╡ d3ad0d4f-859d-44ac-a387-aac8d465cc6d
(pts,tris,markers,edges,edgemarkers)=maketriangulation(1/resolution^2);

# ╔═╡ 83c7bffd-16c6-4cc7-8a68-87cbd739f3f4
md"""
The grid has $(size(pts,2)) points and $(size(tris,2))  triangles.
"""

# ╔═╡ da3bdabb-b81c-4c05-90cd-aee7b209e605
md"""
### trimesh
"""

# ╔═╡ 83be4a71-4f01-4f70-9cbe-f4e9b9222428
trimesh(pts,tris;markers=markers,edges=edges,edgemarkers=edgemarkers)

# ╔═╡ 8186bd23-5727-4d87-805c-5e5c6a092535
md"""
### tricontour
"""

# ╔═╡ ee9a6fb2-3978-40f4-803b-7cb8d50b4fac
func=0.5*[sin(10*pts[1,i])*cos(10*pts[2,i]) for i=1:size(pts,2)];

# ╔═╡ c6d700ec-91a1-4ef7-a104-8574cc162b9a
tricontour(pts,tris,func;colormap=:viridis),
tricontour(pts,tris,func;colormap=:summer,levels=3)

# ╔═╡ 8b25e922-12db-4fae-8f28-65fe4faf40f3
tricontour(pts,tris,func;colormap=:hot,levels=-0.5:0.2:0.5)

# ╔═╡ dce20465-d227-4273-82b7-c6a4621942b9
md"""
Above, we have shown  three ways to specify isolines:
- to have no isolines (default)
- a fixed number of automaticaly generated isolines
- a fixed range of isolines
Colormaps can be chosen from [ColorSchemes.jl](https://juliagraphics.github.io/ColorSchemes.jl/stable/basics/#Pre-defined-schemes)
"""

# ╔═╡ 20a88415-f2b4-4806-9ffc-6be979d12d0a
data(pts,t)=0.5*[sin(10*pts[1,i]-t)*cos(10*pts[2,i]-t)+t for i=1:size(pts,2)];

# ╔═╡ 39412087-3ee3-41fb-818f-23e5396abba3
p2=tricontour(resolution=(200,200))

# ╔═╡ 68cba79c-2577-4250-b045-08e954bde4e5
md"""
shift: $(@bind shift1 Slider(-8:0.1:8,show_value=true))
"""

# ╔═╡ d3055a1a-5b55-4c43-ac09-7704186e714a
tricontour!(p2,pts,tris,data(pts,shift1),colormap=:hot,levels=5)

# ╔═╡ 387bbdee-0175-4a25-9a97-49fdb8afb7fc
md"""
Alternatively, we can use the Plotly backend:
"""

# ╔═╡ 06fb9e66-c7c0-4028-80d9-2a7e36a6626d
md"""
### contour
A contour plot lives on a rectangular grid.
"""

# ╔═╡ 732b8e6a-68e0-4229-a2f9-52abe3ee5a40
md"""
shift: $(@bind shift Slider(-8:0.1:8,show_value=true))
"""

# ╔═╡ fe7fdb00-88a1-4d24-aedd-cedb6e50120b
f(x,y)=sqrt((x-shift)^2+(y-5)^2)*cos(x)*cos(y)

# ╔═╡ 5745f449-abc7-4f9a-a439-645698b781ea
X1=-10:0.1:10

# ╔═╡ 595539f8-f14e-418f-90d6-b6040292a9b6
Y1=0:0.1:10

# ╔═╡ 2eadced9-9e4a-4fa3-aea4-5f163933cb02
contour(X1,Y1,[f(x,y) for x∈X1, y∈Y1],resolution=(600,300),levels=-20:5:20 )

# ╔═╡ d1e13f8c-7cb8-4cb5-9dda-1a9b5d6142b6
md"""
## 3D Data
"""

# ╔═╡ a2bb8861-493d-4003-8a4e-e0ea051fbb72
md"""
### tetmesh
"""

# ╔═╡ 411d8975-f67a-4236-b827-c030675db91c
function sphere_with_hole()

    builder=SimplexGridBuilder(Generator=TetGen)
    facetregion!(builder,7)
    cellregion!(builder,1)
    maxvolume!(builder,0.001)
    regionpoint!(builder,(0,0,1.5))
    sphere!(builder,(0,0,0),2.0,nref=3)

    rect3d!(builder,[-0.5,-0.5,-0.5],[0.5,0.5,0.5], facetregions=[1,2,3,4,5,6])
    holepoint!(builder,(0,0,0))

    simplexgrid(builder)
end

# ╔═╡ b1f255ac-7347-4cf0-8ca1-491713fcb6b0
g3=sphere_with_hole()

# ╔═╡ 0a363d31-5a48-49ad-aba2-bc0058ce1225
p3dgrid=tetmesh(resolution=(300,300))

# ╔═╡ da36cf26-8105-4569-9d09-6f16383000a0
md"""
x: $(@bind gxplane Slider(0:0.01:1,show_value=true,default=0.45))
y: $(@bind gyplane Slider(0:0.01:1,show_value=true,default=0.45))
z: $(@bind gzplane Slider(0:0.01:1,show_value=true,default=0.45))
"""

# ╔═╡ d009c4cb-9ef6-45bd-960f-0213880f662a
 tetmesh!(p3dgrid,g3[Coordinates],g3[CellNodes];
		markers=g3[CellRegions],
	faces=g3[BFaceNodes],
	facemarkers=g3[BFaceRegions],
	xplanes=[gxplane],yplanes=[gyplane],zplanes=[gzplane])

# ╔═╡ b33a9e6b-7c93-475c-96f4-7259b30c2c47
md"""
### tetcontour
"""

# ╔═╡ aded5964-8229-423c-b9f3-c80358b95fcc
f3=map(g3) do x,y,z
	sinpi(x/2)*cospi(y/2)*cospi(z/2)
end

# ╔═╡ 6af0b5d7-5324-43b5-8f99-6f5d35d5deba
TableOfContents()

# ╔═╡ 809ceb74-8201-4cc1-8cdc-656dc070e020
p3d=tetcontour(resolution=(300,300))

# ╔═╡ c222b16b-0ddc-4287-a029-779bdd77dd7b
md"""
f: $(@bind flevel Slider(0:0.01:1,show_value=true,default=0.45))

x: $(@bind xplane Slider(-2:0.01:2,show_value=true,default=-1.1))
y: $(@bind yplane Slider(-2:0.01:2,show_value=true,default=-1.1))
z: $(@bind zplane Slider(-2:0.01:2,show_value=true,default=-1.1))
"""

# ╔═╡ 36e48e9c-9452-4b07-bce4-c1cfe3c19409
tetcontour!(p3d,g3[Coordinates],g3[CellNodes],f3;levels=[flevel],
	xplanes=[xplane],yplanes=[yplane],zplanes=[zplane],levelalpha=0.7)

# ╔═╡ b074a389-bd07-4548-a75c-efa8c7663b15
html"""<hr>"""

# ╔═╡ fdf728b9-ade9-46f3-8aaf-cf22aaaa55d2
md"""
    begin
     using Pkg
     Pkg.activate(".testenv")
     Pkg.add("Revise")
     using Revise
      Pkg.add(["PlutoUI","Triangulate",
	   "ExtendableGrids","SimplexGridFactory","TetGen"])
     Pkg.develop("PlutoVista")
    end
""";

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
ExtendableGrids = "cfc395e8-590f-11e8-1f13-43a2532b2fa8"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
PlutoVista = "646e1f28-b900-46d7-9d87-d554eb38a413"
SimplexGridFactory = "57bfcd06-606e-45d6-baf4-4ba06da0efd5"
TetGen = "c5d3f3f7-f850-59f6-8a2e-ffc6dc1317ea"
Triangulate = "f7e6ffb2-c36d-4f8f-a77e-16e897189344"

[compat]
ExtendableGrids = "~0.8.8"
PlutoUI = "~0.7.18"
PlutoVista = "~0.8.9"
SimplexGridFactory = "~0.5.10"
TetGen = "~1.3.0"
Triangulate = "~2.1.0"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

julia_version = "1.6.3"
manifest_format = "2.0"

[[deps.AbstractPlutoDingetjes]]
deps = ["Pkg"]
git-tree-sha1 = "0ec322186e078db08ea3e7da5b8b2885c099b393"
uuid = "6e696c72-6542-2067-7265-42206c756150"
version = "1.1.0"

[[deps.AbstractTrees]]
git-tree-sha1 = "03e0550477d86222521d254b741d470ba17ea0b5"
uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c"
version = "0.3.4"

[[deps.Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.1"

[[deps.ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

[[deps.Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[deps.ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random"]
git-tree-sha1 = "a851fec56cb73cfdf43762999ec72eff5b86882a"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.15.0"

[[deps.ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.0"

[[deps.Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.8"

[[deps.DataAPI]]
git-tree-sha1 = "cc70b17275652eb47bc9e5f81635981f13cea5c8"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.9.0"

[[deps.DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

[[deps.Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[deps.DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "b19534d1895d702889b219c382a6e18010797f0b"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.8.6"

[[deps.Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"

[[deps.EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3f3a2501fa7236e9b911e0f7a588c657e822bb6d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.2.3+0"

[[deps.ElasticArrays]]
deps = ["Adapt"]
git-tree-sha1 = "a0fcc1bb3c9ceaf07e1d0529c9806ce94be6adf9"
uuid = "fdbdab4c-e67f-52f5-8c3f-e7b388dad3d4"
version = "1.2.9"

[[deps.ExtendableGrids]]
deps = ["AbstractTrees", "Dates", "DocStringExtensions", "ElasticArrays", "InteractiveUtils", "LinearAlgebra", "Printf", "Random", "SparseArrays", "Test"]
git-tree-sha1 = "e03c32179da71e9022381e1224c32a7a89febc10"
uuid = "cfc395e8-590f-11e8-1f13-43a2532b2fa8"
version = "0.8.8"

[[deps.FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"

[[deps.GeometryBasics]]
deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"]
git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c"
uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
version = "0.4.1"

[[deps.GridVisualize]]
deps = ["ColorSchemes", "Colors", "DocStringExtensions", "ElasticArrays", "ExtendableGrids", "GeometryBasics", "HypertextLiteral", "LinearAlgebra", "Observables", "OrderedCollections", "PkgVersion", "Printf", "StaticArrays"]
git-tree-sha1 = "80232407bd1fd4683429e289f6051b730e58cfa5"
uuid = "5eed8a63-0fb0-45eb-886d-8d5a387d12b8"
version = "0.4.1"

[[deps.Hyperscript]]
deps = ["Test"]
git-tree-sha1 = "8d511d5b81240fc8e6802386302675bdf47737b9"
uuid = "47d2ed2b-36de-50cf-bf87-49c2cf4b8b91"
version = "0.0.4"

[[deps.HypertextLiteral]]
git-tree-sha1 = "5efcf53d798efede8fee5b2c8b09284be359bf24"
uuid = "ac1192a8-f4b3-4bfe-ba22-af5b92cd3ab2"
version = "0.9.2"

[[deps.IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[deps.InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[deps.IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.0"

[[deps.IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[deps.JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.3.0"

[[deps.JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.2"

[[deps.LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"

[[deps.LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"

[[deps.LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[deps.LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"

[[deps.Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[deps.LinearAlgebra]]
deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[deps.Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[deps.Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[deps.MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"

[[deps.Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[deps.MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"

[[deps.NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"

[[deps.Observables]]
git-tree-sha1 = "fe29afdef3d0c4a8286128d4e45cc50621b1e43d"
uuid = "510215fc-4207-5dde-b226-833fc4488ee2"
version = "0.4.0"

[[deps.OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"

[[deps.Parsers]]
deps = ["Dates"]
git-tree-sha1 = "ae4bbcadb2906ccc085cf52ac286dc1377dceccc"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.1.2"

[[deps.Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

[[deps.PkgVersion]]
deps = ["Pkg"]
git-tree-sha1 = "a7a7e1a88853564e551e4eba8650f8c38df79b37"
uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
version = "0.1.1"

[[deps.PlutoUI]]
deps = ["AbstractPlutoDingetjes", "Base64", "Dates", "Hyperscript", "HypertextLiteral", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "UUIDs"]
git-tree-sha1 = "57312c7ecad39566319ccf5aa717a20788eb8c1f"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.18"

[[deps.PlutoVista]]
deps = ["ColorSchemes", "Colors", "DocStringExtensions", "GridVisualize", "UUIDs"]
git-tree-sha1 = "74a871aabc16ad6bcfc119f24581015ae8efa17a"
uuid = "646e1f28-b900-46d7-9d87-d554eb38a413"
version = "0.8.9"

[[deps.Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"

[[deps.Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[deps.REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[deps.Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[deps.Reexport]]
git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.2.2"

[[deps.SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[deps.Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[deps.SimplexGridFactory]]
deps = ["DocStringExtensions", "ElasticArrays", "ExtendableGrids", "GridVisualize", "LinearAlgebra", "Printf", "Test"]
git-tree-sha1 = "84e5e7f53ab95bea59d04168ccefb77e2368393c"
uuid = "57bfcd06-606e-45d6-baf4-4ba06da0efd5"
version = "0.5.10"

[[deps.Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[deps.SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[[deps.StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "3c76dde64d03699e074ac02eb2e8ba8254d428da"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.13"

[[deps.Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[[deps.StructArrays]]
deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"]
git-tree-sha1 = "2ce41e0d042c60ecd131e9fb7154a3bfadbf50d3"
uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
version = "0.6.3"

[[deps.TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[[deps.TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[deps.Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"]
git-tree-sha1 = "fed34d0e71b91734bf0a7e10eb1bb05296ddbcd0"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.6.0"

[[deps.Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"

[[deps.Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[deps.TetGen]]
deps = ["DocStringExtensions", "GeometryBasics", "LinearAlgebra", "Printf", "StaticArrays", "TetGen_jll"]
git-tree-sha1 = "2f1d87ccacd2a7faf9e0bade918946ec4d90bfdf"
uuid = "c5d3f3f7-f850-59f6-8a2e-ffc6dc1317ea"
version = "1.3.0"

[[deps.TetGen_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bc67a0d0b799fe248b1f199a5c893ccf316f0e60"
uuid = "b47fdcd6-d2c1-58e9-bbba-c1cee8d8c179"
version = "1.5.2+0"

[[deps.Triangle_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "bfdd9ef1004eb9d407af935a6f36a4e0af711369"
uuid = "5639c1d2-226c-5e70-8d55-b3095415a16a"
version = "1.6.1+0"

[[deps.Triangulate]]
deps = ["DocStringExtensions", "Libdl", "Printf", "Test", "Triangle_jll"]
git-tree-sha1 = "2b4f716b192c0c615d96d541ee029e85666388cb"
uuid = "f7e6ffb2-c36d-4f8f-a77e-16e897189344"
version = "2.1.0"

[[deps.UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[deps.Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[deps.Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"

[[deps.nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"

[[deps.p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"
"""

# ╔═╡ Cell order:
# ╟─93ca4fd0-8f61-4174-b459-55f5395c0f56
# ╠═c102e87a-b570-4d86-b087-3506396fc065
# ╟─53f55db7-225f-4717-ab62-e024211e98a2
# ╟─b8a976e3-7fef-4527-ae6a-4da31c93a04f
# ╟─133d0c31-eea9-4200-88d2-4afdc61a16bd
# ╠═2e92c42c-2923-4f35-9d43-6e38eee3f8f6
# ╟─09cfb361-b897-40b1-8792-23b00151b995
# ╠═60dcfcf5-391e-418f-8e7c-3a0fe94f1e0d
# ╟─f44deb76-e715-477c-9e8a-dcf5cd68577f
# ╟─76d7a50d-40da-4367-a84f-3e2324e0c78d
# ╠═c3b719cd-5134-4395-8f36-4daab6ee3bf0
# ╠═cf9f4d1a-ebcd-4b59-bb0c-cc0d6b3239ca
# ╟─bdde2452-cf28-4d56-935c-693e6a1ca8a9
# ╟─d5240960-7759-48b8-94fd-3436f7b1573d
# ╠═b4afef75-450c-45a9-af8b-87224c3591e9
# ╠═0c8bc23a-cee1-4d6b-a63e-8f2391fc3f3c
# ╟─d0ea4e21-9d8c-416e-bf68-d7522e2ece20
# ╟─f15a91a4-a03b-4125-ba58-e44d9792a7e3
# ╠═ab232244-4fe2-4ab0-a0bf-d1d9510802d2
# ╠═d3ad0d4f-859d-44ac-a387-aac8d465cc6d
# ╟─83c7bffd-16c6-4cc7-8a68-87cbd739f3f4
# ╟─724495e1-d501-4a03-be88-16b644938afd
# ╟─da3bdabb-b81c-4c05-90cd-aee7b209e605
# ╠═83be4a71-4f01-4f70-9cbe-f4e9b9222428
# ╟─8186bd23-5727-4d87-805c-5e5c6a092535
# ╠═ee9a6fb2-3978-40f4-803b-7cb8d50b4fac
# ╠═c6d700ec-91a1-4ef7-a104-8574cc162b9a
# ╠═8b25e922-12db-4fae-8f28-65fe4faf40f3
# ╟─dce20465-d227-4273-82b7-c6a4621942b9
# ╠═20a88415-f2b4-4806-9ffc-6be979d12d0a
# ╠═39412087-3ee3-41fb-818f-23e5396abba3
# ╟─68cba79c-2577-4250-b045-08e954bde4e5
# ╠═d3055a1a-5b55-4c43-ac09-7704186e714a
# ╟─387bbdee-0175-4a25-9a97-49fdb8afb7fc
# ╟─06fb9e66-c7c0-4028-80d9-2a7e36a6626d
# ╟─732b8e6a-68e0-4229-a2f9-52abe3ee5a40
# ╠═2eadced9-9e4a-4fa3-aea4-5f163933cb02
# ╠═fe7fdb00-88a1-4d24-aedd-cedb6e50120b
# ╠═5745f449-abc7-4f9a-a439-645698b781ea
# ╠═595539f8-f14e-418f-90d6-b6040292a9b6
# ╟─d1e13f8c-7cb8-4cb5-9dda-1a9b5d6142b6
# ╟─a2bb8861-493d-4003-8a4e-e0ea051fbb72
# ╠═411d8975-f67a-4236-b827-c030675db91c
# ╠═b1f255ac-7347-4cf0-8ca1-491713fcb6b0
# ╠═0a363d31-5a48-49ad-aba2-bc0058ce1225
# ╟─da36cf26-8105-4569-9d09-6f16383000a0
# ╠═d009c4cb-9ef6-45bd-960f-0213880f662a
# ╟─b33a9e6b-7c93-475c-96f4-7259b30c2c47
# ╠═aded5964-8229-423c-b9f3-c80358b95fcc
# ╟─6af0b5d7-5324-43b5-8f99-6f5d35d5deba
# ╠═809ceb74-8201-4cc1-8cdc-656dc070e020
# ╟─c222b16b-0ddc-4287-a029-779bdd77dd7b
# ╠═36e48e9c-9452-4b07-bce4-c1cfe3c19409
# ╟─b074a389-bd07-4548-a75c-efa8c7663b15
# ╠═fdf728b9-ade9-46f3-8aaf-cf22aaaa55d2
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
romdir = joinpath(@__DIR__, "rom_files")
rm(romdir, recursive=true, force=true)
mkpath(romdir)

# download and unzip the roms, then cleanup zips
urlbase = "http://www.atariage.com/2600/emulation/RomPacks/"
for fn in ["Atari2600_A-E.zip",
           "Atari2600_F-J.zip",
           "Atari2600_K-P.zip",
           "Atari2600_Q-S.zip",
           "Atari2600_T-Z.zip"]
  localfn = joinpath(romdir, fn)
  download(urlbase * fn, localfn)
  run(`unzip -u $localfn -d $romdir`)
  rm(localfn)
end

# rename all to lowercase letters
for fn in readdir(romdir)
  newfn = lowercase(fn)
  (newfn == fn) && continue

  try
    mv(joinpath(romdir, fn), joinpath(romdir, newfn))
  catch
    @warn "rename $fn to lowercase failed"
  end
end


text:
# RSA specific tests
# Copyright (C): Jaka Smrekar (vinctux) <vinctux@outlook.com>, 2016

using Krypto
using Base.Test

println("RSA TESTS STARTED...")

# Test values - 1024 octets (random, 0, 255, flip)
T = [[csrand(0o1:0o255) for i in 1:256],
     [0o0 for i in 1:256],
     [0o377 for i in 1:256],
     [i % 2 == 0 ? 0o0 : 0o1 for i in 1:256]]

# Key generation
# FIXME: Travis is erroring on keygen...
B = 2048
print("Generating $(B)-bit RSA keypair ... ")
@time PUB, PRIV = RSAKeyGen(B)
println("RSA Modulus n: $(hex(PUB.n))")
println("Private component d: $(hex(PRIV.d))")
println("Public component e: $(hex(PUB.e))")

# Encryption/decryption
#for t in T @test RSADecrypt(PRIV, RSAEncrypt(PUB, t)) == t end
for i in 1:length(T) print("Running encryption test #$(i) ..."); @time @test RSADP(PRIV, RSAEP(PUB, OS2IP(T[i]))) == OS2IP(T[i]) end

# Sign/verify
#for t in T @test RSAVerify(PUB, RSASign(PRIV, t), t) end
for i in 1:length(T) print("Running    signing test #$(i) ..."); @time @test RSAVP1(PUB, RSASP1(PRIV, OS2IP(T[i]))) == OS2IP(T[i]) end

println("ALL RSA TESTS PASSED.")


text:
"""
    abstract type Encoding

Transformation of `Block`s. Can encode some `Block`s ([`encode`]), and optionally
decode them [`decode`]

## Interface

- `encode(::E, ::Context, block::Block, data)` encodes `block` of `data`.
    The default is to do nothing. This should be overloaded for an encoding `E`,
    concrete `Block` types and possibly a context.
- `decode(::E, ::Context, block::Block, data)` decodes `block` of `data`. This
    should correspond as closely as possible to the inverse of `encode(::E, ...)`.
    The default is to do nothing, as not all encodings can be reversed. This should
    be overloaded for an encoding `E`, concrete `Block` types and possibly a context.
- `encodedblock(::E, block::Block) -> block'` returns the block that is obtained by
    encoding `block` with encoding `E`. This needs to be constant for an instance of `E`,
    so it cannot depend on the sample or on randomness. The default is to return `nothing`,
    meaning the same block is returned and not changed. Encodings that return the same
    block but change the data (e.g. `ProjectiveTransforms`) should return `block`.
- `decodedblock(::E, block::Block) -> block'` returns the block that is obtained by
    decoding `block` with encoding `E`. This needs to be constant for an instance of `E`,
    so it cannot depend on the sample or on randomness. The default is to return `nothing`,
    meaning the same block is returned and not changed.
- `encode!(buf, ::E, ::Context, block::Block, data)` encodes `data` inplace.
- `decode!(buf, ::E, ::Context, block::Block, data)` decodes `data` inplace.

"""
abstract type Encoding end

"""
    fillblock(inblocks, outblocks)

Replaces all `nothing`s in outblocks with the corresponding block in `inblocks`.
`outblocks` may be obtained by
"""
fillblock(inblocks::Tuple, outblocks::Tuple) =
    map(fillblock, inblocks, outblocks)
fillblock(inblock::AbstractBlock, ::Nothing) = inblock
fillblock(inblocks::Tuple, ::Nothing) = inblocks
fillblock(::AbstractBlock, outblock::AbstractBlock) = outblock

encodedblockfilled(enc, block) = fillblock(block, encodedblock(enc, block))
decodedblockfilled(enc, block) = fillblock(block, decodedblock(enc, block))
# ## `encode` methods

# By default an encoding doesn't change the data
encode(encoding::Encoding, ctx, block::Block, obs; kwargs...) =
    isempty(kwargs) ? obs : encode(encoding, ctx, block, obs)

# By default, a tuple of encodings encodes by encoding the data one encoding
# after the other
"""
    encode(encoding, context, block, obs)
    encode(encoding, context, blocks, obss)
    encode(encodings, context, blocks, obss)

Apply one or more [`Encoding`](#)s to observation(s) `obs`.
"""
function encode(encodings::NTuple{N, Encoding}, context, blocks, data) where N
    for encoding in encodings
        data = encode(encoding, context, blocks, data)
        blocks = encodedblockfilled(encoding, blocks)
    end
    return data
end

# By default, an encoding encodes every element in a tuple separately

function encode(encoding::Encoding, context, blocks::Tuple, obss::Tuple)
    @assert length(blocks) == length(obss)
   return map(
        (block, obs) -> encode(encoding, context, block, obs),
        blocks, obss
    )
end

# Named tuples of data are handled like tuples, but the keys are preserved
function encode(encoding::Encoding, context, blocks::NamedTuple, obss::NamedTuple)
    @assert length(blocks) == length(obss)
    return NamedTuple(
        zip(keys(obss), encode(encoding, context, values(blocks), values(obss))),
    )
end

# ## `decode` methods

# By default an encoding doesn't change the data when decoding
decode(encoding::Encoding, ctx, block::Block, obs; kwargs...) =
    isempty(kwargs) ? obs : decode(encoding, ctx, block, obs)

# By default, a tuple of encodings decodes by decoding the data one encoding
# after the other, with encodings iterated in reverse order
function decode(encodings::NTuple{N,Encoding}, context, blocks, obs) where {N}
    for encoding in Iterators.reverse(encodings)
        obs = decode(encoding, context, blocks, obs)
        blocks = decodedblockfilled(encoding, blocks)
    end
    return obs
end

# By default, an encoding decodes every element in a tuple separately
function decode(encoding::Encoding, context, blocks::Tuple, obss::Tuple)
    @assert length(blocks) == length(obss)
    return map(
        (block, obs) -> decode(encoding, context, block, obs),
        blocks, obss
    )
end

# Named tuples of data are handled like tuples, and the keys are preserved
function decode(encoding::Encoding, context, blocks::NamedTuple, obss::NamedTuple)
    @assert length(blocks) == length(obss)
    return NamedTuple(
        zip(keys(obss), decode(encoding, context, values(blocks), values(obss))),
    )
end

"""
    encodedblock(encoding, block)
    encodedblock(encoding, blocks)
    encodedblock(encodings, blocks)

Return the block that is obtained by encoding `block` with encoding `E`.
This needs to be constant for an instance of `E`, so it cannot depend on the
sample or on randomness. The default is to return `nothing`,
meaning the same block is returned and not changed. Encodings that return the same
block but change the data (e.g. `ProjectiveTransforms`) should return `block`.
"""
encodedblock(::Encoding, ::Block) = nothing

function encodedblock(encoding::Encoding, blocks::Tuple)
    map(block -> encodedblock(encoding, block), blocks)
end
function encodedblock(encodings::Tuple, blocks)
    encoded = false
    for encoding in encodings
        encoded = encoded || !isnothing(encodedblock(encoding, blocks))
        blocks = encodedblockfilled(encoding, blocks)
    end
    return encoded ? blocks : nothing
end

"""
    decodedblock(encoding, block)
    decodedblock(encoding, blocks)
    decodedblock(encodings, blocks)

Return the block that is obtained by decoding `block` with encoding `E`.
This needs to be constant for an instance of `E`, so it cannot depend on the
sample or on randomness. The default is to return `nothing`,
meaning the same block is returned and not changed. Encodings that return the same
block but change the data when decoding should return `block`.
"""
decodedblock(::Encoding, ::Block) = nothing

function decodedblock(encoding::Encoding, blocks::Tuple)
    map(block -> decodedblock(encoding, block), blocks)
end
function decodedblock(encodings, blocks)
    decoded = false
    for encoding in Iterators.reverse(encodings)
        decoded = decoded || !isnothing(decodedblock(encoding, blocks))
        blocks = decodedblockfilled(encoding, blocks)
    end
    return decoded ? blocks : nothing
end

"""
    abstract type StatefulEncoding <: Encoding

Encoding that needs to compute some state from the whole sample, even
if it only transforms some of the blocks. This could be random state
for stochastic augmentations that needs to be the same for every block
that is encoded.

The state is created by calling `encodestate(encoding, context, blocks, sample)`
and passed to recursive calls with the keyword argument `state`.
As a result, you need to implement `encode`, `decode`, `encode!`, `decode!` with a
keyword argument `state` that defaults to the above call.

Same goes for `decode`, which should accept a `state` keyword argument defaulting
to `decodestate(encoding, context, blocks, sample)`
"""
abstract type StatefulEncoding <: Encoding end

encodestate(encoding, context, blocks, obs) = nothing
decodestate(encoding, context, blocks, obs) = nothing

function encode(
    encoding::StatefulEncoding,
    context,
    blocks::Tuple,
    obss::Tuple;
    state = encodestate(encoding, context, blocks, obss),
)
    return map(
        (block, obs) -> encode(encoding, context, block, obs; state = state),
        blocks, obss)
end

function decode(
    encoding::StatefulEncoding,
    context,
    blocks::Tuple,
    obss::Tuple;
    state = decodestate(encoding, context, blocks, obss),
)

    @assert length(blocks) == length(obss)
    return Tuple(
        decode(encoding, context, block, obs; state = state) for
        (block, obs) in zip(blocks, obss)
    )
end

"""
    testencoding(encoding, block[, obs])

Performs some tests that the encoding interface is set up properly for
`encoding` and `block`. Tests that

- `obs` is a valid instance `block`
- `encode` returns a valid `encodedblock(encoding, block)`
- `decode` returns a valid `decodedblock(encoding, encodedblock(encoding, block))`
    and that the block is identical to `block`
"""
function testencoding(encoding, block, obs = mockblock(block))
    Test.@testset "Encoding `$(typeof(encoding))` for block `$block`" begin
        # Test that `obs` is a valid instance of `block`
        Test.@test checkblock(block, obs)
        Test.@test !isnothing(encodedblock(encoding, block))
        outblock = encodedblockfilled(encoding, block)
        outobs = encode(encoding, Training(), block, obs)
        # The encoded data should be a valid instance for the `encodedblock`
        Test.@test checkblock(outblock, outobs)

        # Test decoding (if supported) works correctly
        if (outblock isa Tuple)
            for idx in 1:length(outblock)
                inblock = decodedblock(encoding, outblock[idx])
                if !isnothing(inblock)
                    Test.@test wrapped(block[idx]) == wrapped(inblock)
                    inobs = decode(encoding, Training(), outblock[idx], outobs[idx])
                    Test.@test checkblock(inblock, inobs)
                end
            end
        else
            inblock = decodedblock(encoding, outblock)
            if !isnothing(inblock)
                Test.@test wrapped(block) == wrapped(inblock)
                inobs = decode(encoding, Training(), outblock, outobs)
                Test.@test checkblock(inblock, inobs)
            end
        end
    end
end


text:
using BipolarSphericalHarmonics
using Documenter

DocMeta.setdocmeta!(BipolarSphericalHarmonics, :DocTestSetup, :(using BipolarSphericalHarmonics); recursive=true)

makedocs(;
    modules=[BipolarSphericalHarmonics],
    authors="jishnub <jishnub@users.noreply.github.com> and contributors",
    repo="https://github.com/jishnub/BipolarSphericalHarmonics.jl/blob/{commit}{path}#{line}",
    sitename="BipolarSphericalHarmonics.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://jishnub.github.io/BipolarSphericalHarmonics.jl",
        assets=String[],
    ),
    pages=[
        "Reference" => "index.md",
    ],
)

deploydocs(;
    repo="github.com/jishnub/BipolarSphericalHarmonics.jl",
)


text:
using FTCTests
using Test

@testset "run_multiple_sim" begin
    N = 1
    manoeuvres = [:debug]
    methods = [:adaptive]
    _faults = [
               [FaultTolerantControl.NoFault()],
              ]
    θs_array = [
                [zeros(3)],
               ]
    t0, tf = 0.0, 1.0
    h_threshold = nothing
    actual_time_limit = nothing
    N_thread = 1
    collector = collect
    will_plot = false
    seed = 2021
    run_multiple_sim(
                     N,
                     manoeuvres,
                     methods,
                     _faults,
                     θs_array,
                     t0,
                     tf,
                     h_threshold,
                     actual_time_limit,
                     N_thread,
                     collector,
                     will_plot,
                     seed,
                    )
end


text:
module TestDay15

import AOC2021.Day15.part1, AOC2021.Day15.part2
using Test

test_input = """
1163751742
1381373672
2136511328
3694931569
7463417111
1319128137
1359912421
3125421639
1293138521
2311944581"""

@testset "Day 15" begin
    @testset "part 1" begin
        @test part1(test_input) == 40
    end
    @testset "part 2" begin
        @test part2(test_input) == 315
    end
end

end # module


text:
import CLBLAS
import OpenCL

    const clblas = CLBLAS
    clblas.setup()
    using OpenCL: cl
    alpha = cl.cl_float(10)
    data = Vector{Float32}(5)
    data[1] = 0.1
    data[2] = 0.22
    data[3] = 0.333
    data[4] = 0.4444
    data[5] = 0.55555

    alpha1 = cl.cl_float(5)
    alpha2 = cl.cl_float(2)
    future = clblas.clblasSscal(data, alpha1)
    future = clblas.clblasSscal(future, alpha2)

    result = fetch(future)
    println(result)
    println("------------")
    expected = data * alpha
    println(expected)

    @assert(isapprox(norm(result - expected), zero(Float32)))
    info("success!")

    clblas.teardown()


text:
const HUMAN_BIOMASS_IDER = "biomass_human"
const HUMAN_ATPM_IDER = "HMR_3964"
const HUMAN_GLC_EX_IDER = "HMR_9034"
const HUMAN_GLN_EX_IDER = "HMR_9063"
const HUMAN_GAL_EX_IDER = "HMR_9140"
const HUMAN_O2_EX_IDER = "HMR_9048"
const HUMAN_LAC_EX_IDER = "HMR_9135"
const HUMAN_AC_EX_IDER = "HMR_9086"

const INF = 99999
const ABS_MAX_BOUND = 99999
const ABS_MAX_CONC = 99999


text: # ## Computer vision


text:
# static kernels does laplace filter faster than imfiltering
const LAPLACE_KERNEL = @kernel w -> -w[0,-1] - w[-1,0] + 4 * w[0,0] - w[1,0] - w[0,1]
const MEDFILT3_KERNEL = Kernel{(-1:1,-1:1)}(@inline w -> median(Tuple(w)))
const MEDFILT5_KERNEL = Kernel{(-2:2,-2:2)}(@inline w -> median(Tuple(w)))
const MEDFILT7_KERNEL = Kernel{(-3:3,-3:3)}(@inline w -> median(Tuple(w)))

"""
    lacosmic(data::AbstractMatrix;
        noise=nothing,
        gain=1,
        background=0,
        readnoise=0,
        mask=falses(size(data)),
        sigma_clip=4.5,
        contrast=5,
        neighbor_thresh=0.3,
        maxiter=4,
        saturation_level=2^16,
        block_size=2)

Laplacian cosmic ray detection (LACosmic). This algorithm implements the algorithm presented in [lacosmicx](https://github.com/cmccully/lacosmicx). The return values are the cleaned image and the bad pixel mask. The image cleaning is done via median interpolation.

# Parameters
- `noise` is the pre-determined estimate of the data noise (square root of variance), if any
- `gain` is the image gain in electrons per data number
- `background` is pre-determined image background, if any
- `readnoise` is the read noise of the image in electrons
- `mask` is an input bad pixel mask, where `true` represents a bad pixel
- `sigma_clip` is the Laplacian signal-to-noise ratio for flagging bad pixels
- `contrast` is the minimum contrast required to flag a bad pixel in the ratio of the Laplacian image to the fine-structure image
- `neighbor_thresh` is the fractional detection limit for cosmic rays surrounding other cosmic rays. Should be a number between 0 and 1.
- `maxiter` is the maximum number of iterations used for detecting bad pixels
- `saturation_level` is the saturation value in electrons
- `block_size` is the subsampling factor for the Laplacian filter image.

# Examples
```jldoctest
julia> image = 100 .* randn(1001, 1001) .+ 1000;

julia> clean_image, mask = lacosmic(image, gain=4);
```

# References
> [van Dokkum, P.G. (2001)](https://ui.adsabs.harvard.edu/abs/2001PASP..113.1420V/abstract) - "Cosmic-Ray Rejection by Laplacian Edge Detection"
"""
function lacosmic(
        data::AbstractMatrix{T};
        noise = nothing,
        gain = one(T),
        background = zero(T),
        readnoise = zero(T),
        mask = falses(size(data)),
        sigma_clip = 4.5,
        contrast = 5,
        neighbor_thresh = 0.3,
        maxiter = 4,
        saturation_level = T(2^16),
        block_size = 2) where T
    # convert image to electrons using gain and background if previously subtracted
    if gain != one(T)
        gain_corrected = data .* gain
    else
        gain_corrected = data
    end
    clean_image = @. gain_corrected - background * gain
    if !isnothing(noise) # noise provided, convert to electrons
        clean_var = @. (noise * gain)^2 - background * gain
    else # noise inferred
        clean_var = @. clean_image + readnoise^2
    end

    # update mask with saturated stars
    background_img = map(MEDFILT5_KERNEL, extend(gain_corrected, ExtensionReplicate()))
    sat_mask = @. (gain_corrected ≥ saturation_level) & (background_img > (saturation_level * 0.1))
    mask = dilate!(copy(mask), 3) .| dilate!(dilate!(sat_mask, 5), 5)

    good_data = @view clean_image[.!mask]

    # get the default background level for large cosmic rays
    background_level = median(good_data)

    # define cosmic ray mask
    ray_mask = falses(size(data))

    # pre-allocate work arrays
    conv_img = similar(clean_image, size(clean_image) .* 2)
    var_img = similar(clean_var)
    snr_prime = similar(clean_image)
    med3 = similar(clean_image)
    med7 = similar(clean_image)
    f = similar(clean_image)
    cosmics = similar(mask)

    # loop for `maxiter` or if no more cosmic rays are found
    for iteration in 1:maxiter
        # subsample, convolve, clip, and rebin
        sub_img = subsample(clean_image, block_size)
        map!(LAPLACE_KERNEL, conv_img, extend(sub_img, ExtensionConstant(zero(T))))
        snr = rebin(conv_img, block_size)

        # build Laplacian S/N map
        map!(MEDFILT5_KERNEL, var_img, extend(clean_var, ExtensionReplicate()))
        @. snr /= block_size * sqrt(max(var_img, T(0.00001)) + background * gain)
        # remove large structions
        snr_prime .= snr .- map(MEDFILT5_KERNEL, extend(snr, ExtensionReplicate()))

        # build fine structure image
        map!(MEDFILT3_KERNEL, med3, extend(clean_image, ExtensionReplicate()))
        map!(MEDFILT7_KERNEL, med7, extend(med3, ExtensionReplicate()))
        # clip fine structure image since it is a divisor (similar to IRAF)
        @. f = max((med3 - med7) / sqrt(var_img + background * gain), T(0.01))

        # find candidate cosmic rays
        @. cosmics = !mask & (snr_prime > sigma_clip) & ((snr_prime / f) > contrast)

        # determine neighborhood
        cosmics = dilate!(cosmics, 3)
        @. cosmics &= !mask & (snr_prime > sigma_clip)
        cosmics = dilate!(cosmics, 3)
        @. cosmics &= !mask & (snr_prime > sigma_clip * neighbor_thresh)

        # update cosmic ray mask
        ray_mask .|= cosmics
        num_cosmics = count(cosmics)
        @debug "iteration $iteration: found $num_cosmics bad pixels this iteration"

        # if no more cosmics, break
        if iszero(num_cosmics)
            break
        # otherwise, clean the image
        else
            clean_image = clean(clean_image, ray_mask, mask, 5, background_level)
            clean_var = clean(clean_var, ray_mask, mask, 5, background_level)
        end
    end
    out_image = @. clean_image / gain + background
    return out_image, ray_mask
end

_padded_inds(idx, ax, half_width) = max(first(ax), (idx - half_width)):min(last(ax), (idx + half_width))

function dilate!(mask, size)
    half_width = Int((size - 1) / 2)
    @inbounds for idx in findall(mask)
        rows = _padded_inds(idx.I[1], axes(mask, 1), half_width)
        cols = _padded_inds(idx.I[2], axes(mask, 1), half_width)
        mask[rows, cols] .= true
    end
    return mask
end

function clean(reduction, image, crmask, mask, size, background_level)
    half_width = Int((size - 1) / 2)
    out = copy(image)
    @inbounds for idx in findall(crmask)
        rows = _padded_inds(idx.I[1], axes(mask, 1), half_width)
        cols = _padded_inds(idx.I[2], axes(mask, 1), half_width)
        window = @view image[rows, cols]
        m = @views @. !(crmask[rows, cols] | mask[rows, cols])
        masked_stat = reduction(view(window, m))
        out[idx] = max(masked_stat, background_level)
    end
    return out
end

clean(image, crmask, mask, size, background_level) = clean(mean, image, crmask, mask, size, background_level)