Overview: https://dplyr.tidyverse.org)

dplyr is a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges

1 Functions

2 iris data

iris

3 select

Subset columns using their names and types. Example uses babynames package as in Posit Primers: Work with Data.

Helper Function Use Example
- Columns except select(babynames, -prop)
: Columns between (inclusive) select(babynames, year:n)
contains() Columns that contains a string select(babynames, contains(“n”))
ends_with() Columns that ends with a string select(babynames, ends_with(“n”))
matches() Columns that matches a regex select(babynames, matches(“n”))
num_range() Columns with a numerical suffix in the range Not applicable with babynames
one_of() Columns whose name appear in the given set select(babynames, one_of(c(“sex”, “gender”)))
starts_with() Columns that starts with a string select(babynames, starts_with(“n”))

3.1 Columns except

select(iris, -Species)

3.2 Columns between (inclusive)

select(iris, 3:5)

3.3 Columns that contains a string

select(iris, contains("Sepal"))

3.4 Columns that ends with a string

select(iris, ends_with("Width"))

3.5 Columns that matches a regex

select(iris, matches("pe"))

3.6 Columns with a numerical suffix in the range

Not applicable to iris data

3.7 Columns whose name appear in the given set

select(iris, one_of("Sepal.Length", "Petal.Width"))

3.8 Columns that starts with a string

select(iris, starts_with("s"))

3.9 Columns by changing names

select(iris, sl = Sepal.Length, sw = Sepal.Width, sp = Species)

4 filter

Subset rows using column values

Logical operator tests Example
> Is x greater than y? x > y
>= Is x greater than or equal to y? x >= y
< Is x less than y? x < y
<= Is x less than or equal to y? x <= y
== Is x equal to y? x == y
!= Is x not equal to y? x != y
is.na() Is x an NA? is.na(x)
!is.na() Is x not an NA? !is.na(x)

4.1 Is x greater than y?

filter(iris, Sepal.Length > 6.0)

4.2 Is x greater than or equal to y?

And: &

filter(iris, Sepal.Length >= 5.0 & Sepal.Width >= 4.0)

4.3 Is x less than y?

filter(iris, Sepal.Length < 5.0)

4.4 Is x less than or equal to y?

OR: |

filter(iris, Sepal.Length >= 5.0 & Sepal.Width >= 4.0)

4.5 Is x equal to y?

filter(iris, Species=="virginica")

4.6 Is x an NA?

filter(iris, is.na(Sepal.Width))

4.7 Is x not an NA?

filter(iris, !is.na(Sepal.Width))

4.8 Extra

filter(iris, Sepal.Width > Petal.Length)

5 arrange

Unlike other dplyr verbs, arrange() largely ignores grouping; you need to explicitly mention grouping variables (`or use .by_group = TRUE) in order to group by them, and functions of variables are evaluated once per data frame, not once per group.

arrange(iris, Sepal.Length, desc(Sepal.Width))

5.1 slice, slice_head, slice_tail, slice_min, slice_max, slice_sample

slice_min(iris, Sepal.Length, n = 6)

6 pipes(R4DS)

7 mutate

mutate(iris, rank = min_rank(Sepal.Length)) %>%
  arrange(rank)
iris %>% mutate(Sepal.Ratio = Sepal.Length/Sepal.Width) %>%
  arrange(Sepal.Ratio)

8 group_by

Most data operations are done on groups defined by variables. group_by() takes an existing tbl and converts it into a grouped tbl where operations are performed “by group”. ungroup()removes grouping.

9 summarise or summarize

Summary functions

You can use any function in summarise() so long as it meets one criteria: the function must take a vector of values as input and return a single value as output. Functions that do this are known as summary functions and they are common in the field of descriptive statistics. Some of the most useful summary functions include:

  1. Measures of location - mean(x), median(x), quantile(x, 0.25), min(x), and max(x)
  2. Measures of spread - sd(x), var(x), IQR(x), and mad(x)
  3. Measures of position - first(x), nth(x, 2), and last(x)
  4. Counts - n_distinct(x) and n(), which takes no arguments, and returns the size of the current group or data frame.
  5. Counts and proportions of logical values - sum(!is.na(x)), which counts the number of TRUEs returned by a logical test; mean(y == 0), which returns the proportion of TRUEs returned by a logical test.
  6. if_else(), recode(), case_when()
iris %>% 
  group_by(Species) %>% 
  summarize(sl_mean = mean(Sepal.Length), sw_mean = mean(Sepal.Width), 
  pl_mean = mean(Petal.Length), pw_mean = mean(Petal.Width))
iris %>% 
  group_by(Species) %>%
  mutate(rank = min_rank(Sepal.Length)) %>%
  arrange(rank)

10 Posit Primers

Please practice with Posit Primers - Work with Data

Work with Data

Learn the most important data handling skills in R: how to extract values from a table, subset tables, calculate summary statistics, and derive new variables.

Working with Tibbles

Learn to use tibbles, the most user-friendly tabular data structure in R, as well as how to manage tidyverse packages with... the tidyverse package.

Isolating Data with dplyr

Master three simple functions for finding, and extracting, the data in your data set. Here you will learn to select variables, filter observations, and arrange values. Here, you will also meet R’s pipe operator, %>%.

Deriving Information with dplyr

Data sets contain more information than they display, and this tutorial will show you how to access that information. You’ll learn to derive new variables and to compute groupwise summary statistics.

LS0tCnRpdGxlOiAiZHBseXIgd2l0aCBleGFtcGxlcyBvZiBpcmlzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKLS0tCgo+T3ZlcnZpZXc6IFtodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmddKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZykpCj4KPmBkcGx5cmAgaXMgYSBncmFtbWFyIG9mIGRhdGEgbWFuaXB1bGF0aW9uLCBwcm92aWRpbmcgYSBjb25zaXN0ZW50IHNldCBvZiB2ZXJicyB0aGF0IGhlbHAgeW91IHNvbHZlIHRoZSBtb3N0IGNvbW1vbiBkYXRhIG1hbmlwdWxhdGlvbiBjaGFsbGVuZ2VzCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyBGdW5jdGlvbnMKCi0gICBgc2VsZWN0KClgIHBpY2tzIHZhcmlhYmxlcyBiYXNlZCBvbiB0aGVpciBuYW1lcy4KCi0gICBgZmlsdGVyKClgIHBpY2tzIGNhc2VzIGJhc2VkIG9uIHRoZWlyIHZhbHVlcy4KCi0gICBgbXV0YXRlKClgIGFkZHMgbmV3IHZhcmlhYmxlcyB0aGF0IGFyZSBmdW5jdGlvbnMgb2YgZXhpc3RpbmcgdmFyaWFibGUKCi0gICBgc3VtbWFyaXNlKClgIHJlZHVjZXMgbXVsdGlwbGUgdmFsdWVzIGRvd24gdG8gYSBzaW5nbGUgc3VtbWFyeS4KCi0gICBgYXJyYW5nZSgpYCBjaGFuZ2VzIHRoZSBvcmRlcmluZyBvZiB0aGUgcm93cy4KCi0gICBgZ3JvdXBfYnkoKWAgdGFrZXMgYW4gZXhpc3RpbmcgdGJsIGFuZCBjb252ZXJ0cyBpdCBpbnRvIGEgZ3JvdXBlZCB0YmwuCgojIGBpcmlzYCBkYXRhCgpgYGB7cn0KaXJpcwpgYGAKCiMgW2BzZWxlY3RgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3NlbGVjdC5odG1sKQoKU3Vic2V0IGNvbHVtbnMgdXNpbmcgdGhlaXIgbmFtZXMgYW5kIHR5cGVzLiBFeGFtcGxlIHVzZXMgYGJhYnluYW1lc2AgcGFja2FnZSBhcyBpbiBQb3NpdCBQcmltZXJzOiBXb3JrIHdpdGggRGF0YS4KCnwgSGVscGVyIEZ1bmN0aW9uIHwgVXNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBFeGFtcGxlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgXC0gICAgICAgICAgICAgIHwgQ29sdW1ucyBleGNlcHQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBzZWxlY3QoYmFieW5hbWVzLCAtcHJvcCkgICAgICAgICAgICAgICAgICAgICAgfAp8IDogICAgICAgICAgICAgICB8IENvbHVtbnMgYmV0d2VlbiAoaW5jbHVzaXZlKSAgICAgICAgICAgICAgICAgIHwgc2VsZWN0KGJhYnluYW1lcywgeWVhcjpuKSAgICAgICAgICAgICAgICAgICAgIHwKfCBjb250YWlucygpICAgICAgfCBDb2x1bW5zIHRoYXQgY29udGFpbnMgYSBzdHJpbmcgICAgICAgICAgICAgICB8IHNlbGVjdChiYWJ5bmFtZXMsIGNvbnRhaW5zKCJuIikpICAgICAgICAgICAgICB8CnwgZW5kc193aXRoKCkgICAgIHwgQ29sdW1ucyB0aGF0IGVuZHMgd2l0aCBhIHN0cmluZyAgICAgICAgICAgICAgfCBzZWxlY3QoYmFieW5hbWVzLCBlbmRzX3dpdGgoIm4iKSkgICAgICAgICAgICAgfAp8IG1hdGNoZXMoKSAgICAgICB8IENvbHVtbnMgdGhhdCBtYXRjaGVzIGEgcmVnZXggICAgICAgICAgICAgICAgIHwgc2VsZWN0KGJhYnluYW1lcywgbWF0Y2hlcygibiIpKSAgICAgICAgICAgICAgIHwKfCBudW1fcmFuZ2UoKSAgICAgfCBDb2x1bW5zIHdpdGggYSBudW1lcmljYWwgc3VmZml4IGluIHRoZSByYW5nZSB8IE5vdCBhcHBsaWNhYmxlIHdpdGggYmFieW5hbWVzICAgICAgICAgICAgICAgICB8Cnwgb25lX29mKCkgICAgICAgIHwgQ29sdW1ucyB3aG9zZSBuYW1lIGFwcGVhciBpbiB0aGUgZ2l2ZW4gc2V0ICAgfCBzZWxlY3QoYmFieW5hbWVzLCBvbmVfb2YoYygic2V4IiwgImdlbmRlciIpKSkgfAp8IHN0YXJ0c193aXRoKCkgICB8IENvbHVtbnMgdGhhdCBzdGFydHMgd2l0aCBhIHN0cmluZyAgICAgICAgICAgIHwgc2VsZWN0KGJhYnluYW1lcywgc3RhcnRzX3dpdGgoIm4iKSkgICAgICAgICAgIHwKCiMjIENvbHVtbnMgZXhjZXB0CgpgYGB7cn0Kc2VsZWN0KGlyaXMsIC1TcGVjaWVzKQpgYGAKCiMjIENvbHVtbnMgYmV0d2VlbiAoaW5jbHVzaXZlKQoKYGBge3J9CnNlbGVjdChpcmlzLCAzOjUpCmBgYAoKIyMgQ29sdW1ucyB0aGF0IGNvbnRhaW5zIGEgc3RyaW5nCgpgYGB7cn0Kc2VsZWN0KGlyaXMsIGNvbnRhaW5zKCJTZXBhbCIpKQpgYGAKCiMjIENvbHVtbnMgdGhhdCBlbmRzIHdpdGggYSBzdHJpbmcKCmBgYHtyfQpzZWxlY3QoaXJpcywgZW5kc193aXRoKCJXaWR0aCIpKQpgYGAKCiMjIENvbHVtbnMgdGhhdCBtYXRjaGVzIGEgcmVnZXgKCmBgYHtyfQpzZWxlY3QoaXJpcywgbWF0Y2hlcygicGUiKSkKYGBgCgojIyBDb2x1bW5zIHdpdGggYSBudW1lcmljYWwgc3VmZml4IGluIHRoZSByYW5nZQoKTm90IGFwcGxpY2FibGUgdG8gYGlyaXNgIGRhdGEKCiMjIENvbHVtbnMgd2hvc2UgbmFtZSBhcHBlYXIgaW4gdGhlIGdpdmVuIHNldAoKYGBge3J9CnNlbGVjdChpcmlzLCBvbmVfb2YoIlNlcGFsLkxlbmd0aCIsICJQZXRhbC5XaWR0aCIpKQpgYGAKCiMjIENvbHVtbnMgdGhhdCBzdGFydHMgd2l0aCBhIHN0cmluZwoKYGBge3J9CnNlbGVjdChpcmlzLCBzdGFydHNfd2l0aCgicyIpKQpgYGAKCiMjIENvbHVtbnMgYnkgY2hhbmdpbmcgbmFtZXMKCmBgYHtyfQpzZWxlY3QoaXJpcywgc2wgPSBTZXBhbC5MZW5ndGgsIHN3ID0gU2VwYWwuV2lkdGgsIHNwID0gU3BlY2llcykKYGBgCgojIFtgZmlsdGVyYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9maWx0ZXIuaHRtbCkKClN1YnNldCByb3dzIHVzaW5nIGNvbHVtbiB2YWx1ZXMKCnwgTG9naWNhbCBvcGVyYXRvciB8IHRlc3RzICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgRXhhbXBsZSAgIHwKfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tfAp8IFw+ICAgICAgICAgICAgICAgfCBJcyB4IGdyZWF0ZXIgdGhhbiB5PyAgICAgICAgICAgICB8IHggXD4geSAgICB8CnwgXD49ICAgICAgICAgICAgICB8IElzIHggZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIHk/IHwgeCBcPj0geSAgIHwKfCBcPCAgICAgICAgICAgICAgIHwgSXMgeCBsZXNzIHRoYW4geT8gICAgICAgICAgICAgICAgfCB4IFw8IHkgICAgfAp8IFw8PSAgICAgICAgICAgICAgfCBJcyB4IGxlc3MgdGhhbiBvciBlcXVhbCB0byB5PyAgICB8IHggXDw9IHkgICB8CnwgPT0gICAgICAgICAgICAgICB8IElzIHggZXF1YWwgdG8geT8gICAgICAgICAgICAgICAgIHwgeCA9PSB5ICAgIHwKfCAhPSAgICAgICAgICAgICAgIHwgSXMgeCBub3QgZXF1YWwgdG8geT8gICAgICAgICAgICAgfCB4ICE9IHkgICAgfAp8IGlzLm5hKCkgICAgICAgICAgfCBJcyB4IGFuIE5BPyAgICAgICAgICAgICAgICAgICAgICB8IGlzLm5hKHgpICB8CnwgIWlzLm5hKCkgICAgICAgICB8IElzIHggbm90IGFuIE5BPyAgICAgICAgICAgICAgICAgIHwgIWlzLm5hKHgpIHwKCiMjIElzIHggZ3JlYXRlciB0aGFuIHk/CgpgYGB7cn0KZmlsdGVyKGlyaXMsIFNlcGFsLkxlbmd0aCA+IDYuMCkKYGBgCgojIyBJcyB4IGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byB5PwoKQW5kOiAmCgpgYGB7cn0KZmlsdGVyKGlyaXMsIFNlcGFsLkxlbmd0aCA+PSA1LjAgJiBTZXBhbC5XaWR0aCA+PSA0LjApCmBgYAoKIyMgSXMgeCBsZXNzIHRoYW4geT8KCmBgYHtyfQpmaWx0ZXIoaXJpcywgU2VwYWwuTGVuZ3RoIDwgNS4wKQpgYGAKCiMjIElzIHggbGVzcyB0aGFuIG9yIGVxdWFsIHRvIHk/CgpPUjogXHwKCmBgYHtyfQpmaWx0ZXIoaXJpcywgU2VwYWwuTGVuZ3RoID4gNy4wIHwgU2VwYWwuV2lkdGggPj0gNC4wKQpgYGAKCiMjIElzIHggZXF1YWwgdG8geT8KCmBgYHtyfQpmaWx0ZXIoaXJpcywgU3BlY2llcz09InZpcmdpbmljYSIpCmBgYAoKIyMgSXMgeCBhbiBOQT8KCmBgYHtyfQpmaWx0ZXIoaXJpcywgaXMubmEoU2VwYWwuV2lkdGgpKQpgYGAKCiMjIElzIHggbm90IGFuIE5BPwoKYGBge3J9CmZpbHRlcihpcmlzLCAhaXMubmEoU2VwYWwuV2lkdGgpKQpgYGAKCiMjIEV4dHJhCgpgYGB7cn0KZmlsdGVyKGlyaXMsIFNlcGFsLldpZHRoID4gUGV0YWwuTGVuZ3RoKQpgYGAKCiMgW2BhcnJhbmdlYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hcnJhbmdlLmh0bWwpCgotICAgYGFycmFuZ2UoKWAgb3JkZXJzIHRoZSByb3dzIG9mIGEgZGF0YSBmcmFtZSBieSB0aGUgdmFsdWVzIG9mIHNlbGVjdGVkIGNvbHVtbnMuCgpVbmxpa2Ugb3RoZXIgYGRwbHlyYCB2ZXJicywgYGFycmFuZ2UoKWAgbGFyZ2VseSBpZ25vcmVzIGdyb3VwaW5nOyB5b3UgbmVlZCB0byBleHBsaWNpdGx5IG1lbnRpb24gZ3JvdXBpbmcgdmFyaWFibGVzIChcYG9yIHVzZSAuYnlfZ3JvdXAgPSBUUlVFKSBpbiBvcmRlciB0byBncm91cCBieSB0aGVtLCBhbmQgZnVuY3Rpb25zIG9mIHZhcmlhYmxlcyBhcmUgZXZhbHVhdGVkIG9uY2UgcGVyIGRhdGEgZnJhbWUsIG5vdCBvbmNlIHBlciBncm91cC4KCmBgYHtyfQphcnJhbmdlKGlyaXMsIFNlcGFsLkxlbmd0aCwgZGVzYyhTZXBhbC5XaWR0aCkpCmBgYAoKIyMgYHNsaWNlLCBzbGljZV9oZWFkLCBzbGljZV90YWlsLCBzbGljZV9taW4sIHNsaWNlX21heCwgc2xpY2Vfc2FtcGxlYAoKYGBge3J9CnNsaWNlX21pbihpcmlzLCBTZXBhbC5MZW5ndGgsIG4gPSA2KQpgYGAKCiMgW2BwaXBlc2BdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovcGlwZXMuaHRtbClgKFI0RFMpYAoKIyBbYG11dGF0ZWBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbXV0YXRlLmh0bWwpCgotICAgQ3JlYXRlLCBtb2RpZnksIGFuZCBkZWxldGUgY29sdW1ucwoKLSAgIFVzZWZ1bCBtdXRhdGUgZnVuY3Rpb25zCgogICAgLSAgICssIC0sIGxvZygpLCBldGMuLCBmb3IgdGhlaXIgdXN1YWwgbWF0aGVtYXRpY2FsIG1lYW5pbmdzCgogICAgLSAgIGxlYWQoKSwgbGFnKCkKCiAgICAtICAgZGVuc2VfcmFuaygpLCBtaW5fcmFuaygpLCBwZXJjZW50X3JhbmsoKSwgcm93X251bWJlcigpLCBjdW1lX2Rpc3QoKSwgbnRpbGUoKQoKICAgIC0gICBjdW1zdW0oKSwgY3VtbWVhbigpLCBjdW1taW4oKSwgY3VtbWF4KCksIGN1bWFueSgpLCBjdW1hbGwoKQoKICAgIC0gICBuYV9pZigpLCBjb2FsZXNjZSgpCgpgYGB7cn0KbXV0YXRlKGlyaXMsIHJhbmsgPSBtaW5fcmFuayhTZXBhbC5MZW5ndGgpKSAlPiUKICBhcnJhbmdlKHJhbmspCmBgYAoKYGBge3J9CmlyaXMgJT4lIG11dGF0ZShTZXBhbC5SYXRpbyA9IFNlcGFsLkxlbmd0aC9TZXBhbC5XaWR0aCkgJT4lCiAgYXJyYW5nZShTZXBhbC5SYXRpbykKYGBgCgojIFtgZ3JvdXBfYnlgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dyb3VwX2J5Lmh0bWwpCgpNb3N0IGRhdGEgb3BlcmF0aW9ucyBhcmUgZG9uZSBvbiBncm91cHMgZGVmaW5lZCBieSB2YXJpYWJsZXMuwqBgZ3JvdXBfYnkoKWDCoHRha2VzIGFuIGV4aXN0aW5nIHRibCBhbmQgY29udmVydHMgaXQgaW50byBhIGdyb3VwZWQgdGJsIHdoZXJlIG9wZXJhdGlvbnMgYXJlIHBlcmZvcm1lZCAiYnkgZ3JvdXAiLsKgYHVuZ3JvdXAoKWByZW1vdmVzIGdyb3VwaW5nLgoKIyBbYHN1bW1hcmlzZWAgb3IgYHN1bW1hcml6ZWBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc3VtbWFyaXNlLmh0bWwpCgojIyBTdW1tYXJ5IGZ1bmN0aW9ucyB7LnVubnVtYmVyZWR9CgpZb3UgY2FuIHVzZSBhbnkgZnVuY3Rpb24gaW4gYHN1bW1hcmlzZSgpYCBzbyBsb25nIGFzIGl0IG1lZXRzIG9uZSBjcml0ZXJpYTogdGhlIGZ1bmN0aW9uIG11c3QgdGFrZSBhIHZlY3RvciBvZiB2YWx1ZXMgYXMgaW5wdXQgYW5kIHJldHVybiBhIHNpbmdsZSB2YWx1ZSBhcyBvdXRwdXQuIEZ1bmN0aW9ucyB0aGF0IGRvIHRoaXMgYXJlIGtub3duIGFzIHN1bW1hcnkgZnVuY3Rpb25zIGFuZCB0aGV5IGFyZSBjb21tb24gaW4gdGhlIGZpZWxkIG9mIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MuIFNvbWUgb2YgdGhlIG1vc3QgdXNlZnVsIHN1bW1hcnkgZnVuY3Rpb25zIGluY2x1ZGU6CgoxLiAgTWVhc3VyZXMgb2YgbG9jYXRpb24gLSBtZWFuKHgpLCBtZWRpYW4oeCksIHF1YW50aWxlKHgsIDAuMjUpLCBtaW4oeCksIGFuZCBtYXgoeCkKMi4gIE1lYXN1cmVzIG9mIHNwcmVhZCAtIHNkKHgpLCB2YXIoeCksIElRUih4KSwgYW5kIG1hZCh4KQozLiAgTWVhc3VyZXMgb2YgcG9zaXRpb24gLSBmaXJzdCh4KSwgbnRoKHgsIDIpLCBhbmQgbGFzdCh4KQo0LiAgQ291bnRzIC0gbl9kaXN0aW5jdCh4KSBhbmQgbigpLCB3aGljaCB0YWtlcyBubyBhcmd1bWVudHMsIGFuZCByZXR1cm5zIHRoZSBzaXplIG9mIHRoZSBjdXJyZW50IGdyb3VwIG9yIGRhdGEgZnJhbWUuCjUuICBDb3VudHMgYW5kIHByb3BvcnRpb25zIG9mIGxvZ2ljYWwgdmFsdWVzIC0gc3VtKCFpcy5uYSh4KSksIHdoaWNoIGNvdW50cyB0aGUgbnVtYmVyIG9mIFRSVUVzIHJldHVybmVkIGJ5IGEgbG9naWNhbCB0ZXN0OyBtZWFuKHkgPT0gMCksIHdoaWNoIHJldHVybnMgdGhlIHByb3BvcnRpb24gb2YgVFJVRXMgcmV0dXJuZWQgYnkgYSBsb2dpY2FsIHRlc3QuCjYuICBpZl9lbHNlKCksIHJlY29kZSgpLCBjYXNlX3doZW4oKQoKYGBge3J9CmlyaXMgJT4lIAogIGdyb3VwX2J5KFNwZWNpZXMpICU+JSAKICBzdW1tYXJpemUoc2xfbWVhbiA9IG1lYW4oU2VwYWwuTGVuZ3RoKSwgc3dfbWVhbiA9IG1lYW4oU2VwYWwuV2lkdGgpLCAKICBwbF9tZWFuID0gbWVhbihQZXRhbC5MZW5ndGgpLCBwd19tZWFuID0gbWVhbihQZXRhbC5XaWR0aCkpCmBgYAoKYGBge3J9CmlyaXMgJT4lIAogIGdyb3VwX2J5KFNwZWNpZXMpICU+JQogIG11dGF0ZShyYW5rID0gbWluX3JhbmsoU2VwYWwuTGVuZ3RoKSkgJT4lCiAgYXJyYW5nZShyYW5rKQpgYGAKCiMgUG9zaXQgUHJpbWVycwoKUGxlYXNlIHByYWN0aWNlIHdpdGggUG9zaXQgUHJpbWVycyAtIFdvcmsgd2l0aCBEYXRhCgpbV29yayB3aXRoIERhdGFdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vcHJpbWVycy8yKQoKPiBMZWFybiB0aGUgbW9zdCBpbXBvcnRhbnQgZGF0YSBoYW5kbGluZyBza2lsbHMgaW4gUjogaG93IHRvIGV4dHJhY3QgdmFsdWVzIGZyb20gYSB0YWJsZSwgc3Vic2V0IHRhYmxlcywgY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcywgYW5kIGRlcml2ZSBuZXcgdmFyaWFibGVzLgoKW1dvcmtpbmcgd2l0aCBUaWJibGVzXShodHRwczovL3Bvc2l0LmNsb3VkL2xlYXJuL3ByaW1lcnMvMi4xKQoKTGVhcm4gdG8gdXNlIHRpYmJsZXMsIHRoZSBtb3N0IHVzZXItZnJpZW5kbHkgdGFidWxhciBkYXRhIHN0cnVjdHVyZSBpbiBSLCBhcyB3ZWxsIGFzIGhvdyB0byBtYW5hZ2UgdGlkeXZlcnNlIHBhY2thZ2VzIHdpdGhcLi4uIHRoZSB0aWR5dmVyc2UgcGFja2FnZS4KCltJc29sYXRpbmcgRGF0YSB3aXRoIGRwbHlyXShodHRwczovL3Bvc2l0LmNsb3VkL2xlYXJuL3ByaW1lcnMvMi4yKQoKTWFzdGVyIHRocmVlIHNpbXBsZSBmdW5jdGlvbnMgZm9yIGZpbmRpbmcsIGFuZCBleHRyYWN0aW5nLCB0aGUgZGF0YSBpbiB5b3VyIGRhdGEgc2V0LiBIZXJlIHlvdSB3aWxsIGxlYXJuIHRvIHNlbGVjdCB2YXJpYWJsZXMsIGZpbHRlciBvYnNlcnZhdGlvbnMsIGFuZCBhcnJhbmdlIHZhbHVlcy4gSGVyZSwgeW91IHdpbGwgYWxzbyBtZWV0IFIncyBwaXBlIG9wZXJhdG9yLCAlXD4lLgoKW0Rlcml2aW5nIEluZm9ybWF0aW9uIHdpdGggZHBseXJdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vcHJpbWVycy8yLjMpCgpEYXRhIHNldHMgY29udGFpbiBtb3JlIGluZm9ybWF0aW9uIHRoYW4gdGhleSBkaXNwbGF5LCBhbmQgdGhpcyB0dXRvcmlhbCB3aWxsIHNob3cgeW91IGhvdyB0byBhY2Nlc3MgdGhhdCBpbmZvcm1hdGlvbi4gWW91J2xsIGxlYXJuIHRvIGRlcml2ZSBuZXcgdmFyaWFibGVzIGFuZCB0byBjb21wdXRlIGdyb3Vwd2lzZSBzdW1tYXJ5IHN0YXRpc3RpY3MuCg==