text:
push!(LOAD_PATH,"../src/")
using Dates, Printf
include("Utilidades.jl")
include("Constants.jl")

using InfoZIP, HTTP,  StringEncodings, JSON, StringDistances
export poblacion_mexico, poblacion_entidad, poblacion_municipio, poblacion_todos_municipios, poblacion_todas_entidades, clave,idh,indicadores_pobreza_porcentaje,indicadores_pobreza, indicadores_pobreza_porcentaje_todos,indicadores_pobreza_todos, fechahoy, int_migratoria, geografia, codigos_postales, int_migratoria_todos, geografia_todos_municipios,tasas_vitales,edad_municipios, edad_entidades, similitud_region, similitud_entidad, similitud_municipio, codigos_postales_todos,idh_todos_municipios, edad_municipios_todos

#TODO nombre
"""
    fechahoy()::String

Crea un string con la fecha de hoy utilizando el formato "yyyymmdd". Año con cuarto dígitos, mes y día con dos.

# Ejemplo
```julia-repl
julia> fechahoy()
"20210112"
```
"""
function fechahoy()::String
  string(Dates.format(DateTime(Dates.today()), "yyyymmdd"))
end

# Verifica que el token este presente
# o que este asignado como variable de entorno.
function token_check(token_INEGI::String)::String
  if token_INEGI == ""
    try
      token_INEGI = ENV["token_INEGI"]
    catch e
      error("'token_INEGI' no encotrado. Proporcionala directamente o asignala de la siquiente manera 'ENV[\"token_INEGI\"] = <tu token>'")
    end
  end
  return token_INEGI
end

"""
    poblacion_mexico(token_INEGI::String="")::DataFrame

Regresa un `DataFrame` con los datos más recientes, a nivel nacional, proporcionados por la API de Indicadores del INEGI.
Requiere el token (`token_INEGI`) de la API, puede obtenerse [aquí.](https://www.inegi.org.mx/app/api/indicadores/interna_v1_1/tokenVerify.aspx)
Se puede proporcionar el token directamente o por medio de una [variable de entorno](https://docs.julialang.org/en/v1/base/base/#Base.ENV) llamada de la misma manera, `token_INEGI`.

# Ejemplo
```julia-repl
julia> ENV["token_INEGI"] = "00000000-0000-0000-0000-000000000000"
"00000000-0000-0000-0000-000000000000"

julia> poblacion_mexico()
1×9 DataFrame
 Row │ lugar   total      hombres   mujeres   porcentaje_hombres  porcentaje_mujeres  porcentaje_indigena  densidad_poblacion  extesion_territorial
     │ String  Int64      Int64     Int64     Float64             Float64             Float64              Float64             Float64
─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ México  126014024  61473390  64540634              48.783              51.217              21.4965             64.2717             1.96065e6
```
"""
function poblacion_mexico(token_INEGI::String="")::DataFrame
  token_INEGI = token_check(token_INEGI)
  url = "https://www.inegi.org.mx/app/api/indicadores/desarrolladores/jsonxml/INDICATOR/6207019014,1002000001,1002000002,1002000003,6207020032,6207020033,3105001001/es/0700/true/BISE/2.0/"*token_INEGI*"?type=json"
  return parse_poblacion(jsonparse(url), "México")
end

"""
    poblacion_entidad(cve_entidad::String, token_INEGI::String="")::poblacion

Regresa un una `DataFrame` con los datos más recientes, por entidad federativa, proporcionados por la API de Indicadores del INEGI.
Requiere el token (`token_INEGI`) de la API, puede obtenerse [aquí.](https://www.inegi.org.mx/app/api/indicadores/interna_v1_1/tokenVerify.aspx)
Se puede proporcionar el token directamente o por medio de una [variable de entorno](https://docs.julialang.org/en/v1/base/base/#Base.ENV), de la siguiente manera.

```julia-repl
julia> ENV["token_INEGI"] = "00000000-0000-0000-0000-000000000000"
```
El `DataFrame` resultante contiene los siguientes datos.

- lugar
- población total
- densidad de población (habitantes por kilómetro cuadrado)
- extensión terrotorial (kilómetros cuadrados)
- población total hombres
- población total mujeres
- porcentaje de hombres
- porcentaje de mujeres
- porcentaje de población que se considera indígena

!!! note
    ### Área geoestadística estatal (AGEE)
    La entidad federativa se codifica de acuerdo con el orden alfabético de sus nombres _oficiales_, con una longitud de dos dígitos, a partir del 01 en adelante, según el número de entidades federativas que dispongan las leyes vigentes; en este momento son 32 entidades federativas (Aguascalientes 01, Baja California 02,... y Zacatecas 32).
    Las puedes consultar [aquí.](https://www.inegi.org.mx/app/ageeml/)

   Clave Entidad | Entidad
   --- | ---
    01 | Aguascalientes
    02 | Baja California
    03 | Baja California Sur
    ⋮  |⋮
    30 | Veracruz de Ignacio de la Llave
    31 | Yucatán
    32 | Zacatecas

# Ejemplo
```julia-repl
julia> popu = poblacion_entidad("31", token)
1×9 DataFrame
 Row │ lugar    total    hombres  mujeres  porcentaje_hombres  porcentaje_mujeres  porcentaje_indigena  densidad_poblacion  extesion_territorial
     │ String   Int64    Int64    Int64    Float64             Float64             Float64              Float64             Float64
─────┼───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ Yucatán  2320898  1140279  1180619             49.1309             50.8691              65.4035             58.7206               39524.4
```
"""
function poblacion_entidad(cve_entidad::String, token_INEGI::String="")::DataFrame
  token_INEGI = token_check(token_INEGI)
   cve_entidad, cve_municipio= check(cve_entidad,"")
  try
    global lugar = ENTIDADES[cve_entidad]
  catch e
    error("Verifica tu clave de entidad. Debe de ser de dos dígitos en el rango [01, 32]. cve_entidad '$cve_entidad' no existe.")
  end

  url = "https://www.inegi.org.mx/app/api/indicadores/desarrolladores/jsonxml/INDICATOR/1002000001,1002000002,1002000003,6207019014,6207020032,6207020033,3105001001/es/"*cve_entidad*"/true/BISE/2.0/"*token_INEGI*"?type=json"
  return parse_poblacion(jsonparse(url), lugar)
end

"""
    poblacion_municipio(cve_entidad::String, cve_municipio::String, token_INEGI::String="")::DataFrame

Regresa un `DataFrame` con los datos más recientes, por municipio, proporcionados por la API de Indicadores del INEGI.
Requiere el token (`token_INEGI`) de la API, puede obtenerse [aquí.](https://www.inegi.org.mx/app/api/indicadores/interna_v1_1/tokenVerify.aspx)
Se puede proporcionar el token directamente o por medio de una [variable de entorno](https://docs.julialang.org/en/v1/base/base/#Base.ENV) llamada de la misma manera, `token_INEGI`.

!!! note
    ### Área geoestadística municipal (AGEM)
    La clave del municipio está formada por tres números que se asignan de manera ascendente  a  partir  del  001,  de  acuerdo  con  el  orden  alfabético  de  los  nombres  de  los  municipios,  aunque  a  los  creados  posteriormente  a  la  clavificación  inicial,  se  les  asigna  la  clave  geoestadística  conforme se vayan creando.
    Las puedes consultar [aquí.](https://www.inegi.org.mx/app/ageeml/)

    Clave Entidad | Nombre Entidad | Clave Municipio | Nombre Municipio
      --- | --- | --- | ---
    01|Aguascalientes|001 |Aguascalientes
    01|Aguascalientes|002 |Asientos
    01|Aguascalientes|003 |Calvillo
    ⋮|⋮|⋮|⋮
    32|Zacatecas|056|Zacatecas
    32|Zacatecas|057|Trancoso
    32|Zacatecas|058|Santa María de la Paz

# Ejemplo
```julia-repl
julia> ENV["token_INEGI"] = "00000000-0000-0000-0000-000000000000"
"00000000-0000-0000-0000-000000000000"

julia> popu = poblacion_municipio("01", "002")
1×9 DataFrame
 Row │ lugar                     total  hombres  mujeres  porcentaje_hombres  porcentaje_mujeres  porcentaje_indigena  densidad_poblacion  extesion_territorial
     │ String                    Int64  Int64    Int64    Float64             Float64             Float64              Float64             Float64
─────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ Aguascalientes, Asientos  51536    25261    26275             49.0162             50.9838              3.63938             93.8709               549.009
```
"""
function poblacion_municipio(cve_entidad::String, cve_municipio::String, token_INEGI::String="")::DataFrame
  token_INEGI = token_check(token_INEGI)
     cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)
  try
    global estado = ENTIDADES[cve_entidad]
  catch e
    error("Verifica tu clave de entidad. Debe de ser de dos dígitos en el rango [01, 32]. cve_entidad '$cve_entidad' no existe.")
  end

  try
    global municipio = MUNICIPIOS[cve_entidad*cve_municipio]
  catch e
    error("Verifica tu clave de municipio. Debe de ser de tres dígitos en el rango [001, 570]. cve_municipio '$cve_municipio' no existe.")
  end

  url = "https://www.inegi.org.mx/app/api/indicadores/desarrolladores/jsonxml/INDICATOR/1002000001,6207019014,6207020032,6207020033,3105001001/es/070000"*cve_entidad*"0"*cve_municipio*"/true/BISE/2.0/"*token_INEGI*"?type=json"

  lugar = estado * ", " * municipio

  return parse_poblacion(jsonparse(url), lugar)
end

#TODO documentación
function parse_poblacion(datos::Dict, lugar::String)::DataFrame

  indicadores = Dict{String, Float64}()

  for dato in datos["Series"]
    indicadores[dato["INDICADOR"]] = tryparse(Float64, dato["OBSERVATIONS"][end]["OBS_VALUE"])
  end

  # indicadores INEGI,
  # total =   1002000001 hombres = 1002000002 mujeres = 1002000003
  # densdad = 3105001001 (hab/km^2) porhom = 6207020032 pormuj = 6207020033
  # indígena= 6207019014

  tot = trunc(Int64, indicadores["1002000001"])# población total
  den = indicadores["3105001001"]              # densidad de población
  ext = tot/den                                # extensión territorial
  porcen_hom = indicadores["6207020032"]       # porcentaje de hombres
  porcen_muj = indicadores["6207020033"]       # porcentaje de mujeres
  porcen_ind = indicadores["6207019014"]       # porcentaje de población que se considera indígena
  hom =trunc(Int64,round(0.01*tot*porcen_hom)) # población hombres
  muj =trunc(Int64,round(0.01*tot*porcen_muj)) # población mujeres

  df = DataFrame(lugar=[lugar], total=[tot], hombres=[hom], mujeres=[muj],
    porcentaje_hombres=[porcen_hom], porcentaje_mujeres=[porcen_muj],
    porcentaje_indigena=[porcen_ind], densidad_poblacion=[den], extesion_territorial=[ext])

  return df
end

"""
    poblacion_todos_municipios()::DataFrame

Regresa un `DataFrame` con los datos poblacionales de _todos_ los municipios.

- nombre del lugar
- clave de entidad
- nombre de la entidad
- clave de municipio
- nombre de municipio
- población total
- densidad de población (habitantes por kilómetro cuadrado)
- extensión territorial (kilómetros cuadrados)
- población total hombres
- población total mujeres
- porcentaje de hombres
- porcentaje de mujeres
- porcentaje de población que se considera indígena

# Ejemplo
```julia-repl
julia> poblacion_todos_municipios()
2469×11 DataFrame
  Row │ entidad  entidad_nombre  municipio  municipio_nombre              total   densidad   hombres   mujeres   porcentaje_hombres  porcentajes_mujeres ⋯
      │ String   String          String     String                        Int64   Float64    Int64     Int64     Float64             Float64             ⋯
──────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes  001        Aguascalientes                797010   744.58     386429   410581.0             48.5335              51.4665 ⋯
    2 │ 01       Aguascalientes  002        Asientos                      797010   744.58     386429   410581.0             48.5335              51.4665
    ⋮   │    ⋮           ⋮             ⋮                   ⋮                   ⋮           ⋮         ⋮         ⋮              ⋮                    ⋮          ⋱
 2468 │ 32       Zacatecas       057        Trancoso                      138176   331.026     66297    71879.0             48.482               51.518  ⋯
 2469 │ 32       Zacatecas       058        Santa María de la Paz          16934    87.9192     8358     8576.0             48.962               51.038
```
"""
function poblacion_todos_municipios()::DataFrame
  path = "poblacion_municipios.csv"
  return get_info(path, [String, String, String, String, Int64, Float64, Float64, Int64, Int64, Float64, Float64, Float64, Float64])
end

"""
    poblacion_todas_entidades()::DataFrame

Regresa un `DataFrame` con los datos poblacionales de _todas_ las entidades.

- clave de entidad
- nombre oficial de la entidad
- población total
- densidad de población (habitantes por kilómetro cuadrado)
- extensión territorial (kilómetros cuadrados)
- población total hombres
- población total mujeres
- porcentaje de hombres
- porcentaje de mujeres
- porcentaje de población que se considera indígena

# Ejemplo
```julia-repl
julia> poblacion_todas_entidades()
32×10 DataFrame
 Row │ entidad  entidad_nombre     total     densidad   extension_territorial  hombres    mujeres    ⋯
     │ String   String             Int64     Float64    Float64                Int64      Int64      ⋯
─────┼───────────────────────────────────────────────────────────────────────────────────────────────
   1 │ 01       Aguascalientes      1425607   253.862            5615.67        69668300   72892400  ⋯
   2 │ 02       Baja California     3769020    52.7505          71450.0        190058900  186843100
   ⋮ │ ⋮             ⋮                 ⋮            ⋮           ⋮                 ⋮                      ⋱
  32 │ 32       Zacatecas           1622138    21.5494          75275.3         79105800   83108000  ⋯
```
"""
function poblacion_todas_entidades()::DataFrame
  path = "poblacion_entidades.csv"
  return get_info(path, [String, String, Int64, Float64, Float64, Int64, Int64, Float64, Float64, Float64])
end

"""
    clave(id::String)::String

Toma como parámetro el nombre de algún municipio o entidad y regresa la clave del mismo.

# Ejemplo

```julia-repl
julia> clave("Campeche")
"04"
julia> clave("Calakmul")
"010"
```
"""
function clave(id::String,prio::Bool=true)::String
  if prio == true
    if haskey(ENTIDAD_NOMBRE, id)
      return ENTIDAD_NOMBRE[id]
    end
    if haskey(MUNICIPIO_NOMBRE, id)
      return MUNICIPIO_NOMBRE[id][3:end]
    end
  else
    if haskey(MUNICIPIO_NOMBRE, id)
      return MUNICIPIO_NOMBRE[id][3:end]
    end
    if haskey(ENTIDAD_NOMBRE, id)
      return ENTIDAD_NOMBRE[id]
    end
  end
  aux = "No existe $id en los registros de entidades o estados.\n"
  ent , mun = similitud_region(id)
  ent ,mun = unique(ent), unique(mun)
  if length(ent) > 0
    aux *= "Prueba estas sugerencias: \n"
    aux *= "Entidades: \n"
  end
  for en in ent
    aux *= "\t"*en*" ➜ "*ENTIDAD_NOMBRE[en] *"\n"
  end
  if length(mun) > 0
    aux *= "Municipios: \n"
  end
  for mu in mun
    aux *= "\t"*mu*" ➜ "*MUNICIPIO_NOMBRE[mu][3:5]*"\n"
  end
  error(aux)
end

"""
    idh(cve_entidad::String, cve_municipio::String="")::DataFrame
Regresa el índice de desarrollo humano, los años promedio de escolaridad, los años esperados de escolaridad y los ingresos per cápita de una entidad o de un municipio en formato `DataFrame`, se debe especificar la clave para ambos parámetros, si solo se manda el parámetro _cve_entidad_ se regresará el IDH de la entidad. Los datos son obtenidos de la página oficial de las Naciones Unidas, se pueden consultar [aquí](https://www.mx.undp.org/content/mexico/es/home/library/poverty/idh-municipal-en-mexico--nueva-metodologia.html).
# Ejemplo
```julia-repl
julia> idh(clave("Campeche"),"003").idh*100
1-element Array{Float64,1}:
 77.50874

julia> idh(clave("Campeche"),"003").idh[1]*100
77.50874

julia> idh(clave("Campeche"),"003")
1×8 DataFrame
 Row │ ent  mun  entidad   municipio  idh       anio_promedio_escolaridad  anios_esperados_escolaridad  ingreso_per_capita
     │ Any  Any  Any       Any        Any       Any                        Any                          Any
─────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
   1 │ 04   003  Campeche  Carmen     0.775087  9.19528                    12.6746                      18552.8

julia> idh(clave("Campeche"))
1×3 DataFrame
 Row │ ent  ent_nombre  idh
     │ Any  Any         Any
─────┼───────────────────────
   1 │ 04   Campeche    0.82
```
"""
function idh(cve_entidad::String, cve_municipio::String="")::DataFrame
     cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)
  if cve_municipio == ""
    tabla = get_info("IDH_Entidad.csv",[String,String,Float64])
    try
      return filtrar(tabla,":ent == '$cve_entidad'")
    catch
      error("No se encontro la clave $cve_entidad")
    end
  else
    tabla = get_info("IDH_Municipios.csv",[String,String,String,String,Float64,Float64,Float64,Float64])
    q1 = ":ent == '$cve_entidad'"
    q2 = ":mun == '$cve_municipio'"
    try
      return filtrar(tabla,q1,q2)
    catch
      error("No se encontro la clave")
    end
  end
end
"""
    idh_todos_municipios()::DataFrame

Regresa un `DataFrame` con todos los valores agregados del índice de desarrollo humano de todos los municipios. Los datos son obtenidos de la página oficial de las Naciones Unidas, se pueden consultar [aquí.](https://www.mx.undp.org/content/mexico/es/home/library/poverty/idh-municipal-en-mexico--nueva-metodologia.html)

# Ejemplo
```julia-repl
julia> idh_todos_municipios()
2456×8 DataFrame
  Row │ ent     mun     entidad              municipio                     idh       anio_promedio_escolaridad  anios_esperados_escolaridad  ingreso_per_capita
      │ String  String  String               String                        Float64   Float64                    Float64                      Float64
──────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01      001     Aguascalientes       Aguascalientes                0.789432                    9.55307                      12.8703            17848.3
    2 │ 01      002     Aguascalientes       Asientos                      0.6615                      6.22975                      11.1364             6877.03
    3 │ 01      003     Aguascalientes       Calvillo                      0.669557                    5.95726                      10.8926             8764.94
  ⋮   │   ⋮       ⋮              ⋮                        ⋮                   ⋮                  ⋮                           ⋮                       ⋮
 2455 │ 32      057     Zacatecas            Trancoso                      0.673578                    6.24331                      10.8909             7555.7
 2456 │ 32      058     Zacatecas            Santa María de la Paz         0.668754                    5.86283                      12.143              8092.09
```
"""
function idh_todos_municipios()::DataFrame
   return get_info("IDH_Municipios.csv",[String,String,String,String,Float64,Float64,Float64,Float64])
 end

"""
    indicadores_pobreza(cve_entidad::String,cve_municipio::String)::DataFrame

Proporciona el número de personas que cumple con los indicadores de pobreza según el CONEVAL, del _municipio_ indicado.

Los datos son obtenidos de la página oficial de datos abiertos del gobierno federal de México [datos.gob.mx](https://www.datos.gob.mx/busca/dataset/indicadores-de-pobreza-municipal-2010--2015/resource/d6d6e2a8-a2e3-4e7d-84f8-dd5ea9336671)
Consulta el [Diccionario de Datos, Indicadores de pobreza municipal (2015)](@ref)

# Ejemplo
```julia-repl
julia> df = indicadores_pobreza("01", "001")
1×20 DataFrame
  Row │ entidad  entidad_nombre       municipio  municipio_nombre pobreza  pobreza_e  pobreza_m ⋯
      │ String   String               String     String           Int64    Int64      Int64     ⋯
──────┼────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes       001        Aguascalientes   224949      13650     211299 ⋯

```
"""
function indicadores_pobreza(cve_entidad::String,cve_municipio::String)::DataFrame
  cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)
  try
    estado = ENTIDADES[cve_entidad]
  catch e
    error("Verifica tu clave de entidad. Debe ser de dos digitos en el rango [01, 32]. cve_entidad '$cve_entidad' no existe.")
  end

  try
    municipio = MUNICIPIOS[cve_entidad*cve_municipio]
  catch e
    error("Verifica tu clave de municipio. Debe de ser de tres dígitos en el rango [001, 570]. cve_municipio '$cve_municipio' no existe.")
  end
  path = "indicadores_de_pobreza_municipal_2015_poblacion.csv"
  tabla = get_info(path, [String, String, String, String, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64])
  return filtrar(tabla, ":entidad=='$cve_entidad'", ":municipio=='$cve_municipio'")
end

"""
    indicadores_pobreza_porcentaje(cve_entidad::String,cve_municipio::String)::DataFrame

Proporciona el _porcentaje_ de personas que cumple con los indicadores de pobreza según el CONEVAL, del _municipio_ indicado.

Los datos son obtenidos de la página oficial de datos abiertos del gobierno federal de México [datos.gob.mx](https://www.datos.gob.mx/busca/dataset/indicadores-de-pobreza-municipal-2010--2015/resource/d6d6e2a8-a2e3-4e7d-84f8-dd5ea9336671)
Consulta el [Diccionario de Datos, Indicadores de pobreza municipal (2015)](@ref)
```julia-repl
julia> df = indicadores_pobreza_porcentaje("01", "001")
1×20 DataFrame
  Row │ entidad  entidad_nombre       municipio  municipio_nombre pobreza  pobreza_e  pobreza_m ⋯
      │ String   String               String     String           Float64  Float64    Float64   ⋯
──────┼─────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes       001        Aguascalientes      26.1        1.6       24.5 ⋯

```
"""
function indicadores_pobreza_porcentaje(cve_entidad::String,cve_municipio::String)::DataFrame
     cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)

  try
    estado = ENTIDADES[cve_entidad]
  catch e
    error("Verifica tu clave de entidad. Debe ser de dos digitos en el rango [01, 32]. cve_entidad '$cve_entidad' no existe.")
  end

  try
    municipio = MUNICIPIOS[cve_entidad*cve_municipio]
  catch e
    error("Verifica tu clave de municipio. Debe de ser de tres dígitos en el rango [001, 570]. cve_municipio '$cve_municipio' no existe.")
  end

  path = "indicadores_de_pobreza_municipal_2015_porcentaje.csv"
  tabla = get_info(path, [String, String, String, String, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64])
  return filtrar(tabla, ":entidad=='$cve_entidad'", ":municipio=='$cve_municipio'")
end

"""
    indicadores_pobreza_todos()::DataFrame

Proporciona el número de personas que cumple con los indicadores de pobreza según el CONEVAL, a nivel _nacional_ segregado por _municipios_.

Los datos son obtenidos de la página oficial de datos abiertos del gobierno federal de México [datos.gob.mx](https://www.datos.gob.mx/busca/dataset/indicadores-de-pobreza-municipal-2010--2015/resource/d6d6e2a8-a2e3-4e7d-84f8-dd5ea9336671)
Consulta el [Diccionario de Datos, Indicadores de pobreza municipal (2015)](@ref)
```julia-repl
julia> df = indicadores_pobreza_todos()
2457×20 DataFrame
  Row │ entidad  entidad_nombre       municipio  municipio_nombre pobreza  pobreza_e  pobreza_m ⋯
      │ String   String               String     String           Int64    Int64      Int64     ⋯
──────┼────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes       001        Aguascalientes   224949      13650     211299 ⋯
    2 │ 01       Aguascalientes       002        Asientos          25169       2067      23101 ⋯
   ⋮  │ ⋮             ⋮               ⋮               ⋮            ⋮            ⋮          ⋮
```
"""
function indicadores_pobreza_todos()::DataFrame
  path = "indicadores_de_pobreza_municipal_2015_poblacion.csv"
  if !isfile(path)
    global path = HTTP.download("https://raw.githubusercontent.com/mucinoab/mucinoab.github.io/dev/extras/indicadores_de_pobreza_municipal_2015_poblacion.csv", pwd())
  end
  return DataFrame(CSV.File(path, types=[String, String, String, String, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64, Int64]))
end

"""
    indicadores_pobreza_porcentaje_todos()::DataFrame

Proporciona el _porcentaje_ de personas que cumple con los indicadores de pobreza según el CONEVAL, a nivel _nacional_ segregado por _municipios_.

Los datos son obtenidos de la página oficial de datos abiertos del gobierno federal de México [datos.gob.mx](https://www.datos.gob.mx/busca/dataset/indicadores-de-pobreza-municipal-2010--2015/resource/d6d6e2a8-a2e3-4e7d-84f8-dd5ea9336671)
Consulta el [Diccionario de Datos, Indicadores de pobreza municipal (2015)](@ref)
```julia-repl
julia> df = indicadores_pobreza_porcentaje_todos()
2457×20 DataFrame
  Row │ entidad  entidad_nombre       municipio  municipio_nombre pobreza  pobreza_e  pobreza_m ⋯
      │ String   String               String     String           Float64  Float64    Float64   ⋯
──────┼─────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes       001        Aguascalientes      26.1        1.6       24.5 ⋯
    2 │ 01       Aguascalientes       002        Asientos            54.0        4.4       49.5 ⋯
   ⋮  │ ⋮             ⋮               ⋮               ⋮            ⋮            ⋮          ⋮
```
"""
function indicadores_pobreza_porcentaje_todos()::DataFrame
  path = "indicadores_de_pobreza_municipal_2015_porcentaje.csv"
  if !isfile(path)
    global path = HTTP.download("https://raw.githubusercontent.com/mucinoab/mucinoab.github.io/dev/extras/indicadores_de_pobreza_municipal_2015_porcentaje.csv", pwd())
  end
  return DataFrame(CSV.File(path, types=[String, String, String, String, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64, Float64]))
end

"""
    edad_municipios(cve_entidad::String,cve_municipio::String)::DataFrame

Da a conocer el primer y tercer cuartil, así como mediana(segundo cuartil) de las edades del municipio indicado en formato `DataFrame`.
Dichos datos de edades actualizados al año 2020 se obtuvieron de la página [INEGI.](https://www.inegi.org.mx/sistemas/Olap/Proyectos/bd/censos/cpv2020/pt.asp)
# Ejemplo
```julia-repl
julia> edad_municipios("01", "001")
1×7 DataFrame
  Row │ entidad  entidad_nombre  municipio  municipio_nombre    Q1             Q2            Q3
      │ String   String          String     String            Float64        Float64       Float64
──────┼──────────────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes  001        Aguascalientes        14           28              46
```
"""
function edad_municipios(cve_entidad::String,cve_municipio::String)::DataFrame
  cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)
  try
    estado = ENTIDADES[cve_entidad]
  catch e
    error("Verifica tu clave de entidad. Debe ser de dos digitos en el rango [01, 32]. cve_entidad '$cve_entidad' no existe.")
  end

  try
    municipio = MUNICIPIOS[cve_entidad*cve_municipio]
  catch e
    error("Verifica tu clave de municipio. Debe de ser de tres dígitos en el rango [001, 570]. cve_municipio '$cve_municipio' no existe.")
  end

  tabla = get_info("cuartiles_municipios_2020.csv",[String,String,String,String, Float64,Float64,Float64])
  return filtrar(tabla, ":entidad=='$cve_entidad'", ":municipio=='$cve_municipio'")
end

"""
    edad_municipios_todos()::DataFrame
Da a conocer el primer y tercer cuartil, así como mediana(segundo cuartil) de las edades, a nivel nacional segregado por municipio en formato `DataFrame`.
Dichos datos de edades actualizados al año 2020 se obtuvieron de la página [INEGI.](https://www.inegi.org.mx/sistemas/Olap/Proyectos/bd/censos/cpv2020/pt.asp)
# Ejemplo
```julia-repl
julia> edad_municipios_todos()
2469×7 DataFrame
  Row │ entidad  entidad_nombre  municipio  municipio_nombre    Q1             Q2            Q3
      │ String   String          String     String            Float64        Float64       Float64
──────┼──────────────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes  001        Aguascalientes        14           28              46
    2 │ 01       Aguascalientes  002        Asientos              11           24              42
    3 │ 01       Aguascalientes  003        Calvillo              12           27              46
   ⋮           ⋮             ⋮       ⋮             ⋮                   ⋮            ⋮               ⋮
```
"""
function edad_municipios_todos()::DataFrame
  return get_info("cuartiles_municipios_2020.csv",[String,String,String,String, Float64,Float64,Float64])
end

"""
    edad_entidades()::DataFrame
Da a conocer el primer y tercer cuartil, así como mediana(segundo cuartil) de las edades por entidad en formato `DataFrame`.
Dichos datos de edades actualizados al año 2020 se obtuvieron de la página [INEGI.](https://www.inegi.org.mx/sistemas/Olap/Proyectos/bd/censos/cpv2020/pt.asp)
# Ejemplo
```julia-repl
julia> edad_entidades()
32×5 DataFrame
  Row │ entidad  entidad_nombre       Q1             Q2            Q3
      │ String   String                 Float64        Float64       Float64
──────┼───────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes            13           26              43
    2 │ 02       Baja California           15           28              44
    3 │ 03       Baja California Sur       14           28              43
   ⋮        ⋮             ⋮              ⋮             ⋮                ⋮
```
"""
function edad_entidades()::DataFrame
  return get_info("cuartiles_entidades_2020.csv",[String,String,Float64,Float64,Float64])
end

"""
    int_migratoria(cve_entidad::String, cve_municipio::String ="")::Float64

Devuelve el índice de intensidad migratoria del municipio indicado. En caso de omitir el parámetro `cve_municipio`, devuelve los datos de la entidad indicada.
Datos obtenidos de [aquí](https://www.datos.gob.mx/busca/dataset/indice-absoluto-de-intensidad-migratoria-mexico--estados-unidos-2000--2010).

# Ejemplo

```julia-repl
julia> int_migratoria(clave("Campeche"),"003")
0.288

julia> int_migratoria(clave("Campeche"))
0.64
```
"""
function int_migratoria(cve_entidad::String,cve_municipio::String ="")::Float64
   cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)

  q1 = ":ent == '$cve_entidad'"
  if cve_municipio == ""
    tabla = get_info("IAIM_Entidad.csv",[String,Float64])
    try
      return filtrar(tabla,":ENT == '$cve_entidad'").IAIM[1]
    catch
      error("Clave $cve_entidad no encontrada")
    end
  else
    q2 = ":mun == '$cve_municipio'"
    tabla = get_info("IAIM_Municipio.csv",[String,String,String,String,Float64])
    try
      return filtrar(tabla,q1,q2).iaim[1]
    catch
      error("Clave no encontrada")
    end
  end
end

"""
    int_migratoria_todos()::DataFrame

Regresa un `DataFrame` con los índices de intensidad migratoria de todos los municipios.
Datos obtenidos de [aquí](https://www.datos.gob.mx/busca/dataset/indice-absoluto-de-intensidad-migratoria-mexico--estados-unidos-2000--2010).

# Ejemplo

```julia-repl
julia> int_migratoria_todos()
2469×5 DataFrame
  Row │ ent     ent_nombre      mun     mun_nom                       iaim     ⋯
      │ String  String          String  String                        Float64? ⋯
──────┼─────────────────────────────────────────────────────────────────────────
    1 │ 01      Aguascalientes  001     Aguascalientes                   1.92  ⋯
    2 │ 01      Aguascalientes  002     Asientos                         5.18
  ⋮   │   ⋮           ⋮           ⋮                  ⋮                   ⋮     ⋱
 2468 │ 32      Zacatecas       057     Trancoso                         4.141 ⋯
 2469 │ 32      Zacatecas       058     Santa María de la Paz           10.074
```
 """
 function int_migratoria_todos()::DataFrame
   return get_info("IAIM_Municipio.csv",[String,String,String,String,Float64])
 end

"""
    geografia(cve_entidad::String,cve_municipio::String ="")::DataFrame

Devuelve un `DataFrame` con la latitud, longitud y altitud promedio del municipio indicado. En caso de omitir el parámetro `cve_municipio`, devuelve los datos de la entidad indicada.
Datos obtenidos de [aquí](https://www.inegi.org.mx/app/ageeml/#).

Se pueden hacer consultas de una entidad o de un municipio.
```julia-repl
julia> geografia(clave("Oaxaca"),"003")
1×5 DataFrame
 Row │ ent  mun  latitud       longitud       altitud
     │ Any  Any  Any           Any            Any
─────┼────────────────────────────────────────────────
   1 │ 20   003  17°04´10.549  095°58´04.929  1486

julia> geografia(clave("Oaxaca"),"003").latitud
1-element Array{Any,1}:
 "17°04´10.549"

julia> geografia(clave("Oaxaca"),"003").altitud
1-element Array{Any,1}:
 "1486"

julia> geografia(clave("Campeche"))
1×4 DataFrame
 Row │ ent  latitud       longitud       altitud
     │ Any  Any           Any            Any
─────┼───────────────────────────────────────────
   1 │ 04   19°01´17.138  092°27´54.859  14

```
"""
function geografia(cve_entidad::String,cve_municipio::String ="")::DataFrame
   cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)
  tabla = get_info("lat_lon_alt_municipios.csv",[String,String,String,String,String,String,Float64])
  q1 = ":ent == '$cve_entidad'"
  if cve_municipio == ""
    try
      return seleccionar(filtrar(tabla,q1,":mun =='003'"),["1","2","5","6","7"])
    catch
      error("Clave $cve_entidad no encontrada")
    end
  else
    q2 = ":mun == '$cve_municipio'"
    try
      return filtrar(tabla,q1,q2)
    catch
      error("Clave no encontrada ")
    end
  end
end

"""
    geografia_todos_municipios()::DataFrame
    Devuelve los datos geográficos de todos los municipios(latitud, longitud y altitud promedio), puedes consultar la información [aquí](https://www.inegi.org.mx/app/ageeml/#).

# Ejemplo

```julia-repl
julia> geografia_todos_municipios()
2469×7 DataFrame
  Row │ ent     nom_ent         mun     nom_mun                       latitud  ⋯
      │ String  String          String  String                        String   ⋯
──────┼─────────────────────────────────────────────────────────────────────────
    1 │ 01      Aguascalientes  001     Aguascalientes                22°03´26 ⋯
    2 │ 01      Aguascalientes  002     Asientos                      22°17´45
  ⋮   │   ⋮           ⋮           ⋮                  ⋮                     ⋮   ⋱
 2468 │ 32      Zacatecas       057     Trancoso                      22°49´06 ⋯
 2469 │ 32      Zacatecas       058     Santa María de la Paz         21°33´55
```
"""
function geografia_todos_municipios()::DataFrame
  try
    return get_info("lat_lon_alt_municipios.csv",[String,String,String,String,String,String,Float64])
  catch
    error("Hubo un problema consiguiendo la informacion")
  end
end

"""
    codigos_postales()::DataFrame

Proporciona los _códigos postales_ del municpio indicado,
en un `DataFrame`.
Los datos son obtenidos del [Servicio Postal Mexicano.](https://www.gob.mx/correosdemexico)

# Ejemplo

```julia-repl
julia> codigos_postales("01", "001")
1×6 DataFrame
  Row │ entidad  entidad_nombre  municipio  municipio_nombre  número de códigos postales  códigos postales
      │ String   String          String     String            Int64                       String
──────┼────────────────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes  001        Aguascalientes                           599  20000;20010;20010 ⋯
```
"""
function codigos_postales(cve_entidad::String, cve_municipio::String)::DataFrame

   cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)
  try
    estado = ENTIDADES[cve_entidad]
  catch e
    error("Verifica tu clave de entidad. Debe ser de dos digitos en el rango [01, 32]. cve_entidad '$cve_entidad' no existe.")
  end

  try
    municipio = MUNICIPIOS[cve_entidad*cve_municipio]
  catch e
    error("Verifica tu clave de municipio. Debe de ser de tres dígitos en el rango [001, 570]. cve_municipio '$cve_municipio' no existe.")
  end
  codigos = get_info("codigos_postales_municipios_2021.csv",[String,String,String,String,Int64,String])
  return filtrar(codigos, ":entidad=='$cve_entidad'", ":municipio=='$cve_municipio'")
end

"""
    codigos_postales_todos()::DataFrame

Proporciona todos los _códigos postales_ de México, segregados por municipio,
en un `DataFrame`.
Los datos son obtenidos del [Servicio Postal Mexicano.](https://www.gob.mx/correosdemexico)

# Ejemplo

```julia-repl
julia> codigos_postales_todos()
2465×6 DataFrame
  Row │ entidad  entidad_nombre  municipio  municipio_nombre  número de códigos postales  códigos postales
      │ String   String          String     String            Int64                       String
──────┼────────────────────────────────────────────────────────────────────────────────────────────────────
    1 │ 01       Aguascalientes  001        Aguascalientes                           599  20000;20010;20010 ⋯
    2 │ 01       Aguascalientes  002        Asientos                                  82  20700;20700;20700 ⋯
  ⋮   │    ⋮           ⋮             ⋮                   ⋮                ⋮                               ⋮
```
"""
function codigos_postales_todos()::DataFrame
  return get_info("codigos_postales_municipios_2021.csv",[String,String,String,String,Int64,String])
end

"""
    tasas_vitales(cve_entidad::String, cve_municipio::String="")::DataFrame

Proporciona un `DataFrame` con las tasas de natalidad, fecundidad y mortalidad del municipio indicado.
En caso de omitir el parámetro cve_municipio, se mostrarán datos de la entidad indicada.
Datos obtenidos del registro de nacimientos (2019), defunciones generales (2019) y población de mujeres en edad fértil (15-45 años, 2020) del INEGI.

# Ejemplo

```julia-repl
julia> tasas_vitales("01", "001")
1×3 DataFrame
 Row │ Natalidad  Fecundidad  Mortalidad
     │ Float64    Float64     Float64
─────┼───────────────────────────────────
   1 │ 0.0430915   0.0915221   0.0221098
```
"""
function tasas_vitales(cve_entidad::String, cve_municipio::String="",token_INEGI="")::DataFrame
   cve_entidad, cve_municipio= check(cve_entidad,cve_municipio)
     token_INEGI = token_check(token_INEGI)

  nacimientos = get_info("nacimientos.csv", [String, String, String, Int64])

  if(cve_municipio=="")
    localidad = poblacion_entidad(cve_entidad)
    pob_mujeres = localidad[:1, :4]

    estado = filtrar(nacimientos, ":entidad == '$cve_entidad'", ":municipio == '000'")
    nacimientos_ent = estado[:1, :4]
    if(nacimientos_ent == "")
      natalidad = 0
    else
      natalidad = nacimientos_ent/pob_mujeres
    end
  else
    localidad = poblacion_municipio(cve_entidad, cve_municipio)
    pob_mujeres = localidad[:1, :4]

    municipio = filtrar(nacimientos, ":entidad == '$cve_entidad'", ":municipio == '$cve_municipio'")
    nacimientos_muni = municipio[:1, :4]
    if(nacimientos_muni == "")
      natalidad = 0
    else
      natalidad = nacimientos_muni/pob_mujeres
    end
  end

  fertilidades = get_info("fertilidad_entidad_municipio_2020.csv", [String, String, String, Int64])

  if(cve_municipio=="")
    fertil_ent = filtrar(fertilidades, ":entidad == '$cve_entidad'", ":municipio == '000'")
    pob_fertil = fertil_ent[:1, :4]
    fecundidad = nacimientos_ent/pob_fertil
  else
    fertil_muni = filtrar(fertilidades, ":entidad == '$cve_entidad'", ":municipio == '$cve_municipio'")
    pob_fertil = fertil_muni[:1, :4]
    fecundidad = nacimientos_muni/pob_fertil
  end

  defunciones = get_info("defunciones_municipio_2019.csv", [String, String, String, Int64])

  pob_total = localidad[:1, :2]

  if(cve_municipio=="")
    estado = filtrar(nacimientos, ":entidad == '$cve_entidad'", ":municipio == '000'")
    defunciones_ent = estado[:1, :4]
    if(defunciones_ent == "")
      mortalidad = 0
    else
      mortalidad = defunciones_ent/pob_total
    end
  else
    municipio = filtrar(nacimientos, ":entidad == '$cve_entidad'", ":municipio == '$cve_municipio'")
    defunciones_muni = municipio[:1, :4]
    if(defunciones_muni == "")
      mortalidad = 0
    else
      mortalidad = defunciones_muni/pob_total
    end
  end
  return DataFrame(natalidad=[natalidad], fecundidad=[fecundidad], mortalidad=[mortalidad])
end

# toma un string (a) y un arreglo (b) para regresar los elementos de b
# que tengan similitud con a. Qué tan similar tiene que ser un elemento
# de b a "a" para ser considerado se ajusta con el parámetro "min_score".
function colecta_similitud(id::String, iter::Array{String})::Array{String}
  similar = String[]
  for f in findall(id, iter, StringDistances.Levenshtein(), min_score = 0.5)
    push!(similar, iter[f])
  end
  return similar
end

"""
    similitud_entidad(entidad::String)::Array{String}

Proporciona un arreglo con todas las entidades con un nombre igual o similar a `entidad`.

# Ejemplo
```julia-repl
julia> similitud_entidad("oajaca")
1-element Array{String,1}:
 "Oaxaca"

julia> clave(similitud_entidad("oaxjaca")[1])
"29"
```
"""
function similitud_entidad(id::String)::Array{String}
  entidades_iter = collect(values(ENTIDADES))
  return colecta_similitud(id, entidades_iter)
end

"""
    similitud_municipio(municipio::String)::Array{String}

Proporciona un arreglo con todos los municipios con un nombre igual o similar a `municipio`.

# Ejemplo
```julia-repl
julia> similitud_municipio("tequixciapn")
3-element Array{String,1}:
 "Tequisquiapan"
 "Atlequizayan"
 "Tequixquiac"

julia> clave(similitud_municipio("tequixciapn")[end])
"096"
```
"""
function similitud_municipio(id::String)::Array{String}
  municipios_iter = collect(values(MUNICIPIOS))
  return colecta_similitud(id, municipios_iter)
end

"""
    similitud_region(region::String)::Array{Array{String}}

Proporciona _dos_ arreglos dentro de un arreglo. El primer arreglo contiene todas
las entidades similares a `region` y el segundo todos los municipios similares a
`region`.

# Ejemplo
```julia-repl
julia> similitud_region("jalisto")
2-element Array{Array{String,N} where N,1}:
 ["Jalisco"]
 ["Xalisco", "Naolinco", "Xaloztoc", "Calvillo", "Saltillo"]
```
"""
function similitud_region(id::String)::Array{Array{String}}
  entidades_iter = collect(values(ENTIDADES))
  municipios_iter = collect(values(MUNICIPIOS))
  simil_entidades = colecta_similitud(id, entidades_iter)
  simil_municipio = colecta_similitud(id, municipios_iter)
  return [simil_entidades, simil_municipio]
end
function check(cve_entidad,cve_municipio)::Any
  cve_mun , cve_ent = cve_municipio, cve_entidad
  if !haskey(ENTIDADES,cve_entidad)
    cve_entidad = clave(cve_entidad)
  end
   if cve_municipio != "" && !haskey(MUNICIPIOS,cve_entidad*cve_municipio)
     cve_municipio =  clave(cve_municipio,false)
  end

  if haskey(MUNICIPIO_NOMBRE , cve_mun)
    if cve_entidad*cve_municipio != MUNICIPIO_NOMBRE[cve_mun]
      error("El municipio $(cve_mun) no pertenece la entidad $(ENTIDADES[cve_entidad]).")
    end
  end
  return (cve_entidad ,cve_municipio)
end

max_stars_repo_name: yollotltamayo/Covid

max_issues_repo_name: yollotltamayo/Covid

max_forks_repo_name: yollotltamayo/Covid


text:
function build_F0_tr(
  nbset,  # tuple with the following elements.
    # nodes: array of Strings containing independent node names.
    # num_nodes: number of nodes.
    # branches: array of struct Branch.
    # num_branches: sum of branch phases.
    # num_dyns: number of dynamic branches.
  smset  # tuple with the following elements.
    # Ac: branch-node connectivity matrix.
    # Z: coefficient matrix of i.
    # Y: coefficient matrix of v.
    # B1: incidence matrix of alpha.
    # B2: incidence matrix of [ i v ].
  )  #=

builds the initial form of F which is the coefficient matrix of the sparse tableau equations for a transient simulation and returns it.

Author: Taku Noda
Started on Feb. 11, 2021
Update on June 29, 2021

Copyright 2021 Central Research Institute of Electric Power Industry

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

=#

( nodes, num_nodes, branches, num_branches, num_dyns ) = nbset
( Ac, Z, Y, B1, B2 ) = smset

N = num_nodes + 2*num_branches + num_dyns

F = spzeros( N, N )
range1 = 1:num_nodes
range2 = num_nodes .+ ( 1:num_branches )
range3 = num_branches .+ range2
range4 = ( num_nodes + 2*num_branches ) .+ ( 1:num_dyns )
range23 = num_nodes .+ ( 1:( 2*num_branches ) )
F[ range1, range2 ] = transpose( Ac )
F[ range2, range1 ] = Ac
F[ range2, range3 ] = sparse( -I, num_branches, num_branches )
F[ range3, range2 ] = Z
F[ range3, range3 ] = Y
F[ range3, range4 ] = B1
F[ range4, range23 ] = B2

return F

end

ext: jl

max_stars_repo_name: takunoda/omegasim

max_issues_repo_name: takunoda/omegasim

max_forks_repo_name: takunoda/omegasim


text:
"""
  Software License Agreement (BSD License)

  Point Cloud Library (PCL) - www.pointclouds.org
  Copyright (c) 2010, Willow Garage, Inc.
  Copyright (c) 2012-, Open Perception, Inc.

  All rights reserved.

  Redistribution and use in source and binary forms, with or without
  modification, are permitted provided that the following conditions
  are met:

  * Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
  * Redistributions in binary form must reproduce the above
    copyright notice, this list of conditions and the following
    disclaimer in the documentation and/or other materials provided
    with the distribution.
  * Neither the name of the copyright holder(s) nor the names of its
    contributors may be used to endorse or promote products derived
    from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE.
"""

"""
    _PCL_ENDIAN

FIXME These values have not been validated!

- `_PCL_ENDIAN_BIG_BYTE`, byte-swapped big-endian.
- `_PCL_ENDIAN_BIG_WORD`, word-swapped big-endian.
- `_PCL_ENDIAN_LITTLE_BYTE`, byte-swapped little-endian.
- `_PCL_ENDIAN_LITTLE_WORD`, word-swapped little-endian.

Notes:
- https://docs.julialang.org/en/v1/base/io-network/#Base.ENDIAN_BOM
- https://www.boost.org/doc/libs/1_69_0/boost/predef/other/endian.h

DevNotes
- FIXME, `_PCL_ENDIAN_BIG_WORD`, `_PCL_ENDIAN_LITTLE_BYTE` -- values are not correct
"""
@enum _PCL_ENDIAN begin
  _PCL_ENDIAN_BIG_BYTE=0x01020304
  _PCL_ENDIAN_BIG_WORD=0x00020304
  _PCL_ENDIAN_LITTLE_BYTE=0x04030200
  _PCL_ENDIAN_LITTLE_WORD=0x04030201
end

Base.convert(::Type{<:_PCL_ENDIAN}, val::Integer) = (en=instances(_PCL_ENDIAN); en[findfirst(Int.(en) .== Int.(convert(UInt32, val)))])

"""
Format`:::UInt8` as enum for PointCloud2 messages between ROS and PCL.

References
- https://docs.ros.org/en/api/sensor_msgs/html/msg/PointField.html
"""
@enum _PCL_POINTFIELD_FORMAT begin
  _PCL_INT8    = UInt8(1)
  _PCL_UINT8   = UInt8(2)
  _PCL_INT16   = UInt8(3)
  _PCL_UINT16  = UInt8(4)
  _PCL_INT32   = UInt8(5)
  _PCL_UINT32  = UInt8(6)
  _PCL_FLOAT32 = UInt8(7)
  _PCL_FLOAT64 = UInt8(8)
end

Base.convert(::Type{<:_PCL_POINTFIELD_FORMAT}, val::Integer) = (en=instances(_PCL_POINTFIELD_FORMAT); en[findfirst(Int.(en) .== Int.(convert(UInt8, val)))])

struct asType{T} end

(::Type{<:asType{_PCL_INT8}})()    = Int8
(::Type{<:asType{_PCL_UINT8}})()   = UInt8
(::Type{<:asType{_PCL_INT16}})()   = Int16
(::Type{<:asType{_PCL_UINT16}})()  = UInt16
(::Type{<:asType{_PCL_INT32}})()   = Int32
(::Type{<:asType{_PCL_UINT32}})()  = UInt32
(::Type{<:asType{_PCL_FLOAT32}})() = Float32
(::Type{<:asType{_PCL_FLOAT64}})() = Float64

abstract type PointT end

"""
    $TYPEDEF

Immutable PointXYZ with color information.  E.g. `PointXYZ{RGB}`, `PointXYZ{Gray}`, etc.

Aliases
- `PointXYZRGB`
- `PointXYZRGBA`

See
- https://pointclouds.org/documentation/structpcl_1_1_point_x_y_z.html
- https://pointclouds.org/documentation/point__types_8hpp_source.html
"""
Base.@kwdef struct PointXYZ{C <: Colorant, T <: Number} <: PointT
  color::C           = RGBA(1,1,1,1)
  data::SVector{4,T} = SVector(0,0,0,1f0)
end

"""
    $TYPEDEF

Immutable Header.

See https://pointclouds.org/documentation/structpcl_1_1_p_c_l_header.html
"""
Base.@kwdef struct Header
  """ The sequence number """
  seq::UInt32      = UInt32(0)
  """ A timestamp associated with the time when the data was acquired.
  The value represents microseconds since 1970-01-01 00:00:00 (the UNIX epoch). """
  stamp::UInt64    = UInt64(0)
  """ Coordinate frame ID. """
  frame_id::String = ""
end

# https://pointclouds.org/documentation/structpcl_1_1_p_c_l_point_field.html
Base.@kwdef struct PointField
  """ name of field """
  name::String
  """ Offset from start of point struct """
  offset::UInt32                   = UInt32(0)
  """ Datatype enumeration, see _PCL_POINTFIELD_FORMAT """
  datatype::_PCL_POINTFIELD_FORMAT = convert(_PCL_POINTFIELD_FORMAT, 7)
  """ How many elements in the field """
  count::UInt32                    = UInt32(0)
end

# https://docs.ros.org/en/hydro/api/pcl/html/structpcl_1_1detail_1_1FieldMapping.html
# https://pointclouds.org/documentation/common_2include_2pcl_2point__cloud_8h_source.html#l00072
Base.@kwdef mutable struct FieldMapping
  serialized_offset::UInt32  = UInt32(0)
  struct_offset::UInt32      = UInt32(0)
  size::UInt32               = UInt32(0)
end

struct FieldMatches{T<:PointT} end

# https://docs.ros.org/en/hydro/api/pcl/html/structpcl_1_1detail_1_1FieldAdder.html
Base.@kwdef struct FieldAdder
  fields_::Vector{<:PointField} = Vector{PointField}()
end

# https://docs.ros.org/en/hydro/api/pcl/html/point__cloud_8h_source.html#l00066
# https://pointclouds.org/documentation/common_2include_2pcl_2point__cloud_8h_source.html#l00072
const MsgFieldMap = Vector{FieldMapping}

# https://docs.ros.org/en/hydro/api/pcl/html/conversions_8h_source.html#l00091
Base.@kwdef struct FieldMapper{T<:PointT}
  fields_::Vector{<:PointField} = Vector{PointField}()
  map_::Vector{<:FieldMapping}  = Vector{FieldMapping}()
end

"""
    $TYPEDEF

Immutable point cloud type. Immutable for performance, computations are more
frequent and intensive than anticipated frequency of constructing new clouds.

References:
- https://pointclouds.org/documentation/structpcl_1_1_p_c_l_point_cloud2.html
- https://pointclouds.org/documentation/classpcl_1_1_point_cloud.html
- https://pointclouds.org/documentation/common_2include_2pcl_2point__cloud_8h_source.html
"""
Base.@kwdef struct PCLPointCloud2
  """ the point cloud header """
  header::Header           = Header()
  """ the point cloud height (if organized as image structure).  Specifies the height
  of the point cloud dataset in the number of points. HEIGHT has two meanings:
  - it can specify the height (total number of rows) of an organized point cloud dataset;
  - it is set to 1 for unorganized datasets (thus used to check whether a dataset is organized or not). """
  height::UInt32           = UInt32(0)
  """ the point cloud width (if organized as image structure).  Specifies the width
  of the point cloud dataset in the number of points. WIDTH has two meanings:
  - it can specify the total number of points in the cloud (equal with POINTS see below) for unorganized datasets;
  - it can specify the width (total number of points in a row) of an organized point cloud dataset. """
  width::UInt32            = UInt32(0)
  """ field descriptions of data """
  fields::Vector{<:PointField}= Vector{PointField}()
  """ `Vector` of `<:PointT` representing the point cloud """
  data::Vector{UInt8}      = Vector{UInt8}()
  """ WARNING, untested """
  is_bigendian::_PCL_ENDIAN= convert(_PCL_ENDIAN, Base.ENDIAN_BOM)
  point_step::UInt32       = UInt32(0)
  row_step::UInt32         = UInt32(0)
  """ true if points are invalid (e.g., have NaN or Inf values in any of their floating point fields). """
  is_dense::UInt8          = UInt8(0)
end

"""
    $TYPEDEF

Convert a PCLPointCloud2 binary data blob into a `Caesar._PCL.PointCloud{T}` object using
a `field_map::Caesar._PCL.MsgFieldMap`.

Use `PointCloud(::Caesar._PCL.PCLPointCloud2)` directly or create you
own `MsgFieldMap`:

```julia
field_map = Caesar._PCL.createMapping(msg.fields, field_map)
```

Notes
- Tested on Radar data with height `z=constant` for all points -- i.e. 2D sweeping scan where `.height=1`.

DevNotes
- TODO .PCLPointCloud2 convert not tested on regular 3D data from structured light or lidar yet, but current implementation should be close (or already working).

References
- https://pointclouds.org/documentation/classpcl_1_1_point_cloud.html
- (seems older) https://docs.ros.org/en/hydro/api/pcl/html/conversions_8h_source.html#l00123
"""
Base.@kwdef struct PointCloud{T<:PointT,P,R}
  """ the point cloud header """
  header::Header            = Header()
  """ `Vector` of `UInt8` representing the cloud points """
  points::Vector{T}         = Vector{typeof(PointXYZ())}() # {PointXYZ{RGB{Colors.FixedPointNumbers.N0f8}, Float32}}
  """ the point cloud width (if organized as image structure).  Specifies the width
  of the point cloud dataset in the number of points. WIDTH has two meanings:
  - it can specify the total number of points in the cloud (equal with POINTS see below) for unorganized datasets;
  - it can specify the width (total number of points in a row) of an organized point cloud dataset. """
  width::UInt32            = UInt32(0)
  """ the point cloud height (if organized as image structure).  Specifies the height
  of the point cloud dataset in the number of points. HEIGHT has two meanings:
  - it can specify the height (total number of rows) of an organized point cloud dataset;
  - it is set to 1 for unorganized datasets (thus used to check whether a dataset is organized or not). """
  height::UInt32           = UInt32(0)
  """ true if points are invalid (e.g., have NaN or Inf values in any of their floating point fields). """
  is_dense::Bool           = false
  """ Sensor acquisition pose (origin/translation), optional."""
  sensor_origin_::P        = SA[0f0;0;0]
  """ sensor acquisition pose (rotation), optional."""
  sensor_orientation_::R   = SMatrix{3,3}(1f0,0,0,0,1,0,0,0,1)
end

#


text:
# nested archimedean copulas
# Algorithms from:
# M. Hofert, `Efficiently sampling nested Archimedean copulas` Computational Statistics and Data Analysis 55 (2011) 57–70
# M. Hofert, 'Sampling  Archimedean copulas', Computational Statistics & Data Analysis, Volume 52, 2008
# McNeil, A.J., 2008. 'Sampling nested Archimedean copulas'. Journal of Statistical Computation and Simulation 78, 567–581.

"""
  nestedarchcopulagen(t::Int, n::Vector{Int}, ϕ::Vector{Float64}, θ::Float64, copula::String, m::Int = 0)

Returns Matrix{Float} of t realisations of sum(n)+m random variables generated using
nested archimedean copula, outer copula parameter is θ, inner i'th copulas parameter is
ϕ[i] and size is n[i]. If m ≠ 0, last m variables are from outer copula only.

Following copula families are supported: clayton, frank, gumbel and amh --
Ali-Mikhail-Haq.

Nested archimedean copula in in a form C_θ(C_ϕ₁(u₁₁, ..., u₁,ₙ₁), ..., C_ϕₖ(uₖ₁, ..., uₖ,ₙₖ), u₁ , ... uₘ).
Parameters must fulfill ∀ᵢ ϕ[i] ≥ θ

Basically uses Alg. 5 McNeil, A.J., 2008. 'Sampling nested Archimedean copulas'.
Journal of Statistical Computation and Simulation 78, 567–581.

```jldoctest
julia> Random.seed!(43);

julia> nestedarchcopulagen(10, [2,2], [2., 3.], 1.1, "clayton", 1)
10×5 Array{Float64,2}:
 0.414567  0.683167   0.9953    0.607738  0.793386
 0.533001  0.190563   0.17076   0.273119  0.78807
 0.572782  0.161307   0.418821  0.110356  0.661781
 0.623807  0.140974   0.295422  0.454368  0.477065
 0.386276  0.266261   0.559423  0.449874  0.294137
 0.219757  0.122586   0.371318  0.298965  0.507315
 0.322658  0.0627113  0.738565  0.919912  0.19471
 0.131938  0.0672061  0.364721  0.220329  0.662842
 0.773414  0.812113   0.639333  0.527118  0.545043
 0.958656  0.871822   0.958339  0.801866  0.862751

```
"""
function nestedarchcopulagen(t::Int, n::Vector{Int}, ϕ::Vector{Float64}, θ::Float64, copula::String, m::Int = 0)
  n1 = vcat([collect(1:n[1])], [collect(cumsum(n)[i]+1:cumsum(n)[i+1]) for i in 1:length(n)-1])
  nestedarchcopulagen(t, sum(n)+m, n1, ϕ, θ, copula)
end

function nestedarchcopulagen(t::Int, n::Int, n1::Vector{Vector{Int}}, ϕ::Vector{Float64}, θ::Float64,
                                                     copula::String)
  copula in ["clayton", "amh", "frank", "gumbel"] || throw(AssertionError("$(copula) copula is not supported"))
  nestedcopulag(copula, n1, ϕ, θ, rand(t, n+1))
end

"""
  nestedcopulag(copula::String, n::Vector{Int}, ϕ::Vector{Float64}, θ::Float64, r::Matrix{Float64})

Given [0,1]ᵗˣˡ ∋ r , returns t realisations of l-1 variate data from nested archimedean copula

```jldoctest
julia> Random.seed!(43)

julia> nestedcopulag("clayton", [2, 2], [2., 3.], 1.1, [0.1 0.2 0.3 0.4 0.5; 0.2 0.3 0.4 0.5 0.6])
2×4 Array{Float64,2}:
 0.193949  0.230553  0.515404  0.557686
 0.712034  0.761276  0.190189  0.208867
 ```
"""
function nestedcopulag(copula::String, n::Vector{Vector{Int}}, ϕ::Vector{Float64}, θ::Float64,
                                                        r::Matrix{Float64})
  testnestedθϕ(map(length, n), ϕ, θ, copula)
  V0 = getV0(θ, r[:,end], copula)
  X = r[:,1:end-1]
  X[:,n[1]] = nestedstep(copula, r[:,n[1]], V0, ϕ[1], θ)
  for i in 2:length(n)
    X[:,n[i]] = nestedstep(copula, r[:,n[i]], V0, ϕ[i], θ)
  end
  phi(-log.(X)./V0, θ, copula)
end

"""
  nestedstep(copula::String, u::Matrix{Float64}, V0::Union{Vector{Float64}, Vector{Int}}, ϕ::Float64, θ::Float64)

Given u ∈ [0,1]ᵗⁿ and V0 ∈ ℜᵗ returns u ∈ [0,1]ᵗⁿ for a given archimedean nested copula with
inner copulas parameters ϕ anu auter copula parameter θ

```jldoctest
julia> nestedstep("clayton", [0.2 0.8; 0.1 0.7], [0.2, 0.4], 2., 1.5)
2×2 Array{Float64,2}:
 0.283555  0.789899
 0.322614  0.806915
```
"""
function nestedstep(copula::String, u::Matrix{Float64}, V0::Union{Vector{Float64}, Vector{Int}},
                                                        ϕ::Float64, θ::Float64)
  if copula == "amh"
    w = [quantile(NegativeBinomial(v, (1-ϕ)/(1-θ)), rand()) for v in V0]
    u = -log.(u)./(V0 + w)
    X = ((exp.(u) .-ϕ) .*(1-θ) .+θ*(1-ϕ)) ./(1-ϕ)
    return X.^(-V0)
  elseif copula == "frank"
    u = -log.(u)./nestedfrankgen(ϕ, θ, V0)
    X = (1 .-(1 .-exp.(-u)*(1-exp(-ϕ))).^(θ/ϕ))./(1-exp(-θ))
    return X.^V0
  elseif copula == "clayton"
    u = -log.(u)./tiltedlevygen(V0, ϕ/θ)
    return exp.(V0.-V0.*(1 .+u).^(θ/ϕ))
  elseif copula == "gumbel"
    u = -log.(u)./levygen(ϕ/θ, rand(length(V0)))
    return exp.(-u.^(θ/ϕ))
  end
  throw(AssertionError("$(copula) not supported"))
end

"""
  nestedarchcopulagen::Int, n::Vector{Vector{Int}}, Ψ::Vector{Vector{Float64}},
                                                    ϕ::Vector{Float64}, θ₀::Float64,
                                                    copula::String = "gumbel")

Returns t realisations of ∑ᵢ ∑ⱼ nᵢⱼ variate data from double nested gumbel copula.
C_θ(C_ϕ₁(C_Ψ₁₁(u,...), ..., C_C_Ψ₁,ₗ₁(u...)), ..., C_ϕₖ(C_Ψₖ₁(u,...), ..., C_Ψₖ,ₗₖ(u,...)))
 where lᵢ = length(n[i])

  ```jldoctest
  julia> Random.seed!(43)

  julia> nestedarchcopulagen(5, [[2,2],[2]], [[3., 2.], [4.]], [1.5, 2.1], 1.2, "gumbel")
  5×6 Array{Float64,2}:
   0.464403  0.711722   0.883035   0.896706   0.888614   0.826514
   0.750596  0.768193   0.0659561  0.0252472  0.996014   0.989127
   0.825211  0.712079   0.581356   0.507739   0.882675   0.84959
   0.276326  0.0827071  0.240836   0.434629   0.0184611  0.031363
   0.208422  0.504727   0.27561    0.639089   0.481855   0.573715
  ```
"""
function nestedarchcopulagen(t::Int, n::Vector{Vector{Int}}, Ψ::Vector{Vector{Float64}},
                                                             ϕ::Vector{Float64}, θ::Float64,
                                                             copula::String = "gumbel")
  copula == "gumbel" ||
  throw(AssertionError("double nested cop. generator supported only for gumbel familly"))
  θ <= minimum(ϕ) || throw(DomainError("wrong heirarchy of parameters"))
  X = nestedarchcopulagen(t, n[1], Ψ[1], ϕ[1]./θ, copula)
  for i in 2:length(n)
    X = hcat(X, nestedarchcopulagen(t, n[i], Ψ[i], ϕ[i]./θ, copula))
  end
  phi(-log.(X)./levygen(θ, rand(t)), θ, copula)
end

"""
  nestedarchcopulagen(t::Int, θ::Vector{Float64}, copula::String = "gumbel")

Returns t realisations of length(θ)+1 variate data from hierarchically nested Gumbel copula.
C_θₙ(... C_θ₂(C_θ₁(u₁, u₂), u₃)...,  uₙ)

```jldoctest
julia> Random.seed!(43)

julia> x = nestedarchcopulagen(5, [4., 3., 2.], "gumbel")
5×4 Array{Float64,2}:
 0.483466  0.621572  0.241025  0.312664
 0.827237  0.696634  0.768802  0.730543
 0.401159  0.462126  0.412573  0.72571
 0.970726  0.964746  0.940314  0.934625
 0.684486  0.614142  0.690664  0.401897
```
"""
function nestedarchcopulagen(t::Int, θ::Vector{Float64}, copula::String = "gumbel")
  copula == "gumbel" ||
  throw(AssertionError("hierarchically nasted cop. generator supported only for gumbel familly"))
  testθ(θ[end], copula)
  issorted(θ; rev=true) || throw(DomainError("wrong heirarchy of parameters"))
  θ = vcat(θ, [1.])
  X = rand(t,1)
  for i in 1:length(θ)-1
    X = nestedstep("gumbel", hcat(X, rand(t)), ones(t), θ[i], θ[i+1])
  end
  X
end

"""
  testnestedθϕ(n::Vector{Int}, ϕ::Vector{Float64}, θ::Float64, copula::String)

Tests parameters, its hierarchy and size of parametes vector for nested archimedean copulas.
"""
function testnestedθϕ(n::Vector{Int}, ϕ::Vector{Float64}, θ::Float64, copula::String)
  testθ(θ, copula)
  map(p -> testθ(p, copula), ϕ)
  θ <= minimum(ϕ) || throw(DomainError("wrong heirarchy of parameters"))
  length(n) == length(ϕ) || throw(AssertionError("number of subcopulas ≠ number of parameters"))
  (copula != "clayton") | (maximum(ϕ) < θ+2*θ^2+750*θ^5) || warn("θ << ϕ for clayton nested copula, marginals may not be uniform")
end


text:
#!/usr/bin/julia

function BubbleSort(arr)
    l = length(arr)
    swapped = true
    while swapped
        swapped = false
        for j = 2:l
            if arr[j-1] > arr[j]
                tmp = arr[j-1]
                arr[j-1] = arr[j]
                arr[j] = tmp
                swapped = true
            end
        end
    end
end

function error()
    println("Usage: please provide a list of at least two integers to sort in the format \"1, 2, 3, 4, 5\"")
end

function HandleInput(inp)

    a = split(inp,",")
    a = map(x->split(x," "),a)
    a = map(x->last(x),a)
    numbers = map(x->parse(Int,x),a)
    if length(numbers) == 1
        error()
        exit()
    end
    return numbers

end

function PrintOutput(out)
    for i = 1:length(out)
        print(out[i])
        if i != length(out)
            print(", ")
        end
    end
    println()
end

try

    n = HandleInput(ARGS[1])
    BubbleSort(n)
    PrintOutput(n)

catch e
    error()
end


text:
tuple_data = ("apple","banana","orange")
several_types = ("apple",10, 'a')
tuple_2d = ((1,2),(3,4))
println(tuple_data,several_types,tuple_2d)

# 1-indexに注意
println(tuple_data[1], "apple")

# こんな宣言もできる
tuple_1 = tuple(1,3,2)
println(tuple_1)

# tupleは不変だから情報の更新ができない
# できないからコメントアウト
# tuple_data[1] = "peach"

# pushもできない
# できないからコメントアウト
# push!(tuple_data,4)

# Named Tupleを使っていく
# NamedTuplesをインストール
#
# julia 1.0以降では下の書き方で直接インストールできない
# やるにはrepl modeで ] を押してPkgモードに入り，add Package名する
# Pkg.add("NamedTuples")
#
# どうしてもやりたいなら
# Using Pkgをする
# using NamedTuples
# julia1.0以降では NamedTuplesはないらしい...

# 辞書と違って不変なオブジェクトだけど，indexとして名前登録ができる


text:
""" Dynamic """

function dynamic(ekf::ExtendedKalmanFilter, x::AbstractVector, u::AbstractVector)
    # Non linear function F has to have x and u as inputs
    return ekf.f(x, u) + cholesky(ekf.Q).L*randn(size(ekf.Q, 1))
end

function prediction(ekf::ExtendedKalmanFilter, s::State, u::AbstractVector)
    xp = ekf.f(s.x, u) # predicted state prior
    F = ForwardDiff.jacobian(μ -> ekf.f(μ, u), s.x)
    Pp = F*s.P*F' + ekf.Q # a priori state covariance
    return State(xp, Pp)
end

function param_dynamic(θ, parma_ekf::ExtendedParamKalmanFilter, x::AbstractVector, u::AbstractVector)
    # Non linear function F has to have x and u as inputs
    return param_ekf.f(θ, x, u) + cholesky(param_ekf.Q(θ)).L*randn(size(parma_ekf.Q(θ), 1))
end

function param_prediction(θ, param_ekf::ExtendedParamKalmanFilter, s::State, u::AbstractVector)
    xp = param_ekf.f(θ, s.x, u) # predicted state prior
    F = ForwardDiff.jacobian(μ -> param_ekf.f(θ, μ, u), s.x)
    Pp = F*s.P*F' + param_ekf.Q(θ) # a priori state covariance
    return State(xp, Pp)
end

""" Observation """

function observation(ekf::ExtendedKalmanFilter, x::AbstractVector)
    return ekf.h(x) + cholesky(ekf.R).L*randn(size(ekf.R, 1))
end

function correction(ekf::ExtendedKalmanFilter, s::State, y::AbstractVector)
    v = y - ekf.h(s.x) # measurement pre fit residual
    H = ForwardDiff.jacobian(μ -> ekf.h(μ), s.x)
    S = H*s.P*H' + ekf.R # pre fit residual covariance
    K = s.P*H'*inv(S) # Kalman gain
    x_hat = s.x + K*v # a posteriori state estiamate
    P_hat = (I - K*H)*s.P # a posteriori covariance estiamate
    return State(x_hat, P_hat)
end

function param_observation(θ, param_ekf::ExtendedParamKalmanFilter, x::AbstractVector)
    return param_ekf.h(θ, x) + cholesky(param_ekf.R(θ)).L*randn(size(param_ekf.R(θ), 1))
end

function param_correction(θ, param_ekf::ExtendedParamKalmanFilter, s::State, y::AbstractVector)
    v = y - param_ekf.h(θ, s.x) # measurement pre fit residual
    H = ForwardDiff.jacobian(μ -> param_ekf.h(θ, μ), s.x)
    S = H*s.P*H' + param_ekf.R(θ) # pre fit residual covariance
    K = s.P*H'*inv(S) # Kalman gain
    x_hat = s.x + K*v # a posteriori state estiamate
    P_hat = (I - K*H)*s.P # a posteriori covariance estiamate
    return State(x_hat, P_hat)
end

"""Loss """

function state_mse(filter::ExtendedKalmanFilter, s::State, u::AbstractVector, y::AbstractVector)
    ϵx = norm(s.x - filter.f(s.x))
    ϵy = norm(y - filter.h(s.x))
    return ϵx + ϵy
end


text:
h = 100
v = 20
g = 9.81

println((h + v^2/2g)^2 * pi * v^2/g)

max_stars_repo_name: HarrisonGreen/Project-Euler-Solutions

max_issues_repo_name: HarrisonGreen/Project-Euler-Solutions

max_forks_repo_name: HarrisonGreen/Project-Euler-Solutions


text:
import GZip, CodecBzip2

const LIBSVM_URL = "https://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/binary/"
const SEPARATOR = ":"

struct LibSVMData{Index<:Integer, T<:Real}
    # Label to predict
    labels::Vector{T}
    # Features (as sparse matrix)
    index_rows::Vector{Index}
    index_cols::Vector{Index}
    coefs::Vector{T}
end

LibSVMData{Index, T}() where {Index, T} = LibSVMData(T[], Index[], Index[], T[])
ndata(data::LibSVMData) = length(data.labels)
nfeatures(data::LibSVMData) = maximum(data.index_cols)

function to_dense(data::LibSVMData{I, T}) where {I, T}
    n, m = ndata(data), nfeatures(data)
    X = zeros(T, n, m)
    for (i, j, val) in zip(data.index_rows, data.index_cols, data.coefs)
        @inbounds X[i, j] = val
    end
    return X
end

function to_sparse(data::LibSVMData{I, T}) where {I, T}
    n, m = ndata(data), nfeatures(data)
    return sparse(data.index_rows, data.index_cols, data.coefs, n, m)
end

function parse_libsvm(filename::String, T::Type=Float64)
    dataset = LibSVMData{Int, T}()
    gzip_open(filename, "r") do io
         _fetch_dataset(io, dataset)
    end
    return dataset
end

function gzip_open(f::Function, filename::String, mode::String)
    if endswith(filename, ".gz")
        return GZip.open(f, filename, mode)
    elseif endswith(filename, ".bz2")
        return Base.open(f, CodecBzip2.Bzip2DecompressorStream, filename, mode)
    else
        return open(f, filename, mode)
    end
end

function _fetch_dataset(io::IO, data::LibSVMData{I, T}) where {I, T}
    nline = 0
    nindex = 0
    while !eof(io)
        nline += 1
        line = strip(readline(io))
        values_ = split(line, " ", keepempty=false)
        push!(data.labels, parse(T, values_[1]))
        for val in values_[2:end]
            tmp = split(val, SEPARATOR, limit=2)
            ncol = parse(I, tmp[1])
            push!(data.index_rows, nline)
            push!(data.index_cols, ncol)
            push!(data.coefs, parse(T, tmp[2]))
        end
    end
end


text:
export BrickletPerformanceDCMotion
struct BrickletPerformanceDCMotion
    acceleration::Integer
    deceleration::Integer
end

export BrickletPerformanceDCPowerStatistics
struct BrickletPerformanceDCPowerStatistics
    voltage::Integer
    current::Integer
    temperature::Integer
end

export BrickletPerformanceDCGPIOConfiguration
struct BrickletPerformanceDCGPIOConfiguration
    debounce::Integer
    stop_deceleration::Integer
end

export BrickletPerformanceDCCurrentVelocityCallbackConfiguration
struct BrickletPerformanceDCCurrentVelocityCallbackConfiguration
    period::Integer
    value_has_to_change::Bool
end

export BrickletPerformanceDCSPITFPErrorCount
struct BrickletPerformanceDCSPITFPErrorCount
    error_count_ack_checksum::Integer
    error_count_message_checksum::Integer
    error_count_frame::Integer
    error_count_overflow::Integer
end

export BrickletPerformanceDCIdentity
struct BrickletPerformanceDCIdentity
    uid::String
    connected_uid::String
    position::Char
    hardware_version::Vector{Integer}
    firmware_version::Vector{Integer}
    device_identifier::Integer
end

export BrickletPerformanceDC
"""
Drives one brushed DC motor with up to 36V and 10A
"""
mutable struct BrickletPerformanceDC <: TinkerforgeDevice
    ipcon::IPConnection
    deviceInternal::PyObject

    """
    Creates an object with the unique device ID *uid* and adds it to
    the IP Connection *ipcon*.
    """
    function BrickletPerformanceDC(uid::String, ipcon::IPConnection)
        package = pyimport("tinkerforge.bricklet_performance_dc")
        deviceInternal = package.BrickletPerformanceDC(uid, ipcon.ipconInternal)

        return new(ipcon, deviceInternal)
    end
end

export set_enabled
"""
    $(SIGNATURES)

Enables/Disables the driver chip. The driver parameters can be configured
(velocity, acceleration, etc) before it is enabled.
"""
function set_enabled(device::BrickletPerformanceDC, enabled)
    device.deviceInternal.set_enabled(enabled)
end

export get_enabled
"""
    $(SIGNATURES)

Returns *true* if the driver chip is enabled, *false* otherwise.
"""
function get_enabled(device::BrickletPerformanceDC)
    return device.deviceInternal.get_enabled()
end

export set_velocity
"""
    $(SIGNATURES)

Sets the velocity of the motor. Whereas -32767 is full speed backward,
0 is stop and 32767 is full speed forward. Depending on the
acceleration (see :func:`Set Motion`), the motor is not immediately
brought to the velocity but smoothly accelerated.

The velocity describes the duty cycle of the PWM with which the motor is
controlled, e.g. a velocity of 3277 sets a PWM with a 10% duty cycle.
You can not only control the duty cycle of the PWM but also the frequency,
see :func:`Set PWM Frequency`.
"""
function set_velocity(device::BrickletPerformanceDC, velocity)
    device.deviceInternal.set_velocity(velocity)
end

export get_velocity
"""
    $(SIGNATURES)

Returns the velocity as set by :func:`Set Velocity`.
"""
function get_velocity(device::BrickletPerformanceDC)
    return device.deviceInternal.get_velocity()
end

export get_current_velocity
"""
    $(SIGNATURES)

Returns the *current* velocity of the motor. This value is different
from :func:`Get Velocity` whenever the motor is currently accelerating
to a goal set by :func:`Set Velocity`.
"""
function get_current_velocity(device::BrickletPerformanceDC)
    return device.deviceInternal.get_current_velocity()
end

export set_motion
"""
    $(SIGNATURES)

Sets the acceleration and deceleration of the motor. It is given in *velocity/s*.
An acceleration of 10000 means, that every second the velocity is increased
by 10000 (or about 30% duty cycle).

For example: If the current velocity is 0 and you want to accelerate to a
velocity of 16000 (about 50% duty cycle) in 10 seconds, you should set
an acceleration of 1600.

If acceleration and deceleration is set to 0, there is no speed ramping, i.e. a
new velocity is immediately given to the motor.
"""
function set_motion(device::BrickletPerformanceDC, acceleration, deceleration)
    device.deviceInternal.set_motion(acceleration, deceleration)
end

export get_motion
"""
    $(SIGNATURES)

Returns the acceleration/deceleration as set by :func:`Set Motion`.
"""
function get_motion(device::BrickletPerformanceDC)
    return device.deviceInternal.get_motion()
end

export full_brake
"""
    $(SIGNATURES)

Executes an active full brake.

.. warning::
 This function is for emergency purposes,
 where an immediate brake is necessary. Depending on the current velocity and
 the strength of the motor, a full brake can be quite violent.

Call :func:`Set Velocity` with 0 if you just want to stop the motor.
"""
function full_brake(device::BrickletPerformanceDC)
    device.deviceInternal.full_brake()
end

export set_drive_mode
"""
    $(SIGNATURES)

Sets the drive mode. Possible modes are:

* 0 = Drive/Brake
* 1 = Drive/Coast

These modes are different kinds of motor controls.

In Drive/Brake mode, the motor is always either driving or braking. There
is no freewheeling. Advantages are: A more linear correlation between
PWM and velocity, more exact accelerations and the possibility to drive
with slower velocities.

In Drive/Coast mode, the motor is always either driving or freewheeling.
Advantages are: Less current consumption and less demands on the motor and
driver chip.
"""
function set_drive_mode(device::BrickletPerformanceDC, mode)
    device.deviceInternal.set_drive_mode(mode)
end

export get_drive_mode
"""
    $(SIGNATURES)

Returns the drive mode, as set by :func:`Set Drive Mode`.
"""
function get_drive_mode(device::BrickletPerformanceDC)
    return device.deviceInternal.get_drive_mode()
end

export set_pwm_frequency
"""
    $(SIGNATURES)

Sets the frequency of the PWM with which the motor is driven.
Often a high frequency
is less noisy and the motor runs smoother. However, with a low frequency
there are less switches and therefore fewer switching losses. Also with
most motors lower frequencies enable higher torque.

If you have no idea what all this means, just ignore this function and use
the default frequency, it will very likely work fine.
"""
function set_pwm_frequency(device::BrickletPerformanceDC, frequency)
    device.deviceInternal.set_pwm_frequency(frequency)
end

export get_pwm_frequency
"""
    $(SIGNATURES)

Returns the PWM frequency as set by :func:`Set PWM Frequency`.
"""
function get_pwm_frequency(device::BrickletPerformanceDC)
    return device.deviceInternal.get_pwm_frequency()
end

export get_power_statistics
"""
    $(SIGNATURES)

Returns input voltage, current usage and temperature of the driver.
"""
function get_power_statistics(device::BrickletPerformanceDC)
    return device.deviceInternal.get_power_statistics()
end

export set_thermal_shutdown
"""
    $(SIGNATURES)

Sets a temperature threshold that is used for thermal shutdown.

Additionally to this user defined threshold the driver chip will shut down at a
temperature of 150°C.

If a thermal shutdown is triggered the driver is disabled and has to be
explicitly re-enabled with :func:`Set Enabled`.
"""
function set_thermal_shutdown(device::BrickletPerformanceDC, temperature)
    device.deviceInternal.set_thermal_shutdown(temperature)
end

export get_thermal_shutdown
"""
    $(SIGNATURES)

Returns the thermal shutdown temperature as set by :func:`Set Thermal Shutdown`.
"""
function get_thermal_shutdown(device::BrickletPerformanceDC)
    return device.deviceInternal.get_thermal_shutdown()
end

export set_gpio_configuration
"""
    $(SIGNATURES)

Sets the GPIO configuration for the given channel.
You can configure a debounce and the deceleration that is used if the action is
configured as ``normal stop``. See :func:`Set GPIO Action`.
"""
function set_gpio_configuration(device::BrickletPerformanceDC, channel, debounce, stop_deceleration)
    device.deviceInternal.set_gpio_configuration(channel, debounce, stop_deceleration)
end

export get_gpio_configuration
"""
    $(SIGNATURES)

Returns the GPIO configuration for a channel as set by :func:`Set GPIO Configuration`.
"""
function get_gpio_configuration(device::BrickletPerformanceDC, channel)
    return device.deviceInternal.get_gpio_configuration(channel)
end

export set_gpio_action
"""
    $(SIGNATURES)

Sets the GPIO action for the given channel.

The action can be a normal stop, a full brake or a callback. Each for a rising
edge or falling edge. The actions are a bitmask they can be used at the same time.
You can for example trigger a full brake and a callback at the same time or for
rising and falling edge.

The deceleration speed for the normal stop can be configured with
:func:`Set GPIO Configuration`.
"""
function set_gpio_action(device::BrickletPerformanceDC, channel, action)
    device.deviceInternal.set_gpio_action(channel, action)
end

export get_gpio_action
"""
    $(SIGNATURES)

Returns the GPIO action for a channel as set by :func:`Set GPIO Action`.
"""
function get_gpio_action(device::BrickletPerformanceDC, channel)
    return device.deviceInternal.get_gpio_action(channel)
end

export get_gpio_state
"""
    $(SIGNATURES)

Returns the GPIO state for both channels. True if the state is ``high`` and
false if the state is ``low``.
"""
function get_gpio_state(device::BrickletPerformanceDC)
    return device.deviceInternal.get_gpio_state()
end

export set_error_led_config
"""
    $(SIGNATURES)

Configures the error LED to be either turned off, turned on, blink in
heartbeat mode or show an error.

If the LED is configured to show errors it has three different states:

* Off: No error present.
* 1s interval blinking: Input voltage too low (below 6V).
* 250ms interval blinking: Overtemperature or overcurrent.
"""
function set_error_led_config(device::BrickletPerformanceDC, config)
    device.deviceInternal.set_error_led_config(config)
end

export get_error_led_config
"""
    $(SIGNATURES)

Returns the LED configuration as set by :func:`Set Error LED Config`
"""
function get_error_led_config(device::BrickletPerformanceDC)
    return device.deviceInternal.get_error_led_config()
end

export set_cw_led_config
"""
    $(SIGNATURES)

Configures the CW LED to be either turned off, turned on, blink in
heartbeat mode or if the motor turn clockwise.
"""
function set_cw_led_config(device::BrickletPerformanceDC, config)
    device.deviceInternal.set_cw_led_config(config)
end

export get_cw_led_config
"""
    $(SIGNATURES)

Returns the LED configuration as set by :func:`Set CW LED Config`
"""
function get_cw_led_config(device::BrickletPerformanceDC)
    return device.deviceInternal.get_cw_led_config()
end

export set_ccw_led_config
"""
    $(SIGNATURES)

Configures the CCW LED to be either turned off, turned on, blink in
heartbeat mode or if the motor turn counter-clockwise.
"""
function set_ccw_led_config(device::BrickletPerformanceDC, config)
    device.deviceInternal.set_ccw_led_config(config)
end

export get_ccw_led_config
"""
    $(SIGNATURES)

Returns the LED configuration as set by :func:`Set CCW LED Config`
"""
function get_ccw_led_config(device::BrickletPerformanceDC)
    return device.deviceInternal.get_ccw_led_config()
end

export set_gpio_led_config
"""
    $(SIGNATURES)

Configures the GPIO LED to be either turned off, turned on, blink in
heartbeat mode or the GPIO state.

The GPIO LED can be configured for both channels.
"""
function set_gpio_led_config(device::BrickletPerformanceDC, channel, config)
    device.deviceInternal.set_gpio_led_config(channel, config)
end

export get_gpio_led_config
"""
    $(SIGNATURES)

Returns the LED configuration as set by :func:`Set GPIO LED Config`
"""
function get_gpio_led_config(device::BrickletPerformanceDC, channel)
    return device.deviceInternal.get_gpio_led_config(channel)
end

export set_emergency_shutdown_callback_configuration
"""
    $(SIGNATURES)

Enable/Disable :cb:`Emergency Shutdown` callback.
"""
function set_emergency_shutdown_callback_configuration(device::BrickletPerformanceDC, enabled)
    device.deviceInternal.set_emergency_shutdown_callback_configuration(enabled)
end

export get_emergency_shutdown_callback_configuration
"""
    $(SIGNATURES)

Returns the callback configuration as set by
:func:`Set Emergency Shutdown Callback Configuration`.
"""
function get_emergency_shutdown_callback_configuration(device::BrickletPerformanceDC)
    return device.deviceInternal.get_emergency_shutdown_callback_configuration()
end

export set_velocity_reached_callback_configuration
"""
    $(SIGNATURES)

Enable/Disable :cb:`Velocity Reached` callback.
"""
function set_velocity_reached_callback_configuration(device::BrickletPerformanceDC, enabled)
    device.deviceInternal.set_velocity_reached_callback_configuration(enabled)
end

export get_velocity_reached_callback_configuration
"""
    $(SIGNATURES)

Returns the callback configuration as set by
:func:`Set Velocity Reached Callback Configuration`.
"""
function get_velocity_reached_callback_configuration(device::BrickletPerformanceDC)
    return device.deviceInternal.get_velocity_reached_callback_configuration()
end

export set_current_velocity_callback_configuration
"""
    $(SIGNATURES)

The period is the period with which the :cb:`Current Velocity`
callback is triggered periodically. A value of 0 turns the callback off.

If the `value has to change`-parameter is set to true, the callback is only
triggered after the value has changed. If the value didn't change within the
period, the callback is triggered immediately on change.

If it is set to false, the callback is continuously triggered with the period,
independent of the value.
"""
function set_current_velocity_callback_configuration(device::BrickletPerformanceDC, period, value_has_to_change)
    device.deviceInternal.set_current_velocity_callback_configuration(period, value_has_to_change)
end

export get_current_velocity_callback_configuration
"""
    $(SIGNATURES)

Returns the callback configuration as set by
:func:`Set Current Velocity Callback Configuration`.
"""
function get_current_velocity_callback_configuration(device::BrickletPerformanceDC)
    return device.deviceInternal.get_current_velocity_callback_configuration()
end

export get_spitfp_error_count
"""
    $(SIGNATURES)

Returns the error count for the communication between Brick and Bricklet.

The errors are divided into

* ACK checksum errors,
* message checksum errors,
* framing errors and
* overflow errors.

The errors counts are for errors that occur on the Bricklet side. All
Bricks have a similar function that returns the errors on the Brick side.
"""
function get_spitfp_error_count(device::BrickletPerformanceDC)
    return device.deviceInternal.get_spitfp_error_count()
end

export set_bootloader_mode
"""
    $(SIGNATURES)

Sets the bootloader mode and returns the status after the requested
mode change was instigated.

You can change from bootloader mode to firmware mode and vice versa. A change
from bootloader mode to firmware mode will only take place if the entry function,
device identifier and CRC are present and correct.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function set_bootloader_mode(device::BrickletPerformanceDC, mode)
    return device.deviceInternal.set_bootloader_mode(mode)
end

export get_bootloader_mode
"""
    $(SIGNATURES)

Returns the current bootloader mode, see :func:`Set Bootloader Mode`.
"""
function get_bootloader_mode(device::BrickletPerformanceDC)
    return device.deviceInternal.get_bootloader_mode()
end

export set_write_firmware_pointer
"""
    $(SIGNATURES)

Sets the firmware pointer for :func:`Write Firmware`. The pointer has
to be increased by chunks of size 64. The data is written to flash
every 4 chunks (which equals to one page of size 256).

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function set_write_firmware_pointer(device::BrickletPerformanceDC, pointer)
    device.deviceInternal.set_write_firmware_pointer(pointer)
end

export write_firmware
"""
    $(SIGNATURES)

Writes 64 Bytes of firmware at the position as written by
:func:`Set Write Firmware Pointer` before. The firmware is written
to flash every 4 chunks.

You can only write firmware in bootloader mode.

This function is used by Brick Viewer during flashing. It should not be
necessary to call it in a normal user program.
"""
function write_firmware(device::BrickletPerformanceDC, data)
    return device.deviceInternal.write_firmware(data)
end

export set_status_led_config
"""
    $(SIGNATURES)

Sets the status LED configuration. By default the LED shows
communication traffic between Brick and Bricklet, it flickers once
for every 10 received data packets.

You can also turn the LED permanently on/off or show a heartbeat.

If the Bricklet is in bootloader mode, the LED is will show heartbeat by default.
"""
function set_status_led_config(device::BrickletPerformanceDC, config)
    device.deviceInternal.set_status_led_config(config)
end

export get_status_led_config
"""
    $(SIGNATURES)

Returns the configuration as set by :func:`Set Status LED Config`
"""
function get_status_led_config(device::BrickletPerformanceDC)
    return device.deviceInternal.get_status_led_config()
end

export get_chip_temperature
"""
    $(SIGNATURES)

Returns the temperature as measured inside the microcontroller. The
value returned is not the ambient temperature!

The temperature is only proportional to the real temperature and it has bad
accuracy. Practically it is only useful as an indicator for
temperature changes.
"""
function get_chip_temperature(device::BrickletPerformanceDC)
    return device.deviceInternal.get_chip_temperature()
end

export reset
"""
    $(SIGNATURES)

Calling this function will reset the Bricklet. All configurations
will be lost.

After a reset you have to create new device objects,
calling functions on the existing ones will result in
undefined behavior!
"""
function reset(device::BrickletPerformanceDC)
    device.deviceInternal.reset()
end

export write_uid
"""
    $(SIGNATURES)

Writes a new UID into flash. If you want to set a new UID
you have to decode the Base58 encoded UID string into an
integer first.

We recommend that you use Brick Viewer to change the UID.
"""
function write_uid(device::BrickletPerformanceDC, uid)
    device.deviceInternal.write_uid(uid)
end

export read_uid
"""
    $(SIGNATURES)

Returns the current UID as an integer. Encode as
Base58 to get the usual string version.
"""
function read_uid(device::BrickletPerformanceDC)
    return device.deviceInternal.read_uid()
end

export get_identity
"""
    $(SIGNATURES)

Returns the UID, the UID where the Bricklet is connected to,
the position, the hardware and firmware version as well as the
device identifier.

The position can be 'a', 'b', 'c', 'd', 'e', 'f', 'g' or 'h' (Bricklet Port).
A Bricklet connected to an :ref:`Isolator Bricklet <isolator_bricklet>` is always at
position 'z'.

The device identifier numbers can be found :ref:`here <device_identifier>`.
|device_identifier_constant|
"""
function get_identity(device::BrickletPerformanceDC)
    return device.deviceInternal.get_identity()
end

export register_callback
"""
Registers the given *function* with the given *callback_id*.
"""
function register_callback(device::BrickletPerformanceDC, callback_id, function_)
    if isnothing(function_)
        device.registered_callbacks.pop(callback_id, None)
    else
        device.registered_callbacks[callback_id] = function_
    end
end


text:
# MIT License
#
# Copyright (c) 2018 Martin Biel
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

struct SMPSStage{T <: AbstractFloat, M <: AbstractMatrix}
    id::Int
    model::LPData{T,M}
    uncertain::LPData{T,M}
    technology::UnitRange{Int}
    recourse::UnitRange{Int}

    function SMPSStage(model::LPData{T,M}) where {T <: AbstractFloat, M <: AbstractMatrix}
        return new{T, M}(1, model, LPData(T,M,0,0,0), 0:0, 0:0)
    end

    function SMPSStage(id::Integer, model::LPData{T,M}, uncertain::LPData{T,M}, technology::UnitRange{Int}, recourse::UnitRange{Int}) where {T <: AbstractFloat, M <: AbstractMatrix}
        return new{T, M}(id, model, uncertain, technology, recourse)
    end
end


text:
#=
An sorted array of integers was rotated an unknown number of times.

Given such an array, find the index of the element in the array in faster than linear time. If the element doesn't exist in the array, return null.

For example, given the array [13, 18, 25, 2, 8, 10] and the element 8, return 4 (the index of 8 in the array).

You can assume all the integers in the array are unique.
=#

function findnextpivot(X::Array{Int, 1})
    i = 1
    while X[i] < X[i+1] && i < length(X)
        i = i+1
    end
    return i
end

function findindex(X::Array{Int, 1}, element::Int)
    i = 1
    while X[i] != element && i < length(X)
        if X[i] > element
            i = findnextpivot(X[i:end]) + 1
        elseif X[i] < element
            i = i + 1
        end
    end
    if i > length(X)
        return nothing
    else
        return i
    end
end

max_stars_repo_name: DominiqueCaron/daily-coding-problem

max_issues_repo_name: DominiqueCaron/daily-coding-problem

max_forks_repo_name: DominiqueCaron/daily-coding-problem


text:
using IntervalSets

function brainobservatorycache(manifest_file=brainobservatorymanifest)
   brain_observatory_cache.BrainObservatoryCache(manifest_file=manifest_file)
end

function getophysexperiments(; kwargs...)
    brainobservatorycache().get_ophys_experiments(; kwargs...)
end

function getspatialgrating(; height=100, aspect_ratio=2, ori=45, pix_per_cycle=1/0.04, phase=0, p2p_amp=0.8, baseline=p2p_amp/2)
    if any([ori, pix_per_cycle, phase, p2p_amp, baseline] .== ["null"]) # They showed blank (gray) gratings
        f = stimulus_info.get_spatial_grating(; height, aspect_ratio, ori=45, pix_per_cycle=height, phase=0, p2p_amp=0.8, baseline=0)
        f .= 0.4 # A gray color, mean of sine between 0 and 0.8
    end
    stimulus_info.get_spatial_grating(; height, aspect_ratio, ori, pix_per_cycle, phase, p2p_amp, baseline)
end

function getdriftinggrating(t::Real; temporal_frequency=2, kwargs...)
    d = kwargs |> Dict{Symbol,Any}
    d[:phase] =  pop!(d, :phase, 0) + (t*temporal_frequency)%1 # ! This is how they do it in the allensdk
    getspatialgrating(; d...)
end

function getnaturalmovie(stimulus)
    boc = getophysexperiments(stimuli=[stimulus])[1] # Just need one of these sessions to get the template
    ds = brainobservatorycache().get_ophys_experiment_data(boc["id"])
    template = ds.get_stimulus_template(stimulus)
end

function getnaturalstimulusframes(session, times)
    df = getstimuli(session, times)
    movies = unique(df.stimulus_name)
    @assert all(.∈(movies , (["natural_movie_one", "natural_movie_three", "natural_scenes"],)))
    movieframes = [getnaturalmovie(movie) for movie in movies]
    frames = Vector{Matrix{Float32}}(undef, length(times))
    for t in 1:length(times)
        movieidx = indexin([df.stimulus_name[t]], movies)[1]
        frame = Int(Meta.parse(df.frame[t])+1) # Goddamn python frames start at 0
        if frame < 1
            frames[t] = fill(1.0, size(frames[t-1]))
        else
            frames[t] = movieframes[movieidx][frame, :, :]./256
        end
    end
    return DimArray(frames, (Dim{:time}(times)))
end

getnaturalmovieframes = getnaturalstimulusframes

function getdriftinggratingframes(session, times)
    df = getstimuli(session, times)
    height = 100
    aspect_ratio = 2
    frames = [Matrix{Float32}(undef, height, aspect_ratio*height) for t ∈ 1:length(times)]
    for t ∈ 1:lastindex(times)
        if any((df.temporal_frequency[t], df.orientation[t], df.contrast) .== ["null"])
            frames[t] .= 0.4
        else
            frames[t] = getdriftinggrating(times[t] - df.start_time[t];
                    temporal_frequency=df.temporal_frequency[t]|>Meta.parse,
                    height,
                    aspect_ratio,
                    ori=df.orientation[t]|>Meta.parse,
                    pix_per_cycle=1/Meta.parse(df.spatial_frequency[t]),
                    p2p_amp=df.contrast[t]|>Meta.parse)
        end
    end
    return DimArray(frames, (Dim{:time}(times)))
end

function getstaticgratingframes(session, times, spontaneous=false)
    df = getstimuli(session, times)
    height = 100
    aspect_ratio = 2
    frames = [Matrix{Float32}(undef, height, aspect_ratio*height) for t ∈ 1:length(times)]
    for t ∈ 1:lastindex(times)
        if any((df.orientation[t], df.contrast) .== ["null"]) || spontaneous
            frames[t] .= 0.4
        else
            frames[t] = getspatialgrating(;
                    height,
                    aspect_ratio,
                    ori=df.orientation[t]|>Meta.parse,
                    pix_per_cycle=1/Meta.parse(df.spatial_frequency[t]),
                    p2p_amp=df.contrast[t]|>Meta.parse)
        end
    end
    return DimArray(frames, (Dim{:time}(times)))
end

function _getstimulusframes(session, times, stimulus)
    if stimulus ∈ ["static_gratings", "spontaneous"] # Spontaneous is just a mean-luminance blank screen]
        getstaticgratingframes(session, times, stimulus=="spontaneous")
    elseif stimulus == "drifting_gratings"
        getdriftinggratingframes(session, times)
    elseif stimulus ∈ ["natural_movie_one", "natural_movie_three", "natural_scenes"]
        getnaturalstimulusframes(session, times)
    end
end

function getstimulusframes(session, times)
    epochs = getepochs(session)
    epochs = epochs[[any(times .∈ x.start_time..x.stop_time) for x ∈ eachrow(epochs)], :] # Get epochs that contain the times
    epochtimes = [[t for t ∈ times if t ∈ x.start_time..x.stop_time] for x ∈ eachrow(epochs)]
    frames = [_getstimulusframes(session, epochtimes[x], epochs.stimulus_name[x]) for x ∈ 1:length(epochtimes)]
    frames = DimArray(vcat(frames...), (Dim{:time}(times)))
end