Introduction

In this class, we will use an R notebook to illustrate the code. We will explore the use of packages, and the tidyverse in particular. The tidyverse consists of a collection of packages to do data wrangling in R. All this functionality also exists in base R, but tidyverse makes it easier, more intuitive, and sometimes also faster. A more extensive discussion of this functionality is given in Chapter 3 of the R for Data Science book.

We will use this package to:

In addition, we will review several summary characteristics of data. We will also work with the package foreign to read and write dbf files, which we will use a lot later on.

Before we start, make sure you have set the current working directory. It should contain the files Community_Pop.csv and Community_Pop.dbf.

Working with Packages

Installing a package on your system

Install a package using the install.packages command. For example, we will be using the tidyverse package. As an alternative to the command line, you can use the Tools > Install Packages … command from the RStudio interface. To install tidyverse, we have to put its name in quotes (note, on my system this is already installed).

install.packages("tidyverse")
trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.5/tidyverse_1.2.1.tgz'
Content type 'application/x-gzip' length 88754 bytes (86 KB)
==================================================
downloaded 86 KB

The downloaded binary packages are in
    /var/folders/q_/19vkssyn1c9d1h56wzzl7s0c0000gn/T//Rtmpnbik2a/downloaded_packages

To check which packages you have installed, you can use installed.packages. On my system, this is a very long list, so I will assign it to an object (pp), and then use head to take a peek at the contents.

pp <- installed.packages()
head(pp)
             Package        LibPath                                                         
abind        "abind"        "/Library/Frameworks/R.framework/Versions/3.5/Resources/library"
acepack      "acepack"      "/Library/Frameworks/R.framework/Versions/3.5/Resources/library"
ade4         "ade4"         "/Library/Frameworks/R.framework/Versions/3.5/Resources/library"
adehabitatHR "adehabitatHR" "/Library/Frameworks/R.framework/Versions/3.5/Resources/library"
adehabitatHS "adehabitatHS" "/Library/Frameworks/R.framework/Versions/3.5/Resources/library"
adehabitatLT "adehabitatLT" "/Library/Frameworks/R.framework/Versions/3.5/Resources/library"
             Version  Priority
abind        "1.4-5"  NA      
acepack      "1.4.1"  NA      
ade4         "1.7-11" NA      
adehabitatHR "0.4.15" NA      
adehabitatHS "0.3.13" NA      
adehabitatLT "0.3.23" NA      
             Depends                                                               
abind        "R (>= 1.5.0)"                                                        
acepack      NA                                                                    
ade4         "R (>= 2.10)"                                                         
adehabitatHR "R (>= 3.0.1), sp, methods, deldir, ade4, adehabitatMA,\nadehabitatLT"
adehabitatHS "R (>= 3.0.1), sp, methods, ade4, adehabitatMA, adehabitatHR"         
adehabitatLT "R (>= 2.10.0), sp, methods, ade4, adehabitatMA, CircStats,\nstats"   
             Imports                                            LinkingTo
abind        "methods, utils"                                   NA       
acepack      NA                                                 NA       
ade4         "graphics, grDevices, methods, stats, utils, MASS" NA       
adehabitatHR "graphics, grDevices, stats"                       NA       
adehabitatHS "graphics, grDevices, stats"                       NA       
adehabitatLT "graphics, grDevices, utils"                       NA       
             Suggests                                                                                                  
abind        NA                                                                                                        
acepack      "testthat"                                                                                                
ade4         "ade4TkGUI, adegraphics, adephylo, ape, CircStats, deldir,\nlattice, pixmap, sp, spdep, splancs, waveslim"
adehabitatHR "maptools, tkrplot, MASS, rgeos"                                                                          
adehabitatHS "maptools, tkrplot, MASS, rgeos"                                                                          
adehabitatLT "maptools, tkrplot, MASS"                                                                                 
             Enhances License              License_is_FOSS License_restricts_use OS_type MD5sum
abind        NA       "LGPL (>= 2)"        NA              NA                    NA      NA    
acepack      NA       "MIT + file LICENSE" NA              NA                    NA      NA    
ade4         NA       "GPL (>= 2)"         NA              NA                    NA      NA    
adehabitatHR NA       "GPL (>= 2)"         NA              NA                    NA      NA    
adehabitatHS NA       "GPL (>= 2)"         NA              NA                    NA      NA    
adehabitatLT NA       "GPL (>= 2)"         NA              NA                    NA      NA    
             NeedsCompilation Built  
abind        "no"             "3.5.0"
acepack      "yes"            "3.5.0"
ade4         "yes"            "3.5.0"
adehabitatHR "yes"            "3.5.0"
adehabitatHS "yes"            "3.5.0"
adehabitatLT "yes"            "3.5.0"

We now also install the package foreign, in the same way as for tidyverse.

install.packages("foreign")
trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.5/foreign_0.8-71.tgz'
Content type 'application/x-gzip' length 325064 bytes (317 KB)
==================================================
downloaded 317 KB

The downloaded binary packages are in
    /var/folders/q_/19vkssyn1c9d1h56wzzl7s0c0000gn/T//Rtmpnbik2a/downloaded_packages

Invoking a package

Before we can use the functionality of tidyverse, we need to add it to our library, using the library command. It may seem a little counterintuitive that a package is called a library, but think of the packages as being out there on your system in general, and before you can use them in R, you need to add them to your own currently active library of functions.

library(tidyverse)
── Attaching packages ─────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
βœ” ggplot2 3.0.0     βœ” purrr   0.2.5
βœ” tibble  1.4.2     βœ” dplyr   0.7.6
βœ” tidyr   0.8.1     βœ” stringr 1.3.1
βœ” readr   1.1.1     βœ” forcats 0.3.0
── Conflicts ────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
βœ– dplyr::filter() masks stats::filter()
βœ– dplyr::lag()    masks stats::lag()

After you execute this command, you see a list of the different packages that are installed, including readr and dplyr. The Conflicts part means that the packages override the base R commands of the same name. For example, the interpretation of the filter command in dplyr overrides the filter command from stats. If you want to use the latter, you must specify it as stats::filter() (note, double colons). Typically, this will not be necessary, since you have installed the dplyr package for a reason, i.e., to use its filter command, not the other one.

We now also make the foreign package active using the library command.

library(foreign)

This time, there are no additional messages.

Reading and Writing Data

Reading spreadsheet-like data

We have already seen earlier how to read a file with comma separated values (csv) using the base R read.table command. We also noticed some issues when we had character data, the stringsAsFactors option.

The tidyverse has an alternative function to read such files, read_csv. It deals with the stringsAsFactors issue (strings are kept in character type) and some other issues as well. The result is a so-called tibble, which is basically a data.frame with some additional characteristics.

We will again use the Community_Pop.csv file as our input. We will create a data frame (tibble) as df by passing just the file name to the read_csv function (make sure the file name is in quotes).

df <- read_csv("Community_Pop.csv")
Parsed with column specification:
cols(
  NID = col_integer(),
  CommArea = col_character(),
  POP2010 = col_integer(),
  POP2000 = col_integer()
)

The result is somewhat different from what we saw when we used read.csv. Then, nothing happened, and we had to list the contents of df to see what was entered. Here, there is a brief summary of the variables and their types.

Characteristics of a tibble

When we list the just created df, we no longer get the full output, but an abbreviated list, with the types of the variables listed below them. Also, the table is no longer called a data frame (even though it is one), but a tibble.

df

Writing a data frame (or tibble)

Just as we did previously, we can now write out the tibble/data frame to another csv file with the function write_csv (note the underline instead of a period). In contrast to write.csv, we don’t have to worry about the row names (none are written as the default). For example, we can write df out to myfile2.csv (make sure to have it in quotes).

write_csv(df,"myfile2.csv")

From now on, when we read or write csv files, we will use read_csv and write_csv.

Reading and writing dbf files

Reading a dbf file

In many of our applications, the files will not be comma separated, but in the dBase file format, as a binary file. For example, the data associated with many GIS applications will come that way. There is no functionality to read dBase files in base R or in the tidyverse. Instead, we need to use the function read.dbf from the package foreign that we just installed and activated.

It operates in the same way. To create a data frame, you pass the name of the file. Note that this creates a data frame, not a tibble. For example, create the data frame dt from the file Community_Pop.dbf on your current working directory using read.dbf.

dt <- read.dbf("Community_Pop.dbf")

Check the first few lines using the head command.

head(dt)

Turning a data frame into a tibble

To turn a data frame into a tibble, you use as_tibble. For example, to turn dt into the tibble dt2.

dt2 <- as_tibble(dt)

Now, if we print it, we see the familiar data types listed under the variable names.

dt2

Sometimes, we may want to turn a tibble back into a data frame. To that effect, we can use as.data.frame. For example, the write.dbf function only works with data frames, not with tibbles.

Writing a dbf file

We write out our data frame in dbf format using write.dbf, in the same way as before by passing the name of the data frame and the file name (in quotes).

write.dbf(dt,"myfile3.dbf")

Practice

Turn the data contained in the file NYC_Sub_borough_Area.dbf into a tibble called nyc. Check how many observations and variables the data set contains (note, you must turn it into a tibble, not just a data frame).

Working with Variables (Columns)

Selecting variables with select

A column or variable can be selected from a tibble in the same way as for a data frame, using the dollar ($) notation or the double brackets [[ ]]. In addition, with the tidyverse, you can also specify the variable names in a select command, without quotes around the variable name.

For example, to create a new tibble with just the population values, i.e., the variables POP2000 and POP2010, we use select to assign the result to pop. We pass the table df and then a list of variables, separated by comma.

pop <- select(df,POP2000,POP2010)

When we type pop, we see the usual variable type under the variable name.

pop

Renaming variables with rename

Often, the variable names we get in data sources are not that informative or otherwise not exactly what we would want. The rename command lets us change a variable name. Again, we first pass the tibble, and then an expression with new name = old name. One way to remember the order is to think of it as computing a new variable, only the new variable is the same as the old variable, but with a different name.

For example, if we wanted to change CommArea to Community in df and assign the result to
df2 (note, again, no quotes for the variable names).

df2 <- rename(df,Community = CommArea)

Checking the contents of df2.

df2

Creating new variables with mutate

We typically want to carry out computations with the variables in our data frame and add the results to the data frame. This can be done using base R commands, but in the tidyverse it is easily accomplished by means of the mutate command. Again, we pass the data frame and the expression to be calculated as new_variable = expression. To make the addition permanent, we assign the result to a data frame (could be the original data frame).

For example, say we wanted to compute the ten year population change, i.e., the difference between POP2010 and POP2000. We use mutate with df and popdiff = POP2010 - POP2000. We assign the result back to df. We list the contents of df to check.

df <- mutate(df,popdiff = POP2010 - POP2000)
df

Now, let’s also create a logical variable that is TRUE for those community areas with a positive population growth, say popinc = popdiff > 0. Again, we add the variable to the existing data frame by assigning the result of the mutate operation back to df. We print the result to check.

df <- mutate(df,popinc = popdiff > 0)
df

Practice

Create a subset of the nyc data set that contains only the median rent variables (rent2002, rent2005, rent2008), the borough id (code), and name (subborough). Change the variable code to id. Create a new variable with a value of TRUE when the rent is zero.

Working with Observations (Rows)

Subsetting observations with filter

In order to select specific observations that meet a given criterion, we use filter. For example, if we wanted to extract the community areas that had positive population growth, we would use filter on the data frame df with popinc == TRUE (note the double equal sign). We assign the result to a new data frame, say dfpos and print it out to check.

dfpos <- filter(df,popinc == TRUE)
dfpos

The listing reveals that there are 17 rows. Later, we will see a couple of different ways to count how many community areas saw positive population growth.

Practice

Continue with the NYC rent data set just created and eliminate the observations with zero for the rent in any of the years.

Summaries

Summary on individual variables using summary

We have already seen the use of summary earlier. This works the same way on a tibble as on a data frame. For example, the summary statistics for our data set for the community areas are computed using summary.

summary(df)
      NID       CommArea            POP2010         POP2000          popdiff         popinc       
 Min.   : 1   Length:77          Min.   : 2876   Min.   :  3294   Min.   :-19013   Mode :logical  
 1st Qu.:20   Class :character   1st Qu.:18109   1st Qu.: 18165   1st Qu.: -6017   FALSE:60       
 Median :39   Mode  :character   Median :31028   Median : 33694   Median : -1596   TRUE :17       
 Mean   :39                      Mean   :35008   Mean   : 37611   Mean   : -2603                  
 3rd Qu.:58                      3rd Qu.:48743   3rd Qu.: 52723   3rd Qu.:  -192                  
 Max.   :77                      Max.   :98514   Max.   :117527   Max.   : 12895                  

Descriptive statistics using summarize

The summarize command computes specific descriptive statistics and assigns them to a new variable. All the statistics are then combined in a data frame with a single observation. For example, if we wanted the mean of the population in each of the two years, we create two new variables, say, m00 and m10 and set them equal to respectively mean(POP2000) and mean(POP2010). The summarize command takes the name of the data frame and the expressions entered. For example, for the mean.

summarize(df,m00 = mean(POP2000),m10 = mean(POP2010))

The values are the same as what we obtained from the summary. In addition to the mean, summarize can also yield the median, standard deviation (sd), inter-quartile range (IQR), minimum (min), maximum (max), quantile (quantile(variable, percentile)), counts of logical values (sum).

For example, to find out how many community areas have a positive population growth, we apply sum to popinc (remember that TRUE is the same as 1 and FALSE is 0, so the sum of the observations on a logical variable equals the number of TRUE).

summarize(df,c = sum(popinc))

Summaries by subset using group_by

The main power of summarize is its use in combination with group_by, which computes the descriptive statistics for subsets of the data. For example, say we wanted to compute the mean population separately for the community areas that saw growth and those that saw decline.

We could of course use filter to create two separate data frames and repeat the calculation for each of them. Instead, we use group_by to make the grouping internal to the data frame and then apply the summarize.

For example, first we create the data frame dfgroup using group_by with popinc. We also print the result to check it.

dfgroup <- group_by(df,popinc)
dfgroup

We can’t really see any difference. But now, if we use summarize for the two means on the new data frame, the results give us two rows, one for popinc FALSE and one for TRUE.

summarize(dfgroup,m00 = mean(POP2000),m10 = mean(POP2010))

A very useful summary statistic is n() which gives the count of observations by group, contained in the variable count.

summarize(dfgroup,count=n())

Practice

Compute the median of the median rent in 2008 for the NYC sub-boroughs grouped by whether they had above or below median rent in 2002.

Chaining commands

The real power of the various manipulations of data frames using the tidyverse is by chaining commands using the so-called pipe. In the manipulations above we had to create a new data frame each time, but those were not necessarily of interest in and of themselves. The pipe command, symbolized as %>% moves the output of one operation into the input of the next operation.

For example, say we wanted to compute the logical variable popinc and then immediately use that to group the observations and compute the mean by group. So, basically, the same as what we did before. Now we organize these operations by starting with our data frame, piping it into the mutate operation to compute the new variable, then pipe that into the group_by operation and finally into the summarize.

df %>% mutate(popch = (POP2010 - POP2000) > 0) %>%
  group_by(popch) %>% summarize(m00 = mean(POP2000),m10 = mean(POP2010), count = n())

If we want to keep the results as a new data frame, we assign it to a new object.

dfch <- df %>% mutate(popch = (POP2010 - POP2000) > 0) %>%
  group_by(popch) %>% summarize(m00 = mean(POP2000),m10 = mean(POP2010),count=n())
dfch

Practice

Create a new data frame with the summary statistics for the rent in 2005 and 2008 grouped by whether the sub-boroughs were above or below the median in 2002.

LS0tCnRpdGxlOiAiTWFuaXB1bGF0aW5nIERhdGEgRnJhbWVzIgphdXRob3I6ICJMdWMgQW5zZWxpbiIKZGF0ZTogIjEwLzEzLzIwMTgiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIEludHJvZHVjdGlvbgoKSW4gdGhpcyBjbGFzcywgd2Ugd2lsbCB1c2UgYW4gUiAqKm5vdGVib29rKiogdG8gaWxsdXN0cmF0ZSB0aGUgY29kZS4gV2Ugd2lsbCBleHBsb3JlIHRoZSB1c2Ugb2YgcGFja2FnZXMsCmFuZCB0aGUgYHRpZHl2ZXJzZWAgaW4gcGFydGljdWxhci4gVGhlIGB0aWR5dmVyc2VgIGNvbnNpc3RzIG9mIGEgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyB0byBkbyAqZGF0YSB3cmFuZ2xpbmcqCmluIFIuIEFsbCB0aGlzIGZ1bmN0aW9uYWxpdHkgYWxzbyBleGlzdHMgaW4gYmFzZSBSLCBidXQgYHRpZHl2ZXJzZWAgbWFrZXMgaXQgZWFzaWVyLCBtb3JlIGludHVpdGl2ZSwgYW5kIHNvbWV0aW1lcwphbHNvIGZhc3Rlci4gQSBtb3JlIGV4dGVuc2l2ZSBkaXNjdXNzaW9uIG9mIHRoaXMgZnVuY3Rpb25hbGl0eSBpcyBnaXZlbiBpbiBDaGFwdGVyIDMgb2YgdGhlICpSIGZvciBEYXRhIFNjaWVuY2UqIGJvb2suCgpXZSB3aWxsIHVzZSB0aGlzIHBhY2thZ2UgdG86CgotIHJlYWQgYW5kIHdyaXRlIHNwcmVhZHNoZWV0LWxpa2UgZGF0YQoKLSBleHRyYWN0IHZhcmlhYmxlcwoKLSBjcmVhdGUgbmV3IHZhcmlhYmxlcwoKLSBzZWxlY3Qgcm93cwoKLSBjcmVhdGUgc3Vic2V0cyBvZiB0aGUgZGF0YQoKSW4gYWRkaXRpb24sIHdlIHdpbGwgcmV2aWV3IHNldmVyYWwgc3VtbWFyeSBjaGFyYWN0ZXJpc3RpY3Mgb2YgZGF0YS4gV2Ugd2lsbCBhbHNvIHdvcmsgd2l0aCB0aGUKcGFja2FnZSBgZm9yZWlnbmAgdG8gcmVhZCBhbmQgd3JpdGUgKipkYmYqKiBmaWxlcywgd2hpY2ggd2Ugd2lsbCB1c2UgYSBsb3QgbGF0ZXIgb24uCgpCZWZvcmUgd2Ugc3RhcnQsIG1ha2Ugc3VyZSB5b3UgaGF2ZSBzZXQgdGhlIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkuIEl0IHNob3VsZCBjb250YWluCnRoZSBmaWxlcyAqKkNvbW11bml0eV9Qb3AuY3N2KiogYW5kICoqQ29tbXVuaXR5X1BvcC5kYmYqKi4KCiMjIFdvcmtpbmcgd2l0aCBQYWNrYWdlcwoKIyMjIEluc3RhbGxpbmcgYSBwYWNrYWdlIG9uIHlvdXIgc3lzdGVtCgpJbnN0YWxsIGEgcGFja2FnZSB1c2luZyB0aGUgYGluc3RhbGwucGFja2FnZXNgIGNvbW1hbmQuIEZvciBleGFtcGxlLCB3ZSB3aWxsIGJlIHVzaW5nIHRoZSBgdGlkeXZlcnNlYCBwYWNrYWdlLiBBcyBhbiBhbHRlcm5hdGl2ZSB0byB0aGUgY29tbWFuZCBsaW5lLCB5b3UgY2FuIHVzZSB0aGUgVG9vbHMgPiBJbnN0YWxsIFBhY2thZ2VzIC4uLiBjb21tYW5kIGZyb20gdGhlIFJTdHVkaW8gaW50ZXJmYWNlLiBUbyBpbnN0YWxsIGB0aWR5dmVyc2VgLCB3ZSBoYXZlIHRvIHB1dCBpdHMgbmFtZSBpbiBxdW90ZXMgKG5vdGUsIG9uIG15IHN5c3RlbSB0aGlzIGlzIGFscmVhZHkgaW5zdGFsbGVkKS4KCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQpgYGAKClRvIGNoZWNrIHdoaWNoIHBhY2thZ2VzIHlvdSBoYXZlIGluc3RhbGxlZCwgeW91IGNhbiB1c2UgYGluc3RhbGxlZC5wYWNrYWdlc2AuIE9uIG15IHN5c3RlbSwgdGhpcyBpcyBhIHZlcnkgbG9uZyBsaXN0LCAgc28gSSB3aWxsIGFzc2lnbiBpdCB0byBhbiBvYmplY3QgKHBwKSwgYW5kIHRoZW4gdXNlIGBoZWFkYCB0byB0YWtlIGEgcGVlayBhdCB0aGUgY29udGVudHMuCgpgYGB7cn0KcHAgPC0gaW5zdGFsbGVkLnBhY2thZ2VzKCkKaGVhZChwcCkKYGBgCgpXZSBub3cgYWxzbyBpbnN0YWxsIHRoZSBwYWNrYWdlIGBmb3JlaWduYCwgaW4gdGhlIHNhbWUgd2F5IGFzIGZvciBgdGlkeXZlcnNlYC4KCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJmb3JlaWduIikKYGBgCgojIyMgSW52b2tpbmcgYSBwYWNrYWdlIAoKQmVmb3JlIHdlIGNhbiB1c2UgdGhlIGZ1bmN0aW9uYWxpdHkgb2YgIGB0aWR5dmVyc2VgLCB3ZSBuZWVkIHRvIGFkZCBpdCB0byBvdXIgbGlicmFyeSwgdXNpbmcgdGhlIGBsaWJyYXJ5YCBjb21tYW5kLgpJdCBtYXkgc2VlbSBhIGxpdHRsZSBjb3VudGVyaW50dWl0aXZlIHRoYXQgYSBwYWNrYWdlIGlzIGNhbGxlZCBhIGxpYnJhcnksIGJ1dCB0aGluayBvZiB0aGUgcGFja2FnZXMgYXMgYmVpbmcKb3V0IHRoZXJlIG9uIHlvdXIgc3lzdGVtIGluIGdlbmVyYWwsIGFuZCBiZWZvcmUgeW91IGNhbiB1c2UgdGhlbSBpbiBSLCB5b3UgbmVlZCB0byBhZGQgdGhlbSB0byB5b3VyIG93biBjdXJyZW50bHkgYWN0aXZlIGxpYnJhcnkgb2YgZnVuY3Rpb25zLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCkFmdGVyIHlvdSBleGVjdXRlIHRoaXMgY29tbWFuZCwgeW91IHNlZSBhIGxpc3Qgb2YgdGhlIGRpZmZlcmVudCBwYWNrYWdlcyB0aGF0IGFyZSBpbnN0YWxsZWQsIGluY2x1ZGluZwpgcmVhZHJgIGFuZCBgZHBseXJgLiBUaGUgQ29uZmxpY3RzIHBhcnQgbWVhbnMgdGhhdCB0aGUgcGFja2FnZXMgb3ZlcnJpZGUgdGhlIGJhc2UgUiBjb21tYW5kcyBvZiB0aGUgc2FtZSAKbmFtZS4gRm9yIGV4YW1wbGUsIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgYGZpbHRlcmAgY29tbWFuZCBpbiBgZHBseXJgIG92ZXJyaWRlcyB0aGUgYGZpbHRlcmAgY29tbWFuZCBmcm9tCmBzdGF0c2AuIElmIHlvdSB3YW50IHRvIHVzZSB0aGUgbGF0dGVyLCB5b3UgbXVzdCBzcGVjaWZ5IGl0IGFzIGBzdGF0czo6ZmlsdGVyKClgIChub3RlLCBkb3VibGUgY29sb25zKS4gVHlwaWNhbGx5LAp0aGlzIHdpbGwgbm90IGJlIG5lY2Vzc2FyeSwgc2luY2UgeW91IGhhdmUgaW5zdGFsbGVkIHRoZSBgZHBseXJgIHBhY2thZ2UgZm9yIGEgcmVhc29uLCBpLmUuLCB0byB1c2UgaXRzIGBmaWx0ZXJgCmNvbW1hbmQsIG5vdCB0aGUgb3RoZXIgb25lLgoKV2Ugbm93IGFsc28gbWFrZSB0aGUgYGZvcmVpZ25gIHBhY2thZ2UgYWN0aXZlIHVzaW5nIHRoZSBgbGlicmFyeWAgY29tbWFuZC4KCmBgYHtyfQpsaWJyYXJ5KGZvcmVpZ24pCmBgYAoKVGhpcyB0aW1lLCB0aGVyZSBhcmUgbm8gYWRkaXRpb25hbCBtZXNzYWdlcy4KCiMjIFJlYWRpbmcgYW5kIFdyaXRpbmcgRGF0YQoKIyMjIFJlYWRpbmcgc3ByZWFkc2hlZXQtbGlrZSBkYXRhCgpXZSBoYXZlIGFscmVhZHkgc2VlbiBlYXJsaWVyIGhvdyB0byByZWFkIGEgZmlsZSB3aXRoIGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMgKGNzdikgdXNpbmcgdGhlIGJhc2UgUiBgcmVhZC50YWJsZWAgY29tbWFuZC4gV2UgYWxzbyBub3RpY2VkIHNvbWUgaXNzdWVzIHdoZW4gd2UgaGFkIGNoYXJhY3RlciBkYXRhLCB0aGUgYHN0cmluZ3NBc0ZhY3RvcnNgIG9wdGlvbi4KClRoZSBgdGlkeXZlcnNlYCBoYXMgYW4gYWx0ZXJuYXRpdmUgZnVuY3Rpb24gdG8gcmVhZCBzdWNoIGZpbGVzLCBgcmVhZF9jc3ZgLiBJdCBkZWFscyB3aXRoIHRoZSBgc3RyaW5nc0FzRmFjdG9yc2AgaXNzdWUgKHN0cmluZ3MgYXJlIGtlcHQgaW4gY2hhcmFjdGVyIHR5cGUpIGFuZCBzb21lIG90aGVyIGlzc3VlcyBhcyB3ZWxsLiBUaGUgcmVzdWx0IGlzIGEgc28tY2FsbGVkIGB0aWJibGVgLCB3aGljaCBpcyBiYXNpY2FsbHkgYSBgZGF0YS5mcmFtZWAgd2l0aCBzb21lIGFkZGl0aW9uYWwgY2hhcmFjdGVyaXN0aWNzLgoKV2Ugd2lsbCBhZ2FpbiB1c2UgdGhlICoqQ29tbXVuaXR5X1BvcC5jc3YqKiBmaWxlIGFzIG91ciBpbnB1dC4gV2Ugd2lsbCBjcmVhdGUgYSBkYXRhIGZyYW1lICh0aWJibGUpCmFzICoqZGYqKiBieSBwYXNzaW5nIGp1c3QgdGhlIGZpbGUgbmFtZSB0byB0aGUgYHJlYWRfY3N2YCBmdW5jdGlvbiAobWFrZSBzdXJlIHRoZSBmaWxlIG5hbWUgaXMKaW4gcXVvdGVzKS4KCmBgYHtyfQpkZiA8LSByZWFkX2NzdigiQ29tbXVuaXR5X1BvcC5jc3YiKQpgYGAKClRoZSByZXN1bHQgaXMgc29tZXdoYXQgZGlmZmVyZW50IGZyb20gd2hhdCB3ZSBzYXcgd2hlbiB3ZSB1c2VkIGByZWFkLmNzdmAuIFRoZW4sIG5vdGhpbmcgaGFwcGVuZWQsIGFuZCB3ZSBoYWQgdG8gbGlzdCB0aGUgY29udGVudHMgb2YgKipkZioqIHRvIHNlZSB3aGF0IHdhcyBlbnRlcmVkLiBIZXJlLCB0aGVyZSBpcyBhIGJyaWVmIHN1bW1hcnkgb2YgdGhlIHZhcmlhYmxlcyBhbmQgdGhlaXIgdHlwZXMuIAoKIyMjIyBDaGFyYWN0ZXJpc3RpY3Mgb2YgYSB0aWJibGUKCldoZW4gd2UgbGlzdCB0aGUganVzdCBjcmVhdGVkICoqZGYqKiwgd2Ugbm8gbG9uZ2VyIGdldCB0aGUgZnVsbCBvdXRwdXQsIGJ1dCBhbiBhYmJyZXZpYXRlZCBsaXN0LCB3aXRoIHRoZSB0eXBlcyBvZiB0aGUgdmFyaWFibGVzIGxpc3RlZCBiZWxvdyB0aGVtLiBBbHNvLCB0aGUgdGFibGUgaXMgbm8gbG9uZ2VyIGNhbGxlZCBhIGRhdGEgZnJhbWUgKGV2ZW4gdGhvdWdoIGl0IGlzIG9uZSksIGJ1dCBhICoqdGliYmxlKiouCgpgYGB7cn0KZGYKYGBgCgojIyMgV3JpdGluZyBhIGRhdGEgZnJhbWUgKG9yIHRpYmJsZSkKCkp1c3QgYXMgd2UgZGlkIHByZXZpb3VzbHksIHdlIGNhbiBub3cgd3JpdGUgb3V0IHRoZSB0aWJibGUvZGF0YSBmcmFtZSB0byBhbm90aGVyIGNzdiBmaWxlCndpdGggdGhlIGZ1bmN0aW9uIGB3cml0ZV9jc3ZgIChub3RlIHRoZSB1bmRlcmxpbmUgaW5zdGVhZCBvZiBhIHBlcmlvZCkuIEluIGNvbnRyYXN0IHRvCmB3cml0ZS5jc3ZgLCB3ZSBkb24ndCBoYXZlIHRvIHdvcnJ5IGFib3V0IHRoZSByb3cgbmFtZXMgKG5vbmUgYXJlIHdyaXR0ZW4gYXMgdGhlIGRlZmF1bHQpLgpGb3IgZXhhbXBsZSwgd2UgY2FuIHdyaXRlICoqZGYqKiBvdXQgdG8gKipteWZpbGUyLmNzdioqIChtYWtlIHN1cmUgdG8gaGF2ZSBpdCBpbiBxdW90ZXMpLgoKYGBge3J9CndyaXRlX2NzdihkZiwibXlmaWxlMi5jc3YiKQpgYGAKCkZyb20gbm93IG9uLCB3aGVuIHdlIHJlYWQgb3Igd3JpdGUgY3N2IGZpbGVzLCB3ZSB3aWxsIHVzZSBgcmVhZF9jc3ZgIGFuZCBgd3JpdGVfY3N2YC4KCiMjIyBSZWFkaW5nIGFuZCB3cml0aW5nIGRiZiBmaWxlcwoKIyMjIyBSZWFkaW5nIGEgZGJmIGZpbGUKCkluIG1hbnkgb2Ygb3VyIGFwcGxpY2F0aW9ucywgdGhlIGZpbGVzIHdpbGwgbm90IGJlIGNvbW1hIHNlcGFyYXRlZCwgYnV0IGluIHRoZSBkQmFzZSBmaWxlIGZvcm1hdCwKYXMgYSBiaW5hcnkgZmlsZS4gRm9yIGV4YW1wbGUsIHRoZSBkYXRhIGFzc29jaWF0ZWQgd2l0aCBtYW55IEdJUyBhcHBsaWNhdGlvbnMgd2lsbCBjb21lIHRoYXQgd2F5LgpUaGVyZSBpcyBubyBmdW5jdGlvbmFsaXR5IHRvIHJlYWQgZEJhc2UgZmlsZXMgaW4gYmFzZSBSIG9yIGluIHRoZSBgdGlkeXZlcnNlYC4gSW5zdGVhZCwgd2UgbmVlZAp0byB1c2UgdGhlIGZ1bmN0aW9uIGByZWFkLmRiZmAgZnJvbSB0aGUgcGFja2FnZSBgZm9yZWlnbmAgdGhhdCB3ZSBqdXN0IGluc3RhbGxlZCBhbmQgYWN0aXZhdGVkLgoKSXQgb3BlcmF0ZXMgaW4gdGhlIHNhbWUgd2F5LiBUbyBjcmVhdGUgYSBkYXRhIGZyYW1lLCB5b3UgIHBhc3MgdGhlIG5hbWUgb2YgdGhlIGZpbGUuIE5vdGUgdGhhdAp0aGlzICBjcmVhdGVzIGEgZGF0YSBmcmFtZSwgbm90IGEgdGliYmxlLiBGb3IgZXhhbXBsZSwgY3JlYXRlIHRoZSBkYXRhIGZyYW1lICoqZHQqKiBmcm9tIHRoZQpmaWxlICoqQ29tbXVuaXR5X1BvcC5kYmYqKiBvbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkgdXNpbmcgYHJlYWQuZGJmYC4KCmBgYHtyfQpkdCA8LSByZWFkLmRiZigiQ29tbXVuaXR5X1BvcC5kYmYiKQpgYGAKCkNoZWNrIHRoZSBmaXJzdCBmZXcgbGluZXMgdXNpbmcgdGhlIGBoZWFkYCBjb21tYW5kLgoKYGBge3J9CmhlYWQoZHQpCmBgYAoKIyMjIyBUdXJuaW5nIGEgZGF0YSBmcmFtZSBpbnRvIGEgdGliYmxlCgpUbyB0dXJuIGEgZGF0YSBmcmFtZSBpbnRvIGEgdGliYmxlLCB5b3UgdXNlIGBhc190aWJibGVgLiBGb3IgZXhhbXBsZSwgdG8gdHVybiAqKmR0KiogIGludG8gIHRoZQp0aWJibGUgKipkdDIqKi4KCmBgYHtyfQpkdDIgPC0gYXNfdGliYmxlKGR0KQpgYGAKCk5vdywgaWYgd2UgcHJpbnQgaXQsIHdlIHNlZSB0aGUgZmFtaWxpYXIgZGF0YSB0eXBlcyBsaXN0ZWQgdW5kZXIgdGhlIHZhcmlhYmxlIG5hbWVzLgoKYGBge3J9CmR0MgpgYGAKClNvbWV0aW1lcywgd2UgbWF5IHdhbnQgdG8gdHVybiBhIHRpYmJsZSBiYWNrIGludG8gYSBkYXRhIGZyYW1lLiBUbyB0aGF0IGVmZmVjdCwgd2UKY2FuIHVzZSBgYXMuZGF0YS5mcmFtZWAuIEZvciBleGFtcGxlLCB0aGUgYHdyaXRlLmRiZmAgZnVuY3Rpb24gb25seSB3b3JrcyB3aXRoIGRhdGEgZnJhbWVzLCBub3QKd2l0aCB0aWJibGVzLgoKCiMjIyMgV3JpdGluZyBhIGRiZiBmaWxlCgpXZSB3cml0ZSBvdXQgb3VyIGRhdGEgZnJhbWUgaW4gZGJmIGZvcm1hdCB1c2luZyBgd3JpdGUuZGJmYCwgaW4gdGhlIHNhbWUgd2F5IGFzIGJlZm9yZQpieSBwYXNzaW5nIHRoZSBuYW1lIG9mIHRoZSBkYXRhIGZyYW1lIGFuZCB0aGUgZmlsZSBuYW1lIChpbiBxdW90ZXMpLgoKYGBge3J9CndyaXRlLmRiZihkdCwibXlmaWxlMy5kYmYiKQpgYGAKCiMjIyBQcmFjdGljZQoKVHVybiB0aGUgZGF0YSBjb250YWluZWQgaW4gdGhlIGZpbGUgKipOWUNfU3ViX2Jvcm91Z2hfQXJlYS5kYmYqKiBpbnRvIGEgdGliYmxlIGNhbGxlZCAqKm55YyoqLgpDaGVjayBob3cgbWFueSBvYnNlcnZhdGlvbnMgYW5kIHZhcmlhYmxlcyB0aGUgZGF0YSBzZXQgY29udGFpbnMgKG5vdGUsIHlvdSBtdXN0IHR1cm4gaXQgaW50bwphIHRpYmJsZSwgbm90IGp1c3QgYSBkYXRhIGZyYW1lKS4KCiMjIFdvcmtpbmcgd2l0aCBWYXJpYWJsZXMgKENvbHVtbnMpCgojIyMgU2VsZWN0aW5nIHZhcmlhYmxlcyB3aXRoIGBzZWxlY3RgCgpBIGNvbHVtbiBvciB2YXJpYWJsZSBjYW4gYmUgc2VsZWN0ZWQgZnJvbSBhIHRpYmJsZSBpbiB0aGUgc2FtZSB3YXkgYXMgZm9yIGEgZGF0YSBmcmFtZSwgdXNpbmcKdGhlIGRvbGxhciAoJCkgbm90YXRpb24gb3IgdGhlIGRvdWJsZSBicmFja2V0cyBbWyBdXS4gSW4gYWRkaXRpb24sIHdpdGggdGhlIHRpZHl2ZXJzZSwgeW91IGNhbiBhbHNvIHNwZWNpZnkgdGhlIHZhcmlhYmxlIG5hbWVzIGluIGEgYHNlbGVjdGAgY29tbWFuZCwgd2l0aG91dCBxdW90ZXMgYXJvdW5kIHRoZSB2YXJpYWJsZSBuYW1lLgoKRm9yIGV4YW1wbGUsIHRvIGNyZWF0ZSAgYSBuZXcgdGliYmxlIHdpdGgganVzdCB0aGUgcG9wdWxhdGlvbiB2YWx1ZXMsIGkuZS4sIHRoZSB2YXJpYWJsZXMgKipQT1AyMDAwKioKYW5kICoqUE9QMjAxMCoqLCB3ZSB1c2UgYHNlbGVjdGAgdG8gYXNzaWduIHRoZSByZXN1bHQgdG8gKipwb3AqKi4gV2UgcGFzcyB0aGUgdGFibGUgKipkZioqIGFuZCB0aGVuCmEgbGlzdCBvZiB2YXJpYWJsZXMsIHNlcGFyYXRlZCBieSBjb21tYS4KCmBgYHtyfQpwb3AgPC0gc2VsZWN0KGRmLFBPUDIwMDAsUE9QMjAxMCkKYGBgCgpXaGVuIHdlIHR5cGUgKipwb3AqKiwgd2Ugc2VlIHRoZSB1c3VhbCB2YXJpYWJsZSB0eXBlIHVuZGVyIHRoZSAgdmFyaWFibGUgbmFtZS4KCmBgYHtyfQpwb3AKYGBgCgojIyMgUmVuYW1pbmcgdmFyaWFibGVzIHdpdGggIGByZW5hbWVgCgpPZnRlbiwgdGhlIHZhcmlhYmxlIG5hbWVzIHdlIGdldCBpbiBkYXRhIHNvdXJjZXMgYXJlIG5vdCB0aGF0IGluZm9ybWF0aXZlIG9yIG90aGVyd2lzZSBub3QgZXhhY3RseSB3aGF0IHdlIHdvdWxkIHdhbnQuIFRoZSBgcmVuYW1lYCBjb21tYW5kIGxldHMgdXMgY2hhbmdlIGEgdmFyaWFibGUgbmFtZS4gQWdhaW4sIHdlIGZpcnN0IHBhc3MgdGhlIHRpYmJsZSwgYW5kIHRoZW4gYW4gZXhwcmVzc2lvbiB3aXRoIG5ldyBuYW1lID0gb2xkIG5hbWUuIE9uZSB3YXkgdG8gcmVtZW1iZXIgdGhlIG9yZGVyIGlzIHRvIHRoaW5rIG9mIGl0IGFzIGNvbXB1dGluZyBhIG5ldyB2YXJpYWJsZSwgb25seSB0aGUgbmV3IHZhcmlhYmxlIGlzIHRoZSBzYW1lIGFzIHRoZSBvbGQgdmFyaWFibGUsIGJ1dCB3aXRoIGEgZGlmZmVyZW50IG5hbWUuCgpGb3IgZXhhbXBsZSwgaWYgd2Ugd2FudGVkIHRvIGNoYW5nZSAqKkNvbW1BcmVhKiogIHRvICoqQ29tbXVuaXR5KiogaW4gKipkZioqIGFuZCBhc3NpZ24gdGhlIHJlc3VsdCB0byAgCioqZGYyKiogKG5vdGUsIGFnYWluLCBubyBxdW90ZXMgZm9yIHRoZSB2YXJpYWJsZSBuYW1lcykuCgpgYGB7cn0KZGYyIDwtIHJlbmFtZShkZixDb21tdW5pdHkgPSBDb21tQXJlYSkKYGBgCgpDaGVja2luZyB0aGUgY29udGVudHMgb2YgKipkZjIqKi4KCmBgYHtyfQpkZjIKYGBgCgojIyMgQ3JlYXRpbmcgbmV3IHZhcmlhYmxlcyB3aXRoIGBtdXRhdGVgCgpXZSB0eXBpY2FsbHkgd2FudCB0byBjYXJyeSBvdXQgY29tcHV0YXRpb25zIHdpdGggdGhlIHZhcmlhYmxlcyBpbiBvdXIgZGF0YSBmcmFtZSBhbmQgYWRkIHRoZQpyZXN1bHRzIHRvIHRoZSBkYXRhIGZyYW1lLiBUaGlzIGNhbiBiZSBkb25lIHVzaW5nIGJhc2UgUiBjb21tYW5kcywgYnV0IGluIHRoZSB0aWR5dmVyc2UgaXQgaXMKZWFzaWx5IGFjY29tcGxpc2hlZCBieSBtZWFucyBvZiB0aGUgYG11dGF0ZWAgIGNvbW1hbmQuIEFnYWluLCB3ZSBwYXNzIHRoZSBkYXRhIGZyYW1lIGFuZCB0aGUKZXhwcmVzc2lvbiB0byBiZSBjYWxjdWxhdGVkIGFzIG5ld192YXJpYWJsZSA9IGV4cHJlc3Npb24uIFRvIG1ha2UgdGhlIGFkZGl0aW9uIHBlcm1hbmVudCwgd2UKYXNzaWduIHRoZSByZXN1bHQgdG8gYSBkYXRhIGZyYW1lIChjb3VsZCBiZSB0aGUgb3JpZ2luYWwgZGF0YSBmcmFtZSkuCgpGb3IgZXhhbXBsZSwgc2F5IHdlIHdhbnRlZCB0byBjb21wdXRlIHRoZSB0ZW4geWVhciBwb3B1bGF0aW9uIGNoYW5nZSwgaS5lLiwgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbgpQT1AyMDEwIGFuZCBQT1AyMDAwLiBXZSB1c2UgYG11dGF0ZWAgd2l0aCAqKmRmKiogYW5kIGBwb3BkaWZmID0gUE9QMjAxMCAtIFBPUDIwMDBgLiBXZSBhc3NpZ24gdGhlCnJlc3VsdCBiYWNrIHRvICoqZGYqKi4gV2UgbGlzdCB0aGUgY29udGVudHMgb2YgKipkZioqIHRvIGNoZWNrLgoKYGBge3J9CmRmIDwtIG11dGF0ZShkZixwb3BkaWZmID0gUE9QMjAxMCAtIFBPUDIwMDApCmRmCmBgYAoKTm93LCBsZXQncyBhbHNvIGNyZWF0ZSBhIGxvZ2ljYWwgdmFyaWFibGUgdGhhdCBpcyBUUlVFIGZvciB0aG9zZSBjb21tdW5pdHkgYXJlYXMgd2l0aCBhIHBvc2l0aXZlIHBvcHVsYXRpb24gZ3Jvd3RoLCBzYXkgYHBvcGluYyA9IHBvcGRpZmYgPiAwYC4gQWdhaW4sIHdlIGFkZCB0aGUgdmFyaWFibGUgdG8gdGhlIGV4aXN0aW5nIGRhdGEgZnJhbWUgYnkgYXNzaWduaW5nIHRoZSByZXN1bHQgb2YgdGhlIGBtdXRhdGVgIG9wZXJhdGlvbiBiYWNrIHRvICoqZGYqKi4gV2UgcHJpbnQgdGhlIHJlc3VsdCB0byBjaGVjay4KCmBgYHtyfQpkZiA8LSBtdXRhdGUoZGYscG9waW5jID0gcG9wZGlmZiA+IDApCmRmCmBgYAoKIyMjIFByYWN0aWNlCgpDcmVhdGUgYSBzdWJzZXQgb2YgdGhlICoqbnljKiogZGF0YSBzZXQgdGhhdCBjb250YWlucyBvbmx5IHRoZSBtZWRpYW4gcmVudCB2YXJpYWJsZXMKKHJlbnQyMDAyLCByZW50MjAwNSwgcmVudDIwMDgpLCB0aGUgYm9yb3VnaCBpZCAoY29kZSksIGFuZCBuYW1lIChzdWJib3JvdWdoKS4gQ2hhbmdlIHRoZQp2YXJpYWJsZSBjb2RlIHRvIGlkLiBDcmVhdGUgYSBuZXcgdmFyaWFibGUgd2l0aCBhIHZhbHVlIG9mIFRSVUUgd2hlbiB0aGUgcmVudCBpcyB6ZXJvLgoKCiMjIFdvcmtpbmcgd2l0aCBPYnNlcnZhdGlvbnMgKFJvd3MpCgojIyMgU3Vic2V0dGluZyBvYnNlcnZhdGlvbnMgd2l0aCBgZmlsdGVyYAoKSW4gb3JkZXIgdG8gc2VsZWN0IHNwZWNpZmljIG9ic2VydmF0aW9ucyB0aGF0IG1lZXQgYSBnaXZlbiBjcml0ZXJpb24sIHdlIHVzZSBgZmlsdGVyYC4gRm9yIGV4YW1wbGUsCmlmIHdlIHdhbnRlZCB0byBleHRyYWN0IHRoZSBjb21tdW5pdHkgYXJlYXMgdGhhdCBoYWQgcG9zaXRpdmUgcG9wdWxhdGlvbiBncm93dGgsIHdlIHdvdWxkIHVzZQpgZmlsdGVyYCBvbiB0aGUgZGF0YSBmcmFtZSAqKmRmKiogd2l0aCBgcG9waW5jID09IFRSVUVgIChub3RlIHRoZSBkb3VibGUgZXF1YWwgc2lnbikuIFdlIGFzc2lnbiB0aGUgcmVzdWx0IHRvIGEgbmV3IGRhdGEgZnJhbWUsIHNheSAqKmRmcG9zKiogYW5kIHByaW50IGl0IG91dCB0byBjaGVjay4KCmBgYHtyfQpkZnBvcyA8LSBmaWx0ZXIoZGYscG9waW5jID09IFRSVUUpCmRmcG9zCmBgYAoKVGhlIGxpc3RpbmcgcmV2ZWFscyB0aGF0IHRoZXJlIGFyZSAxNyByb3dzLiBMYXRlciwgd2Ugd2lsbCBzZWUgYSBjb3VwbGUgb2YgZGlmZmVyZW50IHdheXMgdG8gY291bnQgaG93IG1hbnkgIGNvbW11bml0eSBhcmVhcyBzYXcgcG9zaXRpdmUgcG9wdWxhdGlvbiBncm93dGguCgojIyMgUHJhY3RpY2UKCkNvbnRpbnVlIHdpdGggdGhlIE5ZQyByZW50IGRhdGEgc2V0IGp1c3QgY3JlYXRlZCBhbmQgZWxpbWluYXRlIHRoZSBvYnNlcnZhdGlvbnMgd2l0aCB6ZXJvIGZvciB0aGUgcmVudCBpbiBhbnkgb2YgdGhlIHllYXJzLgoKIyMgU3VtbWFyaWVzCgojIyMgU3VtbWFyeSBvbiBpbmRpdmlkdWFsIHZhcmlhYmxlcyB1c2luZyBgc3VtbWFyeWAKCldlIGhhdmUgYWxyZWFkeSBzZWVuIHRoZSB1c2Ugb2YgYHN1bW1hcnlgIGVhcmxpZXIuIFRoaXMgd29ya3MgdGhlIHNhbWUgd2F5IG9uCmEgdGliYmxlIGFzIG9uIGEgZGF0YSBmcmFtZS4gRm9yIGV4YW1wbGUsIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZm9yIG91ciBkYXRhCnNldCBmb3IgdGhlIGNvbW11bml0eSBhcmVhcyBhcmUgY29tcHV0ZWQgdXNpbmcgYHN1bW1hcnlgLgoKYGBge3J9CnN1bW1hcnkoZGYpCmBgYAoKIyMjIERlc2NyaXB0aXZlIHN0YXRpc3RpY3MgdXNpbmcgYHN1bW1hcml6ZWAKClRoZSBgc3VtbWFyaXplYCBjb21tYW5kIGNvbXB1dGVzIHNwZWNpZmljIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgYW5kIGFzc2lnbnMgdGhlbSB0byAgYSBuZXcgdmFyaWFibGUuIEFsbCB0aGUgc3RhdGlzdGljcyBhcmUgdGhlbiBjb21iaW5lZCBpbiBhIGRhdGEgZnJhbWUgd2l0aCBhIHNpbmdsZSBvYnNlcnZhdGlvbi4gRm9yIGV4YW1wbGUsIGlmIHdlIHdhbnRlZCB0aGUgbWVhbiBvZiB0aGUgcG9wdWxhdGlvbiBpbiBlYWNoIG9mIHRoZSB0d28geWVhcnMsIHdlIGNyZWF0ZSB0d28gbmV3IHZhcmlhYmxlcywgc2F5LCBtMDAgYW5kIG0xMCBhbmQgc2V0IHRoZW0gZXF1YWwgdG8gcmVzcGVjdGl2ZWx5IGBtZWFuKFBPUDIwMDApYCBhbmQgYG1lYW4oUE9QMjAxMClgLiBUaGUgYHN1bW1hcml6ZWAgY29tbWFuZCB0YWtlcyB0aGUgbmFtZSBvZiB0aGUgZGF0YSBmcmFtZSBhbmQgdGhlIGV4cHJlc3Npb25zIGVudGVyZWQuIEZvciBleGFtcGxlLCBmb3IgdGhlIG1lYW4uCgpgYGB7cn0Kc3VtbWFyaXplKGRmLG0wMCA9IG1lYW4oUE9QMjAwMCksbTEwID0gbWVhbihQT1AyMDEwKSkKYGBgCgpUaGUgIHZhbHVlcyBhcmUgdGhlIHNhbWUgYXMgd2hhdCB3ZSBvYnRhaW5lZCBmcm9tIHRoZSBgc3VtbWFyeWAuIEluIGFkZGl0aW9uICB0byB0aGUgIGBtZWFuYCwgc3VtbWFyaXplIGNhbiBhbHNvIHlpZWxkIHRoZSBgbWVkaWFuYCwgc3RhbmRhcmQgZGV2aWF0aW9uIChgc2RgKSwgaW50ZXItcXVhcnRpbGUgcmFuZ2UgKGBJUVJgKSwgbWluaW11bSAoYG1pbmApLAptYXhpbXVtIChgbWF4YCksIHF1YW50aWxlIChgcXVhbnRpbGUodmFyaWFibGUsIHBlcmNlbnRpbGUpYCksIGNvdW50cyBvZiBsb2dpY2FsIHZhbHVlcyAoYHN1bWApLgoKRm9yIGV4YW1wbGUsIHRvIGZpbmQgb3V0IGhvdyBtYW55IGNvbW11bml0eSBhcmVhcyBoYXZlIGEgcG9zaXRpdmUgcG9wdWxhdGlvbiBncm93dGgsIHdlIGFwcGx5IGBzdW1gIHRvIApgcG9waW5jYCAocmVtZW1iZXIgdGhhdCBUUlVFIGlzIHRoZSBzYW1lIGFzIDEgYW5kIEZBTFNFIGlzIDAsIHNvIHRoZSBzdW0gb2YgdGhlIG9ic2VydmF0aW9ucyBvbiBhIGxvZ2ljYWwgdmFyaWFibGUgZXF1YWxzIHRoZSBudW1iZXIgb2YgVFJVRSkuCgpgYGB7cn0Kc3VtbWFyaXplKGRmLGMgPSBzdW0ocG9waW5jKSkKYGBgCgojIyMgU3VtbWFyaWVzIGJ5IHN1YnNldCB1c2luZyBgZ3JvdXBfYnlgCgpUaGUgbWFpbiBwb3dlciBvZiBgc3VtbWFyaXplYCBpcyBpdHMgdXNlIGluIGNvbWJpbmF0aW9uIHdpdGggYGdyb3VwX2J5YCwgd2hpY2ggY29tcHV0ZXMKdGhlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgZm9yIHN1YnNldHMgb2YgdGhlIGRhdGEuIEZvciBleGFtcGxlLCBzYXkgd2Ugd2FudGVkIHRvIGNvbXB1dGUgdGhlCm1lYW4gcG9wdWxhdGlvbiBzZXBhcmF0ZWx5IGZvciB0aGUgY29tbXVuaXR5IGFyZWFzIHRoYXQgc2F3IGdyb3d0aCBhbmQgdGhvc2UgdGhhdCBzYXcgZGVjbGluZS4KCldlIGNvdWxkIG9mIGNvdXJzZSB1c2UgYGZpbHRlcmAgdG8gY3JlYXRlIHR3byBzZXBhcmF0ZSBkYXRhIGZyYW1lcyBhbmQgcmVwZWF0IHRoZSBjYWxjdWxhdGlvbiBmb3IgZWFjaCBvZiB0aGVtLiBJbnN0ZWFkLCB3ZSB1c2UgYGdyb3VwX2J5YCB0byBtYWtlIHRoZSBncm91cGluZyBpbnRlcm5hbCB0byB0aGUgZGF0YSBmcmFtZSBhbmQgdGhlbiBhcHBseSB0aGUKYHN1bW1hcml6ZWAuCgpGb3IgZXhhbXBsZSwgZmlyc3Qgd2UgY3JlYXRlIHRoZSBkYXRhIGZyYW1lICoqZGZncm91cCoqIHVzaW5nIGBncm91cF9ieWAgd2l0aCAqKnBvcGluYyoqLiBXZSBhbHNvCnByaW50IHRoZSByZXN1bHQgdG8gY2hlY2sgaXQuCgpgYGB7cn0KZGZncm91cCA8LSBncm91cF9ieShkZixwb3BpbmMpCmRmZ3JvdXAKYGBgCgpXZSBjYW4ndCByZWFsbHkgc2VlIGFueSBkaWZmZXJlbmNlLiBCdXQgbm93LCBpZiB3ZSB1c2UgYHN1bW1hcml6ZWAgZm9yIHRoZSB0d28gbWVhbnMgb24gdGhlIG5ldyBkYXRhIGZyYW1lLCB0aGUgcmVzdWx0cyBnaXZlIHVzIHR3byByb3dzLCBvbmUgZm9yICoqcG9waW5jKiogRkFMU0UgYW5kIG9uZSBmb3IgVFJVRS4KCmBgYHtyfQpzdW1tYXJpemUoZGZncm91cCxtMDAgPSBtZWFuKFBPUDIwMDApLG0xMCA9IG1lYW4oUE9QMjAxMCkpCmBgYAoKQSB2ZXJ5IHVzZWZ1bCBzdW1tYXJ5IHN0YXRpc3RpYyBpcyBgbigpYCB3aGljaCBnaXZlcyB0aGUgY291bnQgb2Ygb2JzZXJ2YXRpb25zIGJ5IGdyb3VwLCAgY29udGFpbmVkCmluIHRoZSB2YXJpYWJsZSAqKmNvdW50KiouCgpgYGB7cn0Kc3VtbWFyaXplKGRmZ3JvdXAsY291bnQ9bigpKQpgYGAKCgoKIyMjIFByYWN0aWNlCgpDb21wdXRlIHRoZSBtZWRpYW4gb2YgdGhlIG1lZGlhbiByZW50IGluIDIwMDggZm9yIHRoZSBOWUMgc3ViLWJvcm91Z2hzIGdyb3VwZWQgYnkgd2hldGhlcgp0aGV5IGhhZCBhYm92ZSBvciBiZWxvdyBtZWRpYW4gcmVudCBpbiAyMDAyLgoKIyMgQ2hhaW5pbmcgY29tbWFuZHMKClRoZSByZWFsIHBvd2VyIG9mIHRoZSB2YXJpb3VzIG1hbmlwdWxhdGlvbnMgb2YgZGF0YSBmcmFtZXMgdXNpbmcgdGhlIHRpZHl2ZXJzZSBpcyBieSBjaGFpbmluZwpjb21tYW5kcyB1c2luZyB0aGUgc28tY2FsbGVkIHBpcGUuIEluIHRoZSAgbWFuaXB1bGF0aW9ucyBhYm92ZSB3ZSBoYWQgdG8gY3JlYXRlIGEgCm5ldyBkYXRhIGZyYW1lIGVhY2ggdGltZSwgYnV0IHRob3NlIHdlcmUgbm90IG5lY2Vzc2FyaWx5IG9mIGludGVyZXN0IGluIGFuZCBvZiB0aGVtc2VsdmVzLgpUaGUgKipwaXBlKiogY29tbWFuZCwgc3ltYm9saXplZCBhcyBgJT4lYCBtb3ZlcyB0aGUgb3V0cHV0IG9mIG9uZSBvcGVyYXRpb24gaW50byB0aGUgaW5wdXQgb2YgdGhlCm5leHQgb3BlcmF0aW9uLgoKRm9yIGV4YW1wbGUsIHNheSB3ZSB3YW50ZWQgdG8gY29tcHV0ZSB0aGUgbG9naWNhbCB2YXJpYWJsZSAqKnBvcGluYyoqIGFuZCB0aGVuIGltbWVkaWF0ZWx5IHVzZSB0aGF0CnRvIGdyb3VwIHRoZSBvYnNlcnZhdGlvbnMgYW5kIGNvbXB1dGUgdGhlIG1lYW4gYnkgZ3JvdXAuIFNvLCBiYXNpY2FsbHksIHRoZSBzYW1lIGFzIHdoYXQgd2UgZGlkCmJlZm9yZS4gTm93IHdlIG9yZ2FuaXplIHRoZXNlIG9wZXJhdGlvbnMgYnkgc3RhcnRpbmcgd2l0aCBvdXIgZGF0YSBmcmFtZSwgKnBpcGluZyogaXQgaW50byB0aGUgYG11dGF0ZWAgb3BlcmF0aW9uIHRvIGNvbXB1dGUgdGhlIG5ldyB2YXJpYWJsZSwgdGhlbiBwaXBlIHRoYXQgaW50byAgdGhlIGBncm91cF9ieWAgb3BlcmF0aW9uIGFuZCBmaW5hbGx5CmludG8gdGhlIGBzdW1tYXJpemVgLgoKYGBge3J9CmRmICU+JSBtdXRhdGUocG9wY2ggPSAoUE9QMjAxMCAtIFBPUDIwMDApID4gMCkgJT4lCiAgZ3JvdXBfYnkocG9wY2gpICU+JSBzdW1tYXJpemUobTAwID0gbWVhbihQT1AyMDAwKSxtMTAgPSBtZWFuKFBPUDIwMTApLCBjb3VudCA9IG4oKSkKYGBgCgpJZiB3ZSB3YW50IHRvIGtlZXAgdGhlIHJlc3VsdHMgYXMgYSBuZXcgZGF0YSBmcmFtZSwgd2UgYXNzaWduIGl0IHRvIGEgbmV3IG9iamVjdC4KCmBgYHtyfQpkZmNoIDwtIGRmICU+JSBtdXRhdGUocG9wY2ggPSAoUE9QMjAxMCAtIFBPUDIwMDApID4gMCkgJT4lCiAgZ3JvdXBfYnkocG9wY2gpICU+JSBzdW1tYXJpemUobTAwID0gbWVhbihQT1AyMDAwKSxtMTAgPSBtZWFuKFBPUDIwMTApLGNvdW50PW4oKSkKZGZjaApgYGAKCiMjIyBQcmFjdGljZQoKQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgd2l0aCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgcmVudCBpbiAyMDA1IGFuZCAyMDA4IGdyb3VwZWQgYnkgd2hldGhlciB0aGUgc3ViLWJvcm91Z2hzIHdlcmUgYWJvdmUgb3IgYmVsb3cgdGhlIG1lZGlhbiBpbiAyMDAyLgoK