text:
"""
    Relax

Used to dispatch relaxations to a standard
"""
struct Relax <: AbstractCacheAttribute end

"""
    RelaxAA
"""
struct RelaxAA <: AbstractCacheAttribute
    v::Vector{Int}
end
RelaxAA() = RelaxAA(Int[])

struct RelaxAAInfo <: AbstractCacheAttribute
    v::Vector{Int}
end
RelaxAAInfo() = RelaxAAInfo(Int[])

"""
    RelaxMulEnum
"""
struct RelaxMulEnum <: AbstractCacheAttribute
    v::Vector{Int}
    use_info::Bool
end
RelaxMulEnum() = RelaxMulEnum(Int[], false)
RelaxMulEnum(x::Vector{Int64}) = RelaxMulEnum(x, false)
RelaxMulEnum(x::Bool) = RelaxMulEnum(Int[], x)

struct RelaxMulEnumInner <: AbstractCacheAttribute
    v::Vector{Int}
    use_info::Bool
end
RelaxMulEnumInner() = RelaxMulEnumInner(Int[], false)
RelaxMulEnumInner(x::Vector{Int64}) = RelaxMulEnumInner(x, false)
RelaxMulEnumInner(x::Bool) = RelaxMulEnumInner(Int[], x)

const RELAX_ATTRIBUTE = Union{Relax,RelaxAA,RelaxAAInfo,RelaxMulEnum,RelaxMulEnumInner,RelaxInterval}
const RELAX_ONLY_ATTRIBUTE = Union{Relax,RelaxAA,RelaxAAInfo,RelaxMulEnum,RelaxMulEnumInner}

mutable struct MCBoxPnt{Q,N,T}
    v::MC{N,T}
    box::Vector{MC{N,T}}
end

cv(x::MCBoxPnt{Q,N,T}) where {Q,N,T} = x.v.cv
cc(x::MCBoxPnt{Q,N,T}) where {Q,N,T} = x.v.cc
cv_grad(x::MCBoxPnt{Q,N,T}) where {Q,N,T} = x.v.cv_grad
cc_grad(x::MCBoxPnt{Q,N,T}) where {Q,N,T} = x.v.cc_grad

cv(x::MC{N,T}) where {N,T} = x.cv
cc(x::MC{N,T}) where {N,T} = x.cc
cv_grad(x::MC{N,T}) where {N,T} = x.cv_grad
cc_grad(x::MC{N,T}) where {N,T} = x.cc_grad

function zero(::Type{MCBoxPnt{Q,N,T}}) where {Q,N,T}
    MCBoxPnt{Q,N,T}(zero(MC{N,T}), zeros(MC{N,T}, Q))
end
function setindex!(d::MCBoxPnt{Q,N,T}, x::MC{N,T}, i::Int) where {Q,N,T}
    d.box[i] = x
end

Base.@kwdef mutable struct RelaxCache{V,N,T<:RelaxTag} <: AbstractCache
    ic::IntervalCache                      = IntervalCache{Float64}()
    dp::Vector{Float64}                    = Float64[]
    dP::Vector{Interval{Float64}}          = Interval{Float64}[]
    _set::Vector{MC{N,T}}                  = MC{N,T}[]
    _num::Vector{Float64}                  = Float64[]
    _is_num::Vector{Bool}                  = Bool[]
    _info::Vector{V}                       = V[]
    _subexpression_set::Dict{Int,MC{N,T}}  = Dict{Int,MC{N,T}}()
    _subexpression_num::Dict{Int,Float64}  = Dict{Int,Float64}()
    _subexpression_is_num::Dict{Int,Bool}  = Dict{Int,Bool}()
    _subexpression_info::Dict{Int,MC{N,T}} = Dict{Int,MC{N,T}}()
    _cv_grad_buffer::Vector{Float64}       = Float64[]
    _cc_grad_buffer::Vector{Float64}       = Float64[]
    _set_mv_buffer::Vector{MC{N,T}}        = MC{N,T}[]
    _num_mv_buffer::Vector{Float64}        = Float64[]
    _info_mv_buffer::Vector{V}             = V[]
    _mult_temp::V                          = zero(V)
    ϵ_sg::Float64                          = 1E-6
    post::Bool                             = false
    cut::Bool                              = false
    cut_interval::Bool                     = false
    first_eval::Bool                       = true
    use_apriori_mul::Bool                  = false
end
function RelaxCache{N,T}(::Relax, n::Int, m::Int, p::Int) where {N,T<:RelaxTag}
    RelaxCache{N,T}(_set                 = zeros(MC{N,T}, n),
                    _num                 = zeros(Float64, n),
                    _is_num              = zeros(Bool, n),
                    _info                = zeros(MC{N,T}, n),
                    _cv_grad_buffer      = zeros(Float64, p),
                    _cc_grad_buffer      = zeros(Float64, p),
                    _set_mv_buffer       = zeros(MC{N,T}, p),
                    _num_mv_buffer       = zeros(p),
                    _info_mv_buffer      = zeros(MC{N,T}, p),
                    )
end
function initialize!(c::RelaxCache{V,N,T}, g::DirectedTree) where {V,N,T<:RelaxTag}

    n = node_count(g)
    m = dep_subexpr_count(g)
    p = length(sparsity(g, 1))

    c._num                 = zeros(n)
    c._is_num              = zeros(Bool, n)
    c._set                 = zeros(MC{N,T}, n)
    c._cv_grad_buffer      = zeros(p)
    c._cc_grad_buffer      = zeros(p)
    c._set_mv_buffer       = zeros(MC{N,T}, p)
    c._num_mv_buffer       = zeros(p)
    c._mult_temp           = zero(V)

    for i = 1:n
        ni = node(g, i)
        if node_class(ni) == CONSTANT
            c._is_num[i] = true
            c._num[i] = g.constant_values[first_index(ni)]
        end
    end

    for i = 1:n
        push!(c._info, zero(V))
    end
    for i = 1:p
        push!(c._info_mv_buffer, zero(V))
    end
    return
end

function copy_subexpr!(rc::RelaxCache{V,N,T}, ds, dn, din, di) where {V,N,T<:RelaxTag}
    rc._subexpression_set = ds
    rc._subexpression_num = dn
    rc._subexpression_is_num = din
    rc._subexpression_info = di
    return
end

mc_type(rc::RelaxCache{V,N,T}) where {V,N,T<:RelaxTag} = MC{N,T}

###
###
### Access functions for RelaxCache.
###
###
set(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag}  = b._set[i]
num(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag}  = b._num[i]
set_or_num(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = !is_num(b, i) ? set(b,i) : num(b,i)
info(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = b._info[i]
is_num(b::RelaxCache{V,N,T}) where {V,N,T<:RelaxTag}       = b._is_num
is_num(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = b._is_num[i]
interval(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = Interval{Float64}(set(b, i))
subexpression_set(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = b._subexpression_set[i]
subexpression_num(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = b._subexpression_num[i]
subexpression_is_num(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = b._subexpression_is_num[i]
function Base.setindex!(b::RelaxCache{V,N,T}, v::Float64, i::Int) where {V,N,T<:RelaxTag}
    b._is_num[i] = true
    b._num[i] = v
    nothing
end
function Base.setindex!(b::RelaxCache{V,N,T}, v::MC{N,T}, i::Int) where {V,N,T<:RelaxTag}
    b._is_num[i] = false
    b._set[i] = v
    nothing
end
store_info!(b::RelaxCache{V,N,T}, v::V, i::Int) where {V,N,T<:RelaxTag} = (b._info[i] = v; nothing)

function store_subexpression_set!(b::RelaxCache{V,N,T}, v::MC{N,T}, i::Int) where {V,N,T<:RelaxTag}
    b._subexpression_is_num[i] = false
    b._subexpression_set[i] = v
    nothing
end
function store_subexpression_num!(b::RelaxCache{V,N,T}, v, i::Int) where {V,N,T<:RelaxTag}
    b._subexpression_is_num[i] = true
    b._subexpression_num[i] = v
    nothing
end

first_eval(t::RELAX_ONLY_ATTRIBUTE, b::RelaxCache) = b.first_eval
first_eval(t::RelaxInterval, b::RelaxCache) = b.first_eval
val(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = val(b.ic, i)
lbd(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = lbd(b.ic, i)
ubd(b::RelaxCache{V,N,T}, i::Int) where {V,N,T<:RelaxTag} = ubd(b.ic, i)
_set_input(b::RelaxCache{V,N,T}, n::Int) where {V,N,T<:RelaxTag} = view(b._set_mv_buffer, 1:n)
_num_input(b::RelaxCache{V,N,T}, n::Int) where {V,N,T<:RelaxTag} = view(b._num_mv_buffer, 1:n)

include(joinpath(@__DIR__, "utilities.jl"))
include(joinpath(@__DIR__, "forward_propagation.jl"))
include(joinpath(@__DIR__, "reverse_propagation.jl"))

function fprop!(t::RELAX_ATTRIBUTE, g::DAT, b::RelaxCache{V,N,T}) where {V,N,T<:RelaxTag}
    for k = node_count(g):-1:1
        c = node_class(g, k)
        (c == EXPRESSION)    && (fprop!(t, Expression(), g, b, k);    continue)
        (c == VARIABLE)      && (fprop!(t, Variable(), g, b, k);      continue)
        (c == SUBEXPRESSION) && (fprop!(t, Subexpression(), g, b, k); continue)
    end
    nothing
end

function rprop!(t::RELAX_ATTRIBUTE, g::DAT, b::RelaxCache{V,N,T}) where {V,N,T<:RelaxTag}
    flag = r_init!(t, g, b)
    for k = 1:node_count(g)
        nt = node_class(g, k)
        if nt == EXPRESSION
            flag = rprop!(t, Expression(), g, b, k)
        elseif nt == VARIABLE
            flag = rprop!(t, Variable(), g, b, k)
        elseif nt == SUBEXPRESSION
            flag = rprop!(t, Subexpression(), g, b, k)
        end
    end
    return flag
end

function display_expr(g, i)
    n = node(g, i)
    nc = node_class(n)
    if nc == VARIABLE
        return "x[$(first_index(n))]"
    elseif nc == PARAMETER
        return "p[$(first_index(n))]"
    elseif nc == EXPRESSION
        ex_typ = ex_type(n)
        ex_sym = ALL_ATOM_DICT[ex_typ]
        ctup = tuple(children(n)...)
        return "$(ex_sym)$(ctup)"
    elseif nc == CONSTANT
        return "c[$(first_index(n))]"
    end
end

function display_table!(g::DAT, b::RelaxCache{V,N,T}) where {V,N,T<:RelaxTag}
    nc = node_count(g)
    val = [b._is_num[i] ? b._num[i] : b._set[i] for i in 1:nc]
    exr = [display_expr(g, i)  for i in 1:nc]
    data = hcat(exr, b._is_num, val)
    show(pretty_table(data, header = ["Expr", "Is Num", "Val"]; show_row_number = true))
end

max_stars_repo_name: PSORLab/RSActivationFunctions

max_issues_repo_name: PSORLab/RSActivationFunctions

max_forks_repo_name: PSORLab/RSActivationFunctions


text:
import PeaceFounder

using DemeNet: DemeSpec, Deme, Signer, Certificate, Profile
using PeaceVote: KeyChain, record, braid!, sync!, Vote, Proposal, load, proposals, attest, voters, AbstractChain
using Recruiters: register

### CleanUp ###

for dir in [homedir() * "/.demenet/"]
    isdir(dir) && rm(dir,recursive=true)
end

### Setting up the system ###
module Setup
include("setup.jl") # defines deme and systemconfig
include("serve.jl")
end

### Maintainer generates invites ###

invites = []

module Maintainer

import PeaceFounder

using DemeNet: Signer, Deme, DemeSpec, AbstractInitializer, init, config
using Recruiters: addtooken, ticket
import ..Setup

demespec = DemeSpec(Setup.uuid)
deme = Deme(demespec)
initializer = AbstractInitializer(deme)
pfconfig = config(initializer)

maintainer = Signer(deme,"maintainer")

import ..invites

tookens = [121233,234324,123133]

for tooken in tookens
    addtooken(pfconfig,deme,tooken,maintainer)  ### Perhaps I could just extend that with recruiters!
    invite = ticket(pfconfig,deme,tooken)
    ### One can send it, for example, over email
    push!(invites,invite)
end

end

### Registration with recruiters

for i in 1:3
    profile = Profile(Dict("uuid"=>i))
    invite = invites[i]
    register(invite,profile,account="account$i")
end

demespec = DemeSpec(Setup.uuid)
deme = Deme(demespec)
braidchain = AbstractChain(deme)

# Braiding

@sync for i in 1:3
    @async begin
        account = "account$i"
        keychain = KeyChain(deme,account)
        braid!(braidchain,keychain)
    end
end

# Proposing

keychain = KeyChain(deme,"account2")
proposal = Proposal("Found peace for a change?",["yes","no","maybe"])
cert = Certificate(proposal,keychain.member)
record(braidchain,cert)

sleep(1)
sync!(braidchain)

loadedledger = load(braidchain)
messages = attest(loadedledger,braidchain.deme.notary)
index = proposals(messages)[1]
proposal = messages[index]

# Now we can vote

for i in 1:3
    account = "account$i"
    keychain = KeyChain(deme,account)

    option = Vote(index,rand(1:length(proposal.document.options)))
    cert = Certificate(option,braidchain,keychain)
    record(braidchain,cert)
end

# Now let's count
sleep(1)
sync!(braidchain)

@show tally = count(index,braidchain)

ext: jl

max_stars_repo_name: akels/DemocracyCredit

max_issues_repo_name: akels/DemocracyCredit

max_forks_repo_name: akels/DemocracyCredit


text:
#Function that group the mainly statistics moments that include nan values

function nanmean(vector::Vector)
N=length(vector);
s=0.0;                                        #--sample mean sum
m=0.0;                                        #--counter for the sample size
  for i=1:N
     if isnan(vector[i])==0;                  #--make sure to sum numeric values
        m=m+1;
        s=s+vector[i];
     end
  end
mu=s/m;
mu
end

#In nanvar.jl
#https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
function nanvar(vector::Array)
N=length(vector);
m=0.0;             #---Initialize counter for samples
mu=0.0;            #---Initialize mean
M2=0.0;            #---Intitialize sum(x_i-mu)^2

 for i=1:N
      if isnan(vector[i])==0;                  #--make sure to sum numeric values
         m=m+1;
         delta=vector[i]-mu;
         mu=mu+delta/m;
         M2=M2+delta*(vector[i]-mu);
      end
 end
 if m<2;
    return NaN
 else
    return M2/m
 end
end

#In nanskew.jl
#https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
function nanskew(vector::Array)
N=length(vector);
m=0.0;             #---Initialize counter for samples
mu=0.0;            #---Initialize mean
M2=0.0;            #---Intitialize sum(x_i-mu)^2
M3=0.0;            #---Intitialize sum(x_i-mu)^2
M4=0.0;            #---Intitialize sum(x_i-mu)^2

 for i=1:N
      if isnan(vector[i])==0;                  #--make sure to sum numeric values
         n1=m;
         m=m+1;
         delta=vector[i]-mu;
         delta_m=delta/m;
         delta_m2=delta_m*delta_m;
         term1=delta*delta_m*n1;
         mu=mu+delta_m;
         M3=M3+term1*delta_m*(m-2)-3*delta_m*M2
         M2=M2+term1;
      end
 end
skewness=sqrt(m)*M3/M2^(3/2);
return skewness
end

#In nankurt.jl
#https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Higher-order_statistics
function nankurt(vector::Array)
N=length(vector);
m=0.0;             #---Initialize counter for samples
mu=0.0;            #---Initialize mean
M2=0.0;            #---Intitialize sum(x_i-mu)^2
M3=0.0;            #---Intitialize sum(x_i-mu)^2
M4=0.0;            #---Intitialize sum(x_i-mu)^2

 for i=1:N
      if isnan(vector[i])==0;                  #--make sure to sum numeric values
         n1=m;
         m=m+1;
         delta=vector[i]-mu;
         delta_m=delta/m;
         delta_m2=delta_m*delta_m;
         term1=delta*delta_m*n1;
         mu=mu+delta_m;
         M4=M4+term1*delta_m2*(m*m-3*m+3)+6*delta_m2*M2-4*delta_m*M3
         M3=M3+term1*delta_m*(m-2)-3*delta_m*M2
         M2=M2+term1;
      end
 end
#kurtosis=(m*M4)/(M2*M2)-3;      #--normalized
kurtosis=(m*M4)/(M2*M2);
return kurtosis
end


text:
##############################################################################
###                            Faraday Grid                                ###
###                       Wholesale market model                           ###
###                        Perfect competition                             ###
###                                v0.1                                    ###
##############################################################################

##############################################################################
###                         initialise packages                            ###
##############################################################################

using JuMP                  #necessary for optimisation
using Gurobi                #solver - requires license. Other solvers can be used
using Complementarity       #required for imperfectly competitive model. Requires free PATH license
using Taro                  #required to read and write Excel files
using Plots                 #required to generate plots
using DataFrames            #required to process input data

try                         #initialises Taro if this has not been done already
    Taro.init()
catch
    print("Taro already initialised")
end

##############################################################################
###                             I/O modules                                ###
##############################################################################
function ProcessData(input_data)
    grid_data=Taro.readxl(input_data, "Network", "A2:E53"; header=false)
        global lines =             Array(grid_data[1])
        global from =              Array(grid_data[2])
        global to =                Array(grid_data[3])
        reactance_raw =     Array(grid_data[4])
        maxflow_raw =       Array(grid_data[5])
    generator_data=Taro.readxl(input_data, "Generators", "A2:H217"; header=false)
        global generators =        Array(generator_data[1])
        global owner =             Array(generator_data[2])
        global location =          Array(generator_data[3])
        fuelcost_raw=       Array(generator_data[4])
        capacity_raw=       Array(generator_data[5])
        variableoutput_raw= Array(generator_data[6])
        uprate_raw=         Array(generator_data[7])
        downrate_raw=       Array(generator_data[8])
    availability_raw = Array(Taro.readxl(input_data, "Availability", "B2:P8762"; header=false))
    load_data = Taro.readxl(input_data, "Load", "A2:AD8762"; header=false)
        global timeperiods =       Array(load_data[1])
        demand_raw =        Array(load_data[2:30])
    global buses = Array(Taro.readxl(input_data, "Load", "B1:AD1"; header=false))
    # transform input data into dictionaries
    global reactance =     Dict(zip(lines,reactance_raw))
    global maxflow =       Dict(zip(lines,maxflow_raw))
    global fuelcost =      Dict(zip(generators,fuelcost_raw))
    global capacity =      Dict(zip(generators,capacity_raw))
    global uprate =        Dict(zip(generators,uprate_raw))
    global downrate =      Dict(zip(generators,downrate_raw))
    global availability=   Dict()
        for i in 1:length(generators), j in 1:length(timeperiods)
            n=1
            if variableoutput_raw[i]==1
                availability[generators[i],timeperiods[j]]=availability_raw[j,n]
                n+=1
            else
                availability[generators[i],timeperiods[j]]=1
            end
        end
    global demand=Dict()
        for i in 1:length(buses), j in 1:length(timeperiods)
            demand[buses[i],timeperiods[j]]=demand_raw[j,i]
        end
    # generate additional dictionaries
    global firms =         unique(owner)                                  #generate list of unique owners using index f
    global gensowned =     Dict()                                         #returns list of generators owned by a particular owner
        for ff in 1:length(firms)
            ow1=[]
            for gg in 1:length(generators)
                if owner[gg]==firms[ff]
                    push!(ow1, generators[gg])
                end
            end
            gensowned[firms[ff]]=ow1
        end
    global ownedby =       Dict(zip(generators,owner))                     #return owner of a particular generator
    global fromd =         Dict(zip(lines,from))                           #generate dictionaries that return the origin and end buses of a given line
    global tod =           Dict(zip(lines,to))
    global connectedto =   Dict()                                          #generate dictionary that returns lists of lines connected to a given bus
        for i in 1:length(buses)                                        #outer loop over buses
            aux1=[]                                                     #temporary auxilliary array
            for l in 1:length(lines)                                    #inner loop over lines
                if to[l]==buses[i]                                      #check if line ends in bus
                    push!(aux1,lines[l])                                #if it does, add its name to the temporary auxilliary variable
                end
            end
            connectedto[buses[i]]=aux1                                  #write contents of auxilliary variable to dictionary
        end
    global connectedfrom=  Dict()                                          #generate dictionary that returns lists of lines connected from a given bus
        for i in 1:length(buses)                                        #outer loop over buses
            aux2=[]                                                     #temporary auxilliary array
            for l in 1:length(lines)                                    #inner loop over lines
                if from[l]==buses[i]                                    #check if line originates in bus
                    push!(aux2,lines[l])                                #if it does, add its name to the temporary auxilliary variable
                end
            end
            connectedfrom[buses[i]]=aux2                                #write contents of auxilliary variable to dictionary
        end
    global locatedat=Dict()                                                #generate dictionary that returns list of generators for a given bus
        for i in 1:length(buses)                                        #outer loop over buses
            aux1=[]                                                     #temporary auxilliary array
            for g in 1:length(generators)                               #inner loop over generators
                if location[g]==buses[i]                                #check if generator is at in bus
                    push!(aux1,generators[g])                           #if it is, add its name to the temporary auxilliary variable
                end
            end
            locatedat[buses[i]]=aux1                                    #write contents of auxilliary variable to dictionary
        end
    global qpeak=Dict()
        for i in buses
            qpeak[i]=maximum([demand[i,t] for t in timeperiods])
        end
end

function LoadDemandFunctions(demand_functions)
    consObjA_raw = Array(Taro.readxl(demand_functions, "Intercepts", "C3:AE8763"; header=false))
    consObjB_raw = Array(Taro.readxl(demand_functions, "Slopes", "C3:AE8763"; header=false))
    global consObjA=Dict()
        for i in 1:length(buses), j in 1:length(timeperiods)
            consObjA[buses[i],timeperiods[j]]=consObjA_raw[j,i]
        end
    global consObjB=Dict()
        for i in 1:length(buses), j in 1:length(timeperiods)
            consObjB[buses[i],timeperiods[j]]=consObjB_raw[j,i]
        end
end

function LoadMarketOutcomes(output_file)
    marketdata_raw = Array(Taro.readxl(output_file, "MarketOutcome", "C3:HJ8763"; header=false))
    global gref=Dict()
        for g in 1:length(generators), j in 1:length(timeperiods)
            gref[generators[g],timeperiods[j]]=marketdata_raw[j,g]
        end
end

function WriteMarketData(output_data_market)
    w=Workbook()
    s=createSheet(w, "MarketOutcome")
    r=createRow(s, 1)
    for g in 1:length(generators)
        c=createCell(r, g+1); setCellValue(c, generators[g])
    end
    for t in 1:length(timeperiods)
        r=createRow(s, t+1)
        c=createCell(r, 1); setCellValue(c, timeperiods[t])
        for g in 1:length(generators)
            c=createCell(r, g+1); setCellValue(c, gref[generators[g],timeperiods[t]])
        end
    end
    s=createSheet(w,"Prices")
    for t in 1:length(timeperiods)
        r=createRow(s,t)
        c=createCell(r, 1); setCellValue(c, timeperiods[t])
        c=createCell(r, 2); setCellValue(c, pref[timeperiods[t]])
    end
    write(output_data_market, w)
end

function WriteRedispatchData(output_data_redispatch)
    w=Workbook()
    s=createSheet(w, "Redispatch")
    r=createRow(s, 1)
    for g in 1:length(generators)
        c=createCell(r, g+1); setCellValue(c, generators[g])
    end

    for t in 1:length(timeperiods)
        r=createRow(s, t+1)
        c=createCell(r, 1); setCellValue(c, timeperiods[t])
        for g in 1:length(generators)
            c=createCell(r, g+1); setCellValue(c, qredi[generators[g],timeperiods[t]])
        end
    end
    s=createSheet(w, "RedispatchCosts")
    for t in 1:length(timeperiods)
        r=createRow(s, t)
        c=createCell(r, 1); setCellValue(c, timeperiods[t])
        c=createCell(r, 2); setCellValue(c, credi[timeperiods[t]])
    end
    write(output_data_redispatch, w)
end

##############################################################################
###                rolling horizon wholesale market model                  ###
##############################################################################
function RollingHorizonModel(hh)
    zz=1                                                #iterator
    global gref=Dict()
    global pref=Dict()

    while zz <= length(timeperiods)
        if zz+hh-1<=length(timeperiods)
            timeperiodssubset=timeperiods[zz:zz+hh-1]
        else
            timeperiodssubset=timeperiods[zz:length(timeperiods)]
        end
        m1 = Model(solver=GurobiSolver())
        @variable(m1,     generation[g in generators, t in timeperiodssubset]  >=0)
        @objective(m1,    Min, sum(generation[g,t]*fuelcost[g] for g in generators, t in timeperiodssubset))
        @constraint(m1,   maxgen[g in generators, t in timeperiodssubset],    capacity[g]*availability[g,t]-generation[g,t]>=0)
        @constraint(m1,   marketclearing[t in timeperiodssubset],      sum(generation[g,t] for g in generators)-sum(demand[i,t] for i in buses) == 0 )
        solve(m1)
        for xx in 1:length(generators)
             gref[generators[xx],timeperiods[zz]] = getvalue(generation)[generators[xx],timeperiods[zz]]
        end
        pref[timeperiods[zz]] = getdual(marketclearing)[timeperiods[zz]]
        zz += 1
    end
end

##############################################################################
###          calculate demand functions  and write to Excel                ###
##############################################################################

function WriteDemandFunctions(output_data_demandfunctions,epsilon)

    consObjA = Dict() #calculate demand function intercept
    consObjB = Dict() #calculate demand function slope
        for i in buses, t in timeperiods[1:length(timeperiods)]
            consObjB[i,t] = (-1) * pref[t] / (demand[i,t] * epsilon)
            consObjA[i,t] = pref[t] + consObjB[i,t] * demand[i,t]
        end

    w=Workbook()
    s=createSheet(w, "Intercepts")
    r=createRow(s, 1)
    for i in 1:length(buses)
        c=createCell(r, i+1); setCellValue(c, buses[i])
    end
    for t in 1:length(timeperiods)
        r=createRow(s, t+1)
        c=createCell(r, 1); setCellValue(c, timeperiods[t])
        for i in 1:length(buses)
            c=createCell(r, i+1); setCellValue(c, consObjA[buses[i],timeperiods[t]])
        end
    end
    s=createSheet(w, "Slopes")
    r=createRow(s, 1)
    for i in 1:length(buses)
        c=createCell(r, i+1); setCellValue(c, buses[i])
    end
    for t in 1:length(timeperiods)
        r=createRow(s, t+1)
        c=createCell(r, 1); setCellValue(c, timeperiods[t])
        for i in 1:length(buses)
            c=createCell(r, i+1); setCellValue(c, consObjB[buses[i],timeperiods[t]])
        end
    end
    write(output_data_demandfunctions, w)
end

##############################################################################
###       rolling horizon wholesale market model - elastic demand          ###
##############################################################################

function RollingHorizonModelElasticDemand(hh)

    zz=1                                                #iterator
    global gref=Dict()
    global qref=Dict()
    global pref=Dict()

    while zz<=length(timeperiods)
        if zz+hh-1<=length(timeperiods)
            timeperiodssubset=timeperiods[zz:zz+hh-1]
        else
            timeperiodssubset=timeperiods[zz:length(timeperiods)]
        end

        m3 = Model(solver=GurobiSolver())

        @variable(m3,     generation[g in generators, t in timeperiodssubset]  >=0)
        @variable(m3,     rdemand[i in buses, t in timeperiodssubset]  >=0)

        @objective(m3,    Max, sum(consObjA[i,t]*rdemand[i,t]-0.5*consObjB[i,t]*rdemand[i,t]*rdemand[i,t] for i in buses, t in timeperiodssubset)-sum(generation[g,t]*fuelcost[g] for g in generators, t in timeperiodssubset))
        @constraint(m3,   maxgen[g in generators, t in timeperiodssubset],    capacity[g]*availability[g,t]-generation[g,t]>=0)
        @constraint(m3,   marketclearing[t in timeperiodssubset],      sum(generation[g,t] for g in generators)-sum(rdemand[i,t] for i in buses) == 0 )
        solve(m3)

        for xx in 1:length(generators)
             gref[generators[xx],timeperiods[zz]] = getvalue(generation)[generators[xx],timeperiods[zz]]
        end

        for ii in 1:length(buses)
            qref[buses[ii],timeperiods[zz]]=getvalue(rdemand)[ii,zz]
        end

        pref[timeperiods[zz]] = getdual(marketclearing)[timeperiods[zz]]

        zz+=1
    end

end

##############################################################################
###                          redispatch model                              ###
##############################################################################

function RedispatchModel(rhh,elastic)

    if elastic=="false"
        demand2=demand
    end

    if elastic=="true"
        demand2=qref
    end

    zz=1                                                #iterator
    global qredi=Dict()
    global credi=Dict()

    while zz <= length(timeperiods)
        if zz+rhh-1<=length(timeperiods)
            timeperiodssubset=timeperiods[zz:zz+rhh-1]
        else
            timeperiodssubset=timeperiods[zz:length(timeperiods)]
        end
        m2 = Model(solver=GurobiSolver())
        @variable(m2,    voltageangle[i in buses, t in timeperiodssubset])
        @variable(m2,    redispatch[g in generators, t in timeperiodssubset])
        @expression(m2,  flow[l in lines,t in timeperiodssubset],         (1/reactance[l])*(voltageangle[fromd[l],t]-voltageangle[tod[l],t]))
        @objective(m2, Min, sum(fuelcost[g]*redispatch[g,t] for g in generators, t in timeperiodssubset))
        @constraint(m2,  slackbus[t in timeperiodssubset],                voltageangle[buses[1],t] ==  0)
        @constraint(m2,  nodalbalance[i in buses, t in timeperiodssubset],demand2[i,t]-sum(gref[g,t]+redispatch[g,t] for g in locatedat[i]) + reduce(+,0,flow[l,t] for l in connectedto[i])-reduce(+,0,flow[l,t] for l in connectedfrom[i]) ==  0)
        @constraint(m2,  maxflowup[l in lines, t in timeperiodssubset],   flow[l,t] <=  maxflow[l])
        @constraint(m2,  maxflowdown[l in lines, t in timeperiodssubset], flow[l,t] >= -maxflow[l])
        @constraint(m2,  maxgen[g in generators, t in timeperiodssubset], gref[g,t]+redispatch[g,t] <=  capacity[g]*availability[g,t])
        @constraint(m2,  mingen[g in generators, t in timeperiodssubset], gref[g,t]+redispatch[g,t] >=  0)
        solve(m2)
        for xx in 1:length(generators)
             qredi[generators[xx],timeperiods[zz]] = getvalue(redispatch)[generators[xx],timeperiods[zz]]
        end
        credi[timeperiods[zz]]=getobjectivevalue(m2)
        zz+=1
    end
end

##############################################################################
###                    imperfectly competitive model                       ###
##############################################################################

function ImperfectCompetitionModel(hh)
    zz=1                                                #iterator
    global gref=Dict()
    global pref=Dict()
    global qref=Dict()

    while zz <= length(timeperiods)
        if zz+hh-1<=length(timeperiods)
            timeperiodssubset=timeperiods[zz:zz+hh-1]
        else
            timeperiodssubset=timeperiods[zz:length(timeperiods)]
        end

        m4=MCPModel()

        @variable(m4,   generation[g in generators, t in timeperiodssubset]   >=  0)
        @variable(m4,   lambda[g in generators, t in timeperiodssubset]       >=  0)

        @mapping(m4,    kktgen[g in generators,t in timeperiodssubset],      consObjA[buses[1],t]+(1/sum(1/consObjB[i,t] for i in buses))*(sum(generation[gg,t] for gg in gensowned[ownedby[g]])+sum(generation[gg,t] for gg in generators))+fuelcost[g]+lambda[g,t])
        @mapping(m4,    kktlambda[g in generators,t in timeperiodssubset],   capacity[g]*availability[g,t]-generation[g,t])

        @complementarity(m4,    kktgen,         generation)
        @complementarity(m4,    kktlambda,      lambda)

        PATHSolver.options(convergence_tolerance=1e-8, output="no", time_limit=3600)

        solveMCP(m4)

        for xx in 1:length(generators)
             gref[generators[xx],timeperiods[zz]] = getvalue(generation)[generators[xx],timeperiods[zz]]
        end
        pref[timeperiods[zz]] = consObjA[buses[1],timeperiods[zz]]+(1/sum(1/consObjB[i,timeperiods[zz]] for i in buses))*sum(gref[g,timeperiods[zz]] for g in generators)
        for ii in 1:length(buses)
            qref[buses[ii],timeperiods[zz]] = consObjA[buses[ii],timeperiods[zz]]+consObjB[buses[ii],timeperiods[zz]]*pref[timeperiods[zz]]
        end

    end
end

##############################################################################
###                            generate plots                              ###
##############################################################################
function GeneratePricePlot()
    plot([pref[t] for t in timeperiods[1:length(timeperiods)]],title="Price",leg=false)
    savefig("priceplot.png")
end

function GenerateRedispatchCostPlot()
    plot([credi[t] for t in timeperiods[1:length(timeperiods)]],title="Redispatch costs",leg=false)
    savefig("redispatchcostplot.png")
end

function GenerateDemandPlot()
    plot([sum(demand[i,t] for i in buses) for t in timeperiods],title="Demand",leg=false)
    savefig("demandplot.png")
end


text:
using Replay
using Test

ENV["CI"] = "true"

const repl_script = """
2+2
print("")
display([1])
display([1 2; 3 4])
@time 1+1
"""

@testset "replay: color=yes" begin
    buf = replay(repl_script, IOBuffer(), julia_project="@.") # color=:yes by default
    out = buf |> take! |> String
    #=
    open(joinpath(@__DIR__, "references", "replay_color_yes.txt"), "w") do f
        write(f, out)
    end
    =#
    reftxt = joinpath(@__DIR__, "references", "replay_color_yes.txt")
    ref = join(readlines(reftxt), "\r\n")
    @test out == ref
end

@testset "replay: color=true" begin
    buf = replay(repl_script, IOBuffer(), color = true)
    out = buf |> take! |> String
    reftxt = joinpath(@__DIR__, "references", "replay_color_yes.txt")
    ref = join(readlines(reftxt), "\r\n")
    @test out == ref
end

@testset "replay: color=no" begin
    buf = replay(repl_script, IOBuffer(), color = :no)
    out = buf |> take! |> String
    #=
    open(joinpath(@__DIR__, "references", "replay_color_no.txt"), "w") do f
        write(f, out)
    end
    =#
    reftxt = joinpath(@__DIR__, "references", "replay_color_no.txt")
    ref = join(readlines(reftxt), "\r\n")
    @test out == ref
end

@testset "replay: color=false" begin
    buf = replay(repl_script, IOBuffer(), color = false)
    out = buf |> take! |> String
    reftxt = joinpath(@__DIR__, "references", "replay_color_no.txt")
    ref = join(readlines(reftxt), "\r\n")
    @test out == ref
end


text:
"""Sums the element of a 2-dimensional matrix."""
function sum_matrix(a)
    sum = 0
    n = array_length(a)
    for i = 0 : (n - 1)
        m = array_length(a[i])
        for j = 0 : (m - 1)
            sum = sum + a[i][j]
        end
    end
    return sum
end

"""Makes matrix of dimension d with level i having a size of l[i] and initial value of val."""
function make_matrix(d :: Int64, l :: Array, val :: Any) :: Any
    if d > 0
        n = array_length(l)
        a = newarray(l[n - d], make_matrix(d - 1, l, val))
        for i = 0 : (l[n - d] - 1)
            b = make_matrix(d - 1, l, val)
            a[i] = b
        end
        return a
    end
    # if d = 0, we simply return the value
    return val
end


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

using Markdown
using InteractiveUtils

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

# ╔═╡ c028100e-f47b-11eb-2547-df779889e265
using WaterLily, StaticArrays, PlutoUI, Interpolations, Plots, Images

# ╔═╡ b0c8df66-1e63-456e-be32-d469e6972e00
md"""
## Simulation of a swimming dogfish shark

**Gabriel D Weymouth** [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/gabrielweymouth.svg?style=social&label=)](https://twitter.com/gabrielweymouth) | 11 Aug 2021

---

This post will demonstrate how to set up and simulate a model of a swimming dogfish shark using [Julia](https://julialang.org/) and [WaterLily.jl](https://www.youtube.com/watch?v=YsPkfZqbNSQ). The post is actually a [Pluto notebook](https://www.youtube.com/watch?v=IAF8DjrQSSk) and can be run interactively by clicking on the top right icon.

We'll use a simple model of the shark based on Lighthill's pioneering [paper on the swimming of slender fish](https://doi.org/10.1017/S0022112060001110). It focuses on the "backbone" of the fish; idealizing the shape as a thickness distribution on either side of the center, and the motion as a lateral ("side-to-side") traveling wave. Amazingly, this simple approach provides insight across a huge range of a swimming animals as illustrated in the image below. [Image credit: Gazzola et al, _Nature_ 2014](https://www.nature.com/articles/nphys3078)
"""

# ╔═╡ e9384124-9708-44eb-b5e7-75abc5177a69
begin
	url = "https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fnphys3078/MediaObjects/41567_2014_Article_BFnphys3078_Fig1_HTML.jpg"
	filename = download(url)
	lighthill = load(filename)
end

# ╔═╡ c8293b7b-0585-45b3-8763-8cfa4671b155
md"""

### Modeling the shark's thickness and motion

We'll define the thickness and motion distributions using `interpolate` to fit splines through a few points.
"""

# ╔═╡ a48ac92c-5cb0-4e51-a08f-b34bc8b06d04
fit = y-> scale(interpolate(y, BSpline(Quadratic(Line(OnGrid())))), range(0,1,length=length(y)))

# ╔═╡ ad96bcbe-4482-4486-b817-e788604d1d96
md"""
Here is an image of the dogfish shark we will model. [Image credit: Shark Trust](file:///C:/Users/admin/Downloads/Spiny%20Dogfish%20ST%20Factsheet.PDF)
"""

# ╔═╡ 47eb9e3f-5617-417a-8924-5a38d90390e8
# Fish shape
begin
	url2 = "https://pterosaurheresies.files.wordpress.com/2020/01/squalus-acanthias-invivo588.jpg"
	filename2 = download(url2)
	dogfish = load(filename2)
end

# ╔═╡ 8752bf4d-50e6-42ea-8d43-c41699769f77
md"""
The bottom view shows the outline we're interested in, and adding a few points along the length defines the thickness distribution function `thk`.
"""

# ╔═╡ 71c9bc8f-921d-4df4-acc2-7321ce6648e0
begin
	plot(dogfish)
	nose,len = (30,224),500
	width = [0.02,0.07,0.06,0.048,0.03,0.019,0.01]
	scatter!(nose[1].+len.*range(0,1,length=length(width)),
		nose[2].-len.*width,color=:blue,legend=false)
	thk = fit(width)
	x = 0:0.01:1
	plot!(nose[1].+len.*x, [nose[2].-len.*thk.(x),nose[2].+len.*thk.(x)],color=:blue)
end

# ╔═╡ d830e69e-f085-495e-be2c-8ea44582fa20
md"""
Looking at [videos of swimming dogfish](https://youtu.be/nMa6lD2CQVI?t=200), we can see a couple general features
 - The motion of the front half of the body has a small amplitude (around 20% of the tail). This sets the amplitude evelope for the traveling wave.
"""

# ╔═╡ c2b7a082-c05d-41bb-af4c-eb23f6a331bf
begin
	envelope = [0.2,0.21,0.23,0.4,0.88,1.0]
	amp = fit(envelope)
end

# ╔═╡ ea51f472-7ffc-4062-a45c-1410d26b1c3e
md"""
- The wavelength of the traveling wave is a bit longer than the body length.

The slider below controls the wavelength of the traveling wave λ, which you can adjust to see the impact it has on the backbone over the motion cycle.
"""

# ╔═╡ 454f7849-2d9b-4250-94bb-9aed4c2f0d54
md"λ: $@bind λ Slider(0.5:0.1:2, show_value=true, default = 1.1)"

# ╔═╡ 67f29fd6-11ed-415f-abc0-2ed3c84fe60f
begin
	scatter(0:0.2:1, envelope)
	colors = palette(:cyclic_wrwbw_40_90_c42_n256)
	for t in 1/12:1/12:1
		plot!(x,amp.(x).*sin.(2π/λ*x.-2π*t),color=colors[floor(Int,t*256)])
	end
	plot!(ylim=(-1.4,1.4),legend=false)
end

# ╔═╡ db1ed38c-a1aa-4173-bb43-d81afedbfab9
md"""

### Setting up the simulation

Now the thickness and motion are defined, but how will we apply these to a fluid simulation? `WaterLily`uses an [immersed boundary method](https://eprints.soton.ac.uk/369635/) and [automatic differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation) to embed a body into the flow. The upshot is that we don't need to do any meshing; all we need is a signed distance function (SDF) to the surface.

Let's start by defining the SDF to the "backbone", which is a line segment from $x=0\ldots 1$. [See this great video from Inigo Quilez for a derivation of this sdf.](https://www.youtube.com/watch?v=PMltMdi1Wzg) The plot below shows the sdf and the zero contour, which is ... just a line segment.

Simple adjustments to the SDF give us more control of the shape and position. By shifting the y offset as `y = y-shift`, we can move the body laterally. And by subtracting a thickness from the distance as `sdf = sdf-thickness`, we can give the line some width. This is all we need to model the shark.
"""

# ╔═╡ bfc73784-8c27-4cdb-a8e4-fbcbf9ddb3c2
md"shift: $@bind shift Slider(-1:0.5:2, show_value=true, default=0.5)"

# ╔═╡ 76dc572f-3cd2-4f1a-9296-2065c877a6ef
md"thickness: $@bind T Slider(0.001:0.25:2, show_value=true)"

# ╔═╡ 839db88c-a477-4718-88bb-796ab0cd6591
begin
	function segment_sdf(x,y)
		s = clamp(x,0,1)         # distance along the segment
		y = y-shift              # shift laterally
		sdf = √sum(abs2,(x-s,y)) # line segment SDF
		return sdf-T*thk(s)      # subtract thickness
	end
	grid = -1:0.05:2
	contourf(grid,grid,segment_sdf,clim=(-1,2),linewidth=0)
	contour!(grid,grid,segment_sdf,levels=[0],color=:black) # zero contour
end

# ╔═╡ f33b9315-b2d1-4fab-91c9-ed3b63fb4d41
md"""
With the basic SDF tested out, we are ready to set up the WaterLily simulation using the function `fish` defined below:
 - The functions `thk` is passed in to create the `sdf` and the function `amp` is passed in to create the traveling wave `map`.
 - The only numerical parameter passed into `fish` is the length of the fish `L` measured in computational cells. This sets the resolution of the simulation and the size of the fluid arrays.
 - The other parameters are the tail amplitude `A` as a fraction of the length, the [Stouhal number](https://en.wikipedia.org/wiki/Strouhal_number) which sets the motion frequency `ω`, and the [Reynolds number](https://en.wikipedia.org/wiki/Reynolds_number) which sets the fluid viscosity `ν`.
"""

# ╔═╡ 11702be3-d125-4ea4-b7e9-74d34943a164
begin
	function fish(thk,amp,k=5.3;L=2^6,A=0.1,St=0.3,Re=1e4)
		# fraction along fish length
		s(x) = clamp(x[1]/L,0,1)

		# fish geometry: thickened line SDF
		sdf(x,t) = √sum(abs2,x-L*SVector(s(x),0.))-L*thk(s(x))

		# fish motion: travelling wave
		U=1
		ω = 2π*St*U/(2A*L)
		function map(x,t)
			xc = x.-L # shift origin
			return xc-SVector(0.,A*L*amp(s(xc))*sin(k*s(xc)-ω*t))
		end

		# make the fish simulation
		return Simulation((4L+2,2L+2),[U,0.],L;
							ν=U*L/Re,body=AutoBody(sdf,map))
	end

	# Create the swimming shark
	L,A,St = 3*2^5,0.1,0.3
	swimmer = fish(thk,amp;L,A,St);

	# Save a time span for one swimming cycle
	period = 2A/St
	cycle = range(0,23/24*period,length=24)
end

# ╔═╡ d844c02d-41c8-43a8-95dd-742ea6c24f86
md"""
We can test our geometry by plotting the immersed boundary function `μ₀`; which equals 1 in the fluid and 0 in the body.
"""

# ╔═╡ e618b2d9-abe8-4e97-aab2-e87c6278378f
@gif for t ∈ cycle
	measure!(swimmer,t*swimmer.L/swimmer.U);
	contour(swimmer.flow.μ₀[:,:,1]',
		aspect_ratio=:equal,legend=false,border=:none)
end

# ╔═╡ 7329a0ba-c57c-4d77-99da-2f8f66d5a94e
md"""

### Running visualizing and measuring the simulation

That animation of the motion looks great, so we are ready to run the flow simulator!

The `sim_step!(sim,t,remeasure=true)` function runs the simulator up to time `t`, remeasuring the body position every time step. (`remeasure=false` by default since it takes a little extra computational time and isn't needed for statics geometries.)
"""

# ╔═╡ 845cbd96-c01b-49f7-94df-15836a68ebba
# run the simulation a few cycles (this takes few seconds)
begin
	sim_step!(swimmer,3,remeasure=true)
	sim_time(swimmer)
end

# ╔═╡ d5c82801-8387-4ef0-9f34-3279a54603c5
md"""
The simulation has now run forward in time, but there are no visualizations or measurements by default.

To see what is going on, lets make a gif of the vorticity `ω=curl(u)` to visualize the vortices in the wake of the shark. This requires simulating a cycle of motion, and computing the `curl` at all the points `@inside` the simulation.
"""

# ╔═╡ a5c9b186-332f-4812-bb07-0bbe288f5091
begin
	# plot the vorcity ω=curl(u) scaled by the body length L and flow speed U
	function plot_vorticity(sim)
		@inside sim.flow.σ[I] = WaterLily.curl(3,I,sim.flow.u)*sim.L/sim.U
		contourf(sim.flow.σ',
			color=palette(:BuGn), clims=(-10,10),linewidth=0,
			aspect_ratio=:equal,legend=false,border=:none)
	end

	# make a gif over a swimming cycle
	@gif for t ∈ sim_time(swimmer).+cycle
		sim_step!(swimmer,t,remeasure=true)
		plot_vorticity(swimmer)
	end
end

# ╔═╡ 85c555bb-f584-4064-9da7-f7f1e4fd5320
md"""
This is pretty (CFD does stand for _Colorful Fluid Dynamics_ after all), but also tells us something important about the flow. Notice that there are no eddies coming off the body anywhere other than the tail! This is a sign of efficiency since energy is only used to create those trailing vortices.

We can dig in and get some quantitative measurements from the simulation as well. The function `∮nds` takes a integral over the body surface. By passing in the pressure `p`, we can measure the thrust force and side force generated by the shark!
"""

# ╔═╡ c4c3fa5c-79bc-4d55-827f-c952b129ab5f
begin
	function get_force(sim,t)
		sim_step!(sim,t,remeasure=true)
		return WaterLily.∮nds(sim.flow.p,sim.body,t*sim.L/sim.U)./(0.5*sim.L*sim.U^2)
	end
	forces = [get_force(swimmer,t) for t ∈ sim_time(swimmer).+cycle]
	"got forces"
end

# ╔═╡ 9fc14582-108f-4434-83da-f7b32b676bd3
scatter(cycle./period,[first.(forces),last.(forces)],
	labels=permutedims(["thrust","side"]),
	xlabel="scaled time", ylabel="scaled force")

# ╔═╡ 9c67282b-e26a-42bf-836e-fc464b8ca63d
md"""
We can learn a lot from this simple plot. For example, the side-to-side force has the same frequency as the swimming motion itself while the thrust force has double the frequency, with a peak every time the tail passes through the centerline.

### Next steps

This simple model is a great start and it opens up a ton of avenues for improving the shark simulation and suggesting research questions:
 - The instantaneous net forces should be zero in a free swimming body! We could add reaction motions to our model to achieve this. Would the model shark swim in a straight line if we did this, or is a control-loop needed?
 - Real sharks are 3D (gasp!). While we could easily extend this approach using 2D splines, it will take much longer to simulate. Is there a way use GPUs to accelerate the simulations without completely changing the solver?
 - If we were going to make a bio-inspired robot of this shark, we will have constraints on the shape and motion and powering available. Can we use this framework to help optimize our robotic within it's contraints?

Below you can find links to all the packages used in this notebook. Happy Simulating!

1. [Interpolations.jl](https://juliamath.github.io/Interpolations.jl/stable/)
1. [JuliaDiff](https://juliadiff.org/)
1. [JuliaImages](https://juliaimages.org/stable/)
1. [Plots.jl](http://docs.juliaplots.org/latest/)
1. [Pluto.jl](https://github.com/fonsp/Pluto.jl) and [PlutoUI.jl](https://github.com/fonsp/PlutoUI.jl)
1. [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl)
1. [WaterLily.jl](https://github.com/weymouth/WaterLily.jl)
"""

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
PlutoUI = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
WaterLily = "ed894a53-35f9-47f1-b17f-85db9237eebd"

[compat]
Images = "~0.24.1"
Interpolations = "~0.13.3"
Plots = "~1.20.0"
PlutoUI = "~0.7.9"
StaticArrays = "~1.2.11"
WaterLily = "~0.2.3"
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

[[ANSIColoredPrinters]]
git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c"
uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9"
version = "0.0.1"

[[AbstractFFTs]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "485ee0867925449198280d4af84bdb46a2a404d0"
uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c"
version = "1.0.1"

[[Adapt]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "84918055d15b3114ede17ac6a7182f68870c16f7"
uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
version = "3.3.1"

[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

[[ArrayInterface]]
deps = ["IfElse", "LinearAlgebra", "Requires", "SparseArrays", "Static"]
git-tree-sha1 = "2e004e61f76874d153979effc832ae53b56c20ee"
uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
version = "3.1.22"

[[Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[AxisAlgorithms]]
deps = ["LinearAlgebra", "Random", "SparseArrays", "WoodburyMatrices"]
git-tree-sha1 = "a4d07a1c313392a77042855df46c5f534076fab9"
uuid = "13072b0f-2c55-5437-9ae7-d433b7a33950"
version = "1.0.0"

[[AxisArrays]]
deps = ["Dates", "IntervalSets", "IterTools", "RangeArrays"]
git-tree-sha1 = "d127d5e4d86c7680b20c35d40b503c74b9a39b5e"
uuid = "39de3d68-74b9-583c-8d2d-e117c070f3a9"
version = "0.4.4"

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[Bzip2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c3598e525718abcc440f69cc6d5f60dda0a1b61e"
uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0"
version = "1.0.6+5"

[[CEnum]]
git-tree-sha1 = "215a9aa4a1f23fbd05b92769fdd62559488d70e9"
uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82"
version = "0.4.1"

[[Cairo_jll]]
deps = ["Artifacts", "Bzip2_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "e2f47f6d8337369411569fd45ae5753ca10394c6"
uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a"
version = "1.16.0+6"

[[CatIndices]]
deps = ["CustomUnitRanges", "OffsetArrays"]
git-tree-sha1 = "a0f80a09780eed9b1d106a1bf62041c2efc995bc"
uuid = "aafaddc9-749c-510e-ac4f-586e18779b91"
version = "0.2.2"

[[ChainRulesCore]]
deps = ["Compat", "LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "f53ca8d41e4753c41cdafa6ec5f7ce914b34be54"
uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
version = "0.10.13"

[[ColorSchemes]]
deps = ["ColorTypes", "Colors", "FixedPointNumbers", "Random", "StaticArrays"]
git-tree-sha1 = "ed268efe58512df8c7e224d2e170afd76dd6a417"
uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
version = "3.13.0"

[[ColorTypes]]
deps = ["FixedPointNumbers", "Random"]
git-tree-sha1 = "024fe24d83e4a5bf5fc80501a314ce0d1aa35597"
uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
version = "0.11.0"

[[ColorVectorSpace]]
deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"]
git-tree-sha1 = "42a9b08d3f2f951c9b283ea427d96ed9f1f30343"
uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4"
version = "0.9.5"

[[Colors]]
deps = ["ColorTypes", "FixedPointNumbers", "Reexport"]
git-tree-sha1 = "417b0ed7b8b838aa6ca0a87aadf1bb9eb111ce40"
uuid = "5ae59095-9a9b-59fe-a467-6f913c188581"
version = "0.12.8"

[[CommonSubexpressions]]
deps = ["MacroTools", "Test"]
git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7"
uuid = "bbf7d656-a473-5ed7-a52c-81e309532950"
version = "0.3.0"

[[Compat]]
deps = ["Base64", "Dates", "DelimitedFiles", "Distributed", "InteractiveUtils", "LibGit2", "Libdl", "LinearAlgebra", "Markdown", "Mmap", "Pkg", "Printf", "REPL", "Random", "SHA", "Serialization", "SharedArrays", "Sockets", "SparseArrays", "Statistics", "Test", "UUIDs", "Unicode"]
git-tree-sha1 = "344f143fa0ec67e47917848795ab19c6a455f32c"
uuid = "34da2185-b29b-5c13-b0c7-acf172513d20"
version = "3.32.0"

[[CompilerSupportLibraries_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae"

[[ComputationalResources]]
git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7"
uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3"
version = "0.3.2"

[[Contour]]
deps = ["StaticArrays"]
git-tree-sha1 = "9f02045d934dc030edad45944ea80dbd1f0ebea7"
uuid = "d38c429a-6771-53c6-b99e-75d170b6e991"
version = "0.5.7"

[[CoordinateTransformations]]
deps = ["LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "6d1c23e740a586955645500bbec662476204a52c"
uuid = "150eb455-5306-5404-9cee-2592286d6298"
version = "0.6.1"

[[CustomUnitRanges]]
git-tree-sha1 = "537c988076d001469093945f3bd0b300b8d3a7f3"
uuid = "dc8bdbbb-1ca9-579f-8c36-e416f6a65cce"
version = "1.0.1"

[[DataAPI]]
git-tree-sha1 = "ee400abb2298bd13bfc3df1c412ed228061a2385"
uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a"
version = "1.7.0"

[[DataStructures]]
deps = ["Compat", "InteractiveUtils", "OrderedCollections"]
git-tree-sha1 = "4437b64df1e0adccc3e5d1adbc3ac741095e4677"
uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
version = "0.18.9"

[[DataValueInterfaces]]
git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6"
uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464"
version = "1.0.0"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

[[DelimitedFiles]]
deps = ["Mmap"]
uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab"

[[DiffResults]]
deps = ["StaticArrays"]
git-tree-sha1 = "c18e98cba888c6c25d1c3b048e4b3380ca956805"
uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5"
version = "1.0.3"

[[DiffRules]]
deps = ["NaNMath", "Random", "SpecialFunctions"]
git-tree-sha1 = "85d2d9e2524da988bffaf2a381864e20d2dae08d"
uuid = "b552c78f-8df3-52c6-915a-8e097449b14b"
version = "1.2.1"

[[Distances]]
deps = ["LinearAlgebra", "Statistics", "StatsAPI"]
git-tree-sha1 = "abe4ad222b26af3337262b8afb28fab8d215e9f8"
uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
version = "0.10.3"

[[Distributed]]
deps = ["Random", "Serialization", "Sockets"]
uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b"

[[DocStringExtensions]]
deps = ["LibGit2"]
git-tree-sha1 = "a32185f5428d3986f47c2ab78b1f216d5e6cc96f"
uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
version = "0.8.5"

[[Documenter]]
deps = ["ANSIColoredPrinters", "Base64", "Dates", "DocStringExtensions", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"]
git-tree-sha1 = "350dced36c11f794c6c4da5dc6493ec894e50c16"
uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
version = "0.27.5"

[[Downloads]]
deps = ["ArgTools", "LibCURL", "NetworkOptions"]
uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6"

[[EarCut_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "92d8f9f208637e8d2d28c664051a00569c01493d"
uuid = "5ae413db-bbd1-5e63-b57d-d24a61df00f5"
version = "2.1.5+1"

[[EllipsisNotation]]
deps = ["ArrayInterface"]
git-tree-sha1 = "8041575f021cba5a099a456b4163c9a08b566a02"
uuid = "da5c29d0-fa7d-589e-88eb-ea29b0a81949"
version = "1.1.0"

[[Expat_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b3bfd02e98aedfa5cf885665493c5598c350cd2f"
uuid = "2e619515-83b5-522b-bb60-26c02a35a201"
version = "2.2.10+0"

[[FFMPEG]]
deps = ["FFMPEG_jll"]
git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8"
uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a"
version = "0.4.1"

[[FFMPEG_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "LibVPX_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "Pkg", "Zlib_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"]
git-tree-sha1 = "3cc57ad0a213808473eafef4845a74766242e05f"
uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5"
version = "4.3.1+4"

[[FFTViews]]
deps = ["CustomUnitRanges", "FFTW"]
git-tree-sha1 = "70a0cfd9b1c86b0209e38fbfe6d8231fd606eeaf"
uuid = "4f61f5a4-77b1-5117-aa51-3ab5ef4ef0cd"
version = "0.3.1"

[[FFTW]]
deps = ["AbstractFFTs", "FFTW_jll", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"]
git-tree-sha1 = "f985af3b9f4e278b1d24434cbb546d6092fca661"
uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
version = "1.4.3"

[[FFTW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "3676abafff7e4ff07bbd2c42b3d8201f31653dcc"
uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a"
version = "3.3.9+8"

[[FileIO]]
deps = ["Pkg", "Requires", "UUIDs"]
git-tree-sha1 = "256d8e6188f3f1ebfa1a5d17e072a0efafa8c5bf"
uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
version = "1.10.1"

[[FixedPointNumbers]]
deps = ["Statistics"]
git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc"
uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93"
version = "0.8.4"

[[Fontconfig_jll]]
deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "35895cf184ceaab11fd778b4590144034a167a2f"
uuid = "a3f928ae-7b40-5064-980b-68af3947d34b"
version = "2.13.1+14"

[[Formatting]]
deps = ["Printf"]
git-tree-sha1 = "8339d61043228fdd3eb658d86c926cb282ae72a8"
uuid = "59287772-0a20-5a39-b81b-1366585eb4c0"
version = "0.4.2"

[[ForwardDiff]]
deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "NaNMath", "Printf", "Random", "SpecialFunctions", "StaticArrays"]
git-tree-sha1 = "b5e930ac60b613ef3406da6d4f42c35d8dc51419"
uuid = "f6369f11-7733-5829-9624-2563aa707210"
version = "0.10.19"

[[FreeType2_jll]]
deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "cbd58c9deb1d304f5a245a0b7eb841a2560cfec6"
uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7"
version = "2.10.1+5"

[[FriBidi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91"
uuid = "559328eb-81f9-559d-9380-de523a88c83c"
version = "1.0.10+0"

[[GLFW_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pkg", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"]
git-tree-sha1 = "dba1e8614e98949abfa60480b13653813d8f0157"
uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89"
version = "3.3.5+0"

[[GR]]
deps = ["Base64", "DelimitedFiles", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Printf", "Random", "Serialization", "Sockets", "Test", "UUIDs"]
git-tree-sha1 = "182da592436e287758ded5be6e32c406de3a2e47"
uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71"
version = "0.58.1"

[[GR_jll]]
deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Pkg", "Qt5Base_jll", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "d59e8320c2747553788e4fc42231489cc602fa50"
uuid = "d2c73de3-f751-5644-a686-071e5b155ba9"
version = "0.58.1+0"

[[GeometryBasics]]
deps = ["EarCut_jll", "IterTools", "LinearAlgebra", "StaticArrays", "StructArrays", "Tables"]
git-tree-sha1 = "58bcdf5ebc057b085e58d95c138725628dd7453c"
uuid = "5c1252a2-5f33-56bf-86c9-59e7332b4326"
version = "0.4.1"

[[Gettext_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046"
uuid = "78b55507-aeef-58d4-861c-77aaff3498b1"
version = "0.21.0+0"

[[Glib_jll]]
deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "7bf67e9a481712b3dbe9cb3dac852dc4b1162e02"
uuid = "7746bdde-850d-59dc-9ae8-88ece973131d"
version = "2.68.3+0"

[[Graphics]]
deps = ["Colors", "LinearAlgebra", "NaNMath"]
git-tree-sha1 = "2c1cf4df419938ece72de17f368a021ee162762e"
uuid = "a2bd30eb-e257-5431-a919-1863eab51364"
version = "1.1.0"

[[Grisu]]
git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2"
uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe"
version = "1.0.2"

[[HTTP]]
deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"]
git-tree-sha1 = "44e3b40da000eab4ccb1aecdc4801c040026aeb5"
uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3"
version = "0.9.13"

[[IOCapture]]
deps = ["Logging", "Random"]
git-tree-sha1 = "f7be53659ab06ddc986428d3a9dcc95f6fa6705a"
uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89"
version = "0.2.2"

[[IdentityRanges]]
deps = ["OffsetArrays"]
git-tree-sha1 = "be8fcd695c4da16a1d6d0cd213cb88090a150e3b"
uuid = "bbac6d45-d8f3-5730-bfe4-7a449cd117ca"
version = "0.3.1"

[[IfElse]]
git-tree-sha1 = "28e837ff3e7a6c3cdb252ce49fb412c8eb3caeef"
uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173"
version = "0.1.0"

[[ImageAxes]]
deps = ["AxisArrays", "ImageCore", "Reexport", "SimpleTraits"]
git-tree-sha1 = "794ad1d922c432082bc1aaa9fa8ffbd1fe74e621"
uuid = "2803e5a7-5153-5ecf-9a86-9b4c37f5f5ac"
version = "0.6.9"

[[ImageContrastAdjustment]]
deps = ["ColorVectorSpace", "ImageCore", "ImageTransformations", "Parameters"]
git-tree-sha1 = "2e6084db6cccab11fe0bc3e4130bd3d117092ed9"
uuid = "f332f351-ec65-5f6a-b3d1-319c6670881a"
version = "0.3.7"

[[ImageCore]]
deps = ["AbstractFFTs", "Colors", "FixedPointNumbers", "Graphics", "MappedArrays", "MosaicViews", "OffsetArrays", "PaddedViews", "Reexport"]
git-tree-sha1 = "db645f20b59f060d8cfae696bc9538d13fd86416"
uuid = "a09fc81d-aa75-5fe9-8630-4744c3626534"
version = "0.8.22"

[[ImageDistances]]
deps = ["ColorVectorSpace", "Distances", "ImageCore", "ImageMorphology", "LinearAlgebra", "Statistics"]
git-tree-sha1 = "6378c34a3c3a216235210d19b9f495ecfff2f85f"
uuid = "51556ac3-7006-55f5-8cb3-34580c88182d"
version = "0.2.13"

[[ImageFiltering]]
deps = ["CatIndices", "ColorVectorSpace", "ComputationalResources", "DataStructures", "FFTViews", "FFTW", "ImageCore", "LinearAlgebra", "OffsetArrays", "Requires", "SparseArrays", "StaticArrays", "Statistics", "TiledIteration"]
git-tree-sha1 = "bf96839133212d3eff4a1c3a80c57abc7cfbf0ce"
uuid = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
version = "0.6.21"

[[ImageIO]]
deps = ["FileIO", "Netpbm", "PNGFiles", "TiffImages", "UUIDs"]
git-tree-sha1 = "d067570b4d4870a942b19d9ceacaea4fb39b69a1"
uuid = "82e4d734-157c-48bb-816b-45c225c6df19"
version = "0.5.6"

[[ImageMagick]]
deps = ["FileIO", "ImageCore", "ImageMagick_jll", "InteractiveUtils", "Libdl", "Pkg", "Random"]
git-tree-sha1 = "5bc1cb62e0c5f1005868358db0692c994c3a13c6"
uuid = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
version = "1.2.1"

[[ImageMagick_jll]]
deps = ["JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pkg", "Zlib_jll", "libpng_jll"]
git-tree-sha1 = "1c0a2295cca535fabaf2029062912591e9b61987"
uuid = "c73af94c-d91f-53ed-93a7-00f77d67a9d7"
version = "6.9.10-12+3"

[[ImageMetadata]]
deps = ["AxisArrays", "ColorVectorSpace", "ImageAxes", "ImageCore", "IndirectArrays"]
git-tree-sha1 = "ae76038347dc4edcdb06b541595268fca65b6a42"
uuid = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
version = "0.9.5"

[[ImageMorphology]]
deps = ["ColorVectorSpace", "ImageCore", "LinearAlgebra", "TiledIteration"]
git-tree-sha1 = "68e7cbcd7dfaa3c2f74b0a8ab3066f5de8f2b71d"
uuid = "787d08f9-d448-5407-9aad-5290dd7ab264"
version = "0.2.11"

[[ImageQualityIndexes]]
deps = ["ColorVectorSpace", "ImageCore", "ImageDistances", "ImageFiltering", "OffsetArrays", "Statistics"]
git-tree-sha1 = "1198f85fa2481a3bb94bf937495ba1916f12b533"
uuid = "2996bd0c-7a13-11e9-2da2-2f5ce47296a9"
version = "0.2.2"

[[ImageShow]]
deps = ["Base64", "FileIO", "ImageCore", "OffsetArrays", "Requires", "StackViews"]
git-tree-sha1 = "832abfd709fa436a562db47fd8e81377f72b01f9"
uuid = "4e3cecfd-b093-5904-9786-8bbb286a6a31"
version = "0.3.1"

[[ImageTransformations]]
deps = ["AxisAlgorithms", "ColorVectorSpace", "CoordinateTransformations", "IdentityRanges", "ImageCore", "Interpolations", "OffsetArrays", "Rotations", "StaticArrays"]
git-tree-sha1 = "e4cc551e4295a5c96545bb3083058c24b78d4cf0"
uuid = "02fcd773-0e25-5acc-982a-7f6622650795"
version = "0.8.13"

[[Images]]
deps = ["AxisArrays", "Base64", "ColorVectorSpace", "FileIO", "Graphics", "ImageAxes", "ImageContrastAdjustment", "ImageCore", "ImageDistances", "ImageFiltering", "ImageIO", "ImageMagick", "ImageMetadata", "ImageMorphology", "ImageQualityIndexes", "ImageShow", "ImageTransformations", "IndirectArrays", "OffsetArrays", "Random", "Reexport", "SparseArrays", "StaticArrays", "Statistics", "StatsBase", "TiledIteration"]
git-tree-sha1 = "8b714d5e11c91a0d945717430ec20f9251af4bd2"
uuid = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
version = "0.24.1"

[[IndirectArrays]]
git-tree-sha1 = "c2a145a145dc03a7620af1444e0264ef907bd44f"
uuid = "9b13fd28-a010-5f03-acff-a1bbcff69959"
version = "0.5.1"

[[Inflate]]
git-tree-sha1 = "f5fc07d4e706b84f72d54eedcc1c13d92fb0871c"
uuid = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9"
version = "0.1.2"

[[IniFile]]
deps = ["Test"]
git-tree-sha1 = "098e4d2c533924c921f9f9847274f2ad89e018b8"
uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f"
version = "0.5.0"

[[IntelOpenMP_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d979e54b71da82f3a65b62553da4fc3d18c9004c"
uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0"
version = "2018.0.3+2"

[[InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"

[[Interpolations]]
deps = ["AxisAlgorithms", "ChainRulesCore", "LinearAlgebra", "OffsetArrays", "Random", "Ratios", "Requires", "SharedArrays", "SparseArrays", "StaticArrays", "WoodburyMatrices"]
git-tree-sha1 = "1470c80592cf1f0a35566ee5e93c5f8221ebc33a"
uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
version = "0.13.3"

[[IntervalSets]]
deps = ["Dates", "EllipsisNotation", "Statistics"]
git-tree-sha1 = "3cc368af3f110a767ac786560045dceddfc16758"
uuid = "8197267c-284f-5f27-9208-e0e47529a953"
version = "0.5.3"

[[IterTools]]
git-tree-sha1 = "05110a2ab1fc5f932622ffea2a003221f4782c18"
uuid = "c8e1da08-722c-5040-9ed9-7db0dc04731e"
version = "1.3.0"

[[IteratorInterfaceExtensions]]
git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856"
uuid = "82899510-4779-5014-852e-03e436cf321d"
version = "1.0.0"

[[JLLWrappers]]
deps = ["Preferences"]
git-tree-sha1 = "642a199af8b68253517b80bd3bfd17eb4e84df6e"
uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210"
version = "1.3.0"

[[JSON]]
deps = ["Dates", "Mmap", "Parsers", "Unicode"]
git-tree-sha1 = "8076680b162ada2a031f707ac7b4953e30667a37"
uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
version = "0.21.2"

[[JpegTurbo_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d735490ac75c5cb9f1b00d8b5509c11984dc6943"
uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8"
version = "2.1.0+0"

[[LAME_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c"
uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d"
version = "3.100.1+0"

[[LZO_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6"
uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac"
version = "2.10.1+0"

[[LaTeXStrings]]
git-tree-sha1 = "c7f1c695e06c01b95a67f0cd1d34994f3e7db104"
uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
version = "1.2.1"

[[Latexify]]
deps = ["Formatting", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "Printf", "Requires"]
git-tree-sha1 = "a4b12a1bd2ebade87891ab7e36fdbce582301a92"
uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316"
version = "0.15.6"

[[LazyArtifacts]]
deps = ["Artifacts", "Pkg"]
uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3"

[[LibCURL]]
deps = ["LibCURL_jll", "MozillaCACerts_jll"]
uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21"

[[LibCURL_jll]]
deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"]
uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0"

[[LibGit2]]
deps = ["Base64", "NetworkOptions", "Printf", "SHA"]
uuid = "76f85450-5226-5b5a-8eaa-529ad045b433"

[[LibSSH2_jll]]
deps = ["Artifacts", "Libdl", "MbedTLS_jll"]
uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8"

[[LibVPX_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "12ee7e23fa4d18361e7c2cde8f8337d4c3101bc7"
uuid = "dd192d2f-8180-539f-9fb4-cc70b1dcf69a"
version = "1.10.0+0"

[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[Libffi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "761a393aeccd6aa92ec3515e428c26bf99575b3b"
uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490"
version = "3.2.2+0"

[[Libgcrypt_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"]
git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae"
uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4"
version = "1.8.7+0"

[[Libglvnd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"]
git-tree-sha1 = "7739f837d6447403596a75d19ed01fd08d6f56bf"
uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29"
version = "1.3.0+3"

[[Libgpg_error_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9"
uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8"
version = "1.42.0+0"

[[Libiconv_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "42b62845d70a619f063a7da093d995ec8e15e778"
uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531"
version = "1.16.1+1"

[[Libmount_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "9c30530bf0effd46e15e0fdcf2b8636e78cbbd73"
uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9"
version = "2.35.0+0"

[[Libtiff_jll]]
deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Pkg", "Zlib_jll", "Zstd_jll"]
git-tree-sha1 = "340e257aada13f95f98ee352d316c3bed37c8ab9"
uuid = "89763e89-9b03-5906-acba-b20f662cd828"
version = "4.3.0+0"

[[Libuuid_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7f3efec06033682db852f8b3bc3c1d2b0a0ab066"
uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700"
version = "2.36.0+0"

[[LinearAlgebra]]
deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[LogExpFunctions]]
deps = ["DocStringExtensions", "LinearAlgebra"]
git-tree-sha1 = "7bd5f6565d80b6bf753738d2bc40a5dfea072070"
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
version = "0.2.5"

[[Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"

[[MKL_jll]]
deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "Pkg"]
git-tree-sha1 = "c253236b0ed414624b083e6b72bfe891fbd2c7af"
uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7"
version = "2021.1.1+1"

[[MacroTools]]
deps = ["Markdown", "Random"]
git-tree-sha1 = "0fb723cd8c45858c22169b2e42269e53271a6df7"
uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
version = "0.5.7"

[[MappedArrays]]
git-tree-sha1 = "18d3584eebc861e311a552cbb67723af8edff5de"
uuid = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
version = "0.4.0"

[[Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[[MbedTLS]]
deps = ["Dates", "MbedTLS_jll", "Random", "Sockets"]
git-tree-sha1 = "1c38e51c3d08ef2278062ebceade0e46cefc96fe"
uuid = "739be429-bea8-5141-9913-cc70e7f3736d"
version = "1.0.3"

[[MbedTLS_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1"

[[Measures]]
git-tree-sha1 = "e498ddeee6f9fdb4551ce855a46f54dbd900245f"
uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e"
version = "0.3.1"

[[Missings]]
deps = ["DataAPI"]
git-tree-sha1 = "4ea90bd5d3985ae1f9a908bd4500ae88921c5ce7"
uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28"
version = "1.0.0"

[[Mmap]]
uuid = "a63ad114-7e13-5084-954f-fe012c677804"

[[MosaicViews]]
deps = ["MappedArrays", "OffsetArrays", "PaddedViews", "StackViews"]
git-tree-sha1 = "b34e3bc3ca7c94914418637cb10cc4d1d80d877d"
uuid = "e94cdb99-869f-56ef-bcf0-1ae2bcbe0389"
version = "0.3.3"

[[MozillaCACerts_jll]]
uuid = "14a3606d-f60d-562e-9121-12d972cd8159"

[[NaNMath]]
git-tree-sha1 = "bfe47e760d60b82b66b61d2d44128b62e3a369fb"
uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
version = "0.3.5"

[[Netpbm]]
deps = ["ColorVectorSpace", "FileIO", "ImageCore"]
git-tree-sha1 = "09589171688f0039f13ebe0fdcc7288f50228b52"
uuid = "f09324ee-3d7c-5217-9330-fc30815ba969"
version = "1.0.1"

[[NetworkOptions]]
uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908"

[[OffsetArrays]]
deps = ["Adapt"]
git-tree-sha1 = "5cc97a6f806ba1b36bac7078b866d4297ae8c463"
uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
version = "1.10.4"

[[Ogg_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7937eda4681660b4d6aeeecc2f7e1c81c8ee4e2f"
uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051"
version = "1.3.5+0"

[[OpenSSL_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "15003dcb7d8db3c6c857fda14891a539a8f2705a"
uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95"
version = "1.1.10+0"

[[OpenSpecFun_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1"
uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
version = "0.5.5+0"

[[Opus_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720"
uuid = "91d4177d-7536-5919-b921-800302f37372"
version = "1.3.2+0"

[[OrderedCollections]]
git-tree-sha1 = "85f8e6578bf1f9ee0d11e7bb1b1456435479d47c"
uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
version = "1.4.1"

[[PCRE_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b2a7af664e098055a7529ad1a900ded962bca488"
uuid = "2f80f16e-611a-54ab-bc61-aa92de5b98fc"
version = "8.44.0+0"

[[PNGFiles]]
deps = ["Base64", "CEnum", "ImageCore", "IndirectArrays", "OffsetArrays", "libpng_jll"]
git-tree-sha1 = "520e28d4026d16dcf7b8c8140a3041f0e20a9ca8"
uuid = "f57f5aa1-a3ce-4bc8-8ab9-96f992907883"
version = "0.3.7"

[[PaddedViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "59925f4ae6861cddc2313a47514b93b6740f9b6f"
uuid = "5432bcbf-9aad-5242-b902-cca2824c8663"
version = "0.5.9"

[[Parameters]]
deps = ["OrderedCollections", "UnPack"]
git-tree-sha1 = "2276ac65f1e236e0a6ea70baff3f62ad4c625345"
uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a"
version = "0.12.2"

[[Parsers]]
deps = ["Dates"]
git-tree-sha1 = "477bf42b4d1496b454c10cce46645bb5b8a0cf2c"
uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0"
version = "2.0.2"

[[Pixman_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "b4f5d02549a10e20780a24fce72bea96b6329e29"
uuid = "30392449-352a-5448-841d-b1acce4e97dc"
version = "0.40.1+0"

[[Pkg]]
deps = ["Artifacts", "Dates", "Downloads", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"]
uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"

[[PkgVersion]]
deps = ["Pkg"]
git-tree-sha1 = "a7a7e1a88853564e551e4eba8650f8c38df79b37"
uuid = "eebad327-c553-4316-9ea0-9fa01ccd7688"
version = "0.1.1"

[[PlotThemes]]
deps = ["PlotUtils", "Requires", "Statistics"]
git-tree-sha1 = "a3a964ce9dc7898193536002a6dd892b1b5a6f1d"
uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a"
version = "2.0.1"

[[PlotUtils]]
deps = ["ColorSchemes", "Colors", "Dates", "Printf", "Random", "Reexport", "Statistics"]
git-tree-sha1 = "501c20a63a34ac1d015d5304da0e645f42d91c9f"
uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043"
version = "1.0.11"

[[Plots]]
deps = ["Base64", "Contour", "Dates", "FFMPEG", "FixedPointNumbers", "GR", "GeometryBasics", "JSON", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "PlotThemes", "PlotUtils", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs"]
git-tree-sha1 = "e39bea10478c6aff5495ab522517fae5134b40e3"
uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
version = "1.20.0"

[[PlutoUI]]
deps = ["Base64", "Dates", "InteractiveUtils", "JSON", "Logging", "Markdown", "Random", "Reexport", "Suppressor"]
git-tree-sha1 = "44e225d5837e2a2345e69a1d1e01ac2443ff9fcb"
uuid = "7f904dfe-b85e-4ff6-b463-dae2292396a8"
version = "0.7.9"

[[Preferences]]
deps = ["TOML"]
git-tree-sha1 = "00cfd92944ca9c760982747e9a1d0d5d86ab1e5a"
uuid = "21216c6a-2e73-6563-6e65-726566657250"
version = "1.2.2"

[[Printf]]
deps = ["Unicode"]
uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7"

[[ProgressMeter]]
deps = ["Distributed", "Printf"]
git-tree-sha1 = "afadeba63d90ff223a6a48d2009434ecee2ec9e8"
uuid = "92933f4c-e287-5a05-a399-4b506db050ca"
version = "1.7.1"

[[Qt5Base_jll]]
deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Pkg", "Xorg_libXext_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "xkbcommon_jll"]
git-tree-sha1 = "ad368663a5e20dbb8d6dc2fddeefe4dae0781ae8"
uuid = "ea2cea3b-5b76-57ae-a6ef-0a8af62496e1"
version = "5.15.3+0"

[[REPL]]
deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"]
uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[RangeArrays]]
git-tree-sha1 = "b9039e93773ddcfc828f12aadf7115b4b4d225f5"
uuid = "b3c3ace0-ae52-54e7-9d0b-2c1406fd6b9d"
version = "0.3.2"

[[Ratios]]
git-tree-sha1 = "37d210f612d70f3f7d57d488cb3b6eff56ad4e41"
uuid = "c84ed2f1-dad5-54f0-aa8e-dbefe2724439"
version = "0.4.0"

[[RecipesBase]]
git-tree-sha1 = "b3fb709f3c97bfc6e948be68beeecb55a0b340ae"
uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
version = "1.1.1"

[[RecipesPipeline]]
deps = ["Dates", "NaNMath", "PlotUtils", "RecipesBase"]
git-tree-sha1 = "2a7a2469ed5d94a98dea0e85c46fa653d76be0cd"
uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c"
version = "0.3.4"

[[Reexport]]
git-tree-sha1 = "5f6c21241f0f655da3952fd60aa18477cf96c220"
uuid = "189a3867-3050-52da-a836-e630ba90ab69"
version = "1.1.0"

[[Requires]]
deps = ["UUIDs"]
git-tree-sha1 = "4036a3bd08ac7e968e27c203d45f5fff15020621"
uuid = "ae029012-a4dd-5104-9daa-d747884805df"
version = "1.1.3"

[[Rotations]]
deps = ["LinearAlgebra", "StaticArrays", "Statistics"]
git-tree-sha1 = "2ed8d8a16d703f900168822d83699b8c3c1a5cd8"
uuid = "6038ab10-8711-5258-84ad-4b1120ba62dc"
version = "1.0.2"

[[SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"

[[Scratch]]
deps = ["Dates"]
git-tree-sha1 = "0b4b7f1393cff97c33891da2a0bf69c6ed241fda"
uuid = "6c6a2e73-6563-6170-7368-637461726353"
version = "1.1.0"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[SharedArrays]]
deps = ["Distributed", "Mmap", "Random", "Serialization"]
uuid = "1a1011a3-84de-559e-8e89-a11a2f7dc383"

[[Showoff]]
deps = ["Dates", "Grisu"]
git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de"
uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f"
version = "1.0.3"

[[SimpleTraits]]
deps = ["InteractiveUtils", "MacroTools"]
git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231"
uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d"
version = "0.9.4"

[[Sockets]]
uuid = "6462fe0b-24de-5631-8697-dd941f90decc"

[[SortingAlgorithms]]
deps = ["DataStructures"]
git-tree-sha1 = "b3363d7460f7d098ca0912c69b082f75625d7508"
uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c"
version = "1.0.1"

[[SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[[SpecialFunctions]]
deps = ["ChainRulesCore", "LogExpFunctions", "OpenSpecFun_jll"]
git-tree-sha1 = "508822dca004bf62e210609148511ad03ce8f1d8"
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
version = "1.6.0"

[[StackViews]]
deps = ["OffsetArrays"]
git-tree-sha1 = "46e589465204cd0c08b4bd97385e4fa79a0c770c"
uuid = "cae243ae-269e-4f55-b966-ac2d0dc13c15"
version = "0.1.1"

[[Static]]
deps = ["IfElse"]
git-tree-sha1 = "62701892d172a2fa41a1f829f66d2b0db94a9a63"
uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
version = "0.3.0"

[[StaticArrays]]
deps = ["LinearAlgebra", "Random", "Statistics"]
git-tree-sha1 = "b28f39450421d07d89ab5d126fd15e5246350e8a"
uuid = "90137ffa-7385-5640-81b9-e52037218182"
version = "1.2.11"

[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[[StatsAPI]]
git-tree-sha1 = "1958272568dc176a1d881acb797beb909c785510"
uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
version = "1.0.0"

[[StatsBase]]
deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"]
git-tree-sha1 = "fed1ec1e65749c4d96fc20dd13bea72b55457e62"
uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
version = "0.33.9"

[[StructArrays]]
deps = ["Adapt", "DataAPI", "StaticArrays", "Tables"]
git-tree-sha1 = "000e168f5cc9aded17b6999a560b7c11dda69095"
uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a"
version = "0.6.0"

[[Suppressor]]
git-tree-sha1 = "a819d77f31f83e5792a76081eee1ea6342ab8787"
uuid = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
version = "0.2.0"

[[TOML]]
deps = ["Dates"]
uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76"

[[TableTraits]]
deps = ["IteratorInterfaceExtensions"]
git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39"
uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c"
version = "1.0.1"

[[Tables]]
deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "TableTraits", "Test"]
git-tree-sha1 = "d0c690d37c73aeb5ca063056283fde5585a41710"
uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
version = "1.5.0"

[[Tar]]
deps = ["ArgTools", "SHA"]
uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e"

[[TensorCore]]
deps = ["LinearAlgebra"]
git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6"
uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50"
version = "0.1.1"

[[Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[[TiffImages]]
deps = ["ColorTypes", "DocStringExtensions", "FileIO", "FixedPointNumbers", "IndirectArrays", "Inflate", "OffsetArrays", "OrderedCollections", "PkgVersion", "ProgressMeter"]
git-tree-sha1 = "03fb246ac6e6b7cb7abac3b3302447d55b43270e"
uuid = "731e570b-9d59-4bfa-96dc-6df516fadf69"
version = "0.4.1"

[[TiledIteration]]
deps = ["OffsetArrays"]
git-tree-sha1 = "52c5f816857bfb3291c7d25420b1f4aca0a74d18"
uuid = "06e1c1a7-607b-532d-9fad-de7d9aa2abac"
version = "0.3.0"

[[URIs]]
git-tree-sha1 = "97bbe755a53fe859669cd907f2d96aee8d2c1355"
uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
version = "1.3.0"

[[UUIDs]]
deps = ["Random", "SHA"]
uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[[UnPack]]
git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b"
uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
version = "1.0.2"

[[Unicode]]
uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5"

[[WaterLily]]
deps = ["Documenter", "ForwardDiff", "LinearAlgebra", "StaticArrays"]
git-tree-sha1 = "9d17cfa8a5e12d568bddd18f4332d1919316fc28"
uuid = "ed894a53-35f9-47f1-b17f-85db9237eebd"
version = "0.2.3"

[[Wayland_jll]]
deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"]
git-tree-sha1 = "3e61f0b86f90dacb0bc0e73a0c5a83f6a8636e23"
uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89"
version = "1.19.0+0"

[[Wayland_protocols_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll"]
git-tree-sha1 = "2839f1c1296940218e35df0bbb220f2a79686670"
uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91"
version = "1.18.0+4"

[[WoodburyMatrices]]
deps = ["LinearAlgebra", "SparseArrays"]
git-tree-sha1 = "59e2ad8fd1591ea019a5259bd012d7aee15f995c"
uuid = "efce3f68-66dc-5838-9240-27a6d6f5f9b6"
version = "0.5.3"

[[XML2_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "Zlib_jll"]
git-tree-sha1 = "1acf5bdf07aa0907e0a37d3718bb88d4b687b74a"
uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a"
version = "2.9.12+0"

[[XSLT_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"]
git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a"
uuid = "aed1982a-8fda-507f-9586-7b0439959a61"
version = "1.1.34+0"

[[Xorg_libX11_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll", "Xorg_xtrans_jll"]
git-tree-sha1 = "5be649d550f3f4b95308bf0183b82e2582876527"
uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc"
version = "1.6.9+4"

[[Xorg_libXau_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4e490d5c960c314f33885790ed410ff3a94ce67e"
uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec"
version = "1.0.9+4"

[[Xorg_libXcursor_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd"
uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724"
version = "1.2.0+4"

[[Xorg_libXdmcp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "4fe47bd2247248125c428978740e18a681372dd4"
uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05"
version = "1.1.3+4"

[[Xorg_libXext_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3"
uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3"
version = "1.3.4+4"

[[Xorg_libXfixes_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4"
uuid = "d091e8ba-531a-589c-9de9-94069b037ed8"
version = "5.0.3+4"

[[Xorg_libXi_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"]
git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246"
uuid = "a51aa0fd-4e3c-5386-b890-e753decda492"
version = "1.7.10+4"

[[Xorg_libXinerama_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"]
git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123"
uuid = "d1454406-59df-5ea1-beac-c340f2130bc3"
version = "1.1.4+4"

[[Xorg_libXrandr_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"]
git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631"
uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484"
version = "1.5.2+4"

[[Xorg_libXrender_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96"
uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa"
version = "0.9.10+4"

[[Xorg_libpthread_stubs_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "6783737e45d3c59a4a4c4091f5f88cdcf0908cbb"
uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74"
version = "0.1.0+3"

[[Xorg_libxcb_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"]
git-tree-sha1 = "daf17f441228e7a3833846cd048892861cff16d6"
uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b"
version = "1.13.0+3"

[[Xorg_libxkbfile_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"]
git-tree-sha1 = "926af861744212db0eb001d9e40b5d16292080b2"
uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a"
version = "1.1.0+4"

[[Xorg_xcb_util_image_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97"
uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b"
version = "0.4.0+1"

[[Xorg_xcb_util_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"]
git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1"
uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5"
version = "0.4.0+1"

[[Xorg_xcb_util_keysyms_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00"
uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7"
version = "0.4.0+1"

[[Xorg_xcb_util_renderutil_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e"
uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e"
version = "0.3.9+1"

[[Xorg_xcb_util_wm_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"]
git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67"
uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361"
version = "0.4.1+1"

[[Xorg_xkbcomp_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxkbfile_jll"]
git-tree-sha1 = "4bcbf660f6c2e714f87e960a171b119d06ee163b"
uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4"
version = "1.4.2+4"

[[Xorg_xkeyboard_config_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xkbcomp_jll"]
git-tree-sha1 = "5c8424f8a67c3f2209646d4425f3d415fee5931d"
uuid = "33bec58e-1273-512f-9401-5d533626f822"
version = "2.27.0+4"

[[Xorg_xtrans_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "79c31e7844f6ecf779705fbc12146eb190b7d845"
uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10"
version = "1.4.0+3"

[[Zlib_jll]]
deps = ["Libdl"]
uuid = "83775a58-1f1d-513f-b197-d71354ab007a"

[[Zstd_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "cc4bf3fdde8b7e3e9fa0351bdeedba1cf3b7f6e6"
uuid = "3161d3a3-bdf6-5164-811a-617609db77b4"
version = "1.5.0+0"

[[libass_jll]]
deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "acc685bcf777b2202a904cdcb49ad34c2fa1880c"
uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0"
version = "0.14.0+4"

[[libfdk_aac_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "7a5780a0d9c6864184b3a2eeeb833a0c871f00ab"
uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280"
version = "0.1.6+4"

[[libpng_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"]
git-tree-sha1 = "94d180a6d2b5e55e447e2d27a29ed04fe79eb30c"
uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f"
version = "1.6.38+0"

[[libvorbis_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"]
git-tree-sha1 = "c45f4e40e7aafe9d086379e5578947ec8b95a8fb"
uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a"
version = "1.3.7+0"

[[nghttp2_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d"

[[p7zip_jll]]
deps = ["Artifacts", "Libdl"]
uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0"

[[x264_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "d713c1ce4deac133e3334ee12f4adff07f81778f"
uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a"
version = "2020.7.14+2"

[[x265_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"]
git-tree-sha1 = "487da2f8f2f0c8ee0e83f39d13037d6bbf0a45ab"
uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76"
version = "3.0.0+3"

[[xkbcommon_jll]]
deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"]
git-tree-sha1 = "ece2350174195bb31de1a63bea3a41ae1aa593b6"
uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd"
version = "0.9.1+5"
"""

# ╔═╡ Cell order:
# ╠═c028100e-f47b-11eb-2547-df779889e265
# ╟─b0c8df66-1e63-456e-be32-d469e6972e00
# ╟─e9384124-9708-44eb-b5e7-75abc5177a69
# ╟─c8293b7b-0585-45b3-8763-8cfa4671b155
# ╠═a48ac92c-5cb0-4e51-a08f-b34bc8b06d04
# ╟─ad96bcbe-4482-4486-b817-e788604d1d96
# ╟─47eb9e3f-5617-417a-8924-5a38d90390e8
# ╟─8752bf4d-50e6-42ea-8d43-c41699769f77
# ╠═71c9bc8f-921d-4df4-acc2-7321ce6648e0
# ╟─d830e69e-f085-495e-be2c-8ea44582fa20
# ╠═c2b7a082-c05d-41bb-af4c-eb23f6a331bf
# ╟─ea51f472-7ffc-4062-a45c-1410d26b1c3e
# ╟─454f7849-2d9b-4250-94bb-9aed4c2f0d54
# ╠═67f29fd6-11ed-415f-abc0-2ed3c84fe60f
# ╟─db1ed38c-a1aa-4173-bb43-d81afedbfab9
# ╟─bfc73784-8c27-4cdb-a8e4-fbcbf9ddb3c2
# ╟─76dc572f-3cd2-4f1a-9296-2065c877a6ef
# ╠═839db88c-a477-4718-88bb-796ab0cd6591
# ╟─f33b9315-b2d1-4fab-91c9-ed3b63fb4d41
# ╠═11702be3-d125-4ea4-b7e9-74d34943a164
# ╟─d844c02d-41c8-43a8-95dd-742ea6c24f86
# ╠═e618b2d9-abe8-4e97-aab2-e87c6278378f
# ╟─7329a0ba-c57c-4d77-99da-2f8f66d5a94e
# ╠═845cbd96-c01b-49f7-94df-15836a68ebba
# ╟─d5c82801-8387-4ef0-9f34-3279a54603c5
# ╠═a5c9b186-332f-4812-bb07-0bbe288f5091
# ╟─85c555bb-f584-4064-9da7-f7f1e4fd5320
# ╠═c4c3fa5c-79bc-4d55-827f-c952b129ab5f
# ╟─9fc14582-108f-4434-83da-f7b32b676bd3
# ╟─9c67282b-e26a-42bf-836e-fc464b8ca63d
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
#=
PolarPlotter.jl
Jacob Child
May 20th, 2022
=#


text:
struct StaticTapirEx{NThreads,SIMD,TaskGroup} <: FoldsBase.Executor
    nthreads::Val{NThreads}
    simd::Val{SIMD}
    taskgroup::TaskGroup

    StaticTapirEx(
        nthreads::Val{NThreads},
        simd::Val{SIMD},
        taskgroup::TaskGroup,
    ) where {NThreads,SIMD,TaskGroup} =
        new{NThreads,SIMD,Core.Typeof(taskgroup)}(nthreads, simd, taskgroup)
end

StaticTapirEx(;
    nthreads::Union{Val,Integer} = static_nthreads(),
    simd::SIMDFlag = Val(false),
    # stoppable::Union{Bool,Nothing} = nothing,
    taskgroup = Tapir.taskgroup,
) = StaticTapirEx(
    asval(Int, nthreads),
    asval(Bool, simd),
    # asval(Union{Bool,Nothing}, stoppable),
    taskgroup,
)

@inline function Transducers.transduce(
    xf::XF,
    rf::RF,
    init,
    xs,
    ex::StaticTapirEx,
) where {XF,RF}
    rf0 = _reducingfunction(xf, rf; init = init)
    rf1, coll = retransform(rf0, xs)
    rf2 = maybe_usesimd(rf1, ex.simd)
    accs = transduce_partitions(rf2, init, static_chunks(coll, ex.nthreads), ex.taskgroup)
    result = complete(rf2, foldlargs((a, b) -> combine(rf, a, b), accs...))
    if unreduced(result) isa DefaultInitOf
        throw(EmptyResultError(rf2))
    end
    return result
end

# **For now** (at least), we need to use `@generated` for unrolling `@spawn`
# because syncregion cannot cross function boundaries.
@generated function transduce_partitions(
    rf::RF,
    init,
    partitions::NTuple{N,Any},
    taskgroup,
) where {RF,N}
    accs = [Symbol(:acc, i) for i in 1:N]
    spawns = map(1:N) do i
        # Like `_reduce_basecase` but without `SizedReducible`:
        if USE_TAPIR_OUTPUT
            lhs = accs[i]
        else
            lhs = Expr(:$, accs[i])
        end
        basecase = :($lhs = foldl_nocomplete(rf, start(rf, init), partitions[$i]))
        if i < N
            :(Tapir.@spawn $basecase)
        else
            basecase  # run the last basecase in the root
        end
    end
    header = if USE_TAPIR_OUTPUT
        :(Tapir.@output($(accs...),))
    else
        :(local $(accs...))
        nothing
    end
    quote
        Base.@_inline_meta
        $header
        Tapir.@sync taskgroup() begin
            $(spawns...)
        end
        return ($(accs...),)
    end
end

struct StaticPartitonIterator{Partitions}
    partitions::Partitions
end
# TODO: actually implement `iterate`

"""
    FoldsTapir.static_partition(xs, [n])

Like `Iterators.partition`, but for `StaticTapirEx`.
"""
function static_partition(xs, n = static_nthreads())
    partitions = map(tuple, static_chunks(xs, n))
    return StaticPartitonIterator(partitions)
end

static_chunks(itr::StaticPartitonIterator, _) = itr.partitions

Transducers.executor_type(::StaticPartitonIterator) = StaticTapirEx


text:
using VariableFrequencyOPF
using Test

using JuMP
using MathOptInterface
using JSON

@testset "VariableFrequencyOPF.jl" begin
    include("opf.jl")
    include("comparisons.jl")
end


text:
using Test
import GDAL
import ArchGDAL; const AG = ArchGDAL

@testset "Test Array getindex" begin
    AG.read("ospy/data4/aster.img") do ds
        @testset "Dataset indexing" begin
            @testset "dims dropped correctly" begin
                @test typeof(ds[:, :, :]) <: Array{UInt8,3}
                @test typeof(ds[:, :, 1]) <: Array{UInt8,2}
                @test typeof(ds[:, 1, 1]) <: Array{UInt8,1}
                @test typeof(ds[1, 1, 1]) <: UInt8
                @test typeof(ds[:, :]) <: Array{UInt8,2}
                @test typeof(ds[:, 1]) <: Array{UInt8,1}
                @test typeof(ds[1, 1]) <: UInt8
            end
            @testset "range indexing" begin
                buffer = ds[1:AG.width(ds), 1:AG.height(ds), 1:1]
                @test typeof(buffer) <: Array{UInt8,3}
                total = sum(buffer)
                count = sum(buffer .> 0)
                @test total / count ≈ 76.33891347095299
                @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172
            end
            @testset "colon indexing" begin
                buffer = ds[:, :, 1]
                @test buffer == ds[:, :]
                total = sum(buffer)
                count = sum(buffer .> 0)
                @test total / count ≈ 76.33891347095299
                @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172
            end
            @testset "int indexing" begin
                @test ds[755, 2107, 1] == 0xff
            end
        end
        @testset "RasterBand indexing" begin
            band = AG.getband(ds, 1)
            @testset "dims dropped correctly" begin
                @test typeof(band[:, :]) <: Array{UInt8,2}
                @test typeof(band[:, 1]) <: Array{UInt8,1}
                @test typeof(band[1, 1]) <: UInt8
            end
            @testset "range indexing" begin
                buffer = band[1:AG.width(band), 1:AG.height(band)]
                @test typeof(buffer) <: Array{UInt8,2}
                total = sum(buffer)
                count = sum(buffer .> 0)
                @test total / count ≈ 76.33891347095299
                @test total / (AG.height(band) * AG.width(band)) ≈ 47.55674749653172
            end
            @testset "colon indexing" begin
                buffer = band[:, :]
                total = sum(buffer)
                count = sum(buffer .> 0)
                @test total / count ≈ 76.33891347095299
                @test total / (AG.height(band) * AG.width(band)) ≈ 47.55674749653172
            end
            @testset "int indexing" begin
                @test band[755, 2107] == 0xff
            end
        end
    end
end

cp("ospy/data4/aster.img", "ospy/data4/aster_write.img"; force=true)

@testset "Test Array setindex" begin
    AG.read("ospy/data4/aster_write.img"; flags=AG.OF_Update) do ds
        @testset "Dataset setindex" begin
            @test ds[755, 2107, 1] == 0xff
            ds[755, 2107, 1] = 0x00
            @test ds[755, 2107, 1] == 0x00
            ds[755:755, 2107:2107, 1:1] = reshape([0x01], 1, 1, 1)
            @test ds[755, 2107, 1] == 0x01
            ds[755:755, 2107:2107, 1] = reshape([0x02], 1, 1)
            @test ds[755, 2107, 1] == 0x02
            ds[755:755, 2107, 1] = [0x03]
            @test ds[755, 2107, 1] == 0x03
            ds[755, 2107] = 0x04
            @test ds[755, 2107] == 0x04
            ds[755:755, 2107:2107] = reshape([0xff], 1, 1)
            @test ds[755, 2107] == 0xff
            buffer = ds[:, :]
            ds[:, :] = buffer .* 0x00
            @test sum(ds[:, :]) == 0x00
            ds[:, 1:500] = buffer[:, 1:500]
            ds[:, 501:end] = buffer[:, 501:end]
            @test sum(buffer) / sum(buffer .> 0) ≈ 76.33891347095299
            @test_throws DimensionMismatch ds[:, 501:end] = [1, 2, 3]
        end
        @testset "RasterBand setindex" begin
            band = AG.getband(ds, 1)
            @test band[755, 2107] == 0xff
            band[755:755, 2107] = [0x00]
            @test band[755, 2107] == 0x00
            band[755, 2107] = 0x01
            @test band[755, 2107] == 0x01
            band[755:755, 2107:2107] = reshape([0xff], 1, 1)
            @test band[755, 2107] == 0xff
            buffer = band[:, :]
            band[:, :] = buffer .* 0x00
            @test sum(band[:, :]) == 0x00
            band[:, 1:500] = buffer[:, 1:500]
            band[:, 501:end] = buffer[:, 501:end]
            @test sum(buffer) / sum(buffer .> 0) ≈ 76.33891347095299
            @test_throws DimensionMismatch band[:, 501:end] = [1, 2, 3]
        end
    end
end

@testset "Test Array constructor" begin
    AG.read("ospy/data4/aster_write.img"; flags=AG.OF_Update) do ds
        buffer = Array(ds)
        typeof(buffer) <: Array{UInt8,3}
        total = sum(buffer[:, :, 1:1])
        count = sum(buffer[:, :, 1:1] .> 0)
        @test total / count ≈ 76.33891347095299
        @test total / (AG.height(ds) * AG.width(ds)) ≈ 47.55674749653172
    end
end