text:
module SigFigs
    using Base: +, -, /, * , %, transpose, inv
    mutable struct SigFig
        value::Real
        figures::Int
    end

    Base.:(==)( a::SigFig, b::SigFig )::Bool = ( a.value == b.value ) && ( a.figures == b.figures )

    significantfigures(a::SigFig) = round(a.value, sigdigits = a.figures)

    SigFig( values::AbstractArray{Real}, figures::UInt )::AbstractArray{SigFig} = [ SigFig(v, figures) for v in values ]
    vals( sfs::AbstractArray{SigFig} ) = map( sf -> sf.value, sfs )
    figs( sfs::AbstractArray{SigFig} ) = map( sf -> sf.figure, sfs )

    for s in (:+, :-, :/, :*, :^, :%)
        @eval begin
            Base.$s(sf::SigFig, value::Real)::SigFig = SigFig( Base.$s(sf.value, value), sf.figures )
            Base.$s(sf::SigFig, sf2::SigFig)::SigFig = SigFig( Base.$s(sf.value, sf2.value), min( sf.figures, sf2.figures ) )
            Base.$s(value::Real, sf::SigFig)::SigFig = SigFig( Base.$s(value,sf.value), sf.figures )
        end
    end

    #1-1 fns are just 1-1 fns
    for s in (:cos, :sin, :tan, :tanh, :cosh, :sinh, :atan, :acos, :asin, :log, :exp, :log10, :sqrt)
        @eval Base.$s(sf::SigFig)::SigFig = SigFig( Base.$s(sf.value), sf.figures )
    end

    #not optimized but OKAY
    Base.transpose(a::AbstractArray{SigFig})::AbstractArray{SigFig} = permutedims( a )
    Base.transpose(a::SigFig) = a

    #Think this one is true? Should handcheck it...
    #Base.inv(a::AbstractArray{SigFig})::AbstractArray{SigFig} =  ( Base.inv( vals( a ) ), reduce(min, figs( a )) )

    export SigFig, vals, figs, signficantfigures
end # module

ext: jl


text:
using ManifoldML, Manifolds, ManifoldsBase, Test
using Random

max_stars_repo_name: JuliaManifolds/ManifoldML

max_issues_repo_name: JuliaManifolds/ManifoldML

max_forks_repo_name: JuliaManifolds/ManifoldML


text:
using Test, MusicManipulations

let

rnotes = randomnotes(1000)
coun = Int[]
pit = pitches(rnotes)
pitchdict = Dict(pitch => count(x->x == pitch, pit) for pitch in pit)

@testset "allowed pitches" begin

    allowed = unique(pit)[1:2]
    anotes = filterpitches(rnotes, allowed)
    @test length(anotes) > 0
    @test length(anotes) == pitchdict[allowed[1]] + pitchdict[allowed[2]]
    for note ∈ anotes
        @test note.pitch ∈ allowed
    end

end
@testset "separate pitches" begin
    sep = separatepitches(rnotes)
    @test sort(collect(keys(sep))) == sort(unique(pitches(rnotes)))

    @test sum(length(n) for n in values(sep)) == length(rnotes)

end
end

@testset "estimate delay" begin
    for midi in (readMIDIFile("serenade_full.mid"), readMIDIFile(testmidi()))
        piano = getnotes(midi, 4)

        d = estimate_delay(piano, 0:1//3:1)
        @test abs(d) < 30

        d2 = estimate_delay_recursive(piano, 0:1//3:1, 5)

        @test d2 ≥ round(Int, d)
        @test abs(d2) < 30
    end
end


text:
function gen_upper_mul_quote(T, N, N2)
    q, qa = initial_quote(N2)
    for i ∈ 1:N2
        push!(qa, :($(Symbol(:B_, i)) = B.data[$i]))
    end
    for i ∈ 1:N
        lti = small_triangle(i)
        for j ∈ 1:i
            C_i = Symbol(:C_, lti + j)
            B_i = Symbol(:B_, lti + j)
            A_i = Symbol(:A_, small_triangle(j) + j)
            push!(qa, :($C_i = $A_i * $B_i) )
            for k ∈ j+1:i
                A_i = Symbol(:A_, small_triangle(k) + j)
                B_i = Symbol(:B_, lti + k)
                push!(qa, :($C_i += $A_i * $B_i) )
            end
        end
    end
    push!(q.args, :($T( ( @ntuple $N2 C ) )))
    q
end

@generated function Base.:*(A::UpperTriangularMatrix{T,N,N2}, B::UpperTriangularMatrix{T,N,N2}) where {T,N,N2}
    gen_upper_mul_quote(UpperTriangularMatrix{T,N,N2}, N, N2)
end

function gen_lower_mul_quote(T, N, N2)
    q, qa = initial_quote(N2)
    for i ∈ 1:N2
        push!(qa, :($(Symbol(:B_, i)) = B.data[$i]))
    end
    for i ∈ 1:N
        lti = small_triangle(i)
        for j ∈ i:N
            ltj = small_triangle(j)
            C_i = Symbol(:C_, ltj + i)
            B_i = Symbol(:B_, lti + i)
            A_i = Symbol(:A_, ltj + i)
            push!(qa, :($C_i = $A_i * $B_i) )
            for k ∈ i+1:j
                A_i = Symbol(:A_, ltj + k)
                B_i = Symbol(:B_, small_triangle(k) + i)
                push!(qa, :($C_i += $A_i * $B_i) )
            end
        end
    end
    push!(q.args, :($T( ( @ntuple $N2 C ) )))
    q
end

@generated function Base.:*(A::LowerTriangularMatrix{T,N,N2}, B::LowerTriangularMatrix{T,N,N2}) where {T,N,N2}
    gen_lower_mul_quote(LowerTriangularMatrix{T,N,N2}, N, N2)
end

function gen_upper_xxt_quote!(qa,T,N,L, A = :A, B = :B, extract = id_symbol)
    for i ∈ 1:N
        lti = small_triangle(i)
        for j ∈ 1:i
            B_i = extract(B, lti + j)
            A1_i = extract(A, lti + j)
            A2_i = extract(A, lti + i)
            push!(qa, :($B_i = $A1_i * $A2_i) )
            for k ∈ i+1:N
                ltk = small_triangle(k)
                A1_i = extract(A, ltk + j)
                A2_i = extract(A, ltk + i)
                push!(qa, :($B_i += $A1_i * $A2_i) )
            end
        end
    end
end

function gen_upper_xtx_quote!(qa,T,N,L)
    for i ∈ 1:N
        lti = small_triangle(i)
        for j ∈ 1:i
            ltj = small_triangle(j)
            B_i = Symbol(:B_, lti + j)
            A1_i = Symbol(:A_, ltj + 1)
            A2_i = Symbol(:A_, lti + 1)
            push!(qa, :($B_i = $A1_i * $A2_i) )
            for k ∈ 2:j
                A1_i = Symbol(:A_, ltj + k)
                A2_i = Symbol(:A_, lti + k)
                push!(qa, :($B_i += $A1_i * $A2_i) )
            end
        end
    end
end

function gen_xxt_quote!(qa,T,M,N,L)
    N2 = big_triangle(M) #number of elements in output
    q, qa = initial_quote(M*N)
    for i ∈ 1:M
        lti = small_triangle(i)
        for j ∈ 1:i
            B_i = Symbol(:B_, lti + j)
            A1_i = Symbol(:A_, j)
            A2_i = Symbol(:A_, i)
            push!(qa, :($B_i = $A1_i * $A2_i) )
            for k ∈ 1:N-1
                Nk = M*k
                A1_i = Symbol(:A_, Nk + j)
                A2_i = Symbol(:A_, Nk + i)
                push!(qa, :($B_i += $A1_i * $A2_i) )
            end
        end
    end
end
function gen_xtx_quote!(qa,T,M,N,L)
    for i ∈ 1:N
        offseti = (i-1)*M
        lti = small_triangle(i)
        for j ∈ 1:i
            offsetj = (j-1)*M
            B_i = Symbol(:B_, lti + j)
            A1_i = Symbol(:A_, offseti + 1)
            A2_i = Symbol(:A_, offsetj + 1)
            push!(qa, :($B_i = $A1_i * $A2_i) )
            for k ∈ 2:M
                A1_i = Symbol(:A_, offseti + k)
                A2_i = Symbol(:A_, offsetj + k)
                push!(qa, :($B_i += $A1_i * $A2_i) )
            end
        end
    end
end

@generated function xxt(A::UpperTriangularMatrix{T,N,L}) where {T,N,L}
    q, qa = initial_quote(L)
    gen_upper_xxt_quote!(qa,T,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$N,$L}( ( @ntuple $L B ) )))
    q
end
@generated function xtx(A::UpperTriangularMatrix{T,N,L}) where {T,N,L}
    q, qa = initial_quote(L)
    gen_upper_xtx_quote!(qa,T,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$N,$L}( ( @ntuple $L B ) )))
    q
end
@generated function xxt(A::Union{SMatrix{M,N,T},StaticRecursiveMatrix{T,M,N}}) where {T,M,N}
    q, qa = initial_quote(M*N)
    L = big_triangle(M)
    gen_xxt_quote!(qa,T,M,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$M,$L}( ( @ntuple $L B ) )))
    q
end
@generated function xtx(A::Union{SMatrix{M,N,T},StaticRecursiveMatrix{T,M,N}}) where {T,M,N}
    q, qa = initial_quote(M*N)
    L = big_triangle(N)
    gen_xtx_quote!(qa,T,M,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$N,$L}( ( @ntuple $L B ) )))
    q
end

xxt(A::LowerTriangularMatrix{T,N,N2}) where {T,N,N2} = xtx(UpperTriangularMatrix{T,N,N2}(A.data))
xtx(A::LowerTriangularMatrix{T,N,N2}) where {T,N,N2} = xxt(UpperTriangularMatrix{T,N,N2}(A.data))

@generated function xxt!(A::UpperTriangularMatrix{T,N,L}) where {T,N,L}
    q, qa = initial_quote(L)
    gen_upper_xxt_quote!(qa,T,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$N,$L}( ( @ntuple $L B ) )))
    q
end
@generated function xtx!(A::UpperTriangularMatrix{T,N,L}) where {T,N,L}
    q, qa = initial_quote(L)
    gen_upper_xtx_quote!(qa,T,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$N,$L}( ( @ntuple $L B ) )))
    q
end
@generated function xxt!(A::Union{MMatrix{M,N,T},RecursiveMatrix{T,M,N}}) where {T,M,N}
    q, qa = initial_quote(M*N)
    L = big_triangle(M)
    gen_xxt_quote!(qa,T,M,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$M,$L}( ( @ntuple $L B ) )))
    q
end
@generated function xtx!(A::Union{MMatrix{M,N,T},RecursiveMatrix{T,M,N}}) where {T,M,N}
    q, qa = initial_quote(M*N)
    L = big_triangle(N)
    gen_xtx_quote!(qa,T,M,N,L)
    push!(q.args, :(SymmetricMatrix{$T,$N,$L}( ( @ntuple $L B ) )))
    q
end

function chol2inv!()

end

# output is an N x N2 SMatrix
function AU_quote!(qa, N, N2)
    for i ∈ 1:N2
        lmi = (i-1)*N
        lti = small_triangle(i)
        for j ∈ 1:N
            C_i = Symbol(:C_, j + lmi)
            A_i = Symbol(:A_, j)
            U_i = Symbol(:U_, lti + 1)
            push!(qa, :($C_i = $A_i * $U_i) )
            for k ∈ 2:i
                A_i = Symbol(:A_, j + (k-1)*N)
                U_i = Symbol(:U_, lti + k)
                push!(qa, :($C_i += $A_i * $U_i) )
            end
        end
    end
end
function UA_quote!(qa, N, N2)
    for i ∈ 1:N2
        lmi = (i-1)*N
        # lti = small_triangle(i)
        for j ∈ 1:N
            # ltj = (j-1)*N
            C_i = Symbol(:C_, j + lmi)
            A_i = Symbol(:A_, j + lmi)
            U_i = Symbol(:U_, big_triangle(j)) # equivalent to j + small_triangle(j)
            push!(qa, :($C_i = $A_i * $U_i) )
            for k ∈ j+1:N
                A_i = Symbol(:A_, k + lmi)
                U_i = Symbol(:U_, j + small_triangle(k))
                push!(qa, :($C_i += $A_i * $U_i) )
            end
        end
    end
end

# Shoot, L is row-major storate.
# Must fix, or write row major extractor? #Writing for SMatrices anyway
function LU_quote!(qa, N)
    for i ∈ 1:N
        lmi = (i-1)*N
        lti = small_triangle(i)
        for j ∈ 1:N
            ltj = small_triangle(j)
            C_i = Symbol(:C_, j + lmi)
            L_i = Symbol(:L_, 1 + ltj)
            U_i = Symbol(:U_, 1 + lti)
            push!(qa, :($C_i = $L_i * $U_i) )
            for k ∈ 2:min(i,j)
                L_i = Symbol(:L_, k + ltj)
                U_i = Symbol(:U_, k + lti)
                push!(qa, :($C_i += $L_i * $U_i) )
            end
        end
    end
end
function UL_quote!(qa, N)
    for i ∈ 1:N
        lmi = (i-1)*N
        lti = small_triangle(i)
        for j ∈ 1:N
            k_start = max(i,j)
            ltk = small_triangle(k_start)
            C_i = Symbol(:C_, j + lmi)
            U_i = Symbol(:U_, ltk + j)
            L_i = Symbol(:L_, ltk + i)
            push!(qa, :($C_i = $U_i * $L_i) )
            for k ∈ k_start+1:N
                ltk = small_triangle(k)
                U_i = Symbol(:U_, ltk + j)
                L_i = Symbol(:L_, ltk + i)
                push!(qa, :($C_i += $U_i * $L_i) )
            end
        end
    end
end
@generated function Base.:*(U::UpperTriangularMatrix{T,N,N2}, L::LowerTriangularMatrix{T,N,N2}) where {T,N,N2}
    # println("Okay...")
    q, qa = initial_quote(N2, :U)
    extract_linear!(qa, N2, :L)
    UL_quote!(qa, N)
    push!(q.args, :( SMatrix{$N,$N}( @ntuple $(N*N) C ) ))
    q
end

function AS_quote!(qa, N, N2)
    for i ∈ 1:N
        lmi = (i-1)*N2
        lti = small_triangle(i)
        for j ∈ 1:N2
            C_i = Symbol(:C_, j + lmi)
            A_i = Symbol(:A_, j)
            S_i = Symbol(:S_, 1 + lti)
            push!(qa, :($C_i = $A_i * $S_i) )
            for k ∈ 2:i #we're in upper triangle of S
                A_i = Symbol(:A_, j + (k-1)*N2)
                S_i = Symbol(:S_, k + lti)
                push!(qa, :($C_i += $A_i * $S_i) )
            end
            for k ∈ 1+i:N
                #we're in mirrored lower triangle
                #thus, k is column, i is row
                A_i = Symbol(:A_, j + (k-1)*N2)
                S_i = Symbol(:S_, i + small_triangle(k))
                push!(qa, :($C_i += $A_i * $S_i) )
            end
        end
    end
end

function gen_AU_quote(N3, N, N2, T)
    Ntotal = N3*N
    q, qa = initial_quote(Ntotal, :A)
    extract_linear!(qa, N2, :U)
    AU_quote!(qa, N3, N)
    push!(q.args, :(StaticArrays.SArray{Tuple{$N3,$N},$T,2,$Ntotal}( ( @ntuple $Ntotal C ))))
    q
end
@generated function Base.:*(A::SMatrix{N3,N,S}, U::UpperTriangularMatrix{T,N,N2}) where {S,T,N,N2,N3}
    gen_AU_quote(N3, N, N2, promote_type(S,T))
end
function gen_LA_quote(N3, N, N2, T)
    Ntotal = N3*N
    q, qa = initial_quote(Ntotal, :L)
    extract_transpose!(qa, N3, N, :A)
    AU_quote!(qa, N3, N)
    push!(q.args, output_transpose(N,N3,T,:C))
    q
end
@generated function Base.:*(L::LowerTriangularMatrix{T,N,N2}, A::SMatrix{N3,N,S}) where {S,T,N,N2,N3}
    gen_LA_quote(N3, N, N2, promote_type(S,T))
end

function gen_UA_quote(N3::Int, N::Int, N2::Int, ::Type{T}) where T
    Ntotal = N3*N
    q, qa = initial_quote(Ntotal, :A)
    extract_linear!(qa, N2, :U)
    UA_quote!(qa, N, N3)
    push!(q.args, :(StaticArrays.SArray{Tuple{$N,$N3},$T,2,$Ntotal}( ( @ntuple $Ntotal C ))))
    q
end
@generated function Base.:*(U::UpperTriangularMatrix{T,N,L}, A::SMatrix{N,L2,S}) where {S,T,N,L,L2}
    gen_UA_quote(L2, N, L, promote_type(S,T))
end

function gen_AL_quote(N3, N, N2, T)
    Ntotal = N3*N
    q, qa = initial_quote(N2, :L)
    extract_transpose!(qa, N3, N, :A)
    UA_quote!(qa, N, N3)
    push!(q.args, output_transpose(N3,N,T,:C))
    q
end
@generated function Base.:*(A::SMatrix{N3,N,S}, L::LowerTriangularMatrix{T,N,N2}) where {S,T,N,N2,N3}
    gen_AL_quote(N3, N, N2, promote_type(S,T))
end

function rank_downdate!()

end

# @generated function Base.:*(A::SMatrix{N3,N}, B::LowerTriangularMatrix{T,N,N2}) where {T,N,N2,N3}
#     gen_lower_mul_quote(LowerTriangularMatrix{T,N,N2}, N, N2)
# end
# @generated function Base.:*(A::LowerTriangularMatrix{T,N,N2}, B::SMatrix{N3,N}) where {T,N,N2,N3}
#     gen_lower_mul_quote(LowerTriangularMatrix{T,N,N2}, N, N2)
# end

function US_quote!()

end
function SU_quote!()

end
function USU_quote!()

end
function LSL_quote!()

end


text:
using Distributions
using Test

a = qqbuild(collect(1:10), collect(1:10))
b = qqbuild(1:10, 1:10)
c = qqbuild(view(collect(1:20), 1:10), view(collect(1:20), 1:10))
@test a.qx ≈ b.qx ≈ c.qx ≈ collect(1.0:10)
@test a.qy ≈ b.qy ≈ c.qy ≈ collect(1.0:10)

a = qqbuild(collect(1:10), Uniform(1,10))
b = qqbuild(1:10, Uniform(1,10))
c = qqbuild(view(collect(1:20), 1:10), Uniform(1,10))
@test a.qx ≈ b.qx ≈ c.qx ≈ collect(2.0:9)
@test a.qy ≈ b.qy ≈ c.qy ≈ collect(2.0:9)

a = qqbuild(Uniform(1,10), collect(1:10))
b = qqbuild(Uniform(1,10), 1:10)
c = qqbuild(Uniform(1,10), view(collect(1:20), 1:10))
@test a.qx ≈ b.qx ≈ c.qx ≈ collect(2.0:9)
@test a.qy ≈ b.qy ≈ c.qy ≈ collect(2.0:9)


text:
##################################################################################################################
#MMMMMMMMMMMMM0;,MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMo;  oM#
#MM0o:;,',:c;. :XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM' oM#
#k.    .lxx:  'WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM' oM#
#    ,KMMMMX   oMMMWlcoolcxWMM0dcoWOo;xMMxclol:dNMMXxlcOkl;,;dXOo:,,cKMMMMM0ocooclkWMMXxlc0kl;,;dWMMMMNxccloo. oM#
#   ;MMMMMK.   :MMMN,WMMM; :MMM0  xk0loMM:0MMMk .MMMM. lKXN0' .0XNXo  NMMW, oWMMMk .KMMM. oXNWK. :MMMx .KMMMM' oM#
#.  ;NWXk;     0MMMMMMWNNl 'MMM0  MMMMMMMMMWNN0  MMMM. kMMMMd .MMMMW  0MMl .MMMMMMc ,MMM. kMMMMo 'MMX  OMMMMM' oM#
#X:          ;KMMMWc.:oxx; 'MMM0  MMMMMMx',oxxo  MMMM. kMMMMd .MMMMW  0MM; 'MMMMMMo .MMM. kMMMMo 'MMO  XMMMMM' oM#
#MO.'OkxxkOXMMMMMMo ,MMMMo 'MMM0  MMMMMK  NMMMX  MMMM. kMMMMd .MMMMW  0MMd .MMMMMM; :MMM. kMMMMo 'MMX  kMMMMM' oM#
#'  kXNNNNNNNWMMMMk  xK0k; 'MMMO  WMMMMN. l00Oo  WMMM. xMMMMo .MMMMN  0MMMl ;KWMXl ,NMMM. kMMMMo 'MMMk  o0XKO. lM#
#;             'dWMKdloxKNxxkW0xxxxOWMMMNxoodOWkxkXXkxxxOWNkxxxkKM0xxxx0MMMWOdoodkXMMMNkxxxOMWOxxxkNMMWOdloxK0xxO#
#Nl.lxxxxxxxdl.  ;MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#
#  KMMMMMMMMMMO  .MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#
# .WMMMMMMMMMN,  kMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#
#. .lxkOOkxl' .lXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#
#MXl'.    .,oXMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM#
##################################################################################################################
#
# ~Garamond~ - Search engine written at 0x0α Research by Corneliu Cofaru, 2018, 2019.
#
module Garamond

    using Unicode
    using Random
    using Logging
    using Dates
    using DelimitedFiles
    using Sockets
    using LinearAlgebra
    using Statistics
    using Serialization
    using PooledArrays
    using SparseArrays
    using QuantizedArrays
    using DataStructures
    using Languages
    using StringAnalysis
    using StringDistances
    using BKTrees
    using ArgParse
    using ProgressMeter
    using ConceptnetNumberbatch
    using Word2Vec
    using Glowe
    using EmbeddingsAnalysis
    using HNSW
    using NearestNeighbors
    using IVFADC
    using Distances
    using LightGraphs: Graph, pagerank
    using JSON
    using HTTP
    using TSVD
    using JuliaDB

    import Base: size, length, show, keys, values,
                 delete!, getindex, names, convert, lowercase,
                 occursin, isempty, parse, sort
    import StringAnalysis: id
    import Word2Vec: WordVectors
    import HNSW: knn_search

    export
        search,
        recommend,
        rank,
        indexfilter,

        build_search_env,
        parse_configuration,
        parse_input,

        AbstractEmbedder,
        embed,
        embed!,

        AbstractIndex,
        NaiveIndex,
        BruteTreeIndex,
        KDTreeIndex,
        HNSWIndex,
        IVFIndex,
        NoopIndex,

        SearchEnv,
        AbstractSearcher,
        Searcher,
        SearchResult,

        id,
        description,
        isenabled, enable!, disable!,
        print_search_results,
        search_server,
        unix_socket_server,
        web_socket_server,
        rest_server

    #=
    The __init__() function includes at runtime all the .jl files located
    at data/loaders/custom; the files should be either code or symlinks to
    files containing data loading functions that take data paths as input
    argument and return IdexedTable/NDSparse datasets representing the data
    to be indexed.
    =#
    function __init__()
        CUSTOM_LOADERS_SUBDIR = "data/loaders/custom"
        CUSTOM_SAMPLERS_SUBDIR = "data/samplers/custom"
        CUSTOM_RANKERS_SUBDIR = "search/rankers/custom"
        CUSTOM_RECOMMENDERS_SUBDIR = "search/recommenders/custom"
        CUSTOM_INPUT_SUBDIR = "input/custom"

        __include_subdirectory(CUSTOM_LOADERS_SUBDIR, printer="Loaders (custom)")
        __include_subdirectory(CUSTOM_SAMPLERS_SUBDIR, printer="Samplers (custom)")
        __include_subdirectory(CUSTOM_RANKERS_SUBDIR, printer="Rankers (custom)")
        __include_subdirectory(CUSTOM_RECOMMENDERS_SUBDIR, printer="Recommenders (custom)")
        __include_subdirectory(CUSTOM_INPUT_SUBDIR, printer="Parsers (custom)")
    end

    function __include_subdirectory(subpath; printer="Including")
        fullpath = joinpath(@__DIR__, subpath)
        if isdir(fullpath)
            included_files = String[]
            for file in readdir(fullpath)
                local filepath
                try
                    filepath = joinpath(fullpath, file)
                    if isfile(filepath) && endswith(filepath, ".jl")
                        include(filepath)
                        push!(included_files, file)
                    end
                catch e
                    @warn "Could not include \"$filepath\".\n$e"
                end
            end

            !isempty(included_files) &&
                @info "• " * printer * ": " * join(included_files, ", ")
        end
    end

    include("data/db.jl")
    include("data/text.jl")
    include("data/parse_and_eval.jl")
    include("data/loaders/noop.jl")
    include("data/loaders/juliadb.jl")

    include("data/samplers/noop.jl")
    include("data/samplers/identity.jl")

    include("config/defaults.jl")
    include("config/engine.jl")

    include("embedder/interface.jl")
    include("embedder/wordvectors.jl")
    include("embedder/boe.jl")
    include("embedder/sif.jl")
    include("embedder/borep.jl")
    include("embedder/cpmean.jl")
    include("embedder/disc.jl")
    include("embedder/dtv.jl")

    include("index/interface.jl")
    include("index/naive.jl")
    include("index/brutetree.jl")
    include("index/kdtree.jl")
    include("index/hnsw.jl")
    include("index/ivfadc.jl")
    include("index/noop.jl")

    include("searchable/config_parser.jl")
    include("searchable/searcher.jl")
    include("searchable/env.jl")
    include("searchable/env_operations.jl")

    include("input/text_parsers.jl")

    include("search/index.jl")
    include("search/filter.jl")
    include("search/results.jl")
    include("search/recommend.jl")
    include("search/recommenders/search.jl")
    include("search/recommenders/noop.jl")
    include("search/rank.jl")
    include("search/rankers/noop.jl")
    include("search/main.jl")

    include("server/requests.jl")
    include("server/unixsocket.jl")
    include("server/websocket.jl")
    include("server/rest.jl")
    include("server/search.jl")

    include("utils/logging.jl")
    include("utils/show.jl")
    include("utils/version.jl")

end # module


text:
# old type interfaces

# function Point2DPoint2DRange(mu,stdev,w)
#   @warn "Point2DPoint2DRange deprecated in favor of Point2Point2Range{<:IIF.SamplableBelief}."
#   Point2Point2Range{Normal}(Normal(mu,stdev))
# end
# function Point2DPoint2D(d::D) where {D <: IIF.SamplableBelief}
#   @warn "Point2DPoint2D deprecated in favor of Point2Point2{<:Distribution}."
#   Point2Point2{D}(d)
# end
# function Point2DPoint2D(mu::Array{Float64}, cov::Array{Float64,2}, W::Array{Float64,1})
#   @warn "Point2DPoint2D deprecated in favor of Point2Point2{<:Distribution}."
#
#   Point2Point2{MvNormal}(MvNormal(mu[:], cov))
# end
#
# function Pose2DPoint2DBearing(x1::B) where {B <: Distributions.Distribution}
#   @warn "Pose2DPoint2DBearing deprecated in favor of Pose2Point2Bearing."
#   Pose2Point2Bearing(B)
# end
#
# function Pose2DPoint2DRange(x1::T,x2::Vector{T},x3) where {T <: Real}
#   @warn "Pose2Point2Range(mu,cov,w) is being deprecated in favor of Pose2Point2Range(T(...)), such as Pose2Point2Range(MvNormal(mu, cov))"
#   Pose2Point2Range(Normal(x1, x2))
# end

# old code below

# This has been moved to transform utils
# TODO Switch to using SE(2) oplus
# DX = [transx, transy, theta]
function addPose2Pose2!(retval::Array{Float64,1}, x::Array{Float64,1}, dx::Array{Float64,1})
  X = SE2(x)
  DX = SE2(dx)
  se2vee!(retval, X*DX)
  nothing
end
function addPose2Pose2(x::Array{Float64,1}, dx::Array{Float64,1})
    retval = zeros(3)
    addPose2Pose2!(retval, x, dx)
    return retval
end

function evalPotential(obs::PriorPose2, Xi::Array{Graphs.ExVertex,1}; N::Int=200)
    cov = diag(obs.Cov)
    ret = zeros(3,N)
    @warn "should not be running"
    for j in 1:N
      for i in 1:size(obs.Zi,1)
        ret[i,j] += obs.Zi[i,1] + (cov[i]*randn())
      end
    end
    return ret
end

function evalPotential(odom::Pose2Pose2, Xi::Array{Graphs.ExVertex,1}, Xid::Int; N::Int=100)
    rz,cz = size(odom.Zij)
    Xval = Array{Float64,2}()
    XvalNull = Array{Float64,2}()
    @warn "should not be running"
    # implicit equation portion -- bi-directional pairwise function
    if Xid == Xi[1].index #odom.
        #Z = (odom.Zij\Matrix{Float64}(LinearAlgebra.I, rz,rz)) # this will be used for group operations
        Z = se2vee(SE2(vec(odom.Zij)) \ Matrix{Float64}(LinearAlgebra.I, 3,3))
        Xval = getVal(Xi[2])
        XvalNull = getVal(Xi[1])
    elseif Xid == Xi[2].index
        Z = odom.Zij
        Xval = getVal(Xi[1])
        XvalNull = getVal(Xi[2])
    else
        error("Bad evalPairwise Pose2Pose2")
    end

    r,c = size(Xval)
    RES = zeros(r,c*cz)

    # TODO -- this should be the covariate error from Distributions, only using diagonals here (approxmition for speed in first implementation)
    # dd = size(Z,1) == r
    ENT = randn(r,c)
    HYP = rand(Categorical(odom.W),c) # TODO consolidate
    HYP -= length(odom.W)>1 ? 1 : 0
    for d in 1:r
       @fastmath @inbounds ENT[d,:] = ENT[d,:].*odom.Cov[d,d]
    end
    # Repeat ENT values for new modes from meas
    for j in 1:cz
      for i in 1:c
        if HYP[i]==1 # TODO consolidate hypotheses on Categorical
          z = Z[1:r,j].+ENT[1:r,i]
          RES[1:r,i*j] = addPose2Pose2(Xval[1:r,i], z )
        else
          RES[1:r,i*j] = XvalNull[1:r,i]
        end
      end
    end

    return RES
end

# to be deprecated
function pack3(xL1, xL2, p1, p2, p3, xF3)
    error("RoME.BearingRange2D:pack3 to be deprecated")
    X = zeros(3)
    X[p1] = xL1
    X[p2] = xL2
    X[p3] = xF3
    return X
end

function bearrang!(residual::Array{Float64,1}, Z::Array{Float64,1}, X::Array{Float64,1}, L::Array{Float64,1})
  @warn "bearrang! is deprecated"
  wTb = SE2(X)
  bTl = wTb\[L[1:2];1.0]
  b = atan(bTl[2],bTl[1])
  residual[1] = Z[2]-norm(bTl[1:2])
  residual[2] = Z[1]-b
  nothing
end

# should be collapsed to use only numericRootGenericRandomizedFnc
function solveLandm(Zbr::Array{Float64,1}, par::Array{Float64,1}, init::Array{Float64,1})
    return numericRoot(bearrang!, Zbr, par, init)
    # return (nlsolve(   (l, res) -> bearrang!(res, Zbr, par, l), init )).zero
end

# old numeric residual function for pose 2 to pose 2 constraint function.
function solvePose2(Zbr::Array{Float64,1}, par::Array{Float64,1}, init::Array{Float64,1})
    # TODO -- rework to ominus oplus and residual type method
    error("solvePose2 is deprecated")
    p = collect(1:3);
    shuffle!(p);
    p1 = p.==1; p2 = p.==2; p3 = p.==3
    #@show init, par
    r = nlsolve(    (res, x) -> bearrang!(res, Zbr,  pack3(x[1], x[2], p1, p2, p3, init[p3]), par),
                    [init[p1];init[p2]] )
    return pack3(r.zero[1], r.zero[2], p1, p2, p3, init[p3]);
end

function solveSetSeps(fnc::Function, Zbr::Array{Float64,1}, CovZ::Array{Float64,2},
                      pars::Array{Float64,2}, inits::Array{Float64,2})
    error("solveSetSeps is deprecated")
    out = zeros(size(inits))
    for i in 1:size(pars,2)
        ent = 1.0*[CovZ[1,1]*randn(); CovZ[2,2]*randn()]
        out[:,i] = fnc((Zbr+ent), vec(pars[:,i]), vec(inits[:,i]) )
    end
    return out
end

# Xid is the one you want to get back
function evalPotential(brpho::Pose2Point2BearingRange, Xi::Array{Graphs.ExVertex,1}, Xid::Int; N::Int=100)
    # TODO -- add null hypothesis here, might even be done one layer higher in call stack
    error("evalPotential(brpho::Pose2Point2BearingRange,...) should not be here anymore")
    val = Array{Float64,2}()
    ini = Array{Graphs.ExVertex,1}()
    par = Array{Graphs.ExVertex,1}()
    oth = Array{Graphs.ExVertex,1}()
    ff::Function = +
    nullhyp = 0.0
    # mmodes = 1 < length(brpho.W)
    # implicit equation portion -- multi-dependent function
    if Xid == Xi[1].index # brpho. ## find the pose

        ff = solvePose2
        # ini = brpho.Xi[1]
        par = Xi[2:end]
        for j in 1:(length(Xi)-1)
            push!(ini, Xi[1])
        end
        #println("Xid == brpho.Xi[1].index inits=", size(inits), ", par=", size(pars))
    elseif Xid == Xi[2].index # find landmark
        if length(Xi) > 2
            nullhyp = 0.5 # should be variable weight
            oth = Xi[3]
        end
        ff = solveLandm
        ini = Xi[2]
        par = Xi[1]
    elseif length(Xi) > 2
        nullhyp = 0.5 # should be variable weight
        if Xid == Xi[3].index # find second mode landmark
            ff = solveLandm
            ini = Xi[3]
            oth = Xi[2]
            par = Xi[1]
        end
    end
    if ff == +
        error("Bad evalPotential Pose2Point2DBearingRange")
    end

    # Gamma = Categorical(brpho.W)

    inits = getVal(ini)
    pars = getVal(par)
    others =  length(Xi) > 2 && Xid != Xi[1].index ? getVal(oth) : Union{}
    # add null hypothesis case
    len = length(Xi) > 2 && Xid != Xi[1].index ? size(others,2) : 0
    # gamma = mmodes ? rand(Gamma) : 1
    numnh = floor(Int, 2*nullhyp*len) # this doubles the value count for null cases
    nhvals = zeros(size(inits,1),numnh)
    for i in 1:numnh
        idx = floor(Int,len*rand()+1)
        # nhvals[:,i] = inits[:,idx] # WRONG! this should be the other landmark, not current landmark
        nhvals[:,i] = others[:,idx]
    end

    val = solveSetSeps(ff, vec(brpho.Zij[:,1]), brpho.Cov, pars, inits)

    return [val';nhvals']'
end

function evalPotentialNew(brpho::Pose2Point2BearingRange, Xi::Array{Graphs.ExVertex,1}, Xid::Int; N::Int=100)
    # TODO -- add null hypothesis here, might even be done one layer higher in call stack
    error("deprecated")
    val = Array{Float64,2}()
    ini = Array{Graphs.ExVertex,1}()
    par = Array{Graphs.ExVertex,1}()
    ff::Function = +
    mmodes = 1 < length(brpho.W)
    x = getVal(brpho.Xi[1])
    l1 = getVal(brpho.Xi[2])
    l2 = mmodes ? getVal(brpho.Xi[3]) : nothing
    nPts = size(x,2)

    pars = Array{Float64,2}(undef,size(x))

    # discrete option, marginalized out before message is sent
    Gamma = mmodes ? rand(Categorical(brpho.W),nPts) : ones(Int,nPts)
    L1s = Gamma .== 1
    nl1s = sum(map(Int, L1s))
    nl2s = nPts-nl1s
    # implicit equation portion -- multi-dependent function

    if Xid == brpho.Xi[1].index # find the pose
        ff = solvePose2
        # par = brpho.Xi[2:end]
        push!(ini, brpho.Xi[1])
        for j in 1:nPts
            pars[:,j] = L1s[j] ? l1[:,j] : l2[:,j]
        end
    elseif Xid == brpho.Xi[2].index # find landmark
        ff = solveLandm
        pars = x
        push!(ini,brpho.Xi[2])
    elseif mmodes
        if Xid == brpho.Xi[3].index # find second mode landmark
            ff = solveLandm
            push!(ini,brpho.Xi[3])
            pars = x
        end
    end
    if ff == +
        error("Bad evalPotential Pose2Point2DBearingRange")
    end

    inits = getVal(ini)

    L1 = sample(getKDE(brpho.Xi[2]),numsampls) #?? # TODO you where here

    len = size(inits,2)
    numnh = floor(Int, 2*nullhyp*len) # this doubles the value count for null cases
    nhvals = zeros(size(inits,1),numnh)
    for i in 1:numnh
        idx = floor(Int,len*rand()+1)
        nhvals[:,i] = inits[:,idx]
    end

    val = solveSetSeps(ff, vec(brpho.Zij[:,1]), brpho.Cov, pars, inits)

    return [val';nhvals']'
end

# Solve for Xid, given values from vertices [Xi] and measurement rho
function evalPotential(rho::Pose2Point2Range, Xi::Array{Graphs.ExVertex,1}, Xid::Int; N::Int=100)
  error("deprecated")
  fromX, ret = nothing, nothing
  if Xi[1].index == Xid
    fromX = getVal( Xi[2] )
    ret = deepcopy(getVal( Xi[1] )) # carry pose yaw row over if required
    ret[3,:] = 2*pi*rand(size(fromX,2))-pi
  elseif Xi[2].index == Xid
    fromX = getVal( Xi[1] )
    ret = deepcopy(getVal( Xi[2] )) # carry pose yaw row over if required
  end
  r,c = size(fromX)
  theta = 2*pi*rand(c)
  noisy = rho.Cov*randn(c) + rho.Zij[1]

  for i in 1:c
    ret[1,i] = noisy[i]*cos(theta[i]) + fromX[1,i]
    ret[2,i] = noisy[i]*sin(theta[i]) + fromX[2,i]
  end

  return ret
end

# should use new multihypo interface that is part of addFactor
# although use of hypothesis here might be good example for other inference situations

mutable struct Pose2Point2BearingRangeMH{B <: Distributions.Distribution, R <: Distributions.Distribution} <: IncrementalInference.FunctorPairwise
    bearing::B
    range::R
    hypothesis::Distributions.Categorical
    Pose2Point2BearingRangeMH{B,R}() where {B,R} = new{B,R}()
    Pose2Point2BearingRangeMH(x1::B,x2::R, w::Distributions.Categorical) where {B,R} = new{B,R}(x1,x2,w)
    Pose2Point2BearingRangeMH(x1::B,x2::R, w::Vector{Float64}=Float64[1.0;]) where {B,R} = new{B,R}(x1,x2,Categorical(w))
end
function getSample(pp2br::Pose2Point2BearingRangeMH, N::Int=1)::Tuple{Array{Float64,2}, Vector{Int}}
  b = rand(pp2br.bearing, N)
  r = rand(pp2br.range, N)
  s = rand(pp2br.hypothesis, N)
  return ([b';r'], s)
end
# define the conditional probability constraint
function (pp2br::Pose2Point2BearingRangeMH)(res::Array{Float64},
            userdata,
            idx::Int,
            meas::Tuple{Array{Float64,2}, Vector{Int}},
            xi::Array{Float64,2},
            lms... )::Nothing  # ::Array{Float64,2}
  #
  @warn "Older interface, not analytically correct."
  res[1] = lms[meas[2][idx]][1,idx] - (meas[1][2,idx]*cos(meas[1][1,idx]+xi[3,idx]) + xi[1,idx])
  res[2] = lms[meas[2][idx]][2,idx] - (meas[1][2,idx]*sin(meas[1][1,idx]+xi[3,idx]) + xi[2,idx])
  nothing
end

mutable struct PackedPose2Point2BearingRangeMH <: IncrementalInference.PackedInferenceType
    bearstr::String
    rangstr::String
    hypostr::String
    PackedPose2Point2BearingRangeMH() = new()
    PackedPose2Point2BearingRangeMH(s1::AS, s2::AS, s3::AS) where {AS <: AbstractString} = new(string(s1),string(s2),string(s3))
end
function convert(::Type{PackedPose2Point2BearingRangeMH}, d::Pose2Point2BearingRangeMH{Normal{T}, Normal{T}}) where T
  return PackedPose2Point2BearingRangeMH(string(d.bearing), string(d.range), string(d.hypothesis))
end
# TODO -- should not be resorting to string, consider specialized code for parametric distribution types
function convert(::Type{Pose2Point2BearingRangeMH}, d::PackedPose2Point2BearingRangeMH)
  Pose2Point2BearingRangeMH(extractdistribution(d.bearstr), extractdistribution(d.rangstr), extractdistribution(d.hypostr))
end

# mutable struct PackedPose2Point2BearingRangeDensity <: IncrementalInference.PackedInferenceType
#     bpts::Vector{Float64} # 0rotations, 1translation in each column
#     bbw::Vector{Float64}
#     rpts::Vector{Float64}
#     rbw::Vector{Float64}
#     PackedPose2Point2BearingRangeDensity() = new()
#     PackedPose2Point2BearingRangeDensity(x...) = new(x[1], x[2], x[3], x[4])
# end
# function convert(::Type{Pose2Point2BearingRangeDensity}, d::PackedPose2Point2BearingRangeDensity)
#   return Pose2Point2BearingRangeDensity(
#     kde!( EasyMessage(d.bpts',d.bbw)), kde!(EasyMessage(d.rpts', d.rbw) )
#   )
# end
# function convert(::Type{PackedPose2Point2BearingRangeDensity}, d::Pose2Point2BearingRangeDensity)
#   return PackedPose2Point2BearingRangeDensity(getPoints(d.bearing)[1,:], getBW(d.bearing)[:,1],
#                                                 getPoints(d.range)[1,:], getBW(d.range)[:,1] )
# end

function getNextLbl(fgl::AbstractDFG, chr)
  @warn "getNextLbl is deprecated, use nextPose, nextLabel, or getLastPoses instead."
  # TODO convert this to use a double lookup
  max = -1
  maxid = -1
  for vid in fgl.IDs
  # for v in fgl.v #fgl.g.vertices # fgl.v
      v = (vid[2], fgl.g.vertices[vid[2]])
      if v[2].attributes["label"][1] == chr
        # TODO test for allnums first, ex. :x1_2
        val = parse(Int,v[2].attributes["label"][2:end])
        if max < val
          max = val
          maxid = v[1]
        end
      end
  end
  if maxid != -1
    v = getVert(fgl,maxid)
    X = getVal(v)
    return v, X, Symbol(string(chr,max+1))
  else
    return nothing, nothing, Symbol(string(chr,max+1)) # Union{}
  end
end

function getLastPose(fgl::FactorGraph)
  return getNextLbl(fgl, 'x')
end
getLastPose2D(fgl::FactorGraph) = getLastPose(fgl)

function getlastpose(slam::SLAMWrapper)
  error("getlastpose -- Not implemented yet")
end

function getLastLandm2D(fgl::FactorGraph)
  return getNextLbl(fgl, 'l')
end

function basicFactorGraphExample(::Type{Pose2}=Pose2; addlandmark::Bool=true)
  @warn "basicFactorGraphExample is deprecated, use loadCanonicalFG_TwoPoseOdo instead"
  loadCanonicalFG_TwoPoseOdo(addlandmark=addlandmark)
end

# Project all particles (columns) Xval with Z, that is for all  SE3(Xval[:,i])*Z
function projectParticles(Xval::Array{Float64,2}, Z::Array{Float64,2}, Cov::Array{Float64,2})
  # TODO optimize convert SE2 to a type
  @warn "projectParticles is an old function, rather standardize on approxConv instead."

  r,c = size(Xval)
  RES = zeros(r,c) #*cz

  # ent, x = SE3(0), SE3(0)
  j=1
  # for j in 1:cz
  ENT = rand( MvNormal(Z[:,1], Cov), c )
    for i in 1:c
      x = SE2(Xval[1:3,i])
      dx = SE2(ENT[1:3,i])
      RES[1:r,i*j] = se2vee(x*dx)
    end
  # end
  #
  return RES
end

⊕(Xpts::Array{Float64,2}, z::Pose2Pose2) = projectParticles(Xpts, z.Zij, z.Cov)
⊕(Xvert::Graphs.ExVertex, z::Pose2Pose2) = ⊕(getVal(Xvert), z)


text:
# ---
# title: 882. Reachable Nodes In Subdivided Graph
# id: problem882
# author: Tian Jun
# date: 2020-10-31
# difficulty: Hard
# categories: Heap
# link: <https://leetcode.com/problems/reachable-nodes-in-subdivided-graph/description/>
# hidden: true
# ---
#
# Starting with an  **undirected** graph (the "original graph") with nodes from
# `0` to `N-1`, subdivisions are made to some of the edges.
#
# The graph is given as follows: `edges[k]` is a list of integer pairs `(i, j,
# n)` such that `(i, j)` is an edge of the original graph,
#
# and `n` is the total number of **new** nodes on that edge.
#
# Then, the edge `(i, j)` is deleted from the original graph, `n` new nodes
# `(x_1, x_2, ..., x_n)` are added to the original graph,
#
# and `n+1` new edges `(i, x_1), (x_1, x_2), (x_2, x_3), ..., (x_{n-1}, x_n),
# (x_n, j)` are added to the original graph.
#
# Now, you start at node `0` from the original graph, and in each move, you
# travel along one edge.
#
# Return how many nodes you can reach in at most `M` moves.
#
#
#
# **Example 1:**
#
#
#
#     Input:edges = [[0,1,10],[0,2,1],[1,2,2]], M = 6, N = 3
#     Output: 13
#     Explanation:
#     The nodes that are reachable in the final graph after M = 6 moves are indicated below.
#     ![](https://s3-lc-upload.s3.amazonaws.com/uploads/2018/08/01/origfinal.png)
#
#
# **Example 2:**
#
#
#
#     Input:edges = [[0,1,4],[1,2,6],[0,2,8],[1,3,1]], M = 10, N = 4
#     Output: 23
#
#
#
# **Note:**
#
#   1. `0 <= edges.length <= 10000`
#   2. `0 <= edges[i][0] < edges[i][1] < N`
#   3. There does not exist any `i != j` for which `edges[i][0] == edges[j][0]` and `edges[i][1] == edges[j][1]`.
#   4. The original graph has no parallel edges.
#   5. `0 <= edges[i][2] <= 10000`
#   6. `0 <= M <= 10^9`
#   7. `1 <= N <= 3000`
#   8. A reachable node is a node that can be travelled to using at most M moves starting from node 0.
#
#
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
#=##############################################################################
# DESCRIPTION
    Functions for database visualization

# AUTHORSHIP
  * Author    : Eduardo J. Alvarez
  * Email     : Edo.AlvarezR@gmail.com
  * Created   : Jul 2020
  * License   : MIT
=###############################################################################

function visualize(; database_path::String=def_database,
                        index_file::String=DEF_NDXFL, plot_backend=plt.plotly)

    # Read database
    db  = jdb.loadtable(joinpath(database_path, index_file))

    # What to plot on every axis
    xaxis = :alpha
    yaxiss = [:re, :ma, :ncrit]
    zaxiss = [:clfile, :cdfile, :cmfile, :xupsepfile, :xlosepfile]

    labels = Dict(    :alpha => "Angle of attack",    # Label of each axis alternative
                      :re    => "Reynolds number",
                      :ma    => "Mach number",
                      :ncrit => "Turbulence parameter",
                      :clfile=> "Lift coefficient",
                      :cdfile=> "Drag coefficient",
                      :cmfile=> "Moment coefficient",
                      :xupsepfile=> "Separation point x",
                      :xlosepfile=> "Separation point x",
                      )
    labelsbttn = Dict(:alpha => "α",                  # Labels on buttons
                      :re    => "Reynolds number",
                      :ma    => "Mach number",
                      :ncrit => "Turbulence ncrit",
                      :clfile=> "Cl",
                      :cdfile=> "Cd",
                      :cmfile=> "Cm",
                      :xupsepfile=> "xsep upper",
                      :xlosepfile=> "xsep lower",
                      )
    lblbttn_2_field = Dict( (val, key) for (key, val) in labelsbttn) # Button labels to field

    # GUI-enabled plot
    # plotly()
    # plotlyjs()
    # pyplot()
    # gr()
    plot_backend()

    plotobj = nothing
    ite = 0

    # TODO: add a throttle
    mp = ntrct.@manipulate for     airfoil in ntrct.togglebuttons( reverse(unique(jdb.select(db, :Airfoil))), label="Airfoil" ),
                                        re in ntrct.slider( unique(jdb.select(db, :Re)), label="Reynolds Number" ),
                                        ma in ntrct.slider( unique(jdb.select(db, :Mach)), label="Mach Number" ),
                                     ncrit in ntrct.slider( unique(jdb.select(db, :Ncrit)), label="Turbulence ncrit" ),
                                     diffe in ntrct.slider( unique(jdb.select(db, :Differentiator)), label="ID" ),
                                     _yaxis in ntrct.togglebuttons( [labelsbttn[ax] for ax in yaxiss], label="y-axis" ),
                                     _zaxis in ntrct.togglebuttons( [labelsbttn[ax] for ax in zaxiss], label="z-axis" )

        yaxis = lblbttn_2_field[_yaxis]
        println(_yaxis, "\t", yaxis)
        zaxis = lblbttn_2_field[_zaxis]
        first_flag = true

        # Convert axes to column names (headers)
        ycolname = Symbol(FIELD2_HEADER[yaxis])
        zcolname = Symbol(FIELD2_HEADER[zaxis])
        ycoli = findfirst(x->x==ycolname, eltype(db.columns_buffer).parameters[1])

        # Filter file names to read
        filter_crit = [(:Airfoil, airfoil), (:Re, re), (:Mach, ma),
                                                (:Ncrit, ncrit), (:Differentiator, diffe)]
        filter_fun(nt) = prod([getfield(nt, field)==val for (field, val) in filter_crit
                                                            if field != Symbol(ycolname)])

        entries = jdb.filter(filter_fun, db)

        # Range of z to color
        zminclr, zmaxclr =  zaxis==:clfile ?     (-1.5, 2.0)   :
                            zaxis==:cdfile ?     (0, 0.2)      :
                            zaxis==:cmfile ?     (-0.02, 0.02) :
                            zaxis==:xupsepfile ? (1, 0.25)     :
                            zaxis==:xlosepfile ? (1, 0.25)     :
                                                 (-1, 1)

        # Iterate over rows reading polar files and plotting them
        for rowi in 1:length(entries)
            entry = entries[rowi]
            filename = entry[zcolname]
            yval = entry[ycoli]

            # Read data
            data = CSV.read(joinpath(database_path, DIRS[zaxis], filename))

            # Selects plot function to use
            if first_flag
                plotfun = plt.scatter
                first_flag = false
            else
                # plotfun = plt.scatter
                plotfun = plt.scatter!
            end

            # Color each point according the z value
            auxs = (zmaxclr .- data[!, 2]) / (zmaxclr - zminclr)
            auxs = [aux > 1 ? 1 : aux<0 ? 0 : aux for aux in auxs]
            clrs = collect((1-aux, 0, aux) for aux in auxs)

            plotobj = plotfun(data[!, 1], yval*ones(size(data, 1)), data[!, 2];
                                        label="",
                                        markerstrokealpha=0.2, markeralpha=0.75,
                                        markersize=1,
                                        xlabel=labels[xaxis], ylabel=labels[yaxis], zlabel=labels[zaxis],
                                        color=[plt.RGB(clr...) for clr in clrs],
                                        edgecolor=[plt.RGB(clr...) for clr in clrs]
                                    )
        end

        display(ite)
        ite += 1

        # display(plotobj)
        plotobj
    end
end


text:
# TODO
# - [ ] Adaptive RWM (ARWM)


text:
using InteractionNetworkModels
using Test

# Write your tests here.
@testset "Multinomial term" begin
    @test log_multinomial_ratio([1,1,1,1],[1]) ≈ 0.0
    @test log_multinomial_ratio([1,2],[1,1,2]) ≈ log(2/3)
end

@testset "Model" begin
    @test (1-1)==0
end


text:
module BS

using ..Distributions
import ..Roots

#
# types
#

struct D₁Parts{T}
    numerator::T
    denominator::T
end

abstract type BSOption end

"European call option."
struct EuropeanCall <: BSOption end

"European put option."
struct EuropeanPut <: BSOption end

@inline N(x) = cdf(Normal(), x)

@inline D₁Parts(s, k, t, σ) = D₁Parts(log(s/k) + (σ^2/2)*t, σ*sqrt(t))

@inline d₁(parts::D₁Parts) = parts.numerator / parts.denominator
@inline d₂(parts::D₁Parts) = d₁(parts) - parts.denominator

@inline d₁(s, k, t, σ) = d₁(D₁Parts(s, k, t, σ))
@inline d₂(s, k, t, σ) = d₂(D₁Parts(s, k, t, σ))

@inline function bscall(s, k, t, σ)
    parts = D₁Parts(s, k, t, σ)
    return s*N(d₁(parts)) - k*N(d₂(parts))
end

@inline function bsput(s, k, t, σ)
    parts = D₁Parts(s, k, t, σ)
    return k*N(-d₂(parts)) - s*N(-d₁(parts))
end

@inline price(::EuropeanCall, s, k, t, σ) = bscall(s, k, t, σ)
@inline price(::EuropeanPut, s, k, t, σ) = bsput(s, k, t, σ)

function impvol(opt::BSOption, observed_price, s, k, t, interval::Tuple=infer_impvol_interval(opt, observed_price, s, k, t))
    f(σ) = observed_price - price(opt, s, k, t, σ)
    return Roots.find_zero(f, interval, Roots.Bisection())
end

function infer_impvol_interval(opt::BSOption, observed_price, s, k, t)
    # most instruments have volatility values below 100%
    min_vol = 0.0
    max_vol = 1.0
    if observed_price <= price(opt, s, k, t, max_vol)
        return (min_vol, max_vol)
    end

    # will increment max_vol 10x until we reach a final interval
    while true
        min_vol = max_vol
        max_vol = min_vol * 10
        if observed_price <= price(opt, s, k, t, max_vol)
            return (min_vol, max_vol)
        end
    end
end

#
# Greeks
#

@inline delta(::EuropeanCall, s, k, t, σ) = N(d₁(s, k, t, σ))
@inline delta(::EuropeanPut, s, k, t, σ) = N(d₁(s, k, t, σ)) - 1

@inline function theta(::EuropeanCall, s, k, t, r, σ)
    parts = D₁Parts(s, k, t, σ)
    return -(s * pdf(Normal(), d₁(parts)) * σ) / ( 2*sqrt(t) ) - r*k*N(d₂(parts))
end

@inline function theta(::EuropeanPut, s, k, t, r, σ)
    parts = D₁Parts(s, k, t, σ)
    return -(s * pdf(Normal(), d₁(parts)) * σ) / ( 2*sqrt(t) ) + r*k*N(-d₂(parts))
end

@inline function gamma(s, k, t, σ)
    parts = D₁Parts(s, k, t, σ)
    return pdf(Normal(), d₁(parts)) / (s * parts.denominator)
end
@inline gamma(::BSOption, s, k, t, σ) = gamma(s, k, t, σ)

@inline vega(s, k, t, σ) = s*sqrt(t)*pdf(Normal(), d₁(s, k, t, σ))
@inline vega(::BSOption, s, k, t, σ) = vega(s, k, t, σ)

@inline rho(::EuropeanCall, s, k, t, σ) = k*t*N(d₂(s, k, t, σ))
@inline rho(::EuropeanPut, s, k, t, σ) = -k*t*N(-d₂(s, k, t, σ))

end # module


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

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ 0dfe9860-16b6-11eb-3d8b-cfa70bc44a58
using NeXLCore  # Load the Julia library

# ╔═╡ fadae630-16ba-11eb-0067-e1909a971b82
using PlutoUI, Gadfly, DataFrames # Other libraries

# ╔═╡ 834e0740-16b6-11eb-0c92-0b49baf28207
md"""# Introduction to NeXLCore
This notebook serves as an introduction to two subjects:
  1)  atomic physics as necessary to understand X-ray microanalysis
  2)  NeXLCore, a Julia language library providing the basic physics algorithms necessary to perform X-ray microanalysis calculations.
You can use the notebook to learn either or both subjects.

When the notebook is first loaded, it will take a while to execute.  That's just the nature of the Julia language - it is slow then fast. When code is first executed, Julia compiles it into fast native code.  This takes time.  However, subsequent execution of the code is exceedingly fast because it is efficient native code.  To mitigate this problem, if you are interested in only learning the atomic physics, there is an [HTML equivalent](https://htmlpreview.github.io/?https://github.com/usnistgov/NeXLCore.jl/blob/master/pluto/Intro%20to%20NeXLCore.jl.html) of this executable document that renders quickly in your browser. (However, the document will not be dynamic and may not be as current.)

Initially, most of the Julia code is hidden placing the emphasis on the atomic physics.  The result is a document that consists of text blocks and output blocks.  However, the Julia code is easily revealed using the "eye" icon to the left of the output block.
"""

# ╔═╡ f31c0d90-16d1-11eb-33ed-afb581fa25bc
md"""
### A Dynamic Notebook
The code in this notebook is dynamic and there are many opportunities within the document for you to interact with the document.  Sometimes the interaction is through a drop-down list and other times through a text edit field.  Use these fields to explore the functionality discussed in the text.
"""

# ╔═╡ f18fbdb0-16d0-11eb-057f-47e9ff54d437
md"""
### Units
Within microanalysis, it is common to express quantities in a consistent set of units.  Most often mass is expressed in grams (g), time in seconds (s), lengths in centimeters (cm), energies in electron volts (eV) and mixtures of these units.  For example, density is expressed in g/cm³.

While there are times when other units may be slightly more convenient, this document (and the NeXLCore library) will always express quantities in terms of these units.
"""

# ╔═╡ b6a3c8c0-16b9-11eb-15df-9564ff8c86b3
md"""
### Elements and the Periodic Table
It should come as no surprise that support for elemental and compositional data is fundamental.  Atoms are the building block of matter and X-ray microanalysis is all about *measuring the relative amount of each element within a material.*
"""

# ╔═╡ 130d9f00-16ba-11eb-2102-c79b595236a5
md"""
To get data about any element, select that element's symbol from this list:

$(@bind str1 Select( [ symbol(el) for el in elements ], default="Si"))
"""

# ╔═╡ 41d6be60-16bb-11eb-2257-63b97f451c89
elm1 = parse(Element, str1)

# ╔═╡ c90474e0-16bb-11eb-17ff-f7d8ca4de240
md"""
Elemental data is available for all elements from Hydrogen (Z=1) to Ununennium (Z=119).  This data is also available to Julia code.  The most commonly used data items are shown here.  You'll notice that they update whenever you select a different element in the list box above.

( Name, Symbol, Atomic number, Atomic Weight, Nominal Density, STP Phase )
"""

# ╔═╡ 40fb5d10-16bc-11eb-2851-ef4bf8924395
( name(elm1), symbol(elm1), z(elm1), a(elm1), elm1.density, elm1.phase )
# Use `fieldnames(Element)` to get a full list of the fields available for Element objects

# ╔═╡ 6d849e90-16bd-11eb-3cab-7d0e7f23e940
md"""
Since elements are so important, there are a couple of different ways you can select elements.  The list above demonstrates one.  Another is through interpreting the name, symbol or atomic number of the element as is demonstrated here.

$(@bind str2 TextField(;default = "zirconium"))

The result from valid inputs will be immediately parsed and displayed below.  Invalid inputs produce an error message.
"""

# ╔═╡ 140f81d0-16be-11eb-27cd-77340c06ecad
elm2 = parse(Element, str2) # The output is the HTML representation of an Element object

# ╔═╡ ec809860-16be-11eb-0270-8d7a43230142
md"""
The elements are often displayed organized into a tabular form which emphsizes the relationships between the elements due to commonalities in atomic shell structure.
"""

# ╔═╡ 5975a962-16bf-11eb-2016-556422ec37e5
elements

# ╔═╡ 9881f370-16bf-11eb-37f9-1d5182008ad5
md"""
### Materials
Elements combine via chemical bonds to form materials.  One common representation of a material is the relative amount of each element by mass-fraction.

$(@bind str3 TextField((40,1);default="0.0377*F+0.3807*O+0.1843*P+0.3974*Ca")) $(@bind str4 TextField((15,1);default="Apatite"))
"""

# ╔═╡ 832034ee-16c0-11eb-3a20-87fe130d85ea
mat1 = parse(Material,str3, name=str4)

# ╔═╡ 1cfb7f12-16c3-11eb-0d43-599fb61feccc
md"""
There are other useful ways to enter material data.  Materials like the default example Apatite are often represented by the number of atoms of each element in a unit cell like this:

$(@bind str5 TextField(;default="Ca5(PO4)3F"))
"""

# ╔═╡ db03ea02-16c4-11eb-3594-e7ed35df96f1
mat2 = parse(Material,str5)

# ╔═╡ 2626bd50-16ca-11eb-09fc-1f91e263915c
md"""
There are various different common representations of material data.  Three common ones are "mass fraction", "normalized mass fraction" and "atom fraction".  The first two represent the contribution of weight of each element relative to the weight of the material.  The final one represents the fraction of atoms in the bulk representing each element.  The transform from mass fraction to atom fraction depends upon the atomic weight.  Atomic weight is not a universal constant.  It varies with isotopic abundances.  Usually, we assume the nominal terrestrial atomic weights but other atomic weights are possible.  For example, isotopically enhanced lithium can vary from the nominal 7.6% Li⁶ / 92.4% Li⁷.  [According to Wikipedia](https://en.wikipedia.org/wiki/Isotopes_of_lithium) commercial sources of Li metal may contain as little at 1.6% Li⁶.  Generally, this doesn't matter.  However, it is worthwhile to remember that any time atom fraction is converted to mass fraction, or vice versa, there is an implicit assumption of atomic weight.  (Programmatically, it is possible to specify custom atomic weights on a per material basis but this is an advanced topic.)
"""

# ╔═╡ 8a271e7e-16c5-11eb-1a9c-199f2fe04110
asa(DataFrame,mat2)

# ╔═╡ 9142bad0-16c5-11eb-3ceb-b7062ec4cda9
md"""
Many minerals can not be readily described by unit cell chemical formulas as Apatite can.  For example, the garnet group has chemical formulae of the form "X₃Y₂(SiO₄)₃".  The subclass of pyralspite garnets have Al in the Y position and Mg, Fe or Mn in the  position.  However, it is improbable to find a pure "end-member" composition of garnet.  Usually, natural garnets are mixtures of these kinds.  For an example, a sample that might be 40% by mass pyrope (Mg₃Al₂(SiO₄)₃) and 60% by mass spessartine (Mn₃Al₂(SiO₄)₃).  (Try adding in a little almandine (Fe₃Al₂(SiO₄)₃) but be careful. The parser won't warn you if the sum of the factors isn't unity.)

$(@bind str6 TextField((50,1),default="0.4*Mg3Al2(SiO4)₃+0.6*Mn3Al2(SiO4)3"))
"""

# ╔═╡ e9be6d52-16cd-11eb-0d3e-edbf627bb532
mat3=parse(Material, str6)

# ╔═╡ b439b680-16cd-11eb-1d83-6dd66a65321f
asa(DataFrame, mat3)

# ╔═╡ 2be45370-16ce-11eb-23e7-29a647364168
md"""Similarly,  iron oxides are often mixtures of Fe₂O₃, FeO₂, FeO and others.

$(@bind str7 TextField((50,1),default="0.35*Fe2O3+0.4*FeO2+0.25*FeO"))
"""

# ╔═╡ 19279d40-16cf-11eb-0452-57417aa56424
asa(DataFrame, parse(Material, str7))

# ╔═╡ 5b2b7c70-16cf-11eb-058c-89d2f93cb857
md"""
Supplemental data can also be associated with materials like density, conductivity, a description or user-define properties.  While the element data provides nominal densities for the pure elements, the density of a material is not readily calculable from the elemental densities.  Wikipedia is often a good source for density and conductivity data.  Within NeXLCore, mass values are always expressed in grams and length units in centimeters.  Thus density is in g/cm³.
"""

# ╔═╡ c5f0dc30-16cf-11eb-0c87-ab45a42ac22c
mat4 = parse(Material,"CaF2", density=3.18, name="Fluorite",description="A white insoluble solid", conductivity=:Insulator)

# ╔═╡ 1c3d7350-16d0-11eb-2bef-93f642dfb2c2
mat4[:Density], name(mat4), mat4[:Description], mat4[:Conductivity]

# ╔═╡ bd5c97ae-16d1-11eb-02ff-cd179f6bd0bb
md"""
Throughout this document we have transformed materials into tables to display various different representations.  There are programmatic ways to access the alternative representations. (These representations will look odd to the non-programmer.)"""

# ╔═╡ 6da0cc90-16d2-11eb-280a-618a1bf5d53e
mat5=parse(Material, "0.2*FeO+0.3*Fe2O3+0.45*FeO2", density=5.4)

# ╔═╡ 9d8e3d1e-16d2-11eb-3628-ed02c1d2d7b2
analyticaltotal(mat5)

# ╔═╡ a646c090-16d2-11eb-3266-07d60f71fbd5
repr(atomicfraction(mat5))

# ╔═╡ b3d6e3c0-16d2-11eb-1813-a5780a0bd5ac
repr(normalizedmassfraction(mat5))

# ╔═╡ 26352620-16d3-11eb-1e88-a369733788ca
md"""
This example makes use of a syntactic shortcut.  Elements may be described in code using the notation n"XX" where XX is the element's name, symbol or atomic number.  The mass fraction value associated with a material may be accessed using indexing syntax where the indexing argument is an Element."""

# ╔═╡ 08493ca0-16d3-11eb-115a-93e93765ad71
( mat5[n"8"], mat5[n"Fe"], mat5[n"Chromium"] )

# ╔═╡ c42bfde0-16d3-11eb-14c8-1b5e0ef2b471
md"""
Finally, there is a little additional syntactic sugar to assist with accessing properties.  I can define custom properties like `:Luster` like this.
"""

# ╔═╡ ec8cc0d0-16d3-11eb-0c51-576d6455d63e
mat5[:Luster]="Metallic to splendent"

# ╔═╡ fd789220-16d3-11eb-1bef-8bd4a2b73433
md"""
The custom properties use a `Symbol` name and allow the user to customize the Material structure to contain any kind of custom data.

I can access properties like this:
"""

# ╔═╡ 06eb64e0-16d4-11eb-2a97-3b7562e67aff
( mat5[:Density], mat5[:Luster] )

# ╔═╡ 40dd39d0-16d4-11eb-0cff-5389bfdbc95f
md"""
Where :Density is a library defined property and :Luster is a user custom property.
"""

# ╔═╡ 783c55f0-16d4-11eb-0b7e-0b333c4fcdc9
md"""
Finally we can display many materials in a single table.  Unfortunately, the representation in this notebook cuts off a handful of columns on the right side. The DataFrame code object contains the columns and they do display in other output formats and are written correctly to CSV files.
"""

# ╔═╡ 5e5ea1b0-16d4-11eb-02eb-45f230da312b
asa(DataFrame, [mat1,mat2,mat3,mat4,mat5])

# ╔═╡ 9cd39850-16d5-11eb-0b78-5dc812aa4048
md"""
### Atomic Structure
The elements are defined by the number of protons in their nucleus.  Hydrogen always has one proton, helium two protons, lithium three protons, all the way to ununennium with 119 protons.  The number of neutrons can vary within a range between 1 to 2 neutrons per proton (mostly).  Since neutrons and protons weight almost the same, this defines the general relationship between Z (atomic number) and A (atomic weight).
"""

# ╔═╡ fd2c5c00-16d5-11eb-01c3-bf6b892f9005
plot(x=z.(elements), y=a.(elements), Geom.point, Guide.xlabel("Atomic Number"),Guide.ylabel("Nominal Atomic Weight"))

# ╔═╡ 92d88d50-16d6-11eb-3293-2d1074dd1d49
md"""
The neutrons and protons are bound together by nuclear forces into a small tight bundle called the *nucleus*.
|
In a neutral atom, the type we always study in microanalysis, the number of electrons exactly equals the number of protons.  This is because the charge on an electron is precisely equal in magnitude to the charge on a proton but opposite in sign.  By convention, electrons are said to have negative charge and protons positive charge. Almost all the mass of the atom is in the nucleus because an electron weights about 1/2000 the mass of either a neutron or a proton.

Positive and negative charges attract but there is a limit to how close the electrons can come to the nucleus.  This limit is imposed by a purely quantum effect - the *Pauli exclusion principle* which in crude terms says that no two electrons can occupy the same position/angular momentum/spin state.

The result is that the electrons in a neutral ground state atom form into a series of shells in which each electron can be thought of as having own well defined state.

There are various different notations to express these states.  We will stick with *IUPAC notation* because it is simple to understand and commonly used in microanalysis. We won't use Seigbahn notation because, while it is more common, it is also ambiguous and less clear having been designed before atomic structure was understood.
"""

# ╔═╡ 7cf2dc50-16d8-11eb-3536-2b23cbffcb97
md"""
### IUPAC Notation
In IUPAC notation, there are atomic shells represented by the letters K, L, M, N... representing the principle atomic numbers, `n`, 1, 2, 3, 4, ....

The K-shell is most tightly bound and can contain only 2 electrons.

The L-shell is second most tightly bound and can contain (2 + 2 + 4) electrons.

The M-shell is next most tightly bound and can contain (2 + 2 + 4 + 4 + 6) electrons, and so on;
"""

# ╔═╡ 491f6280-16d9-11eb-0d35-2dd2de0bed05
md"""
Each shell has sub-shells.
The K-shell has one subshell, the L-shell has 3 sub-shells (L₁, L₂, L₃), the M-shell
has 5 sub-shells (M₁, M₂, M₃, M₄ and M₅) and so on with each subsequent shell gaining two additional sub-shells.
"""

# ╔═╡ 00adabd0-16dc-11eb-2f60-8d98e3b3ddad
@bind sstr1 TextField(;default="M5")

# ╔═╡ dd62d180-16d8-11eb-30aa-55655004bcb8
asa(DataFrame, [ subshell(sstr1)])

# ╔═╡ fb235680-16de-11eb-0d25-71d5c8e0b3ce
md"""
In this table, n represents the principle quantum number, l represents the orbital angular momentum quantum number, j represents the total angular momentum quantum number and the capacity is the maximum number of electrons that can occupy this sub-shell.

It is no problem if you don't understand, the quantum numbers.  Mostly, the library handles issues of quantum numbers transparently.
"""

# ╔═╡ c3392de0-16dd-11eb-04ea-b1d585b7a603
asa(DataFrame,collect(allsubshells))

# ╔═╡ 4a263c70-16df-11eb-1ee7-27bc959030ec
md"""
This table summarizes the properties of all the sub-shells up to O2 (or so.)

Programmatic use is demonstrated below.
"""

# ╔═╡ 460992d0-16e0-11eb-1360-b5fcc070caf7
begin
    ss = subshell("L3")
    ( ss, n(ss), l(ss), j(ss), capacity(ss) )
end

# ╔═╡ be4b0e00-16df-11eb-0b65-193fc505d133
md"""
### Sub-shells and Elements

Sub-shells become less abstract when combined with elements.

As the number of electrons in a element increase, the number of shells necessary to contain the electrons increases.  The shells fill mostly in the nominal order of shells but not entirely.

Use this drop-down list to investigate which shells are occupied (in the ground state) for which elements.

$(@bind asstr2 Select([ symbol(elm) for elm in elements[1:92] ],default="Fe"))
"""

# ╔═╡ 68f6a7a0-16e1-11eb-0b4a-43dba1f6827d
asa(DataFrame, atomicsubshells(parse(Element,asstr2)))

# ╔═╡ e4ffb450-16ef-11eb-1c7d-673fe5bb5b3b
md"""
Alternatively, you can use text parsing to investigate individual elements and sub-shells.

Type the element name/symbol/number followed by the sub-shell name:
"""

# ╔═╡ f00b1bb0-16df-11eb-1eae-556717663920
@bind asstr1 TextField(default="Fe L3")

# ╔═╡ 23fab390-16e0-11eb-306b-6988146b6c75
asa(DataFrame,[atomicsubshell(asstr1)])

# ╔═╡ 2de72fe0-16f0-11eb-0abb-cd18b62b33c9
md"""
As you might expect, these values are also available programmatically.  The units for the edge energy are eV and for the ionization cross-section cm².  The jump ratio and fluorescence yields are unitless ratios.
"""

# ╔═╡ 3fa3e5c0-16f0-11eb-3955-0f8ca0209ee4
begin
	ass2 = atomicsubshell(asstr1)
	( ass2, repr(ass2), subshell(ass2), energy(ass2), ionizationcrosssection(ass2, 2.0*energy(ass2)), jumpratio(ass2), fluorescenceyield(ass2) )
end

# ╔═╡ b76fcbb0-1703-11eb-01c9-17a5871ddfd8
md"""
### Characteristic X-rays

*Characteristic X-rays* result from the relaxation of inner shell ionizations.

Two processes commonly lead to a single electron being ejected from an inner shell to form a singly ionized atom, 1) electron impact; 2) photoionization.  In both cases, energy must be conserved.  In the case of electron impact, the incident electron must have sufficient kinetic energy to overcome the atomic sub-shell's binding energy (as listed as energy in the above tables.) For photoionization, the photon (typically an X-ray) must have enough energy to exceed the sub-shell's binding energy.

An inner shell ionization is not stable.  More weakly bound electrons will want to decay into the inner shell either via X-ray emission or by a two electron process called *Auger emission*.  The fractional likelihood that an ionized atom decays via a characteristic X-ray is called the *fluorescence yield* and is listed in the above tables as FluorYield.
"""

# ╔═╡ c01956d0-170a-11eb-27d9-f185887461e9
md"""
Select an element to display all the characteristic X-rays associated with the element.

$(@bind cstr1 Select([ symbol(el) for el in elements[1:92] ], default="Al"))

Note that as the number of electrons increases, the number of potential transitions also increases.
"""

# ╔═╡ 69de170e-1705-11eb-39ce-931663dd429d
asa(DataFrame, characteristic(parse(Element, cstr1),alltransitions))

# ╔═╡ Cell order:
# ╟─834e0740-16b6-11eb-0c92-0b49baf28207
# ╟─f31c0d90-16d1-11eb-33ed-afb581fa25bc
# ╟─f18fbdb0-16d0-11eb-057f-47e9ff54d437
# ╠═0dfe9860-16b6-11eb-3d8b-cfa70bc44a58
# ╟─b6a3c8c0-16b9-11eb-15df-9564ff8c86b3
# ╟─fadae630-16ba-11eb-0067-e1909a971b82
# ╟─130d9f00-16ba-11eb-2102-c79b595236a5
# ╟─41d6be60-16bb-11eb-2257-63b97f451c89
# ╟─c90474e0-16bb-11eb-17ff-f7d8ca4de240
# ╟─40fb5d10-16bc-11eb-2851-ef4bf8924395
# ╟─6d849e90-16bd-11eb-3cab-7d0e7f23e940
# ╟─140f81d0-16be-11eb-27cd-77340c06ecad
# ╟─ec809860-16be-11eb-0270-8d7a43230142
# ╠═5975a962-16bf-11eb-2016-556422ec37e5
# ╟─9881f370-16bf-11eb-37f9-1d5182008ad5
# ╟─832034ee-16c0-11eb-3a20-87fe130d85ea
# ╟─1cfb7f12-16c3-11eb-0d43-599fb61feccc
# ╠═db03ea02-16c4-11eb-3594-e7ed35df96f1
# ╟─2626bd50-16ca-11eb-09fc-1f91e263915c
# ╟─8a271e7e-16c5-11eb-1a9c-199f2fe04110
# ╟─9142bad0-16c5-11eb-3ceb-b7062ec4cda9
# ╟─e9be6d52-16cd-11eb-0d3e-edbf627bb532
# ╟─b439b680-16cd-11eb-1d83-6dd66a65321f
# ╟─2be45370-16ce-11eb-23e7-29a647364168
# ╟─19279d40-16cf-11eb-0452-57417aa56424
# ╟─5b2b7c70-16cf-11eb-058c-89d2f93cb857
# ╟─c5f0dc30-16cf-11eb-0c87-ab45a42ac22c
# ╟─1c3d7350-16d0-11eb-2bef-93f642dfb2c2
# ╟─bd5c97ae-16d1-11eb-02ff-cd179f6bd0bb
# ╟─6da0cc90-16d2-11eb-280a-618a1bf5d53e
# ╠═9d8e3d1e-16d2-11eb-3628-ed02c1d2d7b2
# ╠═a646c090-16d2-11eb-3266-07d60f71fbd5
# ╠═b3d6e3c0-16d2-11eb-1813-a5780a0bd5ac
# ╟─26352620-16d3-11eb-1e88-a369733788ca
# ╠═08493ca0-16d3-11eb-115a-93e93765ad71
# ╟─c42bfde0-16d3-11eb-14c8-1b5e0ef2b471
# ╠═ec8cc0d0-16d3-11eb-0c51-576d6455d63e
# ╟─fd789220-16d3-11eb-1bef-8bd4a2b73433
# ╠═06eb64e0-16d4-11eb-2a97-3b7562e67aff
# ╟─40dd39d0-16d4-11eb-0cff-5389bfdbc95f
# ╟─783c55f0-16d4-11eb-0b7e-0b333c4fcdc9
# ╟─5e5ea1b0-16d4-11eb-02eb-45f230da312b
# ╟─9cd39850-16d5-11eb-0b78-5dc812aa4048
# ╟─fd2c5c00-16d5-11eb-01c3-bf6b892f9005
# ╟─92d88d50-16d6-11eb-3293-2d1074dd1d49
# ╟─7cf2dc50-16d8-11eb-3536-2b23cbffcb97
# ╟─491f6280-16d9-11eb-0d35-2dd2de0bed05
# ╟─00adabd0-16dc-11eb-2f60-8d98e3b3ddad
# ╟─dd62d180-16d8-11eb-30aa-55655004bcb8
# ╟─fb235680-16de-11eb-0d25-71d5c8e0b3ce
# ╟─c3392de0-16dd-11eb-04ea-b1d585b7a603
# ╟─4a263c70-16df-11eb-1ee7-27bc959030ec
# ╟─460992d0-16e0-11eb-1360-b5fcc070caf7
# ╟─be4b0e00-16df-11eb-0b65-193fc505d133
# ╟─68f6a7a0-16e1-11eb-0b4a-43dba1f6827d
# ╟─e4ffb450-16ef-11eb-1c7d-673fe5bb5b3b
# ╟─f00b1bb0-16df-11eb-1eae-556717663920
# ╟─23fab390-16e0-11eb-306b-6988146b6c75
# ╟─2de72fe0-16f0-11eb-0abb-cd18b62b33c9
# ╟─3fa3e5c0-16f0-11eb-3955-0f8ca0209ee4
# ╟─b76fcbb0-1703-11eb-01c9-17a5871ddfd8
# ╟─c01956d0-170a-11eb-27d9-f185887461e9
# ╟─69de170e-1705-11eb-39ce-931663dd429d

max_stars_repo_path: pluto/Intro to NeXLCore.jl

max_issues_repo_path: pluto/Intro to NeXLCore.jl

max_forks_repo_path: pluto/Intro to NeXLCore.jl


text:
# Struct for model parameters

"""
    parameter(β0, α, β, η, ϕ, ω)
Structure for Count Data models parameters.

* `β0`: Intercept parameter
* `α`: Parameters for autoregression
* `β`: Parameters for MA part/past means
* `η`: Regressor parameters
* `ϕ`: Overdispersion parameter (Negative Binomial)
* `ω`: Zero inflation probability

For details, see For details, see [Documentation](https://github.com/ManuelStapper/CountTimeSeries.jl/blob/master/CountTimeSeries_documentation.pdf)
"""
mutable struct parameter
    β0::T where T<:AbstractFloat
    α::Array{T, 1} where T<:AbstractFloat
    β::Array{T, 1} where T<:AbstractFloat
    η::Array{T, 1} where T<:AbstractFloat
    ϕ::Array{T, 1} where T<:AbstractFloat
    ω::T where T<:AbstractFloat
end


text:
@testset "NonlocalMean" begin
    @info "Test: NonlocalMean"

    @testset "API" begin
        img_gray = imresize(testimage("lena_gray_256"); ratio=0.25)
        n = AdditiveWhiteGaussianNoise(0.05)
        noisy_img = apply_noise(img_gray, n; rng=MersenneTwister(0))

        # NonlocalMean
        @test NonlocalMean(0.1) == NonlocalMean(0.1, 2)
        @test NonlocalMean(0.1, 2) == NonlocalMean(0.1, 2, 5)
        @test NonlocalMean(0.1, noisy_img) == NonlocalMean(0.1, 1) # estimate r_p = 1
        @test NonlocalMean(0.1, 3) == NonlocalMean(0.1, 3, 7)
        @test_throws ArgumentError NonlocalMean(0.1, -1)
        @test_throws ArgumentError NonlocalMean(0.1, 1, -1)

        # reduce_noise
        f = NonlocalMean(0.05, 2)
        denoised_img_1 = reduce_noise(noisy_img, f)
        denoised_img_2 = similar(noisy_img)
        reduce_noise!(denoised_img_2, noisy_img, f)
        denoised_img_3 = copy(noisy_img)
        reduce_noise!(denoised_img_3, f)
        denoised_img_4 = reduce_noise(Gray{Float64}, noisy_img, f)

        @test eltype(denoised_img_1) == Gray{N0f8}
        @test eltype(denoised_img_2) == Gray{N0f8}
        @test eltype(denoised_img_3) == Gray{N0f8}
        @test eltype(denoised_img_4) == Gray{Float64}
        @test denoised_img_1 == denoised_img_2
        @test denoised_img_1 == denoised_img_3
        @test assess(PSNR(), denoised_img_1, denoised_img_4) >= 50
        @test assess(SSIM(), denoised_img_1, denoised_img_4) >= 0.999
    end

    @testset "Types" begin
        # Gray
        img_gray = n0f8.(imresize(testimage("lena_gray_256"); ratio=0.25))
        n = AdditiveWhiteGaussianNoise(0.05)
        noisy_img = apply_noise(img_gray, n; rng=MersenneTwister(0))

        f = NonlocalMean(0.05, 2)
        type_list = generate_test_types([Float32, N0f8], [Gray])
        for T in type_list
            img = T.(noisy_img)
            @test_reference "References/NonlocalMean_Gray.png" Gray{N0f8}.(reduce_noise(img, f))
        end

        # Color3
        img_color = n0f8.(imresize(testimage("lena_color_256"); ratio=0.25))
        n = AdditiveWhiteGaussianNoise(0.05)
        noisy_img = apply_noise(img_color, n; rng=MersenneTwister(0))

        f = NonlocalMean(0.05, 2)
        type_list = generate_test_types([Float32, N0f8], [RGB, Lab])
        for T in type_list
            img = T.(noisy_img)
            @test_reference "References/NonlocalMean_Color3.png" RGB{N0f8}.(reduce_noise(img, f)) by=psnr_equality(24)
        end
    end

    @testset "Numeric" begin
        img_gray = n0f8.(imresize(testimage("lena_gray_256"); ratio=0.25))
        n = AdditiveWhiteGaussianNoise(0.05)
        noisy_img = apply_noise(img_gray, n; rng=MersenneTwister(0))

        f = NonlocalMean(0.05, 2)
        denoised_img = reduce_noise(noisy_img, f)
        # further modification shall not decrease psnr and ssim
        assess(PSNR(), denoised_img, img_gray) >= 28.46
        assess(SSIM(), denoised_img, img_gray) >= 0.918
    end
end


text:
    color_name()::String = executor(data["faker"]["color"]["allcolor"])
    safe_color_name()::String = executor(data["faker"]["color"]["name"])
    hex_color()::String = executor(data["faker"]["color"]["code"])
    rgb_color()::String = string(rand(1:255),",",rand(1:255),",",rand(1:255))
    rgb_color_list()::String = string("(",rgb_color(),")")
    rgb_css_color()::String = string("rgb(",rgb_color(),")")


text:
export buildnet

function buildnet(;name="temp",model=NetworkModel(),method="Euler1",duration=0.1)
  iteration=round(Int64,duration/model.dt)
  syncIter=model.synctime>0?round(Int64,model.synctime÷model.dt):1
  iteration=iteration÷syncIter
  model.nCores=length(model.groups[:index])+1
  ##############################################
  # I.parse

  # equations
  eqs=map(strip,split(replace(model.equations,";","\n"),"\n"));
  vars=[]
  diffEqs=[]

  for k=1:length(eqs)
    if eqs[k][1]=='d'
      vars=[vars;match(r"(?<=d).*(?=/dt)",eqs[k]).match]
      diffEq=vectorize(match(r"(?<==).*$",eqs[k]).match)
      diffEqs=[diffEqs;diffEq]
    else
      funcName=match(r"^.*(?==)",eqs[k]).match
      funcExpr=vectorize(match(r"(?<==).*$",eqs[k]).match)
      diffEqs=map(diffEq->replace(diffEq,funcName,funcExpr),diffEqs)
    end
  end

  # report
  report="""
  k%(1000÷syncIter)==0?@printf("%s%4.2f finished\\n","%",100*k/iteration):nothing
  """

  # integrate
  argument=join([vars;"t"],",")
  splitArg=split(argument,",")
  argumentTemp=join([splitArg[1]*"+Δ$(vars[1])";splitArg[2:end]],",")

  func=map(var->"F$var($argument)",vars)
  tempFunc="F$(vars[1])($argumentTemp)"

  if model.solver=="Euler1"
    integrate="Δ$(vars[1])=dt*$(func[1])\n"
  elseif model.solver=="Euler2"
    integrate="Δ$(vars[1])=dt/2.0*$(func[1])\n"
    integrate*="Δ$(vars[1])+=dt/2.0*$tempFunc\n"
  elseif model.solver=="Euler4"
    integrate="Δ$(vars[1])=dt/4.0*$(func[1])\n"
    integrate*="Δ$(vars[1])+=dt/4.0*$tempFunc\n"
    integrate*="Δ$(vars[1])+=dt/4.0*$tempFunc\n"
    integrate*="Δ$(vars[1])+=dt/4.0*$tempFunc\n"
  end

  for k=2:length(vars)
    integrate*="Δ$(vars[k])=dt*$(func[k])\n"
  end
  for k=1:length(vars)
    integrate*="$(vars[k])+=Δ$(vars[k])\n"
  end

  # spike
  if !isempty(model.spike)
    spike=split(replace(model.spike,";","\n"),"\n")
    threshold=spike[1]
    length(spike)==2? refractory=spike[2]:refractory=""

    spike=""
    spike*="isExceedThreshold=$threshold\n"
    if !isempty(refractory)
      spike*="isNotRefractory=$refractory\n"
      spike*="isSpike=isExceedThreshold & isNotRefractory\n"
    else
      spike*="isSpike=isExceedThreshold\n"
    end
    spike*="spikeIndex=find(isSpike)\n"
    spike*="nSpike=length(spikeIndex)\n"
    spike*="tSpike[state:state+nSpike-1]=t\n"
    spike*="ts[spikeIndex]=t\n"
    spike*="spikeNeuron[state:state+nSpike-1]=spikeIndex\n"
    spike*="state+=nSpike\n"
  else spike=""
  end

  # reset
  if !isempty(model.reset)
    reset=""
    for line in split(replace(model.reset,";","\n"),"\n")
      var=match(r"^.*(?==)",line).match
      expr=match(r"(?<==).*",line).match
      reset*="$(var)[spikeIndex]=$(expr)\n"
    end
  else reset=""
  end

  # synapse
  if !isempty(model.synapse)
    synapse=""
    for line in split(replace(model.synapse,";","\n"),"\n")
      var1=match(r"^.*(?=\+)",line).match
      var2=match(r"(?<==).*",line).match
      synapse*="$(var1)+=sum($(var2)[:,totalSpikeIndex],2)\n"
   end
  else synapse=""
  end

  # plasticity
  if !isempty(model.plasticity)
    str=split(replace(model.plasticity,";","\n"),"\n");
    plasticity1=join(str[1:end÷2],"\n");plasticity2=join(str[end÷2+1:end],"\n")
    plasticity1=replace(replace(plasticity1,"j",":"),"i","totalSpikeIndex")*"\n"
    plasticity2=replace(replace(plasticity2,"j",":"),"i","spikeIndex")*"\n"
    plasticity=plasticity1*plasticity2
  else plasticity=""
  end

  # record
  if !isempty(model.record)
    record=""
    for i=1:length(vars)
      record*="$(vars[i])Record[:,\$k]=$(vars[i])[localRecord]\n"
    end
  else
    record=""
  end
  # end of parse
  ####################################################

  ####################################################
  # II.print

  # open a file
  fid=open(name*".jl","w")

  # set up parameters
  paramsStr="""
  println("\\nExecuting $(name).jl")

  function sendto(p::Int; args...)
    for (nm, val) in args
        @spawnat(p, eval(Main, Expr(:(=), nm, val)))
    end
  end
  function sendto(ps::Vector{Int}; args...)
      for p in ps
          sendto(p; args...)
      end
  end

  using JLD

  if $(model.nCores)>0 && (addCores=$(model.nCores)-nworkers())>0
    nworkers()==1?addprocs(addCores):addprocs(addCores-1)
  end
  println("Using \$(nworkers()) workers")

  @everywhere begin
  index=$(model.groups[:index])
  # units
  metre=1e2;meter=1e2;cm=metre/1e2;mm=metre/1e3;um=metre/1e6;nm=metre/1e9;
  second=1e3;ms=second/1e3;
  Hz=1/second;
  voltage=1e3;mV=voltage/1e3;
  ampere=1e6;mA=ampere/1e3;uA=ampere/1e6;nA=ampere/1e9;
  farad=1e6;uF=ufarad=farad/1e6;
  siemens=1e3;mS=msiemens=siemens/1e3;nS=nsiemens=siemens/1e9
  # parameters
  $(model.parameters)"""
  println(fid,paramsStr)

  varsType=map(var->var*"::Array{Float64}",vars)
  argumentType=join([varsType;"t::Float64"],",");
  println(fid,"# function")
  for k=1:length(vars)
    println(fid,"F",vars[k],"(",argumentType,")=",diffEqs[k])
  end
  println(fid,"end # end of @everywhere\n")

  # function
  initVars=""
  for var in vars
    initVars*="@parallel for p in workers()  @eval $var=zeros(N) end\n"
  end

  initRecord=""
  if !isempty(model.record)
    for k=1:length(vars)
      initRecord*="$(vars[k])Record=zeros(length(localRecord),iteration)\n"
    end
  end

  collectVars="""
  spikeNeuron=@parallel (vcat) for p in workers() @eval spikeNeuron[1:state-1]+localIndex[1]-1  end
  tSpike=@parallel (vcat) for p in workers() @eval tSpike[1:state-1]  end
  """
  if !isempty(model.record)
    collectVars*="$(vars[1])Record=@parallel (vcat) for p in workers() @eval $(vars[1])Record end\n"
  end
  for var in model.saveVars
    collectVars*="$var=@parallel (vcat) for p in workers() @eval $var end\n"
  end

  saveVars="""
  try jldopen("$name.jld","w") do fid
    try  write(fid,"spikeNeuron",spikeNeuron)  end
    try  write(fid,"tSpike",tSpike)            end
    try  write(fid,"$(vars[1])Record",$(vars[1])Record)   end
    """
  for var in model.saveVars
  saveVars*="try write(fid,\"$(var)\",$var) end\n"
  end
  saveVars*="end #end of do\n"
  saveVars*="end #end of try jldopen\n"

  funcStr="""
  function $name(iteration::Int64=10)
  syncIter=$syncIter
  @sync @parallel for n=1:nworkers()
  @eval begin
  t=0.0;dt=$(model.dt); N_tot=$(model.groups[:N]); syncIter=$syncIter;
  localIndex=index[\$n];N=length(localIndex); iteration=\$iteration
  localRecord=intersect($(model.record),localIndex)-localIndex[1]+1
  # initialize record
  $initRecord
  # initialize tSpike
  max_tSpike=round(Int64,N*iteration*dt)
  tSpike=zeros(max_tSpike);spikeNeuron=zeros(Int64,max_tSpike)
  ts=zeros(N);state=1;spikeIndex=[]
  end # end of @eval begin
  end # end of @parallel for

  @sync begin
  # initialize variables
  $initVars
  $(model.initialization)
  # initialize connections
  $(model.connection)
  end # end of @sync begin

  # print status
  iteration>=50?println("simulation starts..."):println("compiling...")

  # for-loop
  tic()
  @fastmath @inbounds for k=1:iteration
  # report
  $report
  # run until sync
  @sync @parallel for p in workers()
  @eval begin
  for idx=1:syncIter
  # integrate
  $integrate
  t+=dt
  end # end of for idx=1:syncIter
  # spike
  $spike
  # reset
  $reset
  # record
  $record
  end # end of @eval begin
  end # @sync for p in workers()

  totalSpikeIndex=@parallel (vcat) for p in workers()
  @eval spikeIndex+localIndex[1]-1
  end
  sendto(workers(),totalSpikeIndex=totalSpikeIndex)
  @sync @parallel for p in workers()
  @eval begin
  # synapse
  $synapse
  # plasticity
  $plasticity
  end
  end

  end # end of @fastmath @inbounds for k=1:iteration
  toc()

  # collect variables
  $collectVars
  # save variables
  $saveVars

  end # end of main function

  # precompile and run
  precompile($name,($iteration,))
  $name(20)
  $name(20)
  $name($iteration)

  # post-processing
  $(model.postProcs)
  """
  println(fid,funcStr)

  # close file id, try to open file in atom
  close(fid)
  try run(`atom $(abspath("$name.jl"))`) end

end # end of function buildNetwork


text:
"""
    relief(data::Array{<:Real,2}, target::Array{<:Integer,1}, m::Signed=-1,
                dist_func::Any=(e1, e2) -> sum(abs.(e1 .- e2), dims=2); f_type::String="continuous")::Array{Float64,1}

Compute feature weights using Relief algorithm. The f_type argument specifies whether the features are continuous or discrete
and can either have the value of "continuous" or "discrete".

---
# Reference:
- Kenji Kira and Larry A. Rendell. The feature selection problem: Tra-
ditional methods and a new algorithm. In Proceedings of the Tenth
National Conference on Artificial Intelligence, AAAI’92, pages 129–134.
AAAI Press, 1992.
"""
function relief(data::Array{<:Real,2}, target::Array{<:Integer,1}, m::Signed=-1,
                dist_func::Any=(e1, e2) -> sum(abs.(e1 .- e2), dims=2); f_type::String="continuous")::Array{Float64,1}

    # Initialize feature weights vector.
    weights = zeros(Float64, size(data, 2))

    # Compute vectors of maximum and minimum feature values.
    max_f_vals = vec(maximum(data, dims=1))
    min_f_vals = vec(minimum(data, dims=1))

    # Sample m samples without replacement. If m has signal value -1, use all samples.
    sample_idxs = StatsBase.sample(1:size(data, 1), if (m==-1) size(data,1) else m end, replace=false)
    if (m == -1) m = size(data, 1) end # If m has signal value -1, set m to total number of examples.

    # Compute pairwise distances between samples (vector form).
    # Note that the number of elements in the distance vector is {n \choose 2} = n!/(2!*(n-2)!) = n*(n-1)/2.
    dists = Array{Float64}(undef, Int64((size(data, 1)^2 - size(data, 1))/2))

    # Construct pairwise distances vector using vectorized distance function.
    top_ptr = 1
    @inbounds for idx = 1:size(data,1)-1
        upper_lim = top_ptr + size(data, 1) - idx - 1
        dists[top_ptr:upper_lim] = dist_func(data[idx:idx, :], data[idx+1:end, :])
        top_ptr = upper_lim + 1
    end

    # Go over sampled indices.
    @inbounds for idx = sample_idxs

        # Row and column indices for querying pairwise distance vector.
        # Use zero-based numbering.
        row_idxs = repeat([idx - 1], size(data, 1))
        col_idxs = collect(0:size(data, 1)-1)

        # Get indices of neighbors with same class in distances vector and find nearest hit.
        sel_neigh_same = (target .== target[idx]) .& (1:length(target) .!= idx)
        neigh_idx_hit = square_to_vec(row_idxs[sel_neigh_same],
                                        col_idxs[sel_neigh_same], size(data, 1)) .+ 1
        idx_nearest_hit = argmin(dists[neigh_idx_hit])
        nearest_hit = vec(data[sel_neigh_same, :][idx_nearest_hit, :])

        # Get indices of neighbors with different class in distances vector and find nearest miss.
        sel_neigh_miss = target .!= target[idx]
        neigh_idx_miss = square_to_vec(row_idxs[sel_neigh_miss], col_idxs[sel_neigh_miss], size(data, 1)) .+ 1
        idx_nearest_miss = argmin(dists[neigh_idx_miss])
        nearest_miss = vec(data[sel_neigh_miss, :][idx_nearest_miss, :])

        ### Weights Update ###

        if f_type == "continuous"
            # If features continuous.
            weights = weights .- (abs.(data[idx, :] .- nearest_hit)./(max_f_vals .- min_f_vals .+ eps(Float64)))./m .+
                (abs.(data[idx, :] .- nearest_miss)./(max_f_vals .- min_f_vals .+ eps(Float64)))./m
        elseif f_type == "discrete"
            # If features discrete.
            weights = weights .- Int64.(data[idx, :] .!= nearest_hit)./m .+ Int64.(data[idx, :] .!= nearest_miss)./m
        else
            throw(DomainError(f_type, "f_type can only be equal to \"continuous\" or \"discrete\"."))
        end

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

    end

    # Return computed feature weights.
    return weights
end


text:
module Makie

module ContoursHygiene
    import Contour
end

using .ContoursHygiene
const Contours = ContoursHygiene.Contour
using Base64

using LaTeXStrings
using MathTeXEngine
using Random
using FFMPEG # get FFMPEG on any system!
using Observables
using GeometryBasics
using IntervalSets
using PlotUtils
using ColorBrewer
using ColorTypes
using Colors
using ColorSchemes
using FixedPointNumbers
using Packing
using SignedDistanceFields
using Markdown
using DocStringExtensions # documentation
using Serialization # serialize events
using StructArrays
using StaticArrays
# Text related packages
using FreeType
using FreeTypeAbstraction
using UnicodeFun
using LinearAlgebra
using Statistics

import RelocatableFolders
import StatsBase
import Distributions
import KernelDensity
import Isoband
import PolygonOps
import GridLayoutBase
import ImageIO
import FileIO
import SparseArrays
using MakieCore
using OffsetArrays

using GeometryBasics: widths, positive_widths, VecTypes, AbstractPolygon, value
using Distributions: Distribution, VariateForm, Discrete, QQPair, pdf, quantile, qqbuild

import FileIO: save
using Printf: @sprintf
using StatsFuns: logit, logistic
# Imports from Base which we don't want to have to qualify
using Base: RefValue
using Base.Iterators: repeated, drop
import Base: getindex, setindex!, push!, append!, parent, get, get!, delete!, haskey
using Observables: listeners, to_value, notify

using MakieCore: SceneLike, AbstractScreen, ScenePlot, AbstractScene, AbstractPlot, Transformable, Attributes, Combined, Theme, Plot
using MakieCore: Heatmap, Image, Lines, LineSegments, Mesh, MeshScatter, Scatter, Surface, Text, Volume
using MakieCore: ConversionTrait, NoConversion, PointBased, SurfaceLike, ContinuousSurface, DiscreteSurface, VolumeLike
using MakieCore: Key, @key_str, Automatic, automatic, @recipe
using MakieCore: Pixel, px, Unit, Billboard
using MakieCore: not_implemented_for
import MakieCore: plot, plot!, theme, plotfunc, plottype, merge_attributes!, calculated_attributes!,
get_attribute, plotsym, plotkey, attributes, used_attributes
import MakieCore: heatmap, image, lines, linesegments, mesh, meshscatter, scatter, surface, text, volume
import MakieCore: heatmap!, image!, lines!, linesegments!, mesh!, meshscatter!, scatter!, surface!, text!, volume!
import MakieCore: convert_arguments, convert_attribute, default_theme, conversion_trait

export @L_str
export ConversionTrait, NoConversion, PointBased, SurfaceLike, ContinuousSurface, DiscreteSurface, VolumeLike
export Pixel, px, Unit, plotkey, attributes, used_attributes

const RealVector{T} = AbstractVector{T} where T <: Number
const RGBAf = RGBA{Float32}
const RGBf = RGB{Float32}
const NativeFont = FreeTypeAbstraction.FTFont

include("documentation/docstringextension.jl")
include("utilities/quaternions.jl")
include("interaction/PriorityObservable.jl")
include("types.jl")
include("utilities/utilities.jl")
include("utilities/texture_atlas.jl")
include("interaction/nodes.jl")
include("interaction/liftmacro.jl")
include("colorsampler.jl")
include("patterns.jl")
# Basic scene/plot/recipe interfaces + types
include("scenes.jl")

include("theming.jl")
include("themes/theme_ggplot2.jl")
include("themes/theme_black.jl")
include("themes/theme_minimal.jl")
include("themes/theme_light.jl")
include("themes/theme_dark.jl")
include("interfaces.jl")
include("units.jl")
include("conversions.jl")
include("shorthands.jl")

# camera types + functions
include("camera/projection_math.jl")
include("camera/camera.jl")
include("camera/camera2d.jl")
include("camera/camera3d.jl")
include("camera/old_camera3d.jl")

# basic recipes
include("basic_recipes/convenience_functions.jl")
include("basic_recipes/annotations.jl")
include("basic_recipes/arc.jl")
include("basic_recipes/arrows.jl")
include("basic_recipes/axis.jl")
include("basic_recipes/band.jl")
include("basic_recipes/barplot.jl")
include("basic_recipes/buffers.jl")
include("basic_recipes/contours.jl")
include("basic_recipes/contourf.jl")
include("basic_recipes/error_and_rangebars.jl")
include("basic_recipes/pie.jl")
include("basic_recipes/poly.jl")
include("basic_recipes/scatterlines.jl")
include("basic_recipes/spy.jl")
include("basic_recipes/stairs.jl")
include("basic_recipes/stem.jl")
include("basic_recipes/streamplot.jl")
include("basic_recipes/timeseries.jl")
include("basic_recipes/volumeslices.jl")
include("basic_recipes/wireframe.jl")

# layouting of plots
include("layouting/transformation.jl")
include("layouting/data_limits.jl")
include("layouting/layouting.jl")
include("layouting/boundingbox.jl")
# more default recipes
# statistical recipes
include("stats/conversions.jl")
include("stats/hist.jl")
include("stats/density.jl")
include("stats/ecdf.jl")
include("stats/distributions.jl")
include("stats/crossbar.jl")
include("stats/boxplot.jl")
include("stats/violin.jl")

# Interactiveness
include("interaction/events.jl")
include("interaction/interactive_api.jl")
include("interaction/inspector.jl")

# documentation and help functions
include("documentation/documentation.jl")
include("display.jl")

# help functions and supporting functions
export help, help_attributes, help_arguments

# Abstract/Concrete scene + plot types
export AbstractScene, SceneLike, Scene, AbstractScreen
export AbstractPlot, Combined, Atomic, OldAxis

# Theming, working with Plots
export Attributes, Theme, attributes, default_theme, theme, set_theme!, with_theme, update_theme!
export xlims!, ylims!, zlims!
export xlabel!, ylabel!, zlabel!

export theme_ggplot2
export theme_black
export theme_minimal
export theme_light
export theme_dark

export xticklabels, yticklabels, zticklabels
export xtickrange, ytickrange, ztickrange
export xticks!, yticks!, zticks!
export xtickrotation, ytickrotation, ztickrotation
export xtickrotation!, ytickrotation!, ztickrotation!

# Observable/Signal related
export Observable, Observable, lift, map_once, to_value, on, onany, @lift, off, connect!

# utilities and macros
export @recipe, @extract, @extractvalue, @key_str, @get_attribute
export broadcast_foreach, to_vector, replace_automatic!

# conversion infrastructure
export @key_str, convert_attribute, convert_arguments
export to_color, to_colormap, to_rotation, to_font, to_align, to_textsize
export to_ndim, Reverse

# Transformations
export translated, translate!, scale!, rotate!, Accum, Absolute
export boundingbox, insertplots!, center!, translation, data_limits

# Spaces for widths and markers
const PixelSpace = Pixel
export SceneSpace, PixelSpace, Pixel

# camera related
export AbstractCamera, EmptyCamera, Camera, Camera2D, Camera3D, cam2d!, cam2d
export campixel!, campixel, cam3d!, cam3d_cad!, old_cam3d!, old_cam3d_cad!
export update_cam!, rotate_cam!, translate_cam!, zoom!
export pixelarea, plots, cameracontrols, cameracontrols!, camera, events
export to_world

# picking + interactive use cases + events
export mouseover, onpick, pick, Events, Keyboard, Mouse, mouse_selection
export ispressed, Exclusively
export register_callbacks
export window_area, window_open, mouse_buttons, mouse_position, scroll,
       keyboard_buttons, unicode_input, dropped_files, hasfocus, entered_window
export disconnect!
export DataInspector
export Consume

# Raymarching algorithms
export RaymarchAlgorithm, IsoValue, Absorption, MaximumIntensityProjection, AbsorptionRGBA, IndexedAbsorptionRGBA
export Billboard

# Reexports of
# Color/Vector types convenient for 3d/2d graphics
export Quaternion, Quaternionf, qrotation
export RGBAf, RGBf, VecTypes, RealVector
export Transformation
export Sphere, Circle
export Vec4f, Vec3f, Vec2f, Point4f, Point3f, Point2f
export Vec, Vec2, Vec3, Vec4, Point, Point2, Point3, Point4
export (..)
export Rect, Rectf, Rect2f, Rect2i, Rect3f, Rect3i, Rect3, Recti, Rect2
export widths, decompose

# building blocks for series recipes
export PlotSpec

export plot!, plot

export Stepper, replay_events, record_events, RecordEvents, record, VideoStream
export VideoStream, recordframe!, record
export save

# colormap stuff from PlotUtils, and showgradients
export cgrad, available_gradients, showgradients

export Pattern

const ASSETS_DIR = RelocatableFolders.@path joinpath(@__DIR__, "..", "assets")
assetpath(files...) = normpath(joinpath(ASSETS_DIR, files...))

export assetpath
# default icon for Makie
function icon()
    path = assetpath("icons")
    imgs = FileIO.load.(joinpath.(path, readdir(path)))
    icons = map(img-> RGBA{Colors.N0f8}.(img), imgs)
    return reinterpret.(NTuple{4,UInt8}, icons)
end

function logo()
    FileIO.load(assetpath("logo.png"))
end

function __init__()
    cfg_path = joinpath(homedir(), ".config", "makie", "theme.jl")
    if isfile(cfg_path)
        @warn "The global configuration file is no longer supported." *
        "Please include the file manually with `include(\"$cfg_path\")` before plotting."
    end
end

include("figures.jl")
export content
export resize_to_layout!

include("makielayout/MakieLayout.jl")
# re-export MakieLayout
for name in names(MakieLayout)
    @eval import .MakieLayout: $(name)
    @eval export $(name)
end

include("figureplotting.jl")
include("basic_recipes/series.jl")
include("basic_recipes/text.jl")

export Heatmap, Image, Lines, LineSegments, Mesh, MeshScatter, Scatter, Surface, Text, Volume
export heatmap, image, lines, linesegments, mesh, meshscatter, scatter, surface, text, volume
export heatmap!, image!, lines!, linesegments!, mesh!, meshscatter!, scatter!, surface!, text!, volume!

export PointLight, EnvironmentLight, AmbientLight, SSAO

end # module


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder

# Collection of sources required to build LCIOWrapBuilder
sources = [
    "LCIOWrapBuilder"
]

# Bash recipe for building across all platforms
function getscript(version)
	shortversion = version[1:3]
	return """
	cd \$WORKSPACE/srcdir
	mkdir build && cd build
	cmake -DCMAKE_INSTALL_PREFIX=\$prefix -DCMAKE_TOOLCHAIN_FILE=/opt/\$target/\$target.toolchain -DCMAKE_FIND_ROOT_PATH=\$prefix -DJulia_PREFIX=\$prefix ..
	VERBOSE=ON cmake --build . --config Release --target install
	"""
end

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = Platform[
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc7, :cxx11)),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc8, :cxx11)),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc7)),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc8)),
]

# The products that we will ensure are always built
products(prefix) = [
    LibraryProduct(prefix, "liblciowrap", :lciowrap)
]

# Dependencies that must be installed before this package can be built
dependencies = [
	"https://github.com/JuliaInterop/libcxxwrap-julia/releases/download/v0.6.2/build_libcxxwrap-julia-1.0.v0.6.2.jl"
	"https://github.com/JuliaPackaging/JuliaBuilder/releases/download/v1.0.0-2/build_Julia.v1.0.0.jl"
	"https://github.com/jstrube/LCIOBuilder/releases/download/v2.12.1-4/build_LCIOBuilder.v2.12.1-4.jl"
]

# Build the tarballs, and possibly a `build.jl` as well.
version_number = get(ENV, "TRAVIS_TAG", "")
if version_number == ""
    version_number = "v0.99"
end
build_tarballs(ARGS, "LCIOWrapBuilder-1.0", VersionNumber(version_number), sources, getscript("1.0.0"), platforms, products, dependencies)


text: #not needed (yet?)- store outputs here and cross check?


text:
##file to include in ComplexModeler.jl which contains all files associated to GOC

include("read_IIDM.jl")
include("read_IIDMeod.jl")

include("IIDMGenerator.jl")
include("IIDMLine_π.jl")
include("IIDMLoad.jl")
include("IIDMVolt.jl")

include("IIDMVoltMeasure.jl")
include("IIDMLine_πMeasure.jl")

max_stars_repo_name: Gimaju/GOC

max_issues_repo_name: Gimaju/GOC

max_forks_repo_name: Gimaju/GOC


text:
"""
    struct Homothecy{D,T} <:Field
      origin::Point{D,T}
      scaling::Point{D,T}
    end

This type is slightly more general than a homothecy since we allow anisotropic scalings.

Implements the `Field` interface up to first order derivatives
"""
struct Homothecy{D,T} <:Field
  origin::Point{D,T}
  scaling::Point{D,T}
end

function field_cache(f::Homothecy,x)
  y = copy(x)
  CachedArray(y)
end

@inline function evaluate_field!(cache,f::Homothecy,x)
  setsize!(cache,size(x))
  y = cache.array
  @inbounds for i in eachindex(x)
    xi = x[i]
    yi = _apply_homothecy(f,xi)
    y[i] = yi
  end
  y
end

function _apply_homothecy(h,x)
  y = zero(mutable(x))
  t = h.origin
  s = h.scaling
  for i in 1:length(y)
    y[i] = s[i]*(x[i]-t[i]) + t[i]
  end
  Point(y)
end

struct HomothecyGrad{D,T} <: Field
  scaling::Point{D,T}
end

function field_gradient(h::Homothecy)
  HomothecyGrad(h.scaling)
end

function field_cache(f::HomothecyGrad,x)
  xi = testitem(x)
  T = typeof(xi)
  G = gradient_type(T,xi)
  j = similar(x,G)
  CachedArray(j)
end

function evaluate_field!(cache,f::HomothecyGrad,x)
  setsize!(cache,size(x))
  y = cache.array
  G = eltype(y)
  yi = _apply_homothecy_gradient(f,G)
  @inbounds for i in eachindex(y)
    y[i] = yi
  end
  y
end

function _apply_homothecy_gradient(h,G)
  y = zero(mutable(G))
  s = h.scaling
  for i in 1:length(s)
    y[i,i] = s[i]
  end
  TensorValue(y)
end


text:
"""
    dfs(digraph, start_node, visitor)

Depth-first search starting from `start_node`. The signature of `visitor` should
be `(prev::Int, curr::Int) -> Union{Bool, Void}`. If visitor returns
(optionally) `false`, then `dfs` stops.

# Examples
```
dg = Digraph()
n1 = add_node!(dg)
n2 = add_node!(dg)
n3 = add_node!(dg)
n4 = add_node!(dg)
add_edge!(dg, n1, n2)
add_edge!(dg, n2, n3)
add_edge!(dg, n1, n4)
order = []
function visitor(p, c)
    push!(order, c)
end
dfs(dg, n1, visitor)
```
"""
function dfs(dg::Digraph, start::Integer, visitor::Function)
    visited = fill(false, node_count(dg))
    dfs_rec(dg, Int(start), visitor, Int(0), visited)
end

export dfs

function dfs_rec(dg::Digraph, curr::Int, visitor::Function, prev::Int, visited::Vector{Bool})
    res = visitor(prev, curr)
    cont::Bool = true
    if isa(res, Bool)
        cont = res
    end
    visited[curr] = true
    if cont
        for eid in out_edges(dg, curr)
            dst = dst_node(dg, eid)
            if !visited[dst]
                cont = dfs_rec(dg, dst, visitor, curr, visited)
                if !cont
                    break
                end
            end
        end
    end
    return cont
end


text:
module Beep
using WAV

export beep
export @beep

include("notifyme.jl")

end