text:
function parseKillerJSON(json_sums)
    sums = []
    for s in json_sums
        indices = Tuple[]
        for ind in s["indices"]
            push!(indices, tuple(ind...))
        end

        if haskey(s, "color")
            push!(sums, (result = s["result"], indices = indices, color = s["color"]))
        else
            push!(sums, (result = s["result"], indices = indices, color = "white"))
        end
    end
    return sums
end

@testset "Killer Sudoku" begin

    @testset "Killer Sudoku from wikipedia" begin
        m = Model(CSJuMPTestOptimizer())
        @variable(m, 1 <= x[1:9, 1:9] <= 9, Int)

        sums = parseKillerJSON(JSON.parsefile("data/killer_wikipedia"))

        for s in sums
            @constraint(m, sum([x[ind[1], ind[2]] for ind in s.indices]) == s.result)
        end

        # sudoku constraints
        for rc in 1:9
            @constraint(m, x[rc, :] in CS.AllDifferentSet())
            @constraint(m, x[:, rc] in CS.AllDifferentSet())
        end
        for br in 0:2
            for bc in 0:2
                @constraint(
                    m,
                    vec(x[(br * 3 + 1):((br + 1) * 3), (bc * 3 + 1):((bc + 1) * 3)]) in
                    CS.AllDifferentSet()
                )
            end
        end

        optimize!(m)

        com = CS.get_inner_model(m)
        @test com.info.n_constraint_types.alldifferent == 27
        @test com.info.n_constraint_types.equality == length(sums)
        @test length(com.search_space) == 81

        @test JuMP.termination_status(m) == MOI.OPTIMAL
        @test jump_fulfills_sudoku_constr(JuMP.value.(x))

        for s in sums
            @test s.result == sum(JuMP.value.([x[i[1], i[2]] for i in s.indices]))
        end
    end

    @testset "Killer Sudoku from wikipedia with normal rules" begin
        m = Model(CSJuMPTestOptimizer())
        @variable(m, 1 <= x[1:9, 1:9] <= 9, Int)

        sums = parseKillerJSON(JSON.parsefile("data/killer_wikipedia"))

        for s in sums
            @constraint(m, sum([x[ind[1], ind[2]] for ind in s.indices]) == s.result)
            @constraint(m, [x[ind[1], ind[2]] for ind in s.indices] in CS.AllDifferentSet())
        end

        # sudoku constraints
        for rc in 1:9
            @constraint(m, x[rc, :] in CS.AllDifferentSet())
            @constraint(m, x[:, rc] in CS.AllDifferentSet())
        end
        for br in 0:2
            for bc in 0:2
                @constraint(
                    m,
                    vec(x[(br * 3 + 1):((br + 1) * 3), (bc * 3 + 1):((bc + 1) * 3)]) in
                    CS.AllDifferentSet()
                )
            end
        end

        optimize!(m)
        @test JuMP.termination_status(m) == MOI.OPTIMAL
        @test jump_fulfills_sudoku_constr(JuMP.value.(x))
        @test JuMP.solve_time(m) >= 0

        for s in sums
            @test s.result == sum(JuMP.value.([x[i[1], i[2]] for i in s.indices]))
        end
    end

    function killer_negative(; reverse_order = false)
        m = Model(optimizer_with_attributes(
            CS.Optimizer,
            "keep_logs" => true,
            "logging" => [],
            "seed" => 1,
        ))

        @variable(m, -9 <= com_grid[1:9, 1:9] <= -1, Int)

        sums = parseKillerJSON(JSON.parsefile("data/killer_niallsudoku_5503"))
        if reverse_order
            reverse!(sums)
        end

        for s in sums
            @constraint(m, sum([com_grid[ind...] for ind in s.indices]) == -s.result)
        end

        jump_add_sudoku_constr!(m, com_grid)
        optimize!(m)

        @test JuMP.termination_status(m) == MOI.OPTIMAL
        @test jump_fulfills_sudoku_constr(com_grid)
        all_correct = true
        for s in sums
            -s.result != sum([JuMP.value(com_grid[i...]) for i in s.indices])
        end
        com = CS.get_inner_model(m)
        @test general_tree_test(com)

        logs = CS.get_logs(com)
        @test CS.sanity_check_log(logs[:tree])

        return com
    end

    @testset "Killer Sudoku niallsudoku_5503 with negative coefficients and -9 to -1" begin
        com1 = killer_negative()
        com1_str = test_string([constraint.indices for constraint in com1.constraints])
        # the constraint order now affects this but calling the same twice should have the same ordering
        # this tests that the order of constraints the user specified is actually moved correctly to ConstraintSolver instead of being random
        # see https://github.com/Wikunia/ConstraintSolver.jl/issues/180
        com2 = killer_negative()
        com2_str = test_string([constraint.indices for constraint in com2.constraints])
        @test com1_str == com2_str
        info_1 = com1.info
        info_2 = com2.info
        @test info_1.pre_backtrack_calls == info_2.pre_backtrack_calls
        @test info_1.backtrack_fixes == info_2.backtrack_fixes
        @test info_1.in_backtrack_calls == info_2.in_backtrack_calls
        @test info_1.backtrack_reverses == info_2.backtrack_reverses
        logs_1 = CS.get_logs(com1)
        logs_2 = CS.get_logs(com2)
        @test CS.same_logs(logs_1[:tree], logs_2[:tree])
    end

end


text:
bitstring(x::Sonum8) = bitstring(UInt8(x))
bitstring(x::Sonum16) = bitstring(UInt16(x))

#TODO something like
#show(io::IO,x::Optim8) = print(io,"Optim8")
#show(io::IO,x::Optim16) = print(io,"Optim16")

ext: jl


text:
using Plots
using SpikeNetOpt
SNO = SpikeNetOpt
using SpikingNeuralNetworks
using Evolutionary
using JLD

SNN = SpikingNeuralNetworks
SNN.@load_units

const Ne = 200;
const Ni = 50
const σee = 1.0
const pee = 0.5
const σei = 1.0
const pei = 0.5
const MU = 10
global E

META_HEUR_OPT = false
EVOLUTIONARY_OPT = true

P, C = SpikeNetOpt.make_net_SNN(Ne, Ni, σee = 0.5, pee = 0.8, σei = 0.5, pei = 0.8)
E, I = P
EE, EI, IE, II = C
SNN.monitor([E, I], [:fire])
sim_length = 1000
@inbounds for t = 1:sim_length*ms
    E.I = vec([11.5 for i = 1:sim_length])
    SNN.sim!(P, C, 1ms)

end

##
# Ground truth simulated data collected below.
##

global spkd_ground = SpikeNetOpt.get_trains(P[1])
display(SNN.raster(P[1]))
SNN.raster(P[1]) |> display

function loss(model)

    σee = model[1]
    pee = model[2]
    σei = model[3]
    pei = model[4]
    P1, C1 = SNO.make_net_SNN(Ne, Ni, σee = σee, pee = pee, σei = σei, pei = pei)#,a=a)
    E1, I1 = P1
    SNN.monitor([E1, I1], [:fire])
    sim_length = 1000
    @inbounds for t = 1:sim_length*ms
        E1.I = vec([11.5 for i = 1:sim_length])
        SNN.sim!(P1, C1, 1ms)
    end

    spkd_found = get_trains(P1[1])
    println("Ground Truth \n")
    SNN.raster([E]) |> display
    println("Best Candidate \n")
    SNN.raster([E1]) |> display
    error = SNO.spike_train_difference(spkd_ground, spkd_found)
    error = sum(error)
    @show(error)
    error
end
if EVOLUTIONARY_OPT
    lower = Float32[0.0 0.0 0.0 0.0]
    upper = Float32[1.0 1.0 1.0 1.0]
    lower = vec(lower)
    upper = vec(upper)

    options = GA(
        populationSize = MU,
        ɛ = 4,
        mutationRate = 0.5,
        selection = ranklinear(1.5),
        crossover = intermediate(0.5),
        mutation = uniform(0.5),
    )
    result = Evolutionary.optimize(
        loss,
        lower,
        upper,
        SNO.initd,
        options,
        Evolutionary.Options(
            iterations = 50,
            successive_f_tol = 1,
            show_trace = true,
            store_trace = true,
        ),
    )
    fitness = minimum(result)

    filename = string("GAsolution.jld")#, py"target_num_spikes")#,py"specimen_id)
    params = result.minimizer

    function eval_best(params)
        σee = params[1]
        pee = params[2]
        σei = params[3]
        pei = params[4]
        P1, C1 = SNO.make_net_SNN(Ne, Ni, σee = σee, pee = pee, σei = σei, pei = pei)#,a=a)
        E1, I1 = P1
        SNN.monitor([E1, I1], [:fire])
        sim_length = 1000
        @inbounds for t = 1:sim_length*ms
            E1.I = vec([11.5 for i = 1:sim_length])#vec(E_stim[t,:])#[i]#3randn(Ne)
            SNN.sim!(P1, C1, 1ms)
        end

        spkd_found = SNO.get_trains(P1[1])
        println("Ground Truth: \n")
        SNN.raster([E]) |> display
        println("candidate: \n")
        SNN.raster([E1]) |> display
        E1, spkd_found

    end

    E1, spkd_found = eval_best(params)
    save(
        filename,
        "spkd_ground",
        spkd_ground,
        "spkd_found",
        spkd_found,
        "Ne",
        Ne,
        "Ni",
        Ni,
        "sim_length",
        sim_length,
    )
    println("best result")
    loss(result.minimizer)
    println("σee = 0.5,  pee= 0.8,σei = 0.5,  pei= 0.8")
    @show(result.minimizer)
    @show(fitness)
    @show(result)
    @show(result.trace)
    trace = result.trace
    trace[1, 1, 1].metadata#["population"]
    E1, spkd_found = eval_best(params)
    evo_loss = [t.value for t in trace[2:length(trace)]]
    display(plot(evo_loss))
    filename = string("PopulationScatter.jld")#, py"target_num_spikes")#,py"specimen_id)
    save(filename, "trace", trace)

end


text:
#===============================================================================

A bag contains one red disc and one blue disc. In a game of chance a player
takes a disc at random and its colour is noted. After each turn the disc is
returned to the bag, an extra red disc is added, and another disc is taken at
random.

The player pays £1 to play and wins if they have taken more blue discs than red
discs at the end of the game.

If the game is played for four turns, the probability of a player winning is
exactly 11/120, and so the maximum prize fund the banker should allocate for
winning in this game would be £10 before they would expect to incur a loss. Note
that any payout will be a whole number of pounds and also includes the original
£1 paid to play the game, so in the example given the player actually wins £9.

Find the maximum prize fund that should be allocated to a single game in which
fifteen turns are played.

===============================================================================#

using Memoize
using Base.Test

#=------------------------------------------------------------------------------
Let p(b, n) the probability to take b blue disks in n attempts, and pb(n) the
probability to take a blue disk at the nth attempt.

It comes:
    p(b, n) = | pb(n) p(b-1, n-1) + (1-pb(n)) p(b, n-1) if b≤n
              | 0 if b>n

    pb(n) = 1 / (1+n)

In addition p(1, 1) = pb(1) p(0,0) + (1-pb(1)) p(0, -1)
                    = 1/2 p(0, 0)
                    = 1/2
            => p(0, 0) = 1
------------------------------------------------------------------------------=#

@memoize
function p(b::Int, n::Int)::Rational{Int}
    if     n<b  0
    elseif b<0  0
    elseif n==0 1  # In that case, b must also be equal to 0
    else        p(b-1, n-1)//(1+n) + n*p(b, n-1)//(1+n)
    end
end

#=------------------------------------------------------------------------------
The probability to win it then given by
    p(n) = sum(p(b,n) for b ∈ [div(n,2)+1, n])
------------------------------------------------------------------------------=#

p(n::Int)::Rational{Int} = sum([p(b,n) for b in div(n,2)+1:n])

@test p(4) == 11//120

#=------------------------------------------------------------------------------
If p(n) = N//D, then the maximal prize fund is div(D/N), i.e. floor(1//p(n))
------------------------------------------------------------------------------=#

maxfund(n) = let p = p(n); div(p.den, p.num) end

@test maxfund(4) == 10

#=----------------------------------------------------------------------------=#
println("Euler 121: $(maxfund(15))")

max_stars_repo_name: dpieroux/euler

max_issues_repo_name: dpieroux/euler

max_forks_repo_name: dpieroux/euler


text:
"""
    connect(points...)

returns a line connecting all points.
"""
function connect(points::XYTuple...)
    line([points...])
end

"""
    connect(point, x, dir=:x)

Connects point to (x, point[2]) if dir is :x or to (point[1], x) if dir is :y
"""
function connect(point::XYTuple, x::Real, dir=:x)
    if dir == :x
        return line([point, (x, point[2])])
    elseif dir == :y
        return line([point, (point[1], x)])
    else
        println("dir :", string(dir), " not recognized.")
    end
    return nothing
end

"""
    dots(points...)

Render circles at points to signify electrical connections
"""
function dots(points::XYTuple...; lw::Measures.Length=1mm, color="black")
    radius = .10# * lw / 1mm
    compose(
        context(),
        map(points) do p
            circle(p[1], p[2], radius)
        end...,
        fill(color),
        linewidth(0mm)
    )
end

"""
    render(renderable::Block)

Returns the render context of a Block.
"""
function render(renderable::Block)
    renderable.composition
end

# function render(renderable::Block, args...)
#     compose(
#         context(units=UnitBox(0, 0, 6, 6)),
#         renderable.composition,
#         args...
#     )
# end

"""
    render(
        pstrs...;
        _fontsize = 40pt,
        _font = "Helvetica-Bold"
    )

Given Tuples (pos, text), this function will generate a renderable object with
text centered around pos.
"""
function render(
        pstrs::Union{Tuple{Tuple{Real, Real}, String}, Tuple{Real, Real, String}}...;
        _fontsize::Measures.Length = 40pt,
        _font::String = "Helvetica-Bold"
    )

    texts = map(pstrs) do pstr
        if typeof(pstr) <: Tuple{Tuple{Real, Real}, String}
            (x, y), s = pstr
        else
            x, y, s = pstr
        end
        text(x, y, s, hcenter, vcenter)
    end
    compose(
        context(), texts...,
        linewidth(0mm), fontsize(_fontsize), font(_font)
    )
end

"""
    autosize(width, height)

Generate sensable sizes based on the circuit width and height.
"""
function autosize(width, height, w_scale=3cm, h_scale=3cm)
    width * w_scale, height * h_scale
end


text:
### Deprecated functions to be excised eventually

#maps ys to simplex somewhat safely
function soft_max(ys)
	ymax = maximum(ys)
	weights = exp.(ys .- ymax)
	weights /= sum(weights)
	@assert isprobvec(weights) "soft_max fail: \n $ys \n $weights"

	weights
end

###########

###########
#  Some functions to optimize the anchors
function genMixComp(mhats, numClusters; maxiter=50)
    #First cluster to find points.
    phats = mhats ./ sum(mhats, dims=1)
    if numClusters < 0
    	mix_comp = phats
    	numClusters = size(phats, 2)
    else
	    cluster_res = kmeans(phats, numClusters; maxiter=maxiter)
    	mix_comp = cluster_res.centers
	end

    #add the grandmean for ease
    hcat(mix_comp, JS.get_GM_anchor(mhats))
end

#optimizes choice of anchor and alpha by approx minimizing LOO
#Heuristic usese particle swarm a 2 starts.
#Passing numClusters = -1 makes anchor a linear comb of all phats
#return anchor, alpha, loo val
function loo_anchor(xs, cs, mhats; numClusters = 20, init_sqrt_alpha = 1.,
                    time_limit = 60., iterations=1000, store_trace=false, info=false)
	mix_comp = genMixComp(mhats, numClusters)

    #write aux function
    function f(ys)
        #use softmax to ensure simplex
        weights = soft_max(ys[1:end-1])
        p0 = vec(mix_comp * weights)
        alpha = (ys[end])^2
        @assert isprobvec(p0) "P0 Failed here: \n $weights \n $ys[1:end-1]"
        JS.zLOObar_unsc(xs, cs, mhats, (p0, alpha))
    end

    #optimize with two starting points and take best one
    x01 = [ones(size(mix_comp, 2)); init_sqrt_alpha]
    init_val1 = f(x01)
    @time res1 = optimize(f, x01,
                    ParticleSwarm(n_particles=10),
                    Optim.Options(time_limit=time_limit, iterations=iterations, store_trace=store_trace))
    z1 = Optim.minimum(res1)

    x02 = [zeros(size(mix_comp, 2) - 1); 1.; init_sqrt_alpha]
    init_val2 = f(x02)
    @time res2 = optimize(f, x02,
                    ParticleSwarm(n_particles=10),
                    Optim.Options(time_limit=time_limit, iterations=iterations, store_trace=store_trace))
    z2 = Optim.minimum(res2)

    if z1 < z2
        xstar = Optim.minimizer(res1)
        zstar = z1
    else
        xstar = Optim.minimizer(res2)
        zstar = z2
    end
    info && println("Perc: Improv over LOO GM:\t", 1- zstar / init_val2)

    weights = soft_max(xstar[1:end-1])
    p0 = vec(mix_comp * weights)
    alpha = (xstar[end])^2
    p0, alpha, zstar
end

#optimizes choice of anchor and alpha by approx minimizing oracle criteria
#Heuristic usese particle swarm a 2 starts.
#Passing numClusters = -1 makes anchor a linear comb of all phats
#return anchor, alpha, loo val
function opt_oracle_anchor(xs, cs, ps, mhats; numClusters = 20, init_sqrt_alpha = 1.,
                    time_limit = 60., iterations=1000, store_trace=false, info=false)
	mix_comp = genMixComp(mhats, numClusters)
	lams = ones(length(xs))
    #write aux function
    function f(ys)
        #use softmax to ensure simplex
        weights = soft_max(ys[1:end-1])
        p0 = vec(mix_comp * weights)
        alpha = (ys[end])^2
        @assert isprobvec(p0) "P0 Failed here: \n $weights \n $ys[1:end-1]"
        JS.zbar(xs, cs, mhats, ps, lams, (p0, alpha))
    end

    #optimize with two starting points and take best one
    x01 = [ones(size(mix_comp, 2)); init_sqrt_alpha]
    init_val1 = f(x01)
    @time res1 = optimize(f, x01,
                    ParticleSwarm(n_particles=10),
                    Optim.Options(time_limit=time_limit, iterations=iterations, store_trace=store_trace))
    z1 = Optim.minimum(res1)

    x02 = [zeros(size(mix_comp, 2) - 1); 1.; init_sqrt_alpha]
    init_val2 = f(x02)
    @time res2 = optimize(f, x02,
                    ParticleSwarm(n_particles=10),
                    Optim.Options(time_limit=time_limit, iterations=iterations, store_trace=store_trace))
    z2 = Optim.minimum(res2)

    if z1 < z2
        xstar = Optim.minimizer(res1)
        zstar = z1
    else
        xstar = Optim.minimizer(res2)
        zstar = z2
    end
    info && println("Perc: Improv over LOO GM:\t", 1- zstar / init_val2)

    weights = soft_max(xstar[1:end-1])
    p0 = vec(mix_comp * weights)
    alpha = (xstar[end])^2
    p0, alpha, zstar
end


text:
using MosaicViews
using Test
using ImageCore, ColorVectorSpace
using OffsetArrays

# Because of the difference in axes types between paddedviews (Base.OneTo) and
# sym_paddedviews (UnitRange), the return type of `_padded_cat` isn't inferrable to a
# concrete type. But it is inferrable to a Union{A,B} where both A and B are concrete.
# While `@inferred(_padded_cat((A, B), ...))` would therefore fail, this is a close substitute.
function _checkinferred_paddedcat(V, A; kwargs...)
    vd = MosaicViews.valdim(first(A))
    RTs = Base.return_types(MosaicViews._padded_cat, (typeof(A), Bool, eltype(V), typeof(vd)))
    @test length(RTs) == 1
    RT = RTs[1]
    @test isconcretetype(RT) || (isa(RT, Union) && isconcretetype(RT.a) && isconcretetype(RT.b))
    return V
end
function checkinferred_mosaic(As::Tuple; kwargs...)
    V = mosaic(As; kwargs...)
    return _checkinferred_paddedcat(V, As)
end
function checkinferred_mosaic(A...; kwargs...)
    V = mosaic(A...; kwargs...)
    return _checkinferred_paddedcat(V, A)
end
function checkinferred_mosaic(As::AbstractVector{<:AbstractArray}; kwargs...)
    V = mosaic(As; kwargs...)
    return _checkinferred_paddedcat(V, (As...,); kwargs...)
end

@testset "MosaicView" begin
    @test_throws ArgumentError MosaicView(rand(2,2,2,2,2))

    @testset "1D input" begin
        A = [1,2,3]
        mva = @inferred MosaicView(A)
        @test parent(mva) === A
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (3, 1) # 1D vector is lifted to 2D matrix
        @test axes(mva) == (Base.OneTo(3), Base.OneTo(1))
        @test @inferred(getindex(mva, 1, 1)) === 1
        @test @inferred(getindex(mva, 2, 1)) === 2
    end

    @testset "2D input" begin
        A = [1 2;3 4]
        mva = @inferred MosaicView(A)
        @test parent(mva) === A
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (2, 2)
        @test axes(mva) == (Base.OneTo(2), Base.OneTo(2))
        @test @inferred(getindex(mva, 1, 1)) === 1
        @test @inferred(getindex(mva, 2, 1)) === 3
    end

    @testset "3D input" begin
        A = zeros(Int,2,2,2)
        A[:,:,1] = [1 2; 3 4]
        A[:,:,2] = [5 6; 7 8]
        mva = @inferred MosaicView(A)
        @test parent(mva) === A
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (4, 2)
        @test @inferred(getindex(mva,1,1)) === 1
        @test @inferred(getindex(mva,2,1)) === 3
        @test_throws BoundsError mva[0,1]
        @test_throws BoundsError mva[1,0]
        @test_throws BoundsError mva[1,3]
        @test_throws BoundsError mva[5,1]
        @test all(mva .== vcat(A[:,:,1],A[:,:,2]))
        # singleton dimension doesn't change anything
        @test mva == MosaicView(reshape(A,2,2,2,1))
    end

    @testset "4D input" begin
        A = zeros(Int,2,2,1,2)
        A[:,:,1,1] = [1 2; 3 4]
        A[:,:,1,2] = [5 6; 7 8]
        mva = @inferred MosaicView(A)
        @test parent(mva) === A
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (2, 4)
        @test @inferred(getindex(mva,1,1)) === 1
        @test @inferred(getindex(mva,2,1)) === 3
        @test_throws BoundsError mva[0,1]
        @test_throws BoundsError mva[1,0]
        @test_throws BoundsError mva[3,1]
        @test_throws BoundsError mva[1,5]
        @test all(mva .== hcat(A[:,:,1,1],A[:,:,1,2]))
        A = zeros(Int,2,2,2,3)
        A[:,:,1,1] = [1 2; 3 4]
        A[:,:,1,2] = [5 6; 7 8]
        A[:,:,1,3] = [9 10; 11 12]
        A[:,:,2,1] = [13 14; 15 16]
        A[:,:,2,2] = [17 18; 19 20]
        A[:,:,2,3] = [21 22; 23 24]
        mva = @inferred MosaicView(A)
        @test parent(mva) === A
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (4, 6)
        @test @inferred(getindex(mva,1,1)) === 1
        @test @inferred(getindex(mva,2,1)) === 3
        @test all(mva .== vcat(hcat(A[:,:,1,1],A[:,:,1,2],A[:,:,1,3]), hcat(A[:,:,2,1],A[:,:,2,2],A[:,:,2,3])))
    end
end

@testset "mosaicview" begin
    @testset "1D input" begin
        A1 = [1, 2, 3]
        A2 = [4, 5, 6]
        mva = checkinferred_mosaic(A1, A2)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A1)
        @test size(mva) == (6, 1)
        @test @inferred(getindex(mva, 1, 1)) == 1

        @test checkinferred_mosaic(A1, A2; nrow=1) == [1 4; 2 5; 3 6]
        @test mosaic(A1, A2; nrow=2) == reshape([1, 2, 3, 4, 5, 6], (6, 1))
        @test mosaic(A1, A2) == mosaic([A1, A2]) == mosaic((A1, A2))
    end

    @testset "2D input" begin
        A = [i*ones(Int, 2, 3) for i in 1:4]
        Ao = [i*ones(Int, 0i:1, 0:2) for i in 1:4]

        for B in (A, tuple(A...), Ao)
            @test_throws ArgumentError mosaic(B, nrow=0)
            @test_throws ArgumentError mosaic(B, ncol=0)
            @test_throws ArgumentError mosaic(B, nrow=1, ncol=1)

            mva = checkinferred_mosaic(B)
            @test mosaic(B...) == mva
            @test typeof(mva) <: MosaicView
            @test eltype(mva) == eltype(eltype(B))
            @test size(mva) == (8, 3)
            @test @inferred(getindex(mva,3,1)) === 2
            @test collect(mva) == [
                1  1  1
                1  1  1
                2  2  2
                2  2  2
                3  3  3
                3  3  3
                4  4  4
                4  4  4
            ]

            mva = checkinferred_mosaic(B, nrow=2)
            @test typeof(mva) <: MosaicView
            @test eltype(mva) == eltype(eltype(B))
            @test size(mva) == (4, 6)
            @test collect(mva) == [
             1  1  1  3  3  3
             1  1  1  3  3  3
             2  2  2  4  4  4
             2  2  2  4  4  4
            ]
        end

        @test mosaic(A...) == mosaic(A)
        @test mosaic(A..., nrow=2) == mosaic(A, nrow=2)
        @test mosaic(A..., nrow=2, rowmajor=true) == mosaic(A, nrow=2, rowmajor=true)

        A1 = reshape([1 2 3], (1, 3))
        A2 = reshape([4;5;6], (3, 1))
        @test checkinferred_mosaic([A1, A2]; center=false) == [
         1 2 3;
         0 0 0;
         0 0 0;
         4 0 0;
         5 0 0;
         6 0 0
        ]
        @test mosaic([A1, A2]; center=true) == [
         0 0 0;
         1 2 3;
         0 0 0;
         0 4 0;
         0 5 0;
         0 6 0
        ]
        @test mosaic([A1, A2]) == mosaic([A1, A2]; center=true)

        # same size but different axes
        A1 = fill(1, 1:2, 1:2)
        A2 = fill(2, 2:3, 2:3)
        @test collect(checkinferred_mosaic(A1, A2; center=true)) == [
            1 1 0;
            1 1 0;
            0 0 0;
            2 2 0;
            2 2 0;
            0 0 0;
        ]
        @test collect(mosaic(A1, A2; center=false)) == [
            1 1 0;
            1 1 0;
            0 0 0;
            0 0 0;
            0 2 2;
            0 2 2;
        ]

        @test mosaicview(A1) == A1 # a trivial case
    end

    @testset "3D input" begin
        A = [(k+1)*l-1 for i in 1:2, j in 1:3, k in 1:2, l in 1:2]
        B = reshape(A, 2, 3, :)
        @test_throws ArgumentError mosaicview(B, nrow=0)
        @test_throws ArgumentError mosaicview(B, ncol=0)
        @test_throws ArgumentError mosaicview(B, nrow=1, ncol=1)

        mva = checkinferred_mosaic(B)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(B)
        @test size(mva) == (8, 3)
        @test @inferred(getindex(mva,3,1)) === 2
        @test mva == [
            1  1  1
            1  1  1
            2  2  2
            2  2  2
            3  3  3
            3  3  3
            5  5  5
            5  5  5
        ]
        mva = checkinferred_mosaic(B, nrow=2)
        @test mva == MosaicView(A)
        @test typeof(mva) != typeof(MosaicView(A))
        @test parent(parent(mva)).data == B
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(B)
        @test size(mva) == (4, 6)

        @test mosaic(B, B) == mosaicview(cat(B, B; dims=4))
        @test mosaic(B, B, nrow=2) == mosaicview(cat(B, B; dims=4), nrow=2)
        @test mosaic(B, B, nrow=2, rowmajor=true) == mosaicview(cat(B, B; dims=4), nrow=2, rowmajor=true)
    end

    @testset "4D input" begin
        A = [(k+1)*l-1 for i in 1:2, j in 1:3, k in 1:2, l in 1:2]
        @test_throws ArgumentError mosaicview(A, nrow=0)
        @test_throws ArgumentError mosaicview(A, ncol=0)
        @test_throws ArgumentError mosaicview(A, nrow=1, ncol=1)

        mva = mosaicview(A)
        @test mva == MosaicView(A)
        @test typeof(mva) != typeof(MosaicView(A))
        @test parent(parent(mva)).data == reshape(A, 2, 3, :)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (4, 6)
        mva = mosaicview(A, npad=1)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (5, 7)
        @test mva == mosaicview(A, nrow=2, npad=1)
        @test mva == mosaicview(A, ncol=2, npad=1)
        @test @inferred(getindex(mva,3,1)) === 0
        @test @inferred(getindex(mva,2,5)) === 3
        @test mva == [
            1  1  1  0  3  3  3
            1  1  1  0  3  3  3
            0  0  0  0  0  0  0
            2  2  2  0  5  5  5
            2  2  2  0  5  5  5
        ]
        mva = mosaicview(A, ncol=3, npad=1)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (5, 11)
        @test mva == [
            1  1  1  0  3  3  3  0  0  0  0
            1  1  1  0  3  3  3  0  0  0  0
            0  0  0  0  0  0  0  0  0  0  0
            2  2  2  0  5  5  5  0  0  0  0
            2  2  2  0  5  5  5  0  0  0  0
        ]
        mva = mosaicview(A, rowmajor=true, ncol=3, npad=1)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (5, 11)
        @test @inferred(getindex(mva,3,1)) === 0
        @test @inferred(getindex(mva,2,5)) === 2
        @test mva == [
            1  1  1  0  2  2  2  0  3  3  3
            1  1  1  0  2  2  2  0  3  3  3
            0  0  0  0  0  0  0  0  0  0  0
            5  5  5  0  0  0  0  0  0  0  0
            5  5  5  0  0  0  0  0  0  0  0
        ]
        mva = mosaicview(A, rowmajor=true, ncol=3, npad=2)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (6, 13)
        @test mva == [
            1  1  1  0  0  2  2  2  0  0  3  3  3
            1  1  1  0  0  2  2  2  0  0  3  3  3
            0  0  0  0  0  0  0  0  0  0  0  0  0
            0  0  0  0  0  0  0  0  0  0  0  0  0
            5  5  5  0  0  0  0  0  0  0  0  0  0
            5  5  5  0  0  0  0  0  0  0  0  0  0
        ]
        mva = mosaicview(A, fillvalue=-1.0, rowmajor=true, ncol=3, npad=1)
        @test typeof(mva) <: MosaicView
        @test eltype(mva) == eltype(A)
        @test size(mva) == (5, 11)
        @test mva == [
             1   1   1  -1   2   2   2  -1   3   3   3
             1   1   1  -1   2   2   2  -1   3   3   3
            -1  -1  -1  -1  -1  -1  -1  -1  -1  -1  -1
             5   5   5  -1  -1  -1  -1  -1  -1  -1  -1
             5   5   5  -1  -1  -1  -1  -1  -1  -1  -1
        ]

        @test mosaic(A, A) == mosaicview(cat(A, A; dims=5))
        @test mosaic(A, A, nrow=2) == mosaicview(cat(A, A; dims=4), nrow=2)
        @test mosaic(A, A, nrow=2, rowmajor=true) == mosaicview(cat(A, A; dims=4), nrow=2, rowmajor=true)
    end

    @testset "Colorant Array" begin
        A = rand(RGB{Float32}, 2, 3, 2, 2)
        mvaa = mosaicview(A)
        @test eltype(mvaa) == eltype(A)
        @test mvaa == @inferred(MosaicView(A))
        mvaa = mosaicview(A, rowmajor=true, ncol=3)
        @test eltype(mvaa) == eltype(A)
        @test @inferred(getindex(mvaa, 3, 4)) == RGB(0,0,0)
        mvaa = mosaicview(A, fillvalue=colorant"white", rowmajor=true, ncol=3)
        @test eltype(mvaa) == eltype(A)
        @test @inferred(getindex(mvaa, 3, 4)) == RGB(1,1,1)

        # this should work regardless they're of different size and color
        @test_nowarn mosaic(rand(RGB{Float32}, 4, 4),
                            rand(Gray{N0f8}, 5, 5))
    end

    # all arrays should have the same dimension
    @test_throws ArgumentError mosaic(ones(2), ones(1, 2))
    @test_throws ArgumentError mosaic((ones(2), ones(1, 2)))
    @test_throws ArgumentError mosaic([ones(2), ones(1, 2)])

    @testset "filltype" begin
        # always a concrete type
        A = checkinferred_mosaic(rand(N0f8, 4, 4), rand(Float64, 4, 4), rand(Float32, 4, 4))
        @test eltype(A) == Float64

        A = mosaic(Any[1 2 3; 4 5 6], rand(Float32, 4, 4))
        @test eltype(A) == Float32
        A = mosaic(rand(Float32, 4, 4), Any[1 2 3; 4 5 6])
        @test eltype(A) == Float32

        # FIXME:
        # A = checkinferred_mosaic(rand(Float64, 4, 4), Union{Missing, Float32}[1 2 3; 4 5 6])
        A = mosaic(rand(Float64, 4, 4), Union{Missing, Float32}[1 2 3; 4 5 6])
        @test eltype(A) == Union{Missing, Float64}
    end
end

@testset "deprecations" begin
    @info "deprecations are expected"
    # mosaicview -> mosaic deprecations
    A = [(k+1)*l-1 for i in 1:2, j in 1:3, k in 1:2, l in 1:2]
    mva_old = mosaicview(A, A, rowmajor=true, ncol=3, npad=1)
    mva_new = mosaic(A, A, rowmajor=true, ncol=3, npad=1)
    @test mva_old == mva_new
    mva_old = mosaicview(A, A, A, rowmajor=true, ncol=3, npad=1)
    mva_new = mosaic(A, A, A, rowmajor=true, ncol=3, npad=1)
    @test mva_old == mva_new
    mva_old = mosaicview([A, A], rowmajor=true, ncol=3, npad=1)
    mva_new = mosaic([A, A], rowmajor=true, ncol=3, npad=1)
    @test mva_old == mva_new
    mva_old = mosaicview((A, A), rowmajor=true, ncol=3, npad=1)
    mva_new = mosaic((A, A), rowmajor=true, ncol=3, npad=1)
    @test mva_old == mva_new
    # center keyword for `mosaicview` (still applies for `mosaic`)
    A = [(k+1)*l-1 for i in 1:2, j in 1:3, k in 1:2, l in 1:2]
    B = reshape(A, 2, 3, :)
    @test mosaicview(B, center=2) == mosaicview(B) # no op
    @test mosaicview(A, center=2) == mosaicview(A) # no op
end


text:
### A Pluto.jl notebook ###
# v0.16.3

using Markdown
using InteractiveUtils

# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).
macro bind(def, element)
    quote
        local el = $(esc(element))
        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : missing
        el
    end
end

# ╔═╡ ecfe7e0c-2918-11ec-3510-65601e1a6072
begin
	using Pkg
	Pkg.activate("../../")
	using Revise
end

# ╔═╡ 110ceade-2a17-4e2f-a156-063fcbdf214f
using TestImages, ImageShow

# ╔═╡ c5851e01-65ce-4d34-9a3d-2e99d2a9bc8a
using Colors

# ╔═╡ c7ed12a9-83eb-4059-90cb-65734e1f0da3
using Plots

# ╔═╡ 4c848822-0937-49de-9317-d8ad59b232c0
using PlutoUI

# ╔═╡ 77cf9d3e-b113-413a-ace1-acde66e580e2
md"# Image Processing in Microscopy

* Lecturer: Rainer Heintzmann
* Teaching assistant: Felix Wechsler (felix.wechsler@uni-jena.de)
* Forum on moddle
* Code on GitHub (https://github.com/bionanoimaging/Image-Processing-In-Microscopy, also on Moodle)
"

# ╔═╡ d45db367-4e34-4c47-adc6-a027b4cd3326

# ╔═╡ 2c3a637e-8149-4839-a79a-bc765da81972

# ╔═╡ b88049f8-3064-4d18-b8f7-47e12a3ddc2f
md"## Julia Lang
* A relatively new dynamic programming language (≈ 3 years since 1.0)
* General purpose and high level language
* Core paradigm is *Multiple Dispatch*
* Performance comparable to C/C++/Fortran
* For example: *for loops* don't have a performance penalty
### Reasons for Speed
* JIT - Just in Time compilation
* type stable functions
* because of the type system Julia can compile efficient code
### Helpfuls resources
* However, several differences to other languages
* There is not a `numpy` package since many parts in the language are fast by itself (arrays, Linear Algebra, ...)
* Consult [performance tips](https://docs.julialang.org/en/v1/manual/performance-tips/)
* [Noteworthy differences to other language](https://docs.julialang.org/en/v1/manual/noteworthy-differences/)
* Ecosystem for image processing for microscopy data is small
### Some important packages
* [Bioformatsloader.jl](https://github.com/ahnlabb/BioformatsLoader.jl)
* [View5D.jl](https://github.com/RainerHeintzmann/View5D.jl)
* [DeconvOptim.jl](https://github.com/roflmaostc/DeconvOptim.jl)
* [Images.jl](https://github.com/JuliaImages/Images.jl/)
* [Napari.jl](https://github.com/mkitti/Napari.jl)
* [FourierTools.jl](https://github.com/bionanoimaging/FourierTools.jl)
* [TestImages.jl](https://github.com/JuliaImages/TestImages.jl)
"

# ╔═╡ c16d830f-5a29-46f8-b97b-e66fb60e7a99
md"### Activating our environment to get the correct versions"

# ╔═╡ d25c41da-613b-44d4-aded-a9b254720a37
Pkg.status()

# ╔═╡ a4890dd2-5f8b-48bd-a237-4b803bdc3e2c

# ╔═╡ 97947b8a-a8e9-423c-8330-37ccf1b50a02
md"## Load TestImages"

# ╔═╡ 1d0ecaa4-2c36-4197-a527-3e1616b3d42f
img = testimage("resolution_test_512")

# ╔═╡ 4b4008ab-6c4f-4766-9cc9-9b730a3c66d3
testimage

# ╔═╡ 443ebbab-b5f9-4222-8590-051a2e63be50
typeof(img)

# ╔═╡ 85ef3691-e89e-4323-8914-b123006dca69
Matrix{Float64} === Array{Float64, 2} # matrix is 2 dimensional array

# ╔═╡ 1c706eee-7664-43aa-9ca6-ba7ba05d3fce
md"## Modify Arrays"

# ╔═╡ 2c5b549e-d217-494f-88d0-587b3282e1c7
x = 0

# ╔═╡ a3511f2c-31bd-4e42-b1a4-5113aaa50e2c
sin(x)

# ╔═╡ 3da31838-ec8e-4f91-9bcf-7d0c5d761a1f
xs = [0, pi / 2, pi, 3/2 * pi]

# ╔═╡ 597ebb46-6ba6-48a2-846b-61185ed24f97
sin.(xs)

# ╔═╡ 3394ee97-fdef-4713-b132-338a991646e5
Float32(1)

# ╔═╡ a31eaa22-0baf-40a1-9417-981d2384b01c
1e-1 # 1 * 10^(-1) but as a Float64

# ╔═╡ baff6b11-f1d5-40fc-ac10-05af6ba0c178
1f-1 # 1 * 10^(-1) but as a Float32

# ╔═╡ 06147f5c-c8c0-49e3-861c-30bd345cf389
arr = Float32.(img)

# ╔═╡ 0c545a78-7eea-4470-a529-5cf2c4e27f84
arr[200, 100]

# ╔═╡ b01daaa3-e9ed-4bc7-b908-c14ac5a94ad8
sub_image = arr[100:200, 100:200]

# ╔═╡ 5a366aef-18c6-4975-97e0-c0231f2bcffc
Gray.(sub_image)

# ╔═╡ fa0d093d-21a5-42b5-9ea7-9bd86404c565
md"## Create Functions"

# ╔═╡ ac66a808-15a4-4ac2-b534-1a0310650577
function foo(x)
	return x + 1.0
end

# ╔═╡ 0e9e362a-7e86-4d78-b33b-ecb975a4314d
function foo_better(x)
	return x + one(x)
	# hello for comments
end

# ╔═╡ 37ba1b25-c44c-4fd7-8903-3b926cb2b935
foo(100)

# ╔═╡ 1e7de4e0-2adb-4aa1-9b53-9236b28a992e
foo(100.0)

# ╔═╡ 0e0fff8b-106d-4a5c-8111-0f3f07ab788c
typeof(1)

# ╔═╡ 2da94408-2168-467c-b12d-d8bb311c2dbe
typeof(foo(1))

# ╔═╡ 031d02b1-4a40-44ce-925e-aadc5c3807e0
typeof(foo_better(1))

# ╔═╡ 4db99cc6-c6f8-4c78-b219-31dcf05751ef
foo.(ones(Float32, (10,10)));

# ╔═╡ 2f22eab7-8032-4cfa-9889-a91b73c8992b
foo(100.0f0)

# ╔═╡ c7c8ad71-35f3-43e4-8cb0-cbeeba741d3b

# ╔═╡ 6e7f40ba-e3b7-482b-94f0-a3d45301f9f3
md"## Why we use Pluto"

# ╔═╡ c91d6219-25d9-4658-8a96-01fe0acdaf99
a = 100_000_000_000

# ╔═╡ 9b7c2a27-f85d-4dcd-98f9-35a076f4fe45
b = -10000000000

# ╔═╡ 536245a4-6c85-4999-bcff-82053b5e1a24
c = a + b

# ╔═╡ 55cd6649-1058-4d76-b88d-97bcdb230847
md"## Ploting"

# ╔═╡ cf7d48cc-7461-4afb-8895-7e197184902c
x2s = 0:0.01:10

# ╔═╡ ab502d9d-4823-471b-b70d-402192714721
(0:10000000000000000000000000000000)[1000]

# ╔═╡ 8c53a3f9-dd6a-4874-b592-f8452ad03a4b
typeof(x2s)

# ╔═╡ 9f4dbf6e-4b8b-4255-a854-6f92cf1e5271
typeof(collect(x2s))

# ╔═╡ 964efb64-b61c-407f-8096-136cd9b0d27c
@bind offset Slider(0:0.1:5, show_value=true)

# ╔═╡ 125facd6-0ad2-4d9a-9056-c0d697bc5396
@bind scaling Slider(0.1:0.1:5, show_value=true)

# ╔═╡ 807a0336-ecff-4b01-8fbf-a31b28a4f8fc
begin
	plot(x2s, sin.(scaling .* x2s .+ offset))
	plot!(x2s, 0.01 .* x2s .^2 )
end

# ╔═╡ 675d49fd-d88f-4933-a27e-db294b3f3130
md"## FFTs"

# ╔═╡ 47d83dca-043b-4099-b4ef-672835412256

# ╔═╡ 88d9de0f-faf6-4d75-84ae-25f79ced49e5

# ╔═╡ 26c178d8-fd80-4a57-9f3f-d7fb170daf86
md"## Interactivity with Pluto"

# ╔═╡ 387bba27-f791-4f2b-814b-2c42c19b3523
# live docs

# ╔═╡ f6b8f22b-1ee7-4332-8bbf-55fc9afb0af4

# ╔═╡ 4b49e628-96b7-454b-bed5-ac66eef97a56

# ╔═╡ 48658d4d-1bc3-4fd1-ba4d-508174c03442

# ╔═╡ 204ac4ea-9359-42ff-ae9a-712a151391b0

# ╔═╡ Cell order:
# ╠═ecfe7e0c-2918-11ec-3510-65601e1a6072
# ╠═77cf9d3e-b113-413a-ace1-acde66e580e2
# ╠═d45db367-4e34-4c47-adc6-a027b4cd3326
# ╠═2c3a637e-8149-4839-a79a-bc765da81972
# ╟─b88049f8-3064-4d18-b8f7-47e12a3ddc2f
# ╠═c16d830f-5a29-46f8-b97b-e66fb60e7a99
# ╠═d25c41da-613b-44d4-aded-a9b254720a37
# ╠═a4890dd2-5f8b-48bd-a237-4b803bdc3e2c
# ╟─97947b8a-a8e9-423c-8330-37ccf1b50a02
# ╠═110ceade-2a17-4e2f-a156-063fcbdf214f
# ╠═1d0ecaa4-2c36-4197-a527-3e1616b3d42f
# ╠═4b4008ab-6c4f-4766-9cc9-9b730a3c66d3
# ╠═443ebbab-b5f9-4222-8590-051a2e63be50
# ╠═85ef3691-e89e-4323-8914-b123006dca69
# ╠═1c706eee-7664-43aa-9ca6-ba7ba05d3fce
# ╠═2c5b549e-d217-494f-88d0-587b3282e1c7
# ╠═a3511f2c-31bd-4e42-b1a4-5113aaa50e2c
# ╠═3da31838-ec8e-4f91-9bcf-7d0c5d761a1f
# ╠═597ebb46-6ba6-48a2-846b-61185ed24f97
# ╠═3394ee97-fdef-4713-b132-338a991646e5
# ╠═a31eaa22-0baf-40a1-9417-981d2384b01c
# ╠═baff6b11-f1d5-40fc-ac10-05af6ba0c178
# ╠═06147f5c-c8c0-49e3-861c-30bd345cf389
# ╠═0c545a78-7eea-4470-a529-5cf2c4e27f84
# ╠═b01daaa3-e9ed-4bc7-b908-c14ac5a94ad8
# ╠═c5851e01-65ce-4d34-9a3d-2e99d2a9bc8a
# ╠═5a366aef-18c6-4975-97e0-c0231f2bcffc
# ╠═fa0d093d-21a5-42b5-9ea7-9bd86404c565
# ╠═ac66a808-15a4-4ac2-b534-1a0310650577
# ╠═0e9e362a-7e86-4d78-b33b-ecb975a4314d
# ╠═37ba1b25-c44c-4fd7-8903-3b926cb2b935
# ╠═1e7de4e0-2adb-4aa1-9b53-9236b28a992e
# ╠═0e0fff8b-106d-4a5c-8111-0f3f07ab788c
# ╠═2da94408-2168-467c-b12d-d8bb311c2dbe
# ╠═031d02b1-4a40-44ce-925e-aadc5c3807e0
# ╠═4db99cc6-c6f8-4c78-b219-31dcf05751ef
# ╠═2f22eab7-8032-4cfa-9889-a91b73c8992b
# ╠═c7c8ad71-35f3-43e4-8cb0-cbeeba741d3b
# ╠═6e7f40ba-e3b7-482b-94f0-a3d45301f9f3
# ╠═c91d6219-25d9-4658-8a96-01fe0acdaf99
# ╠═9b7c2a27-f85d-4dcd-98f9-35a076f4fe45
# ╠═536245a4-6c85-4999-bcff-82053b5e1a24
# ╠═55cd6649-1058-4d76-b88d-97bcdb230847
# ╠═c7ed12a9-83eb-4059-90cb-65734e1f0da3
# ╠═cf7d48cc-7461-4afb-8895-7e197184902c
# ╠═ab502d9d-4823-471b-b70d-402192714721
# ╠═8c53a3f9-dd6a-4874-b592-f8452ad03a4b
# ╠═9f4dbf6e-4b8b-4255-a854-6f92cf1e5271
# ╠═4c848822-0937-49de-9317-d8ad59b232c0
# ╠═964efb64-b61c-407f-8096-136cd9b0d27c
# ╠═125facd6-0ad2-4d9a-9056-c0d697bc5396
# ╠═807a0336-ecff-4b01-8fbf-a31b28a4f8fc
# ╠═675d49fd-d88f-4933-a27e-db294b3f3130
# ╠═47d83dca-043b-4099-b4ef-672835412256
# ╠═88d9de0f-faf6-4d75-84ae-25f79ced49e5
# ╠═26c178d8-fd80-4a57-9f3f-d7fb170daf86
# ╠═387bba27-f791-4f2b-814b-2c42c19b3523
# ╠═f6b8f22b-1ee7-4332-8bbf-55fc9afb0af4
# ╠═4b49e628-96b7-454b-bed5-ac66eef97a56
# ╠═48658d4d-1bc3-4fd1-ba4d-508174c03442
# ╠═204ac4ea-9359-42ff-ae9a-712a151391b0


text:
# initializing and optimizing embeddings

"""
    optimize_embedding(graph, query_embedding_, ref_embedding_, n_epochs, alpha, min_dist, spread, repulsion_strength, neg_sample_rate, _a=nothing, _b=nothing; parallel=Bool) -> embedding

Optimize an embedding by minimizing the fuzzy set cross entropy between the high and low dimensional simplicial sets using stochastic gradient descent.
Optimize "query" samples with respect to "reference" samples. The optimization uses all available threads.

# Arguments
- `graph`: a sparse matrix of shape (n_samples, n_samples)
- `query_embedding_`: a vector of length (n_samples,) of vectors representing the embedded data points to be optimized ("query" samples)
- `ref_embedding_`: a vector of length (n_samples,) of vectors representing the embedded data points to optimize against ("reference" samples)
- `n_epochs`: the number of training epochs for optimization
- `learning_rate`: the initial learning rate
- `repulsion_strength`: the repulsive strength of negative samples
- `neg_sample_rate`: the number of negative samples per positive sample
- `_a`: this controls the embedding. If the actual argument is `nothing`, this is determined automatically by `min_dist` and `spread`.
- `_b`: this controls the embedding. If the actual argument is `nothing`, this is determined automatically by `min_dist` and `spread`.

"""
function optimize_embedding(graph,
                            query_embedding_::AbstractMatrix,
                            ref_embedding_::AbstractMatrix,
                            n_epochs::Int,
                            learning_rate::Float32,
                            repulsion_strength::Float32,
                            neg_sample_rate::Int,
                            a::Float32,
                            b::Float32;
                            learning_rate_decay::Float32=0.9f0)
    self_reference = query_embedding_ === ref_embedding_  # is it training mode?
    self_reference && (query_embedding_ = copy(ref_embedding_))
    query_embedding = MatrixDatabase(query_embedding_)
    ref_embedding = MatrixDatabase(ref_embedding_)
    #learning_rate_step = convert(Float32, learning_rate / n_epochs + eps(typeof(learning_rate)))
    GR = rowvals(graph)
    # NZ = nonzeros(graph)

    @time for _ in 1:n_epochs
        Threads.@threads for i in 1:size(graph, 2)
            @inbounds QEi = query_embedding[i]

            @inbounds for ind in nzrange(graph, i)
                j = GR[ind]
                REj = ref_embedding[j]
                _gd_loop(QEi, REj, a, b, learning_rate)

                for _ in 1:neg_sample_rate
                    k = rand(eachindex(ref_embedding))
                    i == k && self_reference && continue
                    _gd_neg_loop(QEi, ref_embedding[k], a, b, repulsion_strength, learning_rate)
                end
            end
        end

        if self_reference # training -> update embedding
            ref_embedding_ .= query_embedding_
        end

        learning_rate = max(learning_rate * learning_rate_decay, 1f-6)
    end

    query_embedding_
end

@inline function _gd_loop(QEi, REj, a::Float32, b::Float32, learning_rate::Float32)
    sdist = evaluate(SqEuclidean(), QEi, REj)
    sdist < eps(Float32) && return
    delta = (-2f0 * a * b * sdist^(b-1f0))/(1f0 + a * sdist^b)

    @inbounds @simd for d in eachindex(QEi)
        grad = clamp(delta * (QEi[d] - REj[d]), -4f0, 4f0)
        #grad = delta * (QEi[d] - REj[d])
        QEi[d] += learning_rate * grad
    end
end

@inline function _gd_neg_loop(QEi, REk, a::Float32, b::Float32, repulsion_strength::Float32, learning_rate::Float32)
    sdist = evaluate(SqEuclidean(), QEi, REk)
    if sdist > 0
        delta = (2f0 * repulsion_strength * b) / ((0.001f0 + sdist)*(1f0 + a * sdist^b))

        @inbounds @simd for d in eachindex(QEi)
            grad = clamp(delta * (QEi[d] - REk[d]), -4f0, 4f0)
            #grad = delta * (QEi[d] - REk[d])
            QEi[d] += learning_rate * grad
        end
    else
        @inbounds @simd for d in eachindex(QEi)
            QEi[d] += learning_rate * 4f0
        end
    end
end


text:
using AssociativeWindowAggregation
using BenchmarkTools
using RollingFunctions

function run_example(window::Integer, data::Vector{Int})
    state = FixedWindowAssociativeOp{Int,+}(window)
    result = Vector{Int}(undef, length(data) - (window - 1))
    i = 1
    for x in data
        update_state!(state, x)  # Do work
        window_full(state) || continue  # Only record numbers when the window is full
        @inbounds result[i] = window_value(state)  # Extract mean
        i += 1
    end
    return result
end

run_rolling(window::Integer, data::Vector{Int}) = rolling(sum, data, window)

data = rand(-100:100, 100000)
window = 200

@assert run_example(window, data) == run_rolling(window, data)

@benchmark run_example(window, data)
@benchmark run_rolling(window, data)

# Observations on an M1 MacBook Air, running Julia 1.6.2:
#
# Currently this package is faster for windows >= 20, but is slower for smaller windows.
# This is because we have better complexity [O(1) vs O(n) for RollingFunctions], however we
# incur some additional overhead that results in worse constant factors.


text:
using HDF5

"""
Convert plain-text to HDF5 format.
This is used for converting plain-text word embeddings file into HDF5.
"""
function text2h5(path::String; delim=' ')
    T = Float32
    keys = String[]
    values = T[]
    lines = open(readlines, path)
    for line in lines
        items = split(line, delim)
        push!(keys, String(items[1]))
        value = [parse(T,items[i]) for i=2:length(items)]
        append!(values, value)
    end
    n = length(values) ÷ length(lines)
    if findfirst(x -> x == "UNKNOWN", keys) == 0
        push!(keys, "UNKNOWN")
        append!(values, zeros(T,n))
    end
    v = reshape(values, n, length(keys))
    h5write(path*".h5", "key", keys)
    h5write(path*".h5", "value", v)
end

path = joinpath(dirname(@__FILE__), "glove.840B.300d.txt")
text2h5(path)

max_stars_repo_name: hshindo/Merlin-Examples

max_issues_repo_name: hshindo/Merlin-Examples

max_forks_repo_name: hshindo/Merlin-Examples


text:
using Documenter
using MicroMacro

makedocs(
    sitename = "MicroMacro",
    format = :html,
    modules = [MicroMacro]
)

deploydocs(
    deps   = Deps.pip("mkdocs", "python-markdown-math"),
    repo   = "github.com:JuliaVlasov/MicroMacro.jl.git",
)


text:
# This file is a part of StruckVMEDevices.jl, licensed under the MIT License (MIT).

module VMEGateways

using ArraysOfArrays
using BitOperations
using ElasticArrays
using Sockets
using StructArrays
using Tables
using TypedTables
using UnsafeArrays

using Base.Threads: Atomic

using ..MemRegisters

include("vme.jl")
include("abstract_vme_gateway.jl")
include("sis3316_gateway.jl")

end # module


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder, Pkg

name = "tectonic"
version = v"0.8.0"

# Collection of sources required to build tar
sources = [
    ArchiveSource(
        "https://github.com/tectonic-typesetting/tectonic/archive/tectonic@$(version).tar.gz",
        "794cf34aee017b8a4288ed509a4e9d550a36aadc2bc0d35b1727d1135dac8e02"
    )
]

# Bash recipe for building across all platforms
script = raw"""
cd $WORKSPACE/srcdir/tectonic-*/

if [[ "${target}" == *-mingw* ]]; then
    export RUSTFLAGS="-Clink-args=-L${libdir}"
fi

cargo build --release --locked --features external-harfbuzz
cp target/${rust_target}/release/tectonic${exeext} ${bindir}/
"""

# Some platforms disabled for now due issues with rust and musl cross compilation. See #1673.
platforms = supported_platforms(; experimental=true)
# We dont have all dependencies for armv6l
filter!(p -> arch(p) != "armv6l", platforms)
# Rust toolchain for i686 Windows is unusable
filter!(p -> !Sys.iswindows(p) || arch(p) != "i686", platforms)
platforms = expand_cxxstring_abis(platforms)

# The products that we will ensure are always built
products = [
    ExecutableProduct("tectonic", :tectonic),
]

# Dependencies that must be installed before this package can be built
dependencies = [
    Dependency("Fontconfig_jll"),
    Dependency("FreeType2_jll"),
    Dependency("Graphite2_jll"),
    Dependency("HarfBuzz_jll"; compat="2.8.1"),
    Dependency("HarfBuzz_ICU_jll"),
    Dependency("ICU_jll"; compat="69.1"),
    Dependency("OpenSSL_jll"),
    Dependency("Zlib_jll"),
    Dependency("libpng_jll"),
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; compilers=[:c, :rust], preferred_gcc_version=v"7", lock_microarchitecture=false, julia_compat="1.6")

max_stars_repo_name: sharanry/Yggdrasil

max_issues_repo_name: sharanry/Yggdrasil

max_forks_repo_name: sharanry/Yggdrasil


text:
# MathGL samples w/ IupMglPlot
# Based on: MathGL documentation (v. 1.10), Cap. 9

module mathglsamples_

using Printf
using IUP

export
    mathglsamples

# forward declaration to workaround '@cfunction'
function close_cb  end
function action_cb  end
function minmaxY_dial_btndown_cb end
function minmaxY_dial_btnup_cb end
function autoscaleX_tgg_cb end
function autoscaleY_tgg_cb end
function minmaxX_dial_btndown_cb end
function minmaxX_dial_btnup_cb end
function grid_tgg_cb  end
function box_tgg_cb  end
function legend_tgg_cb  end
function transp_tgg_cb  end
function light_tgg_cb  end
function aa_tgg_cb  end
function opengl_tgg_cb  end
function bt1_cb  end
function bt2_cb  end
function bt3_cb  end
function bt4_cb  end
function postdraw_cb  end
function minmaxY_dial_btndown_cb end

global test_list = [
  ["Plot (Linear 1D)" "SamplePlotLinear1D"],
  ["Plot (Linear 3D)" "SamplePlotLinear3D"],
  ["Radar (Linear 1D)" "SampleRadarLinear1D"],
  ["Area (Linear 1D)" "SampleAreaLinear1D"],
  ["Bars (Linear 1D)" "SampleBarsLinear1D"],
  ["Bars (Linear 3D)" "SampleBarsLinear3D"],
  ["Barh (Linear 1D)" "SampleBarhLinear1D"],
  ["Step (Linear 1D)" "SampleStepLinear1D"],
  ["Stem (Linear 1D)" "SampleStemLinear1D"],
  ["Chart (Linear 1D)" "SampleChartLinear1D"],
  ["Pie (Linear 1D)" "SamplePieLinear1D"],
  ["Dots (Linear 3D)" "SampleDotsLinear3D"],
  ["Crust (Linear 3D)" "SampleCrustLinear3D"],
  ["----------" "Dummy"],
  ["Surface (Planar)" "SampleSurfacePlanar"],
  ["Surface Colors (Planar)" "SampleSurfaceColorsPlanar"],
  ["Surface Contour (Planar)" "SampleSurfaceContourPlanar"],
  ["Mesh (Planar)" "SampleMeshPlanar"],
  ["Fall (Planar)" "SampleFallPlanar"],
  ["Belt (Planar)" "SampleBeltPlanar"],
  ["Tile (Planar)" "SampleTilePlanar"],
  ["Boxes (Planar)" "SampleBoxesPlanar"],
  ["Density (Planar)" "SampleDensityPlanar"],
  ["Contour (Planar)" "SampleContourPlanar"],
  ["Contour Filled (Planar)" "SampleContourFilledPlanar"],
  ["Axial Contour (Planar)" "SampleAxialContourPlanar"],
  ["GradientLines (Planar)" "SampleGradientLinesPlanar"],
  ["----------" "Dummy"],
  ["Iso Surface (Volume)" "SampleIsoSurfaceVolume"],
  ["CloudCubes (Volume)" "SampleCloudCubesVolume"],
  ["Cloud (Volume)" "SampleCloudVolume"],
  ["Density (Volume)" "SampleDensityVolume"],
  ["Contour (Volume)" "SampleContourVolume"],
  ["ContourFilled (Volume)" "SampleContourFilledVolume"],
  ["ContourProject (Volume)" "SampleContourProjectVolume"],
  ["ContourFilledProject (Volume)" "SampleContourFilledProjectVolume"],
  ["DensityProject (Volume)" "SampleDensityProjectVolume"],
  ["----------" "Dummy"],
  ["Text Styles" "SampleText"],
  ["Legend" "SampleLegend"],
  ["Semi-log" "SampleSemiLog"],
  ["Log-log" "SampleLogLog"]]

# ----------------------------------------------------------------------------------
# Since there is no feval in Julia just simulate it here for the current need
# THIS IS A VERY DUMB THING TO DO AND WILL BE REPLACED WITH A CLEVER SOLUTION (WHEN I KNOW HOW)
function feval(func::String)
    if (func == "SamplePlotLinear1D")
        SamplePlotLinear1D()
    elseif (func == "SamplePlotLinear3D")
        SamplePlotLinear3D()
    elseif (func == "SampleRadarLinear1D")
        SampleRadarLinear1D()
    elseif (func == "SampleAreaLinear1D")
        SampleAreaLinear1D()
    elseif (func == "SampleBarsLinear1D")
        SampleBarsLinear1D()
    elseif (func == "SampleBarsLinear3D")
        SampleBarsLinear3D()
    elseif (func == "SampleBarhLinear1D")
        SampleBarhLinear1D()
    elseif (func == "SampleStepLinear1D")
        SampleStepLinear1D()
    elseif (func == "SampleStemLinear1D")
        SampleStemLinear1D()
    elseif (func == "SampleChartLinear1D")
        SampleChartLinear1D()
    elseif (func == "SamplePieLinear1D")
        SamplePieLinear1D()
    elseif (func == "SampleDotsLinear3D")
        SampleDotsLinear3D()
    elseif (func == "SampleCrustLinear3D")
        SampleCrustLinear3D()
    elseif (func == "SampleSurfacePlanar")
        SampleSurfacePlanar()
    elseif (func == "SampleSurfaceColorsPlanar")
        SampleSurfaceColorsPlanar()
    elseif (func == "SampleSurfaceContourPlanar")
        SampleSurfaceContourPlanar()
    elseif (func == "SampleMeshPlanar")
        SampleMeshPlanar()
    elseif (func == "SampleFallPlanar")
        SampleFallPlanar()
    elseif (func == "SampleBeltPlanar")
        SampleBeltPlanar()
    elseif (func == "SampleTilePlanar")
        SampleTilePlanar()
    elseif (func == "SampleBoxesPlanar")
        SampleBoxesPlanar()
    elseif (func == "SampleDensityPlanar")
        SampleDensityPlanar()
    elseif (func == "SampleContourPlanar")
        SampleContourPlanar()
    elseif (func == "SampleContourFilledPlanar")
        SampleContourFilledPlanar()
    elseif (func == "SampleAxialContourPlanar")
        SampleAxialContourPlanar()
    elseif (func == "SampleGradientLinesPlanar")
        SampleGradientLinesPlanar()
    elseif (func == "SampleIsoSurfaceVolume")
        SampleIsoSurfaceVolume()
    elseif (func == "SampleCloudCubesVolume")
        SampleCloudCubesVolume()
    elseif (func == "SampleCloudVolume")
        SampleCloudVolume()
    elseif (func == "SampleDensityVolume")
        SampleDensityVolume()
    elseif (func == "SampleContourVolume")
        SampleContourVolume()
    elseif (func == "SampleContourFilledVolume")
        SampleContourFilledVolume()
    elseif (func == "SampleContourProjectVolume")
        SampleContourProjectVolume()
    elseif (func == "SampleContourFilledProjectVolume")
        SampleContourFilledProjectVolume()
    elseif (func == "SampleDensityProjectVolume")
        SampleDensityProjectVolume()
    elseif (func == "SampleText")
        SampleText()
    elseif (func == "SampleLegend")
        SampleLegend()
    elseif (func == "SampleSemiLog")
        SampleSemiLog()
    elseif (func == "SampleLogLog")
        SampleLogLog()
    end
end

#------------------------example html/examples/C/mathglsamples.c --------------------------------
function mathglsamples()
    global plot, test_list

    count = size(test_list)[1]
    #count = 3

    IupOpen()    #Initializes IUP

    IupControlsOpen()
    IupMglPlotOpen()       # init IupMGLPlot library

    #IupSetGlobal("MGLFONTS", "../etc/mglfonts")

    list = IupList()
    plot = IupMglPlot()
    panel = controlPanel()

    dlg = IupDialog(IupHbox(list, panel, IupVbox(IupFill(), plot, IupFill()) ));
    IupSetAttribute(dlg, "MARGIN", "10x10");
    IupSetAttribute(dlg, "GAP", "5");
    IupSetAttribute(dlg, "TITLE", "MathGL samples w/ IupMglPlot");
    IupSetCallback(dlg, "CLOSE_CB", @cfunction(close_cb, Int, (Ptr{Ihandle},)))

    IupSetAttribute(plot, "RASTERSIZE", "700x500")         # Minimum initial size
    #  IupSetAttribute(plot, "RASTERSIZE", "350x250");
    #  IupSetAttribute(plot, "RASTERSIZE", "460x280");
    #  IupSetAttribute(plot, "EXPAND", "NO");

    IupSetAttribute(list, "EXPAND", "VERTICAL")
    IupSetAttribute(list, "VISIBLELINES", "15")            # Not all, because the dialog will be too big
    IupSetAttribute(list, "VISIBLECOLUMNS", "15")
    IupSetCallback(list, "ACTION", @cfunction(action_cb, Int, (Ptr{Ihandle}, Ptr{UInt8}, Int, Int)))

    for i = 1:count
        str = @sprintf("%d", i)
        IupSetAttribute(list, str, test_list[i][1])
    end

    IupSetAttribute(list, "VALUE", "1")

    IupShowXY(dlg, 100, IUP_CENTER);

    IupSetAttribute(plot, "RASTERSIZE")         # Clear initial size

    ChangePlot(1)                               # Initialize the plot (run first example)
    IupMainLoop()                               # Initializes IUP main loop
    IupClose()                                  # And close it when ready
end

# --------------------------------------------------------------------------------------
function controlPanel()
    global minmaxY_dial, minmaxX_dial, autoscaleY_tgg, autoscaleX_tgg, grid_tgg, box_tgg,
        legend_tgg, transp_tgg, light_tgg, opengl_tgg, aa_tgg

    # left panel: plot control
    # Y zooming
    lbl1 = IupLabel("+")
    lbl2 = IupLabel("-")

    minmaxY_dial = IupDial("VERTICAL")
    IupSetAttribute(minmaxY_dial, "ACTIVE", "NO")
    IupSetAttribute(minmaxY_dial, "SIZE", "20x52")
    IupSetCallback(minmaxY_dial, "BUTTON_PRESS_CB", @cfunction(minmaxY_dial_btndown_cb, Int, (Ptr{Ihandle},)))
    IupSetCallback(minmaxY_dial, "MOUSEMOVE_CB", @cfunction(minmaxY_dial_btnup_cb, Int, (Ptr{Ihandle}, Float64)))
    IupSetCallback(minmaxY_dial, "BUTTON_RELEASE_CB", @cfunction(minmaxY_dial_btnup_cb, Int, (Ptr{Ihandle}, Float64)))

    boxminmaxY_dial = IupVbox(lbl1, minmaxY_dial, lbl2)
    IupSetAttribute(boxminmaxY_dial, "ALIGNMENT", "ACENTER");
    IupSetAttribute(boxminmaxY_dial, "GAP", "2");
    IupSetAttribute(boxminmaxY_dial, "MARGIN", "2");

    autoscaleY_tgg = IupToggle("Y Autoscale")
    IupSetCallback(autoscaleY_tgg, "ACTION", @cfunction(autoscaleY_tgg_cb, Int, (Ptr{Ihandle}, Int)))
    IupSetAttribute(autoscaleY_tgg, "VALUE", "ON");

    f1 = IupFrame(IupVbox(boxminmaxY_dial, autoscaleY_tgg) )
    IupSetAttribute(f1, "TITLE", "Y Zoom");
    IupSetAttribute(f1, "GAP", "0");
    IupSetAttribute(f1, "MARGIN", "5x5");

    # X zooming
    lbl1 = IupLabel("-");
    lbl2 = IupLabel("+");

    minmaxX_dial = IupDial("HORIZONTAL");
    IupSetAttribute(minmaxX_dial, "ACTIVE", "NO");
    IupSetAttribute(minmaxX_dial, "SIZE", "64x16");
    IupSetCallback(minmaxX_dial, "BUTTON_PRESS_CB", @cfunction(minmaxX_dial_btndown_cb, Int, (Ptr{Ihandle},)))
    IupSetCallback(minmaxX_dial, "MOUSEMOVE_CB", @cfunction(minmaxX_dial_btnup_cb, Int, (Ptr{Ihandle}, Float64)))
    IupSetCallback(minmaxX_dial, "BUTTON_RELEASE_CB", @cfunction(minmaxX_dial_btnup_cb, Int, (Ptr{Ihandle}, Float64)))

    boxminmaxX_dial = IupHbox(lbl1, minmaxX_dial, lbl2);
    IupSetAttribute(boxminmaxX_dial, "ALIGNMENT", "ACENTER");
    IupSetAttribute(boxminmaxX_dial, "GAP", "2");
    IupSetAttribute(boxminmaxX_dial, "MARGIN", "2");

    autoscaleX_tgg = IupToggle("X Autoscale");
    IupSetCallback(autoscaleX_tgg, "ACTION", @cfunction(autoscaleX_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    f2 = IupFrame(IupVbox(boxminmaxX_dial, autoscaleX_tgg) )
    IupSetAttribute(f2, "TITLE", "X Zoom");
    IupSetAttribute(f2, "GAP", "0");
    IupSetAttribute(f2, "MARGIN", "5x5");

    grid_tgg = IupToggle("Grid");
    IupSetCallback(grid_tgg, "ACTION", @cfunction(grid_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    box_tgg = IupToggle("Box");
    IupSetCallback(box_tgg, "ACTION", @cfunction(box_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    legend_tgg = IupToggle("Legend");
    IupSetCallback(legend_tgg, "ACTION", @cfunction(legend_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    lbl3 = IupLabel("");
    IupSetAttribute(lbl3, "SEPARATOR", "HORIZONTAL");
    IupSetAttribute(lbl3, "EXPAND", "NO");
    IupSetAttribute(lbl3, "RASTERSIZE", "160x2");

    transp_tgg = IupToggle("Transparent");
    IupSetCallback(transp_tgg, "ACTION", @cfunction(transp_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    light_tgg = IupToggle("Light");
    IupSetCallback(light_tgg, "ACTION", @cfunction(light_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    lbl4 = IupLabel("");
    IupSetAttribute(lbl4, "SEPARATOR", "HORIZONTAL");
    IupSetAttribute(lbl4, "EXPAND", "NO");
    IupSetAttribute(lbl4, "RASTERSIZE", "160x2");

    aa_tgg = IupToggle("Antialias");
    IupSetCallback(aa_tgg, "ACTION", @cfunction(aa_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    opengl_tgg = IupToggle("OpenGL");
    IupSetCallback(opengl_tgg, "ACTION", @cfunction(opengl_tgg_cb, Int, (Ptr{Ihandle}, Int)))

    bt1 = IupButton("Export SVG");
    IupSetCallback(bt1, "ACTION", @cfunction(bt1_cb, Int, (Ptr{Ihandle},)))

    bt2 = IupButton("Export EPS");
    IupSetCallback(bt2, "ACTION", @cfunction(bt2_cb, Int, (Ptr{Ihandle},)))

    bt3 = IupButton("Copy RGB");
    #IupSetCallback(bt3, "ACTION", @cfunction(bt3_cb, Int, (Ptr{Ihandle}, Int)))

    bt4 = IupButton("Save RGB");
    #IupSetCallback(bt4, "ACTION", @cfunction(bt4_cb, Int, (Ptr{Ihandle}, Int)))

    vbox1 = IupFrame(IupVbox(f1,
                           f2,
                           grid_tgg,
                           box_tgg,
                           legend_tgg,
                           lbl3,
                           transp_tgg,
                           light_tgg
                           ))
    IupSetAttribute(vbox1, "GAP", "4");
    IupSetAttribute(vbox1, "MARGIN", "5x5");

    vbox2 = IupVbox(aa_tgg, opengl_tgg, lbl4,
                    IupVbox(bt1, bt2, bt3, bt4),
                   )
    IupSetAttribute(vbox2, "GAP", "4");
    IupSetAttribute(vbox2, "MARGIN", "5x0");

    vbox3 = IupVbox(vbox1, vbox2);
    IupSetAttribute(vbox3, "GAP", "7");
    IupSetAttribute(vbox3, "MARGIN", "0x0");

    return vbox3
end

# --------------------------------------------------------------------------------------
function ChangePlot(item::Int)
    global plot, filenameSVG, filenameEPS, filenamePNG, test_list

    ResetClear()
    feval(test_list[item][2])
    UpdateFlags()
    filenameSVG = @sprintf("../%s.svg", test_list[item][1])
    filenameEPS = @sprintf("../%s.eps", test_list[item][1])
    filenamePNG = @sprintf("../%s.png", test_list[item][1])
    #filenameSVG = "lixo.svg"
    #filenameEPS = "lixo.eps"
    #filenamePNG = "lixo.png"

    # sprintf(filenamePNG, "../html/en/ctrl/images/iupmglplot_%s.png", IupGetAttribute(plot, "DS_MODE"));
    # iupStrLower(filenamePNG, filenamePNG);
    IupSetAttribute(plot, "REDRAW")

    errmsg = IupGetAttribute(plot, "ERRORMESSAGE")
    try
        errmsg = unsafe_string(errmsg)        # This will error if pointer is NULL
        IupMessage("Error", errmsg)        # Found f no way to test if the pointer is NULL
    catch
        errmsg = "NULL"
        IupMessage("Error", errmsg)
    end
end

# --------------------------------------------------------------------------------------
function ResetClear()
    global plot

    IupSetAttribute(plot, "RESET")
    IupSetAttribute(plot, "CLEAR")

    # Some defaults in MathGL are different in IupMglPlot
    IupSetAttribute(plot, "AXS_X", "NO")
    IupSetAttribute(plot, "AXS_Y", "NO")
    IupSetAttribute(plot, "AXS_Z", "NO")

    IupSetAttribute(plot, "FONT", "Helvetica, 8")
    #  IupSetAttribute(plot, "FONT", "Courier, 10");
    #  IupSetAttribute(plot, "FONT", "Heros, 10");
    #  IupSetAttribute(plot, "FONT", "Termes, 12");
    #  IupSetAttribute(plot, "FONT", "XXXX, 10");

    IupSetCallback(plot, "POSTDRAW_CB", C_NULL)
end

# --------------------------------------------------------------------------------------
function UpdateFlags()
    global plot, autoscaleY_tgg, autoscaleX_tgg, minmaxY_dial, minmaxX_dial, grid_tgg, legend_tgg
    global box_tgg, aa_tgg, transp_tgg, light_tgg, opengl_tgg

    # auto scaling Y axis
    if (IupGetInt(plot, "AXS_YAUTOMIN") != 0 && IupGetInt(plot, "AXS_YAUTOMAX") != 0)
        IupSetAttribute(autoscaleY_tgg, "VALUE", "ON")
        IupSetAttribute(minmaxY_dial, "ACTIVE", "NO")
    else
        IupSetAttribute(autoscaleY_tgg, "VALUE", "OFF")
        IupSetAttribute(minmaxY_dial, "ACTIVE", "YES")
    end

    # auto scaling X axis
    if (IupGetInt(plot, "AXS_XAUTOMIN") != 0 && IupGetInt(plot, "AXS_XAUTOMAX") != 0)
        IupSetAttribute(autoscaleX_tgg, "VALUE", "ON")
        IupSetAttribute(minmaxX_dial, "ACTIVE", "NO")
    else
        IupSetAttribute(autoscaleX_tgg, "VALUE", "OFF")
        IupSetAttribute(minmaxX_dial, "ACTIVE", "YES")
    end

    # grid
    value = IupGetAttribute(plot, "GRID")
    if (value != 0 && occursin("XYZ", unsafe_string(value)) != 0)
        IupSetAttribute(grid_tgg, "VALUE", "ON")
    else
        IupSetAttribute(grid_tgg, "VALUE", "OFF")
    end

    # legend */
    if (IupGetInt(plot, "LEGEND") != 0)
        IupSetAttribute(legend_tgg, "VALUE", "ON")
    else
        IupSetAttribute(legend_tgg, "VALUE", "OFF")
    end

    # box
    if (IupGetInt(plot, "BOX") != 0)
        IupSetAttribute(box_tgg, "VALUE", "ON")
    else
        IupSetAttribute(box_tgg, "VALUE", "OFF")
    end

    # antialias
    if (IupGetInt(plot, "ANTIALIAS") != 0)
        IupSetAttribute(aa_tgg, "VALUE", "ON");
    else
        IupSetAttribute(aa_tgg, "VALUE", "OFF")
    end

    # transparent
    if (IupGetInt(plot, "TRANSPARENT") != 0)
        IupSetAttribute(transp_tgg, "VALUE", "ON")
    else
        IupSetAttribute(transp_tgg, "VALUE", "OFF")
    end

    # light
    if (IupGetInt(plot, "LIGHT") != 0)
        IupSetAttribute(light_tgg, "VALUE", "ON")
    else
        IupSetAttribute(light_tgg, "VALUE", "OFF")
    end

    # opengl
    if (IupGetInt(plot, "OPENGL") != 0)
        IupSetAttribute(opengl_tgg, "VALUE", "ON")
    else
        IupSetAttribute(opengl_tgg, "VALUE", "OFF")
    end
end

# --------------------------------------------------------------------------------------
# auto scale Y
function autoscaleY_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot, minmaxY_dial

    if (v != 0)
        IupSetAttribute(minmaxY_dial, "ACTIVE", "NO")
        IupSetAttribute(plot, "AXS_YAUTOMIN", "YES")
        IupSetAttribute(plot, "AXS_YAUTOMAX", "YES")
    else
        IupSetAttribute(minmaxY_dial, "ACTIVE", "YES")
        IupSetAttribute(plot, "AXS_YAUTOMIN", "NO")
        IupSetAttribute(plot, "AXS_YAUTOMAX", "NO")
    end

    IupSetAttribute(plot, "REDRAW")
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# auto scale X
function autoscaleX_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot, minmaxX_dial

    if (v != 0)
        IupSetAttribute(minmaxX_dial, "ACTIVE", "NO")
        IupSetAttribute(plot, "AXS_XAUTOMIN", "YES")
        IupSetAttribute(plot, "AXS_XAUTOMAX", "YES")
    else
        IupSetAttribute(minmaxX_dial, "ACTIVE", "YES")
        IupSetAttribute(plot, "AXS_XAUTOMIN", "NO")
        IupSetAttribute(plot, "AXS_XAUTOMAX", "NO")
    end

    IupSetAttribute(plot, "REDRAW")

    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function postdraw_cb(ih::Ptr{Ihandle})
    IupMglPlotDrawText(ih, "It can be \\wire{wire}, \\big{big} or #r{colored}", 0, 1.0, 0)
    IupMglPlotDrawText(ih, "One can change style in string: \\b{bold}, \\i{italic, \\b{both}}", 0, 0.6, 0)
    IupMglPlotDrawText(ih, "Easy to \\a{overline} or \\u{underline}", 0, 0.2, 0)
    IupMglPlotDrawText(ih, "Easy to change indexes ^{up} _{down} @{center}", 0, -0.2, 0)
    IupMglPlotDrawText(ih, "It parse TeX: \\int \\alpha \\sqrt{sin(\\pi x)^2 + \\gamma_{i_k}} dx", 0, -0.6, 0)
    #  IupMglPlotDrawText(ih, "It parse TeX: \\int \\alpha \\cdot \\sqrt3{sin(\\pi x)^2 + \\gamma_{i_k}} dx", 0, -0.6f, 0)
    IupMglPlotDrawText(ih, "And more TeX: \\sqrt{\\frac{\\alpha^{\\gamma^2}+ \\overset 1{\\big\\infty}}{\\sqrt{2+b}}}", 0, -1.0, 0)
    #  IupMglPlotDrawText(ih, "And more TeX: \\sqrt{\\frac{\\alpha^{\\gamma^2}+" "\\overset 1{\\big\\infty}}{\\sqrt3{2+b}}}", 0, -1.0f, 0)
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function k_enter_cb(ih::Ptr{Ihandle})
    pos = IupGetInt(ih, "VALUE")
    if (pos > 0)
        ChangePlot(pos)
    end
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function action_cb(ih::Ptr{Ihandle}, text::Ptr{UInt8}, item::Int, state::Int)
    if (state == 1)
        ChangePlot(item)
    end
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function close_cb(ih::Ptr{Ihandle})
    return IUP_CLOSE
end

# --------------------------------------------------------------------------------------
# Y zoom
function minmaxY_dial_btndown_cb(self::Ptr{Ihandle})
    global plot

    IupStoreAttribute(plot, "OLD_YMIN", IupGetAttribute(plot, "AXS_YMIN"))
    IupStoreAttribute(plot, "OLD_YMAX", IupGetAttribute(plot, "AXS_YMAX"))
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function minmaxY_dial_btnup_cb(self::Ptr{Ihandle}, angle::Float64)
    global plot

    x1 = IupGetFloat(plot, "OLD_YMIN")
    x2 = IupGetFloat(plot, "OLD_YMAX")

    ss = IupGetAttribute(plot, "AXS_YMODE")
    if (ss != C_NULL)            # Otherwise String barfs
        ss = unsafe_string(ss)
    end
    if (ss != C_NULL && ss[3] == '2')        # LOG2:  one circle will zoom 2 times */
        xm = 4.0 * fabs(angle) / 3.141592
        if (angle > 0.0)
            x2 /= xm; x1 *= xm
        else
            x2 *= xm; x1 /= xm
        end
    end

    if (ss != C_NULL && ss[3] == '1')        # LOG10:  one circle will zoom 10 times
        xm = 10.0 * fabs(angle) / 3.141592
        if (angle > 0.0)
            x2 /= xm; x1 *= xm
        else
            x2 *= xm; x1 /= xm
        end
    else                                # LIN: one circle will zoom 2 times */
        xm = (x1 + x2) / 2.0;
        x1 = xm - (xm - x1)*(1.0-angle*1.0/3.141592);
        x2 = xm + (x2 - xm)*(1.0-angle*1.0/3.141592);
    end

    if (x1 < x2)
        IupSetFloat(plot, "AXS_YMIN", x1)
        IupSetFloat(plot, "AXS_YMAX", x2)
    end

    IupSetAttribute(plot, "REDRAW")

    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# X zoom
function minmaxX_dial_btndown_cb(self::Ptr{Ihandle})
    global plot

    IupStoreAttribute(plot, "OLD_XMIN", IupGetAttribute(plot, "AXS_XMIN"));
    IupStoreAttribute(plot, "OLD_XMAX", IupGetAttribute(plot, "AXS_XMAX"));
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function minmaxX_dial_btnup_cb(self::Ptr{Ihandle}, angle::Float64)
    global plot

    x1 = IupGetFloat(plot, "OLD_XMIN");
    x2 = IupGetFloat(plot, "OLD_XMAX");

    xm = (x1 + x2) / 2.0
    x1 = xm - (xm - x1)*(1.0-angle*1.0/3.141592)    # one circle will zoom 2 times
    x2 = xm + (x2 - xm)*(1.0-angle*1.0/3.141592)

    IupSetFloat(plot, "AXS_XMIN", x1)
    IupSetFloat(plot, "AXS_XMAX", x2)
    IupSetAttribute(plot, "REDRAW")

    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# show/hide grid */
function grid_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot

    if (v != 0)
        IupSetAttribute(plot, "GRID", "YES")
    else
        IupSetAttribute(plot, "GRID", "NO")
    end

    IupSetAttribute(plot, "REDRAW")
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# show/hide legend */
function legend_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot

    if (v != 0)
        IupSetAttribute(plot, "LEGEND", "YES")
    else
        IupSetAttribute(plot, "LEGEND", "NO")
    end

    IupSetAttribute(plot, "REDRAW")
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# show/hide box
function box_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot

    if (v != 0)
        IupSetAttribute(plot, "BOX", "YES")
    else
        IupSetAttribute(plot, "BOX", "NO")
    end

    IupSetAttribute(plot, "REDRAW")
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# enable/disable antialias
function aa_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot

    if (v != 0)
        IupSetAttribute(plot, "ANTIALIAS", "YES");
    else
        IupSetAttribute(plot, "ANTIALIAS", "NO")
    end

    IupSetAttribute(plot, "REDRAW")
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# enable/disable transparent
function transp_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot

    if (v != 0)
        IupSetAttribute(plot, "TRANSPARENT", "YES");
    else
        IupSetAttribute(plot, "TRANSPARENT", "NO")
    end

    IupSetAttribute(plot, "REDRAW")

    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# enable/disable light */
function light_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot

    if (v != 0)
        IupSetAttribute(plot, "LIGHT", "YES")
    else
        IupSetAttribute(plot, "LIGHT", "NO")
    end

    IupSetAttribute(plot, "REDRAW")
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
# enable/disable opengl */
function opengl_tgg_cb(self::Ptr{Ihandle}, v::Int)
    global plot, aa_tgg

    if (v != 0)
        IupSetAttribute(plot, "OPENGL", "YES")
    else
        IupSetAttribute(aa_tgg, "VALUE", "OFF")
        IupSetAttribute(plot, "ANTIALIAS", "NO")
        IupSetAttribute(plot, "OPENGL", "NO")
    end

    IupSetAttribute(plot, "REDRAW")
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function bt1_cb(self::Ptr{Ihandle})
    global plot, filenameSVG

    IupMglPlotPaintTo(plot, "SVG", 800, 800, 300, filenameSVG);
    return IUP_DEFAULT
end

# --------------------------------------------------------------------------------------
function bt2_cb(self::Ptr{Ihandle})
    global plot, filenameEPS

    IupMglPlotPaintTo(plot, "EPS", 800, 800, 300, filenameEPS);
    return IUP_DEFAULT
end

#=
# --------------------------------------------------------------------------------------
function bt3_cb(self::Ptr{Ihandle})
  clipboard = IupClipboard();
  IupGetIntInt(plot, "DRAWSIZE", &w, &h);
  gldata = malloc(w*h*3);
  image = imImageCreate(w, h, IM_RGB, IM_BYTE);
  IupMglPlotPaintTo(plot, "RGB", w, h, 0, gldata);
  imConvertPacking(gldata, image->data[0], w, h, 3, 3, IM_BYTE, 1);
  imProcessFlip(image, image);
  IupSetAttribute(clipboard, "NATIVEIMAGE", IupGetImageNativeHandle(image));
  IupDestroy(clipboard);
  free(gldata);
  imImageDestroy(image);
  return IUP_DEFAULT;
end

# --------------------------------------------------------------------------------------
function bt4_cb(self::Ptr{Ihandle})
  global plot, filenamePNG
  IupGetIntInt(plot, "DRAWSIZE", &w, &h);
  gldata = malloc(w*h*3);

  image = imImageCreate(w, h, IM_RGB, IM_BYTE);
  IupMglPlotPaintTo(plot, "RGB", w, h, 0, gldata);
  imConvertPacking(gldata, image->data[0], w, h, 3, 3, IM_BYTE, 1);
  imProcessFlip(image, image);
  imFileImageSave(filenamePNG, "PNG", image);
  free(gldata);
  imImageDestroy(image);
  return IUP_DEFAULT;
end
=#

# --------------------------------------------------------------------------------------
function SampleText()
    IupSetCallback(plot, "POSTDRAW_CB", @cfunction(postdraw_cb, Int, (Ptr{Ihandle},)))
end

# --------------------------------------------------------------------------------------
function SampleLegend()
    global plot

    IupMglPlotNewDataSet(plot, 1)
    IupSetAttribute(plot, "DS_LEGEND", "sin(\\pi {x^2})")
    IupMglPlotSetFromFormula(plot, 0, "sin(2*pi*x*x)", 50, 1, 1)

    IupMglPlotNewDataSet(plot, 1)
    IupSetAttribute(plot, "DS_LEGEND", "sin(\\pi x)")
    IupMglPlotSetFromFormula(plot, 1, "sin(2*pi*x)", 50, 1, 1)

    IupMglPlotNewDataSet(plot, 1)
    IupSetAttribute(plot, "DS_LEGEND", "sin(\\pi \\sqrt{\\a x})")
    IupMglPlotSetFromFormula(plot, 2, "sin(2*pi*sqrt(x))", 50, 1, 1)

    IupSetAttribute(plot, "LEGEND", "YES")
    IupSetAttribute(plot, "AXS_X", "Yes")
    IupSetAttribute(plot, "AXS_Y", "Yes")
    IupSetAttribute(plot, "BOX", "YES")
end

# --------------------------------------------------------------------------------------
function SampleSemiLog()
    global plot

    IupMglPlotNewDataSet(plot, 2)
    IupMglPlotSetFormula(plot, 0, "0.01/(x+10^(-5))", "sin(1/x)", "", 2000);
    IupSetAttribute(plot, "DS_COLOR", "0 0 255");
    IupSetAttribute(plot, "DS_LINEWIDTH", "2");

    IupSetAttribute(plot, "AXS_XSCALE", "LOG10");
    IupSetAttribute(plot, "AXS_X", "Yes");
    IupSetAttribute(plot, "AXS_Y", "Yes");
    IupSetAttribute(plot, "AXS_XLABEL", "x");
    IupSetAttribute(plot, "AXS_YLABEL", "y = sin 1/x");
    IupSetAttribute(plot, "BOX", "YES");
    IupSetAttribute(plot, "GRID", "YES");
    IupSetAttribute(plot, "GRIDCOLOR", "0 255 0");
end

# --------------------------------------------------------------------------------------
function SampleLogLog()
    global plot

    IupMglPlotNewDataSet(plot, 2);
    IupMglPlotSetFormula(plot, 0, "pow(10,6*x-3)", "sqrt(1+x^2)", "", 100)
    IupSetAttribute(plot, "DS_COLOR", "0 0 255")
    IupSetAttribute(plot, "DS_LINEWIDTH", "2")

    IupSetAttribute(plot, "AXS_XSCALE", "LOG10")
    IupSetAttribute(plot, "AXS_YSCALE", "LOG10")
    IupSetAttribute(plot, "AXS_X", "Yes")
    IupSetAttribute(plot, "AXS_Y", "Yes")
    IupSetAttribute(plot, "AXS_XLABEL", "x")
    IupSetAttribute(plot, "AXS_YLABEL", "y=\\sqrt{1+x^2}")
    IupSetAttribute(plot, "BOX", "YES")
    IupSetAttribute(plot, "GRID", "YES")
    IupSetAttribute(plot, "GRIDCOLOR", "0 255 0")
    IupSetAttribute(plot, "GRIDLINESTYLE", "DASHED")
end

# --------------------------------------------------------------------------------------
function SampleVolume(ds_mode::String)
    global plot
    IupMglPlotNewDataSet(plot, 1)
    IupMglPlotSetFromFormula(plot, 0, "-2*((2*x-1)^2 + (2*y-1)^2 + (2*z-1)^4 - (2*z-1)^2 - 0.1)", 60, 50, 40)
    IupSetAttribute(plot, "DS_MODE", ds_mode)

    IupSetAttribute(plot, "ROTATE", "40:0:60")
    IupSetAttribute(plot, "LIGHT", "YES")
    IupSetAttribute(plot, "TRANSPARENT", "YES")
    IupSetAttribute(plot, "BOX", "YES")
end

# --------------------------------------------------------------------------------------
function SampleDensityProjectVolume()
    global plot

    SampleVolume("VOLUME_DENSITY")
    IupSetAttribute(plot, "PROJECT", "YES")
    IupSetAttribute(plot, "PROJECTVALUEX", "-1")
    IupSetAttribute(plot, "PROJECTVALUEY", "1")
    IupSetAttribute(plot, "PROJECTVALUEZ", "-1")
    IupSetAttribute(plot, "TRANSPARENT", "NO")
    IupSetAttribute(plot, "LIGHT", "NO")
end

# --------------------------------------------------------------------------------------
function SampleContourProjectVolume()
    global plot

  SampleVolume("VOLUME_CONTOUR");
  IupSetAttribute(plot, "PROJECT", "YES");
  IupSetAttribute(plot, "PROJECTVALUEX", "-1");
  IupSetAttribute(plot, "PROJECTVALUEY", "1");
  IupSetAttribute(plot, "PROJECTVALUEZ", "-1");
  IupSetAttribute(plot, "TRANSPARENT", "NO");
  #  IupSetAttribute(plot, "LIGHT", "NO");
end

# --------------------------------------------------------------------------------------
function SampleContourFilledProjectVolume()
    global plot

  SampleVolume("VOLUME_CONTOUR")
  IupSetAttribute(plot, "PROJECT", "YES");
  IupSetAttribute(plot, "PROJECTVALUEX", "-1");
  IupSetAttribute(plot, "PROJECTVALUEY", "1");
  IupSetAttribute(plot, "PROJECTVALUEZ", "-1");
  IupSetAttribute(plot, "CONTOURFILLED", "YES");
  IupSetAttribute(plot, "TRANSPARENT", "NO");
  #  IupSetAttribute(plot, "LIGHT", "NO");
end

# --------------------------------------------------------------------------------------
function SampleContourFilledVolume()
    global plot

  SampleVolume("VOLUME_CONTOUR")
  IupSetAttribute(plot, "CONTOURFILLED", "YES");
  IupSetAttribute(plot, "TRANSPARENT", "NO");
end

# --------------------------------------------------------------------------------------
function SampleContourVolume()
    global plot

  SampleVolume("VOLUME_CONTOUR");
  IupSetAttribute(plot, "TRANSPARENT", "NO");
  #  IupSetAttribute(plot, "LIGHT", "NO");
end

# --------------------------------------------------------------------------------------
function SampleDensityVolume()
    global plot

  SampleVolume("VOLUME_DENSITY");
  IupSetAttribute(plot, "AXS_XORIGIN", "0");
  IupSetAttribute(plot, "AXS_YORIGIN", "0");
  IupSetAttribute(plot, "AXS_ZORIGIN", "0");
  IupSetAttribute(plot, "AXS_X", "Yes");
  IupSetAttribute(plot, "AXS_Y", "Yes");
  IupSetAttribute(plot, "AXS_Z", "Yes");
  IupSetAttribute(plot, "LIGHT", "NO");
end

# --------------------------------------------------------------------------------------
function SampleCloudVolume()
    global plot

  SampleVolume("VOLUME_CLOUD")
  IupSetAttribute(plot, "COLORSCHEME", "wyrRk")
  IupSetAttribute(plot, "CLOUDCUBES", "NO");
  IupSetAttribute(plot, "LIGHT", "NO");
end

# --------------------------------------------------------------------------------------
function SampleCloudCubesVolume()
    global plot

  SampleVolume("VOLUME_CLOUD")
  IupSetAttribute(plot, "COLORSCHEME", "wyrRk")
  IupSetAttribute(plot, "LIGHT", "NO")
end

# --------------------------------------------------------------------------------------
function SampleIsoSurfaceVolume()
    global plot
    SampleVolume("VOLUME_ISOSURFACE")
end

# --------------------------------------------------------------------------------------
function SamplePlanar(ds_mode::String)
    global plot

    ds = IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, Int(ds), "0.6*sin(2*pi*x)*sin(3*pi*y) + 0.4*cos(3*pi*(x*y))", 50, 40, 1);
    IupSetAttribute(plot, "DS_MODE", ds_mode);

    IupSetAttribute(plot, "ROTATE", "40:0:60");
    IupSetAttribute(plot, "LIGHT", "YES");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleGradientLinesPlanar()
    global plot

    SamplePlanar("PLANAR_GRADIENTLINES");
    SamplePlanar("PLANAR_DENSITY");
    IupSetAttribute(plot, "TRANSPARENT", "YES");
    IupSetAttribute(plot, "ROTATE");
end

# --------------------------------------------------------------------------------------
function SampleAxialContourPlanar()
    global plot

    SamplePlanar("PLANAR_AXIALCONTOUR");
    IupSetAttribute(plot, "TRANSPARENT", "YES");
end

# --------------------------------------------------------------------------------------
function SampleContourFilledPlanar()
    global plot

    SamplePlanar("PLANAR_CONTOUR");
    IupSetAttribute(plot, "CONTOURFILLED", "Yes");
end

# --------------------------------------------------------------------------------------
function SampleContourPlanar()
    global plot

    SamplePlanar("PLANAR_CONTOUR")
    IupSetAttribute(plot, "CONTOURLABELS", "BELLOW")
end

# --------------------------------------------------------------------------------------
function SampleDensityPlanar()
    global plot

    SamplePlanar("PLANAR_DENSITY");
    IupSetAttribute(plot, "COLORBAR", "Yes");
    IupSetAttribute(plot, "ROTATE");
end

# --------------------------------------------------------------------------------------
function SampleBoxesPlanar()
    SamplePlanar("PLANAR_BOXES");
    IupSetAttribute(plot, "AXS_XORIGIN", "0");
    IupSetAttribute(plot, "AXS_YORIGIN", "0");
    IupSetAttribute(plot, "AXS_ZORIGIN", "0");
end

# --------------------------------------------------------------------------------------
function SampleTilePlanar()
    SamplePlanar("PLANAR_TILE")
end

# --------------------------------------------------------------------------------------
function SampleBeltPlanar()
    SamplePlanar("PLANAR_BELT");
end

# --------------------------------------------------------------------------------------
function SampleFallPlanar()
    SamplePlanar("PLANAR_FALL")
end

# --------------------------------------------------------------------------------------
function SampleMeshPlanar()
    SamplePlanar("PLANAR_MESH")
end

# --------------------------------------------------------------------------------------
function SampleSurfaceColorsPlanar()
    global plot
    SamplePlanar("PLANAR_SURFACE");
    IupSetAttribute(plot, "COLORSCHEME", "BbcyrR|");
end

# --------------------------------------------------------------------------------------
function SampleSurfacePlanar()
    SamplePlanar("PLANAR_SURFACE")
end

# --------------------------------------------------------------------------------------
function SampleSurfaceContourPlanar()
    SamplePlanar("PLANAR_SURFACE");
    SamplePlanar("PLANAR_CONTOUR");
end

# --------------------------------------------------------------------------------------
function SampleDotsLinear3D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotLoadData(plot, 0, "../test/hotdogs.pts", 0, 0, 0);
    IupSetAttribute(plot, "DS_MODE", "DOTS");
    IupSetAttribute(plot, "DS_REARRANGE");

    IupSetAttribute(plot, "ROTATE", "40:0:60");
    IupSetAttribute(plot, "LIGHT", "YES");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleCrustLinear3D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotLoadData(plot, 0, "../test/hotdogs.pts", 0, 0, 0);
    IupSetAttribute(plot, "DS_MODE", "CRUST");
    IupSetAttribute(plot, "DS_REARRANGE");

    IupSetAttribute(plot, "ROTATE", "40:0:60");
    IupSetAttribute(plot, "LIGHT", "YES");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SamplePieLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 0, "rnd+0.1", 7, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "CHART");

    IupSetAttribute(plot, "COLORSCHEME", "bgr cmy");
    IupSetAttribute(plot, "PIECHART", "Yes");
    IupSetAttribute(plot, "DATAGRID", "Yes");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleChartLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 0, "rnd+0.1", 7, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "CHART");
    IupSetAttribute(plot, "DATAGRID", "Yes");

    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleStemLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 0, "0.7*sin(2*pi*x) + 0.5*cos(3*pi*x) + 0.2*sin(pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "STEM");
    IupSetAttribute(plot, "DS_MARKSTYLE", "HOLLOW_CIRCLE");
    IupSetAttribute(plot, "DS_SHOWMARKS", "Yes");

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 1, "sin(2*pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "STEM");
    IupSetAttribute(plot, "DS_MARKSTYLE", "HOLLOW_CIRCLE");
    IupSetAttribute(plot, "DS_SHOWMARKS", "Yes");

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 2, "cos(2*pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "STEM");
    IupSetAttribute(plot, "DS_MARKSTYLE", "HOLLOW_CIRCLE");
    IupSetAttribute(plot, "DS_SHOWMARKS", "Yes");

    IupSetAttribute(plot, "AXS_XORIGIN", "0");
    IupSetAttribute(plot, "AXS_YORIGIN", "0");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleStepLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1)
    IupMglPlotSetFromFormula(plot, 0, "0.7*sin(2*pi*x) + 0.5*cos(3*pi*x) + 0.2*sin(pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "STEP")

    IupMglPlotNewDataSet(plot, 1)
    IupMglPlotSetFromFormula(plot, 1, "sin(2*pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "STEP");

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 2, "cos(2*pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "STEP");

    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleBarhLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1)
    IupMglPlotSetFromFormula(plot, 0, "0.8*sin(pi*(2*x+y/2))+0.2*rnd", 10, 1, 1)
    IupSetAttribute(plot, "DS_MODE", "BARHORIZONTAL")

    IupSetAttribute(plot, "AXS_XORIGIN", "0")
    IupSetAttribute(plot, "AXS_YORIGIN", "0")
    IupSetAttribute(plot, "BOX", "YES")
end

# --------------------------------------------------------------------------------------
function SampleBarsLinear3D()
    global plot

    IupMglPlotNewDataSet(plot, 3);
    IupMglPlotSetFormula(plot, 0, "cos(pi*2*x-pi)", "sin(pi*(2*x-1))", "2*x-1", 50)
    IupSetAttribute(plot, "DS_MODE", "BAR");
    IupSetAttribute(plot, "DS_LINESTYLE", "SMALLDASH_DOT");

    IupSetAttribute(plot, "ROTATE", "40:0:60");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleBarsLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 0, "0.8*sin(pi*(2*x+y/2))+0.2*rnd", 10, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "BAR");

    #TODO: allow combination of several datasets into one bar plot
    #TODO: allow bars "above" sample

    IupSetAttribute(plot, "AXS_XORIGIN", "0");
    IupSetAttribute(plot, "AXS_YORIGIN", "0");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleAreaLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 0, "0.7*sin(2*pi*x) + 0.5*cos(3*pi*x) + 0.2*sin(pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "AREA");

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 1, "sin(2*pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "AREA");

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 2, "cos(2*pi*x)", 50, 1, 1);
    IupSetAttribute(plot, "DS_MODE", "AREA");

    IupSetAttribute(plot, "AXS_XORIGIN", "0");
    IupSetAttribute(plot, "AXS_YORIGIN", "0");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SampleRadarLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 0, "0.4*sin(pi*(2*x+y/2))+0.1*rnd", 10, 3, 1);
    IupSetAttribute(plot, "DS_MODE", "RADAR");
    IupSetAttribute(plot, "DS_SPLIT", "");

    IupSetAttribute(plot, "CURRENT", "1");
    IupSetAttribute(plot, "DS_MODE", "RADAR");

    IupSetAttribute(plot, "CURRENT", "2");
    IupSetAttribute(plot, "DS_MODE", "RADAR");

    IupSetAttribute(plot, "RADARSHIFT", "0.4");  # So all datasets will use the same radarshift
    IupSetAttribute(plot, "DATAGRID", "Yes");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SamplePlotLinear3D()
    global plot

    IupMglPlotNewDataSet(plot, 3);
    IupMglPlotSetFormula(plot, 0, "cos(pi*2*x-pi)", "sin(pi*(2*x-1))", "2*x-1", 50);
    IupSetAttribute(plot, "DS_MODE", "LINE");

    IupSetAttribute(plot, "ROTATE", "40:0:60");
    IupSetAttribute(plot, "BOX", "YES");
end

# --------------------------------------------------------------------------------------
function SamplePlotLinear1D()
    global plot

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 0, "0.7*sin(2*pi*x)+0.5*cos(3*pi*x)+0.2*sin(pi*x)", 50, 1, 1);

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 1, "sin(2*pi*x)", 50, 1, 1);

    IupMglPlotNewDataSet(plot, 1);
    IupMglPlotSetFromFormula(plot, 2, "cos(2*pi*x)", 50, 1, 1);

    IupSetAttribute(plot, "BOX", "YES");
end

function Dummy()
end

end     # module


text:
using TypeClasses

"""
IMPORTANT: we do NOT support AbstractDict, because there is no general way to map over such a type,
i.e. we cannot easily construct new AbstractDict from the same type, but with small alterations.
"""

# Monoid Instances for standard Dict
# ===================================

# generic neutral for Dict
TypeClasses.neutral(::Type{Dict}) = Dict()
TypeClasses.neutral(::Type{Dict{K, V}}) where {K, V} = Dict{K, V}()

# generic combine/⊕ for Dict: using ⊕ on the elements when needed
function TypeClasses.combine(d1::Dict, d2::Dict)
    newdict = Dict(
        key => haskey(d2, key) ? value ⊕ d2[key] : value
        for (key, value) in d1)
    for (key, value) in d2
        if !haskey(newdict, key)
            push!(newdict, key => value)
        end
    end
    newdict
end

# generic orelse/⊘ for Dict
"""
    orelse(d1::Dict, d2::Dict) -> Dict

Following the orelse semantics on Option values, the first value is retained, and the second is dropped.
Hence this is the flipped version of `Base.merge`.
"""
TypeClasses.orelse(d1::Dict, d2::Dict) = merge(d2, d1)

# Functor/Applicative/Monad
# =========================

# Dict does not support map, as it would restrict the function applied to return Pairs and not arbitrary types
# TypeClasses.map(f, d::Dict) = Dict(f(pair) for pair in d)

# FlipTypes
# =========

function TypeClasses.flip_types(a::Dict)
    iter = a
    first = iterate(iter)

    if first === nothing
      # only in this case we actually need `pure(eltype(A))` and `neutral(A)`
      # for non-empty sequences everything works for Types without both
      pure(eltype(a), neutral(Dict))
    else
      (key, b), state = first
      start = map(c -> Dict(key => c), b)  # we can only combine on ABC
      Base.foldl(Iterators.rest(iter, state); init = start) do acc, (key, b)
        mapn(acc, b) do acc′, c  # working in applicative context B
          acc′ ⊕ Dict(key => c)  # combining on AC
        end
      end
    end
end