text:
@testset "Renewable 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 HydroEnergyReservoir, consider changing the device models"
    model = DeviceModel(HydroEnergyReservoir, HydroDispatchRunOfRiver)
    c_sys5 = PSB.build_system(PSITestSystems, "c_sys5")
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5)
    @test_logs (:info,) (:warn, warn_message) match_mode = :any mock_construct_device!(
        op_problem,
        model,
    )
    c_sys14 = PSB.build_system(PSITestSystems, "c_sys14")
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys14)
    @test_logs (:info,) (:warn, warn_message) match_mode = :any mock_construct_device!(
        op_problem,
        model,
    )
end

###################################
###### FIXED OUTPUT TESTS #########
###################################

@testset "Hydro DCPLossLess FixedOutput" begin
    model = DeviceModel(HydroDispatch, FixedOutput)
    c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hy;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 0, 0, 0, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hy)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 0, 0, 0, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hy;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 0, 0, 0, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro DCPLossLess HydroEnergyReservoir with FixedOutput formulations" begin
    model = DeviceModel(HydroEnergyReservoir, FixedOutput)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 0, 0, 0, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 0, 0, 0, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 0, 0, 0, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

###################################
### RUN OF RIVER DISPATCH TESTS ###
###################################

@testset "Hydro DCPLossLess HydroDispatch with HydroDispatchRunOfRiver formulations" begin
    model = DeviceModel(HydroDispatch, HydroDispatchRunOfRiver)
    c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hy;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 24, 0, 24, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hy)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 24, 0, 24, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPPowerModel HydroDispatch with HydroDispatchRunOfRiver formulations" begin
    model = DeviceModel(HydroDispatch, HydroDispatchRunOfRiver)
    c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hy;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 48, 0, 48, 24, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hy)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 48, 0, 48, 24, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hy;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 2, 0, 2, 2, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro DCPLossLess HydroEnergyReservoir with HydroDispatchRunOfRiver formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroDispatchRunOfRiver)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 24, 0, 24, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 24, 0, 24, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 1, 0, 1, 1, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPPowerModel HydroEnergyReservoir with HydroDispatchRunOfRiver formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroDispatchRunOfRiver)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 48, 0, 48, 24, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 48, 0, 48, 24, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 2, 0, 2, 2, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

###################################
#### RUN OF RIVER COMMIT TESTS ####
###################################

@testset "Hydro DCPLossLess HydroDispatch with HydroCommitmentRunOfRiver formulations" begin
    model = DeviceModel(HydroDispatch, HydroCommitmentRunOfRiver)
    c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hy;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 48, 0, 48, 24, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hy)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 48, 0, 48, 24, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPPowerModel HydroDispatch with HydroCommitmentRunOfRiver formulations" begin
    model = DeviceModel(HydroDispatch, HydroCommitmentRunOfRiver)
    c_sys5_hy = PSB.build_system(PSITestSystems, "c_sys5_hy")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hy;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 72, 0, 72, 48, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hy)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 72, 0, 72, 48, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hy;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 3, 0, 2, 2, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro DCPLossLess HydroEnergyReservoir with HydroCommitmentRunOfRiver formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroCommitmentRunOfRiver)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 48, 0, 48, 24, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 48, 0, 48, 24, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 2, 0, 1, 1, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPPowerModel HydroEnergyReservoir with HydroCommitmentRunOfRiver formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroCommitmentRunOfRiver)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 72, 0, 72, 48, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 72, 0, 72, 48, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 3, 0, 2, 2, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

#########################################
#### RESERVOIR BUDGET DISPATCH TESTS ####
#########################################

@testset "Hydro DCPLossLess HydroEnergyReservoir with HydroDispatchReservoirBudget Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroDispatchReservoirBudget)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 24, 0, 1, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 24, 0, 1, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 1, 0, 1, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPPowerModel HydroEnergyReservoir with HydroDispatchReservoirBudget Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroDispatchReservoirBudget)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 48, 0, 1, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 48, 0, 1, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 2, 0, 1, 0, 0, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

#########################################
#### PUMPED STORAGE DISPATCH TESTS ####
#########################################

@testset "Hydro DCPLossLess HydroPumpedStorage with HydroDispatchPumpedStorage Formulations" begin
    model = DeviceModel(HydroPumpedStorage, HydroDispatchPumpedStorage)
    c_sys5_phes_ed = PSB.build_system(PSITestSystems, "c_sys5_phes_ed")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_phes_ed;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 60, 0, 24, 24, 24, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_phes_ed)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 60, 0, 24, 24, 24, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_phes_ed;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 5, 0, 2, 2, 2, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro DCPLossLess HydroPumpedStorage with HydroDispatchPumpedStoragewReservation Formulations" begin
    model = DeviceModel(HydroPumpedStorage, HydroDispatchPumpedStoragewReservation)
    c_sys5_phes_ed = PSB.build_system(PSITestSystems, "c_sys5_phes_ed")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_phes_ed;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 72, 0, 24, 24, 24, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_phes_ed)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 72, 0, 24, 24, 24, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_phes_ed;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 6, 0, 2, 2, 2, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

#########################################
### RESERVOIR BUDGET COMMITMENT TESTS ###
#########################################

@testset "Hydro DCPLossLess HydroEnergyReservoir with HydroCommitmentReservoirBudget Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroCommitmentReservoirBudget)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 48, 0, 25, 24, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 48, 0, 25, 24, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 2, 0, 2, 1, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPPowerModel HydroEnergyReservoir with HydroCommitmentReservoirBudget Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroCommitmentReservoirBudget)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 72, 0, 49, 48, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 72, 0, 49, 48, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 3, 0, 3, 2, 0, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

#########################################
### RESERVOIR STORAGE DISPATCH TESTS ####
#########################################

@testset "Hydro DCPLossLess HydroEnergyReservoir with HydroDispatchReservoirStorage Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroDispatchReservoirStorage)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 120, 0, 0, 0, 48, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 120, 0, 0, 0, 48, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 5, 0, 0, 0, 2, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPLossLess HydroEnergyReservoir with HydroDispatchReservoirStorage Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroDispatchReservoirStorage)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 144, 0, 0, 0, 48, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 144, 0, 0, 0, 48, false)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 6, 0, 0, 0, 2, false)
    psi_checkobjfun_test(op_problem, GAEVF)
end

#########################################
### RESERVOIR STORAGE COMMITMENT TESTS ##
#########################################

@testset "Hydro DCPLossLess HydroEnergyReservoir with HydroCommitmentReservoirStorage Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroCommitmentReservoirStorage)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 144, 0, 24, 24, 48, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, DCPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 144, 0, 24, 24, 48, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        DCPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 6, 0, 1, 1, 2, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Hydro ACPLossLess HydroEnergyReservoir with HydroCommitmentReservoirStorage Formulations" begin
    model = DeviceModel(HydroEnergyReservoir, HydroCommitmentReservoirStorage)
    c_sys5_hyd = PSB.build_system(PSITestSystems, "c_sys5_hyd")

    # Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_parameters = true,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, true, 168, 0, 48, 48, 48, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Parameters Testing
    op_problem = OperationsProblem(MockOperationProblem, ACPPowerModel, c_sys5_hyd)
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 168, 0, 48, 48, 48, true)
    psi_checkobjfun_test(op_problem, GAEVF)

    # No Forecast - No Parameters Testing
    op_problem = OperationsProblem(
        MockOperationProblem,
        ACPPowerModel,
        c_sys5_hyd;
        use_forecast_data = false,
    )
    mock_construct_device!(op_problem, model)
    moi_tests(op_problem, false, 7, 0, 2, 2, 2, true)
    psi_checkobjfun_test(op_problem, GAEVF)
end

@testset "Solving ED Hydro System using Dispatch Run of River" begin
    sys = PSB.build_system(PSITestSystems, "c_sys5_hy")
    parameters_value = [true, false]
    networks = [ACPPowerModel, DCPPowerModel]

    test_results = Dict{Any, Float64}(ACPPowerModel => 177526.0, DCPPowerModel => 175521.0)

    for net in networks, p in parameters_value
        @info("Test solve HydroRoR ED with $(net) network")
        @testset "HydroRoR ED model $(net) and use_parameters = $(p)" begin
            template = get_thermal_dispatch_template_network(net)
            set_device_model!(template, HydroDispatch, HydroDispatchRunOfRiver)
            ED = OperationsProblem(
                EconomicDispatchProblem,
                template,
                sys;
                optimizer = ipopt_optimizer,
                use_parameters = true,
            )
            @test build!(ED; output_dir = mktempdir(cleanup = true)) ==
                  PSI.BuildStatus.BUILT
            psi_checksolve_test(
                ED,
                [MOI.OPTIMAL, MOI.LOCALLY_SOLVED],
                test_results[net],
                1000,
            )
        end
    end
end

@testset "Solving ED Hydro System using Commitment Run of River" begin
    sys = PSB.build_system(PSITestSystems, "c_sys5_hy")
    parameters_value = [true, false]
    net = DCPPowerModel

    template = get_thermal_dispatch_template_network(net)
    set_device_model!(template, HydroDispatch, HydroCommitmentRunOfRiver)
    for p in parameters_value
        @testset "HydroRoR ED model $(net) and use_parameters = $(p)" begin
            ED = OperationsProblem(
                UnitCommitmentProblem,
                template,
                sys;
                optimizer = GLPK_optimizer,
                use_parameters = p,
            )
            @test build!(ED; output_dir = mktempdir(cleanup = true)) ==
                  PSI.BuildStatus.BUILT
            psi_checksolve_test(ED, [MOI.OPTIMAL, MOI.LOCALLY_SOLVED], 175521.0, 1000)
        end
    end
end

@testset "Solving ED Hydro System using Dispatch with Reservoir" begin
    sys = PSB.build_system(PSITestSystems, "c_sys5_hyd")
    parameters_value = [true, false]
    networks = [ACPPowerModel, DCPPowerModel]
    models = [HydroDispatchReservoirBudget, HydroDispatchReservoirStorage]
    test_results = Dict{Any, Float64}(
        (ACPPowerModel, HydroDispatchReservoirBudget) => 33423.0,
        (DCPPowerModel, HydroDispatchReservoirBudget) => 33042.0,
        (ACPPowerModel, HydroDispatchReservoirStorage) => 217572.0,
        (DCPPowerModel, HydroDispatchReservoirStorage) => 215228.0,
    )
    parameters_value = [true, false]

    for net in networks, mod in models, p in parameters_value
        @testset "$(mod) ED model on $(net) and use_parameters = $(p)" begin
            template = get_thermal_dispatch_template_network(net)
            set_device_model!(template, HydroEnergyReservoir, mod)

            ED = OperationsProblem(
                EconomicDispatchProblem,
                template,
                sys;
                optimizer = ipopt_optimizer,
                use_parameters = p,
            )
            @test build!(ED; output_dir = mktempdir(cleanup = true)) ==
                  PSI.BuildStatus.BUILT
            psi_checksolve_test(
                ED,
                [MOI.OPTIMAL, MOI.LOCALLY_SOLVED],
                test_results[(net, mod)],
                10000,
            )
        end
    end
end

@testset "Solving ED Hydro System using Commitment with Reservoir" begin
    sys = PSB.build_system(PSITestSystems, "c_sys5_hyd")
    parameters_value = [true, false]
    net = DCPPowerModel
    models = [HydroCommitmentReservoirBudget, HydroCommitmentReservoirStorage]
    test_results = Dict{Any, Float64}(
        HydroCommitmentReservoirBudget => 33042.0,
        HydroCommitmentReservoirStorage => 215228.0,
    )

    for mod in models, p in parameters_value
        @testset "$(mod) ED model on $(net) and use_parameters = $(p)" begin
            template = get_thermal_dispatch_template_network(net)
            set_device_model!(template, HydroEnergyReservoir, mod)

            ED = OperationsProblem(
                UnitCommitmentProblem,
                template,
                sys;
                optimizer = GLPK_optimizer,
                use_parameters = p,
            )
            @test build!(ED; output_dir = mktempdir(cleanup = true)) ==
                  PSI.BuildStatus.BUILT
            psi_checksolve_test(
                ED,
                [MOI.OPTIMAL, MOI.LOCALLY_SOLVED],
                test_results[mod],
                10000,
            )
        end
    end
end


text:
using CTakesParser
using Test
using DataFrames
using CSV

function test_output_df(df)
    @test nrow(df) == 157
    # @test sum(completecases(df)) == 134
    @test sum(df[:scheme] .== "SNOMEDCT_US") == 149
    @test sum(df[:scheme] .== "RXNORM") == 8
    @test sum(df[:refsem] .== "UmlsConcept") == 157
end

function runtests()
    #test parsing individual file
    @testset "Parse file" begin
        println("-----------------------------------------")
        file_in = dirname(@__FILE__) * "/notes_in/mts_sample_note_97_1152.txt.xmi"
        ctakes_df = CTakesParser.parse_output_v4(file_in)
        test_output_df(ctakes_df)
    end

    #test parsing full directory
    @testset "Parse directory" begin
        println("-----------------------------------------")
        dir_in = dirname(@__FILE__) * "/notes_in/"
        dir_out = dirname(@__FILE__) * "/notes_out/"

        parse_output_dir(dir_in, dir_out)

        #read output files and test
        for f in [x for x in readdir(dir_out) if !endswith(x, ".log")]
            println("File: ", f)
            df =  CSV.read(dir_out*f)
            test_output_df(df)
        end
    end
end

runtests()

ext: jl


text:
"""
```
Kalman{S<:AbstractFloat}
```
### Fields:

- `loglh`: vector of conditional log-likelihoods log p(y_t | y_{1:t-1}), t = 1:T
- `s_T`: state vector in the last period for which data is provided
- `P_T`: variance-covariance matrix for `s_T`
- `s_pred`: `Ns` x `Nt` matrix of s_{t|t-1}, t = 1:T
- `P_pred`: `Ns` x `Ns` x `Nt` array of P_{t|t-1}, t = 1:T
- `s_filt`: `Ns` x `Nt` matrix of s_{t|t}, t = 1:T
- `P_filt`: `Ns` x `Ns` x `Nt` array of P_{t|t}, t = 1:T
- `s_0`: starting-period state vector. If there are presample periods in the
  data, then `s_0` is the state vector at the end of the presample/beginning of
  the main sample
- `P_0`: variance-covariance matrix for `s_0`
- `total_loglh`: log p(y_{1:t})
"""
struct Kalman{S<:AbstractFloat}
    loglh::Vector{S}            # log p(y_t | y_{1:t-1}), t = 1:T
    s_pred::AbstractArray{S}    # s_{t|t-1}, t = 1:T
    P_pred::Array{S, 3}         # P_{t|t-1}, t = 1:T
    s_filt::AbstractArray{S}    # s_{t|t}, t = 1:T
    P_filt::Array{S, 3}         # P_{t|t}, t = 1:T
    s_0::Vector{S}              # s_0
    P_0::AbstractArray{S}       # P_0
    s_T::Vector{S}              # s_{T|T}
    P_T::AbstractArray{S}       # P_{T|T}
    total_loglh::S              # log p(y_{1:t})
end

function Kalman(loglh::Vector{S},
                s_pred::AbstractArray{S}, P_pred::Array{S, 3},
                s_filt::AbstractArray{S}, P_filt::Array{S, 3},
                s_0::Vector{S}, P_0::AbstractArray{S},
                s_T::Vector{S}, P_T::AbstractArray{S}) where S<:AbstractFloat

    return Kalman{S}(loglh, s_pred, P_pred, s_filt, P_filt, s_0, P_0, s_T, P_T, sum(loglh))
end

function Base.getindex(K::Kalman, d::Symbol)
    if d in (:loglh, :s_pred, :P_pred, :s_filt, :P_filt, :s_0, :P_0, :s_T, :P_T, :total_loglh)
        return getfield(K, d)
    else
        throw(KeyError(d))
    end
end

function Base.getindex(kal::Kalman, inds::Union{Int, UnitRange{Int}})
    t0 = first(inds)
    t1 = last(inds)

    return DSGE.Kalman(kal[:loglh][inds],        # loglh
                       kal[:s_pred][:,    inds], # s_pred
                       kal[:P_pred][:, :, inds], # P_pred
                       kal[:s_filt][:,    inds], # filt
                       kal[:P_filt][:, :, inds], # P_filt
                       kal[:s_filt][:,    t0],   # s_0
                       kal[:P_filt][:, :, t0],   # P_0
                       kal[:s_filt][:,    t1],   # s_T
                       kal[:P_filt][:, :, t1],   # P_T
                       sum(kal[:loglh][inds]))   # total_loglh
end

function Base.cat(m::AbstractDSGEModel, k1::Kalman{S},
                  k2::Kalman{S}; allout::Bool = true) where S<:AbstractFloat

    loglh  = cat(k1[:loglh], k2[:loglh], dims = 1)
    s_pred = cat(k1[:s_pred], k2[:s_pred], dims = 2)
    P_pred = cat(k1[:P_pred], k2[:P_pred], dims = 3)
    s_filt = cat(k1[:s_filt], k2[:s_filt], dims = 2)
    P_filt = cat(k1[:P_filt], k2[:P_filt], dims = 3)
    s_0    = k1[:s_0]
    P_0    = k1[:P_0]
    s_T    = k2[:s_T]
    P_T    = k2[:P_T]
    total_loglh = k1[:total_loglh] + k2[:total_loglh]

    return Kalman(loglh, s_pred, P_pred, s_filt, P_filt, s_0, P_0, s_T, P_T, total_loglh)
end

"""
```
zlb_regime_indices(m, data, start_date = date_presample_start(m))
```

Returns a Vector{UnitRange{Int64}} of index ranges for the pre- and post-ZLB
regimes. The optional argument `start_date` indicates the first quarter of
`data`.
"""
function zlb_regime_indices(m::AbstractDSGEModel{S}, data::AbstractArray,
                            start_date::Dates.Date=date_presample_start(m)) where S<:AbstractFloat
    T = size(data, 2)
    if n_anticipated_shocks(m) > 0 && !isempty(data)
        if start_date < date_presample_start(m)
            error("Start date $start_date must be >= date_presample_start(m)")

        elseif 0 < subtract_quarters(date_zlb_start(m), start_date) < T

            n_nozlb_periods = subtract_quarters(date_zlb_start(m), start_date)
            regime_inds::Vector{UnitRange{Int64}} = [1:n_nozlb_periods, (n_nozlb_periods+1):T]

        else
            regime_inds = UnitRange{Int64}[1:T]
        end
    else
        regime_inds = UnitRange{Int64}[1:T]
    end
    return regime_inds
end

"""
```
zlb_regime_matrices(m, system, start_date = date_presample_start(m))
```
Returns `TTTs, RRRs, CCCs, QQs, ZZs, DDs, EEs`, an 8-tuple of
`Vector{AbstractArray{S}}`s and `Vector{Vector{S}}`s of system matrices for the pre-
and post-ZLB regimes. Of these, only `QQ` changes from pre- to post-ZLB: the
entries corresponding to anticipated shock variances are zeroed out pre-ZLB.
"""
function zlb_regime_matrices(m::AbstractDSGEModel{S}, system::System{S},
                             start_date::Dates.Date=date_presample_start(m)) where S<:AbstractFloat
    if n_anticipated_shocks(m) > 0
        if start_date < date_presample_start(m)
            error("Start date $start_date must be >= date_presample_start(m)")

        # TODO: This technically doesn't handle the case where the end_date of the sample
        # is before the start of the ZLB
        elseif date_presample_start(m) <= start_date <= date_zlb_start(m)
            n_regimes = 2

            shock_inds = inds_shocks_no_ant(m)
            QQ_ZLB = system[:QQ]
            QQ_preZLB = zeros(size(QQ_ZLB))
            QQ_preZLB[shock_inds, shock_inds] = QQ_ZLB[shock_inds, shock_inds]
            QQs = Matrix{S}[QQ_preZLB, QQ_ZLB]

        elseif date_zlb_start(m) < start_date
            n_regimes = 1
            QQs = Matrix{S}[system[:QQ]]
        end
    else
        n_regimes = 1
        QQs = Matrix{S}[system[:QQ]]
    end

    TTTs = fill(system[:TTT], n_regimes)
    RRRs = fill(system[:RRR], n_regimes)
    CCCs = fill(system[:CCC], n_regimes)
    ZZs  = fill(system[:ZZ], n_regimes)
    DDs  = fill(system[:DD], n_regimes)
    EEs  = fill(system[:EE], n_regimes)

    return TTTs, RRRs, CCCs, QQs, ZZs, DDs, EEs
end


text:
"""
Google Compute Engine API
"""
module _compute

export compute

using ..api
using ...root

"""
Google Compute Engine API root.
"""
compute = APIRoot(
    "$API_ROOT/compute/v1/projects/{project}",
    Dict(
        "cloud-platform" => "Full access to all resources and services in the specified Cloud Platform project.",
        "compute" => "Read-write access to Google Compute Engine methods.",
        "compute.readonly" => "Read-only access to Google Compute Engine methods.",
    );
    Address=APIResource("regions/{region}/addresses";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/addresses", "Retrieves an aggregated list of addresses."),
        delete=APIMethod(:DELETE, "{address}", "Deletes the specified address resource."),
        get=APIMethod(:GET, "{address}", "Returns the specified address resource."),
        insert=APIMethod(:POST, "", "Creates an address resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of addresses contained within the specified region."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Autoscaler=APIResource("zones/{zone}/autoscalers";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/autoscalers", "Retrieves an aggregated list of autoscalers."),
        delete=APIMethod(:DELETE, "{autoscaler}", "Deletes the specified autoscaler."),
        get=APIMethod(:GET, "{autoscaler}", "Returns the specified autoscaler resource. Get a list of available autoscalers by making a list() request."),
        insert=APIMethod(:POST, "", "Creates an autoscaler in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of autoscalers contained within the specified zone."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "", "Updates an autoscaler in the specified project using the data included in the request. This method supports patch semantics. Required query parameters: autoscaler"),
        update=APIMethod(:PUT, "", "Updates an autoscaler in the specified project using the data included in the request."),
    ),
    BackendService=APIResource("global/backendServices";
        delete=APIMethod(:DELETE, "{backendService}", "Deletes the specified BackendService resource."),
        get=APIMethod(:GET, "{backendService}", "Returns the specified BackendService resource. Get a list of available backend services by making a list() request."),
        getHealth=APIMethod(:POST, "{backendService}/getHealth", "Gets the most recent health check results for this BackendService."),
        insert=APIMethod(:POST, "", "Creates a BackendService resource in the specified project using the data included in the request. There are several restrictions and guidelines to keep in mind when creating a backend service. Read Restrictions and Guidelines for more information."),
        list=APIMethod(:GET, "", "Retrieves the list of BackendService resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "{backendService}", "Updates the specified BackendService resource with the data included in the request. There are several restrictions and guidelines to keep in mind when updating a backend service. Read Restrictions and Guidelines for more information. This method supports patch semantics."),
        update=APIMethod(:PUT, "{backendService}", "Updates the specified BackendService resource with the data included in the request. There are several restrictions and guidelines to keep in mind when updating a backend service. Read Restrictions and Guidelines for more information."),
    ),
    DiskType=APIResource("zones/{zone}/diskTypes";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/diskTypes", "Retrieves an aggregated list of disk types."),
        get=APIMethod(:GET, "{diskType}", "Returns the specified disk type. Get a list of available disk types by making a list() request."),
        list=APIMethod(:GET, "", "Retrieves a list of disk types available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Disk=APIResource("zones/{zone}/disks";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/disks", "Retrieves an aggregated list of persistent disks."),
        createSnapshot=APIMethod(:POST, "{disk}/createSnapshot", "Creates a snapshot of a specified persistent disk."),
        delete=APIMethod(:DELETE, "{disk}", "Deletes the specified persistent disk. Deleting a disk removes its data permanently and is irreversible. However, deleting a disk does not delete any snapshots previously made from the disk. You must separately delete snapshots."),
        get=APIMethod(:GET, "{disk}", "Returns a specified persistent disk. Get a list of available persistent disks by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a persistent disk in the specified project using the data in the request. You can create a disk with a sourceImage, a sourceSnapshot, or create an empty 500 GB data disk by omitting all properties. You can also create a disk that is larger than the default size by specifying the sizeGb property."),
        list=APIMethod(:GET, "", "Retrieves a list of persistent disks contained within the specified zone."; transform=(x, t) -> map(t, get(x, :items, []))),
        resize=APIMethod(:POST, "{disk}/resize", "Resizes the specified persistent disk."),
    ),
    Firewall=APIResource("global/firewalls";
        delete=APIMethod(:DELETE, "{firewall}", "Deletes the specified firewall."),
        get=APIMethod(:GET, "{firewall}", "Returns the specified firewall."),
        insert=APIMethod(:POST, "", "Creates a firewall rule in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of firewall rules available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "{firewall}", "Updates the specified firewall rule with the data included in the request. This method supports patch semantics."),
        update=APIMethod(:PUT, "{firewall}", "Updates the specified firewall rule with the data included in the request."),
    ),
    ForwardingRule=APIResource("regions/{region}/forwardingRules";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/forwardingRules", "Retrieves an aggregated list of forwarding rules."),
        delete=APIMethod(:DELETE, "{forwardingRule}", "Deletes the specified ForwardingRule resource."),
        get=APIMethod(:GET, "{forwardingRule}", "Returns the specified ForwardingRule resource."),
        insert=APIMethod(:POST, "", "Creates a ForwardingRule resource in the specified project and region using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of ForwardingRule resources available to the specified project and region."; transform=(x, t) -> map(t, get(x, :items, []))),
        setTarget=APIMethod(:POST, "{forwardingRule}/setTarget", "Changes target URL for forwarding rule. The new target should be of the same type as the old target."),
    ),
    GlobalAddress=APIResource("global/addresses";
        delete=APIMethod(:DELETE, "{address}", "Deletes the specified address resource."),
        get=APIMethod(:GET, "{address}", "Returns the specified address resource. Get a list of available addresses by making a list() request."),
        insert=APIMethod(:POST, "", "Creates an address resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of global addresses."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    GlobalForwardingRule=APIResource("global/forwardingRules";
        delete=APIMethod(:DELETE, "{forwardingRule}", "Deletes the specified ForwardingRule resource."),
        get=APIMethod(:GET, "{forwardingRule}", "Returns the specified ForwardingRule resource. Get a list of available forwarding rules by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a ForwardingRule resource in the specified project and region using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of ForwardingRule resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        setTarget=APIMethod(:POST, "{forwardingRule}/setTarget", "Changes target URL for forwarding rule. The new target should be of the same type as the old target."),
    ),
    GlobalOperation=APIResource("global/operations";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/operations", "Retrieves an aggregated list of all operations."),
        delete=APIMethod(:DELETE, "{operation}", "Deletes the specified Operations resource."),
        get=APIMethod(:GET, "{operation}", "Retrieves the specified Operations resource. Get a list of operations by making a list() request."),
        list=APIMethod(:GET, "", "Retrieves a list of Operation resources contained within the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    HealthCheck=APIResource("global/healthChecks";
        delete=APIMethod(:DELETE, "{healthCheck}", "Deletes the specified HealthCheck resource."),
        get=APIMethod(:GET, "{healthCheck}", "Returns the specified HealthCheck resource. Get a list of available health checks by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a HealthCheck resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of HealthCheck resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "{healthCheck}", "Updates a HealthCheck resource in the specified project using the data included in the request. This method supports patch semantics."),
        update=APIMethod(:PUT, "{healthCheck}", "Updates a HealthCheck resource in the specified project using the data included in the request."),
    ),
    HttpHealthCheck=APIResource("global/httpHealthChecks";
        delete=APIMethod(:DELETE, "{httpHealthCheck}", "Deletes the specified HttpHealthCheck resource."),
        get=APIMethod(:GET, "{httpHealthCheck}", "Returns the specified HttpHealthCheck resource. Get a list of available HTTP health checks by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a HttpHealthCheck resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of HttpHealthCheck resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "{httpHealthCheck}", "Updates a HttpHealthCheck resource in the specified project using the data included in the request. This method supports patch semantics."),
        update=APIMethod(:PUT, "{httpHealthCheck}", "Updates a HttpHealthCheck resource in the specified project using the data included in the request."),
    ),
    HttpsHealthCheck=APIResource("global/httpsHealthChecks";
        delete=APIMethod(:DELETE, "{httpsHealthCheck}", "Deletes the specified HttpsHealthCheck resource."),
        get=APIMethod(:GET, "{httpsHealthCheck}", "Returns the specified HttpsHealthCheck resource. Get a list of available HTTPS health checks by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a HttpsHealthCheck resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of HttpsHealthCheck resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "{httpsHealthCheck}", "Updates a HttpsHealthCheck resource in the specified project using the data included in the request. This method supports patch semantics."),
        update=APIMethod(:PUT, "{httpsHealthCheck}", "Updates a HttpsHealthCheck resource in the specified project using the data included in the request."),
    ),
    Image=APIResource("global/images";
        delete=APIMethod(:DELETE, "{image}", "Deletes the specified image."),
        deprecate=APIMethod(:POST, "{image}/deprecate", "Sets the deprecation status of an image. If an empty request body is given, clears the deprecation status instead."),
        get=APIMethod(:GET, "{image}", "Returns the specified image. Get a list of available images by making a list() request."),
        getFromFamily=APIMethod(:GET, "family/{family}", "Returns the latest image that is part of an image family and is not deprecated."),
        insert=APIMethod(:POST, "", "Creates an image in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of private images available to the specified project. Private images are images you create that belong to your project. This method does not get any images that belong to other projects, including publicly-available images, like Debian 8. If you want to get a list of publicly-available images, use this method to make a request to the respective image project, such as debian-cloud or windows-cloud."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    InstanceGroupManager=APIResource("zones/{zone}/instanceGroupManagers";
        abandonInstances=APIMethod(:POST, "{instanceGroupManager}/abandonInstances", "Schedules a group action to remove the specified instances from the managed instance group. Abandoning an instance does not delete the instance, but it does remove the instance from any target pools that are applied by the managed instance group. This method reduces the targetSize of the managed instance group by the number of instances that you abandon. This operation is marked as DONE when the action is scheduled even if the instances have not yet been removed from the group. You must separately verify the status of the abandoning action with the listmanagedinstances method."),
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/instanceGroupManagers", "Retrieves the list of managed instance groups and groups them by zone."),
        delete=APIMethod(:DELETE, "instanceGroupManager", "Deletes the specified managed instance group and all of the instances in that group. Note that the instance group must not belong to a backend service. Read Deleting an instance group for more information."),
        deleteInstances=APIMethod(:POST, "{instanceGroupManager}/deleteInstances", "Schedules a group action to delete the specified instances in the managed instance group. The instances are also removed from any target pools of which they were a member. This method reduces the targetSize of the managed instance group by the number of instances that you delete. This operation is marked as DONE when the action is scheduled even if the instances are still being deleted. You must separately verify the status of the deleting action with the listmanagedinstances method."),
        get=APIMethod(:GET, "{instanceGroupManager}", "Returns all of the details about the specified managed instance group. Get a list of available managed instance groups by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a managed instance group using the information that you specify in the request. After the group is created, it schedules an action to create instances in the group using the specified instance template. This operation is marked as DONE when the group is created even if the instances in the group have not yet been created. You must separately verify the status of the individual instances with the listmanagedinstances method."),
        list=APIMethod(:GET, "", "Retrieves a list of managed instance groups that are contained within the specified project and zone."; transform=(x, t) -> map(t, get(x, :items, []))),
        listManagedInstances=APIMethod(:POST, "{instanceGroupManager}/listManagedInstances", "Lists all of the instances in the managed instance group. Each instance in the list has a currentAction, which indicates the action that the managed instance group is performing on the instance. For example, if the group is still creating an instance, the currentAction is CREATING. If a previous action failed, the list displays the errors for that failed action."),
        recreateInstances=APIMethod(:POST, "{instanceGroupManager}/recreateInstances", "Schedules a group action to recreate the specified instances in the managed instance group. The instances are deleted and recreated using the current instance template for the managed instance group. This operation is marked as DONE when the action is scheduled even if the instances have not yet been recreated. You must separately verify the status of the recreating action with the listmanagedinstances method."),
        resize=APIMethod(:POST, "{instanceGroupManager}/resize", "Resizes the managed instance group. If you increase the size, the group creates new instances using the current instance template. If you decrease the size, the group deletes instances. The resize operation is marked DONE when the resize actions are scheduled even if the group has not yet added or deleted any instances. You must separately verify the status of the creating or deleting actions with the listmanagedinstances method. Required query parameters: size"),
        setInstanceTemplate=APIMethod(:POST, "{instanceGroupManager}/setInstanceTemplate", "Specifies the instance template to use when creating new instances in this group. The templates for existing instances in the group do not change unless you recreate them."),
        setTargetPools=APIMethod(:POST, "{instanceGroupManager}/setTargetPools", "Modifies the target pools to which all instances in this managed instance group are assigned. The target pools automatically apply to all of the instances in the managed instance group. This operation is marked DONE when you make the request even if the instances have not yet been added to their target pools. The change might take some time to apply to all of the instances in the group depending on the size of the group."),
    ),
    InstanceGroup=APIResource("zones/{zone}/instanceGroups";
        addInstances=APIMethod(:POST, "{instanceGroup}/addInstances", "Adds a list of instances to the specified instance group. All of the instances in the instance group must be in the same network/subnetwork. Read Adding instances for more information."),
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/instanceGroups", "Retrieves the list of instance groups and sorts them by zone."),
        delete=APIMethod(:DELETE, "{instanceGroup}", "Deletes the specified instance group. The instances in the group are not deleted. Note that instance group must not belong to a backend service. Read Deleting an instance group for more information."),
        get=APIMethod(:GET, "{instanceGroup}", "Returns the specified instance group. Get a list of available instance groups by making a list() request."),
        insert=APIMethod(:POST, "", "Creates an instance group in the specified project using the parameters that are included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of instance groups that are located in the specified project and zone."; transform=(x, t) -> map(t, get(x, :items, []))),
        listInstances=APIMethod(:POST, "{instanceGroup}/listInstances", "Lists the instances in the specified instance group."),
        removeInstances=APIMethod(:POST, "{instanceGroup}/removeInstances", "Removes one or more instances from the specified instance group, but does not delete those instances."),
        setNamedPorts=APIMethod(:POST, "{instanceGroup}/setNamedPorts", "Sets the named ports for the specified instance group."),
    ),
    InstanceTemplate=APIResource("global/instanceTemplates";
        delete=APIMethod(:DELETE, "{instanceTemplate}", "Deletes the specified instance template. If you delete an instance template that is being referenced from another instance group, the instance group will not be able to create or recreate virtual machine instances. Deleting an instance template is permanent and cannot be undone."),
        get=APIMethod(:GET, "{instanceTemplate}", "Returns the specified instance template. Get a list of available instance templates by making a list() request."),
        insert=APIMethod(:POST, "", "Creates an instance template in the specified project using the data that is included in the request. If you are creating a new template to update an existing instance group, your new instance template must use the same network or, if applicable, the same subnetwork as the original template."),
        list=APIMethod(:GET, "", "Retrieves a list of instance templates that are contained within the specified project and zone."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Instance=APIResource("zones/{zone}/instances";
        addAccessConfig=APIMethod(:POST, "{instance}/addAccessConfig", "Adds an access config to an instance's network interface. Required query parameters: networkInterface"),
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/instances", "Retrieves aggregated list of instances."),
        attachDisk=APIMethod(:POST, "{instance}/attachDisk", "Attaches a Disk resource to an instance."),
        delete=APIMethod(:DELETE, "{instance}", "Deletes the specified Instance resource. For more information, see Stopping or Deleting an Instance."),
        deleteAccessConfig=APIMethod(:POST, "{instance}/deleteAccessConfig", "Deletes an access config from an instance's network interface. Required query parameters: accessConfig, networkInterface"),
        detachDisk=APIMethod(:POST, "{instance}/detachDisk", "Detaches a disk from an instance. Required query parameters: deviceName"),
        get=APIMethod(:GET, "{instance}", "Returns the specified Instance resource. Get a list of available instances by making a list() request."),
        getSerialPortOutput=APIMethod(:GET, "{instance}/serialPort", "Returns the specified instance's serial port output."),
        insert=APIMethod(:POST, "", "Creates an instance resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of instances contained within the specified zone."; transform=(x, t) -> map(t, get(x, :items, []))),
        reset=APIMethod(:POST, "{instance}/reset", "Performs a hard reset on the instance."),
        setDiskAutoDelete=APIMethod(:POST, "{instance}/setDiskAutoDelete", "Sets the auto-delete flag for a disk attached to an instance. Required query parameters: autoDelete, deviceName"),
        setMachineType=APIMethod(:POST, "{instance}/setMachineType", "Changes the machine type for a stopped instance to the machine type specified in the request."),
        setMetadata=APIMethod(:POST, "{instance}/setMetadata", "Sets metadata for the specified instance to the data included in the request."),
        setScheduling=APIMethod(:POST, "{instance}/setScheduling", "Sets an instance's scheduling options."),
        setServiceAccount=APIMethod(:POST, "{instance}/setServiceAccount", "Sets the service account on the instance."),
        setTags=APIMethod(:POST, "{instance}/setTags", "Sets tags for the specified instance to the data included in the request."),
        start=APIMethod(:POST, "{instance}/start", "Starts an instance that was stopped using the using the instances().stop method. For more information, see Restart an instance."),
        startWithEncryptionKey=APIMethod(:POST, "{instance}/startWithEncryptionKey", "Starts an instance that was stopped using the using the instances().stop method. For more information, see Restart an instance."),
        stop=APIMethod(:POST, "{instance}/stop", "Stops a running instance, shutting it down cleanly, and allows you to restart the instance at a later time. Stopped instances do not incur per-minute, virtual machine usage charges while they are stopped, but any resources that the virtual machine is using, such as persistent disks and static IP addresses, will continue to be charged until they are deleted. For more information, see Stopping an instance."),
    ),
    License=APIResource("global/licenses";
        get=APIMethod(:GET, "{license}", "Returns the specified License resource. Get a list of available licenses by making a list() request."),
    ),
    MachineType=APIResource("zones/{zone}/machineTypes";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/machineTypes", "Retrieves an aggregated list of machine types."),
        get=APIMethod(:GET, "{machineType}", "Returns the specified machine type. Get a list of available machine types by making a list() request."),
        list=APIMethod(:GET, "{machineTypes}", "Retrieves a list of machine types available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Network=APIResource("global/networks";
        delete=APIMethod(:DELETE, "{network}", "Deletes the specified network."),
        get=APIMethod(:GET, "{network}", "Returns the specified network. Get a list of available networks by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a network in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of networks available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Project=APIResource("";
        get=APIMethod(:GET, "", "Returns the specified Project resource."),
        moveDisk=APIMethod(:POST, "moveDisk", "Moves a persistent disk from one zone to another."),
        moveInstance=APIMethod(:POST, "moveInstance", "Moves an instance and its attached persistent disks from one zone to another."),
        setCommonInstanceMetadata=APIMethod(:POST, "setCommonInstanceMetadata", "Sets metadata common to all instances within the specified project using the data included in the request."),
        setUsageExportBucket=APIMethod(:POST, "setUsageExportBucket", "Enables the usage export feature and sets the usage export bucket where reports are stored. If you provide an empty request body using this method, the usage export feature will be disabled."),
    ),
    RegionOperation=APIResource("regions/{region}/operations";
        delete=APIMethod(:DELETE, "{operation}", "Deletes the specified region-specific Operations resource."),
        get=APIMethod(:GET, "{operation}", "Retrieves the specified region-specific Operations resource."),
        list=APIMethod(:GET, "", "Retrieves a list of Operation resources contained within the specified region."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Region=APIResource("regions";
        get=APIMethod(:GET, "{region}", "Returns the specified Region resource. Get a list of available regions by making a list() request."),
        list=APIMethod(:GET, "", "Retrieves the list of region resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Router=APIResource("regions/{region}/routers";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/routers", "Retrieves an aggregated list of routers."),
        delete=APIMethod(:DELETE, "{router}", "Deletes the specified Router resource."),
        get=APIMethod(:GET, "{router}", "Returns the specified Router resource. Get a list of available routers by making a list() request."),
        getRouterStatus=APIMethod(:GET, "{router}/getRouterStatus", "Retrieves runtime information of the specified router."),
        insert=APIMethod(:POST, "", "Creates a Router resource in the specified project and region using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of Router resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "{router}", "Updates the specified Router resource with the data included in the request. This method supports patch semantics."),
        preview=APIMethod(:POST, "{router}/preview", "Preview fields auto-generated during router create and update operations. Calling this method does NOT create or update the router."),
        update=APIMethod(:PUT, "{router}", "Updates the specified Router resource with the data included in the request."),
    ),
    Route=APIResource("global/routes";
        delete=APIMethod(:DELETE, "{route}", "Deletes the specified Route resource."),
        get=APIMethod(:GET, "{route}", "Returns the specified Route resource. Get a list of available routes by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a Route resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of Route resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Snapshot=APIResource("global/snapshots";
        delete=APIMethod(:DELETE, "{snapshot}", "Deletes the specified Snapshot resource. Keep in mind that deleting a single snapshot might not necessarily delete all the data on that snapshot. If any data on the snapshot that is marked for deletion is needed for subsequent snapshots, the data will be moved to the next corresponding snapshot."),
        get=APIMethod(:GET, "{snapshot}", "Returns the specified Snapshot resource. Get a list of available snapshots by making a list() request."),
        list=APIMethod(:GET, "", "Retrieves the list of Snapshot resources contained within the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    SslCertificate=APIResource("global/sslCertificates";
        delete=APIMethod(:DELETE, "{sslCertificate}", "Deletes the specified SslCertificate resource."),
        get=APIMethod(:GET, "{sslCertificate}", "Returns the specified SslCertificate resource. Get a list of available SSL certificates by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a SslCertificate resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of SslCertificate resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Subnetwork=APIResource("regions/{region}/subnetworks";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/subnetworks", "Retrieves an aggregated list of subnetworks."),
        delete=APIMethod(:DELETE, "{subnetwork}", "Deletes the specified subnetwork."),
        get=APIMethod(:GET, "{subnetwork}", "Returns the specified subnetwork. Get a list of available subnetworks list() request."),
        insert=APIMethod(:POST, "", "Creates a subnetwork in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of subnetworks available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    TargetHttpProxy=APIResource("global/targetHttpProxies";
        delete=APIMethod(:DELETE, "{targetHttpProxy}", "Deletes the specified TargetHttpProxy resource."),
        get=APIMethod(:GET, "{targetHttpProxy}", "Returns the specified TargetHttpProxy resource. Get a list of available target HTTP proxies by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a TargetHttpProxy resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of TargetHttpProxy resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        setUrlMap=APIMethod(:POST, "$API_ROOT/compute/v1/projects/{project}/targetHttpProxies/{targetHttpProxy}/setUrlMap", "Changes the URL map for TargetHttpProxy."),
    ),
    TargetHttpsProxy=APIResource("global/targetHttpsProxies";
        delete=APIMethod(:DELETE, "{targetHttpsProxy}", "Deletes the specified TargetHttpsProxy resource."),
        get=APIMethod(:GET, "{targetHttpsProxy}", "Returns the specified TargetHttpsProxy resource. Get a list of available target HTTPS proxies by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a TargetHttpsProxy resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of TargetHttpsProxy resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        setSslCertificates=APIMethod(:POST, "$API_ROOT/compute/v1/projects/{project}/targetHttpsProxies/{targetHttpsProxy}/setSslCertificates", "Replaces SslCertificates for TargetHttpsProxy."),
        setUrlMap=APIMethod(:POST, "$API_ROOT/compute/v1/projects/{project}/targetHttpProxies/{targetHttpsProxy}/setUrlMap", "Changes the URL map for TargetHttpsProxy."),
    ),
    TargetInstance=APIResource("zones/{zone}/targetInstances";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/targetInstances", "Retrieves an aggregated list of target instances."),
        delete=APIMethod(:DELETE, "{targetInstance}", "Deletes the specified TargetInstance resource."),
        get=APIMethod(:GET, "{targetInstance}", "Returns the specified TargetInstance resource. Get a list of available target instances by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a TargetInstance resource in the specified project and zone using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of TargetInstance resources available to the specified project and zone."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    TargetPool=APIResource("regions/{region}/targetPools";
        addHealthCheck=APIMethod(:POST, "{targetPool}/addHealthCheck", "Adds health check URLs to a target pool."),
        addInstance=APIMethod(:POST, "{targetPool}/addInstance", "Adds an instance to a target pool."),
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/targetPools", "Retrieves an aggregated list of target pools."),
        delete=APIMethod(:DELETE, "{targetPool}", "Deletes the specified target pool."),
        get=APIMethod(:GET, "{targetPool}", "Returns the specified target pool. Get a list of available target pools by making a list() request."),
        getHealth=APIMethod(:POST, "{targetPool}/getHealth", "Gets the most recent health check results for each IP for the instance that is referenced by the given target pool."),
        insert=APIMethod(:POST, "", "Creates a target pool in the specified project and region using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of target pools available to the specified project and region."; transform=(x, t) -> map(t, get(x, :items, []))),
        removeHealthCheck=APIMethod(:POST, "{targetPool}/removeHealthCheck", "Removes health check URL from a target pool."),
        removeInstance=APIMethod(:POST, "{targetPool}/removeInstance", "Removes instance URL from a target pool."),
        setBackup=APIMethod(:POST, "{targetPool}/setBackup", "Changes a backup target pool's configurations."),
    ),
    TargetSslProxy=APIResource("global/targetSslProxies";
        delete=APIMethod(:DELETE, "{targetSslProxy}", "Deletes the specified TargetSslProxy resource."),
        get=APIMethod(:GET, "{targetSslProxy}", "Returns the specified TargetSslProxy resource. Get a list of available target SSL proxies by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a TargetSslProxy resource in the specified project using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves the list of TargetSslProxy resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        setBackendService=APIMethod(:POST, "{targetSslProxy}/setBackendService", "Changes the BackendService for TargetSslProxy."),
        setProxyHeader=APIMethod(:POST, "{targetSslProxy}/setProxyHeader", "Changes the ProxyHeaderType for TargetSslProxy."),
        setSslCertificates=APIMethod(:POST, "{targetSslProxy}/setSslCertificates", "Changes SslCertificates for TargetSslProxy."),
    ),
    TargetVpnGateway=APIResource("regions/{region}/targetVpnGateways";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/targetVpnGateways", "Retrieves an aggregated list of target VPN gateways."),
        delete=APIMethod(:DELETE, "{targetVpnGateway}", "Deletes the specified target VPN gateway."),
        get=APIMethod(:GET, "{targetVpnGateway}", "Returns the specified target VPN gateway. Get a list of available target VPN gateways by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a target VPN gateway in the specified project and region using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of target VPN gateways available to the specified project and region."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    UrlMap=APIResource("global/urlMaps";
        delete=APIMethod(:DELETE, "{urlMap}", "Deletes the specified UrlMap resource."),
        get=APIMethod(:GET, "{urlMap}", "Returns the specified UrlMap resource. Get a list of available URL maps by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a UrlMap resource in the specified project using the data included in the request."),
        invalidateCache=APIMethod(:POST, "{urlMap}/invalidateCache", "Initiates a cache invalidation operation, invalidating the specified path, scoped to the specified UrlMap."),
        list=APIMethod(:GET, "", "Retrieves the list of UrlMap resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
        patch=APIMethod(:PATCH, "{urlMap}", "Updates the specified UrlMap resource with the data included in the request. This method supports patch semantics."),
        update=APIMethod(:PUT, "{urlMap}", "Updates the specified UrlMap resource with the data included in the request."),
        validate=APIMethod(:POST, "{urlMap}/validate", "Runs static validation for the UrlMap. In particular, the tests of the provided UrlMap will be run. Calling this method does NOT create the UrlMap."),
    ),
    VpnTunnel=APIResource("regions/{region}/vpnTunnels";
        aggregatedList=APIMethod(:GET, "$API_ROOT/compute/v1/projects/{project}/aggregated/vpnTunnels", "Retrieves an aggregated list of VPN tunnels."),
        delete=APIMethod(:DELETE, "{vpnTunnel}", "Deletes the specified VpnTunnel resource."),
        get=APIMethod(:GET, "{vpnTunnel}", "Returns the specified VpnTunnel resource. Get a list of available VPN tunnels by making a list() request."),
        insert=APIMethod(:POST, "", "Creates a VpnTunnel resource in the specified project and region using the data included in the request."),
        list=APIMethod(:GET, "", "Retrieves a list of VpnTunnel resources contained in the specified project and region."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    ZoneOperation=APIResource("zones/{zone}/operations";
        delete=APIMethod(:DELETE, "{operation}", "Deletes the specified zone-specific Operations resource."),
        get=APIMethod(:GET, "{operation}", "Retrieves the specified zone-specific Operations resource."),
        list=APIMethod(:GET, "", "Retrieves a list of Operation resources contained within the specified zone."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
    Zone=APIResource("zones";
        get=APIMethod(:GET, "{zone}", "Returns the specified Zone resource. Get a list of available zones by making a list() request."),
        list=APIMethod(:GET, "", "Retrieves the list of Zone resources available to the specified project."; transform=(x, t) -> map(t, get(x, :items, []))),
    ),
)

end


text:
module AdvancedMH

# Import the relevant libraries.
using AbstractMCMC
using Distributions
using Requires
using LinearAlgebra
using PDMats

import Random

# Exports
export
    MetropolisHastings,
    DensityModel,
    RWMH,
    StaticMH,
    StaticProposal,
    SymmetricStaticProposal,
    RandomWalkProposal,
    SymmetricRandomWalkProposal,
    Ensemble,
    StretchProposal,
    MALA,
    AMProposal

# Reexports
export sample, MCMCThreads, MCMCDistributed

# Abstract type for MH-style samplers. Needs better name?
abstract type MHSampler <: AbstractMCMC.AbstractSampler end

# Abstract type for MH-style transitions.
abstract type AbstractTransition end

# Define a model type. Stores the log density function and the data to
# evaluate the log density on.
"""
    DensityModel{F<:Function} <: AbstractModel

`DensityModel` wraps around a self-contained log-liklihood function `logdensity`.

Example:

```julia
l
DensityModel
```
"""
struct DensityModel{F<:Function} <: AbstractMCMC.AbstractModel
    logdensity :: F
end

# Create a very basic Transition type, only stores the
# parameter draws and the log probability of the draw.
struct Transition{T<:Union{Vector, Real, NamedTuple}, L<:Real} <: AbstractTransition
    params :: T
    lp :: L
end

# Store the new draw and its log density.
Transition(model::DensityModel, params) = Transition(params, logdensity(model, params))

# Calculate the density of the model given some parameterization.
logdensity(model::DensityModel, params) = model.logdensity(params)
logdensity(model::DensityModel, t::Transition) = t.lp

# A basic chains constructor that works with the Transition struct we defined.
function AbstractMCMC.bundle_samples(
    ts::Vector{<:AbstractTransition},
    model::DensityModel,
    sampler::MHSampler,
    state,
    chain_type::Type{Vector{NamedTuple}};
    param_names=missing,
    kwargs...
)
    # Check if we received any parameter names.
    if ismissing(param_names)
        param_names = ["param_$i" for i in 1:length(keys(ts[1].params))]
    else
        # Deepcopy to be thread safe.
        param_names = deepcopy(param_names)
    end

    push!(param_names, "lp")

    # Turn all the transitions into a vector-of-NamedTuple.
    ks = tuple(Symbol.(param_names)...)
    nts = [NamedTuple{ks}(tuple(t.params..., t.lp)) for t in ts]

    return nts
end

function AbstractMCMC.bundle_samples(
    ts::Vector{<:Transition{<:NamedTuple}},
    model::DensityModel,
    sampler::MHSampler,
    state,
    chain_type::Type{Vector{NamedTuple}};
    param_names=missing,
    kwargs...
)
    # If the element type of ts is NamedTuples, just use the names in the
    # struct.

    # Extract NamedTuples
    nts = map(x -> merge(x.params, (lp=x.lp,)), ts)

    # Return em'
    return nts
end

function __init__()
    @require MCMCChains="c7f686f2-ff18-58e9-bc7b-31028e88f75d" include("mcmcchains-connect.jl")
    @require StructArrays="09ab397b-f2b6-538f-b94a-2f83cf4a842a" include("structarray-connect.jl")
    @require DiffResults = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" begin
        @require ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" include("MALA.jl")
    end
end

# Include inference methods.
include("proposal.jl")
include("mh-core.jl")
include("emcee.jl")
include("adaptivemetropolis.jl")

end # module AdvancedMH


text:
"""
spin15.jl - T1 optimized pulses
"""

WDIR = get(ENV, "ROBUST_QOC_PATH", "../../")
include(joinpath(WDIR, "src", "spin", "spin.jl"))

using Altro
using HDF5
using LinearAlgebra
using RobotDynamics
using StaticArrays
using TrajectoryOptimization
const RD = RobotDynamics
const TO = TrajectoryOptimization

# paths
const EXPERIMENT_META = "spin"
const EXPERIMENT_NAME = "spin15"
const SAVE_PATH = abspath(joinpath(WDIR, "out", EXPERIMENT_META, EXPERIMENT_NAME))

# problem
const CONTROL_COUNT = 1
const STATE_COUNT = 2
const ASTATE_SIZE_BASE = STATE_COUNT * HDIM_ISO + 3 * CONTROL_COUNT
# state indices
const STATE1_IDX = 1:HDIM_ISO
const STATE2_IDX = STATE1_IDX[end] + 1:STATE1_IDX[end] + HDIM_ISO
const INTCONTROLS_IDX = STATE2_IDX[end] + 1:STATE2_IDX[end] + CONTROL_COUNT
const CONTROLS_IDX = INTCONTROLS_IDX[end] + 1:INTCONTROLS_IDX[end] + CONTROL_COUNT
const DCONTROLS_IDX = CONTROLS_IDX[end] + 1:CONTROLS_IDX[end] + CONTROL_COUNT
const INTGAMMA_IDX = DCONTROLS_IDX[end] + 1:DCONTROLS_IDX[end] + 1
# control indices
const D2CONTROLS_IDX = 1:CONTROL_COUNT
const DT_IDX = D2CONTROLS_IDX[end] + 1:D2CONTROLS_IDX[end] + 1

# model
# DA = decay aware, TO = time optimal
struct Model{DA, TO} <: AbstractModel
    Model(DA::Bool=true, TO::Bool=true) = new{DA, TO}()
end
@inline RD.state_dim(::Model{false, TO}) where TO = ASTATE_SIZE_BASE
@inline RD.state_dim(::Model{true, TO}) where TO = ASTATE_SIZE_BASE + 1
@inline RD.control_dim(::Model{DA, false}) where DA = CONTROL_COUNT
@inline RD.control_dim(::Model{DA, true}) where DA = CONTROL_COUNT + 1

# dynamics
function RD.discrete_dynamics(::Type{RK3}, model::Model{DA, TO}, astate::StaticVector,
                              acontrols::StaticVector, time::Real, dt::Real) where {DA, TO}
    if TO
        dt = acontrols[DT_IDX[1]]^2
    end

    h_prop = exp(dt * (FQ_NEGI_H0_ISO + astate[CONTROLS_IDX[1]] * NEGI_H1_ISO))
    state1 = h_prop * astate[STATE1_IDX]
    state2 = h_prop * astate[STATE2_IDX]
    intcontrols = astate[INTCONTROLS_IDX[1]] + dt * astate[CONTROLS_IDX[1]]
    controls = astate[CONTROLS_IDX[1]] + dt * astate[DCONTROLS_IDX[1]]
    dcontrols = astate[DCONTROLS_IDX[1]] + dt * acontrols[D2CONTROLS_IDX[1]]
    astate_ = [
        state1; state2; intcontrols; controls; dcontrols;
    ]

    if DA
        intgamma = (astate[INTGAMMA_IDX[1]] +
                    dt * amp_t1_reduced_spline(astate[CONTROLS_IDX[1]])^(-1))
        push!(astate_, intgamma)
    end

    return astate_
end

# main
function run_traj(;evolution_time=21.61, gate_type=zpiby2, time_optimal=false,
                  decay_aware=false, solver_type=altro, sqrtbp=false,
                  integrator_type=rk3, qs=[1e0, 1e0, 1e0, 1e-1, 1e2, 1e-1, 5e2],
                  smoke_test=false,
                  al_tol=1e-4, constraint_tol=1e-8, max_penalty=1e11,
                  pn_steps=2, save=true, verbose=true, dt_inv=Int64(1e1),
                  max_cost_value=1e8, max_iterations=Int64(2e5),
                  benchmark=false,)
    # model configuration
    dt = dt_inv^(-1)
    dt_max = (dt_inv / 2)^(-1)
    dt_min = (dt_inv * 2)^(-1)
    model = Model(decay_aware, time_optimal)
    n = state_dim(model)
    m = control_dim(model)
    t0 = 0.

    # initial state
    x0 = zeros(n)
    x0[STATE1_IDX] = IS1_ISO_
    x0[STATE2_IDX] = IS2_ISO_
    x0 = SVector{n}(x0)

    # final state
    if gate_type == xpiby2
        target_state1 = Array(XPIBY2_ISO_1)
        target_state2 = Array(XPIBY2_ISO_2)
    elseif gate_type == ypiby2
        target_state1 = Array(YPIBY2_ISO_1)
        target_state2 = Array(YPIBY2_ISO_2)
    elseif gate_type == zpiby2
        target_state1 = Array(ZPIBY2_ISO_1)
        target_state2 = Array(ZPIBY2_ISO_2)
    end
    xf = zeros(n)
    xf[STATE1_IDX] = target_state1
    xf[STATE2_IDX] = target_state2
    xf = SVector{n}(xf)

    # control amplitude constraint
    x_max = fill(Inf, n)
    x_max[CONTROLS_IDX] .= MAX_CONTROL_NORM_0
    x_max = SVector{n}(x_max)
    x_min = fill(-Inf, n)
    x_min[CONTROLS_IDX] .= -MAX_CONTROL_NORM_0
    x_min = SVector{n}(x_min)

    # control amplitude constraint at boundary
    x_max_boundary = fill(Inf, n)
    x_max_boundary[CONTROLS_IDX] .= 0
    x_max_boundary = SVector{n}(x_max_boundary)
    x_min_boundary = fill(-Inf, n)
    x_min_boundary[CONTROLS_IDX] .= 0
    x_min_boundary = SVector{n}(x_min_boundary)

    # dt bound
    u_max = SVector{m}([
        fill(Inf, CONTROL_COUNT);
        fill(sqrt(dt_max), eval(:($time_optimal ? 1 : 0))); #dt
    ])
    u_min = SVector{m}([
        fill(-Inf, CONTROL_COUNT);
        fill(sqrt(dt_min), eval(:($time_optimal ? 1 : 0))); #dt
    ])

    # initial trajectory
    N = Int(floor(evolution_time * dt_inv)) + 1
    U0 = [SVector{m}([
        fill(1e-4, CONTROL_COUNT);
        fill(sqrt(dt), eval(:($time_optimal ? 1 : 0)));
    ]) for k = 1:N - 1]
    X0 = [SVector{n}(
        fill(NaN, n)
    ) for k = 1:N]
    dt_ = time_optimal ? 1 : dt
    Z = Traj(X0, U0, dt_ * ones(N))

    # cost function
    Q = Diagonal(SVector{n}([
        fill(qs[1], STATE_COUNT * HDIM_ISO); # ψ1, ψ2
        fill(qs[2], CONTROL_COUNT); # ∫a
        fill(qs[3], CONTROL_COUNT); # a
        fill(qs[4], CONTROL_COUNT); # ∂a
        fill(qs[5], eval(:($decay_aware ? 1 : 0))); # ∫γ1
    ]))
    Qf = Q * N
    R = Diagonal(SVector{m}([
        fill(qs[6], CONTROL_COUNT); # ∂2a
        fill(qs[7], eval(:($time_optimal ? 1 : 0))); # Δt
    ]))
    objective = LQRObjective(Q, R, Qf, xf, N)

    # constraints
    # must satisfy control amplitude bound
    control_bnd = BoundConstraint(n, m, x_max=x_max, x_min=x_min)
    # must statisfy conrols start and end at 0
    control_bnd_boundary = BoundConstraint(n, m, x_max=x_max_boundary, x_min=x_min_boundary)
    # must satisfy dt bound
    dt_bnd = BoundConstraint(n, m, u_max=u_max, u_min=u_min)
    # must reach target state, must have zero net flux
    target_astate_constraint = GoalConstraint(xf, [STATE1_IDX; STATE2_IDX; INTCONTROLS_IDX])
    # must obey unit norm
    norm_constraints = [NormConstraint(n, m, 1, TO.Equality(), idx)
                        for idx in [STATE1_IDX, STATE2_IDX]]

    constraints = ConstraintList(n, m, N)
    add_constraint!(constraints, control_bnd, 2:N-2)
    add_constraint!(constraints, control_bnd_boundary, N-1:N-1)
    add_constraint!(constraints, target_astate_constraint, N:N)
    for norm_constraint in norm_constraints
        add_constraint!(constraints, norm_constraint, 2:N-1)
    end
    if time_optimal
        add_constraint!(constraints, dt_bnd, 1:N-1)
    end

    # solve problem
    prob = Problem{IT_RDI[integrator_type]}(model, objective, constraints,
                                            x0, xf, Z, N, t0, evolution_time)
    solver = ALTROSolver(prob)
    verbose_pn = verbose ? true : false
    verbose_ = verbose ? 2 : 0
    projected_newton = solver_type == altro ? true : false
    constraint_tolerance = solver_type == altro ? constraint_tol : al_tol
    iterations_inner = smoke_test ? 1 : 300
    iterations_outer = smoke_test ? 1 : 30
    n_steps = smoke_test ? 1 : pn_steps
    set_options!(solver, square_root=sqrtbp, constraint_tolerance=constraint_tolerance,
                 projected_newton_tolerance=al_tol, n_steps=n_steps,
                 penalty_max=max_penalty, verbose_pn=verbose_pn, verbose=verbose_,
                 projected_newton=projected_newton, iterations_inner=iterations_inner,
                 iterations_outer=iterations_outer, iterations=max_iterations,
                 max_cost_value=max_cost_value)
    if benchmark
        benchmark_result = Altro.benchmark_solve!(solver)
    else
        benchmark_result = nothing
        Altro.solve!(solver)
    end

    # Post-process.
    acontrols_raw = TO.controls(solver)
    acontrols_arr = permutedims(reduce(hcat, map(Array, acontrols_raw)), [2, 1])
    astates_raw = TO.states(solver)
    astates_arr = permutedims(reduce(hcat, map(Array, astates_raw)), [2, 1])
    Q_raw = Array(Q)
    Q_arr = [Q_raw[i, i] for i in 1:size(Q_raw)[1]]
    Qf_raw = Array(Qf)
    Qf_arr = [Qf_raw[i, i] for i in 1:size(Qf_raw)[1]]
    R_raw = Array(R)
    R_arr = [R_raw[i, i] for i in 1:size(R_raw)[1]]
    cidx_arr = Array(CONTROLS_IDX)
    d2cidx_arr = Array(D2CONTROLS_IDX)
    dtidx_arr = Array(DT_IDX)
    # Square the dts.
    if time_optimal
        acontrols_arr[:, DT_IDX] = acontrols_arr[:, DT_IDX] .^2
        # acontrols_arr[:, DT_IDX] = map(abs, acontrols_arr[:, DT_IDX])
    end
    cmax = TO.max_violation(solver)
    cmax_info = TO.findmax_violation(TO.get_constraints(solver))
    iterations_ = iterations(solver)

    result = Dict(
            "acontrols" => acontrols_arr,
            "controls_idx" => cidx_arr,
            "d2controls_dt2_idx" => d2cidx_arr,
            "dt_idx" => dtidx_arr,
            "evolution_time" => evolution_time,
            "astates" => astates_arr,
            "Q" => Q_arr,
            "Qf" => Qf_arr,
            "R" => R_arr,
            "cmax" => cmax,
            "cmax_info" => cmax_info,
            "dt" => dt,
            "solver_type" => Integer(solver_type),
            "sqrtbp" => Integer(sqrtbp),
            "max_penalty" => max_penalty,
            "constraint_tol" => constraint_tol,
            "al_tol" => al_tol,
            "integrator_type" => Integer(integrator_type),
            "gate_type" => Integer(gate_type),
            "save_type" => Integer(jl),
            "iterations" => iterations_,
            "pn_steps" => pn_steps,
            "max_iterations" => max_iterations,
            "max_cost_value" => max_cost_value,
    )

    if time_optimal
        # sample the important metrics
        (controls_sample, d2controls_dt2_sample, evolution_time_sample
         ) = sample_controls(save_file_path)
        result["controls_sample"] = controls_sample
        result["d2controls_dt2_sample"] = d2controls_dt2_sample
        result["evolution_time_sample"] = evolution_time_sample
        result["save_type"] = Integer(samplejl)
    end

    # save
    if save
        save_file_path = generate_file_path("h5", EXPERIMENT_NAME, SAVE_PATH)
        println("Saving this optimization to $(save_file_path)")
        h5open(save_file_path, "cw") do save_file
            for key in keys(result)
                write(save_file, key, result[key])
            end
        end
        result["save_file_path"] = save_file_path
    end

    result = benchmark ? benchmark_result : result

    return result
end

max_stars_repo_name: SchusterLab/rbqoc

max_issues_repo_name: SchusterLab/rbqoc

max_forks_repo_name: SchusterLab/rbqoc


text:
convert(::Type{Any}, s::T)            where {T<:Union{TAMQPShortStr,TAMQPLongStr,TAMQPByteArray}} = convert(String, s)
convert(::Type{String}, s::T)         where {T<:Union{TAMQPShortStr,TAMQPLongStr,TAMQPByteArray}} = String(convert(Vector{UInt8}, s.data))
convert(::Type{T}, s::AbstractString) where {T<:Union{TAMQPShortStr,TAMQPLongStr,TAMQPByteArray}} = T(length(s), Vector{UInt8}(codeunits(String(s))))
convert(::Type{TAMQPLongStr}, d::Vector{UInt8}) = TAMQPLongStr(length(d), d)
convert(::Type{TAMQPByteArray}, d::Vector{UInt8}) = TAMQPByteArray(length(d), d)

convert(::Type{TAMQPFieldValue{T}}, v::T) where {T} = TAMQPFieldValue{T}(FieldIndicatorMap[T], v)

as_fval(v::T) where {T} = convert(TAMQPFieldValue{T}, v)
as_fval(v::Dict{String,Any}) = convert(TAMQPFieldValue{TAMQPFieldTable}, convert(TAMQPFieldTable, v))
as_fval(v::String) = convert(TAMQPFieldValue{TAMQPLongStr}, convert(TAMQPLongStr, v))

convert(::Type{Any}, t::TAMQPFieldTable) = convert(Dict{Any,Any}, t)
convert(::Type{Dict{K,V}}, t::TAMQPFieldTable) where {K, V} = Dict{K,V}(f.name => f.val for f in t.data)
convert(::Type{Dict{String, String}}, t::TAMQPFieldTable) = Dict{String, String}(String(f.name.data) => String(f.val.fld.data) for f in t.data)
function convert(::Type{TAMQPFieldTable}, d::Dict{String,Any})
    data = TAMQPFieldValuePair[]
    for (n,v) in d
        push!(data, TAMQPFieldValuePair(convert(TAMQPShortStr,n), as_fval(v)))
    end
    TAMQPFieldTable(length(data), data)
end

convert(::Type{Any}, t::TAMQPFieldArray) = convert(Vector, t)
convert(::Type{Vector{T}}, t::TAMQPFieldArray) where {T} = convert(Vector{T}, t.data)

convert(::Type{Bool}, b::TAMQPBit) = Bool(b.val & 0x1)
convert(::Type{TAMQPBit}, b::Bool) = TAMQPBit(convert(UInt8, b))
convert(::Type{TAMQPBit}, b::T) where {T<:Integer} = convert(TAMQPBit, Bool(b))


text:
export legendrePoly #Just a basic export to make legendrePoly function visible (ie. Public)

## Function that computes the legendre polynomial of nᵗʰ order
#  INPUTS:
# Type T - ?????
# order - Integer defining the desired Legendre polynomial order
#  OUTPUTS:
# Pₙ(x) - Legendre polynomial of order n="order"

function legendrePoly(::Type{T},order::Int) where {T} #compute Legendre polynomial
##Throw an error if we ask for a polynomial of negative order
    order<0 && error("order must be >= 0") #If order is less than zero, we throw an error
##Manually input legendre polynomials of orders 0 and 1
    P = [Polynomial{T}(1), Polynomial{T}([0,1])] #initialize our horizon for pseudo-recursion with known Legendre Polynomials: P=[1,x]
    x = Polynomial{T}([0,1]) #x is just an instance of type Polynomial
##For orders 0 or 1 we return 1 or x respectively
    order<2 && return P[order+1] #if order is 1 return x, if order is 0 return 1
## For order>=2: we are using Bonnet recursion formula: nPₙ(x)=(2n-1)Pₙ₋₁(x)-(n-1)Pₙ₋₂(x)
# Compute Pᵢ₊₁ from Pᵢ and Pᵢ₋₁ in a pseudo-recursive way
    for i = 1:order-1 #i is effectively n-1 (ie. current order-1) because we index from 1 to "order-1" instead of the natural 2 to "order"
        Ptmp = ( (2*i + 1 ) * x * P[2] - i * P[1]) / (i+1) #this computes Pᵢ₊₁ in a pseudo recursive manner
        P[1] = P[2] #here we save Pᵢ for next iteration
        P[2] = Ptmp #here we save Pᵢ₊₁ for next iteration
    end
## Return P of order "order"
    return P[2]
end

legendrePoly(order::T) where {T} = legendrePoly(T,convert(Int,order)) #I suspect here he tried to do some proper recursion, but idk


text:
using JLD2, Interpolations, StaticArrays, Images, FileIO, ImageFeatures, CoordinateTransformations, Rotations, ImageDraw, ColorVectorSpace, ImageCore, Clustering, ImageMorphology, LinearAlgebra, ImageFiltering, Statistics
using ImageView

function getlinelength(img, line)
    indsy, indsx = axes(img)
    cosθ = cos(line.θ)
    sinθ = sin(line.θ)
    intersections_x = [(x, (line.ρ - x*cosθ)/sinθ) for x in (first(indsx), last(indsx))]
    intersections_y = [((line.ρ - y*sinθ)/cosθ, y) for y in (first(indsy), last(indsy))]
    valid_intersections = ImageDraw.get_valid_intersections(vcat(intersections_x, intersections_y), indsx, indsy)
    data = bresenhamread(img, round(Int,valid_intersections[1][2]), round(Int,valid_intersections[1][1]), round(Int,valid_intersections[2][2]), round(Int,valid_intersections[2][1]))
    i1 = findlast(first, data)
    i2 = findfirst(first, data)
    last(data[i1]), last(data[i2])
end

function bresenhamread(img::AbstractArray{T, 2}, y0::Int, x0::Int, y1::Int, x1::Int) where T
    data = Vector{Pair{T, Tuple{Int, Int}}}(undef, 0)
    dx = abs(x1 - x0)
    dy = abs(y1 - y0)

    sx = x0 < x1 ? 1 : -1
    sy = y0 < y1 ? 1 : -1;

    err = (dx > dy ? dx : -dy) / 2

    while true
        push!(data, img[y0, x0] => (y0, x0))
        (x0 != x1 || y0 != y1) || break
        e2 = err
        if e2 > -dx
            err -= dy
            x0 += sx
        end
        if e2 < dy
            err += dx
            y0 += sy
        end
    end

    data
end

mount_point = "/home/yakir/mnt"

mounted = any(occursin(Regex(mount_point), l) for l in eachline("/proc/mounts"))

if !mounted
    run(`sshfs skyroom:/ $mount_point`)
end
folder = filter(startswith("jl_"), readdir(joinpath(mount_point, "tmp")))
imgfiles = readdir(joinpath(mount_point, "tmp", only(folder)), join = true)

mask = joinpath(mount_point, "home/yakir/tmp.png")
if !isfile(mask)
    run(`ssh skyroom /home/yakir/SkyRoom.jl/calibration/arena/mask.sh`)
end
bw = Gray.(FileIO.load(mask))
th = otsu_threshold(bw)
mask = findall(erode(opening(bw .< th)))

imgfile = imgfiles[3]

function main(imgfile)

    bw = Gray.(FileIO.load(imgfile))
    bw[mask] .= NaN

    bkg = imfilter(bw, Kernel.gaussian(20))
    bw = bw .- bkg
    bw[mask] .= Gray(0.0)

    for i in mask
        bw[i] = Gray(0.0)
    end

    th = otsu_threshold(bw)
    tf = opening(bw .< th)
    lb = label_components(tf)

    w, h = size(tf)
    for i in (1, w), j in (1, h)
        l = lb[j, i]
        lb[lb .== l] .= 0
    end

    tf = lb .!= 0

    t = thinning(tf)

    rθ = hough_transform_standard(t, stepsize = 1, vote_threshold = 10, max_linecount = 100, angles = range(0, pi, length = 10000))

    R = kmeans(Matrix(hcat(vcat.(rθ...)...)'),1)
    M = R.centers
    m = [(; ρ, θ) for (ρ, θ) in eachcol(M)]

    ps = map(m) do line
        getlinelength(tf, line)
    end
    ps

end