text:
function similar_cells( cell_connectivity1::Vector{Int},
                        cell_type1::Int,
                        cell_connectivity2::Union{Tuple{Vararg{Int}}, Vector{Int}},
                        cell_type2::Int
                      )
    for k in cell_connectivity2
        in(k, cell_connectivity1) || return false
    end
    cell_type1 == cell_type2 || return false
    return true
end

function coherent(a::T, b::T) where {T<:AbstractVTKMultiblockData}
    return same_ordered_geometry_shape(a,b) && same_data_shape(a,b)
end
function coherent(a::T, b::T) where {T<:AbstractVTKSimpleData}
    return same_geometry_shape(a, b) && same_data_shape(a, b)
end
function coherent(dataset::AbstractTimeSeriesVTKData)
    length(dataset) == 0 && return true
    block1 = dataset[1]
    for block in dataset[2:end]
        coherent(block1, block) || return false
    end
    return true
end
function coherent(a::T, b::T) where {T<:AbstractTimeSeriesVTKData}
    length(a) == length(b) &&
    coherent(a) && coherent(b) && begin
        _out = true
        for i in 1:length(a)
            (_out = coherent(a[i], b[i])) || break
        end
        _out
    end || return false
    return true
end
coherent(a::AbstractVTKData, b::AbstractVTKData) = false

extents(dataset::VTKStructuredData) = Base.tail(size(dataset.point_coords))
function extents(dataset::VTKRectilinearData)
    return ntuple(i->length(dataset.point_coords[i]), Val(length(dataset.point_coords)))
end
extents(dataset::VTKUniformRectilinearData) = dataset.extents
extents(dataset::AbstractVTKStructuredData, ind::Int) = extents(dataset)[ind]
extents(dataset::AbstractVTKStructuredData, ind::Int...) = extents.(Ref(dataset), ind)
cell_extents(dataset::AbstractVTKStructuredData) = ntuple(i -> extents(dataset, i) .- 1, dim(dataset))
cell_extents(dataset::AbstractVTKStructuredData, ind::Int) = cell_extents(dataset)[ind]
cell_extents(dataset::AbstractVTKStructuredData, ind::Int...) = cell_extents.(Ref(dataset), ind)

dim(dataset::AbstractVTKUnstructuredData) = size(dataset.point_coords, 1)
dim(dataset::AbstractVTKStructuredData) = length(extents(dataset))
function dim(dataset::AbstractVTKMultiblockData)
    return maximum(i -> dim(dataset[i]), 1:length(dataset))
end

num_of_points(dataset::AbstractVTKUnstructuredData) = size(dataset.point_coords, 2)
num_of_points(dataset::AbstractVTKStructuredData) = prod(extents(dataset))
num_of_points(dataset::AbstractVTKMultiblockData) = sum(num_of_points(i) for i in dataset)
num_of_points(dataset::AbstractTimeSeriesVTKData, ind::Int=1) = num_of_points(dataset[ind])

num_of_cells(dataset::AbstractVTKUnstructuredData) = length(dataset.cell_types)
num_of_cells(dataset::AbstractVTKStructuredData) = prod(extents(dataset) .- 1)
num_of_cells(dataset::AbstractVTKMultiblockData) = sum(num_of_cells(i) for i in dataset)
num_of_cells(dataset::AbstractTimeSeriesVTKData, ind::Int=1) = num_of_cells(dataset[ind])

num_of_point_vars(dataset::AbstractVTKSimpleData) = length(dataset.point_data)
num_of_cell_vars(dataset::AbstractVTKSimpleData) = length(dataset.cell_data)

function cell_type(dataset::Union{VTKUnstructuredData, VTKPolyData}, ind::Int)
    return dataset.cell_types[ind]
end
function cell_type(dataset::Union{VTKStructuredData, VTKRectilinearData}, ind::Int)
    if ind < 1 || ind > num_of_cells(dataset)
        throw("Out of bounds.")
    else
        return dim(dataset) == 2 ? 9 : 12
    end
end
function cell_type(dataset::VTKUniformRectilinearData, ind::Int)
    if ind < 1 || ind > num_of_cells(dataset)
        throw("Out of bounds.")
    else
        return dim(dataset) == 2 ? 8 : 11
    end
end
function cell_type(dataset::AbstractVTKStructuredData, ind::Tuple{Vararg{Int}})
    return cell_type(dataset, (LinearIndices(cell_extents(dataset)))[ind...])
end

function cell_connectivity(dataset::AbstractVTKUnstructuredData, cell_ind::Int)
    return dataset.cell_connectivity[cell_ind]
end
function cell_connectivity(dataset::T, cell_ind::Tuple{Vararg{Int}}) where {T<:AbstractVTKStructuredData}
    pextents = extents(dataset)
    return cell_connectivity(T, pextents, cell_ind)
end

function cell_connectivity(dataset::T, cell_ind) where {T<:AbstractVTKStructuredData}
    return cell_connectivity(T, extents(dataset), cell_ind)
end

function cell_connectivity(T::DataType, pextents::NTuple{N,Int}, cell_ind::NTuple{N,Int}) where {N}
    corner_point_ind = (LinearIndices(pextents))[cell_ind...]
    if N == 2
        #1 <= cell_ind <= num_of_cells(dataset) || throw("Out of bounds.")
        if T <: VTKUniformRectilinearData
            return [corner_point_ind, corner_point_ind + 1, corner_point_ind + pextents[1],
                    corner_point_ind + pextents[1] + 1]
        else
            return [corner_point_ind, corner_point_ind + 1, corner_point_ind + pextents[1] + 1,
                    corner_point_ind + pextents[1]]
        end
    elseif N == 3
        #1 <= cell_ind <= num_of_cells(dataset) || throw("Out of bounds.")
        if T <: VTKUniformRectilinearData
            return [corner_point_ind, corner_point_ind + 1, corner_point_ind + pextents[1],
                    corner_point_ind + pextents[1] + 1,
                    corner_point_ind + pextents[1] * pextents[2],
                    corner_point_ind + pextents[1] * pextents[2] + 1,
                    corner_point_ind + pextents[1] * pextents[2] + pextents[1],
                    corner_point_ind + pextents[1] * pextents[2] + pextents[1] + 1]
        else
            return [corner_point_ind, corner_point_ind + 1, corner_point_ind + pextents[1] + 1,          corner_point_ind + pextents[1],
                    corner_point_ind + pextents[1] * pextents[2],
                    corner_point_ind + pextents[1] * pextents[2] + 1,
                    corner_point_ind + pextents[1] * pextents[2] + pextents[1] + 1, corner_point_ind + pextents[1] * pextents[2] + pextents[1]]
        end
    end

    throw("Invalid mesh dimension.")
end

function has_var(dataset::AbstractStaticVTKData, var_name::String)
    if haskey(dataset.point_data, var_name)
        return true, "Point"
    elseif haskey(dataset.cell_data, var_name)
        return true, "Cell"
    else
        return false, ""
    end
end

function var_dim(dataset::AbstractVTKUnstructuredData, var_name::String, var_type::String="")
    if var_type == "Point" && haskey(dataset.point_data, var_name)
        if length(size(dataset.point_data[var_name])) == 1
            return 1
        else
            return length(dataset.point_data[var_name][:,1])
        end
    elseif var_type == "Cell" && haskey(dataset.cell_data, var_name)
        if length(size(dataset.cell_data[var_name])) == 1
            return 1
        else
            return length(dataset.cell_data[var_name][:,1])
        end
    elseif var_type == ""
        try
            return var_dim(dataset, var_name, "Point")
        catch
            return var_dim(dataset, var_name, "Cell")
        end
    else
        throw("Variable $var_name doesn't exist, please use has_var to check if the variable exists before using this function.")
    end
end

function var_dim(dataset::AbstractVTKStructuredData, var_name::String, var_type::String="")
    if var_type == "Point" && haskey(dataset.point_data, var_name)
        if length(size(dataset.point_data[var_name])) == length(extents(dataset))
            return 1
        else
            return size(dataset.point_data[var_name], 1)
        end
    elseif var_type == "Cell" && haskey(dataset.cell_data, var_name)
        if length(size(dataset.cell_data[var_name])) == length(extents(dataset))
            return 1
        else
            return size(dataset.cell_data[var_name], 1)
        end
    elseif var_type == ""
        try
            return var_dim(dataset, var_name, "Point")
        catch
            return var_dim(dataset, var_name, "Cell")
        end
    else
        throw("Variable $var_name doesn't exist, please use has_var to check if the variable exists before using this function.")
    end
end

function vtk_cell_type_name(dataset::AbstractVTKStructuredData, cell_id)
    return VTK_CELL_TYPE[cell_type(dataset, cell_id)]
end
function is_homogeneous(dataset::AbstractVTKUnstructuredData)
    begin
        out = true
        for i in 2:num_of_cells(dataset)
            (out = cell_type(dataset, 1) == cell_type(dataset, i)) || break
        end
        out
    end || return false
    return true
end

is_homogeneous(dataset::AbstractVTKStructuredData) = true

function is_homogeneous(dataset::AbstractVTKMultiblockData)
    return all(i -> typeof(dataset[1]) == typeof(dataset[i]), 2:length(dataset))
end
function is_homogeneous(dataset::AbstractTimeSeriesVTKData)
    length(dataset) == 0 ||
    is_homogeneous(dataset[1]) && begin
        out = true
        i = dataset[1]
        T = typeof(i)
        for j in dataset
            (out = isa(j, T)) || break
            if T <: AbstractVTKMultiblockData || T <: AbstractVTKUnstructuredData
                (out = same_ordered_geometry_shape(i,j)) || break
            else
                (out = same_geometry_shape(i,j)) || break
            end
        end
        out
    end || return false
    return true
end

function get_cell_ids(dataset::AbstractVTKUnstructuredData, cell_types::Vector{Int})
    cell_ids = Int[]
    for (i, c) in enumerate(dataset.cell_types)
        if in(c, cell_types)
            push!(cell_ids, i)
        end
    end
    return cell_ids
end

function get_lowest_index(cell_connectivity)
    return minimum(i -> minimum(cell_connectivity[i]), 1:length(cell_connectivity))
end
function get_highest_index(cell_connectivity)
    return maximum(i -> maximum(cell_connectivity[i]), 1:length(cell_connectivity))
end
function is_valid_cell(cell_connectivity, cell_type)
    return VTK_CELL_TYPE[cell_type].nodes == -1 ||
            length(_cell_connectivity) == VTK_CELL_TYPE[cell_type].nodes
end
num_of_blocks(dataset::AbstractVTKMultiblockData) = length(dataset.blocks)

timespan(dataset::VTKTimeSeriesData) = dataset.timemarkers[end] - dataset.timemarkers[1]

num_of_timesteps(dataset::VTKTimeSeriesData) = length(dataset.timemarkers)

triangular(dataset::AbstractVTKUnstructuredData) = all(i -> i == 5, dataset.cell_types)

function bb(dataset::T) where {T<:AbstractVTKData}
    if T <: AbstractVTKUnstructuredData
        return bb_unstruct(dataset)
    elseif T <: VTKStructuredData
        return bb_struct(dataset)
    elseif T <: VTKRectilinearData
        return bb_rect(dataset)
    elseif T <: VTKUniformRectilinearData
        return bb_image(dataset)
    elseif T <: Union{AbstractVTKMultiblockData, AbstractTimeSeriesVTKData}
        return bb_multiblock_time(dataset)
    end
    throw("Unsupported type.")
end

function bb_unstruct(dataset::AbstractVTKUnstructuredData)
    _dim = dim(dataset)
    min_x = max_x = dataset.point_coords[1,1]
    min_y = max_y = dataset.point_coords[2,1]
    if _dim == 2
        for i in 2:num_of_points(dataset)
            if dataset.point_coords[1,i] > max_x
                max_x = dataset.point_coords[1,i]
            elseif dataset.point_coords[1,i] < min_x
                min_x = dataset.point_coords[1,i]
            end

            if dataset.point_coords[2,i] > max_y
                max_y = dataset.point_coords[2,i]
            elseif dataset.point_coords[2,i] < min_y
                min_y = dataset.point_coords[2,i]
            end
        end
        return (min_x, max_x, min_y, max_y)
    elseif _dim == 3
        min_z = max_z = dataset.point_coords[3,1]
        for i in 2:num_of_points(dataset)
            if dataset.point_coords[1,i] > max_x
                max_x = dataset.point_coords[1,i]
            elseif dataset.point_coords[1,i] < min_x
                min_x = dataset.point_coords[1,i]
            end

            if dataset.point_coords[2,i] > max_y
                max_y = dataset.point_coords[2,i]
            elseif dataset.point_coords[2,i] < min_y
                min_y = dataset.point_coords[2,i]
            end

            if dataset.point_coords[3,i] > max_z
                max_z = dataset.point_coords[3,i]
            elseif dataset.point_coords[3,i] < min_z
                min_z = dataset.point_coords[3,i]
            end
        end
        return (min_x, max_x, min_y, max_y, min_z, max_z)
    else
        throw("Invalid dimension.")
    end
end

@resumable function surface_cell_inds(dataset::AbstractVTKStructuredData)
    cextents = cell_extents(dataset)
    if dim(dataset) == 2
        i = 1
        for j in 1:cextents[2]
            @yield (i,j)
        end
        j = 1
        for i in 2:cextents[1]
            @yield (i,j)
        end

        i = cextents[1]
        for j in 2:cextents[2]
            @yield (i,j)
        end
        j = cextents[2]
        for i in 2:cextents[1]-1
            @yield (i,j)
        end
    else
        i = 1
        for j in 1:cextents[2], k in 1:cextents[3]
            @yield (i,j,k)
        end

        j = 1
        for i in 2:cextents[1], k in 1:cextents[3]
            @yield (i,j,k)
        end

        k = 1
        for i in 2:cextents[1], j in 2:cextents[2]
            @yield (i,j,k)
        end

        i = cextents[1]
        for j in 2:cextents[2], k in 2:cextents[3]
            @yield (i,j,k)
        end

        j = cextents[2]
        for i in 2:cextents[1]-1, k in 2:cextents[3]
            @yield (i,j,k)
        end

        k = cextents[3]
        for i in 2:cextents[1]-1, j in 2:cextents[3]-1
            @yield (i,j,k)
        end
    end
end

function bb_struct(dataset::VTKStructuredData)
    cextents = cell_extents(dataset)
    _dim = dim(dataset)

    min_x = max_x = dataset.point_coords[1,1]
    min_y = max_y = dataset.point_coords[2,1]
    if _dim == 2
        for (i,j) in surface_cell_inds(dataset)
            if dataset.point_coords[1,i] > max_x
                max_x = dataset.point_coords[1,i]
            elseif dataset.point_coords[1,i] < min_x
                min_x = dataset.point_coords[1,i]
            end

            if dataset.point_coords[2,i] > max_y
                max_y = dataset.point_coords[2,i]
            elseif dataset.point_coords[2,i] < min_y
                min_y = dataset.point_coords[2,i]
            end
        end
        return (min_x, max_x, min_y, max_y)
    elseif _dim == 3
        min_z = max_z = dataset.point_coords[3,1]
        for (i,j,k) in surface_cell_inds(dataset)
            if dataset.point_coords[1,i] > max_x
                max_x = dataset.point_coords[1,i]
            elseif dataset.point_coords[1,i] < min_x
                min_x = dataset.point_coords[1,i]
            end

            if dataset.point_coords[2,i] > max_y
                max_y = dataset.point_coords[2,i]
            elseif dataset.point_coords[2,i] < min_y
                min_y = dataset.point_coords[2,i]
            end

            if dataset.point_coords[3,i] > max_z
                max_z = dataset.point_coords[3,i]
            elseif dataset.point_coords[3,i] < min_z
                min_z = dataset.point_coords[3,i]
            end
        end
        return (min_x, max_x, min_y, max_y, min_z, max_z)
    else
        throw("Invalid dimension.")
    end
end

function bb_rect(dataset::VTKRectilinearData)
    min_x = dataset.point_coords[1][1]
    max_x = dataset.point_coords[1][end]
    min_y = dataset.point_coords[2][1]
    max_y = dataset.point_coords[2][end]

    if dim(dataset) == 2
        return (min_x, max_x, min_y, max_y)
    elseif dim(dataset) == 3
        min_z = dataset.point_coords[3][1]
        max_z = dataset.point_coords[3][end]
        return (min_x, max_x, min_y, max_y, min_z, max_z)
    else
        throw("Invalid dimension.")
    end
end

function bb_image(dataset::VTKUniformRectilinearData)
    min_x = dataset.origin[1]
    max_x = dataset.origin[1] + (dataset.extents[1]-1)*dataset.spacing[1]
    min_x = dataset.origin[2]
    max_x = dataset.origin[2] + (dataset.extents[2]-1)*dataset.spacing[2]

    if dim(dataset) == 2
        return (min_x, max_x, min_y, max_y)
    elseif dim(dataset) == 3
        min_x = dataset.origin[3]
        max_x = dataset.origin[3] + (dataset.extents[3]-1)*dataset.spacing[3]
        return (min_x, max_x, min_y, max_y, min_z, max_z)
    else
        throw("Invalid dimension.")
    end
end

function bb_multiblock_time(dataset::Union{AbstractVTKMultiblockData, AbstractTimeSeriesVTKData})
    _bbs = [bb(block) for block in dataset]
    min_x = minimum(i -> _bbs[i][1], 1:length(_bbs))
    max_x = maximum(i -> _bbs[i][2], 1:length(_bbs))
    min_y = minimum(i -> _bbs[i][3], 1:length(_bbs))
    max_y = maximum(i -> _bbs[i][4], 1:length(_bbs))

    _first = true
    min_z = max_z = 0
    for _bb in _bbs
        if length(_bb) == 6
            if _first
                min_z = _bb[5]
                max_z = _bb[6]
                _first = false
            end
            if _bb[5] < min_z
                min_z = _bb[5]
            end
            if _bb[5] > max_z
                max_z = _bb[6]
            end
        end
    end

    if min_z == max_z == 0
        return (min_x, max_x, min_y, max_y)
    else
        return (min_x, max_x, min_y, max_y, min_z, max_z)
    end
end

function pseudo_center(dataset::T) where {T<:AbstractVTKData}
    _bb = bb(dataset)
    return ntuple(i -> (_bb[2i-1] + _bb[2i])/2, Val(length(_bb)÷2))
end


text:
# Given a container `net`, propagates the input `x` through all layers in `net`.
# Returns the final output of the network.
function forward(net, x)
    for layer in net
        x = fprop!(layer, x)
    end
    return x
end

# Given a container `net`, back-propagates the error-signal ∂y through all layers
# in `net` in reverse order. ∂y is typically a vector filled with 1/N.
# Returns the gradient of the output wrt. the input(s).
function backward(net, ∂y)
    for layer in reverse(net)
        ∂y = bprop!(layer, ∂y)
    end
    return ∂y
end

# Sets all gradients of a given layer to zero.
function zero_grads!(layer)
    for ∂θ in grads(layer)
        ∂θ[:] = 0
    end
end

# Calls `update_fn` on all (parameter, gradient) pairs in the container `net`.
function update_grads!(net, update_fn)
    for layer in net
        map(update_fn, params(layer), grads(layer))
    end
end

# Does a full batch of training: forward, backward, update.
function train_batch(net, crit, xb, yb; λ=0.01)
    target!(crit, yb)
    map(zero_grads!, net)

    # Forward pass
    pred = forward(net, xb)
    costs = fprop!(crit, pred)
    # Or alternatively, the following works just as well:
    # costs = forward([net ; crit], xb)

    # Backward pass
    # ∂pred = bprop!(crit, 1/size(xb, 1))
    ∂pred = bprop!(crit, ones(costs) ./ size(xb, 1))
    ∂x = backward(net, ∂pred)
    # Again, the following works just as well:
    # ∂x = backward([net ; crit], ones(costs) ./ size(xb, 1))

    # Update the parameters using the computed gradients, via SGD.
    update_grads!(net, (θ, ∂θ) -> sgd!(θ, ∂θ, λ))

    return mean(costs)
end

# Updates the parameters `θ` in a single step of SGD using their gradients `∂θ`
# and the learning-rate (update ratio) `λ`.
function sgd!(θ, ∂θ, λ)
    θ[:] = θ - λ*∂θ
end

# Loads train/valid/test split of data used for the exercise.
# Also pre-processes it by converting to float and dividing by 255 to get into [0,1] range.
function loadMNIST(prefix, T::Type)
    Xtr = readdlm("$prefix-train-data.csv", T)
    ytr = readdlm("$prefix-train-probas.csv", T)
    Xva = readdlm("$prefix-valid-data.csv", T)
    yva = readdlm("$prefix-valid-probas.csv", T)
    Xte = readdlm("$prefix-test-data.csv", T)
    yte = readdlm("$prefix-test-probas.csv", T)

    return (Xtr/255-0.5, ytr), (Xva/255-0.5, yva), (Xte/255-0.5, yte)
end

ext: jl


text:
export RecordOptions
export RecordAction
export RecordGroup, RecordEvery
export RecordChange, RecordCost, RecordIterate, RecordIteration
export RecordEntry, RecordEntryChange
export get_record, has_record
export RecordActionFactory, RecordFactory

#
#
# record Options Decorator
#
#
@doc raw"""
    RecordAction

A `RecordAction` is a small functor to record values.
The usual call is given by `(p,o,i) -> s` that performs the record based on
a [`Problem`](@ref) `p`, [`Options`](@ref) `o` and the current iterate `i`.

By convention `i<=0` is interpreted as "For Initialization only", i.e. only
initialize internal values, but not trigger any record, the same holds for
`i=typemin(Inf)` which is used to indicate `stop`, i.e. that the record is
called from within [`stop_solver!`](@ref) which returns true afterwards.

# Fields (assumed by subtypes to exist)
* `recorded_values` an `Array` of the recorded values.
"""
abstract type RecordAction <: AbstractOptionsAction end

@doc raw"""
    RecordOptions <: Options

append to any [`Options`](@ref) the decorator with record functionality,
Internally a `Dict`ionary is kept that stores a [`RecordAction`](@ref) for
several occasions using a `Symbol` as reference.
The default occasion is `:All` and for example solvers join this field with
`:Start`, `:Step` and `:Stop` at the beginning, every iteration or the
end of the algorithm, respectively

The original options can still be accessed using the [`get_options`](@ref) function.

# Fields
* `options` – the options that are extended by debug information
* `recordDictionary` – a `Dict{Symbol,RecordAction}` to keep track of all
  different recorded values

# Constructors
    RecordOptions(o,dR)

construct record decorated [`Options`](@ref), where `dR` can be

* a [`RecordAction`](@ref), then it is stored within the dictionary at `:All`
* an `Array` of [`RecordAction`](@ref)s, then it is stored as a
  `recordDictionary`(@ref) within the dictionary at `:All`.
* a `Dict{Symbol,RecordAction}`.
"""
mutable struct RecordOptions{O<:Options,TRD<:NamedTuple} <: Options
    options::O
    recordDictionary::TRD
    function RecordOptions{O}(o::O; kwargs...) where {O<:Options}
        return new{O,typeof(values(kwargs))}(o, values(kwargs))
    end
end
function RecordOptions(o::O, dR::D) where {O<:Options,D<:RecordAction}
    return RecordOptions{O}(o; All=dR)
end
function RecordOptions(o::O, dR::Array{<:RecordAction,1}) where {O<:Options}
    return RecordOptions{O}(o; All=RecordGroup(dR))
end
function RecordOptions(o::O, dR::Dict{Symbol,<:RecordAction}) where {O<:Options}
    return RecordOptions{O}(o; dR...)
end
function RecordOptions(o::O, format::Vector{<:Any}) where {O<:Options}
    return RecordOptions{O}(o; RecordFactory(get_options(o), format)...)
end

dispatch_options_decorator(o::RecordOptions) = Val(true)

"""
    has_record(o)

check whether the [`Options`](@ref)` o` are decorated with
[`RecordOptions`](@ref)
"""
has_record(o::RecordOptions) = true
has_record(o::Options) = has_record(o, dispatch_options_decorator(o))
has_record(o::Options, ::Val{true}) = has_record(o.options)
has_record(o::Options, ::Val{false}) = false

# default - stored in the recorded_values field of the RecordAction
@doc raw"""
    get_record(o[,s=:Step])

return the recorded values from within the [`RecordOptions`](@ref) `o` that where
recorded with respect to the `Symbol s` as an `Array`. The default refers to
any recordings during an Iteration represented by the Symbol `:Step`
"""
function get_record(o::RecordOptions, s::Symbol=:Step)
    if haskey(o.recordDictionary, s)
        return get_record(o.recordDictionary[s])
    elseif haskey(o.recordDictionary, :All)
        return get_record(o.recordDictionary[:All])
    else
        error("No record known for key found, since neither :$s nor :All are present.")
    end
end
get_record(o::Options, s::Symbol=:Step) = get_record(o, s, dispatch_options_decorator(o))
get_record(o::Options, s, ::Val{true}) = get_record(o.options, s)
get_record(o::Options, s, ::Val{false}) = error("No Record decoration found")

@doc raw"""
    get_record(r)

return the recorded values stored within a [`RecordAction`](@ref) `r`.
"""
get_record(r::R) where {R<:RecordAction} = r.recorded_values

"""
    record_or_reset!(r,v,i)

either record (`i>0` and not `Inf`) the value `v` within the [`RecordAction`](@ref) `r`
or reset (`i<0`) the internal storage, where `v` has to match the internal
value type of the corresponding Recordaction.
"""
function record_or_reset!(r::R, v, i::Int) where {R<:RecordAction}
    if i > 0
        push!(r.recorded_values, deepcopy(v))
    elseif i < 0 && i > typemin(Int) # reset if negative but not stop indication
        r.recorded_values = Array{typeof(v),1}()
    end
end
"""
    RecordGroup <: RecordAction

group a set of [`RecordAction`](@ref)s into one action, where the internal prints
are removed by default and the resulting strings are concatenated

# Constructor
    RecordGroup(g)

construct a group consisting of an Array of [`RecordAction`](@ref)s `g`,
that are recording `en bloque`; the method does not perform any record itself,
but keeps an array of records. Accessing these yields a `Tuple` of the recorded
values per iteration
"""
mutable struct RecordGroup <: RecordAction
    group::Array{RecordAction,1}
    RecordGroup(g::Array{<:RecordAction,1}) = new(g)
    RecordGroup() = new(Array{RecordAction,1}())
end
function (d::RecordGroup)(p::P, o::O, i::Int) where {P<:Problem,O<:Options}
    for ri in d.group
        ri(p, o, i)
    end
end
get_record(r::RecordGroup) = [zip(get_record.(r.group)...)...]

@doc raw"""
    RecordEvery <: RecordAction

record only every $i$th iteration.
Otherwise (optionally, but activated by default) just update internal tracking
values.

This method does not perform any record itself but relies on it's childrens methods
"""
mutable struct RecordEvery <: RecordAction
    record::RecordAction
    every::Int
    alwaysUpdate::Bool
    function RecordEvery(r::RecordAction, every::Int=1, alwaysUpdate::Bool=true)
        return new(r, every, alwaysUpdate)
    end
end
function (d::RecordEvery)(p::P, o::O, i::Int) where {P<:Problem,O<:Options}
    if i <= 0
        d.record(p, o, i)
    elseif (rem(i, d.every) == 0)
        d.record(p, o, i)
    elseif d.alwaysUpdate
        d.record(p, o, 0)
    end
end
get_record(r::RecordEvery) = get_record(r.record)
#
# Special single ones
#
@doc raw"""
    RecordChange <: RecordAction

debug for the amount of change of the iterate (stored in `o.x` of the [`Options`](@ref))
during the last iteration.

# Additional Fields
* `storage` a [`StoreOptionsAction`](@ref) to store (at least) `o.x` to use this
  as the last value (to compute the change)
"""
mutable struct RecordChange <: RecordAction
    recorded_values::Array{Float64,1}
    storage::StoreOptionsAction
    function RecordChange(a::StoreOptionsAction=StoreOptionsAction((:x,)))
        return new(Array{Float64,1}(), a)
    end
    function RecordChange(x0, a::StoreOptionsAction=StoreOptionsAction((:x,)))
        update_storage!(a, Dict(:x => x0))
        return new(Array{Float64,1}(), a)
    end
end
function (r::RecordChange)(p::P, o::O, i::Int) where {P<:Problem,O<:Options}
    record_or_reset!(
        r,
        has_storage(r.storage, :x) ? distance(p.M, o.x, get_storage(r.storage, :x)) : 0.0,
        i,
    )
    r.storage(p, o, i)
    return r.recorded_values
end

@doc raw"""
    RecordEntry{T} <: RecordAction

record a certain fields entry of type {T} during the iterates

# Fields
* `recorded_values` – the recorded Iterates
* `field` – Symbol the entry can be accessed with within [`Options`](@ref)

"""
mutable struct RecordEntry{T} <: RecordAction
    recorded_values::Array{T,1}
    field::Symbol
    RecordEntry{T}(f::Symbol) where {T} = new(Array{T,1}(), f)
end
RecordEntry(e::T, f::Symbol) where {T} = RecordEntry{T}(f)
RecordEntry(d::DataType, f::Symbol) = RecordEntry{d}(f)
function (r::RecordEntry{T})(p::Pr, o::O, i::Int) where {T,Pr<:Problem,O<:Options}
    return record_or_reset!(r, getfield(o, r.field), i)
end

@doc raw"""
    RecordEntryChange{T} <: RecordAction

record a certain entries change during iterates

# Additional Fields
* `recorded_values` – the recorded Iterates
* `field` – Symbol the field can be accessed with within [`Options`](@ref)
* `distance` – function (p,o,x1,x2) to compute the change/distance between two values of the entry
* `storage` – a [`StoreOptionsAction`](@ref) to store (at least) `getproperty(o, d.field)`
"""
mutable struct RecordEntryChange <: RecordAction
    recorded_values::Vector{Float64}
    field::Symbol
    distance::Any
    storage::StoreOptionsAction
    function RecordEntryChange(f::Symbol, d, a::StoreOptionsAction=StoreOptionsAction((f,)))
        return new(Float64[], f, d, a)
    end
    function RecordEntryChange(
        v::T where {T}, f::Symbol, d, a::StoreOptionsAction=StoreOptionsAction((f,))
    )
        update_storage!(a, Dict(f => v))
        return new(Float64[], f, d, a)
    end
end
function (r::RecordEntryChange)(p::P, o::O, i::Int) where {P<:Problem,O<:Options}
    record_or_reset!(
        r,
        if has_storage(r.storage, r.field)
            r.distance(p, o, getfield(o, r.field), get_storage(r.storage, r.field))
        else
            0.0
        end,
        i,
    )
    return r.storage(p, o, i)
end

@doc raw"""
    RecordIterate <: RecordAction

record the iterate

# Constructors
    RecordIterate(x0)

initialize the iterate record array to the type of `x0`, e.g. your initial data.

    RecordIterate(P)

initialize the iterate record array to the data type `T`.
"""
mutable struct RecordIterate{T} <: RecordAction
    recorded_values::Array{T,1}
    RecordIterate{T}() where {T} = new(Array{T,1}())
end
RecordIterate(::T) where {T} = RecordIterate{T}()
function RecordIterate()
    return throw(ErrorException("The iterate's data type has to be provided, i.e. RecordIterate(x0)."))
end

function (r::RecordIterate{T})(::Problem, o::Options, i) where {T}
    return record_or_reset!(r, o.x, i)
end

@doc raw"""
    RecordIteration <: RecordAction

record the current iteration
"""
mutable struct RecordIteration <: RecordAction
    recorded_values::Array{Int,1}
    RecordIteration() = new(Array{Int,1}())
end
function (r::RecordIteration)(p::P, o::O, i::Int) where {P<:Problem,O<:Options}
    return record_or_reset!(r, i, i)
end

@doc raw"""
    RecordCost <: RecordAction

record the current cost function value, see [`get_cost`](@ref).
"""
mutable struct RecordCost <: RecordAction
    recorded_values::Array{Float64,1}
    RecordCost() = new(Array{Float64,1}())
end
function (r::RecordCost)(p::P, o::O, i::Int) where {P<:Problem,O<:Options}
    return record_or_reset!(r, get_cost(p, o.x), i)
end

@doc raw"""
    RecordFactory(a)

given an array of `Symbol`s and [`RecordAction`](@ref)s and `Ints`

* The symbol `:Cost` creates a [`RecordCost`](@ref)
* The symbol `:iteration` creates a [`RecordIteration`](@ref)
* The symbol `:Change` creates a [`RecordChange`](@ref)
* any other symbol creates a [`RecordEntry`](@ref) of the corresponding field in [`Options`](@ref)
* any [`RecordAction`](@ref) is directly included
* an Integer `k` introduces that record is only performed every `k`th iteration
"""
function RecordFactory(o::O, a::Array{<:Any,1}) where {O<:Options}
    # filter out every
    group = Array{RecordAction,1}()
    for s in filter(x -> !isa(x, Int), a) # filter ints and stop
        push!(group, RecordActionFactory(o, s))
    end
    record = RecordGroup(group)
    # filter ints
    e = filter(x -> isa(x, Int), a)
    if length(e) > 0
        record = RecordEvery(record, last(e))
    end
    return (; All=record)
end
@doc raw"""
    RecordActionFactory(s)

create a [`RecordAction`](@ref) where

* a [`RecordAction`](@ref) is passed through
* a [`Symbol`] creates [`RecordEntry`](@ref) of that symbol, with the exceptions
  of `:Change`, `:Iterate`, `:Iteration`, and `:Cost`.
"""
RecordActionFactory(o::O, a::A) where {O<:Options,A<:RecordAction} = a
function RecordActionFactory(o::O, s::Symbol) where {O<:Options}
    if (s == :Change)
        return RecordChange()
    elseif (s == :Iteration)
        return RecordIteration()
    elseif (s == :Iterate)
        return RecordIterate(o.x)
    elseif (s == :Cost)
        return RecordCost()
    end
    return RecordEntry(getfield(o, s), s)
end


text:
using Test
using LinearAlgebra
using StaticArrays
using ArnoldiMethod: swap11!, swap12!, swap21!, swap22!, rotate_right!, eigenvalues,
                     sylvsystem, CompletePivoting

# These tests are only necessary in real arithmetic, but why not do complex for completeness

@testset "Reordering the Schur form 1 ↔ 1 ($T)" for T in (Float64, ComplexF64)
    Q1 = Matrix{T}(I, 2, 2)
    R1 = triu(rand(T, 2, 2))
    Q2 = copy(Q1)
    R2 = copy(R1)

    swap11!(R2, 1, Q2)

    @test R2[1,1] ≈ R1[2,2]
    @test R1[1,1] ≈ R2[2,2]
    @test R1 * Q2 ≈ Q2 * R2
end

@testset "Reordering the Schur form 1 ↔ 2 ($T)" for T in (Float64, ComplexF64)
    # x x x
    # . x x
    # . x x
    Q1 = Matrix{T}(I, 3, 3)
    R1 = triu(rand(T, 3, 3))
    R1[3,2] = rand(T)

    Q2 = copy(Q1)
    R2 = copy(R1)

    swap12!(R2, 1, Q2)

    @test iszero(R2[3,1])
    @test iszero(R2[3,2])
    @test R1[1,1] ≈ R2[3,3]
    @test sort!(eigvals(R1[2:3,2:3]), by = reim) ≈ sort!(eigvals(R2[1:2,1:2]), by = reim)
    @test R1 * Q2 ≈ Q2 * R2
end

@testset "Reordering the Schur form 2 ↔ 1 ($T)" for T in (Float64, ComplexF64)
    # x x x
    # x x x
    # . . x
    Q1 = Matrix{T}(I, 3, 3)
    R1 = triu(rand(T, 3, 3))
    R1[2,1] = rand(T)

    Q2 = copy(Q1)
    R2 = copy(R1)

    swap21!(R2, 1, Q2)

    @test iszero(R2[2,1])
    @test iszero(R2[3,1])
    @test R1[3,3] ≈ R2[1,1]
    @test sort!(eigvals(R1[1:2,1:2]), by = reim) ≈ sort!(eigvals(R2[2:3,2:3]), by = reim)
    @test R1 * Q2 ≈ Q2 * R2
end

@testset "Reordering the Schur form 2 ↔ 2 ($T)" for T in (Float64, ComplexF64)
    # x x x x
    # x x x x
    # . . x x
    # . . x x
    Q1 = Matrix{T}(I, 4, 4)
    R1 = triu(rand(T, 4, 4))
    R1[2,1] = rand(T)
    R1[4,3] = rand(T)

    Q2 = copy(Q1)
    R2 = copy(R1)

    swap22!(R2, 1, Q2)

    @test iszero(R2[3,1])
    @test iszero(R2[4,1])
    @test iszero(R2[3,2])
    @test iszero(R2[4,2])
    @test sort!(eigvals(R1[1:2,1:2]), by = reim) ≈ sort!(eigvals(R2[3:4,3:4]), by = reim)
    @test sort!(eigvals(R1[3:4,3:4]), by = reim) ≈ sort!(eigvals(R2[1:2,1:2]), by = reim)
    @test R1 * Q2 ≈ Q2 * R2
end

@testset "Rotation right with single eigenvalues" begin
    # 10 × 10 quasi upper triangular matrix with 2 × 2 block on R[4:5,4:5]
    R = triu(rand(10, 10))
    Q = Matrix(1.0I, 10, 10)
    R[4,5] = -2.0; R[5,4] = 2.0

    λ_before = eigenvalues(R)

    # Rotate the last block right before the first one.
    R_after = copy(R)
    rotate_right!(R_after, 1, 10, Q)
    λ_after = eigenvalues(R_after)

    # Test whether Q is more or less a similarity transform
    @test opnorm(R - Q * R_after * Q', 1) < 10eps() * opnorm(R, 1)

    # Test orthonormality of Q
    @test norm(Q'Q - I) < 10eps()

    # Middle guys have been rotated
    for (i, j) = zip(1:10, circshift(1:10, -1))
        @test λ_before[i] ≈ λ_after[j]
    end
end

@testset "Rotation right with two conjugate pairs" begin
    # 10 × 10 quasi upper triangular matrix with 2 × 2 blocks on R[2:3,2:3] and R[6:7,6:7]
    R = triu(rand(10, 10))
    Q = Matrix(1.0I, 10, 10)
    R[3,2] = -2.0; R[2,3] = 2.0
    R[7,6] = 3.0; R[6,7] = -2.0

    λ_before = eigenvalues(R)

    # Rotate the last block right before the first one.
    R_after = copy(R)
    rotate_right!(R_after, 3, 6, Q)

    λ_after = eigenvalues(R_after)

    # Test whether Q is more or less a similarity transform
    @test opnorm(R - Q * R_after * Q', 1) < 10eps() * opnorm(R, 1)

    # Test orthonormality of Q
    @test norm(Q'Q - I) < 10eps()

    # First eigenvalue should be exactly equal
    @test λ_before[1] == λ_after[1]

    # Middle guys have been rotated
    for (i, j) = zip(2:7, circshift(2:7, -2))
        @test λ_before[i] ≈ λ_after[j]
    end

    # Last eigenvalues should be exactly equal
    @test λ_before[8:10] == λ_after[8:10]
end

@testset "Rotation right with one 2 × 2 block on the right" begin
    # 10 × 10 quasi upper triangular matrix with 2 × 2 block on R[6:7,6:7]
    R = triu(rand(10, 10))
    Q = Matrix(1.0I, 10, 10)
    R[6,7] = -2.0; R[7,6] = 2.0

    λ_before = eigenvalues(R)

    # Rotate the last block right before the first one.
    R_after = copy(R)
    rotate_right!(R_after, 2, 6, Q)
    λ_after = eigenvalues(R_after)

    # Test whether Q is more or less a similarity transform
    @test opnorm(R - Q * R_after * Q', 1) < 10eps() * opnorm(R, 1)

    # Test orthonormality of Q
    @test norm(Q'Q - I) < 10eps()

    # First eigenvalue should be exactly equal
    @test λ_before[1] == λ_after[1]

    # Middle guys have been rotated
    for (i, j) = zip(2:7, circshift(2:7, -2))
        @test λ_before[i] ≈ λ_after[j]
    end

    # Last eigenvalues should be exactly equal
    for i = 8:10
        @test λ_before[i] == λ_after[i]
    end

end

@testset "Rotation right with one 2 × 2 block on the left" begin
    # 10 × 10 quasi upper triangular matrix with 2 × 2 block on R[2:3,2:3]
    R = triu(rand(10, 10))
    Q = Matrix(1.0I, 10, 10)
    R[2,3] = -2.0; R[2,3] = 2.0

    λ_before = eigenvalues(R)

    # Rotate the last block right before the first one.
    R_after = copy(R)
    rotate_right!(R_after, 2, 6, Q)
    λ_after = eigenvalues(R_after)

    # Test whether Q is more or less a similarity transform
    @test opnorm(R - Q * R_after * Q', 1) < 10eps() * opnorm(R, 1)

    # Test orthonormality of Q
    @test opnorm(Q'Q - I, 1) < 10 * eps()

    # First eigenvalue should be exactly equal
    @test λ_before[1] == λ_after[1]

    # Middle guys have been rotated
    for (i, j) = zip(2:6, circshift(2:6, -1))
        @test λ_before[i] ≈ λ_after[j]
    end

    # Last eigenvalues should be exactly equal
    for i = 7:10
        @test λ_before[i] == λ_after[i]
    end

end

# Stewart's example in Bai & Demmel's article
# This basically shows that a direct method is preferred over QR iterations, because of
# forward instabilities of the QR method -- also the reason why I switched away from
# Sorensen's implicit restart and convoluted locking + purging strategies that were
# necessary exactly because of this.
@testset "Stewart's example" begin
    A(τ) = [7.0010 -87.0000  39.4000τ  22.2000τ;
            5.0000   7.0010 -12.2000τ  36.0000τ;
            0.0000   0.0000   7.0100  -11.7567 ;
            0.0000   0.0000   37.0000    7.0100 ]

    for τ in (1, 10, 100)
        B = A(τ)

        # Eigenvalues do not depend on τ, but we just compute them here for ease.
        λs_before = eigenvalues(B)
        swap22!(B, 1)
        λs_after = eigenvalues(B)

        # Test swapping is approximately equal
        @test abs(λs_before[1]) ≈ abs(λs_after[3])
        @test abs(λs_before[3]) ≈ abs(λs_after[1])
    end
end

# Example taken from Bai & Demmel
@testset "Small eigenvalue separation" begin
    # This should result in a very ill-conditioned Sylvester equation.
    A = [ 1.00 -100.0   400.000 -1000.000;
          0.01    1.0  1200.000   -10.000;
          0.00    0.0     1.0+eps()    -0.010;
          0.00    0.0   100.000     1.0+eps()]

    A′ = copy(A)
    Q = Matrix(1.0I, 4, 4)
    λs_before = eigenvalues(A)
    swap22!(A′, 1, Q)
    λs_after = eigenvalues(A)
    @test abs(λs_before[1]) ≈ abs(λs_after[3])
    @test abs(λs_before[3]) ≈ abs(λs_after[1])
    @test opnorm(I - Q'Q, 1) < 10eps() # we should be able to get rid of this prefactor?
    @test opnorm(A * Q - Q * A′, 1) < opnorm(A, 1) * eps()
end

@testset "Identical eigenvalues should not blow up" begin
    A = [1.0 2.0 3.0 4.0;
         0.0 1.0 5.0 6.0;
         0.0 0.0 1.0 7.0;
         0.0 0.0 0.0 1.0]

    A′ = copy(A)
    swap22!(A′, 1)
    @test A == A′
    swap12!(A′, 1)
    @test A == A′
    swap21!(A′, 1)
    @test A == A′
end


text:
using Documenter, StatsModels

makedocs(
    sitename = "StatsModels.jl",
    pages = [
        "Introduction" => "index.md",
        "Modeling tabular data" => "formula.md",
        "Internals and extending the `@formula`" => "internals.md",
        "Contrast coding categorical variables" => "contrasts.md",
        "Temporal variables and Time Series Terms" => "temporal_terms.md",
        "API documentation" => "api.md"
    ]
)

deploydocs(
    repo = "github.com/JuliaStats/StatsModels.jl.git"
)


text:
using Persa
using Test
using DataFrames
using EvaluationCF

function createdummydataset()
    df = DataFrame()
    df[!, :user] = [
        1,
        1,
        1,
        1,
        1,
        1,
        2,
        2,
        2,
        2,
        2,
        2,
        3,
        3,
        3,
        3,
        4,
        4,
        4,
        4,
        4,
        5,
        5,
        5,
        5,
        5,
        5,
        6,
        6,
        6,
    ]
    df[!, :item] = [
        1,
        2,
        3,
        4,
        5,
        6,
        1,
        2,
        3,
        4,
        5,
        6,
        1,
        2,
        3,
        4,
        2,
        3,
        4,
        5,
        6,
        1,
        2,
        3,
        4,
        5,
        6,
        1,
        2,
        4,
    ]
    df[!, :rating] = [
        2.5,
        3.5,
        3.0,
        3.5,
        2.5,
        3.0,
        3,
        3.5,
        1.5,
        5,
        3,
        3.5,
        2.5,
        3.0,
        3.5,
        4.0,
        3.5,
        3.0,
        4.0,
        2.5,
        4.5,
        3.0,
        4.0,
        2.0,
        3.0,
        2.0,
        3.0,
        3.0,
        4.0,
        5.0,
    ]
    return df
end
####

dataframe = createdummydataset()
dataset = Persa.Dataset(dataframe)

@testset "Resampling Tests" begin
    @testset "Holdout" begin
        margin = 0.5

        ds_train, ds_test = EvaluationCF.HoldOut(dataset; margin = margin)[0]

        @test length(ds_train) == length(dataset) * margin
        @test length(ds_test) == length(dataset) * (1 - margin)
    end

    @testset "Holdout" begin
        k = 10

        folds = EvaluationCF.KFolds(dataset; k = k)

        i = 0

        for (ds_train, ds_test) in folds
            i = i + 1
            @test length(ds_train) == length(dataset) * (1 - (1 / k))
            @test length(ds_test) == length(dataset) * (1 / k)
        end

        @test i == k
    end
end

@testset "Metrics Tests" begin
    struct DatasetModel{T} <: Persa.Model{T}
        dataset::Persa.Dataset{T}
        preference::Persa.Preference{T}
        users::Int
        items::Int
    end

    DatasetModel(dataset::Persa.Dataset) = DatasetModel(
        dataset,
        dataset.preference,
        Persa.users(dataset),
        Persa.items(dataset),
    )

    Persa.predict(model::Persa.Model, user::Int, item::Int) = Persa.value(dataset[user, item])

    model = DatasetModel(dataset)

    @test EvaluationCF.mae(model, dataset) == 0
    @test EvaluationCF.rmse(model, dataset) == 0
    @test EvaluationCF.coverage(model, dataset) == 1
end


text:
using OpenVX

# FIXME: There's a problem with channel combine node that causes periodic artifacts
# in the output image. After some testing and checking against reference C API
# examples this looks to be a bug in AMD's implementation.

include("imageio.jl")

"""
Creates a graph with one input image and one output image.
You supply the input and output images, it is assumed that the input and output
images are RGB.
"""
function make_filter_graph(ctx, input::VX.vx_image, output::VX.vx_image, width, height)

    num_virt = 6 # number of virtual UInt8/grayscale images that we need

    graph = VX.CreateGraph(ctx)

    virtu8 = Vector{Ptr{Cvoid}}(undef, num_virt)
    nodes = Vector{Ptr{Cvoid}}(undef, 7)
    for i in 1:num_virt
        virtu8[i] = VX.CreateVirtualImage(graph, width, height, VX.DF_IMAGE_U8)
    end

    # Do custom filtering on the input image
    # First, extract R, G, and B channels to individual virtual images
    nodes[1] = VX.ChannelExtractNode(graph, input, VX.CHANNEL_R, virtu8[1])
    nodes[2] = VX.ChannelExtractNode(graph, input, VX.CHANNEL_G, virtu8[2])
    nodes[3] = VX.ChannelExtractNode(graph, input, VX.CHANNEL_B, virtu8[3])

    # Now, run box filter on each of gray scale images
    for i in 1:3
        nodes[3+i] = VX.Gaussian3x3Node(graph, virtu8[i], virtu8[i + 3])
    end

    # Now combine images together in the output color image
    nodes[7] = VX.ChannelCombineNode(graph, virtu8[4], virtu8[5], virtu8[6], C_NULL, output)

    for i in 1:num_virt
        VX.ReleaseImage(Ref(virtu8[i]))
    end

    for i in 1:length(nodes)
        VX.ReleaseNode(Ref(nodes[i]))
    end

    return graph
end

# Main

ctx = VX.CreateContext()

filename = "assets/airplane.bmp"
width = 1200
height = 1200

image = image_from_file(filename, width, height)
output = VX.CreateImage(ctx, width, height, VX.DF_IMAGE_RGB)
graph = make_filter_graph(ctx, image, output, width, height)
VX.VerifyGraph(graph)
VX.ProcessGraph(graph)
file_from_image(output, "airplane_gauss.bmp", width, height)


text:
module InkLocalizer

using JSON
using JSONPointer
using OrderedCollections

# Any human readable words
const REG_WORD = r"[a-z|A-Z|ㄱ-ㅎ|ㅏ-ㅣ|가-힣]"

function parse_ink(file)
    s = read(file, String)
    # Remove BOM(Byte Order Mars)
    JSON.parse(chop(s, head=1, tail=0); dicttype = OrderedDict)
end

function localize(file)
    function _localize!(arr::AbstractArray, token)
        for (i, row) in enumerate(arr)
            _localize!(row, vcat(token, i))
        end
    end
    function _localize!(dict::AbstractDict, token)
        for (k, v) in dict
            _localize!(v, vcat(token, k))
        end
    end
    function _localize!(sentence::AbstractString, token)
        # All ink words starts with ^
        if startswith(sentence, "^")
            if occursin(REG_WORD, sentence)
                push!(lokalise_data, [token, sentence[2:end]])
            end
        end
    end
    function _localize!(x, key)
        nothing
    end

    # localise
    lokalise_data = Any[]

    data = parse_ink(file)
    _localize!(data["root"], [])

    # save it with localise key
    prefix = "\$" * basename(file)

    for (i, row) in enumerate(lokalise_data)
        p = JSONPointer.Pointer("/root/"* join(row[1], "/"))
        finalkey = ink_localkey(prefix, row[1], i)
        row[1] = finalkey

        # replace text with localize key
        data[p] = "^$finalkey"
    end

    return data, OrderedDict(lokalise_data)
end

function ink_localkey(prefix, tokens, suffix)
    tokens = filter(el -> isa(el, AbstractString), tokens)
    tokens = filter(el -> occursin(REG_WORD, el), tokens)

    return join((prefix, join(tokens, "/"), suffix), ".")
end

end # module


text:
"""
    Potential

Supertype for all kinds of interaction potentials.
"""
abstract type Potential end

"""
    Continuous <: Potential

Type for all potentials that implement an potential energy
and a force function. For example, the Lennard-Jones potential.
"""
abstract type Continuous <: Potential end

"""
    Discrete <: Potential

Type for all potentials that only implement a potential energy, due
to its discontinuous nature. For example, the hard-sphere potential.
"""
abstract type Discrete <: Potential end


text:
#TODO: change all the grey -> gray
const xkcd_rgb = Dict(
    "acid green"=> "#8ffe09",
    "adobe"=> "#bd6c48",
    "algae"=> "#54ac68",
    "algae green"=> "#21c36f",
    "almost black"=> "#070d0d",
    "amber"=> "#feb308",
    "amethyst"=> "#9b5fc0",
    "apple"=> "#6ecb3c",
    "apple green"=> "#76cd26",
    "apricot"=> "#ffb16d",
    "aqua"=> "#13eac9",
    "aqua blue"=> "#02d8e9",
    "aqua green"=> "#12e193",
    "aqua marine"=> "#2ee8bb",
    "aquamarine"=> "#04d8b2",
    "army green"=> "#4b5d16",
    "asparagus"=> "#77ab56",
    "aubergine"=> "#3d0734",
    "auburn"=> "#9a3001",
    "avocado"=> "#90b134",
    "avocado green"=> "#87a922",
    "azul"=> "#1d5dec",
    "azure"=> "#069af3",
    "baby blue"=> "#a2cffe",
    "baby green"=> "#8cff9e",
    "baby pink"=> "#ffb7ce",
    "baby poo"=> "#ab9004",
    "baby poop"=> "#937c00",
    "baby poop green"=> "#8f9805",
    "baby puke green"=> "#b6c406",
    "baby purple"=> "#ca9bf7",
    "baby shit brown"=> "#ad900d",
    "baby shit green"=> "#889717",
    "banana"=> "#ffff7e",
    "banana yellow"=> "#fafe4b",
    "barbie pink"=> "#fe46a5",
    "barf green"=> "#94ac02",
    "barney"=> "#ac1db8",
    "barney purple"=> "#a00498",
    "battleship grey"=> "#6b7c85",
    "beige"=> "#e6daa6",
    "berry"=> "#990f4b",
    "bile"=> "#b5c306",
    "black"=> "#000000",
    "bland"=> "#afa88b",
    "blood"=> "#770001",
    "blood orange"=> "#fe4b03",
    "blood red"=> "#980002",
    "blue"=> "#0343df",
    "blue blue"=> "#2242c7",
    "blue green"=> "#137e6d",
    "blue grey"=> "#607c8e",
    "blue purple"=> "#5729ce",
    "blue violet"=> "#5d06e9",
    "blue with a hint of purple"=> "#533cc6",
    "blue/green"=> "#0f9b8e",
    "blue/grey"=> "#758da3",
    "blue/purple"=> "#5a06ef",
    "blueberry"=> "#464196",
    "bluegreen"=> "#017a79",
    "bluegrey"=> "#85a3b2",
    "bluey green"=> "#2bb179",
    "bluey grey"=> "#89a0b0",
    "bluey purple"=> "#6241c7",
    "bluish"=> "#2976bb",
    "bluish green"=> "#10a674",
    "bluish grey"=> "#748b97",
    "bluish purple"=> "#703be7",
    "blurple"=> "#5539cc",
    "blush"=> "#f29e8e",
    "blush pink"=> "#fe828c",
    "booger"=> "#9bb53c",
    "booger green"=> "#96b403",
    "bordeaux"=> "#7b002c",
    "boring green"=> "#63b365",
    "bottle green"=> "#044a05",
    "brick"=> "#a03623",
    "brick orange"=> "#c14a09",
    "brick red"=> "#8f1402",
    "bright aqua"=> "#0bf9ea",
    "bright blue"=> "#0165fc",
    "bright cyan"=> "#41fdfe",
    "bright green"=> "#01ff07",
    "bright lavender"=> "#c760ff",
    "bright light blue"=> "#26f7fd",
    "bright light green"=> "#2dfe54",
    "bright lilac"=> "#c95efb",
    "bright lime"=> "#87fd05",
    "bright lime green"=> "#65fe08",
    "bright magenta"=> "#ff08e8",
    "bright olive"=> "#9cbb04",
    "bright orange"=> "#ff5b00",
    "bright pink"=> "#fe01b1",
    "bright purple"=> "#be03fd",
    "bright red"=> "#ff000d",
    "bright sea green"=> "#05ffa6",
    "bright sky blue"=> "#02ccfe",
    "bright teal"=> "#01f9c6",
    "bright turquoise"=> "#0ffef9",
    "bright violet"=> "#ad0afd",
    "bright yellow"=> "#fffd01",
    "bright yellow green"=> "#9dff00",
    "british racing green"=> "#05480d",
    "bronze"=> "#a87900",
    "brown"=> "#653700",
    "brown green"=> "#706c11",
    "brown grey"=> "#8d8468",
    "brown orange"=> "#b96902",
    "brown red"=> "#922b05",
    "brown yellow"=> "#b29705",
    "brownish"=> "#9c6d57",
    "brownish green"=> "#6a6e09",
    "brownish grey"=> "#86775f",
    "brownish orange"=> "#cb7723",
    "brownish pink"=> "#c27e79",
    "brownish purple"=> "#76424e",
    "brownish red"=> "#9e3623",
    "brownish yellow"=> "#c9b003",
    "browny green"=> "#6f6c0a",
    "browny orange"=> "#ca6b02",
    "bruise"=> "#7e4071",
    "bubble gum pink"=> "#ff69af",
    "bubblegum"=> "#ff6cb5",
    "bubblegum pink"=> "#fe83cc",
    "buff"=> "#fef69e",
    "burgundy"=> "#610023",
    "burnt orange"=> "#c04e01",
    "burnt red"=> "#9f2305",
    "burnt siena"=> "#b75203",
    "burnt sienna"=> "#b04e0f",
    "burnt umber"=> "#a0450e",
    "burnt yellow"=> "#d5ab09",
    "burple"=> "#6832e3",
    "butter"=> "#ffff81",
    "butter yellow"=> "#fffd74",
    "butterscotch"=> "#fdb147",
    "cadet blue"=> "#4e7496",
    "camel"=> "#c69f59",
    "camo"=> "#7f8f4e",
    "camo green"=> "#526525",
    "camouflage green"=> "#4b6113",
    "canary"=> "#fdff63",
    "canary yellow"=> "#fffe40",
    "candy pink"=> "#ff63e9",
    "caramel"=> "#af6f09",
    "carmine"=> "#9d0216",
    "carnation"=> "#fd798f",
    "carnation pink"=> "#ff7fa7",
    "carolina blue"=> "#8ab8fe",
    "celadon"=> "#befdb7",
    "celery"=> "#c1fd95",
    "cement"=> "#a5a391",
    "cerise"=> "#de0c62",
    "cerulean"=> "#0485d1",
    "cerulean blue"=> "#056eee",
    "charcoal"=> "#343837",
    "charcoal grey"=> "#3c4142",
    "chartreuse"=> "#c1f80a",
    "cherry"=> "#cf0234",
    "cherry red"=> "#f7022a",
    "chestnut"=> "#742802",
    "chocolate"=> "#3d1c02",
    "chocolate brown"=> "#411900",
    "cinnamon"=> "#ac4f06",
    "claret"=> "#680018",
    "clay"=> "#b66a50",
    "clay brown"=> "#b2713d",
    "clear blue"=> "#247afd",
    "cloudy blue"=> "#acc2d9",
    "cobalt"=> "#1e488f",
    "cobalt blue"=> "#030aa7",
    "cocoa"=> "#875f42",
    "coffee"=> "#a6814c",
    "cool blue"=> "#4984b8",
    "cool green"=> "#33b864",
    "cool grey"=> "#95a3a6",
    "copper"=> "#b66325",
    "coral"=> "#fc5a50",
    "coral pink"=> "#ff6163",
    "cornflower"=> "#6a79f7",
    "cornflower blue"=> "#5170d7",
    "cranberry"=> "#9e003a",
    "cream"=> "#ffffc2",
    "creme"=> "#ffffb6",
    "crimson"=> "#8c000f",
    "custard"=> "#fffd78",
    "cyan"=> "#00ffff",
    "dandelion"=> "#fedf08",
    "dark"=> "#1b2431",
    "dark aqua"=> "#05696b",
    "dark aquamarine"=> "#017371",
    "dark beige"=> "#ac9362",
    "dark blue"=> "#00035b",
    "dark blue green"=> "#005249",
    "dark blue grey"=> "#1f3b4d",
    "dark brown"=> "#341c02",
    "dark coral"=> "#cf524e",
    "dark cream"=> "#fff39a",
    "dark cyan"=> "#0a888a",
    "dark forest green"=> "#002d04",
    "dark fuchsia"=> "#9d0759",
    "dark gold"=> "#b59410",
    "dark grass green"=> "#388004",
    "dark green"=> "#033500",
    "dark green blue"=> "#1f6357",
    "dark grey"=> "#363737",
    "dark grey blue"=> "#29465b",
    "dark hot pink"=> "#d90166",
    "dark indigo"=> "#1f0954",
    "dark khaki"=> "#9b8f55",
    "dark lavender"=> "#856798",
    "dark lilac"=> "#9c6da5",
    "dark lime"=> "#84b701",
    "dark lime green"=> "#7ebd01",
    "dark magenta"=> "#960056",
    "dark maroon"=> "#3c0008",
    "dark mauve"=> "#874c62",
    "dark mint"=> "#48c072",
    "dark mint green"=> "#20c073",
    "dark mustard"=> "#a88905",
    "dark navy"=> "#000435",
    "dark navy blue"=> "#00022e",
    "dark olive"=> "#373e02",
    "dark olive green"=> "#3c4d03",
    "dark orange"=> "#c65102",
    "dark pastel green"=> "#56ae57",
    "dark peach"=> "#de7e5d",
    "dark periwinkle"=> "#665fd1",
    "dark pink"=> "#cb416b",
    "dark plum"=> "#3f012c",
    "dark purple"=> "#35063e",
    "dark red"=> "#840000",
    "dark rose"=> "#b5485d",
    "dark royal blue"=> "#02066f",
    "dark sage"=> "#598556",
    "dark salmon"=> "#c85a53",
    "dark sand"=> "#a88f59",
    "dark sea green"=> "#11875d",
    "dark seafoam"=> "#1fb57a",
    "dark seafoam green"=> "#3eaf76",
    "dark sky blue"=> "#448ee4",
    "dark slate blue"=> "#214761",
    "dark tan"=> "#af884a",
    "dark taupe"=> "#7f684e",
    "dark teal"=> "#014d4e",
    "dark turquoise"=> "#045c5a",
    "dark violet"=> "#34013f",
    "dark yellow"=> "#d5b60a",
    "dark yellow green"=> "#728f02",
    "darkblue"=> "#030764",
    "darkgreen"=> "#054907",
    "darkish blue"=> "#014182",
    "darkish green"=> "#287c37",
    "darkish pink"=> "#da467d",
    "darkish purple"=> "#751973",
    "darkish red"=> "#a90308",
    "deep aqua"=> "#08787f",
    "deep blue"=> "#040273",
    "deep brown"=> "#410200",
    "deep green"=> "#02590f",
    "deep lavender"=> "#8d5eb7",
    "deep lilac"=> "#966ebd",
    "deep magenta"=> "#a0025c",
    "deep orange"=> "#dc4d01",
    "deep pink"=> "#cb0162",
    "deep purple"=> "#36013f",
    "deep red"=> "#9a0200",
    "deep rose"=> "#c74767",
    "deep sea blue"=> "#015482",
    "deep sky blue"=> "#0d75f8",
    "deep teal"=> "#00555a",
    "deep turquoise"=> "#017374",
    "deep violet"=> "#490648",
    "denim"=> "#3b638c",
    "denim blue"=> "#3b5b92",
    "desert"=> "#ccad60",
    "diarrhea"=> "#9f8303",
    "dirt"=> "#8a6e45",
    "dirt brown"=> "#836539",
    "dirty blue"=> "#3f829d",
    "dirty green"=> "#667e2c",
    "dirty orange"=> "#c87606",
    "dirty pink"=> "#ca7b80",
    "dirty purple"=> "#734a65",
    "dirty yellow"=> "#cdc50a",
    "dodger blue"=> "#3e82fc",
    "drab"=> "#828344",
    "drab green"=> "#749551",
    "dried blood"=> "#4b0101",
    "duck egg blue"=> "#c3fbf4",
    "dull blue"=> "#49759c",
    "dull brown"=> "#876e4b",
    "dull green"=> "#74a662",
    "dull orange"=> "#d8863b",
    "dull pink"=> "#d5869d",
    "dull purple"=> "#84597e",
    "dull red"=> "#bb3f3f",
    "dull teal"=> "#5f9e8f",
    "dull yellow"=> "#eedc5b",
    "dusk"=> "#4e5481",
    "dusk blue"=> "#26538d",
    "dusky blue"=> "#475f94",
    "dusky pink"=> "#cc7a8b",
    "dusky purple"=> "#895b7b",
    "dusky rose"=> "#ba6873",
    "dust"=> "#b2996e",
    "dusty blue"=> "#5a86ad",
    "dusty green"=> "#76a973",
    "dusty lavender"=> "#ac86a8",
    "dusty orange"=> "#f0833a",
    "dusty pink"=> "#d58a94",
    "dusty purple"=> "#825f87",
    "dusty red"=> "#b9484e",
    "dusty rose"=> "#c0737a",
    "dusty teal"=> "#4c9085",
    "earth"=> "#a2653e",
    "easter green"=> "#8cfd7e",
    "easter purple"=> "#c071fe",
    "ecru"=> "#feffca",
    "egg shell"=> "#fffcc4",
    "eggplant"=> "#380835",
    "eggplant purple"=> "#430541",
    "eggshell"=> "#ffffd4",
    "eggshell blue"=> "#c4fff7",
    "electric blue"=> "#0652ff",
    "electric green"=> "#21fc0d",
    "electric lime"=> "#a8ff04",
    "electric pink"=> "#ff0490",
    "electric purple"=> "#aa23ff",
    "emerald"=> "#01a049",
    "emerald green"=> "#028f1e",
    "evergreen"=> "#05472a",
    "faded blue"=> "#658cbb",
    "faded green"=> "#7bb274",
    "faded orange"=> "#f0944d",
    "faded pink"=> "#de9dac",
    "faded purple"=> "#916e99",
    "faded red"=> "#d3494e",
    "faded yellow"=> "#feff7f",
    "fawn"=> "#cfaf7b",
    "fern"=> "#63a950",
    "fern green"=> "#548d44",
    "fire engine red"=> "#fe0002",
    "flat blue"=> "#3c73a8",
    "flat green"=> "#699d4c",
    "fluorescent green"=> "#08ff08",
    "fluro green"=> "#0aff02",
    "foam green"=> "#90fda9",
    "forest"=> "#0b5509",
    "forest green"=> "#06470c",
    "forrest green"=> "#154406",
    "french blue"=> "#436bad",
    "fresh green"=> "#69d84f",
    "frog green"=> "#58bc08",
    "fuchsia"=> "#ed0dd9",
    "gold"=> "#dbb40c",
    "golden"=> "#f5bf03",
    "golden brown"=> "#b27a01",
    "golden rod"=> "#f9bc08",
    "golden yellow"=> "#fec615",
    "goldenrod"=> "#fac205",
    "grape"=> "#6c3461",
    "grape purple"=> "#5d1451",
    "grapefruit"=> "#fd5956",
    "grass"=> "#5cac2d",
    "grass green"=> "#3f9b0b",
    "grassy green"=> "#419c03",
    "green"=> "#15b01a",
    "green apple"=> "#5edc1f",
    "green blue"=> "#06b48b",
    "green brown"=> "#544e03",
    "green grey"=> "#77926f",
    "green teal"=> "#0cb577",
    "green yellow"=> "#c9ff27",
    "green/blue"=> "#01c08d",
    "green/yellow"=> "#b5ce08",
    "greenblue"=> "#23c48b",
    "greenish"=> "#40a368",
    "greenish beige"=> "#c9d179",
    "greenish blue"=> "#0b8b87",
    "greenish brown"=> "#696112",
    "greenish cyan"=> "#2afeb7",
    "greenish grey"=> "#96ae8d",
    "greenish tan"=> "#bccb7a",
    "greenish teal"=> "#32bf84",
    "greenish turquoise"=> "#00fbb0",
    "greenish yellow"=> "#cdfd02",
    "greeny blue"=> "#42b395",
    "greeny brown"=> "#696006",
    "greeny grey"=> "#7ea07a",
    "greeny yellow"=> "#c6f808",
    "grey"=> "#929591",
    "grey blue"=> "#6b8ba4",
    "grey brown"=> "#7f7053",
    "grey green"=> "#789b73",
    "grey pink"=> "#c3909b",
    "grey purple"=> "#826d8c",
    "grey teal"=> "#5e9b8a",
    "grey/blue"=> "#647d8e",
    "grey/green"=> "#86a17d",
    "greyblue"=> "#77a1b5",
    "greyish"=> "#a8a495",
    "greyish blue"=> "#5e819d",
    "greyish brown"=> "#7a6a4f",
    "greyish green"=> "#82a67d",
    "greyish pink"=> "#c88d94",
    "greyish purple"=> "#887191",
    "greyish teal"=> "#719f91",
    "gross green"=> "#a0bf16",
    "gunmetal"=> "#536267",
    "hazel"=> "#8e7618",
    "heather"=> "#a484ac",
    "heliotrope"=> "#d94ff5",
    "highlighter green"=> "#1bfc06",
    "hospital green"=> "#9be5aa",
    "hot green"=> "#25ff29",
    "hot magenta"=> "#f504c9",
    "hot pink"=> "#ff028d",
    "hot purple"=> "#cb00f5",
    "hunter green"=> "#0b4008",
    "ice"=> "#d6fffa",
    "ice blue"=> "#d7fffe",
    "icky green"=> "#8fae22",
    "indian red"=> "#850e04",
    "indigo"=> "#380282",
    "indigo blue"=> "#3a18b1",
    "iris"=> "#6258c4",
    "irish green"=> "#019529",
    "ivory"=> "#ffffcb",
    "jade"=> "#1fa774",
    "jade green"=> "#2baf6a",
    "jungle green"=> "#048243",
    "kelley green"=> "#009337",
    "kelly green"=> "#02ab2e",
    "kermit green"=> "#5cb200",
    "key lime"=> "#aeff6e",
    "khaki"=> "#aaa662",
    "khaki green"=> "#728639",
    "kiwi"=> "#9cef43",
    "kiwi green"=> "#8ee53f",
    "lavender"=> "#c79fef",
    "lavender blue"=> "#8b88f8",
    "lavender pink"=> "#dd85d7",
    "lawn green"=> "#4da409",
    "leaf"=> "#71aa34",
    "leaf green"=> "#5ca904",
    "leafy green"=> "#51b73b",
    "leather"=> "#ac7434",
    "lemon"=> "#fdff52",
    "lemon green"=> "#adf802",
    "lemon lime"=> "#bffe28",
    "lemon yellow"=> "#fdff38",
    "lichen"=> "#8fb67b",
    "light aqua"=> "#8cffdb",
    "light aquamarine"=> "#7bfdc7",
    "light beige"=> "#fffeb6",
    "light blue"=> "#95d0fc",
    "light blue green"=> "#7efbb3",
    "light blue grey"=> "#b7c9e2",
    "light bluish green"=> "#76fda8",
    "light bright green"=> "#53fe5c",
    "light brown"=> "#ad8150",
    "light burgundy"=> "#a8415b",
    "light cyan"=> "#acfffc",
    "light eggplant"=> "#894585",
    "light forest green"=> "#4f9153",
    "light gold"=> "#fddc5c",
    "light grass green"=> "#9af764",
    "light green"=> "#96f97b",
    "light green blue"=> "#56fca2",
    "light greenish blue"=> "#63f7b4",
    "light grey"=> "#d8dcd6",
    "light grey blue"=> "#9dbcd4",
    "light grey green"=> "#b7e1a1",
    "light indigo"=> "#6d5acf",
    "light khaki"=> "#e6f2a2",
    "light lavendar"=> "#efc0fe",
    "light lavender"=> "#dfc5fe",
    "light light blue"=> "#cafffb",
    "light light green"=> "#c8ffb0",
    "light lilac"=> "#edc8ff",
    "light lime"=> "#aefd6c",
    "light lime green"=> "#b9ff66",
    "light magenta"=> "#fa5ff7",
    "light maroon"=> "#a24857",
    "light mauve"=> "#c292a1",
    "light mint"=> "#b6ffbb",
    "light mint green"=> "#a6fbb2",
    "light moss green"=> "#a6c875",
    "light mustard"=> "#f7d560",
    "light navy"=> "#155084",
    "light navy blue"=> "#2e5a88",
    "light neon green"=> "#4efd54",
    "light olive"=> "#acbf69",
    "light olive green"=> "#a4be5c",
    "light orange"=> "#fdaa48",
    "light pastel green"=> "#b2fba5",
    "light pea green"=> "#c4fe82",
    "light peach"=> "#ffd8b1",
    "light periwinkle"=> "#c1c6fc",
    "light pink"=> "#ffd1df",
    "light plum"=> "#9d5783",
    "light purple"=> "#bf77f6",
    "light red"=> "#ff474c",
    "light rose"=> "#ffc5cb",
    "light royal blue"=> "#3a2efe",
    "light sage"=> "#bcecac",
    "light salmon"=> "#fea993",
    "light sea green"=> "#98f6b0",
    "light seafoam"=> "#a0febf",
    "light seafoam green"=> "#a7ffb5",
    "light sky blue"=> "#c6fcff",
    "light tan"=> "#fbeeac",
    "light teal"=> "#90e4c1",
    "light turquoise"=> "#7ef4cc",
    "light urple"=> "#b36ff6",
    "light violet"=> "#d6b4fc",
    "light yellow"=> "#fffe7a",
    "light yellow green"=> "#ccfd7f",
    "light yellowish green"=> "#c2ff89",
    "lightblue"=> "#7bc8f6",
    "lighter green"=> "#75fd63",
    "lighter purple"=> "#a55af4",
    "lightgreen"=> "#76ff7b",
    "lightish blue"=> "#3d7afd",
    "lightish green"=> "#61e160",
    "lightish purple"=> "#a552e6",
    "lightish red"=> "#fe2f4a",
    "lilac"=> "#cea2fd",
    "liliac"=> "#c48efd",
    "lime"=> "#aaff32",
    "lime green"=> "#89fe05",
    "lime yellow"=> "#d0fe1d",
    "lipstick"=> "#d5174e",
    "lipstick red"=> "#c0022f",
    "macaroni and cheese"=> "#efb435",
    "magenta"=> "#c20078",
    "mahogany"=> "#4a0100",
    "maize"=> "#f4d054",
    "mango"=> "#ffa62b",
    "manilla"=> "#fffa86",
    "marigold"=> "#fcc006",
    "marine"=> "#042e60",
    "marine blue"=> "#01386a",
    "maroon"=> "#650021",
    "mauve"=> "#ae7181",
    "medium blue"=> "#2c6fbb",
    "medium brown"=> "#7f5112",
    "medium green"=> "#39ad48",
    "medium grey"=> "#7d7f7c",
    "medium pink"=> "#f36196",
    "medium purple"=> "#9e43a2",
    "melon"=> "#ff7855",
    "merlot"=> "#730039",
    "metallic blue"=> "#4f738e",
    "mid blue"=> "#276ab3",
    "mid green"=> "#50a747",
    "midnight"=> "#03012d",
    "midnight blue"=> "#020035",
    "midnight purple"=> "#280137",
    "military green"=> "#667c3e",
    "milk chocolate"=> "#7f4e1e",
    "mint"=> "#9ffeb0",
    "mint green"=> "#8fff9f",
    "minty green"=> "#0bf77d",
    "mocha"=> "#9d7651",
    "moss"=> "#769958",
    "moss green"=> "#658b38",
    "mossy green"=> "#638b27",
    "mud"=> "#735c12",
    "mud brown"=> "#60460f",
    "mud green"=> "#606602",
    "muddy brown"=> "#886806",
    "muddy green"=> "#657432",
    "muddy yellow"=> "#bfac05",
    "mulberry"=> "#920a4e",
    "murky green"=> "#6c7a0e",
    "mushroom"=> "#ba9e88",
    "mustard"=> "#ceb301",
    "mustard brown"=> "#ac7e04",
    "mustard green"=> "#a8b504",
    "mustard yellow"=> "#d2bd0a",
    "muted blue"=> "#3b719f",
    "muted green"=> "#5fa052",
    "muted pink"=> "#d1768f",
    "muted purple"=> "#805b87",
    "nasty green"=> "#70b23f",
    "navy"=> "#01153e",
    "navy blue"=> "#001146",
    "navy green"=> "#35530a",
    "neon blue"=> "#04d9ff",
    "neon green"=> "#0cff0c",
    "neon pink"=> "#fe019a",
    "neon purple"=> "#bc13fe",
    "neon red"=> "#ff073a",
    "neon yellow"=> "#cfff04",
    "nice blue"=> "#107ab0",
    "night blue"=> "#040348",
    "ocean"=> "#017b92",
    "ocean blue"=> "#03719c",
    "ocean green"=> "#3d9973",
    "ocher"=> "#bf9b0c",
    "ochre"=> "#bf9005",
    "ocre"=> "#c69c04",
    "off blue"=> "#5684ae",
    "off green"=> "#6ba353",
    "off white"=> "#ffffe4",
    "off yellow"=> "#f1f33f",
    "old pink"=> "#c77986",
    "old rose"=> "#c87f89",
    "olive"=> "#6e750e",
    "olive brown"=> "#645403",
    "olive drab"=> "#6f7632",
    "olive green"=> "#677a04",
    "olive yellow"=> "#c2b709",
    "orange"=> "#f97306",
    "orange brown"=> "#be6400",
    "orange pink"=> "#ff6f52",
    "orange red"=> "#fd411e",
    "orange yellow"=> "#ffad01",
    "orangeish"=> "#fd8d49",
    "orangered"=> "#fe420f",
    "orangey brown"=> "#b16002",
    "orangey red"=> "#fa4224",
    "orangey yellow"=> "#fdb915",
    "orangish"=> "#fc824a",
    "orangish brown"=> "#b25f03",
    "orangish red"=> "#f43605",
    "orchid"=> "#c875c4",
    "pale"=> "#fff9d0",
    "pale aqua"=> "#b8ffeb",
    "pale blue"=> "#d0fefe",
    "pale brown"=> "#b1916e",
    "pale cyan"=> "#b7fffa",
    "pale gold"=> "#fdde6c",
    "pale green"=> "#c7fdb5",
    "pale grey"=> "#fdfdfe",
    "pale lavender"=> "#eecffe",
    "pale light green"=> "#b1fc99",
    "pale lilac"=> "#e4cbff",
    "pale lime"=> "#befd73",
    "pale lime green"=> "#b1ff65",
    "pale magenta"=> "#d767ad",
    "pale mauve"=> "#fed0fc",
    "pale olive"=> "#b9cc81",
    "pale olive green"=> "#b1d27b",
    "pale orange"=> "#ffa756",
    "pale peach"=> "#ffe5ad",
    "pale pink"=> "#ffcfdc",
    "pale purple"=> "#b790d4",
    "pale red"=> "#d9544d",
    "pale rose"=> "#fdc1c5",
    "pale salmon"=> "#ffb19a",
    "pale sky blue"=> "#bdf6fe",
    "pale teal"=> "#82cbb2",
    "pale turquoise"=> "#a5fbd5",
    "pale violet"=> "#ceaefa",
    "pale yellow"=> "#ffff84",
    "parchment"=> "#fefcaf",
    "pastel blue"=> "#a2bffe",
    "pastel green"=> "#b0ff9d",
    "pastel orange"=> "#ff964f",
    "pastel pink"=> "#ffbacd",
    "pastel purple"=> "#caa0ff",
    "pastel red"=> "#db5856",
    "pastel yellow"=> "#fffe71",
    "pea"=> "#a4bf20",
    "pea green"=> "#8eab12",
    "pea soup"=> "#929901",
    "pea soup green"=> "#94a617",
    "peach"=> "#ffb07c",
    "peachy pink"=> "#ff9a8a",
    "peacock blue"=> "#016795",
    "pear"=> "#cbf85f",
    "periwinkle"=> "#8e82fe",
    "periwinkle blue"=> "#8f99fb",
    "perrywinkle"=> "#8f8ce7",
    "petrol"=> "#005f6a",
    "pig pink"=> "#e78ea5",
    "pine"=> "#2b5d34",
    "pine green"=> "#0a481e",
    "pink"=> "#ff81c0",
    "pink purple"=> "#db4bda",
    "pink red"=> "#f5054f",
    "pink/purple"=> "#ef1de7",
    "pinkish"=> "#d46a7e",
    "pinkish brown"=> "#b17261",
    "pinkish grey"=> "#c8aca9",
    "pinkish orange"=> "#ff724c",
    "pinkish purple"=> "#d648d7",
    "pinkish red"=> "#f10c45",
    "pinkish tan"=> "#d99b82",
    "pinky"=> "#fc86aa",
    "pinky purple"=> "#c94cbe",
    "pinky red"=> "#fc2647",
    "piss yellow"=> "#ddd618",
    "pistachio"=> "#c0fa8b",
    "plum"=> "#580f41",
    "plum purple"=> "#4e0550",
    "poison green"=> "#40fd14",
    "poo"=> "#8f7303",
    "poo brown"=> "#885f01",
    "poop"=> "#7f5e00",
    "poop brown"=> "#7a5901",
    "poop green"=> "#6f7c00",
    "powder blue"=> "#b1d1fc",
    "powder pink"=> "#ffb2d0",
    "primary blue"=> "#0804f9",
    "prussian blue"=> "#004577",
    "puce"=> "#a57e52",
    "puke"=> "#a5a502",
    "puke brown"=> "#947706",
    "puke green"=> "#9aae07",
    "puke yellow"=> "#c2be0e",
    "pumpkin"=> "#e17701",
    "pumpkin orange"=> "#fb7d07",
    "pure blue"=> "#0203e2",
    "purple"=> "#7e1e9c",
    "purple blue"=> "#632de9",
    "purple brown"=> "#673a3f",
    "purple grey"=> "#866f85",
    "purple pink"=> "#e03fd8",
    "purple red"=> "#990147",
    "purple/blue"=> "#5d21d0",
    "purple/pink"=> "#d725de",
    "purpleish"=> "#98568d",
    "purpleish blue"=> "#6140ef",
    "purpleish pink"=> "#df4ec8",
    "purpley"=> "#8756e4",
    "purpley blue"=> "#5f34e7",
    "purpley grey"=> "#947e94",
    "purpley pink"=> "#c83cb9",
    "purplish"=> "#94568c",
    "purplish blue"=> "#601ef9",
    "purplish brown"=> "#6b4247",
    "purplish grey"=> "#7a687f",
    "purplish pink"=> "#ce5dae",
    "purplish red"=> "#b0054b",
    "purply"=> "#983fb2",
    "purply blue"=> "#661aee",
    "purply pink"=> "#f075e6",
    "putty"=> "#beae8a",
    "racing green"=> "#014600",
    "radioactive green"=> "#2cfa1f",
    "raspberry"=> "#b00149",
    "raw sienna"=> "#9a6200",
    "raw umber"=> "#a75e09",
    "really light blue"=> "#d4ffff",
    "red"=> "#e50000",
    "red brown"=> "#8b2e16",
    "red orange"=> "#fd3c06",
    "red pink"=> "#fa2a55",
    "red purple"=> "#820747",
    "red violet"=> "#9e0168",
    "red wine"=> "#8c0034",
    "reddish"=> "#c44240",
    "reddish brown"=> "#7f2b0a",
    "reddish grey"=> "#997570",
    "reddish orange"=> "#f8481c",
    "reddish pink"=> "#fe2c54",
    "reddish purple"=> "#910951",
    "reddy brown"=> "#6e1005",
    "rich blue"=> "#021bf9",
    "rich purple"=> "#720058",
    "robin egg blue"=> "#8af1fe",
    "robin's egg"=> "#6dedfd",
    "robin's egg blue"=> "#98eff9",
    "rosa"=> "#fe86a4",
    "rose"=> "#cf6275",
    "rose pink"=> "#f7879a",
    "rose red"=> "#be013c",
    "rosy pink"=> "#f6688e",
    "rouge"=> "#ab1239",
    "royal"=> "#0c1793",
    "royal blue"=> "#0504aa",
    "royal purple"=> "#4b006e",
    "ruby"=> "#ca0147",
    "russet"=> "#a13905",
    "rust"=> "#a83c09",
    "rust brown"=> "#8b3103",
    "rust orange"=> "#c45508",
    "rust red"=> "#aa2704",
    "rusty orange"=> "#cd5909",
    "rusty red"=> "#af2f0d",
    "saffron"=> "#feb209",
    "sage"=> "#87ae73",
    "sage green"=> "#88b378",
    "salmon"=> "#ff796c",
    "salmon pink"=> "#fe7b7c",
    "sand"=> "#e2ca76",
    "sand brown"=> "#cba560",
    "sand yellow"=> "#fce166",
    "sandstone"=> "#c9ae74",
    "sandy"=> "#f1da7a",
    "sandy brown"=> "#c4a661",
    "sandy yellow"=> "#fdee73",
    "sap green"=> "#5c8b15",
    "sapphire"=> "#2138ab",
    "scarlet"=> "#be0119",
    "sea"=> "#3c9992",
    "sea blue"=> "#047495",
    "sea green"=> "#53fca1",
    "seafoam"=> "#80f9ad",
    "seafoam blue"=> "#78d1b6",
    "seafoam green"=> "#7af9ab",
    "seaweed"=> "#18d17b",
    "seaweed green"=> "#35ad6b",
    "sepia"=> "#985e2b",
    "shamrock"=> "#01b44c",
    "shamrock green"=> "#02c14d",
    "shit"=> "#7f5f00",
    "shit brown"=> "#7b5804",
    "shit green"=> "#758000",
    "shocking pink"=> "#fe02a2",
    "sick green"=> "#9db92c",
    "sickly green"=> "#94b21c",
    "sickly yellow"=> "#d0e429",
    "sienna"=> "#a9561e",
    "silver"=> "#c5c9c7",
    "sky"=> "#82cafc",
    "sky blue"=> "#75bbfd",
    "slate"=> "#516572",
    "slate blue"=> "#5b7c99",
    "slate green"=> "#658d6d",
    "slate grey"=> "#59656d",
    "slime green"=> "#99cc04",
    "snot"=> "#acbb0d",
    "snot green"=> "#9dc100",
    "soft blue"=> "#6488ea",
    "soft green"=> "#6fc276",
    "soft pink"=> "#fdb0c0",
    "soft purple"=> "#a66fb5",
    "spearmint"=> "#1ef876",
    "spring green"=> "#a9f971",
    "spruce"=> "#0a5f38",
    "squash"=> "#f2ab15",
    "steel"=> "#738595",
    "steel blue"=> "#5a7d9a",
    "steel grey"=> "#6f828a",
    "stone"=> "#ada587",
    "stormy blue"=> "#507b9c",
    "straw"=> "#fcf679",
    "strawberry"=> "#fb2943",
    "strong blue"=> "#0c06f7",
    "strong pink"=> "#ff0789",
    "sun yellow"=> "#ffdf22",
    "sunflower"=> "#ffc512",
    "sunflower yellow"=> "#ffda03",
    "sunny yellow"=> "#fff917",
    "sunshine yellow"=> "#fffd37",
    "swamp"=> "#698339",
    "swamp green"=> "#748500",
    "tan"=> "#d1b26f",
    "tan brown"=> "#ab7e4c",
    "tan green"=> "#a9be70",
    "tangerine"=> "#ff9408",
    "taupe"=> "#b9a281",
    "tea"=> "#65ab7c",
    "tea green"=> "#bdf8a3",
    "teal"=> "#029386",
    "teal blue"=> "#01889f",
    "teal green"=> "#25a36f",
    "tealish"=> "#24bca8",
    "tealish green"=> "#0cdc73",
    "terra cotta"=> "#c9643b",
    "terracota"=> "#cb6843",
    "terracotta"=> "#ca6641",
    "tiffany blue"=> "#7bf2da",
    "tomato"=> "#ef4026",
    "tomato red"=> "#ec2d01",
    "topaz"=> "#13bbaf",
    "toupe"=> "#c7ac7d",
    "toxic green"=> "#61de2a",
    "tree green"=> "#2a7e19",
    "true blue"=> "#010fcc",
    "true green"=> "#089404",
    "turquoise"=> "#06c2ac",
    "turquoise blue"=> "#06b1c4",
    "turquoise green"=> "#04f489",
    "turtle green"=> "#75b84f",
    "twilight"=> "#4e518b",
    "twilight blue"=> "#0a437a",
    "ugly blue"=> "#31668a",
    "ugly brown"=> "#7d7103",
    "ugly green"=> "#7a9703",
    "ugly pink"=> "#cd7584",
    "ugly purple"=> "#a442a0",
    "ugly yellow"=> "#d0c101",
    "ultramarine"=> "#2000b1",
    "ultramarine blue"=> "#1805db",
    "umber"=> "#b26400",
    "velvet"=> "#750851",
    "vermillion"=> "#f4320c",
    "very dark blue"=> "#000133",
    "very dark brown"=> "#1d0200",
    "very dark green"=> "#062e03",
    "very dark purple"=> "#2a0134",
    "very light blue"=> "#d5ffff",
    "very light brown"=> "#d3b683",
    "very light green"=> "#d1ffbd",
    "very light pink"=> "#fff4f2",
    "very light purple"=> "#f6cefc",
    "very pale blue"=> "#d6fffe",
    "very pale green"=> "#cffdbc",
    "vibrant blue"=> "#0339f8",
    "vibrant green"=> "#0add08",
    "vibrant purple"=> "#ad03de",
    "violet"=> "#9a0eea",
    "violet blue"=> "#510ac9",
    "violet pink"=> "#fb5ffc",
    "violet red"=> "#a50055",
    "viridian"=> "#1e9167",
    "vivid blue"=> "#152eff",
    "vivid green"=> "#2fef10",
    "vivid purple"=> "#9900fa",
    "vomit"=> "#a2a415",
    "vomit green"=> "#89a203",
    "vomit yellow"=> "#c7c10c",
    "warm blue"=> "#4b57db",
    "warm brown"=> "#964e02",
    "warm grey"=> "#978a84",
    "warm pink"=> "#fb5581",
    "warm purple"=> "#952e8f",
    "washed out green"=> "#bcf5a6",
    "water blue"=> "#0e87cc",
    "watermelon"=> "#fd4659",
    "weird green"=> "#3ae57f",
    "wheat"=> "#fbdd7e",
    "white"=> "#ffffff",
    "windows blue"=> "#3778bf",
    "wine"=> "#80013f",
    "wine red"=> "#7b0323",
    "wintergreen"=> "#20f986",
    "wisteria"=> "#a87dc2",
    "yellow"=> "#ffff14",
    "yellow brown"=> "#b79400",
    "yellow green"=> "#c0fb2d",
    "yellow ochre"=> "#cb9d06",
    "yellow orange"=> "#fcb001",
    "yellow tan"=> "#ffe36e",
    "yellow/green"=> "#c8fd3d",
    "yellowgreen"=> "#bbf90f",
    "yellowish"=> "#faee66",
    "yellowish brown"=> "#9b7a01",
    "yellowish green"=> "#b0dd16",
    "yellowish orange"=> "#ffab0f",
    "yellowish tan"=> "#fcfc81",
    "yellowy brown"=> "#ae8b0c",
    "yellowy green"=> "#bff128"
)

max_stars_repo_name: gabrielgellner/OdinSon

max_issues_repo_name: gabrielgellner/OdinSon

max_forks_repo_name: gabrielgellner/OdinSon


text:
module CachedArrays

using Base: @propagate_inbounds

import Base: size
import Base: getindex, setindex!

export CachedArray
export CachedMatrix
export CachedVector
export setsize!

mutable struct CachedArray{T,N,A<:AbstractArray{T,N}} <: AbstractArray{T,N}
  array::A
  size::NTuple{N,Int}
end

const CachedMatrix{T,A} = CachedArray{T,2,A}

const CachedVector{T,A} = CachedArray{T,1,A}

CachedArray(a::AbstractArray) = CachedArray(a,size(a))

CachedVector(a::AbstractVector) = CachedArray(a,size(a))

CachedMatrix(a::AbstractMatrix) = CachedArray(a,size(a))

function CachedArray(T,N)
  s = tuple([0 for i in 1:N]...)
  a = Array{T,N}(undef,s)
  CachedArray(a)
end

function CachedVector(T)
  CachedArray(T,1)
end

function CachedMatrix(T)
  CachedArray(T,2)
end

size(self::CachedArray) = self.size

function setsize!(self::CachedArray{T,N},s::NTuple{N,Int}) where {T,N}
  if s <= size(self.array)
    self.size = s
  else
    self.array = Array{T,N}(undef,s)
    self.size = s
  end
end

@propagate_inbounds function getindex(self::CachedArray{T,N}, kj::Vararg{Integer,N}) where {T,N}
    self.array[kj...]
end

@propagate_inbounds function setindex!(B::CachedArray{T,N}, v, kj::Vararg{Integer,N}) where {T,N}
    B.array[kj...] = v
    v
end

end # module CachedArrays


text:
fizz() = print("fizz ")
buzz() = print("buzz ")

function fizzbuzz(n)
    for i in 1:n
        if i % 3 == 0 || i % 5 == 0
            i % 3 == 0 && fizz()
            i % 5 == 0 && buzz()
        else
            print(i, " ")
        end
    end
    return n
end

using JuliaInterpreter
@interpret fizzbuzz(4)


text:
using PtFEM, SparseArrays, Documenter

DOC_ROOT = ptfem_path("..", "docs")

page_list = Array{Pair{String, Any}, 1}();
append!(page_list, [Pair("Introduction", "INTRO.md")])
append!(page_list, [Pair("Getting started", "GETTINGSTARTED.md")]);
append!(page_list, [Pair("Changes w.r.t. PtFEM", "CHANGES.md")])
append!(page_list, [Pair("PtFEM.jl documentation", "index.md")])
append!(page_list, [Pair("Versions", "VERSIONS.md")]);
append!(page_list, [Pair("Todo", "TODO.md")])
append!(page_list, [Pair("References", "REFERENCES.md")])

makedocs(
    format = Documenter.HTML(prettyurls = haskey(ENV, "GITHUB_ACTIONS")),
    root = DOC_ROOT,
    modules = Module[],
    sitename = "PtFEM",
    authors = "Rob Goedman",
    pages = page_list,
)

deploydocs(
    root = DOC_ROOT,
    repo = "github.com/PtFEM.jl.git",
    devbranch = "master",
    push_preview = true,
 )


text:
using ITensors,
      Test

import ITensors.SmallString,
       ITensors.IntChar,
       ITensors.isint

@testset "SmallString" begin
  @testset "ctors" begin
      s = SmallString()
      @test ITensors.isnull(s)
  end

  @testset "setindex" begin
      s = SmallString()
      @test ITensors.isnull(s)
      t = setindex(s, IntChar(1), 1)
      @test !ITensors.isnull(t)
  end

  #@testset "push" begin
  #  s = SmallString()
  #  @test ITensors.isnull(s)
  #  t = push(s, IntChar(1))
  #  @test !ITensors.isnull(t)
  #end

  @testset "comparison" begin
      u = SmallString("1")
      t = SmallString("1")
      @test u == t
      t = SmallString("2")
      @test u < t
  end

  #@testset "Convert to String" begin
  #  s = SmallString("abc")
  #  @test typeof(s) == SmallString

  #  sg = String(s)
  #  for n=1:length(sg)
  #    @test sg[n] == convert(Char,s[n])
  #  end
  #end

  @testset "isint" begin
    i = SmallString("123")
    @test isint(i) == true

    s = SmallString("abc")
    @test isint(s) == false

    # Test maximum length
    s = SmallString("12345678")
    @test isint(s) == true
  end

  @testset "isless" begin
    s1 = SmallString("ab")
    s2 = SmallString("xy")
    @test isless(s1,s2) == true
    @test isless(s2,s1) == false
    @test isless(s1,s1) == false
    @test isless(s2,s2) == false
  end

  @testset "show" begin
    t = Tag("")
    @test sprint(show,t) == ""

    t = Tag("Red")
    @test sprint(show,t) == "Red"

    # Make sure to test maximum length tag
    t = Tag("Electron")
    @test sprint(show,t) == "Electron"
  end

end


text:
using StochasticDiffEq, Test

tspan = (0.0, 1.0)
explicit = [SRIW1(), SRIW2(), SOSRI(), SOSRI2(), SRA2(), SRA3(), SOSRA(), SOSRA2()]
implicit_autodiff   = [SKenCarp(), ImplicitEulerHeun()]
implicit_noautodiff = [SKenCarp(autodiff=false), ImplicitEulerHeun(autodiff=false)]

@testset "Out-of-place methods" begin
  u0 = 1.0 + 1.0im
  function f(u, p, t)
    t isa Complex && error("time is complex")
    1.01u
  end
  prob = SDEProblem(f, f, u0, tspan)

  for alg in Iterators.flatten((explicit, implicit_noautodiff))
    sol = solve(prob, alg)
    @test eltype(sol[end]) == ComplexF64
  end

  # currently broken
  for alg in implicit_autodiff
    @test_throws MethodError solve(prob, alg)
  end
end

@testset "In-place methods" begin
  u0 = ones(2,4) + im*ones(2,4)
  function f(du, u, p, t)
    t isa Complex && error("time is complex")
    du .= 1.01u
  end
  prob = SDEProblem(f, f, u0, tspan)

  for alg in Iterators.flatten((explicit, implicit_noautodiff))
    sol = solve(prob, alg)
    @test eltype(sol[end]) == ComplexF64
  end

  # currently broken
  for alg in implicit_autodiff
    @test_throws ArgumentError solve(prob, alg)
  end
end


text:
Network = NetLib.Gin

netparams = NetLib.GinHP()

self_play = SelfPlayParams(
  sim=SimParams(
    num_games=ConstSchedule(256),
    num_workers=ConstSchedule(200),
    batch_size=ConstSchedule(100),
    use_gpu=true,
    reset_every=1,
    flip_probability=0.,
    alternate_colors=false),
  mcts=MctsParams(
    num_iters_per_turn=300,
    cpuct=0.8,
    adaptive_cpuct = false,
    temperature=ConstSchedule(0.05),
    dirichlet_noise_ϵ=0.,
    dirichlet_noise_α=0.2))

learning = LearningParams(
  use_gpu=true,
  use_position_averaging=false,
  samples_weighing_policy=LOG_WEIGHT,
  l2_regularization=1e-5,
  optimiser=Adam(lr=1e-4),
  batch_size=2048,
  loss_computation_batch_size=2048,
  nonvalidity_penalty=0.,
  min_checkpoints_per_epoch=1,
  max_batches_per_checkpoint=2_000,
  num_checkpoints=1,
  rewards_renormalization = 1)

params = Params(
  arena=nothing,
  self_play=self_play,
  learning=learning,
  num_iters=50,
  memory_analysis=nothing,
  ternary_rewards=false,
  use_symmetries=false,
  mem_buffer_size=PLSchedule(500_000))

benchmark_sim = SimParams(
  self_play.sim;
  num_games=ConstSchedule(64),
  num_workers=ConstSchedule(44),
  batch_size=ConstSchedule(22),
  deterministic = true)

benchmark = [
  Benchmark.Single(
    Benchmark.Full(MctsParams(self_play.mcts, temperature=ConstSchedule(0.))),
    benchmark_sim),
  Benchmark.Single(
    Benchmark.NetworkOnly(),
    benchmark_sim)]

experiment = Experiment(
  "jsp", GameSpec(PLSchedule([1, 10], [2, 6])=>PLSchedule([1, 10], [4, 8]),
                  PLSchedule([1, 10], [2, 6])=>PLSchedule([1, 10], [4, 8]),
                  1=>5), params, Network, netparams, benchmark)


text:
module A
a = 2

module B
b = 3
export b
end
export a
end


text:
import ..LambdaCalculus: alpha_equivalent, evaluate, evaluateonce, freevars, substitute

export closeterm,
    is_lc,
    openterm

alpha_equivalent(t1::Term, t2::Term) = t1 == t2

freevars(t::FVar) = Set([t.name])
freevars(t::BVar) = Set{Symbol}()
freevars(t::Abs) = freevars(t.body)
freevars(t::App) = freevars(t.car) ∪ freevars(t.cdr)

openterm(k::Index, u::BVar, t::BVar) = t.index == k ? BVar(u.index + k - 1) : t
openterm(k::Index, u::Term, t::BVar) = t.index == k ? u : t
openterm(k::Index, u::Term, t::FVar) = t
openterm(k::Index, u::Term, t::App) = App(openterm(k, u, t.car), openterm(k, u, t.cdr))
openterm(k::Index, u::Term, t::Abs) = Abs(openterm(k + 1, u, t.body))
openterm(u::Term, t::Term) = openterm(1, u, t)

closeterm(k::Index, x::Symbol, t::BVar) = t
closeterm(k::Index, x::Symbol, t::FVar) = t.name == x ? BVar(k) : t
closeterm(k::Index, x::Symbol, t::App) = App(closeterm(k, x, t.car), closeterm(k, x, t.cdr))
closeterm(k::Index, x::Symbol, t::Abs) = Abs(closeterm(k + 1, x, t.body))
closeterm(x::Symbol, t::Term) = closeterm(1, x, t)

substitute(x::Symbol, u::Term, t::BVar) = t
substitute(x::Symbol, u::Term, t::FVar) = t.name == x ? u : t
substitute(x::Symbol, u::Term, t::App) = App(substitute(x, u, t.car), substitute(x, u, t.cdr))
substitute(x::Symbol, u::Term, t::Abs) = Abs(substitute(x, u, t.body))

is_lc(t::BVar, level::Index) = t.index ≤ level
is_lc(t::FVar, level::Index) = true
is_lc(t::App, level::Index) = is_lc(t.car, level) && is_lc(t.cdr, level)
is_lc(t::Abs, level::Index) = is_lc(t.body, level)
is_lc(t::Term) = is_lc(t, 1)

function evaluateonce_app(car::Abs, cdr::Term)
    openterm(cdr, car.body)
end

function evaluateonce_app(car::Union{App, FVar, BVar}, cdr::Term)
    newcar = evaluateonce(car)
    if newcar === nothing
        newcdr = evaluateonce(cdr)
        if newcdr === nothing
            nothing
        else
            App(car, newcdr)
        end
    else
        App(newcar, cdr)
    end
end

evaluateonce(term::Union{FVar, BVar}) = nothing
evaluateonce(term::App) = evaluateonce_app(term.car, term.cdr)
function evaluateonce(term::Abs)
    newbody = evaluateonce(term.body)
    if newbody !== nothing
        Abs(newbody)
    else
        nothing
    end
end

function evaluate(term::Term, maxsteps = Inf)
    while maxsteps > 0
        reduced = evaluateonce(term)

        if reduced === nothing
            return term
        else
            term = reduced
            maxsteps -= 1
        end
    end

    return term
end


text:
module Endpoint

export CustomerController

using Bukdu
import Bukdu.Actions: index, show, new, edit, create, delete, update
# using Octo.Adapters.PostgreSQL

struct CustomerController <: ApplicationController
    conn::Conn
end

# Repo.debug_sql()
# Repo.connect(
#    adapter = Octo.Adapters.PostgreSQL,
#    dbname = "postgresqltest",
#    user = "postgres",
#)

struct Customer
end
# Schema.model(Customer, table_name="customers", primary_key="id")

function index(c::CustomerController) # GET
    # customers = Repo.all(Customer)
    customers = [(firstName="John", lastName="Doe"),
                 (firstName="Russ", lastName="Smith"),
                 (firstName="Kate", lastName="Williams")]
    render(JSON, customers)
end

function show(c::CustomerController) # GET
    customer_id = c.params.id
    # customer = Repo.get(Customer, customer_id)
    customer = (firstName="John", lastName="Doe")
    render(JSON, customer)
end

function new(c::CustomerController) # GET
    "new"
end

function edit(c::CustomerController) # GET
    c.params.id
end

function create(c::CustomerController) # POST
    "create"
end

function delete(c::CustomerController) # DELETE
    c.params.id
end

function update(c::CustomerController) # PATCH, PUT
    (c.conn.request.method, c.params.id)
end

end # module Endpoint

if PROGRAM_FILE == basename(@__FILE__)

using .Endpoint
using Bukdu
import Bukdu.Actions: index, show, new, edit, create, delete, update

pipeline(:api) do conn::Conn
end

routes(:api) do
    resources("/customers", CustomerController)
end

Bukdu.start(8080)

# Router.call(get, "/customers") #
# julia> CLI.routes()
# GET     /customers           CustomerController  index   :api
# GET     /customers/new       CustomerController  new     :api
# GET     /customers/:id/edit  CustomerController  edit    :api
# GET     /customers/:id       CustomerController  show    :api
# POST    /customers           CustomerController  create  :api
# DELETE  /customers/:id       CustomerController  delete  :api
# PATCH   /customers/:id       CustomerController  update  :api
# PUT     /customers/:id       CustomerController  update  :api

Base.JLOptions().isinteractive==0 && wait()

# Bukdu.stop()

end # if

# TEST URL
# curl -X GET http://localhost:8080/customers
# curl -X GET http://localhost:8080/customers/1
# curl -X PUT http://localhost:8080/customers/1

#=
CREATE TABLE customers (
    ID SERIAL,
    firstName VARCHAR(255),
    lastName VARCHAR(255),
    PRIMARY KEY (ID) )
=#

max_stars_repo_name: wookay/TestBukdu

max_issues_repo_name: wookay/TestBukdu

max_forks_repo_name: wookay/TestBukdu


text:
using GLM
glm(:(y ~ 1), DataFrame(y = float64(randbool(10))), Binomial())

n = 2_500_000; srand(1234321)
df2 = DataFrame(x1 = rand(Normal(), n),
                x2 = rand(Exponential(), n),
                ss = pool(rand(DiscreteUniform(50), n)));
beta = unshift!(rand(Normal(),52), 0.5);
eta = ModelMatrix(ModelFrame(Formula(:(~ (x1 + x2 + ss))), df2)).m * beta;
mu = linkinv!(LogitLink(), similar(eta), eta);
df2["y"] = float64(rand(n) .< mu); df2["eta"] = eta; df2["mu"] = mu;
head(df2)
gm6 = glm(:(y ~ x1 + x2 + ss), df2, Binomial(), LogitLink())
@time glm(:(y ~ x1 + x2 + ss), df2, Binomial(), LogitLink());


text:
# This file is part of Kpax3. License is MIT.

RecipesBase.@userplot Kpax3PlotK

RecipesBase.@recipe function plotk(g::Kpax3PlotK)
  if (length(g.args) != 2) ||
     (typeof(g.args[1]) != Vector{Int}) ||
     (typeof(g.args[2]) != Vector{Float64})
      throw(KInputError("Two arguments required. Got: $(typeof(g.args))"))
  end

  k, pk = g.args

  x = if @isdefined xlims
    collect(xlims)
  else
    collect(max(1, k[1] - 5):(k[end] + 5))
  end

  u = length(k)
  v = length(x)

  y = zeros(Float64, v)
  M = 0.0

  i = 1
  j = 1
  while (i <= u) && (j <= v)
    if k[i] == x[j]
      y[j] = pk[i]

      if y[j] > M
        M = y[j]
      end

      i += 1
    end

    j += 1
  end

  multiplier = 10.0
  while (M * multiplier) <= 1.0
    multiplier *= 10
  end
  multiplier *= 10

  M = ceil(M * multiplier) / multiplier

  RecipesBase.@series begin
    seriestype := :bar
    orientation := :vertical
    legend := :none
    ylims := (0, M)

    fillcolor --> :black
    background_color --> :white
    html_output_format --> :svg
    size --> (800, 600)
    window_title --> ""
    grid --> true
    title --> ""
    xlabel --> "k"
    xticks --> :auto
    xlims --> :auto
    ylabel --> "p(k | x)"

    x, y
  end
end


text:
# download and build all non metadata dependences

include("build_funcs.jl")

"""
  This function install all of PDESolvers dependencies (currently PDESolver
  itself does not need to be built).

  Environmental variables:
    PDESOLVER_INSTALL_DEPS_MANUAL: install all dependencies manually (ie. using
                                   the particular commit stored in this file.
                                   Note that if the package is already
                                   installed, this does not do anything

    PDESOLVER_FORCE_DEP_INSTALL_ALL: download and install all dependencies,
          checking out the version stored in this file, even if the package
          is already installed

    PDESOLVER_FORCE_DEP_INSTALL_pkg_name: where pkg_name is replaced with the
          name of a dependency, forces the installation of only that dependency.

    PDESOLVER_BUNDLE_DEPS: download packages to a specified directory, do not
                           install.  Also copies this version of PDESolver
                           to the directory

    PDESOLVER_PKGDIR: the specified directory for PDESOLVER_BUNDLE_DEPS

    PDESOLVER_UNBUNDLE_DEPS: install PDESolver, using the packages in
                             PDESOLVER_PKGDIR rather than downloading from the
                             internet

    TODO: add a bundling mode that copies the currently installed packages to
          a directory, rather than downloading them from the internet
"""
function installPDESolver()

  checkInput()

  f = open("install.log", "w")
  #------------------------------------------------------------------------------
  # these packages are always installed manually
  #-----------------------------------------------------------------------------
  # [pkg_name, git url, commit identified]
  std_pkgs = [
              "PkgFix" "https://github.com/OptimalDesignLab/PkgFix.jl.git" "upgrade_0.6";
              "ArrayViews"   "https://github.com/JaredCrean2/ArrayViews.jl.git" "work";
              "ODLCommonTools" "https://github.com/OptimalDesignLab/ODLCommonTools.jl.git" "master";
              "SummationByParts" "https://github.com/OptimalDesignLab/SummationByParts.jl.git" "jcwork";
              "PumiInterface" "https://github.com/OptimalDesignLab/PumiInterface.jl.git" "master";
              "PETSc2" "https://github.com/OptimalDesignLab/PETSc2.jl.git" "v0.5"
              ]

  pkg_dict = Pkg.installed()  # get dictionary of installed package names to version numbers

  # force installation to a specific commit hash even if package is already present
  # or would normally be install via the REQUIRE file

  global const FORCE_INSTALL_ALL = haskey(ENV, "PDESOLVER_FORCE_DEP_INSTALL_ALL")

  # figure out the package directory
  if haskey(ENV, "PDESOLVER_BUNDLE_DEPS")
    pkgdir = ENV["PDESOLVER_PKGDIR"]
  else  # unbundling deps or regular install
    pkgdir = Pkg.dir()
  end

  println(f, "PDESOLVER_BUNDLE_DEPS = ", haskey(ENV, "PDESOLVER_BUNDLE_DEPS"))
  println(f, "PDESOLVER_PKGDIR = ", haskey(ENV, "PDESOLVER_PKGDIR"))

  println(f, "\n---Installing non METADATA packages---\n")
  for i=1:size(std_pkgs, 1)
    pkg_name = std_pkgs[i, 1]
    git_url = std_pkgs[i, 2]
    git_commit = std_pkgs[i, 3]
    if haskey(ENV, "PDESOLVER_BUNDLE_DEPS")
      bundle_pkg(pkgdir, pkg_name, git_url, git_commit, f)
    elseif haskey(ENV, "PDESOLVER_UNBUNDLE_DEPS")
      unbundle_pkg(pkgdir, pkg_name, f)
    else  # do regular install
      install_pkg(pkgdir, pkg_name, git_url, git_commit, pkg_dict, f)
    end
  end

  println(f, "\n---Finished installing non METADATA packages---\n")
  #------------------------------------------------------------------------------
  # these packages are not always installed manually
  # this section provides a backup mechanism if the regular version resolution
  # mechanism fails
  #------------------------------------------------------------------------------

  # array of [pkg_name, git_url,  commit_hash/tag/branch]
  # these must be in dependency order for unbundling to work
  pkg_list = [
              "Compat" "https://github.com/JuliaLang/Compat.jl.git" "v0.66.0";
              "SHA" "https://github.com/staticfloat/SHA.jl.git" "v0.5.7";
              "URIParser" "https://github.com/JuliaWeb/URIParser.jl.git" "v0.3.1";
              "BinDeps" "https://github.com/JuliaLang/BinDeps.jl.git" "v0.8.8";
              "FactCheck" "https://github.com/JuliaLang/FactCheck.jl.git" "v0.4.3";
              "MPI" "https://github.com/JuliaParallel/MPI.jl.git" "v0.6.0";
              ]

    println(f, "\n---Considering manual package installations---\n")
    for i=1:size(pkg_list, 1)

      pkg_name = pkg_list[i, 1]
      git_url = pkg_list[i, 2]
      git_commit = pkg_list[i, 3]

      if haskey(ENV, "PDESOLVER_INSTALL_DEPS_MANUAL") || haskey(ENV, "PDESOLVER_FORCE_DEP_INSTALL_$pkg_name") || FORCE_INSTALL_ALL

        install_pkg(pkgdir, pkg_name, git_url, git_commit, pkg_dict, f, force=true)
      end
      if haskey(ENV, "PDESOLVER_BUNDLE_DEPS")
        bundle_pkg(pkgdir, pkg_name, git_url, git_commit, f)
      elseif haskey(ENV, "PDESOLVER_UNBUNDLE_DEPS")
        unbundle_pkg(pkgdir, pkg_name, f)
      end

    end

    println(f, "\n---Finished manual package installations---\n")

    if haskey(ENV, "PDESOLVER_BUNDLE_DEPS")
      # PDESolver isn't really a dependency, but copy it anyways
      run(`cp -r ../../PDESolver $pkgdir`)
    end

  close(f)

end  # end function

# run the installation
installPDESolver()

# generate the known_keys dictonary
if !haskey(ENV, "PDESOLVER_BUNDLE_DEPS")
  start_dir = pwd()
  input_path = joinpath(Pkg.dir("PDESolver"), "src/input")
  cd(input_path)
  include(joinpath(input_path, "extract_keys.jl"))
  cd(start_dir)
end