text:
using Test, Random
using MCMCChains
using Distributions

## Test Chain
# Define the experiment
n_iter = 4000
n_name = 3
n_chain = 2

# observations
Random.seed!(1234)
x = map(i -> i + 2e-1*randn(), 1:n_name)

# some sample experiment results
Random.seed!(1234)
val1 = 0.5*randn(n_iter, n_name, n_chain) .+ x'
val2 = 2*randn(n_iter, n_name, n_chain) .+ x'

# construct a Chains object
chn1 = Chains(val1)
chn2 = Chains(val2)

lpfun = function f(chain::Chains)

    p1 = Array(chain[:,1,:])
    p2 = Array(chain[:,2,:])
    p3 = Array(chain[:,3,:])

    lp = map(p -> logpdf(Normal(p), x[1]), p1)
    lp += map(p -> logpdf(Normal(p), x[2]), p2)
    lp += map(p -> logpdf(Normal(p), x[3]), p3)

    return lp
end

@testset "deviance information criterion" begin

    DIC1, pD1 = dic(chn1, lpfun)
    DIC2, pD2 = dic(chn2, lpfun)

    @test DIC1 < DIC2
    @test pD1 < pD2
end


text:
# In this module, all parameters for the cat state preparation are defined:

module parameters
export parameters_qb

parameters_para=Dict(
    "N" =>2,
    "w" => 20.0f0,
    "force_mag" => 5.0f0,
    "max_episode_steps" => 150,
    "dt" => 0.00025f0,
    "n_substeps" => 80,
    "gamma" => 1.0f0)
end

ext: jl


text:
@testset "Exact" begin
    seq = dna"ACGTACG"

    @testset "forward" begin
        @test findfirst(dna"", seq) === 1:0
        @test findfirst(dna"AC", seq) === 1:2
        @test findfirst(dna"AC", seq, 2) === 5:6
        @test findfirst(dna"AC", seq, 2, 5) === nothing
        @test findfirst(dna"TG", seq) === nothing
        @test findfirst(dna"TN", seq) === 4:5
        @test findfirst(dna"ACG", seq)  === 1:3
        @test findfirst(dna"ACG", seq, 2) === 5:7
        @test findfirst(seq, seq) === 1:lastindex(seq)

        @test findfirst(dna"", dna"") === 1:0
        @test findfirst(dna"", dna"", -1) === 1:0
        @test findfirst(dna"", dna"", 2) === nothing

        @test first(findfirst(dna"", seq)) === 1
        @test first(findfirst(dna"AC", seq)) === 1
        @test first(findfirst(dna"AC", seq, 2)) === 5
        @test findfirst(dna"AC", seq, 2, 5) === nothing

        query = ExactSearchQuery(dna"ACG")
        @test findfirst(query, seq) === 1:3
        @test findfirst(query, seq, 2) === 5:7
        @test findfirst(query, seq, 2, 6) === nothing
        @test first(findfirst(query, seq)) === 1
        @test first(findfirst(query, seq, 2)) === 5
        @test findfirst(query, seq, 2, 6) === nothing
    end

    @testset "backward" begin
        @test findlast(dna"", seq) === 8:7
        @test findlast(dna"AC", seq) === 5:6
        @test findlast(dna"AC", seq, 5) === 1:2
        @test findlast(dna"AC", seq, 5, 2) === nothing
        @test findlast(dna"TG", seq) === nothing
        @test findlast(dna"TN", seq) === 4:5
        @test findlast(dna"ACG", seq) === 5:7
        @test findlast(dna"ACG", seq, 6) === 1:3
        @test findlast(seq, seq) === 1:lastindex(seq)

        @test findlast(dna"", dna"") === 1:0
        @test findlast(dna"", dna"", 2) === 1:0
        @test findlast(dna"", dna"", -1) === nothing

        @test first(findlast(dna"", seq)) === 8
        @test first(findlast(dna"AC", seq)) === 5
        @test first(findlast(dna"AC", seq, 5)) === 1
        @test findlast(dna"AC", seq, 5, 2) === nothing

        query = ExactSearchQuery(dna"ACG")
        @test findlast(query, seq) === 5:7
        @test findlast(query, seq, 6) === 1:3
        @test findlast(query, seq, 2, 6) === nothing
        @test first(findlast(query, seq)) === 5
        @test first(findlast(query, seq, 6)) === 1
        @test findlast(query, seq, 6, 2) === nothing
    end
end


text:
module house
    include("./fixed_parameters.jl")
    using .fixed_parameters
    include("./human.jl")
    using .human

    mutable struct House
        quarantine::Bool
        warning::Bool
        walls::Array
        house_border::Tuple
        crew::Array

        in_house::Any
        house_time::Any

        function House(walls::Array, number_of_students::Int64, number_of_teachers::Int64, intern_random_infection_rate::Float64, student_break_rules::Float64, teacher_break_rules::Float64)
            house_border = ((walls[1][1][1],walls[1][2][2]), (walls[2][1][1],walls[2][2][1]))
            crew = [Array{Any}(undef, number_of_students), Array{Any}(undef, number_of_teachers)]
            for i in 1:number_of_students
                spawnpoint = (rand(house_border[1][1]+d:0.01:house_border[2][1]-d), rand(house_border[2][2]+d:0.01:house_border[1][2]-d))
                crew[1][i] = Human(spawnpoint, student_break_rules, true, intern_random_infection_rate)
            end
            for i in 1:number_of_teachers
                spawnpoint = (rand(house_border[1][1]+d:0.01:house_border[2][1]-d), rand(house_border[2][2]+d:0.01:house_border[1][2]-d))
                crew[2][i] = Human(spawnpoint, teacher_break_rules, true, intern_random_infection_rate)
            end

            new(false, false, walls, house_border, crew, in_house, house_time)
        end
    end

    function in_house(self::House, position::Tuple)
        if position[1]>=self.house_border[1][1] && position[1]<=self.house_border[2][1] && position[2]>=self.house_border[2][2] && position[2]<=self.house_border[1][2]
            return true
        else
            return false
        end
    end

    function house_time(self::House, fakewalls::Array)
        for i in self.crew
            for j in i
                if !self.in_house(self, j.position) && (j.destination==nothing || !self.in_house(self, j.destination))
                    j.destination = (rand(self.house_border[1][1]+d:0.01:self.house_border[2][1]-d), rand(self.house_border[2][2]+d:0.01:self.house_border[1][2]-d))
                    j.step = move_speed
                elseif self.in_house(self, j.position) && j.destination!=nothing && sqrt((j.destination[1]-j.position[1])^2+(j.destination[2]-j.position[2])^2)<j.step
                    j.destination = nothing
                    j.step = indoor_speed
                elseif self.in_house(self, j.position) && j.destination==nothing
                    j.step = indoor_speed
                    if self.quarantine
                        j.step = quarantine_speed
                    end
                end
                loc_1 = j.position[2]>lund_size[2]-hwh || j.position[2]<cwh
                j.move(j, fakewalls)
                loc_2 = j.position[2]>lund_size[2]-hwh || j.position[2]<cwh
                if loc_1!=loc_2
                    if !loc_2 && (self.warning || rand()<=1-j.break_rules)
                        j.radius = radius_with_masks
                    else
                        j.radius = radius_without_masks
                    end
                end
            end
        end
    end

    export House

end


text:
"""

```
sphereradiusfromshellvolume(volume,step)
```

Computes the radius that corresponds to a spherical shell of
a given volume.

"""
function sphereradiusfromshellvolume(volume,step)

  fourthirdsofpi = 4*pi/3
  if 3*step*volume - pi*step^4 <= 0.
    return 0.
  end
  rmin = (sqrt(3*pi)*sqrt(3*step*volume-pi*step^4)-3*pi*step^2)/(6*pi*step)
  return ( 0.5*( volume/fourthirdsofpi + 2*rmin^3 ) )^(1/3)

end


text:
struct Size
    slug::String
    available::Bool
    transfer::Real
    price_monthly::Real
    price_hourly::Real
    memory::Integer
    vcpus::Integer
    disk::Integer
    regions::Array{String, 1}

    function Size(data::Dict{String})
        new(
            data["slug"],
            data["available"],
            data["transfer"],
            data["price_monthly"],
            data["price_hourly"],
            data["memory"],
            data["vcpus"],
            data["disk"],
            data["regions"]
        )
    end
end

function show(io::IO, s::Size)
    print(io, "Size ($(s.slug))")
end

function getallsizes!(client::AbstractClient)
    response = getdata!(client, joinpath(ENDPOINT, "sizes"))

    if floor(response.status/100) == 2 # OK
        body = parse(String(response.body))
        meta = body["meta"]
        links = body["links"]
        data = body["sizes"]

        sizes = Array{Size, 1}(UndefInitializer(), meta["total"])

        for (i, dsize) in enumerate(data)
            sizes[i] = Size(dsize)
        end
    else
        error("Received error $(response.status)")
    end

    sizes
end


text:
using DrakeVisualizer
using RigidBodyTreeInspector
using RigidBodyTreeInspector: RGBA, HyperSphere, Point
using Interact

function RigidBodyTreeInspector.inspect!(state::MechanismState, vis::Visualizer, problem::CalibrationProblem, result::CalibrationResult)
    radius = 0.01
    semitransparent_green = RGBA(0., 1, 0, 0.5)
    for points in values(result.marker_positions)
        for point in points
            sphere = HyperSphere(Point{3, Float64}(point.v), radius)
            addgeometry!(vis, state.mechanism, point.frame, GeometryData(sphere, semitransparent_green))
        end
    end
    markervis = vis[:marker_measurements]

    cal_selector = radiobuttons(Dict("Before calibration" => false, "After calibration" => true))
    pose_slider = slider(1 : num_poses(result), value = 1, label = "Pose number")
    map(pose_slider, cal_selector) do i, cal
        set_configuration!(state, result.configurations[i])
        if !cal
            q_before_cal = problem.pose_data[i].configuration
            for joint in keys(problem.calibration_param_bounds)
                set_configuration!(state, joint, q_before_cal[joint])
            end
        end
        settransform!(vis, state)

        delete!(markervis)
        for points in values(problem.pose_data[i].marker_positions)
            for point in points
                num_measured_coordinates = 3 - count(isnan.(point.v))
                if num_measured_coordinates > 0
                    blueness = num_measured_coordinates / 3
                    color = RGBA(0., 0., blueness, 0.5)
                    sphere = HyperSphere(Point{3, Float64}(point.v), radius)
                    addgeometry!(markervis, state.mechanism, point.frame, GeometryData(sphere, color))
                end
            end
        end
    end

    vbox(cal_selector, pose_slider)
end


text:
# Computes coefficients for the I_v and J_v series expansions.

function Iv_series_coeff!(t::Transit_Struct{T}) where {T <: Real}
# Use series expansion to compute coefficients in the
# series solution of I_{v_max}, needed for k^2 < 1:
v = t.v_max
# Computing leading coefficient (n=0):
coeff = 2/(2v+one(T))
# Compute leading coefficient in I_v series:
t.Iv_coeff[1] = convert(T,coeff)
# Now, compute higher order terms until desired precision is reached:
for i = 1:t.nmax-1
  n = 2i
  coeff *= (n-1)*(n+2v-1)
  coeff /= n*(n+2v+1)
  t.Iv_coeff[i+1] = coeff
end
return
end

function dJvdk_series_coeff!(t::Transit_Struct{T}) where {T <: Real}
# Use series expansion to compute J_v and its derivative, dJ_v/dk.
# Computing leading coefficient (n=0).  Need to compute for
# both v_max and v_max-1.  Also, need to keep track of k^2 < 1
# and k^2 >= 1.  So, Jv_coeff is 3-dimensional array:
# Jv_coeff[2,2,nmax], where first dimension is for k^2 <1 (1)
# and k^2 >= 1 (2); second dimension is for v_max (1) and v_max-1 (2),
# while third dimension is for the series coefficients.
# Computing leading coefficient (n=0):
for k2 = 0:1
coeff = zero(T)
# Loop over v_max and v_max-1:
for j=1:2
  # v is v_max or v_max-1 (we need both since downward recursion requires two):
  v = t.v_max-(j-1)
  if k2 < 1
    coeff = 0.75*convert(T,pi)/exp(lfactorial(v+2))
    # multiply by (2v-1)!!
    @inbounds for i=2:2:2v
      coeff *= 0.5*(i-1)
    end
    # Add leading term to J_v:
    t.Jv_coeff[1,j,1] = coeff
    if t.grad
      t.dJvdk_coeff[1,j,1] = coeff*(2v+1)
    end
    # Now, compute higher order terms until desired precision is reached:
    @inbounds for i=1:t.nmax-1
      n = 2i
      coeff *= (n-1)*(n+2v-1)
      coeff /= n*(n+2v+4)
      t.Jv_coeff[1,j,i+1] = coeff
      if t.grad
        t.dJvdk_coeff[1,j,i+1] = coeff*(n+2v+1)
      end
    end
  else # k^2 >= 1
    coeff = convert(T,pi)
    # Compute (2v-1)!!/(2^v v!):
    @inbounds for i=2:2:2v
      coeff *= (i-1)/i
    end
    # Store leading terms:
    t.Jv_coeff[2,j,1] = coeff
    if t.grad
      t.dJvdk_coeff[2,j,1] = zero(T)
    end
    # Loop over higher order terms:
    for i = 1:t.nmax-1
      n = 2i
      coeff *= (n-5)*(n+2v-1)
      coeff /= (n*(n+2v))
      t.Jv_coeff[2,j,i+1] = coeff
      if t.grad
        t.dJvdk_coeff[2,j,i+1] = -n*coeff
      end
    end
  end
end
end
end

max_stars_repo_name: tagordon/limbdark

max_issues_repo_name: tagordon/limbdark

max_forks_repo_name: tagordon/limbdark


text:
using Compose

function plot_compose(q::Tait; show_string = true, show_tait = true, show_half_edges = true, show_faces = true, face_color = "salmon", background = "white",
        start_hes = Dict(), radius = 0.5)
    x_len, y_len, normal_pos = generate_normalized_locations(q)
    es, bds, hes_label, hes_x, hes_y, hes_rot = generate_half_edges_info(q, normal_pos)
    ctrl_points = generate_ctrl_points(q, normal_pos, radius, start_hes)
    update_half_edge_info!(q, hes_x, hes_y, hes_rot, normal_pos, ctrl_points)
    ct_edges = generate_context_edges(q, es, bds, hes_label, hes_x, hes_y, hes_rot, ctrl_points, show_half_edges)
    ct_vs, ct_genus = generate_context_vertices(q, normal_pos, show_tait)
    ct_fs = generate_context_faces(q, normal_pos, ctrl_points, show_faces)

    bg = (context(), rectangle(), fill(background))
    set_default_graphic_size(3(x_len+1)*cm, 3(y_len+1)*cm)
    ct_string = generate_string(q, hes_x, hes_y, hes_rot, radius/4)
    return compose(
        context(units=UnitBox(0, 0, x_len, y_len;
            leftpad=1.5cm, rightpad=1.5cm, toppad=1.5cm, bottompad=1.5cm)),
        show_string ? ct_string : context(),
        ct_genus,
        show_tait ? ct_vs : context(),
        show_tait ? ct_edges : context(),
        show_tait ? ct_fs : context(),
        bg
    )
end

# function generate_locations(q::Tait)
#     vs_boundary = [q.inputs; q.outputs; genuses(q)]
#     vs_proceeded

#     frontier = copy(q.inputs)
#     while !isempty(frontier)

#     end
# end

function generate_normalized_locations(q::Tait)
    x_min, x_max = (0.0, 1.0)
    y_min, y_max = (0.0, 1.0)
    for v in values(q.locations)
        v[1] < x_min && (x_min = v[1])
        v[1] > x_max && (x_max = v[1])
        v[2] < y_min && (y_min = v[2])
        v[2] > y_max && (y_max = v[2])
    end
    x_len = x_max - x_min
    y_len = y_max - y_min
    original_pos = q.locations
    normal_pos = Dict()
    for (k, v) in q.locations
        normal_pos[k] = ((v[1] - x_min), (v[2] - y_min))
    end
    return x_len, y_len, normal_pos
end

function generate_half_edges_info(q::Tait, normal_pos)
    es = Dict()
    bds = Dict()
    boundaries = [q.inputs; q.outputs]
    hes_label = Dict()
    hes_x = Dict()
    hes_y = Dict()
    hes_rot = Dict()

    for he_id in half_edges(q)
        s = src(q, he_id)
        d = dst(q, he_id)
        pos_s = normal_pos[s]
        pos_d = normal_pos[d]
        if s in boundaries || d in boundaries
            bds[he_id] = pos_s
        else
            es[he_id] = pos_s
        end
        hes_label[he_id] = "$he_id\n→"
        hes_x[he_id] = (pos_s[1]+pos_d[1])/2
        hes_y[he_id] = (pos_s[2]+pos_d[2])/2
        hes_rot[he_id] = angle((normal_pos[d][1]-normal_pos[s][1])+im*(normal_pos[d][2]-normal_pos[s][2]))
    end
    return es, bds, hes_label, hes_x, hes_y, hes_rot
end

function generate_ctrl_points(q::Tait, normal_pos, radius, start_hes)
    ctrl_points = Dict()
    for v in vertices(q)
        if !haskey(start_hes, v)
            hes = trace_vertex(q, v)
            n = length(hes)
            θ = -π/2
            pos_v = normal_pos[v]
            for i in 1:n
                he = hes[i]
                θ += 2π/n
                ctrl_points[he] = (pos_v[1] + cos(θ)*radius, pos_v[2] - sin(θ)*radius)
            end
        else
            start_he, θ = start_hes[v]
            hes_v = trace_vertex(q, v)
            n = length(hes_v)
            thetas = [2π*i/n for i = 1:n]
            start_id = findfirst(isequal(start_he), hes_v)
            thetas .+= θ - thetas[start_id]
            pos_v = normal_pos[v]
            for i = 1:n
                he = hes_v[i]
                ctrl_points[he] = (pos_v[1] + cos(thetas[i])*radius, pos_v[2] - sin(thetas[i])*radius)
            end
        end
    end
    return ctrl_points
end

function update_half_edge_info!(q::Tait, hes_x, hes_y, hes_rot, normal_pos, ctrl_points)
    for he in half_edges(q)
        hes_x[he] = hes_x[he]/4 + 3/8*(ctrl_points[he][1] + ctrl_points[twin(q, he)][1])
        hes_y[he] = hes_y[he]/4 + 3/8*(ctrl_points[he][2] + ctrl_points[twin(q, he)][2])
        s = src(q, he)
        d = dst(q, he)

        hes_rot[he] = angle(
            (normal_pos[d][1] + ctrl_points[twin(q, he)][1] - normal_pos[s][1] - ctrl_points[he][1])
                + im * (normal_pos[d][2] + ctrl_points[twin(q, he)][2] - normal_pos[s][2] - ctrl_points[he][2])
        )
    end
    return
end

function generate_context_edges(q::Tait, es, bds, hes_label, hes_x, hes_y, hes_rot, ctrl_points, show_half_edges)
    bd_hes = Set{Int}()
    for he in keys(bds)
        !(twin(q, he) in bd_hes) && push!(bd_hes, he)
    end
    bd_hes = sort(collect(bd_hes))
    inner_hes = Set{Int}()
    for he in keys(es)
        !(twin(q, he) in inner_hes) && push!(inner_hes, he)
    end
    inner_hes = sort(collect(inner_hes))
    ct_bds = length(bd_hes) > 0 ? (context(),
        curve([bds[he] for he in bd_hes],
            [ctrl_points[he] for he in bd_hes],
            [ctrl_points[twin(q, he)] for he in bd_hes],
            [bds[twin(q, he)] for he in bd_hes],
        ),
        stroke("gray"), linewidth(1mm)
    ) : context()
    ct_es = !isempty(inner_hes) ? (context(),
        (context(),
            curve([es[he] for he in inner_hes],
                [ctrl_points[he] for he in inner_hes],
                [ctrl_points[twin(q, he)] for he in inner_hes],
                [es[twin(q, he)] for he in inner_hes],
            ), stroke("white"), linewidth(0.6mm)),
        (context(),
            curve([es[he] for he in inner_hes],
                [ctrl_points[he] for he in inner_hes],
                [ctrl_points[twin(q, he)] for he in inner_hes],
                [es[twin(q, he)] for he in inner_hes],
            ), stroke("gray"), linewidth(1.2mm)),
    ) : context()
    rot = [Rotation(hes_rot[he], hes_x[he], hes_y[he]) for he in half_edges(q)]
    ct_hes = (context(),
        text([hes_x[he] for he in half_edges(q)], [hes_y[he] for he in half_edges(q)],
            [hes_label[he] for he in half_edges(q)], [hcenter], [vbottom], rot),
        fill("gray"),
        fontsize(8pt)
    )
    ct_edges = compose(context(),
        show_half_edges ? ct_hes : context(),
        ct_es,
        ct_bds
    )
    return ct_edges
end

function generate_context_vertices(q, normal_pos, show_tait)
    vs = vertices(q)
    vs_label = ["$v" for v in vs]
    vs_x = [normal_pos[v][1] for v in vs]
    vs_y = [normal_pos[v][2] for v in vs]
    ct_vs = context()
    ct_genus = context()
    for i in 1:length(vs)
        if is_isolated(q, vs[i])
            ct_v = compose(
                context(vs_x[i]-0.1, vs_y[i]-0.1, 0.2, 0.2),
                (
                    context(units = UnitBox(2,2)),
                    is_genus(q, vs[i]) ? (context(), arc([1], [0.8, 1.6], [0.8, 0.4], [0.5, pi+0.5], [pi-0.5, -0.5], [false]), fill("transparent"), stroke("royalblue"), linewidth(0.5mm)) : context(),
                    (context(), text(1, 1, vs_label[i], hcenter, is_genus(q, vs[i]) ? vbottom : vcenter), fill("red"), font("Helvetica-Bold")),
                    (context(), circle(1, 1, 1), is_open_vertex(q, vs[i]) ? fill("royalblue") : fill("white"),
                        (is_genus(q, vs[i]) && !show_tait) ? stroke("black") : stroke("royalblue"), linewidth(1)
                    )
                )
            )
        else
            ct_v = compose(
                context(vs_x[i]-0.15, vs_y[i]-0.15, 0.3, 0.3),
                (
                    context(units = UnitBox(2,2)),
                    is_genus(q, vs[i]) ? (context(), arc([1], [0.8, 1.6], [0.8, 0.4], [0.5, pi+0.5], [pi-0.5, -0.5], [false]), fill("transparent"), stroke("royalblue"), linewidth(0.5mm)) : context(),
                    (context(), text(1, 1, vs_label[i], hcenter, is_genus(q, vs[i]) ? vbottom : vcenter), fill("red"), font("Helvetica-Bold")),
                    (context(), circle(1, 1, 1), is_open_vertex(q, vs[i]) ? fill("royalblue") : fill("white"),
                        (is_genus(q, vs[i]) && !show_tait) ? stroke("white") : stroke("royalblue"), linewidth(0.5)
                    )
                )
            )
        end
        if is_genus(q, vs[i])
            ct_genus = compose(context(), ct_genus, ct_v)
        else
            ct_vs = compose(context(), ct_vs, ct_v)
        end
    end
    return ct_vs, ct_genus
end

function generate_context_faces(q::Tait, normal_pos, ctrl_points, show_faces)
    fs = faces(q)[2:end]
    fs_label = ["$f" for f in fs]
    fs_x = []
    fs_y = []
    ct_bezi = compose(context())
    for f in fs
        hes_f = trace_face(q, f)
        vs_f = [src(q, he) for he in hes_f]
        push!(fs_x, (sum([(ctrl_points[he][1] + ctrl_points[twin(q, he)][1])/2 for he in hes_f])*0.5 + sum([normal_pos[v][1] for v in vs_f])*0.5)/(length(vs_f)))
        push!(fs_y, (sum([(ctrl_points[he][2] + ctrl_points[twin(q, he)][2])/2 for he in hes_f])*0.5 + sum([normal_pos[v][2] for v in vs_f])*0.5)/(length(vs_f)))
        start_point = normal_pos[src(q, hes_f[1])]
        bz_ctrls = [[ctrl_points[he], ctrl_points[twin(q, he)], normal_pos[dst(q, he)]] for he in hes_f]
        ct_bezi_f = compose(context(),
            bezigon(start_point, bz_ctrls), fill("salmon"), fillopacity(0.3),
        )
        ct_bezi = compose(context(), ct_bezi, ct_bezi_f)
    end
    if length(fs_x) > 0
        ct_fs = (context(), text(fs_x, fs_y, fs_label, [hcenter], [vcenter]), fill("blue"), font("Helvetica-Bold"))
    else
        ct_fs = context()
    end
    return compose(context(),
        show_faces ? ct_fs : context(),
        show_faces ? ct_bezi : context(),
    )
end

function generate_string(q::Tait, hes_x, hes_y, hes_rot, radius)
    ct_string = compose(context())
    ct_phase = compose(context())
    for f in faces(q)
        f == 0 && continue
        he_start = surrounding_half_edge(q, f)
        he_curr = he_start
        he_next = next(q, he_curr)
        ct_radius = 3radius
        while true
            if !(is_open_vertex(q, dst(q, he_curr)) && is_open_vertex(q, src(q, he_next)))
                if is_open_vertex(q, src(q, he_curr))
                    pos_curr = (hes_x[he_curr] + radius*cos(hes_rot[he_curr]-π/2), hes_y[he_curr] + radius*sin(hes_rot[he_curr]-π/2))
                    ctrl_curr = (pos_curr[1] + ct_radius*cos(hes_rot[he_curr]), pos_curr[2] + ct_radius*sin(hes_rot[he_curr]))
                else
                    pos_curr = (hes_x[he_curr], hes_y[he_curr])
                    ctrl_curr = (pos_curr[1] + ct_radius*cos(hes_rot[he_curr] - π/4), pos_curr[2] + ct_radius*sin(hes_rot[he_curr] - π/4))
                end
                if is_open_vertex(q, dst(q, he_next))
                    pos_next = (hes_x[he_next] + radius*cos(hes_rot[he_next]-π/2), hes_y[he_next] + radius*sin(hes_rot[he_next]-π/2))
                    ctrl_next = (pos_next[1] + ct_radius*cos(hes_rot[he_next]-π), pos_next[2] + ct_radius*sin(hes_rot[he_next]-π))
                else
                    pos_next = (hes_x[he_next], hes_y[he_next])
                    ctrl_next = (pos_next[1] + ct_radius*cos(hes_rot[he_next] - 3π/4), pos_next[2] + ct_radius*sin(hes_rot[he_next] - 3π/4))
                end
                ct_string = compose(
                    ct_string,
                    (context(),
                    curve(
                        [pos_curr], [ctrl_curr],
                        [ctrl_next], [pos_next],
                    ),
                    stroke("black"), linewidth(1mm))
                )
            end
            he_curr = he_next
            he_next = next(q, he_curr)
            (he_curr == he_start) && break
        end
    end
    hes = Set(half_edges(q))
    while !isempty(hes)
        he = pop!(hes)
        if !is_open_vertex(q, src(q, he)) && !is_open_vertex(q, dst(q, he))
            θ = hes_rot[he]
            crx = generate_phase(hes_x[he], hes_y[he], θ, quon_param(q, he))
            ct_phase = compose(context(), ct_phase, crx)
        end
        delete!(hes, twin(q, he))
    end
    return compose(context(), ct_phase, ct_string)
end

function generate_phase(x, y, θ, p)
    if is_orth_half_pi(p)
        return compose(
            context(x-0.1, y-0.1, 0.2, 0.2),
            (context(units=UnitBox(-1, -1, 2, 2)),
                (context(), line([(cos(θ+π/4), sin(θ+π/4)), (cos(θ+5π/4), sin(θ+5π/4))]), stroke("black"), linewidth(1)),
                (context(), circle(0, 0, 1), fill("white"), stroke("black"))),
        )
    elseif is_para_half_pi(p)
        return compose(
            context(x-0.1, y-0.1, 0.2, 0.2),
            (context(units=UnitBox(-1, -1, 2, 2)),
                (context(), line([(cos(θ+3π/4), sin(θ+3π/4)), (cos(θ+7π/4), sin(θ+7π/4))]), stroke("black"), linewidth(1)),
                (context(), circle(0, 0, 1), fill("white"), stroke("black"))),
        )
    elseif is_para_pi(p)
        return compose(
            context(x-0.1, y-0.1, 0.2, 0.2),
            (context(units=UnitBox(-1, -1, 2, 2)),
                (context(), circle(cos(θ+π/2)/sqrt(2), sin(θ+π/2)/sqrt(2), 0.4), fill("black")),
                (context(), circle(cos(θ+3π/2)/sqrt(2), sin(θ+3π/2)/sqrt(2), 0.4), fill("black")),
                (context(), line([(cos(θ+π/4), sin(θ+π/4)), (cos(θ+3π/4), sin(θ+3π/4))]), stroke("black"), linewidth(1)),
                (context(), line([(cos(θ+7π/4), sin(θ+7π/4)), (cos(θ+5π/4), sin(θ+5π/4))]), stroke("black"), linewidth(1)),
                (context(), circle(0, 0, 1), fill("white"), stroke("black"))
            ),
        )
    elseif is_orth_pi(p)
        return compose(
            context(x-0.1, y-0.1, 0.2, 0.2),
            (context(units=UnitBox(-1, -1, 2, 2)),
                (context(), circle(cos(θ)/sqrt(2), sin(θ)/sqrt(2), 0.4), fill("black")),
                (context(), circle(cos(θ+π)/sqrt(2), sin(θ+π)/sqrt(2), 0.4), fill("black")),
                (context(), line([(cos(θ+3π/4), sin(θ+3π/4)), (cos(θ+5π/4), sin(θ+5π/4))]), stroke("black"), linewidth(1)),
                (context(), line([(cos(θ+7π/4), sin(θ+7π/4)), (cos(θ+π/4), sin(θ+π/4))]), stroke("black"), linewidth(1)),
                (context(), circle(0, 0, 1), fill("white"), stroke("black"))
            ),
        )
    elseif is_para_zero(p)
        return compose(
            context(x-0.1, y-0.1, 0.2, 0.2),
            (context(units=UnitBox(-1, -1, 2, 2)),
                (context(), line([(cos(θ+π/4), sin(θ+π/4)), (cos(θ+3π/4), sin(θ+3π/4))]), stroke("black"), linewidth(1)),
                (context(), line([(cos(θ+7π/4), sin(θ+7π/4)), (cos(θ+5π/4), sin(θ+5π/4))]), stroke("black"), linewidth(1)),
                (context(), circle(0, 0, 1), fill("white"), stroke("black"))
            ),
        )
    elseif is_orth_zero(p)
        return compose(
            context(x-0.1, y-0.1, 0.2, 0.2),
            (context(units=UnitBox(-1, -1, 2, 2)),
                (context(), line([(cos(θ+3π/4), sin(θ+3π/4)), (cos(θ+5π/4), sin(θ+5π/4))]), stroke("black"), linewidth(1)),
                (context(), line([(cos(θ+7π/4), sin(θ+7π/4)), (cos(θ+π/4), sin(θ+π/4))]), stroke("black"), linewidth(1)),
                (context(), circle(0, 0, 1), fill("white"), stroke("black"))
            ),
        )
    else
        return compose(
            context(x-0.1, y-0.1, 0.2, 0.2),
            (context(units=UnitBox(-1, -1, 2, 2)),
                (context(), text(0, 0, "$p", hcenter, vcenter, Rotation(θ, 0, 0)), fontsize(1.5)),
                (context(), circle(0, 0, 1), fill("white"), stroke("black"))
            ),
        )
    end
end


text:
pygui(true)

function useToroidDesignGUI()
    win = GtkWindow("Toroid Designer")
    g = GtkGrid()

    RowCnt = 1

    LTargDesc = GtkEntry()  # a widget for entering text
    set_gtk_property!(LTargDesc, :text, "Target Inductance [H]")
    makeDescBox(LTargDesc)
    LtargVal =GtkEntry()
    makeValBox(LtargVal,100e-6)
    g[1,RowCnt] = LTargDesc
    g[2,RowCnt] = LtargVal

    RowCnt +=1

    WireDiamDesc = GtkEntry()
    set_gtk_property!(WireDiamDesc, :text, "Wire Diameter [m]")
    makeDescBox(WireDiamDesc)
    WireDiamVal =GtkEntry()
    makeValBox(WireDiamVal,0.001)
    g[1,RowCnt] = WireDiamDesc
    g[2,RowCnt] = WireDiamVal
    RowCnt +=1

    WireFillDesc = GtkEntry()
    set_gtk_property!(WireFillDesc, :text, "Wire fill [0-1, UL]")
    makeDescBox(WireFillDesc)
    WireFillVal =GtkEntry()
    makeValBox(WireFillVal,1)
    g[1,RowCnt] = WireFillDesc
    g[2,RowCnt] = WireFillVal
RowCnt +=1

    NumLayersDesc = GtkEntry()
set_gtk_property!(NumLayersDesc, :text, "Number of layers")
makeDescBox(NumLayersDesc)
NumLayersVal =GtkEntry()
makeValBox(NumLayersVal,2)
g[1,RowCnt] = NumLayersDesc
g[2,RowCnt] = NumLayersVal
RowCnt +=1

AlphaDesc = GtkEntry()
set_gtk_property!(AlphaDesc, :text, "Aspect Ratio, α [UL]")
makeDescBox(AlphaDesc)
AlphaVal =GtkEntry()
makeValBox(AlphaVal,2)
g[1,RowCnt] = AlphaDesc
g[2,RowCnt] = AlphaVal
RowCnt +=1

Res_Box = GtkEntry()
set_gtk_property!(Res_Box, :text, " ---Results---")
makeDescBox(Res_Box)
g[1:2,RowCnt] = Res_Box
RowCnt +=1

ID_Desc = GtkEntry()
set_gtk_property!(ID_Desc, :text, "Toroid Inner Diameter [meters]")
makeDescBox(ID_Desc)
ID_Val =GtkEntry()
makeResultsBox(ID_Val,1)
g[1,RowCnt] = ID_Desc
g[2,RowCnt] = ID_Val
RowCnt +=1

OD_Desc = GtkEntry()
set_gtk_property!(OD_Desc, :text, "Toroid Outer Diameter [meters]")
makeDescBox(OD_Desc)
OD_Val =GtkEntry()
makeResultsBox(OD_Val,1)
g[1,RowCnt] = OD_Desc
g[2,RowCnt] = OD_Val
RowCnt +=1

N_Desc = GtkEntry()
set_gtk_property!(N_Desc, :text, "Turns")
makeDescBox(N_Desc)
N_Val =GtkEntry()
makeResultsBox(N_Val,1)
g[1,RowCnt] = N_Desc
g[2,RowCnt] = N_Val
RowCnt +=1

WireL_Desc = GtkEntry()
set_gtk_property!(WireL_Desc, :text, "Wire length [meters]")
makeDescBox(WireL_Desc)
WireL_Val =GtkEntry()
makeResultsBox(WireL_Val,1)
g[1,RowCnt] = WireL_Desc
g[2,RowCnt] = WireL_Val
RowCnt +=1

WireR_Desc = GtkEntry()
set_gtk_property!(WireR_Desc, :text, "Wire resistance [Ω]")
makeDescBox(WireR_Desc)
WireR_Val =GtkEntry()
makeResultsBox(WireR_Val,1)
g[1,RowCnt] = WireR_Desc
g[2,RowCnt] = WireR_Val
RowCnt +=1

CoreShapeDesc = GtkEntry()
set_gtk_property!(CoreShapeDesc, :text, "Core Shape")
makeDescBox(CoreShapeDesc)
g[1,RowCnt] = CoreShapeDesc

Shape_cb = GtkComboBoxText()
choices = ["D-Shaped", "Circular"]
for choice in choices
  push!(Shape_cb,choice)
end
g[2,RowCnt] = Shape_cb
RowCnt +=1

FlatH_Desc = GtkEntry()
set_gtk_property!(FlatH_Desc, :text, "Flat height [meters]")
makeDescBox(FlatH_Desc)
FlatH_Val =GtkEntry()
makeResultsBox(FlatH_Val,1)
g[1,RowCnt] = FlatH_Desc
g[2,RowCnt] = FlatH_Val
RowCnt +=1

MaxH_Desc = GtkEntry()
set_gtk_property!(MaxH_Desc, :text, "Max height [meters]")
makeDescBox(MaxH_Desc)
MaxH_Val =GtkEntry()
makeResultsBox(MaxH_Val,1)
g[1,RowCnt] = MaxH_Desc
g[2,RowCnt] = MaxH_Val
RowCnt +=1

RadAtPeak_Desc = GtkEntry()
set_gtk_property!(RadAtPeak_Desc, :text, "Radius at peak [meters]")
makeDescBox(RadAtPeak_Desc)
RadAtPeak_Val =GtkEntry()
makeResultsBox(RadAtPeak_Val,1)
g[1,RowCnt] = RadAtPeak_Desc
g[2,RowCnt] = RadAtPeak_Val
RowCnt +=1

RunScriptButton = GtkButton()
set_gtk_property!(RunScriptButton, :label, "RUN DESIGNER")
g[1:2,RowCnt] = RunScriptButton
RowCnt +=1

set_gtk_property!(Shape_cb,:active,1)

id = signal_connect(RunScriptButton, "button-press-event") do widget, event
    # println("Starting toroid designer")

    CoilGeom =ToroidOptimizer(
        getVal(WireDiamVal),
        getVal(LtargVal);
    NumLayers = Int(getVal(NumLayersVal)),
    CoreMu = 1,
    Alpha = Int(getVal(AlphaVal)),
    CuFillFactor = getVal(WireFillVal))
    if parse(Int,get_gtk_property(Shape_cb,:active,String))==0
        set_gtk_property!(ID_Val, :text, "$(CoilGeom.DCore.ID)")
        set_gtk_property!(OD_Val, :text, "$(CoilGeom.DCore.OD)")
        set_gtk_property!(MaxH_Val, :text, "$(CoilGeom.DCore.MaxHeight)")
        set_gtk_property!(RadAtPeak_Val, :text, "$(CoilGeom.DCore.RadiusAtPeak)")
        set_gtk_property!(FlatH_Val, :text, "$(CoilGeom.DCore.FlatHeight)")

        set_gtk_property!(N_Val, :text, "$(CoilGeom.DCore.Turns)")
        set_gtk_property!(WireL_Val, :text, "$(CoilGeom.DCore.WireLength)")
        set_gtk_property!(WireR_Val, :text, "$(CoilGeom.DCore.Resistance)")
    else
        set_gtk_property!(ID_Val, :text, "$(CoilGeom.Circ.ID)")
        set_gtk_property!(OD_Val, :text, "$(CoilGeom.Circ.OD)")

        set_gtk_property!(RadAtPeak_Val, :text, "N/A")
        set_gtk_property!(MaxH_Val, :text, "N/A")
        set_gtk_property!(FlatH_Val, :text, "N/A")

        set_gtk_property!(N_Val, :text, "$(CoilGeom.Circ.Turns)")
        set_gtk_property!(WireL_Val, :text, "$(CoilGeom.Circ.WireLength)")
        set_gtk_property!(WireR_Val, :text, "$(CoilGeom.Circ.Resistance)")
    end

end

set_gtk_property!(g, :column_homogeneous, true)
set_gtk_property!(g, :column_spacing, 15)  # introduce a 15-pixel gap between columns
push!(win, g)
showall(win)

end

function makeResultsBox(Box,DefaultVal)

    set_gtk_property!(Box, :sensitive,false)
    set_gtk_property!(Box, :editable,false)
    set_gtk_property!(Box, :text, "$DefaultVal")
end


text:
# function to calcule the force between a pair of particles
# Exercise 39_40
using simulationQP934

#inputs
data = Data();
box  = initial_point(data);
particle1 = box[1];
particle2 = box[2];

function fpair(x,y,r,data)

  # r values for F calculation
  r6  = r^6
  r7  = r6 * r
  r12 = r6^2
  r13 = r12 * r

  # x and y components
  # Fz = -( dU(r) / dz) = - (dr/dz) * (dU(r)/dr), r = sqrt(dx^2,dy^2)
  # calculation for -(dr/dr)

  drdx1 = -(x[1]-y[1])/r
  drdx2 = -(x[2]-y[2])/r

  dudr1 = -12*(data.sig12/r13)
  dudr2 = -6*(data.sig6/r7)

  dfacdr =  data.eps4*(dudr1 - dudr2)

  upair = data.eps4*(data.sig12/r12 - data.sig6/r6)
  f = ( -dfacdr*drdx1, -dfacdr*drdx2 )

  return upair, f

end

r = pbcseparation(particle1,particle2,data.side)

f12 = fpair(particle1,particle2,r,data)

f21 = fpair(particle2,particle1,r,data)


text:
function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    ::DeviceModel{T, FixedOutput},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractActivePowerModel}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    nodal_expression!(optimization_container, devices, S)

    return
end

"""
This function creates the model for a full thermal dispatch formulation depending on combination of devices, device_formulation and system_formulation
"""
function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, D},
    ::Type{S},
) where {
    T <: PSY.ThermalGen,
    D <: AbstractStandardUnitCommitment,
    S <: PM.AbstractPowerModel,
}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(optimization_container, ActivePowerVariable, devices, D())
    add_variables!(optimization_container, ReactivePowerVariable, devices, D())
    add_variables!(optimization_container, OnVariable, devices, D())
    add_variables!(optimization_container, StartVariable, devices, D())
    add_variables!(optimization_container, StopVariable, devices, D())

    # Initial Conditions
    initial_conditions!(optimization_container, devices, D())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ReactivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    time_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

"""
This function creates the model for a full thermal dispatch formulation depending on combination of devices, device_formulation and system_formulation
"""
function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, D},
    ::Type{S},
) where {
    T <: PSY.ThermalGen,
    D <: AbstractStandardUnitCommitment,
    S <: PM.AbstractActivePowerModel,
}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(optimization_container, ActivePowerVariable, devices, D())
    add_variables!(optimization_container, OnVariable, devices, D())
    add_variables!(optimization_container, StartVariable, devices, D())
    add_variables!(optimization_container, StopVariable, devices, D())

    # Initial Conditions
    initial_conditions!(optimization_container, devices, D())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    time_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

"""
This function creates the model for a full thermal dispatch formulation depending on combination of devices, device_formulation and system_formulation
"""
function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalBasicUnitCommitment},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractPowerModel}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        ReactivePowerVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        OnVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StartVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StopVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalBasicUnitCommitment())

    # Constraints
    # TODO: active_power_constraints
    # TODO: refactor constraints such that ALL variables for all devices are added first, and then the constraint creation is trigged
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ReactivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

"""
This function creates the model for a full thermal dispatch formulation depending on combination of devices, device_formulation and system_formulation
"""
function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalBasicUnitCommitment},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractActivePowerModel}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        OnVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StartVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StopVariable,
        devices,
        ThermalBasicUnitCommitment(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalBasicUnitCommitment())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

"""
This function creates the model for a full thermal dispatch formulation depending on combination of devices, device_formulation and system_formulation
"""
function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalRampLimited},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractPowerModel}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalRampLimited(),
    )
    add_variables!(
        optimization_container,
        ReactivePowerVariable,
        devices,
        ThermalRampLimited(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalRampLimited())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ReactivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

"""
This function creates the model for a full thermal dispatch formulation depending on combination of devices, device_formulation and system_formulation
"""
function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalRampLimited},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractActivePowerModel}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalRampLimited(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalRampLimited())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, D},
    ::Type{S},
) where {
    T <: PSY.ThermalGen,
    D <: AbstractThermalDispatchFormulation,
    S <: PM.AbstractPowerModel,
}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(optimization_container, ActivePowerVariable, devices, D())
    add_variables!(optimization_container, ReactivePowerVariable, devices, D())

    # Initial Conditions

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ReactivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, D},
    ::Type{S},
) where {
    T <: PSY.ThermalGen,
    D <: AbstractThermalDispatchFormulation,
    S <: PM.AbstractActivePowerModel,
}
    devices = get_available_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(optimization_container, ActivePowerVariable, devices, D())

    # Initial Conditions

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    feedforward!(optimization_container, devices, model, get_feedforward(model))

    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{PSY.ThermalMultiStart, ThermalMultiStartUnitCommitment},
    ::Type{S};
    kwargs...,
) where {S <: PM.AbstractPowerModel}
    devices = PSY.get_components(PSY.ThermalMultiStart, sys)

    if !validate_available_devices(PSY.ThermalMultiStart, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        ReactivePowerVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        OnVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StopVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        ColdStartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        WarmStartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        HotStartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalMultiStartUnitCommitment())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ReactivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    time_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    startup_time_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    startup_type_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    startup_initial_condition_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    must_run_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    initial_range_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    feedforward!(optimization_container, devices, model, get_feedforward(model))
    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{PSY.ThermalMultiStart, ThermalMultiStartUnitCommitment},
    ::Type{S},
) where {S <: PM.AbstractActivePowerModel}
    devices = PSY.get_components(PSY.ThermalMultiStart, sys)

    if !validate_available_devices(PSY.ThermalMultiStart, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        OnVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StopVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        ColdStartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        WarmStartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        HotStartVariable,
        devices,
        ThermalMultiStartUnitCommitment(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalMultiStartUnitCommitment())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    time_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    startup_time_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    startup_type_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    startup_initial_condition_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    must_run_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    initial_range_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    feedforward!(optimization_container, devices, model, get_feedforward(model))
    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalCompactUnitCommitment},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractPowerModel}
    devices = PSY.get_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        ReactivePowerVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        OnVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StartVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StopVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalCompactUnitCommitment())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ReactivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    time_constraints!(optimization_container, devices, model, S, get_feedforward(model))

    feedforward!(optimization_container, devices, model, get_feedforward(model))
    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalCompactUnitCommitment},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractActivePowerModel}
    devices = PSY.get_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        OnVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StartVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )
    add_variables!(
        optimization_container,
        StopVariable,
        devices,
        ThermalCompactUnitCommitment(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalCompactUnitCommitment())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    commitment_constraints!(
        optimization_container,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    time_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    feedforward!(optimization_container, devices, model, get_feedforward(model))
    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalCompactDispatch},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractPowerModel}
    devices = PSY.get_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalCompactDispatch(),
    )
    add_variables!(
        optimization_container,
        ReactivePowerVariable,
        devices,
        ThermalCompactDispatch(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalCompactDispatch())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ReactivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    feedforward!(optimization_container, devices, model, get_feedforward(model))
    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end

function construct_device!(
    optimization_container::OptimizationContainer,
    sys::PSY.System,
    model::DeviceModel{T, ThermalCompactDispatch},
    ::Type{S},
) where {T <: PSY.ThermalGen, S <: PM.AbstractActivePowerModel}
    devices = PSY.get_components(T, sys)

    if !validate_available_devices(T, devices)
        return
    end

    # Variables
    add_variables!(
        optimization_container,
        ActivePowerVariable,
        devices,
        ThermalCompactDispatch(),
    )

    # Initial Conditions
    initial_conditions!(optimization_container, devices, ThermalCompactDispatch())

    # Constraints
    add_constraints!(
        optimization_container,
        RangeConstraint,
        ActivePowerVariable,
        devices,
        model,
        S,
        get_feedforward(model),
    )
    ramp_constraints!(optimization_container, devices, model, S, get_feedforward(model))
    feedforward!(optimization_container, devices, model, get_feedforward(model))
    # Cost Function
    cost_function!(optimization_container, devices, model, S, get_feedforward(model))

    return
end


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

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

Creates a resource group with the specified name and description. You can optionally
include a resource query, or a service configuration. For more information about
constructing a resource query, see Create a tag-based group in Resource Groups. For more
information about service configurations, see Service configurations for resource groups.
Minimum permissions  To run this command, you must have the following permissions:
resource-groups:CreateGroup

# Arguments
- `name`: The name of the group, which is the identifier of the group in other operations.
  You can't change the name of a resource group after you create it. A resource group name
  can consist of letters, numbers, hyphens, periods, and underscores. The name cannot start
  with AWS or aws; these are reserved. A resource group name must be unique within each AWS
  Region in your AWS account.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Configuration"`: A configuration associates the resource group with an AWS service and
  specifies how the service can interact with the resources in the group. A configuration is
  an array of GroupConfigurationItem elements. For details about the syntax of service
  configurations, see Service configurations for resource groups.  A resource group can
  contain either a Configuration or a ResourceQuery, but not both.
- `"Description"`: The description of the resource group. Descriptions can consist of
  letters, numbers, hyphens, underscores, periods, and spaces.
- `"ResourceQuery"`: The resource query that determines which AWS resources are members of
  this group. For more information about resource queries, see Create a tag-based group in
  Resource Groups.   A resource group can contain either a ResourceQuery or a Configuration,
  but not both.
- `"Tags"`: The tags to add to the group. A tag is key-value pair string.
"""
function create_group(Name; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST",
        "/groups",
        Dict{String,Any}("Name" => Name);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function create_group(
    Name, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/groups",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Name" => Name), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes the specified resource group. Deleting a resource group does not delete any
resources that are members of the group; it only deletes the group structure.  Minimum
permissions  To run this command, you must have the following permissions:
resource-groups:DeleteGroup

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Group"`: The name or the ARN of the resource group to delete.
- `"GroupName"`: Deprecated - don't use this parameter. Use Group instead.
"""
function delete_group(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST", "/delete-group"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function delete_group(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/delete-group",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns information about a specified resource group.  Minimum permissions  To run this
command, you must have the following permissions:    resource-groups:GetGroup

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Group"`: The name or the ARN of the resource group to retrieve.
- `"GroupName"`: Deprecated - don't use this parameter. Use Group instead.
"""
function get_group(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST", "/get-group"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function get_group(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST", "/get-group", params; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end

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

Returns the service configuration associated with the specified resource group. For details
about the service configuration syntax, see Service configurations for resource groups.
Minimum permissions  To run this command, you must have the following permissions:
resource-groups:GetGroupConfiguration

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Group"`: The name or the ARN of the resource group.
"""
function get_group_configuration(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST",
        "/get-group-configuration";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_group_configuration(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/get-group-configuration",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Retrieves the resource query associated with the specified resource group. For more
information about resource queries, see Create a tag-based group in Resource Groups.
Minimum permissions  To run this command, you must have the following permissions:
resource-groups:GetGroupQuery

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Group"`: The name or the ARN of the resource group to query.
- `"GroupName"`: Don't use this parameter. Use Group instead.
"""
function get_group_query(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST", "/get-group-query"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function get_group_query(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/get-group-query",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns a list of tags that are associated with a resource group, specified by an ARN.
Minimum permissions  To run this command, you must have the following permissions:
resource-groups:GetTags

# Arguments
- `arn`: The ARN of the resource group whose tags you want to retrieve.

"""
function get_tags(Arn; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "GET",
        "/resources/$(Arn)/tags";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function get_tags(
    Arn, params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "GET",
        "/resources/$(Arn)/tags",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    group_resources(group, resource_arns)
    group_resources(group, resource_arns, params::Dict{String,<:Any})

Adds the specified resources to the specified group.  Minimum permissions  To run this
command, you must have the following permissions:    resource-groups:GroupResources

# Arguments
- `group`: The name or the ARN of the resource group to add resources to.
- `resource_arns`: The list of ARNs for resources to be added to the group.

"""
function group_resources(
    Group, ResourceArns; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/group-resources",
        Dict{String,Any}("Group" => Group, "ResourceArns" => ResourceArns);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function group_resources(
    Group,
    ResourceArns,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return resource_groups(
        "POST",
        "/group-resources",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("Group" => Group, "ResourceArns" => ResourceArns),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns a list of ARNs of the resources that are members of a specified resource group.
Minimum permissions  To run this command, you must have the following permissions:
resource-groups:ListGroupResources     cloudformation:DescribeStacks
cloudformation:ListStackResources     tag:GetResources

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filters"`: Filters, formatted as ResourceFilter objects, that you want to apply to a
  ListGroupResources operation. Filters the results to include only those of the specified
  resource types.    resource-type - Filter resources by their type. Specify up to five
  resource types in the format AWS::ServiceCode::ResourceType. For example,
  AWS::EC2::Instance, or AWS::S3::Bucket.    When you specify a resource-type filter for
  ListGroupResources, AWS Resource Groups validates your filter resource types against the
  types that are defined in the query associated with the group. For example, if a group
  contains only S3 buckets because its query specifies only that resource type, but your
  resource-type filter includes EC2 instances, AWS Resource Groups does not filter for EC2
  instances. In this case, a ListGroupResources request returns a BadRequestException error
  with a message similar to the following:  The resource types specified as filters in the
  request are not valid.  The error includes a list of resource types that failed the
  validation because they are not part of the query associated with the group. This
  validation doesn't occur when the group query specifies AWS::AllSupported, because a group
  based on such a query can contain any of the allowed resource types for the query type
  (tag-based or AWS CloudFormation stack-based queries).
- `"Group"`: The name or the ARN of the resource group
- `"GroupName"`:    Deprecated - don't use this parameter. Use the Group request field
  instead.
- `"MaxResults"`: The total number of results that you want included on each page of the
  response. If you do not include this parameter, it defaults to a value that is specific to
  the operation. If additional items exist beyond the maximum you specify, the NextToken
  response element is present and has a value (is not null). Include that value as the
  NextToken request parameter in the next call to the operation to get the next part of the
  results. Note that the service might return fewer results than the maximum even when there
  are more results available. You should check NextToken after every operation to ensure that
  you receive all of the results.
- `"NextToken"`: The parameter for receiving additional results if you receive a NextToken
  response in a previous request. A NextToken response indicates that more output is
  available. Set this parameter to the value provided by a previous call's NextToken response
  to indicate where the output should continue from.
"""
function list_group_resources(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST",
        "/list-group-resources";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function list_group_resources(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/list-group-resources",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns a list of existing resource groups in your account.  Minimum permissions  To run
this command, you must have the following permissions:    resource-groups:ListGroups

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Filters"`: Filters, formatted as GroupFilter objects, that you want to apply to a
  ListGroups operation.    resource-type - Filter the results to include only those of the
  specified resource types. Specify up to five resource types in the format
  AWS::ServiceCode::ResourceType . For example, AWS::EC2::Instance, or AWS::S3::Bucket.
  configuration-type - Filter the results to include only those groups that have the
  specified configuration types attached. The current supported values are:
  AWS:EC2::CapacityReservationPool     AWS:EC2::HostManagement
- `"maxResults"`: The total number of results that you want included on each page of the
  response. If you do not include this parameter, it defaults to a value that is specific to
  the operation. If additional items exist beyond the maximum you specify, the NextToken
  response element is present and has a value (is not null). Include that value as the
  NextToken request parameter in the next call to the operation to get the next part of the
  results. Note that the service might return fewer results than the maximum even when there
  are more results available. You should check NextToken after every operation to ensure that
  you receive all of the results.
- `"nextToken"`: The parameter for receiving additional results if you receive a NextToken
  response in a previous request. A NextToken response indicates that more output is
  available. Set this parameter to the value provided by a previous call's NextToken response
  to indicate where the output should continue from.
"""
function list_groups(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST", "/groups-list"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function list_groups(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/groups-list",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Attaches a service configuration to the specified group. This occurs asynchronously, and
can take time to complete. You can use GetGroupConfiguration to check the status of the
update.  Minimum permissions  To run this command, you must have the following permissions:
   resource-groups:PutGroupConfiguration

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Configuration"`: The new configuration to associate with the specified group. A
  configuration associates the resource group with an AWS service and specifies how the
  service can interact with the resources in the group. A configuration is an array of
  GroupConfigurationItem elements. For information about the syntax of a service
  configuration, see Service configurations for resource groups.  A resource group can
  contain either a Configuration or a ResourceQuery, but not both.
- `"Group"`: The name or ARN of the resource group with the configuration that you want to
  update.
"""
function put_group_configuration(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST",
        "/put-group-configuration";
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function put_group_configuration(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/put-group-configuration",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Returns a list of AWS resource identifiers that matches the specified query. The query uses
the same format as a resource query in a CreateGroup or UpdateGroupQuery operation.
Minimum permissions  To run this command, you must have the following permissions:
resource-groups:SearchResources     cloudformation:DescribeStacks
cloudformation:ListStackResources     tag:GetResources

# Arguments
- `resource_query`: The search query, using the same formats that are supported for
  resource group definition. For more information, see CreateGroup.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"MaxResults"`: The total number of results that you want included on each page of the
  response. If you do not include this parameter, it defaults to a value that is specific to
  the operation. If additional items exist beyond the maximum you specify, the NextToken
  response element is present and has a value (is not null). Include that value as the
  NextToken request parameter in the next call to the operation to get the next part of the
  results. Note that the service might return fewer results than the maximum even when there
  are more results available. You should check NextToken after every operation to ensure that
  you receive all of the results.
- `"NextToken"`: The parameter for receiving additional results if you receive a NextToken
  response in a previous request. A NextToken response indicates that more output is
  available. Set this parameter to the value provided by a previous call's NextToken response
  to indicate where the output should continue from.
"""
function search_resources(ResourceQuery; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST",
        "/resources/search",
        Dict{String,Any}("ResourceQuery" => ResourceQuery);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function search_resources(
    ResourceQuery,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return resource_groups(
        "POST",
        "/resources/search",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceQuery" => ResourceQuery), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Adds tags to a resource group with the specified ARN. Existing tags on a resource group are
not changed if they are not specified in the request parameters.  Do not store personally
identifiable information (PII) or other confidential or sensitive information in tags. We
use tags to provide you with billing and administration services. Tags are not intended to
be used for private or sensitive data.   Minimum permissions  To run this command, you must
have the following permissions:    resource-groups:Tag

# Arguments
- `arn`: The ARN of the resource group to which to add tags.
- `tags`: The tags to add to the specified resource group. A tag is a string-to-string map
  of key-value pairs.

"""
function tag(Arn, Tags; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "PUT",
        "/resources/$(Arn)/tags",
        Dict{String,Any}("Tags" => Tags);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function tag(
    Arn,
    Tags,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return resource_groups(
        "PUT",
        "/resources/$(Arn)/tags",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Tags" => Tags), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

"""
    ungroup_resources(group, resource_arns)
    ungroup_resources(group, resource_arns, params::Dict{String,<:Any})

Removes the specified resources from the specified group.  Minimum permissions  To run this
command, you must have the following permissions:    resource-groups:UngroupResources

# Arguments
- `group`: The name or the ARN of the resource group from which to remove the resources.
- `resource_arns`: The ARNs of the resources to be removed from the group.

"""
function ungroup_resources(
    Group, ResourceArns; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/ungroup-resources",
        Dict{String,Any}("Group" => Group, "ResourceArns" => ResourceArns);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function ungroup_resources(
    Group,
    ResourceArns,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return resource_groups(
        "POST",
        "/ungroup-resources",
        Dict{String,Any}(
            mergewith(
                _merge,
                Dict{String,Any}("Group" => Group, "ResourceArns" => ResourceArns),
                params,
            ),
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Deletes tags from a specified resource group.  Minimum permissions  To run this command,
you must have the following permissions:    resource-groups:Untag

# Arguments
- `arn`: The ARN of the resource group from which to remove tags. The command removed both
  the specified keys and any values associated with those keys.
- `keys`: The keys of the tags to be removed.

"""
function untag(Arn, Keys; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "PATCH",
        "/resources/$(Arn)/tags",
        Dict{String,Any}("Keys" => Keys);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function untag(
    Arn,
    Keys,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return resource_groups(
        "PATCH",
        "/resources/$(Arn)/tags",
        Dict{String,Any}(mergewith(_merge, Dict{String,Any}("Keys" => Keys), params));
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Updates the description for an existing group. You cannot update the name of a resource
group.  Minimum permissions  To run this command, you must have the following permissions:
  resource-groups:UpdateGroup

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Description"`: The new description that you want to update the resource group with.
  Descriptions can contain letters, numbers, hyphens, underscores, periods, and spaces.
- `"Group"`: The name or the ARN of the resource group to modify.
- `"GroupName"`: Don't use this parameter. Use Group instead.
"""
function update_group(; aws_config::AbstractAWSConfig=global_aws_config())
    return resource_groups(
        "POST", "/update-group"; aws_config=aws_config, feature_set=SERVICE_FEATURE_SET
    )
end
function update_group(
    params::AbstractDict{String}; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/update-group",
        params;
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end

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

Updates the resource query of a group. For more information about resource queries, see
Create a tag-based group in Resource Groups.  Minimum permissions  To run this command, you
must have the following permissions:    resource-groups:UpdateGroupQuery

# Arguments
- `resource_query`: The resource query to determine which AWS resources are members of this
  resource group.  A resource group can contain either a Configuration or a ResourceQuery,
  but not both.

# Optional Parameters
Optional parameters can be passed as a `params::Dict{String,<:Any}`. Valid keys are:
- `"Group"`: The name or the ARN of the resource group to query.
- `"GroupName"`: Don't use this parameter. Use Group instead.
"""
function update_group_query(
    ResourceQuery; aws_config::AbstractAWSConfig=global_aws_config()
)
    return resource_groups(
        "POST",
        "/update-group-query",
        Dict{String,Any}("ResourceQuery" => ResourceQuery);
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end
function update_group_query(
    ResourceQuery,
    params::AbstractDict{String};
    aws_config::AbstractAWSConfig=global_aws_config(),
)
    return resource_groups(
        "POST",
        "/update-group-query",
        Dict{String,Any}(
            mergewith(_merge, Dict{String,Any}("ResourceQuery" => ResourceQuery), params)
        );
        aws_config=aws_config,
        feature_set=SERVICE_FEATURE_SET,
    )
end


text:
#!/usr/bin/env julia

#=
 Licensing Information: You are free to use or extend these projects for
 education or reserach purposes provided that (1) you retain this notice
 and (2) you provide clear attribution to UC Berkeley, including a link
 to http://barc-project.com

 Attibution Information: The barc project ROS code-base was developed
 at UC Berkeley in the Model Predictive Control (MPC) lab by Jon Gonzales
 (jon.gonzales@berkeley.edu). The cloud services integation with ROS was developed
 by Kiet Lam  (kiet.lam@berkeley.edu). The web-server app Dator was
 based on an open source project by Bruce Wootton
=#

using RobotOS
@rosimport barc.msg: ECU, Vel_est, pos_info
@rosimport geometry_msgs.msg: Vector3
@rosimport sensor_msgs.msg: Imu, NavSatFix
# @rosimport marvelmind_nav.msg: hedge_pos
@rosimport std_msgs.msg: Header
rostypegen()
using barc.msg
using geometry_msgs.msg
using sensor_msgs.msg
using std_msgs.msg
using marvelmind_nav.msg
using JLD

include("barc_lib/classes.jl")
include("barc_lib/simModel.jl")

# This type contains measurement data (time, values and a counter)
type Measurements{T}
    i::Int64          # measurement counter
    t::Array{Float64}       # time data
    t_msg::Array{Float64}
    z::Array{T}       # measurement values
end

# This function cleans the zeros from the type above once the simulation is finished
function clean_up(m::Measurements)
    m.t     = m.t[1:m.i-1]
    m.t_msg = m.t_msg[1:m.i-1]
    m.z     = m.z[1:m.i-1,:]
end

function ECU_callback(msg::ECU,u_current::Array{Float64},cmd_log::Measurements)
    u_current[:] = convert(Array{Float64,1},[msg.motor, msg.servo])
    cmd_log.t_msg[cmd_log.i]    = to_sec(get_rostime())
    cmd_log.t[cmd_log.i]        = to_sec(get_rostime())
    cmd_log.z[cmd_log.i,:]      = u_current
    cmd_log.i += 1
end

function main()
    u_current = zeros(Float64,2)      # msg ECU is Float32 !

    buffersize = 60000
    gps_meas = Measurements{Float64}(1,zeros(buffersize),zeros(buffersize),zeros(buffersize,2))
    imu_meas = Measurements{Float64}(1,zeros(buffersize),zeros(buffersize),zeros(buffersize,2))
    cmd_log  = Measurements{Float64}(1,ones(buffersize)*Inf,ones(buffersize)*Inf,zeros(buffersize,2))
    z_real   = Measurements{Float64}(1,zeros(buffersize),zeros(buffersize),zeros(buffersize,8))
    slip_a   = Measurements{Float64}(1,zeros(buffersize),zeros(buffersize),zeros(buffersize,2))

    # initiate node, set up publisher / subscriber topics
    init_node("barc_sim")
    pub_gps = Publisher("fix", hedge_pos, queue_size=1)::RobotOS.Publisher{marvelmind_nav.msg.hedge_pos}
    pub_imu = Publisher("imu/data", Imu, queue_size=1)::RobotOS.Publisher{sensor_msgs.msg.Imu}
    pub_vel = Publisher("vel_est", Vel_est, queue_size=1)::RobotOS.Publisher{barc.msg.Vel_est}
    real_val = Publisher("real_val", pos_info, queue_size=1)::RobotOS.Publisher{barc.msg.pos_info}

    s1  = Subscriber("ecu", ECU, ECU_callback, (u_current,cmd_log,), queue_size=1)::RobotOS.Subscriber{barc.msg.ECU}

    z_current = zeros(buffersize,8)
    z_current[1,:] = [0.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0]
    slip_ang = zeros(buffersize,2)

    dt = 0.01
    loop_rate = Rate(1/dt)

    i = 2

    dist_traveled = randn(3)        # encoder positions of three wheels
    last_updated  = 0.0

    r_tire      = 0.05             # radius from tire center to perimeter along magnets [m]

    imu_drift = 0.0       # simulates yaw-sensor drift over time (slow sine)

    modelParams     = ModelParams()
    run_id          = get_param("run_id")
    # modelParams.l_A = copy(get_param("L_a"))      # always throws segmentation faults *after* execution!!! ??
    # modelParams.l_B = copy(get_param("L_a"))
    # modelParams.m   = copy(get_param("m"))
    # modelParams.I_z = copy(get_param("I_z"))

    modelParams.l_A = 0.18
    modelParams.l_B = 0.14
    modelParams.m   = 1.98
    modelParams.I_z = 0.03#0.24             # using homogenous distributed mass over a cuboid

    println("Publishing sensor information. Simulator running.")
    imu_data    = Imu()
    vel_est     = Vel_est()
    t0          = to_sec(get_rostime())
    gps_data    = hedge_pos()
    real_data   = pos_info()

    z_real.t_msg[1] = t0
    slip_a.t_msg[1] = t0
    z_real.t[1]     = t0
    slip_a.t[1]     = t0
    t               = 0.0

    sim_gps_interrupt   = 0                 # counter if gps interruption is simulated
    vel_dist_update     = 2*pi*r_tire/8      # distance to travel until velocity is updated (1/8 wheel rotation)

    gps_header = Header()
    while ! is_shutdown()
        t_ros   = get_rostime()
        t       = to_sec(t_ros)
        # if sizeof(cmd_log.z[t.>cmd_log.t+0.2,2]) >= 1
        #    u_current[2] = cmd_log.z[t.>=cmd_log.t+0.2,2][end]       # artificial steering input delay
        # end
        # update current state with a new row vector
        z_current[i,:],slip_ang[i,:]  = simDynModel_exact_xy(z_current[i-1,:], u_current', dt, modelParams)

        z_real.t_msg[i] = t
        z_real.t[i]     = t
        slip_a.t_msg[i] = t
        slip_a.t[i]     = t

        # IMU measurements
        if i%2 == 0                 # 50 Hz
            imu_drift   = 1+(t-t0)/100#sin(t/100*pi/2)     # drifts to 1 in 100 seconds (and add random start value 1)
            rand_yaw = 0.05*randn()
            if rand_yaw > 0.1
                rand_yaw = 0.1
            elseif rand_yaw<-0.1
                rand_yaw=-0.1
            end

            yaw         = z_current[i,5] + imu_drift + rand_yaw#+ 0.002*randn()

            rand_psiDot = 0.01*randn()
            if rand_psiDot > 0.1
                rand_psiDot = 0.1
            elseif rand_psiDot<-0.1
                rand_psiDot=-0.1
            end
            psiDot      = z_current[i,6] +rand_psiDot#+ 0.001*randn()
            imu_meas.t_msg[imu_meas.i] = t
            imu_meas.t[imu_meas.i] = t
            imu_meas.z[imu_meas.i,:] = [yaw psiDot]
            imu_meas.i += 1
            imu_data.orientation = geometry_msgs.msg.Quaternion(cos(yaw/2), sin(yaw/2), 0, 0)
            imu_data.angular_velocity = Vector3(0,0,psiDot)
            imu_data.header.stamp = t_ros

            rand_accX = 0.01*randn()
            if rand_accX > 0.1
                rand_accX = 0.1
            elseif rand_accX<-0.1
                rand_accX=-0.1
            end

            imu_data.linear_acceleration.x = diff(z_current[i-1:i,3])[1]/dt - z_current[i,6]*z_current[i,4] #+rand_accX#+ randn()*0.3*1.0

            rand_accY = 0.01*randn()
            if rand_accY > 0.1
                rand_accY = 0.1
            elseif rand_accY<-0.1
                rand_accY=-0.1
            end

            imu_data.linear_acceleration.y = diff(z_current[i-1:i,4])[1]/dt + z_current[i,6]*z_current[i,3] #+rand_accY#+ randn()*0.3*1.0
            publish(pub_imu, imu_data)      # Imu format is defined by ROS, you can look it up by google "rosmsg Imu"
                                            # It's sufficient to only fill the orientation part of the Imu-type (with one quaternion)
        end

        # real values
        if i%2 == 0
            real_data.psiDot = z_current[i,6]
            real_data.psi    = z_current[i,5]
            real_data.v_x    = z_current[i,3]
            real_data.v_y    = z_current[i,4]
            real_data.x      = z_current[i,1]
            real_data.y      = z_current[i,2]
            publish(real_val,real_data)
        end

        # Velocity measurements
        dist_traveled += norm(diff(z_current[i-1:i,1:2]))
        if i%5 == 0                 # 20 Hz
            if sum(dist_traveled .>= vel_dist_update)>=1 && z_current[i,3] > 0.1     # only update if at least one of the magnets has passed the sensor
                dist_traveled[dist_traveled.>=vel_dist_update] = 0
                vel_est.vel_est = convert(Float32,norm(z_current[i,3:4]))#+0.00*randn())
                vel_est.vel_fl = convert(Float32,0)
                vel_est.vel_fr = convert(Float32,0)
                vel_est.vel_bl = convert(Float32,0)
                vel_est.vel_br = convert(Float32,0)
            end
            vel_est.header.stamp = t_ros
            publish(pub_vel, vel_est)
        end

        # GPS measurements
        if i%6 == 0               # 16 Hz

            rand_x = 0.01*randn()
            if rand_x > 0.1
                rand_x = 0.1
            elseif rand_x<-0.1
                rand_x=-0.1
            end

            x = round(z_current[i,1] +  rand_x,2)#0.002*randn(),2)       # Indoor gps measures, rounded on cm

            rand_y = 0.01*randn()
            if rand_y > 0.1
                rand_y = 0.1
            elseif rand_y<-0.1
                rand_y=-0.1
            end

            y = round(z_current[i,2] + rand_y,2)#0.002*randn(),2)

            if randn()>10            # simulate gps-outlier (probability about 0.13% for randn()>3, 0.62% for randn()>2.5, 2.3% for randn()>2.0 )
                x += 1#randn()        # add random value to x and y
                y -= 1#randn()
                #sim_gps_interrupt = 6       # also do a little interruption
            elseif randn()>10 && sim_gps_interrupt < 0
                sim_gps_interrupt = 10      # simulate gps-interrupt (10 steps at 25 Hz is 0.4 seconds)
            end
            if sim_gps_interrupt < 0
                gps_meas.t_msg[gps_meas.i] = t
                gps_meas.t[gps_meas.i] = t
                gps_meas.z[gps_meas.i,:] = [x y]
                gps_meas.i += 1
                # gps_data.header.stamp = get_rostime()
                gps_data.x_m = x
                gps_data.y_m = y
                publish(pub_gps, gps_data)
            end
            sim_gps_interrupt -= 1
        end
        i += 1
        rossleep(loop_rate)
    end

    # Clean up buffers
    clean_up(gps_meas)
    clean_up(imu_meas)
    clean_up(cmd_log)
    z_real.z[1:i-1,:] = z_current[1:i-1,:]
    slip_a.z[1:i-1,:] = slip_ang[1:i-1,:]
    z_real.i = i
    slip_a.i = i
    clean_up(z_real)
    clean_up(slip_a)

    # Save simulation data to file
    # log_path = "$(homedir())/simulations/output-SIM-$(run_id[1:4]).jld"
    # save(log_path,"gps_meas",gps_meas,"z",z_real,"imu_meas",imu_meas,"cmd_log",cmd_log,"slip_a",slip_a)
    println("Exiting node... Saving data to $log_path. Simulated $((i-1)*dt) seconds.")

end

if ! isinteractive()
    main()
end

max_stars_repo_name: RoyaFiroozi/barc

max_issues_repo_name: RoyaFiroozi/barc

max_forks_repo_name: RoyaFiroozi/barc


text:
using Test

input = read(joinpath(@__DIR__, "input"), String)

RE = r"^([a-zA-Z ]+): (\d+)-(\d+) or (\d+)-(\d+)$"

function parse_input(input)
    parts = split(input, "\n\n")
    notes = split(first(parts), "\n")
    myticket = split(parts[2], "\n")
    othertickets = split(parts[3], "\n")
    tickets = append!(myticket[2:end], othertickets[2:end])
    return (notes, tickets)
end

function parse_notes(notes)
    acc = []
    for note in notes
        m = match(RE, note)
        rule = m.captures[1]
        bounds = map(x -> parse(Int, x), m.captures[2:5])
        r1 = bounds[1]:bounds[2]
        r2 = bounds[3]:bounds[4]
        push!(acc, (rule, r1, r2))
    end
    return acc
end

function parse_tickets(tickets)
    return map(t -> map(x -> parse(Int, x), split(t, ",")), filter(!isempty, tickets))
end

function filter_tickets(tickets, ranges)
    return filter(t -> length(check_ticket(t, ranges)) == 0, tickets)
end

function check_tickets(tickets, ranges)
    acc = []
    for ticket in tickets
        append!(acc, check_ticket(ticket, ranges))
    end
    return acc
end

function check_ticket(ticket, ranges)
    return filter(n -> !check_value(n, ranges), ticket)
end

function check_value(n, ranges)
    return any(map(r -> check_range(n, r), ranges))
end

function check_range(n, r)
    (_, r1, r2) = r
    return n in r1 || n in r2
end

function possibilities(tickets::Array{Array{Int, 1}, 1}, ranges)
    return map(t -> possibilities(t, ranges), tickets)
end

function possibilities(ticket::Array{Int,1}, ranges)
    return map(v -> possibilities(v, ranges), ticket)
end

function possibilities(value::Int, ranges)
    return filter(!isnothing, map(r -> rules(value, r), ranges))
end

function rules(value, range)
    (rule, r1, r2) = range
    return (value in r1 || value in r2) ? rule : nothing
end

function flat(ps)
    rules = Dict{Int, Array{String,1}}()
    for i in 1:length(first(ps))
        r = reduce(intersect!, map(p -> p[i], ps))
        rules[i] = r
    end

    result = Dict{Int, String}()
    seen = []

    while length(result) < 20
        for (k, v) in rules
            left = setdiff(v, seen)
            if length(left) == 1
                append!(seen, left)
                result[k] = left[1]
            end
        end
    end

    return result
end

(ns, ts) = parse_input(input)

notes = parse_notes(ns)
tickets = parse_tickets(ts)

p1 = sum(check_tickets(tickets, notes))

valid = filter_tickets(tickets[2:end], notes)
ps = flat(possibilities(valid, notes))

fields = map(first, filter(kv -> startswith(kv.second, "departure "), collect(ps)))

myticket = tickets[1]
p2 = prod(map(field -> myticket[field], fields))

println("-----------------------------------------------------------------------")
println("docking data -- part one :: $p1")
println("docking data -- part two :: $p2")
println("-----------------------------------------------------------------------")

@assert(p1 == 27870)
@assert(p2 == 3173135507987)