text:
@testset "Genus" begin
  Qx, x = QQ["x"]
  K, a = NumberField(x^2 - 2, "a")
  OK = maximal_order(K)
  Kt, t  = K["t"]

  E1, b1 = NumberField(t^2 - a, "b1") # ramified at 2
  E2, b2 = NumberField(t^2 - 5, "b2") # unramified at 2

  p = prime_decomposition(OK, 2)[1][1]
  q = prime_decomposition(OK, 3)[1][1]

  #
  # ramified & dyadic
  #

  g = @inferred genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1)], type = :det)
  @test isramified(g)
  @test !issplit(g)
  @test !isinert(g)
  @test scales(g) == [0, 2]
  @test ranks(g) == [1, 2]
  @test dets(g) == [1, -1]
  @test det(g) == -1
  @test discriminant(g, 1) == 1
  @test discriminant(g, 2) == 1
  @test discriminant(g) == 1
  @test g == g
  @test g != genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1), (6, 1, -1, 3)])
  @test g != genus(HermLat, E1, p)
  @test g != genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 3, -1, 1)], type = :det)
  @test g != genus(HermLat, E1, p, [(0, 1, 1, 0), (1, 2, -1, 1)], type = :det)
  @test genus(HermLat, E1, p) != genus(HermLat, E1, q)
  @test genus(HermLat, E1, p) != genus(HermLat, E2, p)

  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E1; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E1, K(det(gram_matrix(g))) * inv(d), p)

  # empty genus
  g = @inferred genus(HermLat, E1, p)
  @test isramified(g)
  @test !issplit(g)
  @test !isinert(g)
  @test rank(g) == 0
  @test scales(g) == []
  @test ranks(g) == []
  @test dets(g) == []
  @test det(g) == 1
  @test discriminant(g) == 1
  @test g == g

  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E1; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E1, K(det(gram_matrix(g))) * inv(d), p)

  g = @inferred genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1)], type = :disc)
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  @test g == g
  @test representative(g) in g
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E1; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E1, K(det(gram_matrix(g))) * inv(d), p)

  # negative scale
  g = @inferred genus(HermLat, E1, p, [(-2, 1, 1, -1), (2, 2, -1, 1)], type = :disc)
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  @test g == g
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E1; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E1, K(det(gram_matrix(g))) * inv(d), p)

  @test_throws ArgumentError genus(HermLat, E1, p, [(0, 1, 1, 1), (2, 2, -1, 0)], type = :det)
  @test_throws ArgumentError genus(HermLat, E1, p, [(0, 1, 1, 1), (2, 2, -1, 0)], type = :disc)
  @test_throws ArgumentError genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1)], type = :bla)
  # scale not increasing
  @test_throws ArgumentError genus(HermLat, E1, p, [(3, 1, 1, 0), (2, 2, -1, 1)])
  # negative rank
  @test_throws ArgumentError genus(HermLat, E1, p, [(1, -1, 1, 0), (2, 2, -1, 1)])
  # bad determinant class
  @test_throws ArgumentError genus(HermLat, E1, p, [(0, 1, 2, 0), (2, 2, -1, 1)])
  # wrong norm valuation (must 1 * 2 since rank is odd)
  @test_throws ArgumentError genus(HermLat, E1, p, [(1, 1, 1, 3)], type = :det)
  # wrong prime
  @test_throws ArgumentError genus(HermLat, E1, p, [(1, 1, 1)], type = :det)
  # wrong prime
  @test_throws ArgumentError genus(HermLat, E1, p, [(1, 1)])

  #
  # unramified & inert & dyadic
  #

  g = genus(HermLat, E2, p, [(0, 1, 1), (3, 5, -1)], type = :det)

  @test g == genus(HermLat, E2, p, [(0, 1), (3, 5)])

  @test !isramified(g)
  @test isinert(g)
  @test !issplit(g)
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  @test g == g
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  # rank zero genus
  g = @inferred genus(HermLat, E2, p)
  @test rank(g) == 0
  @test !isramified(g)
  @test isinert(g)
  @test !issplit(g)
  @test scales(g) == []
  @test ranks(g) == []
  @test dets(g) == []
  @test det(g) == 1
  @test discriminant(g) == 1
  @test g == g

  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, +1)], type = :disc)

  @test g == genus(HermLat, E2, p, [(0, 1), (2, 2)])

  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  @test g == g
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  # wrong det class
  @test_throws ArgumentError genus(HermLat, E2, p, [(0, 1, -1), (2, 2, +1)], type = :det)
  # wrong det class
  @test_throws ArgumentError genus(HermLat, E2, p, [(0, 1, +1), (2, 2, -1)], type = :det)
  # wrong det class
  @test_throws ArgumentError genus(HermLat, E2, p, [(0, 1, +2), (2, 2, -1)], type = :det)
  # wrong type
  @test_throws ArgumentError genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :bla)
  # negative rank
  @test_throws ArgumentError genus(HermLat, E2, p, [(0, -1, 1), (2, 2, +1)], type = :det)
  # non-increasing scale
  @test_throws ArgumentError genus(HermLat, E2, p, [(3, 1, 1), (2, 2, +1)], type = :det)
  # wrong prime
  @test_throws ArgumentError genus(HermLat, E2, p, [(0, 1, 1, 1)], type = :det)

  #
  # ramified & non-dyadic
  #

  p = prime_decomposition(OK, 5)[1][1]
  g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :det)
  @test rank(g) == 3
  @test isramified(g)
  @test !issplit(g)
  @test !isinert(g)
  @test g == g

  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  # Test some problem with non_norm_rep
  # It is cached on the genus
  @test all(1:100) do i
    g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :det)
    hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  end

  # rank zero genus
  g = @inferred genus(HermLat, E2, p)
  @test rank(g) == 0
  @test isramified(g)
  @test !issplit(g)
  @test !isinert(g)
  @test scales(g) == []
  @test ranks(g) == []
  @test dets(g) == []
  @test det(g) == 1
  @test discriminant(g) == 1
  @test g == g

  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  g = genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :disc)
  @test g == g
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  @test_throws ArgumentError genus(HermLat, E2, p, [(0, 1, 1), (2, 2, -1)], type = :bla)
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  # -1 is a local square, so the determinant equals discriminant
  @test discriminant(g, 2) == -1
  @test discriminant(g) == -1
  @test det(g) == -1

  #
  # unramified & split & non-dyadic
  #

  p = prime_decomposition(OK, 3)[1][1]
  g = genus(HermLat, E2, p, [(0, 2, 1)], type = :disc)
  @test !isramified(g)
  @test !isinert(g)
  @test issplit(g)
  @test discriminant(g) == 1
  @test g == g
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  g = genus(HermLat, E2, p, [(0, 2, 1), (1, 1, 1), (2, 3, 1)], type = :disc)
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E2; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E2, K(det(gram_matrix(g))) * inv(d), p)

  # Wrong det class in last component (must be 1)
  @test_throws ArgumentError g = genus(HermLat, E2, p, [(0, 2, 1), (1, 1, 1), (2, 3, -1)], type = :disc)

  #
  # ramified & non-dyadic and -1 not a local norm
  #
  K, _ = Hecke.rationals_as_number_field()
  Kt, t = K["t"]
  E, = number_field(t^2 + 7)
  p = prime_decomposition(maximal_order(K), 7)[1][1]

  # islocal_norm(E, -1, 7) == false

  g = genus(HermLat, E, p, [(0, 2, 1)], type = :disc)
  @test g == genus(HermLat, E, p, [(0, 2, -1)])
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  @test g == g
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E, K(det(gram_matrix(g))) * inv(d), p)

  g = genus(HermLat, E, p, [(0, 2, 1), (1, 4, 1), (2, 3, -1)], type = :disc)
  # swap because of the ranks
  @test g == genus(HermLat, E, p, [(0, 2, -1), (1, 4, 1), (2, 3, 1)])
  @test sprint(show, "text/plain", g) isa String
  @test sprint(show, "text/plain", g, context = :compact => true) isa String
  @test sprint(show, g) isa String
  @test sprint(show, g, context = :compact => true) isa String
  L = @inferred representative(g)
  @test L in g
  # p => g is cached on L
  @test hermitian_lattice(E; gram_ambient_space = gram_matrix(g)) in g
  d = @inferred det_representative(g)
  @test islocal_norm(E, K(det(gram_matrix(g))) * inv(d), p)

  for t in [(0, 1, 1), (0, 1, -1), (0, 2, 1), (0, 2, -1), (1, 2, -1)]
    g = genus(HermLat, E, p, [t])
    @test sprint(show, "text/plain", g) isa String
    @test sprint(show, "text/plain", g, context = :compact => true) isa String
    @test sprint(show, g) isa String
    @test sprint(show, g, context = :compact => true) isa String
    @test g == g
    L = @inferred representative(g)
    @test L in g
    # p => g is cached on L
    @test hermitian_lattice(E; gram_ambient_space = gram_matrix(g)) in g
    d = @inferred det_representative(g)
    @test islocal_norm(E, K(det(gram_matrix(g))) * inv(d), p)
  end

  #  odd scale, so rank even, and rank/2 is odd/even => det must be -1,1
  @test_throws ArgumentError genus(HermLat, E, p, [(1, 1, 1)])
  @test_throws ArgumentError genus(HermLat, E, p, [(1, 2, 1)])
  @test_throws ArgumentError genus(HermLat, E, p, [(1, 6, 1)])
  @test_throws ArgumentError genus(HermLat, E, p, [(1, 4, -1)])

  ##############################################################################
  #
  #  Now global
  #
  ##############################################################################

  Qx, x = QQ["x"]
  K, a = NumberField(x^2 - 2, "a")
  OK = maximal_order(K)
  Kt, t  = K["t"]

  E1, b1 = NumberField(t^2 - a, "b1") # ramified at 2
  E2, b2 = NumberField(t^2 - 5, "b2") # unramified at 2

  p = prime_decomposition(OK, 2)[1][1]
  q = prime_decomposition(OK, 3)[1][1]

  g = @inferred genus(HermLat, E1, p, [(0, 1, 1, 0), (2, 2, -1, 1)])
  L = representative(g)
  G = genus(L)
  @test sprint(show, "text/plain", G) isa String
  @test sprint(show, "text/plain", G, context = :compact => true) isa String
  @test sprint(show, G) isa String
  @test sprint(show, G, context = :compact => true) isa String
  g = @inferred genus(HermLat, E1, q, [(0, 2, 1)])
  M = representative(g)
  G2 = genus(M)
  @test genus(representative(orthogonal_sum(G, G2))) == orthogonal_sum(G, G2)
  @test genus(representative(orthogonal_sum(G2, G))) == orthogonal_sum(G2, G)

  rlp = real_places(K)
  sig = Dict(rlp[1] => 2, rlp[2] => 2)
  G = genus([g], sig)
  @test G == genus([g], [(rlp[1], 2), (rlp[2], 2)])

end


text:
function main(n)
  n = round(Int, n / 2 * 2)
  a = rand(0:100, n, n)
  b = a
  c = a * b
  v = round(Int, n/2) + 1
  println(c[v, v])
end

function when_isMainModule()
  n = 100
  if length(ARGS) >= 1
    n = parse(Int, ARGS[1])
  end
  main(n)
end

when_isMainModule()

#########
# Results on i9-9980XE
# Skylake-X overclocked 4.1GHz all-core turbo,
# AVX2 4.0GHz all-core, AVX-512 3.5GHz all-core
# Input 1500x1500 random large int64 matrix

# Nim 1.0.4. Compilation option: "-d:danger -d:openmp"
# Julia v1.3.1
# Python 3.8.1 + Numpy-MKL 1.18.0

# Nim: 0.14s, 22.7Mb
# Julia: 1.67s, 246.5Mb
# Python Numpy: 5.69s, 75.9Mb

ext: jl

max_stars_repo_name: keyehzy/Arraymancer

max_issues_repo_name: keyehzy/Arraymancer

max_forks_repo_name: keyehzy/Arraymancer


text:
ENV["GKSwstype"] = "100" #src

# # Working with Data
#
# > Data is the new oil. It’s valuable, but if unrefined it cannot really be used. It has to be changed into gas, plastic, chemicals, etc to create a valuable entity that drives profitable activity; so must data be broken down, analyzed for it to have value.
# >
# > \- Clive Humbly, 2006
#
# There are several great packages for working with tabular data in Julia.  We have already seen a little bit of **`DataFrames`**, but we will focus on **`JuliaDB`** in this course module.  Many of the reasons one would choose **`JuliaDB`** instead of **`DataFrames`** (and vice versa) are personal preferences.  Most packages that integrate with tabular data, such as **`StatsPlots`**, work equally well with both, so your choice mainly depends on the slight differences in the interfaces for filtering and selecting data.  However, **`JuliaDB`** does have some key differences compared to **`DataFrames`**:
#
# - It has two data types for accessing data in different ways.
# - It can run distributed operations and work with data larger than computer memory.
# - It integrates with [**`OnlineStats`**](https://github.com/joshday/OnlineStats.jl) to calculate statistics and models [out-of-core](https://en.wikipedia.org/wiki/External_memory_algorithm).
#
# # [JuliaDB](https://github.com/JuliaComputing/JuliaDB.jl)
#
# <img src = "https://user-images.githubusercontent.com/25916/36773410-843e61b0-1c7f-11e8-818b-3edb08da8f41.png" width=200>
#
# We will cover the basic functionality of **`JuliaDB`** through an example.  Let's use the **`RDatasets`** package to load the `diamonds` dataset, a collection of measurements on the quality of ~54,000 diamonds.  The variables include:
#
# - Carat: weight of the diamond
# - Cut: quality of the cut (Fair, Good, Very Good, Premium, Ideal)
# - Color: diamond color, from F to D (worst to best)
# - Clarity: categorization of clarity with values (worst to best) I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF
# - Depth: total depth percentage = Z / mean(X, Y)
# - Table: width of diamond's top relative to widest point
# - Price: price in $US
# - X: length
# - Y: width
# - Z: depth
#
# The **`RDatasets`** package loads datasets as a `DataFrame`, but fortunately the data structures provided by **`DataFrames`** and **`JuliaDB`** can be easily converted back and forth.  A `DataFrame` can be converted to a `NextTable` (one of the core **`JuliaDB`** tabular data structures) with the `table` function.

using JuliaDB
import RDatasets

df = RDatasets.dataset("ggplot2", "diamonds")

t = table(df)

# ## Selecting Columns
#
# **`JuliaDB`** has many methods for selecting columns via the `select` function:

select(t, 1)  # select the column as position 1 (:Carat)
select(t, :Carat) # select the column with the given name

#-

select(t, (1,7))  # select the columns at positions 1 and 7 (:Carat and :Price)
select(t, (:Carat, :Price))  # select the columns :Carat and :Price
select(t, r"(Carat|Price)") # select the columns that match a regular expression

# ## Selecting Rows
#
# Rows are selected based on some criteria with the `filter` function.  The first argument is a function which is then applied to whatever is selected by the `select` keyword argument.  Any valid selection via the `select` function (Integers, column names as Symbols, Regexes, etc.) can be used.
#
# Note that if you select a single column, the function is applied to each element of that column and a regular vector is returned.  If the selection selects multiple columns, then the function is applied to each row of the subsetted table (as a NamedTuple) and a table is returned.

filter(x -> x > .7, t; select = :Carat)  # function applied to Vector

#-

filter(x -> x.Carat > .7, t; select = r"(Carat|Price)")  # function applied to NamedTuples of rows

# ## Grouping
#
# A common table operation is to summarize a column across the groups in a different column.  With the `groupby` function , here we are calculating the average `:Price` for each level of `:Cut`.

using Statistics

groupby(mean, t, :Cut; select = :Price)

# It is necessary for groupby operations to allocate temporary vectors (one per group).  What if we could perform a groupby operation without allocating?  First consider the `reduce` function, which applies a pairwise function to the rows of a table.

reduce(+, t; select=:Price)  # Sum of prices for all diamonds in the dataset

# The `groupreduce` function will apply a pairwise function across the groups of a different variable, similar to `groupby`:

groupreduce(+, t, :Cut; select = :Price)

# The drawback is that the function argument for `reduce` and `groupreduce` must work on two items at a time.  To help alleviate this requirement, **`JuliaDB`** integrates with [**`OnlineStats`**](https://github.com/joshday/OnlineStats.jl), a package that provides parallelizable single-pass algorithms for statistics.  `OnlineStat` objects can be provided to both `reduce` and `groupreduce`, which opens up a wide variety of statistical calculations without the need for temporary allocations.

using OnlineStats

reduce(Sum(Int), t; select = :Price)

#-

groupreduce(Sum(Int), t, :Cut; select = :Price)

# Many `OnlineStat` objects are also plottable with **`Plots`**.  Here we will plot a histogram with the `Hist` stat.  We can see that diamonds are often cut down to be a "nice" carat size.

using Plots

o = reduce(Hist(0:.01:1), t; select = :Carat)

plot(o)

max_stars_repo_name: fercarozzi/JuliaAcademyMaterials

max_issues_repo_name: fercarozzi/JuliaAcademyMaterials

max_forks_repo_name: fercarozzi/JuliaAcademyMaterials


text:
"""
DataFrames　の describe() は，sum(), var() を計算しない。

sum(), var() に対応する関数 describe2() を定義しておく。

指定できるのは :sum, :mean, :var, :std, :min, :q25, :median, :q75, :max, :nmissing である。

すべての統計量を計算するときは，統計量の指定をしないようにするか :all を指定すればよい。

なお，DataFrames.describe() と競合してもかまわないならば，関数名を describe にしてもよい。
この場合，DataFrames　の describe() を使うときは，DataFrames.describe() とする。

"""

using DataFrames, Statistics
function describe(df, arg...=:all)
    q25(x) = quantile(x, 0.25)
    q75(x) = quantile(x, 0.75)
    nmissing(x) = nrow(df) - length(ismissing.(x))
    arg0 = [:sum, :mean, :var, :std, :min, :q25, :median, :q75, :max, :nmissing]
    func = [sum, mean, var, std, minimum, q25, median, q75, maximum, nmissing]
    funcname = ["sum", "mean", "var", "std", "minimum", "q25", "median", "q75", "maximum", "nmissing"]
    arg[1] == :all && (arg = arg0)
    res = DataFrame(variable = Symbol.(names(df)))
    n = ncol(df)
    m = length(arg)
    body = Array{Float64, 2}(undef, n, m)
    loc = indexin(arg, arg0)
    for i in 1:n
        x = skipmissing(df[!, i])
        body[i, :] = [func[j](x) for j in loc]
    end
    bodydf = DataFrame(body, funcname[loc])
    pos = indexin(["nmissing"], funcname[loc])[1]
    isnothing(pos) || (bodydf[!, pos] = Int.(bodydf[:, pos]))
    hcat(res, bodydf)
end

"""
使用例

julia> using RDatasets, Statistics

julia> iris = dataset("datasets", "iris");

julia> describe(iris[:, 1:4])
4×11 DataFrame
 Row │ variable     sum      mean     var       std       minimum  q25      median   q75      maximum  nmissing
     │ Symbol       Float64  Float64  Float64   Float64   Float64  Float64  Float64  Float64  Float64  Int64
─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ SepalLength    876.5  5.84333  0.685694  0.828066      4.3      5.1     5.8       6.4      7.9         0
   2 │ SepalWidth     458.6  3.05733  0.189979  0.435866      2.0      2.8     3.0       3.3      4.4         0
   3 │ PetalLength    563.7  3.758    3.11628   1.7653        1.0      1.6     4.35      5.1      6.9         0
   4 │ PetalWidth     179.9  1.19933  0.581006  0.762238      0.1      0.3     1.3       1.8      2.5         0

julia> DataFrames.describe(iris[:, 1:4])
4×7 DataFrame
 Row │ variable     mean     min      median   max      nmissing  eltype
     │ Symbol       Float64  Float64  Float64  Float64  Int64     DataType
─────┼─────────────────────────────────────────────────────────────────────
   1 │ SepalLength  5.84333      4.3     5.8       7.9         0  Float64
   2 │ SepalWidth   3.05733      2.0     3.0       4.4         0  Float64
   3 │ PetalLength  3.758        1.0     4.35      6.9         0  Float64
   4 │ PetalWidth   1.19933      0.1     1.3       2.5         0  Float64
"""

max_stars_repo_name: r-de-r/stats

max_issues_repo_name: r-de-r/stats

max_forks_repo_name: r-de-r/stats


text:
using MixedBlockArrays
using SparseArrays
using LinearAlgebra

ax1 = blockedrange([1,2])
ax2 = blockedrange([1,1,2])
T = Float64
A = MixedBlockArray{T}((ax1,ax2))

A[Block(1,3)] = Fill(2, (1,2))
A[Block(2),Block(2)] = SparseMatrixCSC{Float64}(I, 2, 1)
A[Block(2,2)] = zeros(2,2)

A[Block(2,2)][1,1] = pi
A[3, 2] = -pi

@assert A == [0 0 2 2; 0 pi 0 0; 0 -pi 0 0]


text:
export GRDarray, GRDstack, GRDdimMetadata, GRDarrayMetadata

const GRD_INDEX_ORDER = ForwardIndex()
const GRD_LON_ARRAY = ForwardArray()
const GRD_LAT_ARRAY = ReverseArray()
const GRD_BAND_ARRAY = ForwardArray()
const GRD_LON_RELATION = ForwardRelation()
const GRD_LAT_RELATION = ReverseRelation()
const GRD_BAND_RELATION= ForwardRelation()

# Metadata ########################################################################

"""
[`Metadata`](@ref) wrapper for `GRDarray` dimension metadata.
"""
struct GRDdimMetadata{K,V} <: DimMetadata{K,V}
    val::Dict{K,V}
end

"""
[`Metadata`](@ref) wrapper for `GRDarray` metadata.
"""
struct GRDarrayMetadata{K,V} <: ArrayMetadata{K,V}
    val::Dict{K,V}
end

# GRD attributes wrapper. Only used during file load, for dispatch.
struct GRDattrib{T,F,A}
    filename::F
    attrib::A
end
GRDattrib(filename::AbstractString) = begin
    filename = first(splitext(filename))
    lines = readlines(filename * ".grd")
    entries = filter!(x -> !isempty(x) && !(x[1] == '['), lines)
    attrib = Dict(Pair(string.(strip.(match(r"([^=]+)=(.*)", st).captures[1:2]))...) for st in entries)
    T = grd_datatype_translation[attrib["datatype"]]

    GRDattrib{T,typeof(filename),typeof(attrib)}(filename, attrib)
end

filename(grd::GRDattrib) = grd.filename
attrib(grd::GRDattrib) = grd.attrib

dims(grd::GRDattrib, usercrs=nothing) = begin
    attrib = grd.attrib
    crs = ProjString(attrib["projection"])

    ncols, nrows, nbands = size(grd)

    xbounds = parse.(Float64, (attrib["xmin"], attrib["xmax"]))
    ybounds = parse.(Float64, (attrib["ymin"], attrib["ymax"]))

    xspan = (xbounds[2] - xbounds[1]) / ncols
    yspan = (ybounds[2] - ybounds[1]) / nrows

    # Not fully implemented yet
    latlon_metadata = GRDdimMetadata(Dict())

    latmode = Projected(
        order=Ordered(GRD_INDEX_ORDER, GRD_LAT_ARRAY, GRD_LAT_RELATION),
        span=Regular(yspan),
        sampling=Intervals(Start()),
        crs=crs,
        usercrs=usercrs,
    )
    lonmode = Projected(
        order=Ordered(GRD_INDEX_ORDER, GRD_LON_ARRAY, GRD_LON_RELATION),
        span=Regular(xspan),
        sampling=Intervals(Start()),
        crs=crs,
        usercrs=usercrs,
    )
    lat = Lat(LinRange(ybounds[1], ybounds[2] - yspan, nrows), latmode, latlon_metadata)
    lon = Lon(LinRange(xbounds[1], xbounds[2] - xspan, ncols), lonmode, latlon_metadata)
    band = Band(1:nbands; mode=Categorical(Ordered()))
    lon, lat, band
end

metadata(grd::GRDattrib, args...) = begin
    metadata = GRDarrayMetadata()
    for key in ("creator", "created", "history")
        val = get(grd.attrib, key, "")
        if val != ""
            metadata[key] = val
        end
    end
    metadata
end

missingval(grd::GRDattrib{T}) where T = begin
    mv = try
        parse(T, grd.attrib["nodatavalue"])
    catch
        @warn "No data $(missingval) is not convertible to data type $T. `missingval` set to NaN."
        missing
    end
end

name(grd::GRDattrib) = Symbol(get(grd.attrib, "layername", ""))

Base.eltype(::GRDattrib{T}) where T = T

Base.size(grd::GRDattrib) = begin
    ncols = parse(Int, grd.attrib["ncols"])
    nrows = parse(Int, grd.attrib["nrows"])
    nbands = parse(Int, grd.attrib["nbands"])
    # The order is backwards to jula array order
    ncols, nrows, nbands
end

Base.Array(grd::GRDattrib) = mmapgrd(Array, grd)

# Array ########################################################################

"""
    GRDarray(filename::String;
        usercrs=nothing,
        dims=(),
        refdims=(),
        name=nothing,
        missingval=nothing,
        metadata=nothing)

A [`DiskGeoArray`](@ref) that loads .grd files lazily from disk.

`GRDarray`s are always 3 dimensional, and have [`Lat`](@ref), [`Lon`](@ref) and
[`Band`](@ref) dimensions.

## Arguments

- `filename`: `String` pointing to a grd file. Extension is optional.

## Keyword Arguments

- `usercrs`: CRS format like `EPSG(4326)` used in `Selectors` like `Between` and `At`, and
  for plotting. Can be any CRS `GeoFormat` from GeoFormatTypes.jl, like `WellKnownText`.
- `name`: `String` name for the array, taken from the files `layername` attribute unless passed in.
- `dims`: `Tuple` of `Dimension`s for the array. Detected automatically, but can be passed in.
- `refdims`: `Tuple of` position `Dimension`s the array was sliced from.
- `missingval`: Value reprsenting missing values. Detected automatically when possible, but
  can be passed it.
- `metadata`: [`Metadata`](@ref) object for the array. Detected automatically as
  [`GRDarrayMetadata`](@ref), but can be passed in.

## Example

```julia
A = GRDarray("folder/file.grd"; usercrs=EPSG(4326))
# Select Australia using lat/lon coords, whatever the crs is underneath.
A[Lat(Between(-10, -43), Lon(Between(113, 153)))
```
"""
struct GRDarray{T,N,A,D<:Tuple,R<:Tuple,Na<:Symbol,Me,Mi,S
               } <: DiskGeoArray{T,N,D,LazyArray{T,N}}
    filename::A
    dims::D
    refdims::R
    name::Na
    metadata::Me
    missingval::Mi
    size::S
end
GRDarray(filename::String; kwargs...) =
    GRDarray(GRDattrib(filename), filename; kwargs...)
GRDarray(grd::GRDattrib, filename, key=nothing;
         usercrs=nothing,
         dims=dims(grd, usercrs),
         refdims=(),
         name=name(grd),
         missingval=missingval(grd),
         metadata=metadata(grd),
        ) = begin

    size_ = map(length, dims)

    T = eltype(grd)
    N = length(size_)
    name = Symbol(name)
    GRDarray{T,N,typeof.((filename,dims,refdims,name,metadata,missingval,size_))...
            }(filename, dims, refdims, name, metadata, missingval, size_)
end

# Base methods

"""
    Base.write(filename::AbstractString, ::Type{GRDarray}, s::AbstractGeoArray)

Write a [`GRDarray`](@ref) to a .grd file, with a .gri header file. The extension of
`filename` will be ignored.

Currently the `metadata` field is lost on `write` for `GRDarray`.

Returns `filename`.
"""
Base.write(filename::String, ::Type{<:GRDarray}, A::AbstractGeoArray) = begin
    if hasdim(A, Band)
        correctedA = permutedims(A, (Lon, Lat, Band)) |>
            a -> reorder(a, GRD_INDEX_ORDER) |>
            a -> reorder(a, (Lon(GRD_LON_RELATION), Lat(GRD_LAT_RELATION), Band(GRD_BAND_RELATION)))
        checkarrayorder(correctedA, (GRD_LON_ARRAY, GRD_LAT_ARRAY, GRD_BAND_ARRAY))
        nbands = length(val(dims(correctedA, Band)))
    else
        correctedA = permutedims(A, (Lon, Lat)) |>
            a -> reorder(a, GRD_INDEX_ORDER) |>
            a -> reorder(a, (Lon(GRD_LON_RELATION), Lat(GRD_LAT_RELATION)))
            checkarrayorder(correctedA, (GRD_LON_ARRAY, GRD_LAT_ARRAY))
        nbands = 1
    end
    # Remove extension
    filename = splitext(filename)[1]

    lon, lat = map(dims(A, (Lon(), Lat()))) do d
        convertmode(Projected, d)
    end
    ncols, nrows = size(A)
    xmin, xmax = bounds(lon)
    ymin, ymax = bounds(lat)
    proj = convert(String, convert(ProjString, crs(lon)))
    datatype = rev_grd_datatype_translation[eltype(A)]
    nodatavalue = missingval(A)
    minvalue = minimum(filter(x -> x !== missingval(A), data(A)))
    maxvalue = maximum(filter(x -> x !== missingval(A), data(A)))

    # Data: gri file
    open(filename * ".gri", "w") do IO
        write(IO, data(correctedA))
    end

    # Metadata: grd file
    open(filename * ".grd", "w") do IO
        write(IO,
            """
            [general]
            creator=GeoData.jl
            created= $(string(now()))
            [georeference]
            nrows= $nrows
            ncols= $ncols
            xmin= $xmin
            ymin= $ymin
            xmax= $xmax
            ymax= $ymax
            projection= $proj
            [data]
            datatype= $datatype
            nodatavalue= $nodatavalue
            byteorder= little
            nbands= $nbands
            minvalue= $minvalue
            maxvalue= $maxvalue
            [description]
            layername= $(name(A))
            """
        )
    end
    return filename
end

# AbstractGeoStack methods

"""
    GRDstack(filenames; keys, kwargs...)
    GRDstack(filenames...; keys, kwargs...)
    GRDstack(filenames::NamedTuple;
             window=(),
             metadata=nothing,
             childkwargs=(),
             refdims=())

Convenience method to create a DiskStack of [`GRDarray`](@ref) from `filenames`.

## Arguments

- `filenames`: A NamedTuple of stack keys and `String` filenames, or a `Tuple`,
  `Vector` or splatted arguments of `String` filenames.

## Keyword arguments

- `keys`: Used as stack keys when a `Tuple`, `Vector` or splat of filenames are passed in.
- `window`: A `Tuple` of `Dimension`/`Selector`/indices that will be applied to the
  contained arrays when they are accessed.
- `metadata`: Metadata as a [`StackMetadata`](@ref) object.
- `childkwargs`: A `NamedTuple` of keyword arguments to pass to the `childtype` constructor.
- `refdims`: `Tuple` of  position `Dimension` the array was sliced from.

## Example

Create a `GRDstack` from four files, that sets the child arrays
`usercrs` value when they are loaded.

```julia
files = (:temp="temp.tif", :pressure="pressure.tif", :relhum="relhum.tif")
stack = GRDstack(files; childkwargs=(usercrs=EPSG(4326),))
stack[:relhum][Lat(Contains(-37), Lon(Contains(144))
```
"""
GRDstack(args...; kwargs...) =
    DiskStack(args...; childtype=GRDarray, kwargs...)

withsource(f, ::Type{<:GRDarray}, filename::AbstractString, key...) =
    f(GRDattrib(filename))
withsourcedata(f, ::Type{<:GRDarray}, filename::AbstractString, key...) =
    mmapgrd(f, GRDattrib(filename))
withsourcedata(f, A::GRDarray, key...) =
    mmapgrd(f, A)

# Utils ########################################################################
mmapgrd(f, grd::Union{GRDarray,GRDattrib}) =
    mmapgrd(f, filename(grd), eltype(grd), size(grd))
mmapgrd(f, filename::AbstractString, T::Type, size::Tuple) = begin
    open(filename * ".gri", "r") do io
        mmap = Mmap.mmap(io, Array{T,length(size)}, size)
        output = f(mmap)
        close(io)
        output
    end
end

const grd_datatype_translation = Dict{String, DataType}(
    "LOG1S" => Bool,
    "INT1S" => Int8,
    "INT2S" => Int16,
    "INT4S" => Int32,
    "INT8S" => Int64,
    "INT1U" => UInt8,
    "INT2U" => UInt16,
    "FLT4S" => Float32,
    "FLT8S" => Float64
)

const rev_grd_datatype_translation =
    Dict{DataType, String}(v => k for (k,v) in grd_datatype_translation)


text:
# Benchmarking samplers

using BenchmarkTools
using Distributions

import Random

if haskey(ENV, "allsamplers") || haskey(ENV, "CI")
    for s in ["categorical", "binomial", "poisson", "exponential", "gamma"]
        ENV[s] = ""
    end
end

import Distributions: AliasTable, CategoricalDirectSampler

make_sampler(::Type{<:CategoricalDirectSampler}, k::Integer) = CategoricalDirectSampler(fill(1/k, k))
make_sampler(::Type{<:AliasTable}, k::Integer) = AliasTable(fill(1/k, k))

if haskey(ENV, "categorical") && ENV["categorical"] != "skip"
    @info "Categorical"

    for ST in [CategoricalDirectSampler, AliasTable]
        mt = Random.MersenneTwister(33)
        @info string(ST)
        ks = haskey(ENV, "CI") ? 2 .^ (1:3) : 2 .^ (1:12)
        for k in ks
            s = make_sampler(ST, k)
            b = @benchmark rand($mt, $s)
            @info "k: $k, result: $b"
        end
    end
end

if haskey(ENV, "binomial") && ENV["binomial"] != "skip"
    @info "Binomial"

    import Distributions: BinomialAliasSampler, BinomialGeomSampler, BinomialTPESampler, BinomialPolySampler

    for ST in [BinomialAliasSampler,
                   BinomialGeomSampler,
                   BinomialTPESampler,
                   BinomialPolySampler]
        mt = Random.MersenneTwister(33)
        @info string(ST)
        nvals = haskey(ENV, "CI") ? [2] : 2 .^ (1:12)
        pvals = haskey(ENV, "CI") ? [0.3] : [0.3, 0.5, 0.9]
        for n in nvals, p in pvals
            s = ST(n, p)
            b = @benchmark rand($mt, $s)
            @info "(n,p): $((n,p)), result: $b"
        end
    end
end

if haskey(ENV, "poisson") && ENV["poisson"] != "skip"
    @info "Poisson samplers"

    import Distributions: PoissonCountSampler, PoissonADSampler

    for ST in [PoissonCountSampler, PoissonADSampler]
        @info string(ST)
        mt = Random.MersenneTwister(33)
        µs = haskey(ENV, "CI") ? [5.0] : [5.0, 10.0, 15.0, 20.0, 30.0]
        for μ in µs
            s = ST(µ)
            b = @benchmark rand($mt, $s)
            @info "µ: $µ, result: $b"
        end
    end
end

if haskey(ENV, "exponential") && ENV["exponential"] != "skip"
    @info "Exponential"

    import Distributions: ExponentialSampler, ExponentialLogUSampler

    for ST in (ExponentialSampler, ExponentialLogUSampler)
        @info string(ST)
        mt = Random.MersenneTwister(33)
        scale_values = haskey(ENV, "CI") ? [10.0] : [10.0, 15.0, 20.0, 30.0]
        for scale in scale_values
            s = ST(scale)
            b = @benchmark rand($mt, $s)
            @info "scale: $scale, result: $b"
        end
    end
end

if haskey(ENV, "gamma") && ENV["gamma"] != "skip"
    @info "Gamma"

    import Distributions: GammaGDSampler, GammaGSSampler, GammaMTSampler, GammaIPSampler
    @info "Low"
    for ST in [GammaGSSampler, GammaIPSampler]
        @info string(ST)
        mt = Random.MersenneTwister(33)
        αs = haskey(ENV, "CI") ? [0.1] : [0.1, 0.5, 0.9]
        for α in αs
            g = Gamma(α, 1.0)
            s = ST(g)
            b = @benchmark rand($mt, $s)
            @info "α: $α, result: $b"
        end
    end
    @info "High"
    for ST in [GammaMTSampler, GammaGDSampler]
        @info string(ST)
        mt = Random.MersenneTwister(33)
        αs = haskey(ENV, "CI") ? [1.5] : [1.5, 2.0, 3.0, 5.0, 20.0]
        for α in αs
            g = Gamma(α, 1.0)
            s = ST(g)
            b = @benchmark rand($mt, $s)
            @info "α: $α, result: $b"
        end
    end
end


text:
using DynamicPPL
using Distributions
using AbstractMCMC: sample

using Random
using Statistics
using Test

Random.seed!(100)

@testset "AbstractMCMC interface" begin
    @model gdemo(x, y) = begin
        s ~ InverseGamma(2, 3)
        m ~ Normal(2.0, sqrt(s))
        x ~ Normal(m, sqrt(s))
        y ~ Normal(m, sqrt(s))
    end

    model = gdemo(1.0, 2.0)
    N = 1_000

    chains = sample(model, SampleFromPrior(), N; progress = false)
    @test chains isa Vector{<:VarInfo}
    @test length(chains) == N

    # Expected value of ``X`` where ``X ~ N(2, ...)`` is 2.
    @test mean(vi[@varname(m)] for vi in chains) ≈ 2 atol = 0.1

    # Expected value of ``X`` where ``X ~ IG(2, 3)`` is 3.
    @test mean(vi[@varname(s)] for vi in chains) ≈ 3 atol = 0.1

    chains = sample(model, SampleFromUniform(), N; progress = false)
    @test chains isa Vector{<:VarInfo}
    @test length(chains) == N

    # Expected value of ``X`` where ``X ~ U[-2, 2]`` is ≈ 0.
    @test mean(vi[@varname(m)] for vi in chains) ≈ 0 atol = 0.1

    # Expected value of ``exp(X)`` where ``X ~ U[-2, 2]`` is ≈ 1.8.
    @test mean(vi[@varname(s)] for vi in chains) ≈ 1.8 atol = 0.1
end


text:
module CustomNPC

using ..Ahorn, Maple

CustomExudiasNPC(x::Integer, y::Integer) = Maple.Entity("everest/npc", x=x, y=y)

placements = Dict{String, Ahorn.EntityPlacement}(
    "Custom Exudias NPC" => Ahorn.EntityPlacement(
        CustomExudiasNPC,
        "point",
        Dict{String, Any}(
            "sprite" => "",
            "spriteRate" => 1,
            "dialogId" => "",
            "onlyOnce" => true,
            "endLevel" => false,
            "flipX" => false,
            "flipY" => false,
            "approachWhenTalking" => false,
            "approachDistance" => 16,
            "indicatorOffsetX" => 0,
            "indicatorOffsetY" => 0
        )
    )
)

function selection(entity::Maple.Entity)
    if entity.name == "everest/npc"
        x, y = Ahorn.entityTranslation(entity)

        res = Ahorn.Rectangle[Ahorn.Rectangle(x - 8, y -16, 16, 16)]

        return true, res
    end
end

borderMultiplier = (0.9, 0.9, 0.9, 1)

function render(ctx::Ahorn.Cairo.CairoContext, entity::Maple.Entity, room::Maple.Room)
    if entity.name == "everest/npc"
        Ahorn.drawSprite(ctx, "characters/oldlady/idle00.png", 0, -16)
        return true
    end
end

end

max_stars_repo_name: thefoxbot/MoreEntities

max_issues_repo_name: thefoxbot/MoreEntities

max_forks_repo_name: thefoxbot/MoreEntities


text:
using GatedLinearNetworks
using Dapple

batch_dim = 200

x = 10f0 * rand(Float32, (1, batch_dim))

y = Array{Float32}(undef, (2, batch_dim))
for j in 1:batch_dim
    y[1,j] = x[1,j] * cos(x[1,j]) + 4f-1 * randn(Float32)
    y[2,j] = x[1,j] * sin(x[1,j]) + 4f-1 * randn(Float32)
end

ggln = GatedLinearNetworks.GaussianGGLN(1, 2, 2, 64, 16)

ntrain_steps = 400
for step_num in 1:ntrain_steps
    @show step_num
    train!(ggln, x, y)
end

nsteps = 200
xsteps = reshape(
    collect(Float32, range(minimum(x), maximum(x), length=nsteps)),
    (1, nsteps))
μ, σ2 = predict(ggln, xsteps)

pl = plot(
    points(x=y[1,:], y=y[2,:], color=x[1,:]),
    lines(x=μ[1,:], y=μ[2,:]))
pl |> SVG("example.svg", 4inch, 4inch)


text:
import .FFMPEG, .FileIO

function combine_images(path, delay)
    fr = 1 / delay
    files = [joinpath(path,file) for file in readdir(path) if endswith(file, ".svg")]
    foreach(file->run(`convert $file $(splitext(file)[1]*".png")`), files)
    foreach(rm, files)
    FFMPEG.exe("-r", "$fr",
               "-f", "image2",
               "-s", "1920x1080",
               "-i", "$path/%d.png",
               "-vcodec", "libx264",
               "-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2",
               "-crf", "25",
               "-pix_fmt", "yuv420p",
               "$path/final.mp4")
end

function combine_gantt_images(ctx, svg_path::String, prof_path::String, delay)
    combine_images(svg_path, delay)
    ctx.profile && combine_images(prof_path, delay)
    return (gantt="$svg_path/final.mp4",
            profile="$prof_path/final.mp4")
end


text:
const INLINE_RESULT_LENGTH = 100
const MAX_RESULT_LENGTH = 10_000

"""
    render(x)

Produce a representation of `x` that can be displayed by a UI. Must return a dictionary with
the following fields:
- `inline`: Short one-line plain text representation of `x`. Typically limited to `INLINE_RESULT_LENGTH` characters.
- `all`: Plain text string (that may contain linebreaks and other signficant whitespace) to further describe `x`.
- `iserr`: Boolean. The frontend may style the UI differently depending on this value.
"""
function render(x)
    str = sprintlimited(MIME"text/plain"(), x, limit=MAX_RESULT_LENGTH)

    return ReplRunCodeRequestReturn(
        strlimit(first(split(str, "\n")), limit=INLINE_RESULT_LENGTH),
        str,
        false
    )
end

function render(::Nothing)
    return ReplRunCodeRequestReturn(
        "✓",
        "nothing",
        false
    )
end

struct EvalError
    err
    bt
end

function render(err::EvalError)
    str = sprintlimited(err.err, err.bt, func=Base.display_error, limit=MAX_RESULT_LENGTH)

    return ReplRunCodeRequestReturn(
        strlimit(first(split(str, "\n")), limit=INLINE_RESULT_LENGTH),
        str,
        true
    )
end
"""
    safe_render(x)

Calls `render`, but catches errors in the display system.
"""
function safe_render(x)
    try
        return render(x)
    catch err
        out = render(EvalError(err, catch_backtrace()))

        return ReplRunCodeRequestReturn(
            string("Display Error: ", out.inline),
            string("Display Error: ", out.all),
            out.iserr
        )
    end
end

max_stars_repo_name: fonsp/julia-vscode

max_issues_repo_name: fonsp/julia-vscode

max_forks_repo_name: fonsp/julia-vscode


text:
function initrepl(repl)
  @eval ENV["JULIA_EDITOR"] = "nvim"

  # packages
  @eval using LinearAlgebra
  @eval using SymPy
  @eval using Latexify

  # convert number to hex string
  @eval function hex(n :: Number) :: String
          string(n, base=16)
        end

  # convert number to binary string
  @eval function bin(n :: Number) :: String
          string(n, base=2)
        end

  # convert number to decimal string
  @eval function dec(n :: Number) :: String
          string(n, base=10)
        end

  # --- and some common functions such as rotation matrix, etc.
end

atreplinit(initrepl)


text:
__precompile__()

module LibSndFile

using Compat
# we should be able to remove UTF8String and rename to String when we drop 0.4 support
import Compat: UTF8String, view

using SampledSignals
import SampledSignals: nchannels, nframes, samplerate, unsafe_read!, unsafe_write
using FileIO
import FileIO: load, save
using FixedPointNumbers
using SIUnits

# TODO: move these into FileIO.jl
export loadstream, savestream

# Re-export from FileIO
export load, save

typealias PCM16Sample Fixed{Int16, 15}
typealias PCM32Sample Fixed{Int32, 31}

function __init__()
    # this needs to be run when the module is loaded at run-time, even if
    # the module is precompiled.
    del_format(format"WAV")
    add_format(format"WAV", detectwav, ".wav", [:LibSndFile])
    del_format(format"FLAC")
    add_format(format"FLAC", "fLaC", ".flac", [:LibSndFile])
    add_format(format"OGG", "OggS", [".ogg", ".oga"], [:LibSndFile])
end

include("formats.jl")
include(Pkg.dir("LibSndFile", "deps", "deps.jl"))

# const SF_SEEK_SET = 0
# const SF_SEEK_CUR = 1
# const SF_SEEK_END = 2

const SFM_READ = Int32(0x10)
const SFM_WRITE = Int32(0x20)

formatcode(::Type{format"WAV"}) = SF_FORMAT_WAV
formatcode(::Type{format"FLAC"}) = SF_FORMAT_FLAC
formatcode(::Type{format"OGG"}) = SF_FORMAT_OGG

subformatcode(::Type{PCM16Sample}) = SF_FORMAT_PCM_16
subformatcode(::Type{PCM32Sample}) = SF_FORMAT_PCM_32
subformatcode(::Type{Float32}) = SF_FORMAT_FLOAT
subformatcode(::Type{Float64}) = SF_FORMAT_DOUBLE

# WAV is a subtype of RIFF, as is AVI
function detectwav(io)
    seekstart(io)
    magic = UTF8String(read(io, UInt8, 4))
    magic == "RIFF" || return false
    seek(io, 8)
    submagic = UTF8String(read(io, UInt8, 4))

    submagic == "WAVE"
end

"""Take a LibSndFile format code and return a suitable sample type"""
function fmt_to_type(fmt)
    mapping = Dict{UInt32, Type}(
        SF_FORMAT_PCM_S8 => PCM16Sample,
        SF_FORMAT_PCM_U8 => PCM16Sample,
        SF_FORMAT_PCM_16 => PCM16Sample,
        SF_FORMAT_PCM_24 => PCM32Sample,
        SF_FORMAT_PCM_32 => PCM32Sample,
        SF_FORMAT_FLOAT => Float32,
        SF_FORMAT_DOUBLE => Float64,
        SF_FORMAT_VORBIS => Float32,
    )

    masked = fmt & SF_FORMAT_SUBMASK
    masked in keys(mapping) || error("Format code $masked not recognized by LibSndFile.jl")

    mapping[masked]
end

type SF_INFO
    frames::Int64
    samplerate::Int32
    channels::Int32
    format::Int32
    sections::Int32
    seekable::Int32
end

type SndFileSink{T} <: SampleSink
    path::UTF8String
    filePtr::Ptr{Void}
    sfinfo::SF_INFO
    nframes::Int64
    writebuf::Array{T, 2}
end

function SndFileSink(path, filePtr, sfinfo, bufsize=4096)
    T = fmt_to_type(sfinfo.format)
    writebuf = Array(T, sfinfo.channels, bufsize)
    SndFileSink(path, filePtr, sfinfo, 0, writebuf)
end

nchannels(sink::SndFileSink) = Int(sink.sfinfo.channels)
samplerate(sink::SndFileSink) = quantity(Int, Hz)(sink.sfinfo.samplerate)
nframes(sink::SndFileSink) = sink.nframes
Base.eltype(sink::SndFileSink) = fmt_to_type(sink.sfinfo.format)

type SndFileSource{T} <: SampleSource
    path::UTF8String
    filePtr::Ptr{Void}
    sfinfo::SF_INFO
    pos::Int64
    readbuf::Array{T, 2}
end

function SndFileSource(path, filePtr, sfinfo, bufsize=4096)
    T = fmt_to_type(sfinfo.format)
    readbuf = Array(T, sfinfo.channels, bufsize)

    SndFileSource(path, filePtr, sfinfo, 1, readbuf)
end

nchannels(source::SndFileSource) = Int(source.sfinfo.channels)
samplerate(source::SndFileSource) = quantity(Int, Hz)(source.sfinfo.samplerate)
nframes(source::SndFileSource) = source.sfinfo.frames
Base.eltype{T}(source::SndFileSource{T}) = T

function Base.show(io::IO, s::Union{SndFileSource, SndFileSink})
    println(io, typeof(s))
    println(io, "  path: \"$(s.path)\"")
    println(io, "  channels: ", nchannels(s))
    println(io, "  samplerate: ", samplerate(s))
    # SndFileSinks don't have a position and we're always at the end
    pos = isa(s, SndFileSource) ? s.pos-1 : nframes(s)
    postime = float((pos)/samplerate(s))
    endtime = float((nframes(s))/samplerate(s))
    println(io, "  position: $(pos) of $(nframes(s)) frames")
    @printf(io, "            %0.2f of %0.2f seconds", postime, endtime)
end

loadstream(path::AbstractString, args...; kwargs...) =
    loadstream(query(path), args...; kwargs...)

function loadstream(f::Function, args...)
    str = loadstream(args...)
    try
        f(str)
    finally
        close(str)
    end
end

function loadstream(path::File)
    sfinfo = SF_INFO(0, 0, 0, 0, 0, 0)

    filePtr = ccall((:sf_open, libsndfile), Ptr{Void},
                    (Ptr{UInt8}, Int32, Ptr{SF_INFO}),
                    filename(path), SFM_READ, &sfinfo)

    if filePtr == C_NULL
        errmsg = ccall((:sf_strerror, libsndfile), Ptr{UInt8}, (Ptr{Void},), filePtr)
        error("LibSndFile.jl error while loading $path: ", unsafe_string(errmsg))
    end

    SndFileSource(filename(path), filePtr, sfinfo)
end

function Base.close(s::SndFileSource)
    err = ccall((:sf_close, libsndfile), Int32, (Ptr{Void},), s.filePtr)
    if err != 0
        error("LibSndFile.jl error: Failed to close file")
    end
end

function unsafe_read!(source::SndFileSource, buf::Array, frameoffset, framecount)
    total = min(framecount, nframes(source) - source.pos + 1)
    nread = 0
    readbuf = source.readbuf
    while nread < total
        n = min(size(readbuf, 2), total - nread)
        nr = sf_readf(source.filePtr, readbuf, n)
        # the data comes in interleaved, so we need to transpose
        transpose!(view(buf, (1:nr)+frameoffset+nread, :), view(readbuf, :, 1:nr))
        source.pos += nr
        nread += nr
        nr == n || break
    end

    nread
end

function Base.readall(str::SndFileSource)
    read(str, nframes(str) - str.pos + 1)
end

function load(path::File)
    str = loadstream(path)
    buf = try
        readall(str)
    finally
        close(str)
    end

    buf
end

savestream(path::AbstractString, args...; kwargs...) =
    savestream(query(path), args...; kwargs...)

function savestream(f::Function, args...)
    stream = savestream(args...)
    try
        f(stream)
    finally
        close(stream)
    end
end

# if the samplerate is given in Hz, strip it off
function savestream{SRT}(path::File, nchannels, samplerate::quantity(SRT, Hz), elemtype)
    savestream(path, nchannels, samplerate/Hz, elemtype)
end

function savestream{T}(path::File{T}, nchannels, samplerate, elemtype)
    sfinfo = SF_INFO(0, 0, 0, 0, 0, 0)

    sfinfo.samplerate = samplerate
    sfinfo.channels = nchannels
    sfinfo.format = formatcode(T)
    # TODO: should we auto-convert 32-bit integer samples to 24-bit?
    if T == format"FLAC" && elemtype != PCM16Sample
        error("LibSndFile.jl: FLAC only supports 16-bit integer samples")
    end
    if T == format"OGG"
        sfinfo.format |= SF_FORMAT_VORBIS
    else
        sfinfo.format |= subformatcode(elemtype)
    end

    filePtr = ccall((:sf_open, libsndfile), Ptr{Void},
                    (Ptr{UInt8}, Int32, Ptr{SF_INFO}),
                    filename(path), SFM_WRITE, &sfinfo)

    if filePtr == C_NULL
        errmsg = ccall((:sf_strerror, libsndfile), Ptr{UInt8}, (Ptr{Void},), filePtr)
        error("LibSndFile.jl error while saving $path: ", unsafe_string(errmsg))
    end

    SndFileSink(filename(path), filePtr, sfinfo)
end

# returns the number of samples written
function unsafe_write(sink::SndFileSink, buf::Array, frameoffset, framecount)
    nwritten = 0
    writebuf = sink.writebuf
    while nwritten < framecount
        n = min(size(writebuf, 2), framecount - nwritten)
        # the data needs to be interleaved, so we need to transpose
        transpose!(view(writebuf, :, 1:n), view(buf, (1:n)+frameoffset+nwritten, :))
        nw = sf_writef(sink.filePtr, writebuf, n)
        sink.nframes += nw
        nwritten += nw
        nw == n || break
    end

    nwritten
end

function Base.close(str::SndFileSink)
    err = ccall((:sf_close, libsndfile), Int32, (Ptr{Void},), str.filePtr)
    if err != 0
        error("LibSndFile.jl error while saving $path: Failed to close file")
    end
end

function save{T}(path::File{T}, buf::SampleBuf)
    sfinfo = SF_INFO(0, 0, 0, 0, 0, 0)
    stream = savestream(path, nchannels(buf), samplerate(buf), eltype(buf))

    try
        frameswritten = write(stream, buf)
        if frameswritten != nframes(buf)
            error("Only wrote $frameswritten frames, expected $(nframes(buf))")
        end
    finally
        # make sure we close the file even if something goes wrong
        close(stream)
    end

    nothing
end

"""
Wrappers for the family of sf_readf_* functions, which read the given number
of frames into the given array. Returns the number of frames read.
"""
function sf_readf end

sf_readf{T <: Union{Int16, PCM16Sample}}(filePtr, dest::Array{T}, nframes) =
    ccall((:sf_readf_short, libsndfile), Int64,
        (Ptr{Void}, Ptr{T}, Int64),
        filePtr, dest, nframes)

sf_readf{T <: Union{Int32, PCM32Sample}}(filePtr, dest::Array{T}, nframes) =
    ccall((:sf_readf_int, libsndfile), Int64,
        (Ptr{Void}, Ptr{T}, Int64),
        filePtr, dest, nframes)

sf_readf(filePtr, dest::Array{Float32}, nframes) =
    ccall((:sf_readf_float, libsndfile), Int64,
        (Ptr{Void}, Ptr{Float32}, Int64),
        filePtr, dest, nframes)

sf_readf(filePtr, dest::Array{Float64}, nframes) =
    ccall((:sf_readf_double, libsndfile), Int64,
        (Ptr{Void}, Ptr{Float64}, Int64),
        filePtr, dest, nframes)

"""
Wrappers for the family of sf_writef_* functions, which write the given number
of frames into the given array. Returns the number of frames written.
"""
function sf_writef end

sf_writef{T <: Union{Int16, PCM16Sample}}(filePtr, src::Array{T}, nframes) =
    ccall((:sf_writef_short, libsndfile), Int64,
                (Ptr{Void}, Ptr{T}, Int64),
                filePtr, src, nframes)

sf_writef{T <: Union{Int32, PCM32Sample}}(filePtr, src::Array{T}, nframes) =
    ccall((:sf_writef_int, libsndfile), Int64,
                (Ptr{Void}, Ptr{T}, Int64),
                filePtr, src, nframes)

sf_writef(filePtr, src::Array{Float32}, nframes) =
    ccall((:sf_writef_float, libsndfile), Int64,
                (Ptr{Void}, Ptr{Float32}, Int64),
                filePtr, src, nframes)

sf_writef(filePtr, src::Array{Float64}, nframes) =
    ccall((:sf_writef_double, libsndfile), Int64,
                (Ptr{Void}, Ptr{Float64}, Int64),
                filePtr, src, nframes)

#
# function Base.seek(file::AudioFile, offset::Integer, whence::Integer)
#     new_offset = ccall((:sf_seek, libsndfile), Int64,
#         (Ptr{Void}, Int64, Int32), file.filePtr, offset, whence)
#
#     if new_offset < 0
#         error("Could not seek to $(offset) in file")
#     end
#
#     new_offset
# end
#
# # Some convenience methods for easily navigating through a sound file
# Base.seek(file::AudioFile, offset::Integer) = seek(file, offset, SF_SEEK_SET)
# rewind(file::AudioFile) = seek(file, 0, SF_SEEK_SET)

end # module LibSndFile


text:
using Test
using Cleaner:
    materializer,
    compact_table_ROT,
    compact_columns_ROT,
    compact_rows_ROT,
    delete_const_columns_ROT,
    polish_names_ROT,
    reinfer_schema_ROT,
    row_as_names_ROT,
    rename_ROT,
    drop_missing_ROT,
    add_index_ROT
using DataFrames: DataFrame

@testset "ROT functions are working as expected" begin
    testRM1 = DataFrame(;
        A=[missing, missing, missing], B=[1, missing, 3], C=["x", "", "z"]
    )

    @test compact_columns_ROT(testRM1) isa DataFrame
    @test compact_rows_ROT(testRM1) isa DataFrame
    @test compact_table_ROT(testRM1) isa DataFrame
    @test materializer(testRM1)((a=[1], b=[2])) isa DataFrame

    let testDF = DataFrame(; A=[1, 1, 1], B=[4, 5, 6], C=String["2", "2", "2"])
        @test delete_const_columns_ROT(testDF) isa DataFrame
    end

    let testDF = DataFrame(
            "  _aName with_loTsOfProblems" => [1, 2, 3],
            "  _aName with_loTsOfProblems1" => [4, 5, 6],
            "  _aName with_loTsOfProblems2" => [7, 8, 9],
        )
        @test polish_names_ROT(testDF) isa DataFrame
    end

    let testDF = DataFrame(; A=[1, 2, 3], B=Any[4, missing, "z"], C=Any["5", "6", "9"])
        @test reinfer_schema_ROT(testDF) isa DataFrame
    end

    let testDF = DataFrame(; A=[1, 2, "x", 4], B=[5, 6, "y", 7], C=["x", "y", "z", "a"])
        @test row_as_names_ROT(testDF, 3) isa DataFrame
    end

    let testDF = DataFrame(; A=[1, 2, "x", 4], B=[5, 6, "y", 7], C=["x", "y", "z", "a"])
        @test rename_ROT(testDF, [:a, :b, :c]) isa DataFrame
    end

    let testDF = DataFrame(; A=[1, 2, "x", 4], B=[5, 6, "y", 7], C=["x", "y", "z", "a"])
        @test drop_missing_ROT(testDF) isa DataFrame
    end

    let testDF = DataFrame(; A=[4, 5, 6])
        @test add_index_ROT(testDF) isa DataFrame
    end
end

max_stars_repo_name: TheRoniOne/Cleaner

max_issues_repo_name: TheRoniOne/Cleaner

max_forks_repo_name: TheRoniOne/Cleaner


text:
struct PsisLooComparison
    psis::Vector{PsisLoo}
    df::DataFrame
end

function psis_loo_compare(models::Vector{SampleModel};
    loglikelihood_name="log_lik",
    model_names=nothing,
    sort_models=true)

    nmodels = length(models)
    mnames = [models[i].name for i in 1:nmodels]

    chains_vec = read_samples.(models, :namedtuple)
    ll_vec = Array.(matrix.(chains_vec, loglikelihood_name))
    ll_vecp = map(to_paretosmooth, ll_vec)
    psis_vec = psis_loo.(ll_vecp)

    psis_values = Vector{Float64}(undef, nmodels)
    se_values = Vector{Float64}(undef, nmodels)
    loos = Vector{Vector{Float64}}(undef, nmodels)

    for i in 1:nmodels
        psis_values[i] = psis_vec[i].estimates(:cv_elpd, :total)
        se_values[i] = psis_vec[i].estimates(:cv_elpd, :se_total)
        loos[i] = psis_vec[i].pointwise(:cv_elpd)
    end

    if sort_models
        ind = sortperm([psis_values[i][1] for i in 1:nmodels]; rev=true)
        psis_vec = psis_vec[ind]
        psis_values = psis_values[ind]
        se_values = se_values[ind]
        loos = loos[ind]
        mnames = mnames[ind]
    end

    # Setup comparison vectors

    elpd_diff = zeros(nmodels)
    se_diff = zeros(nmodels)
    weight = ones(nmodels)

    # Compute comparison values

    for i in 2:nmodels
        elpd_diff[i] = psis_values[i] - psis_values[1]
        diff = loos[1] - loos[i]
        se_diff[i] = √(length(loos[i]) * var(diff; corrected=false))
    end
    data = elpd_diff
    data = hcat(data, se_diff)

    sumval = sum([exp(psis_values[i]) for i in 1:nmodels])
    @. weight = exp(psis_values) / sumval
    data = hcat(data, weight)

    # Create DataFrame object for display

    df = DataFrame()

    if length(mnames) > 0
        df.models = String.(mnames)
    end

    df.PSIS = round.(-2 .* [psis_values[i] for i in 1:nmodels], digits=1)
    df.lppd = zeros(nmodels)
    df.SE = se_diff

    dloo = zeros(nmodels)
    for i in 2:nmodels
        dloo[i] = df[i, :PSIS] - df[1, :PSIS]
    end
    df.dPSIS = round.(dloo, digits=1)

    dse = zeros(nmodels)
    for i in 2:nmodels
        diff = 2(loos[1] .- loos[i])
        dse[i] = √(length(loos[i]) * var(diff; corrected=false))
    end
    df.dSE = round.(dse, digits=2)

    ps = zeros(nmodels)
    for i in 1:nmodels
    end
    df.pPSIS = ps

    # Return PsisLooCompare object

    PsisLooComparison(psis_vec, df)

end


text:
"""
    DeployConfig

Abstract type which new deployment configs should be subtypes of.
"""
abstract type DeployConfig end

"""
    Documenter.documenter_key(cfg::DeployConfig)

Return the Base64-encoded SSH private key for the repository.
Defaults to reading the `DOCUMENTER_KEY` environment variable.

This method must be supported by configs that push with SSH, see
[`Documenter.authentication_method`](@ref).
"""
function documenter_key(::DeployConfig)
    return ENV["DOCUMENTER_KEY"]
end

"""
    Documenter.deploy_folder(cfg::DeployConfig; repo, devbranch, push_preview, devurl, kwargs...)

Return the folder where the documentation should be deployed to, or `nothing`
if the current build should not deploy.
This function is called with the `repo`, `devbranch`, `push_preview` and `devurl`
arguments from [`deploydocs`](@ref).

!!! note
    Implementations of this functions should accept trailing `kwargs...` for
    compatibility with future Documenter releases which may pass additional
    keyword arguments.
"""
deploy_folder(::DeployConfig; kwargs...) = nothing
deploy_folder(::Nothing; kwargs...) = nothing # when auto-detection fails

@enum AuthenticationMethod SSH HTTPS

"""
    Documenter.authentication_method(::DeployConfig)

Return enum instance `SSH` or `HTTPS` depending on push method to be used.

Configs returning `SSH` should support [`Documenter.documenter_key`](@ref).
Configs returning `HTTPS` should support [`Documenter.authenticated_repo_url`](@ref).
"""
authentication_method(::DeployConfig) = SSH

"""
    Documenter.authenticated_repo_url(cfg::DeployConfig)

Return an authenticated URL to the upstream repository.

This method must be supported by configs that push with HTTPS, see
[`Documenter.authentication_method`](@ref).
"""
function authenticated_repo_url end

marker(x) = x ? "✔" : "✘"

#############
# Travis CI #
#############

"""
    Travis <: DeployConfig

Default implementation of `DeployConfig`.

The following environment variables influences the build
when using the `Travis` configuration:

 - `DOCUMENTER_KEY`: must contain the Base64-encoded SSH private key for the repository.
   This variable should be set in the Travis settings for the repository. Make sure this
   variable is marked **NOT** to be displayed in the build log.

 - `TRAVIS_PULL_REQUEST`: must be set to `false`.
   This avoids deployment on pull request builds.

 - `TRAVIS_REPO_SLUG`: must match the value of the `repo` keyword to [`deploydocs`](@ref).

 - `TRAVIS_EVENT_TYPE`: may not be set to `cron`. This avoids re-deployment of existing
   docs on builds that were triggered by a Travis cron job.

 - `TRAVIS_BRANCH`: unless `TRAVIS_TAG` is non-empty, this must have the same value as
   the `devbranch` keyword to [`deploydocs`](@ref). This makes sure that only the
   development branch (commonly, the `master` branch) will deploy the "dev" documentation
   (deployed into a directory specified by the `devurl` keyword to [`deploydocs`](@ref)).

 - `TRAVIS_TAG`: if set, a tagged version deployment is performed instead; the value
   must be a valid version number (i.e. match `Base.VERSION_REGEX`). The documentation for
   a package version tag gets deployed to a directory named after the version number in
   `TRAVIS_TAG` instead.

The `TRAVIS_*` variables are set automatically on Travis. More information on how Travis
sets the `TRAVIS_*` variables can be found in the
[Travis documentation](https://docs.travis-ci.com/user/environment-variables/#default-environment-variables).
"""
struct Travis <: DeployConfig
    travis_branch::String
    travis_pull_request::String
    travis_repo_slug::String
    travis_tag::String
    travis_event_type::String
end
function Travis()
    travis_branch       = get(ENV, "TRAVIS_BRANCH",        "")
    travis_pull_request = get(ENV, "TRAVIS_PULL_REQUEST",  "")
    travis_repo_slug    = get(ENV, "TRAVIS_REPO_SLUG",     "")
    travis_tag          = get(ENV, "TRAVIS_TAG",           "")
    travis_event_type   = get(ENV, "TRAVIS_EVENT_TYPE",    "")
    return Travis(travis_branch, travis_pull_request,
        travis_repo_slug, travis_tag, travis_event_type)
end

# Check criteria for deployment
function deploy_folder(cfg::Travis; repo, devbranch, push_preview, devurl, kwargs...)
    io = IOBuffer()
    all_ok = true
    ## Determine build type; release, devbranch or preview
    if cfg.travis_pull_request != "false"
        build_type = :preview
    elseif !isempty(cfg.travis_tag)
        build_type = :release
    else
        build_type = :devbranch
    end
    println(io, "Deployment criteria for deploying $(build_type) build from Travis:")
    ## The deploydocs' repo should match TRAVIS_REPO_SLUG
    repo_ok = occursin(cfg.travis_repo_slug, repo)
    all_ok &= repo_ok
    println(io, "- $(marker(repo_ok)) ENV[\"TRAVIS_REPO_SLUG\"]=\"$(cfg.travis_repo_slug)\" occurs in repo=\"$(repo)\"")
    if build_type === :release
        ## Do not deploy for PRs
        pr_ok = cfg.travis_pull_request == "false"
        println(io, "- $(marker(pr_ok)) ENV[\"TRAVIS_PULL_REQUEST\"]=\"$(cfg.travis_pull_request)\" is \"false\"")
        all_ok &= pr_ok
        ## If a tag exist it should be a valid VersionNumber
        tag_ok = occursin(Base.VERSION_REGEX, cfg.travis_tag)
        all_ok &= tag_ok
        println(io, "- $(marker(tag_ok)) ENV[\"TRAVIS_TAG\"] contains a valid VersionNumber")
        ## Deploy to folder according to the tag
        subfolder = cfg.travis_tag
    elseif build_type === :devbranch
        ## Do not deploy for PRs
        pr_ok = cfg.travis_pull_request == "false"
        println(io, "- $(marker(pr_ok)) ENV[\"TRAVIS_PULL_REQUEST\"]=\"$(cfg.travis_pull_request)\" is \"false\"")
        all_ok &= pr_ok
        ## deploydocs' devbranch should match TRAVIS_BRANCH
        branch_ok = !isempty(cfg.travis_tag) || cfg.travis_branch == devbranch
        all_ok &= branch_ok
        println(io, "- $(marker(branch_ok)) ENV[\"TRAVIS_BRANCH\"] matches devbranch=\"$(devbranch)\"")
        ## Deploy to deploydocs devurl kwarg
        subfolder = devurl
    else # build_type === :preview
        pr_number = tryparse(Int, cfg.travis_pull_request)
        pr_ok = pr_number !== nothing
        all_ok &= pr_ok
        println(io, "- $(marker(pr_ok)) ENV[\"TRAVIS_PULL_REQUEST\"]=\"$(cfg.travis_pull_request)\" is a number")
        btype_ok = push_preview
        all_ok &= btype_ok
        println(io, "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`")
        ## deploy to previews/PR
        subfolder = "previews/PR$(something(pr_number, 0))"
    end
    ## DOCUMENTER_KEY should exist (just check here and extract the value later)
    key_ok = haskey(ENV, "DOCUMENTER_KEY")
    all_ok &= key_ok
    println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists")
    ## Cron jobs should not deploy
    type_ok = cfg.travis_event_type != "cron"
    all_ok &= type_ok
    println(io, "- $(marker(type_ok)) ENV[\"TRAVIS_EVENT_TYPE\"]=\"$(cfg.travis_event_type)\" is not \"cron\"")
    print(io, "Deploying: $(marker(all_ok))")
    @info String(take!(io))
    return all_ok ? subfolder : nothing
end

##################
# GitHub Actions #
##################

"""
    GitHubActions <: DeployConfig

Implementation of `DeployConfig` for deploying from GitHub Actions.

The following environment variables influences the build
when using the `GitHubActions` configuration:

 - `GITHUB_EVENT_NAME`: must be set to `push`.
   This avoids deployment on pull request builds.

 - `GITHUB_REPOSITORY`: must match the value of the `repo` keyword to [`deploydocs`](@ref).

 - `GITHUB_REF`: must match the `devbranch` keyword to [`deploydocs`](@ref), alternatively
   correspond to a git tag.

 - `GITHUB_TOKEN` or `DOCUMENTER_KEY`: used for authentication with GitHub,
   see the manual section for [GitHub Actions](@ref) for more information.

The `GITHUB_*` variables are set automatically on GitHub Actions, see the
[documentation](https://help.github.com/en/actions/automating-your-workflow-with-github-actions/using-environment-variables#default-environment-variables).
"""
struct GitHubActions <: DeployConfig
    github_repository::String
    github_event_name::String
    github_ref::String
end
function GitHubActions()
    github_repository = get(ENV, "GITHUB_REPOSITORY", "") # "JuliaDocs/Documenter.jl"
    github_event_name = get(ENV, "GITHUB_EVENT_NAME", "") # "push", "pull_request" or "cron" (?)
    github_ref        = get(ENV, "GITHUB_REF",        "") # "refs/heads/$(branchname)" for branch, "refs/tags/$(tagname)" for tags
    return GitHubActions(github_repository, github_event_name, github_ref)
end

# Check criteria for deployment
function deploy_folder(cfg::GitHubActions; repo, devbranch, push_preview, devurl, kwargs...)
    io = IOBuffer()
    all_ok = true
    ## Determine build type
    if cfg.github_event_name == "pull_request"
        build_type = :preview
    elseif occursin(r"^refs\/tags\/(.*)$", cfg.github_ref)
        build_type = :release
    else
        build_type = :devbranch
    end
    println(io, "Deployment criteria for deploying $(build_type) build from GitHub Actions:")
    ## The deploydocs' repo should match GITHUB_REPOSITORY
    repo_ok = occursin(cfg.github_repository, repo)
    all_ok &= repo_ok
    println(io, "- $(marker(repo_ok)) ENV[\"GITHUB_REPOSITORY\"]=\"$(cfg.github_repository)\" occurs in repo=\"$(repo)\"")
    if build_type === :release
        ## Do not deploy for PRs
        event_ok = cfg.github_event_name == "push"
        all_ok &= event_ok
        println(io, "- $(marker(event_ok)) ENV[\"GITHUB_EVENT_NAME\"]=\"$(cfg.github_event_name)\" is \"push\"")
        ## If a tag exist it should be a valid VersionNumber
        m = match(r"^refs\/tags\/(.*)$", cfg.github_ref)
        tag_ok = m === nothing ? false : occursin(Base.VERSION_REGEX, String(m.captures[1]))
        all_ok &= tag_ok
        println(io, "- $(marker(tag_ok)) ENV[\"GITHUB_REF\"]=\"$(cfg.github_ref)\" contains a valid VersionNumber")
        ## Deploy to folder according to the tag
        subfolder = m === nothing ? nothing : String(m.captures[1])
    elseif build_type === :devbranch
        ## Do not deploy for PRs
        event_ok = cfg.github_event_name == "push"
        all_ok &= event_ok
        println(io, "- $(marker(event_ok)) ENV[\"GITHUB_EVENT_NAME\"]=\"$(cfg.github_event_name)\" is \"push\"")
        ## deploydocs' devbranch should match the current branch
        m = match(r"^refs\/heads\/(.*)$", cfg.github_ref)
        branch_ok = m === nothing ? false : String(m.captures[1]) == devbranch
        all_ok &= branch_ok
        println(io, "- $(marker(branch_ok)) ENV[\"GITHUB_REF\"] matches devbranch=\"$(devbranch)\"")
        ## Deploy to deploydocs devurl kwarg
        subfolder = devurl
    else # build_type === :preview
        m = match(r"refs\/pull\/(\d+)\/merge", cfg.github_ref)
        pr_number = tryparse(Int, m === nothing ? "" : m.captures[1])
        pr_ok = pr_number !== nothing
        all_ok &= pr_ok
        println(io, "- $(marker(pr_ok)) ENV[\"GITHUB_REF\"] corresponds to a PR number")
        btype_ok = push_preview
        all_ok &= btype_ok
        println(io, "- $(marker(btype_ok)) `push_preview` keyword argument to deploydocs is `true`")
        ## deploydocs to previews/PR
        subfolder = "previews/PR$(something(pr_number, 0))"
    end
    ## GITHUB_ACTOR should exist (just check here and extract the value later)
    actor_ok = haskey(ENV, "GITHUB_ACTOR")
    all_ok &= actor_ok
    println(io, "- $(marker(actor_ok)) ENV[\"GITHUB_ACTOR\"] exists")
    ## GITHUB_TOKEN or DOCUMENTER_KEY should exist (just check here and extract the value later)
    token_ok = haskey(ENV, "GITHUB_TOKEN")
    key_ok = haskey(ENV, "DOCUMENTER_KEY")
    auth_ok = token_ok | key_ok
    all_ok &= auth_ok
    if key_ok
        println(io, "- $(marker(key_ok)) ENV[\"DOCUMENTER_KEY\"] exists exists")
    elseif token_ok
        println(io, "- $(marker(token_ok)) ENV[\"GITHUB_TOKEN\"] exists exists")
    else
        println(io, "- $(marker(auth_ok)) ENV[\"DOCUMENTER_KEY\"] or ENV[\"GITHUB_TOKEN\"]  exists")
    end
    print(io, "Deploying: $(marker(all_ok))")
    return all_ok ? subfolder : nothing
end

function authentication_method(::GitHubActions)
    if haskey(ENV, "DOCUMENTER_KEY")
        return SSH
    else
        @warn "Currently the GitHub Pages build is not triggered when " *
              "using `GITHUB_TOKEN` for authentication. See issue #1177 " *
              "(https://github.com/JuliaDocs/Documenter.jl/issues/1177) " *
              "for more information."
        return HTTPS
    end
end
function authenticated_repo_url(cfg::GitHubActions)
    return "https://$(ENV["GITHUB_ACTOR"]):$(ENV["GITHUB_TOKEN"])@github.com/$(cfg.github_repository).git"
end

##################
# Auto-detection #
##################
function auto_detect_deploy_system()
    if haskey(ENV, "TRAVIS_REPO_SLUG")
        return Travis()
    elseif haskey(ENV, "GITHUB_REPOSITORY")
        return GitHubActions()
    else
        return nothing
    end
end