Introduction
In MetCoOp there is a move towards spreading the load on the HPC(s) by running a small number of ensemble members for MEPS each hour. This contrasts with the normal method of running all members every 6 hours. The enesmeble system is called CMEPS (what does the C stand for?). To produce an ensemble forecast, the members are lagged in time so that a full ensemble is available every 3 hours, which is lagged further to produce a larger ensemble every 6 hours.
For verification, this means that the data have to be lagged before the scores can be computed. In the first instance, the model outputs are interpolated to station locations and written out to FCTABLE files - at this stage, no lead times or forecast dates are updated to reflect the lagging. However, in order to read to the correct members at the correct times, the lagging information is required.
For CMEPS, members 0, 1 and 2 are run every three hours at 0, 3, 6, …, 21 UTC. For the purpose of clarity we will refer to these members as the parent members and the lagged members as the child members. The child members 3, 4 and 7 are run 1 hour later than the parents, and the child members 5, 6 and 8 are run 2 hours later than the parents, i.e. at 2, 5, 8, …, 23 UTC. However, for a lagged forecast, we are interested in the child members that belong to the previous forecast, since the child members from the current parent will not be available yet.
The conversion to sqlite can easily be done using the read_eps_interpolate function from harpIO and specifying the lagging of each member in the lags argument.
library(harpIO)
read_eps_interpolate(
2019021312,
2019022421,
"CMEPS_prod",
"Pcp",
by = "3h",
lead_time = seq(0, 63, 3),
members_in = seq(0, 8),
lags = paste0(c(0, 0, 0, 2, 2, 1, 1, 2, 1), "h"),
file_path = "/lustre/storeB/users/andrewts/harp_test_data/vfld",
sqlite_path = "/lustre/storeB/users/andrewts/harp_test_data/harp_output/FCTABLE"
)
Using the standard sqlite file name template, an sqlite file is created for each lead time, containing only those members that were available for that lead time. Note that it’s quite a slow process and maybe provides motivation for re-implementing the old method for reading from vfld files - just to compare the speed, and also because support for vfld version 2 is needed.
For verification, the data are read in from the sqlite files using read_point_forecast from harpIO. The parent forecast times are basically as derived from the start_date, end_date and by arguments. All of the appropriate lagged members are read in for the relative times and lead times derived from the lags argument. Here we do not need to specifify what the lags are for each member as all data that can be found will be read in. As the lagging is done to 6 hours, need to make sure we adjust the start date to account for 6 hour lagging (take it back 3 hours, as the 0, 1, 2 lags are already there)
cmeps_pcp_3h <- read_point_forecast(
2019021321,
2019022421,
c("CMEPS_prod", "MEPS_prod"),
"EPS",
"AccPcp3h",
by = "3h",
lead_time = seq(0, 63, 3),
lags = list(CMEPS_prod = c("0h", "1h", "2h"), MEPS_prod = "0h"),
file_path = "/lustre/storeB/users/andrewts/harp_test_data/harp_output/FCTABLE"
)
The above could equally be done with by = "6h" and lags = list(CMEPS_prod = paste0(seq(0, 5), "h"), MEPS_prod = "0h") without changing anything, or indeed by = "1h" and lags not set.
Before we can verify, we must actually do the lagging. This is done using the lag_forecast function from harpPoint. In this function, the forecast model in the harp_fcst object you want to do the lagging for, and the parent forecast cycles for the lagged forecast need to be specified. All child members for the parents for each cycle will automatically be found - the child cycles are essentially all cycles between the parent cycles. The function is not able to include child cycles that are older than the next oldest parent cycle, but there is a way around this that will be shown later.
For CMEPS, the intention is to have an ensemble every 6 hours including all members from the current cycle and the cycles for the previous 5 hours. We can do this by lagging the forecast with parent cycles at 0, 6, 12 and 18 UTC.
library(harpPoint)
cmeps_pcp_3h_lag6 <- lag_forecast(
cmeps_pcp_3h,
"CMEPS_prod",
seq(0, 18, 6)
)
For a fair comparison of CMEPS and MEPS, the common cases between the two systems should be selected.
cmeps_pcp_3h_lag6 <- common_cases(cmeps_pcp_3h_lag6)
Now we can read in the observations and do the verification.
obs_pcp_3h <- read_point_obs(
first_validdate(cmeps_pcp_3h_lag6),
last_validdate(cmeps_pcp_3h_lag6),
"AccPcp3h",
obs_path = "/lustre/storeB/users/andrewts/harp_test_data/harp_output/OBSTABLE",
obsfile_template = "OBS_{YYYY}.sqlite"
)
cmeps_pcp_3h_lag6 <- join_to_fcst(
cmeps_pcp_3h_lag6,
obs_pcp_3h
)
verif_cmeps_pcp_3h_lag6 <- ens_verify(cmeps_pcp_3h_lag6, AccPcp3h, verify_members = FALSE)
And the plotting can be done using plot_point_verif from harpVis:
library(harpVis)
plot_point_verif(verif_cmeps_pcp_3h_lag6, spread_skill)

plot_point_verif(verif_cmeps_pcp_3h_lag6, mean_bias)

plot_point_verif(verif_cmeps_pcp_3h_lag6, crps)

The fall in number of cases with lead time is due to the observations only going to 21 UTC 24 Feb 2019.
We can also verify each cycle separately.
verif_cmeps_pcp_3h_lag6_cycles <- ens_verify(
cmeps_pcp_3h_lag6,
AccPcp3h,
groupings = c("leadtime", "fcst_cycle"),
verify_members = FALSE
)
plot_point_verif(verif_cmeps_pcp_3h_lag6_cycles, spread_skill, facet_by = vars(fcst_cycle), num_facet_cols = 2)

plot_point_verif(verif_cmeps_pcp_3h_lag6_cycles, mean_bias, facet_by = vars(fcst_cycle), num_facet_cols = 2)

plot_point_verif(verif_cmeps_pcp_3h_lag6_cycles, crps, facet_by = vars(fcst_cycle), num_facet_cols = 2)

Are these the correct cycles?
By using 0, 6, 12 and 18 as the parent cycles, we are not using the optimum combination of members - we use 3 members from the most recent cycle, 9 members from the previous cycle and 6 members from the cycle before that. To use the members related to the most recent output streams it would seem advantageous to use 2, 8, 14 and 20 as the parent cycles.
cmeps_pcp_3h_lag6_off2 <- lag_forecast(
cmeps_pcp_3h,
"CMEPS_prod",
seq(2, 20, 6)
)
Here it is difficult to select the common cases with MEPS since the run times are different, but we hope for the best!
cmeps_pcp_3h_lag6_off2 <- join_to_fcst(cmeps_pcp_3h_lag6_off2, obs_pcp_3h)
We can then bind the two together - there is no dedicated function to do this yet, so a work around is used using new_harp_fcst, which is not exported from harpPoint and map2 and bind_rows from the purrr and dplyr packages respectively. Proposal for new function: bind_fcst_models
library(purrr)
library(dplyr)
cmeps_pcp_3h_lag6_all <- map2(
cmeps_pcp_3h_lag6,
cmeps_pcp_3h_lag6_off2,
bind_rows
) %>%
harpPoint:::new_harp_fcst()
Now the verfication can be done for all of these cycles.
verif_cmeps_pcp_3h_lag6_cycles <- ens_verify(
cmeps_pcp_3h_lag6_all,
AccPcp3h,
groupings = c("leadtime", "fcst_cycle"),
verify_members = FALSE
)
plot_point_verif(verif_cmeps_pcp_3h_lag6_cycles, spread_skill, facet_by = vars(fcst_cycle), num_facet_cols = 2)

plot_point_verif(verif_cmeps_pcp_3h_lag6_cycles, mean_bias, facet_by = vars(fcst_cycle), num_facet_cols = 2)

plot_point_verif(verif_cmeps_pcp_3h_lag6_cycles, crps, facet_by = vars(fcst_cycle), num_facet_cols = 2)

It doesn’t appear to make a lot of difference for 3h precipitation. However, strange behaviour was seen previously with mean sea level pressure. How is this affected by using different cycles?
cmeps_pmsl <- read_point_forecast(
2019021321,
2019022421,
c("CMEPS_prod", "MEPS_prod"),
"EPS",
"Pmsl",
by = "3h",
lead_time = seq(0, 63, 3),
lags = list(CMEPS_prod = c("0h", "1h", "2h"), MEPS_prod = "0h"),
file_path = "/lustre/storeB/users/andrewts/harp_test_data/harp_output/FCTABLE"
)
Note that MEPS_prod pressure is in Pa - convert to hPa for consistency with CMEPS_prod.
cmeps_pmsl$MEPS_prod <- cmeps_pmsl$MEPS_prod %>%
mutate_at(vars(contains("_mbr")), funs(. / 100))
cmeps_pmsl_lag6 <- lag_forecast(
cmeps_pmsl,
"CMEPS_prod",
seq(0, 18, 6)
) %>%
common_cases()
cmeps_pmsl_lag6_off2 <- lag_forecast(
cmeps_pmsl,
"CMEPS_prod",
seq(0, 18, 6) + 2
)
cmeps_pmsl_lag6_all <- map2(
cmeps_pmsl_lag6["CMEPS_prod"],
cmeps_pmsl_lag6_off2["CMEPS_prod"],
bind_rows
)
cmeps_pmsl_lag6_all <- c(cmeps_pmsl_lag6_all, cmeps_pmsl["MEPS_prod"]) %>%
harpPoint:::new_harp_fcst()
Now we can read in the observations and do the verification.
obs_pmsl <- read_point_obs(
first_validdate(cmeps_pmsl_lag6_all),
last_validdate(cmeps_pmsl_lag6_all),
"Pmsl",
obs_path = "/lustre/storeB/users/andrewts/harp_test_data/harp_output/OBSTABLE",
obsfile_template = "OBS_{YYYY}.sqlite"
)
Note that the observations are in Pa and the forecasts are in hPa - so divide observations by 100 before joining.
obs_pmsl <- obs_pmsl %>%
mutate(Pmsl = Pmsl / 100)
cmeps_pmsl_lag6 <- join_to_fcst(cmeps_pmsl_lag6, obs_pmsl)
cmeps_pmsl_lag6_all <- join_to_fcst(cmeps_pmsl_lag6_all, obs_pmsl)
First we do the verification for all cycles together.
verif_cmeps_pmsl_lag6 <- ens_verify(cmeps_pmsl_lag6, Pmsl, verify_members = FALSE)
And plot some results.
plot_point_verif(verif_cmeps_pmsl_lag6, spread_skill)

plot_point_verif(verif_cmeps_pmsl_lag6, mean_bias)

plot_point_verif(verif_cmeps_pmsl_lag6, crps)

And now for the cycles individually:
verif_cmeps_pmsl_lag6_all <- ens_verify(cmeps_pmsl_lag6_all, Pmsl, groupings = c("leadtime", "fcst_cycle"), verify_members = FALSE)
And plot some results.
plot_point_verif(verif_cmeps_pmsl_lag6_all, spread_skill, facet_by = vars(fcst_cycle), num_facet_cols = 2)

plot_point_verif(verif_cmeps_pmsl_lag6_all, mean_bias, facet_by = vars(fcst_cycle), num_facet_cols = 2)

plot_point_verif(verif_cmeps_pmsl_lag6_all, crps, facet_by = vars(fcst_cycle), num_facet_cols = 2)

LS0tCnRpdGxlOiAiRVBTIHZlcmlmaWNhdGlvbiB3aXRoIGEgbGFnZ2VkIGVuc2VtYmxlIHVzaW5nIGhhcnAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjI0ludHJvZHVjdGlvbgpJbiBNZXRDb09wIHRoZXJlIGlzIGEgbW92ZSB0b3dhcmRzIHNwcmVhZGluZyB0aGUgbG9hZCBvbiB0aGUgSFBDKHMpIGJ5IHJ1bm5pbmcgYSBzbWFsbCBudW1iZXIgb2YgZW5zZW1ibGUgbWVtYmVycyBmb3IgTUVQUyBlYWNoIGhvdXIuIFRoaXMgY29udHJhc3RzIHdpdGggdGhlIG5vcm1hbCBtZXRob2Qgb2YgcnVubmluZyBhbGwgbWVtYmVycyBldmVyeSA2IGhvdXJzLiBUaGUgZW5lc21lYmxlIHN5c3RlbSBpcyBjYWxsZWQgQ01FUFMgKHdoYXQgZG9lcyB0aGUgQyBzdGFuZCBmb3I/KS4gVG8gcHJvZHVjZSBhbiBlbnNlbWJsZSBmb3JlY2FzdCwgdGhlIG1lbWJlcnMgYXJlIGxhZ2dlZCBpbiB0aW1lIHNvIHRoYXQgYSBmdWxsIGVuc2VtYmxlIGlzIGF2YWlsYWJsZSBldmVyeSAzIGhvdXJzLCB3aGljaCBpcyBsYWdnZWQgZnVydGhlciB0byBwcm9kdWNlIGEgbGFyZ2VyIGVuc2VtYmxlIGV2ZXJ5IDYgaG91cnMuIAoKRm9yIHZlcmlmaWNhdGlvbiwgdGhpcyBtZWFucyB0aGF0IHRoZSBkYXRhIGhhdmUgdG8gYmUgbGFnZ2VkIGJlZm9yZSB0aGUgc2NvcmVzIGNhbiBiZSBjb21wdXRlZC4gSW4gdGhlIGZpcnN0IGluc3RhbmNlLCB0aGUgbW9kZWwgb3V0cHV0cyBhcmUgaW50ZXJwb2xhdGVkIHRvIHN0YXRpb24gbG9jYXRpb25zIGFuZCB3cml0dGVuIG91dCB0byBGQ1RBQkxFIGZpbGVzIC0gYXQgdGhpcyBzdGFnZSwgbm8gbGVhZCB0aW1lcyBvciBmb3JlY2FzdCBkYXRlcyBhcmUgdXBkYXRlZCB0byByZWZsZWN0IHRoZSBsYWdnaW5nLiBIb3dldmVyLCBpbiBvcmRlciB0byByZWFkIHRvIHRoZSBjb3JyZWN0IG1lbWJlcnMgYXQgdGhlIGNvcnJlY3QgdGltZXMsIHRoZSBsYWdnaW5nIGluZm9ybWF0aW9uIGlzIHJlcXVpcmVkLiAKCkZvciBDTUVQUywgbWVtYmVycyAwLCAxIGFuZCAyIGFyZSBydW4gZXZlcnkgdGhyZWUgaG91cnMgYXQgMCwgMywgNiwgLi4uLCAyMSBVVEMuIEZvciB0aGUgcHVycG9zZSBvZiBjbGFyaXR5IHdlIHdpbGwgcmVmZXIgdG8gdGhlc2UgbWVtYmVycyBhcyB0aGUgKnBhcmVudCogbWVtYmVycyBhbmQgdGhlIGxhZ2dlZCBtZW1iZXJzIGFzIHRoZSAqY2hpbGQqIG1lbWJlcnMuIFRoZSBjaGlsZCBtZW1iZXJzIDMsIDQgYW5kIDcgYXJlIHJ1biAxIGhvdXIgbGF0ZXIgdGhhbiB0aGUgcGFyZW50cywgYW5kIHRoZSBjaGlsZCBtZW1iZXJzIDUsIDYgYW5kIDggYXJlIHJ1biAyIGhvdXJzIGxhdGVyIHRoYW4gdGhlIHBhcmVudHMsIGkuZS4gYXQgMiwgNSwgOCwgLi4uLCAyMyBVVEMuIEhvd2V2ZXIsIGZvciBhICoqbGFnZ2VkKiogZm9yZWNhc3QsIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBjaGlsZCBtZW1iZXJzIHRoYXQgYmVsb25nIHRvIHRoZSBwcmV2aW91cyBmb3JlY2FzdCwgc2luY2UgdGhlIGNoaWxkIG1lbWJlcnMgZnJvbSB0aGUgY3VycmVudCBwYXJlbnQgd2lsbCBub3QgYmUgYXZhaWxhYmxlIHlldC4gCgpUaGUgY29udmVyc2lvbiB0byBzcWxpdGUgY2FuIGVhc2lseSBiZSBkb25lIHVzaW5nIHRoZSBgcmVhZF9lcHNfaW50ZXJwb2xhdGVgIGZ1bmN0aW9uIGZyb20gaGFycElPIGFuZCBzcGVjaWZ5aW5nIHRoZSBsYWdnaW5nIG9mIGVhY2ggbWVtYmVyIGluIHRoZSBgbGFnc2AgYXJndW1lbnQuIAoKYGBge3IgcmVzdWx0cz0naGlkZScsIGV2YWw9RkFMU0V9CmxpYnJhcnkoaGFycElPKQpyZWFkX2Vwc19pbnRlcnBvbGF0ZSgKICAyMDE5MDIxMzEyLCAKICAyMDE5MDIyNDIxLCAKICAiQ01FUFNfcHJvZCIsIAogICJQY3AiLCAKICBieSAgICAgICAgICA9ICIzaCIsIAogIGxlYWRfdGltZSAgID0gc2VxKDAsIDYzLCAzKSwgCiAgbWVtYmVyc19pbiAgPSBzZXEoMCwgOCksIAogIGxhZ3MgICAgICAgID0gcGFzdGUwKGMoMCwgMCwgMCwgMiwgMiwgMSwgMSwgMiwgMSksICJoIiksIAogIGZpbGVfcGF0aCAgID0gIi9sdXN0cmUvc3RvcmVCL3VzZXJzL2FuZHJld3RzL2hhcnBfdGVzdF9kYXRhL3ZmbGQiLAogIHNxbGl0ZV9wYXRoID0gIi9sdXN0cmUvc3RvcmVCL3VzZXJzL2FuZHJld3RzL2hhcnBfdGVzdF9kYXRhL2hhcnBfb3V0cHV0L0ZDVEFCTEUiCikKYGBgCgpVc2luZyB0aGUgc3RhbmRhcmQgc3FsaXRlIGZpbGUgbmFtZSB0ZW1wbGF0ZSwgYW4gc3FsaXRlIGZpbGUgaXMgY3JlYXRlZCBmb3IgZWFjaCBsZWFkIHRpbWUsIGNvbnRhaW5pbmcgb25seSB0aG9zZSBtZW1iZXJzIHRoYXQgd2VyZSBhdmFpbGFibGUgZm9yIHRoYXQgbGVhZCB0aW1lLiBOb3RlIHRoYXQgaXQncyBxdWl0ZSBhIHNsb3cgcHJvY2VzcyBhbmQgbWF5YmUgcHJvdmlkZXMgbW90aXZhdGlvbiBmb3IgcmUtaW1wbGVtZW50aW5nIHRoZSBvbGQgbWV0aG9kIGZvciByZWFkaW5nIGZyb20gdmZsZCBmaWxlcyAtIGp1c3QgdG8gY29tcGFyZSB0aGUgc3BlZWQsIGFuZCBhbHNvIGJlY2F1c2Ugc3VwcG9ydCBmb3IgdmZsZCB2ZXJzaW9uIDIgaXMgbmVlZGVkLiAgCgpGb3IgdmVyaWZpY2F0aW9uLCB0aGUgZGF0YSBhcmUgcmVhZCBpbiBmcm9tIHRoZSBzcWxpdGUgZmlsZXMgdXNpbmcgcmVhZF9wb2ludF9mb3JlY2FzdCBmcm9tIGhhcnBJTy4gVGhlIHBhcmVudCBmb3JlY2FzdCB0aW1lcyBhcmUgYmFzaWNhbGx5IGFzIGRlcml2ZWQgZnJvbSB0aGUgYHN0YXJ0X2RhdGVgLCBgZW5kX2RhdGVgIGFuZCBgYnlgIGFyZ3VtZW50cy4gQWxsIG9mIHRoZSBhcHByb3ByaWF0ZSBsYWdnZWQgbWVtYmVycyBhcmUgcmVhZCBpbiBmb3IgdGhlIHJlbGF0aXZlIHRpbWVzIGFuZCBsZWFkIHRpbWVzIGRlcml2ZWQgZnJvbSB0aGUgYGxhZ3NgIGFyZ3VtZW50LiBIZXJlIHdlIGRvIG5vdCBuZWVkIHRvIHNwZWNpZmlmeSB3aGF0IHRoZSBsYWdzIGFyZSBmb3IgZWFjaCBtZW1iZXIgYXMgYWxsIGRhdGEgdGhhdCBjYW4gYmUgZm91bmQgd2lsbCBiZSByZWFkIGluLiBBcyB0aGUgbGFnZ2luZyBpcyBkb25lIHRvIDYgaG91cnMsIG5lZWQgdG8gbWFrZSBzdXJlIHdlIGFkanVzdCB0aGUgc3RhcnQgZGF0ZSB0byBhY2NvdW50IGZvciA2IGhvdXIgbGFnZ2luZyAodGFrZSBpdCBiYWNrIDMgaG91cnMsIGFzIHRoZSAwLCAxLCAyIGxhZ3MgYXJlIGFscmVhZHkgdGhlcmUpCgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KY21lcHNfcGNwXzNoIDwtIHJlYWRfcG9pbnRfZm9yZWNhc3QoCiAgMjAxOTAyMTMyMSwgCiAgMjAxOTAyMjQyMSwgCiAgYygiQ01FUFNfcHJvZCIsICJNRVBTX3Byb2QiKSwgCiAgIkVQUyIsIAogICJBY2NQY3AzaCIsIAogIGJ5ICAgICAgICA9ICIzaCIsIAogIGxlYWRfdGltZSA9IHNlcSgwLCA2MywgMyksIAogIGxhZ3MgICAgICA9IGxpc3QoQ01FUFNfcHJvZCA9IGMoIjBoIiwgIjFoIiwgIjJoIiksIE1FUFNfcHJvZCA9ICIwaCIpLCAKICBmaWxlX3BhdGggPSAiL2x1c3RyZS9zdG9yZUIvdXNlcnMvYW5kcmV3dHMvaGFycF90ZXN0X2RhdGEvaGFycF9vdXRwdXQvRkNUQUJMRSIKKQpgYGAKClRoZSBhYm92ZSBjb3VsZCBlcXVhbGx5IGJlIGRvbmUgd2l0aCBgYnkgPSAiNmgiYCBhbmQgYGxhZ3MgPSBsaXN0KENNRVBTX3Byb2QgPSBwYXN0ZTAoc2VxKDAsIDUpLCAiaCIpLCBNRVBTX3Byb2QgPSAiMGgiKWAgd2l0aG91dCBjaGFuZ2luZyBhbnl0aGluZywgb3IgaW5kZWVkIGBieSA9ICIxaCJgIGFuZCBsYWdzIG5vdCBzZXQuCgpCZWZvcmUgd2UgY2FuIHZlcmlmeSwgd2UgbXVzdCBhY3R1YWxseSBkbyB0aGUgbGFnZ2luZy4gVGhpcyBpcyBkb25lIHVzaW5nIHRoZSBsYWdfZm9yZWNhc3QgZnVuY3Rpb24gZnJvbSBoYXJwUG9pbnQuIEluIHRoaXMgZnVuY3Rpb24sIHRoZSBmb3JlY2FzdCBtb2RlbCBpbiB0aGUgaGFycF9mY3N0IG9iamVjdCB5b3Ugd2FudCB0byBkbyB0aGUgbGFnZ2luZyBmb3IsIGFuZCB0aGUgcGFyZW50IGZvcmVjYXN0IGN5Y2xlcyBmb3IgdGhlIGxhZ2dlZCBmb3JlY2FzdCBuZWVkIHRvIGJlIHNwZWNpZmllZC4gQWxsIGNoaWxkIG1lbWJlcnMgZm9yIHRoZSBwYXJlbnRzIGZvciBlYWNoIGN5Y2xlIHdpbGwgYXV0b21hdGljYWxseSBiZSBmb3VuZCAtIHRoZSBjaGlsZCBjeWNsZXMgYXJlIGVzc2VudGlhbGx5IGFsbCBjeWNsZXMgYmV0d2VlbiB0aGUgcGFyZW50IGN5Y2xlcy4gVGhlIGZ1bmN0aW9uIGlzIG5vdCBhYmxlIHRvIGluY2x1ZGUgY2hpbGQgY3ljbGVzIHRoYXQgYXJlIG9sZGVyIHRoYW4gdGhlIG5leHQgb2xkZXN0IHBhcmVudCBjeWNsZSwgYnV0IHRoZXJlIGlzIGEgd2F5IGFyb3VuZCB0aGlzIHRoYXQgd2lsbCBiZSBzaG93biBsYXRlci4gCgpGb3IgQ01FUFMsIHRoZSBpbnRlbnRpb24gaXMgdG8gaGF2ZSBhbiBlbnNlbWJsZSBldmVyeSA2IGhvdXJzIGluY2x1ZGluZyBhbGwgbWVtYmVycyBmcm9tIHRoZSBjdXJyZW50IGN5Y2xlIGFuZCB0aGUgY3ljbGVzIGZvciB0aGUgcHJldmlvdXMgNSBob3Vycy4gV2UgY2FuIGRvIHRoaXMgYnkgbGFnZ2luZyB0aGUgZm9yZWNhc3Qgd2l0aCBwYXJlbnQgY3ljbGVzIGF0IDAsIDYsIDEyIGFuZCAxOCBVVEMuIAoKYGBge3J9CmxpYnJhcnkoaGFycFBvaW50KQpjbWVwc19wY3BfM2hfbGFnNiA8LSBsYWdfZm9yZWNhc3QoCiAgY21lcHNfcGNwXzNoLAogICJDTUVQU19wcm9kIiwKICBzZXEoMCwgMTgsIDYpCikKYGBgCgpGb3IgYSBmYWlyIGNvbXBhcmlzb24gb2YgQ01FUFMgYW5kIE1FUFMsIHRoZSBjb21tb24gY2FzZXMgYmV0d2VlbiB0aGUgdHdvIHN5c3RlbXMgc2hvdWxkIGJlIHNlbGVjdGVkLgoKYGBge3J9CmNtZXBzX3BjcF8zaF9sYWc2IDwtIGNvbW1vbl9jYXNlcyhjbWVwc19wY3BfM2hfbGFnNikKYGBgCgpOb3cgd2UgY2FuIHJlYWQgaW4gdGhlIG9ic2VydmF0aW9ucyBhbmQgZG8gdGhlIHZlcmlmaWNhdGlvbi4KYGBge3IgcmVzdWx0cz0naGlkZSd9Cm9ic19wY3BfM2ggPC0gcmVhZF9wb2ludF9vYnMoCiAgZmlyc3RfdmFsaWRkYXRlKGNtZXBzX3BjcF8zaF9sYWc2KSwgCiAgbGFzdF92YWxpZGRhdGUoY21lcHNfcGNwXzNoX2xhZzYpLCAKICAiQWNjUGNwM2giLCAKICBvYnNfcGF0aCA9ICIvbHVzdHJlL3N0b3JlQi91c2Vycy9hbmRyZXd0cy9oYXJwX3Rlc3RfZGF0YS9oYXJwX291dHB1dC9PQlNUQUJMRSIsIAogIG9ic2ZpbGVfdGVtcGxhdGUgPSAiT0JTX3tZWVlZfS5zcWxpdGUiCikKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpjbWVwc19wY3BfM2hfbGFnNiA8LSBqb2luX3RvX2Zjc3QoCiAgY21lcHNfcGNwXzNoX2xhZzYsCiAgb2JzX3BjcF8zaAopCmBgYAoKYGBge3IgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9CnZlcmlmX2NtZXBzX3BjcF8zaF9sYWc2IDwtIGVuc192ZXJpZnkoY21lcHNfcGNwXzNoX2xhZzYsIEFjY1BjcDNoLCB2ZXJpZnlfbWVtYmVycyA9IEZBTFNFKQpgYGAKCkFuZCB0aGUgcGxvdHRpbmcgY2FuIGJlIGRvbmUgdXNpbmcgcGxvdF9wb2ludF92ZXJpZiBmcm9tIGhhcnBWaXM6CmBgYHtyIG1lc3NhZ2U9RkFMU0UsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CmxpYnJhcnkoaGFycFZpcykKcGxvdF9wb2ludF92ZXJpZih2ZXJpZl9jbWVwc19wY3BfM2hfbGFnNiwgc3ByZWFkX3NraWxsKQpwbG90X3BvaW50X3ZlcmlmKHZlcmlmX2NtZXBzX3BjcF8zaF9sYWc2LCBtZWFuX2JpYXMpCnBsb3RfcG9pbnRfdmVyaWYodmVyaWZfY21lcHNfcGNwXzNoX2xhZzYsIGNycHMpCmBgYAoKVGhlIGZhbGwgaW4gbnVtYmVyIG9mIGNhc2VzIHdpdGggbGVhZCB0aW1lIGlzIGR1ZSB0byB0aGUgb2JzZXJ2YXRpb25zIG9ubHkgZ29pbmcgdG8gMjEgVVRDIDI0IEZlYiAyMDE5LgoKV2UgY2FuIGFsc28gdmVyaWZ5IGVhY2ggY3ljbGUgc2VwYXJhdGVseS4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQp2ZXJpZl9jbWVwc19wY3BfM2hfbGFnNl9jeWNsZXMgPC0gZW5zX3ZlcmlmeSgKICBjbWVwc19wY3BfM2hfbGFnNiwgCiAgQWNjUGNwM2gsIAogIGdyb3VwaW5ncyA9IGMoImxlYWR0aW1lIiwgImZjc3RfY3ljbGUiKSwgCiAgdmVyaWZ5X21lbWJlcnMgPSBGQUxTRQopCmBgYAoKYGBge3Igd2FybmluZz1GQUxTRX0KcGxvdF9wb2ludF92ZXJpZih2ZXJpZl9jbWVwc19wY3BfM2hfbGFnNl9jeWNsZXMsIHNwcmVhZF9za2lsbCwgZmFjZXRfYnkgPSB2YXJzKGZjc3RfY3ljbGUpLCBudW1fZmFjZXRfY29scyA9IDIpCnBsb3RfcG9pbnRfdmVyaWYodmVyaWZfY21lcHNfcGNwXzNoX2xhZzZfY3ljbGVzLCBtZWFuX2JpYXMsIGZhY2V0X2J5ID0gdmFycyhmY3N0X2N5Y2xlKSwgbnVtX2ZhY2V0X2NvbHMgPSAyKQpwbG90X3BvaW50X3ZlcmlmKHZlcmlmX2NtZXBzX3BjcF8zaF9sYWc2X2N5Y2xlcywgY3JwcywgZmFjZXRfYnkgPSB2YXJzKGZjc3RfY3ljbGUpLCBudW1fZmFjZXRfY29scyA9IDIpCmBgYAoKIyMjQXJlIHRoZXNlIHRoZSBjb3JyZWN0IGN5Y2xlcz8KQnkgdXNpbmcgMCwgNiwgMTIgYW5kIDE4IGFzIHRoZSBwYXJlbnQgY3ljbGVzLCB3ZSBhcmUgbm90IHVzaW5nIHRoZSBvcHRpbXVtIGNvbWJpbmF0aW9uIG9mIG1lbWJlcnMgLSB3ZSB1c2UgMyBtZW1iZXJzIGZyb20gdGhlIG1vc3QgcmVjZW50IGN5Y2xlLCA5IG1lbWJlcnMgZnJvbSB0aGUgcHJldmlvdXMgY3ljbGUgYW5kIDYgbWVtYmVycyBmcm9tIHRoZSBjeWNsZSBiZWZvcmUgdGhhdC4gVG8gdXNlIHRoZSBtZW1iZXJzIHJlbGF0ZWQgdG8gdGhlIG1vc3QgcmVjZW50IG91dHB1dCBzdHJlYW1zIGl0IHdvdWxkIHNlZW0gYWR2YW50YWdlb3VzIHRvIHVzZSAyLCA4LCAxNCBhbmQgMjAgYXMgdGhlIHBhcmVudCBjeWNsZXMuIAoKYGBge3J9CmNtZXBzX3BjcF8zaF9sYWc2X29mZjIgPC0gbGFnX2ZvcmVjYXN0KAogIGNtZXBzX3BjcF8zaCwgCiAgIkNNRVBTX3Byb2QiLAogIHNlcSgyLCAyMCwgNikKKQpgYGAKCkhlcmUgaXQgaXMgZGlmZmljdWx0IHRvIHNlbGVjdCB0aGUgY29tbW9uIGNhc2VzIHdpdGggTUVQUyBzaW5jZSB0aGUgcnVuIHRpbWVzIGFyZSBkaWZmZXJlbnQsIGJ1dCB3ZSBob3BlIGZvciB0aGUgYmVzdCEKCmBgYHtyIG1lc3NhZ2U9RkFMU0V9CmNtZXBzX3BjcF8zaF9sYWc2X29mZjIgPC0gam9pbl90b19mY3N0KGNtZXBzX3BjcF8zaF9sYWc2X29mZjIsIG9ic19wY3BfM2gpCmBgYAoKV2UgY2FuIHRoZW4gYmluZCB0aGUgdHdvIHRvZ2V0aGVyIC0gdGhlcmUgaXMgbm8gZGVkaWNhdGVkIGZ1bmN0aW9uIHRvIGRvIHRoaXMgeWV0LCBzbyBhIHdvcmsgYXJvdW5kIGlzIHVzZWQgdXNpbmcgbmV3X2hhcnBfZmNzdCwgd2hpY2ggaXMgbm90IGV4cG9ydGVkIGZyb20gaGFycFBvaW50IGFuZCBtYXAyIGFuZCBiaW5kX3Jvd3MgZnJvbSB0aGUgcHVycnIgYW5kIGRwbHlyIHBhY2thZ2VzIHJlc3BlY3RpdmVseS4gKipQcm9wb3NhbCBmb3IgbmV3IGZ1bmN0aW9uOiBiaW5kX2Zjc3RfbW9kZWxzKioKCmBgYHtyfQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KGRwbHlyKQpjbWVwc19wY3BfM2hfbGFnNl9hbGwgPC0gbWFwMigKICBjbWVwc19wY3BfM2hfbGFnNiwKICBjbWVwc19wY3BfM2hfbGFnNl9vZmYyLAogIGJpbmRfcm93cwopICU+JSAKaGFycFBvaW50Ojo6bmV3X2hhcnBfZmNzdCgpCmBgYAoKTm93IHRoZSB2ZXJmaWNhdGlvbiBjYW4gYmUgZG9uZSBmb3IgYWxsIG9mIHRoZXNlIGN5Y2xlcy4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQp2ZXJpZl9jbWVwc19wY3BfM2hfbGFnNl9jeWNsZXMgPC0gZW5zX3ZlcmlmeSgKICBjbWVwc19wY3BfM2hfbGFnNl9hbGwsIAogIEFjY1BjcDNoLCAKICBncm91cGluZ3MgPSBjKCJsZWFkdGltZSIsICJmY3N0X2N5Y2xlIiksIAogIHZlcmlmeV9tZW1iZXJzID0gRkFMU0UKKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CnBsb3RfcG9pbnRfdmVyaWYodmVyaWZfY21lcHNfcGNwXzNoX2xhZzZfY3ljbGVzLCBzcHJlYWRfc2tpbGwsIGZhY2V0X2J5ID0gdmFycyhmY3N0X2N5Y2xlKSwgbnVtX2ZhY2V0X2NvbHMgPSAyKQpwbG90X3BvaW50X3ZlcmlmKHZlcmlmX2NtZXBzX3BjcF8zaF9sYWc2X2N5Y2xlcywgbWVhbl9iaWFzLCBmYWNldF9ieSA9IHZhcnMoZmNzdF9jeWNsZSksIG51bV9mYWNldF9jb2xzID0gMikKcGxvdF9wb2ludF92ZXJpZih2ZXJpZl9jbWVwc19wY3BfM2hfbGFnNl9jeWNsZXMsIGNycHMsIGZhY2V0X2J5ID0gdmFycyhmY3N0X2N5Y2xlKSwgbnVtX2ZhY2V0X2NvbHMgPSAyKQpgYGAKCkl0IGRvZXNuJ3QgYXBwZWFyIHRvIG1ha2UgYSBsb3Qgb2YgZGlmZmVyZW5jZSBmb3IgM2ggcHJlY2lwaXRhdGlvbi4gSG93ZXZlciwgc3RyYW5nZSBiZWhhdmlvdXIgd2FzIHNlZW4gcHJldmlvdXNseSB3aXRoIG1lYW4gc2VhIGxldmVsIHByZXNzdXJlLiBIb3cgaXMgdGhpcyBhZmZlY3RlZCBieSB1c2luZyBkaWZmZXJlbnQgY3ljbGVzPwoKYGBge3IgcmVzdWx0cz0naGlkZSd9CmNtZXBzX3Btc2wgPC0gcmVhZF9wb2ludF9mb3JlY2FzdCgKICAyMDE5MDIxMzIxLCAKICAyMDE5MDIyNDIxLCAKICBjKCJDTUVQU19wcm9kIiwgIk1FUFNfcHJvZCIpLCAKICAiRVBTIiwgCiAgIlBtc2wiLCAKICBieSAgICAgICAgPSAiM2giLCAKICBsZWFkX3RpbWUgPSBzZXEoMCwgNjMsIDMpLCAKICBsYWdzICAgICAgPSBsaXN0KENNRVBTX3Byb2QgPSBjKCIwaCIsICIxaCIsICIyaCIpLCBNRVBTX3Byb2QgPSAiMGgiKSwgCiAgZmlsZV9wYXRoID0gIi9sdXN0cmUvc3RvcmVCL3VzZXJzL2FuZHJld3RzL2hhcnBfdGVzdF9kYXRhL2hhcnBfb3V0cHV0L0ZDVEFCTEUiCikKYGBgCgpOb3RlIHRoYXQgTUVQU19wcm9kIHByZXNzdXJlIGlzIGluIFBhIC0gY29udmVydCB0byBoUGEgZm9yIGNvbnNpc3RlbmN5IHdpdGggQ01FUFNfcHJvZC4gCgpgYGB7cn0KY21lcHNfcG1zbCRNRVBTX3Byb2QgPC0gY21lcHNfcG1zbCRNRVBTX3Byb2QgJT4lIAogIG11dGF0ZV9hdCh2YXJzKGNvbnRhaW5zKCJfbWJyIikpLCBmdW5zKC4gLyAxMDApKQpgYGAKCmBgYHtyfQpjbWVwc19wbXNsX2xhZzYgPC0gbGFnX2ZvcmVjYXN0KAogIGNtZXBzX3Btc2wsCiAgIkNNRVBTX3Byb2QiLAogIHNlcSgwLCAxOCwgNikKKSAlPiUgCiAgY29tbW9uX2Nhc2VzKCkKCmNtZXBzX3Btc2xfbGFnNl9vZmYyIDwtIGxhZ19mb3JlY2FzdCgKICBjbWVwc19wbXNsLAogICJDTUVQU19wcm9kIiwKICBzZXEoMCwgMTgsIDYpICsgMgopCgpjbWVwc19wbXNsX2xhZzZfYWxsIDwtIG1hcDIoCiAgY21lcHNfcG1zbF9sYWc2WyJDTUVQU19wcm9kIl0sCiAgY21lcHNfcG1zbF9sYWc2X29mZjJbIkNNRVBTX3Byb2QiXSwKICBiaW5kX3Jvd3MKKSAKCmNtZXBzX3Btc2xfbGFnNl9hbGwgPC0gYyhjbWVwc19wbXNsX2xhZzZfYWxsLCBjbWVwc19wbXNsWyJNRVBTX3Byb2QiXSkgJT4lIAogIGhhcnBQb2ludDo6Om5ld19oYXJwX2Zjc3QoKQpgYGAKCk5vdyB3ZSBjYW4gcmVhZCBpbiB0aGUgb2JzZXJ2YXRpb25zIGFuZCBkbyB0aGUgdmVyaWZpY2F0aW9uLgpgYGB7ciByZXN1bHRzPSdoaWRlJ30Kb2JzX3Btc2wgPC0gcmVhZF9wb2ludF9vYnMoCiAgZmlyc3RfdmFsaWRkYXRlKGNtZXBzX3Btc2xfbGFnNl9hbGwpLCAKICBsYXN0X3ZhbGlkZGF0ZShjbWVwc19wbXNsX2xhZzZfYWxsKSwgCiAgIlBtc2wiLCAKICBvYnNfcGF0aCA9ICIvbHVzdHJlL3N0b3JlQi91c2Vycy9hbmRyZXd0cy9oYXJwX3Rlc3RfZGF0YS9oYXJwX291dHB1dC9PQlNUQUJMRSIsIAogIG9ic2ZpbGVfdGVtcGxhdGUgPSAiT0JTX3tZWVlZfS5zcWxpdGUiCikKYGBgCgpOb3RlIHRoYXQgdGhlIG9ic2VydmF0aW9ucyBhcmUgaW4gUGEgYW5kIHRoZSBmb3JlY2FzdHMgYXJlIGluIGhQYSAtIHNvIGRpdmlkZSBvYnNlcnZhdGlvbnMgYnkgMTAwIGJlZm9yZSBqb2luaW5nLiAKCmBgYHtyfQpvYnNfcG1zbCA8LSBvYnNfcG1zbCAlPiUgCiAgbXV0YXRlKFBtc2wgPSBQbXNsIC8gMTAwKQpgYGAKCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpjbWVwc19wbXNsX2xhZzYgICAgIDwtIGpvaW5fdG9fZmNzdChjbWVwc19wbXNsX2xhZzYsIG9ic19wbXNsKQpjbWVwc19wbXNsX2xhZzZfYWxsIDwtIGpvaW5fdG9fZmNzdChjbWVwc19wbXNsX2xhZzZfYWxsLCBvYnNfcG1zbCkKYGBgCgpGaXJzdCB3ZSBkbyB0aGUgdmVyaWZpY2F0aW9uIGZvciBhbGwgY3ljbGVzIHRvZ2V0aGVyLgpgYGB7ciByZXN1bHRzPSdoaWRlJ30KdmVyaWZfY21lcHNfcG1zbF9sYWc2IDwtIGVuc192ZXJpZnkoY21lcHNfcG1zbF9sYWc2LCBQbXNsLCB2ZXJpZnlfbWVtYmVycyA9IEZBTFNFKQpgYGAKCkFuZCBwbG90IHNvbWUgcmVzdWx0cy4KYGBge3IgbWVzc2FnZT1GQUxTRSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KcGxvdF9wb2ludF92ZXJpZih2ZXJpZl9jbWVwc19wbXNsX2xhZzYsIHNwcmVhZF9za2lsbCkKcGxvdF9wb2ludF92ZXJpZih2ZXJpZl9jbWVwc19wbXNsX2xhZzYsIG1lYW5fYmlhcykKcGxvdF9wb2ludF92ZXJpZih2ZXJpZl9jbWVwc19wbXNsX2xhZzYsIGNycHMpCmBgYAoKQW5kIG5vdyBmb3IgdGhlIGN5Y2xlcyBpbmRpdmlkdWFsbHk6CmBgYHtyIHJlc3VsdHM9J2hpZGUnfQp2ZXJpZl9jbWVwc19wbXNsX2xhZzZfYWxsIDwtIGVuc192ZXJpZnkoY21lcHNfcG1zbF9sYWc2X2FsbCwgUG1zbCwgZ3JvdXBpbmdzID0gYygibGVhZHRpbWUiLCAiZmNzdF9jeWNsZSIpLCB2ZXJpZnlfbWVtYmVycyA9IEZBTFNFKQpgYGAKCkFuZCBwbG90IHNvbWUgcmVzdWx0cy4KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OH0KcGxvdF9wb2ludF92ZXJpZih2ZXJpZl9jbWVwc19wbXNsX2xhZzZfYWxsLCBzcHJlYWRfc2tpbGwsIGZhY2V0X2J5ID0gdmFycyhmY3N0X2N5Y2xlKSwgbnVtX2ZhY2V0X2NvbHMgPSAyKQpwbG90X3BvaW50X3ZlcmlmKHZlcmlmX2NtZXBzX3Btc2xfbGFnNl9hbGwsIG1lYW5fYmlhcywgZmFjZXRfYnkgPSB2YXJzKGZjc3RfY3ljbGUpLCBudW1fZmFjZXRfY29scyA9IDIpCnBsb3RfcG9pbnRfdmVyaWYodmVyaWZfY21lcHNfcG1zbF9sYWc2X2FsbCwgY3JwcywgZmFjZXRfYnkgPSB2YXJzKGZjc3RfY3ljbGUpLCBudW1fZmFjZXRfY29scyA9IDIpCmBgYAo=