text:
export State, PositionState

using StaticArrays, NamedTupleTools

import Base: *, +, -, zero, rand, randn, show, promote_rule, rtoldefault,
       isapprox, getproperty, real

import LinearAlgebra: norm, promote_leaf_eltypes

abstract type XState{NT <: NamedTuple} <: AbstractState end

"""
`struct State` the main type for states of input variables (particles).
This type is intended only for storing of information but no arithmetic
should be performed on it. For the latter, we have the DState.
"""
struct State{NT <: NamedTuple} <: XState{NT}
   x::NT

   # standard constructor - SYMS are the same
   State{NT}(t::NT1) where {NT <: NamedTuple{SYMS}, NT1 <: NamedTuple{SYMS}} where {SYMS} =
         new{NT1}(t)

   # if SYMS are not the same we automatically merge them
   State{NT}(t::NT1) where {NT <: NamedTuple, NT1 <: NamedTuple} =
         State( merge( _x(zero(State{NT})), t ) )
end

"""
`struct DState`: A `State`-like variable but acting like vector with arithmetic
operations defined on it, while `State` acts more like a fixed object that cannot
be manipulated. The main application of `DState` is as a derivative of a
`State`; see also `dstate_type`, ``
"""
struct DState{NT <: NamedTuple} <: XState{NT}
   x::NT

   # standard constructor - SYMS are the same
   DState{NT}(t::NT1) where {NT <: NamedTuple{SYMS}, NT1 <: NamedTuple{SYMS}} where {SYMS} =
         new{NT1}(t)

   # if SYMS are not the same we automatically merge them
   DState{NT}(t::NT1) where {NT <: NamedTuple, NT1 <: NamedTuple} =
         DState( merge( _x(zero(DState{NT})), t ) )

   DState{NT}(dX::DState) where {NT} = DState{NT}(_x(dX))

end

# the two standard outward facing constructors
State(nt::NT) where {NT <: NamedTuple} = State{NT}(nt)
State(; kwargs...) = State(NamedTuple(kwargs))

# some variations on the above... which might not be needed anymore
State{NT}(; kwargs...) where {NT <: NamedTuple} = State{NT}(NamedTuple(kwargs))

# accessing X.nt and the fields of X.nt
# this relies heavily on constant propagation
_x(X::XState) = getfield(X, :x)
getproperty(X::XState, sym::Symbol) = getproperty(_x(X), sym)

# ----------- printing / output

const _showdigits = Ref{Int64}(2)

"""
change how many digits are printed in the ACE States
"""
function showdigits!(n::Integer)
   _showdigits[] = n
end

_2str(x) = string(x)
_2str(x::AbstractFloat) = "[$(round(x, digits=_showdigits[]))]"
_2str(x::Complex) = "[$(round(x, digits=_showdigits[]))]"
_2str(x::SVector{N, <: AbstractFloat}) where {N} = string(round.(x, digits=_showdigits[]))
_2str(x::SVector{N, <: Complex}) where {N} = string(round.(x, digits=_showdigits[]))[11:end]

_showsym(X::State) = ""
_showsym(X::DState) = "′"

function show(io::IO, X::XState)
   str = prod( "$(sym):$(_2str(getproperty(_x(X), sym))), "
               for sym in keys(_x(X)) )
   print(io,  "⟨" * str[1:end-2] * "⟩" * _showsym(X))
end

# ----------- some basic manipulations

# extract the symbols and the types
_syms(X::XState) = _syms(typeof(X))
_syms(::Type{<: XState{NamedTuple{SYMS, TT}}}) where {SYMS, TT} = SYMS

_tt(X::XState) = _tt(typeof(X))
_tt(::Type{<: XState{NamedTuple{SYMS, TT}}}) where {SYMS, TT} = TT

_symstt(X::XState) = _symstt(typeof(X))
_symstt(::Type{<: XState{NamedTuple{SYMS, TT}}}) where {SYMS, TT} = SYMS, TT

# which properties are continuous

const CTSTT = Union{AbstractFloat, Complex{<: AbstractFloat},
                    SVector{N, <: AbstractFloat},
                    SVector{N, <: Complex}} where {N}

"""
Find the indices of continuous properties, and return the
indices as well as the symbols and types
"""
_findcts(X::TX) where {TX <: XState} = _findcts(TX)

function _findcts(TX::Type{<: XState})
   SYMS, TT = _symstt(TX)
   icts = findall(T -> T <: CTSTT, TT.types)
end

_ctssyms(X::TX) where {TX <: XState} = _ctssyms(TX)

_ctssyms(TX::Type{<: XState}) = _syms(TX)[_findcts(TX)]

## ----- DState constructors

DState(t::NT) where {NT <: NamedTuple} = DState{NT}(t)

DState(; kwargs...) = DState(NamedTuple(kwargs))

DState(X::TX) where {TX <: State} =
      (dstate_type(X))( select(_x(X), _ctssyms(X)) )

"""
convert a State to a corresponding DState
(basically just remove the discrete variables)
"""
dstate_type(X::DState) = typeof(X)

@generated function dstate_type(X::TX)  where {TX <: State}
   CSYMS = _ctssyms(TX)
   quote
      typeof( DState( select(_x(X), $CSYMS) ) )
   end
end

# the next variant of dstate_type is used to potentially extend
# from real states to complex dstates.

_mypromrl(T::Type{<: Number}, S::Type{<: Number}) =
      promote_type(T, S)
_mypromrl(T::Type{<: Number}, ::Type{<: SVector{N, P}}) where {N, P} =
      SVector{N, promote_type(T, P)}
_mypromrl(::Type{<: SVector{N, P}}, T::Type{<: Number}) where {N, P} =
      promote_type(T, P)
_mypromrl(T::Type{<: SVector{N, P1}}, ::Type{<: SVector{N, P2}}) where {N, P1, P2} =
      SVector{N, promote_type(P1, P2)}

@generated function dstate_type(x::S, X::TX) where {S, TX <: XState}
   SYMS, TT = _symstt(TX)
   icts = _findcts(TX)
   CSYMS = SYMS[icts]
   CTT = [ _mypromrl(S, TT.types[i]) for i in icts ]
   CTTstr = "Tuple{" * "$(tuple(CTT...))"[2:end-1] * "}"
   quote
      $(Meta.parse( "DState{NamedTuple{$(CSYMS), $CTTstr}}" ))
   end
end

dstate_type(S::Type, X::XState) = dstate_type(zero(S), X)

## ---------- explicit real/complex conversion
# this feels a bit like a hack but might be unavoidable;
# real, complex goes to _ace_real, _ace_complex, which is then applied
# in only slightly non-standard fashion recursively to the states

for f in (:real, :imag, :complex, )
   face = Symbol("_ace_$f")
   eval(quote
      import Base: $f
      $face(x::Number) = $f(x)
      $face(x::StaticArrays.StaticArray) = $f.(x)
      function $f(X::TDX) where {TDX <: DState}
         SYMS = _syms(TDX)
         vals = ntuple(i -> $face(getproperty(X, SYMS[i])), length(SYMS))
         return TDX( NamedTuple{SYMS}(vals) )
      end
   end)
end

for f in (:real, :complex, )
   face = Symbol("_ace_$f")
   eval(quote
      $f(TDX::Type{<: DState}) = typeof( $f(zero(TDX)) )
      # import Base: $f
      # $face(x::Type{<: Number}) = $f(x)
      # $face(x::SVector{N, T}) where {N, T} = SVector{N, $f(T)}
      # function $f(TDX::Type{<: DState})
      #    SYMS, TT = _symstt(TDX)
      #    TT1 = ntuple(i -> $face(TT[i]), length(SYMS))
      #    vals = zero.(TT1)
      #    return typeof(TDX( NamedTuple{SYMS}(vals) )
      # end
   end)
end

for f in (:rand, :randn, :zero)
   face = Symbol("_ace_$f")
   eval( quote
      import Base: $f
      $face(T::Type) = $f(T)
      $face(x::Union{Number, AbstractArray}) = $f(typeof(x))

      function $f(x::Union{TX, Type{TX}}) where {TX <: XState}
         SYMS, TT = _symstt(x)
         vals = ntuple(i -> $face(TT.types[i]), length(SYMS))
         return TX( NamedTuple{SYMS}( vals ) )
      end
   end )
end

# an extra for symbols, this is a bit questionable; why do we even need it?
_ace_zero(::Union{Symbol, Type{Symbol}}) = :O

## ----------- Some arithmetic operations

# binary operations

import Base: +, -

# for f in (:+, :-, )
#    eval( quote
#       function $f(X1::TX1, X2::TX2) where {TX1 <: XState, TX2 <: XState}
#          SYMS = _syms(TX1)
#          @assert SYMS == _syms(TX2)
#          vals = ntuple( i -> $f( getproperty(_x(X1), SYMS[i]),
#                                  getproperty(_x(X2), SYMS[i]) ), length(SYMS) )
#          return TX1( NamedTuple{SYMS}(vals) )
#       end
#    end )
# end

for f in (:+, :-, )
   eval( quote
      function $f(X1::TX1, X2::TX2) where {TX1 <: XState, TX2 <: XState}
         SYMS1 = ACE._syms(TX1)
         @assert issubset(ACE._syms(TX2), SYMS1)
         vals = ntuple( i -> begin
                  sym = SYMS1[i]
                  v1 = getproperty(ACE._x(X1), sym)
                  haskey(ACE._x(X2), sym) ? $f(v1, getproperty(ACE._x(X2), sym)) : v1
               end, length(SYMS1))
         return TX1( NamedTuple{SYMS1}(vals) )
      end
   end )
end

# multiplication with a scalar
function *(X1::TX, a::Number) where {TX <: XState}
   SYMS = _syms(TX)
   vals = ntuple( i -> *( getproperty(_x(X1), SYMS[i]), a ), length(SYMS) )
   return TX( NamedTuple{SYMS}(vals) )
end

*(a::Number, X1::XState) = *(X1, a)

*(aa::SVector{N, <: Number}, X1::XState) where {N} = aa .* Ref(X1)
promote_rule(::Type{SVector{N, T}}, ::Type{TX}) where {N, T <: Number, TX <: XState} =
      SVector{N, promote_type(T, TX)}

# unary
import Base: -

for f in (:-, )
   eval(quote
      function $f(X::TX) where {TX <: XState}
         SYMS = _syms(TX)
         vals = ntuple( i -> $f( getproperty(_x(X), SYMS[i]) ), length(SYMS) )
         return TX( NamedTuple{SYMS}(vals) )
      end
   end)
end

# reduction to scalar

import LinearAlgebra: dot
import Base: isapprox

for (f, g) in ( (:dot, :sum), (:isapprox, :all) )  # (:contract, :sum),
   eval( quote
      function $f(X1::TX1, X2::TX2) where {TX1 <: XState, TX2 <: XState}
         SYMS = _syms(TX1)
         @assert SYMS == _syms(TX2)
         return $g( $f( getproperty(_x(X1), sym),
                         getproperty(_x(X2), sym) )   for sym in SYMS)
      end
   end )
end

@generated function contract(X1::TX1, X2::TX2) where {TX1 <: XState, TX2 <: XState}
   SYMS = _syms(TX1)
   @assert SYMS == _syms(TX2)
   code = "contract(X1.$(SYMS[1]), X2.$(SYMS[1]))"
   for sym in SYMS[2:end]
      code *= " + contract(X1.$sym, X2.$sym)"
   end
   return quote
      $(Meta.parse(code))
   end
end

contract(X1::Number, X2::XState) = X1 * X2
contract(X2::XState, X1::Number) = X1 * X2

import LinearAlgebra: norm

for (f, g) in ((:norm, :norm), (:sumsq, :sum), (:normsq, :sum) )
   eval( quote
      function $f(X::TX) where {TX <: XState}
         SYMS = _syms(TX)
         vals = ntuple( i -> $f( getproperty(_x(X), SYMS[i]) ),  length(SYMS))
         return $g(vals)
      end
   end )
end

## --------  not clear where needed; or deleted functionality

function promote_leaf_eltypes(X::TX) where {TX <: XState}
   SYMS = _syms(TX)
   promote_type( ntuple(i -> promote_leaf_eltypes(getproperty(_x(X), SYMS[i])), length(SYMS))... )
end

## ----- Implementation of a Position State, as a basic example

PositionState{T} = State{NamedTuple{(:rr,), Tuple{SVector{3, T}}}}

PositionState(r::AbstractVector{T}) where {T <: AbstractFloat} =
      (@assert length(r) == 3; PositionState{T}(; rr = SVector{3, T}(r)))

promote_rule(::Union{Type{S}, Type{PositionState{S}}},
             ::Type{PositionState{T}}) where {S, T} =
      PositionState{promote_type(S, T)}

# some special functionality for PositionState, mostly needed for testing
*(A::AbstractMatrix, X::TX) where {TX <: PositionState} = TX( (rr = A * X.rr,) )
+(X::TX, u::StaticVector{3}) where {TX <: PositionState} = TX( (rr = X.rr + u,) )

# ------------------ Basic Configurations Code
# TODO: get rid of this?

import ACEbase: AbstractConfiguration
export ACEConfig

"""
`struct ACEConfig`: The canonical implementation of an `AbstractConfiguration`.
Just wraps a `Vector{<: AbstractState}`
"""
struct ACEConfig{STT} <: AbstractConfiguration
   Xs::Vector{STT}   # list of states
end

Base.eltype(cfg::ACEConfig) = eltype(cfg.Xs)

Base.iterate(cfg::ACEConfig, args...) = iterate(cfg.Xs, args...)

Base.length(cfg::ACEConfig) = length(cfg.Xs)

# ---------------- AD code

# TODO: check whether this is still needed
# this function makes sure that gradients w.r.t. a State become a DState
function rrule(::typeof(getproperty), X::XState, sym::Symbol)
   val = getproperty(X, sym)
   return val, w -> ( NoTangent(),
                      dstate_type(w[1], X)( NamedTuple{(sym,)}((w,)) ),
                      NoTangent() )
end

const UConfig = Union{ACEConfig, AbstractVector{<: AbstractState}}


text:
using Documenter, ManifoldLearning

makedocs(
	modules = [ManifoldLearning],
	doctest = false,
	clean = true,
	sitename = "ManifoldLearning.jl",
	pages = [
		"Home" => "index.md",
		"Interface" => "interface.md",
		"Methods" => [
			"Isomap" => "isomap.md",
			"Locally Linear Embedding" => "lle.md",
		 	"Hessian Eigenmaps" => "hlle.md",
		 	"Laplacian Eigenmaps" => "lem.md",
		 	"Local Tangent Space Alignment" => "ltsa.md",
			"Diffusion maps" => "diffmap.md",
		],
		"Misc" => [
			# "Nearest Neighbors" => "knn.md",
			"Datasets" => "datasets.md",
		],
	]
)

deploydocs(repo = "github.com/wildart/ManifoldLearning.jl.git")

ext: jl


text:
using HartreeFock: gaussian_product_center, norm2, boys, Vec3
using Test

@test gaussian_product_center(1.0, Vec3([0.0, 0.0, 0.0]), 1.5, Vec3([1.0, 1.0, 1.0])) == [0.6, 0.6, 0.6]
@test norm2(Vec3([1.0, 2.0, 3.0])) == 14.0
@test boys(0, 1.0) ≈ 0.7468241328


text:
include("call.jl")
using TerminalUI
using Cxx
using Reactive
using DIDebug
using ObjFileBase
using MachO

eval(Base,:(have_color = true))

entrypoint = Gallium.entrypoint_for_julia_expression(dbg,"f","""
buf = IOBuffer()
Base.print_specialized_signature(buf,f)
takebuf_string(buf)
""")
lldb_exec(dbg,"f 7")
lldb_exec(dbg,"f 7")

ctx = Gallium.ctx(dbg)
function getFrameDescription(frame)
  li = Gallium.getASTForFrame(frame).ptr
  str = Gallium.call_prepared_entrypoint(dbg, ctx, entrypoint, Ptr{Void}[li])
  ptr = Gallium.target_call(dbg,:jl_bytestring_ptr,[str.ptr])
  size = Gallium.target_call(dbg,:jl_bytestring_length,[str.ptr])
  bytestring(Gallium.target_read(dbg,convert(UInt64,ptr),size))
end
function Base.print(io::IO,frame::Union{pcpp"lldb_private::StackFrame",
                                    cxxt"lldb::StackFrameSP"})
  if Gallium.isJuliaFrame(frame)
    print(io,getFrameDescription(frame))
  else
    print(io,Gallium.dump(frame))
  end
end

####################

objcache = Dict{Any,Any}()

function processed_cus(frame::Union{pcpp"lldb_private::StackFrame",
                                    cxxt"lldb::StackFrameSP"})
    block = icxx"$frame->GetFrameBlock();"
    if block == C_NULL
        error(sprint(print,frame))
    end
    mod = icxx"$frame->GetFrameBlock()->CalculateSymbolContextModule();"
    id = Gallium.uuid(mod)
    if !haskey(objcache,id)
        oh = Gallium.ReadObjectFile(icxx"$mod->GetObjectFile();")
        debugoh = Gallium.ReadObjectFile(icxx"$mod->GetSymbolVendor()->
                GetSymbolFile()->GetObjectFile();")
        dbgs = debugsections(debugoh);
        strtab = load_strtab(dbgs.debug_str)
        objcache[id] = (oh, dbgs, DIDebug.process_cus(dbgs, strtab))
    end
    objcache[id]
end
function_name(frame::cxxt"lldb::StackFrameSP") =
    bytestring(icxx"$frame->GetFrameBlock()->CalculateSymbolContextFunction()->GetName();")

#####################

function Base.print(io::IO,V::Union{pcpp"lldb_private::Variable",
                                    cxxt"lldb::VariableSP"})
    print(io,Gallium.dump(V))
end

thread = Gallium.current_thread(ctx)
listw = Border(ListWidget(thread),"Stack Frame")
VL = icxx"$(thread[7])->GetVariableList(false);"
listw2 = ListWidget(VL)
w3 = IOBufferView(IOBuffer())
lift(listw.child.highlighted) do it
    VL = icxx"$(thread[it])->GetVariableList(false);"
    if VL != C_NULL
      listw2.item = VL
    else
      listw2.item = ("No Local Variables",)
    end
    listw2.cur_top = start(listw2.item)
    TerminalUI.invalidate(listw2)
end
lift(listw.child.highlighted) do it
    frame = thread[it]
    buf = IOBuffer()
    try
        oh, dbgs, didata = processed_cus(frame)
        name = function_name(frame)
        DIDebug.investigate_function(buf, oh, dbgs, didata, name)
    catch e
        print(buf,e)
    end
    w3.buf = buf #IOBuffer(repr(takebuf_string(buf)))
    w3.cur_top = TerminalUI.element_start(w3)
    TerminalUI.invalidate(w3)
end
b2 = Border(listw2,"Local Variables")
b3 = Border(w3,"Current Function")
tty = Base.Terminals.TTYTerminal("xterm",STDIN,STDOUT,STDERR)
wait(FullScreenDialog(WidgetStack(reverse([b3,listw,b2])),tty))


text:
# Generic concrete representations of PDDL domains, states, actions, etc. #

include("domain.jl")
include("problem.jl")
include("state.jl")
include("action.jl")
include("diff.jl")


text:
#==============================================================================#
# StepFunctions.jl
#
# This file is generated from:
# https://github.com/aws/aws-sdk-js/blob/master/apis/states-2016-11-23.normal.json
#==============================================================================#

__precompile__()

module StepFunctions

using AWSCore

"""
    using AWSSDK.StepFunctions.create_activity
    create_activity([::AWSConfig], arguments::Dict)
    create_activity([::AWSConfig]; name=)

    using AWSCore.Services.states
    states([::AWSConfig], "CreateActivity", arguments::Dict)
    states([::AWSConfig], "CreateActivity", name=)

# CreateActivity Operation

Creates an activity. An activity is a task which you write in any programming language and host on any machine which has access to AWS Step Functions. Activities must poll Step Functions using the `GetActivityTask` API action and respond using `SendTask*` API actions. This function lets Step Functions know the existence of your activity and returns an identifier for use in a state machine and when polling from the activity.

# Arguments

## `name = ::String` -- *Required*
The name of the activity to create. This name must be unique for your AWS account and region for 90 days. For more information, see [Limits Related to State Machine Executions](http://docs.aws.amazon.com/step-functions/latest/dg/limits.html#service-limits-state-machine-executions) in the *AWS Step Functions Developer Guide*.

A name must *not* contain:

*   whitespace

*   brackets `< > { } [ ]`

*   wildcard characters `? *`

*   special characters `" # % \\ ^ | ~ ` \$ & , ; : /`

*   control characters (`U+0000-001F`, `U+007F-009F`)

# Returns

`CreateActivityOutput`

# Exceptions

`ActivityLimitExceeded` or `InvalidName`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/CreateActivity)
"""
@inline create_activity(aws::AWSConfig=default_aws_config(); args...) = create_activity(aws, args)

@inline create_activity(aws::AWSConfig, args) = AWSCore.Services.states(aws, "CreateActivity", args)

@inline create_activity(args) = create_activity(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.create_state_machine
    create_state_machine([::AWSConfig], arguments::Dict)
    create_state_machine([::AWSConfig]; name=, definition=, roleArn=)

    using AWSCore.Services.states
    states([::AWSConfig], "CreateStateMachine", arguments::Dict)
    states([::AWSConfig], "CreateStateMachine", name=, definition=, roleArn=)

# CreateStateMachine Operation

Creates a state machine. A state machine consists of a collection of states that can do work (`Task` states), determine to which states to transition next (`Choice` states), stop an execution with an error (`Fail` states), and so on. State machines are specified using a JSON-based, structured language.

# Arguments

## `name = ::String` -- *Required*
The name of the state machine. This name must be unique for your AWS account and region for 90 days. For more information, see [Limits Related to State Machine Executions](http://docs.aws.amazon.com/step-functions/latest/dg/limits.html#service-limits-state-machine-executions) in the *AWS Step Functions Developer Guide*.

A name must *not* contain:

*   whitespace

*   brackets `< > { } [ ]`

*   wildcard characters `? *`

*   special characters `" # % \\ ^ | ~ ` \$ & , ; : /`

*   control characters (`U+0000-001F`, `U+007F-009F`)

## `definition = ::String` -- *Required*
The Amazon States Language definition of the state machine.

## `roleArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the IAM role to use for this state machine.

# Returns

`CreateStateMachineOutput`

# Exceptions

`InvalidArn`, `InvalidDefinition`, `InvalidName`, `StateMachineAlreadyExists`, `StateMachineDeleting` or `StateMachineLimitExceeded`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/CreateStateMachine)
"""
@inline create_state_machine(aws::AWSConfig=default_aws_config(); args...) = create_state_machine(aws, args)

@inline create_state_machine(aws::AWSConfig, args) = AWSCore.Services.states(aws, "CreateStateMachine", args)

@inline create_state_machine(args) = create_state_machine(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.delete_activity
    delete_activity([::AWSConfig], arguments::Dict)
    delete_activity([::AWSConfig]; activityArn=)

    using AWSCore.Services.states
    states([::AWSConfig], "DeleteActivity", arguments::Dict)
    states([::AWSConfig], "DeleteActivity", activityArn=)

# DeleteActivity Operation

Deletes an activity.

# Arguments

## `activityArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the activity to delete.

# Returns

`DeleteActivityOutput`

# Exceptions

`InvalidArn`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/DeleteActivity)
"""
@inline delete_activity(aws::AWSConfig=default_aws_config(); args...) = delete_activity(aws, args)

@inline delete_activity(aws::AWSConfig, args) = AWSCore.Services.states(aws, "DeleteActivity", args)

@inline delete_activity(args) = delete_activity(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.delete_state_machine
    delete_state_machine([::AWSConfig], arguments::Dict)
    delete_state_machine([::AWSConfig]; stateMachineArn=)

    using AWSCore.Services.states
    states([::AWSConfig], "DeleteStateMachine", arguments::Dict)
    states([::AWSConfig], "DeleteStateMachine", stateMachineArn=)

# DeleteStateMachine Operation

Deletes a state machine. This is an asynchronous operation: It sets the state machine's status to `DELETING` and begins the deletion process. Each state machine execution is deleted the next time it makes a state transition.

**Note**
> The state machine itself is deleted after all executions are completed or deleted.

# Arguments

## `stateMachineArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the state machine to delete.

# Returns

`DeleteStateMachineOutput`

# Exceptions

`InvalidArn`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/DeleteStateMachine)
"""
@inline delete_state_machine(aws::AWSConfig=default_aws_config(); args...) = delete_state_machine(aws, args)

@inline delete_state_machine(aws::AWSConfig, args) = AWSCore.Services.states(aws, "DeleteStateMachine", args)

@inline delete_state_machine(args) = delete_state_machine(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.describe_activity
    describe_activity([::AWSConfig], arguments::Dict)
    describe_activity([::AWSConfig]; activityArn=)

    using AWSCore.Services.states
    states([::AWSConfig], "DescribeActivity", arguments::Dict)
    states([::AWSConfig], "DescribeActivity", activityArn=)

# DescribeActivity Operation

Describes an activity.

# Arguments

## `activityArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the activity to describe.

# Returns

`DescribeActivityOutput`

# Exceptions

`ActivityDoesNotExist` or `InvalidArn`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/DescribeActivity)
"""
@inline describe_activity(aws::AWSConfig=default_aws_config(); args...) = describe_activity(aws, args)

@inline describe_activity(aws::AWSConfig, args) = AWSCore.Services.states(aws, "DescribeActivity", args)

@inline describe_activity(args) = describe_activity(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.describe_execution
    describe_execution([::AWSConfig], arguments::Dict)
    describe_execution([::AWSConfig]; executionArn=)

    using AWSCore.Services.states
    states([::AWSConfig], "DescribeExecution", arguments::Dict)
    states([::AWSConfig], "DescribeExecution", executionArn=)

# DescribeExecution Operation

Describes an execution.

# Arguments

## `executionArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the execution to describe.

# Returns

`DescribeExecutionOutput`

# Exceptions

`ExecutionDoesNotExist` or `InvalidArn`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/DescribeExecution)
"""
@inline describe_execution(aws::AWSConfig=default_aws_config(); args...) = describe_execution(aws, args)

@inline describe_execution(aws::AWSConfig, args) = AWSCore.Services.states(aws, "DescribeExecution", args)

@inline describe_execution(args) = describe_execution(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.describe_state_machine
    describe_state_machine([::AWSConfig], arguments::Dict)
    describe_state_machine([::AWSConfig]; stateMachineArn=)

    using AWSCore.Services.states
    states([::AWSConfig], "DescribeStateMachine", arguments::Dict)
    states([::AWSConfig], "DescribeStateMachine", stateMachineArn=)

# DescribeStateMachine Operation

Describes a state machine.

# Arguments

## `stateMachineArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the state machine to describe.

# Returns

`DescribeStateMachineOutput`

# Exceptions

`InvalidArn` or `StateMachineDoesNotExist`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/DescribeStateMachine)
"""
@inline describe_state_machine(aws::AWSConfig=default_aws_config(); args...) = describe_state_machine(aws, args)

@inline describe_state_machine(aws::AWSConfig, args) = AWSCore.Services.states(aws, "DescribeStateMachine", args)

@inline describe_state_machine(args) = describe_state_machine(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.describe_state_machine_for_execution
    describe_state_machine_for_execution([::AWSConfig], arguments::Dict)
    describe_state_machine_for_execution([::AWSConfig]; executionArn=)

    using AWSCore.Services.states
    states([::AWSConfig], "DescribeStateMachineForExecution", arguments::Dict)
    states([::AWSConfig], "DescribeStateMachineForExecution", executionArn=)

# DescribeStateMachineForExecution Operation

Describes the state machine associated with a specific execution.

# Arguments

## `executionArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the execution you want state machine information for.

# Returns

`DescribeStateMachineForExecutionOutput`

# Exceptions

`ExecutionDoesNotExist` or `InvalidArn`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/DescribeStateMachineForExecution)
"""
@inline describe_state_machine_for_execution(aws::AWSConfig=default_aws_config(); args...) = describe_state_machine_for_execution(aws, args)

@inline describe_state_machine_for_execution(aws::AWSConfig, args) = AWSCore.Services.states(aws, "DescribeStateMachineForExecution", args)

@inline describe_state_machine_for_execution(args) = describe_state_machine_for_execution(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.get_activity_task
    get_activity_task([::AWSConfig], arguments::Dict)
    get_activity_task([::AWSConfig]; activityArn=, <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "GetActivityTask", arguments::Dict)
    states([::AWSConfig], "GetActivityTask", activityArn=, <keyword arguments>)

# GetActivityTask Operation

Used by workers to retrieve a task (with the specified activity ARN) which has been scheduled for execution by a running state machine. This initiates a long poll, where the service holds the HTTP connection open and responds as soon as a task becomes available (i.e. an execution of a task of this type is needed.) The maximum time the service holds on to the request before responding is 60 seconds. If no task is available within 60 seconds, the poll returns a `taskToken` with a null string.

**Important**
> Workers should set their client side socket timeout to at least 65 seconds (5 seconds higher than the maximum time the service may hold the poll request).

# Arguments

## `activityArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the activity to retrieve tasks from (assigned when you create the task using [CreateActivity](@ref).)

## `workerName = ::String`
You can provide an arbitrary name in order to identify the worker that the task is assigned to. This name is used when it is logged in the execution history.

# Returns

`GetActivityTaskOutput`

# Exceptions

`ActivityDoesNotExist`, `ActivityWorkerLimitExceeded` or `InvalidArn`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/GetActivityTask)
"""
@inline get_activity_task(aws::AWSConfig=default_aws_config(); args...) = get_activity_task(aws, args)

@inline get_activity_task(aws::AWSConfig, args) = AWSCore.Services.states(aws, "GetActivityTask", args)

@inline get_activity_task(args) = get_activity_task(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.get_execution_history
    get_execution_history([::AWSConfig], arguments::Dict)
    get_execution_history([::AWSConfig]; executionArn=, <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "GetExecutionHistory", arguments::Dict)
    states([::AWSConfig], "GetExecutionHistory", executionArn=, <keyword arguments>)

# GetExecutionHistory Operation

Returns the history of the specified execution as a list of events. By default, the results are returned in ascending order of the `timeStamp` of the events. Use the `reverseOrder` parameter to get the latest events first.

If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

# Arguments

## `executionArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the execution.

## `maxResults = ::Int`
The maximum number of results that are returned per call. You can use `nextToken` to obtain further pages of results. The default is 100 and the maximum allowed page size is 100. A value of 0 uses the default.

This is only an upper limit. The actual number of results returned per call might be fewer than the specified maximum.

## `reverseOrder = ::Bool`
Lists events in descending order of their `timeStamp`.

## `nextToken = ::String`
If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

The configured `maxResults` determines how many results can be returned in a single call.

# Returns

`GetExecutionHistoryOutput`

# Exceptions

`ExecutionDoesNotExist`, `InvalidArn` or `InvalidToken`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/GetExecutionHistory)
"""
@inline get_execution_history(aws::AWSConfig=default_aws_config(); args...) = get_execution_history(aws, args)

@inline get_execution_history(aws::AWSConfig, args) = AWSCore.Services.states(aws, "GetExecutionHistory", args)

@inline get_execution_history(args) = get_execution_history(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.list_activities
    list_activities([::AWSConfig], arguments::Dict)
    list_activities([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "ListActivities", arguments::Dict)
    states([::AWSConfig], "ListActivities", <keyword arguments>)

# ListActivities Operation

Lists the existing activities.

If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

# Arguments

## `maxResults = ::Int`
The maximum number of results that are returned per call. You can use `nextToken` to obtain further pages of results. The default is 100 and the maximum allowed page size is 100. A value of 0 uses the default.

This is only an upper limit. The actual number of results returned per call might be fewer than the specified maximum.

## `nextToken = ::String`
If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

The configured `maxResults` determines how many results can be returned in a single call.

# Returns

`ListActivitiesOutput`

# Exceptions

`InvalidToken`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/ListActivities)
"""
@inline list_activities(aws::AWSConfig=default_aws_config(); args...) = list_activities(aws, args)

@inline list_activities(aws::AWSConfig, args) = AWSCore.Services.states(aws, "ListActivities", args)

@inline list_activities(args) = list_activities(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.list_executions
    list_executions([::AWSConfig], arguments::Dict)
    list_executions([::AWSConfig]; stateMachineArn=, <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "ListExecutions", arguments::Dict)
    states([::AWSConfig], "ListExecutions", stateMachineArn=, <keyword arguments>)

# ListExecutions Operation

Lists the executions of a state machine that meet the filtering criteria.

If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

# Arguments

## `stateMachineArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the state machine whose executions is listed.

## `statusFilter = "RUNNING", "SUCCEEDED", "FAILED", "TIMED_OUT" or "ABORTED"`
If specified, only list the executions whose current execution status matches the given filter.

## `maxResults = ::Int`
The maximum number of results that are returned per call. You can use `nextToken` to obtain further pages of results. The default is 100 and the maximum allowed page size is 100. A value of 0 uses the default.

This is only an upper limit. The actual number of results returned per call might be fewer than the specified maximum.

## `nextToken = ::String`
If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

The configured `maxResults` determines how many results can be returned in a single call.

# Returns

`ListExecutionsOutput`

# Exceptions

`InvalidArn`, `InvalidToken` or `StateMachineDoesNotExist`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/ListExecutions)
"""
@inline list_executions(aws::AWSConfig=default_aws_config(); args...) = list_executions(aws, args)

@inline list_executions(aws::AWSConfig, args) = AWSCore.Services.states(aws, "ListExecutions", args)

@inline list_executions(args) = list_executions(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.list_state_machines
    list_state_machines([::AWSConfig], arguments::Dict)
    list_state_machines([::AWSConfig]; <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "ListStateMachines", arguments::Dict)
    states([::AWSConfig], "ListStateMachines", <keyword arguments>)

# ListStateMachines Operation

Lists the existing state machines.

If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

# Arguments

## `maxResults = ::Int`
The maximum number of results that are returned per call. You can use `nextToken` to obtain further pages of results. The default is 100 and the maximum allowed page size is 100. A value of 0 uses the default.

This is only an upper limit. The actual number of results returned per call might be fewer than the specified maximum.

## `nextToken = ::String`
If a `nextToken` is returned by a previous call, there are more results available. To retrieve the next page of results, make the call again using the returned token in `nextToken`. Keep all other arguments unchanged.

The configured `maxResults` determines how many results can be returned in a single call.

# Returns

`ListStateMachinesOutput`

# Exceptions

`InvalidToken`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/ListStateMachines)
"""
@inline list_state_machines(aws::AWSConfig=default_aws_config(); args...) = list_state_machines(aws, args)

@inline list_state_machines(aws::AWSConfig, args) = AWSCore.Services.states(aws, "ListStateMachines", args)

@inline list_state_machines(args) = list_state_machines(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.send_task_failure
    send_task_failure([::AWSConfig], arguments::Dict)
    send_task_failure([::AWSConfig]; taskToken=, <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "SendTaskFailure", arguments::Dict)
    states([::AWSConfig], "SendTaskFailure", taskToken=, <keyword arguments>)

# SendTaskFailure Operation

Used by workers to report that the task identified by the `taskToken` failed.

# Arguments

## `taskToken = ::String` -- *Required*
The token that represents this task. Task tokens are generated by the service when the tasks are assigned to a worker (see GetActivityTask::taskToken).

## `error = ::String`
An arbitrary error code that identifies the cause of the failure.

## `cause = ::String`
A more detailed explanation of the cause of the failure.

# Returns

`SendTaskFailureOutput`

# Exceptions

`TaskDoesNotExist`, `InvalidToken` or `TaskTimedOut`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/SendTaskFailure)
"""
@inline send_task_failure(aws::AWSConfig=default_aws_config(); args...) = send_task_failure(aws, args)

@inline send_task_failure(aws::AWSConfig, args) = AWSCore.Services.states(aws, "SendTaskFailure", args)

@inline send_task_failure(args) = send_task_failure(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.send_task_heartbeat
    send_task_heartbeat([::AWSConfig], arguments::Dict)
    send_task_heartbeat([::AWSConfig]; taskToken=)

    using AWSCore.Services.states
    states([::AWSConfig], "SendTaskHeartbeat", arguments::Dict)
    states([::AWSConfig], "SendTaskHeartbeat", taskToken=)

# SendTaskHeartbeat Operation

Used by workers to report to the service that the task represented by the specified `taskToken` is still making progress. This action resets the `Heartbeat` clock. The `Heartbeat` threshold is specified in the state machine's Amazon States Language definition. This action does not in itself create an event in the execution history. However, if the task times out, the execution history contains an `ActivityTimedOut` event.

**Note**
> The `Timeout` of a task, defined in the state machine's Amazon States Language definition, is its maximum allowed duration, regardless of the number of [SendTaskHeartbeat](@ref) requests received.

**Note**
> This operation is only useful for long-lived tasks to report the liveliness of the task.

# Arguments

## `taskToken = ::String` -- *Required*
The token that represents this task. Task tokens are generated by the service when the tasks are assigned to a worker (see [GetActivityTaskOutput\$taskToken](@ref)).

# Returns

`SendTaskHeartbeatOutput`

# Exceptions

`TaskDoesNotExist`, `InvalidToken` or `TaskTimedOut`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/SendTaskHeartbeat)
"""
@inline send_task_heartbeat(aws::AWSConfig=default_aws_config(); args...) = send_task_heartbeat(aws, args)

@inline send_task_heartbeat(aws::AWSConfig, args) = AWSCore.Services.states(aws, "SendTaskHeartbeat", args)

@inline send_task_heartbeat(args) = send_task_heartbeat(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.send_task_success
    send_task_success([::AWSConfig], arguments::Dict)
    send_task_success([::AWSConfig]; taskToken=, output=)

    using AWSCore.Services.states
    states([::AWSConfig], "SendTaskSuccess", arguments::Dict)
    states([::AWSConfig], "SendTaskSuccess", taskToken=, output=)

# SendTaskSuccess Operation

Used by workers to report that the task identified by the `taskToken` completed successfully.

# Arguments

## `taskToken = ::String` -- *Required*
The token that represents this task. Task tokens are generated by the service when the tasks are assigned to a worker (see [GetActivityTaskOutput\$taskToken](@ref)).

## `output = ::String` -- *Required*
The JSON output of the task.

# Returns

`SendTaskSuccessOutput`

# Exceptions

`TaskDoesNotExist`, `InvalidOutput`, `InvalidToken` or `TaskTimedOut`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/SendTaskSuccess)
"""
@inline send_task_success(aws::AWSConfig=default_aws_config(); args...) = send_task_success(aws, args)

@inline send_task_success(aws::AWSConfig, args) = AWSCore.Services.states(aws, "SendTaskSuccess", args)

@inline send_task_success(args) = send_task_success(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.start_execution
    start_execution([::AWSConfig], arguments::Dict)
    start_execution([::AWSConfig]; stateMachineArn=, <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "StartExecution", arguments::Dict)
    states([::AWSConfig], "StartExecution", stateMachineArn=, <keyword arguments>)

# StartExecution Operation

Starts a state machine execution.

# Arguments

## `stateMachineArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the state machine to execute.

## `name = ::String`
The name of the execution. This name must be unique for your AWS account and region for 90 days. For more information, see [Limits Related to State Machine Executions](http://docs.aws.amazon.com/step-functions/latest/dg/limits.html#service-limits-state-machine-executions) in the *AWS Step Functions Developer Guide*.

**Important**
> An execution can't use the name of another execution for 90 days.

When you make multiple `StartExecution` calls with the same name, the new execution doesn't run and the following rules apply:

*   When the original execution is open and the execution input from the new call is *different*, the `ExecutionAlreadyExists` message is returned.

*   When the original execution is open and the execution input from the new call is *identical*, the `Success` message is returned.

*   When the original execution is closed, the `ExecutionAlreadyExists` message is returned regardless of input.

A name must *not* contain:

*   whitespace

*   brackets `< > { } [ ]`

*   wildcard characters `? *`

*   special characters `" # % \\ ^ | ~ ` \$ & , ; : /`

*   control characters (`U+0000-001F`, `U+007F-009F`)

## `input = ::String`
The string that contains the JSON input data for the execution, for example:

`"input": "{\\"first_name\\" : \\"test\\"}"`

**Note**
> If you don't include any JSON input data, you still must include the two braces, for example: `"input": "{}"`

# Returns

`StartExecutionOutput`

# Exceptions

`ExecutionLimitExceeded`, `ExecutionAlreadyExists`, `InvalidArn`, `InvalidExecutionInput`, `InvalidName`, `StateMachineDoesNotExist` or `StateMachineDeleting`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/StartExecution)
"""
@inline start_execution(aws::AWSConfig=default_aws_config(); args...) = start_execution(aws, args)

@inline start_execution(aws::AWSConfig, args) = AWSCore.Services.states(aws, "StartExecution", args)

@inline start_execution(args) = start_execution(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.stop_execution
    stop_execution([::AWSConfig], arguments::Dict)
    stop_execution([::AWSConfig]; executionArn=, <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "StopExecution", arguments::Dict)
    states([::AWSConfig], "StopExecution", executionArn=, <keyword arguments>)

# StopExecution Operation

Stops an execution.

# Arguments

## `executionArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the execution to stop.

## `error = ::String`
An arbitrary error code that identifies the cause of the termination.

## `cause = ::String`
A more detailed explanation of the cause of the termination.

# Returns

`StopExecutionOutput`

# Exceptions

`ExecutionDoesNotExist` or `InvalidArn`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/StopExecution)
"""
@inline stop_execution(aws::AWSConfig=default_aws_config(); args...) = stop_execution(aws, args)

@inline stop_execution(aws::AWSConfig, args) = AWSCore.Services.states(aws, "StopExecution", args)

@inline stop_execution(args) = stop_execution(default_aws_config(), args)

"""
    using AWSSDK.StepFunctions.update_state_machine
    update_state_machine([::AWSConfig], arguments::Dict)
    update_state_machine([::AWSConfig]; stateMachineArn=, <keyword arguments>)

    using AWSCore.Services.states
    states([::AWSConfig], "UpdateStateMachine", arguments::Dict)
    states([::AWSConfig], "UpdateStateMachine", stateMachineArn=, <keyword arguments>)

# UpdateStateMachine Operation

Updates an existing state machine by modifying its `definition` and/or `roleArn`. Running executions will continue to use the previous `definition` and `roleArn`.

**Note**
> All `StartExecution` calls within a few seconds will use the updated `definition` and `roleArn`. Executions started immediately after calling `UpdateStateMachine` may use the previous state machine `definition` and `roleArn`. You must include at least one of `definition` or `roleArn` or you will receive a `MissingRequiredParameter` error.

# Arguments

## `stateMachineArn = ::String` -- *Required*
The Amazon Resource Name (ARN) of the state machine.

## `definition = ::String`
The Amazon States Language definition of the state machine.

## `roleArn = ::String`
The Amazon Resource Name (ARN) of the IAM role of the state machine.

# Returns

`UpdateStateMachineOutput`

# Exceptions

`InvalidArn`, `InvalidDefinition`, `MissingRequiredParameter`, `StateMachineDeleting` or `StateMachineDoesNotExist`.

See also: [AWS API Documentation](https://docs.aws.amazon.com/goto/WebAPI/states-2016-11-23/UpdateStateMachine)
"""
@inline update_state_machine(aws::AWSConfig=default_aws_config(); args...) = update_state_machine(aws, args)

@inline update_state_machine(aws::AWSConfig, args) = AWSCore.Services.states(aws, "UpdateStateMachine", args)

@inline update_state_machine(args) = update_state_machine(default_aws_config(), args)

end # module StepFunctions

#==============================================================================#
# End of file
#==============================================================================#


text:
"""
    Algorithm

A type representing an algorithm backend for a function.

For example, ITensor provides multiple backend algorithms for contracting
an MPO with an MPS, which internally are selected with an `Algorithm` type.

This allows users to extend functions in ITensor with new algorithms, but
use the same interface.
"""
struct Algorithm{Alg} end

Algorithm(s) = Algorithm{Symbol(s)}()
algorithm(::Algorithm{Alg}) where {Alg} = string(Alg)

show(io::IO, alg::Algorithm) = print(io, "Algorithm type ", algorithm(alg))
print(io::IO, ::Algorithm{Alg}) where {Alg} = print(io, Alg)

"""
    @Algorithm_str

A convenience macro for writing [`Algorithm`](@ref) types, typically used when
adding methods to a function in ITensor that supports multiple algorithm
backends (like contracting an MPO with an MPS).
"""
macro Algorithm_str(s)
  return :(Algorithm{$(Expr(:quote, Symbol(s)))})
end


text:
# BED Reader
# ==========
import Automa
import Automa.RegExp: @re_str
import Automa.Stream: @mark, @markpos, @relpos, @abspos

function appendfrom!(dst, dpos, src, spos, n)
    if length(dst) < dpos + n - 1
        resize!(dst, dpos + n - 1)
    end
    unsafe_copyto!(dst, dpos, src, spos, n)
    return dst
end

mutable struct Reader <: BioGenerics.IO.AbstractReader
    state::BioGenerics.Automa.State
    index::Union{Indexes.Tabix,Nothing}

    function Reader(stream::TranscodingStream, index=nothing)
        return new(BioGenerics.Automa.State(stream, 1, 1, false), index)
    end
end

"""
    BED.Reader(input::IO; index=nothing)
    BED.Reader(input::AbstractString; index=:auto)

Create a data reader of the BED file format.

The first argument specifies the data source.
When it is a filepath that ends with *.bgz*, it is considered to be block compression file format (BGZF) and the function will try to find a tabix index file (<filename>.tbi) and read it if any.
See <http://www.htslib.org/doc/tabix.html> for bgzip and tabix tools.

Arguments
---------
- `input`: data source
- `index`: path to a tabix file
"""
function Reader(input::IO; index=nothing)
    if isa(index, AbstractString)
        index = Indexes.Tabix(index)
    end

    stream = TranscodingStreams.NoopStream(input)

    return Reader(stream, index)

end

function Reader(filepath::AbstractString; index=:auto)
    if isa(index, Symbol) && index != :auto
        throw(ArgumentError("invalid index argument: ':$(index)'"))
    end
    if endswith(filepath, ".bgz")
        input = BGZFStreams.BGZFStream(filepath)
        if index == :auto
            index = Indexes.findtabix(filepath)
        end
    else
        input = open(filepath)
    end
    return Reader(input, index = index)
end

function Base.eltype(::Type{Reader})
    return Record
end

function BioGenerics.IO.stream(reader::Reader)
    return reader.state.stream
end

function Base.iterate(reader::Reader, nextone = Record())
    if BioGenerics.IO.tryread!(reader, nextone) === nothing
        return nothing
    end
    return copy(nextone), empty!(nextone) # Empty record for inplace reading and reuse of array allocations.
end

function GenomicFeatures.eachoverlap(reader::Reader, interval::GenomicFeatures.Interval)
    if reader.index === nothing
        throw(ArgumentError("index is null"))
    end
    return Indexes.TabixOverlapIterator(reader, interval)
end

const record_machine, file_machine = (function ()
    cat = Automa.RegExp.cat
    rep = Automa.RegExp.rep
    opt = Automa.RegExp.opt

    record = let
        chrom = re"[ -~]*"
        chrom.actions[:enter] = [:pos]
        chrom.actions[:exit] = [:record_chrom]

        chromstart = re"[0-9]+"
        chromstart.actions[:enter] = [:pos]
        chromstart.actions[:exit] = [:record_chromstart]

        chromend = re"[0-9]+"
        chromend.actions[:enter] = [:pos]
        chromend.actions[:exit] = [:record_chromend]

        name = re"[ -~]*"
        name.actions[:enter] = [:pos]
        name.actions[:exit] = [:record_name]

        score = re"[0-9]+"
        score.actions[:enter] = [:pos]
        score.actions[:exit] = [:record_score]

        strand = re"[+\-.?]"
        strand.actions[:enter] = [:record_strand] #Note: single byte.

        thickstart = re"[0-9]+"
        thickstart.actions[:enter] = [:pos]
        thickstart.actions[:exit] = [:record_thickstart]

        thickend = re"[0-9]+"
        thickend.actions[:enter] = [:pos]
        thickend.actions[:exit] = [:record_thickend]

        itemrgb = cat(re"[0-9]+", opt(cat(',', re"[0-9]+", ',', re"[0-9]+")))
        itemrgb.actions[:enter] = [:pos]
        itemrgb.actions[:exit] = [:record_itemrgb]

        blockcount = re"[0-9]+"
        blockcount.actions[:enter] = [:pos]
        blockcount.actions[:exit] = [:record_blockcount]

        # comma-separated values
        csv(x) = cat(rep(cat(x, ',')), opt(x))

        blocksizes = let
            blocksize = re"[0-9]+"
            blocksize.actions[:enter] = [:pos]
            blocksize.actions[:exit] = [:record_blocksizes_blocksize]

            csv(blocksize)
        end
        blocksizes.actions[:exit] = [:record_blocksizes]

        blockstarts = let
            blockstart = re"[0-9]+"
            blockstart.actions[:enter] = [:pos]
            blockstart.actions[:exit] = [:record_blockstarts_blockstart]

            csv(blockstart)
        end
        blockstarts.actions[:exit] = [:record_blockstarts]

        cat(
            chrom, '\t',
            chromstart, '\t',
            chromend,
            opt(cat('\t', name,
            opt(cat('\t', score,
            opt(cat('\t', strand,
            opt(cat('\t', thickstart,
            opt(cat('\t', thickend,
            opt(cat('\t', itemrgb,
            opt(cat('\t', blockcount,
            opt(cat('\t', blocksizes,
            opt(cat('\t', blockstarts)))))))))))))))))))
    end
    record.actions[:enter] = [:mark]
    record.actions[:exit] = [:record]

    newline = let
        lf = re"\n"
        lf.actions[:enter] = [:countline]

        cat(opt('\r'), lf)
    end

    file = rep(cat(record, newline))

    return map(Automa.compile, (record, file))
end)()

#=
write("bed.dot", Automa.machine2dot(file_machine))
run(`dot -Tsvg -o bed.svg bed.dot`)
=#

const record_actions = Dict(
    :mark => :(@mark),
    :pos => :(pos = @relpos(p)),
    :countline => :(),
    :record_chrom => :(record.chrom = (pos:@relpos(p-1)); record.ncols += 1),
    :record_chromstart => :(record.chromstart = (pos:@relpos(p-1)); record.ncols += 1),
    :record_chromend => :(record.chromend = (pos:@relpos(p-1)); record.ncols += 1),
    :record_name => :(record.name = (pos:@relpos(p-1)); record.ncols += 1),
    :record_score => :(record.score = (pos:@relpos(p-1)); record.ncols += 1),
    :record_strand => :(record.strand = @relpos(p); record.ncols += 1),
    :record_thickstart => :(record.thickstart = (pos:@relpos(p-1)); record.ncols += 1),
    :record_thickend => :(record.thickend = (pos:@relpos(p-1)); record.ncols += 1),
    :record_itemrgb => :(record.itemrgb = (pos:@relpos(p-1)); record.ncols += 1),
    :record_blockcount => :(record.blockcount = (pos:@relpos(p-1)); record.ncols += 1),
    :record_blocksizes_blocksize => :(push!(record.blocksizes, (pos:@relpos(p-1)))),
    :record_blocksizes => :(record.ncols += 1),
    :record_blockstarts_blockstart => :(push!(record.blockstarts, (pos:@relpos(p-1)))),
    :record_blockstarts => :(record.ncols += 1),
    :record => :(record.filled = 1:@relpos(p-1))
)

Automa.Stream.generate_reader(
    :index!,
    record_machine,
    arguments = (:(record::Record),),
    actions = record_actions,
    # context = :(),
    initcode = :(pos = 0),
    # loopcode = :()
    # returncode = :()
) |> eval

const initcode = quote
    pos = 0
    linenum = 0
    found_record=false
    # empty!(record)
    cs, linenum = state
end

const loopcode = quote
    if found_record
        @goto __return__
    end
end

Automa.Stream.generate_reader(
    :readrecord!,
    file_machine,
    arguments = (:(record::Record), :(state::Tuple{Int,Int})),
    actions = merge(record_actions, Dict(
        :record => quote
            appendfrom!(record.data, 1, data, @markpos, p-@markpos)
            record.filled = 1:(p-@markpos)
            found_record = true
            @escape
        end,
        :countline => :(linenum += 1),
    )),
    initcode = initcode,
    loopcode = loopcode,
    returncode = :(return cs, linenum, found_record)
) |> eval

function index!(record::Record)
    stream = TranscodingStreams.NoopStream(IOBuffer(record.data))
    cs = index!(stream, record)
    if cs < 0
        throw(ArgumentError("invalid record"))
    end
    return record
end

"""
    read!(rdr::Reader, rec::Record)
Read a `Record` into `rec`; overwriting or adding to existing field values.
It is assumed that `rec` is already initialized or empty.
"""
function Base.read!(rdr::Reader, record::Record)

    cs, ln, found = readrecord!(rdr.state.stream, record, (rdr.state.state, rdr.state.linenum))

    rdr.state.state = cs
    rdr.state.linenum = ln
    rdr.state.filled = found

    if found
        return record
    end

    if cs == 0 || eof(rdr.state.stream)
        throw(EOFError())
    end

    throw(ArgumentError("malformed file"))
end


text:
using Compat
using Base.Test

v = 1
@test_throws AssertionError @assert(v < 1)

d = Dict{Int,Int}()
d[1] = 1
@test Compat.@Dict(1 => 1) == d
@test Compat.@Dict(1 => v) == d

@test typeof(@compat(Dict(1 => 1))) == Dict{Int,Int}
@test @compat(Dict(1 => 1)) == d
@test @compat(Dict(1 => v)) == d

ad = Dict{Any,Any}()
ad[1] = 1
@test Compat.@AnyDict(1 => 1) == ad
@test Compat.@AnyDict(1 => v) == ad

@test typeof(@compat(Dict{Any,Any}(1 => 1))) == Dict{Any,Any}
@test @compat(Dict{Any,Any}(1 => 1)) == ad
@test @compat(Dict{Any,Any}(1 => v)) == ad

td = Dict{Int,Float64}()
td[1] = 1.0

@test typeof(@compat(Dict{Int,Float64}(1 => 1))) == Dict{Int,Float64}
@test @compat(Dict{Int,Float64}(1 => 1)) == td
@test @compat(Dict{Int,Float64}(1 => v)) == td

@test @compat(Dict()) == Dict()
@test @compat(Dict{Any,Any}()) == Dict{Any,Any}()
if VERSION >= v"0.3.0-"
    @test @compat(Dict([(1, 1)])) == d
end

d2 = Dict{Symbol,Dict{Symbol,Int}}()
d2[:a] = Dict{Symbol,Int}()
d2[:a][:b] = 1
@test @compat(Dict(:a => Dict(:b => 1))) == d2

d = Dict(zip([1, 2], [3, 4]))
@test d == @compat Dict(1=>3, 2=>4)

@compat function f()
    a = :a
    b = Dict(:b => 1)
    Dict(a => b)
end
@test f() == d2

ns = length(d.slots)
@test length(sizehint!(d, ns + 1).slots) > ns

@test @compat split("a,b,,c", ',', limit=2) == ["a", "b,,c"]
@test @compat split("a,b,,c", ',', limit=2,keep=true) == ["a", "b,,c"]
@test @compat split("a,b,,c", ',', keep=false) == ["a", "b", "c"]
@test @compat split("a,b,,c", ',', keep=true) == ["a", "b", "", "c"]

@test @compat rsplit("a,b,,c", ',', limit=2) == ["a,b,", "c"]
@test @compat rsplit("a,b,,c", ',', limit=2,keep=true) == ["a,b,", "c"]
@test @compat rsplit("a,b,,c", ',', keep=false) == ["a", "b", "c"]
@test @compat rsplit("a,b,,c", ',', keep=true) == ["a", "b", "", "c"]

if VERSION < v"0.4.0-dev+1387"
    @test isdefined(Main, :AbstractString)
end

@test round(Int, 3//4) == 1
@test round(Int, 1) == 1
@test round(Int, 1.1) == 1
@test round(Int, [1, 1]) == [1, 1]
@test round(Int, [1.1, 1.1]) == [1, 1]
@test round(Int, [1 1]) == [1 1]
@test round(Int, [1.1 1.1]) == [1 1]
@test round(Int, fill(1.1, 2, 3, 4)) == fill(1, 2, 3, 4)
@test ceil(Int, 1) == 1
@test ceil(Int, 1.1) == 2
@test ceil(Int, [1, 1]) == [1, 1]
@test ceil(Int, [1.1, 1.1]) == [2, 2]
@test ceil(Int, [1 1]) == [1 1]
@test ceil(Int, [1.1 1.1]) == [2 2]
@test ceil(Int, fill(1.1, 2, 3, 4)) == fill(2, 2, 3, 4)
@test floor(Int, 1) == 1
@test floor(Int, 1.1) == 1
@test floor(Int, [1, 1]) == [1, 1]
@test floor(Int, [1.1, 1.1]) == [1, 1]
@test floor(Int, [1 1]) == [1 1]
@test floor(Int, [1.1 1.1]) == [1 1]
@test floor(Int, fill(1.1, 2, 3, 4)) == fill(1, 2, 3, 4)
@test trunc(Int, 1) == 1
@test trunc(Int, 1.1) == 1
@test trunc(Int, [1, 1]) == [1, 1]
@test trunc(Int, [1.1, 1.1]) == [1, 1]
@test trunc(Int, [1 1]) == [1 1]
@test trunc(Int, [1.1 1.1]) == [1 1]
@test trunc(Int, fill(1.1, 2, 3, 4)) == fill(1, 2, 3, 4)

# n % Type
for T in Any[Int16, Int32, UInt32, Int64, UInt64]
    if !(T <: Unsigned)
        @test convert(T, -200) %  Int8 === @compat Int8(56)
        @test convert(T, -200) % UInt8 === 0x38
        @test convert(T, -300) %  Int8 === @compat Int8(-44)
        @test convert(T, -300) % UInt8 === 0xd4
        @test convert(T, -128) %  Int8 === @compat Int8(-128)
        @test convert(T, -128) % UInt8 === 0x80
    end
    @test convert(T,  127) %  Int8 === @compat Int8(127)
    @test convert(T,  127) % UInt8 === 0x7f
    @test convert(T,  128) %  Int8 === @compat Int8(-128)
    @test convert(T,  128) % UInt8 === 0x80
    @test convert(T,  200) %  Int8 === @compat Int8(-56)
    @test convert(T,  300) % UInt8 === 0x2c
end

@test IPv4("1.2.3.4") == ip"1.2.3.4"
@test IPv6("2001:1:2:3::1") == ip"2001:1:2:3::1"
@test isless(ip"1.2.3.4", ip"1.2.3.5")

@test startswith("abcdef","abc") == true
@test startswith("abcdef","def") == false

@test size(bitrand(3, 4)) == (3, 4)
@test size(bitrand((3, 4))) == (3, 4)
@test size(bitrand(MersenneTwister(), 3, 4)) == (3, 4)
@test size(bitrand(MersenneTwister(), (3, 4))) == (3, 4)
@test rand(Bool) in [false, true]

rng = MersenneTwister()
srand()
srand(rng, UInt32[0,0])
srand(rng)
for Tr in (Int8,UInt8,Int32,UInt32,Int64,UInt64,Int128,UInt128,Float16,Float32,Float64)
    for T in (Tr, Complex{Tr})
        @test isa(rand(rng, T), T)
        let x = rand(rng, T, 3,4)
            @test isa(x, Array{T,2})
            @test size(x) == (3,4)
            rand!(rng, x)
        end
    end
end
srand(rng, 0)
let x = rand(rng, Int64, 3,4)
    srand(rng, 0)
    @test x == rand(rng, Int64, (3,4))
end

module CartesianTest
    using Base.Cartesian, Compat
    @ngenerate N NTuple{N,Int} function f(X::NTuple{N,Int}...)
        @ncall N tuple X
    end
end

@test CartesianTest.f(1) == (1,)
@test CartesianTest.f(1,2) == (1,2)
@test CartesianTest.f(1,2,3) == (1,2,3)
@test CartesianTest.f(1,2,3,4) == (1,2,3,4)
@test CartesianTest.f(1,2,3,4,5) == (1,2,3,4,5)

@test readall(pipe(`echo hello`, `sort`)) == "hello\n"
@test success(pipe(`true`, `true`))

let convert_funcs_and_types =
    ((integer, :Integer), (signed, :Signed), (unsigned, :Unsigned),
     (int, :Int), (int8, :Int8), (int16, :Int16), (int32, :Int32),
     (int64, :Int64), (int128, :Int128), (uint, :UInt),
     (uint8, :UInt8), (uint16, :UInt16), (uint32, :UInt32),
     (uint64, :UInt64), (uint128, :UInt128),
     (float16, :Float16), (float32, :Float32), (float64, :Float64),
     (complex32,:Complex32), (complex64,:Complex64),(complex128,:Complex128),
     (char,:Char))

    for (df,t) in convert_funcs_and_types
        x = @compat UInt8(10)
        r1 = eval(:(@compat($t($x))))
        ty = eval(t)
        if ty.abstract
            @test issubtype(typeof(r1),ty)
        else
            @test typeof(r1) === ty
        end
        if VERSION <  v"0.4.0-dev+3732"
            r2 = df(x)
            @test r1 === r2
            if t === :Signed || t === :Complex32
                continue
            end
            x = fill(x, 10)
            r1 = eval(:(@compat map($t, $x)))
            r2 = df(x)
            @test r1 == r2
            @test typeof(r1) === typeof(r2)
        end
    end
    @test (@compat Bool(1))
    @test !(@compat Bool(0))

    # issue #54
    c = @compat Complex128(1)
    @test c.re == 1
    @test c.im == 0

    c = @compat Complex128(1,2)
    @test c.re == 1
    @test c.im == 2

    c = @compat Complex128(1+2im)
    @test c.re == 1
    @test c.im == 2
end

type Test3609
    a
    b
end

if VERSION < v"0.4.0-dev+3609"
    let v = Test3609(1,2)
        @test fieldnames(Test3609) == fieldnames(v) == Symbol[:a, :b]
    end
end

@test parse(Int8, '9') == convert(Int8, 9)
@test parse(Int, 'a', 16) == 10
@test parse(Int, "200") == 200
@test parse(Int16, "1101", 2) == convert(Int16, 13)
@test parse(Float64, "222") == 222.0
@test parse(Float32, "1.1") == convert(Float32, 1.1)
@test parse(BigFloat, "1.125") == convert(BigFloat, 1.125)
@test isa(tryparse(Float32, "1.1"), Nullable)
@test get(tryparse(Float32, "1.1")) == 1.1f0
@test isa(tryparse(Float32, "a"), Nullable{Float32})
@test isa(tryparse(Float64, "1.1"), Nullable)
@test get(tryparse(Float64, "1.1")) == 1.1
@test isa(tryparse(Float64, "a"), Nullable{Float64})
@test get(tryparse(Int32, "1")) == 1
@test isa(tryparse(Int32, "a"), Nullable{Int32})
@test get(tryparse(Int64, "1")) == 1
@test isa(tryparse(Int64, "a"), Nullable{Int64})

# Make sure exports from Libc and Libdl are defined
for x in [:strftime,:systemsleep,:getpid,:FILE,:malloc,:flush_cstdio,:realloc,:strptime,:Libc,:errno,:msync,:TmStruct,:calloc,:time,:strerror,:gethostname,:free]
    Libc.(x)
end
for x in [:RTLD_LOCAL,:RTLD_GLOBAL,:find_library,:dlsym,:RTLD_LAZY,:RTLD_NODELETE,:DL_LOAD_PATH,:RTLD_NOW,:Libdl,:dlext,:dlsym_e,:RTLD_FIRST,:dlopen,:dllist,:dlpath,:RTLD_NOLOAD,:dlclose,:dlopen_e,:RTLD_DEEPBIND]
    Libdl.(x)
end

# Test unsafe_convert
type A; end
x = "abc"
@test bytestring(Compat.unsafe_convert(Ptr{UInt8}, x)) == x
Compat.unsafe_convert(::Ptr{A}, x) = x
@test Compat.unsafe_convert(pointer([A()]), 1) == 1

# Test Ptr{T}(0)
@test @compat(Ptr{Int}(0)) == C_NULL

# Test Tuple{} syntax
if VERSION < v"0.4.0-dev+4319"
    @test @compat Tuple{1} == (1,)
    @test @compat Tuple{:a, :b} == (:a, :b)
    @test @compat Tuple{:a, Tuple{:b}} == (:a, (:b,))
    @test @compat Tuple{:a, Tuple{:b, :c}} == (:a, (:b, :c))
    @test @compat Tuple{Int} == (Int,)
    @test @compat Tuple{Int, Float64} == (Int, Float64)
    @test @compat Tuple{Int, Tuple{Float64}} == (Int, (Float64,))
    @test @compat Tuple{Int, Tuple{Float64, Char}} == (Int, (Float64, Char))
    @test @compat Tuple{Int, Vararg{Float64}} == (Int, Float64...)
    # Issue 81
    a81 = [Int, Int]
    b81 = (Int, Int)
    @test @compat Tuple{a81..., Vararg{Float64}} == (Int, Int, Float64...)
    @test @compat Tuple{b81..., Vararg{Float64}} == (Int, Int, Float64...)
end

# Ensure eachindex iterates over the whole array
let A, B, s
    A = reshape(1:20,4,5)
    s = 0
    for i in eachindex(A)
        s += A[i]
    end
    @test s == 210
    B = sparse(A)
    s = 0
    for i in eachindex(B)
        s += B[i]
    end
    @test s == 210
end

let
    d = @compat Dict(1=>2, 3=>4)
    d[5] = 6
    val = 0
    for I in eachindex(d)
        val += d[I]
    end
    @test val == 12
    empty!(d)
    for I in eachindex(d)
        val += d[I]
    end
    @test val == 12
end

# findlast, findprev
let a = [0,1,2,3,0,1,2,3]
    @test findlast(a) == 8
    @test findlast(a.==0) == 5
    @test findlast(a.==5) == 0
    @test findlast([1,2,4,1,2,3,4], 3) == 6
    @test findlast(isodd, [2,4,6,3,9,2,0]) == 5
    @test findlast(isodd, [2,4,6,2,0]) == 0
    @test findprev(a,4) == 4
    @test findprev(a,5) == 4
    @test findprev(a,1) == 0
    @test findprev(a,1,4) == 2
    @test findprev(a,1,8) == 6
    @test findprev(isodd, [2,4,5,3,9,2,0], 7) == 5
    @test findprev(isodd, [2,4,5,3,9,2,0], 2) == 0
end

# isdiag
@test isdiag(diagm([1,2,3,4]))
@test !isdiag([1 2; 3 4])
@test isdiag(5)

# keytype & valtype
if VERSION < v"0.4.0-dev+4502"
    @test keytype(@compat(Dict(1 => 1.))) == Int
    @test valtype(@compat(Dict(1 => 1.))) == Float64
end

# Val
begin
    local firstlast
    firstlast(::Type{Val{true}}) = "First"
    firstlast(::Type{Val{false}}) = "Last"

    @test firstlast(Val{true}) == "First"
    @test firstlast(Val{false}) == "Last"
end

# Cstring
let s = "foo", w = wstring("foo")
    @test reinterpret(Ptr{Cchar}, Compat.unsafe_convert(Cstring, s)) == pointer(s)
    @test reinterpret(Ptr{Cwchar_t}, Compat.unsafe_convert(Cwstring, w)) == pointer(w)
end

# fma and muladd
@test fma(3,4,5) == 3*4+5 == muladd(3,4,5)

# is_valid_utf32
s = utf32("abc")
@test isvalid(s)
s = utf32(UInt32[65,0x110000])
@test !isvalid(s)

# isvalid
let s = "abcdef", u8 = "abcdef\uff", u16 = utf16(u8), u32 = utf32(u8),
    bad32 = utf32(UInt32[65,0x110000]), badch = Char[0x110000][1]

    @test !isvalid(bad32)
    @test !isvalid(badch)
    @test isvalid(s)
    @test isvalid(u8)
    @test isvalid(u16)
    @test isvalid(u32)
    @test isvalid(ASCIIString, s)
    @test isvalid(UTF8String,  u8)
    @test isvalid(UTF16String, u16)
    @test isvalid(UTF32String, u32)
end

# chol
let A = rand(2,2)
    B = A'*A
    U = @compat chol(B, Val{:U})
    @test_approx_eq U'*U B
end

# @generated
if VERSION > v"0.3.99"
    let
        @compat @generated function foo(x)
            T = x
            :(return $T)
        end
        @test foo(5) == Int
    end
end

# Timer
let c = 0, f, t
    @compat f(t::Timer) = (c += 1)
    t = Timer(f, 0.0, 0.05)
    sleep(0.05)
    @test c >= 1
    sleep(0.1)
    @test c >= 3
    close(t)
    sleep(0.1)
    val = c
    sleep(0.1)
    @test val == c
end

# MathConst -> Irrational
f(::Irrational) = true
@test f(π)
Compat.@irrational mathconst_one 1.0 big(1.)
@test f(mathconst_one)

# gc_enable(::Bool)
@test gc_enable(false)
@test !gc_enable(true)

# Vector{Int}(), Array{Int}

@test @compat typeof(Vector{Int}()) == Array{Int,1}
@test @compat length(Vector{Int}()) == 0

@test @compat typeof(Vector{Int8}(10)) == Array{Int8,1}
@test @compat length(Vector{Int8}(10)) == 10

@test @compat typeof(Array{UInt16}()) == Array{UInt16,0}
@test @compat length(Array{UInt16}()) == 1

@test @compat typeof(Array{UInt16}(0)) == Array{UInt16,1}
@test @compat length(Array{UInt16}(0)) == 0

@test @compat typeof(Array{Float16}(5)) == Array{Float16,1}
@test @compat length(Array{Float16}(5)) == 5

@test @compat typeof(Array{String}(2,2)) == Array{String,2}
@test @compat size(Array{String}(2,2)) == (2,2)

@test @compat typeof(Array{Rational{Int}}(2,2,2,2,2)) == Array{Rational{Int},5}
@test @compat size(Array{Rational{Int}}(2,2,2,2,2)) == (2,2,2,2,2)

@compat utf8(Mmap.mmap(@__FILE__(),Vector{Uint8},11,1)) == "sing Compat"

@test base64encode("hello world") == "aGVsbG8gd29ybGQ="

@test nothing === __precompile__(false) # tests should never be precompiled
@test nothing === include_dependency("foo")

@test real(Int) == real(Complex{Int}) == Int

@test isa(1.2, AbstractFloat)

@test [1,2,3] ≈ [1,2,3+1e-9]
@test [0,1] ≈ [1e-9, 1]
@test [0,1] ≉ [1e-3, 1]


text:
struct MCMC_chain
   chain::Chains
   summaries
   quantiles
end

function Logit_Link(z::Real)
   1 / (1 + exp(-z))
end

function Cauchit_Link(z::Real)
   0.5+atan(z)/π
end

function Probit_Link(z::Real)
   d = Distributions.Normal(0,1)
   cdf(d,z)
end

function Cloglog_Link(z::Real)
  1-exp(-exp(z))
end

function NegativeBinomial2(μ, ϕ)
   p = 1 / (1 + μ / ϕ)
   r = ϕ

   return NegativeBinomial(r, p)
end


text:
# This file includes testset for RODESystem

import DifferentialEquations.RandomEM

@testset "RODESystemTestSet" begin
    @info "Running RODESystemTestSet ..."

    # RODESystem construction
    function statefunc(dx, x, u, t, W)
        dx[1] = 2x[1]*sin(W[1] - W[2])
        dx[2] = -2x[2]*cos(W[1] + W[2])
    end
    outputfunc(x, u, t) = x
    ds = RODESystem(righthandside=statefunc, readout=outputfunc, state=ones(2), input=nothing, output=Outport(2),
        alg=RandomEM())
    ds = RODESystem(righthandside=statefunc, readout=outputfunc, state=ones(2), input=nothing, output=Outport(2),
        alg=RandomEM(), modelkwargs=(rand_prototype=zeros(2),))
    @test typeof(ds.trigger) == Inpin{Float64}
    @test typeof(ds.handshake) == Outpin{Bool}
    @test ds.input === nothing
    @test isa(ds.output, Outport)
    @test length(ds.output) == 2
    @test ds.state == ones(2)
    @test ds.integrator.sol.prob.p === nothing

    # Driving RODESystem
    iport = Inport(2)
    trg = Outpin()
    hnd = Inpin{Bool}()
    connect!(ds.output, iport)
    connect!(trg, ds.trigger)
    connect!(ds.handshake, hnd)
    tsk = launch(ds)
    tsk2 = @async while true
        all(take!(iport) .=== NaN) && break
    end
    for t in 1 : 10
        put!(trg, t)
        take!(hnd)
        @test ds.t == t
        @test [read(pin.link.buffer) for pin in iport] == ds.state
    end
    put!(trg, NaN)
    sleep(0.1)
    @test istaskdone(tsk)
    put!(ds.output, NaN * ones(length(ds.output)))
    sleep(0.1)
    @test istaskdone(tsk2)

    # Driving RODESystem with input
    function sfunc(dx, x, u, t, W)
        dx[1] = 2x[1]*sin(W[1] - W[2]) + cos(u[1](t))
        dx[2] = -2x[2]*cos(W[1] + W[2]) - sin(u[1](t))
    end
    ofunc(x, u, t) = x
    ds = RODESystem(righthandside=sfunc, readout=ofunc, state=ones(2), input=Inport(2), output=Outport(2),
        modelkwargs=(rand_prototype=zeros(2),))
    @test typeof(ds.integrator.sol.prob.p) <: Interpolant
    @test size(ds.integrator.sol.prob.p.timebuf) == (3,)
    @test size(ds.integrator.sol.prob.p.databuf) == (2,3)
    oport = Outport(2)
    iport = Inport(2)
    trg = Outpin()
    hnd = Inpin{Bool}()
    connect!(oport, ds.input)
    connect!(ds.output, iport)
    connect!(trg, ds.trigger)
    connect!(ds.handshake, hnd)
    tsk = launch(ds)
    tsk2 = @async while true
        all(take!(iport) .=== NaN) && break
    end
    for t in 1 : 10
        put!(trg, t)
        put!(oport, [t, 2t])
        take!(hnd)
        @test ds.t == t
        @test [read(pin.link.buffer) for pin in iport] == ds.state
    end
    put!(trg, NaN)
    sleep(0.1)
    @test istaskdone(tsk)
    put!(ds.output, NaN * ones(length(ds.output)))
    sleep(0.1)
    @test istaskdone(tsk2)

    # Test defining new RODESystem types
    # The type must be mutable
    @test_throws Exception @eval @def_rode_system struct RODESystem{RH, RO, ST, IP, OP}
        righthandside::RH
        readout::RO
        state::ST
        input::IP
        output::OP
    end

    # The type must be of type AbstractRODESystem
    @test_throws Exception @eval @def_rode_system mutable struct RODESystem{RH, RO, ST, IP, OP}
        righthandside::RH
        readout::RO
        state::ST
        input::IP
        output::OP
    end

    # The type must be of type AbstractRODESystem
    @test_throws Exception @eval @def_rode_system mutable struct RODESystem{RH, RO, ST, IP, OP} <: MyDummyAbstractRODESystem
        righthandside::RH
        readout::RO
        state::ST
        input::IP
        output::OP
    end

    @info "Done RODESystemTestSet ..."
end  # testset


text:
#!/usr/bin/env julia

using CUDAdrv

const gpus = Dict{String,Int}()
for (i,device) in enumerate(devices())
    id = name(device)
    get!(gpus, id, i-1)
end

const ITERATIONS = length(ARGS) >= 1 ? parse(Int, ARGS[1]) : 128

cd(@__DIR__) do
    for (gpu,i) in gpus
        @info "Collecting data for $gpu (device $i)"
        withenv("CUDA_VISIBLE_DEVICES" => i) do
            run(
                ignorestatus(
                    `julia --depwarn=no --compiled-modules=no collect.jl $ITERATIONS $gpu`
                )
            )
        end
    end
end


text:
using Documenter
using SnoopCompile

makedocs(
    sitename = "SnoopCompile",
    format = Documenter.HTML(
        prettyurls = get(ENV, "CI", nothing) == "true"
    ),
    modules = [SnoopCompile.SnoopCompileCore, SnoopCompile],
    linkcheck = true,
    pages = ["index.md",
             "tutorial.md",
             "Modern tools" => ["snoopr.md", "snoopi_deep.md", "snoopi_deep_analysis.md", "snoopi_deep_parcel.md"],
             "Older tools" => ["snoopi.md", "snoopc.md"],
             "userimg.md",
             "reference.md"]
)

deploydocs(
    repo = "github.com/timholy/SnoopCompile.jl.git",
    push_preview=true
)


text:
function jlfiles(dir::AbstractString)
    paths = String[]
    for n in readdir(dir)
        p = joinpath(dir, n)
        if isdir(p)
            append!(paths, jlfiles(p))
        elseif endswith(p, ".jl")
            push!(paths, p)
        end
    end
    return paths
end

function find_pkgdir(start::AbstractString = ".")
    dir = abspath(start)
    while true
        isfile(joinpath(dir, "Project.toml")) && return dir

        next = dirname(dir)
        next === dir && error("$start is not inside a Julia project.")
        dir = next
    end
end

zipoffsets(args, offset) =
    zip(args, insert!(cumsum([x.fullspan for x in args[1:end-1]]), 1, 0) .+ offset)

function firstmatch(f, xs)
    for x in xs
        f(x) && return Some(x)
    end
    return nothing
end

ifnothing(f) = x -> ifnothing(f, x)
ifnothing(f, ::Nothing) = f()
ifnothing(_, x) = something(x)

_project_toml_path_from_dir(project) =
    firstmatch(
        isfile,
        (
         # Candidate locations of project TOML file:
         joinpath(project, "Project.toml"),
         joinpath(project, "JuliaProject.toml"),
        ),
    ) |> ifnothing() do
        error(
            "Directory `$project` does not have a `Project.toml` or",
            " `JuliaProject.toml` file.",
        )
    end

as_project_toml_path(project) =
    if isdir(project)
        _project_toml_path_from_dir(project)
    elseif basename(project) ∈ ("Manifest.toml", "JuliaManifest.toml")
        _project_toml_path_from_dir(dirname(project))
    else
        project
    end


text:
using Viznet
using Compose
using Random
Random.seed!(4)

function spinglass(lt, coulping::Vector)
    nodebrush = nodestyle(:default; r=0.011)
    eb1 = bondstyle(:default, linewidth(1mm), stroke("skyblue"))
    eb2 = bondstyle(:default, linewidth(1mm), stroke("orange"))
    _coupling = copy(coulping)
    eb() = popfirst!(_coupling) > 0 ? eb1 : eb2
    g = canvas() do
        for v in vertices(lt)
            nodebrush >> lt[v]
        end
        for j=1:lt.Ny-1
            eb() >> lt[(1,j); (1,j+1)]
        end
        for i=2:lt.Nx
            for j=1:lt.Ny
                eb() >> lt[(i-1,j); (i,j)]
            end
            for j=1:lt.Ny-1
                eb() >> lt[(i,j); (i,j+1)]
            end
        end
    end
    compose(context(), g, )
end

L = 20
lt = SquareLattice(L, L)
set_default_graphic_size(12cm, 12cm)
spinglass(lt, rand([-1, 1], L*(L-1)*2)) |> SVG("_spinglass.svg")


text:
function Preprocess2(A,b,C,d,XLB,XUB,VariableDef;TimeLim = 999999)
    t0 = time_ns();
    GUB = Inf
    nObj = size(C)[1];
    nVar = size(A)[2];

    Cuts = [];Xs = zeros(nVar); Ys = zeros(nObj)
    while true
        Lambda = abs.(randn(nObj))
        Lambda = Lambda./sum(Lambda)
        # Time commands
        # ---------------------------------------------
        Time = (time_ns() - t0)/1e9
        if TimeLim-Time < 0.001;
            return Xs, Ys, GUB, Cuts
        end
        # ---------------------------------------------
        Y,X, Model = WSM(A,b,C,d,XLB,XUB,VariableDef,Lambda,TimeLim = TimeLim-Time)
        if Y == []
            return Xs, Ys, GUB, Cuts
        end

        if GUB > prod(Y)
            GUB = prod(Y); Xs = copy(X); Ys = copy(Y);
        end
        push!(Cuts,CutType(Lambda,sum(Lambda.*Y)))
        # Time commands
        # ---------------------------------------------
        Time = (time_ns() - t0)/1e9
        if TimeLim-Time < 0.001;
            return Xs, Ys, GUB, Cuts
        end
        # ---------------------------------------------

    end
end


text:
function codegen(::Type{Val{:iterate}}, ::Type{HMC}, job::BasicMCJob)
  result::Expr
  update = []
  noupdate = []
  burninbody = []
  ifburninbody = []
  body = []
  dindex::Integer

  vform = variate_form(job.pstate)
  if vform != Univariate && vform != Multivariate
    error("Only univariate or multivariate parameter states allowed in HMC code generation")
  end

  if isa(job.tuner, DualAveragingMCTuner)
    push!(body, :(_job.sstate.count += 1))
  end

  if (isa(job.tuner, VanillaMCTuner) && job.tuner.verbose) ||
    isa(job.tuner, AcceptanceRateMCTuner) ||
    (isa(job.tuner, DualAveragingMCTuner) && job.tuner.verbose)
    push!(body, :(_job.sstate.tune.proposed += 1))
  end

  if vform == Univariate
    push!(body, :(_job.sstate.momentum = randn()))
  elseif vform == Multivariate
    push!(body, :(_job.sstate.momentum[:] = randn(_job.pstate.size)))
  end

  push!(body, :(_job.sstate.oldhamiltonian = hamiltonian(_job.pstate.logtarget, _job.sstate.momentum)))

  if vform == Univariate
    push!(body, :(_job.sstate.pstate.value = _job.pstate.value))
    push!(body, :(_job.sstate.pstate.gradlogtarget = _job.pstate.gradlogtarget))
  elseif vform == Multivariate
    push!(body, :(_job.sstate.pstate.value = copy(_job.pstate.value)))
    push!(body, :(_job.sstate.pstate.gradlogtarget = copy(_job.pstate.gradlogtarget)))
  end

  if isa(job.tuner, DualAveragingMCTuner)
    push!(body, :(_job.sstate.nleaps = max(1, Int(round(_job.sstate.tune.λ/_job.sstate.tune.step)))))
  end

  if vform == Univariate
    push!(body, :(
      for i in 1:_job.sstate.nleaps
        _job.sstate.momentum =
          leapfrog!(
            _job.sstate.pstate,
            _job.sstate.pstate,
            _job.sstate.momentum,
            _job.sstate.tune.step,
            _job.parameter.gradlogtarget!
          )
      end
    ))
  elseif vform == Multivariate
    push!(body, :(
      for i in 1:_job.sstate.nleaps
        leapfrog!(
          _job.sstate.pstate,
          _job.sstate.momentum,
          _job.sstate.pstate,
          _job.sstate.momentum,
          _job.sstate.tune.step,
          _job.parameter.gradlogtarget!
        )
      end
    ))
  end

  push!(body, :(_job.parameter.logtarget!(_job.sstate.pstate)))

  push!(body, :(_job.sstate.newhamiltonian = hamiltonian(_job.sstate.pstate.logtarget, _job.sstate.momentum)))

  push!(body, :(_job.sstate.ratio = _job.sstate.newhamiltonian-_job.sstate.oldhamiltonian))

  push!(body, :(_job.sstate.a = min(1., exp(_job.sstate.ratio))))

  if vform == Univariate
    push!(update, :(_job.pstate.value = _job.sstate.pstate.value))
    push!(update, :(_job.pstate.gradlogtarget = _job.sstate.pstate.gradlogtarget))
    if in(:gradloglikelihood, job.outopts[:monitor]) && job.parameter.gradloglikelihood! != nothing
      push!(update, :(_job.pstate.gradloglikelihood = _job.sstate.pstate.gradloglikelihood))
    end
    if in(:gradlogprior, job.outopts[:monitor]) && job.parameter.gradlogprior! != nothing
      push!(update, :(_job.pstate.gradlogprior = _job.sstate.pstate.gradlogprior))
    end
  elseif vform == Multivariate
    push!(update, :(_job.pstate.value = copy(_job.sstate.pstate.value)))
    push!(update, :(_job.pstate.gradlogtarget = copy(_job.sstate.pstate.gradlogtarget)))
    if in(:gradloglikelihood, job.outopts[:monitor]) && job.parameter.gradloglikelihood! != nothing
      push!(update, :(_job.pstate.gradloglikelihood = copy(_job.sstate.pstate.gradloglikelihood)))
    end
    if in(:gradlogprior, job.outopts[:monitor]) && job.parameter.gradlogprior! != nothing
      push!(update, :(_job.pstate.gradlogprior = copy(_job.sstate.pstate.gradlogprior)))
    end
  end
  push!(update, :(_job.pstate.logtarget = _job.sstate.pstate.logtarget))
  if in(:loglikelihood, job.outopts[:monitor]) && job.parameter.loglikelihood! != nothing
    push!(update, :(_job.pstate.loglikelihood = _job.sstate.pstate.loglikelihood))
  end
  if in(:logprior, job.outopts[:monitor]) && job.parameter.logprior! != nothing
    push!(update, :(_job.pstate.logprior = _job.sstate.pstate.logprior))
  end
  dindex = findfirst(job.outopts[:diagnostics], :accept)
  if dindex != 0
    push!(update, :(_job.pstate.diagnosticvalues[$dindex] = true))
    push!(noupdate, :(_job.pstate.diagnosticvalues[$dindex] = false))
  end
  if (isa(job.tuner, VanillaMCTuner) && job.tuner.verbose) ||
    isa(job.tuner, AcceptanceRateMCTuner) ||
    (isa(job.tuner, DualAveragingMCTuner) && job.tuner.verbose)
    push!(update, :(_job.sstate.tune.accepted += 1))
  end

  push!(body, Expr(:if, :(rand() < _job.sstate.a), Expr(:block, update...), noupdate...))

  if (isa(job.tuner, VanillaMCTuner) && job.tuner.verbose) || isa(job.tuner, AcceptanceRateMCTuner)
    push!(burninbody, :(rate!(_job.sstate.tune)))

    if isa(job.tuner, AcceptanceRateMCTuner)
      push!(burninbody, :(tune!(_job.sstate.tune, _job.tuner)))
    end

    if job.tuner.verbose
      fmt_iter = format_iteration(ndigits(job.range.burnin))
      fmt_perc = format_percentage()

      push!(burninbody, :(println(
        "Burnin iteration ",
        $(fmt_iter)(_job.sstate.tune.totproposed),
        " of ",
        _job.range.burnin,
        ": ",
        $(fmt_perc)(100*_job.sstate.tune.rate),
        " % acceptance rate"
      )))
    end

    push!(burninbody, :(reset_burnin!(_job.sstate.tune)))

    push!(
      body,
      Expr(
        :if,
        :(_job.sstate.tune.totproposed <= _job.range.burnin && mod(_job.sstate.tune.proposed, _job.tuner.period) == 0),
        Expr(:block, burninbody...)
      )
    )
  elseif isa(job.tuner, DualAveragingMCTuner)
    push!(burninbody, :(tune!(_job.sstate.tune, _job.tuner, _job.sstate.count, _job.sstate.a)))

    if job.tuner.verbose
      fmt_iter = format_iteration(ndigits(job.tuner.nadapt))
      fmt_perc = format_percentage()

      push!(ifburninbody, :(rate!(_job.sstate.tune)))

      push!(ifburninbody, :(println(
        "Burnin iteration ",
        $(fmt_iter)(_job.sstate.tune.totproposed),
        " of ",
        _job.tuner.nadapt,
        ": ",
        $(fmt_perc)(100*_job.sstate.tune.rate),
        " % acceptance rate"
      )))

      push!(ifburninbody, :(reset_burnin!(_job.sstate.tune)))

      push!(burninbody, Expr(:if, :(mod(_job.sstate.tune.proposed, _job.tuner.period) == 0), Expr(:block, ifburninbody...)))
    end

    push!(
      body,
      Expr(
        :if,
        :(_job.sstate.count <= _job.tuner.nadapt),
        Expr(:block, burninbody...),
        :(_job.sstate.tune.step = _job.sstate.tune.εbar)
      )
    )
  end

  if !job.plain
    push!(body, :(produce()))
  end

  @gensym _iterate

  result = quote
    function $_iterate(_job::BasicMCJob)
      $(body...)
    end
  end

  result
end


text:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
# Description
# ==============================================================================
#
#   Structures related to the atmospheric models.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

#                             Jacchia-Bowman 2008
# ==============================================================================

export JB2008_Output

"""
    JB2008_Output

Output structure of the Jacchia-Bowman 2008.

# Fields

* `nN2`: Number density of N₂ [1/m³].
* `nO2`: Number density of O₂ [1/m³].
* `nO`: Number density of O [1/m³].
* `nAr`: Number density of Ar [1/m³].
* `nHe`: Number density of He [1/m³].
* `nH`: Number density of H [1/m³].
* `rho`: Total density [kg/m³].
* `T_exo`: Exospheric temperature [K].
* `Tz`: Temperature at the selected altitude [K].

"""
@with_kw struct JB2008_Output{T}
    nN2::T
    nO2::T
    nO::T
    nAr::T
    nHe::T
    nH::T
    rho::T
    T_exo::T
    Tz::T
end

#                             Jacchia-Roberts 1971
# ==============================================================================

export JR1971_Output

"""
    JR1971_Output

Output structure of the Jacchia-Roberts 1971 model.

# Fields

* `nN2`: Number density of N₂ [1/m³].
* `nO2`: Number density of O₂ [1/m³].
* `nO`: Number density of O [1/m³].
* `nAr`: Number density of Ar [1/m³].
* `nHe`: Number density of He [1/m³].
* `nH`: Number density of H [1/m³].
* `rho`: Total density [kg/m³].
* `T_exo`: Exospheric temperature [K].
* `Tz`: Temperature at the selected altitude [K].

"""
@with_kw struct JR1971_Output{T}
    nN2::T
    nO2::T
    nO::T
    nAr::T
    nHe::T
    nH::T
    rho::T
    T_exo::T
    Tz::T
end

#                                 NRLMSISE-00
# ==============================================================================

export NRLMSISE00_Structure, NRLMSISE00_Flags, NRLMSISE00_Output

"""
    NRLMSISE00_Flags

Flags to configure NRLMSISE-00.

# Fields

* `output_m_kg`
* `F107_Mean`
* `time_independent`
* `sym_annual`
* `sym_semiannual`
* `asym_annual`
* `asyn_semiannual`
* `diurnal`
* `semidiurnal`
* `daily_ap`
* `all_ut_long_effects`
* `longitudinal`
* `ut_mixed_ut_long`
* `mixed_ap_ut_long`
* `terdiurnal`
* `departures_from_eq`
* `all_tinf_var`
* `all_tlb_var`
* `all_tn1_var`
* `all_s_var`
* `all_tn2_var`
* `all_nlb_var`
* `all_tn3_var`
* `turbo_scale_height`
* `use_ap_array`

"""
@with_kw mutable struct NRLMSISE00_Flags
    output_m_kg::Bool         = false
    F107_Mean::Bool           = true
    time_independent::Bool    = true
    sym_annual::Bool          = true
    sym_semiannual::Bool      = true
    asym_annual::Bool         = true
    asym_semiannual::Bool     = true
    diurnal::Bool             = true
    semidiurnal::Bool         = true
    daily_ap::Bool            = true
    all_ut_long_effects::Bool = true
    longitudinal::Bool        = true
    ut_mixed_ut_long::Bool    = true
    mixed_ap_ut_long::Bool    = true
    terdiurnal::Bool          = true
    departures_from_eq::Bool  = true
    all_tinf_var::Bool        = true
    all_tlb_var::Bool         = true
    all_tn1_var::Bool         = true
    all_s_var::Bool           = true
    all_tn2_var::Bool         = true
    all_nlb_var::Bool         = true
    all_tn3_var::Bool         = true
    turbo_scale_height::Bool  = true
    use_ap_array::Bool        = false
end

"""
    NRLMSISE00_Structure{T}

Structure with the configuration parameters for NRLMSISE-00 model. It can be
created using the function `conf_nrlmsise00`.

"""
@with_kw mutable struct NRLMSISE00_Structure{T}
    # Inputs
    # ======
    year::Int = 0
    doy::Int  = 0
    sec::T    = 0
    alt::T    = 0
    g_lat::T  = 0
    g_long::T = 0
    lst::T    = 0
    f107A::T  = 0
    f107::T   = 0
    ap::T     = 0
    ap_array::Vector{T} = zeros(T, 7)
    flags::NRLMSISE00_Flags = NRLMSISE00_Flags()

    # Auxiliary variables to improve code performance
    # ===============================================
    re::T          = T(6367.088132098377173)
    gsurf::T       = T(0)
    df::T          = T(0)
    dfa::T         = T(0)
    plg::Matrix{T} = zeros(T, 8, 8)
    ctloc::T       = T(0)
    stloc::T       = T(0)
    c2tloc::T      = T(0)
    s2tloc::T      = T(0)
    s3tloc::T      = T(0)
    c3tloc::T      = T(0)

    # In the original source code, it has 4 components, but only 1 is used.
    apt::T         = T(0)
    apdf::T        = T(0)
    dm28::T        = T(0)
    # The original code declared all the `meso_*` vectors as global variables.
    # However, only two values really need to be shared between the functions
    # `gts7` and `gtd7`.
    meso_tn1_5::T  = T(0)
    meso_tgn1_2::T = T(0)
end

"""
    NRLMSISE00_Output

Output structure for NRLMSISE00 model.

# Fields

* `den_N`: Nitrogen number density [U].
* `den_N2`: N₂ number density [U].
* `den_O`: Oxygen number density [U].
* `den_aO`: Anomalous Oxygen number density [U].
* `den_O2`: O₂ number density [U].
* `den_H`: Hydrogen number density [U].
* `den_He`: Helium number density [U].
* `den_Ar`: Argon number density [U].
* `den_Total`: Total mass density \\[T/U] (this value has different meanings for
               routines `gtd7` and `gtd7d`).
* `T_exo`: Exospheric temperature [K].
* `T_alt`: Temperature at the selected altitude [K].
* `flags`: Flags used to compute NRLMSISE-00 model.

Notice that:

* If `flags.output_m_kg` is `false`, then [U] is [cm⁻³] and [T] is [g/cm⁻³].
* If `flags.output_m_kg` is `true`, then [U] is [m⁻³] and [T] is [kg/m⁻³].

# Remarks

Anomalous oxygen is defined as hot atomic oxygen or ionized oxygen that can
become appreciable at high altitudes (`> 500 km`) for some ranges of inputs,
thereby affection drag on satellites and debris. We group these species under
the term **Anomalous Oxygen**, since their individual variations are not
presently separable with the drag data used to define this model component.

"""
@with_kw struct NRLMSISE00_Output{T}
    # Densities.
    den_N::T
    den_N2::T
    den_O::T
    den_aO::T
    den_O2::T
    den_H::T
    den_He::T
    den_Ar::T
    den_Total::T

    # Temperatures.
    T_exo::T
    T_alt::T

    # Flags.
    flags::NRLMSISE00_Flags
end


text:
module Utils

export batch_outer, add_dim

function batch_outer(A, B)
    num_batch = size(A)[end]
    size(A)[end] == size(B)[end] || throw(DimensionMismatch("batch size mismatch"))
    As = [reshape(A[:, i], :, 1) for i = 1:num_batch]
    Bs = [B[:, i]' for i = 1:num_batch]
    Cs = As .* Bs
    # return reshape(hcat(Cs...), (size(A, 1), size(B, 1), num_batch))
    C = similar(A, size(A, 1), size(B, 1), num_batch)
    for i = 1:num_batch
        C[:, :, i] .= Cs[i]
    end
    return C
end

"""
https://stackoverflow.com/a/59849291/3908797
"""
add_dim(x::AbstractArray{T, N}) where {T, N} = reshape(x, Val(N+1))

end


text:
using BenchmarkTools, AutoGrad

function bench_tr_mul_yaad(x1, x2)
    z = tr(x1 * x2)
    backward(z)
    x1.grad, x2.grad
end

function bench_tr_mul_autograd(x1, x2)
    z = @diff tr(x1 * x2)
    grad(z, x1), grad(z, x2)
end

x = Variable(rand(30, 30))
y = Variable(rand(30, 30))

@benchmark bench_tr_mul_yaad(x, y)

x = Param(rand(30, 30))
y = Param(rand(30, 30))

@benchmark bench_tr_mul_autograd(x, y)


text:
using Query
using DataFrames
using Test

@testset "Select Macro" begin

    df = DataFrame(foo=[1,2,3], bar=[3.,2.,1.], bat=["a","b","c"])

    @test DataFrame(df |> @select(:bat)) == DataFrame(bat=["a","b","c"],)
    @test DataFrame(df |> @select(-:foo)) == DataFrame(bar=[3.,2.,1.], bat=["a","b","c"])
    @test DataFrame(df |> @select(-:far)) == df
    @test DataFrame(df |> @select(startswith("b"))) == DataFrame(bar=[3.,2.,1.], bat=["a","b","c"])
    @test DataFrame(df |> @select(endswith("ar"))) == DataFrame(bar=[3.,2.,1.],)
    @test DataFrame(df |> @select(!occursin("a"))) == DataFrame(foo=[1,2,3],)
    @test DataFrame(df |> @select(:foo : :bar)) == DataFrame(foo=[1,2,3], bar=[3.,2.,1.])
    @test DataFrame(df |> @select(2:3, -endswith("at"))) == DataFrame(bar=[3.,2.,1.],)
    @test DataFrame(df |> @select(1:3)) == DataFrame(df |> @select(everything()))

    @test DataFrame(df |> @select(:foo, :bar, :bat)) == df
    @test DataFrame(df |> @select(startswith("f"), endswith("t"))) == DataFrame(foo=[1,2,3], bat=["a","b","c"])
    @test DataFrame(df |> @select(-1, 1)) == DataFrame(bar=[3.,2.,1.],bat=["a","b","c"], foo=[1,2,3])
    @test DataFrame(df |> @select(:bar : :bat, -2)) == DataFrame(bar=[3.,2.,1.],)

end

@testset "Rename Macro" begin

    df = DataFrame(foo=[1,2,3], bar=[3.,2.,1.], bat=["a","b","c"])

    @test DataFrame(df |> @rename(:foo => :far)) == DataFrame(far=[1,2,3], bar=[3.,2.,1.], bat=["a","b","c"])
    @test DataFrame(df |> @rename(:voo => :var)) == df
    @test DataFrame(df |> @rename(:bar => :ban, :bat => :far)) == DataFrame(foo=[1,2,3], ban=[3.,2.,1.], far=["a","b","c"])
    @test DataFrame(df |> @rename(:bar => :ban, :ban => :far)) == DataFrame(foo=[1,2,3], far=[3.,2.,1.], bat=["a","b","c"])
    @test DataFrame(df |> @rename(3 => :three)) == DataFrame(foo=[1,2,3], bar=[3.,2.,1.], three=["a","b","c"])

    @test DataFrame(df |> @rename(:bar => :far) |> @select(startswith("f"))) == DataFrame(foo=[1,2,3], far=[3.,2.,1.])

end

@testset "Mutate Macro" begin

    df = DataFrame(foo=[1,2,3], bar=[3.,2.,1.], bat=["a", "b", "c"])

    @test DataFrame(df |> @mutate(foo = 1)) == DataFrame(foo=[1,1,1], bar=[3.,2.,1.], bat=["a","b","c"])
    @test DataFrame(df |> @mutate(bar = _.foo - 2 * _.bar, fat = _.bat * _.bat)) == DataFrame(foo=[1,2,3], bar=[-5.,-2.,1.], bat=["a","b","c"], fat=["aa","bb","cc"])

    # test a closure

    closure_val = 1
    @test DataFrame(df |> @mutate(foo = closure_val)) == DataFrame(foo=[1,1,1], bar=[3.,2.,1.], bat=["a","b","c"])
end

@testset "@dropna" begin

    df = DataFrame(a=[1,missing,3], b=[1.,2.,3.])

    @test df |> @dropna() |> collect == [(a=1,b=1.), (a=3, b=3.)]
    @test df |> @filter(!any(isna, _)) |> @dropna() |> collect == [(a=1,b=1.), (a=3, b=3.)]
    @test df |> @select(:b) |> @dropna() |> collect == [(b=1.,),(b=2.,),(b=3.,)]

    @test df |> @dropna(:a) |> collect == [(a=1,b=1.), (a=3, b=3.)]
    @test df |> @dropna(:b) |> collect == [(a=DataValue(1),b=1.), (a=DataValue{Int}(),b=2.),(a=DataValue(3), b=3.)]
    @test df |> @dropna(:a, :b) |> collect == [(a=1,b=1.), (a=3, b=3.)]
end

@testset "@replacena" begin

    df = DataFrame(a=[1,missing,3], b=[1.,2.,3.])

    @test df |> @replacena(2) |> collect == [(a=1,b=1.), (a=2, b=2.), (a=3, b=3.)]
    @test df |> @dropna() |> @replacena(2) |> collect == [(a=1,b=1.), (a=3, b=3.)]
    @test df |> @select(:b) |> @replacena(2) |> collect == [(b=1.,),(b=2.,),(b=3.,)]

    @test df |> @replacena(:a=>2) |> collect == [(a=1,b=1.), (a=2, b=2.), (a=3, b=3.)]
    @test df |> @replacena(:b=>2) |> collect == [(a=DataValue(1),b=1.), (a=DataValue{Int}(),b=2.),(a=DataValue(3), b=3.)]
    @test df |> @replacena(:a=>2, :b=>8) |> collect == [(a=1,b=1.), (a=2, b=2.), (a=3, b=3.)]
end

@testset "@disallowna" begin

    df = DataFrame(a=[1,missing,3], b=[1.,2.,3.])

    @test_throws DataValueException df |> @disallowna() |> collect
    @test df |> @filter(!any(isna, _)) |> @disallowna() |> collect == [(a=1,b=1.), (a=3, b=3.)]
    @test_throws DataValueException df |> @disallowna(:a) |> collect
    @test df |> @disallowna(:b) |> collect == [(a=DataValue(1),b=1.), (a=DataValue{Int}(),b=2.),(a=DataValue(3), b=3.)]
end