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.csvcommand. 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 NAs 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:

  1. We define a variable called hf_grand_finale that starts with the original hf data.
  2. We filter this data so that only Delta flights will be analyzed.
  3. We group_by month so that the results will be displayed by month.
  4. We summarise each month by listing the maximum value of DepDelay that appears within each month.
  5. 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=