text:
export visualize_2D_agent_distribution, visualize_1DCA, visualize_2DCA

"""
plot_locs(g, dims::Tuple{Integer,Integer,Integer})

Return three arrays for x, y, z coordinates of each node
"""
function node_locs(g, dims::Tuple{Integer,Integer,Integer})
  coords = []
  for nn in 1:nv(g)
    push!(coords, vertex2coord(nn, dims))
  end
  locs_x = [Float64(i[1]) for i in coords]
  locs_y = [Float64(i[2]) for i in coords]
  locs_z = [Float64(i[3]) for i in coords]
  return locs_x, locs_y, locs_z
end

"""
plot_locs(g, dims::Tuple{Integer,Integer})

Return arrays for x, y coordinates of each node
"""
function node_locs(g, dims::Tuple{Integer,Integer})
  coords = []
  for nn in 1:nv(g)
    push!(coords, vertex2coord(nn, dims))
  end
  locs_x = [Float64(i[1]) for i in coords]
  locs_y = [Float64(i[2]) for i in coords]
  return locs_x, locs_y
end

"""
visualize_2D_agent_distribution(data::DataFrame, model::AbstractModel, position_column::Symbol; types::Symbol=:id, savename::AbstractString="2D_agent_distribution", saveloc::AbstractString="./", cc::Dict=Dict())

Plots the distribution of agents on a 2D grid.

Plots are saved as PDF files under the name given by the `savename` argument. Optionally, choose a path to save the plots using the `saveloc` argument. The default behavior is to save in the current directory, where the code is run.

* You should provide `position_colomn` which is the name of the column that holds agent positions.
* If agents have different types and you want each type to be a different color, provide types=<column name>. Use a dictionary (the `cc` argument) to pass colors for each type. You may choose any color name from the [list of colors on Wikipedia](https://en.wikipedia.org/wiki/Lists_of_colors).
"""
function visualize_2D_agent_distribution(data::DataFrame, model::AbstractModel, position_column::Symbol; types::Symbol=:id, savename::AbstractString="2D_agent_distribution", saveloc::AbstractString="./", cc::Dict=Dict())
  g = model.space.graph
  locs_x, locs_y, = node_locs(g, model.space.dimensions)

  # base node color is light grey
  nodefillc = [RGBA(0.1,0.1,0.1,.1) for i in 1:gridsize(model)]

  # change node color given the position of the agents. Automatically uses any columns with names: pos, or pos_{some number}
  # TODO a new plot where the alpha value of a node corresponds to the value of an individual on a node
  if types == :id  # there is only one type
    pos = position_column
    d = by(data, pos, N = pos => length)
    maxval = maximum(d[!, :N])
    nodefillc[d[pos]] .= [RGBA(0.1, 0.1, 0.1, i) for i in  (d[!, :N] ./ maxval) .- 0.001]
  else  # there are different types of agents based on the values of the "types" column
    dd = dropmissing(data[:, [position_column, types]])
    unique_types = sort(unique(dd[!, types]))
    pos = position_column
    if length(cc) == 0
      colors = colorrgb(length(unique_types))
      colordict = Dict{Any, Tuple}()
      colorvalues = collect(values(colors))
      for ut in 1:length(unique_types)
        colordict[unique_types[ut]] = colorvalues[ut]
      end
    else
      colors = colorrgb(collect(values(cc)))
      colordict = Dict{Any, Tuple}()
      for key in keys(cc)
        colordict[key] = colors[cc[key]]
      end
    end
    colorrev = Dict(v=>k for (k,v) in colors)
    for index in 1:length(unique_types)
      tt = unique_types[index]
      d = by(dd[dd[!, types] .== tt, :], pos, N = pos => length)
      maxval = maximum(d[!, :N])
      nodefillc[d[!, pos]] .= [RGBA(colordict[tt][1], colordict[tt][2], colordict[tt][3], i) for i in  (d[!, :N] ./ maxval) .- 0.001]
      println("$tt: $(colorrev[colordict[tt]])")
    end
  end

  NODESIZE = 0.8/sqrt(gridsize(model))
  draw(PDF(joinpath(saveloc, "$savename.pdf")), gplot(g, locs_x, locs_y, nodefillc=nodefillc, edgestrokec=RGBA(0.1,0.1,0.1,.1), NODESIZE=NODESIZE))
end

"""
colorrgb(color_names::Array)
Returns a dictionary of each colorname and its RGB values. See colors and names on [list of colors on Wikipedia](https://en.wikipedia.org/wiki/Lists_of_colors)
"""
function colorrgb(color_names::Array)
  script_path = splitdir(realpath(@__FILE__))[1]
  f = joinpath(script_path, "color_names.csv")
  rgb_dict = Dict{AbstractString, Tuple}()
  for row in eachline(f)
    fields = split(strip(row), ",")
    if fields[1] in color_names || fields[2] in color_names
      rgb_dict[fields[1]] = (parse(Int, fields[4])/256, parse(Int, fields[5])/256, parse(Int, fields[6])/256)
    end
  end
  if length(rgb_dict) < length(color_names)
    simm = intersect(keys(rgb_dict), color_names)
    for ss in keys(rgb_dict)
      if !in(ss, color_names)
        println("$ss is not a valid color name!")
      end
    end
    throw("Provide valid colornames.")
  end
  return rgb_dict
end

"""
colornames(n::Integer)

Returns n random colors as a dictionary (Dict{colorname=>rgb})
"""
function colorrgb(n::Integer)
  script_path = splitdir(realpath(@__FILE__))[1]
  f = joinpath(script_path, "color_names.csv")
  randcolors = rand(1:866, n)  # 866 is the number of colors in the file above
  rgb_dict = Dict{AbstractString, Tuple}()
  for (index,row) in enumerate(eachline(f))
    if index in randcolors
      fields = split(strip(row), ",")
      rgb_dict[fields[1]] = (parse(Int, fields[4])/256, parse(Int, fields[5])/256, parse(Int, fields[6])/256)
    end
  end
  return rgb_dict
end

"""
visualize_1DCA(data::DataFrame, model::AbstractModel, position_column::Symbol, status_column::Symbol, nrows::Integer; savename::AbstractString="CA_1D", saveloc::AbstractString="./")

Visualizes data of a 1D cellular automaton and saves it in a PDF file under the name given by the `savename` argument. Optionally provide a location for the plots to be saved using the `saveloc` argument. The default behavior is to save them in the currently active directory.

* `data` are the result of multiple runs of the simulation.
* `position_column` is the field of the agent that holds their position.
* `status_column` is the field of the agents that holds their status.
* `nrows` is the number of times the model was run.
"""
function visualize_1DCA(data::DataFrame, model::AbstractModel, position_column::Symbol, status_column::Symbol, nrows::Integer; savename::AbstractString="CA_1D", saveloc::AbstractString="./")
  dims = (model.space.dimensions[1], nrows)
  g = Agents.grid2D(dims[1], dims[2])
  locs_x, locs_y = node_locs(g, dims)

  # base node color is light grey
  nodefillc = [RGBA(0.1,0.1,0.1,.1) for i in 1:gridsize(model)]

  for row in 1:nrows
    pos = Symbol(string(position_column)*"_$row")
    status = Symbol(string(status_column)*"_$row")
    newcolors = [RGBA(0.1, 0.1, 0.1, 0.01) for i in 1:dims[1]]
    for ll in 1:dims[1]
      if data[!, status][ll] == "1"
        newcolors[ll] = RGBA(0.1, 0.1, 0.1, 1.0)
      end
    end
    nodefillc[(dims[1]*row)-(dims[1]-1):dims[1]*row] .= newcolors
  end

  NODESIZE = 1/sqrt(gridsize(model))
  draw(PDF(joinpath(saveloc, "$savename.pdf")), gplot(g, locs_x, locs_y, nodefillc=nodefillc, edgestrokec=RGBA(0.1,0.1,0.1,0.01), NODESIZE=NODESIZE))

end

"""
visualize_2DCA(data::DataFrame, model::AbstractModel, position_column::Symbol, status_column::Symbol, runs::Integer; savename::AbstractString="CA_2D")

Visualizes data of a 2D cellular automaton and saves it in PNG format under the name given by the `savename` argument. Optionally provide a location for the plots to be saved using the `saveloc` argument. The default behavior is to save them in the currently active directory.

`data` are the result of multiple runs of the simulation. `position_column` is the field of the agent that holds their position. `status_column` is the field of the agents that holds their status. `runs` is the number of times the simulation was run.
"""
function visualize_2DCA(data::DataFrame, model::AbstractModel, position_column::Symbol, status_column::Symbol, runs::Integer; savename::AbstractString="CA_2D", saveloc::AbstractString="./")
  dims = model.space.dimensions
  g = model.space.graph
  locs_x, locs_y = node_locs(g, dims)
  NODESIZE = 0.8/sqrt(gridsize(model))

  for r in 1:runs
    # base node color is light grey
    nodefillc = [RGBA(0.1,0.1,0.1,.1) for i in 1:gridsize(model)]
    stat = Symbol(string(status_column)*"_$r")
    nonzeros = findall(a-> a =="1", data[!, stat])

    nodefillc[nonzeros] .= RGBA(0.1, 0.1, 0.1, 1)

    draw(PNG(joinpath(saveloc, "$(savename)_$r.png")), gplot(g, locs_x, locs_y, nodefillc=nodefillc, edgestrokec=RGBA(0.1,0.1,0.1,0.01), NODESIZE=NODESIZE))
  end
end


text:
using Test
using PolynomialBasis
using ImplicitDomainQuadrature
# using Revise
using CutCellDG
include("../useful_routines.jl")

L, W = 1.0, 1.0
levelsetbasis = LagrangeTensorProductBasis(2, 1)
dim,nf = size(interpolation_points(levelsetbasis))

mesh = CutCellDG.CGMesh([0.0, 0.0], [L, W], [1, 1], nf)
levelset = CutCellDG.LevelSet(
    x -> plane_distance_function(x, [1.0, 0.0], [0.75, 0.0]),
    mesh,
    levelsetbasis,
)

refseedpoints, refseedcellids =
    CutCellDG.seed_zero_levelset(2, levelset, [1], 1e3eps(), 4.5)
testseedpoints = [0.5 0.5 0.5 0.5
                 -1/3 1/3 -1/3 1/3]
testseedcellids = repeat([1],4)

@test allapprox(testseedpoints,refseedpoints)
@test allequal(testseedcellids,refseedcellids)

L, W = 2.0, 2.0
levelsetbasis = LagrangeTensorProductBasis(2, 2)
dim,nf = size(interpolation_points(levelsetbasis))

mesh = CutCellDG.CGMesh([0.0, 0.0], [L, W], [2, 2], nf)
levelset = CutCellDG.LevelSet(
    x -> plane_distance_function(x, [1.0, 0.0], [1.5, 0.0]),
    mesh,
    levelsetbasis,
)
cutmesh = CutCellDG.CutMesh(mesh,levelset)

refseedpoints, refseedcellids =
    CutCellDG.seed_zero_levelset(2, levelset, cutmesh)

testseedpoints = [0.   0.  0.   0.  0.   0.  0.   0.
                  -1/3 1/3 -1/3 1/3 -1/3 1/3 -1/3 1/3]
testseedcellids = [3,3,3,3,4,4,4,4]
@test allapprox(testseedpoints,refseedpoints,1e3eps())
@test allequal(refseedcellids, testseedcellids)

refseedpoints,refseedcellids = CutCellDG.seed_zero_levelset(2,levelset,[1,2],1e-12,1.0)
@test isempty(refseedpoints)
@test isempty(refseedcellids)

L, W = 1.0, 1.0
levelsetbasis = HermiteTensorProductBasis(2)

dim,nf = size(interpolation_points(levelsetbasis))

mesh = CutCellDG.CGMesh([0.0, 0.0], [L, W], [1, 1], nf)
levelset = CutCellDG.LevelSet(
    x -> plane_distance_function(x, [1.0, 0.0], [0.75, 0.0]),
    mesh,
    levelsetbasis,
)

refseedpoints, refseedcellids =
    CutCellDG.seed_zero_levelset(2, levelset, [1], 1e3eps(), 4.5)
testseedpoints = [0.5 0.5 0.5 0.5
                 -1/3 1/3 -1/3 1/3]
testseedcellids = repeat([1],4)

@test allapprox(testseedpoints,refseedpoints)
@test allequal(testseedcellids,refseedcellids)

ext: jl


text:
using Documenter, NLPModelsJuMP

makedocs(
  modules = [NLPModelsJuMP],
  doctest = true,
  strict = true,
  assets = ["assets/style.css"],
  format = :html,
  sitename = "NLPModelsJuMP.jl",
  pages = Any["Home" => "index.md",
              "Tutorial" => "tutorial.md",
              "Reference" => "reference.md"]
)

deploydocs(deps = nothing, make = nothing,
  repo = "github.com/JuliaSmoothOptimizers/NLPModelsJuMP.jl.git",
  target = "build",
  devbranch = "master"
)


text:
module Diffusion3D_CUDA
    using ParallelStencil
    using ParallelStencil.FiniteDifferences3D
    @init_parallel_stencil(CUDA, Float64, 3)
    include(joinpath(@__DIR__, "..", "..", "shared", "diffusion3D.jl"))
end


text:
"""
    Neighborhood

Neighborhoods define the pattern of surrounding cells in the "neighborhood"
of the current cell. The `neighbors` function returns the surrounding
cells as an iterable.

The main kinds of neighborhood are demonstrated below:

![Neighborhoods](https://raw.githubusercontent.com/cesaraustralia/DynamicGrids.jl/media/Neighborhoods.png)

```julia
Moore{3}()
```
"""
abstract type Neighborhood{R,L} end

ConstructionBase.constructorof(::Type{<:T}) where T <: Neighborhood{R,L} where {R,L} =
    T.name.wrapper{R,L}

radius(hood::Neighborhood{R}) where R = R
_buffer(hood::Neighborhood) = hood._buffer
@inline positions(hood::Neighborhood, I) = (I .+ o for o in offsets(hood))

Base.eltype(hood::Neighborhood) = eltype(_buffer(hood))
Base.length(hood::Neighborhood{<:Any,L}) where L = L
Base.iterate(hood::Neighborhood, args...) = iterate(neighbors(hood), args...)
Base.getindex(hood::Neighborhood, I...) = getindex(_buffer(hood), I...)

"""
    RadialNeighborhood <: Neighborhood

Square neighborhoods with radius `R`, and side length `2R + 1`
"""
abstract type RadialNeighborhood{R,L} <: Neighborhood{R,L} end

"""
    Moore <: RadialNeighborhood

    Moore(radius::Int=1)

Moore neighborhoods define the neighborhood as all cells within a horizontal or
vertical distance of the central cell. The central cell is omitted.
"""
struct Moore{R,L,B} <: RadialNeighborhood{R,L}
    _buffer::B
end
# Buffer is updated later during the simulation.
Moore(radius::Int=1) = Moore{radius}()
Moore{R}(_buffer=nothing) where R = Moore{R,(2R+1)^2-1}(_buffer)
Moore{R,L}(_buffer::B=nothing) where {R,L,B} = Moore{R,L,B}(_buffer)

@inline function neighbors(hood::Moore{R}) where R
    # Use linear indexing
    buflen = (2R + 1)^2
    centerpoint = buflen ÷ 2 + 1
    return (_buffer(hood)[i] for i in 1:buflen if i != centerpoint)
end
@inline function offsets(hood::Moore{R}) where R
    ((i, j) for j in -R:R, i in -R:R if i != (0, 0))
end
@inline _setbuffer(n::Moore{R,L}, buf::B2) where {R,L,B2} = Moore{R,L,B2}(buf)

# Neighborhood specific `sum` for performance
Base.sum(hood::Moore) = sum(_buffer(hood)) - _centerval(hood)

_centerval(hood::Neighborhood{R}) where R = _buffer(hood)[R + 1, R + 1]

"""
    Window <: RadialNeighborhood

    Window{R}()

A neighboorhood of radius R that includes the central cell.
`R = 1` gives a 3x3 matrix.
"""
struct Window{R,L,B} <: RadialNeighborhood{R,L}
    _buffer::B
end
@inline Window(R::Int) = Window{R}()
@inline Window{R}(_buffer=nothing) where R = Window{R,(2R+1)^2}(_buffer)
@inline Window{R,L}(_buffer::B=nothing) where {R,L,B} = Window{R,L,B}(_buffer)
@inline Window(A::AbstractArray) = Window{(size(A, 1) - 1) ÷ 2}()

@inline _setbuffer(::Window{R,L}, buf::B2) where {R,L,B2} = Window{R,L,B2}(buf)

# The central cell is included
@inline offsets(hood::Window{R}) where R = ((i, j) for j in -R:R, i in -R:R)

neighbors(hood::Window) = _buffer(hood)

"""
    AbstractKernel <: Neighborhood

Abstract supertype for kernel neighborhoods.

These can wrap any other neighborhood object, and include a kernel of
the same length and positions as the neighborhood.
"""
abstract type AbstractKernel{R,L} <: Neighborhood{R,L} end

neighborhood(hood::AbstractKernel) = hood.neighborhood
neighbors(hood::AbstractKernel) = neighbors(neighborhood(hood))
offsets(hood::AbstractKernel) = offsets(neighborhood(hood))
positions(hood::AbstractKernel, I) = positions(neighborhood(hood), I)
kernel(hood::AbstractKernel) = hood.kernel

# We override dot for AbstractKernel as we always mean the sum of the
# products of the kernel and buffer values - never a nested dot product.
LinearAlgebra.dot(hood::AbstractKernel) = _dot(neighborhood(hood), kernel(hood))

function _dot(hood, kernel)
    sum = zero(first(neighbors(hood)))
    for (n, k) in zip(neighbors(hood), kernel)
        sum += n * k
    end
    sum
end
function _dot(hood::Window{<:Any,L}, kernel) where L
    sum = zero(first(neighbors(hood)))
    @simd for i in 1:L
        @inbounds sum += _buffer(hood)[i] * kernel[i]
    end
    sum
end

"""
    Kernel <: AbstractKernel

    Kernel(neighborhood, kernel)

Wrap any other neighborhood object, and includes a kernel of
the same length and positions as the neighborhood.

`R = 1` gives 3x3 matrices.
"""
struct Kernel{R,L,N,K} <: AbstractKernel{R,L}
    neighborhood::N
    kernel::K
end
@inline Kernel(A::AbstractMatrix) = Kernel(Window(A), A)
@inline function Kernel(hood::N, kernel::K) where {N<:Neighborhood{R,L},K} where {R,L}
    length(hood) == length(kernel) || _kernel_length_error(hood, kernel)
    Kernel{R,L,N,K}(hood, kernel)
end

@noinline _kernel_length_error(hood, kernel) =
    throw(ArgumentError("Neighborhood length $(length(hood)) does not match kernel length $(length(kernel))"))

@inline function _setbuffer(n::Kernel{R,L,<:Any,K}, buf) where {R,L,K}
    hood = _setbuffer(neighborhood(n), buf)
    Kernel{R,L,typeof(hood),K}(hood, kernel(n))
end

"""
    AbstractPositional <: Neighborhood

Neighborhoods are tuples or vectors of custom coordinates tuples
that are specified in relation to the central point of the current cell.
They can be any arbitrary shape or size, but should be listed in column-major
order for performance.
"""
abstract type AbstractPositional{R,L} <: Neighborhood{R,L} end

const CustomOffset = Tuple{Vararg{Int}}
const CustomOffsets = Union{AbstractArray{<:CustomOffset},Tuple{Vararg{<:CustomOffset}}}

"""
    Positional <: AbstractPositional

    Positional(coord::Tuple{Vararg{Int}}...)
    Positional(offsets::Tuple{Tuple{Vararg{Int}}})

Neighborhoods that can take arbitrary shapes by specifying each coordinate,
as `Tuple{Int,Int}` of the row/column distance (positive and negative)
from the central point.

The neighborhood radius is calculated from the most distance coordinate.
For simplicity the buffer read from the main grid is a square with sides
`2r + 1` around the central point, and is not shrunk or offset to match the
coordinates if they are not symmetrical.
"""
struct Positional{R,L,O<:CustomOffsets,B} <: AbstractPositional{R,L}
    "A tuple of tuples of Int, containing 2-D coordinates relative to the central point"
    offsets::O
    _buffer::B
end
Positional(args::CustomOffset...) = Positional(args)
Positional(offsets::CustomOffsets, _buffer=nothing) =
    Positional{_absmaxcoord(offsets),length(offsets)}(offsets, _buffer)
Positional{R,L}(offsets::O, _buffer::B=nothing) where {R,L,O<:CustomOffsets,B} =
    Positional{R,L,O,B}(offsets, _buffer)

# Calculate the maximum absolute value in the offsets to use as the radius
_absmaxcoord(offsets::Union{AbstractArray,Tuple}) = maximum(map(x -> maximum(map(abs, x)), offsets))

ConstructionBase.constructorof(::Type{Positional{R,L,C,B}}) where {R,L,C,B} =
    Positional{R,L}

Base.length(hood::Positional) = length(offsets(hood))

offsets(hood::Positional) = hood.offsets
@inline neighbors(hood::Positional) =
    (_buffer(hood)[(offset .+ radius(hood) .+ 1)...] for offset in offsets(hood))
@inline _setbuffer(n::Positional{R,L,O}, buf::B2) where {R,L,O,B2} =
    Positional{R,L,O,B2}(offsets(n), buf)

"""
    LayeredPositional <: AbstractPositional

    LayeredPositional(layers::Positional...)

Sets of [`Positional`](@ref) neighborhoods that can have separate rules for each set.

`neighbors` for `LayeredPositional` returns a tuple of iterators
for each neighborhood layer.
"""
struct LayeredPositional{R,L,La,B} <: AbstractPositional{R,L}
    "A tuple of custom neighborhoods"
    layers::La
    _buffer::B
end
LayeredPositional(layers::Positional...) = LayeredPositional(layers)
LayeredPositional(layers::Tuple{Vararg{<:Positional}}, _buffer=nothing) =
    LayeredPositional{maximum(map(radius, layers)),map(length, layers)}(layers, _buffer)
LayeredPositional{R,L}(layers, _buffer::B) where {R,L,B} = begin
    # Child layers must have the same _buffer, and the same R and L parameters
    layers = map(l -> Positional{R,L}(offsets(l), _buffer), layers)
    LayeredPositional{R,L,typeof(layers),B}(layers, _buffer)
end

@inline neighbors(hood::LayeredPositional) = map(l -> neighbors(l), hood.layers)
@inline offsets(hood::LayeredPositional) = map(l -> offsets(l), hood.layers)
@inline positions(hood::LayeredPositional, args...) = map(l -> positions(l, args...), hood.layers)
@inline _setbuffer(n::LayeredPositional{R,L}, buf) where {R,L} =
    LayeredPositional{R,L}(n.layers, buf)

@inline Base.sum(hood::LayeredPositional) = map(sum, neighbors(hood))

"""
    VonNeumann(radius=1) -> Positional

A convenience wrapper to build Von-Neumann neighborhoods as
a [`Positional`](@ref) neighborhood.
"""
function VonNeumann(radius=1, _buffer=nothing)
    offsets = Tuple{Int,Int}[]
    rng = -radius:radius
    for j in rng, i in rng
        distance = abs(i) + abs(j)
        if distance <= radius && distance > 0
            push!(offsets, (i, j))
        end
    end
    return Positional(Tuple(offsets), _buffer)
end

# Get the size of a neighborhood dimension from its radius,
# which is always 2r + 1.
@inline hoodsize(hood::Neighborhood{R}) where R = hoodsize(R)
@inline hoodsize(radius::Integer) = 2radius + 1


text:
using Bolt
using BenchmarkTools

par = CosmoParams()
bg = Background(par)
ih = IonizationHistory(Peebles(), par, bg)

k_grid = quadratic_k(0.1bg.H₀, 1000bg.H₀, 100)
k_i = 10
hierarchy = Hierarchy(BasicNewtonian(), par, bg, ih, k_grid[k_i])

xᵢ = log(1e-8)
u₀ = Bolt.initial_conditions(xᵢ, hierarchy)
udummy = deepcopy(u₀)
@btime Bolt.hierarchy!(udummy, u₀, hierarchy, xᵢ)

##
@time perturb = boltsolve(hierarchy);

##
@time sf = source_grid(par, bg, ih, k_grid, BasicNewtonian());

##

using Hankel
R = bg.η₀ * bg.H₀
N = 4096
q = QDHT{1,1}(R, N)
using DataInterpolations

x_from_ηH₀ = CubicSpline(bg.x_grid, bg.η.(bg.x_grid) .* bg.H₀)

function f(ηH₀)
    x = x_from_ηH₀(ηH₀)
    u = perturb(x)  # this can be optimized away, save timesteps at the grid!
    du = similar(u)
    Bolt.hierarchy!(du, u, hierarchy, x)
    return Bolt.source_function(du, u, hierarchy, x)
end
fr = f.(q.r)
# fk = q * fr # 0th-order QDHT => fk should have only the first entry non-zero
# fk .*= 1.1

##
clf()
# figure()

# xx =  bg.η.(bg.x_grid[1200:end-1]) .* bg.H₀

xx =  bg.η.(bg.x_grid[1200:end-1]) .* bg.H₀
plot(xx, abs.(f.(xx)) ./ xx,
    label=raw"$k/H_0=$" * "$(k_grid[k_i]/bg.H₀)", "-")

xx = q.r
# plot(xx, abs.(f.(xx)) ./ xx, ".",
#     label=raw"$k/H_0=$" * "$(k_grid[k_i]/bg.H₀)")

xlabel(raw"$η H_0$")
ylabel("Source Function")
xscale("log")
# xlim(0.05, 0.1)
legend()
# yscale("log")
# ylim(1e-5,800)
ylim(1e-5,20)
gcf()

##
bg.η₀ * k_grid[end]

##
clf()
figure()

# xx =  bg.η(bg.x_grid[1200:end]) .* bg.H₀
xx
plot(xx, abs.(sf[1200:end,k_i]) ./ xx, "-",
    label=raw"$k/H_0=$" * "$(k_grid[k_i]/bg.H₀)")
xlabel(raw"$η H_0$")
ylabel("Source Function")
# xscale("log")
xlim(0.05, 0.2)
legend()
# yscale("log")
# ylim(1e-5,20)
gcf()

##
## this is currently the expensive part
# function test(par, bg, ih, sf)
#     @time cltt(2000, par, bg, ih, sf)
# end

# test(par, bg, ih, sf)


text:
using DEBmicroTrait
using CSV, DataFrames, Statistics
using JLD
########################################
# I/O
df_isolates     = CSV.read("/Users/glmarschmann/.julia/dev/DEBmicroTrait/final_manuscript/files/isolates2traits.csv", DataFrame, missingstring="N/A")
########################################

########################################
Genome_size     = convert(Array{Float64,1}, df_isolates.Genome_size)
V_cell          = DEBmicroTrait.genome_size_to_cell_volume(Genome_size)
rrn_copies      = convert(Array{Float64,1}, df_isolates.rRNA_genes)
Min_gen_time    = df_isolates.Min_gen_time
Gram_stain      = convert(Array{String,1}, df_isolates.gram_stain)
########################################

########################################
k_M    = DEBmicroTrait.cell_volume_to_specific_maintenance_rate(V_cell, Min_gen_time, Gram_stain)
k_M_med = median(k_M)
########################################

########################################
# I/O
save("/Users/glmarschmann/.julia/dev/DEBmicroTrait/final_manuscript/files/isolates_maintenance.jld", "kM", k_M)

max_stars_repo_name: giannamars/DEBMicroTrait

max_issues_repo_name: giannamars/DEBMicroTrait

max_forks_repo_name: giannamars/DEBMicroTrait


text:
using Logging
using Base.Test

function test_log_macro_common(flags)
    for (macroname, isshown) in flags
        ex = Expr(:macrocall, symbol(macroname), "test message")
        if isshown
            @test ex |> macroexpand != :nothing
        else
            @test ex |> macroexpand == :nothing
        end
    end
end

@Logging.configure(level=DEBUG)
test_log_macro_common([(:@debug, true), (:@info, true), (:@warn, true),
    (:@err, true), (:@critical, true)])

@Logging.configure(level=INFO)
test_log_macro_common([(:@debug, false), (:@info, true), (:@warn, true),
    (:@err, true), (:@critical, true)])

@Logging.configure(level=WARNING)
test_log_macro_common([(:@debug, false), (:@info, false), (:@warn, true),
    (:@err, true), (:@critical, true)])

@Logging.configure(level=ERROR)
test_log_macro_common([(:@debug, false), (:@info, false), (:@warn, false),
    (:@err, true), (:@critical, true)])

@Logging.configure(level=CRITICAL)
test_log_macro_common([(:@debug, false), (:@info, false), (:@warn, false),
    (:@err, false), (:@critical, true)])

@Logging.configure(level=OFF)
test_log_macro_common([(:@debug, false), (:@info, false), (:@warn, false),
    (:@err, false), (:@critical, false)])


text:
"""
GenX: An Configurable Capacity Expansion Model
Copyright (C) 2021,  Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
A complete copy of the GNU General Public License v2 (GPLv2) is available
in LICENSE.txt.  Users uncompressing this from an archive may not have
received this license file.  If not, see <http://www.gnu.org/licenses/>.
"""

@doc raw"""
	write_power(path::AbstractString, inputs::Dict, setup::Dict, EP::Model)

Function for writing the different values of power generated by the different technologies in operation.
"""
function write_power(path::AbstractString, inputs::Dict, setup::Dict, EP::Model)
	dfGen = inputs["dfGen"]
	G = inputs["G"]     # Number of resources (generators, storage, DR, and DERs)
	T = inputs["T"]     # Number of time steps (hours)

	# Power injected by each resource in each time step
	dfPower = DataFrame(Resource = inputs["RESOURCES"], Zone = dfGen[!,:Zone], AnnualSum = Array{Union{Missing,Float32}}(undef, G))
	if setup["ParameterScale"] ==1
		for i in 1:G
			dfPower[!,:AnnualSum][i] = sum(inputs["omega"].* (value.(EP[:vP])[i,:])) * ModelScalingFactor
		end
		dfPower = hcat(dfPower, DataFrame((value.(EP[:vP]))* ModelScalingFactor, :auto))
	else
		for i in 1:G
			dfPower[!,:AnnualSum][i] = sum(inputs["omega"].* (value.(EP[:vP])[i,:]))
		end
		dfPower = hcat(dfPower, DataFrame(value.(EP[:vP]), :auto))
	end

	auxNew_Names=[Symbol("Resource");Symbol("Zone");Symbol("AnnualSum");[Symbol("t$t") for t in 1:T]]
	rename!(dfPower,auxNew_Names)

	total = DataFrame(["Total" 0 sum(dfPower[!,:AnnualSum]) fill(0.0, (1,T))], :auto)
	for t in 1:T
		total[:,t+3] .= sum(dfPower[:,Symbol("t$t")][1:G])
	end
	rename!(total,auxNew_Names)
	dfPower = vcat(dfPower, total)
 	CSV.write(joinpath(path, "power.csv"), dftranspose(dfPower, false), writeheader=false)
	return dfPower
end

max_stars_repo_name: Betristor/LTESOM

max_issues_repo_name: Betristor/LTESOM

max_forks_repo_name: Betristor/LTESOM


text:
module COVID_data_tool

using DataFrames, JSON, InfoZIP, ZipFile, HTTP, CSV, XLSX, ExcelFiles, Dates
include("diccionarios_claves.jl");
include("url_paths.jl");

if Sys.iswindows()
  if isdir("C:\\archivos_CSV_COVID_data_tool") == true
    println("Carpeta C:\\archivos_CSV_COVID_data_tool existe.")
  else
    mkdir("C:\\archivos_CSV_COVID_data_tool")
  end
elseif Sys.islinux()
  if isdir("/home/archivos_CSV_COVID_data_tool") == true
    println("Carpeta /home/archivos_CSV_COVID_data_tool existe.")
  else
    mkdir("/home/archivos_CSV_COVID_data_tool")
  end
end

function getJSSIn(clave_indicador:: String, clave_municipio:: String, estado:: String, clave_estado:: String)
  l = count(i -> (i == ','), clave_municipio)
  ak = split(clave_municipio[1:end-1], ",")
  j = (length(ak) + 1)
  Ax = Array{Any, 2}(missing, length(ak), 2)

  for i in 1:length(ak)
    if clave_indicador=="1005000039" && ak[i]=="070000020006" && clave_estado=="0002"
        Ar=["San Quintín","No Data"]
    elseif clave_indicador=="1002000026" && ak[i]=="070000020006" && clave_estado=="0002"
        Ar=["San Quintín","No Data"]
    elseif clave_indicador=="1005000039" && ak[i]=="070000040012" && clave_estado=="0004"
        Ar=["Seybaplaya","No Data"]
    elseif clave_indicador=="1002000026" && ak[i]=="070000040012" && clave_estado=="0004"
        Ar=["Seybaplaya","No Data"]
    else
      Ap = getJSS(clave_indicador, string(ak[i]));
      At = get(Ap[1], "OBSERVATIONS", "Not founded");
      Ar = getVG(At, clave_estado);
    end
    Ax[i] = Ar[1]
    Ax[j] = Ar[2]
    j += 1
  end
  return Ax
end

function getJSS(clave_indicador:: String, clave_municipio:: String)
  token = "2e01d681-33e2-9414-67d3-5580000f46b4";
  #token = "60f078ff-60e6-4906-a4da-0dec849de700";
  #token = "d4f8f24b-8030-4864-b5e6-858a83ab5605";
  jurl = "https://www.inegi.org.mx/app/api/indicadores/desarrolladores/jsonxml/INDICATOR/$(clave_indicador)/es/$(clave_municipio)/true/BISE/2.0/$(token)?type=json";
  # @show jurl
  resp = HTTP.get(jurl);
  str = String(resp.body);
  jobj = JSON.Parser.parse(str);

  return ser = jobj["Series"]
end

function getVG(Arob2:: Array{Any, 1}, clave_estado:: String)
  Arax = Array{Any, 2}(undef, 2, 2)
    for k in keys(diccionario_municipios[parse(Int64, clave_estado)])
      if get(Arob2[1], "COBER_GEO", "Not founded") == ("0700" * clave_estado * get(diccionario_municipios[parse(Int64, clave_estado)], k, "Not founded"))
        Arax[1] = k
        break
      else
        Arax[1] = "missing"
      end
    end
    Arax[2] = parse(Float64, get(Arob2[1], "OBS_VALUE", "Not founded"))
  return Arax
end

function get_clave_indicador(indicador:: String)
  clave_indicador = ""
  for k in keys(diccionario_indicadores)
    if k == indicador
      clave_indicador = get(diccionario_indicadores, k, "Not founded")
      break;
    end
  end

  return clave_indicador
end

function get_clave_estado(estado:: String)
  for k in keys(diccionario_estados)
    if k == estado
      return get(diccionario_estados, k, "Estado Not founded")
    end
  end
end

function get_clave_municipio(municipio:: String, clave_estado:: String)
  clave_municipio = ""

  for k in keys(diccionario_municipios[parse(Int64, clave_estado)])
    if k == municipio
      clave_municipio = "0700" * clave_estado * get(diccionario_municipios[parse(Int64, clave_estado)], k, "Not founded") * ","
    end
  end

  return clave_municipio
end

function getDF(data:: Array{Any,2})
  return DataFrame(data, :auto)
end

function Ct_DocCSV(nombre_archivo:: String, tabla_datos:: DataFrame)
  directorio = pathCSV * nombre_archivo
  CSV.write(directorio, tabla_datos)
  return nombre_archivo * " creado"
end

function indicadores_disponibles()
  println("***Indicadores generales***")
  for key in keys(diccionario_indicadores)
    println(key)
  end
  println("***Indicadores IDH***")
  lista_ComponentesIDH()
  println("***Indicadores de Pobreza***")
  lista_IndicadoresPobreza()
  println("***Indicadores de Intensidad Migratoria***")
  println("(Para las entradas de las funciones necesita las claves, no la descripción)")
  lista_ComponentesIIM()
end

#=
function datos_indicador(indicador:: String, estado:: String)
  clave_estado = get_clave_estado(estado);
  clave_indicador = get_clave_indicador(indicador);
  claves_municipios = ""

  for k in keys(diccionario_municipios[parse(Int64, clave_estado)])
    claves_municipios *= get_clave_municipio(k, clave_estado)
  end

  Ardic = getJSSIn(clave_indicador, claves_municipios, estado, clave_estado);

  Ardic = getDF(Ardic)
  rename!(Ardic, :x1 => :Municipio)
  rename!(Ardic, :x2 => indicador)

  nombre_archivo = "datos_" * indicador * "_" * estado * "_" * Dates.format(now(), "dd_u_yyyy_HH_MM_SS") * ".csv"
  Ct_DocCSV(nombre_archivo, Ardic)
end
=#

function lista_ComponentesIDH()
  adh=XLSX.readxlsx(path*"Indice de Desarrollo Humano Municipal 2010 2015.xlsx")["IDH municipal 2015"]
  headIDH=adh["E8:L8"];
  for str in headIDH
    println(str)
  end
end

function lista_IndicadoresPobreza()
  ar=XLSX.readxlsx(path*"Concentrado, indicadores de pobreza.xlsx")["Concentrado municipal"]
  indPob= []
  for a in ar["H5:CU5"]
      if !isequal(missing,a)
          push!(indPob,a)
      end
  end
  push!(indPob,"Indicadores de pobreza")
  indPob[16]="Población con ingreso inferior a la línea de bienestar"
  for i in indPob
    println(i)
  end
end

function lista_ComponentesIIM()
  df=DataFrame(ExcelFiles.load(path*"IIM2010_BASEMUN.xls","IIM2010_BASEMUN"))
  ar=[:ENT,:NOM_ENT,:MUN,:NOM_MUN]
  select!(df,Not(ar))
  dicIIM=Dict("TOT_VIV"=>"Total de viviendas particulares habitadas","VIV_REM"=>"Porcentaje de viviendas que reciben remesas","VIV_EMIG"=>"Porcentaje de viviendas con emigrantes a EU","VIV_CIRC"=>"Porcentaje de viviendas con migrantes circulares","VIV_RET"=>"Porcentaje de viviendas con migrantes de retorno","IIM_2010"=>"Índice de intensidad migratoria 2010","IIM0a100"=>"Indice de instansidad migratoria en escala de 0 a 100","GIM_2010"=>"Grado de intensidad migratoria 2010","LUG_EDO"=>"Lugar que ocupa en el contexto estatal","LUG_NAL"=>"Lugar que ocupa en el contexto nacional")
  for i in names(df)
    println(i*" ---> "*dicIIM[i])
  end
end

#=
function datos_municipio(indicadores, estado::String, municipio::String)
  clave_estado = get_clave_estado(estado)
  clave_municipio = get_clave_municipio(municipio, clave_estado)
  claves_indicadores = []
  tablas_datos = []
  arreglo_df_datos = []

  for indicador in indicadores
    push!(claves_indicadores, get_clave_indicador(indicador))
  end

  for clave_ind in claves_indicadores
    push!(tablas_datos, getJSSIn(clave_ind, clave_municipio, estado, clave_estado))
  end

  for i in 1:length(indicadores)
    aux = DataFrame(tablas_datos[i])
    rename!(aux, :x1 => :Municipio)
    rename!(aux, :x2 => indicadores[i])
    push!(arreglo_df_datos, aux)
  end

  df = DataFrame(Municipio = municipio)

  for i in 1:length(indicadores)
    df = innerjoin(df, arreglo_df_datos[i], on=:Municipio)
  end

  nombre_archivo = "datos_" * estado * "_" * municipio * "_" * Dates.format(now(), "dd_u_yyyy_HH_MM_SS") * ".csv"
  Ct_DocCSV(nombre_archivo, df)
end
=#

function comp_CovInd(indicadores::Vector{String},estado::String,municipio::String)
   dfest=DataFrame(ENTIDAD_UM=parse(Int64,get(diccionario_estados,estado,"Not founded")))
   dfmun=DataFrame(MUNICIPIO_RES=parse(Int64,get(diccionario_municipios[parse(Int64,get(diccionario_estados,estado,"Not founded"))],municipio,"Not founded")))
   f=getCovData()
   f=innerjoin(f,dfest,on=:ENTIDAD_UM)
   f=innerjoin(f,dfmun,on=:MUNICIPIO_RES)
   dfind=conjunto_estado(indicadores,estado,municipio)
   dfrec=hcat(dfmun,dfest)
   dfind=hcat(dfrec,dfind)
   f=innerjoin(f,dfind,on=[:MUNICIPIO_RES,:ENTIDAD_UM])
   nombre="Covid_union_"*estado*"_"*municipio*".csv"
   Ct_DocCSV(nombre,f)
end

function dato_estado(indicador::String,estado::String)
  if indicador=="Extension territorial"
    dfaux=getExtTer(estado)
  elseif indicador=="Indicadores de pobreza"
    dfaux=getIndPobreza("All",estado)
  elseif indicador=="IDH y componentes"
    dfaux=getIDH("All",estado)
  elseif indicador=="Intesidad migratoria y componentes"
    dfaux=getIndIM("All",estado)
  elseif haskey(diccionario_indicadores,indicador)
    dfaux=getIndINEGI(estado, indicador)
  elseif indicador=="Años promedio de escolaridad"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Años esperados de escolarización"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Ingreso per cápita anual"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Tasa de mortalidad infantil"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Índice de educación"
    dfaux=getIDH(indicador,esatdo)
  elseif indicador=="Índice de salud"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Índice de ingreso"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="IDH"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Pobreza"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Pobreza extrema"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Pobreza moderada"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Vulnerables por carencia social"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Vulnerables por ingreso"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="No pobres y no vulnerables"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Rezago educativo"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a los servicios de salud"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a la seguridad social"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a los servicios básicos en la vivienda"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a la alimentación"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con al menos una carencia social"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con tres o más carencias sociales"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con ingreso inferior a la línea de bienestar"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con ingreso inferior a la línea de bienestar mínimo"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="TOT_VIV"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_REM"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_EMIG"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_CIRC"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_RET"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="IIM_2010"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="IIMa100"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="GIM_2010"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="LUG_EDO"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="LUG_NAL"
    dfaux=getIndIM(indicador,estado)
  end
  plu=[]
  for k in keys(diccionario_municipios[parse(Int64,get(diccionario_estados,estado,"Not founded"))])
    push!(plu,k)
  end
  dfDel=DataFrame(Municipio=plu)
  dfaux=innerjoin(dfaux,dfDel,on=:Municipio)
  nombre_doc=indicador*"_"*estado*"_"*Dates.format(now(), "dd_u_yyyy_HH_MM_SS")*".csv"
  Ct_DocCSV(nombre_doc,dfaux)
end

function conjunto_estado(indicadores::Vector{String},estado::String)
  munau=[]
  for k in keys(diccionario_municipios[parse(Int64,get(diccionario_estados,estado,"Not founded"))])
    push!(munau,k)
  end
  dfaux=DataFrame(Municipio=munau)
  for indicador in indicadores
      if indicador=="Extension territorial"
        dfaux=innerjoin(dfaux,getExtTer(estado),on=:Municipio)
        rename!(dfaux,:Total=>indicador)
      elseif indicador=="Indicadores de pobreza"
        dfaux=innerjoin(dfaux,getIndPobreza("All",estado),on=:Municipio)
      elseif indicador=="IDH y componentes"
        dfaux=innerjoin(dfaux,getIDH("All",estado),on=:Municipio)
      elseif indicador=="Intesidad migratoria y componentes"
        dfaux=innerjoin(dfaux,getIndIM("All",estado),on=:Municipio)
      elseif haskey(diccionario_indicadores,indicador)
        dfaux=innerjoin(dfaux,getIndINEGI(estado,indicador),on=:Municipio)
        for c in 1:length(names(dfaux))
           if string(names(dfaux)[c])=="Total"
              rename!(dfaux,:Total=>indicador)
           end
         end
      elseif indicador=="Años promedio de escolaridad"
          dfaux=innerjoin(dfaux,getIDH("Años promedio de escolaridad",estado),on=:Municipio)
      elseif indicador=="Años esperados de escolarización"
          dfaux=innerjoin(dfaux,getIDH("Años esperados de escolaridad",estado),on=:Municipio)
      elseif indicador=="Ingreso per cápita anual"
          dfaux=innerjoin(dfaux,getIDH("Ingreso per cápita anual",estado),on=:Municipio)
      elseif indicador=="Tasa de mortalidad infantil"
          dfaux=innerjoin(dfaux,getIDH("Tasa de mortalidad infantil",estado),on=:Municipio)
      elseif indicador=="Índice de educación"
          dfaux=innerjoin(dfaux,getIDH("Índice de educación",estado),on=:Municipio)
      elseif indicador=="Índice de salud"
          dfaux=innerjoin(dfaux,getIDH("Índice de salud",estado),on=:Municipio)
      elseif indicador=="Índice de ingreso"
          dfaux=innerjoin(dfaux,getIDH("Índice de ingreso",estado),on=:Municipio)
      elseif indicador=="IDH"
          dfaux=innerjoin(dfaux,getIDH("IDH",estado),on=:Municipio)
      elseif indicador=="Pobreza"
        dfaux=innerjoin(dfaux,getIndPobreza("Pobreza",estado),on=:Municipio)
      elseif indicador=="Pobreza extrema"
        dfaux=innerjoin(dfaux,getIndPobreza("Pobreza extrema",estado),on=:Municipio)
      elseif indicador=="Pobreza moderada"
        dfaux=innerjoin(dfaux,getIndPobreza("Pobreza moderada",estado),on=:Municipio)
      elseif indicador=="Vulnerables por carencia social"
        dfaux=innerjoin(dfaux,getIndPobreza("Vulnerables por carencia social",estado),on=:Municipio)
      elseif indicador=="Vulnerables por ingreso"
        dfaux=innerjoin(dfaux,getIndPobreza("Vulnerables por ingreso",estado),on=:Municipio)
      elseif indicador=="No pobres y no vulnerables"
        dfaux=innerjoin(dfaux,getIndPobreza("No pobres y no vulnerables",estado),on=:Municipio)
      elseif indicador=="Rezago educativo"
        dfaux=innerjoin(dfaux,getIndPobreza("Rezago educativo",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a los servicios de salud"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a los servicios de salud",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a la seguridad social"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a la seguridad social",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a los servicios básicos en la vivienda"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a los servicios básicos en la vivienda",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a la alimentación"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a la alimentación",estado),on=:Municipio)
      elseif indicador=="Población con al menos una carencia social"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con al menos una carencia social",estado),on=:Municipio)
      elseif indicador=="Población con tres o más carencias sociales"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con tres o más carencias sociales",estado),on=:Municipio)
      elseif indicador=="Población con ingreso inferior a la línea de bienestar"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con ingreso inferior a la línea de bienestar",estado),on=:Municipio)
      elseif indicador=="Población con ingreso inferior a la línea de bienestar mínimo"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con ingreso inferior a la línea de bienestar mínimo",estado),on=:Municipio)
      elseif indicador=="TOT_VIV"
        dfaux=innerjoin(dfaux,getIndIM("TOT_VIV",estado),on=:Municipio)
      elseif indicador=="VIV_REM"
        dfaux=innerjoin(dfaux,getIndIM("VIV_REM",estado),on=:Municipio)
      elseif indicador=="VIV_EMIG"
        dfaux=innerjoin(dfaux,getIndIM("VIV_EMIG",estado),on=:Municipio)
      elseif indicador=="VIV_CIRC"
        dfaux=innerjoin(dfaux,getIndIM("VIV_CIRC",estado),on=:Municipio)
      elseif indicador=="VIV_RET"
        dfaux=innerjoin(dfaux,getIndIM("VIV_RET",estado),on=:Municipio)
      elseif indicador=="IIM_2010"
        dfaux=innerjoin(dfaux,getIndIM("IIM_2010",estado),on=:Municipio)
      elseif indicador=="IIMa100"
        dfaux=innerjoin(dfaux,getIndIM("IIMa100",estado),on=:Municipio)
      elseif indicador=="GIM_2010"
        dfaux=innerjoin(dfaux,getIndIM("GIM_2010",estado),on=:Municipio)
      elseif indicador=="LUG_EDO"
        dfaux=innerjoin(dfaux,getIndIM("LUG_EDO",estado),on=:Municipio)
      elseif indicador=="LUG_NAL"
        dfaux=innerjoin(dfaux,getIndIM("LUG_NAL",estado),on=:Municipio)
      end
  end
  nombre_doc="Conjunto_de_"*estado*"_"*Dates.format(now(), "dd_u_yyyy_HH_MM_SS")*".csv"
  Ct_DocCSV(nombre_doc,dfaux)
end

function dato_estado(indicador::String,estado::String,municipio::String)
  if indicador=="Extension territorial"
    dfaux=getExtTer(estado)
  elseif indicador=="Indicadores de pobreza"
    dfaux=getIndPobreza("All",estado)
  elseif indicador=="IDH y componentes"
    dfaux=getIDH("All",estado)
  elseif indicador=="Intesidad migratoria y componentes"
    dfaux=getIndIM("All",estado)
  elseif haskey(diccionario_indicadores,indicador)
    dfaux=getIndINEGI(estado, indicador)
  elseif indicador=="Años promedio de escolaridad"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Años esperados de escolarización"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Ingreso per cápita anual"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Tasa de mortalidad infantil"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Índice de educación"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Índice de salud"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="Índice de ingreso"
    dfaux=getIDH(indicador,estado)
  elseif indicador=="IDH"
    dfaux=getIDH(indicador,estado)
    for c in 1:length(names(dfaux))
       if string(names(dfaux)[c])=="Total"
          rename!(dfaux,:Total=>indicador)
       end
     end
  elseif indicador=="Pobreza"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Pobreza extrema"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Pobreza moderada"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Vulnerables por carencia social"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Vulnerables por ingreso"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="No pobres y no vulnerables"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Rezago educativo"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a los servicios de salud"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a la seguridad social"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a los servicios básicos en la vivienda"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Carencia por acceso a la alimentación"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con al menos una carencia social"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con tres o más carencias sociales"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con ingreso inferior a la línea de bienestar"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="Población con ingreso inferior a la línea de bienestar mínimo"
    dfaux=getIndPobreza(indicador,estado)
  elseif indicador=="TOT_VIV"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_REM"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_EMIG"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_CIRC"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="VIV_RET"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="IIM_2010"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="IIMa100"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="GIM_2010"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="LUG_EDO"
    dfaux=getIndIM(indicador,estado)
  elseif indicador=="LUG_NAL"
    dfaux=getIndIM(indicador,estado)
  end
  plu=[]
  for k in keys(diccionario_municipios[parse(Int64,get(diccionario_estados,estado,"Not founded"))])
    push!(plu,k)
  end
  dfDel=DataFrame(Municipio=plu)
  dfaux=innerjoin(dfaux,dfDel,on=:Municipio)
  nombre_doc=indicador*"_"*estado*"_"*Dates.format(now(), "dd_u_yyyy_HH_MM_SS")*".csv"
  Ct_DocCSV(nombre_doc,dfaux)
end

function conjunto_estado(indicadores::Vector{String},estado::String,municipio::String)
  dfaux=DataFrame(Municipio=municipio)
  for indicador in indicadores
      if indicador=="Extension territorial"
        dfaux=innerjoin(dfaux,getExtTer(estado),on=:Municipio)
        rename!(dfaux,:Total=>indicador)
      elseif indicador=="Indicadores de pobreza"
        dfaux=innerjoin(dfaux,getIndPobreza("All",estado),on=:Municipio)
      elseif indicador=="IDH y componentes"
        dfaux=innerjoin(dfaux,getIDH("All",estado),on=:Municipio)
      elseif indicador=="Intensidad migratoria y componentes"
        dfaux=innerjoin(dfaux,getIndIM("All",estado),on=:Municipio)
      elseif haskey(diccionario_indicadores,indicador)
        dfaux=innerjoin(dfaux,getIndINEGI(estado,indicador),on=:Municipio)
        for c in 1:length(names(dfaux))
           if string(names(dfaux)[c])=="Total"
              rename!(dfaux,:Total=>indicador)
           end
         end
      elseif indicador=="Años promedio de escolaridad"
          dfaux=innerjoin(dfaux,getIDH("Años promedio de escolaridad",estado),on=:Municipio)
      elseif indicador=="Años esperados de escolarización"
          dfaux=innerjoin(dfaux,getIDH("Años esperados de escolarización",estado),on=:Municipio)
      elseif indicador=="Ingreso per cápita anual"
          dfaux=innerjoin(dfaux,getIDH("Ingreso per cápita anual",estado),on=:Municipio)
      elseif indicador=="Tasa de mortalidad infantil"
          dfaux=innerjoin(dfaux,getIDH("Tasa de mortalidad infantil",estado),on=:Municipio)
      elseif indicador=="Índice de educación"
          dfaux=innerjoin(dfaux,getIDH("Índice de educación",estado),on=:Municipio)
      elseif indicador=="Índice de salud"
          dfaux=innerjoin(dfaux,getIDH("Índice de salud",estado),on=:Municipio)
      elseif indicador=="Índice de ingreso"
          dfaux=innerjoin(dfaux,getIDH("Índice de ingreso",estado),on=:Municipio)
      elseif indicador=="IDH"
          dfaux=innerjoin(dfaux,getIDH("IDH",estado),on=:Municipio)
      elseif indicador=="Pobreza"
        dfaux=innerjoin(dfaux,getIndPobreza("Pobreza",estado),on=:Municipio)
      elseif indicador=="Pobreza extrema"
        dfaux=innerjoin(dfaux,getIndPobreza("Pobreza extrema",estado),on=:Municipio)
      elseif indicador=="Pobreza moderada"
        dfaux=innerjoin(dfaux,getIndPobreza("Pobreza moderada",estado),on=:Municipio)
      elseif indicador=="Vulnerables por carencia social"
        dfaux=innerjoin(dfaux,getIndPobreza("Vulnerables por carencia social",estado),on=:Municipio)
      elseif indicador=="Vulnerables por ingreso"
        dfaux=innerjoin(dfaux,getIndPobreza("Vulnerables por ingreso",estado),on=:Municipio)
      elseif indicador=="No pobres y no vulnerables"
        dfaux=innerjoin(dfaux,getIndPobreza("No pobres y no vulnerables",estado),on=:Municipio)
      elseif indicador=="Rezago educativo"
        dfaux=innerjoin(dfaux,getIndPobreza("Rezago educativo",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a los servicios de salud"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a los servicios de salud",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a la seguridad social"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a la seguridad social",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a los servicios básicos en la vivienda"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a los servicios básicos en la vivienda",estado),on=:Municipio)
      elseif indicador=="Carencia por acceso a la alimentación"
        dfaux=innerjoin(dfaux,getIndPobreza("Carencia por acceso a la alimentación",estado),on=:Municipio)
      elseif indicador=="Población con al menos una carencia social"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con al menos una carencia social",estado),on=:Municipio)
      elseif indicador=="Población con tres o más carencias sociales"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con tres o más carencias sociales",estado),on=:Municipio)
      elseif indicador=="Población con ingreso inferior a la línea de bienestar"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con ingreso inferior a la línea de bienestar",estado),on=:Municipio)
      elseif indicador=="Población con ingreso inferior a la línea de bienestar mínimo"
        dfaux=innerjoin(dfaux,getIndPobreza("Población con ingreso inferior a la línea de bienestar mínimo",estado),on=:Municipio)
      elseif indicador=="TOT_VIV"
        dfaux=innerjoin(dfaux,getIndIM("TOT_VIV",estado),on=:Municipio)
      elseif indicador=="VIV_REM"
        dfaux=innerjoin(dfaux,getIndIM("VIV_REM",estado),on=:Municipio)
      elseif indicador=="VIV_EMIG"
        dfaux=innerjoin(dfaux,getIndIM("VIV_EMIG",estado),on=:Municipio)
      elseif indicador=="VIV_CIRC"
        dfaux=innerjoin(dfaux,getIndIM("VIV_CIRC",estado),on=:Municipio)
      elseif indicador=="VIV_RET"
        dfaux=innerjoin(dfaux,getIndIM("VIV_RET",estado),on=:Municipio)
      elseif indicador=="IIM_2010"
        dfaux=innerjoin(dfaux,getIndIM("IIM_2010",estado),on=:Municipio)
      elseif indicador=="IIMa100"
        dfaux=innerjoin(dfaux,getIndIM("IIMa100",estado),on=:Municipio)
      elseif indicador=="GIM_2010"
        dfaux=innerjoin(dfaux,getIndIM("GIM_2010",estado),on=:Municipio)
      elseif indicador=="LUG_EDO"
        dfaux=innerjoin(dfaux,getIndIM("LUG_EDO",estado),on=:Municipio)
      elseif indicador=="LUG_NAL"
        dfaux=innerjoin(dfaux,getIndIM("LUG_NAL",estado),on=:Municipio)
      end
  end
  return dfaux
end

#Codigo para la obtencion de los datos faltantes
function downloadCD()
    fileDir=HTTP.download(url,pathCov);
    InfoZIP.unzip(fileDir,pathCov);
end

function getCovData()
    covName=string(Dates.today()-Dates.Day(1))
    covName=replace(covName,"-"=>"")
    covName=SubString(covName,3)
    covName=covName*"COVID19MEXICO.csv"
    covFile= pathCov*covName;
    ce=CSV.read(covFile,DataFrame);
    return ce
end

function downloadIP()
    inPobDir=HTTP.download(turl,path)
    InfoZIP.unzip(inPobDir,path)
end

function getIndPobreza(ind:: String,estado::String)
    ar=XLSX.readxlsx(path*"Concentrado, indicadores de pobreza.xlsx")["Concentrado municipal"]
    pobData=Array{Float64,2}
    indPob= []
    for a in ar["G5:CU5"]
        if !isequal(missing,a)
            push!(indPob,a)
        end
    end
    indPob[16]="Población con ingreso inferior a la línea de bienestar"
    headPob=ar["H6:M6"]
    for i in 1:length(headPob)
        headPob[i]=replace(headPob[i],"\n"=>" ")
    end
    dfIPob=DataFrame(ar["H9:M2465"])
    rename!(dfIPob,[Symbol(headPob[h]) for h in 1:6])
    dfIPobEx=DataFrame(ar["N9:S2465"])
    rename!(dfIPobEx,[Symbol(headPob[h]) for h in 1:6])
    dfIPobMod=DataFrame(ar["T9:Y2465"])
    rename!(dfIPobMod,[Symbol(headPob[h]) for h in 1:6])
    dfIVCS=DataFrame(ar["Z9:AE2465"])
    rename!(dfIVCS,[Symbol(headPob[h]) for h in 1:6])
    dfIVI=DataFrame(ar["AF9:AI2465"])
    rename!(dfIVI,[Symbol(headPob[h]) for h in 1:4])
    dfINPV=DataFrame(ar["AJ9:AM2465"])
    rename!(dfINPV,[Symbol(headPob[h]) for h in 1:4])
    dfIRe=DataFrame(ar["AN9:AS2465"])
    rename!(dfIRe,[Symbol(headPob[h]) for h in 1:6])
    dfICASS=DataFrame(ar["AT9:AY2465"])
    rename!(dfICASS,[Symbol(headPob[h]) for h in 1:6])
    dfICASSO=DataFrame(ar["AZ9:BE2465"])
    rename!(dfICASSO,[Symbol(headPob[h]) for h in 1:6])
    dfICCEV=DataFrame(ar["BF9:BK2465"])
    rename!(dfICCEV,[Symbol(headPob[h]) for h in 1:6])
    dfICASBV=DataFrame(ar["BL9:BQ2465"])
    rename!(dfICASBV,[Symbol(headPob[h]) for h in 1:6])
    dfICAA=DataFrame(ar["BR9:BW2465"])
    rename!(dfICAA,[Symbol(headPob[h]) for h in 1:6])
    dfIUCS=DataFrame(ar["BX9:CC2465"])
    rename!(dfIUCS,[Symbol(headPob[h]) for h in 1:6])
    dfITCS=DataFrame(ar["CD9:CI2465"])
    rename!(dfITCS,[Symbol(headPob[h]) for h in 1:6])
    dfIIILB=DataFrame(ar["CJ9:CO2465"])
    rename!(dfIIILB,[Symbol(headPob[h]) for h in 1:6])
    dfIIILBM=DataFrame(ar["CP9:CU2465"])
    rename!(dfIIILBM,[Symbol(headPob[h]) for h in 1:6])
    headINDPOB=[]
    headPob=ar["H6:CU6"]
    for i in 1:length(headPob)
        headPob[i]=replace(headPob[i],"\n"=>" ")
    end
    for i in 1:6
        push!(headINDPOB,headPob[i]*"_Pob")
    end
    for i in 7:12
        push!(headINDPOB,headPob[i]*"_PobEx")
    end
    for i in 13:18
        push!(headINDPOB,headPob[i]*"_PobMod")
    end
    for i in 19:24
        push!(headINDPOB,headPob[i]*"_VCS")
    end
    for i in 25:28
        push!(headINDPOB,headPob[i]*"_VI")
    end
    for i in 29:32
        push!(headINDPOB,headPob[i]*"_NPV")
    end
    for i in 33:38
        push!(headINDPOB,headPob[i]*"_RE")
    end
    for i in 39:44
        push!(headINDPOB,headPob[i]*"_CASS")
    end
    for i in 45:50
        push!(headINDPOB,headPob[i]*"_CASSO")
    end
    for i in 51:56
        push!(headINDPOB,headPob[i]*"_CCEV")
    end
    for i in 57:62
        push!(headINDPOB,headPob[i]*"_CASBV")
    end
    for i in 63:68
        push!(headINDPOB,headPob[i]*"_CAA")
    end
    for i in 69:74
        push!(headINDPOB,headPob[i]*"_UCS")
    end
    for i in 75:80
        push!(headINDPOB,headPob[i]*"_TCS")
    end
    for i in 81:86
        push!(headINDPOB,headPob[i]*"_IIILB")
    end
    for i in 87:92
        push!(headINDPOB,headPob[i]*"_IIILBM")
    end
    dfINDPOB=hcat(dfIPob,dfIPobEx,dfIPobMod,dfIVCS,dfIVI,dfINPV,dfIRe,dfICASS,dfICASSO,dfICCEV,dfICASBV,dfICAA,dfIUCS,dfITCS,dfIIILB,dfIIILBM;
    makeunique=true)
    rename!(dfINDPOB,[Symbol(headINDPOB[c]) for c in 1:length(headINDPOB)])
    if ind==indPob[1]
        dfIndreq=getdf2015data(ar,dfIPob,estado);
    elseif ind==indPob[2]
        dfIndreq=getdf2015data(ar,dfIPobEx,estado);
    elseif ind==indPob[3]
        dfIndreq=getdf2015data(ar,dfIPobMod,estado);
    elseif ind==indPob[4]
        dfIndreq=getdf2015data(ar,dfIVCS,estado);
    elseif ind==indPob[5]
        dfIndreq=getdf2015data(ar,dfIVI,estado);
    elseif ind==indPob[6]
        dfIndreq=getdf2015data(ar,dfINPV,estado);
    elseif ind==indPob[7]
        dfIndreq=getdf2015data(ar,dfIRe,estado);
    elseif ind==indPob[8]
        dfIndreq=getdf2015data(ar,dfICASS,estado);
    elseif ind==indPob[9]
        dfIndreq=getdf2015data(ar,dfICASSO,estado);
    elseif ind==indPob[10]
        dfIndreq=getdf2015data(ar,dfICCEV,estado);
    elseif ind==indPob[11]
        dfIndreq=getdf2015data(ar,dfICASBV,estado);
    elseif ind==indPob[12]
        dfIndreq=getdf2015data(ar,dfICAA,estado);
    elseif ind==indPob[13]
        dfIndreq=getdf2015data(ar,dfIUCS,estado);
    elseif ind==indPob[14]
        dfIndreq=getdf2015data(ar,dfITCS,estado);
    elseif ind==indPob[15]
        dfIndreq=getdf2015data(ar,dfIIILB,estado);
    elseif ind==indPob[16]
        dfIndreq=getdf2015data(ar,dfIIILBM,estado);
    elseif ind=="All"
        dfIndreq=getdf2015data(ar,dfINDPOB,estado);
    end
    return dfIndreq
end

function getIndPobreza(ind:: String)
    ar=XLSX.readxlsx(path*"Concentrado, indicadores de pobreza.xlsx")["Concentrado municipal"]
    pobData=Array{Float64,2}
    indPob= []
    for a in ar["G5:CU5"]
        if !isequal(missing,a)
            push!(indPob,a)
        end
    end
    indPob[16]="Población con ingreso inferior a la línea de bienestar"
    headPob=ar["H6:M6"]
    for i in 1:length(headPob)
        headPob[i]=replace(headPob[i],"\n"=>" ")
    end
    dfIPob=DataFrame(ar["H9:M2465"])
    rename!(dfIPob,[Symbol(headPob[h]) for h in 1:6])
    dfIPobEx=DataFrame(ar["N9:S2465"])
    rename!(dfIPobEx,[Symbol(headPob[h]) for h in 1:6])
    dfIPobMod=DataFrame(ar["T9:Y2465"])
    rename!(dfIPobMod,[Symbol(headPob[h]) for h in 1:6])
    dfIVCS=DataFrame(ar["Z9:AE2465"])
    rename!(dfIVCS,[Symbol(headPob[h]) for h in 1:6])
    dfIVI=DataFrame(ar["AF9:AI2465"])
    rename!(dfIVI,[Symbol(headPob[h]) for h in 1:4])
    dfINPV=DataFrame(ar["AJ9:AM2465"])
    rename!(dfINPV,[Symbol(headPob[h]) for h in 1:4])
    dfIRe=DataFrame(ar["AN9:AS2465"])
    rename!(dfIRe,[Symbol(headPob[h]) for h in 1:6])
    dfICASS=DataFrame(ar["AT9:AY2465"])
    rename!(dfICASS,[Symbol(headPob[h]) for h in 1:6])
    dfICASSO=DataFrame(ar["AZ9:BE2465"])
    rename!(dfICASSO,[Symbol(headPob[h]) for h in 1:6])
    dfICCEV=DataFrame(ar["BF9:BK2465"])
    rename!(dfICCEV,[Symbol(headPob[h]) for h in 1:6])
    dfICASBV=DataFrame(ar["BL9:BQ2465"])
    rename!(dfICASBV,[Symbol(headPob[h]) for h in 1:6])
    dfICAA=DataFrame(ar["BR9:BW2465"])
    rename!(dfICAA,[Symbol(headPob[h]) for h in 1:6])
    dfIUCS=DataFrame(ar["BX9:CC2465"])
    rename!(dfIUCS,[Symbol(headPob[h]) for h in 1:6])
    dfITCS=DataFrame(ar["CD9:CI2465"])
    rename!(dfITCS,[Symbol(headPob[h]) for h in 1:6])
    dfIIILB=DataFrame(ar["CJ9:CO2465"])
    rename!(dfIIILB,[Symbol(headPob[h]) for h in 1:6])
    dfIIILBM=DataFrame(ar["CP9:CU2465"])
    rename!(dfIIILBM,[Symbol(headPob[h]) for h in 1:6])
    headINDPOB=[]
    headPob=ar["H6:CU6"]
    for i in 1:length(headPob)
        headPob[i]=replace(headPob[i],"\n"=>" ")
    end
    for i in 1:6
        push!(headINDPOB,headPob[i]*"_Pob")
    end
    for i in 7:12
        push!(headINDPOB,headPob[i]*"_PobEx")
    end
    for i in 13:18
        push!(headINDPOB,headPob[i]*"_PobMod")
    end
    for i in 19:24
        push!(headINDPOB,headPob[i]*"_VCS")
    end
    for i in 25:28
        push!(headINDPOB,headPob[i]*"_VI")
    end
    for i in 29:32
        push!(headINDPOB,headPob[i]*"_NPV")
    end
    for i in 33:38
        push!(headINDPOB,headPob[i]*"_RE")
    end
    for i in 39:44
        push!(headINDPOB,headPob[i]*"_CASS")
    end
    for i in 45:50
        push!(headINDPOB,headPob[i]*"_CASSO")
    end
    for i in 51:56
        push!(headINDPOB,headPob[i]*"_CCEV")
    end
    for i in 57:62
        push!(headINDPOB,headPob[i]*"_CASBV")
    end
    for i in 63:68
        push!(headINDPOB,headPob[i]*"_CAA")
    end
    for i in 69:74
        push!(headINDPOB,headPob[i]*"_UCS")
    end
    for i in 75:80
        push!(headINDPOB,headPob[i]*"_TCS")
    end
    for i in 81:86
        push!(headINDPOB,headPob[i]*"_IIILB")
    end
    for i in 87:92
        push!(headINDPOB,headPob[i]*"_IIILBM")
    end
    dfINDPOB=hcat(dfIPob,dfIPobEx,dfIPobMod,dfIVCS,dfIVI,dfINPV,dfIRe,dfICASS,dfICASSO,dfICCEV,dfICASBV,dfICAA,dfIUCS,dfITCS,dfIIILB,dfIIILBM;
    makeunique=true)
    rename!(dfINDPOB,[Symbol(headINDPOB[c]) for c in 1:length(headINDPOB)])
    if ind==indPob[1]
        dfIndreq=getdf2015data(ar,dfIPob);
    elseif ind==indPob[2]
        dfIndreq=getdf2015data(ar,dfIPobEx);
    elseif ind==indPob[3]
        dfIndreq=getdf2015data(ar,dfIPobMod);
    elseif ind==indPob[4]
        dfIndreq=getdf2015data(ar,dfIVCS);
    elseif ind==indPob[5]
        dfIndreq=getdf2015data(ar,dfIVI);
    elseif ind==indPob[6]
        dfIndreq=getdf2015data(ar,dfINPV);
    elseif ind==indPob[7]
        dfIndreq=getdf2015data(ar,dfIRe);
    elseif ind==indPob[8]
        dfIndreq=getdf2015data(ar,dfICASS);
    elseif ind==indPob[9]
        dfIndreq=getdf2015data(ar,dfICASSO);
    elseif ind==indPob[10]
        dfIndreq=getdf2015data(ar,dfICCEV);
    elseif ind==indPob[11]
        dfIndreq=getdf2015data(ar,dfICASBV);
    elseif ind==indPob[12]
        dfIndreq=getdf2015data(ar,dfICAA);
    elseif ind==indPob[13]
        dfIndreq=getdf2015data(ar,dfIUCS);
    elseif ind==indPob[14]
        dfIndreq=getdf2015data(ar,dfITCS);
    elseif ind==indPob[15]
        dfIndreq=getdf2015data(ar,dfIIILB);
    elseif ind==indPob[16]
        dfIndreq=getdf2015data(ar,dfIIILBM);
    elseif ind=="All"
        dfIndreq=getdf2015data(ar,dfINDPOB);
    end
    return dfIndreq
end

function getdf2015data(ar::XLSX.Worksheet,dfaux:: DataFrame,estado::String)
        munPob=ar["E9:E2465"]
        estPob=ar["C9:C2465"]
        dfmunPob=DataFrame(munPob,["Municipio"])
        dfestPob=DataFrame(estPob,["Estado"])
        estado1=estado
        if estado=="Ciudad de México"
	          estado="Distrito Federal"
        elseif estado=="Veracruz de Ignacio de la Llave"
	          estado="Veracruz"
         elseif estado=="Michoacán de Ocampo"
	          estado="Michoacán"
        end
        dfPob=hcat(dfmunPob,dfaux)
        dfPob=hcat(dfestPob,dfPob)
        notoddAR=collect(2:2:length(names(dfaux)))
        headPob2015=[]
        sin=[]
        for i in 1:length(names(dfPob))
            push!(sin,Symbol(names(dfPob)[i]))
        end
        for n in notoddAR
            push!(headPob2015,sin[n])
        end
        select(dfPob,Not(headPob2015))
        munau=[]
        for k in keys(diccionario_municipios[parse(Int64,get(diccionario_estados,estado1,"Not founded"))])
          push!(munau,k)
        end
        dfmunIM=DataFrame(Municipio=munau)
        dfmunIM=innerjoin(dfmunIM,dfPob,on=:Municipio)
        dfmunIM=innerjoin(dfmunIM,DataFrame(Estado=[estado]),on=:Estado)
        return select(dfmunIM,Not(:Estado))
end

function downloadIIM()
    HTTP.download(murl,path)
end

function getIndIM(field:: String,estado::String)
    dfIIM=DataFrame(ExcelFiles.load(path*"IIM2010_BASEMUN.xls","IIM2010_BASEMUN"))
    ar=[:ENT,:MUN]
    select!(dfIIM,Not(ar))
    rename!(dfIIM,:NOM_MUN=>"Municipio")
    rename!(dfIIM,:NOM_ENT=>"Estado")
    if field==names(dfIIM)[2]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[2])])
    elseif field==names(dfIIM)[3]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[3])])
    elseif field==names(dfIIM)[4]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[4])])
    elseif field==names(dfIIM)[5]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[5])])
    elseif field==names(dfIIM)[6]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[6])])
    elseif field==names(dfIIM)[7]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[7])])
    elseif field==names(dfIIM)[8]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[8])])
    elseif field==names(dfIIM)[9]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[9])])
    elseif field==names(dfIIM)[10]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[10])])
    elseif field==names(dfIIM)[11]
        dfret=select(dfIIM,[:Estado,:Municipio,Symbol(names(dfIIM)[11])])
    elseif field=="All"
        dfret=dfIIM
    end
    munau=[]
    for k in keys(diccionario_municipios[parse(Int64,get(diccionario_estados,estado,"Not founded"))])
      push!(munau,k)
    end
    dfmunIM=DataFrame(Municipio=munau)
    dfmunIM=innerjoin(dfmunIM,dfret,on=:Municipio)
    dfmunIM=innerjoin(dfmunIM,DataFrame(Estado=[estado]),on=:Estado)
    return select(dfmunIM,Not(:Estado))
end

function getIDH(componente::String,estado::String)
    adh=XLSX.readxlsx(path*"Indice de Desarrollo Humano Municipal 2010 2015.xlsx")["IDH municipal 2015"]
    headIDH=adh["C8:L8"]
    idhData=adh["C10:L2469"]
    tfd=DataFrame(idhData)
    estado1=estado
    if estado=="Veracruz de Ignacio de la Llave"
	      estado="Veracruz"
    elseif estado=="Michoacán de Ocampo"
	      estado="Michoacán"
    end
    for i in 1:length(headIDH)
        rename!(tfd,Symbol("x$i")=>(headIDH[i]))
    end
    tfd=dropmissing(tfd)
    if componente==names(tfd)[3]
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[3])])
    elseif componente==names(tfd)[4]
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[4])])
    elseif componente=="Ingreso per cápita anual"
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[5])])
    elseif componente==names(tfd)[6]
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[6])])
    elseif componente==names(tfd)[7]
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[7])])
    elseif componente==names(tfd)[8]
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[8])])
    elseif componente==names(tfd)[9]
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[9])])
    elseif componente=="IDH"
        dfret=select(tfd,[:Estado,:Municipio,Symbol(names(tfd)[10])])
    elseif componente=="All"
        dfret=tfd
    end
    munau=[]
    for k in keys(diccionario_municipios[parse(Int64,get(diccionario_estados,estado1,"Not founded"))])
      push!(munau,k)
    end
    dfmunIM=DataFrame(Municipio=munau)
    dfmunIM=innerjoin(dfmunIM,dfret,on=:Municipio)
    dfmunIM=innerjoin(dfmunIM,DataFrame(Estado=[estado]),on=:Estado)
    return select(dfmunIM,Not(:Estado))
end

function getIDH(componente::String)
    adh=XLSX.readxlsx(path*"Indice de Desarrollo Humano Municipal 2010 2015.xlsx")["IDH municipal 2015"]
    headIDH=adh["D8:L8"]
    idhData=adh["D10:L2469"]
    tfd=DataFrame(idhData)
    for i in 1:length(headIDH)
        rename!(tfd,Symbol("x$i")=>(headIDH[i]))
    end
    tfd=dropmissing(tfd)
    if componente==names(tfd)[2]
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[2])])
    elseif componente==names(tfd)[3]
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[3])])
    elseif componente=="Ingreso per cápita anual"
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[4])])
    elseif componente==names(tfd)[5]
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[5])])
    elseif componente==names(tfd)[6]
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[6])])
    elseif componente==names(tfd)[7]
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[7])])
    elseif componente==names(tfd)[8]
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[8])])
    elseif componente=="IDH"
        dfret=select(tfd,[:Municipio,Symbol(names(tfd)[9])])
    elseif componente=="All"
        dfret=tfd
    end
    return dfret
end

function codPos_municipio(estado::String,municipio::String)
    headCP=[:d_codigo,:d_asenta,:d_tipo_asenta,:D_mnpio]
    if estado=="Aguascalientes"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Aguascalientes"])["A2:D1357"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Baja California"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Baja_California"])["A2:D2437"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Baja California Sur"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Baja_California_Sur"])["A2:D988"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Campeche"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Campeche"])["A2:D1310"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Coahuila de Zaragoza"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Coahuila_de_Zaragoza"])["A2:D3967"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Colima"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Colima"])["A2:D834"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Chiapas"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Chiapas"])["A2:D7639"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Chihuahua"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Chihuahua"])["A2:D9561"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Ciudad de México"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Distrito_Federal"])["A2:D1516"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Durango"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Durango"])["A2:D7049"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Guanajuato"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Guanajuato"])["A2:D10050"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Guerrero"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Guerrero"])["A2:D4793"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Hidalgo"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Hidalgo"])["A2:D6090"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Jalisco"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Jalisco"])["A2:D5741"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="México"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["México"])["A2:D8158"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Michoacán de Ocampo"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Michoacán_de_Ocampo"])["A2:D10182"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Morelos"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Morelos"])["A2:D1744"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Nayarit"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Nayarit"])["A2:D1980"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Nuevo León"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Nuevo_León"])["A2:D4878"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Oaxaca"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Oaxaca"])["A2:D6063"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Puebla"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Puebla"])["A2:D5428"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Querétaro"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Querétaro"])["A2:D3100"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Quintana Roo"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Quintana_Roo"])["A2:D1150"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="San Luis Potosí"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["San_Luis_Potosí"])["A2:D5982"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Sinaloa"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Sinaloa"])["A2:D4165"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Sonora"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Sonora"])["A2:D8734"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Tabasco"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Tabasco"])["A2:D2645"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Tamaulipas"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Tamaulipas"])["A2:D3281"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Tlaxcala"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Tlaxcala"])["A2:D1443"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Veracruz de Ignacio de la Llave"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Veracruz_de_Ignacio_de_la_Llave"])["A2:D8950"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Yucatán"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Yucatán"])["A2:D1643"]
      dfCPA=DataFrame(CPdata,headCP)
    elseif estado=="Zacatecas"
      CPdata=(XLSX.readxlsx(path*"CPdescarga.xlsx")["Zacatecas"])["A2:D1846"]
      dfCPA=DataFrame(CPdata,headCP)
    end
        dfaux=DataFrame(D_mnpio=[municipio])
        return innerjoin(dfaux,dfCPA,on= :D_mnpio)
end

function getIndINEGI(estado::String,indicador::String)
    if Sys.iswindows()
      dfIndINE=CSV.read(pathINEGI*estado*"\\datos_"*indicador*"_"*estado*".csv",DataFrame);

    elseif Sys.islinux()
      dfIndINE=CSV.read(pathINEGI*estado*"/datos_"*indicador*"_"*estado*".csv",DataFrame);
    end
    return dfIndINE
end

function getExtTer(estado:: String)
  headET=[:Municipio,:Total]
  if estado=="Aguascalientes"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B11"],headET)
  elseif estado=="Baja California"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B7"],headET)
  elseif estado=="Baja California Sur"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B6"],headET)
  elseif estado=="Campeche"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B13"],headET)
  elseif estado=="Coahuila de Zaragoza"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B39"],headET)
  elseif estado=="Colima"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B11"],headET)
  elseif estado=="Chiapas"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B125"],headET)
  elseif estado=="Chihuahua"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B68"],headET)
  elseif estado=="Ciudad de México"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B17"],headET)
  elseif estado=="Durango"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B40"],headET)
  elseif estado=="Guanajuato"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B47"],headET)
  elseif estado=="Guerrero"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B82"],headET)
  elseif estado=="Hidalgo"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B85"],headET)
  elseif estado=="Jalisco"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B126"],headET)
  elseif estado=="México"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B126"],headET)
  elseif estado=="Michoacán de Ocampo"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B114"],headET)
  elseif estado=="Morelos"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B37"],headET)
  elseif estado=="Nayarit"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B21"],headET)
  elseif estado=="Nuevo León"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B52"],headET)
  elseif estado=="Oaxaca"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B571"],headET)
  elseif estado=="Puebla"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B218"],headET)
  elseif estado=="Querétaro"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B19"],headET)
  elseif estado=="Quintana Roo"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B10"],headET)
  elseif estado=="San Luis Potosí"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B59"],headET)
  elseif estado=="Sinaloa"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B19"],headET)
  elseif estado=="Sonora"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B73"],headET)
  elseif estado=="Tabasco"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B18"],headET)
  elseif estado=="Tamaulipas"
      dfET=DataFrame((XLSX.readxlsx(path*"Extension territorial.xlsx")[estado])["A2:B44"],headET)
  elseif estado=="Tlaxcala"
      dfET=DataFrame((XLSX.readxlsx(path*"\\"*"Extension territorial.xlsx")[estado])["A2:B61"],headET)
  elseif estado=="Veracruz de Ignacio de la Llave"
      dfET=DataFrame((XLSX.readxlsx(path*"\\"*"Extension territorial.xlsx")[estado])["A2:B213"],headET)
  elseif estado=="Yucatán"
      dfET=DataFrame((XLSX.readxlsx(path*"\\"*"Extension territorial.xlsx")[estado])["A2:B106"],headET)
  elseif estado=="Zacatecas"
      dfET=DataFrame((XLSX.readxlsx(path*"\\"*"Extension territorial.xlsx")[estado])["A2:B59"],headET)
  end
      return dfET
end

end #module


text:
using ACE, Test, Printf, LinearAlgebra, StaticArrays, BenchmarkTools

##
@testset "ACE.jl" begin
    # ------------------------------------------
    #   basic polynomial basis building blocks
    include("polynomials/test_ylm.jl")
    include("testing/test_wigner.jl")
    include("polynomials/test_transforms.jl")
    include("polynomials/test_orthpolys.jl")

    # --------------------------------------------
    # core permutation-invariant functionality
    include("test_scal1pbasis.jl")
    include("test_1pbasis.jl")
    include("test_pibasis.jl")

    # ------------------------
    #   rotation_invariance
    include("test_cg.jl")
    include("test_symmbasis.jl")
    include("test_euclvec.jl")

    # Model tests
    include("test_linearmodel.jl")

end

    # -----------------------------------------
    #    old tests to be re-introduced - maybe
    # include("test_real.jl")
    # include("test_orth.jl")
    # include("bonds/test_cylindrical.jl")
    # include("bonds/test_fourier.jl")
    # include("bonds/test_envpairbasis.jl")


text:
"""
    CSV.write(file, table; kwargs...) => file
    table |> CSV.write(file; kwargs...) => file

Write a [Tables.jl interface input](https://github.com/JuliaData/Tables.jl) to a csv file, given as an `IO` argument or `String`/FilePaths.jl type representing the file name to write to.

Supported keyword arguments include:
* `delim::Union{Char, String}=','`: a character or string to print out as the file's delimiter
* `quotechar::Char='"'`: ascii character to use for quoting text fields that may contain delimiters or newlines
* `openquotechar::Char`: instead of `quotechar`, use `openquotechar` and `closequotechar` to support different starting and ending quote characters
* `escapechar::Char='"'`: ascii character used to escape quote characters in a text field
* `missingstring::String=""`: string to print for `missing` values
* `dateformat=Dates.default_format(T)`: the date format string to use for printing out `Date` & `DateTime` columns
* `append=false`: whether to append writing to an existing file/IO, if `true`, it will not write column names by default
* `writeheader=!append`: whether to write an initial row of delimited column names, not written by default if appending
* `header`: pass a list of column names (Symbols or Strings) to use instead of the column names of the input table
* `newline='\\n'`: character or string to use to separate rows (lines in the csv file)
* `quotestrings=false`: whether to force all strings to be quoted or not
* `decimal='.'`: character to use as the decimal point when writing floating point numbers
"""
function write end

_seekstart(p::Base.Process) = return
_seekstart(io::IO) = seekstart(io)

function with(f::Function, io::IO, append)
    !append && _seekstart(io)
    f(io)
end

function with(f::Function, io::Union{Base.TTY, Base.Pipe, Base.PipeEndpoint}, append)
    # seeking in an unbuffered pipe makes no sense...
    f(io)
end

function with(f::Function, file::String, append)
    open(file, append ? "a" : "w") do io
        f(io)
    end
end

const VALUE_BUFFERS = [IOBuffer()]

function _reset(io::IOBuffer)
    io.ptr = 1
    io.size = 0
end

function bufferedwrite(io, x::Union{Float16, Float32, Float64, BigFloat}, df, decimal)
    dot = false
    n = 0
    if isnan(x)
        print(io, "NaN")
        return false
    end
    x < 0 && print(io,'-')
    if isinf(x)
        print(io, "Inf")
        return false
    end
    @static if VERSION < v"1.1.0"
        buffer = Base.Grisu.DIGITSs[Threads.threadid()]
    else
        buffer = Base.Grisu.getbuf()
    end
    len, pt, neg = Base.Grisu.grisu(x,Base.Grisu.SHORTEST,n,buffer)
    pdigits = pointer(buffer)
    e = pt-len
    k = -9<=e<=9 ? 1 : 2
    if -pt > k+1 || e+dot > k+1
        # => ########e###
        unsafe_write(io, pdigits+0, len)
        print(io, 'e')
        print(io, string(e))
        return false
    elseif pt <= 0
        # => 0.000########
        print(io, "0$decimal")
        while pt < 0
            print(io, '0')
            pt += 1
        end
        unsafe_write(io, pdigits+0, len)
    elseif e >= dot
        # => ########000.
        unsafe_write(io, pdigits+0, len)
        while e > 0
            print(io, '0')
            e -= 1
        end
        if dot
            print(io, decimal)
        end
    else # => ####.####
        unsafe_write(io, pdigits+0, pt)
        print(io, decimal)
        unsafe_write(io, pdigits+pt, len-pt)
    end
    return false
end

function bufferedwrite(io, val::Number, df, decimal)
    print(io, val)
    return false
end

getvalue(x, df) = x
getvalue(x::T, df) where {T <: Dates.TimeType} = Dates.format(x, df === nothing ? Dates.default_format(T) : df)

function bufferedwrite(io, val, df, decimal)
    VALUE_BUFFER = VALUE_BUFFERS[Threads.threadid()]
    _reset(VALUE_BUFFER)
    print(VALUE_BUFFER, getvalue(val, df))
    return true
end

function bufferedescape(io, delim, oq, cq, e, newline, quotestrings)
    VALUE_BUFFER = VALUE_BUFFERS[Threads.threadid()]
    n = position(VALUE_BUFFER)
    n == 0 && return
    needtoescape, needtoquote = check(n, delim, oq, cq, newline)
    needtoquote |= quotestrings
    seekstart(VALUE_BUFFER)
    if (needtoquote | needtoescape)
        if needtoescape
            Base.write(io, oq)
            buf = VALUE_BUFFER.data
            for i = 1:n
                @inbounds b = buf[i]
                if b === cq || b === oq
                    Base.write(io, e, b)
                else
                    Base.write(io, b)
                end
            end
            Base.write(io, cq)
        else
            Base.write(io, oq, VALUE_BUFFER, cq)
        end
    else
        Base.write(io, VALUE_BUFFER)
    end
    return
end

function check(n, delim::Char, oq, cq, newline::Char)
    needtoescape = false
    buf = VALUE_BUFFERS[Threads.threadid()].data
    @inbounds needtoquote = buf[1] === oq
    d = delim % UInt8
    new = newline % UInt8
    @simd for i = 1:n
        @inbounds b = buf[i]
        needtoquote |= (b === d) | (b === new)
        needtoescape |= b === cq
    end
    return needtoescape, needtoquote
end

function check(n, delim::Char, oq, cq, newline::String)
    needtoescape = false
    buf = VALUE_BUFFERS[Threads.threadid()].data
    @inbounds needtoquote = buf[1] === oq
    d = delim % UInt8
    @simd for i = 1:n
        @inbounds b = buf[i]
        needtoquote |= b === d
        needtoescape |= b === cq
    end
    needtoquote |= occursin(newline, String(buf[1:n]))
    return needtoescape, needtoquote
end

function check(n, delim::String, oq, cq, newline::Char)
    needtoescape = false
    buf = VALUE_BUFFERS[Threads.threadid()].data
    @inbounds needtoquote = buf[1] === oq
    new = newline % UInt8
    @simd for i = 1:n
        @inbounds b = buf[i]
        needtoquote |= b === new
        needtoescape |= b === cq
    end
    needtoquote |= occursin(delim, String(buf[1:n]))
    return needtoescape, needtoquote
end

function check(n, delim::String, oq, cq, newline::String)
    needtoescape = false
    buf = VALUE_BUFFERS[Threads.threadid()].data
    @inbounds needtoquote = buf[1] === oq
    @simd for i = 1:n
        @inbounds b = buf[i]
        needtoescape |= b === cq
    end
    str = String(buf[1:n])
    needtoquote |= occursin(delim, str)
    needtoquote |= occursin(newline, str)
    return needtoescape, needtoquote
end

write(file; kwargs...) = x->write(file, x; kwargs...)
function write(file, itr;
    quotechar::Char='"',
    openquotechar::Union{Char, Nothing}=nothing,
    closequotechar::Union{Char, Nothing}=nothing,
    escapechar::Char='"',
    newline::Union{Char, String}='\n',
    decimal::Char='.',
    kwargs...)
    (isascii(something(openquotechar, quotechar)) && isascii(something(closequotechar, quotechar)) && isascii(escapechar)) || throw(ArgumentError("quote and escape characters must be ASCII characters "))
    oq, cq = openquotechar !== nothing ? (openquotechar % UInt8, closequotechar % UInt8) : (quotechar % UInt8, quotechar % UInt8)
    e = escapechar % UInt8
    rows = Tables.rows(itr)
    sch = Tables.schema(rows)
    return write(sch, rows, file, oq, cq, e, newline, decimal; kwargs...)
end

function printheader(io, header, delim, oq, cq, e, newline)
    cols = length(header)
    for (col, nm) in enumerate(header)
        bufferedwrite(io, string(nm), nothing, UInt8('.')) && bufferedescape(io, delim, oq, cq, e, newline, false)
        Base.write(io, ifelse(col == cols, newline, delim))
    end
    return
end

function write(sch::Tables.Schema{names}, rows, file, oq, cq, e, newline, decimal;
    delim::Union{Char, String}=',',
    missingstring::AbstractString="",
    dateformat=nothing,
    append::Bool=false,
    writeheader::Bool=!append,
    header::Vector=String[],
    quotestrings::Bool=false,
    kwargs...) where {names}
    colnames = isempty(header) ? names : header
    cols = length(colnames)
    with(file, append) do io
        writeheader && printheader(io, colnames, delim, oq, cq, e, newline)
        for row in rows
            Tables.eachcolumn(sch, row) do val, col, nm
                bufferedwrite(io, coalesce(val, missingstring), dateformat, decimal) && bufferedescape(io, delim, oq, cq, e, newline, quotestrings)
                Base.write(io, ifelse(col == cols, newline, delim))
            end
        end
    end
    return file
end

# handle unknown schema case
function write(::Nothing, rows, file, oq, cq, e, newline, decimal;
    delim::Union{Char, String}=',',
    missingstring::AbstractString="",
    dateformat=nothing,
    append::Bool=false,
    writeheader::Bool=!append,
    header::Vector=String[],
    quotestrings::Bool=false,
    kwargs...)
    state = iterate(rows)
    if state === nothing
        if writeheader && !isempty(header)
            with(file, append) do io
                printheader(io, header, delim, oq, cq, e, newline)
            end
        end
        return file
    end
    row, st = state
    names = isempty(header) ? propertynames(row) : header
    sch = Tables.Schema(names, nothing)
    cols = length(names)
    with(file, append) do io
        writeheader && printheader(io, names, delim, oq, cq, e, newline)
        while true
            Tables.eachcolumn(sch, row) do val, col, nm
                bufferedwrite(io, coalesce(val, missingstring), dateformat, decimal) && bufferedescape(io, delim, oq, cq, e, newline, quotestrings)
                Base.write(io, ifelse(col == cols, newline, delim))
            end
            state = iterate(rows, st)
            state === nothing && break
            row, st = state
        end
    end
    return file
end