Case Study
To finish off this section, let’s pull together everything you’ve learned to tackle a realistic data tidying problem. The tidyr::who
dataset contains tuberculosis (TB) cases broken down by year, country, age, gender, and diagnosis method. The data comes from the 2014 World Health Organization Global Tuberculosis Report, available at http://www.who.int/tb/country/data/download/en/.
There’s a wealth of epidemiological information in this dataset, but it’s challenging to work with the data in the form that it’s provided:
who
This is a very typical real-life example dataset. It contains redundant columns, odd variable codes, and many missing values. In short, who
is messy, and we’ll need multiple steps to tidy it. Like dplyr, tidyr is designed so that each function does one thing well. That means in real-life situations you’ll usually need to string together multiple verbs into a pipeline.
The best place to start is almost always to gather together the columns that are not variables. Let’s have a look at what we’ve got:
- It looks like
country
, iso2
, and iso3
are three variables that redundantly specify the country.
year
is clearly also a variable.
- We don’t know what all the other columns are yet, but given the structure in the variable names (e.g.
new_sp_m014
, new_ep_m014
, new_ep_f014
) these are likely to be values, not variables.
So we need to pivot together all the columns from new_sp_m014
to newrel_f65
. We don’t know what those values represent yet, so we’ll give them the generic name "key"
. We know the cells represent the count of cases, so we’ll use the variable cases
. There are a lot of missing values in the current representation, so for now we’ll use values_drop_na = TRUE
just so we can focus on the values that are present.
who1 <- who %>%
pivot_longer(
cols = new_sp_m014:newrel_f65,
names_to = "key",
values_to = "cases",
values_drop_na = TRUE
)
who1
We can get some hint of the structure of the values in the new key column by counting them:
who1 %>%
count(key)
You might be able to parse this out by yourself with a little thought and some experimentation, but luckily we have the data dictionary handy. It tells us:
The first three letters of each column denote whether the column contains new or old cases of TB. In this dataset, each column contains new cases.
The next two letters describe the type of TB:
rel
stands for cases of relapse
ep
stands for cases of extrapulmonary TB
sn
stands for cases of pulmonary TB that could not be diagnosed by a pulmonary smear (smear negative)
sp
stands for cases of pulmonary TB that could be diagnosed be a pulmonary smear (smear positive)
The sixth letter gives the sex of TB patients. The dataset groups cases by males (m
) and females (f
).
The remaining numbers gives the age group. The dataset groups cases into seven age groups:
014
= 0 – 14 years old
1524
= 15 – 24 years old
2534
= 25 – 34 years old
3544
= 35 – 44 years old
4554
= 45 – 54 years old
5564
= 55 – 64 years old
65
= 65 or older
We need to make a minor fix to the format of the column names: unfortunately the names are slightly inconsistent because instead of new_rel
we have newrel
(it’s hard to spot this here but if you don’t fix it we’ll get errors in subsequent steps). You’ll learn about str_replace()
in strings, but the basic idea is pretty simple: replace the characters “newrel” with “new_rel”. This makes all variable names consistent.
who2 <- who1 %>%
mutate(names_from = stringr::str_replace(key, "newrel", "new_rel"))
who2
We can separate the values in each code with two passes of separate()
. The first pass will split the codes at each underscore.
who3 <- who2 %>%
separate(key, c("new", "type", "sexage"), sep = "_")
Expected 3 pieces. Missing pieces filled with `NA` in 2580 rows [243, 244, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 903, 904, 905, 906, ...].
who3
Then we might as well drop the new
column because it’s constant in this dataset. While we’re dropping columns, let’s also drop iso2
and iso3
since they’re redundant.
who3 %>%
count(new)
who4 <- who3 %>%
select(-new, -iso2, -iso3)
Next we’ll separate sexage
into sex
and age
by splitting after the first character:
who5 <- who4 %>%
separate(sexage, c("sex", "age"), sep = 1)
who5
The who
dataset is now tidy!
I’ve shown you the code a piece at a time, assigning each interim result to a new variable. This typically isn’t how you’d work interactively. Instead, you’d gradually build up a complex pipe:
who %>%
pivot_longer(
cols = new_sp_m014:newrel_f65,
names_to = "key",
values_to = "cases",
values_drop_na = TRUE
) %>%
mutate(
key = stringr::str_replace(key, "newrel", "new_rel")
) %>%
separate(key, c("new", "var", "sexage")) %>%
select(-new, -iso2, -iso3) %>%
separate(sexage, c("sex", "age"), sep = 1)
Exercises
1. In this case study, I set values_drop_na = TRUE
just to make it easier to check that we had the correct values. Is this reasonable? Think about how missing values are represented in this dataset. Are there implicit missing values? What’s the difference between an NA and zero?
The reasonableness of using values_drop_na = TRUE
depends on how missing values are represented in this dataset. The main concern is whether a missing value means that there were no cases of TB or whether it means that the WHO does not have data on the number of TB cases. Here are some things we should look for to help distinguish between these cases.
If there are no 0 values in the data, then missing values may be used to indicate no cases.
If there are both explicit and implicit missing values, then it suggests that missing values are being used differently. In that case, it is likely that explicit missing values would mean no cases, and implicit missing values would mean no data on the number of cases.
First, I’ll check for the presence of zeros in the data.
who1 %>%
filter(cases == 0) %>%
nrow()
[1] 11080
There are zeros in the data, so it appears that cases of zero TB are explicitly indicated, and the value ofNA is used to indicate missing data.
Second, I should check whether all values for a (country, year) are missing or whether it is possible for only some columns to be missing.
pivot_longer(who, new_sp_m014:newrel_f65, names_to = "key", values_to = "cases") %>%
group_by(country, year) %>%
mutate(prop_missing = sum(is.na(cases)) / n()) %>%
filter(prop_missing > 0, prop_missing < 1)
From the results above, it looks like it is possible for a (country, year) row to contain some, but not all, missing values in its columns.
Finally, I will check for implicit missing values. Implicit missing values are (year
, country
) combinations that do not appear in the data.
nrow(who)
[1] 7240
who %>%
complete(country, year) %>%
nrow()
[1] 7446
Since the number of complete cases of (country
, year
) is greater than the number of rows in who, there are some implicit values. But that doesn’t tell us what those implicit missing values are. To do this, I will use the anti_join()
function introduced in the later Relational Data lecture.
anti_join(complete(who, country, year), who, by = c("country", "year")) %>%
select(country, year) %>%
group_by(country) %>%
# so I can make better sense of the years
summarise(min_year = min(year), max_year = max(year))
All of these refer to (country
, year
) combinations for years prior to the existence of the country. For example, Timor-Leste achieved independence in 2002, so years prior to that are not included in the data.
To summarize:
0
is used to represent no cases of TB.
- Explicit missing values (
NA
s) are used to represent missing data for (country
, year
) combinations in which the country existed in that year.
- Implicit missing values are used to represent missing data because a country did not exist in that year.
2. What happens if you neglect the mutate()
step? (mutate(key = str_replace(key, "newrel", "new_rel")
)
The separate()
function emits the warning “too few values”. If we check the rows for keys beginning with "newrel_"
, we see that sexage
is missing, and type = m014
.
who3a <- who1 %>%
separate(key, c("new", "type", "sexage"), sep = "_")
Expected 3 pieces. Missing pieces filled with `NA` in 2580 rows [243, 244, 679, 680, 681, 682, 683, 684, 685, 686, 687, 688, 689, 690, 691, 692, 903, 904, 905, 906, ...].
filter(who3a, new == "newrel") %>% head()
3. I claimed that iso2
and iso3
were redundant with country. Confirm this claim.
If iso2
and iso3
are redundant with country, then, within each country, there should only be one distinct combination of iso2
and iso3
values, which is the case.
select(who3, country, iso2, iso3) %>%
distinct() %>%
group_by(country) %>%
filter(n() > 1)
This makes sense, since iso2
and iso3
contain the 2- and 3-letter country abbreviations for the country. The iso2
variable contains each country’s ISO 3166 alpha-2, and the iso3
variable contains each country’s ISO 3166 alpha-3 abbreviation. You may recognize the ISO 3166-2 abbreviations, since they are almost identical to internet country-code top level domains, such as .uk
(United Kingdom), .ly
(Libya), .tv
(Tuvalu), and .io
(British Indian Ocean Territory).
LS0tDQp0aXRsZTogIlRpZHkgRGF0YSBDYXNlIFN0dWR5Ig0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQpgYGB7cn0NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJ0aWR5dmVyc2UiKSkNCmBgYA0KDQojIyBDYXNlIFN0dWR5DQoNClRvIGZpbmlzaCBvZmYgdGhpcyBzZWN0aW9uLCBsZXTigJlzIHB1bGwgdG9nZXRoZXIgZXZlcnl0aGluZyB5b3XigJl2ZSBsZWFybmVkIHRvIHRhY2tsZSBhIHJlYWxpc3RpYyBkYXRhIHRpZHlpbmcgcHJvYmxlbS4gVGhlIGB0aWR5cjo6d2hvYCBkYXRhc2V0IGNvbnRhaW5zIHR1YmVyY3Vsb3NpcyAoVEIpIGNhc2VzIGJyb2tlbiBkb3duIGJ5IHllYXIsIGNvdW50cnksIGFnZSwgZ2VuZGVyLCBhbmQgZGlhZ25vc2lzIG1ldGhvZC4gVGhlIGRhdGEgY29tZXMgZnJvbSB0aGUgKjIwMTQgV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbiBHbG9iYWwgVHViZXJjdWxvc2lzIFJlcG9ydCosIGF2YWlsYWJsZSBhdCA8aHR0cDovL3d3dy53aG8uaW50L3RiL2NvdW50cnkvZGF0YS9kb3dubG9hZC9lbi8+Lg0KDQpUaGVyZeKAmXMgYSB3ZWFsdGggb2YgZXBpZGVtaW9sb2dpY2FsIGluZm9ybWF0aW9uIGluIHRoaXMgZGF0YXNldCwgYnV0IGl04oCZcyBjaGFsbGVuZ2luZyB0byB3b3JrIHdpdGggdGhlIGRhdGEgaW4gdGhlIGZvcm0gdGhhdCBpdOKAmXMgcHJvdmlkZWQ6DQoNCmBgYHtyfQ0Kd2hvDQpgYGANCg0KVGhpcyBpcyBhIHZlcnkgdHlwaWNhbCByZWFsLWxpZmUgZXhhbXBsZSBkYXRhc2V0LiBJdCBjb250YWlucyByZWR1bmRhbnQgY29sdW1ucywgb2RkIHZhcmlhYmxlIGNvZGVzLCBhbmQgbWFueSBtaXNzaW5nIHZhbHVlcy4gSW4gc2hvcnQsIGB3aG9gIGlzIG1lc3N5LCBhbmQgd2XigJlsbCBuZWVkIG11bHRpcGxlIHN0ZXBzIHRvIHRpZHkgaXQuIExpa2UgZHBseXIsIHRpZHlyIGlzIGRlc2lnbmVkIHNvIHRoYXQgZWFjaCBmdW5jdGlvbiBkb2VzIG9uZSB0aGluZyB3ZWxsLiBUaGF0IG1lYW5zIGluIHJlYWwtbGlmZSBzaXR1YXRpb25zIHlvdeKAmWxsIHVzdWFsbHkgbmVlZCB0byBzdHJpbmcgdG9nZXRoZXIgbXVsdGlwbGUgdmVyYnMgaW50byBhIHBpcGVsaW5lLg0KDQpUaGUgYmVzdCBwbGFjZSB0byBzdGFydCBpcyBhbG1vc3QgYWx3YXlzIHRvIGdhdGhlciB0b2dldGhlciB0aGUgY29sdW1ucyB0aGF0IGFyZSBub3QgdmFyaWFibGVzLiBMZXTigJlzIGhhdmUgYSBsb29rIGF0IHdoYXQgd2XigJl2ZSBnb3Q6DQoNCiAtIEl0IGxvb2tzIGxpa2UgYGNvdW50cnlgLCBgaXNvMmAsIGFuZCBgaXNvM2AgYXJlIHRocmVlIHZhcmlhYmxlcyB0aGF0IHJlZHVuZGFudGx5IHNwZWNpZnkgdGhlIGNvdW50cnkuDQogLSBgeWVhcmAgaXMgY2xlYXJseSBhbHNvIGEgdmFyaWFibGUuDQogLSBXZSBkb27igJl0IGtub3cgd2hhdCBhbGwgdGhlIG90aGVyIGNvbHVtbnMgYXJlIHlldCwgYnV0IGdpdmVuIHRoZSBzdHJ1Y3R1cmUgaW4gdGhlIHZhcmlhYmxlIG5hbWVzIChlLmcuIGBuZXdfc3BfbTAxNGAsIGBuZXdfZXBfbTAxNGAsIGBuZXdfZXBfZjAxNGApIHRoZXNlIGFyZSBsaWtlbHkgdG8gYmUgdmFsdWVzLCBub3QgdmFyaWFibGVzLg0KDQpTbyB3ZSBuZWVkIHRvIHBpdm90IHRvZ2V0aGVyIGFsbCB0aGUgY29sdW1ucyBmcm9tIGBuZXdfc3BfbTAxNGAgdG8gYG5ld3JlbF9mNjVgLiBXZSBkb27igJl0IGtub3cgd2hhdCB0aG9zZSB2YWx1ZXMgcmVwcmVzZW50IHlldCwgc28gd2XigJlsbCBnaXZlIHRoZW0gdGhlIGdlbmVyaWMgbmFtZSBgImtleSJgLiBXZSBrbm93IHRoZSBjZWxscyByZXByZXNlbnQgdGhlIGNvdW50IG9mIGNhc2VzLCBzbyB3ZeKAmWxsIHVzZSB0aGUgdmFyaWFibGUgYGNhc2VzYC4gVGhlcmUgYXJlIGEgbG90IG9mIG1pc3NpbmcgdmFsdWVzIGluIHRoZSBjdXJyZW50IHJlcHJlc2VudGF0aW9uLCBzbyBmb3Igbm93IHdl4oCZbGwgdXNlIGB2YWx1ZXNfZHJvcF9uYSA9IFRSVUVgIGp1c3Qgc28gd2UgY2FuIGZvY3VzIG9uIHRoZSB2YWx1ZXMgdGhhdCBhcmUgcHJlc2VudC4NCg0KYGBge3J9DQp3aG8xIDwtIHdobyAlPiUgDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gbmV3X3NwX20wMTQ6bmV3cmVsX2Y2NSwgDQogICAgbmFtZXNfdG8gPSAia2V5IiwgDQogICAgdmFsdWVzX3RvID0gImNhc2VzIiwgDQogICAgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFDQogICkNCndobzENCmBgYA0KDQpXZSBjYW4gZ2V0IHNvbWUgaGludCBvZiB0aGUgc3RydWN0dXJlIG9mIHRoZSB2YWx1ZXMgaW4gdGhlIG5ldyBrZXkgY29sdW1uIGJ5IGNvdW50aW5nIHRoZW06DQoNCmBgYHtyfQ0Kd2hvMSAlPiUgDQogIGNvdW50KGtleSkNCmBgYA0KDQpZb3UgbWlnaHQgYmUgYWJsZSB0byBwYXJzZSB0aGlzIG91dCBieSB5b3Vyc2VsZiB3aXRoIGEgbGl0dGxlIHRob3VnaHQgYW5kIHNvbWUgZXhwZXJpbWVudGF0aW9uLCBidXQgbHVja2lseSB3ZSBoYXZlIHRoZSBkYXRhIGRpY3Rpb25hcnkgaGFuZHkuIEl0IHRlbGxzIHVzOg0KDQoxLiBUaGUgZmlyc3QgdGhyZWUgbGV0dGVycyBvZiBlYWNoIGNvbHVtbiBkZW5vdGUgd2hldGhlciB0aGUgY29sdW1uIGNvbnRhaW5zIG5ldyBvciBvbGQgY2FzZXMgb2YgVEIuIEluIHRoaXMgZGF0YXNldCwgZWFjaCBjb2x1bW4gY29udGFpbnMgbmV3IGNhc2VzLg0KDQoyLiBUaGUgbmV4dCB0d28gbGV0dGVycyBkZXNjcmliZSB0aGUgdHlwZSBvZiBUQjoNCg0KICAgIC0gYHJlbGAgc3RhbmRzIGZvciBjYXNlcyBvZiByZWxhcHNlDQogICAgLSBgZXBgIHN0YW5kcyBmb3IgY2FzZXMgb2YgZXh0cmFwdWxtb25hcnkgVEINCiAgICAtIGBzbmAgc3RhbmRzIGZvciBjYXNlcyBvZiBwdWxtb25hcnkgVEIgdGhhdCBjb3VsZCBub3QgYmUgZGlhZ25vc2VkIGJ5IGEgcHVsbW9uYXJ5IHNtZWFyIChzbWVhciBuZWdhdGl2ZSkNCiAgICAtIGBzcGAgc3RhbmRzIGZvciBjYXNlcyBvZiBwdWxtb25hcnkgVEIgdGhhdCBjb3VsZCBiZSBkaWFnbm9zZWQgYmUgYSBwdWxtb25hcnkgc21lYXIgKHNtZWFyIHBvc2l0aXZlKQ0KDQozLiBUaGUgc2l4dGggbGV0dGVyIGdpdmVzIHRoZSBzZXggb2YgVEIgcGF0aWVudHMuIFRoZSBkYXRhc2V0IGdyb3VwcyBjYXNlcyBieSBtYWxlcyAoYG1gKSBhbmQgZmVtYWxlcyAoYGZgKS4NCg0KNC4gVGhlIHJlbWFpbmluZyBudW1iZXJzIGdpdmVzIHRoZSBhZ2UgZ3JvdXAuIFRoZSBkYXRhc2V0IGdyb3VwcyBjYXNlcyBpbnRvIHNldmVuIGFnZSBncm91cHM6DQoNCiAgICAtIGAwMTRgID0gMCDigJMgMTQgeWVhcnMgb2xkDQogICAgLSBgMTUyNGAgPSAxNSDigJMgMjQgeWVhcnMgb2xkDQogICAgLSBgMjUzNGAgPSAyNSDigJMgMzQgeWVhcnMgb2xkDQogICAgLSBgMzU0NGAgPSAzNSDigJMgNDQgeWVhcnMgb2xkDQogICAgLSBgNDU1NGAgPSA0NSDigJMgNTQgeWVhcnMgb2xkDQogICAgLSBgNTU2NGAgPSA1NSDigJMgNjQgeWVhcnMgb2xkDQogICAgLSBgNjVgID0gNjUgb3Igb2xkZXINCg0KV2UgbmVlZCB0byBtYWtlIGEgbWlub3IgZml4IHRvIHRoZSBmb3JtYXQgb2YgdGhlIGNvbHVtbiBuYW1lczogdW5mb3J0dW5hdGVseSB0aGUgbmFtZXMgYXJlIHNsaWdodGx5IGluY29uc2lzdGVudCBiZWNhdXNlIGluc3RlYWQgb2YgYG5ld19yZWxgIHdlIGhhdmUgYG5ld3JlbGAgKGl04oCZcyBoYXJkIHRvIHNwb3QgdGhpcyBoZXJlIGJ1dCBpZiB5b3UgZG9u4oCZdCBmaXggaXQgd2XigJlsbCBnZXQgZXJyb3JzIGluIHN1YnNlcXVlbnQgc3RlcHMpLiBZb3XigJlsbCBsZWFybiBhYm91dCBgc3RyX3JlcGxhY2UoKWAgaW4gc3RyaW5ncywgYnV0IHRoZSBiYXNpYyBpZGVhIGlzIHByZXR0eSBzaW1wbGU6IHJlcGxhY2UgdGhlIGNoYXJhY3RlcnMg4oCcbmV3cmVs4oCdIHdpdGgg4oCcbmV3X3JlbOKAnS4gVGhpcyBtYWtlcyBhbGwgdmFyaWFibGUgbmFtZXMgY29uc2lzdGVudC4NCg0KYGBge3J9DQp3aG8yIDwtIHdobzEgJT4lIA0KICBtdXRhdGUobmFtZXNfZnJvbSA9IHN0cmluZ3I6OnN0cl9yZXBsYWNlKGtleSwgIm5ld3JlbCIsICJuZXdfcmVsIikpDQp3aG8yDQpgYGANCg0KV2UgY2FuIHNlcGFyYXRlIHRoZSB2YWx1ZXMgaW4gZWFjaCBjb2RlIHdpdGggdHdvIHBhc3NlcyBvZiBgc2VwYXJhdGUoKWAuIFRoZSBmaXJzdCBwYXNzIHdpbGwgc3BsaXQgdGhlIGNvZGVzIGF0IGVhY2ggdW5kZXJzY29yZS4NCg0KYGBge3J9DQp3aG8zIDwtIHdobzIgJT4lIA0KICBzZXBhcmF0ZShrZXksIGMoIm5ldyIsICJ0eXBlIiwgInNleGFnZSIpLCBzZXAgPSAiXyIpDQp3aG8zDQpgYGANCg0KVGhlbiB3ZSBtaWdodCBhcyB3ZWxsIGRyb3AgdGhlIGBuZXdgIGNvbHVtbiBiZWNhdXNlIGl04oCZcyBjb25zdGFudCBpbiB0aGlzIGRhdGFzZXQuIFdoaWxlIHdl4oCZcmUgZHJvcHBpbmcgY29sdW1ucywgbGV04oCZcyBhbHNvIGRyb3AgYGlzbzJgIGFuZCBgaXNvM2Agc2luY2UgdGhleeKAmXJlIHJlZHVuZGFudC4NCg0KYGBge3J9DQp3aG8zICU+JSANCiAgY291bnQobmV3KQ0Kd2hvNCA8LSB3aG8zICU+JSANCiAgc2VsZWN0KC1uZXcsIC1pc28yLCAtaXNvMykNCmBgYA0KDQpOZXh0IHdl4oCZbGwgc2VwYXJhdGUgYHNleGFnZWAgaW50byBgc2V4YCBhbmQgYGFnZWAgYnkgc3BsaXR0aW5nIGFmdGVyIHRoZSBmaXJzdCBjaGFyYWN0ZXI6DQoNCmBgYHtyfQ0Kd2hvNSA8LSB3aG80ICU+JSANCiAgc2VwYXJhdGUoc2V4YWdlLCBjKCJzZXgiLCAiYWdlIiksIHNlcCA9IDEpDQp3aG81DQpgYGANCg0KVGhlIGB3aG9gIGRhdGFzZXQgaXMgbm93IHRpZHkhDQoNCknigJl2ZSBzaG93biB5b3UgdGhlIGNvZGUgYSBwaWVjZSBhdCBhIHRpbWUsIGFzc2lnbmluZyBlYWNoIGludGVyaW0gcmVzdWx0IHRvIGEgbmV3IHZhcmlhYmxlLiBUaGlzIHR5cGljYWxseSBpc27igJl0IGhvdyB5b3XigJlkIHdvcmsgaW50ZXJhY3RpdmVseS4gSW5zdGVhZCwgeW914oCZZCBncmFkdWFsbHkgYnVpbGQgdXAgYSBjb21wbGV4IHBpcGU6DQoNCmBgYHtyfQ0Kd2hvICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IG5ld19zcF9tMDE0Om5ld3JlbF9mNjUsIA0KICAgIG5hbWVzX3RvID0gImtleSIsIA0KICAgIHZhbHVlc190byA9ICJjYXNlcyIsIA0KICAgIHZhbHVlc19kcm9wX25hID0gVFJVRQ0KICApICU+JSANCiAgbXV0YXRlKA0KICAgIGtleSA9IHN0cmluZ3I6OnN0cl9yZXBsYWNlKGtleSwgIm5ld3JlbCIsICJuZXdfcmVsIikNCiAgKSAlPiUNCiAgc2VwYXJhdGUoa2V5LCBjKCJuZXciLCAidmFyIiwgInNleGFnZSIpKSAlPiUgDQogIHNlbGVjdCgtbmV3LCAtaXNvMiwgLWlzbzMpICU+JSANCiAgc2VwYXJhdGUoc2V4YWdlLCBjKCJzZXgiLCAiYWdlIiksIHNlcCA9IDEpDQpgYGANCg0KIyMgRXhlcmNpc2VzDQoNCiMjIyAxLiBJbiB0aGlzIGNhc2Ugc3R1ZHksIEkgc2V0IGB2YWx1ZXNfZHJvcF9uYSA9IFRSVUVgIGp1c3QgdG8gbWFrZSBpdCBlYXNpZXIgdG8gY2hlY2sgdGhhdCB3ZSBoYWQgdGhlIGNvcnJlY3QgdmFsdWVzLiBJcyB0aGlzIHJlYXNvbmFibGU/IFRoaW5rIGFib3V0IGhvdyBtaXNzaW5nIHZhbHVlcyBhcmUgcmVwcmVzZW50ZWQgaW4gdGhpcyBkYXRhc2V0LiBBcmUgdGhlcmUgaW1wbGljaXQgbWlzc2luZyB2YWx1ZXM/IFdoYXTigJlzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gYW4gTkEgYW5kIHplcm8/DQoNClRoZSByZWFzb25hYmxlbmVzcyBvZiB1c2luZyBgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFYCBkZXBlbmRzIG9uIGhvdyBtaXNzaW5nIHZhbHVlcyBhcmUgcmVwcmVzZW50ZWQgaW4gdGhpcyBkYXRhc2V0LiBUaGUgbWFpbiBjb25jZXJuIGlzIHdoZXRoZXIgYSBtaXNzaW5nIHZhbHVlIG1lYW5zIHRoYXQgdGhlcmUgd2VyZSBubyBjYXNlcyBvZiBUQiBvciB3aGV0aGVyIGl0IG1lYW5zIHRoYXQgdGhlIFdITyBkb2VzIG5vdCBoYXZlIGRhdGEgb24gdGhlIG51bWJlciBvZiBUQiBjYXNlcy4gSGVyZSBhcmUgc29tZSB0aGluZ3Mgd2Ugc2hvdWxkIGxvb2sgZm9yIHRvIGhlbHAgZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGVzZSBjYXNlcy4NCg0KIC0gSWYgdGhlcmUgYXJlIG5vIDAgdmFsdWVzIGluIHRoZSBkYXRhLCB0aGVuIG1pc3NpbmcgdmFsdWVzIG1heSBiZSB1c2VkIHRvIGluZGljYXRlIG5vIGNhc2VzLg0KDQogLSBJZiB0aGVyZSBhcmUgYm90aCBleHBsaWNpdCBhbmQgaW1wbGljaXQgbWlzc2luZyB2YWx1ZXMsIHRoZW4gaXQgc3VnZ2VzdHMgdGhhdCBtaXNzaW5nIHZhbHVlcyBhcmUgYmVpbmcgdXNlZCBkaWZmZXJlbnRseS4gSW4gdGhhdCBjYXNlLCBpdCBpcyBsaWtlbHkgdGhhdCBleHBsaWNpdCBtaXNzaW5nIHZhbHVlcyB3b3VsZCBtZWFuIG5vIGNhc2VzLCBhbmQgaW1wbGljaXQgbWlzc2luZyB2YWx1ZXMgd291bGQgbWVhbiBubyBkYXRhIG9uIHRoZSBudW1iZXIgb2YgY2FzZXMuDQoNCkZpcnN0LCBJ4oCZbGwgY2hlY2sgZm9yIHRoZSBwcmVzZW5jZSBvZiB6ZXJvcyBpbiB0aGUgZGF0YS4NCg0KYGBge3J9DQp3aG8xICU+JQ0KICBmaWx0ZXIoY2FzZXMgPT0gMCkgJT4lDQogIG5yb3coKQ0KYGBgDQoNClRoZXJlIGFyZSB6ZXJvcyBpbiB0aGUgZGF0YSwgc28gaXQgYXBwZWFycyB0aGF0IGNhc2VzIG9mIHplcm8gVEIgYXJlIGV4cGxpY2l0bHkgaW5kaWNhdGVkLCBhbmQgdGhlIHZhbHVlIG9mTkEgaXMgdXNlZCB0byBpbmRpY2F0ZSBtaXNzaW5nIGRhdGEuDQoNClNlY29uZCwgSSBzaG91bGQgY2hlY2sgd2hldGhlciBhbGwgdmFsdWVzIGZvciBhIChjb3VudHJ5LCB5ZWFyKSBhcmUgbWlzc2luZyBvciB3aGV0aGVyIGl0IGlzIHBvc3NpYmxlIGZvciBvbmx5IHNvbWUgY29sdW1ucyB0byBiZSBtaXNzaW5nLg0KDQpgYGB7cn0NCnBpdm90X2xvbmdlcih3aG8sIG5ld19zcF9tMDE0Om5ld3JlbF9mNjUsIG5hbWVzX3RvID0gImtleSIsIHZhbHVlc190byA9ICJjYXNlcyIpICU+JQ0KICBncm91cF9ieShjb3VudHJ5LCB5ZWFyKSAlPiUNCiAgbXV0YXRlKHByb3BfbWlzc2luZyA9IHN1bShpcy5uYShjYXNlcykpIC8gbigpKSAlPiUNCiAgZmlsdGVyKHByb3BfbWlzc2luZyA+IDAsIHByb3BfbWlzc2luZyA8IDEpDQpgYGANCg0KRnJvbSB0aGUgcmVzdWx0cyBhYm92ZSwgaXQgbG9va3MgbGlrZSBpdCBpcyBwb3NzaWJsZSBmb3IgYSAoY291bnRyeSwgeWVhcikgcm93IHRvIGNvbnRhaW4gc29tZSwgYnV0IG5vdCBhbGwsIG1pc3NpbmcgdmFsdWVzIGluIGl0cyBjb2x1bW5zLg0KDQpGaW5hbGx5LCBJIHdpbGwgY2hlY2sgZm9yIGltcGxpY2l0IG1pc3NpbmcgdmFsdWVzLiBJbXBsaWNpdCBtaXNzaW5nIHZhbHVlcyBhcmUgKGB5ZWFyYCwgYGNvdW50cnlgKSBjb21iaW5hdGlvbnMgdGhhdCBkbyBub3QgYXBwZWFyIGluIHRoZSBkYXRhLg0KDQpgYGB7cn0NCm5yb3cod2hvKQ0KDQp3aG8gJT4lDQogIGNvbXBsZXRlKGNvdW50cnksIHllYXIpICU+JQ0KICBucm93KCkNCmBgYA0KDQpTaW5jZSB0aGUgbnVtYmVyIG9mIGNvbXBsZXRlIGNhc2VzIG9mIChgY291bnRyeWAsIGB5ZWFyYCkgaXMgZ3JlYXRlciB0aGFuIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB3aG8sIHRoZXJlIGFyZSBzb21lIGltcGxpY2l0IHZhbHVlcy4gQnV0IHRoYXQgZG9lc27igJl0IHRlbGwgdXMgd2hhdCB0aG9zZSBpbXBsaWNpdCBtaXNzaW5nIHZhbHVlcyBhcmUuIFRvIGRvIHRoaXMsIEkgd2lsbCB1c2UgdGhlIGBhbnRpX2pvaW4oKWAgZnVuY3Rpb24gaW50cm9kdWNlZCBpbiB0aGUgbGF0ZXIgUmVsYXRpb25hbCBEYXRhIGxlY3R1cmUuDQoNCmBgYHtyfQ0KYW50aV9qb2luKGNvbXBsZXRlKHdobywgY291bnRyeSwgeWVhciksIHdobywgYnkgPSBjKCJjb3VudHJ5IiwgInllYXIiKSkgJT4lDQogIHNlbGVjdChjb3VudHJ5LCB5ZWFyKSAlPiUNCiAgZ3JvdXBfYnkoY291bnRyeSkgJT4lDQogICMgc28gSSBjYW4gbWFrZSBiZXR0ZXIgc2Vuc2Ugb2YgdGhlIHllYXJzDQogIHN1bW1hcmlzZShtaW5feWVhciA9IG1pbih5ZWFyKSwgbWF4X3llYXIgPSBtYXgoeWVhcikpDQpgYGANCg0KQWxsIG9mIHRoZXNlIHJlZmVyIHRvIChgY291bnRyeWAsIGB5ZWFyYCkgY29tYmluYXRpb25zIGZvciB5ZWFycyBwcmlvciB0byB0aGUgZXhpc3RlbmNlIG9mIHRoZSBjb3VudHJ5LiBGb3IgZXhhbXBsZSwgVGltb3ItTGVzdGUgYWNoaWV2ZWQgaW5kZXBlbmRlbmNlIGluIDIwMDIsIHNvIHllYXJzIHByaW9yIHRvIHRoYXQgYXJlIG5vdCBpbmNsdWRlZCBpbiB0aGUgZGF0YS4NCg0KVG8gc3VtbWFyaXplOg0KDQogLSBgMGAgaXMgdXNlZCB0byByZXByZXNlbnQgbm8gY2FzZXMgb2YgVEIuDQogLSBFeHBsaWNpdCBtaXNzaW5nIHZhbHVlcyAoYE5BYHMpIGFyZSB1c2VkIHRvIHJlcHJlc2VudCBtaXNzaW5nIGRhdGEgZm9yIChgY291bnRyeWAsIGB5ZWFyYCkgY29tYmluYXRpb25zIGluIHdoaWNoIHRoZSBjb3VudHJ5IGV4aXN0ZWQgaW4gdGhhdCB5ZWFyLg0KIC0gSW1wbGljaXQgbWlzc2luZyB2YWx1ZXMgYXJlIHVzZWQgdG8gcmVwcmVzZW50IG1pc3NpbmcgZGF0YSBiZWNhdXNlIGEgY291bnRyeSBkaWQgbm90IGV4aXN0IGluIHRoYXQgeWVhci4NCiANCiMjIyAyLiBXaGF0IGhhcHBlbnMgaWYgeW91IG5lZ2xlY3QgdGhlIGBtdXRhdGUoKWAgc3RlcD8gKGBtdXRhdGUoa2V5ID0gc3RyX3JlcGxhY2Uoa2V5LCAibmV3cmVsIiwgIm5ld19yZWwiKWApDQoNClRoZSBgc2VwYXJhdGUoKWAgZnVuY3Rpb24gZW1pdHMgdGhlIHdhcm5pbmcg4oCcdG9vIGZldyB2YWx1ZXPigJ0uIElmIHdlIGNoZWNrIHRoZSByb3dzIGZvciBrZXlzIGJlZ2lubmluZyB3aXRoIGAibmV3cmVsXyJgLCB3ZSBzZWUgdGhhdCBgc2V4YWdlYCBpcyBtaXNzaW5nLCBhbmQgYHR5cGUgPSBtMDE0YC4NCg0KYGBge3J9DQp3aG8zYSA8LSB3aG8xICU+JQ0KICBzZXBhcmF0ZShrZXksIGMoIm5ldyIsICJ0eXBlIiwgInNleGFnZSIpLCBzZXAgPSAiXyIpDQpmaWx0ZXIod2hvM2EsIG5ldyA9PSAibmV3cmVsIikgJT4lIGhlYWQoKQ0KYGBgDQoNCiMjIyAzLiBJIGNsYWltZWQgdGhhdCBgaXNvMmAgYW5kIGBpc28zYCB3ZXJlIHJlZHVuZGFudCB3aXRoIGNvdW50cnkuIENvbmZpcm0gdGhpcyBjbGFpbS4NCg0KSWYgYGlzbzJgIGFuZCBgaXNvM2AgYXJlIHJlZHVuZGFudCB3aXRoIGNvdW50cnksIHRoZW4sIHdpdGhpbiBlYWNoIGNvdW50cnksIHRoZXJlIHNob3VsZCBvbmx5IGJlIG9uZSBkaXN0aW5jdCBjb21iaW5hdGlvbiBvZiBgaXNvMmAgYW5kIGBpc28zYCB2YWx1ZXMsIHdoaWNoIGlzIHRoZSBjYXNlLg0KDQpgYGB7cn0NCnNlbGVjdCh3aG8zLCBjb3VudHJ5LCBpc28yLCBpc28zKSAlPiUNCiAgZGlzdGluY3QoKSAlPiUNCiAgZ3JvdXBfYnkoY291bnRyeSkgJT4lDQogIGZpbHRlcihuKCkgPiAxKQ0KYGBgDQoNClRoaXMgbWFrZXMgc2Vuc2UsIHNpbmNlIGBpc28yYCBhbmQgYGlzbzNgIGNvbnRhaW4gdGhlIDItIGFuZCAzLWxldHRlciBjb3VudHJ5IGFiYnJldmlhdGlvbnMgZm9yIHRoZSBjb3VudHJ5LiBUaGUgYGlzbzJgIHZhcmlhYmxlIGNvbnRhaW5zIGVhY2ggY291bnRyeeKAmXMgW0lTTyAzMTY2IGFscGhhLTJdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT18zMTY2LTFfYWxwaGEtMiksIGFuZCB0aGUgYGlzbzNgIHZhcmlhYmxlIGNvbnRhaW5zIGVhY2ggY291bnRyeeKAmXMgW0lTTyAzMTY2IGFscGhhLTNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT18zMTY2LTFfYWxwaGEtMykgYWJicmV2aWF0aW9uLiBZb3UgbWF5IHJlY29nbml6ZSB0aGUgSVNPIDMxNjYtMiBhYmJyZXZpYXRpb25zLCBzaW5jZSB0aGV5IGFyZSBhbG1vc3QgaWRlbnRpY2FsIHRvIGludGVybmV0IFtjb3VudHJ5LWNvZGUgdG9wIGxldmVsIGRvbWFpbnNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvdW50cnlfY29kZV90b3AtbGV2ZWxfZG9tYWluKSwgc3VjaCBhcyBgLnVrYCAoVW5pdGVkIEtpbmdkb20pLCBgLmx5YCAoTGlieWEpLCBgLnR2YCAoVHV2YWx1KSwgYW5kIGAuaW9gIChCcml0aXNoIEluZGlhbiBPY2VhbiBUZXJyaXRvcnkpLg0KDQojIyMgNC4gRm9yIGVhY2ggY291bnRyeSwgeWVhciwgYW5kIHNleCBjb21wdXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgY2FzZXMgb2YgVEIuIE1ha2UgYW4gaW5mb3JtYXRpdmUgdmlzdWFsaXphdGlvbiBvZiB0aGUgZGF0YS4NCg0KYGBge3J9DQp3aG81ICU+JQ0KICBncm91cF9ieShjb3VudHJ5LCB5ZWFyLCBzZXgpICU+JQ0KICBmaWx0ZXIoeWVhciA+IDE5OTUpICU+JQ0KICBzdW1tYXJpc2UoY2FzZXMgPSBzdW0oY2FzZXMpKSAlPiUNCiAgdW5pdGUoY291bnRyeV9zZXgsIGNvdW50cnksIHNleCwgcmVtb3ZlID0gRkFMU0UpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gY2FzZXMsIGdyb3VwID0gY291bnRyeV9zZXgsIGNvbG91ciA9IHNleCkpICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KDQpBIHNtYWxsIG11bHRpcGxlcyBwbG90IGZhY2V0aW5nIGJ5IGNvdW50cnkgaXMgZGlmZmljdWx0IGdpdmVuIHRoZSBudW1iZXIgb2YgY291bnRyaWVzLiBGb2N1c2luZyBvbiB0aG9zZSBjb3VudHJpZXMgd2l0aCB0aGUgbGFyZ2VzdCBjaGFuZ2VzIG9yIGFic29sdXRlIG1hZ25pdHVkZXMgYWZ0ZXIgcHJvdmlkaW5nIHRoZSBjb250ZXh0IGFib3ZlIGlzIGFub3RoZXIgb3B0aW9uLg==