text:
function code_typed_argtys(@nospecialize(types=Tuple); optimize=true, params=nothing, constvals=nothing)
    ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions")
    types = Base.to_tuple_type(types)
    asts = []
    world = ccall(:jl_get_world_counter, UInt, ())
    params = params === nothing ? Core.Compiler.Params(world) : params
    for x in Base._methods_by_ftype(types, -1, world)
        meth = Base.func_for_method_checked(x[3], Tuple{types.parameters[2:end]...})
        argtypes = nothing
        if constvals !== nothing
            argtypes = Vector(undef, length(types.parameters)+1)
            for i = 1:length(types.parameters)+1
                argtypes[i] = isassigned(constvals, i) ?
                    Core.Compiler.Const(constvals[i]) : (
                        i == 1 ? typeof(f) : types.parameters[i-1])
            end
        end
        (code, ty) = Core.Compiler.typeinf_code(meth, x[1], x[2], optimize, params, argtypes)
        code === nothing && error("inference not successful") # inference disabled?
        push!(asts, code => ty)
    end
    return asts
end

function code_typed_xla(f, argtypes::Type)
    argvals = Vector{Any}(undef, length(argtypes.parameters) + 1)
    argvals[1] = f
    params = Compiler.CustomParams(typemax(UInt); aggressive_constant_propagation=true, ignore_all_inlining_heuristics=true)
    ci = (Compiler == Core.Compiler ? Base.code_typed : NI.code_typed)(f, argtypes, argvals; params=params)[1].first
    method = which(f, argtypes)
    (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any),
                              Compiler.argtypes_to_type(Any[typeof(f), argtypes.parameters...]),
                              method.sig)
    mi = Compiler.code_for_method(method, metharg, methsp, params.world);
    sv = Compiler.OptimizationState(mi, params);
    ir = Compiler.inflate_ir(ci, mi);
    run_xla_embedding_passes!(ir, sv)
end

function code_typed_xla(sig::Type; argvals=nothing)
    params = Compiler.CustomParams(typemax(UInt); aggressive_constant_propagation=true, ignore_all_inlining_heuristics=true)
    cds = code_typed_argtys(sig; params=params, constvals=argvals)
    if length(cds) == 0
        return nothing
    end
    ci = cds[1].first
    methods = Base._methods_by_ftype(sig, -1, params.world)
    method = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), sig, params.world).func
    (metharg, methsp) = ccall(:jl_type_intersection_with_env, Any, (Any, Any),
                              Compiler.argtypes_to_type(Any[sig.parameters...]),
                              method.sig)
    mi = Compiler.code_for_method(method, metharg, methsp, params.world);
    sv = Compiler.OptimizationState(mi, params);
    ir = Compiler.inflate_ir(ci, mi);
    run_xla_embedding_passes!(ir, sv)
end

function Base.dump(c::XLAComputation)
   mktemp() do f, io
       writeproto(io, c.hlo_snapshot)
       close(io)
       run(`$(joinpath(dirname(dirname(pathof(TensorFlow))), "deps", "downloads", "bin", "dumped_computation_to_text")) $f`)
   end
   nothing
end

macro tpu_dump(expr)
    @assert isexpr(expr, :call)
    quote
        let f = $(esc(expr.args[1]))
            ir, sv = code_typed_xla(f, Base.typesof($(map(esc, expr.args[2:end])...)))
            Base.dump(XLA.compile_to_xla(ir, sv)[1])
        end
    end
end

macro tpu_dump_file(file, expr)
    @assert isexpr(expr, :call)
    quote
        let f = $(esc(expr.args[1]))
            ir, sv = code_typed_xla(f, Base.typesof($(map(esc, expr.args[2:end])...)))
            c, rt = XLA.compile_to_xla(ir, sv)
            open($(esc(file)), "w") do io
                writeproto(io, c.hlo_snapshot)
            end
        end
    end
end

make_options(sess::Session) = nothing
function make_options(;sess::TPUSession=global_session(), devices=nothing)
    replica_device_coords = devices === nothing ? nothing : sess.topology[devices]
    (sess, replica_device_coords)
end

adjust_type(T) = T
adjust_type(::Type{XRTRemoteStruct{T}}) where {T} = T

macro tpu_compile(args...)
    expr = args[end] # The expression to compile
    kwargs = Expr[]
    for i in 1:length(args)-1
        x = args[i]
        if x isa Expr && x.head == :(=) # Keyword given of the form "foo=bar"
            push!(kwargs, x)
        else
            return Expr(:call, :error, "@tpu_compile expects only one non-keyword argument")
        end
    end
    opts = Expr(:call, make_options,
            (Expr(:kw, esc(kw.args[1]), esc(kw.args[2])) for kw in kwargs)...)
    @assert isexpr(expr, :call)
    quote
        let f = $(esc(expr.args[1]))
            argtypes = Tuple{map(adjust_type, Base.typesof($(map(esc, expr.args[2:end])...)).parameters)...}
            ir, sv = code_typed_xla(f, argtypes)
            sess, replica_device_coords = $opts
            try
                XLA.compile(sess, XLA.compile_to_xla(ir, sv; replica_device_coords=replica_device_coords)...)
            catch err
                @warn("Compilation failed; attempting to explain suboptimal inference:")
                XLA.explain_suboptimal_inference(Tuple{typeof(f), argtypes.parameters...})
                rethrow(err)
            end
        end
    end
end

macro tpu(args...)
    expr = args[end]
    @assert isexpr(expr, :call)
    quote
        the_args = tuple($(map(esc,expr.args)...))
        handle = @tpu_compile $(args[1:end-1]...) $(Expr(:call, (Expr(:call, getfield, :the_args, i) for i = 1:length(expr.args))...))
        run(handle, the_args...)
    end
end


text:
using Distributed
import Dagger: Context, Processor, OSProc, ThreadProc, get_parent, get_processors
import Dagger.Sch: ThunkOptions

@everywhere begin

struct UnknownStruct end

struct OptOutProc <: Dagger.Processor end

end

@testset "Processors" begin
    @testset "Parents/Children" begin
        tp = ThreadProc(1, 1)
        @test tp isa Processor
        op = get_parent(tp)
        @test op isa Processor
        @test op isa OSProc
        @test op.pid == 1
    end
    @testset "Function/argument compatability" begin
        unknown_func = () -> nothing
        tp = ThreadProc(1, 1)
        op = get_parent(tp)
        opts = ThunkOptions()
        us = UnknownStruct()
        for proc in (op, tp)
            @test Dagger.iscompatible_func(proc, opts, unknown_func)
            @test Dagger.iscompatible_arg(proc, opts, typeof(us))
            @test Dagger.iscompatible(proc, opts, unknown_func, typeof(us), Int, typeof(us), Float64)
        end
    end
    @testset "Opt-in/Opt-out" begin
        @test Dagger.default_enabled(OSProc()) == true
        @test Dagger.default_enabled(ThreadProc(1,1)) == true
        @test Dagger.default_enabled(OptOutProc()) == false
    end
    @testset "Processor exhaustion" begin
        opts = ThunkOptions(proclist=[OptOutProc])
        @test_throws_unwrap AssertionError collect(delayed(sum; options=opts)([1,2,3]))
        opts = ThunkOptions(proclist=(proc)->false)
        @test_throws_unwrap AssertionError collect(delayed(sum; options=opts)([1,2,3]))
        opts = ThunkOptions(proclist=nothing)
        @test collect(delayed(sum; options=opts)([1,2,3])) == 6
    end
    @testset "Roundtrip move()" begin
        ctx = Context()
        tp = ThreadProc(1, 1)
        op = get_parent(tp)
        value = rand()
        moved_value = Dagger.move(tp, op, Dagger.move(op, tp, value))
        @test value === moved_value
    end
    @testset "Add callback in same world" begin
        function addcb()
            @everywhere Dagger.add_callback!(cb)
            cb = eval(Dagger, :(proc->PathProc(myid())))
            opts = ThunkOptions(proclist=[PathProc])
            collect(delayed(identity; options=opts)(1.0))
            @everywhere pop!(Dagger.PROCESSOR_CALLBACKS)
        end
    end

    @testset "Modify workers in Context" begin
        ps = addprocs(4, exeflags="--project")
        @everywhere ps using Dagger

        ctx = Context(ps[1:2])

        Dagger.addprocs!(ctx, ps[3:end])
        @test map(p -> p.pid, procs(ctx)) == ps

        Dagger.rmprocs!(ctx, ps[3:end])
        @test map(p -> p.pid, procs(ctx)) == ps[1:2]

        wait(rmprocs(ps))
    end

    @testset "Callable as Thunk function" begin
        @everywhere begin
            struct ABC end
            (::ABC)(x) = x+1
        end

        abc = ABC()
        a = delayed(abc)(1)
        @test collect(a) == 2
    end

    @testset "Processor TLS accessor" begin
        @everywhere function mythunk(x)
            typeof(Dagger.thunk_processor())
        end
        @test collect(delayed(mythunk)(1)) === ThreadProc
    end
end

ext: jl


text:
const histogram_plot_types = [BarPlot, Heatmap, Volume]

function convert_arguments(P::Type{<:AbstractPlot}, h::StatsBase.Histogram{<:Any, N}) where N
    ptype = plottype(P, histogram_plot_types[N])
    f(edges) = edges[1:end-1] .+ diff(edges)./2
    kwargs = N == 1 ? (; width = step(h.edges[1]), gap = 0, dodge_gap = 0) : NamedTuple()
    to_plotspec(ptype, convert_arguments(ptype, map(f, h.edges)..., Float64.(h.weights)); kwargs...)
end

"""
    hist(values; bins = 15, normalization = :none)

Plot a histogram of `values`. `bins` can be an `Int` to create that
number of equal-width bins over the range of `values`.
Alternatively, it can be a sorted iterable of bin edges. The histogram
can be normalized by setting `normalization`. Possible values are:

*  `:pdf`: Normalize by sum of weights and bin sizes. Resulting histogram
   has norm 1 and represents a PDF.
* `:density`: Normalize by bin sizes only. Resulting histogram represents
   count density of input and does not have norm 1. Will not modify the
   histogram if it already represents a density (`h.isdensity == 1`).
* `:probability`: Normalize by sum of weights only. Resulting histogram
   represents the fraction of probability mass for each bin and does not have
   norm 1.
*  `:none`: Do not normalize.

The following attributes can move the histogram around,
which comes in handy when placing multiple histograms into one plot:
* offset = 0.0: adds an offset to every value
* fillto = 0.0: defines where the bar starts
* scale_to = nothing: allows to scale all values to a certain height
* flip = false: flips all values

Color can either be:
* a vector of `bins` colors
* a single color
* `:values`, to color the bars with the values from the histogram

## Attributes
$(ATTRIBUTES)
"""
@recipe(Hist, values) do scene
    Attributes(
        bins = 15, # Int or iterable of edges
        normalization = :none,
        cycle = [:color => :patchcolor],
        color = theme(scene, :patchcolor),
        offset = 0.0,
        fillto = automatic,
        scale_to = nothing,

        bar_labels = nothing,
        flip_labels_at = Inf,
        label_color = theme(scene, :textcolor),
        over_background_color = automatic,
        over_bar_color = automatic,
        label_offset = 5,
        label_font = theme(scene, :font),
        label_size = 20,
        label_formatter = bar_label_formatter
    )
end

function Makie.plot!(plot::Hist)

    values = plot.values

    edges = lift(values, plot.bins) do vals, bins
        if bins isa Int
            mi, ma = float.(extrema(vals))
            if mi == ma
                return [mi - 0.5, ma + 0.5]
            end
            # hist is right-open, so to include the upper data point, make the last bin a tiny bit bigger
            ma = nextfloat(ma)
            return range(mi, ma, length = bins+1)
        else
            if !issorted(bins)
                error("Histogram bins are not sorted: $bins")
            end
            return bins
        end
    end

    points = lift(edges, plot.normalization, plot.scale_to) do edges, normalization, scale_to
        h = StatsBase.fit(StatsBase.Histogram, values[], edges)
        h_norm = StatsBase.normalize(h, mode = normalization)
        centers = edges[1:end-1] .+ (diff(edges) ./ 2)
        weights = h_norm.weights
        if !isnothing(scale_to)
            max = maximum(weights)
            weights .= weights ./ max .* scale_to
        end
        return Point2f.(centers, weights)
    end
    widths = lift(diff, edges)
    color = lift(plot.color) do color
        if color === :values
            return last.(points[])
        else
            return color
        end
    end

    bar_labels = map(plot.bar_labels) do x
        x === :values ? :y : x
    end
    # plot the values, not the observables, to be in control of updating
    bp = barplot!(plot, points[]; width = widths[], gap = 0, plot.attributes..., fillto=plot.fillto, offset=plot.offset, bar_labels=bar_labels, color=color)

    # update the barplot points without triggering, then trigger with `width`
    on(widths) do w
        bp[1].val = points[]
        bp.width = w
    end
    plot
end


text:
using SymPy

@vars x y a b c
ex=5x+4y+3a+2b+c

println(ex)
println(free_symbols((x,y,a,b,c)))
println(ex(PI,0,0,0,0))
println(ex(0,PI,0,0,0))
println(ex(0,0,PI,0,0))
println(ex(0,0,0,PI,0))
println(ex(0,0,0,0,PI))

max_stars_repo_name: terasakisatoshi/juliaExer

max_issues_repo_name: terasakisatoshi/juliaExer

max_forks_repo_name: terasakisatoshi/juliaExer


text:
struct RozemaAnisotropicMinimumDissipation{T} <: AbstractAnisotropicMinimumDissipation{T}
     C :: T
    Cb :: T
     ν :: T
     κ :: T
end

"""
    RozemaAnisotropicMinimumDissipation(T=Float64; C=0.33, ν=1.05e-6, κ=1.46e-7)

Returns a `RozemaAnisotropicMinimumDissipation` closure object of type `T` with

    * `C` : Poincaré constant
    * `ν` : 'molecular' background viscosity
    * `κ` : 'molecular' background diffusivity

See Rozema et al., " (2015)
"""
function RozemaAnisotropicMinimumDissipation(FT=Float64;
         C = 0.33,
        Cb = 0.0,
         ν = ν₀,
         κ = κ₀
    )
    return RozemaAnisotropicMinimumDissipation{FT}(C, Cb, ν, κ)
end

# Bindings
const RAMD = RozemaAnisotropicMinimumDissipation

"Return the filter width for Anisotropic Minimum Dissipation on a Regular Cartesian grid."
@inline Δx(i, j, k, grid::RegularCartesianGrid, ::RAMD) = grid.Δx
@inline Δy(i, j, k, grid::RegularCartesianGrid, ::RAMD) = grid.Δy
@inline Δz(i, j, k, grid::RegularCartesianGrid, ::RAMD) = grid.Δz

# We only have regular grids for now. When we have non-regular grids this will need to be changed.
const Δx_ccc = Δx
const Δy_ccc = Δy
const Δz_ccc = Δz
const Δx_ccf = Δx
const Δy_ccf = Δy
const Δz_ccf = Δz

function TurbulentDiffusivities(arch::AbstractArchitecture, grid::AbstractGrid, ::RAMD)
     νₑ = CellField(arch, grid)
    κTₑ = CellField(arch, grid)
    κSₑ = CellField(arch, grid)
    return (νₑ=νₑ, κₑ=(T=κTₑ, S=κSₑ))
end

@inline function ν_ccc(i, j, k, grid::AbstractGrid{FT}, closure::RAMD, c, buoyancy, U, Φ) where FT
    q = tr_∇u_ccc(i, j, k, grid, U.u, U.v, U.w)

    if q == 0
        νˢᶠˢ = zero(FT)
    else
        r = Δ²ₐ_uᵢₐ_uⱼₐ_Σᵢⱼ_ccc(i, j, k, grid, closure, U.u, U.v, U.w)
        ζ = Δ²ᵢ_wᵢ_bᵢ_ccc(i, j, k, grid, closure, buoyancy, U.w, Φ)
        νˢᶠˢ = -closure.C * (r - closure.Cb * ζ) / q
    end

    return max(zero(FT), νˢᶠˢ) + closure.ν
end

@inline function κ_ccc(i, j, k, grid::AbstractGrid{FT}, closure::RAMD, c, buoyancy, U, Φ) where FT
    σ = θᵢ²_ccc(i, j, k, grid, c) # Tracer variance

    if σ == 0
        κˢᶠˢ = zero(FT)
    else
        ϑ =  Δ²ⱼ_uᵢⱼ_cⱼ_cᵢ_ccc(i, j, k, grid, closure, U.u, U.v, U.w, c)
        κˢᶠˢ = - closure.C * ϑ / σ
    end

    return max(zero(FT), κˢᶠˢ) + closure.κ
end

#####
##### *** 30 terms ***
#####

#####
##### the heinous
#####

@inline function Δ²ₐ_uᵢₐ_uⱼₐ_Σᵢⱼ_ccc(i, j, k, grid, closure, u, v, w)
    Δx = Δx_ccc(i, j, k, grid, closure)
    Δy = Δy_ccc(i, j, k, grid, closure)
    Δz = Δz_ccc(i, j, k, grid, closure)

    ijk = (i, j, k, grid)
    uvw = (u, v, w)
    ijkuvw = (i, j, k, grid, u, v, w)

    Δx²_uᵢ₁_uⱼ₁_Σ₁ⱼ = Δx^2 * (
         Σ₁₁(ijkuvw...) * ∂x_u(ijk..., u)^2
      +  Σ₂₂(ijkuvw...) * ▶xy_cca(ijk..., ∂x_v², uvw...)
      +  Σ₃₃(ijkuvw...) * ▶xz_cac(ijk..., ∂x_w², uvw...)

      +  2 * ∂x_u(ijkuvw...) * ▶xy_cca(ijk..., ∂x_v_Σ₁₂, uvw...)
      +  2 * ∂x_u(ijkuvw...) * ▶xz_cac(ijk..., ∂x_w_Σ₁₃, uvw...)
      +  2 * ▶xy_cca(ijk..., ∂x_v, uvw...) * ▶xz_cac(ijk..., ∂x_w, uvw...) * ▶yz_acc(ijk..., Σ₂₃, uvw...)
    )

    Δy²_uᵢ₂_uⱼ₂_Σ₂ⱼ = Δy^2 * (
      + Σ₁₁(ijkuvw...) * ▶xy_cca(ijk..., ∂y_u², uvw...)
      + Σ₂₂(ijkuvw...) * ∂y_v(ijk..., v)^2
      + Σ₃₃(ijkuvw...) * ▶yz_acc(ijk..., ∂y_w², uvw...)

      +  2 * ∂y_v(ijkuvw...) * ▶xy_cca(ijk..., ∂y_u_Σ₁₂, uvw...)
      +  2 * ▶xy_cca(ijk..., ∂y_u, uvw...) * ▶yz_acc(ijk..., ∂y_w, uvw...) * ▶xz_cac(ijk..., Σ₁₃, uvw...)
      +  2 * ∂y_v(ijkuvw...) * ▶yz_acc(ijk..., ∂y_w_Σ₂₃, uvw...)
    )

    Δz²_uᵢ₃_uⱼ₃_Σ₃ⱼ = Δz^2 * (
      + Σ₁₁(ijkuvw...) * ▶xz_cac(ijk..., ∂z_u², uvw...)
      + Σ₂₂(ijkuvw...) * ▶yz_acc(ijk..., ∂z_v², uvw...)
      + Σ₃₃(ijkuvw...) * ∂z_w(ijk..., w)^2

      +  2 * ▶xz_cac(ijk..., ∂z_u, uvw...) * ▶yz_acc(ijk..., ∂z_v, uvw...) * ▶xy_cca(ijk..., Σ₁₂, uvw...)
      +  2 * ∂z_w(ijkuvw...) * ▶xz_cac(ijk..., ∂z_u_Σ₁₃, uvw...)
      +  2 * ∂z_w(ijkuvw...) * ▶yz_acc(ijk..., ∂z_v_Σ₂₃, uvw...)
    )

    return Δx²_uᵢ₁_uⱼ₁_Σ₁ⱼ + Δy²_uᵢ₂_uⱼ₂_Σ₂ⱼ + Δz²_uᵢ₃_uⱼ₃_Σ₃ⱼ
end

@inline function Δ²ₐ_uᵢₐ_uⱼₐ_Σᵢⱼ_ccf(i, j, k, grid, closure, u, v, w)
    Δx = Δx_ccf(i, j, k, grid, closure)
    Δy = Δy_ccf(i, j, k, grid, closure)
    Δz = Δz_ccf(i, j, k, grid, closure)

    ijk = (i, j, k, grid)
    uvw = (u, v, w)
    ijkuvw = (i, j, k, grid, u, v, w)

    Δx²_uᵢ₁_uⱼ₁_Σ₁ⱼ = Δx^2 * (
         ▶z_aaf(ijk..., Σ₁₁, uvw...) *   ▶z_aaf(ijk..., ∂x_u², uvw...)
      +  ▶z_aaf(ijk..., Σ₂₂, uvw...) * ▶xyz_ccf(ijk..., ∂x_v², uvw...)
      +  ▶z_aaf(ijk..., Σ₃₃, uvw...) *   ▶x_caa(ijk..., ∂x_w², uvw...)

      +  2 *   ▶z_aaf(ijk..., ∂x_u, uvw...) * ▶xyz_ccf(ijk..., ∂x_v_Σ₁₂, uvw...)
      +  2 *   ▶z_aaf(ijk..., ∂x_u, uvw...) *   ▶x_caa(ijk..., ∂x_w_Σ₁₃, uvw...)
      +  2 * ▶xyz_ccf(ijk..., ∂x_v, uvw...) *   ▶x_caa(ijk..., ∂x_w, uvw...) * ▶y_aca(ijk..., Σ₂₃, uvw...)
    )

    Δy²_uᵢ₂_uⱼ₂_Σ₂ⱼ = Δy^2 * (
      + ▶z_aaf(ijk..., Σ₁₁, uvw...) * ▶xyz_ccf(ijk..., ∂y_u², uvw...)
      + ▶z_aaf(ijk..., Σ₂₂, uvw...) *   ▶z_aaf(ijk..., ∂y_v², uvw...)
      + ▶z_aaf(ijk..., Σ₃₃, uvw...) *   ▶y_aca(ijk..., ∂y_w², uvw...)

      +  2 *  ▶z_aaf(ijk..., ∂y_v, uvw...) * ▶xyz_ccf(ijk..., ∂y_u_Σ₁₂, uvw...)
      +  2 * ▶xy_cca(ijk..., ∂y_u, uvw...) *   ▶y_aca(ijk..., ∂y_w, uvw...) * ▶x_caa(ijk..., Σ₁₃, uvw...)
      +  2 *  ▶z_aaf(ijk..., ∂y_v, uvw...) *   ▶y_aca(ijk..., ∂y_w_Σ₂₃, uvw...)
    )

    Δz²_uᵢ₃_uⱼ₃_Σ₃ⱼ = Δz^2 * (
      + ▶z_aaf(ijk..., Σ₁₁, uvw...) * ▶x_caa(ijk..., ∂z_u², uvw...)
      + ▶z_aaf(ijk..., Σ₂₂, uvw...) * ▶y_aca(ijk..., ∂z_v², uvw...)
      + ▶z_aaf(ijk..., Σ₃₃, uvw...) * ▶z_aaf(ijk..., ∂z_w², uvw...)

      +  2 * ▶x_caa(ijk..., ∂z_u, uvw...) * ▶y_aca(ijk..., ∂z_v, uvw...) * ▶xyz_ccf(ijk..., Σ₁₂, uvw...)
      +  2 * ▶z_aaf(ijk..., ∂z_w, uvw...) * ▶x_caa(ijk..., ∂z_u_Σ₁₃, uvw...)
      +  2 * ▶z_aaf(ijk..., ∂z_w, uvw...) * ▶y_aca(ijk..., ∂z_v_Σ₂₃, uvw...)
    )

    return Δx²_uᵢ₁_uⱼ₁_Σ₁ⱼ + Δy²_uᵢ₂_uⱼ₂_Σ₂ⱼ + Δz²_uᵢ₃_uⱼ₃_Σ₃ⱼ
end

#####
##### trace(∇u) = uᵢⱼ uᵢⱼ
#####

@inline function tr_∇u_ccc(i, j, k, grid, uvw...)
    ijk = (i, j, k, grid)

    return (
        # ccc
        ∂x_u²(ijk..., uvw...)
      + ∂y_v²(ijk..., uvw...)
      + ∂z_w²(ijk..., uvw...)

        # ffc
      + ▶xy_cca(ijk..., ∂x_v², uvw...)
      + ▶xy_cca(ijk..., ∂y_u², uvw...)

        # fcf
      + ▶xz_cac(ijk..., ∂x_w², uvw...)
      + ▶xz_cac(ijk..., ∂z_u², uvw...)

        # cff
      + ▶yz_acc(ijk..., ∂y_w², uvw...)
      + ▶yz_acc(ijk..., ∂z_v², uvw...)
    )
end

@inline function Δ²ᵢ_wᵢ_bᵢ_ccc(i, j, k, grid, closure, buoyancy, w, C)
    ijk = (i, j, k, grid)

    Δx = Δx_ccc(ijk..., closure)
    Δy = Δy_ccc(ijk..., closure)
    Δz = Δz_ccc(ijk..., closure)

    Δx²_wx_bx = Δx^2 * (▶xz_cac(ijk..., ∂x_faa, w)
                          * ▶x_caa(ijk..., ∂x_faa, buoyancy_perturbation, buoyancy, C))

    Δy²_wy_by = Δy^2 * (▶yz_acc(ijk..., ∂y_afa, w)
                          * ▶y_aca(ijk..., ∂y_afa, buoyancy_perturbation, buoyancy, C))

    Δz²_wz_bz = Δz^2 * (∂z_aac(ijk..., w)
                          * ▶z_aac(ijk..., ∂z_aaf, buoyancy_perturbation, buoyancy, C))

    return Δx²_wx_bx + Δy²_wy_by + Δz²_wz_bz
end

@inline function Δ²ⱼ_uᵢⱼ_cⱼ_cᵢ_ccc(i, j, k, grid, closure, u, v, w, c)
    ijk = (i, j, k, grid)

    Δx = Δx_ccc(ijk..., closure)
    Δy = Δy_ccc(ijk..., closure)
    Δz = Δz_ccc(ijk..., closure)

    Δx²_cx_ux = Δx^2 * (
                 ∂x_caa(ijk..., u) * ▶x_caa(ijk..., ∂x_c², c)
        + ▶xy_cca(ijk..., ∂x_v, v) * ▶x_caa(ijk..., ∂x_faa, c) * ▶y_aca(ijk..., ∂y_afa, c)
        + ▶xz_cac(ijk..., ∂x_w, w) * ▶x_caa(ijk..., ∂x_faa, c) * ▶z_aac(ijk..., ∂z_aaf, c)
    )

    Δy²_cy_uy = Δy^2 * (
          ▶xy_cca(ijk..., ∂y_u, u) * ▶y_aca(ijk..., ∂y_afa, c) * ▶x_caa(ijk..., ∂x_faa, c)
        +        ∂y_aca(ijk..., v) * ▶y_aca(ijk..., ∂y_c², c)
        + ▶xz_cac(ijk..., ∂y_w, w) * ▶y_aca(ijk..., ∂y_afa, c) * ▶z_aac(ijk..., ∂z_aaf, c)
    )

    Δz²_cz_uz = Δz^2 * (
          ▶xz_cac(ijk..., ∂z_u, u) * ▶z_aac(ijk..., ∂z_aaf, c) * ▶x_caa(ijk..., ∂x_faa, c)
        + ▶yz_acc(ijk..., ∂z_v, v) * ▶z_aac(ijk..., ∂z_aaf, c) * ▶y_aca(ijk..., ∂y_afa, c)
        +        ∂z_aac(ijk..., w) * ▶z_aac(ijk..., ∂z_c², c)
    )

    return Δx²_cx_ux + Δy²_cy_uy + Δz²_cz_uz
end

@inline function Δ²ⱼ_uᵢⱼ_cⱼ_cᵢ_ccf(i, j, k, grid, closure, u, v, w, c)
    ijk = (i, j, k, grid)

    Δx = Δx_ccf(ijk..., closure)
    Δy = Δy_ccf(ijk..., closure)
    Δz = Δz_ccf(ijk..., closure)

    Δx²_cx_ux = Δx^2 * (
            ▶z_aaf(ijk..., ∂x_caa, u) * ▶xz_caf(ijk..., ∂x_c², c)
        + ▶xyz_ccf(ijk..., ∂x_v, v)   * ▶xz_caf(ijk..., ∂x_faa, c) * ▶yz_acf(ijk..., ∂y_afa, c)
        +   ▶x_caa(ijk..., ∂x_w, w)   * ▶xz_caf(ijk..., ∂x_faa, c) *  ∂z_aaf(ijk..., c)
    )

    Δy²_cy_uy = Δy^2 * (
          ▶xyz_ccf(ijk..., ∂y_u, u) * ▶yz_acf(ijk..., ∂y_afa, c) * ▶xz_caf(ijk..., ∂x_faa, c)
        +   ∂y_aca(ijk..., v)       * ▶yz_acf(ijk..., ∂y_c², c)
        +   ▶x_caa(ijk..., ∂y_w, w) * ▶yz_acf(ijk..., ∂y_afa, c) * ∂z_aaf(ijk..., c)
    )

    Δz²_cz_uz = Δz^2 * (
          ▶x_caa(ijk..., ∂z_u, u)   * ∂z_aaf(ijk..., c) * ▶xz_caf(ijk..., ∂x_faa, c)
        + ▶y_aca(ijk..., ∂z_v, v)   * ∂z_aaf(ijk..., c) * ▶yz_acf(ijk..., ∂y_afa, c)
        + ▶z_aaf(ijk..., ∂z_aac, w) *  ∂z_c²(ijk..., c)
    )

    return Δx²_cx_ux + Δy²_cy_uy + Δz²_cz_uz
end

@inline θᵢ²_ccc(i, j, k, grid, c) = (
      ▶x_caa(i, j, k, grid, ∂x_c², c)
    + ▶y_aca(i, j, k, grid, ∂y_c², c)
    + ▶z_aac(i, j, k, grid, ∂z_c², c)
)

@inline θᵢ²_ccf(i, j, k, grid, c) = (
      ▶xz_caf(i, j, k, grid, ∂x_c², c)
    + ▶yz_acf(i, j, k, grid, ∂y_c², c)
    + ∂z_c²(i, j, k, grid, c)
)

"""
    ∇_κ_∇T(i, j, k, grid, T, closure, diffusivities)

Return the diffusive flux divergence `∇ ⋅ (κ ∇ c)` for the turbulence
`closure`, where `c` is an array of scalar data located at cell centers.
"""
@inline ∇_κ_∇T(i, j, k, grid, T, closure::RAMD, diffusivities) = (
      ∂x_caa(i, j, k, grid, κ_∂x_c, T, diffusivities.κₑ.T, closure)
    + ∂y_aca(i, j, k, grid, κ_∂y_c, T, diffusivities.κₑ.T, closure)
    + ∂z_aac(i, j, k, grid, κ_∂z_c, T, diffusivities.κₑ.T, closure)
)

"""
    ∇_κ_∇S(i, j, k, grid, S, closure, diffusivities)

Return the diffusive flux divergence `∇ ⋅ (κ ∇ S)` for the turbulence
`closure`, where `c` is an array of scalar data located at cell centers.
"""
@inline ∇_κ_∇S(i, j, k, grid, S, closure::RAMD, diffusivities) = (
      ∂x_caa(i, j, k, grid, κ_∂x_c, S, diffusivities.κₑ.S, closure)
    + ∂y_aca(i, j, k, grid, κ_∂y_c, S, diffusivities.κₑ.S, closure)
    + ∂z_aac(i, j, k, grid, κ_∂z_c, S, diffusivities.κₑ.S, closure)
)

# This function assumes rigid top and bottom boundary conditions
function calc_diffusivities!(K, grid, closure::RAMD, buoyancy, U, Φ)
    @loop for k in (1:grid.Nz; (blockIdx().z - 1) * blockDim().z + threadIdx().z)
        @loop for j in (1:grid.Ny; (blockIdx().y - 1) * blockDim().y + threadIdx().y)
            @loop for i in (1:grid.Nx; (blockIdx().x - 1) * blockDim().x + threadIdx().x)
                @inbounds K.νₑ[i, j, k]   = ν_ccc(i, j, k, grid, closure, nothing, buoyancy, U, Φ)
                @inbounds K.κₑ.T[i, j, k] = κ_ccc(i, j, k, grid, closure, Φ.T,     buoyancy, U, Φ)
                @inbounds K.κₑ.S[i, j, k] = κ_ccc(i, j, k, grid, closure, Φ.S,     buoyancy, U, Φ)
            end
        end
    end
    return nothing
end


text:
module TestSets

using BangBang: union!!
using MicroCollections
using Test

@testset "empty" begin
    @test EmptySet() === emptyshim(Set)
    @test eltype(EmptySet()) === Union{}
    @test EmptySet(Set{Int}())::AbstractSet{Int} == Set{Int}()
end

@testset "singleton" begin
    @test SingletonSet([0]) === singletonshim(Set, 0)
    @test SingletonSet([0])::AbstractSet{Int} == Set([0])
    @test SingletonSet([nothing])::AbstractSet{Nothing} == Set([nothing])
    @test SingletonSet(Set([0]))::AbstractSet{Int} == Set([0])
    @test SingletonSet((0,))::AbstractSet{Int} == Set([0])
    @test union!!(SingletonSet([0]), [0.5])::Set{Float64} == Set([0.0, 0.5])
    @test union(SingletonSet([0]), [0.5])::Set{Float64} == Set([0.0, 0.5])
end

end  # module


text:
# C- entrypoints for all lowlevel callback functions

F_INIT = 1
function Cinit(userdata::Ptr{Nothing}, conn::Ptr{Nothing})
    try
        log("Cinit called")
        fcallback(F_INIT, userdata, CStruct{FuseConnInfo}(conn))
    finally
    end
end
F_DESTROY = 2
function Cdestroy(userdata::Ptr{Nothing})
    try
        fcallback(F_DESTROY, userdata)
    finally
    end
end
F_LOOKUP = 3
function Clookup(req::FuseReq, parent::FuseIno, name::Cstring)
    error = Base.UV_ENOTSUP
    name = unsafe_string(name)
    try
        log("Clookup called req=$req parent=$parent name=$name")
        entry = CStructGuided(FuseEntryParam)
        error = fcallback(F_LOOKUP, req, parent, name, entry)
        log("back from lookup entry=$(error == 0 ? entry : "")")
        error == 0 && fuse_reply_entry(req, entry)
        error == 0 && log("after fuse_reply_entry")
    finally
        error != 0 && fuse_reply_err(req, -error)
        error != 0 && log("fuse_reply_err(req, $(-error))")
    end
    nothing
end
F_FORGET = 4
function Cforget(req::FuseReq, ino::FuseIno, lookup::UInt64)
    try
        fcallback(F_FORGET, req, ino, lookup)
    finally
    end
end
F_GETATTR = 5
function Cgetattr(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo})
    error = Base.UV_ENOTSUP
    try
        attr = CStructGuided(Cstat)
        attr_timeout = Ref(10.0)
        error = fcallback(F_GETATTR, req, ino, CStruct(fi), attr, attr_timeout)
        error == 0 && fuse_reply_attr(req, attr, attr_timeout[])
    catch ex
        log("getattr ex")
        rethrow()
    finally
        error != 0 && fuse_reply_err(req, -error)
    end
    nothing
end
F_SETATTR = 6
function Csetattr(req::FuseReq, ino::FuseIno, attr::Cstat, to_set::Cint, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_SETATTR, req, ino, attr, to_set, CStruct{FuseFileInfo}(fi))
        # fuse_reply_attr(req, attr, attr_timeout)
    finally
    end
end
F_READLINK = 7
function Creadlink(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_READLINK, req, ino)
    finally
    end
end
F_MKNOD = 8
function Cmknod(req::FuseReq, parent::FuseIno, name::String, mode::FuseMode, rdev::FuseDev)
    try
        fcallback(F_MKNOD, req, parent, name, mode, rdev)
    finally
    end
end
F_MKDIR = 9
function Cmkdir(req::FuseReq, parent::FuseIno, name::String, mode::FuseMode)
    try
        fcallback(F_MKDIR, req, parent, name, mode)
    finally
    end
end
F_UNLINK = 10
function Cunlink(req::FuseReq, parent::FuseIno, name::String)
    try
        fcallback(F_UNLINK, req, parent, name)
    finally
    end
end
F_RMDIR = 11
function Crmdir(req::FuseReq, parent::FuseIno, name::String)
    try
        fcallback(F_RMDIR, req, parent, name)
    finally
    end
end
F_SYMLINK = 12
function Csymlink(req::FuseReq, link::String, parent::FuseIno, name::String)
    try
        fcallback(F_SYMLINK, req, link, parent, name)
    finally
    end
end
F_RENAME = 13
function Crename(req::FuseReq, parent::FuseIno, name::String, newparent::FuseIno, newname::String, flags::Cuint)
    try
        fcallback(F_RENAME, req, parent, name, newparent, newname, flags)
    finally
    end
end
F_LINK = 14
function Clink(req::FuseReq, ino::FuseIno, newparent::FuseIno, newname::String)
    try
        fcallback(F_LINK, req, ino, newparent, newname)
    finally
    end
end
F_OPEN = 15
function Copen(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_OPEN, req, ino, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_READ = 16
function Cread(req::FuseReq, ino::FuseIno, size::Csize_t, off::Csize_t, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_READ, req, ino, size, off, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_WRITE = 17
function Cwrite(req::FuseReq, ino::FuseIno, buf::Vector{UInt8}, size::Csize_t, off::Csize_t, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_WRITE, req, ino, buf, size, off, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_FLUSH = 18
function Cflush(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_FLUSH, req, ino, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_RELEASE = 19
function Crelease(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_RELEASE, req, ino, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_FSYNC = 20
function Cfsync(req::FuseReq, ino::FuseIno, datasync::Cint, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_FSYNC, req, ino, datasync, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_OPENDIR = 21
function Copendir(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_OPENDIR, req, ino)
    finally
    end
end
F_READDIR = 22
function Creaddir(req::FuseReq, ino::FuseIno, size::Csize_t, off::Csize_t, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_READDIR, req, ino, size, off, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_RELEASEDIR = 23
function Creleasedir(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_RELEASEDIR, req, ino, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_FSYNCDIR = 24
function Cfsyncdir(req::FuseReq, ino::FuseIno, datasync::Cint, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_FSYNCDIR, req, ino, datasync, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_STATFS = 25
function Cstatfs(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_STATFS, req, ino)
    finally
    end
end
F_SETXATTR = 26
function Csetxattr(req::FuseReq, ino::FuseIno, name::String, value::String, size::Csize_t, flags::Cint)
    try
        fcallback(F_SETXATTR, req, ino, name, value, size, flags)
    finally
    end
end
F_GETXATTR = 27
function Cgetxattr(req::FuseReq, ino::FuseIno, name::String, size::Csize_t)
    try
        fcallback(F_GETXATTR, req, ino, name, size)
    finally
    end
end
F_LISTXATTR = 28
function Clistxattr(req::FuseReq, ino::FuseIno, size::Csize_t)
    try
        fcallback(F_LISTXATTR, req, ino, size)
    finally
    end
end
F_REMOVEXATTR = 29
function Cremovexattr(req::FuseReq, ino::FuseIno, name::String)
    try
        fcallback(F_REMOVEXATTR, req, ino, name)
    finally
    end
end
F_ACCESS = 30
function Caccess(req::FuseReq, ino::FuseIno, mask::Cint)
    try
        fcallback(F_ACCESS, req, ino, mask)
    finally
    end
end
F_CREATE = 31
function Ccreate(req::FuseReq, parent::FuseIno, name::String, mode::FuseMode, fi::Ptr{FuseFileInfo})
    try
        fcallback(F_CREATE, req, parent, name, mode, CStruct{FuseFileInfo}(fi))
    finally
    end
end
F_GETLK = 32
function Cgetlk(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo}, lock::Ptr{Cflock})
    try
        fcallback(F_GETLK, req, ino, CStruct(fi), CStruct(lock))
    finally
    end
end
F_SETLK = 33
function Csetlk(req::FuseReq, ino::FuseIno, fi::Ptr{FuseFileInfo}, lock::Ptr{Cflock}, sleep::Cint)
    try
        fcallback(F_SETLK, req, ino, CStruct(fi), CStruct(lock), sleep)
    finally
    end
end
F_BMAP = 34
function Cbmap(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_BMAP, req, ino)
    finally
    end
end
F_IOCTL = 35
function Cioctl(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_IOCTL, req, ino)
    finally
    end
end
F_POLL = 36
function Cpoll(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_POLL, req, ino)
    finally
    end
end
F_WRITE_BUF = 37
function Cwrite_buf(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_WRITE_BUF, req, ino)
    finally
    end
end
F_RETRIEVE_REPLY = 38
function Cretrieve_reply(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_RETRIEVE_REPLY, req, ino)
    finally
    end
end
F_FORGET_MULTI = 39
function Cforget_multi(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_FORGET_MULTI, req, ino)
    finally
    end
end
F_FLOCK = 40
function Cflock(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_FLOCK, req, ino)
    finally
    end
end
F_FALLOCATE = 41
function Cfallocate(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_FALLOCATE, req, ino)
    finally
    end
end
F_READDIRPLUS = 42
function Creaddirplus(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_READDIRPLUS, req, ino)
    finally
    end
end
F_COPY_FILE_RANGE = 43
function Ccopy_file_range(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_COPY_FILE_RANGE, req, ino)
    finally
    end
end
F_LSEEK = 44
function Clseek(req::FuseReq, ino::FuseIno)
    try
        fcallback(F_LSEEK, req, ino)
    finally
    end
end

# to become const
ALL_FLO() = [
    (@cfunction Cinit Cvoid (Ptr{Nothing}, Ptr{Nothing})),
    (@cfunction Cdestroy Cvoid (Ptr{Nothing},)),
    (@cfunction Clookup  Cvoid (FuseReq, FuseIno, Cstring)),
    (@cfunction Cforget Cvoid (FuseReq, FuseIno, Culong)),
    (@cfunction Cgetattr Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo})),
    (@cfunction Csetattr Cvoid (FuseReq, FuseIno, Ptr{Cstat}, Cint, Ptr{FuseFileInfo})),
    (@cfunction Creadlink Cvoid (FuseReq, FuseIno)),
    (@cfunction Cmknod Cvoid (FuseReq, FuseIno, Cstring, FuseMode, FuseDev)),
    (@cfunction Cmkdir Cvoid (FuseReq, FuseIno, Cstring, FuseMode)),
    (@cfunction Cunlink Cvoid (FuseReq, FuseIno, Cstring)),
    (@cfunction Crmdir Cvoid (FuseReq, FuseIno, Cstring)),
    (@cfunction Csymlink Cvoid (FuseReq, Cstring, FuseIno, Cstring)),
    (@cfunction Crename Cvoid (FuseReq, FuseIno, Cstring, FuseIno, Cstring)),
    (@cfunction Clink Cvoid (FuseReq, FuseIno, FuseIno, Cstring)),
    (@cfunction Copen Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo})),
    (@cfunction Cread Cvoid (FuseReq, FuseIno, Culong, Culong)),
    (@cfunction Cwrite Cvoid (FuseReq, FuseIno, Cstring, Culong, Culong, Ptr{FuseFileInfo})),
    (@cfunction Cflush Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo})),
    (@cfunction Crelease Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo})),
    (@cfunction Cfsync Cvoid (FuseReq, FuseIno, Cint, Ptr{FuseFileInfo})),
    (@cfunction Copendir Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo})),
    (@cfunction Creaddir Cvoid (FuseReq, FuseIno, Culong, Culong, Ptr{FuseFileInfo})),
    (@cfunction Creleasedir Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo})),
    (@cfunction Cfsyncdir Cvoid (FuseReq, FuseIno, Cint, Ptr{FuseFileInfo})),
    (@cfunction Cstatfs Cvoid (FuseReq, FuseIno)),
    (@cfunction Csetxattr Cvoid (FuseReq, FuseIno, Cstring, Cstring, Culong, Cint)),
    (@cfunction Cgetxattr Cvoid (FuseReq, FuseIno, Cstring, Culong)),
    (@cfunction Clistxattr Cvoid (FuseReq, FuseIno, Culong)),
    (@cfunction Cremovexattr Cvoid (FuseReq, FuseIno, Cstring)),
    (@cfunction Caccess Cvoid (FuseReq, FuseIno, Cint)),
    (@cfunction Ccreate Cvoid (FuseReq, FuseIno, Cstring, FuseMode, Ptr{FuseFileInfo})),
    (@cfunction Cgetlk Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo}, Ptr{Cflock})),
    (@cfunction Csetlk Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo}, Ptr{Cflock}, Cint)),
    (@cfunction Cbmap Cvoid (FuseReq, FuseIno, Culong, Culong)),
    (@cfunction Cioctl Cvoid (FuseReq, FuseIno, Cuint, Ptr{Cvoid}, Ptr{FuseFileInfo}, Cuint, Ptr{Cvoid}, Culong, Culong)),
    (@cfunction Cpoll Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo}, Ptr{FusePollHandle})),
    (@cfunction Cwrite_buf Cvoid (FuseReq, FuseIno, Ptr{FuseBufvec}, Culong, Ptr{FuseFileInfo})),
    (@cfunction Cretrieve_reply Cvoid (FuseReq, Ptr{Cvoid}, FuseIno, Culong, Ptr{FuseBufvec})),
    (@cfunction Cforget_multi Cvoid (FuseReq, Culong, Ptr{FuseForgetData})),
    (@cfunction Cflock Cvoid (FuseReq, FuseIno, Ptr{FuseFileInfo})),
    (@cfunction Cfallocate Cvoid (FuseReq, FuseIno, Cint, Culong, Culong, Ptr{FuseFileInfo})),
    (@cfunction Creaddirplus Cvoid (FuseReq, FuseIno, Culong, Culong,Ptr{FuseFileInfo})),
    (@cfunction Ccopy_file_range Cvoid (FuseReq, FuseIno, Culong, Ptr{FuseFileInfo}, FuseIno, Culong, Ptr{FuseFileInfo}, Culong, Cint)),
    (@cfunction Clseek Cvoid (FuseReq, FuseIno, Culong, Cint, Ptr{FuseFileInfo}))
]

# reply functions to be called inside callback functions

function fuse_reply_attr(req::FuseReq, attr::CStructAccess{Cstat}, attr_timeout::Real)
    ccall((:fuse_reply_attr, :libfuse3), Cint, (FuseReq, Ptr{Cstat}, Cdouble), req, attr, attr_timeout)
end
function fuse_reply_bmap(req::FuseReq, idx::Integer)
    ccall((:fuse_reply_bmap, :libfuse3), Cint, (FuseReq, UInt64), req, idx)
end
function fuse_reply_buf(req::FuseReq, buf::Vector{UInt8}, size::Integer)
    ccall((:fuse_reply_entry, :libfuse3), Cint, (FuseReq, Ptr{UInt8}, Csize_t), req, buf, size)
end
function fuse_reply_create(req::FuseReq, e::CStructAccess{FuseEntryParam}, fi::CStruct{FuseFileInfo})
    ccall((:fuse_reply_create, :libfuse3), Cint, (FuseReq, Ptr{FuseEntryParam}, Ptr{FuseFileInfo}), req, e, CStruct{FuseFileInfo}(fi))
end
function fuse_reply_data(req::FuseReq, bufv::CStructAccess{FuseBufvec}, flags::FuseBufCopyFlags)
    ccall((:fuse_reply_data, :libfuse3), Cint, (FuseReq, Ptr{FuseBufvec}, Cint), req, bufv, flags)
end
function fuse_reply_entry(req::FuseReq, entry::CStructAccess{FuseEntryParam})
    ccall((:fuse_reply_entry, :libfuse3), Cint, (FuseReq, Ptr{FuseEntryParam}), req, entry)
end
function fuse_reply_err(req::FuseReq, err::Integer)
    ccall((:fuse_reply_err, :libfuse3), Cint, (FuseReq, Cint), req, err)
end
function fuse_reply_ioctl(req::FuseReq, result::Integer, buf::CStruct, size::Integer)
    ccall((:fuse_reply_ioctl, :libfuse3), Cint, (FuseReq, Cint, Ptr{Nothing}, Csize_t), req, result, buf, size)
end
function fuse_reply_ioctl_iov(req::FuseReq, result::Integer, iov::CStructAccess{Ciovec}, count::Integer)
    ccall((:fuse_reply_ioctl_iov, :libfuse3), Cint, (FuseReq, Cint, Ptr{Ciovec}, Cint), req, result, iov, count)
end
function fuse_reply_ioctl_retry(req::FuseReq, in_iov::CStructAccess{Ciovec}, in_count::Integer, out_iov::CStruct{Ciovec}, out_count::Integer)
    ccall((:fuse_reply_ioctl_retry, :libfuse3), Cint, (FuseReq, Ptr{Ciovec}, Csize_t, Ptr{Ciovec}, Csize_t), req, in_iov, in_count, out_iov, out_count)
end
function fuse_reply_iov(req::FuseReq, iov::CStructAccess{Ciovec}, count::Integer)
    ccall((:fuse_reply_iov, :libfuse3), Cint, (FuseReq, Ptr{Ciovec}, Cint), req, iov, count)
end
function fuse_reply_lock(req::FuseReq, lock::CStructAccess{Cflock})
    ccall((:fuse_reply_lock, :libfuse3), Cint, (FuseReq, Ptr{Cflock}), req, lock)
end
function fuse_reply_lseek(req::FuseReq, off::Integer)
    ccall((:fuse_reply_lseek, :libfuse3), Cint, (FuseReq, Coff_t), req, off)
end
function fuse_reply_none(req::FuseReq)
    ccall((:fuse_reply_none, :libfuse3), Cint, (FuseReq, ), req)
end
function fuse_reply_open(req::FuseReq, fi::CStructAccess{FuseFileInfo})
    ccall((:fuse_reply_open, :libfuse3), Cint, (FuseReq, Ptr{FuseFileInfo}), req, CStruct{FuseFileInfo}(fi))
end
function fuse_reply_poll(req::FuseReq, revents::Integer )
    ccall((:fuse_reply_entry, :libfuse3), Cint, (FuseReq, Cuint), req, revents)
end
function fuse_reply_readlink(req::FuseReq, link::String)
    ccall((:fuse_reply_readlink, :libfuse3), Cint, (FuseReq, Cstring), req, link)
end
function fuse_reply_statfs(req::FuseReq, stbuf::CStructAccess{Cstatvfs})
    ccall((:fuse_reply_statfs, :libfuse3), Cint, (FuseReq, Ptr{Cstatvfs}), req, stbuf)
end
function fuse_reply_write(req::FuseReq, count::Integer)
    ccall((:fuse_reply_write, :libfuse3), Cint, (FuseReq, Csize_t), req, count)
end
function fuse_reply_xattr(req::FuseReq, count::Integer)
    ccall((:fuse_reply_xattr, :libfuse3), Cint, (FuseReq, Csize_t), req, count)
end

# accessors for req
function fuse_req_ctx(req::FuseReq)
    CStruct{FuseCtx}(ccall((:fuse_req_ctx, :libfuse3), Ptr{FuseCtx}, (FuseReq,), req))
end
function fuse_req_getgroups(req::FuseReq, list::Vector{Cgid_t})
    ccall((:fuse_req_getgroups, :libfuse3), Cint, (FuseReq, Cint, Ptr{Cgid_t}), req, length(list), pointer_from_vector(list))
end
function fuse_req_interrupt_func(req::FuseReq, func::Ptr{Nothing}, data::Any)
    ccall((:fuse_req_ctx, :libfuse3), Cvoid, (FuseReq, Ptr{Nothing}, Ptr{Nothing}), req, func, data)
end
function fuse_req_interrupted(req::FuseReq)
    ccall((:fuse_req_interrupted, :libfuse3), Cint, (FuseReq,), req)
end
function fuse_req_userdata(req::FuseReq)
    ccall((:fuse_req_userdata, :libfuse3), Ptr{Nothing}, (FuseReq,), req)
end
function fuse_req_userdata(req::FuseReq, ::Type{T}) where T
    unsafe_pointer_to_objref(ccall((:fuse_req_userdata, :libfuse3), Ptr{T}, (FuseReq,), req))
end


text:
__precompile__(true)
module WeakRefStrings

export WeakRefString

"""
A custom "weakref" string type that only points to external string data.
Allows for the creation of a "string" instance without copying data,
which allows for more efficient string parsing/movement in certain data processing tasks.

**Please note that no original reference is kept to the parent string/memory, so `WeakRefString` becomes unsafe
once the parent object goes out of scope (i.e. loses a reference to it)**

Internally, a `WeakRefString{T}` holds:

  * `ptr::Ptr{T}`: a pointer to the string data (code unit size is parameterized on `T`)
  * `len::Int`: the number of code units in the string data
  * `ind::Int`: a field that can be used to store an integer, like an index into an array; this can be helpful
                in certain cases when the underlying source may need to move around (which would invalidate
                the WeakRefString's `ptr` field), a new WeakRefString can created using the same offset into
                the parent data as the old one.
"""
immutable WeakRefString{T} <: AbstractString
    ptr::Ptr{T}
    len::Int # of code units
    ind::Int # used to keep track of a string data index
end

WeakRefString{T}(ptr::Ptr{T}, len) = WeakRefString(ptr, Int(len), 0)

const NULLSTRING = WeakRefString(Ptr{UInt8}(0), 0)
const NULLSTRING16 = WeakRefString(Ptr{UInt16}(0), 0)
const NULLSTRING32 = WeakRefString(Ptr{UInt32}(0), 0)
Base.endof(x::WeakRefString) = x.len
Base.length(x::WeakRefString) = x.len
Base.next(x::WeakRefString, i::Int) = (Char(unsafe_load(x.ptr, i)), i + 1)

import Base: ==
function ==(x::WeakRefString{UInt8}, y::WeakRefString{UInt8})
    x.len == y.len && (x.ptr == y.ptr || ccall(:memcmp, Cint, (Ptr{UInt8}, Ptr{UInt8}, Csize_t),
                                           x.ptr, y.ptr, x.len) == 0)
end

function Base.hash(s::WeakRefString{UInt8}, h::UInt)
    h += Base.memhash_seed
    ccall(Base.memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s.ptr, s.len, h % UInt32) + h
end

Base.show{T}(io::IO, ::Type{WeakRefString{T}}) = print(io, "WeakRefString{$T}")
function Base.show{T}(io::IO, x::WeakRefString{T})
    print(io, '"')
    for c in x
        print(io, c)
    end
    print(io, '"')
    return
end

chompnull{T}(x::WeakRefString{T}) = unsafe_load(x.ptr, x.len) == T(0) ? x.len - 1 : x.len

Base.string(x::WeakRefString{UInt16}) = x == NULLSTRING16 ? "" : String(transcode(UInt8, unsafe_wrap(Array, x.ptr, chompnull(x))))
Base.string(x::WeakRefString{UInt32}) = x == NULLSTRING32 ? "" : String(transcode(UInt8, unsafe_wrap(Array, x.ptr, chompnull(x))))

Base.convert(::Type{WeakRefString{UInt8}}, x::String) = WeakRefString(pointer(x.data), length(x))
Base.convert(::Type{String}, x::WeakRefString) = convert(String, string(x))
Base.string(x::WeakRefString) = x == NULLSTRING ? "" : unsafe_string(x.ptr, x.len)
Base.String(x::WeakRefString) = string(x)

end # module


text:
struct Move
    source :: UInt64
    target :: UInt64
    piece_type :: String
    capture_type :: String
    promotion_type :: String
    enpassant_square :: UInt64
    castling_type :: String
end

function validate_move(board::Bitboard, move::Move, color::String="white")
    board = move_piece(board, move, color)
    board = update_attacked(board)
    in_check = king_in_check(board, color)
    board = unmove_piece(board, move, color)
    # board = update_attacked(board)
    return ~in_check
end

# function validate_move(board::Bitboard, move::Move, color::String="white")
#     newb = deepcopy(board)
#     newb = move_piece(newb, move, color)
#     newb = update_attacked(newb)
#     in_check = king_in_check(newb, color)
#     # board = unmove_piece(board, move, color)
#     # board = update_attacked(board)
#     return ~in_check
# end

function get_all_moves(board::Bitboard, color::String="white")
    valid_moves = Array{Move,1}()
    valid_moves = get_non_sliding_pieces_list(valid_moves, board,
        "king", color)
    valid_moves = get_non_sliding_pieces_list(valid_moves, board,
        "night", color)
    valid_moves = get_pawns_list(valid_moves, board, color)
    valid_moves = get_sliding_pieces_list(valid_moves, board, "queen", color)
    valid_moves = get_sliding_pieces_list(valid_moves, board, "rook", color)
    return get_sliding_pieces_list(valid_moves, board, "bishop", color)
end

function print_moves(moves)
    for move in moves
        s = UINT2PGN[move.source]
        t = UINT2PGN[move.target]
        println(move.piece_type, " ", s, " ", t)
    end
end

function get_all_valid_moves(board::Bitboard, color::String="white")
    moves = get_all_moves(board, color)
    valid_moves = Array{Move,1}()
    for move in moves
        if validate_move(board, move, color)
            push!(valid_moves, move)
        end
    end
    return valid_moves
end

function get_non_sliding_pieces_list(piece_moves::Array{Move,1},
    board::Bitboard, piece_type::String, color::String="white")

    if color == "white"
        same = board.white
        other = board.black
        if piece_type == "king"
            pieces = board.K
            home_square = WHITE_KING_HOME
        else
            pieces = board.N
        end
        opponent_color = "black"
    else
        same = board.black
        other = board.white
        if piece_type == "king"
            pieces = board.k
            home_square = BLACK_KING_HOME
        else
            pieces = board.n
        end
        opponent_color = "white"
    end

    if piece_type == "king"
        piece_dict = KING_MOVES

        if pieces == home_square
            if color == "white"
                if ~board.white_king_moved
                    if board.white_can_castle_kingside == true
                        if board.free & F1 != EMPTY &&
                            board.free & G1 != EMPTY &&
                            H1 in board.R
                            if ~square_in_check(board, E1, color) &&
                                ~square_in_check(board, F1, color)
                                push!(piece_moves, Move(pieces, G1,
                                    piece_type, "none", "none", EMPTY, "K"))
                            end
                        end
                    end
                    if board.white_can_castle_queenside == true
                        if board.free & D1 != EMPTY &&
                            board.free & C1 != EMPTY &&
                            board.free & B1 != EMPTY &&
                            A1 in board.R
                            if ~square_in_check(board, E1, color) &&
                                ~square_in_check(board, D1, color)
                                push!(piece_moves, Move(pieces, C1,
                                    piece_type, "none", "none", EMPTY, "Q"))
                            end
                        end
                    end
                end
            else
                if ~board.black_king_moved
                    if board.black_can_castle_kingside == true
                        if board.free & F8 != EMPTY &&
                            board.free & G8 != EMPTY &&
                            H8 in board.r
                            if ~square_in_check(board, E8, color) &&
                                ~square_in_check(board, F8, color)
                                push!(piece_moves, Move(pieces, G8,
                                    piece_type, "none", "none", EMPTY, "k"))
                            end
                        end
                    end
                    if board.black_can_castle_queenside == true
                        if board.free & D8 != EMPTY &&
                            board.free & C8 != EMPTY &&
                            board.free & B8 != EMPTY &&
                            A8 in board.r
                            if ~square_in_check(board, E8, color) &&
                                ~square_in_check(board, D8, color)
                                push!(piece_moves, Move(pieces, C8,
                                    piece_type, "none", "none", EMPTY, "q"))
                            end
                        end
                    end
                end
            end
        end
    else
        piece_dict = NIGHT_MOVES
    end

    for piece in pieces
        for move in piece_dict[piece]
            if move & same == EMPTY && move & other == EMPTY
                push!(piece_moves, Move(piece, move,
                    piece_type, "none", "none", EMPTY, "-"))
            elseif move & same == EMPTY && move & other != EMPTY
                taken_piece = find_piece_type(board, move, opponent_color)
                push!(piece_moves, Move(piece, move,
                    piece_type, taken_piece, "none", EMPTY, "-"))
            end
        end
    end
    return piece_moves
end

function find_piece_type(board::Bitboard, ui::UInt64, color::String)
    if color == "white"
        if ui == board.K
            return "king"
        elseif ui in board.Q
            return "queen"
        elseif ui in board.R
            return "rook"
        elseif ui in board.P
            return "pawn"
        elseif ui in board.B
            return "bishop"
        elseif ui in board.N
            return "night"
        else
            return "none"
        end
    else
        if ui == board.k
            return "king"
        elseif ui in board.q
            return "queen"
        elseif ui in board.r
            return "rook"
        elseif ui in board.p
            return "pawn"
        elseif ui in board.b
            return "bishop"
        elseif ui in board.n
            return "night"
        else
            return "none"
        end
    end
end

function get_sliding_pieces_list(piece_moves::Array{Move,1}, board::Bitboard,
    piece_type::String, color::String="white")

    if color == "white"
        same = board.white
        other = board.black
        if piece_type == "queen"
            pieces = board.Q
        elseif piece_type == "rook"
            pieces = board.R
        elseif piece_type == "bishop"
            pieces = board.B
        end
        opponent_color = "black"
    else
        same = board.black
        other = board.white
        if piece_type == "queen"
            pieces = board.q
        elseif piece_type == "rook"
            pieces = board.r
        elseif piece_type == "bishop"
            pieces = board.b
        end
        opponent_color = "white"
    end

    if piece_type == "queen"
        attack_fun = star_attack
    elseif piece_type == "rook"
        attack_fun = orthogonal_attack
    elseif piece_type == "bishop"
        attack_fun = cross_attack
    end

    moves = Array{UInt64,1}()
    edges = Array{UInt64,1}()
    for piece in pieces
        moves, edges = attack_fun(moves, edges, board.taken, piece)
        while length(moves) > 0
            push!(piece_moves, Move(piece, pop!(moves), piece_type,
                "none", "none", EMPTY, "-"))
        end
        while length(edges) > 0
            edge = pop!(edges)
            if edge & same == EMPTY && edge & other == EMPTY
                push!(piece_moves, Move(piece, edge, piece_type,
                                        "none", "none", EMPTY, "-"))
            elseif edge & same == EMPTY && edge & other != EMPTY
                taken_piece = find_piece_type(board, edge, opponent_color)
                push!(piece_moves, Move(piece, edge, piece_type,
                                        taken_piece, "none", EMPTY, "-"))
            end
        end
    end
    return piece_moves
end

function remove_from_square(b::UInt64, s::UInt64)
    return xor(b, s)
end

function remove_from_square(bs::Array{UInt64,1}, s::UInt64)
    filter!(e -> e != s, bs)
    return bs
end

function add_to_square(b::UInt64, s::UInt64)
    return b |= s
end

function add_to_square(bs::Array{UInt64,1}, s::UInt64)
    push!(bs, s)
    return bs
end

function update_from_to_squares(b::UInt64, s::UInt64, t::UInt64)
    b = remove_from_square(b, s)
    b = add_to_square(b, t)
    return b
end

function update_from_to_squares(bs::Array{UInt64,1}, s::UInt64, t::UInt64)
    bs = remove_from_square(bs, s)
    bs = add_to_square(bs, t)
    return bs
end

function update_castling_rights(board::Bitboard)
    if board.K == E1
        board.white_king_moved = false
    end
    if A1 in board.R
        board.white_can_castle_queenside = true
    end
    if H1 in board.R
        board.white_can_castle_kingside = true
    end
    if board.k == E8
        board.black_king_moved = false
    end
    if A8 in board.r
        board.black_can_castle_queenside = true
    end
    if H8 in board.r
        board.black_can_castle_kingside = true
    end
    for move in board.game
        if move == "whitekinge1"
            board.white_king_moved = true
        elseif move == "whiterooka1"
            board.white_can_castle_queenside = false
        elseif move == "whiterookh1"
            board.white_can_castle_kingside = false
        elseif move == "blackkinge8"
            board.black_king_moved = true
        elseif move == "blackrooka8"
            board.black_can_castle_queenside = false
        elseif move == "blackrookh8"
            board.black_can_castle_kingside = false
        end
    end
    return board
end

function update_attacked(board::Bitboard)
    board.A[1] = EMPTY
    for p in board.P
        for a in WHITE_PAWN_ATTACK[p]
            board.A[1] |= a
        end
    end
    board.A[2] = EMPTY
    for n in board.N
        for a in NIGHT_MOVES[n]
            board.A[2] |= a
        end
    end
    board.A[3] = EMPTY
    for q in board.Q
        board.A[3] |= ORTHO_MASKS[q] | DIAG_MASKS[q]
    end
    board.A[4] = EMPTY
    for r in board.R
        board.A[4] |= ORTHO_MASKS[r]
    end
    board.A[5] = EMPTY
    for b in board.B
        board.A[5] |= DIAG_MASKS[b]
    end
    board.white_attacks = EMPTY
    for i = 1:5
        board.white_attacks |= board.A[i]
    end

    # black
    board.a[1] = EMPTY
    for p in board.p
        for a in BLACK_PAWN_ATTACK[p]
            board.a[1] |= a
        end
    end
    board.a[2] = EMPTY
    for n in board.n
        for a in NIGHT_MOVES[n]
            board.a[2] |= a
        end
    end
    board.a[3] = EMPTY
    for q in board.q
        board.a[3] |= ORTHO_MASKS[q] | DIAG_MASKS[q]
    end
    board.a[4] = EMPTY
    for r in board.r
        board.a[4] |= ORTHO_MASKS[r]
    end
    board.a[5] = EMPTY
    for b in board.b
        board.a[5] |= DIAG_MASKS[b]
    end
    board.black_attacks = EMPTY
    for i = 1:5
        board.black_attacks |= board.a[i]
    end

    return board
end

function move_piece(b::Bitboard, m::Move, color::String="white")
    push!(b.game, color*m.piece_type*UINT2PGN[m.source])

    if color == "white"
        b.white = update_from_to_squares(b.white, m.source, m.target)
        b.taken = update_from_to_squares(b.taken, m.source, m.target)

        if m.capture_type != "none"
            b.black = remove_from_square(b.black, m.target)
            if m.capture_type == "pawn"
                b.p = remove_from_square(b.p, m.target)
            elseif m.capture_type == "night"
                b.n = remove_from_square(b.n, m.target)
            elseif m.capture_type == "bishop"
                b.b = remove_from_square(b.b, m.target)
            elseif m.capture_type == "queen"
                b.q = remove_from_square(b.q, m.target)
            elseif m.capture_type == "rook"
                b.r = remove_from_square(b.r, m.target)
            end
        end

        if m.piece_type == "pawn"
            if m.target == b.enpassant_square
                b.black = remove_from_square(b.black, m.target >> 8)
                b.p = remove_from_square(b.p, m.target >> 8)
                b.taken = remove_from_square(b.taken, m.target >> 8)
                b.enpassant_done = true
            else
                b.enpassant_done = false
                if m.enpassant_square != EMPTY
                    b.enpassant_square = m.enpassant_square
                end
            end
            b.P = update_from_to_squares(b.P, m.source, m.target)
        elseif m.piece_type == "night"
            b.N = update_from_to_squares(b.N, m.source, m.target)
        elseif m.piece_type == "bishop"
            b.B = update_from_to_squares(b.B, m.source, m.target)
        elseif m.piece_type == "queen"
            b.Q = update_from_to_squares(b.Q, m.source, m.target)
        elseif m.piece_type == "rook"
            b.R = update_from_to_squares(b.R, m.source, m.target)
        elseif m.piece_type == "king"
            b.K = update_from_to_squares(b.K, m.source, m.target)
            if m.castling_type != "-"
                if m.castling_type == "K"
                    b.R = update_from_to_squares(b.R, H1, F1)
                    b.white = update_from_to_squares(b.white, H1, F1)
                    b.taken = update_from_to_squares(b.taken, H1, F1)
                elseif m.castling_type == "Q"
                    b.R = update_from_to_squares(b.R, A1, D1)
                    b.white = update_from_to_squares(b.white, A1, D1)
                    b.taken = update_from_to_squares(b.taken, A1, D1)
                end
            end
        end

        if m.promotion_type != "none"
            b.P = remove_from_square(b.P, m.target)
            if m.promotion_type == "queen"
                b.Q = add_to_square(b.Q, m.target)
            elseif m.promotion_type == "rook"
                b.R = add_to_square(b.R, m.target)
            elseif m.promotion_type == "night"
                b.N = add_to_square(b.N, m.target)
            elseif m.promotion_type == "bishop"
                b.B = add_to_square(b.B, m.target)
            end
        end
    else
        b.black = update_from_to_squares(b.black, m.source, m.target)
        b.taken = update_from_to_squares(b.taken, m.source, m.target)

        if m.capture_type != "none"
            b.white = remove_from_square(b.white, m.target)
            if m.capture_type == "pawn"
                b.P = remove_from_square(b.P, m.target)
            elseif m.capture_type == "night"
                b.N = remove_from_square(b.N, m.target)
            elseif m.capture_type == "bishop"
                b.B = remove_from_square(b.B, m.target)
            elseif m.capture_type == "queen"
                b.Q = remove_from_square(b.Q, m.target)
            elseif m.capture_type == "rook"
                b.R = remove_from_square(b.R, m.target)
            end
        end

        if m.piece_type == "pawn"
            if m.target == b.enpassant_square
                b.white = remove_from_square(b.white, m.target << 8)
                b.P = remove_from_square(b.P, m.target << 8)
                b.taken = remove_from_square(b.taken, m.target << 8)
                b.enpassant_done = true
            else
                b.enpassant_done = false
                if m.enpassant_square != EMPTY
                    b.enpassant_square = m.enpassant_square
                end
            end
            b.p = update_from_to_squares(b.p, m.source, m.target)
        elseif m.piece_type == "night"
            b.n = update_from_to_squares(b.n, m.source, m.target)
        elseif m.piece_type == "bishop"
            b.b = update_from_to_squares(b.b, m.source, m.target)
        elseif m.piece_type == "queen"
            b.q = update_from_to_squares(b.q, m.source, m.target)
        elseif m.piece_type == "rook"
            b.r = update_from_to_squares(b.r, m.source, m.target)
        elseif m.piece_type == "king"
            b.k = update_from_to_squares(b.k, m.source, m.target)
            if m.castling_type != "-"
                if m.castling_type == "k"
                    b.r = update_from_to_squares(b.r, H8, F8)
                    b.black = update_from_to_squares(b.black, H8, F8)
                    b.taken = update_from_to_squares(b.taken, H8, F8)
                elseif m.castling_type == "q"
                    b.r = update_from_to_squares(b.r, A8, D8)
                    b.black = update_from_to_squares(b.black, A8, D8)
                    b.taken = update_from_to_squares(b.taken, A8, D8)
                end
            end
        end

        if m.promotion_type != "none"
            b.p = remove_from_square(b.p, m.target)
            if m.promotion_type == "queen"
                b.q = add_to_square(b.q, m.target)
            elseif m.promotion_type == "rook"
                b.r = add_to_square(b.r, m.target)
            elseif m.promotion_type == "night"
                b.n = add_to_square(b.n, m.target)
            elseif m.promotion_type == "bishop"
                b.b = add_to_square(b.b, m.target)
            end
        end
    end
    b.free = ~b.taken

    # b = update_attacked(b)
    # return update_castling_rights(b)
    return b
end

# function unmove_piece(board::Bitboard, move::Move, color::String="white")
#     if color == "white"
#         board.white = update_from_to_squares(board.white, move.target,
#             move.source)
#         board.taken = update_from_to_squares(board.taken, move.target,
#             move.source)

#         if move.piece_type == "pawn"
#             if board.enpassant_done
#                 board.black = add_to_square(board.black, move.target >> 8)
#                 board.p = add_to_square(board.p, move.target >> 8)
#                 board.taken = add_to_square(board.taken, move.target >> 8)
#                 board.enpassant_done = false
#                 board.enpassant_square = move.target
#             end
#             board.P = update_from_to_squares(board.P, move.target, move.source)
#         elseif move.piece_type == "night"
#             board.N = update_from_to_squares(board.N, move.target, move.source)
#         elseif move.piece_type == "bishop"
#             board.B = update_from_to_squares(board.B, move.target, move.source)
#         elseif move.piece_type == "queen"
#             board.Q = update_from_to_squares(board.Q, move.target, move.source)
#         elseif move.piece_type == "rook"
#             board.R = update_from_to_squares(board.R, move.target, move.source)
#         elseif move.piece_type == "king"
#             board.K = update_from_to_squares(board.K, move.target, move.source)
#             if move.castling_type != "-"
#                 if move.castling_type == "K"
#                     board.R = update_from_to_squares(board.R, F1, H1)
#                     board.white = update_from_to_squares(board.white, F1, H1)
#                     board.taken = update_from_to_squares(board.taken, F1, H1)
#                 elseif move.castling_type == "Q"
#                     board.R = update_from_to_squares(board.R, D1, A1)
#                     board.white = update_from_to_squares(board.white, D1, A1)
#                     board.taken = update_from_to_squares(board.taken, D1, A1)
#                 end
#             end
#         end

#         if move.capture_type != "none"
#             board.black = add_to_square(board.black, move.target)
#             board.taken = add_to_square(board.taken, move.target)
#             if move.capture_type == "pawn"
#                 board.p = add_to_square(board.p, move.target)
#             elseif move.capture_type == "night"
#                 board.n = add_to_square(board.n, move.target)
#             elseif move.capture_type == "bishop"
#                 board.b = add_to_square(board.b, move.target)
#             elseif move.capture_type == "queen"
#                 board.q = add_to_square(board.q, move.target)
#             elseif move.capture_type == "rook"
#                 board.r = add_to_square(board.r, move.target)
#             end
#         end
#     else
#         board.black = update_from_to_squares(board.black, move.target,
#             move.source)
#         board.taken = update_from_to_squares(board.taken, move.target,
#             move.source)

#         if move.piece_type == "pawn"
#             if board.enpassant_done
#                 board.white = add_to_square(board.white, move.target << 8)
#                 board.P = add_to_square(board.P, move.target << 8)
#                 board.taken = add_to_square(board.taken, move.target << 8)
#                 board.enpassant_done = false
#                 board.enpassant_square = move.target
#             end
#             board.p = update_from_to_squares(board.p, move.target, move.source)
#         elseif move.piece_type == "night"
#             board.n = update_from_to_squares(board.n, move.target, move.source)
#         elseif move.piece_type == "bishop"
#             board.b = update_from_to_squares(board.b, move.target, move.source)
#         elseif move.piece_type == "queen"
#             board.q = update_from_to_squares(board.q, move.target, move.source)
#         elseif move.piece_type == "rook"
#             board.r = update_from_to_squares(board.r, move.target, move.source)
#         elseif move.piece_type == "king"
#             board.k = update_from_to_squares(board.k, move.target, move.source)
#             if move.castling_type != "-"
#                 if move.castling_type == "k"
#                     board.r = update_from_to_squares(board.r, F8, H8)
#                     board.black = update_from_to_squares(board.black, F8, H8)
#                     board.taken = update_from_to_squares(board.taken, F8, H8)
#                 elseif move.castling_type == "q"
#                     board.r = update_from_to_squares(board.r, D8, A8)
#                     board.black = update_from_to_squares(board.black, D8, A8)
#                     board.taken = update_from_to_squares(board.taken, D8, A8)
#                 end
#             end
#         end

#         if move.capture_type != "none"
#             board.white = add_to_square(board.white, move.target)
#             board.taken = add_to_square(board.taken, move.target)
#             if move.capture_type == "pawn"
#                 board.P = add_to_square(board.P, move.target)
#             elseif move.capture_type == "night"
#                 board.N = add_to_square(board.N, move.target)
#             elseif move.capture_type == "bishop"
#                 board.B = add_to_square(board.B, move.target)
#             elseif move.capture_type == "queen"
#                 board.Q = add_to_square(board.Q, move.target)
#             elseif move.capture_type == "rook"
#                 board.R = add_to_square(board.R, move.target)
#             end
#         end
#     end
#     board.free = ~board.taken
#     pop!(board.game)
#     # board = update_attacked(board)
#     # return update_castling_rights(board)
#     return board
# end


text:
using Test

include("../src/types.jl")

using MdSlicing

@testset "random values are within problem spec range" begin
  ps = ProblemSpec("x1" => (-2., 2.), "x2" => (0., 1.), "x3" => (25., 50.))

  fps = fillfps(ps, [missing, 0., 0.], 500)
  @test all([x[1] >= ps.vals[1][1] for x in fps])
  @test all([x[1] <= ps.vals[1][2] for x in fps])

  fps = fillfps(ps, [0., missing, 0.], 500)
  @test all([x[2] >= ps.vals[2][1] for x in fps])
  @test all([x[2] <= ps.vals[2][2] for x in fps])

  fps = fillfps(ps, [0., 0., missing], 500)
  @test all([x[3] >= ps.vals[3][1] for x in fps])
  @test all([x[3] <= ps.vals[3][2] for x in fps])
end

@testset "properly handles fully-specified fp" begin
  ps = ProblemSpec("x1" => (-2., 2.), "x2" => (0., 1.), "x3" => (25., 50.))

  fps = fillfps(ps, [0., 0., 0.], 50)
  @test length(fps) == 1
end


text:
X = zeros(Int, 12, 12*11*10)

n = 1
for p1 in 1:12
    for p2 in 1:12
        if p2 != p1
            for p3 in 1:12
                if p3 != p1 && p3 != p2
                    X[p1, n] = 1
                    X[p2, n] = 1
                    X[p3, n] = 1
                    n = n + 1
                end
            end
        end
    end
end

Cmaj = [1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0]
X_fourier = abs.(fft(X, 1))
Cmaj_fourier = abs.(fft(Cmaj, 1))
Cmaj_fourier = repmat(Cmaj_fourier, 1, 12*11*10)
dists_fourier = sum((X_fourier - Cmaj_fourier).*(X_fourier - Cmaj_fourier), 1)
sorted_dists_fourier, sorting_indices_fourier = sort(vec(dists_fourier))


text:
module TokenizationSentence

importall ..JukaiNLP
using Merlin

export train, Tokenizer

include("tokenizer.jl")
include("train.jl")

end

max_stars_repo_name: hshindo/JukaiNLP

max_issues_repo_name: hshindo/JukaiNLP

max_forks_repo_name: hshindo/JukaiNLP


text:
module Transformers

using Flux
using Requires
using Requires: @init

export Transformer, TransformerDecoder
export Stack, stack, @nntopo_str, @nntopo, show_stackfunc

export dataset, datafile, get_batch, get_vocab

export todevice
export Gpt, load_gpt_pretrain, lmloss

const Abstract3DTensor{T} = AbstractArray{T, 3}
const Container{T} = Union{NTuple{N, T}, Vector{T}} where N

"move data to device, when CuArrays is loaded, basically = `CuArrays.cu` except `AbstractArray{Int}` become `CuArray{Int}`"
todevice(x) = x
todevice(x, xs...) = (x, xs...)

@init @require CuArrays="3a865a2d-5b23-5a0f-bc46-62713ec82fae" begin
    import .CuArrays

    "move data to device, basically = `CuArrays.cu` except `AbstractArray{Int}` become `CuArray{Int}`"
    todevice(x, xs...) = (todevice(x), todevice.(xs)...)
    todevice(x::AbstractArray{Int}) = CuArray(x)
    todevice(x) = CuArrays.cu(x)
end

#implement batchmul for flux
include("./fix/batchedmul.jl")

#implement of gelu for gpu
include("./fix/gelu.jl")

#dropout noise shape impl
include("./fix/dropout.jl")

include("./basic/Basic.jl")
include("./datasets/Datasets.jl")

include("./gpt/GenerativePreTrain.jl")

using .Basic
using .Datasets
using .GenerativePreTrain

end # module


text:
using LibCImGui.CImGuiPack_jll

import LibCImGui:
    ImVec2, ImVec4,
    ImGuiMouseButton, ImGuiKeyModFlags, ImGuiCond, ImGuiDragDropFlags,
    ImS8, ImU8, ImS16, ImU16, ImS32, ImU32, ImS64, ImU64,
    ImTextureID,
    ImDrawList,
    ImGuiContext,
    ImGuiStyleVar,
    ImGuiStyleMod,
    ImGuiCol,
    ImGuiColorMod,
    ImGuiID,
    ImGuiStoragePair,
    ImGuiTextBuffer,
    ImGuiStorage,
    ImVector_float,
    ImVector_ImU32,
    ImVector_ImGuiStyleMod,
    ImVector_ImGuiColorMod,
    ImRect,
    ImPoolIdx

#Temporary patch; CImGui.jl v1.79.0 aliases ImS8 incorrectly; add to imports in new versions
#const ImS8 = Int8
const IMPLOT_AUTO = Cint(-1)
const IMPLOT_AUTO_COL = ImVec4(0,0,0,-1)
export IMPLOT_AUTO, IMPLOT_AUTO_COL


text:
@testset "Solver" begin
    @testset "simple systems" begin
        @polyvar x y z
        f = [x^2 - 2, x + y - 1]
        solver, starts = solver_startsolutions(f; system = FPSystem)
        @test isa(solver, Solver)
        result = solve(solver, starts)
        @test all(is_success, result)
        @test all(!is_failed, result)

        f = equations(griewank_osborne())
        solver, starts = solver_startsolutions(f; seed = 78373, system = FPSystem)
        result = solve(solver, starts)
        @test nsolutions(result) == 1
        r = first(results(result))
        @test multiplicity(r) == 3
        @test is_singular(r)
        @test !isempty(sprint(show, r))
    end

    @testset "path jumping" begin
        solver, starts = solver_startsolutions(
            equations(katsura(5));
            system = FPSystem,
            seed = 124232,
            max_corrector_iters = 6,
            accuracy = 1e-5,
        )
        result_jumping = solve(solver, starts; path_jumping_check = false)
        @test nsolutions(result_jumping) < 32

        for threading in [false, true]
            result = solve(solver, starts; path_jumping_check = true, threading = threading)
            @test nsolutions(result) == 32
            @test all(is_nonsingular, result)
            @test all(is_success, result)
        end

        # check that path_jumping_check is on by default
        result2 = solve(solver, starts)
        @test nsolutions(result2) == 32
    end

    @testset "polyhedral" begin
        solver, starts = solver_startsolutions(
            equations(cyclic(5));
            seed = 32241,
            start_system = :polyhedral,
        )
        result = solve(solver, starts)
        @test nsolutions(result) == 70
        @test ntracked(result) == 70
        @test nreal(result) == 10

        @polyvar x y
        solver, starts = solver_startsolutions(
            [2y + 3 * y^2 - x * y^3, x + 4 * x^2 - 2 * x^3 * y];
            seed = 32241,
            start_system = :polyhedral,
            only_torus = true,
        )
        result = solve(solver, starts, threading = false)
        @test nsolutions(result) == 3
        @test ntracked(result) == 3

        solver, starts = solver_startsolutions(
            [2y + 3 * y^2 - x * y^3, x + 4 * x^2 - 2 * x^3 * y];
            seed = 32241,
            start_system = :polyhedral,
            only_torus = false,
        )
        result = solve(solver, starts)
        @test nsolutions(result) == 6
        @test ntracked(result) == 8
    end

    @testset "solve" begin
        @polyvar x y z
        f = [x^2 - 2, x + y - 1]
        result = solve(f; system = FPSystem)
        @test all(is_success, result)
        @test nsolutions(result) == 2
        @test result isa Result{Vector{ComplexF64}}

        @test nsolutions(solve(f; system = FPSystem, predictor = Euler())) == 2
        @test nsolutions(solve(f; system = FPSystem, predictor = RK4())) == 2
    end

    @testset "different input" begin
        F = equations(katsura(5))
        F_hom = homogenize(F)
        res = solve(F_hom, threading = false, patch = RandomPatch())
        @test nfinite(res) == 32
        @test res isa Result{PVector{ComplexF64,1}}
        @test nfinite(solve(F_hom, threading = false, patch = EmbeddingPatch())) == 32
        @test nfinite(solve(F_hom, threading = false, patch = OrthogonalPatch())) == 32

        @polyvar w
        F = equations(cyclic(5))
        result = solve(homogenize(F, w), system = FPSystem, threading = false, homvar = w)
        @test nfinite(result) == 70
        @test result isa Result{Vector{ComplexF64}}
        G = FPSystem(homogenize(F))
        result = solve(G, homvar = 6)
        @test nnonsingular(result) == 70
        @test result isa Result{Vector{ComplexF64}}
    end

    @testset "singular" begin
        # 1 singular solution with multiplicity 3
        @polyvar x y
        z = 1
        F = [
            x^2 + 2 * y^2 + 2 * im * y * z,
            (18 + 3 * im) * x * y + 7 * im * y^2 - (3 - 18 * im) * x * z - 14 * y * z -
            7 * im * z^2,
        ]
        result = solve(F; system = FPSystem)
        @test multiplicity(first(nonsingular(result))) == 1
        @test multiplicity(first(singular(result))) == 3
        @test nsingular(result) == 1
        @test nsingular(result; counting_multiplicities = true) == 3
        @test length(singular(result)) == 1
        @test all(r -> multiplicity(r) == 3, singular(result; multiple_results = true))

        # 2 solutions with multiplicity 6, projective
        @polyvar x z y
        # This has two roots of multiplicity 6 at the hyperplane z=0
        F = [
            0.75 * x^4 + 1.5 * x^2 * y^2 - 2.5 * x^2 * z^2 + 0.75 * y^4 - 2.5 * y^2 * z^2 +
            0.75 * z^4
            10 * x^2 * z + 10 * y^2 * z - 6 * z^3
        ]
        res = solve(F; system = FPSystem, threading = false, seed = 703127)
        @test nsingular(res) == 2
        @test nsingular(res; counting_multiplicities = true) == 12
        @test multiplicity.(results(res)) == [6, 6]

        # 2 solutions with multiplicity 6, affine
        @polyvar x z
        y = 1
        # This has two roots of multiplicity 6 at the hyperplane z=0
        F = [
            0.75 * x^4 + 1.5 * x^2 * y^2 - 2.5 * x^2 * z^2 + 0.75 * y^4 - 2.5 * y^2 * z^2 +
            0.75 * z^4
            10 * x^2 * z + 10 * y^2 * z - 6 * z^3
        ]
        res = solve(F; system = FPSystem, threading = false, seed = 412312)
        @test nsingular(res) == 2
        @test nsingular(res; counting_multiplicities = true) == 12
        @test multiplicity.(results(res)) == [6, 6]
    end

    @testset "affine and projective solution types" begin
        @polyvar x y z
        result = solve([x - 2y, y - 2z])
        @test result isa Result{PVector{ComplexF64,1}}
        @test first(solutions(result)) isa PVector{ComplexF64,1}

        result2 = solve([x - 2, y - 2])
        @test result2 isa Result{Vector{ComplexF64}}
        @test first(solutions(result2)) isa Vector{ComplexF64}

        @polyvar u v
        result3 = solve([x * y - u * v, x - u], variable_groups = [(x, u), (y, v)])
        @test result3 isa Result{PVector{ComplexF64,2}}
        @test first(solutions(result3)) isa PVector{ComplexF64,2}
    end

    @testset "Keep invalid start values" begin
        @polyvar x[1:2] a[1:2]
        F = [x[1]^2 - a[1], x[1] * x[2] - a[1] + a[2]]
        startsolutions = [[100, 100]]
        p₁ = [1, 1]
        p₀ = [3im, 0.5 + 2im]
        res = solve(
            F,
            startsolutions;
            parameters = a,
            start_parameters = p₁,
            target_parameters = p₀,
        )
        @test nfailed(res) == 1
        @test res[1].return_code == :terminated_invalid_startvalue
    end

    @testset "overdetermined" begin
        @polyvar x y z
        f = [x * z - y^2, y - z^2, x - y * z, x + y + z + 1]

        @test nsolutions(solve(f; seed = 213412)) == 3
        @test nsolutions(solve(
            f;
            seed = 213412,
            start_system = :polyhedral,
            threading = false,
        )) == 3
        @test nsolutions(solve(f; seed = 213412, start_system = :polyhedral)) == 3

        minors = include(joinpath(@__DIR__, "examples", "3_by_5_minors.jl"))
        @test nsolutions(solve(minors; seed = 312321, system = FPSystem)) == 80

        # singular solutions
        @polyvar x y
        f = [
            (x - 2)^4 * (x + y + 1),
            (x^2 + y^2 - 2) * (y - 2),
            (y - 2) * (x^2 + x * y - 3),
        ]
        result = solve(f)
        @test length(nonsingular(result)) == 1
        @test length(singular(result)) == 1
        @test multiplicity(singular(result)[1]) == 4
    end

    @testset "Parameter promotion to Float64" begin
        d = 3
        k = 1

        R = 1.0
        r = 0.5

        @polyvar lam[1:k] p[1:6]
        F = [(R^2 - r^2 + (p[1] + p[4] * lam[1])^2 + (p[2] + p[5] * lam[1])^2 +
              (p[3] + p[6] * lam[1])^2)^2 -
             4 * R^2 * ((p[1] + p[4] * lam[1])^2 + (p[2] + p[5] * lam[1])^2)]
        #Randomly choose a start system
        # with only Complex{Float32 parameters
        p0 = randn(Complex{Float32}, 6)
        F0 = subs(F, p => p0)
        res0 = solve(F0)
        S0 = solutions(res0)

        #Construct the tracker
        tracker = pathtracker(F; parameters = p, generic_parameters = p0)
        @test HC.basehomotopy(tracker.core_tracker.homotopy).p isa NTuple{
            2,
            Vector{ComplexF64},
        }

        #Let's solve another system using PathTracking
        p_current = randn(6)
        result = track(tracker, S0[1]; target_parameters = p_current)
        sol = solution(result)
        F_SP = SPSystem(F; parameters = p)
        @test residual(result) ≈ euclidean_norm(F_SP(sol, p_current)) atol = 1e-13
    end

    @testset "early stop callback" begin
        @polyvar x
        first_result = nothing
        results = solve(
            [(x - 3) * (x + 6) * (x + 2)];
            system = FPSystem,
            stop_early_cb = r -> begin
                first_result = r
                true
            end,
            threading = false,
        )
        @test length(results) == 1
        @test first(results) == first_result

        nresults = 0
        result = solve(
            [(x - 3) * (x + 6) * (x + 2)];
            system = FPSystem,
            stop_early_cb = r -> (nresults += 1) == 2,
            threading = false,
        )
        @test length(result) == 2

        # threading
        @polyvar x y z
        first_result = nothing
        # this has 5^3 = 125 solutions, so we should definitely stop early if we
        # have less than 64 threads
        results = solve(
            [
             (x - 3) * (x + 6) * (x + 2) * (x - 2) * (x + 2.5),
             (y + 2) * (y - 2) * (y + 3) * (y + 5) * (y - 1),
             (z + 2) * (z - 2) * (z + 3) * (z + 5) * (z - 2.1),
            ];
            system = FPSystem,
            stop_early_cb = r -> begin
                first_result = r
                true
            end,
            threading = true,
        )
        @test length(results) < 125
    end

    @testset "variable ordering" begin
        # total degree
        @polyvar x y z
        f = [x + 1, y + 2, z + 3]
        @test solution(first(solve(f))) ≈ [-1, -2, -3]
        @test solution(first(solve(f; variable_ordering = [z, y, x]))) ≈ [-3, -2, -1]
        @test_throws ArgumentError solve(f; variable_ordering = [z, y])

        # parameter
        @polyvar x a y b
        F = [x^2 - a, x * y - a + b]
        p = [a, b]

        r1 = solve(F, [√2, √2]; parameters = p, p₁ = [2, 0], p₀ = [2, 4])
        @test is_success(first(r1))
        s1 = solution(first(r1))

        r2 = solve(
            F,
            [√2, √2];
            variable_ordering = [y, x],
            parameters = p,
            p₁ = [2, 0],
            p₀ = [2, 4],
        )
        s2 = solution(first(r2))

        @test s1 ≈ s2[[2, 1]]
        @test_throws ArgumentError solve(
            F,
            [√2, √2];
            variable_ordering = [y],
            parameters = p,
            p₁ = [2, 0],
            p₀ = [2, 4],
        )

        # start target
        @polyvar x y
        G = [x^2 - 2, x * y - 2]
        F = [x^2 - 2, x * y + 2]
        r3 = solve(G, F, [√2, √2]; variable_ordering = [y, x])
        s3 = solution(first(r3))
        @test s1 ≈ s3[[2, 1]]

        @test_throws ArgumentError solve(G, F, [√2, √2]; variable_ordering = [x])
    end

    @testset "parameter change" begin
        @polyvar x y z p[1:3]
        F = [
            x + 3 + 2y + 2 * y^2 - p[1],
            (x - 2 + 5y) * z + 4 - p[2] * z,
            (x + 2 + 4y) * z + 5 - p[3] * z,
        ]
        q = randn(ComplexF64, 3)
        S = solutions(solve(subs(F, p => q)))
        # create some fake parameter values
        params = [
            [1.3440639843905677, -2.5074017514201343, -0.39441026816083785],
            [-0.5905470689391262, 0.662942297773609, -0.4383381251082712],
            [-0.07181848550172094, -0.9892961521599329, 0.3648603261873276],
            [2.37916274514681, -0.4796116819359556, 1.4850511902292287],
            [1.0080243329063356, 0.1156613479279384, -0.4496252569645542],
            [0.5658877469626858, 0.2383098725793224, -0.13845976523959966],
            [-0.22823657335108488, -0.7111958812641874, -0.7478056668942166],
            [-0.255059563703982, -0.9004822204456397, -0.3389293738788412],
            [0.3392911355486216, 0.05817061767333153, 0.33923705766735207],
            [1.0861653811709309, -0.31406883615041653, -0.17861101511219216],
        ]
        # create a `Solver` to reuse for the path tracking
        F_solver = solver(F; parameters = p, generic_parameters = q)
        # solve the system F for all paramaters p in params
        params_solutions = map(params) do p
            solutions(solve(F_solver, S; target_parameters = p))
        end
        @test sum(length, params_solutions) == 20
    end

    @testset "Many parameters solver" begin
        # Setup
        @polyvar x y
        f = x^2 + y^2 - 1

        @polyvar a b c
        l = a * x + b * y + c
        F = [f, l]

        # Compute start solutions S₀ for given start parameters p₀
        p₀ = randn(ComplexF64, 3)
        S₀ = solutions(solve(subs(F, [a, b, c] => p₀)))
        # The parameters we are intersted in
        params = [rand(3) for i = 1:100]

        result1 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            threading = true,
        )
        @test typeof(result1) == Vector{Tuple{Result{Vector{ComplexF64}},Vector{Float64}}}
        result1 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            threading = false,
        )
        @test typeof(result1) == Vector{Tuple{Result{Vector{ComplexF64}},Vector{Float64}}}

        # Only keep real solutions
        result2 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            transform_result = (r, p) -> real_solutions(r),
            threading = true,
        )
        @test typeof(result2) == Vector{Vector{Vector{Float64}}}
        @test !isempty(result2)

        # Now instead of an Array{Array{Array{Float64,1},1},1} we want to have an
        # Array{Array{Float64,1},1}
        result3 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = params,
            parameters = [a, b, c],
            transform_result = (r, p) -> real_solutions(r),
            flatten = true,
            threading = false,
        )
        @test typeof(result3) == Vector{Vector{Float64}}
        @test !isempty(result3)

        # The passed `params` do not directly need to be the target parameters.
        # Instead they can be some more concrete informations (e.g. an index)
        # and we can them by using the `transform_parameters` method
        result4 = solve(
            F,
            S₀,
            ;
            start_parameters = p₀,
            target_parameters = 1:100,
            parameters = [a, b, c],
            transform_result = (r, p) -> (real_solutions(r), p),
            transform_parameters = _ -> rand(3),
        )
        @test typeof(result4) == Vector{Tuple{Vector{Vector{Float64}},Int64}}
    end
end