text:
#=
This file is auto-generated. Do not edit.
=#
"""
    mutable struct ThermalMultiStart <: ThermalGen
        name::String
        available::Bool
        status::Bool
        bus::Bus
        active_power::Float64
        reactive_power::Float64
        rating::Float64
        prime_mover::PrimeMovers
        fuel::ThermalFuels
        active_power_limits::NamedTuple{(:min, :max), Tuple{Float64, Float64}}
        reactive_power_limits::Union{Nothing, Min_Max}
        ramp_limits::Union{Nothing, NamedTuple{(:up, :down), Tuple{Float64, Float64}}}
        power_trajectory::Union{Nothing, NamedTuple{(:startup, :shutdown), Tuple{Float64, Float64}}}
        time_limits::Union{Nothing, NamedTuple{(:up, :down), Tuple{Float64, Float64}}}
        start_time_limits::Union{Nothing, NamedTuple{(:hot, :warm, :cold), Tuple{Float64, Float64, Float64}}}
        start_types::Int
        operation_cost::OperationalCost
        base_power::Float64
        services::Vector{Service}
        time_at_status::Float64
        must_run::Bool
        dynamic_injector::Union{Nothing, DynamicInjection}
        ext::Dict{String, Any}
        time_series_container::InfrastructureSystems.TimeSeriesContainer
        internal::InfrastructureSystemsInternal
    end

Data Structure for thermal generation technologies.

# Arguments
- `name::String`
- `available::Bool`
- `status::Bool`
- `bus::Bus`
- `active_power::Float64`, validation range: `active_power_limits`, action if invalid: `warn`
- `reactive_power::Float64`, validation range: `reactive_power_limits`, action if invalid: `warn`
- `rating::Float64`: Thermal limited MVA Power Output of the unit. <= Capacity, validation range: `(0, nothing)`, action if invalid: `error`
- `prime_mover::PrimeMovers`: Prime mover technology according to EIA 923
- `fuel::ThermalFuels`: Prime mover fuel according to EIA 923
- `active_power_limits::NamedTuple{(:min, :max), Tuple{Float64, Float64}}`
- `reactive_power_limits::Union{Nothing, Min_Max}`
- `ramp_limits::Union{Nothing, NamedTuple{(:up, :down), Tuple{Float64, Float64}}}`, validation range: `(0, nothing)`, action if invalid: `error`
- `power_trajectory::Union{Nothing, NamedTuple{(:startup, :shutdown), Tuple{Float64, Float64}}}`: Power trajectory the unit will take during the start-up and shut-down ramp process, validation range: `(0, nothing)`, action if invalid: `error`
- `time_limits::Union{Nothing, NamedTuple{(:up, :down), Tuple{Float64, Float64}}}`: Minimum up and Minimum down time limits in hours, validation range: `(0, nothing)`, action if invalid: `error`
- `start_time_limits::Union{Nothing, NamedTuple{(:hot, :warm, :cold), Tuple{Float64, Float64, Float64}}}`:  Time limits for start-up based on turbine temperature in hours
- `start_types::Int`:  Number of start-up based on turbine temperature, validation range: `(1, 3)`, action if invalid: `error`
- `operation_cost::OperationalCost`
- `base_power::Float64`: Base power of the unit in MVA, validation range: `(0, nothing)`, action if invalid: `warn`
- `services::Vector{Service}`: Services that this device contributes to
- `time_at_status::Float64`
- `must_run::Bool`
- `dynamic_injector::Union{Nothing, DynamicInjection}`: corresponding dynamic injection device
- `ext::Dict{String, Any}`
- `time_series_container::InfrastructureSystems.TimeSeriesContainer`: internal time_series storage
- `internal::InfrastructureSystemsInternal`: power system internal reference, do not modify
"""
mutable struct ThermalMultiStart <: ThermalGen
    name::String
    available::Bool
    status::Bool
    bus::Bus
    active_power::Float64
    reactive_power::Float64
    "Thermal limited MVA Power Output of the unit. <= Capacity"
    rating::Float64
    "Prime mover technology according to EIA 923"
    prime_mover::PrimeMovers
    "Prime mover fuel according to EIA 923"
    fuel::ThermalFuels
    active_power_limits::NamedTuple{(:min, :max), Tuple{Float64, Float64}}
    reactive_power_limits::Union{Nothing, Min_Max}
    ramp_limits::Union{Nothing, NamedTuple{(:up, :down), Tuple{Float64, Float64}}}
    "Power trajectory the unit will take during the start-up and shut-down ramp process"
    power_trajectory::Union{Nothing, NamedTuple{(:startup, :shutdown), Tuple{Float64, Float64}}}
    "Minimum up and Minimum down time limits in hours"
    time_limits::Union{Nothing, NamedTuple{(:up, :down), Tuple{Float64, Float64}}}
    " Time limits for start-up based on turbine temperature in hours"
    start_time_limits::Union{Nothing, NamedTuple{(:hot, :warm, :cold), Tuple{Float64, Float64, Float64}}}
    " Number of start-up based on turbine temperature"
    start_types::Int
    operation_cost::OperationalCost
    "Base power of the unit in MVA"
    base_power::Float64
    "Services that this device contributes to"
    services::Vector{Service}
    time_at_status::Float64
    must_run::Bool
    "corresponding dynamic injection device"
    dynamic_injector::Union{Nothing, DynamicInjection}
    ext::Dict{String, Any}
    "internal time_series storage"
    time_series_container::InfrastructureSystems.TimeSeriesContainer
    "power system internal reference, do not modify"
    internal::InfrastructureSystemsInternal
end

function ThermalMultiStart(name, available, status, bus, active_power, reactive_power, rating, prime_mover, fuel, active_power_limits, reactive_power_limits, ramp_limits, power_trajectory, time_limits, start_time_limits, start_types, operation_cost, base_power, services=Device[], time_at_status=INFINITE_TIME, must_run=false, dynamic_injector=nothing, ext=Dict{String, Any}(), time_series_container=InfrastructureSystems.TimeSeriesContainer(), )
    ThermalMultiStart(name, available, status, bus, active_power, reactive_power, rating, prime_mover, fuel, active_power_limits, reactive_power_limits, ramp_limits, power_trajectory, time_limits, start_time_limits, start_types, operation_cost, base_power, services, time_at_status, must_run, dynamic_injector, ext, time_series_container, InfrastructureSystemsInternal(), )
end

function ThermalMultiStart(; name, available, status, bus, active_power, reactive_power, rating, prime_mover, fuel, active_power_limits, reactive_power_limits, ramp_limits, power_trajectory, time_limits, start_time_limits, start_types, operation_cost, base_power, services=Device[], time_at_status=INFINITE_TIME, must_run=false, dynamic_injector=nothing, ext=Dict{String, Any}(), time_series_container=InfrastructureSystems.TimeSeriesContainer(), internal=InfrastructureSystemsInternal(), )
    ThermalMultiStart(name, available, status, bus, active_power, reactive_power, rating, prime_mover, fuel, active_power_limits, reactive_power_limits, ramp_limits, power_trajectory, time_limits, start_time_limits, start_types, operation_cost, base_power, services, time_at_status, must_run, dynamic_injector, ext, time_series_container, internal, )
end

# Constructor for demo purposes; non-functional.
function ThermalMultiStart(::Nothing)
    ThermalMultiStart(;
        name="init",
        available=false,
        status=false,
        bus=Bus(nothing),
        active_power=0.0,
        reactive_power=0.0,
        rating=0.0,
        prime_mover=PrimeMovers.OT,
        fuel=ThermalFuels.OTHER,
        active_power_limits=(min=0.0, max=0.0),
        reactive_power_limits=nothing,
        ramp_limits=nothing,
        power_trajectory=nothing,
        time_limits=nothing,
        start_time_limits=nothing,
        start_types=1,
        operation_cost=MultiStartCost(nothing),
        base_power=0.0,
        services=Device[],
        time_at_status=INFINITE_TIME,
        must_run=false,
        dynamic_injector=nothing,
        ext=Dict{String, Any}(),
        time_series_container=InfrastructureSystems.TimeSeriesContainer(),
    )
end

InfrastructureSystems.get_name(value::ThermalMultiStart) = value.name
"""Get [`ThermalMultiStart`](@ref) `available`."""
get_available(value::ThermalMultiStart) = value.available
"""Get [`ThermalMultiStart`](@ref) `status`."""
get_status(value::ThermalMultiStart) = value.status
"""Get [`ThermalMultiStart`](@ref) `bus`."""
get_bus(value::ThermalMultiStart) = value.bus
"""Get [`ThermalMultiStart`](@ref) `active_power`."""
get_active_power(value::ThermalMultiStart) = get_value(value, value.active_power)
"""Get [`ThermalMultiStart`](@ref) `reactive_power`."""
get_reactive_power(value::ThermalMultiStart) = get_value(value, value.reactive_power)
"""Get [`ThermalMultiStart`](@ref) `rating`."""
get_rating(value::ThermalMultiStart) = get_value(value, value.rating)
"""Get [`ThermalMultiStart`](@ref) `prime_mover`."""
get_prime_mover(value::ThermalMultiStart) = value.prime_mover
"""Get [`ThermalMultiStart`](@ref) `fuel`."""
get_fuel(value::ThermalMultiStart) = value.fuel
"""Get [`ThermalMultiStart`](@ref) `active_power_limits`."""
get_active_power_limits(value::ThermalMultiStart) = get_value(value, value.active_power_limits)
"""Get [`ThermalMultiStart`](@ref) `reactive_power_limits`."""
get_reactive_power_limits(value::ThermalMultiStart) = get_value(value, value.reactive_power_limits)
"""Get [`ThermalMultiStart`](@ref) `ramp_limits`."""
get_ramp_limits(value::ThermalMultiStart) = get_value(value, value.ramp_limits)
"""Get [`ThermalMultiStart`](@ref) `power_trajectory`."""
get_power_trajectory(value::ThermalMultiStart) = value.power_trajectory
"""Get [`ThermalMultiStart`](@ref) `time_limits`."""
get_time_limits(value::ThermalMultiStart) = value.time_limits
"""Get [`ThermalMultiStart`](@ref) `start_time_limits`."""
get_start_time_limits(value::ThermalMultiStart) = value.start_time_limits
"""Get [`ThermalMultiStart`](@ref) `start_types`."""
get_start_types(value::ThermalMultiStart) = value.start_types
"""Get [`ThermalMultiStart`](@ref) `operation_cost`."""
get_operation_cost(value::ThermalMultiStart) = value.operation_cost
"""Get [`ThermalMultiStart`](@ref) `base_power`."""
get_base_power(value::ThermalMultiStart) = value.base_power
"""Get [`ThermalMultiStart`](@ref) `services`."""
get_services(value::ThermalMultiStart) = value.services
"""Get [`ThermalMultiStart`](@ref) `time_at_status`."""
get_time_at_status(value::ThermalMultiStart) = value.time_at_status
"""Get [`ThermalMultiStart`](@ref) `must_run`."""
get_must_run(value::ThermalMultiStart) = value.must_run
"""Get [`ThermalMultiStart`](@ref) `dynamic_injector`."""
get_dynamic_injector(value::ThermalMultiStart) = value.dynamic_injector
"""Get [`ThermalMultiStart`](@ref) `ext`."""
get_ext(value::ThermalMultiStart) = value.ext

InfrastructureSystems.get_time_series_container(value::ThermalMultiStart) = value.time_series_container
"""Get [`ThermalMultiStart`](@ref) `internal`."""
get_internal(value::ThermalMultiStart) = value.internal

InfrastructureSystems.set_name!(value::ThermalMultiStart, val) = value.name = val
"""Set [`ThermalMultiStart`](@ref) `available`."""
set_available!(value::ThermalMultiStart, val) = value.available = val
"""Set [`ThermalMultiStart`](@ref) `status`."""
set_status!(value::ThermalMultiStart, val) = value.status = val
"""Set [`ThermalMultiStart`](@ref) `bus`."""
set_bus!(value::ThermalMultiStart, val) = value.bus = val
"""Set [`ThermalMultiStart`](@ref) `active_power`."""
set_active_power!(value::ThermalMultiStart, val) = value.active_power = val
"""Set [`ThermalMultiStart`](@ref) `reactive_power`."""
set_reactive_power!(value::ThermalMultiStart, val) = value.reactive_power = val
"""Set [`ThermalMultiStart`](@ref) `rating`."""
set_rating!(value::ThermalMultiStart, val) = value.rating = val
"""Set [`ThermalMultiStart`](@ref) `prime_mover`."""
set_prime_mover!(value::ThermalMultiStart, val) = value.prime_mover = val
"""Set [`ThermalMultiStart`](@ref) `fuel`."""
set_fuel!(value::ThermalMultiStart, val) = value.fuel = val
"""Set [`ThermalMultiStart`](@ref) `active_power_limits`."""
set_active_power_limits!(value::ThermalMultiStart, val) = value.active_power_limits = val
"""Set [`ThermalMultiStart`](@ref) `reactive_power_limits`."""
set_reactive_power_limits!(value::ThermalMultiStart, val) = value.reactive_power_limits = val
"""Set [`ThermalMultiStart`](@ref) `ramp_limits`."""
set_ramp_limits!(value::ThermalMultiStart, val) = value.ramp_limits = val
"""Set [`ThermalMultiStart`](@ref) `power_trajectory`."""
set_power_trajectory!(value::ThermalMultiStart, val) = value.power_trajectory = val
"""Set [`ThermalMultiStart`](@ref) `time_limits`."""
set_time_limits!(value::ThermalMultiStart, val) = value.time_limits = val
"""Set [`ThermalMultiStart`](@ref) `start_time_limits`."""
set_start_time_limits!(value::ThermalMultiStart, val) = value.start_time_limits = val
"""Set [`ThermalMultiStart`](@ref) `start_types`."""
set_start_types!(value::ThermalMultiStart, val) = value.start_types = val
"""Set [`ThermalMultiStart`](@ref) `operation_cost`."""
set_operation_cost!(value::ThermalMultiStart, val) = value.operation_cost = val
"""Set [`ThermalMultiStart`](@ref) `base_power`."""
set_base_power!(value::ThermalMultiStart, val) = value.base_power = val
"""Set [`ThermalMultiStart`](@ref) `services`."""
set_services!(value::ThermalMultiStart, val) = value.services = val
"""Set [`ThermalMultiStart`](@ref) `time_at_status`."""
set_time_at_status!(value::ThermalMultiStart, val) = value.time_at_status = val
"""Set [`ThermalMultiStart`](@ref) `must_run`."""
set_must_run!(value::ThermalMultiStart, val) = value.must_run = val
"""Set [`ThermalMultiStart`](@ref) `ext`."""
set_ext!(value::ThermalMultiStart, val) = value.ext = val

InfrastructureSystems.set_time_series_container!(value::ThermalMultiStart, val) = value.time_series_container = val


text:
module mcompostat0

# The barrel vault (Scordelis-Lo) roof is one of the benchmarks for linear elastic
# analysis of shells.

# Here we test the computation of the resultant forces and moments.

using LinearAlgebra
using FinEtools
using FinEtoolsDeforLinear
using FinEtoolsFlexStructures.FESetShellT3Module: FESetShellT3
using FinEtoolsFlexStructures.FESetShellQ4Module: FESetShellQ4
using FinEtoolsFlexStructures.FEMMShellT3FFModule
using FinEtoolsFlexStructures.FEMMShellT3FFCompModule
using FinEtoolsFlexStructures.CompositeLayupModule
using FinEtoolsFlexStructures.RotUtilModule: initial_Rfield, update_rotation_field!
using FinEtools.MeshExportModule.VTKWrite: vtkwrite

# analytical solution for the vertical deflection and the midpoint of the
# free edge
analyt_sol=-0.3024;
# Parameters:
E=4.32e8;
nu=0.0;
thickness = 0.25; # geometrical dimensions are in feet
R = 25.0;
L = 50.0;

cylindrical!(csmatout::FFltMat, XYZ::FFltMat, tangents::FFltMat, fe_label::FInt) = begin
    r = vec(XYZ); r[2] = 0.0; r[3] += R
    csmatout[:, 3] .= vec(r)/norm(vec(r))
    csmatout[:, 2] .= (0.0, 1.0, 0.0) #  this is along the axis
    cross3!(view(csmatout, :, 1), view(csmatout, :, 2), view(csmatout, :, 3))
    return csmatout
end

function test_homogeneous(visualize = true)
    formul = FEMMShellT3FFModule
    n = 8
    tolerance = R/n/10
    fens, fes = T3block(40/360*2*pi,L/2,n,n,:b);
    fens.xyz = xyz3(fens)
    for i in 1:count(fens)
        a=fens.xyz[i, 1]; y=fens.xyz[i, 2];
        fens.xyz[i, :] .= (R*sin(a), y, R*(cos(a)-1))
    end

    # conn = fill(0, count(fes), 3)
    # for i in 1:count(fes)
    #     conn[i, :] .= fes.conn[i][2], fes.conn[i][3], fes.conn[i][1]
    # end
    # fromarray!(fes, conn)

    mater = MatDeforElastIso(DeforModelRed3D, E, nu)
    ocsys = CSys(3, 3, cylindrical!)

    sfes = FESetShellT3()
    accepttodelegate(fes, sfes)
    femm = formul.make(IntegDomain(fes, TriRule(1), thickness),  ocsys, mater)
    femm.drilling_stiffness_scale = 1.0e-4
    femm.mult_el_size = 5/12
    stiffness = formul.stiffness
    associategeometry! = formul.associategeometry!

    # Construct the requisite fields, geometry and displacement
    # Initialize configuration variables
    geom0 = NodalField(fens.xyz)
    u0 = NodalField(zeros(size(fens.xyz,1), 3))
    Rfield0 = initial_Rfield(fens)
    dchi = NodalField(zeros(size(fens.xyz,1), 6))

    # Apply EBC's
    # rigid diaphragm
    l1 = selectnode(fens; box = Float64[-Inf Inf 0 0 -Inf Inf], inflate = tolerance)
    for i in [1,3,5]
        setebc!(dchi, l1, true, i)
    end
    # plane of symmetry perpendicular to Y
    l1 = selectnode(fens; box = Float64[-Inf Inf L/2 L/2 -Inf Inf], inflate = tolerance)
    for i in [2,4,6]
        setebc!(dchi, l1, true, i)
    end
    # plane of symmetry perpendicular to X
    l1 = selectnode(fens; box = Float64[0 0 -Inf Inf -Inf Inf], inflate = tolerance)
    for i in [1,5,6]
        setebc!(dchi, l1, true, i)
    end
    applyebc!(dchi)
    numberdofs!(dchi);

    # Assemble the system matrix
    associategeometry!(femm, geom0)
    K = stiffness(femm, geom0, u0, Rfield0, dchi);

    # Midpoint of the free edge
    nl = selectnode(fens; box = Float64[sin(40/360*2*pi)*25 sin(40/360*2*pi)*25 L/2 L/2 -Inf Inf], inflate = tolerance)
    lfemm = FEMMBase(IntegDomain(fes, TriRule(3)))
    fi = ForceIntensity(FFlt[0, 0, -90, 0, 0, 0]);
    F = distribloads(lfemm, geom0, dchi, fi, 3);

    # Solve
    U = K\F
    scattersysvec!(dchi, U[:])
    result =   dchi.values[nl, 3][1]
    # @info "Solution: $(result), $(round(result/analyt_sol*100, digits = 4))%"

    max_n = Float64[]
    min_n = Float64[]
    max_en = Float64[]
    min_en = Float64[]

    for nc in 1:3
        fld = fieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
        push!(max_n, maximum(fld.values))
        push!(min_n, minimum(fld.values))
        fld = elemfieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
        push!(max_en, maximum(fld.values))
        push!(min_en, minimum(fld.values))
    end

    max_m = Float64[]
    min_m = Float64[]
    max_em = Float64[]
    min_em = Float64[]

    for nc in 1:3
        fld = fieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
        push!(max_m, maximum(fld.values))
        push!(min_m, minimum(fld.values))
        fld = elemfieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
        push!(max_em, maximum(fld.values))
        push!(min_em, minimum(fld.values))
    end

    max_q = Float64[]
    min_q = Float64[]
    max_eq = Float64[]
    min_eq = Float64[]

    for nc in 1:2
        fld = fieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
        push!(max_q, maximum(fld.values))
        push!(min_q, minimum(fld.values))
        fld = elemfieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
        push!(max_eq, maximum(fld.values))
        push!(min_eq, minimum(fld.values))
    end

    if visualize
        scalars = []
        for nc in 1:3
            fld = fieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
            push!(scalars, ("m$nc", fld.values))
            fld = elemfieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
            push!(scalars, ("em$nc", fld.values))
        end
        vtkwrite("homo-$(n)-m.vtu", fens, fes; scalars = scalars, vectors = [("u", dchi.values[:, 1:3])])
        scalars = []
        for nc in 1:3
            fld = fieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
            push!(scalars, ("n$nc", fld.values))
            fld = elemfieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
            push!(scalars, ("en$nc", fld.values))
        end
        vtkwrite("homo-$(n)-n.vtu", fens, fes; scalars = scalars, vectors = [("u", dchi.values[:, 1:3])])
        scalars = []
        for nc in 1:2
            fld = fieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
            push!(scalars, ("q$nc", fld.values))
            fld = elemfieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
            push!(scalars, ("eq$nc", fld.values))
        end
        vtkwrite("homo-o-$(n)-q.vtu", fens, fes; scalars = scalars, vectors = [("u", dchi.values[:, 1:3])])
    end
    return max_n, min_n, max_en, min_en, max_m, min_m, max_em, min_em, max_q, min_q, max_eq, min_eq
end

function test_composite(visualize = true)
    formul = FEMMShellT3FFCompModule
    CM = CompositeLayupModule
    n = 8
    tolerance = R/n/10
    fens, fes = T3block(40/360*2*pi,L/2,n,n,:b);
    fens.xyz = xyz3(fens)
    for i in 1:count(fens)
        a=fens.xyz[i, 1]; y=fens.xyz[i, 2];
        fens.xyz[i, :] .= (R*sin(a), y, R*(cos(a)-1))
    end

    ocsys = CSys(3, 3, cylindrical!)
    mater = CM.lamina_material(E, nu)
    plies = [
    CM.Ply("ply_1", mater, thickness, 0),  ]

    layup = CM.CompositeLayup("sco-lo", plies, ocsys)

    sfes = FESetShellT3()
    accepttodelegate(fes, sfes)
    femm = formul.make(IntegDomain(fes, TriRule(1), thickness),  layup)
    femm.drilling_stiffness_scale = 1.0e-4
    femm.mult_el_size = 5/12
    stiffness = formul.stiffness
    associategeometry! = formul.associategeometry!

    # Construct the requisite fields, geometry and displacement
    # Initialize configuration variables
    geom0 = NodalField(fens.xyz)
    u0 = NodalField(zeros(size(fens.xyz,1), 3))
    Rfield0 = initial_Rfield(fens)
    dchi = NodalField(zeros(size(fens.xyz,1), 6))

    # Apply EBC's
    # rigid diaphragm
    l1 = selectnode(fens; box = Float64[-Inf Inf 0 0 -Inf Inf], inflate = tolerance)
    for i in [1,3,5]
        setebc!(dchi, l1, true, i)
    end
    # plane of symmetry perpendicular to Y
    l1 = selectnode(fens; box = Float64[-Inf Inf L/2 L/2 -Inf Inf], inflate = tolerance)
    for i in [2,4,6]
        setebc!(dchi, l1, true, i)
    end
    # plane of symmetry perpendicular to X
    l1 = selectnode(fens; box = Float64[0 0 -Inf Inf -Inf Inf], inflate = tolerance)
    for i in [1,5,6]
        setebc!(dchi, l1, true, i)
    end
    applyebc!(dchi)
    numberdofs!(dchi);

    # Assemble the system matrix
    associategeometry!(femm, geom0)
    K = stiffness(femm, geom0, u0, Rfield0, dchi);

    # Midpoint of the free edge
    nl = selectnode(fens; box = Float64[sin(40/360*2*pi)*25 sin(40/360*2*pi)*25 L/2 L/2 -Inf Inf], inflate = tolerance)
    lfemm = FEMMBase(IntegDomain(fes, TriRule(3)))
    fi = ForceIntensity(FFlt[0, 0, -90, 0, 0, 0]);
    F = distribloads(lfemm, geom0, dchi, fi, 3);

    # Solve
    U = K\F
    scattersysvec!(dchi, U[:])
    result =   dchi.values[nl, 3][1]
    # @info "Solution: $(result), $(round(result/analyt_sol*100, digits = 4))%"

    max_n = Float64[]
    min_n = Float64[]
    max_en = Float64[]
    min_en = Float64[]

    for nc in 1:3
        fld = fieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
        push!(max_n, maximum(fld.values))
        push!(min_n, minimum(fld.values))
        fld = elemfieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
        push!(max_en, maximum(fld.values))
        push!(min_en, minimum(fld.values))
    end

    max_m = Float64[]
    min_m = Float64[]
    max_em = Float64[]
    min_em = Float64[]

    for nc in 1:3
        fld = fieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
        push!(max_m, maximum(fld.values))
        push!(min_m, minimum(fld.values))
        fld = elemfieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
        push!(max_em, maximum(fld.values))
        push!(min_em, minimum(fld.values))
    end

    max_q = Float64[]
    min_q = Float64[]
    max_eq = Float64[]
    min_eq = Float64[]

    for nc in 1:2
        fld = fieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
        push!(max_q, maximum(fld.values))
        push!(min_q, minimum(fld.values))
        fld = elemfieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
        push!(max_eq, maximum(fld.values))
        push!(min_eq, minimum(fld.values))
    end

    if visualize
        scalars = []
        for nc in 1:3
            fld = fieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
            push!(scalars, ("m$nc", fld.values))
            fld = elemfieldfromintegpoints(femm, geom0, dchi, :moment, nc, outputcsys = ocsys)
            push!(scalars, ("em$nc", fld.values))
        end
        vtkwrite("comp-$(n)-m.vtu", fens, fes; scalars = scalars, vectors = [("u", dchi.values[:, 1:3])])
        scalars = []
        for nc in 1:3
            fld = fieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
            push!(scalars, ("n$nc", fld.values))
            fld = elemfieldfromintegpoints(femm, geom0, dchi, :membrane, nc, outputcsys = ocsys)
            push!(scalars, ("en$nc", fld.values))
        end
        vtkwrite("comp-$(n)-n.vtu", fens, fes; scalars = scalars, vectors = [("u", dchi.values[:, 1:3])])
        scalars = []
        for nc in 1:2
            fld = fieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
            push!(scalars, ("q$nc", fld.values))
            fld = elemfieldfromintegpoints(femm, geom0, dchi, :shear, nc, outputcsys = ocsys)
            push!(scalars, ("eq$nc", fld.values))
        end
        vtkwrite("comp-o-$(n)-q.vtu", fens, fes; scalars = scalars, vectors = [("u", dchi.values[:, 1:3])])
    end
    return max_n, min_n, max_en, min_en, max_m, min_m, max_em, min_em, max_q, min_q, max_eq, min_eq
end

end # module
using LinearAlgebra
using Test
using .mcompostat0
rh = mcompostat0.test_homogeneous()
rc = mcompostat0.test_composite()

for i in 1:length(rc)
    @test norm(rc[i] - rh[i]) / norm(rh[i]) < 1.0e56
end

ext: jl


text:
"Parse PDDL problem description."
parse_problem(expr::Vector) =
    GenericProblem(parse_description(:problem, expr)...)
parse_problem(str::AbstractString) =
    parse_problem(parse_string(str))
top_level_parsers[:problem] = parse_problem

"Parse domain for planning problem."
parse_domain_name(expr::Vector) = expr[2]
head_field_parsers[:problem][:domain] = parse_domain_name

"Parse objects in planning problem."
function parse_objects(expr::Vector)
    @assert (expr[1].name == :objects) ":objects keyword is missing."
    objs, types = parse_typed_consts(expr[2:end])
    types = Dict{Const,Symbol}(o => t for (o, t) in zip(objs, types))
    return (objects=objs, objtypes=types)
end
parse_objects(::Nothing) =
    (objects=Const[], objtypes=Dict{Const,Symbol}())
head_field_parsers[:problem][:objects] = parse_objects

"Parse initial formula literals in planning problem."
function parse_init(expr::Vector)
    @assert (expr[1].name == :init) ":init keyword is missing."
    return [parse_formula(e) for e in expr[2:end]]
end
parse_init(::Nothing) = Term[]
head_field_parsers[:problem][:init] = parse_init

"Parse goal formula in planning problem."
function parse_goal(expr::Vector)
    @assert (expr[1].name == :goal) ":goal keyword is missing."
    return parse_formula(expr[2])
end
parse_goal(::Nothing) = Const(true)
head_field_parsers[:problem][:goal] = parse_goal

"Parse metric expression in planning problem."
function parse_metric(expr::Vector)
    @assert (expr[1].name == :metric) ":metric keyword is missing."
    @assert (expr[2] in (:minimize, :maximize)) "Unrecognized optimization."
    return Compound(expr[2], [parse_formula(expr[3])])
end
parse_metric(expr::Nothing) = nothing
head_field_parsers[:problem][:metric] = parse_metric

"Parse constraints formula in planning problem."
function parse_constraints(expr::Vector)
    @assert (expr[1].name == :constraints) ":constraints keyword is missing."
    return parse_formula(expr[2])
end
parse_constraints(::Nothing) = nothing
head_field_parsers[:problem][:constraints] = parse_constraints

"Parse initializations for continuous variables"
function parse_cont_inits(expr::Vector)
    return parse_formula(expr[2])
end
parse_cont_inits(::Nothing) = nothing
head_field_parsers[:problem][:continuous_inits] = parse_cont_inits

"Parse goal values for continuous variables"
function parse_cont_goals(expr::Vector)
    return parse_formula(expr[2])
end
parse_cont_goals(::Nothing) = nothing
head_field_parsers[:problem][:continuous_goals] = parse_cont_goals

"Parse external numerical constraints formula in planning problem."
function parse_ext_constraints(expr::Vector)
    return parse_formula(expr[2])
end
parse_ext_constraints(::Nothing) = nothing
head_field_parsers[:problem][:external_constraints] = parse_ext_constraints

"Parse objective function of planning problem."
function parse_objective_function(expr::Vector)
    return parse_formula(expr[2])
end
parse_objective_function(::Nothing) = nothing
head_field_parsers[:problem][:objective_function] = parse_objective_function


text:
import PyPlot
import Random
using LinearAlgebra

import StaticArrays
import Interpolations

import Calculus

include("../src/function_interpolation.jl")
include("../src/patterns.jl")
include("../src/visualize_2D.jl")

PyPlot.close("all")
fig_num = 1

Random.seed!(25)

D = 2

N_array = [50; 50]

x_a = [-3.5; 2.5]
x_b = [3.5; 4.5]

x_ranges = collect( LinRange(x_a[d], x_b[d], N_array[d]) for d = 1:D )
X_nD = ranges2collection(x_ranges, Val(2))

f = xx->sinc.(3*norm(xx))
A = f.(X_nD)

f_itp, d_f_itp, d2_f_itp = setupcubicitp(A, x_ranges, 1.0)
#f_itp, d_f_itp!, d2_f_itp! = setupcubicitpmutator(A, x_ranges, 1.0)

fig_num = visualizemeshgridpcolor(x_ranges, f_itp.(X_nD),
                vec(X_nD), ".",
               fig_num, "f_itp", "x1", "x2")

df_ND = xx->Calculus.gradient(f, xx)

println("Lower bounds: ", x_a)
println("Upper bounds: ", x_b)
println()

x0 = randn(D) + (x_a+x_b) ./2
df_ND(x0)
println("In bounds:")
println("x0 = ", x0)
#println("d_f_itp!(x0) = ", d_f_itp!(x0))
println("d_f_itp(x0) = ", d_f_itp(x0))
println("df_ND(x0)   = ", df_ND(x0))
println()

x0 = randn(D)
df_ND(x0)
println("Out of bounds:")
println("x0 = ", x0)
println("d_f_itp(x0) = ", d_f_itp(x0))
println("df_ND(x0)   = ", df_ND(x0))
println()

max_stars_repo_name: RoyCCWang/RWJuliaUtilities

max_issues_repo_name: RoyCCWang/RWJuliaUtilities

max_forks_repo_name: RoyCCWang/RWJuliaUtilities


text:
function embed2linear(embed::Embedding)
    w = hcat(map(v -> v.data, embed.ws)...)
    outdim = size(w, 1)
    T = typeof(w[1])
    b = fill(T(0), outdim, 1)
    Linear(Merlin.Param(w), Merlin.Param(b))
end

function indmaxk{T}(vec::Array{T}, k::Int)
    arr = copy(vec)
    res = Int[]
    while k > 0
        idx = indmax(arr)
        push!(res, idx)
        arr[idx] = -10000000f0 #typemin(T)
        k -= 1
    end
    res
end

function readprobs(parser::DepParser, tagpath::AbstractString, conllpath::AbstractString, topk::Int)
    tags = map(chomp, open(readlines, tagpath))
    tag2id = Dict([tag => id for (id, tag) in enumerate(tags)])
    doc = Vector[]
    push!(doc, [])
    for line in open(readlines, conllpath)
        line = chomp(line)
        if isempty(line)
            push!(doc, [])
            continue
        end
        items = split(line, "\t")
        word = items[1]
        probs = map(v -> parse(Float32, v), items[2:end])
        # prob(NONE) == 0.0
        probs = [probs[tag2id[getkey(parser.tags, i)]] for i = 2:length(tags)+1]
        probs = convert(Vector{Float32}, probs)
        unshift!(probs, 0f0)
        if topk == 1
            probs = indmax(probs)
        elseif topk > 1
            topidx = indmaxk(probs, topk)
            probs = [i in topidx ? probs[i] : 0f0 for i = 1:length(probs)]
            probs = reshape(probs, length(probs), 1)
        else
            probs = reshape(probs, length(probs), 1)
        end
        push!(doc[end], (lowercase(word), probs))
    end
    if topk != 1
        vsize = size(doc[1][1][2])
        global roottoken = Token(2, zeros(Float32, vsize), 0, 1)
    end
    doc
end

function readconll_withprobs(parser, tagpath, conllpath, probpath; topk=1, train=true)
    conlldoc = readconll(parser, conllpath, train=train)
    conllprobs = readprobs(parser, tagpath, probpath, topk)
    @assert length(conlldoc) == length(conllprobs)
    for (sent, probs) in zip(conlldoc, conllprobs)
        @assert length(sent) == length(probs)
        for (token, prob) in zip(sent, probs)
            form = getkey(parser.words, token.word)
            token.tag = prob[2]
        end
    end
    conlldoc
end

max_stars_repo_name: hshindo/JukaiNLP

max_issues_repo_name: hshindo/JukaiNLP

max_forks_repo_name: hshindo/JukaiNLP


text:
module DataGen

# Types
import RIOPA: TagBase, DataStream, PayloadRange, DataSet
import OrderedCollections: LittleDict
# Functions
import RIOPA: evolve_payload_range!
# Modules
import MPI, Random

abstract type DataGenTag <: TagBase end

function generate! end
function initialize_streams! end

tagmap = LittleDict{String,DataGenTag}()

function add(key::String, tag::DataGenTag)
    tagmap[key] = tag
end

function get_tag(key::String)
    try
        return tagmap[key]
    catch
        println("Invalid DataGen backend: ", key)
        rethrow()
    end
end

struct DefaultDataGenTag <: DataGenTag end

get_tag(::Nothing) = DefaultDataGenTag()

struct ProcessPayloadRatioError <: Exception
    msg::String
end

function check_payload_group_ratios(ds::DataSet)
    for stream_cfg in ds.cfg.streams
        sum = 0.0
        for grp in stream_cfg.payload_groups
            sum += grp.ratio
        end
        if !isapprox(sum, 1.0)
            throw(
                ProcessPayloadRatioError(
                    "Sum of payload group ratios ($sum) must equal 1" *
                    "; dataset: " *
                    ds.cfg.name *
                    ", stream: " *
                    stream_cfg.name,
                ),
            )
        end
    end
end

function initialize_streams!(::DefaultDataGenTag, ds::DataSet)
    check_payload_group_ratios(ds)
    ds.streams = map(stream_cfg -> DataStream(stream_cfg), ds.cfg.streams)
end

function generate_stream_data!(
    stream::DataStream,
    step::Integer,
    step_factor::Integer,
)
    newsize = rand((stream.range.a):(stream.range.b))
    resize!(stream.data.vec, newsize)
    Random.rand!(stream.data.vec)
    evolve_payload_range!(stream, step * step_factor)
end

function generate!(::DefaultDataGenTag, ds::DataSet)
    foreach(
        stream ->
            generate_stream_data!(stream, ds.curr_step, ds.cfg.step_factor),
        ds.streams,
    )
end

end


text:
module MPIWrapper
using NaivePDE
using MPI

# package code goes here
include("AreaDistribution/MPIWrapper.jl")

end # module


text:
using COSMO, Test, LinearAlgebra, Random, SparseArrays
rng = Random.MersenneTwister(41)

@testset "Interface" begin
# set!() -  to directly set the problem data without building constraints first

P = [4. 1;1 2]
q = [1; 1.]
A = [1. 1;1 0; 0 1]
l = [1.;0;0]
u = [1.;0.7;0.7]
Aa = [A; -A]
b = [u; -l]
convex_sets = [COSMO.Nonnegatives(3); COSMO.Nonnegatives(3)]

model = COSMO.Model()
COSMO.set!(model, P, q, Aa, b, convex_sets )
res = COSMO.optimize!(model)
@test isapprox(res.obj_val, 1.88, atol = 1e-3)

qf = [1; 1; 1]
Pf = zeros(1, 1)
Af = [1 2; 1 2]
bf = [1; 2]
model = COSMO.Model()
@test_throws DimensionMismatch COSMO.set!(model, P, qf, Aa, b, convex_sets)
@test_throws DimensionMismatch COSMO.set!(model, Pf, q, Aa, b, convex_sets)
@test_throws DimensionMismatch COSMO.set!(model, P, q, Af, b, convex_sets)
@test_throws DimensionMismatch COSMO.set!(model, P, q, Aa, bf, convex_sets)

# assemble! function test different combinations of P an q

#P not sparse, q dense
P = rand(rng, 2, 2)
q = rand(rng, 2)
A = rand(rng, 1, 2)
b = 1.
constraint = COSMO.Constraint(A, b, COSMO.Nonnegatives)
model = COSMO.Model();
COSMO.assemble!(model, P, q, constraint)
@test model.p.P == P
@test model.p.q == q
@test model.p.A == -A
@test model.p.b == [b]

# sparse input data
COSMO.empty_model!(model)
COSMO.assemble!(model, sparse(P), sparse(q), constraint)
@test model.p.P == P
@test model.p.q == q

A = rand(rng, 5, 1)
b = rand(rng, 5)
constraint = COSMO.Constraint(A, b, COSMO.Nonnegatives)

# P number, q vector
COSMO.empty_model!(model)
COSMO.assemble!(model, 1., [1.], constraint)
@test model.p.P == hcat([1.])
@test model.p.q == [1.]

# P vector, q number
COSMO.empty_model!(model)
COSMO.assemble!(model, [1.], 1., constraint)
@test model.p.P == hcat([1.])
@test model.p.q == [1.]

# P number, q number
COSMO.empty_model!(model)
COSMO.assemble!(model, 1., 1., constraint, settings = COSMO.Settings(verbose = true))
@test model.p.P == hcat([1.])
@test model.p.q == [1.]

# P vector, q matrix
COSMO.empty_model!(model)
COSMO.assemble!(model, [1.], hcat([1.]), constraint)
@test model.p.P == hcat([1.])
@test model.p.q == [1.]

# Test case where dimension of an A in one constraint is inconsistent with P
A = 1.
b = 0.
P = sparse(1.0I, 2, 2)
q = rand(rng, 2)
constraint = COSMO.Constraint(A, b, COSMO.Nonnegatives)
model = COSMO.Model();
@test_throws DimensionMismatch COSMO.assemble!(model, P, q, [constraint])

end


text:
"""
    write_netcdf2d(fname, var, lon, lat, varname, filenc)

Write field `var`to netcdf file `fname`, with coordinates `lon` and `lat` and variable name `varname`.
File `filenc` is used to copy metadata from.
"""
function write_netcdf2d(fname, var, lon, lat, varname, filenc)

    if(length(size(lon))==1)
        (nsx,) = size(lon)
        (nsy,) = size(lat)
    else
        (nsx, nsy) = size(lon)
    end
    if(length(size(var))==2)
        nt = 1
        fnotime = 1
        tim = [0]
    else
        (ns, ns, nt) = size(var)
        fnotime = 0
    end

    mode = NC_NETCDF4

    #println("Reading ",filenc)
    ncin = NetCDF.open(filenc)
    if( haskey(ncin.vars, "lon") )
        lonatt = ncin.vars["lon"].atts
        latatt = ncin.vars["lat"].atts
    elseif (haskey(ncin.vars, "longitude"))
        lonatt = ncin.vars["longitude"].atts
        latatt = ncin.vars["latitude"].atts
    elseif (haskey(ncin.vars, "x"))
        lonatt = ncin.vars["x"].atts
        latatt = ncin.vars["y"].atts
    elseif (haskey(ncin.vars, "X"))
        lonatt = ncin.vars["X"].atts
        latatt = ncin.vars["Y"].atts
    else
        println("Input reference file does not contain lon or longitude dimensional variables")
        quit(1)
    end

    if(fnotime==0)
        tim = NetCDF.readvar(ncin, "time")
        timeatt = ncin.vars["time"].atts
    end
    varatt= ncin.vars[varname].atts

    #ncgetatt(filenc, "global", gatts)

    varattr = Dict()
    for k in collect(keys(varatt))
        if((k!="_FillValue")&&(k!="add_offset")&&(k!="scale_factor"))
            varattr[k] = varatt[k]
        end
    end

    isfile(fname) ? rm(fname) : nothing

    if(length(size(lon))==1)
        if(fnotime==0)
            nccreate(fname, varname, "lon", nsx, "lat", nsy, "time", tim[1:nt] ,timeatt, atts=varattr, mode=mode, t=NC_FLOAT)
        else
            nccreate(fname, varname, "lon", nsx, "lat", nsy, atts=varattr, mode=mode, t=NC_FLOAT)
        end
        nccreate( fname, "lon", "lon", nsx,  atts=lonatt, mode=mode, t=NC_FLOAT)
        nccreate( fname, "lat", "lat", nsy,  atts=latatt, mode=mode, t=NC_FLOAT)
    else
        if(fnotime==0)
            nccreate(fname, varname, "x", nsx, "y", nsy, "time", tim[1:nt] ,timeatt, atts=varattr, mode=mode, t=NC_FLOAT)
        else
            nccreate(fname, varname, "x", nsx, "y", nsy, atts=varattr, mode=mode, t=NC_FLOAT)
        end
        nccreate( fname, "lon", "x", nsx, "y", nsy, atts=lonatt, mode=mode, t=NC_FLOAT)
        nccreate( fname, "lat", "x", nsx, "y", nsy, atts=latatt, mode=mode, t=NC_FLOAT)
    end

    ncwrite(var, fname, varname)
    ncwrite(lon, fname, "lon")
    ncwrite(lat, fname, "lat")
end


text:
module NeutralLandscapes

import NaNMath
using StatsBase: sample
using Random: rand!
using Statistics: quantile
using Distributions: Normal
using Statistics: quantile, mean
using NearestNeighbors: KDTree, knn, nn
using DataStructures: IntDisjointSets, union!, find_root, push!

"""
All algorithms are descended from the `NeutralLandscapeMaker` type. A new
algorithm must minimally implement and `_landscape!` method for this type.
"""
abstract type NeutralLandscapeMaker end
export NeutralLandscapeMaker

include("landscape.jl")
export rand, rand!

include(joinpath("algorithms", "nogradient.jl"))
export NoGradient

include(joinpath("algorithms", "planargradient.jl"))
export PlanarGradient

include(joinpath("algorithms", "edgegradient.jl"))
export EdgeGradient

include(joinpath("algorithms", "diamondsquare.jl"))
export DiamondSquare, MidpointDisplacement

include(joinpath("algorithms", "wavesurface.jl"))
export WaveSurface

include(joinpath("algorithms", "distancegradient.jl"))
export DistanceGradient

include(joinpath("algorithms", "rectangularcluster.jl"))
export RectangularCluster

include(joinpath("algorithms", "nnelement.jl"))
export NearestNeighborElement

include(joinpath("algorithms", "nncluster.jl"))
export NearestNeighborCluster

include(joinpath("algorithms", "perlinnoise.jl"))
export PerlinNoise

include("classify.jl")
export classify!, classify, blend, label

end # module


text:
"""
    PyDict{K=Py,V=Py}([x])

Wraps the Python dict `x` (or anything satisfying the mapping interface) as an `AbstractDict{K,V}`.

If `x` is not a Python object, it is converted to one using `pydict`.
"""
struct PyDict{K,V} <: AbstractDict{K,V}
    py :: Py
    PyDict{K,V}(::Val{:new}, py::Py) where {K,V} = new{K,V}(py)
end
export PyDict

PyDict{K,V}(x=pydict()) where {K,V} = PyDict{K,V}(Val(:new), ispy(x) ? Py(x) : pydict(x))
PyDict{K}(x=pydict()) where {K} = PyDict{K,Py}(x)
PyDict(x=pydict()) = PyDict{Py,Py}(x)

ispy(::PyDict) = true
getpy(x::PyDict) = x.py
pydel!(x::PyDict) = pydel!(x.py)

pyconvert_rule_mapping(::Type{T}, x::Py, ::Type{PyDict{K,V}}=Utils._type_ub(T)) where {T<:PyDict,K,V} =
    if PyDict{Py,Py} <: T
        pyconvert_return(PyDict{Py,Py}(x))
    elseif PyDict{K,Py} <: T
        pyconvert_return(PyDict{K,Py}(x))
    elseif PyDict{Py,V} <: T
        pyconvert_return(PyDict{Py,V}(x))
    else
        pyconvert_return(PyDict{K,V}(x))
    end

Base.length(x::PyDict) = Int(pylen(x))

function Base.iterate(x::PyDict{K,V}, it::Py=pyiter(x)) where {K,V}
    k_ = unsafe_pynext(it)
    pyisnull(k_) && return nothing
    v_ = pygetitem(x, k_)
    k = pyconvert_and_del(K, k_)
    v = pyconvert_and_del(V, v_)
    return (k => v, it)
end

function Base.iterate(x::Base.KeySet{K,PyDict{K,V}}, it::Py=pyiter(x.dict)) where {K,V}
    k_ = unsafe_pynext(it)
    pyisnull(k_) && return nothing
    k = pyconvert_and_del(K, k_)
    return (k, it)
end

function Base.getindex(x::PyDict{K,V}, k) where {K,V}
    return pyconvert_and_del(V, pygetitem(x, convert(K, k)))
end

function Base.setindex!(x::PyDict{K,V}, v, k) where {K,V}
    pysetitem(x, convert(K, k), convert(V, v))
    return x
end

function Base.delete!(x::PyDict{K,V}, k) where {K,V}
    r = pyconvert_tryconvert(K, k)
    if !pyconvert_isunconverted(r)
        pydelitem(x, pyconvert_result(K, r))
    end
    return x
end

function Base.empty!(x::PyDict)
    pydel!(@py x.clear())
    return x
end

function Base.copy(x::PyDict{K,V}) where {K,V}
    PyDict{K,V}(Val(:new), @py x.copy())
end

function Base.haskey(x::PyDict{K,V}, k) where {K,V}
    r = pyconvert_tryconvert(K, k)
    if pyconvert_isunconverted(r)
        return false
    else
        return pycontains(x, pyconvert_result(K, r))
    end
end

function Base.get(x::PyDict, k, d)
    if haskey(x, k)
        return x[k]
    else
        return d
    end
end

function Base.get(f::Union{Function,Type}, x::PyDict, k)
    if haskey(x, k)
        return x[k]
    else
        return f()
    end
end

function Base.get!(x::PyDict{K,V}, k, d) where {K,V}
    if haskey(x, k)
        return x[k]
    else
        return x[k] = convert(V, d)
    end
end

function Base.get!(f::Union{Function,Type}, x::PyDict{K,V}, k) where {K,V}
    if haskey(x, k)
        return x[k]
    else
        return x[k] = convert(V, f())
    end
end


text:
#=
    EodData external utility functions
=#
export set_date_string

# =========
# Functions

# set_date_string()
# -----------------
# Returns a string that represents a date
# INPUT: Date data type
# OUTPUT: Xml tree of the type ::ETree
# http://ws.eoddata.com/data.asmx?op=CountryList
function set_date_string(dt)
    return string(year(dt)) * ("0" * string(month(dt)))[end - 1:end] * ("0" * string(day(dt)))[end - 1:end]
end


text:
# --- Types

struct Reduced{T}
    value::T
end

Base.:(==)(x::Reduced, y::Reduced) = x.value == y.value
Base.:(==)(x::Reduced, ::Any) = false

isreduced(::Reduced) = true
isreduced(::Any) = false

ensure_reduced(x::Reduced) = x
ensure_reduced(x) = Reduced(x)

unreduced(x::Reduced) = x.value
unreduced(x) = x

abstract type Transducer end
abstract type AbstractFilter <: Transducer end

struct Composition{XO <: Transducer, XI <: Transducer} <: Transducer
    outer::XO
    inner::XI
end

struct IdentityTransducer <: Transducer end

"""
    Transducer

The abstract type for transducers.
"""
Transducer

"""
    AbstractFilter <: Transducer

The abstract type for filter-like transducers.  [`outtype`](@ref) is
appropriately defined for child types.
"""
AbstractFilter

struct Reduction{X <: Transducer, I, InType}
    xform::X
    inner::I  # Transducer or a function with arity-2 and -1 methods
end

InType(::T) where T = InType(T)
InType(::Type{Reduction{X, I, intype}}) where {X, I, intype} = intype
InType(T::Type) = throw(MethodError(InType, (T,)))

Transducer(rf::Reduction{<:Transducer, <:Reduction}) =
    Composition(rf.xform, Transducer(rf.inner))
Transducer(rf::Reduction) = rf.xform

"""
    Transducers.R_{X}

When defining a transducer type `X`, it is often required to dispatch
on type `rf::R_{X}` (Reducing Function) which bundles the current
transducer `rf.xform::X` and the inner reducing function
`rf.inner::R_`.

```julia
const R_{X} = Reduction{<:X}
```
"""
const R_{X} = Reduction{<:X}

@inline Reduction(xf::X, inner::I, ::Type{InType}) where {X, I, InType} =
    Reduction{X, I, InType}(xf, inner)

@inline function Reduction(xf_::Composition, f, intype::Type)
    xf = _normalize(xf_)
    # @assert !(xf.outer isa Composition)
    return Reduction(
        xf.outer,
        Reduction(xf.inner, f, outtype(xf.outer, intype)),
        intype)
end

@inline _normalize(xf) = xf
@inline _normalize(xf::Composition{<:Composition}) =
    _normalize(xf.outer.outer |> _normalize(xf.outer.inner |> xf.inner))

outtype(xf::Composition, intype) = outtype(xf.inner, outtype(xf.outer, intype))
# TeeZip needs it

# Not sure if this a good idea... (But it's easier to type)
@inline Base.:|>(f::Transducer, g::Transducer) = _normalize(Composition(f, g))
# Base.∘(f::Transducer, g::Transducer) = Composition(f, g)
# Base.∘(f::Composition, g::Transducer) = f.outer ∘ (f.inner ∘ g)
@inline Base.:|>(::IdentityTransducer, f::Transducer) = f
@inline Base.:|>(f::Transducer, ::IdentityTransducer) = f

"""
    Transducers.start(rf::R_{X}, state)

This is an optional interface for a transducer.  Default
implementation just calls `start` of the inner reducing function; i.e.,

```julia
start(rf::Reduction, result) = start(rf.inner, result)
```

If the transducer `X` is stateful, it can "bundle" its private state
with `state` (so that `next` function can be "pure").

```julia
start(rf::R_{X}, result) = wrap(rf, PRIVATE_STATE, start(rf.inner, result))
```

See [`Take`](@ref), [`PartitionBy`](@ref), etc. for real-world examples.

Side notes: There is no related API in Clojure's Transducers.
Transducers.jl uses it to implement stateful transducers using "pure"
functions.  The idea is based on a slightly different approach taken
in C++ Transducer library [atria](https://github.com/AbletonAG/atria).
"""
start(::Any, result) = result
start(rf::Reduction, result) = start(rf.inner, result)
start(rf::R_{AbstractFilter}, result) = start(rf.inner, result)

"""
    Transducers.next(rf::R_{X}, state, input)

This is the only required interface.  It takes the following form
(if `start` is not defined):

```julia
next(rf::R_{X}, result, input) =
    # code calling next(rf.inner, result, possibly_modified_input)
```

See [`Map`](@ref), [`Filter`](@ref), [`Cat`](@ref), etc. for
real-world examples.
"""
next(f, result, input) = f(result, input)

# done(rf, result)

"""
    Transducers.complete(rf::R_{X}, state)

This is an optional interface for a transducer.  If transducer `X` has
some internal state, this is the last chance to "flush" the result.

See [`PartitionBy`](@ref), etc. for real-world examples.

If **both** `complete(rf::R_{X}, state)` **and** `start(rf::R_{X},
state)` are defined, `complete` **must** unwarp `state` before
returning `state` to the outer reducing function.  If `complete` is
not defined for `R_{X}`, this happens automatically.
"""
complete(f, result) = f(result)
complete(rf::Reduction, result) =
    # Not using dispatch to avoid ambiguity
    if result isa PrivateState{typeof(rf)}
        # TODO: make a test case that this is crucial:
        complete(rf.inner, unwrap(rf, result)[2])
    else
        complete(rf.inner, result)
    end

struct PrivateState{T, S, R}
    state::S
    result::R
end
# TODO: make it a tuple-like so that I can return it as-is

PrivateState(rf::Reduction, state, result) =
    PrivateState{typeof(rf), typeof(state), typeof(result)}(state, result)

"""
    unwrap(rf, result)

Unwrap [`wrap`](@ref)ed `result` to a private state and inner result.
Following identity holds:

```julia
unwrap(rf, wrap(rf, state, iresult)) == (state, iresult)
```

This is intended to be used only in [`complete`](@ref).  Inside
[`next`](@ref), use [`wrapping`](@ref).
"""
unwrap(::T, ps::PrivateState{T}) where {T} = (ps.state, ps.result)

unwrap(::T1, ::PrivateState{T2}) where {T1, T2} =
    error("""
    `unwrap(rf1, ps)` is used for
    typeof(rf1) = $T1
    while `ps` is created by wrap(rf2, ...) where
    typeof(rf2) = $T2
    """)

# TODO: better error message with unmatched `T`

"""
    wrap(rf::R_{X}, state, iresult)

Pack private `state` for reducing function `rf` (or rather the
transducer `X`) with the result `iresult` returned from the inner
reducing function `rf.inner`.  This packed result is typically passed
to the outer reducing function.

This is intended to be used only in [`start`](@ref).  Inside
[`next`](@ref), use [`wrapping`](@ref).

Consider a reducing step constructed as

    rf = Reduction(xf₁ |> xf₂ |> xf₃, f, intype)

where each `xfₙ` is a stateful transducer and hence needs a private
state `stateₙ`.  Then, calling `start(rf, result))` is equivalent to

```julia
wrap(rf,
     state₁,                     # private state for xf₁
     wrap(rf.inner,
          state₂,                # private state for xf₂
          wrap(rf.inner.inner,
               state₃,           # private state for xf₃
               result)))
```

or equivalently

```julia
result₃ = result
result₂ = wrap(rf.inner.inner, state₃, result₃)
result₁ = wrap(rf.inner,       state₂, result₂)
result₀ = wrap(rf,             state₁, result₁)
```

The inner most step function receives the original `result` as the
first argument while transducible processes such as [`mapfoldl`](@ref)
only sees the outer-most "tree" `result₀` during the reduction.  The
whole tree is [`unwrap`](@ref)ed during the [`complete`](@ref) phase.

See [`wrapping`](@ref), [`unwrap`](@ref), and [`start`](@ref).
"""
wrap(rf::T, state, iresult) where {T} = PrivateState(rf, state, iresult)
wrap(rf, state, iresult::Reduced) =
    Reduced(PrivateState(rf, state, unreduced(iresult)))

"""
    wrapping(f, rf, result)

Function `f` must take two argument `state` and `iresult`, and return
a tuple `(state, iresult)`.  This is intended to be used only in
[`next`](@ref), possibly with a `do` block.

```julia
next(rf::R_{MyTransducer}, result, input) =
    wrapping(rf, result) do my_state, iresult
        # code calling `next(rf.inner, iresult, possibly_modified_input)`
        return my_state, iresult  # possibly modified
    end
```

See [`wrap`](@ref), [`unwrap`](@ref), and [`next`](@ref).
"""
@inline function wrapping(f, rf, result)
    state0, iresult0 = unwrap(rf, result)
    state1, iresult1 = f(state0, iresult0)
    return wrap(rf, state1, iresult1)
end
# TODO: Should `wrapping` happen automatically in `next`?  That is to
# say, how about let `__next__(rf, iresult, state, input)` be the
# interface function and `next(rf, result, input)` be the calling API.

unwrap_all(ps::PrivateState) = unwrap_all(ps.result)
unwrap_all(result) = result
unwrap_all(ps::Reduced) = Reduced(unwrap_all(unreduced(ps)))

"""
    outtype(xf::Transducer, intype)

Output item type for the transducer `xf` when the input type is `intype`.
"""
outtype(::Any, ::Any) = Any
outtype(::AbstractFilter, intype) = intype

finaltype(rf::Reduction{<:Transducer, <:Reduction}) = finaltype(rf.inner)
finaltype(rf::Reduction) = outtype(rf.xform, InType(rf))

"""
    Completing(function)

Wrap a `function` to add a no-op [`complete`](@ref) protocol.  Use it
when passing a `function` without 1-argument arity to
[`transduce`](@ref) etc.

$(_thx_clj("completing"))
"""
struct Completing{F}  # Note: not a Transducer
    f::F
end

start(rf::Completing, result) = start(rf.f, result)
next(rf::Completing, result, input)  = next(rf.f, result, input)
complete(::Completing, result) = result

struct SideEffect{F}  # Note: not a Transducer
    f::F
end

# Completing(rf::SideEffect) = rf

start(rf::SideEffect, result) = start(rf.f, result)
complete(::SideEffect, result) = result
function next(rf::SideEffect, result, input)
    rf.f(input)
    return result
end


text:
using WeaklyHard
"""
Example for generating satisfaction sets for weakly-hard constraints
"""

# Constraints
lambda1 = AnyHitConstraint(12, 15)
lambda2 = RowHitConstraint(15, 47)
lambda3 = RowMissConstraint(3)

# Generate automata
G1 = build_automaton(lambda1)
G2 = build_automaton(lambda2)
G3 = build_automaton(lambda3)

# Generate satisfaction set of length N
N = 20
S1 = all_sequences(G1, N)
S2 = all_sequences(G2, N)
S3 = all_sequences(G3, N)

# check that all sequences satisfy the constraint
for w in S1
    if !is_satisfied(lambda1, w)
        println("Sequence ($(bitstring(w)[end-N+1:end])) does not satisfy $lambda1")
    end # if
end # for
for w in S2
    if !is_satisfied(lambda2, w)
        println("Sequence ($(bitstring(w)[end-N+1:end])) does not satisfy $lambda2")
    end # if
end # for
for w in S3
    if !is_satisfied(lambda3, w)
        println("Sequence ($(bitstring(w)[end-N+1:end])) does not satisfy $lambda3")
    end # if
end # for


text:
const REV_SUFIX = "_REV"
function invert_bkwds!(metnet::MetNet; rename::Function = (rxn) -> string(rxn, REV_SUFIX))
    for bkwd_rxn in bkwds_bounded(metnet)
        invert_rxn!(metnet, bkwd_rxn; rename)
    end
    return metnet
end
invert_bkwds(metnet::MetNet; rename::Function = (rxn) -> rxn) =
    invert_bkwds!(deepcopy(metnet); rename)


text:
module Utils

using Reexport
include("elementfeature_utils.jl")
@reexport using .ElementFeatureUtils

include("graph_building.jl")
@reexport using .GraphBuilding

end


text:
developing = false; # are you developing the package or just using?
if developing
	# ] add Revise # if you don't have it
    path = pwd()
    if path[end-6:end] != "MatLang"
        error("cd(to the MatLang path)")
    end
    push!(LOAD_PATH, ".")
    using Revise
    using MatLang
else
    using MatLang
end
################################################################
################################################################

################################################################
clcM() # clears console
################################################################


text:
_view(::Nothing, i) = nothing
_view(A::Fill{T,2,Axes}, i) where {T,Axes} = view(A, :, 1)
_view(A::AbstractMatrix, idx) = view(A, :, idx)

aggregate(aggr::typeof(+), X) = vec(sum(X, dims=2))
aggregate(aggr::typeof(-), X) = -vec(sum(X, dims=2))
aggregate(aggr::typeof(*), X) = vec(prod(X, dims=2))
aggregate(aggr::typeof(/), X) = 1 ./ vec(prod(X, dims=2))
aggregate(aggr::typeof(max), X) = vec(maximum(X, dims=2))
aggregate(aggr::typeof(min), X) = vec(minimum(X, dims=2))
aggregate(aggr::typeof(mean), X) = vec(aggr(X, dims=2))

abstract type GraphNet <: AbstractGraphLayer end

@inline update_edge(gn::GraphNet, e, vi, vj, u) = e
@inline update_vertex(gn::GraphNet, ē, vi, u) = vi
@inline update_global(gn::GraphNet, ē, v̄, u) = u

@inline update_batch_edge(gn::GraphNet, sg::SparseGraph, E, V, u) =
    mapreduce(i -> apply_batch_message(gn, sg, i, neighbors(sg, i), E, V, u), hcat, vertices(sg))

@inline apply_batch_message(gn::GraphNet, sg::SparseGraph, i, js, E, V, u) =
    mapreduce(j -> update_edge(gn, _view(E, edge_index(sg, i, j)), _view(V, i), _view(V, j), u), hcat, js)

@inline update_batch_vertex(gn::GraphNet, Ē, V, u) =
    mapreduce(i -> update_vertex(gn, _view(Ē, i), _view(V, i), u), hcat, 1:size(V,2))

@inline aggregate_neighbors(gn::GraphNet, sg::SparseGraph, aggr, E) = neighbor_scatter(aggr, E, sg)
@inline aggregate_neighbors(gn::GraphNet, sg::SparseGraph, aggr::Nothing, @nospecialize E) = nothing

@inline aggregate_edges(gn::GraphNet, aggr, E) = aggregate(aggr, E)
@inline aggregate_edges(gn::GraphNet, aggr::Nothing, @nospecialize E) = nothing

@inline aggregate_vertices(gn::GraphNet, aggr, V) = aggregate(aggr, V)
@inline aggregate_vertices(gn::GraphNet, aggr::Nothing, @nospecialize V) = nothing

function propagate(gn::GraphNet, fg::FeaturedGraph, naggr=nothing, eaggr=nothing, vaggr=nothing)
    E, V, u = propagate(gn, graph(fg), edge_feature(fg), node_feature(fg), global_feature(fg), naggr, eaggr, vaggr)
    FeaturedGraph(fg, nf=V, ef=E, gf=u)
end

function propagate(gn::GraphNet, sg::SparseGraph, E, V, u, naggr=nothing, eaggr=nothing, vaggr=nothing)
    E = update_batch_edge(gn, sg, E, V, u)
    Ē = aggregate_neighbors(gn, sg, naggr, E)
    V = update_batch_vertex(gn, Ē, V, u)
    ē = aggregate_edges(gn, eaggr, E)
    v̄ = aggregate_vertices(gn, vaggr, V)
    u = update_global(gn, ē, v̄, u)
    return E, V, u
end


text:
mutable struct DAC
    N::Int64
    T::Float64
end
getproperty(x::Vector{DAC}, f::Symbol) = getproperty.(x,f)

function get_sample_times(seq)
    T = seq.DAC.T
    t0 = cumsum([0; T[1:end-1]])
    tf = t0 .+ T
    N = seq.DAC.N
    times = []
    for i = 1:length(T)
        if N[i] > 0
            t = range(t0[i], tf[i], length=N[i])
            append!(times, t)
        end
    end
    times
end


text:
@testset "NoInterp" begin
    a = reshape(1:12, 3, 4)
    ai = interpolate(a, NoInterp())
    @test eltype(ai) == Int
    check_axes(ai, a)
    check_inbounds_values(ai, a)
    check_oob(ai)
    @test_throws InexactError ai(2.2, 2)
    @test_throws InexactError ai(2, 2.2)

    # ae = extrapolate(ai, NaN)
    # @test eltype(ae) == Float64
    # @test ae[1,1] === 1.0
    # @test ae[0,1] === NaN
    # @test_throws InexactError ae(1.5,2)
end


text:
# Suppress warnings during testing.
# required for reducing logging during tests
using Memento
setlevel!(getlogger(PowerModels), "error")

# is this the best way to find a file in a package?
base_dir = string(dirname(dirname(pathof(PowerSystems))))
case5_data = PM.parse_file(joinpath(base_dir, "data/matpower/case5.m"))
case5_data = PM.replicate(case5_data, 2)

case5_dc_data = PM.parse_file(joinpath(base_dir, "data/matpower/case5_dc.m"))
case5_dc_data = PM.replicate(case5_dc_data, 2)

# TODO: currently JuMP.num_variables is the best we can do to introspect the JuMP model.
#  Ideally this would also test the number of constraints generated

@testset "PowerModels Model Build" begin
    pm = PowerSimulations.instantiate_nip_model(case5_data, DCPPowerModel)
    @test JuMP.num_variables(pm.model) == 34
    pm = PowerSimulations.instantiate_nip_model(case5_data, PM.ACPPowerModel)
    @test JuMP.num_variables(pm.model) == 96
    pm = PowerSimulations.instantiate_nip_model(case5_data, PM.SOCWRPowerModel)
    @test JuMP.num_variables(pm.model) == 110
end

@testset "PM with type extensions" begin
    pm = PowerSimulations.instantiate_nip_model(case5_dc_data, DCPPowerModel)
    JuMP.num_variables(pm.model) == 34 # Repetitive?
    pm = PowerSimulations.instantiate_nip_model(case5_dc_data, ACPPowerModel)
    JuMP.num_variables(pm.model) == 96
    pm = PowerSimulations.instantiate_nip_model(case5_dc_data, DCPPowerModel)
    @test JuMP.num_variables(pm.model) == 36
    pm = PowerSimulations.instantiate_nip_model(case5_dc_data, DCPPowerModel)
    @test JuMP.num_variables(pm.model) == 36 # 48 was originally here, verify??
end


text:
"""
    assert(x::Bool)

Assert that the condition generating `x` holds true.
Returns `x`

"""
assert(x::Bool) = x

"""
    prove(x::Bool)

```julia
assert(x < 10)
y = x - 10
prove(y < 0)
```

Will turn into the corresponding SMT2 program:

```
(declare-const x Int)
(declare-const y Int)
(assert (< x 10))
(assert (= y (- x 10)))
(assert (not (< y 0)))
(check-sat)
```

If the model is not satisfiable, given the pre-condition defined by `assert`
the post-condition given by `prove` holds.

For more information see `check`.
"""
prove(x::Bool) = x

# Define a Cassette pass to insert asserts after branches
function insertasserts(::Type{<:TraceCtx}, ::Type{S}, ir::Core.CodeInfo) where {S}
    locations = Int[]
    Cassette.insert_statements!(ir.code, ir.codelocs,
        (stmt, i) -> Base.Meta.isexpr(stmt, :gotoifnot) ? 2 : nothing,
        (stmt, i) -> [:($assert($(stmt.args[1]))),stmt])
    return ir

end

const InsertAssertsPass = Cassette.@pass insertasserts

# We can't use primitives since otherwise our tracer won't trace assert and prove statements
# Which is the entire point.
function Cassette.execute(ctx::TraceCtx, f::F, x::Tagged) where F <: Union{Core.typeof(assert), Core.typeof(prove)}
    call = Callsite(f, record((x, ), ctx))
    retval = untag(x, ctx)
    call.retval = Some(retval)
    push!(ctx.metadata, call)
    return retval
end


text:
module GameTheory

# stdlib
using LinearAlgebra, Random

# Packages
using QuantEcon
using Combinatorics
using Parameters

# Optimization packages
using MathOptInterface
const MOI = MathOptInterface
const MOIU = MOI.Utilities
using Clp

# Geometry packages
using Polyhedra

# Type aliases #

"""
    PureAction

Alias for `Integer`.
"""
const PureAction = Integer

"""
    MixedAction{T}

Alias for `Vector{T}` where `T<:Real`.
"""
MixedAction{T<:Real} = Vector{T}

"""
    Action{T}

Alias for `Union{PureAction,MixedAction{T}}` where `T<:Real`.
"""
Action{T<:Real} = Union{PureAction,MixedAction{T}}

"""
    PureActionProfile{N,T}

Alias for `NTuple{N,T}` where `T<:PureAction`.
"""
PureActionProfile{N,T<:PureAction} = NTuple{N,T}

"""
    MixedActionProfile{T,N}

Alias for `NTuple{N,MixedAction{T}}` where `T<:Real`.
"""
MixedActionProfile{T<:Real,N} = NTuple{N,MixedAction{T}}

"""
    ActionProfile

Alias for `Union{PureActionProfile,MixedActionProfile}`.
"""
const ActionProfile = Union{PureActionProfile,MixedActionProfile}

const RatOrInt = Union{Rational,Int}

# package code goes here
include("normal_form_game.jl")
include("lrsnash.jl")
include("pure_nash.jl")
include("repeated_game.jl")
include("random.jl")
include("support_enumeration.jl")
include("util.jl")
include("generators/Generators.jl")

export
    # Types
    Player, NormalFormGame,

    # Type aliases
    Action, MixedAction, PureAction, ActionProfile,

    # Normal form game functions
    best_response, best_responses, is_best_response, payoff_vector,
    is_nash, pure2mixed, pure_strategy_NE, is_pareto_efficient,
    is_pareto_dominant, is_dominated, dominated_actions, delete_action,

    # General functions
    num_players, num_actions, num_opponents,

    # Utilities
    BROptions,

    # Nash Equilibrium
    pure_nash,

    # Repeated Games
    RepeatedGame, unpack, flow_u_1, flow_u_2, flow_u, best_dev_i,
    best_dev_1, best_dev_2, best_dev_payoff_i, best_dev_payoff_1,
    best_dev_payoff_2, worst_value_i, worst_value_1, worst_value_2,
    worst_values, outerapproximation,

    # Random Games
    random_game, covariance_game,
    random_pure_actions, random_mixed_actions,

    # Support Enumeration
    support_enumeration, support_enumeration_task,

    # LRS
    lrsnash

end # module


text:
module Animations

using Dates
using Random
using GameOne

const SDL2 = GameOne.SDL2
AN_list = [:spin, :shake, :fade, :grow, :shrink, :squish]

function bomb_card(a::Actor)
    then = now()
end

function shake_actor!(a::Actor, f=2)
    if a.angle > 0
        a.angle -= ceil(Int32, a.angle / f)
    elseif a.angle < 0
        a.angle += ceil(Int32, abs(a.angle) / f)
    else
        a.angle = rand(-15:15)
    end
end

function spin_actor!(a::Actor; dθ=2)
    a.angle += a.data[:spin_cw] ? dθ : -dθ
end

function squish_actor(a::Actor, dir=:horizontal, limit=10, f=10)
	if dir == :horizontal
		if a.w > limit
			a.w -= f
		end
	elseif dir == :vertical
		if a.h > limit
			a.h -= f
		end
	end
end

function shrink_actor!(a::Actor, h::Number, w::Number, f=25)
	if a.h > h / 2.5 || a.w > w / 2.5
        a.w -= ceil(Int32, w / f)
        a.h -= ceil(Int32, h / f)
    end
end

function grow_actor!(a::Actor, oh::Number, ow::Number, f=25)
	if a.h < oh * 2.5 || a.w < ow * 2.5
        a.w += ceil(Int32, ow / f)
        a.h += ceil(Int32, oh / f)
    end
end

function fade_actor!(a::Actor, f::Int=2)
    if a.data[:fade_out]
        if a.alpha - f < 0
            a.alpha = 0
            a.data[:fade] = false
            a.data[:fade_out] = false
        else
            a.alpha -= f
        end
    else
        if a.alpha + f > 255
            a.alpha = 255
            a.data[:fade] = false
            a.data[:fade_out] = true
        else
            a.alpha += f
        end
    end
end

function reset_actor!(a::Actor, h::Int32, w::Int32)
	a.w = w
	a.h = h
    a.angle = 0

    for AN_sym in AN_list
        a.data[AN_sym] = false
    end

	return a
end

function change_face!(o::T) where T
	@show "Changing face of $(o.name)!"
	o.faces = circshift(o.faces, -1)
	o.faces[begin].angle = o.faces[end].angle
	return o
end

function splay_actors!(actors::Vector{Actor}, x::Int32, y::Int32,
    SCREEN_HEIGHT::Int32, SCREEN_BORDER::Int32; pitch=Float64[1, 1])

    for a in actors

        if pitch[2] < 0 && y < SCREEN_BORDER
            y = SCREEN_HEIGHT - SCREEN_BORDER - a.h * a.scale[2]
            x += ceil(Int32, a.w * pitch[1] > 0 ? 1.25 : -1.25)
        end

        if pitch[2] > 0 && y + a.h * a.scale[2] > SCREEN_HEIGHT - SCREEN_BORDER
            y = SCREEN_BORDER
            @show x += ceil(Int32, pitch[1] > 0 ? 0.55a.w : -0.55a.w)
        end

        a.x = x
        a.y = y

        x += ceil(Int32, a.w * a.scale[1] * pitch[1])
        y += ceil(Int32, a.h * a.scale[2] * pitch[2])
    end

	return actors
end

function update_text_actor!(a::Actor, new_text::String)
    font = SDL2.TTF_OpenFont(a.data[:font_path], a.data[:pt_size])
    a.surfaces = [ SDL2.TTF_RenderText_Blended_Wrapped(font, new_text,
        SDL2.Color(a.data[:font_color]...), UInt32(a.data[:wrap_length])) ]
    a.w, a.h = size(a.surfaces[begin])
    a.label = new_text
    a.textures = []
    return a
end

function next_frame!(a::Actor)
    a.textures = circshift(a.textures, -1)
    a.data[:then] = now()
	return a
end

end # module

max_stars_repo_name: dustyirwin/PlaymatSimulator

max_issues_repo_name: dustyirwin/PlaymatSimulator

max_forks_repo_name: dustyirwin/PlaymatSimulator