text:
# # [Geodesic Regression](@id Geodesic Regression)
#
# Geodesic regression generalizes [linear regression](https://en.wikipedia.org/wiki/Linear_regression)
# to Riemannian manifolds. Let's first phrase it informally as follows:
#
# > For given data points ``d_1,\ldots,d_n`` on a Riemannian manifold ``\mathcal M`` find
# > the geodesic that “best explains” the data.
#
# The meaning of “best explain” has still to be clarified. We distinguish two cases: time labeled data and unlabeled data
#
# ## [Setup](@id regression-setup)
export_folder = joinpath( #src
    @__DIR__, #src
    "..", #src
    "..", #src
    "docs", #src
    "src", #src
    "assets", #src
    "images", #src
    "tutorials", #src
) #src
using Manopt, Manifolds, Colors, Random
using LinearAlgebra: svd

black = RGBA{Float64}(colorant"#000000")
TolVibrantOrange = RGBA{Float64}(colorant"#EE7733")
TolVibrantBlue = RGBA{Float64}(colorant"#0077BB")
TolVibrantTeal = RGBA{Float64}(colorant"#009988")
TolVibrantMagenta = RGBA{Float64}(colorant"#EE3377")
TolVibrantCyan = RGBA{Float64}(colorant"#33BBEE")
Random.seed!(42)

n = 7
highlighted = 4
(highlighted > n - 1) && error(
    "Please choose a highlighted point from {1,...,$(n-1)} – you set it to $highlighted.",
)
σ = π / 8
S = Sphere(2)
base = 1 / sqrt(2) * [1.0, 0.0, 1.0]
dir = [-0.75, 0.5, 0.75]
data = [exp(S, base, dir, t) for t in range(-0.5, 0.5; length=n)]
data = map(x -> exp(S, x, random_tangent(S, x, :Gaussian, σ)), data)
nothing #hide
#
# which looks as follows (using [`asymptote_export_S2_signals`](@ref))
#
asymptote_export_S2_signals( #src
    export_folder * "/regression_data.asy"; #src
    points=[data], #src
    colors=Dict(:points => [TolVibrantBlue]), #src
    dot_size=3.5, #src
    camera_position=(1.0, 0.5, 0.5), #src
) #src
render_asymptote(export_folder * "/regression_data.asy"; render=2) #src
#md # ```julia
#md # asymptote_export_S2_signals("regression_data.asy";
#md #     points = [ [x], data],
#md #     colors=Dict(:points => [TolVibrantBlue, TolVibrantTeal]),
#md #     dot_size = 3.5, camera_position = (1.,.5,.5)
#md # )
#md # render_asymptote("regression_data.asy"; render = 2)
#md # ```
#md #
#md # ![The data of noisy versions of $x$](../assets/images/tutorials/regression_data.png)

# ## [Time labeled data](@id time-labelled-data-regression)
# if for each data item $d_i$ we are also given a time point $t_i\in\mathbb R$, which are pairwise different.
#
# Then we can use the least squares error to state the objetive function as [^Fletcher2013]
#
# ```math
# F(p,X) = \frac{1}{2}\sum_{i=1}^n d_{\mathcal M}^2(γ_{p,X}(t_i), d_i),
# ```
#
# where ``d_{\mathcal M}`` is the Riemannian distance and ``γ_{p,X}`` is the geodesic
# with ``γ(0) = p`` and ``\dot\gamma(0) = X``.
#
# For the real-valued case ``\mathcal M = \mathbb R^m`` the solution ``(p^*, X^*)`` is given in closed form
# as follows: with ``d^* = \frac{1}{n}\displaystyle\sum_{i=1}^{n}d_i`` and ``t^* = \frac{1}{n}\displaystyle\sum_{i=1}^n t_i``
# we get
#
# ```math
#  X^* = \frac{\sum_{i=1}^n (d_i-d^*)(t-t^*)}{\sum_{i=1}^n (t_i-t^*)^2}
# \quad\text{ and }\quad
# p^* = d^* - t^*X^*
# ```
#
# and hence the linear regression result is the line ``γ_{p^*,x^*}(t) = p^* + tX^*``.
#
# On a Riemannian manifold we can phrase this as an optimization problem on the [tangent bundle](https://en.wikipedia.org/wiki/Tangent_bundle),
# i.e. the disjoiint union of all tangent spaces, as
#
# ```math
# \operatorname*{arg\,min}_{(p,X) \in \mathrm{T}\mathcal M} F(p,X)
# ```
#
# Due to linearity, the gradient of ``F(p,X)`` is the sum of the single gradients of
#
# ```math
#  \frac{1}{2}d_{\mathcal M}^2\bigl(γ_{p,X}(t_i),d_i\bigr)
#  = \frac{1}{2}d_{\mathcal M}^2\bigl(\exp_p(t_iX),d_i\bigr)
#  ,\quad i∈\{1,\ldots,n\}
# ```
#
# which can be computed using a chain rule of the squared distance and the exponential map,
# see for example [^BergmannGousenbourger2018] for details or Equations (7) and (8) of [^Fletcher2013]:

M = TangentBundle(S)

struct RegressionCost{T,S}
    data::T
    times::S
end
RegressionCost(data::T, times::S) where {T,S} = RegressionCost{T,S}(data, times)
function (a::RegressionCost)(M, x)
    pts = [geodesic(M.manifold, x[M, :point], x[M, :vector], ti) for ti in a.times]
    return 1 / 2 * sum(distance.(Ref(M.manifold), pts, a.data) .^ 2)
end
struct RegressionGradient!{T,S}
    data::T
    times::S
end
RegressionGradient!(data::T, times::S) where {T,S} = RegressionGradient!{T,S}(data, times)
function (a::RegressionGradient!)(M, Y, x)
    pts = [geodesic(M.manifold, x[M, :point], x[M, :vector], ti) for ti in a.times]
    gradients = grad_distance.(Ref(M.manifold), a.data, pts)
    Y[M, :point] .= sum(
        adjoint_differential_exp_basepoint.(
            Ref(M.manifold),
            Ref(x[M, :point]),
            [ti * x[M, :vector] for ti in a.times],
            gradients,
        ),
    )
    Y[M, :vector] .= sum(
        adjoint_differential_exp_argument.(
            Ref(M.manifold),
            Ref(x[M, :point]),
            [ti * x[M, :vector] for ti in a.times],
            gradients,
        ),
    )
    return Y
end
#
# Now we need just a start point.
#
# For the Euclidean case, the result is given by the first principal component of a principal component analysis,
# see [PCR](https://en.wikipedia.org/wiki/Principal_component_regression), i.e. with ``p^* = \frac{1}{n}\displaystyle\sum_{i=1}^n d_i``
# the direction ``X^*`` is obtained by defining the zero mean data matrix
#
# ```math
# D = \bigl(d_1-p^*, \ldots, d_n-p^*\bigr) \in \mathbb R^{m,n}
# ```
#
# and taking ``X^*`` as an eigenvector to the larges eigenvalue of ``D^{\mathrm{T}}D``.
#
# We can do something similar, when considering the tangent space at the (Riemannian) mean
# of the data and then do a PCA on the coordinate coefficients with respect to a basis.
#
m = mean(S, data)
A = hcat(map(x -> get_coordinates(S, m, log(S, m, x), DefaultOrthonormalBasis()), data)...)
pca1 = get_vector(S, m, svd(A).U[:, 1], DefaultOrthonormalBasis())
x0 = ProductRepr(m, pca1)
#
# The optimal “time labels” are then just the projections ``t_i = ⟨d_i,X^*⟩``, ``i=1,\ldots,n``.
#
t = map(d -> inner(S, m, pca1, log(S, m, d)), data)
#
# And we can call the gradient descent. Note that since `gradF!` works in place of `Y`, we have to set the
# `evalutation` type accordingly.
#
y = gradient_descent(
    M,
    RegressionCost(data, t),
    RegressionGradient!(data, t),
    x0;
    evaluation=MutatingEvaluation(),
    stepsize=ArmijoLinesearch(1.0, ExponentialRetraction(), 0.95, 0.1),
    stopping_criterion=StopAfterIteration(100) | StopWhenGradientNormLess(1e-8),
    debug=[:Iteration, " | ", :Cost, "\n", :Stop, 50],
);
#
# And plot the result
#
dense_t = range(-0.5, 0.5; length=100)
geo = geodesic(S, y[M, :point], y[M, :vector], dense_t)
init_geo = geodesic(S, x0[M, :point], x0[M, :vector], dense_t)
geo_pts = geodesic(S, y[M, :point], y[M, :vector], t)
geo_conn_highlighted = shortest_geodesic(
    S, data[highlighted], geo_pts[highlighted], 0.5 .+ dense_t
)
nothing #hide
asymptote_export_S2_signals( #src
    export_folder * "/regression_result1.asy"; #src
    points=[data, [y[M, :point]], geo_pts], #src
    curves=[init_geo, geo, geo_conn_highlighted], #src
    tangent_vectors=[[Tuple([y[M, :point], y[M, :vector]])]], #src
    colors=Dict( #src
        :curves => [black, TolVibrantTeal, TolVibrantBlue], #src
        :points => [TolVibrantBlue, TolVibrantOrange, TolVibrantTeal], #src
        :tvectors => [TolVibrantOrange], #src
    ), #src
    dot_sizes=[3.5, 3.5, 2.5], #src
    line_widths=[0.33, 0.66, 0.33, 1.0], #src
    camera_position=(1.0, 0.5, 0.5), #src
) #src
render_asymptote(export_folder * "/regression_result1.asy"; render=2) #src
#md # ```julia
#md # asymptote_export_S2_signals(
#md # export_folder * "/regression_result1.asy";
#md # points=[data, [y[M, :point],], geo_pts],
#md # curves=[init_geo, geo],
#md # tangent_vectors = [ [Tuple([y[M, :point], y[M, :vector]]),],],
#md # colors=Dict(
#md #     :curves => [black,TolVibrantTeal],
#md #     :points => [TolVibrantBlue, TolVibrantOrange, TolVibrantTeal],
#md #     :tvectors => [TolVibrantOrange],
#md # ),
#md # dot_sizes=[3.5, 3.5, 2],
#md # line_widths = [0.33, 0.66, 1.0],
#md # camera_position=(1.0, 0.5, 0.5),
#md # )
#md # render_asymptote("regression_result1.asy"; render = 2)
#md # ```
#md #
#md # ![The result from doing a gradient descent on the tangent bundle](../assets/images/tutorials/regression_result1.png)
#
# In this image, together with the blue data points, you see the geodesic of the initialization in black
# (evaluated on ``[-\frac{1}{2},\frac{1}{2}]``),
# the final point on the tangent bundle in orange, as well as the resulting regression geodesic in teal,
# (on the same interval as the start) as well as small teal points indicating the time points on the geodesic corresponding to the data.
# Additionally, a thin blue line indicates the geodesic between a data point and its corresponding data point on the geodesic.
# While this would be the closest point in Euclidean space and hence the two directions (along the geodesic vs. to the data point) orthogonal, here we have
#
inner(
    S,
    geo_pts[highlighted],
    log(S, geo_pts[highlighted], geo_pts[highlighted + 1]),
    log(S, geo_pts[highlighted], data[highlighted]),
)
#
# But we also started with one of the best scenarios, i.e. equally spaced points on a geodesic obstructed by noise
#
# this gets worse if you start with less even distributed data
#
data2 = [exp(S, base, dir, t) for t in [-0.5, -0.49, -0.48, 0.1, 0.48, 0.49, 0.5]]
data2 = map(x -> exp(S, x, random_tangent(S, x, :Gaussian, σ / 2)), data2)
m2 = mean(S, data2)
A = hcat(map(x -> get_coordinates(S, m, log(S, m, x), DefaultOrthonormalBasis()), data2)...)
pca2 = get_vector(S, m, svd(A).U[:, 1], DefaultOrthonormalBasis())
x1 = ProductRepr(m, pca2)
t2 = map(d -> inner(S, m2, pca2, log(S, m2, d)), data2)
y2 = gradient_descent(
    M,
    RegressionCost(data2, t2),
    RegressionGradient!(data2, t2),
    x1;
    evaluation=MutatingEvaluation(),
    stepsize=ArmijoLinesearch(1.0, ExponentialRetraction(), 0.95, 0.1),
    stopping_criterion=StopAfterIteration(100) | StopWhenGradientNormLess(1e-8),
    debug=[:Iteration, " | ", :Cost, "\n", :Stop, 50],
)
geo2 = geodesic(S, y2[M, :point], y2[M, :vector], dense_t) #src
init_geo2 = geodesic(S, x1[M, :point], x1[M, :vector], dense_t) #src
geo_pts2 = geodesic(S, y2[M, :point], y2[M, :vector], t) #src
geo_conn_highlighted2 = shortest_geodesic(S, data2[4], geo_pts2[4], 0.5 .+ dense_t) #src
asymptote_export_S2_signals( #src
    export_folder * "/regression_result2.asy"; #src
    points=[data2, [y2[M, :point]], geo_pts2], #src
    curves=[init_geo2, geo2, geo_conn_highlighted2], #src
    tangent_vectors=[[Tuple([y2[M, :point], y2[M, :vector]])]], #src
    colors=Dict( #src
        :curves => [black, TolVibrantTeal, TolVibrantCyan], #src
        :points => [TolVibrantBlue, TolVibrantOrange, TolVibrantTeal], #src
        :tvectors => [TolVibrantOrange], #src
    ), #src
    dot_sizes=[3.5, 3.5, 2.5], #src
    line_widths=[0.33, 0.66, 0.33, 1.0], #src
    camera_position=(1.0, 0.5, 0.5), #src
) #src
render_asymptote(export_folder * "/regression_result2.asy"; render=2) #src
#md #
#md # ![The result from doing a gradient descent on the tangent bundle, unevenspaced noisy data](../assets/images/tutorials/regression_result2.png)
#
#
#
# ## [Unlabeled data](@id unlabeled-data-regression)
#
# If we are not given time points $t_i$, then the optimization problem extends – informally speaking –
# to also finding the “best fitting” (in the sense of smallest error).
# To formalize, the objective function here reads
#
#
# ```math
# F(p, X, t) = \frac{1}{2}\sum_{i=1}^n d_{\mathcal M}^2(γ_{p,X}(t_i), d_i),
# ```
#
# where ``t = (t_1,\ldots,t_n) \in \mathbb R^n`` is now an additional parameter of the objective function.
# We write ``F_1(p, X)`` to refer to the function on the tangent bundle for fixed values of ``t`` (as the one in the last part)
# and ``F_2(t)`` for the function ``F(p, X, t)`` as a function in ``t`` with fixed values ``(p, X)``.
#
# For the Euclidean case, there is no neccessity to optimize with respect to ``t``, as we saw
# above for the initialisation of the fixed time points.
#
# On a Riemannian manifold this can be stated as a problem on the product manifold ``\mathcal N = \mathrm{T}\mathcal M \times \mathbb R^n``, i.e.
#
N = M × Euclidean(length(t2))
#
# ```math
#   \operatorname*{arg\,min}_{\bigl((p,X),t\bigr)\in\mathcal N} F(p, X, t).
# ```
#
# In this tutorial we present an approach to solve this using an alternating gradient descent scheme.
# To be precise, we define the cost funcion now on the product manifold
struct RegressionCost2{T}
    data::T
end
RegressionCost2(data::T) where {T} = RegressionCost2{T}(data)
function (a::RegressionCost2)(N, x)
    TM = N[1]
    pts = [
        geodesic(TM.manifold, x[N, 1][TM, :point], x[N, 1][TM, :vector], ti) for
        ti in x[N, 2]
    ]
    return 1 / 2 * sum(distance.(Ref(TM.manifold), pts, a.data) .^ 2)
end
#
# The gradient in two parts, namely (a) the same gradient as before w.r.t. ``(p,X) ∈ T\mathcal M``
# just now with a fixed `t` in mind for the second component of the product manifold ``\mathcal N``
#
struct RegressionGradient2a!{T}
    data::T
end
RegressionGradient2a!(data::T) where {T} = RegressionGradient2a!{T}(data)
function (a::RegressionGradient2a!)(N, Y, x)
    TM = N[1]
    p = x[N, 1]
    pts = [geodesic(TM.manifold, p[TM, :point], p[TM, :vector], ti) for ti in x[N, 2]]
    gradients = grad_distance.(Ref(TM.manifold), a.data, pts)
    Y[TM, :point] .= sum(
        adjoint_differential_exp_basepoint.(
            Ref(TM.manifold),
            Ref(p[TM, :point]),
            [ti * p[TM, :vector] for ti in x[N, 2]],
            gradients,
        ),
    )
    Y[TM, :vector] .= sum(
        adjoint_differential_exp_argument.(
            Ref(TM.manifold),
            Ref(p[TM, :point]),
            [ti * p[TM, :vector] for ti in x[N, 2]],
            gradients,
        ),
    )
    return Y
end
#
# Finally we addionally look for a fixed point ``x=(p,X) ∈ \mathrm{T}\mathcal M`` at
# the gradient with respect to ``t∈\mathbb R^n``, i.e. the second component, which is given by
#
# ```math
#   (\operatorname{grad}F_2(t))_i
#   = - ⟨\dot γ_{p,X}(t_i), \log_{γ_{p,X}(t_i)}d_i⟩_{γ_{p,X}(t_i)}, i = 1, \ldots, n.
# ```
#
struct RegressionGradient2b!{T}
    data::T
end
RegressionGradient2b!(data::T) where {T} = RegressionGradient2b!{T}(data)
function (a::RegressionGradient2b!)(N, Y, x)
    TM = N[1]
    p = x[N, 1]
    pts = [geodesic(TM.manifold, p[TM, :point], p[TM, :vector], ti) for ti in x[N, 2]]
    logs = log.(Ref(TM.manifold), pts, a.data)
    pt = map(d -> vector_transport_to(TM.manifold, p[TM, :point], p[TM, :vector], d), pts)
    Y .= -inner.(Ref(TM.manifold), pts, logs, pt)
    return Y
end
#
# We can reuse the computed initial values from before, just that now we are on a product manifold
#
x2 = ProductRepr(x1, t2)
F3 = RegressionCost2(data2)
gradF3_vector = [RegressionGradient2a!(data2), RegressionGradient2b!(data2)]
y3 = alternating_gradient_descent(
    N,
    F3,
    gradF3_vector,
    x2;
    evaluation=MutatingEvaluation(),
    debug=[:Iteration, " | ", :Cost, "\n", :Stop, 50],
    stepsize=ArmijoLinesearch(),
    inner_iterations=1,
)
nothing #hide
#
#
geo3 = geodesic(S, y3[N, 1][M, :point], y3[N, 1][M, :vector], dense_t) #src
init_geo3 = geodesic(S, x1[M, :point], x1[M, :vector], dense_t) #src
geo_pts3 = geodesic(S, y3[N, 1][M, :point], y3[N, 1][M, :vector], y3[N, 2]) #src
t3 = y3[N, 2] #src
geo_conns = shortest_geodesic.(Ref(S), data2, geo_pts3, Ref(0.5 .+ dense_t)) #src
asymptote_export_S2_signals( #src
    export_folder * "/regression_result3.asy"; #src
    points=[data2, [y3[N, 1][M, :point]], geo_pts3], #src
    curves=[init_geo3, geo3, geo_conns...], #src
    tangent_vectors=[[Tuple([y3[N, 1][M, :point], y3[N, 1][M, :vector]])]], #src
    colors=Dict( #src
        :curves => [black, TolVibrantTeal, repeat([TolVibrantCyan], length(geo_conns))...], #src
        :points => [TolVibrantBlue, TolVibrantOrange, TolVibrantTeal], #src
        :tvectors => [TolVibrantOrange], #src
    ), #src
    dot_sizes=[3.5, 3.5, 2.5], #src
    line_widths=[0.33, 0.66, repeat([0.33], length(geo_conns))..., 1.0], #src
    camera_position=(1.0, 0.5, 0.5), #src
) #src
render_asymptote(export_folder * "/regression_result3.asy"; render=2) #src
#md #
#md # ![The result from doing a gradient descent on the tangent bundle, unevenspaced noisy data](../assets/images/tutorials/regression_result3.png)
#
# Note that the geodesics from the data to the regression geodesic meet at an nearly orthogonal angle.
#
# **Acknowledgement.** Parts of this tutorial are based on the bachelor thesis of
# [Jeremias Arf](https://orcid.org/0000-0003-3765-0130).
#
# [^BergmannGousenbourger2018]:
#     > Bergmann, R. and Gousenbourger, P.-Y.: _A variational model for data fitting on manifolds
#     > by minimizing the acceleration of a Bézier curve_.
#     > Frontiers in Applied Mathematics and Statistics, 2018.
#     > doi: [10.3389/fams.2018.00059](https://dx.doi.org/10.3389/fams.2018.00059),
#     > arXiv: [1807.10090](https://arxiv.org/abs/1807.10090)
# [^Fletcher2013]:
#     > Fletcher, P. T., _Geodesic regression and the theory of least squares on Riemannian manifolds_,
#     > International Journal of Computer Vision(105), 2, pp. 171–185, 2013.
#     > doi: [10.1007/s11263-012-0591-y](https://doi.org/10.1007/s11263-012-0591-y)


text:
using LinearAlgebra
using Distances

"""
    response_distribution(model, X, samples)

Takes one sample and returns a distribution of responses for this sample
by setting the model to trainmode! and thus enabling the dropout layers.
"""
function response_distribution(model, X, lms, samples)
  testmode!(model, false)
  out = zeros(size(lms, 1), samples)
  means = []
  st_dev = []
  for i in 1:samples
    out[:,i] = model(X)
  end
  for i in 1:size(out, 1)
    push!(means, mean(out[i, :]))
    push!(st_dev, std(out[i, :]))
  end
  return means, st_dev
end

"""
    to_3d_array(arr)

Takes a 2D landmark array for 3 dimensions as input and outputs a 3D array
with dimensions [n_points x 3 x n_individuals]
"""
function to_3d_array(arr)
  arr_out = zeros(convert(Int32, size(arr, 1)/3),3,size(arr,2))
  counter = 1
  for i in 1:3:size(arr, 1)
    arr_out[counter, 1, :] = arr[i,:]
    arr_out[counter, 2, :] = arr[i+1, :]
    arr_out[counter, 3, :] = arr[i+2, :]
    counter += 1
  end
  return arr_out
end

"""
    align_all(arr)

Performs procrustes alignment on all the landmarks over all the individuals
in a 3D landmark array as returned by the function to_3d_array().
"""
function align_all(arr)
  ref = arr[:,:,1]
  out = deepcopy(arr)
  for i in 2:size(arr,3)
    out[:,:,i] = align(arr[:,:,i], ref)
  end
  return out
end

"""
    to_2d_array(arr)

converts a 3D landmark array to a 2D landmark array with dimensions
[coordinates x individuals] where coordinates are x,y,z coordinates
repeated as many times as there are landmarks.
"""
function to_2d_array(arr)
  n_lms = size(arr, 1)
  out = zeros(n_lms*3, size(arr, 3))
  for i in 1:size(arr, 3)
    for l in 1:n_lms
      out[l*3-2:l*3, i] = arr[l,:,i]
    end
  end
  return out
end

"""
    mean_shape(arr)

finds the mean shape of all teh individuals in a 3D landmark array. Procrustes
alignment should be performed before finding the mean. Returns the mean shape
and the standard deviations around each point.
"""
function mean_shape(arr)
  n_points = size(arr, 1)
  mean_shape = zeros(n_points, 3)
  stdev = []
  for p in 1:n_points
    all_devs=[]
    mean_shape[p, 1] = mean(arr[p, 1, :])
    mean_shape[p, 2] = mean(arr[p, 2, :])
    mean_shape[p, 3] = mean(arr[p, 3, :])
    for i in 1:size(arr,3)
      push!(all_devs, euclidean(mean_shape[p,:], arr[p,:,i]))
    end
    push!(stdev, std(all_devs))
  end
  return mean_shape, stdev
end

"""
    proc_distance(ref, arr, stdev)

returns the procrustes distances to a reference (e.g. the mean shape)
for every individual in a 3D landmark array in terms of multiples
of the standard deviation around each point.
"""
function proc_distance(ref, arr, stdev)
  n_inds = size(arr, 3)
  n_points = size(arr, 1)
  dists = zeros(1, n_inds)
  for i in 1:n_inds
    maximum_dist = 0.0
    for p in 1:n_points
      dist = euclidean(ref[p, :], arr[p, :, i])/stdev[p]
      if dist>maximum_dist
        maximum_dist = dist
      end
    end
    dists[i] = maximum_dist
  end
  return dists
end

"""
    procrustes_distance_list(arr, names, exclude_highest=false)

returns the procrustes distances to a reference (e.g. the mean shape)
for every individual in a 3D landmark array.
"""
function procrustes_distance_list(arr, names, exclude_highest=false)
  n_inds = size(arr, 2)
  percentile_list = zeros(1,n_inds)
  arr_3d = to_3d_array(arr)
  proc_aligned = align_all(arr_3d)
  proc_mean = mean_shape(proc_aligned)
  proc_dists = proc_distance(proc_mean, proc_aligned)
  max_dist = maximum(proc_dists)
  min_dist = minimum(proc_dists)
  dists_adj = proc_dists .- min_dist
  dist_range = max_dist - min_dist
  ratings = []
  for i in 1:n_inds
    perc = dists_adj[i] / dist_range
    percentile_list[i] = perc
    if perc<=0.5
      push!(ratings, "***")
    elseif perc<=0.75
      push!(ratings, "**")
    elseif perc<=1.0
      push!(ratings, "*")
    end
  end
  out = hcat(hcat(hcat(names, proc_dists'), percentile_list'), ratings)
  if exclude_highest
    outliers = findall(x->x>=0.9, percentile_list[1,:])
    keepers = setdiff(1:n_inds, outliers)
    proc_mean = mean_shape(proc_aligned[:,:,keepers])
    proc_dists = proc_distance(proc_mean, proc_aligned[:,:,keepers])
    max_dist = maximum(proc_dists)
    min_dist = minimum(proc_dists)
    dists_adj = proc_dists .- min_dist
    dist_range = max_dist - min_dist
    for i in 1:length(keepers)
      perc = dists_adj[i] / dist_range
      out[keepers[i],3] = perc
      if perc<=0.5
        out[keepers[i], 4] = "***"
      elseif perc<=0.75
        out[keepers[i], 4] = "**"
      elseif perc<=1.0
        out[keepers[i], 4] = "*"
      end
    end
    out[outliers, 4] .= "excluded as outlier"
    out[outliers, 3] .= "-"
  end
  show(IOContext(stdout, :limit=>false), MIME"text/plain"(), out)
  return out
end

"""
  function align(x,y)

aligns two structures [sets of points in 3D space]. Solves
the "Procrustes" problem. Structures are expected to be of the same size, and the
correspondence is assumed from the vector indices.

Returns x aligned, by performing the rigid body transformation [rotation
and translation that minimizes the RMSD between x and y].

x, y, and xnew (return) are matrices of dimensions (n,3)
(n is the number of points, 3 is the dimension of the space).

L. Martinez, Institute of Chemistry - University of Campinas
Jan 04, 2019
"""
function align( x :: Matrix{Float64}, y :: Matrix{Float64} )

  n = size(x,1)

  # Computing centroid

  cmx = zeros(3)
  cmy = zeros(3)
  for i in 1:n
    for j in 1:3
      cmx[j] = cmx[j] + x[i,j]
      cmy[j] = cmy[j] + y[i,j]
    end
  end
  cmx = cmx / n
  cmy = cmy / n

  # Translating both sets to the origin

  for i in 1:n
    for j in 1:3
      x[i,j] = x[i,j] - cmx[j]
      y[i,j] = y[i,j] - cmy[j]
    end
  end

  # Computing the quaternion matrix

  xm = Vector{Float64}(undef,n)
  ym = Vector{Float64}(undef,n)
  zm = Vector{Float64}(undef,n)
  xp = Vector{Float64}(undef,n)
  yp = Vector{Float64}(undef,n)
  zp = Vector{Float64}(undef,n)
  for i in 1:n
    xm[i] = y[i,1] - x[i,1]
    ym[i] = y[i,2] - x[i,2]
    zm[i] = y[i,3] - x[i,3]
    xp[i] = y[i,1] + x[i,1]
    yp[i] = y[i,2] + x[i,2]
    zp[i] = y[i,3] + x[i,3]
  end

  q = zeros(4,4)
  for i in 1:n
    q[1,1] = q[1,1] + xm[i]^2 + ym[i]^2 + zm[i]^2
    q[1,2] = q[1,2] + yp[i]*zm[i] - ym[i]*zp[i]
    q[1,3] = q[1,3] + xm[i]*zp[i] - xp[i]*zm[i]
    q[1,4] = q[1,4] + xp[i]*ym[i] - xm[i]*yp[i]
    q[2,2] = q[2,2] + yp[i]^2 + zp[i]^2 + xm[i]^2
    q[2,3] = q[2,3] + xm[i]*ym[i] - xp[i]*yp[i]
    q[2,4] = q[2,4] + xm[i]*zm[i] - xp[i]*zp[i]
    q[3,3] = q[3,3] + xp[i]^2 + zp[i]^2 + ym[i]^2
    q[3,4] = q[3,4] + ym[i]*zm[i] - yp[i]*zp[i]
    q[4,4] = q[4,4] + xp[i]^2 + yp[i]^2 + zm[i]^2
  end
  q[2,1] = q[1,2]
  q[3,1] = q[1,3]
  q[3,2] = q[2,3]
  q[4,1] = q[1,4]
  q[4,2] = q[2,4]
  q[4,3] = q[3,4]

  # Computing the eigenvectors 'v' of the q matrix

  v = LinearAlgebra.eigvecs(q)

  # Compute rotation matrix

  u = Matrix{Float64}(undef,3,3)
  u[1,1] = v[1,1]^2 + v[2,1]^2 - v[3,1]^2 - v[4,1]^2
  u[1,2] = 2. * ( v[2,1]*v[3,1] + v[1,1]*v[4,1] )
  u[1,3] = 2. * ( v[2,1]*v[4,1] - v[1,1]*v[3,1] )
  u[2,1] = 2. * ( v[2,1]*v[3,1] - v[1,1]*v[4,1] )
  u[2,2] = v[1,1]^2 + v[3,1]^2 - v[2,1]^2 - v[4,1]^2
  u[2,3] = 2. * ( v[3,1]*v[4,1] + v[1,1]*v[2,1] )
  u[3,1] = 2. * ( v[2,1]*v[4,1] + v[1,1]*v[3,1] )
  u[3,2] = 2. * ( v[3,1]*v[4,1] - v[1,1]*v[2,1] )
  u[3,3] = v[1,1]^2 + v[4,1]^2 - v[2,1]^2 - v[3,1]^2

  # Rotate vector x [will be stored in xnew], and restore y

  xnew = zeros(n,3)
  for i in 1:n
    for j in 1:3
      for k in 1:3
        xnew[i,j] = xnew[i,j] + u[j,k] * x[i,k]
      end
    end
  end

  # Translate vector to the centroid of y [and restore x and y]

  for i in 1:n
    for j in 1:3
      xnew[i,j] = xnew[i,j] + cmy[j]
      y[i,j] = y[i,j] + cmy[j]
      x[i,j] = x[i,j] + cmx[j]
    end
  end

  return xnew

end

ext: jl

max_stars_repo_name: imi-diener/AutoLM

max_issues_repo_name: imi-diener/AutoLM

max_forks_repo_name: imi-diener/AutoLM


text:
#
# Factors Map
#
# log, abs, normalize, etc.

"""
    normalize(ϕ, dims; p=1)
    normalize(ϕ; p=1)

Return a normalized copy of the factor so all instances of dims have
(or the entire factors has) p-norm of 1. Only 1- and 2- norms are supported.
"""
LinAlg.normalize(ϕ::Factor, dims; p::Integer=1) = normalize!(deepcopy(ϕ), dims, p=p)
LinAlg.normalize(ϕ::Factor; p::Integer=1) = normalize!(deepcopy(ϕ), p=p)

"""
    normalize!(ϕ, dims; p=1)
    normalize!(ϕ; p=1)

Normalize the factor so all instances of dims have (or the entire factors has)
p-norm of 1. Only 1- and 2- norms are supported.
"""
LinAlg.normalize!(ϕ::Factor, dim::Symbol; p::Integer=1) = normalize!(ϕ, [dim], p=p)

function LinAlg.normalize!(ϕ::Factor, dims::Vector{Symbol}; p::Int=1)
    isempty(dims) && return ϕ
    _check_dims_valid(dims, ϕ)

    inds = indexin(dims, ϕ)
    ϕ.potential ./= _get_total(ϕ.potential, p, inds)

    return ϕ
end

function LinAlg.normalize!(ϕ::Factor; p::Integer=1)
    ϕ.potential ./= _get_total(ϕ.potential, p)

    return ϕ
end

# zero-dimensional gets weird ...  as always
function LinAlg.normalize!(ϕ::Factor{0}; p::Integer=1)
    ϕ.potential ./= first(_get_total(ϕ.potential, p))

    return ϕ
end

function _get_total{V<:Real, N}(A::Array{V, N}, p, r=(1:N))
    if p == 1
        total = sumabs(A, r)
    elseif p == 2
        total = sqrt(sumabs2(A, r))
    else
        throw(ArgumentError("p = $(p) is not supported"))
    end

    return total
end

"""
    map(f, ϕ::Factor)

Return a new Factor with `f` mapped over `ϕ.potential`.
"""
Base.map(f, ϕ::Factor) = Factor(deepcopy(scope(ϕ)), map(f, ϕ.potential))

"""
    map!(f, ϕ::Factor)

Map `f` over `ϕ.potential`.
"""
@inline function Base.map!(f, ϕ::Factor)
    map!(f, ϕ.potential)
    _check_negatives(ϕ)
    return ϕ
end

"""
    rand!(ϕ::Factor)

Fill `ϕ` with random values.
"""
@inline function Base.rand!(ϕ::Factor)
    rand!(ϕ.potential)
    _check_negatives(ϕ)
    return ϕ
end

"""
    rand!(ϕ::Factor)

Fill `ϕ` with random values from the standard normal.
"""
@inline function Base.randn!(ϕ::Factor)
    randn!(ϕ.potential)
    _check_negatives(ϕ)
    return ϕ
end

"""
    fill!(ϕ::Factor, x)

Fill `ϕ` with `x`.
"""
@inline function Base.fill!(ϕ::Factor, x)
    fill!(ϕ.potential, x)
    _check_negatives(ϕ)
    return ϕ
end

# shamelessly stolen from julia/base/deprecated.jl
for f in (:sin, :sinh, :sind, :asin, :asinh, :asind,
        :tan, :tanh, :tand, :atan, :atanh, :atand,
        :sinpi, :cosc, :ceil, :floor, :trunc, :round, :real, :imag,
        :log1p, :expm1, :abs, :abs2,
        :log, :log2, :log10, :exp, :exp2, :exp10, :sinc, :cospi,
        :cos, :cosh, :cosd, :acos, :acosd,
        :cot, :coth, :cotd, :acot, :acotd,
        :sec, :sech, :secd, :asech,
        :csc, :csch, :cscd, :acsch)
    q = parse("Base.$f")
    @eval ($q)(ϕ::Factor) = map($f, ϕ)
end


text:
module AdaOPS

using POMDPs
using BeliefUpdaters
using Parameters
using CPUTime
using ParticleFilters
using D3Trees
using Random
using Printf
using POMDPModelTools
using POMDPSimulators
using POMDPLinter
using LinearAlgebra
using Distances
using Statistics
using StaticArrays
using Distributions
using Plots
using Plots.PlotMeasures

using MCTS
import MCTS: convert_to_policy
using BasicPOMCP

export
    AdaOPSSolver,
    AdaOPSPlanner,
    AdaOPSTree,

    WPFBelief,

    default_action,

    IndependentBounds,
    bounds,
    init_bounds,
    bound,
    init_bound,

    FOValue,
    POValue,
    FORollout,
    PORollout,
    SemiPORollout,
    RolloutEstimator,

    StateGrid,
    KLDSampleSize,
    zeros_like,
    access,

    info_analysis,
    hist_analysis

include("grid.jl")
include("sampling.jl")

"""
    AdaOPSSolver(<keyword arguments>)

Each field may be set via keyword argument. The fields that correspond to algorithm
parameters match the definitions in the paper exactly.

# Fields
- `epsilon_0`
- `xi`
- `K`
- `D`
- `lambda`
- `T_max`
- `max_trials`
- `bounds`
- `default_action`
- `rng`
- `random_source`
- `bounds_warnings`
- `tree_in_info`

Further information can be found in the field docstrings (e.g.
`?AdaOPSSolver.xi`)
"""
@with_kw struct AdaOPSSolver{N, R<:AbstractRNG, T} <: Solver
    "The target gap between the upper and the lower bound at the root of the AdaOPS tree."
    epsilon_0::Float64                      = 1e-3

    "The minimum relative gap required for a branch to be expanded."
    xi::Float64                             = 0.95

    "The δ-packing of beliefs will be generated."
    delta::Float64                          = 0.1

    "State grid for adaptive particle filters"
    grid::StateGrid{N}                      = StateGrid()

    "The minimum number of bins a belief occupies (at least 2)."
    min_occupied_bins::Int                  = 2

    "The maximum number of bins a belief occupies (default to the grid size)."
    max_occupied_bins::Int                  = prod(size(grid))

    "The minimum number of particles for approximating beliefs"
    m_min::Int                              = 30

    "The target error for belief estimation."
    zeta::Float64                           = KLDSampleSize(min_occupied_bins, m_min)

    "The maximum number of particles for approximating beliefs"
    m_max::Int                              = max(ceil(Int, KLDSampleSize(max_occupied_bins, zeta)), m_min)

    "Resample when the design effect of a belief node exceed Deff_thres"
    Deff_thres::Float64                     = 2.0

    "The maximum depth of the belief tree."
    max_depth::Int                          = 90

    "The maximum online planning time per step."
    T_max::Float64                          = 1.0

    "The maximum number of trials of the planner."
    max_trials::Int                         = typemax(Int)

    "A representation for the upper and lower bound on the discounted value (e.g. `IndependentBounds`)."
    bounds::Any                             = IndependentBounds(-1e6, 1e6)

    """A default action to use if algorithm fails to provide an action because of an error.

    This can either be an action object, i.e. `default_action=1` if `actiontype(pomdp)==Int` or a function `f(pomdp, b, ex)` where b is the belief and ex is the exception that caused the planner to fail.
    """
    default_action::Any                     = ExceptionRethrow()

    "A random number generator for the internal sampling processes."
    rng::R                                  = MersenneTwister(rand(UInt32))

    "If true, sanity checks on the provided bounds are performed."
    bounds_warnings::Bool                   = false

    "If true, a reprenstation of the constructed AdaOPSTree is returned by POMDPModelTools.action_info."
    tree_in_info::Bool                      = false

    "Issue an warning when the planning time surpass the time limit by `timeout_warning_threshold`"
    timeout_warning_threshold::Float64      = T_max * 2.0

    "Search iterations end when `timer() - start_time ≥ T_max`.
    Default to `() -> 1e-9*time_ns()`, which gets the current system time.
    Switch to `() -> 1e-6*CPUtime_us()` when the CPU time is concerned."
    timer::T                                = () -> 1e-9*time_ns()

    "Number of pre-allocated belief nodes"
    num_b::Int                              = 50_000
end

mutable struct AdaOPSTree{S,A,O}
    # belief nodes
    weights::Vector{Vector{Float64}} # stores weights for *belief node*
    children::Vector{UnitRange{Int}} # to children *ba nodes*
    parent::Vector{Int} # maps to the parent *ba node*
    Delta::Vector{Int}
    u::Vector{Float64}
    l::Vector{Float64}
    obs::Vector{O}
    obs_prob::Vector{Float64}

    # action nodes
    ba_particles::Vector{Vector{S}} # stores particles for *ba nodes*
    ba_children::Vector{UnitRange{Int}}
    ba_parent::Vector{Int} # maps to parent *belief node*
    ba_u::Vector{Float64}
    ba_l::Vector{Float64}
    ba_r::Vector{Float64} # needed for backup
    ba_action::Vector{A}

    root_belief::WeightedParticleBelief
    b::Int
    ba::Int
end

mutable struct AdaOPSPlanner{S, A, O, P<:POMDP{S,A,O}, N, B, OD, RNG<:AbstractRNG} <: Policy
    sol::AdaOPSSolver{N, RNG}
    pomdp::P
    bounds::B
    discounts::Vector{Float64}
    rng::RNG
    # The following attributes are used to avoid reallocating memory
    resampled::WeightedParticleBelief{S}
    obs::Vector{O}
    obs_ind_dict::Dict{O, Int}
    w::Vector{Vector{Float64}}
    access_cnt::Array{Int, N}
    obs_w::Vector{Float64}
    u::Vector{Float64}
    l::Vector{Float64}
    obs_dists::Vector{OD}
    tree::Union{Nothing, AdaOPSTree{S,A,O}}
end

function AdaOPSPlanner(sol::AdaOPSSolver{N}, pomdp::POMDP{S,A,O}) where {S,A,O,N}
    rng = deepcopy(sol.rng)
    bounds = init_bounds(sol.bounds, pomdp, sol, rng)
    discounts = discount(pomdp) .^[0:(sol.max_depth+1);]
    @assert sol.m_max >= sol.m_min

    m_min = sol.m_min
    m_max = sol.m_max
    access_cnt = zeros_like(sol.grid)
    obs_dists = Vector{typeof(observation(pomdp, first(actions(pomdp)), rand(initialstate(pomdp))))}(undef, m_max)
    return AdaOPSPlanner(deepcopy(sol), pomdp, bounds, discounts, rng,
                        WeightedParticleBelief(sizehint!(Vector{S}(undef, m_max), m_max), sizehint!(ones(m_max), m_max), m_max), sizehint!(O[], m_max),
                        Dict{O, Int}(), sizehint!(Vector{Float64}[], m_max), access_cnt,
                        sizehint!(Float64[], m_max), sizehint!(Float64[], m_max), sizehint!(Float64[], m_max),
                        obs_dists, nothing)
end

solver(p::AdaOPSPlanner) = p.sol

include("wpf_belief.jl")
include("bounds.jl")
include("tree.jl")
include("planner.jl")
include("pomdps_glue.jl")
include("visualization.jl")
include("analysis.jl")

end # module


text:
using Documenter, Aeconomica

makedocs(
    modules = [Aeconomica],
    format = Documenter.HTML(; prettyurls = get(ENV, "CI", nothing) == "true"),
    authors = "aeconomica",
    sitename = "Aeconomica.jl",
    pages = Any["index.md"],
    # strict = true,
    # clean = true,
    checkdocs = :exports
)

deploydocs(
    repo = "github.com/aeconomica/Aeconomica.jl.git",
    push_preview = true,
    devbranch = "main"
)


text:
"""
```julia
 createpath(output::Real, path::String; namelist::String="")

 return  FileNamesType
 ```
"""
function createpath(output::Real, path::String; namelist::String="")

    if output < 10
        Path_folder = "output_0000$output"
        Info_file   = "info_0000$output.txt"
        AMR_file    = "amr_0000$output."
        Hydro_file  = "hydro_0000$output."
        Grav_file   = "grav_0000$output."
        RT_file     = "rt_0000$output."
        RT_descriptor = "info_rt_0000$output.txt"
        Part_file   = "part_0000$output."
        Clump_file  = "clump_0000$output."
        Timer_file  = "timer_0000$output.txt"
        Header_file = "header_0000$output.txt"

    elseif output < 100 && output > 9
        Path_folder = "output_000$output"
        Info_file   = "info_000$output.txt"
        AMR_file    = "amr_000$output."
        Hydro_file  = "hydro_000$output."
        Grav_file   = "grav_000$output."
        RT_file     = "rt_000$output."
        RT_descriptor = "info_rt_000$output.txt"
        Part_file   = "part_000$output."
        Clump_file  = "clump_000$output."
        Timer_file  = "timer_000$output.txt"
        Header_file = "header_000$output.txt"

    elseif output < 1000 && output > 99
        Path_folder = "output_00$output"
        Info_file   = "info_00$output.txt"
        AMR_file    = "amr_00$output."
        Hydro_file  = "hydro_00$output."
        Grav_file   = "grav_00$output."
        RT_file     = "rt_00$output."
        RT_descriptor = "info_rt_00$output.txt"
        Part_file   = "part_00$output."
        Clump_file  = "clump_00$output."
        Timer_file  = "timer_00$output.txt"
        Header_file = "header_00$output.txt"

    elseif output < 10000 && output > 999
        Path_folder = "output_0$output"
        Info_file   = "info_0$output.txt"
        AMR_file    = "amr_0$output."
        Hydro_file  = "hydro_0$output."
        Grav_file   = "grav_0$output."
        RT_file     = "rt_0$output."
        RT_descriptor = "info_rt_0$output.txt"
        Part_file   = "part_0$output."
        Clump_file  = "clump_0$output."
        Timer_file  = "timer_0$output.txt"
        Header_file = "header_0$output.txt"

    elseif output < 100000 && output > 9999
        Path_folder = "output_$output"
        Info_file   = "info_$output.txt"
        AMR_file    = "amr_$output."
        Hydro_file  = "hydro_$output."
        Grav_file   = "grav_$output."
        RT_file     = "rt_$output."
        RT_descriptor = "info_rt_$output.txt"
        Part_file   = "part_$output."
        Clump_file  = "clump_$output."
        Timer_file  = "timer_$output.txt"
        Header_file = "header_$output.txt"

    end

    fnames = FileNamesType()
    fnames.output    = joinpath(path, Path_folder)
    fnames.info      = joinpath(path, Path_folder, Info_file)
    fnames.amr       = joinpath(path, Path_folder, AMR_file)
    fnames.hydro     = joinpath(path, Path_folder, Hydro_file)
    fnames.hydro_descriptor  = joinpath(path, Path_folder, "hydro_file_descriptor.txt")
    fnames.gravity   = joinpath(path, Path_folder, Grav_file)
    fnames.particles = joinpath(path, Path_folder, Part_file)
    fnames.part_descriptor  = joinpath(path, Path_folder, "part_file_descriptor.txt")
    fnames.rt        = joinpath(path, Path_folder, RT_file)
    fnames.rt_descriptor    = joinpath(path, Path_folder, "rt_file_descriptor.txt") # newer version
    fnames.rt_descriptor_v0  = joinpath(path, Path_folder, RT_descriptor) # older version
    fnames.clumps    = joinpath(path, Path_folder, Clump_file)
    fnames.timer     = joinpath(path, Path_folder, Timer_file)
    fnames.header    = joinpath(path, Path_folder, Header_file)

    fnames.compilation = joinpath(path, Path_folder, "compilation.txt")
    fnames.makefile    = joinpath(path, Path_folder, "makefile.txt")
    fnames.patchfile   = joinpath(path, Path_folder, "patches.txt")

    if namelist == "" || namelist == "./"
        fnames.namelist  = joinpath(path, Path_folder, "namelist.txt")
    else
        fnames.namelist  = namelist
    end

    return fnames
end

function createpath!(dataobject::InfoType; namelist::String="")
    dataobject.fnames = createpath(dataobject.output, dataobject.path, namelist=namelist)
    return dataobject
end

function getproc2string(path::String, icpu::Int32)
    if icpu < 10
        return string(path, "out0000", icpu)
    elseif icpu < 100 && icpu > 9
        return string(path, "out000", icpu)
    elseif icpu < 1000 && icpu > 99
        return string(path, "out00", icpu)
    elseif icpu < 10000 && icpu > 999
        return string(path, "out0", icpu)
    elseif icpu < 100000 && icpu > 9999
        return string(path, "out", icpu)
    end
end

function getproc2string(path::String, textfile::Bool, icpu::Int)
    if icpu < 10
        return string(path, "txt0000", icpu)
    elseif icpu < 100 && icpu > 9
        return string(path, "txt000", icpu)
    elseif icpu < 1000 && icpu > 99
        return string(path, "txt00", icpu)
    elseif icpu < 10000 && icpu > 999
        return string(path, "txt0", icpu)
    elseif icpu < 100000 && icpu > 9999
        return string(path, "txt", icpu)
    end
end


text:
#!/usr/bin/julia

# Copyright (C) 2021 Matheus Fernandes Bigolin <mfrdrbigolin@disroot.org>
# SPDX-License-Identifier: MIT

"Day Seventeen, Trick Shot."

#= I am working in an analytical solution, but, for the time being, I will
 = leave the brute-force solution in part two due to time constrainsts (my
 = personal time, not the program’s).  At the very least, I put some limitations
 = on the ranges.
 =#

include("Utils.jl")

using .Utils

#= Throught this entire code the target area is assumed to be to the
 = bottom-right of the thrower.
 =#

"Find the maximum probe height possible that still lands in `target`."
function solve1(target)
    #= When the probe crosses the y = 0 line for the second time it’s velocity
     = will always be -vy - 1 (assuming vy >= 0).  Therefore, we want the
     = probe’s next step to fall exactly within the outermost vertical line of
     = the target area to maximize it’s vy.
     =#
    max_y = -target[2].start - 1

    return div(max_y^2 + max_y, 2)
end

"Calculate the x position at time `t` given the horizontal speed `vx`."
calc_x(t, vx) = if t < vx t*vx - div(t^2 - t, 2) else div(vx^2 + vx, 2) end
"Calculate the y position at time `t` given the vertical speed `vy`."
calc_y(t, vy) = t*vy - div(t^2 - t, 2)
"Calculate the position at time `t` given the speed pair `(vx, vy)`."
calc_xy(t, (vx, vy)) = (calc_x(t, vx), calc_y(t, vy))

"Count all the initial velocities that fall within the `target`."
function solve2(target)
    found_count = 0

    max_x = target[1].stop

    min_y = target[2].start
    max_y = -target[2].start - 1

    #= The speed that takes the maximum time is the one with maximum vertical
     = velocity.
     =#
    max_time = 2max_y + 2

    for vx in 1:max_x
        for vy in min_y:max_y
            for t in 1:max_time
                if all(calc_xy(t, (vx, vy)) .∈ target)
                    found_count += 1

                    break
                end
            end
        end
    end

    return found_count
end

const AREA_REX = r"target area: x=([-]?\d+)\.\.([-]?\d+), y=([-]?\d+)\.\.([-]?\d+)"

function main()
    usage_and_exit(length(ARGS) != 1)

    data = readline(first(ARGS))
    x₀, x₁, y₀, y₁ = parse.(Int, match(AREA_REX, data).captures)
    target = [x₀:x₁, y₀:y₁]

    println(solve1(target))
    println(solve2(target))
end

if abspath(PROGRAM_FILE) == @__FILE__
    main()
end


text:
function part1(fname::String)
  open(fname, "r") do f
    data = readlines(f)

    vent_map = Dict{Tuple{Int, Int}, Int}()
    for entry in data
      p1, _, p2 = split.(split(entry), ',')
      p1 = parse.(Int, p1)
      p2 = parse.(Int, p2)

      if p1[1] != p2[1] && p1[2] != p2[2]
        continue
      end

      for x in min(p1[1], p2[1]):1:max(p1[1], p2[1])
        for y in min(p1[2], p2[2]):1:max(p1[2], p2[2])
          vent_map[(x, y)] = get(vent_map, (x, y), 0) + 1
        end
      end
    end
    n_danger = sum((values(vent_map) .> 1))
    println("Number of dangerous spots: $n_danger")
  end
end

function part2(fname::String)
  open(fname, "r") do f
    data = readlines(f)

    vent_map = Dict{Tuple{Int, Int}, Int}()
    for entry in data
      p1, _, p2 = split.(split(entry), ',')
      p1 = parse.(Int, p1)
      p2 = parse.(Int, p2)

      if p1[1] != p2[1] && p1[2] != p2[2]
        for (x, y) in zip(p1[1]:sign(p2[1] - p1[1]):p2[1],
                          p1[2]:sign(p2[2] - p1[2]):p2[2])
          vent_map[(x, y)] = get(vent_map, (x, y), 0) + 1
        end
      else
        for x in min(p1[1], p2[1]):1:max(p1[1], p2[1])
          for y in min(p1[2], p2[2]):1:max(p1[2], p2[2])
            vent_map[(x, y)] = get(vent_map, (x, y), 0) + 1
          end
        end
      end
    end
    n_danger = sum((values(vent_map) .> 1))
    println("Number of dangerous spots: $n_danger")
  end
end

part1("test_input.txt")
part1("input.txt")

part2("test_input.txt")
part2("input.txt")


text:
using AutoAccessorMethods
using Test

module Mod2
    export V

    using AutoAccessorMethods

    @auto_accessor_methods struct V
        V_1
    end true
end

using .Mod2

v = V(1//2)

@test V_1(v::V) == 1//2


text:
__size(args::Tuple) = Tuple{args...}
__size(i::Int) = Tuple{i}

__shmem(D::Device, args...) = throw(MethodError(__shmem, (D, args...)))
@inline __shmem(::CPU, ::Type{T}, ::Val{dims}, ::Val) where {T, dims} =MArray{__size(dims), T}(undef)

@init @require CUDAnative="be33ccc6-a3ff-5ff2-a52e-74243cff1e17" begin
    using .CUDAnative

    @inline function __shmem(::CUDA, ::Type{T}, ::Val{dims}, ::Val{id}) where {T, dims, id}
        ptr = CUDAnative._shmem(Val(id), T, Val(prod(dims)))
        CUDAnative.CuDeviceArray(dims, CUDAnative.DevicePtr{T, CUDAnative.AS.Shared}(ptr))
    end
end

shmem_id = 0
macro shmem(T, dims)
    global shmem_id
    id = shmem_id::Int += 1

    quote
        $__shmem($backend(), $(esc(T)), Val($(esc(dims))), Val($id))
    end
end


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

"""
    AcceptInvitation()

Accepts the invitation to be a member account and be monitored by the Security Hub master account that the invitation was sent from. This operation is only used by member accounts that are not added through Organizations. When the member account accepts the invitation, permission is granted to the master account to view findings generated in the member account.

# Required Parameters
- `InvitationId`: The ID of the invitation sent from the Security Hub master account.
- `MasterId`: The account ID of the Security Hub master account that sent the invitation.

"""
accept_invitation(InvitationId, MasterId; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/master", Dict{String, Any}("InvitationId"=>InvitationId, "MasterId"=>MasterId); aws_config=aws_config)
accept_invitation(InvitationId, MasterId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/master", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("InvitationId"=>InvitationId, "MasterId"=>MasterId), args)); aws_config=aws_config)

"""
    BatchDisableStandards()

Disables the standards specified by the provided StandardsSubscriptionArns. For more information, see Security Standards section of the AWS Security Hub User Guide.

# Required Parameters
- `StandardsSubscriptionArns`: The ARNs of the standards subscriptions to disable.

"""
batch_disable_standards(StandardsSubscriptionArns; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/standards/deregister", Dict{String, Any}("StandardsSubscriptionArns"=>StandardsSubscriptionArns); aws_config=aws_config)
batch_disable_standards(StandardsSubscriptionArns, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/standards/deregister", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("StandardsSubscriptionArns"=>StandardsSubscriptionArns), args)); aws_config=aws_config)

"""
    BatchEnableStandards()

Enables the standards specified by the provided StandardsArn. To obtain the ARN for a standard, use the  DescribeStandards  operation. For more information, see the Security Standards section of the AWS Security Hub User Guide.

# Required Parameters
- `StandardsSubscriptionRequests`: The list of standards checks to enable.

"""
batch_enable_standards(StandardsSubscriptionRequests; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/standards/register", Dict{String, Any}("StandardsSubscriptionRequests"=>StandardsSubscriptionRequests); aws_config=aws_config)
batch_enable_standards(StandardsSubscriptionRequests, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/standards/register", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("StandardsSubscriptionRequests"=>StandardsSubscriptionRequests), args)); aws_config=aws_config)

"""
    BatchImportFindings()

Imports security findings generated from an integrated third-party product into Security Hub. This action is requested by the integrated product to import its findings into Security Hub. The maximum allowed size for a finding is 240 Kb. An error is returned for any finding larger than 240 Kb. After a finding is created, BatchImportFindings cannot be used to update the following finding fields and objects, which Security Hub customers use to manage their investigation workflow.    Confidence     Criticality     Note     RelatedFindings     Severity     Types     UserDefinedFields     VerificationState     Workflow

# Required Parameters
- `Findings`: A list of findings to import. To successfully import a finding, it must follow the AWS Security Finding Format. Maximum of 100 findings per request.

"""
batch_import_findings(Findings; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/findings/import", Dict{String, Any}("Findings"=>Findings); aws_config=aws_config)
batch_import_findings(Findings, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/findings/import", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Findings"=>Findings), args)); aws_config=aws_config)

"""
    BatchUpdateFindings()

Used by Security Hub customers to update information about their investigation into a finding. Requested by master accounts or member accounts. Master accounts can update findings for their account and their member accounts. Member accounts can update findings for their account. Updates from BatchUpdateFindings do not affect the value of UpdatedAt for a finding. Master and member accounts can use BatchUpdateFindings to update the following finding fields and objects.    Confidence     Criticality     Note     RelatedFindings     Severity     Types     UserDefinedFields     VerificationState     Workflow    You can configure IAM policies to restrict access to fields and field values. For example, you might not want member accounts to be able to suppress findings or change the finding severity. See Configuring access to BatchUpdateFindings in the AWS Security Hub User Guide.

# Required Parameters
- `FindingIdentifiers`: The list of findings to update. BatchUpdateFindings can be used to update up to 100 findings at a time. For each finding, the list provides the finding identifier and the ARN of the finding provider.

# Optional Parameters
- `Confidence`: The updated value for the finding confidence. Confidence is defined as the likelihood that a finding accurately identifies the behavior or issue that it was intended to identify. Confidence is scored on a 0-100 basis using a ratio scale, where 0 means zero percent confidence and 100 means 100 percent confidence.
- `Criticality`: The updated value for the level of importance assigned to the resources associated with the findings. A score of 0 means that the underlying resources have no criticality, and a score of 100 is reserved for the most critical resources.
- `Note`:
- `RelatedFindings`: A list of findings that are related to the updated findings.
- `Severity`: Used to update the finding severity.
- `Types`: One or more finding types in the format of namespace/category/classifier that classify a finding. Valid namespace values are as follows.   Software and Configuration Checks   TTPs   Effects   Unusual Behaviors   Sensitive Data Identifications
- `UserDefinedFields`: A list of name/value string pairs associated with the finding. These are custom, user-defined fields added to a finding.
- `VerificationState`: Indicates the veracity of a finding. The available values for VerificationState are as follows.    UNKNOWN – The default disposition of a security finding    TRUE_POSITIVE – The security finding is confirmed    FALSE_POSITIVE – The security finding was determined to be a false alarm    BENIGN_POSITIVE – A special case of TRUE_POSITIVE where the finding doesn't pose any threat, is expected, or both
- `Workflow`: Used to update the workflow status of a finding. The workflow status indicates the progress of the investigation into the finding.
"""
batch_update_findings(FindingIdentifiers; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/findings/batchupdate", Dict{String, Any}("FindingIdentifiers"=>FindingIdentifiers); aws_config=aws_config)
batch_update_findings(FindingIdentifiers, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/findings/batchupdate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("FindingIdentifiers"=>FindingIdentifiers), args)); aws_config=aws_config)

"""
    CreateActionTarget()

Creates a custom action target in Security Hub. You can use custom actions on findings and insights in Security Hub to trigger target actions in Amazon CloudWatch Events.

# Required Parameters
- `Description`: The description for the custom action target.
- `Id`: The ID for the custom action target.
- `Name`: The name of the custom action target.

"""
create_action_target(Description, Id, Name; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/actionTargets", Dict{String, Any}("Description"=>Description, "Id"=>Id, "Name"=>Name); aws_config=aws_config)
create_action_target(Description, Id, Name, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/actionTargets", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Description"=>Description, "Id"=>Id, "Name"=>Name), args)); aws_config=aws_config)

"""
    CreateInsight()

Creates a custom insight in Security Hub. An insight is a consolidation of findings that relate to a security issue that requires attention or remediation. To group the related findings in the insight, use the GroupByAttribute.

# Required Parameters
- `Filters`: One or more attributes used to filter the findings included in the insight. The insight only includes findings that match the criteria defined in the filters.
- `GroupByAttribute`: The attribute used to group the findings for the insight. The grouping attribute identifies the type of item that the insight applies to. For example, if an insight is grouped by resource identifier, then the insight produces a list of resource identifiers.
- `Name`: The name of the custom insight to create.

"""
create_insight(Filters, GroupByAttribute, Name; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/insights", Dict{String, Any}("Filters"=>Filters, "GroupByAttribute"=>GroupByAttribute, "Name"=>Name); aws_config=aws_config)
create_insight(Filters, GroupByAttribute, Name, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/insights", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Filters"=>Filters, "GroupByAttribute"=>GroupByAttribute, "Name"=>Name), args)); aws_config=aws_config)

"""
    CreateMembers()

Creates a member association in Security Hub between the specified accounts and the account used to make the request, which is the master account. If you are integrated with Organizations, then the master account is the Security Hub administrator account that is designated by the organization management account.  CreateMembers is always used to add accounts that are not organization members. For accounts that are part of an organization, CreateMembers is only used in the following cases:   Security Hub is not configured to automatically add new accounts in an organization.   The account was disassociated or deleted in Security Hub.   This action can only be used by an account that has Security Hub enabled. To enable Security Hub, you can use the  EnableSecurityHub  operation. For accounts that are not organization members, you create the account association and then send an invitation to the member account. To send the invitation, you use the  InviteMembers  operation. If the account owner accepts the invitation, the account becomes a member account in Security Hub. Accounts that are part of an organization do not receive an invitation. They automatically become a member account in Security Hub. A permissions policy is added that permits the master account to view the findings generated in the member account. When Security Hub is enabled in a member account, findings are sent to both the member and master accounts.  To remove the association between the master and member accounts, use the  DisassociateFromMasterAccount  or  DisassociateMembers  operation.

# Required Parameters
- `AccountDetails`: The list of accounts to associate with the Security Hub master account. For each account, the list includes the account ID and optionally the email address.

"""
create_members(AccountDetails; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members", Dict{String, Any}("AccountDetails"=>AccountDetails); aws_config=aws_config)
create_members(AccountDetails, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AccountDetails"=>AccountDetails), args)); aws_config=aws_config)

"""
    DeclineInvitations()

Declines invitations to become a member account. This operation is only used by accounts that are not part of an organization. Organization accounts do not receive invitations.

# Required Parameters
- `AccountIds`: The list of account IDs for the accounts from which to decline the invitations to Security Hub.

"""
decline_invitations(AccountIds; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/invitations/decline", Dict{String, Any}("AccountIds"=>AccountIds); aws_config=aws_config)
decline_invitations(AccountIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/invitations/decline", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AccountIds"=>AccountIds), args)); aws_config=aws_config)

"""
    DeleteActionTarget()

Deletes a custom action target from Security Hub. Deleting a custom action target does not affect any findings or insights that were already sent to Amazon CloudWatch Events using the custom action.

# Required Parameters
- `ActionTargetArn`: The ARN of the custom action target to delete.

"""
delete_action_target(ActionTargetArn; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/actionTargets/$(ActionTargetArn)"; aws_config=aws_config)
delete_action_target(ActionTargetArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/actionTargets/$(ActionTargetArn)", args; aws_config=aws_config)

"""
    DeleteInsight()

Deletes the insight specified by the InsightArn.

# Required Parameters
- `InsightArn`: The ARN of the insight to delete.

"""
delete_insight(InsightArn; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/insights/$(InsightArn)"; aws_config=aws_config)
delete_insight(InsightArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/insights/$(InsightArn)", args; aws_config=aws_config)

"""
    DeleteInvitations()

Deletes invitations received by the AWS account to become a member account. This operation is only used by accounts that are not part of an organization. Organization accounts do not receive invitations.

# Required Parameters
- `AccountIds`: The list of the account IDs that sent the invitations to delete.

"""
delete_invitations(AccountIds; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/invitations/delete", Dict{String, Any}("AccountIds"=>AccountIds); aws_config=aws_config)
delete_invitations(AccountIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/invitations/delete", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AccountIds"=>AccountIds), args)); aws_config=aws_config)

"""
    DeleteMembers()

Deletes the specified member accounts from Security Hub. Can be used to delete member accounts that belong to an organization as well as member accounts that were invited manually.

# Required Parameters
- `AccountIds`: The list of account IDs for the member accounts to delete.

"""
delete_members(AccountIds; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/delete", Dict{String, Any}("AccountIds"=>AccountIds); aws_config=aws_config)
delete_members(AccountIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/delete", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AccountIds"=>AccountIds), args)); aws_config=aws_config)

"""
    DescribeActionTargets()

Returns a list of the custom action targets in Security Hub in your account.

# Optional Parameters
- `ActionTargetArns`: A list of custom action target ARNs for the custom action targets to retrieve.
- `MaxResults`: The maximum number of results to return.
- `NextToken`: The token that is required for pagination. On your first call to the DescribeActionTargets operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
describe_action_targets(; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/actionTargets/get"; aws_config=aws_config)
describe_action_targets(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/actionTargets/get", args; aws_config=aws_config)

"""
    DescribeHub()

Returns details about the Hub resource in your account, including the HubArn and the time when you enabled Security Hub.

# Optional Parameters
- `HubArn`: The ARN of the Hub resource to retrieve.
"""
describe_hub(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/accounts"; aws_config=aws_config)
describe_hub(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/accounts", args; aws_config=aws_config)

"""
    DescribeOrganizationConfiguration()

Returns information about the Organizations configuration for Security Hub. Can only be called from a Security Hub administrator account.

"""
describe_organization_configuration(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/organization/configuration"; aws_config=aws_config)
describe_organization_configuration(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/organization/configuration", args; aws_config=aws_config)

"""
    DescribeProducts()

Returns information about the available products that you can subscribe to and integrate with Security Hub in order to consolidate findings.

# Optional Parameters
- `MaxResults`: The maximum number of results to return.
- `NextToken`: The token that is required for pagination. On your first call to the DescribeProducts operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
describe_products(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/products"; aws_config=aws_config)
describe_products(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/products", args; aws_config=aws_config)

"""
    DescribeStandards()

Returns a list of the available standards in Security Hub. For each standard, the results include the standard ARN, the name, and a description.

# Optional Parameters
- `MaxResults`: The maximum number of standards to return.
- `NextToken`: The token that is required for pagination. On your first call to the DescribeStandards operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
describe_standards(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/standards"; aws_config=aws_config)
describe_standards(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/standards", args; aws_config=aws_config)

"""
    DescribeStandardsControls()

Returns a list of security standards controls. For each control, the results include information about whether it is currently enabled, the severity, and a link to remediation information.

# Required Parameters
- `StandardsSubscriptionArn`: The ARN of a resource that represents your subscription to a supported standard. To get the subscription ARNs of the standards you have enabled, use the  GetEnabledStandards  operation.

# Optional Parameters
- `MaxResults`: The maximum number of security standard controls to return.
- `NextToken`: The token that is required for pagination. On your first call to the DescribeStandardsControls operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
describe_standards_controls(StandardsSubscriptionArn; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/standards/controls/$(StandardsSubscriptionArn)"; aws_config=aws_config)
describe_standards_controls(StandardsSubscriptionArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/standards/controls/$(StandardsSubscriptionArn)", args; aws_config=aws_config)

"""
    DisableImportFindingsForProduct()

Disables the integration of the specified product with Security Hub. After the integration is disabled, findings from that product are no longer sent to Security Hub.

# Required Parameters
- `ProductSubscriptionArn`: The ARN of the integrated product to disable the integration for.

"""
disable_import_findings_for_product(ProductSubscriptionArn; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/productSubscriptions/$(ProductSubscriptionArn)"; aws_config=aws_config)
disable_import_findings_for_product(ProductSubscriptionArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/productSubscriptions/$(ProductSubscriptionArn)", args; aws_config=aws_config)

"""
    DisableOrganizationAdminAccount()

Disables a Security Hub administrator account. Can only be called by the organization management account.

# Required Parameters
- `AdminAccountId`: The AWS account identifier of the Security Hub administrator account.

"""
disable_organization_admin_account(AdminAccountId; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/organization/admin/disable", Dict{String, Any}("AdminAccountId"=>AdminAccountId); aws_config=aws_config)
disable_organization_admin_account(AdminAccountId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/organization/admin/disable", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AdminAccountId"=>AdminAccountId), args)); aws_config=aws_config)

"""
    DisableSecurityHub()

Disables Security Hub in your account only in the current Region. To disable Security Hub in all Regions, you must submit one request per Region where you have enabled Security Hub. When you disable Security Hub for a master account, it doesn't disable Security Hub for any associated member accounts. When you disable Security Hub, your existing findings and insights and any Security Hub configuration settings are deleted after 90 days and cannot be recovered. Any standards that were enabled are disabled, and your master and member account associations are removed. If you want to save your existing findings, you must export them before you disable Security Hub.

"""
disable_security_hub(; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/accounts"; aws_config=aws_config)
disable_security_hub(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/accounts", args; aws_config=aws_config)

"""
    DisassociateFromMasterAccount()

Disassociates the current Security Hub member account from the associated master account. This operation is only used by accounts that are not part of an organization. For organization accounts, only the master account (the designated Security Hub administrator) can disassociate a member account.

"""
disassociate_from_master_account(; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/master/disassociate"; aws_config=aws_config)
disassociate_from_master_account(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/master/disassociate", args; aws_config=aws_config)

"""
    DisassociateMembers()

Disassociates the specified member accounts from the associated master account. Can be used to disassociate both accounts that are in an organization and accounts that were invited manually.

# Required Parameters
- `AccountIds`: The account IDs of the member accounts to disassociate from the master account.

"""
disassociate_members(AccountIds; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/disassociate", Dict{String, Any}("AccountIds"=>AccountIds); aws_config=aws_config)
disassociate_members(AccountIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/disassociate", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AccountIds"=>AccountIds), args)); aws_config=aws_config)

"""
    EnableImportFindingsForProduct()

Enables the integration of a partner product with Security Hub. Integrated products send findings to Security Hub. When you enable a product integration, a permissions policy that grants permission for the product to send findings to Security Hub is applied.

# Required Parameters
- `ProductArn`: The ARN of the product to enable the integration for.

"""
enable_import_findings_for_product(ProductArn; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/productSubscriptions", Dict{String, Any}("ProductArn"=>ProductArn); aws_config=aws_config)
enable_import_findings_for_product(ProductArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/productSubscriptions", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("ProductArn"=>ProductArn), args)); aws_config=aws_config)

"""
    EnableOrganizationAdminAccount()

Designates the Security Hub administrator account for an organization. Can only be called by the organization management account.

# Required Parameters
- `AdminAccountId`: The AWS account identifier of the account to designate as the Security Hub administrator account.

"""
enable_organization_admin_account(AdminAccountId; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/organization/admin/enable", Dict{String, Any}("AdminAccountId"=>AdminAccountId); aws_config=aws_config)
enable_organization_admin_account(AdminAccountId, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/organization/admin/enable", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AdminAccountId"=>AdminAccountId), args)); aws_config=aws_config)

"""
    EnableSecurityHub()

Enables Security Hub for your account in the current Region or the Region you specify in the request. When you enable Security Hub, you grant to Security Hub the permissions necessary to gather findings from other services that are integrated with Security Hub. When you use the EnableSecurityHub operation to enable Security Hub, you also automatically enable the following standards.   CIS AWS Foundations   AWS Foundational Security Best Practices   You do not enable the Payment Card Industry Data Security Standard (PCI DSS) standard.  To not enable the automatically enabled standards, set EnableDefaultStandards to false. After you enable Security Hub, to enable a standard, use the  BatchEnableStandards  operation. To disable a standard, use the  BatchDisableStandards  operation. To learn more, see Setting Up AWS Security Hub in the AWS Security Hub User Guide.

# Optional Parameters
- `EnableDefaultStandards`: Whether to enable the security standards that Security Hub has designated as automatically enabled. If you do not provide a value for EnableDefaultStandards, it is set to true. To not enable the automatically enabled standards, set EnableDefaultStandards to false.
- `Tags`: The tags to add to the hub resource when you enable Security Hub.
"""
enable_security_hub(; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/accounts"; aws_config=aws_config)
enable_security_hub(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/accounts", args; aws_config=aws_config)

"""
    GetEnabledStandards()

Returns a list of the standards that are currently enabled.

# Optional Parameters
- `MaxResults`: The maximum number of results to return in the response.
- `NextToken`: The token that is required for pagination. On your first call to the GetEnabledStandards operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
- `StandardsSubscriptionArns`: The list of the standards subscription ARNs for the standards to retrieve.
"""
get_enabled_standards(; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/standards/get"; aws_config=aws_config)
get_enabled_standards(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/standards/get", args; aws_config=aws_config)

"""
    GetFindings()

Returns a list of findings that match the specified criteria.

# Optional Parameters
- `Filters`: The finding attributes used to define a condition to filter the returned findings. You can filter by up to 10 finding attributes. For each attribute, you can provide up to 20 filter values. Note that in the available filter fields, WorkflowState is deprecated. To search for a finding based on its workflow status, use WorkflowStatus.
- `MaxResults`: The maximum number of findings to return.
- `NextToken`: The token that is required for pagination. On your first call to the GetFindings operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
- `SortCriteria`: The finding attributes used to sort the list of returned findings.
"""
get_findings(; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/findings"; aws_config=aws_config)
get_findings(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/findings", args; aws_config=aws_config)

"""
    GetInsightResults()

Lists the results of the Security Hub insight specified by the insight ARN.

# Required Parameters
- `InsightArn`: The ARN of the insight for which to return results.

"""
get_insight_results(InsightArn; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/insights/results/$(InsightArn)"; aws_config=aws_config)
get_insight_results(InsightArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/insights/results/$(InsightArn)", args; aws_config=aws_config)

"""
    GetInsights()

Lists and describes insights for the specified insight ARNs.

# Optional Parameters
- `InsightArns`: The ARNs of the insights to describe. If you do not provide any insight ARNs, then GetInsights returns all of your custom insights. It does not return any managed insights.
- `MaxResults`: The maximum number of items to return in the response.
- `NextToken`: The token that is required for pagination. On your first call to the GetInsights operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
get_insights(; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/insights/get"; aws_config=aws_config)
get_insights(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/insights/get", args; aws_config=aws_config)

"""
    GetInvitationsCount()

Returns the count of all Security Hub membership invitations that were sent to the current member account, not including the currently accepted invitation.

"""
get_invitations_count(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/invitations/count"; aws_config=aws_config)
get_invitations_count(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/invitations/count", args; aws_config=aws_config)

"""
    GetMasterAccount()

Provides the details for the Security Hub master account for the current member account. Can be used by both member accounts that are in an organization and accounts that were invited manually.

"""
get_master_account(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/master"; aws_config=aws_config)
get_master_account(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/master", args; aws_config=aws_config)

"""
    GetMembers()

Returns the details for the Security Hub member accounts for the specified account IDs. A master account can be either a delegated Security Hub administrator account for an organization or a master account that enabled Security Hub manually. The results include both member accounts that are in an organization and accounts that were invited manually.

# Required Parameters
- `AccountIds`: The list of account IDs for the Security Hub member accounts to return the details for.

"""
get_members(AccountIds; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/get", Dict{String, Any}("AccountIds"=>AccountIds); aws_config=aws_config)
get_members(AccountIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/get", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AccountIds"=>AccountIds), args)); aws_config=aws_config)

"""
    InviteMembers()

Invites other AWS accounts to become member accounts for the Security Hub master account that the invitation is sent from. This operation is only used to invite accounts that do not belong to an organization. Organization accounts do not receive invitations. Before you can use this action to invite a member, you must first use the  CreateMembers  action to create the member account in Security Hub. When the account owner enables Security Hub and accepts the invitation to become a member account, the master account can view the findings generated from the member account.

# Required Parameters
- `AccountIds`: The list of account IDs of the AWS accounts to invite to Security Hub as members.

"""
invite_members(AccountIds; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/invite", Dict{String, Any}("AccountIds"=>AccountIds); aws_config=aws_config)
invite_members(AccountIds, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/members/invite", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AccountIds"=>AccountIds), args)); aws_config=aws_config)

"""
    ListEnabledProductsForImport()

Lists all findings-generating solutions (products) that you are subscribed to receive findings from in Security Hub.

# Optional Parameters
- `MaxResults`: The maximum number of items to return in the response.
- `NextToken`: The token that is required for pagination. On your first call to the ListEnabledProductsForImport operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
list_enabled_products_for_import(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/productSubscriptions"; aws_config=aws_config)
list_enabled_products_for_import(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/productSubscriptions", args; aws_config=aws_config)

"""
    ListInvitations()

Lists all Security Hub membership invitations that were sent to the current AWS account. This operation is only used by accounts that do not belong to an organization. Organization accounts do not receive invitations.

# Optional Parameters
- `MaxResults`: The maximum number of items to return in the response.
- `NextToken`: The token that is required for pagination. On your first call to the ListInvitations operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
list_invitations(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/invitations"; aws_config=aws_config)
list_invitations(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/invitations", args; aws_config=aws_config)

"""
    ListMembers()

Lists details about all member accounts for the current Security Hub master account. The results include both member accounts that belong to an organization and member accounts that were invited manually.

# Optional Parameters
- `MaxResults`: The maximum number of items to return in the response.
- `NextToken`: The token that is required for pagination. On your first call to the ListMembers operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
- `OnlyAssociated`: Specifies which member accounts to include in the response based on their relationship status with the master account. The default value is TRUE. If OnlyAssociated is set to TRUE, the response includes member accounts whose relationship status with the master is set to ENABLED. If OnlyAssociated is set to FALSE, the response includes all existing member accounts.
"""
list_members(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/members"; aws_config=aws_config)
list_members(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/members", args; aws_config=aws_config)

"""
    ListOrganizationAdminAccounts()

Lists the Security Hub administrator accounts. Can only be called by the organization management account.

# Optional Parameters
- `MaxResults`: The maximum number of items to return in the response.
- `NextToken`: The token that is required for pagination. On your first call to the ListOrganizationAdminAccounts operation, set the value of this parameter to NULL. For subsequent calls to the operation, to continue listing data, set the value of this parameter to the value returned from the previous response.
"""
list_organization_admin_accounts(; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/organization/admin"; aws_config=aws_config)
list_organization_admin_accounts(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/organization/admin", args; aws_config=aws_config)

"""
    ListTagsForResource()

Returns a list of tags associated with a resource.

# Required Parameters
- `ResourceArn`: The ARN of the resource to retrieve tags for.

"""
list_tags_for_resource(ResourceArn; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/tags/$(ResourceArn)"; aws_config=aws_config)
list_tags_for_resource(ResourceArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("GET", "/tags/$(ResourceArn)", args; aws_config=aws_config)

"""
    TagResource()

Adds one or more tags to a resource.

# Required Parameters
- `ResourceArn`: The ARN of the resource to apply the tags to.
- `Tags`: The tags to add to the resource.

"""
tag_resource(ResourceArn, Tags; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/tags/$(ResourceArn)", Dict{String, Any}("Tags"=>Tags); aws_config=aws_config)
tag_resource(ResourceArn, Tags, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/tags/$(ResourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Tags"=>Tags), args)); aws_config=aws_config)

"""
    UntagResource()

Removes one or more tags from a resource.

# Required Parameters
- `ResourceArn`: The ARN of the resource to remove the tags from.
- `tagKeys`: The tag keys associated with the tags to remove from the resource.

"""
untag_resource(ResourceArn, tagKeys; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/tags/$(ResourceArn)", Dict{String, Any}("tagKeys"=>tagKeys); aws_config=aws_config)
untag_resource(ResourceArn, tagKeys, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("DELETE", "/tags/$(ResourceArn)", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("tagKeys"=>tagKeys), args)); aws_config=aws_config)

"""
    UpdateActionTarget()

Updates the name and description of a custom action target in Security Hub.

# Required Parameters
- `ActionTargetArn`: The ARN of the custom action target to update.

# Optional Parameters
- `Description`: The updated description for the custom action target.
- `Name`: The updated name of the custom action target.
"""
update_action_target(ActionTargetArn; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/actionTargets/$(ActionTargetArn)"; aws_config=aws_config)
update_action_target(ActionTargetArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/actionTargets/$(ActionTargetArn)", args; aws_config=aws_config)

"""
    UpdateFindings()

 UpdateFindings is deprecated. Instead of UpdateFindings, use BatchUpdateFindings. Updates the Note and RecordState of the Security Hub-aggregated findings that the filter attributes specify. Any member account that can view the finding also sees the update to the finding.

# Required Parameters
- `Filters`: A collection of attributes that specify which findings you want to update.

# Optional Parameters
- `Note`: The updated note for the finding.
- `RecordState`: The updated record state for the finding.
"""
update_findings(Filters; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/findings", Dict{String, Any}("Filters"=>Filters); aws_config=aws_config)
update_findings(Filters, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/findings", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("Filters"=>Filters), args)); aws_config=aws_config)

"""
    UpdateInsight()

Updates the Security Hub insight identified by the specified insight ARN.

# Required Parameters
- `InsightArn`: The ARN of the insight that you want to update.

# Optional Parameters
- `Filters`: The updated filters that define this insight.
- `GroupByAttribute`: The updated GroupBy attribute that defines this insight.
- `Name`: The updated name for the insight.
"""
update_insight(InsightArn; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/insights/$(InsightArn)"; aws_config=aws_config)
update_insight(InsightArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/insights/$(InsightArn)", args; aws_config=aws_config)

"""
    UpdateOrganizationConfiguration()

Used to update the configuration related to Organizations. Can only be called from a Security Hub administrator account.

# Required Parameters
- `AutoEnable`: Whether to automatically enable Security Hub for new accounts in the organization. By default, this is false, and new accounts are not added automatically. To automatically enable Security Hub for new accounts, set this to true.

"""
update_organization_configuration(AutoEnable; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/organization/configuration", Dict{String, Any}("AutoEnable"=>AutoEnable); aws_config=aws_config)
update_organization_configuration(AutoEnable, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("POST", "/organization/configuration", Dict{String, Any}(mergewith(_merge, Dict{String, Any}("AutoEnable"=>AutoEnable), args)); aws_config=aws_config)

"""
    UpdateSecurityHubConfiguration()

Updates configuration options for Security Hub.

# Optional Parameters
- `AutoEnableControls`: Whether to automatically enable new controls when they are added to standards that are enabled. By default, this is set to true, and new controls are enabled automatically. To not automatically enable new controls, set this to false.
"""
update_security_hub_configuration(; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/accounts"; aws_config=aws_config)
update_security_hub_configuration(args::AbstractDict{String, Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/accounts", args; aws_config=aws_config)

"""
    UpdateStandardsControl()

Used to control whether an individual security standard control is enabled or disabled.

# Required Parameters
- `StandardsControlArn`: The ARN of the security standard control to enable or disable.

# Optional Parameters
- `ControlStatus`: The updated status of the security standard control.
- `DisabledReason`: A description of the reason why you are disabling a security standard control. If you are disabling a control, then this is required.
"""
update_standards_control(StandardsControlArn; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/standards/control/$(StandardsControlArn)"; aws_config=aws_config)
update_standards_control(StandardsControlArn, args::AbstractDict{String, <:Any}; aws_config::AWSConfig=global_aws_config()) = securityhub("PATCH", "/standards/control/$(StandardsControlArn)", args; aws_config=aws_config)


text:
using Pkg
using Dates

PROJECT_DIR = abspath(@__DIR__) |> dirname

# prepare packages
Pkg.build("Turing")
Pkg.add("BenchmarkTools")

BENCHMARK_REV = "master"
Pkg.add(PackageSpec(
    url="https://github.com/TuringLang/ContinuousBenchmarks.jl", rev=BENCHMARK_REV))
Pkg.build("ContinuousBenchmarks")
Pkg.resolve()

# prepare BenchMark information
BASE_BRANCH = "master"
CURRENT_BRANCH = strip(read(`git rev-parse --abbrev-ref HEAD`, String))

if get(ENV, "TRAVIS", "false") == "true"
    if get(ENV, "TRAVIS_PULL_REQUEST", "false") == "true"
        CURRENT_BRANCH = get(ENV, "TRAVIS_PULL_REQUEST_BRANCH")
        exit(0)
    else
        CURRENT_BRANCH = get(ENV, "TRAVIS_BRANCH", "master")
    end
end

SANTI_BR_NAME = 0
COMMIT_SHA = strip(read(`git rev-parse  HEAD`, String))
COMMIT_SHA_7 = COMMIT_SHA[1:7]
TIME = Dates.format(now(), "YYYYmmddHHMM")
BM_JOB_NAME="BMCI-$(SANTI_BR_NAME)-$(COMMIT_SHA_7)-$(TIME)"

if get(ENV, "TRAVIS", "false") == "true"
    run(`git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'`)
    run(`git fetch --all --unshallow`)
end

# run
code_run = """using ContinuousBenchmarks
using ContinuousBenchmarks.Runner
ContinuousBenchmarks.set_project_path("$PROJECT_DIR")
ContinuousBenchmarks.set_benchmark_config_file(
    joinpath("$PROJECT_DIR", "benchmarks/benchmark_config.jl"))
Runner.run_bm_on_travis("$BM_JOB_NAME", ("$BASE_BRANCH", "$CURRENT_BRANCH"), "$COMMIT_SHA")
"""
run(`julia --project=$PROJECT_DIR -e $code_run`)


text:
ACTV_FUNCS = ["relu", "sigmoid", "σ", "tanh"]

function σ(X)
    return 1 ./(1 .+ exp.(.-X))
end

function relu(X)
    return max.(0,X)
end

function dσ(z)
    return σ(z).*(1 .-σ(z))
end

function drelu(z)
    return z.*(z.>0)
end


text:
using PackageCompiler, Libdl

PackageCompiler.create_sysimage(Symbol[:CG];
                                project=".",
                                precompile_execution_file=["build/generate_precompile.jl"],
                                precompile_statements_file=["build/additional_precompile.jl"],
                                sysimage_path="libcg.$(Libdl.dlext)",
                                incremental=false,
                                filter_stdlibs=true)

max_stars_repo_name: kmsquire/libcg

max_issues_repo_name: kmsquire/libcg

max_forks_repo_name: kmsquire/libcg


text:
# Linear model equations, for ocean model implicit vertical diffusion
# convective adjustment step.
#
using ClimateMachine.DGMethods.NumericalFluxes:
    NumericalFluxFirstOrder, NumericalFluxSecondOrder, NumericalFluxGradient

using ClimateMachine.DGMethods.NumericalFluxes:
    CentralNumericalFluxGradient, CentralNumericalFluxSecondOrder

using LinearAlgebra: I, dot, Diagonal

using ClimateMachine.BalanceLaws:
   BalanceLaw, Prognostic, Auxiliary, Gradient, GradientFlux

using ClimateMachine.VariableTemplates

import ClimateMachine.DGMethods:
     init_state_auxiliary!, update_auxiliary_state!, update_auxiliary_state_gradient!, vars_state, VerticalDirection, boundary_state!, compute_gradient_flux!, init_state_prognostic!, flux_first_order!, flux_second_order!, source!, wavespeed, compute_gradient_argument!

using StaticArrays
using Random

"""
 MYDIAGSModel{M} <: BalanceLaw

 DG model container for my diagnostics

 # Usage

"""

# Create a new linear model instance
abstract type AbstractMYDIAGSModel <: BalanceLaw end
struct MYDIAGSModel{FT} <: AbstractMYDIAGSModel
 diagsconf
 function MYDIAGSModel{FT}(
  ;
  diagsconf=()
 ) where {FT<: AbstractFloat}
    return new{FT}(
      diagsconf
    )
 end
end

function MYDIAGSModel(
   bl::MYDIAGSModel,
   grid,
   nfnondiff,
   nfdiff,
   gnf;
   kwargs...,
   )

   modeldata=(diagsconf=bl.diagsconf)

   return DGModel(bl,grid,nfnondiff,nfdiff,gnf;kwargs...,modeldata=modeldata,)
end

max_stars_repo_name: christophernhill/clima-impldiff-testing

max_issues_repo_name: christophernhill/clima-impldiff-testing

max_forks_repo_name: christophernhill/clima-impldiff-testing