if (require(plotly) == FALSE){
  install.packages(plotly)
}
if (require(r02pro) == FALSE){
  install.packages(r02pro)
}
if (require(forcats) == FALSE) {
  install.packages(forcats)
}
library(dplyr)
library(tidyr)
library(readr)
library(gapminder)
library(r02pro)
library(ggplot2)
library(forcats)

This draws on students’ self-study of the DataCamp course Reshaping Data with tidyr. (https://campus.datacamp.com/courses/reshaping-data-with-tidyr) and explains what you might have found difficult in this course. Some issues do not relate to tidyr but to plotting or other aspects of the homework.

We will rename the data frame in each step to keep track of the changes, although normally you would probably do it all in one pipe and in a last step you would assign the whole pipe to a variable name.

Plotting countries on the y-axis and using forcats.

This is inspired by https://campus.datacamp.com/courses/reshaping-data-with-tidyr/from-wide-to-long-and-back?ex=3

The obesity data used in this task come from the r02pro library.

Install this library if you don’t have it installed (you probably don’t):

if (require(r02pro) == FALSE) {
  install.packages("r02pro")
  }
library(r02pro)

You need the gm dataset (snapshot of one year, but dichotomized sex).

glimpse(gm)
Rows: 65,531
Columns: 33
$ country                         <chr> "Albania", "Albania", "Albania", "…
$ year                            <dbl> 1999, 2000, 2001, 2002, 2003, 2004…
$ smoking_female                  <dbl> 4.0, NA, 4.0, NA, NA, 4.0, 29.2, N…
$ smoking_male                    <dbl> NA, NA, 40.5, NA, NA, 40.5, NA, NA…
$ lungcancer_newcases_female      <dbl> 11.1, 10.8, 11.2, 11.5, 11.2, 11.0…
$ lungcancer_newcases_male        <dbl> 46.1, 43.8, 44.1, 45.2, 44.8, 42.8…
$ owid_edu_idx                    <dbl> 58.7, 58.0, 60.0, 60.0, 60.7, 60.7…
$ food_supply                     <dbl> 2730, 2800, 2860, 2770, 2790, 2870…
$ average_daily_income            <dbl> 5.96, 6.30, 6.81, 7.22, 7.39, 7.70…
$ sanitation                      <dbl> 89.5, 90.0, 90.6, 91.2, 91.7, 92.3…
$ child_mortality                 <dbl> 25.90, 24.50, 23.10, 21.80, 20.40,…
$ income_per_person               <dbl> 5890, 6440, 6750, 7150, 7580, 8040…
$ HDI                             <dbl> 0.671, 0.678, 0.684, 0.691, 0.696,…
$ alcohol_male                    <dbl> 10.600000, 11.499156, 11.510882, 1…
$ alcohol_female                  <dbl> 2.5000000, 2.7400844, 2.7389118, 2…
$ livercancer_newcases_male       <dbl> 17.80, 17.10, 16.40, 15.70, 15.10,…
$ livercancer_newcases_female     <dbl> 8.28, 7.66, 7.10, 6.58, 6.10, 5.66…
$ mortality_male                  <dbl> 23.5, 19.6, 20.2, 21.2, 20.6, 25.2…
$ mortality_female                <dbl> 12.9, 11.0, 12.3, 12.6, 12.0, 14.5…
$ cholesterol_fat_in_blood_male   <dbl> 4.90, 4.90, 4.91, 4.91, 4.91, 4.90…
$ cholesterol_fat_in_blood_female <dbl> 4.92, 4.92, 4.92, 4.92, 4.91, 4.90…
$ continent                       <chr> "Europe", "Europe", "Europe", "Eur…
$ region                          <chr> "Southern Europe", "Southern Europ…
$ population                      <dbl> 3130.0, 3130.0, 3130.0, 3120.0, 31…
$ life_expectancy                 <dbl> 75.4, 76.0, 75.9, 75.6, 75.8, 76.2…
$ sugar                           <dbl> 63.4, 66.6, 70.7, 67.0, 55.0, 58.6…
$ BMI_female                      <dbl> 25.1, 25.2, 25.3, 25.3, 25.4, 25.5…
$ BMI_female_group                <chr> "pre_obesity", "pre_obesity", "pre…
$ BMI_male                        <dbl> 25.6, 25.7, 25.8, 25.9, 26.0, 26.1…
$ BMI_male_group                  <chr> "pre_obesity", "pre_obesity", "pre…
$ health_spending                 <dbl> 6.35, 6.04, 6.28, 6.16, 6.88, 6.84…
$ GDP_per_capita                  <dbl> 1.96, 2.14, 2.25, 2.38, 2.52, 2.68…
$ HDI_category                    <chr> "medium", "medium", "medium", "med…

Select columns with dplyr

Create the obesity_df data frame similar to that one in DataCamp’s exercise by selecting the columns BMI_female_group, BMI_male, year, country, and region. Remove rows with NAs in the BMI_female, BMI_male columns.
The BMI_... columns show the average BMI of the population in the given country. (The DataCamp data was showing something else, namely the percentage of obese people in the country) Impute the value “Other” for NA in region.

obesity_df <- gm %>% 
  select(c(BMI_female, BMI_male, year, country, region)) %>%  drop_na(starts_with("BMI")) %>%
  replace_na(list(region = "Other"))

Check the regions (sort distinct values alphabetically, just print it)

obesity_df %>% 
  distinct(region) %>% 
  arrange(region)

Filter data for the year that has data for most countries

If there are ties, pick the most recent year. You can do it in several ways. One of them is to create a new aggregated data frame, find out which year takes most rows and then use this number to filter the original data frame. You can do it without retyping the year value by pulling the value out of the data frame in the last step. Another way would be to mutate this information on the current data frame, filter it and remove the mutated column again. Either way, call the resulting data frame obesity_snapshot.

(maxdf <- obesity_df %>% 
  group_by(year) %>%
  count() %>%
  ungroup() %>%
  slice_max(n = 1,
            order_by = n,
            with_ties = TRUE) %>%
  slice_max(n = 1, order_by = year, 
            with_ties = TRUE) %>% # or together like right below
 #  slice_max(n = 1, order_by = tibble(n,year)) #%>%
   pull(year)
)
[1] 2007
obesity_snapshot <- obesity_df %>% 
  filter(year == maxdf)
  
obesity_snapshot <- obesity_df %>% 
  group_by(year) %>% 
  add_count() %>% 
  ungroup() %>% 
  slice_max(n = 1, 
            order_by = tibble(n, year), 
            with_ties = TRUE) %>% 
  select(-n)

Compute rowwise mean BMI for both sexes

Rowwise operations are cumbersome with some functions and mean is one of them. This is how you work around that problem without any additional skill.

obesity_snapshot <- obesity_snapshot %>% 
  mutate(BMI_both = (BMI_female + BMI_male)/2) 
obesity_snapshot %>% slice_head(n = 5)

This worked well because + is a vectorized operation, unlike sum.

When you have to use a non-vectorized function on individual rows, you can help yourself out with rowwise(). This would work wonderfully with sum, for instance:

obesity_snapshot %>% 
  rowwise() %>% 
  mutate(sum_BMI = sum(BMI_male, BMI_female)) %>%
  ungroup() %>% 
  select(matches("BMI")) %>%
  slice_head(n = 5)

But mean takes only one argument. So you have to combine the two columns into one vector, and this construct then operates for each row.

obesity_snapshot %>% 
  rowwise() %>% 
mutate(BMI_both = mean(c(BMI_male, BMI_female))) %>% 
  ungroup() %>% slice_head(n = 5)
NA

Modify the order of categorical values in ggplot2

By default, ggplot2 orders categorical values alphabetically. You often want to sort them according to the values in a numeric column. ggplot2 treats all columns with categorical variables as factors. Technically, you need to reorder the factor levels, while plotting (that is, inside ggplot2 or other plotting code).

For this, you need the forcats library (part of tidyverse). This library generally enables you to rename or reorder factor levels. Among many other functions, forcats has a dedicated function for ggplot2 that reorders values of a data frame column according to another data frame column. It is called fct_reorder and is used like this:

obesity_snapshot %>% 
  ggplot() +
  geom_point(aes(x = BMI_both, 
                 y = fct_reorder(country, BMI_both)
                 )) 

This is obviously very illegible; nevertheless you can see that the countries are not sorted alphabetically but according to the BMI_both variable.

Display just selected ticks

We want just those countries that are close to the quartiles

selected_1q <- obesity_snapshot %>% 
  slice_min(BMI_male, prop = 0.25) %>% 
  slice_max(BMI_male, n = 1, with_ties = FALSE) %>% pull(country)
selected_2q <- obesity_snapshot %>%
  slice_min(BMI_male, prop = 0.5) %>% 
  slice_max(BMI_male, n = 1, with_ties = FALSE) %>% pull(country)
selected <- 
obesity_snapshot %>% 
  ggplot() +
  geom_point(aes(x = BMI_both, 
                 y = fct_reorder(country, BMI_both)
                 )) +
  scale_y_discrete(breaks = c(selected_1q, selected_2q))
selected

To display the sexes separately, put the sexes together in one column with tidyr

Use tidyr::pivot_longer with the names_prefix argument to directly transform the column names “BMI_male” and “BMI_female” to values “male” and “female”. Call this column gender. Also, call the column where the numeric BMI values appear, the BMI column. Relocate the columns so that the Store this data frame to a variable called obesity_long.


  obesity_long <- obesity_snapshot %>% 
  pivot_longer(c("BMI_female", "BMI_male"), 
               names_prefix = "BMI_",
               names_to = "gender", 
               values_to = "BMI")


obesity_long %>% slice_head(n = 6) %>% select(c(country, gender, matches("BMI")))
LS0tDQp0aXRsZTogImB0aWR5cmAsIGBkcGx5cmAsIGFuZCBgZ2dwbG90MmAgaW4gYWN0aW9uIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KYGBge3J9DQppZiAocmVxdWlyZShwbG90bHkpID09IEZBTFNFKXsNCiAgaW5zdGFsbC5wYWNrYWdlcyhwbG90bHkpDQp9DQppZiAocmVxdWlyZShyMDJwcm8pID09IEZBTFNFKXsNCiAgaW5zdGFsbC5wYWNrYWdlcyhyMDJwcm8pDQp9DQppZiAocmVxdWlyZShmb3JjYXRzKSA9PSBGQUxTRSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKGZvcmNhdHMpDQp9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGdhcG1pbmRlcikNCmxpYnJhcnkocjAycHJvKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShmb3JjYXRzKQ0KYGBgDQoNCg0KVGhpcyBkcmF3cyBvbiBzdHVkZW50cycgc2VsZi1zdHVkeSBvZiB0aGUgRGF0YUNhbXAgY291cnNlIFJlc2hhcGluZyBEYXRhIHdpdGggYHRpZHlyYC4gIChodHRwczovL2NhbXB1cy5kYXRhY2FtcC5jb20vY291cnNlcy9yZXNoYXBpbmctZGF0YS13aXRoLXRpZHlyKSBhbmQgZXhwbGFpbnMgd2hhdCB5b3UgbWlnaHQgaGF2ZSBmb3VuZCBkaWZmaWN1bHQgaW4gdGhpcyBjb3Vyc2UuIFNvbWUgaXNzdWVzIGRvIG5vdCByZWxhdGUgdG8gYHRpZHlyYCBidXQgdG8gcGxvdHRpbmcgb3Igb3RoZXIgYXNwZWN0cyBvZiB0aGUgaG9tZXdvcmsuIA0KDQpXZSB3aWxsIHJlbmFtZSB0aGUgZGF0YSBmcmFtZSBpbiBlYWNoIHN0ZXAgdG8ga2VlcCB0cmFjayBvZiB0aGUgY2hhbmdlcywgYWx0aG91Z2ggbm9ybWFsbHkgeW91IHdvdWxkIHByb2JhYmx5IGRvIGl0IGFsbCBpbiBvbmUgcGlwZSBhbmQgaW4gYSBsYXN0IHN0ZXAgeW91IHdvdWxkIGFzc2lnbiB0aGUgd2hvbGUgcGlwZSB0byBhIHZhcmlhYmxlIG5hbWUuIA0KDQojIFBsb3R0aW5nIGNvdW50cmllcyBvbiB0aGUgeS1heGlzIGFuZCB1c2luZyBgZm9yY2F0c2AuIA0KVGhpcyBpcyBpbnNwaXJlZCBieSBodHRwczovL2NhbXB1cy5kYXRhY2FtcC5jb20vY291cnNlcy9yZXNoYXBpbmctZGF0YS13aXRoLXRpZHlyL2Zyb20td2lkZS10by1sb25nLWFuZC1iYWNrP2V4PTMNCg0KVGhlIG9iZXNpdHkgZGF0YSB1c2VkIGluIHRoaXMgdGFzayBjb21lIGZyb20gdGhlIGByMDJwcm9gIGxpYnJhcnkuIA0KDQpJbnN0YWxsIHRoaXMgbGlicmFyeSBpZiB5b3UgZG9uJ3QgaGF2ZSBpdCBpbnN0YWxsZWQgKHlvdSBwcm9iYWJseSBkb24ndCk6DQoNCmBgYHtyfQ0KaWYgKHJlcXVpcmUocjAycHJvKSA9PSBGQUxTRSkgew0KICBpbnN0YWxsLnBhY2thZ2VzKCJyMDJwcm8iKQ0KICB9DQpsaWJyYXJ5KHIwMnBybykNCmBgYA0KDQpZb3UgbmVlZCB0aGUgYGdtYCBkYXRhc2V0IChzbmFwc2hvdCBvZiBvbmUgeWVhciwgYnV0IGRpY2hvdG9taXplZCBzZXgpLiANCg0KYGBge3J9DQpnbGltcHNlKGdtKQ0KYGBgDQojIyMgU2VsZWN0IGNvbHVtbnMgd2l0aCBgZHBseXJgDQpDcmVhdGUgdGhlIGBvYmVzaXR5X2RmYCBkYXRhIGZyYW1lIHNpbWlsYXIgdG8gdGhhdCBvbmUgaW4gRGF0YUNhbXAncyBleGVyY2lzZSBieSBzZWxlY3RpbmcgdGhlIGNvbHVtbnMgDQpgQk1JX2ZlbWFsZV9ncm91cGAsIGBCTUlfbWFsZWAsIGB5ZWFyYCwgYGNvdW50cnlgLCBhbmQgYHJlZ2lvbmAuICBSZW1vdmUgcm93cyB3aXRoIGBOQWBzIGluIHRoZSBgQk1JX2ZlbWFsZWAsIGBCTUlfbWFsZWAgY29sdW1ucy4gIA0KVGhlIGBCTUlfLi4uYCBjb2x1bW5zIHNob3cgdGhlIGF2ZXJhZ2UgQk1JIG9mIHRoZSBwb3B1bGF0aW9uIGluIHRoZSBnaXZlbiBjb3VudHJ5LiAoVGhlIERhdGFDYW1wIGRhdGEgd2FzIHNob3dpbmcgc29tZXRoaW5nIGVsc2UsIG5hbWVseSB0aGUgcGVyY2VudGFnZSBvZiBvYmVzZSBwZW9wbGUgaW4gdGhlIGNvdW50cnkpDQpJbXB1dGUgdGhlIHZhbHVlICJPdGhlciIgZm9yIGBOQWAgaW4gYHJlZ2lvbmAuDQoNCmBgYHtyfQ0Kb2Jlc2l0eV9kZiA8LSBnbSAlPiUgDQogIHNlbGVjdChjKEJNSV9mZW1hbGUsIEJNSV9tYWxlLCB5ZWFyLCBjb3VudHJ5LCByZWdpb24pKSAlPiUgIGRyb3BfbmEoc3RhcnRzX3dpdGgoIkJNSSIpKSAlPiUNCiAgcmVwbGFjZV9uYShsaXN0KHJlZ2lvbiA9ICJPdGhlciIpKQ0KYGBgDQoNCkNoZWNrIHRoZSByZWdpb25zIChzb3J0IGRpc3RpbmN0IHZhbHVlcyBhbHBoYWJldGljYWxseSwganVzdCBwcmludCBpdCkNCmBgYHtyfQ0Kb2Jlc2l0eV9kZiAlPiUgDQogIGRpc3RpbmN0KHJlZ2lvbikgJT4lIA0KICBhcnJhbmdlKHJlZ2lvbikNCmBgYA0KDQoNCiMjIyBGaWx0ZXIgZGF0YSBmb3IgdGhlIHllYXIgdGhhdCBoYXMgZGF0YSBmb3IgbW9zdCBjb3VudHJpZXMNCg0KSWYgdGhlcmUgYXJlIHRpZXMsIHBpY2sgdGhlIG1vc3QgcmVjZW50IHllYXIuIA0KWW91IGNhbiBkbyBpdCBpbiBzZXZlcmFsIHdheXMuIE9uZSBvZiB0aGVtIGlzIHRvIGNyZWF0ZSBhIG5ldyBhZ2dyZWdhdGVkIGRhdGEgZnJhbWUsIGZpbmQgb3V0IHdoaWNoIHllYXIgdGFrZXMgbW9zdCByb3dzIGFuZCB0aGVuIHVzZSB0aGlzIG51bWJlciB0byBmaWx0ZXIgdGhlIG9yaWdpbmFsIGRhdGEgZnJhbWUuIFlvdSBjYW4gZG8gaXQgd2l0aG91dCByZXR5cGluZyB0aGUgeWVhciB2YWx1ZSBieSBwdWxsaW5nIHRoZSB2YWx1ZSBvdXQgb2YgdGhlIGRhdGEgZnJhbWUgaW4gdGhlIGxhc3Qgc3RlcC4NCkFub3RoZXIgd2F5IHdvdWxkIGJlIHRvIG11dGF0ZSB0aGlzIGluZm9ybWF0aW9uIG9uIHRoZSBjdXJyZW50IGRhdGEgZnJhbWUsIGZpbHRlciBpdCBhbmQgcmVtb3ZlIHRoZSBtdXRhdGVkIGNvbHVtbiBhZ2Fpbi4gDQpFaXRoZXIgd2F5LCBjYWxsIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSBgb2Jlc2l0eV9zbmFwc2hvdGAuIA0KDQpgYGB7cn0NCihtYXhkZiA8LSBvYmVzaXR5X2RmICU+JSANCiAgZ3JvdXBfYnkoeWVhcikgJT4lDQogIGNvdW50KCkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgc2xpY2VfbWF4KG4gPSAxLA0KICAgICAgICAgICAgb3JkZXJfYnkgPSBuLA0KICAgICAgICAgICAgd2l0aF90aWVzID0gVFJVRSkgJT4lDQogIHNsaWNlX21heChuID0gMSwgb3JkZXJfYnkgPSB5ZWFyLCANCiAgICAgICAgICAgIHdpdGhfdGllcyA9IFRSVUUpICU+JSAjIG9yIHRvZ2V0aGVyIGxpa2UgcmlnaHQgYmVsb3cNCiAjICBzbGljZV9tYXgobiA9IDEsIG9yZGVyX2J5ID0gdGliYmxlKG4seWVhcikpICMlPiUNCiAgIHB1bGwoeWVhcikNCikNCg0Kb2Jlc2l0eV9zbmFwc2hvdCA8LSBvYmVzaXR5X2RmICU+JSANCiAgZmlsdGVyKHllYXIgPT0gbWF4ZGYpDQogIA0KYGBgDQoNCg0KYGBge3J9DQpvYmVzaXR5X3NuYXBzaG90IDwtIG9iZXNpdHlfZGYgJT4lIA0KICBncm91cF9ieSh5ZWFyKSAlPiUgDQogIGFkZF9jb3VudCgpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgc2xpY2VfbWF4KG4gPSAxLCANCiAgICAgICAgICAgIG9yZGVyX2J5ID0gdGliYmxlKG4sIHllYXIpLCANCiAgICAgICAgICAgIHdpdGhfdGllcyA9IFRSVUUpICU+JSANCiAgc2VsZWN0KC1uKQ0KYGBgDQoNCg0KIyMjIENvbXB1dGUgcm93d2lzZSBtZWFuIEJNSSBmb3IgYm90aCBzZXhlcw0KDQpSb3d3aXNlIG9wZXJhdGlvbnMgYXJlIGN1bWJlcnNvbWUgd2l0aCBzb21lIGZ1bmN0aW9ucyBhbmQgYG1lYW5gIGlzIG9uZSBvZiB0aGVtLiANClRoaXMgaXMgaG93IHlvdSB3b3JrIGFyb3VuZCB0aGF0IHByb2JsZW0gd2l0aG91dCBhbnkgYWRkaXRpb25hbCBza2lsbC4gIA0KDQpgYGB7cn0NCm9iZXNpdHlfc25hcHNob3QgPC0gb2Jlc2l0eV9zbmFwc2hvdCAlPiUgDQogIG11dGF0ZShCTUlfYm90aCA9IChCTUlfZmVtYWxlICsgQk1JX21hbGUpLzIpIA0Kb2Jlc2l0eV9zbmFwc2hvdCAlPiUgc2xpY2VfaGVhZChuID0gNSkNCmBgYA0KDQpUaGlzIHdvcmtlZCB3ZWxsIGJlY2F1c2UgYCtgIGlzIGEgdmVjdG9yaXplZCBvcGVyYXRpb24sIHVubGlrZSBgc3VtYC4gDQoNCldoZW4geW91IGhhdmUgdG8gdXNlIGEgbm9uLXZlY3Rvcml6ZWQgZnVuY3Rpb24gb24gaW5kaXZpZHVhbCByb3dzLCB5b3UgY2FuIGhlbHAgeW91cnNlbGYgb3V0IHdpdGggYHJvd3dpc2UoKWAuIA0KVGhpcyB3b3VsZCB3b3JrIHdvbmRlcmZ1bGx5IHdpdGggYHN1bWAsIGZvciBpbnN0YW5jZToNCg0KYGBge3J9DQpvYmVzaXR5X3NuYXBzaG90ICU+JSANCiAgcm93d2lzZSgpICU+JSANCiAgbXV0YXRlKHN1bV9CTUkgPSBzdW0oQk1JX21hbGUsIEJNSV9mZW1hbGUpKSAlPiUNCiAgdW5ncm91cCgpICU+JSANCiAgc2VsZWN0KG1hdGNoZXMoIkJNSSIpKSAlPiUNCiAgc2xpY2VfaGVhZChuID0gNSkNCmBgYA0KQnV0IGBtZWFuYCB0YWtlcyBvbmx5IG9uZSBhcmd1bWVudC4gU28geW91IGhhdmUgdG8gY29tYmluZSB0aGUgdHdvIGNvbHVtbnMgaW50byBvbmUgdmVjdG9yLCBhbmQgdGhpcyBjb25zdHJ1Y3QgdGhlbiBvcGVyYXRlcyBmb3IgZWFjaCByb3cuDQoNCmBgYHtyfQ0Kb2Jlc2l0eV9zbmFwc2hvdCAlPiUgDQogIHJvd3dpc2UoKSAlPiUgDQptdXRhdGUoQk1JX2JvdGggPSBtZWFuKGMoQk1JX21hbGUsIEJNSV9mZW1hbGUpKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIHNsaWNlX2hlYWQobiA9IDUpDQoNCmBgYA0KDQojIyMgTW9kaWZ5IHRoZSBvcmRlciBvZiBjYXRlZ29yaWNhbCB2YWx1ZXMgaW4gYGdncGxvdDJgDQoNCkJ5IGRlZmF1bHQsIGBnZ3Bsb3QyYCBvcmRlcnMgY2F0ZWdvcmljYWwgdmFsdWVzIGFscGhhYmV0aWNhbGx5LiBZb3Ugb2Z0ZW4gd2FudCB0byBzb3J0IHRoZW0gYWNjb3JkaW5nIHRvIHRoZSB2YWx1ZXMgaW4gYSBudW1lcmljIGNvbHVtbi4gYGdncGxvdDJgIHRyZWF0cyBhbGwgY29sdW1ucyB3aXRoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcyBmYWN0b3JzLiBUZWNobmljYWxseSwgeW91IG5lZWQgdG8gcmVvcmRlciB0aGUgZmFjdG9yICpsZXZlbHMqLCB3aGlsZSBwbG90dGluZyAodGhhdCBpcywgaW5zaWRlIGBnZ3Bsb3QyYCBvciBvdGhlciBwbG90dGluZyBjb2RlKS4gDQoNCkZvciB0aGlzLCB5b3UgbmVlZCB0aGUgYGZvcmNhdHNgIGxpYnJhcnkgKHBhcnQgb2YgYHRpZHl2ZXJzZWApLiBUaGlzIGxpYnJhcnkgZ2VuZXJhbGx5IGVuYWJsZXMgeW91IHRvIHJlbmFtZSBvciByZW9yZGVyIGZhY3RvciBsZXZlbHMuIEFtb25nIG1hbnkgb3RoZXIgZnVuY3Rpb25zLCBgZm9yY2F0c2AgaGFzIGEgZGVkaWNhdGVkIGZ1bmN0aW9uIGZvciBgZ2dwbG90MmAgdGhhdCByZW9yZGVycyB2YWx1ZXMgb2YgYSBkYXRhIGZyYW1lIGNvbHVtbiBhY2NvcmRpbmcgdG8gYW5vdGhlciBkYXRhIGZyYW1lIGNvbHVtbi4gSXQgaXMgY2FsbGVkICBgZmN0X3Jlb3JkZXJgIGFuZCBpcyB1c2VkIGxpa2UgdGhpczogIA0KDQpgYGB7ciBmaWcuaGVpZ2h0ID0gMTUsIGZpZy53aWR0aD0xNX0NCm9iZXNpdHlfc25hcHNob3QgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBCTUlfYm90aCwgDQogICAgICAgICAgICAgICAgIHkgPSBmY3RfcmVvcmRlcihjb3VudHJ5LCBCTUlfYm90aCkNCiAgICAgICAgICAgICAgICAgKSkgDQpgYGANCiBUaGlzIGlzIG9idmlvdXNseSB2ZXJ5IGlsbGVnaWJsZTsgbmV2ZXJ0aGVsZXNzIHlvdSBjYW4gc2VlIHRoYXQgdGhlIGNvdW50cmllcyBhcmUgbm90IHNvcnRlZCBhbHBoYWJldGljYWxseSBidXQgYWNjb3JkaW5nIHRvIHRoZSBgQk1JX2JvdGhgIHZhcmlhYmxlLiAgDQoNCiMjIyBEaXNwbGF5IGp1c3Qgc2VsZWN0ZWQgdGlja3MgDQpXZSB3YW50IGp1c3QgdGhvc2UgY291bnRyaWVzIHRoYXQgYXJlIGNsb3NlIHRvIHRoZSBxdWFydGlsZXMgDQoNCmBgYHtyfQ0Kc2VsZWN0ZWRfMXEgPC0gb2Jlc2l0eV9zbmFwc2hvdCAlPiUgDQogIHNsaWNlX21pbihCTUlfbWFsZSwgcHJvcCA9IDAuMjUpICU+JSANCiAgc2xpY2VfbWF4KEJNSV9tYWxlLCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpICU+JSBwdWxsKGNvdW50cnkpDQpzZWxlY3RlZF8ycSA8LSBvYmVzaXR5X3NuYXBzaG90ICU+JQ0KICBzbGljZV9taW4oQk1JX21hbGUsIHByb3AgPSAwLjUpICU+JSANCiAgc2xpY2VfbWF4KEJNSV9tYWxlLCBuID0gMSwgd2l0aF90aWVzID0gRkFMU0UpICU+JSBwdWxsKGNvdW50cnkpDQoNCmBgYA0KDQoNCmBgYHtyfQ0Kc2VsZWN0ZWQgPC0gDQpvYmVzaXR5X3NuYXBzaG90ICU+JSANCiAgZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gQk1JX2JvdGgsIA0KICAgICAgICAgICAgICAgICB5ID0gZmN0X3Jlb3JkZXIoY291bnRyeSwgQk1JX2JvdGgpDQogICAgICAgICAgICAgICAgICkpICsNCiAgc2NhbGVfeV9kaXNjcmV0ZShicmVha3MgPSBjKHNlbGVjdGVkXzFxLCBzZWxlY3RlZF8ycSkpDQpzZWxlY3RlZA0KYGBgDQoNCg0KDQojIyMgVG8gZGlzcGxheSB0aGUgc2V4ZXMgc2VwYXJhdGVseSwgcHV0IHRoZSBzZXhlcyB0b2dldGhlciBpbiBvbmUgY29sdW1uIHdpdGggYHRpZHlyYA0KDQpVc2UgYHRpZHlyOjpwaXZvdF9sb25nZXJgIHdpdGggdGhlIGBuYW1lc19wcmVmaXhgIGFyZ3VtZW50IHRvIGRpcmVjdGx5IHRyYW5zZm9ybSB0aGUgY29sdW1uIG5hbWVzICJCTUlfbWFsZSIgYW5kICJCTUlfZmVtYWxlIiB0byB2YWx1ZXMgIm1hbGUiIGFuZCAiZmVtYWxlIi4gQ2FsbCB0aGlzIGNvbHVtbiBgZ2VuZGVyYC4gQWxzbywgY2FsbCB0aGUgY29sdW1uIHdoZXJlIHRoZSBudW1lcmljIEJNSSB2YWx1ZXMgYXBwZWFyLCB0aGUgYEJNSWAgY29sdW1uLiANClJlbG9jYXRlIHRoZSBjb2x1bW5zIHNvIHRoYXQgdGhlIA0KU3RvcmUgdGhpcyBkYXRhIGZyYW1lIHRvIGEgdmFyaWFibGUgY2FsbGVkIGBvYmVzaXR5X2xvbmdgLiANCg0KDQpgYGB7cn0NCg0KICBvYmVzaXR5X2xvbmcgPC0gb2Jlc2l0eV9zbmFwc2hvdCAlPiUgDQogIHBpdm90X2xvbmdlcihjKCJCTUlfZmVtYWxlIiwgIkJNSV9tYWxlIiksIA0KICAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gIkJNSV8iLA0KICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiZ2VuZGVyIiwgDQogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiQk1JIikNCg0KDQpvYmVzaXR5X2xvbmcgJT4lIHNsaWNlX2hlYWQobiA9IDYpICU+JSBzZWxlY3QoYyhjb3VudHJ5LCBnZW5kZXIsIG1hdGNoZXMoIkJNSSIpKSkNCmBgYA0KDQoNCmBgYHtyfQ0Kb2Jlc2l0eV9wbG90IDwtIG9iZXNpdHlfbG9uZyAlPiUgDQogZ2dwbG90KGFlcyh4ID0gQk1JLCB5ID0gZmN0X3Jlb3JkZXIoLmYgPSBjb3VudHJ5LCBCTUkpLCBjb2xvciA9IGdlbmRlciwgDQogICAgICAgICAgICB0ZXh0ID0gY291bnRyeSkpICsgDQogIGdlb21fcG9pbnQoKSANCmxpYnJhcnkocGxvdGx5KQ0KZ2dwbG90bHkob2Jlc2l0eV9wbG90KQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K