text:
#=
  Euler #1 in Julia.

  Problem 1
  """
  If we list all the natural numbers below 10 that are multiples of 3 or 5,
  we get 3, 5, 6 and 9. The sum of these multiples is 23.
  Find the sum of all the multiples of 3 or 5 below 1000.
  """

  This Julia program was created by Hakan Kjellerstrand, hakank@gmail.com
  See also my Julia page: http://www.hakank.org/julia/

=#

include("Euler.jl")

# 0.00001165s
function euler1a()
  sum([i*(i%3==0||i%5==0) for i=1:999])
end

# 0.00001533s
function euler1b()
  sum([i for i in 1:999 if i%3==0||i%5==0])
end

# 0.00000276s
function euler1c()
   s = 0
   for i in 1:999
       if i % 3 == 0 || i % 5 == 0
           s+=i
       end
   end
   s
end

# 0.00001128s
function euler1d()
    filter(x-> x % 3 == 0 || x % 5 == 0,1:999)|>sum
end

# 0.00000898s
function euler1e()
    (1:999).|>(x-> x*(x % 3 == 0 || x % 5 == 0))|>sum
end

# 0.00001087s
function euler1f()
    (1:999)|>x->filter(i->i%3==0||i%5==0,x)|>sum
end

# 0.00003620s
function euler1g()
    x=1:999;(x.*(@. (x%3==0)|(x%5==0)))|>unique|>sum
end

# run_euler(euler1a)
# run_euler(euler1b)
run_euler(euler1c)
# run_euler(euler1d)
# run_euler(euler1e)
# run_euler(euler1f)
# run_euler(euler1g)

max_stars_repo_name: Wikunia/hakank

max_issues_repo_name: Wikunia/hakank

max_forks_repo_name: Wikunia/hakank


text:
using Test
using Spectra

using Random
using Unitful, UnitfulAstro, Measurements

Random.seed!(8675309)

include("spectrum.jl")
include("utils.jl")
include("transforms/transforms.jl")
include("plotting.jl")

ext: jl


text:
# out-of-place initialization
init(𝒜::GLGM06, 𝑆::AbstractSystem, 𝑂::Options) = init!(𝒜, 𝑆, copy(𝑂))

function options_GLGM06()

    𝑂spec = Vector{OptionSpec}()

    # step size
    push!(𝑂spec, OptionSpec(:δ, 1e-2, domain=Float64, aliases=[:sampling_time],
                            domain_check=(v  ->  v > 0.), info="time step"))

    # discretization
    push!(𝑂spec, OptionSpec(:discretization, "forward", domain=String,
                            info="model for bloating/continuous time analysis"))

    push!(𝑂spec, OptionSpec(:sih_method, "concrete", domain=String,
                            info="method to compute the symmetric interval hull in discretization"))

    push!(𝑂spec, OptionSpec(:exp_method, "base", domain=String,
                            info="method to compute the matrix exponential"))

    # approximation options
    push!(𝑂spec, OptionSpec(:max_order, 10, domain=Int,
                            info="maximum allowed order of zonotopes"))

    return 𝑂spec
end

# in-place initialization
function init!(::GLGM06, 𝑆::AbstractSystem, 𝑂::Options)

    # state dimension
    𝑂[:n] = statedim(𝑆)

    # adds default values for unspecified options
    𝑂init = validate_solver_options_and_add_default_values!(𝑂)

    return 𝑂init
end


text:
@testset "FiniteDiff comparison" begin
    q = rand(QuatRotation).q
    @test norm(Dojo.dmrpdq(Dojo.vector(q)) -
        FiniteDiff.finite_difference_jacobian(Dojo.mrp, Dojo.vector(q)), Inf) < 1.0e-5
    @test norm(Dojo.daxisdq(Dojo.vector(q)) -
        FiniteDiff.finite_difference_jacobian(Dojo.axis, Dojo.vector(q))) < 1.0e-5
    @test norm(Dojo.drotation_vectordq(Dojo.vector(q)) -
        FiniteDiff.finite_difference_jacobian(Dojo.rotation_vector, Dojo.vector(q))) < 1.0e-5

    q = one(Quaternion{Float64})
    @test norm(Dojo.dmrpdq(Dojo.vector(q)) -
        FiniteDiff.finite_difference_jacobian(Dojo.mrp, Dojo.vector(q)), Inf) < 1.0e-5
    @test norm(Dojo.drotation_vectordq(Dojo.vector(q)) -
        FiniteDiff.finite_difference_jacobian(Dojo.rotation_vector, Dojo.vector(q))) < 1.0e-5
end


text:
using DistStat, Random, LinearAlgebra

mutable struct MDSUpdate
    maxiter::Int
    step::Int
    verbose::Bool
    tol::Real
    function MDSUpdate(; maxiter::Int=100, step::Int=10, verbose::Bool=false, tol::Real=1e-10)
        maxiter > 0 || throw(ArgumentError("maxiter must be greater than 0."))
        tol > 0 || throw(ArgumentError("tol must be positive."))
        new(maxiter, step, verbose, tol)
    end
end

mutable struct MDSVariables{T, A}
    # MDS variables corresponding to distance matrix of  n x [n], X is external. m: original dimension, n: number of subjects
    # m::Int # rows, original dimension
    n::Int # cols, number of subjects
    r::Int # dimension of new space
    X::MPIMatrix{T,A} # n x [n], weighted distance
    θ::MPIMatrix{T,A} # r x [n]
    θ_prev::MPIMatrix{T,A} # r x [n]
    W::Union{T,MPIMatrix{T,A}} # weights, a singleton or an n x [n] MPIMatrix (a singleton is used for large-scale experiments)
    W_sums::Union{T,A} # sum of weights (by sample), a singleton or an MPIMatrix of [n] x 1.
    tmp_rn::MPIMatrix{T,A}
    tmp_rn_local::A
    tmp_nn::MPIMatrix{T,A}
    tmp_1n::MPIMatrix{T,A}
    tmp_n_local::A
    eval_obj::Bool
    function MDSVariables(Y::MPIMatrix{T,A}, r::Int, W=one(T); eval_obj=false, seed=nothing) where {T,A}
        n    = size(Y, 1)

        X = MPIMatrix{T,A}(undef, n, n)
        X .= W .* Y

        θ      = MPIMatrix{T,A}(undef, r, n)
        θ_prev = MPIMatrix{T,A}(undef, r, n)
        rand!(θ; seed=seed, common_init=true)
        θ .= 2θ .- 1
        if typeof(W) <: MPIMatrix
            fill_diag!(zero(T), W)
            W_sums = sum(weights; dims=2) # length-n vector
        elseif typeof(W) == T
            W_sums = convert(T, W * (n - 1))
        else
            error("type of W mismatch")
        end

        tmp_rn = MPIMatrix{T,A}(undef, r, n)
        tmp_rn_local = A{T}(undef, r, n)
        tmp_nn = MPIMatrix{T,A}(undef, n, n)
        tmp_1n = MPIMatrix{T,A}(undef, 1, n)
        tmp_n_local = A{T}(undef, n)
        new{T,A}(n, r, X, θ, θ_prev, W, W_sums, tmp_rn, tmp_rn_local,
            tmp_nn, tmp_1n, tmp_n_local, eval_obj)
    end
end

function reset!(v::MDSVariables{T,A}; seed=nothing) where {T,A}
    rand!(v.θ; seed=seed, common_init=true)
    v.θ .= 2v.θ .- 1
end

function update!(X::MPIArray, u::MDSUpdate, v::MDSVariables{T,A}) where {T,A}
    d = mul!(v.tmp_nn, transpose(v.θ), v.θ; tmp=v.tmp_rn_local)
    diag!(v.tmp_1n, d) # dist. row vector
    diag!(v.tmp_n_local, d) # local col vector

    d .= -2d .+ v.tmp_1n .+ v.tmp_n_local

    fill_diag!(d, Inf)

    v.tmp_nn .= v.X ./ d # Z
    v.tmp_1n .= sum(v.tmp_nn; dims=1) # Z sums, length-n dist. vector
    v.tmp_nn .= v.W .- v.tmp_nn # W - Z

    if typeof(v.W) == T
        fill_diag!(v.tmp_nn, zero(T))
    end

    θWmZ = mul!(v.tmp_rn, v.θ, (v.tmp_nn); tmp=v.tmp_rn_local)
    v.θ .= (v.θ .* (v.tmp_1n .+ v.W_sums) .+ θWmZ) ./ 2v.W_sums
end

function get_objective!(Y::MPIArray, u::MDSUpdate, v::MDSVariables{T,A}) where {T,A}
    if v.eval_obj
        DistStat.euclidean_distance!(v.tmp_nn, v.θ)
        return false, sum((Y .- v.tmp_nn).^2 .* v.W) / convert(T, 2.0)
    else
        v.tmp_rn .= abs.(v.θ_prev .- v.θ)
        return false, maximum(v.tmp_rn)
    end
end

function mds_one_iter!(Y::MPIArray, u::MDSUpdate, v::MDSVariables)
    copyto!(v.θ_prev, v.θ)
    update!(Y, u, v)
end

function loop!(Y::MPIArray, u, iterfun, evalfun, args...)
    converged = false
    t = 0
    while !converged && t < u.maxiter
        t += 1
        iterfun(Y, u, args...)
        if t % u.step == 0
            converged, monitor = evalfun(Y, u, args...)
            if DistStat.Rank() == 0
                println(t, ' ', monitor)
            end
        end
    end
end

function mds!(Y::MPIArray, u::MDSUpdate, v::MDSVariables)
    loop!(Y, u, mds_one_iter!, get_objective!, v)
end

include("cmdline.jl")
opts = parse_commandline_mds()
if DistStat.Rank() == 0
    println("world size: ", DistStat.Size())
    println(opts)
end

m = opts["rows"]
n = opts["cols"]
r = opts["r"]
iter = opts["iter"]
interval = opts["step"]
T = Float64
A = Array
if opts["gpu"]
    using CUDA
    A = CuArray
end
if opts["Float32"]
    T = Float32
end
init_opt = opts["init_from_master"]
seed = opts["seed"]
eval_obj = opts["eval_obj"]

X = MPIMatrix{T, A}(undef, m, n)
rand!(X; common_init=init_opt, seed=0)
Y = MPIMatrix{T,A}(undef, n,n)
DistStat.euclidean_distance!(Y, X)
uquick = MDSUpdate(;maxiter=2, step=1, verbose=true)
u = MDSUpdate(;maxiter=iter, step=interval, verbose=true)
v = MDSVariables(Y, r; eval_obj=eval_obj, seed=seed)
mds!(Y, uquick, v)
reset!(v; seed=seed)
if DistStat.Rank() == 0
    @time mds!(Y, u, v)
else
    mds!(Y, u, v)
end


text:
# ---
# title: 208. Implement Trie (Prefix Tree)
# id: problem208
# author: zhwang
# date: 2022-03-17
# difficulty: Medium
# categories: Design, Trie
# link: <https://leetcode.com/problems/implement-trie-prefix-tree/description/>
# hidden: true
# ---
#
# Implement a trie with `insert`, `search`, and `startsWith` methods.
#
# **Example:**
#
#
#
#     Trie trie = new Trie();
#
#     trie.insert("apple");
#     trie.search("apple");   // returns true
#     trie.search("app");     // returns false
#     trie.startsWith("app"); // returns true
#     trie.insert("app");
#     trie.search("app");     // returns true
#
#
# **Note:**
#
#   * You may assume that all inputs are consist of lowercase letters `a-z`.
#   * All inputs are guaranteed to be non-empty strings.
#
#
## @lc code=start
using LeetCode

Base.@kwdef mutable struct PrefixNode
    isend::Bool = false
    children = Dict{Char,PrefixNode}()
end

function insert_node!(node::PrefixNode, word::String)::Nothing
    for c in word
        children = node.children
        haskey(children, c) || (children[c] = PrefixNode())
        node = children[c]
    end
    node.isend = true
    return nothing
end

function search_prefix_node(node::PrefixNode, prefix::String)::Union{Nothing,PrefixNode}
    for c in prefix
        children = node.children
        haskey(children, c) || return nothing
        node = children[c]
    end
    return node
end

function search_word(node::PrefixNode, word::String)::Bool
    node = search_prefix_node(node, word)
    return !isnothing(node) && node.isend
end

starts_with(node::PrefixNode, prefix::String) = !isnothing(search_prefix_node(node, prefix))

## @lc code=end


text:
function rosenbrock(x, g=nothing)
    n = length(x)
    f = 0.0
    for i=1:(n-1)
        f += (1-x[i])^2 + 100*(x[i+1]-x[i]^2)^2
    end
    if g != nothing
        for i=1:n
            g[i] = ((i==n ? 0 : -2*(1-x[i]) - 400*(x[i+1]-x[i]^2)*x[i]) +
                    (i==1 ? 0 : 200*(x[i]-x[i-1]^2)))
        end
    end
    return f
end

function gtest(x=randn(10);eps=1e-5)
    g = similar(x)
    @show f0 = rosenbrock(x,g)
    for i=1:length(x)
        xi = x[i]
        x[i] = xi+eps; f1 = rosenbrock(x)
        x[i] = xi-eps; f2 = rosenbrock(x)
        x[i] = xi; df = (f1-f2)/(2*eps)
        @show (i,g[i],df)
    end
end

using Base.LinAlg

function eq10(f, x0; gtol=0.95, gamma=1e-8)
    g0, x1, rb, rr, r = [ zeros(x0) for i=1:5 ]
    f0 = f(x0, g0)
    n0 = vecnorm(g0)
    nf = cosa = 0
    while cosa < gtol
        f1 = f(axpy!(gamma, randn!(r), copy!(x1,x0)))
        nf += 1
        b = f1 - f0
        axpy!(b, r, rb)
        axpy!(1, r.*r, rr)
        g1 = rb ./ (gamma * rr)
        n1 = vecnorm(g1)
        cosa = dot(g0,g1)/(n0*n1)
    end
    return nf
end


text:
module Solve

export solve, load_tasks
using Crayons
using LinearAlgebra
import JSON
using ..Render
using ..Grids

function task_of_json(json, path)
    ARCTask(
        ios_of_json(json["train"]),
        ios_of_json(json["test"]),
        path
    )
end

function ios_of_json(json::Array)
    io_of_json.(json)
end

function io_of_json(json)
    (i=grid_of_json(json["input"]), o=grid_of_json(json["output"]))
end

function grid_of_json(json)
    # transpose to match how the official challenge displays images
    transpose(Int8.(hcat(json...)))
end

function load_tasks(mode=:train; n=0)
    @assert mode in [:train, :test]
    path = Dict(:train => "data/training", :test => "data/evaluation")[mode]
    train_files = readdir(path, join=true)
    if n > 0
        train_files = train_files[1:n]
    end
    print(crayon"yellow", "loading $mode tasks...", crayon"reset")
    tasks = task_of_json.(JSON.parsefile.(train_files), train_files)
    println(crayon"yellow", "loaded $(length(tasks)) tasks", crayon"reset")
    tasks
end

function solve()

end

end


text:
# not exported, used mainly for testing

_copy_convert{T}(::Type{T}, x::Vector{T}) = copy(x)
_copy_convert{R,T}(::Type{R}, x::AbstractVector{T}) = convert(Vector{R}, x)

import Base: Func, AddFun, MulFun, MaxFun, MinFun

if isdefined(Base, :call)
    import Base: call
else
    call(f::Function, x) = f(x)
    call(f::Function, x, y) = f(x, y)
    call(f::Func{1}, x) = Base.evaluate(f, x)
    call(f::Func{2}, x, y) = Base.evaluate(f, x, y)
end

if isdefined(Base, :SubFun)
    import Base: SubFun
else
    immutable SubFun <: Func{2} end
    call(::SubFun, x, y) = x - y
end

immutable RealFun <: Func{1} end
call(::RealFun, x) = real(x)

immutable ImagFun <: Func{1} end
call(::ImagFun, x) = imag(x)

immutable ComplexFun <: Func{2} end
call(::ComplexFun, x::Real, y::Real) = complex(x, y)

immutable DotFun <: Func{2} end
_dot(x::Number, y::Number) = conj(x) * y
_dot(x::Real, y::Real) = x * y
call(::DotFun, x::Number, y::Number) = _dot(x, y)

@compat typealias UnaryOp Union{Function, Func{1}}
@compat typealias BinaryOp Union{Function, Func{2}}


text:
using Distributions
using Plots

grid_size = 40
proba_grid = range(0, 1, length=grid_size)

## Code block 2.5
# prior = ones((grid_size))
# prior = collect(proba_grid) .>= 0.5
prior = exp.(-5 * abs.(collect(proba_grid) .- 0.5))

plot(
    proba_grid, prior, title="40 Points", xlabel="Probability of water",
    ylabel="Prior probability", lw=3
)

likelihood = [pdf(Binomial(9, p), 6) for p in proba_grid]
posterior = prior .* likelihood
posterior_proba = posterior / sum(posterior)

## Code block 2.4
plot(
    proba_grid, posterior_proba, title="40 Points", xlabel="Probability of water",
    ylabel="Posterior probability", lw=3
)


text:
if VERSION >= v"1.3" && !haskey(ENV, "JULIA_CLP_LIBRARY_PATH")
    exit()  # Use Clp_jll instead.
end

using BinaryProvider

# Parse some basic command-line arguments
const verbose = "--verbose" in ARGS
const prefix = Prefix(get([a for a in ARGS if a != "--verbose"], 1, joinpath(@__DIR__, "usr")))
products = [
    LibraryProduct(prefix, ["libClp"], :libClp),
    LibraryProduct(prefix, ["libOsiClp"], :libOsiClp),
    LibraryProduct(prefix, ["libClpSolver"], :libClpSolver),
]

# Download binaries from hosted location
bin_prefix = "https://github.com/JuliaBinaryWrappers/Clp_jll.jl/releases/download/Clp-v1.17.6+4"

# Listing of files generated by BinaryBuilder:
download_info = Dict(
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-gnu-libgfortran3-cxx03.tar.gz", "059439acc52693be3f02fa5af4a64178d29b8682df25371884bc19be19b9e786"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-gnu-libgfortran3-cxx11.tar.gz", "39d1b7528b328ddb3ab59ba28da8e0eccc9c9ae5af8ea07c45cf137b16705cef"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-gnu-libgfortran4-cxx03.tar.gz", "a98d527a2d0a29e8f2ea4158ab3ed1ffc825286427328cad1a7711ec441cc92d"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-gnu-libgfortran4-cxx11.tar.gz", "f435877ef8863296760285027a79e8d061836d21389d36f3713a1fa512793757"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-gnu-libgfortran5-cxx03.tar.gz", "ae7515a6849cff6818e5c0819ae8320535c5fcc797aca75ede4dd0486c7117dc"),
    Linux(:aarch64, libc=:glibc, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-gnu-libgfortran5-cxx11.tar.gz", "3b46bacb16a3f561fbf47ce7079951b8a1cb003b750de72199568a168a5dfbf5"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-musl-libgfortran3-cxx03.tar.gz", "04f7518752e7195d7d725e19ee47e9d0184ae9273880424e60ff385bc6b94599"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-musl-libgfortran3-cxx11.tar.gz", "1583ea3f2567c3d88b5680b98901f75e0ce61af957c0c78c033fbc641649af88"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-musl-libgfortran4-cxx03.tar.gz", "62895d58c94272cc5060d0837ab78fc31f16e87629d5f7e95569c810af2d780a"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-musl-libgfortran4-cxx11.tar.gz", "39b4de5459d304e1db21817d3d90c85edb20630d5c6aa7c05b2a0b1fb4c4540d"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-musl-libgfortran5-cxx03.tar.gz", "cc4232538b8faf5a045ea397ed2e28951b6c1b7e74e030c0c9d79c20763ed560"),
    Linux(:aarch64, libc=:musl, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.aarch64-linux-musl-libgfortran5-cxx11.tar.gz", "b11bed9fb7f537b88040d68c4ce49fb5c83195e56ad6d466def251f992a292f3"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-gnueabihf-libgfortran3-cxx03.tar.gz", "30969dc5bac04677737167372e205586b154fe38e458ccba051f2ba4333e05b8"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-gnueabihf-libgfortran3-cxx11.tar.gz", "e28cc87c9c6b4c1b5a1161654505ad05c9048bf6e50699a477f56aa202cb1bab"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-gnueabihf-libgfortran4-cxx03.tar.gz", "d33c1e4c12af9ccec3e636f3b29d7538a7eae71e0d71cedffbec51e552165f20"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-gnueabihf-libgfortran4-cxx11.tar.gz", "7370687426d753d516e9855151e42b5a03c741e1bab7086ca63b033ee7abc16d"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-gnueabihf-libgfortran5-cxx03.tar.gz", "fa76a2342cae9d0b795d89b11f0475f7cce8726c26b3d95e86567ae711e803ee"),
    Linux(:armv7l, libc=:glibc, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-gnueabihf-libgfortran5-cxx11.tar.gz", "e491590dbfb93578df18727d75b232c6d3fd183129e227ecb9d5b95b1487277c"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-musleabihf-libgfortran3-cxx03.tar.gz", "3676265097ab9c2e7f4acc66d72d0adc6c5035fb5c5c74245553e0f971934465"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-musleabihf-libgfortran3-cxx11.tar.gz", "673a83dd7c4416552fa735cc74253d63d47b0bf553818a3ad4af606ba5ee827d"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-musleabihf-libgfortran4-cxx03.tar.gz", "08acd28c93cef77b92c8624e716b98f4fb1fde49a5a9a1948eb63e11426079c9"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-musleabihf-libgfortran4-cxx11.tar.gz", "51fd91bb457b4383892f8b54cf70c1a337d542ae93853f51c2ec6debcd42bb43"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-musleabihf-libgfortran5-cxx03.tar.gz", "71c72009b18c5585dbcb4c77b54e1f64e10cd87d07924bd4e4d46d20ce3f1f94"),
    Linux(:armv7l, libc=:musl, call_abi=:eabihf, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.armv7l-linux-musleabihf-libgfortran5-cxx11.tar.gz", "e88098e676b92a4feaee97cfb25ddc949c7c97f137420d52055089345363c4ba"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-gnu-libgfortran3-cxx03.tar.gz", "4bf40f8a12b821d6edc86a7dc55431c1ee73ec56d2875ef41c7b974bf6b85c6d"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-gnu-libgfortran3-cxx11.tar.gz", "809012f81214a22e5e6a6fd00b7f4eb3699a6965c7e87abe9fc0df47cf367e7d"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-gnu-libgfortran4-cxx03.tar.gz", "b0d35e6b29ed5c15bf62b6f2bd07c5911eb998f434919611253b8a8eac5dd4c2"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-gnu-libgfortran4-cxx11.tar.gz", "ec8324c8b230ed7befd57346f2285932906d927e874b403b7d87c1bd32fef9ea"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-gnu-libgfortran5-cxx03.tar.gz", "73f6bde94a838d595a70f5fb1d06485afe0adfcd248c119c98dbb64129af8fdb"),
    Linux(:i686, libc=:glibc, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-gnu-libgfortran5-cxx11.tar.gz", "5dad9d1fab13faa83ebdd7bb7d690952a09ddb46cee428185a7abe8566ea3fad"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-musl-libgfortran3-cxx03.tar.gz", "ef21bfebbbeca91f0c3feac91b47ea087a5e229c0867217137ad3248e804004a"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-musl-libgfortran3-cxx11.tar.gz", "24e55b07918952a4320c08b2a2fff96625984438ebadcb6a6515a845fff1c5bb"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-musl-libgfortran4-cxx03.tar.gz", "6c1625eb19d2dd5f9e63c702a611520a11220d5e5448cf2e9c4d1af39596dc0e"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-musl-libgfortran4-cxx11.tar.gz", "29141af307f06d55c4646cad74d64644edc7185768a3d33cae647464bc04cf2f"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-musl-libgfortran5-cxx03.tar.gz", "f4c33d89f50a7287675165122e9c69289e2599cf78bcbe3cb041817ab2c50884"),
    Linux(:i686, libc=:musl, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-linux-musl-libgfortran5-cxx11.tar.gz", "271d40ca1f82e79a69f18526acb4eec12c18a12a1f6cde0b573f254e73c67f73"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-w64-mingw32-libgfortran3-cxx03.tar.gz", "9a18a4440afc4bfe501a5a14ae13a3d6a99d5c0c3974aec887edd172d1e6382d"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-w64-mingw32-libgfortran3-cxx11.tar.gz", "0129cfdb0018918a4aed88d5e4b7512fafc2ba7e837fb01ac7c1882611927ecc"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-w64-mingw32-libgfortran4-cxx03.tar.gz", "42a0314dc96d0f22c9d18c549a698c56b3b5caabf1c88390a12b2552c38c63b3"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-w64-mingw32-libgfortran4-cxx11.tar.gz", "450d21545f3b860ed05f636d122d9751992d94466c352ea4f86a86454db58cbc"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.i686-w64-mingw32-libgfortran5-cxx03.tar.gz", "e5151713849c9b274442dfd5585cfe38ead5706f764516f373c4b69aaf992a9a"),
    Windows(:i686, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.i686-w64-mingw32-libgfortran5-cxx11.tar.gz", "ce6663cfb8cf471f119c7b01ca311656f09c710c5850795dc5b72d67422944c6"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-apple-darwin14-libgfortran3-cxx03.tar.gz", "96a580aa70f931196779d45722ab5aca18ee07b7c4944952550b600d253b0b4d"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-apple-darwin14-libgfortran3-cxx11.tar.gz", "9a5c31a93e87b804e2712a90ec15a4913fb126235445fa09b8d6047b74c3f364"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-apple-darwin14-libgfortran4-cxx03.tar.gz", "f9ca0649a69b0b4921db84a83dbc56bcd161842bac81eb6f31ce15e919073347"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-apple-darwin14-libgfortran4-cxx11.tar.gz", "53bcfb7ba0c9a5a5f45cbc033d09d10bf542559f2501167ad224a6aaf9b8c5c0"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-apple-darwin14-libgfortran5-cxx03.tar.gz", "4e5761e0edcddef0f32690451b0ee047e3f53095f7d12a5951fae29549b1926d"),
    MacOS(:x86_64, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-apple-darwin14-libgfortran5-cxx11.tar.gz", "5f16402c0f0451aa02aae7b131b6043819f29059d5be13c7021c4716b9e64fb5"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-gnu-libgfortran3-cxx03.tar.gz", "9985690f528d2b22e31184a725e643b808fb5d3a275d320b726a3baf70105eac"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-gnu-libgfortran3-cxx11.tar.gz", "991531cce8c7f62289f36aab551cc3318fa51867e3d5250a5b727c2a943be3cb"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-gnu-libgfortran4-cxx03.tar.gz", "1b158ee674aa6c2545eeed2dc301dc9a44efd1a3bc3a14d6be8886ff8ad1429f"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-gnu-libgfortran4-cxx11.tar.gz", "b47ff51552a4701f8cbbf29c8fea92a32a1ea279e9bb55973061741619d1c20e"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-gnu-libgfortran5-cxx03.tar.gz", "85e40e2e121f8d0c71a4cdc625b29348ff32457845c06ca8ccad9e3cf34569cf"),
    Linux(:x86_64, libc=:glibc, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-gnu-libgfortran5-cxx11.tar.gz", "b3c7abe76b820bac309f89c437e422117e37cc75b2c6d518b796e03892238318"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-musl-libgfortran3-cxx03.tar.gz", "73acc89560dc5664444bd8f38f6173ae32f320d4893c6092503648a013075f0a"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-musl-libgfortran3-cxx11.tar.gz", "92c66b1014a4fa0f42e1e11568f9ac0ea592b8406f8df163ffbed17fd2ecf8f8"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-musl-libgfortran4-cxx03.tar.gz", "14334725b48f7c13cdde4f7c3a3adfa38b15b561e1ad9e6f1667729582b26af2"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-musl-libgfortran4-cxx11.tar.gz", "33fa27d4f91cda9b6913ff3d3eb591186c45249433b280f5bc523e0a98664be5"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-musl-libgfortran5-cxx03.tar.gz", "a437d3e04cb8cc5f672290e51b670d6f20f39086acbe87f48c3d236bff39c96b"),
    Linux(:x86_64, libc=:musl, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-linux-musl-libgfortran5-cxx11.tar.gz", "68edc01cd657d9ff979ed2329f2a6459dd69c4bc363f0305ecebf0539f3ba016"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc4, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-w64-mingw32-libgfortran3-cxx03.tar.gz", "f8f5e919145542e67ec5a38bc84f7eacff91f4a427079540eb87d74b6109884f"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc4, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-w64-mingw32-libgfortran3-cxx11.tar.gz", "ad8a66ecb1e1a19f33dc35346dbb9b9833b03c10a2002a5b9234a886e8e33348"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc7, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-w64-mingw32-libgfortran4-cxx03.tar.gz", "44b8d6c8d08f98274364f7008af4375b1a3c56a212792c49cfa6b44b17abfb95"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc7, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-w64-mingw32-libgfortran4-cxx11.tar.gz", "9385fbf05223267b36a3b5607aaac5bb87c378a79a7bdf769909f9603049c752"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc8, :cxx03)) => ("$bin_prefix/Clp.v1.17.6.x86_64-w64-mingw32-libgfortran5-cxx03.tar.gz", "c5f936529b68613c72b6df816f305c98dbf4370dfcf82dc0c1ae3df2a0a40b12"),
    Windows(:x86_64, compiler_abi=CompilerABI(:gcc8, :cxx11)) => ("$bin_prefix/Clp.v1.17.6.x86_64-w64-mingw32-libgfortran5-cxx11.tar.gz", "d41cbab7481daf57a475dc445192085737e4768ff702402ba70c009e1bf1cd7a"),
)

# To fix gcc4 bug in Windows
# https://sourceforge.net/p/mingw-w64/bugs/727/
this_platform = platform_key_abi()
if typeof(this_platform) == Windows && this_platform.compiler_abi.gcc_version == :gcc4
   this_platform = Windows(
       arch(this_platform);
       libc = libc(this_platform),
       compiler_abi = CompilerABI(:gcc6)
    )
end

custom_library = false
if haskey(ENV, "JULIA_CLP_LIBRARY_PATH")
    custom_products = [
        LibraryProduct(
            ENV["JULIA_CLP_LIBRARY_PATH"],
            product.libnames,
            product.variable_name
        ) for product in products
    ]
    if all(satisfied(p; verbose = verbose) for p in custom_products)
        products = custom_products
        custom_library = true
    else
        error("Could not install custom libraries from $(ENV["JULIA_CLP_LIBRARY_PATH"]).\nTo fall back to BinaryProvider call delete!(ENV,\"JULIA_CLP_LIBRARY_PATH\") and run build again.")
    end
end

if !custom_library
    # Install unsatisfied or updated dependencies:
    # We added `, isolate=true` as otherwise, it would segfault when closing `OpenBLAS32`,
    # probably because it is conflicting with Julia openblas.
    unsatisfied = any(!satisfied(p; verbose=verbose, isolate=true) for p in products)

    dl_info = choose_download(download_info, this_platform)
    if dl_info === nothing && unsatisfied
        # If we don't have a compatible .tar.gz to download, complain.
        # Alternatively, you could attempt to install from a separate provider,
        # build from source or something even more ambitious here.
        error("Your platform (\"$(Sys.MACHINE)\", parsed as \"$(triplet(platform_key_abi()))\") is not supported by this package!")
    end

    # If we have a download, and we are unsatisfied (or the version we're
    # trying to install is not itself installed) then load it up!
    if unsatisfied || !isinstalled(dl_info...; prefix=prefix)
        # Download and install binaries
        evalfile("build_CompilerSupportLibraries.v0.3.3.jl")
        evalfile("build_OpenBLAS32.v0.3.9.jl")
        evalfile("build_CoinUtils.v2.11.3.jl")
        evalfile("build_Osi.v0.108.5.jl")
        install(dl_info...; prefix=prefix, force=true, verbose=verbose)
    end
 end

# Write out a deps.jl file that will contain mappings for our products
write_deps_file(joinpath(@__DIR__, "deps.jl"), products, verbose=verbose)


text:
# Test _arg_promote
@testset "_arg_promote" begin
    # setup the model
    m = InfiniteModel()
    @infinite_parameter(m, par in [0, 1])
    @infinite_variable(m, inf(par))
    @hold_variable(m, hold)
    # test GenericAffExpr
    @testset "AffExpr" begin
        aff = 2inf + 2
        @test isa(InfiniteOpt._arg_promote(GeneralVariableRef, aff), GenericAffExpr{Float64, GeneralVariableRef})
        @test InfiniteOpt._arg_promote(InfiniteVariableRef, aff) == aff
    end
    # test GenericQuadExpr
    @testset "QuadExpr" begin
        quad = inf^2 + 2inf
        @test isa(InfiniteOpt._arg_promote(GeneralVariableRef, quad), GenericQuadExpr{Float64, GeneralVariableRef})
        @test InfiniteOpt._arg_promote(InfiniteVariableRef, quad) == quad
    end
    # test Variable references
    @testset "Variables" begin
        @test InfiniteOpt._arg_promote(GeneralVariableRef, inf) == inf + 0
        @test InfiniteOpt._arg_promote(GeneralVariableRef, par) == par + 0
        @test InfiniteOpt._arg_promote(GeneralVariableRef, hold) == hold + 0
    end
end

# Test MutableArithmetics.mutable_operate!
@testset "MA.mutable_operate!" begin
    # setup the model
    m = InfiniteModel()
    @infinite_parameter(m, par in [0, 1])
    @infinite_variable(m, inf(par))
    @hold_variable(m, hold)
    aff1 = 2inf + 3
    aff2 = hold - par + 2
    quad1 = hold^2 + 4hold
    quad2 = 2par^2 - par - 2
    # 2 argument adding
    @testset "+" begin
        # mixed aff + var
        expected = copy(aff1) + hold
        @test MA.mutable_operate!(+, copy(aff1), hold) == expected
        # mixed aff + aff
        expected = copy(aff1) + copy(aff2)
        @test MA.mutable_operate!(+, copy(aff1), copy(aff2)) == expected
        # same aff + var
        expected = copy(aff1) + inf
        @test MA.mutable_operate!(+, copy(aff1), inf) == expected
        # mixed quad + var
        expected = copy(quad1) + par
        @test MA.mutable_operate!(+, copy(quad1), par) == expected
        # mixed quad + aff
        expected = copy(quad2) + copy(aff2)
        @test MA.mutable_operate!(+, copy(quad2), copy(aff2)) == expected
        # test quad + constant (not this function)
        expected = copy(quad2) + 2
        @test MA.mutable_operate!(+, copy(quad2), 2) == expected
    end

    # 2 argument substracting
    @testset "-" begin
        # mixed aff + var
        expected = copy(aff1) - hold
        @test MA.mutable_operate!(-, copy(aff1), hold) == expected
        # mixed aff + aff
        expected = copy(aff1) - copy(aff2)
        @test MA.mutable_operate!(-, copy(aff1), copy(aff2)) == expected
        # same aff + var
        expected = copy(aff1) - inf
        @test MA.mutable_operate!(-, copy(aff1), inf) == expected
        # mixed quad + var
        expected = copy(quad1) - par
        @test MA.mutable_operate!(-, copy(quad1), par) == expected
        # mixed quad + aff
        expected = copy(quad2) - copy(aff2)
        @test MA.mutable_operate!(-, copy(quad2), copy(aff2)) == expected
        # test quad + constant (not this function)
        expected = copy(quad2) - 2
        @test MA.mutable_operate!(-, copy(quad2), 2) == expected
    end

    # 2 argument adding/multiplying
    @testset "MA.add_mul" begin
        # mixed aff + var
        expected = copy(aff1) + hold
        @test MA.mutable_operate!(MA.add_mul, copy(aff1), hold) == expected
        # mixed aff + aff
        expected = copy(aff1) + copy(aff2)
        @test MA.mutable_operate!(MA.add_mul, copy(aff1), copy(aff2)) == expected
        # same aff + var
        expected = copy(aff1) + inf
        @test MA.mutable_operate!(MA.add_mul, copy(aff1), inf) == expected
        # mixed quad + var
        expected = copy(quad1) + par
        @test MA.mutable_operate!(MA.add_mul, copy(quad1), par) == expected
        # mixed quad + aff
        expected = copy(quad2) + copy(aff2)
        @test MA.mutable_operate!(MA.add_mul, copy(quad2), copy(aff2)) == expected
        # test quad + constant (not this function)
        expected = copy(quad2) + 2
        @test MA.mutable_operate!(MA.add_mul, copy(quad2), 2) == expected
    end

    # 2 argument substracting/multiplying
    @testset "MA.sub_mul" begin
        # mixed aff + var
        expected = copy(aff1) - hold
        @test MA.mutable_operate!(MA.sub_mul, copy(aff1), hold) == expected
        # mixed aff + aff
        expected = copy(aff1) - copy(aff2)
        @test MA.mutable_operate!(MA.sub_mul, copy(aff1), copy(aff2)) == expected
        # same aff + var
        expected = copy(aff1) - inf
        @test MA.mutable_operate!(MA.sub_mul, copy(aff1), inf) == expected
        # mixed quad + var
        expected = copy(quad1) - par
        @test MA.mutable_operate!(MA.sub_mul, copy(quad1), par) == expected
        # mixed quad + aff
        expected = copy(quad2) - copy(aff2)
        @test MA.mutable_operate!(MA.sub_mul, copy(quad2), copy(aff2)) == expected
        # test quad + constant (not this function)
        expected = copy(quad2) - 2
        @test MA.mutable_operate!(MA.sub_mul, copy(quad2), 2) == expected
    end

    # 3 argument adding/multiplying (no constants)
    @testset "MA.add_mul (3 args no constants)" begin
        # mixed quad + var * var
        expected = copy(quad2) + hold * par
        @test MA.mutable_operate!(MA.add_mul, copy(quad2), hold, par) == expected
        # same quad + var * var
        expected = copy(quad1) + hold * hold
        @test MA.mutable_operate!(MA.add_mul, copy(quad1), hold, hold) == expected
    end

    # 3 argument adding/multiplying (terminal constant)
    @testset "MA.add_mul (3 args last constant)" begin
        # mixed quad + var * const
        expected = copy(quad2) + hold * 2
        @test MA.mutable_operate!(MA.add_mul, copy(quad2), hold, 2) == expected
        # same quad + var * var
        expected = copy(quad1) + hold * 3
        @test MA.mutable_operate!(MA.add_mul, copy(quad1), hold, 3) == expected
    end

    # 3 argument adding/multiplying (middle constant)
    @testset "MA.add_mul (3 args middle constant)" begin
        # mixed quad + 2 * var
        expected = copy(quad2) + 2 * par
        @test MA.mutable_operate!(MA.add_mul, copy(quad2), 2, par) == expected
        # same quad + 42 * var
        expected = copy(quad1) + 42 * hold
        @test MA.mutable_operate!(MA.add_mul, copy(quad1), 42, hold) == expected
        # all constants (different function)
        expected = copy(quad1) + 42 * 2
        @test MA.mutable_operate!(MA.add_mul, copy(quad1), 42, 2) == expected
    end

    # 3 argument substracting/multiplying (no constants)
    @testset "MA.sub_mul (3 args no constants)" begin
        # mixed quad + var * var
        expected = copy(quad2) - hold * par
        @test MA.mutable_operate!(MA.sub_mul, copy(quad2), hold, par) == expected
        # same quad + var * var
        expected = copy(quad1) - hold * hold
        @test MA.mutable_operate!(MA.sub_mul, copy(quad1), hold, hold) == expected
    end

    # 3 argument substracting/multiplying (terminal constant)
    @testset "MA.sub_mul (3 args last constant)" begin
        # mixed quad + var * const
        expected = copy(quad2) - hold * 2
        @test MA.mutable_operate!(MA.sub_mul, copy(quad2), hold, 2) == expected
        # same quad + var * var
        expected = copy(quad1) - hold * 3
        @test MA.mutable_operate!(MA.sub_mul, copy(quad1), hold, 3) == expected
    end

    # 3 argument substracting/multiplying (middle constant)
    @testset "MA.sub_mul (3 args middle constant)" begin
        # mixed quad + 2 * var
        expected = copy(quad2) - 2 * par
        @test MA.mutable_operate!(MA.sub_mul, copy(quad2), 2, par) == expected
        # same quad + 42 * var
        expected = copy(quad1) - 42 * hold
        @test MA.mutable_operate!(MA.sub_mul, copy(quad1), 42, hold) == expected
        # all constants (different function)
        expected = copy(quad1) - 42 * 2
        @test MA.mutable_operate!(MA.sub_mul, copy(quad1), 42, 2) == expected
    end
end


text:
"""
# plot_model_coef

Multiple regression coefficient plot for multiple models.

$(SIGNATURES)

### Required arguments
```julia
* `models`                             : Vector of `SampleModel`s to compare
* `pars`                               : Vector of parameters to include in comparison
```
### Optional arguments
```julia
* `fig=""`                             : File to store plot
* `title=""`                           : Title for plot
```

### Return values
```julia
* `(s, f)`                             : (particles, plot)
```

"""
function plot_model_coef(models::Vector{SampleModel},
  pars::Vector{Symbol}; fig="", title="")

  mnames = [models[i].name for i in 1:length(models)]
  for i in 1:length(mnames)
    if length(mnames[i]) > 9
      mnames[i] = mnames[i][1:9]
    end
  end

  s = Vector{NamedTuple}(undef, length(models))
  for (mindx, mdl) in enumerate(models)
    df = read_samples(mdl, :dataframe)
    m, l, u = estimparam(df)
    d = Dict{Symbol, NamedTuple}()
    for (indx, par) in enumerate(names(df))
        d[Symbol(par)] = (mean=m[indx], lower=l[indx], upper=u[indx])
    end
    s[mindx] =   (; d...)
  end

  plot_model_coef(s, pars; mnames, fig, title)
end

"""
# plot_model_coef

Multiple regression coefficient plot for a single model.

$(SIGNATURES)

### Required arguments
```julia
* `model`                              : SampleModel to display
* `pars`                               : Vector of parameters to include in comparison
* `fig`                                : File to store the produced plot
```
### Optional arguments
```julia
* `title=""`                           : Title for plot
```

### Return values
```julia
* `(s, f)`                             : (particles, plot)
```

"""
function plot_model_coef(model::SampleModel, pars::Vector{Symbol};
  fig="", title="")
  plot_model_coef([model], pars; fig, title)
end

function plot_models(models::Vector{SampleModel}, type::Symbol;
  fig="", title = uppercase(String(type)))

  mnames = [models[i].name for i in 1:length(models)]
  for i in 1:length(mnames)
    if length(mnames[i]) > 9
      mnames[i] = mnames[i][1:9]
    end
  end

  df_waic = compare(models, type)
  plot_models(df_waic, type; fig, title)
end

export
  plot_model_coef,
  plot_models


text:
#=
  From BLOG example/aircraft-static.blog

  Distributions of variable numAircraft (num:0)
  3.00000 =>    3191  (0.319100)
  4.00000 =>    2608  (0.260800)
  2.00000 =>    1726  (0.172600)
  5.00000 =>    1385  (0.138500)
  6.00000 =>     657  (0.065700)
  1.00000 =>     221  (0.022100)
  7.00000 =>     192  (0.019200)
  8.00000 =>      13  (0.001300)
  9.00000 =>       7  (0.000700)

  location[1]
  Summary Stats:
  Length:         10000
  Missing Count:  0
  Mean:           149.209455
  Minimum:        100.031478
  1st Quartile:   123.426126
  Median:         149.309147
  3rd Quartile:   174.100740
  Maximum:        199.992805

  Also see ~/webppl/aircraft_static.wppl

=#

using Turing, StatsPlots, DataFrames
# using ReverseDiff, Zygote, Tracker
# Turing.setadbackend(:reversediff)
# Turing.setadbackend(:zygote)
# Turing.setadbackend(:tracker)

include("jl_utils.jl")

@model function aircraft_static()
    epsilon = 0.05

    # Poisson might get 0 which is not good for this model
    pp ~ Poisson(3.0)
    numAircraft ~ Dirac(pp == 0 ? 1 : pp)

    blipPerAircraft = tzeros(numAircraft)
    location = tzeros(numAircraft)
    blipLocation = tzeros(numAircraft)
    for b in 1:numAircraft
        blipPerAircraft[b] ~ Poisson(1)
        location[b] ~ Uniform(100.0,200.0)
        if blipPerAircraft[b] > 0
            blipLocation[b] ~ Normal(location[b],1.0)
        else
            blipLocation[b] ~ Uniform(90,210)
        end
    end

    sumBlips ~ Dirac(sum(blipPerAircraft))

    true ~ Dirac(sumBlips == 4)
    locationB0 ~ Dirac(location[1])
end

model = aircraft_static()

num_chns = 4
# chns = sample(model, Prior(), 1000)
# chns = sample(model, MH(), 100_000)
chns = sample(model, PG(5), 10_000)
# chns = sample(model, IS(), 10_000)
# chns = sample(model, SMC(), 40_000)
# chns = sample(model, SMC(), MCMCThreads(), 10_000, num_chns)

display(chns)
# display(plot(chns))

show_var_dist_pct(chns, :numAircraft)

println("\nlocation[1]")
display(summarystats(chns[Symbol("location[1]")]))

max_stars_repo_name: tias/hakank

max_issues_repo_name: tias/hakank

max_forks_repo_name: tias/hakank


text:
import Base:
    eltype, show, ==, zero, copy, issubset

import LightGraphs:
    edgetype, nv, ne, vertices, edges, is_directed,
    add_vertex!, add_edge!, rem_vertex!, rem_edge!,
    has_vertex, has_edge, inneighbors, outneighbors

import LightGraphs.SimpleGraphs:
    SimpleDiGraph,
    SimpleEdge, fadj, badj

function show(io::IO, g::StorageGraph)
    print(io, "{$(nv(g)), $(ne(g))} $(eltype(g)) storage graph")
end

@inline fadj(g::StorageGraph, x...) = fadj(g.graph, x...)
@inline badj(g::StorageGraph, x...) = badj(g.graph, x...)

eltype(g::StorageGraph) = eltype(g.graph)
edgetype(g::StorageGraph) = edgetype(g.graph)
nv(g::StorageGraph) = nv(g.graph)
vertices(g::StorageGraph) = vertices(g.graph)

ne(g::StorageGraph) = ne(g.graph)
edges(g::StorageGraph) = edges(g.graph)

has_vertex(g::StorageGraph, x...) = has_vertex(g.graph, x...)
@inline has_edge(g::StorageGraph, x...) = has_edge(g.graph, x...)

inneighbors(g::StorageGraph, v::Integer) = inneighbors(g.graph, v)
outneighbors(g::StorageGraph, v::Integer) = fadj(g.graph, v)

inneighbors(g::StorageGraph, n::NamedTuple) = inneighbors(g, g.index[n])
outneighbors(g::StorageGraph, n::NamedTuple) = outneighbors(g, g.index[n])

issubset(g::T, h::T) where T <: StorageGraph = issubset(g.graph, h.graph)

SimpleDiGraph(g::StorageGraph) = g.graph
is_directed(::Type{StorageGraph}) = true
is_directed(::Type{StorageGraph{T}}) where {T} = true
is_directed(g::StorageGraph) = true

"""
    add_edge!(g, u, v, id)

Add an edge `(u, v)` to the StorageGraph `g` with the given `id`.
Return true if the edge has been added, false otherwise.
"""
@inline add_edge!(g::StorageGraph, x...) = add_edge!(g.graph, x...)
function add_edge!(g::StorageGraph, u::Integer, v::Integer, id::Integer)
    add_edge!(g, u, v) || return false
    set_prop!(g, u, v, id)
    return true
end

@inline function rem_edge!(g::StorageGraph, x...)
    rem_prop!(g, x...)
    rem_edge!(g.graph, x...)
end

"""
    add_vertex!(g)
    add_vertex!(g, data)

Add a vertex to the StorageGraph `g` with optional data given by the
NamedTuple `data`.
Return true if the vertex has been added, false otherwise.
"""
add_vertex!(g::StorageGraph) = add_vertex!(g.graph)
function add_vertex!(g::StorageGraph, data::NamedTuple)
    add_vertex!(g) || return false
    set_prop!(g, nv(g), data)
    return true
end

function rem_vertex!(g::StorageGraph, v::Integer)
    rem_prop!(g, v)
    rem_vertex!(g.graph, v)
end

"""
    get_prop(g)
    get_prop(g, v)
    get_prop(g, e)
    get_prop(g, s, d)
Return the specific property (data for vertices, ids for edges, max id for the graph)
defined for graph `g`, vertex `v`, or edge `e` (optionally referenced by source
vertex `s` and destination vertex `d`).
If property does not exist, return an empty collection.
"""
get_prop(g::StorageGraph) = g.maxid[]
get_prop(g::StorageGraph, v::Integer) = get(g.data, v, NamedTuple())
get_prop(g::StorageGraph, e::SimpleEdge) = get(g.paths, e, Set{eltype(g)}[])
get_prop(g::StorageGraph, u::Integer, v::Integer) = get_prop(g, Edge(u, v))

"""
    has_prop(g, v, prop)
    has_prop(g, e, prop)
    has_prop(g, s, d, prop)
Return true if the property `prop` belongs to the specific property
(data for vertices, ids for dges) for graph `g`, vertex `v`, or
edge `e` (optionally referenced by source vertex `s` and destination vertex `d`).
For nodes this will check if `prop` is a key of the node, while for edges
it will check if `prop` belongs to the id list.
"""
has_prop(g::StorageGraph, v::Integer, prop::Symbol) = haskey(get_prop(g, v), prop)
has_prop(g::StorageGraph, e::SimpleEdge, prop::Integer) = in(prop, get_prop(g, e))
has_prop(g::StorageGraph, u::Integer, v::Integer, prop::Integer) = has_prop(g, Edge(u, v), prop)

"""
    set_prop!(g, val)
    set_prop!(g, v, val)
    set_prop!(g, e, val)
    set_prop!(g, s, d, val)

Set (replace) the specific property (data for vertices, ids for edges, max id for the graph)
with value `val` in graph `g`, vertex `v`, or edge `e` (optionally referenced by
source vertex `s` and destination vertex `d`).
Will return false if vertex or edge does not exist, true otherwise.
"""
function set_prop!(g::StorageGraph, v::Integer, val::NamedTuple)
    if has_vertex(g, v)
        g.data[v] = val
        g.index[val] = v
        return true
    end
    return false
end

set_prop!(g::StorageGraph{T}, u::Integer, v::Integer, val::Integer) where {T} = set_prop!(g, Edge(T(u), T(v)), val)

function set_prop!(g::StorageGraph, e::SimpleEdge, val::Integer)
    if has_edge(g, e)
        haskey(g.paths, e) ? push!(g.paths[e], val) : push!(g.paths, e=>Set(val))
        return true
    end
    return false
end

function set_prop!(g::StorageGraph, val)
    g.maxid[] = val
end

"""
    rem_prop!(g, v)
    rem_prop!(g, e)
    rem_prop!(g, s, d)

Remove the specific property (data for vertices, ids for edges) from graph `g`,
vertex `v`, or edge `e` (optionally referenced by source vertex `s` and destination vertex `d`).
If property, vertex, or edge does not exist, will not do anything.
"""
rem_prop!(g::StorageGraph, v::Integer) = delete!(g.data, v)
rem_prop!(g::StorageGraph, e::SimpleEdge) = delete!(g.paths, e)
rem_prop!(g::StorageGraph{T}, u::Integer, v::Integer) where {T} = rem_prop!(g, Edge(T(u), T(v)))

==(x::StorageGraph, y::StorageGraph) = (x.graph == y.graph) && (x.data == y.data) && (x.paths == y.paths)

copy(g::StorageGraph) = deepcopy(g)

zero(g::StorageGraph{T}) where {T} = StorageGraph{T}(SimpleDiGraph{T}())


text:
# The unit convention throughout the package and other packages in the CESMIX Julia Suite
# is to assume all unspecified units to be atomic units as defined in UnitfulAtomic.jl.

# Here we provide constants for the atomic units we use to make the code more readable.

const ENERGY_UNIT = u"hartree"
const FORCE_UNIT = u"hartree / bohr"

const ENERGY_TYPE = typeof(zeros() * ENERGY_UNIT)
const FORCE_TYPE = typeof(zeros() * FORCE_UNIT)


text:
# This file is auto-generated by AWSMetadata.jl
using AWS
using AWS.AWSServices: iot_1click_projects
using AWS.Compat
using AWS.UUIDs

"""
    associate_device_with_placement(device_id, device_template_name, placement_name, project_name)
    associate_device_with_placement(device_id, device_template_name, placement_name, project_name, params::Dict{String,<:Any})

Associates a physical device with a placement.

# Arguments
- `device_id`: The ID of the physical device to be associated with the given placement in
  the project. Note that a mandatory 4 character prefix is required for all deviceId values.
- `device_template_name`: The device template name to associate with the device ID.
- `placement_name`: The name of the placement in which to associate the device.
- `project_name`: The name of the project containing the placement in which to associate
  the device.

"""
function associate_device_with_placement(
    deviceId,
    deviceTemplateName,
    placementName,
    projectName;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "PUT",
        "/projects/$(projectName)/placements/$(placementName)/devices/$(deviceTemplateName)",
        Dict{String,Any}("deviceId" => deviceId);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function associate_device_with_placement(
    deviceId,
    deviceTemplateName,
    placementName,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "PUT",
        "/projects/$(projectName)/placements/$(placementName)/devices/$(deviceTemplateName)",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("deviceId" => deviceId), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_placement(placement_name, project_name)
    create_placement(placement_name, project_name, params::Dict{String,<:Any})

Creates an empty placement.

# Arguments
- `placement_name`: The name of the placement to be created.
- `project_name`: The name of the project in which to create the placement.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"attributes"`: Optional user-defined key/value pairs providing contextual data (such as
  location or function) for the placement.
"""
function create_placement(
    placementName, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "POST",
        "/projects/$(projectName)/placements",
        Dict{String,Any}("placementName" => placementName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_placement(
    placementName,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "POST",
        "/projects/$(projectName)/placements",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("placementName" => placementName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_project(project_name)
    create_project(project_name, params::Dict{String,<:Any})

Creates an empty project with a placement template. A project contains zero or more
placements that adhere to the placement template defined in the project.

# Arguments
- `project_name`: The name of the project to create.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"description"`: An optional description for the project.
- `"placementTemplate"`: The schema defining the placement to be created. A placement
  template defines placement default attributes and device templates. You cannot add or
  remove device templates after the project has been created. However, you can update
  callbackOverrides for the device templates using the UpdateProject API.
- `"tags"`: Optional tags (metadata key/value pairs) to be associated with the project. For
  example, { {\"key1\": \"value1\", \"key2\": \"value2\"} }. For more information, see AWS
  Tagging Strategies.
"""
function create_project(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_1click_projects(
        "POST",
        "/projects",
        Dict{String,Any}("projectName" => projectName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_project(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "POST",
        "/projects",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("projectName" => projectName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_placement(placement_name, project_name)
    delete_placement(placement_name, project_name, params::Dict{String,<:Any})

Deletes a placement. To delete a placement, it must not have any devices associated with
it.  When you delete a placement, all associated data becomes irretrievable.

# Arguments
- `placement_name`: The name of the empty placement to delete.
- `project_name`: The project containing the empty placement to delete.

"""
function delete_placement(
    placementName, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "DELETE",
        "/projects/$(projectName)/placements/$(placementName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_placement(
    placementName,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "DELETE",
        "/projects/$(projectName)/placements/$(placementName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_project(project_name)
    delete_project(project_name, params::Dict{String,<:Any})

Deletes a project. To delete a project, it must not have any placements associated with it.
 When you delete a project, all associated data becomes irretrievable.

# Arguments
- `project_name`: The name of the empty project to delete.

"""
function delete_project(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_1click_projects(
        "DELETE",
        "/projects/$(projectName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_project(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "DELETE",
        "/projects/$(projectName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_placement(placement_name, project_name)
    describe_placement(placement_name, project_name, params::Dict{String,<:Any})

Describes a placement in a project.

# Arguments
- `placement_name`: The name of the placement within a project.
- `project_name`: The project containing the placement to be described.

"""
function describe_placement(
    placementName, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)/placements/$(placementName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_placement(
    placementName,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)/placements/$(placementName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_project(project_name)
    describe_project(project_name, params::Dict{String,<:Any})

Returns an object describing a project.

# Arguments
- `project_name`: The name of the project to be described.

"""
function describe_project(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_project(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    disassociate_device_from_placement(device_template_name, placement_name, project_name)
    disassociate_device_from_placement(device_template_name, placement_name, project_name, params::Dict{String,<:Any})

Removes a physical device from a placement.

# Arguments
- `device_template_name`: The device ID that should be removed from the placement.
- `placement_name`: The name of the placement that the device should be removed from.
- `project_name`: The name of the project that contains the placement.

"""
function disassociate_device_from_placement(
    deviceTemplateName,
    placementName,
    projectName;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "DELETE",
        "/projects/$(projectName)/placements/$(placementName)/devices/$(deviceTemplateName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function disassociate_device_from_placement(
    deviceTemplateName,
    placementName,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "DELETE",
        "/projects/$(projectName)/placements/$(placementName)/devices/$(deviceTemplateName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    get_devices_in_placement(placement_name, project_name)
    get_devices_in_placement(placement_name, project_name, params::Dict{String,<:Any})

Returns an object enumerating the devices in a placement.

# Arguments
- `placement_name`: The name of the placement to get the devices from.
- `project_name`: The name of the project containing the placement.

"""
function get_devices_in_placement(
    placementName, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)/placements/$(placementName)/devices";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_devices_in_placement(
    placementName,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)/placements/$(placementName)/devices",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_placements(project_name)
    list_placements(project_name, params::Dict{String,<:Any})

Lists the placement(s) of a project.

# Arguments
- `project_name`: The project containing the placements to be listed.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return per request. If not set, a
  default value of 100 is used.
- `"nextToken"`: The token to retrieve the next set of results.
"""
function list_placements(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)/placements";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_placements(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "GET",
        "/projects/$(projectName)/placements",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_projects()
    list_projects(params::Dict{String,<:Any})

Lists the AWS IoT 1-Click project(s) associated with your AWS account and region.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"maxResults"`: The maximum number of results to return per request. If not set, a
  default value of 100 is used.
- `"nextToken"`: The token to retrieve the next set of results.
"""
function list_projects(; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_1click_projects(
        "GET", "/projects"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_projects(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "GET", "/projects", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    list_tags_for_resource(resource_arn)
    list_tags_for_resource(resource_arn, params::Dict{String,<:Any})

Lists the tags (metadata key/value pairs) which you have assigned to the resource.

# Arguments
- `resource_arn`: The ARN of the resource whose tags you want to list.

"""
function list_tags_for_resource(
    resourceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "GET",
        "/tags/$(resourceArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    resourceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "GET",
        "/tags/$(resourceArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    tag_resource(resource_arn, tags)
    tag_resource(resource_arn, tags, params::Dict{String,<:Any})

Creates or modifies tags for a resource. Tags are key/value pairs (metadata) that can be
used to manage a resource. For more information, see AWS Tagging Strategies.

# Arguments
- `resource_arn`: The ARN of the resouce for which tag(s) should be added or modified.
- `tags`: The new or modifying tag(s) for the resource. See AWS IoT 1-Click Service Limits
  for the maximum number of tags allowed per resource.

"""
function tag_resource(resourceArn, tags; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_1click_projects(
        "POST",
        "/tags/$(resourceArn)",
        Dict{String,Any}("tags" => tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    resourceArn,
    tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "POST",
        "/tags/$(resourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("tags" => tags), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    untag_resource(resource_arn, tag_keys)
    untag_resource(resource_arn, tag_keys, params::Dict{String,<:Any})

Removes one or more tags (metadata key/value pairs) from a resource.

# Arguments
- `resource_arn`: The ARN of the resource whose tag you want to remove.
- `tag_keys`: The keys of those tags which you want to remove.

"""
function untag_resource(
    resourceArn, tagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "DELETE",
        "/tags/$(resourceArn)",
        Dict{String,Any}("tagKeys" => tagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_resource(
    resourceArn,
    tagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "DELETE",
        "/tags/$(resourceArn)",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("tagKeys" => tagKeys), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_placement(placement_name, project_name)
    update_placement(placement_name, project_name, params::Dict{String,<:Any})

Updates a placement with the given attributes. To clear an attribute, pass an empty value
(i.e., \"\").

# Arguments
- `placement_name`: The name of the placement to update.
- `project_name`: The name of the project containing the placement to be updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"attributes"`: The user-defined object of attributes used to update the placement. The
  maximum number of key/value pairs is 50.
"""
function update_placement(
    placementName, projectName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return iot_1click_projects(
        "PUT",
        "/projects/$(projectName)/placements/$(placementName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_placement(
    placementName,
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "PUT",
        "/projects/$(projectName)/placements/$(placementName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_project(project_name)
    update_project(project_name, params::Dict{String,<:Any})

Updates a project associated with your AWS account and region. With the exception of device
template names, you can pass just the values that need to be updated because the update
request will change only the values that are provided. To clear a value, pass the empty
string (i.e., \"\").

# Arguments
- `project_name`: The name of the project to be updated.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"description"`: An optional user-defined description for the project.
- `"placementTemplate"`: An object defining the project update. Once a project has been
  created, you cannot add device template names to the project. However, for a given
  placementTemplate, you can update the associated callbackOverrides for the device
  definition using this API.
"""
function update_project(projectName; aws_config::AbstractAWSConfig=global_aws_config())
    return iot_1click_projects(
        "PUT",
        "/projects/$(projectName)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_project(
    projectName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return iot_1click_projects(
        "PUT",
        "/projects/$(projectName)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
"""
The InputChannel.jl module defines the InputChannel type and methods

------------------------------------------------------------------------------
COPYRIGHT/LICENSE. This file is part of the XYZ package. It is subject to
the license terms in the LICENSE file found in the top-level directory of
this distribution. No part of the XYZ package, including this file, may be
copied, modified, propagated, or distributed except according to the terms
contained in the LICENSE file.
------------------------------------------------------------------------------
"""
# --- Exports

# ------ Types

export InputChannel

# ------ Functions

export listen, loop_listen, is_listening
export get_value

# --- Type definitions

using ZMQ
include("InputChannelState.jl")

"""
    struct InputChannel

Component that listens for and decodes input data. The `data` field stores the
data most recently received by the channel.
"""
struct InputChannel
    #=
      Fields
      ------
      * `state`: current state of channel

      * `data`: most recently received data

      * `url`: URL that InputChannel listens for input data on

      * `socket`: ZMQ Socket for receiving data
    =#
    state::InputChannelState
    data::AbstractChannelData
    url::String
    socket::Socket
end

"""
    InputChannel(data_type::Type{<:AbstractChannelData}, url::String;
                 use_bind=false)

Construct an input channel that listens for input published to `url` and stores
data of type `data_type`.

When `use_bind` is true, the ZMQ Socket that listens for messages is connected
to `url` using the `bind()` method. Otherwise, the ZMQ Socket is connected to
`url` using the `connect()` method.
"""
function InputChannel(data_type::Type{<:AbstractChannelData},
                      url::String;
                      use_bind=false)
    # Create channel state
    state = InputChannelState()

    # Create data storage
    data = data_type()

    # Create Socket to listen for input data
    socket = Socket(SUB)
    subscribe(socket, "")

    # Connect socket to URL
    if use_bind
        bind(socket, url)
    else
        connect(socket, url)
    end

    # Return new InputChannel
    InputChannel(state, data, url, socket)
end

# --- Method definitions

"""
    listen(channel::InputChannel)

Listen for a single incoming message on `channel`.

Notes
-----
* To listen for a message without blocking, call `listen()` within an
  asynchronous Task.
"""
function listen(channel::InputChannel)
    # Preparations
    message = nothing  # initialized so that it is available outside of Task

    # Listen for incoming message
    task = @task message = recv(channel.socket, Vector{UInt8})
    schedule(task)

    # Update state
    channel.state.is_listening = true

    # Wait for message to arrive
    wait(task)

    # Update channel data
    set_data!(channel.data, message, decode=true)

    # Update state
    channel.state.is_listening = false
end

"""
    loop_listen(channel::InputChannel)

Start loop to continuously and asynchronously listen for incoming messages on
`channel`.
"""
function loop_listen(channel::InputChannel)
    @async begin
        while true
            listen(channel)
        end
    end
end

"""
    is_listening(channel::InputChannel)

Return true if `channel` is in 'listening' state; return false otherwise.
"""
is_listening(channel::InputChannel) = channel.state.is_listening

"""
    get_value(channel::InputChannel)

Return the most recent data value received by `channel`.
"""
get_value(channel::InputChannel) = get_data(channel.data)


text:
export nextprime, nextprime1, prevprime, prevprime1, genprimesb, genprimesc
export countprimesb, countprimesc

# Translated with small edits from ifactor.lisp by John Lapeyre (2014)
# ifactor.lisp: Copyright: 2005-2008 Andrej Vodopivec, Volker van Nek
#               Licence  : GPL

# First values of nextprime, prevprime
const next_prime_array = [0, 2, 3, 5, 5, 7, 7]
const prev_prime_array = [0, 0, 0, 2, 3, 3, 5, 5, 7, 7, 7, 7]

# gaps between numbers that are not multiples of 2,3,5,7
const deltaprimes_next =
    [1, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2,
     1, 6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, 1,
     6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 8, 7, 6,
     5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 8, 7, 6, 5, 4, 3, 2, 1, 6, 5,
     4, 3, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, 1, 6, 5, 4,
     3, 2, 1, 6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2, 1, 6, 5, 4, 3,
     2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 2, 1, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 2]

const deltaprimes_prev =
  [-1, -2, -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, -1, -2, -1, -2, -3, -4, -1, -2,
    -1, -2, -3, -4, -1, -2, -3, -4, -5, -6, -1, -2, -1, -2, -3, -4, -5, -6, -1, -2, -3,
    -4, -1, -2, -1, -2, -3, -4, -1, -2, -3, -4, -5, -6, -1, -2, -3, -4, -5, -6, -1, -2,
    -1, -2, -3, -4, -5, -6, -1, -2, -3, -4, -1, -2, -1, -2, -3, -4, -5, -6, -1, -2, -3,
    -4, -1, -2, -3, -4, -5, -6, -1, -2, -3, -4, -5, -6, -7, -8, -1, -2, -3, -4, -1, -2,
    -1, -2, -3, -4, -1, -2, -1, -2, -3, -4, -1, -2, -3, -4, -5, -6, -7, -8, -1, -2, -3,
    -4, -5, -6, -1, -2, -3, -4, -1, -2, -3, -4, -5, -6, -1, -2, -1, -2, -3, -4, -1, -2,
    -3, -4, -5, -6, -1, -2, -1, -2, -3, -4, -5, -6, -1, -2, -3, -4, -5, -6, -1, -2, -3,
    -4, -1, -2, -1, -2, -3, -4, -1, -2, -3, -4, -5, -6, -1, -2, -1, -2, -3, -4, -5, -6,
    -1, -2, -3, -4, -1, -2, -1, -2, -3, -4, -1, -2, -1, -2, -3, -4, -5, -6, -7, -8, -9,
    -10]

const small_primes = primes(100000)

#product of primes in [59..2897]
function makebigprimemultiple()
    a = primes(2897);
    n = BigInt(1)
    @inbounds for i in 17:length(a)
        n *= a[i]
    end
    n
end

const bigprimemultiple = makebigprimemultiple()

# Deterministic. Only works up to a limit (see below)
function next_prime_det(n, deltaprimes)
    @inbounds n += deltaprimes[mod(n,210) + 1]
    while true
        @inbounds for p in small_primes
            mod(n,p) == 0 && break
            p*p >= n  && return n
        end
        @inbounds n += deltaprimes[mod(n,210) + 1]
    end
    n
end

# Probabalistic. Used for larger primes.
# In Maxima, a single miller_rabin test is done.
# Choice of which gcd's to check could be more fine grained.
# Using bigprimemultiple only seems to slow down the algorithm
function next_prime_prob(n, deltaprimes)
    T = typeof(n)  # all this converting does nothing, apparently. As expected.
    @inbounds n += deltaprimes[mod(n,convert(T,210))+one(T)]
    while true
        if  gcd(n,convert(T,955049953)) == one(T) &&
            gcd(n,convert(T,162490421)) == one(T) &&
#            gcd(n,bigprimemultiple) == 1 &&  # Maxima uses this
#            miller_rabin(n)   # Maxima uses this
            isprime(n)
            return n
        end
        @inbounds n += deltaprimes[mod(n,convert(T,210))+one(T)]
    end
end

# Crossover point between deterministic and probabilistic algorithm
const NEXTCROSSOVER = 10^6  # empirical approximate crossover in speed
#const NEXTCROSSOVER = 99460722   # largest that gives correct results
#const NEXTCROSSOVER = 10^4  # value from Maxima

function nextprime{T<:Integer}(n::T)
    mtwo = convert(T,2)
    n < mtwo && return mtwo
    n <= convert(T,6) && return convert(T,next_prime_array[n+1])
    n < NEXTCROSSOVER && return next_prime_det(n,deltaprimes_next)
    next_prime_prob(n,deltaprimes_next)
end

function prevprime(n)
    n <= 11 && return prev_prime_array[n+1]
    n < NEXTCROSSOVER &&  return next_prime_det(n,deltaprimes_prev)
    next_prime_prob(n,deltaprimes_prev)
end

# nextprime1 and prevprime1
# These are usually slower:  1.5x, 3x, ...
# But for BigInts they seem to be uniformly faster
# code by Hans W Borchers https://github.com/hwborchers/Numbers.jl
function nextprime1(n::Integer)
    if n <= 1; return 2; end
    if n == 2; return 3; end
    if iseven(n)
        n += 1
    else
        n += 2
    end
    if isprime(n); return(n); end
    if mod(n, 3) == 1
        a = 4; b = 2
    elseif mod(n, 3) == 2
        a = 2; b = 4
    else
        n += 2
        a = 2; b = 4
    end
    p = n
    while !isprime(p)
        p += a
        if isprime(p); break; end
        p += b
    end
    return p
end

## Find prime number preceeding n
function prevprime1(n::Integer)
    if n <= 2
        return Array{typeof(n)}(0)
    elseif n <= 3
        return 2
    end
    if iseven(n)
        n -= 1
    else
        n -= 2
    end
    if isprime(n); return n; end

    if mod(n, 3) == 1
        a = 2; b = 4
    elseif mod(n, 3) == 2
        a = 4; b = 2
    else
        n -= 2
        a = 2; b = 4
    end
    p = n
    while !isprime(p)
        p -= a
        if isprime(p); break; end
        p -= b
    end
    return p
end

# nextprime1 is nearly a factor of 2 faster for BigInt
# than the code above.
# nextprime(n::BigInt) = nextprime1(n)

# Try using this. Test shows speed equal to speed of nextprime1
function nextprime(n::BigInt)
    z = BigInt()
    ccall((:__gmpz_nextprime, :libgmp), Void,
          (Ptr{BigInt}, Ptr{BigInt}), &z, &n)
    return z
end

prevprime(n::BigInt) = prevprime1(n)

nextprime(n,k) = (p = n; for i in 1:k p = nextprime(p) end; p)
prevprime(n,k) = (p = n; for i in 1:k p = prevprime(p) end; p)
nextprime1(n,k) = (p = n; for i in 1:k p = nextprime1(p) end; p)
prevprime1(n,k) = (p = n; for i in 1:k p = prevprime1(p) end; p)

# maybe we don't need genprimesc.
# Sometimes more efficient than libprimesieve wrapper genprimes, and
# has larger domain (eg BigInts, Int128)
for (f,fc) in ((:genprimesb, :nextprime), (:genprimesc, :nextprime1))
    @eval begin
        function ($f)(n1,n2)
            ret = Array{typeof(n2)}(0)
            v = n1
            while true
                v = ($fc)(v)
                v > n2 && break
                push!(ret,v)
            end
            return ret
        end
        ($f)(n2) = ($f)(1,n2)
    end
end

# countprimesb nearly twice as fast as countprimesc for  Int64
for (f,fc) in ((:countprimesb, :nextprime), (:countprimesc, :nextprime1))
    @eval begin
        function ($f)(n1,n2)
            c = zero(n2)
            v = n1
            while true
                v = ($fc)(v)
                v > n2 && break
                c += one(n2)
            end
            return c
        end
        ($f)(n2) = ($f)(1,n2)
    end
end


text:
import Base: copy, copyto!, ==
import Random: rand, seed!
import RandomNumbers: gen_seed, union_uint, seed_type, unsafe_copyto!, unsafe_compare

"The key for AESNI."
mutable struct AESNIKey
    key1::UInt128
    key2::UInt128
    key3::UInt128
    key4::UInt128
    key5::UInt128
    key6::UInt128
    key7::UInt128
    key8::UInt128
    key9::UInt128
    key10::UInt128
    key11::UInt128
    AESNIKey() = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
end

copyto!(dest::AESNIKey, src::AESNIKey) = unsafe_copyto!(dest, src, UInt128, 11)

copy(src::AESNIKey) = copyto!(AESNIKey(), src)

==(key1::AESNIKey, key2::AESNIKey) = unsafe_compare(key1, key2, UInt128, 11)

"""
```julia
AESNI1x <: R123Generator1x{UInt128}
AESNI1x([seed])
```

AESNI1x is one kind of AESNI Counter-Based RNGs. It generates one `UInt128` number at a time.

`seed` is an `Integer` which will be automatically converted to `UInt128`.

Only available when [`R123_USE_AESNI`](@ref).
"""
mutable struct AESNI1x <: R123Generator1x{UInt128}
    x::UInt128
    ctr::UInt128
    key::AESNIKey
    function AESNI1x(seed::Integer=gen_seed(UInt128))
        Sys.iswindows() && @warn "`AESNI1x` would be unstable on Windows platform in this version, please use other RNGs."
        r = new(0, 0, AESNIKey())
        seed!(r, seed)
        r
    end
end

function seed!(r::AESNI1x, seed::Integer=gen_seed(UInt128))
    initkey(r, seed % UInt128)
    r.ctr = 0
    random123_r(r)
    r
end

seed_type(::Type{AESNI1x}) = UInt128

function copyto!(dest::AESNI1x, src::AESNI1x)
    dest.x = src.x
    copyto!(dest.key, src.key)
    dest.ctr = src.ctr
    dest
end

copy(src::AESNI1x) = copyto!(AESNI1x(), src)

==(r1::AESNI1x, r2::AESNI1x) = r1.x == r2.x && r1.key == r2.key && r1.ctr == r2.ctr

"""
```julia
AESNI4x <: R123Generator4x{UInt32}
AESNI4x([seed])
```

AESNI4x is one kind of AESNI Counter-Based RNGs. It generates four `UInt32` numbers at a time.

`seed` is a `Tuple` of four `Integer`s which will all be automatically converted to `UInt32`.

Only available when [`R123_USE_AESNI`](@ref).
"""
mutable struct AESNI4x <: R123Generator4x{UInt32}
    x1::UInt32
    x2::UInt32
    x3::UInt32
    x4::UInt32
    ctr1::UInt128
    key::AESNIKey
    p::Int
    function AESNI4x(seed::NTuple{4, Integer}=gen_seed(UInt32, 4))
        Sys.iswindows() && @warn "`AESNI4x` would be unstable on Windows platform in this version, please use other RNGs."
        r = new(0, 0, 0, 0, 0, AESNIKey(), 0)
        seed!(r, seed)
        r
    end
end

function seed!(r::AESNI4x, seed::NTuple{4, Integer}=gen_seed(UInt32, 4))
    key = union_uint(map(x -> x % UInt32, seed))
    initkey(r, key)
    r.ctr1 = 0
    p = 0
    random123_r(r)
    r
end

seed_type(::Type{AESNI4x}) = NTuple{4, UInt32}

function copyto!(dest::AESNI4x, src::AESNI4x)
    unsafe_copyto!(dest, src, UInt32, 4)
    copyto!(dest.key, src.key)
    dest.ctr1 = src.ctr1
    dest.p = src.p
    dest
end

copy(src::AESNI4x) = copyto!(AESNI4x(), src)

==(r1::AESNI4x, r2::AESNI4x) = unsafe_compare(r1, r2, UInt32, 4) && r1.key == r2.key &&
    r1.ctr1 == r2.ctr1 && r1.p == r2.p

function initkey(r::AESNI1x, key::UInt128)
    k = Ptr{UInt128}(pointer_from_objref(r.key))
    ref = Ref(key)
    k2 = Ptr{UInt128}(pointer_from_objref(ref))
    ccall((:keyinit, librandom123), Nothing, (
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}
    ), k2, k, k + 16, k + 32, k + 48, k + 64, k + 80, k + 96,
    k + 112, k + 128, k + 144, k + 160)
    r
end

function initkey(r::AESNI4x, key::UInt128)
    k = Ptr{UInt128}(pointer_from_objref(r.key))
    ref = Ref(key)
    k2 = Ptr{UInt128}(pointer_from_objref(ref))
    ccall((:keyinit, librandom123), Nothing, (
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}
    ), k2, k, k + 16, k + 32, k + 48, k + 64, k + 80, k + 96,
    k + 112, k + 128, k + 144, k + 160)
    r
end

function aesni1xm128i(r::AESNI1x)
    k = Ptr{UInt128}(pointer_from_objref(r.key))
    p = Ptr{UInt128}(pointer_from_objref(r))
    ref = Ref(r.ctr)
    p1 = Ptr{UInt128}(pointer_from_objref(ref))
    ccall((:aesni1xm128i, librandom123), Nothing, (
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}
    ), p1, k, k + 16, k + 32, k + 48, k + 64, k + 80, k + 96,
    k + 112, k + 128, k + 144, k + 160, p)
    unsafe_load(p, 1)
end

function aesni1xm128i(r::AESNI4x)
    k = Ptr{UInt128}(pointer_from_objref(r.key))
    p = Ptr{UInt128}(pointer_from_objref(r))
    ref = Ref(r.ctr1)
    p1 = Ptr{UInt128}(pointer_from_objref(ref))
    ccall((:aesni1xm128i, librandom123), Nothing, (
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128}, Ptr{UInt128},
    Ptr{UInt128}
    ), p1, k, k + 16, k + 32, k + 48, k + 64, k + 80, k + 96,
    k + 112, k + 128, k + 144, k + 160, p)
    unsafe_load(p, 1)
end

function random123_r(r::AESNI1x)
    (aesni1xm128i(r),)
end

function random123_r(r::AESNI4x)
    aesni1xm128i(r)
    x = unsafe_wrap(Array, Ptr{UInt32}(pointer_from_objref(r)), 4)
    x[1], x[2], x[3], x[4]
end

# FIXME: Unstable AESNI4x.


text:
function write_pbs(job_basename, run_number)
    #This function writes a PBS script to be submitted to a computing cluster, which runs 'optimize.jl'

    f = open(job_basename*"_"*string(run_number)*".pbs", "w")
    println(f, "#!/bin/tcsh")
    println(f, "#PBS -A ebf11_a_g_sc_default")
    println(f, "#PBS -l nodes=1:ppn=1")
    println(f, "#PBS -l walltime=48:00:00")
    println(f, "#PBS -l pmem=8gb")
    println(f, "#PBS -j oe")
    #println(f, "#PBS -m abe")
    #println(f, "#PBS -M myh7@psu.edu")
    println(f, "")
    println(f, "cd \$PBS_O_WORKDIR")
    println(f, "")
    println(f, "/gpfs/group/ebf11/default/julia/bin/julia optimize.jl "*string(run_number))
    close(f)
end

##### NOTE: must run this script from the same directory in which you want to submit the jobs from!

##### To actually write a number of PBS scripts and submit them:

job_basename = "optimize_job" #base name for the job to be submitted
n_submits = 20 #total number of jobs to submit

for i in 1:n_submits
    write_pbs(job_basename, i)
    job_name = job_basename*"_"*string(i)*".pbs"
    run(`qsub $job_name`) #this line submits the job by running 'qsub' in the command line!
    #println(`qsub $job_name`) #this line is just to see what the command looks like in the command line
    println("Job ", i, " submitted.")
end