text:
include("data.jl")
include("model.jl")
include("train.jl")
include("exp.jl")

function CIFAR10_pretrain_fn(m)
    ds_fn = () -> load_CIFAR10_ds(batch_size=128)
    function train_fn(model)
        ds, test_ds = ds_fn();
        x, y = next_batch!(ds) |> gpu;
        model(x)

        # FIXME record the pretrain as well?
        # FIXME I might want to print the test acc at the end
        #
        # I actually want to record the pretrain process
        # TODO test this
        #
        # FIXME Remove if exist?
        logger = TBLogger("tensorboard_logs/$expID/train", tb_append, min_level=Logging.Info)
        test_logger = TBLogger("tensorboard_logs/$expID/test", tb_append, min_level=Logging.Info)
        test_cb = create_test_cb(model, test_ds,
                                 logger=test_logger,
                                 test_per_steps=50,
                                 test_run_steps=4)
        opt = ADAM(1e-3);
        train!(model, opt, ds,
               train_steps=2000, print_steps=20,
               logger=logger, test_cb=test_cb)
        opt = ADAM(1e-4);
        train!(model, opt, ds,
               train_steps=4000, from_steps=2000, print_steps=20,
               logger=logger, test_cb=test_cb)
    end
    maybe_train("trained/pretrain-CIFAR10.bson", m, train_fn)
end

function CIFAR10_pretrained_fn()
    # test WRN(28,10) on 2080 Ti
    model_fn = () -> WRN(16,4)
    m = model_fn()
    CIFAR10_pretrain_fn(m)
end

function CIFAR10_AE_pretrain_fn(m)
    ds_fn = () -> load_CIFAR10_ds(batch_size=50)
    function train_fn(model)
        ds, test_ds = ds_fn();
        x, y = next_batch!(ds) |> gpu;
        model(x)

        opt = ADAM(1e-3);
        aetrain!(model, opt, ds, train_steps=5000, print_steps=100)
    end
    maybe_train("trained/pretrain-CIFAR10-ae.bson", m, train_fn)
end

function CIFAR10_exp_helper(expID, lr, total_steps, λ; pretrain=false)
    expID = "CIFAR10/" * expID
    @show expID

    model_fn = () -> WRN(16,4)
    ds_fn = () -> load_CIFAR10_ds(batch_size=128)

    adv_exp_helper(expID, lr, total_steps, λ,
                   model_fn, ds_fn,
                   if pretrain CIFAR10_pretrain_fn else (a)->a end,
                   print_steps=2, save_steps=20,
                   test_per_steps=20, test_run_steps=2,
                   attack_fn=attack_CIFAR10_PGD_k(7))
end

function CIFAR10_free_exp_helper(expID, lr, total_steps)
    expID = "CIFAR10-free/" * expID
    @show expID

    model_fn = () -> WRN(16,4)
    ds_fn = () -> load_CIFAR10_ds(batch_size=50)
    free_exp_helper(expID, lr, total_steps, 8/255,
                    model_fn, ds_fn,
                    (a)->a,
                    print_steps=2, save_steps=20,
                    test_per_steps=20, test_run_steps=2,
                    test_attack_fn=attack_CIFAR10_PGD_k(7))
end

function exp_warmup()
    expID="warmup/test-$(now())"
    CIFAR10_exp_helper(expID, 1e-3, 1, 0)
end

function exp_free()
    # TODO add schedule
    CIFAR10_free_exp_helper("test-$(now())", 1e-3, 5000)
end

function CIFAR10_dyattack_exp_helper(expID, lr, attack_fn, total_steps; pretrain)
    # This put log and saved model into MNIST subfolder
    if pretrain
        expID = "CIFAR10-dyattack-pretrain/" * expID
    else
        expID = "CIFAR10-dyattack/" * expID
    end

    model_fn = () -> WRN(16,4)
    ds_fn = () -> load_CIFAR10_ds(batch_size=50)

    adv_exp_helper(expID, lr, total_steps, 0,
                   model_fn, ds_fn,
                   if pretrain CIFAR10_pretrain_fn else (a)->a end,
                   print_steps=2, save_steps=20,
                   test_per_steps=20, test_run_steps=2,
                   attack_fn=attack_fn,
                   test_attack_fn=attack_CIFAR10_PGD_k(7))
end

function exp_dyattack(schedule; pretrain=false)
    # TODO IMPORTANT I'll definitely need to (HEBI: record the training time)
    expID = replace("$schedule", " "=>"")
    @show expID
    for m in schedule
        @show m
        k = m[1][1]
        lr = m[1][2]
        steps = m[2]
        # TODO not only k, but (HEBI: also ε and η)
        CIFAR10_dyattack_exp_helper(expID, lr,
                                    attack_CIFAR10_PGD_k(k),
                                    steps,
                                    pretrain=pretrain)
    end
end

function exp_dymix(expID)
    expID = replace("$schedule", " "=>"")
    @show expID
    for m in schedule
        @show m
        steps = m[2]
        λ = m[1]
        # FIXME when λ is large, reduce lr
        CIFAR10_exp_helper(expID, 1e-3, steps, λ)
    end
end

function exp_lrdecay(schedule)
    expID = replace("$schedule", " "=>"")
    @show expID
    for m in schedule
        lr = m[1]
        steps = m[2]
        CIFAR10_exp_helper("itadv-schedule/"*expID, lr, steps, 0)
    end
end

function tmp()
    exp_warmup()
    # dyattack
    exp_dyattack((0=>200, 1=>400, 2=>600, 3=>800, 4=>1000, 5=>1200, 6=>1400, 7=>2000))
    exp_dyattack((0=>200, 1=>400, 2=>600, 3=>800, 4=>1000, 5=>1200, 6=>1400, 7=>2000), pretrain=true)
    exp_dyattack((1=>500, 2=>1000, 3=>1500, 4=>2000, 5=>2500, 6=>3000, 7=>4000), pretrain=true)
    exp_dyattack((1=>1000, 2=>2000, 3=>3000, 4=>4000, 5=>5000, 6=>6000, 7=>7000), pretrain=true)
    # dymix
    exp_dymix((5=>1000, 4=>2000, 3=>3000, 2=>4000, 1=>5000, 0=>6000))
    exp_dyattack((0=>2000,
                  1=>3000,
                  2=>4000,
                  3=>5000,
                  4=>6000,
                  7=>8000))
    # TODO (HEBI: add lr schedule)
    exp_dyattack(((1,1e-3)=>1000,
                  (1,2e-4)=>2000,
                  (2,1e-3)=>3000,
                  (2,2e-4)=>4000,
                  (3,1e-3)=>5000,
                  (3,2e-4)=>6000,
                  (4,1e-3)=>7000,
                  (4,2e-4)=>8000,
                  (7,1e-3)=>12000,
                  (7,2e-4)=>16000),
                 pretrain=true)
    # itadv baseline
    #
    # CIFAR10_exp_helper("baseline:itadv-1e-3", 1e-3, 2000, 0)
    # CIFAR10_exp_helper("baseline:f1-1e-3", 1e-3, 2000, 1)
    #
    # Maybe reuse the first part?
    exp_lrdecay((1e-3=>2000, 2e-4=>3000, 5e-5=>4000))

    # TODO run this OVERNIGHT
    exp_lrdecay((1e-3=>10000, 2e-4=>15000, 5e-5=>20000))
end

function main()
    # the exact learning rate schedule
    # [[0, 0.1], [40000, 0.01], [60000, 0.001]], 80000
    # 0.1 / 255
    # FIXME weight decay, 0.0002
    exp_lrdecay((4e-4=>40000,
                 4e-5=>60000,
                 4e-6=>80000))
end

# TODO log decoded images to tensorboard
# TODO record rec loss
function CIFAR10_advae_exp_helper(expID, lr, total_steps; λ=0, γ=0, β=1, pretrain=false)
    # This put log and saved model into MNIST subfolder
    expID = "CIFAR10-advae/" * expID

    # ae_model_fn = cifar10_AE
    ae_model_fn = dunet
    # ae_model_fn = cifar10_deep_AE
    ds_fn = () -> load_CIFAR10_ds(batch_size=128)

    advae_exp_helper(expID, lr, total_steps,
                     ae_model_fn, ds_fn,
                     if pretrain CIFAR10_AE_pretrain_fn else (a)->a end,
                     λ=λ, γ=γ, β=β,
                     CIFAR10_pretrained_fn,
                     print_steps=2, save_steps=4,
                     test_per_steps=20, test_run_steps=2,
                     attack_fn=attack_CIFAR10_PGD_k(7))
end

function test_param_count()
    param_count(get_CIFAR10_CNN_model())
    param_count(resnet(20))
    param_count(resnet(32))
    param_count(resnet(44))
    param_count(resnet(56))
    param_count(resnet(110))
    param_count(resnet(1202))
    # The WRN paper has the wrong param count, thus probably the wrong implementation
    param_count(WRN(16,1))
    # (16,4) is probably the best for me, 88% acc
    param_count(WRN(16,4))
    param_count(WRN(28,4))
    param_count(WRN(16,8))
    # This is too wide and consume too much memory (even batch size 32 won't work)
    param_count(WRN(28,10))
    param_count(WRN(40,1))
    param_count(WRN(40,8))
end

function exp_res_model(expID, model_fn, total_steps; lr=1e-3)
    # 390 batch / epoch
    ds_fn = () -> load_CIFAR10_ds(batch_size=128)
    expID = "CIFAR10/model-$expID"
    nat_exp_helper(expID, lr, total_steps, model_fn, ds_fn)
end

function exp_res_model_with_lr(expID, model_fn)
    # Implementing learing rate schedule: the optmizer scheduler uses the
    # implicit opt.apply steps. I do not want to save the optimier, thus I'll
    # need to advance opt steps, which is not clean. Thus, the lr schedule won't
    # be implemented inside opt. Instead, I'm implementing it in experiment
    # level script, i.e. here.
    #
    # Original paper: start with lr 0.1, divide it by 10 at 32k and 48k
    # iterations, and terminate training at 64k iterations. But I think the data
    # is [0,255], so the starting rate is 0.1/255 = 4e-4
    exp_res_model(expID, model_fn, 2000, lr=1e-3)
    exp_res_model(expID, model_fn, 4000, lr=1e-4)
    # FIXME This is not changing anything, is it too small?
    exp_res_model(expID, model_fn, 6000, lr=5e-6)
    # exp_res_model(expID, model_fn, 8000, lr=1e-6)
end

# TODO per_image_standardization, Flux has LayerNorm layer, but might not be
# useful when using BN layers.
#
# TODO data augmentation
#
# Original resnet paper: We follow the simple data augmentation in [24] for
# training: 4 pixels are padded on each side, and a 32×32 crop is randomly
# sampled from the padded image or its horizontal flip

# testing different resnet models, and choose one
function test_res_model()
    exp_res_model_with_lr("resnet20", () -> resnet(20))
    exp_res_model_with_lr("resnet32", () -> resnet(32))
    exp_res_model_with_lr("resnet56", () -> resnet(56))
    exp_res_model_with_lr("resnet110", () -> resnet(110))
    exp_res_model_with_lr("WRN-16-2", () -> WRN(16,2))
    exp_res_model_with_lr("WRN-16-4", () -> WRN(16,4))
    exp_res_model_with_lr("WRN-28-4", () -> WRN(28,4))
    exp_res_model_with_lr("WRN-16-8", () -> WRN(16,8))
end

function exp_itadv(lr, total_steps)
    expID = "f0-$lr"
    CIFAR10_exp_helper(expID, lr, total_steps, 0)
end

function exp_pretrain(lr, total_steps)
    expID = "pretrain-$lr"
    CIFAR10_exp_helper(expID, lr, total_steps, 0, pretrain=true)
end

function exp_f1(lr, total_steps)
    expID = "f1-$lr"
    CIFAR10_exp_helper(expID, lr, total_steps, 1)
end

function exp_itadv()
    # 200 steps costs 15 min
    # 2000 steps costs 2.5h
    exp_itadv(5e-2, 100)
    exp_itadv(1e-2, 100)
    exp_itadv(5e-3, 200)
    # this works
    exp_itadv(1e-3, 500)
    exp_itadv(5e-4, 500)
    exp_itadv(1e-4, 500)
    # exp_itadv(5e-5, 1000)
    # exp_itadv(1e-5, 1000)
end

function exp_tmp()
    exp_f1(5e-3, 500)
    exp_f1(3e-3, 2000)
    exp_f1(1e-3, 2000)
    exp_itadv(1e-3, 2000)
    exp_itadv(5e-4, 2000)
    exp_pretrain(1e-3, 500)
end

function exp_pretrain()
    exp_pretrain(5e-2, 100)
    exp_pretrain(1e-2, 100)
    exp_pretrain(5e-3, 200)
    exp_pretrain(1e-3, 500)
    exp_pretrain(5e-4, 500)
    exp_pretrain(1e-4, 500)
end

function exp_f1()
    exp_f1(5e-2, 100)
    exp_f1(1e-2, 100)
    exp_f1(5e-3, 200)

    exp_f1(1e-3, 500)
    exp_f1(5e-4, 500)
    exp_f1(1e-4, 500)
end

function exp()
    exp_itadv()
    exp_pretrain()
    exp_f1()
    exp_tmp()
end

##############################
## AdvAE experiments
##############################

function exp_advae_f0(lr, total_steps)
    expID = "f0-$lr"
    CIFAR10_advae_exp_helper(expID, lr, total_steps, λ=0)
end

function exp_advae_pretrain(lr, total_steps)
    expID = "pretrain-$lr"
    CIFAR10_advae_exp_helper(expID, lr, total_steps, λ=0, pretrain=true)
end

function exp_advae_f1(lr, total_steps)
    expID = "f1-$lr"
    CIFAR10_advae_exp_helper(expID, lr, total_steps, λ=1)
end

function exp_advae_f01(lr, total_steps)
    expID = "f01-$lr"
    CIFAR10_advae_exp_helper(expID, lr, total_steps, λ=0, γ=1)
end

function exp_advae_test(lr, total_steps; expID="test-$(now())")
    @show expID
    CIFAR10_advae_exp_helper(expID, lr, total_steps, λ=3, γ=3, β=1)
end

# FIXME parametrize cnn_model_fn and ae_model_fn
function test()
    exp_advae_f0(1e-3, 2000)
    exp_advae_f0(1e-4, 2000)
    exp_advae_pretrain(1e-4, 2000)
    exp_advae_pretrain(1e-3, 3000)
    exp_advae_f1(1e-3, 2000)
    exp_advae_f1(2e-3, 2000)
    exp_advae_f01(1e-3, 2000)

    exp_advae_test(1e-3, 1000, expID="test-2019-11-17T13:28:29.278")
    exp_advae_test(4e-4, 2000, expID="test-2019-11-18T00:15:19.833")
    exp_advae_test(4e-4, 2000)
end

max_stars_repo_name: lihebi/AdvAE

max_issues_repo_name: lihebi/AdvAE

max_forks_repo_name: lihebi/AdvAE


text:
using Test
using Plots
include("../computation/finitedifference.jl")

finitediff_tests = false
finitediff_in_place_tests = true

if finitediff_tests

println("Running checkDerivative tests...")

@testset "R^1 -> R^1" begin
    f = x -> x^2*exp(x)
    jacf = x -> 2x*exp(x) + x^2*exp(x)
    (_, _, _, pol) = checkDerivative(f, jacf, .64, nothing) #Float64 -> Float64
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1
    f = x -> x[1]^2*exp(x[1])
    jacf = x -> 2x[1]*exp(x[1])+x[1]^2*exp(x[1])
    (_, _, _, pol) = checkDerivative(f, jacf, [.64], nothing) #Array{Float64, 1} -> Float64
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1
    f = x -> [x[1]^2*exp(x[1])]
    jacf = x -> [2x[1]*exp(x[1])+x[1]^2*exp(x[1])]
    (_, _, _, pol) = checkDerivative(f, jacf, [.64], nothing) #Array{Float64, 1} -> Array{Float64, 1}
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1

    #f = x -> [x^2*exp(x)]
    #jacf = x -> [2x*exp(x) + x^2*exp(x)]
    #(_, _, _, pol) = checkDerivative(f, jacf, .64, nothing) #Float64 -> Array{Float64, 1}
    f = x -> x^2*exp(x)
    jacf = x -> 2x*exp(x) + x^2*exp(x)
    hess_f = x -> 4x*exp(x) + 2*exp(x) + x^2*exp(x)
    (_, _, _, pol) = checkDerivative(f, jacf, .64, hess_f) #Float64 -> Float64
    @test coeffs(pol)[end] ≈ 3 atol = 3e-1
end

@testset "R^3 -> R^1" begin
    ff = xx ->  xx[1]^2 * sin(xx[2]*xx[3])
    jacf = xx -> [2*xx[1]*sin(xx[2]*xx[3])  xx[1]^2*xx[3]*cos(xx[2]*xx[3])  xx[1]^2*xx[2]*cos(xx[2]*xx[3])]
    (_, _, _, pol) = checkDerivative(ff, jacf, [.4 .5 .7], nothing) #initial guess has shape (3, 1)

    @test coeffs(pol)[end] ≈ 2 atol = 3e-1
    (_, _, _, pol) = checkDerivative(ff, jacf, [.4, .5, .7], nothing) #initial guess has shape (3,)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1
    (_, _, _, pol) = checkDerivative(ff, jacf, reshape([.4, .5, .7], 1, 3), nothing) #initial guess has shape (1, 3)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1

    #check Hessian
    hess_f = x -> [2sin(x[2]x[3]) 2x[1]x[3]cos(x[2]x[3]) 2x[1]x[2]cos(x[2]x[3]) ;
                    2x[1]x[3]cos(x[2]x[3]) -x[1]^2 * x[3]^2 * sin(x[2]x[3]) x[1]^2*cos(x[2]x[3]) - x[1]^2*x[2]x[3]sin(x[2]x[3]);
                    2x[1]x[2]cos(x[2]x[3]) x[1]^2*cos(x[2]x[3]) - x[1]^2*x[2]x[3]sin(x[2]x[3]) -x[1]^2*x[2]^2 * sin(x[2]x[3])]
    (_, _, plt1, pol) = checkDerivative(ff, jacf, reshape([.4, .5, .7], 1, 3), hess_f, 2, 6) #initial guess has shape (1, 3)
    #display(plt1)
    @test coeffs(pol)[end] ≈ 3 atol = 3e-1
end

@testset "R^1 -> R^3" begin
    f = x -> [x;sin(x);log(x)]
    jacf = x -> [1;cos(x);1/x]
    (_, _, _, pol) = checkDerivative(f, jacf, .4, nothing)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1 #input is scalar

    g = x -> [x[1];sin(x[1]);log(x[1])]
    jacg = x -> [1;cos(x[1]);1/x[1]] #input is 1x1 array
    (_, _, _, pol) = checkDerivative(g, jacg, [.4], nothing)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1 #input is array
end

@testset "R^3 -> R^3" begin
    f = x -> [x[1]x[2]x[3]; sin(x[1]) + x[2]exp(x[3]); exp(x[2] - x[3])/x[1]]
    jacf = x -> [ x[2]x[3]  x[1]x[3] x[1]x[2];
                 cos(x[1]) exp(x[3]) x[2]exp(x[3]);
                -exp(x[2]-x[3])/x[1]^2 exp(x[2]-x[3])/x[1] -exp(x[2]-x[3])/x[1] ]
    (_, _, _, pol) = checkDerivative(f, jacf, [1;2;3], nothing)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1 #input is scalar
end
end

if finitediff_in_place_tests
println("Running checkDerivative_in_place tests...")

@testset "R^1 -> R^1" begin
    store = [0.0]
    f = x -> x^2*exp(x)
    jacf = (store, x) -> (store[1] = 2x*exp(x) + x^2*exp(x); return nothing)
    (_, _, _, pol) = checkDerivative_in_place(f, jacf, .64, store, nothing) #Float64 -> Float64
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1

    f = x -> x[1]^2*exp(x[1])
    jacf = (store, x) -> (store[1] = 2x[1]*exp(x[1])+x[1]^2*exp(x[1]); return nothing)
    (_, _, _, pol) = checkDerivative_in_place(f, jacf, [.64], store, nothing) #Array{Float64, 1} -> Float64
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1

    #f = x -> [x[1]^2*exp(x[1])]
    #jacf = (x, store) -> (store[1] = [2x[1]*exp(x[1])+x[1]^2*exp(x[1])]; return nothing)
    #(_, _, _, pol) = checkDerivative_in_place(f, jacf, [.64], store, nothing) #Array{Float64, 1} -> Array{Float64, 1}
    #@test coeffs(pol)[end] ≈ 2 atol = 3e-1

    #f = x -> [x^2*exp(x)]
    #jacf = x -> [2x*exp(x) + x^2*exp(x)]
    #(_, _, _, pol) = checkDerivative(f, jacf, .64, nothing) #Float64 -> Array{Float64, 1}
    if false
    f = x-> x^2*exp(x)
    jacf = (store, x) -> (store[1] = 2x*exp(x) + x^2*exp(x); return nothing)
    hess_f = x -> 4x*exp(x) + 2*exp(x) + x^2*exp(x)
    (_, _, _, pol) = checkDerivative_in_place(f, jacf, .64, store, hess_f) #Float64 -> Float64
    @test coeffs(pol)[end] ≈ 3 atol = 3e-1
    end
end

@testset "R^3 -> R^1" begin
    store = [0.0, 0.0, 0.0]

    ff = xx ->  xx[1]^2 * sin(xx[2]*xx[3])
    jacf = (store, xx) -> (store[1] = 2*xx[1]*sin(xx[2]*xx[3]); store[2] =  xx[1]^2*xx[3]*cos(xx[2]*xx[3]); store[3] = xx[1]^2*xx[2]*cos(xx[2]*xx[3]); return nothing)
    (_, _, _, pol) = checkDerivative_in_place(ff, jacf, [.4 .5 .7], store, nothing) #initial guess has shape (3, 1)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1
    (_, _, _, pol) = checkDerivative_in_place(ff, jacf, [.4, .5, .7], store, nothing) #initial guess has shape (3,)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1
    (_, _, _, pol) = checkDerivative_in_place(ff, jacf, reshape([.4, .5, .7], 1, 3), store, nothing) #initial guess has shape (1, 3)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1

    if false
    #check Hessian
    hess_f = x -> [2sin(x[2]x[3]) 2x[1]x[3]cos(x[2]x[3]) 2x[1]x[2]cos(x[2]x[3]) ;
                    2x[1]x[3]cos(x[2]x[3]) -x[1]^2 * x[3]^2 * sin(x[2]x[3]) x[1]^2*cos(x[2]x[3]) - x[1]^2*x[2]x[3]sin(x[2]x[3]);
                    2x[1]x[2]cos(x[2]x[3]) x[1]^2*cos(x[2]x[3]) - x[1]^2*x[2]x[3]sin(x[2]x[3]) -x[1]^2*x[2]^2 * sin(x[2]x[3])]
    (_, _, plt1, pol) = checkDerivative(ff, jacf, reshape([.4, .5, .7], 1, 3), hess_f, 2, 6) #initial guess has shape (1, 3)
    #display(plt1)
    @test coeffs(pol)[end] ≈ 3 atol = 3e-1
    end
end

@testset "R^1 -> R^3" begin
    store = [0.0, 0.0, 0.0]
    f = x -> [x;sin(x);log(x)]
    jacf = (store, x) -> (store[1] =1;store[2] = cos(x); store[3] = 1/x; return nothing)
    (_, _, _, pol) = checkDerivative_in_place(f, jacf, .4, store, nothing)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1 #input is scalar

    g = x -> [x[1];sin(x[1]);log(x[1])]
    jacg = (store, x) -> (store[1] = 1; store[2] = cos(x[1]); store[3] = 1/x[1]; return nothing) #input is 1x1 array
    (_, _, _, pol) = checkDerivative_in_place(g, jacg, [.4], store, nothing)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1 #input is array
end

@testset "R^3 -> R^3" begin
    store = zeros(3, 3)
    f = x -> [x[1]x[2]x[3]; sin(x[1]) + x[2]exp(x[3]); exp(x[2] - x[3])/x[1]]
    jacf = (store, x) -> (store[1, 1] = x[2]x[3]; store[1, 2] = x[1]x[3]; store[1, 3] = x[1]x[2];
                        store[2, 1] =  cos(x[1]); store[2, 2] = exp(x[3]); store[2, 3] = x[2]exp(x[3]);
                        store[3, 1] = -exp(x[2]-x[3])/x[1]^2; store[3, 2] = exp(x[2]-x[3])/x[1]; store[3, 3] = -exp(x[2]-x[3])/x[1]; return nothing)

    #[ x[2]x[3]  x[1]x[3] x[1]x[2];
    #             cos(x[1]) exp(x[3]) x[2]exp(x[3]);
    #            -exp(x[2]-x[3])/x[1]^2 exp(x[2]-x[3])/x[1] -exp(x[2]-x[3])/x[1] ]
    (_, _, _, pol) = checkDerivative_in_place(f, jacf, [1;2;3], store, nothing)
    @test coeffs(pol)[end] ≈ 2 atol = 3e-1 #input is scalar
end

end

ext: jl

max_stars_repo_name: dbindel/btg

max_issues_repo_name: dbindel/btg

max_forks_repo_name: dbindel/btg


text:
include("DNA.jl")
include("Synthesizer.jl")
include("MultiScalarRule.jl")
include("ScalarRule.jl")
include("SetRule.jl")

max_stars_repo_name: unbornchikken/julia-ml-proto

max_issues_repo_name: unbornchikken/julia-ml-proto

max_forks_repo_name: unbornchikken/julia-ml-proto


text:
using EzExcel

@static if VERSION < v"0.7.0-DEV.2005"
    using Base.Test
    using Missings
else
    using Test
end

PATH = Pkg.dir("EzExcel") |> x -> joinpath(x, "test\\datasets")

# testset.xlsx
filename = joinpath(PATH, "testset.xlsx")
wb = WorkBook(filename)

@test length(wb) == 4
@test sheetname(wb) == ("combination", "formats", "empty", "negative")

wss = sheets(wb)

@testset "testset:[combination]" begin
    ws = wss[1]
    sheet_1 = [["Some Numbers", 1, 1.5, 2, 2.5],
               ["Some Strings", "A", "BB", "CCC", "DDDD"],
               ["Some Bools", true, false, false, true],
               ["Mixed column", 2, "EEEEE", false, 1.5],
               ["Mixed with NA", 9, "III", missing, true],
               ["Float64 with NA", 3, missing, 3.5, 4],
               ["String with NA", "FF", missing, "GGG", "HHHH"],
               ["Bool with NA", missing, true, missing, false],
               ["Some dates", DateTime(2015, 3, 3), DateTime(2015, 2, 4, 10, 14), DateTime(1988, 4, 9, 0, 00), DateTime(1900, 1, 1, 15, 02, 00)],
               ["Some errors", missing, missing, missing, missing],
               ["Errors with NA", missing, missing, missing, missing]]
    sheet_1 = hcat(sheet_1...)

    @test sheetname(ws) == "combination"

     for r in 1:size(ws, 1), c in 1:size(ws, 2)
        if ismissing(ws[r, c])
            @test ismissing(sheet_1[r, c])
        else
            @test ws[r, c] == sheet_1[r, c]
        end
    end

end

filename = joinpath(PATH, "big_testset.xlsx")


text:
ec_training = readdlm("../poets/EC.dat")
ec_nominal = readdlm("../poets/EC_training.dat")
#ec_random = readdlm("../poets/results_poets/random_set/EC.dat")
#stopgap
ec_random = readdlm("../poets/results_poets/random_set/EC_full_jl_array.dat")

ec_pred1  = readdlm("../poets/results_poets/cross_validation/EC1_jl_array.dat")
ec_pred2  = readdlm("../poets/results_poets/cross_validation/EC2_jl_array.dat")
ec_pred3  = readdlm("../poets/results_poets/cross_validation/EC3_jl_array.dat")
ec_pred4  = readdlm("../poets/results_poets/cross_validation/EC4_jl_array.dat")
ec_pred5  = readdlm("../poets/results_poets/cross_validation/EC5_jl_array.dat")
ec_pred6  = readdlm("../poets/results_poets/cross_validation/EC6_jl_array.dat")
ec_pred7  = readdlm("../poets/results_poets/cross_validation/EC7_jl_array.dat")
ec_pred8  = readdlm("../poets/results_poets/cross_validation/EC8_jl_array.dat")
ec_pred9  = readdlm("../poets/results_poets/cross_validation/EC9_jl_array.dat")
ec_pred10 = readdlm("../poets/results_poets/cross_validation/EC10_jl_array.dat")
ec_pred11 = readdlm("../poets/results_poets/cross_validation/EC11_jl_array.dat")

# using Debug
# @debug function stop()
#   @bp
# end

# build rows of table
# 1
dat1 = []
push!(dat1 ,ec_nominal[1])
push!(dat1 ,ec_training[1,:]) # objective 1 error for all models
push!(dat1 ,ec_pred1[1,:])
push!(dat1 ,ec_random[1,:])

# @show std(dat1[2])
# @show std(dat1[3])
# #@show std(dat1[4])
# @show dat1[4]
# stop()
row1 = [mean(dat1[1]),mean(dat1[2]),std(dat1[2]),
mean(dat1[3]),std(dat1[3]),mean(dat1[4]),std(dat1[4])]
#using HypothesisTests
#@show dat1[2],dat1[4]
#@show pvalue(TwoSampleTTest(Array{Float32}(dat1[2]),Array{Float32}(dat1[4])))

# 2
dat2 = []
push!(dat2 ,ec_nominal[2])
push!(dat2 ,ec_training[2,:]) # objective 1 error for all models
push!(dat2 ,ec_pred2[2,:])
push!(dat2 ,ec_random[2,:])

row2 = [mean(dat2[1]),mean(dat2[2]),std(dat2[2]),
mean(dat2[3]),std(dat2[3]),mean(dat2[4]),std(dat2[4])]

# 3
dat3 = []
push!(dat3 ,ec_nominal[3])
push!(dat3 ,ec_training[3,:]) # objective 1 error for all models
push!(dat3 ,ec_pred3[3,:])
push!(dat3 ,ec_random[3,:])

row3 = [mean(dat3[1]),mean(dat3[2]),std(dat3[2]),
mean(dat3[3]),std(dat3[3]),mean(dat3[4]),std(dat3[4])]

# 4
dat4 = []
push!(dat4 ,ec_nominal[4])
push!(dat4 ,ec_training[4,:]) # objective 1 error for all models
push!(dat4 ,ec_pred4[4,:])
push!(dat4 ,ec_random[4,:])

row4 = [mean(dat4[1]),mean(dat4[2]),std(dat4[2]),
mean(dat4[3]),std(dat4[3]),mean(dat4[4]),std(dat4[4])]

# 5
dat5 = []
push!(dat5 ,ec_nominal[5])
push!(dat5 ,ec_training[5,:]) # objective 1 error for all models
push!(dat5 ,ec_pred5[5,:])
push!(dat5 ,ec_random[5,:])

row5 = [mean(dat5[1]),mean(dat5[2]),std(dat5[2]),
mean(dat5[3]),std(dat5[3]),mean(dat5[4]),std(dat5[4])]

# 6
dat6 = []
push!(dat6 ,ec_nominal[6])
push!(dat6 ,ec_training[6,:]) # objective 1 error for all models
push!(dat6 ,ec_pred6[6,:])
push!(dat6 ,ec_random[6,:])

row6 = [mean(dat6[1]),mean(dat6[2]),std(dat6[2]),
mean(dat6[3]),std(dat6[3]),mean(dat6[4]),std(dat6[4])]

# 7
dat7 = []
push!(dat7 ,ec_nominal[7])
push!(dat7 ,ec_training[7,:]) # objective 1 error for all models
push!(dat7 ,ec_pred7[7,:])
push!(dat7 ,ec_random[7,:])

row7 = [mean(dat7[1]),mean(dat7[2]),std(dat7[2]),
mean(dat7[3]),std(dat7[3]),mean(dat7[4]),std(dat7[4])]

# 8
dat8 = []
push!(dat8 ,ec_nominal[8])
push!(dat8 ,ec_training[8,:]) # objective 1 error for all models
push!(dat8 ,ec_pred8[8,:])
push!(dat8 ,ec_random[8,:])

row8 = [mean(dat8[1]),mean(dat8[2]),std(dat8[2]),
mean(dat8[3]),std(dat8[3]),mean(dat8[4]),std(dat8[4])]

# 9
dat9 = []
push!(dat9 ,ec_nominal[9])
push!(dat9 ,ec_training[9,:]) # objective 1 error for all models
push!(dat9 ,ec_pred9[9,:])
push!(dat9 ,ec_random[9,:])

row9 = [mean(dat9[1]),mean(dat9[2]),std(dat9[2]),
mean(dat9[3]),std(dat9[3]),mean(dat9[4]),std(dat9[4])]

# 10
dat10 = []
push!(dat10,ec_nominal[10])
push!(dat10,ec_training[10,:]) # objective 1 error for all models
push!(dat10,ec_pred10[10,:])
push!(dat10,ec_random[10,:])

row10 = [mean(dat10[1]),mean(dat10[2]),std(dat10[2]),
mean(dat10[3]),std(dat10[3]),mean(dat10[4]),std(dat10[4])]

# 11
dat11 = []
push!(dat11,ec_nominal[11])
push!(dat11,ec_training[11,:]) # objective 1 error for all models
push!(dat11,ec_pred11[11,:])
push!(dat11,ec_random[11,:])

row11 = [mean(dat11[1]),mean(dat11[2]),std(dat11[2]),
mean(dat11[3]),std(dat11[3]),mean(dat11[4]),std(dat11[4])]

rows=[]
push!(rows,row1)
push!(rows,row2)
push!(rows,row3)
push!(rows,row4)
push!(rows,row5)
push!(rows,row6)
push!(rows,row7)
push!(rows,row8)
push!(rows,row9)
push!(rows,row10)
push!(rows,row11)

dir_out= "../poets/results_poets"

writedlm("$dir_out/fig_error_table.csv",rows,",")

## to get nominal values
#dbassen@butcherhpc:~/Dropbox/server_swap_space/gen_2_model/poets$ ji emt_grn_model_objective_function.jl
#
#julia> global HOLD = 0
#0

#julia> objective_function(parameter_array)
# writedlm("EC_training.dat",objective_function(parameter_array))


text:
include("verify_vertices.jl")
include("interface.jl")


text:
const VecLike{N, T} = Union{NTuple{N, T}, StaticVector{N, T}}

"""
Any GLAbstraction.Camera
"""
function to_camera(b, x::GLAbstraction.Camera)
    x
end
"""
Takes a camera symbol, one of :auto, :perspective, :orthographic, :pixel
"""
function to_camera(b, x::Symbol)
    if x in (:auto, :perspective, :orthographic, :pixel)
        return x
    else
        error(":$x is not a supported camera symbol. Try one of :auto, :perspective, :orthographic, :pixel")
    end
end

"""
3 Numbers for each dimension
"""
to_3floats(b, x::Tuple) = to_float.(b, x)
to_3floats(b, x::Number) = ntuple(i-> x, Val{3})

to_2floats(b, x::Tuple) = to_float.(b, x)
to_2floats(b, x::Number) = ntuple(i-> to_float(b, x), Val{2})

"""
    to_scale(b, s::Number)::Vec
"""
to_scale(b, s::Number) = Vec3f0(s)
"""
    to_scale(b, s::VecLike)::Point
"""
to_scale(b, s::VecLike{2}) = Vec3f0(s[1], s[2], 1)
to_scale(b, s::VecLike{3}) = Vec3f0(s)

"""
    to_offset(b, s::Number)::Point
"""
to_offset(b, s::Number) = Point3f0(s)
"""
    to_scale(b, s::VecLike)::Point
"""
to_offset(b, s::VecLike{2}) = Point3f0(s[1], s[2], 0)
to_offset(b, s::VecLike{3}) = Point3f0(s)

"""
    to_rotation(b, vec4)
"""
to_rotation(b, s::VecLike{4}) = Vec4f0(s)
"""
    to_rotation(b, quaternion)
"""
to_rotation(b, s::Quaternion) = Vec4f0(s.v1, s.v2, s.v3, s.s)

"""
    to_rotation(b, tuple_float)
"""
to_rotation(b, s::Tuple{<:VecLike{3}, <: AbstractFloat}) = qrotation(s[1], s[2])
to_rotation(b, s::Tuple{<:VecLike{2}, <: AbstractFloat}) = qrotation(Vec3f0(s[1][1], s[1][2], 0), s[2])

"""
    to_image(b, image)
All kinds of images
"""
to_image(b, image) = image

"""
    to_bool(b, bool)
To boolean
"""
to_bool(b, bool) = Bool(bool)

"""
    to_bool(b, tuple)
Accepts Tuple of Bool
"""
to_bool(b, x::Tuple) = Bool.(x)

"""

    to_index_buffer(b, x::GLBuffer{UInt32})
"""
to_index_buffer(b, x::GLBuffer) = x

"""
`TOrSignal{Int}, AbstractVector{UnitRange{Int}}, TOrSignal{UnitRange{Int}}`
"""
to_index_buffer(b, x::Union{TOrSignal{Int}, VecOrSignal{UnitRange{Int}}, TOrSignal{UnitRange{Int}}}) = x

"""
`AbstractVector{<:Integer}` assumend 1-based indexing
"""
function to_index_buffer(b, x::AbstractVector{I}) where I <: Integer
    gpu_mem = GLBuffer(Cuint.(to_value(x) .- 1), buffertype = GL_ELEMENT_ARRAY_BUFFER)
    x = lift_node(to_node(x)) do x
        val = Cuint[i-1 for i = x]
        update!(gpu_mem, val)
     end
    gpu_mem
end

"""
`AbstractVector{<:Face{2}}` for linesegments
"""
function to_index_buffer(b, x::AbstractVector{I}) where I <: Face{2}
    Face{2, GLIndex}.(x)
end

"""
`AbstractVector{UInt32}`, is assumed to be 0 based
"""
function to_index_buffer(b, x::AbstractVector{UInt32})
    gpu_mem = GLBuffer(to_value(x), buffertype = GL_ELEMENT_ARRAY_BUFFER)
    lift_node(to_node(x)) do x
        update!(gpu_mem, x)
    end
    gpu_mem
end

to_index_buffer(b, x) = error(
    "Not a valid index type: $(typeof(x)).
    Please choose from Int, Vector{UnitRange{Int}}, Vector{Int} or a signal of either of them"
)

"""
    to_positions(b, positionlike)
`NTuple{2, AbstractArray{Float}}` for 2D points
"""
function to_positions(b, x::Tuple{<: AbstractArray, <: AbstractArray})
    to_position.(b, x...)
end

"""
`NTuple{3, AbstractArray{Float}}` for 3D points
"""
function to_positions(b, x::Tuple{<: AbstractArray, <: AbstractArray, <: AbstractArray})
    Point{3, Float32}.(x...)
end

"""
`view(AbstractArray{Point}, idx)` for a subset of points. Can be shared (so you can plot subsets of the same data)!
"""
function to_positions(b, x::SubArray)
    view(to_positions(b, x.parent), x.indexes...)
end

"""
`AbstractArray{T}` where T needs to have `length` defined and must be convertible to a Point
"""
function to_positions(b, x::AbstractArray{NTuple{N, T}}) where {N, T}
    Point{N, Float32}.(x)
end
function to_positions(b, x::AbstractArray{T}) where T
    N = if applicable(length, T)
        length(T)
    else
        error("Point type needs to have length defined and needs to be convertible to GeometryTypes point (e.g. tuples, abstract arrays etc.)")
    end
    Point{N, Float32}.(x)
end
function to_positions(b, x::T) where T <: StaticVector
    error("Please use an array of StaticVectors for positions. Found: $T")

end

function to_positions(b, x::GeometryPrimitive)
    to_positions(b, decompose(Point, x))
end

function to_positions(b, x::SimpleRectangle)
    # TODO fix the order of decompose
    to_positions(b, decompose(Point, x)[[1, 2, 4, 3, 1]])
end

function to_positions(b, x)
    error("Not a valid position type: $(typeof(x)). Please read the documentation of [`to_positions`](@ref)")
end

"""
Converts a Vec like to a position (Point)
"""
function to_position(b, x::VecLike{N}) where N
    Point{N, Float32}(x)
end

function to_position(b, x, y)
    Point{2, Float32}(to_absolute(b, x), to_absolute(b, y))
end

"""
    to_array(b, arraylike)
`AbstractArray`
"""
to_array(b, x) = collect(x)

"""
    to_scalefunc(b, x)
`Function`
"""
to_scalefunc(b, x) = x # TODO implement it

"""
    to_text(b, x)
All text
"""
to_text(b, x) = x# TODO implement it
"""
    to_font(b, x)
All fonts
"""
to_font(b, x) = x # TODO implement it

"""
    to_colornorm(b, norm, intensity)
anything that can be converted to `Vec2f0` (e.g. `Tuple`, `Vector`)
"""
to_colornorm(b, norm, intensity) = Vec2f0(norm)

"""
If colornorm is `nothing` will default to calculate the extrema from `intensity`
"""
function to_colornorm(b, norm::Void, intensity)
    nan_extrema(intensity)
end

"""
`AbstractArray`
"""
to_intensity(b, x::AbstractArray) = x # TODO implement

"""
    to_surface(b, x::Range)
`Range`
"""
to_surface(b, x::Range) = x
"""
    to_surface(b, arraylike)
Anything that can be converted to Matrix/Vector of Float32
"""
to_surface(b, x) = Float32.(x)

const Image = Matrix{T} where T <: Colorant

const _marker_map = Dict(
    :rect => '■',
    :star5 => '★',
    :diamond => '◆',
    :hexagon => '⬢',
    :cross => '✚',
    :xcross => '❌',
    :utriangle => '▲',
    :dtriangle => '▼',
    :ltriangle => '◀',
    :rtriangle => '▶',
    :pentagon => '⬟',
    :octagon => '⯄',
    :star4 => '✦',
    :star6 => '🟋',
    :star8 => '✷',
    :vline => '┃',
    :hline => '━',
    :+ => '+',
    :x => 'x',
    :circle => '●'
)

"""
    available_marker_symbols()
Displays all available marker symbols
"""
function available_marker_symbols()
    println("Marker Symbols:")
    for (k, v) in _marker_map
        println("    ", k, " => ", v)
    end
end

"""
    to_spritemarker(b, x::Circle)
`GeometryTypes.Circle(Point2(...), radius)`
"""
to_spritemarker(b, x::Circle) = x

"""
    to_spritemarker(b, ::Type{Circle})
`Type{GeometryTypes.Circle}`
"""
to_spritemarker(b, ::Type{Circle}) = Circle(Point2f0(0), 1f0)
"""
    to_spritemarker(b, ::Type{Rectangle})
`Type{GeometryTypes.Rectangle}`
"""
to_spritemarker(b, ::Type{Rectangle}) = HyperRectangle(Vec2f0(0), Vec2f0(1))
"""
    to_spritemarker(b, marker::Char)
Any `Char`, including unicode
"""
to_spritemarker(b, marker::Char) = marker

"""
Matrix of AbstractFloat will be interpreted as a distancefield (negative numbers outside shape, positive inside)
"""
to_spritemarker(b, marker::Matrix{<: AbstractFloat}) = Float32.(marker)

"""
Any AbstractMatrix{<: Colorant} or other image type
"""
to_spritemarker(b, marker::Image) = to_image(marker)

"""
A `Symbol` - Available options can be printed with `available_marker_symbols()`
"""
function to_spritemarker(b, marker::Symbol)
    if haskey(_marker_map, marker)
        return to_spritemarker(b, _marker_map[marker])
    else
        warn("Unsupported marker: $marker, using ● instead")
        return '●'
    end
end

to_spritemarker(b, marker::String) = marker
to_spritemarker(b, marker::AbstractVector{Char}) = String(marker)

"""
Vector of anything that is accepted as a single marker will give each point it's own marker.
Note that it needs to be a uniform vector with the same element type!
"""
function to_spritemarker(b, marker::AbstractVector)
    println(typeof(marker))
    marker = map(marker) do sym
        to_spritemarker(b, sym)
    end
    if isa(marker, AbstractVector{Char})
        String(marker)
    else
        marker
    end
end

# function to_spritemarker(shape::Shape)
#     points = Point2f0[GeometryTypes.Vec{2, Float32}(p) for p in zip(shape.x, shape.y)]
#     bb = GeometryTypes.AABB(points)
#     mini, maxi = minimum(bb), maximum(bb)
#     w3 = maxi-mini
#     origin, width = Point2f0(mini[1], mini[2]), Point2f0(w3[1], w3[2])
#     map!(p -> ((p - origin) ./ width) - 0.5f0, points, points) # normalize and center
#     GeometryTypes.GLNormalMesh(points)
# end
# create a marker/shape type

"""
    to_static_vec(b, x)
`AbstractArray`
"""
function to_static_vec(b, x::AbstractArray)
    Vec(ntuple(length(x)) do i
        x[i]
    end)
end

"""
Any `StaticVector`
"""
to_static_vec(b, x::StaticVector) = Vec(x)

"""
`NTuple`
"""
to_static_vec(b, x::NTuple{N}) where N = Vec(x)

"""
Abstract array of which the elements can be converted to vec
"""
function to_static_vec(b, x::AbstractArray{T}) where T <: Union{Tuple, SVector, AbstractArray}
    to_static_vec.(b, x)
end

"""
    to_rotations(b, x)
`Billboard()` for a rotation that will always face the camera
"""
to_rotations(b, x::Billboard) = x

"""
Any AbstractArray which elements can be converted to Vec4 (as a quaternion x, y, z, w)
"""
to_rotations(b, x::AbstractVector) = to_static_vec(b, x)

"""
    to_markersize2d(b, x)
Anything that can be converted to `Vec2f0` for x, y scale
"""
to_markersize2d(b, x::Number) = Vec2f0(x)
to_markersize2d(b, x::Tuple) = Vec2f0(x)
to_markersize2d(b, x::StaticVector) = Vec2f0(x)
to_markersize2d(b, x::AbstractVector) = Vec2f0.(x)

# TODO generically implement these to share implementation with to_position etc
"""
    to_markersize3d(b, x)
Anything that can be converted to `Vec3f0` for x, y, z scale
"""
to_markersize3d(b, x::Number) = Vec3f0(x)
to_markersize3d(b, x::Tuple) = Vec3f0(x)
to_markersize3d(b, x::StaticVector) = Vec3f0(x)
to_markersize3d(b, x::AbstractVector) = Vec3f0.(x)

"""
    to_linestyle(b, x)
`Nothing` for no style
"""
to_linestyle(b, x::Void) = x

"""
`AbstractVector{<:AbstractFloat}` for denoting sequences of fill/nofill. E.g.
[0.5, 0.8, 1.2] will result in 0.5 filled, 0.3 unfilled, 0.4 filled. 1.0 unit is one linewidth!
"""
to_linestyle(b, A::AbstractVector) = A
"""
A `Symbol` equal to `:dash`, `:dot`, `:dashdot`, `:dashdotdot`
"""
function to_linestyle(b, ls::Symbol)
    return if ls == :dash
        [0.0, 1.0, 2.0, 3.0, 4.0]
    elseif ls == :dot
        tick, gap = 1/2, 1/4
        [0.0, tick, tick+gap, 2tick+gap, 2tick+2gap]
    elseif ls == :dashdot
        dtick, dgap = 1.0, 1.0
        ptick, pgap = 1/2, 1/4
        [0.0, dtick, dtick+dgap, dtick+dgap+ptick, dtick+dgap+ptick+pgap]
    elseif ls == :dashdotdot
        dtick, dgap = 1.0, 1.0
        ptick, pgap = 1/2, 1/4
        [0.0, dtick, dtick+dgap, dtick+dgap+ptick, dtick+dgap+ptick+pgap, dtick+dgap+ptick+pgap+ptick,  dtick+dgap+ptick+pgap+ptick+pgap]
    else
        error("Unkown line style: $linestyle. Available: :dash, :dot, :dashdot, :dashdotdot or a sequence of numbers enumerating the next transparent/opaque region")
    end
end

"""
    to_normals(b, x)
Vector{Normal{3}}
"""
to_normals(b, x) = x

"""
    to_faces(b, x)
Any array of NTuple/GeometryTypes.Face
"""
function to_faces(b, x::AbstractVector{NTuple{N, TI}}) where {N, TI <: Integer}
    to_faces(reinterpret(Face{N, TI}, x))
end

function to_faces(b, faces::AbstractVector{<: Face})
    decompose(GLTriangle, faces)
end
function to_faces(b, faces::AbstractVector{GLTriangle})
    faces
end

function to_faces(b, x::Void)
    x
end

function to_faces(b, x::Vector{Int})
    if length(x) % 3 != 0
        error("Int indices need to represent triangles, therefore need to be a multiple of three. Found: $(length(x))")
    end
    reinterpret(GLTriangle, UInt32.(x .- 1))
end

"""
    to_mesh(b, meshlike)
`AbstractMesh`
"""
function to_mesh(b, mesh::AbstractMesh)
    mesh
end

function to_mesh(b, geom::GeometryPrimitive)
    GLNormalMesh(geom)
end

function to_mesh(b, verts, faces, colors, attribute_id::Node{Void})
    lift_node(verts, faces) do v, f
        GLPlainMesh(v, f)
    end
end
function to_mesh(b, verts, faces, colors::Node{<:Colorant}, attribute_id::Node{Void})
    lift_node(verts, faces) do v, f
        GLNormalMesh(vertices = v, faces = f)
    end
end

function to_mesh(b, verts, faces, colors::AbstractVector, attribute_id::Node{Void})
    lift_node(verts, faces, colors) do v, f, c
        if length(c) != length(v)
            error("You need one color per vertex. Found: $(length(v)) vertices, and $(length(c)) colors")
        end
        GLNormalVertexcolorMesh(vertices = v, faces = f, color = c)
    end
end
function to_mesh(verts, faces, colors::AbstractVector, attribute_id::AbstractVector)
    lift_node(verts, faces, colors, attribute_id) do v, f, c, id
        if length(id) != length(v)
            error("You need one attribute per vertex. Found: $(length(v)) vertices, and $(length(id)) attributes")
        end
        GLNormalAttributeMesh(
            vertices = v, faces = f,
            attributes = c, attribute_id = id
        )
    end
end

"""
    to_attribut_id(b, x)
Index into Mesh attributes, Vector{Integer}
"""
to_attribut_id(backend, x) = x

"""
    to_float(b, x)
Any Object convertible to Floatingpoint
"""
to_float(b, x) = Float32(x)

to_color(c) = to_color(current_backend[], c)

"""
    to_color(b, x)
`Colors.Colorants`
"""
to_color(b, c::Colorant) = RGBA{Float32}(c)
"""
A `Symbol` naming a color, e.g. `:black`
"""
to_color(b, c::Symbol) = to_color(b, string(c))
"""
A `String` naming a color, e.g. `:black` or html style `#rrggbb`
"""
to_color(b, c::String) = parse(RGBA{Float32}, c)
to_color(b, c::UniqueColorIter) = to_color(b, next(c))

"""
A Tuple or Array with elements that `to_color` accepts.
If Array is a Matrix it will get interpreted as an Image
"""
to_color(b, c::Union{Tuple, AbstractArray}) = to_color.(b, c)

"""
Tuple{<: ColorLike, <: AbstractFloat} for a transparent color
"""
to_color(b, c::Tuple{T, F}) where {T, F <: AbstractFloat} = RGBAf0(Colors.color(to_color(b, c[1])), c[2])

const colorbrewer_names = Symbol[
    # All sequential color schemes can have between 3 and 9 colors. The available sequential color schemes are:
    :Blues,
    :Oranges,
    :Greens,
    :Reds,
    :Purples,
    :Greys,
    :OrRd,
    :GnBu,
    :PuBu,
    :PuRd,
    :BuPu,
    :BuGn,
    :YlGn,
    :RdPu,
    :YlOrBr,
    :YlGnBu,
    :YlOrRd,
    :PuBuGn,

    # All diverging color schemes can have between 3 and 11 colors. The available diverging color schemes are:
    :Spectral,
    :RdYlGn,
    :RdBu,
    :PiYG,
    :PRGn,
    :RdYlBu,
    :BrBG,
    :RdGy,
    :PuOr,

    #The number of colors a qualitative color scheme can have depends on the scheme. The available qualitative color schemes are:
    :Name,
    :Set1,
    :Set2,
    :Set3,
    :Dark2,
    :Accent,
    :Paired,
    :Pastel1,
    :Pastel2
]

"""
    available_gradients()

Prints all available gradient names
"""
function available_gradients()
    println("Gradient Symbol/Strings:")
    for name in colorbrewer_names
        println("    ", name)
    end
end

"""
    to_colormap(b, x)
An `AbstractVector{T}` with any object that [`to_color`](@ref) accepts
"""
to_colormap(b, cm::AbstractVector) = RGBAf0.(cm)

"""
Tuple(A, B) or Pair{A, B} with any object that [`to_color`](@ref) accepts
"""
function to_colormap(b, cs::Union{Tuple, Pair})
    [to_color.(cs)...]
end

to_colormap(val) = to_colormap(current_backend[], val)

"""
A Symbol/String naming the gradient. For more on what names are available please see: `available_gradients()
"""
function to_colormap(b, cs::Union{String, Symbol})
    cs_sym = Symbol(cs)
    if cs_sym in colorbrewer_names
        ColorBrewer.palette(string(cs_sym), 9)
    else
        #TODO integrate PlotUtils color gradients
        error("There is no color gradient named: $cs")
    end
end

"""
    to_spatial_order(b, x)
"xy" or "yx"
"""
function to_spatial_order(b, x)
    if !(x in ("yx", "xy"))
        error("Spatial order must be \"yx\" or \"xy\". Found: $x")
    end
    x
end

"""
:xy or :yx
"""
to_spatial_order(b, x::Symbol) = to_spatial_order(b, string(x))

"""
    to_interval(b, x)
`Tuple{<: Number, <: Number}`
"""
function to_interval(b, x)
    if isa(x, Tuple{<: Number, <: Number})
        return x
    else
        error("Not an accepted value for interval. Please have a look at the documentation for to_interval")
    end
end

"""
Pair{<: Number, <: Number} e.g. 2 => 100
"""
to_interval(b, x::Pair{<: Number, <: Number}) = to_interval(b, (x...,))

"""
`AbstractVector` will be interpreted as an interval from minimum to maximum
"""
to_interval(b, x::AbstractVector) = to_interval(b, (minimum(x), maximum(x)))

using GLVisualize: IsoValue, Absorption, MaximumIntensityProjection, AbsorptionRGBA, IndexedAbsorptionRGBA
export IsoValue, Absorption, MaximumIntensityProjection, AbsorptionRGBA, IndexedAbsorptionRGBA

"""
    to_volume_algorithm(b, x)
Enum values: `IsoValue` `Absorption` `MaximumIntensityProjection` `AbsorptionRGBA` `IndexedAbsorptionRGBA`
"""
function to_volume_algorithm(b, value)
    if isa(value, GLVisualize.RaymarchAlgorithm)
        return Int32(value)
    elseif isa(value, Int32) && value in 0:5
        return value
    else
        error("$value is not a valid volume algorithm. Please have a look at the documentation of `to_volume_algorithm`")
    end
end

"""
Symbol/String: iso, absorption, mip, absorptionrgba, indexedabsorption
"""
function to_volume_algorithm(b, value::Union{Symbol, String})
    vals = Dict(
        :iso => IsoValue,
        :absorption => Absorption,
        :mip => MaximumIntensityProjection,
        :absorptionrgba => AbsorptionRGBA,
        :indexedabsorption => IndexedAbsorptionRGBA,
    )
    to_volume_algorithm(b, get(vals, Symbol(value)) do
        error("$value not a valid volume algorithm. Needs to be in $(keys(vals))")
    end)
end


text:
module Csv

using CSV

function read_sheet(filename)
    #reading csv sheet in entirety
     try
         return CSV.read(filename)
     catch
         println("Cannot retrieve data.")
     end

 end

end #module


text:
import LinearAlgebra: I
import ForneyLab: unsafeMean, unsafePrecision, unsafeCov, eye
import StatsFuns: @irrational
@irrational log2π  1.8378770664093454836 log(big(2.)*π)

function eye(dim)
    return Matrix{Float64}(I,dim,dim)
end

function mn(d::ProbabilityDistribution{Univariate, T}) where T<:Union{Gaussian,Gamma}
    return ForneyLab.unsafeMean(d)
end

function mn(d::ProbabilityDistribution{Multivariate, T}) where T<:Union{Gaussian,Gamma}
    return ForneyLab.unsafeMean(d)
end

function pc(d::ProbabilityDistribution{Univariate, T}) where T<:Union{Gaussian,Gamma}
    return ForneyLab.unsafePrecision(d)
end

function pc(d::ProbabilityDistribution{Multivariate, T}) where T<:Union{Gaussian,Gamma}
    return ForneyLab.unsafePrecision(d)
end

function cv(d::ProbabilityDistribution{Univariate, T}) where T<:Union{Gaussian,Gamma}
    return ForneyLab.unsafeCov(d)
end

function cv(d::ProbabilityDistribution{Multivariate, T}) where T<:Union{Gaussian,Gamma}
    return ForneyLab.unsafeCov(d)
end

max_stars_repo_name: wmkouw/actinf-oscillator

max_issues_repo_name: wmkouw/actinf-oscillator

max_forks_repo_name: wmkouw/actinf-oscillator


text:
using PEGParser

@grammar calc2 begin
  start = expr{ _1 }

  expr_op = (term + op1 + expr){
    eval(_2)(_1, _3)
  }

  expr = expr_op | term

  term_op = (factor + op2 + term){
    eval(_2)(_1, _3)
  }

  term = term_op | factor
  factor = number | pfactor
  pfactor = (-lparen + expr + -rparen){ _1 }

  op1 = add | sub
  op2 = mult | div

  number = (-space + r"[1-9][0-9]*"){ parse(Int, _1.value) }
  add = (-space + "+"){ symbol(_1.value) }
  sub = (-space + "-"){ symbol(_1.value) }
  mult = (-space + "*"){ symbol(_1.value) }
  div = (-space + "/"){ symbol(_1.value) }

  lparen = space + "("
  rparen = space + ")"
  space = r"[ \n\r\t]*"
end

data = "4+5*(8+2)"
(ast, pos, error) = parse(calc2, data)
println(ast)


text:
## THE CONSTANT REGRESSOR

"""
    ConstantRegressor(; distribution_type=Distributions.Normal)

A regressor that, for any new input pattern, predicts the univariate
probability distribution best fitting the training target data. Use
`predict_mean` to predict the mean value instead.
"""
struct ConstantRegressor{D<:Distributions.Sampleable} <: Probabilistic
    distribution_type::Type{D}
end

ConstantRegressor(; distribution_type=Distributions.Normal) =
    ConstantRegressor(distribution_type)

function MLJBase.fit(::ConstantRegressor{D}, verbosity::Int, X, y) where D
    fitresult = Distributions.fit(D, y)
    cache     = nothing
    report    = NamedTuple()
    return fitresult, cache, report
end

MLJBase.fitted_params(::ConstantRegressor, fitresult) = (target_distribution=fitresult,)

MLJBase.predict(::ConstantRegressor, fitresult, Xnew) = fill(fitresult, nrows(Xnew))

##
## THE CONSTANT DETERMINISTIC REGRESSOR (FOR TESTING)
##

struct DeterministicConstantRegressor <: Deterministic end

function MLJBase.fit(::DeterministicConstantRegressor, verbosity::Int, X, y)
    fitresult = mean(y)
    cache     = nothing
    report    = NamedTuple()
    return fitresult, cache, report
end

MLJBase.predict(::DeterministicConstantRegressor, fitresult, Xnew) = fill(fitresult, nrows(Xnew))

##
## THE CONSTANT CLASSIFIER
##

"""
    ConstantClassifier()

A classifier that, for any new input pattern, `predict`s the
`UnivariateFinite` probability distribution `d` best fitting the
training target data. So, `pdf(d, level)` is the proportion of levels
in the training data coinciding with `level`. Use `predict_mode` to
obtain the training target mode instead.
"""
struct ConstantClassifier <: Probabilistic end

# here `args` is `y` or `y, w`:
function MLJBase.fit(::ConstantClassifier, verbosity::Int, X, y, w=nothing)
    fitresult = Distributions.fit(MLJBase.UnivariateFinite, y, w)
    cache     = nothing
    report    = NamedTuple
    return fitresult, cache, report
end

MLJBase.fitted_params(::ConstantClassifier, fitresult) = (target_distribution=fitresult,)

MLJBase.predict(::ConstantClassifier, fitresult, Xnew) = fill(fitresult, nrows(Xnew))

##
## DETERMINISTIC CONSTANT CLASSIFIER (FOR TESTING)
##

struct DeterministicConstantClassifier <: Deterministic end

function MLJBase.fit(::DeterministicConstantClassifier, verbosity::Int, X, y)
    # dump missing target values and make into a regular array:
    fitresult = mode(skipmissing(y) |> collect) # a CategoricalValue or CategoricalString
    cache     = nothing
    report    = NamedTuple()
    return fitresult, cache, report
end

MLJBase.predict(::DeterministicConstantClassifier, fitresult, Xnew) = fill(fitresult, nrows(Xnew))

##
## METADATA
##

metadata_pkg.(
    (ConstantRegressor, ConstantClassifier,
     DeterministicConstantRegressor, DeterministicConstantClassifier),
    name       = "MLJModels",
    uuid       = "d491faf4-2d78-11e9-2867-c94bc002c0b7",
    url        = "https://github.com/alan-turing-institute/MLJModels.jl",
    julia      = true,
    license    = "MIT",
    is_wrapper = false)

metadata_model(ConstantRegressor,
    input   = Table(Scientific),
    target  = AbstractVector{Continuous},
    weights = false,
    descr   = "Constant regressor (Probabilistic).",
    path    = "MLJModels.ConstantRegressor")

metadata_model(DeterministicConstantRegressor,
    input   = Table(Scientific),
    target  = AbstractVector{Continuous},
    weights = false,
    descr   = "Constant regressor (Deterministic).",
    path    = "MLJModels.DeterministicConstantRegressor")

metadata_model(ConstantClassifier,
    input   = Table(Scientific),
    target  = AbstractVector{<:Finite},
    weights = true,
    descr   = "Constant classifier (Probabilistic).",
    path    = "MLJModels.ConstantClassifier")

metadata_model(DeterministicConstantClassifier,
    input   = Table(Scientific),
    target  = AbstractVector{<:Finite},
    weights = false,
    descr   = "Constant classifier (Deterministic).",
    path    = "MLJModels.DeterministicConstantClassifier")


text:
using ShirleyRayTracer
using Images
using CUDA

CUDA.allowscalar(false)

function add_random_scene!(hitables)

	push!(hitables, Sphere(Point3(0,-1000,0), 1000.0, Lambertian(0.5, 0.5, 0.5)))

	rand_material(p) = if p < 0.8
				Lambertian()
			elseif p < 0.95
				rf = randf(0.5, 1)
				Metal(rf, rf, rf, 0.5rand())
			else
				Dielectric(1.5)
			end

	for a in -11:10, b in -11:10
		center = Point3(a + 0.9rand(), 0.2, b + 0.9rand())
		if ShirleyRayTracer.magnitude(center - Point3(4, 0.2, 0)) > 0.9
			push!(hitables, Sphere(center, 0.2, rand_material(rand())))
		end
	end

	push!(hitables, Sphere(Point3(0, 1, 0), 1.0, Dielectric(1.5)))
	push!(hitables, Sphere(Point3(-4, 1, 0), 1.0, Lambertian(0.4,0.2,0.1)))
	push!(hitables, Sphere(Point3(4, 1, 0), 1.0, Metal(0.7,0.6,0.5, 0.0)))
end

function main(;filename="render.png", image_width=640, aspect_ratio=16/9, samples_per_pixel=5, max_depth=5, use_cuda=false)

	image_height = round(Int, image_width / aspect_ratio)

	hitables = Union{Sphere{Lambertian}, Sphere{Metal}, Sphere{Dielectric}}[]
	add_random_scene!(hitables)

	cam = Camera(Point3(13.,2.,3.), zero(Point3), Vec3(0,1,0), 20, aspect_ratio, 0.1, 10.0)

	if !use_cuda
		world = Scene(cam, hitables)
		arrtype = Array
	else
		world = Scene(cam, hitables) |> cu
		arrtype = CuArray
	end

	@time image = render(arrtype, world, image_width, image_height, samples_per_pixel, max_depth) |> Array

	save(filename, image)
end

main()
main(use_cuda=true)

main(samples_per_pixel=50)
main(samples_per_pixel=50, use_cuda=true)


text:
################################################################################
# tx.jl
#
# Functional multi-threading tools built around atomic transactions.
#
# @author Noah C. Benson
#
# MIT License
# Copyright (c) 2020-2021 Noah C. Benson

import DataStructures

# The beginning of this file contains the various types and simple functions
# that depend only on those types (like show). These types end with the
# Transaction type.
# The remainder of this file contains methods that depend on transaction
# details. These include all read and write operations for Actors and Volatiles.

# #Actor #######################################################################
# The (private) type of a message that is queued for an actor to eventually run.
"""
    ActorMsg

A message, queued for an `Actor` via the `send()` function.

The `ActorMsg` struct is considered part of `Air`'s private/internal
implementation details.
"""
struct ActorMsg
    fn::Function
end
const ActorMsgQueue = DataStructures.Queue{ActorMsg}
"""
    ActorException{T}(error, value, argno, args)

An ActorException object is thrown whenever one attempts to obtain the value
of or send a function to an actor that is in an error state. An error state
occurs when an unhandled exception is raised while an actor is processing a
sent function. In such a case the `geterror()` and `restart()` functions may
be used. The `geterror()` function yields an `ActorException` object in which
the exception that was raised is stored as `error`, the value of the actor when
the send was run is stored in `value`, and the `args` and `argno` give the
full contex of the `send` call (i.e., the fucntion followed by the arguments is
`args` and the argument number that corresponds to the actor is `argno`).

See also: [`Actor`](@ref), [`geterror`](@ref), [`reset`](@ref), [`send`](@ref)
"""
struct ActorException{T} <: Exception
    error::Any
    value::T
    fn::Function
    queue::Tuple
end
export ActorException
Base.show(io::IO, ::MIME"text/plain", a::ActorException{T}) where {T} = begin
    print(io, "ActorException{$T}($(typeof(a.error)), $(a.value), ...)")
end
Base.show(io::IO, a::ActorException{T}) where {T} = begin
    print(io, "ActorException{$T}($(typeof(a.error)))")
end
const ActorValue{T} = Union{Some{T}, ActorException{T}} where {T}
"""
    Actor{T}

An actor is an object that represents a worker-thread to which tasks can be
scheduled. Any scheduled function is guaranteed to be evaluated at some point in
the future in some other thread, and the return value of that function will
become the new value held by the actor. Each function, when it is run, is passed
the actor's value as one of its argument.

Like with `Ref`s, you can access an actor's current value using `getindex`,
e.g., `actor[]`. Within a transaction, this will be guaranteed to remain fixed
for the duration of the transaction; outside of a transaction, this may change
at arbitrary times.

`Actor`s may be initialized with post-processing functions. These functions are
called immediately after executing any scheduled function and are given as their
first argument the original actor value and as their second argument the return
value of the scheduled function. Instead of saving this return value, the
return value of the post-processing function is saved in the actor. This is
useful for actors whose job is, for example, to serialize output to a log file
being written to across many threads. If we run something like
`send(println, log_actor, "Some log message")` the `println` function will
return `nothing`, which we would like to convert back into the log stream so
that subsequent `send` calls can continue to use the `println` fuction.

Actors may additionally be given an error handler. Whenever an exception occurs
during a scheduled function, the error handler will be called with the arguments
of (1) the actor object, (2) the current actor value, and (3) the exception that
was caught. When an error has occurred, any attempt to schedule a function to
the actor or to read from the actor will cause an exception to be raised. The
error may be examined using `geterror(actor)` and restarted using
`reset(actor)`.

All fields in an actor object should be considered strictly private. These
fields are likely to change between releases, and changing the values will break
your code.

See also: [`tx`](@ref), [`@tx`](@ref), [`send`](@ref), [`geterror`](@ref),
[`reset`](@ref).

# Examples

```@meta
DocTestSetup = quote
    using Air
end
```

```jldoctest; filter=r"Actor{Symbol}\\(@[0-9a-zA-Z]+: :start_sym\\)"
julia> a = Actor{Symbol}(:start_sym)
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> send(a) do val; sleep(0.1); Symbol("new_\$(val)") end
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> sleep(1); a[]
:new_start_sym
```
"""
mutable struct Actor{T} <: TransactionalRef{T}
    mutex::ReentrantLock
    queue::ActorMsgQueue
    task::Union{Nothing, Task}
    value::ActorValue{T}
    function Actor{T}(initval) where {T}
        return new{T}(ReentrantLock(), ActorMsgQueue(), nothing, Some{T}(initval))
    end
end
export Actor
Actor(t::T) where {T} = Actor{T}(t)
Base.propertynames(::Actor) = (:state,:value,:error)
Base.getproperty(a::Actor{T}, k::Symbol) where {T} = begin
    if k == :state
        return (geterror(a) === nothing ? :okay : :error)
    elseif k == :value
        return a[]
    elseif k == :error
        return geterror(a)
    else
        error("type $(typeof(a)) has no field $k")
    end
end
Base.show(io::IO, ::MIME"text/plain", a::Actor{T}) where {T} = begin
    print(io, "Actor{$T}(@")
    print(io, string(objectid(a), base=62))
    print(io, ": ")
    s = getfield(a, :value)
    if isa(s, ActorException{T})
        print(io, "--error--")
    else
        show(io, something(s))
    end
    print(io, ")")
end
Base.show(io::IO, a::Actor{T}) where {T} = begin
    print(io, "Actor{$T}(@")
    print(io, string(objectid(a), base=62))
    print(io, ")")
end
Base.setindex!(a::Actor{T}, args...) where {T} = error(
    "Actor objects cannot be assigned---they can only be sent functions")
# The private main-loop that processes an actor's messages.
"""
    actor_main(actor)

The actor main function is what actually manages everything behind the scenes in
the actor tasks. Note that this function and the task/thread it is running in is
the only place that the actor's value can be legally changed. Changing the value
anywhere else can result in undefined behavior. The actor's condition should
never change.

This function is designed to process all the messages in an actor's queue then
to return. When a new thread wants to enqueue a message to an actor that has an
empty queue, that thread must spin up the `actor_main` function to process the
items in the queue (this is done by the `send` function automatically).

This function is part of `Air`'s internal/private implementation details.
"""
actor_main(a::Actor{T}) where {T} = begin
    mux = getfield(a, :mutex)
    msg = nothing
    args = nothing
    rval = nothing
    # For starters, lock the condition!
    lock(mux)
    try
        queue = getfield(a, :queue)
        value = getfield(a, :value)
        isa(value, ActorException{T}) && return nothing
        # This function is a loop! We process everything in the queue until it
        # is empty, then we exit. If the queue is empty right now, then there's
        # nothing to process (which would be weird).
        while !isempty(queue)
            val = value.value # (value is a Some{T})
            # get the first item in the queue
            msg = DataStructures.dequeue!(queue)
            # Having grabbed that message, we can go ahead and unlock.
            unlock(mux)
            # Okay, we've grabbed the message we want to process, and we've
            # unlocked so that we can do the processing without halting other
            # tasks from queuing up new messages.
            # Note that even though we don't hold the condition, this task is
            # the only one that's allowed to update the value, so the value is
            # effectively task-local.
            f = msg.fn
            try
                value = Some{T}(f(val))
            catch e
                # There was an exception, so we need to lock the actor and set
                # the error state.
                lock(mux)
                e = ActorException{T}(e, val, f, (queue...,))
                setfield!(a, :value, e)
                empty!(queue)
                return nothing
            end
            # Relock the condition to update the value.
            lock(mux)
            setfield!(a, :value, value)
            # At this point we have updated the actor's value and we still
            # hold the lock. We can go ahead and enter the next loop.
        end
        # If we reach this point, then the queue is empty, so we can exit.
    finally
        setfield!(a, :task, nothing)
        unlock(mux)
    end
    return nothing
end
"""
    actor_start!(actor)

If the given `Actor` object is not in an error state, is not running, and does
not have an empty queue, then `actor_start!(actor)` will start `actor`'s
processing task. This should only be called by `send!` when the first message to
an empty queue is finalized.

If either the actor is already running or the task is successfully started, then
that task is yielded. If the actor has an empty queue, then nothing is returned,
and if the actor is in an error state, then an exception is thrown.

Note: It is required that the mutx for the given actor be held when this
function is called; otherwise race conditions could be inadvertantely created.
The [`actor_start`](@ref) method locks the actor's mutex then calls this
function. Both methods are private to `Air` and should not generally be used
outside of it.

This function is considered part of `Air`'s internal/private implementation
details.
"""
actor_start!(a::Actor{T}) where {T} = begin
    task = getfield(a, :task)
    (task === nothing) || return task
    queue = getfield(a, :queue)
    isempty(queue) && return nothing
    isa(getfield(a, :value), ActorException{T}) && return nothing
    # It needs a task! Start one up!
    task = Threads.@spawn actor_main(a)
    setfield!(a, :task, task)
    return task
end
"""
    actor_start(actor)

If the given `Actor` object is not in an error state, is not running, and does
not have an empty queue, `actor_start(actor)` will start `actor`'s processing
task. This should only be called by `send!` when the first message to an empty
queue is finalized.

If either the actor is already running or the task is successfully started, then
that task is yielded. If the actor has an empty queue, then nothing is returned,
and if the actor is in an error state, then an exception is thrown.

This function is considered part of `Air`'s internal/private implementation
details.
"""
actor_start(a::Actor{T}) where {T} = begin
    mux = getfield(a, :mutex)
    lock(mux)
    try
        return actor_start!(a)
    finally
        unlock(mux)
    end
end
"""
    actor_send!(actor, message)

Enqueues the given message to the given actor and starts the actor's task, if
necessary. This function requires that the actor's condition be held. Yields the
actor.

This function is considered part of `Air`'s internal/private implementation
details.
"""
actor_send!(a::Actor{T}, msg::ActorMsg) where {T} = begin
    val = getfield(a, :value)
    isa(val, ActorException{T}) && throw(val)
    DataStructures.enqueue!(getfield(a, :queue), msg)
    (getfield(a, :task) === nothing) && actor_start!(a)
    return a
end
actor_send!(a::Actor{T}, qq::ActorMsgQueue) where {T} = begin
    val = getfield(a, :value)
    isa(val, ActorException{T}) && throw(val)
    if !isempty(qq)
        for msg in qq
            DataStructures.enqueue!(getfield(a, :queue), msg)
        end
        (getfield(a, :task) === nothing) && actor_start!(a)
    end
    return a
end
"""
    actor_reset!(actor, val)

Resets the given actor to have the given value. If the actor is not in an error
state, yields `nothing` and does nothing. Otherwise, yields the `ActorExcption`
object that was cleared. It is required that the actor's mutex be held at ths
time this function is called.

This function is considered part of `Air`'s internal/private implementation
details.
"""
actor_reset!(a::Actor{T}, val) where {T} = begin
    val0 = getfield(a, :value)
    isa(val0, ActorException{T}) || return nothing
    setfield!(a, :value, Some{T}(val))
    empty!(getfield(a, :queue))
    # Since the queue is, as of yet, empty, there's no need to start a task.
    return val0
end

# #Volatile ####################################################################
# Volatile's, like Actor's, have a single immutable state structure.
struct VolatileData{T}
    value::T
    filter::Union{Nothing, Function}
    finalize::Union{Nothing, Function}
    function VolatileData{T}(v::S, flt::Function, fin::Function) where {T,S}
        return new{T}(flt(v), flt, fin)
    end
    function VolatileData{T}(v::S, flt::Function, ::Nothing) where {T,S}
        return new{T}(flt(v), flt, nothing)
    end
    function VolatileData{T}(v::S, ::Nothing, fin::Function) where {T,S}
        return new{T}(v, nothing, fin)
    end
    function VolatileData{T}(v::S, ::Nothing, ::Nothing) where {T,S}
        return new{T}(v, nothing, nothing)
    end
    function VolatileData{T}(v::S, flt::Function) where {T,S}
        return new{T}(flt(v), flt, nothing)
    end
    function VolatileData{T}(v::S, ::Nothing) where {T,S}
        return new{T}(v, nothing, nothing)
    end
    function VolatileData{T}(v::S) where {T,S}
        return new{T}(v, nothing, nothing)
    end
end
"""
    voldata_finalize(voldata)

Calls the finalize function of the given volatile state object (of type
`VolatileData`) if necessary. Yields the new `VolatileData` object, which may be
`voldata` if there is no finalizer or nothing has changed.

This function is a private/internal implementation detail of the `Air` library.
"""
voldata_finalize(v::VolatileData{T}) where {T} = begin
    (v.finalize === nothing) && return v
    u = v.finalize(v.value)
    return (u === v.value ? v : VolatileData{T}(u, v.filter, v.finalize))
end
"""
    Volatile{T}(value)
    Volatile{T}(value, filter_fn)
    Volatile{T}(value, filter_fn, finalize_fn)

`Volatile` objects are `Ref` objects that must be used in conjunction with
transaction blocks (see [`@tx`](@ref) and [`tx`](@ref)). The value of a
`Volatile` can be accessed at any time, and there is no particular guarantee
that a `Volatile`'s value won't be changed by another thread outside of a
transaction, thus reading them outside of a transaction can potentially create
race conditions. Critically, `Volatile` objects can only be set inside of a
transaction, and, within a transaction, a `Volatile`'s value is guaranteed to be
constant. In other words, `Volatile` objects are `Ref` objects that behave as if
they are locked for the executing thread whenever that thread is inside a
transaction block.

The arguments `filter_fn` and `finnalize_fn` are functions for making sure that
the value of a volatile conforms to some standard. The two functions are similar
but are intended for slightly different use cases:
 * `filter_fn` is called every time the value of a volatile is set. The value
   saved in the volatile is `filter_fn(value)` instead of `value` itself. Values
   are only filtered when the volatile is set directly (`vol[] = value`)---the
   filter is not rerun when the `filter_fn` is changed (via `setfilter!`) nor
   when the `finalize_fn` returns a value.
 * `finalize_fn` is called with the volatile's value immediately after the body
   of the transaction has completed, but before a commit is attempted. The
   `finalize_fn` function acts like the `filter_fn` in that `finalize_fn(value)`
   replaces `value` in the volatile.
Both `filter_fn` and `finalize_fn` can throw exceptions, which will abort the
running transaction.

For a `Volatile` `v`, one may set `v`'s stored value via `v[] = value`. This must
be done inside a transaction (via `tx` or `@tx`). To change the filter or the
finalize functions, use the `setfilter!` and `setfinalize!` functions, both
of which also must be run in transactions as well.

See also: [`tx`](@ref), [`@tx`](@ref), [`setfilter!`](@ref),
[`setfinalize!`](@ref).

# Examples

```@meta
DocTestSetup = quote
    using Air
end
```

```jldoctest; filter=r"Volatile{Symbol}\\(@[0-9a-zA-Z]+: :startval\\)"
julia> v = Volatile{Symbol}(:startval)
Volatile{Symbol}(@JIm7aUS2sOl: :startval)

julia> @tx v[] = :newval
:newval

julia> v[]
:newval
```
"""
mutable struct Volatile{T} <: TransactionalRef{T}
    mutex::ReentrantLock
    value::VolatileData{T}
    function Volatile{T}(val::S) where {T,S}
        return new{T}(ReentrantLock(), VolatileData{T}(val, nothing, nothing))
    end
    function Volatile{T}(val::S, f::Function) where {T,S}
        return new{T}(ReentrantLock(), VolatileData{T}(val, f, nothing))
    end
    function Volatile{T}(val::S, ::Nothing) where {T,S}
        return new{T}(ReentrantLock(), VolatileData{T}(val, nothing, nothing))
    end
    function Volatile{T}(val::S, f::Function, ::Nothing) where {T,S}
        return new{T}(ReentrantLock(), VolatileData{T}(val, f, nothing))
    end
    function Volatile{T}(val::S, f::Function, ff::Function) where {T,S}
        return new{T}(ReentrantLock(), VolatileData{T}(val, f, ff))
    end
    function Volatile{T}(val::S, ::Nothing, ff::Function) where {T,S}
        return new{T}(ReentrantLock(), VolatileData{T}(val, nothing, ff))
    end
    function Volatile{T}(val::S, ::Nothing, ::Nothing) where {T,S}
        return new{T}(ReentrantLock(), VolatileData{T}(val, nothing, nothing))
    end
end
export Volatile
Volatile(t::T) where {T} = Volatile{T}(t)
Volatile(t::T, filterfn::Union{Nothing,Function},
         finfn::Union{Nothing,Function}=nothing) where {T} = Volatile{T}(t, filterfn, finfn)
Base.show(io::IO, ::MIME"text/plain", v::Volatile) = begin
    show(io, typeof(v))
    print(io, "(@")
    print(io, string(objectid(v), base=62))
    print(io, ": ")
    show(io, v[])
    print(io, ")")
end
Base.show(io::IO, v::Volatile) = begin
    show(io, typeof(v))
    print(io, "(@")
    print(io, string(objectid(v), base=62))
    print(io, ")")
end
Base.propertynames(::Volatile) = (:value,)
Base.getproperty(v::Volatile, p) = begin
    if p == :value
        return v[]
    else
        error("type $(typeof(v)) has no field $p")
    end
end

# #Transaction #################################################################
"""
    ActorTxData{T}

The data tracked for an `Actor{T}` during a transaction by the transaction's
`Transaction` struct.

The `ActorTxData` struct is considered part of the internal/private code
of Air and generally should not be used outside of the Air library.
"""
mutable struct ActorTxData{T}
    start_value::ActorValue{T}
    tx_value::ActorValue{T}
    reset_value::Union{Nothing, Some{T}}
    msgs::ActorMsgQueue
    function ActorTxData{T}(w::S) where {T,S}
        return new{T}(w, w, nothing, ActorMsgQueue())
    end
end
"""
    TxRetryException

A `TxRetryException` is throws when a transaction needs to be retried but an
error hasn't necessarily been generated otherwise. This can be thrown during a
transaction to restart the transaction; though doing this can easily lead to
infinite loops.
"""
struct TxRetryException <: Exception end
export TxRetryException
"""
    Transaction

A transaction object keeps track of what is going on during a particular
transaction. These are generally low-level objects that shouldn't be
touched directly.

Transactions have the following propertiies:
* `state` is either `:running`, `:validating`, or `:error`;
* `rvolatiles` is the set of all `Volatile` objects that have been read during
  the transaction;
* `wvolatiles` is the set of all `Volatile` objects that have been changed;
* `actors` is the set of all `Actor` objects to which messagees have been sent
  during the transaction; and
* `sources` is the set of all `Source` objects from which items have been
  popped.
"""
mutable struct Transaction
    state::Symbol
    reads::IdDict{Volatile, VolatileData}
    writes::IdDict{Volatile, NTuple{2,VolatileData}}
    actors::IdDict{Actor, ActorTxData}
    Transaction() = new(:running,
                        IdDict{Volatile, VolatileData}(),
                        IdDict{Volatile, NTuple{2,VolatileData}}(),
                        IdDict{Actor, ActorTxData}())
end
export Transaction
Base.propertynames(::Transaction) =
    (:state, :rvolatiles, :wvolatiles, :actors)
Base.getproperty(t::Transaction, p) = begin
    if p == :state
        return getfield(t, :state)
    elseif p == :rvolatiles
        return Base.IdSet(keys(getfield(p, :reads)))
    elseif p == :wvolatiles
        return Base.IdSet(keys(getfield(p, :writes)))
    elseif p == :actors
        return Base.IdSet(keys(getfield(p, :actors)))
    else
        error("type $(typeof(t)) has no field $p")
    end
end
tx_clear!(t::Transaction) = begin
    setfield!(t, :state, :running)
    empty!(getfield(t, :reads))
    empty!(getfield(t, :writes))
    empty!(getfield(t, :actors))
    return nothing
end

# The current running transaction; see currtx() below.
"""
    current_tx

The `current_tx` constant is a `Var{T}` that stores the current task's running
`Transaction`, or `nothing` if there is no transaction running.

See also: [`currtx`](@ref)
"""
@var current_tx = nothing::Union{Nothing, Transaction}

"""
    currtx()

Yields the current `Transaction` object, or `nothing` if there is no transaction
running in the current task.

See also: [`Transaction`](@ref)

# Examples

```@meta
DocTestSetup = quote
    using Air
end
```

```jldoctest
julia> currtx() === nothing
true

julia> @tx (currtx() === nothing)
false
```
"""
currtx() = current_tx[]
export currtx

"""
    TX_MAX_ATTEMPTS

The maximum number of retry attempts that a transaction will make before
aborting the transaction.
"""
const TX_MAX_ATTEMPTS = 2^14

"""
    tx(fn)

Runs the given function in a transaction and yields the result. The function
fn is called as `fn()` without arguments.
"""
function tx(fn::F) where {F <: Function}
    success = false
    res = nothing
    the_tx = current_tx[]
    # If there's already a transaction running, we needn't make a new one---
    # This transaction function will just get rolled up into the current one.
    (the_tx === nothing) || return fn()
    the_tx = Transaction()
    # We need to make a new transaction for this task. We might have to do
    # this a few times if the transaction fails.
    for attempt in 1:TX_MAX_ATTEMPTS
        # Clear the transaction
        tx_clear!(the_tx)
        try
            res = withvars(fn, current_tx => the_tx)
        catch e
            if isa(e, TxRetryException)
                continue
            else
                rethrow()
            end
        end
        # At this point the transaction is over and we need to ensure that the
        # current volatile values are consistent with the transaction we just
        # executed. Before we do that, we should finalize the volatiles.
        writes = getfield(the_tx, :writes)
        setfield!(the_tx, :state, :finalizing)
        for (v,(x0,x1)) in writes
            x = voldata_finalize(x1)
            if x !== x1
                setfield!(the_tx, :state, :running)
                volatile_setindex!(v, x, the_tx)
                setfield!(the_tx, :state, :finalizing)
            end
        end
        # We're now ready to attempt a commit. To do this we must lock the vols
        # and actors in a sorted order. We do volatiles first, since their
        # values must all be tested, then actors.
        reads = getfield(the_tx, :reads)
        actors = getfield(the_tx, :actors)
        setfield!(the_tx, :state, :locking)
        nw = length(writes)
        nr = length(reads)
        n  = nw + nr
        m  = length(actors)
        vols = Vector{Volatile}(undef, n)
        vols[1:nr] .= keys(reads)
        vols[nr+1:end] .= keys(writes)
        sort!(vols, by=objectid)
        acts = Vector{Actor}(undef, m)
        acts[1:m] .= keys(actors)
        sort!(acts, by=objectid)
        locked = 0
        checked = 0
        success = false
        try
            while !success
                # First, go through and lock all the volatiles in order.
                for vol in vols
                    lock(getfield(vol, :mutex))
                    locked += 1
                end
                # Now check their values. First do the read volatiles.
                for (vol,x0) in reads
                    (x0 === getfield(vol, :value)) || break
                    checked += 1
                end
                (checked == nr) || break
                # Next, check the written volatiles.
                for (vol,(x0,x)) in writes
                    (x0 === getfield(vol, :value)) || break
                    checked += 1
                end
                (checked == n) || break
                # Next we need to lock and check the actors that were read from.
                for act in acts
                    lock(getfield(act, :mutex))
                    locked += 1
                end
                for (act,dat) in actors
                    val = getfield(act, :value)
                    (dat.start_value === val) || break
                    checked += 1
                end
                (checked == n + m) || break
                # If we reach this point, then everything checks out and we can
                # commit all of the changes and complete the transaction.
                setfield!(the_tx, :state, :committing)
                for (vol,(x0,x)) in writes
                    setfield!(vol, :value, x)
                end
                # We need to commit the actors as well.
                for (act,dat) in actors
                    q = dat.msgs
                    r = dat.reset_value
                    (r === nothing) || actor_reset!(act, r.value)
                    isempty(q) || actor_send!(act, q)
                end
                # That's all we need to do, aside from unlock.
                success = true
            end
        finally
            for mux in 1:locked
                (mux > n) && break
                unlock(getfield(vols[mux], :mutex))
            end
            locked -= n
            if locked > 0
                for mux in 1:locked
                    #(mux > m) && break
                    unlock(getfield(acts[mux], :mutex))
                end
            end
        end
        success && break
    end
    # It's possible we got here because we were suceessful, but it might be that
    # we failed too many times.
    if success
        return res
    else
        error("transaction aborted after failing $TX_MAX_ATTEMPTS times")
    end
end
export tx

"""
    @tx expr

The macro @tx should be followed by an expression; that expression is run in an
atomic transaction.
"""
macro tx(expr)
    return :(tx(() -> $(esc(expr))))
end
export @tx

# Now that we have definend transactions, we can define the volatile access
# methods.
_volatile_getindex(v::Volatile{T}, t::Transaction) where {T} = begin
    w = get(getfield(t, :writes), v, nothing)
    (w === nothing) || return w[2]
    w = get(getfield(t, :reads), v, nothing)
    (w === nothing) || return w
    w = getfield(v, :value)
    getfield(t, :reads)[v] = w
    return w
end
_volatile_getindex(v::Volatile{T}, ::Nothing) where {T} = getfield(v, :value)
Base.getindex(v::Volatile{T}) where {T} = _volatile_getindex(v, currtx()).value
volatile_setindex!(v::Volatile{T}, x::VolatileData{T}, t::Transaction) where {T} = begin
    (getfield(t, :state) === :running) || error(
        "volatiles can only be set before finalizing the transaction")
    # See if it this ref has already been written to in this transaction.
    w = get(getfield(t, :writes), v, nothing)
    if w !== nothing
        (x0,x1) = w
        # If this doesn't change anything, don't do aything.
        (x === x1) && return x
        # If this changes the volatile back to its inintial value, we convert
        # this to a read operation. Otherwise we record the new value.
        if x === x0
            delete!(getfield(t, :writes), v)
            getfield(t, :reads)[v] = x0
        else
            getfield(t, :writes)[v] = (x0, x)
        end
        return x
    end
    # See if this volatile has already been read.
    w = get(getfield(t, :reads), v, nothing)
    if w !== nothing
        # There is a previously-read value already. If it hasn't changed, it's
        # still just a read value.
        (w === x) && return x
        # Otherwise, we are going to promote it to a written value.
        delete!(getfield(t, :reads), v)
        getfield(t, :writes)[v] = (w, x)
    else
        # There is no previously-read value; grab the current value.
        w = getfield(v, :value)
        if w === x
            getfield(t, :reads)[v] = w
        else
            getfield(t, :writes)[v] = (w, x)
        end
    end
    return x
end
volatile_setindex!(v::Volatile{T}, x::S, ::Nothing) where {T,S} = error(
    "cannot set volatile outside of transaction")
Base.setindex!(v::Volatile{T}, x::S) where {T,S} = begin
    value = getfield(v, :value)
    # Run the filter on the new value.
    (value.filter === nothing) || (x = value.filter(x))
    newdat = VolatileData{T}(x, value.filter, value.finalize)
    return volatile_setindex!(v, newdat, currtx())
end
"""
    getfilter(v)

Yields the filter-function for the volatile v.
"""
getfilter(v::Volatile{T}) where {T} = _volatile_getindex(v, currtx()).filter
export getfilter

"""
    getfinalize(v)

Yields the finalize-function for the volatile v.
"""
getfinalize(v::Volatile{T}) where {T} = _volatile_getindex(v, currtx()).finalize
export getfinalize
"""
    setfilter!(vol, fn)

Sets the filter-function associated with the Volatile object vol. Any time that
the vol is set (`vol[] = x`) the filter-function is called and the value saved
in `vol` is instead `fn(x)`. This must be called within a transaction.
"""
setfilter!(vol::Volatile{T}, f::Function) where {T} = begin
    value = getfield(v, :value)
    newdat = VolatileData{T}(value.value, f, value.finalize)
    return volatile_setindex!(v, newdat, currtx())
end
export setfilter!
"""
    setfinalize!(vol, fn)

Sets the finalize-function associated with the Volatile object vol. Any time
that a transaction contains a change to `vol`, immediately prior to making an
attempt at committing the transaction, the finalize function is called and
the value committed to `vol` is instead `fn(x)` where `x` is the value set to
`vol` in the transaction. This funvtion must also be called within a
transaction.
"""
setfinalize!(vol::Volatile{T}, f::Function) where {T} = begin
    value = getfield(v, :value)
    newdat = VolatileData{T}(value.value, value.filter, f)
    return volatile_setindex!(v, newdat, currtx())
end
export setfinalize!

# Same for actors: we can put the actor code here.
# First, we definne some private functions for handling things on the back
# end.
"""
    tx_actordata(a)

Yields the in-transaction data for the `Actor` object `a`. If `a` is in an
error-state, then throws an `ActorException`. If there is no transaction
currently running, yields `nothing`.

This function is considered part of the internal/private interface of `Air` and
shouldn't generally be called outside of it.
"""
tx_actordata(a::Actor{T}) where {T} = tx_actordata(a, currtx())
tx_actordata(a::Actor{T}, t::Transaction) where {T} = begin
    # See if we've already grabbed it in the transaction.
    actors = getfield(t, :actors)
    w = get(actors, a, nothing)
    if w === nothing
        val = getfield(a, :value)
        w = ActorTxData{T}(val)
        actors[a] = w
    end
    return w
end
tx_actordata(a::Actor{T}, ::Nothing) where {T} = nothing

"""
    actor_value(a)

Yields the in-transaction value for the given `Actor` object `a` if there is a
running transaction; otherwise yields the current out-of-transaction value of
`a`.

This function is considered part of the internal/private interface of `Air` and
shouldn't generally be called outside of it.
"""
actor_value(a::Actor{T}) where {T} = actor_value(a, currtx())
actor_value(a::Actor{T}, t::Transaction) where {T} = tx_actordata(a, t).tx_value
actor_value(a::Actor{T}, ::Nothing) where {T} = getfield(a, :value)
Base.getindex(a::Actor{T}) where {T} = begin
    x = actor_value(a, currtx())
    isa(x, ActorException{T}) && throw(x)
    return x.value
end

"""
    actor_reset(a, s)

If the given `Actor` object `a` is in an error state, this function resets the
actor to have the new value `s` then yields the `ActorException` object that was
just cleared. If `a` is not in an error state, this function simply yields
`nothing`.

This function is considered part of the internal/private interface of `Air` and
shouldn't generally be called outside of it.
"""
actor_reset(a::Actor{T}, x::S) where {T,S} = actor_reset(a, x, currtx())
actor_reset(a::Actor{T}, newval::S, t::Transaction) where {T,S} = begin
    (getfield(t, :state) === :running) || error(
        "actors can only be reset before finalizing the transaction")
    w = tx_actordata(a, t)
    val = w.tx_value
    isa(val, ActorException{T}) || return nothing
    newval = Some{T}(newval)
    w.reset_value = newval
    w.tx_value = newval
    return val
end
actor_reset(a::Actor{T}, newval::S, ::Nothing) where {T,S} = begin
    # We're not in a transaction, so we just lock and queue.
    mux = getfield(a, :mutex)
    lock(mux)
    try
        val = getfield(a, :value)
        isa(val, ActorException{T}) || return false
        return actor_reset!(a, newval)
    finally
        unlock(mux)
    end
end
"""
    reset(actor, x)

If the given actor is in an error state, this (1) resets it, meaning it will
start handling sent messages again, (2) gives it the new initial value `x`, and
(3) yields the `ActorException` object that was just clared.  If the actor is
not in an error state, this just yields `nothing`.
"""
reset(a::Actor{T}, s::S) where {T,S} = actor_reset(a, s, currtx())
export reset

"""
    actor_send(a, msg)

Adds the given `ActorMsg` message object `msg` to the queue of the given `Actor`
object `a` such that it will be processed in a separate thread. Yields
`nothing`.

This function is considered part of the internal/private interface of `Air` and
shouldn't generally be called outside of it.
"""
actor_send(a::Actor{T}, msg::ActorMsg) where {T} = actor_send(a, msg, currtx())
actor_send(a::Actor{T}, msg::ActorMsg, t::Transaction) where {T} = begin
    (getfield(t, :state) === :running) || error(
        "actors can only be sent functions before finalizing the transaction")
    w = tx_actordata(a, t)
    isa(w.tx_value, ActorException{T}) && throw(w.tx_value)
    DataStructures.enqueue!(w.msgs, msg)
    return nothing
end
actor_send(a::Actor{T}, msg::ActorMsg, ::Nothing) where {T} = begin
    # We're not in a transaction, so we just lock and queue.
    mux = getfield(a, :mutex)
    lock(mux)
    try
        # There could be an error on the actor; if so we want to throw an
        # exception ourself.
        val = getfield(a, :value)
        isa(val, ActorException{T}) && throw(val)
        # The actor isn't in an error state; we can schedule the function.
        return actor_send!(a, msg)
    finally
        unlock(mux)
    end
end
"""
   send(fn, actor)

The `send()` method can be used to send a function to an actor. The function
`fn` is put in the actor's queue, which is processed sequentially by a separate
thread.  In the actual function call that is evaluated in this separate thread,
the function is passed the stored value of the actor, and the return value of
the function becomes the new stored value of the actor.

Note that the value of an actor when `send()` is called does not make any
guarantee about its value when the function that is sent gets evaluated---other
functions may be processed in the interim.

If `send()` is called inside of a transaction, then the function is not
immediately queued but rather is held until the transaction successfully
completes. Once this happens, all sends are dispatched simultaneously such that
two sends to the same actor during the same transaction will always run
back-to-back in the actor's processing thread.

**Important**. Due to the design pattern described in the previous paragraph,
one should never wait on the result of a function sent to an actor during a
transaction. This mistake might be made, for example, by sendng an actor, during
a transactoin, a function that delivers some result to a [`Promise`](@ref)
object. If the sending thread then waits on that promise while still in the same
transaction then it will deadlock because the sent function is not put in the
actor's queue until the enclosing transaction is finished.  Thus the promise
will never recieve the result from the actor.

TL;DR---When you touch an actor in a transaction, it's value freezes inside the
transaction. So if the transaction somehow waits for the actor to update or to
do something, that transaction will deadlock.
"""
send(f::Function, a::Actor{T}) where {T} = actor_send(a, ActorMsg(f), currtx())
export send

"""
    geterror(actor)

If the given `Actor` object is currently in an error state, then yields the
`ActorException` object that describes the error. Otherwise, yields `nothing`.

See also: [`Actor`](@ref), [`reset`](@ref), [`send`](@ref)

# Examples

```@meta
DocTestSetup = quote
    using Air
end
```

```jldoctest; filter=r"Actor{Symbol}\\(@[0-9a-zA-Z]+: (:start_sym|--error--)\\)"
julia> a = Actor{Symbol}(:start_sym)
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> geterror(a) === nothing
true

julia> send(a) do val; error("test") end
Actor{Symbol}(@JIm7aUS2sOl: :start_sym)

julia> sleep(1); a
Actor{Symbol}(@JIm7aUS2sOl: --error--)

julia> geterror(a) isa ActorException
true
```
"""
geterror(a::Actor{T}) where {T} = begin
    w = actor_value(a, currtx())
    return isa(w, ActorException{T}) ? w : nothing
end
export geterror

"""
    TxIO

The `TxIO` type is a derivative of the `IO` class and is intended for handling
output to streams across multiple threads in a thread-safe way. `TxIO` objects
can be constructed from `IO` objects and can be printed/written to like normal
`IO` objects. However, all output is performed in a separate asynchronous
thread, and writes that occur during a transaction always occur only if the
transaction succeeds.

See also: [`Actor`](@ref), [`airout`](@ref)
"""
struct TxIO <: IO
    io::IO
    actor::Actor{IO}
end
TxIO(io::IO) = begin
    iswritable(io) || error("TxIO works only with writable IO objects")
    return TxIO(io, Actor{IO}(io))
end
export TxIO
Base.isreadable(io::TxIO) = false
Base.iswritable(io::TxIO) = true
Base.eof(io::TxIO) = eof(io.io)
Base.write(io::TxIO, x::UInt8) = send(io.actor) do io;
    write(io, x)
    return io
end
Base.write(io::TxIO, x::Char) = send(io.actor) do io;
    write(io, x)
    return io
end
Base.write(io::TxIO, x::Union{String,SubString{String}}) = send(io.actor) do io;
    write(io, x)
    return io
end

"""
    AirOut

The `AirOut` type is a derivative of the `IO` class and is intended for handling
output to `stdout` across multiple threads in a thread-safe way. It is similar
`TxIO` except that it only writes to `stdout` and is only really intended for
use with the `airout` object.

See also: [`Actor`](@ref), [`airout`](@ref), [`TxIO`](@ref)
"""
struct AirOut <: IO
    actor::Actor{Nothing}
    function AirOut()
        return new(Actor{Nothing}(nothing))
    end
end
Base.isreadable(io::AirOut) = false
Base.iswritable(io::AirOut) = true
Base.eof(io::AirOut) = eof(stdout)
Base.write(io::AirOut, x::UInt8) = let so = stdout
    return send(io.actor) do io;
        write(so, x)
        return io
    end
end
Base.write(io::AirOut, x::Char) = let so = stdout
    return send(io.actor) do io;
        write(so, x)
        return io
    end
end
Base.write(io::AirOut, x::Union{String,SubString{String}}) = let so = stdout
    return send(io.actor) do io;
        write(so, x)
        return io
    end
end

"""
    airout

The `airout` object is a `AirOut` object that wraps the `stdout` object.

See also: [`AirOut`](@ref)
"""
const airout = AirOut()
export airout


text:
using LinearAlgebra
using ForwardDiff
using Plots
using Distributions

# Bundled Gradients through Contact via Randomized Smoothing

function gd(x0; iters=1000, verbose=true, visualize=true)
    x = copy(x0)
    α = 0.001

    for i = 1:iters
        grad = ∇f(x)
        x -= α * grad
    end

    if verbose
        println("grad. norm: ", norm(∇f(x)))
    end

    if visualize
        x_range = range(-1.0, stop = 1.0, length = 1000)
        plt = plot(x_range, f.(x_range), label="", color=:black, width=2.0)
        plt = scatter!([x0,], [f(x0),], color=:red, label="")
        plt = scatter!([x,], [f(x),], color=:green, label="")
        display(plt)
    end

    return x
end

function gd(x0; iters=1000, verbose=true, visualize=true,
    mode=:gradient,
    μ=0.0, σ=[0.001], σ_schedule=1000, N=100,
    xmin=-1.0, xmax=1.0)
    x = copy(x0)
    α = 0.001

    for s in σ
        dist = Normal(μ, s)
        for i = 1:iters
            if mode == :gradient
                grad = ∇f(x)
            elseif mode == :smooth_gradient
                grad = sum([∇f(x + rand(dist, 1)[1]) for j = 1:N]) / N
            elseif mode == :zero_order_gradient
                fj = []
                for j = 1:N
                    ϵ = rand(dist, 1)[1]
                    # push!(fj, (f(x + ϵ) - f(x)) / (1.0 * ϵ))
                    push!(fj, (f(x + ϵ) - f(x - ϵ)) / (2.0 * ϵ))
                    grad = sum(fj) / N
                end
            else
                nothing
            end
            x -= α * grad
        end
    end

    if verbose
        println("grad. norm: ", norm(∇f(x)))
    end

    if visualize
        x_range = range(xmin, stop = xmax, length = 1000)
        plt = plot(x_range, f.(x_range), label="", color=:black, width=2.0)
        for s in σ
            dist = Normal(μ, s)
            if mode != :gradient
                plt = plot!(x_range, [sum([f(xt + rand(dist, 1)[1]) for j = 1:1000]) / 1000  for xt in x_range],
                    label="σ=$s", width=1.0, legend=:top)
            end
        end
        plt = scatter!([x0,], [f(x0),], color=:red, label="")
        plt = scatter!([x,], [f(x),], color=:green, label="")
        display(plt)
    end

    return x
end

# example 1
f(x) = x^2.0 + 0.1 * sin(20.0 * x)
∇f(x) = 2.0 * x + 2.0 * cos(20.0 * x)

# initial point
x0 = -0.75

# gradient descent
x_sol = gd(x0, iters=1000,
           σ=[0.5, 0.25, 0.1, 0.01, 0.001],
           mode=:smooth_gradient)

# example 2
function f(x)
    if x >= 0.0
        return 1.0
    else
        return 0.0
    end
end
∇f(x) = 0.0

# initial point
x0 = 1.0

# gradient descent
x_sol = gd(x0, iters=10000,
           σ=[1.0, 0.5, 0.1],#, 0.25, 0.1, 0.01, 0.001],#, 0.075, 0.05, 0.01],
           N = 100,
           mode=:zero_order_gradient,
           xmin=-2.0, xmax=2.0)

# example 3
function f(x)
    if x >= 0.0
        return -1.0 + x
    else
        return 1.0 - x
    end
end
function ∇f(x)
    if x >= 0.0
        return 1.0
    else
        return -1.0
    end
end

x0 = 2.0
x_sol = gd(x0, iters=1000,
           σ=[1.0, 0.5, 0.1],#, 0.25, 0.1, 0.01, 0.001],#, 0.075, 0.05, 0.01],
           N = 100,
           mode=:zero_order_gradient,
           xmin=-2.0, xmax=2.0)

max_issues_repo_name: thowell/DirectMotionPlanning


text:
#=
my_queue:
- Julia version: 1.5.0
- Author: shisa
- Date: 2020-08-18
=#

module MyQueue
    # variables
    const queue_size = 10 # max size of queue
    queue_head = 1 # index of next deque
    queue_num = 0
    queue_data = [0 for i in 1:queue_size] # data

    function enqueue(n)
        global queue_num
        if queue_num < queue_size
            en_idx = (queue_head + queue_num) % queue_size
            if en_idx == 0
                en_idx = queue_size
            end
            queue_data[en_idx] = n
            queue_num += 1
            println("Enqueue $(n) : $(queue_data)")
            return true
        else
            println("Enqueue $(n) : Queue is full")
            return false
        end
    end

    function dequeue()
        global queue_head, queue_num
        if queue_num > 0
            deq_data = queue_data[queue_head]
            queue_data[queue_head] = 0
            deq_idx = (queue_head + 1) % queue_size
            if deq_idx == 0
                deq_idx = queue_size
            end
            queue_head = deq_idx
            queue_num -= 1
            println("Dequeue $(deq_data) : $(queue_data)")
            return true
        else
            return false
        end
    end

    function main()
        # enqueue 1~7 to queue
        for i in 1:7
            enqueue(i)
        end
        # dequeue 1~4 from queue
        for i in 1:4
            dequeue()
        end
        # enqueue 8~15 to queue
        for i in 8:15
            enqueue(i)
        end
        # dequeue until queue become empty
        while queue_num > 0
            dequeue()
        end
        println("Queue is empty")
    end
end

if abspath(PROGRAM_FILE) == @__FILE__
    using .MyQueue
    @time MyQueue.main()
end

max_stars_repo_name: ShisatoYano/JuliaPractice

max_issues_repo_name: ShisatoYano/JuliaPractice

max_forks_repo_name: ShisatoYano/JuliaPractice


text:
#=
File containing further dispatches of `generate_exchmkt` to allow
generating ExchangeMarket instances from json files of some internet
exchange market providers.

Currently, only tools for fixer.io and currencylayer.com providers
are implemented.
=#
"""
    generate_exchmkt(exch_data::Dict, ::Val{:fixer})

Return an `ExchangeMarket` instance from a fixer.io Dict.
"""
function generate_exchmkt(exch_data::Dict, ::Val{:fixer})
    base = exch_data["base"]
    return generate_exchmkt([(base,curr) => float(rate) for (curr,rate) in exch_data["rates"]])
end

"""
    generate_exchmkt(exch_data::Dict, ::Val{:currencylayer})

Return an `ExchangeMarket` instance from a currencylayer.com Dict.
"""
function generate_exchmkt(exch_data::Dict, ::Val{:currencylayer})
    return generate_exchmkt([(pair[1:3],pair[4:6]) => float(rate) for (pair,rate) in exch_data["quotes"]])
end

"""
    generate_exchmkt(filename::String, ::Val{T}) where T

Return an `ExchangeMarket` instance from a filename and the symbol T
associated with the appropriate parser for that market.
"""
function generate_exchmkt(filename::String, ::Val{T}) where T
    return generate_exchmkt(JSON.parsefile(filename), Val(T))
end