text:
module panel
import Term:
    split_lines,
    get_last_valid_str_idx,
    reshape_text,
    do_by_line,
    join_lines,
    truncate,
    textlen

import ..consoles: console_width
import ..measure: Measure
import ..renderables: AbstractRenderable, RenderablesUnion, Renderable, RenderableText
import ..segment: Segment
using ..box
import ..layout: Padding, vstack
import ..style: apply_style

export Panel, TextBox

abstract type AbstractPanel <: AbstractRenderable end

# ---------------------------------------------------------------------------- #
#                                     PANEL                                    #
# ---------------------------------------------------------------------------- #
"""
    Panel

`Renderable` with a panel surrounding some content:
        ╭──────────╮
        │ my panel │
        ╰──────────╯
"""
mutable struct Panel <: AbstractPanel
    segments::Vector
    measure::Measure
    title::Union{Nothing,String}
    title_style::Union{String,Nothing}
    style::Union{String,Nothing}
end

"""
    Panel(
        content::RenderablesUnion;
        title::Union{Nothing, String}=nothing,
        title_style::Union{String, Nothing}=nothing,
        title_justify::Symbol=:left,
        subtitle::Union{String, Nothing}=nothing,
        subtitle_style::Union{String, Nothing}=nothing,
        subtitle_justify::Symbol=:left,
        width::Union{Nothing, Symbol, Int}=:fit,
        height::Union{Nothing, Int}=nothing,
        style::Union{String, Nothing}=nothing,
        box::Symbol=:ROUNDED,
        justify=:left
    )

`Panel` constructor to fit a panel to a piece of (renderable) content.

`title` can be used to specify a title to be addded to the top row and
`title_style` and `title_justify` set its appearance and position.
Same for `subtitle` but for the panel's bottom row.
`width` and `height` are used to set the `Panel`'s size. If not passed
they are computed to fit tot the `content`'s size.
"""
function Panel(
    content::RenderablesUnion;
    title::Union{Nothing,String} = nothing,
    title_style::Union{String,Nothing} = nothing,
    title_justify::Symbol = :left,
    subtitle::Union{String,Nothing} = nothing,
    subtitle_style::Union{String,Nothing} = nothing,
    subtitle_justify::Symbol = :left,
    width::Union{Nothing,Symbol,Int} = :fit,
    height::Union{Nothing,Int} = nothing,
    style::Union{String,Nothing} = nothing,
    box::Symbol = :ROUNDED,
    justify = :left,
)
    box = eval(box)  # get box object from symbol

    # style stuff
    title_style = isnothing(title_style) ? style : title_style
    # σ(s) = Segment(s, style)  # applies the main style markup to a string to make a segment
    σ(s) = "[$style]$s[/$style]"  # applies the main style markup to a string to make a segment

    # get size of panel to fit the content
    if content isa AbstractString && width isa Number
        content = do_by_line((ln) -> reshape_text(ln, width - 4), content)
    end

    content_measure = Measure(content)
    panel_measure = Measure(content_measure.w + 2, content_measure.h + 2)

    if width == :fit
        width = panel_measure.w + 2
    else
        width = isnothing(width) ? console_width() - 4 : width
    end
    @assert width > content_measure.w "Width too small for content '$content' with $content_measure"
    panel_measure.w = width

    # create segments
    segments::Vector{Segment} = []

    # create top/bottom rows with titles
    top = get_title_row(
        :top,
        box,
        title;
        width = width - 2,
        style = style,
        title_style = title_style,
        justify = title_justify,
    )

    bottom = get_title_row(
        :bottom,
        box,
        subtitle;
        width = width - 2,
        style = style,
        title_style = subtitle_style,
        justify = subtitle_justify,
    )

    # add a panel row for each content row
    push!(segments, top)
    left, right = σ(string(box.mid.left)), σ(string(box.mid.right))
    content_lines = split_lines(content)

    for n = 1:content_measure.h
        # get padding
        line = content_lines[n]
        padding = Padding(line, width - 2, justify)

        # make line
        segment = Segment(left * padding.left * apply_style(line) * padding.right * right)

        push!(segments, segment)
    end

    # add empty lines to ensure target height is reached
    if !isnothing(height) && content_measure.h < height
        for i = 1:(height-content_measure.h)
            line = " "^(width - 2)
            push!(segments, Segment(left * line * right))
        end
    end
    push!(segments, bottom)

    return Panel(
        segments,
        panel_measure,
        isnothing(title) ? title : title,
        title_style,
        style,
    )
end

"""
    Panel(renderables; kwargs...)

`Panel` constructor for creating a panel out of multiple renderables at once.
"""
function Panel(renderables...; width::Union{Nothing,Int,Symbol} = nothing, kwargs...)
    rend_width = isnothing(width) || width isa Symbol ? width : width - 1
    renderable = vstack(Renderable.(renderables, width = rend_width)...)

    return Panel(renderable; width = width, kwargs...)
end

# ---------------------------------------------------------------------------- #
#                                    TextBox                                   #
# ---------------------------------------------------------------------------- #

"""
    TextBox

Creates a `Panel` and fits input text to it.
The pannel is hidden so that the result is just a text box.
"""
mutable struct TextBox <: AbstractPanel
    segments::Vector
    measure::Measure
end

"""
    TextBox(
        text::Union{Vector, AbstractString};
        width::Union{Nothing, Int}=nothing,
        title::Union{Nothing, String}=nothing,
        title_style::Union{String, Nothing}="default",
        title_justify::Symbol=:left,
        subtitle::Union{String, Nothing}=nothing,
        subtitle_style::Union{String, Nothing}="default",
        subtitle_justify::Symbol=:left,
        justify::Symbol=:left,
        fit::Symbol=:fit,
        )

Creates an hidden `Panel` with `text` in it.

If a `width` is passed, the input `text` is reshaped to have
that size, unless `fit=:truncate` in which case it's cut to size.
If no `width` is passed and `fit=:fit` the `TextBox`'s size
matches the size of the input `text`.
Other arguments behave like `Panel`.

See also [`Panel`](@ref).
"""
function TextBox(
    text::Union{Vector,AbstractString};
    width::Union{Nothing,Int} = nothing,
    title::Union{Nothing,String} = nothing,
    title_style::Union{String,Nothing} = "default",
    title_justify::Symbol = :left,
    subtitle::Union{String,Nothing} = nothing,
    subtitle_style::Union{String,Nothing} = "default",
    subtitle_justify::Symbol = :left,
    justify::Symbol = :left,
    fit::Symbol = :fit,
)

    # fit text
    width = isnothing(width) ? console_width() - 4 : width
    if !isnothing(width)
        text = do_by_line((ln) -> reshape_text(ln, width - 4), text)
    elseif fit == :truncate
        text = do_by_line(ln -> truncate(ln, width - 4), text)
    elseif fit == :fit
        width = Measure(text).w + 4
        width = width < 4 ? 4 : width
    end

    panel = Panel(
        text,
        style = "hidden",
        title = title,
        title_style = title_style,
        title_justify = title_justify,
        subtitle = subtitle,
        subtitle_style = subtitle_style,
        subtitle_justify = subtitle_justify,
        justify = justify,
        width = width,
    )

    return TextBox(panel.segments, panel.measure)

end

TextBox(texts...; kwargs...) = TextBox(join_lines(texts); kwargs...)

end


text:
module Catalan

warn("**Catalan.jl IS DEPRECATED**. Please use the new Combinatorics.jl package.")

using Polynomial

export  bell,
        catalan,
        derangement,
        doublefactorial,
        fibonacci,
        hyperfactorial,
        jacobisymbol,
        lassalle,
        legendresymbol,
        lucas,
        multifactorial,
        multinomial,
        primorial,
        stirlings1,
        subfactorial

include("partitions.jl")
include("youngdiagrams.jl")

# Returns the n-th Bell number
function bell(bn::Integer)
    if bn < 0
        throw(DomainError())
    else
        n = BigInt(bn)
    end
    list = Array(BigInt, div(n*(n+1), 2))
    list[1] = 1
    for i = 2:n
        beg = div(i*(i-1),2)
        list[beg+1] = list[beg]
        for j = 2:i
            list[beg+j] = list[beg+j-1]+list[beg+j-i]
        end
    end
    return list[end]
end

# Returns the n-th Catalan number
function catalan(bn::Integer)
    if bn<0
        throw(DomainError())
    else
        n = BigInt(bn)
    end
    div(binomial(2*n, n), (n + 1))
end

# The number of permutations of n with no fixed points (subfactorial)
function derangement(sn::Integer)
    n = BigInt(sn)
    return num(factorial(n)*sum([(-1)^k//factorial(k) for k=0:n]))
end
subfactorial(n::Integer) = derangement(n)

function doublefactorial(n::Integer)
    if n < 0
        throw(DomainError())
    end
    z = BigInt()
    ccall((:__gmpz_2fac_ui, :libgmp), Void,
        (Ptr{BigInt}, Uint), &z, uint(n))
    return z
end

function fibonacci(n::Integer)
    if n < 0
        throw(DomainError())
    end
    z = BigInt()
    ccall((:__gmpz_fib_ui, :libgmp), Void,
        (Ptr{BigInt}, Uint), &z, uint(n))
    return z
end

# Hyperfactorial
hyperfactorial(n::Integer) = prod([i^i for i = BigInt(2):n])

function jacobisymbol(a::Integer, b::Integer)
    ba = BigInt(a)
    bb = BigInt(b)
    return ccall((:__gmpz_jacobi, :libgmp), Int,
        (Ptr{BigInt}, Ptr{BigInt}), &ba, &bb)
end

#Computes Lassalle's sequence
#OEIS entry A180874
function lassalle(m::Integer)
   A = ones(BigInt,m)
   for n=2:m
       A[n]=(-1)^(n-1) * (catalan(n) + sum([(-1)^j*binomial(2n-1, 2j-1)*A[j]*catalan(n-j) for j=1:n-1]))
   end
   A[m]
end

function legendresymbol(a::Integer, b::Integer)
    ba = BigInt(a)
    bb = BigInt(b)
    return ccall((:__gmpz_legendre, :libgmp), Int,
        (Ptr{BigInt}, Ptr{BigInt}), &ba, &bb)
end

function lucas(n::Integer)
    if n < 0
        throw(DomainError())
    end
    z = BigInt()
    ccall((:__gmpz_lucnum_ui, :libgmp), Void,
        (Ptr{BigInt}, Uint), &z, uint(n))
    return z
end

function multifactorial(n::Integer, m::Integer)
    if n < 0
        throw(DomainError())
    end
    z = BigInt()
    ccall((:__gmpz_mfac_uiui, :libgmp), Void,
        (Ptr{BigInt}, Uint, Uint), &z, uint(n), uint(m))
    return z
end

# Multinomial coefficient where n = sum(k)
function multinomial(k...)
    s = 0
    result = 1
    for i in k
        s += i
        result *= binomial(s, i)
    end
    result
end

function primorial(n::Integer)
    if n < 0
        throw(DomainError())
    end
    z = BigInt()
    ccall((:__gmpz_primorial_ui, :libgmp), Void,
        (Ptr{BigInt}, Uint), &z, uint(n))
    return z
end

# Returns s(n, k), the signed Stirling number of first kind
function stirlings1(n::Integer, k::Integer)
    p = poly(0:(n-1))
    p[n - k + 1]
end

end # module

ext: jl


text:
#=
   functions to enfold:
   organized by applicable signature.

   In these signature-based collections, the arguments to a given function share eltype.
=#

const scalar_functions_of_one_arg = map(Symbol, (
    abs, (-),
    abs2, inv,
    deg2rad, rad2deg,
    mod2pi, rem2pi,
    sqrt, cbrt,
    exp, exp10, exp2, expm1,
    log, log10, log1p, log2,
    sin, sinpi, sinc, cos, cospi, cosc,
    tan, csc, sec, cot,
    asin, acos, atan, acsc, asec, acot,
    sind, cosd, tand, cscd, secd, cotd,
    asind, acosd, atand, acscd, asecd, acotd,
    sinh, cosh, tanh, csch, sech, coth,
    asinh, acosh, atanh, acsch, asech, acoth,
));

# modf, sincos, sincospi, sincosd, return 2
# evalpoly

const scalar_functions_of_two_args = map(Symbol, (
    flipsign, copysign,
    mod, rem, div, fld, cld, hypot,
));

const scalar_functions_of_three_args = map(Symbol, (
    clamp, hypot
));

const vector_functions_of_one_arg  = ()
const vector_functions_of_two_args = ()

const vector_adjoint_functions_of_two_args = ()
const adjoint_vector_functions_of_two_args = ()

const matrix_vector_functions_of_two_args = ()
const vector_matrix_functions_of_two_args = ()

const matrix_functions_of_one_arg  = (
   cond, det, tr, adjoint, transpose,
   eigvals, svdvals, eigvecs,
);
const matrix_functions_of_two_args = ()

const matrix_adjoint_functions_of_two_args = ()
const adjoint_matrix_functions_of_two_args = ()

#=
     provide(x) uses reinterpret

     provide(x::Vector{Robust32}), provide(x::Matrix{Robust32})
        presents an Array of Robust32s as an Array of Float64s

     provide(x::Vector{Float64}), provide(x::Matrix{Float64})
        presents an Array of Float64s as an Array of Robust32s

     provide(x::Vector{ComplexR32}), provide(x::Matrix{ComplexR32})
        presents an Array of ComplexR32s as an Array of ComplexF64s

     provide(x::Vector{ComplexF64}), provide(x::Matrix{ComplexF64})
        presents an Array of ComplexF64s as an Array of ComplexR32s
=#

for (R,F) in ((:Robust32, :Float64), (:ComplexR32, :ComplexF64))
  for A in (:Vector, :Matrix)
    @eval begin
        provide(x::$A{$R}) = reinterpret($F, x)
        provide(x::$A{$F}) = reinterpret($R, x)
    end
  end
end

#=
    enfold(fn, ..) simplifies implementing functions correctly
=#

for F in scalar_functions_of_one_arg
  @eval enfold($F, x::Robust32) = provide(fn(provide(x)))
end

for F in scalar_functions_of_two_args
  @eval enfold($F, x::Robust32, y::Robust32) = provide(fn(provide(x), provide(y)))
end

for F in scalar_functions_of_three_args
  @eval enfold($F, x::Robust32, y::Robust32, z::Robust32) = provide(fn(provide(x), provide(y), provide(z)))
end

for F in vector_functions_of_one_arg
  @eval enfold($F, x::Vector{Robust32}) = provide(fn(provide(x)))
end

for F in vector_functions_of_two_args
  @eval enfold($F, x::Vector{Robust32}, y::Vector{Robust32}) = provide(fn(provide(x), provide(y)))
end

for F in vector_adjoint_functions_of_two_args
  @eval enfold($F, x::Vector{Robust32}, y::Adjoint{Robust32, Vector{Robust32}}) = provide(fn(provide(x), provide(y)))
end

for F in adjoint_vector_functions_of_two_args
  @eval enfold($F, x::Adjoint{Robust32, Vector{Robust32}}, y::Vector{Robust32}) = provide(fn(provide(x), provide(y)))
end

for F in matrix_functions_of_one_arg
  @eval enfold($F, x::Matrix{Robust32}) = provide(fn(provide(x)))
end

for F in matrix_functions_of_two_args
  @eval enfold($F, x::Matrix{Robust32}, y::Matrix{Robust32}) = provide(fn(provide(x), provide(y)))
end

for F in vector_functions_of_one_arg
  @eval enfold($F, x::Vector{Robust32}) = provide(fn(provide(x)))
end

for F in matrix_vector_functions_of_two_args
  @eval enfold($F, x::Matrix{Robust32}, y::Vector{Robust32}) = provide(fn(provide(x), provide(y)))
end

for F in vector_matrix_functions_of_two_args
  @eval enfold($F, x::Vector{Robust32}, y::Matrix{Robust32}) = provide(fn(provide(x), provide(y)))
end

for F in matrix_adjoint_functions_of_two_args
  @eval enfold($F, x::Matrix{Robust32}, y::Adjoint{Robust32, Vector{Robust32}}) = provide(fn(provide(x), provide(y)))
end

for F in adjoint_matrix_functions_of_two_args
  @eval enfold($F, x::Adjoint{Robust32, Vector{Robust32}}, y::Matrix{Robust32}) = provide(fn(provide(x), provide(y)))
end


text:
module trieMusic
using Plots
using Combinatorics

#シーケンスを階差数列にして返却する
function seqenceToDifferenceSequence(sequence)
    differenceSequence = []
    beforeElm = sequence[1]
    for elm in sequence[2:end]
        push!(differenceSequence, elm - beforeElm)
        beforeElm = elm
    end
    return differenceSequence
end

#シーケンスを増加または減少の2値（1, -1）に変換して返却する
function sequenceToBinarySequence(sequence)
    binarySequence = []
    for elm in sequence
        if elm > 0
            push!(binarySequence, 1)
        else
            push!(binarySequence, -1)
        end
    end
    return binarySequence
end

#シーケンスを独自ルールでLOUDSのTrie木構造に変換して返却する
function sequenceToLouds(sequence)
    #トライ木をLOUDSで表現するためのビット配列　初期値として1番ノードを指すtと、
    #ルートノードの終端を指すfと、
    #1番ノードの終端を指すfが入ってる
    bitAry = BitArray([true,false,false])
    #各ノードに対応するラベル　形式は[sequenceの要素, その要素が出現したインデックス1, その要素が出現したインデックス2,...]
    label = []
    #検索対象となるノードの情報。[何階層目か, その階層の中で左から何番目のノードか、bitIdx]
    searchTargetNodes = []
    #シーケンス番号
    seqIdx = 0
    #各階層のノード数　ルート直下からスタート
    eachLevelNodesAccumulatedNumber = []
    #各階層のbitAry上の区切り位置
    levelBoundaryBaIdx = [3]

    for elm in sequence
        seqIdx = seqIdx + 1
        #検索対象となるノードがあれば過去のシーケンスを検索開始
        if length(searchTargetNodes) > 0
            tmpSearchTargetNodes = []
            for searchTargetNode in searchTargetNodes
                #検索対象となるノードが持つsequence上のインデックスを取得。
                #ラベルから取得するが、現在のseqIdx　- 1 と同じ値の要素は取得しない　あくまで過去の要素が対象
                targetLabelIdx = 0
                if searchTargetNode[1] == 1
                    targetLabelIdx = searchTargetNode[2]
                else
                    targetLabelIdx = eachLevelNodesAccumulatedNumber[searchTargetNode[1] - 1] + searchTargetNode[2]
                end
                searchTargetSeqIdxes = filter!(e->e ≠ seqIdx - 1, label[targetLabelIdx][2:end])
                #ラベルから取得した過去のsequence上のインデックスの右隣が検索対象
                searchTargetChildrenSeqIdxes = searchTargetSeqIdxes + repeat([1], length(searchTargetSeqIdxes))
                #シーケンス上に、今来ているelmと一致したインデックスを取得
                foundMatchLabelIdxes = searchTargetChildrenSeqIdxes[findall(isequal(elm), [sequence[i] for i in searchTargetChildrenSeqIdxes])]
                #1つでも一致したシーケンスがあれば、bitAryとlabelを更新する必要がある
                if length(foundMatchLabelIdxes) > 0
                    #foundMatchLabelIdxesに現在のseqIdxも追加する。その配列をラベルに追加するため。
                    push!(foundMatchLabelIdxes, seqIdx)
                    #子ノードの情報（子ノードの数、子ノードと同じ階層の上の従兄弟の数、子ノードの終端のfalseのbitIdx）を取得する
                    bitIdx, childrenNumber, brotherChildrenNumber = getChild(levelBoundaryBaIdx, searchTargetNode, bitAry)
                    #子ノードの開始位置の一つ前のidxを取得する
                    childLabelStartIdx = eachLevelNodesAccumulatedNumber[searchTargetNode[1]] + brotherChildrenNumber
                    #子ノードが存在していたら
                    if childrenNumber > 0
                        #子ノードのラベルインデックスを取得
                        childrenLabelIdxes = childLabelStartIdx + 1:childLabelStartIdx + childrenNumber
                        #子ノードの中で一致したノードのラベルインデックスを取得
                        matchChildLabelIdx = findfirst(isequal(elm), [elmLabel[1] for elmLabel in label[childrenLabelIdxes]])
                        #一致したノードがあれば
                        if matchChildLabelIdx != nothing
                            #既存のノードラベルにfoundMatchLabelIdxesを追加する
                            childLabelStartIdx += matchChildLabelIdx

                            label[childLabelStartIdx] = insert!(unique!(sort(append!(label[childLabelStartIdx][2:end], foundMatchLabelIdxes))), 1, label[childLabelStartIdx][1])

                            push!(tmpSearchTargetNodes, [searchTargetNode[1] + 1, brotherChildrenNumber + matchChildLabelIdx, bitIdx - childrenNumber - 1 + matchChildLabelIdx])
                        #一致したノードがなければノード追加
                        else
                            #子ノード追加
                            insert!(bitAry, bitIdx, true)
                            #各階層のbitAry上の区切り位置更新
                            #追加した子ノードの子ノードが存在しているかもしれない階層まで、区切り位置の配列が情報を持っていなければ追加
                            #適切な位置にfalseを追加
                            insert!(bitAry, getChild(levelBoundaryBaIdx, [searchTargetNode[1] + 1, brotherChildrenNumber + childrenNumber + 1], bitAry)[1], false)
                            #子ノードを追加した階層より右側の区切り位置を1増やす
                            levelBoundaryBaIdx[searchTargetNode[1] + 2] += 1
                            levelBoundaryBaIdx[searchTargetNode[1] + 3:end] += repeat([2], length(levelBoundaryBaIdx[searchTargetNode[1] + 3:end]))
                            #子ノードを追加した階層から右側のノード数を1増やす
                            eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end] += repeat([1], length(eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end]))
                            #labelに追加
                            insert!(label, childrenLabelIdxes[end] + 1, insert!(foundMatchLabelIdxes, 1, elm))
                            #既に登録されているsearchTargetNodesを更新
                            tmpSearchTargetNodes = updateSearchTargetNodes(tmpSearchTargetNodes, bitIdx, searchTargetNode)
                            #searchTargetNodeを追加する。
                            push!(tmpSearchTargetNodes, [searchTargetNode[1] + 1, brotherChildrenNumber + childrenNumber + 1, bitIdx])
                        end
                    #子ノードが存在していなければ子ノードを追加する
                    else
                        #子ノード追加
                        insert!(bitAry, bitIdx, true)

                        #各階層のbitAry上の区切り位置更新
                        #追加した子ノードの子ノードが存在しているかもしれない階層まで、区切り位置の配列が情報を持っていなければ追加
                        if length(levelBoundaryBaIdx) < searchTargetNode[1] + 2
                            push!(levelBoundaryBaIdx, levelBoundaryBaIdx[end] + length(bitAry[levelBoundaryBaIdx[end]:end]))
                            #falseも追加
                            push!(bitAry, false)
                        #既に存在している場合
                        else
                            #適切な位置にfalseを追加
                            insert!(bitAry, getChild(levelBoundaryBaIdx, [searchTargetNode[1] + 1, brotherChildrenNumber + 1], bitAry)[1], false)
                            #子ノードを追加した階層より右側の区切り位置を1増やす
                            levelBoundaryBaIdx[searchTargetNode[1] + 2] += 1
                            levelBoundaryBaIdx[searchTargetNode[1] + 3:end] += repeat([2], length(levelBoundaryBaIdx[searchTargetNode[1] + 3:end]))
                        end
                        #今回追加した子ノードの階層まで、各階層のノード数配列が情報を持っていなければ追加
                        if length(eachLevelNodesAccumulatedNumber) < searchTargetNode[1] + 1
                            push!(eachLevelNodesAccumulatedNumber, eachLevelNodesAccumulatedNumber[end] + 1)
                        #既に存在している場合
                        else
                            #子ノードを追加した階層から右側のノード数を1増やす
                            eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end] += repeat([1], length(eachLevelNodesAccumulatedNumber[searchTargetNode[1] + 1:end]))
                        end
                        #labelに追加
                        insert!(label, childLabelStartIdx + 1, insert!(foundMatchLabelIdxes, 1, elm))
                        #既に登録されているsearchTargetNodesを更新
                        tmpSearchTargetNodes = updateSearchTargetNodes(tmpSearchTargetNodes, bitIdx, searchTargetNode)
                        #searchTargetNodeを追加する。
                        push!(tmpSearchTargetNodes, [searchTargetNode[1] + 1, brotherChildrenNumber + 1, bitIdx])
                    end
                end

            end
            #次回の検索用配列にコピー
            searchTargetNodes = tmpSearchTargetNodes
        end
        #ルートの子ノードに追加する
        #子ノードがあれば
        if length(eachLevelNodesAccumulatedNumber) > 0
            #同じ要素が子ノードにあるか検索
            matchRootChildLabelIdx = findfirst(isequal(elm), [labelElm[1] for labelElm in label[1:eachLevelNodesAccumulatedNumber[1]]])
            #あれば
            if matchRootChildLabelIdx !== nothing
                #一致したラベルに現在のシーケンスインデックスを追加
                push!(label[matchRootChildLabelIdx], seqIdx)
                #ターゲットノードとして追加
                push!(searchTargetNodes, [1, matchRootChildLabelIdx, 2 + matchRootChildLabelIdx])
            else
            #ルートノードの子にいない場合は追加
                insert!(bitAry, 2 + eachLevelNodesAccumulatedNumber[1] + 1, true)
                insert!(bitAry, getChild(levelBoundaryBaIdx, [1, eachLevelNodesAccumulatedNumber[1] + 1], bitAry)[1], false)
                insert!(label,  eachLevelNodesAccumulatedNumber[1] + 1, [elm, seqIdx])
                eachLevelNodesAccumulatedNumber += repeat([1], length(eachLevelNodesAccumulatedNumber))
                levelBoundaryBaIdx[2:end] += repeat([1], length(levelBoundaryBaIdx[2:end]))
            end
        #なければ追加
        else
            insert!(bitAry, 3, true)
            push!(bitAry, false)
            insert!(label, 1, [elm, seqIdx])
            push!(eachLevelNodesAccumulatedNumber, 1)
            push!(levelBoundaryBaIdx, 5)
        end
    end
    return bitAry, label, eachLevelNodesAccumulatedNumber, levelBoundaryBaIdx
end

#渡したノードの子ノードの情報を取得
function getChild(levelBoundaryBaIdx, searchTargetNode, bitAry)
    brotherNumber = 0
    countChildren = false
    childrenNumber = 0
    brotherChildrenNumber = 0
    #調査対象ノードが長男の場合、直下の階層の最初が調査対象ノードの子ノードになるので子ノードとして数えだす
    if searchTargetNode[2] == 1
        countChildren = true
    end
    bitIdx = levelBoundaryBaIdx[searchTargetNode[1] + 1] - 1
    for bit in bitAry[bitIdx + 1:end]
        bitIdx += 1
        if countChildren == true
            if bit == true
                childrenNumber += 1
            else
                brotherNumber += 1
            end
        else
            if bit == false
                brotherNumber += 1
            else
                brotherChildrenNumber += 1
            end
        end
        if brotherNumber == searchTargetNode[2] - 1
            countChildren = true
        end
        if brotherNumber == searchTargetNode[2]
            break
        end
    end
    return bitIdx, childrenNumber, brotherChildrenNumber
end

#既に登録されているsearchTargetNodesを更新
function updateSearchTargetNodes(tmpSearchTargetNodes, bitIdx, searchTargetNode)
    if length(tmpSearchTargetNodes) > 0
        for pastSearchTargetNode in tmpSearchTargetNodes
            #今回追加したbitIdxより後ろのbitIdxの場合、1つ右にずらす
            if bitIdx < pastSearchTargetNode[3]
                pastSearchTargetNode[3] += 1
                #さらに同じ階層の場合、ノードの左からの順番も1つ右にずらす
                if searchTargetNode[1] == pastSearchTargetNode[1]
                    pastSearchTargetNode[1] += 1
                end
            end
        end
    end
    return tmpSearchTargetNodes
end

#全部分列の情報を取得する
function getAllSubSequences(label, sequence, eachLevelNodesAccumulatedNumber)
    subSequences = []
    startSeqIdxes = []
    endSeqIdxes = []
    #取得したlabelつまりノードすべて計算
    for nodeNumber in 1:length(label)
        #trie木の親ノード直下のノードから、指定したノード番号（第四引数）までの部分列の形状を持つ、実際の部分列群を取得する
        subSequence, startSeqIdx, endSeqIdx = getMatchSubsequences(sequence, label, eachLevelNodesAccumulatedNumber, nodeNumber)
        #全部追加する
        push!(subSequences, subSequence)
        push!(startSeqIdxes, startSeqIdx)
        push!(endSeqIdxes, endSeqIdx)
    end
    return subSequences, startSeqIdxes, endSeqIdxes

end
#trie木の親ノード直下のノードから、指定したノード（targetLabelIdx）までの部分列の形状を持つ、実際の部分列群を返却する
function getMatchSubsequences(sequence, label, eachLevelNodesAccumulatedNumber, targetLabelIdx)
    #targetLabelIdxがどの階層にいるか判定 TODO:これ高速化したい
    targetNodeLevel = 0
    for nodesAccumulatedNumber in eachLevelNodesAccumulatedNumber
        targetNodeLevel += 1
        if nodesAccumulatedNumber ≥ targetLabelIdx
            break
        end
    end
    #labelの2番目以降に格納されているseqIdxesは、部分列の終端位置を指す。
    #現時点では、ソースとなるsequenceが階差数列になっているため実際の終端位置を出すために+1する
    endSeqIndexes = label[targetLabelIdx][2:end] + repeat([1], length(label[targetLabelIdx][2:end]))
    #終端位置から所属階層位置の数を引くことで、対象のノードまでの部分列の開始位置を取得する
    startSeqIdxes = endSeqIndexes - repeat([targetNodeLevel], length(endSeqIndexes))
    #全部分列を取得する
    subSequences = []
    for i in 1:length(startSeqIdxes)
        push!(subSequences, Dict("subSequenceStartIdx" => startSeqIdxes[i], "data" => sequence[startSeqIdxes[i]:endSeqIndexes[i]]))
    end
    return subSequences, startSeqIdxes, endSeqIndexes
end

#時系列データセットから2つの時系列データを全組み合わせで取り出して、それらのDTW距離を出す。
function calcDtwDistances(subSequences)
    dtwDistances = 0
    for subSequence in subSequences
        #時系列データセットから2つの時系列データを全組み合わせで取り出す
        sequenceCombination = collect(combinations(subSequence, 2))
        #全組み合わせの時系列同士の距離を計算する
        for seq in sequenceCombination
            dtwDistances += calcDtwDistance(seq[1]["data"], seq[2]["data"])
        end
    end
    return dtwDistances
end

#DTWで2つの時系列の距離を計算する。
#s1:比較対象時系列データその1
#s2:比較対象時系列データその2
function calcDtwDistance(s1, s2)
    #DTW格納用の配列。ループごとに更新される。
    beforeDtwAry = []
    currentDtwAry = []
    #xカウンタ
    x = 0
    for elm2 in s2
    #yカウンタ
        y = 0
        x = x + 1
        currentDtwAry = []
        for elm1 in s1
            y = y + 1
            diff = abs(elm2 - elm1)
            #ループの一番最初だけは先頭同士の差の絶対値
            if x == 1 && y == 1
                currentDtwAry = [diff]
            #ループの1列目は自身の配列のひとつ前の要素
            elseif x == 1
                push!(currentDtwAry, diff + currentDtwAry[y - 1])
            #2列目以降かつ1行目は、前の配列の同じ位置の要素
            elseif x != 1 && y == 1
                push!(currentDtwAry, diff + beforeDtwAry[y])
            #それ以外は、3パターンのうちの最小値
            else
                push!(currentDtwAry, diff + minimum([beforeDtwAry[y], beforeDtwAry[y - 1], currentDtwAry[y - 1]]))
            end
        end
        #完成した現在の列を前の列にコピーする
        beforeDtwAry = currentDtwAry
    end
    #DTW配列の最後の要素がDTW-Distance
    return currentDtwAry[end]
end

#一致した部分列群をプロットする
function plotSubsequences(sequence, subSequences, startSeqIdxes, endSeqIdxes)

    subSequencesForPlot = deepcopy(subSequences)

    for i in 1:length(subSequencesForPlot)
        subSequencesForPlotData = repeat([NaN], startSeqIdxes[i] - 1)
        append!(subSequencesForPlotData, subSequences[i]["data"])
        append!(subSequencesForPlotData, repeat([NaN], length(sequence) - endSeqIdxes[i]))
        subSequencesForPlot[i]["data"] = subSequencesForPlotData
    end
    #plot装飾用
    xticksValues = [1 + i * 16 for i in 0:div(length(sequence), 16)]
    xticksLabels = [i for i in 1:div(length(sequence), 16)]
    majorScaleValue = [0, 2, 4, 5, 7, 9, 11]
    majorScaleLabel = ["C","D","E","F","G","A","B"]

    minValOffset = div(minimum(sequence), 12)
    maxValOffset = div(maximum(sequence), 12)
    yticksValues = []
    yticksLabels = []
    if minValOffset == maxValOffset
        yticksValues = majorScaleValue * minValOffset
    else
        for i in minValOffset:maxValOffset
            append!(yticksValues, majorScaleValue + repeat([12 * i], 7))
            append!(yticksLabels, majorScaleLabel)
        end
    end
    #表示
    plot([subSequencesForPlot[i]["data"] for i in 1:length(subSequencesForPlot)],
    size = (2200, 500),
    xticks = (xticksValues, xticksLabels),
    yticks = (yticksValues, yticksLabels),
    xlabel = "measure",
    ylabel = "pitch(natural tone name)")
end
# PlantUMLファイル生成。Trie木を可視化。
function trieToPuml(bitAry, label)
    open("tree.puml", "w") do file
        write(file, "'This file was automatically generated.\n@startuml tree_diagram\nobject 0\n")

        bitCnt = 0
        for bit in bitAry[3:end]
            if bit == true
                bitCnt += 1
                write(file, "object ", string(bitCnt), "\n", string(bitCnt), " : value=", string(label[bitCnt][1]), "\n", string(bitCnt), " : seqIdx=", string(label[bitCnt][2:end], "\n"))
            end
        end
        children = []
        parent = 0
        labelCnt = 0
        for bit in bitAry[3:end]
            if bit == true
                labelCnt += 1
                push!(children, labelCnt)
            elseif bit == false
                if length(children) > 0
                    for child in children
                        write(file, string(parent), "--", string(child), "\n")
                    end
                    children = []
                end
                parent += 1
            end
        end
        write(file, "@enduml")
    end
end

end


text:
module MPThemes

using Plots

include("MPWong.jl")
export my_wong

include("MPVibrant.jl")
export my_vibrant

end # module MP_Themes


text:
function lower_varname(var::Variable, naming_scheme; lower=false)
    D = var.diff
    D == nothing && return var
    order = lower ? D.order-1 : D.order
    lower_varname(var.name, D.x, order, var.subtype, naming_scheme)
end
function lower_varname(sym::Symbol, idv, order::Int, subtype::Symbol, naming_scheme)
    order == 0 && return Variable(sym, subtype)
    name = String(sym)*naming_scheme*String(idv.name)^order
    Variable(name, subtype=subtype)
end

function ode_order_lowering(sys::DiffEqSystem; kwargs...)
    eqs = sys.eqs
    ivs = sys.ivs
    eqs_lowered = ode_order_lowering(eqs; kwargs...)
    DiffEqSystem(eqs_lowered, ivs)
end
ode_order_lowering(eqs; naming_scheme = "_") = ode_order_lowering!(deepcopy(eqs), naming_scheme)
function ode_order_lowering!(eqs, naming_scheme)
    ind = findfirst(x->!(isintermediate(x)), eqs)
    idv = extract_idv(eqs[ind])
    D   = Differential(idv, 1)
    sym_order = Dict{Symbol, Int}()
    dv_name = eqs[1].args[1].subtype
    for eq in eqs
        isintermediate(eq) && continue
        sym, maxorder = extract_symbol_order(eq)
        maxorder == 1 && continue # fast pass
        if maxorder > get(sym_order, sym, 0)
            sym_order[sym] = maxorder
        end
        eq = lhs_renaming!(eq, D, naming_scheme)
        eq = rhs_renaming!(eq, naming_scheme)
    end
    for sym in keys(sym_order)
        order = sym_order[sym]
        for o in (order-1):-1:1
            lhs = D*lower_varname(sym, idv, o-1, dv_name, naming_scheme)
            rhs = lower_varname(sym, idv, o, dv_name, naming_scheme)
            eq = Operation(==, [lhs, rhs])
            push!(eqs, eq)
        end
    end
    eqs
end

function lhs_renaming!(eq, D, naming_scheme)
    eq.args[1] = D*lower_varname(eq.args[1], naming_scheme, lower=true)
    return eq
end
function rhs_renaming!(eq, naming_scheme)
    rhs = eq.args[2]
    _rec_renaming!(rhs, naming_scheme)
end

function _rec_renaming!(rhs, naming_scheme)
    rhs isa Variable && rhs.diff != nothing && return lower_varname(rhs, naming_scheme)
    if rhs isa Operation
        args = rhs.args
        for i in eachindex(args)
            args[i] = _rec_renaming!(args[i], naming_scheme)
        end
    end
    rhs
end

function extract_symbol_order(eq)
    # We assume that the differential with the highest order is always going to be in the LHS
    dv = eq.args[1]
    sym = dv.name
    order = dv.diff.order
    sym, order
end

export ode_order_lowering


text:
module JImport

export @jimport

using JavaCall.JavaCodeGeneration

macro jimport(expr)
    # return nothing since we want no output, just to eval the methods
    :($__module__.eval(loadclass($__module__, Symbol($expr))); nothing)
end

end


text:
#=
ogm_ls.jl
OGM with a MM line search
2019-03-16, Jeff Fessler, University of Michigan
=#

export ogm_ls

using LinearAlgebra: I, norm, dot

"""
    (x,out) = ogm_ls(B, gradf, curvf, x0; niter=?, ninner=?, fun=?)

OGM with a line search; Drori&Taylor @arxiv 1803.05676;
to minimize a general "inverse problem" cost function of the form
``\\Psi(x) = \\sum_{j=1}^J f_j(B_j x)``
where each function ``f_j(v)`` has a quadratic majorizer of the form
```math
q_j(v;u) = f_j(u) + \\nabla f_j(u) (v - u) + 1/2 \\|v - u\\|^2_{C_j(u)}
```
where ``C_j(u)`` is diagonal matrix of curvatures.
(It suffices for each ``f_j`` to have a Lipschitz smooth gradient.)

This OGM method uses a majorize-minimize (MM) line search.

in
- `B`		array of ``J`` blocks ``B_1,...,B_J``
- `gradf`	array of ``J`` functions return gradients of ``f_1,...,f_J``
- `curvf`	array of ``J`` functions `z -> curv(z)` that return a scalar
or a vector of curvature values for each element of ``z``
- `x0`	initial guess; need `length(x) == size(B[j],2)` for ``j=1...J``

option
- `niter`	# number of outer iterations; default 50
- `ninner`	# number of inner iterations of MM line search; default 5
- `fun`		User-defined function to be evaluated with two arguments (x,iter).
 * It is evaluated at (x0,0) and then after each iteration.

output
- `x`		final iterate
- `out`		`[niter+1] (fun(x0,0), fun(x1,1), ..., fun(x_niter,niter))`
  * (all 0 by default). This is an array of length `niter+1`
"""
function ogm_ls(
    B::AbstractVector{<:Any},
    gradf::AbstractVector{<:Function},
    curvf::AbstractVector{<:Function},
    x0::AbstractArray{<:Number} ; # usually Vector
    niter::Int = 50,
    ninner::Int = 5,
    fun::Function = (x,iter) -> 0,
)

out = Array{Any}(undef, niter+1)
out[1] = fun(x0, 0)

J = length(B)

x = x0
dir = []
grad_old = []
grad_new = []
grad_sum = zeros(size(x0))
ti = 1
thetai = 1

B0 = [B[j] * x for j=1:J]
Bx = copy(B0)
By = copy(B0)
grad = (Bx) -> sum([B[j]' * gradf[j](Bx[j]) for j=1:J])

for iter = 1:niter
	grad_new = grad(Bx) # gradient of x_{iter-1}
	grad_sum += ti * grad_new # sum_{j=0}^{iter-1} t_j * gradient_j

	thetai = (1 + sqrt(8*ti^2 + 1)) / 2 # theta_{i+1}
	ti = (1 + sqrt(4*ti^2 + 1)) / 2 # t_{i+1}

	tt = (iter < niter) ? ti : thetai # use theta_i factor for last iteration
	yi = (1 - 1/tt) * x + (1/tt) * x0

	for j=1:J # update Bj * yi
		By[j] = (1 - 1/tt) * Bx[j] + (1/tt) * B0[j]
	end

	dir = -(1 - 1/tt) * grad_new - (2/tt) * grad_sum # -d_i

	# MM-based line search for step size alpha
	# using h(a) = sum_j f_j(By_j + a * Bd_j)
	Bd = [B[j] * dir for j=1:J]

	alf = 0
	for ii=1:ninner
		derh = 0 # derivative of h(a)
		curv = 0
		for j=1:J
			tmp = By[j] + alf * Bd[j]
			derh += real(dot(Bd[j], gradf[j](tmp)))
			curv += sum(curvf[j](tmp) .* abs2.(Bd[j]))
		end
		curv < 0 && throw("curv < 0")
		if curv > 0
			alf = alf - derh / curv
		end
		if alf == 0
			break
		end
	end

#	# derivative of h(a) = cost(x + a * dir) where \alpha is real
#	dh = alf -> real(sum([Bd[j]' * gradf[j](By[j] + alf * Bd[j]) for j=1:J]))
#	Ldh = sum([Lgf[j] * norm(Bd[j])^2 for j=1:J]) # Lipschitz constant for dh
#	(alf, ) = gd(dh, Ldh, 0, niter=ninner) # GD-based line search
# todo

	x = yi + alf * dir

	if iter < niter
		for j=1:J # update Bj * x
			Bx[j] = By[j] + alf * Bd[j]
		end
	end

#	for j=1:J # recursive update Bj * yi ???
#		By[j] = (1 - 1/ti) * (By[j] + alf * Bd[j]) + (1/ti) * B0[j]
#	end

	out[iter+1] = fun(x, iter)
end

return x, out
end

"""
    (x,out) = ogm_ls(grad, curv, x0, ...)

special case of `ogm_ls` (OGM with line search) for minimizing a cost function
whose gradient is `grad(x)`
and that has a quadratic majorizer with diagonal Hessian given by `curv(x)`.
Typically `curv = (x) -> L` where `L` is the Lipschitz constant of `grad`
"""
function ogm_ls(
    grad::Function,
    curv::Function,
    x0::AbstractVector{<:Number} ;
    kwargs...,
)

	return ogm_ls([I], [grad], [curv], x0; kwargs...)
end


text:
# PolaronPathIntegrals.jl

module PolaronPathIntegrals

using Optim
using QuadGK
using Plots
using AbstractPlotting
using GLMakie
using PyPlot

include("coupling.jl")
include("free_energy.jl")
include("variation.jl")
include("memory_function.jl")
include("mobility.jl")
include("optical_absorption.jl")
include("make_polaron.jl")
include("plot_polaron.jl")

export frohlich_α
export variation
export free_energy
export polaron_mobility
export optical_absorption
export make_polaron
export plot_polaron
export save_polaron_plots
export plot_polaron_interactive

# Individual Functions
# export χ
include("../src/polaronmakie/PolaronMakie.jl")
for name in names(PolaronMakie)
    @eval import .PolaronMakie: $(name)
    @eval export $(name)
end

# Physical constants
const ħ = 1.05457162825e-34; # Reduced Planck's constant (kg m^2 s^{-1})
const eV = 1.602176487e-19; # Electron Volt (kg m^2 s^{-2})
const m_e = 9.10938188e-31; # Electron Mass (kg)
const k_B = 1.3806504e-23; # Boltzmann's constant (kg m^2 K^{-1} s^2)
const ϵ_0 = 8.854e-12 # Dielectric constant (C^2 N^{-1} m^{-2})
const c = 2.99792458e8 # Speed of light (m s^{-1})
const amu = 1.66053906660e-27 # Atomic Mass Unit (kg)

struct Polaron
    α      # Frohlich alpha (unitless)
    T      # Temperature (K)
    β      # Reduced Thermodynamic beta (unitless)
    v      # Variational parameter (s^-1)
    w      # Variational parameter (s^-1)
    κ      # Fictitious spring constant (multiples of m_e) (kg / s^2)
    M      # Fictitious particle (multiples of m_e) (kg)
    F      # Free energy (meV)
    Ω      # Electric field frequencies (multiples of phonon frequency ω) (s^-1)
    μ      # Mobility (cm^2 / Vs)
    Γ      # Absorption coefficient (cm^-1)
end

# Broadcast Polaron data.
function Base.show(io::IO, x::Polaron)
    flush(stdout)
    print(io, "---------------------- \n Polaron Information: \n----------------------\n", "α = ", round(x.α, digits = 3), "\nT = ", round.(x.T, digits = 3), " K \nβ = ", round.(x.β, digits = 3), "\nv = ", round.(x.v, digits = 3), " s^-1\nw = ", round.(x.w, digits = 3), " s^-1\nκ = ", round.(x.κ, digits = 3), " kg/s^2\nM = ", round.(x.M, digits = 3), " kg\nF = ", round.(x.F, digits = 3), " meV\nΩ = ", round.(Float64.(x.Ω), digits = 3),  " s^-1\nμ = ", x.μ .|> y -> round.(Float64.(y), digits = 3), " cm^2/Vs\nΓ = ", x.Γ .|> y -> round.(Float64.(y), digits = 3), " cm^-1")
end

export Polaron

end # module


text:
# ------------------------------------------------------------------
# Licensed under the MIT License. See LICENSE in the project root.
# ------------------------------------------------------------------

"""
    UniformSampling(size, replace=false)

A method for uniform sampling from spatial objects that produces
samples of given `size` with or without replacement depending on
the option `replace`.
"""
struct UniformSampling <: SamplingMethod
  size::Int
  replace::Bool
end

UniformSampling(size::Int) = UniformSampling(size, false)

function sample(object, method::UniformSampling)
  n = nelms(object)
  s = method.size
  r = method.replace
  if s > n && r == false
    @error "invalid sample size for sampling without replacement"
  end
  view(object, sample(1:n, s, replace=r))
end


text:
"""
`framedict[method]` returns the `FrameCode` for `method`. For `@generated` methods,
see [`genframedict`](@ref).
"""
const framedict = Dict{Method,FrameCode}()                # essentially a method table for lowered code

"""
`genframedict[(method,argtypes)]` returns the `FrameCode` for a `@generated` method `method`,
for the particular argument types `argtypes`.

The framecodes stored in `genframedict` are for the code returned by the generator
(i.e, what will run when you call the method on particular argument types);
for the generator itself, its framecode would be stored in [`framedict`](@ref).
"""
const genframedict = Dict{Tuple{Method,Type},FrameCode}() # the same for @generated functions

"""
`meth ∈ compiled_methods` indicates that `meth` should be run using [`Compiled`](@ref)
rather than recursed into via the interpreter.
"""
const compiled_methods = Set{Method}()

const junk = Base.IdSet{FrameData}()      # to allow re-use of allocated memory (this is otherwise a bottleneck)
recycle(frame) = push!(junk, frame.framedata)  # using an IdSet ensures that a frame can't be added twice

const empty_svec = Core.svec()

function namedtuple(kwargs)
    names, types, vals = Symbol[], [], []
    for pr in kwargs
        if isa(pr, Expr)
            push!(names, pr.args[1])
            val = pr.args[2]
            push!(types, typeof(val))
            push!(vals, val)
        elseif isa(pr, Pair)
            push!(names, pr.first)
            val = pr.second
            push!(types, typeof(val))
            push!(vals, val)
        else
            error("unhandled entry type ", typeof(pr))
        end
    end
    return NamedTuple{(names...,), Tuple{types...}}(vals)
end

get_source(meth::Method) = Base.uncompressed_ast(meth)

function get_source(g::GeneratedFunctionStub, env)
    b = g(env..., g.argnames...)
    b isa CodeInfo && return b
    return eval(b)
end

function copy_codeinfo(code::CodeInfo)
    @static if VERSION < v"1.1.0-DEV.762"
        newcode = ccall(:jl_new_struct_uninit, Any, (Any,), CodeInfo)::CodeInfo
        for (i, name) in enumerate(fieldnames(CodeInfo))
            if isdefined(code, name)
                val = getfield(code, name)
                ccall(:jl_set_nth_field, Cvoid, (Any, Csize_t, Any), newcode, i-1, val===nothing || isa(val, Union{Type, Method}) ? val : copy(val))
            end
        end
        return newcode
    else
        # Inline this when support for VERSION above is dropped
        return copy(code)
    end
end

"""
    frun, allargs = prepare_args(fcall, fargs, kwargs)

Prepare the complete argument sequence for a call to `fcall`. `fargs = [fcall, args...]` is a list
containing both `fcall` (the `#self#` slot in lowered code) and the positional
arguments supplied to `fcall`. `kwargs` is a list of keyword arguments, supplied either as
list of expressions `:(kwname=kwval)` or pairs `:kwname=>kwval`.

For non-keyword methods, `frun === fcall`, but for methods with keywords `frun` will be the
keyword-sorter function for `fcall`.

# Example

```jldoctest; setup=(using JuliaInterpreter; empty!(JuliaInterpreter.junk))
julia> mymethod(x) = 1
mymethod (generic function with 1 method)

julia> mymethod(x, y; verbose=false) = nothing
mymethod (generic function with 2 methods)

julia> JuliaInterpreter.prepare_args(mymethod, [mymethod, 15], ())
(mymethod, Any[mymethod, 15])

julia> JuliaInterpreter.prepare_args(mymethod, [mymethod, 1, 2], [:verbose=>true])
(getfield( Symbol("#kw##mymethod"))(), Any[#kw##mymethod(), (verbose = true,), mymethod, 1, 2])
```
"""
function prepare_args(@nospecialize(f), allargs, kwargs)
    if !isempty(kwargs)
        f = Core.kwfunc(f)
        allargs = [f, namedtuple(kwargs), allargs...]
    elseif f === Core._apply
        f = to_function(allargs[2])
        allargs = Base.append_any((allargs[2],), allargs[3:end]...)
    end
    return f, allargs
end

if VERSION < v"1.2-" || !isdefined(Core.Compiler, :specialize_method)
    specialize_method(method::Method, @nospecialize(atypes), sparams::SimpleVector) =
        Core.Compiler.code_for_method(method, atypes, sparams, typemax(UInt))
else
    const specialize_method = Core.Compiler.specialize_method
end

function prepare_framecode(method::Method, @nospecialize(argtypes); enter_generated=false)
    sig = method.sig
    if method.module == Core.Compiler || method.module == Base.Threads || method ∈ compiled_methods
        return Compiled()
    end
    # Get static parameters
    (ti, lenv::SimpleVector) = ccall(:jl_type_intersection_with_env, Any, (Any, Any),
                        argtypes, sig)::SimpleVector
    enter_generated &= is_generated(method)
    if is_generated(method) && !enter_generated
        framecode = get(genframedict, (method, argtypes), nothing)
    else
        framecode = get(framedict, method, nothing)
    end
    if framecode === nothing
        if is_generated(method) && !enter_generated
            # If we're stepping into a staged function, we need to use
            # the specialization, rather than stepping through the
            # unspecialized method.
            code = Core.Compiler.get_staged(specialize_method(method, argtypes, lenv))
            code === nothing && return nothing
            generator = false
        else
            if is_generated(method)
                code = get_source(method.generator, lenv)
                generator = true
            else
                code = get_source(method)
                generator = false
            end
        end
        framecode = FrameCode(method, code; generator=generator)
        if is_generated(method) && !enter_generated
            genframedict[(method, argtypes)] = framecode
        else
            framedict[method] = framecode
        end
    end
    return framecode, lenv
end

function get_framecode(method)
    framecode = get(framedict, method, nothing)
    if framecode === nothing
        code = get_source(method)
        framecode = FrameCode(method, code; generator=false)
        framedict[method] = framecode
    end
    return framecode
end

"""
    framecode, frameargs, lenv, argtypes = prepare_call(f, allargs; enter_generated=false)

Prepare all the information needed to execute lowered code for `f` given arguments `allargs`.
`f` and `allargs` are the outputs of [`prepare_args`](@ref).
For `@generated` methods, set `enter_generated=true` if you want to extract the lowered code
of the generator itself.

On return `framecode` is the [`FrameCode`](@ref) of the method.
`frameargs` contains the actual arguments needed for executing this frame (for generators,
this will be the types of `allargs`);
`lenv` is the "environment", i.e., the static parameters for `f` given `allargs`.
`argtypes` is the `Tuple`-type for this specific call (equivalent to the signature of the `MethodInstance`).

# Example

```jldoctest; setup=(using JuliaInterpreter; empty!(JuliaInterpreter.junk))
julia> mymethod(x::Vector{T}) where T = 1
mymethod (generic function with 1 method)

julia> framecode, frameargs, lenv, argtypes = JuliaInterpreter.prepare_call(mymethod, [mymethod, [1.0,2.0]]);

julia> framecode
  1  1  1 ─     return 1

julia> frameargs
2-element Array{Any,1}:
 mymethod
 [1.0, 2.0]

julia> lenv
svec(Float64)

julia> argtypes
Tuple{typeof(mymethod),Array{Float64,1}}
```
"""
function prepare_call(@nospecialize(f), allargs; enter_generated = false)
    # Can happen for thunks created by generated functions
    if isa(f, Core.Builtin) || isa(f, Core.IntrinsicFunction)
        return nothing
    elseif any(is_vararg_type, allargs)
        return nothing  # https://github.com/JuliaLang/julia/issues/30995
    end
    argtypes = Tuple{map(_Typeof,allargs)...}
    method = whichtt(argtypes)
    if method === nothing
        # Call it to generate the exact error
        f(allargs[2:end]...)
    end
    args = allargs
    ret = prepare_framecode(method, argtypes; enter_generated=enter_generated)
    # Exceptional returns
    if ret === nothing
        # The generator threw an error. Let's generate the same error by calling it.
        f(allargs[2:end]...)
    end
    isa(ret, Compiled) && return ret
    # Typical return
    framecode, lenv = ret
    if is_generated(method) && enter_generated
        args = Any[_Typeof(a) for a in args]
    end
    return framecode, args, lenv, argtypes
end

function prepare_framedata(framecode, argvals::Vector{Any})
    if isa(framecode.scope, Method)
        meth, src = framecode.scope::Method, framecode.src
        ssavt = src.ssavaluetypes
        ng = isa(ssavt, Int) ? ssavt : length(ssavt::Vector{Any})
        nargs = length(argvals)
        if !isempty(junk)
            olddata = first(junk)
            delete!(junk, olddata)
            locals, ssavalues, sparams = olddata.locals, olddata.ssavalues, olddata.sparams
            exception_frames, last_reference = olddata.exception_frames, olddata.last_reference
            last_exception = olddata.last_exception
            callargs = olddata.callargs
            resize!(locals, length(src.slotflags))
            resize!(ssavalues, ng)
            # for check_isdefined to work properly, we need sparams to start out unassigned
            resize!(sparams, 0)
            empty!(exception_frames)
            empty!(last_reference)
            last_exception[] = nothing
        else
            locals = Vector{Union{Nothing,Some{Any}}}(undef, length(src.slotflags))
            ssavalues = Vector{Any}(undef, ng)
            sparams = Vector{Any}(undef, 0)
            exception_frames = Int[]
            last_reference = Dict{Symbol,Int}()
            callargs = Any[]
            last_exception = Ref{Any}(nothing)
        end
        for i = 1:meth.nargs
            last_reference[framecode.src.slotnames[i]] = i
            if meth.isva && i == meth.nargs
                locals[i] = nargs < i ? Some{Any}(()) : (let i=i; Some{Any}(ntuple(k->argvals[i+k-1], nargs-i+1)); end)
                break
            end
            locals[i] = nargs >= i ? Some{Any}(argvals[i]) : Some{Any}(())
        end
        # add local variables initially undefined
        for i = (meth.nargs+1):length(src.slotnames)
            locals[i] = nothing
        end
    else
        src = framecode.src
        locals = Vector{Union{Nothing,Some{Any}}}(undef, length(src.slotflags))
        fill!(locals, nothing)
        ssavalues = Vector{Any}(undef, length(src.code))
        sparams = Any[]
        exception_frames = Int[]
        last_reference = Dict{Symbol,Int}()
        callargs = Any[]
        last_exception = Ref{Any}(nothing)
    end
    FrameData(locals, ssavalues, sparams, exception_frames, last_exception, last_reference, callargs)
end

"""
    frame = prepare_frame(framecode::FrameCode, frameargs, lenv)

Construct a new `Frame` for `framecode`, given lowered-code arguments `frameargs` and
static parameters `lenv`. See [`JuliaInterpreter.prepare_call`](@ref) for information about how to prepare the inputs.
"""
function prepare_frame(framecode::FrameCode, args::Vector{Any}, lenv::SimpleVector)
    framedata = prepare_framedata(framecode, args)
    resize!(framedata.sparams, length(lenv))
    # Add static parameters to environment
    for i = 1:length(lenv)
        T = lenv[i]
        isa(T, TypeVar) && continue  # only fill concrete types
        framedata.sparams[i] = T
    end
    return Frame(framecode, framedata)
end

function prepare_frame_caller(caller::Frame, framecode::FrameCode, args::Vector{Any}, lenv::SimpleVector)
    caller.callee = frame = prepare_frame(framecode, args, lenv)
    frame.caller = caller
    return frame
end

"""
    frame = prepare_thunk(mod::Module, expr::Expr)

Prepare `expr` for evaluation in `mod`. `expr` should be a "straightforward" expression,
one that does not require special top-level handling (see [`JuliaInterpreter.split_expressions`](@ref)).
"""
function prepare_thunk(mod::Module, thunk::Expr, recursive::Bool=false)
    if isexpr(thunk, :thunk)
        framecode = FrameCode(mod, thunk.args[1])
    elseif isexpr(thunk, :error) || isexpr(thunk, :incomplete)
        error("lowering returned an error, ", thunk)
    elseif recursive
        thunk = Meta.lower(mod, thunk)
        if isa(thunk, Expr)
            # If on 2nd attempt to lower it's still an Expr, just evaluate it
            Core.eval(mod, thunk)
            return nothing
        end
        framecode = FrameCode(mod, thunk.args[1])
    else
        lwr = Meta.lower(mod, thunk)
        isa(lwr, Expr) && return prepare_thunk(mod, lwr, true)
        return nothing
    end
    return Frame(framecode, prepare_framedata(framecode, []))
end
prepare_thunk((mod, ex)::Tuple{Module,Expr}) = prepare_thunk(mod, ex)

"""
    modexs, docexprs = split_expressions(mod::Module, expr::Expr; extract_docexprs=false)

Break `expr` into a list `modexs` of sequential blocks. This is often needed when `expr`
needs to be evaluated at top level.

`modexs[i]` is a `(mod::Module, ex::Expr)` tuple, where `ex` is to be evaluated in `mod`.

# Toplevel evaluation

For code that defines new structs, new methods, or new macros, it can be important to evaluate
these expressions carefully:

    stack = Frame[]
    for modex in modexs    # or use `for (mod, ex) in modexs` to split the tuple
        frame = JuliaInterpreter.prepare_thunk(modex)
        while true
            JuliaInterpreter.through_methoddef_or_done!(stack, frame) === nothing && break
        end
    end

The `while` loop here deserves some explanation. Occasionally, a frame may define new methods
(e.g., anonymous or local functions) and then call those methods. In such cases, running
the entire frame as a single block (e.g., with [`JuliaInterpreter.finish_and_return!`](@ref)
can trigger "method is too new..." errors. Instead, the approach above runs each frame,
but returns to the caller after any new method is defined. When this loop is running at
top level (e.g., in the REPL), this allows the world age to update and thus avoid
"method is too new..." errors.

Putting the above nested loop inside a function defeats its purpose, because inside a
compiled function the world age will not update. If necessary, use the following strategy:

    Core.eval(somemodule, Expr(:toplevel, quote
        body
    ))

where `body` contains the nested loop, plus any preparatory statements required to make the
necessary variables available at top level in `somemodule`.
"""
function split_expressions(mod::Module, expr::Expr; filename=nothing, kwargs...)
    modexs = Tuple{Module,Expr}[]
    docexprs = Dict{Module,Vector{Expr}}()
    if filename === nothing
        # On Julia 1.2+, the first line of a :toplevel expr may contain line info
        if length(expr.args) >= 1 && isa(expr.args[1], LineNumberNode)
            filename = expr.args[1].file
        else
            filename="toplevel"
        end
    end
    return split_expressions!(modexs, docexprs, mod, expr; filename=filename, kwargs...)
end
split_expressions!(modexs, docexprs, mod::Module, ex::Expr; kwargs...) =
    split_expressions!(modexs, docexprs, Expr(:block), mod, ex; kwargs...)

function split_expressions!(modexs, docexprs, lex::Expr, mod::Module, ex::Expr; extract_docexprs=false, filename="toplevel")
    # lex is the expression we'll lower; it will accumulate LineNumberNodes and a
    # single top-level expression. We split blocks, module defs, etc.
    if ex.head == :toplevel || ex.head == :block
        split_expressions!(modexs, docexprs, lex, mod, ex.args; extract_docexprs=extract_docexprs, filename=filename)
    elseif ex.head == :module
        newname = ex.args[2]::Symbol
        if isdefined(mod, newname)
            newmod = getfield(mod, newname)
        else
            if (id = Base.identify_package(mod, String(newname))) !== nothing
                newmod = Base.root_module(id)
            else
                newmod = Core.eval(mod, :(module $newname end))
            end
        end
        split_expressions!(modexs, docexprs, lex, newmod, ex.args[3]; extract_docexprs=extract_docexprs, filename=filename)
    elseif extract_docexprs && is_doc_expr(ex)
        docexs = get(docexprs, mod, nothing)
        if docexs === nothing
            docexs = docexprs[mod] = Expr[]
        end
        push!(docexs, ex)
        body = ex.args[4]
        if isa(body, Expr) && body.head != :call
            split_expressions!(modexs, docexprs, lex, mod, body; extract_docexprs=extract_docexprs, filename=filename)
        end
    else
        if isempty(lex.args)
            push!(modexs, (mod, copy(ex)))
        else
            push!(lex.args, ex)
            push!(modexs, (mod, copy(lex)))
            empty!(lex.args)
        end
    end
    return modexs, docexprs
end

function split_expressions!(frames, docexprs, lex, mod::Module, args::Vector{Any}; filename="toplevel", kwargs...)
    for a in args
        if isa(a, Expr)
            split_expressions!(frames, docexprs, lex, mod, a; filename=filename, kwargs...)
        elseif isa(a, LineNumberNode)
            if a.file === nothing  # happens with toplevel expressions on Julia 1.2
                push!(lex.args, LineNumberNode(a.line, Symbol(filename)))
            else
                push!(lex.args, a)
            end
        else
            push!(lex.args, a)
        end
    end
end

"""
    framecode, frameargs, lenv, argtypes = determine_method_for_expr(expr; enter_generated = false)

Prepare all the information needed to execute a particular `:call` expression `expr`.
For example, try `JuliaInterpreter.determine_method_for_expr(:($(sum)([1,2])))`.
See [`JuliaInterpreter.prepare_call`](@ref) for information about the outputs.
"""
function determine_method_for_expr(expr; enter_generated = false)
    f = to_function(expr.args[1])
    allargs = expr.args
    # Extract keyword args
    local kwargs = Expr(:parameters)
    if length(allargs) > 1 && isexpr(allargs[2], :parameters)
        kwargs = splice!(allargs, 2)
    end
    f, allargs = prepare_args(f, allargs, kwargs.args)
    return prepare_call(f, allargs; enter_generated=enter_generated)
end

"""
    frame = enter_call_expr(expr; enter_generated=false)

Build a `Frame` ready to execute the expression `expr`. Set `enter_generated=true`
if you want to execute the generator of a `@generated` function, rather than the code that
would be created by the generator.

# Example

```jldoctest; setup=(using JuliaInterpreter; empty!(JuliaInterpreter.junk))
julia> mymethod(x) = x+1
mymethod (generic function with 1 method)

julia> JuliaInterpreter.enter_call_expr(:(\$mymethod(1)))
Frame for mymethod(x) in Main at none:1
  1* 1  1 ─ %1 = (+)(x, 1)
  2  1  └──      return %1
x = 1

julia> mymethod(x::Vector{T}) where T = 1
mymethod (generic function with 2 methods)

julia> a = [1.0, 2.0]
2-element Array{Float64,1}:
 1.0
 2.0

julia> JuliaInterpreter.enter_call_expr(:(\$mymethod(\$a)))
Frame for mymethod(x::Array{T,1}) where T in Main at none:1
  1* 1  1 ─     return 1
x = [1.0, 2.0]
T = Float64
```

See [`enter_call`](@ref) for a similar approach not based on expressions.
"""
function enter_call_expr(expr; enter_generated = false)
    r = determine_method_for_expr(expr; enter_generated = enter_generated)
    if isa(r, Tuple)
        return prepare_frame(r[1:end-1]...)
    end
    nothing
end

"""
    frame = enter_call(f, args...; kwargs...)

Build a `Frame` ready to execute `f` with the specified positional and keyword arguments.

# Example

```jldoctest; setup=(using JuliaInterpreter; empty!(JuliaInterpreter.junk))
julia> mymethod(x) = x+1
mymethod (generic function with 1 method)

julia> JuliaInterpreter.enter_call(mymethod, 1)
Frame for mymethod(x) in Main at none:1
  1* 1  1 ─ %1 = (+)(x, 1)
  2  1  └──      return %1
x = 1

julia> mymethod(x::Vector{T}) where T = 1
mymethod (generic function with 2 methods)

julia> JuliaInterpreter.enter_call(mymethod, [1.0, 2.0])
Frame for mymethod(x::Array{T,1}) where T in Main at none:1
  1* 1  1 ─     return 1
x = [1.0, 2.0]
T = Float64
```

For a `@generated` function you can use `enter_call((f, true), args...; kwargs...)`
to execute the generator of a `@generated` function, rather than the code that
would be created by the generator.

See [`enter_call_expr`](@ref) for a similar approach based on expressions.
"""
function enter_call(@nospecialize(finfo), @nospecialize(args...); kwargs...)
    if isa(finfo, Tuple)
        f = finfo[1]
        enter_generated = finfo[2]::Bool
    else
        f = finfo
        enter_generated = false
    end
    f, allargs = prepare_args(f, Any[f, args...], kwargs)
    # Can happen for thunks created by generated functions
    if isa(f, Core.Builtin) || isa(f, Core.IntrinsicFunction)
        error(f, " is a builtin or intrinsic")
    end
    r = prepare_call(f, allargs; enter_generated=enter_generated)
    if isa(r, Tuple)
        return prepare_frame(r[1:end-1]...)
    end
    return nothing
end

# This is a version of InteractiveUtils.gen_call_with_extracted_types, except that is passes back the
# call expression for further processing.
function extract_args(__module__, ex0)
    if isa(ex0, Expr)
        if any(a->(Meta.isexpr(a, :kw) || Meta.isexpr(a, :parameters)), ex0.args)
            return quote
                local arg1 = $(ex0.args[1])
                local args, kwargs = $separate_kwargs($(ex0.args[2:end]...))
                tuple(Core.kwfunc(arg1), kwargs, arg1, args...)
            end
        elseif ex0.head == :.
            return Expr(:tuple, :getproperty, ex0.args...)
        elseif ex0.head == :(<:)
            return Expr(:tuple, :(<:), ex0.args...)
        else
            return Expr(:tuple,
                map(x->isexpr(x,:parameters) ? QuoteNode(x) : x, ex0.args)...)
        end
    end
    if isa(ex0, Expr) && ex0.head == :macrocall # Make @edit @time 1+2 edit the macro by using the types of the *expressions*
        return error("Macros are not supported in @enter")
    end
    ex = lower(__module__, ex0)
    exret = Expr(:none)
    if !isa(ex, Expr)
        return error("expression is not a function call or symbol")
    elseif ex.head == :call
        return Expr(:tuple,
                map(x->isexpr(x,:parameters) ? QuoteNode(x) : x, ex.args)...)
    elseif ex.head == :body
        a1 = ex.args[1]
        if isa(a1, Expr) && a1.head == :call
            a11 = a1.args[1]
            if a11 == :setindex!
                return Expr(:tuple,
                map(x->isexpr(x,:parameters) ? QuoteNode(x) : x, arg.args)...)
            end
        end
    end
    return error("expression is not a function call, "
               * "or is too complex for @enter to analyze; "
               * "break it down to simpler parts if possible")
end

"""
    @interpret f(args; kwargs...)

Evaluate `f` on the specified arguments using the interpreter.

# Example

```jldoctest; setup=(using JuliaInterpreter; empty!(JuliaInterpreter.junk))
julia> a = [1, 7]
2-element Array{Int64,1}:
 1
 7

julia> sum(a)
8

julia> @interpret sum(a)
8
```
"""
macro interpret(arg)
    args = try
        extract_args(__module__, arg)
    catch e
        return :(throw($e))
    end
    quote
        local theargs = $(esc(args))
        local frame = JuliaInterpreter.enter_call_expr(Expr(:call, theargs...))
        if frame === nothing
            eval(Expr(:call, map(QuoteNode, theargs)...))
        elseif shouldbreak(frame, 1)
            frame, BreakpointRef(frame.framecode, 1)
        else
            local ret = finish_and_return!(frame)
            isa(ret, BreakpointRef) ? (frame, ret) : ret
        end
    end
end


text:
get_data(forecast::Forecast) = IS.get_data(forecast)
get_horizon(forecast::Forecast) = IS.get_horizon(forecast)
get_initial_time(forecast::Forecast) = IS.get_initial_time(forecast)
get_resolution(forecast::Forecast) = IS.get_resolution(forecast)


text:
struct MyOrderedDict{TK,TV}
    keys::Vector{TK}
    vals::Vector{TV}
end

function MyOrderedDict{K,V}() where {K,V}
    MyOrderedDict(K[], V[])
end

function Base.setindex!(d::MyOrderedDict, val, key)
    ind = findfirst(x->x===key, d.keys)
    if ind isa Nothing
        push!(d.keys, key)
        push!(d.vals, val)
    else
        @inbounds d.vals[ind] = val
    end
    return d
end

function Base.getindex(d::MyOrderedDict, key)
    ind = findfirst(x->x===key, d.keys)
    if ind isa Nothing
        throw(KeyError(ind))
    else
        return d.vals[ind]
    end
end

function Base.delete!(d::MyOrderedDict, key)
    ind = findfirst(x->x==key, d.keys)
    if ind isa Nothing
        throw(KeyError(ind))
    else
        deleteat!(d.vals, ind)
        deleteat!(d.keys, ind)
    end
end

Base.length(d::MyOrderedDict) = length(d.keys)

function Base.pop!(d::MyOrderedDict)
    k = pop!(d.keys)
    v = pop!(d.vals)
    k, v
end

Base.isempty(d::MyOrderedDict) = length(d.keys) == 0
function Base.empty!(d::MyOrderedDict)
    empty!(d.keys)
    empty!(d.vals)
    return d
end

function Base.haskey(d::MyOrderedDict, k)
    k ∈ d.keys
end

function Base.iterate(d::MyOrderedDict, state=1)
    state > length(d) ? nothing : ((d.keys[state], d.vals[state]), state+1)
end


text:
struct ActionType{T} end
struct BulkType{T} end

struct BulkLength{T<:Integer}
   seq::T
   count::T
end

const EsData = Union{NamedTuple,Dict}
const EsId   = Union{Number,AbstractString}

escape(x) = esc(x)
escape(x::AbstractString) = x

macro extra(data, kw...)

	q , len = quote end , length(kw)
	q = Expr(:ref, esc(data) , escape(kw[1]))
	len == 1 && return q

	for i in 2:len
		q = Expr(:ref , q , escape(kw[i]))
	end

	return q

end

Base.iterate(B::BulkLength, state::Integer=0) = state  >= B.count ? nothing : ( ( state+1, ( state+B.seq) > B.count ? B.count : (state + B.seq) ) , state+B.seq )
Base.length(B::BulkLength)           = Int(ceil(B.count/B.seq))

macro returns(title, content)
	esc(:( string($title, "\n" , $content, "\n") ))
end

macro returns(title)
	esc(:( string($title, "\n") ))
end

function make_url(::Type{ActionType{:_count}}, info::Esinfo, index::AbstractString)
	"$(info.url)/$index/_count"
end

function make_url(::Type{ActionType{:_search}}, info::Esinfo, index::AbstractString, path::AbstractString)
	"$(info.url)/$index/$path"
end

function make_url(::Type{ActionType{:_search}}, info::Esinfo)
	"$(info.url)/_search"
end

function make_url(::Type{ActionType{:_scroll}}, info::Esinfo )
	"$(info.url)/_search/scroll"
end

function make_url(::Type{ActionType{:_bulk}}, info::Esinfo, index::AbstractString, doc::AbstractString)
	"$(info.url)/$index/$doc/_bulk"
end

function make_url(::Type{ActionType{:_bulk}}, info::Esinfo)
	"$(info.url)/_bulk"
end

function es_count(info::Esinfo, index::AbstractString ; kw...)

	url   = make_url(ActionType{:_count}, info, index )
	res   = @esexport info "GET" url Dict() Dict(kw...) getfield(info, :jheader)
	@esexp res "count"

end

function es_count(info::Esinfo, index::AbstractString, body::Dict ; kw...)

	url   = make_url(ActionType{:_count}, info, index )
	res   = @esexport info "POST" url json(body) Dict(kw...) getfield(info, :jheader)
	@esexp res "count"

end

function es_count(info::Esinfo, index::AbstractString, body::AbstractString ; kw...)

	url   = make_url(ActionType{:_count}, info, index )
	res   = @esexport info "POST" url body Dict(kw...) getfield(info, :jheader)
	@esexp res "count"

end

@genfunction "POST" es_search ActionType{:_search} 1

function es_search(info::Esinfo, index::AbstractString, body::AbstractString , path::AbstractString="_search" ; kw...)

	query = Dict(kw...)
	url   = make_url(ActionType{:_search}, info, index , path )
	@esexport info "POST" url body query getfield(info, :jheader)

end

function es_search(info::Esinfo, index::AbstractString, body::Dict , path::AbstractString="_search" ; kw...)

	query = Dict(kw...)
	url   = make_url(ActionType{:_search}, info, index , path )
	@esexport info "POST" url json(body) query getfield(info, :jheader)

end

function es_search(info::Esinfo, index::AbstractString, body::Dict ,query::Dict, path::AbstractString="_search" )

	url   = make_url(ActionType{:_search}, info, index , path )
	@esexport info "POST" url json(body) query getfield(info, :jheader)

end

function es_searchs(info::Esinfo, index::AbstractString, body::T ; kw... ) where T <: Union{AbstractString, Dict}

	isa(body , AbstractString) && (body= JSON.Parser.parse(body))

	num   = pop!(body, "size", 10000)
	snum  = (haskey(body, "query") ? Dict("query" => body["query"]) : Dict()) |>
			df -> es_count(info, index, df)
	query = Dict(kw..., :size => num)

	res = es_search(info, index, body, query)
	snum  <= num && return res

	sizehint!(res["hits"]["hits"], snum)
	ids = res["_scroll_id"]
	for _ in 1:(Int(floor(snum/num)))
		tmp = es_scroll(info , ids , query[:scroll])
		append!(res["hits"]["hits"], tmp["hits"]["hits"] )
		ids = tmp["_scroll_id"]
	end
	res
end

function es_scroll(info::Esinfo, id::AbstractString, scroll::AbstractString="1m")

	body  = (scroll = scroll, scroll_id = id, )
	url   = make_url(ActionType{:_scroll}, info )
	@esexport info "POST" url json(body) Dict() getfield(info, :jheader)

end

function es_scroll_clear(info::Esinfo, id::Union{Vector{AbstractString},AbstractString} )

	url   = make_url(ActionType{:_scroll}, info )
	body =  (scroll_id => id , )
	@esexport info "DELETE" url json(body) Dict() getfield(info, :jheader)

end

function es_scroll_clear(info::Esinfo)

	url   = make_url(ActionType{:_scroll}, info )
	body  = Dict()
	@esexport info "DELETE" (url * "/_all") json(body) Dict() getfield(info, :jheader)

end

macro esdatawarn(x, y)
	esc(:(length($x) != length($y) && throw("id size != data size")))
end

function es_bulk_update(info::Esinfo, index::AbstractString, doc::AbstractString,
				data::Vector{<:EsData},
				id::Vector{<:EsId},
				asupsert::Bool=true  ,chunk_num::Integer=1000 ; kw...)

	@esdatawarn data id

	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_update},  x, y , asupsert) for (x ,y) in zip(view(data,m:n),view(id,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_update(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData},
					id::Vector{<:EsId},
					routing::Vector{<:EsId},
					asupsert::Bool=true  ,chunk_num::Integer=1000 ; kw...)

	@esdatawarn data id

	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_update},  x, y , z, asupsert) for (x ,y,z) in zip(view(data,m:n),view(id,m:n),view(routing,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_script(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData}, id::Vector{<:EsId},
					  sid::AbstractString,asupsert::Bool=true,chunk_num::Integer=1000 ; kw... )

	@esdatawarn data id
	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_script},  x, y , sid, asupsert) for (x ,y) in zip(view(data,m:n),view(id,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_script(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData}, id::Vector{<:EsId},routing::Vector{<:EsId},
					  sid::AbstractString,asupsert::Bool=true,chunk_num::Integer=1000 ; kw... )

	@esdatawarn data id
	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_script},  x, y , z, sid, asupsert) for (x ,y,z) in zip(view(data,m:n),view(id,m:n),view(routing,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_index(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData}, id::Vector{<:EsId} ,chunk_num::Integer=1000 ; kw... )

	@esdatawarn data id
	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_index}, x, y  ) for (x ,y) in zip(view(data,m:n),view(id,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_index(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData}, id::Vector{<:EsId} ,
					routing::Vector{<:EsId},chunk_num::Integer=1000 ; kw... )

	@esdatawarn data id
	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_index}, x, y ,z ) for (x ,y,z) in zip(view(data,m:n),view(id,m:n),view(routing,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_index(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData} ,::Val{true}, chunk_num::Integer=1000 ; kw... )

	@sync for (m, n) in BulkLength( chunk_num, length(data) )
		chunk = (make_bulk(BulkType{:_index}, index, doc ,x ) for x in  view(data,m:n) )
		@async es_bulk(info, chunk; kw...)
	end

end

function es_bulk_index(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData} ,routing::Vector{<:EsId},::Val{true},
					chunk_num::Integer=1000 ; kw... )

	@sync for (m, n) in BulkLength( chunk_num, length(data) )
		chunk = (make_bulk(BulkType{:_index}, index, doc ,x ,y,Val(true)) for (x,y) in  zip(view(data,m:n), view(routing,m:n) ) )
		@async es_bulk(info, chunk; kw...)
	end

end

function es_bulk_create(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData}, id::Vector{<:EsId},chunk_num::Integer=1000 ; kw...)

	@esdatawarn data id
	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_create},  x, y ) for (x ,y) in zip(view(data,m:n),view(id,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_create(info::Esinfo, index::AbstractString, doc::AbstractString,
					data::Vector{<:EsData}, id::Vector{<:EsId},
					routing::Vector{<:EsId},chunk_num::Integer=1000 ; kw...)

	@esdatawarn data id
	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_create},  x, y ,z) for (x ,y,z) in zip(view(data,m:n),view(id,m:n),view(routing,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_del(info::Esinfo, index::AbstractString, doc::AbstractString,
					id::Vector{<:EsId},chunk_num::Integer=1000 ; kw... )

	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_del}, x ) for x in view(id,m:n) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

function es_bulk_del(info::Esinfo, index::AbstractString, doc::AbstractString,
					id::Vector{<:EsId},routing::Vector{<:EsId},chunk_num::Integer=1000 ; kw... )

	@sync for (m, n) in BulkLength( chunk_num, length(id) )
		chunk = (make_bulk(BulkType{:_del}, x ,y) for (x ,y) in zip(view(data,m:n), view(id,m:n)) )
		@async es_bulk(info, index, doc, chunk; kw...)
	end

end

macro esmeta(method, id)
	esc(:( ($method = ( _id = $id ,) ,) |> json ))
end

function make_bulk(::Type{BulkType{:_del}}, id::EsId )

	title =  @esmeta delete id
	@returns title

end

function make_bulk(::Type{BulkType{:_del}}, id::EsId,routing::EsId )

	title =  ( delete = (_id = id , routing = routing ,) ,) |> json
	@returns title

end

function make_bulk(::Type{BulkType{:_index}},  data::EsData, id::EsId )

	title =  @esmeta index id
	content = data |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_index}},  data::EsData, id::EsId ,routing::EsId)

	title =  ( index = (_id = id , routing = routing, ) ,) |> json
	content = data |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_index}}, index::AbstractString , type::AbstractString ,
					data::EsData )

	title =  (index = (_index = index , _type = type ,) ,) |> json
	content = data |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_index}}, index::AbstractString , type::AbstractString ,
					data::EsData ,routing::EsId, ::Val{true})

	title =  (index = (_index = index , _type = type,_routing = routing ,), ) |> json
	content = data |> json
	@returns title content

end

 function make_bulk(::Type{BulkType{:_create}},  data::EsData, id::EsId)

	title =  @esmeta create id
	content = data |> json
	@returns title content

end

 function make_bulk(::Type{BulkType{:_create}},  data::EsData, id::EsId,routing::EsId)

	title =  ( create = (_id = id , routing = routing ,),) |> json
	content = data |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_update}},  data::EsData, id::EsId ,asupsert::Bool)

	title =  @esmeta update  id
	content = (doc = data, doc_as_upsert = asupsert ,) |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_update}},  data::EsData, id::EsId ,routing::EsId,asupsert::Bool)

	title =  ( update = (_id = id , routing = routing ,) , ) |> json
	content =  (doc = data, doc_as_upsert = asupsert , ) |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_script}},  data::EsData, id::EsId ,sid::AbstractString,asupsert::Bool)

	title =  @esmeta update id
	content =  (script = (id = sid, params = (event = data ,),),
			scripted_upsert = asupsert, upsert = Dict() ,) |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_script}},  data::EsData, id::EsId ,routing::EsId ,sid::AbstractString,
														asupsert::Bool)

	title =  ( update = (_id = id , routing = routing ,) ,) |> json
	content =  (script =  (id = sid, params =  (event = data ,) ,),
			scripted_upsert = asupsert, upsert = Dict() , ) |> json
	@returns title content

end

function es_bulk(info::Esinfo , index::AbstractString, doc::AbstractString , data ; kw...)
	url   = make_url(ActionType{:_bulk}, info ,index, doc )
	query = Dict(kw...)
	@esexport info "POST" url data query getfield(info, :nheader)
end

function es_bulk(info::Esinfo , data  ; kw...)
	url   = make_url(ActionType{:_bulk}, info  )
	query = Dict(kw...)
	@esexport info "POST" url data query getfield(info, :nheader)
end

# add meta all function

# function fortest( data::SubArray{T, 1}) where T <: EsData
#
# 	res= Vector{String}(undef, length(data))
#
# 	@inbounds @simd for i in eachindex(data)
# 		res[i] = make_bulk(BulkType{:_index}, data[i] )
# 	end
#
# 	res
# end
#
# function es_bulk_test( info::Esinfo, data::Vector{<:EsData}, chunk_num::Integer=1000 ; kw... )
#
# 	@sync for (m, n) in BulkLength( chunk_num, length(data) )
# 		chunk = fortest(view(data,m:n))
# 		@async es_bulk(info, chunk; kw...)
# 	end
#
# end

function es_bulk_update(info::Esinfo,  data::Vector{<:EsData}, asupsert::Bool=true  ,chunk_num::Integer=1000 ; kw...)

	@sync for (m, n) in BulkLength( chunk_num, length(data) )
		chunk = [make_bulk(BulkType{:_update}, i , asupsert) for i in view(data,m:n) ]
		@async es_bulk(info, chunk; kw...)
	end

end

function es_bulk_script(info::Esinfo, data::Vector{<:EsData}, sid::AbstractString,asupsert::Bool=true,
								chunk_num::Integer=1000 ; kw... )

	@sync for (m, n) in BulkLength( chunk_num, length(data) )
		chunk = [ make_bulk(BulkType{:_script}, i , sid, asupsert) for i in view(data,m:n) ]
		@async es_bulk(info, chunk; kw...)
	end

end

function es_bulk_index( info::Esinfo, data::Vector{<:EsData}, chunk_num::Integer=1000 ; kw... )

	@sync for (m, n) in BulkLength( chunk_num, length(data) )
		chunk = [ make_bulk(BulkType{:_index}, i ) for i in view(data,m:n) ]
		@async es_bulk(info, chunk; kw...)
	end

end

function es_bulk_create( info::Esinfo, data::Vector{<:EsData}, chunk_num::Integer=1000 ; kw...)
	chunk = [ make_bulk(BulkType{:_create}, i ) for i in data ]
	@sync for (m, n) in BulkLength( chunk_num, length(data) )
		@async es_bulk(info, view(data,m:n); kw...)
	end

end

function es_bulk_del( info::Esinfo, data::Vector{<:EsData},chunk_num::Integer=1000 ; kw... )

	@sync for (m, n) in BulkLength( chunk_num, length(data) )
		chunk = [ make_bulk(BulkType{:_del}, i) for i in view(data,m:n) ]
		@async es_bulk(info, chunk; kw...)
	end

end

macro esmetaall(method, index, type )
	esc(:(json(($method = (_index = $index, _type = $type ,),) )))
end

macro esmetaall(method, index, type, id )
	esc(:(json( ($method = (_index = $index, _type = $type,  _id = $id ,),) )))
end

macro esmetaallronting(method, index, type, id, routing )
	esc(:(json( ($method = (_index = $index, _type = $type,  _id = $id ,routing = $routing ,),) )))
end

macro flown(x, y )
	esc(Expr(:., x, :($(:($y)))))
end

macro flowd(x, y )
	esc(Expr(:ref, x, y ))
end

@inline ref_new(x::Dict, y::AbstractString)	=  	@flowd(x,y)
@inline ref_new(x::NamedTuple, y::Symbol) 	= 	@flown(x,y)
@inline ref_new(x::Dict, y::Symbol) 	= 	ref_new(x, String(y))

# Base.haskey(nt::Dict, key::Symbol) = Base.haskey(nt, String(key))

macro cheak(method , data)
	esc(quote
		if haskey($data, :routing)
			title = @esmetaallronting($method,ref_new($data, :_index),ref_new($data, :_type),ref_new($data, :_id),ref_new($data, :routing))
		else
			title = @esmetaall($method,ref_new($data, :_index),ref_new($data, :_type),ref_new($data, :_id))
		end
	end)
end

macro cheak(method , data, yes)
	esc(quote
		if haskey($data, :routing)
			title = @esmetaallronting($method,ref_new($data, :_index),ref_new($data, :_type),ref_new($data, :_id),ref_new($data, :routing))
		else
			if haskey($data, :_id)
				title = @esmetaall($method,ref_new($data, :_index),ref_new($data, :_type),ref_new($data, :_id))
			else
				title = @esmetaall($method,ref_new($data, :_index),ref_new($data, :_type) )
			end
		end
	end)
end

function make_bulk(::Type{BulkType{:_del}},  data::EsData )

	@cheak delete data
	@returns title

end

function make_bulk(::Type{BulkType{:_index}}, data::EsData)

	@cheak index data 1
	content = ref_new(data, :_source) |> json
	@returns title content

end

 function make_bulk(::Type{BulkType{:_create}}, data::EsData)
 	@cheak create data
	content = ref_new(data, :_source) |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_update}}, data::EsData ,asupsert::Bool)
	@cheak update data
	content =  (doc= ref_new(data, :_source), doc_as_upsert = asupsert, ) |> json
	@returns title content

end

function make_bulk(::Type{BulkType{:_script}}, data::EsData ,sid::AbstractString,asupsert::Bool)
	@cheak update data
	content = (script = (id= sid, params = (event = ref_new(data, :_source),),),
			scripted_upsert = asupsert, upsert = Dict() ,) |> json
	@returns title content

end

max_stars_repo_name: frankill/ES

max_issues_repo_name: frankill/ES

max_forks_repo_name: frankill/ES


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

## dummy stub for https://github.com/JuliaBinaryWrappers/dSFMT_jll.jl

module dSFMT_jll

using Libdl

const PATH_list = String[]
const LIBPATH_list = String[]

export libdSFMT

# These get calculated in __init__()
PATH = Ref("")
LIBPATH = Ref("")
artifact_dir = ""
libdSFMT_handle = C_NULL
libdSFMT_path = ""

if Sys.iswindows()
    const libdSFMT = "libdSFMT.dll"
elseif Sys.isapple()
    const libdSFMT = "@rpath/libdSFMT.dylib"
else
    const libdSFMT = "libdSFMT.so"
end

function __init__()
    global artifact_dir = dirname(Sys.BINDIR)
    global LIBPATH[] = joinpath(Sys.BINDIR, Base.LIBDIR, "julia")
    global libdSFMT_handle = dlopen(libdSFMT)
    global libdSFMT_path = dlpath(libdSFMT_handle)
end

is_available() = true

end  # module dSFMT_jll

max_stars_repo_name: saberdy/julia

max_issues_repo_name: saberdy/julia

max_forks_repo_name: saberdy/julia


text:
module OctTrees

using GeometricalPredicates
using SharedArrays

export
    OctTree,
    CompiledOctTree,
    QuadTree,
    insert!,
    clear!,
    eltype,
    Point,
    Point2D,
    Point3D,
    Modify,
    modify,
    QuadTreeNode,
    OctTreeNode,
    CompiledOctTreeNode,
    map,
    No_Cond_Data,
    No_Apply_Data,
    AbstractPoint,
    AbstractPoint2D,
    AbstractPoint3D,
    isleaf,
    isemptyleaf,
    isfullleaf,
    divide!,
    initnode!,
    compile!

# for compatibility reasons
if VERSION < v"0.4-"
    sizehint! = sizehint
end

# General stuff good for both Quad and Oct trees

abstract type SpatialTree end
abstract type SpatialTreeNode end

struct Modify end
struct No_Cond_Data end

isleaf(q::SpatialTreeNode) = !q.is_divided
isemptyleaf(q::SpatialTreeNode) = !q.is_divided && q.is_empty
isfullleaf(q::SpatialTreeNode) = !q.is_empty

stop_cond(q::SpatialTreeNode, ::Type{No_Cond_Data}) =
    stop_cond(q)

function clear!(h::SpatialTree)
    h.head.id = 0
    h.head.is_empty = true
    h.head.is_divided = false
    h.number_of_nodes_used = 1
    nothing
end

function insert!(h::SpatialTree, point::AbstractPoint)
    q = h.head
    while q.is_divided
        q = _getsubnode(q, point)
    end
    while !q.is_empty
        divide!(h, q)
        q = _getsubnode(q, point)
    end
    q.point = point
    q.is_empty = false
    q
end

# This function is needed for speed. Using another modify and the function below doesn't help!
# TODO: is this a bug?!
# modify(q::SpatialTreeNode, p::AbstractPoint, ::Type{Modify}) =
#     modify(q,p)
modify() = error("not implemented!")
function insert!(h::SpatialTree, point::AbstractPoint, ::Type{Modify})
    q = h.head
    while q.is_divided
        modify(q, point)
        q = _getsubnode(q, point)
    end
    while !q.is_empty
        friend = q.point
        divide!(h, q)
        modify(q, friend)
        modify(q, point)
        q = _getsubnode(q, point)
    end
    q.point = point
    q.is_empty = false
    q
end

function insert!(h::SpatialTree, point::AbstractPoint, additional_data)
    q = h.head
    while q.is_divided
        modify(q, point, additional_data)
        q = _getsubnode(q, point)
    end
    while !q.is_empty
        friend = q.point
        divide!(h, q)
        modify(q, friend, additional_data)
        modify(q, point, additional_data)
        q = _getsubnode(q, point)
    end
    q.point = point
    q.is_empty = false
    q
end

function insert!(h::SpatialTree, points::AbstractArray{T,1}, ::Type{Modify}) where T <: AbstractPoint
    #hilbertsort!(points)
    for p in points
        insert!(h, p, Modify)
    end
end

# specific stuff for Quad or Oct trees

include("_octtrees.jl")
include("_compiled_octtrees.jl")
include("_quadtrees.jl")

map(h::QuadTree{T}) where T<:AbstractPoint2D = map(h, No_Cond_Data)
map(h::OctTree{T}) where T<:AbstractPoint3D = map(h, No_Cond_Data)

end # module


text:
# Cutoff strategies for long-range interactions

export
    NoCutoff,
    DistanceCutoff,
    ShiftedPotentialCutoff,
    ShiftedForceCutoff,
    CubicSplineCutoff

"""
    NoCutoff()

Placeholder cutoff that does not alter forces or potentials.
"""
struct NoCutoff end

cutoff_points(::Type{NoCutoff}) = 0

force_divr_cutoff(::NoCutoff, r2, inter, params) = force_divr_nocutoff(inter, r2, inv(r2), params)
potential_cutoff(::NoCutoff, r2, inter, params) = potential(inter, r2, inv(r2), params)

"""
    DistanceCutoff(dist_cutoff)

Cutoff that sets the potential and force to be zero past a specified cutoff point.
"""
struct DistanceCutoff{D, S, I}
    dist_cutoff::D
    sqdist_cutoff::S
    inv_sqdist_cutoff::I
end

function DistanceCutoff(dist_cutoff)
    return DistanceCutoff(dist_cutoff, dist_cutoff ^ 2, inv(dist_cutoff ^ 2))
end

cutoff_points(::Type{DistanceCutoff{D, S, I}}) where {D, S, I} = 1

force_divr_cutoff(::DistanceCutoff, r2, inter, params) = force_divr_nocutoff(inter, r2, inv(r2), params)
potential_cutoff(::DistanceCutoff, r2, inter, params) = potential(inter, r2, inv(r2), params)

"""
    ShiftedPotentialCutoff(dist_cutoff)

Cutoff that shifts the potential to be continuous at a specified cutoff point.
"""
struct ShiftedPotentialCutoff{D, S, I}
    dist_cutoff::D
    sqdist_cutoff::S
    inv_sqdist_cutoff::I
end

function ShiftedPotentialCutoff(dist_cutoff)
    return ShiftedPotentialCutoff(dist_cutoff, dist_cutoff ^ 2, inv(dist_cutoff ^ 2))
end

cutoff_points(::Type{ShiftedPotentialCutoff{D, S, I}}) where {D, S, I} = 1

force_divr_cutoff(::ShiftedPotentialCutoff, r2, inter, params) = force_divr_nocutoff(inter, r2, inv(r2), params)

function potential_cutoff(cutoff::ShiftedPotentialCutoff, r2, inter, params)
    potential(inter, r2, inv(r2), params) - potential(inter, cutoff.sqdist_cutoff, cutoff.inv_sqdist_cutoff, params)
end

"""
    ShiftedForceCutoff(dist_cutoff)

Cutoff that shifts the force to be continuous at a specified cutoff point.
"""
struct ShiftedForceCutoff{D, S, I}
    dist_cutoff::D
    sqdist_cutoff::S
    inv_sqdist_cutoff::I
end

function ShiftedForceCutoff(dist_cutoff)
    return ShiftedForceCutoff(dist_cutoff, dist_cutoff ^ 2, inv(dist_cutoff ^ 2))
end

cutoff_points(::Type{ShiftedForceCutoff{D, S, I}}) where {D, S, I} = 1

function force_divr_cutoff(cutoff::ShiftedForceCutoff, r2, inter, params)
    return force_divr_nocutoff(inter, r2, inv(r2), params) - force_divr_nocutoff(
                                inter, cutoff.sqdist_cutoff, cutoff.inv_sqdist_cutoff, params)
end

@fastmath function potential_cutoff(cutoff::ShiftedForceCutoff, r2, inter, params)
    invr2 = inv(r2)
    r = √r2
    rc = cutoff.dist_cutoff
    fc = force_divr_nocutoff(inter, cutoff.sqdist_cutoff, cutoff.inv_sqdist_cutoff, params) * r

    potential(inter, r2, invr2, params) + (r - rc) * fc -
        potential(inter, cutoff.sqdist_cutoff, cutoff.inv_sqdist_cutoff, params)
end

"""
    CubicSplineCutoff(dist_activation, dist_cutoff)

Cutoff that interpolates the true potential and zero between an activation point
and a cutoff point, using a cubic Hermite spline.
"""
struct CubicSplineCutoff{D, S, I}
    dist_cutoff::D
    sqdist_cutoff::S
    inv_sqdist_cutoff::I
    dist_activation::D
    sqdist_activation::S
    inv_sqdist_activation::I
end

function CubicSplineCutoff(dist_activation, dist_cutoff)
    if dist_cutoff <= dist_activation
        error("The cutoff radius must be strictly larger than the activation radius")
    end

    D, S, I = typeof.([dist_cutoff, dist_cutoff^2, inv(dist_cutoff^2)])

    return CubicSplineCutoff{D, S, I}(dist_cutoff, dist_cutoff^2, inv(dist_cutoff^2), dist_activation,
                                        dist_activation^2, inv(dist_activation^2))
end

cutoff_points(::Type{CubicSplineCutoff{D, S, I}}) where {D, S, I} = 2

@fastmath function force_divr_cutoff(cutoff::CubicSplineCutoff, r2, inter, params)
    r = √r2
    t = (r - cutoff.dist_activation) / (cutoff.dist_cutoff-cutoff.dist_activation)

    Va = potential(inter, cutoff.sqdist_activation, cutoff.inv_sqdist_activation, params)
    dVa = -force_divr_nocutoff(inter, cutoff.sqdist_activation, cutoff.inv_sqdist_activation, params) * cutoff.dist_activation

    return -((6t^2 - 6t) * Va / (cutoff.dist_cutoff-cutoff.dist_activation) + (3t^2 - 4t + 1) * dVa)/r

end

@fastmath function potential_cutoff(cutoff::CubicSplineCutoff, r2, inter, params)
    r = √r2
    t = (r - cutoff.dist_activation) / (cutoff.dist_cutoff-cutoff.dist_activation)

    Va = potential(inter, cutoff.sqdist_activation, cutoff.inv_sqdist_activation, params)
    dVa = -force_divr_nocutoff(inter, cutoff.sqdist_activation, cutoff.inv_sqdist_activation, params) * cutoff.dist_activation

    return (2t^3 - 3t^2 + 1) * Va + (t^3 - 2t^2 + t) * (cutoff.dist_cutoff-cutoff.dist_activation) * dVa
end


text:
module CMark
import Markdown
using Libdl
using Compat
using cmark_gfm_jll: cmark_gfm_jll

include("ccalls.jl")

const extensions = ["autolink", "strikethrough", "table", "tagfilter", "tasklist"]

# Public Julia API
struct CMarkNode
    p :: Ptr{Cvoid}
end

struct CMarkSyntaxExtension
    p :: Ptr{Cvoid}
end

#"autolink", "strikethrough", "table", "tagfilter"
function findsyntaxextension(name)
    p = cmark_find_syntax_extension(name)
    p == C_NULL ? nothing : CMarkSyntaxExtension(p)
end

parse_document(markdown::AbstractString) = CMarkNode(cmark_parse_document(markdown))

function parse_document_gfm(markdown::AbstractString)
    p = cmark_parser_new()
    for e in extensions
        ext_p = cmark_find_syntax_extension(e)
        cmark_parser_attach_syntax_extension(p, ext_p)
    end
    cmark_parser_feed(p, markdown)
    n = cmark_parser_finish(p)
    cmark_parser_free(p)
    CMarkNode(n)
end

function nodetype(node::CMarkNode) :: Union{cmark_node_type,Symbol,Missing}
    t_int = cmark_node_get_type(node.p)
    if t_int == CMARK_NODE_STRIKETHROUGH[]
        return :CMARK_NODE_STRIKETHROUGH
    elseif t_int == CMARK_NODE_TABLE[]
        return :CMARK_NODE_TABLE
    elseif t_int == CMARK_NODE_TABLE_CELL[]
        return :CMARK_NODE_TABLE_CELL
    elseif t_int == CMARK_NODE_TABLE_ROW[]
        return :CMARK_NODE_TABLE_ROW
    end
    try
        t = cmark_node_type(t_int)
        return t
    catch e
        isa(e, ArgumentError) ? missing : rethrow(e)
    end
end

function listtype(node::CMarkNode)
    cmark_list_type(cmark_node_get_list_type(node.p))
end

function linkinfo(node::CMarkNode)
    url = cmark_node_get_url(node.p)
    title = cmark_node_get_title(node.p)
    (
        url == C_NULL ? nothing : unsafe_string(url),
        title == C_NULL ? nothing : unsafe_string(title)
    )
end

function Base.first(node::CMarkNode)
    p = cmark_node_first_child(node.p)
    p == C_NULL ? nothing : CMarkNode(p)
end

function next(node::CMarkNode)
    p = cmark_node_next(node.p)
    p == C_NULL ? nothing : CMarkNode(p)
end

function heading_level(node::CMarkNode)
    level = cmark_node_get_heading_level(node.p)
    iszero(level) ? nothing : Int(level)
end

function literal(node::CMarkNode)
    s = cmark_node_get_literal(node.p)
    s == C_NULL ? nothing : unsafe_string(s)
end

function code_fence_info(node::CMarkNode)
    s = cmark_node_get_fence_info(node.p)
    s == C_NULL ? nothing : unsafe_string(s)
end

# Convert to Base Markdown
function markdown_withsiblings(node::Union{CMarkNode,Nothing})
    nodes = Any[]
    n = node
    while !isnothing(n)
        push!(nodes, markdown(n))
        n = next(n)
    end
    return nodes
end

function markdown(node::CMarkNode)
    ntype = nodetype(node)
    if ntype == CMARK_NODE_DOCUMENT
        Markdown.MD(markdown_withsiblings(first(node)))
    elseif ntype == CMARK_NODE_BLOCK_QUOTE
        Markdown.BlockQuote(markdown_withsiblings(first(node)))
    elseif ntype == CMARK_NODE_LIST
        # TODO: implement cmark_node_get_list_start
        # TODO: implement cmark_node_get_list_tight
        list_type = listtype(node)
        items = markdown_withsiblings(first(node))
        if list_type == CMARK_BULLET_LIST
            Markdown.List(items)
        elseif list_type == CMARK_ORDERED_LIST
            Markdown.List(items, 1)
        else
            error("Bad list type $(list_type)")
        end
    elseif ntype == CMARK_NODE_ITEM
        markdown_withsiblings(first(node))
    elseif ntype == CMARK_NODE_CODE_BLOCK
        Markdown.Code(code_fence_info(node), literal(node))
    elseif ntype == CMARK_NODE_HTML_BLOCK
        Markdown.Code("@raw html", literal(node))
    # elseif ntype == CMARK_NODE_CUSTOM_BLOCK
    elseif ntype == CMARK_NODE_PARAGRAPH
        Markdown.Paragraph(markdown_withsiblings(first(node)))
    elseif ntype == CMARK_NODE_HEADING
        level = heading_level(node)
        Markdown.Header(markdown_withsiblings(first(node)), level)
    elseif ntype == CMARK_NODE_THEMATIC_BREAK
        Markdown.HorizontalRule()
    # # Inline
    elseif ntype == CMARK_NODE_TEXT
        literal(node)
    elseif ntype == CMARK_NODE_SOFTBREAK
        " "
    elseif ntype == CMARK_NODE_LINEBREAK
        # TODO: not really supported by Base Markdown?
        "\n"
    elseif ntype == CMARK_NODE_CODE
        Markdown.Code(literal(node))
    elseif ntype == CMARK_NODE_HTML_INLINE
        Markdown.Code("@raw html", literal(node))
    # elseif ntype == CMARK_NODE_CUSTOM_INLINE
    elseif ntype == CMARK_NODE_EMPH
        Markdown.Italic(markdown_withsiblings(first(node)))
    elseif ntype == CMARK_NODE_STRONG
        Markdown.Bold(markdown_withsiblings(first(node)))
    elseif ntype == CMARK_NODE_LINK
        url, _ = linkinfo(node) # we're discarding the title
        Markdown.Link(markdown_withsiblings(first(node)), url)
    elseif ntype == CMARK_NODE_IMAGE
        url, _ = linkinfo(node) # we're discarding the title
        # TODO: we will also discard the alt text, since Julia only supports string, but
        # CommonMark allows for arbitrary formatting.
        Markdown.Image(url, "")

    # Extension nodes
    elseif ntype == :CMARK_NODE_STRIKETHROUGH
        # TODO: we end up with a vector in a vector here
        [
            Markdown.Code("@raw html", "<del>"),
            markdown_withsiblings(first(node))...,
            Markdown.Code("@raw html", "</del>")
        ]
    elseif ntype == :CMARK_NODE_TABLE
        # TODO: alignment
        rows = markdown_withsiblings(first(node))
        ncols = maximum(length.(rows))
        Markdown.Table(rows, [:l for _ in 1:ncols])
    elseif ntype == :CMARK_NODE_TABLE_ROW
        markdown_withsiblings(first(node))
    elseif ntype == :CMARK_NODE_TABLE_CELL
        markdown_withsiblings(first(node))
    else
        error("Node type $(ntype) not implemented")
    end
end

markdown(s::AbstractString) = markdown(parse_document_gfm(s))

function typetree(node::CMarkNode; level=0)
    n = node
    while !isnothing(n)
        c = first(n)
        if isnothing(c)
            println("\t"^level, nodetype(n))
        else
            println("\t"^level, nodetype(n), " => {")
            typetree(c, level = level + 1)
            println("\t"^level, "}")
        end
        n = next(n)
    end
end

export markdown

function __init__()
    # Initialize the default libcmark library
    init!(libcmarkgfm)
end

end # module