Importing Messy Excel Data

Although your data may be in a spreadsheet or CSV file, it may not be in an ideal format. See this registration list:


Here we see a bunch of stuff before the data, the column “names” are unwieldy, there are several extra columns we don’t need, missing values, etc.

You can tackle many of these issues when you import the data. First, we construct the file name and verify it exists:

reg_list_fn <- "./data/SurveyResults_36100.xlsx"
file.exists(reg_list_fn)
[1] TRUE


To import Excel files, we’ll go to read_xlsx() from the readxl package. This function has a lot of options, so it’s worth reading the help page:

library(readxl)
reg_list_tbl <- read_xlsx(path = reg_list_fn, 
                         sheet = "Survey Output", 
                         skip = 3, 
                         col_names = c("user", "reg_date", "fname", "lname", 
                                       "email", "title", "org", 
                                       "cosponsor", "allergies"),
                         col_types = c("text", "text", "text", "text", "text", "text",
                                       "numeric", "text", "text"))

reg_list_tbl


Likewise, the readr package provides super-charged functions for importing and exporting rectangular data in text formats.


Take Home Messages

  • Data wrangling starts when you import

  • You’re not limited to just using read.csv()


dplyr methods

Load dplyr:

library(dplyr)


We can proactively prevent function name clashes with the conflicted package:

## Load the conflicted package
library(conflicted)

# Set conflict preferences
conflict_prefer("filter", "dplyr", quiet = TRUE)
conflict_prefer("count", "dplyr", quiet = TRUE)
conflict_prefer("select", "dplyr", quiet = TRUE)
conflict_prefer("arrange", "dplyr", quiet = TRUE)


We begin by looking at the Palmer Penguins data:

library(palmerpenguins)
head(penguins)


dplyr provides glimpse() which is an alternative way preview a tibble:

glimpse(penguins)
Rows: 344
Columns: 8
$ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Ad~
$ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgersen, Torgers~
$ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, 42.0, 37.8, 37.8, 41.1, 38.6, 34.6, 36.6, 38.7, 42.5, ~
$ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, 20.2, 17.1, 17.3, 17.6, 21.2, 21.1, 17.8, 19.0, 20.7, ~
$ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186, 180, 182, 191, 198, 185, 195, 197, 184, 194, 174, 18~
$ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, 4250, 3300, 3700, 3200, 3800, 4400, 3700, 3450, 4500, ~
$ sex               <fct> male, female, female, NA, female, male, female, male, NA, NA, NA, NA, female, male, male, female, female, ~
$ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007~


Maniuplating Columns

Subset Columns

Suppose we want to compare how bill length and flipper length look for each species. We could start by selecting just those columns:

penguins |> 
  select(species, bill_length_mm, flipper_length_mm) |> 
  head()


You can also rename columns with select() (dplyr also has a rename() function):

penguins |> 
  select(species, bill = bill_length_mm, flipper = flipper_length_mm) |> 
  head()


If you want to extract the values from a single column and get the results back as a vector, use pull() (similar to the $ operator):

penguins |> pull(species) |> table()

   Adelie Chinstrap    Gentoo 
      152        68       124 


Add/Modify Columns

To add a column, we can use mutate(). Let’s create a new column with the ratio of the bill to flipper length:

peng_billflip_tbl <- penguins |> 
  select(species, bill_length_mm, flipper_length_mm) |>
  mutate(bill_flip_ratio = bill_length_mm / flipper_length_mm)
  
head(peng_billflip_tbl)


mutate() can also be used to modify existing columns, for example we could round bill_flip_ratio to 3 decimal places.

peng_billflip_tbl <- peng_billflip_tbl |> 
  mutate(bill_flip_ratio = round(bill_flip_ratio, 3))

head(peng_billflip_tbl)


CHALLENGE #1

Add a column called bill_flip_sum which represents the sum of the bill and flipper length.

Hint: use mutate()

## Your answer here


Maniuplating Rows

Sort Rows

You can sort a table using arrange(), passing the column(s) you want to sort by. The default sort order is ascending, but you can change that to descending by wrapping the column name in desc().

Sort by flipper size (smallest to largest):

peng_billflip_tbl |> 
  arrange(flipper_length_mm) |> 
  head()


Largest to smallest:

peng_billflip_tbl |> 
  arrange(desc(flipper_length_mm)) |> 
  head()


Subset Rows

The two functions most commonly used to subset rows include filter() and slice().

  • use slice() to subset based on indices

  • use filter() to subset based on a logical expressions


Logical expressions you can use in filter() include:


Suppose we only wanted to work with observations of Gentoo penguins:

gentoo_billflip_tbl <- peng_billflip_tbl |> 
  filter(species == "Gentoo") 

glimpse(gentoo_billflip_tbl)
Rows: 124
Columns: 4
$ species           <fct> Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Ge~
$ bill_length_mm    <dbl> 46.1, 50.0, 48.7, 50.0, 47.6, 46.5, 45.4, 46.7, 43.3, 46.8, 40.9, 49.0, 45.5, 48.4, 45.8, 49.3, 42.0, 49.2~
$ flipper_length_mm <int> 211, 230, 210, 218, 215, 210, 211, 219, 209, 215, 214, 216, 214, 213, 210, 217, 210, 221, 209, 222, 218, 2~
$ bill_flip_ratio   <dbl> 0.218, 0.217, 0.232, 0.229, 0.221, 0.221, 0.215, 0.213, 0.207, 0.218, 0.191, 0.227, 0.213, 0.227, 0.218, 0~


If you want to filter on two criteria, add a second expression to filter():

peng_billflip_tbl |> 
  filter(species == "Gentoo", flipper_length_mm > 220) |> 
  select(species, flipper_length_mm) |> 
  glimpse()
Rows: 35
Columns: 2
$ species           <fct> Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Gentoo, Ge~
$ flipper_length_mm <int> 230, 221, 222, 222, 230, 225, 222, 225, 225, 224, 221, 231, 230, 229, 223, 221, 221, 230, 223, 221, 224, 2~


Suppose we wanted to identify the top 5 Gentoo observations with the largest bill_flip_ratio value. We can do that using a combination of arrange() and slice():

gentoo_billflip_tbl |> 
  arrange(desc(bill_flip_ratio)) |> 
  slice(1:5) 


We could also use top_n():

gentoo_billflip_tbl |> 
  top_n(5, bill_flip_ratio) |> 
  arrange(desc(bill_flip_ratio))


CHALLENGE #2

Write an expression that pulls out just the males on Torgersen Island. HOw many are there?

Tip: For challenge questions 2 & 3, mix and match the following functions:

select(), mutate(), pull(), arrange(), filter(), slice()

## Your answer here

Ans. 23


CHALLENGE #3

COmpute the ratio of body mass to bill length and add it as a new column. Return just the species, sex, and this ratio.

# Your answer here


End

That’s it! Remember to save your work to render a copy of your Notebook in HTML

LS0tDQp0aXRsZTogIkRhdGEgV3JhbmdsaW5nIDE6IEltcG9ydGluZyBNZXNzeSBEYXRhLCBNYW5pcHVsYXRpbmcgQ29sdW1ucyBhbmQgUm93cyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6IHllcw0KLS0tDQoNCmBgYHtjc3MgZWNobyA9IEZBTFNFfQ0KaDEsaDIsaDMge2ZvbnQtd2VpZ2h0OmJvbGQ7fQ0KaDEge2ZvbnQtc2l6ZToyNHB4O30NCmgyIHtmb250LXNpemU6MjBweDt9DQpoMyB7Zm9udC1zaXplOjE2cHg7fQ0KYGBgDQpcDQoNCiMgSW1wb3J0aW5nIE1lc3N5IEV4Y2VsIERhdGENCg0KQWx0aG91Z2ggeW91ciBkYXRhIG1heSBiZSBpbiBhIHNwcmVhZHNoZWV0IG9yIENTViBmaWxlLCBpdCBtYXkgbm90IGJlIGluIGFuIGlkZWFsIGZvcm1hdC4gU2VlIHRoaXMgcmVnaXN0cmF0aW9uIGxpc3Q6DQoNCiFbXSguL2ltYWdlcy9yZWdfbGlzdF9leGNlbF8xNTAweDYwMC5wbmcpDQoNClwNCg0KSGVyZSB3ZSBzZWUgYSBidW5jaCBvZiBzdHVmZiBiZWZvcmUgdGhlIGRhdGEsIHRoZSBjb2x1bW4gIm5hbWVzIiBhcmUgdW53aWVsZHksIHRoZXJlIGFyZSBzZXZlcmFsIGV4dHJhIGNvbHVtbnMgd2UgZG9uJ3QgbmVlZCwgbWlzc2luZyB2YWx1ZXMsIGV0Yy4NCg0KWW91IGNhbiB0YWNrbGUgbWFueSBvZiB0aGVzZSBpc3N1ZXMgd2hlbiB5b3UgaW1wb3J0IHRoZSBkYXRhLiBGaXJzdCwgd2UgY29uc3RydWN0IHRoZSBmaWxlIG5hbWUgYW5kIHZlcmlmeSBpdCBleGlzdHM6DQoNCmBgYHtyIGNodW5rMDF9DQpyZWdfbGlzdF9mbiA8LSAiLi9kYXRhL1N1cnZleVJlc3VsdHNfMzYxMDAueGxzeCINCmZpbGUuZXhpc3RzKHJlZ19saXN0X2ZuKQ0KYGBgDQoNClwNCg0KVG8gaW1wb3J0IEV4Y2VsIGZpbGVzLCB3ZSdsbCBnbyB0byBgcmVhZF94bHN4KClgIGZyb20gdGhlICpyZWFkeGwqIHBhY2thZ2UuIFRoaXMgZnVuY3Rpb24gaGFzIGEgbG90IG9mIG9wdGlvbnMsIHNvIGl0J3Mgd29ydGggcmVhZGluZyB0aGUgaGVscCBwYWdlOg0KDQpgYGB7ciBjaHVuazAyfQ0KbGlicmFyeShyZWFkeGwpDQpyZWdfbGlzdF90YmwgPC0gcmVhZF94bHN4KHBhdGggPSByZWdfbGlzdF9mbiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAiU3VydmV5IE91dHB1dCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHNraXAgPSAzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBjKCJ1c2VyIiwgInJlZ19kYXRlIiwgImZuYW1lIiwgImxuYW1lIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZW1haWwiLCAidGl0bGUiLCAib3JnIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29zcG9uc29yIiwgImFsbGVyZ2llcyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGMoInRleHQiLCAidGV4dCIsICJ0ZXh0IiwgInRleHQiLCAidGV4dCIsICJ0ZXh0IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJudW1lcmljIiwgInRleHQiLCAidGV4dCIpKQ0KDQpyZWdfbGlzdF90YmwNCmBgYA0KDQpcDQoNCkxpa2V3aXNlLCB0aGUgW3JlYWRyXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlIHByb3ZpZGVzIHN1cGVyLWNoYXJnZWQgZnVuY3Rpb25zIGZvciBpbXBvcnRpbmcgYW5kIGV4cG9ydGluZyByZWN0YW5ndWxhciBkYXRhIGluIHRleHQgZm9ybWF0cy4NCg0KXA0KDQoqKlRha2UgSG9tZSBNZXNzYWdlcyoqDQoNCi0gRGF0YSB3cmFuZ2xpbmcgc3RhcnRzIHdoZW4geW91IGltcG9ydA0KDQotIFlvdSdyZSBub3QgbGltaXRlZCB0byBqdXN0IHVzaW5nIGByZWFkLmNzdigpYA0KDQpcDQoNCiMgYGRwbHlyYCBtZXRob2RzDQoNCkxvYWQgZHBseXI6DQoNCmBgYHtyIGNodW5rMDN9DQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNClwNCg0KV2UgY2FuIHByb2FjdGl2ZWx5IHByZXZlbnQgZnVuY3Rpb24gbmFtZSBjbGFzaGVzIHdpdGggdGhlIGBjb25mbGljdGVkYCBwYWNrYWdlOg0KDQpgYGB7ciBjaHVuazA0fQ0KIyMgTG9hZCB0aGUgY29uZmxpY3RlZCBwYWNrYWdlDQpsaWJyYXJ5KGNvbmZsaWN0ZWQpDQoNCiMgU2V0IGNvbmZsaWN0IHByZWZlcmVuY2VzDQpjb25mbGljdF9wcmVmZXIoImZpbHRlciIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigiY291bnQiLCAiZHBseXIiLCBxdWlldCA9IFRSVUUpDQpjb25mbGljdF9wcmVmZXIoInNlbGVjdCIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmNvbmZsaWN0X3ByZWZlcigiYXJyYW5nZSIsICJkcGx5ciIsIHF1aWV0ID0gVFJVRSkNCmBgYA0KDQpcDQoNCldlIGJlZ2luIGJ5IGxvb2tpbmcgYXQgdGhlIFBhbG1lciBQZW5ndWlucyBkYXRhOg0KDQpgYGB7ciBjaHVuazA1fQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCmhlYWQocGVuZ3VpbnMpDQpgYGANCg0KXA0KDQpkcGx5ciBwcm92aWRlcyBgZ2xpbXBzZSgpYCB3aGljaCBpcyBhbiBhbHRlcm5hdGl2ZSB3YXkgcHJldmlldyBhIHRpYmJsZToNCg0KYGBge3IgY2h1bmswNn0NCmdsaW1wc2UocGVuZ3VpbnMpDQpgYGANCg0KXA0KDQojIE1hbml1cGxhdGluZyBDb2x1bW5zDQoNCiMjIFN1YnNldCBDb2x1bW5zDQoNClN1cHBvc2Ugd2Ugd2FudCB0byBjb21wYXJlIGhvdyBiaWxsIGxlbmd0aCBhbmQgZmxpcHBlciBsZW5ndGggbG9vayBmb3IgZWFjaCBzcGVjaWVzLiBXZSBjb3VsZCBzdGFydCBieSBzZWxlY3RpbmcganVzdCB0aG9zZSBjb2x1bW5zOg0KDQpgYGB7ciBjaHVuazA3fQ0KcGVuZ3VpbnMgfD4gDQogIHNlbGVjdChzcGVjaWVzLCBiaWxsX2xlbmd0aF9tbSwgZmxpcHBlcl9sZW5ndGhfbW0pIHw+IA0KICBoZWFkKCkNCmBgYA0KDQpcDQoNCllvdSBjYW4gYWxzbyAqKnJlbmFtZSoqIGNvbHVtbnMgd2l0aCBgc2VsZWN0KClgIChkcGx5ciBhbHNvIGhhcyBhIGByZW5hbWUoKWAgZnVuY3Rpb24pOg0KDQpgYGB7ciBjaHVuazA4fQ0KcGVuZ3VpbnMgfD4gDQogIHNlbGVjdChzcGVjaWVzLCBiaWxsID0gYmlsbF9sZW5ndGhfbW0sIGZsaXBwZXIgPSBmbGlwcGVyX2xlbmd0aF9tbSkgfD4gDQogIGhlYWQoKQ0KYGBgDQoNClwNCg0KSWYgeW91IHdhbnQgdG8gZXh0cmFjdCB0aGUgdmFsdWVzIGZyb20gYSBzaW5nbGUgY29sdW1uIGFuZCBnZXQgdGhlIHJlc3VsdHMgYmFjayBhcyBhIHZlY3RvciwgDQp1c2UgYHB1bGwoKWAgKHNpbWlsYXIgdG8gdGhlIGAkYCBvcGVyYXRvcik6DQoNCmBgYHtyIGNodW5rMDl9DQpwZW5ndWlucyB8PiBwdWxsKHNwZWNpZXMpIHw+IHRhYmxlKCkNCmBgYA0KDQpcDQoNCiMjIEFkZC9Nb2RpZnkgQ29sdW1ucw0KDQpUbyAqKmFkZCBhIGNvbHVtbioqLCB3ZSBjYW4gdXNlIGBtdXRhdGUoKWAuIExldCdzIGNyZWF0ZSBhIG5ldyBjb2x1bW4gd2l0aCB0aGUgcmF0aW8gb2YgdGhlIGJpbGwgdG8gZmxpcHBlciBsZW5ndGg6DQoNCmBgYHtyIGNodW5rMTB9DQpwZW5nX2JpbGxmbGlwX3RibCA8LSBwZW5ndWlucyB8PiANCiAgc2VsZWN0KHNwZWNpZXMsIGJpbGxfbGVuZ3RoX21tLCBmbGlwcGVyX2xlbmd0aF9tbSkgfD4NCiAgbXV0YXRlKGJpbGxfZmxpcF9yYXRpbyA9IGJpbGxfbGVuZ3RoX21tIC8gZmxpcHBlcl9sZW5ndGhfbW0pDQogIA0KaGVhZChwZW5nX2JpbGxmbGlwX3RibCkNCmBgYA0KDQpcDQoNCmBtdXRhdGUoKWAgY2FuIGFsc28gYmUgdXNlZCB0byBtb2RpZnkgZXhpc3RpbmcgY29sdW1ucywgZm9yIGV4YW1wbGUgd2UgY291bGQgcm91bmQgYmlsbF9mbGlwX3JhdGlvIHRvIDMgZGVjaW1hbCBwbGFjZXMuDQoNCmBgYHtyIGNodW5rMTF9DQpwZW5nX2JpbGxmbGlwX3RibCA8LSBwZW5nX2JpbGxmbGlwX3RibCB8PiANCiAgbXV0YXRlKGJpbGxfZmxpcF9yYXRpbyA9IHJvdW5kKGJpbGxfZmxpcF9yYXRpbywgMykpDQoNCmhlYWQocGVuZ19iaWxsZmxpcF90YmwpDQpgYGANCg0KXA0KDQoNCiMgQ0hBTExFTkdFICMxDQoNCkFkZCBhIGNvbHVtbiBjYWxsZWQgYGJpbGxfZmxpcF9zdW1gIHdoaWNoIHJlcHJlc2VudHMgdGhlIHN1bSBvZiB0aGUgYmlsbCBhbmQgZmxpcHBlciBsZW5ndGguDQoNCkhpbnQ6IHVzZSBgbXV0YXRlKClgDQoNCmBgYHtyIGNodW5rMTJ9DQojIyBZb3VyIGFuc3dlciBoZXJlDQoNCmBgYA0KDQpcDQoNCiMgTWFuaXVwbGF0aW5nIFJvd3MNCg0KIyMgU29ydCBSb3dzDQoNCllvdSBjYW4gKipzb3J0KiogYSB0YWJsZSB1c2luZyBgYXJyYW5nZSgpYCwgcGFzc2luZyB0aGUgY29sdW1uKHMpIHlvdSB3YW50IHRvIHNvcnQgYnkuIFRoZSBkZWZhdWx0IHNvcnQgb3JkZXIgaXMgYXNjZW5kaW5nLCBidXQgeW91IGNhbiBjaGFuZ2UgdGhhdCB0byBkZXNjZW5kaW5nIGJ5IHdyYXBwaW5nIHRoZSBjb2x1bW4gbmFtZSBpbiBgZGVzYygpYC4NCg0KU29ydCBieSBmbGlwcGVyIHNpemUgKHNtYWxsZXN0IHRvIGxhcmdlc3QpOg0KDQpgYGB7ciBjaHVuazEzfQ0KcGVuZ19iaWxsZmxpcF90YmwgfD4gDQogIGFycmFuZ2UoZmxpcHBlcl9sZW5ndGhfbW0pIHw+IA0KICBoZWFkKCkNCmBgYA0KDQpcDQoNCkxhcmdlc3QgdG8gc21hbGxlc3Q6DQoNCmBgYHtyIGNodW5rMTR9DQpwZW5nX2JpbGxmbGlwX3RibCB8PiANCiAgYXJyYW5nZShkZXNjKGZsaXBwZXJfbGVuZ3RoX21tKSkgfD4gDQogIGhlYWQoKQ0KYGBgDQoNClwNCg0KIyMgU3Vic2V0IFJvd3MNCg0KVGhlIHR3byBmdW5jdGlvbnMgbW9zdCBjb21tb25seSB1c2VkIHRvIHN1YnNldCByb3dzIGluY2x1ZGUgYGZpbHRlcigpYCBhbmQgYHNsaWNlKClgLg0KDQogIC0gdXNlIGBzbGljZSgpYCB0byBzdWJzZXQgYmFzZWQgb24gaW5kaWNlcw0KDQogIC0gdXNlIGBmaWx0ZXIoKWAgdG8gc3Vic2V0IGJhc2VkIG9uIGEgbG9naWNhbCBleHByZXNzaW9ucw0KDQpcDQoNCkxvZ2ljYWwgZXhwcmVzc2lvbnMgeW91IGNhbiB1c2UgaW4gYGZpbHRlcigpYCBpbmNsdWRlOg0KDQohW10oLi9pbWFnZXMvbG9naWNhbC1leHByZXNzaW9ucy03NDB4MjgweDI1Ni5wbmcpDQoNClwNCg0KU3VwcG9zZSB3ZSBvbmx5IHdhbnRlZCB0byB3b3JrIHdpdGggb2JzZXJ2YXRpb25zIG9mICpHZW50b28qIHBlbmd1aW5zOg0KDQpgYGB7ciBjaHVuazE1fQ0KZ2VudG9vX2JpbGxmbGlwX3RibCA8LSBwZW5nX2JpbGxmbGlwX3RibCB8PiANCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkdlbnRvbyIpIA0KDQpnbGltcHNlKGdlbnRvb19iaWxsZmxpcF90YmwpDQpgYGANCg0KXA0KDQpJZiB5b3Ugd2FudCB0byBmaWx0ZXIgb24gdHdvIGNyaXRlcmlhLCBhZGQgYSBzZWNvbmQgZXhwcmVzc2lvbiB0byBgZmlsdGVyKClgOg0KDQpgYGB7ciBjaHVuazE2fQ0KcGVuZ19iaWxsZmxpcF90YmwgfD4gDQogIGZpbHRlcihzcGVjaWVzID09ICJHZW50b28iLCBmbGlwcGVyX2xlbmd0aF9tbSA+IDIyMCkgfD4gDQogIHNlbGVjdChzcGVjaWVzLCBmbGlwcGVyX2xlbmd0aF9tbSkgfD4gDQogIGdsaW1wc2UoKQ0KYGBgDQoNClwNCg0KU3VwcG9zZSB3ZSB3YW50ZWQgdG8gaWRlbnRpZnkgdGhlIHRvcCA1IEdlbnRvbyBvYnNlcnZhdGlvbnMgd2l0aCB0aGUgbGFyZ2VzdCBiaWxsX2ZsaXBfcmF0aW8gdmFsdWUuIFdlIGNhbiBkbyB0aGF0IHVzaW5nIGEgY29tYmluYXRpb24gb2YgYGFycmFuZ2UoKWAgYW5kIGBzbGljZSgpYDoNCg0KYGBge3IgY2h1bmsxN30NCmdlbnRvb19iaWxsZmxpcF90YmwgfD4gDQogIGFycmFuZ2UoZGVzYyhiaWxsX2ZsaXBfcmF0aW8pKSB8PiANCiAgc2xpY2UoMTo1KSANCmBgYA0KDQpcDQoNCldlIGNvdWxkIGFsc28gdXNlIGB0b3BfbigpYDoNCg0KYGBge3IgY2h1bmsxOH0NCmdlbnRvb19iaWxsZmxpcF90YmwgfD4gDQogIHRvcF9uKDUsIGJpbGxfZmxpcF9yYXRpbykgfD4gDQogIGFycmFuZ2UoZGVzYyhiaWxsX2ZsaXBfcmF0aW8pKQ0KYGBgDQoNClwNCg0KIyBDSEFMTEVOR0UgIzINCg0KV3JpdGUgYW4gZXhwcmVzc2lvbiB0aGF0IHB1bGxzIG91dCBqdXN0IHRoZSBtYWxlcyBvbiBUb3JnZXJzZW4gSXNsYW5kLiBIT3cgbWFueSBhcmUgdGhlcmU/DQoNClRpcDogRm9yIGNoYWxsZW5nZSBxdWVzdGlvbnMgMiAmIDMsIG1peCBhbmQgbWF0Y2ggdGhlIGZvbGxvd2luZyBmdW5jdGlvbnM6DQoNCmBzZWxlY3QoKWAsIGBtdXRhdGUoKWAsIGBwdWxsKClgLCBgYXJyYW5nZSgpYCwgYGZpbHRlcigpYCwgYHNsaWNlKClgDQoNCmBgYHtyIGNodW5rMTl9DQojIyBZb3VyIGFuc3dlciBoZXJlDQoNCmBgYA0KDQpBbnMuIDIzDQoNClwNCg0KIyBDSEFMTEVOR0UgIzMNCg0KQ09tcHV0ZSB0aGUgcmF0aW8gb2YgYm9keSBtYXNzIHRvIGJpbGwgbGVuZ3RoIGFuZCBhZGQgaXQgYXMgYSBuZXcgY29sdW1uLiBSZXR1cm4ganVzdCB0aGUgc3BlY2llcywgc2V4LCBhbmQgdGhpcyByYXRpby4NCg0KYGBge3IgY2h1bmsyMH0NCiMgWW91ciBhbnN3ZXIgaGVyZQ0KDQpgYGANCg0KXA0KDQojIEVuZA0KDQpUaGF0J3MgaXQhIFJlbWVtYmVyIHRvIHNhdmUgeW91ciB3b3JrIHRvIHJlbmRlciBhIGNvcHkgb2YgeW91ciBOb3RlYm9vayBpbiBIVE1MDQoNCg0KDQo=