text:
"""
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
"""
mutable struct PlotExample
    header::AbstractString
    desc::AbstractString
    exprs::Vector{Expr}
end

# the _examples we'll run for each
const _examples = PlotExample[
    PlotExample(
        "Lines",
        "A simple line plot of the columns.",
        [:(
            begin
                plot(Plots.fakedata(50, 5), w = 3)
            end
        )],
    ),
    PlotExample(
        "Functions, adding data, and animations",
        """
        Plot multiple functions.  You can also put the function first, or use the form `plot(f,
        xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data:
        `x, y = plt[i]`.  Set series data: `plt[i] = (x,y)`. Add to the series with
        `push!`/`append!`.\n\nEasily build animations.  (`convert` or `ffmpeg` must be available
        to generate the animation.)  Use command `gif(anim, filename, fps=15)` to save the
        animation.
        """,
        [:(
            begin
                p = plot([sin, cos], zeros(0), leg = false)
                anim = Animation()
                for x in range(0, stop = 10π, length = 100)
                    push!(p, x, Float64[sin(x), cos(x)])
                    frame(anim)
                end
            end
        )],
    ),
    PlotExample(
        "Parametric plots",
        "Plot function pair (x(u), y(u)).",
        [
            :(
                begin
                    plot(
                        sin,
                        x -> sin(2x),
                        0,
                        2π,
                        line = 4,
                        leg = false,
                        fill = (0, :orange),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Colors",
        """
        Access predefined palettes (or build your own with the `colorscheme` method).
        Line/marker colors are auto-generated from the plot's palette, unless overridden.  Set
        the `z` argument to turn on series gradients.
        """,
        [
            :(
                begin
                    y = rand(100)
                    plot(
                        0:10:100,
                        rand(11, 4),
                        lab = "lines",
                        w = 3,
                        palette = cgrad(:grays),
                        fill = 0,
                        α = 0.6,
                    )
                    scatter!(
                        y,
                        zcolor = abs.(y .- 0.5),
                        m = (:heat, 0.8, Plots.stroke(1, :green)),
                        ms = 10 * abs.(y .- 0.5) .+ 4,
                        lab = "grad",
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Global",
        """
        Change the guides/background/limits/ticks.  Convenience args `xaxis` and `yaxis` allow
        you to pass a tuple or value which will be mapped to the relevant args automatically.
        The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during
        the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`,
        `yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`
        """,
        [
            :(
                begin
                    using Statistics
                    y = rand(20, 3)
                    plot(
                        y,
                        xaxis = ("XLABEL", (-5, 30), 0:2:20, :flip),
                        background_color = RGB(0.2, 0.2, 0.2),
                        leg = false,
                    )
                    hline!(
                        mean(y, dims = 1) + rand(1, 3),
                        line = (4, :dash, 0.6, [:lightgreen :green :darkgreen]),
                    )
                    vline!([5, 10])
                    title!("TITLE")
                    yaxis!("YLABEL", :log10)
                end
            ),
        ],
    ),

    # PlotExample("Two-axis",
    #             "Use the `axis` arguments.",
    #             [
    #               :(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT", xlabel="X", title="TITLE"))
    #             ]),

    PlotExample(
        "Images",
        "Plot an image.  y-axis is set to flipped",
        [
            :(
                begin
                    import FileIO
                    path =
                            download("http://juliaplots.org/PlotReferenceImages.jl/Plots/pyplot/0.7.0/ref1.png")
                    img = FileIO.load(path)
                    plot(img)
                end
            ),
        ],
    ),
    PlotExample(
        "Arguments",
        """
        Plot multiple series with different numbers of points.  Mix arguments that apply to all
        series (marker/markersize) with arguments unique to each series (colors).  Special
        arguments `line`, `marker`, and `fill` will automatically figure out what arguments to
        set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with
        `line`.)  Note that we pass a matrix of colors, and this applies the colors to each
        series.
        """,
        [
            :(
                begin
                    ys = Vector[rand(10), rand(20)]
                    plot(
                        ys,
                        color = [:black :orange],
                        line = (:dot, 4),
                        marker = ([:hex :d], 12, 0.8, Plots.stroke(3, :gray)),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Build plot in pieces",
        "Start with a base plot...",
        [:(
            begin
                plot(rand(100) / 3, reg = true, fill = (0, :green))
            end
        )],
    ),
    PlotExample(
        "",
        "and add to it later.",
        [:(
            begin
                scatter!(rand(100), markersize = 6, c = :orange)
            end
        )],
    ),
    PlotExample(
        "Histogram2D",
        "",
        [:(
            begin
                histogram2d(randn(10000), randn(10000), nbins = 20)
            end
        )],
    ),
    PlotExample(
        "Line types",
        "",
        [
            :(
                begin
                    linetypes = [:path :steppre :steppost :sticks :scatter]
                    n = length(linetypes)
                    x = Vector[sort(rand(20)) for i = 1:n]
                    y = rand(20, n)
                    plot(
                        x,
                        y,
                        line = (linetypes, 3),
                        lab = map(string, linetypes),
                        ms = 15,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Line styles",
        "",
        [
            :(
                begin
                    styles = filter(
                        s -> s in Plots.supported_styles(),
                        [:solid, :dash, :dot, :dashdot, :dashdotdot],
                    )
                    styles = reshape(styles, 1, length(styles)) # Julia 0.6 unfortunately gives an error when transposing symbol vectors
                    n = length(styles)
                    y = cumsum(randn(20, n), dims = 1)
                    plot(
                        y,
                        line = (5, styles),
                        label = map(string, styles),
                        legendtitle = "linestyle",
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Marker types",
        "",
        [
            :(
                begin
                    markers = filter(
                        m -> m in Plots.supported_markers(),
                        Plots._shape_keys,
                    )
                    markers = reshape(markers, 1, length(markers))
                    n = length(markers)
                    x = range(0, stop = 10, length = n + 2)[2:(end - 1)]
                    y = repeat(reshape(reverse(x), 1, :), n, 1)
                    scatter(
                        x,
                        y,
                        m = (8, :auto),
                        lab = map(string, markers),
                        bg = :linen,
                        xlim = (0, 10),
                        ylim = (0, 10),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Bar",
        "`x` is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
        [:(
            begin
                bar(randn(99))
            end
        )],
    ),
    PlotExample(
        "Histogram",
        "",
        [
            :(
                begin
                    histogram(
                        randn(1000),
                        bins = :scott,
                        weights = repeat(1:5, outer = 200),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Subplots",
        """
        Use the `layout` keyword, and optionally the convenient `@layout` macro to generate
        arbitrarily complex subplot layouts.
        """,
        [
            :(
                begin
                    l = @layout([a{0.1h}; b [c; d e]])
                    plot(
                        randn(100, 5),
                        layout = l,
                        t = [:line :histogram :scatter :steppre :bar],
                        leg = false,
                        ticks = nothing,
                        border = :none,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Adding to subplots",
        """
        Note here the automatic grid layout, as well as the order in which new series are added
        to the plots.
        """,
        [
            :(
                begin
                    plot(
                        Plots.fakedata(100, 10),
                        layout = 4,
                        palette = cgrad.([:grays :blues :heat :lightrainbow]),
                        bg_inside = [:orange :pink :darkblue :black],
                    )
                end
            ),
        ],
    ),
    PlotExample("", "", [:(
        begin
            using Random
            Random.seed!(111)
            plot!(Plots.fakedata(100, 10))
        end
    )]),
    PlotExample(
        "Open/High/Low/Close",
        """
        Create an OHLC chart.  Pass in a list of (open,high,low,close) tuples as your `y`
        argument.  This uses recipes to first convert the tuples to OHLC objects, and
        subsequently create a :path series with the appropriate line segments.
        """,
        [
            :(
                begin
                    n = 20
                    hgt = rand(n) .+ 1
                    bot = randn(n)
                    openpct = rand(n)
                    closepct = rand(n)
                    y = OHLC[
                        (
                            openpct[i] * hgt[i] + bot[i],
                            bot[i] + hgt[i],
                            bot[i],
                            closepct[i] * hgt[i] + bot[i],
                        )
                        for i = 1:n
                    ]
                    ohlc(y)
                end
            ),
        ],
    ),
    PlotExample(
        "Annotations",
        """
        The `annotations` keyword is used for text annotations in data-coordinates.  Pass in a
        tuple (x,y,text) or a vector of annotations.  `annotate!(ann)` is shorthand for `plot!(;
        annotation=ann)`.  Series annotations are used for annotating individual data points.
        They require only the annotation... x/y values are computed.  A `PlotText` object can be
        build with the method `text(string, attr...)`, which wraps font and color attributes.
        """,
        [
            :(
                begin
                    y = rand(10)
                    plot(
                        y,
                        annotations = (3, y[3], Plots.text("this is #3", :left)),
                        leg = false,
                    )
                    annotate!([
                        (5, y[5], Plots.text("this is #5", 16, :red, :center)),
                        (
                            10,
                            y[10],
                            Plots.text("this is #10", :right, 20, "courier"),
                        ),
                    ])
                    scatter!(
                        range(2, stop = 8, length = 6),
                        rand(6),
                        marker = (50, 0.2, :orange),
                        series_annotations = [
                            "series",
                            "annotations",
                            "map",
                            "to",
                            "series",
                            Plots.text("data", :green),
                        ],
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Custom Markers",
        """A `Plots.Shape` is a light wrapper around vertices of a polygon.  For supported
        backends, pass arbitrary polygons as the marker shapes.  Note: The center is (0,0) and
        the size is expected to be rougly the area of the unit circle.
        """,
        [
            :(
                begin
                    verts = [
                        (-1.0, 1.0),
                        (-1.28, 0.6),
                        (-0.2, -1.4),
                        (0.2, -1.4),
                        (1.28, 0.6),
                        (1.0, 1.0),
                        (-1.0, 1.0),
                        (-0.2, -0.6),
                        (0.0, -0.2),
                        (-0.4, 0.6),
                        (1.28, 0.6),
                        (0.2, -1.4),
                        (-0.2, -1.4),
                        (0.6, 0.2),
                        (-0.2, 0.2),
                        (0.0, -0.2),
                        (0.2, 0.2),
                        (-0.2, -0.6),
                    ]
                    x = 0.1:0.2:0.9
                    y = 0.7 * rand(5) .+ 0.15
                    plot(
                        x,
                        y,
                        line = (3, :dash, :lightblue),
                        marker = (Shape(verts), 30, RGBA(0, 0, 0, 0.2)),
                        bg = :pink,
                        fg = :darkblue,
                        xlim = (0, 1),
                        ylim = (0, 1),
                        leg = false,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Contours",
        """
        Any value for fill works here.  We first build a filled contour from a function, then an
        unfilled contour from a matrix.
        """,
        [:(
            begin
                x = 1:0.5:20
                y = 1:0.5:10
                f(x, y) = (3x + y^2) * abs(sin(x) + cos(y))
                X = repeat(reshape(x, 1, :), length(y), 1)
                Y = repeat(y, 1, length(x))
                Z = map(f, X, Y)
                p1 = contour(x, y, f, fill = true)
                p2 = contour(x, y, Z)
                plot(p1, p2)
            end
        )],
    ),
    PlotExample(
        "Pie",
        "",
        [:(
            begin
                x = ["Nerds", "Hackers", "Scientists"]
                y = [0.4, 0.35, 0.25]
                pie(x, y, title = "The Julia Community", l = 0.5)
            end
        )],
    ),
    PlotExample(
        "3D",
        "",
        [
            :(
                begin
                    n = 100
                    ts = range(0, stop = 8π, length = n)
                    x = ts .* map(cos, ts)
                    y = 0.1ts .* map(sin, ts)
                    z = 1:n
                    plot(
                        x,
                        y,
                        z,
                        zcolor = reverse(z),
                        m = (10, 0.8, :blues, Plots.stroke(0)),
                        leg = false,
                        cbar = true,
                        w = 5,
                    )
                    plot!(zeros(n), zeros(n), 1:n, w = 10)
                end
            ),
        ],
    ),
    PlotExample(
        "DataFrames",
        "Plot using DataFrame column symbols.",
        [
            :(using StatsPlots), # can't be inside begin block because @df gets expanded first
            :(
                begin
                    import RDatasets
                    iris = RDatasets.dataset("datasets", "iris")
                    @df iris scatter(
                        :SepalLength,
                        :SepalWidth,
                        group = :Species,
                        title = "My awesome plot",
                        xlabel = "Length",
                        ylabel = "Width",
                        marker = (0.5, [:cross :hex :star7], 12),
                        bg = RGB(0.2, 0.2, 0.2),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Groups and Subplots",
        "",
        [
            :(
                begin
                    group = rand(map(i -> "group $i", 1:4), 100)
                    plot(
                        rand(100),
                        layout = @layout([a b; c]),
                        group = group,
                        linetype = [:bar :scatter :steppre],
                        linecolor = :match,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Polar Plots",
        "",
        [:(
            begin
                Θ = range(0, stop = 1.5π, length = 100)
                r = abs.(0.1 * randn(100) + sin.(3Θ))
                plot(Θ, r, proj = :polar, m = 2)
            end
        )],
    ),
    PlotExample(
        "Heatmap, categorical axes, and aspect_ratio",
        "",
        [:(
            begin
                xs = [string("x", i) for i = 1:10]
                ys = [string("y", i) for i = 1:4]
                z = float((1:4) * reshape(1:10, 1, :))
                heatmap(xs, ys, z, aspect_ratio = 1)
            end
        )],
    ),
    PlotExample(
        "Layouts, margins, label rotation, title location",
        "",
        [
            :(
                begin
                    using Plots.PlotMeasures # for Measures, e.g. mm and px
                    plot(
                        rand(100, 6),
                        layout = @layout([a b; c]),
                        title = ["A" "B" "C"],
                        titlelocation = :left,
                        left_margin = [20mm 0mm],
                        bottom_margin = 10px,
                        xrotation = 60,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Boxplot and Violin series recipes",
        "",
        [
            :(using StatsPlots), # can't be inside begin block because @df gets expanded first
            :(
                begin
                    import RDatasets
                    singers = RDatasets.dataset("lattice", "singer")
                    @df singers violin(
                        :VoicePart,
                        :Height,
                        line = 0,
                        fill = (0.2, :blue),
                    )
                    @df singers boxplot!(
                        :VoicePart,
                        :Height,
                        line = (2, :black),
                        fill = (0.3, :orange),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Animation with subplots",
        "The `layout` macro can be used to create an animation with subplots.",
        [
            :(
                begin
                    l = @layout([[a; b] c])
                    p = plot(
                        plot([sin, cos], 1, leg = false),
                        scatter([atan, cos], 1, leg = false),
                        plot(log, 1, xlims = (1, 10π), ylims = (0, 5), leg = false),
                        layout = l,
                    )

                    anim = Animation()
                    for x in range(1, stop = 10π, length = 100)
                        plot(push!(
                            p,
                            x,
                            Float64[sin(x), cos(x), atan(x), cos(x), log(x)],
                        ))
                        frame(anim)
                    end
                end
            ),
        ],
    ),
    PlotExample(
        "Spy",
        """
        For a matrix `mat` with unique nonzeros `spy(mat)` returns a colorless plot. If `mat` has
        various different nonzero values, a colorbar is added. The colorbar can be disabled with
        `legend = nothing`.
        """,
        [
            :(
                begin
                    using SparseArrays
                    a = spdiagm(
                        0 => ones(50),
                        1 => ones(49),
                        -1 => ones(49),
                        10 => ones(40),
                        -10 => ones(40),
                    )
                    b = spdiagm(
                        0 => 1:50,
                        1 => 1:49,
                        -1 => 1:49,
                        10 => 1:40,
                        -10 => 1:40,
                    )
                    plot(
                        spy(a),
                        spy(b),
                        title = ["Unique nonzeros" "Different nonzeros"],
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Magic grid argument",
        """
        The grid lines can be modified individually for each axis with the magic `grid` argument.
        """,
        [
            :(
                begin
                    x = rand(10)
                    p1 = plot(x, title = "Default looks")
                    p2 = plot(
                        x,
                        grid = (:y, :olivedrab, :dot, 1, 0.9),
                        title = "Modified y grid",
                    )
                    p3 = plot(deepcopy(p2), title = "Add x grid")
                    xgrid!(p3, :on, :cadetblue, 2, :dashdot, 0.4)
                    plot(
                        p1,
                        p2,
                        p3,
                        layout = (1, 3),
                        label = "",
                        fillrange = 0,
                        fillalpha = 0.3,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Framestyle",
        """
        The style of the frame/axes of a (sub)plot can be changed with the `framestyle`
        attribute. The default framestyle is `:axes`.
        """,
        [
            :(
                begin
                    scatter(
                        fill(randn(10), 6),
                        fill(randn(10), 6),
                        framestyle = [:box :semi :origin :zerolines :grid :none],
                        title = [":box" ":semi" ":origin" ":zerolines" ":grid" ":none"],
                        color = permutedims(1:6),
                        layout = 6,
                        label = "",
                        markerstrokewidth = 0,
                        ticks = -2:2,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Lines and markers with varying colors",
        """
        You can use the `line_z` and `marker_z` properties to associate a color with
        each line segment or marker in the plot.
        """,
        [
            :(
                begin
                    t = range(0, stop = 1, length = 100)
                    θ = 6π .* t
                    x = t .* cos.(θ)
                    y = t .* sin.(θ)
                    p1 = plot(x, y, line_z = t, linewidth = 3, legend = false)
                    p2 = scatter(
                        x,
                        y,
                        marker_z = +,
                        color = :bluesreds,
                        legend = false,
                    )
                    plot(p1, p2)
                end
            ),
        ],
    ),
    PlotExample(
        "Portfolio Composition maps",
        """
        see: http://stackoverflow.com/a/37732384/5075246
        """,
        [
            :(
                begin
                    using Random
                    Random.seed!(111)
                    tickers = ["IBM", "Google", "Apple", "Intel"]
                    N = 10
                    D = length(tickers)
                    weights = rand(N, D)
                    weights ./= sum(weights, dims = 2)
                    returns = sort!((1:N) + D * randn(N))

                    portfoliocomposition(
                        weights,
                        returns,
                        labels = permutedims(tickers),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Ribbons",
        """
        Ribbons can be added to lines via the `ribbon` keyword;
        you can pass a tuple of arrays (upper and lower bounds),
        a single Array (for symmetric ribbons), a Function, or a number.
        """,
        [
            :(
                begin
                    plot(
                        plot(
                            0:10;
                            ribbon = (LinRange(0, 2, 11), LinRange(0, 1, 11)),
                        ),
                        plot(0:10; ribbon = 0:0.5:5),
                        plot(0:10; ribbon = sqrt),
                        plot(0:10; ribbon = 1),
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Histogram2D (complex values)",
        "",
        [
            :(
                begin
                    n = 10_000
                    x = exp.(0.1 * randn(n) .+ randn(n) .* (im))
                    histogram2d(
                        x,
                        nbins = (20, 40),
                        show_empty_bins = true,
                        normed = true,
                        aspect_ratio = 1,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Unconnected lines using `missing` or `NaN`",
        """
        Missing values and non-finite values, including `NaN`, are not plotted.
        Instead, lines are separated into segments at these values.
        """,
        [
            :(
                begin
                    x, y = [1, 2, 2, 1, 1], [1, 2, 1, 2, 1]
                    plot(
                        plot([rand(5); NaN; rand(5); NaN; rand(5)]),
                        plot([1, missing, 2, 3], marker = true),
                        plot([x; NaN; x .+ 2], [y; NaN; y .+ 1], arrow = 2),
                        plot(
                            [1, 2 + 3im, Inf, 4im, 3, -Inf * im, 0, 3 + 3im],
                            marker = true,
                        ),
                        legend = false,
                    )
                end
            ),
        ],
    ),
    PlotExample(
        "Lens",
        "A lens lets you easily magnify a region of a plot. x and y coordinates refer to the to be magnified region and the via the `inset` keyword the subplot index and the bounding box (in relative coordinates) of the inset plot with the magnified plot can be specified. Additional attributes count for the inset plot.",
        [
            quote
                begin
                    plot(
                        [(0, 0), (0, 0.9), (1, 0.9), (2, 1), (3, 0.9), (80, 0)],
                        legend = :outertopright,
                    )
                    plot!([(0, 0), (0, 0.9), (2, 0.9), (3, 1), (4, 0.9), (80, 0)])
                    plot!([(0, 0), (0, 0.9), (3, 0.9), (4, 1), (5, 0.9), (80, 0)])
                    plot!([(0, 0), (0, 0.9), (4, 0.9), (5, 1), (6, 0.9), (80, 0)])
                    lens!(
                        [1, 6],
                        [0.9, 1.1],
                        inset = (1, bbox(0.5, 0.0, 0.4, 0.4)),
                    )
                end
            end,
        ],
    ),
    PlotExample(
        "Array Types",
        "Plots supports different `Array` types that follow the `AbstractArray` interface, like `StaticArrays` and `OffsetArrays.`",
        [
            quote
                begin
                    using StaticArrays, OffsetArrays
                    sv = SVector{10}(rand(10))
                    ov = OffsetVector(rand(10), -2)
                    plot([sv, ov], label = ["StaticArray" "OffsetArray"])
                end
            end,
        ],
    ),
    PlotExample(
        "Setting defaults and font arguments",
        "",
        [
            quote
                begin
                    using Plots
                    default(
                        titlefont = (20, "times"),
                        legendfontsize = 18,
                        guidefont = (18, :darkgreen),
                        tickfont = (12, :orange),
                        guide = "x",
                        framestyle = :zerolines,
                        yminorgrid = true
                    )
                    plot(
                        [sin, cos],
                        -2π,
                        2π,
                        label = ["sin(θ)" "cos(θ)"],
                        title = "Trigonometric Functions",
                        xlabel = "θ",
                        linewidth = 2,
                        legend = :outertopleft,
                    )
                end
            end,
        ],
    ),
    PlotExample(
        "Heatmap with DateTime axis",
        "",
        [
            quote
                begin
                    using Dates
                    z = rand(5, 5)
                    x = DateTime.(2016:2020)
                    y = 1:5
                    heatmap(x, y, z)
                end
            end,
        ],
    ),
    PlotExample(
        "Linked axes",
        "",
        [
            quote
                begin
                    x = -5:0.1:5
                    plot(plot(x, x->x^2), plot(x, x->sin(x)), layout = 2, link = :y)
                end
            end,
        ],
    ),
    PlotExample(
        "Error bars and array type recipes",
        "",
        [
            quote
                begin
                    struct Measurement <: Number
                        val::Float64
                        err::Float64
                    end
                    value(m::Measurement) = m.val
                    uncertainty(m::Measurement) = m.err

                    @recipe function f(::Type{T}, m::T) where T <: AbstractArray{<:Measurement}
                        if !(get(plotattributes, :seriestype, :path) in [:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image])
                            error_sym = Symbol(plotattributes[:letter], :error)
                            plotattributes[error_sym] = uncertainty.(m)
                        end
                        value.(m)
                    end

                    x = Measurement.(10sort(rand(10)), rand(10))
                    y = Measurement.(10sort(rand(10)), rand(10))
                    z = Measurement.(10sort(rand(10)), rand(10))
                    surf = Measurement.((1:10) .* (1:10)', rand(10,10))

                    plot(
                        scatter(x, [x y], msw = 0),
                        scatter(x, y, z, msw = 0),
                        heatmap(x, y, surf),
                        wireframe(x, y, surf),
                        legend = :topleft
                    )
                end
            end,
        ],
    ),
]

# Some constants for PlotDocs and PlotReferenceImages
_animation_examples = [2, 31]
_backend_skips = Dict(
    :gr => [25, 30],
    :pyplot => [2, 25, 30, 31],
    :plotlyjs => [2, 21, 24, 25, 30, 31],
    :plotly => [2, 21, 24, 25, 30, 31],
    :pgfplots => [2, 5, 6, 10, 16, 20, 22, 23, 25, 28, 30, 31, 34, 37, 38, 39],
    :pgfplotsx => [
        2, # animation
        6, # images
        10, # histogram2d
        16, # pgfplots thinks the upper panel is too small
        25, # @df
        30, # @df
        31, # animation
        32, # spy
        38, # histogram2d
    ],
)

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

# make and display one plot
function test_examples(pkgname::Symbol, idx::Int; debug = false, disp = true)
    Plots._debugMode.on = debug
    @info("Testing plot: $pkgname:$idx:$(_examples[idx].header)")
    backend(pkgname)
    backend()

    # prevent leaking variables (esp. functions) directly into Plots namespace
    m = Module(:PlotExampleModule)
    Base.eval(m, :(using Plots))
    map(exprs -> Base.eval(m, exprs), _examples[idx].exprs)

    plt = current()
    if disp
        gui(plt)
    end
    plt
end

# generate all plots and create a dict mapping idx --> plt
"""
test_examples(pkgname[, idx]; debug = false, disp = true, sleep = nothing,
                                        skip = [], only = nothing

Run the `idx` test example for a given backend, or all examples if `idx`
is not specified.
"""
function test_examples(
    pkgname::Symbol;
    debug = false,
    disp = true,
    sleep = nothing,
    skip = [],
    only = nothing,
)
    Plots._debugMode.on = debug
    plts = Dict()
    for i in eachindex(_examples)
        only !== nothing && !(i in only) && continue
        i in skip && continue
        try
            plt = test_examples(pkgname, i, debug = debug, disp = disp)
            plts[i] = plt
        catch ex
            # TODO: put error info into markdown?
            @warn("Example $pkgname:$i:$(_examples[i].header) failed with: $ex")
        end
        if sleep !== nothing
            Base.sleep(sleep)
        end
    end
    plts
end


text:
include("functionality_testing.jl")
include("ordinary_eigenvalue_problem.jl")
include("generalized_eigenvalue_problem.jl")

ext: jl


text:
using AeroMDAO
using Test

@testset "NACA-4 Doublet-Source Panel Method" begin
    # Define airfoil
    airfoil = (naca4)((0,0,1,2))

    # Define uniform flow
    uniform = Uniform2D(1., 0.)

    # Evaluate case
    sys_1 = solve_case(airfoil, uniform; num_panels = 80)
    cl_1  = lift_coefficient(sys_1)
    cls_1 = surface_coefficients(sys_1)[1]

    # α = 5ᵒ
    uniform = Uniform2D(1., 5.)
    sys_2 = solve_case(airfoil, uniform; num_panels = 80)
    cl_2  = lift_coefficient(sys_2)
    cls_2 = surface_coefficients(sys_2)[1]

    @test cl_1       ≈ 0.0       atol = 1e-6
    @test cl_2       ≈ 0.5996184 atol = 1e-6
    @test sum(cls_2) ≈ 0.6007449 atol = 1e-6
end

@testset "Airfoil Processing and Doublet-Source Panel Method" begin
    # Import and read airfoil coordinates
    coo_foil = naca4((2,4,1,2))

    # Cosine spacing
    cos_foil = cosine_spacing(coo_foil, 61)

    # Split airfoil
    up, low  = split_surface(cos_foil)

    # Convert coordinates to Kulfan CST variables
    num_dv   = 4
    alpha_u  = coordinates_to_CST(up, num_dv)
    alpha_l  = coordinates_to_CST(low, num_dv)

    # Generate same airfoil using Kulfan CST parametrisation
    cst_foil = kulfan_CST(alpha_u, alpha_l, (0., 0.), (0., 0.))

    # Test coefficients
    uniform  = Uniform2D(1., 5.)

    sys_coo            = solve_case(coo_foil, uniform; num_panels = 80)
    cl_coo             = lift_coefficient(sys_coo)
    cls_coo, cms_coo   = surface_coefficients(sys_coo)[1:2]
    @test cl_coo       ≈  0.83220516 atol = 1e-6
    @test sum(cls_coo) ≈  0.83291636 atol = 1e-6
    @test sum(cms_coo) ≈ -0.25899389 atol = 1e-6

    sys_cos            = solve_case(cos_foil, uniform; num_panels = 80)
    cl_cos             = lift_coefficient(sys_cos)
    cls_cos, cms_cos   = surface_coefficients(sys_cos)[1:2]
    @test cl_cos       ≈  0.83178821 atol = 1e-6
    @test sum(cls_cos) ≈  0.83269773 atol = 1e-6
    @test sum(cms_cos) ≈ -0.25889408 atol = 1e-6

    sys_cst            = solve_case(cst_foil, uniform; num_panels = 80)
    cl_cst             = lift_coefficient(sys_cst)
    cls_cst, cms_cst   = surface_coefficients(sys_cst)[1:2]
    @test cl_cst       ≈  0.83381613 atol = 1e-6
    @test sum(cls_cst) ≈  0.83408259 atol = 1e-6
    @test sum(cms_cst) ≈ -0.25986701 atol = 1e-6
end

@testset "Geometry - Two-Section Trapezoidal Wing" begin
    # Define wing
    wing_right = HalfWing(chords    = [1.0, 0.6, 0.2],
                          twists    = [2.0, 0.0, -0.2],
                          spans     = [5.0, 0.5],
                          dihedrals = [5., 5.],
                          sweeps      = [5., 5.]);

    # Get wing info
    b        = span(wing_right)
    S        = projected_area(wing_right)
    c        = mean_aerodynamic_chord(wing_right)
    AR       = aspect_ratio(wing_right)
    λ        = taper_ratio(wing_right)
    wing_mac = mean_aerodynamic_center(wing_right)

    @test b        ≈ 5.50000000                    atol = 1e-6
    @test S        ≈ 4.19939047                    atol = 1e-6
    @test c        ≈ 0.79841269                    atol = 1e-6
    @test AR       ≈ 7.20342634                    atol = 1e-6
    @test λ        ≈ 0.20000000                    atol = 1e-6
    @test wing_mac ≈ [0.42092866, 1.33432539, 0.0] atol = 1e-6
end

@testset "Vortex Lattice Method (Incompressible) - NACA 0012 Tapered Wing" begin
    # Define wing
    wing = Wing(foils     = [ naca4((0,0,1,2)) for i ∈ 1:2 ],
                chords    = [0.18, 0.16],
                twists    = [0., 0.],
                spans     = [0.5,],
                dihedrals = [5.],
                sweeps    = [1.14])

    # Define freestream and reference values
    fs   = Freestream(2.0, 2.0, [0.0, 0.0, 0.0])
    refs = References(speed    = 1.0,
                      area     = projected_area(wing),
                      span     = span(wing),
                      chord    = mean_aerodynamic_chord(wing),
                      density  = 1.225,
                      location = [0.25 * mean_aerodynamic_chord(wing), 0., 0.])

    aircraft = ComponentArray(wing = make_horseshoes(WingMesh(wing, [10], 5, span_spacing = Sine())))

    # Evaluate stability case
    dv_data = solve_case_derivatives(aircraft, fs, refs)

    dcf = dv_data.wing
    nfs = @views dcf[1:6,1]
    ffs = @views dcf[7:9,1]
    dvs = @views dcf[1:6,3:end]

    # Test values
    nf_tests = [0.0013533, 0.0002199, 0.1159468, 0.0009056, 0.000387, -9.45e-5]
    ff_tests = [0.0014281, 0.0001743, 0.1159415]
    dv_tests = [ 0.0797419  -0.0004936  -0.0039018   0.0637380   0.0004044
                 0.0066033   0.0024036   0.0809152   0.0302107  -0.0121575
                 3.4110222  -0.0060238  -0.185695    5.179196    0.0065573
                 0.0084357   0.0064223   0.285074    0.1109742  -0.0385723
                -0.0088809   0.0004686   0.0618588  -0.6604790  -0.0051181
                -0.0009645  -0.0018438   0.0054144  -0.0044697   0.0013694]

    # Nearfield coefficients test
    [ @test nf_c ≈ nf_t atol = 1e-6 for (nf_c, nf_t) in zip(nfs, nf_tests) ]
    # Farfield coefficients test
    [ @test ff_c ≈ ff_t atol = 1e-6 for (ff_c, ff_t) in zip(ffs, ff_tests) ]
    # Stability derivatives' coefficients test
    [ @test dv_c ≈ dv_t atol = 1e-6 for (dv_c, dv_t) in zip(dvs, dv_tests) ]
end

@testset "Vortex Lattice Method (Incompressible) - Vanilla Aircraft" begin
    ## Wing
    wing = Wing(foils     = fill(naca4((0,0,1,2)), 2),
                chords    = [1.0, 0.6],
                twists    = [0.0, 0.0],
                spans     = [5.0],
                dihedrals = [11.39],
                sweeps    = [0.]);

    # Horizontal tail
    htail = Wing(foils     = fill(naca4((0,0,1,2)), 2),
                 chords    = [0.7, 0.42],
                 twists    = [0.0, 0.0],
                 spans     = [1.25],
                 dihedrals = [0.],
                 sweeps    = [6.39],
                 position  = [4., 0, 0],
                 angle     = -2.,
                 axis      = [0., 1., 0.])

    # Vertical tail
    vtail = HalfWing(foils     = fill(naca4((0,0,0,9)), 2),
                     chords    = [0.7, 0.42],
                     twists    = [0.0, 0.0],
                     spans     = [1.0],
                     dihedrals = [0.],
                     sweeps    = [7.97],
                     position  = [4., 0, 0],
                     angle     = 90.,
                     axis      = [1., 0., 0.])

    ## Assembly
    wing_panels , wing_normals  = panel_wing(wing, 16, 10; spacing = Cosine())
    htail_panels, htail_normals = panel_wing(htail, 6,  6; spacing = Cosine())
    vtail_panels, vtail_normals = panel_wing(vtail, 5,  6; spacing = Cosine())

    aircraft = ComponentArray(
                              wing  = Horseshoe.(wing_panels , wing_normals),
                              htail = Horseshoe.(htail_panels, htail_normals),
                              vtail = Horseshoe.(vtail_panels, vtail_normals)
                             )

    ## Reference quantities
    fs      = Freestream(alpha    = 1.0,
                         beta     = 1.0,
                         omega    = zeros(3))

    refs    = References(speed    = 1.0,
                         area     = projected_area(wing),
                         span     = span(wing),
                         chord    = mean_aerodynamic_chord(wing),
                         density  = 1.225,
                         location = [0.25 * mean_aerodynamic_chord(wing), 0., 0.])

    ## Stability case
    dv_data = solve_case_derivatives(aircraft, fs, refs);

    dcf = dv_data.aircraft
    nfs = @views dcf[1:6,1]
    ffs = @views dcf[7:9,1]
    dvs = @views dcf[1:6,3:end]

    nf_tests = [0.0003809, -0.0092002, 0.0653607, -0.0026762, 0.0601839, 0.0059991]
    ff_tests = [0.0005143, -0.0092706, 0.0653322]
    dv_tests = [ 0.0268317   0.0059901   0.0012994    0.0767768  -0.0023994
                 0.0048546  -0.517466    0.30401     -0.0744164  -0.899784
                 4.7952172   0.0598145  -0.0557912   11.9681911   0.0348196
                 0.0413913  -0.155083    0.489466     0.1469693  -0.0990389
                -1.5608632  -0.0360026  -0.0616955  -25.5170944  -0.124964
                 0.010348    0.336414    0.0155232    0.0906727   0.693076 ]

    # Nearfield coefficients test
    [ @test nf_c ≈ nf_t atol = 1e-6 for (nf_c, nf_t) in zip(nfs, nf_tests) ]
    # Farfield coefficients test
    [ @test ff_c ≈ ff_t atol = 1e-6 for (ff_c, ff_t) in zip(ffs, ff_tests) ]
    # Stability derivatives' coefficients test
    [ @test dv_c ≈ dv_t atol = 1e-6 for (dv_c, dv_t) in zip(dvs, dv_tests) ]
end

@testset "Vortex Lattice Method (Compressible) - Vanilla Aircraft" begin
    ## Wing
    wing = Wing(foils     = fill(naca4((0,0,1,2)), 2),
                chords    = [1.0, 0.6],
                twists    = [0.0, 0.0],
                spans     = [5.0],
                dihedrals = [11.39],
                sweeps    = [0.]);

    # Horizontal tail
    htail = Wing(foils     = fill(naca4((0,0,1,2)), 2),
                 chords    = [0.7, 0.42],
                 twists    = [0.0, 0.0],
                 spans     = [1.25],
                 dihedrals = [0.],
                 sweeps    = [6.39],
                 position  = [4., 0, 0],
                 angle     = -2.,
                 axis      = [0., 1., 0.])

    # Vertical tail
    vtail = HalfWing(foils     = fill(naca4((0,0,0,9)), 2),
                     chords    = [0.7, 0.42],
                     twists    = [0.0, 0.0],
                     spans     = [1.0],
                     dihedrals = [0.],
                     sweeps    = [7.97],
                     position  = [4., 0, 0],
                     angle     = 90.,
                     axis      = [1., 0., 0.])

    ## Assembly
    wing_panels , wing_normals  = panel_wing(wing, 16, 10; spacing = Cosine())
    htail_panels, htail_normals = panel_wing(htail, 6,  6; spacing = Cosine())
    vtail_panels, vtail_normals = panel_wing(vtail, 5,  6; spacing = Cosine())

    aircraft = ComponentArray(
                              wing  = Horseshoe.(wing_panels , wing_normals),
                              htail = Horseshoe.(htail_panels, htail_normals),
                              vtail = Horseshoe.(vtail_panels, vtail_normals)
                             )

    ## Reference quantities
    fs      = Freestream(alpha    = 1.0,
                         beta     = 1.0,
                         omega    = zeros(3))

    refs    = References(speed    = 150.0,
                         area     = projected_area(wing),
                         span     = span(wing),
                         chord    = mean_aerodynamic_chord(wing),
                         density  = 1.225,
                         location = [0.25 * mean_aerodynamic_chord(wing), 0., 0.])

    ## Stability case
    dv_data = solve_case_derivatives(aircraft, fs, refs);

    dcf = dv_data.aircraft
    nfs = @views dcf[1:6,1]
    ffs = @views dcf[7:9,1]
    dvs = @views dcf[1:6,3:end]

    nf_tests = [0.0004394, -0.0096616, 0.0710165, -0.0027487, 0.0643144, 0.0063322]
    ff_tests = [0.0005934, -0.0097369, 0.0709867]
    dv_tests = [ 0.0318917  0.0065702  -0.6380811    -1.3022877    1.8906515;
                 0.0063291 -0.542542   47.3510327   -16.8276434 -159.4190293;
                 5.1640333  0.0671273  -9.0759321   2150.559606    5.2457761;
                 0.0468793 -0.1592905  77.192654     27.2895215  -15.6091835;
                -1.5506993 -0.0366944 -13.4122289 -4464.6998256  -20.6719332;
                 0.0117921  0.3544702   1.1794656    19.5810734  123.8328465]

    # Nearfield coefficients test
    [ @test nf_c ≈ nf_t atol = 1e-6 for (nf_c, nf_t) in zip(nfs, nf_tests) ]
    # Farfield coefficients test
    [ @test ff_c ≈ ff_t atol = 1e-6 for (ff_c, ff_t) in zip(ffs, ff_tests) ]
    # # Stability derivatives' coefficients test
    [ @test dv_c ≈ dv_t atol = 1e-6 for (dv_c, dv_t) in zip(dvs, dv_tests) ]
end

@testset "Structures - Euler-Bernoulli Beam Elastic Stiffness" begin
    # Deflection stiffness matrix
    K = bending_stiffness_matrix([1., 1.], [1., 1.], [2., 2.], :z)

    ## 1. Fixed hinged beam subjected to force and moment at the center
    A = K[[3,4,6],[3,4,6]]  # v2, φ2, φ3
    b = [-1000, 1000, 0]    # F2, M2, M3

    x = A \ b

    ## Forces
    F1 = K * [ 0.; 0.; x[1:2]; 0.; x[3] ]

    ## 2. Propped cantilever beam with force at one end
    A = K[[1,2,4],[1,2,4]] # v1, φ1, φ2
    b = [10, 0, 0]

    x = A \ b

    ## Forces
    F2 = K * [ x[1:2]; 0.; x[3]; 0.; 0. ]

    @test F1 ≈ [968.75, 875., -1e3, 1e3, 31.25, 0.] atol = 1e-6
    @test F2 ≈ [10., 0., -25., 0., 15, -10.] atol = 1e-6
end

@testset "Structures - Euler-Bernoulli Beam Axial Stiffness" begin
    # Axial stiffness matrix
    J = axial_stiffness_matrix([1., 1., 1.], [1., 1., 1.], [2., 2., 2.])

    ## 1. ???
    A = J[[1,2],[1,2]] # ψ1, ψ2
    b = [-1000, 1000]  # R2, R2

    x = A \ b

    M = J * [ x; zeros(2) ]

    @test M ≈ [-1000., 1000., 0., 0.] atol = 1e-6
end;


text:
mkpath(joinpath(@__DIR__, "..", "data", "hyper_pa", "external"))
mkpath(joinpath(@__DIR__, "..", "data", "hyper_pa", "julia"))


text:
using NNlib
import NNlib: conv!, ∇conv_filter!, ∇conv_data!,
  maxpool!, meanpool!, ∇maxpool!, ∇meanpool!,
  softmax, softmax!, ∇softmax!, logsoftmax, logsoftmax!, ∇logsoftmax
import ..CuArrays: CuVecOrMat, @cufunc, cufunc, CuVector
using CUDAnative

# Activation functions
@cufunc σ(x) = ifelse(x < -80, zero(x), one(x) / (one(x) + exp(-x)))

@cufunc function logσ(x)
  max_v = max(zero(x), -x)
  z = exp(-max_v) + exp(-x-max_v)
  -(max_v + log(z))
end

@cufunc elu(x, α = one(x)) =
  ifelse(x ≥ 0, x/1, α * (exp(x) - one(x)))

# TODO: make @cufunc recognise its own definitions
cufunc(::typeof(swish)) = x -> x * cufunc(σ)(x)

@cufunc function selu(x)
  λ = oftype(x/1, 1.0507009873554804934193349852946)
  α = oftype(x/1, 1.6732632423543772848170429916717)
  λ * ifelse(x > 0, x/1, α * (exp(x) - 1))
end

@cufunc softplus(x) = log1p(exp(x))

# Softmax

const CUDNNFloat = Union{Float16,Float32,Float64}

reshape4D(x::AbstractVector) = reshape(x, 1, 1, length(x), 1)
reshape4D(x::AbstractMatrix) = reshape(x, 1, 1, size(x)...)

function softmax!(out::CuVecOrMat{T}, xs::CuVecOrMat{T}) where T<:CUDNNFloat
  cudnnSoftmaxForward(reshape4D(xs), reshape4D(out))
  return out
end

function ∇softmax!(out::CuVecOrMat{T}, Δ::CuVecOrMat{T}, xs::CuVecOrMat{T}) where T<:CUDNNFloat
  cudnnSoftmaxBackward(reshape4D(softmax(xs)), reshape4D(Δ), reshape4D(out))
  return out
end

function logsoftmax!(out::CuVecOrMat{T}, xs::CuVecOrMat{T}) where T<:CUDNNFloat
  cudnnSoftmaxForward(reshape4D(xs), reshape4D(out), algorithm=CUDNN_SOFTMAX_LOG)
  return out
end

function ∇logsoftmax!(out::CuVecOrMat{T}, Δ::CuVecOrMat{T}, xs::CuVecOrMat{T}) where T<:CUDNNFloat
  cudnnSoftmaxBackward(reshape4D(logsoftmax(xs)), reshape4D(Δ), reshape4D(out);
                       algorithm=CUDNN_SOFTMAX_LOG)
  return out
end

∇logsoftmax(Δ::CuVecOrMat{T}, xs::CuVecOrMat{T}) where T<:CUDNNFloat =
  ∇logsoftmax!(similar(xs), Δ, xs)

# Convolution

const _conv_workspace = Ref{CuVector{UInt8}}()

function conv_workspace(bytes)
  global _conv_workspace
  if isassigned(_conv_workspace) && bytes <= length(_conv_workspace[])
    _conv_workspace[]
  else
    _conv_workspace[] = CuVector{UInt8}(undef, bytes)
  end
end

function conv!(y::CuArray{T}, x::CuArray{T}, w::CuArray{T};
               pad=0, stride=1, mode=0, alpha=1, dilation=1,
               workspace::Union{CuVector, Nothing}=nothing, algo=0) where T<:CUDNNFloat
  if CUDNN_VERSION < 6000
    all(x -> x == 1, dilation) || error("Only dilation = 1 is supported in cuDNN version < 6")
  end
  if workspace === nothing
    workspace_size =
      cudnnGetConvolutionForwardWorkspaceSize(y, x, w, padding=pad, stride=stride, dilation=dilation,
                                              algo=algo, mode=mode)
    workspace = workspace_size != 0 ? conv_workspace(workspace_size) : workspace
  else
    workspace_size = length(workspace[])
  end
  cudnnConvolutionForward(y, x, w, padding=pad, stride=stride, dilation=dilation, mode=mode,
			  alpha=alpha, algo=algo, workspace=workspace, workspace_size=workspace_size)
end

function ∇conv_filter!(dw::CuArray{T}, dy::CuArray{T}, x::CuArray{T}, w::CuArray{T};
                       pad=0, stride=1, mode=0, alpha=1, dilation=1,
                       workspace::Union{CuVector, Nothing}=nothing, algo=0) where T<:CUDNNFloat
  if CUDNN_VERSION < 6000
    all(x -> x == 1, dilation) || error("Only dilation = 1 is supported in cuDNN version < 6")
  end
  if workspace === nothing
    workspace_size =
      cudnnGetConvolutionBackwardFilterWorkspaceSize(dw, x, w, dy, padding=pad, stride=stride,
					             dilation=dilation, algo=algo, mode=mode)
    workspace = workspace_size != 0 ? conv_workspace(workspace_size) : workspace
  else
    workspace_size = length(workspace[])
  end
  cudnnConvolutionBackwardFilter(dw, x, w, dy, padding=pad, stride=stride, dilation=dilation,
				 mode=mode, alpha=alpha, algo=algo, workspace=workspace,
                                 workspace_size=workspace_size)
end

function ∇conv_data!(dx::CuArray{T}, dy::CuArray{T}, x::CuArray{T}, w::CuArray{T};
                     pad=0, stride=1, mode=0, alpha=1, dilation=1,
                     workspace::Union{CuVector, Nothing}=nothing, algo=0) where T<:CUDNNFloat
  if CUDNN_VERSION < 6000
    all(x -> x == 1, dilation) || error("Only dilation = 1 is supported in cuDNN version < 6")
  end
  if workspace === nothing
    workspace_size =
      cudnnGetConvolutionBackwardDataWorkspaceSize(dx, x, w, dy, padding=pad, stride=stride,
                                                   dilation=dilation, algo=algo, mode=mode)
    workspace = workspace_size != 0 ? conv_workspace(workspace_size) : workspace
  else
    workspace_size = length(workspace[])
  end
  cudnnConvolutionBackwardData(dx, x, w, dy, padding=pad, stride=stride, dilation=dilation,
			       mode=mode, alpha=alpha, algo=algo, workspace=workspace,
                               workspace_size=workspace_size)
end

∇conv_bias!(db::CuArray{T}, dy::CuArray{T}; alpha=1, beta=0) where T<:CUDNNFloat =
  cudnnConvolutionBackwardBias(db, dy, alpha=alpha, beta=beta)

maxpool!(y::CuArray{T}, x::CuArray{T}, k; pad=map(_->0,k), stride=k) where T<:CUDNNFloat =
  cudnnPoolingForward(y, x, window=k, padding=pad, stride=stride, mode=0)

∇maxpool!(dx::CuArray{T}, dy::CuArray{T}, y::CuArray{T}, x::CuArray{T}, k;
          pad=map(_->0,k), stride=k) where T<:CUDNNFloat =
  cudnnPoolingBackward(dx, dy, x, y, window=k, padding=pad, stride=stride, mode=0)

meanpool!(y::CuArray{T}, x::CuArray{T}, k; pad=map(_->0,k), stride=k) where T<:CUDNNFloat =
  cudnnPoolingForward(y, x, window=k, padding=pad, stride=stride, mode=1)

∇meanpool!(dx::CuArray{T}, dy::CuArray{T}, y::CuArray{T}, x::CuArray{T}, k;
           pad=map(_->0,k), stride=k) where T<:CUDNNFloat =
  cudnnPoolingBackward(dx, dy, x, y, window=k, padding=pad, stride=stride, mode=1)


text:
LEFT = [-1, 0]
RIGHT = [1, 0]
UP = [0, -1]
DOWN = [0, 1]

move(::Val{:left}) = begin
    snake.dir = LEFT
end

move(::Val{:right}) = begin
    snake.dir = RIGHT
end

move(::Val{:up}) = begin
    snake.dir = UP
end

move(::Val{:down}) = begin
    snake.dir = DOWN
end

@use function keypress(event)
    dir = snake.dir
    if event.keyval == keyboard("w") && dir != DOWN
        move(Val(:up))
    elseif event.keyval == keyboard("s") && dir != UP
        move(Val(:down))
    elseif event.keyval == keyboard("a") && dir != RIGHT
        move(Val(:left))
    elseif event.keyval == keyboard("d") && dir != LEFT
        move(Val(:right))
    elseif event.keyval == 32 || event.keyval == 65307
        global paused = !paused
    end
end


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

using JuliaFEM.Testing

function run_tests(; verbose=true)

    maybe_test_files = readdir(Pkg.dir("JuliaFEM")*"/test")
    is_test_file(fn) = startswith(fn, "test_") & endswith(fn, ".jl")
    test_files = filter(is_test_file, maybe_test_files)
    #test_files = ["test_nodal_constraints.jl"]

    verbose && info("Test files:")
    for (i, test_file) in enumerate(test_files)
        verbose && info("$i   $test_file")
    end

    t0 = Base.time()

    body = quote
        @testset "JuliaFEM" begin
            for fn in $test_files
                @testset "$fn" begin include(fn) end
            end
        end
    end
    eval(body)

    t1 = round(Base.time()-t0, 2)
    info("Testing completed in $t1 seconds.")
end

run_tests()

#=
facts("Testing if somebody used print, println(), @sprint in src directory") do
  # TODO: make better reqular expression. Currently it will match all print words
  lines_with_print = Dict()
  src_dir = joinpath(Pkg.dir("JuliaFEM"),"src")
  src = readdir(src_dir)
  for file_name in src
    fil = open(joinpath(src_dir,file_name),"r")
    for (line_number,line) in enumerate(readlines(fil))
      if ismatch(r"print",line)
        lines_with_print[file_name * ":$line_number"] = "print found in line: $line_number"
      end
    end
    close(fil)
  end
  @fact lines_with_print => isempty "Instead of println() use Logging.jl package"
end

facts("Testing if we have non ascii characters in the src files") do
  # TODO: we should allow Greeck letters in documentation. Thus this test should skip docstrings
  lines_with_non_ascii = []
  src_dir = joinpath(Pkg.dir("JuliaFEM"),"src")
  src = readdir(src_dir)
  for file_name in src
    fil = open(joinpath(src_dir,file_name),"r")
    for (line_number,line) in enumerate(readlines(fil))
      if ismatch(r"[^\x00-\x7F]",line)
        push!(lines_with_non_ascii, file_name * ":line $line_number")
      end
    end
    close(fil)
  end
  @fact lines_with_non_ascii => isempty "non ascii charecters found in src -> test is failing"
end

facts("Looking the [src,test] folders *.jl files header information") do
  files_no_license = []
  pkg_dir = Pkg.dir("JuliaFEM")
  dirs = ["test";"src"]
  for folder in dirs
    for file_name in readdir(joinpath(pkg_dir,folder))
      if file_name[end-2:end] == ".jl"
        fil = open(joinpath(pkg_dir,folder,file_name),"r")
        head = readall(fil)
      else
        continue
      end
      if ~ismatch(r"This file is a part of JuliaFEM",head)
        push!(files_no_license,"$file_name is missing JuliaFEM statement")
      end
      if ~ismatch(r"MIT",head)
        push!(files_no_license,"$file_name is missing MIT statement")
      end
      if ~ismatch(r"https://github.com/JuliaFEM/JuliaFEM.jl/blob/master/LICENSE.md",head)
        push!(files_no_license,"$file_name is missing reference to LICENSE.md")
      end
      close(fil)
    end
  end
  out_str = """License information missing or incorrect. Please use these two lines in the beginning of each file:

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

  """
  @fact files_no_license => isempty out_str
end

#=
test_files = readdir(Pkg.dir("JuliaFEM")*"/test")
for test_file in test_files
  Logging.info("checking is $test_file is real test file")
  if (startswith(test_file, "test_")) & (endswith(test_file, ".jl"))
    Logging.info("Running tests from file $test_file")
    include(test_file)
  end
end
=#

# Keep this at the end of this file (include statements above this)
@Logging.configure(level=DEBUG)
@debug("Let's print the summary of the tests")
for dic in FactCheck.getstats()
  @debug(dic[1], ": ",dic[2])
end
=#


text:
fn main() -> int {
    for (let y = 1.0; y >= -1.0; y = y - 0.05) {
        for (let x = -2.5; x < 1.0; x = x + 0.03) {
            let real = 0.0;
            let imag = 0.0;
            let iter = 0;
            let max_iter = 1000;

            while (real * real + imag * imag < 4.0 && iter < max_iter) {
                let next_real = real * real - imag * imag + x;
                let next_imag = 2.0 * real * imag + y;

                real = next_real;
                imag = next_imag;

                iter++;
            }

            if (iter == max_iter) {
                printf("#");
            } else {
                printf(".");
            }
        }
        printf("\n");
    }

    return 0;
}

max_stars_repo_name: paulcacheux/javalette

max_issues_repo_name: paulcacheux/javalette

max_forks_repo_name: paulcacheux/javalette


text:
struct CodePoint
    expr
    line::Union{LineNumberNode, Nothing}
end

struct CalculationSequenceError <: Exception
    var::String
end
CalculationSequenceError() = begin
    errmessage = join(split(
        """
        The `@T` macro was unable to order the given formula(s) in a way that
        resulted in a correct calculation flow. Note that `@T` cannot take
        indexing into account with (1) a mix of forwards `A[t+1]` and backwards
        `A[t-1]` referencing, (2) with runtime variables like `A[c+t]`, (3) with
        nonlinear t indexing like `A[t^2-3t]`, and (4) with non-t indexing
        like A[34].
        """, "\n"
    ))

    CalculationSequenceError(errmessage)
end

"
Function to make testing between macros easier
"
function striplines(ex)
    ex′ = Expr(:block, deepcopy(ex))

    # Define the mutation
    function recurse(ex)
        if ex isa Expr
            for i ∈ 1:length(ex.args)
                if ex.args[i] isa LineNumberNode
                    ex.args[i] = nothing
                else
                    recurse(ex.args[i])

                end
            end
        end
    end

    recurse(ex′)

    ex = ex′.args[1]
    return ex

end

@testset "has_symbol" begin
    @test has_symbol(:(a + b + c + foo(a + b + c + foo(d))), :d)
    @test !has_symbol(:(a + b + c + foo(a + b + c + foo(d))), :q)
end

"
Test if an expression contains the specefic variable
"
has_symbol(ex::Expr, s::Symbol) = any(has_symbol.(ex.args, s))
has_symbol(ex::Symbol, s::Symbol) = ex == s
has_symbol(ex, s::Symbol) = false

@testset "get_variables" begin
    @test get_variables(:(a = foo(b + c + bar(d) + e[f]))) == [:a, :b, :c, :d, :e, :f]
end

"
Get all the variable names from an expression, this excludes macro names and function names
"
function get_variables(ex)
    varnames = Array{Symbol, 1}()

    # behaviour for each type
    recurse(ex) = nothing
    recurse(ex::Symbol) = push!(varnames, ex)
    recurse(ex::Expr) = begin
        i₁ = ex.head ∈ [:call, :macocall] ? 2 : 1
        for i ∈ i₁:length(ex.args)
            recurse(ex.args[i])
        end
    end

    # Apply and return
    recurse(ex)
    return varnames
end

"
Assign a value to a variable within an expression
"
function assign_variable(ex, var::Symbol, value)
    ex′ = Expr(:block, deepcopy(ex))

    # behaviour for each type
    recurse(ex) = nothing
    recurse(ex::Expr) = begin
        i₁ = ex.head ∈ [:call, :macocall] ? 2 : 1
        for i ∈ i₁:length(ex.args)
            if ex.args[i] == var
                ex.args[i] = value
            else
                recurse(ex.args[i])
            end
        end
    end

    # Apply and return
    recurse(ex′)
    ex = ex′.args[1]
    return ex
end

@testset "get_t_refs" begin
    @test(
        get_t_refs(:(a[5] + a[8] + b[t+40])) ==
        [:b => :(t + 40)]
    )
    @test(
        get_t_refs(:(a[5] + a[8] + b[t+40] + b[t-30] + c[t])) ==
        [:b => :(t + 40), :b => :(t - 30), :c => :t]
    )
end
"
Find references involving `t` in an equation. Note: nested referencing is currently not supported.
"
function get_t_refs(ex)
    references = Vector()

    recurse(ex) = nothing
    recurse(ex::Expr) = begin
        if ex.head == :ref && length(ex.args) == 2 && ex.args[1] isa Symbol
            (ex₁, ex₂) = ex.args

            if has_symbol(ex₂, :t)
                push!(references, ex₁=>ex₂)
            end
        end
        for i in ex.args
            recurse(i)
        end
    end

    recurse(ex)
    return references
end

@testset "vars_without_t_index" begin
    @test vars_without_t_index(:(a+b[t])) == Set([:a])
    @test vars_without_t_index(:a) == Set([:a])
    @test vars_without_t_index(:(a+b[t]+c+t)) == Set([:a,:c])
end
"
Collect all variables in an expression that isn't indexed by `t`
"
function vars_without_t_index(ex)
    vars = Set()
    ex′ = Expr(:begin, ex)

    # behaviour for each type
    recurse(ex::Expr) = begin
        i₁ = ex.head ∈ [:call, :macocall] ? 2 : 1
        for i ∈ i₁:length(ex.args)
            if i==i₁ && ex.head == :ref
                # nothing
            elseif ex.args[i] isa Symbol && ex.args[i] != :t
                push!(vars, ex.args[i])
            elseif ex.args[i] isa Expr
                recurse(ex.args[i])
            end
        end
    end

    recurse(ex′)
    return vars
end

@testset "t_linearcombinations" begin
    @test t_linearcombinations(:(t*t)) == false
    @test t_linearcombinations(:(t^2)) == false
    @test t_linearcombinations(:((5+67)*foo(t))) == false
    @test t_linearcombinations(:(t == 0 ? 1 : 2)) == false

    @test t_linearcombinations(:(t+1+2/3*5))
    @test t_linearcombinations(:(t*2+1+2/3*5))
    @test t_linearcombinations(:((5+67)*t))

    @test t_linearcombinations(:(1+(a+t))) == true
    @test t_linearcombinations(:(1+(a+2t))) == true
    @test t_linearcombinations(:(1+(a+(t*t)))) == false
    @test t_linearcombinations(:(1+(a+(q*q)))) === nothing # no t
    @test t_linearcombinations(:(1+(a+(t)+t-t+5))) == false
end
"
Test if expression contains an linear combination of t.
    if t is linear: true
    if t is non-linear: false
    if not explicit: nothing
"
function t_linearcombinations(ex)

    # Can we skip the investigation?
    vars = get_variables(ex)
    if !(:t in vars)
        return nothing
    elseif [i for i in vars if i == :t] != [:t]
        return false
    end

    # behaviour for each type
    recurse(args...) = nothing
    recurse(ex::Symbol, linear_parents) = ex == :t ? linear_parents : nothing
    recurse(ex::Expr, linear_parents) = begin
        # Test if only + - and multiplication by a constant in t
        if ex.head == :call && (ex.args[1] == :+ || ex.args[1] == :- || ex.args[1] == :*)
            calls = [recurse(i, linear_parents) for i in ex.args]

            if false in calls
                false
            elseif true in calls
                true
            else
                nothing
            end
        else
            calls = [recurse(i, false) for i in ex.args]
            if false in calls
                false
            else
                nothing
            end
        end
    end

    # call the recursion
    return recurse(ex, true)
end

@testset "get_t" begin
    t = 2
    @test get_t([1,2,3], t) == 2
    @test get_t(37246, 2) == 37246
end
function get_t(var, t)
    var isa Vector ? var[t] : var
end

@testset "wrap_t_getter" begin
    @test wrap_t_getter(:(a+b[t])) == :($get_t(a, t) + b[t])
    @test wrap_t_getter(:a) == :($get_t(a, t))
    @test wrap_t_getter(:(a+b[t]+c+t)) == :($get_t(a, t) + b[t] + $get_t(c, t) + t)
end
function wrap_t_getter(ex)
    ex′ = Expr(:begin, deepcopy(ex))

    # behaviour for each type
    recurse(ex::Expr) = begin
        i₁ = ex.head ∈ [:call, :macocall] ? 2 : 1
        for i ∈ i₁:length(ex.args)
            if i==i₁ && ex.head == :ref
                # nothing
            elseif ex.args[i] isa Symbol && ex.args[i] != :t
                ex.args[i] = Expr(:call, get_t, ex.args[i], :t)
            elseif ex.args[i] isa Expr
                recurse(ex.args[i])
            end
        end
    end

    recurse(ex′)

    ex = ex′.args[1]
    return ex
end

expr_to_assignment_dict(expr; line::Union{Nothing, LineNumberNode}=nothing) = begin
    # Convert to convenient form
    if !(expr isa Expr) || (expr.head != :(block))
        expr = Expr(:block, expr)
    end

    # Collect all definitions into dict (e.g. Dict(:A=>:(B+C), :B=>:(C+D))
    formulas = OrderedDict{Symbol, CodePoint}()
    lastline = line
    for e ∈ expr.args
        if e isa LineNumberNode || e === nothing
            lastline = e
            continue
        end

        if !(expr isa Expr) || (e.head != :(=))
            throw(ErrorException("Only assignment expressions are valid, like `A = 5`, got: $(e)"))
        end
        formulas[e.args[1]] = CodePoint(e.args[2], lastline)
        lastline = line
    end

    return formulas
end

"
Last item in an OrderedDict
"
last(d::OrderedDict) = begin
    for (i, item) ∈ enumerate(d)
        i == length(d) && return item
    end
end


text:
#!/usr/bin/env julia

using SimradRaw

# Prints the NMEA strings for one or more RAW files.

function nmeas(datagrams)
    function _it(chn1)
        for datagram in datagrams
            if typeof(datagram) == SimradRaw.TextDatagram && datagram.dgheader.datagramtype == "NME0"
                put!(chn1, datagram.text)
            end
        end
    end

    return Channel(_it, ctype=String)
end

function main(args)
    for nmea in nmeas(datagrams(args))
        println(nmea)
    end
end

main(ARGS)