text:
module CBM
using Base.Test
solvers = map(x -> in(x, readdir(Pkg.dir())) ? x : "" , ["GLPK", "CPLEX", "Gurobi", "Clp"])
err_rd, err_wt = redirect_stderr()

using JSON
using ProgressMeter
using JuMP
using MAT
using HDF5
using Combinatorics

include("core/structure.jl")

if in("GLPK", solvers)
    using GLPK
    include("core/solvers/GLPK.jl")
end

if in("CPLEX", solvers)
    using CPLEX
    include("core/solvers/CPLEX.jl")
end

if in("Gurobi", solvers)
    using Gurobi
    include("core/solvers/Gurobi.jl")
end

if in("Clp", solvers)
    using Clp
    using Clp.ClpCInterface
    include("core/solvers/Clp.jl")
end

include("core/solvers/help.jl")

    export FBAsol
    export MOMAsol
    export Model
    export SLG
    export RobustnessAnalysis
    export FBAsolution
    export SolverInfo
    export GeneKnockout

include("core/tools.jl")
    export abstract_gene_names
    export genes_in_reaction
    export disable_genes_in_rules
    export enable_genes_in_rules
    export convert_rule_to_boolean
    export convert_from_abstract
    export find_gene
    export gene_exists
    export find_gene_effects
    export genes_in_reaction_rule
    export setup_lp
    export setup_global_lp
    export min_max_lp
    export analyze_list_of_blocked_reaction_lp
    export answer_lp
    export construct_lp_solution
    export set_solver
    export delete_column
    export add_column
    export add_row
    export get_sparse_columns
    export clone
    export find_metabolite
    export metabolite_exists
    export find_reaction
    export reaction_exists
    export fix_empty_fields
    export fix_model
    export fix_extra_dicts
    export fix_gene_rules
    export fix_genes
    export initialize_cores
    export disable_cores
    export load_model
    export open_mat_file
    export save_mat_file

include("core/io.jl")
    export load_json
    export load_matlab
    export load_table
    export export_json
    export export_matlab
    export export_table

include("core/fba.jl")
    export fba
    export fva
    export fba_norm2
    export fba_norm1
    export fba_norm0
    export fva
    export moma

    if in("GLPK", solvers)
        export settings_glpk
        export setup_glpk
    end

    if in("CPLEX", solvers)
        export settings_cplex
        export setup_cplex
    end

    if in("Gurobi", solvers)
        export setup_gurobi
    end

    if in("Clp", solvers)
        export settings_clp
        export setup_clp
    end

    export change_objective_coef
    export get_variable_bounds
    export get_constraint_bounds
    export get_constraint_matrix
    export get_direction
    export get_objective_value
    export get_reduced_costs
    export get_solution
    export get_solution_status
    export get_solution_status_code
    export get_slack
    export get_objective
    export set_direction
    export set_col_bounds
    export set_objective
    export solve
    export solver_status

include("core/simulations.jl")
    export find_blocked_reactions
    export synthetic_lethal_genes
    export robustness_analysis
    export find_deadend_metabolites
    export find_essential_genes
    export find_exchange_reactions
    export find_exchange_reactions
    export find_reactions_from_gene
    export find_reactions_from_metabolite
    export gene_deletion
    export knockout_genes
    export knockout_reactions
    export print_medium
    export print_reaction_formula
    export reaction_info

include("core/reconstruction.jl")
    export fast_cc
    export fast_core

# must bring PCBM into the scope of CBM
include("core/multicore.jl")
using PCBM

include("core/modification.jl")
    export add_metabolite!
    export add_metabolite
    export add_reaction!
    export add_reaction
    export change_objective!
    export change_objective
    export change_reaction_bounds!
    export change_reaction_bounds
    export convert_to_irreversible!
    export convert_to_irreversible
    export find_reversible
    export convert_to_reversible!
    export convert_to_reversible
    export reduce_model!
    export reduce_model
    export remove_gene!
    export remove_gene
    export remove_reaction!
    export remove_reaction

include("core/tests.jl")
    export test_add_reaction
    export test_change_objective
    export test_change_reaction_bounds
    export test_fba
    export test_find_blocked_reactions
    export test_find_deadend_metabolites
    export test_find_exchange_reactions
    export test_find_reactions_from_gene
    export test_find_reactions_from_metabolite
    export test_fva
    export test_gene_deletion
    export test_remove_reaction
    export test_lp
    export test_robustness_analysis
    export test_fast_cc
    export test_fast_core
    export test_solvers

# print errrors that arent method refefinition
outerr = String(readavailable(err_rd))
outerr = split(outerr, "\n")

for i in outerr
    if contains(i, "WARNING: Method definition")
        continue
    end
    println(i)
end

redirect_stderr(STDOUT)

end

ext: jl

max_stars_repo_name: isebarn/CBM

max_issues_repo_name: isebarn/CBM

max_forks_repo_name: isebarn/CBM


text:
## Basic Graphs.jl functions

Graphs.edgetype(::DataDiGraph{T}) where {T} = Edge{T}

Graphs.nv(g::DataDiGraph) = length(g.fadjlist)
Graphs.ne(g::DataDiGraph) = g.ne

Graphs.vertices(g::DataDiGraph) = 1:nv(g)

function Graphs.edges(g::DataDiGraph)
    return (Edge(u, v) for u in vertices(g) for v in outneighbors(g, u))
end

Graphs.has_vertex(g::DataDiGraph, v::Integer) = v in vertices(g)

function Graphs.has_edge(g::DataDiGraph, s::Integer, d::Integer)
    return (has_vertex(g, s) && has_vertex(g, d) && insorted(d, outneighbors(g, s)))
end

Graphs.inneighbors(g::DataDiGraph, v::Integer) = g.badjlist[v]
Graphs.outneighbors(g::DataDiGraph, v::Integer) = g.fadjlist[v]

Graphs.is_directed(g::DataDiGraph) = true
Graphs.is_directed(::Type{<:DataDiGraph}) = true

## Add vertices and edges

"""
    add_vertex!(g::DataDiGraph, label, data)

Add a vertex to `g` by specifying its `label` along with the associated `data`.
"""
function Graphs.add_vertex!(
    g::DataDiGraph{T,VL,VD,ED}, label::VL, data::VD
) where {T,VL,VD,ED}
    if haskey(g, label)
        return false
    else
        push!(g.fadjlist, T[])
        push!(g.badjlist, T[])
        push!(g.labels, label)
        g.vertices[label] = nv(g)
        push!(g.vertex_data, data)
        push!(g.edge_data, ED[])
        return true
    end
end

"""
    add_edge!(g::DataDiGraph, s, d, data)

Add an edge to `g` by specifying the vertices `s` and `d` along with the associated `data`.
"""
function Graphs.add_edge!(
    g::DataDiGraph{T,VL,VD,ED}, s::Integer, d::Integer, data::ED
) where {T,VL,VD,ED}
    if !has_vertex(g, s) || !has_vertex(g, d)
        return false
    else
        flist = g.fadjlist[s]
        d_index = searchsortedfirst(flist, d)
        if d_index <= length(flist) && flist[d_index] == d
            return false
        else
            blist = g.badjlist[d]
            s_index = searchsortedfirst(blist, s)
            g.ne += 1
            insert!(flist, d_index, d)
            insert!(blist, s_index, s)
            insert!(g.edge_data[s], d_index, data)
            return true
        end
    end
end

function Graphs.rem_edge!(g::DataDiGraph, s::Integer, d::Integer)
    if !has_edge(g, s, d)
        return false
    else
        d_index = searchsortedfirst(g.fadjlist[s], d)
        s_index = searchsortedfirst(g.badjlist[d], s)
        g.ne -= 1
        deleteat!(g.fadjlist[s], d_index)
        deleteat!(g.badjlist[d], s_index)
        deleteat!(g.edge_data[s], d_index)
    end
end

## Other utilities

function Graphs.reverse(g::DataDiGraph)
    rev_ne = g.ne
    rev_fadjlist = deepcopy(g.badjlist)
    rev_badjlist = deepcopy(g.fadjlist)
    rev_labels = deepcopy(g.labels)
    rev_vertices = deepcopy(g.vertices)
    rev_vertex_data = deepcopy(g.vertex_data)
    rev_edge_data = [
        [
            deepcopy(g.edge_data[s][searchsortedfirst(g.fadjlist[s], d)]) for
            s in inneighbors(g, d)
        ] for d in vertices(g)
    ]
    rev_graph_data = deepcopy(g.graph_data)
    rev_g = DataDiGraph(
        rev_ne,
        rev_fadjlist,
        rev_badjlist,
        rev_labels,
        rev_vertices,
        rev_vertex_data,
        rev_edge_data,
        rev_graph_data,
    )
    return rev_g
end


text:
using GraffSDK
using Arena
using CoordinateTransformations

# config = loadGraffConfig()
config = loadGraffConfig()

# config.sessionId = "GreenMonster_b3c8348876fe4a07975f8aaa27cc0b5b"
config.sessionId = "GreenMonster_481ff1d646da49f19ee072fb708fd4ed"

getStatus()

# getSessions()
# @info "Going to visualize once-off from $(config.sessionId)"
# estimates = getEstimates()
# ldict = sort(collect(keys(estimates)))
# map(l -> println("$l -> $(estimates[l])"), ldict)

setGlobalDrawTransform!(quat=Quat(0.0,1.0,0.0,0.0))

# Start the visualizer
botVis = initBotVis2()

# Get poses from DB and draw them
Arena.visualizeSession(botVis.vis; dataImageKey="", pointCloudKey="Depth")

# Testing JPG -> PNG conversion
# entries = getDataEntries(getNode("x40"))
# imData = getData(getNode("x40"), entries[3].id)

# using Base64, FileIO
# rawData = base64decode(imData.data)
# iob = IOBuffer(rawData)
# im = load(Stream(format"JPEG", iob))
# pngBytes = rgbToPng(im)
# Now give this to images lib.

# Depth cloud
# cameraModel = CameraModel(640,480, 1.0, [0.0, 0.0]) #TODO: Fix
# depthData = getData(getNode("x40"), entries[2].id)
# dData = base64decode(depthData.data)
# # iob = IOBuffer(depthData.data)
# c = reinterpret(UInt16, dData)
# depths = collect(reshape(c, (640, 480)))
# pointCloud = cloudFromDepthImage(depths, cameraModel)
# # Now put this on a pose.


text:
# Starting string processing example for Learning Julia

# simple string operations - length, size
mystr = "Ångström"
teststr = "jμΛIα" # test string with some Greek characters
println("String length of mystr: ", length(mystr))
println("Size of mystr: ", sizeof(mystr))
println("String length of teststr: ", length(teststr))
println("Size of teststr: ", sizeof(teststr), "\n") # non-English unicode characters take up
                                                    # more than one bit in storage

# concatenation and repetition operators
teststr = "Hello" * ", world!"
println(teststr)

teststr = "ABCD" ^ 3 # repeat ABCD 3 times
println(teststr, "\n")

# search for substrings
teststr = "Julia programming is awesome"
println(findnext("Julia", teststr, 0)) # find test range where Julia appears
println(occursin("some", teststr), "\n")

# pad strings either left or right
teststr = lpad("Test String", 20) # left padded by 20 spaces
println(teststr)
teststr = rpad("Test String", 20, '*') # right padded by 20 '*' characters
println(teststr, "\n")

# create a string from an array
arr = ["Lions", "Tigers", "Bears"]
teststr = join(arr, ", ", ", and ") # last argument specifies what to insert between the last two
println(teststr)

max_stars_repo_name: scaotravis/Julia-Course

max_issues_repo_name: scaotravis/Julia-Course

max_forks_repo_name: scaotravis/Julia-Course


text:
@testset "Crop" begin
    @test (Crop <: Augmentor.AffineOperation) == false
    @testset "constructor" begin
        @test_throws MethodError Crop()
        @test_throws MethodError Crop(())
        @test typeof(@inferred(Crop(1:10))) <: Crop{1} <: Crop <: Augmentor.ImageOperation
        @test typeof(@inferred(Crop(1:10,3:5))) <: Crop{2} <: Crop <: Augmentor.ImageOperation
        @test @inferred(Crop(Base.OneTo(10), Base.OneTo(5))) === @inferred(Crop((1:10,1:5)))
        @test @inferred(Crop(Base.OneTo(10), 1:5)) === @inferred(Crop((1:10,1:5)))
        @test str_show(Crop(3:4)) == "Augmentor.Crop{1}((3:4,))"
        @test str_showconst(Crop(3:4)) == "Crop(3:4)"
        @test str_showcompact(Crop(3:4)) == "Crop region (3:4,)"
        @test str_show(Crop(1:2,2:3)) == "Augmentor.Crop{2}((1:2, 2:3))"
        @test str_showconst(Crop(1:2,2:3)) == "Crop(1:2, 2:3)"
        @test str_showcompact(Crop(1:2,2:3)) == "Crop region 1:2×2:3"
        @test str_show(Crop(1:2,2:3,3:4)) == "Augmentor.Crop{3}((1:2, 2:3, 3:4))"
        @test str_showconst(Crop(1:2,2:3,3:4)) == "Crop(1:2, 2:3, 3:4)"
        @test str_showcompact(Crop(1:2,2:3,3:4)) == "Crop region (1:2, 2:3, 3:4)"
    end
    @testset "eager" begin
        @test_throws MethodError Augmentor.applyeager(Crop(1:10), nothing)
        @test_throws MethodError Augmentor.applyeager(Crop(1:2,2:3), nothing)
        @test Augmentor.supports_eager(Crop) === false
        imgs = [
            (rect, (1:2, 2:3)),
            (Augmentor.prepareaffine(rect), (1:2, 2:3)),
            (OffsetArray(rect, -2, -1), (-1:0, 1:2)),
            (view(rect, IdentityRange(1:2), IdentityRange(1:3)), (1:2, 2:3)),
        ]
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(Augmentor.applyeager(Crop(1:2,2:3), img_in))
                @test collect(res) == rect[1:2, 2:3]
                @test axes(res) == map(n->1:n, size(res)) # 1-base
                @test typeof(res) <: Array{eltype(img_in),2}
            end
        end
        @testset "multiple images" begin
            for (img_in1, inds1) in imgs, (img_in2, inds2) in imgs
                img_in = (img_in1, img_in2)
                inds = (inds1, inds2)
                res = @inferred(Augmentor.applyeager(Crop(1:2,2:3), img_in))
                @test collect.(res) == ntuple(i->rect[1:2, 2:3],2)
                @test typeof(res) <: NTuple{2,Array{eltype(img_in1),2}}
            end
        end
    end
    @testset "affine" begin
        @test Augmentor.supports_affine(Crop) === false
    end
    imgs = [
        (rect),
        (Augmentor.prepareaffine(rect)),
        (OffsetArray(rect, -2, -1)),
        (view(rect, IdentityRange(1:2), IdentityRange(1:3))),
    ]
    @testset "affineview" begin
        @test Augmentor.supports_affineview(Crop) === true
        @test_throws MethodError Augmentor.applyaffineview(Crop(1:2,2:3), nothing)
        @test @inferred(Augmentor.applyaffineview(Crop(1:2,2:3), rect)) == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for img_in in imgs
                res1, res2 = @inferred(Augmentor.applyaffineview(Crop(1:2,2:3), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
                @test typeof(parent(res1)) <: InvWarpedView
                @test typeof(parent(res2)) <: InvWarpedView
            end
        end
    end
    @testset "lazy" begin
        @test Augmentor.supports_lazy(Crop) === true
        @test @inferred(Augmentor.applylazy(Crop(1:2,2:3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for img_in in imgs
                res1, res2 = @inferred(Augmentor.applylazy(Crop(1:2,2:3), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "view" begin
        @test Augmentor.supports_view(Crop) === true
        @test @inferred(Augmentor.applyview(Crop(1:2,2:3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for img_in in imgs
                res1, res2 = @inferred(Augmentor.applyview(Crop(1:2,2:3), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "stepview" begin
        @test Augmentor.supports_stepview(Crop) === true
        @test @inferred(Augmentor.applystepview(Crop(1:2,2:3), rect)) === view(rect, 1:1:2, 2:1:3)
        @testset "multiple images" begin
            for img_in in imgs
                res1, res2 = @inferred(Augmentor.applystepview(Crop(1:2,2:3), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "permute" begin
        @test Augmentor.supports_permute(Crop) === false
    end
end

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

@testset "CropNative" begin
    @test (CropNative <: Augmentor.AffineOperation) == false
    @testset "constructor" begin
        @test_throws MethodError CropNative()
        @test_throws MethodError CropNative(())
        @test typeof(@inferred(CropNative(1:10))) <: CropNative{1} <: CropNative <: Augmentor.ImageOperation
        @test typeof(@inferred(CropNative(1:10,3:5))) <: CropNative{2} <: CropNative <: Augmentor.ImageOperation
        @test @inferred(CropNative(Base.OneTo(10), Base.OneTo(5))) === @inferred(CropNative((1:10,1:5)))
        @test @inferred(CropNative(Base.OneTo(10), 1:5)) === @inferred(CropNative((1:10,1:5)))
        @test str_show(CropNative(3:4)) == "Augmentor.CropNative{1}((3:4,))"
        @test str_showconst(CropNative(3:4)) == "CropNative(3:4)"
        @test str_showcompact(CropNative(3:4)) == "Crop native region (3:4,)"
        @test str_show(CropNative(1:2,2:3)) == "Augmentor.CropNative{2}((1:2, 2:3))"
        @test str_showconst(CropNative(1:2,2:3)) == "CropNative(1:2, 2:3)"
        @test str_showcompact(CropNative(1:2,2:3)) == "Crop native region 1:2×2:3"
        @test str_show(CropNative(1:2,2:3,3:4)) == "Augmentor.CropNative{3}((1:2, 2:3, 3:4))"
        @test str_showconst(CropNative(1:2,2:3,3:4)) == "CropNative(1:2, 2:3, 3:4)"
        @test str_showcompact(CropNative(1:2,2:3,3:4)) == "Crop native region (1:2, 2:3, 3:4)"
    end
    imgs = [
        (rect, (1:2, 2:3)),
        (Augmentor.prepareaffine(rect), (1:2, 2:3)),
        (OffsetArray(rect, -2, -1), (-1:0, 1:2)),
        (view(rect, IdentityRange(1:2), IdentityRange(1:3)), (1:2, 2:3)),
    ]
    @testset "eager" begin
        @test_throws MethodError Augmentor.applyeager(CropNative(1:10), nothing)
        @test_throws MethodError Augmentor.applyeager(CropNative(1:2,2:3), nothing)
        @test Augmentor.supports_eager(CropNative) === false
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(Augmentor.applyeager(CropNative(inds), img_in))
                @test collect(res) == rect[1:2, 2:3]
                @test axes(res) == map(n->1:n, size(res)) # 1-base
                @test typeof(res) <: Array{eltype(img_in),2}
            end
        end
        img = OffsetArray(rect, -2, -1)
        @test collect(@inferred(Augmentor.applyeager(CropNative(-1:0,1:2), img))) == rect[1:2, 2:3]
        @test typeof(Augmentor.applyeager(CropNative(-1:0,1:2), img)) <: Array
    end
    @testset "affine" begin
        @test Augmentor.supports_affine(CropNative) === false
    end
    @testset "affineview" begin
        @test Augmentor.supports_affineview(CropNative) === true
        @test_throws MethodError Augmentor.applyaffineview(CropNative(1:2,2:3), nothing)
        @test @inferred(Augmentor.applyaffineview(CropNative(1:2,2:3), rect)) == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applyaffineview(CropNative(inds), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
                @test typeof(parent(res1)) <: InvWarpedView
                @test typeof(parent(res2)) <: InvWarpedView
            end
        end
    end
    @testset "lazy" begin
        @test Augmentor.supports_lazy(CropNative) === true
        @test @inferred(Augmentor.applylazy(CropNative(1:2,2:3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applylazy(CropNative(inds), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "view" begin
        @test Augmentor.supports_view(CropNative) === true
        @test @inferred(Augmentor.applyview(CropNative(1:2,2:3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applyview(CropNative(inds), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "stepview" begin
        @test Augmentor.supports_stepview(CropNative) === true
        @test @inferred(Augmentor.applystepview(CropNative(1:2,2:3), rect)) === view(rect, 1:1:2, 2:1:3)
        # TODO: fix behaviour for stepview on IdentityRange
        #@testset "multiple images" begin
        #    for (img_in, inds) in imgs
        #       res1, res2 = @inferred(Augmentor.applystepview(CropNative(inds), (img_in, N0f8.(img_in))))
        #        # make sure both images are processed
        #        @test res1 == res2
        #        @test typeof(res1) <: SubArray{Gray{N0f8}}
        #        @test typeof(res2) <: SubArray{N0f8}
        #    end
        #end
    end
    @testset "permute" begin
        @test Augmentor.supports_permute(CropNative) === false
    end
end

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

@testset "CropSize" begin
    @test (CropSize <: Augmentor.AffineOperation) == false
    @test typeof(@inferred(CropSize())) <: CropSize <: Augmentor.ImageOperation
    @testset "constructor" begin
        @test_throws MethodError CropSize(())
        @test_throws MethodError CropSize(1.,2.)
        @test_throws MethodError CropSize(:a)
        @test_throws MethodError CropSize([:a])
        @test_throws ArgumentError CropSize(-1)
        @test_throws ArgumentError CropSize(0,2)
        op = @inferred(CropSize(10))
        @test str_show(op) == "Augmentor.CropSize{1}((10,))"
        @test str_showconst(op) == "CropSize(10)"
        @test str_showcompact(op) == "Crop a 10-length window at the center"
        op = @inferred(CropSize(20,30))
        @test op === CropSize(width=30, height=20)
        @test op.size == (20,30)
        @test str_show(op) == "Augmentor.CropSize{2}((20, 30))"
        @test str_showconst(op) == "CropSize(20, 30)"
        @test str_showcompact(op) == "Crop a 20×30 window around the center"
        op = @inferred(CropSize(20,30,40))
        @test op === @inferred(CropSize((20,30,40)))
        @test op === @inferred(CropSize{3}((20,30,40)))
        @test op.size == (20,30,40)
        @test str_show(op) == "Augmentor.CropSize{3}((20, 30, 40))"
        @test str_showconst(op) == "CropSize(20, 30, 40)"
        @test str_showcompact(op) == "Crop a 20×30×40 window around the center"
    end
    @testset "cropsize_axes" begin
        @test @inferred(Augmentor.cropsize_axes(CropSize(2,2), square)) === (1:2, 1:2)
        @test @inferred(Augmentor.cropsize_axes(CropSize(2,2), square2)) === (2:3, 2:3)
        @test @inferred(Augmentor.cropsize_axes(CropSize(2,2), checkers)) === (1:2, 2:3)
        @test @inferred(Augmentor.cropsize_axes(CropSize(1,3), checkers)) === (2:2, 2:4)
        @test @inferred(Augmentor.cropsize_axes(CropSize(3,3), checkers)) === (1:3, 2:4)
        @test @inferred(Augmentor.cropsize_axes(CropSize(2,2), OffsetArray(rect, -2, -1))) === (-1:0, 0:1)
    end
    imgs = [
        (rect, (1:2, 1:2)),
        (Augmentor.prepareaffine(rect), (1:2, 1:2)),
        (OffsetArray(rect, -2, -1), (-1:0, 0:1)),
        (view(rect, IdentityRange(1:2), IdentityRange(1:3)), (1:2, 1:2)),
        (square2, (2:3, 2:3)),
        (Augmentor.prepareaffine(square2), (2:3, 2:3)),
        (OffsetArray(square2, -2, -1), (0:1, 1:2)),
        (view(square2, IdentityRange(1:4), IdentityRange(1:4)), (2:3, 2:3)),
    ]
    @testset "eager" begin
        @test_throws MethodError Augmentor.applyeager(CropSize(10), nothing)
        @test_throws MethodError Augmentor.applyeager(CropSize(2,3), nothing)
        @test Augmentor.supports_eager(CropSize) === false
        @test @inferred(Augmentor.applyeager(CropSize(4,4), square2)) == square2
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(Augmentor.applyeager(CropSize(2,2), img_in))
                @test collect(res) == img_in[inds...]
                @test axes(res) == map(n->1:n, size(res)) # 1-base
                @test typeof(res) <: Array{eltype(img_in),2}
            end
        end
        @testset "multiple images" begin
            for (img_in1, inds1) in imgs, (img_in2, inds2) in imgs
                img_in = (img_in1, img_in2)
                inds = (inds1, inds2)
                res = @inferred(Augmentor.applyeager(CropSize(2,2), img_in))
                @test collect.(res) == ntuple(i->img_in[i][inds[i]...],2)
                @test typeof(res) <: NTuple{2,Array{eltype(img_in1),2}}
            end
        end
    end
    @testset "affine" begin
        @test Augmentor.supports_affine(CropSize) === false
    end
    @testset "affineview" begin
        @test Augmentor.supports_affineview(CropSize) === true
        @test_throws MethodError Augmentor.applyaffineview(CropSize(1:2,2:3), nothing)
        @test @inferred(Augmentor.applyaffineview(CropSize(2,3), rect)) == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applyaffineview(CropSize(2,2), square2)) == view(Augmentor.prepareaffine(square2), IdentityRange(2:3), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applyaffineview(CropSize(2,2), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == inds
                @test axes(res2) == inds
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
                @test typeof(parent(res1)) <: InvWarpedView
                @test typeof(parent(res2)) <: InvWarpedView
            end
        end
    end
    @testset "lazy" begin
        @test Augmentor.supports_lazy(CropSize) === true
        @test @inferred(Augmentor.applylazy(CropSize(2,3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applylazy(CropSize(2,2), square2)) === view(square2, IdentityRange(2:3), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applylazy(CropSize(2,2), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == inds
                @test axes(res2) == inds
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "view" begin
        @test Augmentor.supports_view(CropSize) === true
        @test @inferred(Augmentor.applyview(CropSize(2,3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applyview(CropSize(2,2), square2)) === view(square2, IdentityRange(2:3), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applyview(CropSize(2,2), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == inds
                @test axes(res2) == inds
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "stepview" begin
        @test Augmentor.supports_stepview(CropSize) === true
        @test @inferred(Augmentor.applystepview(CropSize(2,3), rect)) === view(rect, 1:1:2, 1:1:3)
        @test @inferred(Augmentor.applystepview(CropSize(2,2), square2)) === view(square2, 2:1:3, 2:1:3)
    end
    @testset "permute" begin
        @test Augmentor.supports_permute(CropSize) === false
    end
end

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

@testset "CropRatio" begin
    @test (CropRatio <: Augmentor.AffineOperation) == false
    @test typeof(@inferred(CropRatio())) <: CropRatio <: Augmentor.ImageOperation
    @testset "constructor" begin
        @test_throws MethodError CropRatio(())
        @test_throws MethodError CropRatio(1.,2.)
        @test_throws MethodError CropRatio(:a)
        @test_throws MethodError CropRatio([:a])
        @test_throws ArgumentError CropRatio(-1)
        @test_throws ArgumentError CropRatio(0)
        op = @inferred(CropRatio(3/4))
        @test op === CropRatio(ratio=3/4)
        @test str_show(op) == "Augmentor.CropRatio(0.75)"
        @test str_showconst(op) == "CropRatio(0.75)"
        @test str_showcompact(op) == "Crop to 3:4 aspect ratio"
        op = @inferred(CropRatio(1))
        @test op === @inferred(CropRatio())
        @test op === CropRatio(ratio=1)
        @test str_show(op) == "Augmentor.CropRatio(1.0)"
        @test str_showconst(op) == "CropRatio(1.0)"
        @test str_showcompact(op) == "Crop to 1:1 aspect ratio"
        op = @inferred(CropRatio(2.5))
        @test op === CropRatio(ratio=2.5)
        @test str_show(op) == "Augmentor.CropRatio(2.5)"
        @test str_showconst(op) == "CropRatio(2.5)"
        @test str_showcompact(op) == "Crop to 5:2 aspect ratio"
        op = @inferred(CropRatio(sqrt(2)))
        @test str_showcompact(op) == "Crop to 1.41 aspect ratio"
    end
    @testset "cropratio_axes" begin
        @test @inferred(Augmentor.cropratio_axes(CropRatio(1), square)) === (1:3, 1:3)
        @test @inferred(Augmentor.cropratio_axes(CropRatio(2), square)) === (2:2, 1:3)
        @test @inferred(Augmentor.cropratio_axes(CropRatio(1), square2)) === (1:4, 1:4)
        @test @inferred(Augmentor.cropratio_axes(CropRatio(1), rect)) === (1:2, 1:2)
        @test @inferred(Augmentor.cropratio_axes(CropRatio(1), checkers)) === (1:3, 2:4)
        @test @inferred(Augmentor.cropratio_axes(CropRatio(1), rotl90(checkers))) === (2:4, 1:3)
        @test @inferred(Augmentor.cropratio_axes(CropRatio(1), OffsetArray(rect, -2, -1))) === (-1:0, 0:1)
    end
    imgs = [
        (rect, (1:2, 1:2)),
        (Augmentor.prepareaffine(rect), (1:2, 1:2)),
        (OffsetArray(rect, -2, -1), (-1:0, 0:1)),
        (view(rect, IdentityRange(1:2), IdentityRange(1:3)), (1:2, 1:2)),
        (checkers, (1:3, 2:4)),
        (Augmentor.prepareaffine(square2), (1:4, 1:4)),
        (OffsetArray(square2, -2, -1), (-1:2, 0:3)),
        (view(checkers, IdentityRange(1:3), IdentityRange(1:5)), (1:3, 2:4)),
    ]
    @testset "eager" begin
        @test_throws MethodError Augmentor.applyeager(CropRatio(10), nothing)
        @test_throws MethodError Augmentor.applyeager(CropRatio(2), nothing)
        @test Augmentor.supports_eager(CropRatio) === false
        @test @inferred(Augmentor.applyeager(CropRatio(1), rect)) == rect[1:2,1:2]
        @test @inferred(Augmentor.applyeager(CropRatio(1), square)) == square
        @test @inferred(Augmentor.applyeager(CropRatio(1), square2)) == square2
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(Augmentor.applyeager(CropRatio(1), img_in))
                @test collect(res) == img_in[inds...]
                @test axes(res) == map(n->1:n, size(res)) # 1-base
                @test typeof(res) <: Array{eltype(img_in),2}
            end
        end
        @testset "multiple images" begin
            for (img_in1, inds1) in imgs, (img_in2, inds2) in imgs
                img_in = (img_in1, img_in2)
                inds = (inds1, inds2)
                res = @inferred(Augmentor.applyeager(CropRatio(1), img_in))
                @test collect.(res) == ntuple(i->img_in[i][inds[i]...],2)
                @test typeof(res) <: NTuple{2,Array{eltype(img_in1),2}}
            end
        end
    end
    @testset "affine" begin
        @test Augmentor.supports_affine(CropRatio) === false
    end
    @testset "affineview" begin
        @test Augmentor.supports_affineview(CropRatio) === true
        @test_throws MethodError Augmentor.applyaffineview(CropRatio(1), nothing)
        @test @inferred(Augmentor.applyaffineview(CropRatio(1), rect)) == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:2))
        @test @inferred(Augmentor.applyaffineview(CropRatio(2), square2)) == view(Augmentor.prepareaffine(square2), IdentityRange(2:3), IdentityRange(1:4))
        @test @inferred(Augmentor.applyaffineview(CropRatio(.5), square2)) == view(Augmentor.prepareaffine(square2), IdentityRange(1:4), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applyaffineview(CropRatio(1), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == inds
                @test axes(res2) == inds
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
                @test typeof(parent(res1)) <: InvWarpedView
                @test typeof(parent(res2)) <: InvWarpedView
            end
        end
    end
    @testset "lazy" begin
        @test Augmentor.supports_lazy(CropRatio) === true
        @test @inferred(Augmentor.applylazy(CropRatio(1), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:2))
        @test @inferred(Augmentor.applylazy(CropRatio(2), square2)) === view(square2, IdentityRange(2:3), IdentityRange(1:4))
        @test @inferred(Augmentor.applylazy(CropRatio(.5), square2)) === view(square2, IdentityRange(1:4), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applylazy(CropRatio(1), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == inds
                @test axes(res2) == inds
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "view" begin
        @test Augmentor.supports_view(CropRatio) === true
        @test @inferred(Augmentor.applyview(CropRatio(1), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:2))
        @test @inferred(Augmentor.applyview(CropRatio(2), square2)) === view(square2, IdentityRange(2:3), IdentityRange(1:4))
        @test @inferred(Augmentor.applyview(CropRatio(.5), square2)) === view(square2, IdentityRange(1:4), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(Augmentor.applyview(CropRatio(1), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == inds
                @test axes(res2) == inds
                @test typeof(res1) <: SubArray{Gray{N0f8}}
                @test typeof(res2) <: SubArray{N0f8}
            end
        end
    end
    @testset "stepview" begin
        @test Augmentor.supports_stepview(CropRatio) === true
        @test @inferred(Augmentor.applystepview(CropRatio(1), rect)) === view(rect, 1:1:2, 1:1:2)
        @test @inferred(Augmentor.applystepview(CropRatio(2), square2)) === view(square2, 2:1:3, 1:1:4)
        @test @inferred(Augmentor.applystepview(CropRatio(.5), square2)) === view(square2, 1:1:4, 2:1:3)
    end
    @testset "permute" begin
        @test Augmentor.supports_permute(CropRatio) === false
    end
end

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

@testset "RCropRatio" begin
    @test (RCropRatio <: Augmentor.AffineOperation) == false
    @test typeof(@inferred(RCropRatio())) <: RCropRatio <: Augmentor.ImageOperation
    @testset "constructor" begin
        @test_throws MethodError RCropRatio(())
        @test_throws MethodError RCropRatio(1.,2.)
        @test_throws MethodError RCropRatio(:a)
        @test_throws MethodError RCropRatio([:a])
        @test_throws ArgumentError RCropRatio(-1)
        @test_throws ArgumentError RCropRatio(0)
        op = @inferred(RCropRatio(3/4))
        @test op === RCropRatio(ratio=3/4)
        @test str_show(op) == "Augmentor.RCropRatio(0.75)"
        @test str_showconst(op) == "RCropRatio(0.75)"
        @test str_showcompact(op) == "Crop random window with 3:4 aspect ratio"
        op = @inferred(RCropRatio(1))
        @test op === @inferred(RCropRatio())
        @test op === RCropRatio(ratio=1)
        @test str_show(op) == "Augmentor.RCropRatio(1.0)"
        @test str_showconst(op) == "RCropRatio(1.0)"
        @test str_showcompact(op) == "Crop random window with 1:1 aspect ratio"
        op = @inferred(RCropRatio(2.5))
        @test op === RCropRatio(ratio=2.5)
        @test str_show(op) == "Augmentor.RCropRatio(2.5)"
        @test str_showconst(op) == "RCropRatio(2.5)"
        @test str_showcompact(op) == "Crop random window with 5:2 aspect ratio"
        op = @inferred(RCropRatio(sqrt(2)))
        @test str_showcompact(op) == "Crop random window with 1.41 aspect ratio"
    end
    @testset "randparam" begin
        @test @inferred(Augmentor.randparam(RCropRatio(1), square)) == (1:3, 1:3)
        @test @inferred(Augmentor.randparam(RCropRatio(1), rect)) in ((1:2, 2:3), (1:2, 1:2))
        @test @inferred(Augmentor.randparam(RCropRatio(1), (rect,square2))) in ((1:2, 2:3), (1:2, 1:2))
    end
    imgs = [
        (rect, (1:2, 1:3)),
        (Augmentor.prepareaffine(rect), (1:2, 1:3)),
        (OffsetArray(rect, -2, -1), (-1:0, 0:2)),
        (view(rect, IdentityRange(1:2), IdentityRange(1:3)), (1:2, 1:3)),
    ]
    @testset "eager" begin
        @test_throws MethodError Augmentor.applyeager(RCropRatio(10), nothing)
        @test_throws MethodError Augmentor.applyeager(RCropRatio(2), nothing)
        @test Augmentor.supports_eager(RCropRatio) === false
        @test @inferred(Augmentor.applyeager(RCropRatio(1), square)) == square
        @test @inferred(Augmentor.applyeager(RCropRatio(1), square2)) == square2
        out = collect(@inferred(Augmentor.applyeager(RCropRatio(1), rect)))
        @test out == rect[1:2,1:2] || out == rect[1:2,2:3]
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(Augmentor.applyeager(RCropRatio(3/2), img_in))
                @test collect(res) == img_in[inds...]
                @test axes(res) == map(n->1:n, size(res)) # 1-base
                @test typeof(res) <: Array{eltype(img_in),2}
            end
        end
        @testset "multiple images" begin
            for (img_in, _) in imgs
                res1, res2 = @inferred(Augmentor.applyeager(RCropRatio(1), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == axes(res2)
                @test typeof(res1) <: Array{Gray{N0f8}}
                @test typeof(res2) <: Array{N0f8}
            end
        end
    end
    @testset "affine" begin
        @test Augmentor.supports_affine(RCropRatio) === false
    end
    @testset "affineview" begin
        @test Augmentor.supports_affineview(RCropRatio) === true
        @test_throws MethodError Augmentor.applyaffineview(RCropRatio(1), nothing)
        # preserve aspect ratio (i.e. not random)
        @test @inferred(Augmentor.applyaffineview(RCropRatio(3/2), rect)) == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applyaffineview(RCropRatio(1), square)) == view(Augmentor.prepareaffine(square), IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(Augmentor.applyaffineview(RCropRatio(1), square2)) == view(Augmentor.prepareaffine(square2), IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred Augmentor.applyaffineview(RCropRatio(1), rect)
        @test out == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:2)) || out == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(2:3))
        out = @inferred Augmentor.applyaffineview(RCropRatio(2/3), square)
        @test out == view(Augmentor.prepareaffine(square), IdentityRange(1:3), IdentityRange(1:2)) || out == view(Augmentor.prepareaffine(square), IdentityRange(1:3), IdentityRange(2:3))
        out = @inferred Augmentor.applyaffineview(RCropRatio(3/2), square)
        @test out == view(Augmentor.prepareaffine(square), IdentityRange(1:2), IdentityRange(1:3)) || out == view(Augmentor.prepareaffine(square), IdentityRange(2:3), IdentityRange(1:3))
    end
    @testset "lazy" begin
        @test Augmentor.supports_lazy(RCropRatio) === true
        # preserve aspect ratio (i.e. not random)
        @test @inferred(Augmentor.applylazy(RCropRatio(3/2), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applylazy(RCropRatio(1), square)) === view(square, IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(Augmentor.applylazy(RCropRatio(1), square2)) === view(square2, IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred Augmentor.applylazy(RCropRatio(1), rect)
        @test out === view(rect, IdentityRange(1:2), IdentityRange(1:2)) || out === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        out = @inferred Augmentor.applylazy(RCropRatio(2/3), square)
        @test out === view(square, IdentityRange(1:3), IdentityRange(1:2)) || out === view(square, IdentityRange(1:3), IdentityRange(2:3))
        out = @inferred Augmentor.applylazy(RCropRatio(3/2), square)
        @test out === view(square, IdentityRange(1:2), IdentityRange(1:3)) || out === view(square, IdentityRange(2:3), IdentityRange(1:3))
    end
    @testset "view" begin
        @test Augmentor.supports_view(RCropRatio) === true
        # preserve aspect ratio (i.e. not random)
        @test @inferred(Augmentor.applyview(RCropRatio(3/2), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applyview(RCropRatio(1), square)) === view(square, IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(Augmentor.applyview(RCropRatio(1), square2)) === view(square2, IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred Augmentor.applyview(RCropRatio(1), rect)
        @test out === view(rect, IdentityRange(1:2), IdentityRange(1:2)) || out === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        out = @inferred Augmentor.applyview(RCropRatio(2/3), square)
        @test out === view(square, IdentityRange(1:3), IdentityRange(1:2)) || out === view(square, IdentityRange(1:3), IdentityRange(2:3))
        out = @inferred Augmentor.applyview(RCropRatio(3/2), square)
        @test out === view(square, IdentityRange(1:2), IdentityRange(1:3)) || out === view(square, IdentityRange(2:3), IdentityRange(1:3))
    end
    @testset "stepview" begin
        @test Augmentor.supports_stepview(RCropRatio) === true
        # preserve aspect ratio (i.e. not random)
        @test @inferred(Augmentor.applystepview(RCropRatio(3/2), rect)) === view(rect, 1:1:2, 1:1:3)
        @test @inferred(Augmentor.applystepview(RCropRatio(1), square)) === view(square, 1:1:3, 1:1:3)
        @test @inferred(Augmentor.applystepview(RCropRatio(1), square2)) === view(square2, 1:1:4, 1:1:4)
        # randomly placed
        out = @inferred Augmentor.applystepview(RCropRatio(1), rect)
        @test out === view(rect, 1:1:2, 1:1:2) || out === view(rect, 1:1:2, 2:1:3)
        out = @inferred Augmentor.applystepview(RCropRatio(2/3), square)
        @test out === view(square, 1:1:3, 1:1:2) || out === view(square, 1:1:3, 2:1:3)
        out = @inferred Augmentor.applystepview(RCropRatio(3/2), square)
        @test out === view(square, 1:1:2, 1:1:3) || out === view(square, 2:1:3, 1:1:3)
    end
    @testset "permute" begin
        @test @inferred(Augmentor.supports_permute(RCropRatio)) === false
    end
end

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

@testset "RCropSize" begin
    @test (RCropSize <: Augmentor.AffineOperation) == false
    @test typeof(@inferred(RCropSize(1))) <: RCropSize <: Augmentor.ImageOperation
    @testset "constructor" begin
        @test_throws MethodError RCropSize(())
        @test_throws MethodError RCropSize(1.,2.)
        @test_throws MethodError RCropSize(:a)
        @test_throws MethodError RCropSize([:a])
        @test_throws ArgumentError RCropSize(-1)
        @test_throws ArgumentError RCropSize(0)
        op = @inferred(RCropSize(100, 100))
        @test op === RCropSize(100, 100)
        @test str_show(op) == "Augmentor.RCropSize(100, 100)"
        @test str_showconst(op) == "RCropSize(100, 100)"
        @test str_showcompact(op) == "Crop random window with size (100, 100)"
    end
    @testset "randparam" begin
        @test @inferred(Augmentor.randparam(RCropSize(3), square)) == (1:3, 1:3)
        @test @inferred(Augmentor.randparam(RCropSize(2,3), rect)) == (1:2, 1:3)
        @test @inferred(Augmentor.randparam(RCropSize(2,2), rect)) in ((1:2, 1:2), (1:2, 2:3))
    end
    imgs = [
        (rect, (1:2, 1:3)),
        (Augmentor.prepareaffine(rect), (1:2, 1:3)),
        (OffsetArray(rect, -2, -1), (-1:0, 0:2)),
        (view(rect, IdentityRange(1:2), IdentityRange(1:3)), (1:2, 1:3)),
    ]
    @testset "eager" begin
        @test_throws MethodError Augmentor.applyeager(RCropSize(10), nothing)
        @test Augmentor.supports_eager(RCropSize) === false
        @test @inferred(Augmentor.applyeager(RCropSize(3), square)) == square
        @test @inferred(Augmentor.applyeager(RCropSize(4), square2)) == square2
        out = collect(@inferred(Augmentor.applyeager(RCropSize(2,2), rect)))
        @test out == rect[1:2,1:2] || out == rect[1:2,2:3]
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(Augmentor.applyeager(RCropSize(2,3), img_in))
                @test collect(res) == img_in[inds...]
                @test axes(res) == map(n->1:n, size(res)) # 1-base
                @test typeof(res) <: Array{eltype(img_in),2}
            end
        end
        @testset "multiple images" begin
            for (img_in, _) in imgs
                res1, res2 = @inferred(Augmentor.applyeager(RCropSize(2), (img_in, N0f8.(img_in))))
                # make sure both images are processed
                @test res1 == res2
                @test axes(res1) == axes(res2)
                @test typeof(res1) <: Array{Gray{N0f8}}
                @test typeof(res2) <: Array{N0f8}
            end
        end
    end
    @testset "affine" begin
        @test Augmentor.supports_affine(RCropSize) === false
    end
    @testset "affineview" begin
        @test Augmentor.supports_affineview(RCropSize) === true
        @test_throws MethodError Augmentor.applyaffineview(RCropSize(1), nothing)
        @test @inferred(Augmentor.applyaffineview(RCropSize(2,3), rect)) == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applyaffineview(RCropSize(3), square)) == view(Augmentor.prepareaffine(square), IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(Augmentor.applyaffineview(RCropSize(4), square2)) == view(Augmentor.prepareaffine(square2), IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred Augmentor.applyaffineview(RCropSize(2), rect)
        @test out == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:2)) || out == view(Augmentor.prepareaffine(rect), IdentityRange(1:2), IdentityRange(2:3))
        out = @inferred Augmentor.applyaffineview(RCropSize(2), square)
        @test (out == view(Augmentor.prepareaffine(square), IdentityRange(1:2), IdentityRange(1:2)) || out == view(Augmentor.prepareaffine(square), IdentityRange(1:2), IdentityRange(2:3))
            || out == view(Augmentor.prepareaffine(square), IdentityRange(2:3), IdentityRange(1:2)) || out == view(Augmentor.prepareaffine(square), IdentityRange(2:3), IdentityRange(2:3)))
    end
    @testset "lazy" begin
        @test Augmentor.supports_lazy(RCropSize) === true
        @test @inferred(Augmentor.applylazy(RCropSize(2,3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applylazy(RCropSize(3), square)) === view(square, IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(Augmentor.applylazy(RCropSize(4), square2)) === view(square2, IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred Augmentor.applylazy(RCropSize(2), rect)
        @test out === view(rect, IdentityRange(1:2), IdentityRange(1:2)) || out === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        out = @inferred Augmentor.applylazy(RCropSize(2), square)
        @test (out === view(square, IdentityRange(1:2), IdentityRange(1:2)) || out === view(square, IdentityRange(1:2), IdentityRange(2:3))
            || out === view(square, IdentityRange(2:3), IdentityRange(1:2)) || out === view(square, IdentityRange(2:3), IdentityRange(2:3)))
    end
    @testset "view" begin
        @test Augmentor.supports_view(RCropSize) === true
        @test @inferred(Augmentor.applyview(RCropSize(2,3), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(Augmentor.applyview(RCropSize(3), square)) === view(square, IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(Augmentor.applyview(RCropSize(4), square2)) === view(square2, IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred Augmentor.applyview(RCropSize(2), rect)
        @test out === view(rect, IdentityRange(1:2), IdentityRange(1:2)) || out === view(rect, IdentityRange(1:2), IdentityRange(2:3))
        out = @inferred Augmentor.applyview(RCropSize(2), square)
        @test (out === view(square, IdentityRange(1:2), IdentityRange(1:2)) || out === view(square, IdentityRange(1:2), IdentityRange(2:3))
            || out === view(square, IdentityRange(2:3), IdentityRange(1:2)) || out === view(square, IdentityRange(2:3), IdentityRange(2:3)))
    end
    @testset "stepview" begin
        @test Augmentor.supports_stepview(RCropSize) === true
        @test @inferred(Augmentor.applystepview(RCropSize(2,3), rect)) === view(rect, 1:1:2, 1:1:3)
        @test @inferred(Augmentor.applystepview(RCropSize(3), square)) === view(square, 1:1:3, 1:1:3)
        @test @inferred(Augmentor.applystepview(RCropSize(4), square2)) === view(square2, 1:1:4, 1:1:4)
        # randomly placed
        out = @inferred Augmentor.applystepview(RCropSize(2), rect)
        @test out === view(rect, 1:1:2, 1:1:2) || out === view(rect, 1:1:2, 2:1:3)
        out = @inferred Augmentor.applystepview(RCropSize(2), square)
        @test (out === view(square, 1:1:2, 1:1:2) || out === view(square, 1:1:2, 2:1:3)
            || out === view(square, 2:1:3, 1:1:2) || out === view(square, 2:1:3, 2:1:3))
    end
    @testset "permute" begin
        @test @inferred(Augmentor.supports_permute(RCropSize)) === false
    end
end


text:
include("viral_load_infectivity_testpos_v2.jl")

const arrival_scen_names = ["Day 2 PCR","Daily LFD","Both"]

function arrival_scenario_1_setup(start_day::Int, max_PCR_day::Int, pre_LFD::Bool = false,
                                  pre_LFD_time::Int = 2)   #1 PCR in first 2 days
    TestDays = [rand(start_day:(start_day+max_PCR_day))]
    PCR_delays = draw_PCR_delays(1)
    ResultDays = TestDays .+ PCR_delays
    TestTypes = [0]
    PreIsolationDays = collect(start_day:(ResultDays[1]-1))
    if pre_LFD
        if start_day > pre_LFD_time
            push!(TestDays, start_day-pre_LFD_time)
            push!(ResultDays, start_day-pre_LFD_time)
            push!(TestTypes,1)
        end
    end
    itd = sortperm(ResultDays)

    return TestDays[itd], TestTypes[itd], ResultDays[itd], PreIsolationDays
end

function arrival_scenario_2_setup(start_day::Int, NLFDs::Int, pre_LFD::Bool = false,
                                  pre_LFD_time::Int = 2)   #Daily LFDs
    TestDays = collect(start_day:(start_day+NLFDs-1))
    ResultDays = copy(TestDays)
    TestTypes = ones(Int64,NLFDs)
    PreIsolationDays = []
    if pre_LFD
        if start_day > pre_LFD_time
            push!(TestDays, start_day-pre_LFD_time)
            push!(ResultDays, start_day-pre_LFD_time)
            push!(TestTypes,1)
        end
    end
    itd = sortperm(ResultDays)

    return TestDays[itd], TestTypes[itd], ResultDays[itd],
           PreIsolationDays
end

function arrival_scenario_3_setup(start_day::Int, NLFDs::Int, max_PCR_day::Int,
                                 pre_LFD::Bool = false, pre_LFD_time::Int = 2,
                                 LFD_after_PCR::Bool = false)  #Daily LFDs and 1 PCR in first 2 days
    PCR_test_days = [rand(start_day:(start_day+max_PCR_day))]
    PCR_delays = draw_PCR_delays(length(PCR_test_days))
    PCR_result_days = PCR_test_days .+ PCR_delays
    PreIsolationDays = collect(start_day:(PCR_result_days[1]-1))

    LFD_test_days = collect(start_day:(start_day+NLFDs-1))
    if LFD_after_PCR
        LFD_test_days = LFD_test_days .+ PCR_result_days[1]
    end
    if pre_LFD
        if start_day > pre_LFD_time
            push!(LFD_test_days, start_day-pre_LFD_time)
        end
    end
    TestDays = vcat(LFD_test_days,PCR_test_days)
    ResultDays = vcat(LFD_test_days,PCR_result_days)
    TestTypes = vcat(ones(Int64,length(LFD_test_days)), zeros(Int64,length(PCR_test_days)))
    itd = sortperm(ResultDays)

    return TestDays[itd], TestTypes[itd], ResultDays[itd],
           PreIsolationDays
end

function init_testing_random_start!(sim::Dict, testing_params::Dict;
                                    Ndaysel::Int = 14)
    #remove people who are either not infectious or would be isolating
    #ToKeep = ones(Bool,sim["Ntot"])
    sim["will_isolate_with_test"] = ones(Bool,sim["Ntot"])
    sim["testing_paused"] = zeros(Bool,sim["Ntot"])
    sim["resume_testing"] = -ones(Int64,sim["Ntot"])
    sim["conf_PCR"] = Array{Array{Bool,1},1}(undef,sim["Ntot"])
    sim["test_days"] = Array{Array{Int64,1},1}(undef,sim["Ntot"])
    sim["test_types"] = Array{Array{Int64,1},1}(undef,sim["Ntot"])
    sim["test_result_days"] = Array{Array{Int64,1},1}(undef,sim["Ntot"])
    sim["preisolation"] = Array{Array{Int64,1},1}(undef,sim["Ntot"])
    TestOutput = []

    if testing_params["scenario"] == arrival_scen_names[1]
        if testing_params["pre_LFD"]
            TestOutput = arrival_scenario_1_setup.(sim["start_days"],
                  testing_params["PCR_max_day"]*ones(Int64,sim["Ntot"]), true,
                  testing_params["pre_LFD_day"])
        else
            TestOutput = arrival_scenario_1_setup.(sim["start_days"],
                  testing_params["PCR_max_day"]*ones(Int64,sim["Ntot"]))
        end
    elseif testing_params["scenario"] == arrival_scen_names[2]
        if testing_params["pre_LFD"]
            TestOutput = arrival_scenario_2_setup.(sim["start_days"],
                testing_params["LFD_tests"]*ones(Int64,sim["Ntot"]), true,
                testing_params["pre_LFD_day"])
        else
            TestOutput = arrival_scenario_2_setup.(sim["start_days"],
                testing_params["LFD_tests"]*ones(Int64,sim["Ntot"]))
        end
    elseif testing_params["scenario"] == arrival_scen_names[3]
        if testing_params["pre_LFD"]
            TestOutput = arrival_scenario_3_setup.(sim["start_days"],
                testing_params["LFD_tests"]*ones(Int64,sim["Ntot"]),
                testing_params["PCR_max_day"]*ones(Int64,sim["Ntot"]), Ref(true),
                Ref(testing_params["pre_LFD_day"]), Ref(testing_params["LFD_after_PCR"]))
        else
            TestOutput = arrival_scenario_3_setup.(sim["start_days"],
                testing_params["LFD_tests"]*ones(Int64,sim["Ntot"]),
                testing_params["PCR_max_day"]*ones(Int64,sim["Ntot"]), Ref(false),
                Ref(0), Ref(testing_params["LFD_after_PCR"]))
        end
    end

    for i in 1:sim["Ntot"]
        sim["test_days"][i] = TestOutput[i][1]
        sim["test_types"][i] = TestOutput[i][2]
        sim["test_result_days"][i] = TestOutput[i][3]
        sim["conf_PCR"][i] = zeros(Bool,length(sim["test_days"][i]))
#         if Conf_PCR
#             sim["conf_PCR"][i][sim["test_types"][i] .== 1] .= true
#         end
        sim["preisolation"][i] = TestOutput[i][4]
    end

    sim["test_pos_prob"] = get_test_probs.(sim["VL_profiles"], sim["test_days"],
        sim["test_types"]; Do_LFD_test_prob = testing_params["comply_prob"])
    #assuming random compliance
end

function run_testing_flight_arrivals(Ntot::Int64, PIsol::Float64,
             testing_params::Array{Dict,1}, arrival_GR::Float64, Ndaysel::Int,
             infected_in_flight::Bool=false)

    sim_baseline = init_VL_and_infectiousness(Ntot, PIsol)
    VL_len = length.(sim_baseline["VL_profiles"])
    #define start day (a.k.a arrival day)
    #exponential distribution (growth rate arrival_GR) -- 0 if uniform
    if infected_in_flight
        sim_baseline["start_days"] = ones(Int64,sim_baseline["Ntot"])
    else
        sim_baseline["start_days"] = sample(2:(Ndaysel+1),Weights(exp.(-arrival_GR.*collect(0:13))),
                                        sim_baseline["Ntot"])
    end
    sim_baseline["days_rel_arrival"] = Array{Array{Int64,1},1}(undef,sim_baseline["Ntot"])
    for i in 1:sim_baseline["Ntot"]
        sim_baseline["days_rel_arrival"][i] = collect(1:VL_len[i]) .- sim_baseline["start_days"][i]
    end
    sims = Array{Dict,1}(undef,length(testing_params))
    for (n,tp) in enumerate(testing_params)
        sim_scen = copy(sim_baseline)
        init_testing_random_start!(sim_scen, tp)
        #Need to sort out symp_day
        sim_scen["inf_profile_isolation"] = run_testing_scenario.(sim_scen["infection_profiles"],
            sim_scen["test_pos_prob"], sim_scen["test_result_days"], sim_scen["symp_day"] .+ 1,
            sim_scen["will_isolate"], sim_scen["VL_profiles"],  sim_scen["conf_PCR"],
            sim_scen["preisolation"])
        sims[n] = sim_scen
    end

    return sim_baseline, sims
end


text:
using Distances
using Random

# export generate_random_point_cloud,
#         generate_geometric_matrix,
#         generate_shuffled_matrix,
#         generate_random_matrix,
#         generate_matrix_ordering,
#         # generate_set_of_graphs,
#         # plot_betti_numbers,
#         # save_matrix_to_file;

"""
Returns a random matrix of size @number_of_points x @dimensions in which every
column is a point and every n-th row is a position in the n-th dimension.
"""
generate_random_point_cloud(number_of_points = 12, dimensions=2) =
                                    rand(Float64, dimensions, number_of_points)

"""
Return a matrix which stores the pariwise distances between every point in the
@random_points matrix.
"""
function generate_geometric_matrix(random_points)

    geometric_matrix = Distances.pairwise(Euclidean(), random_points, dims=2)
    return geometric_matrix
end

"""
Returns a ordered geometric matrix, which was generated by samping 'dims'
dimensional unit cuve with 'total_points' samples.
"""
function get_ordered_geom_matrix(dims::Integer, total_points::Integer)
    # TODO to be completed

end

"""
Returns a symetric matrix with randomly permuted valuse from the @input_matrix.
"""
function generate_shuffled_matrix(input_matrix)
    matrix_size = size(input_matrix,1)

    indicies_collection = findall(x->x>0, input_matrix)
    rand!(indicies_collection, indicies_collection)
    shuffeled_matrix = copy(input_matrix)

    # Swap the elements
    n=1
    for k in 1:matrix_size
        for m in k+1:matrix_size
            a = indicies_collection[n][1]
            b = indicies_collection[n][2]
            shuffeled_matrix[k,m] = input_matrix[a,b]
            shuffeled_matrix[m,k] = input_matrix[b,a]

            shuffeled_matrix[a,b] = input_matrix[k,m]
            shuffeled_matrix[b,a] = input_matrix[m,k]

            n +=1
        end
    end
    return shuffeled_matrix
end

"""
Returns matrix with random values which are symmetric accros the diagonal. The
matrix has @matrix_size rows and @matrix_size columns.
"""
function generate_random_matrix(matrix_size)
    elemnts_above_diagonal = Int((matrix_size^2-matrix_size)/2)
    random_matrix = zeros(matrix_size, matrix_size)
    set_of_random_numbers = rand(elemnts_above_diagonal)

    h = 1
    for k in 1:matrix_size
        for m in k+1:matrix_size
            random_matrix[k,m] = set_of_random_numbers[h]
            random_matrix[m,k] = set_of_random_numbers[h]
            h += 1
        end
    end

    return random_matrix
end

# function generate_set_of_graphs(matrix_size, matrix_ordering)
#     """
#     Returns set of graphs generated from the @matrix_ordering. In every succesive
#     graph, single connection between points is added.
#
#     NOTE: the function does not take the coordinates of the numbered vertices.
#     """
#     vetrices = matrix_size
#     edges = matrix_ordering
#     num_of_edges = size(edges)[2]
#
#     set_of_graphs = [a=Graph(vetrices) for a=1:num_of_edges]
#     edges_counter = zeros(Int, num_of_edges)
#     edge_density =  zeros(num_of_edges)
#
#     k=1
#     for k in range(1,stop=num_of_edges)~
#         add_edge!(set_of_graphs[k], edges[1,k], edges[2,k]);
#         edges_counter[k] = ne(set_of_graphs[k])
#         edge_density[k] = edges_counter[k]/binomial(matrix_size,2)
#
#         if k<num_of_edges # if is used to eliminate copying at last iteration
#             set_of_graphs[k+1] = copy(set_of_graphs[k])
#         end
#     end
#     return set_of_graphs, edge_density
# end

# function save_matrix_to_file(matrix, filename)
#     """
#     Saves given @matrix to the csv file with the name @filename. If there is no path
#     added to the @filename, then file saved is in local folder.
#     """
#     open(filename, "w") do io
#         writedlm(io,  matrix, ',')
#     end
# end

# =====
# Copied form Julia learning repo

"""
Returns ordering of the @geometric_matrix given as an input. If value @ascending
is set to true, the values are number from the lowest value, to the highest. If
false, the values are numbered from highest to the lowest.
"""
function generate_matrix_ordering(geometric_matrix, ascending = true)

    matrix_size = size(geometric_matrix, 2)
    elemnts_above_diagonal = Int((matrix_size^2-matrix_size)/2)
    matrix_ordering = zeros(Int, 2,elemnts_above_diagonal)

    A = copy(geometric_matrix)

    (ascending) ? (method=findmax) : (method=findmin)

    for element in 1:elemnts_above_diagonal
    #     Find maximal distance
        minimal_value = method(A)
    #     Get the coordinates (only 2 dimensions, because it is distance matrix)
        matrix_ordering[1,element] = Int(minimal_value[2][1])
        matrix_ordering[2,element] = Int(minimal_value[2][2])
    #
    # #     Zero minval in A (above and below diagonal) so next minval can be found
        A[matrix_ordering[1,element], matrix_ordering[2,element]] = 0.0
        A[matrix_ordering[2,element], matrix_ordering[1,element]] = 0.0
    end

    # change from min to max order to the max to min order (? necessary ?)
    if ascending
        matrix_ordering = matrix_ordering[:,end:-1:1]
    end

    return matrix_ordering
end

"""
    function get_geometric_matrix(points, dimensions; save_as_file=false)

Created a point cloud with 'points' number of points from 'dimension'
dimensional eucidean unit cube and computes distances between the points.

Distance matrix may be saved to csv file by setting 'save_as_file' to 'true'.
"""
function get_geometric_matrix(points, dimensions; save_as_file=false)
    point_cloud = generate_random_point_cloud(points,dimensions)
    geom_mat = generate_geometric_matrix(point_cloud)

    if save_as_file
        open("geometric_matrix_points$(points)_dims$(dimensions).csv", "w") do io
           writedlm(io, geom_mat, ',')
       end
   end
   return geom_mat
end


text:
export rule

@rule AR(:y, Marginalisation) (m_x::NormalDistributionsFamily, q_θ::NormalDistributionsFamily, q_γ::GammaShapeRate, meta::ARMeta) = begin
    mθ, Vθ = mean(q_θ), cov(q_θ)
    mx, Vx = mean(m_x), cov(m_x)
    mγ = mean(q_γ)

    mA = as_companion_matrix(mθ)
    mV = ar_transition(getvform(meta), getorder(meta), mγ)

    D = inv(Vx) + mean(q_γ)*Vθ

    my = mA*inv(D)*inv(Vx)*mx
    Vy = mA*inv(D)*mA' + mV

    return convert(promote_variate_type(getvform(meta), NormalMeanVariance), my, Vy)
end


text:
#computes the energy of a sequence
#energy(epistasis, parameters, sequence)
function Energy(epistasis::EpistasisMu, x::Vector{Float64}, variant)
	return energy(epistasis, x, variant)
end

#computes the binding probability of a sequence
#pb(epistasis, parameters, sequence)
function pb(epistasis::EpistasisMu, x::Vector{Float64}, variant)
	E = Energy(epistasis, x, variant)
	return 1/(1+exp(E))
end

export Energy, pb


text:
using Mocking

create_access_key_1_patch = @patch function create_access_key(a...; b...)
    return Dict{String, Any}("AccessKey" => Dict{String, Any}("SecretAccessKey" => "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY", "UserName" => "Bob", "Status" => "Active", "AccessKeyId" => "AKIAIOSFODNN7EXAMPLE", "CreateDate" => "2015-03-09T18:39:23.411Z"))
end

create_group_1_patch = @patch function create_group(a...; b...)
    return Dict{String, Any}("Group" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:group/Admins", "Path" => "/", "GroupName" => "Admins", "CreateDate" => "2015-03-09T20:30:24.940Z", "GroupId" => "AIDGPMS9RO4H3FEXAMPLE"))
end

create_instance_profile_1_patch = @patch function create_instance_profile(a...; b...)
    return Dict{String, Any}("InstanceProfile" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:instance-profile/Webserver", "InstanceProfileId" => "AIPAJMBYC7DLSPEXAMPLE", "Path" => "/", "InstanceProfileName" => "Webserver", "CreateDate" => "2015-03-09T20:33:19.626Z", "Roles" => Any[]))
end

create_login_profile_1_patch = @patch function create_login_profile(a...; b...)
    return Dict{String, Any}("LoginProfile" => Dict{String, Any}("UserName" => "Bob", "PasswordResetRequired" => true, "CreateDate" => "2015-03-10T20:55:40.274Z"))
end

create_open_idconnect_provider_1_patch = @patch function create_open_idconnect_provider(a...; b...)
    return Dict{String, Any}("OpenIDConnectProviderArn" => "arn:aws:iam::123456789012:oidc-provider/server.example.com")
end

create_role_1_patch = @patch function create_role(a...; b...)
    return Dict{String, Any}("Role" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:role/Test-Role", "RoleName" => "Test-Role", "Path" => "/", "RoleId" => "AKIAIOSFODNN7EXAMPLE", "CreateDate" => "2013-06-07T20:43:32.821Z", "AssumeRolePolicyDocument" => "<URL-encoded-JSON>"))
end

create_user_1_patch = @patch function create_user(a...; b...)
    return Dict{String, Any}("User" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:user/Bob", "UserName" => "Bob", "Path" => "/", "UserId" => "AKIAIOSFODNN7EXAMPLE", "CreateDate" => "2013-06-08T03:20:41.270Z"))
end

generate_organizations_access_report_1_patch = @patch function generate_organizations_access_report(a...; b...)
    return Dict{String, Any}("JobId" => "examplea-1234-b567-cde8-90fg123abcd4")
end

generate_service_last_accessed_details_1_patch = @patch function generate_service_last_accessed_details(a...; b...)
    return Dict{String, Any}("JobId" => "examplef-1305-c245-eba4-71fe298bcda7")
end

get_account_password_policy_1_patch = @patch function get_account_password_policy(a...; b...)
    return Dict{String, Any}("PasswordPolicy" => Dict{String, Any}("HardExpiry" => false, "RequireLowercaseCharacters" => false, "RequireNumbers" => true, "RequireSymbols" => true, "RequireUppercaseCharacters" => false, "ExpirePasswords" => false, "MaxPasswordAge" => 90, "AllowUsersToChangePassword" => false, "MinimumPasswordLength" => 8, "PasswordReusePrevention" => 12))
end

get_account_summary_1_patch = @patch function get_account_summary(a...; b...)
    return Dict{String, Any}("SummaryMap" => Dict{String, Any}("GroupsPerUserQuota" => 10, "Users" => 27, "PolicyVersionsInUseQuota" => 10000, "AttachedPoliciesPerUserQuota" => 10, "GroupsQuota" => 100, "AccountMFAEnabled" => 0, "MFADevices" => 6, "VersionsPerPolicyQuota" => 5, "Groups" => 15, "MFADevicesInUse" => 3, "AttachedPoliciesPerRoleQuota" => 10, "ServerCertificatesQuota" => 20, "GroupPolicySizeQuota" => 5120, "PoliciesQuota" => 1000, "UsersQuota" => 5000, "AccountSigningCertificatesPresent" => 0, "Policies" => 8, "PolicyVersionsInUse" => 22, "AccountAccessKeysPresent" => 1, "AttachedPoliciesPerGroupQuota" => 10, "UserPolicySizeQuota" => 2048, "ServerCertificates" => 1, "PolicySizeQuota" => 5120, "SigningCertificatesPerUserQuota" => 2, "AccessKeysPerUserQuota" => 2, "GlobalEndpointTokenVersion" => 2))
end

get_instance_profile_1_patch = @patch function get_instance_profile(a...; b...)
    return Dict{String, Any}("InstanceProfile" => Dict{String, Any}("Arn" => "arn:aws:iam::336924118301:instance-profile/ExampleInstanceProfile", "InstanceProfileId" => "AID2MAB8DPLSRHEXAMPLE", "Path" => "/", "InstanceProfileName" => "ExampleInstanceProfile", "CreateDate" => "2013-06-12T23:52:02Z", "Roles" => Any[Dict{String, Any}("Arn" => "arn:aws:iam::336924118301:role/Test-Role", "RoleName" => "Test-Role", "Path" => "/", "RoleId" => "AIDGPMS9RO4H3FEXAMPLE", "CreateDate" => "2013-01-09T06:33:26Z", "AssumeRolePolicyDocument" => "<URL-encoded-JSON>")]))
end

get_login_profile_1_patch = @patch function get_login_profile(a...; b...)
    return Dict{String, Any}("LoginProfile" => Dict{String, Any}("UserName" => "Anika", "CreateDate" => "2012-09-21T23:03:39Z"))
end

get_organizations_access_report_1_patch = @patch function get_organizations_access_report(a...; b...)
    return Dict{String, Any}("AccessDetails" => Any[Dict{String, Any}("TotalAuthenticatedEntities" => 2, "EntityPath" => "o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-1a2b3c-k9l8m7n6o5example/111122223333", "LastAuthenticatedTime" => "2019-05-25T16:29:52Z", "ServiceName" => "Amazon DynamoDB", "ServiceNamespace" => "dynamodb", "Region" => "us-east-1"), Dict{String, Any}("TotalAuthenticatedEntities" => 4, "EntityPath" => "o-a1b2c3d4e5/r-f6g7h8i9j0example/ou-1a2b3c-k9l8m7n6o5example/123456789012", "LastAuthenticatedTime" => "2019-06-15T13:12:06Z", "ServiceName" => "AWS Identity and Access Management", "ServiceNamespace" => "iam", "Region" => "us-east-1"), Dict{String, Any}("TotalAuthenticatedEntities" => 0, "ServiceName" => "Amazon Simple Storage Service", "ServiceNamespace" => "s3")], "JobStatus" => "COMPLETED", "NumberOfServicesNotAccessed" => 1, "JobCreationDate" => "2019-06-18T19:47:31.466Z", "JobCompletionDate" => "2019-06-18T19:47:35.241Z", "IsTruncated" => false, "NumberOfServicesAccessible" => 3)
end

get_role_1_patch = @patch function get_role(a...; b...)
    return Dict{String, Any}("Role" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:role/Test-Role", "MaxSessionDuration" => 3600, "RoleName" => "Test-Role", "Path" => "/", "RoleId" => "AROADBQP57FF2AEXAMPLE", "CreateDate" => "2013-04-18T05:01:58Z", "AssumeRolePolicyDocument" => "<URL-encoded-JSON>", "RoleLastUsed" => Dict{String, Any}("LastUsedDate" => "2019-11-18T05:01:58Z", "Region" => "us-east-1")))
end

get_service_last_accessed_details_1_patch = @patch function get_service_last_accessed_details(a...; b...)
    return Dict{String, Any}("JobStatus" => "COMPLETED", "JobCreationDate" => "2018-10-24T19:47:31.466Z", "JobCompletionDate" => "2018-10-24T19:47:35.241Z", "IsTruncated" => false, "ServicesLastAccessed" => Any[Dict{String, Any}("TotalAuthenticatedEntities" => 2, "LastAuthenticatedEntity" => "arn:aws:iam::123456789012:user/AWSExampleUser01", "ServiceName" => "AWS Identity and Access Management", "ServiceNamespace" => "iam", "LastAuthenticated" => "2018-10-24T19:11:00Z"), Dict{String, Any}("TotalAuthenticatedEntities" => 0, "ServiceName" => "Amazon Simple Storage Service", "ServiceNamespace" => "s3")])
end

get_service_last_accessed_details_with_entities_1_patch = @patch function get_service_last_accessed_details_with_entities(a...; b...)
    return Dict{String, Any}("JobStatus" => "COMPLETED", "EntityDetailsList" => Any[Dict{String, Any}("EntityInfo" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:user/AWSExampleUser01", "Id" => "AIDAEX2EXAMPLEB6IGCDC", "Path" => "/", "Type" => "USER", "Name" => "AWSExampleUser01"), "LastAuthenticated" => "2018-10-24T19:10:00Z"), Dict{String, Any}("EntityInfo" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:role/AWSExampleRole01", "Id" => "AROAEAEXAMPLEIANXSIU4", "Path" => "/", "Type" => "ROLE", "Name" => "AWSExampleRole01"))], "JobCreationDate" => "2018-10-24T19:47:31.466Z", "JobCompletionDate" => "2018-10-24T19:47:35.241Z", "IsTruncated" => false)
end

get_user_1_patch = @patch function get_user(a...; b...)
    return Dict{String, Any}("User" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:user/Bob", "UserName" => "Bob", "Path" => "/", "UserId" => "AKIAIOSFODNN7EXAMPLE", "CreateDate" => "2012-09-21T23:03:13Z"))
end

list_access_keys_1_patch = @patch function list_access_keys(a...; b...)
    return Dict{String, Any}("AccessKeyMetadata" => Any[Dict{String, Any}("UserName" => "Alice", "Status" => "Active", "AccessKeyId" => "AKIA111111111EXAMPLE", "CreateDate" => "2016-12-01T22:19:58Z"), Dict{String, Any}("UserName" => "Alice", "Status" => "Active", "AccessKeyId" => "AKIA222222222EXAMPLE", "CreateDate" => "2016-12-01T22:20:01Z")])
end

list_account_aliases_1_patch = @patch function list_account_aliases(a...; b...)
    return Dict{String, Any}("AccountAliases" => Any["exmaple-corporation"])
end

list_group_policies_1_patch = @patch function list_group_policies(a...; b...)
    return Dict{String, Any}("PolicyNames" => Any["AdminRoot", "KeyPolicy"])
end

list_groups_1_patch = @patch function list_groups(a...; b...)
    return Dict{String, Any}("Groups" => Any[Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:group/Admins", "Path" => "/division_abc/subdivision_xyz/", "GroupName" => "Admins", "CreateDate" => "2016-12-15T21:40:08.121Z", "GroupId" => "AGPA1111111111EXAMPLE"), Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test", "Path" => "/division_abc/subdivision_xyz/product_1234/engineering/", "GroupName" => "Test", "CreateDate" => "2016-11-30T14:10:01.156Z", "GroupId" => "AGP22222222222EXAMPLE"), Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers", "Path" => "/division_abc/subdivision_xyz/product_1234/", "GroupName" => "Managers", "CreateDate" => "2016-06-12T20:14:52.032Z", "GroupId" => "AGPI3333333333EXAMPLE")])
end

list_groups_for_user_1_patch = @patch function list_groups_for_user(a...; b...)
    return Dict{String, Any}("Groups" => Any[Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/engineering/Test", "Path" => "/division_abc/subdivision_xyz/product_1234/engineering/", "GroupName" => "Test", "CreateDate" => "2016-11-30T14:10:01.156Z", "GroupId" => "AGP2111111111EXAMPLE"), Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:group/division_abc/subdivision_xyz/product_1234/Managers", "Path" => "/division_abc/subdivision_xyz/product_1234/", "GroupName" => "Managers", "CreateDate" => "2016-06-12T20:14:52.032Z", "GroupId" => "AGPI222222222SEXAMPLE")])
end

list_policies_granting_service_access_1_patch = @patch function list_policies_granting_service_access(a...; b...)
    return Dict{String, Any}("PoliciesGrantingServiceAccess" => Any[Dict{String, Any}("Policies" => Any[Dict{String, Any}("PolicyArn" => "arn:aws:iam::123456789012:policy/ExampleIamPolicy", "PolicyName" => "ExampleIamPolicy", "PolicyType" => "MANAGED"), Dict{String, Any}("EntityName" => "AWSExampleGroup1", "EntityType" => "GROUP", "PolicyName" => "ExampleGroup1Policy", "PolicyType" => "INLINE")], "ServiceNamespace" => "iam"), Dict{String, Any}("Policies" => Any[Dict{String, Any}("PolicyArn" => "arn:aws:iam::123456789012:policy/ExampleEc2Policy", "PolicyName" => "ExampleEc2Policy", "PolicyType" => "MANAGED")], "ServiceNamespace" => "ec2")], "IsTruncated" => false)
end

list_role_tags_1_patch = @patch function list_role_tags(a...; b...)
    return Dict{String, Any}("Tags" => Any[Dict{String, Any}("Value" => "12345", "Key" => "Dept"), Dict{String, Any}("Value" => "Accounting", "Key" => "Team")], "IsTruncated" => false)
end

list_signing_certificates_1_patch = @patch function list_signing_certificates(a...; b...)
    return Dict{String, Any}("Certificates" => Any[Dict{String, Any}("UploadDate" => "2013-06-06T21:40:08Z", "UserName" => "Bob", "Status" => "Active", "CertificateBody" => "-----BEGIN CERTIFICATE-----<certificate-body>-----END CERTIFICATE-----", "CertificateId" => "TA7SMP42TDN5Z26OBPJE7EXAMPLE")])
end

list_user_tags_1_patch = @patch function list_user_tags(a...; b...)
    return Dict{String, Any}("Tags" => Any[Dict{String, Any}("Value" => "12345", "Key" => "Dept"), Dict{String, Any}("Value" => "Accounting", "Key" => "Team")], "IsTruncated" => false)
end

list_users_1_patch = @patch function list_users(a...; b...)
    return Dict{String, Any}("Users" => Any[Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Juan", "UserName" => "Juan", "Path" => "/division_abc/subdivision_xyz/engineering/", "PasswordLastUsed" => "2016-09-08T21:47:36Z", "UserId" => "AID2MAB8DPLSRHEXAMPLE", "CreateDate" => "2012-09-05T19:38:48Z"), Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:user/division_abc/subdivision_xyz/engineering/Anika", "UserName" => "Anika", "Path" => "/division_abc/subdivision_xyz/engineering/", "PasswordLastUsed" => "2016-09-24T16:18:07Z", "UserId" => "AIDIODR4TAW7CSEXAMPLE", "CreateDate" => "2014-04-09T15:43:45Z")])
end

list_virtual_mfadevices_1_patch = @patch function list_virtual_mfadevices(a...; b...)
    return Dict{String, Any}("VirtualMFADevices" => Any[Dict{String, Any}("SerialNumber" => "arn:aws:iam::123456789012:mfa/ExampleMFADevice"), Dict{String, Any}("SerialNumber" => "arn:aws:iam::123456789012:mfa/Juan")])
end

upload_server_certificate_1_patch = @patch function upload_server_certificate(a...; b...)
    return Dict{String, Any}("ServerCertificateMetadata" => Dict{String, Any}("Arn" => "arn:aws:iam::123456789012:server-certificate/company/servercerts/ProdServerCert", "UploadDate" => "2010-05-08T01:02:03.004Z", "Path" => "/company/servercerts/", "Expiration" => "2012-05-08T01:02:03.004Z", "ServerCertificateName" => "ProdServerCert", "ServerCertificateId" => "ASCA1111111111EXAMPLE"))
end

upload_signing_certificate_1_patch = @patch function upload_signing_certificate(a...; b...)
    return Dict{String, Any}("Certificate" => Dict{String, Any}("UploadDate" => "2015-06-06T21:40:08.121Z", "UserName" => "Bob", "Status" => "Active", "CertificateBody" => "-----BEGIN CERTIFICATE-----<certificate-body>-----END CERTIFICATE-----", "CertificateId" => "ID123456789012345EXAMPLE"))
end


text:
using Secp256k1, Test

tests = ["FieldElement", "Infinity", "Point", "scheme-types", "ECDSA"]

for t ∈ tests
  include("$(t)_tests.jl")
end


text:
@testset "Read a DataPackage from file" begin

    PATH_CITIES = joinpath(dirname(@__FILE__), "../data/cities/")

    @testset "Basic reading" begin
        p = Package(joinpath(PATH_CITIES, "datapackage.json"))
        # Check basic package attributes
        @test p.descriptor["name"] == "datapackage"
        @test length(p.resources) == 1
        # Check resources
        @test p.resources[1].descriptor["name"] == "cities"
        # Check the schema
        @test length(p.resources[1].schema.fields) == 2
    end

    @testset "Read data from package" begin
        p = Package(joinpath(PATH_CITIES, "datapackage.json"))
        r = get_resource(p, "cities")
        data = read(r, PATH_CITIES)
        @test data[1,2] == "London"
    end

    @testset "Read remote data package" begin
        p = Package("https://raw.githubusercontent.com/frictionlessdata/DataPackage.jl/master/data/cities/datapackage.json")
        r = get_resource(p, "cities")
        r.path = "https://raw.githubusercontent.com/frictionlessdata/DataPackage.jl/master/data/cities/cities.csv"
        @test typeof(r) == Resource
        data = read(r)
        @test data[1,2] == "London"
    end

end

max_issues_repo_name: frictionlessdata/datapackage-jl

max_forks_repo_name: frictionlessdata/datapackage-jl


text:
using Documenter
using CrystalNets

# from LiveServer + Literate documentation
# using Literate
# const src = joinpath(@__DIR__, "src")
# const lit = joinpath(@__DIR__, "lit")
# for (root, _, files) ∈ walkdir(lit), file ∈ files
#     splitext(file)[2] == ".jl" || continue
#     ipath = joinpath(root, file)
#     opath = splitdir(replace(ipath, lit=>src))[1]
#     Literate.markdown(ipath, opath)
# end

DocMeta.setdocmeta!(CrystalNets, :DocTestSetup, quote
    using CrystalNets
    import CrystalNets: Options, Clustering, Bonding, StructureType
    const PeriodicGraphs = CrystalNets.PeriodicGraphs
    using .PeriodicGraphs

    CrystalNets.toggle_export(false)
    CrystalNets.toggle_warning(false)
end; recursive=true)

makedocs(
    sitename = "CrystalNets.jl",
    format = Documenter.HTML(),
    modules = [CrystalNets],
    pages = [
        "Home" => "index.md",
        "Visualization" => "man/visualization.md",
        "FAQ" => "faq.md",
        "Library" => [
            "Public"    => "lib/public.md",
            "Internals" => "lib/internals.md",
        ]
    ],
)

# Documenter can also automatically deploy documentation to gh-pages.
# See "Hosting Documentation" and deploydocs() in the Documenter manual
# for more information.
deploydocs(
    repo = "github.com/coudertlab/CrystalNets.jl.git"
)


text:
#=
Figures are supposed to fill the gap that Scenes in combination with Layoutables leave.
A scene is supposed to be a generic canvas on which plot objects can be placed and drawn.
Layoutables always require one specific type of scene, with a PixelCamera, in order to draw
their visual components there.
Figures also have layouts, which scenes do not have.
This is because every figure needs a layout, while not every scene does.
Figures keep track of the Layoutables that are created inside them, which scenes don't.

The idea is there are three types of plotting commands.
They can return either:
    - a FigureAxisPlot (figure, axis and plot)
    - an AxisPlot (axis and plot)
    - or an AbstractPlot (only plot)

This is needed so that users can without much boilerplate create the necessary structures and access them accordingly.

A normal plotting command creates a FigureAxisPlot, which can be splatted into figure, axis and plot. It displays like a figure, so that simple plots show up immediately.

A non-mutating plotting command that references a GridPosition or a GridSubposition creates an axis at that position and returns an AxisPlot, which can be splatted into axis and plot.

All mutating plotting commands return AbstractPlots.
They can either reference a GridPosition or a GridSubposition, in which case it is looked up
if an axis is placed at that position (if not it errors) or it can reference an axis directly to plot into.
=#

get_scene(fig::Figure) = fig.scene
get_scene(fap::FigureAxisPlot) = fap.figure.scene

const _current_figure = Ref{Union{Nothing, Figure}}(nothing)
"Returns the current active figure (or the last figure that got created)"
current_figure() = _current_figure[]
"Set `fig` as the current active scene"
current_figure!(fig) = (_current_figure[] = fig)

"Returns the current active axis (or the last axis that got created)"
current_axis() = current_axis(current_figure())
current_axis(fig::Figure) = fig.current_axis[]
"Set `ax` as the current active axis in `fig`"
function current_axis!(fig::Figure, ax)
    if ax.parent !== fig
        error("This axis' parent is not the given figure")
    end
    fig.current_axis[] = ax
    ax
end
function current_axis!(fig::Figure, ::Nothing)
    fig.current_axis[] = nothing
end
function current_axis!(ax)
    fig = ax.parent
    if !(fig isa Figure)
        error("Axis parent is not a figure but a $(typeof(ax.parent)). Only axes in figures can have current_axis! called on them.")
    end

    current_axis!(fig, ax)
    # if the current axis is in a different figure, we switch to that as well
    # so that current_axis and current_figure are not out of sync
    current_figure!(fig)
    ax
end

to_rectsides(n::Number) = to_rectsides((n, n, n, n))
to_rectsides(t::Tuple{Any, Any, Any, Any}) = GridLayoutBase.RectSides{Float32}(t...)

function Figure(; kwargs...)

    kwargs_dict = Dict(kwargs)
    padding = pop!(kwargs_dict, :figure_padding, current_default_theme()[:figure_padding])
    scene = Scene(; camera=campixel!, kwargs_dict...)
    padding = padding isa Observable ? padding : Observable{Any}(padding)

    alignmode = lift(Outside ∘ to_rectsides, padding)

    layout = GridLayout(scene)

    on(alignmode) do al
        layout.alignmode[] = al
        GridLayoutBase.update!(layout)
    end
    notify(alignmode)

    f = Figure(
        scene,
        layout,
        [],
        Attributes(),
        Ref{Any}(nothing)
    )
    # set figure as layout parent so GridPositions can refer to the figure
    # if connected correctly
    layout.parent = f
    f
end

export Figure, current_axis, current_figure, current_axis!, current_figure!

function Base.getindex(fig::Figure, rows, cols, side = GridLayoutBase.Inner())
    fig.layout[rows, cols, side]
end

function Base.setindex!(fig::Figure, obj, rows, cols, side = GridLayoutBase.Inner())
    fig.layout[rows, cols, side] = obj
    obj
end

function Base.setindex!(fig::Figure, obj::AbstractArray, rows, cols)
    fig.layout[rows, cols] = obj
    obj
end

Base.lastindex(f::Figure, i) = lastindex(f.layout, i)

# for now just redirect figure display/show to the internal scene
Base.show(io::IO, fig::Figure) = show(io, fig.scene)
Base.show(io::IO, ::MIME"text/plain", fig::Figure) = print(io, "Figure()")
# Base.show(io::IO, ::MIME"image/svg+xml", fig::Figure) = show(io, MIME"image/svg+xml"(), fig.scene)

get_figure(gsp::GridLayoutBase.GridSubposition) = get_figure(gsp.parent)
function get_figure(gp::GridLayoutBase.GridPosition)
    top_parent = GridLayoutBase.top_parent(gp.layout)
    if top_parent isa Figure
        top_parent
    else
        nothing
    end
end

"""
    resize_to_layout!(fig::Figure)

Resize `fig` so that it fits the current contents of its top `GridLayout`.
If a `GridLayout` contains fixed-size content or aspect-constrained
columns, for example, it is likely that the solved size of the `GridLayout`
differs from the size of the `Figure`. This can result in superfluous
whitespace at the borders, or content clipping at the figure edges.
Once resized, all content should fit the available space, including
the `Figure`'s outer padding.
"""
function resize_to_layout!(fig::Figure)
    bbox = GridLayoutBase.tight_bbox(fig.layout)
    new_size = (widths(bbox)...,)
    resize!(fig.scene, widths(bbox)...)
    new_size
end


text:
using FractionalDiffEq, Plots, LaTeXStrings

s="\$D^{0.5}y(x)=1-y,\\ y(0)=0\$"

fun(x, y) = 1-y
prob=FDEProblem(fun, 0.5, 0, 5, 0.01)
result=solve(prob, PECE())
tspan=collect(0:0.01:5)

plot(tspan, result, title=s, linewidth=2, legend=:bottomright)


text:
# do not remove this first line
using PkgPage

#
# Feel free to add whatever custom hfun_* or lx_*
# you might want to use in your site here
#


text:
# julia1_2.3.jl Primitive, Abstract and Composite Types
#
# Primitive Types
# Integer Types with maxnumber
println(Int8(127))
println(UInt8(255))
println(Int16(32767))
println(UInt16(65535))
println(Int32(2147483647))
println(UInt32(4294067295))
println(Int64(2)^63-1)
println(UInt64(2)^64-1)
#  Int is default for Int64 if a 64 bit CPU else 32
# UInt is default for UInt64 if a 64 bit CPU else 32
println(Int128(2)^127-1)
println(UInt128(2)^128-1)
println(BigInt(2)^2046-1)

# floating point Types and irrational numbers
# Float16 to Float64 and BigFloat
println(Float16(π))  # 11 bits of precision
println(Float32(π))  # 24 bits of precision
println(Float64(π))  # 53 bits of precision
println(BigFloat(π)) # 256 bits of precision - normally
precision(BigFloat)
println(π)           # irrational number cannot be represented as a rational
BigFloat(π)          #Bigfloat defaults to 256 binary places ~77 decimal places
BigFloat(π,500)      # 500+ bits of precision

# complex numbers
println(Complex{Float16}(π+2π*im)) # 11 bits precision each part
println(Complex{Float32}(π+2π*im)) # 24 bits precision each part
println(Complex{Float64}(π+2π*im)) # 53 bits precision each part
# full gamut of elementary functions work on complex numbers
# Complex shortcuts
ComplexF16(3.1+3.2im)  # Alias for Complex{Float16}()
ComplexF32(3.1+3.2im)  # Alias for Complex{Float32}()
ComplexF64(3.1+3.2im)  # Alias for Complex{Float64}()
Complex(3.1+3.2im)     #default for ComplexF64 if 64 bit CPU else ComplexF32

# Rational numbers
# used for exact arithmetic
# for example 1/3 cannot be represented exactly by any
#  floating point number.
# 1//3 represents the rational number 1/3 exactly
a = 1//3 + 2//5 - 3//7
b = (22//7)*(5//13)
c = ((22//7)/2)^2
print("a=$a, b=$b, c=$c")
Rational(3.875) # Make a rational fraction out of a Float
Rational(3.825) # a repeating binary pattern so not exact, use rationalize() function
BigFloat(3.825) # gives wrong precision due to prior conversion step
BigFloat(3825//1000) # or
BigFloat("3.825")


text:
using Dates
using JuliaDB

path = "./test/Sample data/PatientOverview_test.xlsx"
sheet = "Sheet1"
startdate = Date("2019-01-01")
enddate = Date("2019-02-01")
mastercalendar = HP.MasterCalendar(startdate,enddate)

testpath_resources = "./test/Sample data/resourcetest_output.csv"
testpath_patient = "./test/Sample data/patienttest_output.csv"
testpath_visits = "./test/Sample data/visittest_output.csv"
testpath_timeslots = "./test/Sample data/timeslottest_output.csv"
path_TimeDelta = "./test/Sample data/GUCHamb_timeDelta.csv"

patients = HP.loadpatients(testpath_patient)
visits = HP.loadvisits(testpath_visits)
resources = HP.loadresources(testpath_resources)
timeslots = HP.loadtimeslots(testpath_timeslots)
timeDelta = HP.loadTimeDelta(path_TimeDelta)

mp, subs,sets = HP.columngeneration(patients,visits,resources,timeslots,mastercalendar,timeDelta; multithreading = false,setuponly=false)

plannedappointments = HP.extractsolution(mp,sets,timeslots,visits)
pass = true
@test HP.allvisits(visits,plannedappointments)
@test HP.correctorder(plannedappointments,timeslots,timeDelta)
@test HP.daysused(mp,plannedappointments)
@test HP.deadline()


text:
"""
    function updates_!(p)

Function that checks the variables in the model that are modified at each step in order to make appropiate copy vectors and add them to program.
"""
function updates_!(p::Program_)

    ##Assign updates of variable types
    for t in keys(p.agent.declaredSymbols)
        dict = Dict{Symbol,Int}()
        counter = 1
        for i in p.agent.declaredSymbols[t]

            found = false
            for up in keys(p.agent.declaredUpdates)
                code =  postwalk(x->@capture(x, c_ = f_) && c == i ? :ARGS_ : x , p.agent.declaredUpdates[up])
                code =  postwalk(x->@capture(x, c_ += f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ -= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ *= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ /= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ \= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ ÷= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ %= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ ^= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ &= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ |= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ ⊻= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ >>>= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ >>= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_ <<= f_) && c == i ? :ARGS_ : x , code)

                if inexpr(code,:ARGS_)
                    found = true
                    break
                end
            end

            #Add symbols that are assigned
            for up in keys(p.agent.declaredUpdates)
                code =  postwalk(x->@capture(x, c_.g_ = f_) && c == i && g in INTERACTIONSYMBOLS ? :ARGS_ : x , p.agent.declaredUpdates[up])

                if inexpr(code,:ARGS_)
                    found = true
                    break
                end
            end

            for up in keys(p.agent.declaredUpdates)
                code =  postwalk(x->@capture(x, c_[g__] = f_) && c == i ? :ARGS_ : x , p.agent.declaredUpdates[up])
                code =  postwalk(x->@capture(x, c_[g__] += f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] -= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] *= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] /= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] \= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] ÷= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] %= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] ^= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] &= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] |= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] ⊻= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] >>>= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] >>= f_) && c == i ? :ARGS_ : x , code)
                code =  postwalk(x->@capture(x, c_[g__] <<= f_) && c == i ? :ARGS_ : x , code)

                if inexpr(code,:ARGS_)
                    found = true
                    break
                end
            end

            if "Equation" in keys(p.agent.declaredUpdates)
                code =  postwalk(x->@capture(x, g_(c_) = f_) && c == i && g == DIFFSYMBOL ? :ARGS_ : x , p.agent.declaredUpdates["Equation"])
                if inexpr(code,:ARGS_)
                    found = true
                end
            end

            if "UpdateMedium" in keys(p.agent.declaredUpdates)
                code =  postwalk(x->@capture(x, g_(c_) = f_) && c == i && g == DIFFMEDIUMSYMBOL ? :ARGS_ : x , p.agent.declaredUpdates["UpdateMedium"])
                if inexpr(code,:ARGS_)
                    found = true
                end
            end

            #Add symbols from boundaries
            if t == "Local"
                if i in p.agent.boundary.addSymbols
                    found = true
                end
            end

            if found
                dict[i] = counter
                counter += 1
            end
        end
        p.update[t] = dict
    end

    dict = Dict{Symbol,Int}()
    counter = 1
    for i in p.agent.declaredSymbols["Local"]
        found = false
        if "Equation" in keys(p.agent.declaredUpdates)
            code =  postwalk(x->@capture(x, g_(c_) = f_) && c == i && g == DIFFSYMBOL ? :ARGS_ : x , p.agent.declaredUpdates["Equation"])
            if inexpr(code,:ARGS_)
                found = true
            end
        end

        if found
            dict[i] = counter
            counter += 1
        end
    end
    p.update["Variables"] = dict

    return
end