text:
module Schema

using ..DataModel, ..Contexts, ..ConnectionPools
using SQLite, DBInterface, Strapping, Tables

const DB_POOL = Ref{ConnectionPools.Pod{ConnectionPools.Connection{SQLite.DB}}}()
const COUNTER = Ref{Int64}(0)

function init(dbfile)
    new = () -> SQLite.DB(dbfile)
    DB_POOL[] = ConnectionPools.Pod(SQLite.DB, Threads.nthreads(), 60, 1000, new)
    if !isfile(dbfile)
        db = SQLite.DB(dbfile)
        DBInterface.execute(
            db,
            """
    CREATE TABLE sysalerts (
        id INTEGER,
        entity TEXT,
        unit TEXT,
        status TEXT,
        last_updated TEXT,
        message TEXT
    )
"""
        )
        DBInterface.execute(
            db,
            """
    CREATE INDEX idx_sysid_id ON sysalerts (id)
"""
        )
        #         DBInterface.execute(
        #             db,
        #             """
        #     CREATE TABLE album (
        #         id INTEGER,
        #         userid INTEGER,
        #         name TEXT,
        #         artist TEXT,
        #         year INTEGER,
        #         timespicked INTEGER DEFAULT 0,
        #         songs TEXT
        #     )
        # """
        #         )
        #         DBInterface.execute(
        #             db,
        #             """
        #     CREATE INDEX idx_album_id ON album (id)
        # """
        #         )
        #         DBInterface.execute(
        #             db,
        #             """
        #     CREATE INDEX idx_album_userid ON album (userid)
        # """
        #         )
        #         DBInterface.execute(
        #             db,
        #             """
        #     CREATE INDEX idx_album_id_userid ON album (id, userid)
        # """
        #         )
        #         DBInterface.execute(
        #             db,
        #             """
        #     CREATE TABLE user (
        #         id INTEGER PRIMARY KEY,
        #         username TEXT,
        #         password TEXT
        #     )
        # """
        #         )
    end
    return
end

function insert!(sysalerts::SystemAlert)
    cur = execute("""
        INSERT INTO sysalerts (id, entity, unit, status, last_updated, message) VALUES(?, ?, ?, ?, ?, ?)
    """, (sysalerts.id, sysalerts.entity, sysalerts.unit, sysalerts.status, sysalerts.last_updated, sysalerts.message); executemany = false)
    show(cur)
    return
end

function execute(sql, params; executemany::Bool = false)
    withconnection(DB_POOL[]) do db
        stmt = DBInterface.prepare(db, sql)
        if executemany
            DBInterface.executemany(stmt, params)
        else
            DBInterface.execute(stmt, params)
        end
    end
end

function getSystemAlerts()
    cursor = execute("""
    SELECT * FROM sysalerts
    """, ())
    return Strapping.construct(Vector{SystemAlert}, cursor)
end

# function insert(album)
#     user = Contexts.getuser()
#     album.userid = user.id
#     execute("""
#         INSERT INTO album (id, userid, name, artist, year, timespicked, songs) VALUES(?, ?, ?, ?, ?, ?, ?)
#     """, columntable(Strapping.deconstruct(album)); executemany = true)
#     return
# end

# function create!(album::Album)
#     album.id = COUNTER[] += 1
#     insert(album)
#     return
# end

# function update(album)
#     delete(album.id)
#     insert(album)
#     return
# end

# function get(id)
#     user = Contexts.getuser()
#     cursor = execute("SELECT * FROM album WHERE id = ? AND userid = ?", (id, user.id))
#     return Strapping.construct(Album, cursor)
# end

# function delete(id)
#     user = Contexts.getuser()
#     execute("DELETE FROM album WHERE id = ? AND userid = ?", (id, user.id))
#     return
# end

# function getAllAlbums()
#     user = Contexts.getuser()
#     cursor = execute("SELECT * FROM album WHERE userid = ?", (user.id,))
#     return Strapping.construct(Vector{Album}, cursor)
# end

# function create!(user::User)
#     x = execute("""
#         INSERT INTO user (username, password) VALUES (?, ?)
#     """, (user.username, user.password))
#     user.id = DBInterface.lastrowid(x)
#     return
# end

# function get(user::User)
#     cursor = execute("SELECT * FROM user WHERE username = ?", (user.username,))
#     return Strapping.construct(User, cursor)
# end

end # module


text:
# all also support the HTML_global_attributes
const ElementAttributes = Dict{Symbol, Set}(
    :button => Set([:autofocus, :disabled, :name, :type, :value,
                    :form, :formaction, :formenctype, :formmethod, :formnovalidate, :formtarget]),
    :button_unattached => Set([:autofocus, :disabled, :name, :type, :value]),
    :img => Set([:alt, :crossorigin, :height, :ismap, :longdesc, :sizes, :src, :srcset, :usemap, :width]),
    :label => Set([:for, :form]),
  )

# Events

const WindowEvents = Set([:onafterprint, :onbeforeprint, :onbeforeunload, :onerror, :onhashchange, :onload, :onmessage,
                          :onoffline, :ononline, :onpagehide, :onpageshow, :onpopsate, :onresize, :onstorage, :onunload])
const FormEvents = Set([:onblur, :onchange, :oncontextmenu, :onfocus, :oninput, :oninvalid, :onreset, :onsearch, :onselect, :onsubmit])
const KeyboardEvents = Set([:onkeydown, :onkeypress, :onkeyup])
const MouseEvents = Set([:onclick, :ondblclick, :onmousedown, :onmousemove, :onmouseout, :onmouseover, :onmouseup, :onwheeel])
const DragEvents = Set([:ondrag, :ondragend, :ondregenter, :ondragleave, :ondragover, :ondragstart, :ondrop, :onscroll])
const ClipboardEvents = Set([:oncopy, :oncut, :onpaste])
const MediaEvents = Set([:onabort, :oncanplay, :oncanplaythrough, :oncuechange, :ondurationchange, :oneemptied, :onended, :onerror,
                         :onloadeddata, :onloadedmetadata, :onloadstart, :onpause, :onplay, :onplaying, :onprogress,
                         :onratechange, :onseeked, :onseeking, :onstalled, :onsuspend, :ontimeupdate, :onvolumechange, :onwaiting])
const DetailsEvents = Set([:ontoggle,])

ext: jl


text:
"""
    Code for convenience functions for calculating summed CCFs for each spectra in an AbstractChunkListTimeseries (see RvSpectMLBase)
Author: Eric Ford
Created: August 2020
"""

"""  `calc_ccf_chunklist_timeseries( chunklist_timeseries, line_list )`
Convenience function to compute CCF for a timeseries of spectra, each with a chunklist.
Uses multiple threads if avaliable.
# Inputs:
- chunklist_timeseries
# Optional Arguments:
- ccf_plan (BasicCCFPlan())
- verbose (false)
# Return:
CCF summed over all chunks in a spectrum's chunklist, evaluated using the ccf_plan.
Note that the ccf_plan provided is used as a template for creating a custom ccf_plan for each chunk that
    only includes lines that reliably appear in that order for all spectra in the chunklist_timeseries.
"""
function calc_ccf_chunklist_timeseries(clt::AbstractChunkListTimeseries,
                                plan::PlanT = BasicCCFPlan(); verbose::Bool = false,
                                ccf_var_scale::Real = 1.0, Δfwhm::AbstractVector{T} = zeros(0),
                                calc_ccf_var::Bool = false, calc_ccf_covar::Bool = false  ) where { PlanT<:AbstractCCFPlan, T<:Real }

  @assert length(clt) >= 1
  @assert length(Δfwhm) == 0 || length(Δfwhm) == length(clt)
  num_lines = length(plan.line_list.λ)
  plan_for_chunk = Vector{BasicCCFPlan}(undef,num_chunks(clt))

  for chid in 1:num_chunks(clt)
      order = clt[1].order[chid]
      if typeof(plan.line_list) <: BasicLineList2D
          start_order_idx = searchsortedfirst(plan.line_list.order,order)
          stop_order_idx = searchsortedlast(view(plan.line_list.order,start_order_idx:num_lines),order) + start_order_idx-1
      else
          start_order_idx = 1
          stop_order_idx = num_lines
      end

      if verbose
          println("# Chunk ", chid, " order= ", order)
          println("# start_order_idx = ", start_order_idx, "    stop_order_idx = ", stop_order_idx)
              if (1<=start_order_idx<=stop_order_idx<=num_lines) && typeof(plan.line_list) <: BasicLineList2D
                  println("   order = ", plan.line_list.order[start_order_idx], " - ", plan.line_list.order[stop_order_idx])
              end
      end

      if (1<=start_order_idx<=stop_order_idx<=num_lines)
          # find the maximum lower wavelength for the chunk, and the minumum upper wavelength, over all observations
          λmin = maximum(map(obsid->first(clt.chunk_list[obsid].data[chid].λ), 1:length(clt) ))
          λmax = minimum(map(obsid->last( clt.chunk_list[obsid].data[chid].λ), 1:length(clt) ))
          # extend λmin/λmax by the velocity range over which we don't want the mask to change
          λmin  = λmin / (calc_doppler_factor(plan.v_center)*calc_doppler_factor(-plan.v_range_no_mask_change))
          λmax  = λmax / (calc_doppler_factor(plan.v_center)*calc_doppler_factor(plan.v_range_no_mask_change))
          # extend λmin/λmax by the mask width
          upper_edge_of_mask_for_line_at_λmin = λ_max(plan.mask_shape,λmin)
          lower_edge_of_mask_for_line_at_λmax = λ_min(plan.mask_shape,λmax)
          # find the first and last mask entries to use in each chunk
          start_line_idx = searchsortedfirst(view(plan.line_list.λ,start_order_idx:stop_order_idx),upper_edge_of_mask_for_line_at_λmin) + start_order_idx-1
          stop_line_idx  = start_order_idx-1 + searchsortedlast(view(plan.line_list.λ,start_order_idx:stop_order_idx),lower_edge_of_mask_for_line_at_λmax)
          if verbose
              flush(stdout)
              println("extrema(plan.line_list.λ) = ",extrema(plan.line_list.λ) )
              if (1 <= start_line_idx <= length(plan.line_list.λ)) && (1 <= stop_line_idx <= length(plan.line_list.λ))
                  println("start_order_idx = ", start_order_idx, "  stop_order_idx = ", stop_order_idx)
                  println("start_line_idx = ", start_line_idx, " λ= ", plan.line_list.λ[start_line_idx]) #, " order= ", plan.line_list.order[start_line_idx])
                  println("stop_line_idx = ", stop_line_idx, " λ= ", plan.line_list.λ[stop_line_idx]) #, " order= ", plan.line_list.order[stop_line_idx])
              end
              println("upper_edge_of_mask_for_line_at_λmin = ", upper_edge_of_mask_for_line_at_λmin, " Lower_edge_of_mask_for_line_at_λmax = ", lower_edge_of_mask_for_line_at_λmax)
          end
          if (1 <= start_line_idx <= length(plan.line_list.λ)) && (1 <= stop_line_idx <= length(plan.line_list.λ))
              if verbose && typeof(plan.line_list) <: BasicLineList2D
                  println("start_line_idx = ", start_line_idx, " λ= ", plan.line_list.λ[start_line_idx], " order= ", plan.line_list.order[start_line_idx])
                  println("stop_line_idx = ", stop_line_idx, " λ= ", plan.line_list.λ[stop_line_idx], " order= ", plan.line_list.order[stop_line_idx])
                  println(" Using ", length(start_line_idx:stop_line_idx), " lines for chunk ", chid, " order ", order)
              end
              if ! issorted( view(plan.line_list.λ, start_line_idx:stop_line_idx ) )
                  println("# View is not sorted.  Why?")
                  for i in start_line_idx:stop_line_idx
                      println("i= ", i, " λ= ", plan.line_list.λ[i], " order= ", plan.line_list.order[i])
                  end
              end
              @assert issorted( view(plan.line_list.λ, start_line_idx:stop_line_idx ) )
              # line_list_for_chunk = BasicLineList2D(view(plan.line_list.λ,start_line_idx:stop_line_idx), view(plan.line_list.weight,start_line_idx:stop_line_idx), fill(order,length(start_line_idx:stop_line_idx)) )
              line_list_for_chunk = BasicLineList(view(plan.line_list.λ,start_line_idx:stop_line_idx), view(plan.line_list.weight,start_line_idx:stop_line_idx)  )
          else  # No lines in this chunk!
              line_list_for_chunk = EmptyBasicLineList()
          end
      else
        line_list_for_chunk = EmptyBasicLineList()
      end

      #create a plan for this chunk that only includes the mask entries we want for this chunk
      plan_for_chunk[chid] = BasicCCFPlan( line_list=line_list_for_chunk, midpoint=plan.v_center, step=plan.v_step, max=plan.v_max, mask_shape=plan.mask_shape )
  end
  if calc_ccf_covar
       if length(Δfwhm) == length(clt)
          list_of_ccf_and_covar = @threaded  map(obsid->calc_ccf_and_covar_chunklist(clt.chunk_list[obsid], plan_for_chunk, ccf_var_scale=ccf_var_scale, Δfwhm=Δfwhm[obsid], assume_sorted=true ), 1:length(clt) )
       else
          list_of_ccf_and_covar = @threaded  map(obsid->calc_ccf_and_covar_chunklist(clt.chunk_list[obsid], plan_for_chunk, ccf_var_scale=ccf_var_scale, assume_sorted=true ), 1:length(clt) )
       end
       nvs = length(first(list_of_ccf_and_covar).ccf)
       ccfs_out = zeros(nvs,length(clt))
       ccf_covars_out = zeros(nvs,nvs,length(clt))
       for obsid in 1:length(clt)
           ccfs_out[:,obsid] .= list_of_ccf_and_covar[obsid].ccf
           ccf_covars_out[:,:,obsid] .= list_of_ccf_and_covar[obsid].ccf_covar
       end
       return (ccfs=ccfs_out, ccf_covars=ccf_covars_out)
  elseif calc_ccf_var
      if length(Δfwhm) == length(clt)
          list_of_ccf_and_var = @threaded  map(obsid->calc_ccf_and_var_chunklist(clt.chunk_list[obsid], plan_for_chunk, ccf_var_scale=ccf_var_scale, Δfwhm=Δfwhm[obsid], assume_sorted=true ), 1:length(clt) )
      else
          list_of_ccf_and_var = @threaded  map(obsid->calc_ccf_and_var_chunklist(clt.chunk_list[obsid], plan_for_chunk, ccf_var_scale=ccf_var_scale, assume_sorted=true ), 1:length(clt) )
      end
       nvs = length(first(list_of_ccf_and_var).ccf)
       ccfs_out = zeros(nvs,length(clt))
       ccf_vars_out = zeros(nvs,length(clt))
       for obsid in 1:length(clt)
           ccfs_out[:,obsid] .= list_of_ccf_and_var[obsid].ccf
           ccf_vars_out[:,obsid] .= list_of_ccf_and_var[obsid].ccf_var
       end
       return (ccfs=ccfs_out, ccf_vars=ccf_vars_out)
  else
      if length(Δfwhm) == length(clt)
         return @threaded mapreduce(obsid->calc_ccf_chunklist(clt.chunk_list[obsid], plan_for_chunk, Δfwhm=Δfwhm[obsid], assume_sorted=true ),hcat, 1:length(clt) )
     else
         return @threaded mapreduce(obsid->calc_ccf_chunklist(clt.chunk_list[obsid], plan_for_chunk, assume_sorted=true ),hcat, 1:length(clt) )
     end
  end
end

max_stars_repo_name: RvSpectML/EchelleCCFs

max_issues_repo_name: RvSpectML/EchelleCCFs

max_forks_repo_name: RvSpectML/EchelleCCFs


text:
# ---
# title: 1604. Alert Using Same Key-Card Three or More Times in a One Hour Period
# id: problem1604
# author: AquaIndigo
# date: 2020-11-08
# difficulty: Medium
# categories: String, Ordered Map
# link: <https://leetcode.com/problems/alert-using-same-key-card-three-or-more-times-in-a-one-hour-period/description/>
# hidden: true
# ---
#
# LeetCode company workers use key-cards to unlock office doors. Each time a
# worker uses their key-card, the security system saves the worker's name and
# the time when it was used. The system emits an **alert** if any worker uses
# the key-card **three or more times** in a one-hour period.
#
# You are given a list of strings `keyName` and `keyTime` where `[keyName[i],
# keyTime[i]]` corresponds to a person's name and the time when their key-card
# was used **in a** **single day**.
#
# Access times are given in the **24-hour time format "HH:MM"**, such as
# `"23:51"` and `"09:49"`.
#
# Return a _list of unique worker names who received an alert for frequent
# keycard use_. Sort the names in **ascending order alphabetically**.
#
# Notice that `"10:00"` \- `"11:00"` is considered to be within a one-hour
# period, while `"22:51"` \- `"23:52"` is not considered to be within a one-hour
# period.
#
#
#
# **Example 1:**
#
#
#
#     Input: keyName = ["daniel","daniel","daniel","luis","luis","luis","luis"], keyTime = ["10:00","10:40","11:00","09:00","11:00","13:00","15:00"]
#     Output: ["daniel"]
#     Explanation: "daniel" used the keycard 3 times in a one-hour period ("10:00","10:40", "11:00").
#
#
# **Example 2:**
#
#
#
#     Input: keyName = ["alice","alice","alice","bob","bob","bob","bob"], keyTime = ["12:01","12:00","18:00","21:00","21:20","21:30","23:00"]
#     Output: ["bob"]
#     Explanation: "bob" used the keycard 3 times in a one-hour period ("21:00","21:20", "21:30").
#
#
# **Example 3:**
#
#
#
#     Input: keyName = ["john","john","john"], keyTime = ["23:58","23:59","00:01"]
#     Output: []
#
#
# **Example 4:**
#
#
#
#     Input: keyName = ["leslie","leslie","leslie","clare","clare","clare","clare"], keyTime = ["13:00","13:20","14:00","18:00","18:51","19:30","19:49"]
#     Output: ["clare","leslie"]
#
#
#
#
# **Constraints:**
#
#   * `1 <= keyName.length, keyTime.length <= 105`
#   * `keyName.length == keyTime.length`
#   * `keyTime[i]` is in the format **" HH:MM"**.
#   * `[keyName[i], keyTime[i]]` is **unique**.
#   * `1 <= keyName[i].length <= 10`
#   * `keyName[i] contains only lowercase English letters.`
#
#
## @lc code=start
using LeetCode
using Dates

function alert_names(key_name::Vector{String}, key_time::Vector{String})
    res = Vector{String}()
    mp = Dict{String,Vector{Time}}()
    for (idx, name) in enumerate(key_name)
        if !haskey(mp, name)
            mp[name] = String[]
        end
        push!(mp[name], Time(key_time[idx]))
    end
    for (name, time) in mp
        sort!(time)
        for idx in 1:(length(time) - 2)
            time1h = time[idx] + Hour(1)
            if time[idx + 1] <= time1h && time[idx + 2] <= time1h
                push!(res, name)
                break
            end
        end
    end
    return sort!(res)
end

## add your code here:
## @lc code=end


text:
#=
Copyright (c) 2016 Saurav Sachidanand

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.
=#

module CBOR

using Printf, Serialization, Base64

num2hex(n) = string(n, base = 16, pad = sizeof(n) * 2)
num2hex(n::AbstractFloat) = num2hex(reinterpret(Unsigned, n))
hex2num(s) = reinterpret(Float64, parse(UInt64, s, base = 16))
hex(n) = string(n, base = 16)

struct Tag{T}
    id::Int
    data::T
end
Base.:(==)(a::Tag, b::Tag) = a.id == b.id && a.data == b.data
Tag(id::Integer, data) = Tag(Int(id), data)

include("constants.jl")
include("encode.jl")
include("decode.jl")

export encode
export decode, decode_with_iana
export Simple, Null, Undefined

function decode(data::Array{UInt8, 1})
    return decode_internal(IOBuffer(data))
end

function decode(data::IO)
    return decode(read(data))
end

function encode(data)
    io = IOBuffer()
    encode(io, data)
    return take!(io)
end

end


text:
@testset "Diagonal" begin

    # diag:
    let P = 10, rng = MersenneTwister(123456), N = 10, k = 2

        # Some rngs.
        vP, mPP, vPk = ()->randn(rng, P), ()->randn(rng, P, P), ()->randn(rng, P - k)

        # Test on-central-diagonal `diag`.
        X0 = randn!(rng, similar(mPP()))
        @test check_errs(N, unary_ȲD(diag)..., vP, mPP, mPP)
        @test check_errs(N, unary_ȲD_inplace(diag, X0)..., vP, mPP, mPP)

        # Test off-central-diagonal `diag`.
        _diag = X->diag(X, k)
        _∇diag = (y, ȳ, X)->∇(diag, Val{1}, (), y, ȳ, X, k)
        _∇diag_inp = (y, ȳ, X)->∇(copy(X0), diag, Val{1}, (), y, ȳ, X, k) - X0
        @test check_errs(N, _diag, _∇diag, vPk, mPP, mPP)
        @test check_errs(N, _diag, _∇diag_inp, vPk, mPP, mPP)
    end

    # diagm:
    let P = 10, rng = MersenneTwister(123456), N = 10, k = 3

        # Some rngs.
        sc, vP = ()->randn(rng), ()->randn(rng, P)
        mPP, mPPk = ()->randn(rng, P, P), ()->randn(rng, P + k, P + k)

        # Test on-central-diagonal `diagm`.
        x0 = randn!(rng, similar(vP()))
        @test check_errs(N, unary_ȲD(diagm)..., mPP, vP, vP)
        @test check_errs(N, unary_ȲD_inplace(diagm, x0)..., mPP, vP, vP)

        # Test off-central-diagonal `diagm`.
        _diagm = x->diagm(x, k)
        _∇diagm = (Y, Ȳ, x)->∇(diagm, Val{1}, (), Y, Ȳ, x, k)
        _∇diagm_inp = (Y, Ȳ, x)->∇(copy(x0), diagm, Val{1}, (), Y, Ȳ, x, k) - x0
        @test check_errs(N, _diagm, _∇diagm, mPPk, vP, vP)
        @test check_errs(N, _diagm, _∇diagm_inp, mPPk, vP, vP)

        # Test scalar input to `diagm`.
        @test check_errs(N, unary_ȲD(diagm)..., ()->randn(rng, 1, 1), sc, sc)
    end

    # Diagonal:
    let P = 10, rng = MersenneTwister(123456), N = 10
        sc, vP = ()->abs(randn(rng)), ()->abs.(randn(rng, P))
        mPP, dPP = ()->abs.(randn(rng, P, P)), ()->Diagonal(abs.(randn(rng, P)))
        D0 = Diagonal(randn!(rng, similar(vP())))

        # Construction.
        @test check_errs(N, unary_ȲD(Diagonal)..., dPP, vP, vP)
        @test check_errs(N, unary_ȲD_inplace(Diagonal, vP())..., dPP, vP, vP)
        @test check_errs(N, unary_ȲD(Diagonal)..., dPP, mPP, mPP)
        @test check_errs(N, unary_ȲD_inplace(Diagonal, mPP())..., dPP, mPP, mPP)

        # Determinant.
        @test check_errs(N, unary_ȲD(det)..., sc, dPP, dPP)
        @test check_errs(N, unary_ȲD_inplace(det, D0)..., sc, dPP, dPP)

        # Log Determinant.
        @test check_errs(N, unary_ȲD(logdet)..., sc, dPP, dPP)
        @test check_errs(N, unary_ȲD_inplace(logdet, D0)..., sc, dPP, dPP)
    end
end


text:
import Gen
import Distributions
import LinearAlgebra: norm, cross, det, dot
import Rotations
using StaticArrays: SVector, SMatrix

# Hopf fibration, see: https://en.wikipedia.org/wiki/Hopf_fibration
# also see http://lavalle.pl/anna/papers/YerJaiLavMit10.pdf

normalize(vec) = vec / norm(vec)

# Finds the rotation that rotates that minimizes the cosine distance, giving a unique way of defining the new axes
# https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d/897677#897677
function find_minimal_angle_rotation(a::SVector{3,Float64}, b::SVector{3,Float64})
    a = normalize(a)
    b = normalize(b)
    if isapprox(a, -b)
        @warn "Two vectors are almost opposites; beware"
    end
    v = cross(a, b)
    s = norm(v)
    c = dot(a, b)
    Vcross = SMatrix{3,3,Float64}(
        0.0, v[3], -v[2], # column 1
        -v[3], 0.0, v[1], # column 2
        v[2], -v[1], 0.0) # column 3
    R = (
        (SMatrix{3,3,Float64}(
            1.0, 0.0, 0.0, # column 1
            0.0, 1.0, 0.0, # column 2
            0.0, 0.0, 1.0)) # column 3
        .+ Vcross .+ ((Vcross * Vcross) / (1 + c)))
    @assert isapprox(det(R), 1.0)
    return R
end

function check_angle_bounds(angle)
    if angle < -pi || angle > pi
        error("angle out of bounds [-pi, pi]")
    end
end

function to_rotation_matrix(z_axis::SVector{3,Float64}, angle_around_z_axis::Float64)
    if !isapprox(norm(z_axis), 1.0)
        error("z_axis was not a unit vector")
    end
    check_angle_bounds(angle_around_z_axis)
    R2DoF = find_minimal_angle_rotation(SVector{3,Float64}(0.0, 0.0, 1.0), z_axis)
    R1DoF = SMatrix{3,3,Float64}(
        cos(angle_around_z_axis), sin(angle_around_z_axis), 0.0, # column 1
        -sin(angle_around_z_axis), cos(angle_around_z_axis), 0.0, # column 2
        0.0, 0.0, 1.0) # column 3
    @assert isapprox(det(R1DoF), 1.0)
    R = R2DoF * R1DoF
    @assert isapprox(det(R), 1.0)
    return R
end

function is_rotation_around_z(R)
    eps = 1e-6
    return (norm(R * SVector{3,Float64}(0.0, 0.0, 1.0) .- SVector{3,Float64}(0.0, 0.0, 1.0)) < eps &&
        abs(R[3,1]) < eps &&
        abs(R[3,2]) < eps &&
        isapprox(R[2,2], R[1,1]) &&
        isapprox(R[1,2], -R[2,1]))
end

function from_rotation_matrix(R::SMatrix{3,3,Float64})::Tuple{SVector{3,Float64},Float64}
    z_axis = R[:,3]
    R2DoF = find_minimal_angle_rotation(SVector{3,Float64}(0.0, 0.0, 1.0), z_axis)
    R1DoF = R2DoF \ R
    @assert is_rotation_around_z(R1DoF)
    angle_around_z_axis = atan(R1DoF[2,1], R1DoF[1,1])
    return (z_axis, angle_around_z_axis)
end

function to_direction_and_plane_rotation(rot3::Rot3)::Tuple{UnitVector3,Rot2}
    R = Rotations.RotMatrix{3,Float64,9}(rot3).mat
    (_z_axis, _angle_around_z_axis) = from_rotation_matrix(R)
    direction = UnitVector3(_z_axis)
    rot2 = Rotations.RotMatrix{2,Float64}(_angle_around_z_axis)
    return (direction, rot2)
end

function from_direction_and_plane_rotation(direction::UnitVector3, plane_rotation::Rot2)::Rot3
    R = to_rotation_matrix(direction.v, Rotations.rotation_angle(plane_rotation))
    return Rot3(R)
end

# the Jacobian determinant of the transformation between S^3 and
# S^2 x S^1 should be constant.
# the total measure of S^3 is 2 * pi * pi
# the total measure of (S^2) x (S^1) is (4 * pi) * (2 * pi) = 8 * pi * pi
# therefore, the RJMCMC acceptance ratio must be multiplied/divided by a factor of 4

const FROM_DIRECTION_AND_PLANE_ROTATION_JACOBIAN_CORRECTION = (
    -log(total_area(UnitVector3)) - log(total_area(Rot2)) + log(total_area(Rot3)))

const TO_DIRECTION_AND_PLANE_ROTATION_JACOBIAN_CORRECTION = -FROM_DIRECTION_AND_PLANE_ROTATION_JACOBIAN_CORRECTION

export to_direction_and_plane_rotation
export from_direction_and_plane_rotation
export FROM_DIRECTION_AND_PLANE_ROTATION_JACOBIAN_CORRECTION
export TO_DIRECTION_AND_PLANE_ROTATION_JACOBIAN_CORRECTION


text:
"""
    SEIso <: Isotropic{SqEuclidean}

Isotropic Squared Exponential kernel (covariance)
```math
k(x,x') = σ²\\exp(- (x - x')ᵀ(x - x')/(2ℓ²))
```
with length scale ``ℓ`` and signal standard deviation ``σ``.
"""
mutable struct SEIso{T<:Real} <: Isotropic{SqEuclidean}
    "Squared length scale"
    ℓ2::T
    "Signal variance"
    σ2::T
    "Priors for kernel parameters"
    priors::Array
end

"""
Squared Exponential kernel function

    SEIso(ll::T, lσ::T)

# Arguments:
- `ll::Real`: length scale (given on log scale)
- `lσ::Real`: signal standard deviation (given on log scale)
"""
SEIso(ll::T, lσ::T) where T = SEIso{T}(exp(2 * ll), exp(2 * lσ), [])

function set_params!(se::SEIso, hyp::AbstractVector)
    length(hyp) == 2 || throw(ArgumentError("Squared exponential has two parameters, received $(length(hyp))."))
    se.ℓ2, se.σ2 = exp(2 * hyp[1]), exp(2 * hyp[2])
end

get_params(se::SEIso{T}) where T = T[log(se.ℓ2) / 2, log(se.σ2) / 2]
get_param_names(se::SEIso) = [:ll, :lσ]
num_params(se::SEIso) = 2

cov(se::SEIso, r::Number) = se.σ2*exp(-0.5*r/se.ℓ2)

@inline dk_dll(se::SEIso, r::Real) = r/se.ℓ2*cov(se,r)
@inline function dk_dθp(se::SEIso, r::Real, p::Int)
    if p==1
        return dk_dll(se, r)
    elseif p==2
        return dk_dlσ(se, r)
    else
        return NaN
    end
end


text:
const tag_factor = 1_000_000;

struct TimeTag
    time_tag::Int;
    function TimeTag(timetag::Int)
        validate_time_tag(timetag);
        new(timetag);
    end
end

TimeTag() = TimeTag(generate_time_tag_int());
TimeTag(timetag::AbstractString) = TimeTag(parse(Int, timetag));

Base.string(tt::TimeTag) = string(tt.time_tag);

generate_time_tag_int() = round(Int, time() * tag_factor);

strf_time_tag(tt::TimeTag) = Libc.strftime(tt.time_tag / tag_factor);
strf_time_tag(tt) = strf_time_tag(TimeTag(tt));


text:
#========== adjusting index ranges, runtime ==========#

# This is to get the range of j in A[2j], from axes(A,1):

function divrange(r::AbstractUnitRange, f::Integer)
    if f > 0
        # a = div(first(r), f, RoundUp) # onnly 1.4 it seems?
        a = cld(first(r), f)
        # z = div(last(r), f, RoundDown)
        z = fld(last(r), f)
    elseif f < 0
        # a = div(last(r), f, RoundUp)
        a = cld(last(r), f)
        # z = div(first(r), f, RoundDown)
        z = fld(first(r), f)
    else
        error("can't scale indices by zero")
    end
    a:z
end

#=
divrange(1:10, 2) .* 2
divrange(0:10, 2) .* 2
divrange(1:11, 2) .* 2
divrange(1:10, 3) .* 3

divrange(1:10, -1) .* -1 |> sort
divrange(1:10, -2) .* -2 |> sort
divrange(0:10, -2) .* -2 |> sort
divrange(0:11, -2) .* -2 |> sort
=#

# Special case of A[-i]:

function minusrange(r::AbstractRange)
    -last(r):-first(r)
end

#=
minusrange(1:11) == divrange(1:11, -1)
minusrange(1:10) == divrange(1:10, -1)
=#

# This is to get the range of j in A[j+k], given axes(A,1) and the range of k

function subranges(r::AbstractUnitRange, s::AbstractRange)
    first(r)-minimum(s) : last(r)-maximum(s)
end

function addranges(r::AbstractUnitRange, s::AbstractRange)
    first(r)+maximum(s) : last(r)+minimum(s)
end

#=
issubset(subranges(1:10, 1:3) .+ 1, 1:10)
issubset(subranges(1:10, 1:3) .+ 3, 1:10)

issubset(addranges(1:10, 1:3) .- 1, 1:10)
issubset(addranges(1:10, 1:3) .- 3, 1:10)
=#

# This is for A[I[j]] (where this range must be a subset of axes(A,1))
# and for A[I[j]+k] (where it enters into the calculation of k's range).

function extremerange(A)
    α, ω = extrema(A)
    α isa Integer && ω isa Integer || error("expected integers!")
    α:ω
end

#========== functions used by the macro ==========#

@nospecialize

"""
    range_expr_walk(:(axes(A,1)), :(2i+1)) -> range, :i

Given the axis of `A`, and the expression inside `A[2i+1]`,
this returns an expression for the resulting range of index `i`.
Understands operations `+, -, *, ÷`.
(Don't really need `÷`, as this results in a non-`UnitRange`
which can't be a valid index.)

If the expression is from something like `A[2i+j]`, then it returns a tuple of ranges
and a tuple of symbols. The range for `:j` contains `:$(AXIS)i` and v-v.

If the expression is from `A[I[j]]` then it returns `(min:max, nothing)`,
and the caller should check `issubset(min:max, axes(A,1))`.
"""
function range_expr_walk(r::Expr, ex::Expr, con=[])
    ex.head == :kw && return range_expr_kw(r, ex)
    if ex.head == :ref # case of M[I[j], k] with r=size(M,1)
        A = ex.args[1]
        push!(con, :(minimum($A) in $r && maximum($A) in $r || error("not safe!")))
        # return (r,nothing)
        return (:($extremerange($A)),nothing)
    end
    ex.head == :call || error("not sure what to do with $ex")
    if length(ex.args) == 2
        op, a = ex.args
        if op == :+
            return range_expr_walk(r, a)
        elseif op == :-
            return range_expr_walk(:($minusrange($r)), a)
        end
    elseif length(ex.args) == 3
        op, a, b = ex.args
        if op == :+
            is_const(a) && return range_expr_walk(:($r .- $a), b)
            is_const(b) && return range_expr_walk(:($r .- $b), a)
            # with neither constant, first go outwards from index j to expression b...
            ax_a = range_unwrap(a)
            ax_b = range_unwrap(b)
            #... then use that with given size(A,d) to constrain range of i, and v-v:
            range_a, i_a = range_expr_walk(:($subranges($r, $ax_b)), a)
            range_b, i_b = range_expr_walk(:($subranges($r, $ax_a)), b)
            return (range_a, range_b), (i_a, i_b)

        elseif op == :-
            is_const(a) && return range_expr_walk(:($minusrange($r .- $a)), b)
            is_const(b) && return range_expr_walk(:($r .+ $b), a)
            ax_a = range_unwrap(a)
            ax_b = range_unwrap(b)
            range_a, i_a = range_expr_walk(:($addranges($r, $ax_b)), a)
            range_b, i_b = range_expr_walk(:($minusrange($subranges($r, $ax_a))), b)
            return (range_a, range_b), (i_a, i_b)

        elseif op == :*
            is_const(a) && return range_expr_walk(:($divrange($r, $a)), b)
            is_const(b) && return range_expr_walk(:($divrange($r, $b)), a)
        elseif op == :÷
            is_const(b) && return range_expr_walk(:($r .* $b), a)
        elseif op == :/
            error("not sure what to do with $ex, perhaps you wanted ÷")
        end
    elseif length(ex.args) > 3
        op, a, b, c = ex.args[1:4]
        ds = ex.args[5:end]
        if op == :+
            is_const(a) && return range_expr_walk(:($r .- $a), :(+($b, $c, $(ds...))))
            is_const(b) && return range_expr_walk(:($r .- $b), :(+($a, $c, $(ds...))))
            is_const(c) && return range_expr_walk(:($r .- $c), :(+($a, $b, $(ds...))))
        end
    end
    error("not sure what to do with $ex, sorry")
end

range_expr_walk(range::Expr, s::Symbol) = range, s
range_expr_walk(range::Expr, n::Integer) = range, nothing

is_const(::Int) = true
is_const(::Any) = false
is_const(s::Symbol) = s === :(:) # for the purposes of saveconstraints setting :intersect
is_const(ex::Expr) = begin
    ex.head == :$ && return true # what's returned by range_expr_walk will still contain $
    if ex.head == :call && ex.args[1] in (:+, :-, :*, :÷)
        return all(is_const, ex.args[2:end])
    end
    false
end

"""
    range_expr_walk(:(axes(A,1)), :(i=j)) -> :(axes(A, :i)), :j

Special case for keyword indexing, `A[i=j, k=j+2]` comes here.
"""
function range_expr_kw(r::Expr, ex::Expr)
    @assert ex.head == :kw
    @assert r.head == :call && r.args[1] == :axes
    r.args[3] = QuoteNode(ex.args[1])
    range_expr_walk(r, ex.args[2])
end

"""
    range_unwrap(:(2i+1)) -> :(2 .* AXIS_i .+ 1)

This goes in the opposite direction to `range_expr_walk`, and gives
the range of values taken by the expression, in terms of `Symbol($AXIS, i)`.
"""
range_unwrap(i::Symbol) = Symbol(AXIS, i)
range_unwrap(ex::Expr) = begin
    if ex.head == :ref # case of A[I[j]+k] comes here
        A = ex.args[1]
        return :($extremerange($A))
    end
    ex.head == :call || error("don't know how to handle $ex")
    if length(ex.args) == 2
        op, a = ex.args
        if op == :-
            return :($minusrange($(range_unwrap(a))))
        end
    elseif length(ex.args) == 3
        op, a, b = ex.args
        if op == :*
            a == -1 && return :($minusrange($(range_unwrap(b))))
            b == -1 && return :($minusrange($(range_unwrap(a))))
            is_const(a) && return :($a .* $(range_unwrap(b)))
            is_const(b) && return :($b .* $(range_unwrap(a)))
        elseif op == :+
            is_const(a) && return :($a .+ $(range_unwrap(b)))
            is_const(b) && return :($b .+ $(range_unwrap(a)))
        elseif op == :-
            is_const(a) && return :($a .- $(range_unwrap(b)))
            is_const(b) && return :($(range_unwrap(a)) .- $b)
        end
    end
    error("don't know how to handle $ex, sorry")
end

@specialize

#========== the end ==========#


text:
module GridapGmshTests

using GridapGmsh
using Test

if GridapGmsh.GMSH_FOUND
  @testset "gmsh" begin
    include("gmshTests.jl")
  end

  @testset "GmshDiscreteModel" begin
   include("GmshDiscreteModelsTests.jl")
  end
else
  @warn "GridapGmsh is not loaded or installed properly."
end

end # module


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

"""
    build_suggesters(domain_name)
    build_suggesters(domain_name, params::Dict{String,<:Any})

Indexes the search suggestions. For more information, see Configuring Suggesters in the
Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`:

"""
function build_suggesters(DomainName; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "BuildSuggesters",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function build_suggesters(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "BuildSuggesters",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_domain(domain_name)
    create_domain(domain_name, params::Dict{String,<:Any})

Creates a new search domain. For more information, see Creating a Search Domain in the
Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`: A name for the domain you are creating. Allowed characters are a-z
  (lower-case letters), 0-9, and hyphen (-). Domain names must start with a letter or number
  and be at least 3 and no more than 28 characters long.

"""
function create_domain(DomainName; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "CreateDomain",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_domain(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "CreateDomain",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    define_analysis_scheme(analysis_scheme, domain_name)
    define_analysis_scheme(analysis_scheme, domain_name, params::Dict{String,<:Any})

Configures an analysis scheme that can be applied to a text or text-array field to define
language-specific text processing options. For more information, see Configuring Analysis
Schemes in the Amazon CloudSearch Developer Guide.

# Arguments
- `analysis_scheme`:
- `domain_name`:

"""
function define_analysis_scheme(
    AnalysisScheme, DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DefineAnalysisScheme",
        Dict{String,Any}("AnalysisScheme" => AnalysisScheme, "DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function define_analysis_scheme(
    AnalysisScheme,
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DefineAnalysisScheme",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AnalysisScheme" => AnalysisScheme, "DomainName" => DomainName
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    define_expression(domain_name, expression)
    define_expression(domain_name, expression, params::Dict{String,<:Any})

Configures an Expression for the search domain. Used to create new expressions and modify
existing ones. If the expression exists, the new configuration replaces the old one. For
more information, see Configuring Expressions in the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`:
- `expression`:

"""
function define_expression(
    DomainName, Expression; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DefineExpression",
        Dict{String,Any}("DomainName" => DomainName, "Expression" => Expression);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function define_expression(
    DomainName,
    Expression,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DefineExpression",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("DomainName" => DomainName, "Expression" => Expression),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    define_index_field(domain_name, index_field)
    define_index_field(domain_name, index_field, params::Dict{String,<:Any})

Configures an IndexField for the search domain. Used to create new fields and modify
existing ones. You must specify the name of the domain you are configuring and an index
field configuration. The index field configuration specifies a unique name, the index field
type, and the options you want to configure for the field. The options you can specify
depend on the IndexFieldType. If the field exists, the new configuration replaces the old
one. For more information, see Configuring Index Fields in the Amazon CloudSearch Developer
Guide.

# Arguments
- `domain_name`:
- `index_field`: The index field and field options you want to configure.

"""
function define_index_field(
    DomainName, IndexField; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DefineIndexField",
        Dict{String,Any}("DomainName" => DomainName, "IndexField" => IndexField);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function define_index_field(
    DomainName,
    IndexField,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DefineIndexField",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("DomainName" => DomainName, "IndexField" => IndexField),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    define_suggester(domain_name, suggester)
    define_suggester(domain_name, suggester, params::Dict{String,<:Any})

Configures a suggester for a domain. A suggester enables you to display possible matches
before users finish typing their queries. When you configure a suggester, you must specify
the name of the text field you want to search for possible matches and a unique name for
the suggester. For more information, see Getting Search Suggestions in the Amazon
CloudSearch Developer Guide.

# Arguments
- `domain_name`:
- `suggester`:

"""
function define_suggester(
    DomainName, Suggester; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DefineSuggester",
        Dict{String,Any}("DomainName" => DomainName, "Suggester" => Suggester);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function define_suggester(
    DomainName,
    Suggester,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DefineSuggester",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("DomainName" => DomainName, "Suggester" => Suggester),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_analysis_scheme(analysis_scheme_name, domain_name)
    delete_analysis_scheme(analysis_scheme_name, domain_name, params::Dict{String,<:Any})

Deletes an analysis scheme. For more information, see Configuring Analysis Schemes in the
Amazon CloudSearch Developer Guide.

# Arguments
- `analysis_scheme_name`: The name of the analysis scheme you want to delete.
- `domain_name`:

"""
function delete_analysis_scheme(
    AnalysisSchemeName, DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DeleteAnalysisScheme",
        Dict{String,Any}(
            "AnalysisSchemeName" => AnalysisSchemeName, "DomainName" => DomainName
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_analysis_scheme(
    AnalysisSchemeName,
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DeleteAnalysisScheme",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AnalysisSchemeName" => AnalysisSchemeName, "DomainName" => DomainName
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_domain(domain_name)
    delete_domain(domain_name, params::Dict{String,<:Any})

Permanently deletes a search domain and all of its data. Once a domain has been deleted, it
cannot be recovered. For more information, see Deleting a Search Domain in the Amazon
CloudSearch Developer Guide.

# Arguments
- `domain_name`: The name of the domain you want to permanently delete.

"""
function delete_domain(DomainName; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "DeleteDomain",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_domain(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DeleteDomain",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_expression(domain_name, expression_name)
    delete_expression(domain_name, expression_name, params::Dict{String,<:Any})

Removes an Expression from the search domain. For more information, see Configuring
Expressions in the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`:
- `expression_name`: The name of the Expression to delete.

"""
function delete_expression(
    DomainName, ExpressionName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DeleteExpression",
        Dict{String,Any}("DomainName" => DomainName, "ExpressionName" => ExpressionName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_expression(
    DomainName,
    ExpressionName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DeleteExpression",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DomainName" => DomainName, "ExpressionName" => ExpressionName
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_index_field(domain_name, index_field_name)
    delete_index_field(domain_name, index_field_name, params::Dict{String,<:Any})

Removes an IndexField from the search domain. For more information, see Configuring Index
Fields in the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`:
- `index_field_name`: The name of the index field your want to remove from the domain's
  indexing options.

"""
function delete_index_field(
    DomainName, IndexFieldName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DeleteIndexField",
        Dict{String,Any}("DomainName" => DomainName, "IndexFieldName" => IndexFieldName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_index_field(
    DomainName,
    IndexFieldName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DeleteIndexField",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DomainName" => DomainName, "IndexFieldName" => IndexFieldName
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_suggester(domain_name, suggester_name)
    delete_suggester(domain_name, suggester_name, params::Dict{String,<:Any})

Deletes a suggester. For more information, see Getting Search Suggestions in the Amazon
CloudSearch Developer Guide.

# Arguments
- `domain_name`:
- `suggester_name`: Specifies the name of the suggester you want to delete.

"""
function delete_suggester(
    DomainName, SuggesterName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DeleteSuggester",
        Dict{String,Any}("DomainName" => DomainName, "SuggesterName" => SuggesterName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_suggester(
    DomainName,
    SuggesterName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DeleteSuggester",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DomainName" => DomainName, "SuggesterName" => SuggesterName
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_analysis_schemes(domain_name)
    describe_analysis_schemes(domain_name, params::Dict{String,<:Any})

Gets the analysis schemes configured for a domain. An analysis scheme defines
language-specific text processing options for a text field. Can be limited to specific
analysis schemes by name. By default, shows all analysis schemes and includes any pending
changes to the configuration. Set the Deployed option to true to show the active
configuration and exclude pending changes. For more information, see Configuring Analysis
Schemes in the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`: The name of the domain you want to describe.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AnalysisSchemeNames"`: The analysis schemes you want to describe.
- `"Deployed"`: Whether to display the deployed configuration (true) or include any pending
  changes (false). Defaults to false.
"""
function describe_analysis_schemes(
    DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DescribeAnalysisSchemes",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_analysis_schemes(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeAnalysisSchemes",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_availability_options(domain_name)
    describe_availability_options(domain_name, params::Dict{String,<:Any})

Gets the availability options configured for a domain. By default, shows the configuration
with any pending changes. Set the Deployed option to true to show the active configuration
and exclude pending changes. For more information, see Configuring Availability Options in
the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`: The name of the domain you want to describe.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Deployed"`: Whether to display the deployed configuration (true) or include any pending
  changes (false). Defaults to false.
"""
function describe_availability_options(
    DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DescribeAvailabilityOptions",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_availability_options(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeAvailabilityOptions",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_domain_endpoint_options(domain_name)
    describe_domain_endpoint_options(domain_name, params::Dict{String,<:Any})

Returns the domain's endpoint options, specifically whether all requests to the domain must
arrive over HTTPS. For more information, see Configuring Domain Endpoint Options in the
Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`: A string that represents the name of a domain.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Deployed"`: Whether to retrieve the latest configuration (which might be in a
  Processing state) or the current, active configuration. Defaults to false.
"""
function describe_domain_endpoint_options(
    DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DescribeDomainEndpointOptions",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_domain_endpoint_options(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeDomainEndpointOptions",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_domains()
    describe_domains(params::Dict{String,<:Any})

Gets information about the search domains owned by this account. Can be limited to specific
domains. Shows all domains by default. To get the number of searchable documents in a
domain, use the console or submit a matchall request to your domain's search endpoint:
q=matchall&amp;q.parser=structured&amp;size=0. For more information, see Getting
Information about a Search Domain in the Amazon CloudSearch Developer Guide.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"DomainNames"`: The names of the domains you want to include in the response.
"""
function describe_domains(; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "DescribeDomains"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function describe_domains(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DescribeDomains", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    describe_expressions(domain_name)
    describe_expressions(domain_name, params::Dict{String,<:Any})

Gets the expressions configured for the search domain. Can be limited to specific
expressions by name. By default, shows all expressions and includes any pending changes to
the configuration. Set the Deployed option to true to show the active configuration and
exclude pending changes. For more information, see Configuring Expressions in the Amazon
CloudSearch Developer Guide.

# Arguments
- `domain_name`: The name of the domain you want to describe.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Deployed"`: Whether to display the deployed configuration (true) or include any pending
  changes (false). Defaults to false.
- `"ExpressionNames"`: Limits the DescribeExpressions response to the specified
  expressions. If not specified, all expressions are shown.
"""
function describe_expressions(DomainName; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "DescribeExpressions",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_expressions(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeExpressions",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_index_fields(domain_name)
    describe_index_fields(domain_name, params::Dict{String,<:Any})

Gets information about the index fields configured for the search domain. Can be limited to
specific fields by name. By default, shows all fields and includes any pending changes to
the configuration. Set the Deployed option to true to show the active configuration and
exclude pending changes. For more information, see Getting Domain Information in the Amazon
CloudSearch Developer Guide.

# Arguments
- `domain_name`: The name of the domain you want to describe.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Deployed"`: Whether to display the deployed configuration (true) or include any pending
  changes (false). Defaults to false.
- `"FieldNames"`: A list of the index fields you want to describe. If not specified,
  information is returned for all configured index fields.
"""
function describe_index_fields(
    DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DescribeIndexFields",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_index_fields(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeIndexFields",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_scaling_parameters(domain_name)
    describe_scaling_parameters(domain_name, params::Dict{String,<:Any})

Gets the scaling parameters configured for a domain. A domain's scaling parameters specify
the desired search instance type and replication count. For more information, see
Configuring Scaling Options in the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`:

"""
function describe_scaling_parameters(
    DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DescribeScalingParameters",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_scaling_parameters(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeScalingParameters",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_service_access_policies(domain_name)
    describe_service_access_policies(domain_name, params::Dict{String,<:Any})

Gets information about the access policies that control access to the domain's document and
search endpoints. By default, shows the configuration with any pending changes. Set the
Deployed option to true to show the active configuration and exclude pending changes. For
more information, see Configuring Access for a Search Domain in the Amazon CloudSearch
Developer Guide.

# Arguments
- `domain_name`: The name of the domain you want to describe.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Deployed"`: Whether to display the deployed configuration (true) or include any pending
  changes (false). Defaults to false.
"""
function describe_service_access_policies(
    DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "DescribeServiceAccessPolicies",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_service_access_policies(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeServiceAccessPolicies",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_suggesters(domain_name)
    describe_suggesters(domain_name, params::Dict{String,<:Any})

Gets the suggesters configured for a domain. A suggester enables you to display possible
matches before users finish typing their queries. Can be limited to specific suggesters by
name. By default, shows all suggesters and includes any pending changes to the
configuration. Set the Deployed option to true to show the active configuration and exclude
pending changes. For more information, see Getting Search Suggestions in the Amazon
CloudSearch Developer Guide.

# Arguments
- `domain_name`: The name of the domain you want to describe.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Deployed"`: Whether to display the deployed configuration (true) or include any pending
  changes (false). Defaults to false.
- `"SuggesterNames"`: The suggesters you want to describe.
"""
function describe_suggesters(DomainName; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "DescribeSuggesters",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_suggesters(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "DescribeSuggesters",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    index_documents(domain_name)
    index_documents(domain_name, params::Dict{String,<:Any})

Tells the search domain to start indexing its documents using the latest indexing options.
This operation must be invoked to activate options whose OptionStatus is
RequiresIndexDocuments.

# Arguments
- `domain_name`:

"""
function index_documents(DomainName; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "IndexDocuments",
        Dict{String,Any}("DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function index_documents(
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "IndexDocuments",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("DomainName" => DomainName), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_domain_names()
    list_domain_names(params::Dict{String,<:Any})

Lists all search domains owned by an account.

"""
function list_domain_names(; aws_config::AbstractAWSConfig=global_aws_config())
    return cloudsearch(
        "ListDomainNames"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_domain_names(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "ListDomainNames", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

"""
    update_availability_options(domain_name, multi_az)
    update_availability_options(domain_name, multi_az, params::Dict{String,<:Any})

Configures the availability options for a domain. Enabling the Multi-AZ option expands an
Amazon CloudSearch domain to an additional Availability Zone in the same Region to increase
fault tolerance in the event of a service disruption. Changes to the Multi-AZ option can
take about half an hour to become active. For more information, see Configuring
Availability Options in the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`:
- `multi_az`: You expand an existing search domain to a second Availability Zone by setting
  the Multi-AZ option to true. Similarly, you can turn off the Multi-AZ option to downgrade
  the domain to a single Availability Zone by setting the Multi-AZ option to false.

"""
function update_availability_options(
    DomainName, MultiAZ; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "UpdateAvailabilityOptions",
        Dict{String,Any}("DomainName" => DomainName, "MultiAZ" => MultiAZ);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_availability_options(
    DomainName,
    MultiAZ,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "UpdateAvailabilityOptions",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("DomainName" => DomainName, "MultiAZ" => MultiAZ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_domain_endpoint_options(domain_endpoint_options, domain_name)
    update_domain_endpoint_options(domain_endpoint_options, domain_name, params::Dict{String,<:Any})

Updates the domain's endpoint options, specifically whether all requests to the domain must
arrive over HTTPS. For more information, see Configuring Domain Endpoint Options in the
Amazon CloudSearch Developer Guide.

# Arguments
- `domain_endpoint_options`: Whether to require that all requests to the domain arrive over
  HTTPS. We recommend Policy-Min-TLS-1-2-2019-07 for TLSSecurityPolicy. For compatibility
  with older clients, the default is Policy-Min-TLS-1-0-2019-07.
- `domain_name`: A string that represents the name of a domain.

"""
function update_domain_endpoint_options(
    DomainEndpointOptions, DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "UpdateDomainEndpointOptions",
        Dict{String,Any}(
            "DomainEndpointOptions" => DomainEndpointOptions, "DomainName" => DomainName
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_domain_endpoint_options(
    DomainEndpointOptions,
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "UpdateDomainEndpointOptions",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DomainEndpointOptions" => DomainEndpointOptions,
                    "DomainName" => DomainName,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_scaling_parameters(domain_name, scaling_parameters)
    update_scaling_parameters(domain_name, scaling_parameters, params::Dict{String,<:Any})

Configures scaling parameters for a domain. A domain's scaling parameters specify the
desired search instance type and replication count. Amazon CloudSearch will still
automatically scale your domain based on the volume of data and traffic, but not below the
desired instance type and replication count. If the Multi-AZ option is enabled, these
values control the resources used per Availability Zone. For more information, see
Configuring Scaling Options in the Amazon CloudSearch Developer Guide.

# Arguments
- `domain_name`:
- `scaling_parameters`:

"""
function update_scaling_parameters(
    DomainName, ScalingParameters; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "UpdateScalingParameters",
        Dict{String,Any}(
            "DomainName" => DomainName, "ScalingParameters" => ScalingParameters
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_scaling_parameters(
    DomainName,
    ScalingParameters,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "UpdateScalingParameters",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "DomainName" => DomainName, "ScalingParameters" => ScalingParameters
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_service_access_policies(access_policies, domain_name)
    update_service_access_policies(access_policies, domain_name, params::Dict{String,<:Any})

Configures the access rules that control access to the domain's document and search
endpoints. For more information, see  Configuring Access for an Amazon CloudSearch Domain.

# Arguments
- `access_policies`: The access rules you want to configure. These rules replace any
  existing rules.
- `domain_name`:

"""
function update_service_access_policies(
    AccessPolicies, DomainName; aws_config::AbstractAWSConfig=global_aws_config()
)
    return cloudsearch(
        "UpdateServiceAccessPolicies",
        Dict{String,Any}("AccessPolicies" => AccessPolicies, "DomainName" => DomainName);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_service_access_policies(
    AccessPolicies,
    DomainName,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return cloudsearch(
        "UpdateServiceAccessPolicies",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AccessPolicies" => AccessPolicies, "DomainName" => DomainName
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
export max_path_through_pyramid

"""
Returns the max value of a path through a pyramid of integers.
"""
function max_path_through_pyramid(pyramid::Vector{Vector{Integer}}=Vector{Vector{Integer}}([Vector{Integer}([1])]))::Integer

    pyramid_best_paths = Vector{Vector{Integer}}()
    for i = 1:length(pyramid)
        push!(pyramid_best_paths, zeros(Integer, length(pyramid[i])))
        # Initialize edge values
        if i == 1
            @inbounds pyramid_best_paths[i][1] = pyramid[i][1]
        else
            @inbounds pyramid_best_paths[i][1] = pyramid_best_paths[i-1][1] + pyramid[i][1]
            @inbounds pyramid_best_paths[i][end] = pyramid_best_paths[i-1][end] + pyramid[i][end]
        end
    end

    for i = 3:length(pyramid)
        for j = 2:length(pyramid[i])-1
            @inbounds pyramid_best_paths[i][j] = max(pyramid_best_paths[i-1][j-1],
                                                     pyramid_best_paths[i-1][j]) +
                                                 pyramid[i][j]
        end
    end

    return maximum(pyramid_best_paths[end])
end

max_stars_repo_name: gnujosh/julia-euler-project

max_issues_repo_name: gnujosh/julia-euler-project

max_forks_repo_name: gnujosh/julia-euler-project


text:
### JS_SAA main file
module JS

using Distributions, StatsBase, LinearAlgebra

include("genPurpose.jl")
include("babyNewsvendor.jl")

end #ends module

max_stars_repo_name: CausalML/DataPooling

max_issues_repo_name: CausalML/DataPooling

max_forks_repo_name: CausalML/DataPooling


text:
#####
##### UniversalConstants
#####

# These constants are planet-independent.

export gas_constant,
       light_speed,
       h_Planck,
       k_Boltzmann,
       Stefan,
       astro_unit,
       avogad

"""
    gas_constant

Universal gas constant (J/mol/K)
"""
gas_constant() =     8.3144598

"""
    light_speed

Speed of light in vacuum (m/s)
"""
light_speed() =      2.99792458e8

"""
    h_Planck

Planck constant (m^2 kg/s)
"""
h_Planck() =         6.626e-34

"""
    k_Boltzmann

Boltzmann constant (m^2 kg/s^2/K)
"""
k_Boltzmann() =      1.381e-23

"""
    Stefan

Stefan-Boltzmann constant (W/m^2/K^4)
"""
Stefan() =           5.670e-8

"""
    astro_unit

Astronomical unit (m)
"""
astro_unit() =       1.4959787e11

"""
    avogad(::Type{FT})

Avogadro's number [molec/mol]
"""
avogad() = 6.02214076e23


text:
function _anat_entities(suffix, src::Entities)
    if suffix == "T1w" || suffix == "T2w" || suffix == "PDw" || suffix == "T2starw" || suffix == "FLAIR" || suffix == "inplaneT1" || suffix == "inplaneT2" || suffix == "PDT2" || suffix == "angio" || suffix == "T2star" || suffix == "FLASH" || suffix == "PD"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, part))
    elseif suffix == "T1map" || suffix == "T2map" || suffix == "T2starmap" || suffix == "R1map" || suffix == "R2map" || suffix == "R2starmap" || suffix == "PDmap" || suffix == "MTRmap" || suffix == "MTsat" || suffix == "UNIT1" || suffix == "T1rho" || suffix == "MWFmap" || suffix == "MTVmap" || suffix == "PDT2map" || suffix == "Chimap" || suffix == "S0map" || suffix == "M0map"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction))
    elseif suffix == "defacemask"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, modality))
    elseif suffix == "MESE" || suffix == "MEGRE"
        return Entities(_get_opt(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, echo), src, part))
    elseif suffix == "VFA"
        return Entities(_get_opt(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, flip), src, part))
    elseif suffix == "IRT1"
        return Entities(_get_opt(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, inversion), src, part))
    elseif suffix == "MP2RAGE"
        return Entities(_get_opt(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, echo), src, flip), src, inversion), src, part))
    elseif suffix == "MPM" || suffix == "MTS"
        return Entities(_get_opt(_get_req(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, echo), src, flip), src, mtransfer), src, part))
    elseif suffix == "MTR"
        return Entities(_get_opt(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, mtransfer), src, part))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _eeg_entities(suffix, src::Entities)
    if suffix == "eeg"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    elseif suffix == "channels"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    elseif suffix == "coordsystem"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, space))
    elseif suffix == "electrodes"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, space))
    elseif suffix == "events"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    elseif suffix == "photo"
        return Entities(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _perf_entities(suffix, src::Entities)
    if suffix == "asl" || suffix == "m0scan"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, reconstruction), src, direction), src, run))
    elseif suffix == "aslcontext"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, reconstruction), src, direction), src, run))
    elseif suffix == "asllabeling"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, reconstruction), src, run))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _pet_entities(suffix, src::Entities)
    if suffix == "pet"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, task), src, tracer), src, reconstruction), src, run))
    elseif suffix == "blood"
        return Entities(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, task), src, tracer), src, reconstruction), src, run), src, recording))
    elseif suffix == "events"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, tracer), src, reconstruction), src, run))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _dwi_entities(suffix, src::Entities)
    if suffix == "dwi"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, direction), src, run), src, part))
    elseif suffix == "sbref"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, direction), src, run), src, part))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _beh_entities(suffix, src::Entities)
    if suffix == "stim" || suffix == "physio"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run), src, recording))
    elseif suffix == "events" || suffix == "beh"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _meg_entities(suffix, src::Entities)
    if suffix == "meg"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run), src, processing), src, split))
    elseif suffix == "headshape"
        return Entities(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition))
    elseif suffix == "markers"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, space))
    elseif suffix == "coordsystem"
        return Entities(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition))
    elseif suffix == "channels"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run), src, processing))
    elseif suffix == "events"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    elseif suffix == "photo"
        return Entities(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _fmap_entities(suffix, src::Entities)
    if suffix == "phasediff" || suffix == "phase1" || suffix == "phase2" || suffix == "magnitude1" || suffix == "magnitude2" || suffix == "magnitude" || suffix == "fieldmap"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, run))
    elseif suffix == "epi" || suffix == "m0scan"
        return Entities(_get_opt(_get_req(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, ceagent), src, direction), src, run))
    elseif suffix == "TB1DAM"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, flip), src, inversion), src, part))
    elseif suffix == "TB1EPI"
        return Entities(_get_opt(_get_opt(_get_req(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, echo), src, flip), src, inversion), src, part))
    elseif suffix == "TB1AFI" || suffix == "TB1TFL" || suffix == "TB1RFM" || suffix == "RB1COR"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, echo), src, flip), src, inversion), src, part))
    elseif suffix == "TB1SRGE"
        return Entities(_get_opt(_get_req(_get_req(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction), src, echo), src, flip), src, inversion), src, part))
    elseif suffix == "TB1map" || suffix == "RB1map"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, run), src, acquisition), src, ceagent), src, reconstruction))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _func_entities(suffix, src::Entities)
    if suffix == "bold" || suffix == "cbv" || suffix == "sbref"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, ceagent), src, reconstruction), src, direction), src, run), src, echo), src, part))
    elseif suffix == "phase"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, ceagent), src, reconstruction), src, direction), src, run), src, echo))
    elseif suffix == "events"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, ceagent), src, reconstruction), src, direction), src, run))
    elseif suffix == "physio" || suffix == "stim"
        return Entities(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, ceagent), src, reconstruction), src, direction), src, run), src, recording))
    else
        error(suffix * " is not a supported suffix.")
    end
end

function _ieeg_entities(suffix, src::Entities)
    if suffix == "ieeg"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    elseif suffix == "channels"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    elseif suffix == "coordsystem"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, space))
    elseif suffix == "electrodes"
        return Entities(_get_opt(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition), src, space))
    elseif suffix == "events"
        return Entities(_get_opt(_get_opt(_get_req(_get_opt(_get_req((), src, subject), src, session), src, task), src, acquisition), src, run))
    elseif suffix == "photo"
        return Entities(_get_opt(_get_opt(_get_req((), src, subject), src, session), src, acquisition))
    else
        error(suffix * " is not a supported suffix.")
    end
end


text:
using PlantGeom
using Test
using ReferenceTests

using ColorSchemes
using Meshes
using StaticArrays
using MultiScaleTreeGraph
using CoordinateTransformations
using LinearAlgebra
using CairoMakie
using MeshViz

# Update the reference plots (do only when you know awhat you are doing!):
# include("makes_references.jl")

@testset "Read OPF" begin
    include("test-read_opf.jl")
end

@testset "Write OPF" begin
    include("test-read_opf.jl")
end

@testset "Makie recipes" begin
    include("test-makie-recipes.jl")
end

using RecipesBase
using Plots # Add this dependency because else the tests on plot recipes return an error (I don't know why)
@testset "Plots.jl recipes" begin
    include("test-plots-recipe.jl")
end


text:
function getwraparoundDFTfreqs(N::Int, fs::T, ν_begin::T) where T

    U0 = getDFTfreqrange(N, fs)
    out, inds = wrapfreqrange(U0, ν_begin, fs)

    return U0, out, inds
end

# Assumes U0 is sorted in ascending order.
function wrapfreqrange(U0, ν_begin::T, fs::T) where T <: Real

    N = length(U0)
    ind = findfirst(xx->(xx>ν_begin), U0)

    # TODO handle these exceptions with more grace.
    @assert typeof(ind) == Int
    @assert ind <= N

    out = Vector{T}(undef, N)
    #out[1:ind] = U0[1:ind] .+ fs
    #out[ind+1:end] = U0[ind+1:end]

    M = N-ind
    out[1:M] = U0[ind+1:end]
    out[M+1:end] = U0[1:ind] .+ fs

    inds = collect(1:N)
    inds[1:M] = collect(ind+1:N)
    inds[M+1:end] = collect(1:ind)

    return out, inds
end

function gettimerange(N::Int, fs::T) where T
    Ts::T = 1/fs

    return zero(T):Ts:(N-1)*Ts
 end

 function getDFTfreqrange(N::Int, fs::T)::LinRange{T} where T
     a = zero(T)
     b = fs-fs/N

     return LinRange(a, b, N)
 end

 # case 1D.
function computeDTFTch3eq29(h, u::T, Λ)::Complex{T} where T <: Real

    # debug_array = zeros(Complex{T}, length(Λ))
    # debug_array2 = zeros(Complex{T}, length(Λ))
    # fs = 1/(Λ[2]-Λ[1])

    running_sum = zero(T)
    for i = 1:length(Λ)
        x = Λ[i]

        running_sum += h[i]*exp(-im*2*π*u*x)

        # # debug.
        # debug_array[i] = h[i]*exp(-im*2*π*u*x)
        #
        # #debug_array2[i] = h[i]*exp(-im*2*π*u*x +im*35*2*pi)
        # debug_array2[i] = h[i]*exp(-im*2*π*(u+3*fs)*x)
    end

    #println("debug: norm(debug_array -debug_array2 ) = ", norm(debug_array -debug_array2 ) )
    return running_sum
end


text:
using ObjectDetectors
using GeometryBasics

@testset "OrientedBoundingBoxDetectionTests" begin
    core_args = ("class", 0.7, 256, 256, MACHINE, "detector")
    detection = OrientedBoundingBoxDetection(Point2(3.0, 3.0), 4.0, 2.0, pi / 2, core_args...)

    bb = bounding_box(detection)
    error_margin = 1.0e-5
    origin_diff = bb.origin - Point2(2.0, 1.0)
    @test abs(origin_diff.data[1]) < error_margin && abs(origin_diff.data[2]) < error_margin
    widths_diff = bb.widths - Point2(2.0, 4.0)
    @test abs(widths_diff.data[1]) < error_margin && abs(widths_diff.data[2]) < error_margin
end


text:
##
## A stegosaur with a top hat?
##
"""
    function stegosaurus()

A stegosaur with a top hat?

# Example
```jldoctest
julia> cowsay("How do you do?", cow=Cowsay.stegosaurus)
 ________________
< How do you do? >
 ----------------
\\                             .       .
 \\                           / `.   .' "
  \\                  .---.  <    > <    >  .---.
   \\                 |    \\  \\ - ~ ~ - /  /    |
         _____          ..-~             ~-..-~
        |     |   \\~~~\\.'                    `./~~~/
       ---------   \\__/                        \\__/
      .'  O    \\     /               /       \\  "
     (_____,    `._.'               |         }  \\/~~~/
      `----.          /       }     |        /    \\__/
            `-.      |       /      |       /      `. ,~~|
                ~-.__|      /_ - ~ ^|      /- _      `..-'
                     |     /        |     /     ~-.     `-. _  _  _
                     |_____|        |_____|         ~ - . _ _ _ _ _>
```
"""
function stegosaurus(;eyes="oo", tongue="  ", thoughts="\\")
the_cow =
"""
$thoughts                             .       .
 $thoughts                           / `.   .' "
  $thoughts                  .---.  <    > <    >  .---.
   $thoughts                 |    \\  \\ - ~ ~ - /  /    |
         _____          ..-~             ~-..-~
        |     |   \\~~~\\.'                    `./~~~/
       ---------   \\__/                        \\__/
      .'  O    \\     /               /       \\  "
     (_____,    `._.'               |         }  \\/~~~/
      `----.          /       }     |        /    \\__/
            `-.      |       /      |       /      `. ,~~|
                ~-.__|      /_ - ~ ^|      /- _      `..-'
                     |     /        |     /     ~-.     `-. _  _  _
                     |_____|        |_____|         ~ - . _ _ _ _ _>
"""
return the_cow
end


text:
# The submarine has been making some odd creaking noises, so you ask it to
# produce a diagnostic report just in case.
#
# The diagnostic report (your puzzle input) consists of a list of binary
# numbers which, when decoded properly, can tell you many useful things about
# the conditions of the submarine. The first parameter to check is the power
# consumption.
#
# You need to use the binary numbers in the diagnostic report to generate two
# new binary numbers (called the gamma rate and the epsilon rate). The power
# consumption can then be found by multiplying the gamma rate by the epsilon
# rate.
#
# Each bit in the gamma rate can be determined by finding the most common bit
# in the corresponding position of all numbers in the diagnostic report. For
# example, given the following diagnostic report:
#
# 00100
# 11110
# 10110
# 10111
# 10101
# 01111
# 00111
# 11100
# 10000
# 11001
# 00010
# 01010
#
# Considering only the first bit of each number, there are five 0 bits and
# seven 1 bits. Since the most common bit is 1, the first bit of the gamma rate
# is 1.
#
# The most common second bit of the numbers in the diagnostic report is 0, so
# the second bit of the gamma rate is 0.
#
# The most common value of the third, fourth, and fifth bits are 1, 1, and 0,
# respectively, and so the final three bits of the gamma rate are 110.
#
# So, the gamma rate is the binary number 10110, or 22 in decimal.
#
# The epsilon rate is calculated in a similar way; rather than use the most
# common bit, the least common bit from each position is used. So, the epsilon
# rate is 01001, or 9 in decimal. Multiplying the gamma rate (22) by the
# epsilon rate (9) produces the power consumption, 198.
#
# Use the binary numbers in your diagnostic report to calculate the gamma rate
# and epsilon rate, then multiply them together. What is the power consumption
# of the submarine? (Be sure to represent your answer in decimal, not binary.)

# convert each line to a vector of bits
function mapper(l)
  map(d -> parse(Int, d), split(l, ""))
end

# sum all lines into a vector of sums - if any sum is >500 (since there are
# 1000 rows) then 1's are the most common value.
cnts = mapreduce(mapper, +, eachline("input.txt"))

# ok, we're going to use a trick here... cnts .> 500 will return a BitVector
# (aka BitArray with one dimension) where each element is a 1 if the condition
# is true (ie, >500) and a 0 otherwise. Under the hood, a BitVector stores the
# data as an Int64 but the bits are reversed. By reversing cnt, we can read out
# that int without writing any code to convert the BitVector to an int
gamma = (reverse(cnts) .> 500).chunks[1]

# the epsilon rate will always be the bitwise negation of gamma anded with
# 0xFFF since we're only interested in the 12 least significant bits
epsilon = (~gamma) & 0xfff

println("Result: ", gamma * epsilon)