1.1
Functions introduced in this module:
read.csv
, select
, rename
, rm
, filter
, slice
, arrange
, mutate
, all.equal
, ifelse
, transmute
, summarise
, group_by
, %>%
Introduction
This tutorial will import some data from the web and then explore it using the amazing dplyr
package, a package which is quickly becoming the de facto standard among R users for manipulating data.
This introduction is modified based on R module “5. Manipulating data” and dplyr tutorial http://genomicsclass.github.io/book/pages/dplyr_tutorial.html.
Load packages
We load the mosaic
package as usual, but this time it is to give us access to the dplyr
package, which is loaded alongside our other mosaic
package commands.
library(mosaic)
Importing CSV data
The file we’ll import is a random sample from all the commercial domestic flights that departed from Houston, Texas, in 2011.
We use the read.csv
command to import a CSV file. In this case, we’re grabbing the file from a web page where the file is hosted. If you have a file on your computer, you can also put the file into your project directory and import it from there. Put the URL (for a web page) or the filename (for a file in your project directory) in quotes inside the read.csv
command. We also need to assign the output to a data frame, so we’ve called it hf
for “Houston flights”.
hf <- read.csv("https://raw.githubusercontent.com/VectorPosse/Intro_Stats/master/data/hf.csv")
hf
str(hf)
'data.frame': 22758 obs. of 21 variables:
$ Year : int 2011 2011 2011 2011 2011 2011 2011 2011 2011 2011 ...
$ Month : int 1 1 1 1 1 1 1 1 1 1 ...
$ DayofMonth : int 12 17 24 9 18 22 11 14 26 14 ...
$ DayOfWeek : int 3 1 1 7 2 6 2 5 3 5 ...
$ DepTime : int 1419 1530 1356 714 721 717 1953 2119 2009 1629 ...
$ ArrTime : int 1515 1634 1513 829 827 829 2051 2229 2103 1734 ...
$ UniqueCarrier : chr "AA" "AA" "AA" "AA" ...
$ FlightNum : int 428 428 428 460 460 460 533 533 533 1121 ...
$ TailNum : chr "N577AA" "N518AA" "N531AA" "N586AA" ...
$ ActualElapsedTime: int 56 64 77 75 66 72 58 70 54 65 ...
$ AirTime : int 41 48 43 51 46 47 44 45 39 47 ...
$ ArrDelay : int 5 84 3 -6 -8 -6 -29 69 -17 -11 ...
$ DepDelay : int 19 90 -4 -6 1 -3 -12 74 4 -1 ...
$ Origin : chr "IAH" "IAH" "IAH" "IAH" ...
$ Dest : chr "DFW" "DFW" "DFW" "DFW" ...
$ Distance : int 224 224 224 224 224 224 224 224 224 224 ...
$ TaxiIn : int 4 8 6 11 7 18 3 5 9 8 ...
$ TaxiOut : int 11 8 28 13 13 7 11 20 6 10 ...
$ Cancelled : int 0 0 0 0 0 0 0 0 0 0 ...
$ CancellationCode : chr "" "" "" "" ...
$ Diverted : int 0 0 0 0 0 0 0 0 0 0 ...
The one disadvantage of a file imported from the internet or your computer is that it does not come with a help file. (Only packages in R have help files.) Hopefully you have access to some kind of information about the data you’re importing. In this case, we get lucky because the full Houston flights data set happens to be available in a package called hflights
.
Introduction to dplyr
The dplyr
package (pronounced “dee-ply-er”) contains tools for manipulating the rows and columns of data frames. The key to using dplyr
is to familiarize yourself with the “key verbs”:
select
(and rename
)
filter
(and slice
)
arrange
mutate
(and transmute
)
summarise
(with group_by
)
We’ll consider these one by one. We won’t have time to cover every aspect of these functions. More information appears in the help files, as well as this very helpful “cheat sheet”: https://github.com/rstudio/cheatsheets/raw/master/data-transformation.pdf
select
The select
verb is very easy. It just selects some subset of variables (the columns of your data set). Suppose all we wanted to see was the carrier, origin, and destination. We would type
hf_select <- select(hf, UniqueCarrier, Origin, Dest)
hf_select
If you don’t like the names of the variables, you can change them as part of the select process.
hf_select <- select(hf, carrier = UniqueCarrier,
origin = Origin, dest = Dest)
hf_select
There are a few notational shortcuts. For example, see what the following do.
hf_select2 <- select(hf, DayOfWeek:UniqueCarrier)
hf_select2
hf_select3 <- select(hf, starts_with("Taxi"))
hf_select3
The rm
command
Recall that earlier we mentioned the pros and cons of creating a new data frame every time we make a change. On one hand, making a new data frame instead of overwriting the original one will keep the original one available so that we can run different commands on it. On the other hand, making a new data frame does eat up a lot of memory.
One way to get rid of an object once we are done with it is the rm
command, where rm
is short for “remove”. When you run the code chunk below, you’ll see that all the data frames we created with select
will disappear from your Global Environment.
rm(hf_select, hf_select2, hf_select3)
If you need one these data frames back later, you can always go back and re-run the code chunk that defined it.
We’ll use rm
at the end of some of the following sections so that we don’t use up too much memory.
filter
The filter
verb works a lot like select
, but for rows instead of columns.
For example, let’s say we only want to see Delta flights. We use filter
:
hf_filter <- filter(hf, UniqueCarrier == "DL")
hf_filter
In the printout of the data frame above, if you can’t see the UniqueCarrier
column, click the black arrow on the right to scroll through the columns until you can see it. You can click “Next” at the bottom to scroll through the rows.
Just like select
, the first argument is the data frame. Following that, you must specify some condition. Only rows meeting that condition will be included in the output.
Condition includes ==, <, >, !=, %in%, etc. Logical operations includes !, &, |.
As another example, suppose we wanted to find out all flights that leave before 6:00 a.m.
hf_filter2 <- filter(hf, DepTime < 600)
hf_filter2
The following will give us only the Delta flights that departed before 6:00 a.m.
hf_filter3 <- filter(hf, UniqueCarrier == "DL" & DepTime < 600)
# Another way to filter
# hf_filter3 <- filter(hf, UniqueCarrier == "DL", DepTime < 600)
hf_filter3
Again, check the cheat sheet for more complicated condition-checking.
Exercise 1
Use the filter
command to create a data frame called hf_filter4
that finds all flights except those flying into Salt Lake City (“SLC”). As before, print the output to the screen.
# Add code here to define hf_filter4.
# Add code here to print hf_filter4.
hf_filter4 <- filter(hf, Dest != "SLC")
hf_filter4
Exercise 2
Use filter with %in% to create a data frame called hf_filter5
that only contains flight information of AA, DL, and UA.
# Add code here to define hf_filter5.
# Add code here to print hf_filter5.
hf_filter5 <- filter(hf, UniqueCarrier %in% c("AA", "DL", "UA"))
hf_filter5
Now use the rm
command to remove all the extra data frames you created in this section with filter
.
rm(hf_filter, hf_filter2, hf_filter3, hf_filter4, hf_filter5)
arrange
This just re-orders the rows, sorting on the values of one or more specified columns. As I mentioned before, in most data analyses you work with summaries of the data that do not depend on the order of the rows, so this is not quite as interesting as some of the other verbs. In fact, since the re-ordering is usually for the visual benefit of the reader, there is often no need to store the output in a new variable. We’ll just print the output to the screen.
arrange(hf, ActualElapsedTime)
Scroll over to the ActualElapsedTime
variable in the output below to see that these are now sorted in ascending order.
Exercise 3
How long is the shortest actual elapsed time? Why is this flight so short? (Hint: look at the destination.) Which airline flies that route? You may have to use your best friend Google to look up airport and airline codes.
Please write up your answer here.
35 minutes to Austin because Houston and Austin are in the same state. The airline is Southwest.
If you want descending order, do this:
arrange(hf, desc(ActualElapsedTime))
mutate
Frequently, we want to create new variables that combine information from one or more existing variables. We use mutate
for this. For example, suppose we wanted to find the total time of the flight. We might do this by adding up the minutes from several variables: TaxiOut
, AirTime
, and TaxiIn
, and assigning that sum to a new variable called total
. Scroll all the way to the right in the output below to see the new total
variable.
hf_mutate <- mutate(hf, total = TaxiOut + AirTime + TaxiIn)
hf_mutate
As it turns out, that was wasted effort because that variable already exists in ActualElapsedTime
. (The all.equal
command checks that both specified columns contain the exact same values.)
all.equal(hf_mutate$total, hf$ActualElapsedTime)
[1] TRUE
Perhaps we want a variable that just classifies a flight as arriving late or not. Scroll all the way to the right to see the new late
variable.
hf_mutate2 <- mutate(hf, late = (ArrDelay > 0))
hf_mutate2
Having said that, I would generally recommend that you leave these kinds of variables as logical types. It’s much easier to summarize such variables in R, namely because R treats TRUE
as 1 and FALSE
as 0, allowing us to do things like this:
mean(hf_mutate2$late, na.rm = TRUE)
[1] 0.4761522
This gives us the percentage of late flights.
One note of explanation in the mean
command: there is an argument na.rm
. This stands for “NA remove”, which refers to R’s convention of using NA
to refer to missing values. For example, look at the 93rd row of the data frame:
hf_mutate2[93, ]
Notice that the all the times are missing. There are a bunch of rows like this. Since there is not always an arrival delay listed, the ArrDelay
variable doesn’t always have a value, and if ArrDelay
is NA
, the late
variable will be too. So if we try to calculate the mean with just the mean
command, this happens:
mean(hf_mutate2$late)
[1] NA
Therefore, we have to remove the NA
s in order to properly calculate the mean, and that’s what the na.rm
argument does.
Exercise 4
Create a new data frame called hf_mutate3
that uses the mutate
command to create a new variable called dist_k
which measures the flight distance in kilometers instead of miles. (Hint: to get from miles to kilometers, multiply the distance by 1.60934.) Print the output to the screen.
# Add code here to define hf_mutate3.
hf_mutate3 <- mutate(hf, disk_k = Distance * 1.60934)
# Add code here to print hf_mutate3.
hf_mutate3
Before moving on to the next section, we’ll clean up the extra data frames lying around:
rm(hf_mutate, hf_mutate2, hf_mutate3)
summarise
(with group_by
)
First, before you mention that summarise
is spelled wrong…well, the author of the dplyr
package is named Hadley Wickham and he is from New Zealand. So that’s the way he spells it. He was nice enough to include the summarize
function as an alias if you need to use it ’cause this is ’Murica!
The summarise
function, by itself, is kind of boring, and doesn’t do anything that couldn’t be done more easily with base R functions.
summarise(hf, mean(Distance))
mean(hf$Distance)
[1] 790.5861
Where summarise
shines is in combination with group_by
. For example, let’s suppose that we want to see average flight distances, but broken down by airline.
hf_summ_grouped <- group_by(hf, UniqueCarrier)
hf_summ <- summarise(hf_summ_grouped, mean(Distance))
`summarise()` ungrouping output (override with `.groups` argument)
hf_summ
This is a good spot to introduce a time-saving and helpful device called “piping”, denoted by the symbol %>%
. Piping always looks more complicated than it really is. The technical definition is that
x %>% f(y)
is equivalent to
f(x, y)
.
As a simple example, we could add two numbers like this:
sum(2, 3)
[1] 5
Or using the pipe, we could do it like this:
2 %>% sum(3)
[1] 5
All this is really saying is that the pipe takes the thing on its left, and plugs it into the first slot of the function on its right. So why do we care?
Let’s revisit the combination group_by
/summarise
example above. There are two ways to do this without pipes, and both are a little ugly. One way is above, where you have to keep reassigning the output to new variables (in the case above, to hf_summ_grouped
and then hf_summ
). The other way is to nest the functions:
summarise(group_by(hf, UniqueCarrier), mean(Distance))
`summarise()` ungrouping output (override with `.groups` argument)
This requires a lot of brain power to parse. In part, this is because the function is inside-out: first you group hf
by UniqueCarrier
, and then the result of that is summarized. Here’s how the pipe fixes it:
hf %>%
group_by(UniqueCarrier) %>%
summarise(mean(Distance))
`summarise()` ungrouping output (override with `.groups` argument)
Look at the group_by
line. The group_by
function should take two arguments, the data frame, and then the grouping variable. It appears to have only one argument. But look at the previous line. The pipe says to insert whatever is on its left (hf
) into the first slot of the function on its right (group_by
). So the net effect is still to evaluate the function group_by(hf, UniqueCarrier)
. Now look at the summarise
line. Again, summarise
is a function of two inputs, but all we see is the part that finds the mean. The pipe at the end of the previous line tells the summarise
function to insert the stuff already computed (the grouped data frame returned by group_by(hf, UniqueCarrier)
) into the first slot of the summarise
function.
Piping takes a little getting used to, but once you’re good at it, you’ll never go back. It’s just makes more sense semantically. When I read the above set of commands, I see a set of instructions in chronological order:
Take the data frame hf
. Now group by the carrier. Next summarize each group using the mean distance.
Now we can assign the result of all that to the new variable hf_summ
:
hf_summ <- hf %>%
group_by(UniqueCarrier) %>%
summarise(mean(Distance))
`summarise()` ungrouping output (override with `.groups` argument)
hf_summ
Let’s try some counting using summarise
. What if we wanted to know how many flights correspond to each carrier?
hf_summ2 <- hf %>%
group_by(UniqueCarrier) %>%
summarize(total_count = n())
`summarise()` ungrouping output (override with `.groups` argument)
hf_summ2
You can see the n()
function at work. It simply counts the observations in each group. (You do need to include the empty parentheses after the n
!) Also note that we can give the summary column a new name if we wish. In hf_summ
, we didn’t give the new column an explicit name, so it showed up in our data frame as a column called mean(Distance)
. If we hadn’t included total_count
in our definition of hf_summ2
, the new column would simply be called n()
. That’s okay, but not anywhere near as user-friendly as total_count
.
Exercise 5
Create a data frame called hf_summ3
that lists the total count of flights for each day of the week. Be sure to use the pipe as above. Print the output to the screen
# Add code here to define hf_summ3.
hf_summ3 <- hf %>%
group_by(DayOfWeek) %>%
summarize(total_count = n())
`summarise()` ungrouping output (override with `.groups` argument)
# Add code here to print hf_summ3.
hf_summ3
Do not forget to remove datasets which you do not need anymore but cost the memory.
rm(hf_summ_grouped, hf_summ, hf_summ2, hf_summ3)
Putting it all together
Often we need more than one of these verbs. In many data analyses, we need to do a sequence of operations to get at the answer we seek. This is most easily accomplished using a more complicated sequence of pipes.
Here’s a example of multi-step piping. Let’s say that we only care about Delta flights, and even then, we only want to know about the month of the flight and the departure delay. From there, we wish to group by months so we can find the maximum departure delay by month. Here is a solution, piping hot and ready to go. [groan]
hf_grand_finale <- hf %>%
filter(UniqueCarrier == "DL") %>%
group_by(Month) %>%
summarise(max_delay = max(DepDelay, na.rm = TRUE))
`summarise()` ungrouping output (override with `.groups` argument)
hf_grand_finale
Go through each line of code carefully and translate it into English:
- We define a variable called
hf_grand_finale
that starts with the original hf
data.
- We
filter
this data so that only Delta flights will be analyzed.
- We
group_by
month so that the results will be displayed by month.
- We
summarise
each month by listing the maximum value of DepDelay
that appears within each month.
- We print the result to the screen.
Exercise 6
Create a data frame called hf_final2
that counts the flights to LAX that were canceled, grouping by day of the week. (Hint: you need to filter
to get both flights to LAX and flights that are canceled. Then you’ll need to group_by
the day of the week in order to summarise
the number of cancellations using n()
.) Print the output to the screen.
# Add code here to count the flights to LAX that were canceled,
# grouping by day of the week.
# Print the output to the screen.
hf_final2 <- hf %>%
filter(Dest == "LAX" & Cancelled == 1) %>%
group_by(DayOfWeek) %>%
summarise(LAX_cancel = n())
`summarise()` ungrouping output (override with `.groups` argument)
hf_final2
Exercise 7
Create a data frame called hf_final3
that finds the median distance flight for each airline. Sort the resulting data frame from highest distance to lowest. (Hint: You’ll need to group_by
carrier and summarise
using the median
function. Finally, you’ll need to arrange
the result according to the median distance variable that you just created.) Print the output to the screen.
# Add code here to find the median distance by airline.
# Print the output to the screen.
hf_final3 <- hf %>%
group_by(UniqueCarrier) %>%
summarize(med_dis = median(Distance)) %>%
arrange(desc(med_dis))
`summarise()` ungrouping output (override with `.groups` argument)
hf_final3
Conclusion
Raw data often doesn’t come in the right form for us to run our analyses. The dplyr
verbs are powerful tools for manipulating data frames until they are in the right form.
LS0tCnRpdGxlOiAiZHBseXIgcGFja2FnZSBzb2x1dGlvbiIKYXV0aG9yOiAiUHV0IHlvdXIgbmFtZSBoZXJlIgpkYXRlOiAiUHV0IHRoZSBkYXRlIGhlcmUiCm91dHB1dDoKICAgIGh0bWxfbm90ZWJvb2s6CiAgICAgICAgdG9jOiB5ZXMKICAgICAgICB0b2NfZmxvYXQ6IHllcwotLS0KCjwhLS0gUGxlYXNlIGRvbid0IG1lc3Mgd2l0aCB0aGUgbmV4dCBmZXcgbGluZXMhIC0tPgo8c3R5bGU+aDV7Zm9udC1zaXplOjJlbTtjb2xvcjojMDAwMEZGfWg2e2ZvbnQtc2l6ZToxLjVlbTtjb2xvcjojMDAwMEZGfWRpdi5hbnN3ZXJ7bWFyZ2luLWxlZnQ6NSU7Ym9yZGVyOjFweCBzb2xpZCAjMDAwMEZGO2JvcmRlci1sZWZ0LXdpZHRoOjEwcHg7cGFkZGluZzoyNXB4fSBkaXYuc3VtbWFyeXtiYWNrZ3JvdW5kLWNvbG9yOnJnYmEoMzAsMTQ0LDI1NSwwLjEpO2JvcmRlcjozcHggZG91YmxlICMwMDAwRkY7cGFkZGluZzoyNXB4fTwvc3R5bGU+PHAgc3R5bGU9ImNvbG9yOiNmZmZmZmYiPmByIGludFRvVXRmOChjKDQ5LDQ2LDQ5KSlgPC9wPgo8IS0tIFBsZWFzZSBkb24ndCBtZXNzIHdpdGggdGhlIHByZXZpb3VzIGZldyBsaW5lcyEgLS0+Cgo8ZGl2IGNsYXNzID0gInN1bW1hcnkiPgojIyMgRnVuY3Rpb25zIGludHJvZHVjZWQgaW4gdGhpcyBtb2R1bGU6CmByZWFkLmNzdmAsIGBzZWxlY3RgLCBgcmVuYW1lYCwgYHJtYCwgYGZpbHRlcmAsIGBzbGljZWAsIGBhcnJhbmdlYCwgYG11dGF0ZWAsIGBhbGwuZXF1YWxgLCBgaWZlbHNlYCwgYHRyYW5zbXV0ZWAsIGBzdW1tYXJpc2VgLCBgZ3JvdXBfYnlgLCBgJT4lYAo8L2Rpdj4KCgojIyBJbnRyb2R1Y3Rpb24KClRoaXMgdHV0b3JpYWwgd2lsbCBpbXBvcnQgc29tZSBkYXRhIGZyb20gdGhlIHdlYiBhbmQgdGhlbiBleHBsb3JlIGl0IHVzaW5nIHRoZSBhbWF6aW5nIGBkcGx5cmAgcGFja2FnZSwgYSBwYWNrYWdlIHdoaWNoIGlzIHF1aWNrbHkgYmVjb21pbmcgdGhlICpkZSBmYWN0byogc3RhbmRhcmQgYW1vbmcgUiB1c2VycyBmb3IgbWFuaXB1bGF0aW5nIGRhdGEuIAoKVGhpcyBpbnRyb2R1Y3Rpb24gaXMgbW9kaWZpZWQgYmFzZWQgb24gUiBtb2R1bGUgIjUuIE1hbmlwdWxhdGluZyBkYXRhIiBhbmQgZHBseXIgdHV0b3JpYWwgaHR0cDovL2dlbm9taWNzY2xhc3MuZ2l0aHViLmlvL2Jvb2svcGFnZXMvZHBseXJfdHV0b3JpYWwuaHRtbC4KCgojIyBMb2FkIHBhY2thZ2VzCgpXZSBsb2FkIHRoZSBgbW9zYWljYCBwYWNrYWdlIGFzIHVzdWFsLCBidXQgdGhpcyB0aW1lIGl0IGlzIHRvIGdpdmUgdXMgYWNjZXNzIHRvIHRoZSBgZHBseXJgIHBhY2thZ2UsIHdoaWNoIGlzIGxvYWRlZCBhbG9uZ3NpZGUgb3VyIG90aGVyIGBtb3NhaWNgIHBhY2thZ2UgY29tbWFuZHMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KG1vc2FpYykKYGBgCgoKIyMgSW1wb3J0aW5nIENTViBkYXRhCgpUaGUgZmlsZSB3ZSdsbCBpbXBvcnQgaXMgYSByYW5kb20gc2FtcGxlIGZyb20gYWxsIHRoZSBjb21tZXJjaWFsIGRvbWVzdGljIGZsaWdodHMgdGhhdCBkZXBhcnRlZCBmcm9tIEhvdXN0b24sIFRleGFzLCBpbiAyMDExLgoKV2UgdXNlIHRoZSBgcmVhZC5jc3ZgIGNvbW1hbmQgdG8gaW1wb3J0IGEgQ1NWIGZpbGUuIEluIHRoaXMgY2FzZSwgd2UncmUgZ3JhYmJpbmcgdGhlIGZpbGUgZnJvbSBhIHdlYiBwYWdlIHdoZXJlIHRoZSBmaWxlIGlzIGhvc3RlZC4gSWYgeW91IGhhdmUgYSBmaWxlIG9uIHlvdXIgY29tcHV0ZXIsIHlvdSBjYW4gYWxzbyBwdXQgdGhlIGZpbGUgaW50byB5b3VyIHByb2plY3QgZGlyZWN0b3J5IGFuZCBpbXBvcnQgaXQgZnJvbSB0aGVyZS4gUHV0IHRoZSBVUkwgKGZvciBhIHdlYiBwYWdlKSBvciB0aGUgZmlsZW5hbWUgKGZvciBhIGZpbGUgaW4geW91ciBwcm9qZWN0IGRpcmVjdG9yeSkgaW4gcXVvdGVzIGluc2lkZSB0aGUgYHJlYWQuY3N2YGNvbW1hbmQuIFdlIGFsc28gbmVlZCB0byBhc3NpZ24gdGhlIG91dHB1dCB0byBhIGRhdGEgZnJhbWUsIHNvIHdlJ3ZlIGNhbGxlZCBpdCBgaGZgIGZvciAiSG91c3RvbiBmbGlnaHRzIi4KCmBgYHtyfQpoZiA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL1ZlY3RvclBvc3NlL0ludHJvX1N0YXRzL21hc3Rlci9kYXRhL2hmLmNzdiIpCmhmCmBgYAoKYGBge3J9CnN0cihoZikKYGBgCgpUaGUgb25lIGRpc2FkdmFudGFnZSBvZiBhIGZpbGUgaW1wb3J0ZWQgZnJvbSB0aGUgaW50ZXJuZXQgb3IgeW91ciBjb21wdXRlciBpcyB0aGF0IGl0IGRvZXMgbm90IGNvbWUgd2l0aCBhIGhlbHAgZmlsZS4gKE9ubHkgcGFja2FnZXMgaW4gUiBoYXZlIGhlbHAgZmlsZXMuKSBIb3BlZnVsbHkgeW91IGhhdmUgYWNjZXNzIHRvIHNvbWUga2luZCBvZiBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZGF0YSB5b3UncmUgaW1wb3J0aW5nLiBJbiB0aGlzIGNhc2UsIHdlIGdldCBsdWNreSBiZWNhdXNlIHRoZSBmdWxsIEhvdXN0b24gZmxpZ2h0cyBkYXRhIHNldCBoYXBwZW5zIHRvIGJlIGF2YWlsYWJsZSBpbiBhIHBhY2thZ2UgY2FsbGVkIGBoZmxpZ2h0c2AuCgoKIyMgSW50cm9kdWN0aW9uIHRvIGBkcGx5cmAKClRoZSBgZHBseXJgIHBhY2thZ2UgKHByb25vdW5jZWQgImRlZS1wbHktZXIiKSBjb250YWlucyB0b29scyBmb3IgbWFuaXB1bGF0aW5nIHRoZSByb3dzIGFuZCBjb2x1bW5zIG9mIGRhdGEgZnJhbWVzLiBUaGUga2V5IHRvIHVzaW5nIGBkcGx5cmAgaXMgdG8gZmFtaWxpYXJpemUgeW91cnNlbGYgd2l0aCB0aGUgImtleSB2ZXJicyI6CgoqICAgYHNlbGVjdGAgKGFuZCBgcmVuYW1lYCkKKiAgIGBmaWx0ZXJgIChhbmQgYHNsaWNlYCkKKiAgIGBhcnJhbmdlYAoqICAgYG11dGF0ZWAgKGFuZCBgdHJhbnNtdXRlYCkKKiAgIGBzdW1tYXJpc2VgICh3aXRoIGBncm91cF9ieWApCgpXZSdsbCBjb25zaWRlciB0aGVzZSBvbmUgYnkgb25lLiBXZSB3b24ndCBoYXZlIHRpbWUgdG8gY292ZXIgZXZlcnkgYXNwZWN0IG9mIHRoZXNlIGZ1bmN0aW9ucy4gTW9yZSBpbmZvcm1hdGlvbiBhcHBlYXJzIGluIHRoZSBoZWxwIGZpbGVzLCBhcyB3ZWxsIGFzIHRoaXMgdmVyeSBoZWxwZnVsICJjaGVhdCBzaGVldCI6IGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL3Jhdy9tYXN0ZXIvZGF0YS10cmFuc2Zvcm1hdGlvbi5wZGYKCgojIyBgc2VsZWN0YAoKVGhlIGBzZWxlY3RgIHZlcmIgaXMgdmVyeSBlYXN5LiBJdCBqdXN0IHNlbGVjdHMgc29tZSBzdWJzZXQgb2YgdmFyaWFibGVzICh0aGUgY29sdW1ucyBvZiB5b3VyIGRhdGEgc2V0KS4gU3VwcG9zZSBhbGwgd2Ugd2FudGVkIHRvIHNlZSB3YXMgdGhlIGNhcnJpZXIsIG9yaWdpbiwgYW5kIGRlc3RpbmF0aW9uLiBXZSB3b3VsZCB0eXBlCgpgYGB7cn0KaGZfc2VsZWN0IDwtIHNlbGVjdChoZiwgVW5pcXVlQ2FycmllciwgT3JpZ2luLCBEZXN0KQpoZl9zZWxlY3QKYGBgCgpJZiB5b3UgZG9uJ3QgbGlrZSB0aGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcywgeW91IGNhbiBjaGFuZ2UgdGhlbSBhcyBwYXJ0IG9mIHRoZSBzZWxlY3QgcHJvY2Vzcy4KCmBgYHtyfQpoZl9zZWxlY3QgPC0gc2VsZWN0KGhmLCBjYXJyaWVyID0gVW5pcXVlQ2FycmllciwKICAgICAgICAgICAgICAgICAgICBvcmlnaW4gPSBPcmlnaW4sIGRlc3QgPSBEZXN0KQpoZl9zZWxlY3QKYGBgCgpUaGVyZSBhcmUgYSBmZXcgbm90YXRpb25hbCBzaG9ydGN1dHMuIEZvciBleGFtcGxlLCBzZWUgd2hhdCB0aGUgZm9sbG93aW5nIGRvLgoKYGBge3J9CmhmX3NlbGVjdDIgPC0gc2VsZWN0KGhmLCBEYXlPZldlZWs6VW5pcXVlQ2FycmllcikKaGZfc2VsZWN0MgpgYGAKCmBgYHtyfQpoZl9zZWxlY3QzIDwtIHNlbGVjdChoZiwgc3RhcnRzX3dpdGgoIlRheGkiKSkKaGZfc2VsZWN0MwpgYGAKCgojIyBUaGUgYHJtYCBjb21tYW5kCgpSZWNhbGwgdGhhdCBlYXJsaWVyIHdlIG1lbnRpb25lZCB0aGUgcHJvcyBhbmQgY29ucyBvZiBjcmVhdGluZyBhIG5ldyBkYXRhIGZyYW1lIGV2ZXJ5IHRpbWUgd2UgbWFrZSBhIGNoYW5nZS4gT24gb25lIGhhbmQsIG1ha2luZyBhIG5ldyBkYXRhIGZyYW1lIGluc3RlYWQgb2Ygb3ZlcndyaXRpbmcgdGhlIG9yaWdpbmFsIG9uZSB3aWxsIGtlZXAgdGhlIG9yaWdpbmFsIG9uZSBhdmFpbGFibGUgc28gdGhhdCB3ZSBjYW4gcnVuIGRpZmZlcmVudCBjb21tYW5kcyBvbiBpdC4gT24gdGhlIG90aGVyIGhhbmQsIG1ha2luZyBhIG5ldyBkYXRhIGZyYW1lIGRvZXMgZWF0IHVwIGEgbG90IG9mIG1lbW9yeS4KCk9uZSB3YXkgdG8gZ2V0IHJpZCBvZiBhbiBvYmplY3Qgb25jZSB3ZSBhcmUgZG9uZSB3aXRoIGl0IGlzIHRoZSBgcm1gIGNvbW1hbmQsIHdoZXJlIGBybWAgaXMgc2hvcnQgZm9yICJyZW1vdmUiLiBXaGVuIHlvdSBydW4gdGhlIGNvZGUgY2h1bmsgYmVsb3csIHlvdSdsbCBzZWUgdGhhdCBhbGwgdGhlIGRhdGEgZnJhbWVzIHdlIGNyZWF0ZWQgd2l0aCBgc2VsZWN0YCB3aWxsIGRpc2FwcGVhciBmcm9tIHlvdXIgR2xvYmFsIEVudmlyb25tZW50LgoKYGBge3J9CnJtKGhmX3NlbGVjdCwgaGZfc2VsZWN0MiwgaGZfc2VsZWN0MykKYGBgCgpJZiB5b3UgbmVlZCBvbmUgdGhlc2UgZGF0YSBmcmFtZXMgYmFjayBsYXRlciwgeW91IGNhbiBhbHdheXMgZ28gYmFjayBhbmQgcmUtcnVuIHRoZSBjb2RlIGNodW5rIHRoYXQgZGVmaW5lZCBpdC4KCldlJ2xsIHVzZSBgcm1gIGF0IHRoZSBlbmQgb2Ygc29tZSBvZiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zIHNvIHRoYXQgd2UgZG9uJ3QgdXNlIHVwIHRvbyBtdWNoIG1lbW9yeS4KCgojIyBgZmlsdGVyYAoKVGhlIGBmaWx0ZXJgIHZlcmIgd29ya3MgYSBsb3QgbGlrZSBgc2VsZWN0YCwgYnV0IGZvciByb3dzIGluc3RlYWQgb2YgY29sdW1ucy4KCkZvciBleGFtcGxlLCBsZXQncyBzYXkgd2Ugb25seSB3YW50IHRvIHNlZSBEZWx0YSBmbGlnaHRzLiBXZSB1c2UgYGZpbHRlcmA6CgpgYGB7cn0KaGZfZmlsdGVyIDwtIGZpbHRlcihoZiwgVW5pcXVlQ2FycmllciA9PSAiREwiKQpoZl9maWx0ZXIKYGBgCgpJbiB0aGUgcHJpbnRvdXQgb2YgdGhlIGRhdGEgZnJhbWUgYWJvdmUsIGlmIHlvdSBjYW4ndCBzZWUgdGhlIGBVbmlxdWVDYXJyaWVyYCBjb2x1bW4sIGNsaWNrIHRoZSBibGFjayBhcnJvdyBvbiB0aGUgcmlnaHQgdG8gc2Nyb2xsIHRocm91Z2ggdGhlIGNvbHVtbnMgdW50aWwgeW91IGNhbiBzZWUgaXQuIFlvdSBjYW4gY2xpY2sgIk5leHQiIGF0IHRoZSBib3R0b20gdG8gc2Nyb2xsIHRocm91Z2ggdGhlIHJvd3MuCgpKdXN0IGxpa2UgYHNlbGVjdGAsIHRoZSBmaXJzdCBhcmd1bWVudCBpcyB0aGUgZGF0YSBmcmFtZS4gRm9sbG93aW5nIHRoYXQsIHlvdSBtdXN0IHNwZWNpZnkgc29tZSBjb25kaXRpb24uIE9ubHkgcm93cyBtZWV0aW5nIHRoYXQgY29uZGl0aW9uIHdpbGwgYmUgaW5jbHVkZWQgaW4gdGhlIG91dHB1dC4KCkNvbmRpdGlvbiBpbmNsdWRlcyA9PSwgPCwgPiwgIT0sICVpbiUsIGV0Yy4gTG9naWNhbCBvcGVyYXRpb25zIGluY2x1ZGVzICEsICYsIHwuIAoKQXMgYW5vdGhlciBleGFtcGxlLCBzdXBwb3NlIHdlIHdhbnRlZCB0byBmaW5kIG91dCBhbGwgZmxpZ2h0cyB0aGF0IGxlYXZlIGJlZm9yZSA2OjAwIGEubS4KCmBgYHtyfQpoZl9maWx0ZXIyIDwtIGZpbHRlcihoZiwgRGVwVGltZSA8IDYwMCkKaGZfZmlsdGVyMgpgYGAKClRoZSBmb2xsb3dpbmcgd2lsbCBnaXZlIHVzIG9ubHkgdGhlIERlbHRhIGZsaWdodHMgdGhhdCBkZXBhcnRlZCBiZWZvcmUgNjowMCBhLm0uCgpgYGB7cn0KaGZfZmlsdGVyMyA8LSBmaWx0ZXIoaGYsIFVuaXF1ZUNhcnJpZXIgPT0gIkRMIiAmIERlcFRpbWUgPCA2MDApCiMgQW5vdGhlciB3YXkgdG8gZmlsdGVyCiMgaGZfZmlsdGVyMyA8LSBmaWx0ZXIoaGYsIFVuaXF1ZUNhcnJpZXIgPT0gIkRMIiwgRGVwVGltZSA8IDYwMCkKaGZfZmlsdGVyMwpgYGAKCkFnYWluLCBjaGVjayB0aGUgY2hlYXQgc2hlZXQgZm9yIG1vcmUgY29tcGxpY2F0ZWQgY29uZGl0aW9uLWNoZWNraW5nLgoKIyMjIyMgRXhlcmNpc2UgMQoKVXNlIHRoZSBgZmlsdGVyYCBjb21tYW5kIHRvIGNyZWF0ZSBhIGRhdGEgZnJhbWUgY2FsbGVkIGBoZl9maWx0ZXI0YCB0aGF0IGZpbmRzIGFsbCBmbGlnaHRzICpleGNlcHQqIHRob3NlIGZseWluZyBpbnRvIFNhbHQgTGFrZSBDaXR5ICgiU0xDIikuIEFzIGJlZm9yZSwgcHJpbnQgdGhlIG91dHB1dCB0byB0aGUgc2NyZWVuLgoKPGRpdiBjbGFzcyA9ICJhbnN3ZXIiPgoKYGBge3J9CiMgQWRkIGNvZGUgaGVyZSB0byBkZWZpbmUgaGZfZmlsdGVyNC4KIyBBZGQgY29kZSBoZXJlIHRvIHByaW50IGhmX2ZpbHRlcjQuCmhmX2ZpbHRlcjQgPC0gZmlsdGVyKGhmLCBEZXN0ICE9ICJTTEMiKQpoZl9maWx0ZXI0CmBgYAoKPC9kaXY+CgojIyMjIyBFeGVyY2lzZSAyCgpVc2UgZmlsdGVyIHdpdGggJWluJSB0byBjcmVhdGUgYSBkYXRhIGZyYW1lIGNhbGxlZCBgaGZfZmlsdGVyNWAgdGhhdCBvbmx5IGNvbnRhaW5zIGZsaWdodCBpbmZvcm1hdGlvbiBvZiBBQSwgREwsIGFuZCBVQS4gCgpgYGB7cn0KIyBBZGQgY29kZSBoZXJlIHRvIGRlZmluZSBoZl9maWx0ZXI1LgojIEFkZCBjb2RlIGhlcmUgdG8gcHJpbnQgaGZfZmlsdGVyNS4KaGZfZmlsdGVyNSA8LSBmaWx0ZXIoaGYsIFVuaXF1ZUNhcnJpZXIgJWluJSBjKCJBQSIsICJETCIsICJVQSIpKQpoZl9maWx0ZXI1CmBgYAoKTm93IHVzZSB0aGUgYHJtYCBjb21tYW5kIHRvIHJlbW92ZSBhbGwgdGhlIGV4dHJhIGRhdGEgZnJhbWVzIHlvdSBjcmVhdGVkIGluIHRoaXMgc2VjdGlvbiB3aXRoIGBmaWx0ZXJgLgoKYGBge3J9CnJtKGhmX2ZpbHRlciwgaGZfZmlsdGVyMiwgaGZfZmlsdGVyMywgaGZfZmlsdGVyNCwgaGZfZmlsdGVyNSkKYGBgCgoKIyMgYGFycmFuZ2VgCgpUaGlzIGp1c3QgcmUtb3JkZXJzIHRoZSByb3dzLCBzb3J0aW5nIG9uIHRoZSB2YWx1ZXMgb2Ygb25lIG9yIG1vcmUgc3BlY2lmaWVkIGNvbHVtbnMuIEFzIEkgbWVudGlvbmVkIGJlZm9yZSwgaW4gbW9zdCBkYXRhIGFuYWx5c2VzIHlvdSB3b3JrIHdpdGggc3VtbWFyaWVzIG9mIHRoZSBkYXRhIHRoYXQgZG8gbm90IGRlcGVuZCBvbiB0aGUgb3JkZXIgb2YgdGhlIHJvd3MsIHNvIHRoaXMgaXMgbm90IHF1aXRlIGFzIGludGVyZXN0aW5nIGFzIHNvbWUgb2YgdGhlIG90aGVyIHZlcmJzLiBJbiBmYWN0LCBzaW5jZSB0aGUgcmUtb3JkZXJpbmcgaXMgdXN1YWxseSBmb3IgdGhlIHZpc3VhbCBiZW5lZml0IG9mIHRoZSByZWFkZXIsIHRoZXJlIGlzIG9mdGVuIG5vIG5lZWQgdG8gc3RvcmUgdGhlIG91dHB1dCBpbiBhIG5ldyB2YXJpYWJsZS4gV2UnbGwganVzdCBwcmludCB0aGUgb3V0cHV0IHRvIHRoZSBzY3JlZW4uCgpgYGB7cn0KYXJyYW5nZShoZiwgQWN0dWFsRWxhcHNlZFRpbWUpCmBgYAoKU2Nyb2xsIG92ZXIgdG8gdGhlIGBBY3R1YWxFbGFwc2VkVGltZWAgdmFyaWFibGUgaW4gdGhlIG91dHB1dCBiZWxvdyB0byBzZWUgdGhhdCB0aGVzZSBhcmUgbm93IHNvcnRlZCBpbiBhc2NlbmRpbmcgb3JkZXIuCgojIyMjIyBFeGVyY2lzZSAzCgpIb3cgbG9uZyBpcyB0aGUgc2hvcnRlc3QgYWN0dWFsIGVsYXBzZWQgdGltZT8gV2h5IGlzIHRoaXMgZmxpZ2h0IHNvIHNob3J0PyAoSGludDogbG9vayBhdCB0aGUgZGVzdGluYXRpb24uKSBXaGljaCBhaXJsaW5lIGZsaWVzIHRoYXQgcm91dGU/IFlvdSBtYXkgaGF2ZSB0byB1c2UgeW91ciBiZXN0IGZyaWVuZCBHb29nbGUgdG8gbG9vayB1cCBhaXJwb3J0IGFuZCBhaXJsaW5lIGNvZGVzLgoKPGRpdiBjbGFzcyA9ICAiYW5zd2VyIj4KClBsZWFzZSB3cml0ZSB1cCB5b3VyIGFuc3dlciBoZXJlLgoKMzUgbWludXRlcyB0byBBdXN0aW4gYmVjYXVzZSBIb3VzdG9uIGFuZCBBdXN0aW4gYXJlIGluIHRoZSBzYW1lIHN0YXRlLiBUaGUgYWlybGluZSBpcyBTb3V0aHdlc3QuCgo8L2Rpdj4KCioqKioqCgpJZiB5b3Ugd2FudCBkZXNjZW5kaW5nIG9yZGVyLCBkbyB0aGlzOgoKYGBge3J9CmFycmFuZ2UoaGYsIGRlc2MoQWN0dWFsRWxhcHNlZFRpbWUpKQpgYGAKCgojIyBgbXV0YXRlYAoKRnJlcXVlbnRseSwgd2Ugd2FudCB0byBjcmVhdGUgbmV3IHZhcmlhYmxlcyB0aGF0IGNvbWJpbmUgaW5mb3JtYXRpb24gZnJvbSBvbmUgb3IgbW9yZSBleGlzdGluZyB2YXJpYWJsZXMuIFdlIHVzZSBgbXV0YXRlYCBmb3IgdGhpcy4gRm9yIGV4YW1wbGUsIHN1cHBvc2Ugd2Ugd2FudGVkIHRvIGZpbmQgdGhlIHRvdGFsIHRpbWUgb2YgdGhlIGZsaWdodC4gV2UgbWlnaHQgZG8gdGhpcyBieSBhZGRpbmcgdXAgdGhlIG1pbnV0ZXMgZnJvbSBzZXZlcmFsIHZhcmlhYmxlczogYFRheGlPdXRgLCBgQWlyVGltZWAsIGFuZCBgVGF4aUluYCwgYW5kIGFzc2lnbmluZyB0aGF0IHN1bSB0byBhIG5ldyB2YXJpYWJsZSBjYWxsZWQgYHRvdGFsYC4gU2Nyb2xsIGFsbCB0aGUgd2F5IHRvIHRoZSByaWdodCBpbiB0aGUgb3V0cHV0IGJlbG93IHRvIHNlZSB0aGUgbmV3IGB0b3RhbGAgdmFyaWFibGUuCgpgYGB7cn0KaGZfbXV0YXRlIDwtIG11dGF0ZShoZiwgdG90YWwgPSBUYXhpT3V0ICsgQWlyVGltZSArIFRheGlJbikKaGZfbXV0YXRlCmBgYAoKQXMgaXQgdHVybnMgb3V0LCB0aGF0IHdhcyB3YXN0ZWQgZWZmb3J0IGJlY2F1c2UgdGhhdCB2YXJpYWJsZSBhbHJlYWR5IGV4aXN0cyBpbiBgQWN0dWFsRWxhcHNlZFRpbWVgLiAoVGhlIGBhbGwuZXF1YWxgIGNvbW1hbmQgY2hlY2tzIHRoYXQgYm90aCBzcGVjaWZpZWQgY29sdW1ucyBjb250YWluIHRoZSBleGFjdCBzYW1lIHZhbHVlcy4pCgpgYGB7cn0KYWxsLmVxdWFsKGhmX211dGF0ZSR0b3RhbCwgaGYkQWN0dWFsRWxhcHNlZFRpbWUpCmBgYAoKUGVyaGFwcyB3ZSB3YW50IGEgdmFyaWFibGUgdGhhdCBqdXN0IGNsYXNzaWZpZXMgYSBmbGlnaHQgYXMgYXJyaXZpbmcgbGF0ZSBvciBub3QuIFNjcm9sbCBhbGwgdGhlIHdheSB0byB0aGUgcmlnaHQgdG8gc2VlIHRoZSBuZXcgYGxhdGVgIHZhcmlhYmxlLgoKYGBge3J9CmhmX211dGF0ZTIgPC0gbXV0YXRlKGhmLCBsYXRlID0gKEFyckRlbGF5ID4gMCkpCmhmX211dGF0ZTIKYGBgCgpIYXZpbmcgc2FpZCB0aGF0LCBJIHdvdWxkIGdlbmVyYWxseSByZWNvbW1lbmQgdGhhdCB5b3UgbGVhdmUgdGhlc2Uga2luZHMgb2YgdmFyaWFibGVzIGFzIGxvZ2ljYWwgdHlwZXMuIEl0J3MgbXVjaCBlYXNpZXIgdG8gc3VtbWFyaXplIHN1Y2ggdmFyaWFibGVzIGluIFIsIG5hbWVseSBiZWNhdXNlIFIgdHJlYXRzIGBUUlVFYCBhcyAxIGFuZCBgRkFMU0VgIGFzIDAsIGFsbG93aW5nIHVzIHRvIGRvIHRoaW5ncyBsaWtlIHRoaXM6CgpgYGB7cn0KbWVhbihoZl9tdXRhdGUyJGxhdGUsIG5hLnJtID0gVFJVRSkKYGBgCgpUaGlzIGdpdmVzIHVzIHRoZSBwZXJjZW50YWdlIG9mIGxhdGUgZmxpZ2h0cy4KCk9uZSBub3RlIG9mIGV4cGxhbmF0aW9uIGluIHRoZSBgbWVhbmAgY29tbWFuZDogdGhlcmUgaXMgYW4gYXJndW1lbnQgYG5hLnJtYC4gVGhpcyBzdGFuZHMgZm9yICJOQSByZW1vdmUiLCB3aGljaCByZWZlcnMgdG8gUidzIGNvbnZlbnRpb24gb2YgdXNpbmcgYE5BYCB0byByZWZlciB0byBtaXNzaW5nIHZhbHVlcy4gRm9yIGV4YW1wbGUsIGxvb2sgYXQgdGhlIDkzcmQgcm93IG9mIHRoZSBkYXRhIGZyYW1lOgoKYGBge3J9CmhmX211dGF0ZTJbOTMsIF0KYGBgCgpOb3RpY2UgdGhhdCB0aGUgYWxsIHRoZSB0aW1lcyBhcmUgbWlzc2luZy4gVGhlcmUgYXJlIGEgYnVuY2ggb2Ygcm93cyBsaWtlIHRoaXMuIFNpbmNlIHRoZXJlIGlzIG5vdCBhbHdheXMgYW4gYXJyaXZhbCBkZWxheSBsaXN0ZWQsIHRoZSBgQXJyRGVsYXlgIHZhcmlhYmxlIGRvZXNuJ3QgYWx3YXlzIGhhdmUgYSB2YWx1ZSwgYW5kIGlmIGBBcnJEZWxheWAgaXMgYE5BYCwgdGhlIGBsYXRlYCB2YXJpYWJsZSB3aWxsIGJlIHRvby4gU28gaWYgd2UgdHJ5IHRvIGNhbGN1bGF0ZSB0aGUgbWVhbiB3aXRoIGp1c3QgdGhlIGBtZWFuYCBjb21tYW5kLCB0aGlzIGhhcHBlbnM6CgpgYGB7cn0KbWVhbihoZl9tdXRhdGUyJGxhdGUpCmBgYAoKVGhlcmVmb3JlLCB3ZSBoYXZlIHRvIHJlbW92ZSB0aGUgYE5BYHMgaW4gb3JkZXIgdG8gcHJvcGVybHkgY2FsY3VsYXRlIHRoZSBtZWFuLCBhbmQgdGhhdCdzIHdoYXQgdGhlIGBuYS5ybWAgYXJndW1lbnQgZG9lcy4KCgojIyMjIyBFeGVyY2lzZSA0CgpDcmVhdGUgYSBuZXcgZGF0YSBmcmFtZSBjYWxsZWQgYGhmX211dGF0ZTNgIHRoYXQgdXNlcyB0aGUgYG11dGF0ZWAgY29tbWFuZCB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgY2FsbGVkIGBkaXN0X2tgIHdoaWNoIG1lYXN1cmVzIHRoZSBmbGlnaHQgZGlzdGFuY2UgaW4ga2lsb21ldGVycyBpbnN0ZWFkIG9mIG1pbGVzLiAoSGludDogdG8gZ2V0IGZyb20gbWlsZXMgdG8ga2lsb21ldGVycywgbXVsdGlwbHkgdGhlIGRpc3RhbmNlIGJ5IDEuNjA5MzQuKSBQcmludCB0aGUgb3V0cHV0IHRvIHRoZSBzY3JlZW4uCgo8ZGl2IGNsYXNzID0gICJhbnN3ZXIiPgoKYGBge3J9CiMgQWRkIGNvZGUgaGVyZSB0byBkZWZpbmUgaGZfbXV0YXRlMy4KaGZfbXV0YXRlMyA8LSBtdXRhdGUoaGYsIGRpc2tfayA9IERpc3RhbmNlICogMS42MDkzNCkKIyBBZGQgY29kZSBoZXJlIHRvIHByaW50IGhmX211dGF0ZTMuCmhmX211dGF0ZTMKYGBgCgo8L2Rpdj4KCkJlZm9yZSBtb3Zpbmcgb24gdG8gdGhlIG5leHQgc2VjdGlvbiwgd2UnbGwgY2xlYW4gdXAgdGhlIGV4dHJhIGRhdGEgZnJhbWVzIGx5aW5nIGFyb3VuZDoKCmBgYHtyfQpybShoZl9tdXRhdGUsIGhmX211dGF0ZTIsIGhmX211dGF0ZTMpCmBgYAoKCiMjIGBzdW1tYXJpc2VgICh3aXRoIGBncm91cF9ieWApCgpGaXJzdCwgYmVmb3JlIHlvdSBtZW50aW9uIHRoYXQgYHN1bW1hcmlzZWAgaXMgc3BlbGxlZCB3cm9uZy4uLndlbGwsIHRoZSBhdXRob3Igb2YgdGhlIGBkcGx5cmAgcGFja2FnZSBpcyBuYW1lZCBIYWRsZXkgV2lja2hhbSBhbmQgaGUgaXMgZnJvbSBOZXcgWmVhbGFuZC4gU28gdGhhdCdzIHRoZSB3YXkgaGUgc3BlbGxzIGl0LiBIZSB3YXMgbmljZSBlbm91Z2ggdG8gaW5jbHVkZSB0aGUgYHN1bW1hcml6ZWAgZnVuY3Rpb24gYXMgYW4gYWxpYXMgaWYgeW91IG5lZWQgdG8gdXNlIGl0ICdjYXVzZSB0aGlzIGlzICdNdXJpY2EhCgpUaGUgYHN1bW1hcmlzZWAgZnVuY3Rpb24sIGJ5IGl0c2VsZiwgaXMga2luZCBvZiBib3JpbmcsIGFuZCBkb2Vzbid0IGRvIGFueXRoaW5nIHRoYXQgY291bGRuJ3QgYmUgZG9uZSBtb3JlIGVhc2lseSB3aXRoIGJhc2UgUiBmdW5jdGlvbnMuCgpgYGB7cn0Kc3VtbWFyaXNlKGhmLCBtZWFuKERpc3RhbmNlKSkKYGBgCgpgYGB7cn0KbWVhbihoZiREaXN0YW5jZSkKYGBgCgpXaGVyZSBgc3VtbWFyaXNlYCBzaGluZXMgaXMgaW4gY29tYmluYXRpb24gd2l0aCBgZ3JvdXBfYnlgLiBGb3IgZXhhbXBsZSwgbGV0J3Mgc3VwcG9zZSB0aGF0IHdlIHdhbnQgdG8gc2VlIGF2ZXJhZ2UgZmxpZ2h0IGRpc3RhbmNlcywgYnV0IGJyb2tlbiBkb3duIGJ5IGFpcmxpbmUuCgpgYGB7cn0KaGZfc3VtbV9ncm91cGVkIDwtIGdyb3VwX2J5KGhmLCBVbmlxdWVDYXJyaWVyKQpoZl9zdW1tIDwtIHN1bW1hcmlzZShoZl9zdW1tX2dyb3VwZWQsIG1lYW4oRGlzdGFuY2UpKQpoZl9zdW1tCmBgYAoKVGhpcyBpcyBhIGdvb2Qgc3BvdCB0byBpbnRyb2R1Y2UgYSB0aW1lLXNhdmluZyBhbmQgaGVscGZ1bCBkZXZpY2UgY2FsbGVkICJwaXBpbmciLCBkZW5vdGVkIGJ5IHRoZSBzeW1ib2wgYCU+JWAuIFBpcGluZyBhbHdheXMgbG9va3MgbW9yZSBjb21wbGljYXRlZCB0aGFuIGl0IHJlYWxseSBpcy4gVGhlIHRlY2huaWNhbCBkZWZpbml0aW9uIGlzIHRoYXQKCmB4ICU+JSBmKHkpYAoKaXMgZXF1aXZhbGVudCB0byAKCmBmKHgsIHkpYC4KCkFzIGEgc2ltcGxlIGV4YW1wbGUsIHdlIGNvdWxkIGFkZCB0d28gbnVtYmVycyBsaWtlIHRoaXM6CgpgYGB7cn0Kc3VtKDIsIDMpCmBgYAoKT3IgdXNpbmcgdGhlIHBpcGUsIHdlIGNvdWxkIGRvIGl0IGxpa2UgdGhpczoKCmBgYHtyfQoyICU+JSBzdW0oMykKYGBgCgpBbGwgdGhpcyBpcyByZWFsbHkgc2F5aW5nIGlzIHRoYXQgdGhlIHBpcGUgdGFrZXMgdGhlIHRoaW5nIG9uIGl0cyBsZWZ0LCBhbmQgcGx1Z3MgaXQgaW50byB0aGUgZmlyc3Qgc2xvdCBvZiB0aGUgZnVuY3Rpb24gb24gaXRzIHJpZ2h0LiBTbyB3aHkgZG8gd2UgY2FyZT8KCkxldCdzIHJldmlzaXQgdGhlIGNvbWJpbmF0aW9uIGBncm91cF9ieWAvYHN1bW1hcmlzZWAgZXhhbXBsZSBhYm92ZS4gVGhlcmUgYXJlIHR3byB3YXlzIHRvIGRvIHRoaXMgd2l0aG91dCBwaXBlcywgYW5kIGJvdGggYXJlIGEgbGl0dGxlIHVnbHkuIE9uZSB3YXkgaXMgYWJvdmUsIHdoZXJlIHlvdSBoYXZlIHRvIGtlZXAgcmVhc3NpZ25pbmcgdGhlIG91dHB1dCB0byBuZXcgdmFyaWFibGVzIChpbiB0aGUgY2FzZSBhYm92ZSwgdG8gYGhmX3N1bW1fZ3JvdXBlZGAgYW5kIHRoZW4gYGhmX3N1bW1gKS4gVGhlIG90aGVyIHdheSBpcyB0byBuZXN0IHRoZSBmdW5jdGlvbnM6CgpgYGB7cn0Kc3VtbWFyaXNlKGdyb3VwX2J5KGhmLCBVbmlxdWVDYXJyaWVyKSwgbWVhbihEaXN0YW5jZSkpCmBgYAoKVGhpcyByZXF1aXJlcyBhIGxvdCBvZiBicmFpbiBwb3dlciB0byBwYXJzZS4gSW4gcGFydCwgdGhpcyBpcyBiZWNhdXNlIHRoZSBmdW5jdGlvbiBpcyBpbnNpZGUtb3V0OiBmaXJzdCB5b3UgZ3JvdXAgYGhmYCBieSBgVW5pcXVlQ2FycmllcmAsIGFuZCB0aGVuIHRoZSByZXN1bHQgb2YgdGhhdCBpcyBzdW1tYXJpemVkLiBIZXJlJ3MgaG93IHRoZSBwaXBlIGZpeGVzIGl0OgoKYGBge3J9CmhmICU+JQogICAgZ3JvdXBfYnkoVW5pcXVlQ2FycmllcikgJT4lCiAgICBzdW1tYXJpc2UobWVhbihEaXN0YW5jZSkpCmBgYAoKTG9vayBhdCB0aGUgYGdyb3VwX2J5YCBsaW5lLiBUaGUgYGdyb3VwX2J5YCBmdW5jdGlvbiBzaG91bGQgdGFrZSB0d28gYXJndW1lbnRzLCB0aGUgZGF0YSBmcmFtZSwgYW5kIHRoZW4gdGhlIGdyb3VwaW5nIHZhcmlhYmxlLiBJdCBhcHBlYXJzIHRvIGhhdmUgb25seSBvbmUgYXJndW1lbnQuIEJ1dCBsb29rIGF0IHRoZSBwcmV2aW91cyBsaW5lLiBUaGUgcGlwZSBzYXlzIHRvIGluc2VydCB3aGF0ZXZlciBpcyBvbiBpdHMgbGVmdCAoYGhmYCkgaW50byB0aGUgZmlyc3Qgc2xvdCBvZiB0aGUgZnVuY3Rpb24gb24gaXRzIHJpZ2h0IChgZ3JvdXBfYnlgKS4gU28gdGhlIG5ldCBlZmZlY3QgaXMgc3RpbGwgdG8gZXZhbHVhdGUgdGhlIGZ1bmN0aW9uIGBncm91cF9ieShoZiwgVW5pcXVlQ2FycmllcilgLiBOb3cgbG9vayBhdCB0aGUgYHN1bW1hcmlzZWAgbGluZS4gQWdhaW4sIGBzdW1tYXJpc2VgIGlzIGEgZnVuY3Rpb24gb2YgdHdvIGlucHV0cywgYnV0IGFsbCB3ZSBzZWUgaXMgdGhlIHBhcnQgdGhhdCBmaW5kcyB0aGUgbWVhbi4gVGhlIHBpcGUgYXQgdGhlIGVuZCBvZiB0aGUgcHJldmlvdXMgbGluZSB0ZWxscyB0aGUgYHN1bW1hcmlzZWAgZnVuY3Rpb24gdG8gaW5zZXJ0IHRoZSBzdHVmZiBhbHJlYWR5IGNvbXB1dGVkICh0aGUgZ3JvdXBlZCBkYXRhIGZyYW1lIHJldHVybmVkIGJ5IGBncm91cF9ieShoZiwgVW5pcXVlQ2FycmllcilgKSBpbnRvIHRoZSBmaXJzdCBzbG90IG9mIHRoZSBgc3VtbWFyaXNlYCBmdW5jdGlvbi4KClBpcGluZyB0YWtlcyBhIGxpdHRsZSBnZXR0aW5nIHVzZWQgdG8sIGJ1dCBvbmNlIHlvdSdyZSBnb29kIGF0IGl0LCB5b3UnbGwgbmV2ZXIgZ28gYmFjay4gSXQncyBqdXN0IG1ha2VzIG1vcmUgc2Vuc2Ugc2VtYW50aWNhbGx5LiBXaGVuIEkgcmVhZCB0aGUgYWJvdmUgc2V0IG9mIGNvbW1hbmRzLCBJIHNlZSBhIHNldCBvZiBpbnN0cnVjdGlvbnMgaW4gY2hyb25vbG9naWNhbCBvcmRlcjoKCj4gVGFrZSB0aGUgZGF0YSBmcmFtZSBgaGZgLiBOb3cgZ3JvdXAgYnkgdGhlIGNhcnJpZXIuIE5leHQgc3VtbWFyaXplIGVhY2ggZ3JvdXAgdXNpbmcgdGhlIG1lYW4gZGlzdGFuY2UuCgpOb3cgd2UgY2FuIGFzc2lnbiB0aGUgcmVzdWx0IG9mIGFsbCB0aGF0IHRvIHRoZSBuZXcgdmFyaWFibGUgYGhmX3N1bW1gOgoKYGBge3J9CmhmX3N1bW0gPC0gaGYgJT4lCiAgICBncm91cF9ieShVbmlxdWVDYXJyaWVyKSAlPiUKICAgIHN1bW1hcmlzZShtZWFuKERpc3RhbmNlKSkKaGZfc3VtbQpgYGAKCkxldCdzIHRyeSBzb21lIGNvdW50aW5nIHVzaW5nIGBzdW1tYXJpc2VgLiBXaGF0IGlmIHdlIHdhbnRlZCB0byBrbm93IGhvdyBtYW55IGZsaWdodHMgY29ycmVzcG9uZCB0byBlYWNoIGNhcnJpZXI/CgpgYGB7cn0KaGZfc3VtbTIgPC0gaGYgJT4lCiAgICBncm91cF9ieShVbmlxdWVDYXJyaWVyKSAlPiUKICAgIHN1bW1hcml6ZSh0b3RhbF9jb3VudCA9IG4oKSkKaGZfc3VtbTIKYGBgCgpZb3UgY2FuIHNlZSB0aGUgYG4oKWAgZnVuY3Rpb24gYXQgd29yay4gSXQgc2ltcGx5IGNvdW50cyB0aGUgb2JzZXJ2YXRpb25zIGluIGVhY2ggZ3JvdXAuIChZb3UgZG8gbmVlZCB0byBpbmNsdWRlIHRoZSBlbXB0eSBwYXJlbnRoZXNlcyBhZnRlciB0aGUgYG5gISkgQWxzbyBub3RlIHRoYXQgd2UgY2FuIGdpdmUgdGhlIHN1bW1hcnkgY29sdW1uIGEgbmV3IG5hbWUgaWYgd2Ugd2lzaC4gSW4gYGhmX3N1bW1gLCB3ZSBkaWRuJ3QgZ2l2ZSB0aGUgbmV3IGNvbHVtbiBhbiBleHBsaWNpdCBuYW1lLCBzbyBpdCBzaG93ZWQgdXAgaW4gb3VyIGRhdGEgZnJhbWUgYXMgYSBjb2x1bW4gY2FsbGVkIGBtZWFuKERpc3RhbmNlKWAuIElmIHdlIGhhZG4ndCBpbmNsdWRlZCBgdG90YWxfY291bnRgIGluIG91ciBkZWZpbml0aW9uIG9mIGBoZl9zdW1tMmAsIHRoZSBuZXcgY29sdW1uIHdvdWxkIHNpbXBseSBiZSBjYWxsZWQgYG4oKWAuIFRoYXQncyBva2F5LCBidXQgbm90IGFueXdoZXJlIG5lYXIgYXMgdXNlci1mcmllbmRseSBhcyBgdG90YWxfY291bnRgLgoKIyMjIyMgRXhlcmNpc2UgNQoKQ3JlYXRlIGEgZGF0YSBmcmFtZSBjYWxsZWQgYGhmX3N1bW0zYCB0aGF0IGxpc3RzIHRoZSB0b3RhbCBjb3VudCBvZiBmbGlnaHRzIGZvciBlYWNoIGRheSBvZiB0aGUgd2Vlay4gQmUgc3VyZSB0byB1c2UgdGhlIHBpcGUgYXMgYWJvdmUuIFByaW50IHRoZSBvdXRwdXQgdG8gdGhlIHNjcmVlbgoKPGRpdiBjbGFzcyA9ICAiYW5zd2VyIj4KCmBgYHtyfQojIEFkZCBjb2RlIGhlcmUgdG8gZGVmaW5lIGhmX3N1bW0zLgpoZl9zdW1tMyA8LSBoZiAlPiUgCiAgZ3JvdXBfYnkoRGF5T2ZXZWVrKSAlPiUKICBzdW1tYXJpemUodG90YWxfY291bnQgPSBuKCkpCiMgQWRkIGNvZGUgaGVyZSB0byBwcmludCBoZl9zdW1tMy4KaGZfc3VtbTMKYGBgCgo8L2Rpdj4KCkRvIG5vdCBmb3JnZXQgdG8gcmVtb3ZlIGRhdGFzZXRzIHdoaWNoIHlvdSBkbyBub3QgbmVlZCBhbnltb3JlIGJ1dCBjb3N0IHRoZSBtZW1vcnkuCgpgYGB7cn0Kcm0oaGZfc3VtbV9ncm91cGVkLCBoZl9zdW1tLCBoZl9zdW1tMiwgaGZfc3VtbTMpCmBgYAoKIyMgUHV0dGluZyBpdCBhbGwgdG9nZXRoZXIKCk9mdGVuIHdlIG5lZWQgbW9yZSB0aGFuIG9uZSBvZiB0aGVzZSB2ZXJicy4gSW4gbWFueSBkYXRhIGFuYWx5c2VzLCB3ZSBuZWVkIHRvIGRvIGEgc2VxdWVuY2Ugb2Ygb3BlcmF0aW9ucyB0byBnZXQgYXQgdGhlIGFuc3dlciB3ZSBzZWVrLiBUaGlzIGlzIG1vc3QgZWFzaWx5IGFjY29tcGxpc2hlZCB1c2luZyBhIG1vcmUgY29tcGxpY2F0ZWQgc2VxdWVuY2Ugb2YgcGlwZXMuCgpIZXJlJ3MgYSBleGFtcGxlIG9mIG11bHRpLXN0ZXAgcGlwaW5nLiBMZXQncyBzYXkgdGhhdCB3ZSBvbmx5IGNhcmUgYWJvdXQgRGVsdGEgZmxpZ2h0cywgYW5kIGV2ZW4gdGhlbiwgd2Ugb25seSB3YW50IHRvIGtub3cgYWJvdXQgdGhlIG1vbnRoIG9mIHRoZSBmbGlnaHQgYW5kIHRoZSBkZXBhcnR1cmUgZGVsYXkuIEZyb20gdGhlcmUsIHdlIHdpc2ggdG8gZ3JvdXAgYnkgbW9udGhzIHNvIHdlIGNhbiBmaW5kIHRoZSBtYXhpbXVtIGRlcGFydHVyZSBkZWxheSBieSBtb250aC4gSGVyZSBpcyBhIHNvbHV0aW9uLCBwaXBpbmcgaG90IGFuZCByZWFkeSB0byBnby4gW2dyb2FuXQoKCmBgYHtyfQpoZl9ncmFuZF9maW5hbGUgPC0gaGYgJT4lCiAgICBmaWx0ZXIoVW5pcXVlQ2FycmllciA9PSAiREwiKSAlPiUKICAgIGdyb3VwX2J5KE1vbnRoKSAlPiUKICAgIHN1bW1hcmlzZShtYXhfZGVsYXkgPSBtYXgoRGVwRGVsYXksIG5hLnJtID0gVFJVRSkpCmhmX2dyYW5kX2ZpbmFsZQpgYGAKCkdvIHRocm91Z2ggZWFjaCBsaW5lIG9mIGNvZGUgY2FyZWZ1bGx5IGFuZCB0cmFuc2xhdGUgaXQgaW50byBFbmdsaXNoOgoKMS4gV2UgZGVmaW5lIGEgdmFyaWFibGUgY2FsbGVkIGBoZl9ncmFuZF9maW5hbGVgIHRoYXQgc3RhcnRzIHdpdGggdGhlIG9yaWdpbmFsIGBoZmAgZGF0YS4KMi4gV2UgYGZpbHRlcmAgdGhpcyBkYXRhIHNvIHRoYXQgb25seSBEZWx0YSBmbGlnaHRzIHdpbGwgYmUgYW5hbHl6ZWQuCjMuIFdlIGBncm91cF9ieWAgbW9udGggc28gdGhhdCB0aGUgcmVzdWx0cyB3aWxsIGJlIGRpc3BsYXllZCBieSBtb250aC4KNC4gV2UgYHN1bW1hcmlzZWAgZWFjaCBtb250aCBieSBsaXN0aW5nIHRoZSBtYXhpbXVtIHZhbHVlIG9mIGBEZXBEZWxheWAgdGhhdCBhcHBlYXJzIHdpdGhpbiBlYWNoIG1vbnRoLgo1LiBXZSBwcmludCB0aGUgcmVzdWx0IHRvIHRoZSBzY3JlZW4uCgoKIyMjIyMgRXhlcmNpc2UgNgoKQ3JlYXRlIGEgZGF0YSBmcmFtZSBjYWxsZWQgYGhmX2ZpbmFsMmAgdGhhdCBjb3VudHMgdGhlIGZsaWdodHMgdG8gTEFYIHRoYXQgd2VyZSBjYW5jZWxlZCwgZ3JvdXBpbmcgYnkgZGF5IG9mIHRoZSB3ZWVrLiAoSGludDogeW91IG5lZWQgdG8gYGZpbHRlcmAgdG8gZ2V0IGJvdGggZmxpZ2h0cyB0byBMQVggYW5kIGZsaWdodHMgdGhhdCBhcmUgY2FuY2VsZWQuIFRoZW4geW91J2xsIG5lZWQgdG8gYGdyb3VwX2J5YCB0aGUgZGF5IG9mIHRoZSB3ZWVrIGluIG9yZGVyIHRvIGBzdW1tYXJpc2VgIHRoZSBudW1iZXIgb2YgY2FuY2VsbGF0aW9ucyB1c2luZyBgbigpYC4pIFByaW50IHRoZSBvdXRwdXQgdG8gdGhlIHNjcmVlbi4KCjxkaXYgY2xhc3MgPSAgImFuc3dlciI+CgpgYGB7cn0KIyBBZGQgY29kZSBoZXJlIHRvIGNvdW50IHRoZSBmbGlnaHRzIHRvIExBWCB0aGF0IHdlcmUgY2FuY2VsZWQsCiMgZ3JvdXBpbmcgYnkgZGF5IG9mIHRoZSB3ZWVrLgojIFByaW50IHRoZSBvdXRwdXQgdG8gdGhlIHNjcmVlbi4KaGZfZmluYWwyIDwtIGhmICU+JQogICAgZmlsdGVyKERlc3QgPT0gIkxBWCIgJiBDYW5jZWxsZWQgPT0gMSkgJT4lCiAgICBncm91cF9ieShEYXlPZldlZWspICU+JQogICAgc3VtbWFyaXNlKExBWF9jYW5jZWwgPSBuKCkpCmhmX2ZpbmFsMgpgYGAKCjwvZGl2PgoKIyMjIyMgRXhlcmNpc2UgNwoKQ3JlYXRlIGEgZGF0YSBmcmFtZSBjYWxsZWQgYGhmX2ZpbmFsM2AgdGhhdCBmaW5kcyB0aGUgbWVkaWFuIGRpc3RhbmNlIGZsaWdodCBmb3IgZWFjaCBhaXJsaW5lLiBTb3J0IHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSBmcm9tIGhpZ2hlc3QgZGlzdGFuY2UgdG8gbG93ZXN0LiAoSGludDogWW91J2xsIG5lZWQgdG8gYGdyb3VwX2J5YCBjYXJyaWVyIGFuZCBgc3VtbWFyaXNlYCB1c2luZyB0aGUgYG1lZGlhbmAgZnVuY3Rpb24uIEZpbmFsbHksIHlvdSdsbCBuZWVkIHRvIGBhcnJhbmdlYCB0aGUgcmVzdWx0IGFjY29yZGluZyB0byB0aGUgbWVkaWFuIGRpc3RhbmNlIHZhcmlhYmxlIHRoYXQgeW91IGp1c3QgY3JlYXRlZC4pIFByaW50IHRoZSBvdXRwdXQgdG8gdGhlIHNjcmVlbi4KCjxkaXYgY2xhc3MgPSAgImFuc3dlciI+CgpgYGB7cn0KIyBBZGQgY29kZSBoZXJlIHRvIGZpbmQgdGhlIG1lZGlhbiBkaXN0YW5jZSBieSBhaXJsaW5lLgojIFByaW50IHRoZSBvdXRwdXQgdG8gdGhlIHNjcmVlbi4KaGZfZmluYWwzIDwtIGhmICU+JQogIGdyb3VwX2J5KFVuaXF1ZUNhcnJpZXIpICU+JQogIHN1bW1hcml6ZShtZWRfZGlzID0gbWVkaWFuKERpc3RhbmNlKSkgJT4lCiAgYXJyYW5nZShkZXNjKG1lZF9kaXMpKQpoZl9maW5hbDMKYGBgCgo8L2Rpdj4KCgojIyBDb25jbHVzaW9uCgpSYXcgZGF0YSBvZnRlbiBkb2Vzbid0IGNvbWUgaW4gdGhlIHJpZ2h0IGZvcm0gZm9yIHVzIHRvIHJ1biBvdXIgYW5hbHlzZXMuIFRoZSBgZHBseXJgIHZlcmJzIGFyZSBwb3dlcmZ1bCB0b29scyBmb3IgbWFuaXB1bGF0aW5nIGRhdGEgZnJhbWVzIHVudGlsIHRoZXkgYXJlIGluIHRoZSByaWdodCBmb3JtLgo=