In this lesson, you’ll learn how to manipulate data using dplyr. dplyr is a fast and powerful R package written by Hadley Wickham and Romain Francois that provides a consistent and concise grammar for manipulating tabular data.

One unique aspect of dplyr is that the same set of tools allow you to work with tabular data from a variety of sources, including data frames, data tables, databases and multidimensional arrays. In this lesson, we’ll focus on data frames, but everything you learn will apply equally to other formats.

As you may know, “CRAN is a network of ftp and web servers around the world that store identical, up-to-date, versions of code and documentation for R” (http://cran.rstudio.com/). RStudio maintains one of these so-called ‘CRAN mirrors’ and they generously make their download logs publicly available (http://cran-logs.rstudio.com/).

We’ll be working with the log from July 8, 2014, which contains information on roughly 225,000 package downloads.

I’ve created a variable called path2csv, which contains the full file path to the dataset. Call read.csv() with two arguments, path2csv and stringsAsFactors = FALSE, and save the result in a new variable called mydf. Check ?read.csv if you need help.

path2csv <- "/home/cabunic/R/x86_64-pc-linux-gnu-library/3.4/swirl/Courses/Getting_and_Cleaning_Data/Manipulating_Data_with_dplyr/2014-07-08.csv"
mydf <- read.csv(path2csv, stringsAsFactors = FALSE)


Use dim() to look at the dimensions of mydf.

dim(mydf)
[1] 225468     11


Now use head() to preview the data.

head(mydf)


The dplyr package was automatically installed (if necessary) and loaded at the beginning of this lesson. Normally, this is something you would have to do on your own. Just to build the habit, type library(dplyr) now to load the package again.

library(dplyr)


It’s important that you have dplyr version 0.4.0 or later. To confirm this, type packageVersion(“dplyr”).

If your dplyr version is not at least 0.4.0, then you should hit the Esc key now, reinstall dplyr, then resume this lesson where you left off.

packageVersion("dplyr")
[1] ‘0.7.4’


The first step of working with data in dplyr is to load the data into what the package authors call a ‘data frame tbl’ or ‘tbl_df’. Use the following code to create a new tbl_df called cran: cran <- tbl_df(mydf).

cran <- tbl_df(mydf)


To avoid confusion and keep things running smoothly, let’s remove the original data frame from your workspace with rm(“mydf”).

rm("mydf")


From ?tbl_df, “The main advantage to using a tbl_df over a regular data frame is the printing.” Let’s see what is meant by this. Type cran to print our tbl_df to the console.

cran


This output is much more informative and compact than what we would get if we printed the original data frame (mydf) to the console.

First, we are shown the class and dimensions of the dataset. Just below that, we get a preview of the data. Instead of attempting to print the entire dataset, dplyr just shows us the first 10 rows of data and only as many columns as fit neatly in our console. At the bottom, we see the names and classes for any variables that didn’t fit on our screen.

According to the “Introduction to dplyr” vignette written by the package authors, “The dplyr philosophy is to have small functions that each do one thing well.”

Specifically, dplyr supplies five ‘verbs’ that cover most fundamental data manipulation tasks:

  1. select()

  2. filter()

  3. arrange()

  4. mutate()

  5. summarize()



select()


Use ?select to pull up the documentation for the first of these core functions.

?select


Help files for the other functions are accessible in the same way.

As may often be the case, particularly with larger datasets, we are only interested in some of the variables. Use select(cran, ip_id, package, country) to select only the ip_id, package, and country variables from the cran dataset.

select(cran, ip_id, package, country)


The first thing to notice is that we don’t have to type cran\(ip_id, cran\)package, and cran$country, as we normally would when referring to columns of a data frame. The select() function knows we are referring to columns of the cran dataset.

Also, note that the columns are returned to us in the order we specified, even though ip_id is the rightmost column in the original dataset.

Recall that in R, the : operator provides a compact notation for creating a sequence of numbers. For example, try 5:20.

5:20
 [1]  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20


Normally, this notation is reserved for numbers, but select() allows you to specify a sequence of columns this way, which can save a bunch of typing. Use select(cran, r_arch:country) to select all columns starting from r_arch and ending with country.

select(cran, r_arch:country)


We can also select the same columns in reverse order. Give it a try.

select(cran, country:r_arch)


Print the entire dataset again, just to remind yourself of what it looks like. You can do this at anytime during the lesson.

cran


Instead of specifying the columns we want to keep, we can also specify the columns we want to throw away. To see how this works, do select(cran, -time) to omit the time column.

select(cran, -time)


The negative sign in front of time tells select() that we DON’T want the time column. Now, let’s combine strategies to omit all columns from X through size (X:size). To see how this might work, let’s look at a numerical example with -5:20.

-5:20
 [1] -5 -4 -3 -2 -1  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20


Oops! That gaves us a vector of numbers from -5 through 20, which is not what we want. Instead, we want to negate the entire sequence of numbers from 5 through 20, so that we get -5, -6, -7, … , -18, -19, -20. Try the same thing, except surround 5:20 with parentheses so that R knows we want it to first come up with the sequence of numbers, then apply the negative sign to the whole thing.

-(5:20)
 [1]  -5  -6  -7  -8  -9 -10 -11 -12 -13 -14 -15 -16 -17 -18 -19 -20


Use this knowledge to omit all columns X:size using select().

select(cran, -(X:size))



filter()


Now that you know how to select a subset of columns using select(), a natural next question is “How do I select a subset of rows?” That’s where the filter() function comes in.

Use filter(cran, package == “swirl”) to select all rows for which the package variable is equal to “swirl”. Be sure to use two equals signs side-by-side!

filter(cran, package == "swirl")


Again, note that filter() recognizes ‘package’ as a column of cran, without you having to explicitly specify cran$package.

The == operator asks whether the thing on the left is equal to the thing on the right. If yes, then it returns TRUE. If no, then FALSE. In this case, package is an entire vector (column) of values, so package == “swirl” returns a vector of TRUEs and FALSEs. filter() then returns only the rows of cran corresponding to the TRUEs.

You can specify as many conditions as you want, separated by commas. For example filter(cran, r_version == “3.1.1”, country == “US”) will return all rows of cran corresponding to downloads from users in the US running R version 3.1.1. Try it out.

filter(cran, r_version == "3.1.1", country == "US")


The conditions passed to filter() can make use of any of the standard comparison operators. Pull up the relevant documentation with ?Comparison (that’s an uppercase C).

?Comparison


Edit your previous call to filter() to instead return rows corresponding to users in “IN” (India) running an R version that is less than or equal to “3.0.2”. The up arrow on your keyboard may come in handy here. Don’t forget your double quotes!

filter(cran, r_version <= "3.0.2", country == "IN")


Our last two calls to filter() requested all rows for which some condition AND another condition were TRUE. We can also request rows for which EITHER one condition OR another condition are TRUE. For example, filter(cran, country == “US” | country == “IN”) will gives us all rows for which the country variable equals either “US” or “IN”. Give it a go.

filter(cran, country == "US" | country == "IN")


Now, use filter() to fetch all rows for which size is strictly greater than (>) 100500 (no quotes, since size is numeric) AND r_os equals “linux-gnu”. Hint: You are passing three arguments to filter(): the name of the dataset, the first condition, and the second condition.

filter(cran, size > 100500, r_os == "linux-gnu")


Finally, we want to get only the rows for which the r_version is not missing. R represents missing values with NA and these missing values can be detected using the is.na() function.

To see how this works, try is.na(c(3, 5, NA, 10)).

is.na(c(3, 5, NA, 10))
[1] FALSE FALSE  TRUE FALSE


Now, put an exclamation point (!) before is.na() to change all of the TRUEs to FALSEs and all of the FALSEs to TRUEs, thus telling us what is NOT NA: !is.na(c(3, 5, NA, 10)).

!is.na(c(3, 5, NA, 10))
[1]  TRUE  TRUE FALSE  TRUE


Okay, ready to put all of this together? Use filter() to return all rows of cran for which r_version is NOT NA. Hint: You will need to use !is.na() as part of your second argument to filter().

filter(cran, !is.na(r_version))


We’ve seen how to select a subset of columns and rows from our dataset using select() and filter(), respectively. Inherent in select() was also the ability to arrange our selected columns in any order we please.



arrange()


Sometimes we want to order the rows of a dataset according to the values of a particular variable. This is the job of arrange().

To see how arrange() works, let’s first take a subset of cran. select() all columns from size through ip_id and store the result in cran2.

cran2 <- select(cran, size:ip_id)


Now, to order the ROWS of cran2 so that ip_id is in ascending order (from small to large), type arrange(cran2, ip_id). You may want to make your console wide enough so that you can see ip_id, which is the last column.

arrange(cran2, ip_id)


To do the same, but in descending order, change the second argument to desc(ip_id), where desc() stands for ‘descending’. Go ahead.

arrange(cran2, desc(ip_id))


We can also arrange the data according to the values of multiple variables. For example, arrange(cran2, package, ip_id) will first arrange by package names (ascending alphabetically), then by ip_id. This means that if there are multiple rows with the same value for package, they will be sorted by ip_id (ascending numerically). Try arrange(cran2, package, ip_id) now.

arrange(cran2, package, ip_id)
arrange(cran2, country, desc(r_version), ip_id)



mutate()


To illustrate the next major function in dplyr, let’s take another subset of our original data. Use select() to grab 3 columns from cran – ip_id, package, and size (in that order) – and store the result in a new variable called cran3.

cran3 <- select(cran, ip_id, package, size)


Take a look at cran3 now.

cran3


It’s common to create a new variable based on the value of one or more variables already in a dataset. The mutate() function does exactly this.

The size variable represents the download size in bytes, which are units of computer memory. These days, megabytes (MB) are a more common unit of measurement. One megabyte is equal to 2^20 bytes. That’s 2 to the power of 20, which is approximately one million bytes!

We want to add a column called size_mb that contains the download size in megabytes. Here’s the code to do it: mutate(cran3, size_mb = size / 2^20)

mutate(cran3, size_mb = size / 2^20)


An even larger unit of memory is a gigabyte (GB), which equals 2^10 megabytes. We might as well add another column for download size in gigabytes!

One very nice feature of mutate() is that you can use the value computed for your second column (size_mb) to create a third column, all in the same line of code. To see this in action, repeat the exact same command as above, except add a third argument creating a column that is named size_gb and equal to size_mb / 2^10.

mutate(cran3, size_mb = size/2^20, size_gb = size_mb/2^10)


Let’s try one more for practice. Pretend we discovered a glitch in the system that provided the original values for the size variable. All of the values in cran3 are 1000 bytes less than they should be. Using cran3, create just one new column called correct_size that contains the correct size.

mutate(cran3, correct_size = size + 1000)



summarize()


The last of the five core dplyr verbs, summarize(), collapses the dataset to a single row. Let’s say we’re interested in knowing the average download size. summarize(cran, avg_bytes = mean(size)) will yield the mean value of the size variable. Here we’ve chosen to label the result ‘avg_bytes’, but we could have named it anything. Give it a try.

summarize(cran, avg_bytes = mean(size))


That’s not particularly interesting. summarize() is most useful when working with data that has been grouped by the values of a particular variable.

We’ll look at grouped data in the next lesson, but the idea is that summarize() can give you the requested value FOR EACH group in your dataset.

In this lesson, you learned how to manipulate data using dplyr’s five main functions. In the next lesson, we’ll look at how to take advantage of some other useful features of dplyr to make your life as a data analyst much easier.



END

LS0tClN3aXJsIERhdGEgV3JhbmdsaW5nIExlc3NvbiAxOgp0aXRsZTogIk1hbmlwdWxhdGluZyBEYXRhIHdpdGggZHBseXIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KICAgIAo8YnIgLz4KICAKSW4gdGhpcyBsZXNzb24sIHlvdSdsbCBsZWFybiBob3cgdG8gbWFuaXB1bGF0ZSBkYXRhIHVzaW5nIGRwbHlyLiBkcGx5ciBpcyBhIGZhc3QgYW5kIHBvd2VyZnVsIFIgcGFja2FnZSB3cml0dGVuIGJ5IEhhZGxleSBXaWNraGFtIGFuZCBSb21haW4gRnJhbmNvaXMgdGhhdCBwcm92aWRlcyBhIGNvbnNpc3RlbnQgYW5kIGNvbmNpc2UgZ3JhbW1hciBmb3IgbWFuaXB1bGF0aW5nIHRhYnVsYXIgZGF0YS4KCk9uZSB1bmlxdWUgYXNwZWN0IG9mIGRwbHlyIGlzIHRoYXQgdGhlIHNhbWUgc2V0IG9mIHRvb2xzIGFsbG93IHlvdSB0byB3b3JrIHdpdGggdGFidWxhciBkYXRhIGZyb20gYSB2YXJpZXR5IG9mIHNvdXJjZXMsIGluY2x1ZGluZyBkYXRhIGZyYW1lcywgZGF0YSB0YWJsZXMsIGRhdGFiYXNlcyBhbmQgbXVsdGlkaW1lbnNpb25hbCBhcnJheXMuIEluIHRoaXMgbGVzc29uLCB3ZSdsbCBmb2N1cyBvbiBkYXRhIGZyYW1lcywgYnV0IGV2ZXJ5dGhpbmcgeW91IGxlYXJuIHdpbGwgYXBwbHkgZXF1YWxseSB0byBvdGhlciBmb3JtYXRzLgoKQXMgeW91IG1heSBrbm93LCAiQ1JBTiBpcyBhIG5ldHdvcmsgb2YgZnRwIGFuZCB3ZWIgc2VydmVycyBhcm91bmQgdGhlIHdvcmxkIHRoYXQgc3RvcmUgaWRlbnRpY2FsLCB1cC10by1kYXRlLCB2ZXJzaW9ucyBvZiBjb2RlIGFuZCBkb2N1bWVudGF0aW9uIGZvciBSIiAoaHR0cDovL2NyYW4ucnN0dWRpby5jb20vKS4gUlN0dWRpbyBtYWludGFpbnMgb25lIG9mIHRoZXNlIHNvLWNhbGxlZCAnQ1JBTiBtaXJyb3JzJyBhbmQgdGhleSBnZW5lcm91c2x5IG1ha2UgdGhlaXIgZG93bmxvYWQgbG9ncyBwdWJsaWNseSBhdmFpbGFibGUgKGh0dHA6Ly9jcmFuLWxvZ3MucnN0dWRpby5jb20vKS4KCldlJ2xsIGJlIHdvcmtpbmcgd2l0aCB0aGUgbG9nIGZyb20gSnVseSA4LCAyMDE0LCB3aGljaCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiByb3VnaGx5IDIyNSwwMDAgcGFja2FnZSBkb3dubG9hZHMuCgpJJ3ZlIGNyZWF0ZWQgYSB2YXJpYWJsZSBjYWxsZWQgcGF0aDJjc3YsIHdoaWNoIGNvbnRhaW5zIHRoZSBmdWxsIGZpbGUgcGF0aCB0byB0aGUgZGF0YXNldC4gQ2FsbCByZWFkLmNzdigpIHdpdGggdHdvIGFyZ3VtZW50cywgcGF0aDJjc3YgYW5kIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwgYW5kIHNhdmUgdGhlIHJlc3VsdCBpbiBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgbXlkZi4gQ2hlY2sgP3JlYWQuY3N2IGlmIHlvdSBuZWVkIGhlbHAuCgpgYGB7cn0KcGF0aDJjc3YgPC0gIi9ob21lL2NhYnVuaWMvUi94ODZfNjQtcGMtbGludXgtZ251LWxpYnJhcnkvMy40L3N3aXJsL0NvdXJzZXMvR2V0dGluZ19hbmRfQ2xlYW5pbmdfRGF0YS9NYW5pcHVsYXRpbmdfRGF0YV93aXRoX2RwbHlyLzIwMTQtMDctMDguY3N2IgoKbXlkZiA8LSByZWFkLmNzdihwYXRoMmNzdiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpgYGAKCjxiciAvPgpVc2UgZGltKCkgdG8gbG9vayBhdCB0aGUgZGltZW5zaW9ucyBvZiBteWRmLgoKYGBge3J9CmRpbShteWRmKQpgYGAKCjxiciAvPgpOb3cgdXNlIGhlYWQoKSB0byBwcmV2aWV3IHRoZSBkYXRhLgoKYGBge3J9CmhlYWQobXlkZikKYGBgCgo8YnIgLz4KVGhlIGRwbHlyIHBhY2thZ2Ugd2FzIGF1dG9tYXRpY2FsbHkgaW5zdGFsbGVkIChpZiBuZWNlc3NhcnkpIGFuZCBsb2FkZWQgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGlzIGxlc3Nvbi4gTm9ybWFsbHksIHRoaXMgaXMgc29tZXRoaW5nIHlvdSB3b3VsZCBoYXZlIHRvIGRvIG9uIHlvdXIgb3duLiBKdXN0IHRvIGJ1aWxkIHRoZSBoYWJpdCwgdHlwZSBsaWJyYXJ5KGRwbHlyKSBub3cgdG8gbG9hZCB0aGUgcGFja2FnZSBhZ2Fpbi4KCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpgYGAKCjxiciAvPgpJdCdzIGltcG9ydGFudCB0aGF0IHlvdSBoYXZlIGRwbHlyIHZlcnNpb24gMC40LjAgb3IgbGF0ZXIuIFRvIGNvbmZpcm0gdGhpcywgdHlwZSBwYWNrYWdlVmVyc2lvbigiZHBseXIiKS4KCklmIHlvdXIgZHBseXIgdmVyc2lvbiBpcyBub3QgYXQgbGVhc3QgMC40LjAsIHRoZW4geW91IHNob3VsZCBoaXQgdGhlIEVzYyBrZXkgbm93LCByZWluc3RhbGwgZHBseXIsIHRoZW4gcmVzdW1lIHRoaXMgbGVzc29uIHdoZXJlIHlvdSBsZWZ0IG9mZi4KCmBgYHtyfQpwYWNrYWdlVmVyc2lvbigiZHBseXIiKQpgYGAKCjxiciAvPgpUaGUgZmlyc3Qgc3RlcCBvZiB3b3JraW5nIHdpdGggZGF0YSBpbiBkcGx5ciBpcyB0byBsb2FkIHRoZSBkYXRhIGludG8gd2hhdCB0aGUgcGFja2FnZSBhdXRob3JzIGNhbGwgYSAnZGF0YSBmcmFtZSB0YmwnIG9yICd0YmxfZGYnLiBVc2UgdGhlIGZvbGxvd2luZyBjb2RlIHRvIGNyZWF0ZSBhIG5ldyB0YmxfZGYgY2FsbGVkIGNyYW46IGNyYW4gPC0gdGJsX2RmKG15ZGYpLgoKYGBge3J9CmNyYW4gPC0gdGJsX2RmKG15ZGYpCmBgYAoKPGJyIC8+ClRvIGF2b2lkIGNvbmZ1c2lvbiBhbmQga2VlcCB0aGluZ3MgcnVubmluZyBzbW9vdGhseSwgbGV0J3MgcmVtb3ZlIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lIGZyb20geW91ciB3b3Jrc3BhY2Ugd2l0aCBybSgibXlkZiIpLgoKYGBge3J9CnJtKCJteWRmIikKYGBgCgo8YnIgLz4KRnJvbSA/dGJsX2RmLCAiVGhlIG1haW4gYWR2YW50YWdlIHRvIHVzaW5nIGEgdGJsX2RmIG92ZXIgYSByZWd1bGFyIGRhdGEgZnJhbWUgaXMgdGhlIHByaW50aW5nLiIgTGV0J3Mgc2VlIHdoYXQgaXMgbWVhbnQgYnkgdGhpcy4gVHlwZSBjcmFuIHRvIHByaW50IG91ciB0YmxfZGYgdG8gdGhlIGNvbnNvbGUuCgpgYGB7cn0KY3JhbgpgYGAKCjxiciAvPgpUaGlzIG91dHB1dCBpcyBtdWNoIG1vcmUgaW5mb3JtYXRpdmUgYW5kIGNvbXBhY3QgdGhhbiB3aGF0IHdlIHdvdWxkIGdldCBpZiB3ZSBwcmludGVkIHRoZSBvcmlnaW5hbCBkYXRhIGZyYW1lIChteWRmKSB0byB0aGUgY29uc29sZS4KCkZpcnN0LCB3ZSBhcmUgc2hvd24gdGhlIGNsYXNzIGFuZCBkaW1lbnNpb25zIG9mIHRoZSBkYXRhc2V0LiBKdXN0IGJlbG93IHRoYXQsIHdlIGdldCBhIHByZXZpZXcgb2YgdGhlIGRhdGEuIEluc3RlYWQgb2YgYXR0ZW1wdGluZyB0byBwcmludCB0aGUgZW50aXJlIGRhdGFzZXQsIGRwbHlyIGp1c3Qgc2hvd3MgdXMgdGhlIGZpcnN0IDEwIHJvd3Mgb2YgZGF0YSBhbmQgb25seSBhcyBtYW55IGNvbHVtbnMgYXMgZml0IG5lYXRseSBpbiBvdXIgY29uc29sZS4gQXQgdGhlIGJvdHRvbSwgd2Ugc2VlIHRoZSBuYW1lcyBhbmQgY2xhc3NlcyBmb3IgYW55IHZhcmlhYmxlcyB0aGF0IGRpZG4ndCBmaXQgb24gb3VyIHNjcmVlbi4KCkFjY29yZGluZyB0byB0aGUgIkludHJvZHVjdGlvbiB0byBkcGx5ciIgdmlnbmV0dGUgd3JpdHRlbiBieSB0aGUgcGFja2FnZSBhdXRob3JzLCAiVGhlIGRwbHlyIHBoaWxvc29waHkgaXMgdG8gaGF2ZSBzbWFsbCBmdW5jdGlvbnMgdGhhdCBlYWNoIGRvIG9uZSB0aGluZyB3ZWxsLiIKClNwZWNpZmljYWxseSwgZHBseXIgc3VwcGxpZXMgZml2ZSAndmVyYnMnIHRoYXQgY292ZXIgbW9zdCBmdW5kYW1lbnRhbCBkYXRhIG1hbmlwdWxhdGlvbiB0YXNrczogCgoxLiBzZWxlY3QoKQoKMi4gZmlsdGVyKCkKCjMuIGFycmFuZ2UoKQoKNC4gbXV0YXRlKCkKCjUuIHN1bW1hcml6ZSgpCgoKPGJyIC8+CgotLS0KCiMjIyBzZWxlY3QoKQoKPGJyIC8+ClVzZSA/c2VsZWN0IHRvIHB1bGwgdXAgdGhlIGRvY3VtZW50YXRpb24gZm9yIHRoZSBmaXJzdCBvZiB0aGVzZSBjb3JlIGZ1bmN0aW9ucy4KCmBgYHtyfQo/c2VsZWN0CmBgYAoKPGJyIC8+CkhlbHAgZmlsZXMgZm9yIHRoZSBvdGhlciBmdW5jdGlvbnMgYXJlIGFjY2Vzc2libGUgaW4gdGhlIHNhbWUgd2F5LgoKQXMgbWF5IG9mdGVuIGJlIHRoZSBjYXNlLCBwYXJ0aWN1bGFybHkgd2l0aCBsYXJnZXIgZGF0YXNldHMsIHdlIGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gc29tZSBvZiB0aGUgdmFyaWFibGVzLiBVc2Ugc2VsZWN0KGNyYW4sIGlwX2lkLCBwYWNrYWdlLCBjb3VudHJ5KSB0byBzZWxlY3Qgb25seSB0aGUgaXBfaWQsIHBhY2thZ2UsIGFuZCBjb3VudHJ5IHZhcmlhYmxlcyBmcm9tIHRoZSBjcmFuIGRhdGFzZXQuCgpgYGB7cn0Kc2VsZWN0KGNyYW4sIGlwX2lkLCBwYWNrYWdlLCBjb3VudHJ5KQpgYGAKCjxiciAvPgpUaGUgZmlyc3QgdGhpbmcgdG8gbm90aWNlIGlzIHRoYXQgd2UgZG9uJ3QgaGF2ZSB0byB0eXBlIGNyYW4kaXBfaWQsIGNyYW4kcGFja2FnZSwgYW5kIGNyYW4kY291bnRyeSwgYXMgd2Ugbm9ybWFsbHkgd291bGQgd2hlbiByZWZlcnJpbmcgdG8gY29sdW1ucyBvZiBhIGRhdGEgZnJhbWUuIFRoZSBzZWxlY3QoKSBmdW5jdGlvbiBrbm93cyB3ZSBhcmUgcmVmZXJyaW5nIHRvIGNvbHVtbnMgb2YgdGhlIGNyYW4gZGF0YXNldC4KCkFsc28sIG5vdGUgdGhhdCB0aGUgY29sdW1ucyBhcmUgcmV0dXJuZWQgdG8gdXMgaW4gdGhlIG9yZGVyIHdlIHNwZWNpZmllZCwgZXZlbiB0aG91Z2ggaXBfaWQgaXMgdGhlIHJpZ2h0bW9zdCBjb2x1bW4gaW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQuCgpSZWNhbGwgdGhhdCBpbiBSLCB0aGUgYDpgIG9wZXJhdG9yIHByb3ZpZGVzIGEgY29tcGFjdCBub3RhdGlvbiBmb3IgY3JlYXRpbmcgYSBzZXF1ZW5jZSBvZiBudW1iZXJzLiBGb3IgZXhhbXBsZSwgdHJ5IDU6MjAuCgpgYGB7cn0KNToyMApgYGAKCjxiciAvPgpOb3JtYWxseSwgdGhpcyBub3RhdGlvbiBpcyByZXNlcnZlZCBmb3IgbnVtYmVycywgYnV0IHNlbGVjdCgpIGFsbG93cyB5b3UgdG8gc3BlY2lmeSBhIHNlcXVlbmNlIG9mIGNvbHVtbnMgdGhpcyB3YXksIHdoaWNoIGNhbiBzYXZlIGEgYnVuY2ggb2YgdHlwaW5nLiBVc2Ugc2VsZWN0KGNyYW4sIHJfYXJjaDpjb3VudHJ5KSB0byBzZWxlY3QgYWxsIGNvbHVtbnMgc3RhcnRpbmcgZnJvbSByX2FyY2ggYW5kIGVuZGluZyB3aXRoIGNvdW50cnkuCgpgYGB7cn0Kc2VsZWN0KGNyYW4sIHJfYXJjaDpjb3VudHJ5KQpgYGAKCjxiciAvPgpXZSBjYW4gYWxzbyBzZWxlY3QgdGhlIHNhbWUgY29sdW1ucyBpbiByZXZlcnNlIG9yZGVyLiBHaXZlIGl0IGEgdHJ5LgoKYGBge3J9CnNlbGVjdChjcmFuLCBjb3VudHJ5OnJfYXJjaCkKYGBgCgo8YnIgLz4KUHJpbnQgdGhlIGVudGlyZSBkYXRhc2V0IGFnYWluLCBqdXN0IHRvIHJlbWluZCB5b3Vyc2VsZiBvZiB3aGF0IGl0IGxvb2tzIGxpa2UuIFlvdSBjYW4gZG8gdGhpcyBhdCBhbnl0aW1lIGR1cmluZyB0aGUgbGVzc29uLgoKYGBge3J9CmNyYW4KYGBgCgo8YnIgLz4KSW5zdGVhZCBvZiBzcGVjaWZ5aW5nIHRoZSBjb2x1bW5zIHdlIHdhbnQgdG8ga2VlcCwgd2UgY2FuIGFsc28gc3BlY2lmeSB0aGUgY29sdW1ucyB3ZSB3YW50IHRvIHRocm93IGF3YXkuIFRvIHNlZSBob3cgdGhpcyB3b3JrcywgZG8gc2VsZWN0KGNyYW4sIC10aW1lKSB0byBvbWl0IHRoZSB0aW1lIGNvbHVtbi4KCmBgYHtyfQpzZWxlY3QoY3JhbiwgLXRpbWUpCmBgYAoKPGJyIC8+ClRoZSBuZWdhdGl2ZSBzaWduIGluIGZyb250IG9mIHRpbWUgdGVsbHMgc2VsZWN0KCkgdGhhdCB3ZSBET04nVCB3YW50IHRoZSB0aW1lIGNvbHVtbi4gTm93LCBsZXQncyBjb21iaW5lIHN0cmF0ZWdpZXMgdG8gb21pdCBhbGwgY29sdW1ucyBmcm9tIFggdGhyb3VnaCBzaXplIChYOnNpemUpLiBUbyBzZWUgaG93IHRoaXMgbWlnaHQgd29yaywgbGV0J3MgbG9vayBhdCBhIG51bWVyaWNhbCBleGFtcGxlIHdpdGggLTU6MjAuCgpgYGB7cn0KLTU6MjAKYGBgCgo8YnIgLz4KT29wcyEgVGhhdCBnYXZlcyB1cyBhIHZlY3RvciBvZiBudW1iZXJzIGZyb20gLTUgdGhyb3VnaCAyMCwgd2hpY2ggaXMgbm90IHdoYXQgd2Ugd2FudC4gSW5zdGVhZCwgd2Ugd2FudCB0byBuZWdhdGUgdGhlIGVudGlyZSBzZXF1ZW5jZSBvZiBudW1iZXJzIGZyb20gNSB0aHJvdWdoIDIwLCBzbyB0aGF0IHdlIGdldCAtNSwgLTYsIC03LCAuLi4gLCAtMTgsIC0xOSwgLTIwLiBUcnkgdGhlIHNhbWUgdGhpbmcsIGV4Y2VwdCBzdXJyb3VuZCA1OjIwIHdpdGggcGFyZW50aGVzZXMgc28gdGhhdCBSIGtub3dzIHdlIHdhbnQgaXQgdG8gZmlyc3QgY29tZSB1cCB3aXRoIHRoZSBzZXF1ZW5jZSBvZiBudW1iZXJzLCB0aGVuIGFwcGx5IHRoZSBuZWdhdGl2ZSBzaWduIHRvIHRoZSB3aG9sZSB0aGluZy4KCmBgYHtyfQotKDU6MjApCmBgYAoKPGJyIC8+ClVzZSB0aGlzIGtub3dsZWRnZSB0byBvbWl0IGFsbCBjb2x1bW5zIFg6c2l6ZSB1c2luZyBzZWxlY3QoKS4KCmBgYHtyfQpzZWxlY3QoY3JhbiwgLShYOnNpemUpKQpgYGAKCjxiciAvPgoKLS0tCgojIyMgZmlsdGVyKCkKPGJyIC8+Ck5vdyB0aGF0IHlvdSBrbm93IGhvdyB0byBzZWxlY3QgYSBzdWJzZXQgb2YgY29sdW1ucyB1c2luZyBzZWxlY3QoKSwgYSBuYXR1cmFsIG5leHQgcXVlc3Rpb24gaXMgIkhvdyBkbyBJIHNlbGVjdCBhIHN1YnNldCBvZiByb3dzPyIgVGhhdCdzIHdoZXJlIHRoZSBmaWx0ZXIoKSBmdW5jdGlvbiBjb21lcyBpbi4KClVzZSBmaWx0ZXIoY3JhbiwgcGFja2FnZSA9PSAic3dpcmwiKSB0byBzZWxlY3QgYWxsIHJvd3MgZm9yIHdoaWNoIHRoZSBwYWNrYWdlIHZhcmlhYmxlIGlzIGVxdWFsIHRvICJzd2lybCIuIEJlIHN1cmUgdG8gdXNlIHR3byBlcXVhbHMgc2lnbnMgc2lkZS1ieS1zaWRlIQoKYGBge3J9CmZpbHRlcihjcmFuLCBwYWNrYWdlID09ICJzd2lybCIpCmBgYAoKPGJyIC8+CkFnYWluLCBub3RlIHRoYXQgZmlsdGVyKCkgcmVjb2duaXplcyAncGFja2FnZScgYXMgYSBjb2x1bW4gb2YgY3Jhbiwgd2l0aG91dCB5b3UgaGF2aW5nIHRvIGV4cGxpY2l0bHkgc3BlY2lmeSBjcmFuJHBhY2thZ2UuCgpUaGUgPT0gb3BlcmF0b3IgYXNrcyB3aGV0aGVyIHRoZSB0aGluZyBvbiB0aGUgbGVmdCBpcyBlcXVhbCB0byB0aGUgdGhpbmcgb24gdGhlIHJpZ2h0LiBJZiB5ZXMsIHRoZW4gaXQgcmV0dXJucyBUUlVFLiBJZiBubywgdGhlbiBGQUxTRS4gSW4gdGhpcyBjYXNlLCBwYWNrYWdlIGlzIGFuIGVudGlyZSB2ZWN0b3IgKGNvbHVtbikgb2YgdmFsdWVzLCBzbyBwYWNrYWdlID09ICJzd2lybCIgcmV0dXJucyBhIHZlY3RvciBvZiBUUlVFcyBhbmQgRkFMU0VzLiBmaWx0ZXIoKSB0aGVuIHJldHVybnMgb25seSB0aGUgcm93cyBvZiBjcmFuIGNvcnJlc3BvbmRpbmcgdG8gdGhlIFRSVUVzLgoKWW91IGNhbiBzcGVjaWZ5IGFzIG1hbnkgY29uZGl0aW9ucyBhcyB5b3Ugd2FudCwgc2VwYXJhdGVkIGJ5IGNvbW1hcy4gRm9yIGV4YW1wbGUgZmlsdGVyKGNyYW4sIHJfdmVyc2lvbiA9PSAiMy4xLjEiLCBjb3VudHJ5ID09ICJVUyIpIHdpbGwgcmV0dXJuIGFsbCByb3dzIG9mIGNyYW4gY29ycmVzcG9uZGluZyB0byBkb3dubG9hZHMgZnJvbSB1c2VycyBpbiB0aGUgVVMgcnVubmluZyBSIHZlcnNpb24gMy4xLjEuIFRyeSBpdCBvdXQuCgpgYGB7cn0KZmlsdGVyKGNyYW4sIHJfdmVyc2lvbiA9PSAiMy4xLjEiLCBjb3VudHJ5ID09ICJVUyIpCmBgYAoKPGJyIC8+ClRoZSBjb25kaXRpb25zIHBhc3NlZCB0byBmaWx0ZXIoKSBjYW4gbWFrZSB1c2Ugb2YgYW55IG9mIHRoZSBzdGFuZGFyZCBjb21wYXJpc29uIG9wZXJhdG9ycy4gUHVsbCB1cCB0aGUgcmVsZXZhbnQgZG9jdW1lbnRhdGlvbiB3aXRoID9Db21wYXJpc29uICh0aGF0J3MgYW4gdXBwZXJjYXNlIEMpLgoKYGBge3J9Cj9Db21wYXJpc29uCmBgYAoKPGJyIC8+CkVkaXQgeW91ciBwcmV2aW91cyBjYWxsIHRvIGZpbHRlcigpIHRvIGluc3RlYWQgcmV0dXJuIHJvd3MgY29ycmVzcG9uZGluZyB0byB1c2VycyBpbiAiSU4iIChJbmRpYSkgcnVubmluZyBhbiBSIHZlcnNpb24gdGhhdCBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gIjMuMC4yIi4gVGhlIHVwIGFycm93IG9uIHlvdXIga2V5Ym9hcmQgbWF5IGNvbWUgaW4gaGFuZHkgaGVyZS4gRG9uJ3QgZm9yZ2V0IHlvdXIgZG91YmxlIHF1b3RlcyEKCmBgYHtyfQpmaWx0ZXIoY3Jhbiwgcl92ZXJzaW9uIDw9ICIzLjAuMiIsIGNvdW50cnkgPT0gIklOIikKYGBgCgo8YnIgLz4KT3VyIGxhc3QgdHdvIGNhbGxzIHRvIGZpbHRlcigpIHJlcXVlc3RlZCBhbGwgcm93cyBmb3Igd2hpY2ggc29tZSBjb25kaXRpb24gQU5EIGFub3RoZXIgY29uZGl0aW9uIHdlcmUgVFJVRS4gV2UgY2FuIGFsc28gcmVxdWVzdCByb3dzIGZvciB3aGljaCBFSVRIRVIgb25lIGNvbmRpdGlvbiBPUiBhbm90aGVyIGNvbmRpdGlvbiBhcmUgVFJVRS4gRm9yIGV4YW1wbGUsIGZpbHRlcihjcmFuLCBjb3VudHJ5ID09ICJVUyIgfCBjb3VudHJ5ID09ICJJTiIpIHdpbGwgZ2l2ZXMgdXMgYWxsIHJvd3MgZm9yIHdoaWNoIHRoZSBjb3VudHJ5IHZhcmlhYmxlIGVxdWFscyBlaXRoZXIgIlVTIiBvciAiSU4iLiBHaXZlIGl0IGEgZ28uCgpgYGB7cn0KZmlsdGVyKGNyYW4sIGNvdW50cnkgPT0gIlVTIiB8IGNvdW50cnkgPT0gIklOIikKYGBgCgo8YnIgLz4KTm93LCB1c2UgZmlsdGVyKCkgdG8gZmV0Y2ggYWxsIHJvd3MgZm9yIHdoaWNoIHNpemUgaXMgc3RyaWN0bHkgZ3JlYXRlciB0aGFuICg+KSAxMDA1MDAgKG5vIHF1b3Rlcywgc2luY2Ugc2l6ZSBpcyBudW1lcmljKSBBTkQgcl9vcyBlcXVhbHMgImxpbnV4LWdudSIuIEhpbnQ6IFlvdSBhcmUgcGFzc2luZyB0aHJlZSBhcmd1bWVudHMgdG8gZmlsdGVyKCk6IHRoZSBuYW1lIG9mIHRoZSBkYXRhc2V0LCB0aGUgZmlyc3QgY29uZGl0aW9uLCBhbmQgdGhlIHNlY29uZCBjb25kaXRpb24uCgpgYGB7cn0KZmlsdGVyKGNyYW4sIHNpemUgPiAxMDA1MDAsIHJfb3MgPT0gImxpbnV4LWdudSIpCmBgYAoKPGJyIC8+CkZpbmFsbHksIHdlIHdhbnQgdG8gZ2V0IG9ubHkgdGhlIHJvd3MgZm9yIHdoaWNoIHRoZSByX3ZlcnNpb24gaXMgbm90IG1pc3NpbmcuIFIgcmVwcmVzZW50cyBtaXNzaW5nIHZhbHVlcyB3aXRoIE5BIGFuZCB0aGVzZSBtaXNzaW5nIHZhbHVlcyBjYW4gYmUgZGV0ZWN0ZWQgdXNpbmcgdGhlIGlzLm5hKCkgZnVuY3Rpb24uCgpUbyBzZWUgaG93IHRoaXMgd29ya3MsIHRyeSBpcy5uYShjKDMsIDUsIE5BLCAxMCkpLgoKYGBge3J9CmlzLm5hKGMoMywgNSwgTkEsIDEwKSkKYGBgCgo8YnIgLz4KTm93LCBwdXQgYW4gZXhjbGFtYXRpb24gcG9pbnQgKCEpIGJlZm9yZSBpcy5uYSgpIHRvIGNoYW5nZSBhbGwgb2YgdGhlIFRSVUVzIHRvIEZBTFNFcyBhbmQgYWxsIG9mIHRoZSBGQUxTRXMgdG8gVFJVRXMsIHRodXMgdGVsbGluZyB1cyB3aGF0IGlzIE5PVCBOQTogIWlzLm5hKGMoMywgNSwgTkEsIDEwKSkuCgpgYGB7cn0KIWlzLm5hKGMoMywgNSwgTkEsIDEwKSkKYGBgCgo8YnIgLz4KT2theSwgcmVhZHkgdG8gcHV0IGFsbCBvZiB0aGlzIHRvZ2V0aGVyPyBVc2UgZmlsdGVyKCkgdG8gcmV0dXJuIGFsbCByb3dzIG9mIGNyYW4gZm9yIHdoaWNoIHJfdmVyc2lvbiBpcyBOT1QgTkEuIEhpbnQ6IFlvdSB3aWxsIG5lZWQgdG8gdXNlICFpcy5uYSgpIGFzIHBhcnQgb2YgeW91ciBzZWNvbmQgYXJndW1lbnQgdG8gZmlsdGVyKCkuCgpgYGB7cn0KZmlsdGVyKGNyYW4sICFpcy5uYShyX3ZlcnNpb24pKQpgYGAKCjxiciAvPgpXZSd2ZSBzZWVuIGhvdyB0byBzZWxlY3QgYSBzdWJzZXQgb2YgY29sdW1ucyBhbmQgcm93cyBmcm9tIG91ciBkYXRhc2V0IHVzaW5nIHNlbGVjdCgpIGFuZCBmaWx0ZXIoKSwgcmVzcGVjdGl2ZWx5LiBJbmhlcmVudCBpbiBzZWxlY3QoKSB3YXMgYWxzbyB0aGUgYWJpbGl0eSB0byBhcnJhbmdlIG91ciBzZWxlY3RlZCBjb2x1bW5zIGluIGFueSBvcmRlciB3ZSBwbGVhc2UuCgo8YnIgLz4KCi0tLQoKIyMjIGFycmFuZ2UoKQoKPGJyIC8+ClNvbWV0aW1lcyB3ZSB3YW50IHRvIG9yZGVyIHRoZSByb3dzIG9mIGEgZGF0YXNldCBhY2NvcmRpbmcgdG8gdGhlIHZhbHVlcyBvZiBhIHBhcnRpY3VsYXIgdmFyaWFibGUuIFRoaXMgaXMgdGhlIGpvYiBvZiBhcnJhbmdlKCkuCgpUbyBzZWUgaG93IGFycmFuZ2UoKSB3b3JrcywgbGV0J3MgZmlyc3QgdGFrZSBhIHN1YnNldCBvZiBjcmFuLiBzZWxlY3QoKSBhbGwgY29sdW1ucyBmcm9tIHNpemUgdGhyb3VnaCBpcF9pZCBhbmQgc3RvcmUgdGhlIHJlc3VsdCBpbiBjcmFuMi4KCmBgYHtyfQpjcmFuMiA8LSBzZWxlY3QoY3Jhbiwgc2l6ZTppcF9pZCkKYGBgCgo8YnIgLz4KTm93LCB0byBvcmRlciB0aGUgUk9XUyBvZiBjcmFuMiBzbyB0aGF0IGlwX2lkIGlzIGluIGFzY2VuZGluZyBvcmRlciAoZnJvbSBzbWFsbCB0byBsYXJnZSksIHR5cGUgYXJyYW5nZShjcmFuMiwgaXBfaWQpLiBZb3UgbWF5IHdhbnQgdG8gbWFrZSB5b3VyIGNvbnNvbGUgd2lkZSBlbm91Z2ggc28gdGhhdCB5b3UgY2FuIHNlZSBpcF9pZCwgd2hpY2ggaXMgdGhlIGxhc3QgY29sdW1uLgoKYGBge3J9CmFycmFuZ2UoY3JhbjIsIGlwX2lkKQpgYGAKCjxiciAvPgpUbyBkbyB0aGUgc2FtZSwgYnV0IGluIGRlc2NlbmRpbmcgb3JkZXIsIGNoYW5nZSB0aGUgc2Vjb25kIGFyZ3VtZW50IHRvIGRlc2MoaXBfaWQpLCB3aGVyZSBkZXNjKCkgc3RhbmRzIGZvciAnZGVzY2VuZGluZycuIEdvIGFoZWFkLgoKYGBge3J9CmFycmFuZ2UoY3JhbjIsIGRlc2MoaXBfaWQpKQpgYGAKCjxiciAvPgpXZSBjYW4gYWxzbyBhcnJhbmdlIHRoZSBkYXRhIGFjY29yZGluZyB0byB0aGUgdmFsdWVzIG9mIG11bHRpcGxlIHZhcmlhYmxlcy4gRm9yIGV4YW1wbGUsIGFycmFuZ2UoY3JhbjIsIHBhY2thZ2UsIGlwX2lkKSB3aWxsIGZpcnN0IGFycmFuZ2UgYnkgcGFja2FnZSBuYW1lcyAoYXNjZW5kaW5nIGFscGhhYmV0aWNhbGx5KSwgdGhlbiBieSBpcF9pZC4gVGhpcyBtZWFucyB0aGF0IGlmIHRoZXJlIGFyZSBtdWx0aXBsZSByb3dzIHdpdGggdGhlIHNhbWUgdmFsdWUgZm9yIHBhY2thZ2UsIHRoZXkgd2lsbCBiZSBzb3J0ZWQgYnkgaXBfaWQgKGFzY2VuZGluZyBudW1lcmljYWxseSkuIFRyeSBhcnJhbmdlKGNyYW4yLCBwYWNrYWdlLCBpcF9pZCkgbm93LgoKYGBge3J9CmFycmFuZ2UoY3JhbjIsIHBhY2thZ2UsIGlwX2lkKQpgYGAKCmBgYHtyfQphcnJhbmdlKGNyYW4yLCBjb3VudHJ5LCBkZXNjKHJfdmVyc2lvbiksIGlwX2lkKQpgYGAKCjxiciAvPgoKLS0tCgojIyMgbXV0YXRlKCkKCjxiciAvPgpUbyBpbGx1c3RyYXRlIHRoZSBuZXh0IG1ham9yIGZ1bmN0aW9uIGluIGRwbHlyLCBsZXQncyB0YWtlIGFub3RoZXIgc3Vic2V0IG9mIG91ciBvcmlnaW5hbCBkYXRhLiBVc2Ugc2VsZWN0KCkgdG8gZ3JhYiAzIGNvbHVtbnMgZnJvbSBjcmFuIC0tIGlwX2lkLCBwYWNrYWdlLCBhbmQgc2l6ZSAoaW4gdGhhdCBvcmRlcikgLS0gYW5kIHN0b3JlIHRoZSByZXN1bHQgaW4gYSBuZXcgdmFyaWFibGUgY2FsbGVkIGNyYW4zLgoKYGBge3J9CmNyYW4zIDwtIHNlbGVjdChjcmFuLCBpcF9pZCwgcGFja2FnZSwgc2l6ZSkKYGBgCgo8YnIgLz4KVGFrZSBhIGxvb2sgYXQgY3JhbjMgbm93LgoKYGBge3J9CmNyYW4zCmBgYAoKPGJyIC8+Ckl0J3MgY29tbW9uIHRvIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSBiYXNlZCBvbiB0aGUgdmFsdWUgb2Ygb25lIG9yIG1vcmUgdmFyaWFibGVzIGFscmVhZHkgaW4gYSBkYXRhc2V0LiBUaGUgbXV0YXRlKCkgZnVuY3Rpb24gZG9lcyBleGFjdGx5IHRoaXMuCgpUaGUgc2l6ZSB2YXJpYWJsZSByZXByZXNlbnRzIHRoZSBkb3dubG9hZCBzaXplIGluIGJ5dGVzLCB3aGljaCBhcmUgdW5pdHMgb2YgY29tcHV0ZXIgbWVtb3J5LiBUaGVzZSBkYXlzLCBtZWdhYnl0ZXMgKE1CKSBhcmUgYSBtb3JlIGNvbW1vbiB1bml0IG9mIG1lYXN1cmVtZW50LiBPbmUgbWVnYWJ5dGUgaXMgZXF1YWwgdG8gMl4yMCBieXRlcy4gVGhhdCdzIDIgdG8gdGhlIHBvd2VyIG9mIDIwLCB3aGljaCBpcyBhcHByb3hpbWF0ZWx5IG9uZSBtaWxsaW9uIGJ5dGVzIQoKV2Ugd2FudCB0byBhZGQgYSBjb2x1bW4gY2FsbGVkIHNpemVfbWIgdGhhdCBjb250YWlucyB0aGUgZG93bmxvYWQgc2l6ZSBpbiBtZWdhYnl0ZXMuIEhlcmUncyB0aGUgY29kZSB0byBkbyBpdDogbXV0YXRlKGNyYW4zLCBzaXplX21iID0gc2l6ZSAvIDJeMjApCgpgYGB7cn0KbXV0YXRlKGNyYW4zLCBzaXplX21iID0gc2l6ZSAvIDJeMjApCmBgYAoKPGJyIC8+CkFuIGV2ZW4gbGFyZ2VyIHVuaXQgb2YgbWVtb3J5IGlzIGEgZ2lnYWJ5dGUgKEdCKSwgd2hpY2ggZXF1YWxzIDJeMTAgbWVnYWJ5dGVzLiBXZSBtaWdodCBhcyB3ZWxsIGFkZCBhbm90aGVyIGNvbHVtbiBmb3IgZG93bmxvYWQgc2l6ZSBpbiBnaWdhYnl0ZXMhCgpPbmUgdmVyeSBuaWNlIGZlYXR1cmUgb2YgbXV0YXRlKCkgaXMgdGhhdCB5b3UgY2FuIHVzZSB0aGUgdmFsdWUgY29tcHV0ZWQgZm9yIHlvdXIgc2Vjb25kIGNvbHVtbiAoc2l6ZV9tYikgdG8gY3JlYXRlIGEgdGhpcmQgY29sdW1uLCBhbGwgaW4gdGhlIHNhbWUgbGluZSBvZiBjb2RlLiBUbyBzZWUgdGhpcyBpbiBhY3Rpb24sIHJlcGVhdCB0aGUgZXhhY3Qgc2FtZSBjb21tYW5kIGFzIGFib3ZlLCBleGNlcHQgYWRkIGEgdGhpcmQgYXJndW1lbnQgY3JlYXRpbmcgYSBjb2x1bW4gdGhhdCBpcyBuYW1lZCBzaXplX2diIGFuZCBlcXVhbCB0byBzaXplX21iIC8gMl4xMC4KCmBgYHtyfQptdXRhdGUoY3JhbjMsIHNpemVfbWIgPSBzaXplLzJeMjAsIHNpemVfZ2IgPSBzaXplX21iLzJeMTApCmBgYAoKPGJyIC8+CkxldCdzIHRyeSBvbmUgbW9yZSBmb3IgcHJhY3RpY2UuIFByZXRlbmQgd2UgZGlzY292ZXJlZCBhIGdsaXRjaCBpbiB0aGUgc3lzdGVtIHRoYXQgcHJvdmlkZWQgdGhlIG9yaWdpbmFsIHZhbHVlcyBmb3IgdGhlIHNpemUgdmFyaWFibGUuIEFsbCBvZiB0aGUgdmFsdWVzIGluIGNyYW4zIGFyZSAxMDAwIGJ5dGVzIGxlc3MgdGhhbiB0aGV5IHNob3VsZCBiZS4gVXNpbmcgY3JhbjMsIGNyZWF0ZSBqdXN0IG9uZSBuZXcgY29sdW1uIGNhbGxlZCBjb3JyZWN0X3NpemUgdGhhdCBjb250YWlucyB0aGUgY29ycmVjdCBzaXplLgoKYGBge3J9Cm11dGF0ZShjcmFuMywgY29ycmVjdF9zaXplID0gc2l6ZSArIDEwMDApCmBgYAoKPGJyIC8+CgotLS0KCiMjIyBzdW1tYXJpemUoKQoKPGJyIC8+ClRoZSBsYXN0IG9mIHRoZSBmaXZlIGNvcmUgZHBseXIgdmVyYnMsIHN1bW1hcml6ZSgpLCBjb2xsYXBzZXMgdGhlIGRhdGFzZXQgdG8gYSBzaW5nbGUgcm93LiBMZXQncyBzYXkgd2UncmUgaW50ZXJlc3RlZCBpbiBrbm93aW5nIHRoZSBhdmVyYWdlIGRvd25sb2FkIHNpemUuIHN1bW1hcml6ZShjcmFuLCBhdmdfYnl0ZXMgPSBtZWFuKHNpemUpKSB3aWxsIHlpZWxkIHRoZSBtZWFuIHZhbHVlIG9mIHRoZSBzaXplIHZhcmlhYmxlLiBIZXJlIHdlJ3ZlIGNob3NlbiB0byBsYWJlbCB0aGUgcmVzdWx0ICdhdmdfYnl0ZXMnLCBidXQgd2UgY291bGQgaGF2ZSBuYW1lZCBpdCBhbnl0aGluZy4gR2l2ZSBpdCBhIHRyeS4KCmBgYHtyfQpzdW1tYXJpemUoY3JhbiwgYXZnX2J5dGVzID0gbWVhbihzaXplKSkKYGBgCgo8YnIgLz4KVGhhdCdzIG5vdCBwYXJ0aWN1bGFybHkgaW50ZXJlc3RpbmcuIHN1bW1hcml6ZSgpIGlzIG1vc3QgdXNlZnVsIHdoZW4gd29ya2luZyB3aXRoIGRhdGEgdGhhdCBoYXMgYmVlbiBncm91cGVkIGJ5IHRoZSB2YWx1ZXMgb2YgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlLgoKV2UnbGwgbG9vayBhdCBncm91cGVkIGRhdGEgaW4gdGhlIG5leHQgbGVzc29uLCBidXQgdGhlIGlkZWEgaXMgdGhhdCBzdW1tYXJpemUoKSBjYW4gZ2l2ZSB5b3UgdGhlIHJlcXVlc3RlZCB2YWx1ZSBGT1IgRUFDSCBncm91cCBpbiB5b3VyIGRhdGFzZXQuCgpJbiB0aGlzIGxlc3NvbiwgeW91IGxlYXJuZWQgaG93IHRvIG1hbmlwdWxhdGUgZGF0YSB1c2luZyBkcGx5cidzIGZpdmUgbWFpbiBmdW5jdGlvbnMuIEluIHRoZSBuZXh0IGxlc3Nvbiwgd2UnbGwgbG9vayBhdCBob3cgdG8gdGFrZSBhZHZhbnRhZ2Ugb2Ygc29tZSBvdGhlciB1c2VmdWwgZmVhdHVyZXMgb2YgZHBseXIgdG8gbWFrZSB5b3VyIGxpZmUgYXMgYSBkYXRhIGFuYWx5c3QgbXVjaCBlYXNpZXIuCgo8YnIgLz4KCi0tLQo8Y2VudGVyPkVORDwvY2VudGVyPgoqKio=