text:
# computation using analytical expression

# define cache
struct SL_cache{cP1type, cP2type, cprobtype, cpredxtype, cweighttype, clabeltype, crestype}
  cP1::cP1type
  cP2::cP2type
  cprob::cprobtype
  cpredx::cpredxtype
  cweight::cweighttype
  clabel::clabeltype
  cres::crestype
end

# get optimal predictions for specific sample
function get_pred_opt_x_SL!(x, distribution, p_range_1, p_range_2, SLc)
  @unpack cP1, cP2, cprob, cpredx, cweight, clabel, cres = SLc

  cP1[1] = zero(eltype(cP1))
  cP2[1] = zero(eltype(cP2))

  for p in p_range_1
    cP1[1] += distribution(x, p)
  end

  for p in p_range_2
    cP2[1] += distribution(x, p)
  end

  if cP1[1] + cP2[1] == zero(eltype(cP2[1]))

    # can detect inputs not contained in training data
    return zero(eltype(cP2[1]))
  else

    # adjust depending on the choice of data labelling
    # alternative: cP2[1]/(cP1[1]+cP2[1])
    return cP1[1]/(cP1[1]+cP2[1])
  end
end

# adjust labels and weights based on value of the tuning parameter p_tar
function l_add!(p_min, p_max, p_tar, cweight, clabel)
  if p_tar <= p_min
    cweight[1] = one(eltype(cweight[1]))
    clabel[1]  = one(eltype(clabel[1]))
  elseif p_tar >= p_max
    cweight[1] = one(eltype(cweight[1]))
    clabel[1]  = zero(eltype(clabel[1]))
  else
    cweight[1] = zero(eltype(cweight[1]))
    clabel[1]  = zero(eltype(clabel[1]))
  end
  return nothing
end

# get mean optimal predictions for fixed value of tuning parameter
function get_pred_opt_p_SL!(samples, distribution, p_range_1, p_range_2, p_tar, SLc)
  @unpack cP1, cP2, cprob, cpredx, cweight, clabel, cres = SLc

  cres[1] = zero(eltype(cres[1]))

  l_add!(p_range_1[end], p_range_2[1], p_tar, cweight, clabel)
  cres[3] = cres[3] + cweight[1]
  for x in samples
    cprob[1] = distribution(x, p_tar)
    cpredx[1] = get_pred_opt_x_SL!(x, distribution, p_range_1, p_range_2, SLc)
    if cpredx[1] == zero(eltype(cres[1]))
    end

    # weight contribution cpredx[1] of each sample by its probability cprob[1]
    cres[1] += cprob[1]*cpredx[1]

    # add up loss
    cres[2] += cweight[1]*cprob[1]*crossentropy(cpredx[1], clabel[1])
  end
  return nothing
end

# compute optimal predictions and indicators, as well as optimal loss of SL
function get_indicators_SL_analytical(samples, distribution, p_range, dp, p_min, p_max)

  # define range of tuning parameter containing training data
  p_range_1 = Tuple(collect(p_range[1]:dp:p_min))
  if p_range[end] == Inf
    p_range_2 = Tuple(p_range[end])
  else
    p_range_2 = Tuple(collect(p_max:dp:p_range[end]))
  end

  pred_SL_opt = zeros(eltype(dp),length(p_range))
  loss = zero(eltype(dp))

  caches=[SL_cache([zero(eltype(dp))], [zero(eltype(dp))], [zero(eltype(dp))], [zero(eltype(dp))], [zero(eltype(dp))], [zero(eltype(dp))], [zero(eltype(dp)), zero(eltype(dp)), zero(eltype(dp))]) for i in 1:Threads.nthreads()]

  # start parallel computation for sampled values of tuning parameter
  Threads.@threads for indxp in collect(1:length(p_range))
    get_pred_opt_p_SL!(samples, distribution, p_range_1, p_range_2, p_range[indxp], caches[Threads.threadid()])
    pred_SL_opt[indxp] = caches[Threads.threadid()].cres[1]
  end

  cres2 = zero(eltype(dp))
  cres3 = zero(eltype(dp))
  for i in 1:Threads.nthreads()
    cres2 += caches[i].cres[2]
    cres3 += caches[i].cres[3]
  end

  # compute indicator using symmetric difference quotient
  return pred_SL_opt, (-1*(circshift(pred_SL_opt, -1).-circshift(pred_SL_opt, 1))./(2*dp))[2:end-1], cres2/cres3
end

# computation using neural networks

# loss function where each prediction is weighted by probability associated with input
function main_loss_SL_weighted(NN, pnn, data, p_range, p_min, p_max, inputs)
  indices = convert.(Int, data[1, :])
  input = inputs[:, indices]

  pred = NN(pnn)(input)
  pred = Flux.softmax(pred)
  loss = zero(eltype(p_range[1]))
  for i in 1:size(pred)[2]
    if p_range[Int(data[3, i])] <=p_min
      loss += data[2, i]*MLP.crossentropy(pred[1, i], one(eltype(loss)))
    elseif p_range[Int(data[3,i])] >=p_max
      loss += data[2, i]*MLP.crossentropy(pred[1, i], zero(eltype(loss)))
    else
      error("wrong asignment of training data")
    end
  end
  return loss
end

# loss function without weighting
function main_loss_SL_stochastic(NN, pnn, data, p_range, p_min, p_max, inputs)
  indices = convert.(Int, data[1,:])
  input = inputs[:, indices]

  pred = NN(pnn)(input)
  pred = Flux.softmax(pred)
  loss = zero(eltype(p_range[1]))
  for i in 1:size(pred)[2]
    if p_range[Int(data[3, i])] <= p_min
      loss += MLP.crossentropy(pred[1, i], one(eltype(loss)))
    elseif p_range[Int(data[3, i])] >= p_max
      loss += MLP.crossentropy(pred[1, i], zero(eltype(loss)))
    else
      error("wrong asignment of training data")
    end
  end
  return loss/length(data[3, :])
end

# train NN in SL based on weighted loss function
function train_SL_weighted(NN, pnn, data_train, data_test, epochs, p_range, dp, p_min, p_max, opt, batchsize, n_batches_train, n_batches_test, len_p_train, inputs; verbose=false, saveat=epochs)
  NN_logger = [zeros(eltype(p_range[1]), length(pnn))]
  pred_logger = [(zeros(eltype(p_range[1]), length(p_range)), zeros(eltype(p_range[1]), length(p_range)-2), zeros(eltype(p_range[1]), 1))]

  pnn_best = zeros(eltype(pnn[1]), length(pnn))
  best_loss = zero(eltype(pnn[1]))

  losses = zeros(eltype(p_range[1]), epochs)
  indices = collect(1:size(data_train)[2])
  for epoch in 1:epochs
    Random.shuffle!(indices)

    # compute loss and gradient batchwise
    grad = zeros(eltype(p_range[1]), length(pnn))
    for batch in 1:n_batches_train
      randint = get_batches(indices, batchsize, batch)
      data = reshape(data_train[:, randint], 3, length(randint))
      val, back = Flux.Zygote.pullback(p -> main_loss_SL_weighted(NN, p, data, p_range, p_min, p_max, inputs), pnn)
      grad .+= back(one(val))[1]
      losses[epoch] += val
    end

    # update NN parameters based on overall gradient
    Flux.Optimise.update!(opt, pnn, grad./len_p_train)
    losses[epoch] =losses[epoch]/len_p_train

    # keep track of best performing NN
    if epoch == 1
      best_loss = losses[epoch]
    elseif losses[epoch] < best_loss
      println("better")
      println(best_loss)
      println(losses[epoch])
      pnn_best = deepcopy(pnn)
      best_loss = losses[epoch]
    end

    # save at regular intervals
    if epoch % saveat == 0
      push!(NN_logger, pnn_best)
      push!(pred_logger, predict_SL(data_train, data_test, p_range, p_min, p_max, dp, NN, pnn_best, batchsize, n_batches_train, n_batches_test, len_p_train, inputs, calc_loss=true))
    end

    verbose && println("epoch: $epoch / $(epochs)")
    verbose && println("loss: "*string(losses[epoch]))
  end
  return losses, NN_logger, pred_logger
end

# train NN in SL based on ``stochastic'' loss function
function train_SL_stochastic(NN, pnn, data_train, data_test, epochs, p_range, dp, p_min, p_max,opt, batchsize, batchsize_stochastic, n_batches_train, n_batches_train_stochastic, n_batches_test, len_p_train, inputs; verbose=false, saveat=epochs)
  NN_logger = [zeros(eltype(p_range[1]), length(pnn))]
  pred_logger = [(zeros(eltype(p_range[1]), length(p_range)), zeros(eltype(p_range[1]), length(p_range)-2), zeros(eltype(p_range[1]), 1))]

  pnn_best = zeros(eltype(pnn[1]), length(pnn))
  best_loss = zero(eltype(pnn[1]))

  losses = zeros(eltype(p_range[1]), epochs)
  for epoch in 1:epochs

    # compute loss and gradient batchwise
    for batch in 1:n_batches_train_stochastic
      randint = sample(1:length(data_train[1, :]), Weights(data_train[2, :]), batchsize_stochastic)
      data = reshape(data_train[:, randint], 3, length(randint))
      val, back = Flux.Zygote.pullback(p -> main_loss_SL_stochastic(NN, p, data, p_range, p_min, p_max, inputs), pnn)
      grad = back(one(val))[1]

      # update NN parameters based on batch gradient
      Flux.Optimise.update!(opt, pnn, grad)
      losses[epoch] += val
    end
    losses[epoch] =losses[epoch]/n_batches_train_stochastic

    # keep track of best performing NN
    if epoch == 1
      best_loss = losses[epoch]
    elseif losses[epoch] < best_loss
      println("better")
      println(best_loss)
      println(losses[epoch])
      pnn_best = deepcopy(pnn)
      best_loss = losses[epoch]
    end

    # save at regular intervals
    if epoch % saveat == 0
      push!(NN_logger, pnn_best)
      push!(pred_logger, predict_SL(data_train, data_test, p_range, p_min, p_max, dp, NN, pnn_best, batchsize, n_batches_train, n_batches_test, len_p_train, inputs, calc_loss=true))
    end

    verbose && println("epoch: $epoch / $(epochs)")
    verbose && println("loss: "*string(losses[epoch]))
  end
  return losses, NN_logger, pred_logger
end

# compute predictions, indicators, and loss value based on current NN
function predict_SL(data_train, data_test, p_range, p_min, p_max, dp, NN, pnn, batchsize, n_batches_train, n_batches_test, len_p_train, inputs; calc_loss=false, loss=zero(eltype(p_range[1])))

  # compute predictions batch-wise
  predictions = zeros(eltype(p_range[1]), length(p_range))
  indices = collect(1:size(data_test)[2])
  for batch in 1:n_batches_test
    rand_int = get_batches(indices, batchsize, batch)
    data = reshape(data_test[:, rand_int], 3, length(rand_int))

    indicess = convert.(Int,data[1, :])
    input = inputs[:, indicess]

    pred = NN(pnn)(input)
    pred = Flux.softmax(pred)
    for indxp in 1:length(rand_int)
      predictions[Int(data[3, indxp])] += data[2, indxp]*pred[1, indxp]
    end
  end

  # compute loss batch-wise
  if calc_loss
    indices = collect(1:size(data_train)[2])
    for batch in 1:n_batches_train
      rand_int = get_batches(indices, batchsize, batch)
      data = reshape(data_train[:, rand_int],3,length(rand_int))
      loss += main_loss_SL_weighted(NN, pnn, data, p_range, p_min, p_max, inputs)
    end
    loss = loss/len_p_train
  end

  # compute indicator using symmetric difference quotient
  return predictions, -1*((circshift(predictions, -1).-circshift(predictions, 1))./(2*dp))[2:end-1], [loss]
end

# perform SL using neural networks
function get_indicators_SL_numerical(pnn, NN, data_train, data_test, epochs, p_range, dp, p_min, p_max, opt, inputs; verbose=false, trained=false, saveat=epochs, batchsize=length(data_train[1, :]), stochastic=false,  n_batches_train_stochastic=1, batchsize_stochastic=length(data_train[1, :]))

  n_batches_train = ceil(eltype(batchsize), size(data_train)[2]/batchsize)
  n_batches_test = ceil(eltype(batchsize), size(data_test)[2]/batchsize)
  len_p_train = length(collect(p_range[1]:dp:p_min))+length(collect(p_max:dp:p_range[end]))

  if !trained
    if stochastic
      losses, NN_logger, pred_logger = train_SL_stochastic(NN, pnn, data_train, data_test, epochs, p_range, dp, p_min, p_max, opt, batchsize, batchsize_stochastic, n_batches_train, n_batches_train_stochastic, n_batches_test, len_p_train, inputs, verbose=verbose, saveat=saveat)
    else
      losses, NN_logger, pred_logger = train_SL_weighted(NN, pnn, data_train, data_test, epochs, p_range, dp, p_min, p_max, opt, batchsize, n_batches_train, n_batches_test, len_p_train, inputs, verbose=verbose, saveat=saveat)
    end

    pred_logger = pred_logger[2:end]
    NN_logger = NN_logger[2:end]

  else
    predictions, indicator, loss = predict_SL(data_train, data_test, p_range, p_min, p_max, dp, NN, pnn, batchsize, n_batches_train, n_batches_test, len_p_train, inputs, calc_loss=true)

    losses = zeros(eltype(dp), epochs)
    NN_logger = [pnn]
    pred_logger = [(predictions, indicator, loss)]
  end

  return pred_logger, losses, NN_logger
end


text:
module SSpline

    using LinearAlgebra

    import Base: ==, +, -, *, /
    import Base: convert

    export  spline0, stepspline, nearestneighbor, nspline,
            spline1,linearspline, lspline,
            spline2, quadraticspline, qspline,
            spline3, cubicspline, cspline, cub,
            naturalspline, clampedspline, periodicspline,
            hermitespline, hspline,
            nakspline,
            interpolate, interp, extrapolate, extrap,
            deriv1, grad, slope,
            deriv2, curvature,
            deriv3, curvrate,
            CubicSpline, HermiteSpline, QuadraticSpline, LinearSpline, NearestNeighborSpline

    include("types.jl")
    include("splinefunctions.jl")
    include("interpolation.jl")
    include("extrapolation.jl")
    include("derivatives.jl")
end #module

ext: jl


text:
import Pkg

using Oceananigans: @hascuda, AbstractArchitecture

@hascuda using CUDAdrv

function print_benchmark_info()
    versioninfo()
    @hascuda begin
        dev = device(CuCurrentContext()) |> string
        gpu_name = split(dev, ":")[end] |> strip
        println("  GPU: $gpu_name")
    end
    println()
end

arch_name(::CPU) = "CPU"
arch_name(::GPU) = "GPU"

benchmark_name(N)               = benchmark_name(N, "", nothing, nothing)
benchmark_name(N, arch::Symbol) = benchmark_name(N, "", arch, nothing)
benchmark_name(N, ft::DataType) = benchmark_name(N, "", nothing, ft)

function benchmark_name(N, id, arch, ft; npad=3)
    Nx, Ny, Nz = N
    print_arch = typeof(arch) <: AbstractArchitecture ? true : false
    print_ft   = typeof(ft) == DataType && ft <: AbstractFloat ? true : false

    bn = ""
    bn *= lpad(Nx, npad, " ") * "×" * lpad(Ny, npad, " ") * "×" * lpad(Nz, npad, " ")
    bn *= " $id"

    if print_arch && print_ft
        arch = arch_name(arch)
        bn *= " ($arch, $ft)"
    elseif print_arch && !print_ft
        arch = arch_name(arch)
        bn *= " ($arch)"
    elseif !print_arch && print_ft
        bn *= " ($ft)"
    end

    return bn
end


text:
module TestCMAES
    using Evolutionary
    using Base.Test

    # Schwefel's ellipsoid test function:
    # a moderately conditioned ellipsoid with a dominating isolated eigenvalue
    function schwefel{T <: Vector}(x::T)
        out = 0
        for i = 1:length(x)
            out += sumabs2(x[1:i])
        end
        return out
    end

    # Parameters
    N = 30

    # Testing: CMA-ES
    result, fitness, cnt = cmaes(schwefel, N; μ = 3, λ = 12, iterations = 1000)
    println("(3/3,12)-CMA-ES (Schwefel) => F: $(fitness), C: $(cnt)")

    @test_approx_eq_eps result zeros(N) 1e-5
    @test_approx_eq_eps fitness 0.0 1e-5

end


text:
types_names = (
    (MangalReferenceTaxon, :backbone),
    (MangalNode, :node),
    (MangalNetwork, :network),
    (MangalDataset, :dataset),
    (MangalReference, :reference),
    (MangalInteraction, :interaction),
    (MangalAttribute, :attribute),
    (MangalUser, :user),
)

import Base.count

for mg_type_pair in types_names
    mg_type, mg_singular = mg_type_pair
    mg_plural = Symbol(string(mg_singular) * "s")
    @eval begin
        """
            count(::Type{$($mg_type)}, query::Pair...)

        Returns the number of $($mg_type) objects that match a query.
        """
        function count(::Type{$mg_type}, query::Pair...)
            return Mangal.number_of_objects(Mangal._MANGAL_ENDPOINTS[$mg_type], query...)
        end
    end
    if haskey(Mangal._MANGAL_CACHES, mg_type)
        @eval begin
            """
                $($mg_plural)(query::Pair...)

            This function will return objects of type $($mg_type) according to the query
            parameters. To accelerate future queries, the objects returned will be cached.

            To get the latest $($mg_type) records, this function can be called with no arguments.
            """
            function $mg_plural(query::Pair...)
                results = search_objects_by_query($mg_type, query...)
                Mangal.cache(results)
                return results
            end

            """
                $($mg_singular)(id::Int64)

            Returns the object of type $($mg_type) whose identifier is `id`.
            """
            function $mg_singular(id::Int64)
                if haskey(Mangal._MANGAL_CACHES[$mg_type], id)
                    return Mangal._MANGAL_CACHES[$mg_type][id]
                else
                    return first($mg_plural(Pair("id", id)))
                end
            end
        end
    else
        @eval begin
            """
                $($mg_plural)(query::Pair...)

            This function will return objects of type $($mg_type) according to the query
            parameters.

            To get the latest $($mg_type) records, this function can be called with no arguments.
            """
            function $mg_plural(query::Pair...)
                results = search_objects_by_query($mg_type, query...)
                return results
            end

            """
                $($mg_singular)(id::Int64)

            Returns the object of type $($mg_type) whose identifier is `id`.
            """
            function $mg_singular(id::Int64)
                return first($mg_plural(Pair("id", id)))
            end
        end
    end
end


text:
@testset "Crop" begin
    @test (Crop <: AugmentorBase.AffineOperation) == false
    @testset "constructor" begin
        @test_throws MethodError Crop()
        @test_throws MethodError Crop(())
        @test typeof(@inferred(Crop(1:10))) <: Crop{1} <: Crop <: AugmentorBase.ArrayOperation
        @test typeof(@inferred(Crop(1:10,3:5))) <: Crop{2} <: Crop <: AugmentorBase.ArrayOperation
        @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)) == "AugmentorBase.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)) == "AugmentorBase.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)) == "AugmentorBase.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 AugmentorBase.applyeager(Crop(1:10), nothing)
        @test_throws MethodError AugmentorBase.applyeager(Crop(1:2,2:3), nothing)
        @test AugmentorBase.supports_eager(Crop) === false
        imgs = [
            (rect, (1:2, 2:3)),
            (AugmentorBase.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(AugmentorBase.applyeager(Crop(1:2,2:3), img_in))
                @test collect(res) == rect[1:2, 2:3]
                @test axes(res) == inds
                @test typeof(res) <: OffsetArray{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(AugmentorBase.applyeager(Crop(1:2,2:3), img_in))
                @test collect.(res) == ntuple(i->rect[1:2, 2:3],2)
                @test typeof(res) <: NTuple{2,OffsetArray{eltype(img_in1),2}}
            end
        end
    end
    @testset "affine" begin
        @test AugmentorBase.supports_affine(Crop) === false
    end
    imgs = [
        (rect),
        (AugmentorBase.prepareaffine(rect)),
        (OffsetArray(rect, -2, -1)),
        (view(rect, IdentityRange(1:2), IdentityRange(1:3))),
    ]
    @testset "affineview" begin
        @test AugmentorBase.supports_affineview(Crop) === true
        @test_throws MethodError AugmentorBase.applyaffineview(Crop(1:2,2:3), nothing)
        @test @inferred(AugmentorBase.applyaffineview(Crop(1:2,2:3), rect)) ==
            view(AugmentorBase.prepareaffine(rect), IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for img_in in imgs
                res1, res2 = @inferred(AugmentorBase.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 AugmentorBase.supports_lazy(Crop) === true
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_view(Crop) === true
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_stepview(Crop) === true
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_permute(Crop) === false
    end
end

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

@testset "CropNative" begin
    @test (CropNative <: AugmentorBase.AffineOperation) == false
    @testset "constructor" begin
        @test_throws MethodError CropNative()
        @test_throws MethodError CropNative(())
        @test typeof(@inferred(CropNative(1:10))) <: CropNative{1} <: CropNative <: AugmentorBase.ArrayOperation
        @test typeof(@inferred(CropNative(1:10,3:5))) <: CropNative{2} <: CropNative <: AugmentorBase.ArrayOperation
        @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)) == "AugmentorBase.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)) == "AugmentorBase.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)) == "AugmentorBase.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)),
        (AugmentorBase.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 AugmentorBase.applyeager(CropNative(1:10), nothing)
        @test_throws MethodError AugmentorBase.applyeager(CropNative(1:2,2:3), nothing)
        @test AugmentorBase.supports_eager(CropNative) === false
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(AugmentorBase.applyeager(CropNative(inds), img_in))
                @test collect(res) == rect[1:2, 2:3]
                @test axes(res) == inds
                @test typeof(res) <: OffsetArray{eltype(img_in),2}
            end
        end
        img = OffsetArray(rect, -2, -1)
        @test collect(@inferred(AugmentorBase.applyeager(CropNative(-1:0,1:2), img))) == rect[1:2, 2:3]
        @test typeof(AugmentorBase.applyeager(CropNative(-1:0,1:2), img)) <: OffsetArray
    end
    @testset "affine" begin
        @test AugmentorBase.supports_affine(CropNative) === false
    end
    @testset "affineview" begin
        @test AugmentorBase.supports_affineview(CropNative) === true
        @test_throws MethodError AugmentorBase.applyaffineview(CropNative(1:2,2:3), nothing)
        @test @inferred(AugmentorBase.applyaffineview(CropNative(1:2,2:3), rect)) ==
            view(AugmentorBase.prepareaffine(rect), IdentityRange(1:2), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(AugmentorBase.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 AugmentorBase.supports_lazy(CropNative) === true
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_view(CropNative) === true
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_stepview(CropNative) === true
        @test @inferred(AugmentorBase.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[1:3]
               res1, res2 = @inferred(AugmentorBase.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 AugmentorBase.supports_permute(CropNative) === false
    end
end

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

@testset "CropSize" begin
    @test (CropSize <: AugmentorBase.AffineOperation) == false
    @test typeof(@inferred(CropSize())) <: CropSize <: AugmentorBase.ArrayOperation
    @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) == "AugmentorBase.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) == "AugmentorBase.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) == "AugmentorBase.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(AugmentorBase.cropsize_axes(CropSize(2,2), square)) == [1:2, 1:2]
        @test @inferred(AugmentorBase.cropsize_axes(CropSize(2,2), square2)) == [2:3, 2:3]
        @test @inferred(AugmentorBase.cropsize_axes(CropSize(2,2), checkers)) == [1:2, 2:3]
        @test @inferred(AugmentorBase.cropsize_axes(CropSize(1,3), checkers)) == [2:2, 2:4]
        @test @inferred(AugmentorBase.cropsize_axes(CropSize(3,3), checkers)) == [1:3, 2:4]
        @test @inferred(AugmentorBase.cropsize_axes(CropSize(2,2), OffsetArray(rect, -2, -1))) == [-1:0, 0:1]
    end
    imgs = [
        (rect, (1:2, 1:2)),
        (AugmentorBase.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)),
        (AugmentorBase.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 AugmentorBase.applyeager(CropSize(10), nothing)
        @test_throws MethodError AugmentorBase.applyeager(CropSize(2,3), nothing)
        @test AugmentorBase.supports_eager(CropSize) === false
        @test @inferred(AugmentorBase.applyeager(CropSize(4,4), square2)) == square2
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(AugmentorBase.applyeager(CropSize(2,2), img_in))
                @test collect(res) == img_in[inds...]
                @test axes(res) == inds
                @test typeof(res) <: OffsetArray{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(AugmentorBase.applyeager(CropSize(2,2), img_in))
                @test collect.(res) == ntuple(i->img_in[i][inds[i]...],2)
                @test typeof(res) <: NTuple{2,OffsetArray{eltype(img_in1),2}}
            end
        end
    end
    @testset "affine" begin
        @test AugmentorBase.supports_affine(CropSize) === false
    end
    @testset "affineview" begin
        @test AugmentorBase.supports_affineview(CropSize) === true
        @test_throws MethodError AugmentorBase.applyaffineview(CropSize(1:2,2:3), nothing)
        @test @inferred(AugmentorBase.applyaffineview(CropSize(2,3), rect)) ==
            view(AugmentorBase.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(AugmentorBase.applyaffineview(CropSize(2,2), square2)) ==
            view(AugmentorBase.prepareaffine(square2), IdentityRange(2:3), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(AugmentorBase.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 AugmentorBase.supports_lazy(CropSize) === true
        @test @inferred(AugmentorBase.applylazy(CropSize(2,3), rect)) ===
            view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_view(CropSize) === true
        @test @inferred(AugmentorBase.applyview(CropSize(2,3), rect)) ===
            view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_stepview(CropSize) === true
        @test @inferred(AugmentorBase.applystepview(CropSize(2,3), rect)) === view(rect, 1:1:2, 1:1:3)
        @test @inferred(AugmentorBase.applystepview(CropSize(2,2), square2)) === view(square2, 2:1:3, 2:1:3)
    end
    @testset "permute" begin
        @test AugmentorBase.supports_permute(CropSize) === false
    end
end

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

@testset "CropRatio" begin
    @test (CropRatio <: AugmentorBase.AffineOperation) == false
    @test typeof(@inferred(CropRatio())) <: CropRatio <: AugmentorBase.ArrayOperation
    @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) == "AugmentorBase.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) == "AugmentorBase.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) == "AugmentorBase.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(AugmentorBase.cropratio_axes(CropRatio(1), square)) == UnitRange{Int64}[1:3, 1:3]
        @test @inferred(AugmentorBase.cropratio_axes(CropRatio(2), square)) == UnitRange{Int64}[2:2, 1:3]
        @test @inferred(AugmentorBase.cropratio_axes(CropRatio(1), square2)) == UnitRange{Int64}[1:4, 1:4]
        @test @inferred(AugmentorBase.cropratio_axes(CropRatio(1), rect)) == UnitRange{Int64}[1:2, 1:2]
        @test @inferred(AugmentorBase.cropratio_axes(CropRatio(1), checkers)) == UnitRange{Int64}[1:3, 2:4]
        @test @inferred(AugmentorBase.cropratio_axes(CropRatio(1), rotl90(checkers))) == UnitRange{Int64}[2:4, 1:3]
        @test @inferred(AugmentorBase.cropratio_axes(CropRatio(1), OffsetArray(rect, -2, -1))) == UnitRange{Int64}[-1:0, 0:1]
    end
    imgs = [
        (rect, (1:2, 1:2)),
        (AugmentorBase.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)),
        (AugmentorBase.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 AugmentorBase.applyeager(CropRatio(10), nothing)
        @test_throws MethodError AugmentorBase.applyeager(CropRatio(2), nothing)
        @test AugmentorBase.supports_eager(CropRatio) === false
        @test @inferred(AugmentorBase.applyeager(CropRatio(1), rect)) == rect[1:2,1:2]
        @test @inferred(AugmentorBase.applyeager(CropRatio(1), square)) == square
        @test @inferred(AugmentorBase.applyeager(CropRatio(1), square2)) == square2
        @testset "single image" begin
            for (img_in, inds) in imgs
                res = @inferred(AugmentorBase.applyeager(CropRatio(1), img_in))
                @test collect(res) == img_in[inds...]
                @test axes(res) == inds
                @test typeof(res) <: OffsetArray{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(AugmentorBase.applyeager(CropRatio(1), img_in))
                @test collect.(res) == ntuple(i->img_in[i][inds[i]...],2)
                @test typeof(res) <: NTuple{2,OffsetArray{eltype(img_in1),2}}
            end
        end
    end
    @testset "affine" begin
        @test AugmentorBase.supports_affine(CropRatio) === false
    end
    @testset "affineview" begin
        @test AugmentorBase.supports_affineview(CropRatio) === true
        @test_throws MethodError AugmentorBase.applyaffineview(CropRatio(1), nothing)
        @test @inferred(AugmentorBase.applyaffineview(CropRatio(1), rect)) ==
            view(AugmentorBase.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:2))
        @test @inferred(AugmentorBase.applyaffineview(CropRatio(2), square2)) ==
            view(AugmentorBase.prepareaffine(square2), IdentityRange(2:3), IdentityRange(1:4))
        @test @inferred(AugmentorBase.applyaffineview(CropRatio(.5), square2)) ==
            view(AugmentorBase.prepareaffine(square2), IdentityRange(1:4), IdentityRange(2:3))
        @testset "multiple images" begin
            for (img_in, inds) in imgs
                res1, res2 = @inferred(AugmentorBase.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 AugmentorBase.supports_lazy(CropRatio) === true
        @test @inferred(AugmentorBase.applylazy(CropRatio(1), rect)) ===
            view(rect, IdentityRange(1:2), IdentityRange(1:2))
        @test @inferred(AugmentorBase.applylazy(CropRatio(2), square2)) ===
            view(square2, IdentityRange(2:3), IdentityRange(1:4))
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_view(CropRatio) === true
        @test @inferred(AugmentorBase.applyview(CropRatio(1), rect)) ===
            view(rect, IdentityRange(1:2), IdentityRange(1:2))
        @test @inferred(AugmentorBase.applyview(CropRatio(2), square2)) ===
            view(square2, IdentityRange(2:3), IdentityRange(1:4))
        @test @inferred(AugmentorBase.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(AugmentorBase.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 AugmentorBase.supports_stepview(CropRatio) === true
        @test @inferred(AugmentorBase.applystepview(CropRatio(1), rect)) === view(rect, 1:1:2, 1:1:2)
        @test @inferred(AugmentorBase.applystepview(CropRatio(2), square2)) === view(square2, 2:1:3, 1:1:4)
        @test @inferred(AugmentorBase.applystepview(CropRatio(.5), square2)) === view(square2, 1:1:4, 2:1:3)
    end
    @testset "permute" begin
        @test AugmentorBase.supports_permute(CropRatio) === false
    end
end

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

@testset "RCropRatio" begin
    @test (RCropRatio <: AugmentorBase.AffineOperation) == false
    @test typeof(@inferred(RCropRatio())) <: RCropRatio <: AugmentorBase.ArrayOperation
    @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) == "AugmentorBase.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) == "AugmentorBase.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) == "AugmentorBase.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(AugmentorBase.randparam(RCropRatio(1), square)) == (1:3, 1:3)
        @test @inferred(AugmentorBase.randparam(RCropRatio(1), rect)) in ((1:2, 2:3), (1:2, 1:2))
        @test @inferred(AugmentorBase.randparam(RCropRatio(1), (rect,square2))) in ((1:2, 2:3), (1:2, 1:2))
    end
    imgs = [
        (rect, (1:2, 1:3)),
        (AugmentorBase.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 AugmentorBase.applyeager(RCropRatio(10), nothing)
        @test_throws MethodError AugmentorBase.applyeager(RCropRatio(2), nothing)
        @test AugmentorBase.supports_eager(RCropRatio) === false
        @test @inferred(AugmentorBase.applyeager(RCropRatio(1), square)) == square
        @test @inferred(AugmentorBase.applyeager(RCropRatio(1), square2)) == square2
        out = collect(@inferred(AugmentorBase.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(AugmentorBase.applyeager(RCropRatio(3/2), img_in))
                @test collect(res) == img_in[inds...]
                @test axes(res) == inds
                @test typeof(res) <: OffsetArray{eltype(img_in),2}
            end
        end
        @testset "multiple images" begin
            for (img_in, _) in imgs
                res1, res2 = @inferred(AugmentorBase.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) <: OffsetArray{Gray{N0f8}}
                @test typeof(res2) <: OffsetArray{N0f8}
            end
        end
    end
    @testset "affine" begin
        @test AugmentorBase.supports_affine(RCropRatio) === false
    end
    @testset "affineview" begin
        @test AugmentorBase.supports_affineview(RCropRatio) === true
        @test_throws MethodError AugmentorBase.applyaffineview(RCropRatio(1), nothing)
        # preserve aspect ratio (i.e. not random)
        @test @inferred(AugmentorBase.applyaffineview(RCropRatio(3/2), rect)) ==
            view(AugmentorBase.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(AugmentorBase.applyaffineview(RCropRatio(1), square)) ==
            view(AugmentorBase.prepareaffine(square), IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(AugmentorBase.applyaffineview(RCropRatio(1), square2)) ==
            view(AugmentorBase.prepareaffine(square2), IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred AugmentorBase.applyaffineview(RCropRatio(1), rect)
        @test out == view(AugmentorBase.prepareaffine(rect), IdentityRange(1:2), IdentityRange(1:2)) ||
            out == view(AugmentorBase.prepareaffine(rect), IdentityRange(1:2), IdentityRange(2:3))
        out = @inferred AugmentorBase.applyaffineview(RCropRatio(2/3), square)
        @test out == view(AugmentorBase.prepareaffine(square), IdentityRange(1:3), IdentityRange(1:2)) ||
            out == view(AugmentorBase.prepareaffine(square), IdentityRange(1:3), IdentityRange(2:3))
        out = @inferred AugmentorBase.applyaffineview(RCropRatio(3/2), square)
        @test out == view(AugmentorBase.prepareaffine(square), IdentityRange(1:2), IdentityRange(1:3)) ||
            out == view(AugmentorBase.prepareaffine(square), IdentityRange(2:3), IdentityRange(1:3))
    end
    @testset "lazy" begin
        @test AugmentorBase.supports_lazy(RCropRatio) === true
        # preserve aspect ratio (i.e. not random)
        @test @inferred(AugmentorBase.applylazy(RCropRatio(3/2), rect)) ===
            view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(AugmentorBase.applylazy(RCropRatio(1), square)) ===
            view(square, IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(AugmentorBase.applylazy(RCropRatio(1), square2)) ===
            view(square2, IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred AugmentorBase.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 AugmentorBase.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 AugmentorBase.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 AugmentorBase.supports_view(RCropRatio) === true
        # preserve aspect ratio (i.e. not random)
        @test @inferred(AugmentorBase.applyview(RCropRatio(3/2), rect)) ===
            view(rect, IdentityRange(1:2), IdentityRange(1:3))
        @test @inferred(AugmentorBase.applyview(RCropRatio(1), square)) ===
            view(square, IdentityRange(1:3), IdentityRange(1:3))
        @test @inferred(AugmentorBase.applyview(RCropRatio(1), square2)) ===
            view(square2, IdentityRange(1:4), IdentityRange(1:4))
        # randomly placed
        out = @inferred AugmentorBase.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 AugmentorBase.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 AugmentorBase.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 AugmentorBase.supports_stepview(RCropRatio) === true
        # preserve aspect ratio (i.e. not random)
        @test @inferred(AugmentorBase.applystepview(RCropRatio(3/2), rect)) === view(rect, 1:1:2, 1:1:3)
        @test @inferred(AugmentorBase.applystepview(RCropRatio(1), square)) === view(square, 1:1:3, 1:1:3)
        @test @inferred(AugmentorBase.applystepview(RCropRatio(1), square2)) === view(square2, 1:1:4, 1:1:4)
        # randomly placed
        out = @inferred AugmentorBase.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 AugmentorBase.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 AugmentorBase.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(AugmentorBase.supports_permute(RCropRatio)) === false
    end
end


text:
"""
Module with general utilities and missing standard library features that could be useful in any Julia project
"""
module Utils
using StatsFuns
import StatsFuns.logsumexp

export copy_with_eltype, issomething, flatmap, map_something, ntimes, some_vector, always, never,
assign, accumulate_val, accumulate_prod, accumulate_prod_normalized, assign_prod,
assign_prod_normalized, prod_fast, count_conjunction, sum_weighted_product,
order_asc, @no_error, disjoint, typejoin, lower_element_type, map_values, groupby, logsumexp,
unzip, uniform, pushrand!,
Node, DagNode, TreeNode, DiGraph, Dag, Tree,
NodeType, Leaf, Inner,
inodes, innernodes, leafnodes, children, num_children, has_children, num_nodes, num_edges,
node_stats, leaf_stats, inode_stats, tree_num_nodes, node2dag, dag2node, grapheltype,
isequal_unordered, isequal_local, pre_order_traverse,
left_most_child, right_most_child, isleaf, isinner, lca, parent, descends_from,
foldup, foldup_rec, foreach_rec, flip_bit, foldup_aggregate, foldup_aggregate_rec,
folddown_aggregate, folddown_aggregate_rec

import Base.@time
import Base.print
import Base.println

# various utilities

"""
Is the argument not `nothing`?
"""
@inline issomething(x) = !isnothing(x)

@inline map_something(f,v) = (v == nothing) ? nothing : f(v)

ntimes(f,n) = (for i in 1:n-1; f(); end; f())

@inline order_asc(x, y) = x > y ? (y, x) : (x , y)

macro no_error(ex)
    quote
        try
            $(esc(ex))
            true
        catch
            false
        end
    end
end

function disjoint(set1::AbstractSet, sets::AbstractSet...)::Bool
    seen = set1 # can be sped up by copying seen first then reusing it with union!?
    for set in sets
        if !isempty(intersect(seen,set))
            return false
        else
            seen = union(seen, set)
        end
    end
    return true
end

"Marginalize out dimensions `dims` from log-probability tensor"
function logsumexp(A::AbstractArray, dims)
    return dropdims(mapslices(StatsFuns.logsumexp, A, dims=dims), dims=dims)
end

macro unzip(x)
    quote
        local a, b = zip($(esc(x))...)
        a = collect(a)
        b = collect(b)
        a, b
    end
end

"""
Push element into random position
"""
function pushrand!(v::AbstractVector{<:Any}, element)
    len = length(v)
    i = rand(1:len + 1)
    if i == len + 1
        push!(v, element)
    else
        splice!(v, i:i, [element, v[i]])
    end
    v
end

#####################
# array parametric type helpers
#####################

"""
Copy the array while changing the element type
"""
copy_with_eltype(input, Eltype::Type) = copyto!(similar(input, Eltype), input)

import Base.typejoin

"Get the most specific type parameter possible for an array"
Base.typejoin(array::AbstractArray) = mapreduce(e -> typeof(e), typejoin, array)

"Specialize the type parameter of an array to be most specific"
function lower_element_type(array::AbstractArray{T}) where T
    U = typejoin(array)
    if T == U
        return array
    else
        return copy_with_eltype(array, U)
    end
end

#####################
# probability semantics and other initializers for various data types
#####################

@inline always(::Type{T}, dims::Int...) where T<:Number = ones(T, dims...)
@inline always(::Type{T}, dims::Int...) where T<:Bool = trues(dims...)

@inline never(::Type{T}, dims::Int...) where T<:Number = zeros(T, dims...)
@inline never(::Type{T}, dims::Int...) where T<:Bool = falses(dims...)

@inline some_vector(::Type{T}, dims::Int...) where T<:Number = Vector{T}(undef, dims...)
@inline some_vector(::Type{T}, dims::Int...) where T<:Bool = BitArray(undef, dims...)

@inline uniform(dims::Int...) = ones(Float64, dims...) ./ prod(dims)

#####################
# functional programming
#####################

# Your regular flatmap
# if you want the return array to have the right element type, provide an init with the desired type. Otherwise it may become Array{Any}
@inline flatmap(f, arr::AbstractVector, init=[]) = mapreduce(f, append!, arr; init=init)

function map_values(f::Function, dict::AbstractDict{K}, vtype::Type)::AbstractDict{K,vtype} where K
    mapped_dict = Dict{K,vtype}()
    for key in keys(dict)
        mapped_dict[key] = f(dict[key])
    end
    mapped_dict
end

function groupby(f::Function, list::Union{Vector{E},Set{E}})::Dict{Any,Vector{E}} where E
    groups = Dict{Any,Vector{E}}()
    for v in list
        push!(get!(groups, f(v), []), v)
    end
    groups
end

#####################
# compute kernels
#####################

include("Kernels.jl")

#####################
# graphs
#####################

include("Graphs.jl")

end #module


text:
using Test
using MARY_fRG.Triangulated, MARY_fRG.Basics
using MARY_fRG.Drawers

#rts = Triangulated.split_single_square(Square(Point2D(0., 0.), 1.))
#
#plt = draw_figure(
#    [rt.center for rt in rts], nothing, rts;
#    ptext = 1:1:4
#)
#
#@savepng plt "sss"

@testset "正方形的切割" begin
    squ1 = Square(Point2D(0., 0.), 2.)
    ltris, ladjs = split_square(squ1, 10)
    #plt = draw_figure(
    #    nothing, nothing, ltris
    #)
    #@savepng plt "ss1"
    tri2idx = Dict(((tri, idx) for (idx, tri) in enumerate(ltris)))
    #pts = vcat([ltris[1].center; ladjs[1][1].center])#; ladjs[1][2].center; ladjs[1][3].center])
    #draw_points!(plt, pts;
    #text=[tri2idx[ltris[1]], tri2idx[ladjs[1][1]]])
    #@savepng plt "ss2"
    @test tri2idx[ladjs[202][1]] == 102
    @test ismissing(ladjs[202][2])
    @test tri2idx[ladjs[202][3]] == 302
    @test tri2idx[ladjs[300][1]] == 200
    @test tri2idx[ladjs[300][2]] == 90
    @test tri2idx[ladjs[300][3]] == 400
    println(ladjs[310])
end


text:
module Exports

# include("FElements.jl")
# include("FEFields.jl")
# include("FESpaces.jl")
# include("QPIterators.jl")
# include("FEIterators.jl")

###############################################################################
using ..LocalAssemblers: LocalMatrixAssembler, init!
using ..LocalAssemblers: LocalVectorAssembler, init!

export LocalMatrixAssembler, init!
export LocalVectorAssembler, init!

###############################################################################
using ..Assemblers: AbstractSysmatAssembler, SysmatAssemblerSparse
using ..Assemblers: AbstractSysvecAssembler, SysvecAssembler
using ..Assemblers: start!, assemble!, finish!

export AbstractSysmatAssembler, SysmatAssemblerSparse
export AbstractSysvecAssembler, SysvecAssembler
export start!, assemble!, finish!

###############################################################################
using ..RefShapes: AbstractRefShape, RefShapePoint, RefShapeInterval, RefShapeSquare, RefShapeCube, RefShapeTriangle, RefShapeTetrahedron
using ..RefShapes: manifdimv
using ..RefShapes: IntegRule, npts, param_coords, weights, quadrature

export AbstractRefShape, RefShapePoint, RefShapeInterval, RefShapeSquare, RefShapeCube, RefShapeTriangle, RefShapeTetrahedron, manifdimv
export IntegRule, npts, param_coords, weights, quadrature

###############################################################################
using ..FElements
using ..FElements: FE, FEData
using ..FElements: shapedesc, refshape, nfeatofdim, ndofperfeat, ndofsperel, manifdim, Jacobian, jacjac, bfun, bfungradpar
using ..FElements: FEH1_L2, FEH1_T3, FEH1_T6, FEH1_Q4, FEH1_T3_BUBBLE, FEH1_T4
using ..FElements: FEL2_Q4, FEL2_T3, FEL2_T4

export FE, FEData
export shapedesc, refshape, nfeatofdim, ndofperfeat, ndofsperel, manifdim, Jacobian, jacjac, bfun, bfungradpar
export FEH1_L2, FEH1_T3, FEH1_T6, FEH1_Q4, FEH1_T3_BUBBLE, FEH1_T4
export FEL2_Q4, FEL2_T3, FEL2_T4

###############################################################################
import ..FESpaces: FESpace
import ..FESpaces: doftype, edofmdim, edofbfnum, edofcompnt
import ..FESpaces: ndofsperel, dofnum, numberfreedofs!, numberdatadofs!, ndofs
import ..FESpaces: nunknowns, highestfreedofnum, highestdatadofnum
import ..FESpaces: numberdofs!, setebc!, setdofval!
import ..FESpaces: gathersysvec!, scattersysvec!, makeattribute

export FESpace
export doftype, edofmdim, edofbfnum, edofcompnt
export ndofsperel, dofnum, numberfreedofs!, numberdatadofs!, ndofs
export nunknowns, highestfreedofnum, highestdatadofnum
export numberdofs!, setebc!, setdofval!
export gathersysvec!, scattersysvec!, makeattribute

###############################################################################
using ..QPIterators: QPIterator
using ..QPIterators: bfun, bfungrad, bfungradpar, weight

export QPIterator
export bfun, bfungrad, bfungradpar, weight

###############################################################################
using ..FEIterators: FEIterator
using ..FEIterators: jacjac, location, ndofsperel, elnodes, eldofs, eldofentmdims, eldofcomps, eldofvals

export FEIterator
export jacjac, location, ndofsperel, elnodes, eldofs, eldofentmdims, eldofcomps, eldofvals

end


text:
using Gnuplot
function ngonShape(h, k, r, n)
    x = [h .+ r*sin.(m*2π/n) for m in 1:n]
    y = [k .+ r*cos.(m*2π/n) for m in 1:n]
    x, y
end
function strpolygon(x, y)
    pt0 = "$(x[1]),$(y[1]) to"
    for p in 2:length(x)
       pt0=pt0*" $(x[p]),$(y[p]) to"
    end
    pt0[1:end-3]
end
#rectangle with filledcu
x1 = [-0.5,0.5,0.5,-0.5]
y1 = [0.55,0.55,3.45,3.45]
@gp x1 y1 "w filledcu notit lc 'grey'" yrange=(0.5,3.5) xrange=(-0.7,6.5)
@gp :- "set style fill transparent solid 0.5" "set size ratio -1"
#polygon from gnuplot
c = 1
for i in 1:6, j in 1:3
    global c
    x, y = ngonShape(i, j, 0.45, 2 + c)
    @gp :- "set obj $(c) polygon from $(strpolygon(x, y))" :-
    @gp :- "set obj $(c) fc lt $(c) fill transparent solid 0.3" :-
    @gp :- "set label font ',12' '$(2+c)' at $(i-0.05), $(j)" :-
    c += 1
end
@gp
save(term ="pngcairo size 600,400", output ="polygons.png")

max_stars_repo_name: lazarusA/gnuplot-examples

max_issues_repo_name: lazarusA/gnuplot-examples

max_forks_repo_name: lazarusA/gnuplot-examples


text:
@inline field_arguments(i, j, k, grid, model_fields, ℑ, idx::NTuple{1}) =
    @inbounds (ℑ[1](i, j, k, grid, model_fields[idx[1]]),)

@inline field_arguments(i, j, k, grid, model_fields, ℑ, idx::NTuple{2}) =
    @inbounds (ℑ[1](i, j, k, grid, model_fields[idx[1]]),
               ℑ[2](i, j, k, grid, model_fields[idx[2]]))

@inline field_arguments(i, j, k, grid, model_fields, ℑ, idx::NTuple{3}) =
    @inbounds (ℑ[1](i, j, k, grid, model_fields[idx[1]]),
               ℑ[2](i, j, k, grid, model_fields[idx[2]]),
               ℑ[3](i, j, k, grid, model_fields[idx[3]]))

@inline field_arguments(i, j, k, grid, model_fields, ℑ, idx::NTuple{N}) where N =
    @inbounds ntuple(n -> ℑ[n](i, j, k, grid, model_fields[idx[n]]), Val(N))

""" Returns field arguments in user-defined functions for forcing and boundary conditions."""
@inline function user_function_arguments(i, j, k, grid, model_fields, ::Nothing, user_func)

    ℑ = user_func.field_dependencies_interp
    idx = user_func.field_dependencies_indices

    return field_arguments(i, j, k, grid, model_fields, ℑ, idx)
end

""" Returns field arguments plus parameters in user-defined functions for forcing and boundary conditions."""
@inline function user_function_arguments(i, j, k, grid, model_fields, parameters, user_func)

    ℑ = user_func.field_dependencies_interp
    idx = user_func.field_dependencies_indices
    parameters = user_func.parameters

    field_args = field_arguments(i, j, k, grid, model_fields, ℑ, idx)

    return tuple(field_args..., parameters)
end


text:
using Flux, Images
using Random
using StatsBase: sample, shuffle
using Base.Iterators

const PATH = joinpath(@__FILE__, "train")
const FILES = joinpath.(PATH, readdir(PATH))
if isempty(readdir(PATH))
  error("Empty train folder - perhaps you need to download and extract the kaggle dataset.")
end

const DOGS = filter(x -> occursin("dog", x), FILES)
const CATS = filter(x -> occursin("cat", x), FILES)

function load_batch(n = 10, nsize = (224,224); path = PATH)

  imgs_paths = shuffle(vcat(sample(DOGS, Int(n/2)), sample(CATS, Int(n/2))))
  labels = map(x -> occursin("dog.",x) ? 1 : 0, imgs_paths)
  labels = Flux.onehotbatch(labels, [0,1])
  imgs = Images.load.(imgs_paths)
  imgs = map(img -> Images.imresize(img, nsize...), imgs)
  imgs = map(img -> permutedims(channelview(img), (3,2,1)), imgs)
  imgs = cat(imgs..., dims = 4)
  Float32.(imgs), labels
end

max_stars_repo_name: thomascherickal/model-zoo

max_issues_repo_name: thomascherickal/model-zoo

max_forks_repo_name: thomascherickal/model-zoo


text:
using MultipleViewGeometry, Base.Test
using MultipleViewGeometry.ModuleCostFunction
using MultipleViewGeometry.ModuleTypes
using BenchmarkTools, Compat
using StaticArrays

𝒳 = [Point3DH(x,y,z,1.0)
                        for x=-1000:5:1000 for y=-1000:5:1000 for z=1:-5:-1000]

# Intrinsic and extrinsic parameters of camera one.
𝐊₁ = eye(3)
𝐑₁ = eye(3)
𝐭₁ = [0.0, 0.0, 0.0]

# Intrinsic and extrinsic parameters of camera two.
𝐊₂ = eye(3)
𝐑₂ = eye(3)
𝐭₂ = [100.0, 2.0, -100.0]

# Camera projection matrices.
𝐏₁ = construct(ProjectionMatrix(),𝐊₁,𝐑₁,𝐭₁)
𝐏₂ = construct(ProjectionMatrix(),𝐊₂,𝐑₂,𝐭₂)

# Set of corresponding points.
ℳ = project(Pinhole(),𝐏₁,𝒳)
ℳʹ = project(Pinhole(),𝐏₂,𝒳)

@time ℳ = project(Pinhole(),𝐏₁,𝒳)
@time ℳʹ = project(Pinhole(),𝐏₂,𝒳)

#@btime  project(Pinhole(),𝐏₂,𝒳)

#@time ℳ = project(Pinhole(),SMatrix{3,4}(𝐏₁),𝒳)
#@time ℳʹ = project(Pinhole(),SMatrix{3,4}(𝐏₂),𝒳)

#@btime project(Pinhole(),SMatrix{3,4}(𝐏₂),𝒳)


text:
Base.show(io::IO, c::Contract) = _show_tree(io, c)
Base.show(io::IO, o::Observable) = _show_tree(io, o)

_show_root(io::IO, x) = print(io, x)
_children(x) = ()

_show_root(io::IO, c::Contract) = printstyled(io, string(typeof(c).name.name), color=:magenta)
_children(c::Contract) = (getfield(c,i) for i = 1:nfields(c))

_show_root(io::IO, o::Observable) = printstyled(io, string(typeof(o).name.name), color=:cyan)
_children(o::Observable) = (getfield(o,i) for i = 1:nfields(o))

_show_root(io::IO, o::ConstObs) = printstyled(io, string(o.val), color=:yellow)
_children(o::ConstObs) = ()

function _show_root(io::IO, o::LiftObs)
    printstyled(io, "{", color=:cyan)
    print(io,o.f)
    printstyled(io, "}", color=:cyan)
end
_children(o::LiftObs) = o.a

function _show_tree(io::IO, x, indent_root="", indent_leaf="")
    print(io, indent_root)
    _show_root(io, x)
    print(io, '\n')
    cs = _children(x)
    for (i,c) in enumerate(cs)
        if i < length(cs)
            _show_tree(io, c, indent_leaf*" ├─",indent_leaf*" │ ")
        else
            _show_tree(io, c, indent_leaf*" └─",indent_leaf*"   ")
        end
    end
end

function Base.show(io::IO, m::GeomBMModel{C}) where C<:CoreModel
    str = "Geometric Brownian Motion Model"
    print(io, str)
    print(io, "\n")
    println(io, prod(fill("-", length(str))))
    println(io, "S₀ = $(m.core.startprice)")
    println(io, "T = $(m.core.yieldcurve.reference_date)")
    print(io, "Yield "); println(io, m.core.yieldcurve)
    print(io, "Carry "); println(io, m.core.carrycurve)
    println(io, "σ = $(m.volatility)")
end

function Base.show(io::IO, m::GeomBMModel{C}) where C<:CoreForwardModel
    str = "Geometric Brownian Motion Model"
    print(io, str)
    print(io, "\n")
    println(io, prod(fill("-", length(str))))
    println(io, "Forward-based prices")
    println(io, "T = $(m.core.yieldcurve.reference_date)")
    print(io, "Yield "); println(io, m.core.yieldcurve)
    println(io, "σ = $(m.volatility)")
end

function Base.show(io::IO, x::ConstantYieldCurve)
    print(io, "Constant Curve with r = $(x.rate), T = $(x.reference_date) and $(x.compounding) Compounding")
end

function Base.show(io::IO, x::ConstantContinuousYieldCurve)
    print(io, "Constant Continuous Curve with r = $(x.rate), T = $(x.reference_date) ")
end

function Base.show(io::IO, m::CoreModel)
    str = "Core Model"
    print(io, str)
    print(io, "\n")
    println(io, prod(fill("-", length(str))))
    println(io, "S₀ = $(m.startprice)")
    println(io, "T = $(m.yieldcurve.reference_date)")
    print(io, "Yield "); println(io, m.yieldcurve)
    print(io, "Carry "); println(io, m.carrycurve)
end

function Base.show(io::IO, m::MonteCarloModel)
    str = "Monte Carlo Model"
    print(io, str)
    print(io, "\n")
    println(io, prod(fill("-", length(str))))
    str = "every"
    if m.dates.step.value == 1
        str *= "day"
    else
        str *= " $(m.dates.step)"
    end
    println(io, "$(size(m.paths, 1)) Simulations $str from $(first(m.dates)) to $(last(m.dates))")
    print(io, "Yield "); println(io, m.core.yieldcurve)
end

function Base.show(io::IO, m::T) where T<:AbstractModel
    print(io, T)
end


text:
"""
    ElectronOutput(init, ruleset::Ruleset; kwargs...)

A html output using Interact.jl and an Electron window through Blink.jl
ElectronOutput automatically generates sliders to control simulations
in realtime. args and kwargs are passed to [`InteractOutput`](@ref).

## Example
```julia
using Blink
ElectronOutput(init, ruleset; tspan=(1, 100))
```

### Arguments

- `init`: initialisation array, or `NamedTuple` of arrays
- `ruleset::Ruleset`: A DynamicGrids `Ruleset`

Keyword arguments are passed to [`InteractOutput`](@ref).
"""
mutable struct ElectronOutput{T,F,I<:InteractOutput{T,F}} <: AbstractInteractOutput{T,F}
    interface::I
    window::Blink.AtomShell.Window
end
function ElectronOutput(; kwargs...)
    interface = InteractOutput(; kwargs...)
    window = _newelectronwindow(interface)
    ElectronOutput(interface, window)
end

interface(o::ElectronOutput) = o.interface

# Forward output methods to InteractOutput: ElectronOutput is just a wrapper.
Base.length(o::ElectronOutput) = length(interface(o))
Base.size(o::ElectronOutput) = size(interface(o))
Base.firstindex(o::ElectronOutput) = firstindex(interface(o))
Base.lastindex(o::ElectronOutput) = lastindex(interface(o))
Base.getindex(o::ElectronOutput, i::Union{Int,AbstractVector,Colon}) =
    getindex(interface(o), i)
Base.setindex!(o::ElectronOutput, x, i::Union{Int,AbstractVector,Colon}) =
    setindex!(interface(o), x, i)
Base.push!(o::ElectronOutput, x) = push!(interface(o), x)
Base.append!(o::ElectronOutput, x) = append!(interface(o), x)

# DynamicGrids.jl interface
DG.frames(o::ElectronOutput) = DG.frames(interface(o))
DG.init(o::ElectronOutput) = DG.init(interface(o))
DG.aux(o::ElectronOutput) = DG.aux(interface(o))
DG.mask(o::ElectronOutput) = DG.mask(interface(o))
DG.extent(o::ElectronOutput) = DG.extent(interface(o))
DG.graphicconfig(o::ElectronOutput) = DG.graphicconfig(interface(o))
DG.imageconfig(o::ElectronOutput) = DG.imageconfig(interface(o))
DG.stoppedframe(o::ElectronOutput) = DG.stoppedframe(interface(o))
DG.tspan(o::ElectronOutput) = DG.tspan(interface(o))
DG.fps(o::ElectronOutput) = DG.fps(interface(o))
DG.isasync(o::ElectronOutput) = DG.isasync(interface(o))
DG.isstored(o::ElectronOutput) = DG.isstored(interface(o))
DG.store(o::ElectronOutput) = DG.store(interface(o))
DG.isshowable(o::ElectronOutput) = DG.isshowable(interface(o))

DG.setfps!(o::ElectronOutput, x) = DG.setfps!(interface(o), x)
DG.setrunning!(o::ElectronOutput, x) = DG.setrunning!(interface(o), x)
DG.settspan!(o::ElectronOutput, x) = DG.settspan!(interface(o), x)
DG.settimestamp!(o::ElectronOutput, x) = DG.settimestamp!(interface(o), x)
DG.setstoppedframe!(o::ElectronOutput, x) = DG.setstoppedframe!(interface(o), x)
DG.initialise!(o::ElectronOutput, args...) = DG.initialise!(interface(o), args...)
DG.finalise!(o::ElectronOutput, args...) = DG.finalise!(interface(o), args...)
DG.initialisegraphics(o::ElectronOutput, args...) = DG.initialisegraphics(interface(o), args...)
DG.finalisegraphics(o::ElectronOutput, args...) = DG.finalisegraphics(interface(o), args...)
DG.delay(o::ElectronOutput, x) = DG.delay(interface(o), x)

DG.minval(o::ElectronOutput) = DG.minval(interface(o))
DG.maxval(o::ElectronOutput) = DG.maxval(interface(o))
DG.processor(o::ElectronOutput) = DG.processor(interface(o))

DG.storeframe!(o::ElectronOutput, data::DG.AbstractSimData) =
    DG.storeframe!(interface(o), data)
DG.showframe(o::ElectronOutput, data::DG.AbstractSimData, args...) =
    DG.showframe(interface(o), data, args...)

# Running checks depend on the blink window still being open
DG.isrunning(o::ElectronOutput) = _isalive(o) && DG.isrunning(interface(o))

_isalive(o::ElectronOutput) = o.window.content.sock.state == WebSockets.ReadyState(1)

# Display

function Base.display(o::ElectronOutput)
    if !_isalive(o)
        o.window = _newelectronwindow(interface(o))
    end
    return nothing
end

function _newelectronwindow(interface)
    window = Blink.AtomShell.Window()
    body!(window, interface.page)
    return window
end


text:
# This file is a part of JuliaFEM.
# License is MIT: see https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md

# # Generating local matrices for problems

using JuliaFEM

# Plane stress Quad4 element with linear material model:

# In JuliaFEM the plane stress element can be defined using Quad4 element which
# has four nodes. First we need to define geometry. Geometry is defined with
# node number and coordinates into a dictionary.

X = Dict(1 => [0.0, 0.0],
         2 => [2.0, 0.0],
         3 => [2.0, 2.0],
         4 => [0.0, 2.0])

# Element is created using Element(element_type, connectivity) -function. Here
# we create a Quad4 element which is connected to nodes 1-4.

element = Element(Quad4, [1, 2, 3, 4])

# Element properties are defined using update!(element, field, value) -function.
# To create stiffness matrix we need to define geometry, Young's modulus and
# Poisson's ratio.

update!(element, "geometry", X)
update!(element, "youngs modulus", 288.0)
update!(element, "poissons ratio", 1/3)

# Then we have to create a problem using
# Problem(problem_type, name, number_of_dofs_per_node) -function. Problem type
# in this case is Elasticity and number of dofs per node is two in 2D problem.
# In the problem.properties.formulation we must define whether we are using
# :plane_stress or :plane_strain formulation.

problem = Problem(Elasticity, "example elasticity problem", 2)
problem.properties.formulation = :plane_stress

# Elements need to added to the problem using add_elements!(problem, element)
# -function.

add_elements!(problem, [element])

# Normally next thing to do after defining problems would be running the analysis
# but now we are only interested in stiffness matrix. We have to assemble the
# matrix using assemble!(problem, time) -function.

assemble!(problem, 0.0)

# Now we have the stiffness matrix in problem.assembly.K you may type it to
# console and see that it is in sparse matrix form. We can write it as normal
# matrix with full() -function.

K = full(problem.assembly.K)
display(K)

# This is not necessary but we can round it with round() -function.

K = round(K,2)
display(K)