@with_kw struct cylinder_inputs{T <: AbstractFloat}
    μsp::T = 10.0
    μa::T = 0.1
    n_ext::T = 1.0  #surrounding index of refraction
    n_med::T = 1.4 # layers index of refraction

    #source, detector
    lz::T = 5.0 # length of cylinder
    ρ::T = 1.0 #
    a::T = 5.0 # radius of cylinder
    z::T = 0.0
    pw::T = 0.1

    ω::T = 0.0 #
end

# look at get_afac for some reason gives type instabillity
function diffusionparams(μsp, n_med, n_ext)
    ## Diffusion parameters
    D = 1/3μsp
    ν = 29.9792345/n_med
    A = 1.0 #get_afac(n_ext/n_med) # need to calculate reflection between layers and surrounding medium
    zb = 2*A*D
    z0 = 1/(μsp)

    return D, ν, A, zb, z0
end

function _greencylin(α, D, z, z0, zb, lz)
    g = (exp(-α*abs(z - z0)) - exp(-α*(z + z0 + 2*zb)))
    g -= exp(α*(z0 + z - 2*lz - 2*zb))*(1 - exp(-2*α*(z0 + zb)))*(1 - exp(-2*α*(zb + z)))/(1 - exp(-2*α*(lz + 2*zb)))
    return g / (2*D*α)
end

function _greencylin_CW(sn, μa, D, z, z0, zb, lz)
    α = sqrt(μa/D + sn^2)
    return _greencylin(α, D, z, z0, zb, lz)
end

function _greencylin_FD(sn, μa, D, z, z0, zb, lz, ν, ω)
    α = sqrt(μa/D + sn^2 + im*ω/(D*ν))
    return _greencylin(α, D, z, z0, zb, lz)
end

function _greencylin_Laplace(sn, μa, D, z, z0, zb, lz, ν, s)
    α = sqrt(μa/D + sn^2 + s/(D*ν))
    return _greencylin(α, D, z, z0, zb, lz)
end

function fluence_DA_cylinder_CW(data::cylinder_inputs, besselroots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ϕ = zero(eltype(data.ρ))
    for bessel in besselroots
        ϕ += _greencylin_CW(bessel/(data.a + zb), data.μa, D, data.z, z0, zb, data.lz)*besselj0(bessel/(data.a + zb)*data.ρ)/(besselj1(bessel))^2
    end

    return ϕ / (π*(data.a + zb)^2)
end

function fluence_DA_cylinder_FD(data::cylinder_inputs, besselroots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ϕ = zero(Complex{eltype(data.ρ)})

    for n in eachindex(besselroots)
        ϕ += _greencylin_FD(besselroots[n]/(data.a + zb), data.μa, D, data.z, z0, zb, data.lz, ν, data.ω)*besselj0(besselroots[n]/(data.a + zb)*data.ρ)/(besselj1(besselroots[n]))^2
    end

    return ϕ / (π*(data.a + zb)^2)
end

### Compute Time Domain Signal

# calulcate fluence in laplace space: s -> im*ω
function _fluence_DA_cylinder_Laplace(s, data::cylinder_inputs, besselroots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ϕ = zero(eltype(s))

    for bessel in besselroots
        ϕ += _greencylin_Laplace(bessel/(data.a + zb), data.μa, D, data.z, z0, zb, data.lz, ν, s)*besselj0(bessel/(data.a + zb)*data.ρ)/(besselj1(bessel))^2
    end

    return ϕ/(π*(data.a + zb)^2)
end

# use adaptive hyperbola contour for each time point Laplace Transform
# much more accurate but slower. N = 8 is usually sufficient
# need larger N for very short times (< 0.1 ns) and very long times (> 3ns)
function fluence_DA_cylinder_TD(t, N, data::cylinder_inputs, besselroots)
    Rt = zeros(Float64, length(t))
    Rt = LT_hyperbola(s -> _fluence_DA_cylinder_Laplace(s, data, besselroots), N, t)

    return Rt
end

# use fixed contour for every time point
# signifanctly faster (10-100x) than adaptive contour but less accurate
# utilizing this is usually ok but further testing is needed
# N = 28 is usuall pretty accurate
# Need about 600 Bessel Zeros
# the 59.9 is dividing it by some constant to match the fluence in semi-inf. Unsure why this is needed (not needed in the steady-state: match exactly)
function fluence_DA_cylinder_TD(t, N, data::cylinder_inputs, besselroots)
    Rt = zeros(Float64, length(t))
    Rt = LT_hyper_fixed(s -> _fluence_DA_cylinder_Laplace(s, data, besselroots), N, t)

    return Rt
end

### for a finite flat beam incident onto center of cylinder top
# have data structure contain pw (radius of beam)

function fluence_DA_cylinder_CW_beam(data::cylinder_inputs1, besselroots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ϕ = zero(eltype(data.ρ))
    sn = zero(eltype(data.ρ))

    for n in eachindex(besselroots)
        sn = besselroots[n]/(data.a + zb)
        ϕ += _greencylin_CW(sn, data.μa, D, data.z, z0, zb, data.lz)*besselj0(sn*data.ρ)*besselj1(sn*data.pw)/(sn*(besselj1(sn*(data.a + zb))^2))
    end

    return 2*ϕ/(data.pw*π*(data.a + zb)^2)
end

function _fluence_DA_cylinder_Laplace_beam(s, data::cylinder_inputs1, besselroots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)
    ϕ = zero(eltype(data.ρ))

    sn = zero(eltype(data.ρ))
    for n in eachindex(besselroots)
        sn = besselroots[n]/(data.a + zb)
        ϕ += _greencylin_Laplace(sn, data.μa, D, data.z, z0, zb, data.lz, ν, s)*besselj0(sn*data.ρ)*besselj1(sn*data.pw)/(sn*(besselj1(sn*(data.a + zb))^2))
    end

    return 2*ϕ/(data.pw*π*(data.a + zb)^2)
end

function fluence_DA_cylinder_TD_beam(t, N, data::cylinder_inputs1, besselroots)
    Rt = zeros(Float64, length(t))
    Rt = LT_hyper_fixed(s -> _fluence_DA_cylinder_Laplace_beam(s, data, besselroots), N, t)

    return Rt
end

### arbitrary position

@with_kw struct cylinder_inputs_arb3
    μsp::Float64 = 10.0
    μa::Float64 = 0.1
    n_ext::Float64 = 1.0  #surrounding index of refraction
    n_med::Float64 = 1.4 # layers index of refraction

    #source, detector
    lz::Float64 = 5.0 # length of cylinder
    ρ::Float64 = 5.0 #
    ρ0::Float64 = 5.0
    a::Float64 = 5.0 # radius of cylinder
    z::Float64 = 2.0
    z0::Float64 = 3.0

    θ = 0.0

    ω::Float64 = 0.0 #
end

function fluence_DA_cylinder_CW(data::cylinder_inputs_arb2, besselj_roots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ϕ = zero(eltype(data.ρ))
    ϕ1 = zero(eltype(data.ρ))

    for m = 0:100
        ϕ1 = 0.0
        for bessel in besselj_roots[:, abs(m) + 1] # 1 column is 0 order, bessel roots symmetric J(-nu, x) = (-1)^2 J(nu, x)
            ϕ1 += _greencylin_CW(bessel/(data.a + zb), data.μa, D, data.z, z0, zb, data.lz)*besselj(m, bessel/(data.a + zb)*data.ρ)*besselj(m, bessel/(data.a + zb)*data.ρ0)/(besselj(m + 1, bessel))^2

        end
        ϕ1 *= ((2 - (m == 0 ? 1 : 0))*cos(m*data.θ))

        ϕ += ϕ1
    end

    return ϕ / (π*(data.a + zb)^2)
end

function fluence_DA_cylinder_CW(data::cylinder_inputs_arb3, besselj_roots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ρ0 = data.ρ0 -  1/data.μsp[1]
    z0 = data.z0

    ϕ = 0.0
    ϕ1 = 0.0

    for m = 0:1200
        ϕ1 = 0.0
        for bessel in besselj_roots[:, abs(m) + 1] # 1 column is 0 order, bessel roots symmetric J(-nu, x) = (-1)^2 J(nu, x)
            ϕ1 += _greencylin_CW(bessel/(data.a + zb), data.μa, D, data.z, z0, zb, data.lz)*besselj(m, bessel/(data.a + zb)*data.ρ)*besselj(m, bessel/(data.a + zb)*ρ0)/(besselj(m + 1, bessel))^2

        end
        #println(ϕ1)
        ϕ1 *= ((2 - (m == 0 ? 1 : 0))*cos(m*data.θ))

        ϕ += ϕ1
        #println(ϕ)
    end

    return ϕ / (π*(data.a + zb)^2)
end

## work in process: need to test best convergence
## can't break for order m when theta is non-zero. You get oscillating term
##appears to be a bug when z = z0. Slow convergence or something off? Seems when theta = 0 everything is ok...
function fluence_DA_cylinder_CW(data::cylinder_inputs_arb3, besselj_roots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ρ0 = data.ρ0 -  1/data.μsp[1]
    z0 = data.z0

    ϕ = 0.0
    ϕ1 = 0.0

    root_old = 0.0
    order_old = 0.0
    count = 0

    TOL = 1e-12

    for m = -1600:1600#0:8000
        ϕ1 = 0.0
        for bessel in besselj_roots[:, abs(m) + 1] # 1 column is 0 order, bessel roots symmetric J(-nu, x) = (-1)^2 J(nu, x)
            root_old = ϕ1
            ϕ1 += _greencylin_CW(bessel/(data.a + zb), data.μa, D, data.z, z0, zb, data.lz)*besselj(m, bessel/(data.a + zb)*data.ρ)*besselj(m, bessel/(data.a + zb)*ρ0)/(besselj(m + 1, bessel))^2

            if (abs(ϕ1 - root_old))/ϕ1 < TOL
                #println(bessel)
                break
            end
        end

        ϕ1 *= #=((2 - (m == 0 ? 1 : 0))*=#cos(m*data.θ)
        order_old = ϕ1

        ϕ += ϕ1
    end

    return ϕ / (π*(data.a + zb)^2)
end

function _fluence_DA_cylinder_Laplace(s, data::cylinder_inputs_arb3, besselj_roots)

    D, ν, A, zb, z0 = diffusionparams(data.μsp, data.n_med, data.n_ext)

    ρ0 = data.ρ0 -  1/data.μsp[1]
    z0 = data.z0

    ϕ = 0.0
    ϕ1 = 0.0

    for m = 0:600
        ϕ1 = 0.0
        for bessel in besselj_roots[:, abs(m) + 1] # 1 column is 0 order, bessel roots symmetric J(-nu, x) = (-1)^2 J(nu, x)
            ϕ1 += _greencylin_Laplace(bessel/(data.a + zb), data.μa, D, data.z, z0, zb, data.lz, ν, s)*besselj(m, bessel/(data.a + zb)*data.ρ)*besselj(m, bessel/(data.a + zb)*ρ0)/(besselj(m + 1, bessel))^2

        end
        ϕ1 *= ((2 - (m == 0 ? 1 : 0))*cos(m*data.θ))

        ϕ += ϕ1
    end

    return ϕ / (π*(data.a + zb)^2)
end
function fluence_DA_cylinder_TD(t, N, data::cylinder_inputs_arb3, besselroots)
    Rt = zeros(Float64, length(t))
    Rt = LT_hyper_fixed(s -> _fluence_DA_cylinder_Laplace(s, data, besselroots), N, t)

    return Rt
end
#=
Calculate Bessel Roots

using GSL

s = 1:1200 # number of roots to calculate
m = 0:1300 # order of besselj
besselj_roots = [sf_bessel_zero_Jnu_e(m, s).val for s in s, m in m]

=#


#######
# det #
#######

function LinearAlgebra.det(x::TrackedArray{V,D}) where {V,D}
    tp = tape(x)
    x_value = value(x)
    det_x_value = det(x_value)
    out = track(det_x_value, D, tp)
    record!(tp, SpecialInstruction, det, x, out)
    return out
end

@noinline function special_reverse_exec!(instruction::SpecialInstruction{typeof(det)})
    output = instruction.output
    if output != 0
        input = instruction.input
        input_deriv = deriv(input)
        inv_input_value = inv(value(input))
        k = deriv(output) * value(output)
        for i in 1:size(input_deriv, 1), j in 1:size(input_deriv, 2)
            input_deriv[i, j] += k * inv_input_value[j, i]
        end
    end
    unseed!(output)
    return nothing
end

@noinline function special_forward_exec!(instruction::SpecialInstruction{typeof(det)})
    output, input = instruction.output, instruction.input
    value!(output, det(value(input)))
    return nothing
end

#######
# inv #
#######

function LinearAlgebra.inv(x::TrackedArray{V,D}) where {V,D}
    tp = tape(x)
    out_value = inv(value(x))
    out = track(out_value, D, tp)
    cache = (similar(out_value, D), similar(out_value, D))
    record!(tp, SpecialInstruction, inv, x, out, cache)
    return out
end

@noinline function special_reverse_exec!(instruction::SpecialInstruction{typeof(inv)})
    output = instruction.output
    output_value, output_deriv = value(output), deriv(output)
    output_tmp1, output_tmp2 = instruction.cache
    mul!(output_tmp1, output_deriv, adjoint(output_value))
    mul!(output_tmp2, adjoint(output_value), output_tmp1)
    decrement_deriv!(instruction.input, output_tmp2)
    unseed!(output)
    return nothing
end

@noinline function special_forward_exec!(instruction::SpecialInstruction{typeof(inv)})
    input = instruction.input
    value!(instruction.output, inv(value(input)))
    return nothing
end


export Vortex

"""
$(TYPEDEF)

Defines a point vortex with x-position `x`, y-position `y`, and strength `Γ`.

# Fields

$(TYPEDFIELDS)
"""
mutable struct Vortex
    """x: x-coordinate of the vortex position."""
    x::Float64
    """y: y-coordinate of the vortex position."""
    y::Float64
    """Γ: Strength of the vortex. Positive if counter-clockwise."""
    Γ::Float64
end

"""
$(TYPEDSIGNATURES)

Returns the strengths of all point vortices in the `StructArray` `vl` as `ScalarData`.
"""
function getstrengths(vl::StructArray{Vortex})
    return ScalarData(vl.Γ)
end

"""
$(TYPEDSIGNATURES)

Returns the positions of all point vortices in the `StructArray` `vl` as `VectorData`.
"""
function getpositions(vl::StructArray{Vortex})
    return VectorData(vl.x,vl.y)
end

"""
$(TYPEDSIGNATURES)

Sets the `x` and `y` fields of the point vortices in the `StructArray` `vl` to the entries of `xnew` and `ynew`, respectively.
"""
function setpositions!(vl::StructArray{Vortex}, xnew, ynew)
    vl.x .= xnew
    vl.y .= ynew
end

"""
$(TYPEDSIGNATURES)

Sets the `Γ` field of the point vortices in the `StructArray` `vl` to the entries of `Γnew`.
"""
function setstrengths!(vl::StructArray{Vortex}, Γnew, idx=nothing)
    if isnothing(idx)
        vl.Γ .= Γnew
    else
        setindex!(vl.Γ, Γnew, idx)
    end
end


@doc raw"""
    cyclic_proximal_point(M, F, proxes, x)

perform a cyclic proximal point algorithm.

# Input
* `M` – a manifold $\mathcal M$
* `F` – a cost function $F\colon\mathcal M\to\mathbb R$ to minimize
* `proxes` – an Array of proximal maps (`Function`s) `(λ,x) -> y` for the summands of $F$
* `x` – an initial value $x ∈ \mathcal M$

# Optional
the default values are given in brackets
* `evaluationOrder` – ( [`LinearEvalOrder`](@ref) ) – whether
  to use a randomly permuted sequence ([`FixedRandomEvalOrder`](@ref)), a per
  cycle permuted sequence ([`RandomEvalOrder`](@ref)) or the default linear one.
* `λ` – ( `iter -> 1/iter` ) a function returning the (square summable but not
  summable) sequence of λi
* `stopping_criterion` – ([`StopWhenAny`](@ref)`(`[`StopAfterIteration`](@ref)`(5000),`[`StopWhenChangeLess`](@ref)`(10.0^-8))`) a [`StoppingCriterion`](@ref).
* `return_options` – (`false`) – if actiavated, the extended result, i.e. the
  complete [`Options`](@ref) are returned. This can be used to access recorded values.
  If set to false (default) just the optimal value `xOpt` if returned
and the ones that are passed to [`decorate_options`](@ref) for decorators.

# Output
* `xOpt` – the resulting (approximately critical) point of gradientDescent
OR
* `options` - the options returned by the solver (see `return_options`)
"""
function cyclic_proximal_point(M::MT,
  F::Function, proxes::Array{Function,N} where N, x0;
  evaluationOrder::EvalOrder = LinearEvalOrder(),
  stopping_criterion::StoppingCriterion = StopWhenAny( StopAfterIteration(5000), StopWhenChangeLess(10.0^-12)),
  λ = i -> 1/i,
  return_options=false,
  kwargs... #decorator options
  ) where {MT <: Manifold}
    p = ProximalProblem(M,F,proxes)
    o = CyclicProximalPointOptions(x0,stopping_criterion,λ,evaluationOrder)

    o = decorate_options(o; kwargs...)
    resultO = solve(p,o)
    if return_options
        return resultO
    else
        return get_solver_result(resultO)
    end
end
function initialize_solver!(p::ProximalProblem, o::CyclicProximalPointOptions)
    c = length(p.proxes)
    o.order = update_cpp_order(c,0,[1:c...],o.orderType)
end
function step_solver!(p::ProximalProblem, o::CyclicProximalPointOptions, iter)
    c = length(p.proxes)
    λi = o.λ(iter)
    for k=o.order
        o.x = getProximalMap(p,λi,o.x,k)
    end
    o.order = update_cpp_order(c,iter,o.order,o.orderType)
end
get_solver_result(o::CyclicProximalPointOptions) = o.x
update_cpp_order(n,i,o,::LinearEvalOrder) = o
update_cpp_order(n,i,o,::RandomEvalOrder) = collect(1:n)[randperm(length(o))]
update_cpp_order(n,i,o,::FixedRandomEvalOrder) = (i==0) ? collect(1:n)[randperm(length(o))] : o


# graphtheoryufrj.jl
# Authors : Heitor Guimaraes and Luiz Ciafrino
# @brief: Package control file with include of all files and functions.

module graphtheoryufrj
using DataStructures, Compat

export
	# Graph data structure
	simpleGraph,
	graph_properties,

	# Algorithms
	bfs,
	fast_bfs,
	dfs,
	connected_components,
	show_cycle,
	has_cycle,
	dia_bfs,
	diameter,
	dijkstra,
	floydw,
	prim_mst,
	mean_distance_dijkstra,
	mean_distance_fw,

	# Graph_Coloring
	direct_coloring,
	gurobi_coloring,
	edges,
	load,
	run,
	colors,
	coloring_correct,
	color_report,

	#DSATUR
	run_dsatur_heuristc,
	run_optdsatur_heuristc,
	dsatur,
	optdsatur,
	iteratedGreedy,
	localSearch,
	colorGraph,

	# Plot data
	plot_graph,
	plot_bfs,
	plot_dfs

# Load source files
include("graphstructure.jl")
include("algorithms.jl")
include("graph_coloring.jl")
include("dsatur.jl")
include("plot.jl")

end


using  LinearAlgebra
const  IdentityMatrix = UniformScaling{Bool}

function clip(s::Real, min_thresh::Real, max_thresh::Real, min_new::Real = min_thresh, max_new::Real = max_thresh)
	s = ifelse(s < min_thresh, min_new, ifelse(s > max_thresh, max_new, s))
end

function scaled_norm(E::IdentityMatrix, v::Array, p::T = T(2)) where {T <: AbstractFloat}
	E.λ ? norm(v, p) : zero(eltype(v))
end

function scaled_norm(E::Diagonal{T}, v::Array{T}, p::T = T(2)) where {T <: AbstractFloat}
	if p == 2
		return scaled_norm2(E, v)
	elseif p == Inf
		return scaled_norm_Inf(E, v)
	elseif p == 1
		return scaled_norm1(E, v)
	else
		throw(ArgumentError("bad norm specified"))
	end
end

function scaled_norm2(E::Diagonal, v::Array)
	sum_sq  = zero(eltype(v))
	for i = 1:length(v)
		sum_sq += (E.diag[i] * v[i])^2
	end
	return sqrt(sum_sq)
end

function scaled_norm_Inf(E::Diagonal, v::Array)
	norm  = zero(eltype(v))
	for i = 1:length(v)
		norm = max(norm, abs(E.diag[i] * v[i]))
	end
	return norm
end

function scaled_norm1(E::Diagonal, v::Array)
	norm  = zero(eltype(v))
	for i = 1:length(v)
		norm += abs(E.diag[i] * v[i])
	end
	return norm
end

function col_norms!(v::Array{Tf, 1},
	A::Matrix{Tf};
	reset::Bool = true) where {Tf <: AbstractFloat}

	if reset
		fill!(v,0.)
	end

	for i = 1:size(A, 2)
		v[i] = max(v[i], norm(view(A, :, i), Inf))
	end
	return v
end

function col_norms!(v::Array{Tf, 1},
	A::SparseMatrixCSC{Tf,Ti}; reset::Bool = true) where{Tf <: AbstractFloat, Ti <: Integer}

	if reset
		fill!(v,0.)
	end

	@inbounds for i = eachindex(v)
		for j = A.colptr[i]:(A.colptr[i + 1] - 1)
			tmp = abs(A.nzval[j])
			v[i] = v[i] > tmp ? v[i] : tmp;
		end
	end
	return v
end

function row_norms!(v::Array{Tf, 1},
	A::Matrix{Tf};
	reset::Bool = true) where{Tf <: AbstractFloat}

	if reset
		fill!(v,0.)
	end

	for i = 1:size(A, 1)
		v[i] = max(v[i], norm(view(A, i, :), Inf))
	end
	return v
end

function row_norms!(v::Array{Tf, 1},
	A::SparseMatrixCSC{Tf, Ti};
	reset::Bool = true) where{Tf <: AbstractFloat, Ti <: Integer}

	if reset
		fill!(v,0.)
	end

	@inbounds for i = 1:(A.colptr[end] - 1)
		idx = A.rowval[i]
		tmp = abs(A.nzval[i])
		v[idx] = v[idx] > tmp ? v[idx] : tmp
	end
	return v
end

function scalarmul!(A::SparseMatrixCSC, c::Real)
	A.nzval .*= c
end

function scalarmul!(A::AbstractMatrix, c::Real)
	A .*= c
end

function lmul!(L::Diagonal{T}, M::SparseMatrixCSC{T}) where {T <: AbstractFloat}

	#NB : Same as:  @views M.nzval .*= D.diag[M.rowval]
	#but this way allocates no memory at all and
	#is marginally faster
	m, n = size(M)
	(m == length(L.diag)) || throw(DimensionMismatch())

	@inbounds for i = 1:(M.colptr[end] - 1)
	 		M.nzval[i] *= L.diag[M.rowval[i]]
	end
	return M
end

lmul!(L::IdentityMatrix, M::AbstractMatrix) = L.λ ? M : M .= zero(eltype(M))

function lmul!(L::Diagonal{T}, x::AbstractVector{T}) where {T <: AbstractFloat}
	(length(L.diag) == length(x)) || throw(DimensionMismatch())
	@. x = x * L.diag
	return nothing
end

lmul!(L::IdentityMatrix, x::AbstractVector{T}) where {T <: AbstractFloat} = L.λ ? x : x .= zero(eltype(x))

function rmul!(M::SparseMatrixCSC{T}, R::Diagonal{T}) where {T <: AbstractFloat}

	m, n = size(M)
	(n == length(R.diag)) || throw(DimensionMismatch())

	@inbounds for i = 1:n, j = M.colptr[i]:(M.colptr[i + 1] - 1)
		 	M.nzval[j] *= R.diag[i]
	end
	return M
end

rmul!(M::AbstractMatrix, R::IdentityMatrix) = R.λ ? R : R .= zero(eltype(R))

function lrmul!(L::Diagonal{T}, M::SparseMatrixCSC{T}, R::Diagonal{T}) where {T <: AbstractFloat}

	m, n = size(M)
	Mnzval  = M.nzval
	Mrowval = M.rowval
	Mcolptr = M.colptr
	Rd      = R.diag
	Ld      = L.diag
	(m == length(Ld) && n == length(Rd)) || throw(DimensionMismatch())

	@inbounds for i = 1:n
		for j = Mcolptr[i]:(Mcolptr[i + 1] - 1)
	 		Mnzval[j] *= Ld[Mrowval[j]] * Rd[i]
		end
	end
	return M
end

lrmul!(L::IdentityMatrix,
	M::AbstractMatrix,
	R::IdentityMatrix) = (L.λ && R.λ) ? M : M .= zero(eltype(M))

lrmul!(L::Diagonal,
	M::SparseMatrixCSC,
	R::IdentityMatrix) = R.λ ? lmul!(L, M) : M .= zero(eltype(M))

lrmul!(L::Diagonal,
	M::AbstractMatrix,
	R::Diagonal) = LinearAlgebra.lmul!(L, LinearAlgebra.rmul!(M, R))

lrmul!(L::Diagonal,
	M::AbstractMatrix,
	R::IdentityMatrix) = R.λ ? LinearAlgebra.lmul!(L, M) : M .= zero(eltype(M))

lrmul!(L::IdentityMatrix,
	M::AbstractMatrix,
	R::Diagonal) = L.λ ? LinearAlgebra.rmul!(M, R) : M .= zero(eltype(M))

"""
    symmetrize_upper!(A)

Symmetrizes the matrix A by calculating A = 0.5 * (A + A') but only performs the operation on the upper triangular part.
"""
function symmetrize_upper!(A::AbstractMatrix{T}) where {T <: AbstractFloat}
	n = size(A, 1)
	@assert(size(A, 1) == size(A, 2), "Matrix is not square.")
	@inbounds for j in 1:n, i in 1:j
 		A[i, j] = (A[i, j] + A[j, i]) / T(2)
	end
	nothing
end

"""
    symmetrize_full!(A)

Symmetrizes the matrix A by calculating A = 0.5 * (A + A') and storing the result in-place.
"""
function symmetrize_full!(A::AbstractMatrix{T}) where {T <: AbstractFloat}
	n = size(A, 1)
	@assert(size(A, 1) == size(A, 2), "Matrix is not square.")
	@inbounds for j in 1:n, i in 1:j
 		A[i, j] = (A[i, j] + A[j, i]) / T(2)
 		A[j, i] = A[i, j]
	end
	nothing
end

# this function assumes real symmetric X and only considers the upper triangular part
function is_pos_def!(X::AbstractMatrix{T}, tol::T=zero(T)) where T
	# See https://math.stackexchange.com/a/13311
	@inbounds for i = 1:size(X, 1)
		X[i, i] += tol
	end
	F = cholesky!(Symmetric(X), check = false)
	return issuccess(F)
end

function is_neg_def!(X::AbstractMatrix{T}, tol::T=zero(T)) where T
	@. X *= -one(T)
	return is_pos_def!(X, tol)
end

is_pos_def(X::AbstractMatrix{T}, tol::T=zero(T)) where T = is_pos_def!(copy(X), tol)
is_neg_def(X::AbstractMatrix{T}, tol::T=zero(T)) where T = is_pos_def!(-X, tol)

"Round x to the closest multiple of N."
function round_multiple(x::T, N::T) where {T <: Integer}
	return floor(T, x +  0.5 * N - rem(x + 0.5 * N, N))
end


# ---
# title: 1266. Minimum Time Visiting All Points
# id: problem1266
# author: Tian Jun
# date: 2020-10-31
# difficulty: Easy
# categories: Array, Geometry
# link: <https://leetcode.com/problems/minimum-time-visiting-all-points/description/>
# hidden: true
# ---
#
# On a plane there are `n` points with integer coordinates `points[i] = [xi,
# yi]`. Your task is to find the minimum time in seconds to visit all points.
#
# You can move according to the next rules:
#
#   * In one second always you can either move vertically, horizontally by one unit or diagonally (it means to move one unit vertically and one unit horizontally in one second).
#   * You have to visit the points in the same order as they appear in the array.
#
#
#
# **Example 1:**
#
# ![](https://assets.leetcode.com/uploads/2019/11/14/1626_example_1.PNG)
#
#
#
#     Input: points = [[1,1],[3,4],[-1,0]]
#     Output: 7
#     Explanation: One optimal path is **[1,1]** -> [2,2] -> [3,3] -> **[3,4]** -> [2,3] -> [1,2] -> [0,1] -> **[-1,0]**
#     Time from [1,1] to [3,4] = 3 seconds
#     Time from [3,4] to [-1,0] = 4 seconds
#     Total time = 7 seconds
#
# **Example 2:**
#
#
#
#     Input: points = [[3,2],[-2,2]]
#     Output: 5
#
#
#
#
# **Constraints:**
#
#   * `points.length == n`
#   * `1 <= n <= 100`
#   * `points[i].length == 2`
#   * `-1000 <= points[i][0], points[i][1] <= 1000`
#
#
## @lc code=start
using LeetCode

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


using StatsBase
using Base.Test
import Base: maxabs
import StatsBase: norepeat, randi

srand(1234)

#### randi

n = 10^5

x = [randi(10) for i = 1:n]
@test isa(x, Vector{Int})
@test extrema(x) == (1, 10)
@test_approx_eq_eps proportions(x, 1:10) fill(0.1, 10) 5.0e-3

x = [randi(3, 12) for i = 1:n]
@test isa(x, Vector{Int})
@test extrema(x) == (3, 12)
@test_approx_eq_eps proportions(x, 3:12) fill(0.1, 10) 5.0e-3

#### sample with replacement

function check_sample_wrep(a::AbstractArray, vrgn, ptol::Real; ordered::Bool=false)
    vmin, vmax = vrgn
    (amin, amax) = extrema(a)
    @test vmin <= amin <= amax <= vmax
    n = vmax - vmin + 1
    p0 = fill(1/n, n)
    if ordered
        @test issorted(a)
        if ptol > 0
            @test_approx_eq_eps proportions(a, vmin:vmax) p0 ptol
        end
    else
        @test !issorted(a)
        ncols = size(a,2)
        if ncols == 1
            @test_approx_eq_eps proportions(a, vmin:vmax) p0 ptol
        else
            for j = 1:ncols
                aj = view(a, :, j)
                @test_approx_eq_eps proportions(aj, vmin:vmax) p0 ptol
            end
        end
    end
end

import StatsBase: direct_sample!

a = direct_sample!(1:10, zeros(Int, n, 3))
check_sample_wrep(a, (1, 10), 5.0e-3; ordered=false)

a = direct_sample!(3:12, zeros(Int, n, 3))
check_sample_wrep(a, (3, 12), 5.0e-3; ordered=false)

a = direct_sample!([11:20;], zeros(Int, n, 3))
check_sample_wrep(a, (11, 20), 5.0e-3; ordered=false)

a = sample(3:12, n)
check_sample_wrep(a, (3, 12), 5.0e-3; ordered=false)

a = sample(3:12, n; ordered=true)
check_sample_wrep(a, (3, 12), 5.0e-3; ordered=true)

a = sample(3:12, 10; ordered=true)
check_sample_wrep(a, (3, 12), 0; ordered=true)

#### sampling pairs

srand(1);

@test samplepair(2)  === (1, 2)
@test samplepair(10) === (7, 3)

@test samplepair([3, 4, 2, 6, 8]) === (4, 3)
@test samplepair([1, 2])          === (1, 2)

#### sample without replacement

function check_sample_norep(a::AbstractArray, vrgn, ptol::Real; ordered::Bool=false)
    # each column of a for one run

    vmin, vmax = vrgn
    (amin, amax) = extrema(a)
    @test vmin <= amin <= amax <= vmax
    n = vmax - vmin + 1

    for j = 1:size(a,2)
        aj = view(a,:,j)
        @assert norepeat(aj)
        if ordered
            @assert issorted(aj)
        end
    end

    if ptol > 0
        p0 = fill(1/n, n)
        if ordered
            @test_approx_eq_eps proportions(a, vmin:vmax) p0 ptol
        else
            b = transpose(a)
            for j = 1:size(b,2)
                bj = view(b,:,j)
                @test_approx_eq_eps proportions(bj, vmin:vmax) p0 ptol
            end
        end
    end
end

import StatsBase: knuths_sample!, fisher_yates_sample!, self_avoid_sample!
import StatsBase: seqsample_a!, seqsample_c!

a = zeros(Int, 5, n)
for j = 1:size(a,2)
    knuths_sample!(3:12, view(a,:,j))
end
check_sample_norep(a, (3, 12), 5.0e-3; ordered=false)

a = zeros(Int, 5, n)
for j = 1:size(a,2)
    fisher_yates_sample!(3:12, view(a,:,j))
end
check_sample_norep(a, (3, 12), 5.0e-3; ordered=false)

a = zeros(Int, 5, n)
for j = 1:size(a,2)
    self_avoid_sample!(3:12, view(a,:,j))
end
check_sample_norep(a, (3, 12), 5.0e-3; ordered=false)

a = zeros(Int, 5, n)
for j = 1:size(a,2)
    seqsample_a!(3:12, view(a,:,j))
end
check_sample_norep(a, (3, 12), 5.0e-3; ordered=true)

a = zeros(Int, 5, n)
for j = 1:size(a,2)
    seqsample_c!(3:12, view(a,:,j))
end
check_sample_norep(a, (3, 12), 5.0e-3; ordered=true)

a = sample(3:12, 5; replace=false)
check_sample_norep(a, (3, 12), 0; ordered=false)

a = sample(3:12, 5; replace=false, ordered=true)
check_sample_norep(a, (3, 12), 0; ordered=true)


# ---
# title: 373. Find K Pairs with Smallest Sums
# id: problem373
# author: Indigo
# date: 2021-06-14
# difficulty: Medium
# categories: Heap
# link: <https://leetcode.com/problems/find-k-pairs-with-smallest-sums/description/>
# hidden: true
# ---
#
# You are given two integer arrays **nums1** and **nums2** sorted in ascending
# order and an integer **k**.
#
# Define a pair **(u,v)** which consists of one element from the first array and
# one element from the second array.
#
# Find the k pairs **(u 1,v1),(u2,v2) ...(uk,vk)** with the smallest sums.
#
# **Example 1:**
#
#
#
#     Input: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
#     Output: [[1,2],[1,4],[1,6]]
#     Explanation: The first 3 pairs are returned from the sequence:
#                  [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]
#
# **Example 2:**
#
#
#
#     Input: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
#     Output: [1,1],[1,1]
#     Explanation: The first 2 pairs are returned from the sequence:
#                  [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
#
# **Example 3:**
#
#
#
#     Input: nums1 = [1,2], nums2 = [3], k = 3
#     Output: [1,3],[2,3]
#     Explanation: All possible pairs are returned from the sequence: [1,3],[2,3]
#
#
#
## @lc code=start
using LeetCode
using DataStructures
function k_smallest_pairs(nums1::Vector{Int}, nums2::Vector{Int}, k::Int)
    hp = Tuple{Int, Int}[]
    odr = Base.Order.By(x -> -x[1] - x[2])
    for n1 in nums1, n2 in nums2
        heappush!(hp, (n1, n2), odr)
        length(hp) > k && heappop!(hp, odr)
    end
    sort!(hp; by = x -> x[1] + x[2])
end
## @lc code=end


N = 100_000

fit(Bernoulli, rand(Bernoulli(0.7), N))

fit(Beta, rand(Beta(1.3, 3.7), N))

fit(Binomial, rand(Binomial(N, 0.3)), N)

fit(DiscreteUniform, rand(DiscreteUniform(300_000, 700_000), N))

fit(Exponential, rand(Exponential(0.1), N))

fit(Gamma, rand(Gamma(7.9, 3.1), N))

fit(Geometric, rand(Geometric(0.1), N))

fit(Laplace, rand(Laplace(10.0, 3.0), N))

fit(Normal, rand(Normal(11.3, 5.7), N))

fit(Poisson, rand(Poisson(19.0), N))

fit(Uniform, rand(Uniform(1.1, 98.3), N))


export
    Grid,
    build,
    boundary,

    Light,

    Permittivity,
    implant!,

    Permeability,

    Simulator,
    next!,
    simulate!

const C = 299792458

struct Grid{T<:Real}
    nx::Int
    ny::Int
    nt::Int

    Δx::T
    Δy::T
    Δt::T

    max_x::T
    max_y::T
    max_t::T
end

"""
    Grid(max_x, max_y, max_t, nx, ny)

Construct a discretization grid for computational domain.

## Arguments

* `max_x`: Linear sizes of first computational domain in meters.
* `max_y`: Linear sizes of second computational domain in meters.
* `max_t`: Maximum computational time in seconds.
* `nx`: Number of discretization grid for first computational domain.
* `ny`: Number of discretization grid for second computational domain.

## Example

```jldoctest
julia> Grid(3e-6, 10e-6, 1e-12, 60, 200);
```
"""
function Grid(max_x, max_y, max_t, nx, ny)
    Δx = max_x / nx
    Δy = max_y / ny

    Δt = 1 / C / √(1/Δx^2 + 1/Δy^2)
    nt = round(Int, max_t/Δt)

    return Grid(nx, ny, nt, Δx, Δy, Δt, max_x, max_y, max_t)
end

Base.size(grid::Grid) = (grid.nx, grid.ny)
Base.size(grid::Grid, d) = d::Integer <= 2 ? size(grid)[d] : 1
function Base.axes(grid::Grid)
    axes_x = grid.Δx * (Base.OneTo(grid.nx) .- grid.nx/2)
    axes_y = grid.Δy * (Base.OneTo(grid.ny) .- grid.Δy/2)

    return (axes_x, axes_y)
end
Base.axes(grid::Grid, d) = d::Integer <= 2 ? axes(grid)[d] : 1
boundary(grid::Grid) = (grid.max_x, grid.max_y)
boundary(grid::Grid, d) = d::Integer <= 2 ? boundary(grid)[d] : 1

function build(grid::Grid)
    return cat(
        repeat(axes(grid, 1), 1, size(grid, 2), 1),
        repeat(axes(grid, 2)', size(grid, 1), 1, 1),
        dims=3
    )
end

struct Light{T<:Real}
    λ::T
    k::T
end

"""
    Light(λ)

Construct a light to propagate.

## Arguments

* `λ`: Wavelength of the light in meters.

## Example

```jldoctest
julia> Light(2.04e-6);
```
"""
function Light(λ)
    return Light(λ, 2π/λ)
end

struct Permittivity{T<:AbstractMatrix}
    ϵ::T
    ϵx::T
    ϵy::T
end

"""
    Permittivity(ϵ_const::Real, grid::Grid)

Construct a uniform permittivity for the medium.

## Arguments

* `ϵ_const`: Permittivity of the medium in F/m.
* `grid`: Discretization grid.

## Example

```jldoctest
julia> grid = Grid(3e-6, 10e-6, 1e-12, 60, 200);

julia> Permittivity(9., grid);
```
"""
function Permittivity(ϵ_const::Real, grid::Grid)
    ϵ = ϵ_const * ones(size(grid))
    ϵx = C * grid.Δt/grid.Δx ./ ϵ
    ϵy = C * grid.Δt/grid.Δy ./ ϵ

    return Permittivity(ϵ, ϵx, ϵy)
end

"""
    implant!(
        permittivity::Permittivity, ϵ_const::Real,
        xs::AbstractVector, ys::AbstractVector, rs::AbstractVector,
        grid::Grid
    )

Implant some bubble defect into the medium.

## Arguments

* `permittivity`: Permittivity object.
* `ϵ_const`: Permittivity of the defect in F/m.
* `xs`: Position of defect of first computational domain in meters.
* `ys`: Position of defect of second computational domain in meters.
* `rs`: Radius of defect in meters.
* `grid`: Discretization grid.

## Example

```jldoctest
julia> grid = Grid(3e-6, 10e-6, 1e-12, 60, 200);

julia> permittivity = Permittivity(9., grid);

julia> ϵ_defect = 1.;

julia> xs_defect = [0, 1e-6, -1e-6];

julia> ys_defect = [1e-6, 2e-6, 3e-6];

julia> rs_defect = [0.5e-6, 0.1e-6, 0.2e-6];

julia> implant!(
           permittivity, ϵ_defect,
           xs_defect, ys_defect, rs_defect,
           grid
       );
```
"""
function implant!(
    permittivity::Permittivity, ϵ_const::Real,
    xs::AbstractVector, ys::AbstractVector, rs::AbstractVector,
    grid::Grid
)
    length(xs) == length(ys) == length(rs) || throw(DimensionMismatch("xs, ys, rs must have same length"))

    in_circle(i, j) = true in [
        √(
            (axes(grid, 1)[i] - xs[c])^2 +
            (axes(grid, 2)[j] - ys[c])^2
        ) < rs[c]
        for c in 1:length(xs)
    ]

    for i in 1:size(grid, 1), j in 1:size(grid, 2)
        in_circle(i, j) && (permittivity.ϵ[i, j] = ϵ_const)
    end

    permittivity.ϵx .= C * grid.Δt/grid.Δx ./ permittivity.ϵ
    permittivity.ϵy .= C * grid.Δt/grid.Δy ./ permittivity.ϵ

    return permittivity
end

struct Permeability{T<:AbstractMatrix}
    μ::T
    μx::T
    μy::T
end

"""
    Permeability(μ_const::Real, grid::Grid)

Construct a uniform permeability for the medium.

## Arguments

* `μ_const`: Permeability of the medium in N/A².
* `grid`: Discretization grid.

## Example

```jldoctest
julia> grid = Grid(3e-6, 10e-6, 1e-12, 60, 200);

julia> Permeability(1., grid);
```
"""
function Permeability(μ_const::Real, grid::Grid)
    μ = μ_const * ones(size(grid))
    μx = C * grid.Δt/grid.Δx ./ μ
    μy = C * grid.Δt/grid.Δy ./ μ

    return Permeability(μ, μx, μy)
end

mutable struct Simulator{T<:AbstractArray}
    grid::Grid
    light::Light
    permittivity::Permittivity
    permeability::Permeability

    ez::T
    hx::T
    hy::T

    t::Int
end

function get_default_e_field(light::Light, grid::Grid; t=1)
    Δt = grid.Δt
    k = light.k

    return 0.1 * exp.(
        -axes(grid, 1)[2:end].^2 ./
        (boundary(grid, 1)/4)^2
    ) * sin(k * C*Δt*t)
end

"""
    Simulator(grid::Grid, light::Light, permittivity::Permittivity, permeability::Permeability)

## Arguments

* `grid`: Discretization grid for computational domain.
* `light`: Light to propagate.
* `permittivity`: Permittivity for the medium.
* `permeability`: Permeability for the medium.

## Example

```jldoctest
julia> grid = Grid(3e-6, 10e-6, 1e-12, 60, 200);

julia> light = Light(2.04e-6);

julia> permittivity = Permittivity(9., grid);

julia> permeability = Permeability(1., grid);

julia> Simulator(grid, light, permittivity, permeability);
"""
function Simulator(grid::Grid, light::Light, permittivity::Permittivity, permeability::Permeability)
    ez = zeros(Float64, size(grid))
    ez[2:end, 1] .= get_default_e_field(light, grid)

    return Simulator(
        grid,
        light,
        permittivity,
        permeability,

        ez,
        zeros(Float64, size(grid)),
        zeros(Float64, size(grid)),

        1
    )
end

function Simulator(;
    max_x=3e-6, max_y=10e-6, max_t=0.1e-12,
    nx=300, ny=1000,
    λ=2.04e-6,
    ϵ = 9., μ = 1.
)
    grid = Grid(max_x, max_y, max_t, nx, ny)
    light = Light(λ)
    permittivity = Permittivity(ϵ, grid)
    permeability = Permeability(μ, grid)

    return Simulator(grid, light, permittivity, permeability)
end

function next!(s::Simulator)
    s.t += 1

    ϵx, ϵy = s.permittivity.ϵx, s.permittivity.ϵy
    μx, μy = s.permeability.μx, s.permeability.μy

    s.ez[2:end, 1] .+= get_default_e_field(s.light, s.grid, t=s.t)

    s.hx[2:end-1, 2:end-1] .+= -μx[2:end-1, 2:end-1].*(s.ez[2:end-1, 2:end-1] - s.ez[2:end-1, 1:end-2])
    s.hy[2:end-1, 2:end-1] .+= +μy[2:end-1, 2:end-1].*(s.ez[2:end-1, 2:end-1] - s.ez[1:end-2, 2:end-1])

    s.ez[2:end-1, 2:end-1] .+=
        ϵx[2:end-1, 2:end-1].*(s.hy[3:end, 2:end-1] - s.hy[2:end-1, 2:end-1]) -
        ϵy[2:end-1, 2:end-1].*(s.hx[2:end-1, 3:end] - s.hx[2:end-1, 2:end-1])

    return s
end

"""
    simulate!(s::Simulator)

Run simulation from current `t` to `max_t`

## Arguments

* `s`: Simulator.

## Example

```julia
julia> grid = Grid(3e-6, 10e-6, 1e-12, 60, 200);

julia> light = Light(2.04e-6);

julia> permittivity = Permittivity(9., grid);

julia> permeability = Permeability(1., grid);

julia> s = Simulator(grid, light, permittivity, permeability);

julia> simulate!(s);
```
"""
function simulate!(s::Simulator)
    nt = s.grid.nt

    p = Progress(nt)
    for _ in (s.t+1):nt
        next!(s)
        ProgressMeter.next!(p)
    end

    return s
end


using Rubin
using Tests
using Elliptic
using HypergeometricFunctions
using Polylogarithms
using SpecialFunctions

@test integrate(x^100*SpecialFunctions.gamma(0,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(101,a*x)+1//101*x^101*SpecialFunctions.gamma(0,a*x))
@test integrate(x^2*SpecialFunctions.gamma(0,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(3,a*x)+1//3*x^3*SpecialFunctions.gamma(0,a*x))
@test integrate(x*SpecialFunctions.gamma(0,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(0,a*x)+-1//2*a^-2*SpecialFunctions.gamma(2,a*x))
@test integrate(SpecialFunctions.gamma(0,a*x), x) == :(x*SpecialFunctions.gamma(0,a*x)+-1*a^-1*exp(-1*a*x))
@test integrate(x^-2*SpecialFunctions.gamma(0,a*x), x) == :(a*SpecialFunctions.gamma(-1,a*x)+-1*x^-1*SpecialFunctions.gamma(0,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(0,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(-2,a*x)+-1//2*x^-2*SpecialFunctions.gamma(0,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(0,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(0,a*x)+1//3*a^3*SpecialFunctions.gamma(-3,a*x))
@test integrate(x^3*SpecialFunctions.gamma(1,a*x), x) == :(-6*a^-4*exp(-1*a*x)+-1*a^-1*x^3*exp(-1*a*x)+-6*x*a^-3*exp(-1*a*x)+-3*a^-2*x^2*exp(-1*a*x))
@test integrate(x^2*SpecialFunctions.gamma(1,a*x), x) == :(-2*a^-3*exp(-1*a*x)+-1*a^-1*x^2*exp(-1*a*x)+-2*x*a^-2*exp(-1*a*x))
@test integrate(x*SpecialFunctions.gamma(1,a*x), x) == :(-1*a^-2*exp(-1*a*x)+-1*x*a^-1*exp(-1*a*x))
@test integrate(SpecialFunctions.gamma(1,a*x), x) == :(-1*a^-1*exp(-1*a*x))
@test integrate(x^-1*SpecialFunctions.gamma(1,a*x), x) == :(expintegralei(-1*a*x))
@test integrate(x^-2*SpecialFunctions.gamma(1,a*x), x) == :(-1*a*expintegralei(-1*a*x)+-1*x^-1*exp(-1*a*x))
@test integrate(x^-3*SpecialFunctions.gamma(1,a*x), x) == :((1/2)*a^2*expintegralei(-1*a*x)+-1//2*x^-2*exp(-1*a*x)+(1/2)*a*x^-1*exp(-1*a*x))
@test integrate(x^-4*SpecialFunctions.gamma(1,a*x), x) == :(-1//3*x^-3*exp(-1*a*x)+-1//6*a^3*expintegralei(-1*a*x)+-1//6*a^2*x^-1*exp(-1*a*x)+1//6*a*x^-2*exp(-1*a*x))
@test integrate(x^100*SpecialFunctions.gamma(2,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(103,a*x)+1//101*x^101*SpecialFunctions.gamma(2,a*x))
@test integrate(x^2*SpecialFunctions.gamma(2,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(5,a*x)+1//3*x^3*SpecialFunctions.gamma(2,a*x))
@test integrate(x*SpecialFunctions.gamma(2,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(2,a*x)+-1//2*a^-2*SpecialFunctions.gamma(4,a*x))
@test integrate(SpecialFunctions.gamma(2,a*x), x) == :(x*SpecialFunctions.gamma(2,a*x)+-1*a^-1*SpecialFunctions.gamma(3,a*x))
@test integrate(x^-1*SpecialFunctions.gamma(2,a*x), x) == :(-1*exp(-1*a*x)+expintegralei(-1*a*x))
@test integrate(x^-2*SpecialFunctions.gamma(2,a*x), x) == :(a*exp(-1*a*x)+-1*x^-1*SpecialFunctions.gamma(2,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(2,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(0,a*x)+-1//2*x^-2*SpecialFunctions.gamma(2,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(2,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(2,a*x)+1//3*a^3*SpecialFunctions.gamma(-1,a*x))
@test integrate(x^100*SpecialFunctions.gamma(3,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(104,a*x)+1//101*x^101*SpecialFunctions.gamma(3,a*x))
@test integrate(x^2*SpecialFunctions.gamma(3,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(6,a*x)+1//3*x^3*SpecialFunctions.gamma(3,a*x))
@test integrate(x*SpecialFunctions.gamma(3,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(3,a*x)+-1//2*a^-2*SpecialFunctions.gamma(5,a*x))
@test integrate(SpecialFunctions.gamma(3,a*x), x) == :(x*SpecialFunctions.gamma(3,a*x)+-1*a^-1*SpecialFunctions.gamma(4,a*x))
@test integrate(x^-1*SpecialFunctions.gamma(3,a*x), x) == :(-1*SpecialFunctions.gamma(2,a*x)+-2*exp(-1*a*x)+2*expintegralei(-1*a*x))
@test integrate(x^-2*SpecialFunctions.gamma(3,a*x), x) == :(a*SpecialFunctions.gamma(2,a*x)+-1*x^-1*SpecialFunctions.gamma(3,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(3,a*x), x) == :((1/2)*a^2*exp(-1*a*x)+-1//2*x^-2*SpecialFunctions.gamma(3,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(3,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(3,a*x)+1//3*a^3*SpecialFunctions.gamma(0,a*x))
@test integrate(x^100*SpecialFunctions.gamma(-1,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(100,a*x)+1//101*x^101*SpecialFunctions.gamma(-1,a*x))
@test integrate(x^3*SpecialFunctions.gamma(-1,a*x), x) == :(-1//4*a^-4*SpecialFunctions.gamma(3,a*x)+1//4*x^4*SpecialFunctions.gamma(-1,a*x))
@test integrate(x^2*SpecialFunctions.gamma(-1,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(2,a*x)+1//3*x^3*SpecialFunctions.gamma(-1,a*x))
@test integrate(x*SpecialFunctions.gamma(-1,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(-1,a*x)+-1//2*a^-2*exp(-1*a*x))
@test integrate(SpecialFunctions.gamma(-1,a*x), x) == :(x*SpecialFunctions.gamma(-1,a*x)+-1*a^-1*SpecialFunctions.gamma(0,a*x))
@test integrate(x^-2*SpecialFunctions.gamma(-1,a*x), x) == :(a*SpecialFunctions.gamma(-2,a*x)+-1*x^-1*SpecialFunctions.gamma(-1,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(-1,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(-3,a*x)+-1//2*x^-2*SpecialFunctions.gamma(-1,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(-1,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(-1,a*x)+1//3*a^3*SpecialFunctions.gamma(-4,a*x))
@test integrate(x^100*SpecialFunctions.gamma(-2,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(99,a*x)+1//101*x^101*SpecialFunctions.gamma(-2,a*x))
@test integrate(x^3*SpecialFunctions.gamma(-2,a*x), x) == :(-1//4*a^-4*SpecialFunctions.gamma(2,a*x)+1//4*x^4*SpecialFunctions.gamma(-2,a*x))
@test integrate(x^2*SpecialFunctions.gamma(-2,a*x), x) == :(-1//3*a^-3*exp(-1*a*x)+1//3*x^3*SpecialFunctions.gamma(-2,a*x))
@test integrate(x*SpecialFunctions.gamma(-2,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(-2,a*x)+-1//2*a^-2*SpecialFunctions.gamma(0,a*x))
@test integrate(SpecialFunctions.gamma(-2,a*x), x) == :(x*SpecialFunctions.gamma(-2,a*x)+-1*a^-1*SpecialFunctions.gamma(-1,a*x))
@test integrate(x^-2*SpecialFunctions.gamma(-2,a*x), x) == :(a*SpecialFunctions.gamma(-3,a*x)+-1*x^-1*SpecialFunctions.gamma(-2,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(-2,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(-4,a*x)+-1//2*x^-2*SpecialFunctions.gamma(-2,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(-2,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(-2,a*x)+1//3*a^3*SpecialFunctions.gamma(-5,a*x))
@test integrate(x^100*SpecialFunctions.gamma(-3,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(98,a*x)+1//101*x^101*SpecialFunctions.gamma(-3,a*x))
@test integrate(x^3*SpecialFunctions.gamma(-3,a*x), x) == :(-1//4*a^-4*exp(-1*a*x)+1//4*x^4*SpecialFunctions.gamma(-3,a*x))
@test integrate(x^2*SpecialFunctions.gamma(-3,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(0,a*x)+1//3*x^3*SpecialFunctions.gamma(-3,a*x))
@test integrate(x*SpecialFunctions.gamma(-3,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(-3,a*x)+-1//2*a^-2*SpecialFunctions.gamma(-1,a*x))
@test integrate(SpecialFunctions.gamma(-3,a*x), x) == :(x*SpecialFunctions.gamma(-3,a*x)+-1*a^-1*SpecialFunctions.gamma(-2,a*x))
@test integrate(x^-2*SpecialFunctions.gamma(-3,a*x), x) == :(a*SpecialFunctions.gamma(-4,a*x)+-1*x^-1*SpecialFunctions.gamma(-3,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(-3,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(-5,a*x)+-1//2*x^-2*SpecialFunctions.gamma(-3,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(-3,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(-3,a*x)+1//3*a^3*SpecialFunctions.gamma(-6,a*x))
@test integrate(x^100*SpecialFunctions.gamma(1/2,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(203//2,a*x)+1//101*x^101*SpecialFunctions.gamma(1/2,a*x))
@test integrate(x^2*SpecialFunctions.gamma(1/2,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(7//2,a*x)+1//3*x^3*SpecialFunctions.gamma(1/2,a*x))
@test integrate(x*SpecialFunctions.gamma(1/2,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(1/2,a*x)+-1//2*a^-2*SpecialFunctions.gamma(5//2,a*x))
@test integrate(SpecialFunctions.gamma(1/2,a*x), x) == :(x*SpecialFunctions.gamma(1/2,a*x)+-1*a^-1*SpecialFunctions.gamma(3//2,a*x))
@test integrate(x^-2*SpecialFunctions.gamma(1/2,a*x), x) == :(a*SpecialFunctions.gamma(-1//2,a*x)+-1*x^-1*SpecialFunctions.gamma(1/2,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(1/2,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(-3//2,a*x)+-1//2*x^-2*SpecialFunctions.gamma(1/2,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(1/2,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(1/2,a*x)+1//3*a^3*SpecialFunctions.gamma(-5//2,a*x))
@test integrate(x^100*SpecialFunctions.gamma(3//2,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(205//2,a*x)+1//101*x^101*SpecialFunctions.gamma(3//2,a*x))
@test integrate(x^2*SpecialFunctions.gamma(3//2,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(9//2,a*x)+1//3*x^3*SpecialFunctions.gamma(3//2,a*x))
@test integrate(x*SpecialFunctions.gamma(3//2,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(3//2,a*x)+-1//2*a^-2*SpecialFunctions.gamma(7//2,a*x))
@test integrate(SpecialFunctions.gamma(3//2,a*x), x) == :(x*SpecialFunctions.gamma(3//2,a*x)+-1*a^-1*SpecialFunctions.gamma(5//2,a*x))
@test integrate(x^-2*SpecialFunctions.gamma(3//2,a*x), x) == :(a*SpecialFunctions.gamma(1/2,a*x)+-1*x^-1*SpecialFunctions.gamma(3//2,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(3//2,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(-1//2,a*x)+-1//2*x^-2*SpecialFunctions.gamma(3//2,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(3//2,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(3//2,a*x)+1//3*a^3*SpecialFunctions.gamma(-3//2,a*x))
@test integrate((d*x)^m*SpecialFunctions.gamma(3,b*x), x) == :(d^-1*(d*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(3,b*x)+-1*b^-1*(b*x)^(-1m)*(d*x)^m*(1+m)^-1*SpecialFunctions.gamma(4+m,b*x))
@test integrate((d*x)^m*SpecialFunctions.gamma(2,b*x), x) == :(d^-1*(d*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(2,b*x)+-1*b^-1*(b*x)^(-1m)*(d*x)^m*(1+m)^-1*SpecialFunctions.gamma(3+m,b*x))
@test integrate((d*x)^m*SpecialFunctions.gamma(1,b*x), x) == :(-1*b^-1*(b*x)^(-1m)*(d*x)^m*SpecialFunctions.gamma(1+m,b*x))
@test integrate((d*x)^m*SpecialFunctions.gamma(0,b*x), x) == :(d^-1*(d*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(0,b*x)+-1*b^-1*(b*x)^(-1m)*(d*x)^m*(1+m)^-1*SpecialFunctions.gamma(1+m,b*x))
@test integrate((d*x)^m*SpecialFunctions.gamma(-1,b*x), x) == :(d^-1*(d*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(-1,b*x)+-1*b^-1*(b*x)^(-1m)*(d*x)^m*(1+m)^-1*SpecialFunctions.gamma(m,b*x))
@test integrate((d*x)^m*SpecialFunctions.gamma(-2,b*x), x) == :(d^-1*(d*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(-2,b*x)+-1*b^-1*(b*x)^(-1m)*(d*x)^m*(1+m)^-1*SpecialFunctions.gamma(-1+m,b*x))
@test integrate(x^m*SpecialFunctions.gamma(n,x), x) == :(-1*(1+m)^-1*SpecialFunctions.gamma(1+m+n,x)+x^(1+m)*(1+m)^-1*SpecialFunctions.gamma(n,x))
@test integrate(x^m*SpecialFunctions.gamma(n,b*x), x) == :(x^(1+m)*(1+m)^-1*SpecialFunctions.gamma(n,b*x)+-1*b^-1*x^m*(b*x)^(-1m)*(1+m)^-1*SpecialFunctions.gamma(1+m+n,b*x))
@test integrate((d*x)^m*SpecialFunctions.gamma(n,x), x) == :(d^-1*(d*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(n,x)+-1*x^(-1m)*(d*x)^m*(1+m)^-1*SpecialFunctions.gamma(1+m+n,x))
@test integrate((d*x)^m*SpecialFunctions.gamma(n,b*x), x) == :(d^-1*(d*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(n,b*x)+-1*b^-1*(b*x)^(-1m)*(d*x)^m*(1+m)^-1*SpecialFunctions.gamma(1+m+n,b*x))
@test integrate(x^100*SpecialFunctions.gamma(n,a*x), x) == :(-1//101*a^-101*SpecialFunctions.gamma(101+n,a*x)+1//101*x^101*SpecialFunctions.gamma(n,a*x))
@test integrate(x^2*SpecialFunctions.gamma(n,a*x), x) == :(-1//3*a^-3*SpecialFunctions.gamma(3+n,a*x)+1//3*x^3*SpecialFunctions.gamma(n,a*x))
@test integrate(x*SpecialFunctions.gamma(n,a*x), x) == :((1/2)*x^2*SpecialFunctions.gamma(n,a*x)+-1//2*a^-2*SpecialFunctions.gamma(2+n,a*x))
@test integrate(SpecialFunctions.gamma(n,a*x), x) == :(x*SpecialFunctions.gamma(n,a*x)+-1*a^-1*SpecialFunctions.gamma(1+n,a*x))
@test integrate(x^-2*SpecialFunctions.gamma(n,a*x), x) == :(a*SpecialFunctions.gamma(-1+n,a*x)+-1*x^-1*SpecialFunctions.gamma(n,a*x))
@test integrate(x^-3*SpecialFunctions.gamma(n,a*x), x) == :((1/2)*a^2*SpecialFunctions.gamma(-2+n,a*x)+-1//2*x^-2*SpecialFunctions.gamma(n,a*x))
@test integrate(x^-4*SpecialFunctions.gamma(n,a*x), x) == :(-1//3*x^-3*SpecialFunctions.gamma(n,a*x)+1//3*a^3*SpecialFunctions.gamma(-3+n,a*x))
@test integrate(x^2*SpecialFunctions.gamma(n,2x), x) == :(-1//24*SpecialFunctions.gamma(3+n,2x)+1//3*x^3*SpecialFunctions.gamma(n,2x))
@test integrate(x*SpecialFunctions.gamma(n,2x), x) == :(-1//8*SpecialFunctions.gamma(2+n,2x)+(1/2)*x^2*SpecialFunctions.gamma(n,2x))
@test integrate(SpecialFunctions.gamma(n,2x), x) == :(-1//2*SpecialFunctions.gamma(1+n,2x)+x*SpecialFunctions.gamma(n,2x))
@test integrate(x^-2*SpecialFunctions.gamma(n,2x), x) == :(2*SpecialFunctions.gamma(-1+n,2x)+-1*x^-1*SpecialFunctions.gamma(n,2x))
@test integrate(x^-3*SpecialFunctions.gamma(n,2x), x) == :(2*SpecialFunctions.gamma(-2+n,2x)+-1//2*x^-2*SpecialFunctions.gamma(n,2x))
@test integrate(x^-4*SpecialFunctions.gamma(n,2x), x) == :(8//3*SpecialFunctions.gamma(-3+n,2x)+-1//3*x^-3*SpecialFunctions.gamma(n,2x))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(0,a+b*x), x) == :(-1//4*b^-4*(b*c+-1*a*d)^3*exp(-1a+-1*b*x)+1//4*d^-1*(c+d*x)^4*SpecialFunctions.gamma(0,a+b*x)+-1//4*b^-4*d^-1*(b*c+-1*a*d)^4*SpecialFunctions.gamma(0,a+b*x)+-1//4*b^-4*d^3*SpecialFunctions.gamma(4,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//4*d*b^-4*(b*c+-1*a*d)^2*SpecialFunctions.gamma(2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//4*b^-4*d^2*(b*c+-1*a*d)*SpecialFunctions.gamma(3,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(0,a+b*x), x) == :(-1//3*b^-3*(b*c+-1*a*d)^2*exp(-1a+-1*b*x)+1//3*d^-1*(c+d*x)^3*SpecialFunctions.gamma(0,a+b*x)+-1//3*b^-3*d^-1*(b*c+-1*a*d)^3*SpecialFunctions.gamma(0,a+b*x)+-1//3*b^-3*d^2*SpecialFunctions.gamma(3,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//3*d*b^-3*(b*c+-1*a*d)*SpecialFunctions.gamma(2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)*SpecialFunctions.gamma(0,a+b*x), x) == :((1/2)*d^-1*(c+d*x)^2*SpecialFunctions.gamma(0,a+b*x)+-1//2*b^-2*(b*c+-1*a*d)*exp(-1a+-1*b*x)+-1//2*d*b^-2*SpecialFunctions.gamma(2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//2*b^-2*d^-1*(b*c+-1*a*d)^2*SpecialFunctions.gamma(0,a+b*x))
@test integrate(SpecialFunctions.gamma(0,a+b*x), x) == :(-1*b^-1*exp(-1a+-1*b*x)+b^-1*(a+b*x)*SpecialFunctions.gamma(0,a+b*x))
@test integrate((c+d*x)^-2*SpecialFunctions.gamma(0,a+b*x), x) == :(-1*d^-1*(c+d*x)^-1*SpecialFunctions.gamma(0,a+b*x)+b*d^-1*(b*c+-1*a*d)^-1*SpecialFunctions.gamma(0,a+b*x)+-1*b*d^-1*(b*c+-1*a*d)^-1*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-3*SpecialFunctions.gamma(0,a+b*x), x) == :(-1//2*d^-1*(c+d*x)^-2*SpecialFunctions.gamma(0,a+b*x)+(1/2)*b^2*d^-1*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(0,a+b*x)+-1//2*b^2*d^-1*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//2*b^2*d^-2*(b*c+-1*a*d)^-1*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-4*SpecialFunctions.gamma(0,a+b*x), x) == :(-1//3*d^-1*(c+d*x)^-3*SpecialFunctions.gamma(0,a+b*x)+1//3*b^3*d^-1*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(0,a+b*x)+-1//3*b^3*d^-1*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//3*b^3*d^-3*(b*c+-1*a*d)^-1*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//3*b^3*d^-2*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^4*SpecialFunctions.gamma(1,a+b*x), x) == :(-1*b^-1*(c+d*x)^4*exp(-1a+-1*b*x)+-24*b^-5*d^4*exp(-1a+-1*b*x)+-24*b^-4*d^3*(c+d*x)*exp(-1a+-1*b*x)+-12*b^-3*d^2*(c+d*x)^2*exp(-1a+-1*b*x)+-4*d*b^-2*(c+d*x)^3*exp(-1a+-1*b*x))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(1,a+b*x), x) == :(-1*b^-1*(c+d*x)^3*exp(-1a+-1*b*x)+-6*b^-4*d^3*exp(-1a+-1*b*x)+-6*b^-3*d^2*(c+d*x)*exp(-1a+-1*b*x)+-3*d*b^-2*(c+d*x)^2*exp(-1a+-1*b*x))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(1,a+b*x), x) == :(-1*b^-1*(c+d*x)^2*exp(-1a+-1*b*x)+-2*b^-3*d^2*exp(-1a+-1*b*x)+-2*d*b^-2*(c+d*x)*exp(-1a+-1*b*x))
@test integrate((c+d*x)*SpecialFunctions.gamma(1,a+b*x), x) == :(-1*d*b^-2*exp(-1a+-1*b*x)+-1*b^-1*(c+d*x)*exp(-1a+-1*b*x))
@test integrate(SpecialFunctions.gamma(1,a+b*x), x) == :(-1*b^-1*exp(-1a+-1*b*x))
@test integrate((c+d*x)^-1*SpecialFunctions.gamma(1,a+b*x), x) == :(d^-1*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x)))
@test integrate((c+d*x)^-2*SpecialFunctions.gamma(1,a+b*x), x) == :(-1*d^-1*(c+d*x)^-1*exp(-1a+-1*b*x)+-1*b*d^-2*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x)))
@test integrate((c+d*x)^-3*SpecialFunctions.gamma(1,a+b*x), x) == :(-1//2*d^-1*(c+d*x)^-2*exp(-1a+-1*b*x)+(1/2)*b*d^-2*(c+d*x)^-1*exp(-1a+-1*b*x)+(1/2)*b^2*d^-3*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x)))
@test integrate((c+d*x)^-4*SpecialFunctions.gamma(1,a+b*x), x) == :(-1//3*d^-1*(c+d*x)^-3*exp(-1a+-1*b*x)+-1//6*b^2*d^-3*(c+d*x)^-1*exp(-1a+-1*b*x)+-1//6*b^3*d^-4*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x))+1//6*b*d^-2*(c+d*x)^-2*exp(-1a+-1*b*x))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(2,a+b*x), x) == :(1//4*d^-1*(c+d*x)^4*SpecialFunctions.gamma(2,a+b*x)+-1//4*b^-4*d^3*SpecialFunctions.gamma(6,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+1//4*b^-4*d^2*(b*c+-1*a*d)*SpecialFunctions.gamma(5,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(2,a+b*x), x) == :(1//3*d^-1*(c+d*x)^3*SpecialFunctions.gamma(2,a+b*x)+-1//3*b^-3*d^2*SpecialFunctions.gamma(5,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+1//3*d*b^-3*(b*c+-1*a*d)*SpecialFunctions.gamma(4,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)*SpecialFunctions.gamma(2,a+b*x), x) == :((1/2)*d^-1*(c+d*x)^2*SpecialFunctions.gamma(2,a+b*x)+(1/2)*b^-2*(b*c+-1*a*d)*SpecialFunctions.gamma(3,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//2*d*b^-2*SpecialFunctions.gamma(4,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate(SpecialFunctions.gamma(2,a+b*x), x) == :(-1*b^-1*SpecialFunctions.gamma(3,a+b*x)+b^-1*(a+b*x)*SpecialFunctions.gamma(2,a+b*x))
@test integrate((c+d*x)^-1*SpecialFunctions.gamma(2,a+b*x), x) == :(-1*d^-1*exp(-1a+-1*b*x)+d^-1*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x))+-1*d^-2*(b*c+-1*a*d)*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x)))
@test integrate((c+d*x)^-2*SpecialFunctions.gamma(2,a+b*x), x) == :(b*d^-2*exp(-1a+-1*b*x)+-1*d^-1*(c+d*x)^-1*SpecialFunctions.gamma(2,a+b*x)+-1*b*d^-3*(b*c+-1*a*d)*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-3*SpecialFunctions.gamma(2,a+b*x), x) == :(-1//2*d^-1*(c+d*x)^-2*SpecialFunctions.gamma(2,a+b*x)+(1/2)*b^2*d^-3*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//2*b^2*d^-4*(b*c+-1*a*d)*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-4*SpecialFunctions.gamma(2,a+b*x), x) == :(-1//3*d^-1*(c+d*x)^-3*SpecialFunctions.gamma(2,a+b*x)+1//3*b^3*d^-4*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//3*b^3*d^-5*(b*c+-1*a*d)*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-5*SpecialFunctions.gamma(2,a+b*x), x) == :(-1//4*d^-1*(c+d*x)^-4*SpecialFunctions.gamma(2,a+b*x)+1//4*b^4*d^-5*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//4*b^4*d^-6*(b*c+-1*a*d)*SpecialFunctions.gamma(-3,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(3,a+b*x), x) == :(1//4*d^-1*(c+d*x)^4*SpecialFunctions.gamma(3,a+b*x)+-1//4*b^-4*d^3*SpecialFunctions.gamma(7,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+(1/2)*b^-4*d^2*(b*c+-1*a*d)*SpecialFunctions.gamma(6,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//4*d*b^-4*(b*c+-1*a*d)^2*SpecialFunctions.gamma(5,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(3,a+b*x), x) == :(1//3*d^-1*(c+d*x)^3*SpecialFunctions.gamma(3,a+b*x)+-1//3*b^-3*d^2*SpecialFunctions.gamma(6,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//3*b^-3*(b*c+-1*a*d)^2*SpecialFunctions.gamma(4,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+2//3*d*b^-3*(b*c+-1*a*d)*SpecialFunctions.gamma(5,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)*SpecialFunctions.gamma(3,a+b*x), x) == :((1/2)*d^-1*(c+d*x)^2*SpecialFunctions.gamma(3,a+b*x)+-1*b^-2*(b*c+-1*a*d)*SpecialFunctions.gamma(4,a+b*x)+-1//2*d*b^-2*SpecialFunctions.gamma(5,a+b*x)+-1//2*b^-2*d^-1*(b*c+-1*a*d)^2*SpecialFunctions.gamma(3,a+b*x))
@test integrate(SpecialFunctions.gamma(3,a+b*x), x) == :(-1*b^-1*SpecialFunctions.gamma(4,a+b*x)+b^-1*(a+b*x)*SpecialFunctions.gamma(3,a+b*x))
@test integrate((c+d*x)^-1*SpecialFunctions.gamma(3,a+b*x), x) == :(-3*d^-1*exp(-1a+-1*b*x)+d^-2*(b*c+-1*a*d)*exp(-1a+-1*b*x)+-1*d^-1*(a+b*x)*exp(-1a+-1*b*x)+2*d^-1*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x))+d^-3*(b*c+-1*a*d)^2*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x))+-1*d^-2*(-2*a*d+2*b*c)*exp(-1a+b*c*d^-1)*expintegralei(-1*b*d^-1*(c+d*x)))
@test integrate((c+d*x)^-2*SpecialFunctions.gamma(3,a+b*x), x) == :(b*d^-2*SpecialFunctions.gamma(2,a+b*x)+-1*d^-1*(c+d*x)^-1*SpecialFunctions.gamma(3,a+b*x)+-1*b*d^-3*(b*c+-1*a*d)*exp(-1a+-1*b*x)+b*d^-4*(b*c+-1*a*d)^2*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-3*SpecialFunctions.gamma(3,a+b*x), x) == :((1/2)*b^2*d^-3*exp(-1a+-1*b*x)+-1//2*d^-1*(c+d*x)^-2*SpecialFunctions.gamma(3,a+b*x)+(1/2)*b^2*d^-5*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1*b^2*d^-4*(b*c+-1*a*d)*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-4*SpecialFunctions.gamma(3,a+b*x), x) == :(-1//3*d^-1*(c+d*x)^-3*SpecialFunctions.gamma(3,a+b*x)+1//3*b^3*d^-4*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-2//3*b^3*d^-5*(b*c+-1*a*d)*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+1//3*b^3*d^-6*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-5*SpecialFunctions.gamma(3,a+b*x), x) == :(-1//4*d^-1*(c+d*x)^-4*SpecialFunctions.gamma(3,a+b*x)+1//4*b^4*d^-5*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//2*b^4*d^-6*(b*c+-1*a*d)*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+1//4*b^4*d^-7*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-3,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(-1,a+b*x), x) == :(-1*b^-4*(b*c+-1*a*d)^3*SpecialFunctions.gamma(0,a+b*x)+-1//4*b^-4*d^3*SpecialFunctions.gamma(3,a+b*x)+1//4*d^-1*(c+d*x)^4*SpecialFunctions.gamma(-1,a+b*x)+-1*b^-4*d^2*(b*c+-1*a*d)*SpecialFunctions.gamma(2,a+b*x)+-3//2*d*b^-4*(b*c+-1*a*d)^2*exp(-1a+-1*b*x)+-1//4*b^-4*d^-1*(b*c+-1*a*d)^4*SpecialFunctions.gamma(-1,a+b*x))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(-1,a+b*x), x) == :(-1*b^-3*(b*c+-1*a*d)^2*SpecialFunctions.gamma(0,a+b*x)+1//3*d^-1*(c+d*x)^3*SpecialFunctions.gamma(-1,a+b*x)+-1//3*d*b^-3*(-2*a*d+3*b*c)*exp(-1a+-1*b*x)+-1//3*b^-3*d^-1*(b*c+-1*a*d)^3*SpecialFunctions.gamma(-1,a+b*x)+-1//3*b^-3*d^2*SpecialFunctions.gamma(2,b*x)*exp(-1a))
@test integrate((c+d*x)*SpecialFunctions.gamma(-1,a+b*x), x) == :((1/2)*d^-1*(c+d*x)^2*SpecialFunctions.gamma(-1,a+b*x)+-1*b^-2*(b*c+-1*a*d)*SpecialFunctions.gamma(0,a+b*x)+-1//2*d*b^-2*exp(-1a+-1*b*x)+-1//2*b^-2*d^-1*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-1,a+b*x))
@test integrate(SpecialFunctions.gamma(-1,a+b*x), x) == :(-1*b^-1*SpecialFunctions.gamma(0,a+b*x)+b^-1*(a+b*x)*SpecialFunctions.gamma(-1,a+b*x))
@test integrate((c+d*x)^-2*SpecialFunctions.gamma(-1,a+b*x), x) == :(-1*b*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(0,a+b*x)+-1*d^-1*(c+d*x)^-1*SpecialFunctions.gamma(-1,a+b*x)+b*d^-1*(b*c+-1*a*d)^-1*SpecialFunctions.gamma(-1,a+b*x)+b*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-3*SpecialFunctions.gamma(-1,a+b*x), x) == :(-1*b^2*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(0,a+b*x)+-1//2*d^-1*(c+d*x)^-2*SpecialFunctions.gamma(-1,a+b*x)+b^2*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+(1/2)*b^2*d^-1*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-1,a+b*x)+(1/2)*b^2*d^-1*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-4*SpecialFunctions.gamma(-1,a+b*x), x) == :(-1*b^3*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(0,a+b*x)+-1//3*d^-1*(c+d*x)^-3*SpecialFunctions.gamma(-1,a+b*x)+b^3*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+1//3*b^3*d^-1*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-1,a+b*x)+1//3*b^3*d^-2*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+2//3*b^3*d^-1*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(-2,a+b*x), x) == :(-1*b^-4*(b*c+-1*a*d)^3*SpecialFunctions.gamma(-1,a+b*x)+1//4*d^-1*(c+d*x)^4*SpecialFunctions.gamma(-2,a+b*x)+-3//2*d*b^-4*(b*c+-1*a*d)^2*SpecialFunctions.gamma(0,a+b*x)+-1//4*b^-4*d^-1*(b*c+-1*a*d)^4*SpecialFunctions.gamma(-2,a+b*x)+-1//4*b^-4*d^2*(-3*a*d+4*b*c)*exp(-1a+-1*b*x)+-1//4*b^-4*d^3*SpecialFunctions.gamma(2,b*x)*exp(-1a))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(-2,a+b*x), x) == :(-1*b^-3*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-1,a+b*x)+-1//3*b^-3*d^2*exp(-1a+-1*b*x)+1//3*d^-1*(c+d*x)^3*SpecialFunctions.gamma(-2,a+b*x)+-1*d*b^-3*(b*c+-1*a*d)*SpecialFunctions.gamma(0,a+b*x)+-1//3*b^-3*d^-1*(b*c+-1*a*d)^3*SpecialFunctions.gamma(-2,a+b*x))
@test integrate((c+d*x)*SpecialFunctions.gamma(-2,a+b*x), x) == :((1/2)*d^-1*(c+d*x)^2*SpecialFunctions.gamma(-2,a+b*x)+-1*b^-2*(b*c+-1*a*d)*SpecialFunctions.gamma(-1,a+b*x)+-1//2*d*b^-2*SpecialFunctions.gamma(0,a+b*x)+-1//2*b^-2*d^-1*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-2,a+b*x))
@test integrate(SpecialFunctions.gamma(-2,a+b*x), x) == :(-1*b^-1*SpecialFunctions.gamma(-1,a+b*x)+b^-1*(a+b*x)*SpecialFunctions.gamma(-2,a+b*x))
@test integrate((c+d*x)^-2*SpecialFunctions.gamma(-2,a+b*x), x) == :(-1*b*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-1,a+b*x)+-1*d^-1*(c+d*x)^-1*SpecialFunctions.gamma(-2,a+b*x)+b*d*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(0,a+b*x)+b*d^-1*(b*c+-1*a*d)^-1*SpecialFunctions.gamma(-2,a+b*x)+-1*b*d*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-3*SpecialFunctions.gamma(-2,a+b*x), x) == :(-1*b^2*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-1,a+b*x)+-1//2*d^-1*(c+d*x)^-2*SpecialFunctions.gamma(-2,a+b*x)+(1/2)*b^2*d^-1*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-2,a+b*x)+-1//2*b^2*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+3//2*d*b^2*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(0,a+b*x)+-3//2*d*b^2*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-4*SpecialFunctions.gamma(-2,a+b*x), x) == :(-1*b^3*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(-1,a+b*x)+-1//3*d^-1*(c+d*x)^-3*SpecialFunctions.gamma(-2,a+b*x)+-1*b^3*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+2*d*b^3*(b*c+-1*a*d)^-5*SpecialFunctions.gamma(0,a+b*x)+1//3*b^3*d^-1*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-2,a+b*x)+-2*d*b^3*(b*c+-1*a*d)^-5*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1//3*b^3*d^-1*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(-3,a+b*x), x) == :(-1*b^-4*(b*c+-1*a*d)^3*SpecialFunctions.gamma(-2,a+b*x)+-1//4*b^-4*d^3*exp(-1a+-1*b*x)+1//4*d^-1*(c+d*x)^4*SpecialFunctions.gamma(-3,a+b*x)+-1*b^-4*d^2*(b*c+-1*a*d)*SpecialFunctions.gamma(0,a+b*x)+-3//2*d*b^-4*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-1,a+b*x)+-1//4*b^-4*d^-1*(b*c+-1*a*d)^4*SpecialFunctions.gamma(-3,a+b*x))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(-3,a+b*x), x) == :(-1*b^-3*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-2,a+b*x)+-1//3*b^-3*d^2*SpecialFunctions.gamma(0,a+b*x)+1//3*d^-1*(c+d*x)^3*SpecialFunctions.gamma(-3,a+b*x)+-1*d*b^-3*(b*c+-1*a*d)*SpecialFunctions.gamma(-1,a+b*x)+-1//3*b^-3*d^-1*(b*c+-1*a*d)^3*SpecialFunctions.gamma(-3,a+b*x))
@test integrate((c+d*x)*SpecialFunctions.gamma(-3,a+b*x), x) == :((1/2)*d^-1*(c+d*x)^2*SpecialFunctions.gamma(-3,a+b*x)+-1*b^-2*(b*c+-1*a*d)*SpecialFunctions.gamma(-2,a+b*x)+-1//2*d*b^-2*SpecialFunctions.gamma(-1,a+b*x)+-1//2*b^-2*d^-1*(b*c+-1*a*d)^2*SpecialFunctions.gamma(-3,a+b*x))
@test integrate(SpecialFunctions.gamma(-3,a+b*x), x) == :(-1*b^-1*SpecialFunctions.gamma(-2,a+b*x)+b^-1*(a+b*x)*SpecialFunctions.gamma(-3,a+b*x))
@test integrate((c+d*x)^-2*SpecialFunctions.gamma(-3,a+b*x), x) == :(-1*b*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-2,a+b*x)+-1*d^-1*(c+d*x)^-1*SpecialFunctions.gamma(-3,a+b*x)+b*d*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-1,a+b*x)+b*d^-1*(b*c+-1*a*d)^-1*SpecialFunctions.gamma(-3,a+b*x)+-1*b*d^2*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(0,a+b*x)+b*d^2*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-3*SpecialFunctions.gamma(-3,a+b*x), x) == :(-1*b^2*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-2,a+b*x)+-1//2*d^-1*(c+d*x)^-2*SpecialFunctions.gamma(-3,a+b*x)+(1/2)*b^2*d^-1*(b*c+-1*a*d)^-2*SpecialFunctions.gamma(-3,a+b*x)+-2*b^2*d^2*(b*c+-1*a*d)^-5*SpecialFunctions.gamma(0,a+b*x)+3//2*d*b^2*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(-1,a+b*x)+(1/2)*d*b^2*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+2*b^2*d^2*(b*c+-1*a*d)^-5*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^-4*SpecialFunctions.gamma(-3,a+b*x), x) == :(-1*b^3*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(-2,a+b*x)+-1//3*d^-1*(c+d*x)^-3*SpecialFunctions.gamma(-3,a+b*x)+2*d*b^3*(b*c+-1*a*d)^-5*SpecialFunctions.gamma(-1,a+b*x)+-10//3*b^3*d^2*(b*c+-1*a*d)^-6*SpecialFunctions.gamma(0,a+b*x)+1//3*b^3*d^-1*(b*c+-1*a*d)^-3*SpecialFunctions.gamma(-3,a+b*x)+1//3*b^3*(b*c+-1*a*d)^-4*SpecialFunctions.gamma(-2,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+4//3*d*b^3*(b*c+-1*a*d)^-5*SpecialFunctions.gamma(-1,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+10//3*b^3*d^2*(b*c+-1*a*d)^-6*SpecialFunctions.gamma(0,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate(x^5//2*SpecialFunctions.gamma(2,a+b*x), x) == :(2//7*x^7//2*SpecialFunctions.gamma(2,a+b*x)+-2//7*b^-3*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(11//2,b*x)*exp(-1a)+-2//7*a*b^-3*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(9//2,b*x)*exp(-1a))
@test integrate(x^3//2*SpecialFunctions.gamma(2,a+b*x), x) == :(2//5*x^5//2*SpecialFunctions.gamma(2,a+b*x)+-2//5*b^-2*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(9//2,b*x)*exp(-1a)+-2//5*a*b^-2*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(7//2,b*x)*exp(-1a))
@test integrate(x^(1/2)*SpecialFunctions.gamma(2,a+b*x), x) == :(2//3*x^3//2*SpecialFunctions.gamma(2,a+b*x)+-2//3*b^-1*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(7//2,b*x)*exp(-1a)+-2//3*a*b^-1*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(5//2,b*x)*exp(-1a))
@test integrate(x^-1//2*SpecialFunctions.gamma(2,a+b*x), x) == :(2*x^(1/2)*SpecialFunctions.gamma(2,a+b*x)+-2*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(5//2,b*x)*exp(-1a)+-2*a*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(3//2,b*x)*exp(-1a))
@test integrate(x^-3//2*SpecialFunctions.gamma(2,a+b*x), x) == :(-2*x^-1//2*SpecialFunctions.gamma(2,a+b*x)+2*a*x^-1//2*(b*x)^(1/2)*SpecialFunctions.gamma(1/2,b*x)*exp(-1a)+2*b*x^(1/2)*(b*x)^-1//2*SpecialFunctions.gamma(3//2,b*x)*exp(-1a))
@test integrate(x^-5//2*SpecialFunctions.gamma(2,a+b*x), x) == :(-2//3*x^-3//2*SpecialFunctions.gamma(2,a+b*x)+2//3*b*x^-1//2*(b*x)^(1/2)*SpecialFunctions.gamma(1/2,b*x)*exp(-1a)+2//3*a*b*x^-1//2*(b*x)^(1/2)*SpecialFunctions.gamma(-1//2,b*x)*exp(-1a))
@test integrate(x^-7//2*SpecialFunctions.gamma(2,a+b*x), x) == :(-2//5*x^-5//2*SpecialFunctions.gamma(2,a+b*x)+2//5*b^2*x^-1//2*(b*x)^(1/2)*SpecialFunctions.gamma(-1//2,b*x)*exp(-1a)+2//5*a*b^2*x^-1//2*(b*x)^(1/2)*SpecialFunctions.gamma(-3//2,b*x)*exp(-1a))
@test integrate(x^-9//2*SpecialFunctions.gamma(2,a+b*x), x) == :(-2//7*x^-7//2*SpecialFunctions.gamma(2,a+b*x)+2//7*b^3*x^-1//2*(b*x)^(1/2)*SpecialFunctions.gamma(-3//2,b*x)*exp(-1a)+2//7*a*b^3*x^-1//2*(b*x)^(1/2)*SpecialFunctions.gamma(-5//2,b*x)*exp(-1a))
@test integrate(x^4//3*SpecialFunctions.gamma(2,a+b*x), x) == :(3//7*x^7//3*SpecialFunctions.gamma(2,a+b*x)+-3//7*b^-2*x^1//3*(b*x)^-1//3*SpecialFunctions.gamma(13//3,b*x)*exp(-1a)+-3//7*a*b^-2*x^1//3*(b*x)^-1//3*SpecialFunctions.gamma(10//3,b*x)*exp(-1a))
@test integrate(x^2//3*SpecialFunctions.gamma(2,a+b*x), x) == :(3//5*x^5//3*SpecialFunctions.gamma(2,a+b*x)+-3//5*b^-1*x^2//3*(b*x)^-2//3*SpecialFunctions.gamma(11//3,b*x)*exp(-1a)+-3//5*a*b^-1*x^2//3*(b*x)^-2//3*SpecialFunctions.gamma(8//3,b*x)*exp(-1a))
@test integrate(x^1//3*SpecialFunctions.gamma(2,a+b*x), x) == :(3//4*x^4//3*SpecialFunctions.gamma(2,a+b*x)+-3//4*b^-1*x^1//3*(b*x)^-1//3*SpecialFunctions.gamma(10//3,b*x)*exp(-1a)+-3//4*a*b^-1*x^1//3*(b*x)^-1//3*SpecialFunctions.gamma(7//3,b*x)*exp(-1a))
@test integrate(x^-1//3*SpecialFunctions.gamma(2,a+b*x), x) == :(3//2*x^2//3*SpecialFunctions.gamma(2,a+b*x)+-3//2*x^2//3*(b*x)^-2//3*SpecialFunctions.gamma(8//3,b*x)*exp(-1a)+-3//2*a*x^2//3*(b*x)^-2//3*SpecialFunctions.gamma(5//3,b*x)*exp(-1a))
@test integrate(x^-2//3*SpecialFunctions.gamma(2,a+b*x), x) == :(3*x^1//3*SpecialFunctions.gamma(2,a+b*x)+-3*x^1//3*(b*x)^-1//3*SpecialFunctions.gamma(7//3,b*x)*exp(-1a)+-3*a*x^1//3*(b*x)^-1//3*SpecialFunctions.gamma(4//3,b*x)*exp(-1a))
@test integrate(x^-4//3*SpecialFunctions.gamma(2,a+b*x), x) == :(-3*x^-1//3*SpecialFunctions.gamma(2,a+b*x)+3*a*x^-1//3*(b*x)^1//3*SpecialFunctions.gamma(2//3,b*x)*exp(-1a)+3*b*x^2//3*(b*x)^-2//3*SpecialFunctions.gamma(5//3,b*x)*exp(-1a))
@test integrate((c+d*x)^m*SpecialFunctions.gamma(3,a+b*x), x) == :(d^-1*(1+m)^-1*(c+d*x)^(1+m)*SpecialFunctions.gamma(3,a+b*x)+-1*b^-1*(b*d^-1*(c+d*x))^(-1m)*(1+m)^-1*(c+d*x)^m*SpecialFunctions.gamma(4+m,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+b^-1*d^-1*(b*d^-1*(c+d*x))^(-1m)*(1+m)^-1*(c+d*x)^m*(-2*a*d+2*b*c)*SpecialFunctions.gamma(3+m,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+-1*b^-1*d^-2*(b*d^-1*(c+d*x))^(-1m)*(1+m)^-1*(c+d*x)^m*(b*c+-1*a*d)^2*SpecialFunctions.gamma(2+m,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^m*SpecialFunctions.gamma(2,a+b*x), x) == :(d^-1*(1+m)^-1*(c+d*x)^(1+m)*SpecialFunctions.gamma(2,a+b*x)+-1*b^-1*(b*d^-1*(c+d*x))^(-1m)*(1+m)^-1*(c+d*x)^m*SpecialFunctions.gamma(3+m,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1)+b^-1*d^-1*(b*d^-1*(c+d*x))^(-1m)*(1+m)^-1*(c+d*x)^m*(b*c+-1*a*d)*SpecialFunctions.gamma(2+m,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^m*SpecialFunctions.gamma(1,a+b*x), x) == :(-1*b^-1*(b*d^-1*(c+d*x))^(-1m)*(c+d*x)^m*SpecialFunctions.gamma(1+m,b*d^-1*(c+d*x))*exp(-1a+b*c*d^-1))
@test integrate((c+d*x)^4*SpecialFunctions.gamma(n,a+b*x), x) == :(-1*b^-5*(b*c+-1*a*d)^4*SpecialFunctions.gamma(1+n,a+b*x)+-1//5*b^-5*d^4*SpecialFunctions.gamma(5+n,a+b*x)+1//5*d^-1*(c+d*x)^5*SpecialFunctions.gamma(n,a+b*x)+-1*b^-5*d^3*(b*c+-1*a*d)*SpecialFunctions.gamma(4+n,a+b*x)+-2*d*b^-5*(b*c+-1*a*d)^3*SpecialFunctions.gamma(2+n,a+b*x)+-2*b^-5*d^2*(b*c+-1*a*d)^2*SpecialFunctions.gamma(3+n,a+b*x)+-1//5*b^-5*d^-1*(b*c+-1*a*d)^5*SpecialFunctions.gamma(n,a+b*x))
@test integrate((c+d*x)^3*SpecialFunctions.gamma(n,a+b*x), x) == :(-1*b^-4*(b*c+-1*a*d)^3*SpecialFunctions.gamma(1+n,a+b*x)+-1//4*b^-4*d^3*SpecialFunctions.gamma(4+n,a+b*x)+1//4*d^-1*(c+d*x)^4*SpecialFunctions.gamma(n,a+b*x)+-1*b^-4*d^2*(b*c+-1*a*d)*SpecialFunctions.gamma(3+n,a+b*x)+-3//2*d*b^-4*(b*c+-1*a*d)^2*SpecialFunctions.gamma(2+n,a+b*x)+-1//4*b^-4*d^-1*(b*c+-1*a*d)^4*SpecialFunctions.gamma(n,a+b*x))
@test integrate((c+d*x)^2*SpecialFunctions.gamma(n,a+b*x), x) == :(-1*b^-3*(b*c+-1*a*d)^2*SpecialFunctions.gamma(1+n,a+b*x)+-1//3*b^-3*d^2*SpecialFunctions.gamma(3+n,a+b*x)+1//3*d^-1*(c+d*x)^3*SpecialFunctions.gamma(n,a+b*x)+-1*d*b^-3*(b*c+-1*a*d)*SpecialFunctions.gamma(2+n,a+b*x)+-1//3*b^-3*d^-1*(b*c+-1*a*d)^3*SpecialFunctions.gamma(n,a+b*x))
@test integrate((c+d*x)*SpecialFunctions.gamma(n,a+b*x), x) == :((1/2)*d^-1*(c+d*x)^2*SpecialFunctions.gamma(n,a+b*x)+-1*b^-2*(b*c+-1*a*d)*SpecialFunctions.gamma(1+n,a+b*x)+-1//2*d*b^-2*SpecialFunctions.gamma(2+n,a+b*x)+-1//2*b^-2*d^-1*(b*c+-1*a*d)^2*SpecialFunctions.gamma(n,a+b*x))
@test integrate(SpecialFunctions.gamma(n,a+b*x), x) == :(-1*b^-1*SpecialFunctions.gamma(1+n,a+b*x)+b^-1*(a+b*x)*SpecialFunctions.gamma(n,a+b*x))
@test integrate(x^2*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n))), x) == :(1//3*x^3*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n)))+-1//3*x^3*(c*x^n)^(-3*n^-1)*(d*(a+b*log(c*x^n)))^p*(-1*b^-1*n^-1*(3+-1*b*d*n)*(a+b*log(c*x^n)))^(-1p)*SpecialFunctions.gamma(p,-1*b^-1*n^-1*(3+-1*b*d*n)*(a+b*log(c*x^n)))*exp(-3*a*b^-1*n^-1))
@test integrate(x*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n))), x) == :((1/2)*x^2*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n)))+-1//2*x^2*(c*x^n)^(-2*n^-1)*(d*(a+b*log(c*x^n)))^p*(-1*b^-1*n^-1*(2+-1*b*d*n)*(a+b*log(c*x^n)))^(-1p)*SpecialFunctions.gamma(p,-1*b^-1*n^-1*(2+-1*b*d*n)*(a+b*log(c*x^n)))*exp(-2*a*b^-1*n^-1))
@test integrate(SpecialFunctions.gamma(p,d*(a+b*log(c*x^n))), x) == :(x*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n)))+-1*x*(c*x^n)^(-1*n^-1)*(d*(a+b*log(c*x^n)))^p*(-1*b^-1*n^-1*(1+-1*b*d*n)*(a+b*log(c*x^n)))^(-1p)*SpecialFunctions.gamma(p,-1*b^-1*n^-1*(1+-1*b*d*n)*(a+b*log(c*x^n)))*exp(-1*a*b^-1*n^-1))
@test integrate(x^-1*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n))), x) == :(b^-1*n^-1*(a+b*log(c*x^n))*SpecialFunctions.gamma(p,a*d+b*d*log(c*x^n))+-1*b^-1*d^-1*n^-1*SpecialFunctions.gamma(1+p,a*d+b*d*log(c*x^n)))
@test integrate(x^-2*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n))), x) == :(-1*x^-1*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n)))+x^-1*(c*x^n)^(n^-1)*(d*(a+b*log(c*x^n)))^p*(b^-1*n^-1*(1+b*d*n)*(a+b*log(c*x^n)))^(-1p)*SpecialFunctions.gamma(p,b^-1*n^-1*(1+b*d*n)*(a+b*log(c*x^n)))*exp(a*b^-1*n^-1))
@test integrate(x^-3*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n))), x) == :(-1//2*x^-2*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n)))+(1/2)*x^-2*(c*x^n)^(2*n^-1)*(d*(a+b*log(c*x^n)))^p*(b^-1*n^-1*(2+b*d*n)*(a+b*log(c*x^n)))^(-1p)*SpecialFunctions.gamma(p,b^-1*n^-1*(2+b*d*n)*(a+b*log(c*x^n)))*exp(2*a*b^-1*n^-1))
@test integrate((e*x)^m*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n))), x) == :(e^-1*(e*x)^(1+m)*(1+m)^-1*SpecialFunctions.gamma(p,d*(a+b*log(c*x^n)))+-1*e^-1*(c*x^n)^(-1*n^-1*(1+m))*(d*(a+b*log(c*x^n)))^p*(e*x)^(1+m)*(-1*b^-1*n^-1*(a+b*log(c*x^n))*(1+m+-1*b*d*n))^(-1p)*(1+m)^-1*SpecialFunctions.gamma(p,-1*b^-1*n^-1*(a+b*log(c*x^n))*(1+m+-1*b*d*n))*exp(-1*a*b^-1*n^-1*(1+m)))
@test integrate((c+d*x)^3*loggamma(a+b*x), x) == :(b^-1*(c+d*x)^3*SpecialFunctions.polygamma(-2,x)+-6*b^-4*d^3*SpecialFunctions.polygamma(-5,x)+-3*d*b^-2*(c+d*x)^2*SpecialFunctions.polygamma(-3,x)+6*b^-3*d^2*(c+d*x)*SpecialFunctions.polygamma(-4,x))
@test integrate((c+d*x)^2*loggamma(a+b*x), x) == :(b^-1*(c+d*x)^2*SpecialFunctions.polygamma(-2,x)+2*b^-3*d^2*SpecialFunctions.polygamma(-4,x)+-2*d*b^-2*(c+d*x)*SpecialFunctions.polygamma(-3,x))
@test integrate((c+d*x)*loggamma(a+b*x), x) == :(b^-1*(c+d*x)*SpecialFunctions.polygamma(-2,x)+-1*d*b^-2*SpecialFunctions.polygamma(-3,x))
@test integrate(loggamma(a+b*x), x) == :(b^-1*SpecialFunctions.polygamma(-2,x))
@test integrate((c+d*x)^2*log(SpecialFunctions.gamma(a+b*x)), x) == :(b^-1*(c+d*x)^2*SpecialFunctions.polygamma(-2,x)+2*b^-3*d^2*SpecialFunctions.polygamma(-4,x)+-1//3*d^-1*(c+d*x)^3*loggamma(a+b*x)+1//3*d^-1*(c+d*x)^3*log(SpecialFunctions.gamma(a+b*x))+-2*d*b^-2*(c+d*x)*SpecialFunctions.polygamma(-3,x))
@test integrate((c+d*x)*log(SpecialFunctions.gamma(a+b*x)), x) == :(b^-1*(c+d*x)*SpecialFunctions.polygamma(-2,x)+(1/2)*d^-1*(c+d*x)^2*log(SpecialFunctions.gamma(a+b*x))+-1*d*b^-2*SpecialFunctions.polygamma(-3,x)+-1//2*d^-1*(c+d*x)^2*loggamma(a+b*x))
@test integrate(log(SpecialFunctions.gamma(a+b*x)), x) == :(x*log(SpecialFunctions.gamma(a+b*x))+b^-1*SpecialFunctions.polygamma(-2,x)+-1*x*loggamma(a+b*x))
@test integrate((c+d*x)^3*SpecialFunctions.polygamma(n,x), x) == :(b^-1*(c+d*x)^3*SpecialFunctions.polygamma(-1+n,x)+-6*b^-4*d^3*SpecialFunctions.polygamma(-4+n,x)+-3*d*b^-2*(c+d*x)^2*SpecialFunctions.polygamma(-2+n,x)+6*b^-3*d^2*(c+d*x)*SpecialFunctions.polygamma(-3+n,x))
@test integrate((c+d*x)^2*SpecialFunctions.polygamma(n,x), x) == :(b^-1*(c+d*x)^2*SpecialFunctions.polygamma(-1+n,x)+2*b^-3*d^2*SpecialFunctions.polygamma(-3+n,x)+-2*d*b^-2*(c+d*x)*SpecialFunctions.polygamma(-2+n,x))
@test integrate((c+d*x)*SpecialFunctions.polygamma(n,x), x) == :(b^-1*(c+d*x)*SpecialFunctions.polygamma(-1+n,x)+-1*d*b^-2*SpecialFunctions.polygamma(-2+n,x))
@test integrate(SpecialFunctions.polygamma(n,x), x) == :(b^-1*SpecialFunctions.polygamma(-1+n,x))
@test integrate(x^2*SpecialFunctions.polygamma(1,x), x) == :(2*b^-3*SpecialFunctions.polygamma(-2,x)+b^-1*x^2*SpecialFunctions.polygamma(0,x)+-2*x*b^-2*loggamma(a+b*x))
@test integrate(x^-2*SpecialFunctions.polygamma(1,x)+-1*b*x^-1*SpecialFunctions.polygamma(2,x), x) == :(-1*x^-1*SpecialFunctions.polygamma(1,x))
@test integrate(x^-2*SpecialFunctions.polygamma(n,x)+-1*b*x^-1*SpecialFunctions.polygamma(1+n,x), x) == :(-1*x^-1*SpecialFunctions.polygamma(n,x))
@test integrate(SpecialFunctions.gamma(a+b*x)^n*SpecialFunctions.polygamma(0,x), x) == :(b^-1*n^-1*SpecialFunctions.gamma(a+b*x)^n)
@test integrate(factorial(a+b*x)^n*SpecialFunctions.polygamma(0,x), x) == :(b^-1*n^-1*factorial(a+b*x)^n)

test/8 Special functions/8.6 Gamma functions.jl


using Distributions
using SpecialFunctions

function fim(d::Normal)
    μ,σ = params(d)
    [1 / σ^2 0.0; 0.0 2/σ^2]
end
function fim(d::Beta)
    α,β = params(d)
    [trigamma(α) - trigamma(α + β)  -trigamma(α + β);
     -trigamma(α + β)   trigamma(β) - trigamma(α + β)]
end
function fim(d::Gamma)
    α,θ = params(d);
    [polygamma(1,α) θ; θ  α*θ^2]
end

function fim(d::Product)
    BlockDiagonal([fim(marginal) for marginal in d.v])
end


using JuMP, EAGO

                     m = Model()

                     EAGO.register_eago_operators!(m)

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

                     @variable(m, -9.06497171739458 <= q <= 7.221276301954956)

                     add_NL_constraint(m, :((0.6131370972312755 + 0.7644530725075485*$(x[1]) + 0.7481034782956382*$(x[2]) + 0.43160377180111187*$(x[3]))/(1 + exp(-(0.6131370972312755 + 0.7644530725075485*$(x[1]) + 0.7481034782956382*$(x[2]) + 0.43160377180111187*$(x[3])))) + (-0.2235485813631728 + -0.4821458309603419*$(x[1]) + -0.0034703029219445014*$(x[2]) + 0.5577106220080164*$(x[3]))/(1 + exp(-(-0.2235485813631728 + -0.4821458309603419*$(x[1]) + -0.0034703029219445014*$(x[2]) + 0.5577106220080164*$(x[3])))) + (0.3888102587500133 + 0.8414638325842052*$(x[1]) + 0.671097918031764*$(x[2]) + -0.05238562929678103*$(x[3]))/(1 + exp(-(0.3888102587500133 + 0.8414638325842052*$(x[1]) + 0.671097918031764*$(x[2]) + -0.05238562929678103*$(x[3])))) + (-0.9543429273497122 + 0.7406334788256062*$(x[1]) + 0.9764525500434011*$(x[2]) + 0.3990593176587991*$(x[3]))/(1 + exp(-(-0.9543429273497122 + 0.7406334788256062*$(x[1]) + 0.9764525500434011*$(x[2]) + 0.3990593176587991*$(x[3])))) + (-0.7459035549882156 + 0.4045099462664137*$(x[1]) + 0.9504581535443215*$(x[2]) + -0.1195761049288735*$(x[3]))/(1 + exp(-(-0.7459035549882156 + 0.4045099462664137*$(x[1]) + 0.9504581535443215*$(x[2]) + -0.1195761049288735*$(x[3])))) - $q <= 0.0))

                     @objective(m, Min, q)

                     return m


using Turing
using Test
using Random
Random.seed!(125)

x = [1.5 2.0]

@model mhtest(x) = begin
  s ~ InverseGamma(2,3)
  m ~ Normal(0,sqrt(s))
  for i in 1:length(x)
    x[i] ~ Normal(m, sqrt(s))
  end
  s, m
end

# MH with prior as proposal
alg = MH(2000)
chain = sample(mhtest(x), alg)
@test mean(chain[:s]) ≈ 49/24 atol=0.1
@test mean(chain[:m]) ≈ 7/6 atol=0.1

# MH with Gaussian proposal
GKernel(var) = (x) -> Normal(x, sqrt.(var))
alg = MH(5000, (:s, GKernel(5)), (:m, GKernel(1.0)))
chain = sample(mhtest(x), alg)
@test mean(chain[:s]) ≈ 49/24 atol=0.2
@test mean(chain[:m]) ≈ 7/6 atol=0.1

# MH within Gibbs
alg = Gibbs(1000, MH(5, :m), MH(5, :s))
chain = sample(mhtest(x), alg)
@test mean(chain[:s]) ≈ 49/24 atol=0.1
@test mean(chain[:m]) ≈ 7/6 atol=0.1


# This file is part of IntegerSequences.
# Copyright Peter Luschny. License is MIT.

(@__DIR__) ∉ LOAD_PATH && push!(LOAD_PATH, (@__DIR__))

module BellNumbers
using Nemo, Triangles, Products, NumberTheory, SeqUtils

export ModuleBellNumbers
export BellTrans, BellTriangle, BellNumberList, BellNumber
export V000110, L000110
export T137452, T264428, T137513, T104556, T001497, T132062, T039683, T203412
export T004747, T051141, T265606, T119274, T000369, T051142

"""

The Bell transform transforms an integer sequence into an integer triangle; also known as incomplete Bell polynomials. Let ``X`` be an integer sequence, then

 ``B_{n, k}(X) = \\sum_{m=1}^{n-k+1} \\binomial{n-1}{m-1} X[m] B_{n-m,k-1}(X)``

 where ``B_{0,0} = 1, B_{n,0} = 0`` for ``n≥1, B_{0,k} = 0`` for ``k≥1``.

The Bell transform is (0,0)-based and the associated triangle always has as first column 1,0,0,0,... This column is often missing in the OEIS. Other Stirling number related sequences are implemented in the module StirlingLahNumbers.

* BellTrans, BellTriangle, BellNumberList, BellNumber
* V000110, L000110, T137452, T264428, T137513, T104556, T001497, T132062, T039683, T203412, T004747, T051141, T265606, T119274, T000369, T051142
"""
const ModuleBellNumbers = ""

"""

Return a list of the first m Bell numbers (a.k.a. exponential numbers).
"""
function BellNumberList(m::Int)
    m == 0 && return fmpz[]
    R = ZArray(m)
    R[1] = 1
    m == 1 && return R
    R[2] = 1
    m == 2 && return R

    A = ZArray(m)
    A[1] = fmpz(1)
    for n ∈ 2:m-1
        A[n] = A[1]
        for k ∈ n:-1:2
            A[k-1] += A[k]
        end
        R[n+1] = A[1]
    end
    R
end

# --- much slower:
#function BellNumberList(m::Int)
#    m == 0 && return fmpz[]
#    SeqArray(m, n -> Nemo.bell(n))
#end

"""

Return the n-th Bell number. Bell numbers count the ways to partition a set of ``n`` labeled elements.

```
julia> BellNumber(10)
115975
```
"""
BellNumber(n::Int) = Nemo.bell(n)

"""

Return the n-th Bell number ``B_n``.

```
julia> V000110(11)
678570
```
"""
V000110(n::Int) = Nemo.bell(n)

"""

Return a list of Bell numbers of length len.

```
julia> L000110(10)
[1, 1, 2, 5, 15, 52, 203, 877, 4140, 21147]
```
"""
L000110(len::Int) = BellNumberList(len)

"""

The Bell transform transforms an integer sequence into an integer triangle; also known as incomplete Bell polynomials. Let ``X`` be an integer sequence, then
``B_{n,k}(X) = \\sum_{m=1}^{n-k+1} \\binomial{n-1}{m-1} X[m] B_{n-m,k-1}(X)``
where ``B_{0,0} = 1, B_{n,0} = 0`` for ``n≥1, B_{0,k} = 0`` for ``k≥1``.
"""
function BellTrans(n::Int, k::Int, X::Array)
    global CacheBellA
    haskey(CacheBellA, (n, k, X)) && return CacheBellA[(n, k, X)]

    a, s = fmpz(1), fmpz(0)
    ((n == 0) && (k == 0)) && return a
    ((n == 0) || (k == 0)) && return s

    for m ∈ 1:n-k+1
        s += a * BellTrans(n - m, k - 1, X) * X[m]
        a = div(a * (n - m), m)
    end

    CacheBellA[(n, k, X)] = s
end

const CacheBellA = Dict{Tuple{Int,Int,Array},fmpz}()

"""

The Bell transform transforms an integer sequence into an integer triangle; also known as incomplete Bell polynomials. Let ``F`` be an integer sequence generating function, then

``B_{n,k}(F) = \\sum_{m=1}^{n-k+1} \\binomial{n-1}{m-1} F(m) B_{n-m,k-1}(F)``

where ``B_{0,0} = 1, B_{n,0} = 0`` for ``n≥1, B_{0,k} = 0`` for ``k≥1``.
"""
function BellTrans(n::Int, k::Int, F::Function)
    global CacheBellF
    haskey(CacheBellF, (n, k, F)) && return CacheBellF[(n, k, F)]

    a, s = fmpz(1), fmpz(0)
    ((n == 0) && (k == 0)) && return a
    ((n == 0) || (k == 0)) && return s

    for m ∈ 1:n-k+1
        s += a * BellTrans(n - m, k - 1, F) * F(m - 1)
        a = div(a * (n - m), m)
    end

    CacheBellF[(n, k, F)] = s
end

const CacheBellF = Dict{Tuple{Int,Int,Function},fmpz}()

"""

The Bell triangle gathers the results of the Bell transform applied to the initial segments of the input sequence.

Famously the sequence (1,1,1,...) is mapped to the triangle of the Stirling set numbers.

```
julia> ShowAsΔ(BellTriangle(5, k -> 1))
1
0 1
0 1 1
0 1 3 1
0 1 7 6 1
```
"""
function BellTriangle(n::Int, seq)
    M = ZTriangle(n)
    i = 1

    for j ∈ 0:n-1, k ∈ 0:j
        M[i] = BellTrans(j, k, seq)
        i += 1
    end
    return M
end

"""

Return the coefficients of the first ``n`` Abel polynomials.

```
julia> ShowAsΔ(T137452(5))
1
0 1
0 -2 1
0 9 -6 1
0 -64 48 -12 1
```
"""
T137452(n::Int) = BellTriangle(n, k -> (-k - 1)^k)

"""

Return the Bell transform of the Bell numbers.

```
julia> ShowAsΔ(T264428(5))
1
0 1
0 1 1
0 2 3 1
0 5 11 6 1
```
"""
T264428(n::Int) = BellTriangle(n, BellNumber)

# --- Keep the comments below.
# Stirling number related sequences are now implemented in the module StirlingLah.
# doc"""
# Return the triangle of Stirling set numbers (a.k.a. Stirling numbers of 2nd kind).
# """
# StirlingSetTriangle(n::Int) = BellTriangle(n, k -> 1)
# T048993(n::Int) = StirlingSetTriangle(n)

"""

Return the triangle of the coefficients of the Mittag-Leffler polynomials.

```
julia> ShowAsΔ(T137513(5))
1
0 2
0 0 4
0 4 0 8
0 0 32 0 16
```
"""
T137513(n::Int) = BellTriangle(n, k -> isOdd(k) ? 0 : 2*factorial(k))

"""

Return the matrix inverse of coefficients of Bessel polynomials; essentially the same as coefficients of modified Hermite polynomials T096713.

```
julia> ShowAsΔ(T104556(5))
1
0 1
0 -1 1
0 0 -3 1
0 0 3 -6 1
```
"""
T104556(n::Int) = BellTriangle(n, k -> k < 2 ? (-1)^k : 0)

"""

Return a triangle of coefficients of Bessel polynomials (better use A132062).
"""
T001497(n::Int) = BellTriangle(n, MultiFactorial(2, 1))

# Stirling number related sequences are now implemented in the module StirlingLah.
# Keep the comments below.
# doc"""
# Return the triangle of unsigned Stirling numbers of the first kind (a.k.a.
# Stirling cycle numbers).
# """
# StirlingCycleTriangle(n::Int) = BellTriangle(n, MultiFactorial(1,1))
# T132393(n::Int) = StirlingCycleTriangle(n)

"""

Return the triangle of coefficients of Bessel polynomials, also the Sheffer triangle ``(1, 1 - √(1 - 2x))`` (Cf. A001497).

```
julia> ShowAsΔ(T132062(5))
1
0 1
0 1 1
0 3 3 1
0 15 15 6 1
```
"""
T132062(n::Int) = BellTriangle(n, MultiFactorial(2, 1))

"""

Return the signed double Pochhammer triangle: expansion of ``x(x-2)(x-4)..(x-2n+2)``.

```
julia> ShowAsΔ(T039683(5))
1
0 1
0 2 1
0 8 6 1
0 48 44 12 1
```
"""
T039683(n::Int) = BellTriangle(n, MultiFactorial(2, 2))

"""

Return the Bell transform of the MultiFactorial numbers of type (3,1).

```
julia> ShowAsΔ(T203412(5))
1
0 1
0 1 1
0 4 3 1
0 28 19 6 1
```
"""
T203412(n::Int) = BellTriangle(n, MultiFactorial(3, 1))

"""

Return the Bell transform of the MultiFactorial numbers of type (3,2).

```
julia> ShowAsΔ(T004747(5))
1
0 1
0 2 1
0 10 6 1
0 80 52 12 1
```
"""
T004747(n::Int) = BellTriangle(n, MultiFactorial(3, 2))

"""

Return the triangle ``3^{n-m}S1(n, m)`` where S1 are the signed Stirling numbers of first kind.

```
julia> ShowAsΔ(T051141(5))
1
0 1
0 3 1
0 18 9 1
0 162 99 18 1
```
"""
T051141(n::Int) = BellTriangle(n, MultiFactorial(3, 3))

"""

Return the Bell transform of the quartic factorial numbers.

```
julia> ShowAsΔ(T265606(5))
1
0 1
0 1 1
0 5 3 1
0 45 23 6 1
```
"""
T265606(n::Int) = BellTriangle(n, MultiFactorial(4, 1))

"""

Return the triangle of coefficients of numerators in Pade approximation to ``e^x``.

```
julia> ShowAsΔ(T119274(5))
1
0 1
0 2 1
0 12 6 1
0 120 60 12 1
```
"""
T119274(n::Int) = BellTriangle(n, MultiFactorial(4, 2))

"""

Return the Bell transform of the MultiFactorial numbers of type (4,3).

```
julia> ShowAsΔ(T000369(5))
1
0 1
0 3 1
0 21 9 1
0 231 111 18 1
```
"""
T000369(n::Int) = BellTriangle(n, MultiFactorial(4, 3))

"""

Return the Bell transform of the MultiFactorial numbers of type (4,4).

```
julia> ShowAsΔ(T051142(5))
1
0 1
0 4 1
0 32 12 1
0 384 176 24 1
```
"""
T051142(n::Int) = BellTriangle(n, MultiFactorial(4, 4))

#START-TEST-########################################################

using Test, SeqTests

function test()

    @testset "Bell" begin
        seq = fmpz[1, 1, 2, 5, 15, 52]
        a = [BellTrans(6, k, seq) for k ∈ 0:5]
        b = [0, 52, 205, 210, 85, 15]
        @test all(a .== b)

        seq = [1, 1, 1, 1, 1, 1]
        a = BellTriangle(6, seq)
        b = fmpz[1, 0, 1, 0, 1, 1, 0, 1, 3, 1, 0, 1, 7, 6, 1, 0, 1, 15, 25]
        @test all(a[1:5] .== b[1:5])

        a = fmpz[1, 1, 2, 5, 15, 52, 203]
        b = [BellNumber(n) for n ∈ 0:6]
        @test all(a .== b)

        if data_installed()
            SeqTest(V000110, 'V')
            SeqTest([L000110], 'L')

            T = [T264428, T137452, T132062, T265606]
            SeqTest(T, 'T')

            # These triangles have an additional column 1,0,0,... on the left
            # or have different signs. But they are essentially the same.
            P = [T104556, T001497, T039683, T203412, T004747,
                 T051141, T119274, T000369, T051142, T137513]
            SeqTest(P, 'P')
        end
    end
end

function demo()
    seq = [1, 1, 2, 5, 15, 52]
    len = size(seq)[1]

    for n ∈ 0:len
        println([BellTrans(n, k, seq) for k ∈ 0:n])
    end

    M = BellTriangle(len, seq)
    ShowAsΔ(M)

    for n ∈ 0:6
        println(n, " -> ", BellNumberList(n))
    end

    T = T137452(8)
    ShowAsΔ(T)
end

"""

BellNumberList(1000) :: 0.539088 seconds (855.00 k allocations: 13.062 MiB)
BellTriangle(100, [1 for _ ∈ 1:100]) :: 0.576952 seconds (1.22 M allocations: 24.571 MB, 14.23% gc time)
"""
function perf()
    GC.gc()
    BellNumberList(5)
    @time BellNumberList(1000)
    @time BellTriangle(100, [1 for _ ∈ 1:100])
end

function main()
    test()
    demo()
    perf()
end

main()

end # module


using Manifolds
using Test
using LinearAlgebra

@testset "Testing @defVariable" begin
##

struct NotAManifold end

@test_throws AssertionError  @defVariable(MyVar, NotAManifold(), zeros(3,3))

##

@defVariable(TestVarType1, Euclidean(3), zeros(3))
@defVariable(TestVarType2, SpecialEuclidean(3), ProductRepr(zeros(3), diagm(ones(3))))

##

@test getManifold( TestVarType1) == Euclidean(3)
@test getManifold( TestVarType2) == SpecialEuclidean(3)

@test getDimension(TestVarType1) === 3
@test getDimension(TestVarType2) === 6

@test getPointType(TestVarType1) == Vector{Float64}
@test getPointType(TestVarType2) == ProductRepr{Tuple{Vector{Float64}, Matrix{Float64}}}

@test getPointIdentity(TestVarType1) == zeros(3)
@test getPointIdentity(TestVarType2).parts[1] == ProductRepr(zeros(3), diagm(ones(3))).parts[1]
@test getPointIdentity(TestVarType2).parts[2] == ProductRepr(zeros(3), diagm(ones(3))).parts[2]

##

@test getManifold( TestVarType1()) == Euclidean(3)
@test getManifold( TestVarType2()) == SpecialEuclidean(3)

@test getDimension(TestVarType1()) === 3
@test getDimension(TestVarType2()) === 6

@test getPointType(TestVarType1()) == Vector{Float64}
@test getPointType(TestVarType2()) == ProductRepr{Tuple{Vector{Float64}, Matrix{Float64}}}

@test getPointIdentity(TestVarType1()) == zeros(3)
@test getPointIdentity(TestVarType2()).parts[1] == ProductRepr(zeros(3), diagm(ones(3))).parts[1]
@test getPointIdentity(TestVarType2()).parts[2] == ProductRepr(zeros(3), diagm(ones(3))).parts[2]

end


N = 10
D1 = speye(N-1,N) - spdiagm(ones(N-1),1,N-1,N)
D = [ kron(D1, speye(N)); kron(speye(N), D1) ]
i, j = N*1 + 2, N*7+7
b = zeros(N^2); b[i], b[j] = 1, -1
v = (D' * D) \ b
v[i] - v[j]


using CompScienceMeshes, BEAST

o, x, y, z = euclidianbasis(3)

Γ = meshsphere(1.0, 0.11)
X = lagrangec0d1(Γ)
@show numfunctions(X)

κ = 1.0; γ = im*κ
a = -1Helmholtz3D.hypersingular(gamma=γ)
b = Helmholtz3D.doublelayer(gamma=γ) - 0.5Identity()

uⁱ = Helmholtz3D.planewave(wavenumber=κ, direction=z)
f = strace(uⁱ,Γ)
g = ∂n(uⁱ)

@hilbertspace u
@hilbertspace v
eq1 = @discretise a[v,u] == g[v] u∈X v∈X
eq2 = @discretise b[v,u] == f[v] u∈X v∈X

x1 = solve(eq1)
x2 = solve(eq2)

fcr1, geo1 = facecurrents(x1, X)
fcr2, geo2 = facecurrents(x2, X)

include(Pkg.dir("CompScienceMeshes","examples","plotlyjs_patches.jl"))
p1 = patch(geo1, real.(norm.(fcr1)))
p2 = patch(geo2, real.(norm.(fcr2)))


gauss_lowpass(x, λc)  = exp(-π*((√(log(2)/π)*λc)/x)^2)
gauss_highpass(x, λc) = 1-gauss_lowpass(x, λc)

"""
    separatefeatures(frequencies, rate; filterwavelengths=[2.5, 250, 2500])

Filter frequencies for roughness-, waviness- and form-components. `rate`(points/mm) is needed to convert `filterwavelengths`(μm) to corresponding filter bins.
"""
function separatefeatures(profile::SurfaceProfile; filterwavelengths=[profile.λs, profile.λc, profile.λf])
    frequencies = dct(profile.data)
    bins = length(frequencies)
    N = 1 / profile.resolution / 2 # maximal spatial frequency (points/mm)
    filterbins = 1 ./ (filterwavelengths*1e-3) / (N/bins)

    weights(b1, b2) = gauss_highpass.(1:bins, b1) .* gauss_lowpass.(1:bins, b2)

    res = Array{Array{Float64, 1}, 1}(length(filterwavelengths))
    for i in 1:length(filterwavelengths)
        b1 = filterbins[i]
        b2 = i == length(filterwavelengths) ? 0 : filterbins[i+1]
        res[i] = frequencies .* weights(b1, b2)
    end
    return res
end


module NetworkResilience

#Library packages
using LinearAlgebra
using Statistics
using Distributed
using Distributions
using DifferentialEquations

#Local files
include("netconstructors.jl")
include("cascadedrivers.jl")
include("networkalgorithms.jl")
include("swing.jl")

#Utilities to export
export NetworkRecorder, Edge, Node, Graph, addnode!, addedge!, numedges, numnodes, NetStore
export incidencematrix, getdegrees, sourcesinkvector, smallworldnetwork, sourcesinklocs
export connectedcomponents, adjacencymatrix, weightedlaplacianmatrix
export nodevoltages, edgecurrents, testnetwork, fracture!
export newrecorder, cascaderesults, smallworldcascade, smallworldensemble, smallworldalphas
export smcascadefixedtop, smcascadeclock, sourcessinkclockface, cascadesequence
export singlecascade, smcascadedist, cascadebisection, sequencedstartbisection
export noisysourcesinkvector, partitionadjacency, avcompontentsize, clustersize
export sourcesinkwellmixed, sourcesinkhalfhalf, austriannetwork
export triggerhist, cascadebisectionrandomtrigger
export edgepower, fswing, fsteadystate, mainjac, centralityvstransient, fracture2!
export smcascadeclock2, cascadebisection2, fracture3!, simplecascade, NetStore2
export swingfracture!, swingcascadevalpha, makegammasourcesinkvector
export fracturerand!, cascadebisection3rand, smcascadeclockrand, swingcascadebisection
export CascadeNetworkSwing, criticalcouplingbisection, swingcascadeandtvalpha, svt
export swingcascadeouterbisection, swingcascadebreakdownvsalpha

end


module SVM

using StatsBase
import StatsBase: predict

export svm, cddual, pegasos, predict

type SVMFit
    w::Vector{Float64}
    pass::Int
    converged::Bool
end

function Base.show(io::IO, fit::SVMFit)
    @printf(io, "Fitted linear SVM\n")
    @printf(io, " * Non-zero weights: %d\n", countnz(fit.w))
    @printf(io, " * Iterations: %d\n", fit.pass)
    @printf(io, " * Converged: %s\n", string(fit.converged))
end

function predict{T<:Real}(fit::SVMFit, X::AbstractMatrix{T})
    n, l = size(X)
    preds = Array{Float64,1}(l)
    for i in 1:l
        tmp = 0.0
        for j in 1:n
            tmp += fit.w[j] * X[j, i]
        end
        preds[i] = sign(tmp)
    end
    return preds
end

include("pegasos.jl")
include("cddual.jl")

function svm{T<:Real}(X::AbstractMatrix{T},
                      Y::AbstractVector{T};
                      k::Integer = 5,
                      lambda::Real = 0.1,
                      maxpasses::Integer = 100)
    pegasos(X, Y, k = k, lambda = lambda, maxpasses = maxpasses)
end

end # module


function fRhoBGrd(x,Param)
if lower(Param.ProfRhoBGrd)== "baldaufcart"
    delta=Param.Grav/(Param.Rd*Param.T0);
    p=Param.p0*exp(-delta*x[3]);
    TLoc=Param.T0;
    Rho=p/(Param.Rd*TLoc);
else
    Rho=0;
end
return Rho
end


rng = StableRNG(666899)

@testset "built-in regressor measures" begin
    y    = [1, 2, 3, 4]
    yhat = [4, 3, 2, 1]
    w = [1, 2, 4, 3]
    @test isapprox(mae(yhat, y), 2)
    @test isapprox(mae(yhat, y, w), (1*3 + 2*1 + 4*1 + 3*3)/4)
    @test isapprox(rms(yhat, y), sqrt(5))
    @test isapprox(rms(yhat, y, w), sqrt((1*3^2 + 2*1^2 + 4*1^2 + 3*3^2)/4))
    @test isapprox(mean(l1(yhat, y)), 2)
    @test isapprox(mean(l1(yhat, y, w)), mae(yhat, y, w))
    @test isapprox(mean(l2(yhat, y)), 5)
    @test isapprox(mean(l2(yhat, y, w)), rms(yhat, y, w)^2)
    @test isapprox(mean(log_cosh(yhat, y)), 1.3715546675)

    yhat = y .+ 1
    @test isapprox(rmsl(yhat, y),
                   sqrt((log(1/2)^2 + log(2/3)^2 + log(3/4)^2 + log(4/5)^2)/4))
    @test isapprox(rmslp1(yhat, y),
                   sqrt((log(2/3)^2 + log(3/4)^2 + log(4/5)^2 + log(5/6)^2)/4))
    @test isapprox(rmsp(yhat, y), sqrt((1 + 1/4 + 1/9 + 1/16)/4))
    @test isapprox(mape(yhat, y), (1/1 + 1/2 + 1/3 + 1/4)/4)
end

@testset "MLJBase.value" begin
    yhat = randn(rng,5)
    X = (weight=randn(rng,5), x1 = randn(rng,5))
    y = randn(rng,5)
    w = randn(rng,5)

    @test MLJBase.value(mae, yhat, nothing, y, nothing) ≈ mae(yhat, y)
    @test MLJBase.value(mae, yhat, nothing, y, w) ≈ mae(yhat, y, w)

    spooky(yhat, y) = abs.(yhat - y) |> mean
    @test MLJBase.value(spooky, yhat, nothing, y, nothing) ≈ mae(yhat, y)

    cool(yhat, y, w) = abs.(yhat - y) .* w |> mean
    MLJBase.supports_weights(::Type{typeof(cool)}) = true
    @test MLJBase.value(cool, yhat, nothing, y, w) ≈ mae(yhat, y, w)

    funky(yhat, X, y) = X.weight .* abs.(yhat - y) |> mean
    MLJBase.is_feature_dependent(::Type{typeof(funky)}) = true
    @test MLJBase.value(funky, yhat, X, y, nothing) ≈ mae(yhat, y, X.weight)

    weird(yhat, X, y, w) = w .* X.weight .* abs.(yhat - y) |> mean
    MLJBase.is_feature_dependent(::Type{typeof(weird)}) = true
    MLJBase.supports_weights(::Type{typeof(weird)}) = true
    @test MLJBase.value(weird, yhat, X, y, w) ≈ mae(yhat, y, X.weight .* w)
end