text:
@testset "ThermalGen data misspecification" begin
    # See https://discourse.julialang.org/t/how-to-use-test-warn/15557/5 about testing for warning throwing
    warn_message = "The data doesn't include devices of type ThermalStandard, consider changing the device models"
    model = DeviceModel(ThermalStandard, ThermalStandardUnitCommitment)
    c_sys5_re_only = build_system("c_sys5_re_only")
    op_problem =
        OperationsProblem(TestOpProblem, DCPPowerModel, build_system("c_sys5_re_only"))
    @test_logs (:warn, warn_message) construct_device!(op_problem, :Thermal, model)
end

################################### Unit Commitment tests ##################################
@testset "Thermal UC With DC - PF" begin
    bin_variable_names = [
        PSI.variable_name(PSI.ON, PSY.ThermalStandard),
        PSI.variable_name(PSI.START, PSY.ThermalStandard),
        PSI.variable_name(PSI.STOP, PSY.ThermalStandard),
    ]
    uc_constraint_names = [
        PSI.constraint_name(PSI.RAMP_UP, PSY.ThermalStandard),
        PSI.constraint_name(PSI.RAMP_DOWN, PSY.ThermalStandard),
        PSI.constraint_name(PSI.DURATION_UP, PSY.ThermalStandard),
        PSI.constraint_name(PSI.DURATION_DOWN, PSY.ThermalStandard),
    ]
    model = DeviceModel(ThermalStandard, ThermalStandardUnitCommitment)

    @info "5-Bus testing"
    c_sys5_uc = build_system("c_sys5_uc")
    op_problem = OperationsProblem(TestOpProblem, DCPPowerModel, c_sys5_uc)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, false, 480, 0, 480, 120, 120, true)
    psi_constraint_test(op_problem, uc_constraint_names)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    op_problem =
        OperationsProblem(TestOpProblem, DCPPowerModel, c_sys5_uc; use_parameters = true)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, true, 480, 0, 480, 120, 120, true)
    psi_constraint_test(op_problem, uc_constraint_names)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 480, 0, 240, 120, 120, true)
        psi_checkbinvar_test(op_problem, bin_variable_names)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

@testset "Thermal UC With AC - PF" begin
    bin_variable_names = [
        PSI.variable_name(PSI.ON, PSY.ThermalStandard),
        PSI.variable_name(PSI.START, PSY.ThermalStandard),
        PSI.variable_name(PSI.STOP, PSY.ThermalStandard),
    ]
    uc_constraint_names = [
        PSI.constraint_name(PSI.RAMP_UP, PSY.ThermalStandard),
        PSI.constraint_name(PSI.RAMP_DOWN, PSY.ThermalStandard),
        PSI.constraint_name(PSI.DURATION_UP, PSY.ThermalStandard),
        PSI.constraint_name(PSI.DURATION_DOWN, PSY.ThermalStandard),
    ]
    model = DeviceModel(ThermalStandard, ThermalStandardUnitCommitment)

    @info "5-Bus testing"
    c_sys5_uc = build_system("c_sys5_uc")
    op_problem = OperationsProblem(TestOpProblem, ACPPowerModel, c_sys5_uc)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, false, 600, 0, 600, 240, 120, true)
    psi_constraint_test(op_problem, uc_constraint_names)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    op_problem =
        OperationsProblem(TestOpProblem, ACPPowerModel, c_sys5_uc; use_parameters = true)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, true, 600, 0, 600, 240, 120, true)
    psi_constraint_test(op_problem, uc_constraint_names)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 600, 0, 360, 240, 120, true)
        psi_checkbinvar_test(op_problem, bin_variable_names)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

################################### Basic Unit Commitment tests ############################
@testset "Thermal Basic UC With DC - PF" begin
    bin_variable_names = [
        PSI.variable_name(PSI.ON, PSY.ThermalStandard),
        PSI.variable_name(PSI.START, PSY.ThermalStandard),
        PSI.variable_name(PSI.STOP, PSY.ThermalStandard),
    ]
    model = DeviceModel(ThermalStandard, ThermalBasicUnitCommitment)

    @info "5-Bus testing"
    c_sys5_uc = build_system("c_sys5_uc")
    op_problem = OperationsProblem(TestOpProblem, DCPPowerModel, c_sys5_uc)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, false, 480, 0, 240, 120, 120, true)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    op_problem =
        OperationsProblem(TestOpProblem, DCPPowerModel, c_sys5_uc; use_parameters = true)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, true, 480, 0, 240, 120, 120, true)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 480, 0, 240, 120, 120, true)
        psi_checkbinvar_test(op_problem, bin_variable_names)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

@testset "Thermal Basic UC With AC - PF" begin
    bin_variable_names = [
        PSI.variable_name(PSI.ON, PSY.ThermalStandard),
        PSI.variable_name(PSI.START, PSY.ThermalStandard),
        PSI.variable_name(PSI.STOP, PSY.ThermalStandard),
    ]
    model = DeviceModel(ThermalStandard, ThermalBasicUnitCommitment)

    @info "5-Bus testing"
    c_sys5_uc = build_system("c_sys5_uc")
    op_problem = OperationsProblem(TestOpProblem, ACPPowerModel, c_sys5_uc)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, false, 600, 0, 360, 240, 120, true)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    op_problem =
        OperationsProblem(TestOpProblem, ACPPowerModel, c_sys5_uc; use_parameters = true)
    construct_device!(op_problem, :Thermal, model)
    moi_tests(op_problem, true, 600, 0, 360, 240, 120, true)
    psi_checkbinvar_test(op_problem, bin_variable_names)
    psi_checkobjfun_test(op_problem, GAEVF)

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 600, 0, 360, 240, 120, true)
        psi_checkbinvar_test(op_problem, bin_variable_names)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

################################### Basic Dispatch tests ###################################
@testset "Thermal Dispatch With DC - PF" begin
    model = DeviceModel(ThermalStandard, ThermalDispatch)
    @info "5-Bus testing"
    c_sys5 = build_system("c_sys5")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys5; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 120, 0, 120, 120, 0, false)
        psi_checkobjfun_test(op_problem, GAEVF)
    end

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 120, 0, 120, 120, 0, false)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

@testset "Thermal Dispatch With AC - PF" begin
    model = DeviceModel(ThermalStandard, ThermalDispatch)
    @info "5-Bus testing"
    c_sys5 = build_system("c_sys5")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys5; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 240, 0, 240, 240, 0, false)
        psi_checkobjfun_test(op_problem, GAEVF)
    end

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 240, 0, 240, 240, 0, false)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

################################### No Minimum Dispatch tests ##############################

@testset "Thermal Dispatch NoMin With DC - PF" begin
    model = DeviceModel(ThermalStandard, ThermalDispatchNoMin)
    @info "5-Bus testing"
    c_sys5 = build_system("c_sys5")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys5; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 120, 0, 120, 120, 0, false)
        moi_lbvalue_test(
            op_problem,
            PSI.constraint_name(PSI.ACTIVE_RANGE_LB, PSY.ThermalStandard),
            0.0,
        )
        psi_checkobjfun_test(op_problem, GAEVF)
    end

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 120, 0, 120, 120, 0, false)
        moi_lbvalue_test(
            op_problem,
            PSI.constraint_name(PSI.ACTIVE_RANGE_LB, PSY.ThermalStandard),
            0.0,
        )
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

@testset "Thermal Dispatch NoMin With AC - PF" begin
    model = DeviceModel(ThermalStandard, ThermalDispatchNoMin)
    @info "5-Bus testing"
    c_sys5 = build_system("c_sys5")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys5; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 240, 0, 240, 240, 0, false)
        moi_lbvalue_test(
            op_problem,
            PSI.constraint_name(PSI.ACTIVE_RANGE_LB, PSY.ThermalStandard),
            0.0,
        )
        psi_checkobjfun_test(op_problem, GAEVF)
    end

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 240, 0, 240, 240, 0, false)
        moi_lbvalue_test(
            op_problem,
            PSI.constraint_name(PSI.ACTIVE_RANGE_LB, PSY.ThermalStandard),
            0.0,
        )
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

################################### Ramp Limited Testing ##################################
@testset "Thermal Ramp Limited Dispatch With DC - PF" begin
    constraint_names = [
        PSI.constraint_name(PSI.RAMP_UP, PSY.ThermalStandard),
        PSI.constraint_name(PSI.RAMP_DOWN, PSY.ThermalStandard),
    ]
    model = DeviceModel(ThermalStandard, ThermalRampLimited)
    @info "5-Bus testing"
    c_sys5_uc = build_system("c_sys5_uc")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys5_uc; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 120, 0, 216, 120, 0, false)
        psi_constraint_test(op_problem, constraint_names)
        psi_checkobjfun_test(op_problem, GAEVF)
    end

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, DCPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 120, 0, 120, 120, 0, false)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

@testset "Thermal Ramp Limited Dispatch With AC - PF" begin
    constraint_names = [
        PSI.constraint_name(PSI.RAMP_UP, PSY.ThermalStandard),
        PSI.constraint_name(PSI.RAMP_DOWN, PSY.ThermalStandard),
    ]
    model = DeviceModel(ThermalStandard, ThermalRampLimited)
    @info "5-Bus testing"
    c_sys5_uc = build_system("c_sys5_uc")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys5_uc; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 240, 0, 336, 240, 0, false)
        psi_constraint_test(op_problem, constraint_names)
        psi_checkobjfun_test(op_problem, GAEVF)
    end

    @info "14-Bus testing"
    c_sys14 = build_system("c_sys14")
    for p in [true, false]
        op_problem =
            OperationsProblem(TestOpProblem, ACPPowerModel, c_sys14; use_parameters = p)
        construct_device!(op_problem, :Thermal, model)
        moi_tests(op_problem, p, 240, 0, 240, 240, 0, false)
        psi_checkobjfun_test(op_problem, GQEVF)
    end
end

############################# UC validation tests ##########################################
branches = Dict{Symbol, DeviceModel}()
services = Dict{Symbol, ServiceModel}()
ED_devices = Dict{Symbol, DeviceModel}(
    :Generators => DeviceModel(ThermalStandard, ThermalRampLimited),
    :Loads => DeviceModel(PowerLoad, StaticPowerLoad),
)
UC_devices = Dict{Symbol, DeviceModel}(
    :Generators => DeviceModel(ThermalStandard, ThermalStandardUnitCommitment),
    :Loads => DeviceModel(PowerLoad, StaticPowerLoad),
)
# Testing Ramping Constraint
@testset "Solving ED with CopperPlate for testing Ramping Constraints" begin
    node = Bus(1, "nodeA", "PV", 0, 1.0, (min = 0.9, max = 1.05), 230, nothing, nothing)
    load = PowerLoad("Bus1", true, node, nothing, 0.4, 0.9861, 1.0, 2.0)
    DA_ramp = collect(
        DateTime("1/1/2024  0:00:00", "d/m/y  H:M:S"):Hour(1):DateTime(
            "1/1/2024  4:00:00",
            "d/m/y  H:M:S",
        ),
    )
    gen_ramp = [
        ThermalStandard(
            "Alta",
            true,
            true,
            node,
            0.20,
            0.010,
            0.5,
            PrimeMovers.ST,
            ThermalFuels.COAL,
            (min = 0.0, max = 0.40),
            nothing,
            nothing,
            nothing,
            ThreePartCost((0.0, 1400.0), 0.0, 4.0, 2.0),
        ),
        ThermalStandard(
            "Park City",
            true,
            true,
            node,
            0.70,
            0.20,
            2.0,
            PrimeMovers.ST,
            ThermalFuels.COAL,
            (min = 0.7, max = 2.20),
            nothing,
            (up = 0.010625, down = 0.010625),
            nothing,
            ThreePartCost((0.0, 1500.0), 0.0, 1.5, 0.75),
        ),
    ]
    ramp_load = [0.9, 1.1, 2.485, 2.175, 0.9]
    load_forecast_ramp = Deterministic("get_maxactivepower", TimeArray(DA_ramp, ramp_load))
    ramp_test_sys = System(100.0)
    add_component!(ramp_test_sys, node)
    add_component!(ramp_test_sys, load)
    add_component!(ramp_test_sys, gen_ramp[1])
    add_component!(ramp_test_sys, gen_ramp[2])
    add_forecast!(ramp_test_sys, load, load_forecast_ramp)

    template =
        OperationsProblemTemplate(CopperPlatePowerModel, ED_devices, branches, services)
    ED = OperationsProblem(
        TestOpProblem,
        template,
        ramp_test_sys;
        optimizer = Cbc_optimizer,
        use_parameters = true,
    )
    psi_checksolve_test(ED, [MOI.OPTIMAL], 11191.00)
    moi_tests(ED, true, 10, 0, 20, 10, 5, false)
end

#= Disabled temporarily due to the elimination of initial conditions passing
# Testing Duration Constraints
@testset "Solving UC with CopperPlate for testing Duration Constraints" begin
    node = Bus(1, "nodeA", "PV", 0, 1.0, (min = 0.9, max = 1.05), 230, nothing, nothing)
    load = PowerLoad("Bus1", true, node, nothing, 0.4, 0.9861, 1.0, 2.0)
    DA_dur = collect(
        DateTime("1/1/2024  0:00:00", "d/m/y  H:M:S"):Hour(1):DateTime(
            "1/1/2024  6:00:00",
            "d/m/y  H:M:S",
        ),
    )
    gens_dur = [
        ThermalStandard(
            "Alta",
            true,
            true,
            node,
            0.40,
            0.010,
                0.5,
                PrimeMovers.ST,
                ThermalFuels.COAL,
                (min = 0.3, max = 0.9),
                nothing,
                nothing,
                (up = 4, down = 2),
            ThreePartCost((0.0, 1400.0), 0.0, 4.0, 2.0),
        ),
        ThermalStandard(
            "Park City",
            true,
            true,
            node,
            1.70,
            0.20,
                2.2125,
                PrimeMovers.ST,
                ThermalFuels.COAL,
                (min = 0.7, max = 2.2),
                nothing,
                nothing,
                (up = 6, down = 4),
            ThreePartCost((0.0, 1500.0), 0.0, 1.5, 0.75),
        ),
    ]

    duration_load = [0.3, 0.6, 0.8, 0.7, 1.7, 0.9, 0.7]
    load_forecast_dur =
        Deterministic("get_maxactivepower", TimeArray(DA_dur, duration_load))
    duration_test_sys = System(100.0)
    add_component!(duration_test_sys, node)
    add_component!(duration_test_sys, load)
    add_component!(duration_test_sys, gens_dur[1])
    add_component!(duration_test_sys, gens_dur[2])
    add_forecast!(duration_test_sys, load, load_forecast_dur)

    status = [1.0, 0.0]
    up_time = [2.0, 0.0]
    down_time = [0.0, 3.0]

    alta = gens_dur[1]
    init_cond = PSI.InitialConditions()
    PSI.set_initial_conditions!(
        init_cond,
        PSI.ICKey(DeviceStatus, typeof(alta)),
        build_init(gens_dur, status),
    )
    PSI.set_initial_conditions!(
        init_cond,
        PSI.ICKey(TimeDurationON, typeof(alta)),
        build_init(gens_dur, up_time),
    )
    PSI.set_initial_conditions!(
        init_cond,
        PSI.ICKey(TimeDurationOFF, typeof(alta)),
        build_init(gens_dur, down_time),
    )

    template =
        OperationsProblemTemplate(CopperPlatePowerModel, UC_devices, branches, services)
    UC = OperationsProblem(
        TestOpProblem,
        template,
        duration_test_sys;
        optimizer = Cbc_optimizer,
        use_parameters = true,
    )
    psi_checksolve_test(UC, [MOI.OPTIMAL], 8223.50)
    moi_tests(UC, true, 56, 0, 56, 14, 21, true)
end
=#

## PWL linear Cost implementation test
@testset "Solving UC with CopperPlate testing Linear PWL" begin
    node = Bus(1, "nodeA", "PV", 0, 1.0, (min = 0.9, max = 1.05), 230, nothing, nothing)
    load = PowerLoad("Bus1", true, node, nothing, 0.4, 0.9861, 1.0, 2.0)
    gens_cost = [
        ThermalStandard(
            "Alta",
            true,
            true,
            node,
            0.52,
            0.010,
            0.5,
            PrimeMovers.ST,
            ThermalFuels.COAL,
            (min = 0.22, max = 0.55),
            nothing,
            nothing,
            nothing,
            ThreePartCost(
                [(589.99, 0.220), (884.99, 0.33), (1210.04, 0.44), (1543.44, 0.55)],
                532.44,
                5665.23,
                0.0,
            ),
        ),
        ThermalStandard(
            "Park City",
            true,
            true,
            node,
            0.62,
            0.20,
            2.2125,
            PrimeMovers.ST,
            ThermalFuels.COAL,
            (min = 0.62, max = 1.55),
            nothing,
            nothing,
            nothing,
            ThreePartCost(
                [(1264.80, 0.62), (1897.20, 0.93), (2594.4787, 1.24), (3433.04, 1.55)],
                235.397,
                5665.23,
                0.0,
            ),
        ),
    ]
    DA_cost = collect(
        DateTime("1/1/2024  0:00:00", "d/m/y  H:M:S"):Hour(1):DateTime(
            "1/1/2024  1:00:00",
            "d/m/y  H:M:S",
        ),
    )
    cost_load = [1.3, 2.1]
    load_forecast_cost = Deterministic("get_maxactivepower", TimeArray(DA_cost, cost_load))
    cost_test_sys = System(100.0)
    add_component!(cost_test_sys, node)
    add_component!(cost_test_sys, load)
    add_component!(cost_test_sys, gens_cost[1])
    add_component!(cost_test_sys, gens_cost[2])
    add_forecast!(cost_test_sys, load, load_forecast_cost)

    template =
        OperationsProblemTemplate(CopperPlatePowerModel, UC_devices, branches, services)
    UC = OperationsProblem(
        TestOpProblem,
        template,
        cost_test_sys;
        optimizer = Cbc_optimizer,
        use_parameters = true,
    )
    psi_checksolve_test(UC, [MOI.OPTIMAL], 9336.736919354838)
    moi_tests(UC, true, 32, 0, 8, 4, 10, true)
end

## PWL SOS-2 Cost implementation test
@testset "Solving UC with CopperPlate testing SOS2 implementation" begin
    node = Bus(1, "nodeA", "PV", 0, 1.0, (min = 0.9, max = 1.05), 230, nothing, nothing)
    load = PowerLoad("Bus1", true, node, nothing, 0.4, 0.9861, 1.0, 2.0)
    gens_cost_sos = [
        ThermalStandard(
            "Alta",
            true,
            true,
            node,
            0.52,
            0.010,
            0.5,
            PrimeMovers.ST,
            ThermalFuels.COAL,
            (min = 0.22, max = 0.55),
            nothing,
            nothing,
            nothing,
            ThreePartCost(
                [(1122.43, 0.22), (1617.43, 0.33), (1742.48, 0.44), (2075.88, 0.55)],
                0.0,
                5665.23,
                0.0,
            ),
        ),
        ThermalStandard(
            "Park City",
            true,
            true,
            node,
            0.62,
            0.20,
            2.2125,
            PrimeMovers.ST,
            ThermalFuels.COAL,
            (min = 0.62, max = 1.55),
            nothing,
            nothing,
            nothing,
            ThreePartCost(
                [(1500.19, 0.62), (2132.59, 0.929), (2829.875, 1.24), (2831.444, 1.55)],
                0.0,
                5665.23,
                0.0,
            ),
        ),
    ]
    DA_cost_sos = collect(
        DateTime("1/1/2024  0:00:00", "d/m/y  H:M:S"):Hour(1):DateTime(
            "1/1/2024  1:00:00",
            "d/m/y  H:M:S",
        ),
    )
    cost_sos_load = [1.3, 2.1]
    load_forecast_cost_sos =
        Deterministic("get_maxactivepower", TimeArray(DA_cost_sos, cost_sos_load))
    cost_test_sos_sys = System(100.0)
    add_component!(cost_test_sos_sys, node)
    add_component!(cost_test_sos_sys, load)
    add_component!(cost_test_sos_sys, gens_cost_sos[1])
    add_component!(cost_test_sos_sys, gens_cost_sos[2])
    add_forecast!(cost_test_sos_sys, load, load_forecast_cost_sos)

    for g in gens_cost_sos
        @test PSI._pwlparamcheck(PSY.get_op_cost(g).variable) == false
    end

    template =
        OperationsProblemTemplate(CopperPlatePowerModel, UC_devices, branches, services)
    UC = OperationsProblem(
        TestOpProblem,
        template,
        cost_test_sos_sys;
        optimizer = Cbc_optimizer,
        use_parameters = true,
    )
    psi_checksolve_test(UC, [MOI.OPTIMAL], 8500.89716, 10.0)
    moi_tests(UC, true, 32, 0, 8, 4, 14, true)
end


text:
@testset "equal_windows" begin
    @test collect(equal_windows(3,2)) == [1]
    @test collect(equal_windows(2,1)) == [1,2]
end
@testset "STTR" begin
    @test sttr(["a","b","a","c","c"],3) == 0.6666666666666666
end

ext: jl


text:
# This file is part of the IntervalArithmetic.jl package; MIT licensed

using IntervalArithmetic
using Base.Test

setprecision(Interval, 128)
setprecision(Interval, Float64)

@testset "Hyperb tests" begin
    @test sinh(emptyinterval()) == emptyinterval()
    @test sinh(Interval(0.5)) == Interval(0.5210953054937473, 0.5210953054937474)
    @test sinh(Interval(0.5, 1.67)) == Interval(0.5210953054937473, 2.5619603657712102)
    @test sinh(Interval(-4.5, 0.1)) == Interval(-45.00301115199179, 0.10016675001984404)
    @test sinh(@biginterval(0.5)) ⊆ sinh(@interval(0.5))

    @test sinh(@biginterval(0.5, 1.67)) ⊆ sinh(@interval(0.5, 1.67))
    @test sinh(@biginterval(1.67, 3.2)) ⊆ sinh(@interval(1.67, 3.2))
    @test sinh(@biginterval(2.1, 5.6)) ⊆ sinh(@interval(2.1, 5.6))
    @test sinh(@biginterval(0.5, 8.5)) ⊆ sinh(@interval(0.5, 8.5))
    @test sinh(@biginterval(-4.5, 0.1)) ⊆ sinh(@interval(-4.5, 0.1))
    @test sinh(@biginterval(1.3, 6.3)) ⊆ sinh(@interval(1.3, 6.3))

    @test cosh(emptyinterval()) == emptyinterval()
    @test cosh(Interval(0.5)) == Interval(1.1276259652063807, 1.127625965206381)
    @test cosh(Interval(0.5, 1.67)) == Interval(1.1276259652063807, 2.750207431409957)
    @test cosh(Interval(-4.5, 0.1)) == Interval(1.0, 45.01412014853003)
    @test cosh(@biginterval(0.5)) ⊆ cosh(@interval(0.5))
    @test cosh(@biginterval(0.5, 1.67)) ⊆ cosh(@interval(0.5, 1.67))
    @test cosh(@biginterval(1.67, 3.2)) ⊆ cosh(@interval(1.67, 3.2))
    @test cosh(@biginterval(2.1, 5.6)) ⊆ cosh(@interval(2.1, 5.6))
    @test cosh(@biginterval(0.5, 8.5)) ⊆ cosh(@interval(0.5, 8.5))
    @test cosh(@biginterval(-4.5, 0.1)) ⊆ cosh(@interval(-4.5, 0.1))
    @test cosh(@biginterval(1.3, 6.3)) ⊆ cosh(@interval(1.3, 6.3))

    @test tanh(emptyinterval()) == emptyinterval()
    @test tanh(Interval(0.5)) == Interval(0.46211715726000974, 0.4621171572600098)
    @test tanh(Interval(0.5, 1.67)) == Interval(0.46211715726000974, 0.9315516846152083)
    @test tanh(Interval(-4.5, 0.1)) == Interval(-0.9997532108480276, 0.09966799462495583)

    @test tanh(@biginterval(0.5)) ⊆ tanh(@interval(0.5))
    @test tanh(@biginterval(0.5, 1.67)) ⊆ tanh(@interval(0.5, 1.67))
    @test tanh(@biginterval(1.67, 3.2)) ⊆ tanh(@interval(1.67, 3.2))
    @test tanh(@biginterval(2.1, 5.6)) ⊆ tanh(@interval(2.1, 5.6))
    @test tanh(@biginterval(0.5, 8.5)) ⊆ tanh(@interval(0.5, 8.5))
    @test tanh(@biginterval(-4.5, 0.1)) ⊆ tanh(@interval(-4.5, 0.1))
    @test tanh(@biginterval(1.3, 6.3)) ⊆ tanh(@interval(1.3, 6.3))

    @test asinh(@biginterval(1)) ⊆ asinh(@interval(1))
    @test asinh(@biginterval(0.9, 2)) ⊆ asinh(@interval(0.9, 2))
    @test asinh(@biginterval(3, 4)) ⊆ asinh(@interval(3, 4))

    @test acosh(@biginterval(1)) ⊆ acosh(@interval(1))
    @test acosh(@biginterval(-2, -0.9)) ⊆ acosh(@interval(-2, -0.9))
    @test acosh(@biginterval(3, 4)) ⊆ acosh(@interval(3, 4))

    for a in ( Interval(17, 19), Interval(0.5, 1.2) )
        @test tanh(a) ⊆ sinh(a)/cosh(a)
    end
end


text:
@userplot Joint
@recipe function f(g::Joint; colormode=:columns)
    @assert length(g.args) == 2 && g.args[2] isa AbstractMatrix
    x, y = g.args
    for (n, r) ∈ y |> eachrow |> enumerate
        @series begin
            seriestype := :violin
            if colormode == :rows
                (seriescolor := n)
            else
                (seriescolor := RGBA(0, 0, 0, 0.1))
            end
            linecolor := RGBA(0, 0, 0, 0.2)
            label := nothing
            x := n
            y := r
        end
    end
    for (m, s) ∈ y |> eachcol |> enumerate
        @series begin
            seriestype := :path
            linealpha --> 0.5
            if colormode == :columns
                (seriescolor := m)
            else
                (seriescolor := :gray)
            end
            label := nothing
            x := x
            y := s
        end
        @series begin
            seriestype := :scatter
            markeralpha --> 0.5
            if colormode == :columns
                (seriescolor := m)
            else
                (seriescolor := :gray)
            end
            label := nothing
            xticks := (1:length(x), x)
            x := x
            y := s
        end
    end
end


text:
# Find check if n is prime

function is_prime(n::Int64)

	if n <= 3
	    return true;
	end

	if is_divisible(n, 2)
	    return false;
	end

	return true;

	i = 3;

	while i <= sqrt(n)
		if is_divisible(n, i)
			return false;
		end
		i += 2;
	end

	return true;
end

function is_divisible(n, i)

	return n % i == 0;

end

println(is_prime(101));

max_stars_repo_name: neilpanchal/Julia-Algorithms

max_issues_repo_name: neilpanchal/Julia-Algorithms

max_forks_repo_name: neilpanchal/Julia-Algorithms


text:
import Base.kron

# (A ⊗ B)×vec(X) = vec(BXAᵀ)
"""`kron(A, B)`
Kronecker tensor product of A and B in linear operator form, if either
or both are linear operators. If both A and B are matrices, then
`Base.kron` is used.
"""
function kron(A::AbstractLinearOperator, B::AbstractLinearOperator)
  m, n = size(A)
  p, q = size(B)
  T = promote_type(eltype(A), eltype(B))
  function prod!(res, x, α, β)
    S = promote_type(T, eltype(x))
    X = reshape(convert(Vector{S}, x), q, n)
    res .= Matrix(B * X * transpose(A))[:]
  end
  function tprod!(res, x, α, β)
    S = promote_type(T, eltype(x))
    X = reshape(convert(Vector{S}, x), p, m)
    res .= Matrix(transpose(B) * X * A)[:]
  end
  function ctprod!(res, x, α, β)
    S = promote_type(T, eltype(x))
    X = reshape(convert(Vector{S}, x), p, m)
    res .= Matrix(B' * X * conj(A))[:]
  end
  symm = issymmetric(A) && issymmetric(B)
  herm = ishermitian(A) && ishermitian(B)
  nrow, ncol = m * p, n * q
  Mv = Vector{T}(undef, nrow)
  Mtu = symm ? Mv : Vector{T}(undef, ncol)
  Maw = herm ? Mv : Vector{T}(undef, ncol)
  return LinearOperator{T}(nrow, ncol, symm, herm, prod!, tprod!, ctprod!, Mv, Mtu, Maw)
end

kron(A::AbstractMatrix, B::AbstractLinearOperator) = kron(LinearOperator(A), B)

kron(A::AbstractLinearOperator, B::AbstractMatrix) = kron(A, LinearOperator(B))


text:
# Parse PSS(R)E data from PTI file into PowerModels data format

"""
    _init_bus!(bus, id)

Initializes a `bus` of id `id` with default values given in the PSS(R)E
specification.
"""
function _init_bus!(bus::Dict{String, Any}, id::Int)
    bus["bus_i"] = id
    bus["bus_type"] = 1
    bus["area"] = 1
    bus["vm"] = 1.0
    bus["va"] = 0.0
    bus["base_kv"] = 1.0
    bus["zone"] = 1
    bus["name"] = "            "
    bus["vmax"] = 1.1
    bus["vmin"] = 0.9
    bus["index"] = id
    return
end

"""
    _get_bus_value(bus_i, field, pm_data)

Returns the value of `field` of `bus_i` from the PowerModels data. Requires
"bus" Dict to already be populated.
"""
function _get_bus_value(bus_i, field, pm_data)
    if isa(pm_data["bus"], Array)
        for bus in pm_data["bus"]
            if bus["index"] == bus_i
                return bus[field]
            end
        end
    elseif isa(pm_data["bus"], Dict)
        for (k, bus) in pm_data["bus"]
            if bus["index"] == bus_i
                return bus[field]
            end
        end
    end

    @info("Could not find bus $bus_i, returning 0 for field $field")
    return 0
end

"""
    _find_max_bus_id(pm_data)

Returns the maximum bus id in `pm_data`
"""
function _find_max_bus_id(pm_data::Dict)::Int
    max_id = 0
    for bus in pm_data["bus"]
        if bus["index"] > max_id && !endswith(bus["name"], "starbus")
            max_id = bus["index"]
        end
    end

    return max_id
end

"""
    create_starbus(pm_data, transformer)

Creates a starbus from a given three-winding `transformer`. "source_id" is given
by `["bus_i", "name", "I", "J", "K", "CKT"]` where "bus_i" and "name" are the
modified names for the starbus, and "I", "J", "K" and "CKT" come from the
originating transformer, in the PSS(R)E transformer specification.
"""
function _create_starbus_from_transformer(
    pm_data::Dict,
    transformer::Dict,
    starbus_id::Int,
)::Dict
    starbus = Dict{String, Any}()

    _init_bus!(starbus, starbus_id)

    starbus["name"] = "starbus_$(transformer["I"])_$(transformer["J"])_$(transformer["K"])_$(strip(transformer["CKT"]))"

    bus_type = 1
    if transformer["STAT"] == 0
        bus_type = 4
    end

    starbus["vm"] = transformer["VMSTAR"]
    starbus["va"] = transformer["ANSTAR"]
    starbus["bus_type"] = bus_type
    starbus["area"] = _get_bus_value(transformer["I"], "area", pm_data)
    starbus["zone"] = _get_bus_value(transformer["I"], "zone", pm_data)
    starbus["source_id"] = push!(
        ["transformer", starbus["bus_i"], starbus["name"]],
        transformer["I"],
        transformer["J"],
        transformer["K"],
        transformer["CKT"],
    )

    return starbus
end

"Imports remaining top level component lists from `data_in` into `data_out`, excluding keys in `exclude`"
function _import_remaining_comps!(data_out::Dict, data_in::Dict; exclude = [])
    for (comp_class, v) in data_in
        if !(comp_class in exclude)
            comps_out = Dict{String, Any}()

            if isa(v, Array)
                for (n, item) in enumerate(v)
                    if isa(item, Dict)
                        comp_out = Dict{String, Any}()
                        _import_remaining_keys!(comp_out, item)
                        if !("index" in keys(item))
                            comp_out["index"] = n
                        end
                        comps_out["$(n)"] = comp_out
                    else
                        @error("psse data parsing error, please post an issue")
                    end
                end
            elseif isa(v, Dict)
                comps_out = Dict{String, Any}()
                _import_remaining_keys!(comps_out, v)
            else
                @error("psse data parsing error, please post an issue")
            end

            data_out[lowercase(comp_class)] = comps_out
        end
    end
end

"Imports remaining keys from a source component into detestation component, excluding keys in `exclude`"
function _import_remaining_keys!(comp_dest::Dict, comp_src::Dict; exclude = [])
    for (k, v) in comp_src
        if !(k in exclude)
            key = lowercase(k)
            if !haskey(comp_dest, key)
                comp_dest[key] = v
            else
                if key != "index"
                    @warn("duplicate key $(key), please post an issue")
                end
            end
        end
    end
end

"""
    _psse2pm_branch!(pm_data, pti_data)

Parses PSS(R)E-style Branch data into a PowerModels-style Dict. "source_id" is
given by `["I", "J", "CKT"]` in PSS(R)E Branch specification.
"""
function _psse2pm_branch!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["branch"] = []
    if haskey(pti_data, "BRANCH")
        for (i, branch) in enumerate(pti_data["BRANCH"])
            sub_data = Dict{String, Any}()

            sub_data["f_bus"] = pop!(branch, "I")
            sub_data["t_bus"] = pop!(branch, "J")
            sub_data["br_r"] = pop!(branch, "R")
            sub_data["br_x"] = pop!(branch, "X")
            sub_data["g_fr"] = pop!(branch, "GI")
            sub_data["b_fr"] =
                branch["BI"] == 0.0 && branch["B"] != 0.0 ? branch["B"] / 2 :
                pop!(branch, "BI")
            sub_data["g_to"] = pop!(branch, "GJ")
            sub_data["b_to"] =
                branch["BJ"] == 0.0 && branch["B"] != 0.0 ? branch["B"] / 2 :
                pop!(branch, "BJ")
            sub_data["rate_a"] = pop!(branch, "RATEA")
            sub_data["rate_b"] = pop!(branch, "RATEB")
            sub_data["rate_c"] = pop!(branch, "RATEC")
            sub_data["tap"] = 1.0
            sub_data["shift"] = 0.0
            sub_data["br_status"] = pop!(branch, "ST")
            sub_data["angmin"] = 0.0
            sub_data["angmax"] = 0.0
            sub_data["transformer"] = false

            sub_data["source_id"] =
                ["branch", sub_data["f_bus"], sub_data["t_bus"], pop!(branch, "CKT")]
            sub_data["index"] = i

            if import_all
                _import_remaining_keys!(sub_data, branch; exclude = ["B", "BI", "BJ"])
            end

            if sub_data["rate_a"] == 0.0
                delete!(sub_data, "rate_a")
            end
            if sub_data["rate_b"] == 0.0
                delete!(sub_data, "rate_b")
            end
            if sub_data["rate_c"] == 0.0
                delete!(sub_data, "rate_c")
            end

            push!(pm_data["branch"], sub_data)
        end
    end
end

"""
    _psse2pm_generator!(pm_data, pti_data)

Parses PSS(R)E-style Generator data in a PowerModels-style Dict. "source_id" is
given by `["I", "ID"]` in PSS(R)E Generator specification.
"""
function _psse2pm_generator!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["gen"] = []
    if haskey(pti_data, "GENERATOR")
        for gen in pti_data["GENERATOR"]
            sub_data = Dict{String, Any}()

            sub_data["gen_bus"] = pop!(gen, "I")
            sub_data["gen_status"] = pop!(gen, "STAT")
            sub_data["pg"] = pop!(gen, "PG")
            sub_data["qg"] = pop!(gen, "QG")
            sub_data["vg"] = pop!(gen, "VS")
            sub_data["mbase"] = pop!(gen, "MBASE")
            sub_data["pmin"] = pop!(gen, "PB")
            sub_data["pmax"] = pop!(gen, "PT")
            sub_data["qmin"] = pop!(gen, "QB")
            sub_data["qmax"] = pop!(gen, "QT")
            sub_data["r_source"] = pop!(gen, "ZR")
            sub_data["x_source"] = pop!(gen, "ZX")

            # Default Cost functions
            sub_data["model"] = 2
            sub_data["startup"] = 0.0
            sub_data["shutdown"] = 0.0
            sub_data["ncost"] = 2
            sub_data["cost"] = [1.0, 0.0]

            sub_data["source_id"] = ["generator", sub_data["gen_bus"], pop!(gen, "ID")]
            sub_data["index"] = length(pm_data["gen"]) + 1

            if import_all
                _import_remaining_keys!(sub_data, gen)
            end

            push!(pm_data["gen"], sub_data)
        end
    end
end

"""
    _psse2pm_bus!(pm_data, pti_data)

Parses PSS(R)E-style Bus data into a PowerModels-style Dict. "source_id" is given
by ["I", "NAME"] in PSS(R)E Bus specification.
"""
function _psse2pm_bus!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["bus"] = []
    if haskey(pti_data, "BUS")
        for bus in pti_data["BUS"]
            sub_data = Dict{String, Any}()

            sub_data["bus_i"] = bus["I"]
            sub_data["bus_type"] = pop!(bus, "IDE")
            sub_data["area"] = pop!(bus, "AREA")
            sub_data["vm"] = pop!(bus, "VM")
            sub_data["va"] = pop!(bus, "VA")
            sub_data["base_kv"] = pop!(bus, "BASKV")
            sub_data["zone"] = pop!(bus, "ZONE")
            sub_data["name"] = pop!(bus, "NAME")
            sub_data["vmax"] = pop!(bus, "NVHI")
            sub_data["vmin"] = pop!(bus, "NVLO")

            sub_data["source_id"] = ["bus", "$(bus["I"])"]
            sub_data["index"] = pop!(bus, "I")

            if import_all
                _import_remaining_keys!(sub_data, bus)
            end

            push!(pm_data["bus"], sub_data)
        end
    end
end

"""
    _psse2pm_load!(pm_data, pti_data)

Parses PSS(R)E-style Load data into a PowerModels-style Dict. "source_id" is given
by `["I", "ID"]` in the PSS(R)E Load specification.
"""
function _psse2pm_load!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["load"] = []
    bus_lookup = Dict{Int, Any}()
    if haskey(pti_data, "LOAD")
        for load in pti_data["LOAD"]
            sub_data = Dict{String, Any}()
            sub_data["load_bus"] = pop!(load, "I")
            sub_data["pd"] = pop!(load, "PL")
            sub_data["qd"] = pop!(load, "QL")
            sub_data["source_id"] = ["load", sub_data["load_bus"], pop!(load, "ID")]
            sub_data["status"] = pop!(load, "STATUS")
            sub_data["index"] = length(pm_data["load"]) + 1
            # Only build lookup once if necessary
            if (load["IP"] != 0.0) ||
               (load["IQ"] != 0.0) ||
               (load["YP"] != 0.0) ||
               (load["YQ"] != 0.0)
                if isempty(bus_lookup)
                    bus_lookup = Dict(bus["index"] => bus for bus in pm_data["bus"])
                end
                bus_vm = bus_lookup[sub_data["load_bus"]]["vm"]
                if (load["IP"] != 0.0) || (load["IQ"] != 0.0)
                    # Uses matpower transformation instead of pd = real(V*I) and qd = imag(V*I)
                    # where I and V are in vector form.
                    sub_data["pd"] += bus_vm * load["IP"]
                    sub_data["qd"] += bus_vm * load["IQ"]
                    @warn "Load id = $(sub_data["index"]) detected as I Load  IP = $(load["IP"]) IQ = $(load["IQ"]). Converting to Power Load Pd = $(bus_vm*load["IP"]) Qd = $(bus_vm*load["IQ"]) using Vm = $(bus_vm)"
                end
                if (load["YP"] != 0.0) || (load["YQ"] != 0.0)
                    # Uses matpower transformation instead of pd = real(V*(V*Y)^*) and qd = imag(V*(V*Y)^*)
                    # where Y and V are in vector form.
                    sub_data["pd"] += bus_vm^2 * load["YP"]
                    # NOTE: In PSSe reactive power in constant admittance loads is negative for inductive loads and positive for capacitive loads
                    sub_data["qd"] -= bus_vm^2 * load["YQ"]
                    @warn "Load id = $(sub_data["index"]) detected as Z Load YP = $(load["YP"]) YQ = $(load["YQ"]). Converting to Power Load Pd = $(bus_vm^2*load["YP"]) Qd = $(-1*bus_vm^2*load["YQ"]) using Vm = $(bus_vm)"
                end
            end

            if import_all
                _import_remaining_keys!(sub_data, load)
            end

            push!(pm_data["load"], sub_data)
        end
    end
end

"""
    _psse2pm_shunt!(pm_data, pti_data)

Parses PSS(R)E-style Fixed and Switched Shunt data into a PowerModels-style
Dict. "source_id" is given by `["I", "ID"]` for Fixed Shunts, and `["I", "SWREM"]`
for Switched Shunts, as given by the PSS(R)E Fixed and Switched Shunts
specifications.
"""
function _psse2pm_shunt!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["shunt"] = []

    if haskey(pti_data, "FIXED SHUNT")
        for shunt in pti_data["FIXED SHUNT"]
            sub_data = Dict{String, Any}()

            sub_data["shunt_bus"] = pop!(shunt, "I")
            sub_data["gs"] = pop!(shunt, "GL")
            sub_data["bs"] = pop!(shunt, "BL")
            sub_data["status"] = pop!(shunt, "STATUS")

            sub_data["source_id"] =
                ["fixed shunt", sub_data["shunt_bus"], pop!(shunt, "ID")]
            sub_data["index"] = length(pm_data["shunt"]) + 1

            if import_all
                _import_remaining_keys!(sub_data, shunt)
            end

            push!(pm_data["shunt"], sub_data)
        end
    end

    if haskey(pti_data, "SWITCHED SHUNT")
        @info("Switched shunt converted to fixed shunt, with default value gs=0.0")

        for shunt in pti_data["SWITCHED SHUNT"]
            sub_data = Dict{String, Any}()

            sub_data["shunt_bus"] = pop!(shunt, "I")
            sub_data["gs"] = 0.0
            sub_data["bs"] = pop!(shunt, "BINIT")
            sub_data["status"] = pop!(shunt, "STAT")

            sub_data["source_id"] =
                ["switched shunt", sub_data["shunt_bus"], pop!(shunt, "SWREM")]
            sub_data["index"] = length(pm_data["shunt"]) + 1

            if import_all
                _import_remaining_keys!(sub_data, shunt)
            end

            push!(pm_data["shunt"], sub_data)
        end
    end
end

"""
    _psse2pm_transformer!(pm_data, pti_data)

Parses PSS(R)E-style Transformer data into a PowerModels-style Dict. "source_id"
is given by `["I", "J", "K", "CKT", "winding"]`, where "winding" is 0 if
transformer is two-winding, and 1, 2, or 3 for three-winding, and the remaining
keys are defined in the PSS(R)E Transformer specification.
"""
function _psse2pm_transformer!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    if !haskey(pm_data, "branch")
        pm_data["branch"] = []
    end

    if haskey(pti_data, "TRANSFORMER")
        starbus_id = 10^ceil(Int, log10(abs(_find_max_bus_id(pm_data)))) + 1

        for transformer in pti_data["TRANSFORMER"]
            if !(transformer["CZ"] in [1, 2, 3])
                @warn(
                    "transformer CZ value outside of valid bounds assuming the default value of 1.  Given $(transformer["CZ"]), should be 1, 2 or 3",
                )
                transformer["CZ"] = 1
            end

            if !(transformer["CW"] in [1, 2, 3])
                @warn(
                    "transformer CW value outside of valid bounds assuming the default value of 1.  Given $(transformer["CW"]), should be 1, 2 or 3",
                )
                transformer["CW"] = 1
            end

            if !(transformer["CM"] in [1, 2])
                @warn(
                    "transformer CM value outside of valid bounds assuming the default value of 1.  Given $(transformer["CM"]), should be 1 or 2",
                )
                transformer["CM"] = 1
            end

            if transformer["K"] == 0  # Two-winding Transformers
                sub_data = Dict{String, Any}()

                sub_data["f_bus"] = transformer["I"]
                sub_data["t_bus"] = transformer["J"]

                # Unit Transformations
                if transformer["CZ"] == 1  # "for resistance and reactance in pu on system MVA base and winding voltage base"
                    br_r, br_x = transformer["R1-2"], transformer["X1-2"]
                else  # NOT "for resistance and reactance in pu on system MVA base and winding voltage base"
                    if transformer["CZ"] == 3  # "for transformer load loss in watts and impedance magnitude in pu on a specified MVA base and winding voltage base."
                        br_r = 1e-6 * transformer["R1-2"] / transformer["SBASE1-2"]
                        br_x = sqrt(transformer["X1-2"]^2 - br_r^2)
                    else
                        br_r, br_x = transformer["R1-2"], transformer["X1-2"]
                    end
                    per_unit_factor =
                        (
                            transformer["NOMV1"]^2 /
                            _get_bus_value(transformer["I"], "base_kv", pm_data)^2
                        ) * (pm_data["baseMVA"] / transformer["SBASE1-2"])
                    if per_unit_factor == 0
                        @warn "Per unit conversion for transformer $(sub_data["f_bus"]) -> $(sub_data["t_bus"]) couldn't be done, assuming system base instead. Check field NOMV1 is valid"
                        per_unit_factor = 1
                    end
                    br_r *= per_unit_factor
                    br_x *= per_unit_factor
                end

                # Zeq scaling for tap2 (see eq (4.21b) in PROGRAM APPLICATION GUIDE 1 in PSSE installation folder)
                # Unit Transformations
                if transformer["CW"] == 1  # "for off-nominal turns ratio in pu of winding bus base voltage"
                    br_r *= transformer["WINDV2"]^2
                    br_x *= transformer["WINDV2"]^2
                else  # NOT "for off-nominal turns ratio in pu of winding bus base voltage"
                    if transformer["CW"] == 2  # "for winding voltage in kV"
                        br_r *=
                            (
                                transformer["WINDV2"] /
                                _get_bus_value(transformer["J"], "base_kv", pm_data)
                            )^2
                        br_x *=
                            (
                                transformer["WINDV2"] /
                                _get_bus_value(transformer["J"], "base_kv", pm_data)
                            )^2
                    else  # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3."
                        br_r *=
                            (
                                transformer["WINDV2"] * (
                                    transformer["NOMV2"] /
                                    _get_bus_value(transformer["J"], "base_kv", pm_data)
                                )
                            )^2
                        br_x *=
                            (
                                transformer["WINDV2"] * (
                                    transformer["NOMV2"] /
                                    _get_bus_value(transformer["J"], "base_kv", pm_data)
                                )
                            )^2
                    end
                end

                sub_data["br_r"] = br_r
                sub_data["br_x"] = br_x

                sub_data["g_fr"] = pop!(transformer, "MAG1")
                sub_data["b_fr"] = pop!(transformer, "MAG2")
                sub_data["g_to"] = 0.0
                sub_data["b_to"] = 0.0

                sub_data["rate_a"] = pop!(transformer, "RATA1")
                sub_data["rate_b"] = pop!(transformer, "RATB1")
                sub_data["rate_c"] = pop!(transformer, "RATC1")

                if sub_data["rate_a"] == 0.0
                    delete!(sub_data, "rate_a")
                end
                if sub_data["rate_b"] == 0.0
                    delete!(sub_data, "rate_b")
                end
                if sub_data["rate_c"] == 0.0
                    delete!(sub_data, "rate_c")
                end

                if import_all
                    sub_data["windv1"] = transformer["WINDV1"]
                    sub_data["windv2"] = transformer["WINDV2"]
                    sub_data["nomv1"] = transformer["NOMV1"]
                    sub_data["nomv2"] = transformer["NOMV2"]
                end

                sub_data["tap"] = pop!(transformer, "WINDV1") / pop!(transformer, "WINDV2")
                sub_data["shift"] = pop!(transformer, "ANG1")

                # Unit Transformations
                if transformer["CW"] != 1  # NOT "for off-nominal turns ratio in pu of winding bus base voltage"
                    sub_data["tap"] *=
                        _get_bus_value(transformer["J"], "base_kv", pm_data) /
                        _get_bus_value(transformer["I"], "base_kv", pm_data)
                    if transformer["CW"] == 3  # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3."
                        sub_data["tap"] *= transformer["NOMV1"] / transformer["NOMV2"]
                    end
                end

                if import_all
                    sub_data["cw"] = transformer["CW"]
                end

                if transformer["STAT"] == 0 || transformer["STAT"] == 2
                    sub_data["br_status"] = 0
                else
                    sub_data["br_status"] = 1
                end

                sub_data["angmin"] = 0.0
                sub_data["angmax"] = 0.0

                sub_data["source_id"] = [
                    "transformer",
                    pop!(transformer, "I"),
                    pop!(transformer, "J"),
                    pop!(transformer, "K"),
                    pop!(transformer, "CKT"),
                    0,
                ]
                sub_data["transformer"] = true
                sub_data["index"] = length(pm_data["branch"]) + 1

                if import_all
                    _import_remaining_keys!(
                        sub_data,
                        transformer;
                        exclude = [
                            "I",
                            "J",
                            "K",
                            "CZ",
                            "CW",
                            "R1-2",
                            "R2-3",
                            "R3-1",
                            "X1-2",
                            "X2-3",
                            "X3-1",
                            "SBASE1-2",
                            "SBASE2-3",
                            "SBASE3-1",
                            "MAG1",
                            "MAG2",
                            "STAT",
                            "NOMV1",
                            "NOMV2",
                        ],
                    )
                end

                push!(pm_data["branch"], sub_data)
            else  # Three-winding Transformers
                bus_id1, bus_id2, bus_id3 =
                    transformer["I"], transformer["J"], transformer["K"]

                # Creates a starbus (or "dummy" bus) to which each winding of the transformer will connect
                starbus = _create_starbus_from_transformer(pm_data, transformer, starbus_id)
                push!(pm_data["bus"], starbus)
                starbus_id += 1

                # Create 3 branches from a three winding transformer (one for each winding, which will each connect to the starbus)
                br_r12, br_r23, br_r31 =
                    transformer["R1-2"], transformer["R2-3"], transformer["R3-1"]
                br_x12, br_x23, br_x31 =
                    transformer["X1-2"], transformer["X2-3"], transformer["X3-1"]

                # Unit Transformations
                if transformer["CZ"] == 3  # "for transformer load loss in watts and impedance magnitude in pu on a specified MVA base and winding voltage base."
                    br_r12 *= 1e-6 / transformer["SBASE1-2"]
                    br_r23 *= 1e-6 / transformer["SBASE2-3"]
                    br_r31 *= 1e-6 / transformer["SBASE3-1"]

                    br_x12 = sqrt(br_x12^2 - br_r12^2)
                    br_x23 = sqrt(br_x23^2 - br_r23^2)
                    br_x31 = sqrt(br_x31^2 - br_r31^2)
                end

                # Unit Transformations
                if transformer["CZ"] != 1  # NOT "for resistance and reactance in pu on system MVA base and winding voltage base"
                    br_r12 *=
                        (
                            transformer["NOMV1"] /
                            _get_bus_value(bus_id1, "base_kv", pm_data)
                        )^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"])
                    br_r23 *=
                        (
                            transformer["NOMV2"] /
                            _get_bus_value(bus_id2, "base_kv", pm_data)
                        )^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"])
                    br_r31 *=
                        (
                            transformer["NOMV3"] /
                            _get_bus_value(bus_id3, "base_kv", pm_data)
                        )^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"])

                    br_x12 *=
                        (
                            transformer["NOMV1"] /
                            _get_bus_value(bus_id1, "base_kv", pm_data)
                        )^2 * (pm_data["baseMVA"] / transformer["SBASE1-2"])
                    br_x23 *=
                        (
                            transformer["NOMV2"] /
                            _get_bus_value(bus_id2, "base_kv", pm_data)
                        )^2 * (pm_data["baseMVA"] / transformer["SBASE2-3"])
                    br_x31 *=
                        (
                            transformer["NOMV3"] /
                            _get_bus_value(bus_id3, "base_kv", pm_data)
                        )^2 * (pm_data["baseMVA"] / transformer["SBASE3-1"])
                end

                # See "Power System Stability and Control", ISBN: 0-07-035958-X, Eq. 6.72
                Zr_p = 1 / 2 * (br_r12 - br_r23 + br_r31)
                Zr_s = 1 / 2 * (br_r23 - br_r31 + br_r12)
                Zr_t = 1 / 2 * (br_r31 - br_r12 + br_r23)
                Zx_p = 1 / 2 * (br_x12 - br_x23 + br_x31)
                Zx_s = 1 / 2 * (br_x23 - br_x31 + br_x12)
                Zx_t = 1 / 2 * (br_x31 - br_x12 + br_x23)

                # Build each of the three transformer branches
                for (m, (bus_id, br_r, br_x)) in enumerate(
                    zip(
                        [bus_id1, bus_id2, bus_id3],
                        [Zr_p, Zr_s, Zr_t],
                        [Zx_p, Zx_s, Zx_t],
                    ),
                )
                    sub_data = Dict{String, Any}()

                    sub_data["f_bus"] = bus_id
                    sub_data["t_bus"] = starbus["bus_i"]

                    sub_data["br_r"] = br_r
                    sub_data["br_x"] = br_x

                    sub_data["g_fr"] = m == 1 ? pop!(transformer, "MAG1") : 0.0
                    sub_data["b_fr"] = m == 1 ? pop!(transformer, "MAG2") : 0.0
                    sub_data["g_to"] = 0.0
                    sub_data["b_to"] = 0.0

                    sub_data["rate_a"] = pop!(transformer, "RATA$m")
                    sub_data["rate_b"] = pop!(transformer, "RATB$m")
                    sub_data["rate_c"] = pop!(transformer, "RATC$m")

                    if sub_data["rate_a"] == 0.0
                        delete!(sub_data, "rate_a")
                    end
                    if sub_data["rate_b"] == 0.0
                        delete!(sub_data, "rate_b")
                    end
                    if sub_data["rate_c"] == 0.0
                        delete!(sub_data, "rate_c")
                    end

                    if import_all
                        sub_data["windv$m"] = transformer["WINDV$m"]
                        #sub_data["windvs"] = 1.0
                        sub_data["nomv$m"] = transformer["NOMV$m"]
                    end

                    sub_data["tap"] = pop!(transformer, "WINDV$m")
                    sub_data["shift"] = pop!(transformer, "ANG$m")

                    # Unit Transformations
                    if transformer["CW"] != 1  # NOT "for off-nominal turns ratio in pu of winding bus base voltage"
                        sub_data["tap"] /= _get_bus_value(bus_id, "base_kv", pm_data)
                        if transformer["CW"] == 3  # "for off-nominal turns ratio in pu of nominal winding voltage, NOMV1, NOMV2 and NOMV3."
                            sub_data["tap"] *= transformer["NOMV$m"]
                        end
                    end

                    if import_all
                        sub_data["cw"] = transformer["CW"]
                    end

                    sub_data["br_status"] = 1
                    if transformer["STAT"] == 0
                        sub_data["br_status"] = 0
                    elseif transformer["STAT"] == 2 && m == 2
                        sub_data["br_status"] = 0
                    elseif transformer["STAT"] == 3 && m == 3
                        sub_data["br_status"] = 0
                    elseif transformer["STAT"] == 4 && m == 1
                        sub_data["br_status"] = 0
                    end

                    sub_data["angmin"] = 0.0
                    sub_data["angmax"] = 0.0

                    sub_data["source_id"] = [
                        "transformer",
                        transformer["I"],
                        transformer["J"],
                        transformer["K"],
                        transformer["CKT"],
                        m,
                    ]
                    sub_data["transformer"] = true
                    sub_data["index"] = length(pm_data["branch"]) + 1

                    if import_all
                        _import_remaining_keys!(
                            sub_data,
                            transformer;
                            exclude = [
                                "I",
                                "J",
                                "K",
                                "CZ",
                                "CW",
                                "R1-2",
                                "R2-3",
                                "R3-1",
                                "X1-2",
                                "X2-3",
                                "X3-1",
                                "SBASE1-2",
                                "SBASE2-3",
                                "CKT",
                                "SBASE3-1",
                                "MAG1",
                                "MAG2",
                                "STAT",
                                "NOMV1",
                                "NOMV2",
                                "NOMV3",
                                "WINDV1",
                                "WINDV2",
                                "WINDV3",
                                "RATA1",
                                "RATA2",
                                "RATA3",
                                "RATB1",
                                "RATB2",
                                "RATB3",
                                "RATC1",
                                "RATC2",
                                "RATC3",
                                "ANG1",
                                "ANG2",
                                "ANG3",
                            ],
                        )
                    end

                    push!(pm_data["branch"], sub_data)
                end
            end
        end
    end
end

"""
    _psse2pm_dcline!(pm_data, pti_data)

Parses PSS(R)E-style Two-Terminal and VSC DC Lines data into a PowerModels
compatible Dict structure by first converting them to a simple DC Line Model.
For Two-Terminal DC lines, "source_id" is given by `["IPR", "IPI", "NAME"]` in the
PSS(R)E Two-Terminal DC specification. For Voltage Source Converters, "source_id"
is given by `["IBUS1", "IBUS2", "NAME"]`, where "IBUS1" is "IBUS" of the first
converter bus, and "IBUS2" is the "IBUS" of the second converter bus, in the
PSS(R)E Voltage Source Converter specification.
"""
function _psse2pm_dcline!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["dcline"] = []

    if haskey(pti_data, "TWO-TERMINAL DC")
        for dcline in pti_data["TWO-TERMINAL DC"]
            @info(
                "Two-Terminal DC lines are supported via a simple *lossless* dc line model approximated by two generators."
            )
            sub_data = Dict{String, Any}()

            # Unit conversions?
            power_demand =
                dcline["MDC"] == 1 ? abs(dcline["SETVL"]) :
                dcline["MDC"] == 2 ? abs(dcline["SETVL"] / pop!(dcline, "VSCHD") / 1000) : 0

            sub_data["f_bus"] = dcline["IPR"]
            sub_data["t_bus"] = dcline["IPI"]
            sub_data["br_status"] = pop!(dcline, "MDC") == 0 ? 0 : 1
            sub_data["pf"] = power_demand
            sub_data["pt"] = power_demand
            sub_data["qf"] = 0.0
            sub_data["qt"] = 0.0
            sub_data["vf"] = _get_bus_value(pop!(dcline, "IPR"), "vm", pm_data)
            sub_data["vt"] = _get_bus_value(pop!(dcline, "IPI"), "vm", pm_data)

            sub_data["pminf"] = 0.0
            sub_data["pmaxf"] = dcline["SETVL"] > 0 ? power_demand : -power_demand
            sub_data["pmint"] = pop!(dcline, "SETVL") > 0 ? -power_demand : power_demand
            sub_data["pmaxt"] = 0.0

            anmn = []
            for key in ["ANMNR", "ANMNI"]
                if abs(dcline[key]) <= 90.0
                    push!(anmn, pop!(dcline, key))
                else
                    push!(anmn, 0)
                    @info("$key outside reasonable limits, setting to 0 degress")
                end
            end

            sub_data["qmaxf"] = 0.0
            sub_data["qmaxt"] = 0.0
            sub_data["qminf"] =
                -max(abs(sub_data["pminf"]), abs(sub_data["pmaxf"])) * cosd(anmn[1])
            sub_data["qmint"] =
                -max(abs(sub_data["pmint"]), abs(sub_data["pmaxt"])) * cosd(anmn[2])

            # Can we use "number of bridges in series (NBR/NBI)" to compute a loss?
            sub_data["loss0"] = 0.0
            sub_data["loss1"] = 0.0

            # Costs (set to default values)
            sub_data["startup"] = 0.0
            sub_data["shutdown"] = 0.0
            sub_data["ncost"] = 3
            sub_data["cost"] = [0.0, 0.0, 0.0]
            sub_data["model"] = 2

            sub_data["source_id"] = [
                "two-terminal dc",
                sub_data["f_bus"],
                sub_data["t_bus"],
                pop!(dcline, "NAME"),
            ]
            sub_data["index"] = length(pm_data["dcline"]) + 1

            if import_all
                _import_remaining_keys!(sub_data, dcline)
            end

            push!(pm_data["dcline"], sub_data)
        end
    end

    if haskey(pti_data, "VOLTAGE SOURCE CONVERTER")
        @info(
            "VSC-HVDC lines are supported via a dc line model approximated by two generators and an associated loss."
        )
        for dcline in pti_data["VOLTAGE SOURCE CONVERTER"]
            # Converter buses : is the distinction between ac and dc side meaningful?
            dcside, acside = dcline["CONVERTER BUSES"]

            # PowerWorld conversion from PTI to matpower seems to create two
            # artificial generators from a VSC, but it is not clear to me how
            # the value of "pg" is determined and adds shunt to the DC-side bus.
            sub_data = Dict{String, Any}()

            # VSC intended to be one or bi-directional?
            sub_data["f_bus"] = pop!(dcside, "IBUS")
            sub_data["t_bus"] = pop!(acside, "IBUS")
            sub_data["br_status"] =
                pop!(dcline, "MDC") == 0 ||
                pop!(dcside, "TYPE") == 0 ||
                pop!(acside, "TYPE") == 0 ? 0 : 1

            sub_data["pf"] = 0.0
            sub_data["pt"] = 0.0

            sub_data["qf"] = 0.0
            sub_data["qt"] = 0.0

            sub_data["vf"] = pop!(dcside, "MODE") == 1 ? pop!(dcside, "ACSET") : 1.0
            sub_data["vt"] = pop!(acside, "MODE") == 1 ? pop!(acside, "ACSET") : 1.0

            sub_data["pmaxf"] =
                dcside["SMAX"] == 0.0 && dcside["IMAX"] == 0.0 ?
                max(abs(dcside["MAXQ"]), abs(dcside["MINQ"])) :
                min(pop!(dcside, "IMAX"), pop!(dcside, "SMAX"))
            sub_data["pmaxt"] =
                acside["SMAX"] == 0.0 && acside["IMAX"] == 0.0 ?
                max(abs(acside["MAXQ"]), abs(acside["MINQ"])) :
                min(pop!(acside, "IMAX"), pop!(acside, "SMAX"))
            sub_data["pminf"] = -sub_data["pmaxf"]
            sub_data["pmint"] = -sub_data["pmaxt"]

            sub_data["qminf"] = pop!(dcside, "MINQ")
            sub_data["qmaxf"] = pop!(dcside, "MAXQ")
            sub_data["qmint"] = pop!(acside, "MINQ")
            sub_data["qmaxt"] = pop!(acside, "MAXQ")

            sub_data["loss0"] =
                (
                    pop!(dcside, "ALOSS") +
                    pop!(acside, "ALOSS") +
                    pop!(dcside, "MINLOSS") +
                    pop!(acside, "MINLOSS")
                ) * 1e-3
            sub_data["loss1"] = (pop!(dcside, "BLOSS") + pop!(acside, "BLOSS")) * 1e-3 # how to include resistance?

            # Costs (set to default values)
            sub_data["startup"] = 0.0
            sub_data["shutdown"] = 0.0
            sub_data["ncost"] = 3
            sub_data["cost"] = [0.0, 0.0, 0.0]
            sub_data["model"] = 2

            sub_data["source_id"] =
                ["vsc dc", sub_data["f_bus"], sub_data["t_bus"], pop!(dcline, "NAME")]
            sub_data["index"] = length(pm_data["dcline"]) + 1

            if import_all
                _import_remaining_keys!(sub_data, dcline)

                for cb in sub_data["converter buses"]
                    for (k, v) in cb
                        cb[lowercase(k)] = v
                        delete!(cb, k)
                    end
                end
            end

            push!(pm_data["dcline"], sub_data)
        end
    end
end

function _psse2pm_storage!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["storage"] = []
    return
end

function _psse2pm_switch!(pm_data::Dict, pti_data::Dict, import_all::Bool)
    pm_data["switch"] = []
    return
end

"""
    _pti_to_powermodels!(pti_data)

Converts PSS(R)E-style data parsed from a PTI raw file, passed by `pti_data`
into a format suitable for use internally in PowerModels. Imports all remaining
data from the PTI file if `import_all` is true (Default: false).
"""
function _pti_to_powermodels!(pti_data::Dict; import_all = false, validate = true)::Dict
    pm_data = Dict{String, Any}()

    rev = pop!(pti_data["CASE IDENTIFICATION"][1], "REV")

    pm_data["per_unit"] = false
    pm_data["source_type"] = "pti"
    pm_data["source_version"] = "$rev"
    pm_data["baseMVA"] = pop!(pti_data["CASE IDENTIFICATION"][1], "SBASE")
    pm_data["name"] = pop!(pti_data["CASE IDENTIFICATION"][1], "NAME")

    if import_all
        _import_remaining_keys!(pm_data, pti_data["CASE IDENTIFICATION"][1])
    end

    _psse2pm_bus!(pm_data, pti_data, import_all)
    _psse2pm_load!(pm_data, pti_data, import_all)
    _psse2pm_shunt!(pm_data, pti_data, import_all)
    _psse2pm_generator!(pm_data, pti_data, import_all)
    _psse2pm_branch!(pm_data, pti_data, import_all)
    _psse2pm_transformer!(pm_data, pti_data, import_all)
    _psse2pm_dcline!(pm_data, pti_data, import_all)
    _psse2pm_storage!(pm_data, pti_data, import_all)
    _psse2pm_switch!(pm_data, pti_data, import_all)

    if import_all
        _import_remaining_comps!(
            pm_data,
            pti_data;
            exclude = [
                "CASE IDENTIFICATION",
                "BUS",
                "LOAD",
                "FIXED SHUNT",
                "SWITCHED SHUNT",
                "GENERATOR",
                "BRANCH",
                "TRANSFORMER",
                "TWO-TERMINAL DC",
                "VOLTAGE SOURCE CONVERTER",
            ],
        )
    end

    # update lookup structure
    for (k, v) in pm_data
        if isa(v, Array)
            #println("updating $(k)")
            dict = Dict{String, Any}()
            for item in v
                @assert("index" in keys(item))
                dict[string(item["index"])] = item
            end
            pm_data[k] = dict
        end
    end

    if validate
        correct_network_data!(pm_data)
    end

    return pm_data
end

"""
    parse_psse(filename::String; kwargs...)::Dict

Parses directly from file
"""
function parse_psse(filename::String; kwargs...)::Dict
    pm_data = open(filename) do f
        parse_psse(f; kwargs...)
    end

    return pm_data
end

"""
    function parse_psse(io::IO; kwargs...)::Dict

Parses directly from iostream
"""
function parse_psse(io::IO; kwargs...)::Dict
    @info(
        "The PSS(R)E parser currently supports buses, loads, shunts, generators, branches, transformers, and dc lines",
    )
    pti_data = parse_pti(io)
    pm = _pti_to_powermodels!(pti_data; kwargs...)
    return pm
end


text:
using OpenTelemetryAPI
using OpenTelemetrySDK
using OpenTelemetryProto
using OpenTelemetryExporterOtlpProtoGrpc
using BenchmarkTools
using Logging
using Dates

Logging.disable_logging(Logging.Error)

suite = BenchmarkGroup()

#####

trace_suite = BenchmarkGroup()
suite["Trace"] = trace_suite

const TRACER = Tracer(
    provider = TracerProvider(;
        sampler = DEFAULT_ON,
        resource = Resource(
            attributes = StaticAttrs((;
                Symbol("service.name") => "A123456789",
                Symbol("service.version") => "1.34567890",
                Symbol("service.instance.id") => "123ab456-a123-12ab-12ab-12340a1abc12",
            )),
        ),
    ),
)

const DUMMY_TRACER = Tracer(provider = OpenTelemetryAPI.DUMMY_TRACER_PROVIDER)

function init_span(t)
    with_span(
        "benchmarkedSpan",
        t;
        attributes = Dict{String,TAttrVal}("long.attribute" => -10000000001000000000),
    ) do
        push!(current_span(), OpenTelemetryAPI.Event(name = "benchmarkEvent"))
    end
end

trace_suite["Create Span"] = @benchmarkable init_span($TRACER)

trace_suite["Create Dummy Span"] = @benchmarkable init_span($DUMMY_TRACER)

const TYPICAL_SPAN = OpenTelemetrySDK.Span(
    Ref{String}("TYPICAL_SPAN"),
    TRACER,
    INVALID_SPAN_CONTEXT,
    nothing,
    SPAN_KIND_INTERNAL,
    UInt(time() * 10^9),
    Ref{Union{Nothing,UInt}}(UInt(time() * 10^9)),
    DynamicAttrs(
        Dict{String,TAttrVal}(
            "aaa" => repeat("a", 32),
            "bbb" => repeat("b", 32),
            "ccc" => repeat("c", 32),
        ),
    ),
    Limited(Link[Link(INVALID_SPAN_CONTEXT, StaticAttrs(; a = 1, b = 2.0))]),
    Limited(Event[Event("xyz", UInt(time() * 10^9), StaticAttrs(; m = [1], n = "2"))]),
    Ref{SpanStatus}(SpanStatus(SPAN_STATUS_UNSET)),
)

trace_suite["Convert Span to Grpc"] = @benchmarkable convert(
    OpenTelemetryProto.OpentelemetryClients.opentelemetry.proto.collector.trace.v1.ExportTraceServiceRequest,
    TYPICAL_SPAN,
)
#####

meter_suite = BenchmarkGroup()
suite["Meter"] = meter_suite

const PROVIDER = MeterProvider(;
    views = [
        View("benchmark_counter"),
        View(
            "benchmark_histogram";
            aggregation = HistogramAgg{Int}(; boundaries = Tuple(100.0:100.0:900.0)),
        ),
    ],
)
const METER = Meter("benchmark_meter"; provider = PROVIDER)
const COUNTER = Counter{Int}("benchmark_counter", METER)

function update_counter(c, n)
    c(n; name = "a", code = 2, msg = "hi")
end

meter_suite["Update Counter"] =
    @benchmarkable update_counter(c, n) setup = (c = $COUNTER; n = rand(1:100))

const HISTOGRAM = Histogram{Int}("benchmark_histogram", METER)

function update_histogram(h, n)
    h(n; name = "a", code = 2, msg = "hi")
end

meter_suite["Update Histogram"] =
    @benchmarkable update_histogram(h, n) setup = (h = $HISTOGRAM; n = rand(1:1_000))

function convert_counter_to_grpc(m) end

meter_suite["Convert Metrics to Grpc"] = @benchmarkable convert(
    OpenTelemetryProto.OpentelemetryClients.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest,
    metrics(PROVIDER),
)
#####

tune!(suite)
results = run(suite)
println(results)
BenchmarkTools.save(joinpath(@__DIR__, "output.json"), median(results))


text:
# functions that are used for other purposes than machine learning or preprocessing

macro msg(_x)
    :(join(STDOUT,[Dates.format(now(),"HH:MM:SS"), $(esc(_x)),'\n'],' '); flush(STDOUT))
end

date(x)=(join(STDOUT,[Dates.format(now(),"HH:MM:SS"), x,'\n'],' '); flush(STDOUT))

type StopWatch
    tstart # start time
    nstart # start token
    ncurr  # current token
    nnext  # next token
    StopWatch()=new(time(), 0, 0, 1000)
end

function inc(s::StopWatch, n, step=1000)
    s.ncurr += n
    if s.ncurr >= s.nnext
        tcurr = time()
        dt = tcurr - s.tstart
        dn = s.ncurr - s.nstart
        s.tstart = tcurr
        s.nstart = s.ncurr
        s.nnext += step
        return dn/dt
    end
end

# labeled-attachment score calculator
function las(corpus)
    nword = ncorr = 0
    for s in corpus
        p = s.parse
        nword += length(s)
        ncorr += sum((s.head .== p.head) .& (s.deprel .== p.deprel))
    end
    return ncorr / nword
end

function empty_parses!(corpus)
    for s in corpus
        s.parse = nothing
    end
end

# To generat .conllu formatted files
# To preprocess the files from erenay,
# Replace the head and deprel fields w/ gold ones
function writeconllu1(goldfile, outputfile, erenayfile, v)
    sentences = load_conllu(goldfile, v) # gold-sentences
    out = open(outputfile, "w")
    deprels = Array{String}(length(v.deprels))
    for (k, v) in v.deprels; deprels[v]=k; end;
    s = ph = pd = nothing
    ns = nw = nl = 0
    for line in eachline(erenayfile)
        nl += 1
        if ismatch(r"^\d+\t", line)
            if s == nothing
                s = sentences[ns+1]
                ph = s.head
                pd = s.deprel
            end
            f = split(line, '\t')
            nw += 1
            if f[1] != "$nw"; error();end;
            if f[2] != s.word[nw]; error(); end
            f[7] = string(ph[nw])
            f[8] = deprels[pd[nw]]
            print(out, join(f, "\t"))
            print(out, "\n")
        else
            if line == ""
                if s == nothing; error(); end
                if nw != length(s.word); error(); end
                ns += 1; nw = 0
                s = ph = pd = nothing
            end
            print(out, line)
            print(out, "\n")
        end
    end
    if ns != length(sentences); error("#of sentences different");end;
    close(out)
end

# That generateds .conllu formatted file.
# Input: Erenay's conllu formatted file
# Output: Manipulate head and deprel columns
function writeconllu(sentences, inputfile, outputfile)
    v = sentences[1].vocab
    out = open(outputfile, "w")
    deprels = Array{String}(length(v.deprels))
    for (k, v) in v.deprels; deprels[v]=k;end;
    s = p = nothing
    ns = nw = nl = 0
    for line in eachline(inputfile)
        nl += 1
        if ismatch(r"^\d+\t", line)
            if s == nothing
                s = sentences[ns+1]
                p = s.parse
            end
            f = split(line, '\t')
            nw += 1
            if f[1] != "$nw"; error();end;
            if f[2] != s.word[nw]; error(); end
            f[7] = string(p.head[nw])
            f[8] = deprels[p.deprel[nw]]
            print(out, join(f, "\t"))
            print(out, "\n")
        else
            if line == ""
                if s== nothing; error("s can't be nothing");end;
                if nw != length(s.word); error();end;
                ns += 1; nw =0
                s = p = nothing
            end
            print(out, line)
            print(out, "\n")
        end
    end
    if ns != length(sentences); error("#of sentences different");end;
    close(out)
end

# There may be no need for the following methods
map2cpu(x)=(if isbits(x); x; else; map2cpu2(x); end)
map2cpu(x::KnetArray)=Array(x)
map2cpu(x::Tuple)=map(map2cpu,x)
map2cpu(x::AbstractString)=x
map2cpu(x::DataType)=x
map2cpu(x::Array)=map(map2cpu,x)
map2cpu{T<:Number}(x::Array{T})=x
map2cpu(x::Associative)=(y=Dict();for (k,v) in x; y[k] = map2cpu(x[k]); end; y)
map2cpu2(x)=(y=deepcopy(x); for f in fieldnames(x); setfield!(y,f,map2cpu(getfield(x,f))); end; y)

map2gpu(x)=(if isbits(x); x; else; map2gpu2(x); end)
map2gpu(x::KnetArray)=x
map2gpu(x::AbstractString)=x
map2gpu(x::DataType)=x
map2gpu(x::Tuple)=map(map2gpu,x)
map2gpu(x::Array)=map(map2gpu,x)
map2gpu{T<:AbstractFloat}(x::Array{T})=KnetArray(x)
map2gpu(x::Associative)=(y=Dict();for (k,v) in x; y[k] = map2gpu(x[k]); end; y)
map2gpu2(x)=(y=deepcopy(x); for f in fieldnames(x); setfield!(y,f,map2gpu(getfield(x,f))); end; y)


text:
module PatternDispatch
export @pattern, show_dispatch

include("Meta.jl")

include("Intern.jl")
include("Patterns.jl")
include("Nodes.jl")
include("Recode.jl")

include("PartialOrder.jl")
include("Dispatch.jl")
include("Encode.jl")

include("Methods.jl")
using .Methods
import .Methods.method_tables

end # module


text:
function show_with_header(io::IO, question::Question, header)
    condition = ifelse(is_numeric(question.value), ">=", "==")
    colname = header[question.column]
    print(io, "Is $colname $condition $(question.value)?")
end


text:
################################################################################
# ZeroFPR iterator (with L-BFGS directions)

mutable struct ZeroFPRIterator{I <: Integer, R <: Real, D, CS, FS, AS, CQ, FQ, AQ, G, HH} <: ProximalAlgorithm{I,D}
    x::D
    fs::FS
    As::AS
    fq::FQ
    Aq::AQ
    g::G
    gamma::R
    maxit::I
    tol::R
    adaptive::Bool
    verbose::I
    verbose_freq::I
    alpha::R
    beta::R
    tau::R
    y::D # gradient step
    xbar::D # proximal-gradient step
    xbarbar::D # proximal-gradient step
    xnew::D
    xnewbar::D
    H::HH # inverse Jacobian approximation
    FPR_x::D
    Aqx::CQ
    Asx::CS
    Aqxbar::CQ
    Asxbar::CS
    Aqxnew::CQ
    Asxnew::CS
    Aqd::CQ
    Asd::CS
    gradfq_Aqx::CQ
    gradfs_Asx::CS
    fs_Asx::R
    fq_Aqx::R
    f_Ax::R
    At_gradf_Ax::D
    Ast_gradfs_Asx::D
    Aqt_gradfq_Aqx::D
    g_xbar::R
    FBE_x::R
    FPR_xbar::D
    FPR_xbar_prev::D
    d::D
end

################################################################################
# Constructor

function ZeroFPRIterator(x0::D;
                         fs::FS=Zero(), As::AS=Identity(size(x0)),
                         fq::FQ=Zero(), Aq::AQ=Identity(size(x0)),
                         g::G=Zero(),
                         gamma::R=-1.0, maxit::I=10000, tol::R=1e-4, adaptive::Bool=false, memory::I=10,
                         verbose::I=1, verbose_freq::I=100, alpha::R=0.95, beta::R=0.5) where {I, R, D, FS, AS, FQ, AQ, G}
    x = copy(x0)
    y = zero(x0)
    xbar = zero(x0)
    xbarbar = zero(x0)
    xnew = zero(x0)
    xnewbar = zero(x0)
    xbar = zero(x0)
    FPR_x = zero(x0)
    FPR_xbar_prev = zero(x0)
    FPR_xbar = zero(x0)
    Aqx = Aq*x
    Asx = As*x
    Aqxbar = zero(Aqx)
    Asxbar = zero(Asx)
    Aqxnew = zero(Aqx)
    Asxnew = zero(Asx)
    Aqd = zero(Aqx)
    Asd = zero(Asx)
    gradfq_Aqx = zero(Aqx)
    gradfs_Asx = zero(Asx)
    At_gradf_Ax = zero(x0)
    Ast_gradfs_Asx = zero(x0)
    Aqt_gradfq_Aqx = zero(x0)
    d = zero(x0)
    H = LBFGS(x, memory)
    CQ = typeof(Aqx)
    CS = typeof(Asx)
    HH = typeof(H)
    ZeroFPRIterator{I, R, D, CS, FS, AS, CQ, FQ, AQ, G, HH}(x,
                 fs, As,
                 fq, Aq, g,
                 gamma, maxit, tol, adaptive, verbose, verbose_freq,
                 alpha, beta, one(R), y,
                 xbar, xbarbar, xnew, xnewbar,
                 H, FPR_x,
                 Aqx, Asx,
                 Aqxbar, Asxbar,
                 Aqxnew, Asxnew,
                 Aqd, Asd,
                 gradfq_Aqx, gradfs_Asx,
                 zero(R), zero(R), zero(R),
                 At_gradf_Ax, Ast_gradfs_Asx, Aqt_gradfq_Aqx,
                 zero(R), zero(R),
                 FPR_xbar, FPR_xbar_prev, d)
end

################################################################################
# Utility methods

maxit(sol::ZeroFPRIterator{I}) where {I} = sol.maxit

converged(sol::ZeroFPRIterator{I,R,D}, it::I) where {I,R,D} = it > 0 && maximum(abs,sol.FPR_x)/sol.gamma <= sol.tol

verbose(sol::ZeroFPRIterator) = sol.verbose > 0
verbose(sol::ZeroFPRIterator, it) = sol.verbose > 0 && (sol.verbose == 2 ? true : (it == 1 || it%sol.verbose_freq == 0))

function display(sol::ZeroFPRIterator)
    @printf("%6s | %10s | %10s | %10s | %10s |\n ", "it", "gamma", "fpr", "tau", "FBE")
    @printf("------|------------|------------|------------|------------|\n")
end
function display(sol::ZeroFPRIterator, it)
    @printf("%6d | %7.4e | %7.4e | %7.4e | %7.4e | \n", it, sol.gamma, maximum(abs,sol.FPR_x)/sol.gamma, sol.tau, sol.FBE_x)
end

function Base.show(io::IO, sol::ZeroFPRIterator)
    println(io, "ZeroFPR" )
    println(io, "fpr        : $(maximum(abs,sol.FPR_x))")
    println(io, "gamma      : $(sol.gamma)")
    println(io, "tau        : $(sol.tau)")
    print(  io, "FBE        : $(sol.FBE_x)")
end

################################################################################
# Initialization

function initialize!(sol::ZeroFPRIterator{I, R, D, CS, FS, AS, CQ, FQ, AQ, G, HH}) where {I, R, D, CS, FS, AS, CQ, FQ, AQ, G, HH}

    # reset L-BFGS operator (would be nice to have this option)
    # TODO add function reset!(::LBFGS) in AbstractOperators
    sol.H.currmem, sol.H.curridx = 0, 0
    sol.H.H = 1.0

    # compute first forward-backward step here
    mul!(sol.Aqx, sol.Aq, sol.x)
    sol.fq_Aqx = gradient!(sol.gradfq_Aqx, sol.fq, sol.Aqx)
    mul!(sol.Asx, sol.As, sol.x)
    sol.fs_Asx = gradient!(sol.gradfs_Asx, sol.fs, sol.Asx)
    sol.At_gradf_Ax .= sol.As'*sol.gradfs_Asx .+ sol.Aq'*sol.gradfq_Aqx
    sol.f_Ax = sol.fs_Asx + sol.fq_Aqx

    if sol.gamma <= 0.0 # estimate L in this case, and set gamma = 1/L
        # this part should be as follows:
        # 1) if adaptive = false and only fq is present then L is "accurate"
        # 2) otherwise L is "inaccurate" and set adaptive = true
        # TODO: implement case 1), now 2) is always performed
        xeps = sol.x .+ sqrt(eps())
        Aqxeps = sol.Aq*xeps
        gradfq_Aqxeps, = gradient(sol.fq, Aqxeps)
        Asxeps = sol.As*xeps
        gradfs_Asxeps, = gradient(sol.fs, Asxeps)
        At_gradf_Axeps = sol.As'*gradfs_Asxeps .+ sol.Aq'*gradfq_Aqxeps
        L = norm(sol.At_gradf_Ax .- At_gradf_Axeps)/(sqrt(eps()*length(xeps)))
        sol.adaptive = true
        # in both cases set gamma = 1/L
        sol.gamma = sol.alpha/L
    end

    sol.y .= sol.x .- sol.gamma .* sol.At_gradf_Ax
    sol.g_xbar = prox!(sol.xbar, sol.g, sol.y, sol.gamma)
    sol.FPR_x .= sol.x .- sol.xbar

    return sol.xbar

end

################################################################################
# Iteration

function iterate!(sol::ZeroFPRIterator{I, R, D, CS, FS, AS, CQ, FQ, AQ, G, HH}, it::I) where {I, R, D, CS, FS, AS, CQ, FQ, AQ, G, HH}

    # These need to be performed anyway (to compute xbarbar later on)
    mul!(sol.Aqxbar, sol.Aq, sol.xbar)
    fq_Aqxbar = gradient!(sol.gradfq_Aqx, sol.fq, sol.Aqxbar)
    mul!(sol.Asxbar, sol.As, sol.xbar)
    fs_Asxbar = gradient!(sol.gradfs_Asx, sol.fs, sol.Asxbar)
    f_Axbar = fs_Asxbar + fq_Aqxbar

    if sol.adaptive
        for it_gam = 1:100 # TODO: replace/complement with lower bound on gamma
            normFPR_x = norm(sol.FPR_x)
            uppbnd = sol.f_Ax - real(dot(sol.At_gradf_Ax, sol.FPR_x)) + 0.5/sol.gamma*normFPR_x^2
            if f_Axbar > uppbnd + 1e-6*abs(sol.f_Ax)
                sol.gamma = 0.5*sol.gamma
                sol.y .= sol.x .- sol.gamma .* sol.At_gradf_Ax
                sol.xbar, sol.g_xbar = prox(sol.g, sol.y, sol.gamma)
                sol.FPR_x .= sol.x .- sol.xbar
            else
                break
            end
            mul!(sol.Aqxbar, sol.Aq, sol.xbar)
            fq_Aqxbar = gradient!(sol.gradfq_Aqx, sol.fq, sol.Aqxbar)
            mul!(sol.Asxbar, sol.As, sol.xbar)
            fs_Asxbar = gradient!(sol.gradfs_Asx, sol.fs, sol.Asxbar)
            f_Axbar = fs_Asxbar + fq_Aqxbar
        end
    end

    # Compute value of FBE at x

    normFPR_x = norm(sol.FPR_x)
    FBE_x = sol.f_Ax - real(dot(sol.At_gradf_Ax, sol.FPR_x)) + 0.5/sol.gamma*normFPR_x^2 + sol.g_xbar

    # Compute search direction
    mul!(sol.Ast_gradfs_Asx, sol.As', sol.gradfs_Asx)
    mul!(sol.Aqt_gradfq_Aqx, sol.Aq', sol.gradfq_Aqx)
    sol.At_gradf_Ax .= sol.Ast_gradfs_Asx .+ sol.Aqt_gradfq_Aqx
    sol.y .= sol.xbar .- sol.gamma .* sol.At_gradf_Ax
    g_xbarbar = prox!(sol.xbarbar, sol.g, sol.y, sol.gamma)
    sol.FPR_xbar .= sol.xbar .- sol.xbarbar

    if it > 1
        update!(sol.H, sol.xbar, sol.xnewbar, sol.FPR_xbar, sol.FPR_xbar_prev)
    end
    mul!(sol.d, sol.H, ( x -> .-x ).(sol.FPR_xbar)) # TODO: not nice

    # Perform line-search over the FBE

    sol.tau = 1.0

    mul!(sol.Asd, sol.As, sol.d)
    mul!(sol.Aqd, sol.Aq, sol.d)

    sigma = 0.5*sol.beta/sol.gamma*(1.0-sol.alpha)

    g_xnewbar = zero(R)
    f_Axnew = zero(R)
    FBE_xnew = zero(R)

    maxit_tau = 10
    for it_tau = 1:maxit_tau # TODO: replace/complement with lower bound on tau
        sol.xnew .= sol.xbar .+ sol.tau .* sol.d
        sol.Asxnew .= sol.Asxbar .+ sol.tau .* sol.Asd
        sol.Aqxnew .= sol.Aqxbar .+ sol.tau .* sol.Aqd
        fs_Asxnew = gradient!(sol.gradfs_Asx, sol.fs, sol.Asxnew)
        # TODO: can precompute most of next line before the iteration
        fq_Aqxnew = gradient!(sol.gradfq_Aqx, sol.fq, sol.Aqxnew)
        f_Axnew = fs_Asxnew + fq_Aqxnew
        mul!(sol.Ast_gradfs_Asx, sol.As', sol.gradfs_Asx)
        mul!(sol.Aqt_gradfq_Aqx, sol.Aq', sol.gradfq_Aqx)
        sol.At_gradf_Ax .= sol.Ast_gradfs_Asx .+ sol.Aqt_gradfq_Aqx
        sol.y .= sol.xnew  .- sol.gamma .* sol.At_gradf_Ax
        g_xnewbar = prox!(sol.xnewbar, sol.g, sol.y, sol.gamma)
        sol.FPR_x .= sol.xnew .- sol.xnewbar
        normFPR_xnew = norm(sol.FPR_x)
        FBE_xnew = f_Axnew - real(dot(sol.At_gradf_Ax, sol.FPR_x)) + 0.5/sol.gamma*normFPR_xnew^2 + g_xnewbar
        if FBE_xnew <= FBE_x - sigma*normFPR_x^2
            break
        end
        sol.tau = 0.5*sol.tau
    end

    sol.FBE_x = FBE_xnew
    sol.x, sol.xnew = sol.xnew, sol.x
    sol.f_Ax = f_Axnew
    sol.g_xbar = g_xnewbar
    sol.FPR_xbar_prev, sol.FPR_xbar = sol.FPR_xbar, sol.FPR_xbar_prev
    sol.xbar, sol.xnewbar = sol.xnewbar, sol.xbar #xnewbar becames xbar_prev

    return sol.xbar

end

################################################################################
# Solver interface(s)

function ZeroFPR(x0; kwargs...)
    sol = ZeroFPRIterator(x0; kwargs...)
    it, point = run!(sol)
    return (it, point, sol)
end


text:
# Default barriers:
SyncBarriers.Barrier(n::Integer) = _Barrier(n, Threads.nthreads())
SyncBarriers.fuzzy_barrier(n::Integer) = _fuzzy_barrier(n, Threads.nthreads())
SyncBarriers.fuzzy_reduce_barrier(op, ::Type{T}, n::Integer) where {T} =
    _fuzzy_reduce_barrier(op, T, n, Threads.nthreads())
SyncBarriers.reduce_barrier(op, ::Type{T}, n::Integer) where {T} =
    _reduce_barrier(op, T, n, Threads.nthreads())

# TODO: find the best cutoff
# TODO: make it configurable?
function _Barrier(n::Integer, nthreads::Integer)
    nthreads = min(nthreads, n)
    if nthreads >= 32
        return DisseminationBarrier(n)
    else
        return CentralizedBarrier(n)
    end
end

function _fuzzy_barrier(n::Integer, nthreads::Integer)
    nthreads = min(nthreads, n)
    if nthreads >= 32
        return TreeBarrier{8}(n)
    else
        return CentralizedBarrier(n)
    end
end

function _fuzzy_reduce_barrier(op, ::Type{T}, n::Integer, nthreads::Integer) where {T}
    nthreads = min(nthreads, n)
    if nthreads >= 32
        return TreeBarrier{8,T}(op, n)
    elseif nthreads >= 16
        return FlatTreeBarrier{4,T}(op, n)
    else
        return FlatTreeBarrier{2,T}(op, n)
    end
end

function _reduce_barrier(op, ::Type{T}, n::Integer, nthreads::Integer) where {T}
    nthreads = min(nthreads, n)
    if nthreads >= 32
        return StaticTreeBarrier{8,8,T}(op, n)
    else
        return _fuzzy_reduce_barrier(op, T, n, nthreads)
    end
end

SyncBarriers.CentralizedBarrier(n) = CentralizedBarrier(n)
SyncBarriers.DisseminationBarrier(n) = DisseminationBarrier(n)
SyncBarriers.StaticTreeBarrier{NArrive,NDepart}(n) where {NArrive,NDepart} =
    StaticTreeBarrier{NArrive,NDepart}(n)
SyncBarriers.StaticTreeBarrier{NArrive,NDepart,T}(op, n) where {NArrive,NDepart,T} =
    StaticTreeBarrier{NArrive,NDepart,T}(op, n)
SyncBarriers.TreeBarrier{N}(n) where {N} = TreeBarrier{N}(n)
SyncBarriers.TreeBarrier{N,T}(op, n) where {N,T} = TreeBarrier{N,T}(op, n)
SyncBarriers.FlatTreeBarrier{N}(n) where {N} = FlatTreeBarrier{N}(n)
SyncBarriers.FlatTreeBarrier{N,T}(op, n) where {N,T} = FlatTreeBarrier{N,T}(op, n)

const FuzzyReduceBarrier{T,NBranches} =
    Union{TreeBarrier{NBranches,T},FlatTreeBarrier{NBranches,T}}
const FuzzyBarrier = Union{CentralizedBarrier,TreeBarrier,FlatTreeBarrier}

# Used only in testing ATM:
const ReduceBarrier{T} =
    Union{TreeBarrier{<:Any,T},FlatTreeBarrier{<:Any,T},StaticTreeBarrier{<:Any,<:Any,T}}
acctype(::Type{B}) where {T,B<:ReduceBarrier{T}} = T
acctype(b::SyncBarriers.Barrier) = acctype(typeof(b))

function SyncBarriers.cycle!(
    handle::BarrierHandle{<:FuzzyBarrier},
    spin::Union{Integer,Nothing} = nothing,
)
    islast = SyncBarriers.arrive!(handle)
    islast || SyncBarriers.depart!(handle, spin)
    return islast
end

SyncBarriers.arrive!(handle::BarrierHandle{<:FuzzyReduceBarrier{Nothing}}) =
    SyncBarriers.reduce_arrive!(handle, nothing) === Some(nothing)

Base.summary(io::IO, ::CentralizedBarrier) = show(io, SyncBarriers.CentralizedBarrier)
Base.summary(io::IO, ::DisseminationBarrier) = show(io, SyncBarriers.DisseminationBarrier)
Base.summary(io::IO, ::StaticTreeBarrier{NArrive,NDepart,T}) where {NArrive,NDepart,T} =
    show(io, SyncBarriers.StaticTreeBarrier{NArrive,NDepart,T})
Base.summary(io::IO, ::TreeBarrier{T,N}) where {T,N} = show(io, SyncBarriers.TreeBarrier{T,N})
Base.summary(io::IO, ::FlatTreeBarrier{T,N}) where {T,N} =
    show(io, SyncBarriers.FlatTreeBarrier{T,N})

function Base.show(io::IO, ::MIME"text/plain", barrier::SyncBarriers.Barrier)
    summary(io, barrier)
    print(io, " for ", length(barrier), " task(s)")
end


text:
#=
    Loads modelling
 =#

mutable struct Demand <: AbstractDemand
     # Variables
     carrier::EnergyCarrier
     timestamp::Array{DateTime,3}

     # Inner constructor
     Demand(; carrier = Electricity()) = new(carrier)
end

### Preallocation
function preallocate!(ld::Demand, nh::Int64, ny::Int64, ns::Int64)
    ld.carrier.power = convert(SharedArray,zeros(nh, ny, ns))
    ld.timestamp = Array{DateTime}(undef,(nh, ny, ns))
    return ld
end


text:
#! /usr/bin/julia

# Rosetta Code, Determine if a string is numeric

function isnumeric{T<:String}(s::T)
    isa(parse(s), Number)
end

tests = ["1",
         "-121",
         "one",
         "pi",
         "1 + 1",
         "NaN",
         "1234567890123456789",
         "1234567890123456789123456789",
         "1234567890123456789123456789.0",
         "1.3",
         "1.4e10",
         "Inf",
         "1//2",
         "1.0 + 1.0im"]

for t in tests
    fl = isnumeric(t) ? "is" : "is not"
    println(@sprintf("    %35s %s a direct numeric literal.", t, fl))
end

max_stars_repo_name: MichaeLeroy/rosetta-code

max_issues_repo_name: MichaeLeroy/rosetta-code

max_forks_repo_name: MichaeLeroy/rosetta-code


text:
using Documenter
using Diderot

makedocs(sitename="Diderot")

deploydocs(
    repo = "github.com/rschwarz/Diderot.jl.git",
)