<commit_before># Recurrence parameters as defined by Marwan et al. (2007)

# Recurrence rate

"""
    recurrencerate(x; theiler=0)

Calculate the recurrence rate of the recurrence matrix `x`, ruling out
the points within the Theiler window of size `theiler`.
"""
function recurrencerate(x::ARM; theiler::Integer=0, kwargs...)::Float64
    (theiler < 0) && throw(ErrorException(
        "Theiler window length must be greater than or equal to 0"))
    if theiler == 0
        return nnz(x)/length(x)
    end
    diags_remove = -(theiler-1):(theiler-1)
    theiler_nz = 0
    for d in diags_remove
        theiler_nz += nnz(diag(x,d))
    end
    return (nnz(x) - theiler_nz)/length(x)
end

# Generic parameters of histograms: mean, average and entropy

macro histogram_params(keyword, description, hist_fun)
    combined_descriptions = Dict(:average => "average of the $(description)s",
                                 :max     => "longest $(description)",
                                 :entropy => "entropy of the $(description)s")
    function_bodies = Dict(
        :average => quote
                points = (1:length(hist)) .* hist
                return sum(points)/sum(hist)
            end,
        :max => quote length(hist) end,
        :entropy => quote
                prob_bins = hist ./ sum(hist)
                prob_bins = prob_bins[findall(!iszero, prob_bins)]
                return -sum(prob_bins .* log.(prob_bins))
            end
        )

    ret = quote end
    for (name, param) in combined_descriptions
        fname = Symbol("$(keyword)_$(name)")
        _fname = Symbol("_$(keyword)_$(name)")
        fbody = function_bodies[name]
        doc = """
                $fname(x; lmin=2, theiler=0)

            Calculate the $(param) contained in the recurrence matrix `x`,
            ruling out the points within the Theiler window of size `theiler`
            and diagonals shorter than `lmin`.
            """
        push!(ret.args, quote
            @doc $doc ->
            $fname(x::ARM; kwargs...) = $_fname($hist_fun(x; kwargs...))

            function $_fname(hist::Vector{<:Integer})
                $fbody
            end
        end)
    end
    return esc(ret)
end


# 1. Based on diagonal lines

@histogram_params dl "diagonal line" diagonalhistogram

@deprecate avgdiag dl_average
@deprecate maxdiag dl_max
@deprecate rqaentropy dl_entropy

"""
    determinism(x::AbstractRecurrenceMatrix; lmin=2, theiler=0)

Calculate the determinism of the recurrence matrix `x`, ruling out
the points within the Theiler window of size `theiler` and diagonals shorter
than `lmin`.
"""
function determinism(x::ARM; kwargs...)
    npoints = recurrencerate(x; kwargs...)*length(x)
    return _determinism(diagonalhistogram(x; kwargs...), npoints)
end

function _determinism(diag_hist::Vector{<:Integer}, npoints)::Float64
    diag_points = (1:length(diag_hist)) .* diag_hist
    return sum(diag_points)/npoints
end


"""
    divergence(x; theiler=0)

Calculate the divergence of the recurrence matrix `x`
(actually the inverse of [`dl_max`](@ref)), ruling out
the points within the Theiler window of size `theiler`.<commit_msg>zero RQA parameters for empty histograms (#42)<commit_after># Recurrence parameters as defined by Marwan et al. (2007)

# Recurrence rate

"""
    recurrencerate(x; theiler=0)

Calculate the recurrence rate of the recurrence matrix `x`, ruling out
the points within the Theiler window of size `theiler`.
"""
function recurrencerate(x::ARM; theiler::Integer=0, kwargs...)::Float64
    (theiler < 0) && throw(ErrorException(
        "Theiler window length must be greater than or equal to 0"))
    if theiler == 0
        return nnz(x)/length(x)
    end
    diags_remove = -(theiler-1):(theiler-1)
    theiler_nz = 0
    for d in diags_remove
        theiler_nz += nnz(diag(x,d))
    end
    return (nnz(x) - theiler_nz)/length(x)
end

# Generic parameters of histograms: mean, average and entropy

macro histogram_params(keyword, description, hist_fun)
    combined_descriptions = Dict(:average => "average of the $(description)s",
                                 :max     => "longest $(description)",
                                 :entropy => "entropy of the $(description)s")
    function_bodies = Dict(
        :average => quote
                (hist==[0]) && return 0.0
                points = (1:length(hist)) .* hist
                return sum(points)/sum(hist)
            end,
        :max => quote
                (hist==[0]) && return 0
                return length(hist)
            end,
        :entropy => quote
                (hist==[0]) && return 0.0
                prob_bins = hist ./ sum(hist)
                prob_bins = prob_bins[findall(!iszero, prob_bins)]
                return -sum(prob_bins .* log.(prob_bins))
            end
        )

    ret = quote end
    for (name, param) in combined_descriptions
        fname = Symbol("$(keyword)_$(name)")
        _fname = Symbol("_$(keyword)_$(name)")
        fbody = function_bodies[name]
        doc = """
                $fname(x; lmin=2, theiler=0)

            Calculate the $(param) contained in the recurrence matrix `x`,
            ruling out the points within the Theiler window of size `theiler`
            and diagonals shorter than `lmin`.
            """
        push!(ret.args, quote
            @doc $doc ->
            $fname(x::ARM; kwargs...) = $_fname($hist_fun(x; kwargs...))

            function $_fname(hist::Vector{<:Integer})
                $fbody
            end
        end)
    end
    return esc(ret)
end


# 1. Based on diagonal lines

@histogram_params dl "diagonal line" diagonalhistogram

@deprecate avgdiag dl_average
@deprecate maxdiag dl_max
@deprecate rqaentropy dl_entropy

"""
    determinism(x::AbstractRecurrenceMatrix; lmin=2, theiler=0)

Calculate the determinism of the recurrence matrix `x`, ruling out
the points within the Theiler window of size `theiler` and diagonals shorter
than `lmin`.
"""
function determinism(x::ARM; kwargs...)
    npoints = recurrencerate(x; kwargs...)*length(x)
    return _determinism(diagonalhistogram(x; kwargs...), npoints)
end

function _determinism(diag_hist::Vector{<:Integer}, npoints)::Float64
    (diag_hist==[0]) && return 0.0
    diag_points = (1:length(diag_hist)) .* diag_hist
    return sum(diag_points)/npoints
end


"""
    divergence(x; theiler=0)

Calculate the divergence of the recurrence matrix `x`
(actually the inverse of [`dl_max`](@ref)), ruling out
the points within the Theiler window of size `theiler`.
{'fraction_non_alphanumeric': 0.11649594863170769, 'fraction_numerical': 0.009325791163430668, 'mean_word_length': 2.5535035306898424, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '442093', 'n_tokens_mistral': 2066, 'n_tokens_neox': 1955, 'n_words': 572}