text:
function sqlite3_open(file::String,handle::Array{Ptr{Void},1})
	@windows_only return ccall( (:sqlite3_open, sqlite3_lib), stdcall,
			Cint, (Ptr{Uint8},Ptr{Void}),
			file,handle)
	@unix_only return ccall( (:sqlite3_open, sqlite3_lib),
		Cint, (Ptr{Uint8},Ptr{Void}),
		file,handle)
end
function sqlite3_open16(file::String,handle::Array{Ptr{Void},1})
	@windows_only return ccall( (:sqlite3_open16, sqlite3_lib), stdcall,
			Cint, (Ptr{Uint8},Ptr{Void}),
			file,handle)
	@unix_only return ccall( (:sqlite3_open16, sqlite3_lib),
		Cint, (Ptr{Uint8},Ptr{Void}),
		file,handle)
end
function sqlite3_open_v2(file::String,handle::Array{Ptr{Void},1},flags::Cint,vfs::String)
	@windows_only return ccall( (:sqlite3_open_v2, sqlite3_lib), stdcall,
			Cint, (Ptr{Uint8},Ptr{Void},Cint,Ptr{Uint8}),
			file,handle,flags,vfs)
	@unix_only return ccall( (:sqlite3_open_v2, sqlite3_lib),
		Cint, (Ptr{Uint8},Ptr{Void},Cint,Ptr{Uint8}),
		file,handle,flags,vfs)
end
function sqlite3_prepare(handle::Ptr{Void},query::String,stmt::Array{Ptr{Void},1},unused::Array{Ptr{Void},1})
	@windows_only return ccall( (:sqlite3_prepare, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
			handle,query,length(query),stmt,unused)
	@unix_only return ccall( (:sqlite3_prepare, sqlite3_lib),
		Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
			handle,query,length(query),stmt,unused)
end
function sqlite3_prepare_v2(handle::Ptr{Void},query::String,stmt::Array{Ptr{Void},1},unused::Array{Ptr{Void},1})
	@windows_only return ccall( (:sqlite3_prepare_v2, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
			handle,query,length(query),stmt,unused)
	@unix_only return ccall( (:sqlite3_prepare_v2, sqlite3_lib),
		Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
			handle,query,length(query),stmt,unused)
end
function sqlite3_prepare16(handle::Ptr{Void},query::String,stmt::Array{Ptr{Void},1},unused::Array{Ptr{Void},1})
	@windows_only return ccall( (:sqlite3_prepare16, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
			handle,query,length(query),stmt,unused)
	@unix_only return ccall( (:sqlite3_prepare16, sqlite3_lib),
		Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
			handle,query,length(query),stmt,unused)
end
function sqlite3_prepare16_v2(handle::Ptr{Void},query::String,stmt::Array{Ptr{Void},1},unused::Array{Ptr{Void},1})
	@windows_only return ccall( (:sqlite3_prepare16_v2, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
			handle,query,length(query),stmt,unused)
	@unix_only return ccall( (:sqlite3_prepare16_v2, sqlite3_lib),
		Cint, (Ptr{Void},Ptr{Uint8},Cint,Ptr{Void},Ptr{Void}),
		handle,query,length(query),stmt,unused)
end
function sqlite3_step(stmt::Ptr{Void})
	@windows_only return ccall( (:sqlite3_step, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			stmt)
	@unix_only return ccall( (:sqlite3_step, sqlite3_lib),
		Cint, (Ptr{Void},),
		stmt)
end
function sqlite3_column_count(stmt::Ptr{Void})
	@windows_only return ccall( (:sqlite3_column_count, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			stmt)
	@unix_only return ccall( (:sqlite3_column_count, sqlite3_lib),
		Cint, (Ptr{Void},),
		stmt)
end
function sqlite3_column_blob(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_blob, sqlite3_lib), stdcall,
			Ptr{Void}, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_blob, sqlite3_lib),
		Ptr{Void}, (Ptr{Void},Cint),
		stmt,col)
end

function sqlite3_column_bytes(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_bytes, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_bytes, sqlite3_lib),
		Cint, (Ptr{Void},Cint),
		stmt,col)
end
function sqlite3_column_bytes16(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_bytes16, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_bytes16, sqlite3_lib),
		Cint, (Ptr{Void},Cint),
		stmt,col)
end
function sqlite3_column_double(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_double, sqlite3_lib), stdcall,
			Cdouble, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_double, sqlite3_lib),
		Cdouble, (Ptr{Void},Cint),
		stmt,col)
end
function sqlite3_column_int(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_int, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_int, sqlite3_lib),
		Cint, (Ptr{Void},Cint),
		stmt,col)
end
function sqlite3_column_int64(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_int64, sqlite3_lib), stdcall,
			Clonglong, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_int64, sqlite3_lib),
		Clonglong, (Ptr{Void},Cint),
		stmt,col)
end
function sqlite3_column_text(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_text, sqlite3_lib), stdcall,
			Ptr{Uint8}, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_text, sqlite3_lib),
		Ptr{Uint8}, (Ptr{Void},Cint),
		stmt,col)
end
function sqlite3_column_text16(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_text16, sqlite3_lib), stdcall,
			Ptr{Void}, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_text16, sqlite3_lib),
		Ptr{Void}, (Ptr{Void},Cint),
		stmt,col)
end
const FUNCS = [SQLITE_INTEGER=>sqlite3_column_int,SQLITE_FLOAT=>sqlite3_column_double,SQLITE3_TEXT=>sqlite3_column_text,SQLITE_BLOB=>sqlite3_column_blob,SQLITE_NULL=>sqlite3_column_text]
function sqlite3_column_type(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_column_type, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_column_type, sqlite3_lib),
		Cint, (Ptr{Void},Cint),
		stmt,col)
end
# function sqlite3_column_value(stmt::Ptr{Void},col::Cint)
# 	return ccall( (:sqlite3_column_value, sqlite3_lib), stdcall,
# 			Ptr{Void}, (Ptr{Void},Cint),
# 			stmt,col)
# 	return ccall( (:sqlite3_column_value, sqlite3_lib),
# 			Ptr{Void}, (Ptr{Void},Cint),
# 			stmt,col)
# end
# SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
function sqlite3_finalize(stmt::Ptr{Void})
	@windows_only return ccall( (:sqlite3_finalize, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			stmt)
	@unix_only return ccall( (:sqlite3_finalize, sqlite3_lib),
		Cint, (Ptr{Void},),
		stmt)
end
function sqlite3_reset(stmt::Ptr{Void})
	@windows_only return ccall( (:sqlite3_reset, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			stmt)
	@unix_only return ccall( (:sqlite3_reset, sqlite3_lib),
		Cint, (Ptr{Void},),
		stmt)
end

# SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N);
function sqlite3_column_name(stmt::Ptr{Void},n::Int)
	@windows_only return ccall( (:sqlite3_column_name, sqlite3_lib), stdcall,
			Ptr{Uint8}, (Ptr{Void},Cint),
			stmt,n)
	@unix_only return ccall( (:sqlite3_column_name, sqlite3_lib),
		Ptr{Uint8}, (Ptr{Void},Cint),
		stmt,n)
end
function sqlite3_column_name16(stmt::Ptr{Void},n::Int)
	@windows_only return ccall( (:sqlite3_column_name16, sqlite3_lib), stdcall,
			Ptr{Uint8}, (Ptr{Void},Cint),
			stmt,n)
	@unix_only return ccall( (:sqlite3_column_name16, sqlite3_lib),
		Ptr{Uint8}, (Ptr{Void},Cint),
		stmt,n)
end
# SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);

# SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int);
# SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int);
# SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int);
# SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int);
# SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int);
# SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);

# SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int);
# SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);

# SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);

function sqlite3_close(handle::Ptr{Void})
	@windows_only return ccall( (:sqlite3_close, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			handle)
	@unix_only return ccall( (:sqlite3_close, sqlite3_lib),
		Cint, (Ptr{Void},),
		handle)
end
function sqlite3_close_v2(handle::Ptr{Void})
	@windows_only return ccall( (:sqlite3_close_v2, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			handle)
	@unix_only return ccall( (:sqlite3_close_v2, sqlite3_lib),
		Cint, (Ptr{Void},),
		handle)
end
function sqlite3_initialize()
	@windows_only return ccall( (:sqlite3_initialize, sqlite3_lib), stdcall,
			Cint, (),
			)
	@unix_only return ccall( (:sqlite3_initialize, sqlite3_lib),
		Cint, (),
		)
end
function sqlite3_shutdown()
	@windows_only return ccall( (:sqlite3_shutdown, sqlite3_lib), stdcall,
			Cint, (),
			)
	@unix_only return ccall( (:sqlite3_shutdown, sqlite3_lib),
		Cint, (),
		)
end
function sqlite3_os_init()
	@windows_only return ccall( (:sqlite3_os_init, sqlite3_lib), stdcall,
			Cint, (),
			)
	@unix_only return ccall( (:sqlite3_os_init, sqlite3_lib),
		Cint, (),
		)
end
function sqlite3_os_end()
	@windows_only return ccall( (:sqlite3_os_end, sqlite3_lib), stdcall,
			Cint, (),
			)
	@unix_only return ccall( (:sqlite3_os_end, sqlite3_lib),
		Cint, (),
		)
end
function sqlite3_free_table(result::Array{String,1})
	@windows_only return ccall( (:sqlite3_free_table, sqlite_lib), stdcall,
			Void, (Ptr{Ptr{Void}},),
			result)
	@unix_only return ccall( (:sqlite3_free_table, sqlite_lib),
		Void, (Ptr{Ptr{Void}},),
		result)
end

# SQLITE_API int sqlite3_exec(
#   sqlite3*,                                  /* An open database */
#   const char *sql,                           /* SQL to be evaluated */
#   int (*callback)(void*,int,char**,char**),  /* Callback function */
#   void *,                                    /* 1st argument to callback */
#   char **errmsg                              /* Error msg written here */
# );
# SQLITE_API int sqlite3_get_table(
#   sqlite3 *db,          /* An open database */
#   const char *zSql,     /* SQL to be evaluated */
#   char ***pazResult,    /* Results of the query */
#   int *pnRow,           /* Number of result rows written here */
#   int *pnColumn,        /* Number of result columns written here */
#   char **pzErrmsg       /* Error msg written here */
# );

# SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
# SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
# SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
function sqlite3_errcode(db::Ptr{Void})
	@windows_only return ccall( (:sqlite3_errcode, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			db)
	@unix_only return ccall( (:sqlite3_errcode, sqlite3_lib),
		Cint, (Ptr{Void},),
		db)
end
function sqlite3_extended_errcode(db::Ptr{Void})
	@windows_only return ccall( (:sqlite3_extended_errcode, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},),
			db)
	@unix_only return ccall( (:sqlite3_extended_errcode, sqlite3_lib),
		Cint, (Ptr{Void},),
		db)
end
# SQLITE_API int sqlite3_errcode(sqlite3 *db);
# SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
# SQLITE_API const char *sqlite3_errmsg(sqlite3*);
function sqlite3_errmsg(db::Ptr{Void})
	@windows_only return ccall( (:sqlite3_errmsg, sqlite3_lib), stdcall,
			Ptr{Uint8}, (Ptr{Void},),
			db)
	@unix_only return ccall( (:sqlite3_errmsg, sqlite3_lib),
		Ptr{Uint8}, (Ptr{Void},),
		db)
end
# SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
function sqlite3_errstr(ret::Cint)
	@windows_only return ccall( (:sqlite3_errstr, sqlite3_lib), stdcall,
			Ptr{Uint8}, (Cint,),
			ret)
	@unix_only return ccall( (:sqlite3_errstr, sqlite3_lib),
		Ptr{Uint8}, (Cint,),
		ret)
end
# SQLITE_API const char *sqlite3_errstr(int);

# SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);

function sqlite3_sql(stmt::Ptr{Void})
	@windows_only return ccall( (:sqlite3_sql, sqlite3_lib), stdcall,
			Void, (Ptr{Void},),
			stmt)
	@unix_only return ccall( (:sqlite3_sql, sqlite3_lib),
		Void, (Ptr{Void},),
		stmt)
end

# SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);

# SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);

# SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
# SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);
function sqlite3_bind_double(stmt::Ptr{Void},col::Int,value::Float64)
	@windows_only return ccall( (:sqlite3_bind_double, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint,Float64),
			stmt,col,value)
	@unix_only return ccall( (:sqlite3_bind_double, sqlite3_lib),
		Cint, (Ptr{Void},Cint,Float64),
		stmt,col,value)
end
# SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);
function sqlite3_bind_int(stmt::Ptr{Void},col::Int,value::Int32)
	@windows_only return ccall( (:sqlite3_bind_int, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint,Int32),
			stmt,col,value)
	@unix_only return ccall( (:sqlite3_bind_int, sqlite3_lib),
		Cint, (Ptr{Void},Cint,Int32),
		stmt,col,value)
end
# SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
function sqlite3_bind_int64(stmt::Ptr{Void},col::Int,value::Int64)
	@windows_only return ccall( (:sqlite3_bind_int64, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint,Int64),
			stmt,col,value)
	@unix_only return ccall( (:sqlite3_bind_int64, sqlite3_lib),
		Cint, (Ptr{Void},Cint,Int64),
		stmt,col,value)
end
# SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);
function sqlite3_bind_null(stmt::Ptr{Void},col::Int)
	@windows_only return ccall( (:sqlite3_bind_null, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint),
			stmt,col)
	@unix_only return ccall( (:sqlite3_bind_null, sqlite3_lib),
		Cint, (Ptr{Void},Cint),
		stmt,col)
end
# SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
function sqlite3_bind_text(stmt::Ptr{Void},col::Int,value::String,len::Int,cb::Ptr{Void})
	@windows_only return ccall( (:sqlite3_bind_text, sqlite3_lib), stdcall,
			Cint, (Ptr{Void},Cint,Ptr{Uint8},Cint,Ptr{Void}),
			stmt,col,value,len,cb)
	@unix_only return ccall( (:sqlite3_bind_text, sqlite3_lib),
		Cint, (Ptr{Void},Cint,Ptr{Uint8},Cint,Ptr{Void}),
		stmt,col,value,len,cb)
end
# SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
# SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
# SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);

# SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*);

# SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);

# SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);

# SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);

# SQLITE_API int sqlite3_table_column_metadata(
#   sqlite3 *db,                /* Connection handle */
#   const char *zDbName,        /* Database name or NULL */
#   const char *zTableName,     /* Table name */
#   const char *zColumnName,    /* Column name */
#   char const **pzDataType,    /* OUTPUT: Declared data type */
#   char const **pzCollSeq,     /* OUTPUT: Collation sequence name */
#   int *pNotNull,              /* OUTPUT: True if NOT NULL constraint exists */
#   int *pPrimaryKey,           /* OUTPUT: True if column part of PK */
#   int *pAutoinc               /* OUTPUT: True if column is auto-increment */
# );

# SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);


text:
using OrderedCollections: OrderedDict

@testset "JobQueue" begin
    @testset "constructor" begin
        arn = "arn:aws:batch:us-east-1:000000000000:job-queue/queue"
        @test JobQueue(arn).arn == arn

        patch = describe_job_queues_patch(
            OrderedDict(
                "jobQueueName" => "queue-name",
                "jobQueueArn" => arn,
            )
        )
        apply(patch) do
            @test JobQueue("queue-name").arn == arn
        end

        patch = describe_job_queues_patch()
        apply(patch) do
            @test_throws ErrorException JobQueue("queue-name")
        end
    end

    @testset "compute_environments" begin
        # Note: to date we've only used queues with a single compute environment
        queue = JobQueue("arn:aws:batch:us-east-1:000000000000:job-queue/queue")
        patch = describe_job_queues_patch(
            OrderedDict(
                "jobQueueArn" => queue.arn,
                "computeEnvironmentOrder" => [
                    OrderedDict("order" => 2, "computeEnvironment" => "arn:aws:batch:us-east-1:000000000000:compute-environment/two"),
                    OrderedDict("order" => 1, "computeEnvironment" => "arn:aws:batch:us-east-1:000000000000:compute-environment/one"),
                ],
            )
        )

        expected = [
            ComputeEnvironment("arn:aws:batch:us-east-1:000000000000:compute-environment/one"),
            ComputeEnvironment("arn:aws:batch:us-east-1:000000000000:compute-environment/two"),
        ]

        apply(patch) do
            @test AWSBatch.compute_environments(queue) == expected
        end
    end

    @testset "max_vcpus" begin
        queue = JobQueue("arn:aws:batch:us-east-1:000000000000:job-queue/queue")
        patches = [
            describe_job_queues_patch(
                OrderedDict(
                    "jobQueueArn" => queue.arn,
                    "computeEnvironmentOrder" => [
                        OrderedDict("order" => 1, "computeEnvironment" => "arn:aws:batch:us-east-1:000000000000:compute-environment/one"),
                        OrderedDict("order" => 2, "computeEnvironment" => "arn:aws:batch:us-east-1:000000000000:compute-environment/two"),
                    ],
                )
            )
            describe_compute_environments_patch([
                OrderedDict(
                    "computeEnvironmentArn" => "arn:aws:batch:us-east-1:000000000000:compute-environment/one",
                    "computeResources" => OrderedDict("maxvCpus" => 7),
                ),
                OrderedDict(
                    "computeEnvironmentArn" => "arn:aws:batch:us-east-1:000000000000:compute-environment/two",
                    "computeResources" => OrderedDict("maxvCpus" => 8),
                )
            ])
        ]

        apply(patches) do
            @test AWSBatch.max_vcpus(queue) == 15
        end
    end
end

ext: jl


text:
@testset "testing length" begin
    @test length(buildKnots(1.0, 4., 10)) == 10
end

@testset "testing bounds" begin
    @test buildKnots(1.0, 4., 10)[2] == 1.0 && buildKnots(1.0, 4., 10)[end-1] == 4.0
end


text:
include("helpers.jl")

# tests that start new processes:

@timeit_include("compiletimes.jl")
verify_no_running_processes()
if get(ENV, "PLUTO_TEST_ONLY_COMPILETIMES", nothing) == "true"
    print_timeroutput()
    exit(0)
end
@timeit_include("Events.jl")
verify_no_running_processes()
@timeit_include("WorkspaceManager.jl")
verify_no_running_processes()
@timeit_include("packages/Basic.jl")
verify_no_running_processes()
@timeit_include("Bonds.jl")
verify_no_running_processes()
@timeit_include("RichOutput.jl")
verify_no_running_processes()
@timeit_include("React.jl")
verify_no_running_processes()
@timeit_include("Dynamic.jl")
verify_no_running_processes()
@timeit_include("MacroAnalysis.jl")
verify_no_running_processes()
@timeit_include("Logging.jl")
verify_no_running_processes()
@timeit_include("webserver.jl")
verify_no_running_processes()
@timeit_include("Notebook.jl")
verify_no_running_processes()
@timeit_include("Configuration.jl")
verify_no_running_processes()

# tests that don't start new processes:
@timeit_include("ReloadFromFile.jl")
@timeit_include("packages/PkgCompat.jl")
@timeit_include("ExpressionExplorer.jl")
@timeit_include("MethodSignatures.jl")
@timeit_include("MoreAnalysis.jl")
@timeit_include("Analysis.jl")
@timeit_include("webserver_utils.jl")
@timeit_include("data structures.jl")
@timeit_include("DependencyCache.jl")
@timeit_include("Throttled.jl")
@timeit_include("cell_disabling.jl")

verify_no_running_processes()

print_timeroutput()

# TODO: test PlutoRunner functions like:
# - from_this_notebook

# TODO: test include() inside notebooks

# TODO: test async execution order
# TODO: test @bind

# TODO: test if notebooks are saved correctly after edits


text:
# Model
include_model("hopper")
model_ft = free_time_model(model)

# Horizon
T = 51

# Time step
tf = 1.0
h = tf / (T - 1)

# Bounds
_uu = Inf * ones(model_ft.m)
_uu[model_ft.idx_u] = model_ft.uU
_uu[end] = 2.0 * h
_ul = zeros(model_ft.m)
_ul[model_ft.idx_u] = model_ft.uL
_ul[end] = 0.5 * h
ul, uu = control_bounds(model_ft, T, _ul, _uu)

# Initial and final states
z_h = 0.25
q1 = [0.0, 0.5 + z_h, 0.0, 0.25]

xl, xu = state_bounds(model_ft, T,
		[model_ft.qL; model_ft.qL],
		[model_ft.qU; model_ft.qU],
        x1 = [q1; Inf * ones(model.nq)])

# Objective
include_objective("velocity")
obj_tracking = quadratic_time_tracking_objective(
    [Diagonal(zeros(model_ft.n)) for t = 1:T],
    [Diagonal([1.0, 1.0,
		ones(model_ft.nc)..., ones(model_ft.nb)...,
		zeros(model_ft.m - model_ft.nu - model_ft.nc - model_ft.nb - 1)..., 0.0])
		for t = 1:T-1],
    [zeros(model_ft.n) for t = 1:T],
    [zeros(model_ft.m) for t = 1:T],
    1.0)
obj_contact_penalty = PenaltyObjective(1.0e5, model_ft.m - 1)
obj_velocity = velocity_objective(
    [Diagonal(1.0 * ones(model_ft.nq)) for t = 1:T-1],
    model_ft.nq,
    h = h,
    idx_angle = collect([3]))
obj = MultiObjective([obj_tracking, obj_contact_penalty, obj_velocity])

# Constraints
include_constraints(["free_time", "contact", "loop"])
con_free_time = free_time_constraints(T)
con_contact = contact_constraints(model_ft, T)
con_loop = loop_constraints(model, (1:model.n), 1, T)
con = multiple_constraints([con_free_time, con_contact, con_loop])

# Problem
prob = trajectory_optimization_problem(model_ft,
               obj,
               T,
               xl = xl,
               xu = xu,
               ul = ul,
               uu = uu,
               con = con)

# Trajectory initialization
x0 = [[q1; q1] for t = 1:T] # linear interpolation on state
u0 = [[1.0e-3 * rand(model_ft.m-1); h] for t = 1:T-1] # random controls

# Pack trajectories into vector
z0 = pack(x0, u0, prob)

#NOTE: may need to run examples multiple times to get good trajectories
# Solve nominal problem
@time z̄, info = solve(prob, copy(z0),
	nlp = :ipopt,
	tol = 1.0e-7, c_tol = 1.0e-7, mapl = 5,
	time_limit = 60)
@show check_slack(z̄, prob)
x̄, ū = unpack(z̄, prob)
tf, t, h̄ = get_time(ū)

q = state_to_configuration(x̄)
u = [u[model.idx_u] for u in ū]
γ = [u[model.idx_λ] for u in ū]
b = [u[model.idx_b] for u in ū]
h̄ = mean(h̄)
@save joinpath(pwd(), "examples/trajectories/hopper_vertical_gait.jld2") z̄ x̄ ū h̄ q u γ b

if true
	# include_snopt()
	@time z̄ , info = solve(prob, copy(z0),
		nlp = :ipopt,
		tol = 1.0e-5, c_tol = 1.0e-5, mapl = 5,
		time_limit = 60)
	@show check_slack(z̄, prob)
	x̄, ū = unpack(z̄, prob)
	tf, t, h̄ = get_time(ū)

	# projection
	Q = [Diagonal(ones(model_ft.n)) for t = 1:T]
	R = [Diagonal(0.1 * ones(model_ft.m)) for t = 1:T-1]
	x_proj, u_proj = lqr_projection(model_ft, x̄, ū, h̄[1], Q, R)

	@show tf
	@show h̄[1]
	@save joinpath(pwd(), "examples/trajectories/hopper_vertical_gait.jld2") x̄ ū h̄ x_proj u_proj
else
	@load joinpath(pwd(), "examples/trajectories/hopper_vertical_gait.jld2") x̄ ū h̄ x_proj u_proj
end

z_traj = [x[2] for x in x̄]
k_traj = [u[3] for u in ū]
λ_traj = [u[model_ft.idx_λ] for u in ū]
f_traj = [u[2] for u in ū]

plot(hcat(k_traj...)', linetype = :steppost, label = "impedance")
plot!(h̄[1] * hcat(λ_traj...)', linetype = :steppost, label = ["" "λ1"])
plot!(h̄[1] * hcat(f_traj...)', linetype = :steppost, label = "force")

plot(hcat(z_traj...)', linetype = :steppost, label = "z")

#
# using Plots
# plot(hcat(ū...)[1:2, :]',
#     linetype = :steppost,
#     label = "",
#     color = :red,
#     width = 2.0)
#
# plot!(hcat(u_proj...)[1:2, :]',
#     linetype = :steppost,
#     label = "", color = :black)
#
# plot(hcat(state_to_configuration(x̄)...)',
#     color = :red,
#     width = 2.0,
# 	label = "")
#
# plot!(hcat(state_to_configuration(x_proj)...)',
#     color = :black,
# 	label = "")
include(joinpath(pwd(), "models/visualize.jl"))
vis = Visualizer()
render(vis)
visualize!(vis, model_ft,
	state_to_configuration([x̄..., ([x̄[2:end] for i = 1:5]...)...]),
	Δt = h̄[1],
	scenario = :vertical)

# @time z̄ , info = solve(prob, copy(z̄),
# 	nlp = :ipopt,
# 	tol = 1.0e-2, c_tol = 1.0e-2, mapl = 0,
# 	time_limit = 60)
#
# obj_track = quadratic_tracking_objective(
#     [Diagonal(100.0 * ones(model.n)) for t = 1:T],
#     [Diagonal(ones(model.m)) for t = 1:T-1],
#     [x̄[t] for t = 1:T],
#     [ū[t][1:end-1] for t = 1:T-1])
#
#
# prob_track = trajectory_optimization_problem(model,
#                obj_track,
#                T,
#                xl = xl,
#                xu = xu,
#                ul = [ul[t][1:end-1] for t = 1:T-1],
#                uu = [uu[t][1:end-1] for t = 1:T-1],
#                con = con_contact)
#
# z_track = pack(x̄, [ū[t][1:end-1] for t = 1:T-1], prob_track) #+ 1.0e-2 * ones(prob_track.num_var)
# # z_track[1:model.n] .+= 0.25
# # prob_track.primal_bounds[1][1:model.n] = z_track[1:model.n]
# # prob_track.primal_bounds[2][1:model.n] = z_track[1:model.n]
#
# @time z_sol , info = solve(prob_track, copy(z_track),
# 	nlp = :ipopt,
# 	tol = 1.0e-5, c_tol = 1.0e-5, mapl = 0,
# 	time_limit = 60)
#
# x_sol, u_sol = unpack(z_sol, prob_track)
# plot(hcat(state_to_configuration(x̄)...)')
# plot!(hcat(state_to_configuration(x_sol)...)')
#
# plot(hcat([ū[t][1:end-1] for t = 1:T-1]...)', color = :red, width = 2.0, label = "")
# plot!(hcat(u_sol...)', color = :black, width = 1.0)
#
# x_sol[1] - z_track[1:model.n]


text:
# Example of running this routine:

include("run_hmc_background_student_rescale.jl")

# File containing the transit times:
fname = "timing_simon.txt"

#felements = "../../data/elements_noprior_students.txt"
# Use elements from 2017 analysis (copied from /Users/ericagol/Observing/Spitzer/Cycle13/TRAPPIST1/campaign02/September2017
# but with addition of inclination and longitude of nodes:
#felements = "elements_11082017.txt"
felements = "/Users/ericagol/Observing/Spitzer/Cycle13/TRAPPIST1/campaign02/December2017/elements_12072017.txt"

# Name an output file:
foutput = string("T1_run_hmc_student_ecc_lgndof_V1exp2nuinv_nstep10_eps0.1_nleap20_002.jld2")

# Call the likelihood profile function:
#run_hmc(fname,felements,foutput,2000,0.1,20,false)
run_hmc(fname,felements,foutput,10,0.1,20,false)


text:
export Modifier
export make_string, make_modifier, make_modifier_vector, main_mod

"""
	$(SIGNATURES)

Modifier for a `Case` or `Experiment`. Can be a `Symbol`, such as `:openAdmission`, or a `Tuple`, such as `(:capacityUp, 10)`.
"""
struct Modifier{T}
    m :: T
end

main_mod(x :: Modifier{Symbol}) = x.m;
main_mod(x :: Modifier{T}) where T = x.m[1];

function find_main_mod(xV :: AbstractVector, mainMod :: Symbol)
    if isempty(xV)
        return nothing
    else
        # matchV = [main_mod(x) == mainMod  for x in xV];
        j = findfirst(x -> main_mod(x) == mainMod,  xV);
        if isnothing(j)
            return nothing
        else
            return xV[j]
        end
    end
end

"""
	$(SIGNATURES)

Make a Modifier or pass through an existing one.
"""
make_modifier(x :: Modifier{T}) where T = x;
make_modifier(x :: T) where T = Modifier(x);

"""
	$(SIGNATURES)

Convert a Vector of anything into a Vector of Modifier.
"""
function make_modifier_vector(v :: AbstractVector)
    if isempty(v)
        return Vector{Modifier}();
    else
        return sort([make_modifier(x)  for x in v]);
    end
end

make_modifier_vector(v :: AbstractVector{Modifier}) = sort(v);

Base.show(io :: IO, m :: Modifier{T}) where T =
    print(io, make_string(m));

## ------------  String representation

"""
	$(SIGNATURES)

String representation of a `Modifier`.
Note: These are used to generate file names. Avoid dashes. Longleaf does not like them.

# Example
```julia
make_string(Modifier((:workStudyPrefScale, 2.0))) == "workStudyPrefScale2p0"
```
"""
make_string(m :: Modifier{Symbol}) = string(m.m);

function make_string(m :: Modifier{T1}) where T1
    return join(make_string.(m.m), "");
end

function make_string(m :: Modifier{Tuple{Symbol, T}}) where T <: Integer
    return make_string(m.m);
end

# 1.0 => "1p0"
make_string(m :: Real) = replace(string(m), "." => "p");
make_string(m :: Symbol) = string(m);

# Ints stay Ints
function make_string(m :: Tuple{Symbol,T1}) where T1 <: Integer
    return "$(m[1])$(m[2])";
end

# Floats get rounded
function make_string(m :: Tuple{Symbol,T1}) where T1 <: Real
    s2 = make_string(m[2]);
    return "$(m[1])$(s2)";
end

function make_string(mods :: AbstractVector{Modifier})
    modStr = join(make_string.(mods), "_");
end

## -----------  Access

# So we can sort a Modifier vector.
Base.isless(m1 :: Modifier{T1}, m2 :: Modifier{T2}) where {T1, T2} =
    Base.isless(make_string(m1), make_string(m2));

Base.isequal(m1 :: Modifier{T1}, m2 :: Modifier{T1}) where T1 =
    Base.isequal(m1.m, m2.m);
Base.isequal(m1 :: Modifier{T1}, m2 :: Modifier{T2}) where {T1, T2} =
    false;

Base.getindex(m :: Modifier{T1}, idx) where T1 = m.m[idx];

# ----------------


text:
#=
conjugateUpdates.jl

The function naming convention for this file is:
post_likelihood_unknownParams
=#

export post_norm_mean, post_norm_var, rpost_normlm_beta1, rpost_normlmGenCov_beta1,
       post_alphaDP,
       rpost_MvN_knownPrec, rpost_MvNprec_knownMean;

"""
    post_norm_mean(ȳ, n, σ2, μ0, v0)

  Returns conjugate normal posterior under independent normal likelihood
  with known variance and normal prior on the mean.

  Accepts the sample mean and returns a normal distribution.
"""
function post_norm_mean(ȳ::T, n::Int, σ2::T, μ0::T, v0::T) where T <: Real
  σ2 > 0.0 || throw(ArgumentError("σ must be positive."))
  v0 > 0.0 || throw(ArgumentError("v0 must be positive."))
  n > 0.0 || throw(ArgumentError("n must be positive."))

  # n = convert(Float, n)

  v1 = 1.0 / (1.0/v0 + n/σ2)
  sd1 = sqrt(v1)
  μ1 = v1 * (μ0/v0 + n*ȳ/σ2)

  Normal(μ1, sd1)
end

"""
    post_norm_var(ss, n, a0, b0)

  Returns conjugate inverse-gamma posterior for variance under
  independent normal likelihood with known mean and
  inverse-gamma prior on the variance.

  Accepts `ss```= ∑(y_i - μ_i)^2`` and sample size `n`.

  `a0` is the shape parameter of the inverse-gamma prior.
  `b0` is the scale parameter of the inverse-gamma prior.
"""
function post_norm_var(ss::T, n::T, a0::T, b0::T) where T <: Real
  a0 > 0.0 || throw(ArgumentError("a0 must be positive."))
  b0 > 0.0 || throw(ArgumentError("b0 must be positive."))

  # n = convert(Float, n)
  a1 = a0 + 0.5*n
  b1 = b0 + 0.5*ss

  InverseGamma(a1, b1)
end

"""
    rpost_normlm_beta1(y, X, σ2, τ2, β0=0.0)

  Returns draw from conjugate normal posterior for linear model
  coefficients under independent normal likelihood with
  (optionally) known intercept `β0` and observation variance `σ2`.

  Assumes independent normals prior with mean `0.0` and variance `τ2`.
"""
function rpost_normlm_beta1(y::Vector{T},
  X::Matrix{T},
  σ2::T, τ2::T, β0::T=0.0) where T <: Real
  # n > 1 and p > 1 case

  σ2 > 0.0 || throw(ArgumentError("σ2 must be positive."))
  τ2 > 0.0 || throw(ArgumentError("τ2 must be positive."))

  n,p = size(X) # assumes X was a matrix
  length(y) == n || throw(ArgumentError("y and X dimension mismatch"))
  ystar = y .- β0

  A = Matrix(1.0I, p, p) / τ2 + X'X / σ2
  U = (cholesky(A)).U
  Ut = transpose(U)

  # μ_a = At_ldiv_B(U, (X'ystar/σ2))
  μ_a = Ut \ (X'ystar/σ2)
  μ = U \ μ_a

  z = randn(p)
  β = U \ z + μ
end
function rpost_normlm_beta1(y::T, X::Vector{T},
  σ2::T, τ2::T, β0::T=0.0) where T <: Real
  # n = 1 and p > 1 case

  σ2 > 0.0 || throw(ArgumentError("σ2 must be positive."))
  τ2 > 0.0 || throw(ArgumentError("τ2 must be positive."))

  p = length(X)
  ystar = y .- β0

  A = Matrix(1.0I, p, p) / τ2 + (X * transpose(X)) / σ2
  U = (cholesky(A)).U
  Ut = transpose(U)

  # μ_a = At_ldiv_B(U, (ystar*X/σ2))
  μ_a = Ut \ (ystar*X/σ2)
  μ = U \ μ_a

  z = randn(p)
  β = U \ z + μ
end
function rpost_normlm_beta1(y::Vector{T},
  X::Vector{T},
  σ2::T, τ2::T, β0::T=0.0) where T <: Real
  # n > 1 and p = 1 case

  σ2 > 0.0 || throw(ArgumentError("σ2 must be positive."))
  τ2 > 0.0 || throw(ArgumentError("τ2 must be positive."))
  length(y) == length(X) || throw(ArgumentError("y and X dimension mismatch"))

  ystar = y .- β0

  A = 1.0 / τ2 + dot(X,X) / σ2
  μ = (X'ystar/σ2) / A

  z = randn(1)
  β = z / sqrt(A) .+ μ
end
function rpost_normlm_beta1(y::T, X::T,
  σ2::T, τ2::T, β0::T=0.0) where T <: Real
  # n = 1 and p = 1 case

  σ2 > 0.0 || throw(ArgumentError("σ2 must be positive."))
  τ2 > 0.0 || throw(ArgumentError("τ2 must be positive."))

  ystar = y .- β0

  A = 1.0 / τ2 + dot(X,X) / σ2
  μ = (X'ystar/σ2) / A

  z = randn(1)
  β = z / sqrt(A) .+ μ
end

function rpost_normlmGenCov_beta1(y::Vector{T}, X::Array{T,2}, Σ::TT,
                                  mβ::Vector{T}, # prior mean vector
                                  Vβdiag::Vector{T}) where {T <: Real, TT <: PDMat}

    all(Vβdiag .> 0.0) || thow("All varaince components must be positive.")

    Vβinv = Diagonal(1.0 ./ Vβdiag)
    Prec = Vβinv + Xt_invA_X(Σ, X)
    pot = Vβinv*mβ + whiten(Σ, X)'whiten(Σ, y)

    return rand( MvNormalCanon( pot, Prec) )
end
function rpost_normlmGenCov_beta1(y::Vector{T}, X::Array{T,2}, Σ::TT,
                                  Vβdiag::Vector{T}) where {T <: Real, TT <: PDMat}

    ## assumes zero vector for prior mean
    all(Vβdiag .> 0.0) || thow("All varaince components must be positive.")

    Vβinv = Diagonal(1.0 ./ Vβdiag)
    Prec = Vβinv + Xt_invA_X(Σ, X)
    pot =  whiten(Σ, X)'whiten(Σ, y)

    return rand( MvNormalCanon( pot, Prec) )
end

"""
    post_alphaDP(H::Int, lω_last::T, a_α::T, b_α::T)

    Returns posterior full conditional gamma distribution
    for the Dirichlet Process mass parameter `α`
    when using the truncated stick breaking representation.

    `H` is the the DP truncation level.
    `lω_last` is the Hth and last log weight.
    `a_α` is the shape of the gamma prior.
    `b_α` is the rate of the gamma prior.
"""
function post_alphaDP(H::Int, lω_last::T, a_α::T, b_α::T) where T <: Real
    a1 = a_α + H - 1.0
    b1 = b_α - lω_last # rate parameter
    Gamma(a1, 1.0/b1)
end

function rpost_MvN_knownPrec(Y::Array{T,2}, Λ::TT,
                             μ0::Array{T,1}, Λ0::TT) where {T <: Real, TT <: PDMat}
    n = float(T)(size(Y,1))
    ybar = vec(mean(Y, dims=1))
    return rpost_MvN_knownPrec(n, ybar, Λ, μ0, Λ0)
end
function rpost_MvN_knownPrec(n::T, ybar::Array{T,1}, Λ::TT,
                             μ0::Array{T,1}, Λ0::TT) where {T <: Real, TT <: PDMat}
    d = length(ybar)
    Λ1 = Λ0 + (n * Λ) # result is of type PDMat
    a = Λ0*μ0 + (n * Λ * ybar)
    μ1 = Λ1 \ a
    return μ1 + Λ.chol.U \ randn(d)
end

function rpost_MvNprec_knownMean(Y::Array{T,2}, μ::Array{T,1},
                                 df::T, invSc::TT) where {T <: Real, TT <: PDMat}
    n = size(Y,1)
    K = length(μ)
    SS = zeros(T, K, K)
    for i = 1:n
        dev = Y[i,:] - μ
        SS +=  dev * dev' # not necessarily pos. def.
    end
    return rpost_MvNprec_knownMean(float(T)(n), SS, df, invSc)
end
function rpost_MvNprec_knownMean(n::T, SS::Array{T,2}, df::T, invSc::TT) where {T <: Real, TT <: PDMat}
    df1 = df + n
    invSc1 = PDMat(invSc + SS) # result is of type PDMat
    Sc1 = inv(invSc1) # is there some way around this?
    return rand(Distributions.Wishart(df1, Sc1))
end


text:
function workspace_didChangeWatchedFiles_notification(params::DidChangeWatchedFilesParams, server::LanguageServerInstance, conn)
    for change in params.changes
        uri = change.uri

        startswith(uri, "file:") || continue

        if change.type == FileChangeTypes.Created || change.type == FileChangeTypes.Changed
            if hasdocument(server, URI2(uri))
                doc = getdocument(server, URI2(uri))

                # Currently managed by the client, we don't do anything
                if get_open_in_editor(doc)
                    continue
                else
                    filepath = uri2filepath(uri)
                    content = try
                        s = read(filepath, String)
                        if !isvalid(s)
                            deletedocument!(server, URI2(uri))
                            continue
                        end
                        s
                    catch err
                        isa(err, Base.IOError) || isa(err, Base.SystemError) || rethrow()
                        deletedocument!(server, URI2(uri))
                        continue
                    end

                    set_text!(doc, content)
                    set_is_workspace_file(doc, true)
                    parse_all(doc, server)
                end
            else
                filepath = uri2filepath(uri)
                content = try
                    s = read(filepath, String)
                    isvalid(s) || continue
                    s
                catch err
                    isa(err, Base.IOError) || isa(err, Base.SystemError) || rethrow()
                    continue
                end

                doc = Document(uri, content, true, server)
                setdocument!(server, URI2(uri), doc)
                parse_all(doc, server)
            end
        elseif change.type == FileChangeTypes.Deleted
            if hasdocument(server, URI2(uri))
                doc = getdocument(server, URI2(uri))

                # We only handle if currently not managed by client
                if !get_open_in_editor(doc)
                    deletedocument!(server, URI2(uri))

                    publishDiagnosticsParams = PublishDiagnosticsParams(uri, missing, Diagnostic[])
                    JSONRPC.send(conn, textDocument_publishDiagnostics_notification_type, publishDiagnosticsParams)
                else
                    # TODO replace with accessor function once the other PR
                    # that introduces the accessor is merged
                    doc._workspace_file = false
                end
            end
        else
            error("Unknown change type.")
        end
    end
end

function workspace_didChangeConfiguration_notification(params::DidChangeConfigurationParams, server::LanguageServerInstance, conn)
    request_julia_config(server, conn)
end

@static if VERSION < v"1.1"
    isnothing(::Any) = false
    isnothing(::Nothing) = true
end

const LINT_DIABLED_DIRS = ["test", "docs"]

function request_julia_config(server::LanguageServerInstance, conn)
    (ismissing(server.clientCapabilities.workspace) || server.clientCapabilities.workspace.configuration !== true) && return

    response = JSONRPC.send(conn, workspace_configuration_request_type, ConfigurationParams([
        ConfigurationItem(missing, "julia.format.indent"), # FormatOptions
        ConfigurationItem(missing, "julia.format.indents"),
        ConfigurationItem(missing, "julia.format.ops"),
        ConfigurationItem(missing, "julia.format.tuples"),
        ConfigurationItem(missing, "julia.format.curly"),
        ConfigurationItem(missing, "julia.format.calls"),
        ConfigurationItem(missing, "julia.format.iterOps"),
        ConfigurationItem(missing, "julia.format.comments"),
        ConfigurationItem(missing, "julia.format.docs"),
        ConfigurationItem(missing, "julia.format.lineends"),
        ConfigurationItem(missing, "julia.format.keywords"),
        ConfigurationItem(missing, "julia.format.kwarg"),
        ConfigurationItem(missing, "julia.lint.call"), # LintOptions
        ConfigurationItem(missing, "julia.lint.iter"),
        ConfigurationItem(missing, "julia.lint.nothingcomp"),
        ConfigurationItem(missing, "julia.lint.constif"),
        ConfigurationItem(missing, "julia.lint.lazy"),
        ConfigurationItem(missing, "julia.lint.datadecl"),
        ConfigurationItem(missing, "julia.lint.typeparam"),
        ConfigurationItem(missing, "julia.lint.modname"),
        ConfigurationItem(missing, "julia.lint.pirates"),
        ConfigurationItem(missing, "julia.lint.useoffuncargs"),
        ConfigurationItem(missing, "julia.lint.run"),
        ConfigurationItem(missing, "julia.lint.missingrefs"),
        ConfigurationItem(missing, "julia.lint.disabledDirs"),
        ConfigurationItem(missing, "julia.completionmode")
        ]))

    server.format_options = DocumentFormat.FormatOptions(response[1:12]...)
    new_runlinter = something(response[23], true)
    new_SL_opts = StaticLint.LintOptions(response[13:22]...)

    new_lint_missingrefs = Symbol(something(response[24], :all))
    new_lint_disableddirs = something(response[25], LINT_DIABLED_DIRS)
    new_completion_mode = Symbol(something(response[26], :import))

    rerun_lint = begin
        any(getproperty(server.lint_options, opt) != getproperty(new_SL_opts, opt) for opt in fieldnames(StaticLint.LintOptions)) ||
        server.runlinter != new_runlinter ||
        server.lint_missingrefs != new_lint_missingrefs ||
        server.lint_disableddirs != new_lint_disableddirs
    end

    server.lint_options = new_SL_opts
    server.runlinter = new_runlinter
    server.lint_missingrefs = new_lint_missingrefs
    server.lint_disableddirs = new_lint_disableddirs
    server.completion_mode = new_completion_mode

    if rerun_lint
        relintserver(server)
    end
end

function workspace_didChangeWorkspaceFolders_notification(params::DidChangeWorkspaceFoldersParams, server::LanguageServerInstance, conn)
    for wksp in params.event.added
        push!(server.workspaceFolders, uri2filepath(wksp.uri))
        load_folder(wksp, server)
    end
    for wksp in params.event.removed
        delete!(server.workspaceFolders, uri2filepath(wksp.uri))
        remove_workspace_files(wksp, server)
    end
end

function workspace_symbol_request(params::WorkspaceSymbolParams, server::LanguageServerInstance, conn)
    syms = SymbolInformation[]
    for doc in getdocuments_value(server)
        bs = collect_toplevel_bindings_w_loc(getcst(doc), query=params.query)
        for x in bs
            p, b = x[1], x[2]
            push!(syms, SymbolInformation(valof(b.name), 1, false, Location(doc._uri, Range(doc, p)), missing))
        end
    end

    return syms
end


text:
abstract type AbstractRenewableFormulation <: AbstractDeviceFormulation end

abstract type AbstractRenewableDispatchForm <: AbstractRenewableFormulation end

struct RenewableFixed <: AbstractRenewableFormulation end

struct RenewableFullDispatch <: AbstractRenewableDispatchForm end

struct RenewableConstantPowerFactor <: AbstractRenewableDispatchForm end

# renewable variables

function activepower_variables(ps_m::CanonicalModel, devices::Array{R,1}, time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen}

    add_variable(ps_m,
                 devices,
                 time_range,
                 :Pre,
                 false,
                 :var_active)

    return

end

function reactivepower_variables(ps_m::CanonicalModel, devices::Array{R,1}, time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen}

    add_variable(ps_m,
                 devices,
                 time_range,
                 :Qre,
                 false,
                 :var_reactive)

    return

end

###### output constraints without Time Series ##############

function activepower_constraints(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                device_formulation::Type{D},
                                system_formulation::Type{S},
                                parameters::Bool) where {R <: PSY.RenewableGen,
                                                                    D <: AbstractRenewableDispatchForm,
                                                                    S <: PM.AbstractPowerFormulation}

    if parameters
        device_timeseries_param_ub(ps_m,
                                    _get_time_series(devices),
                                    time_range,
                                    :renewable_active_ub,
                                    Symbol("Pre_$(eltype(devices))"),
                                    :Pre)
    else
        device_timeseries_ub(ps_m,
                            _get_time_series(devices),
                            time_range,
                            :renewable_active_ub,
                            :Pre)
    end

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                device_formulation::Type{RenewableFullDispatch},
                                system_formulation::Type{S}) where {R <: PSY.RenewableGen,
                                                                    S <: PM.AbstractPowerFormulation}

    range_data = [(r.name, r.tech.reactivepowerlimits) for r in devices]

    device_range(ps_m,
                range_data ,
                time_range,
                :renewable_reactive_range,
                :Qre)

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                device_formulation::Type{RenewableConstantPowerFactor},
                                system_formulation::Type{S}) where {R <: PSY.RenewableGen,
                                                                    S <: PM.AbstractPowerFormulation}

    names = [r.name for r in devices]

    ps_m.constraints[:renewable_reactive] = JuMP.Containers.DenseAxisArray{JuMP.ConstraintRef}(undef, names, time_range)

    for t in time_range, d in devices
        ps_m.constraints[:renewable_reactive][d.name, t] = JuMP.@constraint(ps_m.JuMPmodel, ps_m.variables[:Qre][d.name, t] == ps_m.variables[:Pre][d.name, t]*sin(acos(d.tech.powerfactor)))
    end

    return

end

###### output constraints with Time Series ##############

function _get_time_series(devices::Array{T}) where {T <: PSY.RenewableGen}

    names = Vector{String}(undef, length(devices))
    series = Vector{Vector{Float64}}(undef, length(devices))

    for (ix,d) in enumerate(devices)
        names[ix] = d.name
        series[ix] = values(d.scalingfactor)*d.tech.installedcapacity
    end

    return names, series

end

function activepower_constraints(ps_m::CanonicalModel,
                                 devices::Array{R,1},
                                 device_formulation::Type{D},
                                 system_formulation::Type{S},
                                 time_range::UnitRange{Int64},
                                 parameters::Bool) where {R <: PSY.RenewableGen,
                                                                      D <: AbstractRenewableDispatchForm,
                                                                      S <: PM.AbstractPowerFormulation}

    if parameters
        device_timeseries_param_ub(ps_m,
                                   _get_time_series(devices),
                                   time_range,
                                   :renewable_active_ub,
                                   Symbol("Pre_$(eltype(devices))"),
                                   :Pre)
    else
        device_timeseries_ub(ps_m,
                            _get_time_series(devices),
                            time_range,
                            :renewable_active_ub,
                            :Pre)
    end

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                   devices::Array{R,1},
                                   device_formulation::Type{RenewableFullDispatch},
                                   system_formulation::Type{S},
                                   time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                        S <: PM.AbstractPowerFormulation}

    range_data = [(r.name, r.tech.reactivepowerlimits) for r in devices]

    device_range(ps_m,
                range_data ,
                time_range,
                :renewable_reactive_range,
                :Qre)

    return

end

function reactivepower_constraints(ps_m::CanonicalModel,
                                   devices::Array{R,1},
                                   device_formulation::Type{RenewableConstantPowerFactor},
                                   system_formulation::Type{S},
                                   time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                        S <: PM.AbstractPowerFormulation}

    names = [r.name for r in devices]

    ps_m.constraints[:renewable_reactive] = JuMP.Containers.DenseAxisArray{JuMP.ConstraintRef}(undef, names, time_range)

    for t in time_range, d in devices
        ps_m.constraints[:renewable_reactive][d.name, t] = JuMP.@constraint(ps_m.JuMPmodel, ps_m.variables[:Qre][d.name, t] == ps_m.variables[:Pre][d.name, t]*sin(acos(d.tech.powerfactor)))
    end

    return

end

##### Injection Expression with parameters #####

function _nodal_expression_param(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractPowerFormulation}

    ts_data_active = Vector{Tuple{String,Int64,Vector{Float64}}}(undef, length(devices))
    ts_data_reactive = Vector{Tuple{String,Int64,Vector{Float64}}}(undef, length(devices))

    for (ix,d) in enumerate(devices)
        ts_data_active[ix] = (d.name, d.bus.number, values(d.scalingfactor)*d.tech.installedcapacity)
        ts_data_reactive[ix] = (d.name, d.bus.number, values(d.scalingfactor)*d.tech.installedcapacity)
    end

    add_parameters(ps_m,
                   ts_data_active,
                   time_range,
                   Symbol("Pre_$(eltype(devices))"),
                   :var_active)
    add_parameters(ps_m,
                   ts_data_reactive,
                   time_range,
                   Symbol("Qre_$(eltype(devices))"),
                   :var_reactive)

    return

end

function _nodal_expression_param(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractActivePowerFormulation}

    ts_data_active = Vector{Tuple{String,Int64,Vector{Float64}}}(undef, length(devices))

    for (ix,d) in enumerate(devices)
        ts_data_active[ix] = (d.name, d.bus.number, values(d.scalingfactor)*d.tech.installedcapacity)
    end

    add_parameters(ps_m,
                   ts_data_active,
                   time_range,
                   Symbol("Pre_$(eltype(devices))"),
                   :var_active)
    return

end

###### injection expression with fixed values #######

function _nodal_expression_fixed(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractPowerFormulation}

    for t in time_range, d in devices
        _add_to_expression!(ps_m.expressions[:var_active],
                            d.bus.number,
                            t,
                            d.tech.installedcapacity * values(d.scalingfactor)[t])
        _add_to_expression!(ps_m.expressions[:var_reactive],
                            d.bus.number,
                            t,
                            d.tech.installedcapacity * values(d.scalingfactor)[t]*sin(acos(d.tech.powerfactor)))
    end

    return

end

function _nodal_expression_fixed(ps_m::CanonicalModel,
                                devices::Array{R,1},
                                system_formulation::Type{S},
                                time_range::UnitRange{Int64}) where {R <: PSY.RenewableGen,
                                                                     S <: PM.AbstractActivePowerFormulation}

    for t in time_range, d in devices
        _add_to_expression!(ps_m.expressions[:var_active],
                            d.bus.number,
                            t,
                            d.tech.installedcapacity * values(d.scalingfactor)[t])
    end

    return

end

function nodal_expression(ps_m::CanonicalModel,
                          devices::Array{R,1},
                          system_formulation::Type{S},
                          time_range::UnitRange{Int64},
                          parameters::Bool) where {R <: PSY.RenewableGen,
                                                          S <: PM.AbstractPowerFormulation}

    if parameters
        _nodal_expression_param(ps_m, devices, system_formulation, time_range)
    else
        _nodal_expression_fixed(ps_m, devices, system_formulation, time_range)
    end

    return

end

######## renewable generation cost ##############

function cost_function(ps_m::CanonicalModel,
                       devices::Array{PSY.RenewableCurtailment,1},
                       device_formulation::Type{D},
                       system_formulation::Type{S}) where {D <: AbstractRenewableDispatchForm,
                                                           S <: PM.AbstractPowerFormulation}

    add_to_cost(ps_m, devices, :Pre, :curtailpenalty, -1)

    return

end


text:
# Contents: Utility functions for SecureSessions.jl

"Cryptographically secure RNG"
function csrng(numbytes::Integer)
    entropy = MbedTLS.Entropy()
    rng     = MbedTLS.CtrDrbg()
    MbedTLS.seed!(rng, entropy)
    rand(rng, numbytes)
end

"""
Returns true if username adheres to the following formatting rules:
1) username contains none of the following tags:
       "<script>", "<link>", "<img>", "<iframe>", "<object>"
"""
function username_is_permissible(username)
    result = true
    bad_tags = ["<script>", "<link>", "<img>", "<iframe>", "<object>"]
    for tag in bad_tags
	if contains(username, tag)
	     result = false
	     break
	end
    end
    result
end

"""
Returns true is password adheres to the following formatting rules:
  R1) Length at least 8 characters
  R2) At least 5 unique characters
  R3) At least 1 upper case letter
  R4) At least 1 lower case letter
  R5) At least 1 number
  R6) At least 1 special character
"""
function password_is_permissible(password)
    # Determine the truth values of rules 1 to 6.
    uniq = unique(password)
    n    = length(uniq)
    r1   = length(password) >= 8
    r2   = n >= 5
    r3   = false
    r4   = false
    r5   = false
    r6   = false
    for i = 1:n
	c     = Int(uniq[i])    # Integer representation of Char
	if c >= 65 && c <= 90
	    r3 = true
	elseif c >= 97 && c <= 122
	    r4 = true
	elseif c >= 48 && c <= 57
	    r5 = true
	else
	    r6 = true
	end
    end

    # Calculate result
    result = false
    if r1 && r2 && r3 && r4 && r5 && r6
	result = true
    end
    result
end

"""
Returns: Milliseconds since the epoch.

Takes 13 characters (valid until some time around the year 2287).
"""
function get_timestamp()
    Int(round(1000*Dates.datetime2unix(now())))
end

"""
Convert number to byte array.
The implementation is a modified version of that found in Stack Overflow question 3076680.
"""
function num_to_bytearray(x)
    io = IOBuffer()
    write(io, x)
    seekstart(io)
    read(io)
end

### EOF


text:
save_idxsinitialize(integrator,cache::OrdinaryDiffEqCache,::Type{uType}) where {uType} =
               error("This algorithm does not have an initialization function")

function loopheader!(integrator)
  # Apply right after iterators / callbacks

  # Accept or reject the step
  if integrator.iter > 0
    if ((integrator.opts.adaptive && integrator.accept_step) || !integrator.opts.adaptive) && !integrator.force_stepfail
      integrator.success_iter += 1
      apply_step!(integrator)
    elseif integrator.opts.adaptive && !integrator.accept_step
      if integrator.isout
        integrator.dt = integrator.dt*integrator.opts.qmin
      elseif !integrator.force_stepfail
        step_reject_controller!(integrator,integrator.alg)
      end
    end
  end

  integrator.iter += 1
  choose_algorithm!(integrator,integrator.cache)
  fix_dt_at_bounds!(integrator)
  modify_dt_for_tstops!(integrator)
  integrator.force_stepfail = false
end

last_step_failed(integrator::ODEIntegrator) =
  integrator.last_stepfail && !integrator.opts.adaptive

function modify_dt_for_tstops!(integrator)
  tstops = integrator.opts.tstops
  if !isempty(tstops)
    if integrator.opts.adaptive
      if integrator.tdir > 0
        integrator.dt = min(abs(integrator.dt), abs(top(tstops) - integrator.t)) # step! to the end
      else
        integrator.dt = - min(abs(integrator.dt), abs(top(tstops) + integrator.t))
      end
    elseif iszero(integrator.dtcache) && integrator.dtchangeable
      # Use integrator.opts.tstops
      integrator.dt = integrator.tdir * abs(top(tstops) - integrator.tdir * integrator.t)
  elseif integrator.dtchangeable && !integrator.force_stepfail
      # always try to step! with dtcache, but lower if a tstops
      # however, if force_stepfail then don't set to dtcache, and no tstop worry
      integrator.dt = integrator.tdir * min(abs(integrator.dtcache), abs(top(tstops) - integrator.tdir * integrator.t)) # step! to the end
    end
  end
end

# Want to extend savevalues! for DDEIntegrator
savevalues!(integrator::ODEIntegrator, force_save = false, reduce_size = true) =
  _savevalues!(integrator, force_save, reduce_size)

function _savevalues!(integrator, force_save, reduce_size)::Tuple{Bool,Bool}
  saved, savedexactly = false, false
  !integrator.opts.save_on && return saved, savedexactly
  tdir_t = integrator.tdir * integrator.t
  while !isempty(integrator.opts.saveat) && top(integrator.opts.saveat) <= tdir_t # Perform saveat
    integrator.saveiter += 1; saved = true
    curt = integrator.tdir * pop!(integrator.opts.saveat)
    if curt!=integrator.t # If <t, interpolate
      DiffEqBase.addsteps!(integrator)
      Θ = (curt - integrator.tprev)/integrator.dt
      val = ode_interpolant(Θ,integrator,integrator.opts.save_idxs,Val{0}) # out of place, but no force copy later
      copyat_or_push!(integrator.sol.t,integrator.saveiter,curt)
      save_val = val
      copyat_or_push!(integrator.sol.u,integrator.saveiter,save_val,Val{false})
      if typeof(integrator.alg) <: OrdinaryDiffEqCompositeAlgorithm
        copyat_or_push!(integrator.sol.alg_choice,integrator.saveiter,integrator.cache.current)
      end
    else # ==t, just save
      savedexactly = true
      copyat_or_push!(integrator.sol.t,integrator.saveiter,integrator.t)
      if integrator.opts.save_idxs === nothing
        copyat_or_push!(integrator.sol.u,integrator.saveiter,integrator.u)
      else
        copyat_or_push!(integrator.sol.u,integrator.saveiter,integrator.u[integrator.opts.save_idxs],Val{false})
      end
      if typeof(integrator.alg) <: FunctionMap || integrator.opts.dense
        integrator.saveiter_dense +=1
        if integrator.opts.dense
          if integrator.opts.save_idxs === nothing
            copyat_or_push!(integrator.sol.k,integrator.saveiter_dense,integrator.k)
          else
            copyat_or_push!(integrator.sol.k,integrator.saveiter_dense,[k[integrator.opts.save_idxs] for k in integrator.k],Val{false})
          end
        end
      end
      if typeof(integrator.alg) <: OrdinaryDiffEqCompositeAlgorithm
        copyat_or_push!(integrator.sol.alg_choice,integrator.saveiter,integrator.cache.current)
      end
    end
  end
  if force_save || integrator.opts.save_everystep
    integrator.saveiter += 1; saved, savedexactly = true, true
    if integrator.opts.save_idxs === nothing
      copyat_or_push!(integrator.sol.u,integrator.saveiter,integrator.u)
    else
      copyat_or_push!(integrator.sol.u,integrator.saveiter,integrator.u[integrator.opts.save_idxs],Val{false})
    end
    copyat_or_push!(integrator.sol.t,integrator.saveiter,integrator.t)
    if typeof(integrator.alg) <: FunctionMap || integrator.opts.dense
      integrator.saveiter_dense +=1
      if integrator.opts.dense
        if integrator.opts.save_idxs === nothing
          copyat_or_push!(integrator.sol.k,integrator.saveiter_dense,integrator.k)
        else
          copyat_or_push!(integrator.sol.k,integrator.saveiter_dense,[k[integrator.opts.save_idxs] for k in integrator.k],Val{false})
        end
      end
    end
    if typeof(integrator.alg) <: OrdinaryDiffEqCompositeAlgorithm
      copyat_or_push!(integrator.sol.alg_choice,integrator.saveiter,integrator.cache.current)
    end
  end
  reduce_size && resize!(integrator.k,integrator.kshortsize)
  return saved, savedexactly
end

# Want to extend postamble! for DDEIntegrator
postamble!(integrator::ODEIntegrator) = _postamble!(integrator)

function _postamble!(integrator)
  solution_endpoint_match_cur_integrator!(integrator)
  resize!(integrator.sol.t,integrator.saveiter)
  resize!(integrator.sol.u,integrator.saveiter)
  if !(integrator.sol isa DAESolution)
    resize!(integrator.sol.k,integrator.saveiter_dense)
  end
  if integrator.opts.progress
    @logmsg(LogLevel(-1),
    integrator.opts.progress_name,
    _id = :OrdinaryDiffEq,
    message=integrator.opts.progress_message(integrator.dt,integrator.u,integrator.p,integrator.t),
    progress="done")
  end
end

function solution_endpoint_match_cur_integrator!(integrator)
  if integrator.opts.save_end && (integrator.saveiter == 0 || integrator.sol.t[integrator.saveiter] !=  integrator.t)
    integrator.saveiter += 1
    copyat_or_push!(integrator.sol.t,integrator.saveiter,integrator.t)
    if integrator.opts.save_idxs === nothing
      copyat_or_push!(integrator.sol.u,integrator.saveiter,integrator.u)
    else
      copyat_or_push!(integrator.sol.u,integrator.saveiter,integrator.u[integrator.opts.save_idxs],Val{false})
    end
    if typeof(integrator.alg) <: FunctionMap || integrator.opts.dense
      integrator.saveiter_dense +=1
      if integrator.opts.dense
        if integrator.opts.save_idxs === nothing
          copyat_or_push!(integrator.sol.k,integrator.saveiter_dense,integrator.k)
        else
          copyat_or_push!(integrator.sol.k,integrator.saveiter_dense,[k[integrator.opts.save_idxs] for k in integrator.k],Val{false})
        end
      end
    end
    if typeof(integrator.alg) <: OrdinaryDiffEqCompositeAlgorithm
      copyat_or_push!(integrator.sol.alg_choice,integrator.saveiter,integrator.cache.current)
    end
  end
end

# Want to extend loopfooter! for DDEIntegrator
loopfooter!(integrator::ODEIntegrator) = _loopfooter!(integrator)

function _loopfooter!(integrator)

  # Carry-over from callback
  # This is set to true if u_modified requires callback FSAL reset
  # But not set to false when reset so algorithms can check if reset occurred
  integrator.reeval_fsal = false
  integrator.u_modified = false
  ttmp = integrator.t + integrator.dt
  if integrator.force_stepfail
      if integrator.opts.adaptive
        integrator.dt = integrator.dt/integrator.opts.failfactor
      elseif integrator.last_stepfail
        return
      end
      integrator.last_stepfail = true
      integrator.accept_step = false
  elseif integrator.opts.adaptive
    q = stepsize_controller!(integrator,integrator.alg)
    integrator.isout = integrator.opts.isoutofdomain(integrator.u,integrator.p,ttmp)
    integrator.accept_step = (!integrator.isout && integrator.EEst <= 1.0) || (integrator.opts.force_dtmin && abs(integrator.dt) <= timedepentdtmin(integrator))
    if integrator.accept_step # Accept
      integrator.destats.naccept += 1
      integrator.last_stepfail = false
      dtnew = DiffEqBase.value(step_accept_controller!(integrator,integrator.alg,q)) * oneunit(integrator.dt)
      integrator.tprev = integrator.t
      # integrator.EEst has unitless type of integrator.t
      if typeof(integrator.EEst)<: AbstractFloat && !isempty(integrator.opts.tstops)
        tstop = integrator.tdir * top(integrator.opts.tstops)
        abs(ttmp - tstop) < 10eps(max(integrator.t,tstop)/oneunit(integrator.t))*oneunit(integrator.t) ?
                                  (integrator.t = tstop) : (integrator.t = ttmp)
      else
        integrator.t = ttmp
      end
      calc_dt_propose!(integrator,dtnew)
      handle_callbacks!(integrator)
    else # Reject
      integrator.destats.nreject += 1
    end
  elseif !integrator.opts.adaptive #Not adaptive
    integrator.destats.naccept += 1
    integrator.tprev = integrator.t
    # integrator.EEst has unitless type of integrator.t
    if typeof(integrator.EEst)<: AbstractFloat && !isempty(integrator.opts.tstops)
      tstop = integrator.tdir * top(integrator.opts.tstops)
      abs(ttmp - tstop) < 10eps(integrator.t/oneunit(integrator.t))*oneunit(integrator.t) ?
                                  (integrator.t = tstop) : (integrator.t = ttmp)
    else
      integrator.t = ttmp
    end
    integrator.last_stepfail = false
    integrator.accept_step = true
    integrator.dtpropose = integrator.dt
    handle_callbacks!(integrator)
  end
  if integrator.opts.progress && integrator.iter%integrator.opts.progress_steps==0
    @logmsg(LogLevel(-1),
    integrator.opts.progress_name,
    _id = :OrdinaryDiffEq,
    message=integrator.opts.progress_message(integrator.dt,integrator.u,integrator.p,integrator.t),
    progress=integrator.t/integrator.sol.prob.tspan[2])
  end

  # Take value because if t is dual then maxeig can be dual
  if integrator.cache isa CompositeCache
    cur_eigen_est = integrator.opts.internalnorm(DiffEqBase.value(integrator.eigen_est),integrator.t)
    cur_eigen_est > integrator.destats.maxeig && (integrator.destats.maxeig = cur_eigen_est)
  end
  nothing
end

function handle_callbacks!(integrator)
  discrete_callbacks = integrator.opts.callback.discrete_callbacks
  continuous_callbacks = integrator.opts.callback.continuous_callbacks
  atleast_one_callback = false

  continuous_modified = false
  discrete_modified = false
  saved_in_cb = false
  if !(typeof(continuous_callbacks)<:Tuple{})
    time,upcrossing,event_occurred,event_idx,idx,counter =
              DiffEqBase.find_first_continuous_callback(integrator,continuous_callbacks...)
    if event_occurred
      integrator.event_last_time = idx
      integrator.vector_event_last_time = event_idx
      continuous_modified,saved_in_cb = DiffEqBase.apply_callback!(integrator,continuous_callbacks[idx],time,upcrossing,event_idx)
    else
      integrator.event_last_time = 0
      integrator.vector_event_last_time = 1
    end
  end
  if !integrator.force_stepfail && !(typeof(discrete_callbacks)<:Tuple{})
    discrete_modified,saved_in_cb = DiffEqBase.apply_discrete_callback!(integrator,discrete_callbacks...)
  end
  if !saved_in_cb
    savevalues!(integrator)
  end

  integrator.u_modified = continuous_modified || discrete_modified
  if integrator.u_modified
    handle_callback_modifiers!(integrator)
  end
end

function handle_callback_modifiers!(integrator::ODEIntegrator)
  integrator.reeval_fsal = true
end

function apply_step!(integrator)

  integrator.accept_step = false # yay we got here, don't need this no more

  #Update uprev
  if alg_extrapolates(integrator.alg)
    if isinplace(integrator.sol.prob)
      recursivecopy!(integrator.uprev2,integrator.uprev)
    else
      integrator.uprev2 = integrator.uprev
    end
  end
  if isinplace(integrator.sol.prob)
    recursivecopy!(integrator.uprev,integrator.u)
  else
    integrator.uprev = integrator.u
  end

  #Update dt if adaptive or if fixed and the dt is allowed to change
  if integrator.opts.adaptive || integrator.dtchangeable
    integrator.dt = integrator.dtpropose
  elseif integrator.dt != integrator.dtpropose && !integrator.dtchangeable
    error("The current setup does not allow for changing dt.")
  end

  # Update fsal if needed
  if !isempty(integrator.opts.d_discontinuities) &&
      top(integrator.opts.d_discontinuities) == integrator.t

      handle_discontinuities!(integrator)
      get_current_isfsal(integrator.alg, integrator.cache) && reset_fsal!(integrator)
  elseif get_current_isfsal(integrator.alg, integrator.cache)
    if integrator.reeval_fsal || integrator.u_modified || (typeof(integrator.alg)<:DP8 && !integrator.opts.calck) || (typeof(integrator.alg)<:Union{Rosenbrock23,Rosenbrock32} && !integrator.opts.adaptive)
        reset_fsal!(integrator)
    else # Do not reeval_fsal, instead copyto! over
      if isinplace(integrator.sol.prob)
        recursivecopy!(integrator.fsalfirst,integrator.fsallast)
      else
        integrator.fsalfirst = integrator.fsallast
      end
    end
  end
end

function handle_discontinuities!(integrator)
    pop!(integrator.opts.d_discontinuities)
end

function calc_dt_propose!(integrator,dtnew)
  if (typeof(integrator.alg) <: Union{ROCK2,ROCK4,SERK2,ESERK4,ESERK5}) && integrator.opts.adaptive && (integrator.iter >= 1)
    (integrator.alg isa ROCK2) && (dtnew = min(dtnew,typeof(dtnew)((((min(integrator.alg.max_stages,200)^2.0)*.811 - 1.5)/integrator.eigen_est))))
    (integrator.alg isa ROCK4) && (dtnew = min(dtnew,typeof(dtnew)((((min(integrator.alg.max_stages,152)^2.0)*.353 - 3)/integrator.eigen_est))))
    (integrator.alg isa SERK2) && (dtnew = min(dtnew,typeof(dtnew)((0.8*250*250/(integrator.eigen_est+1.0)))))
    (integrator.alg isa ESERK4) && (dtnew = min(dtnew,typeof(dtnew)((0.98*4000*4000/integrator.eigen_est))))
    (integrator.alg isa ESERK5) && (dtnew = min(dtnew,typeof(dtnew)((0.98*2000*2000/integrator.eigen_est))))
  end
  dtpropose = integrator.tdir*min(abs(integrator.opts.dtmax),abs(dtnew))
  dtpropose = integrator.tdir*max(abs(dtpropose),timedepentdtmin(integrator))
  integrator.dtpropose = dtpropose
end

function fix_dt_at_bounds!(integrator)
  if integrator.tdir > 0
    integrator.dt = min(integrator.opts.dtmax,integrator.dt)
  else
    integrator.dt = max(integrator.opts.dtmax,integrator.dt)
  end
  dtmin = timedepentdtmin(integrator)
  if integrator.tdir > 0
    integrator.dt = max(integrator.dt,dtmin) #abs to fix complex sqrt issue at end
  else
    integrator.dt = min(integrator.dt,dtmin) #abs to fix complex sqrt issue at end
  end
end

function handle_tstop!(integrator)
  tstops = integrator.opts.tstops
  if !isempty(tstops)
    tdir_t = integrator.tdir * integrator.t
    tdir_ts_top = top(tstops)
    if tdir_t == tdir_ts_top
      while tdir_t == tdir_ts_top #remove all redundant copies in heap == tdir_t
        pop!(tstops)
        isempty(tstops) ? break : tdir_ts_top = top(tstops)
      end
      integrator.just_hit_tstop = true
    elseif tdir_t > tdir_ts_top
      if !integrator.dtchangeable
        DiffEqBase.change_t_via_interpolation!(integrator, integrator.tdir * pop!(tstops), Val{true})
        integrator.just_hit_tstop = true
      else
        error("Something went wrong. Integrator stepped past tstops but the algorithm was dtchangeable. Please report this error.")
      end
    end
  end
end

function reset_fsal!(integrator)
  # Under these condtions, these algorithms are not FSAL anymore
  integrator.destats.nf += 1
  if typeof(integrator.cache) <: OrdinaryDiffEqMutableCache ||
     (typeof(integrator.cache) <: CompositeCache &&
      typeof(integrator.cache.caches[1]) <: OrdinaryDiffEqMutableCache)
    integrator.f(integrator.fsalfirst,integrator.u,integrator.p,integrator.t)
  else
    integrator.fsalfirst = integrator.f(integrator.u,integrator.p,integrator.t)
  end
  # Do not set false here so it can be checked in the algorithm
  # integrator.reeval_fsal = false
end

DiffEqBase.nlsolve_f(f, alg::OrdinaryDiffEqAlgorithm) = f isa SplitFunction && issplit(alg) ? f.f1 : f
DiffEqBase.nlsolve_f(f, alg::DAEAlgorithm) = f
DiffEqBase.nlsolve_f(integrator::ODEIntegrator) =
  nlsolve_f(integrator.f, unwrap_alg(integrator, true))

function (integrator::ODEIntegrator)(t,deriv::Type=Val{0};idxs=nothing)
  current_interpolant(t,integrator,idxs,deriv)
end

(integrator::ODEIntegrator)(val::AbstractArray,t::Union{Number,AbstractArray},deriv::Type=Val{0};idxs=nothing) = current_interpolant!(val,t,integrator,idxs,deriv)


text:
using Test
using DynamicalBilliards
using DynamicalBilliards.Testing

function coordinate_invariance(p, bd, args...)
    @testset "$(tag(p, bd)) Coordinate change" begin
        for t in 1:2e4
            i, tmin = bounce!(p, bd)
            ξ, sφ = to_bcoords(p, bd[i])
            pos, vel = from_bcoords(ξ, sφ, bd[i])
            @test *(isapprox.(p.pos, pos, atol=1e-4)...)
            @test *(isapprox.(p.vel, vel, atol=1e-4)...)
        end
    end
end

billiards_testset("Coordinate change invariance", coordinate_invariance; caller = omni_tests_noellipse)

function cut_psos(args...)
    t = 1000
    bt = billiard_sinai(0.25)
    plane = InfiniteWall([0.5, 0.0], [0.5, 1.0], [-1.0, 0.0])
    @testset "PSOS: pinned particle" begin

        # case of pinned that crosses plane but not walls
        p = MagneticParticle(0.2, 0.5, -π/2, 1/0.3)
        a, b = psos(bt, plane, t, p)
        @test typeof(a) <: Vector{<:SVector}
        @test length(a) == length(b) == 1

        # case of pinned that doesn't cross anything
        p = MagneticParticle(0.1, 0.5, -π/2, 1/0.05)
        a, b = psos(bt,plane, t, p)
        @test typeof(a) <: Vector{<:SVector}
        @test length(a) == length(b) == 0

        # case of pinned but does cross periodic walls *and* section
        bd = billiard_sinai(;setting = "periodic")
        p = MagneticParticle(0.25, 0.25, -π/2 + π/4, 0.44*2)
        a, b = psos(bd, plane, t, p)
        @test typeof(a) <: Vector{<:SVector}
        @test length(a) == length(b) == 1
    end

    for ω ∈ [0.0, 1.0]
        p = ω == 0 ? randominside(bt) : randominside(bt, ω)
        @testset "$(tag(p, bt)) psos" begin
            a, b = psos(bt, plane, t, p)
            for j in 1:length(a)
                @test a[j][1] ≈ 0.5
                @test 0 < a[j][2] < 1
                @test -1 < b[j][1] < 1
                @test -1 < b[j][2] < 1
            end
        end
    end

    @testset "many particles" begin
        a, b = psos(bt, plane, 1000, 10)
        for i in 1:length(a)
            @test length(a[i]) == length(b[i])
        end
    end
end

billiards_testset("PSOS", identity; caller = cut_psos)

function fills_boundarymap(p, bd)
    @testset "$(tag(p, bd)) Fills boundary map" begin
        bmap, = boundarymap(p, bd, 10000)
        ξs = [b[1] for b in bmap]; sφs = [b[2] for b in bmap]
        partition = (10,10) # partition size
        A = falses(partition)
        l = totallength(bd)
        ε = (l/partition[1], 2/partition[2]) # box size
        c = 0
        for point ∈ zip(ξs, sφs)
            id = clamp.(ceil.(Int, (point .- (0, -1))./ε), (1,1), partition)
            if !A[id...]
                A[id...] = true
                c += 1
                if c == partition[1]*partition[2]
                    break
                end
            end
        end
        for a in A
            @test a
        end
    end
end

billiards_testset("Fills boundary map", fills_boundarymap; caller = ergodic_tests)

function phasespace_ratio(f, g)
    @testset "Bunimovich" begin
        t = 100000.0
        bt = billiard_bunimovich()
        for ω in [0.0, 0.1]
            p = ω == 0 ? randominside(bt) : randominside(bt, ω)
            @testset "$(tag(p, bt))" begin
                φ = π/4 * rand() # so that we never find bouncing walls
                p.vel = (cos(φ), sin(φ))
                ratio, = f(bt,t, randominside(bt), 0.1)
                @test ratio == 1.0
            end
        end
    end

    t = 1000000.0
    l = 1.0; r = 1.0
    @testset "Mushroom w=$(w)" for w ∈ [0.2, 0.4]

        bt = billiard_mushroom(l, w, r)
        @testset "regular" begin
            p = MushroomTools.randomregular(l, w, r)
            ratio, = f(bt,t, p, 0.1)
            trueratio =  1 - g(l,w,r)
            # Only one regular particle covers very small amount of space:
            @test ratio < trueratio
        end
        @testset "chaotic" begin
            p = MushroomTools.randomchaotic(l, w, r)
            ratio, = f(bt, t, p, 0.1)
            trueratio = g(l,w,r)
            @test trueratio - 0.1 ≤ ratio ≤ trueratio + 0.1
        end
    end
end
ratio_caller(x, f, g) = phasespace_ratio(f, g)

billiards_testset("2D Phasespace Ratio", identity, boundarymap_portion, MushroomTools.g_c_2D;
caller = ratio_caller)

billiards_testset("3D Phasespace Ratio", identity, phasespace_portion, MushroomTools.g_c_3D;
caller = ratio_caller)


text:
export result_modal_period,result_eigen_value,result_eigen_vector

"""
    result_modal_period(assembly,lc_id,node_id)
读取已求解的assembly实例的模态周期
# Arguments
- `assembly`: Assembly类型实例
- `lc_id`: 工况id
- `order`: 模态阶数，默认为全部结果
"""
function result_modal_period(assembly,lc_id,order="all")
    path=assembly.working_path
    lc=string(lc_id)
    if !(lc in keys(assembly.lcset.modals))
        throw("loadcase with id "*string(lc_id)*" doesn't exist!")
    end
    ω²=read_vector(path*"/.analysis",lc*"_o.v")
    if order=="all"
        return 2π./sqrt.(ω²)
    else
        return 2π/sqrt(ω²[order])
    end
end

"""
    result_modal_period(assembly,lc_id,node_id)
读取已求解的assembly实例的模态特征值
# Arguments
- `assembly`: Assembly类型实例
- `lc_id`: 工况id
- `order`: 模态阶数，默认为全部结果
"""
function result_eigen_value(assembly,lc_id;order="all")
    path=assembly.working_path
    d=read_vector(path*"/.analysis",lc_id*"_o.v")
    if order=="all"
        return d
    else
        return d[order]
    end
end

"""
    result_modal_period(assembly,lc_id,node_id)
读取已求解的assembly实例的模态特征向量
# Arguments
- `assembly`: Assembly类型实例
- `lc_id`: 工况id
- `order`: 模态阶数，默认为全部结果
"""
function result_eigen_vector(assembly,lc_id;order="all")
    path=assembly.working_path
    ϕ=read_matrix(path*"/.analysis",lc_id*"_p.m")
    if order=="all"
        return ϕ
    else
        return ϕ[:,order]
    end
end

# """
#     result_modal_period(assembly,lc_id,node_id)
# 读取已求解的assembly实例的模态周期
# # Arguments
# - `assembly`: Assembly类型实例
# - `lc_id`: 工况id
# - `order`: 模态阶数，默认为全部结果
# """
# function result_modal_mass_paticipation(assembly,lc_id,node_id)
#     path=assembly.working_path
#     node_idx=findfirst(x->x.id==string(node_id),assembly.structure.nodes)
#     if node_idx isa Nothing
#         throw("node with id "*string(node_id)*" doesn't exist!")
#     end
#     lc_idx=findfirst(x->x.id==string(lc_id),assembly.lcset.cases)
#     if lc_idx isa Nothing
#         throw("loadcase with id "*string(lc_id)*" doesn't exist!")
#     end
#     d=read_vector(path*"/.analysis",lc_id*".v")
#     hid=assembly.structure.nodes[node_idx].hid
#     DOFs=6*hid-5:6*hid
#     R=assembly.structure.K*d-assembly.loadcases[lc_idx].P
#     return R[DOFs]
# end


text:
# 1-Steiner spanner functionality

# Returns a graph G = (V, E) which is a Manhattan network of the original point
# set, i.e., a graph where each edge is parallel to one of the main coordinate
# axes and each pair of points has a path in the graph equal to the L1 distance.
# If the original set S has n points, this graph will have O(n log n) points
# and edges. Based on the algorithm from Gudmundsson et al.
#
# Args:
#   S - n x p matrix of points in R^d
# Returns
#   X - the original points plus the new projected points
#   E - an array of (int, int) representing the edges of the graph where the
#       int index the rows of Xnew.

# constant used for numerical issues
EPS = 10 * eps(0.0)

function makel1spanner(X::AbstractArray{T,2}) where {T<:Number}
    n, d = size(X)
    # copy so we don't corrupt the original data
    Xnew = copy(X)
    E = Tuple{Int, Int}[]
    # begin the recursion!
    Xnew = _makel1spanner!(Xnew, E, collect(1:n), d)
    return (Xnew, E)
end

function _makel1spanner!(X::AbstractArray{T,2},
                         E::Array{Tuple{Int,Int},1},
                         Xidx::Array{Int,1},
                         k::Int) where {T<:Number}
    # initialize the iterative counters
    Xindexs = Array{Int,1}[]
    k_indexs = Int[]
    push!(Xindexs, Xidx)
    push!(k_indexs, k)
    # now iterate through the recursion
    while length(Xindexs) > 0
        # pop off the local counters
        Xidx = pop!(Xindexs)
        k = pop!(k_indexs)
        # this is crazy, but if points already lie on hyperplane,
        # we should decrement k
        while (k > 1) && (maximum(X[Xidx,k]) - minimum(X[Xidx,k])) < EPS
            k -= 1
        end
        # handle base case first
        if k == 0 || length(Xidx) <= 1
            continue
        elseif k == 1
            # in this case, we only must add the adjacent points
            pointidx = sortperm(X[Xidx,1])
            for ii in 1:(length(pointidx)-1)
                push!(E, (Xidx[pointidx[ii]], Xidx[pointidx[ii+1]]))
            end
            continue
        else
            n = size(X,1)
            # make projected points (be sure to copy!)
            P = copy(X[Xidx,:])
            pstar = median(P[:,k])
            P[:,k] = pstar
            # add projected points
            X = vcat(X, P)
            nP = size(P,1)
            # add projected edges
            for ii in 1:nP
                push!(E, (Xidx[ii], n + ii))
            end
            Lhighidx = Xidx[X[Xidx,k] .> pstar]
            Llowidx = Xidx[X[Xidx,k] .< pstar]
            # Make sure we put median points on smaller indices
            Lequalidx = Xidx[X[Xidx,k] .== pstar]
            if length(Lequalidx) > 0 && (length(Llowidx) < length(Lhighidx))
                Llowidx = vcat(Llowidx, Lequalidx)
            elseif length(Lequalidx) > 0
                Lhighidx = vcat(Lhighidx, Lequalidx)
            end
            # call the operation on the upper half of original points
            push!(Xindexs, Lhighidx)
            push!(k_indexs, k)
            # call the operation on the lower half of original points
            push!(Xindexs, Llowidx)
            push!(k_indexs, k)
            # call the operation on the projected points
            Pidx = collect((n + 1):(n + nP))
            push!(Xindexs, Pidx)
            push!(k_indexs, k - 1)
        end
    end
    # return X!
    return X
end


text:
using OptimalTransport

using LinearAlgebra
using Random
using Test

Random.seed!(100)

@testset "utils.jl" begin
    @testset "add_singleton" begin
        x = rand(3)
        y = @inferred(OptimalTransport.add_singleton(x, Val(1)))
        @test size(y) == (1, length(x))
        @test vec(y) == x

        y = @inferred(OptimalTransport.add_singleton(x, Val(2)))
        @test size(y) == (length(x), 1)
        @test vec(y) == x

        x = rand(3, 4)
        y = @inferred(OptimalTransport.add_singleton(x, Val(1)))
        @test size(y) == (1, size(x, 1), size(x, 2))
        @test vec(y) == vec(x)

        y = @inferred(OptimalTransport.add_singleton(x, Val(2)))
        @test size(y) == (size(x, 1), 1, size(x, 2))
        @test vec(y) == vec(x)

        y = @inferred(OptimalTransport.add_singleton(x, Val(3)))
        @test size(y) == (size(x, 1), size(x, 2), 1)
        @test vec(y) == vec(x)
    end

    @testset "dot_matwise" begin
        l, m, n = 4, 5, 3
        x = rand(l, m)
        y = rand(l, m)
        @test OptimalTransport.dot_matwise(x, y) == dot(x, y)

        y = rand(l, m, n)
        @test OptimalTransport.dot_matwise(x, y) ≈
              mapreduce(vcat, (view(y, :, :, i) for i in axes(y, 3))) do yi
            dot(x, yi)
        end
        @test OptimalTransport.dot_matwise(y, x) == OptimalTransport.dot_matwise(x, y)
    end

    @testset "checksize" begin
        for xsize in ((5,), (5, 1), (5, 4)), ysize in ((3,), (3, 1), (3, 6))
            x = rand(xsize...)
            y = rand(ysize...)
            z = rand(xsize[1], ysize[1])
            OptimalTransport.checksize(x, y, z)
        end
    end

    @testset "checksize2" begin
        x = rand(5)
        y = rand(10)
        @test OptimalTransport.checksize2(x, y) === ()

        d = 4
        for (size2_x, size2_y) in
            (((), (d,)), ((1,), (d,)), ((d,), ()), ((d,), (1,)), ((d,), (d,)))
            x = rand(5, size2_x...)
            y = rand(10, size2_y...)
            @test OptimalTransport.checksize2(x, y) == (d,)
        end

        x = rand(5, 4)
        y = rand(10, 3)
        @test_throws DimensionMismatch OptimalTransport.checksize2(x, y)
    end

    @testset "checkbalanced" begin
        mass = rand()

        x1 = normalize!(rand(20), 1)
        x1 .*= mass
        y1 = normalize!(rand(30), 1)
        y1 .*= mass
        @test OptimalTransport.checkbalanced(x1, y1) === nothing
        @test OptimalTransport.checkbalanced(y1, x1) === nothing
        @test_throws ArgumentError OptimalTransport.checkbalanced(rand() .* x1, y1)
        @test_throws ArgumentError OptimalTransport.checkbalanced(x1, rand() .* y1)

        y2 = rand(30, 5)
        y2 .*= mass ./ sum(y2; dims=1)
        @test OptimalTransport.checkbalanced(x1, y2) === nothing
        @test OptimalTransport.checkbalanced(y2, x1) === nothing
        @test_throws ArgumentError OptimalTransport.checkbalanced(rand() .* x1, y2)
        @test_throws ArgumentError OptimalTransport.checkbalanced(
            x1, y2 .* hcat(rand(), ones(1, size(y2, 2) - 1))
        )

        x2 = rand(20, 5)
        x2 .*= mass ./ sum(x2; dims=1)
        @test OptimalTransport.checkbalanced(x2, y2) === nothing
        @test OptimalTransport.checkbalanced(y2, x2) === nothing
        @test_throws ArgumentError OptimalTransport.checkbalanced(
            x2 .* hcat(ones(1, size(x2, 2) - 1), rand()), y2
        )
        @test_throws ArgumentError OptimalTransport.checkbalanced(
            x2, y2 .* hcat(rand(), ones(1, size(y2, 2) - 1))
        )
    end

    @testset "A_batched_mul_B!" begin
        l, m, n = 5, 10, 4
        A = rand(l, m)
        b = rand(m)
        c = rand(l)
        OptimalTransport.A_batched_mul_B!(c, A, b)
        @test c == A * b

        B = rand(m, n)
        C = rand(l, n)
        OptimalTransport.A_batched_mul_B!(C, A, B)
        @test C == A * B

        As = rand(l, m, n)
        OptimalTransport.A_batched_mul_B!(C, As, B)
        @test all(C[:, i] ≈ As[:, :, i] * B[:, i] for i in 1:n)
    end

    @testset "At_batched_mul_B!" begin
        l, m, n = 5, 10, 4
        A = rand(l, m)
        b = rand(l)
        c = rand(m)
        OptimalTransport.At_batched_mul_B!(c, A, b)
        @test c == transpose(A) * b

        B = rand(l, n)
        C = rand(m, n)
        OptimalTransport.At_batched_mul_B!(C, A, B)
        @test C == transpose(A) * B

        As = rand(l, m, n)
        OptimalTransport.At_batched_mul_B!(C, As, B)
        @test all(C[:, i] ≈ transpose(As[:, :, i]) * B[:, i] for i in 1:n)
    end
end


text:
const stdlibs = isdefined(Pkg.Types, :stdlib) ? Pkg.Types.stdlib : Pkg.Types.stdlibs
# Julia 1.8 changed from `name` to `(name, version)`.
get_stdlib_name(s::AbstractString) = s
get_stdlib_name(s::Tuple) = first(s)
const BUILTIN_PKGS = Dict(get_stdlib_name(v)=>string(k) for (k, v) in stdlibs())


text:
using ROS
ROS.@include std_srvs: SetBool
ROS.@include roscpp_tutorials: TwoInts

srvc = nothing

function init()
    ROS.init("test_services")
    nh = ROS.NodeHandle()

    global srvc = ROS.serviceClient(nh, "add_two_ints", ROS.roscpp_tutorials_TwoInts);
    srv = ROS.advertiseService(nh, "test_srv", ROS.std_srvs_SetBool_Request, ROS.std_srvs_SetBool_Response, srv_callback)
    while ROS.ok()
        ROS.spinOnce()
    end
end

function srv_callback(req,res)
    res.success = true
    res.message = ":D"
    st = ROS.roscpp_tutorials_TwoInts()
    st.request.a = 1
    st.request.b = 3
    ROS.call(srvc, st)
    return true
end

init()


text:
using Swizzles: mask

@testset "Swizzles" begin

  A = [1 2 3; 4 5 6; 7 8 9]

  @test mask(Swizzle(+, 1, 3, 5)) === (1, 3, 5)
  @test mask(typeof(Swizzle(+, 10, 8, 6, 4, 2))) === (10, 8, 6, 4, 2)

  @test_throws DimensionMismatch Pour(max, (1, nil)).(A, [1, 2, 3, 4]) == [3; 6; 9]

  C = zeros(Int, 4)

  C .= Sum().(A)
  @test C == [45; 45; 45; 45]

  @test Sum(()).(A) == A
  @test Sum(:).(A) == 45
  @test Reduce(+, ()).(A) == A
  @test Reduce(+, :).(A) == 45
  @test Sum().([]) == nothing
  @test Beam(2, 3).([]) == Array{Any}(undef,1,0)
  @test_throws DimensionMismatch Beam(nil, 3).([])
  @test_throws DimensionMismatch Beam(1, nil, nil).(Array{Any}(undef,1,0))
  @test Sum(2).(Array{Any}(undef,1,0)) == [nothing, nothing][1:1,1:1]

  @test Pour(max, (1, nil)).(A) == [3; 6; 9]
  @test Pour(min, (1, nil)).(A) == [1; 4; 7]
  @test Pour(max, (1,)).(A) == [3; 6; 9]
  @test Pour(min, (1,)).(A) == [1; 4; 7]
  @test Pour(max, (nil, 1)).(A) == [7; 8; 9]
  @test Pour(min, (nil, 1)).(A) == [1; 2; 3]
  @test Pour(max, (2, nil)).(A) == [3  6  9]
  @test Pour(min, (2, nil)).(A) == [1  4  7]
  @test Pour(max, (nil, 2)).(A) == [7  8  9]
  @test Pour(min, (nil, 2)).(A) == [1  2  3]

  R = [0; 0; 0;]
  @test (R .= Pour(max, (1, nil)).(A)) == [3; 6; 9]
  R = [0; 0; 0;]
  @test (R .= Pour(min, (1, nil)).(A)) == [1; 4; 7]
  R = [0; 0; 0;]
  @test (R .= Pour(max, (1,)).(A)) == [3; 6; 9]
  R = [0; 0; 0;]
  @test (R .= Pour(min, (1,)).(A)) == [1; 4; 7]
  R = [0; 0; 0;]
  @test (R .= Pour(max, (nil, 1)).(A)) == [7; 8; 9]
  R = [0; 0; 0;]
  @test (R .= Pour(min, (nil, 1)).(A)) == [1; 2; 3]

  R = [0 0 0]
  @test (R .= Pour(max, (2, nil)).(A)) == [3  6  9]
  R = [0 0 0]
  @test (R .= Pour(min, (2, nil)).(A)) == [1  4  7]
  R = [0 0 0]
  @test (R .= Pour(max, (nil, 2)).(A)) == [7  8  9]
  R = [0 0 0]
  @test (R .= Pour(min, (nil, 2)).(A)) == [1  2  3]

  @test Pour(max, (1, nil, 2)).(A) == [3; 6; 9]
  @test Pour(min, (1, nil, 2)).(A) == [1; 4; 7]
  @test Pour(max, (nil, 1, 2)).(A) == [7; 8; 9]
  @test Pour(min, (nil, 1, 2)).(A) == [1; 2; 3]

  R = [0; 0; 0;]
  @test (R .= Pour(max, (1, nil, 2)).(A)) == [3; 6; 9]
  R = [0; 0; 0;]
  @test (R .= Pour(min, (1, nil, 2)).(A)) == [1; 4; 7]
  R = [0; 0; 0;]
  @test (R .= Pour(max, (nil, 1, 2)).(A)) == [7; 8; 9]
  R = [0; 0; 0;]
  @test (R .= Pour(min, (nil, 1, 2)).(A)) == [1; 2; 3]

  R = [0; 0; 0;]
  @test_throws DimensionMismatch R .= Beam((nil, 1, 2)).(A)
  @test_throws DimensionMismatch R .= Beam((1, nil, 2)).(A)

  @test Beam((2, 1)).(A) == transpose(A)

  R = [0 0 0;
       0 0 0;
       0 0 0]
  @test (R .= Pour(min, (2, 1)).(A)) == transpose(A)

  @test Beam(2).([11; 12; 13]) == [11 12 13]
  @test Pour(max, ()).([11; 12; 13]) == 13
  @test Pour(max, ()).((11, 12, 13)) == 13

  R = [0 0 0]
  @test (R .= Beam((2,)).([11; 12; 13])) == [11 12 13]
  R = [0]
  @test Pour(max, ()).((11, 12, 13)) == 13

  @test Pour(+).(A) == 45
  @test Pour(+, 2).(A) == [6 15 24]
  @test Pour(+, 1).(A) == [6; 15; 24]
  @test Pour(+, 1,2).(A) == A
  @test Pour(+, 1,2,3).(A) == A
  @test Pour(+, 2,1).(A) == transpose(A)
  @test Pour(+, 2,1,3).(A) == transpose(A)

  @test_throws DimensionMismatch Beam().(A)
  @test_throws DimensionMismatch Beam(2).(A)
  @test_throws DimensionMismatch Beam(1).(A)
  @test Beam(1,2).(A) == A
  @test Beam(1,2,3).(A) == A
  @test Beam(2,1).(A) == transpose(A)
  @test Beam(2,1,3).(A) == transpose(A)

  @test Reduce(+).(A) == 45
  @test Reduce(+, 1).(A) == [12 15 18]
  @test Reduce(+, 2).(A) == [6 15 24]'
  @test Reduce(+, 1, 2).(A) == [45]'
  @test Reduce(+, 2, 1).(A) == [45]'
  @test Reduce(+, 2, 1, 3).(A) == [45]'

  @test Sum().(A) == 45
  @test Sum(1).(A) == [12 15 18]
  @test Sum(2).(A) == [6 15 24]'
  @test Sum(1, 2).(A) == [45]'
  @test Sum(2, 1).(A) == [45]'
  @test Sum(2, 1, 3).(A) == [45]'

  @test Drop(+).(A) == 45
  @test Drop(+, 1).(A) == [12, 15, 18]
  @test Drop(+, 2).(A) == [6; 15; 24]
  @test Drop(+, 1, 2).(A) == 45
  @test Drop(+, 2, 1).(A) == 45
  @test Drop(+, 2, 1, 3).(A) == 45

  @test SumOut().(A) == 45
  @test SumOut(1).(A) == [12; 15; 18]
  @test SumOut(2).(A) == [6; 15; 24]
  @test SumOut(1, 2).(A) == 45
  @test SumOut(2, 1).(A) == 45
  @test SumOut(2, 1, 3).(A) == 45

  @test_throws DimensionMismatch Focus().(A)
  @test_throws DimensionMismatch Focus(2).(A)
  @test_throws DimensionMismatch Focus(1).(A)
  @test Focus(1,2).(A) == A
  @test Focus(1,2,3).(A) == reshape(A, 3, 3, 1)
  @test Focus(2,1).(A) == transpose(A)
  @test Focus(2,1,3).(A) == reshape(transpose(A), 3, 3, 1)

  @test Swizzle(+).(A) == 45
  @test Swizzle(+, 1).(A) == [6; 15; 24]
  @test Swizzle(+, 2).(A) == [12; 15; 18]
  @test Swizzle(+, 1,2).(A) == A
  @test Swizzle(+, 1,2,3).(A) == reshape(A, 3, 3, 1)
  @test Swizzle(+, 2,1).(A) == transpose(A)
  @test Swizzle(+, 2,1,3).(A) == reshape(transpose(A), 3, 3, 1)

  @test Beam(1).((1, 2)) isa Tuple
  @test Pour(+, (1,)).((1, 2)) isa Tuple
  @test SumOut(1).((1.0, 2)) isa Float64

  A = rand(1,1)
  B = rand(1,1)

  @test SumOut(2).(A.*Beam(2,3).(B)) isa Matrix
  @test Sum().(A.*Beam(2,3).(B)) isa Float64
  @test SumOut(2).(A.*Beam(2,3).(B)) == A * B

  A = rand(5,7)
  B = rand(7,6)

  @test SumOut(2).(A.*Beam(2,3).(B)) ≈ A * B

  @test reshape(Beam(2, 4).(A).*Beam(1, 3).(B), size(A, 1) * size(B, 1), :) == kron(A, B)

  x = rand(3)
  y = rand(4)

  @test reshape(Beam(2).(x).*y, :) == kron(x, y)

  z = Array{Int64,0}(undef)
  z[] = 13

  @test Beam(3).(z) == 13

  A = [1 2 3 4 5]
  Focus(nil, nil, 2).(A) == Beam(nil, 3).(A)

  @test Swizzle(+).(rand(3,3)) isa Float64
  @test copy(Swizzle(+)(rand(3,3))) isa AbstractArray{Float64, 0}

  A = reshape(1:27, 3, 3, 3)

  @test Sum(2).(A)[1, 1:1, 2:3] == Sum(2)(A)[1, 1:1, 2:3]
  @test Sum(2).(A)[1, 1:1, 3] == Sum(2)(A)[1, 1:1, 3]
  @test Sum(2).(A)[1, 1, 3:3] == Sum(2)(A)[1, 1, 3:3]

end


text:
include("./DC3Df90.jl")

# -----------------------------
# parameters
# -----------------------------
X = -24.5:1.0:24.5
Y = X
nx = length(X)
ny = length(Y)

z = -30.0
depth = 50.0
dip = 0.0
# -----------------------------

# -----------------------------
# calc
# -----------------------------
Uf90 = zeros(Float64,ny,nx,4)
for i = 1:ny
for j = 1:nx
    Uf90[i,j,1] = DC3D0f90(X[j], Y[i], z, depth, dip, 1e3, 0.0, 0.0, 0.0)[3]
    Uf90[i,j,2] = DC3D0f90(X[j], Y[i], z, depth, dip, 0.0, 1e3, 0.0, 0.0)[3]
    Uf90[i,j,3] = DC3D0f90(X[j], Y[i], z, depth, dip, 0.0, 0.0, 1e3, 0.0)[3]
    Uf90[i,j,4] = DC3D0f90(X[j], Y[i], z, depth, dip, 0.0, 0.0, 0.0, 1e3)[3]
end
end

# -----------------------------

# -----------------------------
# Figure
# -----------------------------
using Plots
#plotlyjs()
pyplot()

plt1 = plot(X, Y, Uf90[:,:,1], linetype=:surface, c=:balance, clims=(-0.20,0.20), zlims=(-0.15,0.15))
plt2 = plot(X, Y, Uf90[:,:,2], linetype=:surface, c=:balance, clims=(-0.20,0.20), zlims=(-0.15,0.15))
plt3 = plot(X, Y, Uf90[:,:,3], linetype=:surface, c=:balance, clims=(-0.50,0.50), zlims=( 0.00,0.55))
plt4 = plot(X, Y, Uf90[:,:,4], linetype=:surface, c=:balance, clims=(-0.10,0.10), zlims=( 0.00,0.10))

plts = plot(plt1, plt2, plt3, plt4, layout=(2,2), size=(1000,600))
# -----------------------------


text:
 # Load required packages.
using CSVFiles, DataFrames, Mimi, MimiRICE2010, MimiNICE, Statistics

# Load helper functions and revenue recycling components being added to the NICE model.
include("helper_functions.jl")
include(joinpath("revenue_recycling_components", "nice_revenue_recycle_component_time_varying.jl"))

#-----------------------#
# ----- Load Data ----- #
#-----------------------#

# Load studies used to calculate elasticities.
elasticity_studies = DataFrame(load(joinpath(@__DIR__, "..", "data", "elasticity_study_data.csv")))

# Load updated UN population projections and convert units to millions of people.
un_population_data = Matrix(DataFrame(load(joinpath(@__DIR__, "..", "data", "UN_medium_population_scenario.csv"), skiplines_begin=3))[:, 3:end]) ./ 1000

# Load quintile income distribution data that remains fixed over time (these shares are an update from the original NICE income distributions).
consumption_distribution_raw = DataFrame(load(joinpath(@__DIR__, "..", "data", "constant_quintile_distributions_consumption.csv")))

# Clean up and organize time-varying income distribution data into required NICE format (time × regions × quintiles).
consumption_distributions = get_quintile_income_shares(consumption_distribution_raw)

# -------------------------------------------------
# -------------------------------------------------
# Create function to add revenue recyling to NICE.
# -------------------------------------------------
# -------------------------------------------------

function create_nice_recycle(;slope_type::Symbol=:central, percentile::Float64=0.90)

    # Initialize an instance of NICE to build in revenue recycling.
    nice_rr = MimiNICE.create_nice()

    # Get number of timesteps across model time horizon.
    n_steps = length(dim_keys(nice_rr, :time))

    # Add in NICE revenue recycling component.
    add_comp!(nice_rr, nice_recycle, after = :nice_neteconomy)

    #-------------------------------------#
    # ----- Assign Model Parameters ----- #
    #-------------------------------------#

    # First, perform a meta-regression based on study results to calculate elasticity vs. ln gdp per capita relationship.
    meta_intercept, meta_slope = meta_regression(elasticity_studies, slope_type=slope_type, percentile=percentile)

    # Update parameters common to multiple components or that already have external Mimi parameters defined.
    update_param!(nice_rr, :l, un_population_data)
    update_param!(nice_rr, :quintile_pop, un_population_data ./ 5)

    # Set values for additional NICE and recycling parameters.
    set_param!(nice_rr, :nice_recycle, :min_study_gdp, minimum(elasticity_studies.pcGDP))
    set_param!(nice_rr, :nice_recycle, :max_study_gdp, maximum(elasticity_studies.pcGDP))
    set_param!(nice_rr, :nice_recycle, :elasticity_intercept, meta_intercept)
    set_param!(nice_rr, :nice_recycle, :elasticity_slope, meta_slope)
    set_param!(nice_rr, :nice_recycle, :regional_population, un_population_data)
    set_param!(nice_rr, :nice_recycle, :quintile_income_shares, consumption_distributions)
    set_param!(nice_rr, :nice_recycle, :damage_elasticity, 1.0)
    set_param!(nice_rr, :nice_recycle, :recycle_share, ones(12,5).*0.2)
    set_param!(nice_rr, :nice_recycle, :global_carbon_tax, zeros(n_steps))
    set_param!(nice_rr, :nice_recycle, :lost_revenue_share, 0.0)
    set_param!(nice_rr, :nice_recycle, :global_recycle_share, zeros(12))

    # Create parameter connections (:component => :parameter).
    connect_param!(nice_rr, :nice_recycle => :industrial_emissions, :emissions       => :EIND)
    connect_param!(nice_rr, :nice_recycle => :DAMFRAC,              :damages         => :DAMFRAC)
    connect_param!(nice_rr, :nice_recycle => :ABATEFRAC,            :nice_neteconomy => :ABATEFRAC)
    connect_param!(nice_rr, :nice_recycle => :Y,                    :nice_neteconomy => :Y)
    connect_param!(nice_rr, :nice_recycle => :CPC,                  :nice_neteconomy => :CPC)
    connect_param!(nice_rr, :nice_welfare => :quintile_c,           :nice_recycle    => :qc_post_recycle)

    # Return NICE model with revenue recycling.
    return nice_rr
end