function DiffusionDefinition.diffusion_message(::Val{:LotkaVolterra})
    println("* * * * * * * * * * *")
    println("Lotka–Volterra system")
    println("* * * * * * * * * * *")
    println("Description")
    println("------------")
    println("A simple, scalar-valued predator-prey model.")
    println("It is represented by a two-dimensional process (Xₜ, Yₜ).")
    println("dXₜ = (αXₜ - βXₜYₜ)dt + σ₁dW¹ₜ,")
    println("dYₜ = (δXₜYₜ - γYₜ)dt + σ₂dW²ₜ.")
    println("The model has six parameters:")
    println("\tα, β, γ, δ, σ1, σ2")
    println("\nConstructors")
    println("------------")
    println("You may initialize the process either by passing the parameters")
    println("in the order above, as in:")
    println("α, β, γ, δ, σ1, σ2 = ...")
    println("P = LotkaVolterra(α, β, γ, δ, σ1, σ2)")
    println("or by keyword arguments")
    println("p = (α=..., β=..., γ=..., δ=..., σ1=..., σ2=...)")
    println("P = LotkaVolterra(;p...)")
    println()
end

@diffusion_process LotkaVolterra{K} begin
    :dimensions
    process --> 2
    wiener --> 2

    :parameters
    (α, β, γ, δ, σ1, σ2) --> K

    #=
    :conjugate
    phi(t, x) --> (
        (0.0, 0.0),
        (x[1], 0.0),
        (-x[1]*x[2], 0.0),
        (0.0, x[1]*x[2]),
        (0.0, -x[2]),
        (0.0, 0.0),
        (0.0, 0.0)
    )
    nonhypo(x) --> x
    num_non_hypo --> 2
    =#

    :additional
    constdiff --> true
    statespace --> LowerBoundedStateSpace((1,2),(0.0, 0.0))
end

function DiffusionDefinition.b(t, x, P::LotkaVolterra)
    @SVector [
        P.α*x[1] - P.β*x[1]*x[2],
        P.δ*x[1]*x[2] - P.γ*x[2],
    ]
end

DiffusionDefinition.σ(t, x, P::LotkaVolterra) = SDiagonal(P.σ1, P.σ2)


using LOWDER

fmin = Array{Function}(undef, 11)

for i = 1:11

    y = i + 14.0

    fmin[i] = x -> 2.0 * x[1] ^ 2.0 + 0.008 * y ^ 3.0 - 3.2 * y * x[1] - 2.0 * y

end

n_points = 2
l = [3.0]
u = [5.5]
xinit = [0.0]

sol = LOWDER.lowder( fmin, xinit, l, u; m = n_points )


# Code written entirely by Eric Alcaide.

# An implementation of Informed search methods algorithms
# in julia for a future package: SearchMethods.jl

""" Greedy Best-First Search method algorithm. Expands node that appears to
    be closest to goal.
    Inputs:
        * root: the initial node. Any type.
        * tree: the search space. Any type.
        * goal_check: checks if the goal is met. Function.
        * expand: returns an array of new nodes. Function.
        * heuristic: returns the estimated cost to goal from a node. Function.
    Returns:
        * the goal if it's found in tree.
        * false (boolean value) otherwise.
"""
function greedy(root, tree, goal_check, expand, heuristic)
	explored = Set([])
	# Keep track of node, depth while node in frontier
    frontier = PriorityQueue()
    enqueue!(frontier, root, 0)
    # While frontier is active, pop least cost node.
    while !isempty(frontier)
    	# Peek least cost value and cost. Eliminate from frontier.
    	node, cost_to_goal = map(Any, peek(frontier))
    	dequeue!(frontier)
    	# Check if goal found. Else => continue.
    	if goal_check(node) return node end
    	# Explore node
    	for neighbor in expand(node, tree)
    		if !in(neighbor, union(explored, keys(frontier)))
    			enqueue!(frontier, neighbor, heuristic(node,neighbor))
    		end
    	end
    end
    # Return false if goal not found.
    return false
end

""" A* Search method algorithm. Expands always the shallowest node.
    Inputs:
        * root: the initial node. Any type.
        * tree: the search space. Any type.
        * goal_check: checks if the goal is met. Function.
        * expand: returns an array of new nodes. Function.
        * cost: returns the cost of expanding a node. Function.
        * heuristic: returns the estimated cost to goal from a node. Function.
    Returns:
        * the goal if it's found in tree.
        * false (boolean value) otherwise.
"""
function a_star(root, tree, goal_check, expand, cost, heuristic)
	explored = Set([])
	# Keep track of node, depth while node in frontier
    frontier = PriorityQueue()
    enqueue!(frontier, root, 0)
    # While frontier is active, pop least cost node.
    while !isempty(frontier)
    	# Peek least cost value and cost. Eliminate from frontier.
    	node, cost_to_goal = map(Any, peek(frontier))
    	dequeue!(frontier)
    	# Check if goal found. Else => continue.
    	if goal_check(node) return node end
    	# Explore node
    	for neighbor in expand(node, tree)
    		if !in(neighbor, union(explored, keys(frontier)))
    			enqueue!(frontier, neighbor, cost(node, neighbor)+heuristic(node,neighbor))
    		end
    	end
    end
    # Return false if goal not found.
    return false
end


#=
Created on Saturday 22 Feb 2020
Last update: -

@author: Michiel Stock
michielfmstock@gmail.com

Example plots convergence unconstrained convex optimization.
=#

using STMO
using Plots, LaTeXStrings
using LinearAlgebra
using STMO.TestFuns
using STMO.Unconstrained

# starting point
x0q = [9.0, 2.0];
x0nq = [-1.0, 0.75];

# GRADIENT DESCENT
trackerqgd = PathTrack(x0q);
xstarq = Unconstrained.gradient_descent(fquadr, copy(x0q), grad_fquadr, tracker=trackerqgd);

trackernqgd = PathTrack(x0nq)
xstarnq = gradient_descent(fnonquadr, copy(x0nq), grad_fnonquadr,
                tracker=trackernqgd);

# COORDINATE DESCENT

trackerqcd = PathTrack(x0q);
xstarq = Unconstrained.coordinate_descent(fquadr, copy(x0q), grad_fquadr,
                tracker=trackerqcd);

trackernqcd = PathTrack(x0nq);
xstarq = Unconstrained.coordinate_descent(fnonquadr, copy(x0nq), grad_fnonquadr,
            tracker=trackernqcd);

# NEWTON

trackerqnm = PathTrack(x0q)
xstarq = Unconstrained.newtons_method(fquadr, copy(x0q), grad_fquadr, hess_fquadr, tracker=trackerqnm)

trackernqnm = PathTrack(x0nq)
xstarq = Unconstrained.newtons_method(fnonquadr, copy(x0nq), grad_fnonquadr, hess_fnonquadr, tracker=trackernqnm)

# PLOTTING

pq = contour(-10:0.1:10, -5:0.1:5, (x1, x2) -> fquadr((x1, x2)), xlabel="\$ x_1 \$",
                ylabel="\$ x_2 \$", title="quadratic", fill=false)
path!(trackerqgd, label="GD")
path!(trackerqcd, label="CD", color=myblue)
path!(trackerqnm, label="Newton", color=mygreen)

savefig(pq, "figures/path_quadr.png")

pnq = contour(-2:0.1:2, -1:0.1:1, (x1, x2) -> fnonquadr((x1, x2)), xlabel="\$ x_1 \$",
                ylabel="\$ x_2 \$", title="non-quadratic", fill=false)
path!(trackernqgd, label="GD")
path!(trackernqcd, label="CD", color=myblue)
path!(trackernqnm, label="Newton", color=mygreen)
savefig(pnq, "figures/path_nonquadr.png")

# convergence

fnqexact = newtons_method(fnonquadr, copy(x0nq), grad_fnonquadr, hess_fnonquadr, ϵ=1e-12) |> fnonquadr
nonquadrerr = x -> abs(fnonquadr(x) .- fnqexact);

p1 = plotobj(fquadr, trackerqgd, label="GD", yscale=:log10, ylabel="\$ f(x) - f(x^\\star)\$", title="quadratic", ylim=(1e-15, 10));
plotobj!(fquadr, trackerqcd, color=myblue, label="CD");
plotobj!(fquadr, trackerqnm, color=mygreen, label="Newton");
p2 = plotobj(nonquadrerr, trackernqgd, label="GD", yscale=:log10, ylabel="\$ f(x) - f(x^\\star)\$", title="non-quadratic", ylim=(1e-15, 10));
plotobj!(nonquadrerr, trackernqcd, color=myblue, label="CD");
plotobj!(fquadr, trackernqnm, color=mygreen, label="Newton");
pconv = plot(p1, p2);
savefig(pconv, "figures/unconstrained_conv.png")


import Statistics: quantile

function seq_along(x)
    1:length(x[:])
end

function seq(from, to, by = 1)
    from:by:to
end

function Range(x::AbstractArray{<:Real})
    minimum(x), maximum(x)
end

function meshgrid(x::AbstractArray{T,1}, y::AbstractArray{T,1}) where T <: Real
    X = x .* ones(1, length(y))
    Y = ones(length(x)) .* y'
    X, Y
end

function r_summary(x::AbstractArray; digits=2)
    probs = [0, 0.25, 0.5, 0.75, 1]
    x2 = filter(!isnan, x)
    if (length(x2) == 0)
        printstyled("empty array"; color=:red)
        return
    end

    n_nan = length(x) - length(x2)
    # u = mean(x2);
    r = quantile(x2, probs)
    insert!(r, 4, mean(x2))
    r = round.(r, digits=digits)
    # @show r
    printstyled("Min\t 1st.Qu\t Median\t Mean\t 3rd.Qu\t Max\t NA's\n"; color=:blue)
    printstyled("$(r[1])\t $(r[2])\t $(r[3])\t $(r[4])\t $(r[5])\t $(r[6])\t $(n_nan)"; color=:blue)
    nothing
end

cbind  = hcat
rbind  = vcat
# cbind! = hcat!
# rbind! = vcat!
# hcat! not defined

export cbind, rbind,
    r_summary,
    seq_along, seq, Range, aggregate


sample = "00100
11110
10110
10111
10101
01111
00111
11100
10000
11001
00010
01010"

function flip(bin)
    s = ""
    for i in bin
        if i == '1'
            s = s * "0"
        else
            s = s * "1"
        end
    end
    return s
end

function sol1(sample)
    bit = ""
    for i in 1:sizeof(sample[1])
        most = ""

        for j in 1:size(sample)[1]
            most = most * sample[j][i]
        end

        if count(i->(i=='0'), most) >= size(sample)[1]/2
            bit = bit * "0"
        else
            bit = bit * "1"
        end
    end

    return parse(Int, bit, base=2) * parse(Int, flip(bit), base=2)
end

println("Part 1: ", sol1(split(sample, "\n")))


module VizEKP

using Plots
using JLD
using LinearAlgebra
using Statistics
using BoundingSphere

export ekp_sphere_evol
export plot_bounding_radius
export plot_ekp_params
export plot_error_evolution
export plot_covmat
export plot_output_profiles

"""
    function ekp_sphere_evol(ekp_u::Array{Array{Float64,2},1})

Returns the evolution with EKP iteration of the BoundingSphere
parameters (center, radius) for the EK particle ensemble.
"""
function ekp_sphere_evol(ekp_u::Array{Array{Float64,2},1})
    ekp_center = zeros((length(ekp_u), length(ekp_u[1][1,:])))
    ekp_radius = zeros(length(ekp_u))
    ekp_center_jump = zeros(length(ekp_u))
    #Iterate through EKP stages
    for i in 1:length(ekp_u)
        ekp_center[i,:], ekp_radius[i] = boundingsphere(
            mapslices(x->[x], ekp_u[i], dims=2)[:])
        if i > 1
            ekp_center_jump[i] = sqrt( (ekp_center[i,:]-ekp_center[i-1,:])'*(
                ekp_center[i,:]-ekp_center[i-1,:]) )
        end
    end
    return ekp_radius, ekp_center_jump
end

"""
    function plot_bounding_radius(
        ekp_u::Union{Array{Array{Float64,2},1}, Array{Array{Array{Float64,2},1},1}};
        normalized::Bool=false,
        newplot::Bool=true,
        ylims=nothing,)

Plots the evolution of the BoundingSphere radius for one or several EKPs.
"""
function plot_bounding_radius(
    ekp_u::Union{Array{Array{Float64,2},1}, Array{Array{Array{Float64,2},1},1}};
    normalized::Bool=true,
    newplot::Bool=true,
    ylims=nothing,)

    if ekp_u isa Array{Array{Array{Float64,2},1},1}
        # Wrapper for kwarg broadcasting
        wrapper(ekp_u) = plot_bounding_radius(
                            ekp_u, normalized=normalized,
                            newplot=false, ylims=ylims)
        plot()
        wrapper.(ekp_u)
    else

        ekp_radius, _ = ekp_sphere_evol(ekp_u)
        if normalized
            ekp_radius = ekp_radius./ekp_radius[1]
        end
        if newplot
            plot(ekp_radius, lw=2)
        else
            plot!(ekp_radius, ylims=ylims, lw=2)
        end
        xlabel!("EKP iteration")
        ylabel!("Min sphere radius")
        savefig("ekp_radius.png")
    end
    return
end

"""
    function plot_ekp_params(ekp_u::Array{Array{Float64,2},1};
                         exp_transform::Bool=false,
                         param_names::Union{Vector{String}, Nothing}=nothing)

Plots the evolution of EK parameters with EKP iteration. The ensemble is represented by
the mean, min and max values of each parameter at each EKP iteration.

If exp_transform, the parameter values are exponentiated before plotting.
"""
function plot_ekp_params(
    ekp_u::Array{Array{Float64,2},1};
    exp_transform::Bool=false,
    param_names::Union{Vector{String}, Nothing}=nothing)

    if exp_transform
        ekp_u_ = deepcopy( map(x->exp.(x), ekp_u) )
    else
        ekp_u_ = ekp_u
    end
    #One-dimensional measures
    ekp_mean = zeros( (length(ekp_u_), length(ekp_u_[1][1,:])) )
    ekp_min =  zero(ekp_mean)
    ekp_max =  zero(ekp_mean)
    #Iterate through EKP stages
    for i in 1:length(ekp_u_)
        ekp_mean[i,:] = mean(ekp_u_[i], dims=1)
        ekp_min[i,:] = minimum(ekp_u_[i], dims=1)
        ekp_max[i,:] = maximum(ekp_u_[i], dims=1)
    end

    for j in 1:length(ekp_u_[1][1,:])
        plot(ekp_mean[:,j], ribbon=(
            -ekp_min[:,j].+ekp_mean[:,j], ekp_max[:,j].-ekp_mean[:,j]),
            label=string("parameter ", j), lw=2)
        xlabel!("EKP iteration")
        if !isnothing(param_names)
            ylabel!(param_names[j])
        end
        xlabel!("EKP iteration")
        savefig(string("evol_param_",j,".png"))
    end
    return
end

function plot_ekp_params(
    ekp_u::Array{Array{Array{Float64,2},1},1};
    exp_transform::Bool=false,
    param_names::Union{Vector{String}, Nothing}=nothing)

    ekp_u_ = hcat(ekp_u...)
    if exp_transform
        ekp_u_ = deepcopy( map(x->exp.(x), ekp_u_) )
    end

    #One-dimensional measures
    ekp_mean = zeros( (length(ekp_u_[:,1]), length(ekp_u_[1,1][1,:])) )
    ekp_min =  zero(ekp_mean)
    ekp_max =  zero(ekp_mean)
    for j in 1:length(ekp_u_[1,1][1,:]) # For each parameter
        plot()
        for k in 1:length(ekp_u_[1,:]) # For each simulation
            #Iterate through EKP stages
            for i in 1:length(ekp_u_[:,1]) #For each iteration
                ekp_mean[i,:] = mean(ekp_u_[i,k], dims=1)
                ekp_min[i,:] = minimum(ekp_u_[i,k], dims=1)
                ekp_max[i,:] = maximum(ekp_u_[i,k], dims=1)
            end

            plot!(ekp_mean[:,j], ribbon=(
                -ekp_min[:,j].+ekp_mean[:,j], ekp_max[:,j].-ekp_mean[:,j]),
                label=string("parameter ", j), lw=2)
        end
        if !isnothing(param_names)
            ylabel!(param_names[j])
        end
        xlabel!("EKP iteration")
        savefig(string("evol_param_",j,".png"))
    end
    return
end

"""
    function plot_error_evolution(ekp_err::Union{Vector{Float64}, Array{Vector{Float64},1} };
                              ekp_std_scale::Union{Float64, Array{Float64,1}}=1.0 )

Plots the observation covariance-weighted EK error for each iteration. If Arrays are provided,
the function returns a single plot with the errors from all EK processes provided.

ekp_std_scale defines a proportionality constant for the errors from each EK process.

"""
function plot_error_evolution(
    ekp_err::Union{Vector{Float64}, Array{Vector{Float64},1} };
    ekp_std_scale::Union{Float64, Array{Float64,1}}=1.0,
    newplot::Bool=true,
    ylims=nothing, plt_scale=:identity)

    if ekp_err isa Array{Vector{Float64},1}
        # Wrapper for kwarg broadcasting
        wrapper(ekp_err, ekp_std_scale) = plot_error_evolution(
                            ekp_err, ekp_std_scale=ekp_std_scale,
                            newplot=false, plt_scale=plt_scale,
                            ylims=ylims)
        plot()
        wrapper.(ekp_err, ekp_std_scale)
    else
        ekp_err_ = deepcopy(ekp_err.*(ekp_std_scale*ekp_std_scale))

        if newplot
            plot(ekp_err_, lw=2)
        else
            plot!(ekp_err_, ylims=ylims, scale = plt_scale, lw=2)
        end
        xlabel!("EKP iteration")
        ylabel!("Error")
        savefig("ekp_error.png")
    end
    return
end

"""
    function plot_covmat(cov::Array{Float64, 2};
                     figname::Union{String, Nothing}=nothing)

Plots the given covariance matrix as a heat map.

"""
function plot_covmat(
    cov::Array{Float64, 2};
    figname::Union{String, Nothing}=nothing)

    # Scale by diagonal elements
    heatmap(cov, clim=(0, minimum(diag(cov)) ), yflip=true)
    if !isnothing(figname)
        figname_ = string(figname, ".png")
    else
        figname_ = "covmat.png"
    end
    savefig(figname_)
    return
end

"""
    function plot_output_profiles(ekp_g::Array{Array{Float64,2},1},
                      true_g::Array{Float64,1},
                      num_var::Int64,
                      num_heights::Int64)

Plots the mean output from the last EK ensemble, as well as the mean of the true output,
for a model whose outputs are vertical profiles of quantities of interest.=, with the
same number of vertical levels.
"""
function plot_output_profiles(
    ekp_g::Array{Array{Float64,2},1},
    true_g::Array{Float64,1},
    num_var::Array{Int64,1},
    num_heights::Array{Int64,1})

    for sim in 1:length(num_var)
        h_r = range(0, 1, length=Integer(num_heights[sim]))
        for k in 1:num_var[sim]
            inf_lim = Integer(
                num_heights[sim]*(k-1) + (num_heights[1:sim-1]'*num_var[1:sim-1]))+1
            sup_lim = Integer( inf_lim + num_heights[sim] - 1)
            y_model_mean = mean(ekp_g[end][:,inf_lim:sup_lim],dims=1)[1,:]

            plot(true_g[inf_lim:sup_lim] , h_r, label="Truth", lw=2)
            plot!(y_model_mean, h_r, label="Model", lw=2, ls=:dash)
            ylabel!("Normalized height")
            savefig(string("sim_",sim,"_output_variable",k,".png"))
        end
    end
    return
end

end #module


using Random
using Plots
pyplot(size = (600,1100), legend = true)
"""

Arguments:
    - `f`: Function to be optimized
    - `g`: Gradient function for `f`
    - `c`: Constraints function. Evaluates all the constraint equations and return a Vector of values
    - `x0`: (Vector) Initial position to start from
    - `n`: (Int) Number of evaluations allowed. Remember `g` costs twice of `f`
    - `prob`: (String) Name of the problem. So you can use a different strategy for each problem
"""
function optimize(f, g, c, x0, n, prob)
    p = squared_sum_penalty_function(c)
    x_best, xlist,fpvalues = penalty_method(f, p, x0, n)
    return x_best
end

function squared_sum_penalty_function(c)
    f = function(x)
        ceval = c(x)
        sum(ceval[ceval .> 0].^2)
    end
    return f
end

function penalty_method(f, p, x, n; ρ=1, γ=2)
    numEvalsLeft = n
    hj_max_eval = 10;
    xlist = Array{Float64}[]
    fpvalues = Array{Float64}[]
    push!(xlist, x)
    while numEvalsLeft >= hj_max_eval
        x, evalsUsed, newFeasFound, fpvalues = hooke_jeeves(f, ρ, p, x, hj_max_eval, fpvalues)
        push!(xlist, x)
        numEvalsLeft = numEvalsLeft - evalsUsed
        # if !newFeasFound
        #     ρ /= γ
        # else
            ρ *= γ
        # end
    end
    return x,xlist,fpvalues
end

function last_feasible_x(currXFeas, x, feasFound, last_x_feas)
    if currXFeas
        return x
    else
        if !feasFound
            return x
        else
            return last_x_feas
        end
    end
end

basis(i, n) = [k == i ? 1.0 : 0.0 for k in 1 : n]

function hooke_jeeves(f, ρ, p, x, maxEval, fpvalues; α=0.2, γ=0.1)
    xf = f(x)
    xp = p(x)
    push!(fpvalues,[xf,xp])
    y, m = xf + ρ*xp, length(x)
    evalsUsed = 1
    last_x_feas = x
    newFeasFound = false
    if xp==0
        feasFound = true
        currXFeas = true
    else
        feasFound = false
        currXFeas = false
    end
    while evalsUsed < maxEval
        improved = false
        for i in 1 : m
            x′ = x + α*basis(i, m)
            if evalsUsed >= maxEval
                return last_feasible_x(currXFeas, x, feasFound, last_x_feas), evalsUsed, newFeasFound, fpvalues
            end
            xf′ = f(x′)
            xp′ = p(x′)
            push!(fpvalues,[xf′,xp′])
            y′ = xf′ + ρ*xp′
            evalsUsed = evalsUsed + 1
            if y′ < y
                x, y, improved = x′, y′, true
                if xp′==0
                    last_x_feas = x
                    newFeasFound = true
                    feasFound = true
                    currXFeas = true
                else
                    currXFeas = false
                end
            else
                x′ = x - α*basis(i, m)
                if evalsUsed >= maxEval
                    return last_feasible_x(currXFeas, x, feasFound, last_x_feas), evalsUsed, newFeasFound, fpvalues
                end
                xf′ = f(x′)
                xp′ = p(x′)
                push!(fpvalues,[xf′,xp′])
                y′ = xf′ + ρ*xp′
                evalsUsed = evalsUsed + 1
                if y′ < y
                    x, y, improved = x′, y′, true
                    if xp′==0
                        last_x_feas = x
                        newFeasFound = true
                        feasFound = true
                        currXFeas = true
                    else
                        currXFeas = false
                    end
                end
            end
        end
        if !improved
            α *= γ
        end
    end
    return last_x_feas, evalsUsed, newFeasFound, fpvalues
end

function grad(x::Vector)
    return 0
end

function simple1(x::Vector)
    return -x[1] * x[2]
end

function simple1_constraints(x::Vector)
    return [x[1] + x[2]^2 - 1,
            -x[1] - x[2]]
end

function simple1_init()
    return rand(2) * 2.0
end

function simple2(x::Vector)
    return (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2
end

function simple2_constraints(x::Vector)
    return [(x[1]-1)^3 - x[2] + 1,
            x[1] + x[2] - 2]
end

function simple2_init()
    return rand(2) .* 2.0 .- 1.0
end

function simple3(x::Vector)
    return x[1] - 2*x[2] + x[3]
end

function simple3_constraints(x::Vector)
    return [x[1]^2 + x[2]^2 + x[3]^2 - 1]
end

function simple3_init()
    b = 2.0 .* [1.0, -1.0, 0.0]
    a = -2.0 .* [1.0, -1.0, 0.0]
    return rand(3) .* (b-a) + a
end

function get_full_list(f, g, c, x0, n, prob)
    p = squared_sum_penalty_function(c)
    x_best, xlist, fpvalues = penalty_method(f, p, x0, n)
    return x_best, xlist, fpvalues
end

function create_path_plot(problemNumber, fn, constr, numConstr, lvls, initial_points, true_min, xMin, xMax, yMin, yMax, lineColors, contourColors)
    pyplot(size = (600,1000), legend = true)
    # Plot function contours
    x = xMin:0.1:xMax
    y = yMin:0.1:yMax
    f(x, y) = begin
            fn([x,y])
        end
    p1 = contour(x, y, f, fill=false, seriescolor=:grays,w=3, label="function")

    # Plot constraint contours
    for i in 1:numConstr
        c(x, y) = begin
            simple1_constraints([x,y])[i]
        end
        scolor = contourColors[i]
        contour!(x, y, c, seriescolor=scolor, w=2,  levels=lvls)
    end

    # Plot true minimum
    scatter!([true_min[1]],[true_min[2]],markercolor=:green,markersize=8,label="true minimum")

    for i in 1:size(initial_points,1)
        x0 = initial_points[i]
        lineColor = lineColors[i]

        # Plot initial point
        scatter!([x0[1]],[x0[2]],marker=:d,markersize=5,markercolor=lineColor,label="initial point")

        # Plot trajectory and final point
        x_best, xlist, _  = get_full_list(simple1, grad, simple1_constraints, x0, 1000, "simple_1")
        xnlist=hcat(xlist...)'
        plot!(xnlist[:,1],xnlist[:,2], color=lineColor, marker=:dot, label="path of feasible points", w=2)
        scatter!([x_best[1]],[x_best[2]],marker=:d,markersize=5,markercolor=:red, label="best found")
    end
    title!("Simple $problemNumber path taken")
    return p1
end

problemNumber = 1
fn = simple1
constr = simple1_constraints
numConstr = 2
lvls = [-2, -1, 0]
initial_points1 = [[1.51183, 1.63151],
 [0.447248, 0.233549],
 [1.09013, 0.345318]]
# for i in 1:3
#     initial_points[i] = simple1_init()
# end
true_min = [2.0/3.0; 1.0/sqrt(3)]
xMin = -2
xMax = 2
yMin = -2
yMax = 2
lineColors = [:indigo, :orchid, :lightpink]
contourColors = [:blues, :reds, :greens]

p1 = create_path_plot(problemNumber, fn, constr, numConstr, lvls, initial_points1, true_min, xMin, xMax, yMin, yMax, lineColors, contourColors)

problemNumber = 2
fn = simple2
constr = simple2_constraints
lvls = [-3, -2, -1, 0]
initial_points2 = [[-0.303123, 0.733811],
 [-0.807351, -0.710301],
 [-0.450702, 0.209268]]
# for i in 1:3
#     initial_points[i] = simple2_init()
# end
true_min = [1;1]
p2 = create_path_plot(problemNumber, fn, constr, numConstr, lvls, initial_points2, true_min, xMin, xMax, yMin, yMax, lineColors, contourColors)

plt1 = plot(p1,p2,layout=(2,1))
gui(plt1)

function create_convergence_plot(fpvalues, problemNumber)
    pyplot(size = (1000,1000), legend = true)
    fp=hcat(fpvalues...)'
    colors = [:green, :red]
    color = fp[2,2] <= 0 ? colors[1] : colors[2]
    firstRed = false
    firstGreen = false
    p1 = plot(fp[:,1], label= "", color = :black)
    # p2=plot([1],[fp[1,1]],marker=:circle, markersize=5, color=(fp[1,2] <= 0 ? colors[1] : colors[2]), label = "")
    for i in 1:size(fp,1)
        lbl = ""
        if !firstRed && fp[i,2] > 0
            lbl = "infeasible"
            firstRed = true
        elseif !firstGreen && fp[i,2] <= 0
            lbl = "feasible"
            firstGreen = true
        end

        plot!([i],[fp[i,1]],marker=:circle, markersize=5, color=(fp[i,2] <= 0 ? colors[1] : colors[2]), label = lbl)
    end
    # plot(p2)
    xlabel!("Iteration number")
    ylabel!("Value of f(x)")
    title!("Simple $problemNumber convergence")
    return p1
end

# xbest, _, fpvalues  = get_full_list(simple1, grad, simple1_constraints, initial_points1[1], 1000, "simple_1")
# problemNumber = 1
# p1 = create_convergence_plot(fpvalues, problemNumber)

# xbest, _, fpvalues  = get_full_list(simple2, grad, simple2_constraints, initial_points2[1], 1000, "simple_2")
# problemNumber = 2
# p2 = create_convergence_plot(fpvalues, problemNumber)

# init3 = [-1.9022234854723372,
#  -1.3978361756095108,
#   0.0]
# xbest, _, fpvalues  = get_full_list(simple3, grad, simple1_constraints, init3, 1000, "simple_3")
# problemNumber = 3
# p3 = create_convergence_plot(fpvalues, problemNumber)

# plt1 = plot(p1,p2,p3,layout=(3,1))
# gui(plt1)


"""
    exponential_solver(rho_u0,rho_v0,LK,LS,LD,t_span,N=0;initial_type = "density")
# Purpose:
- use an exponential integrator to integrate

    ``\\rho_t=-i(L_K+iL_S) \\rho+L_D \rho``

# Argument:
- ``\\rho_{u0},\\rho_{v0}``: ``\\rho0 = ``\\rho_{u0} - i \\rho_{v0}``
- LK: real part of the Hamiltonian operator
- LS: imaginary part of the Hamiltonain operator
- LD: Lindblad operator
- t_span: where the solution will be stored
- initial_type: "density" vectorized density matrix, "states" states vector
# Output:
- rho_u,rho_v: solutions at t_span given by ``\\rho(t_i) = \rho_u(:,i)-i \rho_v(:,i)``
"""
function exponential_solver(rho_u0,rho_v0,
                            LK,LS,LD,
                            t_span,N=0;
                            initial_type = "density")
    if(initial_type == "states")
        rho_u_initial = (rho_u0*transpose(rho_u0)+rho_v0*transpose(rho_v0))[:]
        rho_v_initial = (rho_v0*transpose(rho_u0)-rho_u0*transpose(rho_v0))[:]
        rho_vec0 = [rho_u_initial;rho_v_initial]
        if(N==0)
            N = length(rho_u_initial)
        end
    elseif(initial_type == "density")
        rho_vec0 = [rho_u0;rho_v0];
        if(N==0)
            N = length(rho_u0)
        end
    else
        println("Error! initial_type must be \"density\" or \"states\"")
        return
    end
    #rho_vec = zeros(Float64,length(rho_vec0),length(t_span))
    LL = [LS+LD -LK;
          LK     LS+LD]
    # Zygote does not support functions with setindex!(), so code is organized in this way in order to
    # use auto differentiation
    rho_vec = exp(LL*t_span[1])*rho_vec0
    for time_step = 2:length(t_span)
        rho_vec = [rho_vec exp(LL*t_span[time_step])*rho_vec0]
    end
    return rho_vec[1:N,:],rho_vec[N+1:2*N,:]
end

"""
    exponential_solver_complex(rho_vec0,L,t_span::Array{Float64};initial_type = "density"):

# Argument:
- L: the whole propagation operator
- t_span: where the function will be evaluated stored in
- rho0_vec: initial density/state
- initial_type: decide we aer given an initial density matrix or a state vector

# Output:
- solutions at t_span
"""
function exponential_solver_complex(rho_vec0,L,t_span::Array{Float64};initial_type = "density")
    if(initial_type == "states")
        rho_vec0 = (rho_vec0*adjoint(rho_vec0))[:]
    elseif(initial_type != "density")
        println("Error! initial_type must be \"density\" or \"states\"")
        return
    end
    rho_vec = zeros(ComplexF64,length(rho_vec0),length(t_span))
    for time_step = 1:length(t_span)
        rho_vec[:,time_step] = exp(L*t_span[time_step])*rho_vec0
    end
    return rho_vec
end
"""
    exponential_solver_complex(rho_vec0,L,t_span::StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}};
                       initial_type = "density")

# Argument:
- L: the whole propagation operator
- t_span: where the function will be evaluated stored in
- rho0_vec: initial density/state
- initial_type: decide we aer given an initial density matrix or a state vector

# Output:
- Complex-valued solutions at t_span
"""
function exponential_solver_complex(rho_vec0,L,
                            t_span::StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}};
                            initial_type = "density")
    if(initial_type == "states")
        rho_vec0 = (rho_vec0*adjoint(rho_vec0))[:]
    elseif(initial_type != "density")
        println("Error! initial_type must be \"density\" or \"states\"")
        return
    end
    rho_vec = zeros(ComplexF64,length(rho_vec0),length(t_span))
    dt = t_span[2]-t_span[1]
    Propagator = exp(L*dt)
    rho_vec[:,1] = exp(L*t_span[1])*rho_vec0
    for time_step = 2:length(t_span)
        rho_vec[:,time_step] = Propagator*rho_vec[:,time_step-1]
    end
    return rho_vec
end

"""
    LindbladODEProblemComplex(rho0,L::Array{ComplexF64,2},time_final::Float64;initial_type = "density")

Function provide interfaces to DifferentialEquations
package to solve: ``\\rho_t = L \\rho``

# Argument:
- L: Lindblad operator
- time: final time or time beining evaluated
- rho0: initial condition
- initial_type: specify initial value is a density matrix/a state vector

# Output:
A complex-valued problem object which we will feed to DifferentialEquations.jl
"""
function LindbladODEProblemComplex(rho0,L::Array{ComplexF64,2},time_final::Float64;initial_type = "density")
    if(initial_type=="states")
        rho0 = (rho0*adjoint(rho0))[:];
    end
    rho0 = convert(Vector{ComplexF64},rho0)
    function ode_problem!(_drho,_rho,_p,_t)
    	_drho .= _p*_rho
    end
    return ODEProblem(ode_problem!,rho0,(0.0,time_final),L)
end

"""
    LindbladODEProblem(rho_u0,rho_v0,LK::Array{Float64,2},LS::Array{Float64},LD::Array{Float64,2},time_final::Float64;initial_type = "density")

Function provides interfaces to DifferentialEquations
package to solve the real-valued Lindblad system:
``(\\rho_u - i \\rho_v)_t = -i (L_K+iL_S)(\\rho_u-i \\rho_v) + L_D(\\rho_u-i \\rho_v)``

# Argument:
- ``\\rho_{u0}, \\rho_{v0}``: initial condition ``\\rho_0 = \\rho_{u0} - i \\rho_{v0}``
- LK: real part of the Hamiltonian operator
- LS: imaginary part of the Hamiltonain operator
- LD: Lindblad operator
- time: final time or time beining evaluated
- initial_type: specify initial value is a density matrix/a state vector

# Output:
A problem object which we will feed to DifferentialEquations.jl
"""
function LindbladODEProblem(rho_u0,rho_v0,LK::Array{Float64,2},LS::Array{Float64},LD::Array{Float64,2},time_final::Float64;initial_type = "density")
    if(initial_type=="states")
        rho_u0,rho_v0 = convert_state_to_density(rho_u0,rho_v0)
    end
    L_real = [LS+LD -LK;
              LK     LS+LD]
    function ode_problem!(_drho,_rho,_p,_t)
    	_drho .= _p*_rho
    end
    return ODEProblem(ode_problem!,[rho_u0;rho_v0],(0.0,time_final),L_real)
end


###
  Name     : c9_32_iris.jl
  Book     : Hands-on Data Science with Anaconda )
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan and James Yan
  Date     : 4/6/2018
  email    : yany@canisius.edu
             paulyxy@hotmail.com
###

using Gadfly
using RDatasets
using Clustering
iris = dataset("datasets", "iris")
head(iris)
features=permutedims(convert(Array, iris[:,1:4]),[2, 1])
result=kmeans(features,3)
nameX="PetalLength"
nameY="PetalWidth"
assignments=result.assignments
plot(iris, x=nameX,y=nameY,color=assignments,Geom.point)


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

# Version of: UTC 2021-05-22 22:01:34
# 82230d30-bb38-11eb-33cc-7717c99dd904

# Do not edit this file, it is generated from the modules and will be overwritten!
# Edit the modules in the src directory and build this file with BuildTriangles.jl!

__precompile__()
module IntegerTriangles
using Nemo,
    HTTP,
    DocStringExtensions,
    OrderedCollections,
    PrettyTables
export
A046802Transform,
A046802Triangle,
A343237Triangle,
AbstractSequence,
AbstractTriangle,
AitkenTriangle,
AllPrettyTraits,
AllTraitCards,
AltSum,
AssociatedTriangles,
BernoulliPolynomial,
Bessel1Transform,
Bessel1Triangle,
BinConv,
Binomial,
BinomialTransform,
BinomialTriangle,
Catalan,
CatalanBallot,
CatalanTransform,
CatalanTriangle,
Central,
Coefficients,
ConvSum,
ConvTrans,
ConvTri,
Convolution,
ConvolutionTransformation,
ConvolutionTriangle,
DArcaisTriangle,
DelannoyTriangle,
DelehamΔ,
DiagSum,
DiagTri,
DiagonalTriangle,
EgfExpansionCoeff,
EgfExpansionPoly,
EulerSecTriangle,
EulerTanTriangle,
EulerTriangle,
EulerianSO2Triangle,
EulerianTransform,
EulerianTriangle,
EulerianTriangle2,
Evaluate,
EvenSum,
Explore,
ExtCatalanTriangle,
Factorial,
FallingFactTriangle,
FibonacciTransform,
FibonacciTriangle,
Fine,
FineTriangle,
Flat,
FubiniTriangle,
FubiniTriangle,
GetSeqnum,
GetSeqnumUri,
HermiteTransform,
HermiteTriangle,
IBinConv,
IBinomial,
IBinomialTransform,
IBinomialTriangle,
Inspect,
InvRev,
Inverse,
InverseTriangle,
JacobsthalTransform,
JacobsthalTriangle,
Laguerre,
LaguerreTransform,
LaguerreTriangle,
LahNumbers,
LahTransform,
LahTriangle,
LeftSide,
LinMap,
MakeSeqUri,
Middle,
MotzkinTransform,
MotzkinTriangle,
NarayanaTransform,
NarayanaTriangle,
NegHalf,
OddSum,
OrthoPoly,
PascalTriangle,
PolyArray,
PolyTri,
PolyTriangle,
PolyVal2,
PolyVal3,
PolyValn,
PolyValue,
Polynomial,
PolynomialFunction,
PosHalf,
PrettyTraits,
Print,
Println,
QInt,
QPolyRing,
QPolySeq,
QSeq,
QTri,
RecTriangle,
RencontresTriangle,
RevInv,
Reverse,
ReversePolynomial,
RightSide,
RiordanSquare,
RisingFactTriangle,
SaveToCSV,
SchroederBigTransform,
SchroederBigTriangle,
SchröderBTriangle,
SchröderLTriangle,
SeqToFixlenString,
SeqToString,
Show,
StirlingCycleTransform,
StirlingCycleTriangle,
StirlingSetTransform,
StirlingSetTriangle,
SwingTriangle,
TRAITS,
TRIANGLES,
TTreeTriangle,
Telescope,
TraitCard,
Traits,
Trans,
TransAlts,
TransNat0,
TransNat1,
TransSqrs,
TransTraits,
TransUnos,
Triangles,
TrinomialTransform,
TrinomialTriangle,
UniTriangle,
UpdateDocs,
WorpitzkyTriangle,
ZInt,
ZPolyRing,
ZPolySeq,
ZSeq,
ZTri,
csv_files,
datapath,
devdoctrait,
docsrcpath,
oeis_notinstalled,
oeis_search,
profilespath,
search_failed,
ℚInt,
ℚPolyRing,
ℚPolySeq,
ℚSeq,
ℚTri,
ℤInt,
ℤPolyRing,
ℤPolySeq,
ℤSeq,
ℤTri,
V000111,
A000166,
I008279,T008279,
A038048,
I048993,T048993,
I094587,T094587,
I132393,T132393,
I225478,T225478,
I271703,T271703
# *** TrianglesBase.jl ****************
import Base.sum
"""
Basic definitions used in the library IntegerTriangles.
We consider the following traits of an triangle:

```
Flat, Reverse, Inverse, RevInv, InvRev, DiagTri,

PolyTri, Sum, EvenSum, OddSum, AltSum, DiagSum,

Middle, Central, LeftSide, RightSide, PosHalf,

NegHalf, PolyVal2, PolyVal3, BinConv, IBinConv,

TransSqrs, TransNat0, TransNat1
```
"""
const ModuleTrianglesBase = ""
"""
Supertype for sequences (or sequence-like types).
"""
abstract type AbstractSequence end
"""
Supertype for triangles (or triangles-like types).
"""
abstract type AbstractTriangle end
const Seq{T} = Array{T,1}
"""
ℤInt is an alias for the type Nemo.fmpz.
"""
const ℤInt  = Nemo.fmpz
"""
ℚInt is an alias for the type Nemo.fmpq.
"""
const ℚInt  = Nemo.fmpq
"""
ℤSeq is an alias for the type Array{Nemo.fmpz, 1}.
"""
const ℤSeq = Seq{ℤInt}
"""
ℚSeq is an alias for the type Array{Nemo.fmpq, 1}.
"""
const ℚSeq = Seq{ℚInt}
"""
ℤTri is an alias for the type Array{Array{Nemo.fmpz, 1}, 1}.
"""
const ℤTri = Seq{ℤSeq}
"""
ℚTri is an alias for the type Array{Array{Nemo.fmpq, 1}, 1}.
"""
const ℚTri = Seq{ℚSeq}
"""
Constructor for an ℤSeq of length len. If a second parameter f
is given the sequence is constructed as [ZZ(f(n)) for n in 0:len-1]
"""
ZSeq(len::Int) = ℤSeq(undef, len)
ZSeq(len::Int, f::Function) = [ZZ(f(n)) for n in 0:len-1]
"""
Constructor for an ℚSeq of length len. If a second parameter f
is given the sequence is constructed as [QQ(f(n)) for n in 0:len-1]
"""
QSeq(len::Int) = ℚSeq(undef, len)
QSeq(len::Int, f::Function) = [QQ(f(n)) for n in 0:len-1]
"""
Constructor for ZTri.
"""
function ZTri(dim::Int; reg=false)
reg ? ℤTri(ZSeq.(1:dim)) : ℤTri(undef, dim)
end
ZTri(dim, f::Function) = f.(0:dim-1)
ZTri(T::AbstractTriangle) = [row for row ∈ T]
"""
Constructor for QTri.
"""
function QTri(dim::Int; reg=false)
reg ? QSeq.(1:dim) : ℚTri(undef, dim)
end
"""
ℤPoly is an alias for the type Nemo.fmpz_poly.
"""
const ℤPoly = Nemo.fmpz_poly
"""
ℚPoly is an alias for the type Nemo.fmpq_poly.
"""
const ℚPoly = Nemo.fmpq_poly
"""
ℤPolySeq is an alias for the type Array{Nemo.fmpz_poly, 1}.
"""
const ℤPolySeq = Seq{ℤPoly}
"""
ℚPolySeq is an alias for the type Array{Nemo.fmpq_poly, 1}.
"""
const ℚPolySeq = Seq{ℚPoly}
"""
ℤPolyTri is an alias for the type Array{Array{Nemo.fmpz_poly, 1}, 1}.
"""
const ℤPolyTri = Seq{ℤPolySeq}
"""
ℚPolyTri is an alias for the type Array{Array{Nemo.fmpq_poly, 1}, 1}.
"""
const ℚPolyTri = Seq{ℚPolySeq}
"""
(SIGNATURES)
"""
ZPolyRing(x) = PolynomialRing(ZZ, x)
"""
(SIGNATURES)
"""
QPolyRing(x) = PolynomialRing(QQ, x)
const WARNING_ON_NOTINVERTIBLE = false
"""
Return the diagonal triangle T(n - k, k) where k in 0:n/2.
```
julia> Println.(DiagonalTriangle(MotzkinTriangle(8)))
[1]
[1]
[2, 1]
[4, 2]
[9, 5, 1]
[21, 12, 3]
[51, 30, 9, 1]
[127, 76, 25, 4]  # A106489
```
"""
function DiagonalTriangle(T::ℤTri)
dim = length(T)
U = ZTri(dim)
for n = 1:dim
R = ZSeq(div(n + 1, 2))
for k = 0:div(n - 1, 2)
R[k + 1] = T[n - k][k + 1]
end
U[n] = R
end
U
end
DiagTri(T::ℤTri) = DiagonalTriangle(T)
function Convolution(S::ℤSeq, T::ℤSeq)
L = length(S);
if L != length(T)
@warn "Incompatible lengths!"
return []
end
[sum(S[k + 1] * T[n - k + 1] for k in 0:n) for n in 0:L-1]
end
Convolution(S::ℤSeq) = Convolution(S, S)
ConvSum(S::ℤSeq) = sum(Convolution(S))
"""
Return the convolution triangle of T.
```
julia> T = [ℤInt[1], ℤInt[2, 3], ℤInt[4, 5, 6], ℤInt[7, 8, 9, 10]]
Println.(ConvolutionTriangle(T))
[1]
[4, 12]
[16, 40, 73]
[49, 112, 190, 284]
```
"""
ConvolutionTriangle(T::ℤTri) = Convolution.(T)
ConvTri(T::ℤTri) = ConvolutionTriangle(T) # alias
"""
The sum of a ℤTri is the sequence of the sum of the rows.
"""
Base.sum(T::ℤTri) = [sum(row) for row ∈ T]
Base.sum(T::ℚTri) = [sum(row) for row ∈ T]
"""
The EvenSum of a ℤSeq is the sum of the even indexed terms, indexing starts with 0.
```
julia> EvenSum([0, 1, 2, 3, 4, 5])
0 + 2 + 4 = 6
```
"""
EvenSum(A) = sum(A[1:2:end])
"""
The OddSum of a ℤSeq is the sum of the odd indexed terms, indexing starts with 0.
```
julia> OddSum([0, 1, 2, 3, 4, 5])
1 + 3 + 5 = 9
```
"""
OddSum(A) = sum(A[2:2:end])
"""
The AltSum of a ℤSeq is the alternating sum.
```
julia> AltSum([0, 1, 2, 3, 4, 5])
+ 0 - 1 + 2 - 3 + 4 - 5 = 6 - 9 = - 3
```
"""
AltSum(A) = EvenSum(A) - OddSum(A)
"""
The Middle of a ℤSeq A is the middle term, A[div(begin + end, 2)].
```
julia> Middle([0, 1, 2, 3, 4, 5])
3
```
"""
Middle(A) = A[div(begin + end, 2)]
"""
The LeftSide of a ℤSeq is the first term.
```
julia> LeftSide([0, 1, 2, 3, 4, 5])
0
```
"""
LeftSide(A) = A[begin]
"""
The RightSide of a ℤSeq is the last term.
```
julia> RightSide([0, 1, 2, 3, 4, 5])
5
```
"""
RightSide(A) = A[end]
"""
The EvenSum of a ℤTri is the sequence of the even sums of the rows.
```
EvenSum(T::ℤTri) = EvenSum.(T)
```
"""
EvenSum(T::ℤTri) = EvenSum.(T)
"""
The OddSum of a ℤTri is the sequence of the odd sums of the rows.
```
OddSum(T::ℤTri) = OddSum.(T)
```
"""
OddSum(T::ℤTri) = OddSum.(T)
"""
The AltSum of a ℤTri is the sequence of the alternating sums of the rows.
```
AltSum(T::ℤTri) = EvenSum(T) - OddSum(T)
```
"""
AltSum(T::ℤTri) = EvenSum(T) - OddSum(T)
"""
The DiagSum of a ℤTri is the sum of the diagonal triangle.
```
DiagSum(T::ℤTri) = sum(DiagonalTriangle(T))
```
"""
DiagSum(T::ℤTri) = sum(DiagonalTriangle(T))
"""
The Middle of a ℤTri is the sequence of the middle term in the rows.
```
Middle(T::ℤTri) = Middle.(T)
```
"""
Middle(T::ℤTri) = Middle.(T)
"""
The Central of a ℤTri is the sequence of the middle term
of the even indexed rows, indexing starts with 0.
```
Central(T::ℤTri) = Middle.(T[begin:2:end])
```
"""
Central(T::ℤTri) = Middle.(T[begin:2:end])
"""
The LeftSide of a ℤTri is the sequence of the first term in the rows.
```
LeftSide(T::ℤTri) = LeftSide.(T)
```
"""
LeftSide(T::ℤTri) = LeftSide.(T)
"""
The RightSide of a ℤTri is the sequence of the last term in the rows.
```
RightSide(T::ℤTri) = RightSide.(T)
```
"""
RightSide(T::ℤTri) = RightSide.(T)
"""
Return the ℤ-polynomial whose coefficients are the terms of the sequence.
```
[1, 2, 3] -> 1 + 2*x + 3*x^2
```
"""
function Polynomial(S::ℤSeq)
R, x = ZPolyRing("x")
sum(c * x^(k - 1) for (k, c) ∈ enumerate(S))
end
"""
Return the sequence of ℤ-polynomials whose coefficients are the terms of the triangle.
```
Polynomial(T::ℤTri) = Polynomial.(T)
```
"""
Polynomial(T::ℤTri) = Polynomial.(T)
"""
Return the ℤ-polynomial whose coefficients are the terms of the reversed sequence.
```
[1, 2, 3] -> x^2 + 2*x + 3
```
"""
ReversePolynomial(S::ℤSeq) = Polynomial(reverse(S))
"""
Return the sequence of ℤ-polynomials whose coefficients are the terms of the reversed triangle.
```
ReversePolynomial(T::ℤTri) = ReversePolynomial.(T)
```
"""
ReversePolynomial(T::ℤTri) = ReversePolynomial.(T)
"""
Return the polynomial function associated with the polynomial with coefficients
given by the sequence S. A polynomial function evaluates to Float64 values.
```
p = PolynomialFunction([1, 2, 3])
julia> [p(r) for r in 0:3]
4-element Vector{Float64}:
  1.0
  6.0
 17.0
 34.0
```
"""
function PolynomialFunction(S)
y -> sum(Float64(c) * y^(k - 1)
for (k, c) ∈ enumerate(S))
end
"""
Return the sequence of polynomial functions associated to the polynomials with coefficients
given by the triangle T.
```
PolynomialFunction(T::ℤTri) = PolynomialFunction.(T)
```
"""
PolynomialFunction(T::ℤTri) = PolynomialFunction.(T)
"""
Return the value of the ℤ-polynomial p evaluated at x.
```
julia> R, x = ZPolyRing("x")
p = 1 + 2*x + 3*x^2
Evaluate(p, 2)
17
```
"""
Evaluate(p::ℤPoly, x) = subst(p, x)
Evaluate(p::ℚPoly, x) = subst(p, x)
"""
Return the sequence of values to which the sequence of ℤ-polynomials P evaluate at x.
```
julia> R, x = ZPolyRing("x")
P = [sum(k * x^k for k in 0:n) for n in 0:9]
Evaluate(P, 3)  # A289399
```
"""
Evaluate(P::ℤPolySeq, x) = Evaluate.(P, x)
Evaluate(P::ℚPolySeq, x) = Evaluate.(P, x)
"""
Return the list of coefficients of the polynomial p (ordered by ascending powers).
```
julia> R, x = ZPolyRing("x")
p = 1 + 2*x + 3*x^2
Coefficients(p)
```
"""
Coefficients(p, n=0) = p == 0 ? ℤInt[0] : coeff.(p, 0:max(n,degree(p)))
"""
Return the sequence of list of coefficients of the polynomials P.
"""
Coefficients(P::AbstractArray) = Coefficients.(P)
import Nemo.numerator
numerator(T::ℚTri) = [numerator.(t) for t ∈ T]
"""
Return numerator(2^degree(p) * Evaluate(p, QQ(1, 2))).
```
julia> R, x = ZPolyRing("x")
p = 1 + 2*x + 3*x^2
PosHalf(p)
11
```
"""
PosHalf(p) = p == 0 ? ZZ(0) : numerator(2^degree(p) * Evaluate(p, QQ(1, 2)))
"""
Return Numerator((-2)^degree(p) * Evaluate(p, QQ(-1, 2)))
```
julia> R, x = ZPolyRing("x")
p = 1 + 2*x + 3*x^2
NegHalf(p)
3
```
"""
NegHalf(p) = p == 0 ? ZZ(0) : numerator((-2)^degree(p) * Evaluate(p, QQ(-1, 2)))
"""
Return the sequence generated by broadcasting PosHalf over the
rows of the triangle interpreted as a polynomials.
```
julia> T = [[ZZ(k) for k in 0:n] for n in 1:5]
PosHalf(Polynomial(T))  # A000295
5-element ℤSeq
 1
 4
 11
 26
 57
```
"""
PosHalf(T::ℤTri) = PosHalf.(Polynomial(T))
"""
Return the sequence generated by broadcasting NegHalf over the
rows of the triangle interpreted as a polynomials.
```
NegHalf(T::ℤTri) = NegHalf.(Polynomial(T))
```
"""
NegHalf(T::ℤTri) = NegHalf.(Polynomial(T))
"""
Return the sequence generated by broadcasting PosHalf over the
sequence of polynomials.
"""
PosHalf(P::ℤPolySeq) = PosHalf.(P)
"""
Return the sequence generated by broadcasting NegHalf over the
sequence of polynomials.
"""
NegHalf(P::ℤPolySeq) = NegHalf.(P)
LinMap(M::ℤTri, V::ℤSeq, n) = sum(M[n][k] * V[k] for k = 1:n)
"""
LinMap(M::ℤTri, V::ℤSeq) returns the 'matrix times vector' product
of M and V. Expands up to length(V) and we assume length(M) >= length(V).
"""
LinMap(M::ℤTri, V::ℤSeq) = (n -> LinMap(M, V, n)).(1:length(V))
LinMap(F::Function, V::ℤSeq, n) = sum(F(n - 1)[k] * V[k] for k = 1:n)
"""
LinMap(F::Function, V::ℤSeq) returns the 'matrix times vector' product
of a matrix generated by F and V.
```
julia>  L(n) = [ZZ(k) for k in 0:n]
[LinMap(L, L(n)) for n in 0:9]
0
1
5
14
30
... # A000330
```
"""
LinMap(F::Function, V::ℤSeq) = LinMap(F, V, length(V))
"""
Transform a ℤSeq V by the matrix/vector product by applying LinMap to (M, V).
Expands up to min(length(M), length(V)).
"""
Trans(M::ℤTri, V::ℤSeq) = (n -> LinMap(M, V, n)).(1:min(length(M), length(V)))
"""
TransAlts(T) = Trans(T, [(-1)^n * ZZ(1) for n = 0:length(T)])

Standard trait of T.
"""
TransAlts(T) = Trans(T, [(-1)^n * ZZ(1) for n = 0:length(T)])
"""
TransSqrs(T) = Trans(T, [ZZ(n^2) for n = 0:length(T)])

Standard trait of T.
"""
TransSqrs(T) = Trans(T, [ZZ(n^2) for n = 0:length(T)])
"""
TransNat0(T) = Trans(T, [ZZ(n) for n = 0:length(T)])

Standard trait of T.
"""
TransNat0(T) = Trans(T, [ZZ(n) for n = 0:length(T)])
"""
TransNat1(T) = Trans(T, [ZZ(n) for n = 1:length(T)])

Standard trait of T.
"""
TransNat1(T) = Trans(T, [ZZ(n) for n = 1:length(T)])
"""
Return an iterator expanding the given sequence to a regular triangle.
```
julia> T = Telescope(ℤInt[1, 2, 3, 4, 5, 6])
collect(T)
 [1]
 [1, 2]
 [1, 2, 3]
 [1, 2, 3, 4]
 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5, 6]
```
"""
Telescope(A::ℤSeq) = (A[1:k] for k = 1:size(A, 1))
"""
Return an iterator expanding the sequence generated by the function f to a regular triangle.
"""
Telescope(len::Int, f::Function) = Telescope([ZZ(f(k)) for k = 0:len - 1])
"""
(SIGNATURES)
"""
Binomial(n, k) = Nemo.binomial(ZZ(n), ZZ(k))
Binomial(n) = [Binomial(n, k) for k = 0:n]
Binomial(A::ℤSeq) = LinMap(Binomial, A)
"""
(SIGNATURES)
"""
BinomialTriangle(dim) = [Binomial(n) for n = 0:dim - 1]
"""
(SIGNATURES)
"""
BinomialTransform(A::ℤSeq) = Binomial.(Telescope(A))
BinomialTransform(A::ℤTri) = Binomial.(A)
BinConv(A::ℤTri) = BinomialTransform(A::ℤTri)
IBinomial(n, k) = ZZ((-1)^(n-k))*Binomial(n, k)
IBinomial(n) = [IBinomial(n, k) for k = 0:n]
IBinomial(A::ℤSeq) = LinMap(IBinomial, A)
"""
(SIGNATURES)
"""
IBinomialTriangle(dim) = [IBinomial(n) for n = 0:dim - 1]
"""
(SIGNATURES)
"""
IBinomialTransform(A::ℤSeq) = IBinomial.(Telescope(A))
IBinomialTransform(A::ℤTri) = IBinomial.(A)
IBinConv(A::ℤTri) = IBinomialTransform(A::ℤTri)
"""
Return the factorial ``n! = ∏(1, n)``.
"""
Factorial(n) = Nemo.factorial(ZZ(n))
"""
(SIGNATURES)
"""
Laplace(s, k) = factorial(k) * coeff(s, k)
"""
Return the Laplace expansion of a bivariate exponential
generating function as a power series, or, if 'coeff=true',
as the coefficients of the series.
"""
function EgfExpansion(prec, gf::Function, coeff=true)
R, x = QPolyRing("x")
S, t = PowerSeriesRing(R, prec + 1, "t")
ser = gf(x, t)
P = Laplace.(ser, 0:prec - 1)
coeff ? Coefficients.(P) : P
end
"""
Return the Laplace expansion of a bivariate exponential
generating function as a power series.
"""
EgfExpansionCoeff(prec, gf::Function) = EgfExpansion(prec, gf, true)
"""
Return the Laplace expansion of a bivariate exponential generating
function as a list of the coefficients of the power series.
"""
EgfExpansionPoly(prec, gf::Function) = EgfExpansion(prec, gf, false)
function OrthoPoly(dim::Int, s::Function, t::Function)
T = ZTri(dim, reg=true) # regular triangle
for n ∈ 1:dim T[n][n] = 1 end
for n ∈ 2:dim
u(k) = k == 0 || k == n ? 0 : T[n - 1][k]
for k ∈ 1:n - 1
T[n][k] = (u(k - 1) + s(k - 1) * u(k) + t(k) * u(k + 1))
end
end
T
end
"""
(SIGNATURES)
"""
function DelehamΔ(dim::Int, s::Function, t::Function)
T = ZTri(dim)
R, x = ZPolyRing("x")
A = [R(s(k) + x * t(k)) for k ∈ 0:dim - 2]
C = [R(1) for i ∈ 0:dim]
C[1] = R(0)
m = 1
for k ∈ 0:dim - 1
for j ∈ k + 1:-1:2
C[j] = C[j - 1] + C[j + 1] * A[j - 1]
end
T[m] = [coeff(C[2], j) for j ∈ 0:k]
m += 1
end
T
end
"""
(SIGNATURES)
"""
function RiordanSquare(dim::Int, s::Function)
T = ZTri(dim, reg=true)
for n ∈ 0:dim - 1
T[n + 1][1] = s(n)
end
for k ∈ 1:dim - 1, m ∈ k:dim - 1
T[m + 1][k + 1] = sum(T[j][k] * T[m - j + 2][1] for j ∈ k:m)
end
T
end
"""
(SIGNATURES)
"""
function ExpRiordanSquare(dim::Int, s::Function)
R = RiordanSquare(dim, s)
u = ZZ(1)
for k ∈ 1:dim - 1
u *= k
for m ∈ 1:k
j = (m == 1 ? u : div(j, m - 1))
R[k + 1][m] *= j
end
end
R
end
"""
(SIGNATURES)
"""
function check(invM, dim)
for n = 1:dim
for k = 1:n
if denominator(invM[n, k]) != 1
return false
end
end
end
return true
end
"""
(SIGNATURES)
"""
function InverseTriangle(T)
dim = length(T)
M = zeros(QQ, dim, dim)
for n in 1:dim, k in 1:n
M[n, k] = QQ(T[n][k])
end
S = MatrixSpace(QQ, dim, dim)
invM = try
inv(S(M))
catch e
if isa(e, Exception)
if WARNING_ON_NOTINVERTIBLE
@warn("\n\n Not invertible!\n\n")
end
return ℤSeq[]
end
end
if check(invM, dim) != true
if WARNING_ON_NOTINVERTIBLE
@warn("\n\n Inverse not in MatrixSpace(ZZ)!\n\n")
end
return ℤSeq[]
end
return [[numerator(invM[n, k]) for k = 1:n] for n = 1:dim]
end
"""
Alias for InverseTriangle
"""
Inverse(T::ℤTri) = InverseTriangle(T)
"""
Return the row reversed triangle.
"""
Reverse(T::ℤTri) = reverse.(T)
"""
(SIGNATURES)
"""
function RevInv(T::ℤTri)
I = Inverse(T)
return I != [] ? Reverse(I) : ℤSeq[]
end
"""
(SIGNATURES)
"""
InvRev(T::ℤTri) = Inverse(Reverse(T))
"""
(SIGNATURES)
"""
function Flat(T::ℤTri)
T === [] && return []
Empty(s) = isempty(s) ? [ZZ(0)] : s
[z for t ∈ T for z ∈ Empty(t)]
end
"""
(SIGNATURES)
"""
function DeFlat(S::ℤSeq)
S == [] && return []
len = length(S)
n = 1
while div(n*(n + 1), 2) < len
n += 1
end
T = ZTri(n-1)
end
"""
Print the array without typeinfo.
"""
function Println(io, v::AbstractVector, newline=true)
print(io, "[")
for (i, el) ∈ enumerate(v)
i > 1 && print(io, ", ")
print(io, el)
end
newline ? println(io, "]") : print(io, "]")
end
Println(io, str::String) = println(io, str)
Println(T::ℤSeq) = Println(IOContext(stdout), T)
Println(T::ℤTri) = Println.(IOContext(stdout), T)
Print(io, str::String) = print(io, str)
Print(T::ℤSeq) = Println(IOContext(stdout), T, false)
Print(T::ℤTri) = Println.(IOContext(stdout), T, false)
"""
A recursive triangle `RecTriangle` is a subtype of `AbstractTriangle`. The rows
of the triangle are generated by a function `gen(n, k, prevrow)` defined for
``n ≥ 0`` and ``0 ≤ k ≤ n``. The function returns value of type ℤInt.

The parameter prevrow is a function which returns the values of `row(n-1)` of
the triangle and 0 if ``k < 0`` or ``k > n``. The function prevrow is provided
by an instance of `RecTriangle` and must not be defined by the user.
"""
struct RecTriangle <: AbstractTriangle
dim::Int
A::ℤSeq
gen::Function  # generating function
function RecTriangle(dim::Int, gen::Function)
dim <= 0 && throw(ArgumentError("dim has to be a positive integer."))
new(
dim,
fill(ZZ(0), dim),
(n::Int, k::Int, f::Function) -> gen(n, k, f)::ℤInt,
)
end
end
function Base.iterate(T::RecTriangle)
top = T.gen(0, 0, () -> undef)
T.A[1] = ZZ(top)
([top], 1)
end
"""
Return the next row of the triangle.
"""
function Base.iterate(T::RecTriangle, n)
@inline prevrow(A, n) = (k) -> (k < 0 || k > n) ? ZZ(0) : A[k + 1]
n == T.dim && return nothing
F(n, k) = T.gen(n, k, prevrow(T.A, n))
row = [F(n, k) for k ∈ 0:n]
for k ∈ 1:n + 1
T.A[k] = row[k]
end
(row, n + 1)
end
Base.length(R::RecTriangle) = R.dim
Base.eltype(R::RecTriangle) = ℤInt
function PolyArray(T::ℤTri)
P = Polynomial(T)
dim = length(T)
U = ZTri(dim)
for n = 1:dim
p = P[n]
eva = [Evaluate(p, k) for k in 0:dim - 1]
U[n] = numerator.(eva)
end
U
end
"""
(SIGNATURES)
"""
function PolyTriangle(T::ℤTri)
A = PolyArray(T)
U = ZTri(length(T))
for n = 1:length(T)
U[n] = [A[n - k][k + 1] for k = 0:n - 1]
end
U
end
PolyTri(T::ℤTri) = PolyTriangle(T)
"""
(SIGNATURES)
"""
function PolyValue(T::ℤTri, k::Int)
P = PolyArray(T)
[P[n][k + 1] for n = 1:length(P)]
end
"""
(SIGNATURES)
"""
PolyVal2(T::ℤTri) = PolyValue(T, 2)
"""
(SIGNATURES)
"""
PolyVal3(T::ℤTri) = PolyValue(T, 3)
"""
(SIGNATURES)
"""
function PolyValn(T::ℤTri)
P = PolyArray(T)
[P[n][n] for n = 1:length(P)]
end
"""
(SIGNATURES)
"""
const TRAITS = Function[
Flat,
Reverse,
Inverse,
RevInv,
InvRev,
DiagTri,
PolyTri,
sum,
EvenSum,
OddSum,
AltSum,
DiagSum,
Middle,
Central,
LeftSide,
RightSide,
PosHalf,
NegHalf,
PolyVal2,
PolyVal3,
PolyValn,
BinConv,
IBinConv,
TransSqrs,
TransNat0,
TransNat1
]
# *** TrianglesExamples.jl ****************
const CacheA000166 = Dict{Int,ℤInt}(0 => ZZ(1))
const CacheA038048 = Dict{Int,ℤInt}(0 => ZZ(0))
const CacheFine = Dict{Int,ℤInt}(0 => ZZ(1), 1 => ZZ(1), 2 => ZZ(0))
const CacheAndré = Dict{Tuple{Int,Int},fmpz}()
const CacheBeta = Dict{Tuple{Int,Int},fmpz}()
const CacheLah = Dict{Int,ℤSeq}([0 => [ZZ(1)]])
const CacheFubini = Dict{Int,ℤSeq}([0 => [ZZ(1)]])
const CacheDelannoy = Dict{Tuple{Int,Int},ℤInt}([(0,0) => ZZ(1), (1,0) => ZZ(1), (1,1) => ZZ(1)])
const CacheAitken = Dict{Int,ℤSeq}([0 => [ZZ(1)]])
const CacheBallot = Dict{Tuple{Int,Int},ℤInt}()
const CacheDArcais = Dict{Tuple{Int,Int},ℤInt}()
const CacheWorpitzky = Dict{Tuple{Int,Int},ℤInt}()
const CacheEulerianSO2 = Dict{Tuple{Int,Int},ℤInt}((0,0) => ZZ(1))
const CacheSwing = Dict{Tuple{Int,Int},ℤInt}((0,0) => ZZ(1))
const CacheEulerian = Dict{Tuple{Int,Int},ℤInt}()
const CacheEulerianClassic = Dict{Tuple{Int,Int},fmpz}()
const CacheNarayana = Dict{Tuple{Int,Int},fmpz}()
const CacheLaguerre = Dict{Int,ℤSeq}(0 => [ZZ(1)])
"""
Recurrences and iterations for some triangles.
The examples given are:
```
Aitken, Bessel1, Binomial, Catalan, DArcais,

Delannoy, Eulerian, EulerianSO2, EulerSec,

EulerTan, Euler, FallingFact, Fibonacci,

Fine, Fubini, Hermite, Laguerre, Lah, Motzkin,

Narayana, Rencontres, RisingFact, SchröderB,

SchröderL, StirlingCycle, StirlingSet, Trinomial,

TTree, Uni, Worpitzky
```
"""
const ModuleTrianglesExamples = ""
function A000166(n::Int)
n <= 1 && return ZZ(1 - n)
haskey(CacheA000166, n) && return CacheA000166[n]
a = (n - 1) * (A000166(n - 1) + A000166(n - 2))
return CacheA000166[n] = a
end
function A038048(n::Int)
haskey(CacheA038048, n) && return CacheA038048[n]
s = Factorial(n - 1) * divisor_sigma(n, 1)
return CacheA038048[n] = s
end
function Fine(n::Int)
haskey(CacheFine, n) && return CacheFine[n]
s = div((7*n - 12)*Fine(n-1) + (4*n - 6)*Fine(n-2), 2*n)
CacheFine[n] = s
end
function André(m::Int, n::Int)
haskey(CacheAndré, (m, n)) && return CacheAndré[(m, n)]
n ≤ 0 && return ZZ(1)
r = range(0, step = m, stop = n - 1)
S = sum(binomial(ZZ(n), ZZ(k)) * André(m, k) for k ∈ r)
return CacheAndré[(m, n)] = n % m == 0 ? -S : S
end
"""
Return the up-down numbers (2-alternating permutations).
"""
V000111(n::Int) = abs(André(2, n))
function PrimeDivisors(n)
n < 2 && return ℤInt[]
sort!([p for (p, e) ∈ factor(ZZ(n))])
end
Euler(n, k) = Binomial(n, k) * V000111(n - k)
EulerTriangle(dim) = [[Euler(n, k) for k = 0:n] for n = 0:dim - 1]
Euler(A::ℤSeq) = LinMap(Euler, A, length(A))
EulerTransform(A::ℤSeq) = Euler.(Telescope(A))
"""
https://oeis.org/wiki/User:Peter_Luschny/SwissKnifePolynomials
"""
function BetaPoly(n)
function v(n, k)
if haskey(CacheBeta, (n, k))
CacheBeta[(n, k)]
else
CacheBeta[(n, k)] = binomial(n, k)*subst(BetaPoly(k), 0)
end
end
R, z = ZPolyRing("z")
n == 0 && return R(1)
R(sum(isodd(k) ? 0 : v(n, k)*(z - 1)^(n - k - 1) for k in 0:n-1))
end
SECH(n) = isodd(n)  ? 0 : subst(BetaPoly(n), 0)
TANH(n) = iseven(n) ? 0 : subst(BetaPoly(n), 0)
function SecPoly(n)
R, z = ZPolyRing("z")
R(sum(Binomial(n, k)*SECH(k)*z^(n-k) for k in 0:n))
end
function TanPoly(n)
R, z = ZPolyRing("z")
n == 0 && return R(0)
R(sum(Binomial(n, k)*TANH(n - k)*z^k for k in 0:n-1))
end
function SwissKnifePoly(n, p, q)
R, z = ZPolyRing("z")
P = p == 0 ? 0 : SecPoly(n)
Q = q == 0 ? 0 : TanPoly(n)
p*P + q*Q
end
EulerTanTriangle(dim) = [Coefficients(TanPoly(n), n) for n in 0:dim-1]
EulerSecTriangle(dim) = Coefficients([SecPoly(n) for n in 0:dim-1])
FineTriangle(dim::Int) = RiordanSquare(dim, Fine)
TernaryTree(n::Int) = div(Binomial(ZZ(3*n), n), ZZ(2*n + 1))
TTreeTriangle(dim::Int) =  RiordanSquare(dim, TernaryTree)
"""
(SIGNATURES)
"""
function LahNumbers(n::Int64)
haskey(CacheLah, n) && return CacheLah[n]
prevrow = LahNumbers(n - 1)
row = ZSeq(n + 1)
row[1] = 0
row[n + 1] = 1
for k = 2:n
row[k] = prevrow[k - 1] + prevrow[k] * (n + k - 2)
end
CacheLah[n] = row
end
LahNumbers(n, k) = LahNumbers(n)[k + 1]
"""
(SIGNATURES)
"""
function LahTriangle(size)
length(CacheLah) < size && LahNumbers(size)
[CacheLah[n] for n = 0:size - 1]
end
LahNumbers(A::ℤSeq)   = LinMap(LahNumbers, A, length(A))
LahTransform(A::ℤSeq) = LahNumbers.(Telescope(A))
"""
(SIGNATURES)
"""
function FubiniNumbers(n::Int64)
haskey(CacheFubini, n) && return CacheFubini[n]
prevrow = FubiniNumbers(n - 1)
row = ZSeq(n + 1)
row[1] = 0
row[2] = 1
row[n + 1] = n * prevrow[n]
for k = 3:n
row[k] = (k - 1)*(prevrow[k - 1] + prevrow[k])
end
CacheFubini[n] = row
end
FubiniNumbers(n, k) = FubiniNumbers(n)[k + 1]
"""
(SIGNATURES)
"""
function FubiniTriangle(size)
length(CacheFubini) < size && FubiniNumbers(size)
[CacheFubini[n] for n = 0:size - 1]
end
FubiniNumbers(A::ℤSeq) = LinMap(FubiniNumbers, A, length(A))
FubiniTransform(A::ℤSeq) = FubiniNumbers.(Telescope(A))
"""
(SIGNATURES)
"""
function Delannoy(n::Int64, k::Int64)
haskey(CacheDelannoy, (n, k)) && return CacheDelannoy[(n, k)]
if n < 0 || k < 0 || k > n
return CacheDelannoy[(n,k)] = 0
end
CacheDelannoy[(n,k)] = Delannoy(n-1, k-1) + Delannoy(n-2, k-1) + Delannoy(n-1, k)
end
Delannoy(n) = [Delannoy(n, k) for k ∈ 0:n]
DelannoyTriangle(dim) = [Delannoy(n) for n = 0:dim - 1]
Delannoy(A::ℤSeq) = LinMap(Delannoy, A, length(A))
DelannoyTransform(A::ℤSeq) = Delannoy.(Telescope(A))
"""
(SIGNATURES)
"""
function AitkenNumbers(n::Int64)
haskey(CacheAitken, n) && return CacheAitken[n]
prevrow = AitkenNumbers(n - 1)
row = ZSeq(n + 1)
row[1] = prevrow[n]
for k = 2:n + 1
row[k] = row[k-1] + prevrow[k - 1]
end
CacheAitken[n] = row
end
AitkenNumbers(n, k) = AitkenNumbers(n)[k + 1]
"""
(SIGNATURES)
"""
function AitkenTriangle(size)
length(CacheAitken) < size && AitkenNumbers(size)
[CacheAitken[n] for n = 0:size - 1]
end
AitkenNumbers(A::ℤSeq) = LinMap(AitkenNumbers, A, length(A))
AitkenTransform(A::ℤSeq) = AitkenNumbers.(Telescope(A))
G271703(x, t) = exp(t * divexact(x, 1 - t))
T271703(dim) = EgfExpansionCoeff(dim, G271703)
egfBernoulli(x, t) = divexact(t * exp(x * t), exp(t) - 1)
BernoulliPolynomial(n) = EgfExpansionPoly(n, egfBernoulli)
G278075(x, t) = divexact(1, 1 - x * (1 - exp(-t)))
FubiniPolynomial(n) = EgfExpansionPoly(n, G278075)
function MotzkinTriangle2(dim::Int)
T = ZTri(dim)
for n = 1:dim
T[n][n] = 1
end
for n = 2:dim
u(k) = k == 0 || k == n ? 0 : T[n - 1][k]
for k = 1:n - 1
T[n][k] = u(k - 1) + u(k) + u(k + 1)
end
end
T
end
"""
(SIGNATURES)
"""
MotzkinTriangle(dim) = OrthoPoly(dim, n -> 1, n -> 1)
"""
(SIGNATURES)
"""
Motzkin(n) = MotzkinTriangle(n + 1)[n + 1] # TODO!!
Motzkin(n, k) = Motzkin(n)[k + 1]
Motzkin(A::ℤSeq) = LinMap(Motzkin, A, length(A))
MotzkinTransform(A::ℤSeq) = Motzkin.(Telescope(A))
"""
(SIGNATURES)
"""
CatalanTriangle(dim) = OrthoPoly(dim, n -> 0, n -> 1)
Catalan(n) = CatalanTriangle(n + 1)[n + 1] # TODO!!
Catalan(n::Int, k::Int) = Catalan(n)[k + 1]
Catalan(A::ℤSeq) = LinMap(Catalan, A, length(A))
CatalanTransform(A::ℤSeq) = Catalan.(Telescope(A))
function CatalanBallot(n::Int, k::Int)
haskey(CacheBallot, (n, k)) && return CacheBallot[(n, k)]
(k > n || k < 0) && return ZZ(0)
n == k && return ZZ(1)
CacheBallot[(n, k)] = (
CatalanBallot(n - 1, k - 1) +
CatalanBallot(n - 1, k + 1) +
(iseven(n - k) ? 0 : CatalanBallot(n - 1, k))
)
end
CatalanBallot(n) = [CatalanBallot(n, k) for k ∈ 0:n]
ExtCatalanTriangle(dim) = [[CatalanBallot(n, k) for k = 0:n] for n = 0:dim - 1]
Rencontres(n, k) = A000166(n - k)*Binomial(n, k)
RencontresTriangle(dim) = [[Rencontres(n, k) for k = 0:n] for n = 0:dim - 1]
Rencontres(A::ℤSeq) = LinMap(Rencontres, A, length(A))
RencontresTransform(A::ℤSeq) = Rencontres.(Telescope(A))
function DArcais(n, k)
haskey(CacheDArcais, (n, k)) && return CacheDArcais[(n, k)]
k == 0 && return ZZ(0^n)
S = sum(Binomial(n-1, j-1)*A038048(j)*DArcais(n-j, k-1) for j in 0:n-k+1)
CacheDArcais[(n, k)] = S
end
DArcaisTriangle(dim) = [[DArcais(n, k) for k = 0:n] for n = 0:dim - 1]
DArcais(A::ℤSeq) = LinMap(DArcais, A, length(A))
DArcaisTransform(A::ℤSeq) = DArcais.(Telescope(A))
function Worpitzky(n, k)
haskey(CacheWorpitzky, (n, k)) && return CacheWorpitzky[(n, k)]
S = sum(Eulerian(n, j)*Binomial(n-j, n-k) for j in 0:n)
CacheWorpitzky[(n, k)] = S
end
WorpitzkyTriangle(dim) = [[Worpitzky(n, k) for k = 0:n] for n = 0:dim - 1]
Worpitzky(A::ℤSeq) = LinMap(Worpitzky, A, length(A))
WorpitzkyTransform(A::ℤSeq) = Worpitzky.(Telescope(A))
"""
Return the second order Eulerian number E2(n,k).
"""
function EulerianSO2(n, k)
n < 0 && return ZZ(0)
haskey(CacheEulerianSO2, (n, k)) && return CacheEulerianSO2[(n, k)]
S = EulerianSO2(n-1, k)*k + EulerianSO2(n-1, k-1)*(2*n - k)
CacheEulerianSO2[(n, k)] = S
end
EulerianSO2Triangle(dim) = [[EulerianSO2(n, k) for k = 0:n] for n = 0:dim - 1]
EulerianSO2(A::ℤSeq) = LinMap(EulerianSO2, A, length(A))
EulerianSO2Transform(A::ℤSeq) = EulerianSO2.(Telescope(A))
function Swing(n, k)
haskey(CacheSwing, (n, k)) && return CacheSwing[(n, k)]
p = ZZ(2)^n
k == 0 && return p
S = numerator(p*prod(QQ(2,j)^((-1)^(j )) for j in 1:k))
CacheSwing[(n, k)] = S
end
SwingTriangle(dim) = [[Swing(n, k) for k = 0:n] for n = 0:dim - 1]
Swing(A::ℤSeq) = LinMap(Swing, A, length(A))
SwingTransform(A::ℤSeq) = Swing.(Telescope(A))
Uni(n, k) = 1
UniTriangle(dim) = [[ZZ(1) for k = 0:n] for n = 0:dim - 1]
Uni(A::ℤSeq) = LinMap(Uni, A, length(A))
UniTransform(A::ℤSeq) = Uni.(Telescope(A))
function Bessel1(n, k)
(k == 0) && return ZZ(k^n)
div(Factorial(2*n - k - 1),((-2)^(n - k)*Factorial(k - 1)*Factorial(n - k)))
end
Bessel1Triangle(dim) = [[Bessel1(n, k) for k = 0:n] for n = 0:dim - 1]
Bessel1(A::ℤSeq) = LinMap(Bessel1, A, length(A))
Bessel1Transform(A::ℤSeq) = Bessel1.(Telescope(A))
function Hermite(n, k)
isodd(n - k) && return ZZ(0)
m = div(n - k, 2)
div(Factorial(n), Factorial(k)*Factorial(m)*2^m)
end
HermiteTriangle(dim) = [[Hermite(n, k) for k = 0:n] for n = 0:dim - 1]
Hermite(A::ℤSeq) = LinMap(Hermite, A, length(A))
HermiteTransform(A::ℤSeq) = Hermite.(Telescope(A))
function Trinomial(n, k)
sum(Binomial(n, j) * Binomial(j, k-j) for j in 0:n)
end
TrinomialTriangle(dim) = [[Trinomial(n, k) for k = 0:n] for n = 0:dim - 1]
Trinomial(A::ℤSeq) = LinMap(Trinomial, A, length(A))
TrinomialTransform(A::ℤSeq) = Trinomial.(Telescope(A))
bs(n) = iszero(n) ? 0 : isodd(n) ? 2 : 1
SchröderBTriangle(dim) = DelehamΔ(dim, bs, n -> 0^n)
ls(n) = isodd(n) ? 2 : 1
SchröderLTriangle(dim) = DelehamΔ(dim, ls, n -> 0^n)
"""
Recurrence for A132393, StirlingCycle numbers.
"""
function R132393(n::Int, k::Int, prevrow::Function)
(k == 0 && n == 0) && return ZZ(1)
(n - 1) * prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A048993, StirlingSet numbers.
"""
function R048993(n::Int, k::Int, prevrow::Function)
(k == 0 && n == 0) && return ZZ(1)
k * prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A271703, Lah numbers.
"""
function R271703(n::Int, k::Int, prevrow::Function)
(k == 0 && n == 0) && return ZZ(1)
(k - 1 + n) * prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A094587, (Rising factorials).
"""
function R094587(n::Int, k::Int, prevrow::Function)
(k == 0 && n == 0) && return ZZ(1)
(n - k) * prevrow(k) + prevrow(k - 1)
end
"""
Recurrence for A008279. Number of permutations of n things k at a time.
(Falling factorials)
"""
function R008279(n::Int, k::Int, prevrow::Function)
(k == 0 && n == 0) && return ZZ(1)
prevrow(k) + k * prevrow(k - 1)
end
"""
xIterates over the first n rows of `A132393`.
Triangle of unsigned Stirling numbers of the first kind.
"""
I132393(n) = RecTriangle(n, R132393)
T132393(dim) = ZTri(I132393(dim))
"""
(SIGNATURES)
"""
StirlingCycleTriangle(dim) = T132393(dim)
StirlingCycle(n) = StirlingCycleTriangle(n + 1)[n + 1]
StirlingCycle(n, k) = StirlingCycle(n)[k + 1]
StirlingCycle(A::ℤSeq) = LinMap(StirlingCycle, A, length(A))
StirlingCycleTransform(A::ℤSeq) = StirlingCycle.(Telescope(A))
"""
Iterates over the first n rows of `A048993`.
Triangle of Stirling numbers of 2nd kind.
"""
I048993(n) = RecTriangle(n, R048993)
T048993(dim) = ZTri(I048993(dim))
"""
(SIGNATURES)
"""
StirlingSetTriangle(dim) = T048993(dim)
StirlingSet(n) = StirlingSetTriangle(n + 1)[n + 1]
StirlingSet(n, k) = StirlingSet(n)[k + 1]
StirlingSet(A::ℤSeq) = LinMap(StirlingSet, A, length(A))
StirlingSetTransform(A::ℤSeq) = StirlingSet.(Telescope(A))
"""
Iterates over the first n rows of `A094587`.
"""
I094587(n) = RecTriangle(n, R094587)
T094587(dim) = ZTri(I094587(dim))
"""
Iterates over the first n rows of `A008279`.
"""
I008279(n) = RecTriangle(n, R008279)
T008279(dim) = ZTri(I008279(dim))
FallingFactTriangle(dim) = ZTri(I008279(dim))
PermCoeffsTriangle(dim) = ZTri(I008279(dim))
function R225478(n, k, prevrow::Function)
(k == 0 && n == 0) && return ZZ(1)
4 * prevrow(k - 1) + (4 * n - 1) * prevrow(k)
end
I225478(dim) = RecTriangle(dim, R225478)
T225478(dim) = ZTri(I225478(dim))
function R007318(n, k, prevrow::Function)
(k == 0 && n == 0) && return ZZ(1)
prevrow(k - 1) + prevrow(k)
end
I007318(dim) = RecTriangle(dim, R007318)
PascalTriangle(dim) = ZTri(I007318(dim))
function SchroederBig(n, k)
div(
(k + 1) * sum(Binomial(n + 1, k + j + 1) *
Binomial(n + j, j) for j = 0:(n - k)),
n + 1
)
end
function SchroederBigTriangle(dim)
[[SchroederBig(n, k) for k = 0:n] for n = 0:dim - 1]
end
SchroederBig(n) = SchroederBigTriangle(n + 1)[n + 1]
SchroederBig(A::ℤSeq) = LinMap(SchroederBig, A, length(A))
SchroederBigTransform(A::ℤSeq) = SchroederBig.(Telescope(A))
FJ(n, k) = n <= 2 ? 1 : FJ(n - 1, k) + k * FJ(n - 2, k)
"""
(SIGNATURES)
"""
JacobsthalTriangle(dim) = RiordanSquare(dim, n -> FJ(n, 2))
Jacobsthal(n) = JacobsthalTriangle(n + 1)[n + 1]
Jacobsthal(n, k) = Jacobsthal(n)[k + 1]
Jacobsthal(A::ℤSeq) = LinMap(Jacobsthal, A, length(A))
JacobsthalTransform(A::ℤSeq) = Jacobsthal.(Telescope(A))
"""
(SIGNATURES)
"""
FibonacciTriangle(dim) = RiordanSquare(dim, n -> FJ(n, 1))
Fibonacci(n) = FibonacciTriangle(n + 1)[n + 1]
Fibonacci(n, k) = Fibonacci(n)[k + 1]
Fibonacci(A::ℤSeq) = LinMap(Fibonacci, A, length(A))
FibonacciTransform(A::ℤSeq) = Fibonacci.(Telescope(A))
function EulerianNumbers(n, k)
haskey(CacheEulerian, (n, k)) && return CacheEulerian[(n, k)]
CacheEulerian[(n, k)] = if (k == 0) && (n >= 0)
ZZ(1)
elseif (k < 0) || (k > n)
ZZ(0)
else
(n - k) * EulerianNumbers(n - 1, k - 1) +
(k + 1) * EulerianNumbers(n - 1, k)
end
end
"""
(SIGNATURES)
"""
EulerianTriangle(dim) = [[EulerianNumbers(n, k) for k = 0:n] for n = 0:dim - 1]
Eulerian(n) = EulerianTriangle(n + 1)[n + 1]
Eulerian(n, k) = Eulerian(n)[k + 1]
Eulerian(A::ℤSeq) = LinMap(Eulerian, A, length(A))
EulerianTransform(A::ℤSeq) = Eulerian.(Telescope(A))
function A046802(n, k)
sum(Binomial(n, j) * Eulerian(j, j-k) for j in k:n)
end
A046802Triangle(dim) = [[A046802(n, k) for k = 0:n] for n = 0:dim - 1]
A046802(A::ℤSeq) = LinMap(A046802, A, length(A))
A046802Transform(A::ℤSeq) = A046802.(Telescope(A))
function EulerianNumbersClassic(n, k)
haskey(CacheEulerianClassic, (n, k)) && return CacheEulerianClassic[(n, k)]
CacheEulerianClassic[(n, k)] = if (k == n)
ZZ(1)
elseif (k <= 0) || (k > n)
ZZ(0)
else
(n - k + 1) * EulerianNumbersClassic(n - 1, k - 1) +
(k) * EulerianNumbersClassic(n - 1, k)
end
end
EulerianTriangleClassic(dim) = [[EulerianNumbersClassic(n, k) for k = 0:n] for n = 0:dim - 1]
EulerianClassic(n) = EulerianTriangleClassic(n + 1)[n + 1]
EulerianClassic(n, k) = EulerianClassic(n)[k + 1]
EulerianClassic(A::ℤSeq) = LinMap(EulerianClassic, A, length(A))
EulerianTransform2(A::ℤSeq) = EulerianClassic.(Telescope(A))
function NarayanaNumbers(n::Int, k::Int)
haskey(CacheNarayana, (n, k)) && return CacheNarayana[(n, k)]
CacheNarayana[(n, k)] = if (k == n)
ZZ(1)
elseif k > n || k <= 0
ZZ(0)
else div(
Binomial(ZZ(n), n - k) * Binomial(ZZ(n - 1), n - k),
ZZ(n - k + 1)
)
end
end
"""
(SIGNATURES)
"""
NarayanaTriangle(dim) = [[NarayanaNumbers(n, k) for k = 0:n] for n = 0:dim]
Narayana(n) = NarayanaTriangle(n + 1)[n + 1]
Narayana(n, k) = Narayana(n)[k + 1]
Narayana(A::ℤSeq) = LinMap(Narayana, A, length(A))
NarayanaTransform(A::ℤSeq) = Narayana.(Telescope(A))
A343237(n, k) = ZZ(k + 1)^(n - k + 1) - ZZ(k)^(n - k + 1)
A343237Triangle(dim) = [[A343237(n, k) for k = 0:n] for n = 0:dim - 1]
A343237(A::ℤSeq) = LinMap(A343237, A, length(A))
A343237Transform(A::ℤSeq) = A343237.(Telescope(A))
"""
(SIGNATURES)
"""
RisingFactTriangle(dim) = [[rising_factorial(ZZ(n), ZZ(k)) for k = 0:n] for n = 0:dim]
RisingFact(n) = RisingFactTriangle(n + 1)[n + 1]
RisingFact(n, k) = RisingFact(n)[k + 1]
RisingFact(A::ℤSeq) = LinMap(RisingFact, A, length(A))
RisingFactTransform(A::ℤSeq) = RisingFact.(Telescope(A))
function Laguerre(n::Int)
haskey(CacheLaguerre, n) && return CacheLaguerre[n]
prevrow = Laguerre(n - 1)
row = ZSeq(n + 1)
row[n + 1] = ZZ(1)
for k ∈ 1:n
row[k] = (  get(prevrow, k - 1, 0)
+ get(prevrow, k, 0) * (2 * k - 1)
+ get(prevrow, k + 1, 0) * k^2 )
end
CacheLaguerre[n] = row
end
Laguerre(n::Int, k::Int) = Laguerre(n)[k + 1]
Laguerre(A::ℤSeq) = LinMap(Laguerre, A, length(A))
"""
(SIGNATURES)
"""
function LaguerreTriangle(size::Int)
length(CacheLaguerre) < size && Laguerre(size)
[CacheLaguerre[n] for n in 0:size - 1]
end
LaguerreTransform(A::ℤSeq) = Laguerre.(Telescope(A))
function transforms(trans)
len = 18
println("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
println(trans)
println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
println()
Unos = [ZZ(1) for n = 0:len]
print("UNOS ")
Println(Unos)
trans(Unos) |> Println
println()
AltUnos = [(-1)^n * ZZ(1) for n = 0:len]
println("ALTS ")
Println(AltUnos)
trans(AltUnos) |> Println
println()
Squares = [ZZ(n^2) for n = 0:len]
println("SQRS ")
Println(Squares)
trans(Squares) |> Println
println()
Nat = [ZZ(n) for n = 0:len]
println("N0TS ")
Println(Nat)
trans(Nat) |> Println
println()
Nut = [ZZ(n) for n = 1:len]
println("NATS ")
Println(Nut)
trans(Nut) |> Println
end
const TRIANGLES = Function[
AitkenTriangle,
Bessel1Triangle,
BinomialTriangle,
CatalanTriangle,
DArcaisTriangle,
DelannoyTriangle,
EulerianTriangle,
EulerianSO2Triangle,
EulerSecTriangle,
EulerTanTriangle,
EulerTriangle,
FallingFactTriangle,
FibonacciTriangle,
FineTriangle,
FubiniTriangle,
HermiteTriangle,
LaguerreTriangle,
LahTriangle,
MotzkinTriangle,
NarayanaTriangle,
RencontresTriangle,
RisingFactTriangle,
SchröderBTriangle,
SchröderLTriangle,
StirlingCycleTriangle,
StirlingSetTriangle,
TrinomialTriangle,
TTreeTriangle,
UniTriangle,
WorpitzkyTriangle
]
# *** TrianglesExplorer.jl ****************
"""
Explore integer triangles via their traits.
The traits and their OEIS references will be saved
as csv-files in the data directory.
"""
const ModuleTrianglesExplorer = ""
const WARNING_ON_NOTFOUND = false
const Kind = ["Std", "Rev", "Inv", "RevInv", "InvRev"]
const Triangles = LittleDict{String, Function}(
"Aitken"        => AitkenTriangle,
"Bessel1"       => Bessel1Triangle,
"Binomial"      => BinomialTriangle,
"Catalan"       => CatalanTriangle,
"DArcais"       => DArcaisTriangle,
"Delannoy"      => DelannoyTriangle,
"Euler"         => EulerTriangle,
"EulerS"        => EulerSecTriangle,
"EulerT"        => EulerTanTriangle,
"Eulerian"      => EulerianTriangle,
"EulerianSO2"   => EulerianSO2Triangle,
"FallingFact"   => FallingFactTriangle,
"Fibonacci"     => FibonacciTriangle,
"Fine"          => FineTriangle,
"Fubini"        => FubiniTriangle,
"Hermite"       => HermiteTriangle,
"Laguerre"      => LaguerreTriangle,
"Lah"           => LahTriangle,
"Motzkin"       => MotzkinTriangle,
"Narayana"      => NarayanaTriangle,
"RisingFact"    => RisingFactTriangle,
"SchroederB"    => SchröderBTriangle,
"SchroederL"    => SchröderLTriangle,
"StirlingCycle" => StirlingCycleTriangle,
"StirlingSet"   => StirlingSetTriangle,
"Rencontres"    => RencontresTriangle,
"Trinomial"     => TrinomialTriangle,
"TTree"         => TTreeTriangle,
"Uni"           => UniTriangle,
"Worpitzky"     => WorpitzkyTriangle
)
const TraitFunc = LittleDict{String, Function}(
"Triangle"   => Flat,
"Reverse"    => Reverse,
"Inverse"    => Inverse,
"RevInv"     => RevInv,
"InvRev"     => InvRev,
"DiagTri"    => DiagTri,
"PolyTri"    => PolyTri,
"Sum"        => sum,
"EvenSum"    => EvenSum,
"OddSum"     => OddSum,
"AltSum"     => AltSum,
"DiagSum"    => DiagSum,
"Middle"     => Middle,
"Central"    => Central,
"LeftSide"   => LeftSide,
"RightSide"  => RightSide,
"PosHalf"    => PosHalf,
"NegHalf"    => NegHalf,
"PolyVal2"   => PolyVal2,
"PolyVal3"   => PolyVal3,
"PolyValn"   => PolyValn,
"BinConv"    => BinomialTransform,
"IBinConv"   => IBinomialTransform,
"TransSqrs"  => TransSqrs,
"TransNat0"  => TransNat0,
"TransNat1"  => TransNat1
)
function Show(io, name, kind, trait, seq, savetofile=false)
seq == [] && return []
if typeof(seq) === ℤTri
seq = Flat(seq)
end
anum = GetSeqnum(seq, WARNING_ON_NOTFOUND)
if savetofile
print(".")
print(io, anum, ",", name, ",", kind, ",", trait, ",")
Println(io, SeqToString(seq, 10))
else
print(anum, " ", name, " ", kind, " ", trait, " ")
Println(IOContext(stdout), SeqToString(seq, 10))
end
end
function TriangleVariant(Tri, dim, kind="Std")
if ! (kind in Kind)
@warn("No valid kind!")
return []
end
M = Tri(dim)
kind == "Std" && return M
kind == "Rev" && return reverse.(M)
kind == "InvRev" && (M = reverse.(M))
invM = InverseTriangle(M)
(kind == "Inv" || kind == "InvRev"
|| invM == []) && return invM
return reverse.(invM)
end
const LEN = 19
function Inspect(triangle, kind, trait, dim, oeissearch=false)
T = TriangleVariant(Triangles[triangle], dim, kind)
seq = TraitFunc[trait](T)
seq == [] && return []
if typeof(seq) === ℤTri
Println.(seq)
seq = Flat(seq)
end
anum = GetSeqnum(seq, oeissearch)
println(anum, " ", triangle, " ", kind, " ", trait)
println(SeqToString(seq))
end
"""
(SIGNATURES)
"""
function Explore(triangle, kind, trait, dim)
T = TriangleVariant(Triangles[triangle], dim, kind)
seq = TraitFunc[trait](T)
Show(stdout, triangle, kind, trait, seq)
end
function Explore(T::ℤTri, trait::Function)
seq = trait(T)
if seq == [] || seq === nothing
return []
end
typeof(seq) === ℤTri && (seq = Flat(seq))
println("$trait ")
anum = GetSeqnumUri(seq)
anum === nothing && (anum = "nothing")
seqstr = string(seq[1:min(10, end)])[max(0,11):max(0,end-1)]
String["$trait ", anum, seqstr]
end
"""

"""
function Explore(triangle, kind, trait)
dim = 19
T = TriangleVariant(Triangles[triangle], dim, kind)
seq = TraitFunc[trait](T)
anum = GetSeqnum(seq)
anum === nothing && (anum = "nothing")
seqstr = string(seq[1:min(10, end)])[max(0,11):max(0,end-1)]
String[anum, triangle, kind, trait, seqstr]
end
function Explore(trait::String, dim::Int)
for (name, triangle) in Triangles
for kind in Kind
T = TriangleVariant(triangle, dim, kind)
if T != []
seq = TraitFunc[trait](T)
Show(stdout, name, kind, trait, seq)
end
end
end
end
function Explore(savetofile::Bool)
@warn "This will take several minutes and produce the file 'BIGLIST.csv' in the data directory."
path = datapath("BIGLIST.csv")
rm(path; force=true)
open(path, "w") do io
println(io, "Anumber,Triangle,Type,Trait,Sequence")
for (name, triangle) in Triangles
kind = "Std"
T = TriangleVariant(triangle, LEN, kind)
if T != []
for (trait, f) in TraitFunc
Show(io, name, kind, trait, f(T), savetofile)
end
end
end
end
end
function SaveToCSV(name)
T = Triangles[name](LEN)
path = datapath(name * ".csv")
rm(path; force=true)
open(path, "w") do io
println(io, "Trait,ANumber,Sequence")
for (trait, f) in TraitFunc
seq = f(T)
seq == [] && continue
if typeof(seq) === ℤTri
seq = Flat(seq)
end
anum = GetSeqnum(seq)
anum === nothing && (anum = "nothing")
println(io, trait, ",", anum, ",", SeqToString(seq, 10))
end
end
end
function SaveAllToCSV()
@warn "This will take several minutes and produce the csv-files in the data directory."
for (name, f) in Triangles
println(name)
SaveToCSV(name)
end
end
# *** TrianglesPlot.jl ****************
"""
Plotting triangles.
"""
const ModuleTrianglesPlot = ""
function PolyPlot(F, title)
x = -2.0:0.05:1.0
plot(x,  F[1], c="black",  linewidth=1)
plot!(x, F[2], c="green",  line=:dash)
plot!(x, F[3], c="blue",   linewidth=1)
plot!(x, F[4], c="red",    line=:dash)
plot!(x, F[5], c="orange", linewidth=1)
plot!(x, F[6], c="blue",   line=:dash)
plot!(x, F[7], c="black",  linewidth=1)
title!(title)
xlabel!("x")
ylabel!("p(x)")
P = plot!(xlims=(-2.0, 1.0), ylims=(-5.0, 5.0))
display(P)
end
# *** TrianglesTables.jl ****************
"""
Pretty printing of triangles trait cards.

| A-number | Triangle   | Form | Function  | Sequence                                    |
|----------|------------|------|-----------|---------------------------------------------|
| A000302  | Binomial   | Std  | PolyVal3  | 1, 4, 16, 64, 256, 1024, 4096, 16384        |
| A001333  | SchroederB | Inv  | AltSum    | 1, -1, 3, -7, 17, -41, 99, -239             |
| A006012  | SchroederL | Inv  | AltSum    | 1, -2, 6, -20, 68, -232, 792, -2704         |
| A026302  | Motzkin    | Rev  | Central   | 1, 2, 9, 44, 230, 1242, 6853, 38376         |
| A103194  | Laguerre   | Std  | TransNat0 | 0, 1, 6, 39, 292, 2505, 24306, 263431       |
| A111884  | Lah        | Std  | TransAlts | 1, -1, -1, -1, 1, 19, 151, 1091             |
| nothing  | Laguerre   | Rev  | TransNat1 | 1, 3, 15, 97, 753, 6771, 68983, 783945      |
"""
const ModuleTrianglesTables = ""
const CSS = ["<style> table, td, th {border-collapse: collapse; font-family: sans-serif; color: blue;}",
"td, th {border-bottom: 0; padding: 4px}",
"tr:nth-child(odd) {background: #eee;}", "tr:nth-child(even) {background: #fff;}",
"tr.header {background: orange !important; color: white; font-weight: bold;}",
"tr.subheader {background: lightgray !important; color: black;}",
"tr.headerLastRow {border-bottom: 2px solid black;}",
"th.rowNumber, td.rowNumber {text-align: right;} </style><body>"]
function PrettyTraits(T, name)
header = [ "Trait", "ANumber", "Sequence" ]
mat = String[name "" ""]
for tra in TRAITS
s = Explore(T, tra)
s == [] && continue
mat = [mat; reshape(s, 1, 3)]
end
path = datapath(name * ".md")
rm(path; force=true)
open(path, "w") do io
println(io, "```@raw html")
for ln in CSS
println(io, ln)
end
pretty_table(io, mat, header, backend = :html, standalone= false, alignment=[:l,:l,:l])
println(io, "```")
end
end
function AllPrettyTraits()
for T in Triangles
println("-- ", T[1])
PrettyTraits(Triangles[T[1]](32), T[1])
end
end
function AllCSVtoTable()
header = ["ANumber", "Triangle", "Type", "Trait", "Sequence"]
mat = String["" "" "" "" ""]
inpath = datapath("BIGLIST.csv")
open(inpath, "r") do csv
for L ∈ eachline(csv, keep=false)
mat = [mat; reshape(split(L, ","), 1, 5)]
end
end
outpath = datapath("BIGLISTTEST.md")
rm(outpath; force=true)
open(outpath, "w") do html
println(html, "```@raw html")
for ln in CSS
println(html, ln)
end
pretty_table(html, mat, header, backend = :html, standalone= false,
alignment=[:l,:l,:l,:l,:l])
println(html, "```")
end
end
function CSVtoTable(name)
header = ["Trait", "ANumber", "Sequence"]
p = splitext(name)[begin]
p = splitpath(p)[end]
mat = String[p "" ""]
firstline = true
inpath = profilespath(name)
open(inpath, "r") do csv
for L ∈ eachline(csv, keep=false)
firstline && (firstline = false; continue)
s = split(L, ",")
s[2] = MakeSeqUri(s[2], s[3])
mat = [mat; reshape(s, 1, 3)]
end
end
outpath = docsrcpath(p * ".md")
rm(outpath; force=true)
open(outpath, "w") do html
println(html, "```@raw html")
for ln in CSS
println(html, ln)
end
pretty_table(html, mat, header, backend = :html, standalone= false, alignment=[:l,:l,:l])
println(html, "```")
end
end
function UpdateDocs()
csvfiles = csv_files()
for filename ∈ csvfiles
fulname = profilespath(filename)
CSVtoTable(fulname)
end
end
# *** TrianglesTraitCard.jl ****************
"""
Build the trait card of one or many triangles with Anums.
```
julia> TraitCard(LaguerreTriangle)
Triangle:  A021009 [1, 1, 1, 2, 4, 1, 6, 18, 9, 1]
Reverse:   A021010 [1, 1, 1, 1, 4, 2, 1, 9, 18, 6]
Inverse:   A021009 [1, -1, 1, 2, -4, 1, -6, 18, -9, 1]
RevInv:    A021010 [1, 1, -1, 1, -4, 2, 1, -9, 18, -6]
Diagonal:  A084950 [1, 1, 2, 1, 6, 4, 24, 18, 1, 120]
Sum:       A002720 [1, 2, 7, 34, 209, 1546, 13327, 130922, 1441729, 17572114]
EvenSum:   A331325 [1, 1, 3, 15, 97, 745, 6571, 65359, 723969, 8842257]
OddSum:    A331326 [0, 1, 4, 19, 112, 801, 6756, 65563, 717760, 8729857]
AltSum:    A009940 [1, 0, -1, -4, -15, -56, -185, -204, 6209, 112400]
DiagSum:   C001040 [1, 1, 3, 10, 43, 225, 1393, 9976, 81201, 740785]
Central:   A295383 [1, 4, 72, 2400, 117600, 7620480, 614718720, 59364264960]
LeftSide:  A000142 [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]
RightSide: A000012 [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
PosHalf:   A025167 [1, 3, 17, 139, 1473, 19091, 291793, 5129307, 101817089]
NegHalf:   A025166 [1, -1, 1, 7, -127, 1711, -23231, 334391, -5144063, 84149983]
TransUnos: A002720 [1, 2, 7, 34, 209, 1546, 13327, 130922, 1441729, 17572114]
TransSqrs: A105219 [0, 1, 8, 63, 544, 5225, 55656, 653023, 8379008, 116780049]
TransNat0: A103194 [0, 1, 6, 39, 292, 2505, 24306, 263431, 3154824, 41368977]
TransNat1: C000262 [1, 3, 13, 73, 501, 4051, 37633, 394353, 4596553, 58941091]
PolyVal2:  A087912 [1, 3, 14, 86, 648, 5752, 58576, 671568, 8546432, 119401856]
PolyVal3:  A277382 [1, 4, 23, 168, 1473, 14988, 173007, 2228544, 31636449]
```
"""
const ModuleTrianglesTraitCard = ""
const SEARCHONLINE = false # true
function TraitCard(T::ℚTri, name)
@warn "WRONG TYPE! Will not process!"
end
"""
(SIGNATURES)
"""
function Traits(T::ℤTri, name, N=10, an=false)
an = an && ! oeis_notinstalled()
sep = " "
println("\n=================")
println(name)
println()
len = max(N, min(N - 1, length(T)))
len = len < 10 ? len : 72
S = Flat(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "Triangle", sep, SeqToFixlenString(S, len))
R = Reverse(T); S = Flat(R)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "Reverse", sep, SeqToFixlenString(S, len))
I = Inverse(T)
if I != []
S = Flat(I);
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "Inverse", sep, SeqToFixlenString(S, len))
RI = Reverse(I); S = Flat(RI);
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "RevInv", sep, SeqToFixlenString(S, len))
end
IR = Inverse(R)
if IR != []
S = Flat(IR);
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "InvRev", sep, SeqToFixlenString(S, len))
end
S = DiagonalTriangle(T); S = Flat(S)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "DiagTri", sep, SeqToFixlenString(S, len))
S = ConvolutionTriangle(T); S = Flat(S)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "ConvTri", sep, SeqToFixlenString(S, len))
S = PolyTriangle(T); S = Flat(S)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "PolyTri", sep, SeqToFixlenString(S, len))
S = sum(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "Sum", sep, SeqToFixlenString(S, len))
S = EvenSum(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "EvenSum", sep, SeqToFixlenString(S, len))
S = OddSum(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "OddSum", sep, SeqToFixlenString(S, len))
S = AltSum(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "AltSum", sep, SeqToFixlenString(S, len))
S = DiagSum(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "DiagSum", sep, SeqToFixlenString(S, len))
S = Middle(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "Middle", sep, SeqToFixlenString(S, len))
S = Central(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "Central", sep, SeqToFixlenString(S, len))
S = LeftSide(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "LeftSide", sep, SeqToFixlenString(S, len))
S = RightSide(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "RightSide", sep, SeqToFixlenString(S, len))
P = Polynomial(T)
S = PosHalf(P)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "PosHalf", sep, SeqToFixlenString(S, len))
S = NegHalf(P)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "NegHalf", sep, SeqToFixlenString(S, len))
S = BinConv(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "BinConv", sep, SeqToFixlenString(S, len))
S = IBinConv(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "IBinConv", sep, SeqToFixlenString(S, len))
S = Trans(T, [ZZ(n^2) for n = 0:19])
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "TransSqrs", sep, SeqToFixlenString(S, len))
S = Trans(T, [ZZ(n) for n = 0:19])
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "TransNat0", sep, SeqToFixlenString(S, len))
S = Trans(T, [ZZ(n) for n = 1:19])
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "TransNat1", sep, SeqToFixlenString(S, len))
S = PolyVal2(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "PolyVal2", sep, SeqToFixlenString(S, len))
S = PolyVal3(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "PolyVal3", sep, SeqToFixlenString(S, len))
S = PolyValn(T)
anum = an ? GetSeqnum(S, SEARCHONLINE) : ""
println(anum, sep, name, sep, "PolyValn", sep, SeqToFixlenString(S, len))
end
"""
Print the standard traits generated by linear transformation.
```
julia> TransTraits(BinomialTriangle)
BinomialTriangle
TransSqrs: A001788 [0, 1, 6, 24, 80, 240, 672, 1792, 4608, 11520]
TransNat0: A001787 [0, 1, 4, 12, 32, 80, 192, 448, 1024, 2304]
TransNat1: A001792 [1, 3, 8, 20, 48, 112, 256, 576, 1280, 2816]
```
"""
function TransTraits(triangle::Function)
dim = 19
T = triangle(dim)
println("$triangle ")
len = min(10, min(dim - 1, length(T)))
S = Trans(T, [ZZ(1) for n = 0:dim]); anum = GetSeqnum(S, false)
print("TransUnos: ", anum, " "); S[1:len] |> Println
S = Trans(T, [ZZ((-1)^n) for n = 0:dim]); anum = GetSeqnum(S, false)
print("TransAlts: ", anum, " "); S[1:len] |> Println
S = Trans(T, [ZZ(n^2) for n = 0:dim]);    anum = GetSeqnum(S, false)
print("TransSqrs: ", anum, " "); S[1:len] |> Println
S = Trans(T, [ZZ(n) for n = 0:dim]);      anum = GetSeqnum(S, false)
print("TransNat0: ", anum, " "); S[1:len] |> Println
S = Trans(T, [ZZ(n) for n = 1:dim]);      anum = GetSeqnum(S, false)
print("TransNat1: ", anum, " "); S[1:len] |> Println
println()
end
"""
(SIGNATURES)
"""
function AssociatedTriangles(T::ℤTri, N)
len = max(N, min(N - 1, length(T)))
println("\nTriangle:\n")
for row in T[1:len] Println(row) end
println()
print("Flat triangle:  ");
S = Flat(T);
S[1:len] |> Println
println("\nInverse triangle:\n")
S = InverseTriangle(T)
if S != []
for row in S[1:len] Println(row) end
println()
S = Flat(S); print("Inverse:  "); S[1:len] |> Println
end
println("\nDiagonal triangle:\n")
S = DiagonalTriangle(T)
Println.(S[1:len])
println()
S = Flat(S); print("Diagonal:  "); S[1:len] |> Println
println("\nPolynomial values, array:\n")
Println.(PolyArray(T))
println("\nPolynomial values, triangle:\n")
S = PolyTriangle(T)
Println.(S[1:len])
println()
S = Flat(S); print("PolyVal:  "); S[1:len] |> Println
end
"""
Prints a list of traits of the triangle with Anums.
"""
function TraitCard(triangle::Function)
dim = 19
T = triangle(dim)
name = "$triangle"
c = findfirst("Triangle", name)
Traits(T, name[1:c[1]-1], dim, true)
end
"""
Prints a list of traits of the triangle without Anums
and, if assoc=true, also a list of associated triangles.
"""
function TraitCard(triangle::Function, dim, assoc=false)
T = triangle(dim)
name = "$triangle"
c = findfirst("Triangle", name)
Traits(T, name[1:c[1]-1], dim)
AssociatedTriangles(T, dim)
end
function AllTraitCards()
for T in TRIANGLES
TraitCard(T)
end
end
# *** TrianglesUtils.jl ****************
"""
* Search the OEIS for a sequence.
* Save a file in the 'data' directory in json format.

* Triangle in standard format (without typeinfo):
```
[1]
[1, 1]
[2, 4, 1]
[6, 18, 9, 1]
[24, 96, 72, 16, 1]
[120, 600, 600, 200, 25, 1]
[720, 4320, 5400, 2400, 450, 36, 1]
```
* Triangle as a nested array:
```
[[1], [1, 1], [2, 4, 1], [6, 18, 9, 1], [24, 96, 72, 16, 1], [120, 600, 600, 200, 25, 1], [720, 4320, 5400, 2400, 450, 36, 1]]
```
* Triangle in flattened format:
```
1, 1, 1, 2, 4, 1, 6, 18, 9, 1, 24, 96, 72, 16, 1, 120, 600, 600, 200, 25, 1, 720, 4320, 5400, 2400, 450, 36, 1,
```
* Triangle in info format with sequence ID:
```
[A021009] 1, 1, 1, 2, 4, 1, 6, 18, 9, 1, 24, 96, 72, 16, 1, ...
```
* Triangle in mapped format:
```
0 ↦ 1
1 ↦ 1
2 ↦ 1
3 ↦ 2
4 ↦ 4
5 ↦ 1
6 ↦ 6
7 ↦ 18
8 ↦ 9
9 ↦ 1
```
"""
const ModuleTrianglesUtils = ""
const srcdir = realpath(joinpath(dirname(@__FILE__)))
const ROOTDIR = dirname(srcdir)
const datadir = joinpath(ROOTDIR, "data")
const profilesdir = joinpath(ROOTDIR, "profiles")
const docsdir = joinpath(ROOTDIR, "docs")
const docsrcdir = joinpath(docsdir, "src")
datapath(name) = joinpath(datadir, name)
profilespath(name) = joinpath(profilesdir, name)
docsrcpath(name) = joinpath(docsrcdir, name)
oeis_file() = joinpath(datadir, "stripped")
is_oeis_installed() = isfile(oeis_file())
csv_files() = filter!(s -> occursin(r"\.csv$", s), readdir(profilesdir))
function oeis_notinstalled()
if !is_oeis_installed()
@warn("OEIS data not installed! Download stripped.gz from oeis.org,")
@warn("expand it and put it in the directory ../data.")
return true
end
return false
end
function search_failed(name)
for ln ∈ eachline(name)
occursin("greeting", ln) && continue
occursin("{", ln) && continue
occursin("start", ln) && continue
if occursin("\"results\": null", ln)
return true
end
end
false
end
"""
Search the OEIS for a sequence. The file is saved in the 'data' directory in json format.
"""
function oeis_search(seq)
seqstr = AbsSeqToString(seq[1:min(end,12)])
filename = joinpath(datadir, seqstr[1:min(end,12)] * ".json")
uristr = "https://oeis.org/search?q=" * seqstr * "&go=Search&fmt=json"
url = HTTP.URI(uristr)
tries = 3
r = nothing
for i = 1:tries
try
r = HTTP.get(url; readtimeout = 2)
getfield(r, :status) == 200 && break
getfield(r, :status) == 302 && break
catch e
@warn(e)
end
sleep(2)
end
if !(r === nothing) && getfield(r, :status) == 200
open(filename, "w") do f
write(f, getfield(r, :body))
end
@info("Dowloaded " * basename(filename) * " to " * datadir)
else
if r === nothing
@warn("Search did not succeed, connection timed out.\n")
else
@warn("Search did not succeed, $url\nStatus: $(getfield(r, :status))")
end
end
filename
end
function AbsSeqToString(seq::ℤSeq, max=100)
separator = ","
str = ""
c = 1
for trm in seq
str *= string(abs(trm)) * separator
c += 1
c > max && break
end
str
end
function SeqToString(seq::ℤSeq, max::Int=100)
separator = " "
str = "["
c = 1
for trm in seq
str *= string(trm) * separator
c += 1
c > max && break
end
str * "]"
end
function SeqToFixlenString(seq::ℤSeq, maxlen::Int=72)
separator = " "
str = "["
len = 3
for trm in seq
s = string(trm) * separator
len += length(s)
len > maxlen && break
str *= s
end
str * "]"
end
const minlen = 30  # fragil! do not reduce!
function GetSeqnum(seq::ℤSeq)
str = AbsSeqToString(seq)
soff = 1; loff = 10
for ln ∈ eachline(oeis_file())
ln[1] == '#' && continue
l = replace(ln, "-" => "")
m = min(length(str), length(l))
len = min(length(str), length(l)-loff) - 1
len < minlen && continue
cmp(str[soff:soff+len], l[loff:loff+len]) == 0 && return ln[1:7]
end
toff = findfirst(",", str)[1] + 1
for ln ∈ eachline(oeis_file())
ln[1] == '#' && continue
l = replace(ln, "-" => "")
len = min(length(str)-toff, length(l)-loff) - 1
len < minlen && continue
cmp(str[toff:toff+len], l[loff:loff+len]) == 0 && return "A" * ln[2:7]
end
for ln ∈ eachline(oeis_file())
ln[1] == '#' && continue
l = replace(ln, "-" => "")
ff = findnext(",", l, 10)
ff === nothing && continue
loff = ff[1] + 1; soff = 1
len = min(length(str)-soff, length(l)-loff) - 1
len < minlen && continue
cmp(str[soff:soff+len], l[loff:loff+len]) == 0 && return "A" * ln[2:7]
len = min(length(str)-toff, length(l)-loff) - 1
len < minlen && continue
cmp(str[toff:toff+len], l[loff:loff+len]) == 0 && return "A" * ln[2:7]
end
nothing
end
GetSeqnum(seq::ℤTri) = GetSeqnum(Flat(seq))
function GetSeqnum(seq, search=false)
if seq === nothing || seq == []
return nothing
end
anum = GetSeqnum(seq)
if search
if anum === nothing
filename = oeis_search(seq)
if ! search_failed(filename)
println()
Println(seq[1:min(18, end)])
@warn "Not found in the local base but possibly in the OEIS!"
println()
end
end
end
anum
end
function GetSeqnumUri(seq::ℤSeq, len=10)
anum = GetSeqnum(seq)
if anum === nothing
s = SeqToFixlenString(seq, 72)
return "<a href='" * "https://oeis.org/?q=" * s * "'>" * "nomatch</a>"
end
uri = joinpath("https://oeis.org/", anum)
return "<a href='" * uri * "'>" * anum * "</a>"
end
function MakeSeqUri(anum, seq)
if anum == "nothing"
return "<a href='" * "https://oeis.org/?q=" * seq * "'>" * "nomatch</a>"
end
uri = joinpath("https://oeis.org/", anum)
return "<a href='" * uri * "'>" * anum * "</a>"
end
function print_without_type(io, v::AbstractVector)
print(io, "[")
for (i, el) ∈ enumerate(v)
i > 1 && print(io, ", ")
print(io, el)
end
println(io, "]")
end
"""
Print the array ``A`` in the format ``n ↦ A[n]`` for n in the given range.
"""
function MappedShow(A::Array, R::AbstractRange, offset=0)
for k ∈ R
if isassigned(A, k)
println(k + offset, " ↦ ", A[k])
else
println(k + offset, " ↦ ", "undef")
end
end
end
"""
Print an integer triangle without typeinfo.
"""
function Show(T::ℤTri, format="std")
if format == "std"  # default
for t in T[1:min(10, end)]
print_without_type(IOContext(stdout), t)
end
elseif format == "nest"
T |> println
elseif format == "flat"
for n in Flat(T)
print(n, ", ")
end
elseif format == "map"
offset = -1 # Julia is unfortunately 1-based.
for n in T
MappedShow(n, 1:length(n), offset)
offset += length(n)
end
elseif format == "info"
S = Flat(T)
print("[", GetSeqnum(S), "] ")
for t in T[1:min(5, end)], k in t
print(k, ", ")
end
println("... ")
end
end
const devdoc = "https://openlibmathseq.github.io/IntegerTriangles.jl/dev/#IntegerTriangles."
devdoctrait(trait) =  "<a href='" * devdoc * trait * "'>" * trait * "</a>"
end


#=

  From https://reference.wolfram.com/language/ref/BinomialDistribution.html
  """
  A basketball player has a free-throw percentage of 0.75.

  Find the probability that the player hits 2 out of 3 free throws in a game.
  [free_throwIs2]
    Answer: 0.421875

  Find the probability that the player hits the last 2 of 5 free throws.
  [q1]
     Answer: 0.00878906

  Find the expected number of hits in a game with n free throws.
  [q2]
     Answer: 0.75 n
  """

  This model don't find the answer on q1 since the probability is too small...

  q2:  0.7504

  Distributions of variable free_throwIs2 (num:0)
  0.00000 =>   57552  (0.575520)
  1.00000 =>   42448  (0.424480)
  Distributions of variable q1 (num:0)
  0.00000 =>  100000  (1.000000)
  Distributions of variable q2 (num:0)
  0.80000 =>   27864  (0.278640)
  0.70000 =>   25049  (0.250490)
  0.90000 =>   18829  (0.188290)
  0.60000 =>   14636  (0.146360)
  0.50000 =>    5906  (0.059060)
  1.00000 =>    5803  (0.058030)
  0.40000 =>    1606  (0.016060)
  0.30000 =>     269  (0.002690)
  0.20000 =>      37  (0.000370)
  0.10000 =>       1  (0.000010)

  Cf ~/webppl/binomial_basketball.wppl

=#

using Turing
include("jl_utils.jl")

@model function binomial_basketball()

    p = 0.75
    n = 2

    # Find the probability that the player hits 2 out of 3 free throws in a game.
    free_throw ~ Binomial(3,p)

    free_throwIs2 ~ Dirac(free_throw == 2)

    # Find the probability that the player hits the last 2 of 5 free throws.
    free_throw2a ~ Binomial(3,p) # This should be 0
    free_throw2b ~ Binomial(2,p) # This should be 2

    # Find the expected number of hits in a game with n free throws.
    free_throw3 ~ Binomial(10,p)

    q1 ~ Dirac(free_throw2a == 0 & free_throw2b == 2)
    q2 ~ Dirac(free_throw3 / 10.0)

end

model = binomial_basketball()

num_chains = 4
# chs = sample(model, Prior(), 1000)
# chs = sample(model, MH(), 10_000)
# chs = sample(model, PG(15), 40_000)
# chs = sample(model, IS(), 50_000)
chs = sample(model, SMC(), 100_000)
# chs = sample(model, SMC(), MCMCThreads(), 10_000, num_chains)

# chs = sample(model, SGLD(), 10_000)
# chs = sample(model,NUTS(), 10_000)
# chs = sample(model,HMC(0.01,5), 10_000)

display(chs)
# display(plot(chs))

show_var_dist_pct(chs,:free_throwIs2)
show_var_dist_pct(chs,:q1)
show_var_dist_pct(chs,:q2)