text:
## Bulge chasing

## A bulge is introduced by a unitary transformation. In the single shift case this is just U^t A U; for
## the double shift case this is (VU)^T A (VU).
## In either case, we have three steps:
##
## * create the unitary transformations (U or (V,U)) -- create_bulge
## * absorb the left side -- absorb_Ut
## * chase the right side down until it is absorbed -- absorb_U
##
function bulge_step(state::FactorizationType{T, St, P, Val{:NotTwisted}}) where {T, St, P}
    create_bulge(state)
    absorb_Ut(state)
    absorb_U(state)
end

##################################################
## Create Bulge
## One for DoubleShift/SingleShift
## Pencil or Twisted don't matter as they come out in diagonalblock.

# ## The bulge is created by  (A-rho1) * (A - rho2) * e_1 where rho1 and rho2 are eigenvalue or random
function create_bulge(state::FactorizationType{T, Val{:DoubleShift}, P, Tw}) where {T, P, Tw}

    if mod(state.ctrs.it_count, 15) == 0

        t = rand(T) * pi
        re1, ie1 = cos(t), sin(t)
        re2, ie2 = re1, -ie1

        state.U = Rotator(re1, ie1,  state.ctrs.start_index)
        state.Ut = Rotator(re1, -ie1,  state.ctrs.start_index)

        state.V = Rotator(re2, ie2, state.ctrs.start_index + 1)
        state.Vt = Rotator(re2, -ie2, state.ctrs.start_index + 1)

    else

        # compute (A-rho1) * (A - rho2) * e_1
        # find e1, e2

        flag = diagonal_block(state, state.ctrs.stop_index+1)
        eigen_values(state)
        l1r, l1i = state.e1
        l2r, l2i =  state.e2

        # find first part of A[1:3, 1:2]
        flag = flag | diagonal_block(state,  state.ctrs.start_index+1)

        bk11, bk12 = state.A[1,1], state.A[1,2]
        bk21, bk22 = state.A[2,1], state.A[2,2]

        # find last part
        flag = flag | diagonal_block(state, state.ctrs.start_index+2)
        bk32 = state.A[2,1]

        # an issue... restart
        # if isnan(l1r) || isnan(l1i) || isnan(l2r) || isnan(l2i)
        #      ## eigvals gone awry
        #      restart(state)
        #      return create_bulge(state)
        # end

#        if !flag  # flag is false if there is an issue
#            restart(state)
#            return create_bulge(state)
#        end

        # make first three elements of c1,c2,c3
        # c1 = real(-l1i⋅l2i + ⅈ⋅l1i⋅l2r - ⅈ⋅l1i⋅t₁₁ + ⅈ⋅l1r⋅l2i + l1r⋅l2r - l1r⋅t₁₁ - ⅈ⋅l2i⋅t₁₁ - l2r⋅t₁₁ + t₁₁^2  + t₁₂⋅t₂₁)
        # c2 = real(-ⅈ⋅l1i⋅t₂₁ - l1r⋅t₂₁ - ⅈ⋅l2i⋅t₂₁ - l2r⋅t₂₁ + t₁₁⋅t₂₁ + t₂₁⋅t₂₂)
        # c3 = real(t₂₁⋅t₃₂)

        c1 = -l1i * l2i + l1r*l2r -l1r*bk11 -l2r * bk11 + bk11^2 + bk12 * bk21
        c2 = -l1r * bk21 - l2r * bk21 + bk11* bk21 + bk21 * bk22
        c3 = bk21 * bk32

        c, s, nrm = givensrot(c2, c3)
        j = state.ctrs.start_index + 1
        state.V = Rotator(c, -s, j)

        c, s, tmp = givensrot(c1, nrm)
        state.U = Rotator(c, -s, j-1)

    end

end

function create_bulge(state::FactorizationType{T, Val{:SingleShift}, P, Tw}) where {T, P, Tw}

    if mod(state.ctrs.it_count, 15) == 0

        t = rand(T) * pi
        if state.ray
            shift = complex(cos(t), sin(t))
        else
            shift = complex(cos(t), zero(T))
        end

    else

        flag = diagonal_block(state, state.ctrs.stop_index+1)
        if state.ray
            e1, e2 = eigen_values(state)
            shift = norm(state.A[2,2] - e1) < norm(state.A[2,2] - e2) ? e1 : e2
        else
            shift = state.A[2,2]
        end

    end

    flag = diagonal_block(state, state.ctrs.start_index+1)
    c,s,nrm = givensrot(state.A[1,1] - shift, state.A[2,1])

    state.U = Rotator(conj(c), -s, state.ctrs.start_index)
    state.Ut = Rotator(c, s, idx(state.U))

    nothing
end

##################################################
## Absorb Ut
## We have the bulge is generated by
## Ut * A * U in the singleshift case and
## Ut * Vt * A * V * U in the DoubleShift Case
## This combines the Ut with A (or Ut, Vt, A into W,A)

# This depends on Q, D, and possible sigma but not Pencil case
function absorb_Ut(state::FactorizationType{T, Val{:SingleShift}, P, Val{:NotTwisted}}) where {T, P}
    i = idx(state.Ut)
    state.Q[i], alpha = fuse(state.Ut, state.Q[i], Val{:right})
    cascade(state.Q, state.D, alpha, i, state.ctrs.stop_index) # cascade move Di into D through Qs
end

# This depends on Q
function absorb_Ut(state::FactorizationType{T, Val{:DoubleShift}, P, Val{:NotTwisted}}) where {T, P}

    state.Ut = state.U'
    state.Vt = state.V'

    i = idx(state.U); j  = i + 1
    state.W = state.Q[i]

    p = i == 1 ? one(T) : getd(state.Q[i-1]) #  zero index implies Q0 = RR(1,0) or RR(-1,0)

    state.W = dflip(state.W, p)
    state.Ut, state.Vt, state.W = turnover(state.Ut, state.Vt, state.W, Val{:right})
    state.Q[j], alpha = fuse(state.Vt, state.Q[j], Val{:right})  # V' Q3
    state.Ut = dflip(state.Ut, p)

    state.Q[i] = Rotator(state.Ut.c, state.Ut.s, state.Q[i].i)

    nothing
end

##################################################
## Absorb U
## After U absorbtion we have either
## A * U or (W,A) * V * U
## THis chases U or (V*U) through until the value can be absorbed
## The available operations are
## * turnover
## * unitary move (which doesn't change U, it just moves it from left to right)
## * fuse
## This just pushes work to passthrough_triu and passthroug_Q
function absorb_U(state::FactorizationType{T, St, P, Val{:NotTwisted}}) where {T, St, P}
    flag = false
    while !flag
        passthrough_triu(state)
        flag = passthrough_Q(state)
    end
end

## pass through triu
## we one or two triangular matrices to pass through (QV or QVW^(-1)). THis passes U
## through from right to left
## we also do left to right, needed with twisted factorizations

# right to left is default
passthrough_triu(state::FactorizationType) = passthrough_triu(state, Val{:right}) # right is default

## singleshift case has only one rotator to pass through
function passthrough_triu(state::FactorizationType{T, Val{:SingleShift}, P, Tw}, dir) where {T, P, Tw}
    ## can't do case of i <= state.ctrs.tr; as we don't have Ct[i] * B[i] = I due to pulling out of alpha.
    state.U = _passthrough_triu(state.U, state, dir)
end

## For double shift we have V then U
function passthrough_triu(state::FactorizationType{T, Val{:DoubleShift}, P, Tw}, ::Type{Val{:right}}) where {T, P, Tw}
    i = idx(state.V)
    if i <= state.ctrs.tr
        state.Vt = state.V
        state.Ut = state.U

        state.B[i], state.B[i+1], state.Vt = turnover(state.B[i],    state.B[i+1], state.Vt, Val{:right})
        state.B[i-1], state.B[i], state.Ut = turnover(state.B[i-1],  state.B[i],   state.Ut, Val{:right})
        for k in -1:1
            a,b = vals(state.B[i+k])
            state.Ct[i+k] = Rotator(a, -b, state.Ct[i+k].i)
        end
    else
        state.V = _passthrough_triu(state.V, state, Val{:right})
        state.U = _passthrough_triu(state.U, state, Val{:right})
    end
end

function passthrough_triu(state::FactorizationType{T, Val{:DoubleShift}, Val{:NoPencil}, Tw}, ::Type{Val{:left}}) where {T, Tw}
    state.U = _passthrough_triu(state.U, state, Val{:left})
    state.V = _passthrough_triu(state.V, state, Val{:left})
end

## lower level work of passing a rotatator through the triangular part
## There may be one (no pencil) or two such to pass through
## pass a specified U through
function _passthrough_triu(U::AbstractRotator, state::FactorizationType{T, St, Val{:NoPencil}, Tw}, ::Type{Val{:right}}) where {T, St, Tw}

    i = idx(U)
    state.B[i], state.B[i+1], U = turnover(state.B[i], state.B[i+1], U, Val{:right})
    state.Ct[i+1], state.Ct[i], U = turnover(state.Ct[i+1], state.Ct[i], U, Val{:right})

    if St == Val{:SingleShift}
        state.D, U = passthrough(state.D, U)
    end

    U
end

function _passthrough_triu(U::AbstractRotator, state::FactorizationType{T, St, Val{:NoPencil}, Tw}, ::Type{Val{:left}}) where {T, St, Tw}

    i = idx(U)

    if St == Val{:SingleShift}
        state.D, U = passthrough(state.D, U)
    end

    U, state.Ct[i+1], state.Ct[i] = turnover(U, state.Ct[i+1], state.Ct[i],  Val{:left})
    U, state.B[i], state.B[i+1] = turnover(U, state.B[i], state.B[i+1],    Val{:left})

    U
end

function _passthrough_triu(U::AbstractRotator, state::FactorizationType{T, St, Val{:HasPencil}, Tw}, ::Type{Val{:right}}) where {T, St, Tw}

    i = idx(U)

    # we pass Ut -> W; not W^{-1} <- U
    state.Ut = U'

    if St == Val{:SingleShift}
        state.D1, state.Ut = passthrough(state.D1, state.Ut)
    end

    state.Ut, state.Ct1[i+1], state.Ct1[i] =turnover(state.Ut, state.Ct1[i+1], state.Ct1[i], Val{:left})
    state.Ut, state.B1[i], state.B1[i+1] = turnover(state.Ut, state.B1[i], state.B1[i+1], Val{:left})

    U = state.Ut'

    # through V
    state.B[i], state.B[i+1], U = turnover(state.B[i], state.B[i+1], U, Val{:right})
    state.Ct[i+1], state.Ct[i], U = turnover(state.Ct[i+1], state.Ct[i], U, Val{:right})

    if St == Val{:SingleShift}
        state.D, U = passthrough(state.D, U)
    end

    U

end

function _passthrough_triu(U::AbstractRotator, state::FactorizationType{T, St, Val{:HasPencil}, Tw}, ::Type{Val{:left}}) where {T, St, Tw}

    i = idx(U)

    # through V
    if St == Val{:SingleShift}
        state.D, U = passthrough(state.D, U)
    end

    state.Ct[i+1], state.Ct[i], U = turnover(state.Ct[i+1], state.Ct[i], U, Val{:right})
    state.B[i], state.B[i+1], U = turnover(state.B[i], state.B[i+1], U, Val{:right})

    # through W^{-1}

    state.Ut = U'

    state.B1[i], state.B1[i+1], state.Ut =
        turnover(state.B1[i], state.B1[i+1], state.Ut, Val{:right})
    state.Ct1[i+1], state.Ct1[i], state.Ut =
        turnover(state.Ct1[i+1], state.Ct1[i], state.Ut, Val{:right})

    if St == Val{:SingleShift}
        state.D1, state.Ut = passthrough(state.D1, state.Ut)
    end
    U = state.Ut'

    U

end

##################################################
## passthrough Q
##
## we have Q D U or (W,Q) V U to pass through
## This depends on Twisted but not pencil
##
## If we update indices and use a unitary transform, return false (not absorbed)
## else return true (was absorved)
function passthrough_Q(state::FactorizationType{T, Val{:SingleShift}, P, Val{:NotTwisted}}) where {T,P}

    i = idx(state.U)

    if i < state.ctrs.stop_index
        state.Q[i], state.Q[i+1], state.U = turnover(state.Q[i], state.Q[i+1], state.U)

        false
    else
        state.Q[i], alpha = fuse(state.Q[i], state.U, Val{:left})
        state.D[i] *= alpha
        state.D[i+1] *= conj(alpha)
        true
    end
end

function passthrough_Q(state::FactorizationType{T, Val{:DoubleShift}, P, Val{:NotTwisted}}) where {T,P}

    i = idx(state.U); j = i + 1

    if j < state.ctrs.stop_index
        state.Q[j], state.Q[j+1], state.V =
            turnover(state.Q[j], state.Q[j+1], state.V)
        state.Q[i], state.Q[i+1], state.U =
            turnover(state.Q[i], state.Q[i+1], state.U)
        state.W, state.V, state.U =
            turnover(state.W, state.V, state.U, Val{:left})
        false
    else
        p = getd(state.Q[j+1])
        state.V = dflip(state.V, p)
        state.Q[j], alpha = fuse(state.Q[j], state.V, Val{:left})

        # now turnover U, merge with W, unitary over, pass through, and fuse
        state.Q[i], state.Q[i+1], state.U =
            turnover(state.Q[i], state.Q[i+1], state.U)
        i += 1 # after turnover, U moves down
        state.U, alpha = fuse(state.W, state.U, Val{:right})

        # pass U through triangle then fuse
        state.U = _passthrough_triu(state.U, state, Val{:right})
        state.U = dflip(state.U, p)
        state.Q[i], alpha = fuse(state.Q[i], state.U, Val{:left})

        true
    end
end


text:
# This computes a straight-line transit lightcurve, integrated over a timestep.

include("integrate_transit_simpson_vec.jl")

function integrate_lightcurve!(trans::Transit_Struct{T},param::Array{T,1},t::Array{T,1},dt::T,favg1::Array{T,2},nt::Int64,tol::T,maxdepth::Int64,neval_t::Array{Int64,1},depthmax::Array{Int64,1}) where {T <: Real}
# This routine integrates a lightcurve with a constant velocity, v,
# and impact parameter, b0, which are specified in the "param" vector.
# The trans structure needs to be initialized before calling this routine,
# which will contain workspace for the transit computation, the radius ratio,
# and the limb-darkening parameters.

# Input quantities:
# trans:  structure which holds the parameters describing the transit
# param:  parameters specifying the orbital path of the transit (straight line for now)
#     t:  vector of transit times
#    dt:  exposure time
# favg1:  vector which contains the time-averaged flux and derivatives
#    nt:  number of timesteps
#   tol:  tolerance for computing the integration
# maxdepth:  maximum depth allowed for the adaptive Simpson integration
# neval_t:  number of evaluations for each time step
# depthmax:  maximum depth reached for each timestep
#
# Upon return, favg1, neval_t, and depthmax will be updated.
#
# First, find the points of contact:
b0 = abs(param[3]); r = trans.r; t0 = param[1]; v = param[2]
# Check to see whether the two bodies overlap:
if b0 > (1.0+r)
  println("b0: ",b0," r: ",r)
  println("No transit")
  # No transit occurs, so returns ones for lightcurves, and zeros for derivatives:
#  favg1[1,:] = one(T)
  favg1[1,:] = zero(T)
  favg1[2:5+trans.n,:] = zero(T)
  return
# If a grazing transit, then there are only two points of contact:
elseif (b0+r) > 1.0
  # Times of two points of contact:
  tc = [t0 - sqrt((1.0+r)^2-b0^2)/v,t0 + sqrt((1.0+r)^2-b0^2)/v]
  nc = 2
# If a full transit, then there are four points of contact:
else
  # Times of four points of contact:
  tc = [t0 - sqrt((1.0+r)^2-b0^2)/v,t0 - sqrt((1.0-r)^2-b0^2)/v,t0 + sqrt((1.0-r)^2-b0^2)/v,t0 + sqrt((1.0+r)^2-b0^2)/v]
  nc = 4
end
# Invers of time step:
dtinv = inv(dt)
# Set up array to hold the integrated time step:
fint  = zeros(T,6+trans.n)
# Loop over the number of time steps:
@inbounds for i=1:nt
  # Keep track of the number of evaluations and the maximum depth:
  neval_t[i] = 0
  depthmax[i] = 0
  # Specify the starting and final timestep endpoints:
  t1 = t[i]-0.5*dt ; t2 = t[i]+0.5*dt
  # Check to see whether these midpoints contain the contact times:
  if t2 < tc[1] || t1 > tc[nc]
    # No points lie within the transit:
    ftmp=zeros(T,6+trans.n)
#    ftmp[1]=one(T)
    ftmp[1]=zero(T)
  else
    # Loop over points of contact:
    tlim = [t1]
    for j=1:nc
      if t1 < tc[j] && tc[j] < t2
        push!(tlim,tc[j])
      end
    end
    push!(tlim,t2)
    # ftmp is an array which contains the derivatives with respect to g_n:
    ftmp=zeros(T,6+trans.n)
    # Carry out the integration between the start & end time of the exposure,
    # as well as up to each contact point:
    for j=1:length(tlim)-1
      nevali,depthmaxi =  integrate_timestep_gradient!(param,trans,tlim[j],tlim[j+1],tol,maxdepth,fint)
#      println("r: ",trans.r," b: ",trans.b," fint: ",fint," dt: ",tlim[j+1]-tlim[j])
      neval_t[i] += nevali
      if depthmaxi > depthmax[i]
        depthmax[i] = depthmaxi
      end
      ftmp += fint
    end
    # Now divide by the exposure time to get the average flux over the integration:
    ftmp *= dtinv
#    println("ftmp: ",ftmp)
  end
  # Pass the flux and derivatives with respect to the lightcurve variables to
  # the average flux vector:
  favg1[1:5,i]=ftmp[1:5]
  # Convert from g_n to u_n derivatives for the remaining parts of the vector:
  favg1[6:5+trans.n,i]=BLAS.gemv('T',1.0,trans.dgdu,ftmp[6:6+trans.n])
#  println("i: ",i," t: ",t[i]," result: ",ftmp)
end
return
end

ext: jl


text: eval(load_taylormodels_overapproximation())


text:
# exp(-x^2/2) H_n(x) / sqrt(π*prod(1:n))

struct ForwardWeightedHermitePlan{T}
    Vtw::Matrix{T} # vandermonde
end

struct BackwardWeightedHermitePlan{T}
    V::Matrix{T} # vandermonde
end

function _weightedhermite_vandermonde(n)
    V = Array{Float64}(undef, n, n)
    x,w = unweightedgausshermite(n)
    for k=1:n
        V[k,:] = FastGaussQuadrature.hermpoly_rec(0:n-1, sqrt(2)*x[k])
    end
    V,w
end

function ForwardWeightedHermitePlan(n::Integer)
    V,w = _weightedhermite_vandermonde(n)
    ForwardWeightedHermitePlan(V' * Diagonal(w / sqrt(π)))
end

BackwardWeightedHermitePlan(n::Integer) = BackwardWeightedHermitePlan(_weightedhermite_vandermonde(n)[1])

*(P::ForwardWeightedHermitePlan, v::AbstractVector) = P.Vtw*v
*(P::BackwardWeightedHermitePlan, v::AbstractVector) = P.V*v

weightedhermitetransform(v) = ForwardWeightedHermitePlan(length(v))*v
iweightedhermitetransform(v) = BackwardWeightedHermitePlan(length(v))*v


text:
# an ordering to be used in sorting DictArrays or Labeledarrays.
immutable AbstractArrayLT{N,O,F} <: Base.Ordering
  ords::O #NTuple{N,Base.Ordering}
  fields::F #NTuple{N,Vector}
end

AbstractArrayLT(arr::Union{DictArray,LabeledArray}, axis::Integer, field_names...; kwargs...) = begin
  kv = Dict(kwargs)
  ords = map(field_names) do field
    lt = get(kv, symbol(field, "_lt"), isless)
    by = get(kv, symbol(field, "_by"), identity)
    rev = get(kv, symbol(field, "_rev"), false)
    order = get(kv, symbol(field, "_ord"), Base.Forward)
    Base.Order.ord(lt, by, rev, order)
  end
  ndimsarr = ndims(arr)
  fields = map(field_names) do name
    #collect(selectfield(arr, name)[get(kv, symbol(name, "_coords"), ntuple(d->d==axis ? Colon() : 1, ndimsarr))...])
    selectfield(arr, name)[get(kv, symbol(name, "_coords"), ntuple(d->d==axis ? Colon() : 1, ndimsarr))...].a
  end
  AbstractArrayLT{length(field_names),typeof(ords),typeof(fields)}(ords, fields)
end

Base.Sort.lt{N}(ltobj::AbstractArrayLT{N}, x::Integer, y::Integer) = begin
  for i in 1:N
    atix = ltobj.fields[i][x]
    atiy = ltobj.fields[i][y]
    ord = ltobj.ords[i]
    if atix.isnull
      return !atiy.isnull
    elseif atiy.isnull
      return false
    elseif Base.lt(ord, atix.value, atiy.value)
      return true
    elseif Base.lt(ord, atiy.value, atix.value)
      return false
    end
  end
  false
end

Base.Sort.lt{O,T,A}(ltobj::AbstractArrayLT{1,O,Tuple{FloatNAArray{T,1,A}}}, x::Integer, y::Integer) = begin
  atix = ltobj.fields[1].data[x]
  atiy = ltobj.fields[1].data[y]
  ord = ltobj.ords[1]
  if isnan(atix)
    return !isnan(atiy)
  elseif isnan(atiy)
    return false
  end
  Base.lt(ord, atix, atiy)
end

sortpermbase(arr::Union{DictArray, LabeledArray}, axis::Integer, algorithm, order::Base.Ordering) = begin
  sorted_indices = collect(1:size(arr, axis))
  sort!(sorted_indices, algorithm, order)
  permuted_coords = ntuple(ndims(arr)) do d
    if d == axis
      sorted_indices
    else
      Colon()
    end
  end
  permuted_coords
end
Base.sortperm(arr::Union{DictArray,LabeledArray}, axis::Integer, fields...; alg=Base.Sort.defalg(arr), kwargs...) = begin
  ordering = AbstractArrayLT(arr, axis, fields...;kwargs...)
  sortpermbase(arr, axis, alg, ordering)
end
sortbase(arr::Union{DictArray,LabeledArray}, axis::Integer, algorithm, order::Base.Ordering) =
  arr[sortpermbase(arr, axis, algorithm, order)...]

"""

`sort(arr, axis fields... [; alg=..., ...])`

Sort a `DictArray` or `LabeledArray` along some axis.

##### Arguments

* `arr` : either a `DictArray` or a `LabeledArray`.
* `axis` : an axis direction integer to denote which direction to sort along.
* `fields...` : the names of fields to determine the order. The preceding ones have precedence over the later ones. Note only the components [1,...,1,:,1,...1], where : is placed at the axis position, will be used out of each field.
* optionally, `alg=algorithm` determines the sorting algorithm. `fieldname_lt=ltfunc` sets the less-than function for the field fieldname, and similarly for `by`/`rev`/`ord`.

##### Examples

```julia
julia> t = larr(a=[3 3 2;7 5 3], b=[:b :a :c;:d :e :f], axis1=["X","Y"])
2 x 3 LabeledArray

  |1   |2   |3
--+----+----+----
  |a b |a b |a b
--+----+----+----
X |3 b |3 a |2 c
Y |7 d |5 e |3 f

julia> sort(t, 1, :a)
2 x 3 LabeledArray

  |1   |2   |3
--+----+----+----
  |a b |a b |a b
--+----+----+----
X |3 b |3 a |2 c
Y |7 d |5 e |3 f

julia> sort(t, 2, :a)
2 x 3 LabeledArray

  |1   |2   |3
--+----+----+----
  |a b |a b |a b
--+----+----+----
X |2 c |3 b |3 a
Y |3 f |7 d |5 e

julia> sort(t, 2, :a, :b)
2 x 3 LabeledArray

  |1   |2   |3
--+----+----+----
  |a b |a b |a b
--+----+----+----
X |2 c |3 a |3 b
Y |3 f |5 e |7 d

julia> sort(t, 2, :a, :b, a_rev=true)
2 x 3 LabeledArray

  |1   |2   |3
--+----+----+----
  |a b |a b |a b
--+----+----+----
X |3 a |3 b |2 c
Y |5 e |7 d |3 f
```

"""
Base.sort(arr::LabeledArray, axis::Integer, fields...; alg=Base.Sort.defalg(arr), kwargs...) = begin
  ordering = AbstractArrayLT(arr, axis, fields...;kwargs...)
  sortbase(arr, axis, alg, ordering)
end
Base.sort(arr::DictArray, axis::Integer, fields...; alg=Base.Sort.defalg(arr), kwargs...) = begin
  ordering = AbstractArrayLT(arr, axis, fields...;kwargs...)
  sortbase(arr, axis, alg, ordering)
end


text:
function solve()
    s = readline()
    s == "ABC" ? "ARC" : "ABC"
end

println(solve())

max_stars_repo_name: aishikawa/atcoder-julia

max_issues_repo_name: aishikawa/atcoder-julia

max_forks_repo_name: aishikawa/atcoder-julia


text:
@testset "162.find-peak-element.jl" begin
    @test find_peak_element([1, 2, 3, 1]) == 2
    @test find_peak_element([1, 2, 1, 3, 5, 6, 4]) in (1, 5)
    @test find_peak_element([1]) == 0
    @test find_peak_element([3, 2, 1]) == 0
end


text:
"""
    minimumdistance(myModel::CPLEX.Model, timeStart::UInt, timeLim::Float64,
        xTilde::Vector{Float64}, indices::Vector{Int})

Replace the objective function to minimize the L1-distance to xTilde. Then
optimize to return xOverline.
"""
function minimumdistance(
        myModel::CPLEX.Model,
        timeStart::UInt,
        timeLim::Float64,
        xTilde::Vector{Float64},
        indices::Vector{Int},
        opts...
        ;
        compile::Bool = false
    )
    if compile
        return [NaN]
    end

    nVars = length(xTilde)
    objectiveFunction = zeros(Float64, nVars)
    for i in indices
        if xTilde[i] > 0.5
            objectiveFunction[i] = -1.0
        else
            objectiveFunction[i] = 1.0
        end
    end
    CPLEX.set_obj!(myModel, objectiveFunction)

    CPLEX.set_param!(myModel.env, "CPX_PARAM_TILIM", maximum([0, timeLim - (time_ns() - timeStart) / 1.0e9]))
    CPLEX.optimize!(myModel)

    xOverline = zeros(Float64, nVars)
    if CPLEX.get_status(myModel) == :CPX_STAT_OPTIMAL
        xOverline = CPLEX.get_solution(myModel)
    end

    return xOverline
end

"""
    minimumdeltaalpha(myModel::CPLEX.Model, timeStart::UInt, timeLim::Float64,
        xTilde::Vector{Float64}, indices::Vector{Int},
        initObjectiveFunction::Vector{Float64}, alpha::Float64, coef::Float64)

Replace the objective function to minimize the L1-distance to xTilde taking into
account the initial objective function. Then optimize to return xOverline.
"""
function minimumdeltaalpha(
        myModel::CPLEX.Model,
        timeStart::UInt,
        timeLim::Float64,
        xTilde::Vector{Float64},
        indices::Vector{Int},
        initObjectiveFunction::Vector{Float64},
        alpha::Float64,
        coef::Float64,
        opts...
        ;
        compile::Bool = false
    )
    if compile
        return [NaN]
    end

    nVars = length(xTilde)

    # objectiveFunction minimumdistance
    objectiveFunction = zeros(Float64, nVars)
    for i in indices
        if xTilde[i] > 0.5
            objectiveFunction[i] = -1.0
        else
            objectiveFunction[i] = 1.0
        end
    end

    # Add objective function and alpha to it
    objectiveFunction .= (objectiveFunction .* (1-alpha)) .+ ((alpha*coef) .* initObjectiveFunction)
    CPLEX.set_obj!(myModel, objectiveFunction)

    CPLEX.set_param!(myModel.env, "CPX_PARAM_TILIM", maximum([0, timeLim - (time_ns() - timeStart) / 1.0e9]))
    CPLEX.optimize!(myModel)

    xOverline = zeros(Float64, nVars)
    if CPLEX.get_status(myModel) == :CPX_STAT_OPTIMAL
        xOverline = CPLEX.get_solution(myModel)
    end

    return xOverline
end

"""
    minimumdistance_reduce(myModel::CPLEX.Model, timeStart::UInt,
        timeLim::Float64, xTilde::Vector{Float64}, indices::Vector{Int})

Replace the objective function to minimize the L1-distance to xTilde. Multiply
the new objective function by the non zeros reduced costs. Then optimize to
return xOverline.
"""
function minimumdistance_reduce(
        myModel::CPLEX.Model,
        timeStart::UInt,
        timeLim::Float64,
        xTilde::Vector{Float64},
        indices::Vector{Int},
        opts...
        ;
        compile::Bool = false
    )
    if compile
        return [NaN]
    end

    nVars = length(xTilde)
    reducedCosts = Vector{Float64}()
    try
        reducedCosts = CPLEX.get_reduced_costs(myModel)
    catch
        reducedCosts = zeros(nVars)
    end
    objectiveFunction = zeros(Float64, nVars)
    for i in indices
        if xTilde[i] > 0.5
            objectiveFunction[i] = -1.0
        else
            objectiveFunction[i] = 1.0
        end
        if abs(reducedCosts[i]) > 1e-8
            objectiveFunction[i] *= abs(reducedCosts[i])
        end
    end
    CPLEX.set_obj!(myModel, objectiveFunction)

    CPLEX.set_param!(myModel.env, "CPX_PARAM_TILIM", maximum([0, timeLim - (time_ns() - timeStart) / 1.0e9]))
    CPLEX.optimize!(myModel)

    xOverline = zeros(Float64, nVars)
    if CPLEX.get_status(myModel) == :CPX_STAT_OPTIMAL
        xOverline = CPLEX.get_solution(myModel)
    end

    return xOverline
end

max_stars_repo_name: remi-garcia/FeasibilityPump

max_issues_repo_name: remi-garcia/FeasibilityPump

max_forks_repo_name: remi-garcia/FeasibilityPump


text:
# syntax: proto3
using ProtoBuf
import ProtoBuf.meta
import ProtoBuf.google.protobuf

const _DataType = (;[
    Symbol("DATA_TYPE_UNSET") => Int32(0),
    Symbol("DATA_TYPE_STRING") => Int32(1),
    Symbol("DATA_TYPE_BOOL") => Int32(2),
    Symbol("DATA_TYPE_FLOAT64") => Int32(3),
]...)

const DatasetType = (;[
    Symbol("DATASET_UNKNOWN") => Int32(0),
    Symbol("DATASET_TRAINING") => Int32(1),
    Symbol("DATASET_VALIDATION") => Int32(2),
]...)

const Status = (;[
    Symbol("STATUS_UNKNOWN") => Int32(0),
    Symbol("STATUS_SUCCESS") => Int32(1),
    Symbol("STATUS_FAILURE") => Int32(2),
    Symbol("STATUS_RUNNING") => Int32(3),
]...)

const SortOrder = (;[
    Symbol("ORDER_UNSPECIFIED") => Int32(0),
    Symbol("ORDER_ASC") => Int32(1),
    Symbol("ORDER_DESC") => Int32(2),
]...)

const AggregationType = (;[
    Symbol("AGGREGATION_UNSET") => Int32(0),
    Symbol("AGGREGATION_AVG") => Int32(1),
    Symbol("AGGREGATION_MEDIAN") => Int32(2),
    Symbol("AGGREGATION_MIN") => Int32(3),
    Symbol("AGGREGATION_MAX") => Int32(4),
]...)

mutable struct Interval <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Interval(; kwargs...)
        obj = new(meta(Interval), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Interval
const __meta_Interval = Ref{ProtoMeta}()
function meta(::Type{Interval})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Interval)
            __meta_Interval[] = target = ProtoMeta(Interval)
            allflds = Pair{Symbol,Union{Type,String}}[:min_value => Float64, :max_value => Float64]
            meta(target, Interval, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Interval[]
    end
end
function Base.getproperty(obj::Interval, name::Symbol)
    if name === :min_value
        return (obj.__protobuf_jl_internal_values[name])::Float64
    elseif name === :max_value
        return (obj.__protobuf_jl_internal_values[name])::Float64
    else
        getfield(obj, name)
    end
end

mutable struct HParamInfo <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function HParamInfo(; kwargs...)
        obj = new(meta(HParamInfo), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct HParamInfo
const __meta_HParamInfo = Ref{ProtoMeta}()
function meta(::Type{HParamInfo})
    ProtoBuf.metalock() do
        if !isassigned(__meta_HParamInfo)
            __meta_HParamInfo[] = target = ProtoMeta(HParamInfo)
            allflds = Pair{Symbol,Union{Type,String}}[:name => AbstractString, :display_name => AbstractString, :description => AbstractString, :_type => Int32, :domain_discrete => ProtoBuf.google.protobuf.ListValue, :domain_interval => Interval]
            oneofs = Int[0,0,0,0,1,1]
            oneof_names = Symbol[Symbol("domain")]
            meta(target, HParamInfo, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, oneofs, oneof_names)
        end
        __meta_HParamInfo[]
    end
end
function Base.getproperty(obj::HParamInfo, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :display_name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :description
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :_type
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :domain_discrete
        return (obj.__protobuf_jl_internal_values[name])::ProtoBuf.google.protobuf.ListValue
    elseif name === :domain_interval
        return (obj.__protobuf_jl_internal_values[name])::Interval
    else
        getfield(obj, name)
    end
end

mutable struct MetricName <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function MetricName(; kwargs...)
        obj = new(meta(MetricName), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct MetricName
const __meta_MetricName = Ref{ProtoMeta}()
function meta(::Type{MetricName})
    ProtoBuf.metalock() do
        if !isassigned(__meta_MetricName)
            __meta_MetricName[] = target = ProtoMeta(MetricName)
            allflds = Pair{Symbol,Union{Type,String}}[:group => AbstractString, :tag => AbstractString]
            meta(target, MetricName, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_MetricName[]
    end
end
function Base.getproperty(obj::MetricName, name::Symbol)
    if name === :group
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :tag
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct MetricInfo <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function MetricInfo(; kwargs...)
        obj = new(meta(MetricInfo), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct MetricInfo
const __meta_MetricInfo = Ref{ProtoMeta}()
function meta(::Type{MetricInfo})
    ProtoBuf.metalock() do
        if !isassigned(__meta_MetricInfo)
            __meta_MetricInfo[] = target = ProtoMeta(MetricInfo)
            fnum = Int[1,3,4,5]
            allflds = Pair{Symbol,Union{Type,String}}[:name => MetricName, :display_name => AbstractString, :description => AbstractString, :dataset_type => Int32]
            meta(target, MetricInfo, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_MetricInfo[]
    end
end
function Base.getproperty(obj::MetricInfo, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::MetricName
    elseif name === :display_name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :description
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :dataset_type
        return (obj.__protobuf_jl_internal_values[name])::Int32
    else
        getfield(obj, name)
    end
end

mutable struct Experiment <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Experiment(; kwargs...)
        obj = new(meta(Experiment), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Experiment
const __meta_Experiment = Ref{ProtoMeta}()
function meta(::Type{Experiment})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Experiment)
            __meta_Experiment[] = target = ProtoMeta(Experiment)
            fnum = Int[6,1,2,3,4,5]
            allflds = Pair{Symbol,Union{Type,String}}[:name => AbstractString, :description => AbstractString, :user => AbstractString, :time_created_secs => Float64, :hparam_infos => Base.Vector{HParamInfo}, :metric_infos => Base.Vector{MetricInfo}]
            meta(target, Experiment, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Experiment[]
    end
end
function Base.getproperty(obj::Experiment, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :description
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :user
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :time_created_secs
        return (obj.__protobuf_jl_internal_values[name])::Float64
    elseif name === :hparam_infos
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{HParamInfo}
    elseif name === :metric_infos
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{MetricInfo}
    else
        getfield(obj, name)
    end
end

mutable struct MetricValue <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function MetricValue(; kwargs...)
        obj = new(meta(MetricValue), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct MetricValue
const __meta_MetricValue = Ref{ProtoMeta}()
function meta(::Type{MetricValue})
    ProtoBuf.metalock() do
        if !isassigned(__meta_MetricValue)
            __meta_MetricValue[] = target = ProtoMeta(MetricValue)
            allflds = Pair{Symbol,Union{Type,String}}[:name => MetricName, :value => Float64, :training_step => Int32, :wall_time_secs => Float64]
            meta(target, MetricValue, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_MetricValue[]
    end
end
function Base.getproperty(obj::MetricValue, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::MetricName
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::Float64
    elseif name === :training_step
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :wall_time_secs
        return (obj.__protobuf_jl_internal_values[name])::Float64
    else
        getfield(obj, name)
    end
end

mutable struct Session <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function Session(; kwargs...)
        obj = new(meta(Session), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct Session
const __meta_Session = Ref{ProtoMeta}()
function meta(::Type{Session})
    ProtoBuf.metalock() do
        if !isassigned(__meta_Session)
            __meta_Session[] = target = ProtoMeta(Session)
            allflds = Pair{Symbol,Union{Type,String}}[:name => AbstractString, :start_time_secs => Float64, :end_time_secs => Float64, :status => Int32, :model_uri => AbstractString, :metric_values => Base.Vector{MetricValue}, :monitor_url => AbstractString]
            meta(target, Session, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_Session[]
    end
end
function Base.getproperty(obj::Session, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :start_time_secs
        return (obj.__protobuf_jl_internal_values[name])::Float64
    elseif name === :end_time_secs
        return (obj.__protobuf_jl_internal_values[name])::Float64
    elseif name === :status
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :model_uri
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :metric_values
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{MetricValue}
    elseif name === :monitor_url
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct SessionGroup_HparamsEntry <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function SessionGroup_HparamsEntry(; kwargs...)
        obj = new(meta(SessionGroup_HparamsEntry), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct SessionGroup_HparamsEntry (mapentry)
const __meta_SessionGroup_HparamsEntry = Ref{ProtoMeta}()
function meta(::Type{SessionGroup_HparamsEntry})
    ProtoBuf.metalock() do
        if !isassigned(__meta_SessionGroup_HparamsEntry)
            __meta_SessionGroup_HparamsEntry[] = target = ProtoMeta(SessionGroup_HparamsEntry)
            allflds = Pair{Symbol,Union{Type,String}}[:key => AbstractString, :value => ProtoBuf.google.protobuf.Value]
            meta(target, SessionGroup_HparamsEntry, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_SessionGroup_HparamsEntry[]
    end
end
function Base.getproperty(obj::SessionGroup_HparamsEntry, name::Symbol)
    if name === :key
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :value
        return (obj.__protobuf_jl_internal_values[name])::ProtoBuf.google.protobuf.Value
    else
        getfield(obj, name)
    end
end

mutable struct SessionGroup <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function SessionGroup(; kwargs...)
        obj = new(meta(SessionGroup), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct SessionGroup
const __meta_SessionGroup = Ref{ProtoMeta}()
function meta(::Type{SessionGroup})
    ProtoBuf.metalock() do
        if !isassigned(__meta_SessionGroup)
            __meta_SessionGroup[] = target = ProtoMeta(SessionGroup)
            allflds = Pair{Symbol,Union{Type,String}}[:name => AbstractString, :hparams => Base.Dict{AbstractString,ProtoBuf.google.protobuf.Value}, :metric_values => Base.Vector{MetricValue}, :sessions => Base.Vector{Session}, :monitor_url => AbstractString]
            meta(target, SessionGroup, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_SessionGroup[]
    end
end
function Base.getproperty(obj::SessionGroup, name::Symbol)
    if name === :name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :hparams
        return (obj.__protobuf_jl_internal_values[name])::Base.Dict{AbstractString,ProtoBuf.google.protobuf.Value}
    elseif name === :metric_values
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{MetricValue}
    elseif name === :sessions
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{Session}
    elseif name === :monitor_url
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct GetExperimentRequest <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function GetExperimentRequest(; kwargs...)
        obj = new(meta(GetExperimentRequest), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct GetExperimentRequest
const __meta_GetExperimentRequest = Ref{ProtoMeta}()
function meta(::Type{GetExperimentRequest})
    ProtoBuf.metalock() do
        if !isassigned(__meta_GetExperimentRequest)
            __meta_GetExperimentRequest[] = target = ProtoMeta(GetExperimentRequest)
            allflds = Pair{Symbol,Union{Type,String}}[:experiment_name => AbstractString]
            meta(target, GetExperimentRequest, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_GetExperimentRequest[]
    end
end
function Base.getproperty(obj::GetExperimentRequest, name::Symbol)
    if name === :experiment_name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    else
        getfield(obj, name)
    end
end

mutable struct ColParams <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ColParams(; kwargs...)
        obj = new(meta(ColParams), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ColParams
const __meta_ColParams = Ref{ProtoMeta}()
function meta(::Type{ColParams})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ColParams)
            __meta_ColParams[] = target = ProtoMeta(ColParams)
            allflds = Pair{Symbol,Union{Type,String}}[:metric => MetricName, :hparam => AbstractString, :order => Int32, :missing_values_first => Bool, :filter_regexp => AbstractString, :filter_interval => Interval, :filter_discrete => ProtoBuf.google.protobuf.ListValue, :exclude_missing_values => Bool]
            oneofs = Int[1,1,0,0,2,2,2,0]
            oneof_names = Symbol[Symbol("name"),Symbol("filter")]
            meta(target, ColParams, allflds, ProtoBuf.DEF_REQ, ProtoBuf.DEF_FNUM, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, oneofs, oneof_names)
        end
        __meta_ColParams[]
    end
end
function Base.getproperty(obj::ColParams, name::Symbol)
    if name === :metric
        return (obj.__protobuf_jl_internal_values[name])::MetricName
    elseif name === :hparam
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :order
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :missing_values_first
        return (obj.__protobuf_jl_internal_values[name])::Bool
    elseif name === :filter_regexp
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :filter_interval
        return (obj.__protobuf_jl_internal_values[name])::Interval
    elseif name === :filter_discrete
        return (obj.__protobuf_jl_internal_values[name])::ProtoBuf.google.protobuf.ListValue
    elseif name === :exclude_missing_values
        return (obj.__protobuf_jl_internal_values[name])::Bool
    else
        getfield(obj, name)
    end
end

mutable struct ListSessionGroupsRequest <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ListSessionGroupsRequest(; kwargs...)
        obj = new(meta(ListSessionGroupsRequest), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ListSessionGroupsRequest
const __meta_ListSessionGroupsRequest = Ref{ProtoMeta}()
function meta(::Type{ListSessionGroupsRequest})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ListSessionGroupsRequest)
            __meta_ListSessionGroupsRequest[] = target = ProtoMeta(ListSessionGroupsRequest)
            fnum = Int[6,7,1,2,3,4,5]
            pack = Symbol[:allowed_statuses]
            allflds = Pair{Symbol,Union{Type,String}}[:experiment_name => AbstractString, :allowed_statuses => Base.Vector{Int32}, :col_params => Base.Vector{ColParams}, :aggregation_type => Int32, :aggregation_metric => MetricName, :start_index => Int32, :slice_size => Int32]
            meta(target, ListSessionGroupsRequest, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, pack, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ListSessionGroupsRequest[]
    end
end
function Base.getproperty(obj::ListSessionGroupsRequest, name::Symbol)
    if name === :experiment_name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :allowed_statuses
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{Int32}
    elseif name === :col_params
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{ColParams}
    elseif name === :aggregation_type
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :aggregation_metric
        return (obj.__protobuf_jl_internal_values[name])::MetricName
    elseif name === :start_index
        return (obj.__protobuf_jl_internal_values[name])::Int32
    elseif name === :slice_size
        return (obj.__protobuf_jl_internal_values[name])::Int32
    else
        getfield(obj, name)
    end
end

mutable struct ListSessionGroupsResponse <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ListSessionGroupsResponse(; kwargs...)
        obj = new(meta(ListSessionGroupsResponse), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ListSessionGroupsResponse
const __meta_ListSessionGroupsResponse = Ref{ProtoMeta}()
function meta(::Type{ListSessionGroupsResponse})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ListSessionGroupsResponse)
            __meta_ListSessionGroupsResponse[] = target = ProtoMeta(ListSessionGroupsResponse)
            fnum = Int[1,3]
            allflds = Pair{Symbol,Union{Type,String}}[:session_groups => Base.Vector{SessionGroup}, :total_size => Int32]
            meta(target, ListSessionGroupsResponse, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ListSessionGroupsResponse[]
    end
end
function Base.getproperty(obj::ListSessionGroupsResponse, name::Symbol)
    if name === :session_groups
        return (obj.__protobuf_jl_internal_values[name])::Base.Vector{SessionGroup}
    elseif name === :total_size
        return (obj.__protobuf_jl_internal_values[name])::Int32
    else
        getfield(obj, name)
    end
end

mutable struct ListMetricEvalsRequest <: ProtoType
    __protobuf_jl_internal_meta::ProtoMeta
    __protobuf_jl_internal_values::Dict{Symbol,Any}
    __protobuf_jl_internal_defaultset::Set{Symbol}

    function ListMetricEvalsRequest(; kwargs...)
        obj = new(meta(ListMetricEvalsRequest), Dict{Symbol,Any}(), Set{Symbol}())
        values = obj.__protobuf_jl_internal_values
        symdict = obj.__protobuf_jl_internal_meta.symdict
        for nv in kwargs
            fldname, fldval = nv
            fldtype = symdict[fldname].jtyp
            (fldname in keys(symdict)) || error(string(typeof(obj), " has no field with name ", fldname))
            values[fldname] = isa(fldval, fldtype) ? fldval : convert(fldtype, fldval)
        end
        obj
    end
end # mutable struct ListMetricEvalsRequest
const __meta_ListMetricEvalsRequest = Ref{ProtoMeta}()
function meta(::Type{ListMetricEvalsRequest})
    ProtoBuf.metalock() do
        if !isassigned(__meta_ListMetricEvalsRequest)
            __meta_ListMetricEvalsRequest[] = target = ProtoMeta(ListMetricEvalsRequest)
            fnum = Int[3,1,2]
            allflds = Pair{Symbol,Union{Type,String}}[:experiment_name => AbstractString, :session_name => AbstractString, :metric_name => MetricName]
            meta(target, ListMetricEvalsRequest, allflds, ProtoBuf.DEF_REQ, fnum, ProtoBuf.DEF_VAL, ProtoBuf.DEF_PACK, ProtoBuf.DEF_WTYPES, ProtoBuf.DEF_ONEOFS, ProtoBuf.DEF_ONEOF_NAMES)
        end
        __meta_ListMetricEvalsRequest[]
    end
end
function Base.getproperty(obj::ListMetricEvalsRequest, name::Symbol)
    if name === :experiment_name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :session_name
        return (obj.__protobuf_jl_internal_values[name])::AbstractString
    elseif name === :metric_name
        return (obj.__protobuf_jl_internal_values[name])::MetricName
    else
        getfield(obj, name)
    end
end

export _DataType, DatasetType, Status, SortOrder, AggregationType, Experiment, HParamInfo, Interval, MetricName, MetricInfo, SessionGroup_HparamsEntry, SessionGroup, MetricValue, Session, GetExperimentRequest, ListSessionGroupsRequest, ColParams, ListSessionGroupsResponse, ListMetricEvalsRequest
# mapentries: "SessionGroup_HparamsEntry" => ("AbstractString", "ProtoBuf.google.protobuf.Value")


text:
export Parallel, Serial, Thread, serial, thread

"""
    Parallel

An abstract type for the different kinds of parallel programming supported.
"""
abstract type Parallel end

"""
    Serial <: Parallel

A type for serial computing
"""
struct Serial <:Parallel end

"""
    Thread <: Parallel

A type for multi-threaded computing
"""
struct Thread <:Parallel end

const serial = Serial()
const thread = Thread()


text:
struct Preconditioner{ML<:MultiLevel}
    ml::ML
    init::Symbol
end
Preconditioner(ml) = Preconditioner(ml, :zero)

aspreconditioner(ml::MultiLevel) = Preconditioner(ml)

import LinearAlgebra: \, *, ldiv!, mul!
ldiv!(p::Preconditioner, b) = copyto!(b, p \ b)
function ldiv!(x, p::Preconditioner, b)
    if p.init == :zero
        x .= 0
    else
        x .= b
    end
    solve!(x, p.ml, b, maxiter = 1, calculate_residual = false)
end
mul!(b, p::Preconditioner, x) = mul!(b, p.ml.levels[1].A, x)

function \(p::Preconditioner, b)
    ldiv!(similar(b), p, b)
end
*(p::Preconditioner, b) = p.ml.levels[1].A * x


text:
include("help.jl")
function average_temperature(r::Robot)
    temp_sum, marks_num = pass_field(r, 0, 0)

    if marks_num == 0
        return 0
    else
        return temp_sum / marks_num
    end
end

function pass_vert(r::Robot, side::HorizonSide, temp_sum::Int, marks_num::Int)
    while !isborder(r, side)
        if ismarker(r)
            marks_num += 1
            temp_sum += temperature(r)
        end

        move!(r,side)
    end

    if ismarker(r)
        marks_num += 1
        temp_sum += temperature(r)
    end

    return temp_sum, marks_num
end

function pass_field(r::Robot, temp_sum::Int, marks_num::Int)
    side = Nord

    while !isborder(r, Ost)
        temp_sum, marks_num = pass_vert(r, side, temp_sum, marks_num)

        side = inverse(side)

        move!(r, Ost)
    end

    return pass_vert(r, side, temp_sum, marks_num)
end

inverse(side::HorizonSide) = HorizonSide(mod(Int(side)+2, 4))

max_stars_repo_name: MCPavlo/mirea-progs

max_issues_repo_name: MCPavlo/mirea-progs

max_forks_repo_name: MCPavlo/mirea-progs


text:
using CLEARSWI

TEs = [20]
nifti_folder = "/neurodesktop-storage/swi-demo/01_bids/sub-170705134431std1312211075243167001/ses-1/anat"
magfile = joinpath(nifti_folder, "sub-170705134431std1312211075243167001_ses-1_acq-qsm_run-1_magnitude.nii.gz")
phasefile = joinpath(nifti_folder, "sub-170705134431std1312211075243167001_ses-1_acq-qsmPH00_run-1_phase.nii.gz")

mag = readmag(magfile);
phase = readphase(phasefile);
data = Data(mag, phase, mag.header, TEs);

swi = calculateSWI(data);
# mip = createIntensityProjection(swi, minimum); # minimum intensity projection, other Julia functions can be used instead of minimum
mip = createMIP(swi); # shorthand for createIntensityProjection(swi, minimum)

savenii(swi, "/neurodesktop-storage/swi-demo/swi.nii"; header=mag.header)
savenii(mip, "/neurodesktop-storage/swi-demo/mip.nii"; header=mag.header)


text:
function DOB(ϕ, alpharange, alphastep, tN, tstep)
    alphas = collect(alpharange[1]:alphastep:alpharange[2])
    alphacount = length(alphas)

    result = zeros(tN, tN)
    phi = ϕ.(alphas)

    for k=1:alphacount
        result = result .+ phi[k]*alphastep*D(tN, alphas[k], tstep)
    end
    return result
end

function DOF(ϕ, alpharange, alphastep, tN, tstep)
    alphas = collect(alpharange[1]:alphastep:alpharange[2])
    alphacount = length(alphas)

    result = zeros(tN, tN)

    phi = ϕ.(alphas)

    for k=1:alphacount
        result = result .+ phi[k]*alphastep*F(tN, alphas[k], tstep)
    end
    return result
end

function DORANORT(ϕ, alpharange, alphastep, tN, tstep)
    alphas = collect(alpharange[1]:alphastep:alpharange[2])
    alphacount = length(alphas)

    result = zeros(tN, tN)

    phi = ϕ.(alphas)

    for k=1:alphacount
        result = result .+ phi[k]*alphastep*ranort(alphas[k], tN, tstep)
    end
    return result
end

function ranort(alpha, N)
    k=collect(0:N-1)
    rc = ((-1)*ones(size(k))).^k.*gamma.(alpha+1).*(gamma.(alpha*0.5 .-k.+1).*gamma.(alpha*0.5 .+ k.+1)).^(-1)
    rc = rc*(cos(alpha*π*0.5))

    R = zeros(N, N)

    for m=1:N
        R[m, m:N] = rc[1:N-m+1]
    end

    for i=1:N-1
        for j=i:N
            R[j, i] = R[i, j]
        end
    end
    return R
end
# Multiple dispatch for ranort
function ranort(alpha, N, h)
    R = ranort(alpha, N)
    R = R*h^(-alpha)
    return R
end


text:
### VanillaMCTuner is a dummy tuner type in the sense that it does not perform any tuning
### It is used only for determining whether the Monte Carlo sampler will be verbose

immutable VanillaMCTuner <: MCTuner
  period::Int # Period over which acceptance rate is computed
  verbose::Bool # Specify whether the tuner will be in verbose or silent mode

  function VanillaMCTuner(period::Int, verbose::Bool)
    @assert period > 0 "Adaptation period should be positive."
    new(period, verbose)
  end
end

VanillaMCTuner(; period::Int=100, verbose::Bool=false) = VanillaMCTuner(period, verbose)

### VanillaMCTune holds the temporary output of a Monte Carlo sampler that uses the VanillaMCMCTuner

type VanillaMCTune <: MCTune
  accepted::Int # Number of accepted Monte Carlo samples within current tuner period
  proposed::Int # Number of proposed Monte Carlo samples within current tuner period
  rate::Float64 # Acceptance rate over current tuner period

  function VanillaMCTune(accepted::Int, proposed::Int, rate::Float64)
    @assert accepted >= 0 "Number of accepted MCMC samples should be non-negative."
    @assert proposed >= 0 "Number of proposed MCMC samples should be non-negative."
    new(accepted, proposed, rate)
  end
end

VanillaMCTune(accepted::Int, proposed::Int) = VanillaMCTune(accepted::Int, proposed::Int, NaN)
VanillaMCTune() = VanillaMCTune(0, 0, NaN)

reset!(tune::VanillaMCTune) = ((tune.accepted, tune.proposed) = (0, 0))

count!(tune::VanillaMCTune) = (tune.accepted += 1)

rate!(tune::VanillaMCTune) = (tune.rate = tune.accepted/tune.proposed)


text:
"""
    br_ind()

Gives the sum of values of H and V channel
"""
function br_ind(x::AbstractArray{<:Number, 3})
    sum1=0
    sum2=0
    for i in x[:,:,1]
        sum1+=i
    end
    for i in x[:,:,3]
        sum2+=i
    end
    return sum1,sum2
end


text:
# This file is a part of ForwardDiffPullbacks.jl, licensed under the MIT License (MIT).

using ForwardDiffPullbacks
using Test

include("testfuncs.jl")

@testset "fwd_back" begin
    sum_pow2(x) = sum(map(x -> x^2, x))
    sum_pow3(x) = sum(map(x -> x^3, x))
    f = (xs...) -> (sum(map(sum_pow2, xs)), sum(map(sum_pow3, xs)), 42)
    xs = (2f0, (3, 4f0), SVector(5f0, 6f0, 7f0))
    ΔΩ = (10, 20, 30)

    fy = let f = f; (xs...) -> SVector(f(xs...)); end
    ΔΩy = SVector(ΔΩ)

    @test @tinferred(ForwardDiffPullbacks.forwarddiff_fwd_back(f, xs, Val(1), ΔΩ)) == 280
    @test @tinferred(ForwardDiffPullbacks.forwarddiff_fwd_back(f, xs, Val(2), ΔΩ)) == (600, 1040)
    @test @tinferred(ForwardDiffPullbacks.forwarddiff_fwd_back(f, xs, Val(3), ΔΩ)) == SVector(1600, 2280, 3080)
end


text:
"""
# The default options for [`WebsocketClient`](@ref)
!!! info "maxReceivedFrameSize"
    `0x100000::Integer` 1MiB

    The maximum frame size that the client will accept

!!! info "maxReceivedMessageSize"
    `8 * 0x100000::Integer` 8MiB

    The maximum assembled message size that the client will accept

!!! info "fragmentOutgoingMessages"
    `true::Bool`

    Outgoing frames are fragmented if they exceed the set threshold.

!!! info "fragmentationThreshold"
    `16 * 0x0400::Integer` 16KiB

    Outgoing frames are fragmented if they exceed this threshold.

!!! info "closeTimeout"
    `5::Int`

    The number of seconds to wait after sending a close frame for an acknowledgement to
    return from the server. Will force close the connection if timed out.

!!! info "keepaliveTimeout"
    `1::Union{Int, Bool}`

    The interval in number of seconds to solicit the server with a ping / pong
    response. The connection will be closed if no pong is received within the interval.

    The timer is only active when no data is received from the server within the interval,
    ie. the server will only be pinged if inactive for a period longer than the interval.

    `false` to disable.
    !!! warning
        Due to an underlying issue with [HTTP](https://juliaweb.github.io/HTTP.jl/stable/),
        a client network disconnect will cause the connection to block in it's listen loop,
        only registering `disconnect` when the network re-connects.

        `keepaliveTimeout` uses ping/pong and will register a disconnect more efficiently
        in network outage events.

!!! info "useNagleAlgorithm"
    `false::Bool`

    The Nagle Algorithm makes more efficient use of network resources
    by introducing a small delay before sending small packets so that
    multiple messages can be batched together before going onto the
    wire.  This however comes at the cost of latency, so the default
    is to disable it.  If you don't need low latency and are streaming
    lots of small messages, you can change this to `true`
    !!! info "Julia 1.3"
        This setting only has an affect as of Julia 1.3

!!! info "binary"
    `false::Bool`

    Use Array{UInt8, 1} instead of String as messaging format.
"""
const clientConfig = (
    maxReceivedFrameSize = 1 * 0x100000,
    maxReceivedMessageSize = 8 * 0x100000,
    fragmentOutgoingMessages = true,
    fragmentationThreshold = 16 * 0x0400,
    closeTimeout = 5,
    keepaliveTimeout = 1,
    useNagleAlgorithm = false,
    binary = false
)

"""
# The default options for [`WebsocketServer`](@ref)

!!! info "authfunction"
    `false::Union{Bool, Function}`

    Provide a function with one parametre to check authentication. Must return `Bool`.

    See [Http request validation](@ref).
!!! info "ssl"
    `false::Bool`

    Whether to use ssl on the server.
    !!! warning
        Due to an underlying [issue](https://github.com/JuliaWeb/HTTP.jl/issues/318) in HTTP, a client calling `ws://` on a `wss:://`
        server will cause the server to error and close. Ensure that only ssl traffic can
        reach your server port.

!!! info "sslcert"
    `"./snakeoil/snakeoil.crt"::String` (default is in the SimpleWebsockets module dir, otherwise provide an absolute path)

    Absolute path to your ssl cert

!!! info "sslkey"
    `"./snakeoil/snakeoil.key"::String` (default is in the SimpleWebsockets module dir, otherwise provide an absolute path)

    Absolute path to your ssl key

!!! info "maxReceivedFrameSize"
    `64 * 0x0400::Integer` 64KiB

    The maximum frame size that the server will accept

!!! info "maxReceivedMessageSize"
    `1 * 0x100000::Integer` 1MiB

    The maximum assembled message size that the server will accept

!!! info "fragmentOutgoingMessages"
    `true::Bool`

    Outgoing frames are fragmented if they exceed the set threshold.

!!! info "fragmentationThreshold"
    `16 * 0x0400::Integer` 16KiB

    Outgoing frames are fragmented if they exceed this threshold.

!!! info "closeTimeout"
    `5::Int`

    The number of seconds to wait after sending a close frame for an acknowledgement to
    return from the client. Will force close the client if timed out.

!!! info "keepaliveTimeout"
    `20::Union{Int, Bool}`

    The interval in number of seconds to solicit each client with a ping / pong
    response. The client will be closed if no pong is received within the interval.

    The timer is only active when no data is received from the client within the interval,
    ie. the client will only be pinged if inactive for a period longer than the interval.

    `false` to disable.
    !!! warning
        Due to an underlying issue with [HTTP](https://juliaweb.github.io/HTTP.jl/stable/),
        a server network disconnect will cause all clients to block in their listen loop,
        only registering `disconnect` when the network re-connects.

        `keepaliveTimeout` uses ping/pong and will register the client disconnects more efficiently
        in network outage events.

!!! info "useNagleAlgorithm"
    `false::Bool`

    The Nagle Algorithm makes more efficient use of network resources
    by introducing a small delay before sending small packets so that
    multiple messages can be batched together before going onto the
    wire.  This however comes at the cost of latency, so the default
    is to disable it.  If you don't need low latency and are streaming
    lots of small messages, you can change this to `true`

    !!! info "Julia 1.3"
        This setting only has an affect as of Julia 1.3

!!! info "binary"
    `false::Bool`

    Use Array{UInt8, 1} instead of String as messaging format.
"""
const serverConfig = (
    ssl = false,
    sslcert = normpath(joinpath(dirname(pathof(SimpleWebsockets)), "../snakeoil/snakeoil.crt")),
    sslkey = normpath(joinpath(dirname(pathof(SimpleWebsockets)), "../snakeoil/snakeoil.key")),
    maxReceivedFrameSize = 64 * 0x0400,
    maxReceivedMessageSize = 1 * 0x100000,
    fragmentOutgoingMessages = true,
    fragmentationThreshold = 16 * 0x0400,
    closeTimeout = 5,
    keepaliveTimeout = 20,
    useNagleAlgorithm = false,
    binary = false,
    authfunction = false,
)
"""
    defaultHeaders::Dict{String, String}
The default headers passed to a http upgrade request
```julia
Dict{String, String}(
    "Sec-WebSocket-Version" => "13",
    "Upgrade" => "websocket",
    "Connection" => "Upgrade",
    "Sec-WebSocket-Key" => "", #new key made for every request
)
```
"""
const defaultHeaders = Dict{String, String}(
    "Sec-WebSocket-Version" => "13",
    "Upgrade" => "websocket",
    "Connection" => "Upgrade",
    "Sec-WebSocket-Key" => "",
)
const clientOptions = (;
    reuse_limit = 0,
)
const serverOptions = (;
    sslconfig = nothing,
    verbose = false
)

const CONTINUATION_FRAME = 0x00
const TEXT_FRAME = 0x01
const BINARY_FRAME = 0x02
const CONNECTION_CLOSE_FRAME = 0x08
const PING_FRAME = 0x09
const PONG_FRAME = 0x0a

const WS_FINAL = 0x80
const WS_OPCODE = 0x0F
const WS_MASK = 0x80
const WS_LENGTH = 0x7F
const WS_RSV1 = 0x40
const WS_RSV2 = 0x20
const WS_RSV3 = 0x10

const DECODE_HEADER = 1
const WAITING_FOR_16_BIT_LENGTH = 2
const WAITING_FOR_64_BIT_LENGTH = 3
const WAITING_FOR_MASK_KEY = 4
const WAITING_FOR_PAYLOAD = 5
const COMPLETE = 6

const CLOSE_REASON_NORMAL = 1000
const CLOSE_REASON_GOING_AWAY = 1001
const CLOSE_REASON_PROTOCOL_ERROR = 1002
const CLOSE_REASON_UNPROCESSABLE_INPUT = 1003
const CLOSE_REASON_RESERVED = 1004              #Not to be used on the wire. Reserved value.  Undefined meaning.
const CLOSE_REASON_NOT_PROVIDED = 1005          #Not to be used on the wire
const CLOSE_REASON_ABNORMAL = 1006              #Not to be used on the wire
const CLOSE_REASON_INVALID_DATA = 1007
const CLOSE_REASON_POLICY_VIOLATION = 1008
const CLOSE_REASON_MESSAGE_TOO_BIG = 1009
const CLOSE_REASON_EXTENSION_REQUIRED = 1010
const CLOSE_REASON_INTERNAL_SERVER_ERROR = 1011
const CLOSE_REASON_TLS_HANDSHAKE_FAILED = 1015
const CLOSE_DESCRIPTIONS = Dict{Int, String}(
    1000 => "Normal connection closure",
    1001 => "Remote peer is going away",
    1002 => "Protocol error",
    1003 => "Unprocessable input",
    1004 => "Reserved",
    1005 => "Reason not provided",
    1006 => "Abnormal closure, no further detail available",
    1007 => "Invalid data received",
    1008 => "Policy violation",
    1009 => "Message too big",
    1010 => "Extension requested by client is required",
    1011 => "Internal Server Error",
    1015 => "TLS Handshake Failed"
)

max_stars_repo_name: citkane/SimpleWebsockets

max_issues_repo_name: citkane/SimpleWebsockets

max_forks_repo_name: citkane/SimpleWebsockets


text:
#FileIO2 base types & core functions
#-------------------------------------------------------------------------------

#==Lists for code generation
===============================================================================#
const fmtsymblist = Symbol[] #List of sybols corresponding to DataFormat types

#==Data encoding definitions
===============================================================================#
abstract type DataEncoding end
abstract type BinaryEncoding <: DataEncoding end
abstract type TextEncoding <: DataEncoding end
abstract type UnknownEncoding <: DataEncoding end

#TODO: Add Endianess for Binary encoding??

#Specific text encoding schemes:
abstract type UnknownTextEncoding <: TextEncoding end
abstract type Unicode <: TextEncoding end
abstract type ASCIIEncoding <: TextEncoding end
abstract type UTF8Encoding <: Unicode end
abstract type UTF16Encoding <: Unicode end
abstract type UTF32Encoding <: Unicode end

#==Generic data format/file definitions
===============================================================================#
struct Shorthand{T}; end; #Temp: used to dispatch on symbol (ex: Shorthand{:png})
Shorthand(datafmt::Symbol) = Shorthand{datafmt}();

abstract type DataFormat end
abstract type UnknownDataFormat <: DataFormat end

#Image formats:
abstract type ImageFormat{E<:DataEncoding} <: DataFormat end
abstract type PixelImageFormat{E<:DataEncoding} <: ImageFormat{E} end
abstract type VectorImageFormat{E<:DataEncoding} <: ImageFormat{E} end

#File object definitions
#-------------------------------------------------------------------------------
mutable struct File{T<:DataFormat}
	path::String
end
const UnknownFileFormat = File{UnknownDataFormat}

#Handy way to construct file objects:
File(path::String) = @info("TODO: Implement filetype auto-detection")
File(::Type{T}, path::String) where T<:DataFormat = File{T}(path)
#Shortcut to construct file object of default data format wrt symbol:
File(datafmt::Symbol, path::String) = File(Shorthand(datafmt), path)
File(::Shorthand{T}, path::String) where T =
	throw(ArgumentError("Unrecognized data format: File(:$T, ::String)"))

#==Text format definitions
===============================================================================#
abstract type TextFormat{E<:TextEncoding} <: DataFormat end

#Handy aliases (Eventually want to export aliases):
#-------------------------------------------------------------------------------
const TextFmt = TextFormat{UnknownTextEncoding}
const ASCIIFmt = TextFormat{ASCIIEncoding}
const UTF8Fmt  = TextFormat{UTF8Encoding}
const UTF16Fmt = TextFormat{UTF16Encoding}
const UTF32Fmt = TextFormat{UTF32Encoding}
push!(fmtsymblist, :TextFmt, :ASCIIFmt, :UTF8Fmt, :UTF16Fmt, :UTF32Fmt)

#Specify expected file extensions:
Extensions(::Type{File{TextFmt}}) = ["txt", "log", "dat", "csv"]

#==Tabular (text) data format definitions
===============================================================================#
#Identify Delimiter Seperated Value (DSV) schemes:
abstract type DelimiterScheme end
abstract type SpaceDelimiters <: DelimiterScheme end
abstract type TabDelimiters <: DelimiterScheme end
abstract type CommaDelimiters <: DelimiterScheme end
#Actually use the ASCII codes created to store table data to file:
abstract type ASCIIDelimiters <: DelimiterScheme end

#Delimiter seperated values:
abstract type DSVFormat{E<:TextEncoding, D<:DelimiterScheme} <: TextFormat{TextEncoding} end
abstract type CSVFormat{E<:TextEncoding} <: DSVFormat{E, CommaDelimiters} end
	const CSVFmt = CSVFormat{UnknownTextEncoding}
push!(fmtsymblist, :CSVFmt)

#==Markup language data format definitions
===============================================================================#
abstract type HTMLFormat{E<:TextEncoding} <: DataFormat end
abstract type XMLFormat{E<:TextEncoding} <: DataFormat end
abstract type MarkdownFormat{E<:TextEncoding} <: DataFormat end
abstract type AsciiDocFormat{E<:TextEncoding} <: DataFormat end

#TODO: Is the above better, or should we structure things this way:
#abstract type HTMLFmt{E<:TextEncoding} <: TextFormat{E} end
#...or how about:
#abstract type HTMLFmt{TextFormat} <: DataFormat end
#???
#...Try to figure out which way is most natural/easiest to use...

#Handy aliases
#-------------------------------------------------------------------------------
const HTMLFmt = HTMLFormat{UnknownTextEncoding}
const ASCIIHTMLFmt = HTMLFormat{ASCIIEncoding}
const UTF8HTMLFmt  = HTMLFormat{UTF8Encoding}
push!(fmtsymblist, :HTMLFmt, :ASCIIHTMLFmt, :UTF8HTMLFmt)

const XMLFmt = XMLFormat{UnknownTextEncoding}
const MarkdownFmt = MarkdownFormat{UnknownTextEncoding}
const AsciiDocFmt = AsciiDocFormat{UnknownTextEncoding}
#TODO: Register others
push!(fmtsymblist, :XMLFmt, :MarkdownFmt, :AsciiDocFmt)

#Specify expected file extensions:
Extensions(::Type{File{HTMLFmt}}) = ["htm", "html"]

#==Bitmap image formats
===============================================================================#
abstract type BMPFmt  <: PixelImageFormat{BinaryEncoding} end
abstract type PNGFmt  <: PixelImageFormat{BinaryEncoding} end
abstract type GIFFmt  <: PixelImageFormat{BinaryEncoding} end
abstract type JPEGFmt <: PixelImageFormat{BinaryEncoding} end
abstract type TIFFFmt <: PixelImageFormat{BinaryEncoding} end
#NOTE: Default show() function gives desired result
push!(fmtsymblist, :BMPFmt, :PNGFmt, :GIFFmt, :JPEGFmt, :TIFFFmt)

#==Vector image formats
===============================================================================#
abstract type SVGFormat{E<:DataEncoding} <: VectorImageFormat{E} end
	const SVGFmt = SVGFormat{UnknownEncoding}
abstract type CGMFmt <: VectorImageFormat{BinaryEncoding} end
abstract type EPSFormat{E<:TextEncoding} <: VectorImageFormat{E} end
	const EPSFmt = EPSFormat{UnknownTextEncoding}
abstract type EMFFmt <: VectorImageFormat{BinaryEncoding} end
abstract type STLImgFormat{E<:TextEncoding} <: VectorImageFormat{E} end
	const STLImgFmt = SVGFormat{UnknownTextEncoding}
push!(fmtsymblist, :SVGFmt, :CGMFmt, :EPSFmt, :EMFFmt, :STLImgFmt)

#==Default File object constructors
===============================================================================#
File(::Shorthand{:text}, path::String) = File{TextFmt}(path)
File(::Shorthand{:csv}, path::String) = File{CSVFmt}(path)
File(::Shorthand{:html}, path::String) = File{HTMLFmt}(path)
File(::Shorthand{:xml}, path::String) = File{XMLFmt}(path)
File(::Shorthand{:md}, path::String) = File{MarkdownFmt}(path)
File(::Shorthand{:adoc}, path::String) = File{AsciiDocFmt}(path)

File(::Shorthand{:svg}, path::String) = File{SVGFmt}(path)
File(::Shorthand{:cgm}, path::String) = File{CGMFmt}(path)
File(::Shorthand{:eps}, path::String) = File{EPSFmt}(path)
File(::Shorthand{:emf}, path::String) = File{EMFFmt}(path)
File(::Shorthand{:stl}, path::String) = File{STLImgFmt}(path)

File(::Shorthand{:bmp}, path::String) = File{BMPFmt}(path)
File(::Shorthand{:png}, path::String) = File{PNGFmt}(path)
File(::Shorthand{:gif}, path::String) = File{GIFFmt}(path)
File(::Shorthand{:jpeg}, path::String) = File{JPEGFmt}(path)
File(::Shorthand{:tiff}, path::String) = File{TIFFFmt}(path)

#==File object casting functions
===============================================================================#
#TODO: Add more casting functions for other DataFormat types
#Question: Should casting be done using Base.convert instead?

File(datafmt::Symbol, f::File) = File(Shorthand(datafmt), f)
File(::Shorthand{T}, f::TF) where {T,TF<:File} =
	throw(ArgumentError("Conversion not possible: File($T, ::$TF)"))

#Markup language --> plain text:
#TODO: Use convert()??
File(::Type{TextFormat}, f::File{HTMLFormat{E}}) where E<:TextEncoding = File(TextFormat{E}, f.path)
File(::Shorthand{:text}, f::File{HTMLFormat{E}}) where E<:TextEncoding = File(TextFormat{E}, f.path)

#Casting on Vector{File} to arbitrary data format:
File(::Type{RFMT}, v::Vector{VT}) where {RFMT<:DataFormat, VT<:File} = map((f)->File(RFMT, f), v)
File(datafmt::Symbol, v::Vector{VT}) where VT<:File = map((f)->File(datafmt, f), v)

#==Generic data reader/writer functions
===============================================================================#
#Define generic interface for user-defined reader/writer state-machines:
abstract type AbstractDataIO{READ,WRITE,T<:DataFormat} end
const AbstractDataIORW{T<:DataFormat} = AbstractDataIO{true,true,T} #Same state machine to read/write
#If user prefers to seperate reader/writer state machines:
const AbstractReader{T<:DataFormat} = AbstractDataIO{true,false,T}
const AbstractWriter{T<:DataFormat} = AbstractDataIO{false,true,T}

#Identify IO options & make it easier to dispatch on types (ex: read function):
struct IOOptions{READ,WRITE}
	create::Bool
	truncate::Bool
	append::Bool
end
const IOOptionsRead = IOOptions{true,false}
const IOOptionsWrite = IOOptions{false,true}
const IOOptionsReadWrite = IOOptions{true,true}
function IOOptionsWrite(write::Bool, create::Bool, truncate::Bool, append::Bool)
	write = write||create||truncate||append
	create = write #Don't create for read-only
	return (write, create, truncate, append)
end
function IOOptionsWrite(write::Bool, create::Nothing, truncate::Bool, append::Bool)
	write = write||truncate||append
	create = write #Don't create for read-only
	return (write, create, truncate, append)
end
function IOOptions(;read=nothing, write::Bool=false,
	create = nothing, truncate::Bool=false, append::Bool=false)

	(write, create, truncate, append) = IOOptionsWrite(write, create, truncate, append)
	if nothing==read
		#if user specified write, default is no read;
		#if user did not specify write, default is read.
		read = !write
	end
	return IOOptions{read, write}(create, truncate, append)
end

#==Base _open "algorithm" (not exported externally)
===============================================================================#

#Open for read:
function _open(fn::Function, f::File{DF}, ::IOOptionsRead, args...; kwargs...) where DF<:DataFormat
	readerlist = subtypes(AbstractReader{DF})
	dataiolist = subtypes(AbstractDataIORW{DF})
	if length(readerlist) + length(dataiolist) < 1
		msg = "No registered readers for $DF"
		error(msg)
	end
	for reader in readerlist; try
		result = fn(reader, f.path, args...; kwargs...)
		return result
	catch; end; end
	for reader in dataiolist; try
		result = fn(reader, f.path, args...; kwargs...)
		return result
	catch; end; end
	listall = vcat(readerlist, dataiolist)
	msg = "Failed to $fn $f with readers: $listall"
	error(msg)
end

#Open for write:
function _open(fn::Function, f::File{DF}, opt::IOOptionsWrite, args...; kwargs...) where DF<:DataFormat
	writerlist = subtypes(AbstractWriter{DF})
	dataiolist = subtypes(AbstractDataIORW{DF})
		writerlist = vcat(writerlist, dataiolist)
	if length(writerlist) < 1
		msg = "No registered writers for $DF"
		error(msg)
	end
	for writer in writerlist; try
		result = fn(writer, f.path, args...; kwargs..., opt=opt)
		return result
	catch; end; end
	msg = "Failed to $fn $f with writers: $writerlist"
	error(msg)
end

#Open for read/write (Neither pure readers nor writers):
function _open(fn::Function, f::File{DF}, opt::IOOptions, args...; kwargs...) where DF<:DataFormat
	dataiolist = subtypes(AbstractDataIORW{DF})
	if length(dataiolist) < 1
		msg = "No registered AbstractDataIO for $DF"
		error(msg)
	end
	for dataio in dataiolist; try
		result = fn(dataio, f.path, args...; kwargs..., opt=opt)
		return result
	catch; end; end
	msg = "Failed to $fn $f with AbstractDataIO: $dataiolist"
	error(msg)
end

#==Generic open/close read/write functions
===============================================================================#
#(High-level API called by user)

function open(f::File, args...; read=nothing, write::Bool=false,
	create=nothing, truncate::Bool=false, append::Bool=false, kwargs...)
	opt = IOOptions(read=read, write=write, create=create, truncate=truncate, append=append)
	return _open(open, f, opt, args...; kwargs...)
end

#Support open-do (default declaration does not support kwargs...):
function open(fn::Function, file::File, args...; kwargs...)
    reader = open(file, args...; kwargs...)
    try
        fn(reader)
    finally
        close(reader)
    end
end

function read(f::File, args...; kwargs...)
	return _open(read, f, IOOptions(read=true), args...; kwargs...)
end

function write(f::File, args...; kwargs...)
	return _open(write, f, IOOptions(write=true), args...; kwargs...)
end

#Support for open() do syntax:
function open(fn::Function, iot::Type{IOT}, args...; kwargs...) where IOT<:AbstractDataIO
	io = open(iot, args...; kwargs...)
	try
		return fn(io)
	finally
		close(io)
	end
end

#Read in entire file using a particular reader:
function read(RT::Type{T}, path::String) where T<:AbstractReader
	open(RT, path) do reader
		return read(reader, String)
	end
end

#Define generic read(reader, filepath) functionality:
function read(RT::Type{T}, path::String, args...; kwargs...) where T<:AbstractReader
	open(RT, path) do reader
		return read(reader, args...; kwargs...)
	end
end

#Define generic write(writer, filepath) functionality:
function write(WT::Type{T}, path::String, args...;
	opt::IOOptionsWrite=IOOptions(write=true), kwargs...) where T<:AbstractWriter
	open(WT, path, opt=opt) do writer
		return write(writer, args...; kwargs...)
	end
end

#==Generate friendly show functions
===============================================================================#
Base.string(f::File) = f.path

for fmt in fmtsymblist
	eval(genexpr_ShowSimpleFileSign(fmt))
end

function Base.show(io::IO, filelist::Vector{T}) where T<:File
	typestr = string(T)
	print(io, "$typestr[\n")
	for f in filelist
		print(io, "   "); show(io, f); println(io)
	end
	print(io, "]\n")
end

#Last line


text:
struct HestonGaussLaguerre <: HestonIntegration
  integration::GaussLaguerreIntegration
end

HestonGaussLaguerre(intOrder::Int) = HestonGaussLaguerre(GaussLaguerreIntegration(intOrder))

num_evals(integration::HestonGaussLaguerre) = get_order(integration.integration)

struct Gatheral <: ComplexLogFormula end

struct AnalyticHestonEngine{C <: ComplexLogFormula} <: AbstractHestonEngine{HestonGaussLaguerre}
  model::HestonModel
  evaluations::Int
  cpxLog::C
  integration::HestonGaussLaguerre
end

function AnalyticHestonEngine(hestonModel::HestonModel)
  evals = 0
  cpxLog = Gatheral()
  integration = HestonGaussLaguerre(144)

  return AnalyticHestonEngine{Gatheral}(hestonModel, evals, cpxLog, integration)
end

add_on_term(engine::AnalyticHestonEngine, ::Real, ::Real, ::Int) = complex(0.0)

# Helper #
struct FJHelper{C <: ComplexLogFormula, A <: AbstractHestonEngine} <: IntegrationFunction
  j::Int
  kappa::Float64
  theta::Float64
  sigma::Float64
  v0::Float64
  cpxLog::C
  term::Float64
  x::Float64
  sx::Float64
  dd::Float64
  sigma2::Float64
  rsigma::Float64
  t0::Float64
  b::Int
  g_km1::Float64
  engine::A
end

(fjh::FJHelper)(phi::Float64) = fjh(phi, fjh.cpxLog)

function (fjh::FJHelper)(phi::Float64, cpxLog::Gatheral)
  rpsig = fjh.rsigma * phi
  t1 = fjh.t0 + complex(0.0, -rpsig)
  d = sqrt(t1 * t1 - fjh.sigma2 * phi * complex(-phi, fjh.j == 1 ? 1.0 : -1.0))

  ex = exp(-d * fjh.term)
  addOnTerm = add_on_term(fjh.engine, phi, fjh.term, fjh.j)

  if phi != 0.0
    if fjh.sigma > 1e-5
      p = (t1 - d) / (t1 + d)
      g = log((1.0 - p * ex) / (1.0 - p))

      return imag(exp(fjh.v0 * (t1 - d) * (1.0 - ex)/ (fjh.sigma2 * (1.0 - ex * p)) +
                  (fjh.kappa * fjh.theta) / fjh.sigma2 * ((t1 - d) * fjh.term - 2.0 * g) +
                  complex(0.0, phi * (fjh.dd - fjh.sx)) + addOnTerm)) / phi
    else
      #
      td = phi / (2.0 * t1) * complex(-phi, fjh.j == 1 ? 1.0 : -1.0)
      p = td * fjh.sigma2 / (t1 + d)
      g = p * (1.0 - ex)
      return imag(fjh.v0 * td * (1.0 - ex) / (1.0 - p * ex) + (fjh.kappa * fjh.theta) *
                  (td * fjh.term - 2.0 * g / fjh.sigma2) + complex(0.0, phi * (fjh.dd - fjh.sx))
                  + addOnTerm) / phi
    end
  else
    # use l'Hospital's rule to get lim_ {phi -> 0}
    if j == 1
      kmr = fjh.rsigma - fjh.kappa
      if abs(kmr) > 1e-7
        return fjh.dd - fjh.sx + (exp(kmr * fjh.term) * fjh.kappa * fjh.theta - fjh.kappa
                * fjh.theta * (kmr * fjh.term + 1.0)) / (2.0 * kmr * kmr) - fjh.v0 *
                (1.0 - exp(kmr * fjh.term)) / (2.0 * kmr)
      else
        # kappa = rho * sigma
        return fjh.dd - fjh.sx + 0.25 * fjh.kappa * fjh.theta * fjh.term * fjh.term + 0.5 * fjh.v0 * fjh.term
      end
    else
      return fjh.dd - fjh.sx - (exp(-fjh.kappa * fjh.term) * fjh.kappa * fjh.theta + fjh.kappa * fjh.theta *
              (fjh.kappa * fjh.term - 1.0)) / (2.0 * fjh.kappa * fjh.kappa) - fjh.v0 * (1.0 -
              exp(-fjh.kappa * fjh.term)) / (2.0 * fjh.kappa)
    end
  end
end

function FJHelper(kappa::Float64, theta::Float64, sigma::Float64, v0::Float64, s0::Float64, rho::Float64,
          engine::AbstractHestonEngine, cpxLog::ComplexLogFormula, term::Float64, strike::Float64,
          ratio::Float64, j::Int)

  x = log(s0)
  dd = x - log(ratio)
  # sig = get_sigma(engine.model)
  t0 = kappa - (j == 1 ? rho * sigma : 0.0)

  FJHelper(j, kappa, theta, sigma, v0, cpxLog, term, x, log(strike), dd, sigma * sigma, rho * sigma, t0, 0, 0.0, engine)
end

calculate!(integration::HestonGaussLaguerre, ::Float64, f::FJHelper) = integration.integration(f)

function _calculate!(pe::AbstractHestonEngine, opt::EuropeanOption)
  payoff = opt.payoff
  process = pe.model.process

  riskFreeDiscount = discount(get_risk_free_rate(process), opt.exercise.dates[end])
  dividendDiscount = discount(get_dividend_yield(process), opt.exercise.dates[end])

  spotPrice = get_s0(process).value
  strikePrice = payoff.strike

  term = get_time(process, opt.exercise.dates[end])

  val = do_calculation!(pe, opt, riskFreeDiscount, dividendDiscount, spotPrice, strikePrice, term, get_kappa(pe.model),
                  get_theta(pe.model), get_sigma(pe.model), get_v0(pe.model), get_rho(pe.model), payoff,
                  pe.integration, pe.cpxLog, pe.evaluations)

  opt.results.value = val
  return opt
end

get_val(::Call, spotPrice::Float64, dividendDiscount::Float64, riskFreeDiscount::Float64, strikePrice::Float64, p1::Float64, p2::Float64) =
        spotPrice * dividendDiscount * (p1 + 0.5) - strikePrice * riskFreeDiscount * (p2 + 0.5)

get_val(::Put, spotPrice::Float64, dividendDiscount::Float64, riskFreeDiscount::Float64, strikePrice::Float64, p1::Float64, p2::Float64) =
        spotPrice * dividendDiscount * (p1 - 0.5) - strikePrice * riskFreeDiscount * (p2 - 0.5)

function do_calculation!(pe::AbstractHestonEngine, opt::EuropeanOption, riskFreeDiscount::Float64, dividendDiscount::Float64,
                        spotPrice::Float64, strikePrice::Float64, term::Float64, kappa::Float64, theta::Float64,
                        sigma::Float64, v0::Float64, rho::Float64, payoff::PlainVanillaPayoff, integration::HestonGaussLaguerre,
                        cpxLog::ComplexLogFormula, evaluations::Int)
  # calculation stuff
  ratio = riskFreeDiscount / dividendDiscount
  c_inf = min(10.0, max(0.0001, sqrt(1.0 - rho^2) / sigma)) * (v0 + kappa * theta * term)

  evaluations = 0
  p1 = calculate!(integration, c_inf, FJHelper(kappa, theta, sigma, v0, spotPrice, rho, pe, cpxLog, term, strikePrice,
                  ratio, 1)) / pi
  evaluations += num_evals(integration)

  p2 = calculate!(integration, c_inf, FJHelper(kappa, theta, sigma, v0, spotPrice, rho, pe, cpxLog, term, strikePrice,
                  ratio, 2)) / pi
  evaluations += num_evals(integration)

  return get_val(payoff.optionType, spotPrice, dividendDiscount, riskFreeDiscount, strikePrice, p1, p2)
end


text:
### Extract Terms

# returns RegexMatch Type
# function extract_numbers(s::AbstractString)
#            res = eachmatch(r"\d+",s)
#            return collect(res)
# end

"""
extract_numbers: Extract all numbers from a given text
"""
function extract_numbers(text::AbstractString)
        res = [m.match for m in eachmatch(NUMBERS_REGEX,text)]
        return res
    end

"""
extract_phonenumbers: Extract all phone numbers from a given text
"""
function extract_phonenumbers(text::AbstractString)
        res = [m.match for m in eachmatch(PHONENUMBERS_REGEX,text)]
        return res
    end

"""
extract_currencies: Extract all currencies from a given text
"""
function extract_currencies(text::AbstractString)
        res = [m.match for m in eachmatch(CURRENCY_REGEX,text)]
        return res
    end

"""
extract_btc: Extract all Bitcoin BTC Address/Values from a given text
"""
function extract_btc(text::AbstractString)
        res = [m.match for m in eachmatch(BTC_ADDRESS_REGEX,text)]
        return res
    end

"""
extract_mastercards: Extract all MasterCard Address/Values from a given text
"""
function extract_mastercards(text::AbstractString)
        res = [m.match for m in eachmatch(MASTERCard_REGEX,text)]
        return res
    end

"""
extract_visacards: Extract all VisaCards Address/Values from a given text
"""
function extract_visacards(text::AbstractString)
        res = [m.match for m in eachmatch(VISACard_REGEX,text)]
        return res
    end

"""
extract_emails: Extract all emails from a given text
"""
function extract_emails(text::AbstractString)
        res = [m.match for m in eachmatch(EMAIL_REGEX,text)]
        return res
    end

"""
extract_urls: Extract all urls from a given text
"""
function extract_urls(text::AbstractString)
        res = [m.match for m in eachmatch(URL_REGEX,text)]
        return res
    end

"""
extract_puncts: Extract all punctuations from a given text
"""
function extract_puncts(text::AbstractString)
        res = [m.match for m in eachmatch(PUNCTS_REGEX,text)]
        return res
    end

"""
extract_special_characters: Extract all special characters from a given text
"""
function extract_special_characters(text::AbstractString)
        res = [m.match for m in eachmatch(SPECIAL_CHARACTERS_REGEX,text)]
        return res
    end

"""
extract_emojis: Extract all emojis from a given text
"""
function extract_emojis(text::AbstractString)
        res = [m.match for m in eachmatch(EMOJI_REGEX,text)]
        return res
    end

"""
extract_userhandles: Extract all userhandles and mentions from a given text
"""
function extract_userhandles(text::AbstractString)
        res = [m.match for m in eachmatch(USER_HANDLES_REGEX,text)]
        return res
    end

"""
extract_hashtags: Extract all hashtags from a given text
"""
function extract_hashtags(text::AbstractString)
        res = [m.match for m in eachmatch(HASHTAG_REGEX,text)]
        return res
    end

"""
extract_userhandles: Extract all dates from a given text
"""
function extract_dates(text::AbstractString)
        res = [m.match for m in eachmatch(DATE_REGEX,text)]
        return res
    end

"""
extract_pobox: Extract all Post Office Box from a given text
"""
function extract_pobox(text::AbstractString)
        res = [m.match for m in eachmatch(PoBOX_REGEX,text)]
        return res
    end

"""
extract_streetaddress: Extract all Street Address from a given text
"""
function extract_streetaddress(text::AbstractString)
        res = [m.match for m in eachmatch(STREET_ADDRESS_REGEX,text)]
        return res
    end

"""
extract_md5sha: Extract all MD5 or SHA Hashes from a given text
"""
function extract_md5sha(text::AbstractString)
        res = [m.match for m in eachmatch(MD5_SHA_REGEX,text)]
        return res
    end


text:
using AlgebraicDynamics.UWDDynam
using AlgebraicDynamics.DWDDynam

using StaticArrays
using DynamicalSystems
import DynamicalSystems: trajectory

using Base.Iterators

"""    trajectory(r::DiscreteResourceSharer, u0::AbstractVector, p, nsteps::Int; dt::Int = 1)

Evolves the resouce sharer `r` for `nsteps` times with step size `dt`, initial condition `u0`, and parameters `p`.
"""
trajectory(r::DiscreteResourceSharer, u0::AbstractVector, p, T::Int; dt::Int= 1) =
  trajectory((u,p,t) -> eval_dynamics(r, u, p, t), u0, p, T; dt = dt)

"""    trajectory(m::DiscreteMachine, u0::AbstractVector, xs::AbstractVector, p, nsteps::Int; dt::Int = 1)

Evolves the machine `m` for `nsteps` times with step size `dt`, initial condition `u0`, and parameters `p`. Any inputs to `m` are determied by `xs`. If `m` has no inputs then you can omit `xs`.
"""
trajectory(m::DiscreteMachine, u0::AbstractVector, xs::AbstractVector,  p, T::Int; dt::Int= 1) =
  trajectory((u,p,t) -> eval_dynamics(m, u, xs, p, t), u0, p, T; dt = dt)

trajectory(m::DiscreteMachine, u0::AbstractVector, x,  p, T::Int; dt::Int = 1) =
  trajectory(m, u0, collect(repeated(x, ninputs(m))), p, T; dt = dt)

trajectory(m::DiscreteMachine, u0::AbstractVector, p, T::Int; dt::Int = 1) =
  trajectory(m, u0, [], p, T; dt = dt)

function trajectory1d(update::Function, u0::Vector{S}, p, T::Int; dt::Int) where S
  dt = round(Int, dt)
  tvec = 0:dt:T
  L = length(tvec)
  data = Vector{S}(undef, L)
  data[1] = u0[1]
  u = copy(u0)
  for i in 2:L
    for j in 1:dt
      u = update(u, p, (i - 1)*dt + j)
    end
    data[i] = u[1]
  end
  return data
end

function trajectory(update::Function, u0::Vector{S}, p, T::Int; dt::Int) where S
  if length(u0) == 1
    return trajectory1d(update, u0, p, T; dt = dt)
  end
  dt = round(Int, dt)
  tvec = 0:dt:T
  L = length(tvec)
  data = Vector{SVector{length(u0), S}}(undef, L)
  data[1] = u0
  u = copy(u0)
  for i in 2:L
    for j in 1:dt
      u = update(u, p, (i - 1)*dt + j)
    end
    data[i] = u
  end
  return Dataset(data)
end


text:
using GraphMakie, CairoMakie, Graphs, NetworkLayout
using Pkg, Revise
using CSV, DataFrames

Pkg.activate(".")
using RRSTExperiments

n, E, C = parse_graph_data("data/london/london.gr")
g = SimpleGraph(n)
for e in E
  add_edge!(g, e[1], e[2])
end

# df = CSV.read("data/london/london_output.csv", DataFrame)

layout = Stress(Ptype=Float32)
f, ax, p = graphplot(g, layout=layout)
hidedecorations!(ax); hidespines!(ax); ax.aspect = DataAspect(); f
# save("figure.pdf", f, pt_per_unit = 1)

n, E, C = parse_graph_data("data/ryanair/ryanair.gr")
node_limit = ceil(Int, n / 4)
println(node_limit)
g = SimpleGraph(node_limit)
for e in E
  if (e[1] ≤ node_limit && e[2] ≤ node_limit)
    add_edge!(g, e[1], e[2])
  end
end

layout = Shell(nlist=[floor(Int, node_limit/2):node_limit,])
f, ax, p = graphplot(g, layout=layout)
hidedecorations!(ax); hidespines!(ax); ax.aspect = DataAspect();
# save("ryanair.pdf", f, pt_per_unit = 1)