text:
# # Predator-prey dynamics

# ```@raw html
# <video width="auto" controls autoplay loop>
# <source src="../sheepwolf.mp4" type="video/mp4">
# </video>
# ```

# The predator-prey model emulates the population dynamics of predator and prey animals who
# live in a common ecosystem and compete over limited resources. This model is an
# agent-based analog to the classic
# [Lotka-Volterra](https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations)
# differential equation model. This example illustrates how to develop models with
# heterogeneous agents (sometimes referred to as a *mixed agent based model*).

# The environment is a two dimensional grid containing sheep, wolves and grass. In the
# model, wolves eat sheep and sheep eat grass. Their populations will oscillate over time
# if the correct balance of resources is achieved. Without this balance however, a
# population may become extinct. For example, if wolf population becomes too large,
# they will deplete the sheep and subsequently die of starvation.

# We will begin by loading the required packages and defining three subtypes of
# `AbstractAgent`: `Sheep`, Wolf, and `Grass`. All three agent types have `id` and `pos`
# properties, which is a requirement for all subtypes of `AbstractAgent` when they exist
# upon a `GridSpace`. Sheep and wolves have identical properties, but different behaviors
# as explained below. The property `energy` represents an animals current energy level.
# If the level drops below zero, the agent will die. Sheep and wolves reproduce asexually
# in this model, with a probability given by `reproduction_prob`. The property `Δenergy`
# controls how much energy is acquired after consuming a food source.

# Grass is a replenishing resource that occupies every position in the grid space. Grass can be
# consumed only if it is `fully_grown`. Once the grass has been consumed, it replenishes
# after a delay specified by the property `regrowth_time`. The property `countdown` tracks
# the delay between being consumed and the regrowth time.

# It is also available from the `Models` module as [`Models.predator_prey`](@ref).

using Agents, Random

mutable struct SheepWolf <: AbstractAgent
    id::Int
    pos::Dims{2}
    type::Symbol # :sheep or :wolf
    energy::Float64
    reproduction_prob::Float64
    Δenergy::Float64
end

# Simple helper functions
Sheep(id, pos, energy, repr, Δe) = SheepWolf(id, pos, :sheep, energy, repr, Δe)
Wolf(id, pos, energy, repr, Δe) = SheepWolf(id, pos, :wolf, energy, repr, Δe)

# The function `initialize_model` returns a new model containing sheep, wolves, and grass
# using a set of pre-defined values (which can be overwritten). The environment is a two
# dimensional grid space, which enables animals to walk in all
# directions.

function initialize_model(;
    n_sheep = 100,
    n_wolves = 50,
    dims = (20, 20),
    regrowth_time = 30,
    Δenergy_sheep = 4,
    Δenergy_wolf = 20,
    sheep_reproduce = 0.04,
    wolf_reproduce = 0.05,
    seed = 23182,
)

    rng = MersenneTwister(seed)
    space = GridSpace(dims, periodic = false)
    ## Model properties contain the grass as two arrays: whether it is fully grown
    ## and the time to regrow. Also have static parameter `regrowth_time`.
    ## Notice how the properties are a `NamedTuple` to ensure type stability.
    properties = (
        fully_grown = falses(dims),
        countdown = zeros(Int, dims),
        regrowth_time = regrowth_time,
    )
    model = ABM(SheepWolf, space; properties, rng, scheduler = schedule_randomly)
    id = 0
    for _ in 1:n_sheep
        id += 1
        energy = rand(1:(Δenergy_sheep*2)) - 1
        sheep = Sheep(id, (0, 0), energy, sheep_reproduce, Δenergy_sheep)
        add_agent!(sheep, model)
    end
    for _ in 1:n_wolves
        id += 1
        energy = rand(1:(Δenergy_wolf*2)) - 1
        wolf = Wolf(id, (0, 0), energy, wolf_reproduce, Δenergy_wolf)
        add_agent!(wolf, model)
    end
    for p in positions(model) # random grass initial growth
        fully_grown = rand(model.rng, Bool)
        countdown = fully_grown ? regrowth_time : rand(model.rng, 1:regrowth_time) - 1
        model.countdown[p...] = countdown
        model.fully_grown[p...] = fully_grown
    end
    return model
end

# The function `sheepwolf_step!` is dispatched on the sheep and wolves similarly:
# both lose 1 energy unit by moving to an adjacent position and both consume
# a food source if available. If their energy level is below zero, they die.
# Otherwise, they live and reproduces with some probability.

# Sheep and wolves move to a random adjacent position with the [`walk!`](@ref) function.

function sheepwolf_step!(agent::SheepWolf, model)
    if agent.type == :sheep
        sheep_step!(agent, model)
    else # then `agent.type == :wolf`
        wolf_step!(agent, model)
    end
end

function sheep_step!(sheep, model)
    walk!(sheep, rand, model)
    sheep.energy -= 1
    sheep_eat!(sheep, model)
    if sheep.energy < 0
        kill_agent!(sheep, model)
        return
    end
    if rand(model.rng) <= sheep.reproduction_prob
        reproduce!(sheep, model)
    end
end

function wolf_step!(wolf, model)
    walk!(wolf, rand, model)
    wolf.energy -= 1
    agents = collect(agents_in_position(wolf.pos, model))
    dinner = filter!(x -> x.type == :sheep, agents)
    wolf_eat!(wolf, dinner, model)
    if wolf.energy < 0
        kill_agent!(wolf, model)
        return
    end
    if rand(model.rng) <= wolf.reproduction_prob
        reproduce!(wolf, model)
    end
end

# Sheep and wolves have separate `eat!` functions. If a sheep eats grass, it will acquire
# additional energy and the grass will not be available for consumption until regrowth time
# has elapsed. If a wolf eats a sheep, the sheep dies and the wolf acquires more energy.

function sheep_eat!(sheep, model)
    if model.fully_grown[sheep.pos...]
        sheep.energy += sheep.Δenergy
        model.fully_grown[sheep.pos...] = false
    end
end

function wolf_eat!(wolf, sheep, model)
    if !isempty(sheep)
        dinner = rand(model.rng, sheep)
        kill_agent!(dinner, model)
        wolf.energy += wolf.Δenergy
    end
end

# Sheep and wolves share a common reproduction method. Reproduction has a cost of 1/2 the
# current energy level of the parent. The offspring is an exact copy of the parent, with
# exception of `id`.
function reproduce!(agent, model)
    agent.energy /= 2
    id = nextid(model)
    offspring = SheepWolf(
        id,
        agent.pos,
        agent.type,
        agent.energy,
        agent.reproduction_prob,
        agent.Δenergy,
    )
    add_agent_pos!(offspring, model)
    return
end

# The behavior of grass functions differently. If it is fully grown, it is consumable.
# Otherwise, it cannot be consumed until it regrows after a delay specified by
# `regrowth_time`. The grass is tuned from a model stepping function

function grass_step!(model)
    @inbounds for p in positions(model) # we don't have to enable bound checking
        if !(model.fully_grown[p...])
            if model.countdown[p...] ≤ 0
                model.fully_grown[p...] = true
                model.countdown[p...] = model.regrowth_time
            else
                model.countdown[p...] -= 1
            end
        end
    end
end

model = initialize_model()

# ## Running the model
# %% #src
# We will run the model for 500 steps and record the number of sheep, wolves and consumable
# grass patches after each step. First: initialize the model.
using InteractiveDynamics
using CairoMakie

# To view our starting population, we can build an overview plot using [`abm_plot`](@ref).
# We define the plotting details for the wolves and sheep:
offset(a) = a.type == :sheep ? (-0.7, -0.5) : (-0.3, -0.5)
ashape(a) = a.type == :sheep ? :circle : :utriangle
acolor(a) = a.type == :sheep ? RGBAf0(1.0, 1.0, 1.0, 0.8) : RGBAf0(0.2, 0.2, 0.2, 0.8)

# and instruct [`abm_plot`](@ref) how to plot grass as a heatmap:
grasscolor(model) = model.countdown ./ model.regrowth_time
# and finally define a colormap for the grass:
heatkwargs = (colormap = [:brown, :green], colorrange = (0, 1))

plotkwargs = (
    ac = acolor,
    as = 15,
    am = ashape,
    offset = offset,
    heatarray = grasscolor,
    heatkwargs = heatkwargs,
)

fig, _ = abm_plot(model; plotkwargs...)
fig

# Now, lets run the simulation and collect some data. Define datacollection:
sheep(a) = a.type == :sheep
wolves(a) = a.type == :wolf
count_grass(model) = count(model.fully_grown)
# Run simulation:
model = initialize_model()
n = 500
adata = [(sheep, count), (wolves, count)]
mdata = [count_grass]
adf, mdf = run!(model, sheepwolf_step!, grass_step!, n; adata, mdata)

# The following plot shows the population dynamics over time.
# Initially, wolves become extinct because they consume the sheep too quickly.
# The few remaining sheep reproduce and gradually reach an
# equilibrium that can be supported by the amount of available grass.
function plot_population_timeseries(adf, mdf)
    figure = Figure(resolution = (600, 400))
    ax = figure[1, 1] = Axis(figure; xlabel = "Step", ylabel = "Population")
    sheepl = lines!(ax, adf.step, adf.count_sheep, color = :blue)
    wolfl = lines!(ax, adf.step, adf.count_wolves, color = :orange)
    grassl = lines!(ax, mdf.step, mdf.count_grass, color = :green)
    figure[1, 2] = Legend(figure, [sheepl, wolfl, grassl], ["Sheep", "Wolves", "Grass"])
    figure
end

plot_population_timeseries(adf, mdf)

# Altering the input conditions, we now see a landscape where sheep, wolves and grass
# find an equilibrium
model = initialize_model(
    n_wolves = 20,
    dims = (25, 25),
    Δenergy_sheep = 5,
    sheep_reproduce = 0.2,
    wolf_reproduce = 0.08,
    seed = 7756,
)
adf, mdf = run!(model, sheepwolf_step!, grass_step!, n; adata, mdata)

plot_population_timeseries(adf, mdf)

# ## Video
# Given that we have defined plotting functions, making a video is as simple as

model = initialize_model(
    n_wolves = 20,
    dims = (25, 25),
    Δenergy_sheep = 5,
    sheep_reproduce = 0.2,
    wolf_reproduce = 0.08,
    seed = 7756,
)
abm_video(
    "sheepwolf.mp4",
    model,
    sheepwolf_step!,
    grass_step!;
    frames = 150,
    framerate = 8,
    plotkwargs...,
)

# ```@raw html
# <video width="auto" controls autoplay loop>
# <source src="../sheepwolf.mp4" type="video/mp4">
# </video>
# ```


text:
config[@__DIR__] = (
    directory=@__DIR__,
    case="AlSiO2H",
    n_repeat=20,
    kwargs_scf=(kspacing=0.15, smearing=Smearing.Gaussian(), temperature=0.001),
    firstmixkey="HybridVac",
    #
    construct_localiser=(lattice, atoms) -> identity,
    mixings=Dict(
        # "Custom"      => same as HybridVac
        "HybridGaAs"  => (α, localiser) -> HybridMixing(α=α, kTF=1.0, εr=14, localisation=identity),
        "HybridVac"   => (α, localiser) -> HybridMixing(α=α, kTF=1.0, εr=1,  localisation=identity),
        "Kerker"      => (α, localiser) -> KerkerMixing(α=α, kTF=1.0),
        "None"        => (α, localiser) -> SimpleMixing(α=α),
        "RestaGaAs"   => (α, localiser) -> RestaMixing( α=α, kTF=1.0, εr=14),
    ),
)

ext: jl


text:
@test simplify(:x/:x,Div)==1

@test simplify(@equ a=c*c/c).rhs==:c
@test simplify(@equ a=c^2/c).rhs==:c
@test simplify(:a^3.5/:a)==:a^2.5

E=@equ E=sqrt(p^2*c^2+m^2*c^4)
vars=@equs p=sqrt(2)*1e6/c m=0.5e6/c^2
r=E&vars[2]
@test r.rhs==Equations.Sqrt(:p*:p*:c*:c+2.5e11)


text:
#!/usr/bin/env julia
if length(ARGS) != 4
    throw(ArgumentError("""
        wrong number of arguments. Usage:
            julia generate_shims.jl binary jll_package jll_func binpath
            """))
end

binary, jll_package, jll_func, yggbindir = ARGS

const LIBPATH_ENV = Sys.islinux() ? "LD_LIBRARY_PATH" : "DYLD_FALLBACK_LIBRARY_PATH"

code = """
import $(jll_package)

$(jll_package).$(jll_func)() do f
    # Print out the full path to the executable
    println(f)

    # Configure env
    env = Dict{String,String}()
    ## PATH
    env["PATH"] = ENV["PATH"]
    ## LD_LIBRARY_PATH
    env["$(LIBPATH_ENV)"] = ENV["$(LIBPATH_ENV)"]

    # Git requires some extra variables
    if "$(binary)" == "git"
        artifact_root = dirname(dirname(f))
        ## Add libcurl to libpath
        curl_libdir = dirname(Git_jll.LibCURL_jll.libcurl_path)
        env["$(LIBPATH_ENV)"] = "\$(curl_libdir):\$(env["$(LIBPATH_ENV)"])"
        ## Set GIT_EXEC_PATH
        env["GIT_EXEC_PATH"] = joinpath(artifact_root, "libexec", "git-core")
        ## Set GIT_TEMPLATE_DIR
        env["GIT_TEMPLATE_DIR"] = joinpath(artifact_root, "share", "git-core", "templates")
        ## Set GIT_SSL_CAINFO
        env["GIT_SSL_CAINFO"] = joinpath(dirname(Sys.BINDIR), "share", "julia", "cert.pem")
    end

    # Print env to stdout
    for k in sort(collect(keys(env)))
        print(k, "=\\\"", env[k], "\\\" ")
    end
end
"""

exepath, env = split(strip(read(`$(Base.julia_cmd()) -e $code`, String)), '\n')

shimpath = joinpath(yggbindir, binary)
mkpath(dirname(shimpath))
open(shimpath, "w") do io
    print(io, """
        #!/bin/bash
        $(env) exec -a "\$0" $(basename(exepath)) "\$@"
        """)
end

# chmod +x
chmod(shimpath, filemode(shimpath) | 0o111)

max_stars_repo_name: fredrikekre/y

max_issues_repo_name: fredrikekre/y

max_forks_repo_name: fredrikekre/y


text:
# syntax: proto3
mutable struct DoubleValue <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function DoubleValue(; kwargs...)
        obj = new(meta(DoubleValue), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct DoubleValue
const __meta_DoubleValue = Ref{ProtoMeta}()
function meta(::Type{DoubleValue})
    ProtoBuf.metalock() do
        if !isassigned(__meta_DoubleValue)
            __meta_DoubleValue[] = target = ProtoMeta(DoubleValue)
            allflds = Pair{Symbol,Union{Type,String}}[:value => Float64]
            meta(target, DoubleValue, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_DoubleValue[]
    end
end
function Base.getproperty(obj::DoubleValue, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::Float64
    else
        getfield(obj, name)
    end
end

mutable struct FloatValue <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function FloatValue(; kwargs...)
        obj = new(meta(FloatValue), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct FloatValue
const __meta_FloatValue = Ref{ProtoMeta}()
function meta(::Type{FloatValue})
    ProtoBuf.metalock() do
        if !isassigned(__meta_FloatValue)
            __meta_FloatValue[] = target = ProtoMeta(FloatValue)
            allflds = Pair{Symbol,Union{Type,String}}[:value => Float32]
            meta(target, FloatValue, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_FloatValue[]
    end
end
function Base.getproperty(obj::FloatValue, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::Float32
    else
        getfield(obj, name)
    end
end

mutable struct Int64Value <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Int64Value(; kwargs...)
        obj = new(meta(Int64Value), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Int64Value
const __meta_Int64Value = Ref{ProtoMeta}()
function meta(::Type{Int64Value})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Int64Value)
            __meta_Int64Value[] = target = ProtoMeta(Int64Value)
            allflds = Pair{Symbol,Union{Type,String}}[:value => Int64]
            meta(target, Int64Value, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Int64Value[]
    end
end
function Base.getproperty(obj::Int64Value, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::Int64
    else
        getfield(obj, name)
    end
end

mutable struct UInt64Value <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function UInt64Value(; kwargs...)
        obj = new(meta(UInt64Value), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct UInt64Value
const __meta_UInt64Value = Ref{ProtoMeta}()
function meta(::Type{UInt64Value})
    ProtoBuf.metalock() do
        if !isassigned(__meta_UInt64Value)
            __meta_UInt64Value[] = target = ProtoMeta(UInt64Value)
            allflds = Pair{Symbol,Union{Type,String}}[:value => UInt64]
            meta(target, UInt64Value, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_UInt64Value[]
    end
end
function Base.getproperty(obj::UInt64Value, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::UInt64
    else
        getfield(obj, name)
    end
end

mutable struct Int32Value <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Int32Value(; kwargs...)
        obj = new(meta(Int32Value), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Int32Value
const __meta_Int32Value = Ref{ProtoMeta}()
function meta(::Type{Int32Value})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Int32Value)
            __meta_Int32Value[] = target = ProtoMeta(Int32Value)
            allflds = Pair{Symbol,Union{Type,String}}[:value => Int32]
            meta(target, Int32Value, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Int32Value[]
    end
end
function Base.getproperty(obj::Int32Value, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::Int32
    else
        getfield(obj, name)
    end
end

mutable struct UInt32Value <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function UInt32Value(; kwargs...)
        obj = new(meta(UInt32Value), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct UInt32Value
const __meta_UInt32Value = Ref{ProtoMeta}()
function meta(::Type{UInt32Value})
    ProtoBuf.metalock() do
        if !isassigned(__meta_UInt32Value)
            __meta_UInt32Value[] = target = ProtoMeta(UInt32Value)
            allflds = Pair{Symbol,Union{Type,String}}[:value => UInt32]
            meta(target, UInt32Value, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_UInt32Value[]
    end
end
function Base.getproperty(obj::UInt32Value, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::UInt32
    else
        getfield(obj, name)
    end
end

mutable struct BoolValue <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function BoolValue(; kwargs...)
        obj = new(meta(BoolValue), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct BoolValue
const __meta_BoolValue = Ref{ProtoMeta}()
function meta(::Type{BoolValue})
    ProtoBuf.metalock() do
        if !isassigned(__meta_BoolValue)
            __meta_BoolValue[] = target = ProtoMeta(BoolValue)
            allflds = Pair{Symbol,Union{Type,String}}[:value => Bool]
            meta(target, BoolValue, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_BoolValue[]
    end
end
function Base.getproperty(obj::BoolValue, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct StringValue <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function StringValue(; kwargs...)
        obj = new(meta(StringValue), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct StringValue
const __meta_StringValue = Ref{ProtoMeta}()
function meta(::Type{StringValue})
    ProtoBuf.metalock() do
        if !isassigned(__meta_StringValue)
            __meta_StringValue[] = target = ProtoMeta(StringValue)
            allflds = Pair{Symbol,Union{Type,String}}[:value => AbstractString]
            meta(target, StringValue, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_StringValue[]
    end
end
function Base.getproperty(obj::StringValue, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct BytesValue <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function BytesValue(; kwargs...)
        obj = new(meta(BytesValue), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct BytesValue
const __meta_BytesValue = Ref{ProtoMeta}()
function meta(::Type{BytesValue})
    ProtoBuf.metalock() do
        if !isassigned(__meta_BytesValue)
            __meta_BytesValue[] = target = ProtoMeta(BytesValue)
            allflds = Pair{Symbol,Union{Type,String}}[:value => Array{UInt8,1}]
            meta(target, BytesValue, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_BytesValue[]
    end
end
function Base.getproperty(obj::BytesValue, name::Symbol)
    if name === :value
        return (obj.__protobuf_jl_internal_values[name])::Array{UInt8,1}
    else
        getfield(obj, name)
    end
end

export DoubleValue, FloatValue, Int64Value, UInt64Value, Int32Value, UInt32Value, BoolValue, StringValue, BytesValue


text:
using Images
using Iterators

#Base.call(Generator)=("Testing $(now())",["unittest1.png","unittest2.png"])
type FractalGenerator
   cameraspec::CameraSpec
   imagespec::ImageSpec
   fracspec::FractalSpec
end

function Base.call(fracgen::FractalGenerator)
   image_filename="temp.png"

   @time img=generate_fractal(fracgen)
   return ("Generated a fractal! $(now())",image_filename)
end

function generate_fractal(fracgen::FractalGenerator)
   w,h=fracgen.imagespec.width,fracgen.imagespec.height
   simgdata=SharedArray(RGB8bit,(w,h))
   # points=product(1:fracgen.imagespec.width,1:fracgen.imagespec.height)
   # @sync @parallel for point in points
   #    simgdata[point[1],point[2]]=JuliaFractal.get_pixel_color(fracgen,point[2],point[1])
   # end
   @sync @parallel for j in 1:w
      @inbounds @fastmath for i in 1:h
         simgdata[i,j]=get_pixel_color(fracgen,j,i)
      end
      # println("$j/$w")
   end
   # @sync @parallel for i in 1:w*h
   #    r=floor(Int,(i-1)/w)+1
   #    c=(i-1)%w+1
   #    simgdata[r,c]=get_pixel_color(fracgen,c,r)
   # end

   # imagedata=sdata(simgdata)
   imagedata=Array{RGB8bit}(w,h)
   imagedata[:,:]=sdata(simgdata)
   # # 1010 seconds
   # points=product(1:fracgen.imagespec.width,1:fracgen.imagespec.height)
   # getpixel=point->imagedata[point[1],point[2]]=get_pixel_color(fracgen,point[1],point[2])
   # pmap(getpixel,points)
   # 90 seconds in normal loop
   # for px in 1:fracgen.imagespec.width
   #    for py in 1:fracgen.imagespec.height
   #       imagedata[px,py]=get_pixel_color(fracgen,px,py)
   #    end
   # end
   return Image(imagedata)
end

max_stars_repo_name: jblondin/Fractal

max_issues_repo_name: jblondin/Fractal

max_forks_repo_name: jblondin/Fractal


text:
using Interpolations, Test

@testset "Core" begin
    A = reshape([0], 1, 1, 1, 1, 1)
    wis = ntuple(d->Interpolations.WeightedAdjIndex(1, (1,)), ndims(A))
    @test @inferred(A[wis...]) === 0
    wis = ntuple(d->Interpolations.WeightedAdjIndex(1, (1.0,)), ndims(A))
    @test @inferred(A[wis...]) === 0.0
    wis = ntuple(d->Interpolations.WeightedArbIndex((1,), (1,)), ndims(A))
    @test @inferred(A[wis...]) === 0
    wis = ntuple(d->Interpolations.WeightedArbIndex((1,), (1.0,)), ndims(A))
    @test @inferred(A[wis...]) === 0.0
    wis = ntuple(d->Interpolations.WeightedArbIndex((1,1), (1,0)), ndims(A))
    @test @inferred(A[wis...]) === 0
    wis = ntuple(d->Interpolations.WeightedArbIndex((1,1), (1.0,0.0)), ndims(A))
    @test @inferred(A[wis...]) === 0.0

    wi = Interpolations.WeightedAdjIndex(2, (0.2, 0.8))
    @test wi*2 === Interpolations.WeightedAdjIndex(2, (0.4, 1.6))
    @test wi/2 === Interpolations.WeightedAdjIndex(2, (0.1, 0.4))
    @test Interpolations.indextuple(wi) == (2, 3)
    wi = Interpolations.WeightedArbIndex((2, -1), (0.2, 0.8))
    @test wi*2 === Interpolations.WeightedArbIndex((2, -1), (0.4, 1.6))
    @test wi/2 === Interpolations.WeightedArbIndex((2, -1), (0.1, 0.4))
    @test Interpolations.indextuple(wi) == (2, -1)
end


text:
function NNlib.count_indices(idx::AnyCuArray)
    dst_counts = length.(NNlib.reverse_indices(idx))
    src_counts = NNlib.gather(cu(dst_counts), idx)
    return src_counts
end

function divide_kernel!(xs, ys, max_idx)
    index = threadIdx().x + (blockIdx().x - 1) * blockDim().x

    @inbounds if index <= max_idx
        xs[index] = xs[index] / ys[index]
    end
    return nothing
end

function divide_kernel!(xs, counts, max_idx, max_dims_idx, dims_size)
    index = threadIdx().x + (blockIdx().x - 1) * blockDim().x

    @inbounds if index <= max_idx
        j, k = divrem(index-1, max_dims_idx)
        dims_i = Tuple(CartesianIndices(dims_size)[k+1])
        @atomic xs[dims_i..., j+1] = xs[dims_i..., j+1] / counts[j+1]
    end
    return nothing
end

function NNlib.divide_by_counts!(xs::AnyCuArray{T}, idx::AnyCuArray, dims) where {T}
    counts = CuArray{T}(NNlib.count_indices(idx))
    args = if dims == 0
        max_idx = length(idx)
        xs, counts, max_idx
    else
        dims_size = size(xs)[1:dims]
        max_dims_idx = prod(dims_size)
        max_idx = prod(size(xs))
        xs, counts, max_idx, max_dims_idx, dims_size
    end

    kernel = @cuda launch=false divide_kernel!(args...)
    config = launch_configuration(kernel.fun; max_threads=256)
    threads = min(max_idx, config.threads)
    blocks = cld(max_idx, threads)
    kernel(args...; threads=threads, blocks=blocks)
    return xs
end

function NNlib.reverse_indices(idx::AnyCuArray{<:Any,N}) where N
    max_dims = NNlib.maximum_dims(idx)
    T = CartesianIndex{N}
    rev = Array{Vector{T}}(undef, max_dims...)
    for i in eachindex(rev)
        rev[i] = T[]
    end
    NNlib.reverse_indices!(rev, idx)
    return map(cu, rev)
end


text:
# module ShaleDrillingLikelihood_CounterfactualObjects_Test
using Revise

using ShaleDrillingLikelihood
using ShaleDrillingLikelihood.SDLParameters
using Distributed
using Test
using SharedArrays

using ShaleDrillingLikelihood: drill, _x, _D, LeaseCounterfactual,
    ObservationGroup, InitialDrilling, ichars, zchars, uniti,
    end_ex0, _data, _i, simulate_lease!, Pprime,
    state_if_never_drilled, tstart, jtstart, first_state,
    update_sparse_state_transition!, vw_revenue,
    split_thetas, SimulationTmp, SimulationPrimitives,
    simulationPrimitives_information, dataFramesForSimulations,
    SharedSimulations, update_sim_dataframes_from_simdata!,
    value_function,check_theta, doSimulations

num_i = 50
M = 10
nz = 13
nψ = 13

datafull, thetafull = last(MakeTestData(; num_i=num_i, nz=nz, nψ=nψ))

using ShaleDrillingLikelihood: SharedPosterior, _qm, _ψ2, royalty, produce,
    num_i

posteriors = SharedPosterior(datafull, M)
sim = SimulationDraws(M, datafull)

@eval @everywhere set_g_BaseDataSetofSets($datafull)
@eval @everywhere set_g_SimulationDrawsMatrix($sim)
@eval @everywhere set_g_SharedPosterior($posteriors)

N = num_i(datafull)

let x=theta, dograd=dograd, kwargs=kwargs
    pmap(i -> simloglik_posterior!(i, x), wp, OneTo(N))
end

psi2 = _ψ2(sim)
post_d = sdata(drill(posteriors))
post_r = sdata(royalty(posteriors))
post_p = sdata(produce(posteriors))


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

module ResolveUtils

using UUIDs
import ..Pkg # ensure we are using the correct Pkg
using Pkg.Types
using Pkg.Resolve
using Pkg.Resolve: VersionWeight, add_reqs!, simplify_graph!, ResolverError, Fixed, Requires

export sanity_tst, resolve_tst, VERBOSE

# print info, stats etc.
const VERBOSE = false

# auxiliary functions
const uuid_package = UUID("cfb74b52-ec16-5bb7-a574-95d9e393895e")
pkguuid(p::String) = uuid5(uuid_package, p)
function storeuuid(p::String, uuid_to_name::Dict{UUID,String})
    uuid = p == "julia" ? Resolve.uuid_julia : pkguuid(p)
    if haskey(uuid_to_name, uuid)
        @assert uuid_to_name[uuid] == p
    else
        uuid_to_name[uuid] = p
    end
    return uuid
end
wantuuids(want_data) = Dict{UUID,VersionNumber}(pkguuid(p) => v for (p,v) in want_data)

"""
    graph = graph_from_data(deps_data)

Generate a package dependency graph from the entries in the array `deps_data`, where each entry
is an array of the form `["PkgName", v"x.y.z", "DependencyA", v"Ax.Ay.Az", ...]`.
This states that the package "PkgName" with version `v"x.y.z"` depends on "DependencyA" with the
specified compatibility information.
"""
function graph_from_data(deps_data)
    uuid_to_name = Dict{UUID,String}()
    uuid(p) = storeuuid(p, uuid_to_name)
    fixed = Dict{UUID,Fixed}()
    all_compat = Dict{UUID,Dict{VersionNumber,Dict{UUID,VersionSpec}}}()

    deps = Dict{String,Dict{VersionNumber,Dict{String,VersionSpec}}}()
    for d in deps_data
        p, vn, r = d[1], d[2], d[3:end]
        if !haskey(deps, p)
            deps[p] = Dict{VersionNumber,Dict{String,VersionSpec}}()
        end
        if !haskey(deps[p], vn)
            deps[p][vn] = Dict{String,VersionSpec}()
        end
        isempty(r) && continue
        rp = r[1]
        rvs = VersionSpec(r[2:end])
        deps[p][vn][rp] = rvs
    end
    for (p,preq) in deps
        u = uuid(p)

        deps_pkgs = Dict{String,Set{VersionNumber}}()
        for (vn,vreq) in deps[p], rp in keys(vreq)
            push!(get!(Set{VersionNumber}, deps_pkgs, rp), vn)
        end
        all_compat[u] = Dict{VersionNumber,Dict{UUID,VersionSpec}}()
        for (vn,vreq) in preq
            all_compat[u][vn] = Dict{UUID,VersionSpec}()
            for (rp,rvs) in vreq
                all_compat[u][vn][uuid(rp)] = rvs
            end
        end
    end
    return Graph(all_compat, uuid_to_name, Requires(), fixed, VERBOSE)
end
function reqs_from_data(reqs_data, graph::Graph)
    reqs = Dict{UUID,VersionSpec}()
    function uuid_check(p)
        uuid = pkguuid(p)
        @assert graph.data.uuid_to_name[uuid] == p
        return uuid
    end
    for r in reqs_data
        p = uuid_check(r[1])
        reqs[p] = VersionSpec(r[2:end])
    end
    reqs
end
function sanity_tst(deps_data, expected_result; pkgs=[])
    if VERBOSE
        println()
        @info("sanity check")
        # @show deps_data
        # @show pkgs
    end
    graph = graph_from_data(deps_data)
    id(p) = pkgID(pkguuid(p), graph)
    result = sanity_check(graph, Set(pkguuid(p) for p in pkgs), VERBOSE)

    length(result) == length(expected_result) || return false
    expected_result_uuid = [(id(p), vn) for (p,vn) in expected_result]
    for r in result
        r ∈ expected_result_uuid || return  false
    end
    return true
end
sanity_tst(deps_data; kw...) = sanity_tst(deps_data, []; kw...)

function resolve_tst(deps_data, reqs_data, want_data = nothing; clean_graph = false)
    if VERBOSE
        println()
        @info("resolving")
        # @show deps_data
        # @show reqs_data
    end
    graph = graph_from_data(deps_data)
    reqs = reqs_from_data(reqs_data, graph)
    add_reqs!(graph, reqs)
    simplify_graph!(graph, clean_graph = clean_graph)
    want = resolve(graph)
    return want == wantuuids(want_data)
end

end


text:
function esplot(regmodel::T where T<:SupportedEstimation; normalized_period::Any=missing, by=x->parse(Int64,x), verbose::Bool=false)
    # Event Study Specification can be formulated by @formula(outcome_it ~ treatment_i & eventtime_t + ...) with the keyward arg contrast
    # "contrasts = Dict(:eventtime_t => DummyCoding(base = event_time))"
    # this function is a shortcut to plot event studies that are formulated like this.
    coefplot = esparse(regmodel; normalized_period=normalized_period, by=by, verbose=verbose)
    plot(coefplot; verbose)
end

function get_time_from_coefname(coefnames; pre_print::Function=x->Int64(parse(Float64,x)))
    time_marks = Pair[]
    for coefname in coefnames
        timevar_name_treatment_name_and_time_mark = strip.(split(coefname)) # return a vector of "timevar_name:", treatment_name and time_mark, but might be of random order
        # find timevar_name by looking at which element ends with a colon
        istimevar = map(timevar_name_treatment_name_and_time_mark) do x
            endswith(x,":")
        end
        timevar_ind = findfirst(istimevar)
        if checkbounds(Bool, timevar_name_treatment_name_and_time_mark, timevar_ind + 1)
            time_mark = timevar_name_treatment_name_and_time_mark[timevar_ind + 1]
        else
            throw(AssertionError("Fail to extract time mark!"))
        end
        push!(time_marks, Symbol(coefname) => string(pre_print(time_mark)))
    end
    return time_marks
end

function esparse(regmodel::T where T<:SupportedEstimation; normalized_period::Any=missing, by=x->parse(Int64,x), pre_print::Function=x->Int64(parse(Float64,x)), verbose::Bool=false, extend::Bool=true)
    # Event Study Specification can be formulated by @formula(outcome_it ~ treatment_i & eventtime_t + ...) with the keyward arg contrast
    # "contrasts = Dict(:eventtime_t => DummyCoding(base = event_time))"
    # this function is a shortcut to plot event studies that are formulated like this.

    coef_names = coefnames(regmodel)
    keys_of_changes = get_time_from_coefname(coef_names; pre_print)
    parsed_model = parse(regmodel)
    coefrename!(parsed_model, keys_of_changes...)

    # re-insert normalized the event_time
    if ~ismissing(normalized_period)
        Base.push!(parsed_model, :normalized_period => empty_sc(label = string(normalized_period)))
    end
    composite_by = x -> by(x.thiscoef_label)
    sort!(parsed_model.dict, by = composite_by, byvalue=true)
    equidist!(parsed_model)
    transpose!(parsed_model)
    if extend
        include_tails!(parsed_model)
    end

    return parsed_model
end

function include_tails!(parsed_model::Coefplot)
    first_sc_sym, first_sc = first(parsed_model.dict)
    first_sc.thiscoef_label = join(["\$\\leq\$",first_sc.thiscoef_label])
    parsed_model.dict[first_sc_sym] = first_sc

    last_sc_sym, last_sc = last(parsed_model.dict)
    last_sc.thiscoef_label = join(["\$\\geq\$",last_sc.thiscoef_label])
    parsed_model.dict[last_sc_sym] = last_sc
    return parsed_model
end

Base.last(itr::OrderedDict, n::Integer) = reverse!(collect(Iterators.take(Iterators.reverse(collect(itr)), n)))
Base.last(itr::OrderedDict) = first(Base.last(itr::OrderedDict, 1))


text:
using Parsers

function read_arrays_jl_prealloc(filename)
  lines = split.(readlines(filename), "#")
  D = Dict(
    Parsers.parse(Int, line[1]) => Parsers.parse.(Int, split(line[2], ","))
    for line in lines
  )
  n = sum(length(v) for (k, v) in D)
  keys = zeros(Int, n)
  indexes = zeros(Int, n)
  elements = zeros(Int, n)

  count = 0
  for (k, v) in D
    m = length(v)
    idx = count+1:count+m
    keys[idx] .= k
    indexes[idx] .= 0:m-1
    elements[idx] .= v
    count += m
  end

  return keys, indexes, elements
end


text:
module Accessors
using MacroTools
using MacroTools: isstructdef, splitstructdef, postwalk
using Requires: @require

include("setindex.jl")
include("optics.jl")
include("sugar.jl")
include("functionlenses.jl")
include("testing.jl")

function __init__()
    @require StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" begin
        setindex(a::StaticArrays.StaticArray, args...) = Base.setindex(a, args...)
    end
end

end


text:
import CuArrays
# CuArrays.allowscalar(false)

include("../models/AttentionEmbed/Transformer.jl")
include("../src/TensorOps.jl")

const TimeSteps = 100
const EmbDims = 128
const Vocab = Integer(1e3)
const Layers = 8
const Stacks = 1

const X = map(a->Int32.(a), [[3,7,3,5], [8,6,4,5,2,1], [10,7,5], [11,3]]);
const Y = map(a->Int32.(a), [[3,7,3,5], [8,6,4,5,2,1], [10,7,5], [11,3]]);

const Input = SequenceFeed(EmbDims, Vocab, TimeSteps)
const Encode = Encoding(EmbDims, TimeSteps, Layers, Stacks)
const Decode = Decoding(EmbDims, TimeSteps, Layers, Stacks)
const Output = DenseProjection(Vocab)
const θ = Flux.params(Input, Encode, Decode, Output)

const opt = Flux.ADAM(0.1);

function Model(x::AbstractArray{T, 1}, y::AbstractArray{T, 1}) where T
    EncSeqLens = SequenceLengths(x)
    x = Input(x)
    x, EncMask, context = Encode(x, EncSeqLens)

    DecSeqLens = SequenceLengths(y)
    y = Input(y)
    y = Decode(x, y, DecSeqLens, context, EncMask, Encode.Attn)
    y = Output(y, Input.Emb.E)
    return y
end

function Loss(x::AbstractArray{T, 1}, y::AbstractArray{T, 1}) where T
    ϵ = Float32(1e-8)
    ŷ = Model(x, y)
    # GC.gc()
    (Steps, Labels, Batches) = size(ŷ)

    # y = map(seq -> Float32.(permutedims(Flux.onehotbatch(seq, 1:Labels), [2, 1])) |> Flux.param, y)
    # y = ArrayPad(y, Steps)
    y = map(seq -> Float32.(permutedims(Flux.onehotbatch(seq, 1:Labels), [2, 1])) |> Flux.data, y)
    y = SequencePad(y, Steps)

    s = TensorSoftmax(ŷ, dims=2)
    cost = y .* log.(s .+ ϵ) + (1 .- y) .* log.(1 .- s .+ ϵ)
    return - sum(cost) / prod(size(cost))
end

@time grads = Flux.Tracker.gradient(() -> Loss(X, Y), θ);
@time Flux.Tracker.update!(opt, θ, grads)

# @time Flux.Optimise.train!(Loss, θ, [(X, Y)], opt)


text:
const strip_patterns                = UInt32(0)
const strip_corrupt_utf8            = UInt32(0x1) << 0
const strip_case                    = UInt32(0x1) << 1
const stem_words                    = UInt32(0x1) << 2
const tag_part_of_speech            = UInt32(0x1) << 3

const strip_whitespace              = UInt32(0x1) << 5
const strip_punctuation             = UInt32(0x1) << 6
const strip_numbers                 = UInt32(0x1) << 7
const strip_non_letters             = UInt32(0x1) << 8

const strip_indefinite_articles     = UInt32(0x1) << 9
const strip_definite_articles       = UInt32(0x1) << 10
const strip_articles                = (strip_indefinite_articles |
                                       strip_definite_articles)

const strip_prepositions            = UInt32(0x1) << 13
const strip_pronouns                = UInt32(0x1) << 14

const strip_stopwords               = UInt32(0x1) << 16
const strip_sparse_terms            = UInt32(0x1) << 17
const strip_frequent_terms          = UInt32(0x1) << 18

const strip_html_tags               = UInt32(0x1) << 20

const alpha_sparse      = 0.05
const alpha_frequent    = 0.95

const regex_cache = Dict{AbstractString, Regex}()
function mk_regex(regex_string)
    d = haskey(regex_cache, regex_string) ?
            regex_cache[regex_string] :
            (regex_cache[regex_string] = Regex(regex_string))
    (length(regex_cache) > 50) && empty!(regex_cache)
    d
end

##############################################################################
#
# Remove corrupt UTF8 characters
#
##############################################################################
function remove_corrupt_utf8(s::AbstractString)
    return map(x->isvalid(x) ? x : ' ', s)
end

remove_corrupt_utf8!(d::FileDocument) = error("FileDocument cannot be modified")

function remove_corrupt_utf8!(d::StringDocument)
    d.text = remove_corrupt_utf8(d.text)
    nothing
end

function remove_corrupt_utf8!(d::TokenDocument)
    for i in 1:length(d.tokens)
        d.tokens[i] = remove_corrupt_utf8(d.tokens[i])
    end
end

function remove_corrupt_utf8!(d::NGramDocument)
    new_ngrams = Dict{AbstractString, Int}()
    for token in keys(d.ngrams)
        new_token = remove_corrupt_utf8(token)
        if haskey(new_ngrams, new_token)
            new_ngrams[new_token] = new_ngrams[new_token] + 1
        else
            new_ngrams[new_token] = 1
        end
    end
    d.ngrams = new_ngrams
end

function remove_corrupt_utf8!(crps::Corpus)
    for doc in crps
        remove_corrupt_utf8!(doc)
    end
end

##############################################################################
#
# Conversion to lowercase
#
##############################################################################

remove_case(s::T) where {T <: AbstractString} = lowercase(s)

remove_case!(d::FileDocument) = error("FileDocument cannot be modified")

function remove_case!(d::StringDocument)
    d.text = remove_case(d.text)
    nothing
end

function remove_case!(d::TokenDocument)
    for i in 1:length(d.tokens)
        d.tokens[i] = remove_case(d.tokens[i])
    end
end

function remove_case!(d::NGramDocument)
    new_ngrams = Dict{AbstractString, Int}()
    for token in keys(d.ngrams)
        new_token = remove_case(token)
        if haskey(new_ngrams, new_token)
            new_ngrams[new_token] = new_ngrams[new_token] + 1
        else
            new_ngrams[new_token] = 1
        end
    end
    d.ngrams = new_ngrams
end

function remove_case!(crps::Corpus)
    for doc in crps
        remove_case!(doc)
    end
end

##############################################################################
#
# Stripping HTML tags
#
##############################################################################
const script_tags = Regex("<script\\b[^>]*>([\\s\\S]*?)</script>")
const style_tags = Regex("<style\\b[^>]*>([\\s\\S]*?)</style>")
const html_tags = Regex("<[^>]*>")

function remove_html_tags(s::AbstractString)
    s = remove_patterns(s, script_tags)
    s = remove_patterns(s, style_tags)
    remove_patterns(s, html_tags)
end

function remove_html_tags!(d::AbstractDocument)
    error("HTML tags can be removed only from a StringDocument")
end

function remove_html_tags!(d::StringDocument)
    d.text = remove_html_tags(d.text)
    nothing
end

function remove_html_tags!(crps::Corpus)
    for doc in crps
        remove_html_tags!(doc)
    end
end

##############################################################################
#
# Remove specified words
#
##############################################################################
function remove_words!(entity::(Union{AbstractDocument,Corpus}),
               words::Vector{T}) where T <: AbstractString
    skipwords = Set{AbstractString}()
    union!(skipwords, words)
    prepare!(entity, strip_patterns, skip_words = skipwords)
end

##############################################################################
#
# Part-of-Speech tagging
#
##############################################################################

tag_pos!(entity) = error("Not yet implemented")

##############################################################################
#
# Drop terms based on frequency
#
##############################################################################

function sparse_terms(crps::Corpus, alpha::Real = alpha_sparse)
    update_lexicon!(crps)
    update_inverse_index!(crps)
    res = Array{String}(undef, 0)
    ndocs = length(crps.documents)
    for term in keys(crps.lexicon)
        f = length(crps.inverse_index[term]) / ndocs
        if f <= alpha
            push!(res, String(term))
        end
    end
    return res
end

function frequent_terms(crps::Corpus, alpha::Real = alpha_frequent)
    update_lexicon!(crps)
    update_inverse_index!(crps)
    res = Array{String}(undef, 0)
    ndocs = length(crps.documents)
    for term in keys(crps.lexicon)
        f = length(crps.inverse_index[term]) / ndocs
        if f >= alpha
            push!(res, String(term))
        end
    end
    return res
end

# Sparse terms occur in less than x percent of all documents
remove_sparse_terms!(crps::Corpus, alpha::Real = alpha_sparse) = remove_words!(crps, sparse_terms(crps, alpha))

# Frequent terms occur in more than x percent of all documents
remove_frequent_terms!(crps::Corpus, alpha::Real = alpha_frequent) = remove_words!(crps, frequent_terms(crps, alpha))

##############################################################################
#
# Remove parts from document based on flags or regular expressions
#
##############################################################################

function prepare!(crps::Corpus, flags::UInt32; skip_patterns = Set{AbstractString}(), skip_words = Set{AbstractString}())
    ((flags & strip_sparse_terms) > 0) && union!(skip_words, sparse_terms(crps))
    ((flags & strip_frequent_terms) > 0) && union!(skip_words, frequent_terms(crps))

    ((flags & strip_corrupt_utf8) > 0) && remove_corrupt_utf8!(crps)
    ((flags & strip_case) > 0) && remove_case!(crps)
    ((flags & strip_html_tags) > 0) && remove_html_tags!(crps)

    lang = language(crps.documents[1])   # assuming all documents are of the same language - practically true
    r = _build_regex(lang, flags, skip_patterns, skip_words)
    !isempty(r.pattern) && remove_patterns!(crps, r)

    ((flags & stem_words) > 0) && stem!(crps)
    ((flags & tag_part_of_speech) > 0) && tag_pos!(crps)
    nothing
end

function prepare!(d::AbstractDocument, flags::UInt32; skip_patterns = Set{AbstractString}(), skip_words = Set{AbstractString}())
    ((flags & strip_corrupt_utf8) > 0) && remove_corrupt_utf8!(d)
    ((flags & strip_case) > 0) && remove_case!(d)
    ((flags & strip_html_tags) > 0) && remove_html_tags!(d)

    r = _build_regex(language(d), flags, skip_patterns, skip_words)
    !isempty(r.pattern) && remove_patterns!(d, r)

    ((flags & stem_words) > 0) && stem!(d)
    ((flags & tag_part_of_speech) > 0) && tag_pos!(d)
    nothing
end

function remove_patterns(s::AbstractString, rex::Regex)
    iob = IOBuffer()
    ibegin = 1
    v=codeunits(s)
    for m in eachmatch(rex, s)
        len = m.match.offset-ibegin+1
        if len > 0
            Base.write_sub(iob, v, ibegin, len)
            write(iob, ' ')
        end
        ibegin = nextind(s, lastindex(m.match)+m.match.offset)
    end
    len = length(v) - ibegin + 1
    (len > 0) && Base.write_sub(iob, v, ibegin, len)
    String(take!(iob))
end

function remove_patterns(s::SubString{T}, rex::Regex) where T <: String
    iob = IOBuffer()
    ioffset = s.offset
    data = codeunits(s.string)
    ibegin = 1
    for m in eachmatch(rex, s)
        len = m.match.offset-ibegin+1
        if len > 0
            Base.write_sub(iob, data, ibegin+ioffset, len)
            write(iob, ' ')
        end
        ibegin = nextind(s, lastindex(m.match)+m.match.offset)
    end
    len = lastindex(s) - ibegin + 1
    (len > 0) && Base.write_sub(iob, data, ibegin+ioffset, len)
    String(take!(iob))
end

remove_patterns!(d::FileDocument, rex::Regex) = error("FileDocument cannot be modified")

function remove_patterns!(d::StringDocument, rex::Regex)
    d.text = remove_patterns(d.text, rex)
    nothing
end

function remove_patterns!(d::TokenDocument, rex::Regex)
    for i in 1:length(d.tokens)
        d.tokens[i] = remove_patterns(d.tokens[i], rex)
    end
end

function remove_patterns!(d::NGramDocument, rex::Regex)
    new_ngrams = Dict{AbstractString, Int}()
    for token in keys(d.ngrams)
        new_token = remove_patterns(token, rex)
        if haskey(new_ngrams, new_token)
            new_ngrams[new_token] = new_ngrams[new_token] + 1
        else
            new_ngrams[new_token] = 1
        end
    end
    d.ngrams = new_ngrams
    nothing
end

function remove_patterns!(crps::Corpus, rex::Regex)
    for doc in crps
        remove_patterns!(doc, rex)
    end
end

##
# internal helper methods

_build_regex(lang, flags::UInt32) = _build_regex(lang, flags, Set{AbstractString}(), Set{AbstractString}())
_build_regex(lang, flags::UInt32, patterns::Set{T}, words::Set{T}) where {T <: AbstractString} = _combine_regex(_build_regex_patterns(lang, flags, patterns, words))

function _combine_regex(regex_parts::Set{T}) where T <: AbstractString
    l = length(regex_parts)
    (0 == l) && return r""
    (1 == l) && return mk_regex(pop!(regex_parts))

    iob = IOBuffer()
    write(iob, "($(pop!(regex_parts)))")
    for part in regex_parts
        write(iob, "|($part)")
    end
    mk_regex(String(take!(iob)))
end

function _build_regex_patterns(lang, flags::UInt32, patterns::Set{T}, words::Set{T}) where T <: AbstractString
    ((flags & strip_whitespace) > 0) && push!(patterns, "\\s+")
    if (flags & strip_non_letters) > 0
        push!(patterns, "[^a-zA-Z\\s]")
    else
        ((flags & strip_punctuation) > 0) && push!(patterns, "[-.,:;,!?'\"\\[\\]\\(\\)\\{\\}|\\`#\$%@^&*_+<>]+")
        ((flags & strip_numbers) > 0) && push!(patterns, "\\d+")
    end
    if (flags & strip_articles) > 0
        union!(words, articles(lang))
    else
        ((flags & strip_indefinite_articles) > 0) && union!(words, indefinite_articles(lang))
        ((flags & strip_definite_articles) > 0) && union!(words, definite_articles(lang))
    end
    ((flags & strip_prepositions) > 0) && union!(words, prepositions(lang))
    ((flags & strip_pronouns) > 0) && union!(words, pronouns(lang))
    ((flags & strip_stopwords) > 0) && union!(words, stopwords(lang))

    words_pattern = _build_words_pattern(collect(words))
    !isempty(words_pattern) && push!(patterns, words_pattern)
    patterns
end

function _build_words_pattern(words::Vector{T}) where T <: AbstractString
    isempty(words) && return ""

    iob = IOBuffer()
    write(iob, "\\b(")
    write(iob, words[1])
    l = length(words)
    for idx in 2:l
        write(iob, '|')
        write(iob, words[idx])
    end
    write(iob, ")\\b")
    String(take!(iob))
end


text:
# Copyright 2020 Google LLC
#
# 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.

using Test
include("../src/AGD_distance_restart.jl")

# A quadratic of the form
#   sum_{i=1}^{n} 0.5 * i * q[i]^2
# for testing.
function simple_quadratic_value(x::Vector{Float64})
  q = (1:length(x))
  return 0.5 * sum(q[i] * x[i]^2 for i = 1:length(x))
end
function simple_quadratic_gradient(x::Vector{Float64})
  q = (1:length(x))
  return x .* q
end
testing_function = differentiable_function(
  simple_quadratic_value,
  simple_quadratic_gradient,
  0,
)

v_0 = ones(10)
grad_v_0 = testing_function.smooth_gradient(v_0)
@testset "do_gradient_step" begin
  result =
    do_gradient_step_with_backtracking_line_search(
      testing_function,
      v_0,
      grad_v_0,
      1.0,
      1e-8,
      2.0,
    )
  @test result.step_size == 0.125
end

@testset "test_agd" begin
  parameters = default_basic_agd_parameters()
  parameters.max_iter=1000
  parameters.verbose = false
  v_0 = [30.0, 10.0, 4.0, 24.0, 4.0, 5.0, 1.0, 2.0, 10.0, 30.0, 10.0, 4.0, 24.0,
    4.0, 5.0, 1.0, 2.0, 10.0, 1.0, 2.0, 10.0, 30.0, 10.0, 4.0, 24.0,
    4.0, 5.0, 1.0, 2.0, 10.0]
  basic_final_point, basic_history = basic_AGD(testing_function,
    v_0,
    parameters)
  @test norm(basic_final_point, 2) < 1e-3

  final_point_fixed_frequency, fixed_frequency_history =
    fixed_frequency_AGD(testing_function, v_0,
                                              parameters, 10)
  @test norm(final_point_fixed_frequency, 2) < 1e-8

  function_value_final_point, fval_restart_history =
    function_value_restart_AGD(testing_function, v_0, parameters
     )
  @test norm(function_value_final_point, 2) < 1e-8

  final_point_adaptive, adaptive_history =
    our_adaptive_restart_AGD(testing_function, v_0, parameters)
  @test norm(final_point_adaptive, 2) < 1e-8
end

parameters = default_basic_agd_parameters()
parameters.max_iter=2000
parameters.verbose = false
n = 5000
v_0 = ones(n)

basic_history, fval_restart_history, adaptive_history = run_algs(
  testing_function, v_0, parameters);
plot_algs(basic_history, fval_restart_history,
  adaptive_history)
savefig(joinpath(@__DIR__, "../plots/test.pdf"))


text:
"""Julia program to check if a number is a Neon Number or not
A neon number is a number where the sum of digits of square of the number is equal to the number.
"""

function check_neon_num(n)
    sum = 0
    temp = n ^ 2
    while(temp != 0)
        sum = sum + (temp % 10)
        temp = temp  ÷ 10
    end
    if(sum == n)
        return true
    else
        return false
    end
end

print("Enter the number: ")
n = readline()
n = parse(Int, n)
res = check_neon_num(n)
if res
    println("The given number $n is a Neon Number.")
else
    println("The given number $n is not a Neon Number.")
end

"""
Time Complexity: O(log(num)), where 'num' is the given number
Space Complexity: O(1)

SAMPLE INPUT AND OUTPUT

SAMPLE 1
Enter the number: 0
The given number 0 is a Neon Number..

SAMPLE 2
Enter the number: 14
The given number 14 is not a Neon Number.

"""


text:
module RomanNumerals

export
    RomanNumeral,
    @rn_str,
    I, V, X, L, C, D, M

import Base:
    +, -, *, ÷,
    Int8, Int16, Int32, Int64, Int128,
    BigInt,
    UInt8, UInt16, UInt32, UInt64, UInt128

using Random

const MAXIMUM_ROMAN_NUMERAL = 5000

"""
    RomanNumeral(num)

A type that represents a Roman numeral.

`RomanNumeral` objects can be created using `Integer` or `AbstractString` objects, or using
the `@rn_str` string macro.

# Examples

```jldoctest
julia> using RomanNumerals

julia> foreach(i->println(RomanNumeral(i)), 1:5)
I
II
III
IV
V

julia> rand(RomanNumeral, 5)
5-element Array{RomanNumeral,1}:
 LXXI
 CXX
 XCVIII
 XLIX
 VIII

```

# String literal

```jldoctest
julia> rn"MMXIX"
MMXIX

```

# Arithmetic

```jldoctest
julia> I + I
II

julia> V - I
IV

julia> 3X - 2I
XXVIII

julia> M ÷ X
C

```

# Conversion

```jldoctest
julia> RomanNumeral(2019)
MMXIX

julia> Int16(rn"MMXIX")
2019

julia> string(rn"MMXIX")
"MMXIX"

julia> rn"I" == RomanNumeral("I") == RomanNumeral('I') == RomanNumeral(1)
true
```
"""
struct RomanNumeral{T<:Integer}
    num::T

    function RomanNumeral{T}(num::T) where T<:Integer
        num ≤ 0 && throw(DomainError(num, "Roman numerals must be positive"))
        new{T}(num)
    end
end

RomanNumeral(num::T) where T<:Integer = RomanNumeral{T}(num)
RomanNumeral(rn::AbstractString) = parse(RomanNumeral, rn)
RomanNumeral(rn::AbstractChar) = parse(RomanNumeral, string(rn))

# I, V, X, L, C, D and M consts

for (rn, v) in (
    (:I,    1),
    (:V,    5),
    (:X,   10),
    (:L,   50),
    (:C,  100),
    (:D,  500),
    (:M, 1000),
)
    @eval begin
        """
            $($rn)

        The Roman numeral for $($v).
        """
        const $rn = RomanNumeral($v)
    end
end

const ROMAN_NUMERAL_CHARS = ('I', 'V', 'X', 'L', 'C', 'D', 'M')

# Arithmetic

for op in (
    :+,
    :-,
    :*,
    :÷,
)
    @eval begin
        ($op)(x::RomanNumeral, y::RomanNumeral) = RomanNumeral(($op)(x.num, y.num))
        ($op)(x::RomanNumeral, y::Integer) = RomanNumeral(($op)(x.num, y))
        ($op)(x::Integer, y::RomanNumeral) = RomanNumeral(($op)(x, y.num))
    end
end

# Parsing

const INT_NUMERAL = Dict(
       1 => "I",
       4 => "IV",
       5 => "V",
       9 => "IX",
      10 => "X",
      40 => "XL",
      50 => "L",
      90 => "XC",
     100 => "C",
     400 => "CD",
     500 => "D",
     900 => "CM",
    1000 => "M",
)

const NUMERAL_INT = Dict(
    zip(
        values(INT_NUMERAL),
        keys(INT_NUMERAL),
    )
)

const ROMAN_NUMERAL_REGEX =
    r"(M*(CM|CD|DC{0,4}|C{0,9})(XC|XL|LX{0,4}|X{0,9})(IX|IV|VI{0,4}|I{0,9}))"x

function is_valid_roman_numeral(rn::AbstractString)
    m = match(ROMAN_NUMERAL_REGEX, rn)
    isnothing(m) && throw(InvalidRomanNumeral())
    length(m.match) != length(rn) && throw(InvalidRomanNumeral())
    return true
end

function Base.parse(::Type{String}, rn::RomanNumeral)
    x = rn.num
    s = ""
    for y in (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
        s *= repeat(INT_NUMERAL[y], x ÷ y)
        x %= y
    end
    return s
end

function Base.parse(::Type{RomanNumeral}, rn::String)
    is_valid_roman_numeral(rn)
    f = i -> NUMERAL_INT[string(rn[i])]
    x = 0
    curr = f(1)
    for i = 1:length(rn) - 1
        next = f(i + 1)
        if curr < next
            x -= curr
        else
            x += curr
        end
        curr = next
    end
    x += curr
    return RomanNumeral(x)
end

Base.convert(::Type{String}, rn::RomanNumeral) = parse(String, rn)
Base.convert(::Type{RomanNumeral}, rn::String) = parse(RomanNumeral, rn)
Base.convert(::Type{T}, rn::RomanNumeral) where T<:Integer = convert(T, rn.num)

for T in (
    :Int8, :Int16, :Int32, :Int64, :Int128,
    :BigInt,
    :UInt8, :UInt16, :UInt32, :UInt64, :UInt128,
)
    @eval $T(rn::RomanNumeral) = convert($T, rn)
end

Base.show(io::IO, rn::RomanNumeral) = print(io, convert(String, rn))

# Random numerals

function Random.rand(rng::AbstractRNG, ::Random.SamplerType{RomanNumeral{T}}) where T<:Integer
    max_num = min(typemax(T), MAXIMUM_ROMAN_NUMERAL)
    num = rand(rng, 1:max_num)
    return RomanNumeral(T(num))
end

function Random.rand(rng::AbstractRNG, ::Random.SamplerType{RomanNumeral})
    num = rand(rng, 1:MAXIMUM_ROMAN_NUMERAL)
    return RomanNumeral(num)
end

# String literal

"""
    rn"IVXLCDM"

RomanNumeral string literal.
"""
macro rn_str(rn)
    RomanNumeral(rn)
end

# Exceptions

struct InvalidRomanNumeral <: Exception end

Base.showerror(io::IO, e::InvalidRomanNumeral) = print(io, "InvalidRomanNumeral:")

end # module


text:
function lsdev(; show::Bool=true)
    buf = DAQStringBuffer(DAQmx.GetSysDevNames(Vector{UInt8}(), UInt32(0)))
    DAQmx.GetSysDevNames(buf.str, buf.size) |> catch_error
    out = convert(Vector{String}, buf)
    show && (println.(out); return)
    return out
end

DefaultDev() = DAQDevice(lsdev(show=false)[1])

function lschan(iotype::Union{Type{<:AbstractIO},Nothing} = nothing,
                   dev::DAQDevice = DefaultDev();
                  show::Bool = true)

    types = collect(keys(getchanfun))
    names = string.(types)
    numtypes = length(names)

    if iotype == nothing
        chans = Vector{Vector{String}}(undef, numtypes)
        for (i, key) in enumerate(types)
            buf = DAQStringBuffer(getchanfun[key](dev.name, Vector{UInt8}(), UInt32(0)))
            getchanfun[key](dev.name, buf.str, buf.size) |> catch_error
            chans[i] = convert(Vector{String}, buf)
        end
        show || return chans
        chandata = fill("", maximum(length.(chans)), numtypes)
        for (j, chan) in enumerate(chans)
            chandata[1:length(chan), j] = chan
        end
        return pretty_table(chandata, names)
    else
        buf = DAQStringBuffer(getchanfun[iotype](dev.name, Vector{UInt8}(), UInt32(0)))
        getchanfun[iotype](dev.name, buf.str, buf.size) |> catch_error
        chans = convert(Vector{String}, buf)
        show || return chans
        header = names[iotype .== types]
        return pretty_table(chans, header)
    end
end

function ranges(iotype::Type{T},
                dev::DAQDevice = DefaultDev()) where {T<:Union{AnalogIn,AnalogOut}}

    getfun = LittleDict(AI => DAQmx.GetDevAIVoltageRngs,
                        AO => DAQmx.GetDevAOVoltageRngs)

    sz = getfun[iotype](dev.name, Vector{Float64}(), UInt32(0))
    result = Vector{Float64}(undef, sz)
    getfun[iotype](dev.name, result, UInt32(sz)) |> catch_error
    pairs = Vector{Tuple{Float64,Float64}}(undef, sz>>1)

    for i in 1:length(pairs)
        pairs[i] = (popfirst!(result),popfirst!(result))
    end

    return pairs
end

function lschan(dev::DAQDevice; asobjects = true)
    allchans = lschan(nothing, dev; show = false)
    chanobjs = Vector{Vector{PhysicalChannel{<:AbstractIO}}}(undef, length(keys(getchanfun)))
    alltypes = collect(keys(getchanfun))
    for (i, chanvec) in enumerate(allchans)
        t  = alltypes[i]
        rng = t <: Union{AI,AO} ? ranges(t,dev) : nothing
        chanobjs[i] = [PhysicalChannel{t}(x,dev,rng) for x in chanvec]
    end
    return chanobjs
end

# Sort the property functions first

function Base.startswith(sym::Symbol, x)
    return startswith(String(sym), x)
end

function parse_symbols()

    get_prefixes = ["AI","AO","DI","DO","CI","CO","Sys","Dev","Task"]
    set_prefixes = get_prefixes[1:end-3]

    all_symbols = names(DAQmx, all=true)

    get_functions = all_symbols[startswith.(all_symbols,"Get")]
    set_functions = all_symbols[startswith.(all_symbols,"Set")]

    get_function_map = Dict(x => Vector{Function}() for x in get_prefixes)
    set_function_map = Dict(x => Vector{Function}() for x in set_prefixes)

    for getkey in get_prefixes
        get_subset = getfield.([DAQmx], get_functions[startswith.(get_functions,"Get$getkey")])
        get_function_map[getkey] = get_subset
    end

    for setkey in set_prefixes
        set_subset = getfield.([DAQmx], set_functions[startswith.(set_functions,"Set$setkey")])
        set_function_map[setkey] = set_subset
    end

    return (get_function_map, set_function_map)
end

const (get_function_map, set_function_map) = parse_symbols()

"""
`channel_type(task,channel) -> channel_type, measurement/output_type`
    GetChanType(channel.parent, channel.name, val1)
"""
function info(channel::TaskChannel{T}) where T <: AbstractIO

    val = Int32(0)

    if T == AI
        GetAIMeasType(channel.parent, channel.alias, val) |> catch_error
    elseif T == AO
        GetAOOutputType(channel.parent, channel.alias, val) |> catch_error
    elseif T <: Union{DI,DO}
        return (T, nothing)
    elseif T == CI
        GetCIMeasType(channel.parent, channel.alias, val) |> catch_error
    elseif T == CO
        GetCOOutputType(channel.parent, channel.alias, val) |> catch_error
    end

    return (T, string(DAQmx.DAQmxConstant(val)))
end

function argtypes(fun::Function)
    a = methods(fun).ms[1] # DAQmx wrappers have only one method
    return fieldtypes(a.sig)[2:end] # ignore first field (sym)
end

# Get function for dev, sys, task, channel

# channel properties
function getproperties(channel::TaskChannel{T}; warning=false) where T <: AbstractIO

    for f in get_function_map[string(T)]
        fun_argtypes = argtypes(f)
    end

    return
end

function rate(task::DAQTask)
    out = Ref{Float64}()
    DAQmx.GetSampClkRate(task.handle, out) |> catch_error
    return out[]
end

function rate!(task::DAQTask, newrate::Float64)
    DAQmx.SetSampClkRate(task.handle, newrate) |> catch_error
    return
end

function maxrate(task::DAQTask)
    out = Ref{Float64}()
    DAQmx.GetSampClkMaxRate(task.handle, out) |> catch_error
    return out[]
end

# system properties
function getproperties(; warning=false, show=true)

    result = Dict()
    ver = version()
    push!(result, "NIDAQmx Version" => ver)
    prefix = length("GetSys") + 1

    for f in get_function_map["Sys"]
        fname = string(f)[prefix:end]
        occursin("Attribute", fname) && continue
        occursin("Version", fname) && continue

        buf = DAQStringBuffer(f(Vector{UInt8}(),UInt32(0)))
        f(buf.str, buf.size) |> catch_error
        out = convert(Vector{String}, buf)
        out == nothing && (out = "--")
        push!(result, fname => out)
    end

    show && return pretty_table(result)
    return result
end

# device properties
function getproperties(dev::DAQDevice; warning=false)
    result = Dict()
    prefix = length("GetDev") + 1
    for f in get_function_map["Dev"]
        fname = string(f)[prefix:end]
        occursin("Attribute", fname) && continue
        # WIP -- argtypes() -> parse by whether
        # argument types include Vector/Ref

    end

    return
end

# task properties -- will follow example of dev prop
function getproperties(task::DAQTask; warning=false)

    for f in get_function_map["Task"]
    end

    return
end

# set properties (maybe split this up)
function setproperty!(t::DAQTask, channel::Type{T}, property::String, value) where {T <: DAQChannel}
    for f in set_function_map[]
    end

    return
end