text:
# Copyright (c) 2019 Arpit Bhatia and contributors                               #src
#                                                                                #src
# Permission is hereby granted, free of charge, to any person obtaining a copy   #src
# of this software and associated documentation files (the "Software"), to deal  #src
# in the Software without restriction, including without limitation the rights   #src
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      #src
# copies of the Software, and to permit persons to whom the Software is          #src
# furnished to do so, subject to the following conditions:                       #src
#                                                                                #src
# The above copyright notice and this permission notice shall be included in all #src
# copies or substantial portions of the Software.                                #src
#                                                                                #src
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     #src
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       #src
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    #src
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         #src
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  #src
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  #src
# SOFTWARE.                                                                      #src

# # Getting started with Julia

# Since JuMP is embedded in Julia, knowing some basic Julia is important
# for learning JuMP. This tutorial is designed to provide a minimalist
# crash course in the basics of Julia. You can find resources that provide
# a more comprehensive introduction to Julia [here](https://julialang.org/learning/).

# ## Installing Julia

# To install Julia, [download the latest stable release](https://julialang.org/downloads/),
# then follow the [platform specific install instructions](https://julialang.org/downloads/platform/).

# !!! tip
#     Unless you know otherwise, you probably want the 64-bit version.

# Next, you need an IDE to develop in. VS Code is a popular choice, so follow
# [these install instructions](https://www.julia-vscode.org/docs/stable/gettingstarted/).

# ## Numbers and arithmetic

# Since we want to solve optimization problems, we're going to be using a lot of
# math. Luckily, Julia is great for math, with all the usual operators:

@show 1 + 1
@show 1 - 2
@show 2 * 2
@show 4 / 5
@show 3^2
nothing #hide

# !!! info
#     The `@` in front of something indicates that it is a macro, which is just
#     a special type of function. In this case, `@show` prints the expression as
#     typed (e.g., `1 - 2`), as well as the evaluation of the expression (`-1`).

# Did you notice how Julia didn't print `.0` after some of the numbers? Julia is
# a dynamic language, which means you never have to explictly declare the type
# of a variable. However, in the background, Julia is giving each variable a
# type. Check the type of something using the `typeof` function:

@show typeof(1)
@show typeof(1.0)
nothing #hide

# Here `1` is an `Int64`, which is an integer with 64 bits of precision, and
# `1.0` is a `Float64`, which is a floating point number with 64-bits of
# precision.

# !!! tip
#     If you aren't familiar with floating point numbers, make sure to read
#     the [Floating point numbers](@ref) section.

# We create complex numbers using `im`:

x = 2 + 1im
@show real(x)
@show imag(x)
@show typeof(x)
@show x * (1 - 2im)
nothing #hide

# !!! info
#     The curly brackets surround what we call the _parameters_ of a type. You
#     can read `Complex{Int64}`  as "a complex number, where the real and
#     imaginary parts are represented by `Int64`." If we call
#     `typeof(1.0 + 2.0im)` it will be `Complex{Float64}`, which a complex
#     number with the parts represented by `Float64`.

# There are also some cool things like an irrational representation of π.

π

# !!! tip
#     To make π (and most other greek letters), type `\pi` and then press
#     `[TAB]`.

typeof(π)

# Athough if we do math with irrational numbers, they get converted to
# `Float64`:

typeof(2π / 3)

# ### Floating point numbers

# !!! warning
#     If you aren't familiar with floating point numbers, make sure to read this
#     section carefully.

# A `Float64` is a [floating point](https://en.wikipedia.org/wiki/Floating-point_arithmetic)
# approximation of a real number using 64-bits of information.

# Because it is an approximation, things we know hold true in mathematics don't
# hold true in a computer! For example:

0.1 * 3 == 0.3

#- A more complicated example is:

sin(2π / 3) == √3 / 2

# !!! tip
#     Get `√` by typing `\sqrt` then press `[TAB]`.

# Let's see what the differences are:

0.1 * 3 - 0.3

#-

sin(2π / 3) - √3 / 2

# They are small, but not zero!

# One way of explaining this difference is to consider how we would write
# `1 / 3` and `2 / 3` using only four digits after the decimal point. We would
# write `1 / 3` as `0.3333`, and `2 / 3` as `0.6667`. So, depiste the fact that
# `2 * (1 / 3) == 2 / 3`, `2 * 0.3333 == 0.6666 != 0.6667`.

# Let's try that again using ≈ (`\approx + [TAB]`) instead of `==`:

0.1 * 3 ≈ 0.3

#-

sin(2π / 3) ≈ √3 / 2

# `≈` is just a clever way of calling the `isapprox` function:

isapprox(sin(2π / 3), √3 / 2; atol = 1e-8)

# !!! warning
#     Floating point is the reason solvers use tolerances when they solve
#     optimization models. A common mistake you're likely to make is checking
#     whether a binary variable is 0 using `value(z) == 0`. Always remember to
#     use something like `isapprox` when comparing floating point numbers.
#
#     Gurobi has a [good series of articles](https://www.gurobi.com/documentation/9.0/refman/num_grb_guidelines_for_num.html)
#     on the implications of floating point in optimization if you want to read
#     more.

# If you aren't careful, floating point arithmetic can throw up all manner of
# issues. For example:

1 + 1e-16 == 1

# It even turns out that floating point numbers aren't associative!

(1 + 1e-16) - 1e-16 == 1 + (1e-16 - 1e-16)

# It's important to note that this issue isn't Julia-specific. It happens in
# every programming language (try it out in Python).

# ## Vectors, matrices and arrays

# Similar to Matlab, Julia has native support for vectors, matrices and tensors;
# all of which are represented by arrays of different dimensions. Vectors are
# constructed by comma-separated elements surrounded by square brackets:

b = [5, 6]

# !!! info
#     `Array{Int64, 1}` means that this is an `Array`, with `Int64` elements,
#     and it has `1` dimension.

# Matrices can by constructed with spaces separating the columns, and semicolons
# separating the rows:

A = [1.0 2.0; 3.0 4.0]

# Note how this time the type is `Array{Float64, 2}`; the elements are `Float64`
# and there are `2` dimenions.

# We can do linear algebra:

x = A \ b

# !!! info
#     Here is floating point at work again! `x` is approximately `[-4, 4.5]`.

#-

A * x

#-

A * x ≈ b

# Note that when multiplying vectors and matrices, dimensions matter. For
# example, you can't multiply a vector by a vector:

try  #hide
b * b
catch err; showerror(stderr, err); end  #hide

# But multiplying transposes works:

b' * b

#-

b * b'

# ## Other common types

# ### Strings

# Double quotes are used for strings:

typeof("This is Julia")

# Unicode is fine in strings:

typeof("π is about 3.1415")

# Use [`println`](https://docs.julialang.org/en/v1/base/io-network/#Base.println)
# to print a string:

println("Hello, World!")

# We can use `$()` to interpolate values into a string:

x = 123
println("The value of x is: $(x)")

# ### Symbols

# Julia `Symbol`s provide a way to make human readable unique identifiers:

:my_id

#-

typeof(:my_id)

# You can think of a `Symbol` as a `String` that takes up less memory, and that
# can't be modified.

# ### Tuples

# Julia makes extensive use of a simple data structure called Tuples. Tuples are
# immutable collections of values. For example:

t = ("hello", 1.2, :foo)

#-

typeof(t)

# Tuples can be accessed by index, similar to arrays:

t[2]

# And they be "unpacked" like so:

a, b, c = t
b

# The values can also be given names, which is a convenient way of making
# light-weight data structures.

t = (word = "hello", num = 1.2, sym = :foo)

# Values can be accessed using dot syntax:

t.word

# ## Dictionaries

# Similar to Python, Julia has native support for dictionaries. Dictionaries
# provide a very generic way of mapping keys to values.  For example, a map of
# integers to strings:

d1 = Dict(1 => "A", 2 => "B", 4 => "D")

# !!! info
#     Type-stuff again: `Dict{Int64,String}` is a dictionary with `Int64` keys
#     and `String` values.

# Looking up a values uses the bracket syntax:

d1[2]

# Dictionaries support non-integer keys and can mix data types:

Dict("A" => 1, "B" => 2.5, "D" => 2 - 3im)

# !!! info
#     Julia types form a hierarchy. Here the value type of the dictionary is
#     `Number`, which is a generalization of `Int64`, `Float64`, and `Complex{Int}`.
#     In general, having variables with "Abstract" types like `Number` can lead
#     to slower code, so you should try to make sure every element in a
#     dictionary or vector is the same type. For example, in this case we could
#     represent every element as a `Complex{Float64}`:

Dict("A" => 1.0 + 0.0im, "B" => 2.5 + 0.0im, "D" => 2.0 - 3.0im)

# Dictionaries can be nested:

d2 = Dict("A" => 1, "B" => 2, "D" => Dict(:foo => 3, :bar => 4))

#-

d2["B"]

#-

d2["D"][:foo]

# ## Loops

# Julia has native support for for-each style loops with the syntax
# `for <value> in <collection> end`:

for i in 1:5
    println(i)
end

# !!! info
#     Ranges are constructed as `start:stop`, or `start:step:stop`.

#-

for i in [1.2, 2.3, 3.4, 4.5, 5.6]
    println(i)
end

# This for-each loop also works with dictionaries:

for (key, value) in Dict("A" => 1, "B" => 2.5, "D" => 2 - 3im)
    println("$(key): $(value)")
end

# Note that in contrast to vector languages like Matlab and R, loops do not
# result in a significant performance degradation in Julia.

# ## Control Flow

# Julia control flow is similar to Matlab, using the keywords
# `if-elseif-else-end`, and the logical operators `||` and `&&` for **or** and
# **and** respectively:

for i in 0:3:15
    if i < 5
        println("$(i) is less than 5")
    elseif i < 10
        println("$(i) is less than 10")
    else
        if i == 10
            println("the value is 10")
        else
            println("$(i) is bigger than 10")
        end
    end
end

# ## Comprehensions

# Similar to languages like Haskell and Python, Julia supports the use of simple
# loops in the construction of arrays and dictionaries, called comprehenions.
#
# A list of increasing integers:

[i for i in 1:5]

# Matrices can be built by including multiple indices:

[i * j for i in 1:5, j in 5:10]

# Conditional statements can be used to filter out some values:

[i for i in 1:10 if i % 2 == 1]

# A similar syntax can be used for building dictionaries:

Dict("$(i)" => i for i in 1:10 if i % 2 == 1)

# ## Functions

# A simple function is defined as follows:

function print_hello()
    println("hello")
end
print_hello()

# Arguments can be added to a function:

function print_it(x)
    println(x)
end
print_it("hello")
print_it(1.234)
print_it(:my_id)

# Optional keyword arguments are also possible:

function print_it(x; prefix = "value:")
    println("$(prefix) $(x)")
end
print_it(1.234)
print_it(1.234, prefix = "val:")

# The keyword `return` is used to specify the return values of a function:

function mult(x; y = 2.0)
    return x * y
end

mult(4.0)

#-

mult(4.0, y = 5.0)

# ### Anonymous functions

# The syntax `input -> output` creates an anonymous function. These are most
# useful when passed to other functions. For example:

f = x -> x^2
f(2)

#-

map(x -> x^2, 1:4)

# ### Type parameters

# We can constrain the inputs to a function using type parameters, which are
# `::` followed by the type of the input we want. For example:

function foo(x::Int)
    return x^2
end

function foo(x::Float64)
    return exp(x)
end

function foo(x::Number)
    return x + 1
end

@show foo(2)
@show foo(2.0)
@show foo(1 + 1im)
nothing #hide

# But what happens if we call `foo` with something we haven't defined it for?

try  #hide
foo([1, 2, 3])
catch err; showerror(stdout, err) end  #hide

# We get a dreaded `MethodError`! A `MethodError` means that you passed a
# function something that didn't match the type that it was expecting. In this
# case, the error message says that it doesn't know how to handle an
# `Array{Int64, 1}`, but it does know how to handle `Float64`, `Int64`, and
# `Number`.
#
# !!! tip
#     Read the "Closest candidates" part of the error message carefully to get a
#     hint as to what was expected.

# ### Broadcasting

# In the example above, we didn't define what to do if `f` was passed an
# `Array`. Luckily, Julia provides a convienient syntax for mapping `f`
# element-wise over arrays! Just add a `.` between the name of the function and
# the opening `(`. This works for _any_ function, including functions with
# multiple arguments. For example:

f.([1, 2, 3])

# !!! tip
#     Get a `MethodError` when calling a function that takes an `Array`? Try
#     broadcasting it!

# ## Mutable vs immutable objects

# Some types in Julia are *mutable*, which means you can change the values
# inside them. A good example is an array. You can modify the contents of an
# array without having to make a new array.

# In contrast, types like `Float64` are *immutable*. You can't modify the
# contents of a `Float64`.

# This is something to be aware of when passing types into functions. For
# example:

function mutability_example(mutable_type::Vector{Int}, immutable_type::Int)
    mutable_type[1] += 1
    immutable_type += 1
    return
end

mutable_type = [1, 2, 3]
immutable_type = 1

mutability_example(mutable_type, immutable_type)

println("mutable_type: $(mutable_type)")
println("immutable_type: $(immutable_type)")

# Because `Vector{Int}` is a mutable type, modifying the variable inside the
# function changed the value outside of the function. In constrast, the change
# to `immutable_type` didn't modify the value outside the function.

# You can check mutability with the `isimmutable` function:

isimmutable([1, 2, 3])

#-

isimmutable(1)

# ## The package manager

# ### Installing packages

# No matter how wonderful Julia's base language is, at some point you will want
# to use an extension package.  Some of these are built-in, for example random
# number generation is available in the `Random` package in the standard
# library. These packages are loaded with the commands `using` and `import`.

using Random  # The equivalent of Python's `from Random import *`
import Random  # The equivalent of Python's `import Random`

Random.seed!(33)

[rand() for i in 1:10]

# The Package Manager is used to install packages that are not part of Julia's
# standard library.

# For example the following can be used to install JuMP,
# ```julia
# using Pkg
# Pkg.add("JuMP")
# ```

# For a complete list of registed Julia packages see the package listing at
# [JuliaHub](https://juliahub.com).

# From time to you may wish to use a Julia package that is not registered.  In
# this case a git repository URL can be used to install the package.
# ```julia
# using Pkg
# Pkg.add("https://github.com/user-name/MyPackage.jl.git")
# ```

# ### Package environments

# By default, `Pkg.add` will add packages to Julia's global environment.
# However, Julia also has built-in support for virtual environments.

# Activate a virtual environment with:
# ```julia
# import Pkg; Pkg.activate("/path/to/environment")
# ```

# You can see what packages are installed in the current environment with
# `Pkg.status()`.

# !!! tip
#     We _strongly_ recommend you create a Pkg environment for each project
#     that you create in Julia, and add only the packages that you need, instead
#     of adding lots of packages to the global environment. The [Pkg manager documentation](https://julialang.github.io/Pkg.jl/v1/environments/)
#     has more information on this topic.

max_stars_repo_name: ethansaxenian/RosettaDecode

max_issues_repo_name: ethansaxenian/RosettaDecode

max_forks_repo_name: ethansaxenian/RosettaDecode


text:
"""
    Fermi.AbstractWavefunction

Abstract type common to all wave functions.

_struct tree:_

**AbstractWavefunction**  (Top level)
"""
abstract type AbstractWavefunction end

"""
    Fermi.AbstractReferenceWavefunction

Abstract type common to all reference wave functions.

_struct tree:_

**AbstractReferenceWavefunction** <: AbstractWavefunction
"""
abstract type AbstractReferenceWavefunction <: AbstractWavefunction end

"""
    Fermi.AbstractCorrelatedWavefunction

Abstract type common to all correlated wave functions.

_struct tree:_

**AbstractCorrelatedWavefunction** <: AbstractWavefunction
"""
abstract type AbstractCorrelatedWavefunction <: AbstractWavefunction end

ext: jl


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

using Markdown
using InteractiveUtils

# ╔═╡ bd94028c-5d8e-11eb-22b6-5fa384999fdb
begin
	using ReinforcementLearning
	using Flux
	using Statistics
	using Plots
end

# ╔═╡ fbf4f518-5d8e-11eb-1611-2b658a468f57
md"""
To describe the **Grid World** in **Example 3.5**, we'll create a distributional environment model. Here the *distributional* means, given a state-action paire, we can predict the possible next state, reward, termination info and the corresponding probability.
"""

# ╔═╡ 5ff21d54-5d8f-11eb-0e8f-95ca0eabc01f
begin
	function nextstep(s::CartesianIndex{2}, a::CartesianIndex{2})
		if s == CartesianIndex(1, 2)
			r, s′ =  10., CartesianIndex(5, 2)
		elseif s == CartesianIndex(1, 4)
			r, s′ = 5., CartesianIndex(3, 4)
		else
			s′ = s + a
			if 1 ≤ s′[1] ≤ 5 && 1 ≤ s′[2] ≤ 5
				r = 0.
			else
				r = -1.
				s′ = s
			end
		end
		[(r, false, LinearIndices((5,5))[s′]) => 1.0]
	end

	ACTIONS = (
		CartesianIndex(-1, 0),
		CartesianIndex(1,0),
		CartesianIndex(0, 1),
		CartesianIndex(0, -1)
	)

	struct GridWorldModel <: AbstractEnvironmentModel
	end

	function (m::GridWorldModel)(s, a)
		nextstep(CartesianIndices((5,5))[s], ACTIONS[a])
	end

	RLBase.state_space(m::GridWorldModel) = Base.OneTo(5*5)
	RLBase.action_space(m::GridWorldModel) = Base.OneTo(length(ACTIONS))
end

# ╔═╡ 81bcb7a8-5d8f-11eb-070b-97e0199e6c4e
V = TabularVApproximator(;n_state=25, opt=Descent(1.0))

# ╔═╡ 86d47256-5d8f-11eb-27ae-ffa516974fe2
policy_evaluation!(
    V = V,
    π=RandomPolicy(Base.OneTo(4)),
    model=GridWorldModel(),
    γ=0.9
)

# ╔═╡ 8b4d1bc8-5d8f-11eb-1e5b-ddbaa49c216b
table = reshape(V.table, 5, 5)

# ╔═╡ 90c88768-5d8f-11eb-2a17-0dcdf7e0772a
heatmap(table,yflip=true)

# ╔═╡ Cell order:
# ╠═bd94028c-5d8e-11eb-22b6-5fa384999fdb
# ╟─fbf4f518-5d8e-11eb-1611-2b658a468f57
# ╠═5ff21d54-5d8f-11eb-0e8f-95ca0eabc01f
# ╠═81bcb7a8-5d8f-11eb-070b-97e0199e6c4e
# ╠═86d47256-5d8f-11eb-27ae-ffa516974fe2
# ╠═8b4d1bc8-5d8f-11eb-1e5b-ddbaa49c216b
# ╠═90c88768-5d8f-11eb-2a17-0dcdf7e0772a


text:
module EMLtray
	using DanaTypes
	using DotPlusInheritance
	using Reexport
	@reexport using ...streams.EMLstreams
	import EMLtypes.length
	include("tray/tray.jl")
	include("tray/trayReac.jl")
	include("tray/packedStage.jl")
end


text:
module jradau5

function r_fcn(p_N   ::Ptr{Int64},
               p_X   ::Ptr{Float64},
               p_Y   ::Ptr{Float64},
               p_F   ::Ptr{Float64},
               p_RPAR::Ptr{Float64},
               p_IPAR::Ptr{Int64},
               j_out ::Ptr{Void}, # FIXME: needed?
               j_fcn ::Ptr{Void})
   # Tell Julia how to use the pointers as arrays
   n_arr = pointer_to_array(p_N, 1)
   x_arr = pointer_to_array(p_X, 1)
   y     = pointer_to_array(p_Y, n_arr[1])
   f     = pointer_to_array(p_F, n_arr[1])
   #FIXME: RPAR and IPAR unsupported
   # Tell Julia j_fcn is actually a Julia funtion
   fcn   = unsafe_pointer_to_objref(j_fcn)::Function

   # Actual function call
   f[:] = fcn(x_arr[1], y)

   ## Other method to do the function call
   #f = fcn(x_arr[1], y)
   #
   #for i=1:n
   #   unsafe_store!(p_F, f[i], i)
   #end
   #
   ##DEBUG
   #@printf("x = %f,\t y = %f\t, f = %f\n", x_arr[1], y[1], f[1])

   # Return anything since this is called as a Fortran subroutine
   return convert(Int64, 0)
end

function r_solout(p_NR     ::Ptr{Int64},
                  p_XOLD   ::Ptr{Float64},
                  p_X      ::Ptr{Float64},
                  p_Y      ::Ptr{Float64},
                  p_CONT   ::Ptr{Float64},
                  p_LRC    ::Ptr{Float64},
                  p_N      ::Ptr{Int64},
                  p_RPAR   ::Ptr{Float64},
                  p_IPAR   ::Ptr{Int64},
                  j_out    ::Ptr{Void},
                  j_solout ::Ptr{Void})
   # Tell Julia how to use the pointers as arrays
   nr_arr   = pointer_to_array(p_NR,   1)
   xold_arr = pointer_to_array(p_XOLD, 1)
   x_arr    = pointer_to_array(p_X,    1)
   n_arr    = pointer_to_array(p_N,    1)
   y        = pointer_to_array(p_Y,    n_arr[1])
   #FIXME: Continuous output unsupported
   #FIXME: RPAR and IPAR unsupported
   # Tell Julia that j_solout and j_out are actually a Julia objects
   solout   = unsafe_pointer_to_objref(j_solout)
   out      = unsafe_pointer_to_objref(j_out)

   # Update out Variable
   tout, yout = out
   push!(tout, x_arr[1])
   push!(yout, deepcopy(y))

   if solout == 0
   else
      solout() # FIXME
   end

   # DEBUG
   @printf("nr=%d\t x = %f,\t y[1] = %f\n", nr_arr[1], x_arr[1], y[1])

   return convert(Int64, 0) # IRTRN
end

# FIXME
function solout_dummy()
   @printf("soloutdummy\n")
end
# EMXIF

function radau5(f::Function, tspan::Array{Float64,1}, y0::Array{Float64,1};
         rtol     ::Float64 = 1e-6,  # Scalar relative tolerance
         atol     ::Float64 = 1e-6,  # Scalar absolute tolerance
         h0       ::Float64 = 0.0,   # Size of first step (0.0 defaults to 1e-6)
         maxstp   ::Int64   = 0,     # Maximum number of allowed steps (0 defaults to 100000)
         maxnewtit::Int64   = 0,     # Maximum number of newton iterations for the solution of the implicit system in each step (0 defaults to 7)
         uround   ::Float64 = 0.0,   # Rounding unit (0.0 defaults to 1e-16)
         safety   ::Float64 = 0.0,   # Safety factor in step size prediction (0.0 defaults to 0.9)
         )
   # Make a C function of r_fcn, which wraps the input value f
   const c_fcn = cfunction(r_fcn, Int64,
                                   (Ptr{Int64},    # N
                                    Ptr{Float64},  # X
                                    Ptr{Float64},  # Y
                                    Ptr{Float64},  # F
                                    Ptr{Float64},  # RPAR
                                    Ptr{Int64},    # IPAR
                                    Ptr{Void},     # j_out
                                    Ptr{Void}))    # j_fcn
   # Make a C function of r_solout, which controls the output
   # and FIXME the user-defined output function
   const c_solout = cfunction(r_solout, Int64,
                 (Ptr{Int64},    # NR
                  Ptr{Float64},  # XOLD
                  Ptr{Float64},  # X
                  Ptr{Float64},  # Y
                  Ptr{Float64},  # CONT
                  Ptr{Float64},  # LRC
                  Ptr{Int64},    # N
                  Ptr{Float64},  # RPAR
                  Ptr{Int64},    # IPAR
                  Ptr{Void},     # j_out
                  Ptr{Void}))    # j_solout

   # Make a deep copy of y0, as RADAU5 will alter the variable
   y = convert(Array{Float64,1},deepcopy(y0))
   # Get dimension of the differential equation
   const n = length(y0)

   # Calulate the size for the work and iwork array and allocate them
   const lwork  = 4*n*n+12*n+20
   work   = zeros(Float64, lwork)
   const liwork = 3*n + 20
   iwork  = zeros(Int64, liwork)

   # Settings
   iwork[2] = maxstp
   iwork[3] = maxnewtit
   work[1] = uround
   work[2] = safety

   # Initialize output
   tout = Array(Float64, 0)
   yout = Array(Array{Float64,1},0)

   out = (tout, yout)

   # Call RADAU5
   idid = ccall((:radau5wrapfor, "./jradau5.so"), Int64,
      (Ptr{Int64},   # N
       Ptr{Void},    # FCN
       Ptr{Float64}, # X
       Ptr{Float64}, # Y
       Ptr{Float64}, # XEND
       Ptr{Float64}, # H
       Ptr{Float64}, # RTOL
       Ptr{Float64}, # ATOL
       Ptr{Int64},   # ITOL
       Ptr{Void},    # JAC
       Ptr{Int64},   # IJAC
       Ptr{Int64},   # MLJAC
       Ptr{Int64},   # MUJAC
       Ptr{Void},    # MAS
       Ptr{Int64},   # IMAS
       Ptr{Int64},   # MLMAS
       Ptr{Int64},   # MUMAS
       Ptr{Void},    # SOLOUT
       Ptr{Int64},   # IOUT
       Ptr{Float64}, # WORK
       Ptr{Int64},   # LWORK
       Ptr{Int64},   # IWORK
       Ptr{Int64},   # LIWORK
       Ptr{Float64}, # RPAR
       Ptr{Int64},   # IPAR
       Any,          # j_out
       Any,          # j_fcn
       Any           # j_solout
      ),
      &n,         # N
      c_fcn,      # FCN
      &tspan[1],  # X
      y,          # Y
      &tspan[end],# XEND
      &h0,        # H
      &rtol,      # RTOL    FIXME: Support vector-valued tolerances
      &atol,      # ATOL    FIXME: - "" -
      &0,         # ITOL    FIXME: - "" -
      0,          # JAC     FIXME: Support analytic jacobian
      &0,         # IJAC    FIXME: - "" -
      &n,         # MLJAC   FIXME: - "" -
      &n,         # MUJAC   FIXME: - "" -
      0,          # MAS     FIXME: Support non-identity mass matrix
      &0,         # IMAS    FIXME: - "" -
      &0,         # MLMAS   FIXME: - "" -
      &0,         # MUMAS   FIXME: - "" -
      c_solout,   # SOLOUT
      &1,         # IOUT
      work,       # WORK    FIXME!!
      &lwork,     # LWORK   FIXME!!
      iwork,      # IWORK   FIXME!!
      &liwork,    # LIWORK  FIXME!!
      &0,         # RPAR    FIXME!!
      &0,         # IPAR    FIXME!!
      out,        # j_out
      f,          # j_fcn
      solout_dummy# j_soloutFIXME!!
      )

   return out[1], out[2]
end

end


text:
# The arithmetic sequence, 1487, 4817, 8147, in which each of the terms
# increases by 3330, is unusual in two ways: (i) each of the three terms are
# prime, and, (ii) each of the 4-digit numbers are permutations of one another.
#
# There are no arithmetic sequences made up of three 1-, 2-, or 3-digit primes,
# exhibiting this property, but there is one other 4-digit increasing sequence.
#
# What 12-digit number do you form by concatenating the three terms in this
# sequence?

using ProjectEulerSolutions

# Use Sieve of Eratosthenes and then loop through looking for primes that
# satisfy the specified criterion.
function p049solution(n::Integer=100)::Integer

    primes = Set(sieve_eratosthenes(n))

    for p in primes
        if p > 1488 && p + 3330 in primes && p + 6660 in primes
            d = Set(digits(p))
            if d == Set(digits(p + 3330)) && d == Set(digits(p + 6660))
                return p * 1e8 + (p + 3330) * 1e4 + p + 6660
            end
        end
    end

    return -1
end

p049 = Problems.Problem(p049solution)

Problems.benchmark(p049, 9999)

max_stars_repo_name: gnujosh/julia-euler-project

max_issues_repo_name: gnujosh/julia-euler-project

max_forks_repo_name: gnujosh/julia-euler-project


text:
function ImmutableTimeStampedCache(arg0::jint, arg1::Collection)
    return ImmutableTimeStampedCache((jint, Collection), arg0, arg1)
end

function empty_cache(::Type{ImmutableTimeStampedCache})
    return jcall(ImmutableTimeStampedCache, "emptyCache", ImmutableTimeStampedCache, ())
end

function get_all(obj::ImmutableTimeStampedCache)
    return jcall(obj, "getAll", List, ())
end

function get_earliest(obj::ImmutableTimeStampedCache)
    return jcall(obj, "getEarliest", TimeStamped, ())
end

function get_latest(obj::ImmutableTimeStampedCache)
    return jcall(obj, "getLatest", TimeStamped, ())
end

function get_neighbors(obj::ImmutableTimeStampedCache, arg0::AbsoluteDate)
    return jcall(obj, "getNeighbors", Stream, (AbsoluteDate,), arg0)
end

function get_neighbors_size(obj::ImmutableTimeStampedCache)
    return jcall(obj, "getNeighborsSize", jint, ())
end

function to_string(obj::ImmutableTimeStampedCache)
    return jcall(obj, "toString", JString, ())
end


text:
# treemap/boxmap

# Luxor

using Luxor

fibonacci = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]

# make a boxmap and store the tiles
@png begin
    tiles = boxmap(fibonacci, Luxor.BoundingBox()[1], 600, 400)

    for (n, t) in enumerate(tiles)
        randomhue()
        bb = Luxor.BoundingBox(t)
        sethue("black")
        Luxor.box(bb - 5, :stroke)

        randomhue()
        Luxor.box(bb - 8, :fill)

        # text labels
        sethue("white")

        # rescale text to fit better
        fontsize(boxwidth(bb) > boxheight(bb) ? boxheight(bb)/4 : boxwidth(bb)/4)
        Luxor.text(string(sort(fib, rev=true)[n]),
            Luxor.midpoint(bb[1], bb[2]),
            halign=:center,
                valign=:middle)
    end
end

max_stars_repo_name: Ellon-M/visualizations

max_issues_repo_name: Ellon-M/visualizations

max_forks_repo_name: Ellon-M/visualizations


text:
using BinaryProvider

# This is where all binaries will get installed
const prefix = Prefix(!isempty(ARGS) ? ARGS[1] : joinpath(@__DIR__,"usr"))

libsundials_nvecserial       = LibraryProduct(prefix, String["libsundials_nvecserial"])
libsundials_kinsol           = LibraryProduct(prefix, String["libsundials_kinsol"])
libsundials_cvodes           = LibraryProduct(prefix, String["libsundials_cvodes"])
libsundials_idas             = LibraryProduct(prefix, String["libsundials_idas"])
libsundials_sunlinsolspfgmr  = LibraryProduct(prefix, String["libsundials_sunlinsolspfgmr"])
libsundials_sunmatrixdense   = LibraryProduct(prefix, String["libsundials_sunmatrixdense"])
libsundials_sunlinsolspbcgs  = LibraryProduct(prefix, String["libsundials_sunlinsolspbcgs"])
libsundials_sunlinsoldense   = LibraryProduct(prefix, String["libsundials_sunlinsoldense"])
libsundials_sunlinsolspgmr   = LibraryProduct(prefix, String["libsundials_sunlinsolspgmr"])
libsundials_sunlinsolpcg     = LibraryProduct(prefix, String["libsundials_sunlinsolpcg"])
libsundials_sunlinsolsptfqmr = LibraryProduct(prefix, String["libsundials_sunlinsolsptfqmr"])
libsundials_sunmatrixsparse  = LibraryProduct(prefix, String["libsundials_sunmatrixsparse"])
libsundials_sunlinsolband    = LibraryProduct(prefix, String["libsundials_sunlinsolband"])
libsundials_sunmatrixband    = LibraryProduct(prefix, String["libsundials_sunmatrixband"])
libsundials_arkode           = LibraryProduct(prefix, String["libsundials_arkode"])

products = [
libsundials_nvecserial,
libsundials_kinsol,
libsundials_cvodes,
libsundials_idas,
libsundials_sunlinsolspfgmr,
libsundials_sunmatrixdense,
libsundials_sunlinsolspbcgs,
libsundials_sunlinsoldense,
libsundials_sunlinsolspgmr,
libsundials_sunlinsolpcg,
libsundials_sunlinsolsptfqmr,
libsundials_sunmatrixsparse,
libsundials_sunlinsolband,
libsundials_sunmatrixband,
libsundials_arkode,
]

# Download binaries from hosted location
bin_prefix = "https://github.com/JuliaDiffEq/SundialsBuilder/releases/download/sundials-3.1.0-2"

# Listing of files generated by BinaryBuilder:
download_info = Dict(
    BinaryProvider.Linux(:aarch64, :glibc) => ("$bin_prefix/Sundials.aarch64-linux-gnu.tar.gz", "a6a8894a48c8ff93d28d437e5efd378933707b65c054f0c1feb9897964bb7c11"),
    BinaryProvider.Linux(:armv7l, :glibc) => ("$bin_prefix/Sundials.arm-linux-gnueabihf.tar.gz", "b2c4379a211250cbaeb73e21e0a4a893fb3ac3255212039879d4fcdfd54e34ac"),
    BinaryProvider.Linux(:i686, :glibc) => ("$bin_prefix/Sundials.i686-linux-gnu.tar.gz", "616525eb308bedb2d09486ec7783362856e21b56473c47a6e06eded94c25f0d9"),
    BinaryProvider.Windows(:i686) => ("$bin_prefix/Sundials.i686-w64-mingw32.tar.gz", "e6b629e341ec5475846d87da991089b28a8992bfe32f073b62d819bd2d342c7d"),
    BinaryProvider.Linux(:powerpc64le, :glibc) => ("$bin_prefix/Sundials.powerpc64le-linux-gnu.tar.gz", "2354d4a22b7be8ab208457f8863953d23628200af83e4f735ec26f52c526a94c"),
    BinaryProvider.MacOS() => ("$bin_prefix/Sundials.x86_64-apple-darwin14.tar.gz", "d39bcc4f724c742ab966c46391989226b1654dbfc152f55ae5a5fd19b2eaf9bd"),
    BinaryProvider.Linux(:x86_64, :glibc) => ("$bin_prefix/Sundials.x86_64-linux-gnu.tar.gz", "40f55bea93a6bb5cdd07537e2bee3be230dfef30605ccb385c9234ffb5f15fb9"),
    BinaryProvider.Windows(:x86_64) => ("$bin_prefix/Sundials.x86_64-w64-mingw32.tar.gz", "a3888df5b793e310e88efc2356f149e5d5258f6aa7d5627420e3ae0302e330c7"),
)
if platform_key() in keys(download_info)
    # First, check to see if we're all satisfied
    if any(!satisfied(p; verbose=true) for p in products)
        # Download and install binaries
        url, tarball_hash = download_info[platform_key()]
        install(url, tarball_hash; prefix=prefix, force=true, verbose=true)
    end

    # Finally, write out a deps.jl file that will contain mappings for each
    # named product here: (there will be a "libfoo" variable and a "fooifier"
    # variable, etc...)
    @write_deps_file libsundials_kinsol libsundials_idas libsundials_nvecserial libsundials_cvodes libsundials_sunlinsolspfgmr  libsundials_sunmatrixdense libsundials_sunlinsolspbcgs libsundials_sunlinsoldense libsundials_sunlinsolspgmr  libsundials_sunlinsolpcg libsundials_sunlinsolsptfqmr libsundials_sunmatrixsparse libsundials_sunlinsolband libsundials_sunmatrixband libsundials_arkode
else
    error("Your platform $(Sys.MACHINE) is not supported by this package!")
end


text:
using CUSPARSE
using CUDArt
using Base.Test

m = 25
n = 35
k = 10
blockdim = 5

@testset "bsrilu02" begin
    @testset for elty in [Float32,Float64,Complex64,Complex128]
        A = rand(elty,m,m)
        A += transpose(A)
        A += m * eye(elty,m)
        @testset "bsrilu02!" begin
            d_A = CudaSparseMatrixCSR(sparse(A))
            d_A = CUSPARSE.switch2bsr(d_A, convert(Cint,5))
            d_A = CUSPARSE.ilu02!(d_A,'O')
            h_A = to_host(CUSPARSE.switch2csr(d_A))
            Alu = lufact(full(A),Val{false})
            Ac = sparse(Alu[:L]*Alu[:U])
            h_A = ctranspose(h_A) * h_A
            @test h_A.rowval ≈ Ac.rowval
            @test reduce(&, isfinite.(h_A.nzval))
            d_A = CudaSparseMatrixCSR(sparse(rand(elty,m,n)))
            d_A = CUSPARSE.switch2bsr(d_A, convert(Cint,5))
            @test_throws DimensionMismatch CUSPARSE.ilu02!(d_A,'O')
        end

        @testset "bsrilu02" begin
            d_A = CudaSparseMatrixCSR(sparse(A))
            d_A = CUSPARSE.switch2bsr(d_A, convert(Cint,5))
            d_B = CUSPARSE.ilu02(d_A,'O')
            h_A = to_host(CUSPARSE.switch2csr(d_B))
            Alu = lufact(full(A),Val{false})
            Ac = sparse(Alu[:L]*Alu[:U])
            h_A = ctranspose(h_A) * h_A
            @test h_A.rowval ≈ Ac.rowval
            @test reduce(&, isfinite.(h_A.nzval))
        end
    end
end


text:
const EAGER_INIT = Ref{Bool}(false)
const EAGER_THUNK_CHAN = Channel(typemax(Int))
const EAGER_ID_MAP = Dict{UInt,Int}()
const EAGER_CONTEXT = Ref{Context}()
const EAGER_STATE = Ref{ComputeState}()

eager_context() = isassigned(EAGER_CONTEXT) ? EAGER_CONTEXT[] : nothing

function init_eager()
    EAGER_INIT[] && return
    EAGER_INIT[] = true
    if eager_context() === nothing
        EAGER_CONTEXT[] = Context([myid(),workers()...])
    end
    ctx = EAGER_CONTEXT[]
    @async try
        sopts = SchedulerOptions(;allow_errors=true)
        topts = ThunkOptions(;single=1)
        Dagger.compute(ctx, Dagger.delayed(eager_thunk;options=topts)(); options=sopts)
    catch err
        iob = IOContext(IOBuffer(), :color=>true)
        println(iob, "Error in eager scheduler:")
        Base.showerror(iob, err)
        Base.show_backtrace(iob, catch_backtrace())
        println(iob)
        seek(iob.io, 0)
        write(stderr, iob)
    finally
        EAGER_INIT[] = false
    end
end

"Adjusts the scheduler's cached pressure indicator for the specified worker by
the specified amount."
function adjust_pressure!(h::SchedulerHandle, proctype::Type, pressure)
    uid = Dagger.get_tls().sch_uid
    lock(TASK_SYNC) do
        PROC_UTILIZATION[uid][proctype][] += pressure
        notify(TASK_SYNC)
    end
    exec!(_adjust_pressure!, h, myid(), proctype, pressure)
end
function _adjust_pressure!(ctx, state, task, tid, (pid, proctype, pressure))
    state.worker_pressure[pid][proctype] += pressure
    nothing
end

"Allows a thunk to safely wait on another thunk, by temporarily reducing its
effective pressure to 0."
function thunk_yield(f)
    if Dagger.in_thunk()
        h = sch_handle()
        tls = Dagger.get_tls()
        proctype = typeof(tls.processor)
        util = tls.utilization
        adjust_pressure!(h, proctype, -util)
        try
            f()
        finally
            adjust_pressure!(h, proctype, util)
        end
    else
        f()
    end
end

function eager_thunk()
    h = sch_handle()
    util = Dagger.get_tls().utilization
    exec!(h) do ctx, state, task, tid, _
        EAGER_STATE[] = state
    end
    # Don't apply pressure from this thunk
    adjust_pressure!(h, Dagger.ThreadProc, -util)
    while isopen(EAGER_THUNK_CHAN)
        try
            ev, future, uid, f, args, opts = take!(EAGER_THUNK_CHAN)
            # preserve inputs until they enter the scheduler
            tid = GC.@preserve args begin
                args = map(x->x isa Dagger.EagerThunk ? ThunkID(EAGER_ID_MAP[x.uid]) : x, args)
                add_thunk!(f, h, args...; future=future, opts...)
            end
            EAGER_ID_MAP[uid] = tid.id
            notify(ev)
        catch err
            iob = IOContext(IOBuffer(), :color=>true)
            println(iob, "Error in eager listener:")
            Base.showerror(iob, err)
            Base.show_backtrace(iob, catch_backtrace())
            println(iob)
            seek(iob.io, 0)
            write(stderr, iob)
        end
    end
end

eager_cleanup(t::Dagger.EagerThunkFinalizer) =
    @async eager_cleanup(EAGER_STATE[], t.uid)
function eager_cleanup(state, uid)
    lock(state.lock) do
        if !haskey(EAGER_ID_MAP, uid)
            return
        end
        tid = EAGER_ID_MAP[uid]
        delete!(EAGER_ID_MAP, uid)

        # N.B. cache and errored expire automatically
        delete!(state.thunk_dict, tid)
    end
end


text:
using PyCall

@testset "matplotlib backend" begin
    idata = load_arviz_data("centered_eight")

    backend = get(ArviZ.rcParams, "plot.backend", nothing)
    @test backend in ["matplotlib", nothing]
    @test plot_trace(idata) isa Array{PyObject}
    @test plot_trace(idata; backend = nothing) isa Array{PyObject}
end

if !ispynull(ArviZ.bokeh) && "plot.backend" in keys(ArviZ.rcParams)
    try
        ArviZ.initialize_bokeh_png_deps()
    catch
        @info "bokeh png depencies not found. Skipping tests for bokeh png"
    end

    @testset "bokeh backend" begin
        idata = load_arviz_data("centered_eight")

        with_rc_context(rc = Dict("plot.backend" => "bokeh")) do
            backend = get(ArviZ.rcParams, "plot.backend", nothing)
            @test backend == "bokeh"

            @test plot_trace(idata) isa ArviZ.BokehPlot
            @test plot_trace(idata; backend = "matplotlib") isa Array{PyObject}

            @testset "ArviZ.BokehPlot" begin
                plot = plot_trace(idata)

                pyobj = PyObject(plot)
                plot2 = convert(ArviZ.BokehPlot, pyobj)
                @test plot2 isa ArviZ.BokehPlot
                @test ArviZ.BokehPlot(plot) === plot
                @test hash(plot) == hash(plot2)
                @test pyobj === PyObject(plot2)

                @test propertynames(plot) == propertynames(PyObject(plot))
                @test occursin("bokeh", "$(getproperty(plot, :__class__))")

                ArviZ.has_bokeh_png_deps && @testset "show MIME\"image/png\"" begin
                    fn = tempname() * ".png"
                    open(fn, "w") do s
                        return show(s, MIME"image/png"(), plot)
                    end
                    bytes = read(fn)
                    @test bytes[1:8] ==
                          UInt8[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]
                end

                @testset "show MIME\"$(mime)\"" for mime in [
                    "text/html",
                    "juliavscode/html",
                    "application/juno+plotpane",
                    "application/prs.juno.plotpane+html",
                ]
                    text = sprint(show, MIME(mime), plot)
                    @test text isa String
                    @test occursin("<body", text)
                end

                @testset "write html" begin
                    text = sprint(write, plot)
                    @test text isa String
                    @test occursin("<body", text)
                end
            end
            return nothing
        end
    end
end


text:
@testset "Hash" begin
    seq = dna"ACGTACGT"
    @test isa(hash(seq), UInt64)
    @test hash(seq) === hash(dna"ACGTACGT")
    @test hash(seq) !== hash(seq[1:6])
    @test hash(seq) !== hash(seq[1:7])
    @test hash(seq) === hash(seq[1:8])
    @test hash(seq[1:4]) === hash(dna"ACGT")
    @test hash(seq[2:4]) === hash(dna"CGT")
    @test hash(seq[3:4]) === hash(dna"GT")
    @test hash(seq[4:4]) === hash(dna"T")
    @test hash(seq[5:8]) === hash(dna"ACGT")

    @test hash(dna"") !== hash(dna"A")
    @test hash(dna"A") !== hash(dna"AA")
    @test hash(dna"AA") !== hash(dna"AAA")
    @test hash(dna"AAA") !== hash(dna"AAAA")

    for n in 1:200, seq in [dna"A", dna"AC", dna"ACG", dna"ACGT", dna"ACGTN"]
        @test hash(seq^n) === hash((dna""     * seq^n)[1:end])
        @test hash(seq^n) === hash((dna"T"    * seq^n)[2:end])
        @test hash(seq^n) === hash((dna"TT"   * seq^n)[3:end])
        @test hash(seq^n) === hash((dna"TTT"  * seq^n)[4:end])
        @test hash(seq^n) === hash((dna"TTTT" * seq^n)[5:end])

        @test hash(seq^n) === hash((seq^n * dna""    )[1:end  ])
        @test hash(seq^n) === hash((seq^n * dna"T"   )[1:end-1])
        @test hash(seq^n) === hash((seq^n * dna"TT"  )[1:end-2])
        @test hash(seq^n) === hash((seq^n * dna"TTT" )[1:end-3])
        @test hash(seq^n) === hash((seq^n * dna"TTTT")[1:end-4])
    end

    @test hash(rna"AAUU") === hash(rna"AAUU")
    @test hash(rna"AAUUAA"[3:5]) === hash(rna"UUA")
    @test hash(aa"MTTQAPMFTQPLQ") === hash(aa"MTTQAPMFTQPLQ")
    @test hash(aa"MTTQAPMFTQPLQ"[5:10]) === hash(aa"APMFTQ")

    @testset "MinHash" begin
        seq = DNASequence(random_dna(1000))
        h = minhash(seq, 10, 100)

        @test length(h) == 100
        @test h == minhash(seq, 10, 100)

        @test_throws BoundsError h[101]
    end

end


text:
module JungleHelperRemoteKevins

using ..Ahorn, Maple

@mapdef Entity "JungleHelper/RemoteKevin" RemoteKevin(x::Integer, y::Integer, restrained::Bool=false, axes::String="both", spriteXmlName::String="", spriteDirectory::String="", infiniteCharges::Bool=false, ignoreJumpthrus::Bool=false)

const placements = Ahorn.PlacementDict(
    "Slide Block (Restraintless) (Jungle Helper)" => Ahorn.EntityPlacement(
        RemoteKevin,
        "rectangle",
        Dict{String, Any}(
            "restrained" => false
        )
    ),
    "Slide Block (Restrained) (Jungle Helper)" => Ahorn.EntityPlacement(
        RemoteKevin,
        "rectangle",
        Dict{String, Any}(
            "restrained" => true
        )
    )
)

const frameImage = Dict{String, String}(
    "none" => "block00",
    "horizontal" => "block01",
    "vertical" => "block02",
    "both" => "block03"
)

Ahorn.editingOptions(entity::RemoteKevin) = Dict{String, Any}(
    "axes" => Maple.kevin_axes
)

kevinColor = (138, 156, 96) ./ 255
Ahorn.minimumSize(entity::RemoteKevin) = 24, 24
Ahorn.resizable(entity::RemoteKevin) = true, true

Ahorn.selection(entity::RemoteKevin) = Ahorn.getEntityRectangle(entity)

function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::RemoteKevin, room::Maple.Room)
    restrainedTex = (get(entity.data, "restrained", false) ? "JungleHelper/SlideBlockGreen" : "JungleHelper/SlideBlockRed")
    frame = string(restrainedTex, "/", frameImage[lowercase(get(entity.data, "axes", "both"))])

    width = Int(get(entity.data, "width", 32))
    height = Int(get(entity.data, "height", 32))

    if height >= 48 && width >= 48
        faceSprite = Ahorn.getSprite(restrainedTex * "/big_active_up00")
    else
        faceSprite = Ahorn.getSprite(restrainedTex * "/small_active_up00")
    end

    x, y = Ahorn.position(entity)

    width = Int(get(entity.data, "width", 32))
    height = Int(get(entity.data, "height", 32))

    tilesWidth = div(width, 8)
    tilesHeight = div(height, 8)

    Ahorn.drawRectangle(ctx, 2, 2, width - 4, height - 4, kevinColor)
    Ahorn.drawImage(ctx, faceSprite, div(width - faceSprite.width, 2), div(height - faceSprite.height, 2))

    for i in 2:tilesWidth - 1
        Ahorn.drawImage(ctx, frame, (i - 1) * 8, 0, 8, 0, 8, 8)
        Ahorn.drawImage(ctx, frame, (i - 1) * 8, height - 8, 8, 24, 8, 8)
    end

    for i in 2:tilesHeight - 1
        Ahorn.drawImage(ctx, frame, 0, (i - 1) * 8, 0, 8, 8, 8)
        Ahorn.drawImage(ctx, frame, width - 8, (i - 1) * 8, 24, 8, 8, 8)
    end

    Ahorn.drawImage(ctx, frame, 0, 0, 0, 0, 8, 8)
    Ahorn.drawImage(ctx, frame, width - 8, 0, 24, 0, 8, 8)
    Ahorn.drawImage(ctx, frame, 0, height - 8, 0, 24, 8, 8)
    Ahorn.drawImage(ctx, frame, width - 8, height - 8, 24, 24, 8, 8)
end

end


text:
using Plots, LaTeXStrings, Measures; pyplot()

a, c, m = 69069, 1, 2^32
next(z) = (a*z + c) % m

N = 10^6
data = Array{Float64,1}(undef, N)

x = 808
for i in 1:N
    data[i] = x/m
    global x = next(x)
end

p1 = scatter(1:1000, data[1:1000],
    c=:blue, m=4, msw=0, xlabel=L"n", ylabel=L"x_n")
p2 = histogram(data, bins=50, normed=:true,
    ylims=(0,1.1), xlabel="Support", ylabel="Density")
plot(p1, p2, size=(800, 400), legend=:none, margin = 5mm)

max_stars_repo_name: Yoshinobu-Ishizaki/StatsWithJuliaBook

max_issues_repo_name: Yoshinobu-Ishizaki/StatsWithJuliaBook

max_forks_repo_name: Yoshinobu-Ishizaki/StatsWithJuliaBook


text:
#MAIN ENTRY POINT
#final columns for the decompressed file

#TODO: Check the portfolio sorts and the factor returns
#(replicate on 2015/12/31)

#merges crsp and compustat
# note refresh merge will be automatically run if any of the other two are run
function formfactors(;
  refreshcomp::Bool = true,
  refreshcrsp::Bool = true,
  refreshmerge::Bool = true,
  refreshfactors::Bool = true,
  datapath::String=DATA_PATH,
  factname::String = FACT_NAME,
  injlsstream::Function = IN_JLS_STREAM,
  outjlsstream::Function = OUT_JLS_STREAM,
  validatemerged::Bool = true,
  parallel::Bool = false,
  outpath::String = OUT_PATH,
  writecsv::Bool = true)::Nothing

  local univ::DataFrame
  local fact::DataFrame

  #NOTE: Need to move this to the entry point if the entry point changes
  PARALLEL[] = parallel

  #refreshfactors must be true if any of the others are true
  refreshfactors = (refreshfactors || refreshmerge || refreshcrsp ||
    refreshcomp || validatemerged)

  #makes a serialization object so we don't have to keep parsing the CSV
  if refreshfactors || (!isfile("$datapath\\$factname.jls.lz4"))
    univ = makeuniv(refreshmerge=refreshmerge,  refreshcomp=refreshcomp,
      refreshcrsp=refreshcrsp, validatemerged=validatemerged) do univ::DataFrame
        formportfolios!(univ)
    end

    fact = formfactors(univ)

    #NOTE: may want to push the portfolio formation into univ once it is done
    #if so, the below will be just the factor portfolio
    outjlsstream("$datapath\\$factname.jls.lz4") do s
      serialize(s, fact)
    end

  else
    injlsstream("$datapath\\$factname.jls.lz4") do s
      fact = deserialize(s) #NOTE: DEBUG ONLY
    end
  end

  writecsv && (fact |> CSV.write("$outpath\\$factname.csv"))

  return nothing
end

#assigns the group for a portfolio
function assigngroups!(vals::AbstractVector{T},
  quantiles::AbstractVector{MFloat64},
  groups::AbstractVector{MInt},
  breakpoints::NTuple) where T

  #compute the quantiles
  qdist = StatsBase.ecdf((val::T->val).(vals))
  @inbounds @simd for i ∈ 1:length(vals)
    f::Float64 = vals[i]
    quantiles[i] = qdist(f)
  end

  #compute the groups
  groups .= (q::Float64-> findfirst(b::Float64->
      q≤b, breakpoints)).(quantiles)

  return nothing
end

function formportfolios!(univ::DataFrame;
    pcs::PortfolioConstructions = PortfolioConstructions(),
    weighton::Symbol = WEIGHT_ON, returnfield::Symbol=RETURN_FIELD)

  local sunivs::GroupedDataFrame

  N::Int = size(univ,1)
  #first pre-allocate the requisite fields
  for pc ∈ pcs
    univ[!, pc.Fquantile] = Vector{MFloat64}(undef, N)
    univ[!, pc.Fgroup] = Vector{MInt}(undef, N)
    univ[!, pc.Fweight] = Vector{MFloat64}(undef, N)
  end

  sunivs = groupby(univ, :date)
  for i ∈ 1:length(sunivs)
    suniv::SubDataFrame = sunivs[i]
    for pc ∈ pcs
      assigngroups!(suniv[!,pc.Fval],
        suniv[!, pc.Fquantile], suniv[!, pc.Fgroup], pc.breakpoints)
    end
  end

  println("size: ", N)
  #group field
  groupfields = (pc->pc.Fgroup).(pcs)
  univ.returndollars = Vector{MFloat64}(undef, N)
  for suniv ∈ groupby(univ, [:date; groupfields])
    for pc ∈ pcs
      suniv.returndollars .= suniv[!,weighton] .* suniv[!, returnfield]
    end
  end

  return univ
end

#this actually computes the factor returns
function computefactors(univ::DataFrame;
    pcs::PortfolioConstructions = PortfolioConstructions(),
    weighton::Symbol = WEIGHT_ON)

  fact::DataFrame = DataFrame(date=unique(univ.date))
  sort!(fact, :date)
  Nfact::Int = size(fact,1)
  fact.retid = collect(1:Nfact)

  #pre-allocate the return fields
  for pc ∈ pcs
     fact[!,pc.Fval] = zeros(Float64, Nfact)
  end

  #index the rows by date
  rfacts::Dict = Dict(r.date => r for r ∈ eachrow(fact))

  #now compute the returns
  groupfields::Vector{Symbol} = (pc->pc.Fgroup).(pcs)
  for suniv ∈ groupby(univ, :date)
    rfact = rfacts[suniv.date[1]]

    #split each date by triple sort
    for ssuniv ∈ groupby(suniv, groupfields)

      #this is the overall portfolio return
      portret::Float64 = sum(ssuniv.returndollars) / sum(ssuniv[!,weighton])
      #println("portret: $portret")

      for pc ∈ pcs
        #lookup the portfolio weight for the specific factor

        group::Int = ssuniv[1, pc.Fgroup]
        groupweight::Float64 = pc.groupweights[group]

        #println("groupweight: $groupweight")

        rfact[pc.Fval] += groupweight * portret
      end
    end
  end

  return fact
end

max_stars_repo_name: clintonTE/CCA

max_issues_repo_name: clintonTE/CCA

max_forks_repo_name: clintonTE/CCA


text:
#
# Update positions, simple integrator
#

function update_positions!(atoms :: Atoms, f :: Array{Float64}, v :: Array{Float64}, input :: MDInput)
  n = input.n
  dt = input.dt
  x = atoms.x
  for i in 1:n
    if atoms.status[i] == 2
      continue
    end
    x[i,1] = image(x[i,1] + v[i,1]*dt + 0.5*f[i,1]*(dt^2),input)
    x[i,2] = image(x[i,2] + v[i,2]*dt + 0.5*f[i,2]*(dt^2),input)
  end
end


text:
"""
    AugmentedNLPBlockModel{T, S} <: AbstractNLPModel{T, S}

A data type to store augmented subproblems.
"""
mutable struct AugmentedNLPBlockModel{T,S} <: AbstractNLPModel{T,S}
    meta::NLPModelMeta{T,S}
    counters::Counters
    subproblem::AbstractNLPModel
    λ::AbstractVector # dual variables
    ρ::Number # penalty Parameter
    A::AbstractMatrix # linking matrix
    b::AbstractVector
    sol::AbstractVector # current primal solution
    hess_info::AugmentedHessianInfo # precompute to save computation effort
end

"""
    AugmentedNLPBlockModel(
        nlp::AbstractNLPModel,
        λ::AbstractVector,
        ρ::Number,
        A::AbstractMatrix,
        b::AbstractVector,
        sol::AbstractVector
    )
Modifies a subproblem by penalizing and dualizing the linking constraints.

# Arguments
- `nlp::AbstractNLPModel`: the subproblem
- `λ::AbstractVector`: vector of dual variables
- `ρ::Number`: penalty parameter
- `A::AbstractMatrix`: full linking matrix
- `b::AbstractVector`: RHS vector for linking constraints
- `sol::AbstractVector`: vector of primal variables
"""
function AugmentedNLPBlockModel(
    nlp::AbstractNLPModel,
    λ::AbstractVector,
    ρ::Number,
    A::AbstractMatrix,
    b::AbstractVector,
    sol::AbstractVector,
)

    ATA = findnz(ρ .* A[:, nlp.var_idx]' * A[:, nlp.var_idx])

    block_hess_struct = hess_structure(nlp.problem_block)
    I = vcat(block_hess_struct[1], ATA[1])
    J = vcat(block_hess_struct[2], ATA[2])
    V = vcat(ones(nlp.meta.nnzh), ρ .* abs.(ATA[3]))
    aug_hess_struct = findnz(sparse(I, J, V, nlp.meta.nvar, nlp.meta.nvar))
    meta = update_nnzh(nlp.meta, length(aug_hess_struct[1]))

    hess_struct = AugmentedHessianInfo(
        (aug_hess_struct[1], aug_hess_struct[2]),
        (block_hess_struct[1], block_hess_struct[2]),
        ATA,
    )
    return AugmentedNLPBlockModel(meta, Counters(), nlp, λ, ρ, A, b, deepcopy(sol), hess_struct)
end

"""
    update_primal!(
      nlp::AugmentedNLPBlockModel,
      sol::AbstractVector
    )
Updates the primal solution estimate for the augmented nlp block `nlp`.

# Arguments
- `nlp::AugmentedNLPBlockModel`: the subproblem
- `sol::AbstractVector`: vector of primal variables
"""
function update_primal!(nlp::AugmentedNLPBlockModel, sol::AbstractVector)
    nlp.sol .= sol
end

"""
    update_dual!(
      nlp::AugmentedNLPBlockModel,
      λ::AbstractVector,
    )
Updates the dual solution estimate in-place for the augmented nlp block `nlp`.

# Arguments
- `nlp::AugmentedNLPBlockModel`: the subproblem
- `λ::AbstractVector`: vector of dual variables
"""
function update_dual!(nlp::AugmentedNLPBlockModel, λ::AbstractVector)
    nlp.λ .= λ
end

function NLPModels.obj(nlp::AugmentedNLPBlockModel, x::AbstractVector)
    nlp.sol[nlp.subproblem.var_idx] = x

    return obj(nlp.subproblem.problem_block, x) +
           dot(nlp.λ, nlp.A[:, nlp.subproblem.var_idx], x) +
           (nlp.ρ / 2) * norm(nlp.A * nlp.sol - nlp.b)^2
end

function NLPModels.grad!(nlp::AugmentedNLPBlockModel, x::AbstractVector, g::AbstractVector)
    nlp.sol[nlp.subproblem.var_idx] = x

    grad!(nlp.subproblem.problem_block, x, g)
    mul!(
        g,
        nlp.A[:, nlp.subproblem.var_idx]',
        nlp.λ + nlp.ρ .* (nlp.A * nlp.sol - nlp.b),
        1,
        1,
    )
    return g
end

function NLPModels.hess_structure!(
    nlp::AugmentedNLPBlockModel,
    rows::AbstractVector{T},
    cols::AbstractVector{T},
) where {T}
    rows .= nlp.hess_info.augmented_hessian_struct[1]
    cols .= nlp.hess_info.augmented_hessian_struct[2]
    return rows, cols
end

function NLPModels.hess_coord!(
    nlp::AugmentedNLPBlockModel,
    x::AbstractVector{T},
    vals::AbstractVector{T};
    obj_weight = one(T),
) where {T}
    get_augmented_hessian_coord!(nlp, x, vals, obj_weight)
    return vals
end

function NLPModels.hess_coord!(
    nlp::AugmentedNLPBlockModel,
    x::AbstractVector{T},
    y::AbstractVector{T},
    vals::AbstractVector{T};
    obj_weight = one(T),
) where {T}
    get_augmented_hessian_coord!(nlp, x, vals, obj_weight, y = y)
    return vals
end

function NLPModels.cons!(nlp::AugmentedNLPBlockModel, x::AbstractVector, cx::AbstractVector)
    return cons!(nlp.subproblem.problem_block, x, cx)
end

function NLPModels.jac_structure!(
    nlp::AugmentedNLPBlockModel,
    rows::AbstractVector{T},
    cols::AbstractVector{T},
) where {T}
    return jac_structure!(nlp.subproblem.problem_block, rows, cols)
end

function NLPModels.jac_coord!(
    nlp::AugmentedNLPBlockModel,
    x::AbstractVector,
    vals::AbstractVector,
)
    return jac_coord!(nlp.subproblem.problem_block, x, vals)
end


text:
module Trees

import MyterialColors: yellow, orange, red, blue
import OrderedCollections: OrderedDict
import Base: @kwdef

using InteractiveUtils

import Term:
    loop_last,
    escape_brackets,
    fillin,
    highlight,
    int,
    term_theme,
    textlen,
    truncate,
    expr2string

import ..Renderables: AbstractRenderable
import ..Layout: vstack, pad, hLine
import ..Style: apply_style
import ..Segments: Segment
import ..Measures: Measure
import ..Panels: Panel

export Tree

# ---------------------------------------------------------------------------- #
#                                    GUIDES                                    #
# ---------------------------------------------------------------------------- #

treeguides = Dict(
    :standardtree => ("    ", "│   ", "├── ", "└── "),
    :boldtree => ("    ", "┃   ", "┣━━ ", "┗━━ "),
    :asciitree => ("    ", "|   ", "+-- ", "`-- "),
)

"""
    TreeGuides

Store strings to make up a `Tree`'s guides (the line elements showing connections).
"""
struct TreeGuides
    space::String
    vline::String
    branch::String
    leaf::String
end

"""
    TreeGuides(guides_type::Symbol, style::String)

Get tree guides with style information applied
"""
TreeGuides(guides_type::Symbol, style::String) =
    TreeGuides(map((g) -> apply_style("{$style}$g{/$style}"), treeguides[guides_type])...)

# ---------------------------------------------------------------------------- #
#                                     TREE                                     #
# ---------------------------------------------------------------------------- #

# ----------------------------------- leaf ----------------------------------- #

"""
    asleaf

Style an object to render it as a a string
"""
function asleaf end

asleaf(x) = truncate(highlight(string(x)), term_theme[].tree_max_width)
asleaf(x::Nothing) = nothing
function asleaf(x::AbstractVector)
    return truncate((escape_brackets ∘ string)(x), term_theme[].tree_max_width)
end
asleaf(x::AbstractString) = truncate(highlight(x, :string), term_theme[].tree_max_width)

"""
    Leaf

End items in a `Tree`. No sub-trees.
"""
struct Leaf
    name::Union{Nothing,String}
    text::Union{Nothing,String}
end

# ----------------------------------- tree ----------------------------------- #
"""
    Tree

A tree is composed of nodes (other trees) and leaves (end items).
It renders as a hierarchical structure with lines (guides) connecting the various elements
"""
@kwdef struct Tree <: AbstractRenderable
    segments::Union{Nothing,Vector{Segment}} = nothing
    measure::Union{Nothing,Measure} = nothing

    name::String
    level::Int
    nodes::Vector{Tree}
    leaves::Vector{Leaf}

    title_style::String = term_theme[].tree_title_style
    node_style::String = term_theme[].tree_node_style
    leaf_style::String = term_theme[].tree_leaf_style
    guides_style::String = term_theme[].tree_guide_style
    guides_type::Symbol = :standardtree
end

"""
Show/render a `Tree`
"""
function Base.show(io::IO, tree::Tree)
    if io != stdout
        print(io, "Tree: $(length(tree.nodes)) nodes, $(length(tree.leaves)) leaves")
    else
        for seg in tree.segments
            println(io, seg)
        end
    end
end

"""
Add a new node to an existing tree's nodes or levaes.
"""
function addnode!(nodes::Vector{Tree}, leaves::Vector{Leaf}, level, k, v::AbstractDict)
    return push!(
        nodes,
        Tree(
            v;
            level = level + 1,
            title = truncate(string(k), term_theme[].tree_max_width),
        ),
    )
end

function addnode!(nodes::Vector{Tree}, leaves::Vector{Leaf}, level, k, v::Pair)
    k = if isnothing(v.first)
        nothing
    else
        truncate(string(v.first), term_theme[].tree_max_width)
    end
    return push!(leaves, Leaf(k, asleaf(v.second)))
end

function addnode!(nodes::Vector{Tree}, leaves::Vector{Leaf}, level, k, v::Any)
    k = isnothing(k) ? nothing : truncate(string(k), term_theme[].tree_max_width)
    return push!(leaves, Leaf(k, asleaf(v)))
end

function addnode!(nodes::Vector{Tree}, leaves::Vector{Leaf}, level, k, v::Vector)
    for _v in v
        _k = _v isa Dict ? collect(keys(_v))[1] : (v isa Pair ? _v.first : v)
        addnode!(nodes, leaves, level + 1, _k, _v)
    end
end

"""
    Tree(data::Union{Dict, Pair}; level=0, title::String="tree", kwargs...)

Construct a `Tree` out of a `Dict`. Recursively handle nested `Dict`s.
"""
function Tree(
    data::Union{AbstractDict,Pair,Vector};
    level = 0,
    title::String = "tree",
    kwargs...,
)

    # initialize
    nodes::Vector{Tree} = []
    leaves::Vector{Leaf} = []

    # go over all entries
    for (k, v) in zip(keys(data), values(data))
        addnode!(nodes, leaves, level, k, v)
    end

    # if we're handling the first tree, render it. Otherwise parse nested trees.
    if level > 0
        # we don't need to render
        return Tree(;
            name = title,
            level = level,
            nodes = nodes,
            leaves = leaves,
            kwargs...,
        )
    else
        # render and get measure
        segments = render(
            Tree(; name = title, level = level, nodes = nodes, leaves = leaves, kwargs...),
        )
        measure = Measure(segments)

        return Tree(;
            segments = segments,
            measure = measure,
            name = truncate(title, term_theme[].tree_max_width),
            level = level,
            nodes = nodes,
            leaves = leaves,
            kwargs...,
        )
    end
end

# ---------------------------------- render ---------------------------------- #

"""
    render(tree::Tree)::Vector{Segment}

Render a `Tree` into segments. Recursively handle nested trees.

"""
function render(
    tree::Tree;
    prevguides::String = "",
    lasttree = false,
    waslast = [],
    guides = nothing,
)::Vector{Segment}
    guides = isnothing(guides) ? TreeGuides(tree.guides_type, tree.guides_style) : guides
    hasleaves = length(tree.leaves) > 0

    segments::Vector{Segment} = []

    """
        Add a segment to the segments vector
    """
    _add(x::String, style) = push!(segments, Segment(x, style))
    _add(x::String) = _add(x, "default")

    # ------------------------------ render in parts ----------------------------- #
    # render initial part
    if tree.level == 0
        header_text = "{$(tree.title_style)}$(tree.name){/$(tree.title_style)}"
        header =
            (" " * header_text * " ") /
            hLine(textlen(tree.name) + 2; style = "$(tree.title_style) dim", box = :HEAVY)

        append!(segments, header.segments)
    else
        _pre_guides = ""
        for (n, (l, last)) in enumerate(loop_last(waslast))
            # ugly, get the correct sequence of guides based on where we are/what came before
            if last
                _end = lasttree ? guides.leaf : guides.branch
                _pre_guides *= l ? _end : guides.space

            else
                _end = n == length(waslast) ? guides.branch : guides.vline
                _pre_guides *= lasttree ? (l ? guides.leaf : guides.vline) : _end
            end
        end

        _add(_pre_guides * "{$(tree.node_style)}$(tree.name){/$(tree.node_style)}")
    end
    tree.level == 0 && _add(prevguides * guides.vline)

    # render sub-trees
    for (last, node) in loop_last(tree.nodes)
        # check if it's the last entry in the tree
        lasttree = last && !hasleaves

        # get the appropriate guides
        prev = prevguides * (lasttree ? guides.space : guides.vline)

        append!(
            segments,
            render(
                node;
                prevguides = prev,
                lasttree = lasttree,
                waslast = vcat(waslast, lasttree),
                guides = guides,
            ),
        )
        hasleaves && length(node.leaves) > 0 && _add(prevguides * guides.vline)
    end

    # render leaves
    if hasleaves
        for (last, leaf) in loop_last(tree.leaves)
            seg = last ? guides.leaf : guides.branch
            if isnothing(leaf.text)
                k = isnothing(leaf.name) ? "" : highlight(leaf.name)
                v = ""
            else
                k = if isnothing(leaf.name)
                    ""
                else
                    "{$(tree.leaf_style)}$(leaf.name){/$(tree.leaf_style)}: "
                end
                v = leaf.text
            end
            _add(prevguides * seg * k * v)
        end
    end

    # left pad segments
    if tree.level == 0
        header_length = length(header.segments)
        padded_segments = vcat(
            header.segments...,
            pad(segments[(header_length + 1):end], int(header.measure.w / 2 - 1))...,
        )
        return fillin(padded_segments)
    else
        return segments
    end
end

# ---------------------------------------------------------------------------- #
#                                HIERARCHY TREE                                #
# ---------------------------------------------------------------------------- #
"""
Apply style for the type whose hierarchy Tree we are making
"""
style_T(T) = "{orange1 italic underline}$T{/orange1 italic underline}"

"""
    make_hierarchy_dict(x::Vector{DataType}, T::DataType, Tsubs::Dict)::Dict

Recursively create a dictionary with the types hierarchy for `T`.
`Tsubs` carries information about T's subtypes.
The Dict is made backwards. From  the deepest levels up.
"""
function make_hierarchy_dict(x::NTuple, T::DataType, Tsubs::Dict)::Dict
    data = Dict()
    prev = ""
    for (n, y) in enumerate(x)
        if n == 1
            continue
        elseif n < length(x)
            subs = Dict()
            for s in subtypes(y)
                if s == T
                    subs[style_T(s)] = Tsubs
                else
                    subs[string(s)] = nothing
                end
            end

            if n == 2
                data = subs
            else
                subs[prev] = data
                data = subs
            end

            prev = string(y)
        end
    end
    return data
end

"""
    Tree(T::DataType)::Tree

Construct a `Tree` visualization of `T`'s types hierarchy
"""
function Tree(T::DataType)::Tree
    # create a dictionary of types hierarchy
    subs = Dict(string(s) => nothing for s in subtypes(T))
    data = make_hierarchy_dict(supertypes(T), T, subs)

    return Tree(
        data;
        # title=string(supertypes(T)[end]),
        title = "Any",
        title_style = "bright_green italic",
        guides_style = "green dim",
    )
end

function _key(e::Expr)
    if length(e.args) > 1
        "$(expr2string(e))  {dim blue}($(e.head): {/dim blue}{red bold default}$(e.args[1]){/red bold default}{dim blue}){/dim blue}"
    else
        string(e.head)
    end
end
_values(e::Expr) = length(e.args) > 1 ? e.args[2:end] : e.args

_pair(x) = nothing => x
_pair(e::Expr) = Dict(_key(e) => _pair.(_values(e)))

function Tree(expr::Expr; kwargs...)
    parsed = _pair(expr)
    parsed = Dict(collect(keys(parsed))[1] => parsed)
    return Tree(parsed; title = expr2string(expr))
end

end


text:
module Configurations

export read_configs

const COMONICON_TOML = ["Comonicon.toml", "JuliaComonicon.toml"]

using PackageCompiler
using Pkg.TOML
using ..Comonicon.PATH

"""
abstract type for Comonicon configurations.
"""
abstract type AbstractConfiguration end

"""
    Install <: AbstractConfiguration

Installation configurations.

## Keywords

- `path`: installation path.
- `completion`: set to `true` to install shell auto-completion scripts.
- `quiet`: print logs or not, default is `false`.
- `compile`: julia compiler option for CLIs if not built as standalone application, default is "min".
- `optimize`: julia compiler option for CLIs if not built as standalone application, default is `2`.
"""
Base.@kwdef struct Install <: AbstractConfiguration
    path::String="~/.julia"
    completion::Bool=true
    quiet::Bool=false
    compile::String="yes"
    optimize::Int=2
end

"""
    Precompile <: AbstractConfiguration

Precompilation files for `PackageCompiler`.

## Keywords

- `execution_file`: precompile execution file.
- `statements_file`: precompile statements file.
"""
Base.@kwdef struct Precompile <: AbstractConfiguration
    execution_file::Vector{String} = String[]
    statements_file::Vector{String} = String[]
end

"""
    SysImg <: AbstractConfiguration

System image build configurations.

## Keywords

- `path`: system image path to generate into, default is "deps/lib".
- `incremental`: set to `true` to build incrementally, default is `true`.
- `filter_stdlibs`: set to `true` to filter out unused stdlibs, default is `false`.
- `cpu_target`: cpu target to build, default is `PackageCompiler.default_app_cpu_target()`.
- `precompile`: precompile configurations, see [`Precompile`](@ref), default is `Precompile()`.
"""
Base.@kwdef struct SysImg <: AbstractConfiguration
    path::String="deps"
    incremental::Bool=true
    filter_stdlibs::Bool=false
    cpu_target::String=PackageCompiler.default_app_cpu_target()
    precompile::Precompile = Precompile()

    function SysImg(path::String, incremental::Bool, filter_stdlibs::Bool, cpu_target::String, precompile::Precompile)
        if isabspath(path)
            throw(ArgumentError("sysimg path must be project relative"))
        end
        new(path, incremental, filter_stdlibs, cpu_target, precompile)
    end
end

"""
    Download <: AbstractConfiguration

Download information.

## Keywords

- `host`: where are the tarballs hosted, default is "github.com"
- `user`: required, user name on the host.
- `repo`: required, repo name on the host.

!!! note
    Currently this only supports github, and this is considered experimental.
"""
Base.@kwdef struct Download <: AbstractConfiguration
    host::String="github.com"
    user::String
    repo::String
end

"""
    Application <: AbstractConfiguration

Application build configurations.

## Keywords

- `path`: application build path, default is "build".
- `incremental`: set to `true` to build incrementally, default is `true`.
- `filter_stdlibs`: set to `true` to filter out unused stdlibs, default is `false`.
- `cpu_target`: cpu target to build, default is `PackageCompiler.default_app_cpu_target()`.
- `precompile`: precompile configurations, see [`Precompile`](@ref), default is `Precompile()`.
"""
Base.@kwdef struct Application <: AbstractConfiguration
    path::String="build"
    incremental::Bool=false
    filter_stdlibs::Bool=true
    cpu_target::String=PackageCompiler.default_app_cpu_target()
    precompile::Precompile=Precompile()

    function Application(path::String, incremental::Bool, filter_stdlibs::Bool, cpu_target::String, precompile::Precompile)
        if isabspath(path)
            throw(ArgumentError("build path must be project relative"))
        end
        new(path, incremental, filter_stdlibs, cpu_target, precompile)
    end
end

Base.@kwdef struct Daemon <: AbstractConfiguration
end

"""
    Comonicon <: AbstractConfiguration

Build configurations for Comonicon. One can set this option
via `Comonicon.toml` under the root path of a Julia
project directory and read in using [`read_configs`](@ref).

## Keywords

- `name`: required, the name of CLI file to install.
- `install`: installation options, see also [`Install`](@ref).
- `sysimg`: system image build options, see also [`SysImg`](@ref).
- `download`: download options, see also [`Download`](@ref).
- `application`: application build options, see also [`Application`](@ref).
"""
Base.@kwdef struct Comonicon <: AbstractConfiguration
    name::String

    install::Install = Install()
    sysimg::Union{SysImg, Nothing} = nothing
    download::Union{Download, Nothing} = nothing
    application::Union{Application, Nothing} = nothing
end

function Base.show(io::IO, x::AbstractConfiguration)
    indent = get(io, :indent, 0)

    summary(io, x)
    println(io, "(")
    fnames = fieldnames(typeof(x))
    for each in fieldnames(typeof(x))
        inner_io = IOContext(io, :indent => indent+2)
        print(inner_io, " "^indent, " "^2, each, " = ")
        show(inner_io, getfield(x, each))
        println(inner_io, ", ")
    end
    print(io, " "^indent, ")")
    return
end

function _list_configurations(::Type{T}) where {T <: AbstractConfiguration}
    map(x->" "^2 * string(x), fieldnames(T))
end

function Comonicon(d::Dict{String})
    haskey(d, "name") || error("key \"name\" is missing in (Julia)Comonicon.toml")

    return Comonicon(;
        name = d["name"],
        install = haskey(d, "install") ? Install(d["install"]) : Install(),
        sysimg = haskey(d, "sysimg") ? SysImg(d["sysimg"]) : nothing,
        download = haskey(d, "download") ? Download(d["download"]) : nothing,
        application = haskey(d, "application") ? Application(d["application"]) : nothing,
    )
end

function _handle_precompile(d::Dict{String})
    return _to_kwargs(d) do k, v
        if k == "precompile"
            return Precompile(v)
        else
            return v
        end
    end
end

function _to_kwargs(f, d::Dict{String})
    kwargs = Dict{Symbol, Any}()
    for (k, v) in d
        kwargs[Symbol(k)] = f(k, v)
    end
    return kwargs
end

_to_kwargs(d::Dict{String}) = _to_kwargs((k,v)->v, d)

function SysImg(d::Dict{String})
    return SysImg(;_handle_precompile(d)...)
end

function Application(d::Dict{String})
    return Application(;_handle_precompile(d)...)
end

function (::Type{T})(d::Dict{String}) where {T <: AbstractConfiguration}
    return T(;_to_kwargs(d)...)
end

"""
    find_comonicon_toml(path::String)

Find `Comonicon.toml` or `JuliaComonicon.toml` in given path.
"""
function find_comonicon_toml(path::String)
    # user input file path
    basename(path) in COMONICON_TOML && return path

    # user input dir path
    for file in COMONICON_TOML
        path = joinpath(path, file)
        if ispath(path)
            return path
        end
    end
    return
end

"""
    read_toml(path::String)

Read `Comonicon.toml` or `JuliaComonicon.toml` in given path.
"""
function read_toml(path::String)
    file = find_comonicon_toml(path)
    file === nothing && return Dict{String, Any}()
    return TOML.parsefile(file)
end

"""
    read_toml(mod::Module)

Read `Comonicon.toml` or `JuliaComonicon.toml` in given module's project path.
"""
function read_toml(mod::Module)
    return read_toml(PATH.project(mod))
end

"""
    read_configs(comonicon; kwargs...)

Read in Comonicon build options. The argument `comonicon` can be:

- a module of a Comonicon CLI project.
- a path to a Comonicon CLI project that contains either `JuliaComonicon.toml` or `Comonicon.toml`.
- a path to a Comonicon CLI build configuration file named either `JuliaComonicon.toml` or `Comonicon.toml`.

In some cases, you might want to change the configuration written in the TOML file temporarily, e.g for writing
build tests etc. In this case, you can modify the configuration using corresponding keyword arguments.

keyword arguments of [`Application`](@ref) and [`SysImg`](@ref) are the same, thus keys like `filter_stdlibs`
are considered ambiguous in `read_configs`, but you can specifiy them by specifiy the specific [`Application`](@ref)
or [`SysImg`](@ref) object, e.g

```julia
read_configs(MyCLI; sysimg=SysImg(filter_stdlibs=false))
```

See also [`Comonicon`](@ref), [`Install`](@ref), [`SysImg`](@ref), [`Application`](@ref),
[`Download`](@ref), [`Precompile`](@ref).
"""
function read_configs(m::Union{Module, String}; kwargs...)
    configurations = read_toml(m)

    for (k, v) in kwargs
        if k in fieldnames(Comonicon)
            configurations[string(k)] = v
        elseif k in fieldnames(Install)
            option_install = get!(configurations, "install", Dict{String, Any}())
            option_install[string(k)] = v
        elseif k in fieldnames(Download)
            option_download = get!(configurations, "download", Dict{String, Any}())
            option_download[string(k)] = v
        elseif k in fieldnames(SysImg) || k in fieldnames(Application)
            throw(ArgumentError(
                "ambiguous option, please use SysImg/Application struct " *
                "with keyword \"sysimg\"/\"application\" to specifiy option \"$k\""
            ))
        else
            throw(ArgumentError("""
            unsupported kwargs $k, options are:
            $(join([
                "comonicon options:",
                _list_configurations(Comonicon)...,
                "",
                "install options:",
                _list_configurations(Install)...,
                "",
                "download options:",
                _list_configurations(Download)...,
            ], "\n"))
            """))
        end
    end

    if !haskey(configurations, "name")
        configurations["name"] = default_cmd_name(m)
    end

    return Comonicon(configurations)
end

default_cmd_name(m::Module) = lowercase(string(nameof(m)))
default_cmd_name(path::String) = error("missing field \"name\" in (Julia)Comonicon.toml")

function Base.:(==)(x::T, y::T) where {T <: AbstractConfiguration}
    return all(fieldnames(T)) do field
        getfield(x, field) == getfield(y, field)
    end
end

end # Configs


text:
showbounds(bounds) = showbounds!(zeros(RGBA{N0f8}, boundssize(bounds), bounds))

function showbounds!(img, bounds::AbstractArray{<:SVector{2}}) where N
    points = [
        bounds[1] + SVector(1, 1),
        bounds[2] + SVector(0, 1),
        bounds[4] + SVector(0, 0),
        bounds[3] + SVector(1, 0),
    ]
    drawpolygon!(img, points, RGBA(0, 0, 0, 1))
    return img
end

function drawkeypoint!(img, point::SVector{N}, C; sz = 3) where N
    I = CartesianIndex(Tuple(round.(Int, reverse(point))))
    offset = sz ÷ 2
    Is = I-offset*one(CartesianIndex{N}):I+offset*one(CartesianIndex{N})
    img[Is[[checkbounds(Bool, img, I) for I in Is]]] .= C
end

function drawpolygon!(img, points, C)
    polydraw = ImageDraw.Polygon([Tuple(round.(Int, reverse(point))) for point in reshape(points, :)])
    draw!(img, polydraw, C)
end


text:
module TestParameterTypes

using Mimi
using Test

import Mimi:
    model_params, model_param, TimestepMatrix, TimestepVector,
    ArrayModelParameter, ScalarModelParameter, FixedTimestep, import_params!,
    set_first_last!, _get_param_times

#
# Test that simple constructors don't error
#

values = [1,2,3]
dim_names = [:time]
shared = true
p1 = ArrayModelParameter(values, dim_names, shared)
p2 = ArrayModelParameter(values, dim_names)
@test p1.values == p2.values == values
@test p1.dim_names == p2.dim_names == dim_names
@test Mimi.is_shared(p1) && !Mimi.is_shared(p2)

p3 = ScalarModelParameter(3, shared)
p4 = ScalarModelParameter(3)
@test p3.value == p4.value == 3
@test Mimi.is_shared(p3) && !Mimi.is_shared(p4)

#
# Test that parameter type mismatches are caught
#
expr = :(
    @defcomp BadComp1 begin
        a = Parameter(index=[time, regions], default=[10, 11, 12])  # should be 2D default
        function run_timestep(p, v, d, t)
        end
    end
)
@test_throws ErrorException eval(expr)

expr = :(
    @defcomp BadComp2 begin
        a = Parameter(default=[10, 11, 12])  # should be scalar default
        function run_timestep(p, v, d, t)
        end
    end
)
@test_throws ErrorException eval(expr)

#
# Test that the old type parameterization syntax errors
#
expr = :(
    @defcomp BadComp3 begin
        a::Int = Parameter()
        function run_timestep(p, v, d, t)
        end
    end
)
@test_throws LoadError eval(expr)

@defcomp MyComp begin
    a = Parameter(index=[time, regions], default=ones(101,3))
    b = Parameter(index=[time], default=1:101)
    c = Parameter(index=[regions])
    d = Parameter()
    e = Parameter(index=[four])
    f = Parameter{Array{Float64, 2}}()
    g = Parameter{Int}(default=10.0)    # value should be Int despite Float64 default
    h = Parameter(default=10)           # should be "numtype", despite Int default
    j = Parameter{Int}(index = [regions])

    function run_timestep(p, v, d, t)
    end
end

# Check that explicit number type for model works as expected
numtype = Float32
arrtype = Union{Missing, numtype}

m = Model(numtype)

set_dimension!(m, :time, 2000:2100)
set_dimension!(m, :regions, 3)
set_dimension!(m, :four, 4)

add_comp!(m, MyComp)
update_param!(m, :MyComp, :c, [4,5,6])
update_param!(m, :MyComp, :d, 0.5)   # 32-bit float constant
update_param!(m, :MyComp, :e, [1,2,3,4])
update_param!(m, :MyComp, :f, reshape(1:16, 4, 4))
update_param!(m, :MyComp, :j, [1,2,3])

Mimi.build!(m)
extpars = model_params(m.mi.md)

a_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :a)
b_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :b)
c_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :c)
d_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :d)
e_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :e)
f_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :f)
g_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :g)
h_sym = Mimi.get_model_param_name(m.mi.md, :MyComp, :h)

@test isa(extpars[a_sym], ArrayModelParameter)
@test isa(extpars[b_sym], ArrayModelParameter)
@test _get_param_times(extpars[a_sym]) == _get_param_times(extpars[b_sym]) == 2000:2100

@test isa(extpars[c_sym], ArrayModelParameter)
@test isa(extpars[d_sym], ScalarModelParameter)
@test isa(extpars[e_sym], ArrayModelParameter)
@test isa(extpars[f_sym], ScalarModelParameter) # note that :f is stored as a scalar parameter even though its values are an array

@test typeof(extpars[a_sym].values) == TimestepMatrix{FixedTimestep{2000, 1, 2100}, arrtype, 1, Array{arrtype, 2}}
@test typeof(extpars[b_sym].values) == TimestepVector{FixedTimestep{2000, 1, 2100}, arrtype, Array{arrtype, 1}}

@test typeof(extpars[c_sym].values) == Array{arrtype, 1}
@test typeof(extpars[d_sym].value) == numtype
@test typeof(extpars[e_sym].values) == Array{arrtype, 1}
@test typeof(extpars[f_sym].value) == Array{Float64, 2}
@test typeof(extpars[g_sym].value) <: Int
@test typeof(extpars[h_sym].value) == numtype

# test updating parameters
@test_throws ErrorException update_param!(m, :a, 5) # expects an array
@test_throws ErrorException update_param!(m, :a, ones(101)) # wrong size
@test_throws ErrorException update_param!(m, :a, fill("hi", 101, 3)) # wrong type

update_param!(m, :MyComp, :a, Array{Int,2}(zeros(101, 3))) # should be able to convert from Int to Float
@test_throws ErrorException update_param!(m, :MyComp, :d, ones(5)) # wrong type; should be scalar
update_param!(m, :MyComp, :d, 5) # should work, will convert to float
new_extpars = model_params(m)    # Since there are changes since the last build, need to access the updated dictionary in the model definition
@test extpars[d_sym].value == 0.5      # The original dictionary still has the old value
@test new_extpars[d_sym].value == 5.   # The new dictionary has the updated value
@test_throws ErrorException update_param!(m, :e, 5) # wrong type; should be array
@test_throws ErrorException update_param!(m, :e, ones(10)) # wrong size
update_param!(m, :MyComp, :e, [4,5,6,7])

@test length(extpars) == length(new_extpars) == 9 # we replaced the unshared default for :a with a shared for :a
@test typeof(new_extpars[a_sym].values) == TimestepMatrix{FixedTimestep{2000, 1, 2100}, arrtype, 1, Array{arrtype, 2}}

@test typeof(new_extpars[d_sym].value) == numtype
@test typeof(new_extpars[e_sym].values) == Array{arrtype, 1}

#------------------------------------------------------------------------------
# Test updating TimestepArrays with update_param!
#------------------------------------------------------------------------------

@defcomp MyComp2 begin
    x=Parameter(index=[time])
    y=Variable(index=[time])
    function run_timestep(p,v,d,t)
        v.y[t]=p.x[t]
    end
end

# 1. update_param! with Fixed Timesteps

m = Model()
set_dimension!(m, :time, 2000:2004)
add_comp!(m, MyComp2, first=2001, last=2003)
update_param!(m, :MyComp2, :x, [1, 2, 3, 4, 5])
# Year      x       Model   MyComp2
# 2000      1       first
# 2001      2               first
# 2002      3
# 2003      4               last
# 2004      5      last

update_param!(m, :MyComp2, :x, [2.,3.,4.,5.,6.])
update_param!(m, :MyComp2, :x, zeros(5))
update_param!(m, :MyComp2, :x, [1,2,3,4,5])

set_dimension!(m, :time, 1999:2001)
# Year      x       Model   MyComp2
# 1999      missing first
# 2000      1
# 2001      2       last    first, last

x = model_param(m, :MyComp2, :x)
@test ismissing(x.values.data[1])
@test x.values.data[2:3] == [1.0, 2.0]
@test _get_param_times(x) == 1999:2001
run(m) # should be runnable

update_param!(m, :MyComp2, :x, [2, 3, 4]) # change x to match
# Year      x       Model   MyComp2
# 1999      2       first
# 2000      3
# 2001      4       last    first, last

x = model_param(m, :MyComp2, :x)
@test x.values isa Mimi.TimestepArray{Mimi.FixedTimestep{1999, 1, 2001}, Union{Missing,Float64}, 1}
@test x.values.data == [2., 3., 4.]
run(m)
@test ismissing(m[:MyComp2, :y][1])  # 1999
@test ismissing(m[:MyComp2, :y][2])  # 2000
@test m[:MyComp2, :y][3] == 4   # 2001

set_first_last!(m, :MyComp2, first = 1999, last = 2001)
# Year      x       Model   MyComp2
# 1999      2       first   first
# 2000      3
# 2001      4       last    last

run(m)
@test m[:MyComp2, :y] == [2, 3, 4]

# 2. Test with Variable Timesteps

m = Model()
set_dimension!(m, :time, [2000, 2005, 2020])
add_comp!(m, MyComp2)
update_param!(m, :MyComp2, :x, [1, 2, 3])
# Year      x       Model   MyComp2
# 2000      1       first   first
# 2005      2
# 2010      3       last    last

set_dimension!(m, :time, [2000, 2005, 2020, 2100])
# Year      x       Model   MyComp2
# 2000      1       first   first
# 2005      2
# 2020      3               last
# 2100      missing last

x = model_param(m, :MyComp2, :x)
@test ismissing(x.values.data[4])
@test x.values.data[1:3] == [1.0, 2.0, 3.0]

update_param!(m, :MyComp2, :x, [2, 3, 4, 5]) # change x to match
# Year      x       Model   MyComp2
# 2000      2       first   first
# 2005      3
# 2020      4               last
# 2100      5        last

x = model_param(m, :MyComp2, :x)
@test x.values isa Mimi.TimestepArray{Mimi.VariableTimestep{(2000, 2005, 2020, 2100)}, Union{Missing,Float64}, 1}
@test x.values.data == [2., 3., 4., 5.]
run(m)
@test m[:MyComp2, :y][1] == 2   # 2000
@test m[:MyComp2, :y][2] == 3   # 2005
@test m[:MyComp2, :y][3] == 4   # 2020
@test ismissing(m[:MyComp2, :y][4]) # 2100 - past last attribute for component

set_first_last!(m, :MyComp2, first = 2000, last = 2020)
# Year      x       Model   MyComp2
# 2000      1       first   first
# 2005      2
# 2020      3       last    last

run(m)
@test m[:MyComp2, :y][1:3] == [2., 3., 4.]
@test ismissing(m[:MyComp2, :y][4])

# 3. Test updating from a dictionary

m = Model()
set_dimension!(m, :time, [2000, 2005, 2020])
add_comp!(m, MyComp2)
update_param!(m, :MyComp2, :x, [1, 2, 3])

set_dimension!(m, :time, [2000, 2005, 2020, 2100])

update_params!(m, Dict((:MyComp2, :x)=>[2, 3, 4, 5]))
x = model_param(m, :MyComp2, :x)
@test x.values isa Mimi.TimestepArray{Mimi.VariableTimestep{(2000, 2005, 2020, 2100)}, Union{Missing,Float64}, 1}
@test x.values.data == [2., 3., 4., 5.]
run(m)

@test m[:MyComp2, :y][1] == 2   # 2000
@test m[:MyComp2, :y][2] == 3   # 2005
@test m[:MyComp2, :y][3] == 4   # 2020
@test ismissing(m[:MyComp2, :y][4])   # 2100

# 4. Test updating the time index to a different length

m = Model()
set_dimension!(m, :time, 2000:2002)     # length 3
add_comp!(m, MyComp2)
update_param!(m, :MyComp2, :x, [1, 2, 3])
# Year      x       Model   MyComp2
# 2000      1       first   first
# 2001      2
# 2002      3       last    last

set_dimension!(m, :time, 1999:2003)     # length 5
update_param!(m, :MyComp2, :x, [2, 3, 4, 5, 6])
# Year      x       Model   MyComp2
# 1999      2       first
# 2000      3               first
# 2001      4
# 2002      5               last
# 2003      6       last

x = model_param(m, :MyComp2, :x)
@test x.values isa Mimi.TimestepArray{Mimi.FixedTimestep{1999, 1, 2003}, Union{Missing, Float64}, 1, 1}
@test x.values.data == [2., 3., 4., 5., 6.]

run(m)
@test ismissing(m[:MyComp2, :y][1])
@test m[:MyComp2, :y][2:4] == [3., 4., 5.]
@test ismissing(m[:MyComp2, :y][5])

set_first_last!(m, :MyComp2, first = 1999, last = 2001)
# Year      x       Model   MyComp2
# 1999      2       first   first
# 2000      3
# 2001      4               last
# 2002      5
# 2003      6       last

run(m)
@test ismissing(m[:MyComp2, :y][4])
@test ismissing(m[:MyComp2, :y][5])
@test m[:MyComp2, :y][1:3] == [2., 3., 4.]

# 5. Test all the warning and error cases

@defcomp MyComp3 begin
    regions=Index()
    x=Parameter(index=[time])       # One timestep array parameter
    y=Parameter(index=[regions])    # One non-timestep array parameter
    z=Parameter()                   # One scalar parameter
end

m = Model()                             # Build the model
set_dimension!(m, :time, 2000:2002)     # Set the time dimension
set_dimension!(m, :regions, [:A, :B])
add_comp!(m, MyComp3)
update_param!(m, :MyComp3, :x, [1, 2, 3])
update_param!(m, :MyComp3, :y, [10, 20])
update_param!(m, :MyComp3, :z, 0)

@test_throws ErrorException update_param!(m, :x, [1, 2, 3, 4]) # Will throw an error because size
update_param!(m, :MyComp3, :y, [10, 15])
@test model_param(m, :MyComp3, :y).values == [10., 15.]
update_param!(m, :MyComp3, :z, 1)
@test model_param(m, :MyComp3, :z).value == 1

# Reset the time dimensions
set_dimension!(m, :time, 1999:2001)

update_params!(m, Dict((:MyComp3, :x) =>[3,4,5], (:MyComp3, :y) =>[10,20], (:MyComp3, :z) =>0)) # Won't error when updating from a dictionary

@test model_param(m, :MyComp3, :x).values isa Mimi.TimestepArray{Mimi.FixedTimestep{1999,1, 2001},Union{Missing,Float64},1}
@test model_param(m, :MyComp3, :x).values.data == [3.,4.,5.]
@test model_param(m, :MyComp3, :y).values == [10.,20.]
@test model_param(m, :MyComp3, :z).value == 0

#------------------------------------------------------------------------------
# Test the three different set_param! methods for a Symbol type parameter
#------------------------------------------------------------------------------

@defcomp A begin
    p1 = Parameter{Symbol}()
end

function _get_model()
    m = Model()
    set_dimension!(m, :time, 10)
    add_comp!(m, A)
    return m
end

# Test the 3-argument version of set_param!
m = _get_model()
@test_throws MethodError set_param!(m, :p1, 3)  # Can't set it with an Int

set_param!(m, :p1, :foo)    # Set it with a Symbol
run(m)
@test m[:A, :p1] == :foo

# Test the 4-argument version of set_param!
m = _get_model()
@test_throws MethodError set_param!(m, :A, :p1, 3)

set_param!(m, :A, :p1, :foo)
run(m)
@test m[:A, :p1] == :foo

# Test the 5-argument version of set_param!
m = _get_model()
@test_throws MethodError set_param!(m, :A, :p1, :A_p1, 3)

set_param!(m, :A, :p1, :A_p1, :foo)
run(m)
@test m[:A, :p1] == :foo

#------------------------------------------------------------------------------
# Test a few different update_param! methods for a Symbol type parameter
#------------------------------------------------------------------------------

@defcomp A begin
    p1 = Parameter{Symbol}()
end

function _get_model()
    m = Model()
    set_dimension!(m, :time, 10)
    add_comp!(m, A)
    return m
end

# Test the 3-argument version of update_param!
m = _get_model()

add_shared_param!(m, :p1_fail, 3)
@test_throws ErrorException connect_param!(m, :A, :p1, :p1_fail)  # Can't connect it to an Int

add_shared_param!(m, :p1, :foo)
connect_param!(m, :A, :p1, :p1) # connect it to a Symbol

run(m)
@test m[:A, :p1] == :foo

# Test the 4-argument version of update_param!
m = _get_model()
@test_throws MethodError update_param!(m, :A, :p1, 3) # wrong type
@test_throws MethodError update_param!(m, :A, :p1, [1,2,3]) # wrong type

update_param!(m, :A, :p1, :foo)
run(m)
@test m[:A, :p1] == :foo

#------------------------------------------------------------------------------
# Test that if set_param! errors in the connection step,
#       the created param doesn't remain in the model's list of params
#------------------------------------------------------------------------------

@defcomp A begin
    p1 = Parameter(index = [time])
end

@defcomp B begin
    p1 = Parameter(index = [time])
end

m = Model()
set_dimension!(m, :time, 10)
add_comp!(m, A)
add_comp!(m, B)

@test_throws ErrorException set_param!(m, :p1, 1:5)     # this will error because the provided data is the wrong size
@test !(:p1 in keys(model_params(m)))                     # But it should not be added to the model's dictionary

end #module


text:
"""
    _get_titles_from_PDF(target::String) -> Vector{String}

Extract all titles from all PDFs found recursively in the passed directory.

# Arguments
- `dir::String`: $(DIR_ARGUMENT)

# Returns
- `Vector{String}`: the titles

# Throws
- Exceptions from: [`get_authors_titles`](@ref)

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

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

PDFHighlights.Internal.PDF._get_titles_from_PDF(path_to_pdf_dir) ==
["A dummy PDF for tests"]

# output

true
```
"""
function _get_titles_from_PDF(dir::String)::Vector{String}
    _, titles = get_authors_titles(dir)
    return titles
end


text:
struct CountingObservationModel{S1, S2, TF} <: ObservationModel{Vector{S1}, Vector{S2}, ContinuousTime}
    n::Int
    m::Int
    observation_function::TF
end

#####################
### BASIC METHODS ###
#####################

drift(model::CountingObservationModel) = model.observation_function
state_dim(model::CountingObservationModel) = model.n
obs_dim(model::CountingObservationModel) = model.m
noise_dim(model::CountingObservationModel) = model.m

observation_function(obs_model::CountingObservationModel) = drift(obs_model)

function Base.show(io::IO, ::MIME"text/plain", model::CountingObservationModel)
    print(io, "Counting process model for the observation
    type of hidden state:                    ", state_dim(model),"-dimensional vector
    type of observation:                     ", obs_dim(model),"-dimensional vector
    number of independent Poisson processes: ", noise_dim(model))
end

function Base.show(io::IO, model::CountingObservationModel)
    print(io, obs_dim(model),"-dimensional counting process")
end

function Base.show(io::IO, ::MIME"text/plain", model::Type{CountingObservationModel{S,T}}) where {S,T}
    print(io, "CountingObservationModel{", S, ",", T, "}")
end

function Base.show(io::IO, model::Type{CountingObservationModel{S,T}}) where {S,T}
    print(io, "CountingObservationModel")
end

######################
### TIME EVOLUTION ###
######################

function (model::CountingObservationModel{S1, S2, TF})(x::AbstractVector{S1}, dt) where {S1, S2, TF}
    return [rand(Poisson(r * dt)) for r in observation_function(model)(x)]
end

function (model::CountingObservationModel)(x::AbstractMatrix{T}, dt) where T
    h  = observation_function(model)
    return [rand(Poisson(r * dt)) for r in mapslices(h, x, dims=1)]
end

function (model::CountingObservationModel{S1, S2, TF})(x::AbstractVector{Vector{S1}}, dt) where {S1, S2, TF}
    return [model(y, dt) for y in x]
end

max_stars_repo_name: simsurace/FeedbackParticleFilters

max_issues_repo_name: simsurace/FeedbackParticleFilters

max_forks_repo_name: simsurace/FeedbackParticleFilters