text:
macro test_da_pda(da, code)
    esc(quote
        let $da = copy($da)
            $code
        end
        let $da = PooledDataArray($da)
            $code
        end
    end)
end

@testset "Operators" begin
    const bit_operators = [&, |, ⊻]

    const arithmetic_operators = [+, -, *, /, Base.div, Base.mod, Base.fld, Base.rem]

    const comparison_operators = [==, !=, >, >=, <, <=]

    const elementary_functions = [abs,
                                  abs2,
                                  sign,
                                  acos,
                                  acosh,
                                  asin,
                                  asinh,
                                  atan,
                                  atanh,
                                  sin,
                                  sinh,
                                  conj,
                                  cos,
                                  cosh,
                                  tan,
                                  tanh,
                                  ceil,
                                  floor,
                                  round,
                                  trunc,
                                  exp,
                                  exp2,
                                  expm1,
                                  log,
                                  log10,
                                  log1p,
                                  log2,
                                  exponent,
                                  sqrt,
                                  gamma,
                                  lgamma]

    # All unary operators return missing when evaluating missing
    for f in [+, -]
        @test ismissing(f(missing))
    end

    # All elementary functions return missing when evaluating missing
    for f in elementary_functions
        @test ismissing(f(missing))
    end

    # All comparison operators return missing when comparing missing with missing
    # All comparison operators return missing when comparing scalars with missing
    # All comparison operators return missing when comparing missing with scalars
    for f in comparison_operators
        @test ismissing(f(missing, missing))
        @test ismissing(f(missing, 1))
        @test ismissing(f(1, missing))
    end

    # All arithmetic operators return missing when operating on two missings
    # All arithmetic operators return missing when operating on a scalar and an missing
    # All arithmetic operators return missing when operating on an missing and a scalar
    for f in arithmetic_operators
        @test ismissing(f(missing, missing))
        @test ismissing(f(1, missing))
        @test ismissing(f(missing, 1))
    end

    # All bit operators return missing when operating on two missings
    # All bit operators return missing when operating on a scalar and an missing
    # All bit operators return missing when operating on an missing and a scalar
    for f in bit_operators
        @test ismissing(f(missing, missing))
        @test ismissing(f(1, missing))
        @test ismissing(f(missing, 1))
    end

    # Unary operators on DataVector's should be equivalent to elementwise
    # application of those same operators
    dv = @data ones(5)
    @test_da_pda dv begin
        for f in [+,-]
            for i in 1:length(dv)
                @test f(dv)[i] == f(dv[i])
            end
        end
    end
    dv = convert(DataArray, trues(5))
    @test_da_pda dv begin
        for f in [!]
            for i in 1:length(dv)
                @test f(dv)[i] == f(dv[i])
            end
        end
    end

    # transpose and ctranpose on DataVectors and DataMatrices
    for (da, dat) in ((@data([5, 2, 3, 4]), @data([5 2 3 4])),
                      (@data([1.0+1.0im, 5.0, 3.0+3.0im, 4.0-4.0im]), @data([1.0+1.0im 5.0 3.0+3.0im 4.0-4.0im])),
                      (@data([1 2; 5 4]), @data([1 5; 2 4])),
                      (@data([1.0+1.0im 2.0-2.0im; 3.0+3.0im 5.0]), @data([1.0+1.0im 3.0+3.0im; 2.0-2.0im 5.0])))
        # Test for both bits and non-bits types
        for da in (da, convert(DataArray{Number}, da))
            let da = copy(da), dat = copy(dat)
                # No missing
                @test isequal(transpose(da), dat)
                @test isequal(da', conj(dat))

                # With missing
                # XXX we should fix indexing so that this isn't necessary
                for i = 1:length(da)
                    da[i] == 5 && (da[i] = missing)
                    dat[i] == 5 && (dat[i] = missing)
                end

                # Make sure that missings are undefined in the non-bits array
                da = conj(conj(da))
                @test isequal(transpose(da), dat)
                @test isequal(da', conj(dat))
            end
        end
    end

    # Elementary functions on DataVector's
    dv = convert(DataArray, ones(5))
    @test_da_pda dv begin
        for f in elementary_functions
            for i in 1:length(dv)
                @test f.(dv)[i] == f(dv[i])
            end
        end
    end

    # Broadcasting operations between missings and DataVector's
    dv = convert(DataArray, ones(5))
    @test_da_pda dv begin
        for f in [+, *, Base.div, Base.mod, Base.fld, Base.rem]
            for i in 1:length(dv)
                @test ismissing(f(dv, missing)[i])
                @test ismissing(f(missing, dv)[i])
                @test f(dv, 1)[i] == f(dv[i], 1)
                @test f(1, dv)[i] == f(1, dv[i])
            end
        end
        for f in arithmetic_operators
            for i in 1:length(dv)
                @test ismissing(f.(dv, missing)[i])
                @test ismissing(f.(missing, dv)[i])
                @test f.(dv, 1)[i] == f(dv[i], 1)
                @test f.(1, dv)[i] == f(1, dv[i])
            end
        end
    end

    @test_da_pda dv begin
        for i in 1:length(dv)
            @test ismissing((dv / missing)[i])
            @test (dv / 1)[i] == dv[i] / 1
        end
    end

    dv = @data([false, true, false, true, false])
    for f in bit_operators
        for i in 1:length(dv)
            @test f.(dv, true)[i] == f.(dv[i], true)
            @test f.(true, dv)[i] == f.(true, dv[i])
        end
    end

    # Binary operations on (DataVector, Vector) or (Vector, DataVector)
    v = ones(5)
    dv = convert(DataArray, ones(5))
    dv[1] = missing
    bv = [true, false, false, true, true]
    bbv = BitArray([true, false, false, true, true])
    bdv = @data [false, true, false, false, true]
    @test_da_pda dv begin
        for f in [+, -, *, ^]
            for i in 1:length(dv)
                @test ismissing(f.(v, dv)[i]) && ismissing(dv[i]) || f.(v, dv)[i] == f(v[i], dv[i])
                @test ismissing(f.(dv, v)[i]) && ismissing(dv[i]) || f.(dv, v)[i] == f(dv[i], v[i])
            end
        end
        for f in bit_operators
            for i in 1:length(bdv)
                @test f.(bv, bdv)[i]  == f.(bv[i], bdv[i])
                @test f.(bdv, bv)[i]  == f.(bdv[i], bv[i])
                @test f.(bbv, bdv)[i] == f.(bbv[i], bdv[i])
                @test f.(bdv, bbv)[i] == f.(bdv[i], bbv[i])
            end
        end
    end

    # Binary operations on pairs of DataVector's
    dv = convert(DataArray, ones(5))
    # Dates are an example of type for which - and .- return a different type from its inputs
    dvd = @data([Date("2000-01-01"), Date("2010-01-01"), Date("2010-01-05")])
    dv[1] = dvd[1] = missing
    @test_da_pda dv begin
        for f in [+, -, *, ^]
            for i in 1:length(dv)
                @test ismissing(f.(dv, dv)[i]) && ismissing(dv[i]) || f.(dv, dv)[i] == f(dv[i], dv[i])
            end
        end
        for f in [+,-]
            for i in 1:length(dv)
                @test ismissing((f)(dv, dv)[i]) && ismissing(dv[i]) || (f)(dv, dv)[i] == (f)(dv[i], dv[i])
            end
        end
        for f in bit_operators
            for i in 1:length(bv)
                @test f.(bv, bv)[i] == f.(bv[i], bv[i])
            end
        end
        for i in 1:length(dvd)
            @test ismissing((dvd - dvd)[i]) && ismissing(dvd[i]) || (dvd - dvd)[i] == dvd[i] - dvd[i]
            @test ismissing((dvd .- dvd)[i]) && ismissing(dvd[i]) || (dvd .- dvd)[i] == dvd[i] - dvd[i]
        end
    end

    # + and - with UniformScaling
    mI = zeros(Int, 5, 5) + 5I
    for dm in (convert(DataArray, ones(5, 5)), convert(DataArray, trues(5, 5)))
        dm[1] = missing
        @test_da_pda dm begin
            @test isequal(dm + 5I, dm + mI)
            @test isequal(5I + dm, mI + dm)
            @test isequal(dm - 5I, dm - mI)
            @test isequal(5I - dm, mI - dm)
        end
    end

    # Division (special case since return type for Int is a Float64)
    for curdv in (dv,
                  convert(DataVector{Int}, dv),
                  convert(DataVector{Float32}, dv))
        for i in 1:length(curdv)
            @test ismissing((curdv./curdv)[i]) && ismissing(curdv[i]) ||
                    isequal((curdv./curdv)[i], (curdv[i]./curdv[i]))
            @test ismissing((curdv./2)[i]) && ismissing(curdv[i]) ||
                    isequal((curdv./2)[i], (curdv[i]./2))
            @test ismissing((curdv/2)[i]) && ismissing(curdv[i]) ||
                    isequal((curdv/2)[i], (curdv[i]/2))
        end
    end

    # Unary vector operators on DataVector's
    dv = convert(DataArray, ones(5))
    for f in map(eval, DataArrays.unary_vector_operators)
        @test isequal(f(dv), f(dv.data))
    end
    dv[1] = missing
    for f in map(eval, DataArrays.unary_vector_operators)
        @test ismissing(f(dv))
    end

    # Pairwise vector operators on DataVector's
    const pairwise_vector_operators = [diff]

    dv = @data([911, 269, 835.0, 448, 772])
    # Dates are an example of type for which operations return a different type from their inputs
    dvd = @data([Date("2000-01-01"), Date("2010-01-01"), Date("2010-01-05")])
    for f in pairwise_vector_operators
        @test isequal(f(dv), f(dv.data))
        @test isequal(f(dvd), f(dvd.data))
    end
    dv = @data([missing, 269, 835.0, 448, 772])
    dvd = @data([missing, Date("2000-01-01"), Date("2010-01-01"), Date("2010-01-05")])
    for f in pairwise_vector_operators
        v = f(dv)
        @test ismissing(v[1])
        @test isequal(v[2:4], f(dv.data)[2:4])

        d = f(dvd)
        @test ismissing(d[1])
        @test isequal(d[2:3], f(dvd.data)[2:3])
    end
    dv = @data([911, missing, 835.0, 448, 772])
    dvd = @data([Date("2000-01-01"), missing, Date("2010-01-01"), Date("2010-01-05")])
    for f in pairwise_vector_operators
        v = f(dv)
        @test ismissing(v[1])
        @test ismissing(v[2])
        @test isequal(v[3:4], f(dv.data)[3:4])

        d = f(dvd)
        @test ismissing(d[1])
        @test ismissing(d[2])
        @test isequal(d[3:3], f(dvd.data)[3:3])
    end
    dv = @data([911, 269, 835.0, 448, missing])
    dvd = @data([Date("2000-01-01"), Date("2010-01-01"), Date("2010-01-05"), missing])
    for f in pairwise_vector_operators
        v = f(dv)
        @test ismissing(v[4])
        @test isequal(v[1:3], f(dv.data)[1:3])

        d = f(dvd)
        @test ismissing(d[3])
        @test isequal(d[1:2], f(dvd.data)[1:2])
    end

    # Cumulative vector operators on DataVector's
    dv = convert(DataArray, ones(5))
    for f in [Base.cumprod, Base.cumsum, t -> accumulate(min, t), t -> accumulate(max, t)]
        for i in 1:length(dv)
            @test f(dv)[i] == f(dv.data)[i]
        end
    end
    dv[4] = missing
    for f in [Base.cumprod, Base.cumsum]
        for i in 1:3
            @test f(dv)[i] == f(dv.data)[i]
        end
        for i in 4:5
            @test ismissing(f(dv)[i])
        end
    end

    # Binary vector operators on DataVector's
    dv = convert(DataArray, ones(5))
    for f in map(eval, DataArrays.binary_vector_operators)
        @test f(dv, dv) == f(dv.data, dv.data) ||
                (isnan(f(dv, dv)) && isnan(f(dv.data, dv.data)))
    end
    dv[1] = missing
    for f in map(eval, DataArrays.binary_vector_operators)
        @test ismissing(f(dv, dv))
    end

    # Boolean operators on DataVector's
    @test any(convert(DataArray, falses(5))) == false
    @test any(convert(DataArray, trues(5))) == true
    @test all(convert(DataArray, falses(5))) == false
    @test all(convert(DataArray, trues(5))) == true
    @test any(PooledDataArray(convert(DataArray, falses(5)))) == false
    @test any(PooledDataArray(convert(DataArray, trues(5)))) == true
    @test all(PooledDataArray(convert(DataArray, falses(5)))) == false
    @test all(PooledDataArray(convert(DataArray, trues(5)))) == true

    dv = convert(DataArray, falses(5))
    dv[3] = true
    @test_da_pda dv begin
        @test any(dv) == true
        @test all(dv) == false
    end

    dv = convert(DataArray, falses(5))
    dv[1] = missing
    @test_da_pda dv begin
        @test ismissing(any(dv))
        @test all(dv) == false
    end

    dv = convert(DataArray, falses(5))
    dv[2] = missing
    dv[3] = true
    @test_da_pda dv begin
        @test any(dv) == true
        @test all(dv) == false
    end

    dv = convert(DataArray, falses(5))
    dv[2] = missing
    @test_da_pda dv begin
        @test ismissing(any(dv))
        @test all(dv) == false
    end

    dv = convert(DataArray, falses(1))
    dv[1] = missing
    @test_da_pda dv begin
        @test ismissing(any(dv))
        @test ismissing(all(dv))
    end

    dv = convert(DataArray, trues(5))
    dv[1] = missing
    @test_da_pda dv begin
        @test any(dv) == true
        @test ismissing(all(dv))
    end

    dv = convert(DataArray, trues(5))
    dv[2] = missing
    @test_da_pda dv begin
        @test any(dv) == true
        @test ismissing(all(dv))
    end

    #
    # Equality tests
    #

    v = [1, 2]
    dv = @data([1, missing])
    alt_dv = @data([2, missing])
    pdv = convert(PooledDataArray, @data([1, missing]))
    alt_pdv = convert(PooledDataArray, @data([2, missing]))

    @test ismissing(missing == missing)
    @test ismissing(missing != missing)

    function test_da_eq(v1::AbstractArray, v2::AbstractArray, out)
        for a in (v1, convert(DataArray, v1), convert(PooledDataArray, v1))
            for b in (v2, convert(DataArray, v2), convert(PooledDataArray, v2))
                try
                    @test isequal(a == b, out)
                    @test isequal(b == a, out)
                    if size(a) == size(b)
                        @test isequal(all(a .== b), out)
                        @test isequal(all(b .== a), out)
                    end
                catch e
                    println("a = $a")
                    println("b = $b")
                    rethrow(e)
                end
            end
        end
    end

    # Comparing two otherwise equal DataArray with missings returns missing
    test_da_eq(dv, dv, missing)
    test_da_eq(dv, v, missing)
    test_da_eq(dv, @data([missing, 1]), missing)
    # Comparing two equal arrays with no missings returns true
    test_da_eq(v, v, true)
    # Comparing two unequal arrays with no missings returns false
    test_da_eq(v, @data([1, 3]), false)
    # Comparing two otherwise unequal arrays with missings returns false
    test_da_eq(dv, alt_dv, false)
    # Comparing two arrays of unequal sizes returns false
    test_da_eq(dv, [1], false)

    @test isequal(dv, dv)
    @test isequal(pdv, pdv)

    @test !isequal(dv, alt_dv)
    @test !isequal(pdv, alt_pdv)

    @test isequal(@data([1, missing]) .== @data([1, missing]), @data([true, missing]))
    @test isequal(@pdata([1, missing]) .== @pdata([1, missing]), @data([true, missing]))

    @test all(ismissing.(missing .== convert(DataArray, ones(5))))
    @test all(ismissing, ismissing.(convert(DataArray, ones(5))) .== missing)
    @test all(ismissing.(missing .== PooledDataArray(convert(DataArray, ones(5)))))
    @test all(ismissing, ismissing.(convert(PooledDataArray, convert(DataArray, ones(5)))) .== missing)

    # Run length encoding
    dv = convert(DataArray, ones(5))
    dv[3] = missing

    v, l = DataArrays.rle(dv)
    @test isequal(v, @data([1.0, missing, 1.0]))
    @test l == [2, 1, 2]

    rdv = DataArrays.inverse_rle(v, l)
    @test isequal(dv, rdv)

    # Issue #90
    a = @data([false, true, false, true])
    b = @data([false, false, true, true])
    a[:] = missing
    b[:] = missing
    @test all(ismissing, a .& b)
    @test all(ismissing, a .| b)
end


text:
function infer_type(binding::Binding, scope, state)
    if binding isa Binding
        binding.type !== nothing && return
        if binding.val isa EXPR && (typof(binding.val) === CSTParser.ModuleH || typof(binding.val) === CSTParser.BareModule)
            binding.type = CoreTypes.Module
        elseif binding.val isa EXPR && typof(binding.val) === CSTParser.FunctionDef
            binding.type = CoreTypes.Function
        elseif binding.val isa EXPR && (typof(binding.val) === CSTParser.Struct || typof(binding.val) === CSTParser.Mutable || typof(binding.val) === CSTParser.Abstract  || typof(binding.val) === CSTParser.Primitive)
            binding.type = CoreTypes.DataType
        elseif binding.val isa EXPR && typof(binding.val) === BinaryOpCall
            if kindof(binding.val.args[2]) === CSTParser.Tokens.EQ
                if CSTParser.is_func_call(binding.val.args[1])
                    binding.type = CoreTypes.Function
                elseif CSTParser.is_func_call(binding.val.args[3])
                    callname = CSTParser.get_name(binding.val.args[3])
                    if CSTParser.isidentifier(callname)
                        resolve_ref(callname, scope, state)
                        if hasref(callname)
                            rb = get_root_method(refof(callname), state.server)
                            if (rb isa Binding && (rb.type == CoreTypes.DataType || rb.val isa SymbolServer.DataTypeStore)) || rb isa SymbolServer.DataTypeStore
                                binding.type = rb
                            end
                        end
                    end
                elseif kindof(binding.val.args[3]) === CSTParser.Tokens.INTEGER
                    binding.type = CoreTypes.Int
                elseif kindof(binding.val.args[3]) === CSTParser.Tokens.FLOAT
                    binding.type = CoreTypes.Float64
                elseif kindof(binding.val.args[3]) === CSTParser.Tokens.STRING || typof(binding.val.args[3]) === CSTParser.Tokens.TRIPLE_STRING
                    binding.type = CoreTypes.String
                elseif typof(binding.val.args[3]) === IDENTIFIER && refof(binding.val.args[3]) isa Binding
                    binding.type = refof(binding.val.args[3]).type
                end
            elseif kindof(binding.val.args[2]) === CSTParser.Tokens.DECLARATION
                t = binding.val.args[3]
                if CSTParser.isidentifier(t)
                    resolve_ref(t, scope, state)
                end
                if typof(t) === CSTParser.Curly
                    t = t.args[1]
                    resolve_ref(t, scope, state)
                end
                if typof(t) === CSTParser.BinaryOpCall && kindof(t.args[2]) === CSTParser.Tokens.DOT && t.args[3].args isa Vector && length(t.args[3].args) > 0
                    t = t.args[3].args[1]
                end

                if refof(t) isa Binding
                    rb = get_root_method(refof(t), state.server)
                    if rb isa Binding && rb.type == CoreTypes.DataType
                        binding.type = rb
                    else
                        binding.type = refof(t)
                    end
                elseif refof(t) isa SymbolServer.DataTypeStore
                    binding.type = refof(t)
                end
            end
        end
    end
end

ext: jl


text:
# Originally from IncrementalInference

abstract type InferenceType end
abstract type PackedInferenceType end

abstract type FunctorInferenceType <: Function end

abstract type InferenceVariable end
abstract type ConvolutionObject <: Function end

abstract type FunctorSingleton <: FunctorInferenceType end
abstract type FunctorPairwise <: FunctorInferenceType end
abstract type FunctorPairwiseMinimize <: FunctorInferenceType end

"""
$(TYPEDEF)
"""
mutable struct GenericFunctionNodeData{T, S}
  fncargvID::Vector{Symbol}
  eliminated::Bool
  potentialused::Bool
  edgeIDs::Array{Int,1}
  frommodule::S #Union{Symbol, AbstractString}
  fnc::T
  multihypo::String # likely to moved when GenericWrapParam is refactored
  certainhypo::Vector{Int}
  GenericFunctionNodeData{T, S}() where {T, S} = new{T,S}()
  GenericFunctionNodeData{T, S}(x1, x2, x3, x4, x5::S, x6::T, x7::String="", x8::Vector{Int}=Int[]) where {T, S} = new{T,S}(x1, x2, x3, x4, x5, x6, x7, x8)
  GenericFunctionNodeData(x1, x2, x3, x4, x5::S, x6::T, x7::String="", x8::Vector{Int}=Int[]) where {T, S} = new{T,S}(x1, x2, x3, x4, x5, x6, x7, x8)
  # GenericFunctionNodeData(x1, x2, x3, x4, x5::S, x6::T, x7::String) where {T, S} = new{T,S}(x1, x2, x3, x4, x5, x6, x7)
end

"""
    $(SIGNATURES)
Fundamental structure for a DFG factor.
"""
mutable struct DFGFactor{T, S} <: AbstractDFGFactor
    label::Symbol
    tags::Vector{Symbol}
    data::GenericFunctionNodeData{T, S}
    ready::Int
    backendset::Int
    _internalId::Int64
    _variableOrderSymbols::Vector{Symbol}
    DFGFactor{T, S}(label::Symbol) where {T, S} = new{T, S}(label, Symbol[], GenericFunctionNodeData{T, S}(), 0, 0, 0, Symbol[])
    DFGFactor{T, S}(label::Symbol, _internalId::Int64) where {T, S} = new{T, S}(label, Symbol[], GenericFunctionNodeData{T, S}(), 0, 0, _internalId, Symbol[])
end

label(f::F) where F <: DFGFactor = f.label
tags(f::F) where F <: DFGFactor = f.tags
"""
    $SIGNATURES

Retrieve solver data structure stored in a factor.
"""
function solverData(f::F) where F <: DFGFactor
  return f.data
end
"""
    $SIGNATURES

Retrieve solver data structure stored in a factor.
"""
function data(f::DFGFactor)::GenericFunctionNodeData
  @warn "data() is deprecated, please use solverData()"
  return f.data
end
"""
    $SIGNATURES

Retrieve solver data structure stored in a factor.
"""
function getData(f::DFGFactor)::GenericFunctionNodeData
  #FIXME but back in later, it just slows everything down
  if !(@isdefined getDataWarnOnce)
    @warn "getData is deprecated, please use solverData(), future warnings in getData is suppressed"
    global getDataWarnOnce = true
  end
  # @warn "getData is deprecated, please use solverData()"
  return f.data
end

internalId(f::F) where F <: DFGFactor = f._internalId

# Simply for convenience - don't export
const PackedFunctionNodeData{T} = GenericFunctionNodeData{T, <: AbstractString}
PackedFunctionNodeData(x1, x2, x3, x4, x5::S, x6::T, x7::String="", x8::Vector{Int}=Int[]) where {T <: PackedInferenceType, S <: AbstractString} = GenericFunctionNodeData(x1, x2, x3, x4, x5, x6, x7, x8)
const FunctionNodeData{T} = GenericFunctionNodeData{T, Symbol}
FunctionNodeData(x1, x2, x3, x4, x5::Symbol, x6::T, x7::String="", x8::Vector{Int}=Int[]) where {T <: Union{FunctorInferenceType, ConvolutionObject}}= GenericFunctionNodeData{T, Symbol}(x1, x2, x3, x4, x5, x6, x7, x8)

"""
    $(SIGNATURES)
Structure for first-class citizens of a DFGFactor.
"""
mutable struct DFGFactorSummary <: AbstractDFGFactor
    label::Symbol
    tags::Vector{Symbol}
    _internalId::Int64
    _variableOrderSymbols::Vector{Symbol}
end

label(f::DFGFactorSummary) = f.label
data(f::DFGFactorSummary) = f.data
tags(f::DFGFactorSummary) = f.tags
internalId(f::DFGFactorSummary) = f._internalId

# SKELETON DFG
"""
	$(TYPEDEF)
Skeleton variable with essentials.
"""
struct SkeletonDFGVariable <: AbstractDFGVariable
	label::Symbol
	tags::Vector{Symbol}
end

SkeletonDFGVariable(label::Symbol) = SkeletonDFGVariable(label, Symbol[])

label(v::SkeletonDFGVariable) = v.label
tags(v::SkeletonDFGVariable) = v.tags


text:
# ----------------------------------------------------------------------------------- #
# Copyright (c) 2019 Varnerlab
# Robert Frederick School of Chemical and Biomolecular Engineering
# Cornell University, Ithaca NY 14850

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# ----------------------------------------------------------------------------------- #
function _include_my_codes(path_to_src::String, fileNameArray::Array{String,1})

    try

        for file_name in fileNameArray

            path_to_load = joinpath(path_to_src, file_name)
            include(path_to_load)
        end

    catch error
        rethrow(error)
    end

end

# get files from dir -
searchdir(path,key) = filter(x -> contains(x, key), readdir(path))

# setup project paths -
const _PATH_TO_SRC = dirname(pathof(@__MODULE__))
const _PATH_TO_BASE = joinpath(_PATH_TO_SRC, "base")
const _PATH_TO_STRATEGY = joinpath(_PATH_TO_SRC, "strategy")

# load external packages -
using Test
using TOML
using FASTX
using Logging
using JSON
using DataFrames
using Mustache
using Dates
using DelimitedFiles
using LinearAlgebra

# includes -
include("Types.jl")

# base -
my_src_codes_array = searchdir(_PATH_TO_BASE, ".jl")
_include_my_codes(_PATH_TO_BASE, my_src_codes_array)

# strategy -
my_src_codes_array = searchdir(_PATH_TO_STRATEGY, ".jl")
_include_my_codes(_PATH_TO_STRATEGY, my_src_codes_array)

# load main -
include("MakeJuliaModel.jl")


text:
#using NIFTy
using Test

@testset "NIFTy.jl" begin
    # Write your tests here.

    @testset "Domain" begin
        #TODO put dimension as parameter in testset
        @testset "Basics" begin
            for s in ((3,), (3,3), (3,3,3))
                dom = RGDomain(s...)
                @test lengths(dom) == map(x -> 1., size(dom))
                @test distances(dom) == 1 ./ size(dom)

                for harmonic in (true, false)
                    l = ntuple(x -> 1/x, length(s))
                    dom = RGDomain(s...; lengths = l, harmonic = harmonic)
                    @test isharmonic(dom) == harmonic
                    @test size(dom) == s
                    @test shape(dom) == s
                    @test dims(dom) == length(s)
                    @test lengths(dom) == l
                    @test distances(dom) == lengths(dom) ./ size(dom)

                    @test isharmonic(getcodomain(dom)) == !harmonic
                    @test getcodomain(getcodomain(dom)) == dom
                end
            end
        end

        @testset "Domain Tuple" begin
            nelem = 3
            doms = []
            for dim = 1:3
                s = ntuple(x -> nelem, dim)
                push!(doms, RGDomain(s..., harmonic = (dim%2 == 0)))
            end
            for i in 1:length(doms)
                dt = DomainTuple(doms[1:i]...)
                @test domains(dt) == tuple(doms[1:i]...)
                @test shape(dt) == map(size, tuple(doms[1:i]...))
                @test size(dt) == ntuple(x -> nelem, div(i * (i + 1), 2))
                @test length(dt) == nelem^(div(i * (i + 1), 2))
            end
        end
    end

    @testset "Field $dim-dim of $T" for dim = 1:3, T = [Int, Float64, Float32, ComplexF64]
        s = ntuple(x -> 3, dim)
        dom = RGDomain(s...)

        f = randn(T, dom)
        g = similar(f)
        @test eltype(f) == T
        for func in [size, shape, length]
            @test @eval $func($f) == $func($g) == $func($dom)
        end

        val = fill(1, s.+1)
        @test_throws AssertionError Field(dom, val)

        #TODO make val imutable if assigned to field!
        v1 = T(4)
        v2 = T(2)
        val = fill(v1, s)
        f = Field(dom, val)
        val = fill(v2, s)
        g = Field(dom, val)
        for op in [:+, :-, :*, :/]
            h = @eval broadcast($op, $f, $g)
            @test getdomain(h) == dom
            @test h.val == fill((@eval $T($op($v1, $v2))), s)
        end
        !(T <: Integer) && for op in [:exp, :log, :sin, :cos, :tan]
            h = @eval broadcast($op, $g)
            @test getdomain(h) == dom
            @test h.val == fill((@eval $T($op($v2))), s)
        end
    end
end


text:
# We want these enums to be made available in the HELICS api.
# Manually typing these out is verbose and annoying
# We make the enums uppercase since they are essentially constant integer values
# for e.g. `Lib.helics_iteration_request` -> `HELICS.HELICS_ITERATION_REQUEST`
#          `Lib.helics_iteration_request_no_iteration` -> `HELICS.HELICS_ITERATION_REQUEST_NO_ITERATION`
#          `Lib.helics_iteration_request_force_iteration` -> `HELICS.HELICS_ITERATION_REQUEST_FORCE_ITERATION`
#          `Lib.helics_iteration_request_iterate_if_needed` -> `HELICS.HELICS_ITERATION_REQUEST_ITERATE_IF_NEEDED`
# and so on ...
CEnum = Lib.CEnum

for enum_name in [
    Lib.helics_iteration_request,
    Lib.helics_iteration_result,
    Lib.helics_federate_state,
    Lib.helics_data_type,
    Lib.helics_core_type,
    Lib.helics_federate_flags,
    Lib.helics_log_levels,
    Lib.helics_error_types,
    Lib.helics_properties,
    Lib.helics_handle_options,
    Lib.helics_filter_type,
]
    # Hacks to not populate namespace
    eval(
        :(
            const $(Symbol(uppercase(split(String(Symbol(enum_name)), ".")[end]))) =
                $enum_name
        ),
    )

    for (_sym, ans) in Lib.CEnum.name_value_pairs(enum_name)
        eval(:(const $(Symbol(uppercase(String(_sym)))) = Lib.$_sym))
    end

end

# Generate the docstring for these enumerations.
# e.g. the following generates documentation like this:
# **Enumeration of the different iteration results**
#
#   •    HELICS_ITERATION_REQUEST_NO_ITERATION: No iteration is requested (0)
#
#   •    HELICS_ITERATION_REQUEST_FORCE_ITERATION: Force iteration return when able (1)
#
#   •    HELICS_ITERATION_REQUEST_ITERATE_IF_NEEDED: Only return an iteration if
#       necessary (2)

function _enum_doc_string(enum_type)
    final = String[]

    for (name, value) in CEnum.name_value_pairs(enum_type)

        n = uppercase(String(name))
        d = strip(string(Docs.doc(Base.Docs.Binding(HELICS, Symbol(n)))))
        v = string(value)

        push!(final, "- `$n`: $d ($v)")

    end
    join(final, "\n")
end

"""No iteration is requested"""
HELICS_ITERATION_REQUEST_NO_ITERATION
"""Force iteration return when able"""
HELICS_ITERATION_REQUEST_FORCE_ITERATION
"""Only return an iteration if necessary"""
HELICS_ITERATION_REQUEST_ITERATE_IF_NEEDED
"""
**Enumeration of the different iteration results**
$(_enum_doc_string(HELICS.HELICS_ITERATION_REQUEST))
"""
HELICS.HELICS_ITERATION_REQUEST

"""the iterations have progressed to the next time"""
HELICS_ITERATION_RESULT_NEXT_STEP
"""there was an error"""
HELICS_ITERATION_RESULT_ERROR
"""the federation has halted"""
HELICS_ITERATION_RESULT_HALTED
"""the federate is iterating at current time"""
HELICS_ITERATION_RESULT_ITERATING
"""
**Enumeration of possible return values from an iterative time request**
$(_enum_doc_string(HELICS.HELICS_ITERATION_RESULT))
"""
HELICS.HELICS_ITERATION_RESULT

"""when created the federate is in startup state"""
HELICS_STATE_STARTUP
"""entered after the enterInitializingMode call has returned"""
HELICS_STATE_INITIALIZATION
"""entered after the enterExectuationState call has returned"""
HELICS_STATE_EXECUTION
"""the federate has finished executing normally final values may be retrieved"""
HELICS_STATE_FINALIZE
"""error state no core communication is possible but values can be retrieved the following states are for asynchronous operations"""
HELICS_STATE_ERROR
"""indicator that the federate is pending entry to initialization state"""
HELICS_STATE_PENDING_INIT
"""state pending EnterExecution State"""
HELICS_STATE_PENDING_EXEC
"""state that the federate is pending a timeRequest"""
HELICS_STATE_PENDING_TIME
"""state that the federate is pending an iterative time request"""
HELICS_STATE_PENDING_ITERATIVE_TIME
"""state that the federate is pending a finalize request"""
HELICS_STATE_PENDING_FINALIZE
"""
**Enumeration of possible federate states**
$(_enum_doc_string(HELICS.HELICS_FEDERATE_STATE))
"""
HELICS.HELICS_FEDERATE_STATE

"""a sequence of characters"""
HELICS_DATA_TYPE_STRING
"""a double precision floating point number"""
HELICS_DATA_TYPE_DOUBLE
"""a 64 bit integer"""
HELICS_DATA_TYPE_INT
"""a pair of doubles representing a complex number"""
HELICS_DATA_TYPE_COMPLEX
"""an array of doubles"""
HELICS_DATA_TYPE_VECTOR
"""a complex vector object"""
HELICS_DATA_TYPE_COMPLEX_VECTOR
"""a named point consisting of a string and a double"""
HELICS_DATA_TYPE_NAMED_POINT
"""a boolean data type"""
HELICS_DATA_TYPE_BOOLEAN
"""time data type"""
HELICS_DATA_TYPE_TIME
"""raw data type"""
HELICS_DATA_TYPE_RAW
"""open type that can be anything"""
HELICS_DATA_TYPE_ANY
"""
**Enumeration of allowable data types for publications and inputs**
$(_enum_doc_string(HELICS.HELICS_DATA_TYPE))
"""
HELICS.HELICS_DATA_TYPE

"""a default core type that will default to something available"""
HELICS_CORE_TYPE_DEFAULT
"""use the Zero MQ networking protocol"""
HELICS_CORE_TYPE_ZMQ
"""use MPI for operation on a parallel cluster"""
HELICS_CORE_TYPE_MPI
"""use the Test core if all federates are in the same process"""
HELICS_CORE_TYPE_TEST
"""interprocess uses memory mapped files to transfer data (for use when all federates are on the same machine"""
HELICS_CORE_TYPE_INTERPROCESS
"""interprocess uses memory mapped files to transfer data (for use when all federates are on the same machine ipc is the same as [`HELICS_CORE_TYPE_INTERPROCESS`](@ref)"""
HELICS_CORE_TYPE_IPC
"""use a generic TCP protocol message stream to send messages"""
HELICS_CORE_TYPE_TCP
"""use UDP packets to send the data"""
HELICS_CORE_TYPE_UDP
"""single socket version of ZMQ core usually for high fed count on the same system"""
HELICS_CORE_TYPE_ZMQ_TEST
"""for using the nanomsg communications"""
HELICS_CORE_TYPE_NNG
"""a single socket version of the TCP core for more easily handling firewalls"""
HELICS_CORE_TYPE_TCP_SS
"""a core type using http for communication"""
HELICS_CORE_TYPE_HTTP
"""a core using websockets for communication"""
HELICS_CORE_TYPE_WEBSOCKET
"""an in process core type for handling communications in shared memory it is pretty similar to the test core but stripped from the "test" components"""
HELICS_CORE_TYPE_INPROC
"""an explicit core type that is recognized but explicitly doesn't exist, for testing and a few other assorted reasons"""
HELICS_CORE_TYPE_NULL
"""
**Pick a core type depending on compile configuration**
$(_enum_doc_string(HELICS.HELICS_CORE_TYPE))
"""
HELICS.HELICS_CORE_TYPE

"""flag indicating that a federate is observe only"""
HELICS_FLAG_OBSERVER
"""flag indicating that a federate can only return requested times"""
HELICS_FLAG_UNINTERRUPTIBLE
"""flag indicating that a federate can be interrupted"""
HELICS_FLAG_INTERRUPTIBLE
"""flag indicating that a federate/interface is a signal generator only"""
HELICS_FLAG_SOURCE_ONLY
"""flag indicating a federate/interface should only transmit values if they have changed(binary equivalence)"""
HELICS_FLAG_ONLY_TRANSMIT_ON_CHANGE
"""flag indicating a federate/interface should only trigger an update if a value has changed (binary equivalence)"""
HELICS_FLAG_ONLY_UPDATE_ON_CHANGE
"""flag indicating a federate should only grant time if all other federates have already passed the requested time"""
HELICS_FLAG_WAIT_FOR_CURRENT_TIME_UPDATE
"""flag indicating a federate should operate on a restrictive time policy, which disallows some 2nd order time evaluation and can be useful for certain types of dependency cycles and update patterns, but generally shouldn't be used as it can lead to some very slow update conditions"""
HELICS_FLAG_RESTRICTIVE_TIME_POLICY
"""flag indicating that a federate has rollback capability"""
HELICS_FLAG_ROLLBACK
"""flag indicating that a federate performs forward computation and does internal rollback"""
HELICS_FLAG_FORWARD_COMPUTE
"""flag indicating that a federate needs to run in real time"""
HELICS_FLAG_REALTIME
"""flag indicating that the federate will only interact on a single thread"""
HELICS_FLAG_SINGLE_THREAD_FEDERATE
"""flag specifying that a federate, core, or broker may be slow to respond to pings If the federate goes offline there is no good way to detect it so use with caution """
HELICS_FLAG_SLOW_RESPONDING
"""used to delay a core from entering initialization mode even if it would otherwise be ready"""
HELICS_FLAG_DELAY_INIT_ENTRY
"""used to clear the [`HELICS_DELAY_INIT_ENTRY`](@ref HELICS.HELICS_FEDERATE_FLAGS) flag in cores"""
HELICS_FLAG_ENABLE_INIT_ENTRY
"""used to not display warnings on mismatched requested times"""
HELICS_FLAG_IGNORE_TIME_MISMATCH_WARNINGS
"""specify that a federate error should terminate the federation"""
HELICS_FLAG_TERMINATE_ON_ERROR
"""
**Enumeration of possible federate flags**
$(_enum_doc_string(HELICS.HELICS_FEDERATE_FLAGS))
"""
HELICS.HELICS_FEDERATE_FLAGS

"""don't print anything except a few catastrophic errors"""
HELICS_LOG_LEVEL_NO_PRINT
"""only print error level indicators"""
HELICS_LOG_LEVEL_ERROR
"""only print warnings and errors"""
HELICS_LOG_LEVEL_WARNING
"""warning errors and summary level information"""
HELICS_LOG_LEVEL_SUMMARY
"""summary+ notices about federate and broker connections +messages about network connections"""
HELICS_LOG_LEVEL_CONNECTIONS
"""connections+ interface definitions"""
HELICS_LOG_LEVEL_INTERFACES
"""interfaces + timing message"""
HELICS_LOG_LEVEL_TIMING
"""timing+ data transfer notices"""
HELICS_LOG_LEVEL_DATA
"""all internal messages"""
HELICS_LOG_LEVEL_TRACE
@doc """
**Log level definitions**
$(_enum_doc_string(HELICS.HELICS_LOG_LEVELS))
"""
HELICS.HELICS_LOG_LEVELS

"""the function executed successfully """
HELICS_OK
"""registration has failed"""
HELICS_ERROR_REGISTRATION_FAILURE
"""the operation to connect has failed"""
HELICS_ERROR_CONNECTION_FAILURE
"""indicator that the object used was not a valid object """
HELICS_ERROR_INVALID_OBJECT
"""the parameter passed was invalid and unable to be used"""
HELICS_ERROR_INVALID_ARGUMENT
"""the input was discarded and not used for some reason """
HELICS_ERROR_DISCARD
"""the federate has terminated unexpectedly and the call cannot be completed"""
HELICS_ERROR_SYSTEM_FAILURE
"""the function issued a warning of some kind """
HELICS_WARNING
"""error issued when an invalid state transition occurred """
HELICS_ERROR_INVALID_STATE_TRANSITION
"""the call made was invalid in the present state of the calling object"""
HELICS_ERROR_INVALID_FUNCTION_CALL
"""the function execution has failed"""
HELICS_ERROR_EXECUTION_FAILURE
"""insufficient space is available to store requested data"""
HELICS_ERROR_INSUFFICIENT_SPACE
"""the function produced a helics error of some other type """
HELICS_ERROR_OTHER
"""global fatal error for federation """
HELICS_ERROR_FATAL
"""an unknown non-helics error was produced"""
HELICS_ERROR_EXTERNAL_TYPE
@doc """
**Enumeration of return values from the C interface functions**
$(_enum_doc_string(HELICS.HELICS_ERROR_TYPES))
"""
HELICS.HELICS_ERROR_TYPES

"""the property controlling the minimum time delta for a federate"""
HELICS_PROPERTY_TIME_DELTA
"""the property controlling the period for a federate"""
HELICS_PROPERTY_TIME_PERIOD
"""the property controlling time offset for the period of federate"""
HELICS_PROPERTY_TIME_OFFSET
"""the property controlling real time lag for a federate the max time a federate can lag real time"""
HELICS_PROPERTY_TIME_RT_LAG
"""the property controlling real time lead for a federate the max time a federate can be ahead of real time"""
HELICS_PROPERTY_TIME_RT_LEAD
"""the property controlling real time tolerance for a federate sets both [`HELICS_PROPERTY_RT_LAG`](@ref) and [`HELICS_PROPERTY_RT_LEAD`](@ref)"""
HELICS_PROPERTY_TIME_RT_TOLERANCE
"""the property controlling input delay for a federate"""
HELICS_PROPERTY_TIME_INPUT_DELAY
"""the property controlling output delay for a federate"""
HELICS_PROPERTY_TIME_OUTPUT_DELAY
"""integer property controlling the maximum number of iterations in a federate"""
HELICS_PROPERTY_INT_MAX_ITERATIONS
"""integer property controlling the log level in a federate see [`HELICS_LOG_LEVELS`](@ref)"""
HELICS_PROPERTY_INT_LOG_LEVEL
"""integer property controlling the log level for file logging in a federate see [`HELICS_LOG_LEVELS`](@ref)"""
HELICS_PROPERTY_INT_FILE_LOG_LEVEL
"""integer property controlling the log level for file logging in a federate see [`HELICS_LOG_LEVELS`](@ref)"""
HELICS_PROPERTY_INT_CONSOLE_LOG_LEVEL
@doc """
**Enumeration of properties that apply to federates**
$(_enum_doc_string(HELICS.HELICS_PROPERTIES))
"""
HELICS.HELICS_PROPERTIES

"""specify that a connection is required for an interface and will generate an error if not available"""
HELICS_HANDLE_OPTION_CONNECTION_REQUIRED
"""specify that a connection is NOT required for an interface and will only be made if available no warning will be issues if not available"""
HELICS_HANDLE_OPTION_CONNECTION_OPTIONAL
"""specify that only a single connection is allowed for an interface"""
HELICS_HANDLE_OPTION_SINGLE_CONNECTION_ONLY
"""specify that multiple connections are allowed for an interface"""
HELICS_HANDLE_OPTION_MULTIPLE_CONNECTIONS_ALLOWED
"""specify that the last data should be buffered and send on subscriptions after init"""
HELICS_HANDLE_OPTION_BUFFER_DATA
"""specify that the types should be checked strictly for pub/sub and filters"""
HELICS_HANDLE_OPTION_STRICT_TYPE_CHECKING
"""specify that the mismatching units should be ignored"""
HELICS_HANDLE_OPTION_IGNORE_UNIT_MISMATCH
"""specify that an interface will only transmit on change (only applicable to publications)"""
HELICS_HANDLE_OPTION_ONLY_TRANSMIT_ON_CHANGE
"""specify that an interface will only update if the value has actually changed"""
HELICS_HANDLE_OPTION_ONLY_UPDATE_ON_CHANGE
"""specify that an interface does not participate in determining time interrupts"""
HELICS_HANDLE_OPTION_IGNORE_INTERRUPTS
@doc """
**Enumeration of options that apply to handles**
$(_enum_doc_string(HELICS.HELICS_HANDLE_OPTIONS))
"""
HELICS.HELICS_HANDLE_OPTIONS

"""a custom filter type that executes a user defined callback"""
HELICS_FILTER_TYPE_CUSTOM
"""a filter type that executes a fixed delay on a message"""
HELICS_FILTER_TYPE_DELAY
"""a filter type that executes a random delay on the messages"""
HELICS_FILTER_TYPE_RANDOM_DELAY
"""a filter type that randomly drops messages"""
HELICS_FILTER_TYPE_RANDOM_DROP
"""a filter type that reroutes a message to a different destination than originally specified"""
HELICS_FILTER_TYPE_REROUTE
"""a filter type that duplicates a message and sends the copy to a different destination"""
HELICS_FILTER_TYPE_CLONE
"""a customizable filter type that can perform different actions on a message based on firewall like rules"""
HELICS_FILTER_TYPE_FIREWALL
@doc """
**Enumeration of the predefined filter types**
$(_enum_doc_string(HELICS.HELICS_FILTER_TYPE))
"""
HELICS.HELICS_FILTER_TYPE


text:
module TestSIMD
include("preamble.jl")

using Transducers: @simd_if, UseSIMD, usesimd, Reduction, skipcomplete, R_

function simd_if_demo(xf, ys, xs)
    @inbounds @simd_if xf for i in eachindex(ys, xs)
        ys[i] = 2 .* xs[i]
    end
    return ys
end

@testset "@simd_if" begin
    xs = [1:100;] .* 1.0
    @test simd_if_demo(Map(identity), zero(xs), xs) == 2xs
    @test simd_if_demo(UseSIMD{false}(), zero(xs), xs) == 2xs
    @test simd_if_demo(UseSIMD{true}(), zero(xs), xs) == 2xs
end

asrf(xf) = Reduction(xf, right)

@testset "usesimd" begin
    xfsimd = UseSIMD{false}()
    @test usesimd(asrf(Map(identity)), xfsimd) === asrf(xfsimd |> Map(identity))
    @test usesimd(asrf(Cat()), xfsimd) === asrf(Cat() |> xfsimd)
    @test usesimd(asrf(Map(sin) |> Cat() |> Map(cos)), xfsimd) ===
        asrf(Map(sin) |> Cat() |> xfsimd |> Map(cos))
    @test usesimd(asrf(Map(sin) |> Cat() |> Map(cos) |> Cat() |> Map(tan)),
                  xfsimd) ===
        asrf(Map(sin) |> Cat() |> Map(cos) |> Cat() |> xfsimd |> Map(tan))
end

@testset "skipcomplete" begin
    @testset for rf in [
            asrf(UseSIMD{false}()),
            usesimd(asrf(Map(identity)), UseSIMD{false}()),
            usesimd(asrf(Map(sin) |> Map(cos)), UseSIMD{false}()),
            ]
        @test rf isa R_{UseSIMD}
        @test skipcomplete(rf) isa R_{UseSIMD}
    end
end

@testset "foldl" begin
    @testset for simd in [false, true]
        xs = [1:100;]
        result = foldl(eduction(Map(identity), xs);
                       init = 0.0,
                       simd = simd) do y, x
            y + 2x
        end
        @test result == sum(2 .* xs)
    end
end

@testset "foreach" begin
    @testset for simd in [false, true, :ivdep]
        xs = [1:100;]
        ys = zeros(100)
        foreach(Zip(Count(), Map(x -> x + 1.0)), xs; simd=simd) do (i, x)
            @inbounds ys[i] = x
        end
        @test ys == xs .+ 1.0
    end
end

@testset "invalid option" begin
    err = @test_error reduce(+, Map(identity), 1:1; simd = :invalid_option)
    @test occursin("Unknown `simd` argument: invalid_option", sprint(showerror, err))
end

end  # module


text:
using JuMP, EAGO

                     m = Model()

                     EAGO.register_eago_operators!(m)

                     @variable(m, -1 <= x[i=1:5] <= 1)

                     @variable(m, -36.7700820821577 <= q <= 37.6713142442229)

                     add_NL_constraint(m, :(1/(1 + exp(-(0.9266287961121966 + 0.9630341445616355*1/(1 + exp(-(0.9080159995418975 + 0.10769169268185319*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.2597058016512084*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.8597063403720888*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.17950756837409587*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + 0.8197021439185934*1/(1 + exp(-(-0.9264374759333807 + -0.7028105280156569*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.6656745963467641*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.9099166873549671*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.9082646170966706*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + -0.8564198456770362*1/(1 + exp(-(-0.7118497063916944 + 0.36586202274128254*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + -0.9129427463398758*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.5191517354247042*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.42477634545139953*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + 0.7868467121279954*1/(1 + exp(-(0.8429853377330745 + -0.6377098680254001*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.04468698484435718*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.3607403638969684*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.4004266402265344*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5])))))))))) + 1/(1 + exp(-(-0.9209290943615045 + -0.04560727661330999*1/(1 + exp(-(0.9080159995418975 + 0.10769169268185319*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.2597058016512084*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.8597063403720888*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.17950756837409587*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + -0.04187997574232316*1/(1 + exp(-(-0.9264374759333807 + -0.7028105280156569*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.6656745963467641*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.9099166873549671*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.9082646170966706*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + 0.009897273687461627*1/(1 + exp(-(-0.7118497063916944 + 0.36586202274128254*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + -0.9129427463398758*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.5191517354247042*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.42477634545139953*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + -0.4138607422831795*1/(1 + exp(-(0.8429853377330745 + -0.6377098680254001*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.04468698484435718*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.3607403638969684*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.4004266402265344*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5])))))))))) + 1/(1 + exp(-(0.41055131347137674 + 0.5074173875637427*1/(1 + exp(-(0.9080159995418975 + 0.10769169268185319*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.2597058016512084*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.8597063403720888*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.17950756837409587*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + 0.7865169798440914*1/(1 + exp(-(-0.9264374759333807 + -0.7028105280156569*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.6656745963467641*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.9099166873549671*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.9082646170966706*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + -0.6322955384333278*1/(1 + exp(-(-0.7118497063916944 + 0.36586202274128254*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + -0.9129427463398758*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.5191517354247042*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.42477634545139953*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + 0.03598168048021799*1/(1 + exp(-(0.8429853377330745 + -0.6377098680254001*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.04468698484435718*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.3607403638969684*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.4004266402265344*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5])))))))))) + 1/(1 + exp(-(-0.7045693885767101 + 0.765322888364711*1/(1 + exp(-(0.9080159995418975 + 0.10769169268185319*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.2597058016512084*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.8597063403720888*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.17950756837409587*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + 0.9087169409161477*1/(1 + exp(-(-0.9264374759333807 + -0.7028105280156569*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.6656745963467641*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.9099166873549671*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.9082646170966706*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + -0.29202574365958966*1/(1 + exp(-(-0.7118497063916944 + 0.36586202274128254*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + -0.9129427463398758*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + -0.5191517354247042*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.42477634545139953*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5]))))))) + -0.14636407330293322*1/(1 + exp(-(0.8429853377330745 + -0.6377098680254001*1/(1 + exp(-(-0.02399021036854254 + -0.4943608904262171*$(x[1]) + -0.3028702711871105*$(x[2]) + 0.7730701583831108*$(x[3]) + -0.5656579689281509*$(x[4]) + 0.08835909373115314*$(x[5])))) + 0.04468698484435718*1/(1 + exp(-(-0.6908874409241781 + -0.9493883109743697*$(x[1]) + 0.1034335229123764*$(x[2]) + 0.3901179347519834*$(x[3]) + -0.16073609758033847*$(x[4]) + 0.052390589356133166*$(x[5])))) + 0.3607403638969684*1/(1 + exp(-(0.7510327333371474 + 0.48933101371416265*$(x[1]) + 0.5299590321169751*$(x[2]) + 0.05546767568522437*$(x[3]) + -0.4663274814822995*$(x[4]) + -0.6567285643500202*$(x[5])))) + 0.4004266402265344*1/(1 + exp(-(-0.27157848416188113 + 0.560003468778171*$(x[1]) + 0.2933473130688298*$(x[2]) + 0.8808820816540863*$(x[3]) + 0.6726368959629845*$(x[4]) + -0.19953521813356856*$(x[5])))))))))) - $q <= 0.0))

                     @objective(m, Min, q)

                     return m

max_stars_repo_name: PSORLab/RSActivationFunctions

max_issues_repo_name: PSORLab/RSActivationFunctions

max_forks_repo_name: PSORLab/RSActivationFunctions


text:
using VisualStringDistances
using Documenter

makedocs(;
    modules=[VisualStringDistances],
    authors="Eric P. Hanson",
    repo="https://github.com/sgibbs409/VisualStringDistances.jl/blob/{commit}{path}#L{line}",
    sitename="VisualStringDistances.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://sgibbs409.github.io/VisualStringDistances.jl",
        assets=String[],
    ),
    pages=[
        "Home" => "index.md",
        "Visualizations" => "visualizations.md",
        "Package names" => "packagenames.md",
    ],
)

deploydocs(;
    repo="github.com/sgibbs409/VisualStringDistances.jl",
)


text:
using Base.Test
using GpSelection

n = 100;  K = 5; num_sig = 2; df = 1.0; p_beta = 2
dat = generateData(K, p_beta, n, df, num_sig)
options = Options(100)
priors = PriorPars(5, 10, 10, fill(5, K), fill(10, K), 0.0, zeros(K, K))

#@elapsed beta_E, sigma2_E, gamma_E, H_E = varBayes(dat, priors, options)


text:
#####
##### upgrades/deprecations
#####

# For some functions below, we use `Base.depwarn`.
#
# In these cases, we don't use `@deprecate` because the method we're "redirecting to" for the deprecation path
# (e.g. `_deprecated_read_table`) is different than the method that we're suggesting downstream callers use
# instead (`Legolas.read`).

using Base: depwarn

function _deprecated_read_table(io_or_path, schema=nothing)
    table = Legolas.read_arrow(io_or_path)
    schema isa Legolas.Schema && Legolas.validate(table, schema)
    return table
end

function read_signals(io_or_path; validate_schema::Bool=true)
    depwarn("`Onda.read_signals(io_or_path)` is deprecated, use `Legolas.read(io_or_path)` instead", :read_signals)
    return _deprecated_read_table(io_or_path, validate_schema ? Legolas.Schema("onda.signal@1") : nothing)
end
export read_signals

function read_annotations(io_or_path; validate_schema::Bool=true)
    depwarn("`Onda.read_annotations(io_or_path)` is deprecated, use `Legolas.read(io_or_path)` instead", :read_annotations)
    return _deprecated_read_table(io_or_path, validate_schema ? Legolas.Schema("onda.annotation@1") : nothing)
end
export read_annotations

@deprecate materialize Legolas.materialize false
@deprecate gather Legolas.gather false

# Note: no deprecation can be provided for when `validate_on_construction()` is re-defined
@deprecate validate_on_construction VALIDATE_SAMPLES_DEFAULT[]

@deprecate(validate_signal_schema(s),
           isnothing(s) ? nothing : Legolas.validate(s, Legolas.Schema("onda.signal@1")),
           false)

@deprecate(validate_annotation_schema(s),
           isnothing(s) ? nothing : Legolas.validate(s, Legolas.Schema("onda.annotation@1")),
           false)

if VERSION >= v"1.5"
    @deprecate Annotation(recording, id, span; custom...) Annotation(; recording, id, span, custom...)
    @deprecate(Signal(recording, file_path, file_format, span, kind, channels, sample_unit,
                      sample_resolution_in_unit, sample_offset_in_unit, sample_type, sample_rate;
                      custom...),
               Signal(; recording, file_path, file_format, span, kind, channels, sample_unit,
                      sample_resolution_in_unit, sample_offset_in_unit, sample_type, sample_rate,
                      custom...))
    @deprecate(SamplesInfo(kind, channels, sample_unit,
                           sample_resolution_in_unit, sample_offset_in_unit,
                           sample_type, sample_rate; custom...),
               SamplesInfo(; kind, channels, sample_unit,
                           sample_resolution_in_unit, sample_offset_in_unit,
                           sample_type, sample_rate, custom...))
else
    @deprecate(Annotation(recording, id, span; custom...),
               @compat Annotation(; recording, id, span, custom...))
    @deprecate(Signal(recording, file_path, file_format, span, kind, channels, sample_unit,
                      sample_resolution_in_unit, sample_offset_in_unit, sample_type, sample_rate;
                      custom...),
               @compat Signal(; recording, file_path, file_format, span, kind, channels, sample_unit,
                              sample_resolution_in_unit, sample_offset_in_unit, sample_type, sample_rate,
                              custom...))
    @deprecate(SamplesInfo(kind, channels, sample_unit,
                           sample_resolution_in_unit, sample_offset_in_unit,
                           sample_type, sample_rate; custom...),
               @compat SamplesInfo(; kind, channels, sample_unit,
                                   sample_resolution_in_unit, sample_offset_in_unit,
                                   sample_type, sample_rate, custom...))
end

@deprecate Signal(info::SamplesInfo; kwargs...) Signal(Tables.rowmerge(info; kwargs...))

for T in (:SamplesInfo, :Signal, :Annotation)
    S = string(T)
    @eval function ConstructionBase.setproperties(x::$T, patch::NamedTuple)
        depwarn("`setproperties(x::$($S), patch)` is deprecated in favor of $($S)(Tables.rowmerge(x, patch))", :setproperties)
        return $T(Tables.rowmerge(x, patch))
    end
end

function validate(::SamplesInfo)
    depwarn("`validate(::SamplesInfo)` is deprecated; avoid invoking this method in favor of calling `validate(::Samples)`", :validate)
    return nothing
end


text:
function fib(n)
    M = [BigInt(1) BigInt(1); BigInt(1) BigInt(0)]
    R = M^(n-1)
    return R[1]
end

n = parse(Int64, ARGS[1])
println("res = ", fib(n))

max_stars_repo_name: juliancoffee/fib

max_issues_repo_name: juliancoffee/fib

max_forks_repo_name: juliancoffee/fib


text:
using NeuralQuantum
using Test

N = 4
@testset "Hilbert spaces " begin
    hilb = HomogeneousSpin(N)
    s = state(hilb)
    vals = Int[]
    for i=1:spacedimension(hilb)
        set!(s, hilb, i)
        push!(vals, toint(s, hilb))
    end

    @test all(vals .== 1:spacedimension(hilb))

    shilb = SuperOpSpace(hilb)
    @test physical(shilb) === hilb

    s = state(shilb)
    vals = Int[]
    for i=1:spacedimension(shilb)
        set!(s, shilb, i)
        push!(vals, toint(s, shilb))
    end
    @test all(vals .== 1:spacedimension(shilb))
end

function test_hilb_iterator(hilb)
    it = states(hilb)

    all_states = collect(it)

    @test length(all_states) == spacedimension(hilb)
    @test length(it) == spacedimension(hilb)
    @test eltype(it) == typeof(state(hilb))

    vals = zeros(length(all_states))
    for (i,v) = enumerate(all_states)
        vals[i] = index(hilb, v)
    end

    @test all(vals .== 1:spacedimension(hilb))
end

@testset "Hilbert space iterator" begin
    test_hilb_iterator(HomogeneousSpin(N))
    test_hilb_iterator(HomogeneousFock(N,2))
    test_hilb_iterator(SuperOpSpace(HomogeneousSpin(N)))
end


text:
@polly function kernel_nussinov(seq, table)
    n = size(seq,1)

    for i = n:-1:1, j = i+1:n
        if (j-1) >= 1
            table[i,j] = max(table[i,j], table[i,j-1])
        end

        if (i+1) <= n
            table[i,j] = max(table[i,j], table[i+1,j])
        end

        if ((j-1) >= 1) & ((i+1) <= n)
            # don't allow adjacent elements to bond */
            if (i < j-1)
                table[i,j] = max(table[i,j], table[i+1,j-1] + (seq[i] + seq[j] == 3 ? 1 : 0))
            else
                table[i,j] = max(table[i,j], table[i+1,j-1])
            end
        end

        for k = (i+1):(j-1)
            table[i,j] = max(table[i,j], table[i,k] + table[k+1,j])
        end
    end
end

let
    n = 2500

    seq = zeros(Float32, n)
    table = zeros(Float32, n, n)

    # base is AGCT/0..3
    for i = 1:n
        seq[i] = ((i+1)%4)
    end

    for i = 1:n, j = 1:n
        table[i,j] = 0
    end

    SUITE["nussinov"] = @benchmarkable kernel_nussinov(seq, table) setup = (seq = copy($seq); table = copy($table))
end


text:
using BenchmarkTools
BenchmarkTools.DEFAULT_PARAMETERS.samples = 100

function compute(file::String)::Int
    chars = Dict(Char(Int('A') + i) => i + 1 for i ∈ 0:25)
    return sum(i * chars[c] for (i, name) ∈ enumerate(sort(split(replace(file, "\"" => ""), ","))) for c ∈ name)
end

file = read("problems/0022/p022_names.txt", String)

compute(file)

@benchmark compute(file)

max_stars_repo_name: Dynortice/Project-Euler

max_issues_repo_name: Dynortice/Project-Euler

max_forks_repo_name: Dynortice/Project-Euler


text:
using LightGraphs
using .QCCDevDes_Types
using .QCCDevControl_Types

NIONS = 0

"""
Creates a dictionary of junctions from JSON objects.
Throws ArgumentError if junction IDs are repeated.
Throws ArgumentError if junction is not connected to anything.
Throws ArgumentError if unsupported junction type is passed.
"""
function _initJunctions(gateZones::Union{Nothing,Array{ZoneInfoDesc}},
    auxZones::Union{Nothing,Array{ZoneInfoDesc}}, loadZones ::Union{Nothing,Array{LoadZoneInfoDesc}},
    junctions::Array{JunctionInfoDesc})::Dict{Symbol,Junction}

    aux = (zone,id) -> !isnothing(zone) ? filter(x -> x.end0 == id || x.end1 == id, zone) : []

    res = Dict{Symbol,Junction}()
    for j ∈ junctions
        haskey(res, Symbol(j.id)) && throw(ArgumentError("Repeated junction ID: $(j.id)."))

        connectedGateZones = aux(gateZones,j.id)
        connectedAuxZones = aux(auxZones,j.id)
        connectedLoadZones = aux(loadZones,j.id)

        if isempty(connectedGateZones) && isempty(connectedAuxZones) && isempty(connectedLoadZones)
            throw(ArgumentError("Junction with ID $(j.id) is not connected to anything."))
        end

        ends = Symbol[]
        map(x -> push!(ends,Symbol(x.id)), connectedGateZones)
        map(x -> push!(ends,Symbol(x.id)), connectedAuxZones)
        map(x -> push!(ends,Symbol(x.id)), connectedLoadZones)

        tmpId = j.id == "" ? nothing : Symbol(j.id)
        res[tmpId] = Junction(tmpId, Symbol(j.type), ends)
    end
    return res
end

"""
Creates a dictionary of auxiliary zones using an object AuxZoneDesc.
Throws ArgumentError if auxiliary zones IDs are repeated.
"""
function _initAuxZones(auxZoneDesc::AuxZoneDesc)::Dict{Symbol,AuxZone}
    auxZonesCtrl = Dict{Symbol,AuxZone}()
    err = id -> ArgumentError("Repeated auxiliary zone ID: $id ")
    endId = id -> id == "" ? nothing : Symbol(id)

    map(aux -> haskey(auxZonesCtrl, Symbol(aux.id)) ? throw(err(aux.id)) :
              auxZonesCtrl[Symbol(aux.id)] = AuxZone(Symbol(aux.id), aux.capacity,
                                                    endId(aux.end0), endId(aux.end1)),
              auxZoneDesc.auxZones)
    return auxZonesCtrl
end

"""
Creates a new Qubit in a loading hole
"""
function initQubit(loading_zone::Symbol)::Qubit
    global NIONS
    NIONS += 1
    return Qubit(NIONS, loading_zone)
end

"""
Creates a dictionary of loading zones using an object LoadZoneDesc.
Throws ArgumentError if loading zones IDs are repeated.
"""
function _initLoadingZones(loadZoneDesc::LoadZoneDesc)::Dict{Symbol,LoadingZone}
    loadingZonesCtrl = Dict{Symbol, LoadingZone}()
    err = id -> ArgumentError("Repeated loading zone with ID: $id")
    endId = id -> id == "" ? nothing : Symbol(id)

    map(aux -> haskey(loadingZonesCtrl, Symbol(aux.id)) ? throw(err(aux.id)) :
               loadingZonesCtrl[Symbol(aux.id)] = LoadingZone(Symbol(aux.id),
                                             endId(aux.end0), endId(aux.end1)),
               loadZoneDesc.loadZones)
    return loadingZonesCtrl
end

"""
Creates a dictionary of gate zones using a object GateZoneDesc.
Throws ArgumentError if gate zones ID is repeated.
"""
function _initGateZone(gateZoneDesc::GateZoneDesc)::Dict{Symbol,GateZone}
    gateZonessCtrl = Dict{Symbol,GateZone}()
    err = id -> ArgumentError("Repeated gate zone with ID: $id.")
    endId = id -> id == "" ? nothing : Symbol(id)

    map(tr -> haskey(gateZonessCtrl, Symbol(tr.id)) ? throw(err(tr.id)) :
                     gateZonessCtrl[Symbol(tr.id)] = GateZone(Symbol(tr.id), tr.capacity,
                     endId(tr.end0), endId(tr.end1)),
             gateZoneDesc.gateZones)

    return gateZonessCtrl
end

"""
Tests that the user input topology makes sense.
Throws error when:
    - Zones are wrong connected
    - Zones don't exist
"""
function _checkInitErrors(junctions:: Dict{Symbol, Junction},
                          auxZones::Dict{Symbol,AuxZone},
                          gateZones::Dict{Symbol,GateZone},
                          loadingZones::Dict{Symbol,LoadingZone})

    map(aux -> __auxCheck(aux.end0,aux.end1,gateZones,loadingZones,junctions,aux.id),
        values(auxZones))
    map(aux -> __auxCheck(aux.end0,aux.end1,auxZones,loadingZones,junctions,aux.id),
        values(gateZones))
    map(aux -> __auxCheck(aux.end0,aux.end1,auxZones,gateZones,junctions,aux.id),
        values(loadingZones))

end

function __auxCheck(end0::Union{Symbol, Nothing}, end1::Union{Symbol, Nothing},
                    zone1::Dict{Symbol,T}, zone2::Dict{Symbol,N},
                     zone3::Dict{Symbol,Z}, id::Symbol) where {T, N, Z}

    isWrong = (zone1,zone2,zone3,id) -> !isnothing(id) &&
                           !(haskey(zone1,id) || haskey(zone2,id) || haskey(zone3,id))

    if isnothing(end0) && isnothing(end1)
        throw(ArgumentError("Topology's element with ID $id is isolated." *
                            " Element cannot be isolated"))
    elseif isWrong(zone1,zone2,zone3,end0) || isWrong(zone1,zone2,zone3,end1)
        throw(ArgumentError("Topology's element with ID $id is wrong connected."))

    end
end

################################### ↓↓↓↓↓↓ DEPRECATED ZONE ↓↓↓↓↓↓ #####################################################################

"""
--> DEPRECATED
Creates a graph using an object QCCDevDescription.
Throws ArgumentError if LightGraphs fails to add a node. This will happen
    if there are redundancies in the adjacency list (i.e. repeated edges),
    so maybe is not worth having.
"""
function initGraph(topology::QCCDevDescription)::SimpleGraph{Int64}
    nodesAdjacency::Dict{String,Array{Int64}} = topology.adjacency.nodes
    graph::SimpleGraph{Int64} = SimpleGraph(length(nodesAdjacency))

    for nodes in keys(nodesAdjacency)
        for node in nodesAdjacency[nodes]
            stat = add_edge!(graph, parse(Int64, nodes), node)
            stat || throw(ArgumentError("Failed adding edge ($nodes,$node) to graph."))
        end
    end
    return graph
end

########################################################################################################

# Doesn't work && not needed.

# """
# Helper function  for `_initAdjacency`. Takes the current adjacency object and modifies it in-place.
# It adds new connections to the adjacency list if they're not already added.
# """
# function _addToAdjacency(adjacency ::Dict{String, Array{String}}, collection ::Array{T}) where T
#     for element ∈ collection
#         id = element.id
#         end0 = element.end0
#         end1 = element.end1
#         if !haskey(adjacency, end0)
#             adjacency[id] = [end0]
#             if !haskey(adjacency, end1)
#                 push!(adjacency[id], end1)
#             end
#         elseif !haskey(adjacency, end1)
#             adjacency[id] = [end1]
#         end
#     end
# end
#
# """
# Creates adjacency list from QCCDevCtrl attributes.
# The adjacency list is a dictionary in which the key is the ID of one device component, and the value
# is an array of Ids to the element its adjacent to.
# """
# function _initAdjacency(device ::QCCDevDescription)::Dict{String,Array{String}}
#     adjacency = Dict{String, Array{String}}()
#     _addToAdjacency(adjacency, device.gateZone.gateZones)
#     _addToAdjacency(adjacency, device.auxZone.auxZones)
#     _addToAdjacency(adjacency, device.junction.junctions)
#     _addToAdjacency(adjacency, device.loadZone.loadZones)
#     return adjacency
# end

max_stars_repo_name: TartuQC/trapped-ion-qccd-simulator

max_issues_repo_name: TartuQC/trapped-ion-qccd-simulator

max_forks_repo_name: TartuQC/trapped-ion-qccd-simulator


text:
module ConservationLawsDiffEq
  using DiffEqCallbacks
  using LinearAlgebra
  using SparseArrays
  using TreeViews
  using DelimitedFiles
  import Tensors

  import ForwardDiff
  using RecipesBase
  import FastGaussQuadrature
  using StaticArrays

  # Interfaces
  import DiffEqBase: DiscreteCallback,AbstractODESolution

  # Reconstructions
  abstract type AbstractReconstruction end

  # Limiters
  abstract type AbstractSlopeLimiter end

  #Interface functions
  include("mesh.jl")
  include("mesh_generator.jl")
  include("fvmesh.jl")
  include("fvintegrator.jl")
  include("fvSchemesAPI.jl")
  include("fvflux.jl")
  include("timecallbacks.jl")
  include("fvutils.jl")
  include("fvsolve.jl")
  include("clsolution.jl")
  include("uniform1Dmesh.jl")

  #Algoritms
  include("ENO_WENO.jl")
  include("schemes/Tecno_scheme.jl")
  include("schemes/WENO_Scheme.jl")
  include("schemes/Lax_Friedrichs_scheme.jl")
  include("schemes/Lax_Wendroff2s_scheme.jl")
  include("schemes/CU_scheme.jl")
  include("schemes/SKT_scheme.jl")
  include("limiters.jl")

  # Other
  include("errors.jl")
  include("plotRecipe.jl")

  #Exports
  # Schemes API
  export update_flux_value

  # User API
    export getSemiDiscretization
  export getInitialState
  export get_adaptative_callback, getCFLCallback
  export update_dt!
  export Periodic, ZeroFlux, Dirichlet
  export save_csv
  export fv_solution

  # mesh related functions
  export line_mesh, rectangle_mesh
  export LineCell, TriangleCell, RectangleCell
  export getncells, get_coordinates, getnnodes, getnedges, getnfaces
  export PolytopalMesh, getdimension
  export getnodeset, getnodecoords
  export cell_volume, cell_centroid, cell_diameter
  export Uniform1DFVMesh

  # Schemes
  export FVTecnoScheme
  export FVCompWENOScheme, FVCompMWENOScheme, FVSpecMWENOScheme
  export LaxFriedrichsScheme
  export LaxWendroff2sScheme, LaxWendroffScheme
  export LocalLaxFriedrichsScheme, GlobalLaxFriedrichsScheme
  export FVCUScheme, FVDRCUScheme
  export FVSKTScheme
  export FVDRCU5Scheme

  # scheme utils
  export get_total_u, get_relative_L1_error, get_L1_error, approx_L1_error
  export num_integrate
  export get_conv_order_table, mesh_norm, get_LP_error, get_num_LP_error
  export GeneralizedMinmodLimiter, OsherLimiter, MinmodLimiter, SuperbeeLimiter
end


text:
# Implicitly solving the heat equation
# at cell centers and cell faces
# Equation: ∂_t T = α * ∇²(T) + S, S = [β*sin(γ*z), β]
# Boundary conditions: ∇T(z=a) = ∇T_bottom, T(z=b) = T_top
import Plots
using LinearAlgebra

# Note that we can stably take huge timesteps even when α is large
# warning, analytic solution only valid for a=0, b=1
a,b, n = 0, 1, 10               # zmin, zmax, number of cells
n̂_min, n̂_max = -1, 1            # Outward facing unit vectors
α = 100;                        # thermal diffusivity, larger means more stiff
β, γ = 10000, π;                # source term coefficients
Δt = 1000;                      # timestep
N_t = 10;                       # number of timesteps
FT = Float64;                   # float type
Δz = FT(b-a)/FT(n)
Δz² = Δz^2;
∇²_op = [1/Δz², -2/Δz², 1/Δz²]; # row or Laplacian operator
T_init = 1;                     # Initial temperature, shouldn't matter for steady solution
∇T_bottom = 10;                  # Temperature gradient at the top
T_top = 1;                      # Temperature at the bottom
use_sin_forcing = true
S(z) = use_sin_forcing ? β*sin(γ*z) : β # source term, (sin for easy integration)
zf = range(a, b, length=n+1);
zc = map(i-> zf[i]+Δz/2, 1:n);

# Analytic steady state solution:
# ∂∂T∂∂z = -S(z)/α = -β*sin(γ*z)/α
# ∂T∂z = β*cos(γ*z)/(γ*α)+c1
# T(z) = β*sin(γ*z)/(γ^2*α)+c1*z+c2 # generic solution
# Apply bc: ∂T∂z(a) = β*cos(γ*a)/(γ*α)+c1 = ∇T_bottom => c1 = ∇T_bottom-β*cos(γ*a)/(γ*α)
# Apply bc: T(b) =    β*sin(γ*b)/(γ^2*α)+c1*b+c2 = T_top =>
#         c2 = T_top-(β*sin(γ*b)/(γ^2*α)+c1*b)
function T_analytic_sin(z)
    c1 = ∇T_bottom-β*cos(γ*a)/(γ*α)
    c2 = T_top-(β*sin(γ*b)/(γ^2*α)+c1*b)
    return β*sin(γ*z)/(γ^2*α)+c1*z+c2
end

# Analytic steady state solution:
# ∂∂T∂∂z = -S(z)/α = -β/α
# ∂T∂z = -z*β/α+c1
# T(z) = -β*z^2/(2*α)+c1*z+c2 # generic solution
# Apply bc: ∂T∂z(a) = -a*β/α+c1 = ∇T_bottom => c1 = ∇T_bottom+a*β/α
# Apply bc: T(b) =    -β*b^2/(2*α)+c1*b+c2 = T_top =>
#         c2 = T_top-(-β*b^2/(2*α)+c1*b)
function T_analytic_uniform(z)
    c1 = ∇T_bottom+a*β/α
    c2 = T_top-(-β*b^2/(2*α)+c1*b)
    return -β*z^2/(2*α)+c1*z+c2
end

T_analytic(z) =
    use_sin_forcing ? T_analytic_sin(z) : T_analytic_uniform(z)

#####
##### T ∈ cell centers, ∇T ∈ cell faces
#####
println("Solve for T ∈ cell centers")
T = zeros(FT, n);

# Equations: derivation to matrix form
# ∂_t T = α * ∇²(T) + S
# (T^{n+1}-T^n) = Δt (α * ∇²(T^{n+1}) + S)
# (T^{n+1} - Δt α * ∇²(T^{n+1})) = T^n + Δt*S
# (I - Δt α * ∇²) T^{n+1} = T^n + Δt*S

# Derive Dirichlet boundary stencil & source:
# ∂_t T = α * (T[i-1]+T[g]-2*T[i])/Δz² + S
# ∂_t T = α * (T[i-1]+(2 T[b] - T[i])-2*T[i])/Δz² + S
# ∂_t T = α * (T[i-1]-3*T[i])/Δz² + S + α * (2 T[b])/Δz²

# Derive Neumann boundary stencil & source:
# ∇T_bottom*n̂ = (T[g] - T[i])/Δz,     n̂ = [-1,1] ∈ [zmin,zmax]
# ∇T_bottom*n̂ = -(T[i] - T[g])/Δz,    n̂ = [-1,1] ∈ [zmin,zmax]
# ∂_t T = α * ((T[ii] - T[i])/Δz - (T[i] - T[g])/Δz)/Δz + S
# ∂_t T = α * ((T[ii] - T[i])/Δz + ∇T_bottom*n̂)/Δz + S
# ∂_t T = α * (T[ii] - T[i])/Δz² + S + α*∇T_bottom*n̂/Δz

# Note that size(A) = (n,n) due to 1 Dirichlet and 1 Neumann.
∇² = Tridiagonal(
    ones(FT, n-1) .* ∇²_op[1],
    ones(FT, n)   .* ∇²_op[2],
    ones(FT, n-1) .* ∇²_op[3]
);
# Modify boundary stencil to account for BC
∇².d[1] = -1/Δz²
∇².d[end] = -3/Δz²

A = LinearAlgebra.I - Δt.* α .* ∇²

# Compute boundary sources: α * (2 T[b])/Δz², α * ∇T_bottom*n̂/Δz
AT_b = zeros(FT, n);
AT_b[1] = α*∇T_bottom*n̂_min/Δz;
AT_b[end] = α*2*T_top/Δz²;

# Set initial condition:
T .= T_init;
@inbounds for i_t in 1:N_t
    # change in temperature per iter:
    b_rhs = T .+ Δt .* (S.(zc) .+ AT_b)
    T_new = A \ b_rhs
    ΔT_norm = sum(T .- T_new)/length(T)
    @show ΔT_norm # watch ΔT norm
    T .= T_new
end

# Interpolate to, and plot on, cell faces
zf = range(a, b, length=n+1);
Tf = zeros(FT, n+1);
Tf[2:end-1] = [(T[i]+T[i+1])/2 for i in 1:length(T)-1]
# ∇T_bottom*n̂ = (T[g] - T[i])/Δz, T[g] = 2*T[b] - T[i]
# ∇T_bottom*n̂ = ((2*T[b] - T[i]) - T[i])/Δz
# Δz*∇T_bottom*n̂ = 2*T[b] - 2*T[i]
# (Δz*∇T_bottom*n̂ + 2*T[i])/2 = T[b]
Tf[1] = (Δz*∇T_bottom*n̂_min + 2*T[1])/2
Tf[end] = T_top
p1 = Plots.plot(zf, T_analytic.(zf), label="analytic", markershape=:circle, markersize=6)
p1 = Plots.plot!(p1, zf, Tf, label="numerical", markershape=:diamond)
p1 = Plots.plot!(p1, title="T ∈ cell centers")

p3 = Plots.plot(zf, abs.(Tf .- T_analytic.(zf)), label="error", markershape=:circle, markersize=6)
p3 = Plots.plot!(p3, title="T ∈ cell centers")

#####
##### T ∈ cell faces, ∇T ∈ cell centers
#####

println("Solve for T ∈ cell faces")
T = zeros(FT, n+1);

# Equations: derivation to matrix form
# ∂_t T = α * ∇²(T) + S
# (T^{n+1}-T^n) = Δt (α * ∇²(T^{n+1}) + S)
# (T^{n+1} - Δt α * ∇²(T^{n+1})) = T^n + Δt*S
# (I - Δt α * ∇²) T^{n+1} = T^n + Δt*S

# Derive Dirichlet boundary stencil & source:
# ∂_t T = α * (T[i-1]+T[b]-2*T[i])/Δz² + S
# ∂_t T = α * (T[i-1]-2*T[i])/Δz² + S + α * T[b] / Δz²

# Derive Neumann boundary stencil & source:
# ∇T_bottom*n̂= (T[g] - T[i])/(2Δz),     n̂ = [-1,1] ∈ [zmin,zmax]
# T[i] + 2*Δz*∇T_bottom*n̂ = T[g]
# ∂_t T = α * (((T[i] + 2*Δz*∇T_bottom*n̂) - T[b])/Δz - (T[b] - T[i])/Δz)/Δz + S
# ∂_t T = α * (((T[i]) - T[b])/Δz - (T[b] - T[i])/Δz)/Δz + S + α/Δz²*2*Δz*∇T_bottom*n̂
# ∂_t T = α * (2*T[i] - 2*T[b])/Δz² + S + 2α/Δz*∇T_bottom*n̂

# Note that size(A) = (n,n) due to 1 Dirichlet and 1 Neumann.
∇² = Tridiagonal(
    ones(FT, n) .* ∇²_op[1],
    ones(FT, n+1)   .* ∇²_op[2],
    ones(FT, n) .* ∇²_op[3]
);

# Modify boundary stencil to account for BCs
∇².d[1] = -2/Δz²
∇².du[1] = +2/Δz²

# Modify boundary stencil to account for BCs
∇².du[n] = 0  # modified stencil
∇².d[n+1] = 0 # to ensure `∂_t T = 0` at `z=zmax`
∇².dl[n] = 0  # to ensure `∂_t T = 0` at `z=zmax`

A = LinearAlgebra.I - Δt.* α .* ∇²

# Compute boundary source: α * T[b] / Δz²
AT_b = zeros(FT, n+1);
AT_b[1] = α*2/Δz*∇T_bottom*n̂_min;
AT_b[end-1] = α*T_top/Δz²;

# Set initial condition:
T .= T_init;
T[n+1] = T_top
@inbounds for i_t in 1:N_t
    interior = 1:n
    b_rhs = T .+ Δt .* (S.(zf) .+ AT_b)
    T_new = A \ b_rhs
    ΔT_norm = sum(T .- T_new)/length(T)
    @show ΔT_norm # watch ΔT norm
    T .= T_new
end

p2 = Plots.plot(zf, T_analytic.(zf), label="analytic", markershape=:circle, markersize=6)
p2 = Plots.plot!(p2, zf, T, label="numerical", markershape=:diamond)
p2 = Plots.plot!(p2, title="T ∈ cell faces")

p4 = Plots.plot(zf, abs.(T .- T_analytic.(zf)), label="error", markershape=:circle, markersize=6)
p4 = Plots.plot!(p4, title="T ∈ cell faces")

Plots.plot(p1, p3, p2, p4)


text:
# This file is a part of TextSearch.jl

export Tokenizer, tokenize, tokenize_corpus, qgrams, unigrams, encode, decode

"""
    struct Tokenizer

A tokenizer converts a text into a set of tokens, and in particular, each token
in this implementation is represented as the hash code of the corresponding string token.
This methods also normalize and preprocess the text following instructions in the given `TextConfig` object.
The structure has several fields:
- the text config object
- an inverse map `invmap` that need to be captured at construction time (only needed if the tokens need to be inspected).
- `isconstruction` indicator (must be true only when the tokens are being parsed for building a model)
- the rest of the fields are used as buffers (multithreaded applications need independent copies of tokenizers)

Note: non-thread safe, make a copy of this structure for each thread.
"""
struct Tokenizer
    config::TextConfig
    invmap::Union{Dict{UInt64,String},Nothing}
    isconstruction::Bool
    normtext::Vector{Char}
    tokens::Vector{UInt64}
    unigrams::Vector{String}
    io::IOBuffer
end

function Tokenizer(
        config::TextConfig;
        isconstruction=true,
        invmap=Dict{UInt64,String}(),
        n=128
    )
    normtext = Vector{Char}(undef, n)
    tokens = Vector{UInt64}(undef, n)
    unigrams = Vector{String}(undef, n)
    resize!(normtext, 0)
    resize!(tokens, 0)
    resize!(unigrams, 0)

    Tokenizer(config, invmap, isconstruction, normtext, tokens, unigrams, IOBuffer())
end

function Tokenizer(tok::Tokenizer; isconstruction=false, n=128)
    Tokenizer(tok.config, invmap=tok.invmap, isconstruction=isconstruction, n=n)
end

function Base.show(io::IO, tok::Tokenizer)
    print(io, "{Tokenizer isconstruction=$(tok.isconstruction) ")
    if tok.invmap === nothing
        print(io, "invmap=nothing")
    else
        print(io, "invmap=", length(tok.invmap))
    end
    print(io, " config=$(tok.config)\n}")
end

Base.broadcastable(m::Tokenizer) = (m,)

function encode(tok::Tokenizer, token::AbstractString)
    h = hash(token)
    tok.isconstruction && tok.invmap !== nothing && (tok.invmap[h] = token)
    h
end

function decode(tok::Tokenizer, id::UInt64)
    tok.invmap === nothing ? nothing : tok.invmap[id]
end

function decode(tok::Tokenizer, vec::Dict{UInt64,S}) where S
    tok.invmap === nothing ? nothing : Dict(tok.invmap[k] => v for (k,v) in vec)
end

function Base.empty!(tok::Tokenizer)
    empty!(tok.normtext)
    empty!(tok.tokens)
    empty!(tok.unigrams)
end

#=function tokenize__(config::TextConfig, textlist::AbstractVector, tok::Tokenizer=Tokenizer())
    n = length(textlist) * length(first(textlist))

    for text in textlist
        empty!(tok)
        if text isa AbstractString
            normalize_text(config, text, tok.normtext)
            tokenize_(tok)
        else
            tokenize(tok, text)
        end
    end

    tok.tokens
end=#

"""
    tokenize_corpus(tok::Tokenizer, arr)

Tokenize a list of texts.
"""
function tokenize_corpus(tok::Tokenizer, arr)
    [copy(tokenize(tok, text)) for text in arr]
end

"""
    tokenize(tok::Tokenizer, text::AbstractString)

Tokenizes `text` using the given configuration
"""
function tokenize(tok::Tokenizer, text::AbstractString)
    #@info text
    empty!(tok)
    normalize_text(tok.config, text, tok.normtext)
    tokenize_(tok)
end

function tokenize_(tok::Tokenizer)
    config = tok.config
    for q in config.qlist
        qgrams(tok, q)
    end

    if length(config.nlist) > 0 || length(config.slist) > 0
        n1 = length(tok.tokens)
        unigrams(tok)  # unigrams are always activated if any |nlist| > 0 or |slist| > 0

        if length(config.nlist) == 0 || config.nlist[1] != 1 # always sorted
            resize!(tok.tokens, n1)
        end

        for q in config.nlist
            q != 1 && nwords(tok, q)
        end

        for q in config.slist
            skipgrams(tok, q)
        end
    end

    tok.tokens
end

"""
    flush_token!(tok::Tokenizer)

Pushes the word inside buffer into token list; it discards empty strings.
"""
function flush_token!(tok::Tokenizer)
    io = tok.io
    if io.size > 0
        s = String(take!(io))
        push!(tok.tokens, encode(tok, s))
        s
    else
        nothing
    end
end

"""
    qgrams(tok::Tokenizer, q::Integer)

Computes character q-grams for the given input
"""
function qgrams(tok::Tokenizer, q::Integer)
    n = length(tok.normtext)

    for i in 1:(n - q + 1)
        write(tok.io, '\t', 'q')
        for j in i:i+q-1
            @inbounds write(tok.io, tok.normtext[j])
        end
        flush_token!(tok)
    end

    tok.tokens
end

"""
    unigrams(tok::Tokenizer, dropunigrams::Bool)

Performs the word tokenization
"""
function unigrams(tok::Tokenizer)
    n = length(tok.normtext)
    # @info tok.normtext
    @inbounds for i in 2:n  # normtext[1] is BLANK
        c = tok.normtext[i]
        p = tok.normtext[i-1]

        ## @show i, p, c
        if ispunct(c) && !ispunct(p) && p !== BLANK
            ## @show :a
            s = flush_token!(tok)
            s !== nothing && push!(tok.unigrams, s)
            write(tok.io, c)
        elseif ispunct(p) && !ispunct(c) && !(p in ('#', '@', '_'))
            ## @show :b
            s = flush_token!(tok)
            s !== nothing && push!(tok.unigrams, s)
            c !== BLANK && write(tok.io, c)
        elseif isemoji(c)
            s = flush_token!(tok)
            s !== nothing && push!(tok.unigrams, s)
            write(tok.io, c)
            s = flush_token!(tok)
            s !== nothing && push!(tok.unigrams, s)
        elseif c == BLANK
            if p !== BLANK
                s = flush_token!(tok)
                #write(tok.io, c)
                s !== nothing && push!(tok.unigrams, s)
            end
        else
            ## @show :d
            write(tok.io, c)
        end
    end

    s = flush_token!(tok)
    s !== nothing && push!(tok.unigrams, s)
    tok.tokens
end

"""
    nwords(tok::Tokenizer, q::Integer)
"""
function nwords(tok::Tokenizer, q::Integer)
    n = length(tok.unigrams)

    @inbounds for i in 1:(n - q + 1)
        _last = i + q - 1
        write(tok.io, '\t', 'n')
        for j in i:_last-1
            write(tok.io, tok.unigrams[j])
            write(tok.io, BLANK)
        end

        write(tok.io, tok.unigrams[_last])
        flush_token!(tok)
    end

    tok.tokens
end

"""
    skipgrams(tok::Tokenizer, q::Skipgram)

Tokenizes using skipgrams
"""
function skipgrams(tok::Tokenizer, q::Skipgram)
    n = length(tok.unigrams)

    for start in 1:(n - (q.qsize + (q.qsize - 1) * q.skip) + 1)
        write(tok.io, '\t', 's')
        if q.qsize == 2
            write(tok.io, tok.unigrams[start])
            write(tok.io, BLANK)
            write(tok.io, tok.unigrams[start + 1 + q.skip])
        else
            ep = q.qsize - 2
            for i in 0:ep
                write(tok.io, tok.unigrams[start + i * (1+q.skip)])
                write(tok.io, BLANK)
            end
            ep += 1
            write(tok.io, tok.unigrams[start + ep * (1+q.skip)])
        end

        flush_token!(tok)
    end

    tok.tokens
end


text:
using Test
using Flux: flatten

@testset "helpers" begin
  @testset "flatten" begin
    x = randn(Float32, 10, 10, 3, 2)
    @test size(flatten(x)) == (300, 2)
  end

  @testset "normalise" begin
    x = randn(Float32, 3, 2, 2)
    @test Flux.normalise(x) == Flux.normalise(x; dims=3)
  end
end


text:
"""
    DeviceBuffer{T,A}
A memory address that refers to data of type `T` that is accessible from the GPU. It is the
on-device counterpart of `MtlDAdrv.MtlPtr`, additionally keeping track of the address space
`A` where the data resides (shared, global, constant, etc). This information is used to
provide optimized implementations of operations such as `unsafe_load` and `unsafe_store!.`
"""
DeviceBuffer

# constructors
DeviceBuffer{T,A}(x::Union{Int,UInt,DeviceBuffer,DevicePtr}) where {T,A<:AddressSpace} = Base.bitcast(DeviceBuffer{T,A}, x)
DeviceBuffer{T,A}(ptr::MtlBuffer{T})                         where {T,A<:AddressSpace} = Base.bitcast(DeviceBuffer{T,A}, handle(ptr))
DeviceBuffer{T}(ptr::MtlBuffer{T})                           where {T}                 = Base.bitcast(DeviceBuffer{T,AS.Device}, handle(ptr))
DeviceBuffer(ptr::MtlBuffer{T})                              where {T}                 = Base.bitcast(DeviceBuffer{T,AS.Device}, handle(ptr))

## conversions
Base.convert(::Type{DeviceBuffer{T,A}}, x::Union{Int,UInt}) where {T,A<:AddressSpace} = DeviceBuffer{T,A}(x)

# between host and device pointers
Base.convert(::Type{MtlBuffer{T}},      p::DeviceBuffer)  where {T}                   = MtlBuffer{T}(Base.bitcast(MTL.MTLBuffer, p))
Base.convert(::Type{DeviceBuffer{T,A}}, p::MtlBuffer)     where {T,A<:AddressSpace}   = Base.bitcast(DeviceBuffer{T,A}, handle(p))
Base.convert(::Type{DeviceBuffer{T}},   p::MtlBuffer)     where {T}                   = Base.bitcast(DeviceBuffer{T,AS.Generic}, handle(p))

# between CPU pointers, for the purpose of working with `ccall`
Base.unsafe_convert(::Type{MTL.MTLBuffer}, x::DeviceBuffer{T}) where {T} = reinterpret(MTL.MTLBuffer, x)
Base.unsafe_convert(::Type{MTL.MTLResource}, x::DeviceBuffer{T}) where {T} = reinterpret(MTL.MTLResource, x)

# between device pointers
Base.convert(::Type{<:DeviceBuffer}, p::DeviceBuffer)                         = throw(ArgumentError("cannot convert between incompatible device pointer types"))
Base.convert(::Type{DeviceBuffer{T,A}}, p::DeviceBuffer{T,A})   where {T,A}   = p
Base.unsafe_convert(::Type{DeviceBuffer{T,A}}, p::DeviceBuffer) where {T,A}   = Base.bitcast(DeviceBuffer{T,A}, p)
## identical addrspaces
Base.convert(::Type{DeviceBuffer{T,A}}, p::DeviceBuffer{U,A}) where {T,U,A} = Base.unsafe_convert(DeviceBuffer{T,A}, p)
## convert to & from generic
Base.convert(::Type{DeviceBuffer{T,AS.Generic}}, p::DeviceBuffer)               where {T}     = Base.unsafe_convert(DeviceBuffer{T,AS.Generic}, p)
Base.convert(::Type{DeviceBuffer{T,A}},          p::DeviceBuffer{U,AS.Generic}) where {T,U,A} = Base.unsafe_convert(DeviceBuffer{T,A}, p)
Base.convert(::Type{DeviceBuffer{T,AS.Generic}}, p::DeviceBuffer{T,AS.Generic}) where {T}     = p  # avoid ambiguities
## unspecified, preserve source addrspace
Base.convert(::Type{DeviceBuffer{T}}, p::DeviceBuffer{U,A}) where {T,U,A} = Base.unsafe_convert(DeviceBuffer{T,A}, p)

## memory operations

@generated function pointerref(p::DeviceBuffer{T,A}, i::Int, ::Val{align}) where {T,A,align}
    sizeof(T) == 0 && return T.instance
    eltyp = convert(LLVMType, T)

    T_int = convert(LLVMType, Int)
    T_ptr = convert(LLVMType, DevicePtr{T,A})

    T_actual_ptr = LLVM.PointerType(eltyp, convert(Int, A))

    # create a function
    param_types = [T_ptr, T_int]
    llvm_f, _ = create_function(eltyp, param_types)

    # generate IR
    Builder(JuliaContext()) do builder
        entry = BasicBlock(llvm_f, "entry", JuliaContext())
        position!(builder, entry)

        ptr = inttoptr!(builder, parameters(llvm_f)[1], T_actual_ptr)
        ptr = gep!(builder, ptr, [parameters(llvm_f)[2]])
        ld = load!(builder, ptr)

        if A != AS.Generic
            metadata(ld)[LLVM.MD_tbaa] = tbaa_addrspace(A)
        end
        alignment!(ld, align)

        ret!(builder, ld)
    end

    call_function(llvm_f, T, Tuple{DevicePtr{T,A}, Int}, :((p, Int(i-one(i)))))
end

@generated function pointerset(p::DeviceBuffer{T,A}, x::T, i::Int, ::Val{align}) where {T,A,align}
    sizeof(T) == 0 && return
    eltyp = convert(LLVMType, T)

    T_int = convert(LLVMType, Int)
    T_ptr = convert(LLVMType, DevicePtr{T,A})

    T_actual_ptr = LLVM.PointerType(eltyp, convert(Int, A))

    # create a function
    param_types = [T_ptr, eltyp, T_int]
    llvm_f, _ = create_function(LLVM.VoidType(JuliaContext()), param_types)

    # generate IR
    Builder(JuliaContext()) do builder
        entry = BasicBlock(llvm_f, "entry", JuliaContext())
        position!(builder, entry)

        ptr = inttoptr!(builder, parameters(llvm_f)[1], T_actual_ptr)
        ptr = gep!(builder, ptr, [parameters(llvm_f)[3]])
        val = parameters(llvm_f)[2]
        st = store!(builder, val, ptr)

        if A != AS.Generic
            metadata(st)[LLVM.MD_tbaa] = tbaa_addrspace(A)
        end
        alignment!(st, align)

        ret!(builder)
    end

    call_function(llvm_f, Cvoid, Tuple{DevicePtr{T,A}, T, Int},
                  :((p, convert(T,x), Int(i-one(i)))))
end

## new set methods
MTL.set_buffer!(cce::MtlArgumentEncoder, buf::DeviceBuffer, offset::Integer, index::Integer) =
    MTL.mtArgumentEncoderSetBufferOffsetAtIndex(cce, buf, offset, index-1)
MTL.set_buffers!(cce::MtlArgumentEncoder, bufs::Vector{<:DeviceBuffer},
             offsets::Vector{Int}, indices::UnitRange{Int}) =
    MTL.mtArgumentSetBuffersOffsetsWithRange(cce, handle_array(bufs), offsets, indices .- 1)

MTL.use!(cce::MtlComputeCommandEncoder, buf::DeviceBuffer, mode::MTL.MtResourceUsage=ReadWriteUsage) =
    MTL.mtComputeCommandEncoderUseResourceUsage(cce, buf, mode)

MTL.use!(cce::MtlComputeCommandEncoder, buf::Vector{DeviceBuffer}, mode::MTL.MtResourceUsage=ReadWriteUsage) =
    MTL.mtComputeCommandEncoderUseResourceCountUsage(cce, handle_array(buf), length(buf), mode)