text:
export get_prop_mass,
       hohmann_circular_coplanar,
       hohmann_circular,
       hohmann_circle2circle,
       get_launch_asymptote,
       lambert,
       lambert_target,
       lambert_transfer

function get_prop_mass(m0, dV, Isp)
    # dV in m/s, Isp in s, m0 in kg
    return m0 * (1. - exp(-dV/(9.81*Isp)))
end

"Hohmann transfer between two circular orbits"
function hohmann_circle2circle(a1, a2, mu)
    aT = (a1 + a2)/2.
    vc1 = getv( mu, a1, a1 )
    vc2 = getv( mu, a2, a2 )

    vT1 = getv( mu, a1, aT )
    vT2 = getv( mu, a2, aT )

    dV1 = abs(vc1 - vT1)
    dV2 = abs(vc2 - vT2)

    return (aT, dV1, dV2)
end

function get_launch_asymptote(v_INF_HCI::Vector{S}, epoch) where {S<:AbstractFloat}
    T = get_hci2eci_matrix( epoch )
    v_INF_ECI = T*v_INF_HCI
    α = atan(v_INF_ECI[2],v_INF_ECI[1])
    δ = asin(v_INF_ECI[3]/norm(v_INF_ECI))
    return (α,δ)
end

"""
Compute the relevant quantities for a Hohmann transfer between objects
orbiting the Sun, assuming circular, coplanar orbits also assuming
circular parking orbit
"""
function hohmann_circular_coplanar(muDepart,
                                   aDepart,
                                   rPark,
                                   muDest,
                                   aDest,
                                   aCapture,
                                   rCapture)

    # v_INF_depart: v_infinity required wrt departure planet
    # v_INF_arrive: v_infinity wrt destination planet
    (aTrans, v_INF_depart, v_INF_arrive) = hohmann_circle2circle(aDepart, aDest, muSun)

    ### Departure

    # departure velocity wrt Sun:
    v_SC_S_depart = getv(muSun, aDepart, aTrans)

    # delta V required:
    dV_depart = getvhyp(muDepart, rPark, v_INF_depart) -
                sqrt(muDepart/rPark)

    ### Arrival

    # arrival velocity wrt Sun:
    v_SC_S_arrive = getv(muSun, aDest, aTrans)

    # delta V required for capture into orbit specified by aCapture and rCapture
    dV_cap = getArrivalDV( muDest, rCapture, aCapture, v_INF_arrive )

    TOF = getPeriod(muSun, aTrans)/2. # seconds

    return (v_INF_depart, dV_depart, v_INF_arrive, dV_cap, TOF)
end

function getArrivalDV(muDest, rCapture, aCapture, v_INF_arrive)
    return abs( getvhyp(muDest, rCapture, v_INF_arrive) -
               getv(muDest, rCapture, aCapture) )
end

"""
Compute the relevant quantities for a Hohmann transfer between objects
orbiting the Sun, assuming circular non-coplanar orbits
"""
function hohmann_circular(muDepart, aDepart, rPark, muDest,
                          aDest, dInc, aCapture, rCapture)

    aTrans = (aDepart + aDest)/2

    # Departure

    # departure velocity wrt Sun:
    v_SC_S_depart = getv(muSun, aDepart, aTrans)
    v_depart_S = sqrt(muSun/aDepart)

    # arrival velocity wrt Sun:
    v_SC_S_arrive = getv(muSun, aDest, aTrans)
    v_dest_S = sqrt(muSun/aDest)

    # v_infinity required
    v_INF_depart(alpha) = sqrt(v_SC_S_depart^2 + v_depart_S^2 -
                        2*v_SC_S_depart*v_depart_S*cos(alpha))
    v_INF_arrive(alpha) = sqrt(v_SC_S_arrive^2 + v_dest_S^2 -
                               2*v_SC_S_arrive*v_dest_S*cos(dInc - alpha))

    # numerically solve for the optimal plane change split
    f(beta) = v_SC_S_depart*v_depart_S*sin(beta)/v_INF_depart(beta) -
                v_dest_S*v_SC_S_arrive*sin(dInc - beta)/v_INF_arrive(beta)
    alpha1 = fzero(f,0.,dInc)

    # delta V required for departure:
    dV_depart = getvhyp(muDepart, rPark, v_INF_depart(alpha1))-
                sqrt(muDepart/rPark)

    # delta V required for capture into orbit specified by aCapture and rCapture
    dV_cap = getArrivalDV( muDest, rCapture, aCapture, v_INF_arrive(alpha1) )

    TOF = getPeriod(muSun, aTrans)/2. #seconds

    return (v_INF_depart(alpha1), dV_depart, v_INF_arrive(alpha1), dV_cap, alpha1, TOF)
end

"""
Translation of lambert_target.m

Original Matlab code provided by Brent Barbee
"""
function lambert_target(mu, TOF, r1::Vector{T}, r2::Vector{T},
                        v1::Vector{T}, v2::Vector{T}) where {T<:AbstractFloat}

    uu = cross(r1, r2)

    uh = (1/norm(uu))*uu

    (vi1, vf1) = lambert(mu, TOF, r1, r2, uh)

    tot_dv1 = norm(v1-vi1) + norm(vf1-v2)

    uu = cross(r2, r1)

    uh = (1/norm(uu))*uu

    (vi2, vf2) = lambert(mu, TOF, r1, r2, uh)

    tot_dv2 = norm(v1-vi2) + norm(vf2-v2)

    if tot_dv1 < tot_dv2

        vi = vi1
        vf = vf1

    else

        vi = vi2
        vf = vf2

    end
    return (vi, vf)
end

"""
Title     : Lambert
Purpose   : Given a conic gravity field, compute the required velocity
            to transfer between initial and final inertial position
            vectors in a specified transfer time interval.
Inputs    : mu  - (km^3/s^2) Gravitational constant
            dt  - (s)        Transfer time interval
            ri  - (km)       Init. inertial position vector
            rf  - (km)       Final inertial position vector
            uh  - (na)       Unit angular momentum vector
Outputs   : vi  - (km/s)     Init. inertial velocity vector (required)
            vf  - (km/s)     Final inertial velocity vector
Comments  : 1) This routine does not have a multi-revolution capability
            2) The unit angular momentum vector determines the plane,
               and direction, of the transfer
Reference : Shepperd, S.W., unpublished notes.
Original author: Brent Barbee
"""
function lambert(mu, dt, ri::Vector{T}, rf::Vector{T},
                 uh::Vector{T}) where {T<:AbstractFloat}

    ### Initialization Iteration Loop

    r0 = ri - dot(ri, uh) * uh
    r1 = rf - dot(rf, uh) * uh

    if abs(dot(uh, uh) - 1) > 10000 * eps()
        error("Lambert : Aborting : Normal vector not unitary\n")
    end

    if abs(dot(ri, uh)) > 0.00001
        error("Lambert : Warning : Init. position not in plane\n")
    end

    if abs(dot(rf, uh)) > 0.00001
        error("Lambert : Warning : Final position not in plane\n")
    end

    imax      = 20
    maxu      = typemax(Int64)
    tolerance = 1000 * eps()

    m0 = norm(r0)
    m1 = norm(r1)
    cc = norm(r1 - r0)
    ss = (m0 + m1 + cc) / 2

    rc = ss / 2
    vc = sqrt(mu / rc)
    wc = vc / rc

    k1 = sqrt(abs((ss - m0) * (ss - m1)))
    k  = dot(cross(r0, r1), uh)

    if  (k1 * k1) > (ss * (ss - cc))
        k  = k / (2 * k1 * ss)
        k2 = 1 - k * k
    else
        k2 = cc / ss
        if k >= 0
            k  = +sqrt(1 - k2)
        else
            k  = -sqrt(1 - k2)
        end
    end

    tdesired = wc * dt

    if tdesired > 4 * (1 - k * k * k) / 3
        u = 0
        umin = -1
        umax = 1
    else
        u = 1
        umin = 1
        umax = maxu
    end

    ### Iterate Until Transfer Time Matches

    # initialize values outside of loop
    q = 0.
    y = 0.
    h1 = 0.
    h0 = 0.
    uu = 0.
    pp = 0.
    dt = 0.
    slope = 0.
    terror = 0.
    uold = 0.0
    dtold = 0.0

    for i=1:imax

        q = k * u
        y = sqrt(q * q + k2)

        if q <= 0
            h1 = y - q
        else
            h1 = k2 / (y + q)
        end

        h0 = k + u * h1
        q  = (1 - abs(h0)) / 2
        uu = (16 / 15) * h1 ^ 5 * cfgauss(5, q)

        if h0 < 0
            pp = 2 * pi / sqrt(1 - u * u)^5
            uu = pp - uu
        end

        dt     = 4 * h1 * (k + h0 * h1 * h1 / 3) + (1 - u * u) * uu
        slope  = 3 * u * uu - 4 * (h1 / y) * (k * k + (k * h0 + h1 * h1) * h1 * h1)
        terror = tdesired - dt

        if abs(terror) < wc
            if abs(terror) < tolerance * abs(tdesired )
                break
            end

            if abs(terror) < tolerance * abs(slope * u)
                break
            end
        end

        if (i > 1) && (u  ==  uold)
            break
        end

        if (i > 1) && (dt == dtold)
            break
        end

        uold  = u
        dtold = dt
        ustep = terror / slope

        if ustep > 0
            umin = u
            u    = u + ustep
            if u > umax
                u = (umin + umax) / 2
            end
        else
            umax = u
            u    = u + ustep
            if u < umin
                u = (umin + umax) / 2
            end
        end

    end

    ### Final Computations

    h1 = h1 / vc
    uu = uu / vc^5

    h  = k1 / h1
    n0 = +(k * ss - m0 * h0) / h1
    n1 = -(k * ss - m1 * h0) / h1
    v0 = (n0 * r0 + h * cross(uh, r0)) / (m0 * m0)
    v1 = (n1 * r1 + h * cross(uh, r1)) / (m1 * m1)

    vi  = v0
    vf  = v1

    return (vi, vf)
end

"""
Title     : CF Gauss
Purpose   : Compute the Gaussian continued fraction.
Inputs    : i - (na) Order of continued fraction
            q - (na) Argument of continued fraction
Outputs   : g - (na) Continued fraction result
Comments  : The continued fraction is defined as the ratio of two
            hypergeometric functions as follows:

                                   F(i,1,1+(i/2),q)
                g = G(i,0,i/2,q) = ----------------
                                     F(i,0,i/2,q)

Exception : Illegal argument
Reference : Shepperd, S.W., "Universal Keplerian State Transition
            Matrix," Celestial Mechanics, Vol. 35, 1985.
Original author: Brent Barbee
"""
function cfgauss(i, q)

    if q > 0.5 + 16 * eps()
        error("CF Gauss : Illegal argument\n\n")
    end

    g = 1
    r = 1
    s = 1
    n = 0
    l = i - 2
    d = i * (i - 2)
    k = 1 - 2 * i

    while(true)

        k    = -k
        l    = l + 2
        d    = d + 4 * l
        n    = n + (1 + k) * l
        r    = d / (d - n * r * q)
        s    = (r - 1) * s
        gold = g
        g    = gold + s

        if g == gold
            break
        end

    end

    return g
end

"""
Wrapper to get delta V values for a Lambert-targeted transfer
assumes Sun as the central body

Units are km and s
"""
function lambert_transfer(muDepart,
                         TOF,
                         launchEpoch,
                         rPark,
                         x1_HCI::Vector{T}, x2_HCI::Vector{T},
                         muDest = 0.,
                         rCapture = 1000.0,
                         aCapture = 1000.0 ) where {T<:AbstractFloat}
    (v_SC_S_depart_HCI, v_SC_S_arrive_HCI) = lambert_target( muSun,
                                                             TOF,
                                                             x1_HCI[1:3], x2_HCI[1:3],
                                                             x1_HCI[4:6], x2_HCI[4:6] )
    # departure:
    v_INF_E_HCI = v_SC_S_depart_HCI - x1_HCI[4:6]
    C3_depart = norm(v_INF_E_HCI)^2
    dV_depart = abs(getvhyp(muEarth, rPark, norm(v_INF_E_HCI)) - getv(muEarth, rPark, rPark))

    (RLA,DLA) = get_launch_asymptote( v_INF_E_HCI, launchEpoch )

    # arrival:
    v_INF_arrive = norm(v_SC_S_arrive_HCI - x2_HCI[4:6])
    dV_arrive = abs(getArrivalDV( muDest, rCapture, aCapture, v_INF_arrive ))

    return (dV_depart, dV_arrive, C3_depart, RLA, DLA)
end


text:
function MarkChess(r)
    flag::Bool = true
    side = Ost
    moves_from_start_west = 0
    moves_from_start_sud = 0
    moves_from_start_west, flag = movementsandcountmovesandswitchflag(r, West, flag)
    moves_from_start_sud, flag = movementsandcountmovesandswitchflag(r, Sud, flag)

    flag = markrowbyflags(r, side, flag)
    while !isborder(r, Nord)
        move!(r, Nord)
        flag = !flag
        side = inverse(side)
        flag = markrowbyflags(r, side, flag)
    end

    movements(r, West)
    movements(r, Sud)
    movementsbyfixmoves(r, Ost, moves_from_start_west)
    movementsbyfixmoves(r, Nord, moves_from_start_sud)
end

function inverse(side)
    return HorizonSide((Int(side) + 2) % 4)
end

function movements(r, side)
    while !isborder(r, side)
        move!(r, side)
    end
end

function movementsandcountmovesandswitchflag(r, side, flag)
    moves = 0
    while !isborder(r, side)
        move!(r, side)
        moves += 1
        flag = !flag
    end
    return moves, flag
end

function movementsbyfixmoves(r, side, moves)
    while moves != 0
        move!(r, side)
        moves -= 1
    end
end

function markrowbyflags(r, side, flag)
    while !isborder(r, side)
        if flag
            putmarker!(r)
        end
        move!(r, side)
        flag = !flag
    end
    if flag
        putmarker!(r)
    end
    return flag
end

ext: jl


text:
using DiffEqBayesStan, MCMCChains
using OrdinaryDiffEq
using ParameterizedFunctions
using RecursiveArrayTools
using Distributions
using StatsPlots
using DataFrames, MonteCarloMeasurements
using StanSample

ProjDir = @__DIR__

println("\nFour parameter case\n")

f1 = @ode_def begin
    dx = a*x - b*x*y
    dy = -c*y + d*x*y
end a b c d

u0 = [1.0,1.0]
tspan = (0.0,10.0)
p = [1.5,1.0,3.0,1.0]
prob1 = ODEProblem(f1,u0,tspan,p)
sol = solve(prob1,Tsit5())

t = collect(range(1,stop=10,length=50))
randomized = VectorOfArray([(sol(t[i]) + .3randn(2)) for i in 1:length(t)])
data_array = convert(Array, randomized)

priors = [truncated(Normal(1.5,0.1),0.5,2),truncated(Normal(1.0,0.1),0.5,1.5),
          truncated(Normal(3.0,0.1),0.5,4),truncated(Normal(1.0,0.1),0.5,2)]

@time bayesian_result = stan_inference(prob1, t, data_array, priors;
    num_chains=4, num_samples=1000, num_warmups=1000,
    vars =(DiffEqBayesStan.StanODEData(), InverseGamma(4,1)),
    output_format=:dataframe)

if success(bayesian_result.return_code)
    df = bayesian_result.chains

    # Quick summary of results
    parts = read_samples(bayesian_result.model, :particles)
    parts |> display

    # Read in chains as a vector of DataFrames
    dfs = read_samples(bayesian_result.model, :dataframes)

    for j in 1:4
        for i in 1:4
            if i == 1
                plot(dfs[i][:, "theta_$j"], lab="chain[$i]")
            else
                plot!(dfs[i][:, "theta_$j"], lab="chain[$i]")
            end
            title!("theta_$j")
        end
    savefig(joinpath(ProjDir, "theta_$j.png"))
    end

    println()
    stan_summary(bayesian_result.model, true)
    println()

end


text:
#Transforms a string like \"This time in 1996\" to \"This time in YEAR\"
function year2year(s)
    rtest = r"(19|20)\d{2}"
    replace(s, rtest => "YEAR")
end
#If x or y are supersets of each other, keep the superset. Otherwise OR them
function checkerror(x, y)
    x, y = year2year(x), year2year(y)
    if x == y
        return x
    elseif x ⊆ y
        return y
    elseif y ⊆ x
        return x
    else # give up
        return "$x PSIDOR $y"
    end
end

dropY(s) = parse(Int, match(r"(19|20)\d{2}", s).match)

#Check if this label describes a missing value code
function checkmissing(s)
    for r in (r"NA", r"DK", r"Inap.", r"Wild code", r"Missing")
        occursin(r, s) && return true
    end
    return false
end

#Check if this is a continuous variable
function iscontinuous(k)
    for key in k
        out = tryparse(Float64, key)
        if out === nothing
            return true
        end
    end
    return false
end

dropcomma(s) = String([c for c in s if !(c == ',')])

#Try to parse this value as a float
function parse2(s, v)
    out = tryparse(Float64, dropcomma(s))
    # if this isn't a Float, maybe it was a range "-89.0 - -0.4"
    # TODO fix this
    if out === nothing
        #@show s v
        return missing
    else
        return out
    end
end
narrowtypes(A) = [a for a in A]

"""
Inputs:
name: The variable ID we want to match
var2ind_dict: The crosswalk table
df_vars: The data
codebook_df: The codebook table
fastfind: Dict mapping from variable IDs to their index in the codebook
Processes a variable ID, finds all years thats match, and collects the labels
"""
function process_varname(name, var2ind_dict, df_vars, codebook_df, fastfind)
    ## Find the row in the crosswalk we can find this variable in
    myrow = var2ind_dict[name]
    ## Fetch all the names in that row
    dfvar = df_vars[myrow, :]
    mynames = [ r for r in dfvar if r !== missing]
    ## Need to figure out the variable label expansion
    # Can I just take the union?
    codevec = [fastfind[s] for s in values(mynames)]
    codedict = [codebook_df.codedict[i] for i in codevec]
    un = Dict{String, String}()
    merge!(checkerror, un, codedict...)
    map!(trimlabel, values(un))
    varnames = Dict{String, Tuple{String, String, Vector{Float64}}}(
    codebook_df.YEAR[i] => (codebook_df.NAME[i], codebook_df.LABEL[i],
     codebook_df.excluding[i]) for i in codevec)
    varnames, iscontinuous(keys(un)), un
end

"""
Sometimes the labels uses a comma in one year and a semicolon in another,
but are otherwise identical.
This function parses the different labels and drops these duplicates.
It also keeps only labels which are unique after cleaning, and constructs
a label which is a union of the parts (A OR B OR C)
"""
function trimlabel(s)
    sp = strip.(split(s, "PSIDOR"))
    # find common substrings
    # For each string in s, check if it occurs in another string in s
    # If so, drop it from s
    # If not, push it to the new string list

    # For each index in s
    # Check if s[i] is in s \ excluded
    # If so, add this index to the excluded list
    clean(x) = lowercase(dropcomma(x))
    setsp = Set(sp)
    #cleaned = Set(clean.(sp))
    excluded = Int[]
    # We want to find the unique (after cleaning) labels
    # Iterate through the set and check if we have seen this label before
    # If not, add it to the seen list
    for i in eachindex(sp)
        targind = setdiff(1:length(sp), union(i, excluded))
        cleaned = clean.(sp[targind])
        any(clean(sp[i]) ⊆ c for c in cleaned) && push!(excluded, i)
    end
    newsp = sp[setdiff(1:length(sp), excluded)]
    if length(newsp) == 1
        return newsp[1]
    else
        return reduce((x, y) -> "$x OR $y", newsp[2:end], init = newsp[1])
    end
end
"""
Processes input JSON file
Reads the crosswalk and codebook table from disk and
harmonizes the labels. Constructs the output JSON
"""
function process_input(inputjson)
    @assert last(splitext(inputjson)) == ".json"
    codebook_json = jsontable(read("output/codebook.json", String));
    codebook_df = DataFrame(codebook_json);
    codebook_df.codedict = [Dict(string(x) => y for (x, y) in dt) for dt in codebook_df.codedict]
    crosswalk_df = DataFrame(XLSX.readtable("psid.xlsx", "MATRIX")...)
    crosswalk_df = mapcols(narrowtypes, crosswalk_df)
    ## Need a map from VAR to the right row
    df_vars = crosswalk_df[!, r"^Y.+"]
    var2ind_dict = Dict{String, Int}()
    ##
    for col in eachcol(df_vars)
        x = Dict(col[i] => i for i in 1:length(col) if col[i] !== missing)
        merge!(checkerror, var2ind_dict, x)
    end
    ## Need to figure out the variable label expansion
    fastfind = Dict(codebook_df.NAME[i] => i for i in 1:length(codebook_df.NAME))
    # Check if this label denotes a missing value code. If so, this value is an excluding value
    codebook_df.excluding = [[parse2(k, v) for (k, v) in d if checkmissing(v)] |> skipmissing |> narrowtypes for d in codebook_df.codedict]

    ### Do the final processing of the input JSON, produce the output
    read_input = JSON3.read(read(inputjson, String), Vector{VarInput})
    process_varinput(v::VarInput) = VarInfo5(v.name_user, v.unit, process_varname(v.varID, var2ind_dict, df_vars, codebook_df, fastfind)...)
    procvar = process_varinput.(read_input)
    write("output/user_output.json", JSON3.write(procvar))

    modpath = dirname(pathof(PSID))
    indpath = "$modpath/ind_input.json"

    read_input = JSON3.read(read(indpath, String), Vector{VarInput})
    procvar = process_varinput.(read_input)
    write("output/ind_output.json", JSON3.write(procvar))
end


text:
using BinaryBuilder, Pkg

name = "MPICH"
version = v"3.4.3"

sources = [
    ArchiveSource("https://www.mpich.org/static/downloads/$(version)/mpich-$(version).tar.gz",
                  "8154d89f3051903181018166678018155f4c2b6f04a9bb6fe9515656452c4fd7"),
    ArchiveSource("https://github.com/eschnett/MPIconstants/archive/refs/tags/v1.4.0.tar.gz",
                  "610d816c22cd05e16e17371c6384e0b6f9d3a2bdcb311824d0d40790812882fc"),
]

script = raw"""
################################################################################
# Install MPICH
################################################################################

# Enter the funzone
cd ${WORKSPACE}/srcdir/mpich*

EXTRA_FLAGS=()
if [[ "${target}" != i686-linux-gnu ]] || [[ "${target}" != x86_64-linux-* ]]; then
    # Define some obscure undocumented variables needed for cross compilation of
    # the Fortran bindings.  See for example
    # * https://stackoverflow.com/q/56759636/2442087
    # * https://github.com/pmodels/mpich/blob/d10400d7a8238dc3c8464184238202ecacfb53c7/doc/installguide/cfile
    export CROSS_F77_SIZEOF_INTEGER=4
    export CROSS_F77_TRUE_VALUE=1
    export CROSS_F77_FALSE_VALUE=0

    export CROSS_F90_ADDRESS_KIND=8
    export CROSS_F90_OFFSET_KIND=8
    export CROSS_F90_INTEGER_KIND=4
    export CROSS_F90_DOUBLE_MODEL=15,307
    export CROSS_F90_REAL_MODEL=6,37

    if [[ "${target}" == i686-linux-musl ]]; then
        # Our `i686-linux-musl` platform is a bit rotten: it can run C programs,
        # but not C++ or Fortran.  `configure` runs a C program to determine
        # whether it's cross-compiling or not, but when it comes to running
        # Fortran programs, it fails.  In addition, `configure` ignores the
        # above exported variables if it believes it's doing a native build.
        # Small hack: edit `configure` script to force `cross_compiling` to be
        # always "yes".
        sed -i 's/cross_compiling=no/cross_compiling=yes/g' configure
        EXTRA_FLAGS+=(ac_cv_sizeof_bool="1")
    fi
fi

if [[ "${target}" == aarch64-apple-* ]]; then
    export FFLAGS=-fallow-argument-mismatch
fi

./configure --prefix=${prefix} --build=${MACHTYPE} --host=${target} \
    --enable-shared=yes --enable-static=no \
    --with-device=ch3 --disable-dependency-tracking \
    --enable-fast=all,O3 \
    --docdir=/tmp \
    "${EXTRA_FLAGS[@]}"

# Remove empty `-l` flags from libtool
# (Why are they there? They should not be.)
# Run the command several times to handle multiple (overlapping) occurrences.
sed -i 's/"-l /"/g;s/ -l / /g;s/-l"/"/g' libtool
sed -i 's/"-l /"/g;s/ -l / /g;s/-l"/"/g' libtool
sed -i 's/"-l /"/g;s/ -l / /g;s/-l"/"/g' libtool

# Build the library
make -j${nproc}

# Install the library
make install

################################################################################
# Install MPIconstants
################################################################################

cd ${WORKSPACE}/srcdir/MPIconstants*
mkdir build
cd build
# Yes, this is tedious. No, without being this explicit, cmake will
# not properly auto-detect the MPI libraries.
if [ -f ${prefix}/lib/libpmpi.${dlext} ]; then
    cmake \
        -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} \
        -DCMAKE_FIND_ROOT_PATH=${prefix} \
        -DCMAKE_INSTALL_PREFIX=${prefix} \
        -DBUILD_SHARED_LIBS=ON \
        -DMPI_C_COMPILER=cc \
        -DMPI_C_LIB_NAMES='mpi;pmpi' \
        -DMPI_mpi_LIBRARY=${prefix}/lib/libmpi.${dlext} \
        -DMPI_pmpi_LIBRARY=${prefix}/lib/libpmpi.${dlext} \
        ..
else
    cmake \
        -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} \
        -DCMAKE_FIND_ROOT_PATH=${prefix} \
        -DCMAKE_INSTALL_PREFIX=${prefix} \
        -DBUILD_SHARED_LIBS=ON \
        -DMPI_C_COMPILER=cc \
        -DMPI_C_LIB_NAMES='mpi' \
        -DMPI_mpi_LIBRARY=${prefix}/lib/libmpi.${dlext} \
        ..
fi

cmake --build . --config RelWithDebInfo --parallel $nproc
cmake --build . --config RelWithDebInfo --parallel $nproc --target install

################################################################################
# Install licenses
################################################################################

install_license $WORKSPACE/srcdir/mpich*/COPYRIGHT $WORKSPACE/srcdir/MPIconstants-*/LICENSE.md
"""

platforms = expand_gfortran_versions(filter!(!Sys.iswindows, supported_platforms(; experimental=true)))

products = [
    # MPICH
    LibraryProduct("libmpicxx", :libmpicxx),
    LibraryProduct("libmpifort", :libmpifort),
    LibraryProduct("libmpi", :libmpi),
    ExecutableProduct("mpiexec", :mpiexec),
    # MPIconstants
    LibraryProduct("libload_time_mpi_constants", :libload_time_mpi_constants),
    ExecutableProduct("generate_compile_time_mpi_constants", :generate_compile_time_mpi_constants),
]

dependencies = [
    Dependency(PackageSpec(name="CompilerSupportLibraries_jll", uuid="e66e0078-7015-5450-92f7-15fbd957f2ae")),
]

# Build the tarballs.
build_tarballs(ARGS, name, version, sources, script, platforms, products, dependencies; julia_compat="1.6")

max_stars_repo_name: AbhimanyuAryan/Yggdrasil

max_issues_repo_name: AbhimanyuAryan/Yggdrasil

max_forks_repo_name: AbhimanyuAryan/Yggdrasil


text:
fi = "C:\\Users\\bernhard.konig\\Documents\\ASync\\home\\Code\\Julia\\DecisionTrees.jl\\src\\boosting.jl"
# fio = open(fi,"r")
#   a=readline(fio)
# close(fio)
txt1 = readlines(fi)


text:
using Distributed
addprocs(2)
println(string("Number of procs:", nprocs()))

@everywhere begin
    using PowerModels
    using Suppressor
    using Ipopt
    using Memento
    # using SharedArrays
    using JuMP
end
using ArgParse
using DelimitedFiles

function get_header_names(variables::AbstractArray)
    header = [] # used as header in x_opt results csv
    push!(header, JuMP.name.(variables))
    header = map((x) -> uppercase(replace(x, r"0_|\[|\]" => "")),header[1]) # prettify
    return [header]::AbstractArray
end

function get_slack_idx(power_model::AbstractPowerModel)
    # Slack type == 3
    bus_idx =  [k for (k,v) in power_model.data["bus"] if v["bus_type"] == 3]
    bus_idx = parse(Int64,bus_idx[1])
    gen_idx = [k for (k,v) in power_model.data["gen"] if v["gen_bus"] == bus_idx]
    return parse(Int64, gen_idx[1])
end

function mod_acopf!(pm::AbstractPowerModel)
"""Modify the objective function and add NL constraint to AC-OPF problem."""
    vars = []
    slack_gen_idx = get_slack_idx(pm)
    for i=1:length(pm.var[:nw][0][:pg].data)
        if i != slack_gen_idx
            push!(vars, JuMP.variable_by_name(pm.model, string("0_pg[",i,"]")))
        end
    end
    gen_indexes = map(x -> x["gen_bus"], values(pm.data["gen"]))
    for g in gen_indexes
        push!(vars, JuMP.variable_by_name(pm.model, string("0_vm[",g,"]")))
    end
    pm.model[:vars] = vars;
    N = length(vars);
    x_hat = zeros(1,N);

    @variable(pm.model, r);
    @objective(pm.model, Min, r);
    @NLparameter(pm.model, x_hat_p[i = 1:N] == x_hat[i]) # current row of samples
    pm.model[:x_hat_p] = x_hat_p;

    @NLconstraint(pm.model, con_sphere,
        sqrt(sum((vars[i]-x_hat_p[i])^2 for i in 1:N))<= r);

    return nothing
end

@everywhere function run_mod_acopf(pm::AbstractPowerModel,sp::AbstractArray)
    """Run modified ACOPF with setpoints defined by sp"""
    power_model = pm; # do not modify original model
    JuMP.set_value.(power_model.model[:x_hat_p],sp)
    @suppress_out result = optimize_model!(power_model, optimizer=optimizer_with_attributes(Ipopt.Optimizer, "print_level" => 0))

    return JuMP.value.(power_model.model[:vars])
end

function parse_cli()
    s = ArgParseSettings()
    @add_arg_table! s begin
        "--log", "-l"
            help = "Define log level. Possible values are: [debug | info |
            notice | warn | error]."
            arg_type = String
            default = "info"
        "out_file_name"
            help = "Output file containing the Pg and Vg values of the sovled
            AC-OPFs."
            required = true
        "case_file_name"
            help = "Case file name (relative or full path with extension).
            Supported file formats:\n\t- matpower v2.0"
            required = true
        "samples"
            help = ".csv file containing the samples from the convex polytope
            of several QC relaxation runs."
            required = true
    end

    return parse_args(s)
end

function main()
"""Solve AC-OPF for each point in the data set defined by SAMPLES and save the
resulting optimal scenarios' Pg, Vg values."""
    logger = Memento.config!("info"; fmt="[{level} | {name}]: {msg}")

    parsed_inputs = parse_cli()
    start = time();
    samples_file_name = parsed_inputs["samples"]#string(pwd(), "\\case14_results4.csv")
    case_file_name = parsed_inputs["case_file_name"]#string(pwd(), "\\case14.m")
    out_file_name = parsed_inputs["out_file_name"]#string(pwd(), "\\n24_case14_acopf.csv")

    debug(logger,"Inputs parsed.")
    logger = Memento.config!(parsed_inputs["log"]; fmt="[{level} | {name}]: {msg}")
    Memento.setlevel!(Memento.getlogger(PowerModels.InfrastructureModels), "error")
    if parsed_inputs["log"] == "debug"
        # enable debug:
        ENV["JULIA_DEBUG"] = "all"
    end

    # Load .csv (already scaled) setpoint add_arg_table
    samples = readdlm(samples_file_name,',', Float64, header=true)
    sSamples = size(samples[1]);
    info(logger,string("Executing for ",sSamples[1], " setpoints."))
    # Load matpower case14
    network_data = PowerModels.parse_file(case_file_name)#parsed_inputs["case_file_name"])

    # Initialize abstract power system model
    power_model = instantiate_model(network_data, ACPPowerModel,
        PowerModels.build_opf)

    # to display the obj func: JuMP.objective_function(power_model.model)
    # to check on named constraint: power_model.model[:con_sphere]
    mod_acopf!(power_model)

    M = [samples[1][i,:] for i=1:sSamples[1]]
    info(logger,string("Start parallel run."))
    res = pmap(x -> run_mod_acopf(power_model,x), M)

    elapsed = time() - start

    info(logger,string("Finished. Elapsed time: $elapsed sec ."))
    # map to Array
    out = Array{Float64}(undef,sSamples);
    for i=1:sSamples[1]
        out[i,:] = res[i]
    end
    info(logger,string("Writing output file $out_file_name."))
    header = get_header_names(power_model.model[:vars])
    writedlm(out_file_name, header, ',')
    open(out_file_name,"a") do io
        writedlm(io, out, ',')
    end
end

main()

max_stars_repo_name: timonviola/PSDC

max_issues_repo_name: timonviola/PSDC

max_forks_repo_name: timonviola/PSDC


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

"""
    create_app_instance(client_request_token, name)
    create_app_instance(client_request_token, name, params::Dict{String,<:Any})

Creates an Amazon Chime SDK messaging AppInstance under an AWS account. Only SDK messaging
customers use this API. CreateAppInstance supports idempotency behavior as described in the
AWS API Standard. identity

# Arguments
- `client_request_token`: The ClientRequestToken of the AppInstance.
- `name`: The name of the AppInstance.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Metadata"`: The metadata of the AppInstance. Limited to a 1KB string in UTF-8.
- `"Tags"`: Tags assigned to the AppInstanceUser.
"""
function create_app_instance(
    ClientRequestToken, Name; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "POST",
        "/app-instances",
        Dict{String,Any}("ClientRequestToken" => ClientRequestToken, "Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_app_instance(
    ClientRequestToken,
    Name,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/app-instances",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ClientRequestToken" => ClientRequestToken, "Name" => Name
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_app_instance_admin(app_instance_admin_arn, app_instance_arn)
    create_app_instance_admin(app_instance_admin_arn, app_instance_arn, params::Dict{String,<:Any})

Promotes an AppInstanceUser to an AppInstanceAdmin. The promoted user can perform the
following actions.     ChannelModerator actions across all channels in the AppInstance.
DeleteChannelMessage actions.   Only an AppInstanceUser can be promoted to an
AppInstanceAdmin role.

# Arguments
- `app_instance_admin_arn`: The ARN of the administrator of the current AppInstance.
- `app_instance_arn`: The ARN of the AppInstance.

"""
function create_app_instance_admin(
    AppInstanceAdminArn, appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "POST",
        "/app-instances/$(appInstanceArn)/admins",
        Dict{String,Any}("AppInstanceAdminArn" => AppInstanceAdminArn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_app_instance_admin(
    AppInstanceAdminArn,
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/app-instances/$(appInstanceArn)/admins",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("AppInstanceAdminArn" => AppInstanceAdminArn),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    create_app_instance_user(app_instance_arn, app_instance_user_id, client_request_token, name)
    create_app_instance_user(app_instance_arn, app_instance_user_id, client_request_token, name, params::Dict{String,<:Any})

Creates a user under an Amazon Chime AppInstance. The request consists of a unique
appInstanceUserId and Name for that user.

# Arguments
- `app_instance_arn`: The ARN of the AppInstance request.
- `app_instance_user_id`: The user ID of the AppInstance.
- `client_request_token`: The token assigned to the user requesting an AppInstance.
- `name`: The user's name.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Metadata"`: The request's metadata. Limited to a 1KB string in UTF-8.
- `"Tags"`: Tags assigned to the AppInstanceUser.
"""
function create_app_instance_user(
    AppInstanceArn,
    AppInstanceUserId,
    ClientRequestToken,
    Name;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/app-instance-users",
        Dict{String,Any}(
            "AppInstanceArn" => AppInstanceArn,
            "AppInstanceUserId" => AppInstanceUserId,
            "ClientRequestToken" => ClientRequestToken,
            "Name" => Name,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_app_instance_user(
    AppInstanceArn,
    AppInstanceUserId,
    ClientRequestToken,
    Name,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/app-instance-users",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AppInstanceArn" => AppInstanceArn,
                    "AppInstanceUserId" => AppInstanceUserId,
                    "ClientRequestToken" => ClientRequestToken,
                    "Name" => Name,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes an AppInstance and all associated data asynchronously.

# Arguments
- `app_instance_arn`: The ARN of the AppInstance.

"""
function delete_app_instance(
    appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instances/$(appInstanceArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_app_instance(
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instances/$(appInstanceArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    delete_app_instance_admin(app_instance_admin_arn, app_instance_arn)
    delete_app_instance_admin(app_instance_admin_arn, app_instance_arn, params::Dict{String,<:Any})

Demotes an AppInstanceAdmin to an AppInstanceUser. This action does not delete the user.

# Arguments
- `app_instance_admin_arn`: The ARN of the AppInstance's administrator.
- `app_instance_arn`: The ARN of the AppInstance.

"""
function delete_app_instance_admin(
    appInstanceAdminArn, appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instances/$(appInstanceArn)/admins/$(appInstanceAdminArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_app_instance_admin(
    appInstanceAdminArn,
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instances/$(appInstanceArn)/admins/$(appInstanceAdminArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes an AppInstanceUser.

# Arguments
- `app_instance_user_arn`: The ARN of the user request being deleted.

"""
function delete_app_instance_user(
    appInstanceUserArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instance-users/$(appInstanceUserArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function delete_app_instance_user(
    appInstanceUserArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instance-users/$(appInstanceUserArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    deregister_app_instance_user_endpoint(app_instance_user_arn, endpoint_id)
    deregister_app_instance_user_endpoint(app_instance_user_arn, endpoint_id, params::Dict{String,<:Any})

Deregisters an AppInstanceUserEndpoint.

# Arguments
- `app_instance_user_arn`: The ARN of the AppInstanceUser.
- `endpoint_id`: The unique identifier of the AppInstanceUserEndpoint.

"""
function deregister_app_instance_user_endpoint(
    appInstanceUserArn, endpointId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instance-users/$(appInstanceUserArn)/endpoints/$(endpointId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function deregister_app_instance_user_endpoint(
    appInstanceUserArn,
    endpointId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "DELETE",
        "/app-instance-users/$(appInstanceUserArn)/endpoints/$(endpointId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the full details of an AppInstance.

# Arguments
- `app_instance_arn`: The ARN of the AppInstance.

"""
function describe_app_instance(
    appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_app_instance(
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_app_instance_admin(app_instance_admin_arn, app_instance_arn)
    describe_app_instance_admin(app_instance_admin_arn, app_instance_arn, params::Dict{String,<:Any})

Returns the full details of an AppInstanceAdmin.

# Arguments
- `app_instance_admin_arn`: The ARN of the AppInstanceAdmin.
- `app_instance_arn`: The ARN of the AppInstance.

"""
function describe_app_instance_admin(
    appInstanceAdminArn, appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)/admins/$(appInstanceAdminArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_app_instance_admin(
    appInstanceAdminArn,
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)/admins/$(appInstanceAdminArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns the full details of an AppInstanceUser.

# Arguments
- `app_instance_user_arn`: The ARN of the AppInstanceUser.

"""
function describe_app_instance_user(
    appInstanceUserArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users/$(appInstanceUserArn)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_app_instance_user(
    appInstanceUserArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users/$(appInstanceUserArn)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    describe_app_instance_user_endpoint(app_instance_user_arn, endpoint_id)
    describe_app_instance_user_endpoint(app_instance_user_arn, endpoint_id, params::Dict{String,<:Any})

Returns the full details of an AppInstanceUserEndpoint.

# Arguments
- `app_instance_user_arn`: The ARN of the AppInstanceUser.
- `endpoint_id`: The unique identifier of the AppInstanceUserEndpoint.

"""
function describe_app_instance_user_endpoint(
    appInstanceUserArn, endpointId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users/$(appInstanceUserArn)/endpoints/$(endpointId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function describe_app_instance_user_endpoint(
    appInstanceUserArn,
    endpointId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users/$(appInstanceUserArn)/endpoints/$(endpointId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Gets the retention settings for an AppInstance.

# Arguments
- `app_instance_arn`: The ARN of the AppInstance.

"""
function get_app_instance_retention_settings(
    appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)/retention-settings";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_app_instance_retention_settings(
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)/retention-settings",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns a list of the administrators in the AppInstance.

# Arguments
- `app_instance_arn`: The ARN of the AppInstance.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max-results"`: The maximum number of administrators that you want to return.
- `"next-token"`: The token returned from previous API requests until the number of
  administrators is reached.
"""
function list_app_instance_admins(
    appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)/admins";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_app_instance_admins(
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instances/$(appInstanceArn)/admins",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists all the AppInstanceUserEndpoints created under a single AppInstanceUser.

# Arguments
- `app_instance_user_arn`: The ARN of the AppInstanceUser.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max-results"`: The maximum number of endpoints that you want to return.
- `"next-token"`: The token passed by previous API calls until all requested endpoints are
  returned.
"""
function list_app_instance_user_endpoints(
    appInstanceUserArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users/$(appInstanceUserArn)/endpoints";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_app_instance_user_endpoints(
    appInstanceUserArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users/$(appInstanceUserArn)/endpoints",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    list_app_instance_users(app-instance-arn)
    list_app_instance_users(app-instance-arn, params::Dict{String,<:Any})

List all AppInstanceUsers created under a single AppInstance.

# Arguments
- `app-instance-arn`: The ARN of the AppInstance.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max-results"`: The maximum number of requests that you want returned.
- `"next-token"`: The token passed by previous API calls until all requested users are
  returned.
"""
function list_app_instance_users(
    app_instance_arn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users",
        Dict{String,Any}("app-instance-arn" => app_instance_arn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_app_instance_users(
    app_instance_arn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "GET",
        "/app-instance-users",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("app-instance-arn" => app_instance_arn), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists all Amazon Chime AppInstances created under a single AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"max-results"`: The maximum number of AppInstances that you want to return.
- `"next-token"`: The token passed by previous API requests until you reach the maximum
  number of AppInstances.
"""
function list_app_instances(; aws_config::AbstractAWSConfig=global_aws_config())
    return chime_sdk_identity(
        "GET", "/app-instances"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_app_instances(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/app-instances",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Lists the tags applied to an Amazon Chime SDK identity resource.

# Arguments
- `arn`: The ARN of the resource.

"""
function list_tags_for_resource(arn; aws_config::AbstractAWSConfig=global_aws_config())
    return chime_sdk_identity(
        "GET",
        "/tags",
        Dict{String,Any}("arn" => arn);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_tags_for_resource(
    arn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "GET",
        "/tags",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("arn" => arn), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    put_app_instance_retention_settings(app_instance_retention_settings, app_instance_arn)
    put_app_instance_retention_settings(app_instance_retention_settings, app_instance_arn, params::Dict{String,<:Any})

Sets the amount of time in days that a given AppInstance retains data.

# Arguments
- `app_instance_retention_settings`: The time in days to retain data. Data type: number.
- `app_instance_arn`: The ARN of the AppInstance.

"""
function put_app_instance_retention_settings(
    AppInstanceRetentionSettings,
    appInstanceArn;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "PUT",
        "/app-instances/$(appInstanceArn)/retention-settings",
        Dict{String,Any}("AppInstanceRetentionSettings" => AppInstanceRetentionSettings);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function put_app_instance_retention_settings(
    AppInstanceRetentionSettings,
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "PUT",
        "/app-instances/$(appInstanceArn)/retention-settings",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "AppInstanceRetentionSettings" => AppInstanceRetentionSettings
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    register_app_instance_user_endpoint(client_request_token, endpoint_attributes, resource_arn, type, app_instance_user_arn)
    register_app_instance_user_endpoint(client_request_token, endpoint_attributes, resource_arn, type, app_instance_user_arn, params::Dict{String,<:Any})

Registers an endpoint under an Amazon Chime AppInstanceUser. The endpoint receives messages
for a user. For push notifications, the endpoint is a mobile device used to receive mobile
push notifications for a user.

# Arguments
- `client_request_token`: The idempotency token for each client request.
- `endpoint_attributes`: The attributes of an Endpoint.
- `resource_arn`: The ARN of the resource to which the endpoint belongs.
- `type`: The type of the AppInstanceUserEndpoint. Supported types:    APNS: The mobile
  notification service for an Apple device.    APNS_SANDBOX: The sandbox environment of the
  mobile notification service for an Apple device.    GCM: The mobile notification service
  for an Android device.   Populate the ResourceArn value of each type as PinpointAppArn.
- `app_instance_user_arn`: The ARN of the AppInstanceUser.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AllowMessages"`: Boolean that controls whether the AppInstanceUserEndpoint is opted in
  to receive messages. ALL indicates the endpoint receives all messages. NONE indicates the
  endpoint receives no messages.
- `"Name"`: The name of the AppInstanceUserEndpoint.
"""
function register_app_instance_user_endpoint(
    ClientRequestToken,
    EndpointAttributes,
    ResourceArn,
    Type,
    appInstanceUserArn;
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/app-instance-users/$(appInstanceUserArn)/endpoints",
        Dict{String,Any}(
            "ClientRequestToken" => ClientRequestToken,
            "EndpointAttributes" => EndpointAttributes,
            "ResourceArn" => ResourceArn,
            "Type" => Type,
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function register_app_instance_user_endpoint(
    ClientRequestToken,
    EndpointAttributes,
    ResourceArn,
    Type,
    appInstanceUserArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/app-instance-users/$(appInstanceUserArn)/endpoints",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}(
                    "ClientRequestToken" => ClientRequestToken,
                    "EndpointAttributes" => EndpointAttributes,
                    "ResourceArn" => ResourceArn,
                    "Type" => Type,
                ),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    tag_resource(resource_arn, tags)
    tag_resource(resource_arn, tags, params::Dict{String,<:Any})

Applies the specified tags to the specified Amazon Chime SDK identity resource.

# Arguments
- `resource_arn`: The resource ARN.
- `tags`: The tag key-value pairs.

"""
function tag_resource(ResourceARN, Tags; aws_config::AbstractAWSConfig=global_aws_config())
    return chime_sdk_identity(
        "POST",
        "/tags?operation=tag-resource",
        Dict{String,Any}("ResourceARN" => ResourceARN, "Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag_resource(
    ResourceARN,
    Tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/tags?operation=tag-resource",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("ResourceARN" => ResourceARN, "Tags" => Tags),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    untag_resource(resource_arn, tag_keys)
    untag_resource(resource_arn, tag_keys, params::Dict{String,<:Any})

Removes the specified tags from the specified Amazon Chime SDK identity resource.

# Arguments
- `resource_arn`: The resource ARN.
- `tag_keys`: The tag keys.

"""
function untag_resource(
    ResourceARN, TagKeys; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "POST",
        "/tags?operation=untag-resource",
        Dict{String,Any}("ResourceARN" => ResourceARN, "TagKeys" => TagKeys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag_resource(
    ResourceARN,
    TagKeys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "POST",
        "/tags?operation=untag-resource",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("ResourceARN" => ResourceARN, "TagKeys" => TagKeys),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_app_instance(metadata, name, app_instance_arn)
    update_app_instance(metadata, name, app_instance_arn, params::Dict{String,<:Any})

Updates AppInstance metadata.

# Arguments
- `metadata`: The metadata that you want to change.
- `name`: The name that you want to change.
- `app_instance_arn`: The ARN of the AppInstance.

"""
function update_app_instance(
    Metadata, Name, appInstanceArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "PUT",
        "/app-instances/$(appInstanceArn)",
        Dict{String,Any}("Metadata" => Metadata, "Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_app_instance(
    Metadata,
    Name,
    appInstanceArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "PUT",
        "/app-instances/$(appInstanceArn)",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("Metadata" => Metadata, "Name" => Name), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_app_instance_user(metadata, name, app_instance_user_arn)
    update_app_instance_user(metadata, name, app_instance_user_arn, params::Dict{String,<:Any})

Updates the details of an AppInstanceUser. You can update names and metadata.

# Arguments
- `metadata`: The metadata of the AppInstanceUser.
- `name`: The name of the AppInstanceUser.
- `app_instance_user_arn`: The ARN of the AppInstanceUser.

"""
function update_app_instance_user(
    Metadata, Name, appInstanceUserArn; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "PUT",
        "/app-instance-users/$(appInstanceUserArn)",
        Dict{String,Any}("Metadata" => Metadata, "Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_app_instance_user(
    Metadata,
    Name,
    appInstanceUserArn,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "PUT",
        "/app-instance-users/$(appInstanceUserArn)",
        Dict{String,Any}(
            mergewith(
                _merge, Dict{String,Any}("Metadata" => Metadata, "Name" => Name), params
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    update_app_instance_user_endpoint(app_instance_user_arn, endpoint_id)
    update_app_instance_user_endpoint(app_instance_user_arn, endpoint_id, params::Dict{String,<:Any})

Updates the details of an AppInstanceUserEndpoint. You can update the name and AllowMessage
values.

# Arguments
- `app_instance_user_arn`: The ARN of the AppInstanceUser.
- `endpoint_id`: The unique identifier of the AppInstanceUserEndpoint.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"AllowMessages"`: Boolean that controls whether the AppInstanceUserEndpoint is opted in
  to receive messages. ALL indicates the endpoint will receive all messages. NONE indicates
  the endpoint will receive no messages.
- `"Name"`: The name of the AppInstanceUserEndpoint.
"""
function update_app_instance_user_endpoint(
    appInstanceUserArn, endpointId; aws_config::AbstractAWSConfig=global_aws_config()
)
    return chime_sdk_identity(
        "PUT",
        "/app-instance-users/$(appInstanceUserArn)/endpoints/$(endpointId)";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_app_instance_user_endpoint(
    appInstanceUserArn,
    endpointId,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return chime_sdk_identity(
        "PUT",
        "/app-instance-users/$(appInstanceUserArn)/endpoints/$(endpointId)",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
module TestLCRQ

using Base.Experimental: @sync
using ConcurrentCollections
using ProgressLogging: @logprogress, @withprogress
using Test
using ..Utils: ⊏

function test_push_pop_once_int()
    q = LinkedConcurrentRingQueue{Int}()
    push!(q, 111)
    @test maybepopfirst!(q) == Some(111)
end

function test_push_pop_once_any()
    q = LinkedConcurrentRingQueue()
    push!(q, 111)
    @test maybepopfirst!(q) == Some{Any}(111)
end

function test_push_pop_100()
    n = 100
    q = LinkedConcurrentRingQueue{Int}()
    foldl(push!, 1:n; init = q)
    ys = Int[]
    while (y = maybepopfirst!(q)) !== nothing
        push!(ys, something(y))
    end
    @test ys == 1:n
end

function concurrent_push_pop!(q, nitems::Integer, nsend::Integer, nrecv::Integer)
    received = Vector{Int}[]
    activesenders = Threads.Atomic{Int}(nsend)
    @sync begin
        for t in 1:nsend
            Threads.@spawn begin
                for i in t:nsend:nitems
                    push!(q, i)
                end
                if Threads.atomic_sub!(activesenders, 1) == 1
                    for _ in 1:nrecv
                        push!(q, -1)
                    end
                end
            end
        end
        for _ in 1:nrecv
            ys = Int[]
            push!(received, ys)
            Threads.@spawn begin
                while true
                    y = maybepopfirst!(q)
                    if y === nothing
                        yield()
                    else
                        i = something(y)
                        i == -1 && break
                        push!(ys, i)
                    end
                end
            end
        end
    end
    return received
end

function check_consecutive(xs)
    notfound = Int[]
    dups = Int[]
    pre = xs[begin] - 1
    for x in xs
        e = pre + 1
        append!(notfound, e:x-1)
        append!(dups, x:e-1)
        pre = x
    end
    return (; notfound, dups)
end

function test_concurrent_push_pop(ntrials = 100)
    Threads.nthreads() > 1 || return
    @withprogress name = "concurrent push-pop" begin
        @testset for trial in 1:ntrials
            @logprogress (trial - 1) / ntrials
            check_concurrent_push_pop()
        end
    end
end

function check_concurrent_push_pop()
    nsend = cld(Threads.nthreads(), 2)
    nrecv = Threads.nthreads() - nsend
    @assert nsend ≥ 1
    @assert nrecv ≥ 1
    q = LinkedConcurrentRingQueue{Int}(32)
    nitems = 2^20
    received = concurrent_push_pop!(q, nitems, nsend, nrecv)
    allreceived = reduce(vcat, received)
    @test length(allreceived) == nitems
    sort!(allreceived)
    (; notfound, dups) = check_consecutive(allreceived)
    @test notfound == []
    @test dups == []
    @test allreceived == 1:nitems
end

function test_print()
    q = LinkedConcurrentRingQueue{Int}()
    push!(q, 333)
    str = sprint(show, "text/plain", q)
    @test "LCRQ: " ⊏ str
    @test "1 item" ⊏ str
end

end  # module


text:
using PatternFolds
using Documenter

DocMeta.setdocmeta!(PatternFolds, :DocTestSetup, :(using PatternFolds); recursive=true)

makedocs(;
    modules=[PatternFolds],
    authors="Jean-Francois Baffier",
    repo="https://github.com/Humans-of-Julia/PatternFolds.jl/blob/{commit}{path}#{line}",
    sitename="PatternFolds.jl",
    format=Documenter.HTML(;
        prettyurls=get(ENV, "CI", "false") == "true",
        canonical="https://Humans-of-Julia.github.io/PatternFolds.jl",
        assets=String[],
    ),
    pages=[
        "Home" => "index.md",
    ],
)

deploydocs(;
    repo="github.com/Humans-of-Julia/PatternFolds.jl",
    devbranch="main",
)


text:
using Oceananigans.BoundaryConditions: default_auxiliary_bc
using Oceananigans.Fields: FunctionField, data_summary
using Oceananigans.AbstractOperations: AbstractOperation
using Oceananigans.Operators: assumed_field_location
using Oceananigans.OutputWriters: output_indices

import Oceananigans.Fields: set!, compute!, compute_at!, validate_field_data, validate_boundary_conditions
import Oceananigans.Fields: validate_indices, FieldBoundaryBuffers
import Oceananigans.BoundaryConditions: FieldBoundaryConditions, regularize_field_boundary_conditions
import Base: fill!
import Oceananigans.Simulations: hasnan

# Field and FunctionField (both fields with "grids attached")
const MultiRegionField{LX, LY, LZ, O} = Field{LX, LY, LZ, O, <:MultiRegionGrid} where {LX, LY, LZ, O}
const MultiRegionComputedField{LX, LY, LZ, O} = Field{LX, LY, LZ, <:AbstractOperation, <:MultiRegionGrid} where {LX, LY, LZ}
const MultiRegionFunctionField{LX, LY, LZ, C, P, F} = FunctionField{LX, LY, LZ, C, P, F, <:MultiRegionGrid} where {LX, LY, LZ, C, P, F}

const GriddedMultiRegionField = Union{MultiRegionField, MultiRegionFunctionField}
const GriddedMultiRegionFieldTuple{N, T} = NTuple{N, T} where {N, T<:GriddedMultiRegionField}
const GriddedMultiRegionFieldNamedTuple{S, N} = NamedTuple{S, N} where {S, N<:GriddedMultiRegionFieldTuple}

# Utils
Base.size(f::GriddedMultiRegionField) = size(getregion(f.grid, 1))

@inline isregional(f::GriddedMultiRegionField) = true
@inline devices(f::GriddedMultiRegionField)    = devices(f.grid)
@inline sync_all_devices!(f::GriddedMultiRegionField)  = sync_all_devices!(devices(f.grid))

@inline switch_device!(f::GriddedMultiRegionField, d) = switch_device!(f.grid, d)
@inline getdevice(f::GriddedMultiRegionField, d)      = getdevice(f.grid, d)

@inline getregion(f::MultiRegionFunctionField{LX, LY, LZ}, r) where {LX, LY, LZ} =
    FunctionField{LX, LY, LZ}(_getregion(f.func, r),
                              _getregion(f.grid, r),
                              clock = _getregion(f.clock, r),
                              parameters = _getregion(f.parameters, r))

@inline getregion(f::MultiRegionField{LX, LY, LZ}, r) where {LX, LY, LZ} =
    Field{LX, LY, LZ}(_getregion(f.grid, r),
                      _getregion(f.data, r),
                      _getregion(f.boundary_conditions, r),
                      _getregion(f.indices, r),
                      _getregion(f.operand, r),
                      _getregion(f.status, r),
                      _getregion(f.boundary_buffers, r))

@inline _getregion(f::MultiRegionFunctionField{LX, LY, LZ}, r) where {LX, LY, LZ} =
    FunctionField{LX, LY, LZ}(getregion(f.func, r),
                              getregion(f.grid, r),
                              clock = getregion(f.clock, r),
                              parameters = getregion(f.parameters, r))

@inline _getregion(f::MultiRegionField{LX, LY, LZ}, r) where {LX, LY, LZ} =
    Field{LX, LY, LZ}(getregion(f.grid, r),
                      getregion(f.data, r),
                      getregion(f.boundary_conditions, r),
                      getregion(f.indices, r),
                      getregion(f.operand, r),
                      getregion(f.status, r),
                      getregion(f.boundary_buffers, r))

@inline reconstruct_global_field(f::AbstractField) = f

function reconstruct_global_field(mrf::MultiRegionField)
    global_grid  = on_architecture(CPU(), reconstruct_global_grid(mrf.grid))
    global_field = Field(location(mrf), global_grid)

    data = construct_regionally(interior, mrf)
    data = construct_regionally(Array, data)
    compact_data!(global_field, global_grid, data, mrf.grid.partition)

    fill_halo_regions!(global_field)
    return global_field
end

## Functions applied regionally
set!(mrf::MultiRegionField, v)  = apply_regionally!(set!,  mrf, v)
fill!(mrf::MultiRegionField, v) = apply_regionally!(fill!, mrf, v)

compute_at!(mrf::GriddedMultiRegionField, time)  = apply_regionally!(compute_at!, mrf, time)
compute_at!(mrf::MultiRegionComputedField, time) = apply_regionally!(compute_at!, mrf, time)

@inline hasnan(field::MultiRegionField) = (&)(construct_regionally(hasnan, field).regions...)

validate_indices(indices, loc, mrg::MultiRegionGrid, args...) =
    construct_regionally(validate_indices, indices, loc, mrg.region_grids, args...)

FieldBoundaryBuffers(grid::MultiRegionGrid, args...; kwargs...) =
    construct_regionally(FieldBoundaryBuffers, grid, args...; kwargs...)

FieldBoundaryConditions(mrg::MultiRegionGrid, loc, args...; kwargs...) =
  construct_regionally(inject_regional_bcs, mrg, Iterate(1:length(mrg)), Reference(mrg.partition), Reference(loc), args...; kwargs...)

function regularize_field_boundary_conditions(bcs::FieldBoundaryConditions,
                                              mrg::MultiRegionGrid,
                                              field_name::Symbol,
                                              prognostic_field_name=nothing)

  reg_bcs = regularize_field_boundary_conditions(bcs, mrg.region_grids[1], field_name, prognostic_field_name)
  loc = assumed_field_location(field_name)

  return FieldBoundaryConditions(mrg, loc; west = reg_bcs.west,
                                           east = reg_bcs.east,
                                           south = reg_bcs.south,
                                           north = reg_bcs.north,
                                           bottom = reg_bcs.bottom,
                                           top = reg_bcs.top,
                                           immersed = reg_bcs.immersed)
end

function inject_regional_bcs(grid, region, partition, loc, args...;
                              west = default_auxiliary_bc(topology(grid, 1)(), loc[1]()),
                              east = default_auxiliary_bc(topology(grid, 1)(), loc[1]()),
                             south = default_auxiliary_bc(topology(grid, 2)(), loc[2]()),
                             north = default_auxiliary_bc(topology(grid, 2)(), loc[2]()),
                            bottom = default_auxiliary_bc(topology(grid, 3)(), loc[3]()),
                               top = default_auxiliary_bc(topology(grid, 3)(), loc[3]()),
                          immersed = NoFluxBoundaryCondition())

  west  = inject_west_boundary(region, partition, west)
  east  = inject_east_boundary(region, partition, east)
  south = inject_south_boundary(region, partition, south)
  north = inject_north_boundary(region, partition, north)
  return FieldBoundaryConditions(west, east, south, north, bottom, top, immersed)
end

function Base.show(io::IO, field::MultiRegionField)

  bcs = field.boundary_conditions

  prefix =
      string("$(summary(field))\n",
             "├── grid: ", summary(field.grid), '\n',
             "├── boundary conditions: ", summary(bcs), '\n')
  middle = isnothing(field.operand) ? "" :
      string("├── operand: ", summary(field.operand), '\n',
             "├── status: ", summary(field.status), '\n')

  suffix = string("└── data: ", summary(field.data), '\n',
                  "    └── ", data_summary(field))

  print(io, prefix, middle, suffix)
end


text:
using Mustache
using Suppressor
include("_template.jl")

struct HPC
    server::String
    user::String
    cores::Integer
    jobs::Integer
    function HPC(server::String, user::String, cores::Integer = 2, jobs::Integer = 2)
        new(server, user, cores, jobs)
    end
end

sshcommand(hpc::HPC, command::String) =
    run(`ssh $(hpc.user)"@"$(hpc.server) "source /etc/profile;" $command`)

function createworkspaceinserver(exp::Experiment, hpc::HPC)
    name = exp.name
    @info "Creating a copy of the project in $name direcotry of your folder in server ..."
    workspace = exp.workspace
    sshcommand(hpc, "mkdir -p $workspace")
    run(`scp -r src $(hpc.user)"@"$(hpc.server)":~/"$workspace`)
    run(`scp -r $workspace"/." $(hpc.user)"@"$(hpc.server)":~/"$workspace`)
    # sshCommand("mkdir results/$name")
end

function createtasktemplate(exp::Experiment, hpc::HPC)
    home_directory = @capture_out run(`ssh "amraj@login2.hpc.dtu.dk" 'pwd'`)
    home_directory = home_directory[1:end-1] # To remove '\n'
    workspace = exp.workspace
    tpl = template()
    data = Dict(
        "name" => exp.name,
        "workspace" => "$home_directory/$workspace",
        "cores" => hpc.cores,
        "cluster" => "compute",
        "command" =>
            "julia $home_directory/$workspace/main.jl --project=$home_directory/$workspace",
    )
    rendered = render(tpl, data)
    open("$workspace/job.sh", "w") do io
        write(io, rendered)
    end
end

function submitjob(exp::Experiment, hpc::HPC)
    tasknumber = hpc.jobs
    workspace = exp.workspace
    bash_file = "job.sh"
    thejob = "cd $workspace && for i in {1..$tasknumber};  do bsub < $bash_file -env SERVER_ID=\$i ; sleep 0.2; done"
    @info sshcommand(hpc, thejob)
end

function fetchresults(exp::Experiment, hpc::HPC)
    workspace = exp.workspace
    hpcresult = workspace * "/hpc-" * hpc.server
    if !ispath(hpcresult)
        mkdir(hpcresult)
    end

    run(`scp -r $workspace"/." $(hpc.user)"@"$(hpc.server)":~/"$workspace"/*" $hpcresult`)
    @info "The results folder is downloaded in $hpcresult."
end

function fetch(exp::Experiment, hpc::HPC)
    s = sshcommand(hpc, "bstat")
    if success(s)
        @capture_out s
    end
    fetchresults(exp, hpc)
end

function showerror(exp::Experiment, hpc::HPC)
    workspace = exp.workspace
    command = "cat amraj@login2.hpc.dtu.dk:~/$workspace/Error.err"
    s = sshcommand(hpc, command)
    if success(s)
        @capture_out s
    end
end

#

# cluster(hpc::HPC) = @info sshcommand(hpc, "classstat")
# cluster(hpc::HPC, class::String) = @info sshcommand(hpc, "classstat $(class)")
#
# submitTask("asymmetric", "examples/asymmetric.jl", 10)
#
# removeWorkSpaceInServer("asymmetric")
# removeWorkSpaceDataServer("asymmetric")
#
# fetchResult("asymmetric")

# function submitTask(workspace, taskfile, tasknumber=40)
#     home_directory = @capture_out run(`ssh "amraj@login2.hpc.dtu.dk" 'pwd'`)
#     home_directory = home_directory[1:end-1] # To remove '\n'
#     command = "$home_directory/julia/$workspace/$taskfile"
#     bash_file = "$home_directory/julia/$workspace/server/templates/$workspace.sh"
#     createTaskTemplate(workspace, command)
#     createWorkSpaceInServer(workspace) #Uncomment to work
#     thejob = "for i in {1..$tasknumber}; do bsub < $bash_file ; sleep 1; done"
#     @info sshCommand(thejob)
#     # @info @capture_out run(`ssh amraj@login2.hpc.dtu.dk $thejob`)
# end

# function removeWorkSpaceInServer(name)
#     sshCommand("rm -rf julia/$name")
# end
#
# function removeWorkSpaceDataServer(name)
#     sshCommand("rm -rf results/$name")
# end


text:
mutable struct Operator
    ref::RefValue{C.CeedOperator}
    qf::AbstractQFunction
    dqf::AbstractQFunction
    dqfT::AbstractQFunction
    sub_ops::Vector{Operator}
    function Operator(ref, qf, dqf, dqfT)
        obj = new(ref, qf, dqf, dqfT, [])
        finalizer(obj) do x
            # ccall(:jl_safe_printf, Cvoid, (Cstring, Cstring), "Finalizing %s.\n", repr(x))
            destroy(x)
        end
        return obj
    end
end
destroy(op::Operator) = C.CeedOperatorDestroy(op.ref) # COV_EXCL_LINE
Base.getindex(op::Operator) = op.ref[]
Base.show(io::IO, ::MIME"text/plain", op::Operator) = ceed_show(io, op, C.CeedOperatorView)

"""
    Operator(ceed::Ceed; qf, dqf=QFunctionNone(), dqfT=QFunctionNone(), fields)

Creates a libCEED `CeedOperator` object using the given Q-function `qf`, and optionally its
derivative and derivative transpose.

An array of fields must be provided, where each element of the array is a tuple containing
the name of the field (as a string or symbol), the corresponding element restriction, basis,
and vector.

# Examples

Create the operator that builds the Q-data associated with the mass matrix.
```
build_oper = Operator(
    ceed,
    qf=build_qfunc,
    fields=[
        (:J, mesh_restr, mesh_basis, CeedVectorActive()),
        (:w, ElemRestrictionNone(), mesh_basis, CeedVectorNone()),
        (:qdata, sol_restr_i, BasisCollocated(), CeedVectorActive())
    ]
)
```
"""
function Operator(c::Ceed; qf, dqf=QFunctionNone(), dqfT=QFunctionNone(), fields)
    op = Operator(c, qf, dqf, dqfT)
    for f ∈ fields
        set_field!(op, String(f[1]), f[2], f[3], f[4])
    end
    op
end

function Operator(
    c::Ceed,
    qf::AbstractQFunction,
    dqf::AbstractQFunction,
    dqfT::AbstractQFunction,
)
    ref = Ref{C.CeedOperator}()
    C.CeedOperatorCreate(c[], qf[], dqf[], dqfT[], ref)
    Operator(ref, qf, dqf, dqfT)
end

"""
    create_composite_operator(c::Ceed, ops)

Create an [`Operator`](@ref) whose action represents the sum of the operators in the
collection `ops`.
"""
function create_composite_operator(c::Ceed, ops)
    ref = Ref{C.CeedOperator}()
    C.CeedCompositeOperatorCreate(c[], ref)
    comp_op = Operator(ref, QFunctionNone(), QFunctionNone(), QFunctionNone())
    comp_op.sub_ops = ops
    for op ∈ ops
        C.CeedCompositeOperatorAddSub(comp_op[], op[])
    end
    comp_op
end

function set_field!(
    op::Operator,
    fieldname::AbstractString,
    r::AbstractElemRestriction,
    b::AbstractBasis,
    v::AbstractCeedVector,
)
    C.CeedOperatorSetField(op[], fieldname, r[], b[], v[])
end

"""
    apply!(op::Operator, vin, vout; request=RequestImmediate())

Apply the action of the operator `op` to the input vector `vin`, and store the result in the
output vector `vout`.

For non-blocking application, the user can specify a request object. By default, immediate
(synchronous) completion is requested.
"""
function apply!(
    op::Operator,
    vin::AbstractCeedVector,
    vout::AbstractCeedVector;
    request=RequestImmediate(),
)
    C.CeedOperatorApply(op[], vin[], vout[], request[])
end

"""
    apply_add!(op::Operator, vin, vout; request=RequestImmediate())

Apply the action of the operator `op` to the input vector `vin`, and add the result to the
output vector `vout`.

For non-blocking application, the user can specify a request object. By default, immediate
(synchronous) completion is requested.
"""
function apply_add!(
    op::Operator,
    vin::AbstractCeedVector,
    vout::AbstractCeedVector;
    request=RequestImmediate(),
)
    C.CeedOperatorApplyAdd(op[], vin[], vout[], request[])
end

"""
    assemble_diagonal!(op::Operator, diag::CeedVector; request=RequestImmediate())

Overwrites a [`CeedVector`](@ref) with the diagonal of a linear [`Operator`](@ref).

!!! note "Note:"
    Currently only [`Operator`](@ref)s with a single field are supported.
"""
function assemble_diagonal!(op::Operator, diag::CeedVector; request=RequestImmediate())
    C.CeedOperatorLinearAssembleDiagonal(op[], diag[], request[])
end

"""
    assemble_diagonal!(op::Operator, diag::CeedVector; request=RequestImmediate())

Adds the diagonal of a linear [`Operator`](@ref) to the given [`CeedVector`](@ref).

!!! note "Note:"
    Currently only [`Operator`](@ref)s with a single field are supported.
"""
function assemble_add_diagonal!(op::Operator, diag::CeedVector; request=RequestImmediate())
    C.CeedOperatorLinearAssembleAddDiagonal(op[], diag[], request[])
end

max_stars_repo_name: AdelekeBankole/libCEED

max_issues_repo_name: AdelekeBankole/libCEED

max_forks_repo_name: AdelekeBankole/libCEED


text:
export MtlCommandQueue

const MTLCommandQueue = Ptr{MtCommandQueue}

"""
    MtlCommandQueue(dev::MtlDevice)

A queue that organizes command buffers to be executed by the GPU `MtlDevice`.

A MTLCommandQueue object is used to queue an ordered list of command buffers for a
MTLDevice to execute. Command queues are thread-safe and allow multiple outstanding
command buffers to be encoded simultaneously.

[Metal Docs](https://developer.apple.com/documentation/metal/mtlcommandqueue?language=objc)
"""
mutable struct MtlCommandQueue
    handle::MTLCommandQueue
    device::MtlDevice
end

Base.unsafe_convert(::Type{MTLCommandQueue}, q::MtlCommandQueue) = q.handle

Base.:(==)(a::MtlCommandQueue, b::MtlCommandQueue) = a.handle == b.handle
Base.hash(q::MtlCommandQueue, h::UInt) = hash(q.handle, h)

function MtlCommandQueue(dev::MtlDevice)
    queue = mtNewCommandQueue(dev)
    obj = MtlCommandQueue(queue, dev)
    finalizer(unsafe_destroy!, obj)
    return obj
end

function unsafe_destroy!(queue::MtlCommandQueue)
    mtRelease(queue.handle)
end

## properties

Base.propertynames(::MtlCommandQueue) = (:device, :label)

function Base.getproperty(o::MtlCommandQueue, f::Symbol)
    if f === :device
        MtlDevice(mtCommandQueueDevice(o))
    elseif f === :label
        ptr = mtCommandQueueLabel(o)
        ptr == C_NULL ? nothing : unsafe_string(ptr)
    else
        getfield(o, f)
    end
end

## display

function show(io::IO, ::MIME"text/plain", q::MtlCommandQueue)
    println(io, "MtlCommandQueue:")
    print(io,   " device: ", q.device)
end


text:
using QuantumStateBase
using SqState
using Dash
using DashHtmlComponents
using DashCoreComponents
using PlotlyJS

export start_real_time_system

const DIM = 70
const WF = WignerFunction(LinRange(-3, 3, 101), LinRange(-3, 3, 101), dim=DIM)
const M = SqState.get_model("model")

function banner()
    return html_div([
        html_h1("Real Time Quantum State Tomography"),
        html_div("Real time QST: A real time state inference system for RK Lee's lab in NTHU"),
    ])
end

function ctl()
    return html_div([
        dcc_interval(id="interval", interval=3*1000, n_intervals=0),
        html_h2("Inference"),
        dcc_radioitems(id="mode", options=[
            Dict("label"=>"Single", "value"=>"S"),
            Dict("label"=>"Continuous", "value"=>"C"),
        ], value="S"),
        html_button(id="snap", children="Snap", n_clicks=0),
    ])
end

function get_plots(filename::String, width::Integer, height::Integer)
    isempty(filename) && (return [])
    ρ, w = get_𝛒_and_𝐰(filename, fix_θ=false, wf=WF, m=M)

    return [
        dcc_graph(figure=wigner_surface(w, width, height)),
        dcc_graph(figure=wigner_heatmap(w, width, height)),
        dcc_graph(figure=density_matrix(ρ, width, height))
    ]
end

function plots(init_filename::String, width::Integer, height::Integer)
    return html_div(
        id="plots",
        style=Dict("columnCount"=>3),
        get_plots(init_filename, width, height)
    )
end

function gen_app(; data_path=datadep"QSTDemo", width=500, height=500)
    files = readdir(data_path)

    app = dash(external_stylesheets=["https://codepen.io/chriddyp/pen/bWLwgP.css"])
    app.layout = html_div([
        banner(),
        ctl(),
        plots(joinpath(data_path, files[1]), width, height),
    ])

    callback!(
        app,
        Output("plots", "children"),
        Input("snap", "n_clicks"),
    ) do n

        return get_plots(joinpath(data_path, files[(n-1) % length(files) + 1]), width, height)
    end

    callback!(
        app,
        Output("snap", "n_clicks"),
        Input("interval", "n_intervals"),
        State("mode", "value")
    ) do n, mode
        (mode=="S") && (return no_update())

        return n
    end

    return app
end

start_real_time_system() = run_server(gen_app(), "127.0.0.1", 8080)


text:
# # Optimiziation of an excitatory/inhibitory (E/I) recurrent network

#=
In this example, I optimize an RNN network with E/I units.
The dynamics is expressed as follows:
```math
\tau\, \frac{\text{d} \mathbf{u}}{\text{d}t} = - \mathbf{u} +
W f\left(\mathbf{u}\right) + \mathbf{h}
```
Where $W$ is a connection matrix with no autapses that preserves the separation
between E and I units (Dale's law). There is also a sparseness parameter for $W$,
to increase the difficulty (and as proof of concept).
Then there is a leak term $-\mathbf{u}$
and a constant external input $\mathbf{h}$. Finally the activation function
$f\left( \cdot \right)$ is a rectified-linear function.
=#

# ## Initialization
using Plots,NamedColors
using LinearAlgebra,Statistics
using Random
using SmoothedSpectralAbscissa ; const SSA=SmoothedSpectralAbscissa
using OrdinaryDiffEq # to run the dynamics
Random.seed!(0)

iofunction(x::Real) = max(0.0,x)

function run_rnn_dynamics(u0::Vector{R},W::Matrix{R},h::Vector{R},
    tmax::R,dt::R=0.01;verbose=false) where R
  f = function (du,u,p,t)
    mul!(du,W,iofunction.(u))
    @. du = du - u + h
    return du
  end
  prob = ODEProblem(f,u0,(0.,tmax))
  solv = solve(prob,Tsit5();verbose=verbose,saveat=dt)
  ret_u =hcat(solv.u...)
  ret_norms = mapslices(norm,ret_u;dims=1)[:]
  return solv.t,ret_u,ret_norms
end;

# ## Build the starting weight matrix

const sparseness = 0.5
const ne = 35
const ni = 15
const ntot = ne+ni
Wmask = hcat(fill(1.,ntot,ne),fill(-1.,ntot,ni)) # 1 for E , -1 for I, 0. for no connection
for i in 1:ntot,j in 1:ntot
  if i==j || rand()<sparseness
    Wmask[i,j]=0.
  end
end
heatmap(Wmask;
  ratio=1,seriescolor=:bwr,
  clims=(-1,1),cbar=nothing,axis=nothing,ticks=nothing,border=:none)
#=
The matrix `Wmask` shown above specifies the connectivy
(presence of a conneciton, and wether the neuron is E or I)
The full weight matrix $W$ prior to optimization is defined as follows:
=#
W0 = 2.0 .* rand(ntot,ntot) .* Wmask;
#=
Even with the leaky term, the dynamics $\mathbf{u}(t)$ is very unstable.
=#
u0 = 3.0.*randn(ntot)
h = 0.1 .* rand(ntot)
times,dyn_t,dyn_norms = run_rnn_dynamics(u0,W0,h,10.0,0.05)
plot(times,dyn_norms;
  leg=false,linewidth=3,color=:black,xlabel="time",ylabel="norm(u(t))",
  yscale=:log10,label="before optimization")

# Note that the y-scale is exponential here.

# ## Objective function that excludes diagonal
using Optim
#=
To keep the signs of $W_{i,j}$ I make a reparametrization as follows.
```math
W_{i,j} = s_j \; \exp\left(\beta_{i,j}\right)
```
I then need to propagate the gradient of the SSA. I will also inclide a
2-norm regularizer on the weights
=#
function objective_and_grad_constraints(x::Vector{R},grad::Union{Nothing,Vector{R}},
    n::Integer,ssa_eps::R,alloc::SSA.SSAAlloc,W0::Matrix{R}) where R
  betas=reshape(x,(n,n))
  mat = @. exp(betas) * W0  # transform, apply constraints
  gradmat = isnothing(grad) ? nothing : similar(mat)
  obj = SSA.ssa!(mat,gradmat,alloc,ssa_eps)
  if !isnothing(grad)
    gradmat .*= mat # propagate gradient for constraint
    for i in eachindex(gradmat)
      grad[i]=gradmat[i]
    end
  end
  return obj
end;

# ## Optimizer and optimization

const ssa_eps=0.001
const alloc = SSA.SSAAlloc(ntot)
const y0 = map(w-> w!=0. ? log(abs(w)) : 0. ,W0[:])

function objfun!(F,G,y)
  λ = 3.0/length(y) # regularizer calibration
  obj=objective_and_grad_constraints(y,G,ntot,ssa_eps,alloc,W0) # add the regularizer
  obj += 0.5*λ*mapreduce(x->x^2,+,y)
  if !isnothing(G)
    @. G += λ*y # gradient of regularizer
  end
  return obj
end

opt_out = optimize(Optim.only_fg!(objfun!),y0,BFGS(),Optim.Options(iterations=1_000))
y_opt=Optim.minimizer(opt_out)
W_opt = Wmask .* exp.(reshape(y_opt,(ntot,ntot))); # convert from beta to weight

# Comparison between inital matrix and the optimized version.
# Note the E/I separation and the spontaneous symmetry.
_ = let wboth = hcat(W0,fill(NaN,ntot,10),W_opt)
  _wex = extrema(W_opt)
  _zero_rel =abs(_wex[1])/(_wex[2]-_wex[1])
  _cgrad = cgrad([:blue,:white,:red],[0, _zero_rel ,1.])
  heatmap(wboth;ratio=1,seriescolor=_cgrad,clim=_wex,
    axis=nothing,ticks=nothing,border=:none)
end

# Now, I consider the norm of $\mathbf u(t)$ in the optimized system...
times,dyn_t_opt,dyn_norms_opt = run_rnn_dynamics(u0,W_opt,h,40.0,0.05)
plot(times,dyn_norms_opt;
      leg=:topright,linewidth=3,color=:blue,
      xlabel="time",ylabel="norm(x(t))", label="after optimization")

#=
STABLE !

There seems to be a large initial amplificaiton, but the activity settles to a stable point.
=#

# ## Extras

#=
In gradient based optimization with no automatic differentiation, it is
always necessary to test the gradient of the objective function.
The procedure is illustrated below.
=#

using Calculus
function test_gradient(myobjfun,y0)
  grad_an = similar(y0)
  _ = myobjfun(1.0,grad_an,y0) # compute gradient analytically
  grad_num = Calculus.gradient(y->myobjfun(1.0,nothing,y),y0) # compute it numerically
  for (k,y) in enumerate(y0)
    if y == 0.
      grad_num[k] = 0.
   end end
  return (grad_an,grad_num)
end

#=
To enable testing, set `do_the_test=true`
It is advised to reduce the size of the system.
=#

(x1,x2)=test_gradient(objfun!,randn(ntot^2))
scatter(x1,x2;ratio=1)
plot!(identity)

# Literate.markdown("examples/03_EI.jl","docs/src";documenter=true,repo_root_url="https://github.com/dylanfesta/SmoothedSpectralAbscissa.jl/blob/master") #src


text:
using Test, DiscreteExteriorCalculus
using Combinatorics: combinations

@testset "relative orientation" begin
    s = Simplex(Point(0,0,0), Point(1,0,0))
    comp = CellComplex([s])
    for v in comp.cells[1]
        @test length(v.parents) == 1
        p = collect(keys(v.parents))[1]
        o = v.parents[p]
        @test p == comp.cells[2][1]
        if v.points[1] == s.points[1]
            @test !o
        else
            @test o
        end
    end
    @test comp.cells[1][1].points[1] != comp.cells[1][2].points[1]
end

@testset "CellComplex" begin
    points = [Point(0,0,0), Point(1,0,0), Point(0,1,0), Point(0,0,1), Point(1,1,1)]
    s1, s2 = Simplex(points[1:4]), Simplex(points[2:5])
    comp1, comp2 = CellComplex([s1]), CellComplex([s2])
    @test Set([c.points[1] for c in comp1.cells[1]]) == Set(s1.points)
    @test Set(comp1.cells[end][1].points) == Set(s1.points)
    @test Set([c.points[1] for c in comp2.cells[1]]) == Set(s2.points)
    @test Set(comp2.cells[end][1].points) == Set(s2.points)
    for k in 1:4
        @test length(comp1.cells[k]) == length(combinations(1:4, k))
        @test length(comp2.cells[k]) == length(combinations(1:4, k))
    end
    comp = CellComplex([s1, s2])
    @test [length(a) for a in comp.cells] == [4 * 2 - 3, 6 * 2 - 3, 4 * 2 - 1, 1 * 2 - 0]
    @test Set([c.points[1] for c in comp.cells[1]]) == Set(points)
    ps = [Set(c.points) for c in comp.cells[end]]
    @test (ps == [Set(s1.points), Set(s2.points)]) || (ps == [Set(s2.points), Set(s1.points)])
end

@testset "boundary and absolute orientation" begin
    points = [Point(0,0), Point(1,0), Point(0,1), Point(1,1),
        Point(2,0), Point(2,1), Point(3,0)]
    s1, s2, s3 = Simplex(points[1:3]), Simplex(points[2:4]), Simplex(points[5:7])
    comp = CellComplex([s1, s2, s3])
    @test Set(map(Simplex, comp.cells[end])) == Set([s1, s2, s3])
    orient!(comp)
    s2_positive = Simplex(points[3], points[2], points[4])
    s3_positive = Simplex(points[6], points[5], points[7])
    @test Set(map(Simplex, comp.cells[end])) == Set([s1, s2_positive, s3_positive])
    # boundaries
    interior, exterior = boundary_components(comp)
    @test isempty(interior)
    @test length(exterior) == 2
    @test Set([length(e.cells[end]) for e in exterior]) == Set([3,4])
    exterior_union = vcat([e.cells[end] for e in exterior]...)
    relative_orientations = Bool[]
    for c in exterior_union
        @test length(c.parents) == 1
        push!(relative_orientations, collect(values(c.parents))[1])
    end
    # boundary not positively oriented
    @test !(all(relative_orientations))
    orient!(exterior_union)
    # now boundary is positively oriented
    for c in exterior_union
        @test length(c.parents) == 1
        @test collect(values(c.parents))[1]
    end
end


text:
using GtkIDE
using Test, Gtk

###############
## EDITOR

sleep_time = 0.5

function _test_completion_232_(x::Int64, y::Float64)
end

@testset "Editor" begin

main_window = GtkIDE.main_window
editor = main_window.editor
console = GtkIDE.current_console(main_window)

cd(joinpath(GtkIDE.HOMEDIR,".."))
GtkIDE.update_pathEntry(main_window.pathCBox,pwd())

sleep(sleep_time)#time for loading
GtkIDE.open_in_new_tab(joinpath("test","testfile.jl"),main_window.editor)
sleep(sleep_time)#time for loading

t = GtkIDE.current_tab(editor)
b = t.buffer
#some helper functions
function goto_line(buffer::GtkIDE.GtkTextBuffer,line::Integer)
    it = GtkIDE.mutable( GtkIDE.GtkTextIter(buffer,1) )
    GtkIDE.set_gtk_property!(it,:line,line-1)
    GtkIDE.place_cursor(buffer,it)
end
function to_line_end(buffer::GtkIDE.GtkTextBuffer)
    it = GtkIDE.mutable( GtkIDE.get_text_iter_at_cursor(buffer) )
    skip(it,:forward_to_line_end)
    GtkIDE.place_cursor(buffer,it)
end

goto_line(b,1)
    sleep(sleep_time)
GtkIDE.run_line(console, t)
    sleep(sleep_time)

@test x == 2

goto_line(b,2)
to_line_end(b)
GtkIDE.init_autocomplete(t.view,t)
sleep(0.5)

(txt,its,ite) = GtkIDE.get_line_text(b, GtkIDE.get_text_iter_at_cursor(b) )

@test txt == "_test_completion_232_"

sleep(sleep_time)
t = GtkIDE.current_tab(editor)
t.modified = false
GtkIDE.close_tab(editor)

end

###############
## CONSOLE

@testset "Console" begin

import GtkIDE.GtkREPL.command

main_window = GtkIDE.main_window
console = GtkIDE.current_console(main_window)

#stress test printing
function printtest(k)
    for i=1:5
        @show k
        println(rand(3))
        sleep(rand()/10001)
    end
end

for i=1:3
    global t = @async printtest(i)
end

wait(t)
sleep(sleep_time)

##

#don't know how to get the hardware_keycode
function emit_keypress(w)

    keyevent = Gtk.GdkEventKey(Gtk.GdkEventType.KEY_PRESS, Gtk.gdk_window(w),
               Int8(0), UInt32(0), UInt32(0), GdkKeySyms.Return, UInt32(0),
               convert(Ptr{UInt8},C_NULL), UInt16(13), UInt8(0), UInt32(0) )

    signal_emit(w, "key-press-event", Bool, keyevent)
end

command(console,"x = 3")
    sleep(sleep_time)
emit_keypress(console.view)
    sleep(sleep_time)
@test x == 3

command(console,"_test_completion_")
    sleep(sleep_time)
cmd = command(console)
    sleep(sleep_time)
GtkIDE.GtkREPL.autocomplete(console,cmd, length(cmd))
    sleep(sleep_time)

@test command(console) == "_test_completion_232_"

cmd = command(console)
command(console, cmd * "(")
cmd = command(console)
    sleep(sleep_time)
GtkIDE.GtkREPL.autocomplete(console,cmd, length(cmd))
    sleep(sleep_time)

@test startswith(command(console), "_test_completion_232_(x::Int64, y::Float64)")

command(console,"clc")
    sleep(sleep_time)
emit_keypress(console.view)
    sleep(sleep_time)

end

##


text:
function interiorbcs!(system::LegSystem,n::Int64,split::Int64,
    Q::Vector{Float64},A::Vector{Float64},children::Vector{Int64})
    # children = system.branches.children[split];
    # println("Interior boundary function assignment:")
    if length(children) == 1
        f = LegHemodynamics.fsingle!;
        J = LegHemodynamics.Jsingle!;
    elseif length(children) == 2
        f = LegHemodynamics.fdouble!;
        J = LegHemodynamics.Jdouble!;
    elseif length(children) == 3
        f = LegHemodynamics.ftriple!;
        J = LegHemodynamics.Jtriple!;
    end
    # println("Interior boundary allocation:")
    W = zeros(length(children)+1);
    beta = zeros(length(children)+1);
    A0 = zeros(length(children)+1);
    c0 = zeros(length(children)+1);
    beta[1] = system.branches.beta[split][end];
    A0[1] = system.branches.A0[split][end];
    c0[1] = system.branches.c0[split][end];
    for j = 1:length(children)
        beta[j+1] = system.branches.beta[children[j]][end];
        A0[j+1] = system.branches.A0[children[j]][end];
        c0[j+1] = system.branches.c0[children[j]][end];
    end
    W[1] = system.branches.W1[split];
    W[2:end] = system.branches.W2[children];
    Qnew = zeros(length(children)+1);
    Anew = zeros(length(children)+1);
    iters = Int64[0];
    # println("Interior Newton solver:")
    LegHemodynamics.solvesplits!(iters,Qnew,Anew,children,Q,A,W,beta,A0,c0,
        system.solverparams.rho,f,J,system.solverparams.maxiter,
        system.solverparams.maxval,system.solverparams.epsJ,
        system.solverparams.epsN)
    # println("Interior boundary update save:")
    system.solverparams.totaliter += iters[1];
    system.branches.Q[split][system.solverparams.JL,n+2] = Qnew[1];
    system.branches.A[split][system.solverparams.JL,n+2] = Anew[1];
    for j = 1:length(children)
        system.branches.Q[children[j]][1,n+2] = Qnew[j+1];
        system.branches.A[children[j]][1,n+2] = Anew[j+1];
    end
end

max_stars_repo_name: dcanuto/leg-hemodynamics

max_issues_repo_name: dcanuto/leg-hemodynamics

max_forks_repo_name: dcanuto/leg-hemodynamics


text:
"""
    showitems(items)

Visualize `items`.
"""
function showitems(items; C=RGBA{N0f8}, showbounds = false)
    bounds = boundsof([getbounds(item) for item in items])
    a = OffsetArray(zeros(C, length.(bounds.rs)...), bounds.rs...)

    for item in items
        showitem!(a, item)
    end

    if showbounds
        for item in items
            showbounds!(a, getbounds(item))
        end
    end

    return a
end

showitems(item::AbstractItem; kwargs...) = showitems((item,); kwargs...)

## Visualization utilities

function showimage!(dst, img)
    for I in CartesianIndices(img)
        if checkbounds(Bool, dst, I)
            dst[I] = img[I]
        end
    end
end

function showgrid(items; fillvalue = RGBA{N0f8}(0.,0.,0.,0.), kwargs...)
    imgs = [showitems(item) for item in items]
    mosaicview(imgs; fillvalue = fillvalue, kwargs...)
end

showbounds(bounds) = showbounds!(zeros(RGBA{N0f8}, sum.(bounds.rs)), bounds)

function showbounds!(img, bounds::Bounds{2})
    ry, rx = bounds.rs
    miny, maxy = extrema(ry)
    minx, maxx = extrema(rx)
    points = [
        SVector(miny, minx),
        SVector(miny, maxx),
        SVector(maxy, maxx),
        SVector(maxy, minx),
    ]
    showpolygon!(img, points, RGBA(0, 0, 0, 1))
    return img
end

function showkeypoint!(img, point::SVector{N}, C; sz = 3) where N
    I = CartesianIndex(Tuple(round.(Int, point)))
    offset = sz ÷ 2
    Is = I-offset*one(CartesianIndex{N}):I+offset*one(CartesianIndex{N})
    img[Is[[checkbounds(Bool, img, I) for I in Is]]] .= C
end

function showpolygon!(img, points, C)
    polydraw = ImageDraw.Polygon([Tuple(round.(Int, reverse(point))) for point in reshape(points, :)])
    draw!(img, polydraw, C)
end

"""
    boundingranges(ps)

Find bounding index ranges for points `ps`.
"""
function boundsof(boundss::AbstractVector{<:Bounds{N}}) where N
    rs = map(1:N) do i
        minimum((minimum(bs.rs[i]) for bs in boundss)):maximum((maximum(bs.rs[i]) for bs in boundss))
    end
    return Bounds(Tuple(rs))
end