text:
@testset "PointForecast" begin
    @testset "constructor" begin
        timestamps     = collect(DateTime(2000):Hour(1):DateTime(2000, 2, 1))
        forecast       = rand(length(timestamps))
        point_forecast = PointForecast("teste", timestamps, forecast)
        @test isa(point_forecast, PointForecast)

        timestamps     = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        forecast       = rand(length(timestamps))
        point_forecast = PointForecast("teste", timestamps, forecast)
        @test isa(point_forecast, PointForecast)

        timestamps     = collect(DateTime(2000):Second(1):DateTime(2000, 1, 1, 1, 1, 1))
        forecast       = rand(length(timestamps))
        point_forecast = PointForecast("teste", timestamps, forecast)
        @test isa(point_forecast, PointForecast)

        timestamps     = collect(Date(2000):Day(1):Date(2000, 2, 1))
        forecast       = rand(length(timestamps))
        point_forecast = PointForecast("teste", timestamps, forecast)
        @test isa(point_forecast, PointForecast)

        timestamps     = collect(Date(2000):Month(1):Date(2000, 12, 1))
        forecast       = rand(length(timestamps))
        point_forecast = PointForecast("teste", timestamps, forecast)
        @test isa(point_forecast, PointForecast)

        timestamps = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        forecast   = rand(length(timestamps))[1:end - 1]
        @test_throws DimensionMismatch PointForecast("teste", timestamps, forecast)

        timestamps = [DateTime(2001); DateTime(2000, 2, 1)]
        forecast   = [1; 1]
        @test_throws ErrorException PointForecast("teste", timestamps, forecast)

        timestamps = [DateTime(2001); DateTime(2001, 1, 1)]
        forecast   = [1; 1]
        @test_throws ErrorException PointForecast("teste", timestamps, forecast)
    end
    @testset "forecast_metrics" begin
        timestamps     = collect(DateTime(2000):Hour(1):DateTime(2000, 2, 1))
        vals           = rand(length(timestamps))
        forecast       = rand(length(timestamps))
        real_ts        = TimeSeries("teste", timestamps, vals)
        point_forecast = PointForecast("teste", timestamps, forecast)
        perfect_forecast = PointForecast("teste", timestamps, vals)

        point_forecast_metrics = forecast_metrics(point_forecast, real_ts)
        @test isa(point_forecast_metrics, PointForecastMetrics)
        @test all(point_forecast_metrics.absolute_percentage_errors .>= 0)

        perfect_forecast_metrics = forecast_metrics(perfect_forecast, real_ts)
        @test all(perfect_forecast_metrics.errors .== 0)
        @test all(perfect_forecast_metrics.absolute_percentage_errors .== 0)

        real_ts.vals[3] = 0
        warn_msg = "The real observations have values too close to zero. This makes the " *
                   "absolute percentage error impractical, NaNs will be returned."

        @test_logs (:warn, warn_msg) forecast_metrics(point_forecast, real_ts)

        point_forecast_metrics = forecast_metrics(point_forecast, real_ts)
        @test isnan(point_forecast_metrics.absolute_percentage_errors[1])

        point_forecast = PointForecast("teste", timestamps[1:end - 1], forecast[1:end - 1])
        @test_throws DimensionMismatch forecast_metrics(point_forecast, real_ts)
    end
end

@testset "ScenariosForecast" begin
    @testset "constructor" begin
        timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 2, 1))
        scenarios               = rand(length(timestamps), 10)
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)
        @test isa(scenarios_forecast, ScenariosForecast)

        timestamps              = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        scenarios               = rand(length(timestamps), 10)
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)
        @test isa(scenarios_forecast, ScenariosForecast)

        timestamps              = collect(DateTime(2000):Second(1):DateTime(2000, 1, 1, 1, 1, 1))
        scenarios               = rand(length(timestamps), 10)
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)
        @test isa(scenarios_forecast, ScenariosForecast)

        timestamps              = collect(Date(2000):Day(1):Date(2000, 2, 1))
        scenarios               = rand(length(timestamps), 10)
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)
        @test isa(scenarios_forecast, ScenariosForecast)

        timestamps              = collect(Date(2000):Month(1):Date(2000, 12, 1))
        scenarios               = rand(length(timestamps), 10)
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)
        @test isa(scenarios_forecast, ScenariosForecast)

        timestamps              = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        scenarios               = rand(length(timestamps), 10)[1:end - 1, :]
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        @test_throws DimensionMismatch ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

        timestamps              = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        scenarios               = rand(length(timestamps), 10)
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)[1:end - 1, :]
        @test_throws DimensionMismatch ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

        timestamps              = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        scenarios               = rand(length(timestamps), 10)
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 1)
        @test_throws DimensionMismatch ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

        timestamps              = [DateTime(2001); DateTime(2000, 2, 1)]
        scenarios               = [1.0 2.0; 3.0 4.0]
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        @test_throws ErrorException ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

        timestamps = [DateTime(2001); DateTime(2001, 1, 1)]
        scenarios               = [1.0 2.0; 3.0 4.0]
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        @test_throws ErrorException ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

    end
    @testset "forecast_metrics" begin
        @testset "probabilistic_calibration" begin
            # probabilistic_calibration
            timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 1, 1, 24))
            scenarios               = vcat([collect(1.0:100)' for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

            vals    = 0.9 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)
            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)
            @test scen_forecast_metrics.probabilistic_calibration[1][0.025] == true
            @test scen_forecast_metrics.probabilistic_calibration[1][0.975] == true
            @test scen_forecast_metrics.probabilistic_calibration[end][0.025] == true
            @test scen_forecast_metrics.probabilistic_calibration[end][0.975] == true

            vals    = 50 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)
            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)
            @test scen_forecast_metrics.probabilistic_calibration[1][0.025] == false
            @test scen_forecast_metrics.probabilistic_calibration[1][0.475] == false
            @test scen_forecast_metrics.probabilistic_calibration[1][0.525] == true
            @test scen_forecast_metrics.probabilistic_calibration[1][0.975] == true
            @test scen_forecast_metrics.probabilistic_calibration[end][0.025] == false
            @test scen_forecast_metrics.probabilistic_calibration[end][0.475] == false
            @test scen_forecast_metrics.probabilistic_calibration[end][0.525] == true
            @test scen_forecast_metrics.probabilistic_calibration[end][0.975] == true

            real_ts = TimeSeries("teste", timestamps[1:end - 1], vals[1:end - 1])
            @test_throws DimensionMismatch forecast_metrics(scenarios_forecast, real_ts)
        end

        @testset "interval_width" begin
            # interval_width

            timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 1, 1, 24))
            scenarios               = vcat([collect(0.0:100)' for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

            vals    = 50 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)
            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.interval_width[1][0.95] ≈ 95 atol = 1e-8
            @test scen_forecast_metrics.interval_width[1][0.85] ≈ 85 atol = 1e-8
            @test scen_forecast_metrics.interval_width[1][0.75] ≈ 75 atol = 1e-8
            @test scen_forecast_metrics.interval_width[1][0.05] ≈ 5  atol = 1e-8

            scenarios_forecast = ScenariosForecast("teste",
                                                   timestamps,
                                                   fill(1.0, size(scenarios)),
                                                   quantiles_probabilities,
                                                   quantiles)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.interval_width[1][0.95] ≈ 0 atol = 1e-8
            @test scen_forecast_metrics.interval_width[1][0.85] ≈ 0 atol = 1e-8
            @test scen_forecast_metrics.interval_width[1][0.75] ≈ 0 atol = 1e-8
            @test scen_forecast_metrics.interval_width[1][0.05] ≈ 0 atol = 1e-8
        end

        @testset "crps" begin
            timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 1, 1, 24))
            scenarios               = vcat([collect(1.0:10)' for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

            vals    = 5 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test isapprox(scen_forecast_metrics.crps[1], 0.85, atol=1e-8)
            @test isapprox(scen_forecast_metrics.crps[end], 0.85, atol=1e-8)

            timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 1, 1, 24))
            scenarios               = vcat([100 * ones(100)' for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

            vals    = 100 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.crps[1] == 0
            @test scen_forecast_metrics.crps[end] == 0

            vals    = 75 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.crps[1] == 25
            @test scen_forecast_metrics.crps[end] == 25

            sample                  = [-0.199187226 -0.654257527 -0.288307156  0.418189042  0.197436432  1.895931546  1.017915723 -0.272472671 2.214909442  1.380922228  0.282614757  0.913507684 -0.053783258  1.881443019  0.103841771 -1.252779110 -0.132589256 -0.579265376 -0.583994395  0.002848462 -0.269945185 -0.456626860 -2.109125117  0.733358687 -1.504387776  0.056461675 -1.152835005  1.378847909 -1.337631414 -1.523305411]
            timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 1, 1, 24))
            scenarios               = vcat([sample for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

            vals    = 0 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.crps[1] ≈ 0.2302562 atol = 1e-5

            sample = [-0.6889628 -2.4344944  0.9186731  0.2957021 -2.4110891 -1.5940046 -0.9326339 -0.9258965  0.1313751 -0.4786784 -1.2069921 -0.5199624 -0.5428000  0.7640408  2.4055897 -0.6818087 -0.5684771  1.2199405  0.7852317 -1.0741016 0.8523647  0.2882272  0.1489849  0.5510892  2.2931913 -0.4039241  0.4205298  1.0697491  1.6286060 -0.5272440]
            timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 1, 1, 24))
            scenarios               = vcat([sample for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

            vals    = 0 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.crps[1] ≈ 0.3051423 atol = 1e-5

            vals    = 2 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.crps[1] ≈ 1.433508 atol = 1e-5

            sample = [-0.22081509 -1.86684550 -1.44745981 -0.29519517 -0.64409419 -1.10534094 -2.50302412 -0.85041688 -1.39955151 0.36355304 -1.04568980  0.06692555 -0.10243452  0.82107023 -0.12405433  0.44602407  0.21596285  0.54478824 0.18668522  0.17744940  0.11352589  2.07332063  1.32748061 -2.08552933 -0.30302669  0.09773248 -0.26601496 -0.17519691  0.93558685 -1.36265031]
            timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 1, 1, 24))
            scenarios               = vcat([sample for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)

            vals    = 0 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.crps[1] ≈ 0.2197104 atol = 1e-5

            vals    = 2 * ones(length(timestamps))
            real_ts = TimeSeries("teste", timestamps, vals)

            scen_forecast_metrics = forecast_metrics(scenarios_forecast, real_ts)

            @test scen_forecast_metrics.crps[1] ≈ 1.733258 atol = 1e-5
        end
        @testset "mean_of_metrics" begin
            timestamps              = collect(DateTime(2000):Year(1):DateTime(2001))
            scenarios               = vcat([collect(1.0:10)' for i in 1:length(timestamps)]...)
            quantiles_probabilities = [0.05; 0.95]
            quantiles               = TimeSeriesInterface.get_quantiles(quantiles_probabilities, scenarios)
            scenarios_forecast      = ScenariosForecast("teste", timestamps, scenarios, quantiles_probabilities, quantiles)
            real_ts                 = TimeSeries("teste", timestamps, [5.0; 5.0])
            metrics                 = forecast_metrics(scenarios_forecast, real_ts)

            mean_of_probabilistic_calibration,
            mean_of_interval_width,
            mean_of_crps = TimeSeriesInterface.mean_of_metrics([metrics; metrics])

            @test mean_of_crps == [0.85; 0.85]
        end
    end
end

@testset "QuantilesForecast" begin
    @testset "constructor" begin
        timestamps              = collect(DateTime(2000):Hour(1):DateTime(2000, 2, 1))
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        quantiles_forecast      = QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)
        @test isa(quantiles_forecast, QuantilesForecast)

        timestamps              = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        quantiles_forecast      = QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)
        @test isa(quantiles_forecast, QuantilesForecast)

        timestamps              = collect(DateTime(2000):Second(1):DateTime(2000, 1, 1, 1, 1, 1))
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        quantiles_forecast      = QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)
        @test isa(quantiles_forecast, QuantilesForecast)

        timestamps              = collect(Date(2000):Day(1):Date(2000, 2, 1))
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        quantiles_forecast      = QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)
        @test isa(quantiles_forecast, QuantilesForecast)

        timestamps              = collect(Date(2000):Month(1):Date(2000, 12, 1))
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        quantiles_forecast      = QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)
        @test isa(quantiles_forecast, QuantilesForecast)

        timestamps              = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)[1:end - 1, :]
        @test_throws DimensionMismatch QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)

        timestamps              = collect(DateTime(2000):Day(1):DateTime(2000, 2, 1))
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 1)
        @test_throws DimensionMismatch QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)

        timestamps              = [DateTime(2001); DateTime(2000, 2, 1)]
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        @test_throws ErrorException QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)

        timestamps = [DateTime(2001); DateTime(2001, 1, 1)]
        quantiles_probabilities = [0.05; 0.95]
        quantiles               = rand(length(timestamps), 2)
        @test_throws ErrorException QuantilesForecast("teste", timestamps, quantiles_probabilities, quantiles)

    end
end


text:
using Lib, Lib.Neat
using Test

@testset "Lib.jl" begin

    @testset "Neat" begin

        @testset "Connection" begin
            conn1 = Connection(1, 2)
            conn2 = copy(conn1)
            @test conn1 != conn2
        end

        @testset "Node" begin
            conns = Dict{String, Vector{Connection}}("in" => [], "out" => [])
            n1 = Node(1, INPUT::NodeType)
            n2 = copy(n1)
            @test n1 != n2
            #@test_throws AssertionError Node(1, INPUT::NodeType, "sigmoid", 1., conns)

        end

        @testset "Network" begin
            conf = Dict(
                "num_inputs" => 2,
                "num_outputs" => 1
            )

            net = Network(conf)
            #println(net)

        end
    end
end

ext: jl


text:
mutable struct SymbolContext
    current::Symbol # the service we're currently in
    needed::Base.Set{Symbol} # services required to be imported by the current service
    neededEnums::Base.Set{Tuple{Symbol, Symbol}} # service/enum combinations needed
end

abstract type ProcedureType end
struct Standard <: ProcedureType
    name::String
end
struct ServiceGetter <: ProcedureType
    name::String
end
struct ServiceSetter <: ProcedureType
    name::String
end
struct ClassGetter <: ProcedureType
    clss::String
    name::String
end
struct ClassSetter <: ProcedureType
    clss::String
    name::String
end
struct StaticMember <: ProcedureType
    clss::String
    name::String
end
struct Member <: ProcedureType
    clss::String
    name::String
end

function sanitizeName(kName::Symbol)
    if kName == :String
        return :kString
    elseif kName == :Bool
        return :kBool
    elseif kName == :function
        return :kFunction
    elseif kName == :end
        return :kEnd
    end
    return kName
end

function kerbal_name(name::String)
    return sanitizeName(Symbol(name))
end

function kRPCToJuliaType(kType::krpc.schema._Type, ctx::SymbolContext)
    kcode = kType.code
    types = kType.types
    if kcode == krpc.schema.Type_TypeCode.NONE # ?
        return Nothing()
    elseif kcode == krpc.schema.Type_TypeCode.DOUBLE return :Float64
    elseif kcode == krpc.schema.Type_TypeCode.FLOAT return :Float32
    elseif kcode == krpc.schema.Type_TypeCode.SINT32 return :Int32
    elseif kcode == krpc.schema.Type_TypeCode.SINT64 return :Int64
    elseif kcode == krpc.schema.Type_TypeCode.UINT32 return :UInt32
    elseif kcode == krpc.schema.Type_TypeCode.UINT64 return :UInt64
    elseif kcode == krpc.schema.Type_TypeCode.BOOL return :Bool
    elseif kcode == krpc.schema.Type_TypeCode.STRING return :String
    elseif kcode == krpc.schema.Type_TypeCode.BYTES return :(Array{UInt8, 1})
    elseif kcode == krpc.schema.Type_TypeCode.CLASS
        sname = Symbol(kType.service)
        cname = kerbal_name(kType.name)
        if sname == ctx.current
            return :(RemoteTypes.$cname)
        end
        push!(ctx.needed, sname)
        return :($sname.RemoteTypes.$cname)
    elseif kcode == krpc.schema.Type_TypeCode.ENUMERATION
        sname = Symbol(kType.service)
        emname = Symbol("Enum_" * kType.name)
        ename = Symbol("E"*kType.name)
        push!(ctx.neededEnums, (sname, ename))
        if sname == ctx.current
            return :($ename)
        end
        push!(ctx.needed, sname)
        return :($sname.$ename)
    elseif kcode == krpc.schema.Type_TypeCode.EVENT
        return :(kRPCTypes.kEvent)
    elseif kcode == krpc.schema.Type_TypeCode.PROCEDURE_CALL
        return :Request
    elseif kcode == krpc.schema.Type_TypeCode.STREAM
        return :(kRPCTypes.kStream)
    elseif kcode == krpc.schema.Type_TypeCode.STATUS
        return :(kRPCTypes.Service)
    elseif kcode == krpc.schema.Type_TypeCode.SERVICES
        return :(kRPCTypes.Services)
    elseif kcode == krpc.schema.Type_TypeCode.TUPLE
        return quote Tuple{$(map(x-> kRPCToJuliaType(x,ctx), types)...)} end
    elseif kcode == krpc.schema.Type_TypeCode.LIST
        return quote Array{$(kRPCToJuliaType(types[1],ctx)),1} end
    elseif kcode == krpc.schema.Type_TypeCode.SET
        return quote Base.Set{$(kRPCToJuliaType(types[1],ctx))} end
    elseif kcode == krpc.schema.Type_TypeCode.DICTIONARY
        kty = kRPCToJuliaType(types[1],ctx)
        vty = kRPCToJuliaType(types[2],ctx)
        return quote Dict{$kty, $vty} end
    end
end

function makeparam(kName::String)
    kName = Symbol(kName)
    if kName == :type
        return :ktype
    elseif kName == :begin
        return :kbegin
    elseif kName == :end
        return :kend
    elseif kName == :function
        return :kfunction
    end
    return kName
end

twopartTypeR = r"^([^_]+)_([^_]+)$"
threepartTypeR = r"^([^_]+)_([^_]+)_([^_]+)$"

function parseKind(name::String) :: ProcedureType
    if occursin(twopartTypeR, name)
        captures = match(twopartTypeR, name).captures
        if captures[1] == "get"
            return ServiceGetter(captures[2])
        elseif captures[1] == "set"
            return ServiceSetter(captures[2])
        else
            return Member(captures[1], captures[2])
        end
    elseif occursin(threepartTypeR, name)
        captures = match(threepartTypeR, name).captures
        if captures[2] == "static"
            return StaticMember(captures[1], captures[3])
        elseif captures[2] == "get"
            return ClassGetter(captures[1], captures[3])
        elseif captures[2] == "set"
            return ClassSetter(captures[1], captures[3])
        else
            throw("Unexpected method name $name encountered")
        end
    else
        return Standard(name)
    end
end

#=
struct Vessel <: kRPCTypes.Class
    id::Int
end
struct GetActiveVessel <: Request{:SpaceCenter, :get_ActiveVessel, Vessel} end
=#

function generate_documentation(fnname, raw_argnames, argtypes, docstring)
    if isempty(docstring) return "" end
    doc = parse_string(docstring)
    root = LightXML.root(doc)
    example_args = join(map((n,t)->"$n::$t", kerbal_name.(raw_argnames), MacroTools.striplines.(argtypes)), ", ")
    example_call = "    $(fnname)($example_args)"
    summary = content(find_element(root, "summary"))

    remarks = nothing
    for rmk in root["remarks"]
        remarks = replace(content(rmk), "\n"=>" ")
    end

    arg_typing = Dict(Pair.(raw_argnames, argtypes))
    arg_descs = String[]
    for param in root["param"]
        param_name = attribute(param, "name")
        param_typing = MacroTools.striplines(arg_typing[param_name])
        displayed_param_name = kerbal_name(param_name)
        push!(arg_descs, "- `$displayed_param_name::$param_typing`: $(replace(content(param), "\n"=>" "))")
    end

    return_desc = nothing
    for return_el in root["returns"]
        return_desc = replace(content(return_el), "\n"=>" ")
    end

    body = example_call * "\n"
    body *= summary * "\n"
    if length(arg_descs) > 0
        body *= "# Arguments\n"
        body *= join(arg_descs, "\n")
        body *= "\n"
    end

    if !isnothing(return_desc)
        body *= "# Returns\n"
        body *= return_desc * "\n"
    end

    if !isnothing(remarks)
        body *= "# Remarks\n"
        body *= remarks * "\n"
    end
    return body
end

function generate_wrapper_method(g::ClassGetter, proc, procname, ctx::SymbolContext)
    getter_name = Symbol(g.name)
    return getter_name, :($getter_name(this::$(kRPCToJuliaType(proc.parameters[1]._type, ctx))) = kerbal(this.conn, $procname(this)))
end
function generate_wrapper_method(g::ClassSetter, proc, procname, ctx::SymbolContext)
    setter_name = Symbol(g.name * "!")
    return setter_name, :($setter_name(this::$(kRPCToJuliaType(proc.parameters[1]._type, ctx)), value::$(kRPCToJuliaType(proc.parameters[2]._type, ctx))) = kerbal(this.conn, $procname(this, value)))
end

function make_param(argname, param, ctx)
    if !isnothing(param.default_value) && length(param.default_value) > 0
        return Expr(:kw, Expr(:(::), argname, kRPCToJuliaType(param._type, ctx)), :(getJuliaValue(this.conn, $(param.default_value), $(kRPCToJuliaType(param._type, ctx)))))
    else
        return :($(argname)::$(kRPCToJuliaType(param._type, ctx)))
    end
end
function generate_wrapper_method(g::Member, proc, procname, ctx::SymbolContext)
    name = Symbol(g.name)
    args = []
    argnames = []
    push!(args, :(this::$(kRPCToJuliaType(proc.parameters[1]._type, ctx))))
    push!(argnames, :this)
    for param in proc.parameters[2:end]
        argname = kerbal_name(param.name)
        push!(args, make_param(argname, param, ctx))
        push!(argnames, argname)
    end
    return name, :($name($(args...)) = kerbal(this.conn, $procname($(argnames...))))
end
function generate_wrapper_method(g::Union{StaticMember,Standard}, proc, procname, ctx::SymbolContext)
    mname = Symbol(g.name)
    args = []
    argnames = []
    for param in proc.parameters
        argname = kerbal_name(param.name)
        push!(args, make_param(argname, param, ctx))
        push!(argnames, argname)
    end
    return mname, :($mname(conn::KRPCConnection, $(args...)) = kerbal(conn, $procname($(argnames...))))
end
function generate_wrapper_method(g::ServiceGetter, proc, procname, ctx::SymbolContext)
    name = Symbol(g.name)
    args = []
    argnames = []
    for param in proc.parameters
        argname = kerbal_name(param.name)
        push!(args, make_param(argname, param, ctx))
        push!(argnames, argname)
    end
    return name, :($name(this::RemoteTypes.$(ctx.current), $(args...)) = kerbal(this.conn, $procname($(argnames...))))
end
function generate_wrapper_method(g::ServiceSetter, proc, procname, ctx::SymbolContext)
    name = Symbol(g.name * "!")
    args = []
    argnames = []
    for param in proc.parameters
        argname = kerbal_name(param.name)
        push!(args, make_param(argname, param, ctx))
        push!(argnames, argname)
    end
    return name, :($name(this::RemoteTypes.$(ctx.current), $(args...)) = kerbal(this.conn, $procname($(argnames...))))
end
generate_wrapper_method(g, proc, procname, ctx) = nothing, :()

function generateHelpers(info::krpc.schema.Services, status::krpc.schema.Status)
    services = Dict{String, Any}()
    req = Dict{String, Set{String}}()
    for service in info.services
        service_module_name = Symbol(service.name)
        ctx = SymbolContext(service_module_name, Set{Symbol}(), Set{Tuple{Symbol, Symbol}}())

        classes_ast = []
        classnames = []
        for clazz in service.classes
            classname = kerbal_name(clazz.name)
            push!(classes_ast,:(struct $classname <: kRPCTypes.Class conn::KRPCConnection; id::Int end))
            push!(classnames, classname)
        end

        enumerations_ast = []
        enumeration_names = []
        for enum in service.enumerations
            enumName = Symbol("E" * enum.name)
            push!(enumerations_ast, :(struct $enumName <: kRPCTypes.Enum value::Int32 end))
            for val in enum.values
                push!(enumerations_ast, :(const $(Symbol("E" * enum.name * "_" * val.name)) = $enumName($(val.value))))
            end
            push!(enumeration_names, enumName)
        end

        procedures_ast = []
        procedure_names =[]
        helpers_ast = []
        helper_type_map = Dict{Symbol, Symbol}()
        setindex!.((helper_type_map,), classnames, [gensym() for i=1:length(classnames)])
        for proc in service.procedures
            kind = parseKind(proc.name)
            procname = kerbal_name(proc.name)

            service_name = QuoteNode(Symbol(service.name))
            procedure_name = QuoteNode(Symbol(proc.name))
            return_type = if hasproperty(proc, :return_type) kRPCToJuliaType(proc.return_type, ctx) else Nothing end
            params = [:($(kerbal_name(param.name))::$(kRPCToJuliaType(param._type, ctx))) for param in proc.parameters]

            cstr_params = [
                if !isnothing(param.default_value) && length(param.default_value) > 0
                    :($(kerbal_name(param.name))::$(kRPCToJuliaType(param._type, ctx)) = getJuliaValue(Nothing(), $(param.default_value), $(kRPCToJuliaType(param._type, ctx))))
                else
                    :($(kerbal_name(param.name))::$(kRPCToJuliaType(param._type, ctx)))
                end
                for param in proc.parameters]
            cstr_new_args = [kerbal_name(param.name) for param in proc.parameters]
            cstr = :($procname($(params...)) = new($(cstr_new_args...)))
            proc_ast = :(struct $procname <: Request{$service_name, $procedure_name, $return_type}
                $cstr
                $(params...) end)
            gen_procname = gensym()
            expname, helper = generate_wrapper_method(kind, proc, gen_procname, ctx)
            if !isnothing(expname)
                push!(helpers_ast, :(import ..($procname) as $gen_procname))
                push!(helpers_ast, helper)
                push!(helpers_ast, :(@doc $(generate_documentation(expname, getproperty.(proc.parameters, :name), kRPCToJuliaType.(getproperty.(proc.parameters, :_type), (ctx,)), proc.documentation)) $expname))
                push!(helpers_ast, :(export $expname))
            end
            push!(procedures_ast, proc_ast)
            push!(procedure_names, procname)
        end

        body = Expr(:block)
        push!(body.args, :(import ...kRPCTypes))
        push!(body.args, :(import ...Request))
        for req in ctx.needed
            push!(body.args, :(import ..($(Symbol(req)))))
        end
        push!(classes_ast, :(struct $service_module_name conn::KRPCConnection end))
        push!(body.args, :(module RemoteTypes import ....kRPCTypes; import ....KRPCConnection; $(classes_ast...); $(Expr(:export, classnames...)); export $service_module_name; end))
        append!(body.args, enumerations_ast)
        if length(enumeration_names) > 0
            push!(body.args, Expr(:export, enumeration_names...))
        end
        append!(body.args, procedures_ast)
        if length(procedure_names) > 0
            push!(body.args, Expr(:export, procedure_names...))
        end
        push!(body.args, :(module Helpers
            import ....kerbal;
            import ....KRPCConnection;
            import ....Request;
            import ..RemoteTypes;
            $((:(import ...($(Symbol(req)))) for req in ctx.needed)...);
            $((:(import ...($(enum[1])).($(enum[2]))) for enum in ctx.neededEnums)...);
            $(helpers_ast...) end))
        ast = :(module $service_module_name end)
        ast.args[3] = body
        services[service.name] = ast
        req[service.name] = Set(String.(ctx.needed))
    end

    services_ast_body = Expr(:block)

    added = Set{String}()
    working = collect(services)
    while !isempty(working)
        for (index, (service, ast)) in enumerate(working)
            if any(req->!(req in added), req[service])
                continue
            end
            push!(services_ast_body.args, ast)
            push!(added, service)
            deleteat!(working, index)
            break
        end
    end
    push!(services_ast_body.args, :(version = $(status.version)))
    outmod = :(module Interface end)
    outmod.args[3] = services_ast_body
    return outmod
end


text:
## xgrad0.jl - dynamic cache for xdiff

DERIV_CACHE = Dict{Any,Any}()

getsize(x::AbstractArray) = size(x)
getsize(x::Number) = ()

function getsize(x)
    if isstruct(x)
        sz_arr = []
        for fld in fieldnames(typeof(x))
            val = getfield(x, fld)
            push!(sz_arr, getsize(val))
        end
        return (sz_arr...,)
    else
        return size(x)
    end
end

"""
Calculate gradient of a function at specified inputs, cache the derivative.

    loss(w, x, y) = sum(w * x .- y)
    val, dw, dx, dy = xgrad(loss; w=rand(2,3), x=rand(3,4), y=rand(2))

`xgrad` also accepts context `ctx::Dict{}` and cache `mem::Dict{Any,Any}`.

See also: `xdiff`.
"""
function xgrad(f::Function; ctx=Dict(), mem=Dict(), inputs...)
    vals = [v for (k, v) in inputs]
    types = [typeof(v) for v in vals]
    sizes = [getsize(v) for v in vals]
    key = (f, types)
    if haskey(DERIV_CACHE, key)
        df, old_sizes = DERIV_CACHE[key]
        if sizes != old_sizes
            # new input sizes - recompile and clean up buffers
            # println("recompiling for new sizes and cleaning up memory")
            df = xdiff(f; ctx=copy(ctx), inputs...)
            DERIV_CACHE[key] = (df, sizes)
            for k in keys(mem)
                delete!(mem, k)
            end
        end
    else
        # println("compiling derivative")
        # if derivative function isn't compiled yet, do it
        # use copy of context in order not to pollute the passed one
        df = xdiff(f; ctx=copy(ctx), inputs...)
        DERIV_CACHE[key] = (df, sizes)
    end
    dvals = Base.invokelatest(df, vals..., mem)
    return dvals
end

function _kgrad(f::Function, args...; kwargs...)
    vnames, _ = funexpr(f, map(typeof, args))
    inputs = [n => v for (n, v) in zip(vnames, args)]
    res = xgrad(f; inputs...)
    return res[2]  # derivative of the first argument
end

"""
    kgrad(f::Function)

Experimental: derivatives in the same form as used in AutoGrad/Knet.

"""
function kgrad(f::Function)
    # TODO: add kwargs & integrate mem
    (args...) -> _kgrad(f, args...)
end


text:
module TestVariableFlipSign

using Test

using VecMathOptInterface
const MOI = VecMathOptInterface

include("../utilities.jl")

function runtests()
    for name in names(@__MODULE__; all = true)
        if startswith("$(name)", "test_")
            @testset "$(name)" begin
                getfield(@__MODULE__, name)()
            end
        end
    end
    return
end

function test_NonposToNonneg()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Variable.NonposToNonneg{Float64}(mock)
    mock.optimize! =
        (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
            mock,
            [-4, 3, 16, 0],
            (MOI.VectorAffineFunction{Float64}, MOI.Zeros) => [[7, 2, -4]],
        )
    MOI.Test.test_conic_linear_VectorOfVariables_2(
        bridged_mock,
        MOI.Test.Config(),
    )
    @test MOI.get(mock, MOI.NumberOfVariables()) == 4
    @test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 4
    vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
    y = vis[4]
    @test y.value == -1

    @test MOI.supports(
        bridged_mock,
        MOI.VariablePrimalStart(),
        MOI.VariableIndex,
    )
    @test MOI.supports(
        bridged_mock,
        MOI.VariablePrimalStart(),
        typeof(MOI.Bridges.bridge(bridged_mock, y)),
    )
    MOI.set(bridged_mock, MOI.VariablePrimalStart(), y, 1.0)
    x, y_flipped, z, s = MOI.get(mock, MOI.ListOfVariableIndices())
    @test MOI.get(mock, MOI.VariablePrimalStart(), y_flipped) == -1
    @test MOI.get(bridged_mock, MOI.VariablePrimalStart(), y) == 1

    var_names = ["x", "y", "z", "w"]
    MOI.set(
        mock,
        MOI.VariableName(),
        MOI.get(mock, MOI.ListOfVariableIndices()),
        var_names,
    )
    con_w = MOI.get(
        mock,
        MOI.ListOfConstraintIndices{
            VecMathOptInterface.VectorOfVariables,
            VecMathOptInterface.Zeros,
        }(),
    )[1]
    con_yz = MOI.get(
        mock,
        MOI.ListOfConstraintIndices{
            VecMathOptInterface.VectorOfVariables,
            VecMathOptInterface.Nonnegatives,
        }(),
    )
    con_ex = MOI.get(
        mock,
        MOI.ListOfConstraintIndices{
            VecMathOptInterface.VectorAffineFunction{Float64},
            VecMathOptInterface.Zeros,
        }(),
    )[1]

    MOI.set(mock, MOI.ConstraintName(), con_w, "cw")
    MOI.set(mock, MOI.ConstraintName(), con_yz[1], "cy")
    MOI.set(mock, MOI.ConstraintName(), con_yz[2], "cz")
    MOI.set(mock, MOI.ConstraintName(), con_ex, "cex")

    MOI.set(
        bridged_mock,
        MOI.VariableName(),
        MOI.get(bridged_mock, MOI.ListOfVariableIndices())[4],
        "v",
    )
    con_v = MOI.get(
        bridged_mock,
        MOI.ListOfConstraintIndices{
            VecMathOptInterface.VectorOfVariables,
            VecMathOptInterface.Nonpositives,
        }(),
    )[1]
    MOI.set(bridged_mock, MOI.ConstraintName(), con_v, "cv")
    s = """
    variables: x, y, z, w
    cw: [w] in VecMathOptInterface.Zeros(1)
    cy: [y] in VecMathOptInterface.Nonnegatives(1)
    cz: [z] in VecMathOptInterface.Nonnegatives(1)
    cex: [1*x + -1*w + 4.0, -1*y + 3.0, 1*x + 1*z + -12.0] in VecMathOptInterface.Zeros(3)
    minobjective: 3*x + -2*y + -4*z
    """
    model = MOI.Utilities.Model{Float64}()
    MOI.Utilities.loadfromstring!(model, s)
    MOI.Test.util_test_models_equal(
        mock,
        model,
        var_names,
        ["cw", "cy", "cz", "cex"],
    )
    s = """
    variables: x, z, w, v
    cv: [v] in VecMathOptInterface.Nonpositives(1)
    cw: [w] in VecMathOptInterface.Zeros(1)
    cz: [z] in VecMathOptInterface.Nonnegatives(1)
    cex: [1*x + -1*w + 4.0, 1*v + 3.0, 1*x + 1*z + -12.0] in VecMathOptInterface.Zeros(3)
    minobjective: 3*x + 2*v + -4*z
    """
    model = MOI.Utilities.Model{Float64}()
    MOI.Utilities.loadfromstring!(model, s)
    MOI.Test.util_test_models_equal(
        bridged_mock,
        model,
        ["x", "z", "w", "v"],
        ["cv", "cw", "cz", "cex"],
    )
    return
end

function test_conic_linear_INFEASIBLE_2()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Variable.NonposToNonneg{Float64}(mock)
    MOI.Utilities.set_mock_optimize!(
        mock,
        (mock::MOI.Utilities.MockOptimizer) -> MOI.Utilities.mock_optimize!(
            mock,
            MOI.INFEASIBLE,
            MOI.INFEASIBLE_POINT,
            MOI.INFEASIBILITY_CERTIFICATE,
        ),
    )
    MOI.Test.test_conic_linear_INFEASIBLE_2(bridged_mock, MOI.Test.Config())
    @test MOI.get(mock, MOI.NumberOfVariables()) == 1
    @test length(MOI.get(mock, MOI.ListOfVariableIndices())) == 1
    @test first(MOI.get(mock, MOI.ListOfVariableIndices())).value ≥ 0
    @test MOI.get(bridged_mock, MOI.NumberOfVariables()) == 1
    vis = MOI.get(bridged_mock, MOI.ListOfVariableIndices())
    @test vis == [MOI.VariableIndex(-1)]
    _test_delete_bridged_variable(
        bridged_mock,
        vis[1],
        MOI.Nonpositives,
        1,
        ((MOI.VectorOfVariables, MOI.Nonnegatives, 0),),
    )
    return
end

function test_delete_in_vector()
    mock = MOI.Utilities.MockOptimizer(
        MOI.Utilities.UniversalFallback(MOI.Utilities.Model{Float64}()),
    )
    bridged_mock = MOI.Bridges.Variable.NonposToNonneg{Float64}(mock)
    vis, _ = MOI.add_constrained_variables(bridged_mock, MOI.Nonpositives(4))
    _test_delete_bridged_variable(
        bridged_mock,
        vis[2],
        MOI.Nonpositives,
        4,
        ((MOI.VectorOfVariables, MOI.Nonnegatives, 0),),
        used_bridges = 0,
        used_constraints = 0,
    )
    return
end

end  # module

TestVariableFlipSign.runtests()


text:
# This file was generated, do not modify it. # hide
function MLJFlux.build(model::MyNetworkBuilder, input_dims, output_dims)
    layer1 = Flux.Dense(input_dims, model.n1)
    layer2 = Flux.Dense(model.n1, model.n2)
    layer3 = Flux.Dense(model.n2, output_dims)
    return Flux.Chain(layer1, layer2, layer3)
end


text:
export escapetime, meancollisiontime
export escapetime!, meancollisiontime!

#######################################################################################
## Escape times
#######################################################################################
"""
    escapetime([p,] bd, t; warning = false)
Calculate the escape time of a particle `p` in the billiard `bd`, which
is the time until colliding with any "door" in `bd`.
As a "door" is considered any [`FiniteWall`](@ref) with
field `isdoor = true`.

If the particle evolves for more than `t` (integer or float) without colliding with the
`Door` (i.e. escaping) the returned result is `Inf`.

A warning can be thrown if the result is `Inf`. Enable this using the keyword
`warning = true`.

If a particle is not given, a random one is picked through [`randominside`](@ref).
See [`parallelize`](@ref) for a parallelized version.
"""
escapetime(p, bd, t; warning = false) =
    escapetime!(copy(p), bd, t; warning = warning)
escapetime(bd::Billiard, t; kwargs...) =
    escapetime(randominside(bd), bd, t; kwargs...)

function escapetime!(p::AbstractParticle{T}, bd::Billiard{T}, t;
        warning::Bool=false)::T where {T<:AbstractFloat}

    ei = escapeind(bd)
    if length(ei) == 0
        error("""The billiard does not have any "doors"!""")
    end

    totalt = zero(T); count = zero(t); t_to_write = zero(T)

    while count < t

        i, tmin, pos, vel = bounce!(p, bd)
        t_to_write += tmin

        if isperiodic(i, bd)
            continue
        else
            totalt += t_to_write
            i ∈ ei &&  break # the collision happens with a Door!

            # set counter
            count += increment_counter(t, t_to_write)
            t_to_write = zero(T)
        end
    end#time, or collision number, loop
    if count ≥ t
        warning && warn("Particle did not escape within max-iter window.")
        return T(Inf)
    end
    return totalt
end

function escapeind(bd)
    j = Int[]
    for (i, obst) in enumerate(bd)
        if typeof(obst) <: FiniteWall && obst.isdoor == true
            push!(j, i)
        end
    end
    return j
end

#######################################################################################
## Mean Collision Time
#######################################################################################
function meancollisiontime!(p::AbstractParticle{T}, bd::Billiard{T}, t)::T where {T}

    ispinned(p, bd) && return Inf
    ismagnetic = typeof(p) <: MagneticParticle
    ismagnetic && (absω = abs(p.omega))
    tmin, i = next_collision(p, bd)
    tmin == Inf && return Inf

    κ = zero(T); count = zero(t); t_to_write = zero(T); colcount = 0

    while count < t
        i, tmin, pos, vel = bounce!(p, bd)

        t_to_write += tmin

        if isperiodic(bd) && i ∈ bd.peridx
            continue
        else
            κ += t_to_write
            colcount += 1
            # set counter
            count += increment_counter(t, t_to_write)
            t_to_write = zero(T)
        end
    end
    return κ/colcount
end

"""
    meancollisiontime([p,] bd, t) → κ
Compute the mean collision time `κ` of the particle `p` in the billiard `bd` by
evolving for total amount `t` (either float for time or integer for collision number).

Collision times are counted only between obstacles that are *not*
[`PeriodicWall`](@ref).

If a particle is not given, a random one is picked through [`randominside`](@ref).
See [`parallelize`](@ref) for a parallelized version.
"""
meancollisiontime(p, bd, t) = meancollisiontime!(copy(p), bd, t)
meancollisiontime(bd::Billiard, t) = meancollisiontime(randominside(bd), bd, t)


text:
using Revise
using LinearAlgebra
using Plots

includet("src/InternalArray.jl")
includet("src/FixedStepMethods.jl")
includet("src/Interpolation.jl")
IArray = InternalArray

G = 6.67430e-11 * 3600^2
earth_mass = 5.9724e-3
sun_mass = 1.9885e3
reduced_mass = 1/(1/sun_mass + 1/earth_mass)
year = 365.242
periheliom = 147.09

r0 = [152.1, 0]
v0 = [0, 2.929e-5] * 3600
step = 150

function f(t, x)
    r = [x[1], x[2]]
    rr = dot(r, r)
    v = [x[3], x[4]]
    return [v; -G*(sun_mass+earth_mass)/rr/sqrt(rr)*r]
end

function full_turn(t, x)
    p2 = x[length(x)-1][1:2]
    p1 = x[length(x)][1:2]
    return p2[2] < 0 && p1[2] >= 0
end

function half_turn(t, x)
    p2 = x[length(x)-1][1:2]
    p1 = x[length(x)][1:2]
    return p2[2] > 0 && p1[2] <= 0
end

methods = [euler_step, mod_euler_step, heum_step, RK4_step]
xs = []
for met in methods
    push!(xs, fixed_step_ode_solver(
        f,
        0,
        [r0; v0],
        full_turn;
        method = met,
        step = step,
        maxit = 10^6
    ))
end

function two_bodies_error(x)
    return maximum(abs.(norm.(x).-norm([r0; v0])))
end

function two_bodies_year(x)
    return (length(x)-1)*step/24
end

plots = []
for i = 1:length(methods)
    print("\033[32;1mtwo_bodies>\033[0m \n")
    print("\tMethod $(methods[i])\n")

    n = length(xs[i])
    last5x = [p[1] for p in xs[i][n-4:n]]
    last5y = [p[2] for p in xs[i][n-4:n]]
    last5t = [Float64(step*i) for i in n-5:n-1]
    closingx = Interpolation.hermite(last5y, last5x, 0)
    closingt = Interpolation.hermite(last5y, last5t, 0)

    m = 2
    while m < n && !half_turn(0, xs[i][1:m])
        m += 1
    end
    half5x = [p[1] for p in xs[i][m-2:m+2]]
    half5y = [p[2] for p in xs[i][m-2:m+2]]
    half5t = [Float64(step*i) for i in m-3:m+1]
    periheliomx = Interpolation.hermite(half5y, half5x, 0)

    print("\tClosing Error: $(norm(last(xs[i])[1:2]-[closingx, 0]))\n")
    print("\tYear: $(closingt/24)\n")
    print("\tYear Error(NASA): $(abs(year-closingt/24))\n")
    print("\tPeriheliom: $(-periheliomx)\n")
    print("\tPeriheliom Error(NASA): $(abs(periheliom+periheliomx))\n\n")

    push!(plots, plot(IArray(xs[i], 1),
                      IArray(xs[i], 2),
                      label = "$(methods[i])",
                      aspect_ratio = :equal))
    savefig("media/two_bodies_$(methods[i]).png")
end

plot(plots[1], plots[2], plots[3], plots[4], title = "Step: $(step)")
savefig("media/two_bodies_step_$(step).png")

max_stars_repo_name: useredsa/numeric-differential-equations

max_issues_repo_name: useredsa/numeric-differential-equations

max_forks_repo_name: useredsa/numeric-differential-equations


text:
using AcuteML

# Type Definition

@aml mutable struct Person "person", courseCheck
    age::UInt64, "~"
    field, "study-field"
    GPA::Float64 = 4.5, "~", GPAcheck
    courses::Vector{String}, "taken-courses"
    id::Int64, a"~"
end

@aml mutable struct University "university"
    name, a"university-name"
    people::Vector{Person}, "person"
end

@aml mutable struct Doc xd""
    university::University, "~"
end

# Value Checking Functions
GPAcheck(x) = x <= 4.5 && x >= 0

function courseCheck(age, field, GPA, courses, id)

    if field == "Mechanical Engineering"
        relevant = ["Artificial Intelligence", "Robotics", "Machine Design"]
    elseif field == "Computer Engineering"
        relevant = ["Julia", "Algorithms"]
    else
        error("study field is not known")
    end

    return any(in.(courses, Ref(relevant)))
end

# Constructor

P1 = Person(age=24, field="Mechanical Engineering", courses=["Artificial Intelligence", "Robotics"], id = 1)

P2 = Person(age=18, field="Computer Engineering", GPA=4, courses=["Julia"], id = 2)

P2.GPA=4.2 # mutability support

# Two examples that doesn't meet the critertia function for GPA because GPA is more than 4.5
#=
P3 = Person(age=99, field="Macro Wizard", GPA=10, courses=["Julia Magic"], id = 3)
# GPA doesn't meet criteria function

P1.GPA=5.0
# GPA doesn't meet criteria function
=#

U = University(name="Julia University", people=[P1, P2])

D = Doc(university = U)

print(P1.aml)
#=
<person id="1">
  <age>24</age>
  <study-field>Mechanical Engineering</study-field>
  <GPA>4.5</GPA>
  <taken-courses>Artificial Intelligence</taken-courses>
  <taken-courses>Robotics</taken-courses>
</person>
=#

print(P2.aml)
#=
<person id="2">
  <age>18</age>
  <study-field>Computer Engineering</study-field>
  <GPA>4.2</GPA>
  <taken-courses>Julia</taken-courses>
</person>
=#

print(U.aml)
#=
<university university-name="Julia University">
  <person id="1">
    <age>24</age>
    <study-field>Mechanical Engineering</study-field>
    <GPA>4.5</GPA>
    <taken-courses>Artificial Intelligence</taken-courses>
    <taken-courses>Robotics</taken-courses>
  </person>
  <person id="2">
    <age>18</age>
    <study-field>Computer Engineering</study-field>
    <GPA>4.2</GPA>
    <taken-courses>Julia</taken-courses>
  </person>
</university>
=#

print(D.aml)
#=
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<university university-name="Julia University">
  <person id="1">
    <age>24</age>
    <study-field>Mechanical Engineering</study-field>
    <GPA>4.5</GPA>
    <taken-courses>Artificial Intelligence</taken-courses>
    <taken-courses>Robotics</taken-courses>
  </person>
  <person id="2">
    <age>18</age>
    <study-field>Computer Engineering</study-field>
    <GPA>4.2</GPA>
    <taken-courses>Julia</taken-courses>
  </person>
</university>
=#


text:
# AUTO GENERATED FILE - DO NOT EDIT

export dcc_logoutbutton

"""
    dcc_logoutbutton(;kwargs...)

A LogoutButton component.
Logout button to submit a form post request to the `logout_url` prop.
Usage is intended for dash-deployment-server authentication.

DDS usage:

`dcc.LogoutButton(logout_url=os.getenv('DASH_LOGOUT_URL'))`

Custom usage:

- Implement a login mechanism.
- Create a flask route with a post method handler.
`@app.server.route('/logout', methods=['POST'])`
  - The logout route should perform what's necessary for the user to logout.
  - If you store the session in a cookie, clear the cookie:
  `rep = flask.Response(); rep.set_cookie('session', '', expires=0)`

- Create a logout button component and assign it the logout_url
`dcc.LogoutButton(logout_url='/logout')`

See https://dash.plotly.com/dash-core-components/logout_button
for more documentation and examples.
Keyword arguments:
- `id` (String; optional): Id of the button.
- `label` (String; optional): Text of the button
- `logout_url` (String; optional): Url to submit a post logout request.
- `style` (Dict; optional): Style of the button
- `method` (String; optional): Http method to submit the logout form.
- `className` (String; optional): CSS class for the button.
- `loading_state` (optional): Object that holds the loading state object coming from dash-renderer. loading_state has the following type: lists containing elements 'is_loading', 'prop_name', 'component_name'.
Those elements have the following types:
  - `is_loading` (Bool; optional): Determines if the component is loading or not
  - `prop_name` (String; optional): Holds which property is loading
  - `component_name` (String; optional): Holds the name of the component that is loading
"""
function dcc_logoutbutton(; kwargs...)
        available_props = Symbol[:id, :label, :logout_url, :style, :method, :className, :loading_state]
        wild_props = Symbol[]
        return Component("dcc_logoutbutton", "LogoutButton", "dash_core_components", available_props, wild_props; kwargs...)
end

max_stars_repo_name: asc-csa/dash-core-components

max_issues_repo_name: asc-csa/dash-core-components

max_forks_repo_name: asc-csa/dash-core-components


text:
struct Sequence{T} <: FiniteLengthAnimation{T}
    animations::Vector{<:FiniteLengthAnimation{T}}
    start::Float64
    gaps::Vector{Float64}
end

function Sequence(animations::Vector{<:FiniteLengthAnimation{T}}, start::Real, gap::Real) where T
    Sequence(animations, convert(Float64, start), Float64[gap for _ in 1:length(animations) - 1])
end

function at(s::Sequence{T}, t::Real)::T where {T}
    durations = duration.(s.animations)
    d = duration(s)
    boundaries = [s.start; s.start .+ durations[1:end-1] .+ s.gaps; s.start + d]

    i_first_after_t = findfirst(b -> b >= t, boundaries)

    if isnothing(i_first_after_t)
        # t lies after the last boundary
        return at(s.animations[end], t - boundaries[end-1])
    elseif i_first_after_t == 1
        # t lies before the first boundary
        return at(s.animations[1], t - boundaries[1])
    else
        # t lies between two boundaries
        i_from = i_first_after_t - 1
        return at(s.animations[i_from], t - boundaries[i_from])
    end
end

(s::Sequence)(t::Real) = at(s, t)

duration(s::Sequence) = sum(s.gaps) + sum(duration.(s.animations))


text:
export getAnalytic2DeikonalSolution,selfMultiplyWithAnalyticSolution2D,analyticLocal,T02D,getAnalytic2DeikonalSolutionAll

function getAnalytic2DeikonalSolution(n::Array{Int64,1},h::Array{Float64,1},src::Array{Int64,1})

T = zeros(n[1],n[2]);
for j = 1:n[2]
	for i = 1:n[1]
		@inbounds T[i,j] = T02D(h,src,i,j);
	end
end
# finite_vol_integral_for_inverse_r_over_square
Lsrc = 2*(h[1]*asinh(h[2]/h[1]) + h[2]*asinh(h[1]/h[2]))/(h[1]*h[2]);
return T,Lsrc;
end

function T02D(h::Array{Float64,1},src::Array{Int64,1},loc1::Int64,loc2::Int64)
return sqrt( ((loc1 - src[1])*h[1])^2 + ((loc2 - src[2])*h[2])^2 );
end

function selfMultiplyWithAnalyticSolution2D(n::Array{Int64,1},h::Array{Float64,1},src::Array{Int64,1},T::Union{Array{Float32,2},Array{Float64,2}})
for j = 1:n[2]
	for i = 1:n[1]
		@inbounds T[i,j] *= T02D(h,src,i,j);
	end
end
return;
end

function selfMultiplyWithAnalyticSolution2D(n::Array{Int64,1},h::Array{Float64,1},src::Array{Int64,1},T::Union{Array{Float32,1},Array{Float64,1}})
loc = zeros(Int64,2);
for k=1:length(T)
	cs2loc(loc,k,n);
	@inbounds T[k] *= T02D(h,src,loc[1],loc[2]);
end
return;
end

function analyticLocal(h::Array{Float64,1},src::Array{Int64,1},loc1::Int64,loc2::Int64)
@inbounds G01loc = (loc1 - src[1])*h[1];
@inbounds G02loc = (loc2 - src[2])*h[2];
T0loc = sqrt(G01loc*G01loc + G02loc*G02loc);
invt0 = 1/T0loc;
G01loc *= invt0;
G02loc *= invt0;
return T0loc,G01loc,G02loc
end

function analyticLocal(h::Array{Float64,1},src::Array{Int64,1},loc1::Int64,loc2::Int64,GradAns::Array{Float64,1})
@inbounds G01loc = (loc1 - src[1])*h[1];
@inbounds G02loc = (loc2 - src[2])*h[2];
T0loc = sqrt(G01loc*G01loc + G02loc*G02loc);
invt0 = 1/T0loc;
G01loc *= invt0;
G02loc *= invt0;
GradAns[1] = G01loc;
GradAns[2] = G02loc;
return T0loc
end

function getAnalytic2DeikonalSolutionAll(n::Array{Int64,1},h::Array{Float64,1},src::Array{Int64,1})

source1 = (src[1]-1)*h[1];
source2 = (src[2]-1)*h[2];

X1,X2 = ndgrid((0:(n[1]-1))*h[1] .- source1,(0:(n[2]-1))*h[2] .- source2);

r = sqrt.(X1.^2 + X2.^2);
T = r;
L = 1.0./r;
#Check that for h1=h2:
# finite_vol_integral_for_inverse_r_over_square = 3.52;
# L[src[1],src[2]] = (finite_vol_integral_for_inverse_r_over_square)/sqrt(h[1]*h[2]);
L[src[1],src[2]] = getL0AtSrc2D(h);

G2 = X2.*L;
G2[src[1],src[2]] = 1.0./sqrt(2);

G1 = X1.*L;
G1[src[1],src[2]] = 1.0./sqrt(2);

return T,G1,G2,L;
end

function getL0AtSrc2D(h)
	return (2*(h[1]*asinh(h[2]/h[1]) + h[2]*asinh(h[1]/h[2]))/(h[1]*h[2]));
end

## A = 0.25*sqrt(2/Fsrc)*exp(1i*pi/4)*(1/sqrt(pi))*sqrt((1./r));
## const_for_integral = 1.76775; % or 1.76775 for a square cell
## A(src(1),src(2)) = 0.25*sqrt(2/Fsrc)*exp(1i*pi/4)*(1/sqrt(pi))*(const_for_integral/(sqrt(sqrt(h(1)*h(2)))));


text:
import DataFrames
import SQLite

include("types.jl")

#################
### START WIN ###
#################

#println(capturedByOdd)
#println(capturedByEven)
function samePiecesCaptured(capturedByEven, capturedByOdd)
    numOfCapturedByOdd = length(capturedByOdd)
    numOfCapturedByEven =length(capturedByEven)
    if !isdefined(capturedByEven) || !isdefined(capturedByOdd)
        return true
    end
    if numOfCapturedByOdd==numOfCapturedByEven

        return true
    end
    return false
end

function samePiece(p1,p2)
    if p1.name==p2.name && p1.x==p2.x && p1.y==p2.y && p1.side==p2.side
        return true
    else
        return false
    end
end

function sameBoard(b1,b2)
    for i in eachindex(b1)
        for j in eachindex(b1)
            if !samePiece(b1[i][j],b2[i][j])
                return false
            end
        end
    end
    return true
end

function check_draw(curr, allBoards)
    count = 0

    for b in eachindex(allBoards)
        if sameBoard(curr,allBoards[b])
            count+=1
        end
    end
    if count == 4
        return true
    end
    return false
end

function find_our_king(board, side)
    boardLength = length(board)
    for i in 1:boardLength
        for j in 1:boardLength
            if board[j][i].name == "king" && board[j][i].side == side
                return board[j][i]
            end
        end
    end
    return -1
end

function find_our_prince(board, side)
    boardLength = length(board)
    for i in 1:boardLength
        for j in 1:boardLength
            if board[j][i].name == "prince" && board[j][i].side == side
                return board[j][i]
            end
        end
    end
    return -1
end

function win(moveType, is_timed, sente_time, gote_time, allBoards)
    numOfBoards = length(allBoards)
    board = allBoards[numOfBoards]
    boardLength=length(allBoards[1])
    if boardLength == 5
        gameType = "minishogi"
    elseif boardLength == 9
        gameType = "standard"
    elseif boardLength == 12
        gameType = "chu"
    else
        gameType = "ten"
    end

    #CHECK FOR A RESIGN
    if moveType =="resign"
        #decisive=true
        if side == 0
            #println("r")
            return "r"
        else
            #println("R")
            return "R"
        end
    end

    if is_timed
        if sente_time <= 0
            #println("W")
            #decisive=true
            return "W"
        elseif gote_time <= 0
            #println("B")
            #decisive=true
            return "B"
        end
    end

    if check_draw(board, allBoards) && samePiecesCaptured()
        return "D"
    end

    #=if decisive==true
        return
    end
    =#

    sentePieces = Array(piece, 0) #0 element piece array
    gotePieces = Array(piece, 0)
    senteKing = empty
    goteKing = empty
    sentePrince = empty
    gotePrince = empty

    # go through board
    for i in 1:length(board)
        for j in 1:length(board[1])

            #get all our pieces in board into ourPieces array
            if board[i][j].side == 1
                push!(sentePieces, board[i][j])
            elseif board[i][j].side == 0
                push!(gotePieces, board[i][j])
            end

            #get enemy king
            if board[i][j].name == "king" && board[i][j].side == 1
                senteKing = board[i][j]
            #get our king
            elseif board[i][j].name == "king" && board[i][j].side == 0
                goteKing = board[i][j]

            elseif board[i][j].name == "prince" && board[i][j].side == 1
                sentePrince = board[i][j]
            elseif board[i][j].name == "prince" && board[i][j].side == 0
                sentePrince = board[i][j]
            end

        end
    end

    #CHECK IF KING IS KILLED
    if gameType == "minishogi" || gameType == "standard"
        #println("check wins")
        if senteKing == empty
            return "W"
        elseif goteKing == empty
            return "B"
        end
        #println("not a win")
    end

    if gameType == "ten" || gameType == "chu"
        #println("check wins")
        if senteKing == empty && sentePrince == empty
            return "W"
        elseif goteKing == empty && gotePrince == empty
            return "B"
        end
        #println("not a win")
    end
#=    prevBoard=duplicateBoard(board)
    push!(allBoards,prevBoard)
    push!(counts,1)=#
    #if moveType=="move"
        #sourcex= movesTable[3][i]
        #sourcey = movesTable[4][i]
        #if !isnull(targetx2) && !isnull(t)
        #movePiece(board, i, sourcex, sourcey, move_targetx, move_targety, option, targetx2, targety2)
    #end
    #println("?")
    return "?"
end

################
### MAIN WIN ###
################
#=
i =1

for i in 1:movesPlayed

    #decisive=false
    counts=[]
    prevBoard=0
    allBoards=[]

    #CHECK FOR A RESIGN
    if moveType=="resign"
        #decisive=true
        if side==0
            #println("r")
            return "r"
        else
            #println("R")
            return "R"
        end
    end

    if is_timed
        if sente_time <= 0
            #println("W")
            #decisive=true
            return "W"
        elseif gote_time <= 0
            #println("B")
            #decisive=true
            return "B"
        end
    end

    #CHECK FOR A SAME BOARD POSITION
    if findBoard(board, allBoards)>0
        counts[findBoard(board)]+=1
    end

    #CHECK DRAW
    for count in counts
        if count == 4 && samePiecesCaptured()
            #decisive = true
            #println("D")
            return "D"
        end
    end=#
    #=if decisive==true
        return
    end
    counter = 0

    #CHECK IF KING IS KILLED
    if board[targety][targetx].name=="king" || board[targety][targetx].name=="prince"
        counter+=1
    end

    if targetx2 != -1 && targety2 != -1
        if board[targety2][targetx2].name=="king" || board[targety2][targetx2].name=="prince"
            counter+=1
        end
    end

    if targetx3 != -1 && targety3 != -1
        if board[targety3][targetx3].name=="king" || board[targety3][targetx3].name=="prince"
            counter+=1
        end
    end

    if counter == 2
        #decisive=true
        if side == 0 #White wins
            #println('W')
            return "W"
        else
            #println('B')
            return "B"
        end
        return
    end
    prevBoard=duplicateBoard(board)
    push!(allBoards,prevBoard)
    push!(counts,1)
    #if moveType=="move"
        #sourcex= movesTable[3][i]
        #sourcey = movesTable[4][i]
        #if !isnull(targetx2) && !isnull(t)
        #movePiece(board, i, sourcex, sourcey, move_targetx, move_targety, option, targetx2, targety2)
    #end
    return "?"
end
#println(decisive)
#if !decisive
#    println("?")
#end
#displayBoard(board)
#displayBoard(prevBoard)
=#

max_stars_repo_name: DarienFlamont/Shogi

max_issues_repo_name: DarienFlamont/Shogi

max_forks_repo_name: DarienFlamont/Shogi


text:
using Test
using TestSetExtensions
using Mocking
using SnoopCompileWithMocking

@testset ExtendedTestSet "Not using mocking" begin
    @test make_fancy("SnoopCompile") === nothing
end

@testset ExtendedTestSet "Using mocking" begin
    fancy = "";
    fancynize_patch = @patch SnoopCompileWithMocking.fancynize(normal::AbstractString) = begin
        fancy = "This is now awesome: $normal";
        nothing
    end
    Mocking.activate()
    apply(fancynize_patch) do
        @test make_fancy("SnoopCompile") === nothing
    end
    Mocking.deactivate()
    @test fancy == "This is now awesome: SnoopCompile"
end


text:
struct FillDictionary{I, T, Inds <: AbstractIndices{I}} <: AbstractDictionary{I, T}
    inds::Inds
    value::T
end

FillDictionary(inds, value) = FillDictionary(Indices(inds), value)

Base.keys(d::FillDictionary) = getfield(d, :inds)

@inline function Base.getindex(d::FillDictionary{I}, i::I) where I
    @boundscheck checkindex(keys(d), i)
    return getfield(d, :value)
end

Base.isassigned(d::FillDictionary{I}, i::I) where {I} = Base.isassigned(keys(d), i)

gettokenvalue(d::FillDictionary, t) = getfield(d, :value)

istokenassigned(d::FillDictionary, t) = true


text:
cd(@__DIR__); include("setups/grid7x3.jl");
pyplot(dpi = 200)

# DAG pseudo-metric
distDAG = eigDAG_Distance(𝚽, Q, N)

# MDS embedding into R²
D = distDAG
E = transform(fit(MDS, D, maxoutdim=2, distances=true))

# set up all heatmap plots' positions
dx = 0.01; dy = dx;
xej = zeros(Nx, N); yej=zeros(Ny, N);
a = 5.0; b = 9.0;
for k = 1:N
    xej[:,k] = LinRange(E[1,k] - Ny * a * dx, E[1, k] + Ny * a * dx, Nx)
    yej[:,k] = LinRange(E[2,k] - a * dy, E[2, k] + a * dy, Ny)
end

##
plot()
for k=1:N
    heatmap!(xej[:, k], yej[:, k], reshape(𝚽[:, k], (Nx, Ny))', c = :viridis,
                colorbar = false, ratio = 1, annotations = (xej[4, k],
                yej[3, k] + b*dy, text(latexstring("\\varphi_{",
                string(eig2dct[k, 1]), ",", string(eig2dct[k, 2]), "}"), 10)))
end
plt = plot!(xlim = [-1.4, 1.3], ylim = [-1.4, 1.3], grid = false, clims = (-0.4, 0.4))
# first level partition
p1x = [-0.2, 1.0, NaN]; p1y = [1.3, -1.0, NaN];
plot!(p1x, p1y, c = :red, legend = false, width = 3)
# second level partition
p2x = [-1.0, 0.2, NaN, 0.4, 1.2, NaN]; p2y = [-0.8, 0.45, NaN, 0.25, 0.2, NaN];
plot!(p2x, p2y, c=:orange, legend = false, width = 2)
plt = current()
savefig(plt, "../figs/Grid7x3_DAG_2levels_partition.png")


text:
"""
$(TYPEDEF)

Vector with constant value
"""
struct VectorOfConstants{T,Tl} <: AbstractVector{T}
    val::T
    len::Tl
end

Base.IndexStyle(::Type{<:VectorOfConstants}) = IndexLinear()

"""
$(TYPEDSIGNATURES)

Length
"""
Base.length(v::VectorOfConstants)=v.len

"""
$(TYPEDSIGNATURES)

Size
"""
Base.size(v::VectorOfConstants)=(v.len,)

"""
$(TYPEDSIGNATURES)

Access
"""
function Base.getindex(v::VectorOfConstants,i)
    if i>v.len
        throw(BoundsError(v, i))
    end
    v.val
end

"""
$(TYPEDSIGNATURES)

Iterator
"""
Base.iterate(v::VectorOfConstants)  = (v.val,1)
"""
$(TYPEDSIGNATURES)

Iterator
"""
Base.iterate(v::VectorOfConstants,state) =  state>=v.len ? nothing : (v.val, state+1)

"""
$(TYPEDSIGNATURES)

Shortcut for unique
"""
Base.unique(v::VectorOfConstants)=[v.val]


text:
Base.@kwdef mutable struct CGStruct
    OrdPoly = nothing
    OrdPolyZ = nothing
    Faces = nothing
    NumG = nothing
    NumI = nothing
    Glob = nothing
    FaceGlob = nothing
    Stencil = nothing
    w = nothing
    xw = nothing
    wX = nothing
    xwX = nothing
    wY = nothing
    xwY = nothing
    wZ = nothing
    xwZ = nothing
    DW = nothing
    DS = nothing
    DST = nothing
    DWZ = nothing
    DSZ = nothing
    M = nothing
    MW = nothing
end

function Discretization(OrdPoly,OrdPolyZ,Jacobi,Param)
# Discretization

CG = CGStruct(;)
CG.OrdPoly=OrdPoly;
CG.OrdPolyZ=OrdPolyZ;
nz=Param.Grid.nz;
dXdxIF = Param.cache.dXdxIF
dXdxIC = Param.cache.dXdxIC

(CG.Faces,CG.NumG,CG.NumI,CG.Glob,CG.FaceGlob,CG.Stencil) =
  NumberingFemCG(Param.Grid,OrdPoly);
J = Param.cache.J;
JC = Param.cache.JC;
lat = Param.cache.lat;
X = Param.cache.X;
dXdx = Param.cache.dXdx;
dXdxI = Param.cache.dXdxI;

J .= 0;
lat .= 0;
X .= 0;
dXdx .= 0;
dXdxI .= 0;
(CG.w,CG.xw)=GaussLobattoQuad(CG.OrdPoly);
(CG.wX,CG.xwX)=GaussLobattoQuad(CG.OrdPoly);
(CG.wY,CG.xwY)=GaussLobattoQuad(CG.OrdPoly);
(CG.wZ,CG.xwZ)=GaussLobattoQuad(CG.OrdPolyZ);
(CG.DW,CG.DS)=DerivativeMatrixSingle(CG.OrdPoly);
CG.DST=CG.DS'
(CG.DWZ,CG.DSZ)=DerivativeMatrixSingle(CG.OrdPolyZ);

for iF=1:Param.Grid.NumFaces
  for iz=1:nz
    zI=[Param.Grid.z[iz],Param.Grid.z[iz+1]];
    (X_Fz,J_Fz,dXdx_Fz,dXdxI_Fz,lat_Fz)=Jacobi(CG,Param.Grid.Faces[iF],zI,Topo,Param);
    X[:,:,:,:,iF,iz]=X_Fz;
    J[:,:,:,iF,iz]=J_Fz;
    dXdx[:,:,:,iF,iz,:,:]=reshape(dXdx_Fz,OrdPoly+1,OrdPoly+1,OrdPolyZ+1,1,3,3);
    dXdxI[:,:,:,iF,iz,:,:]=reshape(dXdxI_Fz,OrdPoly+1,OrdPoly+1,OrdPolyZ+1,1,3,3);
    lat[:,:,iF]=lat_Fz;
  end
end
JC .= Average(J);
Param.JF=AverageFB(J);
dXdxIC .= 0;
dXdxIF .= 0;
for i=1:3
  for j=1:3
    dXdxIC[:,:,:,:,i,j] .= Average(dXdxI[:,:,:,:,:,i,j]);
    dXdxIF[:,:,:,:,i,j] .= AverageFB(dXdxI[:,:,:,:,:,i,j]);
  end
end
Param.dXdxIC11 = view(Param.cache.dXdxIC,:,:,:,:,1,1)
Param.dXdxIC12 = view(Param.cache.dXdxIC,:,:,:,:,1,2)
Param.dXdxIC21 = view(Param.cache.dXdxIC,:,:,:,:,2,1)
Param.dXdxIC22 = view(Param.cache.dXdxIC,:,:,:,:,2,2)
Param.dXdxIC31 = view(Param.cache.dXdxIC,:,:,:,:,3,1)
Param.dXdxIC32 = view(Param.cache.dXdxIC,:,:,:,:,3,2)
Param.dXdxIC33 = view(Param.cache.dXdxIC,:,:,:,:,3,3)
Param.dXdxIF11 = view(Param.cache.dXdxIF,:,:,:,2:nz,1,1)
Param.dXdxIF12 = view(Param.cache.dXdxIF,:,:,:,2:nz,1,2)
Param.dXdxIF21 = view(Param.cache.dXdxIF,:,:,:,2:nz,2,1)
Param.dXdxIF22 = view(Param.cache.dXdxIF,:,:,:,2:nz,2,2)
Param.dXdxIF31 = view(Param.cache.dXdxIF,:,:,:,2:nz,3,1)
Param.dXdxIF32 = view(Param.cache.dXdxIF,:,:,:,2:nz,3,2)
Param.dXdxIF33 = view(Param.cache.dXdxIF,:,:,:,2:nz,3,3)

(CG.M,CG.MW)=MassCG(CG,Param);
Param.latN=zeros(CG.NumG,1);
lat = Param.cache.lat
OP=CG.OrdPoly+1;
for iM=1:size(CG.FaceGlob,1)
  i = CG.FaceGlob[iM].Ind
  arr = reshape(CG.Glob[:,i,:,:] ,OP*OP*size(i,1),1)
  mat = lat[:,:,i] .*JC[:,:,i,1]
  Param.latN[arr,:,:]= Param.latN[arr,:,:] + reshape(mat ,OP*OP*size(i,1),1);
end
Param.latN=Param.latN./CG.M[:,1];
return (CG,Param)
end


text:
# Loader for external sources
maybedownload(url, filename=splitdir(url)[2]) = begin
    dirpath = joinpath(dirname(pathof(Rasters)), "../test/data")
    mkpath(dirpath)
    filepath = joinpath(dirpath, filename)
    isfile(filepath) || download(url, filepath)
    filepath
end