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

## hashing small, built-in numeric types
## for a system image built without floating point support

hx(a::UInt64, b::UInt64, h::UInt) = hash_uint64(3a + b - h)

hash(x::UInt64,  h::UInt) = hx(x, x, h)
hash(x::Int64,   h::UInt) = hx(reinterpret(UInt64,abs(x)), reinterpret(UInt64,x), h)
hash(x::Union{Bool,Char,Int8,UInt8,Int16,UInt16,Int32,UInt32}, h::UInt) = hash(Int64(x), h)

max_stars_repo_name: caodi/julia

max_issues_repo_name: caodi/julia

max_forks_repo_name: caodi/julia


text:
using Pkg
Pkg.activate(".")
using Soss

using SymPy
import PyCall

stats = PyCall.pyimport_conda("sympy.stats", "sympy")
import_from(stats)

# macro symport(s)
#     str = "sympy.$s"
#     quote
#         $s = PyCall.pyimport_conda($str, "sympy")
#         import_from($s)
#     end
# end

import MacroTools: postwalk, @capture
using MLStyle

using MacroTools: @capture, postwalk, @q

function symlogpdf(expr, ctx)
    @assert @capture(expr, v_ ~ dist_)

    if @capture(dist, d_(dargs__))
        newargs = map(dargs) do arg
            postwalk(arg) do x
                get(ctx, x, x)
            end
        end

        s = ctx[d](ctx[v],newargs)
        s = :(SymPy.density($s).pdf($(ctx[v])) |> log)
        s = @q expand($s,log=true,deep=false,force=true)
        eval(s)

    else
        expr
    end

end

function symlogpdf(m::Model)
    m = canonical(m)
    vs = variables(m)
    ctx = Dict{Symbol, Any}(v => SymPy.symbols(v) for v in vs )

    stochs = []
    for arg in m.body.args
        if @capture(arg, v_=x_)
            merge!(ctx, Dict(v => x ))
        else push!(stochs, arg)
        end
    end

    merge!(ctx, Dict(
        :Normal => (v,args) -> :(SymPy.Normal($v,$(args...)))
      , :HalfNormal => (v,args) -> :(SymPy.Normal($v,$(args...)))
      , :TDist => (v,args) -> :(SymPy.StudentT($v,$(args...)))
      , :Cauchy => (v,args) -> :(SymPy.Cauchy($v,$(args...)))
      , :HalfCauchy => (v,args) -> :(SymPy.Cauchy($v,0,$(args...)))
    #   , :For => (f,xs) => :()
      , :iid => (v, args) -> begin

                :()
            end
    ))

    sum(symlogpdf(expr,ctx) for expr in stochs)
end

a = @model begin
    x ~ Normal(0,1)
    y ~ Normal(0,1)
end

m = @model x,y begin
    α ~ Cauchy(0,5)
    β ~ Normal(0,1)
    σ ~ HalfCauchy(3)
    x0 ~ Normal(0,1)
    ε ~ HalfNormal(0,1)
    x ~ Normal(x0, ε)
    y0 ~ Normal(α + β * x , σ)
    y ~ Normal(y0, ε)
end

symlogpdf(m)

ext: jl


text:
module test_emojisymbols_latex

using Test
using EmojiSymbols

@test EmojiSymbols.latex_name_table["β"] == "\\beta"
@test sprint(show, MIME"text/plain"(), 'β') == "\\beta 'β': Unicode U+03B2 (category Ll: Letter, lowercase)"
@test sprint(show, MIME"text/plain"(), ' ') == "\\thickspace ' ': Unicode U+2005 (category Zs: Separator, space)"

@test length("⊊") == 2
@test EmojiSymbols.latex_name_table["⊊"] == "\\varsubsetneqq"
@test sprint(show, MIME"text/plain"(), "⊊") == string("\\varsubsetneqq ", repr("⊊"))

end # module test_emojisymbols_latex


text:
module magpoly
include("structures_and_functions.jl")

#############################################################################################################
#############################################################################################################

"""
    MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Runs a MC simulation of the magnetic polymer by indexing the monte carlo
steps from 'start' to 'finish'. Modifications are done on the pol::Magnetic_polymer
and the data is stored in the traj::MC_data at the positions from 'start' to 'finish'
"""
function MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MC_run_base!(pol, traj, start, finish, inv_temp, J, fields, quenched_spins)
end

"""
    MC_run!(pol::Magnetic_polymer, traj::MC_data, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Same thins as MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, quenched_spins::Bool=false)
but start and finish are defaulted to 1 and n_steps
"""
function MC_run!(pol::Magnetic_polymer, traj::MC_data, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MC_run_base!(pol, traj, 1, traj.n_steps, inv_temp, J, fields, quenched_spins)
end

function MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, sample_lag::Int, n_samples::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MC_run_base!(pol, traj, start, min(finish, sample_lag*n_samples), inv_temp, J, fields, quenched_spins)
end

"""
    MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, spins_configs::Matrix{Int}, sample_lag::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Still performs the monte carlo simulation but one every 'sample_lag' steps the spins configuration is stored.
The configurations are stored in spins_configs::Matrix{Int}
"""
function MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, spins_configs::Matrix{Int}, sample_lag::Int, n_samples::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MC_run_save!(pol, traj, start, finish, spins_configs, sample_lag, n_samples, inv_temp, J, fields, quenched_spins)
end

"""
    MC_run!(pol::Magnetic_polymer, traj::MC_data, spins_configs::Matrix{Int}, sample_lag::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Monte Carlo that saves configurations as above but defaults starting and ending steps to 1 and n_steps
"""
function MC_run!(pol::Magnetic_polymer, traj::MC_data, spins_configs::Matrix{Int}, sample_lag::Int, n_samples::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MC_run_save!(pol, traj, 1, traj.n_steps, spins_configs, sample_lag, n_samples, inv_temp, J, fields, quenched_spins)
end

"""
    MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, spins_configs::Matrix{Int}, ising_energies::Array{Float64}, sample_lag::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Not only the spin configurations are saved every 'sample_lag' steps but also the ising part of the energy.
The configurations are stored in spins_configs::Matrix{Int} and the energies in ising_energies::Array{Float64}
"""
function MC_run!(pol::Magnetic_polymer, traj::MC_data, start::Int, finish::Int, spins_configs::Matrix{Int}, ising_energies::Array{Float64}, sample_lag::Int, n_samples::Int, inv_temp::Float64, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MC_run_save!(pol, traj, start, finish, spins_configs, ising_energies, sample_lag, n_samples, inv_temp, J, fields, quenched_spins)
end

#############################################################################################################
#############################################################################################################

"""
    MMC_run!(polymers::Array{Magnetic_polymer}, trajs::Array{MC_data},
    n_strides::Int, stride::Int, inv_temps::Array{Float64}, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Performs simulation with Multiple Markov Chains (MMC) attempting a swap between Chains
at two contiguous temperatures every 'stride' steps. The number of attempet
swaps is 'n_strides' and the values of the inverse temperatures of the
different Markov chains are in the array 'inv_temps'
"""
function MMC_run!(polymers::Array{Magnetic_polymer}, trajs::Array{MC_data},
    n_strides::Int, stride::Int, inv_temps::Array{Float64}, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MMC_run_base!(polymers, trajs, n_strides ,stride, inv_temps, J, fields, quenched_spins)
end

"""
    MMC_run!(polymers::Array{Magnetic_polymer}, trajs::Array{MC_data},
    n_strides::Int, stride::Int, inv_temps::Array{Float64}, spins_configs::Matrix{Int}, sample_lag::Int, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Same thing as above but the spins configurations are saved once every 'sample_lag' steps. (only for the chain with lower temperature)
"""
function MMC_run!(polymers::Array{Magnetic_polymer}, trajs::Array{MC_data},
    n_strides::Int, stride::Int, inv_temps::Array{Float64}, spins_configs::Matrix{Int}, sample_lag::Int,n_samples::Int, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MMC_run_save!(polymers, trajs, n_strides ,stride, inv_temps,spins_configs,sample_lag,n_samples, J, fields, quenched_spins)
end

"""
    MMC_run!(polymers::Array{Magnetic_polymer}, trajs::Array{MC_data},
    n_strides::Int, stride::Int, inv_temps::Array{Float64}, spins_configs::Matrix{Int}, ising_energies::Array{Float64}, sample_lag::Int, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
Same thing as above but the spins configurations are saved once every 'sample_lag' steps and also the ising part of the energy. (only for the chain with lower
temperature)
"""
function MMC_run!(polymers::Array{Magnetic_polymer}, trajs::Array{MC_data},
    n_strides::Int, stride::Int, inv_temps::Array{Float64}, spins_configs::Matrix{Int}, ising_energies::Array{Float64}, sample_lag::Int,n_samples::Int, J::Float64, fields::Array{Float64}, quenched_spins::Bool=false)
    MMC_run_save!(polymers, trajs, n_strides ,stride, inv_temps,spins_configs, ising_energies, sample_lag,n_samples, J, fields, quenched_spins)
end

#############################################################################################################
#############################################################################################################

function write_results(pol::Magnetic_polymer, traj::MC_data)
    !isdir("simulation_data") && mkdir("simulation_data")

    open("simulation_data/final_config_$(pol.n_mono).txt", "w") do io
        writedlm(io, [transpose(pol.coord) pol.spins])
    end
    open("simulation_data/MC_data_$(pol.n_mono).txt", "w") do io
        writedlm(io, [traj.energies traj.magnetization traj.rg2])
    end
end

function write_results(pol::Magnetic_polymer)
    !isdir("simulation_data") && mkdir("simulation_data")

    open("simulation_data/final_config_$(pol.n_mono).txt", "w") do io
        writedlm(io, [transpose(pol.coord) pol.spins])
    end
end

end # end of module


text:
# MIT license
# Copyright (c) Microsoft Corporation. All rights reserved.
# See LICENSE in the project root for full license information.

using OpticSim # this will only work after the main build steps are completed
@info "Running representative workload"
# add stuff here #
Examples.autodrawrays()
Examples.hexapolarspotdiagramexample()
@info "Finished running representative workload"


text:
module Motions

export induce_velocity, induce_velocity!, allocate_velocity,
       self_induce_velocity, self_induce_velocity!,
       mutually_induce_velocity!, reset_velocity!,
       advect, advect!

using ..Properties
using ..Elements
using ..RigidBodyMotions

"""
    induce_velocity(target, element, time)

Compute the velocity induced by `element` on `target`

`target` can be:

- a `ComplexF64`
- a subtype of `Vortex.PointSource`
- an array or tuple of vortex elements

while the `element` can be:

- any subtype of `Vortex.Element`
- an array or tuple of vortex elements

# Example
```jldoctest
julia> z = rand(ComplexF64)
0.23603334566204692 + 0.34651701419196046im

julia> point = Vortex.Point(z, rand());

julia> srcs = Vortex.Point.(rand(ComplexF64, 10), rand(10));

julia> induce_velocity(z, srcs[1], 0.0)
0.08722212007570912 + 0.14002850279102955im

julia> induce_velocity(point, srcs[1], 0.0)
0.08722212007570912 + 0.14002850279102955im

julia> induce_velocity(z, srcs, 0.0)
-0.4453372874427177 - 0.10592646656959151im

julia> induce_velocity(point, srcs, 0.0)
-0.4453372874427177 - 0.10592646656959151im
```
"""
@property begin
    signature = induce_velocity(targ::Target, src::Source, t)
    preallocator = allocate_velocity
    stype = ComplexF64
end

@doc """
    allocate_velocity(srcs)

Allocate arrays of `ComplexF64` to match the structure of `srcs`

# Example

```jldoctest
julia> points = Vortex.Point.(rand(ComplexF64, 2), rand(2));

julia> blobs  = Vortex.Blob.(rand(ComplexF64, 3), rand(3), rand(3));

julia> allocate_velocity(points)
2-element Array{Complex{Float64},1}:
 0.0 + 0.0im
 0.0 + 0.0im

julia> allocate_velocity((points, blobs))
(Complex{Float64}[0.0+0.0im, 0.0+0.0im], Complex{Float64}[0.0+0.0im, 0.0+0.0im, 0.0+0.0im])
```
""" allocate_velocity

@doc """
    induce_velocity!(vels, target, element, time)

Compute the velocity induced by `element` on `target` and store the result in `vels`

`vels` should be the output of a call to [`allocate_velocity`](@ref),
`target` can be an array or tuple of vortex elements,
while the `element` can be:
- any subtype of `Vortex.Element`
- an array or tuple of vortex elements

# Example

```jldoctest
julia> cluster₁ = Vortex.Point.(rand(ComplexF64, 5), rand(5));

julia> cluster₂ = Vortex.Point.(rand(ComplexF64, 5), rand(5));

julia> targets = (cluster₁, cluster₂);

julia> sources = Vortex.Blob.(rand(ComplexF64), rand(10), 0.1);

julia> ẋs = allocate_velocity(targets);

julia> induce_velocity!(ẋs, targets, sources, 0.0)
(Complex{Float64}[-1.28772-1.82158im, 1.9386-1.64147im, -1.56438+1.57158im, -0.626254+0.375842im, -0.806568-0.213201im], Complex{Float64}[-0.583672-2.26031im, -0.329778-1.43388im, 0.426927+1.55352im, -0.93755+0.241361im, -1.08949-0.35598im])
```
""" induce_velocity!

"""
    mutually_induce_velocity!(vs₁, vs₂, e₁, e₂, t)

Compute the mutually induced velocities between `e₁` and `e₂` at time
`t` and store the results in `vs₁` and `vs₂`

The default implementation simply calls [`induce_velocity!`](@ref)
twice.  This method is meant to be overwritten to take advantage of
symmetries in certain pairwise vortex interations.  For example, the
velocity kernel for a point vortex is antisymmetric, so in computing
the mutually induced velocities of two arrays of point vortices, we
can half the number of calls to the velocity kernel.
"""
function mutually_induce_velocity!(ws₁, ws₂, v₁, v₂, t)
    induce_velocity!(ws₁, v₁, v₂, t)
    induce_velocity!(ws₂, v₂, v₁, t)
    nothing
end

"""
    self_induce_velocity!(vels, elements, time)

Compute the self induced velocity of one or more vortex elements

This involves a recursive call to `self_induce_velocity!` and pairwise
calls to [`mutually_induce_velocity!`](@ref).

# Example

```jldoctest
julia> points = Vortex.Point.([-1, 1], 1.0)
2-element Array{PotentialFlow.Points.Point{Float64},1}:
 Vortex.Point(-1.0 + 0.0im, 1.0)
 Vortex.Point(1.0 + 0.0im, 1.0)

julia> vels = allocate_velocity(points)
2-element Array{Complex{Float64},1}:
 0.0 + 0.0im
 0.0 + 0.0im

julia> self_induce_velocity!(vels, points, 0.0) # should be ±0.25im/π
2-element Array{Complex{Float64},1}:
 0.0 - 0.07957747154594767im
 0.0 + 0.07957747154594767im
```
"""
function self_induce_velocity!(out, group::Tuple, t)
    N = length(group)
    for s in 1:N
        self_induce_velocity!(out[s], group[s], t)
        for t in s+1:N
            mutually_induce_velocity!(out[s], out[t], group[s], group[t], t)
        end
    end
    out
end

function self_induce_velocity!(out, src, t)
    _self_induce_velocity!(out, Elements.unwrap_src(src), kind(Elements.unwrap_src(src)), t)
end

function _self_induce_velocity!(out, src, ::Type{Group}, t)
    N = length(src)
    for s in 1:N
        out[s] += self_induce_velocity(src[s], t)
        for t in s+1:N
            out[s] += induce_velocity(src[s], src[t], t)
            out[t] += induce_velocity(src[t], src[s], t)
        end
    end
    out
end

self_induce_velocity(src, t) = _self_induce_velocity(src, t, kind(Elements.unwrap_src(src)))
_self_induce_velocity(src, t, ::Type{Singleton}) = zero(ComplexF64)
function _self_induce_velocity(src, t, ::Type{Group})
    out = allocate_velocity(src)
    self_induce_velocity!(out, src, t)
end

"""
    reset_velocity!(vels[, srcs])

Set all velocities in `vels` to zero

If `srcs` is provided, then the arrays in `vels` are resized their source counterpart, if necessary.

# Example

```jldoctest
julia> ẋs = (rand(ComplexF64, 1), rand(ComplexF64, 1))
(Complex{Float64}[0.236033+0.346517im], Complex{Float64}[0.312707+0.00790928im])

julia> points = Vortex.Point.(rand(ComplexF64, 2), rand(2));

julia> blobs  = Vortex.Blob.(rand(ComplexF64, 3), rand(3), rand(3));

julia> reset_velocity!(ẋs, (points, blobs));

julia> ẋs
(Complex{Float64}[0.0+0.0im, 0.0+0.0im], Complex{Float64}[0.0+0.0im, 0.0+0.0im, 0.0+0.0im])
```
"""
reset_velocity!(array::Vector{ComplexF64}) = fill!(array, zero(ComplexF64))

function reset_velocity!(group::Tuple)
    for ẋ in group
        reset_velocity!(ẋ)
    end
    group
end

function reset_velocity!(array::Vector{ComplexF64}, v)
    if length(array) != length(v)
        resize!(array, length(v))
    end
    fill!(array, zero(ComplexF64))
end

function reset_velocity!(group::T, src) where {T <: Tuple}
    for i in 1:length(group)
        reset_velocity!(group[i], src[i])
    end
    group
end

function reset_velocity!(m::RigidBodyMotion, src)
    m.ċ = m.c̈ = zero(ComplexF64)
    m.α̇ = zero(ComplexF64)
    m
end

function reset_velocity!(ẋ::ComplexF64,src)
    ẋ = zero(ComplexF64)
end

"""
    advect(src::Element, velocity::ComplexF64, Δt)

Return a new element that represents `src` advected by `velocity` over
`Δt`.

 If this method is implemented by any type `T` where `kind(T)` is a
`Singleton`, then an array of type `AbstractArray{T}` can be passed in
the first two arguments of [`advect!`](@ref).
Note that this method is usually only defined for singleton elements

# Example

```jldoctest
julia> point = Vortex.Point(1.0 + 0.0, 1.0);

julia> advect(point, 1.0im, 1e-2)
Vortex.Point(1.0 + 0.01im, 1.0)
```
"""
function advect end

"""
    advect!(srcs₊, srcs₋, vels, Δt)

Moves the elements in `srcs₋` by their corresponding velocity in
`vels` over the interval `Δt` and store the results in `src₊`.

`srcs₋` and `srcs₊` can be either a array of vortex elements or a tuple.

# Example

```jldoctest
julia> points₋ = [Vortex.Point(x + 0im, 1.0) for x in 1:5];

julia> points₊ = Vector{Vortex.Point}(undef, 5);

julia> vels = [ y*im for y in 1.0:5 ];

julia> advect!(points₊, points₋, vels, 1e-2);

julia> points₊
5-element Array{PotentialFlow.Points.Point{Float64},1}:
 Vortex.Point(1.0 + 0.01im, 1.0)
 Vortex.Point(2.0 + 0.02im, 1.0)
 Vortex.Point(3.0 + 0.03im, 1.0)
 Vortex.Point(4.0 + 0.04im, 1.0)
 Vortex.Point(5.0 + 0.05im, 1.0)
```
"""
function advect!(vs₊::T, vs₋::T, w, Δt) where
    {T <: Union{AbstractArray, Tuple}}

    for (i, v) in enumerate(vs₋)
        vs₊[i] = advect(v, w[i], Δt)
    end
    vs₊
end

function advect!(group₊::T, group₋::T, ws, Δt) where {T <: Tuple}
    for i in 1:length(group₋)
        advect!(group₊[i], group₋[i], ws[i], Δt)
    end
    group₊
end

end


text:
# This only declares these constants with dummy values. They are
# initialized at run time in the file
# `read_load_time_mpi_constants.jl`.

module Consts

import ..MPI_Comm
import ..MPI_Datatype
import ..MPI_Errhandler
import ..MPI_File
import ..MPI_Group
import ..MPI_Info
import ..MPI_Message
import ..MPI_Offset
import ..MPI_Op
import ..MPI_Request
import ..MPI_Win

# We want to be able to use these values for comparisons while
# precompiling, so they should all be different
_mpi_val_counter = 1

const MPI_ANY_SOURCE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ANY_TAG = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_PROC_NULL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ROOT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_CART = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_DIST_GRAPH = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_GRAPH = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_CONGRUENT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_IDENT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_SIMILAR = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_UNEQUAL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_KEYVAL_INVALID = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_UNDEFINED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_APPNUM = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_HOST = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_IO = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_LASTUSEDCODE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_TAG_UB = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_UNIVERSE_SIZE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_WIN_BASE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_WIN_CREATE_FLAVOR = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_WIN_DISP_UNIT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_WIN_MODEL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_WIN_SIZE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_WTIME_IS_GLOBAL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_CONTIGUOUS = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_DARRAY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_DUP = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_F90_COMPLEX = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_F90_INTEGER = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_F90_REAL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_HINDEXED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_HINDEXED_BLOCK = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_HVECTOR = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_INDEXED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_INDEXED_BLOCK = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_NAMED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_RESIZED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_STRUCT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_SUBARRAY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMBINER_VECTOR = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_COMM_TYPE_SHARED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_DISTRIBUTE_BLOCK = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_DISTRIBUTE_CYCLIC = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_DISTRIBUTE_NONE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_ACCESS = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_AMODE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_ARG = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_ASSERT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_BAD_FILE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_BASE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_BUFFER = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_COMM = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_CONVERSION = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_COUNT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_DIMS = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_DISP = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_DUP_DATAREP = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_FILE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_FILE_EXISTS = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_FILE_IN_USE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_GROUP = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_INFO = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_INFO_KEY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_INFO_NOKEY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_INFO_VALUE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_INTERN = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_IN_STATUS = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_IO = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_KEYVAL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_LASTCODE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_LOCKTYPE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_NAME = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_NOT_SAME = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_NO_MEM = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_NO_SPACE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_NO_SUCH_FILE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_OP = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_OTHER = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_PENDING = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_PORT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_QUOTA = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_RANK = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_READ_ONLY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_REQUEST = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_RMA_ATTACH = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_RMA_CONFLICT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_RMA_FLAVOR = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_RMA_RANGE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_RMA_SHARED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_RMA_SYNC = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_ROOT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_SERVICE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_SIZE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_SPAWN = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_TAG = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_TOPOLOGY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_TRUNCATE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_TYPE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_UNKNOWN = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_UNSUPPORTED_DATAREP = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_UNSUPPORTED_OPERATION = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ERR_WIN = Ref(Cint(-(_mpi_val_counter += 1)))
# We special-case MPI_SUCCESS which is required to be 0
# const MPI_SUCCESS = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_SUCCESS = Ref(Cint(0))
const MPI_LOCK_EXCLUSIVE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_LOCK_SHARED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_APPEND = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_CREATE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_DELETE_ON_CLOSE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_EXCL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_NOCHECK = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_NOPRECEDE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_NOPUT = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_NOSTORE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_NOSUCCEED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_RDONLY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_RDWR = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_SEQUENTIAL = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_UNIQUE_OPEN = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_MODE_WRONLY = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ORDER_C = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_ORDER_FORTRAN = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_SEEK_CUR = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_SEEK_END = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_SEEK_SET = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_THREAD_SINGLE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_THREAD_FUNNELED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_THREAD_SERIALIZED = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_THREAD_MULTIPLE = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_TYPECLASS_COMPLEX = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_TYPECLASS_INTEGER = Ref(Cint(-(_mpi_val_counter += 1)))
const MPI_TYPECLASS_REAL = Ref(Cint(-(_mpi_val_counter += 1)))

const MPI_ARGV_NULL = Ref(C_NULL)
const MPI_ARGVS_NULL = Ref(C_NULL)
const MPI_UNWEIGHTED = Ref(C_NULL)
const MPI_WEIGHTS_EMPTY = Ref(C_NULL)
const MPI_BOTTOM = Ref(C_NULL)
const MPI_IN_PLACE = Ref(C_NULL)

const MPI_COMM_NULL = Ref(MPI_Comm(_mpi_val_counter += 1))
const MPI_COMM_SELF = Ref(MPI_Comm(_mpi_val_counter += 1))
const MPI_COMM_WORLD = Ref(MPI_Comm(_mpi_val_counter += 1))

const MPI_COMM_DUP_FN = Ref(C_NULL)

const MPI_COMM_NULL_COPY_FN = Ref(C_NULL)

const MPI_COMM_NULL_DELETE_FN = Ref(C_NULL)

const MPI_NULL_COPY_FN = Ref(C_NULL)

const MPI_2DOUBLE_PRECISION = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_2INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_2INTEGER = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_2REAL = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_AINT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_BYTE = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_CHAR = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_CHARACTER = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_COMPLEX16 = Ref(MPI_Datatype(_mpi_val_counter += 1))
# const MPI_COMPLEX32 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_COMPLEX8 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_COUNT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_CXX_BOOL = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_CXX_DOUBLE_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_CXX_FLOAT_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_CXX_LONG_DOUBLE_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_C_BOOL = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_C_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_C_DOUBLE_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_C_FLOAT_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_C_LONG_DOUBLE_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_DATATYPE_NULL = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_DOUBLE = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_DOUBLE_COMPLEX = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_DOUBLE_INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_DOUBLE_PRECISION = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_FLOAT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_FLOAT_INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INT16_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INT32_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INT64_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INT8_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INTEGER = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INTEGER1 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INTEGER2 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INTEGER4 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_INTEGER8 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_LOGICAL = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_LONG = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_LONG_DOUBLE = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_LONG_DOUBLE_INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_LONG_INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_LONG_LONG = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_LONG_LONG_INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_OFFSET = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_PACKED = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_REAL = Ref(MPI_Datatype(_mpi_val_counter += 1))
# const MPI_REAL16 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_REAL4 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_REAL8 = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_SHORT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_SHORT_INT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_SIGNED_CHAR = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UINT16_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UINT32_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UINT64_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UINT8_T = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UNSIGNED = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UNSIGNED_CHAR = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UNSIGNED_LONG = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UNSIGNED_LONG_LONG = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_UNSIGNED_SHORT = Ref(MPI_Datatype(_mpi_val_counter += 1))
const MPI_WCHAR = Ref(MPI_Datatype(_mpi_val_counter += 1))

const MPI_NULL_DELETE_FN = Ref(C_NULL)

const MPI_ERRHANDLER_NULL = Ref(MPI_Errhandler(_mpi_val_counter += 1))
const MPI_ERRORS_ARE_FATAL = Ref(MPI_Errhandler(_mpi_val_counter += 1))
const MPI_ERRORS_RETURN = Ref(MPI_Errhandler(_mpi_val_counter += 1))

const MPI_FILE_NULL = Ref(MPI_File(_mpi_val_counter += 1))

const MPI_GROUP_EMPTY = Ref(MPI_Group(_mpi_val_counter += 1))
const MPI_GROUP_NULL = Ref(MPI_Group(_mpi_val_counter += 1))

const MPI_INFO_ENV = Ref(MPI_Info(_mpi_val_counter += 1))
const MPI_INFO_NULL = Ref(MPI_Info(_mpi_val_counter += 1))

const MPI_MESSAGE_NO_PROC = Ref(MPI_Message(_mpi_val_counter += 1))
const MPI_MESSAGE_NULL = Ref(MPI_Message(_mpi_val_counter += 1))

const MPI_DISPLACEMENT_CURRENT = Ref(MPI_Offset(_mpi_val_counter += 1))

const MPI_BAND = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_BOR = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_BXOR = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_LAND = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_LOR = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_LXOR = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_MAX = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_MAXLOC = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_MIN = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_MINLOC = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_NO_OP = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_OP_NULL = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_PROD = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_REPLACE = Ref(MPI_Op(_mpi_val_counter += 1))
const MPI_SUM = Ref(MPI_Op(_mpi_val_counter += 1))

const MPI_REQUEST_NULL = Ref(MPI_Request(_mpi_val_counter += 1))

const MPI_STATUS_IGNORE = Ref(C_NULL)
const MPI_STATUSES_IGNORE = Ref(C_NULL)

const MPI_TYPE_DUP_FN = Ref(C_NULL)

const MPI_TYPE_NULL_COPY_FN = Ref(C_NULL)

const MPI_TYPE_NULL_DELETE_FN = Ref(C_NULL)

const MPI_WIN_NULL = Ref(MPI_Win(_mpi_val_counter += 1))

const MPI_WIN_DUP_FN = Ref(C_NULL)
const MPI_WIN_NULL_COPY_FN = Ref(C_NULL)

const MPI_WIN_NULL_DELETE_FN = Ref(C_NULL)

end

# These functions are run after reading the values of the constants above)
const _mpi_load_time_hooks = Any[]
const _finished_loading = Ref(false)
function add_load_time_hook!(f)
    @assert !_finished_loading[]
    push!(_mpi_load_time_hooks, f)
end
function run_load_time_hooks()
    @assert !_finished_loading[]
    _finished_loading[] = true
    for hook in _mpi_load_time_hooks
        hook()
    end
    empty!(_mpi_load_time_hooks)
    nothing
end


text:
using RoboticsToolkit
using Test

@testset "RoboticsToolkit.jl" begin
    # Write your tests here.
end


text:
"""
    @unsafe_wrap(array::Symbol, len::Symbol) -> Expr

Wrap a Julia `Array` object around the data at the address given by `array[]` pointer with
length equal to `len[]`.

# Arguments
- `array::Symbol`: name of the variable which holds the pointer to the array data
- `len::Symbol`: name of the variable which holds the pointer to the length of this array

# Returns
- `Expr`: a wrapping expression

# Example
```jldoctest; output = false
using PDFHighlights

_array = :array
_len = :len

@macroexpand(PDFHighlights.Internal.PDF.@unsafe_wrap(array, len)) ==
:(unsafe_wrap(Array, \$(_array)[], \$(_len)[]; own = true))

# output

true
```

See also: [`@unsafe_wrap_strings`](@ref)
"""
macro unsafe_wrap(array::Symbol, len::Symbol)
    return esc(:(unsafe_wrap(Array, $(array)[], $(len)[]; own = true)))
end

"""
    @unsafe_wrap(array::Expr, len::Union{Symbol, Expr}) -> Expr

Wrap a Julia `Array` object around the data at the address given by `array` pointer with
length equal to `len`.

# Arguments
- `array::Expr`: expression that will yield a pointer to the array data
- `len::Union{Symbol, Expr}`: name of the variable which holds the length of this array,
  or an expression that will yield it

# Returns
- `Expr`: a wrapping expression

# Example
```jldoctest; output = false
using PDFHighlights

_array = :(array[index])
_len = :len

@macroexpand(PDFHighlights.Internal.PDF.@unsafe_wrap(array[index], len)) ==
:(unsafe_wrap(Array, \$(_array), \$(_len); own = true))

# output

true
```

See also: [`@unsafe_wrap_strings`](@ref)
"""
macro unsafe_wrap(array::Expr, len::Union{Symbol, Expr})
    return esc(:(unsafe_wrap(Array, $(array), $(len); own = true)))
end

"""
    @unsafe_wrap_strings(array::Union{Symbol, Expr}, len::Union{Symbol, Expr}) -> Expr

Wrap a Julia `Array` object around the array of C-style strings at the address given by
`array` (or `array[]`) pointer with length equal to `len` (or `len[]`); convert each
string to a Julia string encoded as UTF-8.

# Arguments
- `array::Union{Symbol, Expr}`: name of the variable which holds the pointer to the array
  data, or expression that will yield it
- `len::Union{Symbol, Expr}`: name of the variable which holds the length of this array
  (or a pointer to it), or expression that will yield it

# Returns
- `Expr`: a wrapping expression

# Example
```jldoctest; output = false
using PDFHighlights
using PDFHighlights: Internal.PDF.@unsafe_wrap

_array = :array
_len = :len

@macroexpand(PDFHighlights.Internal.PDF.@unsafe_wrap_strings(array, len)) ==
:(unsafe_string.(unsafe_wrap(Array, \$(_array)[], \$(_len)[]; own = true)))

# output

true
```

See also: [`@unsafe_wrap`](@ref)
"""
macro unsafe_wrap_strings(array::Union{Symbol, Expr}, len::Union{Symbol, Expr})
    return esc(:(unsafe_string.(@unsafe_wrap $(array) $(len))))
end

"""
    get_highlights_comments_pages(
        target::String;
        concatenate::Bool=true
    ) -> Tuple{Vector{String}, Vector{String}, Vector{Int32}}

Extract the highlights, comments, and pages from a passed PDF or all PDFs found recursively
in the passed directory.

# Arguments
- `target::String`: $(TARGET_PDF_ARGUMENT)

# Keywords
- `concatenate::Bool=true`: $(CONCATENATE_KEYWORD)

# Returns
- `Tuple{Vector{String}, Vector{String}, Vector{Int32}}`: the highlights, comments,
  and pages

# Throws
- [`DoesNotExist`](@ref): $(DOES_NOT_EXIST_EXCEPTION)
- [`NotPDF`](@ref): $(NOT_PDF_EXCEPTION)

# Example
```jldoctest; output = false
using PDFHighlights

path_to_pdf_dir = joinpath(pathof(PDFHighlights) |> dirname |> dirname, "test", "pdf")
path_to_pdf = joinpath(path_to_pdf_dir, "TestPDF.pdf")

get_highlights_comments_pages(path_to_pdf_dir) ==
get_highlights_comments_pages(path_to_pdf) ==
(
    [
        "Highlight 1",
        "Highlight 2 Highlight 3",
        "Highlight 4",
        "Highhighlight 5",
        "6th Highhigh light-",
        "High light 7",
        "8th Highlight-",
    ],
    ["Comment 1", "Comment 2 Comment 3", "Comment 4", "", "", "", ""],
    Int32[1, 2, 4, 6, 7, 8, 9],
)

# output

true
```
"""
function get_highlights_comments_pages(
    target::String;
    concatenate::Bool=true,
)::Tuple{Vector{String}, Vector{String}, Vector{Int32}}

    if isdir(target)

        highlights = String[]
        comments = String[]
        pages = Int32[]

        for (root, dirs, files) in walkdir(target), file in files
            if endswith(file, ".pdf")
                _highlights, _comments, _pages = get_highlights_comments_pages(
                    joinpath(root, file);
                    concatenate,
                )
                highlights = vcat(highlights, _highlights)
                comments = vcat(comments, _comments)
                pages = vcat(pages, _pages)
            end
        end

        return highlights, comments, pages

    end

    !isfile(target) && throw(DoesNotExist(target))
    !endswith(target, ".pdf") && throw(NotPDF(target))

    # Compute the check sum
    checksum = ""
    open(target, "r") do io
        checksum = bytes2hex(sha1(io))
    end

    get!(GET_HIGHLIGHTS_COMMENTS_PAGES_OUTPUTS, (checksum, concatenate)) do

        # Prepare pointers
        lines_arrays_ref = Ref{Ptr{Ptr{Cstring}}}()
        lines_x_anchors_arrays_ref = Ref{Ptr{Ptr{Cdouble}}}()
        lines_yl_anchors_arrays_ref = Ref{Ptr{Ptr{Cdouble}}}()
        lines_yu_anchors_arrays_ref = Ref{Ptr{Ptr{Cdouble}}}()
        lines_lens_ref = Ref{Ptr{UInt}}()
        comments_ref = Ref{Ptr{Cstring}}()
        pages_ref = Ref{Ptr{Cint}}()
        num_ref = Ref{UInt}(0)

        # Call
        ccall(
            # Function and library names
            (
                :get_lines_comments_pages,
                PDFHighlightsWrapper,
            ),
            # Type of return value
            Cvoid,
            # Types of the arguments
            (
                Cstring,
                Ref{Ptr{Ptr{Cstring}}},
                Ref{Ptr{Ptr{Cdouble}}},
                Ref{Ptr{Ptr{Cdouble}}},
                Ref{Ptr{Ptr{Cdouble}}},
                Ref{Ptr{UInt}},
                Ref{Ptr{Cstring}},
                Ref{Ptr{Cint}},
                Ref{UInt},
            ),
            # Arguments
            target,
            lines_arrays_ref,
            lines_x_anchors_arrays_ref,
            lines_yl_anchors_arrays_ref,
            lines_yu_anchors_arrays_ref,
            lines_lens_ref,
            comments_ref,
            pages_ref,
            num_ref,
        )

        # Steal the upper-level arrays
        lines_arrays = @unsafe_wrap lines_arrays_ref num_ref
        lines_x_anchors_arrays = @unsafe_wrap lines_x_anchors_arrays_ref num_ref
        lines_yl_anchors_arrays = @unsafe_wrap lines_yl_anchors_arrays_ref num_ref
        lines_yu_anchors_arrays = @unsafe_wrap lines_yu_anchors_arrays_ref num_ref
        lines_lens = @unsafe_wrap lines_lens_ref num_ref
        comments = @unsafe_wrap_strings comments_ref num_ref
        pages = @unsafe_wrap pages_ref num_ref

        highlights = Vector{String}(undef, num_ref[])

        highlights_x_anchors = Vector{Float64}(undef, num_ref[])
        highlights_yl_anchors = Vector{Float64}(undef, num_ref[])
        highlights_yu_anchors = Vector{Float64}(undef, num_ref[])

        # Sort the lines
        for index in 1:num_ref[]

            # Steal the lower-level array
            len = lines_lens[index]
            lines = @unsafe_wrap_strings lines_arrays[index] len
            lines_x_anchors = @unsafe_wrap lines_x_anchors_arrays[index] len
            lines_yl_anchors = @unsafe_wrap lines_yl_anchors_arrays[index] len
            lines_yu_anchors = @unsafe_wrap lines_yu_anchors_arrays[index] len

            # Sort the lines by `x` if they cross each other by `y`
            _sort!(lines, lines_x_anchors, lines_yl_anchors, lines_yu_anchors)

            # Save the highlights anchors
            highlights_x_anchors[index] = lines_x_anchors[1]
            highlights_yl_anchors[index] = lines_yl_anchors[end]
            highlights_yu_anchors[index] = lines_yu_anchors[1]

            # Highlight string
            highlight = ""

            hyphen_chopped = false

            # Remove word transfers
            for (index, line) in enumerate(lines)
                if endswith(line, "-")
                    if hyphen_chopped
                        if index == len
                            highlight = string(highlight, line)
                        else
                            highlight = string(highlight, chop(line))
                        end
                    else
                        if len == 1
                            highlight = string(highlight, line)
                        elseif index == 1
                            highlight = string(highlight, chop(line))
                        elseif index == len
                            highlight = string(highlight, ' ', line)
                        else
                            highlight = string(highlight, ' ', chop(line))
                        end
                    end
                    hyphen_chopped = true
                else
                    if hyphen_chopped
                        highlight = string(highlight, line)
                    else
                        if index == 1
                            highlight = string(highlight, line)
                        else
                            highlight = string(highlight, ' ', line)
                        end
                    end
                    hyphen_chopped = false
                end
            end

            # Combine all lines in a highlight and save it
            highlights[index] = highlight

        end

        # Sort the highlights by `x` if they cross
        # each other and by `y` if they don't
        _sort!(
            highlights,
            comments,
            pages,
            highlights_x_anchors,
            highlights_yl_anchors,
            highlights_yu_anchors,
        )

        if concatenate
            return _concatenate(highlights, comments, pages)
        else
            return highlights, comments, pages
        end

    end

end


text:
# test distance functions

@testset "Forward" begin
    @test_throws ArgumentError forward(0, 2π, 0, 0, 1, 0)
    @test_throws ArgumentError forward(0, 2π, 0, 0, 0, 0)
    @test_throws ArgumentError inverse(0, 0, 1, 1, 1, 1)

    # Compare to Haversine computation on the unit sphere
    let lon0 = 0, lat0 = 0, azimuth = 1, distance = 1, a = 1, f = 0
        lon1, lat1, baz = forward(lon0, lat0, azimuth, distance, a, f)
        @test lon1 ≈ 0.9189892552937267
        @test lat1 ≈ 0.4719777676633856
        @test baz ≈ mod(-1.9047280216911358, 2π) atol=1e-6
    end
end

@testset "Inverse" begin
    @test_throws ArgumentError inverse(0, 0, 1, 1, 0, 0)
    @test_throws ArgumentError inverse(0, 2π, 0, -3π, 1, 1)
    @test_throws ArgumentError inverse(0, 0, 1, 1, 1, 1)

    # Compare to Haversine computation on the unit sphere
    let lon0 = 0, lat0 = 0, lon1 = 1, lat1 = 1, a = 1, f = 0, atol = 1e-6
        distance, azimuth, backazimuth = inverse(lon0, lat0, lon1, lat1, a, f)
        @test distance ≈ 1.2745557823062943 atol=atol
        @test azimuth ≈ 0.49536728921867335 atol=atol
        @test backazimuth ≈ mod(-2.0661636160135703, 2π) atol=atol
    end
end

@testset "Ang dist" begin
    # Degrees
    let lon0 = 360*rand() - 180, lat0 = 180*rand() - 90, lon1 = 360*rand() - 180,
            lat1 = 180*rand() - 90, atol = 1e-6
        @test angular_distance(lon0, lat0, lon1, lat1, f=0) ≈
            rad2deg(inverse(deg2rad.((lon0, lat0, lon1, lat1))..., 1, 0)[1]) atol=atol
        @test angular_distance(lon0, lat0, lon1, lat1) ≈
            rad2deg(inverse(deg2rad.((lon0, lat0, lon1, lat1))..., 1, SeisNoise.F_WGS84)[1]) atol=atol
    end

    # Radians
    let lon0 = 2π*(rand() - 0.5), lat0 = π*(rand() - 0.5), lon1 = 2π*(rand() - 0.5),
            lat1 = π*(rand() - 0.5), atol = 1e-6
        @test angular_distance(lon0, lat0, lon1, lat1, false, f=0) ≈
            inverse(lon0, lat0, lon1, lat1, 1, 0)[1] atol=atol
        @test angular_distance(lon0, lat0, lon1, lat1, false) ≈
            inverse(lon0, lat0, lon1, lat1, 1, SeisNoise.F_WGS84)[1] atol=atol
    end
end

@testset "Azimuth" begin
    # Degrees
    let lon0 = 360*rand() - 180, lat0 = 180*rand() - 90, lon1 = 360*rand() - 180,
            lat1 = 180*rand() - 90, atol = 1e-6
        @test azimuth(lon0, lat0, lon1, lat1, f=0) ≈
            rad2deg(inverse(deg2rad.((lon0, lat0, lon1, lat1))..., 1, 0)[2]) atol=atol
        @test azimuth(lon0, lat0, lon1, lat1) ≈
            rad2deg(inverse(deg2rad.((lon0, lat0, lon1, lat1))..., 1, SeisNoise.F_WGS84)[2]) atol=atol
    end

    # Radians
    let lon0 = 2π*(rand() - 0.5), lat0 = π*(rand() - 0.5), lon1 = 2π*(rand() - 0.5),
            lat1 = π*(rand() - 0.5), atol = 1e-6
        @test azimuth(lon0, lat0, lon1, lat1, false, f=0) ≈
            inverse(lon0, lat0, lon1, lat1, 1, 0)[2] atol=atol
        @test azimuth(lon0, lat0, lon1, lat1, false) ≈
            inverse(lon0, lat0, lon1, lat1, 1, SeisNoise.F_WGS84)[2] atol=atol
    end

    # One point at pole
    let lon0 = 0, lat0 = -90, lon1 = 0, lat1 = 0
        for T in (Int16, Int32, Int64, Float16, Float32, Float64)
            @test azimuth(T(lon0), T(lat0), T(lon1), T(lat1), true) == 0.0
        end
    end
    let lon0 = 90, lat0 = 0, lon1 = 90, lat1 = 90
        for T in (Int16, Int32, Int64, Float16, Float32, Float64)
            @test azimuth(T(lon0), T(lat0), T(lon1), T(lat1), true) == 0.0
        end
    end
end

@testset "Ang step" begin
    # Degrees
    let lon0 = 360*rand() - 180, lat0 = 180*rand() - 90, dist = 360*rand(),
            az = 360*rand() - 90, atol = 1e-6
        @test all(isapprox.(angular_step(lon0, lat0, az, dist, f=0),
            rad2deg.(forward(deg2rad.((lon0, lat0, az, dist))..., 1, 0)), atol=atol))
        @test all(isapprox.(angular_step(lon0, lat0, az, dist),
            rad2deg.(forward(deg2rad.((lon0, lat0, az, dist))..., 1, SeisNoise.F_WGS84)), atol=atol))
    end

    # Radians
    let lon0 = 2π*(rand() - 0.5), lat0 = π*(rand() - 0.5), dist = 2π*rand(),
            az = 2π*(rand() - 0.5), atol = 1e-6
        @test all(isapprox.(angular_step(lon0, lat0, az, dist, false, f=0),
            forward(lon0, lat0, az, dist, 1, 0), atol=atol))
        @test all(isapprox.(angular_step(lon0, lat0, az, dist, false),
            forward(lon0, lat0, az, dist, 1, SeisNoise.F_WGS84), atol=atol))
    end
end

@testset "Surf dist" begin
    # Degrees
    let lon0 = 360*rand() - 180, lat0 = 180*rand() - 90, lon1 = 360*rand() - 180,
            lat1 = 180*rand() - 90, a = 10_000_000*rand(), atol = 1e-6
        @test surface_distance(lon0, lat0, lon1, lat1, a, f=0) ≈
            inverse(deg2rad.((lon0, lat0, lon1, lat1))..., a, 0)[1] atol=atol
        @test surface_distance(lon0, lat0, lon1, lat1, a) ≈
            inverse(deg2rad.((lon0, lat0, lon1, lat1))..., a, SeisNoise.F_WGS84)[1] atol=atol
    end

    # Radians
    let lon0 = 2π*(rand() - 0.5), lat0 = π*(rand() - 0.5), lon1 = 2π*(rand() - 0.5),
            lat1 = π*(rand() - 0.5), a = 10_000_000*rand(), atol = 1e-6
        @test surface_distance(lon0, lat0, lon1, lat1, a, false, f=0) ≈
            inverse(lon0, lat0, lon1, lat1, a, 0)[1] atol=atol
        @test surface_distance(lon0, lat0, lon1, lat1, a, false) ≈
            inverse(lon0, lat0, lon1, lat1, a, SeisNoise.F_WGS84)[1] atol=atol
    end
end

@testset "NoiseData Distance" begin
    # test get_dist
    loc1 = GeoLoc()
    loc2 = GeoLoc()
    @test get_dist(loc1,loc2) == 0

    # second location empty
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    @test get_dist(loc1,loc2) == 0

    # first location empty
    loc1 = GeoLoc()
    loc2.lat = rand(-89.:1e-4:89.,1)[1]
    loc2.lon = rand(-89.:1e-4:89.,1)[1]
    @test get_dist(loc1,loc2) == 0

    # both locations contain coordinates
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    # check dist in km is less than 1/2 Earth circumference
    @test 0 < get_dist(loc1,loc2) < SeisNoise.EARTH_R_MAJOR_WGS84/1000 *  π

    # test get_dist with FFTData
    F1 = FFTData()
    F1.loc = loc1
    F2 = FFTData()
    F2.loc = loc2
    @test 0 < get_dist(F1,F2) < SeisNoise.EARTH_R_MAJOR_WGS84/1000 *  π

    # test get_azi
    loc1 = GeoLoc()
    loc2 = GeoLoc()
    @test get_azi(loc1,loc2) == 0

    # second location empty
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    @test get_azi(loc1,loc2) == 0

    # first location empty
    loc1 = GeoLoc()
    loc2.lat = rand(-89.:1e-4:89.,1)[1]
    loc2.lon = rand(-89.:1e-4:89.,1)[1]
    @test get_azi(loc1,loc2) == 0

    # both locations contain coordinates
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    @test 0 <= get_azi(loc1,loc2) <= 360

    # test get_azi with FFTData
    F1 = FFTData()
    F1.loc = loc1
    F2 = FFTData()
    F2.loc = loc2
    @test 0 <= get_azi(F1,F2) <= 360

    # test get_baz
    loc1 = GeoLoc()
    loc2 = GeoLoc()
    @test get_baz(loc1,loc2) == 0

    # second location empty
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    @test get_baz(loc1,loc2) == 0

    # first location empty
    loc1 = GeoLoc()
    loc2.lat = rand(-89.:1e-4:89.,1)[1]
    loc2.lon = rand(-89.:1e-4:89.,1)[1]
    @test get_baz(loc1,loc2) == 0

    # both locations contain coordinates
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    @test 0 <= get_baz(loc1,loc2) <= 360

    # test get_azi with FFTData
    F1 = FFTData()
    F1.loc = loc1
    F2 = FFTData()
    F2.loc = loc2
    @test 0 <= get_baz(F1,F2) <= 360

    # test get_loc
    azi = rand(0:360.,1)[1]
    dist = rand(1:SeisNoise.EARTH_R_MAJOR_WGS84/1000 *  π,1)[1]
    @test isa(get_loc(loc1,azi,dist),GeoLoc)
    @test !isempty(get_loc(loc1,azi,dist))
    @test isempty(get_loc(GeoLoc(),azi,dist))

    loc1 = GeoLoc()
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    loc2 = GeoLoc()
    loc2.lat = rand(-89.:1e-4:89.,1)[1]
    loc2.lon = rand(-89.:1e-4:89.,1)[1]
    loc3 = get_loc(loc1,get_azi(loc1,loc2),get_dist(loc1,loc2))
    @test isapprox(loc2.lat,loc3.lat,atol=1e-5) # test to ~ 1m accuracy
    @test isapprox(loc2.lon,loc3.lon,atol=1e-5) # test to ~ 1m accuracy

    # test get_loc with CorrData
    C = CorrData()

    # test empty location
    @test isa(get_loc(C)[2],GeoLoc)
    @test isempty(get_loc(C)[2])

    # test with loc1
    loc1 = GeoLoc()
    loc1.lat = rand(-89.:1e-4:89.,1)[1]
    loc1.lon = rand(-89.:1e-4:89.,1)[1]
    loc2 = GeoLoc()
    loc2.lat = rand(-89.:1e-4:89.,1)[1]
    loc2.lon = rand(-89.:1e-4:89.,1)[1]
    C.loc = loc1
    C.azi = get_azi(loc1,loc2)
    C.dist = get_dist(loc1,loc2)
    loc4 = get_loc(C)[2]
    @test isapprox(loc2.lat,loc4.lat,atol=1e-5) # test to ~ 1m accuracy
    @test isapprox(loc2.lon,loc4.lon,atol=1e-5) # test to ~ 1m accuracy
end


text:
module SpinGlassPEPS

    using Reexport
    @reexport using SpinGlassTensors, SpinGlassNetworks, SpinGlassEngine

    using Requires
    function __init__()
        @require CUDA="052768ef-5323-5732-b1bb-66c8b64840ba" begin
            if CUDA.functional() && CUDA.has_cutensor() && false
                const CuArray = CUDA.CuArray
                const CuVector = CUDA.CuVector
                const CuMatrix = CUDA.CuMatrix
                const CuSVD = CUDA.CUSOLVER.CuSVD
                const CuQR = CUDA.CUSOLVER.CuQR
                const cu = CUDA.cu
                using CUDA
                # const cuda = CUDA.@cuda

                CUDA.allowscalar(false)

                @inline function cudiv(x::Int)
                    max_threads = 256
                    threads_x = min(max_threads, x)
                    threads_x, ceil(Int, x/threads_x)
                end
                include("cuda/base.jl")
                include("cuda/utils.jl")
                include("cuda/contractions.jl")
                include("cuda/compressions.jl")
                include("cuda/spectrum.jl")
            end
        end
    end
end


text:
# calculating the length / size of strings.
# If the string contains 'special' characters these may be different.
s1 = "ángeł"
println("the length of s1 is : ", length(s1))
println("the size of s1 is : ", sizeof(s1))

# concatenate two strings
s2 = "Hello" * " " * "World"
println(s2)

# repeat a string
s3 = "ABCD"^3
println(s3)

# substrings
s4 = "There once was a little pig named Dave"
println(findnext("once", s4, 1)) # substring / string / start_at - returns the 'range' where the substring is found
println(occursin("Dave", s4))
println(occursin("Bob", s4))

# string padding - spaces is the default
s5 = "Whats up today"
println(lpad(s5, 25)) # the 'n' value is the total length of the string once padding has been applied - so a padding value of 10 will leave the string unchanged
println(rpad(s5, 25, '*')) # you can specify a character to pad with

# create a string from an array
a1 = ["Lions", "Tigers", "Dogs", "Cats"]
println(join(a1, '/', " and ")) # args to join - array / join char or string / join char or string between the last two elements

max_stars_repo_name: rpillar/JuliaExamples

max_issues_repo_name: rpillar/JuliaExamples

max_forks_repo_name: rpillar/JuliaExamples


text:
@testset "Test multiple shooting" begin
    # Define logistic map
    function logistic_f(x_next, x, k, θ)
        x_next .=  θ*x - θ*x.^2
        return
    end
    function logistic_g(y, x, k, θ)
        y .= x
        return
    end
    # Define initial conditions
    x0 = [0.5]
    x0_list = Vector{Vector{Float64}}(10)
    x0_list[1] = copy(x0)
    grid = collect(1:10:101)
    k0_list = grid[1:end-1]
    # Initialize buffer
    y = Vector{Vector{Float64}}(100)
    for i = 1:length(y)
        y[i] = zeros(1)
    end
    # Simulate
    x_final = copy(x0)
    for i = 1:length(grid)-1
        x_final = ms.simulate_space_state!(y[grid[i]:grid[i+1]-1],
                                           logistic_f, logistic_g,
                                           x0, (grid[i], grid[i+1]-1), (3.78,))
        if i != length(grid)-1
            x0 .= x_final
            x0_list[i+1] = copy(x0)
        end
    end
    # Sanity check: Check if multiple simulations corresponds to a single one
    y2 = Vector{Vector{Float64}}(100)
    for i = 1:length(y)
        y2[i] = zeros(1)
    end
    x_final2 = ms.simulate_space_state!(y2, logistic_f, logistic_g,
                                       [0.5], (1, 100), (3.78,))
    @test vcat(y...) == vcat(y2...)

    # Simulate using multiple
    θ = [3.78, 3.78, 1.00]
    # Define polynomial model
    # f(x) = θ1*x - θ2*x^2
    function f(y, dx, dθ, x, k, θ)
        y .= θ[1]*x - θ[2]*x.^2
        dx .= θ[1] - 2*θ[2]*x
        dθ .=  [ x  -x.^2  0]
        return
    end
    # g(x) = x
    function g(y, dx, dθ, x, k, θ)
        y .= θ[3]*x
        dx .= θ[3]
        dθ .=  [ 0 0 x ]
        return
    end
    multiple_shoot = ms.MultipleShooting(f, g, x0_list,
                                         y, k0_list, θ)

    @testset "Test function evaluation" begin
        for m = 1:10
            ms.new_simulation!(multiple_shoot, x0_list, [3.78, 3.78, 1.00])
            ys1 = vcat(fetch(multiple_shoot.simulations[m]).ys...)
            ms.new_simulation!(multiple_shoot, x0_list, [3.78, 3.78, 2.00])
            ys2 = vcat(fetch(multiple_shoot.simulations[m]).ys...)
            @test ys1 ≈ y[grid[m]:grid[m+1]-1]
            @test 2*ys1 ≈ ys2
        end
    end

    @testset "Test cost funciton" begin
        ms.new_simulation!(multiple_shoot, x0_list, [3.78, 3.78, 1.00])
        @test ms.cost_function(multiple_shoot) ≈ 0.0
        ms.new_simulation!(multiple_shoot, x0_list, [3.78, 3.78, 2.00])
        @test ms.cost_function(multiple_shoot) ≈ sum(vcat(y...).^2)
    end

    @testset "Test gradient θ" begin
        function wrapper_cost(θ1)
            ms.new_simulation!(multiple_shoot, x0_list, θ1)
            return ms.cost_function(multiple_shoot)
        end

        function wrapper_gradient(θ1)
            ms.new_simulation!(multiple_shoot, x0_list, θ1)
            gradθ = zeros(3)
            ms.gradient!(gradθ, multiple_shoot, "θ")
            return gradθ
        end

        function finite_difference_gradient(θ)
            return Calculus.gradient(wrapper_cost, θ, :central)
        end

        # OBS: finite difference approximation
        # fails when trying parameters in the
        # chaotic region
        for θ in ([3.21, 3.2, 1.00],
                [3.21, 1.2, 1.54],
                [3.56, 3.07, 1.54])
            @test wrapper_gradient(θ) ≈ finite_difference_gradient(θ) rtol=1e-7
        end
    end

    @testset "Test gradient x0" begin
        function wrapper_cost(x0_expanded)
            x0_list1 = [[x0] for x0 in x0_expanded]
            ms.new_simulation!(multiple_shoot, x0_list1, θ)
            return ms.cost_function(multiple_shoot)
        end

        function wrapper_gradient(x0_expanded)
            x0_list1 = [[x0] for x0 in x0_expanded]
            ms.new_simulation!(multiple_shoot, x0_list1, θ)
            gradx0 = [zeros(1) for x0 in x0_expanded]
            ms.gradient!(gradx0, multiple_shoot, "x0")
            gradx0_expanded = [g[1] for g in gradx0]
            return gradx0_expanded
        end

        function finite_difference_gradient(x0_expanded)
            return Calculus.gradient(wrapper_cost, x0_expanded, :central)
        end

        for x0_expanded in (collect(linspace(0.2, 0.3, 10)),
                            collect(linspace(0.1, 0.8, 10)))
            @test wrapper_gradient(x0_expanded) ≈ finite_difference_gradient(x0_expanded) rtol=1e-5
        end
    end

    @testset "Test constraint" begin
        constraint = [zeros(1) for i = 1:9]
        ms.new_simulation!(multiple_shoot, x0_list, [3.78, 3.78, 1.00])
        expected_result = [zeros(1) for i = 1:9]
        ms.constr!(constraint, multiple_shoot)
        @test ms.constr!(constraint, multiple_shoot) == expected_result
        ms.new_simulation!(multiple_shoot, x0_list, [3.78, 3.78, 2.00])
        expected_result = [zeros(1) for i = 1:9]
        @test ms.constr!(constraint, multiple_shoot) == expected_result
    end

    @testset "Test constraint Jacobian θ" begin
        function wrapper_constr(θ1)
            ms.new_simulation!(multiple_shoot, x0_list, θ1)
            constr = deepcopy(x0_list)[1:9]
            ms.constr!(constr, multiple_shoot)
            return vcat(constr...)
        end

        function wrapper_jacobian(θ1)
            ms.new_simulation!(multiple_shoot, x0_list, θ1)
            jac = [zeros(1, 3) for i = 1:9]
            ms.constr_jac!(jac, multiple_shoot, "θ")
            return vcat(jac...)
        end

        function finite_difference_jacobian(θ1)
            return Calculus.jacobian(wrapper_constr, θ1, :central)
        end

        # OBS: finite difference approximation
        # fails when trying parameters in the
        # chaotic region
        for θ in ([3.21, 3.2, 1.00],
                  [3.21, 1.2, 1.54],
                  [3.56, 3.07, 1.54])
            @test wrapper_jacobian(θ) ≈ finite_difference_jacobian(θ) rtol=1e-6
        end
    end

    @testset "Test constraint Jacobian x0" begin
        function wrapper_constr(x0_expanded)
            x0_list = [[x0] for x0 in x0_expanded]
            ms.new_simulation!(multiple_shoot, x0_list, θ)
            constr = [zeros(1) for i = 1:9]
            ms.constr!(constr, multiple_shoot)
            return vcat(constr...)
        end

        function wrapper_jacobian(x0_expanded)
            x0_list = [[x0] for x0 in x0_expanded]
            ms.new_simulation!(multiple_shoot, x0_list, θ)
            jac = [zeros(1, 1) for i = 1:9]
            ms.constr_jac!(jac, multiple_shoot, "x0")
            return ([diagm(squeeze(vcat(jac...), 2)) zeros(9, 1)] -
                    diagm(ones(9), 1)[1:9, :])
        end

        function finite_difference_jacobian(x0_expanded)
            return Calculus.jacobian(wrapper_constr, x0_expanded, :central)
        end

        for x0_expanded in (collect(linspace(0.2, 0.3, 10)),
                            collect(linspace(0.1, 0.8, 10)))
            @test wrapper_jacobian(x0_expanded) ≈ finite_difference_jacobian(x0_expanded) rtol=1e-3
        end
    end
end


text:
# vim:set ft=julia ts=4 sw=4 sts=4 autoindent:

include("LoadEnvironment.jl")

require("Corpora.jl")
require("CVMAbstract.jl")
require("CVMAddition.jl")
require("CVMVariance.jl")
require("CVMMean.jl")
require("CVMBigramAddition.jl")
require("CVMBigramMean.jl")
require("CommonTools.jl")
require("DataTypes.jl")
require("Optimizers.jl")

# using ProfileView
using DataTypes
using ArrayViews
using CVMAbstractModel
using Addition
using Mean
using BigramAddition
using BigramMean
using Variance
using CommonTools.CVMIntermediates
using Optimizers
using CommonTools.@nogc

using Corpora: Token, Sentence
using Corpora: DummyToken, DataToken
using Corpora: text, read_linecorpus, replace_vocab!
using JSON
using ArgParse

function parse_commandline()
    s = ArgParseSettings()

    @add_arg_table s begin
        "path_l1"
            help = "Path for l1 training data (sentence aligned to l2!!)"
            required = true
        "path_l2"
            help = "Path for l2 training data (sentence aligned to l1!!)"
            required = true
        "path_l1_mono"
            help = "Path for monolingual l1 training data"
            required = true
        "path_l2_mono"
            help = "Path for monolingual l2 training data"
            required = true
        "path_output"
            help = "Path to store the model and representations after training."
            required = true
        "--pre_trained_l1"
            help = "Path to pre-trained word representations for language 1."
            arg_type = String
            default = ""
        "--pre_trained_l2"
            help = "Path to pre-trained word representations for language 2."
            arg_type = String
            default = ""
        "--modeltype"
            help = "Type of model to train. (composition function)"
            arg_type = String
            default = "Addition"
        "--optimizer"
            help = "Optimizer: Choose from 'AdaGrad', 'RMSProp', 'AdaDelta'"
            arg_type = String
            default = "AdaGrad"
        "--wordvecdim"
            help = "Dimensionality of the word vectors"
            arg_type = Int
            default = 40
        "--margin"
            help = "Hinge Loss margin"
            arg_type = Float
            default = float(40.0)
        "--nnoise"
            help = "Number of noise samples"
            arg_type = Int
            default = 1
        "--batchsize"
            help = "Size of batches over which the gradient will be accumulated"
            arg_type = Int
            default = 40000
        "--regularizer"
            help = "L2 regularizer influence"
            arg_type = Float
            default = float(0.2)
        "--gamma"
            help = "Influence of the noise contrastive objective (in contrast to the 'keep it close' objective"
            arg_type = Float
            default = float(1.0)
        "--bi_closeness_scaling"
            help = "Influence of closeness objective"
            arg_type = Float
            default = float(1.0)
        "--initial_lr"
            help = "Initial learning rate (will be decreased during training)"
            arg_type = Float
            default = float(0.2)
        "--minimal_lr"
            help = "Minimal learning rate"
            arg_type = Float
            default = float(0.2)
        "--rms_decay"
            help = "Decay for RMSProp or AdaDelta(between 0.0 and 1.0)"
            arg_type = Float
            default = float(0.99)
        "--niter"
            help = "Number of iterations"
            arg_type = Int
            default = 100
        "--limit_lines_mono"
            help = "Only read in the specified number of lines in the monolingual case"
            arg_type = Int
            default = 0
        "--unk"
            help = "Unk token"
            default = "_UNK_"
        "--skipunk"
            help = "Skip unknown tokens instead of replacing them by the unk"
            action = :store_true
        "--sparse_updates"
            help = "Only add regularization to gradients that are non-zero"
            action = :store_true
        "--bilingual_contrastive"
            help = "Instead of bilingual closeness, apply the contrastive criterion as used in Hermann et al. (2014)"
            action = :store_true
        "--debug"
            help = "Turn on debug mode (working with less data)"
            action = :store_true
    end

    return parse_args(s)
end

function dump_to_file(model, vocab1, vocab2, path, args)
    """
        Dumps the given model to disk
    """

    # Save model
    open(joinpath(path,"model"),"w") do fout
        d = JSON.parse(JSON.json(model))
        d["modeltype"] = args["modeltype"]
        d["path_l1"] = args["path_l1"]
        d["path_l2"] = args["path_l2"]
        d["path_l1_mono"] = args["path_l1_mono"]
        d["path_l2_mono"] = args["path_l2_mono"]
        write(fout,JSON.json(d))
    end
    # Save vocabulary l1
    open(joinpath(path,"wordvecs.l1"),"w") do fout
        println(fout , model.wordvecdim, " ", length(vocab1))
        for tok in sort(collect(values(vocab1)), by=x->x.count[1], rev=true)
            string_vec = join(map(x->(@sprintf "%0.8f" x),tok.vector), " ")
            println(fout ,tok.word, " ", string_vec)
        end
    end
    # Save vocabulary l2
    open(joinpath(path,"wordvecs.l2"),"w") do fout
        println(fout , model.wordvecdim, " ", length(vocab2))
        for tok in sort(collect(values(vocab2)), by=x->x.count[1], rev=true)
            string_vec = join(map(x->(@sprintf "%0.8f" x),tok.vector), " ")
            println(fout ,tok.word, " ", string_vec)
        end
    end
end

function load_wordvecs(path_wordvecs::String)
    """
        Load pre-trained word vectors from disk
    """
    d = Dict{String,Token}()
    open(path_wordvecs) do fin
        dim, nvecs = map(int64, split(strip(readline(fin))," "))
        for line in eachline(fin)
            s = split(strip(line)," ")
            d[s[1]] = Token(s[1], map(float,s[2:end]),
                                ones(dim), [0])
        end
    end
    return d;
end

function main()
    # Parse Command line
    args = parse_commandline()

    # Create folder and log file
    mkpath(args["path_output"])
    logfile = open(joinpath(args["path_output"],"progress.log"),"w")

    # Dump arguments
    open(joinpath(args["path_output"],"args"),"w") do fout
        write(fout,JSON.json(args))
    end

    # Save the current state of the code together with the model
    dump_code_path = abspath(joinpath(args["path_output"],"code"))
    mkpath(dump_code_path)
    current_dir = splitdir(@__FILE__)[1]
    # Note: This will only work on systems that have find installed
    # (=> not Windows)
    run(`find $current_dir -maxdepth 1 -name "*\.jl"` |>
                `xargs -i cp {} $dump_code_path`)

    # Initialize model according to the user's selection
    if args["modeltype"] == "BigramMean"
       global ModelModule = BigramMean
       typealias Model BigramMean.BigramMeanModel
       typealias Parameters BigramMean.BigramMeanParameters
    elseif args["modeltype"] == "BigramAddition"
       global ModelModule = BigramAddition
       typealias Model BigramAddition.BigramAdditionModel
       typealias Parameters BigramAddition.BigramAdditionParameters
    elseif args["modeltype"] == "Addition"
       global ModelModule = Addition
       typealias Model Addition.AdditionModel
       typealias Parameters Addition.AdditionParameters
    elseif args["modeltype"] == "Mean"
       global ModelModule = Mean
       typealias Model Mean.MeanModel
       typealias Parameters Mean.MeanParameters
    elseif args["modeltype"] == "Variance"
       global ModelModule = Variance
       typealias Model Variance.VarianceModel
       typealias Parameters Variance.VarianceParameters
    end

    ## Some statics
    # RMSProp/AdaDelta decay
    rms_decay = float(args["rms_decay"])
    @assert 0.0 <= rms_decay <= 1.0
    # Influence of bilingual closeness objective
    # The bigger, the more emphasis we put on the bilingual objective
    bi_closeness_scaling = args["bi_closeness_scaling"]
    # Only regularize parameters that have non-zero gradients
    sparse_updates = args["sparse_updates"]
    # Instead of applying bilingual closeness, use the
    # bilingual noise contrastive criterion in Hermann et al. (2014)
    bilingual_contrastive = args["bilingual_contrastive"]
    # Debug mode with smaller number of samples
    line_limit = args["debug"] ? 10000 : 0
    line_limit_mono = args["debug"] ? 10000 : 0
    if args["limit_lines_mono"] > 0
        line_limit_mono = args["limit_lines_mono"]
    end

    # Initialize model
    model = Model(args["wordvecdim"], # word vector dimensionality
                    args["margin"], # Margin
                    args["regularizer"], #l2 regularizer influence
                    args["initial_lr"], # initial learning rate
                    args["minimal_lr"], # minimal learning rate
                    args["niter"], # number of iterations
                    args["gamma"], # weighting of the hinge objective
                    args["nnoise"], # number of noise samples
                    args["batchsize"]) # size of gradient batches

    # If the user has specified a path to pre-trained vectors, load them
    vocab_l1 = Dict{String,Token}()
    if !isempty(args["pre_trained_l1"])
        vocab_l1 = load_wordvecs(args["pre_trained_l1"])
    end
    vocab_l2 = Dict{String,Token}()
    if !isempty(args["pre_trained_l2"])
        vocab_l2 = load_wordvecs(args["pre_trained_l2"])
    end

    # Initializes a large parameter array for the given vocabulary size
    function init_params(nvocab::Int64, params::AbstractParameters)
        params.wordvecs = randn(model.wordvecdim, nvocab) * 0.1
        params.gradients = zeros(model.wordvecdim, nvocab)
    end

    # Creates a DataToken from a given DataToken
    # Usually needed when we load pre-trained word vectors
    function init_from_data(iword::Int64, tok::DataToken,
                                        params::AbstractParameters)
        return DataToken(tok.word, iword, tok.count, tok.vector, tok.gradient)
    end

    ## tricking Julia here a bit
    # Create a vector that doesn't hold a copy but only a reference
    # to the big parameter array
    # This way, we have everything nicely encapsulated in the
    # Token struct and don't need to fiddle with indices anymore
    function vec_from_pointer(p::Ptr{Float}, iword, width)
        byteshift = sizeof(Float) * (iword-1) * width
        return pointer_to_array(p+byteshift, (width,))
    end

    # Initializes a data token from a dummy token
    # Dummy tokens are used a placeholders when we load all the data.
    # After loading the whole data we know the vocabulary sizes and
    # can initialize the large arrays holding all the parameters.
    # Once that is done, we replace DummyTokens by DataTokens which
    # then hold references to the actual word vectors
    function init_from_dummy(iword::Int64, tok::DummyToken,
                                            params::AbstractParameters)
        return DataToken(tok.word, iword, tok.count,
                            vec_from_pointer(pointer(params.wordvecs),
                                                    iword, model.wordvecdim),
                            vec_from_pointer(pointer(params.gradients),
                                                    iword, model.wordvecdim))
    end

    ##### Load language 1 part of the bilingual corpus
    params_l1 = Parameters()
    (data_l1, vocab_l1_bi,
        ntokens_l1, max_len_l1) = read_linecorpus(args["path_l1"],
                                    (x, y) -> init_from_data(x,y,params_l1),
                                    (x, y) -> init_from_dummy(x,y,params_l1),
                                    x -> init_params(x, params_l1),
                                    limit_lines=line_limit,
                                    skiprows=0, dummy_vocab=vocab_l1,
                                    dummies_only=true);

    ##### Load language 2 part of the bilingual corpus
    params_l2 = Parameters()
    (data_l2, vocab_l2_bi,
        ntokens_l2, max_len_l2) = read_linecorpus(args["path_l2"],
                                    (x, y) -> init_from_data(x,y,params_l2),
                                    (x, y) -> init_from_dummy(x,y,params_l2),
                                    x -> init_params(x, params_l2),
                                    limit_lines=line_limit,
                                    skiprows=0, dummy_vocab=vocab_l2,
                                    dummies_only=true);

    println(logfile, "[Only Bilingual]: [L1] Vocab size: $(length(vocab_l1))")
    println(logfile, "[Only Bilingual]: [L2] Vocab size: $(length(vocab_l2))")

    ##### Load monolingual data for language 1
    tic()
    (data_mono_l1, vocab_l1,
        ntokens_mono_l1, max_len_mono_l1) = read_linecorpus(args["path_l1_mono"],
                                    (x, y) -> init_from_data(x,y,params_l1),
                                    (x, y) -> init_from_dummy(x,y,params_l1),
                                    x -> init_params(x, params_l1),
                                    limit_lines=line_limit_mono,
                                    dummy_vocab=vocab_l1_bi,
                                    skiprows=0);

    ##### Load monolingual data for language 2
    (data_mono_l2, vocab_l2,
        ntokens_mono_l2, max_len_mono_l2) = read_linecorpus(args["path_l2_mono"],
                                    (x, y) -> init_from_data(x,y,params_l2),
                                    (x, y) -> init_from_dummy(x,y,params_l2),
                                    x -> init_params(x, params_l2),
                                    limit_lines=line_limit_mono,
                                    dummy_vocab=vocab_l2_bi,
                                    skiprows=0);
    println("Time to read corpus: $(toq()) seconds")
    tic()
    ##### Replace the dummy tokens by real data tokens
    replace_vocab!(data_l1, vocab_l1)
    replace_vocab!(data_l2, vocab_l2)
    println("Time to replace vocab: $(toq()) seconds")

    # Check if the data from both languages has the same size
    # If it doesn't, it can't be sentence aligned
    @assert size(data_l1) == size(data_l2)
    nsentences = size(data_l1,1)
    nsentences_mono_l1 = size(data_mono_l1,1)
    nsentences_mono_l2 = size(data_mono_l2,1)

    # Vocabulary sizes
    n_vocab_l1 = length(vocab_l1)
    n_vocab_l2 = length(vocab_l2)

    # Vocabulary lists
    words_l1 = collect(values(vocab_l1))
    words_l2 = collect(values(vocab_l2))

    # Create Optimizer for l1 and l2
    (opt_l1, opt_l2) = if args["optimizer"] == "AdaGrad"
        (AdaGrad(params_l1.wordvecs, params_l1.gradients),
         AdaGrad(params_l2.wordvecs, params_l2.gradients))
    elseif args["modeltype"] == "RMSProp"
        (RMSProp(params_l1.wordvecs, params_l1.gradients, args["rms_decay"]),
         RMSProp(params_l2.wordvecs, params_l2.gradients, args["rms_decay"]))
    elseif args["modeltype"] == "AdaDelta"
        (AdaDelta(params_l1.wordvecs, params_l1.gradients, args["rms_decay"]),
         AdaDelta(params_l2.wordvecs, params_l2.gradients, args["rms_decay"]))
    end

    # Initialize objectes for intermedate values
    # => since garbage collection is slow, we don't want to re-create them
    # for every run of the inner loop.
    # These structures simply store intermediate values (forward pass results).
    # CAREFUL: In case of implementing parallelism later these intermediate
    # values need to be taken into account. Each thread/process needs its own
    # set of intermediates.
    intermediates = CVMIntermediates[CVMIntermediates(model.wordvecdim,
                                 max(max_len_l1, max_len_l2,
                                     max_len_mono_l1, max_len_mono_l2)),
                     CVMIntermediates(model.wordvecdim,
                                 max(max_len_l1, max_len_l2,
                                     max_len_mono_l1, max_len_mono_l2)),
                     CVMIntermediates(model.wordvecdim,
                                 max(max_len_l1, max_len_l2,
                                    max_len_mono_l1, max_len_mono_l2))]

    println(logfile, "Loading Data completed")
    println(logfile, "Bilingual")
    println(logfile, "\tL1")
    println(logfile, "\t\tNumber of sentences: $(size(data_l1,1))")
    println(logfile, "\t\tNumber of tokens: $(ntokens_l1)")
    println(logfile, "\t\tVocabulary size: $(length(vocab_l1))")
    println(logfile, "\tL2")
    println(logfile, "\t\tNumber of sentences: $(size(data_l2,1))")
    println(logfile, "\t\tNumber of tokens: $(ntokens_l2)")
    println(logfile, "\t\tVocabulary size: $(length(vocab_l2))")
    println(logfile, "Monolingual")
    println(logfile, "\tL1")
    println(logfile, "\t\tNumber of sentences: $(size(data_mono_l1,1))")
    println(logfile, "\t\tNumber of tokens: $(ntokens_mono_l1)")
    println(logfile, "\tL2")
    println(logfile, "\t\tNumber of sentences: $(size(data_mono_l2,1))")
    println(logfile, "\t\tNumber of tokens: $(ntokens_mono_l2)")

    #########################################################################
    ######################## Training #######################################
    #########################################################################

    println(logfile, "##############")
    println(logfile, "Starting training")
    # Outer and inner windows of the inclusion criterion will always be between
    # the specified boundaries
    minmaxwindow_outer = (10, 50);
    minwindow_inner = 3;
    minmaxwindown = (10, 50);
    ### Perform the training
    # The total number of samples that we process is calculated
    # as the number of sentences in the largest data collection (mono_l1,
    # mono_l2 or bi) times the number of iterations specified by the user
    max_nsentences = max(nsentences, nsentences_mono_l1, nsentences_mono_l2)
    nsentences_total = max_nsentences * model.niter
    isentence_total = 0
    # Some varibles to store accumulated errors
    acc_total = const_zero;
    acc_closeness_bi = const_zero;
    acc_contr_mono_l1 = const_zero;
    acc_closeness_mono_l1 = const_zero;
    acc_contr_mono_l2 = const_zero;
    acc_closeness_mono_l2 = const_zero;
    acc_reg = const_zero;
    isentence_tic = const_zero;
    tic();
    # Learning rate (initially) (will be decreased according to specified
    # parameters)
    lr = model.lr_init;

    ################## START THE TRAINING #######################
    println("Starting training")
    #= Profile.clear() =#
    while isentence_total < nsentences_total
        # Since we always iterate over a full batch,
        # we might end up training for slightly more than the specified
        # iterations.
        # Turn off the garbage collection while processing a batch
        @nogc for ibatch=1:model.batchsize
            # Calculate the index of the current bilingual sentence pair
            isentence = (isentence_total % nsentences) + 1
            # Adaptive learning rate
            lr = float(max(model.lr_min,
                        model.lr_init *
                            (const_one - isentence_total / nsentences_total)))

            # Select the text of the current sentence and its translation
            s1 = data_l1[isentence].text
            s2 = data_l2[isentence].text
            # Make sure all sentences have a certain minimal length
            if (size(s1,1) < 5 || size(s2,1) < 5)
                isentence_total += 1
                continue
            end

            # Collect the gradients for the bilingual criterion.
            # All the gradients are stored in the corresponding field
            # of each token. This field is a reference to the large gradient
            # array the contains all gradients.
            if bilingual_contrastive
                sn = data_l2[rand(1:nsentences)].text
                err_closeness_bi, err_contr_bi = accumulate_gradients_batch!(
                                                model,
                                                s1, s2, Vector{Token}[sn],
                                                intermediates,
                                                scaling=bi_closeness_scaling)
                acc_closeness_bi += err_closeness_bi + err_contr_bi
            else
                acc_closeness_bi += accumulate_closeness_gradients_batch!(model,
                                                s1, s2,
                                                intermediates,
                                                scaling=bi_closeness_scaling)
            end

            #### Monolingual
            for (data, ns, lbl) in ((data_mono_l1, nsentences_mono_l1, 1),
                            (data_mono_l2, nsentences_mono_l2, 2))
                # Calculate the running index for the current
                # sentence in the current corpus
                icursentence = (isentence_total%ns)+1
                # Get the sentence length and check if it
                # is longer than the specified minimum
                lensen = size(data[icursentence].text,1)
                if lensen < minmaxwindow_outer[1]+2
                    continue
                end

                ### Outer window
                # Calculate the offset for the valid sentence
                offset_outer = rand(1:lensen-1-minmaxwindow_outer[1])
                # Get the end of the window
                # |-----|...............|.........|------------------|
                #    offset         min_winend   max_winend    sentence_end
                min_winend_outer = offset_outer+minmaxwindow_outer[1]
                window_end_outer = rand(min_winend_outer:
                                            min(min_winend_outer +
                                                minmaxwindow_outer[2],
                                                lensen))
                ### Inner window
                # Length of the sub sentence
                lensubsen = window_end_outer - offset_outer
                # Get the offset for the inner window
                offset_inner = rand(1:lensubsen-1-minwindow_inner)
                # Earliest end of inner window
                min_winend_inner = offset_inner+minwindow_inner
                # Get a random inner window end that is bigger than the minsize
                window_end_inner = rand(min_winend_inner:lensubsen)

                ### Noise (same procedure as for outer window)
                # Calculate the offset for the valid sentence
                noise_samples = Vector{Token}[]
                while size(noise_samples,1) < model.nnoise
                    # Get the index for the noise sentence
                    inoise = icursentence
                    # Make sure the noise sentence is not equivalent
                    # to the current valid sentence
                    while inoise == icursentence
                        inoise = rand(1:ns)
                    end
                    # Check if the length of the noise sentence
                    # is above the specified threshold
                    lensen_noise = size(data[inoise].text,1)
                    if lensen_noise < minmaxwindown[1]+2
                        continue
                    end

                    offsetn = rand(1:lensen_noise-1-minmaxwindown[1])
                    min_winendn = offsetn+minmaxwindown[1]
                    window_endn = rand(min_winendn:
                                                min(min_winendn +
                                                    minmaxwindown[2],
                                                    lensen_noise))
                    sn = data[inoise].text[offsetn:window_endn]
                    push!(noise_samples,sn)
                end

                ### Retrieve the sentences with the previously calculated indices
                s1 = data[icursentence].text[offset_outer:window_end_outer]
                s2 = s1[offset_inner:window_end_inner]
                # Calculate the overlap ratio of the inner and outer window
                overlap_ratio = size(s2,1)/size(s1,1);
                # Accumulate the gradients for the inclusion objective
                err_closeness, err_contr = accumulate_gradients_batch!(model,
                                                s1, s2, noise_samples,
                                                intermediates,
                                                scaling=overlap_ratio)
                # Update the accumulative error
                if lbl == 1
                    acc_contr_mono_l1 += err_contr
                    acc_closeness_mono_l1 += err_closeness
                elseif lbl == 2
                    acc_contr_mono_l2 += err_contr
                    acc_closeness_mono_l2 += err_closeness
                else
                    throw(ErrorException("Unknown label given for acc errors."))
                end
            end

            # Increase the sentence counter
            isentence_total += 1
        end

        # Apply the squared l2 regularizer
        acc_reg += regularize_l2!(model, params_l1,
                                        sparse_updates=sparse_updates)
        acc_reg += regularize_l2!(model, params_l2,
                                        sparse_updates=sparse_updates)
        # Execute one update step of the optimizers
        # The optimizer also resets all the gradients accumulated in the
        # last batch to zero
        step!(opt_l1, lr)
        step!(opt_l2, lr)

        # Calculate the total accumulated error
        acc_total = (acc_closeness_bi + acc_reg + acc_contr_mono_l1 +
                        acc_closeness_mono_l1 + acc_contr_mono_l2 +
                        acc_closeness_mono_l2)

        # Print some progress statistics
        time_elapsed = toq();
        @printf logfile "Progress: %0.2f%% " (isentence_total)/nsentences_total*100
        @printf logfile "lr: %0.5f " lr
        @printf logfile "error: total %0.1f " (acc_total/model.batchsize)
        @printf logfile "closeness %0.3f " (acc_closeness_bi/model.batchsize)
        @printf logfile "cl-mono-l1 %0.3f " (acc_closeness_mono_l1/model.batchsize)
        @printf logfile "co-mono-l1 %0.3f " (acc_contr_mono_l1/model.batchsize/model.nnoise)
        @printf logfile "cl-mono-l2 %0.3f " (acc_closeness_mono_l2/model.batchsize)
        @printf logfile "co-mono-l2 %0.3f " (acc_contr_mono_l2/model.batchsize/model.nnoise)
        @printf logfile "reg %0.3f " (acc_reg/model.batchsize)
        @printf logfile "sents/s: %6d \n" ((isentence_total-isentence_tic)/time_elapsed)
        flush(logfile)
        isentence_tic = isentence_total
        # Reset the accumulative errors
        acc_total = const_zero;
        acc_contr_mono_l1 = const_zero;
        acc_closeness_mono_l1 = const_zero;
        acc_contr_mono_l2 = const_zero;
        acc_closeness_mono_l2 = const_zero;
        acc_closeness_bi = const_zero;
        acc_reg = const_zero;
        # Rest the timer that we use to estimate number-sentences/sec
        tic()
    end

    ##################### TRAINING FINISHED #####################

    # Create the output path if it doesn't exist
    dump_to_file(model, vocab_l1, vocab_l2, args["path_output"],
                    args);

    # Close the log file
    close(logfile)

end

main()

max_stars_repo_name: ogh/binclusion

max_issues_repo_name: ogh/binclusion

max_forks_repo_name: ogh/binclusion


text:
## renumbercols, renumberrows
## FIXME: Factor out the builtin-only core.
# Wow, this is not super-readable
for (dim, Ind) in ((:cols, :Ic), (:rows, :Ir))
    renumberdim = Symbol(:renumber, dim)
    renumberdim! = Symbol(renumberdim, :!)
    _renumberdim! = Symbol(:_, renumberdim!)
    if dim == :cols
        call1 = :(SparseMatrixCOO(coo.m, last(newInd), coo.Ir, newInd, coo.nzval))
        call2 = :(SparseMatrixCOO(coo.m, last(coo.Ic), coo.Ir, coo.Ic, coo.nzval))
    else
        call1 = :(SparseMatrixCOO(maximum(newInd), coo.n, newInd, coo.Ic, coo.nzval))
        call2 = :(SparseMatrixCOO(maximum(coo.Ir), coo.n, coo.Ir, coo.Ic, coo.nzval))
    end
    @eval begin
        function $(renumberdim)(coo::SparseMatrixCOO{Tv,Ti}) where {Tv,Ti}
            newInd = Vector{Ti}(undef, length(coo.$Ind))
            $(_renumberdim!)(newInd, coo)
            return $call1
        end
        function $(renumberdim!)(coo::SparseMatrixCOO{Tv,Ti}) where {Tv,Ti}
            $(_renumberdim!)(coo.Ic, coo)
            return $call2
        end
        function $(_renumberdim!)(newInd, coo::SparseMatrixCOO{Tv,Ti}) where {Tv,Ti}
            oldIndlist = sort!(unique(coo.$Ind))
            for j in 1:length(coo.$Ind)
                newInd[j] = searchsortedfirst(oldIndlist, coo.$Ind[j])
            end
            return nothing
        end
    end
end

@doc """
    renumberrows(S::SparseMatrixCOO)

Remove rows that have no non-zero values
and renumber the rows.
""" renumberrows

@doc """
    renumberrows!(S::SparseMatrixCOO)

Remove rows that have no non-zero values
and renumber the rows.
""" renumberrows!

@doc """
    renumbercols(S::SparseMatrixCOO)

Remove cols that have no non-zero values
and renumber the cols.
""" renumbercols

@doc """
    renumbercols!(S::SparseMatrixCOO)

Remove cols that have no non-zero values
and renumber the cols.
""" renumbercols!

"""
    renumberrowscols(S::SparseMatrixCOO)

Equivalent to applying `renumberrow!` and `renumbercols`
"""
renumberrowscols(S::SparseMatrixCOO) = S |> renumberrows |> renumbercols

"""
    renumberrowscols!(S::SparseMatrixCOO)

Equivalent to applying `renumberrows!` and `renumbercols!`
"""
renumberrowscols!(S::SparseMatrixCOO) = S |> renumberrows! |> renumbercols!


text:
using Documenter
using Revise
using Smg2s
using SparseArrays

makedocs(
        modules = [Smg2s],
        sitename="Smg2s.jl",
        format = Documenter.HTML(
              prettyurls = get(ENV, "CI", nothing) == "true",
        ),
        pages = [
                "Home" => "index.md",
                "User Guide" => Any[
                        "Getting Started" => "getting_started.md"
                        "Customization" => "custorm.md"
                        "Examples" => "examples.md"
                ],
                "Gallery: Sparsity Patterns" => "gallery.md",
                "Citing Smg2s" => "citing.md",
                "Licence" => "licence.md"
        ]

)

deploydocs(
    repo = "https://github.com/Smg2s/Smg2s.jl")


text:
x = randn(500)
hist(x; nbins=50, scaling="pdf")
plot!(x -> exp(-x^2/2)/sqrt(2π), -3, 3)