text:
abstract type AbstractQGramDistance <: StringSemiMetric end

"""
	QGram(q::Int)

Creates a QGram distance.

The distance corresponds to

``||v(s1, q) - v(s2, q)||``

where ``v(s, q)`` denotes the vector on the space of q-grams of length q,
that contains the number of times a q-gram appears for the string s
"""
struct QGram <: AbstractQGramDistance
	q::Int
end
eval_start(::QGram) = 0
@inline function eval_op(::QGram, c::Integer, n1::Integer, n2::Integer)
	c + abs(n1 - n2)
end
eval_end(::QGram, c::Integer) = c

"""
	Cosine(q::Int)

Creates a Cosine distance.

The distance corresponds to

`` 1 - v(s1, q).v(s2, q)  / ||v(s1, q)|| * ||v(s2, q)||``

where ``v(s, q)`` denotes the vector on the space of q-grams of length q,
that contains the  number of times a q-gram appears for the string s
"""
struct Cosine <: AbstractQGramDistance
	q::Int
end
eval_start(::Cosine) = (0, 0, 0)
@inline function eval_op(::Cosine, c::NTuple{3, <:Integer}, n1::Integer, n2::Integer)
	(c[1] + n1^2, c[2] + n2^2, c[3] + n1 * n2)
end
eval_end(::Cosine, c::NTuple{3, <:Integer}) = 1 - c[3] / sqrt(c[1] * c[2])

"""
	Jaccard(q::Int)

Creates a Jaccard distance.

The distance corresponds to

``1 - |Q(s1, q) ∩ Q(s2, q)| / |Q(s1, q) ∪ Q(s2, q))|``

where ``Q(s, q)``  denotes the set of q-grams of length n for the string s
"""
struct Jaccard <: AbstractQGramDistance
	q::Int
end
eval_start(::Jaccard) = (0, 0, 0)
@inline function eval_op(::Jaccard, c::NTuple{3, <:Integer}, n1::Integer, n2::Integer)
	(c[1] + (n1 > 0), c[2] + (n2 > 0), c[3] + (n1 > 0) * (n2 > 0))
end
eval_end(::Jaccard, c::NTuple{3, <:Integer}) = 1 - c[3] / (c[1] + c[2] - c[3])

"""
	SorensenDice(q::Int)

Creates a SorensenDice distance.

The distance corresponds to

``1 - 2 * |Q(s1, q) ∩ Q(s2, q)|  / (|Q(s1, q)| + |Q(s2, q))|)``

where ``Q(s, q)``  denotes the set of q-grams of length n for the string s
"""
struct SorensenDice <: AbstractQGramDistance
	q::Int
end
eval_start(::SorensenDice) = (0, 0, 0)
@inline function eval_op(::SorensenDice, c::NTuple{3, <:Integer}, n1::Integer, n2::Integer)
	(c[1] + (n1 > 0), c[2] + (n2 > 0), c[3] + (n1 > 0) * (n2 > 0))
end
eval_end(::SorensenDice, c::NTuple{3, <:Integer}) = 1 - 2 * c[3] / (c[1] + c[2])

"""
	Overlap(q::Int)

Creates a Overlap distance.

The distance corresponds to

``1 - |Q(s1, q) ∩ Q(s2, q)|  / min(|Q(s1, q)|, |Q(s2, q)|)``

where ``Q(s, q)``  denotes the set of q-grams of length n for the string s
"""
struct Overlap <: AbstractQGramDistance
	q::Int
end
eval_start(::Overlap) = (0, 0, 0)
@inline function eval_op(::Overlap, c::NTuple{3, <:Integer}, n1::Integer, n2::Integer)
	(c[1] + (n1 > 0), c[2] + (n2 > 0), c[3] + (n1 > 0) * (n2 > 0))
end
eval_end(::Overlap, c::NTuple{3, <:Integer}) = 1 - c[3] / min(c[1], c[2])

"""
	NMD(q::Int)
	NMD(q::Int)

Creates a NMD (Normalized Multiset Distance) as introduced by Besiris and
Zigouris 2013. The goal with this distance is to behave similarly to a normalized
compression distance without having to do any actual compression (and thus being
faster to compute).

The distance corresponds to

``(sum(max.(m(s1), m(s2)) - min(M(s1), M(s2))) / max(M(s1), M(s2))``

where ``m(s)`` is the vector of q-gram counts for string ``s`` and ``M(s)`` is the
sum of those counts.

For details see:
https://www.sciencedirect.com/science/article/pii/S1047320313001417
"""
struct NMD <: AbstractQGramDistance
	q::Int
end
eval_start(::NMD) = (0, 0, 0)
@inline function eval_op(::NMD, c::NTuple{3, <:Integer}, n1::Integer, n2::Integer)
	(c[1] + n1, c[2] + n2, c[3] + max(n1, n2))
end
eval_end(::NMD, c::NTuple{3, <:Integer}) = (c[3] - min(c[1], c[2])) / max(c[1], c[2])

"""
	MorisitaOverlap(q::Int)

Creates a MorisitaOverlap distance, a general, statistical measure of
dispersion which can also be used on dictionaries such as created
from q-grams. See https://en.wikipedia.org/wiki/Morisita%27s_overlap_index
This is more fine-grained than many of the other QGramDistances since
it is based on the counts per q-gram rather than only which q-grams are
in the strings.

The distance corresponds to

``(2 * sum(m(s1) .* m(s2)) / (sum(m(s1).^2)*M(s2)/M(s1) + sum(m(s2).^2)*M(s1)/M(s2))``

where ``m(s)`` is the vector of q-gram counts for string ``s`` and ``M(s)`` is the
sum of those counts.
"""
struct MorisitaOverlap <: AbstractQGramDistance
	q::Int
end
eval_start(::MorisitaOverlap) = (0, 0, 0, 0, 0)
@inline function eval_op(::MorisitaOverlap, c::NTuple{5, <:Integer}, n1::Integer, n2::Integer)
	(c[1] + n1, c[2] + n2, c[3] + n1^2, c[4] + n2^2, c[5] + n1 * n2)
end
eval_end(::MorisitaOverlap, c::NTuple{5, <:Integer}) = 1 - 2 * c[5] / (c[3] * c[2] / c[1] + c[4] * c[1] / c[2])

#==========================================================================
QGramIterator
==========================================================================#
@doc """
Return an iterator corresponding to the the q-gram of an iterator.
When the iterator is a String, qgrams are SubStrings.

### Arguments
* `s` iterator
* `q::Integer`: length of q-gram

## Examples
```julia
for x in qgrams("hello", 2)
	println(x)
end
```
"""
qgrams

struct QGramIterator{S <: Union{AbstractString, AbstractVector}}
	s::S   # Collection
	q::Int # Length of Qgram
	function QGramIterator{S}(s, q) where {S <: Union{AbstractString, AbstractVector}}
		q > 0 || throw(ArgumentError("The qgram length must be higher than zero"))
		new(s, q)
	end
end
function QGramIterator(s::Union{AbstractString, AbstractVector}, q::Integer)
	QGramIterator{typeof(s)}(s, q)
end
Base.length(qgram::QGramIterator) = max(length(qgram.s) - qgram.q + 1, 0)

# q-grams of AbstractString
function Base.iterate(qgram::QGramIterator{<: AbstractString},
	state = (1, nextind(qgram.s, 0, qgram.q)))
	istart, iend = state
	iend > ncodeunits(qgram.s) && return nothing
	element = SubString(qgram.s, istart, iend)
	nextstate = nextind(qgram.s, istart), nextind(qgram.s, iend)
	element, nextstate
end
Base.eltype(qgram::QGramIterator{SubString{S}}) where {S} = SubString{S}
Base.eltype(qgram::QGramIterator{S}) where {S <: AbstractString} = SubString{S}
qgrams(s::AbstractString, q::Integer) = QGramIterator(s, q)

# q-grams of General Iterators
function Base.iterate(qgram::QGramIterator{<: AbstractVector}, state = firstindex(qgram.s))
	state + qgram.q - 1 > lastindex(qgram.s) && return nothing
	view(qgram.s, state:(state + qgram.q - 1)), state + 1
end
Base.eltype(qgram::QGramIterator{<: AbstractVector}) = typeof(first(qgram))
qgrams(s::AbstractVector, q::Integer) = QGramIterator(s, q)
qgrams(s, q::Integer) = QGramIterator(collect(s), q)

#==========================================================================
Compute QGramDistances on general iterators
==========================================================================#
# For two iterators s1 and s2, that define a length and eltype method,
# this returns an iterator that,
# for each element in s1 ∪ s2, returns (numbers of times it appears in s1, numbers of times it appears in s2)
function _count(qgrams1, qgrams2)
	K = promote_type(eltype(qgrams1), eltype(qgrams2))
	d = Dict{K, Tuple{Int, Int}}()
	sizehint!(d, length(qgrams1) + length(qgrams2))
	# I use a faster way to change a dictionary key
	# see setindex! in https://github.com/JuliaLang/julia/blob/master/base/dict.jl#L380
	for x1 in qgrams1
		index = Base.ht_keyindex2!(d, x1)
		if index > 0
			d.age += 1
			@inbounds d.keys[index] = x1
			@inbounds d.vals[index] = (d.vals[index][1] + 1, 0)
		else
			@inbounds Base._setindex!(d, (1, 0), x1, -index)
		end
	end
	for x2 in qgrams2
		index = Base.ht_keyindex2!(d, x2)
		if index > 0
			d.age += 1
			@inbounds d.keys[index] = x2
			@inbounds d.vals[index] = (d.vals[index][1], d.vals[index][2] + 1)
		else
			@inbounds Base._setindex!(d, (0, 1), x2, -index)
		end
	end
	return values(d)
end

function (dist::AbstractQGramDistance)(s1, s2)
	(s1 === missing) | (s2 === missing) && return missing
	c = eval_start(dist)
	for (n1, n2) in _count(qgrams(s1, dist.q), qgrams(s2, dist.q))
		c = eval_op(dist, c, n1, n2)
	end
	eval_end(dist, c)
end

#==========================================================================
Compute QGramDistances on QGramDicts, iterators that store a dictionary associating qgrams to the number of their occurences
==========================================================================#

"""
	QGramDict(s, q::Integer = 2)

An iterator with a pre-computed dictionary of its qgrams. This enables faster calculation of QGram
distances.

Note that the qgram length must correspond with the q length used
in the distance.

## Examples
```julia
str1, str2 = "my string", "another string"
qd1 = QGramDict(str1, 2)
qd2 = QGramDict(str2, 2)
evaluate(Overlap(2), qd1, qd2)
```
"""
struct QGramDict{S, K}
	s::S
	q::Int
	counts::Dict{K, Int}
end
Base.length(s::QGramDict) = length(s.s)
Base.iterate(s::QGramDict, args...) = iterate(s.s, args...)

function QGramDict(s, q::Integer = 2)
	(s isa QGramDict) && (s.q == q) && return s
	qgs = qgrams(s, q)
	countpairs = countdict(qgs)
	QGramDict{typeof(s), eltype(qgs)}(s, q, countpairs)
end

# Turn a sequence of qgrams to a count dict for them, i.e. map each
# qgram to the number of times it has been seen.
function countdict(qgrams)
	d = Dict{eltype(qgrams), Int}()
	for qg in qgrams
		index = Base.ht_keyindex2!(d, qg)
		if index > 0
			d.age += 1
			@inbounds d.keys[index] = qg
			@inbounds d.vals[index] = d.vals[index][1] + 1
		else
			@inbounds Base._setindex!(d, 1, qg, -index)
		end
	end
	return d
end

function (dist::AbstractQGramDistance)(qc1::QGramDict, qc2::QGramDict)
	dist.q == qc1.q == qc2.q || throw(ArgumentError("The distance and the QGramDict must have the same qgram length"))
	d1, d2 = qc1.counts, qc2.counts
	c = eval_start(dist)
	for (s1, n1) in d1
		index = Base.ht_keyindex2!(d2, s1)
		if index <= 0
			c = eval_op(dist, c, n1, 0)
		else
			c = eval_op(dist, c, n1, d2.vals[index])
		end
	end
	for (s2, n2) in d2
		index = Base.ht_keyindex2!(d1, s2)
		if index <= 0
			c = eval_op(dist, c, 0, n2)
		end
	end
	eval_end(dist, c)
end

#==========================================================================
Compute QGramDistances on QGramSortedVectors, iterators that store a sorted vector associating qgrams to the number of their occurences
Note that QGramSortedVectors require qgrams to have a natural order
==========================================================================#

"""
	QGramSortedVector(s, q::Integer = 2)

An iterator with a pre-computed sorted vector of its qgrams. This enables faster calculation of QGram
distances.

Since qgrams are sorted in lexicographic order QGram distances can be
calculated even faster than when using a QGramDict. However, the
sorting means that updating the counts after creation is less
efficient. However, for most use cases QGramSortedVector is preferred
over a QgramDict.

Note that the qgram length must correspond with the q length used
in the distance.

## Examples
```julia
str1, str2 = "my string", "another string"
qs1 = QGramSortedVector(str1, 2)
qs2 = QGramSortedVector(str2, 2)
evaluate(Jaccard(2), qs1, qs2)
```
"""
struct QGramSortedVector{S, K}
	s::S
	q::Int
	counts::Vector{Pair{K, Int}}
end
Base.length(s::QGramSortedVector) = length(s.s)
Base.iterate(s::QGramSortedVector, args...) = iterate(s.s, args...)

function QGramSortedVector(s, q::Integer = 2)
	(s isa QGramSortedVector) && (s.q == q) && return s
	qgs = qgrams(s, q)
	# todo: maybe more efficient to create sorteddict directly
	countpairs = collect(countdict(qgs))
	sort!(countpairs, by = first)
	QGramSortedVector{typeof(s), eltype(qgs)}(s, q, countpairs)
end

function (dist::AbstractQGramDistance)(qc1::QGramSortedVector, qc2::QGramSortedVector)
	dist.q == qc1.q == qc2.q || throw(ArgumentError("The distance and the QGramSortedVectors must have the same qgram length"))
	d1, d2 = qc1.counts, qc2.counts
	c = eval_start(dist)
	i1 = i2 = 1
	while true
		# length can be zero
		if i2 > length(d2)
			for i in i1:length(d1)
				@inbounds c = eval_op(dist, c, d1[i][2], 0)
			end
		break
		elseif i1 > length(d1)
			for i in i2:length(d2)
				@inbounds c = eval_op(dist, c, 0, d2[i][2])
			end
		break
		end
		@inbounds s1, n1 = d1[i1]
		@inbounds s2, n2 = d2[i2]
		cmpval = Base.cmp(s1, s2)
		if cmpval == -1 # s1 < s2
			c = eval_op(dist, c, n1, 0)
			i1 += 1
		elseif cmpval == 1 # s1 > s2
			c = eval_op(dist, c, 0, n2)
			i2 += 1
		else # s1 == s2
			c = eval_op(dist, c, n1, n2)
			i1 += 1
			i2 += 1
		end
	end
	eval_end(dist, c)
end


text:
function _flatten(ex::Expr)
    head = ex.head
    args = _flatten.(ex.args)
    if head == :block
        newargs = Any[]
        for e in args
            if e isa Expr && e.head == :block
                append!(newargs, e.args)
            else
                push!(newargs, e)
            end
        end
        return Expr(:block, newargs...)
    elseif head == :(=) && args[2] isa Expr && args[2].head == :block
        newargs = args[2].args
        newargs[end] = Expr(:(=), args[1], newargs[end])
        return Expr(:block, newargs...)
    elseif head == :call && args[1]==:scalar && args[2] isa Expr && args[2].head == :block
        newargs = args[2].args
        newargs[end] = Expr(:call, args[1], newargs[end])
        return Expr(:block, newargs...)
    else
        return Expr(head, args...)
    end
end
_flatten(e) = e

function removelinenumbernode(ex::Expr)
    if ex.head == :block
        return Expr(:block, (removelinenumbernode(e) for e in ex.args if !(e isa LineNumberNode))...)
    else
        return ex
    end
end
removelinenumbernode(ex) = ex

const tensoroperationsfunctions = (:similar_from_indices,
                                    :cached_similar_from_indices,
                                    :add!, :trace!, :contract!,
                                    :scalar, :IndexError)
function addtensoroperations(ex::Expr)
    if ex.head == :call && ex.args[1] in tensoroperationsfunctions
        return Expr(ex.head, GlobalRef(TensorOperations, ex.args[1]),
                        (addtensoroperations(ex.args[i]) for i in 2:length(ex.args))...)
    else
        return Expr(ex.head, (addtensoroperations(e) for e in ex.args)...)
    end
end
addtensoroperations(ex) = ex

ext: jl


text:
# Activate ./Project.toml.  Excluding `"@v#.#"` from `Base.LOAD_PATH`
# to make compilation more reproducible.
using Pkg
empty!(Base.LOAD_PATH)
append!(Base.LOAD_PATH, ["@", "@stdlib"])
Pkg.activate(@__DIR__)

# Manually invoking `__init__` to workaround:
# https://github.com/JuliaLang/julia/issues/22910

import MacroTools
isdefined(MacroTools, :__init__) && MacroTools.__init__()

using PyCall
PyCall.__init__()
PyCall.pyimport("sys")[:executable]

has_numpy = try
    PyCall.pyimport("numpy")
    true
catch
    false
end
if has_numpy
    # Invoke numpy support (which mutates various states in PyCall) to
    # test that it does not introduce any run-time bugs:
    PyCall.npyinitialize()
end


text:
using PokerHands
using Test

@testset "PokerHands.jl" begin
    # Write your tests here.
end


text:
int_rules_1_2_2_2 = @theory begin

    #= ::Subsection::Closed:: =#
    #= 1.2.2.2*(d*x)^m*(a+b*x^2+c*x^4)^p =#
    #= Antiderivative(((~d')*(~x))^(~m')*((~b')*(~x)^2+(~c')*(~x)^4)^(~p'),~x) :=  1/d^(2*p)*Antiderivative((d*x)^(m+2*p)*(b+c*x^2)^p,x) <-- FreeQ([b,c,d,m],x) &&  IntegerQ(p) =#
    #= Antiderivative(((~d')*(~x))^(~m')*((~b')*(~x)^2+(~c')*(~x)^4)^(~p),~x) :=  (b*x^2+c*x^4)^p/((d*x)^(2*p)*(b+c*x^2)^p)*Antiderivative((d*x)^(m+2*p)*(b+c*x^2)^ p,x) <-- FreeQ([b,c,d,m,p],x) && Not(IntegerQ(p)) =#
    @apply_utils Antiderivative(~x * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~(p'), ~x) => (1 / 2) * Subst(Antiderivative((~a + ~b * ~x + ~c * (~x) ^ 2) ^ ~p, ~x), ~x, (~x) ^ 2) <-- FreeQ([~a, ~b, ~c, ~p], ~x)
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~(p'), ~x) => Antiderivative(ExpandIntegrand((~d * ~x) ^ ~m * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ ~p, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~m], ~x) && (IGtQ(~p, 0) && Not(IntegerQ((~m + 1) / 2)))
    #= Antiderivative(((~d')*(~x))^(~m')*((~a)+(~b')*(~x)^2+(~c')*(~x)^4)^(~p'),~x) :=  1/c^p*Antiderivative((d*x)^m*(b/2+c*x^2)^(2*p),x) <-- FreeQ([a,b,c,d,m,p],x) &&  EqQ(b^2-4*a*c,0) && IntegerQ(p) =#
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (2 * (~d * ~x) ^ (~m + 1) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (~d * (~m + 3) * (2 * ~a + ~b * (~x) ^ 2)) - ((~d * ~x) ^ (~m + 1) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (2 * ~a * ~d * (~m + 3) * (~p + 1)) <-- FreeQ([~a, ~b, ~c, ~d, ~m, ~p], ~x) && (EqQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (Not(IntegerQ(~p)) && (EqQ(~m + 4 * ~p + 5, 0) && LtQ(~p, -1))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => ((~d * ~x) ^ (~m + 1) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (4 * ~a * ~d * (~p + 1) * (2 * ~p + 1)) - ((~d * ~x) ^ (~m + 1) * (2 * ~a + ~b * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ ~p) / (4 * ~a * ~d * (2 * ~p + 1)) <-- FreeQ([~a, ~b, ~c, ~d, ~m, ~p], ~x) && (EqQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (Not(IntegerQ(~p)) && (EqQ(~m + 4 * ~p + 5, 0) && NeQ(~p, -1 / 2))))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (1 / 2) * Subst(Antiderivative((~x) ^ ((~m - 1) / 2) * (~a + ~b * ~x + ~c * (~x) ^ 2) ^ ~p, ~x), ~x, (~x) ^ 2) <-- FreeQ([~a, ~b, ~c, ~p], ~x) && (EqQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (IntegerQ(~p - 1 / 2) && (IntegerQ((~m - 1) / 2) && (GtQ(~m, 0) || LtQ(0, 4 * ~p, -(~m) - 1)))))
    #= Antiderivative(((~d')*(~x))^(~m')*((~a)+(~b')*(~x)^2+(~c')*(~x)^4)^(~p),~x) :=  c*(a+b*x^2+c*x^4)^(p+1)/(b/2+c*x^2)^(2*(p+1))*Antiderivative((d*x)^m*(b/2+c*x^2)^ (2*p),x) <-- FreeQ([a,b,c,d,m,p],x) && EqQ(b^2-4*a*c,0) &&  IntegerQ(p-1/2) && IGeQ(m,2*p) =#
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => ((~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ FracPart(~p) / ((~c) ^ IntPart(~p) * (~b / 2 + ~c * (~x) ^ 2) ^ (2 * FracPart(~p)))) * Antiderivative((~d * ~x) ^ ~m * (~b / 2 + ~c * (~x) ^ 2) ^ (2 * ~p), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~m, ~p], ~x) && (EqQ((~b) ^ 2 - 4 * ~a * ~c, 0) && IntegerQ(~p - 1 / 2))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (((~a) ^ IntPart(~p) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ FracPart(~p)) / (1 + (2 * ~c * (~x) ^ 2) / ~b) ^ (2 * FracPart(~p))) * Antiderivative((~d * ~x) ^ ~m * (1 + (2 * ~c * (~x) ^ 2) / ~b) ^ (2 * ~p), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~m, ~p], ~x) && (EqQ((~b) ^ 2 - 4 * ~a * ~c, 0) && Not(IntegerQ(2 * ~p)))
    @apply_utils Antiderivative((~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~(p'), ~x) => (1 / 2) * Subst(Antiderivative((~x) ^ ((~m - 1) / 2) * (~a + ~b * ~x + ~c * (~x) ^ 2) ^ ~p, ~x), ~x, (~x) ^ 2) <-- FreeQ([~a, ~b, ~c, ~p], ~x) && IntegerQ((~m - 1) / 2)
    @apply_utils Antiderivative((~(d') * ~x) ^ ~m * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => With([k = Denominator(~m)], (k / ~d) * Subst(Antiderivative((~x) ^ (k * (~m + 1) - 1) * (~a + (~b * (~x) ^ (2k)) / (~d) ^ 2 + (~c * (~x) ^ (4k)) / (~d) ^ 4) ^ ~p, ~x), ~x, (~d * ~x) ^ (1 / k))) <-- FreeQ([~a, ~b, ~c, ~d, ~p], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (FractionQ(~m) && IntegerQ(~p)))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (~d * (~d * ~x) ^ (~m - 1) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ ~p * (2 * ~b * ~p + ~c * ((~m + 4 * ~p) - 1) * (~x) ^ 2)) / (~c * (~m + 4 * ~p + 1) * ((~m + 4 * ~p) - 1)) - ((2 * ~p * (~d) ^ 2) / (~c * (~m + 4 * ~p + 1) * ((~m + 4 * ~p) - 1))) * Antiderivative((~d * ~x) ^ (~m - 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p - 1) * Simp(~a * ~b * (~m - 1) - (2 * ~a * ~c * ((~m + 4 * ~p) - 1) - (~b) ^ 2 * ((~m + 2 * ~p) - 1)) * (~x) ^ 2, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (GtQ(~p, 0) && (GtQ(~m, 1) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m))))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => ((~d * ~x) ^ (~m + 1) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ ~p) / (~d * (~m + 1)) - ((2 * ~p) / ((~d) ^ 2 * (~m + 1))) * Antiderivative((~d * ~x) ^ (~m + 2) * (~b + 2 * ~c * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p - 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (GtQ(~p, 0) && (LtQ(~m, -1) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m))))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => ((~d * ~x) ^ (~m + 1) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ ~p) / (~d * (~m + 4 * ~p + 1)) + ((2 * ~p) / (~m + 4 * ~p + 1)) * Antiderivative((~d * ~x) ^ ~m * (2 * ~a + ~b * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p - 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~m], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (GtQ(~p, 0) && (NeQ(~m + 4 * ~p + 1, 0) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m))))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (~d * (~d * ~x) ^ (~m - 1) * (~b + 2 * ~c * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (2 * (~p + 1) * ((~b) ^ 2 - 4 * ~a * ~c)) - ((~d) ^ 2 / (2 * (~p + 1) * ((~b) ^ 2 - 4 * ~a * ~c))) * Antiderivative((~d * ~x) ^ (~m - 2) * (~b * (~m - 1) + 2 * ~c * (~m + 4 * ~p + 5) * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (LtQ(~p, -1) && (GtQ(~m, 1) && (LeQ(~m, 3) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m)))))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (-((~d) ^ 3) * (~d * ~x) ^ (~m - 3) * (2 * ~a + ~b * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (2 * (~p + 1) * ((~b) ^ 2 - 4 * ~a * ~c)) + ((~d) ^ 4 / (2 * (~p + 1) * ((~b) ^ 2 - 4 * ~a * ~c))) * Antiderivative((~d * ~x) ^ (~m - 4) * (2 * ~a * (~m - 3) + ~b * (~m + 4 * ~p + 3) * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (LtQ(~p, -1) && (GtQ(~m, 3) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m))))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (-((~d * ~x) ^ (~m + 1)) * (((~b) ^ 2 - 2 * ~a * ~c) + ~b * ~c * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (2 * ~a * ~d * (~p + 1) * ((~b) ^ 2 - 4 * ~a * ~c)) + (1 / (2 * ~a * (~p + 1) * ((~b) ^ 2 - 4 * ~a * ~c))) * Antiderivative((~d * ~x) ^ ~m * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1) * Simp(((~b) ^ 2 * (~m + 2 * ~p + 3) - 2 * ~a * ~c * (~m + 4 * ~p + 5)) + ~b * ~c * (~m + 4 * ~p + 7) * (~x) ^ 2, ~x), ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~m], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (LtQ(~p, -1) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m)))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => ((~d) ^ 3 * (~d * ~x) ^ (~m - 3) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (~c * (~m + 4 * ~p + 1)) - ((~d) ^ 4 / (~c * (~m + 4 * ~p + 1))) * Antiderivative((~d * ~x) ^ (~m - 4) * Simp(~a * (~m - 3) + ~b * ((~m + 2 * ~p) - 1) * (~x) ^ 2, ~x) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~p], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (GtQ(~m, 3) && (NeQ(~m + 4 * ~p + 1, 0) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m))))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~m * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => ((~d * ~x) ^ (~m + 1) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ (~p + 1)) / (~a * ~d * (~m + 1)) - (1 / (~a * (~d) ^ 2 * (~m + 1))) * Antiderivative((~d * ~x) ^ (~m + 2) * (~b * (~m + 2 * ~p + 3) + ~c * (~m + 4 * ~p + 5) * (~x) ^ 2) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~p], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (LtQ(~m, -1) && (IntegerQ(2 * ~p) && (IntegerQ(~p) || IntegerQ(~m)))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~m / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => (~d * ~x) ^ (~m + 1) / (~a * ~d * (~m + 1)) - (1 / (~a * (~d) ^ 2)) * Antiderivative(((~d * ~x) ^ (~m + 2) * (~b + ~c * (~x) ^ 2)) / (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && LtQ(~m, -1))
    @apply_utils Antiderivative((~x) ^ ~m / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => Antiderivative(PolynomialDivide((~x) ^ ~m, ~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4, ~x), ~x) <-- FreeQ([~a, ~b, ~c], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && IGtQ(~m, 5))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~m / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => ((~d) ^ 3 * (~d * ~x) ^ (~m - 3)) / (~c * (~m - 3)) - ((~d) ^ 4 / ~c) * Antiderivative(((~d * ~x) ^ (~m - 4) * (~a + ~b * (~x) ^ 2)) / (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && GtQ(~m, 3))
    @apply_utils Antiderivative((~x) ^ 2 / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt(~a / ~c, 2)], (1 / 2) * Antiderivative((q + (~x) ^ 2) / (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) - (1 / 2) * Antiderivative((q - (~x) ^ 2) / (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x)) <-- FreeQ([~a, ~b, ~c], ~x) && (LtQ((~b) ^ 2 - 4 * ~a * ~c, 0) && PosQ(~a * ~c))
    @apply_utils Antiderivative((~x) ^ ~(m') / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt(~a / ~c, 2)], With([r = Rt(2q - ~b / ~c, 2)], (1 / (2 * ~c * r)) * Antiderivative(((~x) ^ (~m - 3) * (q + r * ~x)) / (q + r * ~x + (~x) ^ 2), ~x) - (1 / (2 * ~c * r)) * Antiderivative(((~x) ^ (~m - 3) * (q - r * ~x)) / ((q - r * ~x) + (~x) ^ 2), ~x))) <-- FreeQ([~a, ~b, ~c], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (GeQ(~m, 3) && (LtQ(~m, 4) && NegQ((~b) ^ 2 - 4 * ~a * ~c))))
    @apply_utils Antiderivative((~x) ^ ~(m') / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt(~a / ~c, 2)], With([r = Rt(2q - ~b / ~c, 2)], (1 / (2 * ~c * r)) * Antiderivative((~x) ^ (~m - 1) / ((q - r * ~x) + (~x) ^ 2), ~x) - (1 / (2 * ~c * r)) * Antiderivative((~x) ^ (~m - 1) / (q + r * ~x + (~x) ^ 2), ~x))) <-- FreeQ([~a, ~b, ~c], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (GeQ(~m, 1) && (LtQ(~m, 3) && NegQ((~b) ^ 2 - 4 * ~a * ~c))))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~m / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], ((~d) ^ 2 / 2) * (~b / q + 1) * Antiderivative((~d * ~x) ^ (~m - 2) / (~b / 2 + q / 2 + ~c * (~x) ^ 2), ~x) - ((~d) ^ 2 / 2) * (~b / q - 1) * Antiderivative((~d * ~x) ^ (~m - 2) / ((~b / 2 - q / 2) + ~c * (~x) ^ 2), ~x)) <-- FreeQ([~a, ~b, ~c, ~d], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && GeQ(~m, 2))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') / (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], (~c / q) * Antiderivative((~d * ~x) ^ ~m / ((~b / 2 - q / 2) + ~c * (~x) ^ 2), ~x) - (~c / q) * Antiderivative((~d * ~x) ^ ~m / (~b / 2 + q / 2 + ~c * (~x) ^ 2), ~x)) <-- FreeQ([~a, ~b, ~c, ~d, ~m], ~x) && NeQ((~b) ^ 2 - 4 * ~a * ~c, 0)
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], 2 * sqrt(-(~c)) * Antiderivative((~x) ^ 2 / (sqrt(~b + q + 2 * ~c * (~x) ^ 2) * sqrt((-(~b) + q) - 2 * ~c * (~x) ^ 2)), ~x)) <-- FreeQ([~a, ~b, ~c], ~x) && (GtQ((~b) ^ 2 - 4 * ~a * ~c, 0) && LtQ(~c, 0))
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt(~c / ~a, 2)], (1 / q) * Antiderivative(1 / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) - (1 / q) * Antiderivative((1 - q * (~x) ^ 2) / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x)) <-- FreeQ([~a, ~b, ~c], ~x) && (GtQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (GtQ(~c / ~a, 0) && LtQ(~b / ~a, 0)))
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], (-((~b - q)) / (2 * ~c)) * Antiderivative(1 / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) + (1 / (2 * ~c)) * Antiderivative(((~b - q) + 2 * ~c * (~x) ^ 2) / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x)) <-- FreeQ([~a, ~b, ~c], ~x) && (GtQ((~b) ^ 2 - 4 * ~a * ~c, 0) && (LtQ(~a, 0) && GtQ(~c, 0)))
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], (~x * (~b + q + 2 * ~c * (~x) ^ 2)) / (2 * ~c * sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4)) - ((Rt((~b + q) / (2 * ~a), 2) * (2 * ~a + (~b + q) * (~x) ^ 2) * sqrt((2 * ~a + (~b - q) * (~x) ^ 2) / (2 * ~a + (~b + q) * (~x) ^ 2))) / (2 * ~c * sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4))) * Elliptic.E(atan(Rt((~b + q) / (2 * ~a), 2) * ~x), (2q) / (~b + q)) <-- PosQ((~b + q) / ~a) && Not(PosQ((~b - q) / ~a) && SimplerSqrtQ((~b - q) / (2 * ~a), (~b + q) / (2 * ~a)))) <-- FreeQ([~a, ~b, ~c], ~x) && GtQ((~b) ^ 2 - 4 * ~a * ~c, 0)
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], (~x * ((~b - q) + 2 * ~c * (~x) ^ 2)) / (2 * ~c * sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4)) - ((Rt((~b - q) / (2 * ~a), 2) * (2 * ~a + (~b - q) * (~x) ^ 2) * sqrt((2 * ~a + (~b + q) * (~x) ^ 2) / (2 * ~a + (~b - q) * (~x) ^ 2))) / (2 * ~c * sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4))) * Elliptic.E(atan(Rt((~b - q) / (2 * ~a), 2) * ~x), (-2q) / (~b - q)) <-- PosQ((~b - q) / ~a)) <-- FreeQ([~a, ~b, ~c], ~x) && GtQ((~b) ^ 2 - 4 * ~a * ~c, 0)
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], (-((~b + q)) / (2 * ~c)) * Antiderivative(1 / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) + (1 / (2 * ~c)) * Antiderivative((~b + q + 2 * ~c * (~x) ^ 2) / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) <-- NegQ((~b + q) / ~a) && Not(NegQ((~b - q) / ~a) && SimplerSqrtQ(-((~b - q)) / (2 * ~a), -((~b + q)) / (2 * ~a)))) <-- FreeQ([~a, ~b, ~c], ~x) && GtQ((~b) ^ 2 - 4 * ~a * ~c, 0)
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], (-((~b - q)) / (2 * ~c)) * Antiderivative(1 / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) + (1 / (2 * ~c)) * Antiderivative(((~b - q) + 2 * ~c * (~x) ^ 2) / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) <-- NegQ((~b - q) / ~a)) <-- FreeQ([~a, ~b, ~c], ~x) && GtQ((~b) ^ 2 - 4 * ~a * ~c, 0)
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt(~c / ~a, 2)], (1 / q) * Antiderivative(1 / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x) - (1 / q) * Antiderivative((1 - q * (~x) ^ 2) / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4), ~x)) <-- FreeQ([~a, ~b, ~c], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && PosQ(~c / ~a))
    @apply_utils Antiderivative((~x) ^ 2 / sqrt(~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4), ~x) => With([q = Rt((~b) ^ 2 - 4 * ~a * ~c, 2)], ((sqrt(1 + (2 * ~c * (~x) ^ 2) / (~b - q)) * sqrt(1 + (2 * ~c * (~x) ^ 2) / (~b + q))) / sqrt(~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4)) * Antiderivative((~x) ^ 2 / (sqrt(1 + (2 * ~c * (~x) ^ 2) / (~b - q)) * sqrt(1 + (2 * ~c * (~x) ^ 2) / (~b + q))), ~x)) <-- FreeQ([~a, ~b, ~c], ~x) && (NeQ((~b) ^ 2 - 4 * ~a * ~c, 0) && NegQ(~c / ~a))
    @apply_utils Antiderivative((~(d') * ~x) ^ ~(m') * (~a + ~(b') * (~x) ^ 2 + ~(c') * (~x) ^ 4) ^ ~p, ~x) => (((~a) ^ IntPart(~p) * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ 4) ^ FracPart(~p)) / ((1 + (2 * ~c * (~x) ^ 2) / (~b + Rt((~b) ^ 2 - 4 * ~a * ~c, 2))) ^ FracPart(~p) * (1 + (2 * ~c * (~x) ^ 2) / (~b - Rt((~b) ^ 2 - 4 * ~a * ~c, 2))) ^ FracPart(~p))) * Antiderivative((~d * ~x) ^ ~m * (1 + (2 * ~c * (~x) ^ 2) / (~b + sqrt((~b) ^ 2 - 4 * ~a * ~c))) ^ ~p * (1 + (2 * ~c * (~x) ^ 2) / (~b - sqrt((~b) ^ 2 - 4 * ~a * ~c))) ^ ~p, ~x) <-- FreeQ([~a, ~b, ~c, ~d, ~m, ~p], ~x)
    @apply_utils Antiderivative((~u) ^ ~(m') * (~(a') + ~(b') * (~v) ^ 2 + ~(c') * (~v) ^ 4) ^ ~(p'), ~x) => ((~u) ^ ~m / (Coefficient(~v, ~x, 1) * (~v) ^ ~m)) * Subst(Antiderivative((~x) ^ ~m * (~a + ~b * (~x) ^ 2 + ~c * (~x) ^ (2 * 2)) ^ ~p, ~x), ~x, ~v) <-- FreeQ([~a, ~b, ~c, ~m, ~p], ~x) && LinearPairQ(~u, ~v, ~x)
end

max_stars_repo_path: src/rules/1 Algebraic functions/1.2 Trinomial products/1.2.2 Quartic/1.2.2.2 (d x)^m (a+b x^2+c x^4)^p.jl


text:
using BinaryProvider # requires BinaryProvider 0.4.0 or later

const forcecompile = get(ENV, "JULIA_SPECIALFUNCTIONS_BUILD_SOURCE", "false") == "true"

# Parse some basic command-line arguments
const verbose = "--verbose" in ARGS
const prefix = Prefix(get(filter(!isequal("verbose"), ARGS), 1, joinpath(@__DIR__, "usr")))
products = [
    LibraryProduct(prefix, String["libopenspecfun"], :openspecfun),
]

# Download binaries from hosted location
bin_prefix = "https://github.com/JuliaMath/OpenspecfunBuilder/releases/download/v0.5.3-3"

# Listing of files generated by BinaryBuilder:
download_info = Dict(
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.aarch64-linux-gnu-gcc4.tar.gz",
         "7afa17d39b0e764cb2485b4487819bd6cc2d0ade1d66eccf244a61f06022ee20"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.aarch64-linux-gnu-gcc7.tar.gz",
         "2502c8ff21078d78de3d2da039f0ff27510513e88389287dedc8a0dae0226732"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.aarch64-linux-gnu-gcc8.tar.gz",
         "fd1a373bd618ce07aa76984c1532c1e1262c138907fd9bc2d988e4b97ee87459"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.aarch64-linux-musl-gcc4.tar.gz",
         "f2e09335ce251148bad2442af8ca3f4da554adb0020731ba81b760c0f4ca9bf0"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.aarch64-linux-musl-gcc7.tar.gz",
         "c407910c4cf3bafa54302f804e31b57bd1c4a6179e416cd04b45389243481a98"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.aarch64-linux-musl-gcc8.tar.gz",
         "67b7450516bc0638bf34634f7036fb4d1f6e27f40f30e9d1d1096896a3928817"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.arm-linux-gnueabihf-gcc4.tar.gz",
         "56c9d3fe31d14806060f2c65a2e52b0b4c74b08b801060e5f7897e738fdff624"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.arm-linux-gnueabihf-gcc7.tar.gz",
         "877032790dc274df0a12c8c7507991b76b46525173f8ef19b703c3ca598cd2dc"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.arm-linux-gnueabihf-gcc8.tar.gz",
         "3af0a5535645bfe1132c7226f2c9b26277d272df0fa1d22515755a3d6180078d"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.arm-linux-musleabihf-gcc4.tar.gz",
         "1dd62ca5d5971b068074cb6dd1fd35965cba17ae01d221f5c6ab71a478a7fa4b"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.arm-linux-musleabihf-gcc7.tar.gz",
         "586b280f6cc22fb05cba9054a5d8fecff353e6eb1cf1a55c83fdcd5b24ef2683"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.arm-linux-musleabihf-gcc8.tar.gz",
         "1938a8cd65dfe3c1a6a118f6eb751ffa70be7af817fd29831f0ffbfdbce703ab"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-linux-gnu-gcc4.tar.gz",
         "9e15a95e03428a5155ab8540d4b1c02c4da9bc1f66e9cf0f6829d752be35d233"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-linux-gnu-gcc7.tar.gz",
         "6f606b30921671e6b2747274f2ee1c42c67309116e1be4f2efa4d95f2c2480d6"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-linux-gnu-gcc8.tar.gz",
         "1112ab4f6114eed32aea6079bfd69140b5a057b6c94bd8e0a033d283906f5d6b"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-linux-musl-gcc4.tar.gz",
         "4f2c6c225f3f299918bfe96c09d3ea6699e447cf4b0615a45614b6df7b8700da"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-linux-musl-gcc7.tar.gz",
         "d4ba0747da4a8884a96a0ddab5a7b8387724a7395caa46e4b4d4de63cd894756"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-linux-musl-gcc8.tar.gz",
         "adff04533751ba348905f490f774c4cc998d0c96704e0c0883ba73b2c6f0d6b0"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-w64-mingw32-gcc4.tar.gz",
         "9f6816bdf1a326ba7af7b51befc2a39a159b6be439ea9e0f7e6908e44b249f4f"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-w64-mingw32-gcc7.tar.gz",
         "2d2c10f0620ffd4575b2b0f3c99dc451f189f9b41d9ff7c1770ba0836433c86b"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.i686-w64-mingw32-gcc8.tar.gz",
         "abbef77bf9404c65a3505e0f86c62b45711d7421b183636e81803f8cfb27a321"),
    Linux(:powerpc64le, libc=:glibc, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.powerpc64le-linux-gnu-gcc4.tar.gz",
         "786ba99b3fec5a1d03f0c3f4df670105439994a2fc105a3bfe5214bb923adcb1"),
    Linux(:powerpc64le, libc=:glibc, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.powerpc64le-linux-gnu-gcc7.tar.gz",
         "78334155f048bc13d65a6d8a8c60cd03dfae3cb23ae29c957b2db2f903e93bcb"),
    Linux(:powerpc64le, libc=:glibc, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.powerpc64le-linux-gnu-gcc8.tar.gz",
         "8fc88fe37ce3111a21c2c4b4e942f1d6dd744ef1e685be363ce9813d49f7fa19"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-apple-darwin14-gcc4.tar.gz",
         "c9520ef62e04208576bf13c50a89ddf3c59b8b73119aae8b7bc5630e916b4f19"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-apple-darwin14-gcc7.tar.gz",
         "56b145b4d1e86ca79dcb09bd1d8b35d32053149a532bb1019c4ef420c13171bb"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-apple-darwin14-gcc8.tar.gz",
         "8380ed0e7f2ce4e8e7b382aed41855ece7865e03c76a5adb2dcfce116e312dd1"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-linux-gnu-gcc4.tar.gz",
         "37d97bff1c43e1e4c8ace825d0bd171a50439b32c913437cef8b720375ad08dc"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-linux-gnu-gcc7.tar.gz",
         "a41e8dd20c4f68021ca857245912a4d0c5a26a4f149985ab9e82ddf4d76997e1"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-linux-gnu-gcc8.tar.gz",
         "39333e2fc84a242d632a1604fd042679e5faed6ea3ba311a1896f1b8f76eab79"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-linux-musl-gcc4.tar.gz",
         "e3a2699920fd1746ad45bd4a8f5589305d22d30c5a5da44ada23bb5662139bc2"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-linux-musl-gcc7.tar.gz",
         "828284dff268dd5279a5546ec31abcf087953546d55d4160de8a0e27dd323f52"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-linux-musl-gcc8.tar.gz",
         "83710ec095df0496de04a51d3f89edf68a925f1d126d11db39f0dd64d077b1e8"),
    FreeBSD(:x86_64, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-unknown-freebsd11.1-gcc4.tar.gz",
         "84d5cfae325cd6b28c40c99e37b84cb0edef35d137e527da1d7aadd79848b886"),
    FreeBSD(:x86_64, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-unknown-freebsd11.1-gcc7.tar.gz",
         "d48fb50fc088d1a97dd8d270b164394dcc15b927b9534f59c6e806ee41536812"),
    FreeBSD(:x86_64, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-unknown-freebsd11.1-gcc8.tar.gz",
         "53170e3ea896fb1afe4457a12c6ab873fc658dc8d8e0534d37d64b598ded1aa2"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc4)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-w64-mingw32-gcc4.tar.gz",
         "f1002f31916a85bd065e1573920516a97e66ca50ebadc9d56d894f2e694a966a"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc7)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-w64-mingw32-gcc7.tar.gz",
         "d37c26428d2db568fddab26189a98c4cce51702008d95db036aaca9ee3361602"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc8)) =>
        ("$bin_prefix/Openspecfun.v0.5.3.x86_64-w64-mingw32-gcc8.tar.gz",
         "266608d0392a69179db6f2e1bfca782d634203a34cb56adb256d31b91a3362fc"),
)

# Install unsatisfied or updated dependencies:
unsatisfied = any(p->!satisfied(p; verbose=verbose), products)
to_download = choose_download(download_info, platform_key_abi())
if to_download !== nothing && !forcecompile
    if !isinstalled(to_download...; prefix=prefix)
        # Download and install binaries
        install(to_download...; prefix=prefix, force=true, verbose=verbose)
        unsatisfied = any(p->!satisfied(p; verbose=verbose), products)
    end
    if unsatisfied
        rm(joinpath(@__DIR__, "usr", "lib"); force=true, recursive=true)
    end
end
if unsatisfied || forcecompile
    include("scratch.jl")
else
    # Write out a deps.jl file that will contain mappings for our products
    write_deps_file(joinpath(@__DIR__, "deps.jl"), products; verbose=verbose)
end


text:
using Dash
using DashHtmlComponents
using DashCoreComponents

outputs = [
    [nothing, ""],
    ["a string", "a string"],
    [123, "123"],
    [123.45, "123.45"],
    [[6, 7, 8], "678"],
    [["a", "list", "of", "strings"], "alistofstrings"],
    [["strings", 2, "numbers"], "strings2numbers"],
    [["a string", html_div("and a div")], "a string\nand a div"],
]
app = dash()

app.layout = html_div() do
    html_button(id="btn", children = "Update Input"),
    html_div(id = "out", children = "init")
end

callback!(app, callid"btn.n_clicks => out.children") do n_clicks
    (isnothing(n_clicks) || n_clicks > length(outputs)) && throw(PreventUpdate())
    return outputs[n_clicks][1]
end

run_server(app)


text:
"""
# module HyperGraphTools

- Julia version:
- Author: bramb
- Date: 2019-02-13

# Examples

```jldoctest
julia>
```
"""
module HyperGraphTools
    include("simple_tagml_tokenizer.jl")
    include("visualization.jl")
    include("earley.jl")
    include("cyk.jl")
end

max_stars_repo_name: brambg/HyperGraphTools

max_issues_repo_name: brambg/HyperGraphTools

max_forks_repo_name: brambg/HyperGraphTools


text:
# Request handlers

function run_notification(conn, state::DebuggerState, params::NamedTuple{(:program,),Tuple{String}})
    @debug "run_request"

    state.debug_mode = :launch

    put!(state.next_cmd, (cmd = :run, program = params.program))
end

function debug_notification(conn, state::DebuggerState, params::DebugArguments)
    @debug "debug_request" params = params

    state.debug_mode = :launch

    filename_to_debug = params.program

    @debug "We are debugging the file $filename_to_debug."

    put!(state.next_cmd, (cmd = :set_source_path, source_path = filename_to_debug))

    file_content = try
        read(filename_to_debug, String)
    catch err
        # TODO Think about some way to return an error message in the UI
        JSONRPC.send(conn, finished_notification_type, nothing)
        put!(state.next_cmd, (cmd = :stop,))
        return
    end

    ex = Base.parse_input_line(file_content; filename = filename_to_debug)

    # handle a case when lowering fails
    if !is_valid_expression(ex)
        # TODO Think about some way to return an error message in the UI
        JSONRPC.send(conn, finished_notification_type, nothing)
        put!(state.next_cmd, (cmd = :stop,))
        return
    end

    params.compiledModulesOrFunctions !== missing && set_compiled_items_request(conn, state, (compiledModulesOrFunctions = params.compiledModulesOrFunctions,))
    params.compiledMode !== missing && set_compiled_mode_request(conn, state, (compiledMode = params.compiledMode,))

    state.expr_splitter = JuliaInterpreter.ExprSplitter(Main, ex)
    state.frame = get_next_top_level_frame(state)

    if params.stopOnEntry
        JSONRPC.send(conn, stopped_notification_type, StoppedEventArguments("entry", missing, 1, missing, missing, missing))
    elseif JuliaInterpreter.shouldbreak(state.frame, state.frame.pc)
        JSONRPC.send(conn, stopped_notification_type, StoppedEventArguments("breakpoint", missing, 1, missing, missing, missing))
    else
        put!(state.next_cmd, (cmd = :continue,))
    end
end

is_valid_expression(x) = true # atom
is_valid_expression(::Nothing) = false # empty
is_valid_expression(ex::Expr) = !Meta.isexpr(ex, (:incomplete, :error))

function exec_notification(conn, state::DebuggerState, params::ExecArguments)
    @debug "exec_request" params = params

    state.debug_mode = :attach

    state.sources[0] = params.code

    @debug "setting source_path" file = params.file
    put!(state.next_cmd, (cmd = :set_source_path, source_path = params.file))

    params.compiledModulesOrFunctions !== missing && set_compiled_items_request(conn, state, (compiledModulesOrFunctions = params.compiledModulesOrFunctions,))
    params.compiledMode !== missing && set_compiled_mode_request(conn, state, (compiledMode = params.compiledMode,))

    ex = Meta.parse(params.code)
    state.expr_splitter = JuliaInterpreter.ExprSplitter(Main, ex) # TODO: line numbers ?
    state.frame = get_next_top_level_frame(state)

    if params.stopOnEntry
        JSONRPC.send(conn, stopped_notification_type, StoppedEventArguments("entry", missing, 1, missing, missing, missing))
    elseif JuliaInterpreter.shouldbreak(state.frame, state.frame.pc)
        JSONRPC.send(conn, stopped_notification_type, StoppedEventArguments("breakpoint", missing, 1, missing, missing, missing))
    else
        put!(state.next_cmd, (cmd = :continue,))
    end
end

function reset_compiled_items()
    @debug "reset_compiled_items"
    # reset compiled modules/methods
    empty!(JuliaInterpreter.compiled_modules)
    empty!(JuliaInterpreter.compiled_methods)
    empty!(JuliaInterpreter.interpreted_methods)
    JuliaInterpreter.set_compiled_methods()
    JuliaInterpreter.clear_caches()
end

function set_compiled_items_request(conn, state::DebuggerState, params)
    @debug "set_compiled_items_request"
    reset_compiled_items()
    state.not_yet_set_compiled_items = set_compiled_functions_modules!(params)
end

function set_compiled_mode_request(conn, state::DebuggerState, params)
    @debug "set_compiled_mode_request"

    if params.compiledMode
        state.compile_mode = JuliaInterpreter.Compiled()
    else
        state.compile_mode = JuliaInterpreter.finish_and_return!
    end
end

function set_compiled_functions_modules!(items::Vector{String})
    @debug "set_compiled_functions_modules!"

    unset = String[]

    @debug "setting as compiled" items = items

    remove_later_modules = Module[]

    # user wants these compiled:
    for acc in items
        is_interpreted = startswith(acc, '-') && length(acc) > 1
        if is_interpreted
            acc = acc[2:end]
        end
        all_submodules = endswith(acc, '.')
        acc = strip(acc, '.')
        obj = get_obj_by_accessor(acc)

        if obj === nothing
            push!(unset, acc)
            continue
        end

        if is_interpreted
            if obj isa Base.Callable
                try
                    for m in methods(Base.unwrap_unionall(obj))
                        push!(JuliaInterpreter.interpreted_methods, m)
                    end
                    continue
                catch err
                    @warn "Setting $obj as an interpreted method failed."
                end
            elseif obj isa Module
                push!(remove_later_modules, obj)
            end
        end

        if obj isa Module
            push!(JuliaInterpreter.compiled_modules, obj)
            if all_submodules
                compile_mode_for_all_submodules(obj)
            end
        elseif obj isa Base.Callable
            for m in methods(Base.unwrap_unionall(obj))
                push!(JuliaInterpreter.compiled_methods, m)
            end
        end
    end

    for mod in remove_later_modules
        delete!(JuliaInterpreter.compiled_modules, mod)
    end

    @debug "could not set as compiled" unset = unset
    return unset
end

function set_compiled_functions_modules!(params)
    if params.compiledModulesOrFunctions isa Vector && length(params.compiledModulesOrFunctions) > 0
        return set_compiled_functions_modules!(params.compiledModulesOrFunctions)
    end
    return []
end

function compile_mode_for_all_submodules(mod, seen = Set())
    for name in names(mod; all = true)
        if isdefined(mod, name)
            obj = getfield(mod, name)
            if obj !== mod && obj isa Module && !(obj in seen)
                push!(seen, obj)
                push!(JuliaInterpreter.compiled_modules, obj)
                compile_mode_for_all_submodules(obj, seen)
            end
        end
    end
end

function get_obj_by_accessor(accessor, super = nothing)
    parts = split(accessor, '.')
    @assert length(parts) > 0
    top = popfirst!(parts)
    if super === nothing
        # try getting module from loaded_modules_array first and then from Main:
        loaded_modules = Base.loaded_modules_array()
        ind = findfirst(==(top), string.(loaded_modules))
        if ind !== nothing
            root = loaded_modules[ind]
            if length(parts) > 0
                return get_obj_by_accessor(join(parts, '.'), root)
            end
            return root
        else
            return get_obj_by_accessor(accessor, Main)
        end
    else
        if isdefined(super, Symbol(top))
            this = getfield(super, Symbol(top))
            if length(parts) > 0
                if this isa Module
                    return get_obj_by_accessor(join(parts, '.'), this)
                end
            else
                return this
            end
        end
    end
    return nothing
end

function set_break_points_request(conn, state::DebuggerState, params::SetBreakpointsArguments)
    @debug "setbreakpoints_request"

    file = params.source.path

    for bp in JuliaInterpreter.breakpoints()
        if bp isa JuliaInterpreter.BreakpointFileLocation
            if bp.path == file
                JuliaInterpreter.remove(bp)
            end
        end
    end

    for bp in params.breakpoints
        condition = bp.condition === missing ? nothing : try
            Meta.parse(bp.condition)
        catch err
            @debug "invalid condition: `$(bp.condition)`. falling back to always breaking." exception = err
            nothing
        end

        @debug "Setting one breakpoint at line $(bp.line) with condition $(condition) in file $file."
        JuliaInterpreter.breakpoint(file, bp.line, condition)
    end

    SetBreakpointsResponseArguments([Breakpoint(true) for _ in params.breakpoints])
end

function set_exception_break_points_request(conn, state::DebuggerState, params::SetExceptionBreakpointsArguments)
    @debug "setexceptionbreakpoints_request"

    opts = Set(params.filters)

    if "error" in opts
        JuliaInterpreter.break_on(:error)
    else
        JuliaInterpreter.break_off(:error)
    end

    if "throw" in opts
        JuliaInterpreter.break_on(:throw)
    else
        JuliaInterpreter.break_off(:throw)
    end

    return SetExceptionBreakpointsResponseArguments(String[], missing)
end

function set_function_break_points_request(conn, state::DebuggerState, params::SetFunctionBreakpointsArguments)
    @debug "setfunctionbreakpoints_request"

    bps = map(params.breakpoints) do i
        decoded_name = i.name
        decoded_condition = i.condition

        parsed_condition = try
            decoded_condition == "" ? nothing : Meta.parse(decoded_condition)
        catch err
            @debug "invalid condition: `$(decoded_condition)`. falling back to always breaking." exception = err
            nothing
        end

        try
            parsed_name = Meta.parse(decoded_name)

            if parsed_name isa Symbol
                return (mod = Main, name = parsed_name, signature = nothing, condition = parsed_condition)
            elseif parsed_name isa Expr
                if parsed_name.head == :.
                    # TODO Support this case
                    return nothing
                elseif parsed_name.head == :call
                    all_args_are_legit = true
                    if length(parsed_name.args) > 1
                        for arg in parsed_name.args[2:end]
                            if !(arg isa Expr) || arg.head != Symbol("::") || length(arg.args) != 1
                                all_args_are_legit = false
                            end
                        end
                        if all_args_are_legit

                            return (mod = Main, name = parsed_name.args[1], signature = map(j -> j.args[1], parsed_name.args[2:end]), condition = parsed_condition)
                        else
                            return (mod = Main, name = parsed_name.args[1], signature = nothing, condition = parsed_condition)
                        end
                    else
                        return (mod = Main, name = parsed_name.args[1], signature = nothing, condition = parsed_condition)
                    end
                else
                    return nothing
        end
            else
                return nothing
            end
        catch err
            return nothing
        end

        return nothing
    end

    bps = filter(i -> i !== nothing, bps)

    for bp in JuliaInterpreter.breakpoints()
        if bp isa JuliaInterpreter.BreakpointSignature
            JuliaInterpreter.remove(bp)
        end
    end

    state.not_yet_set_function_breakpoints = Set{Any}(bps)

    attempt_to_set_f_breakpoints!(state.not_yet_set_function_breakpoints)

    return SetFunctionBreakpointsResponseArguments([Breakpoint(true) for i = 1:length(bps)])
end

function stack_trace_request(conn, state::DebuggerState, params::StackTraceArguments)
    @debug "getstacktrace_request"

    frames = StackFrame[]
    fr = state.frame

    if fr === nothing
        @info fr
        return StackTraceResponseArguments(frames, length(frames))
    end

    curr_fr = JuliaInterpreter.leaf(fr)

    frames_as_string = String[]

    id = 1
    while curr_fr !== nothing
        curr_scopeof = JuliaInterpreter.scopeof(curr_fr)
        curr_whereis = JuliaInterpreter.whereis(curr_fr)

        file_name = curr_whereis[1]
        lineno = curr_whereis[2]
        meth_or_mod_name = Base.nameof(curr_fr)

        # Is this a file from base?
        if !isabspath(file_name)
            file_name = basepath(file_name)
        end

        if isfile(file_name)
            push!(
                frames,
                StackFrame(
                    id,
                    string(meth_or_mod_name),
                    Source(
                        basename(file_name),
                        file_name,
                        missing,
                        missing,
                        "julia-adapter-data",
                        missing,
                        missing,
                        missing
                    ),
                    lineno,
                    0,
                    missing,
                    missing,
                    missing,
                    missing,
                    missing
                )
            )
        elseif curr_scopeof isa Method
            ret = JuliaInterpreter.CodeTracking.definition(String, curr_fr.framecode.scope)
            if ret !== nothing
                state.sources[state.next_source_id], loc = ret
                push!(
                    frames,
                    StackFrame(
                        id,
                        string(meth_or_mod_name),
                        Source(
                            file_name,
                            missing,
                            state.next_source_id,
                            missing,
                            missing,
                            missing,
                            missing,
                            missing
                        ),
                        lineno,
                        0,
                        missing,
                        missing,
                        missing,
                        missing,
                        missing
                    )
                )
                state.next_source_id += 1
            else
                src = curr_fr.framecode.src
                src = JuliaInterpreter.copy_codeinfo(src)
                JuliaInterpreter.replace_coretypes!(src; rev = true)
                code = Base.invokelatest(JuliaInterpreter.framecode_lines, src)

                state.sources[state.next_source_id] = join(code, '\n')

                push!(
                    frames,
                    StackFrame(
                        id,
                        string(meth_or_mod_name),
                        Source(
                            file_name,
                            missing,
                            state.next_source_id,
                            missing,
                            missing,
                            missing,
                            missing,
                            missing
                        ),
                        lineno,
                        0,
                        missing,
                        missing,
                        missing,
                        missing,
                        missing
                    )
                )
                state.next_source_id += 1
            end
        else
            # For now we are assuming that this can only happen
            # for code that is passed via the @enter or @run macros,
            # and that code we have stored as source with id 0
            push!(
                    frames,
                    StackFrame(
                        id,
                        string(meth_or_mod_name),
                        Source(
                            "REPL",
                            missing,
                            0,
                            missing,
                            missing,
                            missing,
                            missing,
                            missing
                        ),
                        lineno,
                        0,
                        missing,
                        missing,
                        missing,
                        missing,
                        missing
                    )
                )
        end

        id += 1
        curr_fr = curr_fr.caller
    end

    return StackTraceResponseArguments(frames, length(frames))
end

function scopes_request(conn, state::DebuggerState, params::ScopesArguments)
    @debug "getscope_request"
    empty!(state.varrefs)

    curr_fr = JuliaInterpreter.leaf(state.frame)

    i = 1

    while params.frameId > i
        curr_fr = curr_fr.caller
        i += 1
    end

    curr_scopeof = JuliaInterpreter.scopeof(curr_fr)
    curr_whereis = JuliaInterpreter.whereis(curr_fr)

    file_name = curr_whereis[1]
    code_range = curr_scopeof isa Method ? JuliaInterpreter.compute_corrected_linerange(curr_scopeof) : nothing

    push!(state.varrefs, VariableReference(:scope, curr_fr))
    local_var_ref_id = length(state.varrefs)

    push!(state.varrefs, VariableReference(:scope_globals, curr_fr))
    global_var_ref_id = length(state.varrefs)

    scopes = []

    if isfile(file_name) && code_range !== nothing
        push!(scopes, Scope(name = "Local", variablesReference = local_var_ref_id, expensive = false, source = Source(name = basename(file_name), path = file_name), line = code_range.start, endLine = code_range.stop))
        push!(scopes, Scope(name = "Global", variablesReference = global_var_ref_id, expensive = false, source = Source(name = basename(file_name), path = file_name), line = code_range.start, endLine = code_range.stop))
    else
        push!(scopes, Scope(name = "Local", variablesReference = local_var_ref_id, expensive = false))
        push!(scopes, Scope(name = "Global", variablesReference = global_var_ref_id, expensive = false))
    end

    curr_mod = JuliaInterpreter.moduleof(curr_fr)
    push!(state.varrefs, VariableReference(:module, curr_mod))

    push!(scopes, Scope(name = "Global ($(curr_mod))", variablesReference = length(state.varrefs), expensive = true))

    return ScopesResponseArguments(scopes)
end

function source_request(conn, state::DebuggerState, params::SourceArguments)
    @debug "getsource_request"

    source_id = params.source.sourceReference

    return SourceResponseArguments(state.sources[source_id], missing)
end

function construct_return_msg_for_var(state::DebuggerState, name, value)
    v_type = typeof(value)
    v_value_as_string = try
        Base.invokelatest(repr, value)
    catch err
        "Error while showing this value."
    end

    if (isstructtype(v_type) || value isa AbstractArray || value isa AbstractDict) && !(value isa String || value isa Symbol)
        push!(state.varrefs, VariableReference(:var, value))
        new_var_id = length(state.varrefs)

        named_count = if value isa Array || value isa Tuple
            0
        elseif value isa AbstractArray || value isa AbstractDict
            fieldcount(v_type) > 0 ? 1 : 0
            else
            fieldcount(v_type)
        end

        indexed_count = 0

        if value isa AbstractArray || value isa AbstractDict || value isa Tuple
            try
                indexed_count = Base.invokelatest(length, value)
            catch err
            end
        end

        return Variable(
            name = name,
            value = v_value_as_string,
            type = string(v_type),
            variablesReference = new_var_id,
            namedVariables = named_count,
            indexedVariables = indexed_count
        )
    else
        return Variable(name = name, value = v_value_as_string, type = string(v_type), variablesReference = 0)
    end
end

function construct_return_msg_for_var_with_undef_value(state::DebuggerState, name)
    v_type_as_string = ""

    return Variable(name = name, type = v_type_as_string, value = "#undef", variablesReference = 0)
end

function get_keys_with_drop_take(value, skip_count, take_count)
    collect(Iterators.take(Iterators.drop(keys(value), skip_count), take_count))
end

function get_cartesian_with_drop_take(value, skip_count, take_count)
    collect(Iterators.take(Iterators.drop(CartesianIndices(value), skip_count), take_count))
end

function collect_global_refs(frame::JuliaInterpreter.Frame)
    try
        m = JuliaInterpreter.scopeof(frame)
        m isa Method || return []

        func = frame.framedata.locals[1].value
        args = (Base.unwrap_unionall(m.sig).parameters[2:end]...,)

        ci = code_typed(func, args, optimize = false)[1][1]

        return collect_global_refs(ci)
    catch err
        @error err
        []
    end
end

function collect_global_refs(ci::Core.CodeInfo, refs = Set([]))
    for expr in ci.code
        collect_global_refs(expr, refs)
    end
    refs
end

function collect_global_refs(expr::Expr, refs = Set([]))
    args = Meta.isexpr(expr, :call) ? expr.args[2:end] : expr.args
    for arg in args
        collect_global_refs(arg, refs)
    end

    refs
end

collect_global_refs(expr, refs) = nothing
collect_global_refs(expr::GlobalRef, refs) = push!(refs, expr)

function push_module_names!(variables, state, mod)
    for n in names(mod, all = true)
        !isdefined(mod, n) && continue
        Base.isdeprecated(mod, n) && continue

        x = getfield(mod, n)
    x === Main && continue

        s = string(n)
        startswith(s, "#") && continue

        push!(variables, construct_return_msg_for_var(state, s, x))
    end
end

function variables_request(conn, state::DebuggerState, params::VariablesArguments)
    @debug "getvariables_request"

    var_ref_id = params.variablesReference

    filter_type = coalesce(params.filter, "")
    skip_count = coalesce(params.start, 0)
    take_count = coalesce(params.count, typemax(Int))

    var_ref = state.varrefs[var_ref_id]

    variables = Variable[]

    if var_ref.kind == :scope
        curr_fr = var_ref.value

        vars = JuliaInterpreter.locals(curr_fr)

        for v in vars
            # TODO Figure out why #self# is here in the first place
            # For now we don't report it to the client
            if !startswith(string(v.name), "#") && string(v.name) != ""
                push!(variables, construct_return_msg_for_var(state, string(v.name), v.value))
            end
        end

        if JuliaInterpreter.isexpr(JuliaInterpreter.pc_expr(curr_fr), :return)
            ret_val = JuliaInterpreter.get_return(curr_fr)
            push!(variables, construct_return_msg_for_var(state, "Return Value", ret_val))
        end
    elseif var_ref.kind == :scope_globals
        curr_fr = var_ref.value
        globals = collect_global_refs(curr_fr)

        for g in globals
            if isdefined(g.mod, g.name)
                push!(variables, construct_return_msg_for_var(state, string(g.name), getfield(g.mod, g.name)))
            end
        end
    elseif var_ref.kind == :module
        push_module_names!(variables, state, var_ref.value)
    elseif var_ref.kind == :var
        container_type = typeof(var_ref.value)

        if filter_type == "" || filter_type == "named"
            if (var_ref.value isa AbstractArray || var_ref.value isa AbstractDict) && !(var_ref.value isa Array) &&
                fieldcount(container_type) > 0
                push!(state.varrefs, VariableReference(:fields, var_ref.value))
                new_var_id = length(state.varrefs)
                named_count = fieldcount(container_type)

                push!(variables, Variable(
                    "Fields",
                    "",
                    "",
                    missing,
                    missing,
                    new_var_id,
                    named_count,
                    0,
                    missing
                ))
            elseif var_ref.value isa Module
                push_module_names!(variables, state, var_ref.value)
            else
                for i = Iterators.take(Iterators.drop(1:fieldcount(container_type), skip_count), take_count)
                    s = isdefined(var_ref.value, i) ?
                        construct_return_msg_for_var(state, string(fieldname(container_type, i)), getfield(var_ref.value, i)) :
                        construct_return_msg_for_var_with_undef_value(state, string(fieldname(container_type, i)))
                    push!(variables, s)
                end
            end
        end

        if (filter_type == "" || filter_type == "indexed")
            if var_ref.value isa Tuple
                for i in Iterators.take(Iterators.drop(1:length(var_ref.value), skip_count), take_count)
                    s = construct_return_msg_for_var(state, join(string.(i), ','), var_ref.value[i])
                    push!(variables, s)
                end
            elseif var_ref.value isa AbstractArray
                for i in Base.invokelatest(get_cartesian_with_drop_take, var_ref.value, skip_count, take_count)
                    s = ""
                    try
                        val = Base.invokelatest(getindex, var_ref.value, i)
                        s = construct_return_msg_for_var(state, join(string.(i.I), ','), val)
                    catch err
                        s = Variable(name = join(string.(i.I), ','), type = "", value = "#error", variablesReference = 0)
                    end
                    push!(variables, s)
                end
            elseif var_ref.value isa AbstractDict
                for i in Base.invokelatest(get_keys_with_drop_take, var_ref.value, skip_count, take_count)
                    key_as_string = try
                        Base.invokelatest(repr, i)
                    catch err
                        "Error while showing this value."
                    end
                    s = ""
                    try
                        val = Base.invokelatest(getindex, var_ref.value, i)
                        s = construct_return_msg_for_var(state, key_as_string, val)
                    catch err
                        s = Variable(
                            join(string.(i.I), ','),
                            "#error",
                            "",
                            missing,
                            missing,
                            0,
                            0,
                            0,
                            missing
                        )
                    end
                    push!(variables, s)
                end
            end
        end
    elseif var_ref.kind == :fields
        container_type = typeof(var_ref.value)

        if filter_type == "" || filter_type == "named"
            for i = Iterators.take(Iterators.drop(1:fieldcount(container_type), skip_count), take_count)
                s = isdefined(var_ref.value, i) ?
                    construct_return_msg_for_var(state, string(fieldname(container_type, i)), getfield(var_ref.value, i)) :
                    construct_return_msg_for_var_with_undef_value(state, string(fieldname(container_type, i)))
                push!(variables, s)
            end
        end

    end

    return VariablesResponseArguments(variables)
end

function set_variable_request(conn, state::DebuggerState, params::SetVariableArguments)
    varref_id = params.variablesReference
    var_name = params.name
    var_value = params.value

    val_parsed = try
        parsed = Meta.parse(var_value)

        if parsed isa Expr && !(parsed.head == :call || parsed.head == :vect || parsed.head == :tuple)
            return JSONRPC.JSONRPCError(-32600, "Only values or function calls are allowed.", nothing)
        end

        parsed
    catch err
        return JSONRPC.JSONRPCError(-32600, string("Something went wrong in the eval: ", sprint(showerror, err)), nothing)
    end

    var_ref = state.varrefs[varref_id]

    if var_ref.kind == :scope
        try
            ret = JuliaInterpreter.eval_code(var_ref.value, "$var_name = $var_value");

            s = construct_return_msg_for_var(state::DebuggerState, "", ret)

            return SetVariableResponseArguments(s.value, s.type, s.variablesReference, s.namedVariables, s.indexedVariables)
        catch err
            return JSONRPC.JSONRPCError(-32600, string("Something went wrong while setting the variable: ", sprint(showerror, err)), nothing)
        end
    elseif var_ref.kind == :var
        if isnumeric(var_name[1])
            try
                new_val = try
                    Core.eval(Main, val_parsed)
                catch err
                    return JSONRPC.JSONRPCError(-32600, string("Expression could not be evaluated: ", sprint(showerror, err)), nothing)
                end

                idx = Core.eval(Main, Meta.parse("($var_name)"))

                setindex!(var_ref.value, new_val, idx...)

                s = construct_return_msg_for_var(state::DebuggerState, "", new_val)

                return SetVariableResponseArguments(s.value, s.type, s.variablesReference, s.namedVariables, s.indexedVariables)
            catch err
                return JSONRPC.JSONRPCError(-32600, "Something went wrong while setting the variable: $err", nothing)
            end
        else
            if Base.isimmutable(var_ref.value)
                return JSONRPC.JSONRPCError(-32600, "Cannot change the fields of an immutable struct.", nothing)
            else
                try
                    new_val = try
                        Core.eval(Main, val_parsed)
                    catch err
                        return JSONRPC.JSONRPCError(-32600, string("Expression could not be evaluated: ", sprint(showerror, err)), nothing)
                    end

                    setfield!(var_ref.value, Symbol(var_name), new_val)

                    s = construct_return_msg_for_var(state::DebuggerState, "", new_val)

                    return SetVariableResponseArguments(s.value, s.type, s.variablesReference, s.namedVariables, s.indexedVariables)
                catch err
                    return JSONRPC.JSONRPCError(-32600, string("Something went wrong while setting the variable: ", sprint(showerror, err)), nothing)
                end
            end
        end
    elseif var_ref.kind == :scope_globals || var_ref.kind == :module
        mod = if var_ref.value isa JuliaInterpreter.Frame
            JuliaInterpreter.moduleof(var_ref.value)
        elseif var_ref.value isa Module
            var_ref.value
        else
            return JSONRPC.JSONRPCError(-32600, "No module attached to this global variable.", nothing)
        end

        if !(mod isa Module)
            return JSONRPC.JSONRPCError(-32600, "Can't determine the module this variable is defined in.", nothing)
        end

        new_val = try
            mod.eval(Meta.parse("$var_name = $var_value"))
        catch err
            return JSONRPC.JSONRPCError(-32600, string("Something went wrong while setting the variable: ", sprint(showerror, err)), nothing)
        end
        s = construct_return_msg_for_var(state::DebuggerState, "", new_val)

        return SetVariableResponseArguments(s.value, s.type, s.variablesReference, s.namedVariables, s.indexedVariables)
    else
        return JSONRPC.JSONRPCError(-32600, "Unknown variable ref type.", nothing)
    end
end

function restart_frame_request(conn, state::DebuggerState, params::RestartFrameArguments)
    frame_id = params.frameId

    curr_fr = JuliaInterpreter.leaf(state.frame)

        i = 1

    while frame_id > i
        curr_fr = curr_fr.caller
        i += 1
    end

    if curr_fr.caller === nothing
        # We are in the top level

        state.frame = get_next_top_level_frame(state)
    else
        curr_fr.pc = 1
        curr_fr.assignment_counter = 1
        curr_fr.callee = nothing

        state.frame = curr_fr
    end

    put!(state.next_cmd, (cmd = :continue,))

    return RestartFrameResponseResponseArguments()
end

function exception_info_request(conn, state::DebuggerState, params::ExceptionInfoArguments)
    exception_id = string(typeof(state.last_exception))
    exception_description = sprint(Base.showerror, state.last_exception)

    exception_stacktrace = try
        Base.invokelatest(sprint, Base.show_backtrace, state.frame)
    catch err
        "Error while printing the backtrace."
    end

    return ExceptionInfoResponseArguments(exception_id, exception_description, "userUnhandled", ExceptionDetails(missing, missing, missing, missing, exception_stacktrace, missing))
end

function evaluate_request(conn, state::DebuggerState, params::EvaluateArguments)
    @debug "evaluate_request"

    curr_fr = state.frame
    curr_i = 1

    while params.frameId > curr_i
        if curr_fr.caller !== nothing
            curr_fr = curr_fr.caller
            curr_i += 1
        else
            break
        end
    end

    try
        ret_val = JuliaInterpreter.eval_code(curr_fr, params.expression)

        return EvaluateResponseArguments(Base.invokelatest(repr, ret_val), missing, missing, 0, missing, missing, missing)
    catch err
        return EvaluateResponseArguments(string("Internal Error: ", sprint(showerror, err)), missing, missing, 0, missing, missing, missing)
    end
end

    function continue_request(conn, state::DebuggerState, params::ContinueArguments)
    @debug "continue_request"

    put!(state.next_cmd, (cmd = :continue,))

    return ContinueResponseArguments(true)
end

function next_request(conn, state::DebuggerState, params::NextArguments)
    @debug "next_request"

    put!(state.next_cmd, (cmd = :next,))

    return NextResponseArguments()
end

function setp_in_request(conn, state::DebuggerState, params::StepInArguments)
    @debug "stepin_request"

    put!(state.next_cmd, (cmd = :stepIn, targetId = params.targetId))

    return StepInResponseArguments()
end

function step_in_targets_request(conn, state::DebuggerState, params::StepInTargetsArguments)
    @debug "stepin_targets_request"

    targets = calls_on_line(state)

    return StepInTargetsResponseArguments([
        StepInTarget(pc, string(expr)) for (pc, expr) in targets
    ])
end

function setp_out_request(conn, state::DebuggerState, params::StepOutArguments)
    @debug "stepout_request"

    put!(state.next_cmd, (cmd = :stepOut,))

    return StepOutResponseArguments()
end

function disconnect_request(conn, state::DebuggerState, params::DisconnectArguments)
    @debug "disconnect_request"

    put!(state.next_cmd, (cmd = :stop,))

    return DisconnectResponseArguments()
end

function terminate_request(conn, state::DebuggerState, params::TerminateArguments)
    @debug "terminate_request"

    JSONRPC.send(conn, finished_notification_type, nothing)
    put!(state.next_cmd, (cmd = :stop,))

    return TerminateResponseArguments()
end

function threads_request(conn, state::DebuggerState, params::Nothing)
    return ThreadsResponseArguments([Thread(id = 1, name = "Main Thread")])
end

function breakpointlocations_request(conn, state::DebuggerState, params::BreakpointLocationsArguments)
    return BreakpointLocationsResponseArguments(BreakpointLocation[])
end


text:
module dynamicRisk

using Reexport
@reexport using DifferentialEquations
@reexport using ScenTreesMakie

import RiskMeasures

using LinearAlgebra, NeuralPDE, Flux
using Test

include("BSDE.jl")
include("tree.jl")
include("lattice.jl")

export dynamicRM, Var, CTE, AVaR, EVaR, EVaR2, Expectation, entropic,
        mSD, meanVariance, meanDeviation, meanSemiVariance, meanSemiDevi

end # module


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

"""
    nnpars(lpars, data, target)

Pass local anisotropies `lpars` from `data` to `target` object, via nearest
neighbors. Useful to pass parameters from domain to samples.
"""
nnpars(lpars, data, target) = slice(lpars, grid2hd_ids(target, data))

"""
    idwpars(localaniso, searcher, domain; power=2, metric=Euclidean())

Interpolate `LocalAnisotropy` data into a `domain`, using the
local neighbors returned from the `searcher`. The interpolation can be inverse
distance weighted by given `power` and `metric` or a simply averaged if
`power=0`. The ellipses/ellipsoids rotations are interpolated using
(weighted) average of quaternions as described by Markley et al (2007). The
interpolated magnitude is the weighted median of the neighbors magnitude.

## Example

```julia
searcher = KNearestSearch(data, 10)
idw3_into_grid = idwpars(localaniso, searcher, grid, power=3)
```

## Reference

Markley, F.L., et al. (2007). [Averaging quaternions](https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20070017872.pdf)
"""
idwpars(lpars, searcher::NeighborSearchMethod, domain; power=2.0, metric=Euclidean()) =
	interpolate(lpars, searcher, domain, power=power)

"""
    smoothpars(localaniso, searcher; power=0, metric=Euclidean())

Smooth `LocalAnisotropy`, using the local neighbors returned from the `searcher`.
The interpolation can be inverse distance weighted by given `power` and `metric`
or a simply averaged if `power=0`. The ellipses/ellipsoids rotations are
interpolated using (weighted) average of quaternions as described by Markley
et al. (2007). The interpolated magnitude is the weighted median of the
neighbors magnitude.

## Example

```julia
searcher = KNearestSearch(data, 10)
averaged_inplace = smoothpars(localaniso, searcher)
```

## Reference

Markley, F.L., et al. (2007). [Averaging quaternions](https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20070017872.pdf)
"""
smoothpars(lpars, searcher::NeighborSearchMethod; power=0.0, metric=Euclidean()) =
	interpolate(lpars, searcher, power=power)

function interpolate(lpars, searcher::NeighborSearchMethod, domain=nothing;
	power::Real=0, metric=Euclidean())
	D = searcher.domain
	N = embeddim(D)
	len = domain==nothing ? nelements(D) : nelements(domain)
	@assert nelements(D)==nvals(lpars) "searcher domain must match number of local anisotropies"

    quat = Array{Quaternion}(undef,len)
	mag  = lpars.magnitude
    m    = domain==nothing && power==0 ? mag : Array{Float64}(undef,N,len)

    Threads.@threads for i in 1:len
        ic  = domain==nothing ? centroid(D,i) : centroid(domain,i)
		icoords  = coordinates(ic)
		neighids = search(ic, searcher)

		if length(neighids) == 0
			throw(ErrorException("zero neighbors at some location; adjust searcher"))
			# or accept missing in LocalAnisotropy?
		elseif power==0.0
			quat[i] = quatavg(rotation(lpars,neighids))
			if domain!=nothing
				mi      = magnitude(lpars,neighids)
				m[:,i] .= mapreduce(x->quantile(view(mi,x,:),0.5), vcat, 1:N)
			end
		else
			xcoords = coords(D, neighids)
			prewgts = 1 ./ (eps() .+ Distances.colwise(metric, icoords, xcoords)) .^ power
			weights = prewgts ./ sum(prewgts)
			quat[i] = quatavg(rotation(lpars,neighids), weights)
			mi      = magnitude(lpars,neighids)
			wgts    = Weights(weights)
			m[:,i] .= mapreduce(x->quantile(view(mi,x,:),wgts,0.5), vcat, 1:N)
			# mean instead median for magnitude?
		end
    end

    LocalAnisotropy(quat, m)
end

quat2vector(q) = [q.q0; q.q1; q.q2; q.q3]
tensor(q) = q' .* q
wtensor(q) = q[1] .* (q[2]' .* q[2])

function quatavg(qarr, warr=[])
	length(qarr) == 1 && (return qarr)
	#@assert in(length(warr),[n,0])
	Q = mapreduce(quat2vector, hcat, qarr)
    n = size(Q,2)
    W = length(warr) > 0

    A = W ? mapreduce(wtensor, +, zip(warr, eachcol(Q))) : mapreduce(tensor, +, eachcol(Q))

    # scale
    Nw = W ? sum(warr) : n
    A ./= Nw

    # compute eigenvalues and -vectors
    T = eigen(Symmetric(SMatrix{4,4}(A)))
    V = view(T.vectors[:, sortperm(T.values,rev=true)],:,1)
    Quaternion(V...)
end


text:
module HilbertTestSuite
using BijectiveHilbert: HilbertAlgorithm, decode_hilbert_zero!, encode_hilbert_zero
using BijectiveHilbert: index_type, axis_type

function check_own_inverse(gg::HilbertAlgorithm, b::Int, n)
    success = true
    p = zeros(axis_type(gg), n)
    H = index_type(gg)
    for h in 0:(1<<(n*b) - 1)
        decode_hilbert_zero!(gg, p, H(h))
        h2 = encode_hilbert_zero(gg, p)
        if h2 != H(h)
            success = false
        end
    end
    success
end

function check_own_inverse(gg::HilbertAlgorithm, ms::Vector{Int}, n)
    success = true
    A = axis_type(gg)
    p = zeros(A, n)
    H = index_type(gg)
    total = prod(ms)
    for i in 0:(1<<total - 1)
        h = encode_hilbert_zero(gg, p)
        q = similar(p)
        decode_hilbert_zero!(gg, q, h)
        if p != q
            success = false
        end
        for inc in 1:n
            p[inc] += one(A)
            if p[inc] == one(A)<<ms[inc]
                p[inc] = zero(A)
            else
                break
            end
        end
        @assert p != q
    end
    success
end

function check_complete_set(gg::HilbertAlgorithm{H}, b, n) where {H}
dim_cnt = n
m = b
A = axis_type(gg)
success = true
indices = Base.IteratorsMD.CartesianIndices(tuple(collect(1<<m for i in 1:dim_cnt)...))
seen2 = Dict{H, Vector{A}}()
for idx in indices
    # -1 for zero-based.
    vidx = convert(Vector{A}, [Tuple(idx)...] .- 1)
    h = encode_hilbert_zero(gg, vidx)
    seen2[h] = vidx
    if h < zero(H)
        success = false
    end
    if h >= one(H)<<(dim_cnt * m)
        success = false
    end
end
if length(seen2) != 1<<(dim_cnt * m)
    success = false
end
for ihidx in 0:(1<<(dim_cnt*m) - 2)  # compare with next, so stop one early.
    hidx = H(ihidx)
    differ = seen2[hidx] .!= seen2[hidx + one(H)]
    if sum(differ) != 1
        @show ihidx, seen2[hidx], seen2[hidx + one(H)]
        success = false
    end
    if sum(differ) == 1
        a = seen2[hidx][differ][1]
        b = seen2[hidx + 1][differ][1]
        dx = (a > b) ? a - b : b - a
        if H(dx) != one(H)
            success = false
            break
        end
    end
end
success
end

end


text:
const _Constant = JuMP._Constant
const _VariableAffExpr{C} = GenericAffExpr{C, VariableRef}
const _DecisionAffExpr{C} = GenericAffExpr{C, DecisionRef}
const _VAE = _VariableAffExpr{Float64}
const _DAE = _DecisionAffExpr{Float64}

mutable struct DecisionAffExpr{C} <: JuMP.AbstractJuMPScalar
    variables::GenericAffExpr{C, VariableRef}
    decisions::GenericAffExpr{C, DecisionRef}
end
const DAE = DecisionAffExpr{Float64}

DAE() = zero(DAE{Float64})

is_decision_type(::Type{<:DecisionAffExpr}) = true

# Base overrides #
# ========================== #
function Base.iszero(aff::DecisionAffExpr)
    return iszero(aff.variables) && iszero(aff.decisions)
end
function Base.zero(::Type{DecisionAffExpr{C}}) where C
    return DecisionAffExpr{C}(
        zero(_VariableAffExpr{C}),
        zero(_DecisionAffExpr{C}))
end
function Base.one(::Type{DecisionAffExpr{C}}) where C
    return DecisionAffExpr{C}(one(_VariableAffExpr{C}),
                              zero(_DecisionAffExpr{C}))
end
function Base.zero(aff::DecisionAffExpr)
    return zero(typeof(aff))
end
function Base.one(aff::DecisionAffExpr)
    return one(typeof(aff))
end
function Base.copy(aff::DecisionAffExpr{C}) where C
    return DecisionAffExpr{C}(copy(aff.variables),
                              copy(aff.decisions))
end
function Base.broadcastable(aff::DecisionAffExpr)
    return Ref(aff)
end

function Base.convert(::Type{DecisionAffExpr{C}}, v::VariableRef) where C
    return DecisionAffExpr{C}(_VariableAffExpr{C}(zero(C), v => one(C)),
                              zero(_DecisionAffExpr{C}))
end
function Base.convert(::Type{DecisionAffExpr{C}}, dv::DecisionRef) where C
    return DecisionAffExpr{C}(zero(_VariableAffExpr{C}),
                              _DecisionAffExpr{C}(zero(C), dv => one(C)))
end
function Base.convert(::Type{DecisionAffExpr{C}}, c::Number) where C
    return DecisionAffExpr{C}(_VariableAffExpr{C}(convert(C, c)),
                              zero(_DecisionAffExpr{C}))
end
function Base.convert(::Type{DecisionAffExpr{C}}, aff::_VariableAffExpr{C}) where C
    return DecisionAffExpr{C}(aff,
                              zero(_DecisionAffExpr{C}))
end
function Base.convert(::Type{DecisionAffExpr{C}}, aff::_DecisionAffExpr{C}) where C
    return DecisionAffExpr{C}(zero(_VariableAffExpr{C}),
                              aff)
end

function Base.convert(::Type{T}, aff::DecisionAffExpr{T}) where T
    if !isempty(aff.variables.terms) && !isempty(aff.decisions.terms)
        throw(InexactError(:convert, T, aff))
    end
    return convert(T, aff.variables.constant)
end

function Base.isequal(aff::DecisionAffExpr{C}, other::DecisionAffExpr{C}) where {C}
    return isequal(aff.variables, other.variables) && isequal(aff.decisions, other.decisions)
end

function JuMP.isequal_canonical(aff::DecisionAffExpr{C}, other::DecisionAffExpr{C}) where {C}
    return JuMP.isequal_canonical(aff.variables, other.variables) && JuMP.isequal_canonical(aff.decisions, other.decisions)
end

function Base.hash(aff::DecisionAffExpr, h::UInt)
    return hash(aff.variables.constant,
                hash(aff.variables.terms, h),
                hash(aff.decisions.terms, h))
end

# JuMP overrides #
# ========================== #
function JuMP.drop_zeros!(aff::DecisionAffExpr)
    JuMP.drop_zeros!(aff.variables)
    JuMP.drop_zeros!(aff.decisions)
    return nothing
end

function JuMP._affine_coefficient(f::DecisionAffExpr, variable::VariableRef)
    return JuMP._affine_coefficient(f.variables, variable)
end
function JuMP._affine_coefficient(f::DecisionAffExpr, decision::DecisionRef)
    return JuMP._affine_coefficient(f.decisions, decision)
end
function JuMP._affine_coefficient(f::_VariableAffExpr{C}, decision::DecisionRef) where C
    return zero(C)
end

function JuMP.map_coefficients_inplace!(f::Function, aff::DecisionAffExpr)
    JuMP.map_coefficients_inplace!(f, aff.variables)
    for (coef, dvar) in linear_terms(aff.decisions)
        aff.decisions.terms[dvar] = f(coef)
    end
    return aff
end

function JuMP.map_coefficients(f::Function, aff::DecisionAffExpr)
    return JuMP.map_coefficients_inplace!(f, copy(aff))
end

function JuMP.value(aff::DecisionAffExpr, value::Function)
    return JuMP.value(aff.variables, value) + JuMP.value(aff.decisions, value)
end

function JuMP.constant(aff::DecisionAffExpr)
    return aff.variables.constant
end

function SparseArrays.dropzeros(aff::DecisionAffExpr{C}) where C
    variables = SparseArrays.dropzeros(aff.variables)
    decisions = SparseArrays.dropzeros(aff.decisions)
    return DecisionAffExpr(variables, decisions)
end

function JuMP._assert_isfinite(aff::_DecisionAffExpr)
    for (coef, dv) in linear_terms(aff)
        isfinite(coef) || error("Invalid coefficient $coef on decision $dv.")
    end
    return nothing
end

function JuMP._assert_isfinite(aff::DecisionAffExpr)
    JuMP._assert_isfinite(aff.variables)
    JuMP._assert_isfinite(aff.decisions)
    return nothing
end

function JuMP.check_belongs_to_model(aff::DecisionAffExpr, model::AbstractModel)
    JuMP.check_belongs_to_model(aff.variables, model)
    JuMP.check_belongs_to_model(aff.decisions, model)
    return nothing
end

function JuMP.function_string(mode, aff::DecisionAffExpr, show_constant=true)
    # Decisions
    ret = ""
    decision_terms = JuMP.function_string(mode, aff.decisions, false)
    if decision_terms != "0"
        ret = decision_terms
    end
    # Variables
    variable_terms = JuMP.function_string(mode, aff.variables, false)
    ret = _add_terms(ret, variable_terms)
    # Constant
    if !JuMP._is_zero_for_printing(aff.variables.constant) && show_constant
        ret = string(ret, JuMP._sign_string(aff.variables.constant),

                     JuMP._string_round(abs(aff.variables.constant)))
    end
    return ret
end

function _add_terms(ret::String, terms::String)
    if terms == ""
        return ret
    end
    if terms != "0"
        if ret == ""
            ret = terms
        else
            if terms[1] == '-'
                ret = ret * " - " * terms[2:end]
            else
                ret = ret * " + " * terms
            end
        end
    end
    return ret
end

# With one factor.
function JuMP.add_to_expression!(aff::DAE, other::Number)
    JuMP.add_to_expression!(aff.variables, other)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_var::VariableRef)
    JuMP.add_to_expression!(aff.variables, new_var)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_dvar::DecisionRef)
    JuMP.add_to_expression!(aff.decisions, new_dvar)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_vae::_VAE)
    JuMP.add_to_expression!(aff.variables, new_vae)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_dae::_DAE)
    JuMP.add_to_expression!(aff.decisions, new_dae)
    return aff
end
function JuMP.add_to_expression!(lhs_aff::DAE, rhs_aff::DAE)
    JuMP.add_to_expression!(lhs_aff.variables, rhs_aff.variables)
    JuMP.add_to_expression!(lhs_aff.decisions, rhs_aff.decisions)
    return lhs_aff
end

# With two factors.
function JuMP.add_to_expression!(aff::DAE, new_coef::_Constant, new_var::VariableRef)
    JuMP.add_to_expression!(aff.variables, new_coef, new_var)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_var::VariableRef, new_coef::_Constant)
    JuMP.add_to_expression!(aff.variables, new_coef, new_var)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_coef::_Constant, new_dvar::DecisionRef)
    JuMP.add_to_expression!(aff.decisions, new_coef, new_dvar)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_dvar::DecisionRef, new_coef::_Constant)
    JuMP.add_to_expression!(aff.decisions, new_coef, new_dvar)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_coef::_Constant, new_vae::_VAE)
    JuMP.add_to_expression!(aff.variables, new_coef, new_vae)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_vae::_VAE, new_coef::_Constant)
    JuMP.add_to_expression!(aff.variables, new_coef, new_vae)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_coef::_Constant, new_dae::_DAE)
    JuMP.add_to_expression!(aff.decisions, new_coef, new_dae)
    return aff
end
function JuMP.add_to_expression!(aff::DAE, new_dae::_DAE, new_coef)
    JuMP.add_to_expression!(aff.decisions, new_coef, new_dae)
    return aff
end
function JuMP.add_to_expression!(lhs_aff::DAE, new_coef::_Constant, rhs_aff::DAE)
    JuMP.add_to_expression!(lhs_aff, new_coef, rhs_aff.variables)
    JuMP.add_to_expression!(lhs_aff, new_coef, rhs_aff.decisions)
    return lhs_aff
end
function JuMP.add_to_expression!(lhs_aff::DAE, rhs_aff::DAE, new_coef::_Constant)
    JuMP.add_to_expression!(lhs_aff, rhs_aff.variables, new_coef)
    JuMP.add_to_expression!(lhs_aff, rhs_aff.decisions, new_coef)
    return lhs_aff
end


text:
using SpecialFunctions

@doc raw"""
Evaluates the Boys function ``Fₘ(x) = ∫₀¹ t²ᵐ \exp(-xt²) dt``.
`IND==0` gives around 14 digits, `IND==1` around 6 and `IND==3` around 3.
"""
function boys(m::Int, x::T, IND=0) where T <: Real
    x < 0 && throw(DomainError(x, "`x` must be nonnegative."))
    if x > 0
        # gamma_inc returns tuple of incomplete Γ function ratios,
        # we only care about the first returned result
        Px, _ = gamma_inc(m + 1 / T(2), x, IND)
        1 / 2x^(m + 1 / T(2)) * gamma(m + 1 / T(2)) * Px
    else
        1 / T(2m + 1)
    end
end

function doublefactorial(n::Integer)
    n < 0 && throw(DomainError(n, "`n` must be nonnegative."))
    start = iseven(n) ? 2 : 1
    f::typeof(n*n) = 1
    for i::typeof(n*n) = start:2:n
        f *= i
    end
    return f
end


text:
using CoordinateTransformations
using LinearAlgebra
using Optim

const XYPosition = SArray{Tuple{2},Float64,1,2}

"""
    translate(oldPts::AbstractVector{XYPosition}, newPts::AbstractVector{XYPosition})::AffineMap

Determines the optimal AffineMap to transform the X-Y coordinates in `oldPts` into the X-Y coordinates in `newPts`.
The coordinates in `oldPts` and `newPts` are assumed to represent the same features in the same order and thus
must contain the same number of X-Y coordinates.
"""
function translate(oldPts::AbstractVector{XYPosition}, newPts::AbstractVector{XYPosition})::AffineMap
    @assert length(oldPts)==length(newPts) "The number of old and new points must match."
    if length(oldPts)==1
        offset, scale, rotatio = SA[ newPts[1] .- oldPts[1] ], SA[1.0, 1.0], 0.0
    else # Compute a first estimate from points 1 and 2
        offset = 0.5*(newPts[2] .+ newPts[1]) .- 0.5*(oldPts[2] .+ oldPts[1])
        dold, dnew = oldPts[2] .- oldPts[1], newPts[2] .- newPts[1]
        scale = SA[ norm(dnew)/norm(dold), norm(dnew)/norm(dold) ]
        rotation = acos((dold⋅dnew)/(norm(dold)*norm(dnew)))
    end
    res = AffineMap(SA[ cos(rotation) -sin(rotation) ; sin(rotation) cos(rotation) ] * SA[ scale[1] 0.0; 0.0 scale[2]], offset)
    if length(oldPts)>2
        # Simplex optimize to discover the minimum offset, scale and rotation over all point pairs
        x0 = [offset..., scale..., rotation]
        function f(x)
            res = AffineMap(SA[ cos(x[5]) -sin(x[5]) ; sin(x[5]) cos(x[5]) ] * SA[ x[3] 0.0; 0.0 x[4] ], SA[x[1], x[2]])
            return sum(norm(res(oldPt) .- newPt) for (oldPt, newPt) in zip(oldPts, newPts))
        end
        xp = Optim.minimizer(optimize(f, x0, Optim.NelderMead()))
        res = AffineMap(SA[ cos(xp[5]) -sin(xp[5]) ; sin(xp[5]) cos(xp[5]) ] * SA[ xp[3] 0.0; 0.0 xp[4] ], SA[xp[1], xp[2]])
    end
    return res
end