Introduction

In this session, you will learn the basic grammar of data manipulation, some best-practice advices. Since data manipulation always follows a purpose and requires some understanding of the data at hand, we will also have a first glance data exploration and visualization. However, we will her only cover the very basics and skip most of the details. Again, you will have a dedicated sessions lateron.

In this session, you will learn:

  • How to do basic variable filtering, selection, and manipulation
  • How to create various types of data summarization
  • How to also apply these actions on grouped data
  • How to join data from different sources
  • How to reshape (pivot) your data

From my experience, this covers ca. 80% of common data manipulation tasks. Sound like fun? Lets get started!

The 5 core verbs of data-manipulation

So, let’s zoom in the dplyr functionality and syntax for a moment, before we illustrate it at the following data-manipulation tasks.

At it’s core are 5 central verbs of data/manipulation, which are

  1. filter() picks cases based on their values.
  2. select() picks variables based on their names.
  3. arrange() changes the ordering of the rows.
  4. mutate() adds new variables that are functions of existing variables
  5. summarise() reduces multiple values down to a single summary.

These all combine naturally with group_by() which allows you to perform any operation “by group”. You can learn more about them in vignette("dplyr"). As well as these single-table verbs, dplyr also provides a variety of two-table verbs (joins and mutations), which you can learn about in vignette("two-table").

The dataset

We will explore now a bit with the starwars dataset, which comes together with dplyr. First, let’s get an intuition what’s in it. head() shows us the first 10 rows.

glimpse() gives us an overview over all variable types.

Rows: 87
Columns: 14
$ name       <chr> "Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia Organa", "Owen Lars", "Beru Whitesun lars", "R5-D4", "Biggs Darklighter", "Obi-Wan Kenobi"…
$ height     <int> 172, 167, 96, 202, 150, 178, 165, 97, 183, 182, 188, 180, 228, 180, 173, 175, 170, 180, 66, 170, 183, 200, 190, 177, 175, 180, 150, NA, 88, 160, 19…
$ mass       <dbl> 77.0, 75.0, 32.0, 136.0, 49.0, 120.0, 75.0, 32.0, 84.0, 77.0, 84.0, NA, 112.0, 80.0, 74.0, 1358.0, 77.0, 110.0, 17.0, 75.0, 78.2, 140.0, 113.0, 79.…
$ hair_color <chr> "blond", NA, NA, "none", "brown", "brown, grey", "brown", NA, "black", "auburn, white", "blond", "auburn, grey", "brown", "brown", NA, NA, "brown",…
$ skin_color <chr> "fair", "gold", "white, blue", "white", "light", "light", "light", "white, red", "light", "fair", "fair", "fair", "unknown", "fair", "green", "gree…
$ eye_color  <chr> "blue", "yellow", "red", "yellow", "brown", "blue", "blue", "red", "brown", "blue-gray", "blue", "blue", "blue", "brown", "black", "orange", "hazel…
$ birth_year <dbl> 19.0, 112.0, 33.0, 41.9, 19.0, 52.0, 47.0, NA, 24.0, 57.0, 41.9, 64.0, 200.0, 29.0, 44.0, 600.0, 21.0, NA, 896.0, 82.0, 31.5, 15.0, 53.0, 31.0, 37.…
$ sex        <chr> "male", "none", "none", "male", "female", "male", "female", "none", "male", "male", "male", "male", "male", "male", "male", "hermaphroditic", "male…
$ gender     <chr> "masculine", "masculine", "masculine", "masculine", "feminine", "masculine", "feminine", "masculine", "masculine", "masculine", "masculine", "mascu…
$ homeworld  <chr> "Tatooine", "Tatooine", "Naboo", "Tatooine", "Alderaan", "Tatooine", "Tatooine", "Tatooine", "Tatooine", "Stewjon", "Tatooine", "Eriadu", "Kashyyyk…
$ species    <chr> "Human", "Droid", "Droid", "Human", "Human", "Human", "Human", "Droid", "Human", "Human", "Human", "Human", "Wookiee", "Human", "Rodian", "Hutt", "…
$ films      <list> [<"The Empire Strikes Back", "Revenge of the Sith", "Return of the Jedi", "A New Hope", "The Force Awakens">, <"The Empire Strikes Back", "Attack …
$ vehicles   <list> [<"Snowspeeder", "Imperial Speeder Bike">, <>, <>, <>, "Imperial Speeder Bike", <>, <>, <>, <>, "Tribubble bongo", <"Zephyr-G swoop bike", "XJ-6 a…
$ starships  <list> [<"X-wing", "Imperial shuttle">, <>, <>, "TIE Advanced x1", <>, <>, <>, <>, "X-wing", <"Jedi starfighter", "Trade Federation cruiser", "Naboo star…

1: filter()

The verb filter() lets you subset a dataframe by rows (observations), meaning the output will filter for only rows which fulfill a certain condition.

Conditions can be combined with boolean operators (&, |)

2: select()

The verb select() lets you subset a dataframe by column (variable), meaning the output will only contain certain columns in the stated order

It can also be used to deselect columns with a leading -

Lastly, many of dplyr’s other conditions and helper functions can be applied to select or deselect columns. More on that later.

3: arrange()

The verb arrange() defines the way the rows of your dataframe are ordered

4: mutate()

The verb mutate() lets you manipulate existing variables or create new ones.

5: summarize()

The verb summarize() reduces your dataset to one observation, which is summarized according to a defined function.

(+1) Pivoting

By now, we worked with the traditional data structure, where every column corresponds to one variable. We also call that a **wide*“** data format. This is in most cases the most efficient and prefered way to keep your data. However, there are some cases, where another dataformat would be more convenient.

For example, for range of graphical as well as numerical summaries, it would be nice to have every observation and every variable to be mapped in a single row. This format is also calles long (or in R slang, tidy) data. Adittionally, some real life datasets (including a lot of WorldBank, Eurostats, OECD, ect.) data is usually available in a long format. Therefore, weare in need of easily switching between long and wide formats, to reshape your data (in Excel slang, people call that pivoting).

Lets take a look at a pivot_wider example

And now pivot_longer

(+2) Doing manipulation by group: group_by

Finally, by_group() is offers the perfect complement to the 5 basic verbs, since it allows to perform aqll the above mentioned tasks sepperate according to used-defined groupings in one or multiple categories. Lets look at some examples:

First of all, it works amazingly well with the summarize() verb, producing within group summaries

Note that it is adviced to finish always such operations with ungroup(). Otherwise, the still active grouping might at a later point lead to all kind of unforseen results.

Your turn

Some little exercises in between: Please solve Exercise 1 in the corresponding section on github

Two-table verbs

It’s rare that a data analysis involves only a single table of data. In practice, you’ll normally have many tables that contribute to an analysis, and you need flexible tools to combine them. In dplyr, there are three families of verbs that work with two tables at a time:

  1. Mutating joins, which add new variables to one table from matching rows in another.
  2. Filtering joins, which filter observations from one table based on whether or not they match an observation in the other table.
  3. Set operations, which combine the observations in the data sets as if they were set elements.

Among those, mutating joins are the by far most used ones, so we will briefly discuss them here, and cover other types later in the applied part. For questions right away, consult vignette(two-"two-table"). Mutating joins allow you to combine variables from multiple tables (merging data). We here assume our data is “tidy” (meaning: rows = observations, columns = variables).

Lets briefly create some example data:

There are 4 types of mutating join to combine variables from dataframe x and `y, which differ in their behaviour when a match is not found. We’ll illustrate each with a simple example:

  1. left_join(x, y) includes all observations in x, regardless of whether they match or not. This is the most commonly used join because it ensures that you don’t lose observations from your primary table.
  1. right_join(x, y) includes all observations in y. It’s equivalent to left_join(y, x), but the columns will be ordered differently.
  1. inner_join(x, y) only includes observations that match in both x and y.
  1. full_join() includes all observations from x and y.

The left, right and full joins are collectively know as outer joins. When a row doesn’t match in an outer join, the new variables are filled in with missing values.

While mutating joins are primarily used to add new variables, they can also generate new observations. If a match is not unique, a join will add all possible combinations (the Cartesian product) of the matching observations:

Note: In case of multiple matches in y, adittional rows will be added. Therefore, if that is not the behavior you want, use the distinct() function afterwards.

By default, joins are performed on the bases of all variables appearing in x and y. You can also specify one or multiple variables that should be matched by using the by argument. This can also be used to match on variables with different names in x and y.

Your turn

Some little exercises in between: Please solve Exercise 2 in the corresponding section on github

Endnotes

References

Suggestions for further study

Session info

R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.6

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] magrittr_1.5    forcats_0.5.0   stringr_1.4.0   dplyr_1.0.2     purrr_0.3.4     readr_1.3.1     tidyr_1.1.2     tibble_3.0.3    ggplot2_3.3.2   tidyverse_1.3.0
[11] knitr_1.29     

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.5       cellranger_1.1.0 pillar_1.4.6     compiler_4.0.2   dbplyr_1.4.4     tools_4.0.2      jsonlite_1.7.1   lubridate_1.7.9  lifecycle_0.2.0 
[10] gtable_0.3.0     pkgconfig_2.0.3  rlang_0.4.7      reprex_0.3.0     cli_2.0.2        DBI_1.1.0        rstudioapi_0.11  yaml_2.2.1       haven_2.3.1     
[19] xfun_0.17        withr_2.2.0      xml2_1.3.2       httr_1.4.2       fs_1.5.0         generics_0.0.2   vctrs_0.3.4      hms_0.5.3        grid_4.0.2      
[28] tidyselect_1.1.0 glue_1.4.2       R6_2.4.1         fansi_0.4.1      readxl_1.3.1     modelr_0.1.8     blob_1.2.1       backports_1.1.9  scales_1.1.1    
[37] ellipsis_0.3.1   rvest_0.3.6      assertthat_0.2.1 colorspace_1.4-1 utf8_1.1.4       stringi_1.5.3    munsell_0.5.0    broom_0.7.0      crayon_1.3.4    
LS0tCnRpdGxlOiAnSG93VG86IERhdGEgbWFuaXB1bGF0aW9uIGluIFInCmF1dGhvcjogIkRhbmllbCBTLiBIYWluIChkc2hAYnVzaW5lc3MuYWF1LmRrKSIKZGF0ZTogIlVwZGF0ZWQgYHIgZm9ybWF0KFN5cy50aW1lKCksICclQiAlZCwgJVknKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIHRoZW1lOiBmbGF0bHkKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KIyBLbml0ciBvcHRpb25zCiMjIyBHZW5lcmljIHByZWFtYmxlClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpICMgRm9yIGVuZ2xpc2ggbGFuZ3VhZ2UKb3B0aW9ucyhzY2lwZW4gPSA1KSAjIFRvIGRlYWN0aXZhdGUgYW5ub3lpbmcgc2NpZW50aWZpYyBudW1iZXIgbm90YXRpb24KCiMgcm0obGlzdD1scygpKTsgZ3JhcGhpY3Mub2ZmKCkgIyBnZXQgcmlkIG9mIGV2ZXJ5dGhpbmcgaW4gdGhlIHdvcmtzcGFjZQppZiAoIXJlcXVpcmUoImtuaXRyIikpIGluc3RhbGwucGFja2FnZXMoImtuaXRyIik7IGxpYnJhcnkoa25pdHIpICMgRm9yIGRpc3BsYXkgb2YgdGhlIG1hcmtkb3duCgojIyMgS25pdHIgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZz1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZT1GQUxTRSwKICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduPSJjZW50ZXIiCiAgICAgICAgICAgICAgICAgICAgICkKYGBgCgpgYGB7cn0KIyMjIExvYWQgcGFja2FnZXMKbGlicmFyeSh0aWR5dmVyc2UpICMgQ29sbGVjdGlvbiBvZiBhbGwgdGhlIGdvb2Qgc3R1ZmYgbGlrZSBkcGx5ciwgZ2dwbG90MiBlY3QuCmxpYnJhcnkobWFncml0dHIpICMgRm9yIGV4dHJhLXBpcGluZyBvcGVyYXRvcnMgKGVnLiAlPD4lKQpgYGAKCgojIEludHJvZHVjdGlvbgoKSW4gdGhpcyBzZXNzaW9uLCB5b3Ugd2lsbCBsZWFybiB0aGUgYmFzaWMgZ3JhbW1hciBvZiBkYXRhIG1hbmlwdWxhdGlvbiwgc29tZSBiZXN0LXByYWN0aWNlIGFkdmljZXMuIFNpbmNlIGRhdGEgbWFuaXB1bGF0aW9uIGFsd2F5cyBmb2xsb3dzIGEgcHVycG9zZSBhbmQgcmVxdWlyZXMgc29tZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkYXRhIGF0IGhhbmQsIHdlIHdpbGwgYWxzbyBoYXZlIGEgZmlyc3QgZ2xhbmNlIGRhdGEgZXhwbG9yYXRpb24gYW5kIHZpc3VhbGl6YXRpb24uIEhvd2V2ZXIsIHdlIHdpbGwgaGVyIG9ubHkgY292ZXIgdGhlIHZlcnkgYmFzaWNzIGFuZCBza2lwIG1vc3Qgb2YgdGhlIGRldGFpbHMuIEFnYWluLCB5b3Ugd2lsbCBoYXZlIGEgZGVkaWNhdGVkIHNlc3Npb25zIGxhdGVyb24uCgpJbiB0aGlzIHNlc3Npb24sIHlvdSB3aWxsIGxlYXJuOgoKKiBIb3cgdG8gZG8gYmFzaWMgdmFyaWFibGUgZmlsdGVyaW5nLCBzZWxlY3Rpb24sIGFuZCBtYW5pcHVsYXRpb24KKiBIb3cgdG8gY3JlYXRlIHZhcmlvdXMgdHlwZXMgb2YgZGF0YSBzdW1tYXJpemF0aW9uCiogSG93IHRvIGFsc28gYXBwbHkgdGhlc2UgYWN0aW9ucyBvbiBncm91cGVkIGRhdGEKKiBIb3cgdG8gam9pbiBkYXRhIGZyb20gZGlmZmVyZW50IHNvdXJjZXMKKiBIb3cgdG8gcmVzaGFwZSAocGl2b3QpIHlvdXIgZGF0YQoKRnJvbSBteSBleHBlcmllbmNlLCB0aGlzIGNvdmVycyBjYS4gODAlIG9mIGNvbW1vbiBkYXRhIG1hbmlwdWxhdGlvbiB0YXNrcy4gU291bmQgbGlrZSBmdW4/IExldHMgZ2V0IHN0YXJ0ZWQhCgojIFRoZSA1IGNvcmUgdmVyYnMgb2YgZGF0YS1tYW5pcHVsYXRpb24KClNvLCBsZXQncyB6b29tIGluIHRoZSBgZHBseXJgIGZ1bmN0aW9uYWxpdHkgYW5kIHN5bnRheCBmb3IgYSBtb21lbnQsIGJlZm9yZSB3ZSBpbGx1c3RyYXRlIGl0IGF0IHRoZSBmb2xsb3dpbmcgZGF0YS1tYW5pcHVsYXRpb24gdGFza3MuCgpBdCBpdCdzIGNvcmUgYXJlIDUgY2VudHJhbCB2ZXJicyBvZiBkYXRhL21hbmlwdWxhdGlvbiwgd2hpY2ggYXJlCgoxLiBgZmlsdGVyKClgIHBpY2tzIGNhc2VzIGJhc2VkIG9uIHRoZWlyIHZhbHVlcy4KMi4gYHNlbGVjdCgpYCBwaWNrcyB2YXJpYWJsZXMgYmFzZWQgb24gdGhlaXIgbmFtZXMuCjMuIGBhcnJhbmdlKClgIGNoYW5nZXMgdGhlIG9yZGVyaW5nIG9mIHRoZSByb3dzLgo0LiBgbXV0YXRlKClgIGFkZHMgbmV3IHZhcmlhYmxlcyB0aGF0IGFyZSBmdW5jdGlvbnMgb2YgZXhpc3RpbmcgdmFyaWFibGVzCjUuIGBzdW1tYXJpc2UoKWAgcmVkdWNlcyBtdWx0aXBsZSB2YWx1ZXMgZG93biB0byBhIHNpbmdsZSBzdW1tYXJ5LgoKVGhlc2UgYWxsIGNvbWJpbmUgbmF0dXJhbGx5IHdpdGggYGdyb3VwX2J5KClgIHdoaWNoIGFsbG93cyB5b3UgdG8gcGVyZm9ybSBhbnkgb3BlcmF0aW9uICJieSBncm91cCIuIFlvdSBjYW4gbGVhcm4gbW9yZSBhYm91dCB0aGVtIGluIGB2aWduZXR0ZSgiZHBseXIiKWAuIEFzIHdlbGwgYXMgdGhlc2Ugc2luZ2xlLXRhYmxlIHZlcmJzLCBkcGx5ciBhbHNvIHByb3ZpZGVzIGEgdmFyaWV0eSBvZiB0d28tdGFibGUgdmVyYnMgKGpvaW5zIGFuZCBtdXRhdGlvbnMpLCB3aGljaCB5b3UgY2FuIGxlYXJuIGFib3V0IGluIGB2aWduZXR0ZSgidHdvLXRhYmxlIilgLgoKCiMjIFRoZSBkYXRhc2V0CldlIHdpbGwgZXhwbG9yZSBub3cgYSBiaXQgd2l0aCB0aGUgc3RhcndhcnMgZGF0YXNldCwgd2hpY2ggY29tZXMgdG9nZXRoZXIgd2l0aCBgZHBseXJgLiBGaXJzdCwgbGV0J3MgZ2V0IGFuIGludHVpdGlvbiB3aGF0J3MgaW4gaXQuIGBoZWFkKClgIHNob3dzIHVzIHRoZSBmaXJzdCAxMCByb3dzLgoKYGBge3J9CnN0YXJ3YXJzICU+JSBoZWFkKCkgIyBUaGUgInN0YXJ0d2FycyIgZGF0YXNldCBpcyBhbiBpbnRlZ3JhdGVkIGRwbHlyIGdvZGRpZSA6KQpgYGAKCmBnbGltcHNlKClgIGdpdmVzIHVzIGFuIG92ZXJ2aWV3IG92ZXIgYWxsIHZhcmlhYmxlIHR5cGVzLgoKYGBge3J9CnN0YXJ3YXJzICU+JSBnbGltcHNlKCkKYGBgCgojIyAxOiBgZmlsdGVyKClgClRoZSB2ZXJiIGBmaWx0ZXIoKWAgbGV0cyB5b3Ugc3Vic2V0IGEgZGF0YWZyYW1lIGJ5IHJvd3MgKG9ic2VydmF0aW9ucyksIG1lYW5pbmcgdGhlIG91dHB1dCB3aWxsIGZpbHRlciBmb3Igb25seSByb3dzIHdoaWNoIGZ1bGZpbGwgYSBjZXJ0YWluIGNvbmRpdGlvbi4KCmBgYHtyfQpzdGFyd2FycyAlPiUgCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkRyb2lkIikgCmBgYAoKQ29uZGl0aW9ucyBjYW4gYmUgY29tYmluZWQgd2l0aCBib29sZWFuIG9wZXJhdG9ycyAoYCYsIHxgKQoKYGBge3J9CnN0YXJ3YXJzICU+JSAKICBmaWx0ZXIoaG9tZXdvcmxkID09ICJUYXRvb2luZSIgJiBzcGVjaWVzID09ICJEcm9pZCIpCmBgYAoKIyMgMjogYHNlbGVjdCgpYApUaGUgdmVyYiBgc2VsZWN0KClgIGxldHMgeW91IHN1YnNldCBhIGRhdGFmcmFtZSBieSBjb2x1bW4gKHZhcmlhYmxlKSwgbWVhbmluZyB0aGUgb3V0cHV0IHdpbGwgb25seSBjb250YWluIGNlcnRhaW4gY29sdW1ucyBpbiB0aGUgc3RhdGVkIG9yZGVyCgpgYGB7cn0Kc3RhcndhcnMgJT4lIAogIHNlbGVjdChuYW1lLCBob21ld29ybGQpICU+JQogIGhlYWQoMTApIApgYGAKCkl0IGNhbiBhbHNvIGJlIHVzZWQgdG8gZGVzZWxlY3QgY29sdW1ucyB3aXRoIGEgbGVhZGluZyBgLWAKCmBgYHtyfQpzdGFyd2FycyAlPiUgCiAgc2VsZWN0KC1iaXJ0aF95ZWFyLCAtc2tpbl9jb2xvcikgJT4lCiAgaGVhZCgxMCkgCmBgYAoKTGFzdGx5LCBtYW55IG9mIGBkcGx5cmAncyBvdGhlciBjb25kaXRpb25zIGFuZCBoZWxwZXIgZnVuY3Rpb25zIGNhbiBiZSBhcHBsaWVkIHRvIHNlbGVjdCBvciBkZXNlbGVjdCBjb2x1bW5zLiBNb3JlIG9uIHRoYXQgbGF0ZXIuCmBgYHtyfQpzdGFyd2FycyAlPiUgCiAgc2VsZWN0KG5hbWUsIGVuZHNfd2l0aCgiY29sb3IiKSkgJT4lCiAgaGVhZCgxMCkgCmBgYAoKIyMgMzogYGFycmFuZ2UoKWAKVGhlIHZlcmIgYGFycmFuZ2UoKWAgZGVmaW5lcyB0aGUgd2F5IHRoZSByb3dzIG9mIHlvdXIgZGF0YWZyYW1lIGFyZSBvcmRlcmVkCgpgYGB7cn0Kc3RhcndhcnMgJT4lIAogIGFycmFuZ2UoaG9tZXdvcmxkLCBkZXNjKG1hc3MpKSAlPiUgIyBOb3RlOiBkZXNjKCkgd2l0bGwgb3JkZXIgaXQgZGVzY2VuZGluZywgc28gdGhlIGhlYXZpZXN0IG9uZSBmaXJzdC4KICBoZWFkKDEwKSAKYGBgCgojIyA0OiBgbXV0YXRlKClgClRoZSB2ZXJiIGBtdXRhdGUoKWAgbGV0cyB5b3UgbWFuaXB1bGF0ZSBleGlzdGluZyB2YXJpYWJsZXMgb3IgY3JlYXRlIG5ldyBvbmVzLgoKYGBge3J9CnN0YXJ3YXJzICU+JSAKICBtdXRhdGUoYm1pID0gbWFzcyAvICgoaGVpZ2h0IC8gMTAwKSAgXiAyKSwgIyBJZiB5b3UgZGlkbnQga25vdyBob3cgdG8gY2FsY3VsYXRlIHRoZSBCTUksIG5vdyB5b3UgZG8uLi4KICAgICAgICAgbWFzcy5yZWwgPSBtYXNzIC8gbWF4KG1hc3MsIG5hLnJtID0gVFJVRSkpICU+JQogIHNlbGVjdChuYW1lOm1hc3MsIG1hc3MucmVsLCBibWkpICU+JQogIGFycmFuZ2UoZGVzYyhibWkpKSAlPiUKICBoZWFkKDEwKSAKYGBgCgojIyA1OiBgc3VtbWFyaXplKClgClRoZSB2ZXJiIGBzdW1tYXJpemUoKWAgcmVkdWNlcyB5b3VyIGRhdGFzZXQgdG8gb25lIG9ic2VydmF0aW9uLCB3aGljaCBpcyBzdW1tYXJpemVkIGFjY29yZGluZyB0byBhIGRlZmluZWQgZnVuY3Rpb24uCgpgYGB7cn0Kc3RhcndhcnMgJT4lIAogIHN1bW1hcml6ZShtaW4uaGVpZ2h0ID0gbWluKGhlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWVhbi5oZWlnaHQgPSBtZWFuKGhlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWF4LmhlaWdodCA9IG1heChoZWlnaHQsIG5hLnJtID0gVFJVRSksICAgICAgICAgICAgCiAgICAgICAgICAgIHNkLmhlaWdodCA9IHNkKGhlaWdodCwgbmEucm0gPSBUUlVFKSApCmBgYAoKCgojIyAoKzEpIFBpdm90aW5nCkJ5IG5vdywgd2Ugd29ya2VkIHdpdGggdGhlIHRyYWRpdGlvbmFsIGRhdGEgc3RydWN0dXJlLCB3aGVyZSBldmVyeSBjb2x1bW4gY29ycmVzcG9uZHMgdG8gb25lIHZhcmlhYmxlLiBXZSBhbHNvIGNhbGwgdGhhdCBhICoqd2lkZSoiKiogZGF0YSBmb3JtYXQuIFRoaXMgaXMgaW4gbW9zdCBjYXNlcyB0aGUgbW9zdCBlZmZpY2llbnQgYW5kIHByZWZlcmVkIHdheSB0byBrZWVwIHlvdXIgZGF0YS4gSG93ZXZlciwgdGhlcmUgYXJlIHNvbWUgY2FzZXMsIHdoZXJlIGFub3RoZXIgZGF0YWZvcm1hdCB3b3VsZCBiZSBtb3JlIGNvbnZlbmllbnQuCgpGb3IgZXhhbXBsZSwgZm9yIHJhbmdlIG9mIGdyYXBoaWNhbCBhcyB3ZWxsIGFzIG51bWVyaWNhbCBzdW1tYXJpZXMsIGl0IHdvdWxkIGJlIG5pY2UgdG8gaGF2ZSBldmVyeSBvYnNlcnZhdGlvbiBhbmQgZXZlcnkgdmFyaWFibGUgdG8gYmUgbWFwcGVkIGluIGEgc2luZ2xlIHJvdy4gVGhpcyBmb3JtYXQgaXMgYWxzbyBjYWxsZXMgKipsb25nKiogKG9yIGluIFIgc2xhbmcsICoqdGlkeSoqKSBkYXRhLiBBZGl0dGlvbmFsbHksIHNvbWUgcmVhbCBsaWZlIGRhdGFzZXRzIChpbmNsdWRpbmcgYSBsb3Qgb2YgV29ybGRCYW5rLCBFdXJvc3RhdHMsIE9FQ0QsIGVjdC4pIGRhdGEgaXMgdXN1YWxseSBhdmFpbGFibGUgaW4gYSBsb25nIGZvcm1hdC4gVGhlcmVmb3JlLCB3ZWFyZSBpbiBuZWVkIG9mIGVhc2lseSBzd2l0Y2hpbmcgYmV0d2VlbiBsb25nIGFuZCB3aWRlIGZvcm1hdHMsIHRvICoqcmVzaGFwZSoqIHlvdXIgZGF0YSAoaW4gRXhjZWwgc2xhbmcsIHBlb3BsZSBjYWxsIHRoYXQgKipwaXZvdGluZyoqKS4KCmBgYHtyfQpmaXNoX2VuY291bnRlcnMgJT4lIGhlYWQoMTApCmBgYAoKTGV0cyB0YWtlIGEgbG9vayBhdCBhIGBwaXZvdF93aWRlcmAgZXhhbXBsZQpgYGB7cn0KIyBTZWUgdmlnbmV0dGUoInBpdm90IikgZm9yIGV4YW1wbGVzIGFuZCBleHBsYW5hdGlvbgpmaXNoX2VuY291bnRlcnMgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHN0YXRpb24sIHZhbHVlc19mcm9tID0gc2VlbikgJT4lCiAgaGVhZCgxMCkKYGBgCkFuZCBub3cgYHBpdm90X2xvbmdlcmAKYGBge3J9CiMgU2VlIHZpZ25ldHRlKCJwaXZvdCIpIGZvciBleGFtcGxlcyBhbmQgZXhwbGFuYXRpb24KIyBTaW1wbGVzdCBjYXNlIHdoZXJlIGNvbHVtbiBuYW1lcyBhcmUgY2hhcmFjdGVyIGRhdGEKcmVsaWdfaW5jb21lICU+JQogIGhlYWQoMTApCmBgYAoKYGBge3J9CnJlbGlnX2luY29tZSAlPiUKICBwaXZvdF9sb25nZXIoIXJlbGlnaW9uLCBuYW1lc190byA9ICJpbmNvbWUiLCB2YWx1ZXNfdG8gPSAiY291bnQiKSAlPiUKICBoZWFkKDEwKQpgYGAKCgoKCgojIyAoKzIpIERvaW5nIG1hbmlwdWxhdGlvbiBieSBncm91cDogYGdyb3VwX2J5YApGaW5hbGx5LCBgYnlfZ3JvdXAoKWAgaXMgb2ZmZXJzIHRoZSBwZXJmZWN0IGNvbXBsZW1lbnQgdG8gdGhlIDUgYmFzaWMgdmVyYnMsIHNpbmNlIGl0IGFsbG93cyB0byBwZXJmb3JtIGFxbGwgdGhlIGFib3ZlIG1lbnRpb25lZCB0YXNrcyBzZXBwZXJhdGUgYWNjb3JkaW5nIHRvIHVzZWQtZGVmaW5lZCBncm91cGluZ3MgaW4gb25lIG9yIG11bHRpcGxlIGNhdGVnb3JpZXMuIExldHMgbG9vayBhdCBzb21lIGV4YW1wbGVzOgoKRmlyc3Qgb2YgYWxsLCBpdCB3b3JrcyBhbWF6aW5nbHkgd2VsbCB3aXRoIHRoZSBgc3VtbWFyaXplKClgIHZlcmIsIHByb2R1Y2luZyB3aXRoaW4gZ3JvdXAgc3VtbWFyaWVzCgpgYGB7cn0Kc3RhcndhcnMgJT4lIAogIGdyb3VwX2J5KGhvbWV3b3JsZCkgJT4lCiAgc3VtbWFyaXplKG1lYW4uaGVpZ2h0ID0gbWVhbihoZWlnaHQsIG5hLnJtID0gVFJVRSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGRlc2MobWVhbi5oZWlnaHQpKQpgYGAKCk5vdGUgdGhhdCBpdCBpcyBhZHZpY2VkIHRvIGZpbmlzaCBhbHdheXMgc3VjaCBvcGVyYXRpb25zIHdpdGggYHVuZ3JvdXAoKWAuIE90aGVyd2lzZSwgdGhlIHN0aWxsIGFjdGl2ZSBncm91cGluZyBtaWdodCBhdCBhIGxhdGVyIHBvaW50IGxlYWQgdG8gYWxsIGtpbmQgb2YgdW5mb3JzZWVuIHJlc3VsdHMuCgojIyBZb3VyIHR1cm4KClNvbWUgbGl0dGxlIGV4ZXJjaXNlcyBpbiBiZXR3ZWVuOiBQbGVhc2Ugc29sdmUgKipFeGVyY2lzZSAxKiogaW4gdGhlIGNvcnJlc3BvbmRpbmcgc2VjdGlvbiBvbiBgZ2l0aHViYAoKCgoKIyBUd28tdGFibGUgdmVyYnMKSXQncyByYXJlIHRoYXQgYSBkYXRhIGFuYWx5c2lzIGludm9sdmVzIG9ubHkgYSBzaW5nbGUgdGFibGUgb2YgZGF0YS4gSW4gcHJhY3RpY2UsIHlvdSdsbCBub3JtYWxseSBoYXZlIG1hbnkgdGFibGVzIHRoYXQgY29udHJpYnV0ZSB0byBhbiBhbmFseXNpcywgYW5kIHlvdSBuZWVkIGZsZXhpYmxlIHRvb2xzIHRvIGNvbWJpbmUgdGhlbS4gSW4gYGRwbHlyYCwgdGhlcmUgYXJlIHRocmVlIGZhbWlsaWVzIG9mIHZlcmJzIHRoYXQgd29yayB3aXRoIHR3byB0YWJsZXMgYXQgYSB0aW1lOgoKMS4gTXV0YXRpbmcgam9pbnMsIHdoaWNoIGFkZCBuZXcgdmFyaWFibGVzIHRvIG9uZSB0YWJsZSBmcm9tIG1hdGNoaW5nIHJvd3MgaW4gYW5vdGhlci4KMi4gRmlsdGVyaW5nIGpvaW5zLCB3aGljaCBmaWx0ZXIgb2JzZXJ2YXRpb25zIGZyb20gb25lIHRhYmxlIGJhc2VkIG9uIHdoZXRoZXIgb3Igbm90IHRoZXkgbWF0Y2ggYW4gb2JzZXJ2YXRpb24gaW4gdGhlIG90aGVyIHRhYmxlLgozLiBTZXQgb3BlcmF0aW9ucywgd2hpY2ggY29tYmluZSB0aGUgb2JzZXJ2YXRpb25zIGluIHRoZSBkYXRhIHNldHMgYXMgaWYgdGhleSB3ZXJlIHNldCBlbGVtZW50cy4KCkFtb25nIHRob3NlLCBtdXRhdGluZyBqb2lucyBhcmUgdGhlIGJ5IGZhciBtb3N0IHVzZWQgb25lcywgc28gd2Ugd2lsbCBicmllZmx5IGRpc2N1c3MgdGhlbSBoZXJlLCBhbmQgY292ZXIgb3RoZXIgdHlwZXMgbGF0ZXIgaW4gdGhlIGFwcGxpZWQgcGFydC4gRm9yIHF1ZXN0aW9ucyByaWdodCBhd2F5LCBjb25zdWx0IGB2aWduZXR0ZSh0d28tInR3by10YWJsZSIpYC4gTXV0YXRpbmcgam9pbnMgYWxsb3cgeW91IHRvIGNvbWJpbmUgdmFyaWFibGVzIGZyb20gbXVsdGlwbGUgdGFibGVzIChtZXJnaW5nIGRhdGEpLiBXZSBoZXJlIGFzc3VtZSBvdXIgZGF0YSBpcyAidGlkeSIgKG1lYW5pbmc6IHJvd3MgPSBvYnNlcnZhdGlvbnMsIGNvbHVtbnMgPSB2YXJpYWJsZXMpLiAKCkxldHMgYnJpZWZseSBjcmVhdGUgc29tZSBleGFtcGxlIGRhdGE6CmBgYHtyfQpkZjEgPC0gZGF0YV9mcmFtZSh4ID0gYygxLCAyKSwgeSA9IDI6MSkKZGYyIDwtIGRhdGFfZnJhbWUoeCA9IGMoMSwgMyksIGEgPSAxMCwgYiA9ICJhIikKZGYxCmRmMgpgYGAKClRoZXJlIGFyZSA0IHR5cGVzIG9mIG11dGF0aW5nIGpvaW4gdG8gY29tYmluZSB2YXJpYWJsZXMgZnJvbSBkYXRhZnJhbWUgYHhgIGFuZCBgeSwgd2hpY2ggZGlmZmVyIGluIHRoZWlyIGJlaGF2aW91ciB3aGVuIGEgbWF0Y2ggaXMgbm90IGZvdW5kLiBXZSdsbCBpbGx1c3RyYXRlIGVhY2ggd2l0aCBhIHNpbXBsZSBleGFtcGxlOgoKMS4gYGxlZnRfam9pbih4LCB5KWAgaW5jbHVkZXMgYWxsIG9ic2VydmF0aW9ucyBpbiBgeGAsIHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciB0aGV5IG1hdGNoIG9yIG5vdC4gVGhpcyBpcyB0aGUgbW9zdCBjb21tb25seSB1c2VkIGpvaW4gYmVjYXVzZSBpdCBlbnN1cmVzIHRoYXQgeW91IGRvbid0IGxvc2Ugb2JzZXJ2YXRpb25zIGZyb20geW91ciBwcmltYXJ5IHRhYmxlLgpgYGB7cn0KZGYxICU+JSBsZWZ0X2pvaW4oZGYyKQpgYGAKCgoyLiBgcmlnaHRfam9pbih4LCB5KWAgaW5jbHVkZXMgYWxsIG9ic2VydmF0aW9ucyBpbiBgeWAuIEl0J3MgZXF1aXZhbGVudCB0byBgbGVmdF9qb2luKHksIHgpYCwgYnV0IHRoZSBjb2x1bW5zIHdpbGwgYmUgb3JkZXJlZCBkaWZmZXJlbnRseS4KYGBge3J9CmRmMSAlPiUgcmlnaHRfam9pbihkZjIpCmBgYAoKYGBge3J9CmRmMiAlPiUgbGVmdF9qb2luKGRmMSkKYGBgCgozLiBgaW5uZXJfam9pbih4LCB5KWAgb25seSBpbmNsdWRlcyBvYnNlcnZhdGlvbnMgdGhhdCBtYXRjaCBpbiBib3RoIGB4YCBhbmQgYHlgLgpgYGB7cn0KZGYxICU+JSBpbm5lcl9qb2luKGRmMikKYGBgCgo0LiBgZnVsbF9qb2luKClgIGluY2x1ZGVzIGFsbCBvYnNlcnZhdGlvbnMgZnJvbSBgeGAgYW5kIGB5YC4KYGBge3J9CmRmMSAlPiUgZnVsbF9qb2luKGRmMikKYGBgCgpUaGUgbGVmdCwgcmlnaHQgYW5kIGZ1bGwgam9pbnMgYXJlIGNvbGxlY3RpdmVseSBrbm93IGFzICoqb3V0ZXIgam9pbnMqKi4gV2hlbiBhIHJvdyBkb2Vzbid0IG1hdGNoIGluIGFuIG91dGVyIGpvaW4sIHRoZSBuZXcgdmFyaWFibGVzIGFyZSBmaWxsZWQgaW4gd2l0aCBtaXNzaW5nIHZhbHVlcy4gCgpXaGlsZSBtdXRhdGluZyBqb2lucyBhcmUgcHJpbWFyaWx5IHVzZWQgdG8gYWRkIG5ldyB2YXJpYWJsZXMsIHRoZXkgY2FuIGFsc28gZ2VuZXJhdGUgbmV3IG9ic2VydmF0aW9ucy4gSWYgYSBtYXRjaCBpcyBub3QgdW5pcXVlLCBhIGpvaW4gd2lsbCBhZGQgYWxsIHBvc3NpYmxlIGNvbWJpbmF0aW9ucyAodGhlIENhcnRlc2lhbiBwcm9kdWN0KSBvZiB0aGUgbWF0Y2hpbmcgb2JzZXJ2YXRpb25zOgoKYGBge3J9CmRmMSA8LSBkYXRhX2ZyYW1lKHggPSBjKDEsIDEsIDIpLCB5ID0gMTozKQpkZjIgPC0gZGF0YV9mcmFtZSh4ID0gYygxLCAxLCAyKSwgeiA9IGMoImEiLCAiYiIsICJhIikpCgpkZjEgJT4lIGxlZnRfam9pbihkZjIpCmBgYAoKTm90ZTogSW4gY2FzZSBvZiBtdWx0aXBsZSBtYXRjaGVzIGluIGB5YCwgYWRpdHRpb25hbCByb3dzIHdpbGwgYmUgYWRkZWQuIFRoZXJlZm9yZSwgaWYgdGhhdCBpcyBub3QgdGhlIGJlaGF2aW9yIHlvdSB3YW50LCB1c2UgdGhlIGBkaXN0aW5jdCgpYCBmdW5jdGlvbiBhZnRlcndhcmRzLgoKQnkgZGVmYXVsdCwgam9pbnMgYXJlIHBlcmZvcm1lZCBvbiB0aGUgYmFzZXMgb2YgKiphbGwqKiB2YXJpYWJsZXMgYXBwZWFyaW5nIGluIGB4YCBhbmQgYHlgLiBZb3UgY2FuIGFsc28gc3BlY2lmeSBvbmUgb3IgbXVsdGlwbGUgdmFyaWFibGVzIHRoYXQgc2hvdWxkIGJlIG1hdGNoZWQgYnkgdXNpbmcgdGhlIGBieWAgYXJndW1lbnQuIFRoaXMgY2FuIGFsc28gYmUgdXNlZCB0byBtYXRjaCBvbiB2YXJpYWJsZXMgd2l0aCBkaWZmZXJlbnQgbmFtZXMgaW4gYHhgIGFuZCBgeWAuCgpgYGB7cn0KZGYxIDwtIGRhdGFfZnJhbWUoeCA9IGMoMywgMiwgMSksIHkgPSAxOjMsIHogPSBjKDEsMyw0KSkKZGYyIDwtIGRhdGFfZnJhbWUoeCA9IGMoMSwgMiwgMyksIHkgPSAyOjQsIHEgPSBjKCJhIiwgImIiLCAiYSIpKQoKZGYxICU+JSBsZWZ0X2pvaW4oZGYyLCBieSA9ICJ4IikKZGYxICU+JSBsZWZ0X2pvaW4oZGYyLCBieSA9IGMoIngiLCAieSIpKQpkZjEgJT4lIGxlZnRfam9pbihkZjIsIGJ5ID0gYygieCIgPSAieSIpKQpgYGAKCgojIFlvdXIgdHVybgoKU29tZSBsaXR0bGUgZXhlcmNpc2VzIGluIGJldHdlZW46IFBsZWFzZSBzb2x2ZSAqKkV4ZXJjaXNlIDIqKiBpbiB0aGUgY29ycmVzcG9uZGluZyBzZWN0aW9uIG9uIGBnaXRodWJgCgoKIyBFbmRub3RlcwoKIyMjIFJlZmVyZW5jZXMKCiMjIyBTdWdnZXN0aW9ucyBmb3IgZnVydGhlciBzdHVkeQoKKiBSIGZvciBEYXRhIFNjaWVuY2UgKEdyb2xlbXVuZCAmIFdpY2toYW0pCiAgKiBbQ2hhcHRlciA1XShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RyYW5zZm9ybS5odG1sKQogICogW0NoYXB0ZXIgMTJdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovdGlkeS1kYXRhLmh0bWwpCiAgKiBbQ2hhcHRlciAxM10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9yZWxhdGlvbmFsLWRhdGEuaHRtbCkKKiBEYXRhQ2FtcAogICogW1dvcmtpbmcgd2l0aCBEYXRhIGluIHRoZSBUaWR5dmVyc2VdKGh0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvd29ya2luZy13aXRoLWRhdGEtaW4tdGhlLXRpZHl2ZXJzZSkKICAqIFtEYXRhIE1hbmlwdWxhdGlvbiB3aXRoIGRwbHlyXShodHRwczovL2xlYXJuLmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2RhdGEtbWFuaXB1bGF0aW9uLXdpdGgtZHBseXIpCiAgKiBbSm9pbmluZyBEYXRhIHdpdGggZHBseXJdKGh0dHBzOi8vbGVhcm4uZGF0YWNhbXAuY29tL2NvdXJzZXMvam9pbmluZy1kYXRhLXdpdGgtZHBseXIpCgoKIyMjIFNlc3Npb24gaW5mbwpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGA=