text:
module TestExamples3DLatticeBoltzmann

using Test
using Trixi

include("test_trixi.jl")

# pathof(Trixi) returns /path/to/Trixi/src/Trixi.jl, dirname gives the parent directory
EXAMPLES_DIR = joinpath(pathof(Trixi) |> dirname |> dirname, "examples", "3d")

@testset "Lattice-Boltzmann" begin
  @testset "elixir_lbm_constant.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_lbm_constant.jl"),
      l2   = [5.861930511199053e-16, 6.282772442363201e-16, 5.47591540767842e-16,
              6.379244339335046e-16, 5.81421258408584e-16, 6.634626069779352e-16,
              2.9188639102691596e-16, 2.1539168764807097e-16, 3.0131714783573674e-16,
              2.2126555191449657e-16, 2.622901122013102e-16, 2.2115776381362187e-16,
              6.32031843208421e-17, 6.103875364141341e-17, 6.821138567646266e-17,
              6.48022057541854e-17, 5.013642264182462e-17, 7.169498358338181e-17,
              1.3879946832660896e-16, 5.649850447603551e-17, 3.4686869828797276e-17,
              3.8719518141614167e-17, 3.5852179230919525e-17, 4.292415147083455e-17,
              3.608945206319316e-17, 4.187850903422495e-17, 8.254587760492495e-16],
      linf = [1.1657341758564144e-15, 1.5543122344752192e-15, 1.1657341758564144e-15,
              1.4710455076283324e-15, 1.0547118733938987e-15, 1.4988010832439613e-15,
              5.273559366969494e-16, 5.065392549852277e-16, 5.967448757360216e-16,
              5.342948306008566e-16, 5.412337245047638e-16, 5.967448757360216e-16,
              3.608224830031759e-16, 3.469446951953614e-16, 2.42861286636753e-16,
              2.498001805406602e-16, 2.2898349882893854e-16, 4.3021142204224816e-16,
              2.1510571102112408e-16, 1.43982048506075e-16, 1.214306433183765e-16,
              1.4224732503009818e-16, 1.214306433183765e-16, 1.3877787807814457e-16,
              8.673617379884035e-17, 9.71445146547012e-17, 2.7755575615628914e-15],
      tspan=(0.0, 0.5))
  end

  @testset "elixir_lbm_taylor_green_vortex.jl" begin
    @test_trixi_include(joinpath(EXAMPLES_DIR, "elixir_lbm_taylor_green_vortex.jl"),
      l2   = [7.516128821554829e-5, 7.516128821554695e-5, 7.516128821554932e-5,
              7.516128821554856e-5, 7.022624942862394e-6, 7.022624942862171e-6,
              2.961794427142361e-6, 2.961794427142168e-6, 2.6527195181287848e-5,
              2.6527195181287404e-5, 2.652719518128811e-5, 2.6527195181287916e-5,
              2.9617944271423104e-6, 2.961794427142108e-6, 2.652719518128758e-5,
              2.6527195181287513e-5, 2.6527195181287916e-5, 2.6527195181287872e-5,
              6.697526775466e-6, 6.697526775466029e-6, 6.697526775465903e-6, 6.697526775465986e-6,
              6.697526775466051e-6, 6.697526775465938e-6, 6.697526775465983e-6,
              6.697526775466005e-6, 2.8805380887802028e-6],
      linf = [0.00021570074723312183, 0.0002157007472331357, 0.00021570074723314958,
              0.00021570074723316346, 1.9675688146578163e-5, 1.967568814660592e-5,
              9.53539471547013e-6, 9.53539471547013e-6, 5.7339968249785905e-5,
              5.7339968249778966e-5, 5.7339968249792844e-5, 5.7339968249778966e-5,
              9.535394715480539e-6, 9.53539471546666e-6, 5.73399682497755e-5, 5.7339968249785905e-5,
              5.7339968249782436e-5, 5.7339968249782436e-5, 1.3570400471223966e-5,
              1.3570400471222231e-5, 1.3570400471220496e-5, 1.3570400471224833e-5,
              1.3570400471223966e-5, 1.3570400471221364e-5, 1.3570400471224833e-5,
              1.3570400471224833e-5, 1.4249297322244114e-5],
      tspan=(0.0, 0.1),
      initial_refinement_level=3)
  end
end

end # module

ext: jl


text:
using ParameterEstimateScatterPlots, Plots, Test

# This test just makes sure scatterestimates doesn't error
function test1()

    x = LinRange(0, 1, 10)
    xhat = x.^1.1
    name = "x"
    units = "cm"
    scatterestimates(x, xhat; name, units)
    return true

end

@testset "ParameterEstimateScatterPlots" begin

    @test test1()

end


text:
export time_modeling

GeomOrNot = Union{Geometry, Array, Nothing}
ArrayOrNot = Union{Array, PyArray, PyObject, Nothing}
PhysOrNot = Union{PhysicalParameter, Array, Nothing}

# Setup time-domain linear or nonlinear foward and adjoint modeling and interface to devito
function time_modeling(model_full::Model, srcGeometry::GeomOrNot, srcData::ArrayOrNot,
                       recGeometry::GeomOrNot, recData::ArrayOrNot, dm::PhysOrNot, op::Symbol, options::JUDIOptions)
    # Load full geometry for out-of-core geometry containers
    recGeometry = Geometry(recGeometry)
    srcGeometry = Geometry(srcGeometry)

    # Reutrn directly for J*0
    if op==:born
        if norm(dm) == 0 && options.return_array == false
            return judiVector(recGeometry, zeros(Float32, recGeometry.nt[1], length(recGeometry.xloc[1])))
        elseif norm(dm) == 0 && options.return_array == true
            return vec(zeros(Float32, recGeometry.nt[1], length(recGeometry.xloc[1])))
        end
    end

    # limit model to area with sources/receivers
    if options.limit_m == true
        model = deepcopy(model_full)
        model, dm = limit_model_to_receiver_area(srcGeometry, recGeometry, model, options.buffer_size; pert=dm)
    else
        model = model_full
    end

    # Set up Python model structure
    modelPy = devito_model(model, options, dm)

    # Remove receivers outside the modeling domain (otherwise leads to segmentation faults)
    recGeometry, recData = remove_out_of_bounds_receivers(recGeometry, recData, model)

    # Devito interface
    argout = devito_interface(modelPy, srcGeometry, srcData, recGeometry, recData, dm, options)
    # Extend gradient back to original model size
    if op==:adjoint_born && options.limit_m==true
        argout = extend_gradient(model_full, model, argout)
    end

    argout = save_to_disk(argout, srcGeometry, srcData, options, Val(options.save_data_to_disk))

    return argout
end

# Saving to disk utilities
save_to_disk(shot, srcGeometry, srcData, options, ::Val) = shot
save_to_disk(shot::judiVector, srcGeometry, srcData, options, ::Val{false}) = shot

function save_to_disk(shot::judiVector, srcGeometry::GeometryIC, srcData::Array, options::JUDIOptions, ::Val{true})
    container = write_shot_record(srcGeometry, srcData, shot.geometry[1], shot.data[1], options)
    return judiVector(container)
end


text:
# ---
# title: 829. Consecutive Numbers Sum
# id: problem829
# author: Tian Jun
# date: 2020-10-31
# difficulty: Hard
# categories: Math
# link: <https://leetcode.com/problems/consecutive-numbers-sum/description/>
# hidden: true
# ---
#
# Given a positive integer `N`, how many ways can we write it as a sum of
# consecutive positive integers?
#
# **Example 1:**
#
#
#
#     Input: 5
#     Output: 2
#     Explanation: 5 = 5 = 2 + 3
#
# **Example 2:**
#
#
#
#     Input: 9
#     Output: 3
#     Explanation: 9 = 9 = 4 + 5 = 2 + 3 + 4
#
# **Example 3:**
#
#
#
#     Input: 15
#     Output: 4
#     Explanation: 15 = 15 = 8 + 7 = 4 + 5 + 6 = 1 + 2 + 3 + 4 + 5
#
# **Note:**  `1 <= N <= 10 ^ 9`.
#
#
## @lc code=start
using LeetCode

## add your code here:
## @lc code=end


text:
#
# fibonacci server
#

using ThreadingExperiments

function do_fib(cache, n)
    haskey(cache, n) || (cache[n] = BigInt(do_fib(cache, n-1) + do_fib(cache, n-2)))
    return cache[n]
end

const cch = Dict(0=>big"0", 1=>big"1")

function reset!(cache)
    empty!(cache)
    cache[0] = big"0"
    cache[1] = big"1"
end


text:
function get_tugbot(;
    timestep=0.01,
    gravity=[0.0; 0.0; -9.81],
    friction_coefficient=0.2,
    radius=0.25,
    object_dimension=[0.4, 0.4, 0.1],
    contact=true,
    contact_type=:nonlinear,
    T=Float64)

    origin = Origin{T}(name=:origin)
    drone_mass = 1.0
    object_mass = 2.5
    bodies = [
        Sphere(radius, drone_mass, name=:drone),
        Box(object_dimension..., object_mass, name=:object)]
    joints = [
        JointConstraint(Floating(origin, bodies[1]), name=:drone_joint)
        # JointConstraint(Floating(origin, bodies[2]), name=:object_joint)
        ]
    mechanism = Mechanism(origin, bodies, joints;
        timestep,
        gravity)

    if contact
        contacts = [[0.2, -0.2, -0.05], [-0.2, 0.2, -0.05], [0.2, 0.2, -0.05], [-0.2, -0.2, -0.05]]
        normal = [0.0, 0.0, 1.0]
        contacts = [contact_constraint(get_body(mechanism, :object), normal;
            friction_coefficient,
            contact_origin=contacts[i],
            contact_radius=0.0,
            contact_type,
            name=Symbol(:contact,i)) for i=1:length(contacts)]

        collision = StringCollision{Float64,0,3,0}(
                1*[0,0,-0.1],
                1*[0.2, 0.2, 0.05],
                2.0)
        parameterization = szeros(T, 0, 2)
        body_body_contact = ImpactContact{Float64,2}(parameterization, collision)

        contacts = [contacts; ContactConstraint((body_body_contact,
            get_body(mechanism, :drone).id,
            get_body(mechanism, :object).id,), name=:body_body)]
        mechanism = Mechanism(origin, bodies, joints, contacts;
            gravity,
            timestep)
    end
    return mechanism
end

function initialize_tugbot!(mechanism::Mechanism{T};
    drone_position=[0,1,1.0],
    drone_orientation=one(Quaternion),
    drone_velocity=zeros(3),
    drone_angular_velocity=zeros(3),
    object_position=zeros(3),
    object_orientation=one(Quaternion),
    object_velocity=zeros(3),
    object_angular_velocity=zeros(3),
    ) where T

    r_drone = get_body(mechanism, :drone).shape.r
    r_object = get_body(mechanism, :object).shape.xyz[3]/2
    joint = get_joint(mechanism, :drone_joint)
    zero_velocity!(mechanism)
    z_drone = [drone_position + [0,0,r_drone]; drone_velocity; Dojo.vector(drone_orientation); drone_angular_velocity]
    z_object = [object_position + [0,0,r_object]; object_velocity; Dojo.vector(object_orientation); object_angular_velocity]
    set_maximal_state!(mechanism, [z_drone; z_object])
end


text:
struct PCSAFTParam <: EoSParam
    Mw::SingleParam{Float64}
    segment::SingleParam{Float64}
    sigma::PairParam{Float64}
    epsilon::PairParam{Float64}
    epsilon_assoc::AssocParam{Float64}
    bondvol::AssocParam{Float64}
end

abstract type PCSAFTModel <: SAFTModel end
@newmodel PCSAFT PCSAFTModel PCSAFTParam

export PCSAFT
function PCSAFT(components;
    idealmodel=BasicIdeal,
    userlocations=String[],
    ideal_userlocations=String[],
    verbose=false,
    assoc_options = AssocOptions())
    params,sites = getparams(components, ["SAFT/PCSAFT","properties/molarmass.csv"]; userlocations=userlocations, verbose=verbose)

    segment = params["m"]
    k = params["k"]
    Mw = params["Mw"]
    params["sigma"].values .*= 1E-10
    sigma = sigma_LorentzBerthelot(params["sigma"])
    epsilon = epsilon_LorentzBerthelot(params["epsilon"], k)
    epsilon_assoc = params["epsilon_assoc"]
    bondvol = params["bondvol"]

    packagedparams = PCSAFTParam(Mw, segment, sigma, epsilon, epsilon_assoc, bondvol)
    references = ["10.1021/ie0003887", "10.1021/ie010954d"]

    model = PCSAFT(packagedparams, sites, idealmodel; ideal_userlocations, references, verbose, assoc_options)
    return model
end

function a_res(model::PCSAFTModel, V, T, z)
    _data = @f(data)
    return @f(a_hc,_data) + @f(a_disp,_data) + @f(a_assoc,_data)
end

function data(model::PCSAFTModel,V,T,z)
    _d = @f(d)
    ζ0,ζ1,ζ2,ζ3 = @f(ζ0123,_d)
    m = model.params.segment.values
    m̄ = dot(z, m)/sum(z)
    return (_d,ζ0,ζ1,ζ2,ζ3,m̄)
end

function a_hc(model::PCSAFTModel, V, T, z,_data=@f(data))
    dii,ζ0,ζ1,ζ2,ζ3,m̄ = _data
    m = model.params.segment.values
    Σz = sum(z)
    c1 = 1/(1-ζ3)
    c2 = 3ζ2/(1-ζ3)^2
    c3 = 2ζ2^2/(1-ζ3)^3
    a_hs = 1/ζ0 * (3ζ1*ζ2/(1-ζ3) + ζ2^3/(ζ3*(1-ζ3)^2) + (ζ2^3/ζ3^2-ζ0)*log(1-ζ3))
    res = zero(a_hs)
    for i ∈ @comps
        dᵢ = dii[i]
        di,dj = dᵢ,dᵢ
        g_hsᵢᵢ = c1 + di*dj/(di+dj)*c2 + (di*dj/(di+dj))^2*c3
        res += z[i]*(m[i]-1)*log(g_hsᵢᵢ)
    end
    #return  m̄*@f(a_hs) - ∑(z[i]*(m[i]-1)*log(@f(g_hs,i,i)) for i ∈ @comps)/Σz
    return m̄*a_hs - res/Σz
end

function a_disp(model::PCSAFTModel, V, T, z,_data=@f(data))
    di,ζ0,ζ1,ζ2,ζ3,m̄ = _data
    Σz = sum(z)
    m2ϵσ3₁,m2ϵσ3₂ = @f(m2ϵσ3)
    return -2*π*N_A*Σz/V*@f(I,1,_data)*m2ϵσ3₁ - π*m̄*N_A*Σz/V*@f(C1,_data)*@f(I,2,_data)*m2ϵσ3₂
end

function d(model::PCSAFTModel, V, T, z)
    ϵᵢᵢ = model.params.epsilon.diagvalues
    σᵢᵢ = model.params.sigma.diagvalues
    return σᵢᵢ .* (1 .- 0.12 .* exp.(-3ϵᵢᵢ ./ T))
end

function ζ(model::PCSAFTModel, V, T, z, n , _d)
    m = model.params.segment.values
    res = zero(V+T+first(z))
    for i ∈ @comps
        dᵢ = _d[i]
        res += z[i]*m[i]*dᵢ^n
    end
    res *= N_A*π/6/V
    return res
end

function ζ0123(model::PCSAFTModel, V, T, z,_d)
    m = model.params.segment.values
    ζ0 = zero(V+T+first(z))
    ζ1 = ζ0
    ζ2 = ζ0
    ζ3 = ζ0
    for i ∈ @comps
        dᵢ = _d[i]
        zᵢmᵢ = z[i]*m[i]
        d1 = dᵢ
        d2 = d1*d1
        d3 = d2*d1
        ζ0 += zᵢmᵢ
        ζ1 += zᵢmᵢ*d1
        ζ2 += zᵢmᵢ*d2
        ζ3 += zᵢmᵢ*d3
    end
    NV = N_A*π/6/V
    ζ0 *= NV
    ζ1 *= NV
    ζ2 *= NV
    ζ3 *= NV
    return ζ0,ζ1,ζ2,ζ3
end

function g_hs(model::PCSAFTModel, V, T, z, i, j,_data=@f(data))
    _d,ζ0,ζ1,ζ2,ζ3,_ = _data
    di = _d[i]
    dj = _d[j]
    return 1/(1-ζ3) + di*dj/(di+dj)*3ζ2/(1-ζ3)^2 + (di*dj/(di+dj))^2*2ζ2^2/(1-ζ3)^3
end

function a_hs(model::PCSAFTModel, V, T, z,_data=@f(data))
    _,ζ0,ζ1,ζ2,ζ3,_ = _data
    return 1/ζ0 * (3ζ1*ζ2/(1-ζ3) + ζ2^3/(ζ3*(1-ζ3)^2) + (ζ2^3/ζ3^2-ζ0)*log(1-ζ3))
end

function C1(model::PCSAFTModel, V, T, z,_data=@f(data))
    _,_,_,_,η,m̄ = _data
    return (1 + m̄*(8η-2η^2)/(1-η)^4 + (1-m̄)*(20η-27η^2+12η^3-2η^4)/((1-η)*(2-η))^2)^-1
end

function m2ϵσ3(model::PCSAFTModel, V, T, z)
    m = model.params.segment.values
    σ = model.params.sigma.values
    ϵ = model.params.epsilon.values
    m2ϵσ3₂ = zero(V+T+first(z))
    m2ϵσ3₁ = m2ϵσ3₂
    @inbounds for i ∈ @comps
        for j ∈ @comps
            constant = z[i]*z[j]*m[i]*m[j] * σ[i,j]^3
            exp1 = (ϵ[i,j]/T)
            exp2 = exp1*exp1
            m2ϵσ3₁ += constant*exp1
            m2ϵσ3₂ += constant*exp2
        end
    end
    Σz = sum(z)
    k = (1/Σz)^2
    return k*m2ϵσ3₁,k*m2ϵσ3₂
    #return ∑(z[i]*z[j]*m[i]*m[j] * (ϵ[i,j]*(1)/T)^n * σ[i,j]^3 for i ∈ @comps, j ∈ @comps)/(sum(z)^2)
end

function I(model::PCSAFTModel, V, T, z, n , _data=@f(data))
    _,_,_,_,η,m̄ = _data
    σ = model.params.sigma.values
    if n == 1
        corr = PCSAFTconsts.corr1
    elseif n == 2
        corr = PCSAFTconsts.corr2
    end
    res = zero(η)
    @inbounds for i ∈ 1:7
        ii = i-1
        corr1,corr2,corr3 = corr[i]
        ki = corr1 + (m̄-1)/m̄*corr2 + (m̄-1)/m̄*(m̄-2)/m̄*corr3
        res +=ki*η^ii
    end
    return res
end

function Δ(model::PCSAFTModel, V, T, z, i, j, a, b,_data=@f(data))
    _0 = zero(V+T+first(z))
    ϵ_assoc = model.params.epsilon_assoc.values
    κ = model.params.bondvol.values
    κijab = κ[i,j][a,b]
    iszero(κijab) && return _0
    σ = model.params.sigma.values
    gij = @f(g_hs,i,j,_data)
    res = gij*σ[i,j]^3*(exp(ϵ_assoc[i,j][a,b]/T)-1)*κijab
    return res
end

#optimized version, reduces memory allocations, but is specific to this PCSAFT
#thats why it is bound to the specific PCSAFT struct
#instead of the more general PCSAFTModel
function  Δ(model::PCSAFT, V, T, z,_data=@f(data))
    ϵ_assoc = model.params.epsilon_assoc.values
    κ = model.params.bondvol.values
    σ = model.params.sigma.values
    Δres = zero_assoc(κ,typeof(V+T+first(z)))
    for (idx,(i,j),(a,b)) in indices(Δres)
        gij = @f(g_hs,i,j,_data)
        Δres[idx] = gij*σ[i,j]^3*(exp(ϵ_assoc[i,j][a,b]/T)-1)*κ[i,j][a,b]
    end
    return Δres
end

const PCSAFTconsts = (
    corr1 =
    [(0.9105631445,-0.3084016918, -0.0906148351),
    (0.6361281449, 0.1860531159, 0.4527842806),
    (2.6861347891, -2.5030047259, 0.5962700728),
    (-26.547362491, 21.419793629, -1.7241829131),
    (97.759208784, -65.255885330, -4.1302112531),
    (-159.59154087, 83.318680481, 13.776631870),
    (91.297774084, -33.746922930, -8.6728470368)],

    corr2 =
    [(0.7240946941, -0.5755498075, 0.0976883116),
    (2.2382791861, 0.6995095521, -0.2557574982),
    (-4.0025849485, 3.8925673390, -9.1558561530),
    (-21.003576815, -17.215471648, 20.642075974),
    (26.855641363, 192.67226447, -38.804430052),
    (206.55133841, -161.82646165, 93.626774077),
    (-355.60235612, -165.20769346, -29.666905585)]
)


text:
struct AuxVarKey{T <: AuxVariableType, U <: PSY.Component} <: OptimizationContainerKey
    meta::String
end

function AuxVarKey(
    ::Type{T},
    ::Type{U},
    meta=CONTAINER_KEY_EMPTY_META,
) where {T <: AuxVariableType, U <: PSY.Component}
    if isabstracttype(U)
        error("Type $U can't be abstract")
    end
    return AuxVarKey{T, U}(meta)
end

get_entry_type(::AuxVarKey{T, U}) where {T <: AuxVariableType, U <: PSY.Component} = T
get_component_type(::AuxVarKey{T, U}) where {T <: AuxVariableType, U <: PSY.Component} = U

struct TimeDurationOn <: AuxVariableType end
struct TimeDurationOff <: AuxVariableType end

"""
Auxiliary Variable for Thermal Generation Models that solve for power above min
"""
struct PowerOutput <: AuxVariableType end
"""
Auxiliary Variable for Hydro and Storage Models that solve for total energy output
"""
struct EnergyOutput <: AuxVariableType end

should_write_resulting_value(::Type{<:AuxVariableType}) = true

convert_result_to_natural_units(::Type{<:AuxVariableType}) = false
convert_result_to_natural_units(::Type{PowerOutput}) = true


text:
using Adapt
using Dates: AbstractTime, DateTime, Nanosecond, Millisecond
using Oceananigans.Utils: prettytime

import Base: show
import Oceananigans: short_show

"""
    Clock{T<:Number}

Keeps track of the current `time`, `iteration` number, and time-stepping `stage`.
`stage` is updated only for multi-stage time-stepping methods.
The `time::T` can be either a number or a `DateTime` object.
"""
mutable struct Clock{T}
         time :: T
    iteration :: Int
        stage :: Int

    @doc """
        Clock{T}(time, iteration, stage=1)

    Returns a `Clock` with time of type `T`, initialized to the first stage.
    """
    function Clock{T}(time, iteration=0, stage=1) where T
        return new{T}(time, iteration, stage)
    end
end

"""
    Clock(; time, iteration=0, stage=1)

Returns a `Clock` initialized to the zeroth iteration and first time step stage.
"""
Clock(; time, iteration=0, stage=1) = Clock{typeof(time)}(time, iteration, stage)

short_show(clock::Clock) = string("Clock(time=$(prettytime(clock.time)), iteration=$(clock.iteration))")

Base.show(io::IO, c::Clock{T}) where T =
    println(io, "Clock{$T}: time = $(prettytime(c.time)), iteration = $(c.iteration), stage = $(c.stage)")

next_time(clock, Δt) = clock.time + Δt
next_time(clock::Clock{<:AbstractTime}, Δt) = clock.time + Nanosecond(round(Int, 1e9 * Δt))

tick_time!(clock, Δt) = clock.time += Δt
tick_time!(clock::Clock{<:AbstractTime}, Δt) = clock.time += Nanosecond(round(Int, 1e9 * Δt))

# Convert the time to units of clock.time (assumed to be seconds if using DateTime or TimeDate).
unit_time(t) = t
unit_time(t::Millisecond) = t.value / 1_000
unit_time(t::Nanosecond) = t.value / 1_000_000_000

# Convert to a base Julia type (a float or DateTime). Mainly used by NetCDFOutputWriter.
float_or_date_time(t) = t
float_or_date_time(t::AbstractTime) = DateTime(t)

function tick!(clock, Δt; stage=false)

    tick_time!(clock, Δt)

    if stage # tick a stage update
        clock.stage += 1
    else # tick an iteration and reset stage
        clock.iteration += 1
        clock.stage = 1
    end

    return nothing
end

"Adapt `Clock` to work on the GPU via CUDAnative and CUDAdrv."
Adapt.adapt_structure(to, clock::Clock) = (time=clock.time, iteration=clock.iteration, stage=clock.stage)


text:
## Deprecations for Languages

function WordTokenizers.tokenize(::Type{S}, s::T) where {S <: Language, T <: AbstractString}
    depwarn("Use of Languages as types is deprecated. Use instances.",  Symbol(S))
    tokenize(S(), s)
end

function ngramize(::Type{S}, words::Vector{T}, n::Int) where {S <: Language, T <: AbstractString}
    depwarn("Use of Languages as types is deprecated. Use instances.",  Symbol(S))
    ngramize(S(), words, n)
end

function onegramize(::Type{S}, words::Vector{T}) where {S <: Language, T <: AbstractString}
    depwarn("Use of Languages as types is deprecated. Use instances.",  Symbol(S))
    onegramize(S(), words)
end

#pre-processing functions

function remove_whitespace!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_whitespace! is deprecated, Use prepare! instead."
    prepare!(entity, strip_whitespace)
end
function remove_punctuation!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_punctuation! is deprecated, Use prepare! instead."
    prepare!(entity, strip_punctuation)
end
function remove_nonletters!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_nonletters! is deprecated, Use prepare! instead."
    prepare!(entity, strip_non_letters)
end
function remove_numbers!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_numbers! is deprecated, Use prepare! instead."
    prepare!(entity, strip_numbers)
end
function remove_articles!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_articles! is deprecated, Use prepare! instead."
    prepare!(entity, strip_articles)
end
function remove_indefinite_articles!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_indefinite_articles! is deprecated, Use prepare! instead."
    prepare!(entity, strip_indefinite_articles)
end
function remove_definite_articles!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_definite_articles! is deprecated, Use prepare! instead."
    prepare!(entity, strip_definite_articles)
end
function remove_prepositions!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_prepositions! is deprecated, Use prepare! instead."
    prepare!(entity, strip_prepositions)
end
function remove_pronouns!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_pronouns! is deprecated, Use prepare! instead."
    prepare!(entity, strip_pronouns)
end
function remove_stop_words!(entity::(Union{AbstractDocument,Corpus}))
    @warn "remove_stop_words! is deprecated, Use prepare! instead."
    prepare!(entity, strip_stopwords)
end


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

"""
    ConstValueShape{T} <: AbstractValueShape

A `ConstValueShape` describes the shape of constant values of type `T`.

Constructor:

    ConstValueShape(value)

`value` may be of arbitrary type, e.g. a constant scalar value or array:

    ConstValueShape(4.2)
    ConstValueShape([11 21; 12 22])

Shapes of constant values have zero degrees of freedom (see
[`totalndof`](@ref)).

See also the documentation of [`AbstractValueShape`](@ref).
"""
struct ConstValueShape{T} <: AbstractValueShape
    value::T
end

export ConstValueShape

@inline Base.size(shape::ConstValueShape) = size(shape.value)
@inline Base.length(shape::ConstValueShape) = length(shape.value)

import Base.<=
@inline <=(a::ConstValueShape{T}, b::ConstValueShape{U}) where {T,U} = T<:U && a.value ≈ b.value

@inline default_unshaped_eltype(shape::ConstValueShape) = Int32

@inline shaped_type(shape::ConstValueShape, ::Type{T}) where {T<:Real} = typeof(shape.value)

@inline totalndof(::ConstValueShape) = 0

# ToDo/Decision: Return a copy instead?
(shape::ConstValueShape)(::UndefInitializer) = shape.value

function unshaped(x::Any, shape::ConstValueShape)
    x == shape.value || throw(ArgumentError("Given value does not match value of ConstValueShape"))
    Float32[]
end

"""
    const_zero_shape(shape::ConstValueShape)

Get the equivalent of a constant zero shape for shape `shape`.
"""
const_zero_shape(shape::ConstValueShape) = ConstValueShape(const_zero(shape.value))

replace_const_shapes(f::Function, shape::ConstValueShape) = f(shape)

const ConstAccessor = ValueAccessor{ConstValueShape{T}} where {T}

@inline vs_getindex(data::AbstractVector{<:Real}, va::ConstAccessor) = va.shape.value

@inline vs_unsafe_view(::AbstractVector, va::ConstAccessor) = va.shape.value

function vs_setindex!(data::AbstractVector{<:Real}, v, va::ConstAccessor)
    v == va.shape.value || throw(ArgumentError("Cannot set constant value to a different value"))
    data
end

@inline _bcasted_view(data::AbstractArrayOfSimilarVectors{<:Real,N}, va::ConstAccessor) where N =
    Fill(va.shape.value, size(data)...)

Base.copy(instance::VSBroadcasted2{N,typeof(getindex),AbstractArrayOfSimilarVectors{<:Real,N},Ref{<:ConstAccessor}}) where N =
    _bcasted_view(instance.args[1], instance.args[2][])

Base.copy(instance::VSBroadcasted2{N,typeof(view),AbstractArrayOfSimilarVectors{<:Real,N},Ref{<:ConstAccessor}}) where N =
    _bcasted_view(instance.args[1], instance.args[2][])


text:
module Epsilon

# imports
using LinearAlgebra

struct Dependency
    t::Tensor
    grad_fn

    function Dependency(t, grad_fn)
        return new(t, grad_fn)
    end

end

struct Tensor <: AbstractArray{Float64, Float64}

    data::Array{Float64}
    requires_grad::Bool
    depends_on::Array{Dependency}

    shape

    function Tensor(data, requires_grad=false, depends_on =Array[])
        shape = size(data)

        # Makes it possible to use Tensor(1)
        if shape == ()
            data = [data]
        end

        return new(data, requires_grad, depends_on, shape)
    end

    function Base.:transpose(t1::Tensor)
        return Tensor(transpose(t1.data), t1.requires_grad, t1.depends_on)
    end

    function Base.:show(io::IO, t1::Tensor)
        display(t1.data)
    end

    function Base.:+(t1::Tensor, t2::Tensor)
        return add(t1, t2)
    end

    function Base.:*(t1::Tensor, t2::Tensor)
        return mul(t1, t2)
    end

    function Base.:sum(t1::Tensor)
        return tensor_sum(t1)
    end

end

function tensor_sum(t::Tensor)
    result = sum(t.data)
    requires_grad = t.requires_grad

    if requires_grad
        function grad_fn(grad)

            grad * ones(t.shape[1], t.shape[2])
            return grad
        end

        depends_on = Dependency(t, grad_fn)
    else
        depends_on = Nothing

    return Tensor(data, requires_grad, depends_on)
end

function add(t1::Tensor, t2::Tensor)
    result = t1.data + t2.data
    return result
end

function mul(t1::Tensor, t2::Tensor)
    result = t1.data * t2.data
    return result
end

function greet()
    println("hello from Epsilon")
end

end # module


text:
using Statistics
using PyPlot
pygui(true)

function biased(x::Array{Float64,1})
    N = length(x)
    Nsub = Int(round(0.05*N))    # index to disregard the first 5% of the array
    # calculate a cumulative mean, disregard the first 5%
    cummean = (cumsum(x)./Array(1:N))[Nsub:end]
    # for a bias free x, the mean shouldn't be much larger than the std
    bias = abs(mean(cummean)) / std(cummean)
end

function biassample(::Type{T},          # number format to test for bias
                    op=+,               # arithmetic operation
                    rng=rand,          # random number generator, e.g. rand,randn
                    N=1000000) where T

    x = fill(0.0,N)   # preallocate

    for i in 1:N
        a,b = T.(8*rng(2))
        truth = op(Float64(a),Float64(b))
        approx = Float64(op(a,b))
        x[i] = approx-truth
    end

    return x
end

function cummean(x::AbstractArray)
    N = length(x)
    return cumsum(x)./Array(1:N)
end


text:
using Documenter, PowerPlots, VegaLite, UUIDs

function Base.show(io::IO, m::MIME"text/html", v::VegaLite.VLSpec)
    divid = string("vl", replace(string(uuid4()), "-"=>""))
    print(io, "

")
    print(io, "")
end

# makedocs(
#     modules = [PowerPlots],
#     format = Documenter.HTML(mathengine = Documenter.MathJax()),
#     sitename = "PowerPlots",
#     authors = "Noah Rhodes",
#     pages = [
#         "Home" => "index.md",
#         "Examples" => "examples.md",
#         "Plot Attributes" => "plot_attributes.md",
#     ]
# )

makedocs(
    modules = [PowerPlots],
    format = Documenter.HTML(mathengine = Documenter.MathJax()),
    sitename = "PowerPlots",
    authors = "Noah Rhodes",
    pages = [
        "Home" => "index.md",
        "Examples" => Any[
            "examples/basic examples.md",
            "examples/advanced examples.md",
        ],
        "Parameters" => "parameters.md",
        "Data Transformations" => Any[
            "data_transformations/powermodelsdataframes.md",
            "data_transformations/powermodelsgraphs.md",
            "data_transformations/layouts.md",
        ],
        "Experimental Features" => "experimental.md",
    ]
)

deploydocs(
    repo = "github.com/WISPO-POP/PowerPlots.jl.git",
)


text:
# Part 1
program = map(i -> [i[1], parse(Int32, i[2])], split.(readlines("day08_input.txt")))

# Execute an instruction in prog, returning next instruction index and acc
function exec_inst(prog, instr, acc)
    instruction = prog[instr]
    handlers = Dict(
        "acc" => (instr, acc, arg) -> (instr + 1, acc + arg),
        "jmp" => (instr, acc, arg) -> (instr + arg, acc),
        "nop" => (instr, acc, arg) -> (instr + 1, acc)
    )
    handlers[instruction[1]](instr, acc, instruction[2])
end

# Execute instructions in prog until an instruction is executed twice
# If loop detected, return acc, false
# If about to exec 1 past the instruction array, return acc, true
function exec_until_loop(prog, acc=0)
    next_instr = 1
    executed = Set()
    while next_instr ∉ executed
        push!(executed, next_instr)
        next_instr, acc = exec_inst(prog, next_instr, acc)
        if next_instr == length(prog) + 1 return acc, true end
    end
    acc, false
end

println("Part 1: accumulator=$(first(exec_until_loop(program)))")

# Part 2
# Patch each instruction in the program (swapping jmp <-> nop) until it exits normally
function try_patches(prog)
    for i in 1:length(prog)
        patched = deepcopy(prog)
        patched[i][1] = patched[i][1] |> x -> (x == "jmp" ? "nop" : (x == "nop" ? "jmp" : x))
        acc, good = exec_until_loop(patched)
        if good return acc end
    end
end

println("Part 2: accumulator=$(try_patches(program))")

max_stars_repo_name: ericgreveson/adventofcode

max_issues_repo_name: ericgreveson/adventofcode

max_forks_repo_name: ericgreveson/adventofcode


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

@doc raw"""
    ObjsMessage(;
        attachments=nothing,
        blocks=nothing,
        bot_id=nothing,
        bot_profile=nothing,
        client_msg_id=nothing,
        comment=nothing,
        display_as_bot=nothing,
        file=nothing,
        files=nothing,
        icons=nothing,
        inviter=nothing,
        is_delayed_message=nothing,
        is_intro=nothing,
        is_starred=nothing,
        last_read=nothing,
        latest_reply=nothing,
        name=nothing,
        old_name=nothing,
        parent_user_id=nothing,
        permalink=nothing,
        pinned_to=nothing,
        purpose=nothing,
        reactions=nothing,
        reply_count=nothing,
        reply_users=nothing,
        reply_users_count=nothing,
        source_team=nothing,
        subscribed=nothing,
        subtype=nothing,
        team=nothing,
        text=nothing,
        thread_ts=nothing,
        topic=nothing,
        ts=nothing,
        type=nothing,
        unread_count=nothing,
        upload=nothing,
        user=nothing,
        user_profile=nothing,
        user_team=nothing,
        username=nothing,
    )

    - attachments::Vector{ObjsMessageAttachments}
    - blocks::Blocks
    - bot_id::Any
    - bot_profile::ObjsBotProfile
    - client_msg_id::String
    - comment::ObjsComment
    - display_as_bot::Bool
    - file::ObjsFile
    - files::Vector{ObjsFile}
    - icons::ObjsMessageIcons
    - inviter::DefsUserId
    - is_delayed_message::Bool
    - is_intro::Bool
    - is_starred::Bool
    - last_read::DefsTs
    - latest_reply::DefsTs
    - name::String
    - old_name::String
    - parent_user_id::DefsUserId
    - permalink::String
    - pinned_to::Vector{DefsChannel}
    - purpose::String
    - reactions::Vector{ObjsReaction}
    - reply_count::Int32
    - reply_users::Vector{DefsUserId}
    - reply_users_count::Int32
    - source_team::DefsWorkspaceId
    - subscribed::Bool
    - subtype::String
    - team::DefsWorkspaceId
    - text::String
    - thread_ts::DefsTs
    - topic::String
    - ts::DefsTs
    - type::String
    - unread_count::Int32
    - upload::Bool
    - user::DefsUserId
    - user_profile::ObjsUserProfileShort
    - user_team::DefsWorkspaceId
    - username::String
"""
mutable struct ObjsMessage <: SwaggerModel
    attachments::Any # spec type: Union{ Nothing, Vector{ObjsMessageAttachments} } # spec name: attachments
    blocks::Any # spec type: Union{ Nothing, Blocks } # spec name: blocks
    bot_id::Any # spec type: Union{ Nothing, Any } # spec name: bot_id
    bot_profile::Any # spec type: Union{ Nothing, ObjsBotProfile } # spec name: bot_profile
    client_msg_id::Any # spec type: Union{ Nothing, String } # spec name: client_msg_id
    comment::Any # spec type: Union{ Nothing, ObjsComment } # spec name: comment
    display_as_bot::Any # spec type: Union{ Nothing, Bool } # spec name: display_as_bot
    file::Any # spec type: Union{ Nothing, ObjsFile } # spec name: file
    files::Any # spec type: Union{ Nothing, Vector{ObjsFile} } # spec name: files
    icons::Any # spec type: Union{ Nothing, ObjsMessageIcons } # spec name: icons
    inviter::Any # spec type: Union{ Nothing, DefsUserId } # spec name: inviter
    is_delayed_message::Any # spec type: Union{ Nothing, Bool } # spec name: is_delayed_message
    is_intro::Any # spec type: Union{ Nothing, Bool } # spec name: is_intro
    is_starred::Any # spec type: Union{ Nothing, Bool } # spec name: is_starred
    last_read::Any # spec type: Union{ Nothing, DefsTs } # spec name: last_read
    latest_reply::Any # spec type: Union{ Nothing, DefsTs } # spec name: latest_reply
    name::Any # spec type: Union{ Nothing, String } # spec name: name
    old_name::Any # spec type: Union{ Nothing, String } # spec name: old_name
    parent_user_id::Any # spec type: Union{ Nothing, DefsUserId } # spec name: parent_user_id
    permalink::Any # spec type: Union{ Nothing, String } # spec name: permalink
    pinned_to::Any # spec type: Union{ Nothing, Vector{DefsChannel} } # spec name: pinned_to
    purpose::Any # spec type: Union{ Nothing, String } # spec name: purpose
    reactions::Any # spec type: Union{ Nothing, Vector{ObjsReaction} } # spec name: reactions
    reply_count::Any # spec type: Union{ Nothing, Int32 } # spec name: reply_count
    reply_users::Any # spec type: Union{ Nothing, Vector{DefsUserId} } # spec name: reply_users
    reply_users_count::Any # spec type: Union{ Nothing, Int32 } # spec name: reply_users_count
    source_team::Any # spec type: Union{ Nothing, DefsWorkspaceId } # spec name: source_team
    subscribed::Any # spec type: Union{ Nothing, Bool } # spec name: subscribed
    subtype::Any # spec type: Union{ Nothing, String } # spec name: subtype
    team::Any # spec type: Union{ Nothing, DefsWorkspaceId } # spec name: team
    text::Any # spec type: Union{ Nothing, String } # spec name: text
    thread_ts::Any # spec type: Union{ Nothing, DefsTs } # spec name: thread_ts
    topic::Any # spec type: Union{ Nothing, String } # spec name: topic
    ts::Any # spec type: Union{ Nothing, DefsTs } # spec name: ts
    type::Any # spec type: Union{ Nothing, String } # spec name: type
    unread_count::Any # spec type: Union{ Nothing, Int32 } # spec name: unread_count
    upload::Any # spec type: Union{ Nothing, Bool } # spec name: upload
    user::Any # spec type: Union{ Nothing, DefsUserId } # spec name: user
    user_profile::Any # spec type: Union{ Nothing, ObjsUserProfileShort } # spec name: user_profile
    user_team::Any # spec type: Union{ Nothing, DefsWorkspaceId } # spec name: user_team
    username::Any # spec type: Union{ Nothing, String } # spec name: username

    function ObjsMessage(;attachments=nothing, blocks=nothing, bot_id=nothing, bot_profile=nothing, client_msg_id=nothing, comment=nothing, display_as_bot=nothing, file=nothing, files=nothing, icons=nothing, inviter=nothing, is_delayed_message=nothing, is_intro=nothing, is_starred=nothing, last_read=nothing, latest_reply=nothing, name=nothing, old_name=nothing, parent_user_id=nothing, permalink=nothing, pinned_to=nothing, purpose=nothing, reactions=nothing, reply_count=nothing, reply_users=nothing, reply_users_count=nothing, source_team=nothing, subscribed=nothing, subtype=nothing, team=nothing, text=nothing, thread_ts=nothing, topic=nothing, ts=nothing, type=nothing, unread_count=nothing, upload=nothing, user=nothing, user_profile=nothing, user_team=nothing, username=nothing)
        o = new()
        validate_property(ObjsMessage, Symbol("attachments"), attachments)
        setfield!(o, Symbol("attachments"), attachments)
        validate_property(ObjsMessage, Symbol("blocks"), blocks)
        setfield!(o, Symbol("blocks"), blocks)
        validate_property(ObjsMessage, Symbol("bot_id"), bot_id)
        setfield!(o, Symbol("bot_id"), bot_id)
        validate_property(ObjsMessage, Symbol("bot_profile"), bot_profile)
        setfield!(o, Symbol("bot_profile"), bot_profile)
        validate_property(ObjsMessage, Symbol("client_msg_id"), client_msg_id)
        setfield!(o, Symbol("client_msg_id"), client_msg_id)
        validate_property(ObjsMessage, Symbol("comment"), comment)
        setfield!(o, Symbol("comment"), comment)
        validate_property(ObjsMessage, Symbol("display_as_bot"), display_as_bot)
        setfield!(o, Symbol("display_as_bot"), display_as_bot)
        validate_property(ObjsMessage, Symbol("file"), file)
        setfield!(o, Symbol("file"), file)
        validate_property(ObjsMessage, Symbol("files"), files)
        setfield!(o, Symbol("files"), files)
        validate_property(ObjsMessage, Symbol("icons"), icons)
        setfield!(o, Symbol("icons"), icons)
        validate_property(ObjsMessage, Symbol("inviter"), inviter)
        setfield!(o, Symbol("inviter"), inviter)
        validate_property(ObjsMessage, Symbol("is_delayed_message"), is_delayed_message)
        setfield!(o, Symbol("is_delayed_message"), is_delayed_message)
        validate_property(ObjsMessage, Symbol("is_intro"), is_intro)
        setfield!(o, Symbol("is_intro"), is_intro)
        validate_property(ObjsMessage, Symbol("is_starred"), is_starred)
        setfield!(o, Symbol("is_starred"), is_starred)
        validate_property(ObjsMessage, Symbol("last_read"), last_read)
        setfield!(o, Symbol("last_read"), last_read)
        validate_property(ObjsMessage, Symbol("latest_reply"), latest_reply)
        setfield!(o, Symbol("latest_reply"), latest_reply)
        validate_property(ObjsMessage, Symbol("name"), name)
        setfield!(o, Symbol("name"), name)
        validate_property(ObjsMessage, Symbol("old_name"), old_name)
        setfield!(o, Symbol("old_name"), old_name)
        validate_property(ObjsMessage, Symbol("parent_user_id"), parent_user_id)
        setfield!(o, Symbol("parent_user_id"), parent_user_id)
        validate_property(ObjsMessage, Symbol("permalink"), permalink)
        setfield!(o, Symbol("permalink"), permalink)
        validate_property(ObjsMessage, Symbol("pinned_to"), pinned_to)
        setfield!(o, Symbol("pinned_to"), pinned_to)
        validate_property(ObjsMessage, Symbol("purpose"), purpose)
        setfield!(o, Symbol("purpose"), purpose)
        validate_property(ObjsMessage, Symbol("reactions"), reactions)
        setfield!(o, Symbol("reactions"), reactions)
        validate_property(ObjsMessage, Symbol("reply_count"), reply_count)
        setfield!(o, Symbol("reply_count"), reply_count)
        validate_property(ObjsMessage, Symbol("reply_users"), reply_users)
        setfield!(o, Symbol("reply_users"), reply_users)
        validate_property(ObjsMessage, Symbol("reply_users_count"), reply_users_count)
        setfield!(o, Symbol("reply_users_count"), reply_users_count)
        validate_property(ObjsMessage, Symbol("source_team"), source_team)
        setfield!(o, Symbol("source_team"), source_team)
        validate_property(ObjsMessage, Symbol("subscribed"), subscribed)
        setfield!(o, Symbol("subscribed"), subscribed)
        validate_property(ObjsMessage, Symbol("subtype"), subtype)
        setfield!(o, Symbol("subtype"), subtype)
        validate_property(ObjsMessage, Symbol("team"), team)
        setfield!(o, Symbol("team"), team)
        validate_property(ObjsMessage, Symbol("text"), text)
        setfield!(o, Symbol("text"), text)
        validate_property(ObjsMessage, Symbol("thread_ts"), thread_ts)
        setfield!(o, Symbol("thread_ts"), thread_ts)
        validate_property(ObjsMessage, Symbol("topic"), topic)
        setfield!(o, Symbol("topic"), topic)
        validate_property(ObjsMessage, Symbol("ts"), ts)
        setfield!(o, Symbol("ts"), ts)
        validate_property(ObjsMessage, Symbol("type"), type)
        setfield!(o, Symbol("type"), type)
        validate_property(ObjsMessage, Symbol("unread_count"), unread_count)
        setfield!(o, Symbol("unread_count"), unread_count)
        validate_property(ObjsMessage, Symbol("upload"), upload)
        setfield!(o, Symbol("upload"), upload)
        validate_property(ObjsMessage, Symbol("user"), user)
        setfield!(o, Symbol("user"), user)
        validate_property(ObjsMessage, Symbol("user_profile"), user_profile)
        setfield!(o, Symbol("user_profile"), user_profile)
        validate_property(ObjsMessage, Symbol("user_team"), user_team)
        setfield!(o, Symbol("user_team"), user_team)
        validate_property(ObjsMessage, Symbol("username"), username)
        setfield!(o, Symbol("username"), username)
        o
    end
end # type ObjsMessage

const _property_map_ObjsMessage = Dict{Symbol,Symbol}(Symbol("attachments")=>Symbol("attachments"), Symbol("blocks")=>Symbol("blocks"), Symbol("bot_id")=>Symbol("bot_id"), Symbol("bot_profile")=>Symbol("bot_profile"), Symbol("client_msg_id")=>Symbol("client_msg_id"), Symbol("comment")=>Symbol("comment"), Symbol("display_as_bot")=>Symbol("display_as_bot"), Symbol("file")=>Symbol("file"), Symbol("files")=>Symbol("files"), Symbol("icons")=>Symbol("icons"), Symbol("inviter")=>Symbol("inviter"), Symbol("is_delayed_message")=>Symbol("is_delayed_message"), Symbol("is_intro")=>Symbol("is_intro"), Symbol("is_starred")=>Symbol("is_starred"), Symbol("last_read")=>Symbol("last_read"), Symbol("latest_reply")=>Symbol("latest_reply"), Symbol("name")=>Symbol("name"), Symbol("old_name")=>Symbol("old_name"), Symbol("parent_user_id")=>Symbol("parent_user_id"), Symbol("permalink")=>Symbol("permalink"), Symbol("pinned_to")=>Symbol("pinned_to"), Symbol("purpose")=>Symbol("purpose"), Symbol("reactions")=>Symbol("reactions"), Symbol("reply_count")=>Symbol("reply_count"), Symbol("reply_users")=>Symbol("reply_users"), Symbol("reply_users_count")=>Symbol("reply_users_count"), Symbol("source_team")=>Symbol("source_team"), Symbol("subscribed")=>Symbol("subscribed"), Symbol("subtype")=>Symbol("subtype"), Symbol("team")=>Symbol("team"), Symbol("text")=>Symbol("text"), Symbol("thread_ts")=>Symbol("thread_ts"), Symbol("topic")=>Symbol("topic"), Symbol("ts")=>Symbol("ts"), Symbol("type")=>Symbol("type"), Symbol("unread_count")=>Symbol("unread_count"), Symbol("upload")=>Symbol("upload"), Symbol("user")=>Symbol("user"), Symbol("user_profile")=>Symbol("user_profile"), Symbol("user_team")=>Symbol("user_team"), Symbol("username")=>Symbol("username"))
const _property_types_ObjsMessage = Dict{Symbol,String}(Symbol("attachments")=>"Vector{ObjsMessageAttachments}", Symbol("blocks")=>"Blocks", Symbol("bot_id")=>"Any", Symbol("bot_profile")=>"ObjsBotProfile", Symbol("client_msg_id")=>"String", Symbol("comment")=>"ObjsComment", Symbol("display_as_bot")=>"Bool", Symbol("file")=>"ObjsFile", Symbol("files")=>"Vector{ObjsFile}", Symbol("icons")=>"ObjsMessageIcons", Symbol("inviter")=>"DefsUserId", Symbol("is_delayed_message")=>"Bool", Symbol("is_intro")=>"Bool", Symbol("is_starred")=>"Bool", Symbol("last_read")=>"DefsTs", Symbol("latest_reply")=>"DefsTs", Symbol("name")=>"String", Symbol("old_name")=>"String", Symbol("parent_user_id")=>"DefsUserId", Symbol("permalink")=>"String", Symbol("pinned_to")=>"Vector{DefsChannel}", Symbol("purpose")=>"String", Symbol("reactions")=>"Vector{ObjsReaction}", Symbol("reply_count")=>"Int32", Symbol("reply_users")=>"Vector{DefsUserId}", Symbol("reply_users_count")=>"Int32", Symbol("source_team")=>"DefsWorkspaceId", Symbol("subscribed")=>"Bool", Symbol("subtype")=>"String", Symbol("team")=>"DefsWorkspaceId", Symbol("text")=>"String", Symbol("thread_ts")=>"DefsTs", Symbol("topic")=>"String", Symbol("ts")=>"DefsTs", Symbol("type")=>"String", Symbol("unread_count")=>"Int32", Symbol("upload")=>"Bool", Symbol("user")=>"DefsUserId", Symbol("user_profile")=>"ObjsUserProfileShort", Symbol("user_team")=>"DefsWorkspaceId", Symbol("username")=>"String")
Base.propertynames(::Type{ ObjsMessage }) = collect(keys(_property_map_ObjsMessage))
Swagger.property_type(::Type{ ObjsMessage }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_ObjsMessage[name]))}
Swagger.field_name(::Type{ ObjsMessage }, property_name::Symbol) =  _property_map_ObjsMessage[property_name]

function check_required(o::ObjsMessage)
    (getproperty(o, Symbol("text")) === nothing) && (return false)
    (getproperty(o, Symbol("ts")) === nothing) && (return false)
    (getproperty(o, Symbol("type")) === nothing) && (return false)
    true
end

function validate_property(::Type{ ObjsMessage }, name::Symbol, val)
end


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

struct VarMapping{
    VFV<:AbstractVector{<:Real},
    VFI<:AbstractVector{Int},
    VVI<:AbstractVector{Int}
}
    fixed_idxs::VFI
    fixed_values::VFV
    variable_idxs::VVI
end

function VarMapping(bounds::HyperRectBounds)
    vol = bounds.vol
    lo_hi_cmp = vol.lo .≈ vol.hi
    fixed_idxs = findall(v -> v == true, lo_hi_cmp)
    fixed_values = vol.lo[fixed_idxs]
    variable_idxs = findall(v -> v == false, lo_hi_cmp)
    VarMapping(fixed_idxs, fixed_values, variable_idxs)
end

function apply_varmap!(
    mapped_v::AbstractVector{<:Real},
    varmap::VarMapping,
    v::AbstractVector{<:Real}
)
    mapped_v[varmap.fixed_idxs] = varmap.fixed_values
    mapped_v[varmap.variable_idxs] = v
    mapped_v
end

function apply_varmap!(
    mapped_v::VectorOfSimilarVectors{<:Real},
    varmap::VarMapping,
    v::VectorOfSimilarVectors{<:Real}
)
    flatview(mapped_v)[varmap.fixed_idxs, :] = varmap.fixed_values
    flatview(mapped_v)[varmap.variable_idxs, :] = flatview(v)
    mapped_v
end

function apply_varmap(varmap::VarMapping, v::AbstractVector{<:Real})
    mapped_v = similar(v, size(varmap.fixed_idxs, 1) + size(varmap.variable_idxs, 1))
    apply_varmap!(mapped_v, varmap, v)
end

function apply_varmap(varmap::VarMapping, v::VectorOfSimilarVectors{<:Real})
    mapped_v = VectorOfSimilarVectors(similar(flatview(v), size(varmap.fixed_varidxs, 1) + size(varmap.variable_idxs, 1), size(flatview(v), 2)))
    apply_varmap!(mapped_v, varmap, v)
end

function invapply_varmap!(
    v::AbstractVector{<:Real},
    varmap::VarMapping,
    mapped_v::AbstractVector{<:Real}
)
    v[:] = view(mapped_v, varmap.variable_idxs)
    v
end

function invapply_varmap!(
    v::VectorOfSimilarVectors{<:Real},
    varmap::VarMapping,
    mapped_v::VectorOfSimilarVectors{<:Real}
)
    flatview(v)[:, :] = view(flatview(mapped_v), varmap.variable_idxs, :)
    v
end

function invapply_varmap(varmap::VarMapping, mapped_v::AbstractVector{<:Real})
    v = similar(mapped_v, size(varmap.variable_idxs, 1))
    invapply_varmap!(v, varmap, mapped_v)
end

function invapply_varmap(varmap::VarMapping, mapped_v::VectorOfSimilarVectors{<:Real})
    v = VectorOfSimilarVectors(similar(flatview(mapped_v), size(varmap.variable_idxs, 1), size(flatview(mapped_v), 2)))
    invapply_varmap!(v, varmap, mapped_v)
end

function invapply_varmap(varmap::VarMapping, bounds::HyperRectBounds)
    idxs = varmap.variable_idxs
    vol = HyperRectVolume(bounds.vol.lo[idxs], bounds.vol.hi[idxs])
    bt = bounds.bt[idxs]
    HyperRectBounds(vol, bt)
end

function invapply_varmap(varmap::VarMapping, bounds::NoVarBounds)
    NoVarBounds(size(varmap.variable_idxs, 1))
end


text:
module Utils

using CSV
using DataFrames
using Dates
using JSON
using Mustache

using ..Config

export
    # string
    snip,
    snip7,
    drop2,
    # git
    githeadsha,
    gitcurrentbranch,
    gitbranches,
    onbranch,
    gitbranchsha,
    gitbranchshas,
    # path
    result_filename,
    result_dir,
    # bm
    benchmark_branches,
    bm_name,
    # template
    code_bm_run,
    stringify_log,
    generate_report,
    generate_report_dataframe

const report_repo = Config.get_config("github.report_repo")
const report_branch = Config.get_config("github.report_branch", "master")
const bot_user = Config.get_config("github.user")

# string utils

snip(str, len) = str[1:min(len, end)]
snip7(sha) = snip(sha, 7)
drop2(x) = x[3:end]

# git utils

githeadsha() = strip(read(`git rev-parse HEAD`, String))
githeadsha(path) = cd(githeadsha, path)

function gitcurrentbranch()
    branches = readlines(`git branch`)
    ind = findfirst(x -> x[1] == '*', branches)
    brname = drop2(branches[ind])
    if brname[1] == '('
        brname = snip7(githeadsha())
    end
    return brname
end
gitcurrentbranch(path) = cd(gitcurrentbranch, path)

function gitbranches(branches)
    currentbranch = gitcurrentbranch()
    all_branches = drop2.(readlines(`git branch -al`))
    run(`git fetch --all`)
    for brch in branches
        (brch in all_branches) && continue
        br_idx = findfirst(all_branches) do b endswith(b, "/" * brch) end
        br_idx == nothing && throw("Branch $brch not found.")
        run(`git checkout -b $brch $(all_branches[br_idx])`)
    end
    run(`git checkout $currentbranch`)
end
gitbranches(path, branches) = cd(() -> gitbranches(branches), path)

function onbranch(f::Function, repopath, branch)
    remote = Config.get_config("target.use_remote_branches", true)
    currentbranch = gitcurrentbranch(repopath)
    cd(repopath) do
        if remote
            run(`git fetch --all`)
            all_branches = drop2.(readlines(`git branch -al`))
            if "origin/$branch" in all_branches
                run(`git checkout origin/$branch`)
            else
                run(`git checkout $branch`)
            end
        else # local
            run(`git checkout $branch`)
        end
    end
    f()
    cd(repopath) do
        run(`git checkout $currentbranch`)
    end
end

gitbranchsha(branch) = strip(read(`git rev-parse $branch`, String))
gitbranchsha(path, branch) = cd(()-> gitbranchsha(branch), path)
gitbranchshas(path, branches) = gitbranchsha.((path,), branches)

# path utils

project_root = (@__DIR__) |> dirname

function result_filename(filename)
    filename = replace(filename, [' ', ',', '('] => "_")
    filename = replace(filename, [')', '.', ':'] => "")
    filename
end
result_filename(data::Dict) = result_filename(data["name"] * "_" * data["engine"])
result_filename(data::DataFrame) = result_filename(data[:name][1])

function result_dir(name)
    project_dir = (@__DIR__) |> dirname
    results_dir = joinpath(project_dir, "benchmark_results")
    isdir(results_dir) || mkdir(results_dir)
    cd(results_dir) do
        isdir(name) || mkdir(name)
    end
    return joinpath(results_dir, name)
end

# benchmark utils
function nonmaster_br(brches::Vector{String})
    for br in sort(brches)
        br != "master" && return br
    end
    return "none"
end

function benchmark_branches(brches::Vector{String})
    "master" in brches ? brches : ["master", brches...]
end

function benchmark_branches(text::String)
    reg = r".*bm\((.+)\)"
    m = match(reg, text)
    m == nothing && return []
    brs = map(split(m[1], ",")) do x convert(String, strip(x, [' ', '"'])) end
    return benchmark_branches(brs)
end

bm_name(branch::String) = "BM-" * Dates.format(now(), "YYYYmmddHHMM-") * branch
bm_name(brches::Vector{String}) = bm_name(nonmaster_br(brches))

bm_file_content(user, issue_url, comment_url, branches) = """
[trigger]
issue_url = "$(issue_url)"
comment_url = "$(comment_url)"
user = "$(user)"

[benchmark]
branches = $(repr(branches))
"""

bm_pr_content(comment_url) = """
This Pull Request is created for tracking the benchmark job.

See more information at $comment_url.
"""

bm_reply0_content(user, pr_url) = """
Hi @$user, a new benchmark job will be dispatched soon at your command,
you can track it here: $(pr_url).
"""

const tmpl_bm_pr_report_content = """
The benchmark job is finished.

The report is committed in this commit: {{{ :commit_id }}}.

You can see the report at {{{ :report_url }}}.

And below is the log reported from the benchmark:

```
{{#:report_logs}}
{{.}}
{{/:report_logs}}
```

If it has no issues, please consider to merge or close this PullRequest.
"""
function bm_pr_report_content(bm_name, commit_id, report_url)
    tmpl = Config.get_config(
        "comment_templates.app_bm_complete_for_pr",
        tmpl_bm_pr_report_content)
    report_logs = get_benchmark_log(bm_name)
    render(tmpl,
           Dict(:commit_id => commit_id,
                :report_url => report_url,
                :report_logs => report_logs))
end

const tmpl_bm_commit_report_content = """
The benchmark job for this commit is finished.

The report is committed in this commit: {{{ :report_repo }}}#{{{ :commit_id }}}.

You can see the report at {{{ :report_url }}}.

Below is the log reported from the benchmark:

```
{{#:report_logs}}
{{.}}
{{/:report_logs}}
```
"""
function bm_commit_report_content(bm_name, commit_id, report_url)
    tmpl = Config.get_config(
        "comment_templates.ci_bm_complete_for_commit",
        tmpl_bm_commit_report_content)
    report_logs = get_benchmark_log(bm_name)
    render(tmpl,
           Dict(:report_repo => report_repo,
                :commit_id => commit_id,
                :report_url => report_url,
                :report_logs => report_logs))
end

bm_pr_error_content(exc) = """
An error occurred while running the benchmark:

  $exc

Please consider to fix it and trigger another one.
"""

bm_reply1_content(bm_name, user, repo, commit_id, report_url) = """
Hi @$user,

The benchmark [$bm_name] job is finished.

The report is committed in this commit: $repo@$commit_id.

You can see the report at $report_url  or go to the tracking PR to see it.
"""

bm_reply2_content(bm_name, user, repo, exc) = """
Hi @$user,

I am sorry that an error has occurred while running the benchmark [$bm_name]:

  $exc

Please consider to fix it and trigger another one.
"""

# code templates
const tmpl_code_bm_run = """
using ContinuousBenchmarks;
using ContinuousBenchmarks.Reporter;

{{#:config_file}}
ContinuousBenchmarks.set_benchmark_config_file("{{{ :config_file }}}")
{{/:config_file}}
include("{{{ :bm_file }}}");
Reporter.save_result(LOG_DATA, "{{{ :bm_file }}}", "{{{ :save_path }}}")
Reporter.save_log(LOG_DATA, "{{{ :save_path }}}")
"""
code_bm_run(data) = render(tmpl_code_bm_run, data)

# report template

const tmpl_report_md = """
# Benchmark Report

## Job properties

**Target Project Branches**:
{{#branches}}
- **{{{ name }}}**({{ sha }}) {{#is_base}}**[BASE_BRANCH]**{{/is_base}}
{{/branches}}

## Results Table:

Below is a table of this job's results. The table shows the time ratio
of the N (N >= 2) commits benchmarked. A ratio greater than `1.0`
denotes a possible regression (marked with :-1:), while a ratio less
than `1.0` denotes a possible improvement (marked with :+1:). Results
are subject to noise so small fluctuations around `1.0` may be
ignored.

| BenchMark    | {{#branches}} TimeRatio({{{ name }}}) | {{/branches}}
| -----------  | {{#branches}} ----------------------- | {{/branches}}
{{#benchmarks}}
| {{{ name }}} | {{#results}} {{{ label }}} | {{/results}}
{{/benchmarks}}

## Raw Results:

{{#benchmarks}}
### {{{ name }}}
{{#results}}
#### On Branch `{{{ branch }}`
```javascript
{{{ result_json }}}
```

{{/results}}
{{/benchmarks}}

"""

function generate_report(bm_name, branches, shas, base_branch = "")
    result_path = result_dir(bm_name)
    if !(base_branch in branches)
        base_branch = ("master" in branches) ? "master" : branches[1]
    end

    data = Dict{Any, Any}(
        "branches" => [],
        "benchmarks" => [],
    )

    for (br, sha) in zip(branches, shas)
        item = Dict{Any, Any}(
            "name" => br,
            "sha" => snip7(sha),
            "is_base" => br == base_branch,
        )
        push!(data["branches"], item)
    end

    result_files = readdir(joinpath(result_path, snip7(shas[1])))
    result_files = filter(result_files) do fname endswith(fname, ".json") end
    result_files = filter(result_files) do fname
        all(isfile, map((sha) -> joinpath(result_path, snip7(sha), fname), shas))
    end

    for fname in result_files
        base_br_idx = indexin([base_branch], branches)[1]
        base_sha = snip7(shas[base_br_idx])
        base_result_file = joinpath(result_path, base_sha, fname)
        base_result_json = read(open(base_result_file), String)
        base_result_data = JSON.parse(base_result_json)
        base_time = round(base_result_data["time"] * 1000, digits = 3)

        bench = Dict{Any, Any}(
            "name" => """`$(base_result_data["name"]) - $(base_result_data["engine"])`""",
            "results" => [],
        )

        for (br, sha) in zip(branches, shas)
            result_file = joinpath(result_path, snip7(sha), fname)
            result_json = read(open(result_file), String)
            result_data = JSON.parse(result_json)
            time = round(result_data["time"] * 1000, digits = 3)
            ratio = round(time / base_time, digits=2)
            symbol = ratio > 1 ? ":-1:" : ":+1:"
            item = Dict{Any, Any}(
                "branch" => br,
                "result_json" => result_json,
                "label" => "$ratio ($time ms / $base_time ms) $symbol",
            )

            push!(bench["results"], item)
        end

        push!(data["benchmarks"], bench)
    end

    render(tmpl_report_md, data)
end

const tmpl_report_md_dataframe = """
# Benchmark Report

## Job properties

**Target Project Branches**:
{{#branches}}
- **{{{ name }}}**({{ sha }}) {{#is_base}}**[BASE_BRANCH]**{{/is_base}}
{{/branches}}

## Results Table:

Below is a table of this job's results. The table shows the
performance indicators of the N (N >= 2) commits benchmarked.

| Row |{{#columns}} {{{ . }}} | {{/columns}}
| --- |{{#columns}} --------- | {{/columns}}
{{{ table_body }}}

## Benchmark Logs:

The benchmark log (printed via `log_report`) can be found below.

```
{{#report_logs}}
{{.}}
{{/report_logs}}
```
"""

function generate_report_dataframe(bm_name, branches, shas, base_branch = "")
    result_path = result_dir(bm_name)
    if !(base_branch in branches)
        base_branch = ("master" in branches) ? "master" : branches[1]
    end

    data = Dict{Any, Any}(
        "branches" => [],
    )

    for (br, sha) in zip(branches, shas)
        item = Dict{Any, Any}(
            "name" => br,
            "sha" => snip7(sha),
            "is_base" => br == base_branch,
        )
        push!(data["branches"], item)
    end

    result_files = readdir(joinpath(result_path, snip7(shas[1])))
    result_files = filter(result_files) do fname endswith(fname, ".csv") end
    result_files = filter(result_files) do fname
        all(isfile, map((sha) -> joinpath(result_path, snip7(sha), fname), shas))
    end

    sha_col = Vector{String}()
    results = []

    for fname in result_files
        for sha in shas
            result_file = joinpath(result_path, snip7(sha), fname)
            result = CSV.read(result_file)
            push!(results, result)
            row_count = size(result)[1]
            push!(sha_col, repeat([sha], row_count)...)
        end
    end

    df_results = vcat(results...)
    df_branches = DataFrame(Branch=branches[indexin(sha_col, shas)], Commit=snip7.(sha_col))
    big_dataframe = hcat(df_branches, df_results)
    table_body = join(split(replace(string(big_dataframe), "│" => "|"), "\n")[5:end], "\n")
    data["table_body"] = table_body
    data["columns"] = names(big_dataframe)
    data["report_logs"] = get_benchmark_log(bm_name)

    render(tmpl_report_md_dataframe, data)
end

function get_benchmark_log(bm_name)
    result_path = result_dir(bm_name)
    result_dirs = map(result_path |> readdir) do x joinpath(result_path, x) end
    result_dirs = filter(result_dirs) do x isdir(x) end
    logs = Vector{String}()
    for dir in result_dirs
        log_files = filter(dir |> readdir) do fname endswith(fname, ".log") end
        for fname in log_files
            log_file = joinpath(dir, fname)
            push!(logs, readlines(open(log_file))...)
        end
    end
    return logs
end

# legacy
# Get running time of Stan
function get_stan_time(stan_model_name::String)
    s = readlines(pwd() * "/tmp/$(stan_model_name)_samples_1.csv")
    println(s[end - 1])
    m = match(r"(?<time>[0-9]+.[0-9]*)", s[end - 1])
    parse(Float64, m[:time])
end

end


text:
# External packages
using Revise, DelimitedFiles, SymPy

# Add Exasim to Julia search path
cdir = pwd(); ii = findlast("Exasim", cdir);
include(cdir[1:ii[end]] * "/Installation/setpath.jl");

# Exasim packages
using Preprocessing, Mesh, Gencode, Postprocessing

# create pde structure and mesh structure
pde, mesh = Preprocessing.initializeexasim();

# Define PDE model: governing equations, initial solutions, and boundary conditions
pde.model = "ModelC";            # ModelC, ModelD, ModelW
include("pdemodel.jl");          # include the PDE model file

# Set discretization parameters, physical parameters, and solver parameters
pde.porder = 4;          # polynomial degree
pde.torder = 3;          # time-stepping order of accuracy
pde.nstage = 3;          # time-stepping number of stages
pde.physicsparam = [1 1];    # convective velocity
pde.tau = [1.0];               # DG stabilization parameter
pde.dt = 0.01*ones(200);   # time step sizes
pde.soltime = collect(10:10:length(pde.dt)); # steps at which solution are collected
pde.visdt = 0.01; # visualization timestep size

# Choose computing platform and set number of processors
#pde.platform = "gpu";           # choose this option if NVIDIA GPUs are available
pde.mpiprocs = 1;                # number of MPI processors

# create a linear mesh for a square domain
mesh.p, mesh.t = Mesh.SquareMesh(10,10,1); # a mesh of 8 by 8 quadrilaterals
# expressions for disjoint boundaries
mesh.boundaryexpr = [p -> (p[2,:] .< 1e-3), p -> (p[1,:] .> 1-1e-3), p -> (p[2,:] .> 1-1e-3), p -> (p[1,:] .< 1e-3)];
mesh.boundarycondition = [1 1 1 1]; # Set boundary condition for each disjoint boundary
mesh.periodicexpr = [2 p->p[2,:] 4 p->p[2,:]; 1 p->p[1,:] 3 p->p[1,:]];

# call exasim to generate and run C++ code to solve the PDE model
sol, pde, mesh,~,~,~,~  = Postprocessing.exasim(pde,mesh);


text:
##
#
# Christensen set Julia package
#
# Tomáš Kalvoda, FIT CTU in Prague, 2018--2020
#

"""
A Julia package aimed at exploring the [Christensen set](http://math.ucr.edu/home/baez/roots/) discovered by [Dan Christensen](http://jdc.math.uwo.ca/roots/).
"""
module ChristensenSet

using PolynomialRoots, Statistics
using Images, FileIO
using Distributed, ProgressMeter

export PolynomialIterator
export RootsImage
export find_roots!
export save_image, fill_image!, fill_images!, plot, animate_growth

include("PolynomialIterator.jl")
include("RootsPlot.jl")
include("Computations.jl")

end # module


text:
using RefFields
using Test

struct Imm{T}
    x::T
end

mutable struct Mut{T}
    x::T
end

@testset "RefFields.jl" begin
    let val = Mut(Imm(Imm(1)))
        r = Ref(val)
        gepfield(gepfield(gepfield(val, :x), :x), :x)[] = 2
        @test val.x.x.x == 2
        gepfield(gepfield(gepfield(r, :x), :x), :x)[] = 3
        @test val.x.x.x == 3
    end

    let vec=[1], val = Mut(Imm(vec))
        @test_throws ErrorException gepfield(val, :x)
    end

    let t = ntuple(identity, 4)
        r = Ref{typeof(t)}(t)
        gepfield(r, 2)[] = 1
        @test r[] == (1,1,3,4)
        @test_throws ErrorException gepfield(t, 2)
    end

    let t = ntuple(i->Imm(Imm(i)), 3)
        r = Ref{typeof(t)}(t)
        gepfield(gepfield(gepfield(r, 2), :x), :x)[] = 6
        @test r[][2].x.x == 6
        @test_throws ErrorException gepfield(t, 2)
    end

end


text:
"""
    get_all_symbols(cur_module=current_module())

Lorem ipsum dolor sit amet.
"""
function get_all_symbols(cur_module=current_module())
  all_symbols = []

  for n in names(cur_module, true)
    ( !Base.isidentifier(n) ) && continue
    ( in(n, (Symbol(cur_module), :eval)) ) && continue

    push!(all_symbols, n)
  end

  all_symbols
end


text:
using DynamicBounds

x0(p) = [34.0; 20.0; 0.0; 0.0; 16.0; 0.0]
function f!(du, u, p, t)
    du[1] = -p[1]*u[1]*u[2] + p[2]*u[3] + p[6]*u[6]
    du[2] = -p[1]*u[1]*u[2] + p[2]*u[3] + p[3]*u[3]
    du[3] =  p[1]*u[1]*u[2] - p[2]*u[3] - p[3]*u[3]
    du[4] =  p[3]*u[3] - p[4]*u[4]*u[5] + p[5]*u[6]
    du[5] = -p[4]*u[4]*u[5] + p[5]*u[6] + p[6]*u[6]
    du[6] =  p[4]*u[4]*u[5] - p[5]*u[6] - p[6]*u[6]
    return
end

tspan = (0.0,18.0e-5*250)
pL = [0.1; 0.033; 16.0; 5.0; 0.5; 0.3]
pU = 10.0*pL

prob = ODERelaxProb(f!, tspan, x0, pL, pU)

A = [0.0 -1.0 -1.0  0.0  0.0  0.0;
     0.0  0.0  0.0  0.0 -1.0 -1.0;
     1.0 -1.0 0.0 1.0 -1.0 0.0]
b = [-20.0; -16.0; -2.0]
set!(prob, PolyhedralConstraint(A, b))

xL = zeros(6)
xU = [34.0; 20.0; 20.0; 34.0; 16.0; 16.0]
set!(prob, ConstantStateBounds(xL,xU))

integrator = DifferentialInequality(prob, calculate_relax = false, calculate_subgradient = false)

relax!(integrator)

ratio = rand(6)
pstar = pL.*ratio .+ pU.*(1.0 .- ratio)
setall!(integrator, ParameterValue(), pstar)
integrate!(integrator)


text:
@inline \(::SparseMatrixCSC, ::TransposedVector) = error("Cannot left-divide matrix by transposed vector")

@inline Ac_ldiv_B(L::SparseArrays.CHOLMOD.FactorComponent, ::TransposedVector) = error("Cannot left-divide matrix by transposed vector")


text:
@testset "HeapKNNGraph Tests" begin
    Random.seed!(0)
    small_data_f64 = [rand(5) for _ in 1:50]
    small_data_f32 = [rand(Float32, 5) for _ in 1:50]

    function is_valid_knn_graph(g::ApproximateKNNGraph)
        for i in eachindex(g.heaps)
            heap = g.heaps[i]
            # check that all nodes have exactly K outgoing edges
            if length(heap) != g.n_neighbors
                return false
            end
            # check that all edges start at the src for this heap
            for edge in heap.valtree
                if src(edge) != i
                    return false
                end
            end
            # check that the outgoing edge destinations are unique
            if length(heap) != length(Set(dst.(heap.valtree)))
                return false
            end
        end
        return true
    end

    @testset "Constructor Tests" begin
        for GraphT in [HeapKNNGraph, LockHeapKNNGraph]
            test_data = [rand(2) for _ in 1:4]
            test_metric = Euclidean()
            test_inds = Int[4 3 2 1; 2 1 4 3]
            test_dsts = Float64[1. 2. 3. 4.; 2. 3. 4. 5.]
            g = GraphT(test_data, test_metric, test_inds, test_dsts)
            @test g isa GraphT{Int, Float64, Vector{Vector{Float64}}, Euclidean}
            @test is_valid_knn_graph(g)

            # test construct from inds, dists matrices with matrix data input
            test_data_mat = rand(2, 4)
            DataViewT = typeof(collect(eachcol(test_data_mat)))
            g = GraphT(test_data_mat, test_metric, test_inds, test_dsts)
            @test g isa GraphT{Int, Float64, DataViewT, Euclidean}
            @test is_valid_knn_graph(g)

            @test_throws ErrorException GraphT(test_data, test_metric, test_inds, rand(3, 4))

            k = 10
            n = length(small_data_f64)
            g = GraphT(small_data_f64, k, Euclidean())
            @test g isa GraphT{Int, Float64, Vector{Vector{Float64}}}
            @test is_valid_knn_graph(g)

            # construct new KNNGraph with matrix data input
            g = GraphT(test_data_mat, 2, Euclidean())
            @test g isa GraphT{Int, Float64, DataViewT, Euclidean}
            @test is_valid_knn_graph(g)
        end
    end
    @testset "LightGraphs interface tests" begin
        # test LightGraphs interface methods exist on HeapKNNGraph, LockHeapKNNGraph
        for GraphT in [HeapKNNGraph, LockHeapKNNGraph]
            for m in (edges, vertices, weights, edgetype, ne, nv, eltype)
                @test hasmethod(m, (GraphT,))
            end
            @test hasmethod(has_edge, (GraphT, Int, Int))
            @test hasmethod(has_edge, (GraphT, HeapKNNGraphEdge))
            @test hasmethod(has_vertex, (GraphT, Int))
            @test hasmethod(inneighbors, (GraphT, Int))
            @test hasmethod(outneighbors, (GraphT, Int))
            @test hasmethod(add_edge!, (GraphT, HeapKNNGraphEdge))

            k = 10
            n = length(small_data_f64)
            g = GraphT(small_data_f64, k, Euclidean())
            # edges
            @test length(collect(edges(g))) == n*k
            # vertices
            @test length(collect(vertices(g))) == n
            # weights
            @test size(weights(g)) == (n, n)
            @test sum(weights(g) .!= 0) == n*k
            # edgetype
            @test edgetype(g) <: HeapKNNGraphEdge
            # has_edge(g, s, d), has_edge(g, e)
            e, _ = iterate(edges(g))
            @test has_edge(g, src(e), dst(e))
            @test has_edge(g, e)
            # has_vertex
            @test has_vertex(g, 1)
            @test !has_vertex(g, -1)
            # ne
            @test ne(g) == n*k
            # nv
            @test nv(g) == n
            # outneighbors
            @test length(outneighbors(g, 1)) == k
            @test sizeof(outneighbors(g, 1)) == sizeof(eltype(g))*k
            # inneighbors
            @test eltype(inneighbors(g, 1)) == eltype(g)
            # add_edge!
            # case: fail to add bc it exists
            @test !(add_edge!(g, e))
            # case: fail to add edge bc it exists with a different weight
            e2 = HeapKNNGraphEdge(src(e), dst(e), rand(), flag(e))
            @test !(add_edge!(g, e2))
            # find two nodes without an edge
            function absent_edge(graph)
                # return the index to an edge that isn't in graph
                w = weights(graph)
                for i in eachindex(w)
                    w[i] == 0 && i[1] != i[2] && return i
                end
                error("Shouldn't get here")
            end
            ind = absent_edge(g)
            # test has_edge and add_edge!
            e3 = HeapKNNGraphEdge(ind[2], ind[1], 0.0)
            @test !has_edge(g, e3)
            @test is_valid_knn_graph(g)
            @test add_edge!(g, e3)
            @test is_valid_knn_graph(g)
        end
    end

    @testset "HeapKNNGraph utilities" begin
        for GraphT in [HeapKNNGraph, LockHeapKNNGraph]
            # knn_diameter
            @test hasmethod(knn_diameter, (GraphT{Int}, Int))
            # knn_matrices
            k = 10
            n = length(small_data_f64)
            g = GraphT(small_data_f64, k, Euclidean())
            @test hasmethod(knn_matrices, (GraphT,))
            inds, dists = knn_matrices(g)
            @test size(inds) == size(dists) == (k, n)
            @test all(issorted(col) for col in eachcol(dists))
            for i in 1:size(inds, 2), j_idx in 1:size(inds, 1)
                j = inds[j_idx, i]
                @test has_edge(g, i, j)
            end
            # edge_indices, node_edge
            @test length(edge_indices(g)) == n*k
            for ind in edge_indices(g)
                e = node_edge(g, ind[1], ind[2])
                @test e isa HeapKNNGraphEdge
                @test has_edge(g, e)
            end
            # node_edges
            @test length(node_edges(g, 1)) == k
            for e in node_edges(g, 1)
                @test src(e) == 1
                @test has_edge(g, e)
            end
            # update_flag!
            @test sum((!).(flag).(e for e in edges(g))) == 0 # all true flags
            @test flag(node_edge(g, 1, 1))
            new_e = update_flag!(g, 1, 1, false)
            @test !flag(new_e)
            @test new_e === node_edge(g, 1, 1)
            @test sum((!).(flag).(e for e in edges(g))) == 1 # one false flag
        end
    end

end


text:
using LightXML
using XMLconvert  # https://github.com/bcbi/XMLconvert.jl

include("constants.jl")
include("path.jl")
include("describe.jl")

function write_json(filename)
    s_xml = FunctionDescriptionXML()
    xdoc = parse_string(s_xml)
    xroot = root(xdoc)
    xdict = xml2dict(xroot)
    json_string = xml2json(xroot)
    f = open(filename, "w")
    write(f, json_string)
    close(f)
end

function write_xml(filename)
    s_xml = FunctionDescriptionXML()
    f = open(filename, "w")
    write(f, s_xml)
    close(f)
end

function main()
    path = joinpath(basepath(), "generated")

    filename = joinpath(path, "ta_func_api.xml")
    println("# Write XML file to '$filename'")
    write_xml(filename)

    filename = joinpath(path, "ta_func_api.json")
    println("# Write JSON file to '$filename'")
    write_json(filename)
end

main()


text:
using AbstractFFTs
using FFTW

struct FourierTransform{ℳ, 𝒩, RT <: Number, CT <: Number}
    ℳcut::Int
    𝒩cut::Int
    m::Vector{RT}
    n::Vector{RT}
    χ::Matrix{RT}
    μ::Matrix{CT}
    forw_plan::FFTW.rFFTWPlan{RT,-1,false,2}
    back_plan::AbstractFFTs.ScaledPlan{CT, FFTW.rFFTWPlan{CT, 1, false, 2, Tuple{Int,Int}}, RT}
end

function FourierTransform(grid::Grid2d{M,N,RT}; ℳcut::Int=0, 𝒩cut::Int=0) where {M,N,RT}
    ℳ = M
    𝒩 = div(N,2)+1

    CT = typeof(complex(one(RT),0))

    m = circshift(collect(-div(ℳ-1,2):div(ℳ,2)), -div(ℳ-1,2))
    n = collect(1:𝒩) .- 1

    χ   = cutoff_frequencies(RT, ℳ, 𝒩, ℳcut, 𝒩cut)
    μ   = multiplicity(CT, ℳ, 𝒩)

    forw_plan = plan_rfft(zeros(RT, M, N), (2,1))
    back_plan = plan_irfft(zeros(CT, ℳ, 𝒩), N, (2,1))

    # println(size(forw_plan), eltype(forw_plan), typeof(forw_plan))
    # println(size(back_plan), eltype(back_plan), typeof(back_plan))

    FourierTransform{ℳ, 𝒩, RT, CT}(ℳcut, 𝒩cut, m, n, χ, μ, forw_plan, back_plan)
end

function Base.show(io::IO, ft::FourierTransform{ℳ,𝒩,RT,CT}) where {ℳ,𝒩,RT,CT}
    print(io, "Fourier Transform for data types (", RT, ",", CT, ") with (", ℳ, ",", 𝒩, ") frequencies.\n")
    print(io, "   Cut off frequencies: ", ft.χ, "\n")
    print(io, "   Multiplicity:        ", ft.μ, "\n")
end

function get_trans(ft::FourierTransform{ℳ,𝒩,RT,CT}) where {ℳ,𝒩,RT,CT}
    zeros(CT, ℳ, 𝒩)
end

"""
Plain (real) FFT.
"""
function prfft!(ft::FourierTransform{ℳ,𝒩,RT,CT}, u::Matrix{RT}, û::Matrix{CT}) where {ℳ,𝒩,RT,CT}
    mul!(û, ft.forw_plan, u)
end

"""
Filtered (real) FFT.
"""
function frfft!(ft::FourierTransform{ℳ,𝒩,RT,CT}, u::Matrix{RT}, û::Matrix{CT}) where {ℳ,𝒩,RT,CT}
    mul!(û, ft.forw_plan, u)
    û .*= ft.χ
end

"""
Inverse (real) FFT.
"""
function irfft!(ft::FourierTransform{ℳ,𝒩,RT,CT}, û::Matrix{CT}, u::Matrix{RT}) where {ℳ,𝒩,RT,CT}
    mul!(u, ft.back_plan, û)
end

function cutoff_frequencies(T, ℳ, 𝒩, ℳcut, 𝒩cut)
    χ = ones(T, ℳ, 𝒩)

    if ℳcut > 0
        for n in 1:𝒩
            for m in ℳcut+1:div(ℳ+1,2)
                χ[m, n] = 0
            end
            for m in ℳcut-1:div(ℳ,2)
                χ[end-m, n] = 0
            end
        end
    end

    if 𝒩cut > 0
        for n in 𝒩cut+1:𝒩
            for m in 1:ℳ
                χ[m,n] = 0
            end
        end
    end

    χ
end

function multiplicity(T, ℳ, 𝒩)
    μ = 2ones(T, ℳ, 𝒩)
    μ[:,1] .= 1
    μ
end


text:
push!(LOAD_PATH, joinpath(@__DIR__,"Launcher"))
push!(LOAD_PATH, joinpath(@__DIR__,"Simulation"))
println(LOAD_PATH)
using Launcher

launch()


text:
"""
    get_action_timers() : Nothing

Test performance of current interpolation routines.
"""
function get_action_timers() :: Nothing

    # init test dummys (taking action_su2 as an example)
    list = get_mesh(rand(), 1.0, 30, 0.4)
    m    = Mesh(31, 31, 31, 31, list, list, list, list, list, list)
    p1   = get_param(rand(), list)
    p2   = get_param(rand(), list)
    p3   = get_param(rand(), list)
    l    = get_lattice("square", 10, verbose = false)
    r    = get_reduced_lattice("heisenberg", [[1.0]], l, verbose = false)
    a    = get_action_empty("su2", r, m); init_action!(l, r, a)
    temp = zeros(Float64, length(r.sites), 1, 1)

    # fill self energy with random values
    a.Σ .= rand(31)

    # fill s channel with random values
    a.Γ[1].ch_s.q3 .= rand(length(r.sites), 31, 31, 31)

    # init timer
    to = TimerOutput()

    # time self energy interpolation and extrapolation
    val   = rand()
    outer = m.σ[end] + val

    @timeit to "=> interpolation / extrapolation Σ" begin
        for rep in 1 : 10
            @timeit to "-> interpolation" get_Σ(val, m, a)
            @timeit to "-> extrapolation" get_Σ(outer, m, a)
        end
    end

    # deref channel and generate temp view
    ch    = a.Γ[1].ch_s
    vtemp = view(temp, :, 1, 1)

    # time q3 interpolations
    @timeit to "=> interpolation q3 kernel" begin
        # time sequential routine
        for rep in 1 : 10
            @timeit to "-> sequential" begin
                for i in 1 : size(temp, 1)
                    temp[i, 1, 1] = get_q3(i, p1, p2, p3, ch)
                end
            end
        end

        # time vectorized routine
        for rep in 1 : 10
            @timeit to "-> vectorized" begin
                get_q3_avx!(r, p1, p2, p3, ch, vtemp, false, 1.0)
            end
        end
    end

    # time q2_2 interpolations
    @timeit to "=> interpolation q2_2 kernel" begin
        # time sequential routine
        for rep in 1 : 10
            @timeit to "-> sequential" begin
                for i in 1 : size(temp, 1)
                    temp[i, 1, 1] = get_q2_2(i, p1, p3, ch)
                end
            end
        end

        # time vectorized routine
        for rep in 1 : 10
            @timeit to "-> vectorized" begin
                get_q2_2_avx!(r, p1, p3, ch, vtemp, false, 1.0)
            end
        end
    end

    # time q2_1 interpolations
    @timeit to "=> interpolation q2_1 kernel" begin
        # time sequential routine
        for rep in 1 : 10
            @timeit to "-> sequential" begin
                for i in 1 : size(temp, 1)
                    temp[i, 1, 1] = get_q2_1(i, p1, p2, ch)
                end
            end
        end

        # time vectorized routine
        for rep in 1 : 10
            @timeit to "-> vectorized" begin
                get_q2_1_avx!(r, p1, p2, ch, vtemp, false, 1.0)
            end
        end
    end

    # time q1 interpolations
    @timeit to "=> interpolation q1 kernel" begin
        # time sequential routine
        for rep in 1 : 10
            @timeit to "-> sequential" begin
                for i in 1 : size(temp, 1)
                    temp[i, 1, 1] = get_q1(i, p1, ch)
                end
            end
        end

        # time vectorized routine
        for rep in 1 : 10
            @timeit to "-> vectorized" begin
                get_q1_avx!(r, p1, ch, vtemp, false, 1.0)
            end
        end
    end

    show(to)

    return nothing
end


text:
### Utilities used by @match macro
# author: Kevin Squire (@kmsquire)

#
# ismatch
#

Base.ismatch{R<:Number}(r::Range{R}, s::Number) = s in r
Base.ismatch{T}(r::Range{T}, s::T) = s in r
Base.ismatch(c::Char, s::Number) = false
Base.ismatch(s::Number, c::Char) = false
Base.ismatch(r,s) = (r == s)

#
# slicedim
#
# "view" version of slicedim

function _slicedim(A::AbstractArray, d::Integer, i::Integer)
    if (d < 1) | (d > ndims(A))
        throw(BoundsError())
    end
    sz = size(A)
    # Force 1x..x1 slices to extract the value
    # Note that this is no longer a reference.
    otherdims = [sz...]
    splice!(otherdims, d)
    if all(otherdims .== 1)
        A[[ n==d ? i : 1 for n in 1:ndims(A) ]...]
    else
        view(A, [ n==d ? i : (1:sz[n]) for n in 1:ndims(A) ]...)
    end
end

function _slicedim(A::AbstractArray, d::Integer, i)
    if (d < 1) | (d > ndims(A))
        throw(BoundsError())
    end
    sz = size(A)
    view(A, [ n==d ? i : (1:sz[n]) for n in 1:ndims(A) ]...)
end

_slicedim(A::AbstractVector, d::Integer, i::Integer) =
    (if (d < 0) | (d > 1);  throw(BoundsError()) end;  A[i])

_slicedim(A::AbstractVector, d::Integer, i) =
    (if (d < 0) | (d > 1);  throw(BoundsError()) end;  view(A, i))

function slicedim(A::AbstractArray, s::Integer, i::Integer, from_end::Bool = false)
    d = s + max(ndims(A)-2, 0)
    from_end && (i = size(A,d)-i)
    _slicedim(A, d, i)
end

function slicedim(A::AbstractArray, s::Integer, i::Integer, j::Integer)
    d = s + max(ndims(A)-2, 0)
    j = size(A,d)-j # this is the distance from the end of the dim size
    _slicedim(A, d, i:j)
end

#
# getvars
#
# get all symbols in an expression

getvars(e)         = Symbol[]
getvars(e::Symbol) = startswith(string(e),'@') ? Symbol[] : Symbol[e]

function getvars(e::Expr)
    if isexpr(e, :call)
        getvars(e.args[2:end])
    else
        getvars(e.args)
    end
end
getvars(es::AbstractArray) = union([getvars(e) for e in es]...)

#
# getvar
#
# get the symbol from a :(::) expression

getvar(x::Expr) = isexpr(x, :(::)) ? x.args[1] : x
getvar(x::Symbol) = x

#
# check_dim_size_expr
#
# generate an expression to check the size of a variable dimension against an array of expressions

function check_dim_size_expr(val, dim, ex::Expr)
    if length(ex.args) == 0 || !any([isexpr(e, :(...)) for e in ex.args])
        :(Match.checkdims($val, $dim, $(length(ex.args))))
    else
        :(Match.checkdims2($val, $dim, $(length(ex.args))))
    end
end

function checkdims(val::AbstractArray, dim, dimsize)
    dim = dim + max(ndims(val)-2, 0)
    dim <= ndims(val) && size(val, dim) == dimsize
end

checkdims(val, dim, dimsize) = false

function checkdims2(val::AbstractArray, dim, dimsize)
    dim = dim + max(ndims(val)-2, 0)
    dim <= ndims(val) && size(val, dim) >= dimsize-1
end

checkdims2(val, dim, dimsize) = false

#
# check_tuple_len_expr
#
# generate an expression to check the length of a tuple variable against a tuple expression

function check_tuple_len_expr(val, ex::Expr)
    if length(ex.args) == 0 || !any([isexpr(e, :(...)) for e in ex.args])
        :(length($val) == $(length(ex.args)))
    else
        :(length($val) >= $(length(ex.args)-1))
    end
end

function check_tuple_len(val::Expr, ex::Expr)
    if !isexpr(val, :tuple) || !isexpr(ex, :tuple)
        false
    elseif length(ex.args) == 0 || !any([isexpr(e, :(...)) for e in ex.args])
        length(val.args) == length(ex.args)
    else
        length(val.args) >= length(ex.args)-1
    end
end

#
# joinexprs
#
# join an array of (e.g., true/false) expressions with an operator

function joinexprs(exprs::AbstractArray, oper::Symbol, default=:nothing)
    len = length(exprs)

    len == 0 ? default :
    len == 1 ? exprs[1] :
               Expr(oper, joinexprs(view(exprs, 1:(len-1)), oper, default), exprs[end])
end

#
# let_expr
#
# generate an optional let expression

let_expr(expr, assignments::AbstractArray) =
    length(assignments) > 0 ? Expr(:let, expr, assignments...) : expr

#
# array_type_of
#
# modify x::Type => x::AbstractArray{Type}

function array_type_of(ex::Expr)
    if isexpr(ex, :(::))
        :($(ex.args[1])::AbstractArray{$(ex.args[2])})
    else
        ex
    end
end

array_type_of(sym::Symbol) = :($sym::AbstractArray)


text:
ignore = false  # when re-generating

draw(axes, PNG("defaults.png", 140, 100))
ignore || compare("defaults.png")

include("portrait.jl")
ignore || compare("portrait.png")

draw(axes, PNG("landscape.png", 140, 100), Paper("lightgrey"))
ignore || compare("landscape.png")

draw(axes, PNG("scale.png", 100, 140), Paper("lightgrey"), Scale(0.5))
ignore || compare("scale.png")

draw(axes, PNG("translate.png", 100, 140), Paper("lightgrey"), Translate(0.1, 0.1))
ignore || compare("translate.png")

draw(axes, PNG("negative-portrait.png", 100, 140), Paper("lightgrey"),
     Axes(negative=true))
ignore || compare("negative-portrait.png")

include("negative-landscape.jl")
ignore || compare("negative-landscape.png")

include("round-round.jl")
ignore || compare("round-round.png")

include("butt-mitre.jl")
ignore || compare("butt-mitre.png")

include("square-bevel.jl")
ignore || compare("square-bevel.png")

include("square.jl")
ignore || compare("square.png")

include("square-scale.jl")
ignore || compare("square-scale.png")

include("square-scale-translate.jl")
ignore || compare("square-scale-translate.png")

include("square-rotate.jl")
ignore || compare("square-rotate.png")

with(PNG("orange-blue-square.png", 100, 100), Pen(0.05)) do
    draw(Ink("orange")) do
        move(0.0, 0.0)
        line(1.0, 0.0)
        line(1.0, 1.0)
    end
    draw(Ink("blue")) do
        line(0.0, 1.0)
        line(0.0, 0.0)
    end
end
ignore || compare("orange-blue-square.png")

include("red-blue-square.jl")
ignore || compare("red-blue-square.png")

with(PNG("cross-left.png", 140, 100),
     Axes(centred=false, negative=true)) do
    draw() do
        move(-1.0, 0.0)
        line(1.0, 0.0)
        move(0.0, -1.0)
        line(0.0, 1.0)
    end
end
ignore || compare("cross-left.png")

with(PNG("cross-bottom.png", 100, 140),
     Axes(centred=false, negative=true)) do
    draw() do
        move(-1.0, 0.0)
        line(1.0, 0.0)
        move(0.0, -1.0)
        line(0.0, 1.0)
    end
end
ignore || compare("cross-bottom.png")


text:
# TODO: set ttl?

"""
Local cache on each worker to reduce remote call.
The links may be staled.
"""
const POT_LINK_CACHE = Dict{PotID, RemoteChannel{Channel{Any}}}()

"""
Only valid on the driver to keep track of all registered pots.
TODO: use a kv db
"""
const POT_REGISTRY = Dict{PotID, Pot}()
const POT_CHILDREN = Dict{PotID, Set{PotID}}()

function is_registered(p::Pot)
    is_exist = remotecall_wait(1) do
        haskey(Oolong.POT_REGISTRY, p.pid)
    end
    is_exist[]
end

function register(p::Pot)
    remotecall_wait(1) do
        Oolong.POT_REGISTRY[p.pid] = p
        children = get!(Oolong.POT_CHILDREN, parent(p.pid), Set{PotID}())
        push!(children, p.pid)
    end
end

function unregister(p::PotID)
    remotecall_wait(1) do
        delete!(Oolong.POT_REGISTRY, p)
        delete!(Oolong.POT_CHILDREN, p)
    end
end

function children(p::PotID)
    remotecall_wait(1) do
        # ??? data race
        get!(Oolong.POT_CHILDREN, p, Set{PotID}())
    end
end

function link(p::PotID, ch::RemoteChannel)
    POT_LINK_CACHE[p] = ch
    if myid() != 1
        remotecall_wait(1) do
            Oolong.POT_LINK_CACHE[p] = ch
        end
    end
end

function Base.getindex(p::PotID)
    get!(POT_LINK_CACHE, p) do
        ch = remotecall_wait(1) do
            get(Oolong.POT_LINK_CACHE, p, nothing)
        end
        if isnothing(ch[])
            boil(p)
        else
            ch[]
        end
    end
end

whereis(p::PotID) = p[].where

function Base.getindex(p::PotID, ::typeof(!))
    pot = remotecall_wait(1) do
        get(Oolong.POT_REGISTRY, p, nothing)
    end
    if isnothing(pot[])
        throw(PotNotRegisteredError(p))
    else
        pot[]
    end
end

"""
For debug only. Only a snapshot is returned.
!!! DO NOT MODIFY THE RESULT DIRECTLY
"""
Base.getindex(p::PotID, ::typeof(*)) = p(_self())[]

local_boil(p::PotID) = local_boil(p[!])

function local_boil(p::Pot)
    pid, tea_bag, logger = p.pid, p.tea_bag, p.logger
    ch = RemoteChannel() do
        Channel(typemax(Int),spawn=true) do ch
            task_local_storage(KEY, PotState(pid, current_task()))
            with_logger(logger) do
                tea = tea_bag()
                while true
                    try
                        flavor = take!(ch)
                        process(tea, flavor)
                        if flavor isa CloseMsg || flavor isa RemoveMsg
                            break
                        end
                    catch err
                        @debug err
                        flavor = parent()(err)[]
                        if msg isa ResumeMsg
                            process(tea, flavor)
                        elseif msg isa CloseMsg
                            process(tea, flavor)
                            break
                        elseif msg isa RestartMsg
                            process(tea, PreRestartMsg())
                            tea = tea_bag()
                            process(tea, PostRestartMsg())
                        else
                            @error "unknown msg received from parent: $exec"
                            rethrow()
                        end
                    finally
                    end
                end
            end
        end
    end
    link(pid, ch)
    ch
end

"blocking until a valid channel is established"
boil(p::PotID) = local_scheduler()(p)[!]

struct CPUInfo
    total_threads::Int
    allocated_threads::Int
    total_memory::Int
    free_memory::Int
    function CPUInfo()
        new(
            Sys.CPU_THREADS,
            Threads.nthreads(),
            convert(Int, Sys.total_memory()),
            convert(Int, Sys.free_memory()),
        )
    end
end

struct GPUInfo
    name::String
    total_memory::Int
    free_memory::Int
    function GPUInfo()
        new(
            name(device()),
            CUDA.total_memory(),
            CUDA.available_memory()
        )
    end
end

struct ResourceInfo
    cpu::CPUInfo
    gpu::Vector{GPUInfo}
end

function ResourceInfo()
    cpu = CPUInfo()
    gpu = []
    if CUDA.functional()
        for d in devices()
            device!(d) do
                push!(gpu, GPUInfo())
            end
        end
    end
    ResourceInfo(cpu, gpu)
end

Base.convert(::Type{ResourceInfo}, r::ResourceInfo) = ResourceInfo(r.cpu.allocated_threads, length(r.gpu))

struct HeartBeat
    resource::ResourceInfo
    available::ResourceInfo
    from::PotID
end

struct LocalScheduler
    pending::Dict{PotID, Future}
    peers::Ref{Dict{PotID, ResourceInfo}}
    available::Ref{ResourceInfo}
    timer::Timer
end

# TODO: watch exit info

function LocalScheduler()
    pid = self()
    req = convert(ResourceInfo, ResourceInfo())
    available = Ref(req)
    timer = Timer(1;interval=1) do t
        HeartBeat(ResourceInfo(), available[], pid) |> SCHEDULER  # !!! non blocking
    end

    pending = Dict{PotID, Future}()
    peers = Ref(Dict{PotID, ResourceInfo}(pid => req))

    LocalScheduler(pending, peers, available, timer)
end

function (s::LocalScheduler)(p::PotID)
    pot = p[!]
    if pot.require <= s.available[]
        res = local_boil(p)
        s.available[] -= pot.require
        res
    else
        res = Future()
        s.pending[p] = res
        res
    end
end

function (s::LocalScheduler)(peers::Dict{PotID, ResourceInfo})
    s.peers[] = peers
    for (p, f) in s.pending
        pot = p[!]
        for (w, r) in peers
            if pot.require <= r
                # transfer to w
                put!(f, w(p))
                delete!(s.pending, p)
                break
            end
        end
    end
end

Base.@kwdef struct Scheduler
    workers::Dict{PotID, HeartBeat} = Dict()
    pending::Dict{PotID, Future} = Dict()
end

# ??? throttle
function (s::Scheduler)(h::HeartBeat)
    # ??? TTL
    s.workers[h.from] = h

    for (p, f) in s.pending
        pot = p[!]
        if pot.require <= h.available
            put!(f, h.from(p))
        end
    end

    Dict(
        p => h.available
        for (p, h) in s.workers
    ) |> h.from  # !!! non blocking
end

# pots are all scheduled on workers only
function (s::Scheduler)(p::PotID)
    pot = p[!]
    for (w, h) in s.workers
        if pot.require <= h.available
            return w(p)
        end
    end
    res = Future()
    s.pending[p] = res
    res
end


text:
# --------------------------------------------------------------------------------
# Liquidity traits
# --------------------------------------------------------------------------------

abstract type LiquidityStyle end
struct IsLiquid <: LiquidityStyle end
struct IsIlliquid <: LiquidityStyle end

# Default behavior is illiquid
LiquidityStyle(::Type{<:Asset}) = IsIlliquid()

# Cash is always liquid
LiquidityStyle(::Type{<:Cash}) = IsLiquid()

# Any subtype of Investment is liquid
LiquidityStyle(::Type{<:Investment}) = IsLiquid()

# --- Trait behavior ---

# The thing is tradable if it is liquid
tradable(x::T) where {T} = tradable(LiquidityStyle(T), x)
tradable(::IsLiquid, x) = true
tradable(::IsIlliquid, x) = false

# The thing has a market price if it is liquid
marketprice(x::T) where {T} = marketprice(LiquidityStyle(T), x)
marketprice(::IsLiquid, x) = error("Please implement pricing function for ", typeof(x))
marketprice(::IsIlliquid, x) = error("Price for illiquid asset $x is not available.")

# Sample pricing functions for Money and Stock
marketprice(x::Money) = x.amount
marketprice(x::Stock) = rand(200:250)


text:
abstract type AbstractMissile <: AbstractEnv end

"""
Single Point Mass Vehicle Kinematics
"""
struct PointMassMissile <: AbstractMissile
    dim::Int  # @assert dim == 2 || dim == 3
    A_max::Float64
end

function State(env::PointMassMissile)
    @unpack dim = env
    return function (p=zeros(dim), v=zeros(dim))
        ComponentArray(p=p, v=v)
    end
end

function Dynamics!(env::PointMassMissile)
    @unpack A_max = env
    @Loggable function dynamics!(dx, x, params, t; u)
        @unpack p, v = x
        @log p
        @log v
        dx.p = v
        if norm(u) >= A_max
            u = normalize(u) * min(max(norm(u), -A_max), A_max)
        end
        dx.v = u
    end
end

"""
1 Pursuer on 1 Evador Engagement
"""
struct PursuerEvadorMissile <: AbstractMissile
    pursuer::PointMassMissile
    evador::PointMassMissile
end

function State(env::PursuerEvadorMissile)
    @unpack pursuer, evador = env
    return function (x0_pursuer, x0_evador)
        ComponentArray(pursuer=x0_pursuer, evador=x0_evador)
    end
end

function Dynamics!(env::PursuerEvadorMissile)
    @unpack pursuer, evador = env
    @Loggable function dynamics!(dx, x, params, t; u_pursuer, u_evador)
        @onlylog r = norm(x.pursuer.p - x.evador.p)
        @nested_log :pursuer Dynamics!(pursuer)(dx.pursuer, x.pursuer, nothing, t; u=u_pursuer)
        @nested_log :evador Dynamics!(evador)(dx.evador, x.evador, nothing, t; u=u_evador)
    end
end


text:
__precompile__()

module StructuredOptimization

using LinearAlgebra
using RecursiveArrayTools
using AbstractOperators
using ProximalOperators
using ProximalAlgorithms

include("syntax/syntax.jl")
include("calculus/precomposeNonlinear.jl") #TODO move to ProximalOperators?
include("solvers/solvers.jl")

end