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

import Libdl

# helper function for passing input to stdin
# and returning the stdout result
function writereadpipeline(input, exename)
    p = open(exename, "w+")
    @async begin
        write(p.in, input)
        close(p.in)
    end
    return (read(p.out, String), success(p))
end

# helper function for returning stderr and stdout
# from running a command (ignoring failure status)
function readchomperrors(exename::Cmd)
    out = Base.PipeEndpoint()
    err = Base.PipeEndpoint()
    p = run(exename, devnull, out, err, wait=false)
    o = @async(readchomp(out))
    e = @async(readchomp(err))
    return (success(p), fetch(o), fetch(e))
end

function format_filename(s)
    p = ccall(:jl_format_filename, Cstring, (Cstring,), s)
    r = unsafe_string(p)
    ccall(:free, Cvoid, (Cstring,), p)
    return r
end

let
    fn = format_filename("a%d %p %i %L %l %u z")
    hd = withenv("HOME" => nothing) do
        # get the homedir, as reported by uv_os_get_passwd, as used by jl_format_filename
        try
            homedir()
        catch ex
            (ex isa Base.IOError && ex.code == Base.UV_ENOENT) || rethrow(ex)
            ""
        end
    end
    @test startswith(fn, "a$hd ")
    @test endswith(fn, " z")
    @test !occursin('%', fn)
    @test occursin(" $(getpid()) ", fn)
    @test occursin(" $(Libc.gethostname()) ", fn)
    @test format_filename("%a%%b") == "a%b"
end

let exename = `$(Base.julia_cmd()) --startup-file=no`
    # tests for handling of ENV errors
    let v = writereadpipeline("println(\"REPL: \", @which(less), @isdefined(InteractiveUtils))",
                setenv(`$exename -i -E 'empty!(LOAD_PATH); @isdefined InteractiveUtils'`,
                    "JULIA_LOAD_PATH" => "",
                    "JULIA_DEPOT_PATH" => "",
                    "HOME" => homedir()))
        @test v[1] == "false\nREPL: InteractiveUtilstrue\n"
        @test v[2]
    end
    let v = writereadpipeline("println(\"REPL: \", InteractiveUtils)",
                setenv(`$exename -i -e 'const InteractiveUtils = 3'`,
                    "JULIA_LOAD_PATH" => ";;;:::",
                    "JULIA_DEPOT_PATH" => ";;;:::",
                    "HOME" => homedir()))
        # TODO: ideally, `@which`, etc. would still work, but Julia can't handle `using $InterativeUtils`
        @test v[1] == "REPL: 3\n"
        @test v[2]
    end
    let v = readchomperrors(`$exename -i -e '
            empty!(LOAD_PATH)
            Base.unreference_module(Base.PkgId(Base.UUID(0xb77e0a4c_d291_57a0_90e8_8db25a27a240), "InteractiveUtils"))
            '`)
        # simulate not having a working version of InteractiveUtils,
        # make sure this is a non-fatal error and the REPL still loads
        @test v[1]
        @test isempty(v[2])
        @test startswith(v[3], "┌ Warning: Failed to import InteractiveUtils into module Main\n")
    end
    real_threads = string(ccall(:jl_cpu_threads, Int32, ()))
    for nc in ("0", "-2", "x", "2x", " ", "")
        v = readchomperrors(setenv(`$exename -i -E 'Sys.CPU_THREADS'`, "JULIA_CPU_THREADS" => nc, "HOME" => homedir()))
        @test v[1]
        @test v[2] == real_threads
        @test v[3] == "WARNING: couldn't parse `JULIA_CPU_THREADS` environment variable. Defaulting Sys.CPU_THREADS to $real_threads."
    end
    for nc in ("1", " 1 ", " +1 ", " 0x1 ")
        v = readchomperrors(setenv(`$exename -i -E 'Sys.CPU_THREADS'`, "JULIA_CPU_THREADS" => nc, "HOME" => homedir()))
        @test v[1]
        @test v[2] == "1"
        @test isempty(v[3])
    end
end

let exename = `$(Base.julia_cmd()) --startup-file=no`
    # --version
    let v = split(read(`$exename -v`, String), "julia version ")[end]
        @test Base.VERSION_STRING == chomp(v)
    end
    @test read(`$exename -v`, String) == read(`$exename --version`, String)

    # --help
    let header = "julia [switches] -- [programfile] [args...]"
        @test startswith(read(`$exename -h`, String), header)
        @test startswith(read(`$exename --help`, String), header)
    end

    # ~ expansion in --project and JULIA_PROJECT
    if !Sys.iswindows()
        expanded = abspath(expanduser("~/foo"))
        @test occursin(expanded, readchomp(`$exename --project='~/foo' -E 'Base.active_project()'`))
        @test occursin(expanded, readchomp(setenv(`$exename -E 'Base.active_project()'`, "JULIA_PROJECT" => "~/foo", "HOME" => homedir())))
    end

    # --quiet, --banner
    let t(q,b) = "Base.JLOptions().quiet == $q && Base.JLOptions().banner == $b"
        @test success(`$exename                 -e $(t(0, -1))`)
        @test success(`$exename -q              -e $(t(1,  0))`)
        @test success(`$exename --quiet         -e $(t(1,  0))`)
        @test success(`$exename --banner=no     -e $(t(0,  0))`)
        @test success(`$exename --banner=yes    -e $(t(0,  1))`)
        @test success(`$exename -q --banner=no  -e $(t(1,  0))`)
        @test success(`$exename -q --banner=yes -e $(t(1,  1))`)
        @test success(`$exename --banner=no  -q -e $(t(1,  0))`)
        @test success(`$exename --banner=yes -q -e $(t(1,  1))`)
    end

    # --home
    @test success(`$exename -H $(Sys.BINDIR)`)
    @test success(`$exename --home=$(Sys.BINDIR)`)

    # --eval
    @test  success(`$exename -e "exit(0)"`)
    @test !success(`$exename -e "exit(1)"`)
    @test  success(`$exename --eval="exit(0)"`)
    @test !success(`$exename --eval="exit(1)"`)
    @test !success(`$exename -e`)
    @test !success(`$exename --eval`)
    # --eval --interactive (replaced --post-boot)
    @test  success(`$exename -i -e "exit(0)"`)
    @test !success(`$exename -i -e "exit(1)"`)
    # issue #34924
    @test  success(`$exename -e 'const LOAD_PATH=1'`)

    # --print
    @test read(`$exename -E "1+1"`, String) == "2\n"
    @test read(`$exename --print="1+1"`, String) == "2\n"
    @test !success(`$exename -E`)
    @test !success(`$exename --print`)

    # --load
    let testfile = tempname()
        try
            write(testfile, "testvar = :test\nprintln(\"loaded\")\n")
            @test read(`$exename -i --load=$testfile -e "println(testvar)"`, String) == "loaded\ntest\n"
            @test read(`$exename -i -L $testfile -e "println(testvar)"`, String) == "loaded\ntest\n"
            # multiple, combined
            @test read(```$exename
                -e 'push!(ARGS, "hi")'
                -E "1+1"
                -E "2+2"
                -L $testfile
                -E '3+3'
                -L $testfile
                -E 'pop!(ARGS)'
                -e 'show(ARGS); println()'
                9 10
                ```, String) == """
                2
                4
                loaded
                6
                loaded
                "hi"
                ["9", "10"]
                """
        finally
            rm(testfile)
        end
    end
    # -L, --load requires an argument
    @test !success(`$exename -L`)
    @test !success(`$exename --load`)

    # --cpu-target (requires LLVM enabled)
    @test !success(`$exename -C invalidtarget`)
    @test !success(`$exename --cpu-target=invalidtarget`)

    # -t, --threads
    code = "print(Threads.nthreads())"
    cpu_threads = ccall(:jl_cpu_threads, Int32, ())
    @test string(cpu_threads) ==
          read(`$exename --threads auto -e $code`, String) ==
          read(`$exename --threads=auto -e $code`, String) ==
          read(`$exename -tauto -e $code`, String) ==
          read(`$exename -t auto -e $code`, String) ==
          read(`$exename -t $(cpu_threads+1) -e $code`, String)
    if cpu_threads > 1
        for nt in (nothing, "1"); withenv("JULIA_NUM_THREADS"=>nt) do
            @test read(`$exename --threads 2 -e $code`, String) ==
                  read(`$exename --threads=2 -e $code`, String) ==
                  read(`$exename -t2 -e $code`, String) ==
                  read(`$exename -t 2 -e $code`, String) == "2"
        end end
    end
    @test !success(`$exename -t 0`)
    @test !success(`$exename -t -1`)

    # Combining --threads and --procs: --threads does propagate
    if cpu_threads > 1; withenv("JULIA_NUM_THREADS"=>nothing) do
        code = "print(sum(remotecall_fetch(Threads.nthreads, x) for x in procs()))"
        @test read(`$exename -p2 -t2 -e $code`, String) == "6"
    end end

    # --procs
    @test readchomp(`$exename -q -p 2 -e "println(nworkers())"`) == "2"
    @test !success(`$exename -p 0`)
    let p = run(`$exename --procs=1.0`, wait=false)
        wait(p)
        @test p.exitcode == 1 && p.termsignal == 0
    end

    # --machine-file
    # this does not check that machine file works,
    # only that the filename gets correctly passed to the option struct
    let fname = tempname()
        touch(fname)
        fname = realpath(fname)
        try
            @test readchomp(`$exename --machine-file $fname -e
                "println(unsafe_string(Base.JLOptions().machine_file))"`) == fname
        finally
            rm(fname)
        end
    end

    # -i, isinteractive
    @test readchomp(`$exename -E "isinteractive()"`) == "false"
    @test readchomp(`$exename -E "isinteractive()" -i`) == "true"

    # --color
    @test readchomp(`$exename --color=yes -E "Base.have_color"`) == "true"
    @test readchomp(`$exename --color=no -E "Base.have_color"`) == "false"
    @test !success(`$exename --color=false`)

    # --history-file
    @test readchomp(`$exename -E "Bool(Base.JLOptions().historyfile)"
        --history-file=yes`) == "true"
    @test readchomp(`$exename -E "Bool(Base.JLOptions().historyfile)"
        --history-file=no`) == "false"
    @test !success(`$exename --history-file=false`)

    # --code-coverage
    mktempdir() do dir
        helperdir = joinpath(@__DIR__, "testhelpers")
        inputfile = joinpath(helperdir, "coverage_file.jl")
        expected = replace(read(joinpath(helperdir, "coverage_file.info"), String),
            "<FILENAME>" => realpath(inputfile))
        covfile = replace(joinpath(dir, "coverage.info"), "%" => "%%")
        @test !isfile(covfile)
        defaultcov = readchomp(`$exename -E "Base.JLOptions().code_coverage != 0" -L $inputfile`)
        opts = Base.JLOptions()
        coverage_file = (opts.output_code_coverage != C_NULL) ?  unsafe_string(opts.output_code_coverage) : ""
        @test !isfile(covfile)
        @test defaultcov == string(opts.code_coverage != 0 && (isempty(coverage_file) || occursin("%p", coverage_file)))
        @test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
            --code-coverage=$covfile --code-coverage=none`) == "0"
        @test !isfile(covfile)
        @test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
            --code-coverage=$covfile --code-coverage`) == "1"
        @test isfile(covfile)
        got = read(covfile, String)
        rm(covfile)
        @test occursin(expected, got) || (expected, got)
        @test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
            --code-coverage=$covfile --code-coverage=user`) == "1"
        @test isfile(covfile)
        got = read(covfile, String)
        rm(covfile)
        @test occursin(expected, got) || (expected, got)
        @test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
            --code-coverage=$covfile --code-coverage=all`) == "2"
        @test isfile(covfile)
        got = read(covfile, String)
        rm(covfile)
        @test occursin(expected, got) || (expected, got)
    end

    # --track-allocation
    @test readchomp(`$exename -E "Base.JLOptions().malloc_log != 0"`) == "false"
    @test readchomp(`$exename -E "Base.JLOptions().malloc_log != 0" --track-allocation=none`) == "false"

    @test readchomp(`$exename -E "Base.JLOptions().malloc_log != 0" --track-allocation`) == "true"
    @test readchomp(`$exename -E "Base.JLOptions().malloc_log != 0" --track-allocation=user`) == "true"
    mktempdir() do dir
        helperdir = joinpath(@__DIR__, "testhelpers")
        inputfile = joinpath(helperdir, "allocation_file.jl")
        pid = readchomp(`$exename -E "getpid()" -L $inputfile --track-allocation=user`)
        memfile = "$inputfile.$pid.mem"
        got = readlines(memfile)
        rm(memfile)
        @test popfirst!(got) == "        0 g(x) = x + 123456"
        @test popfirst!(got) == "        - function f(x)"
        @test popfirst!(got) == "       80     []"
        if Sys.WORD_SIZE == 64
            # P64 pools with 64 bit tags
            @test popfirst!(got) == "       32     Base.invokelatest(g, 0)"
            @test popfirst!(got) == "       48     Base.invokelatest(g, x)"
        elseif 12 == (() -> @allocated ccall(:jl_gc_allocobj, Ptr{Cvoid}, (Csize_t,), 8))()
            # See if we have a 12-byte pool with 32 bit tags (MAX_ALIGN = 4)
            @test popfirst!(got) == "       24     Base.invokelatest(g, 0)"
            @test popfirst!(got) == "       36     Base.invokelatest(g, x)"
        else # MAX_ALIGN >= 8
            @test popfirst!(got) == "       16     Base.invokelatest(g, 0)"
            @test popfirst!(got) == "       48     Base.invokelatest(g, x)"
        end
        @test popfirst!(got) == "       80     []"
        @test popfirst!(got) == "        - end"
        @test popfirst!(got) == "        - f(1.23)"
        @test isempty(got) || got
    end

    # --optimize
    @test readchomp(`$exename -E "Base.JLOptions().opt_level"`) == "2"
    @test readchomp(`$exename -E "Base.JLOptions().opt_level" -O`) == "3"
    @test readchomp(`$exename -E "Base.JLOptions().opt_level" --optimize`) == "3"
    @test readchomp(`$exename -E "Base.JLOptions().opt_level" -O0`) == "0"

    # -g
    @test readchomp(`$exename -E "Base.JLOptions().debug_level" -g`) == "2"
    let code = writereadpipeline("code_llvm(stdout, +, (Int64, Int64), raw=true, dump_module=true)", `$exename -g0`)
        @test code[2]
        code = code[1]
        @test occursin("llvm.module.flags", code)
        @test !occursin("llvm.dbg.cu", code)
        @test !occursin("int.jl", code)
        @test !occursin("Int64", code)
    end
    let code = writereadpipeline("code_llvm(stdout, +, (Int64, Int64), raw=true, dump_module=true)", `$exename -g1`)
        @test code[2]
        code = code[1]
        @test occursin("llvm.module.flags", code)
        @test occursin("llvm.dbg.cu", code)
        @test occursin("int.jl", code)
        @test !occursin("Int64", code)
    end
    let code = writereadpipeline("code_llvm(stdout, +, (Int64, Int64), raw=true, dump_module=true)", `$exename -g2`)
        @test code[2]
        code = code[1]
        @test occursin("llvm.module.flags", code)
        @test occursin("llvm.dbg.cu", code)
        @test occursin("int.jl", code)
        @test occursin("\"Int64\"", code)
    end

    # --check-bounds
    let JL_OPTIONS_CHECK_BOUNDS_DEFAULT = 0,
        JL_OPTIONS_CHECK_BOUNDS_ON = 1,
        JL_OPTIONS_CHECK_BOUNDS_OFF = 2
        exename_default_checkbounds = `$exename`
        filter!(a -> !startswith(a, "--check-bounds="), exename_default_checkbounds.exec)
        @test parse(Int, readchomp(`$exename_default_checkbounds -E "Int(Base.JLOptions().check_bounds)"`)) ==
            JL_OPTIONS_CHECK_BOUNDS_DEFAULT
        @test parse(Int, readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)"
            --check-bounds=yes`)) == JL_OPTIONS_CHECK_BOUNDS_ON
        @test parse(Int, readchomp(`$exename -E "Int(Base.JLOptions().check_bounds)"
            --check-bounds=no`)) == JL_OPTIONS_CHECK_BOUNDS_OFF
    end
    # check-bounds takes yes/no as argument
    @test !success(`$exename -E "exit(0)" --check-bounds=false`)

    # --depwarn
    @test readchomp(`$exename --depwarn=no  -E "Base.JLOptions().depwarn"`) == "0"
    @test readchomp(`$exename --depwarn=yes -E "Base.JLOptions().depwarn"`) == "1"
    @test !success(`$exename --depwarn=false`)
    # test deprecated syntax
    @test !success(`$exename -e "foo (x::Int) = x * x" --depwarn=error`)
    # test deprecated method
    @test !success(`$exename -e "
        foo() = :foo; bar() = :bar
        @deprecate foo() bar()
        foo()
    " --depwarn=error`)

    # test deprecated bindings, #13269
    let code = """
        module Foo
            import Base: @deprecate_binding

            const NotDeprecated = true
            @deprecate_binding Deprecated NotDeprecated
        end

        Foo.Deprecated
        """

        @test !success(`$exename -E "$code" --depwarn=error`)

        @test readchomperrors(`$exename -E "$code" --depwarn=yes`) ==
            (true, "true", "WARNING: Foo.Deprecated is deprecated, use NotDeprecated instead.\n  likely near none:8")

        @test readchomperrors(`$exename -E "$code" --depwarn=no`) ==
            (true, "true", "")
    end

    # --inline
    @test readchomp(`$exename -E "Bool(Base.JLOptions().can_inline)"`) == "true"
    @test readchomp(`$exename --inline=yes -E "Bool(Base.JLOptions().can_inline)"`) == "true"
    @test readchomp(`$exename --inline=no -E "Bool(Base.JLOptions().can_inline)"`) == "false"
    # --inline takes yes/no as argument
    @test !success(`$exename --inline=false`)

    # --polly
    @test readchomp(`$exename -E "Bool(Base.JLOptions().polly)"`) == "true"
    @test readchomp(`$exename --polly=yes -E "Bool(Base.JLOptions().polly)"`) == "true"
    @test readchomp(`$exename --polly=no -E "Bool(Base.JLOptions().polly)"`) == "false"
    # --polly takes yes/no as argument
    @test !success(`$exename --polly=false`)

    # --fast-math
    let JL_OPTIONS_FAST_MATH_DEFAULT = 0,
        JL_OPTIONS_FAST_MATH_ON = 1,
        JL_OPTIONS_FAST_MATH_OFF = 2
        @test parse(Int,readchomp(`$exename -E
            "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT
        @test parse(Int,readchomp(`$exename --math-mode=user -E
            "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_DEFAULT
        @test parse(Int,readchomp(`$exename --math-mode=ieee -E
            "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_OFF
        @test parse(Int,readchomp(`$exename --math-mode=fast -E
            "Int(Base.JLOptions().fast_math)"`)) == JL_OPTIONS_FAST_MATH_ON
    end

    # --worker takes default / custom as argument (default/custom arguments
    # tested in test/parallel.jl)
    @test !success(`$exename --worker=true`)

    # test passing arguments
    mktempdir() do dir
        testfile, io = mktemp(dir)
        # write a julia source file that just prints ARGS to stdout
        write(io, """
            println(ARGS)
            """)
        close(io)
        mkpath(joinpath(dir, ".julia", "config"))
        cp(testfile, joinpath(dir, ".julia", "config", "startup.jl"))

        withenv((Sys.iswindows() ? "USERPROFILE" : "HOME") => dir) do
            output = "[\"foo\", \"-bar\", \"--baz\"]"
            @test readchomp(`$exename $testfile foo -bar --baz`) == output
            @test readchomp(`$exename $testfile -- foo -bar --baz`) == output
            @test readchomp(`$exename -L $testfile -e 'exit(0)' -- foo -bar --baz`) ==
                output
            @test readchomp(`$exename --startup-file=yes -e 'exit(0)' -- foo -bar --baz`) ==
                output

            output = "String[]\nString[]"
            @test readchomp(`$exename -L $testfile $testfile`) == output
            @test readchomp(`$exename --startup-file=yes $testfile`) == output

            @test !success(`$exename --foo $testfile`)
            @test readchomp(`$exename -L $testfile -e 'exit(0)' -- foo -bar -- baz`) ==
                "[\"foo\", \"-bar\", \"--\", \"baz\"]"
        end
    end

    # test the program name remains constant
    mktempdir() do dir
        # dir can be case-incorrect sometimes
        dir = realpath(dir)

        a = joinpath(dir, "a.jl")
        b = joinpath(dir, "b.jl")
        c = joinpath(dir, ".julia", "config", "startup.jl")

        write(a, """
            println(@__FILE__)
            println(PROGRAM_FILE)
            include(\"$(escape_string(b))\")
            """)
        write(b, """
            println(@__FILE__)
            println(PROGRAM_FILE)
            """)
        mkpath(dirname(c))
        cp(b, c)

        readsplit(cmd) = split(readchomp(cmd), '\n')

        withenv((Sys.iswindows() ? "USERPROFILE" : "HOME") => dir) do
            @test readsplit(`$exename $a`) ==
                [a, a,
                 b, a]
            @test readsplit(`$exename -L $b -e 'exit(0)'`) ==
                [b, ""]
            @test readsplit(`$exename -L $b $a`) ==
                [b, a,
                 a, a,
                 b, a]
            @test readsplit(`$exename --startup-file=yes -e 'exit(0)'`) ==
                [c, ""]
            @test readsplit(`$exename --startup-file=yes -L $b -e 'exit(0)'`) ==
                [c, "",
                 b, ""]
            @test readsplit(`$exename --startup-file=yes -L $b $a`) ==
                [c, a,
                 b, a,
                 a, a,
                 b, a]
        end
    end

    # issue #10562
    @test readchomp(`$exename -e 'println(ARGS);' ''`) == "[\"\"]"

    # issue #12679
    @test readchomperrors(`$exename --startup-file=no --compile=yes -ioo`) ==
        (false, "", "ERROR: unknown option `-o`")
    @test readchomperrors(`$exename --startup-file=no -p`) ==
        (false, "", "ERROR: option `-p/--procs` is missing an argument")
    @test readchomperrors(`$exename --startup-file=no --inline`) ==
        (false, "", "ERROR: option `--inline` is missing an argument")
    @test readchomperrors(`$exename --startup-file=no -e "@show ARGS" -now -- julia RUN.jl`) ==
        (false, "", "ERROR: unknown option `-n`")

    # --compiled-modules={yes|no}
    @test readchomp(`$exename -E "Bool(Base.JLOptions().use_compiled_modules)"`) == "true"
    @test readchomp(`$exename --compiled-modules=yes -E
        "Bool(Base.JLOptions().use_compiled_modules)"`) == "true"
    @test readchomp(`$exename --compiled-modules=no -E
        "Bool(Base.JLOptions().use_compiled_modules)"`) == "false"
    @test !success(`$exename --compiled-modules=foo -e "exit(0)"`)

    # issue #12671, starting from a non-directory
    # rm(dir) fails on windows with Permission denied
    # and was an upstream bug in llvm <= v3.3
    if !Sys.iswindows() && Base.libllvm_version > v"3.3"
        testdir = mktempdir()
        cd(testdir) do
            rm(testdir)
            @test Base.current_project() === nothing
            @test success(`$exename -e "exit(0)"`)
            for load_path in ["", "@", "@."]
                withenv("JULIA_LOAD_PATH" => load_path) do
                    @test success(`$exename -e "exit(!(Base.load_path() == []))"`)
                end
            end
        end
    end
end

# Find the path of libjulia (or libjulia-debug, as the case may be)
# to use as a dummy shlib to open
libjulia = if Base.DARWIN_FRAMEWORK
    abspath(Libdl.dlpath(Base.DARWIN_FRAMEWORK_NAME *
        (ccall(:jl_is_debugbuild, Cint, ()) != 0 ? "_debug" : "")))
else
    abspath(Libdl.dlpath((ccall(:jl_is_debugbuild, Cint, ()) != 0) ? "libjulia-debug" : "libjulia"))
end

# test error handling code paths of running --sysimage
let exename = Base.julia_cmd()
    sysname = unsafe_string(Base.JLOptions().image_file)
    for nonexist_image in (
            joinpath(@__DIR__, "nonexistent"),
            "$sysname.nonexistent",
            )
        let err = Pipe(),
            p = run(pipeline(`$exename --sysimage=$nonexist_image`, stderr=err), wait=false)
            close(err.in)
            let s = read(err, String)
                @test occursin("ERROR: could not load library \"$nonexist_image\"\n", s)
                @test !occursin("Segmentation fault", s)
                @test !occursin("EXCEPTION_ACCESS_VIOLATION", s)
            end
            @test !success(p)
            @test !Base.process_signaled(p)
            @test p.exitcode == 1
        end
    end
    let err = Pipe(),
        p = run(pipeline(`$exename --sysimage=$libjulia`, stderr=err), wait=false)
        close(err.in)
        let s = read(err, String)
            @test s == "ERROR: System image file failed consistency check: maybe opened the wrong version?\n"
        end
        @test !success(p)
        @test !Base.process_signaled(p)
        @test p.exitcode == 1
    end
end

let exename = Base.julia_cmd()
    # --startup-file
    let JL_OPTIONS_STARTUPFILE_ON = 1,
        JL_OPTIONS_STARTUPFILE_OFF = 2
        # `HOME=$tmpdir` to avoid errors in the user startup.jl, which hangs the tests. Issue #17642
        mktempdir() do tmpdir
            withenv("HOME"=>tmpdir) do
                @test parse(Int,readchomp(`$exename -E "Base.JLOptions().startupfile" --startup-file=yes`)) == JL_OPTIONS_STARTUPFILE_ON
            end
        end
        @test parse(Int,readchomp(`$exename -E "Base.JLOptions().startupfile"
            --startup-file=no`)) == JL_OPTIONS_STARTUPFILE_OFF
    end
    @test !success(`$exename --startup-file=false`)
end

# Make sure `julia --lisp` doesn't break
run(pipeline(devnull, `$(joinpath(Sys.BINDIR, Base.julia_exename())) --lisp`, devnull))

# Test that `julia [some other option] --lisp` is disallowed
@test readchomperrors(`$(joinpath(Sys.BINDIR, Base.julia_exename())) -Cnative --lisp`) ==
    (false, "", "ERROR: --lisp must be specified as the first argument")

# --sysimage-native-code={yes|no}
let exename = `$(Base.julia_cmd()) --startup-file=no`
    @test readchomp(`$exename --sysimage-native-code=yes -E
        "Bool(Base.JLOptions().use_sysimage_native_code)"`) == "true"
    @test readchomp(`$exename --sysimage-native-code=no -E
        "Bool(Base.JLOptions().use_sysimage_native_code)"`) == "false"
end

# backtrace contains line number info (esp. on windows #17179)
for precomp in ("yes", "no")
    succ, out, bt = readchomperrors(`$(Base.julia_cmd()) --startup-file=no --sysimage-native-code=$precomp -E 'sqrt(-2)'`)
    @test !succ
    @test out == ""
    @test occursin(r"\.jl:(\d+)", bt)
end

# PR #23002
let exename = `$(Base.julia_cmd()) --startup-file=no`
    for (mac, flag, pfix, msg) in [("@test_nowarn", ``, "_1", ""),
                                   ("@test_warn",   `--warn-overwrite=yes`, "_2", "\"WARNING: Method definition\"")]
        str = """
        using Test
        try
            # issue #18725
            $mac $msg @eval Main begin
                f18725$(pfix)(x) = 1
                f18725$(pfix)(x) = 2
            end
            @test Main.f18725$(pfix)(0) == 2
            # PR #23030
            $mac $msg @eval Main module Module23030$(pfix)
                f23030$(pfix)(x) = 1
                f23030$(pfix)(x) = 2
            end
        catch
            exit(-1)
        end
        exit(0)
        """
        run(`$exename $flag -e $str`)
    end
end

# issue #6310
let exename = `$(Base.julia_cmd()) --startup-file=no`
    @test writereadpipeline("2+2", exename) == ("4\n", true)
    @test writereadpipeline("2+2\n3+3\n4+4", exename) == ("4\n6\n8\n", true)
    @test writereadpipeline("", exename) == ("", true)
    @test writereadpipeline("print(2)", exename) == ("2", true)
    @test writereadpipeline("print(2)\nprint(3)", exename) == ("23", true)
    let infile = tempname()
        touch(infile)
        try
            @test read(pipeline(exename, stdin=infile), String) == ""
            write(infile, "(1, 2+3)")
            @test read(pipeline(exename, stdin=infile), String) == "(1, 5)\n"
            write(infile, "1+2\n2+2\n1-2\n")
            @test read(pipeline(exename, stdin=infile), String) == "3\n4\n-1\n"
            write(infile, "print(2)")
            @test read(pipeline(exename, stdin=infile), String) == "2"
            write(infile, "print(2)\nprint(3)")
            @test read(pipeline(exename, stdin=infile), String) == "23"
        finally
            rm(infile)
        end
    end
end

# Issue #29855
for yn in ("no", "yes")
    exename = `$(Base.julia_cmd()) --inline=no --startup-file=no --inline=$yn`
    v = writereadpipeline("Base.julia_cmd()", exename)
    if yn == "no"
        @test occursin(r" --inline=no", v[1])
    else
        @test !occursin(" --inline", v[1])
    end
    @test v[2]
end

max_stars_repo_name: sloede/julia

max_issues_repo_name: sloede/julia

max_forks_repo_name: sloede/julia


text:
#
# base.jl --
#
# Basic methods for Andor cameras.
#
#-------------------------------------------------------------------------------
#
# This file is part of "AndorCameras.jl" released under the MIT license.
#
# Copyright (C) 2017-2021, Éric Thiébaut.
#

# A bit of magic for ccall.
Base.cconvert(::Type{AT.Handle}, cam::Camera) = cam.handle
Base.cconvert(::Type{Cwstring}, key::AbstractFeature) =
    WideStrings.buffer(key.name)

getnumberofdevices() = Int(_NDEVS[])

const _NDEVS = Ref{Int}(0)

function __init__()
    check(AT.InitialiseLibrary())
    status, ndevs = AT.GetInt(AT.HANDLE_SYSTEM, L"DeviceCount")
    if isfailure(status)
        AT.FinaliseLibrary()
        error(status)
    end
    _NDEVS[] = ndevs
    nothing
end

"""
    checkstate(cam, state; throwerrors=false)

returns whether camera `cam` is in a specific state: 0 if it must be closed
(or not yet open), 1 if it must be open (but acquisition not running) or 2
if acquisition must be running.  If the state is not the expected one, an
error is thrown if `throwerrors` is true or a warning message printed
otherwise.

"""
function checkstate(cam::Camera, state::Integer; throwerrors::Bool = false)
    if cam.state == state
        return true
    else
        local msg::String
        if state == 1 && cam.state == 0
            msg = "camera not open"
        elseif state == 1 && cam.state == 2
            msg = "acquisition is running"
        elseif state == 2 && (cam.state == 0 || cam.state == 1)
            msg = "acquisition not started"
        elseif state == 0 && (cam.state == 1 || cam.state == 2)
            msg = "camera not closed"
        else
            msg = "corrupted camera instance"
        end
        if throwerrors
            error(msg)
        else
            @warn msg
        end
        return false
    end
end

"""
    isfailure(status)

yields whether `status` indicates a failure.  Argument `status` is the result
of a call to a function of the Andor SDK.

"""
isfailure(status::AT.Status) = (status.code != AT.SUCCESS)

"""
    issuccess(status)

yields whether `status` indicates a success.  Argument `status` is the result
of a call to a function of the Andor SDK.

"""
issuccess(status::AT.Status) = (status.code == AT.SUCCESS)

Base.error(status::AT.Status) = throw(AndorError(status.func, status.code))

check(status::AT.Status) = (status.code == AT.SUCCESS || error(status))

check(args::Tuple{AT.Status,Any}) =
    (@inbounds check(args[1]); @inbounds args[2])

check(args::Tuple{AT.Status,Any,Any}) =
    (@inbounds check(args[1]); @inbounds (args[2], args[3]))

check(args::Tuple{AT.Status,Vararg}) =
    (@inbounds check(args[1]); @inbounds args[2:end])

function _close(cam::Camera; throwerrors::Bool=false)
    if cam.state > 1
        # Stop acquisition, then flush buffers.
        status = _stop(cam)
        throwerrors && isfailure(status) && error(status)
        status = AT.Flush(cam)
        throwerrors && isfailure(status) && error(status)
        cam.state = 1
    end
    if cam.state > 0
        # Close handle.  Manage to avoid re-closing.
        status = AT.Close(cam)
        cam.handle = AT.HANDLE_UNINITIALISED
        cam.state = 0
        cam.model = _UNKNOWN_MODEL
        throwerrors && isfailure(status) && error(status)
    end
    nothing
end

# Execute AcquisitionStop command for camera that support it.
_stop(cam::Camera) :: AT.Status =
    (issimcam(cam) ? AT.Status(:AT_Command, AT.SUCCESS) :
     AT.Command(cam, AcquisitionStop))

"""
    AndorCameras.issimcam(cam) -> boolean

returns whether Andor camera `cam` is a simulated one, i.e. named "SimCam" in
the documentation of Andor SDK.

"""
issimcam(cam::Camera) = (cam.model == _SIM_CAM_MODEL)

# Methods for any feature type:

isimplemented(cam::Camera, key::AbstractFeature) =
    check(AT.IsImplemented(cam, key))

isreadable(cam::Camera, key::AbstractFeature) =
    check(AT.IsReadable(cam, key))

iswritable(cam::Camera, key::AbstractFeature) =
    check(AT.IsWritable(cam, key))

isreadonly(cam::Camera, key::AbstractFeature) =
    check(AT.IsReadOnly(cam, key))

# Integer features:

Base.setindex!(cam::Camera, val::Integer, key::IntegerFeature) =
    (check(AT.SetInt(cam, key, val)); return cam)

Base.getindex(cam::Camera, key::IntegerFeature) =
    Int(check(AT.GetInt(cam, key)))

Base.minimum(cam::Camera, key::IntegerFeature) =
    Int(check(AT.GetIntMin(cam, key)))

Base.maximum(cam::Camera, key::IntegerFeature) =
    Int(check(AT.GetIntMax(cam, key)))

# Floating-point features:

Base.getindex(cam::Camera, key::FloatingPointFeature) =
    Float64(check(AT.GetFloat(cam, key)))

Base.setindex!(cam::Camera, val::Real, key::FloatingPointFeature) =
    (check(AT.SetFloat(cam, key, val)); return cam)

Base.minimum(cam::Camera, key::FloatingPointFeature) =
    Float64(check(AT.GetFloatMin(cam, key, val)))

Base.maximum(cam::Camera, key::FloatingPointFeature) =
    Float64(check(AT.GetFloatMax(cam, key, val)))

# Boolean features:

Base.setindex!(cam::Camera, val::Bool, key::BooleanFeature) =
    (check(AT.SetBool(cam, key, val)); return cam)

Base.getindex(cam::Camera, key::BooleanFeature) =
    check(AT.GetBool(cam, key))

# String features:

function Base.getindex(cam::Camera, key::StringFeature) :: String
    len = check(AT.GetStringMaxLength(cam, key))
    len ≥ 1 || error("invalid string length for feature \"$(repr(key.name))\"")
    buf = Vector{Cwchar_t}(undef, len)
    check(AT.GetString(cam, key, buf))
    buf[len] = zero(eltype(buf))
    return String(trunc!(WideString, buf))
end

Base.setindex!(cam::Camera, val::AbstractString, key::StringFeature) =
    setindex!(cam, WideString(val), key)

Base.setindex!(cam::Camera, val::WideString, key::StringFeature) = begin
    check(AT.SetString(cam, key, val))
    return cam
end

# Enumerated features:

Base.getindex(cam::Camera, key::EnumeratedFeature) =
    check(AT.GetEnumIndex(cam, key)) + 1

Base.minimum(cam::Camera, key::EnumeratedFeature) = 1

Base.maximum(cam::Camera, key::EnumeratedFeature) =
    check(AT.GetEnumCount(cam, key))

isavailable(cam::Camera, key::EnumeratedFeature, index::Integer) =
    check(AT.IsEnumIndexAvailable(cam, key, index - 1))

isimplemented(cam::Camera, key::EnumeratedFeature, index::Integer) =
    check(AT.IsEnumIndexImplemented(cam, key, index - 1))

Base.repr(cam::Camera, key::EnumeratedFeature) =
    repr(cam, key, cam[key])

function Base.repr(cam::Camera, key::EnumeratedFeature, index::Integer)
    buf = Vector{Cwchar_t}(undef, 64)
    check(AT.GetEnumStringByIndex(cam, key, index - 1, buf))
    buf[end] = zero(eltype(buf))
    return trunc!(WideString, buf)
end

Base.setindex!(cam::Camera, val::AbstractString, key::EnumeratedFeature) =
    (check(AT.SetEnumString(cam, key, WideString(val))); return cam)

Base.setindex!(cam::Camera, val::Integer, key::EnumeratedFeature) =
    (check(AT.SetEnumIndex(cam, key, val - 1)); return cam)

# Extend basic methods:

Base.String(key::AbstractFeature) = String(key.name)
Base.string(key::AbstractFeature) = string(key.name)

Base.print(io::IO, m::MIME, key::AbstractFeature) =
    print(io, m, key.name)

Base.print(io::IO, key::AbstractFeature) =
    print(io, key.name)

Base.show(io::IO, key::T) where {T <: AbstractFeature} =
    print(io, T, "(", String(key.name), ")")

function Base.show(io::IO, cam::T) where {T <: Camera}
    print(io, T, ":")
    count = typemax(Int)
    function prt(args...)
        local pfx::String
        if count < 3
            count += 1
            pfx = " "
        else
            count = 1
            pfx = "\n    "
        end
        print(io, pfx, args...)
    end
    if isimplemented(cam, CameraFamily)
        prt("family: \"", cam[CameraFamily], "\",")
    end
    if isimplemented(cam, CameraModel)
        prt("model: \"", cam[CameraModel], "\",")
    end
    if isimplemented(cam, CameraName)
        prt("name: \"", cam[CameraName], "\",")
    end
    if isimplemented(cam, CameraPresent)
        prt("present: ", cam[CameraPresent], ",")
    end
    if isimplemented(cam, SensorTemperature)
        prt("temperature: ", @sprintf("%.1f°C,", cam[SensorTemperature]))
    end
    if isimplemented(cam, CycleMode)
        prt("cycle mode: \"", repr(cam, CycleMode), "\",")
    end
    if isimplemented(cam, CameraAcquiring)
        prt("acquiring: ", cam[CameraAcquiring], ",")
    end
    if isimplemented(cam, PixelEncoding)
        prt("pixel encoding: \"", repr(cam, PixelEncoding), "\",")
    end
    if isimplemented(cam, BytesPerPixel)
        prt("bits per pixel: ", round(Int, 8*cam[BytesPerPixel]), ",")
    end
    if isimplemented(cam, SensorWidth) && isimplemented(cam, SensorHeight)
        prt("sensor size: ", cam[SensorWidth], "×", cam[SensorHeight], ",")
    end
    if isimplemented(cam, FrameRate)
        prt("frames per second: ", @sprintf("%g", cam[FrameRate]), ",")
    end
    if isimplemented(cam, ExposureTime)
        prt("exposure time: ", @sprintf("%g", cam[ExposureTime]), " s,")
    end
    roi = getroi(cam)
    prt("macro-pixel: ", roi.xsub, "×", roi.ysub, ",")
    prt("ROI offsets: (", roi.xoff, ",", roi.yoff, "),")
    prt("image size: ", roi.width, "×", roi.height, "\n")
end

# Extract images from buffers.

"""
    extract!(dst, buf, bytesperline) -> dst

stores in `dst` the contents of `buf` reinterpreted as values of same type
as the elements of `dst`.  Argument `bytesperline` is the number of bytes
per line stored in `buf`.  The destination `dst` is returned.

See also: [`extractpacked12!`](@ref)

"""
function extract!(dst::Array{T,2},
                  buf::Vector{UInt8},
                  bytesperline::Int) where {T}
    width, height = size(dst)
    stride, extra = divrem(bytesperline, sizeof(T))
    if extra != 0
        error("number of bytes per line must be a multiple of $(sizeof(T))")
    end
    if bytesperline < width*sizeof(T)
        error("number of bytes per line is too small")
    end
    if sizeof(buf) < bytesperline*height
        error("buffer is too small")
    end
    src = reinterpret(T, buf)
    @inbounds for y in 1:height
        offset = (y - 1)*stride
        @simd for x in 1:width
            dst[x,y] = src[offset + x]
        end
    end
    return dst
end

@inline function extractlowpacked(::Type{T},
                                  b0::UInt8, b1::UInt8) where {T <: Unsigned}
    ((convert(T, b0) << 4) | convert(T, b1 & 0x0F))
end
@inline function extracthighpacked(::Type{T},
                                   b1::UInt8, b2::UInt8) where {T <: Unsigned}
    ((convert(T, b2) << 4) | convert(T, b1 >> 4))
end

"""
    extractmono12packed!(dst, buf, bytesperline) -> dst

stores in `dst` the contents of `buf` interpreted as packed 12-bit unsigned
integers.  Argument `bytesperline` is the number of bytes per line stored
in `buf`.  The destination `dst` is returned.

See also: [`extract!`](@ref)

"""
function extractmono12packed!(dst::Array{T,2},
                              buf::Vector{UInt8},
                              bytesperline::Int) where {T <: Unsigned}
    width, height = size(dst)
    if bytesperline < 3*(width >> 1) + 2*(width & 1)
        error("number of bytes per line is too small")
    end
    if sizeof(buf) < bytesperline*height
        error("buffer is too small")
    end
    range = 1:2:(width & ~1)
    @inbounds for y in 1:height
        i = 1 + (y - 1)*bytesperline
        @simd for x in range
            b0, b1, b2 = buf[i], buf[i+1], buf[i+2]
            dst[x,y] = extractlowpacked(T, b0, b1)
            dst[x+1,y] = extracthighpacked(T, b1, b2)
            i += 3
        end
        if isodd(width)
            b0, b1 = buf[i], buf[i+1]
            dst[width,y] = extractlowpacked(T, b0, b1)
        end
    end
    return dst
end

"""
   syserrorinfo(code=Libc.errno())

yields the error message associated with `code`.

"""
syserrorinfo(code::Integer = Libc.errno()) =
    (Int(code), Libc.strerror(code))

"""
   reset_usb(dev)

resets USB device `dev`.

"""
reset_usb
@static if Sys.islinux()
    function reset_usb(dev::AbstractString)
        fd = ccall(:open, Cint, (Cstring, Cint), dev, AT.O_WRONLY)
        if fd == -1
            code, mesg = syserrorinfo()
            error("can't open USB device '$dev' for writing: $mesg (errno=$code)")
        end
        try
            if ccall(:ioctl, Cint, (Cint, Culong, Cint),
                     fd, AT.USBDEVFS_RESET, 0) == -1
                code, mesg = syserrorinfo()
                error("can't reset USB device '$dev': $mesg (errno=$code)")
            end
        finally
            if ccall(:close, Cint, (Cint,), fd) == -1
                code, mesg = syserrorinfo()
                @warn "failed to close USB device '$dev': $mesg (errno=$code)"
            end
        end
        nothing
    end
else
    function reset_usb(dev::AbstractString)
        @warn "don't know how to reset USB device '$dev' for your machine"
        nothing
    end
end

"""
    find_zyla()

yields the name of the USB device to which the Andor Zyla camera is connected.
An empty string is returned if no connected Andor Zyla cameras is found.

"""
find_zyla() = open(_find_zyla_proc, "r") do io; readline(io); end
const _find_zyla_path = joinpath(@__DIR__, "..", "deps", "find-zyla")
const _find_zyla_proc = `$_find_zyla_path`

"""
    reset_zyla(;quiet=false)

resets the USB device to which the Andor Zyla camera is connected.  If keyword
`quiet` is true, no warning is printed if no connected Andor Zyla cameras is
found.

"""
function reset_zyla(;quiet::Bool=false)
    dev = find_zyla()
    if dev != ""
        reset_usb(dev)
    elseif !quiet
        @warn "No Andor Zyla cameras found on USB bus."
    end
end

"""
    read_zyla(cam, T=getcapturebitstype(cam);
              reset=0, maxresets=10, ...)

reads one image from the Ando Zyla camera `cam`.

If 1st bit of keyword `reset` is set, then the USB device to which the Andor
Zyla camera is attached is reset prior to trying to read an image.  If 2nd bit
of of keyword `reset` is set, then the USB device is reset if a timeout
exception occurs while reading the camera and a new attempt to read an image is
performed.  Keyword `maxresets` specifies the maximum number of resets.

"""
function read_zyla(cam::Camera, ::Type{T} = getcapturebitstype(cam);
                   reset::Integer = 0,
                   maxresets::Integer = 10,
                   kwds...) where {T}
    nresets = 0
    if (reset & 1) != 0 && nresets < maxresets
        reset_zyla()
        nresets += 1
    end
    while true
        try
            return read(cam, T; kwds...)
        catch err
            if (reset & 2) != 0 && nresets < maxresets && isa(err, TimeoutError)
                reset_zyla()
                nresets += 1
            else
                rethrow(err)
            end
        end
    end
end

ext: jl


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

@doc raw"""EndpointSubset is a group of addresses with a common set of ports. The expanded set of endpoints is the Cartesian product of Addresses x Ports. For example, given:   {     Addresses: [{\"ip\": \"10.10.1.1\"}, {\"ip\": \"10.10.2.2\"}],     Ports:     [{\"name\": \"a\", \"port\": 8675}, {\"name\": \"b\", \"port\": 309}]   } The resulting set of endpoints can be viewed as:     a: [ 10.10.1.1:8675, 10.10.2.2:8675 ],     b: [ 10.10.1.1:309, 10.10.2.2:309 ]

    IoK8sApiCoreV1EndpointSubset(;
        addresses=nothing,
        notReadyAddresses=nothing,
        ports=nothing,
    )

    - addresses::Vector{IoK8sApiCoreV1EndpointAddress} : IP addresses which offer the related ports that are marked as ready. These endpoints should be considered safe for load balancers and clients to utilize.
    - notReadyAddresses::Vector{IoK8sApiCoreV1EndpointAddress} : IP addresses which offer the related ports but are not currently marked as ready because they have not yet finished starting, have recently failed a readiness check, or have recently failed a liveness check.
    - ports::Vector{IoK8sApiCoreV1EndpointPort} : Port numbers available on the related IP addresses.
"""
mutable struct IoK8sApiCoreV1EndpointSubset <: SwaggerModel
    addresses::Any # spec type: Union{ Nothing, Vector{IoK8sApiCoreV1EndpointAddress} } # spec name: addresses
    notReadyAddresses::Any # spec type: Union{ Nothing, Vector{IoK8sApiCoreV1EndpointAddress} } # spec name: notReadyAddresses
    ports::Any # spec type: Union{ Nothing, Vector{IoK8sApiCoreV1EndpointPort} } # spec name: ports

    function IoK8sApiCoreV1EndpointSubset(;addresses=nothing, notReadyAddresses=nothing, ports=nothing)
        o = new()
        validate_property(IoK8sApiCoreV1EndpointSubset, Symbol("addresses"), addresses)
        setfield!(o, Symbol("addresses"), addresses)
        validate_property(IoK8sApiCoreV1EndpointSubset, Symbol("notReadyAddresses"), notReadyAddresses)
        setfield!(o, Symbol("notReadyAddresses"), notReadyAddresses)
        validate_property(IoK8sApiCoreV1EndpointSubset, Symbol("ports"), ports)
        setfield!(o, Symbol("ports"), ports)
        o
    end
end # type IoK8sApiCoreV1EndpointSubset

const _property_map_IoK8sApiCoreV1EndpointSubset = Dict{Symbol,Symbol}(Symbol("addresses")=>Symbol("addresses"), Symbol("notReadyAddresses")=>Symbol("notReadyAddresses"), Symbol("ports")=>Symbol("ports"))
const _property_types_IoK8sApiCoreV1EndpointSubset = Dict{Symbol,String}(Symbol("addresses")=>"Vector{IoK8sApiCoreV1EndpointAddress}", Symbol("notReadyAddresses")=>"Vector{IoK8sApiCoreV1EndpointAddress}", Symbol("ports")=>"Vector{IoK8sApiCoreV1EndpointPort}")
Base.propertynames(::Type{ IoK8sApiCoreV1EndpointSubset }) = collect(keys(_property_map_IoK8sApiCoreV1EndpointSubset))
Swagger.property_type(::Type{ IoK8sApiCoreV1EndpointSubset }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_IoK8sApiCoreV1EndpointSubset[name]))}
Swagger.field_name(::Type{ IoK8sApiCoreV1EndpointSubset }, property_name::Symbol) =  _property_map_IoK8sApiCoreV1EndpointSubset[property_name]

function check_required(o::IoK8sApiCoreV1EndpointSubset)
    true
end

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


text:
using DrWatson
@quickactivate :FractalDimension # uses DynamicalSystems, PyPlot
include(srcdir("style.jl"))

N = 1*10^5

ds = range(2; step = 1, length = 6)

qH = 1
qC = 2

Cmethod = "standard" # bueno or standard. Decides the ε range.

systems = fill(:roessler_chaotic, 3)
append!(systems, fill(:roessler_dynamic, 2))
push!(systems, :roessler_rounded)
# imo a 10% dynamic noise would correspond to a η≈2 for Rössler,
# as the regularisation happens afterwards
parameters = ["η" => x for x in [0, 0.05, 0.1, 1, 2, 0]]
Cmethod = "standard" # bueno or standard. Decides εmax for correlation sum.

# Calculate values for H, C
eHs, eCs, Hs, Cs = [Vector{Float64}[] for i in 1:4]
for i in 1:length(systems)
    data = systems[i]
    params = @strdict N qH qC data
    if Cmethod ≠ "standard"
        params["Cmethod"] = Cmethod
    end
    push!(params, parameters[i])
    params["z"] = -1

    # Uncomment these lines to plot attractors
    # params["Δt"] = 0.1
    # data_producing_function = getfield(Data, data)
    # X = data_producing_function(; dict2ntuple(params)...)
    # figure()
    # plot3D(columns(X)...; lw = 1, alpha = 0.25)
    # scatter3D(columns(X)...; alpha = 0.25)
    # title(labels[i])

    # This is the main call that calculates everything
    output, s = produce_or_load(
        datadir("main"), params, make_C_H;
        prefix = string(data), suffix = "jld2", force = false,
        ignores = ["data"], storepatch = false,
    )
    @unpack eH, eC, H, C = output
    push!(eHs, eH); push!(Hs, H); push!(eCs, eC); push!(Cs, C)
end

# Do the actual plot
labels = ["none", "5% additive", "10% additive", "5% dynamic", "10% dynamic", "rounding"]
legendtitle = "noise"

fig, axs = mainplot(
    Hs, Cs, eHs, eCs, labels, legendtitle;
    qH, qC, tol = 0.25,
    # offsets = range(0; length = 6, step = 2),
    # dimension_fit_C = FractalDimension.logarithmic_corrected_fit_lsqfit,
    dimension_fit_C = FractalDimension.linear_regression_fit_glm,
)

# Find bueno-orovio distance for highlighting where things break down,
# and we would miss the stuff.

data = systems[5]
data_producing_function = getfield(Data, data)
X = data_producing_function(; N)
P = autoprismdim(X)
r0 = ChaosTools.estimate_r0_buenoorovio(X, P)[1]

axs[2].axvline(log(r0); color = "C0", lw = 2, ls = ":")

if Cmethod == "standard"
wsave(plotsdir("paper", "noise"), fig)
else
wsave(plotsdir("paper", "noise_$(Cmethod)"), fig)
end

max_stars_repo_name: Datseris/FractalDimension

max_issues_repo_name: Datseris/FractalDimension

max_forks_repo_name: Datseris/FractalDimension


text:
##############################################################################
#
# Wishart Distribution
#
#  Parameters nu and S such that E(X) = nu * S
#  See the rwish and dwish implementation in R's MCMCPack
#  This parametrization differs from Bernardo & Smith p 435
#  in this way: (nu, S) = (2.0 * alpha, 0.5 * beta^-1)
#
##############################################################################

immutable Wishart <: ContinuousMatrixDistribution
    nu::Float64
    Schol::Cholesky{Float64}
    function Wishart(n::Real, Sc::Cholesky{Float64})
        if n > size(Sc, 1) - 1
            new(float64(n), Sc)
        else
            error("Wishart parameters must be df > p - 1")
        end
    end
end

Wishart(nu::Real, S::Matrix{Float64}) = Wishart(nu, cholfact(S))

function insupport(W::Wishart, X::Matrix{Float64})
    return size(X, 1) == size(X, 2) && isApproxSymmmetric(X) &&
           size(X, 1) == size(W.Schol, 1) && hasCholesky(X)
end

mean(w::Wishart) = w.nu * w.Schol[:U]' * w.Schol[:U]

pdf(W::Wishart, X::Matrix{Float64}) = exp(logpdf(W, X))

function logpdf(W::Wishart, X::Matrix{Float64})
    if !insupport(W, X)
        return -Inf
    else
        p = size(X, 1)
        logd::Float64 = W.nu * p / 2.0 * log(2.0)
        logd += W.nu / 2.0 * log(det(W.Schol))
        logd += lpgamma(p, W.nu / 2.0)
        logd = -logd
        logd += 0.5 * (W.nu - p - 1.0) * logdet(X)
        logd -= 0.5 * trace(W.Schol \ X)
        return logd
    end
end

function rand(w::Wishart)
    p = size(w.Schol, 1)
    X = zeros(p, p)
    for ii in 1:p
        X[ii, ii] = sqrt(rand(Chisq(w.nu - ii + 1)))
    end
    if p > 1
        for col in 2:p
            for row in 1:(col - 1)
                X[row, col] = randn()
            end
        end
    end
    Z = X * w.Schol[:U]
    return Z' * Z
end

var(w::Wishart) = error("Not yet implemented")

# multivariate gamma / partial gamma function
function lpgamma(p::Int64, a::Float64)
    res::Float64 = p * (p - 1.0) / 4.0 * log(pi)
    for ii in 1:p
        res += lgamma(a + (1.0 - ii) / 2.0)
    end
    return res
end


text:
# convert_a3d

# Function that allows federation by setting the `output_format`  in the Stanmodel().

function convert_a3d(a3d_array, cnames, ::Val{:mcmcchain})
  Chains(a3d_array, names=cnames)
end


text:
module utility
export Float, each, flatten, identity, partition

# Load the submodules.
include("utility/reflect.jl")
using .Reflect
export anonymous_type, anonymous_module, define_function

if is(Int, Int64)
  typealias Float Float64
else
  typealias Float Float32
end

function partition(s::AbstractString, at::Union{AbstractString, Regex})
  i = search(s, at)
  i == 0:-1 ? (s, "", "") : (s[1:i.start-1], s[i], s[i.stop+1:end])
end

"""
Applies a given function to each member of a given array.
"""
each(f::Function, a::Array) = for e in a; f(e); end

identity(x::Any) = x

"""
Flattens a vector of vectors into a one-dimensional vector.

Credit to:
http://permalink.gmane.org/gmane.comp.lang.julia.user/860
"""
flatten{T}(a::Array{T,1}) =
  any(map(x -> isa(x, Array), a)) ? flatten(vcat(map(flatten, a)...)) : a
flatten{T}(a::Array{T}) = reshape(a, prod(size(a)))
flatten(a) = a
end


text:
function check_open_sell(config, open_orders, closed_orders, bot_path, client, usr)
    if length(open_orders["SELL"]) == 0
        return open_orders, closed_orders
    end
    fees = haskey(config, "current_fees") ? config["current_fees"][config["symbol"]] : client.get_trade_fee(symbol=config["symbol"])["tradeFee"][1]

    #
    ## Go through all open SELL orders
    for (k,v) in open_orders["SELL"]
        id_str = split(k, "_")[end]
        ##------------------------------------------------------------------------##
        ## Pinguu in STOP mode----------------------------------------------------##
        ##------------------------------------------------------------------------##
        if config["status"] == "stopped" && v["status"] != "stopped"
            check_stopped_orders(k, v, config, open_orders, closed_orders, bot_path, client, usr)
            continue
        elseif config["status"] == "active" && v["status"] == "stopped"
            check_orders_to_revive(k, v, config, open_orders, closed_orders, bot_path, client, usr)
            continue
        end

        ##------------------------------------------------------------------------##
        ## PLACE ORDER command----------------------------------------------------##
        ##------------------------------------------------------------------------##
        if v["status"] == "place order"
            place_sell_order_command(k, v, id_str, config, open_orders, closed_orders, bot_path, client, usr, fees)
            continue
        end

        ##------------------------------------------------------------------------##
        ## Load SELL updates------------------------------------------------------##
        ##------------------------------------------------------------------------##
        tmp = try client.get_order(
            symbol=config["symbol"],
            orderId=v["orderId"])
        catch
            @info("Skipped..")
            open(joinpath(bot_path, "trigger.json"), "w") do f
                JSON.print(f, Dict(), 4)
            end
            continue
        end

        ##------------------------------------------------------------------------##
        ## SELL Filled------------------------------------------------------------##
        ##------------------------------------------------------------------------##
        if tmp["status"] == client.ORDER_STATUS_FILLED
            sell_filled(tmp, k, v, id_str, config, open_orders, closed_orders, bot_path, client, usr, fees)
            continue
        end

        ##------------------------------------------------------------------------##
        ## SELL Canceled----------------------------------------------------------##
        ##------------------------------------------------------------------------##
        if tmp["status"] == client.ORDER_STATUS_CANCELED
            sell_canceled(tmp, k, v, id_str, config, open_orders, closed_orders, bot_path, client, usr, fees)
            continue
        end

        ##------------------------------------------------------------------------##
        ## Cancel SELL------------------------------------------------------------##
        ##------------------------------------------------------------------------##
        if tmp["status"] == "cancel order"
            cancel_sell(tmp, k, v, id_str, config, open_orders, closed_orders, bot_path, client, usr, fees)
            continue
        end

        ##------------------------------------------------------------------------##
        ## SELL status changed----------------------------------------------------##
        ##------------------------------------------------------------------------##
        if tmp["status"] != v["status"]
            sell_status_changed(tmp, k, v, id_str, config, open_orders, closed_orders, bot_path, client, usr, fees)
            continue
        end
    end
    return open_orders, closed_orders
end
export check_open_sell


text:
# title: Fitzhugh-Nagumo Bayesian Parameter Estimation Benchmarks
# author: Vaibhav Dixit, Chris Rackauckas

ProjDir = @__DIR__
using DiffEqBayesStan, BenchmarkTools, MCMCChains, StatsPlots
using Tables, AxisKeys, DataFrames, Random

using OrdinaryDiffEq, RecursiveArrayTools, Distributions
using ParameterizedFunctions, StanSample
using Plots
gr(fmt=:png)
Random.seed!(123)

### Defining the problem

# The [FitzHugh-Nagumo model](https://en.wikipedia.org/wiki/FitzHugh%E2%80%93Nagumo_model) is a simplified version of [Hodgkin-Huxley model](https://en.wikipedia.org/wiki/Hodgkin%E2%80%93Huxley_model) and is used to describe an excitable system (e.g. neuron).

fitz = @ode_def FitzhughNagumo begin
  dv = v - v^3/3 -w + l
  dw = τinv*(v +  a - b*w)
end a b τinv l

prob_ode_fitzhughnagumo = ODEProblem(fitz,[1.0,1.0],(0.0,10.0),[0.7,0.8,1/12.5,0.5])
sol = solve(prob_ode_fitzhughnagumo, Tsit5())

# Data is genereated by adding noise to the solution obtained above.

t = collect(range(1,stop=10,length=10))
sig = 0.20
data = convert(Array, VectorOfArray([(sol(t[i]) + sig*randn(2)) for i in 1:length(t)]))

### Plot of the data and the solution.

scatter(t, data[1,:])
scatter!(t, data[2,:])
plot!(sol)

### Priors for the parameters which will be passed for the Bayesian Inference

priors = [
  truncated(Normal(1.0,0.5),0,1.5),truncated(Normal(1.0,0.5),0,1.5),
  truncated(Normal(0.0,0.5),0.0,0.5),truncated(Normal(0.5,0.5),0,1)
]

### Benchmarks

#### Stan.jl backend

tmpdir = joinpath(ProjDir, "tmp")

diffeq_string = "
  vector sho(
    real t,vector internal_var___u,
    real internal_var___p_1, real internal_var___p_2,
    real internal_var___p_3, real internal_var___p_4) {

  vector[2] internal_var___du;

  internal_var___du[1] = internal_var___p_4 - 0.3333 * internal_var___u[1] ^ 3 + -1 * internal_var___u[2] + internal_var___u[1];
  internal_var___du[2] = internal_var___p_3 * (internal_var___p_1 + -1 * internal_var___p_2 * internal_var___u[2] + internal_var___u[1]);

  return internal_var___du;
}
"

bayesian_result_stan =
  stan_inference(prob_ode_fitzhughnagumo,t,data,priors;
    num_threads=4, num_cpp_chains=4, num_chains=2, num_samples=1250, output_format = :dataframe,
    diffeq_string, tmpdir);

describe(bayesian_result_stan.chains) |> display
println()

st =  read_samples(bayesian_result_stan.model, :table)
describe(DataFrame(st)) |> display
println()

ka = read_samples(bayesian_result_stan.model, :keyedarray)
ka |> display
println()

chns = read_samples(bayesian_result_stan.model, :mcmcchains)
plot(chns)


text:
@testset "LinkedList" begin

    @testset "l1" begin
        l = list([1,2,3]...)
        @test length(l) == 3
        @test head(l) == 1
        @test head(tail(l)) == 2
    end

end


text:
abstract type GraphNetwork <: AbstractNetwork end

"""
    evaluate(::AbstractNetwork, state)

    (nn::AbstractNetwork)(state) = evaluate(nn, state)

Evaluate the neural network as an MCTS oracle on a single state.

Note, however, that evaluating state positions once at a time is slow and so you
may want to use a `BatchedOracle` along with an inference server that uses
[`evaluate_batch`](@ref).
"""
function evaluate(nn::GraphNetwork, state)
  gspec = game_spec(nn)
  actions_mask = GI.actions_mask(GI.init(gspec, state))
  x = GI.graph_state(gspec, state)
  a = Float32.(actions_mask)
  # @show @__LINE__
  # @show sizeof(convert_input_tuple(nn, (x, a)))
  xnet, anet = convert_input_tuple(nn, (x, a))
  net_output = forward_normalized(nn, [xnet], anet)
  p, v, _ = from_singletons.(convert_output_tuple(nn, net_output))
  return (p[actions_mask], v[1])
end

"""
    evaluate_batch(::AbstractNetwork, batch)

Evaluate the neural network as an MCTS oracle on a batch of states at once.

Take a list of states as input and return a list of `(P, V)` pairs as defined in the
MCTS oracle interface.
"""
function evaluate_batch(nn::GraphNetwork, batch)
  gspec = game_spec(nn)
  X = Flux.batch((GI.graph_state(gspec, b) for b in batch))
  X = Flux.batch(Vector{typeof(X[1])}(X))
  A = Flux.batch((GI.actions_mask(GI.init(gspec, b)) for b in batch))
  Xnet, Anet = convert_input_tuple(nn, (X, Float32.(A)))
  P, V, _ = convert_output_tuple(nn, forward_normalized(nn, Xnet, Anet))
  return [(P[A[:,i],i], V[1,i]) for i in eachindex(batch)]
end


text:
finalize!(::Printer) = nothing
finalize!(::Plotter) = nothing
finalize!(writer::VTKWriter) = vtk_save(writer.pvd)


text:
using .ARPACK

## eigs

eigs(A; args...) = eigs(A, I; args...)

function eigs(A, B;
              nev::Integer=6, ncv::Integer=20, which::ASCIIString="LM",
              tol=0.0, maxiter::Integer=1000, sigma=nothing, v0::Vector=zeros((0,)),
              ritzvec::Bool=true)

    n = chksquare(A)

    T = eltype(A)
    iscmplx = T <: Complex
    isgeneral = B !== I
    sym = issym(A)
    (nev = min(nev, sym ? n - 1 : n - 2)) > 0 || throw(ArgumentError("nev must be at least one"))
    ncv = blas_int(min(max(2*nev + 2, ncv), n))
    isgeneral && !isposdef(B) && throw(PosDefException(0))
    bmat = isgeneral ? "G" : "I"
    isshift = sigma !== nothing
    sigma = isshift ? sigma : zero(T)

    if !isempty(v0)
        length(v0)==n || throw(DimensionMismatch(""))
        eltype(v0)==T || error("Starting vector must have eltype $T")
    else
        v0=zeros(T,(0,))
    end

    # Refer to ex-*.doc files in ARPACK/DOCUMENTS for calling sequence
    matvecA(x) = A * x
    if !isgeneral           # Standard problem
        matvecB(x) = x
        if !isshift         #    Regular mode
            mode       = 1
            solveSI(x) = x
        else                #    Shift-invert mode
            mode       = 3
            solveSI(x) = factorize(sigma==0 ? A : A - UniformScaling(sigma)) \ x
        end
    else                    # Generalized eigen problem
        matvecB(x) = B * x
        if !isshift         #    Regular inverse mode
            mode       = 2
            solveSI(x) = factorize(B) \ x
        else                #    Shift-invert mode
            mode       = 3
            solveSI(x) = factorize(sigma==0 ? A : A-sigma*B) \ x
        end
    end

    # Compute the Ritz values and Ritz vectors
    (resid, v, ldv, iparam, ipntr, workd, workl, lworkl, rwork, TOL) =
       ARPACK.aupd_wrapper(T, matvecA, matvecB, solveSI, n, sym, iscmplx, bmat, nev, ncv, which, tol, maxiter, mode, v0)

    # Postprocessing to get eigenvalues and eigenvectors
    if ritzvec
        (eval, evec, nconv, niter, nmult, resid) =
             ARPACK.eupd_wrapper(T, n, sym, iscmplx, bmat, nev, which, ritzvec, TOL,
                                 resid, ncv, v, ldv, sigma, iparam, ipntr, workd, workl, lworkl, rwork)
    else
        (eval, nconv, niter, nmult, resid) =
             ARPACK.eupd_wrapper(T, n, sym, iscmplx, bmat, nev, which, ritzvec, TOL,
                                 resid, ncv, v, ldv, sigma, iparam, ipntr, workd, workl, lworkl, rwork)
    end

end

max_stars_repo_name: jakebolewski/julia

max_issues_repo_name: jakebolewski/julia

max_forks_repo_name: jakebolewski/julia


text:
# Copyright 2018 Erik Edin
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

abstract type Engine end
abstract type OSAbstraction end

findfileswithextension(::OSAbstraction, ::String, ::String) = error("override this method")
readfile(::OSAbstraction, ::String) = error("override this method")
fileexists(::OSAbstraction, ::String) = error("override this method")

struct ExecutorEngine <: Engine
    accumulator::ResultAccumulator
    executor::Executor
    matcher::StepDefinitionMatcher
    selector::Selection.TagSelector

    function ExecutorEngine(realtimepresenter::RealTimePresenter;
                            executionenv=NoExecutionEnvironment(),
                            selector::Selection.TagSelector = Selection.AllScenarios)
        matcher = CompositeStepDefinitionMatcher()
        executor = Executor(matcher, realtimepresenter; executionenv=executionenv)
        new(ResultAccumulator(), executor, matcher, selector)
    end
end

addmatcher!(engine::ExecutorEngine, matcher::StepDefinitionMatcher) = addmatcher!(engine.matcher, matcher)

executionenvironment(engine::ExecutorEngine) = engine.executor.executionenv

"""
    runfeature!(::ExecutorEngine, ::Feature; [keepgoing=true])

Run the scenarios in a feature and record the result.

The keyword argument `keepgoing` (default: true) controls whether the execution stops
after a failing scenario (`keepgoing=false`), or continues (`keepgoing=true`).
"""
function runfeature!(engine::ExecutorEngine, feature::Feature; keepgoing=true)
    result = executefeature(engine.executor, feature; keepgoing=keepgoing)
    accumulateresult!(engine.accumulator, result)
end

"""
    runfeature!(::ExecutorEngine, ::Gherkin.OKParseResult{Feature})

Wrapper method for the above runfeature!.
"""
const GoodParseResultType = Union{Gherkin.OKParseResult{Feature}, Gherkin.Experimental.OKParseResult{Feature}}
function runfeature!(engine::ExecutorEngine, parseresult::GoodParseResultType, _featurefile::String, keepgoing::Bool)
    # Filter all features to run only the scenarios chosen by the tag selector, if any.
    # Any features or scenarios that do not match the tag selector will be removed here.
    filteredfeature = Selection.select(engine.selector, parseresult.value)

    # If there are no scenarios in this feature, then do not run it at all.
    # This matters because we don't want it listed in the results view having 0 successes
    # and 0 failures.
    if !isempty(filteredfeature.scenarios)
        runfeature!(engine, filteredfeature; keepgoing=keepgoing)
    end
end

"""
    runfeature!(::ExecutorEngine, ::Gherkin.BadParseResult{Feature})

A feature could not be parsed. Record the result.
"""
function runfeature!(engine::ExecutorEngine, parsefailure::Gherkin.BadParseResult{Feature}, featurefile::String, _keepgoing::Bool)
    accumulateresult!(engine.accumulator, parsefailure, featurefile)
end
function runfeature!(engine::ExecutorEngine, parsefailure::Gherkin.Experimental.BadParseResult{Feature}, featurefile::String, _keepgoing::Bool)
    accumulateresult!(engine.accumulator, parsefailure, featurefile)
end

finish(engine::ExecutorEngine) = engine.accumulator

struct Driver
    os::OSAbstraction
    engine::Engine

    Driver(os::OSAbstraction, engine::Engine) = new(os, engine)
end

function readstepdefinitions!(driver::Driver, path::String)
    stepdefinitionfiles = findfileswithextension(driver.os, path, ".jl")
    for f in stepdefinitionfiles
        addmatcher!(driver.engine, FromMacroStepDefinitionMatcher(readfile(driver.os, f), filename = f))
    end
end

function readfeature(driver::Driver, featurefile::String, parseoptions::ParseOptions)
    if parseoptions.use_experimental
        input = Gherkin.Experimental.ParserInput(read(featurefile, String))
        parser = Gherkin.Experimental.FeatureFileParser()
        parser(input)
    else
        parsefeature(readfile(driver.os, featurefile), options=parseoptions)
    end
end

function runfeatures!(driver::Driver, path::String; parseoptions::ParseOptions = ParseOptions(), keepgoing::Bool=true)
    featurefiles = findfileswithextension(driver.os, path, ".feature")

    # Call the hook that runs before any feature
    beforeall(executionenvironment(driver.engine))

    for featurefile in featurefiles
        featureparseresult = readfeature(driver, featurefile, parseoptions)
        runfeature!(driver.engine, featureparseresult, featurefile, keepgoing)

        if !issuccess(driver.engine.accumulator) && !keepgoing
            break
        end
    end

    # Call the hook that runs after all features
    afterall(executionenvironment(driver.engine))

    finish(driver.engine)
end


text:
using Plots
using DifferentialEquations

plotlyjs()

mutable struct stream
    """State properties
    """
    q
    Ss
    Snh
    Xb
    Xphb
end

function kinetics(s)
    """Returns the rate constants for the growth (r1) and accumulation (r2) reaction, given the state.
    """

    μₘₐₓ_g = 4.56;  # d-1	Maximum biomass growth rate
    μₘₐₓ_p = 3.6;  # d-1	Maximum PHA production rate
    KSₛ_g = 2.98;  # kg/m3	half sat coeff for growth
    KSₛ_p = 9.3;  # kg/m3	half sat coeff for PHA production
    KSₙₕ_g = 0.021;  # gN/m3	Ammonia sat coeff for growth
    KIₙₕ_p = 0.021;  # gN/m3	Ammonia inhib coeff for PHA uptake
    KSᵢ = 2.68; # kg/m3 substate inhibition coef for microbe activity
    PHAₘₐₓ = 0.8;  # max fraction of PHA content

    V, Ss, Snh, Xb, Xphb = s; # extract variables from state

    Xₚₕₐ = Xphb / (Xb + Xphb);

    r1 = μₘₐₓ_g * Xb * Ss / (KSₛ_g + Ss) * Snh / (KSₙₕ_g + Snh); # [kg/m3.d] growth
    r2 = μₘₐₓ_p * Xb * Ss / (KSₛ_p + Ss) * KIₙₕ_p / (KIₙₕ_p + Snh) * PHAₘₐₓ / (PHAₘₐₓ + Xₚₕₐ) * KSᵢ / (KSᵢ + Ss); # [kg/m3.d] accumulation

    r1, r2
end

function pha_reactions!(ds, s, p, t)
    V, Ss, Snh, Xb, Xphb = s
    IN, OUT = p

    r1, r2 = kinetics(s)

    # mass stoichiometry
    v = [
    -2 -0.71740413 -0.150442478 1 0 0.986430678; # growth
    -1.666666667 -0.103359173 0 0 1 0.397932817; # accumulation
    ];

    ds[1] = dV = IN.q - OUT.q # reactor volume
    ds[2] = dSs = IN.q / V * (IN.Ss - Ss) + v[1, 1] * r1 + v[2, 1] * r2 # Substate
    ds[3] = dSnh = IN.q / V * (IN.Snh - Snh) + v[1, 3] * r1 + v[2, 3] * r2 # Ammonia
    ds[4] = dXb = IN.q / V * (IN.Xb - Xb) + v[1, 4] * r1 + v[2, 4] * r2 # bacteria
    ds[5] = dXphb = IN.q / V * (IN.Xphb - Xphb) + v[1, 5] * r1 + v[2, 5] * r2 # PHB

    ds

end

function batch_feed_policy(t, F_Ss, Q)
    """
    t, time [days]
    F_Ss, Fresh feed glucose concentration [kg/m3]
    Q, fresh feed rate [L/d ]
    """
    Ss = F_Ss
    Snh = 0
    if t < 1
        # first day conditions for growth
        Snh = 10.0 # Ammonia for first day
        Ss = 140.00  # g/L in both feeds.
    end
    stream(Q, Ss, Snh, 0, 0)
end

function no_flow(t)
    """Outlet condition of zero flow
    """
    stream(0, 0, 0, 0, 0)
end

function fedBatchConfig(S0, tspan, F_Ss, Q = 2.0)
    inlet = t->batch_feed_policy(t, F_Ss, Q)
    outlet = t->no_flow(t)
    p = (inlet, outlet)
    prob = ODEProblem(batchSystem!, S0, tspan, p)
    sol = solve(prob, Rodas4())
end

function batchConfig(S0, tspan)
    inlet = t->no_flow(t)
    outlet = t->no_flow(t)
    p = (inlet, outlet)
    prob = ODEProblem(batchSystem!, S0, tspan, p)
    sol = solve(prob, Rodas4())
end

function batchSystem!(ds, S, P, t)
    p = (P[1](t), P[2](t))
    ds = pha_reactions!(ds, S, p, t)
end

function chemostat_reactor(ds, s, p, t)

    ds = pha_reactions!(ds, s, p, t)

    in, out = p

    out = stream(out.q, s[2], s[3], s[4], s[5])

    ds, out
end

function chemostat_system!(DS, S, P, t)
    stream_states = 5
    IN, OUT = P
    num_reactors = length(IN)
    s = S[1:stream_states]
    ds = DS[1:stream_states]
    p = (IN[1], stream(IN[1].q, 0, 0, 0, 0))
    ds, out = chemostat_reactor(ds, s, p, t)
    DS[1:stream_states] = ds

    for i = 2:num_reactors
        in = mixer(IN[i], out)
        s = S[stream_states * (i - 1) + 1:stream_states * i]
        ds = DS[stream_states * (i - 1) + 1:stream_states * i]
        p = (in, stream(in.q, 0, 0, 0, 0))
        ds, out = chemostat_reactor(ds, s, p, t)
        DS[stream_states * (i - 1) + 1:stream_states * i] = ds
    end
    DS
end

function mixer(m1, m2)
    q = m1.q + m2.q

    Ss = (m1.q * m1.Ss + m2.q * m2.Ss) / q
    Snh = (m1.q * m1.Snh + m2.q * m2.Snh) / q
    Xb = (m1.q * m1.Xb + m2.q * m2.Xb) / q
    Xphb = (m1.q * m1.Xphb + m2.q * m2.Xphb) / q

    stream(q, Ss, Snh, Xb, Xphb)
end

function gen_Qfresh_Array(QF, R, n)
    Rs = [R^(i - 2) for i = 2:n]
    base_R = sum(Rs)
    Qfreshs = QF .* Rs ./ base_R
end

function process_flow_config_n(V, tspan, S0, n, QF = 1.0, R = 1.0)

    Qfresh = QF / (n - 1)
    IN = [stream(2.0, 140.00, 10.0, 0.0, 0.0)]

    Qfreshs = gen_Qfresh_Array(QF, R, n)

    for Qfresh in Qfreshs
        push!(IN, stream(Qfresh, 140.0, 0.0, 0.0, 0.0))
    end
    OUT = stream(Qfresh, 0, 0, 0, 0)
    P = (IN, OUT)
    prob = ODEProblem(chemostat_system!, S0, tspan, P)
    sol = solve(prob, Rodas4())
    # gui()
    # plot(sol)
    return sol
end

function make_S0(V, s)
    base_S0 = [V, 1e-6, 1e-6, 1e-6, 1e-6];
    S0 = [];
    for i = 1:s
        append!(S0, base_S0);
    end
    S0;
end

function initial_HRT_plot(V_span, S0s, labels, step = 0.1)
    rs = length(S0s)
    Cs = Array{Float64}(undef, 0, rs);

    Vs = [V for V = V_span[1]:step:V_span[2]];

    QF = 0.75; # baseline fresh feed rate
    R = 1.0; # baseline fresh feed distribution ratio

    for V in Vs # calculate for each system volume
        tspan = (0.0, 100.0);
        C = Array{Float64}(undef, 1, rs); # array to store steady state PHB conc in

        for (i, S0) in enumerate(S0s) # calculate for each number of accumulation reactors
            num_r = length(S0) / 5.0
            Vr = V / num_r; # accumulation reactor volume
            for k = 6:5:length(S0)
                S0[k] = Vr # specify accumulation reactor volums
            end
            sol = process_flow_config_n(V, tspan, S0, num_r, QF, R);

            # extract steady state PHB concentration
            Xphb = sol.u[end][end];
            C[i] = Xphb;

        end

        Cs = [Cs; C];
    end

    sysVol = Vs .+ S0s[1][1];
    sysInletQ = 2 + QF;
    HRT = sysVol / sysInletQ;
    yield = (Cs .* sysInletQ) ./ (sysInletQ * 140.00)

    plot(HRT, yield, xlabel = "Hydraulic Retention Time (d)", ylabel = "Yield (g PHB / g Glucose)", label = labels, lw = 3)
    # savefig("chemostat_trains.svg")

    # add fed batch to plot
    tspan = (0.0, 8.0)
    S0 = [0.5, 140.0, 10.0, 5.0, 0.0]
    F_Ss = 140.0;
    fedbatch_sol = fedBatchConfig(S0, tspan, F_Ss)
    HRT = (fedbatch_sol[1,:] .- 0.5) ./ 2;
    yield = (fedbatch_sol[5,:] .* fedbatch_sol[1,:]) ./ (S0[1] * S0[2] .+ F_Ss .* fedbatch_sol[1,:])
    plot!(HRT, yield, label = "FB(Qf=2)", lw = 3, linestyle = :dot)

    # basic batch
    tspan = (0.0, 8.0)
    S0 = [10, 140.0, 10.0, 5.0, 0.0]
    batch_sol = batchConfig(S0, tspan)
    yield = (batch_sol[5,:] .* batch_sol[1,:]) ./ (S0[2] * S0[1])

    plot!(batch_sol.t, yield, label = "B", lw = 3, linestyle = :dot)
    # savefig("Reactor Configuration Comparison.svg")

    # concentration profiles for batch reactors
    gui()
    plot(batch_sol, title = "Batch Reactor")
    gui()
    plot(fedbatch_sol, title = "Fed Batch")

end

# Chemostat initial conditions
V0 = 2;
S01 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0];
S02 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0];
S03 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0];
S04 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0];
S05 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0];

labels = [ "C(n=1, Qf=0.75, R=1)" "C(n=2, Qf=0.75, R=1)" "C(n=3, Qf=0.75, R=1)" "C(n=4, Qf=0.75, R=1)" "C(n=5, Qf=0.75, R=1)" ];

S0s = [S01, S02, S03, S04, S05]

######################
# initial assessment #
######################
initial_HRT_plot([0.1, 20.00], S0s, labels)

################
# optimisation #
################

# fed batch conc comparison:
tspan = (0.0, 11.0)
S0 = [0.5, 140.0, 10.0, 5.0, 0.0]

function FB_glucose_feed_comp()
    # gui()
    # plot()
    for F_Ss in [10.0, 20.0, 40.0, 70.0, 100.0, 140.0]
        # gui()
        gui()
        fedbatch_sol = fedBatchConfig(S0, tspan, F_Ss);
        HRT = (fedbatch_sol[1,:] .- 0.5) ./ 2;
        yield = (fedbatch_sol[5,:] .* fedbatch_sol[1,:]) ./ (S0[1] * S0[2] .+ F_Ss .* (fedbatch_sol[1,:] .- S0[1]));
        plot!(HRT, fedbatch_sol[5,:], label = "Feed Glucose $F_Ss g/L", lw = 3, palette = :Spectral_8)
    end
    plot!(xlabel = "Hydraulic Retention Time (d)", ylabel = "PHB Concentration (g / L)")
end
gui()
plot()
FB_glucose_feed_comp()

function FB_feed_rate()
    # gui()
    # plot()
    F_Ss = 140.0
    for Q in [0.0, 0.05, 0.10, 0.15, 0.25,  0.5, 1.0, 2.0]

        # gui()
        gui()
        fedbatch_sol = fedBatchConfig(S0, tspan, F_Ss, Q);
        HRT = (fedbatch_sol[1,:] .- 0.5) ./ Q;
        yield = (fedbatch_sol[5,:] .* fedbatch_sol[1,:]) ./ (S0[1] * S0[2] .+ F_Ss .* (fedbatch_sol[1,:] .- S0[1]));
        plot!(HRT, yield, label = "FB(Qf=$Q L/d)", lw = 3, palette = :Spectral_8)
    end
    plot!(xlabel = "Hydraulic Retention Time (d)", ylabel = "Yield (g PHB / g Glucose)")
    # savefig("FB_feed_rate.svg")
end
gui()
plot()
FB_feed_rate()

function C_fresh_feed_rate()
    # gui()
    # plot()
    step = 0.1

    V0 = 2.0;
    S0 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0]

    V_span = [0.1, 40.00];

    Vs = [V for V = V_span[1]:step:V_span[2]];

    # QFs = [0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
    QFs = [0.85, 1.0, 1.15, 1.25, 1.35, 1.5]

    rs = length(QFs)
    Cs = Array{Float64}(undef, 0, rs);
    yields = Array{Float64}(undef, 0, rs);
    HRTs = Array{Float64}(undef, 0, rs);

    for V in Vs
        tspan = (0.0, 100.0);
        C = Array{Float64}(undef, 1, rs);
        yield = Array{Float64}(undef, 1, rs);
        HRT = Array{Float64}(undef, 1, rs);

        for (i, QF) in enumerate(QFs)
            # S0 = make_S0(V, length(S0s[i]));
            # println(S0)
            num_r = length(S0) / 5.0
            Vr = V / num_r;
            for k = 6:5:length(S0)
                S0[k] = Vr
            end
            sol = process_flow_config_n(V, tspan, S0, num_r, QF);

            Xphb = sol.u[end][end];
            C[i] = Xphb;
            yield[i] = Xphb * (2.0 + QF) / ((2.0 + QF) * 140.0)
            sysVol = V + S0[1];
            HRT[i] = sysVol / (2.0 + QF);
            # S0s[i] = sol.u[end]

        end

        Cs = [Cs; C];
        yields = [yields; yield]
        HRTs = [HRTs; HRT]
    end

    labels = Array{String}(undef, 1, rs);
    for (i, QF) in enumerate(QFs)
        labels[i] = "C(n=3, Qf=$QF, R=1)"
    end

    plot(HRTs, yields, xlabel = "Hydraulic Retention Time (d)", ylabel = "Yield (g PHB / g Glucose)", xlims = (0, 7.5), label = labels, lw = 3, palette = :Spectral_6)
    # savefig("chemostat_feed_rate.svg")
end
gui()
plot()
C_fresh_feed_rate()

function C_fresh_feed_distribution()
    # gui()
    # plot()
    step = 0.1

    V0 = 2.0;
    S0 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0]

    V_span = [0.1, 40.00];

    Vs = [V for V = V_span[1]:step:V_span[2]];

    Rs = [0.7, 0.8, 0.9, 1.0, 1.1, 1.2]

    rs = length(Rs)
    Cs = Array{Float64}(undef, 0, rs);
    yields = Array{Float64}(undef, 0, rs);
    HRTs = Array{Float64}(undef, 0, rs);

    QF = 1.25;

    for V in Vs
        tspan = (0.0, 100.0);
        C = Array{Float64}(undef, 1, rs);
        yield = Array{Float64}(undef, 1, rs);
        HRT = Array{Float64}(undef, 1, rs);

        for (i, R) in enumerate(Rs)
            # S0 = make_S0(V, length(S0s[i]));
            # println(S0)
            num_r = length(S0) / 5.0
            Vr = V / num_r;
            for k = 6:5:length(S0)
                S0[k] = Vr
            end
            sol = process_flow_config_n(V, tspan, S0, num_r, QF, R);

            Xphb = sol.u[end][end];
            C[i] = Xphb;
            yield[i] = Xphb * (2.0 + QF) / ((2.0 + QF) * 140.0)
            sysVol = V + S0[1];
            HRT[i] = sysVol / (2.0 + QF);
            # S0s[i] = sol.u[end]

        end

        Cs = [Cs; C];
        yields = [yields; yield]
        HRTs = [HRTs; HRT]
    end

    labels = Array{String}(undef, 1, rs);
    for (i, R) in enumerate(Rs)
        Qfreshs = gen_Qfresh_Array(QF, R, length(S0) / 5)
        Q1 = Qfreshs[1]

        labels[i] = "C(n=3, Qf=$QF, R=$R)"
    end

    plot(HRTs, yields, xlabel = "Hydraulic Retention Time (d)", ylabel = "Yield (g PHB / g Glucose)", xlims = (0, 7.5), label = labels, lw = 3, palette = :Spectral_6)
    # savefig("chemostat_feed_ratio.svg")
end
gui()
plot()
C_fresh_feed_distribution()

###
# Best against original #
###

function chemstat_result_com(QF = 1.25, R = 1.0)

    step = 0.1

    V0 = 2.0;
    S0 = [V0, 140.0, 10.0, 100.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0, V0, 40.0, 0.0, 10.0, 0.0]

    V_span = [0.1, 40.00];

    Vs = [V for V = V_span[1]:step:V_span[2]];

    Rs = [R]

    rs = length(Rs)
    Cs = Array{Float64}(undef, 0, rs);
    yields = Array{Float64}(undef, 0, rs);
    HRTs = Array{Float64}(undef, 0, rs);

    for V in Vs
        tspan = (0.0, 100.0);
        C = Array{Float64}(undef, 1, rs);
        yield = Array{Float64}(undef, 1, rs);
        HRT = Array{Float64}(undef, 1, rs);

        for (i, R) in enumerate(Rs)

            num_r = length(S0) / 5.0
            Vr = V / num_r;
            for k = 6:5:length(S0)
                S0[k] = Vr
            end
            sol = process_flow_config_n(V, tspan, S0, num_r, QF, R);

            Xphb = sol.u[end][end];
            C[i] = Xphb;
            yield[i] = Xphb * (2.0 + QF) / ((2.0 + QF) * 140.0)
            sysVol = V + S0[1];
            HRT[i] = sysVol / (2.0 + QF);
            # S0s[i] = sol.u[end]

        end

        Cs = [Cs; C];
        yields = [yields; yield]
        HRTs = [HRTs; HRT]
    end

    labels = Array{String}(undef, 1, rs);
    for (i, R) in enumerate(Rs)
        Qfreshs = gen_Qfresh_Array(QF, R, length(S0) / 5)
        Q1 = Qfreshs[1]

        labels[i] = "C(n=3, Qf=$QF, R=$R)"
    end

    return HRTs, yields, labels
end

gui()
F_Ss = 140.0
Q = 2.0
fedbatch_sol = fedBatchConfig(S0, tspan, F_Ss, Q);
HRT = (fedbatch_sol[1,:] .- 0.5) ./ Q;
yield = (fedbatch_sol[5,:] .* fedbatch_sol[1,:]) ./ (S0[1] * S0[2] .+ F_Ss .* (fedbatch_sol[1,:] .- S0[1]));
plot(HRT, yield, label = "FB(Qf=$Q)", lw = 3, linestyle = :dot, linecolor = RGB(50 / 255, 136 / 255, 189 / 255))

Q = 0.25
fedbatch_sol = fedBatchConfig(S0, tspan, F_Ss, Q);
HRT = (fedbatch_sol[1,:] .- 0.5) ./ Q;
yield = (fedbatch_sol[5,:] .* fedbatch_sol[1,:]) ./ (S0[1] * S0[2] .+ F_Ss .* (fedbatch_sol[1,:] .- S0[1]));
plot!(HRT, yield, label = "FB(Qf=$Q)", lw = 3, linecolor = RGB(50 / 255, 136 / 255, 189 / 255))

HRTs, yields, labels = chemstat_result_com(0.75, 1.0)
plot!(HRTs, yields, xlims = (0, 7.5), label = labels, lw = 3, linestyle = :dot, linecolor = RGB(213 / 255, 62 / 255, 79 / 255))

HRTs, yields, labels = chemstat_result_com(1.25, 0.8)
plot!(HRTs, yields, xlims = (0, 7.5), label = labels, lw = 3, linecolor = RGB(213 / 255, 62 / 255, 79 / 255))

plot!(xlabel = "Hydraulic Retention Time (d)", ylabel = "Yield (g PHB / g Glucose)")

# savefig("optimised_improvement.svg")

max_stars_repo_name: rileyodon/PHBmodel

max_issues_repo_name: rileyodon/PHBmodel

max_forks_repo_name: rileyodon/PHBmodel


text:
# Unstable features, preview status. May change at any time.
module Experimental

import ..Publish
import ..Publish.Objects: Figure, Table
import TOML

export Page, Project, Figure, Table, deploy

"""
    Page(blocks...)

Virtual page contents in a markdown file. Each block can be either a `String`,
which is interpreted as markdown text and directly embedded in the page, or any
other value, which will be embedded in a `{cell}` block.
"""
struct Page
    blocks::Vector{Any}
    Page(blocks...) = new(collect(blocks))
end

Base.show(io::IO, ::Page) = print(io, "$Page(...)")

"""
    Project(pages...; config...)

Virtual Publish project object for programmatically building a document.
`pages` can be any number of `Page` objects. `config` is any number of keyword
arguments which is used to build the generated project's `Project.toml`.
"""
struct Project
    pages::Vector{Page}
    config::Dict
end

function Project(pages...; config...)
    # Convert the nested `config` into `Dict{String,Any}` structure that can be
    # saved as a TOML file.
    _nt_dict(ps::Iterators.Pairs) = Dict(string(k) => _nt_dict(v) for (k, v) in ps)
    _nt_dict(nt::NamedTuple) = Dict(string(k) => _nt_dict(v) for (k, v) in pairs(nt))
    _nt_dict(d::Dict) = Dict(string(k) => _nt_dict(v) for (k, v) in d)
    _nt_dict(other) = other

    config = _nt_dict(config)
    get!(config, "name", "Project") # Ensure that name exists.
    return Project(collect(pages), config)
end

Base.show(io::IO, ::Project) = print(io, "$Project(...)")

"""
    deploy(project::Project, out, formats...; clean)

Build a Publish project from the given "virtual" `project` object.  Output is
built in `out` directory, which is either an absolute path or relative to the
present working directory. `formats` is the list of formats to build in `out`:
`pdf` and `html` are supported.

# Keywords

  - `clean = false` controls whether to only include the final document in the
    provided `out` folder. This is only support for `formats` that are *all*
    classified as 'standalone' (`pdf` is currently the only such format).
"""
function Publish.deploy(p::Project, out::AbstractString, formats...; clean = false)
    out = isabspath(out) ? out : joinpath(pwd(), out)
    sandbox() do
        project = "Project.toml"
        open(project, "w") do io
            TOML.print(io, p.config)
        end
        # Build a table of contents for the virtual Pages.  Print out the
        # contents as well for each Page and capture the cell values for
        # evaluation.
        toc = "toc.md"
        __CELLS__ = []
        open(toc, "w") do toc_io
            for (n, page) in enumerate(p.pages)
                println(toc_io, "  - [$n]($n.md)")
                open("$n.md", "w") do page_io
                    for block in page.blocks
                        if isa(block, String)
                            println(page_io, block)
                        else
                            push!(__CELLS__, block)
                            cell =
                                """
                                {:cell display = false output = false}
                                ```julia
                                __CELLS__[$(length(__CELLS__))]
                                ```
                                """
                            println(page_io, cell)
                        end
                    end
                end
            end
        end
        # Make the cell values available to the cells.
        mod = Module()
        Core.eval(mod, :(export __CELLS__; __CELLS__ = $__CELLS__))
        globals = Dict("publish" => Dict("cell-imports" => [mod]))

        # Curry the deploy function since we only need to vary the destination.
        function deployer(destination::AbstractString)
            Publish.deploy(
                project,
                destination,
                formats...;
                force = true,
                versioned = false,
                globals = globals,
            )
        end

        # Build and deploy the generated project.
        if clean && is_standalone(formats)
            mktempdir() do dir
                deployer(dir)
                isdir(out) || mkpath(out)
                file = p.config["name"] * ".pdf"
                cp(joinpath(dir, file), joinpath(out, file); force = true)
            end
        else
            deployer(out)
        end
    end
    return out
end

is_standalone(::typeof(Publish.pdf)) = true
is_standalone(::typeof(Publish.html)) = false
is_standalone(tuple::Tuple) = all(is_standalone, tuple)

sandbox(f) = mktempdir(dir -> cd(f, dir))

end


text:
import Base: collect, eachindex, getindex, length, ndims, size

"""
ParameterSpace collects all parameters of a system as well as samples in the parameter space.
"""
struct ParameterSpace{PT <: NamedTuple, ST <: Table}
    parameters::PT
    samples::ST

    function ParameterSpace(parameters::PT, samples::ST) where {PT,ST}
        # for s in samples
        #     @assert length(parameters) == length(s)
        # end
        new{PT,ST}(parameters, samples)
    end
end

function ParameterSpace(sampler::ParameterSampler, parameters::Vararg{Parameter})
    ParameterSpace(NamedTuple(parameters...), sample(sampler, parameters...))
end

function ParameterSpace(parameters::Vararg{Parameter})
    ParameterSpace(CartesianParameterSampler(), parameters...)
end

function ParameterSpace(parameters::NamedTuple)
    ParameterSpace(values(parameters)...)
end

Base.:(==)(ps1::ParameterSpace, ps2::ParameterSpace) = (
                        ps1.parameters == ps2.parameters
                     && ps1.samples    == ps2.samples)

(ps::ParameterSpace)(i::Union{Int,CartesianIndex}) = NamedTuple{keys(ps.parameters)}(ps.samples[i])

Base.collect(ps::ParameterSpace) = collect(ps.samples)
Base.eachindex(ps::ParameterSpace) = eachindex(ps.samples)
Base.length(ps::ParameterSpace) = length(ps.samples)
Base.ndims(ps::ParameterSpace) = length(ps.parameters)
Base.size(ps::ParameterSpace) = (length(ps.samples), length(ps.parameters))
Base.size(ps::ParameterSpace, d) = size(ps)[d]

@inline Base.@propagate_inbounds Base.getindex(ps::ParameterSpace, args...) = getindex(ps.samples, args...)

function ParameterSpace(h5::H5DataStore, path::AbstractString="/")
    group = h5[path]
    samps = group["samples"]
    sinds = Symbol.(keys(samps))
    svals = (read(samps[key]) for key in keys(samps))
    samples = NamedTuple{Tuple(sinds)}(Tuple(svals))

    pgroup = group["parameters"]
    params = NamedTuple{Symbol.(Tuple(keys(pgroup)))}(h5load(Parameter, pgroup, key) for key in keys(pgroup))

    ParameterSpace(params, Table(; samples...))
end

"""
save ParameterSpace
"""
function h5save(ps::ParameterSpace, h5::H5DataStore, path::AbstractString="/")
    cols = columns(ps.samples)
    g = _create_group(h5, path)
    s = _create_group(g, "samples")
    for key in keys(cols)
        s[string(key)] = cols[key]
    end

    p = _create_group(g, "parameters")
    for param in ps.parameters
        h5save(param, p)
    end
end

"""
Load ParameterSpace
"""
function h5load(::Type{ParameterSpace}, h5::H5DataStore, path::AbstractString="/")
    ParameterSpace(h5, path)
end


text:
# This file was generated, do not modify it. # hide
rename!(X2, :x1 => :interaction);