text:
export
    AbstractComposite, isDescendantOf, countDescendants, removeChild, getParent,
    getChildren, appendChild, isSibling, appendSibling, prependSibling, hasProperty,
    recursive_collect, clearSelectionTree, countChildren, getProperties,
    getProperty, setProperty, getName, recursive_count, removeComposite,
    getFirstAtom, getFirstResidue, getFirstChain, getFirstSystem

import Base.convert, Base.iterate

"""
An Interface which implements functions for tree traversal, exploration, manipulation, iteration.
Implements getters and setters. Also implements functions to collect objects from the tree.\n
See also [`System`](@ref), [`Chain`](@ref), [`Residue`](@ref), [`Atom`](@ref).
"""
abstract type AbstractComposite <: KernelInterface end

#=  Preorder, Neutral-Left-Right iterator
    (first current object, then DFS-like left subtree,
    then DFS-like right subtree)
=#
"""
    Base.iterate(::AbstractComposite)
Iterates over the tree rooted in `c` in [Pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR).
Use [`recursive_collect`](@ref) as `Base.iterate` is slower.
"""
Base.iterate(c :: AbstractComposite) = (c, AbstractComposite[c])
function Base.iterate(_ :: AbstractComposite, stack :: Vector{AbstractComposite})
    cp = last(stack)
    if cp.first_child_ !== nothing
        return (cp.first_child_, push!(stack, cp.first_child_))
    else
        if cp.next_ !== nothing
            stack[end] = cp.next_
            return (cp.next_, stack)
        else # backtracks
            pop!(stack)
            while !isempty(stack)
                if !isdefined(last(stack),:next_)
                    println(typeof(last(stack)), " ",last(stack))
                end
                cp = last(stack).next_
                if cp !== nothing
                    stack[end] = cp
                    return (cp, stack)
                end
                pop!(stack)
            end
            return nothing
        end
    end
end

Base.length(C::AbstractComposite) = countDescendants(C)
Base.eltype(::AbstractComposite) = AbstractComposite

"""
    isDescendantOf(this::T, other::S) where {T,S <: AbstractComposite}
Checks if `other` is a descendant of `this`.
"""
isDescendantOf(this::T, other::S) where {T,S <: AbstractComposite} = begin
    cur::AbstractComposite = this
    while !isnothing(cur.parent_)
        cur = cur.parent_
        (cur == other) && return true
    end
    return false
end

"""
    countDescendants(node::Type) where {Type <: AbstractComposite}
Counts the numbers of descendants of `node`.
"""
countDescendants(node::Type) where {Type <: AbstractComposite} = begin
    number_of_descendants::Int64 = 1
    if isnothing(node.first_child_)
        return 1
    end

    cur = node.first_child_
    number_of_descendants += countDescendants(cur)
    while !isnothing(cur.next_)
        number_of_descendants += countDescendants(cur.next_)
        cur = cur.next_
    end
    return number_of_descendants
end

"""
    removeChild(root::T, child_node::S) where{T,S <: AbstractComposite}
Removes `child_node` from `root`. Return `false` if `child_node` is not `root`'s child.
"""
removeChild(root::T, child_node::S) where{T,S <: AbstractComposite} = begin
    # avoid self-removal and removal of ancestors
    if root == child_node || isDescendantOf(root, child_node)
        return false
    end

    #if child has no parent, we cannot remove it
    if isnothing(child_node.parent_)
        return false
    end

    #remove child from the list of children
    if root.first_child_ == child_node
        root.first_child_ = root.first_child_.next_

        if (!isnothing(root.first_child_))
            first_child_->previous_ = nothing
        else
            root.last_child_ = nothing
        end
        root.number_of_children_ -= (!isnothing(countDescendants(child_node)) ? countDescendants(child_node) : 0)

        root.child.next_ = nothing
    else
        if root.last_child_ == child_node
            root.last_child_ = child_node.previous_
            root.last_child_.next_ = child_node.previous_ = nothing
        else
            #println(root, child_node)
            child_node.previous_.next_ = child_node.next_

            child_node.next_.previous_ = child_node.previous_

            child_node.previous_ = child_node.next_ = nothing
        end

        root.number_of_children_ -= countDescendants(child_node)
    end

    # delete the child`s parent pointer
    child_node.parent_ = nothing

    # adjust some counters
    root.number_of_children_ -= 1

    #=
    if (child_node.contains_selection_)
        number_of_children_containing_selection_ -= 1
        if (child_node.selected_)
            number_of_selected_children_ -= 1
        end
    end

    # update the selection
    updateSelection_();

    # update modification time stamp
    stamp(MODIFICATION);
    =#
    return true

end

"""
    removeChild(child::T) where T<:AbstractComposite
Removes `child_node` from `root`. Return `false` if `child_node` is not `root`'s child.
"""
removeChild(child::T) where T<:AbstractComposite = removeChild(child.parent_, child)

"""
    removeComposite(comp::T) where T<:AbstractComposite
Removes `child_node` from `root`. Return `false` if `child_node` is not `root`'s child.
"""
removeComposite(comp::T) where T<:AbstractComposite = removeChild(comp.parent_, comp)

"""
    getParent(node::AbstractComposite)
Gets `node`'s parent. Can return `nothing`.
"""
getParent(node::AbstractComposite) = begin
    return node.parent_
end

"""
    getChildren(node::T) where T <: AbstractComposite
Gets `node`'s immediate children.
"""
getChildren(node::T) where T <: AbstractComposite = begin

    if node.first_child_ !== nothing
        cur = node.first_child_
        children = (typeof(node.first_child_))[node.first_child_]
        while cur.next_ !== nothing
            push!(children,cur.next_)
            cur = cur.next_
        end
        return children

    end

end

"""
    appendChild(old_node::T, new_node::S) where {T,S <: AbstractComposite}
Appends `new_node` to `old_node`. Returns `nothing` if not possible and removes `old_node`'s previous
parent if needed.\n
See also [`removeChild`](@ref).
"""
appendChild(old_node::T, new_node::S) where {T,S <: AbstractComposite} = begin
    #avoid self-appending and appending of parent nodes
    if old_node == new_node || isDescendantOf(new_node, old_node)
       return nothing
    end

    # if composite has a parent, remove it from there
    if !isnothing(new_node.parent_)
        removeChild(new_node.parent_, new_node);
    end

    # insert it
    if isnothing(old_node.last_child_)
        # its the only child - easy!
        old_node.first_child_ = old_node.last_child_ = new_node
    else
        # append it to the list of children
        old_node.last_child_.next_ = new_node
        new_node.previous_ = old_node.last_child_
        old_node.last_child_ = new_node
    end

    isnothing(old_node.number_of_children_) && ( old_node.number_of_children_ = 0)
    old_node.number_of_children_ += countDescendants(new_node)

    new_node.parent_ = old_node

    #=

    # update modification time stamp
    old_node.last_child_.stamp(MODIFICATION);

    # update selection counters
    if (new_node.containsSelection())
        number_of_children_containing_selection_ += 1
        if (new_node.selected_)
            number_of_selected_children_+=1
        end
        # recursively update the nodes` states
        (old_node.)updateSelection_();
    end
    =#

end

appendChild(::Nothing, ::S) where S<:AbstractComposite = nothing
appendChild(::S, ::Nothing) where S<:AbstractComposite = nothing

"""
   isSibling(comp::AbstractComposite, other::AbstractComposite)
Returns `true` if `other` and `comp are siblings`, else `false`
"""
isSibling(comp::AbstractComposite, other::AbstractComposite) = begin
    if !isnothing(comp.parent_)
        return (other in getChildren(comp.parent_))
    else
        return false
    end
end

"""
    appendSibling(comp::T, other::T) where T<:AbstractComposite
Inserts `other` after `comp` in the list of Siblings.\n
See also [`prependSibling`](@ref).
"""
appendSibling(comp::T, other::T) where T<:AbstractComposite = begin
    isSibling(comp, other) && return
    cur::T = comp
    while !isnothing(cur.next_)
         cur = cur.next_
    end
    cur.next_ = other
    other.previous_ = cur
    other.parent_ = comp.parent_

    if !isnothing(comp.parent_)
        comp.parent_.number_of_children_ += countDescendants(other)
    end
end

appendSibling(::Nothing, ::S) where S<:AbstractComposite = nothing
appendSibling(::S, ::Nothing) where S<:AbstractComposite = nothing

"""
    prependSibling(comp::T, other::T) where T<:AbstractComposite
Inserts `other` before `comp` in the list of Siblings. \n
See also [`appendSibling`](@ref).
"""
prependSibling(comp::T, other::T) where T<:AbstractComposite = begin
    isSibling(comp, other) && return
    cur::T = comp
    while !isnothing(cur.previous_)
         cur = cur.previous_
    end
    cur.prev_ = other
    other.next_ = cur
    other.parent_ = comp.parent_

    if !isnothing(comp.parent_)
        comp.parent_.number_of_children_ += countDescendants(other)
    end
end

countDescendants_iterate(node::Type) where {Type <: AbstractComposite} = begin
    number_of_descendants::Int64 = 0
    for descendants in node
        number_of_descendants += 1
    end
    return number_of_descendants
end

"""
    recursive_count(node::Type1, collectType::Type{Type2} where {Type1, Type2 <: AbstractComposite}
Counts nodes of the tree rooted in `node`. `collectType` filters the type to be counted.
"""
recursive_count(node::Type1, collectType::Type{Type2}) where {Type1, Type2 <: AbstractComposite} = begin

    recursive_count_counter = 0
        recursive_count(node::Type1,counter::Int64, collectType::Type{Type2}) where {Type1, Type2 <: AbstractComposite} = begin
        if collectType == AbstractComposite
            recursive_count_counter += 1
        end
        (typeof(node) == collectType) && (recursive_count_counter +=1)
        if !isnothing(node.first_child_)
            cur = node.first_child_
            recursive_count(cur,counter,collectType)
            while !isnothing(cur.next_)
                recursive_count(cur.next_,counter,collectType)
                cur = cur.next_
            end
        end
        return
    end

    recursive_count(node,0,collectType)
    return recursive_count_counter
end

"""
    recursive_collect(node::Type1, collectType::Type{Type2}) where {Type1, Type2 <: AbstractComposite}
Collects nodes of the tree rooted in `node` in [Pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order,_NLR).
`collectType` filters the type to be collected. \n
See also [`collectAtoms`](@ref), [`collectResidues`](@ref), [`collectChains`](@ref).
"""
recursive_collect(node::Type1, collectType::Type{Type2}) where {Type1, Type2 <: AbstractComposite} = begin
    #performs a collect on current node and all its ancestors
    vec = Vector{collectType}()

    recursive_collect(node::Type1,vec::Vector{Type2}, collectType::Type{Type2}) where {Type1, Type2 <: AbstractComposite} = begin
        if collectType == AbstractComposite
            push!(vec,node)
        end
        (typeof(node) == collectType) && push!(vec,node)
        if !isnothing(node.first_child_)
            cur = node.first_child_
            recursive_collect(cur,vec,collectType)
            while !isnothing(cur.next_)
                recursive_collect(cur.next_,vec,collectType)
                cur = cur.next_
            end
        end
        return vec
    end

    recursive_collect(node,vec,collectType)
    return vec
end
"""
    collect(node::T) where T <: AbstractComposite
Collects all nodes in the subtree rooted in `node`.
"""
Base.collect(node::T) where T <: AbstractComposite = begin
    return recursive_collect(node,AbstractComposite)
end

"""
    clearSelectionTree(x::AbstractComposite)
Deselets any selected Nodes. See [`Selectable`](@ref).
"""
function clearSelectionTree(x::AbstractComposite)
    for node in x
        deselect(x)
    end
end

"""
    countChildren(comp::AbstractComposite)
Counts the number of immediate Children.
"""
countChildren(comp::AbstractComposite) = begin
    count = 0
    if !isnothing(comp.first_child_)
        count = 1
        cur = comp.first_child_
        while !isnothing(cur.next_)
            count += 1
            cur = cur.next_
        end
    end
    return count
end

"""
    getProperties(::AbstractComposite)
Getter of property-Vector
"""
getProperties(comp::AbstractComposite) = begin
    return comp.properties_
end
"""
    hasProperty(comp::AbstractComposite, property::String)
Checks if `comp` has property `property`.
"""
hasProperty(comp::AbstractComposite, property::String) = begin
    if any([property == x[1] for x in getProperties(comp) ])
       return true
    end
    return false
end

"""
    getProperty(comp::AbstractComposite, property::String)
Gets value of `property` if set, else `nothing`.
"""
getProperty(comp::AbstractComposite, property::String) = begin
    if hasProperty(comp,property)
        index = findfirst((x::Tuple{String,UInt8})-> property == x[1], getProperties(comp))
        return getProperties(comp)[index][2]
    end
    return nothing
end

"""
    setProperty(::AbstractComposite, ::Tuple{String,UInt8})
Setter. Deletes old property if needed.
"""
setProperty(comp::AbstractComposite, property::Tuple{String,UInt8}) = begin
    if hasProperty(comp,property[1])
        index = findfirst((x::Tuple{String,UInt8})-> property[1] == x[1], getProperties(comp))
        deleteat!(getProperties(comp), index)
    end
    push!(comp.properties_, property)
end
setProperty(comp::AbstractComposite, property::Tuple{String,Bool}) = setProperty(comp,(property[1],UInt8(property[2])))

"""
    getFirstAtom(comp::AbstractComposite)
Returns the first object that is an `AbstractAtom` subtype.
"""
getFirstAtom(comp::AbstractComposite) = begin
    cur::T where T <: AbstractComposite = comp
    while !isnothing(cur)
        if typeof(cur) <: AbstractAtom
            return cur
        end
        cur = cur.first_child_
    end
    return nothing
end

"""
    getFirstResidue(comp::AbstractComposite)
Returns the first object that is an `AbstractResidue` subtype.
"""
getFirstResidue(comp::AbstractComposite) = begin
    cur::T where T <: AbstractComposite = comp
    while !isnothing(cur)
        if typeof(cur) <: AbstractResidue
            return cur
        end
        cur = cur.first_child_
    end
    return nothing
end

"""
    getFirstChain(comp::AbstractComposite)
Returns the first object that is an `AbstractChain` subtype.
"""
getFirstChain(comp::AbstractComposite) = begin
    cur::T where T <: AbstractComposite = comp
    while !isnothing(cur)
        if typeof(cur) <: AbstractChain
            return cur
        end
        cur = cur.first_child_
    end
    return nothing
end

"""
    getFirstSystem(comp::AbstractComposite)
Returns the first object that is an `AbstractSystem` subtype.
"""
getFirstSystem(comp::AbstractComposite) = begin
    cur::T where T<:AbstractComposite = comp
    while !isnothing(cur)
        if typeof(cur) <: AbstractSystem
            return cur
        end
        cur = cur.first_child_
    end
    return nothing
end

"""
    getName(comp::AbstractComposite)
Fallback getter for a `Composite`'s' name.\n See also [getName(::System)](@ref), [getName(::Chain)](@ref),
 [getName(::Atom)](@ref), [getName(::Residue)](@ref)
"""
getName(comp::AbstractComposite) = begin
#chain and residue override this function in their respective julia file
    !isnothing(comp.name_) ? (return comp.name_) : "-"
end

getName(::Any) = "N/A"


text:
using Random
using TemporalGPs: RectilinearGrid, Separable

@testset "separable_kernel" begin
    rng = MersenneTwister(123456)

    k = Separable(SEKernel(), Matern32Kernel())
    x0 = collect(RectilinearGrid(randn(rng, 2), randn(rng, 3)))
    x1 = collect(RectilinearGrid(randn(rng, 2), randn(rng, 3)))
    x2 = collect(RectilinearGrid(randn(rng, 3), randn(rng, 1)))
    atol=1e-9

    # Check that elementwise basically works.
    @test kernelmatrix_diag(k, x0, x1) isa AbstractVector
    @test length(kernelmatrix_diag(k, x0, x1)) == length(x0)

    # Check that pairwise basically works.
    @test kernelmatrix(k, x0, x2) isa AbstractMatrix
    @test size(kernelmatrix(k, x0, x2)) == (length(x0), length(x2))

    # Check that elementwise is consistent with pairwise.
    @test kernelmatrix_diag(k, x0, x1) ≈ diag(kernelmatrix(k, x0, x1)) atol=atol

    # Check additional binary elementwise properties for kernels.
    @test kernelmatrix_diag(k, x0, x1) ≈ kernelmatrix_diag(k, x1, x0)
    @test kernelmatrix(k, x0, x2) ≈ kernelmatrix(k, x2, x0)' atol=atol

    # Check that unary elementwise basically works.
    @test kernelmatrix_diag(k, x0) isa AbstractVector
    @test length(kernelmatrix_diag(k, x0)) == length(x0)

    # Check that unary pairwise basically works.
    @test kernelmatrix(k, x0) isa AbstractMatrix
    @test size(kernelmatrix(k, x0)) == (length(x0), length(x0))
    @test kernelmatrix(k, x0) ≈ kernelmatrix(k, x0)' atol=atol

    # Check that unary elementwise is consistent with unary pairwise.
    @test kernelmatrix_diag(k, x0) ≈ diag(kernelmatrix(k, x0)) atol=atol

    # Check that unary pairwise produces a positive definite matrix (approximately).
    @test all(eigvals(Matrix(kernelmatrix(k, x0))) .> -atol)

    # Check that unary elementwise / pairwise are consistent with the binary versions.
    @test kernelmatrix_diag(k, x0) ≈ kernelmatrix_diag(k, x0, x0) atol=atol
    @test kernelmatrix(k, x0) ≈ kernelmatrix(k, x0, x0) atol=atol
end

ext: jl


text:
# References
#
# 18.8.30 numFmt (Number Format)
# lists a table with predefined numFmtId.
# In this case, a numFmtId value is written on the xf record,
# but no corresponding numFmt element is written.
# Some of these Ids can be interpreted differently,
# depending on the UI language of the implementing application.
#
# General formatCodes ranges from 0 to 81.
#
# 18.8.10 cellXfs (Cell Formats)
import Base: isempty

function CellValue(ws::Worksheet, val::CellValueType)
    CellValue(val, default_cell_format(ws, val))
end

id(format::CellDataFormat) = string(format.id)
id(::EmptyCellDataFormat) = ""
isempty(::CellDataFormat) = false
isempty(::EmptyCellDataFormat) = true

# The number of predefined number formats in XLSX
# Any custom number formats must have an id >= PREDEFINED_NUMFMT_COUNT
const PREDEFINED_NUMFMT_COUNT = 164

# these formats may appear differently in different editors
const DEFAULT_DATE_numFmtId = 14 # dd-mm-yyyy
const DEFAULT_TIME_numFmtId = 20 # h:mm
const DEFAULT_DATETIME_numFmtId = 22 # dd-mm-yyyy h:mm

# Returns the default `CellDataFormat` for a type
default_cell_format(::Worksheet, ::CellValueType) = EmptyCellDataFormat()
default_cell_format(ws::Worksheet, ::Dates.Date) = get_num_style_index(ws, DEFAULT_DATE_numFmtId)
default_cell_format(ws::Worksheet, ::Dates.Time) = get_num_style_index(ws, DEFAULT_TIME_numFmtId)
default_cell_format(ws::Worksheet, ::Dates.DateTime) = get_num_style_index(ws, DEFAULT_DATETIME_numFmtId)

# Attempts to get CellDataFormat associated with a numFmtId and sets a default style if it is not found
# Use for ensuring default formats exist
function get_num_style_index(ws::Worksheet, numformatid::Integer)
    @assert numformatid >= 0 "Invalid number format id"

    wb = get_workbook(ws)
    style_index = styles_get_cellXf_with_numFmtId(wb, numformatid)
    if isempty(style_index)
        # adds default style <xf applyNumberFormat="1" borderId="0" fillId="0" fontId="0" numFmtId=formatid xfId="0"/>
        style_index = styles_add_cell_xf(wb, Dict("applyNumberFormat"=>"1", "borderId"=>"0", "fillId"=>"0", "fontId"=>"0", "numFmtId"=>string(numformatid), "xfId"=>"0"))
    end

    return style_index
end

# get styles document for workbook
function styles_xmlroot(workbook::Workbook)
    if workbook.styles_xroot == nothing
        STYLES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles"
        if has_relationship_by_type(workbook, STYLES_RELATIONSHIP_TYPE)
            styles_target = get_relationship_target_by_type("xl", workbook, STYLES_RELATIONSHIP_TYPE)
            styles_root = xmlroot(get_xlsxfile(workbook), styles_target)

            # check root node name for styles.xml
            @assert get_default_namespace(styles_root) == SPREADSHEET_NAMESPACE_XPATH_ARG[1][2] "Unsupported styles XML namespace $(get_default_namespace(styles_root))."
            @assert EzXML.nodename(styles_root) == "styleSheet" "Malformed package. Expected root node named `styleSheet` in `styles.xml`."
            workbook.styles_xroot = styles_root
        else
            error("Styles not found for this workbook.")
        end
    end

    return workbook.styles_xroot
end

# Returns the xf XML node element for style `index`.
# `index` is 0-based.
function styles_cell_xf(wb::Workbook, index::Int) :: EzXML.Node
    xroot = styles_xmlroot(wb)
    xf_elements = findall("/xpath:styleSheet/xpath:cellXfs/xpath:xf", xroot, SPREADSHEET_NAMESPACE_XPATH_ARG)
    return xf_elements[index+1]
end

# Queries numFmtId from cellXfs -> xf nodes."
function styles_cell_xf_numFmtId(wb::Workbook, index::Int) :: Int
    el = styles_cell_xf(wb, index)
    if !haskey(el, "numFmtId")
        return 0
    end
    return parse(Int, el["numFmtId"])
end

# Defines a custom number format to render numbers, dates or text.
# Returns the index to be used as the `numFmtId` in a cellXf definition.
function styles_add_numFmt(wb::Workbook, format_code::AbstractString) :: Integer
    xroot = styles_xmlroot(wb)

    numfmts = findall("/xpath:styleSheet/xpath:numFmts", xroot, SPREADSHEET_NAMESPACE_XPATH_ARG)
    if isempty(numfmts)
        stylesheet = findfirst("/xpath:styleSheet", xroot, SPREADSHEET_NAMESPACE_XPATH_ARG)

        # We need to add the numFmts node directly after the styleSheet node
        child = EzXML.firstelement(stylesheet)
        numfmts = EzXML.addelement!(stylesheet, "numFmts")
        EzXML.unlink!(numfmts)
        EzXML.linkprev!(child, numfmts)
    else
        numfmts = numfmts[1]
    end

    existing_numFmt_elements_count = EzXML.countelements(numfmts)
    new_fmt = EzXML.addelement!(numfmts, "numFmt")

    fmt_code = existing_numFmt_elements_count + PREDEFINED_NUMFMT_COUNT
    new_fmt["numFmtId"] = fmt_code
    new_fmt["formatCode"] = xlsx_escape(format_code)

    return fmt_code
end

const FontAttribute = Union{AbstractString, Pair{String, Pair{String, String}}}

# Defines a custom font. Returns the index to be used as the `fontId` in a cellXf definition.
function styles_add_font(wb::Workbook, attributes::Vector{FontAttribute})
    xroot = styles_xmlroot(wb)
    fonts_element = findfirst("/xpath:styleSheet/xpath:fonts", xroot, SPREADSHEET_NAMESPACE_XPATH_ARG)
    existing_font_elements_count = EzXML.countelements(fonts_element)

    new_font = EzXML.addelement!(fonts_element, "font")
    for a in attributes
        if a isa Pair
            name, val = last(a)
            attr = EzXML.addelement!(new_font, first(a))
            attr[name] = val
        else
            EzXML.addelement!(new_font, a)
        end
    end

    return existing_font_elements_count
end

# Queries numFmt formatCode field by numFmtId.
function styles_numFmt_formatCode(wb::Workbook, numFmtId::AbstractString) :: String
    xroot = styles_xmlroot(wb)
    elements_found = findall("/xpath:styleSheet/xpath:numFmts/xpath:numFmt[@numFmtId='$(numFmtId)']", xroot, SPREADSHEET_NAMESPACE_XPATH_ARG)
    @assert length(elements_found) == 1 "numFmtId $numFmtId not found."
    return elements_found[1]["formatCode"]
end

styles_numFmt_formatCode(wb::Workbook, numFmtId::Int) = styles_numFmt_formatCode(wb, string(numFmtId))

const DATETIME_CODES = ["d", "m", "yy", "h", "s", "a/p", "am/pm"]

function remove_formatting(code::AbstractString)
    # this regex should cover all the formatting cases found here(colors/conditionals/quotes/spacing):
    # https://support.office.com/en-us/article/create-or-delete-a-custom-number-format-78f2a361-936b-4c03-8772-09fab54be7f4
    ignoredformatting = r"""
        \[.{2,}?\]|
        ".+"|
        _.|
        \\.|
        \*.
        """x
    replace(code, ignoredformatting => "")
end

function styles_is_datetime(wb::Workbook, index::Int) :: Bool
    if !haskey(wb.buffer_styles_is_datetime, index)
        isdatetime = false

        numFmtId = styles_cell_xf_numFmtId(wb, index)

        if (14 <= numFmtId && numFmtId <= 22) || (45 <= numFmtId && numFmtId <= 47)
            isdatetime = true
        elseif numFmtId > 81
            code = lowercase(styles_numFmt_formatCode(wb, numFmtId))
            code = remove_formatting(code)
            if any(map(x->occursin(x, code), DATETIME_CODES))
                isdatetime = true
            end
        end

        wb.buffer_styles_is_datetime[index] = isdatetime
    end

    return wb.buffer_styles_is_datetime[index]
end

styles_is_datetime(wb::Workbook, fmt::CellDataFormat) = styles_is_datetime(wb, Int(fmt.id))

function styles_is_datetime(wb::Workbook, index::AbstractString)
    @assert !isempty(index)
    styles_is_datetime(wb, parse(Int, index))
end

styles_is_datetime(ws::Worksheet, index) = styles_is_datetime(get_workbook(ws), index)

function styles_is_float(wb::Workbook, index::Int) :: Bool
    if !haskey(wb.buffer_styles_is_float, index)
        isfloat = false
        numFmtId = styles_cell_xf_numFmtId(wb, index)

        if numFmtId == 2 || numFmtId == 4 || (7 <= numFmtId && numFmtId <= 11) || numFmtId == 39 || numFmtId == 40 || numFmtId == 44 || numFmtId == 48
            isfloat = true
        elseif numFmtId > 81
            code = styles_numFmt_formatCode(wb, numFmtId)
            code = remove_formatting(code)

            floatformats = r"""
                \.[0#?]|
                [0#?]e[+-]?[0#?]|
                [0#?]/[0#?]|
                %
                """ix
            if occursin(floatformats, code)
                isfloat = true
            end
        end

        wb.buffer_styles_is_float[index] = isfloat
    end

    return wb.buffer_styles_is_float[index]
end

function styles_is_float(wb::Workbook, index::AbstractString)
    @assert !isempty(index)
    styles_is_float(wb, parse(Int, index))
end

styles_is_float(wb::Workbook, fmt::CellDataFormat) = styles_is_float(wb, Int(fmt.id))
styles_is_float(ws::Worksheet, index) = styles_is_float(get_workbook(ws), index)

#=
Cell Xf element follows the XML format below.
This function queries the 0-based index of the first xf element that has the provided numFmtId.
Returns -1 if not found.

```
<styleSheet ...
    <cellXfs count="5">
            <xf borderId="0" fillId="0" fontId="0" numFmtId="0" xfId="0"/>
            <xf applyNumberFormat="1" borderId="0" fillId="0" fontId="0" numFmtId="14" xfId="0"/>
            <xf applyNumberFormat="1" borderId="0" fillId="0" fontId="0" numFmtId="20" xfId="0"/>
            <xf applyNumberFormat="1" borderId="0" fillId="0" fontId="0" numFmtId="22" xfId="0"/>
```
=#
function styles_get_cellXf_with_numFmtId(wb::Workbook, numFmtId::Int) :: AbstractCellDataFormat
    xroot = styles_xmlroot(wb)
    elements_found = findall("/xpath:styleSheet/xpath:cellXfs/xpath:xf", xroot, SPREADSHEET_NAMESPACE_XPATH_ARG)

    if isempty(elements_found)
        return EmptyCellDataFormat()
    else
        for i in 1:length(elements_found)
            el = elements_found[i]
            if haskey(el, "numFmtId")
                if parse(Int, el["numFmtId"]) == numFmtId
                    return CellDataFormat(i-1)
                end
            end
        end

        # not found
        return EmptyCellDataFormat()
    end
end

function styles_add_cell_xf(wb::Workbook, attributes::Dict{String, String}) :: CellDataFormat
    xroot = styles_xmlroot(wb)
    cellXfs_element = findfirst("/xpath:styleSheet/xpath:cellXfs", xroot, SPREADSHEET_NAMESPACE_XPATH_ARG)
    existing_cellxf_elements_count = EzXML.countelements(cellXfs_element)

    new_xf = EzXML.addelement!(cellXfs_element, "xf")
    for k in keys(attributes)
        new_xf[k] = attributes[k]
    end

    return CellDataFormat(existing_cellxf_elements_count) # turns out this is the new index
end


text:
using BenchmarkTools
using CUDA
using Oceananigans
using Benchmarks

# Benchmark function

function benchmark_particle_tracking(Arch, N_particles)
    grid = RegularRectilinearGrid(size=(128, 128, 128), extent=(1, 1, 1))

    if N_particles == 0
        particles = nothing
    else
        ArrayType = Arch == CPU ? Array : CuArray
        x₀ = zeros(N_particles) |> ArrayType
        y₀ = zeros(N_particles) |> ArrayType
        z₀ = zeros(N_particles) |> ArrayType
        particles = LagrangianParticles(x=x₀, y=y₀, z=z₀)
    end

    model = IncompressibleModel(architecture=Arch(), grid=grid, particles=particles)

    time_step!(model, 1) # warmup

    trial = @benchmark begin
        @sync_gpu time_step!($model, 1)
    end samples=10

    return trial
end

# Benchmark parameters

Architectures = has_cuda() ? [CPU, GPU] : [CPU]
N_particles = [0, 1, 10, 10^2, 10^3, 10^4, 10^5, 10^6, 10^7, 10^8]

# Run and summarize benchmarks

print_system_info()
suite = run_benchmarks(benchmark_particle_tracking; Architectures, N_particles)

df = benchmarks_dataframe(suite)
sort!(df, [:Architectures, :N_particles], by=(string, identity))
benchmarks_pretty_table(df, title="Lagrangian particle tracking benchmarks")

if GPU in Architectures
    df_Δ = gpu_speedups_suite(suite) |> speedups_dataframe
    sort!(df_Δ, :N_particles)
    benchmarks_pretty_table(df_Δ, title="Lagrangian particle tracking CPU -> GPU speedup")
end

for Arch in Architectures
    suite_arch = speedups_suite(suite[@tagged Arch], base_case=(Arch, N_particles[1]))
    df_arch = speedups_dataframe(suite_arch, slowdown=true)
    sort!(df_arch, :N_particles)
    benchmarks_pretty_table(df_arch, title="Lagrangian particle tracking relative performance ($Arch)")
end


text:
"get specific of heat capacity"
function get_cp(gas, T, X, mean_MW)
    cp_T = [1.0, T, T^2, T^3, T^4]
    if T <= 1000.0
        cp = @view(gas.thermo.nasa_low[:, 1:5]) * cp_T
    else
        cp = @view(gas.thermo.nasa_high[:, 1:5]) * cp_T
    end
    # TODO: not sure if inplace operation will be an issue for AD
    if !gas.thermo.isTcommon
        ind_correction = @. (T > 1000.0) & (T < gas.thermo.Trange[:, 2])
        cp[ind_correction] .=
            @view(gas.thermo.nasa_low[ind_correction, 1:5]) * cp_T
    end
    cp_mole = dot(cp, X) * R
    cp_mass = cp_mole / mean_MW
    return cp_mole, cp_mass
end
export get_cp

"get specific of heat capacity"
function get_cv(cp_mole, cp_mass, mean_MW)
    cv_mole = cp_mole - R
    cv_mass = cv_mole / mean_MW
    return cv_mole, cv_mass
end
export get_cv

"get enthaphy (H) per mole"
function get_H(gas, T, Y, X)
    H_T = [1.0, T / 2.0, T^2 / 3.0, T^3 / 4.0, T^4 / 5.0, 1.0 / T]
    if T <= 1000.0
        h_mole = @view(gas.thermo.nasa_low[:, 1:6]) * H_T * R * T
    else
        h_mole = @view(gas.thermo.nasa_high[:, 1:6]) * H_T * R * T
    end
    if !gas.thermo.isTcommon
        ind_correction = @. (T > 1000.0) & (T < gas.thermo.Trange[:, 2])
        h_mole[ind_correction] .=
            @view(gas.thermo.nasa_low[ind_correction, 1:6]) * H_T * R * T
    end
    # H_mole = dot(h_mole, X)
    return h_mole
end
export get_H

"get enthaphy (H) per mass"
function H_mass_func(gas, h_mole, Y)
    return dot(h_mole ./ gas.MW, Y)
end
export H_mass_func

"get enthaphy (U) per mole"
function get_U(h_mole, T)
    u_mole = h_mole .- (R * T)
    return u_mole
end
export get_U

"get enthaphy (U) per mass"
function U_mass_func(gas, u_mole, Y)
    return dot(u_mole ./ gas.MW, Y)
end
export U_mass_func

"get entropy (S)"
function get_S(gas, T, P, X)
    S_T = [log(T), T, T^2 / 2.0, T^3 / 3.0, T^4 / 4.0, 1.0]
    if T <= 1000.0
        S0 = @view(gas.thermo.nasa_low[:, [1, 2, 3, 4, 5, 7]]) * S_T * R
    else
        S0 = @view(gas.thermo.nasa_high[:, [1, 2, 3, 4, 5, 7]]) * S_T * R
    end
    if !gas.thermo.isTcommon
        ind_correction = @. (T > 1000.0) & (T < gas.thermo.Trange[:, 2])
        S0[ind_correction] .=
            @view(gas.thermo.nasa_low[ind_correction, [1, 2, 3, 4, 5, 7]]) *
            S_T *
            R
    end
    # _X = @. S0 - R * log(clamp(X, 1.e-30, Inf))
    # s_mole = _X .- R * (P / one_atm)
    # S_mole = dot(s_mole, X)
    return S0
end
export get_S

function S_mass_func(gas, s_mole, Y)
    return dot(s_mole ./ gas.MW, Y)
end
export S_mass_func


text:
"""
    read_cuboid(cfgpath :: String)

Read 3D array from a disk. The data on disk consists of two files:
JSON configuration file (which is passed to this function) and a raw
binary array data.

Scheme of the configuration file is as follows, where `x`, `y` and `z`
are dimensions of the array:

~~~~{.json}
{
    "dimensions": [x, y, z],
    "datapath": "file-with-data"
}
~~~~

The file with binary data, whose name is specified in `datapath`
field, is searched relatively to the directory with the JSON
configuration file. Its size must be exactly `x`⋅`y`⋅`z` bytes, each
byte containing an element of the resulting array.
"""
function read_cuboid(cfgpath :: String)
    dim, datapath = open(cfgpath) do input
        json = JSON.parse(input)
        Vector{Int64}(json["dimensions"]), json["datapath"]
    end

    datapath = joinpath(dirname(cfgpath), datapath)
    s = stat(datapath)
    if s.size != prod(dim)
        error("File size mismatch. Got $(s.size), expected $(prod(dim))")
    end

    data = Array{Int8, length(dim)}(undef, dim...)
    open(datapath) do input
        read!(input, data)
    end

    return data
end

@doc raw"""
    read_cuboid(datapath :: String, side, dim)

Read 3D array from a disk. The data on the disk must be in binary
format, one octet per sample. Totally, there must be $side^{dim}$
octets which are read into $side \times side \times \dots \times side$
array.
"""
function read_cuboid(datapath :: String,
                     side     :: Integer,
                     dim      :: Integer)
    s = stat(datapath)
    if s.size != prod(side^dim)
        error("File size mismatch. Got $(s.size), expected $(side^dim)")
    end

    dimensions = fill(side, dim) |> Tuple
    data = Array{Int8, dim}(undef, dimensions...)

    open(datapath) do input
        read!(input, data)
    end

    return data
end

######################
# Component labeling #
######################

# Two components are considered connected if they have manhattan_dist == 1
manhattan_dist(x :: NTuple{N, Int}, y :: NTuple{N, Int}) where N =
    sum(abs.(x .- y))
manhattan_dist(x :: CartesianIndex{N}, y :: CartesianIndex{N}) where N =
    manhattan_dist(x |> Tuple, y |> Tuple)

abstract type Topology end
struct Plane <: Topology end # For non-periodic c2
struct Torus <: Topology end # For periodic c2

function wrapidx(idx :: CartesianIndex{N}, array :: AbstractArray{T, N}) where {T, N}
    tuple = mod.(Tuple(idx), axes(array))
    return CartesianIndex(tuple)
end

# Pregenerate an array of adjacent elements
macro gen_adjacent(N :: Int)
    name = Symbol("adj", N, "d")
    center = zeros(Int, N)
    diffs = Tuple(-1:1 for n in 1:N)
    quote
        const $(esc(name)) = [
            x
            for x in CartesianIndices($diffs)
            if manhattan_dist(x, CartesianIndex($(center...))) == 1
        ]
    end
end

@gen_adjacent 1
@gen_adjacent 2
@gen_adjacent 3

function adjacent_elements(N :: Int)
    if N == 3 return adj3d
    elseif N == 2 return adj2d
    elseif N == 1 return adj1d
    else error("Unsupported number of dimensions")
    end
end

# Iterate over adjacent element in a "wrapped" torus space
function iterate_adjacent(index :: CartesianIndex{N},
                          array :: AbstractArray{T, N}) where{T, N}
    return (wrapidx(index + adj, array)
            for adj in adjacent_elements(N)
            if manhattan_dist(index, index + adj) == 1)
end

function Images.label_components(input :: AbstractArray{T, N},
                                 _     :: Torus) where {T <: Integer, N}
    # -1 means an absence of label
    output = fill(-1, size(input))
    # Current label
    label :: Int = 1
    # Queue of unlabeled neighbors
    queue = Vector{CartesianIndex{N}}(undef, 0)

    push_in_queue!(index :: CartesianIndex) =
        if input[index] == 0
            # If an element is a background element, label it as so
            output[index] = 0
        elseif output[index] == -1
            # If an element does not have a label, assign current label
            output[index] = label
            push!(queue, index)
        else
            # The item is labeled, do not insert it in the queue
        end
    pop_from_queue!() = pop!(queue)

    for index in CartesianIndices(input)
        push_in_queue!(index)

        delta = (length(queue) != 0) ? 1 : 0
        while length(queue) > 0
            idx = pop_from_queue!()

            for aidx in iterate_adjacent(idx, input)
                push_in_queue!(aidx)
            end
        end
        label = label + delta
    end

    return output
end

Images.label_components(input :: AbstractArray, :: Plane) = Images.label_components(input)

################################
# Euclidean distance transform #
################################

# This code can be rewritten into more beautiful implementation.
# The bad thing is that Julia is against any beauty.

function edt(array    :: AbstractArray{Bool},
             topology :: Topology)
    len = array |> length |> Float64
    binary = map(x -> len^2*(1-x), array)
    return edt!(binary, topology) .|> sqrt
end

function lower_envelope(array :: AbstractVector{Float64})
    # This code is Fortran-like.
    # Proper data structures like lists are required to make it more understandable.
    # Unfortunately, Julia does not have them, so prepare to suffer.
    len = length(array)
    v = zeros(Int, len)
    z = zeros(Float64, len+1)

    # Compute the lower envelope of family of parabolas
    k = 1
    v[1] = 1
    z[1] = -Inf
    z[2] = Inf

    for i in 2:len
        s = 0
        while true
            j = v[k]
            s = ((array[i] + i^2) - (array[j] + j^2))/(2i - 2j)
            if s > z[k]
                break
            end
            k = k - 1
        end
        k = k + 1
        v[k] = i
        z[k] = s
        z[k+1] = Inf
    end

    return v, z
end

function edt!(array :: AbstractVector{Float64}, :: Plane)
    v, z = lower_envelope(array)
    len = length(array)
    dist = similar(array)

    # Compute EDT by selecting a parabola which belongs to a lower
    # envelope for this line segment
    k = 1
    for i = 1:len
        while z[k+1] < i
            k = k + 1
        end
        j = v[k]
        dist[i] = array[j] + (i - j)^2
    end

    array .= dist
    return array
end

function edt!(array :: AbstractVector{Float64}, :: Torus)
    # Just do three times more work than for ::Plane version.
    # This can be optimized in the future.
    len = length(array)
    a2 = vcat(array, array, array)
    edt!(a2, Plane())
    array .= a2[len+1:2len]
    return array
end

function edt!(array :: AbstractMatrix{Float64}, topology :: Topology)
    x, y = size(array)
    local edtfn!(x) = edt!(x, topology)

    columns = (view(array, :, i) for i in 1:y)
    foreach(edtfn!, columns)

    rows = (view(array, i, :) for i in 1:x)
    foreach(edtfn!, rows)

    return array
end

function edt!(array :: AbstractArray{Float64, 3}, topology :: Topology)
    x, y, z = size(array)
    local edtfn!(x) = edt!(x, topology)

    planes = (view(array, :, :, i) for i in 1:z)
    foreach(edtfn!, planes)

    sides = (view(array, i, j, :) for i in 1:x for j in 1:y)
    foreach(edtfn!, sides)

    return array
end

# Use faster implementation from ImageMorphology with Plane topology.
Images.distance_transform(array :: AbstractArray{Bool}, :: Plane) =
    array |> Images.feature_transform |> Images.distance_transform

Images.distance_transform(array :: AbstractArray{Bool}, :: Torus) =
    edt(array, Torus())


text:
# Copyright (c) 2018: Matthew Wilhelm & Matthew Stuber.
# This code is licensed under MIT license (see LICENSE.md for full details)
#############################################################################
# EAGO
# A development environment for robust and global optimization
# See https://github.com/PSORLab/EAGO.jl
#############################################################################
# src/eago_optimizer/functions/nonlinear/functions.jl
# Defines variable info and function types.
#############################################################################

"""
$(TYPEDEF)

An abstract super-type used for representing constraints built by EAGO's backend.
"""
abstract type AbstractEAGOConstraint end

"""

Computes the lower interval bound for `AbstractEAGOConstraint` representing an
inequality constraint.
"""
function lower_interval_bound end

"""

Computes a tuple representing the lower and upper interval bounds for a
`AbstractEAGOConstraint` representing an equality constraint.
"""
function interval_bound end

"""

Eliminate fixed variables by rearrangment or restructuring of `AbstractEAGOConstraint`.
"""
function eliminate_fixed_variables! end

@enum(BRANCH_VARIABLE, UNSPEC_BRANCH, NO_BRANCH, BRANCH)

"""
$(TYPEDEF)

A structure used to store information related to the bounds assigned to each
variable.

$(TYPEDFIELDS)
"""
mutable struct VariableInfo
    "Is the variable integer valued?"
    is_integer::Bool
    "Lower bounds. May be -Inf."
    lower_bound::Float64
    "Boolean indicating whether finite lower bound exists."
    has_lower_bound::Bool
    "Upper bounds. May be Inf."
    upper_bound::Float64
    "Boolean indicating whether finite upper bound exists."
    has_upper_bound::Bool
    "Boolean indicating variable is fixed to a finite value."
    is_fixed::Bool
    "Is variable used for branching (unset in input model)"
    branch_on::BRANCH_VARIABLE
end
VariableInfo() = VariableInfo(false,-Inf, false, Inf, false, false, UNSPEC_BRANCH)
lower_bound(x::VariableInfo) = x.lower_bound
upper_bound(x::VariableInfo) = x.upper_bound
is_integer(x::VariableInfo) = x.is_integer

include("affine.jl")
include("quadratic.jl")
include("cone.jl")
include("nonlinear/nonlinear.jl")


text:
# This file was generated by the Julia Swagger Code Generator
# Do not modify this file directly. Modify the swagger specification instead.

mutable struct PacketCaptureResultProperties <: SwaggerModel
    target::Any # spec type: Union{ Nothing, String } # spec name: target
    bytesToCapturePerPacket::Any # spec type: Union{ Nothing, Int32 } # spec name: bytesToCapturePerPacket
    totalBytesPerSession::Any # spec type: Union{ Nothing, Int32 } # spec name: totalBytesPerSession
    timeLimitInSeconds::Any # spec type: Union{ Nothing, Int32 } # spec name: timeLimitInSeconds
    storageLocation::Any # spec type: Union{ Nothing, PacketCaptureStorageLocation } # spec name: storageLocation
    filters::Any # spec type: Union{ Nothing, Vector{PacketCaptureFilter} } # spec name: filters

    function PacketCaptureResultProperties(;target=nothing, bytesToCapturePerPacket=nothing, totalBytesPerSession=nothing, timeLimitInSeconds=nothing, storageLocation=nothing, filters=nothing)
        o = new()
        validate_property(PacketCaptureResultProperties, Symbol("target"), target)
        setfield!(o, Symbol("target"), target)
        validate_property(PacketCaptureResultProperties, Symbol("bytesToCapturePerPacket"), bytesToCapturePerPacket)
        setfield!(o, Symbol("bytesToCapturePerPacket"), bytesToCapturePerPacket)
        validate_property(PacketCaptureResultProperties, Symbol("totalBytesPerSession"), totalBytesPerSession)
        setfield!(o, Symbol("totalBytesPerSession"), totalBytesPerSession)
        validate_property(PacketCaptureResultProperties, Symbol("timeLimitInSeconds"), timeLimitInSeconds)
        setfield!(o, Symbol("timeLimitInSeconds"), timeLimitInSeconds)
        validate_property(PacketCaptureResultProperties, Symbol("storageLocation"), storageLocation)
        setfield!(o, Symbol("storageLocation"), storageLocation)
        validate_property(PacketCaptureResultProperties, Symbol("filters"), filters)
        setfield!(o, Symbol("filters"), filters)
        o
    end
end # type PacketCaptureResultProperties

const _property_map_PacketCaptureResultProperties = Dict{Symbol,Symbol}(Symbol("target")=>Symbol("target"), Symbol("bytesToCapturePerPacket")=>Symbol("bytesToCapturePerPacket"), Symbol("totalBytesPerSession")=>Symbol("totalBytesPerSession"), Symbol("timeLimitInSeconds")=>Symbol("timeLimitInSeconds"), Symbol("storageLocation")=>Symbol("storageLocation"), Symbol("filters")=>Symbol("filters"))
const _property_types_PacketCaptureResultProperties = Dict{Symbol,String}(Symbol("target")=>"String", Symbol("bytesToCapturePerPacket")=>"Int32", Symbol("totalBytesPerSession")=>"Int32", Symbol("timeLimitInSeconds")=>"Int32", Symbol("storageLocation")=>"PacketCaptureStorageLocation", Symbol("filters")=>"Vector{PacketCaptureFilter}")
Base.propertynames(::Type{ PacketCaptureResultProperties }) = collect(keys(_property_map_PacketCaptureResultProperties))
Swagger.property_type(::Type{ PacketCaptureResultProperties }, name::Symbol) = Union{Nothing,eval(Base.Meta.parse(_property_types_PacketCaptureResultProperties[name]))}
Swagger.field_name(::Type{ PacketCaptureResultProperties }, property_name::Symbol) =  _property_map_PacketCaptureResultProperties[property_name]

function check_required(o::PacketCaptureResultProperties)
    (getproperty(o, Symbol("target")) === nothing) && (return false)
    (getproperty(o, Symbol("storageLocation")) === nothing) && (return false)
    true
end

function validate_property(::Type{ PacketCaptureResultProperties }, name::Symbol, val)
end


text:
# Files for fetching stuff from the StaffJoy API

function fetch_organization(task)
    path = string("/api/v1/organizations/", task["organization_id"])
    results = Requests.get(build_uri(config, path))
    if results.status != 200
        code = results.status
        err("Organization API Down - code $code")
    end
    organization = JSON.parse(Requests.text(results))
    Logging.info( "Organization Info Received")
    return organization["data"]
end

function fetch_schedule(task)
    path = string(
        "/api/v1/organizations/",
        task["organization_id"],
        "/locations/",
        task["location_id"],
        "/roles/",
        task["role_id"],
        "/schedules/",
        task["schedule_id"],
    )
    results = Requests.get(build_uri(config, path))

    if results.status != 200
        code = results.status
        Logging.err( "schedule API down - code $code")
        error()
    end
    schedule = JSON.parse(Requests.text(results))
    Logging.info("Schedule Info Received")
    return schedule["data"]
end

function fetch_workers(task)
    path = string(
        "/api/v1/organizations/",
        task["organization_id"],
        "/locations/",
        task["location_id"],
        "/roles/",
        task["role_id"],
        "/users/",
    )
    results = Requests.get(build_uri(config, path))

    if results.status != 200
        code = results.status
        Logging.err("workers API down - code $code")
        error()
    end
    workers = JSON.parse(Requests.text(results))["data"]
    Logging.info( "workers Info Received")
    Logging.debug( "workers Info: $workers")
    for worker in workers
        if worker["name"] == nothing
            worker["name"] = worker["email"]
        end
    end
    return workers
end

function fetch_availability(task)
    path = string(
        "/api/v1/organizations/",
        task["organization_id"],
        "/locations/",
        task["location_id"],
        "/roles/",
        task["role_id"],
        "/schedules/",
        task["schedule_id"],
        "/availabilities/",
    )
    results = Requests.get(build_uri(config, path))

    if results.status != 200
        code = results.status
        Logging.err( "availabilities API down - code $code")
        error()
    end
    availabilities = JSON.parse(Requests.text(results))
    Logging.info( "availabilities Info Received")
    return availabilities["data"]
end

max_stars_repo_name: shdann/autoshift

max_issues_repo_name: shdann/autoshift

max_forks_repo_name: shdann/autoshift


text:
# This is only here to define a namespace
module JuliaTutorial

###############################################################################
# BASICS OF TYPES AND PACKAGES
###############################################################################
# This is how every packages that you will use work udner the hood.

# 1/ Use the function typeof() to get the type of anything you like

# 2/ Investigate the type hierarchy:

1 isa Real
1 isa Integer
Integer <: Real

# 3/ Multiple dispatch: Define a function to add 2 numbers

function myaddition(a, b)
    return
end

myaddition(1, 2)

function myaddition(a, b::AbstractFloat)
    return
end

myaddition(1, 2)

# 4/ Defining a new type and extending our function

struct Cocktail
    ingredients::Tuple
end

sexonthebeach = Cocktail(("vodka", "schnaps", "orange juice", "cranberry juice"))
tipunch = Cocktail(("rhum", "sugar", "lime juice"))

function myaddition(a::Cocktail, b::Cocktail)
    return ((a.ingredients..., b.ingredients...))
end

olivierssecret = myaddition(sexonthebeach, tipunch)

olivierssecret isa Cocktail
# Make your great function `myaddition` available to the public

export myaddition

# 5/ Bonus: move the methods into `functions.jl` and include the file in this module

# Let's move to the ML part --> `machinelearning.jl`

end


text:
using OmegaCore
using Test
using Spec

@testset begin
  x = (a = 3, b = 4, c = 12)
  x_ = OmegaCore.Util.rmkey(x, :a)
  @test :b ∈ x_
  @test :c ∈ x_
  @test :a ∉ x_
end


text:
LibCImPlot.ImPlotPoint() = LibCImPlot.ImPlotPoint(0,0)
LibCImPlot.ImPlotPoint(p::ImVec2) = LibCImPlot.ImPlotPoint(p.x, p.y)

LibCImPlot.ImPlotRange() = LibCImPlot.ImPlotRange(0,0)

LibCImPlot.ImPlotLimits() = LibCImPlot.ImPlotLimits(ImPlotRange(), ImPlotRange())


text:
function initialize_frequency_estimator!(
    device_states,
    device::PSY.DynamicInverter{C, O, IC, DC, PSY.KauraPLL, F},
) where {
    C <: PSY.Converter,
    O <: PSY.OuterControl,
    IC <: PSY.InnerControl,
    DC <: PSY.DCSource,
    F <: PSY.Filter,
}

    Vr_filter = get_inner_vars(device)[Vr_filter_var]
    Vi_filter = get_inner_vars(device)[Vi_filter_var]

    θ0_pll = angle(Vr_filter + Vi_filter * 1im)
    Vpll_d0 = Vr_filter
    Vpll_q0 = 0.0
    ϵ_pll0 = 0.0

    pll_ix = get_local_state_ix(device, PSY.KauraPLL)

    #Obtain indices for component w/r to device
    local_ix = get_local_state_ix(device, PSY.KauraPLL)

    #Update guess of PLL states
    pll_states = @view device_states[local_ix]
    pll_states[1] = Vpll_d0
    pll_states[2] = Vpll_q0
    pll_states[3] = ϵ_pll0
    pll_states[4] = θ0_pll

    #Update guess of frequency estimator
    get_inner_vars(device)[ω_freq_estimator_var] = PSY.get_ω_ref(device)
end


text:
struct Line{T} <: AbstractLinePrimitive{T}
    origin::CartesianPoint{T}
    direction::CartesianVector{T}
end

distance(pt::CartesianPoint, l::Line) = norm((pt - l.origin) × l.direction) / norm(l.direction)

function _transform_into_object_coordinate_system(l::Line{T}, p::AbstractPrimitive) where {T}
    origin = _transform_into_object_coordinate_system(l.origin, p)
    direction = inv(rotation(p)) * l.direction
    Line( origin, CartesianVector(direction) )
end


text:
### A Pluto.jl notebook ###
# v0.17.1

using Markdown
using InteractiveUtils

# ╔═╡ f019936e-8e7f-11ec-2c9e-3b68771ae51c
md"""
## The basics 2
We just learned how to use Bayes theorem to calculate the posterior probability distribution that allows us to make a quantitative assessment of our decision whether we should accept this gambling game or not.  Such decisions are constantly made in the financial industry and Bayesian methods are commonly used in this environment.  In the financial industry the goal is to maximize profit and to minimize losses, so it is imperative to use quantitative tools.  In science, we have the problem that scientists are not in the same situation.  For scientists it is more important to get more grants, to write more papers since this is what is seen as the deliverables of their efforts.  What I would like to teach you is that by following Bayes theorem, we can improve the reliability of our research without sacrificing academic productivity.  We need to know our data though.
"""

# ╔═╡ 88d6a70f-85aa-41a1-b159-ce7c16238e5c
md"""
# Some important properties of Bayes theorem

Before we continue, I would like to point out a few important properties of Bayes theorem and its application.  The first property has to do with the way the posterior is calculated.  Let us go back to the coin flipping experiment and assume that we did an experiment with the coin in question that initially involved N=10 coin flips.  We used our prior and by combining it with the Bionomial likelihood, we were able to calculate the posterior:

$$P(p \mid \alpha,\beta, N, k) =  \frac{\Gamma(\alpha + \beta +N)}{\Gamma(\alpha+k)\Gamma(\beta + N -k)}  p^{\alpha-1+k}(1-p)^{\beta-1+N-k}$$

Lets now say that we are not quite sure about what to do and we ask the Gambler to let us flip another 10 times.  What we would do is to take the posterior that we derived, use it as our prior and then again use the Bionomial distribution as likelihood.  When looking back at the equations, it is pretty obvious that it does not matter whether you analyze the data sequentially (flip 10 times, calculate the posterior, flip 10 more times, update the posterior) or analyze it in one shot (take result of 20 flips, use prior, calculate posterior).  This is an important property of Bayes theorem.  It should not matter how you do the analysis.  It would be perfectly fine to flip the coin and update the posterior each time.  The result should be the same.  This statement is pretty obvious in our case but it holds in general. Later in this class we will see examples that are much more complicated than simple coin flips and here it sometimes makes sense to perform a complicated analysis with parts of the data and then repeat the process, rather than take all the data at once.  Also, in science it often happens that you measure some data, and you want to see whether it makes sense to persue this direction further.  That is perfectly ok.  If your inital posterior looks promising then go ahead and do some more measurements.  Bayesian analysis allows you to improve your statistics by repeating the experiment.
"""

# ╔═╡ dbec33c5-8ae8-4c77-8adc-979259063dad
md"""
The next property has to do with the shape of the posterior.  We have seen last lecture that a typical outcome of your posterior can be described by the maximum of the distribution (or the 50th percentile) and its width.  In our coin flipping experiment, the maximum will tell you whether or not the gamble will be profitable for you and the width will tell you about the certainty or uncertainty that this will happen.  In the financial market you would be interested whether a stock is likely to go up or down, and what the volatility is.  So lets use our posterior to calculate these quantities.

The first thing that we want to do is to take the natural logarithm of the probability distribution.  This will not change the maximum of $P(x)$ since the transformation is monotonic.  This transformation is often using in computational approaches to probability because it turns multiplication into additions.  You will see another reason in the next paragraph.

$$logp(x) = \log(P(x | D)$$

Now we can do a Taylor expansion around $x=x_{0}$.  Since we are concerned about a peaked probability distribution we only have a constant term and a quadratic term.

$$logp(x) = logp(x_{0}) + \frac{1}{2}\frac{d^{2}logp(x)}{dx^{2}}\big | _{x_{0}}(x-x_{0})^{2}+\dots$$

Putting this back into the previous equation yields:

$$P(x|D) \propto A \exp\big[\frac{1}{2}\frac{d^{2}logp(x)}{dx^{2}}\big | _{x_{0}}(x-x_{0})^{2}\big]$$
"""

# ╔═╡ ec134312-fdcf-4ec4-990f-3156cd9d0095
md"""
If you look at this equation you will recognize that what we have just done is to approximate our pdf with a Gaussian distribution.  Gaussian distributions are very commonly found, and the reason for this is beyond this class, but suffice to say that in many cases posterior distributions tend to approach Gaussian distributions for large N (law of large numbers).  Lets look at a Gaussian distribution:

$$P(x \mid \mu, \sigma) = \frac{1}{{\sigma \sqrt {2\pi } }}e^{{{ - \left( {x - \mu }\right)^2 } /{2\sigma ^2 }}}$$
"""

# ╔═╡ 73af177b-3dd3-4785-af4d-3191ed96efde
md"""
The maximum of this distribution can be found by finding the root of the first derivative with respect to x, and the standard deviation can be found by taking the second derivative.  Here is the calculation.  You can check it if you want yourself.  In particuar,

$$ \sigma = (-\frac{d^{2}logp(x)}{dx^{2}}|_{x_{0}})^{-\frac{1}{2}}$$

Once we know $\mu$ and $\sigma$ we can report our posterior pdf as:

$$ x = x_{0} \pm \sigma$$

Since we approximated our pdf with a Gaussian we also know that true value of x lies in within $\pm \sigma$ $67\%$ of the time and within $\pm 2\sigma$ $95\%$ of the time.
"""

# ╔═╡ 4d018fd2-5971-48ba-896a-27170f138b82
md"""
Lets get back to our example posterior and see what happens

$$P(p \mid |N,k) \propto p^{k}(1-p)^{N-k}$$ with
$$logp = c + k\log(p)+(N-k)\log(1-p)$$

The derivatives are:

$$\frac{dlogp}{dp}=\frac{k}{p}-\frac{N-k}{1-p}$$
$$\frac{d^2 logp}{dp^2}=-\frac{k}{p^2}-\frac{N-k}{(1-p)^2}$$

The maximum can be found

$$\frac{dlogp}{dp}\big | _{p_{0}} = \frac{k}{p_{0}}-\frac{N-k}{1-p_{0}}=0$$
$$p_{0}=\frac{k}{N}$$

which is not surprising.  The second derivate is then:
$$\frac{d^2 logp}{dp^2}\big | _{p_{0}}=-\frac{N}{p_{0}(1-p_{0})}$$

$$\sigma = \sqrt{\frac{p_{0}(1-p_{0})}{N}}$$

For larger N, p_{0} will not change much and the numerator will be essentially constant.  In this regime, the standard deviation \mu is inversely proportional to the square root of the number of tries.
"""

# ╔═╡ Cell order:
# ╠═f019936e-8e7f-11ec-2c9e-3b68771ae51c
# ╠═88d6a70f-85aa-41a1-b159-ce7c16238e5c
# ╠═dbec33c5-8ae8-4c77-8adc-979259063dad
# ╠═ec134312-fdcf-4ec4-990f-3156cd9d0095
# ╠═73af177b-3dd3-4785-af4d-3191ed96efde
# ╠═4d018fd2-5971-48ba-896a-27170f138b82


text:
module JuliaInterpreter

using Base.Meta
import Base: +, -, convert, isless
using Core: CodeInfo, SSAValue, SlotNumber, TypeMapEntry, SimpleVector, LineInfoNode, GotoNode, Slot,
            GeneratedFunctionStub, MethodInstance, NewvarNode, TypeName

using UUIDs
# The following are for circumventing #28, memcpy invalid instruction error,
# in Base and stdlib
using Random.DSFMT
using InteractiveUtils
using CodeTracking

export @interpret, Compiled, Frame, root, leaf,
       BreakpointRef, breakpoint, @breakpoint, breakpoints, enable, disable, remove,
       debug_command, @bp, break_on, break_off

module CompiledCalls
# This module is for handling intrinsics that must be compiled (llvmcall)
end

const BUILTIN_FILE = joinpath(@__DIR__, "builtins-julia$(Int(VERSION.major)).$(Int(VERSION.minor)).jl")

@info "Generating builtins for this julia version..."
gen_builtins_file = joinpath(@__DIR__, "generate_builtins.jl")
# Run as separate command to prevent including the generate_builtins into the precompile cache
run(`$(Base.julia_cmd()) --startup-file=no $gen_builtins_file`)
include_dependency(gen_builtins_file)

include("types.jl")
include("utils.jl")
include("construct.jl")
include("localmethtable.jl")
include("interpret.jl")
include(BUILTIN_FILE)
include("optimize.jl")
include("commands.jl")
include("breakpoints.jl")

function set_compiled_methods()
    # Work around #28 by preventing interpretation of all Base methods that have a ccall to memcpy
    push!(compiled_methods, which(vcat, (Vector,)))
    push!(compiled_methods, first(methods(Base._getindex_ra)))
    push!(compiled_methods, first(methods(Base._setindex_ra!)))
    push!(compiled_methods, which(Base.decompose, (BigFloat,)))
    push!(compiled_methods, which(DSFMT.dsfmt_jump, (DSFMT.DSFMT_state, DSFMT.GF2X)))
    if Sys.iswindows()
        push!(compiled_methods, which(InteractiveUtils.clipboard, (AbstractString,)))
    end
    # issue #76
    push!(compiled_methods, which(unsafe_store!, (Ptr{Any}, Any, Int)))
    push!(compiled_methods, which(unsafe_store!, (Ptr, Any, Int)))
    # issue #92
    push!(compiled_methods, which(objectid, Tuple{Any}))
    # issue #106 --- anything that uses sigatomic_(begin|end)
    push!(compiled_methods, which(flush, Tuple{IOStream}))
    push!(compiled_methods, which(disable_sigint, Tuple{Function}))
    push!(compiled_methods, which(reenable_sigint, Tuple{Function}))
    # Signal-handling in the `print` dispatch hierarchy
    push!(compiled_methods, which(Base.unsafe_write, Tuple{Base.LibuvStream, Ptr{UInt8}, UInt}))
    # Libc.GetLastError()
    @static if Sys.iswindows()
        push!(compiled_methods, which(Base.access_env, Tuple{Function, AbstractString}))
        push!(compiled_methods, which(Base._hasenv, Tuple{Vector{UInt16}}))
    end
end

function __init__()
    set_compiled_methods()
end

include("precompile.jl")
_precompile_()

end # module


text:
"""
    plsnipals(X, Y, weights = ones(size(X, 1)); nlv)
Partial Least Squares Regression (PLSR) with the NIPALS algorithm
(e.g. Tenenhaus 1998, Wold 2002).
* `X` : X-data.
* `Y` : Y-data.
* `weights` : Weights of the observations.
* `nlv` : Nb. latent variables (LVs) to consider.

In the function, the usual PLS2 NIPALS iterations are replaced by a
direct computation of the PLS weights (`w`) by SVD decomposition of matrix X'Y
(Hoskuldsson 1988 p.213).

Vector `weights` (row-weighting) is internally normalized to sum to 1.
See the help of `plskern` for details.

`X` and `Y` are internally centered.

## References

Hoskuldsson, A., 1988. PLS regression methods. Journal of Chemometrics 2, 211-228.
https://doi.org/10.1002/cem.1180020306

Tenenhaus, M., 1998. La régression PLS: thÃ©orie et pratique. Editions Technip,
Paris, France.

Wold, S., Sjostrom, M., Eriksson, l., 2001. PLS-regression: a basic tool
for chemometrics. Chem. Int. Lab. Syst., 58, 109-130.
"""
function plsnipals(X, Y, weights = ones(size(X, 1)); nlv)
    plsnipals!(copy(X), copy(Y), weights; nlv = nlv)
end

function plsnipals!(X, Y, weights = ones(size(X, 1)); nlv)
    X = ensure_mat(X)
    Y = ensure_mat(Y)
    n = size(X, 1)
    p = size(X, 2)
    q = size(Y, 2)
    nlv = min(nlv, n, p)
    weights = mweights(weights)
    D = Diagonal(weights)
    xmeans = colmeans(X, weights)
    ymeans = colmeans(Y, weights)
    center!(X, xmeans)
    center!(Y, ymeans)
    # Pre-allocation
    XtY = similar(X, p, q)
    T = similar(X, n, nlv)
    P = similar(X, p, nlv)
    W = copy(P)
    C = similar(X, q, nlv)
    TT = similar(X, nlv)
    t   = similar(X, n)
    dt  = similar(X, n)
    zp  = similar(X, p)
    w   = similar(X, p)
    c   = similar(X, q)
    # End
    @inbounds for a = 1:nlv
        XtY .= X' * (D * Y)
        if q == 1
            w .= vec(XtY)
            w ./= sqrt(dot(w, w))
        else
            w .= svd!(XtY).U[:, 1]
        end
        mul!(t, X, w)
        dt .= weights .* t
        tt = dot(t, dt)
        mul!(c, Y', dt)
        c ./= tt
        mul!(zp, X', dt)
        zp ./= tt
        X .-= t * zp'
        Y .-= t * c'
        P[:, a] .= zp
        T[:, a] .= t
        W[:, a] .= w
        C[:, a] .= c
        TT[a] = tt
     end
     R = W * inv(P' * W)
     Plsr(T, P, R, W, C, TT, xmeans, ymeans, weights, nothing)
end


text:
export FSimGate

# https://arxiv.org/pdf/1711.04789.pdf
# Google supremacy paper
mutable struct FSimGate{T<:Number} <: PrimitiveBlock{2}
    theta::T
    phi::T
end

function Base.:(==)(fs1::FSimGate, fs2::FSimGate)
    return fs1.theta == fs2.theta && fs1.phi == fs2.phi
end

function YaoBlocks.mat(::Type{T}, fs::FSimGate) where T
    θ, ϕ = fs.theta, fs.phi
    T[1 0          0          0;
     0 cos(θ)     -im*sin(θ) 0;
     0 -im*sin(θ) cos(θ)     0;
     0 0          0          exp(-im*ϕ)]
end

YaoBlocks.iparams_eltype(::FSimGate{T}) where T = T
YaoBlocks.getiparams(fs::FSimGate{T}) where T = (fs.theta, fs.phi)
function YaoBlocks.setiparams!(fs::FSimGate{T}, θ, ϕ) where T
    fs.theta = θ
    fs.phi = ϕ
    return fs
end


text:
abstract type AbstractReadOnlyDict{K,V,P} <: AbstractDict{K,V} end

Base.parent(x::AbstractReadOnlyDict) = getfield(x, :parent)

isfrozenvalue(::Type{<:AbstractReadOnlyDict}) = true
isfrozenindex(::Type{<:AbstractReadOnlyDict}) = true

meltvalue!(x::AbstractReadOnlyDict) = parent(x)
meltindex!(x::AbstractReadOnlyDict) = parent(x)

# Workaround: `Base.copymutable(::AbstractDict)` returns an array
ownmutablevalue(x::AbstractDict) = Dict(x)
ownmutableindex(x::AbstractDict) = Dict(x)

Base.IteratorSize(::Type{<:AbstractReadOnlyDict{K,V,P}}) where {K,V,P} =
    Base.IteratorSize(P)
Base.IteratorEltype(::Type{<:AbstractReadOnlyDict{K,V,P}}) where {K,V,P} =
    Base.IteratorEltype(P)
Base.eltype(::Type{<:AbstractReadOnlyDict{K,V,P}}) where {K,V,P} = eltype(P)
Base.@propagate_inbounds Base.getindex(x::AbstractReadOnlyDict, I...) =
    getindex(parent(x), I...)
Base.iterate(x::AbstractReadOnlyDict, args...) = iterate(parent(x), args...)
Base.length(x::AbstractReadOnlyDict) = length(parent(x))
Base.get(x::AbstractReadOnlyDict, k, default) = get(parent(x), k, default)

Base.copy(x::AbstractReadOnlyDict) = copy(parent(x))
Base.copymutable(x::AbstractReadOnlyDict) = Base.copymutable(parent(x))

Base.getproperty(x::AbstractReadOnlyDict, name::Symbol) =
    constructorof(typeof(x))(getproperty(parent(x), name))
Base.getproperty(x::AbstractReadOnlyDict, field) =
    constructorof(typeof(x))(getproperty(parent(x), field))

Base.broadcastable(x::AbstractReadOnlyDict) = Base.broadcastable(parent(x))

struct ReadOnlyDict{K,V,P<:AbstractDict{K,V}} <: AbstractReadOnlyDict{K,V,P}
    parent::P
end

isimmutablevalue(::Type{<:ReadOnlyDict}) = true
isimmutableindex(::Type{<:ReadOnlyDict}) = true
isfrozenvalue(::Type{<:ReadOnlyDict}) = false
isfrozenindex(::Type{<:ReadOnlyDict}) = false
readonly_impl(x::AbstractDict) = ReadOnlyDict(x)

struct ImmutableDict{K,V,P<:AbstractDict{K,V}} <: AbstractReadOnlyDict{K,V,P}
    parent::P
end

freeze!(x::AbstractDict) = ImmutableDict(x)

struct ImmutableIndexDict{K,V,P<:AbstractDict{K,V}} <: AbstractReadOnlyDict{K,V,P}
    parent::P
end

isfrozenvalue(::Type{<:ImmutableIndexDict}) = false
function Base.setindex!(x::ImmutableIndexDict, v, k)
    haskey(x, k) || throw(KeyError(k))
    setindex!(parent(x), v, k)
    return x
end

freezeindex!(x::AbstractDict) = ImmutableIndexDict(x)

struct AppendOnlyDict{K,V,P<:AbstractDict{K,V}} <: AbstractReadOnlyDict{K,V,P}
    parent::P
end

isfrozenindex(::Type{<:AppendOnlyDict}) = false
function Base.setindex!(x::AppendOnlyDict, v, k)
    haskey(x, k) && throw(ArgumentError("key already exist"))
    setindex!(parent(x), v, k)
    return x
end

freezevalue!(x::AbstractDict) = AppendOnlyDict(x)

factoryof(::ReadOnlyDict) = readonly
factoryof(::ImmutableDict) = freeze
factoryof(::ImmutableIndexDict) = freezeindex
factoryof(::AppendOnlyDict) = freezevalue

function Base.showarg(io::IO, x::AbstractReadOnlyDict, toplevel::Bool)
    print(io, nameof(factoryof(x)), '(')
    Base.showarg(io, parent(x), false)
    print(io, ')')
end


text:
"""
    rref(A::AbstractMatrix{T}) where {T<:Interval}

Computes the reduced row echelon form of the interval matrix `A` using maximum
mignitude as pivoting strategy.

### Examples

```jldoctest
julia> A = [2..4 -1..1; -1..1 2..4]
2×2 Matrix{Interval{Float64}}:
  [2, 4]  [-1, 1]
 [-1, 1]   [2, 4]

julia> rref(A)
2×2 Matrix{Interval{Float64}}:
 [2, 4]  [-1, 1]
 [0, 0]       [1.5, 4.5]
```
"""
function rref(A::AbstractMatrix{T}) where {T<:Interval}
    A1 = copy(A)
    return rref!(A1)
end

"""
    rref!(A::AbstractMatrix{T}) where {T<:Interval}

In-place version of [`rref`](@ref).
"""
function rref!(A::AbstractMatrix{T}) where {T<:Interval}
    m, n = size(A)
    minmn = min(m,n)
    @inbounds for k = 1:minmn

        if k < m
            # find maximum index
            migmax, kp = _findmax_mig(view(A, k:m, k))
            iszero(migmax) && throw(ArgumentError("Could not find a pivot with non-zero mignitude in column $k."))
            kp += k - 1

            # Swap rows k and kp if needed
            k != kp && _swap!(A, k, kp)
        end

        # Scale first column
        _scale!(A, k)

        # Update the rest
        _eliminate!(A, k)
    end
    return A
end

@inline function _findmax_mig(v)

    @inbounds begin
        migmax = mig(first(v))
        kp = firstindex(v)

        for (i, vi) in enumerate(v)
            migi = mig(vi)
            if migi > migmax
                kp = i
                migmax = migi
            end
        end
    end
    return migmax, kp
end

@inline function _swap!(A, k, kp)
    @inbounds for i = 1:size(A, 2)
        tmp = A[k,i]
        A[k,i] = A[kp,i]
        A[kp,i] = tmp
    end
end

@inline function _scale!(A, k)
    @inbounds begin
        Akkinv = inv(A[k,k])
        for i = k+1:size(A, 1)
            A[i,k] *= Akkinv
        end
    end
end

@inline function _eliminate!(A, k)
    m, n = size(A)
    @inbounds begin
        for j = k+1:n
            for i = k+1:m
                A[i,j] -= A[i,k]*A[k,j]
            end
        end

        for i = k+1:m
            A[i, k] = zero(eltype(A))
        end
    end
end


text:
using Neighborhood
using DelayEmbeddings
using LinearAlgebra # for eigenvalues
using StaticArrays

export ManifoldProjection

# TODO: Would be nice to be able to generalize this to have `τ` as delay time
# instead of forcing `τ=1`. But then again, maybe the algorithm *requires* τ=1,
# similarly with how Broomhead-King needs smallest delay time.

"""
    ManifoldProjection(m, Q, k, τ=1, w=0, r=1000.0) <: Decomposition
A nonlinear noise reduction method, also known as "locally linear projections", which
works by bringing a noisy signal closer to a multi-dimensional manifold that represents
the deterministic dynamics of the signal. The method is method "IV" of [^Grassberger1993].

`m::Int` is the same as in [^Grassberger1993], the embedding dimension.
`Q` is related with the *expected* dimension `d` of the manifold of the deterministic
with approximate relation `d ≈ m-Q+1`. However, it is best t consider `Q` as an input
parameter (the dimension of the set you project locally into) and you need to vary
it to achieve the best performance.
If given a `Vector{Int}` as `Q` the algorithm will iteratively do noise reduction
to the resulting outputs (thus a vector is strongly recommended).
Duplicate entries can exist in `Q`.

`k` can be either `Int` or a `SearchType` from [Neighborhood.jl](https://julianeighbors.github.io/Neighborhood.jl/stable/#Search-types-1).
If `Int`, the `k` nearest neighbors are choosen as the neighborhood 𝓤 of each point.
The paper contains an involved process for determining optimal `k::Int`, see eq.(5.4).
`w` is just the Theiler window, while `r` is the value of the edge entries of vector R,
and probably has not much impact (the rest of the entries are 1).

In the paper too big correction vectors were rescaled to the average magnitude of corrections,
using as a criterion the distribution of their size. This is not implemented here (as it
is not clear *exactly* what it means computationally, what is "too big"?)
Contributing it is welcomed if you know how...

See also [^Schreiber1996] for an application of the same algorithm in real ECG data.

[^Schreiber1996]: Schreiber & Kaplan (1996). Nonlinear noise reduction for electrocardiograms. [Chaos, 6(1), 87–92](https://doi.org/10.1063/1.166148)

[^Grassberger1993]: Grassberger et al., (1993). On noise reduction methods for chaotic data. [Chaos 3(2), 127–141](https://doi.org/10.1063/1.165979)
"""
struct ManifoldProjection{ST <: SearchType} <: Decomposition
    m::Int
    Qs::Vector{Int}
    st::ST
    w::Int
    r::Float64
end
ManifoldProjection(m, Q::Int, args...) = ManifoldProjection(m, [Q], args...)
ManifoldProjection(m, Q, k::Int, args...) = ManifoldProjection(m, Q, NeighborNumber(k), args...)
ManifoldProjection(m, Q, st::ST, w = 0, r = 1000.0) where {ST<:SearchType} =
ManifoldProjection{ST}(m, Q, st, w, r)

function SignalDecomposition.decompose(t, s, method::ManifoldProjection)
    m = method.m
    @assert maximum(method.Qs) < m
    x = copy(s) # x is "input timeseries", iteratively corrected
    ge = GeneralizedEmbedding(tuple(0:m...))

    # Function barrier to make `m` type parameter:
    Vc = genembed(x, ge)   # corrected embedded set
    iteratively_project!(x, method, ge, Vc)
    return x, s .- x
end

function iteratively_project!(x::AbstractVector{T}, method, ge::GeneralizedEmbedding{D}, Vc) where {T, D}
    m = D-1
    # Initialize a bunch of stuff
    r = copy(x)
    R = ones(eltype(x), m+1)
    R[1] = R[end] = method.r # something "large"
    Γ = C = zeros(eltype(x), m+1, m+1)
    𝒟 = copy(C)
    ξ = zeros(eltype(x), m+1)
    θ = zeros(eltype(x), m+1)

    # Main algorithm loop, repeated for Qs
    for Q in method.Qs
        V = genembed(x, ge) # embedded set, which is "bold x" in paper
        tree = searchstructure(KDTree, V.data, Euclidean())
        𝒰 = bulkisearch(tree, V.data, method.st, Theiler(method.w); sortds = false)

        @inbounds for n in 1:length(V) # start process for every point in the embedded space
            𝒰n = 𝒰[n]
            K = length(𝒰n)
            iK = 1/K

            for i in 1:m+1; ξ[i] = iK*sum(x[k+i-1] for k in 𝒰n); end

            for j in 1:m+1, i in 1:m+1
                ξij = ξ[i]*ξ[j]
                C[i, j] = @fastmath iK*sum(x[k+i-1]*x[k+j-1] - ξij for k in 𝒰n)
                Γ[i, j] = @fastmath R[i]*C[i, j]*R[j] # make Γ
            end
            eig::Matrix{T} = eigvecs(Γ) # these are sorted by eigenvalue. Important for the algorithm.
            for j in 1:m+1, i in 1:m+1
                𝒟[i, j] = sum(eig[i, q]*eig[j, q] for q in 1:Q)
            end

            # corrections vectors:
            for i in 1:m+1
                θ[i] = (1/R[i]) * sum(𝒟[i, j] * R[j] * (ξ[j] - x[n+j-1]) for j in 1:m+1)
            end

            Vc.data[n] = V.data[n] + SVector{D, T}(θ)
        end
        # okay, now use corrected points to estimate new points, by taking the average.
        @inbounds for n in (m+1):length(V)-m
            r[n] = 1/(m+1)*sum(Vc[n-j, j+1] for j in 0:m)
        end
        # Edge cases
        @inbounds for n in 1:m
            r[n] = sum(Vc[n-j+1, j] for j in 1:n)/n
        end
        @inbounds for (z, n) in enumerate((length(V)-m):-1:(length(V)-2m))
            r[n] = sum(Vc[n-j, m+1-j] for j in 0:(z-1))/z
        end
        x .= r # set corrected vector to input vector.
    end
end


text:
using Distributed
import Base.Threads.@spawn

#=
@author Alvaro Lucas Castillo Calabacero
contact alvarolucascc96@gmail.com
=#

# function to sort array with mergesort algorithm
function mergesort_parallel!(array, start::Int=1, finish::Int=length(array))
    # if start >= finish, nothing to do
    if start >= finish
        return array
    end
    # support 100000 numbers, and apply the MergeSort
    #this algorithm is provide for Julialang
    if finish - start < 100000
        sort!(view(array, start:finish), alg = MergeSort)
        return array
    end

    pivot = div(finish+start,2)
    #Resolve first part of array
    semiArray = @spawn mergesort_parallel!(array, start, pivot)

    #wait to finish ordering the previous half
    wait(semiArray)
    temp = array[lo:mid]
    i =1
    k =1
    j=1
    #start= pivot+1

    #merge two parts with a temporal array
    @inbounds while k < j <= finish

        if array[j] < temp[i]
            array[k] = array[j]
            j += 1
        else
            array[k] = temp[i]
            i += 1
        end
        k += 1
    end
    @inbounds while k < j
        array[k] = temp[i]
        k += 1
        i += 1
    end
    return array
end

totalNumbers = 100000;
list = [rem(rand(Int64),100) for i =1:totalNumbers]
#JULIA_NUM_THREADS = 5
#@time print(mergesort_parallel!(list));
@time mergesort_parallel!(list);
#@time @spawn psort!(vectorDisorderly);


text:
export visualize

using DifferentiableNAS
using Flux
using CUDA
using LightGraphs
using GraphPlot
using Cairo
using Compose
using BSON
using LaTeXStrings
include("training_utils.jl")

function visualize(αs, filename)
    inputindices, _, opnames = discretize(αs, 1, false, 4)
    g = SimpleDiGraph(7)
    nodelabels = [latexstring("c_{k-2}"), latexstring("c_{k-1}"), "1", "2", "3", "4", latexstring("c_{k}")]
    locs_x = [-1.5,-1.5,1,2,3,4,6]
    locs_y = [4.5,3,3.5,2,4.5,3,2]
    nodefillcs = ["lightgreen", "lightgreen", "lightblue", "lightblue", "lightblue", "lightblue", "lightyellow"]
    edgelabels = []
    for i in 1:length(inputindices)
        add_edge!(g, inputindices[i][1], i+2)
        push!(edgelabels, (opnames[i][1], inputindices[i][1], i+2))
        add_edge!(g, inputindices[i][2], i+2)
        push!(edgelabels, (opnames[i][2], inputindices[i][2], i+2))
    end
    for i in 3:6
        add_edge!(g, i, 7)
        push!(edgelabels,  ("", i, 7))
    end
    sort!(edgelabels, by = x -> (x[2],x[3]))
    edgelabels = [e[1] for e in edgelabels]
    collect(zip(edgelabels, edges(g)))
    gp = gplot(g, locs_x, locs_y, edgelabel=edgelabels, nodelabel=nodelabels, nodefillc=nodefillcs, NODELABELSIZE=3.0, edgelabeldisty = -0.5)
    Compose.draw(PNG(filename), gp)
end
function load_vis()
    #BSON.@load "test/models/alphas_only.bson" normal_ reduce_
    #visualize(normal_, "test/models/normal.png")
    #visualize(reduce_, "test/models/reduce.png")

    folder_name = "test/models/osirim/masked_2021-01-08T09:57:42.725"
    file_name = string(folder_name, "/alphas.bson")
    BSON.@load file_name normal reduce
    visualize(normal, string(folder_name, "/normal.png"))
    visualize(reduce, string(folder_name, "/reduce.png"))
end
#load_vis()

max_stars_repo_name: huangyxi/DifferentiableNAS

max_issues_repo_name: huangyxi/DifferentiableNAS

max_forks_repo_name: huangyxi/DifferentiableNAS


text:
using SharedMATLABEngine
using Test

@testset "Did it import?" begin
    @test true
end


text:
module TPI
    using UnsafeArrays

    include("barrier.jl")

    include("communicator.jl")
    include("collective.jl")
    include("point_toall.jl")
end


text:
# Copyright (c) 2017: Miles Lubin and contributors
# Copyright (c) 2017: Google Inc.
#
# Use of this source code is governed by an MIT-style license that can be found
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.

module TestConstraints

using Test

using MathOptInterface
const MOI = MathOptInterface

function runtests()
    for name in names(@__MODULE__; all = true)
        if startswith("$name", "test_")
            @testset "$(name)" begin
                getfield(@__MODULE__, name)()
            end
        end
    end
end

function _constant_not_zero_test(::Type{T}) where {T}
    S = MOI.EqualTo{T}
    x = MOI.VariableIndex(1)
    @test nothing === MOI.throw_if_scalar_and_constant_not_zero(x, S)
    func1 = one(T) * x + one(T)
    @test_throws MOI.ScalarFunctionConstantNotZero begin
        MOI.throw_if_scalar_and_constant_not_zero(func1, S)
    end
    func2 = one(T) * x
    @test nothing === MOI.throw_if_scalar_and_constant_not_zero(func2, S)
    func = MOI.Utilities.operate(vcat, T, func1, func2)
    @test nothing === MOI.throw_if_scalar_and_constant_not_zero(func, MOI.Zeros)
end

function test_constraints_ConstantNotZero()
    _constant_not_zero_test(Int)
    _constant_not_zero_test(Float64)
    return
end

function test_LowerBoundAlreadySet_error()
    x = MOI.VariableIndex(1)
    S1 = MOI.LessThan{Int}
    S2 = MOI.Interval{Int}
    err = MOI.LowerBoundAlreadySet{S1,S2}(x)
    @test sprint(showerror, err) ==
          "$(typeof(err)): Cannot add `VariableIndex`-in-`$(S2)` constraint " *
          "for variable $(x) as a `VariableIndex`-in-`$(S1)` constraint was " *
          "already set for this variable and both constraints set a lower bound."
    return
end

function test_UpperBoundAlreadySet_error()
    x = MOI.VariableIndex(1)
    S1 = MOI.GreaterThan{Int}
    S2 = MOI.Interval{Int}
    err = MOI.UpperBoundAlreadySet{S1,S2}(x)
    @test sprint(showerror, err) ==
          "$(typeof(err)): Cannot add `VariableIndex`-in-`$(S2)` constraint " *
          "for variable $(x) as a `VariableIndex`-in-`$(S1)` constraint was " *
          "already set for this variable and both constraints set an upper bound."
    return
end

function test_ScalarFunctionConstantNotZero_error()
    x = MOI.VariableIndex(1)
    T = Int
    F = MOI.ScalarAffineFunction{T}
    S = MOI.EqualTo{T}
    err = MOI.ScalarFunctionConstantNotZero{T,F,S}(one(T))
    @test sprint(showerror, err) ==
          "In `$F`-in-`$S` constraint: Constant $(one(T)) of the function is " *
          "not zero. The function constant should be moved to the set. You can " *
          "use `MOI.Utilities.normalize_and_add_constraint` which does this " *
          "automatically."
    return
end

end

TestConstraints.runtests()

max_stars_repo_name: JuliaOpt/MathOptInterface

max_issues_repo_name: JuliaOpt/MathOptInterface

max_forks_repo_name: JuliaOpt/MathOptInterface


text:
using DelayDiffEq, DiffEqProblemLibrary.DDEProblemLibrary
using Test

DDEProblemLibrary.importddeproblems()

const prob_ip = DDEProblemLibrary.prob_dde_constant_1delay_ip
const prob_scalar = DDEProblemLibrary.prob_dde_constant_1delay_scalar
const ts = 0:0.1:10

# ODE algorithms
const working_algs = [ImplicitMidpoint(), SSPSDIRK2(), KenCarp5()]

const broken_algs = [ImplicitEuler(), Trapezoid(),
                     TRBDF2(), SDIRK2(),
                     Kvaerno3(), KenCarp3(),
                     Cash4(), Hairer4(), Hairer42(), Kvaerno4(), KenCarp4(),
                     Kvaerno5()]

@testset "Algorithm $(nameof(typeof(alg)))" for alg in working_algs
  println(nameof(typeof(alg)))

  stepsalg = MethodOfSteps(alg)
  sol_ip = solve(prob_ip, stepsalg; dt = 0.1)
  sol_scalar = solve(prob_scalar, stepsalg; dt = 0.1)

  @test sol_ip(ts, idxs=1) ≈ sol_scalar(ts)
  @test sol_ip.t ≈ sol_scalar.t
  @test sol_ip[1, :] ≈ sol_scalar.u
end

@testset "Algorithm $(nameof(typeof(alg)))" for alg in broken_algs
  println(nameof(typeof(alg)))

  stepsalg = MethodOfSteps(alg)
  sol_ip = solve(prob_ip, stepsalg; dt = 0.1)
  sol_scalar = solve(prob_scalar, stepsalg; dt = 0.1)

  @test_broken sol_ip(ts, idxs=1) ≈ sol_scalar(ts)
  # this test is not broken for KenCarp4
  if alg isa KenCarp4
    @test sol_ip.t ≈ sol_scalar.t
  else
    @test_broken sol_ip.t ≈ sol_scalar.t
  end
  @test_broken sol_ip[1, :] ≈ sol_scalar.u
end


text:
# :PySlice_New => (PyPtr, PyPtr, PyPtr) => PyPtr,

"""
    pyslice([start], stop, [step])

Construct a Python `slice`. Unspecified arguments default to `None`.
"""
pyslice(x, y, z=pybuiltins.None) = pynew(errcheck(@autopy x y z C.PySlice_New(getptr(x_), getptr(y_), getptr(z_))))
pyslice(y) = pyslice(pybuiltins.None, y, pybuiltins.None)
export pyslice

pyisslice(x) = pytypecheck(x, pybuiltins.slice)


text:
# Note that this script can accept some limited command-line arguments, run
# `julia build_tarballs.jl --help` to see a usage message.
using BinaryBuilder, Pkg

name = "lrslib"
version = v"0.3.3"

# Collection of sources required to complete build
sources = [
    ArchiveSource("http://cgm.cs.mcgill.ca/~avis/C/lrslib/archive/lrslib-071b.tar.gz",
                  "df22682cd742315fe04f866cfe4804d5950f7dc7f514d5b5f36f5b7f5aff9188"),
]

# Bash recipe for building across all platforms
script = raw"""
cd $WORKSPACE/srcdir/lrslib*
extraargs=""
cflags="-fPIC -O3 -Wall -DLRS_QUIET"

# 32bit linux, arm and windows:
if [[ $target == i686* ]] || [[ $target == arm* ]]; then
  # no 128 bit support needs extra flags
  extraargs="BITS=-DB32 MPLRSOBJ2= SHLIBOBJ2= "
fi

if [[ $target == *apple* ]]; then
  export CC=gcc
  sed -i -e 's#-Wl,-soname=#-install_name #' makefile
  extraargs="SONAME=liblrs.0.dylib SHLINK=liblrs.dylib SHLIB=liblrs.0.0.0.dylib ${extraargs}"
elif [[ $target == *freebsd* ]]; then
  export CC="gcc"
elif [[ $target == *mingw* ]]; then
  extraargs="SONAME=liblrs-0.dll SHLINK=liblrs.dll SHLIB=liblrs-0-0-0.dll ${extraargs}"
  cflags="$cflags -DSIGNALS -DTIMES"
fi

make prefix=${prefix} \
     INCLUDEDIR=${includedir} \
     LIBDIR=${libdir} \
     CFLAGS="${cflags}" \
     ${extraargs} \
     -j ${nproc} \
   install

if [[ $target == *mingw* ]]; then
  # rename binaries and move libraries
  for file in ${bindir}/{lrs,lrsnash}; do mv $file $file.exe; done
  mv ${prefix}/lib/*lrs*.dll ${libdir}/
fi

${CC} -shared ${cflags} -o "${libdir}/liblrsnash.${dlext}" lrsnashlib.c -L${libdir} -llrs -lgmp -Wl,-rpath,${libdir} -DMA -DGMP -DLRS_QUIET -I${includedir}
"""

# These are the platforms we will build for by default, unless further
# platforms are passed in on the command line
platforms = supported_platforms(; experimental=true)

# The products that we will ensure are always built
products = [
    ExecutableProduct("lrs", :lrs)
    ExecutableProduct("lrsnash", :lrsnash)
    LibraryProduct("liblrs", :liblrs)
    LibraryProduct("liblrsnash", :liblrsnash)
]

# Dependencies that must be installed before this package can be built
dependencies = [
    Dependency("GMP_jll", v"6.2.0"),
]

# Build the tarballs, and possibly a `build.jl` as well.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat = "1.6")

max_stars_repo_name: sharanry/Yggdrasil

max_issues_repo_name: sharanry/Yggdrasil

max_forks_repo_name: sharanry/Yggdrasil


text:
eval(quote
    using .Expokit: expmv
end)

eval(load_expokit_sparsematrixexp())
eval(load_expokit_exponentialmap())
eval(load_expokit_exponentialprojectionmap())


text:
struct Record <: Object
    f::Any
    args::Any
    kwargs::Any
    isdo::Bool
    metadata::Metadata
end

(re::Record)() = _call(re)

"""
    Recalls.CALLS

This is a vector of records created by [`@recall`](@ref).
"""
const CALLS = Record[]

function _record(metadata, f, args...; kwargs...)
    @nospecialize
    push!(CALLS, Record(f, args, kwargs, false, metadata))
    return
end

recorder(f, isdo, metadata) = function _recorder_(args...; kwargs...)
    push!(CALLS, Record(f, args, kwargs, isdo, metadata))
    return f(args...; kwargs...)
end

"""
    recall()

Re-run the last call to function `f` instrumented by `@recall f(...)`.

This is a shortcut of `Recalls.CALLS[end]()`.
"""
recall() =
    _call(CALLS[end]) # NOTE: Hit `c` (continue) to jump to the point just before the actual call, if you are in a debugger.

#=
macro recall()
    :(recall())
end
=#

as_kwarg(x::Symbol) = x
as_kwarg(ex::Expr) =
    if isexpr(ex, :kw) || isexpr(ex, :(::))
        as_kwarg(ex.args[1])
    elseif isexpr(ex, :(...))
        ex
    else
        error("Unsupported keyword argument syntax: ", ex)
    end

"""
    @recall function f(...) ... end
    @recall f(...)
    @recall f(...) do ... end

Record history of function calls.

See also: [`Recalls`](@ref), [`recall`](@ref).
"""
macro recall(ex)
    metadata = metadata_expr(__source__, __module__)
    if isexpr(ex, :call)
        return esc(Expr(ex.head, :($recorder($(ex.args[1]), false, $metadata)), ex.args[2:end]...))
    elseif isexpr(ex, :do)
        ex.args[1].args[1] = :($recorder($(ex.args[1].args[1]), true, $metadata))
        return esc(ex)
    elseif (def = splitdef(ex)) !== nothing
        f = get(def, :name, nothing)
        f === nothing && error("Cannot record an anonymous function:\n", ex)
        args = def[:args]
        kwargs = map(as_kwarg, get(def, :kwargs, []))
        def[:body] = Expr(:block, quote
            $_record($metadata, $f, $(args...); $(kwargs...))
        end, def[:body])
        return esc(combinedef(def))
    end
    error("Not call or function definition:\n", ex)
end

function _summary(io, re::Record)
    print(io, "@recall ", re.f, "(")
    if get(io, :compact, false)
        if isempty(re.args)
            if !isempty(re.kwargs)
                k, v = first(re.kwargs)
                print(io, "; ", k, "=")
                print(io, v)
                print(io, ", ...")
            end
        elseif length(re.args) == 1
            print(io, re.args[1])
            if !isempty(re.kwargs)
                print(io, "; ...")
            end
        else
            print(io, re.args[1])
            print(io, ", ...")
        end
    else
        join(io, re.args, ", ")
        if !isempty(re.kwargs)
            isfirst = true
            for (k, v) in pairs(re.kwargs)
                if isfirst
                    print(io, "; ")
                    isfirst = true
                else
                    print(io, ", ")
                end
                print(io, k, "=", v)
            end
        end
    end
    print(io, ") at ")
    _summary(io, re.metadata)
end

function Base.show(io::IO, ::MIME"text/plain", re::Record)
    _summary(IOContext(io, :compact => true), re)
    get(io, :compact, false) && return
    if !isempty(re.args)
        println(io)
        print(io, length(re.args), " argument(s):")
        for (i, a) in enumerate(re.args)
            println(io)
            print(io, "  #", i, " = ")
            print(io, a)
        end
    end
    if !isempty(re.kwargs)
        println(io)
        print(io, length(re.kwargs), " keyword argument(s):")
        for (k, v) in pairs(re.kwargs)
            println(io)
            print(io, "  ", k, " = ")
            print(io, v)
        end
    end
end


text:
module Quiqbox

include("AbstractTypes.jl")

include("Tools.jl")
include("FileIO.jl")

include("../lib/libcint/LibcintFunctions.jl")
include("../lib/libcint/Interface.jl")
include("../lib/BasisSets/BasisSets.jl")

include("Library.jl")
include("HartreeFock.jl")
include("Differentiation.jl")
include("Basis.jl")
include("Box.jl")
include("Optimization.jl")
include("Molecule.jl")
include("Overload.jl")
include("math.jl") # In early development.

include("Integration/DataStructure.jl")
include("Integration/OneBody.jl")
include("Integration/TwoBody.jl")

include("Initialization.jl")
end


text:
### A Pluto.jl notebook ###
# v0.18.0

using Markdown
using InteractiveUtils

# ╔═╡ 8399ed34-7dc4-11eb-16ec-e572834e149d
# ╠═╡ disabled = true
#=╠═╡
x = 7
  ╠═╡ =#

# ╔═╡ 88c83d42-7dc4-11eb-1b5e-a1ecad4b6ff1
# ╠═╡ disabled = true
#=╠═╡
x
  ╠═╡ =#

# ╔═╡ 48f7cf80-c1f6-4002-8639-a0addb8e6780
#=╠═╡
x + 1
  ╠═╡ =#

# ╔═╡ 18f57a3f-3b1f-4346-9686-752438f6067f
z = 14

# ╔═╡ 00000000-0000-0000-0000-000000000001
PLUTO_PROJECT_TOML_CONTENTS = """
[deps]
"""

# ╔═╡ 00000000-0000-0000-0000-000000000002
PLUTO_MANIFEST_TOML_CONTENTS = """
# This file is machine-generated - editing it directly is not advised

julia_version = "1.7.1"
manifest_format = "2.0"

[deps]
"""

# ╔═╡ Cell order:
# ╠═88c83d42-7dc4-11eb-1b5e-a1ecad4b6ff1
# ╠═8399ed34-7dc4-11eb-16ec-e572834e149d
# ╠═48f7cf80-c1f6-4002-8639-a0addb8e6780
# ╠═18f57a3f-3b1f-4346-9686-752438f6067f
# ╟─00000000-0000-0000-0000-000000000001
# ╟─00000000-0000-0000-0000-000000000002


text:
using PyPlot,Seismic
d,e = SeisLinearEvents();
SeisPlot(d, style="wiggles", xcur=1, wiggle_trace_increment=5, oy=0, dy=0.004);

max_stars_repo_name: fercarozzi/myseismicjulia

max_issues_repo_name: fercarozzi/myseismicjulia

max_forks_repo_name: fercarozzi/myseismicjulia


text:
# Same as tracks_get_saved in tracks.jl
# library_get_saved_tracks(limit=20, offset=0, market="US")

# Same as show_get_saved in shows.jl
# library_get_saved_shows(limit=20, offset=0)

# Same as album_get_saved in albums.jl
# library_get_saved_albums(limit=20, offset=0, market="US")

# Same as tracks_get_contains in tracks.jl
# library_check_saved_tracks(track_ids)

# Same as show_get_contains in shows.jl
# library_check_saved_shows(show_ids)

# Same as album_get_contains in albums.jl
# library_check_saved_albums(album_ids)

#### DELETE ####

## https://developer.spotify.com/documentation/web-api/reference/library/remove-albums-user/
@doc """
# Remove Albums for Current User
**Summary**: Remove one or more albums for the current user's 'Your Music' library.\n

`album_ids` _Required_: A comma-separated list of the Spotify IDs. Maximum 50.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/library/remove-albums-user/)
""" ->
function library_remove_albums(album_ids)
    return spotify_request("me/albums?ids=$album_ids", method="DELETE")
end

## https://developer.spotify.com/documentation/web-api/reference/library/remove-shows-user/
@doc """
# Remove Shows for Current User
**Summary**: Remove one or more shows for the current user's library.\n

`show_ids` _Required_: A comma-separated list of the Spotify IDs. Maximum 50.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/library/remove-shows-user/)
""" ->
function library_remove_shows(show_ids)
    return spotify_request("me/shows?ids=$show_ids", method="DELETE")
end

## https://developer.spotify.com/documentation/web-api/reference/library/remove-tracks-user/
@doc """
# Remove Tracks for Current User
**Summary**: Remove one or more tracks for the current user's 'Your Music' library.\n

`track_ids` _Required_: A comma-separated list of the Spotify IDs. Maximum 50.\n

[Reference](https://developer.spotify.com/documentation/web-api/reference/library/remove-tracks-user/)
""" ->
function library_remove_tracks(track_ids)
    return spotify_request("me/tracks?ids=$track_ids", method="DELETE")
end

#### PUT #####

## https://developer.spotify.com/documentation/web-api/reference/library/save-albums-user/
@doc """
# Save Albums for Current User
** Summary**: Save one or more albums to the current user's 'Your Music' library.\n

`album_ids` _Required_: A comma-separated list of Spotify IDs. Maximum 50. \n

[Reference](https://developer.spotify.com/documentation/web-api/reference/library/save-albums-user/)
""" ->
function library_save_album(album_ids)
    return spotify_request("me/albums?ids=$album_ids", method="PUT")
end

## https://developer.spotify.com/documentation/web-api/reference/library/save-shows-user/
@doc """
# Save Shows for Current User
** Summary**: Save one or more shows to the current user's library.\n

`shows_ids` _Required_: A comma-separated list of Spotify IDs. Maximum 50. \n

[Reference](https://developer.spotify.com/documentation/web-api/reference/library/save-shows-user/)
""" ->
function library_save_show(shows_ids)
    return spotify_request("me/shows?ids=$show_ids", method="PUT")
end

## https://developer.spotify.com/documentation/web-api/reference/library/save-tracks-user/
@doc """
# Save Tracks for Current User
** Summary**: Save one or more tracks to the current user's 'Your Music' library.\n

`track_ids` _Required_: A comma-separated list of Spotify IDs. Maximum 50. \n

[Reference](https://developer.spotify.com/documentation/web-api/reference/library/save-tracks-user/)
""" ->
function library_save_track(track_ids)
    return spotify_request("me/tracks?ids=$track_ids", method="PUT")
end