text:
# This file is a part of Julia. License is MIT: https://julialang.org/license

"""
    Threads.foreach(f, channel::Channel;
                    schedule::Threads.AbstractSchedule=Threads.FairSchedule(),
                    ntasks=Threads.nthreads())

Similar to `foreach(f, channel)`, but iteration over `channel` and calls to
`f` are split across `ntasks` tasks spawned by `Threads.@spawn`. This function
will wait for all internally spawned tasks to complete before returning.

If `schedule isa FairSchedule`, `Threads.foreach` will attempt to spawn tasks in a
manner that enables Julia's scheduler to more freely load-balance work items across
threads. This approach generally has higher per-item overhead, but may perform
better than `StaticSchedule` in concurrence with other multithreaded workloads.

If `schedule isa StaticSchedule`, `Threads.foreach` will spawn tasks in a manner
that incurs lower per-item overhead than `FairSchedule`, but is less amenable
to load-balancing. This approach thus may be more suitable for fine-grained,
uniform workloads, but may perform worse than `FairSchedule` in concurrence
with other multithreaded workloads.

!!! compat "Julia 1.6"
    This function requires Julia 1.6 or later.
"""
function Threads.foreach(f, channel::Channel;
                         schedule::Threads.AbstractSchedule=Threads.FairSchedule(),
                         ntasks=Threads.nthreads())
    apply = _apply_for_schedule(schedule)
    stop = Threads.Atomic{Bool}(false)
    @sync for _ in 1:ntasks
        Threads.@spawn try
            for item in channel
                $apply(f, item)
                # do `stop[] && break` after `f(item)` to avoid losing `item`.
                # this isn't super comprehensive since a task could still get
                # stuck on `take!` at `for item in channel`. We should think
                # about a more robust mechanism to avoid dropping items. See also
                # https://github.com/JuliaLang/julia/pull/34543#discussion_r422695217
                stop[] && break
            end
        catch
            stop[] = true
            rethrow()
        end
    end
    return nothing
end

_apply_for_schedule(::Threads.StaticSchedule) = (f, x) -> f(x)
_apply_for_schedule(::Threads.FairSchedule) = (f, x) -> wait(Threads.@spawn f(x))

max_stars_repo_name: jonas-schulze/julia

max_issues_repo_name: jonas-schulze/julia

max_forks_repo_name: jonas-schulze/julia


text:
module QuantumAlgebra

# helper function used in some definitons, copied from ColorTypes.jl
Base.@pure basetype(T::Type) = Base.typename(T).wrapper
Base.@pure basetype(A) = basetype(typeof(A))

include("operator_defs.jl")
include("operator_iteration.jl")
include("operator_baseops.jl")
include("index_handling.jl")
include("output.jl")
include("correlations.jl")
include("vacuum_expvals.jl")

include("precompile.jl")
_precompile_()

end # module

ext: jl


text:
"""
This file contains descriptions of test cases of an array initialization.
Each test case has unique name and an implementation body.
The body is a function with the only argument - array size.
"""

register_test_case("direct") do n
    local x = Vector{Int}(undef, n)
    for i in 1:n
        x[i] = i
    end
    x
end

register_test_case("push_hint") do n
    local x = Int[]
    for i in 1:n
        push!(x, i)
    end
    x
end

register_test_case("push") do n
    local x = Int[]
    sizehint!(x, n)
    for i in 1:n
        push!(x, i)
    end
    x
end

register_test_case("f4", ignore = true) do n
    local x = Int[]
    sizehint!(x, n)
    for i in 1:n
        x[i] = i
    end
    x
end

register_test_case("f5", ignore = true) do n
    local x = Vector{Int}(undef, n)
    for i in 1:n
        push!(x, i)
    end
    x
end

register_test_case("direct_inbounds") do n
    local x = Vector{Int}(undef, n)
    for i in 1:n
        @inbounds x[i] = i
    end
    x
end

register_test_case("beggar_push") do n
    local x = Int[]
    local len = 0
    for i in 1:n
        if i > len
            len = 2*len + 1
            resize!(x, len)
        end
        @inbounds x[i] = i
    end
    resize!(x, n)
    x
end


text:
struct Iteration
    i::Int
end

MatrixOf(::Type{Vector{T}}) where T = Matrix{T}
MatrixOf(::Type{CuVector{T}}) where T = CuMatrix{T}
zerosof(::Type{TM}, n...) where TM = (TM(undef, n...) .= 0)
onesof(::Type{TM}, n...) where TM = (TM(undef, n...) .= 1)
infsof(::Type{TM}, n...) where TM = (TM(undef, n...) .= Inf)
ninfsof(::Type{TM}, n...) where TM = (TM(undef, n...) .= -Inf)
nansof(::Type{TM}, n...) where TM = (TM(undef, n...) .= NaN)

@inline minus_plus(a, b) = a - b, a + b

@inline or(a,b) = a || b

@inline function matdot(v::AbstractVector, A::AbstractMatrix, j::Int)
    T = promote_type(eltype(v), eltype(A))
    r = mapreduce((va)->(va[1]*va[2]), +, zip(v, @view(A[j,:])), init=T(0))
    return r
end

function check_error(m, x0)
    if length(x0) != dim(m)
        throw(ArgumentError("initial variable must have same length as number of design variables"))
    end

    for j in 1:length(x0)
        # x is not in box
        if !(min(m, j) <= x0[j] <= max(m,j))
            throw(ArgumentError("initial variable at index $j outside box constraint"))
        end
    end
end

# From Optim.jl
function assess_convergence(x::Array,
                            x_previous::Array,
                            f_x::Real,
                            f_x_previous::Real,
                            gr::Array,
                            xtol::Real,
                            ftol::Real,
                            grtol::Real)
    x_converged, f_converged, gr_converged, f_increased = false, false, false, false

    x_residual = maxdiff(x, x_previous)
    f_residual = abs(f_x - f_x_previous)
    gr_residual = maximum(abs, gr)

    if x_residual < xtol
        x_converged = true
    end

    # Absolute Tolerance
    # if abs(f_x - f_x_previous) < ftol
    # Relative Tolerance
    if f_residual / (abs(f_x) + ftol) < ftol
        f_converged = true
    end

    if f_x > f_x_previous
        f_increased = true
    end

    if gr_residual < grtol
        gr_converged = true
    end

    converged = x_converged || f_converged || gr_converged

    return x_converged, f_converged, gr_converged, x_residual, f_residual, gr_residual, f_increased, converged
end


text:
@enum PoolState begin
    Done
    Running
    Failed
end

struct PoolFailedException <: Exception
    s::Any
end

mutable struct WorkerPool{TArgs}
    workerfn::Any
    args::Vector{TArgs}
    useprimary::Bool
    state::PoolState
    ntasks::Threads.Atomic{Int}
end

function WorkerPool(workerfn, args::AbstractVector{TArgs}; useprimary = false) where {TArgs}
    (useprimary || Threads.nthreads() > 1) ||
        error("Cannot load data off main thread with only one thread available. Pass `useprimary = true` or start Julia with >1 threads.")
    return WorkerPool{TArgs}(workerfn, collect(args), useprimary, Done, Threads.Atomic{Int}(0))
end

function run(pool::WorkerPool)
    pool.state = Running
    # set remaining tasks counter.
    pool.ntasks[] = length(pool.args)

    # watchdog that sends exception to main thread if a worker fails
    maintask = current_task()
    @async begin
        while pool.state !== Done
            if pool.state === Failed
                Base.throwto(
                    maintask,
                    PoolFailedException("Failed to process all tasks. $(pool.ntasks[]) unfinished tasks remaining"),
                )
            end
            sleep(0.1)
        end
    end

    function inloop(args)
        #for args in pool.args  # uncomment for debugging
        # task error handling
        pool.state !== Failed || error("Shutting down worker $(Threads.threadid())")
        try
            # execute task
            pool.workerfn(args...)
            Threads.atomic_add!(pool.ntasks, -1)
        catch e
            println(stacktrace())
            @error "Exception while executing task on worker $(Threads.threadid()). Shutting down WorkerPool." e =
                e stacktrace = stacktrace() args = args
            pool.state = Failed
            rethrow()
        end
    end

    if pool.useprimary
        @qthreads for args in pool.args
            inloop(args)
        end
    else
        @qbthreads for args in pool.args
            inloop(args)
        end
    end

    # Tasks completed successfully
    pool.state = Done
end


text:
#####
##### Benchmark.jl
##### Script to run a new benchmark on a previous training session
##### (requires training with option `save_intermediate=true`)
#####

using AlphaZero

include("games.jl")
GAME = get(ENV, "GAME", "connect-four")
SelectedGame = GAME_MODULE[GAME]
using .SelectedGame: Game, Training

SESSION_DIR = "sessions/$GAME"

# Example: run a network-only benchmark

make_duel(baseline) =
  Benchmark.Duel(
    Benchmark.NetworkOnly(τ=0.5, use_gpu=true),
    baseline,
    num_games=200,
    flip_probability=0.5,
    color_policy=CONTENDER_WHITE)

benchmark = make_duel.(Training.baselines)

AlphaZero.UserInterface.run_new_benchmark(
  Game, Training.Network{Game}, SESSION_DIR,
  "netonly", benchmark,
  params=Training.params, itcmax=nothing)


text:
module WavePropagation

    using FFTW
    using FastChebInterp
    using FastChebInterp: ChebPoly
    using Memoize
    using ChainRulesCore: ProjectTo, NoTangent
    import ChainRulesCore.rrule

    export planned_fft, planned_ifft
    export convolve, convolveT, convolve!, convolveT!
    export incident_field, greens
    export get_model

    include("planned_fft.jl")
    include("fields.jl")
    include("convolve.jl")
    include("scattering.jl")

end


text:
#! /usr/local/bin/julia
const srcpath = "$(Pkg.dir("Marble"))/src"

include("$srcpath/cli/cli_framework.jl")

using ArgParse
using CLIFramework
using Marble

"""
The baseic `mrbl` command.

    mrbl [options] [path]

Options:
    clean: whether to clean the latex build dir
    out <path>: what should the outfile be name (single file only)
    format <fmt>: Out format. Options are `tex` and `pdf`. `html` comming... eventually

Arguments:
    path: Which file or directory to build using marble
"""
commands = CommandBundle() do args
    s = ArgParseSettings()
    @add_arg_table s begin
        "--clean", "-c"
            help = "clean marble build dir befor building"
            action = :store_true
        "--out", "-o"
            help = "output name/path for finished file (single file only)"
            default = nothing
        "--format"
            help = "output format. `tex` or `pdf`. `html` comming... eventually"
            arg_type = Symbol
            default = :pdf
        "--watch", "-w"
            help = "watch for changes and update file build"
            action = :store_true
        "path"
            help = "File or directory to compile with Marble"
            default = pwd()
    end
    a = parse_args(args, s)
    path = ispath(a["path"]) ? a["path"] : error("$(a["path"]) is a not a valide path.")

    # Remove existing tex build files
    if a["clean"]
        Marble.clean_tex(path)
    end

    Marble.build_path(path; watch=a["watch"], fmt=a["format"], out=a["out"])
end

add!(commands, "init") do args
    if length(args) == 1
        init_dir(args[1])
    end
end

add!(commands, "stream") do args
    error("Improperly implemented. Try later")
    exit()
    global STDOUT
    global STDIN

    # Do some magic in hopes of silencing println output
    sout = STDOUT
    STDOUT = IOBuffer()

    # Do the build
    build_stream(STDIN, sout; fmt=:tex)

    # Print out any error messages that happened
    seek(STDOUT, 0)
    println(sout, readstring(STDOUT))
end

# Run that darn script
dispatch(commands)


text:
using Test
using AeroAcoustics
using DSP

oct1 = [(11,22),
    (22,44),
    (44,88),
    (88,177),
    (177,355),
    (355,710),
    (710,1420),
    (1420,2840),
    (2840,5680),
    (5680,11360),
    (11360,22720)]

@testset "SPL:" begin
    @test isnan(SPL(-1.0))
    @test isnan(SPL(NaN))
    @test isnan(SPL(missing))
    @test SPL(1.0) ≈ 94 rtol=1e-3
    @test SPL.([1,4]) ≈ [94,100] rtol=1e-3
end

@testset "octavebands:" begin
    # Test 1/1 octavebands (using oct1 table above)
    @test all(isapprox.(oct1[7],octavebandlimits(1000,1);rtol=0.05))
    fc = AeroAcoustics.octavebands_nomial(1)
    d = octavebandlimits(fc,1)
    dt = collect(zip(d...))
    dtInt = map(x->round.(Int,x), dt)
    for i in 1:length(oct1)
        @test all(isapprox.(oct1[i],dtInt[i];rtol=0.05))
    end
end

@testset "narrow2oct:" begin
    n = 100
    fc = octavebands(1;nomial=true)
    x = FreqArray(ones(n,length(fc)),fc)
    @test narrow2oct(x,1).arr == ones(n,length(fc))
    @test narrow2oct(x,1).fc == fc
end

@testset "point_to_region:" begin
    pt = (0.0,0.0)
    @test AeroAcoustics.point_to_region(pt,(1.0,2.0)) == [-0.5,0.5,-1.0,1.0]
    @test AeroAcoustics.point_to_region(pt,(1.0,)) == [-0.5,0.5,-0.5,0.5]
    @test AeroAcoustics.point_to_region(pt,1.0) == [-0.5,0.5,-0.5,0.5]
    @test AeroAcoustics.point_to_region(pt,1) == [-0.5,0.5,-0.5,0.5]

    a = -[1.25,0.75,1.25,0.75]
    b = [0.75,1.25,0.75,1.25]

    s = [[-1.0,-1.0],[1.0,1.0]]
    dxdy = (0.5,0.5)
    @test AeroAcoustics.point_to_region(s,dxdy) == [a,b]

    s = [(-1.0,-1.0),(1.0,1.0)]
    dxdy = 0.5
    @test AeroAcoustics.point_to_region(s,dxdy) == [a,b]

    # TODO: This fails due to point_to_region(s::NTuple{2},...)
    #s = ((-1.0,-1.0),(1.0,1.0))
    #dxdy = 0.5
    #@which AeroAcoustics.point_to_region(s,dxdy) == [a,b]

    s = [(-1.0,-1.0),(1.0,1.0)]
    dxdy = (0.5,)
    @test AeroAcoustics.point_to_region(s,dxdy) == [a,b]

    s = [(-1.0,-1.0),(1.0,1.0)]
    dxdy = (0.5,0.5)
    @test AeroAcoustics.point_to_region(s,dxdy) == [a,b]
end

@testset "ENBW:" begin
    fs = 2^16
    n = 1024
    win = DSP.rect(n)
    @test enbw(fs,win) == fs/n
end


text:
module Schedules

using  Cascadia: nodeText, Selector, @sel_str
using  CSV
using  DataFrames
using  Dates: Date, ENGLISH
using  Gumbo: parsehtml
using  HTTP
using  JSON
using  Logging
using  Scratch: @get_scratch!
using  URIs

using  NFLTables: POST, PRE, REG, SeasonPart

const SEASONS = tuple(2010:2021...)

function extractdate(datestring::AbstractString, season::Integer)
    if datestring == "Games not yet scheduled"
        return missing
    end
    monthday = split(datestring, ", ")[2]
    month, day = split(monthday)
    monthnum = findfirst(x -> x == month, ENGLISH.months)
    daynum = parse(Int, match(r"^[0-9]+", day).match)
    if monthnum > 7
        return Date(season, monthnum, daynum)
    else
        return Date(season+oneunit(season), monthnum, daynum)
    end
end

function extractschedule(content::AbstractString, season::Integer, seasonpart::SeasonPart, week::Integer)

    df = DataFrame(
        season = Int[],
        seasonpart = SeasonPart[],
        week = Int[],
        date = Union{Date,Missing}[],
        state = Union{String,Missing}[],
        home = String[],
        away = String[],
        # site = String[],
        # gameid = Int[],
        # gc_url = String[],
        # gametime = Union{String,Missing}[],
        homescore = Union{Int,Missing}[],
        awayscore = Union{Int,Missing}[],
    )

    htmlcontent = parsehtml(content)

    for date_element in eachmatch(sel"section.nfl-o-matchup-group", htmlcontent.root)
        datetext = nodeText(eachmatch(sel"h2.d3-o-section-title", date_element)[1])
        date = try
            extractdate(datetext, season)
        catch e
            @error "Unable to extract date" datetext
            rethrow()
        end

        for game_element in eachmatch(sel"div.nfl-c-matchup-strip", date_element)
            state_elements = eachmatch(sel"p.nfl-c-matchup-strip__period", game_element)
            state = if isempty(state_elements)
                missing
            else
                state = nodeText(state_elements[1])
            end
            away_element, home_element = eachmatch(sel"div.nfl-c-matchup-strip__team", game_element)  # two teams, away@home_score
            away = strip(nodeText(eachmatch(sel"span.nfl-c-matchup-strip__team-abbreviation", away_element)[1]))

            awayscore_elements = eachmatch(sel"div.nfl-c-matchup-strip__team-score", away_element)
            awayscore = if isempty(awayscore_elements)
                missing
            else
                attrib = awayscore_elements[1].attributes
                if haskey(attrib, "data-score")
                    parse(Int, attrib["data-score"])
                else
                    missing
                end
            end

            home = strip(nodeText(eachmatch(sel"span.nfl-c-matchup-strip__team-abbreviation", home_element)[1]))
            homescore_elements = eachmatch(sel"div.nfl-c-matchup-strip__team-score", home_element)
            homescore = if isempty(homescore_elements)
                missing
            else
                attrib = homescore_elements[1].attributes
                if haskey(attrib, "data-score")
                    parse(Int, attrib["data-score"])
                else
                    missing
                end
            end

            push!(df, Dict(
                :season => season,
                :seasonpart => seasonpart,
                :week => week,
                :date => date,
                :state => state,
                :home => home,
                :away => away,
                # :site => d.attributes["data-site"],
                # :gameid => parse(Int, d.attributes["data-gameid"]),
                # :gc_url => d.attributes["data-gc-url"],
                # :gametime => gametime,
                :homescore => homescore,
                :awayscore => awayscore,
            ))
        end
    end
    return df
end

const REQUEST = Dict{String, Any}(
    "Name"   => "Schedules",
    "Module" => Dict{String, Any}(
        "PreSeasonPlacement"=>0,
        "PostSeasonPlacement"=>0,
        "SeasonType"=>"REG1",
        "seasonFromUrl"=>2018,
        "HeaderCountryCode"=>"US",
        "RegularSeasonPlacement"=>0,
        "WeekFromUrl"=>5,
        "TimeZoneID"=>"America/New_York",
    ),
)

function requesturi(season::Integer, part::SeasonPart, week::Integer)
    rd = Dict(REQUEST)
    rd["Module"]["SeasonType"] = "$(string(part))$week"
    rd["Module"]["seasonFromUrl"] = season
    query = JSON.json(rd)
    uri = URI(
        scheme="https",
        host="www.nfl.com",
        path="/api/lazy/load",
        query="json=$(string(query))",
    )
    return uri
end

function rawdownload(uri::URI)
    r = HTTP.get(string(uri));
    r.status == 200 || error("Unable to get site (status: $(r.status))")
    return String(r.body)
end

function seasonweeks(season::Integer)
    season in SEASONS || error("Invalid season: $season")
    rtn = Tuple{SeasonPart,Any}[]
    for i in 0:4
        push!(rtn, (PRE, i))
    end
    for i in 1:17
        push!(rtn, (REG, i))
    end
    if season != 2021
        for i in 1:4
            push!(rtn, (POST, i))
        end
    end
    return rtn
end

function getfilepath(season::Integer)
    season in SEASONS || error("invalid season $season")
    return "$(season).csv"
end

"""
    schedule(season::Integer)

Obtain the NFL schedule for `season`

# Examples
```jldoctest
julia> df = Schedules.schedule(2018);

julia> show(first(df[:, [:date, :home, :away, :homescore, :awayscore]], 5), eltypes=false)
5×5 DataFrame
 Row │ date        home  away  homescore  awayscore
─────┼──────────────────────────────────────────────
   1 │ 2018-08-02  BAL   CHI          17         16
   2 │ 2018-08-09  BAL   LAR          33          7
   3 │ 2018-08-09  BUF   CAR          23         28
   4 │ 2018-08-09  CIN   CHI          30         27
   5 │ 2018-08-09  GB    TEN          31         17

```
"""
function schedule(season::Integer; redownload=false)
    dirpath = @get_scratch!("schedules")
    localpath = joinpath(dirpath, getfilepath(season))
    return if redownload || !isfile(localpath)
        @info "Downloading data for $season and writing to $localpath"
        dataframes = []
        for (part, week) in seasonweeks(season)
            content = rawdownload(requesturi(season, part, week))
            part_df = try
                extractschedule(content, season, part ,week)
            catch
                @error "Unable to extract a schedule" season part week
                rethrow()
            end
            push!(dataframes, part_df)
        end
        df = vcat(dataframes...)
        sort!(df, [:seasonpart, :week, :date, :home])  # for consistency
        CSV.write(localpath, df)
        df
    else
        DataFrame(CSV.File(localpath, missingstring="NA"))
    end
end

end  # module Schedules

nflschedule = Schedules.schedule


text:
# Function to normalize values
function normalize(vec)
    [(x - minimum(vec))/(maximum(vec) - minimum(vec)) for x in vec]
end


text:
xs = collect(1:12)
ys = xs + rand(12)

titles = ["Euler", "Modified_Euler", "Mid_Point_Euler", "Runga_Kutta_4"]

plot(
  layer(x=xs, y=ys, Geom.line,
    Theme(default_color=colorant"darkred")),
  layer(x=xs, y=ys+1, Geom.line,
    Theme(default_color=colorant"red")),
  layer(x=xs, y=ys+2, Geom.line,
    Theme(default_color=colorant"darkblue")),
  layer(x=xs, y=ys+3, Geom.line,
    Theme(default_color=colorant"darkgreen")),
  Guide.xlabel("x", orientation=:horizontal),
  Guide.ylabel("y", orientation=:vertical),
  Guide.title("Four ODE one-step methods on y'=(x + y)/x"))

plot(
  layer(x=xs, y=ys-0.5, Geom.line,
    color=repeat([symbol(titles[1])], inner=[1])),
  layer(x=xs, y=ys-1, Geom.line,
    color=repeat([symbol(titles[2])], inner=[2])),
  layer(x=xs, y=ys-2, Geom.line,
    color=repeat([symbol(titles[3])], inner=[3])),
  layer(x=xs, y=ys-3, Geom.line,
    color=repeat([symbol(titles[4])], inner=[4])),
  Scale.color_discrete_manual("darkred", "red","green","blue"),
  Guide.colorkey("Legend"),
  Guide.xlabel("x", orientation=:horizontal),
  Guide.ylabel("y", orientation=:vertical),
  Guide.title("Four ODE one-step methods on y'=(x + y)/x"))


text:
using Manifolds, Manopt, Test, ManifoldsBase

@testset "Differentials (on Sphere(2))" begin
    # The Adjoint Differentials test using the same variables as the differentials
    # test
    p = [1.0, 0.0, 0.0]
    q = [0.0, 1.0, 0.0]
    M = Sphere(2)
    X = log(M, p, q)
    # Tept differentials (1) Dp of Log_pq
    Y = similar(X)
    @test adjoint_differential_log_basepoint(M, p, p, X) == -X
    adjoint_differential_log_basepoint!(M, Y, p, p, X)
    @test Y == -X
    @test adjoint_differential_log_argument(M, p, p, X) == X
    adjoint_differential_log_argument!(M, Y, p, p, X)
    @test Y == X
    @test adjoint_differential_exp_basepoint(M, p, zero_vector(M, p), X) == X
    adjoint_differential_exp_basepoint!(M, Y, p, zero_vector(M, p), X)
    @test Y == X
    @test adjoint_differential_exp_argument(M, p, zero_vector(M, p), X) == X
    adjoint_differential_exp_argument!(M, Y, p, zero_vector(M, p), X)
    @test Y == X
    for t in [0, 0.15, 0.33, 0.66, 0.9]
        @test adjoint_differential_geodesic_startpoint(M, p, p, t, X) == (1 - t) * X
        adjoint_differential_geodesic_startpoint!(M, Y, p, p, t, X)
        @test Y == (1 - t) * X
        @test norm(M, p, adjoint_differential_geodesic_endpoint(M, p, p, t, X) - t * X) ≈ 0 atol =
            10.0^(-16)
        adjoint_differential_geodesic_endpoint!(M, Y, p, p, t, X)
        @test norm(M, p, Y - t * X) ≈ 0 atol = 10.0^(-16)
    end
    Mp = PowerManifold(M, NestedPowerRepresentation(), 3)
    pP = [p, q, p]
    qP = [p, p, q]
    XP = [X, zero_vector(M, p), -X]
    YP = similar.(XP)
    @test norm(
        Mp,
        pP,
        adjoint_differential_forward_logs(Mp, pP, XP) -
        [-X, zero_vector(M, p), zero_vector(M, p)],
    ) ≈ 0 atol = 4 * 10.0^(-16)
    adjoint_differential_forward_logs!(Mp, YP, pP, XP)
    @test norm(Mp, pP, YP - [-X, zero_vector(M, p), zero_vector(M, p)]) ≈ 0 atol =
        4 * 10.0^(-16)
    ZP = [[0.0, π / 2, 0.0], [0.0, 0.0, 0.0], [π / 2, 0.0, 0.0]]
    @test adjoint_differential_log_argument(Mp, pP, qP, XP) == ZP
    adjoint_differential_log_argument!(Mp, YP, pP, qP, XP)
    @test ZP == YP
end


text:
# Copyright 2022 RelationalAI, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License

# There are several ways to display transaction results in a friendly way:
#
#     show_result(rsp)
#
# .. which is equivalent to:
#
#     show(TransactionResult(rsp))
#
# .. or, if you want to navigate the relations explicitly:
#
#     for relation in result.relations
#         show(relation)
#     end

using RAI: Context, HTTPError, exec, load_config, show_result

include("parseargs.jl")

const source = """
def output =
    :drink, "martini", 2, 12.50, "2020-01-01";
    :drink, "sazerac", 4, 14.25, "2020-02-02";
    :drink, "cosmopolitan", 4, 11.00, "2020-03-03";
    :drink, "bellini", 3, 12.25, "2020-04-04"
"""

function run(database, engine; profile)
    conf = load_config(; profile = profile)
    ctx = Context(conf)
    rsp = exec_v1(ctx, database, engine, source)
    show_result(rsp)
end

function main()
    args = parseargs(
        "database", Dict(:help => "database name", :required => true),
        "engine", Dict(:help => "engine name", :required => true),
        "--profile", Dict(:help => "config profile (default: default)"))
    try
        run(args.database, args.engine; profile = args.profile)
    catch e
        e isa HTTPError ? show(e) : rethrow()
    end
end

main()

max_stars_repo_name: RelationalAI/rai-sdk-julia

max_issues_repo_name: RelationalAI/rai-sdk-julia

max_forks_repo_name: RelationalAI/rai-sdk-julia


text:
## UTF-16 tests

let u8 = "\U10ffff\U1d565\U1d7f6\U00066\U2008a",
    u16 = utf16(u8)

    @test sizeof(u16) == 18
    @test length(u16) == 5
    #@test utf8(u16) == u8
    @test collect(u8) == collect(u16)
    #@test_throws UnicodeError utf16(utf32(Char(0x120000)))
    @test_throws UnicodeError utf16(UInt8[1,2,3])

    @test convert(UTF16Str, "test") == "test"
    @test convert(UTF16Str, u16) == u16

    # Do we really need to convert Arrays that aren't Vectors?
    #@test convert(UTF16Str, UInt16[[0x65, 0x66] [0x67, 0x68]]) == "efgh"
    #@test convert(UTF16Str, Int16[[0x65, 0x66] [0x67, 0x68]]) == "efgh"

    @test map(lowercase, utf16("TEST\U1f596")) == "test\U1f596"
    #@test typeof(Base.unsafe_convert(Ptr{UInt16}, utf16("test"))) == Ptr{UInt16}
end


text:
include("op-codes.jl")

"""
    Script

Script data type has a single `data` field, stored as `Vector{Vector{UInt8}}`
in which each inner `Vector{UInt8}` represent a stack item. It represents
either a PubKey or Signature script.

A script included in outputs which sets the conditions that must be fulfilled
for those satoshis to be spent. Data for fulfilling the conditions can be
provided in a signature script. Pubkey Scripts are called a scriptPubKey in code.

Data generated by a spender which is almost always used as variables to satisfy
a pubkey script. Signature Scripts are called scriptSig in code.
"""
struct Script
    data::Vector{Vector{UInt8}}
end

Script() = Script(Vector{UInt8}[])

function Base.show(io::IO, z::Script)
    for item in z.data
        if length(item) == 1
            if haskey(OP_CODE_NAMES, item[1])
                println(io, OP_CODE_NAMES[item[1]])
            else
                println(io, string("OP_CODE_", Int(item[1])))
            end
        else
            println(io, bytes2hex(item))
        end
    end
end

"""
    Script(::IOBuffer) -> Script

Parse a `Script` from an IOBuffer
"""
function Script(io::IOBuffer)
    length_ = CompactSizeUInt(io).value
    data = Vector{UInt8}[]
    count = 0
    while count < length_
        count += 1
        current_byte = read(io, UInt8)
        if current_byte >= 0x01 && current_byte <= 0x4b
            n = current_byte
            push!(data, read(io, n))
            count += n
        elseif current_byte == 0x4c
            # op_pushdata1
            n = read(io, Int8)
            push!(data, read(io, n))
            count += n + 1
        elseif current_byte == 0x4d
            # op_pushdata2
            n = read(io, Int16)
            push!(data, read(io, n))
            count += n + 2
        else
            # op_code
            push!(data, [current_byte])
        end
    end
    @assert count == length_ "Parsing failed"
    return Script(data)
end

"""
    serialize(s::Script) -> Vector{UInt8}

Serialize a `Script` to a Vector{UInt8}
"""
function serialize(s::Script)
    result = UInt8[]
    for item in s.data
        length_ = length(item)
        if length_ == 1
            append!(result, item)
        else
            length_ = length(item)
            if length_ < 0x4b
                append!(result, UInt8(length_))
            elseif length_ > 0x4b && length_ < 0x100
                append!(result, 0x4c)
                append!(result, UInt8(length_))
            elseif length_ >= 0x100 && length_ <= 0x208
                append!(result, 0x4d)
                result += bytes(length_, len=2)
            else
                error("too long an item")
            end
            append!(result, item)
        end
    end
    total = CompactSizeUInt(length(result))
    prepend!(result, serialize(total))
    return result
end

"""
    script(::Vector{UInt8}; type::Symbol=:P2WSH) -> Script

Returns a `Script` of set type for given hash.
- `type` can be `:P2PKH`, `:P2SH`, `:P2WPKH` or `:P2WSH`
- hash must be 32 bytes long for P2WSH script, 20 for the others
"""
function script(bin::Vector{UInt8}; type::Symbol=:P2PKH)
    if type == :P2WSH
        @assert length(bin) == 32
        return Script([[0x00], bin])
    else
        @assert length(bin) == 20
        if type == :P2PKH
            return Script([[0x76], [0xa9], bin, [0x88], [0xac]])
        elseif type == :P2SH
            return Script([0xa9], bin, [0x87])
        elseif type == :P2WPKH
            return Script([[0x00], bin])
        end
    end
end

"""
    type(script::Script) -> Symbol

Return type of `Script` : `:P2PKH`, `:P2SH`, `:P2WPKH` or `:P2WSH`
"""
function type(script::Script)
    if is_p2pkh(script)
        return :P2PKH
    elseif is_p2sh(script)
        return :P2SH
    elseif is_p2wsh(script)
        return :P2WSH
    elseif is_p2wpkh(script)
        return :P2WPKH
    else
        return error("Unknown Script type")
    end
end

"""
Returns whether this follows the
OP_DUP OP_HASH160 <20 byte hash> OP_EQUALVERIFY OP_CHECKSIG pattern.
"""
function is_p2pkh(script::Script)
    return length(script.data) == 5 &&
        script.data[1] == [0x76] &&
        script.data[2] == [0xa9] &&
        typeof(script.data[3]) == Vector{UInt8} &&
        length(script.data[3]) == 20 &&
        script.data[4] == [0x88] &&
        script.data[5] == [0xac]
end

"""
Returns whether this follows the
OP_HASH160 <20 byte hash> OP_EQUAL pattern.
"""
function is_p2sh(script::Script)
    return length(script.data) == 3 &&
           script.data[1] == [0xa9] &&
           typeof(script.data[2]) == Vector{UInt8} &&
           length(script.data[2]) == 20 &&
           script.data[3] == [0x87]
end

function is_p2wpkh(script::Script)
    length(script.data) == 2 &&
    script.data[1] == [0x00] &&
    typeof(script.data[2]) == Vector{UInt8} &&
    length(script.data[2]) == 20
end

"""
Returns whether this follows the
OP_0 <20 byte hash> pattern.
"""
function is_p2wsh(script::Script)
    length(script.data) == 2 &&
    script.data[1] == [0x00] &&
    typeof(script.data[2]) == Vector{UInt8} &&
    length(script.data[2]) == 32

end

const H160_INDEX = Dict([
    ("P2PKH", 3),
    ("P2SH", 2)
])

"""
Returns the address corresponding to the script
"""
function script2address(script::Script, testnet::Bool)
    type = scripttype(script)
    h160 = script.data[H160_INDEX[type]]
    return h160_2_address(h160, testnet, type)
end


text:
export
    SpectralConv,
    FourierOperator

struct SpectralConv{P, N, T, S, F}
    weight::T
    in_channel::S
    out_channel::S
    modes::NTuple{N, S}
    σ::F
end

"""
    SpectralConv(
        ch, modes, σ=identity;
        init=c_glorot_uniform, permuted=false, T=ComplexF32
    )

## Arguments

* `ch`: Input and output channel size, e.g. `64=>64`.
* `modes`: The Fourier modes to be preserved.
* `σ`: Activation function.
* `permuted`: Whether the dim is permuted. If `permuted=true`, layer accepts
    data in the order of `(ch, ..., batch)`, otherwise the order is `(..., ch, batch)`.

## Example

```jldoctest
julia> SpectralConv(2=>5, (16, ))
SpectralConv(2 => 5, (16,), σ=identity, permuted=false)

julia> using Flux

julia> SpectralConv(2=>5, (16, ), relu)
SpectralConv(2 => 5, (16,), σ=relu, permuted=false)

julia> SpectralConv(2=>5, (16, ), relu, permuted=true)
SpectralConv(2 => 5, (16,), σ=relu, permuted=true)
```
"""
function SpectralConv(
    ch::Pair{S, S},
    modes::NTuple{N, S},
    σ=identity;
    init=c_glorot_uniform,
    permuted=false,
    T::DataType=ComplexF32
) where {S<:Integer, N}
    in_chs, out_chs = ch
    scale = one(T) / (in_chs * out_chs)
    weights = scale * init(out_chs, in_chs, prod(modes))
    W = typeof(weights)
    F = typeof(σ)

    return SpectralConv{permuted,N,W,S,F}(weights, in_chs, out_chs, modes, σ)
end

Flux.@functor SpectralConv

Base.ndims(::SpectralConv{P,N}) where {P,N} = N

permuted(::SpectralConv{P}) where {P} = P

function Base.show(io::IO, l::SpectralConv{P}) where {P}
    print(io, "SpectralConv($(l.in_channel) => $(l.out_channel), $(l.modes), σ=$(string(l.σ)), permuted=$P)")
end

function spectral_conv(m::SpectralConv, 𝐱::AbstractArray)
    n_dims = ndims(𝐱)

    𝐱_fft = fft(Zygote.hook(real, 𝐱), 1:ndims(m)) # [x, in_chs, batch]
    𝐱_flattened = reshape(view(𝐱_fft, map(d->1:d, m.modes)..., :, :), :, size(𝐱_fft, n_dims-1), size(𝐱_fft, n_dims))
    𝐱_weighted = apply_spectral_pattern(𝐱_flattened, m.weight) # [prod(m.modes), out_chs, batch], only 3-dims
    𝐱_shaped = reshape(𝐱_weighted, m.modes..., size(𝐱_weighted, 2), size(𝐱_weighted, 3))
    𝐱_padded = spectral_pad(𝐱_shaped, (size(𝐱_fft)[1:end-2]..., size(𝐱_weighted, 2), size(𝐱_weighted, 3))) # [x, out_chs, batch] <- [modes, out_chs, batch]
    𝐱_ifft = real(ifft(𝐱_padded, 1:ndims(m))) # [x, out_chs, batch]

    return m.σ.(𝐱_ifft)
end

function (m::SpectralConv{false})(𝐱)
    𝐱ᵀ = permutedims(𝐱, (ntuple(i->i+1, ndims(m))..., 1, ndims(m)+2)) # [x, in_chs, batch] <- [in_chs, x, batch]
    𝐱_out = spectral_conv(m, 𝐱ᵀ) # [x, out_chs, batch]
    𝐱_outᵀ = permutedims(𝐱_out, (ndims(m)+1, 1:ndims(m)..., ndims(m)+2)) # [out_chs, x, batch] <- [x, out_chs, batch]

    return 𝐱_outᵀ
end

function (m::SpectralConv{true})(𝐱)
    return spectral_conv(m, 𝐱) # [x, out_chs, batch]
end

############
# operator #
############

struct FourierOperator{L, C, F}
    linear::L
    conv::C
    σ::F
end

"""
    FourierOperator(ch, modes, σ=identity; permuted=false)

## Arguments

* `ch`: Input and output channel size for spectral convolution, e.g. `64=>64`.
* `modes`: The Fourier modes to be preserved for spectral convolution.
* `σ`: Activation function.
* `permuted`: Whether the dim is permuted. If `permuted=true`, layer accepts
    data in the order of `(ch, ..., batch)`, otherwise the order is `(..., ch, batch)`.

## Example

```jldoctest
julia> FourierOperator(2=>5, (16, ))
FourierOperator(2 => 5, (16,), σ=identity, permuted=false)

julia> using Flux

julia> FourierOperator(2=>5, (16, ), relu)
FourierOperator(2 => 5, (16,), σ=relu, permuted=false)

julia> FourierOperator(2=>5, (16, ), relu, permuted=true)
FourierOperator(2 => 5, (16,), σ=relu, permuted=true)
```
"""
function FourierOperator(
    ch::Pair{S, S},
    modes::NTuple{N, S},
    σ=identity;
    permuted=false
) where {S<:Integer, N}
    linear = permuted ? Conv(Tuple(ones(Int, length(modes))), ch) : Dense(ch.first, ch.second)
    conv = SpectralConv(ch, modes; permuted=permuted)

    return FourierOperator(linear, conv, σ)
end

Flux.@functor FourierOperator

function Base.show(io::IO, l::FourierOperator)
    print(io, "FourierOperator($(l.conv.in_channel) => $(l.conv.out_channel), $(l.conv.modes), σ=$(string(l.σ)), permuted=$(permuted(l.conv)))")
end

function (m::FourierOperator)(𝐱)
    return m.σ.(m.linear(𝐱) + m.conv(𝐱))
end

#########
# utils #
#########

c_glorot_uniform(dims...) = Flux.glorot_uniform(dims...) + Flux.glorot_uniform(dims...)*im

# [prod(modes), out_chs, batch] <- [prod(modes), in_chs, batch] * [out_chs, in_chs, prod(modes)]
apply_spectral_pattern(𝐱₁, 𝐱₂) = @tullio 𝐲[m, o, b] := 𝐱₁[m, i, b] * 𝐱₂[o, i, m]

spectral_pad(𝐱::AbstractArray, dims::NTuple) = spectral_pad!(similar(𝐱, dims), 𝐱)

function spectral_pad!(𝐱_padded::AbstractArray, 𝐱::AbstractArray)
    fill!(𝐱_padded, eltype(𝐱)(0)) # zeros(eltype(𝐱), dims)
    𝐱_padded[map(d->1:d, size(𝐱))...] .= 𝐱

    return 𝐱_padded
end

function ChainRulesCore.rrule(::typeof(spectral_pad), 𝐱::AbstractArray, dims::NTuple)
    function spectral_pad_pullback(𝐲̄)
        return NoTangent(), view(𝐲̄, map(d->1:d, size(𝐱))...), NoTangent()
    end

    return spectral_pad(𝐱, dims), spectral_pad_pullback
end


text:
#=
check a sufficient condition for pointwise membership of vector valued
polynomials in the epinormone/epinormeucl cone
=#

struct NormConePoly{T <: Real} <: ExampleInstanceJuMP{T}
    polys_name::Symbol
    deg::Int
    is_feas::Bool # whether model should be primal-dual feasible; only for testing
    use_L2::Bool # check for membership in epigraph of L2, otherwise use L1
end

function build(inst::NormConePoly{T}) where {T <: Float64}
    halfdeg = div(inst.deg + 1, 2)
    (U, pts, Ps) = PolyUtils.interpolate(
        PolyUtils.FreeDomain{T}(1), halfdeg)
    vals = normconepoly_data[inst.polys_name].(pts)
    l = length(vals[1])
    if inst.use_L2
        cone = Hypatia.WSOSInterpEpiNormEuclCone{T}(l, U, Ps)
    else
        cone = Hypatia.WSOSInterpEpiNormOneCone{T}(l, U, Ps)
    end

    model = JuMP.Model()
    JuMP.@constraint(model, [v[i] for i in 1:l for v in vals] in cone)

    return model
end

function test_extra(inst::NormConePoly{T}, model::JuMP.Model) where T
    stat = JuMP.termination_status(model)
    @test stat == (inst.is_feas ? MOI.OPTIMAL : MOI.INFEASIBLE)
end

normconepoly_data = Dict(
    :polys1 => (x -> [x^2 + 2, x]),
    :polys2 => (x -> [2x^2 + 2, x, x]),
    :polys3 => (x -> [x^2 + 2, x, x]),
    :polys4 => (x -> [2 * x^4 + 8 * x^2 + 4, x + 2 + (x + 1)^2, x]),
    :polys5 => (x -> [x, x^2 + x]),
    :polys6 => (x -> [x, x + 1]),
    :polys7 => (x -> [x^2, x]),
    :polys8 => (x -> [x + 2, x]),
    :polys9 => (x -> [x - 1, x, x]),
    )


text:
"""
    _colon(I, J)

`_colon(I, J)` works equivelently to `I:J`, it's used to backward support julia v"1.0".
"""
_colon(I, J) = I:J
if v"1.0" <= VERSION < v"1.1"
    _colon(I::CartesianIndex{N}, J::CartesianIndex{N}) where N =
        CartesianIndices(map((i,j) -> i:j, Tuple(I), Tuple(J)))
end


text:
module Enzyme

export autodiff
export Const, Active, Duplicated

using LLVM
using LLVM.Interop
using Libdl
using Cassette

include("utils.jl")
include("compiler.jl")

abstract type Annotation{T} end
struct Const{T} <: Annotation{T}
    val::T
end
struct Active{T<:AbstractFloat} <: Annotation{T}
    val::T
end
Active(i::Integer) = Active(float(i))
struct Duplicated{T} <: Annotation{T}
    val::T
    dval::T
end
struct DuplicatedNoNeed{T} <: Annotation{T}
    val::T
    dval::T
end

Base.eltype(::Type{<:Annotation{T}}) where T = T

struct LLVMThunk{RT}
    mod::LLVM.Module
    entry::LLVM.Function

    function LLVMThunk(f, tt; optimize=true, run_enzyme=true)
        primal_tt = map(eltype, tt)

        # CTX, f are ghosts
        overdub_tt = Tuple{typeof(Compiler.CTX), typeof(f), primal_tt...}
        rt = Core.Compiler.return_type(Cassette.overdub, overdub_tt)
        # can't return array since that's complicated.
        @assert rt<:Union{AbstractFloat, Nothing}

        name   = String(nameof(f))
        source = Compiler.FunctionSpec(Cassette.overdub, overdub_tt, #=kernel=# false, #=name=# name)
        target = Compiler.EnzymeTarget()
        params = Compiler.EnzymeCompilerParams()
        job    = Compiler.CompilerJob(target, source, params)

        # Codegen the primal function and all its dependency in one module
        mod, primalf = Compiler.codegen(:llvm, job, optimize=false, validate=false)

        # Do our own validation
        Compiler.check_ir(job, mod)

        # Now build the actual wrapper function
        ctx     = context(mod)
        rettype = convert(LLVMType, rt)

        params = parameters(primalf)
        adjoint_tt = LLVMType[]
        for (i, T) in enumerate(tt)
            llvmT = llvmtype(params[i])
            push!(adjoint_tt, llvmT)
            if T <: Duplicated
                push!(adjoint_tt, llvmT)
            end
        end

        # create a wrapper Function that we will inline into the llvmcall
        # generated by `call_function` in `autodiff`
        llvmf = LLVM.Function(mod, "enzyme_entry", LLVM.FunctionType(rettype, adjoint_tt))
        push!(function_attributes(llvmf), EnumAttribute("alwaysinline", 0, ctx))

        # Create the FunctionType and funtion decleration for the intrinsic
        pt       = LLVM.PointerType(LLVM.Int8Type(ctx))
        ftd      = LLVM.FunctionType(rettype, LLVMType[pt], true)
        autodiff = LLVM.Function(mod, string("__enzyme_autodiff.", rt), ftd)

        params = LLVM.Value[]
        llvm_params = parameters(llvmf)
        i = 1
        for T in tt
            if T <: Const
                push!(params, MDString("diffe_const"))
            elseif T <: Active
                push!(params, MDString("diffe_out"))
            elseif T <: Duplicated
                push!(params, MDString("diffe_dup"))
                push!(params, llvm_params[i])
                i += 1
            elseif T <: DuplicatedNoNeed
                push!(params, MDString("diffe_dupnoneed"))
                push!(params, llvm_params[i])
                i += 1
            else
                @assert("illegal annotation type")
            end
            push!(params, llvm_params[i])
            i += 1
        end

        Builder(ctx) do builder
            entry = BasicBlock(llvmf, "entry", ctx)
            position!(builder, entry)

            tc = bitcast!(builder, primalf,  pt)
            pushfirst!(params, tc)

            val = call!(builder, autodiff, params)

            ret!(builder, val)
        end

        LLVM.strip_debuginfo!(mod)
        if optimize
            # Run pipeline and Enzyme pass
            Compiler.optimize!(mod, llvmf, run_enzyme=run_enzyme)
        end

        new{rt}(mod, llvmf)
    end
end
return_type(::LLVMThunk{RT}) where RT = RT

struct Thunk{Ptr, RT, TT}
    function Thunk(f, tt)
        thunk = LLVMThunk(f, tt)
        triple = LLVM.triple()
        target = LLVM.Target(triple)
        objfile = tempname()
        libfile = tempname()
        TargetMachine(target, triple, "", "", LLVM.API.LLVMCodeGenLevelDefault, LLVM.API.LLVMRelocPIC) do tm
            LLVM.emit(tm, thunk.mod, LLVM.API.LLVMObjectFile, objfile)
        end

        run(`ld -shared $objfile -o $libfile`)
        libptr = Libdl.dlopen(libfile, Libdl.RTLD_LOCAL)
        ptr = Libdl.dlsym(libptr, :enzyme_entry)

        return new{ptr, return_type(thunk), Tuple{tt...}}()
    end
end

# https://github.com/JuliaGPU/GPUCompiler.jl/issues/3
# We are also re-running Julia's optimization pipeline again
# @generated function (thunk::Thunk{F, RT, TT, LLVMF})(args...) where {F, RT, TT, LLVMF}
#     _args = (:(args[$i]) for i in 1:length(args))
#     call_function(LLVMF, Float64, Tuple{args...}, Expr(:tuple, _args...))
# end

# Now this is just getting worse...
@generated function (thunk::Thunk{Ptr, RT})(args...) where {Ptr, RT}
    _args = (:(args[$i]) for i in 1:length(args))
    quote
        ccall($Ptr, $RT, ($(args...),), $(_args...))
    end
end

function enzyme_code_llvm(io::IO, @nospecialize(func), @nospecialize(types);
                   optimize::Bool=true, run_enzyme::Bool=true, raw::Bool=false,
                   debuginfo::Symbol=:default, dump_module::Bool=false)
    thunk = LLVMThunk(func, types, optimize=optimize, run_enzyme=run_enzyme)

    str = ccall(:jl_dump_function_ir, Ref{String},
                (Ptr{Cvoid}, Bool, Bool, Ptr{UInt8}),
                LLVM.ref(thunk.entry), !raw, dump_module, debuginfo)
    print(io, str)
end
enzyme_code_llvm(@nospecialize(func), @nospecialize(types); kwargs...) = enzyme_code_llvm(stdout, func, types; kwargs...)

annotate() = ()
annotate(arg::Annotation, args...) = (arg, annotate(args...)...)
annotate(arg, args...) = (Const(arg), annotate(args...)...)

prepare_cc() = ()
prepare_cc(arg::Duplicated, args...) = (arg.val, arg.dval, prepare_cc(args...)...)
prepare_cc(arg::DuplicatedNoNeed, args...) = (arg.val, arg.dval, prepare_cc(args...)...)
prepare_cc(arg::Annotation, args...) = (arg.val, prepare_cc(args...)...)

function autodiff(f, args...)
    args′ =  annotate(args...)
    thunk = Thunk(f, map(Core.Typeof, args′))
    thunk(prepare_cc(args′...)...)
end

import .Compiler: EnzymeCtx
# Ops that have intrinsics
for op in (sin, cos, exp)
    for (T, suffix) in ((Float32, "f32"), (Float64, "f64"))
        llvmf = "llvm.$(nameof(op)).$suffix"
        @eval begin
            @inline function Cassette.overdub(::EnzymeCtx, ::typeof($op), x::$T)
                ccall($llvmf, llvmcall, $T, ($T,), x)
            end
        end
    end
end

for op in (asin,)
    for (T, llvm_t) in ((Float32, "float"), (Float64, "double"))
        decl = "declare double @$(nameof(op))($llvm_t)"
        func = """
               %val = call $llvm_t @asin($llvm_t %0)
               ret $llvm_t %val
               """
       @eval begin
            @inline function Cassette.overdub(::EnzymeCtx, ::typeof($op), x::$T)
                Base.llvmcall(($decl,$func), $T, Tuple{$T}, x)
            end
        end
    end
end

@inline function pack(args...)
    ntuple(Val(length(args))) do i
        Base.@_inline_meta
        arg = args[i]
        @assert arg isa AbstractFloat
        return Duplicated(Ref(args[i]), Ref(zero(args[i])))
    end
end

@inline unpack() = ()
@inline unpack(arg) = (arg[],)
@inline unpack(arg, args...) = (arg[], unpack(args...)...)

@inline ∇unpack() = ()
@inline ∇unpack(arg::Duplicated) = (arg.dval[],)
@inline ∇unpack(arg::Duplicated, args...) = (arg.dval[], ∇unpack(args...)...)

function gradient(f, args...)
    ∇args = pack(args...)
    f′ = function (args...)
        Base.@_inline_meta
        f(unpack(args...)...)
    end
    autodiff(f′, ∇args...)
    return ∇unpack(∇args...)
end

function pullback(f, args...)
    return (c) -> begin
        ∇vals = gradient(f, args...)
        return ntuple(Val(length(∇vals))) do i
            Base.@_inline_meta
            return c*∇vals[i]
        end
    end
end

# WIP
# @inline Cassette.overdub(::EnzymeCtx, ::typeof(asin), x::Float64) = ccall(:asin, Float64, (Float64,), x)
# @inline Cassette.overdub(::EnzymeCtx, ::typeof(asin), x::Float32) = ccall(:asin, Float32, (Float32,), x)
end # module


text:
using GIS

include("jenks.jl")

MApop = open_raster("mapop300.tif")

# Use the Jenks algorithm to create classes
jenks_classes = jenks(vec(copy(MApop.data)[MApop.data.>0]),5)

# Find which class each point belongs to
MApopbinned = zeros(Int,size(MApop.data))

for i = 1:5
    MApopbinned += i*(jenks_classes[i].<=MApop.data.<jenks_classes[i+1])
end

# Deal with the maximum which isn't assigned to any bin by the former procedure
index = findmax(MApop.data)[2]
MApopbinned[index] = 5

# Create raster of the binned data

MApopbinnedraster = Raster(C_NULL,MApop.width,MApop.height,MApop.transform,MApop.projection,MApopbinned)

# Display raster

display(MApopbinnedraster)


text:
using MPI
using MPIAsyncPools
using Test

MPI.Init()
const comm = MPI.COMM_WORLD
const root = 0
const rank = MPI.Comm_rank(comm)
const nworkers = MPI.Comm_size(comm) - 1
isroot() = MPI.Comm_rank(comm) == root
const count = 1
const tag = 0

if isroot()
    pool = MPIAsyncPool(nworkers)
    sendbuf = repeat([3.14], count)
    isendbuf = zeros(eltype(sendbuf), nworkers*length(sendbuf))
    recvbuf = Vector{Float64}(undef, nworkers)
    irecvbuf = copy(recvbuf)
    nwait = nworkers
    repochs = asyncmap!(pool, sendbuf, recvbuf, isendbuf, irecvbuf, comm; nwait, tag)
    @test recvbuf ≈ collect(1:nworkers)
else
    recvbuf = Vector{Float64}(undef, 1)
    sendbuf = Vector{Float64}(undef, 1)
    sendbuf[1] = rank

    rreq = MPI.Irecv!(recvbuf, root, tag, comm)
    MPI.Wait!(rreq)
    @test recvbuf ≈ repeat([3.14], count)
    sreq = MPI.Isend(sendbuf, root, tag, comm)
    MPI.Wait!(sreq)
end

MPI.Barrier(comm)


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

using Markdown
using InteractiveUtils

# ╔═╡ 408d0ede-4de5-11eb-17d8-d7eaadf05d13
md"""
# String-Matching Algorithms

Locate strings (pattern) on a larger string or text. Sometimes called searching for a "needle in a haystack" problem.

A basic example is when the pattern and the searched text are arrays of elements of an alphabet (finite set) Σ. Σ may be a human language alphabet, for example, the letters A through Z and other applications may use a binary alphabet (Σ = {0,1}) or a DNA alphabet (Σ = {A,C,G,T}) in bioinformatics.

"""

# ╔═╡ 707faa44-4de6-11eb-01d8-5f7a5d15e4cc
md"""
## Types of Search Algorithms
- Single-pattern algorithms
- Finite-state-automaton-based search
- Stubs
- Index Methods
- ...
"""

# ╔═╡ daf10bc8-4de6-11eb-2862-c5e1729b95c7
md"""
## [Regular Expression](https://docs.julialang.org/en/v1/manual/strings/#Regular-Expressions): string pattern for search

"""

# ╔═╡ Cell order:
# ╟─408d0ede-4de5-11eb-17d8-d7eaadf05d13
# ╠═707faa44-4de6-11eb-01d8-5f7a5d15e4cc
# ╠═daf10bc8-4de6-11eb-2862-c5e1729b95c7