1 Reading data

One of the golden rules to follow when reading data is to make your life easy by not having to memorize or discover through frantic trial-and-error runs where a particular file is located. So how do we make our life easy?

  1. Put all code in a sub-folder called code and all data in a sub-folder called data
  2. When reading or saving data files, make sure to use the {here} library. In the examples that follow you will see how {here} is used to (a) first specify where the data file is located, and (b) then to list the filename.

Both (1) and (2) have been done for us. Look inside the data folder and you will see a number of data files. Look inside the code folder and you will see files called Module01.Rmd, Module02.Rmd, and so on. These *.Rmd files are in the RMarkdown format, and each file corresponds to a module. Now on to reading data …

Data come in several formats but I will walk you through the formats you are most likely to encounter – MS Excel, CSV, TXT, fixed-width, and then in any one of these commercial software formats: SAS, Stata, or SPSS.

1.1 CSV data files

We start by reading a simple comma-separated variable format file and then a tab-delimited variable format file. A CSV file has each column separated by a comma while a tab-delimited file has every column separated by a tab, versus \t

Note … * here("data") specifies where the files are located but this is an incomplete command because it does not specify the name of the file to be read. Instead, here("data", "ImportDataCSV.csv") is the complete command. * The sep = "," switch says the individual variables are separated by a comma * header = TRUE switch indicates that the first row in the file that is being read in has the column names
* The tab-delimited file needs sep = "\t" because the columns are separated by a tab

If both files were read then your RStudio Environment should show objects called df.csv and df.tab. If you don’t see these, check to see if both files are indeed in the data folder or not.

1.2 MS Excel files

Microsoft Excel files can be read via the readxl package, and you see two versions below – one for the older *.xls format and the other for the newer *.xlsx format.

As a rule, I would recommend against storing data in Excel formats since Excel tends to do some funny things. If you must store and share data, try to use the CSV format.

1.3 SPSS, Stata, SAS files

Some agencies and other sources tend to use SAS, Stata, or SPSS for storing data and for carrying out various data analyses. This is a legacy issue that is changing but a little too slowly for most of us who do not use these commercial software packages as the centerpiece of our work. But, even if you do not use these packages, you should know how to read in data created in their native formats. As it turns out, SPSS, Stata, SAS files can be read via the haven package, and with relative ease.

Again, if you look at the Environment you should see these three data files as well.

1.4 Fixed-width files

It is also common to encounter fixed-width files where the raw data are stored without any gaps between successive columns. This is also a legacy format from the early days of computers and punch cards, and one of the most efficient ways of storing large amounts of data. These files come with documentation that will give you the necessary details about where each column starts and ends, etc. See here for some examples of layouts from the Census Bureau.

Notice we need widths = c(4,9,2,4) to indicate how many slots each column takes and then col.names = c("Name", "Month", "Day", "Year") to label the columns since the data file does not have variable names. if you mess up with widths = you end up with garbage because R does not know where any column starts or ends so be careful.

2 Reading Files from the Web

One of the benefits of software like R is its ability to read data files over the web without requiring you to manually download the file and save a physical copy to be read in. Specifically, it is possible to list the full web-path for a file and read it in. This ability is invaluable when the data tend to be periodically updated by the source (for example, by the Census Bureau, Bureau of Labor, Bureau of Economic Analysis, etc.). Here are a few examples.

##                setting effort change
## Bolivia             46      0      1
## Brazil              74      0     10
## Chile               89     16     29
## Colombia            77     16     25
## CostaRica           84     21     29
## Cuba                89     15     40
## DominicanRep        68     14     21
## Ecuador             70      6      0
## ElSalvador          60     13     13
## Guatemala           55      9      4
## Haiti               35      3      0
## Honduras            51      7      7
## Jamaica             87     23     21
## Mexico              83      4      9
## Nicaragua           68      0      7
## Panama              84     19     22
## Paraguay            74      3      6
## Peru                73      0      2
## TrinidadTobago      84     15     29
## Venezuela           91      7     11

If you look at fpe you will notice that there are three columns but the rows each have a unique name. These are row names that are remembered by R but will not show up with a column name.

3 Reading compressed files

Large files may sit in compressed archives (.zip, .gz, etc.) on the web and R has a neat way of allowing you to download the file, unarchive it, and then read it in. Why is this useful? Because if these files tend to be periodically updated, R’s ability lets you use the same piece of R code to download/unzip/read in the updated file. The tedious way would be to manually download the file, unzip it, place the file in the appropriate data folder, and then read it in.

4 Saving R data files

You can save your data in a format that R will recognize, giving it the RData or rdata extension

Check the data directory to confirm that both files are present

5 Minimal example of data processing

Working with the hsb2 data: 200 students from the High School and Beyond study. The variables in this file are:

  • female = (0/1)
  • race = (1=hispanic 2=asian 3=african-amer 4=white)
  • ses = socioeconomic status (1=low 2=middle 3=high)
  • schtyp = type of school (1=public 2=private)
  • prog = type of program (1=general 2=academic 3=vocational)
  • read = standardized reading score
  • write = standardized writing score
  • math = standardized math score
  • science = standardized science score
  • socst = standardized social studies score

What are the variable names (i.e., the column headings) in this file? The names() command will tell you that.

##  [1] "id"      "female"  "race"    "ses"     "schtyp"  "prog"    "read"   
##  [8] "write"   "math"    "science" "socst"

Similarly, the str() command will show you the details of each variable

## 'data.frame':    200 obs. of  11 variables:
##  $ id     : int  70 121 86 141 172 113 50 11 84 48 ...
##  $ female : int  0 1 0 0 0 0 0 0 0 0 ...
##  $ race   : int  4 4 4 4 4 4 3 1 4 3 ...
##  $ ses    : int  1 2 3 3 2 2 2 2 2 2 ...
##  $ schtyp : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ prog   : int  1 3 1 3 2 2 1 2 1 2 ...
##  $ read   : int  57 68 44 63 47 44 50 34 63 57 ...
##  $ write  : int  52 59 33 44 52 52 59 46 57 55 ...
##  $ math   : int  41 53 54 47 57 51 42 45 54 52 ...
##  $ science: int  47 63 58 53 53 63 53 39 58 50 ...
##  $ socst  : int  57 61 31 56 61 61 61 36 51 51 ...

and the summary() command will give you some summary information about each variable.

##        id             female           race           ses            schtyp    
##  Min.   :  1.00   Min.   :0.000   Min.   :1.00   Min.   :1.000   Min.   :1.00  
##  1st Qu.: 50.75   1st Qu.:0.000   1st Qu.:3.00   1st Qu.:2.000   1st Qu.:1.00  
##  Median :100.50   Median :1.000   Median :4.00   Median :2.000   Median :1.00  
##  Mean   :100.50   Mean   :0.545   Mean   :3.43   Mean   :2.055   Mean   :1.16  
##  3rd Qu.:150.25   3rd Qu.:1.000   3rd Qu.:4.00   3rd Qu.:3.000   3rd Qu.:1.00  
##  Max.   :200.00   Max.   :1.000   Max.   :4.00   Max.   :3.000   Max.   :2.00  
##       prog            read           write            math      
##  Min.   :1.000   Min.   :28.00   Min.   :31.00   Min.   :33.00  
##  1st Qu.:2.000   1st Qu.:44.00   1st Qu.:45.75   1st Qu.:45.00  
##  Median :2.000   Median :50.00   Median :54.00   Median :52.00  
##  Mean   :2.025   Mean   :52.23   Mean   :52.77   Mean   :52.65  
##  3rd Qu.:2.250   3rd Qu.:60.00   3rd Qu.:60.00   3rd Qu.:59.00  
##  Max.   :3.000   Max.   :76.00   Max.   :67.00   Max.   :75.00  
##     science          socst      
##  Min.   :26.00   Min.   :26.00  
##  1st Qu.:44.00   1st Qu.:46.00  
##  Median :53.00   Median :52.00  
##  Mean   :51.85   Mean   :52.41  
##  3rd Qu.:58.00   3rd Qu.:61.00  
##  Max.   :74.00   Max.   :71.00

There are no value labels for the various qualitative/categorical variables (female, race, ses, schtyp, and prog) but these are easily created as shown below.

Let us now take a quick look at the contents of each column in hsb2

##        id             female           race           ses            schtyp    
##  Min.   :  1.00   Min.   :0.000   Min.   :1.00   Min.   :1.000   Min.   :1.00  
##  1st Qu.: 50.75   1st Qu.:0.000   1st Qu.:3.00   1st Qu.:2.000   1st Qu.:1.00  
##  Median :100.50   Median :1.000   Median :4.00   Median :2.000   Median :1.00  
##  Mean   :100.50   Mean   :0.545   Mean   :3.43   Mean   :2.055   Mean   :1.16  
##  3rd Qu.:150.25   3rd Qu.:1.000   3rd Qu.:4.00   3rd Qu.:3.000   3rd Qu.:1.00  
##  Max.   :200.00   Max.   :1.000   Max.   :4.00   Max.   :3.000   Max.   :2.00  
##       prog            read           write            math      
##  Min.   :1.000   Min.   :28.00   Min.   :31.00   Min.   :33.00  
##  1st Qu.:2.000   1st Qu.:44.00   1st Qu.:45.75   1st Qu.:45.00  
##  Median :2.000   Median :50.00   Median :54.00   Median :52.00  
##  Mean   :2.025   Mean   :52.23   Mean   :52.77   Mean   :52.65  
##  3rd Qu.:2.250   3rd Qu.:60.00   3rd Qu.:60.00   3rd Qu.:59.00  
##  Max.   :3.000   Max.   :76.00   Max.   :67.00   Max.   :75.00  
##     science          socst         female.f                race.f   
##  Min.   :26.00   Min.   :26.00   Male  : 91   Hispanic        : 24  
##  1st Qu.:44.00   1st Qu.:46.00   Female:109   Asian           : 11  
##  Median :53.00   Median :52.00                African American: 20  
##  Mean   :51.85   Mean   :52.41                White           :145  
##  3rd Qu.:58.00   3rd Qu.:61.00                                      
##  Max.   :74.00   Max.   :71.00                                      
##     ses.f       schtyp.f          prog.f   
##  Low   :47   Public :168   General   : 45  
##  Middle:95   Private: 32   Academic  :105  
##  High  :58                 Vocational: 50  
##                                            
##                                            
## 

Note: We did not overwrite the original columns, and this is important because in case we mess up with this modification, we still have the original columns to work with. Also pay attention to how the modifications were made, for example, …

  1. take column female that has values 0 and 1
  2. a value of 0 should be labeled Male and a value of 1 should be labeled Female
  3. save the new column as female.f

Further, there are are four values for race, 3 for ses, 2 for schtyp, and 3 for prog, so the mapping has to reflect this.

This is just a quick run through with creating value labels; we will cover this in greater detail in a later module.

save your work!!

Having created new columns we can now save the resulting data for later use.

Let us test if this R Markdown file will Knit to html.

6 Data in packages

Almost all R packages come bundled with data-sets, too many of them to walk you through but

To load data from a package, if you know the data-set’s name, run

## [1] "parent" "child"

or you can run

## [1] "family"          "father"          "mother"          "midparentHeight"
## [5] "children"        "childNum"        "gender"          "childHeight"

7 More on saving data and workspaces

You can save your data via

  • save(dataname, file = here("filepath", "filename.RData"))

or

  • save(dataname, file = here("filepath", "filename.rdata"))

Check the data folder and you should see mtcars.RData.

Now, say we wanted to load a saved R data file. How might we do that? This is done below but as you run the code, note that the rm(list = ls()) command is basically wiping the Global Environment clean by removing everything in it. This is the same as clicking the ‘broom" icon beside ’Import Dataset v’

You can also save multiple data files, individual objects, and even everything you have done in a work session. These are covered in the textbook.

If you are not in a project and they try to close RStudio after some code has been run, you will be prompted to save (or not) the workspace and you should say “no” by default unless you want to save the workspace.

8 RStudio webinars

The fantastic team at RStudio runs free webinar that are often very helpful so be sure to signup with your email. Here are some video recordings of webinars that are relevant to what we have covered so far.


9 Exercises for practice

These are some exercises you can use to practice and build your R skills. They are not for grade.

9.1 Ex. 1: Creating and knitting a new RMarkdown file

Open a fresh session by launching RStudio and then running File -> Open Project...

Give it a title, your name as the author, and then save it with in code with the following name: m1ex1.Rmd

Add this level 1 heading The Starwars Data and then insert your first code chunk as shown below but change eval = FALSE to eval = TRUE. The R code will not be executed if eval = FALSE.

Add this level 2 heading Character Heights and Weights and then your second code chunk

Now knit this file to html

9.2 Ex. 2: Lorem Ipsum paragraphs and graphs

Go to this website and generate five Lorem Ipsum placeholder text paragraphs

  • para 1: must have level 1 heading
  • para 2: must have level 2 heading
  • para 3: must have level 3 heading
  • para 4: must have level 4 heading
  • para 5: must have level 5 heading

Using the starwars data, create five code chunks, one after each paragraph, making sure to set eval = TRUE

  • Each code chunk will have the same R code (see below)

Now knit this file to html

9.3 Ex. 3: Reading in three data files

Create a new RMarkdown file that is blank after the initial setup code chunk

Insert a code chunk that reads in both these files found on the web

  • http://www.stata.com/data/jwooldridge/eacsap/mroz.dta
  • http://calcnet.mth.cmich.edu/org/spss/V16_materials/DataSets_v16/airline_passengers.sav In a follow-up code chunk, run the summary() command on each data-set

In a separate code chunk, read in this dataset after you download it and save the unzipped file in your data folder.

  • The variable gender has the following codes: Zero = unknown; 1 = male; 2 = female
  • Use this coding scheme to convert gender into a factor with these value labels

In a follow-up chunk run the following commands on this data-set

  • names()
  • str()
  • summary()

In a final chunk, run the commands necessary to save each of the three data-sets as separate RData files. Make sure you save them in your data folder.

Now knit the complete Rmd file to html

9.3.1 Ex. 4: Welcome to Kaggle & Mass Shootings

Go to this page on Kaggle and read the description of the data-set on mass shootings in the United States that occurred during the 1966-2017 period. once you have read the overview of the data, click the “Data” tab and download the file called Mass Shootings Dataset.csv. Be careful; there are several versions so the one you want is the very last one and not any that have a version number attached, such as “Mass Shootings Dataset Ver 2.csv” for example.

Now read this file into R, perhaps naming it shootings and run the summary() command on it. Note the number of observations and the number of variables in the data-set.

9.3.2 Ex. 5: Animal Shelters

Go to this page on Kaggle and download the file called aac_shelter_outcomes.zip, unzip it, and AFTER reading the data overview, read in the file and generate a list of variable names with an appropriate command.

LS0tCnRpdGxlOiAiTVBBIDU4MzAgLSBNb2R1bGUgMDEiCnN1YnRpdGxlOiAiU3ByaW5nIDIwMjAiCmF1dGhvcjogIlByb2Zlc3NvciBSdWhpbCIKZGF0ZTogIlVwZGF0ZWQgb24gYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogZmxhdGx5CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmVkaXRvcl9vcHRpb25zOiAKICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBjYWNoZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIiwgZmlnLndpZHRoID0gOSwgZmlnLmhlaWdodCA9IDksCiAgICAgICAgICAgICAgICAgICAgICBvdXQud2lkdGggPSAiMTAwJSIsIGZpZy5yZXRpbmEgPSAxLCBoaWdobGlnaHQgPSBUUlVFKSAKCmlmICghcmVxdWlyZShyZW1vdGVzKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcygicmVtb3RlcyIpCiAgICBsaWJyYXJ5KHJlbW90ZXMpCiAgfQoKbGlicmFyeShoZXJlKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIFJlYWRpbmcgZGF0YQpPbmUgb2YgdGhlIGdvbGRlbiBydWxlcyB0byBmb2xsb3cgd2hlbiByZWFkaW5nIGRhdGEgaXMgdG8gbWFrZSB5b3VyIGxpZmUgZWFzeSBieSBub3QgaGF2aW5nIHRvIG1lbW9yaXplIG9yIGRpc2NvdmVyIHRocm91Z2ggZnJhbnRpYyB0cmlhbC1hbmQtZXJyb3IgcnVucyB3aGVyZSBhIHBhcnRpY3VsYXIgZmlsZSBpcyBsb2NhdGVkLiBTbyBob3cgZG8gd2UgbWFrZSBvdXIgbGlmZSBlYXN5PyAKCigxKSBQdXQgYWxsIGNvZGUgaW4gYSBzdWItZm9sZGVyIGNhbGxlZCBgY29kZWAgYW5kIGFsbCBkYXRhIGluIGEgc3ViLWZvbGRlciBjYWxsZWQgYGRhdGFgIAooMikgV2hlbiByZWFkaW5nIG9yIHNhdmluZyBkYXRhIGZpbGVzLCBtYWtlIHN1cmUgdG8gdXNlIHRoZSB7aGVyZX0gbGlicmFyeS4gSW4gdGhlIGV4YW1wbGVzIHRoYXQgZm9sbG93IHlvdSB3aWxsIHNlZSBob3cge2hlcmV9IGlzIHVzZWQgdG8gKGEpIGZpcnN0IHNwZWNpZnkgX193aGVyZV9fIHRoZSBkYXRhIGZpbGUgaXMgbG9jYXRlZCwgYW5kIChiKSB0aGVuIHRvIGxpc3QgdGhlIF9fZmlsZW5hbWVfXy4gICAKCkJvdGggKDEpIGFuZCAoMikgaGF2ZSBiZWVuIGRvbmUgZm9yIHVzLiBMb29rIGluc2lkZSB0aGUgYGRhdGFgIGZvbGRlciBhbmQgeW91IHdpbGwgc2VlIGEgbnVtYmVyIG9mIGRhdGEgZmlsZXMuIExvb2sgaW5zaWRlIHRoZSBgY29kZWAgZm9sZGVyIGFuZCB5b3Ugd2lsbCBzZWUgZmlsZXMgY2FsbGVkIE1vZHVsZTAxLlJtZCwgTW9kdWxlMDIuUm1kLCBhbmQgc28gb24uIFRoZXNlICouUm1kIGZpbGVzIGFyZSBpbiB0aGUgUk1hcmtkb3duIGZvcm1hdCwgYW5kIGVhY2ggZmlsZSBjb3JyZXNwb25kcyB0byBhIG1vZHVsZS4gTm93IG9uIHRvIHJlYWRpbmcgZGF0YSAuLi4gIAoKRGF0YSBjb21lIGluIHNldmVyYWwgZm9ybWF0cyBidXQgSSB3aWxsIHdhbGsgeW91IHRocm91Z2ggdGhlIGZvcm1hdHMgeW91IGFyZSBtb3N0IGxpa2VseSB0byBlbmNvdW50ZXIgLS0gTVMgRXhjZWwsIENTViwgVFhULCBmaXhlZC13aWR0aCwgYW5kIHRoZW4gaW4gYW55IG9uZSBvZiB0aGVzZSBjb21tZXJjaWFsIHNvZnR3YXJlIGZvcm1hdHM6IFNBUywgU3RhdGEsIG9yIFNQU1MuIAoKIyMgQ1NWIGRhdGEgZmlsZXMgCldlIHN0YXJ0IGJ5IHJlYWRpbmcgYSBzaW1wbGUgYGNvbW1hLXNlcGFyYXRlZCB2YXJpYWJsZWAgZm9ybWF0IGZpbGUgYW5kIHRoZW4gYSBgdGFiLWRlbGltaXRlZCB2YXJpYWJsZWAgZm9ybWF0IGZpbGUuIEEgQ1NWIGZpbGUgaGFzIGVhY2ggY29sdW1uIHNlcGFyYXRlZCBieSBhIGBjb21tYWAgd2hpbGUgYSB0YWItZGVsaW1pdGVkIGZpbGUgaGFzIGV2ZXJ5IGNvbHVtbiBzZXBhcmF0ZWQgYnkgYSBgdGFiYCAtLSBgLGAgdmVyc3VzIGBcdGAgCgpgYGB7ciBjc3Z0YWIsIGV2YWw9VFJVRX0KcmVhZC5jc3YoCiAgaGVyZSgiZGF0YSIsICJJbXBvcnREYXRhQ1NWLmNzdiIpLAogIHNlcCA9ICIsIiwKICBoZWFkZXIgPSBUUlVFCiAgKSAtPiBkZi5jc3YgCgpyZWFkLmNzdigKICBoZXJlKCJkYXRhIiwgIkltcG9ydERhdGFUQUIudHh0IiksCiAgc2VwID0gIlx0IiwKICBoZWFkZXIgPSBUUlVFCiAgKSAtPiBkZi50YWIKYGBgCgpOb3RlIC4uLiAKKiBgaGVyZSgiZGF0YSIpYCBzcGVjaWZpZXMgd2hlcmUgdGhlIGZpbGVzIGFyZSBsb2NhdGVkIGJ1dCB0aGlzIGlzIGFuIGluY29tcGxldGUgY29tbWFuZCBiZWNhdXNlIGl0IGRvZXMgbm90IHNwZWNpZnkgdGhlIG5hbWUgb2YgdGhlIGZpbGUgdG8gYmUgcmVhZC4gSW5zdGVhZCwgYGhlcmUoImRhdGEiLCAiSW1wb3J0RGF0YUNTVi5jc3YiKWAgaXMgdGhlIGNvbXBsZXRlIGNvbW1hbmQuIAoqIFRoZSBgc2VwID0gIiwiYCBzd2l0Y2ggc2F5cyB0aGUgaW5kaXZpZHVhbCB2YXJpYWJsZXMgYXJlIHNlcGFyYXRlZCBieSBhIGNvbW1hIAoqIGBoZWFkZXIgPSBUUlVFYCBzd2l0Y2ggaW5kaWNhdGVzIHRoYXQgdGhlIGZpcnN0IHJvdyBpbiB0aGUgZmlsZSB0aGF0IGlzIGJlaW5nIHJlYWQgaW4gaGFzIHRoZSBjb2x1bW4gbmFtZXMgIAoqIFRoZSB0YWItZGVsaW1pdGVkIGZpbGUgbmVlZHMgYHNlcCA9ICJcdCJgIGJlY2F1c2UgdGhlIGNvbHVtbnMgYXJlIHNlcGFyYXRlZCBieSBhIHRhYiAKCklmIGJvdGggZmlsZXMgd2VyZSByZWFkIHRoZW4geW91ciBSU3R1ZGlvIGBFbnZpcm9ubWVudGAgc2hvdWxkIHNob3cgb2JqZWN0cyBjYWxsZWQgYGRmLmNzdmAgYW5kIGBkZi50YWJgLiBJZiB5b3UgZG9uJ3Qgc2VlIHRoZXNlLCBjaGVjayB0byBzZWUgaWYgYm90aCBmaWxlcyBhcmUgaW5kZWVkIGluIHRoZSBgZGF0YWAgZm9sZGVyIG9yIG5vdC4gICAKCgojIyBNUyBFeGNlbCBmaWxlcwoKTWljcm9zb2Z0ICoqRXhjZWwqKiBmaWxlcyBjYW4gYmUgcmVhZCB2aWEgdGhlIGByZWFkeGxgIHBhY2thZ2UsIGFuZCB5b3Ugc2VlIHR3byB2ZXJzaW9ucyBiZWxvdyAtLSBvbmUgZm9yIHRoZSBvbGRlciBgKi54bHNgIGZvcm1hdCBhbmQgdGhlIG90aGVyIGZvciB0aGUgbmV3ZXIgYCoueGxzeGAgZm9ybWF0LiAKCj4gQXMgYSBydWxlLCBJIHdvdWxkIHJlY29tbWVuZCBhZ2FpbnN0IHN0b3JpbmcgZGF0YSBpbiBFeGNlbCBmb3JtYXRzIHNpbmNlIEV4Y2VsIHRlbmRzIHRvIGRvIHNvbWUgZnVubnkgdGhpbmdzLiBJZiB5b3UgbXVzdCBzdG9yZSBhbmQgc2hhcmUgZGF0YSwgdHJ5IHRvIHVzZSB0aGUgQ1NWIGZvcm1hdC4gICAgCgpgYGB7ciBleGNlbCwgZXZhbD1UUlVFfQpsaWJyYXJ5KHJlYWR4bCkKcmVhZF9leGNlbCgKICBoZXJlKCJkYXRhIiwgIkltcG9ydERhdGFYTFMueGxzIikKICApIC0+IGRmLnhscyAKCnJlYWRfZXhjZWwoCiAgaGVyZSgiZGF0YSIsICJJbXBvcnREYXRhWExTWC54bHN4IikKICApIC0+IGRmLnhsc3gKYGBgCgojIyBTUFNTLCBTdGF0YSwgU0FTIGZpbGVzClNvbWUgYWdlbmNpZXMgYW5kIG90aGVyIHNvdXJjZXMgdGVuZCB0byB1c2UgU0FTLCBTdGF0YSwgb3IgU1BTUyBmb3Igc3RvcmluZyBkYXRhIGFuZCBmb3IgY2Fycnlpbmcgb3V0IHZhcmlvdXMgZGF0YSBhbmFseXNlcy4gVGhpcyBpcyBhIGxlZ2FjeSBpc3N1ZSB0aGF0IGlzIGNoYW5naW5nIGJ1dCBhIGxpdHRsZSB0b28gc2xvd2x5IGZvciBtb3N0IG9mIHVzIHdobyBkbyBub3QgdXNlIHRoZXNlIGNvbW1lcmNpYWwgc29mdHdhcmUgcGFja2FnZXMgYXMgdGhlIGNlbnRlcnBpZWNlIG9mIG91ciB3b3JrLiBCdXQsIGV2ZW4gaWYgeW91IGRvIG5vdCB1c2UgdGhlc2UgcGFja2FnZXMsIHlvdSBzaG91bGQga25vdyBob3cgdG8gcmVhZCBpbiBkYXRhIGNyZWF0ZWQgaW4gdGhlaXIgbmF0aXZlIGZvcm1hdHMuIEFzIGl0IHR1cm5zIG91dCwgKipTUFNTLCBTdGF0YSwgU0FTKiogZmlsZXMgY2FuIGJlIHJlYWQgdmlhIHRoZSBgaGF2ZW5gIHBhY2thZ2UsIGFuZCB3aXRoIHJlbGF0aXZlIGVhc2UuIAoKYGBge3Igb3RoZXJzLCBldmFsPVRSVUV9CmxpYnJhcnkoaGF2ZW4pCgpyZWFkX3N0YXRhKAogIGhlcmUoImRhdGEiLCAiSW1wb3J0RGF0YVN0YXRhLmR0YSIpCiAgKSAtPiBkZi5zdGF0YQoKcmVhZF9zYXMoCiAgaGVyZSgiZGF0YSIsICJJbXBvcnREYXRhU0FTLnNhczdiZGF0IikKICApIC0+IGRmLnNhcwoKcmVhZF9zYXYoCiAgaGVyZSgiZGF0YSIsICJJbXBvcnREYXRhU1BTUy5zYXYiKQogICkgLT4gZGYuc3BzcwpgYGAKCkFnYWluLCBpZiB5b3UgbG9vayBhdCB0aGUgYEVudmlyb25tZW50YCB5b3Ugc2hvdWxkIHNlZSB0aGVzZSB0aHJlZSBkYXRhIGZpbGVzIGFzIHdlbGwuIAoKCiMjIEZpeGVkLXdpZHRoIGZpbGVzIApJdCBpcyBhbHNvIGNvbW1vbiB0byBlbmNvdW50ZXIgKipmaXhlZC13aWR0aCoqIGZpbGVzIHdoZXJlIHRoZSByYXcgZGF0YSBhcmUgc3RvcmVkIHdpdGhvdXQgYW55IGdhcHMgYmV0d2VlbiBzdWNjZXNzaXZlIGNvbHVtbnMuIFRoaXMgaXMgYWxzbyBhIGxlZ2FjeSBmb3JtYXQgZnJvbSB0aGUgZWFybHkgZGF5cyBvZiBjb21wdXRlcnMgYW5kIHB1bmNoIGNhcmRzLCBhbmQgb25lIG9mIHRoZSBtb3N0IGVmZmljaWVudCB3YXlzIG9mIHN0b3JpbmcgbGFyZ2UgYW1vdW50cyBvZiBkYXRhLiBUaGVzZSBmaWxlcyBjb21lIHdpdGggZG9jdW1lbnRhdGlvbiB0aGF0IHdpbGwgZ2l2ZSB5b3UgdGhlIG5lY2Vzc2FyeSBkZXRhaWxzIGFib3V0IHdoZXJlIGVhY2ggY29sdW1uIHN0YXJ0cyBhbmQgZW5kcywgZXRjLiBbU2VlIGhlcmUgZm9yIHNvbWUgZXhhbXBsZXMgb2YgbGF5b3V0cyBmcm9tIHRoZSBDZW5zdXMgQnVyZWF1XShodHRwczovL3d3dy5jZW5zdXMuZ292L3Byb2dyYW1zLXN1cnZleXMvZ2VvZ3JhcGh5L3RlY2huaWNhbC1kb2N1bWVudGF0aW9uL3JlY29yZHMtbGF5b3V0LzIwMTAtemN0YS1yZWNvcmQtbGF5b3V0Lmh0bWwjcGFyX3RleHRpbWFnZV8wKS4gIAoKYGBge3IgZGZ3LCBldmFsPVRSVUV9CnJlYWQuZndmKAogIGhlcmUoImRhdGEiLCAiZndmZGF0YS50eHQiKSwKICB3aWR0aHMgPSBjKDQsIDksIDIsIDQpLAogIGhlYWRlciA9IEZBTFNFLAogIGNvbC5uYW1lcyA9IGMoIk5hbWUiLCAiTW9udGgiLCAiRGF5IiwgIlllYXIiKQogICkgLT4gZGYuZncgCmBgYAoKTm90aWNlIHdlIG5lZWQgYHdpZHRocyA9IGMoNCw5LDIsNClgIHRvIGluZGljYXRlIGhvdyBtYW55IHNsb3RzIGVhY2ggY29sdW1uIHRha2VzIGFuZCB0aGVuIGBjb2wubmFtZXMgPSBjKCJOYW1lIiwgIk1vbnRoIiwgIkRheSIsICJZZWFyIilgIHRvIGxhYmVsIHRoZSBjb2x1bW5zIHNpbmNlIHRoZSBkYXRhIGZpbGUgZG9lcyBub3QgaGF2ZSB2YXJpYWJsZSBuYW1lcy4gaWYgeW91IG1lc3MgdXAgd2l0aCBgd2lkdGhzID0gYCB5b3UgZW5kIHVwIHdpdGggZ2FyYmFnZSBiZWNhdXNlIFIgZG9lcyBub3Qga25vdyB3aGVyZSBhbnkgY29sdW1uIHN0YXJ0cyBvciBlbmRzIHNvIGJlIGNhcmVmdWwuICAgCgoKIyBSZWFkaW5nIEZpbGVzIGZyb20gdGhlIFdlYgpPbmUgb2YgdGhlIGJlbmVmaXRzIG9mIHNvZnR3YXJlIGxpa2UgUiBpcyBpdHMgYWJpbGl0eSB0byByZWFkIGRhdGEgZmlsZXMgb3ZlciB0aGUgd2ViIF9fd2l0aG91dCByZXF1aXJpbmcgeW91IHRvIG1hbnVhbGx5IGRvd25sb2FkIHRoZSBmaWxlIGFuZCBzYXZlIGEgcGh5c2ljYWwgY29weSB0byBiZSByZWFkIGluX18uIFNwZWNpZmljYWxseSwgaXQgaXMgcG9zc2libGUgdG8gbGlzdCB0aGUgZnVsbCB3ZWItcGF0aCBmb3IgYSBmaWxlIGFuZCByZWFkIGl0IGluLiBUaGlzIGFiaWxpdHkgaXMgaW52YWx1YWJsZSB3aGVuIHRoZSBkYXRhIHRlbmQgdG8gYmUgcGVyaW9kaWNhbGx5IHVwZGF0ZWQgYnkgdGhlIHNvdXJjZSAoZm9yIGV4YW1wbGUsIGJ5IHRoZSBDZW5zdXMgQnVyZWF1LCBCdXJlYXUgb2YgTGFib3IsIEJ1cmVhdSBvZiBFY29ub21pYyBBbmFseXNpcywgZXRjLikuIEhlcmUgYXJlIGEgZmV3IGV4YW1wbGVzLiAgCgpgYGB7ciByZWFkZmlsZXMsIGV2YWw9VFJVRX0KcmVhZC50YWJsZSgKICAiaHR0cDovL2RhdGEucHJpbmNldG9uLmVkdS93d3M1MDkvZGF0YXNldHMvZWZmb3J0LmRhdCIKICApIC0+IGZwZSAKCmZwZSAjIFNob3cgbWUgd2hhdCBmcGUgY29udGFpbnMgCgpyZWFkLnRhYmxlKAogICJodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvc3RhdC9kYXRhL3Rlc3QudHh0IiwKICBoZWFkZXIgPSBUUlVFCiAgKSAtPiB0ZXN0LnR4dCAKCnJlYWRfY3N2KAogICJodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvc3RhdC9kYXRhL3Rlc3QuY3N2IgogICkgLT4gdGVzdC5jc3YKCnJlYWRfc2F2KAogICJodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvc3RhdC9kYXRhL2hzYjIuc2F2IgogICkgLT4gaHNiMi5zcHNzIApgYGAKCklmIHlvdSBsb29rIGF0IGBmcGVgIHlvdSB3aWxsIG5vdGljZSB0aGF0IHRoZXJlIGFyZSB0aHJlZSBjb2x1bW5zIGJ1dCB0aGUgcm93cyBlYWNoIGhhdmUgYSB1bmlxdWUgbmFtZS4gVGhlc2UgYXJlIGByb3cgbmFtZXNgIHRoYXQgYXJlIHJlbWVtYmVyZWQgYnkgUiBidXQgd2lsbCBub3Qgc2hvdyB1cCB3aXRoIGEgY29sdW1uIG5hbWUuICAKCgojIFJlYWRpbmcgY29tcHJlc3NlZCBmaWxlcyAKTGFyZ2UgZmlsZXMgbWF5IHNpdCBpbiBjb21wcmVzc2VkIGFyY2hpdmVzICgqLnppcCwgKi5neiwgZXRjLikgb24gdGhlIHdlYiBhbmQgUiBoYXMgYSBuZWF0IHdheSBvZiBhbGxvd2luZyB5b3UgdG8gZG93bmxvYWQgdGhlIGZpbGUsIHVuYXJjaGl2ZSBpdCwgYW5kIHRoZW4gcmVhZCBpdCBpbi4gV2h5IGlzIHRoaXMgdXNlZnVsPyBCZWNhdXNlIGlmIHRoZXNlIGZpbGVzIHRlbmQgdG8gYmUgcGVyaW9kaWNhbGx5IHVwZGF0ZWQsIFIncyBhYmlsaXR5IGxldHMgeW91IHVzZSB0aGUgc2FtZSBwaWVjZSBvZiBSIGNvZGUgdG8gZG93bmxvYWQvdW56aXAvcmVhZCBpbiB0aGUgdXBkYXRlZCBmaWxlLiBUaGUgdGVkaW91cyB3YXkgd291bGQgYmUgdG8gbWFudWFsbHkgZG93bmxvYWQgdGhlIGZpbGUsIHVuemlwIGl0LCBwbGFjZSB0aGUgZmlsZSBpbiB0aGUgYXBwcm9wcmlhdGUgZGF0YSBmb2xkZXIsIGFuZCB0aGVuIHJlYWQgaXQgaW4uCgpgYGB7ciBnemlwLCBldmFsPUZBTFNFfQp0ZW1wID0gdGVtcGZpbGUoKQoKZG93bmxvYWQuZmlsZSgiZnRwOi8vZnRwLmNkYy5nb3YvcHViL0hlYWx0aF9TdGF0aXN0aWNzL05DSFMvRGF0YXNldHMvTlZTUy9icmlkZ2Vwb3AvMjAxNi9wY2VuX3YyMDE2X3kxMDE2LnNhczdiZGF0LnppcCIsIHRlbXApCgpvdXJzYXNkYXRhID0gaGF2ZW46OnJlYWRfc2FzKHVueih0ZW1wLCAicGNlbl92MjAxNl95MTAxNi5zYXM3YmRhdCIpKQoKdW5saW5rKHRlbXApCmBgYCAKCiMgU2F2aW5nIFIgZGF0YSBmaWxlcwpZb3UgY2FuIHNhdmUgeW91ciBkYXRhIGluIGEgZm9ybWF0IHRoYXQgUiB3aWxsIHJlY29nbml6ZSwgZ2l2aW5nIGl0IHRoZSAqKlJEYXRhKiogb3IgKipyZGF0YSoqIGV4dGVuc2lvbiAKCmBgYHtyIHNhdmVyZCwgZXZhbD1GQUxTRX0Kc2F2ZSgKICBvdXJzYXNkYXRhLAogIGZpbGUgPSBoZXJlKCJkYXRhIiwgIm91cnNhc2RhdGEuUkRhdGEiKQogICkKYGBgCgpDaGVjayB0aGUgKipkYXRhKiogZGlyZWN0b3J5IHRvIGNvbmZpcm0gdGhhdCBib3RoIGZpbGVzIGFyZSBwcmVzZW50IAoKIyBNaW5pbWFsIGV4YW1wbGUgb2YgZGF0YSBwcm9jZXNzaW5nCldvcmtpbmcgd2l0aCB0aGUgKipoc2IyKiogZGF0YTogMjAwIHN0dWRlbnRzIGZyb20gdGhlIFtIaWdoIFNjaG9vbCBhbmQgQmV5b25kIHN0dWR5XShodHRwczovL25jZXMuZWQuZ292L3N1cnZleXMvaHNiLykuIFRoZSB2YXJpYWJsZXMgaW4gdGhpcyBmaWxlIGFyZTogIAoKLSBmZW1hbGUgID0gKDAvMSkgCi0gcmFjZSA9ICgxPWhpc3BhbmljIDI9YXNpYW4gMz1hZnJpY2FuLWFtZXIgND13aGl0ZSkgCi0gc2VzICA9IHNvY2lvZWNvbm9taWMgc3RhdHVzICgxPWxvdyAyPW1pZGRsZSAzPWhpZ2gpIAotIHNjaHR5cCA9ICB0eXBlIG9mIHNjaG9vbCAoMT1wdWJsaWMgMj1wcml2YXRlKSAKLSBwcm9nICAgPSB0eXBlIG9mIHByb2dyYW0gKDE9Z2VuZXJhbCAyPWFjYWRlbWljIDM9dm9jYXRpb25hbCkgCi0gcmVhZCAgPSAgc3RhbmRhcmRpemVkIHJlYWRpbmcgc2NvcmUgCi0gd3JpdGUgID0gc3RhbmRhcmRpemVkIHdyaXRpbmcgc2NvcmUgCi0gbWF0aCAgID0gc3RhbmRhcmRpemVkIG1hdGggc2NvcmUgCi0gc2NpZW5jZSA9IHN0YW5kYXJkaXplZCBzY2llbmNlIHNjb3JlIAotIHNvY3N0ID0gc3RhbmRhcmRpemVkIHNvY2lhbCBzdHVkaWVzIHNjb3JlIAoKYGBge3IgZHQxfQpyZWFkLnRhYmxlKAogICdodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvc3RhdC9kYXRhL2hzYjIuY3N2JywKICBoZWFkZXIgPSBUUlVFLAogIHNlcCA9ICIsIgogICkgLT4gaHNiMgpgYGAKCldoYXQgYXJlIHRoZSB2YXJpYWJsZSBuYW1lcyAoaS5lLiwgdGhlIGNvbHVtbiBoZWFkaW5ncykgaW4gdGhpcyBmaWxlPyBUaGUgYG5hbWVzKClgIGNvbW1hbmQgd2lsbCB0ZWxsIHlvdSB0aGF0LiAKCmBgYHtyfQpuYW1lcyhoc2IyKQpgYGAKClNpbWlsYXJseSwgdGhlIGBzdHIoKWAgY29tbWFuZCB3aWxsIHNob3cgeW91IHRoZSBkZXRhaWxzIG9mIGVhY2ggdmFyaWFibGUgIAoKYGBge3J9CnN0cihoc2IyKQpgYGAKCmFuZCB0aGUgYHN1bW1hcnkoKWAgY29tbWFuZCB3aWxsIGdpdmUgeW91IHNvbWUgc3VtbWFyeSBpbmZvcm1hdGlvbiBhYm91dCBlYWNoIHZhcmlhYmxlLiAKCmBgYHtyfQpzdW1tYXJ5KGhzYjIpCmBgYAoKVGhlcmUgYXJlIG5vIHZhbHVlIGxhYmVscyBmb3IgdGhlIHZhcmlvdXMgcXVhbGl0YXRpdmUvY2F0ZWdvcmljYWwgdmFyaWFibGVzIChmZW1hbGUsIHJhY2UsIHNlcywgc2NodHlwLCBhbmQgcHJvZykgYnV0IHRoZXNlIGFyZSBlYXNpbHkgY3JlYXRlZCBhcyBzaG93biBiZWxvdy4gCgpgYGB7ciBoc2IyY2xlYW59CmZhY3Rvcihoc2IyJGZlbWFsZSwKICAgICAgIGxldmVscyA9IGMoMCwgMSksCiAgICAgICBsYWJlbHMgPSBjKCJNYWxlIiwgIkZlbWFsZSIpCiAgICAgICApIC0+IGhzYjIkZmVtYWxlLmYgCgpmYWN0b3IoaHNiMiRyYWNlLAogICAgICAgbGV2ZWxzID0gYygxOjQpLAogICAgICAgbGFiZWxzID0gYygiSGlzcGFuaWMiLCAiQXNpYW4iLCAiQWZyaWNhbiBBbWVyaWNhbiIsICJXaGl0ZSIpCiAgICAgICApIC0+IGhzYjIkcmFjZS5mCgpmYWN0b3IoaHNiMiRzZXMsCiAgICAgICBsZXZlbHMgPSBjKDE6MyksCiAgICAgICBsYWJlbHMgPSBjKCJMb3ciLCAiTWlkZGxlIiwgIkhpZ2giKQogICAgICAgKSAtPiBoc2IyJHNlcy5mCgpmYWN0b3IoaHNiMiRzY2h0eXAsCiAgICAgICBsZXZlbHMgPSBjKDE6MiksCiAgICAgICBsYWJlbHMgPSBjKCJQdWJsaWMiLCAiUHJpdmF0ZSIpCiAgICAgICApIC0+IGhzYjIkc2NodHlwLmYKCmZhY3Rvcihoc2IyJHByb2csCiAgICAgICBsZXZlbHMgPSBjKDE6MyksCiAgICAgICBsYWJlbHMgPSBjKCJHZW5lcmFsIiwgIkFjYWRlbWljIiwgIlZvY2F0aW9uYWwiKQogICAgICAgKSAtPiBoc2IyJHByb2cuZgpgYGAKCkxldCB1cyBub3cgdGFrZSBhIHF1aWNrIGxvb2sgYXQgdGhlIGNvbnRlbnRzIG9mIGVhY2ggY29sdW1uIGluIGBoc2IyYCAKCmBgYHtyIHN1bW1hcnlfb2ZfaHNiMn0Kc3VtbWFyeShoc2IyKQpgYGAKCioqX05vdGU6XyoqIFdlIGRpZCBub3Qgb3ZlcndyaXRlIHRoZSBvcmlnaW5hbCBjb2x1bW5zLCBhbmQgdGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSBpbiBjYXNlIHdlIG1lc3MgdXAgd2l0aCB0aGlzIG1vZGlmaWNhdGlvbiwgd2Ugc3RpbGwgaGF2ZSB0aGUgb3JpZ2luYWwgY29sdW1ucyB0byB3b3JrIHdpdGguIEFsc28gcGF5IGF0dGVudGlvbiB0byBob3cgdGhlIG1vZGlmaWNhdGlvbnMgd2VyZSBtYWRlLCBmb3IgZXhhbXBsZSwgLi4uIAoKKDEpIHRha2UgY29sdW1uIGBmZW1hbGVgIHRoYXQgaGFzIHZhbHVlcyAwIGFuZCAxIAooMikgYSB2YWx1ZSBvZiAwIHNob3VsZCBiZSBsYWJlbGVkIE1hbGUgYW5kIGEgdmFsdWUgb2YgMSBzaG91bGQgYmUgbGFiZWxlZCBGZW1hbGUgCigzKSBzYXZlIHRoZSBuZXcgY29sdW1uIGFzIGBmZW1hbGUuZmAgCgpGdXJ0aGVyLCB0aGVyZSBhcmUgYXJlIGZvdXIgdmFsdWVzIGZvciBgcmFjZWAsIDMgZm9yIGBzZXNgLCAyIGZvciBgc2NodHlwYCwgYW5kIDMgZm9yIGBwcm9nYCwgc28gdGhlIG1hcHBpbmcgaGFzIHRvIHJlZmxlY3QgdGhpcy4gCgpUaGlzIGlzIGp1c3QgYSBxdWljayBydW4gdGhyb3VnaCB3aXRoIGNyZWF0aW5nIHZhbHVlIGxhYmVsczsgd2Ugd2lsbCBjb3ZlciB0aGlzIGluIGdyZWF0ZXIgZGV0YWlsIGluIGEgbGF0ZXIgbW9kdWxlLiAKCioqc2F2ZSB5b3VyIHdvcmshISoqCgpIYXZpbmcgY3JlYXRlZCBuZXcgY29sdW1ucyB3ZSBjYW4gbm93IHNhdmUgdGhlIHJlc3VsdGluZyBkYXRhIGZvciBsYXRlciB1c2UuIAoKYGBge3IgaGFzYjJzYXZlYW5ldywgZXZhbD1UUlVFfQpzYXZlKGhzYjIsCiAgICAgZmlsZSA9IGhlcmUoImRhdGEiLCAiaHNiMi5SRGF0YSIpCiAgKQpgYGAKCkxldCB1cyB0ZXN0IGlmIHRoaXMgUiBNYXJrZG93biBmaWxlIHdpbGwgS25pdCB0byBodG1sLiAgIAoKIyBEYXRhIGluIHBhY2thZ2VzIApBbG1vc3QgYWxsIFIgcGFja2FnZXMgY29tZSBidW5kbGVkIHdpdGggZGF0YS1zZXRzLCB0b28gbWFueSBvZiB0aGVtIHRvIHdhbGsgeW91IHRocm91Z2ggYnV0CgotIFtzZWUgaGVyZSBmb3Igc3RhbmRhcmQgb25lc10oaHR0cHM6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2RhdGFzZXRzL2h0bWwvMDBJbmRleC5odG1sKSAKLSBbaGVyZSBhcmUgc29tZSBtb3JlXShodHRwczovL3ZpbmNlbnRhcmVsYnVuZG9jay5naXRodWIuaW8vUmRhdGFzZXRzL2RhdGFzZXRzLmh0bWwpIAotIFthbmQgc29tZSBtb3JlXShodHRwOi8vd3d3LnB1YmxpYy5pYXN0YXRlLmVkdS9+aG9mbWFubi9kYXRhX2luX3Jfc29ydGFibGUuaHRtbCkgCgpUbyBsb2FkIGRhdGEgZnJvbSBhIHBhY2thZ2UsIGlmIHlvdSBrbm93IHRoZSBkYXRhLXNldCdzIG5hbWUsIHJ1biAKCmBgYHtyIGRhdGFzZXQxfQpsaWJyYXJ5KEhpc3REYXRhKQoKZGF0YSgiR2FsdG9uIikKCm5hbWVzKEdhbHRvbikKYGBgCgpvciB5b3UgY2FuIHJ1biAKCmBgYHtyIGRhdGFzZXQyfQpkYXRhKCJHYWx0b25GYW1pbGllcyIsIHBhY2thZ2UgPSAiSGlzdERhdGEiKQoKbmFtZXMoR2FsdG9uRmFtaWxpZXMpCmBgYAoKIyBNb3JlIG9uIHNhdmluZyBkYXRhIGFuZCB3b3Jrc3BhY2VzIAoKWW91IGNhbiBzYXZlIHlvdXIgZGF0YSB2aWEgCgogIC0gYHNhdmUoZGF0YW5hbWUsIGZpbGUgPSBoZXJlKCJmaWxlcGF0aCIsICJmaWxlbmFtZS5SRGF0YSIpKWAKICAKICAgb3IgCiAgCiAgLSBgc2F2ZShkYXRhbmFtZSwgZmlsZSA9IGhlcmUoImZpbGVwYXRoIiwgImZpbGVuYW1lLnJkYXRhIikpYAoKCmBgYHtyIG15c2F2ZTEsIGV2YWw9RkFMU0V9CmRhdGEobXRjYXJzKQoKc2F2ZShtdGNhcnMsCiAgICAgZmlsZSA9IGhlcmUoImRhdGEiLCAibXRjYXJzLlJEYXRhIikKICApCmBgYAoKQ2hlY2sgdGhlIGBkYXRhYCBmb2xkZXIgYW5kIHlvdSBzaG91bGQgc2VlIGBtdGNhcnMuUkRhdGFgLiAKCk5vdywgc2F5IHdlIHdhbnRlZCB0byBsb2FkIGEgc2F2ZWQgUiBkYXRhIGZpbGUuIEhvdyBtaWdodCB3ZSBkbyB0aGF0PyBUaGlzIGlzIGRvbmUgYmVsb3cgYnV0IGFzIHlvdSBydW4gdGhlIGNvZGUsIG5vdGUgdGhhdCB0aGUgYHJtKGxpc3QgPSBscygpKWAgY29tbWFuZCBpcyBiYXNpY2FsbHkgd2lwaW5nIHRoZSBHbG9iYWwgRW52aXJvbm1lbnQgY2xlYW4gYnkgcmVtb3ZpbmcgZXZlcnl0aGluZyBpbiBpdC4gVGhpcyBpcyB0aGUgc2FtZSBhcyBjbGlja2luZyB0aGUgJ2Jyb29tIiBpY29uIGJlc2lkZSAnSW1wb3J0IERhdGFzZXQgdicKCmBgYHtyIGxvYWQtUkRhdGF9CnJtKGxpc3QgPSBscygpKSAjIFRvIGNsZWFyIHRoZSBFbnZpcm9ubWVudCBvZiBldmVyeXRoaW5nIGluIGl0IAoKbG9hZCgKICBoZXJlKCJkYXRhIiwgIm10Y2Fycy5SRGF0YSIpCiAgKQpgYGAKCllvdSBjYW4gYWxzbyBzYXZlIG11bHRpcGxlIGRhdGEgZmlsZXMsIGluZGl2aWR1YWwgb2JqZWN0cywgYW5kIGV2ZW4gZXZlcnl0aGluZyB5b3UgaGF2ZSBkb25lIGluIGEgd29yayBzZXNzaW9uLiBUaGVzZSBhcmUgY292ZXJlZCBpbiB0aGUgdGV4dGJvb2suICAKCklmIHlvdSBhcmUgbm90IGluIGEgcHJvamVjdCBhbmQgdGhleSB0cnkgdG8gY2xvc2UgUlN0dWRpbyBhZnRlciBzb21lIGNvZGUgaGFzIGJlZW4gcnVuLCB5b3Ugd2lsbCBiZSBwcm9tcHRlZCB0byBzYXZlIChvciBub3QpIHRoZSAgYHdvcmtzcGFjZWAgYW5kIHlvdSBzaG91bGQgc2F5ICJubyIgYnkgZGVmYXVsdCB1bmxlc3MgeW91IHdhbnQgdG8gc2F2ZSB0aGUgd29ya3NwYWNlLiAKCgojIFJTdHVkaW8gd2ViaW5hcnMKVGhlIGZhbnRhc3RpYyB0ZWFtIGF0IFJTdHVkaW8gcnVucyBmcmVlIHdlYmluYXIgdGhhdCBhcmUgb2Z0ZW4gdmVyeSBoZWxwZnVsIHNvIGJlIHN1cmUgdG8gc2lnbnVwIHdpdGggeW91ciBlbWFpbC4gSGVyZSBhcmUgc29tZSB2aWRlbyByZWNvcmRpbmdzIG9mIHdlYmluYXJzIHRoYXQgYXJlIHJlbGV2YW50IHRvIHdoYXQgd2UgaGF2ZSBjb3ZlcmVkIHNvIGZhci4gCgotIFtQcm9ncmFtbWluZyBQYXJ0IDEgKFdyaXRpbmcgY29kZSBpbiBSU3R1ZGlvKV0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL3dlYmluYXJzL3JzdHVkaW8tZXNzZW50aWFscy13ZWJpbmFyLXNlcmllcy1wYXJ0LTEvKSAKLSBbUHJvZ3JhbW1pbmcgUGFydCAyIChEZWJ1Z2dpbmcgY29kZSBpbiBSU3R1ZGlvKV0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL3dlYmluYXJzL3JzdHVkaW8tZXNzZW50aWFscy13ZWJpbmFyLXNlcmllcy1wcm9ncmFtbWluZy1wYXJ0LTIvKSAKLSBbTWFuYWdpbmcgQ2hhbmdlIFBhcnQgMSAoUHJvamVjdHMgaW4gUlN0dWRpbyldKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy93ZWJpbmFycy9yc3R1ZGlvLWVzc2VudGlhbHMtd2ViaW5hci1zZXJpZXMtbWFuYWdpbmctY2hhbmdlLXBhcnQtMS8pIAotIFtJbXBvcnRpbmcgRGF0YSBpbnRvIFJdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy93ZWJpbmFycy9pbXBvcnRpbmctZGF0YS1pbnRvLXIvKSAKLSBbV2hhdHMgbmV3IHdpdGggcmVhZHhsXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvd2ViaW5hcnMvd2hhdHMtbmV3LXdpdGgtcmVhZHhsLykgCi0gW0dldHRpbmcgeW91ciBkYXRhIGludG8gUl0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL3dlYmluYXJzL2dldHRpbmcteW91ci1kYXRhLWludG8tci8pIAotIFtHZXR0aW5nIFN0YXJ0ZWQgd2l0aCBSIE1hcmtkb3duXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvd2ViaW5hcnMvZ2V0dGluZy1zdGFydGVkLXdpdGgtci1tYXJrZG93bi8pCgoKLS0tLS0tLS0tLS0tLS0tCgojIEV4ZXJjaXNlcyBmb3IgcHJhY3RpY2UgClRoZXNlIGFyZSBzb21lIGV4ZXJjaXNlcyB5b3UgY2FuIHVzZSB0byBwcmFjdGljZSBhbmQgYnVpbGQgeW91ciBSIHNraWxscy4gVGhleSBhcmUgbm90IGZvciBncmFkZS4gCgojIyBFeC4gMTogQ3JlYXRpbmcgYW5kIGtuaXR0aW5nIGEgbmV3IFJNYXJrZG93biBmaWxlCk9wZW4gYSBmcmVzaCBzZXNzaW9uIGJ5IGxhdW5jaGluZyBSU3R1ZGlvIGFuZCB0aGVuIHJ1bm5pbmcgYEZpbGUgLT4gT3BlbiBQcm9qZWN0Li4uYCAgCgpHaXZlIGl0IGEgdGl0bGUsIHlvdXIgbmFtZSBhcyB0aGUgYXV0aG9yLCBhbmQgdGhlbiBzYXZlIGl0IHdpdGggaW4gKipjb2RlKiogd2l0aCB0aGUgZm9sbG93aW5nIG5hbWU6ICBgbTFleDEuUm1kYCAgCgoKQWRkIHRoaXMgbGV2ZWwgMSBoZWFkaW5nIGBUaGUgU3RhcndhcnMgRGF0YWAgYW5kIHRoZW4gaW5zZXJ0IHlvdXIgZmlyc3QgY29kZSBjaHVuayBhcyBzaG93biBiZWxvdyBidXQgY2hhbmdlIGBldmFsID0gRkFMU0VgIHRvIGBldmFsID0gVFJVRWAuIFRoZSBSIGNvZGUgd2lsbCBub3QgYmUgZXhlY3V0ZWQgaWYgYGV2YWwgPSBGQUxTRWAuIAoKYGBge3IgZXgxYSwgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KGRwbHlyKQpkYXRhKHN0YXJ3YXJzKQpuYW1lcyhzdGFyd2FycykKYGBgCgpBZGQgdGhpcyBsZXZlbCAyIGhlYWRpbmcgYENoYXJhY3RlciBIZWlnaHRzIGFuZCBXZWlnaHRzYCBhbmQgdGhlbiB5b3VyIHNlY29uZCBjb2RlIGNodW5rIAoKYGBge3IgZXgxYiwgZXZhbD1GQUxTRX0KcGxvdChzdGFyd2FycyRoZWlnaHQsIHN0YXJ3YXJzJG1hc3MpCmBgYAoKTm93IGtuaXQgdGhpcyBmaWxlIHRvICoqaHRtbCoqIAoKCgojIyBFeC4gMjogTG9yZW0gSXBzdW0gcGFyYWdyYXBocyBhbmQgZ3JhcGhzIApHbyB0byBbdGhpcyB3ZWJzaXRlXShodHRwczovL2xvcmVtaXBzdW1nZW5lcmF0b3IuY29tL2dlbmVyYXRvci8/bj0yJnQ9cCkgYW5kIGdlbmVyYXRlIGZpdmUgTG9yZW0gSXBzdW0gcGxhY2Vob2xkZXIgdGV4dCBwYXJhZ3JhcGhzIAoKICAtIHBhcmEgMTogbXVzdCBoYXZlIGxldmVsIDEgaGVhZGluZyAKICAtIHBhcmEgMjogbXVzdCBoYXZlIGxldmVsIDIgaGVhZGluZyAKICAtIHBhcmEgMzogbXVzdCBoYXZlIGxldmVsIDMgaGVhZGluZyAKICAtIHBhcmEgNDogbXVzdCBoYXZlIGxldmVsIDQgaGVhZGluZyAKICAtIHBhcmEgNTogbXVzdCBoYXZlIGxldmVsIDUgaGVhZGluZyAKClVzaW5nIHRoZSBgc3RhcndhcnNgIGRhdGEsIGNyZWF0ZSBmaXZlIGNvZGUgY2h1bmtzLCBvbmUgYWZ0ZXIgZWFjaCBwYXJhZ3JhcGgsIG1ha2luZyBzdXJlIHRvIHNldCBldmFsID0gVFJVRSAKCiAgLSBFYWNoIGNvZGUgY2h1bmsgd2lsbCBoYXZlIHRoZSBzYW1lIFIgY29kZSAoc2VlIGJlbG93KQoKYGBge3IgZXgxYywgZXZhbD1GQUxTRX0KcGxvdChzdGFyd2FycyRoZWlnaHQsIHN0YXJ3YXJzJG1hc3MpCmBgYAoKTm93IGtuaXQgdGhpcyBmaWxlIHRvICoqaHRtbCoqIAoKCiMjIEV4LiAzOiBSZWFkaW5nIGluIHRocmVlIGRhdGEgZmlsZXMgCkNyZWF0ZSBhIG5ldyBgUk1hcmtkb3duYCBmaWxlIHRoYXQgaXMgYmxhbmsgYWZ0ZXIgdGhlIGBpbml0aWFsIHNldHVwIGNvZGUgY2h1bmtgIAoKSW5zZXJ0IGEgY29kZSBjaHVuayB0aGF0IHJlYWRzIGluIGJvdGggdGhlc2UgZmlsZXMgZm91bmQgb24gdGhlIHdlYiAKCi0gYGh0dHA6Ly93d3cuc3RhdGEuY29tL2RhdGEvandvb2xkcmlkZ2UvZWFjc2FwL21yb3ouZHRhYCAKLSBgaHR0cDovL2NhbGNuZXQubXRoLmNtaWNoLmVkdS9vcmcvc3Bzcy9WMTZfbWF0ZXJpYWxzL0RhdGFTZXRzX3YxNi9haXJsaW5lX3Bhc3NlbmdlcnMuc2F2YCAKSW4gYSBmb2xsb3ctdXAgY29kZSBjaHVuaywgcnVuIHRoZSBgc3VtbWFyeSgpYCBjb21tYW5kIG9uIGVhY2ggZGF0YS1zZXQgCgpJbiBhIHNlcGFyYXRlIGNvZGUgY2h1bmssIHJlYWQgaW4gW3RoaXMgZGF0YXNldF0oaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3RyaXBkYXRhLzIwMTUwMi1jaXRpYmlrZS10cmlwZGF0YS56aXApIGFmdGVyIHlvdSBkb3dubG9hZCBpdCBhbmQgc2F2ZSB0aGUgdW56aXBwZWQgZmlsZSBpbiB5b3VyICoqZGF0YSoqIGZvbGRlci4gCgotIFRoZSB2YXJpYWJsZSBgZ2VuZGVyYCBoYXMgdGhlIGZvbGxvd2luZyBjb2RlczogYFplcm8gPSB1bmtub3duOyAxID0gbWFsZTsgMiA9IGZlbWFsZWAgCi0gVXNlIHRoaXMgY29kaW5nIHNjaGVtZSB0byBjb252ZXJ0IGBnZW5kZXJgIGludG8gYSBgZmFjdG9yYCB3aXRoIHRoZXNlIHZhbHVlIGxhYmVscyAKCkluIGEgZm9sbG93LXVwIGNodW5rIHJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmRzIG9uIHRoaXMgZGF0YS1zZXQgCgotIGBuYW1lcygpYCAKLSBgc3RyKClgIAotIGBzdW1tYXJ5KClgIAoKSW4gYSBmaW5hbCBjaHVuaywgcnVuIHRoZSBjb21tYW5kcyBuZWNlc3NhcnkgdG8gc2F2ZSBlYWNoIG9mIHRoZSB0aHJlZSBkYXRhLXNldHMgYXMgc2VwYXJhdGUgYFJEYXRhYCBmaWxlcy4gTWFrZSBzdXJlIHlvdSBzYXZlIHRoZW0gaW4geW91ciAqKmRhdGEqKiBmb2xkZXIuIAoKTm93IGtuaXQgdGhlIGNvbXBsZXRlIGBSbWRgIGZpbGUgdG8gKipodG1sKiogCgoKIyMjIEV4LiA0OiBXZWxjb21lIHRvIEthZ2dsZSAmIE1hc3MgU2hvb3RpbmdzIApHbyB0byBbdGhpcyBwYWdlIG9uIEthZ2dsZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS96dXNtYW5pL3VzLW1hc3Mtc2hvb3RpbmdzLWxhc3QtNTAteWVhcnMpIGFuZCByZWFkIHRoZSBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YS1zZXQgb24gbWFzcyBzaG9vdGluZ3MgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgdGhhdCBvY2N1cnJlZCBkdXJpbmcgdGhlIDE5NjYtMjAxNyBwZXJpb2QuIG9uY2UgeW91IGhhdmUgcmVhZCB0aGUgb3ZlcnZpZXcgb2YgdGhlIGRhdGEsIGNsaWNrIHRoZSAiRGF0YSIgdGFiIGFuZCBkb3dubG9hZCB0aGUgZmlsZSBjYWxsZWQgYE1hc3MgU2hvb3RpbmdzIERhdGFzZXQuY3N2YC4gQmUgY2FyZWZ1bDsgdGhlcmUgYXJlIHNldmVyYWwgdmVyc2lvbnMgc28gdGhlIG9uZSB5b3Ugd2FudCBpcyB0aGUgdmVyeSBsYXN0IG9uZSBhbmQgbm90IGFueSB0aGF0IGhhdmUgYSB2ZXJzaW9uIG51bWJlciBhdHRhY2hlZCwgc3VjaCBhcyAiTWFzcyBTaG9vdGluZ3MgRGF0YXNldCBWZXIgMi5jc3YiIGZvciBleGFtcGxlLiAKCk5vdyByZWFkIHRoaXMgZmlsZSBpbnRvIFIsIHBlcmhhcHMgbmFtaW5nIGl0IHNob290aW5ncyBhbmQgcnVuIHRoZSBgc3VtbWFyeSgpYCBjb21tYW5kIG9uIGl0LiBOb3RlIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGFuZCB0aGUgbnVtYmVyIG9mIHZhcmlhYmxlcyBpbiB0aGUgZGF0YS1zZXQuIAogCgojIyMgRXguIDU6IEFuaW1hbCBTaGVsdGVycyAKR28gdG8gW3RoaXMgcGFnZSBvbiBLYWdnbGVdKGh0dHBzOi8vd3d3LmthZ2dsZS5jb20vYWFyb25zY2hsZWdlbC9hdXN0aW4tYW5pbWFsLWNlbnRlci1zaGVsdGVyLW91dGNvbWVzLWFuZCkgYW5kIGRvd25sb2FkIHRoZSBmaWxlIGNhbGxlZCBgYWFjX3NoZWx0ZXJfb3V0Y29tZXMuemlwYCwgdW56aXAgaXQsIGFuZCBBRlRFUiByZWFkaW5nIHRoZSBkYXRhIG92ZXJ2aWV3LCByZWFkIGluIHRoZSBmaWxlIGFuZCBnZW5lcmF0ZSBhIGxpc3Qgb2YgdmFyaWFibGUgbmFtZXMgd2l0aCBhbiBhcHByb3ByaWF0ZSBjb21tYW5kLiAKCgo=