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?
- Put all code in a sub-folder called
code
and all data in a sub-folder called data
- 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.
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.
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.
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.
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.
read.fwf(
here("data", "fwfdata.txt"),
widths = c(4, 9, 2, 4),
header = FALSE,
col.names = c("Name", "Month", "Day", "Year")
) -> df.fw
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.
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.
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.
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
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.
factor(hsb2$female,
levels = c(0, 1),
labels = c("Male", "Female")
) -> hsb2$female.f
factor(hsb2$race,
levels = c(1:4),
labels = c("Hispanic", "Asian", "African American", "White")
) -> hsb2$race.f
factor(hsb2$ses,
levels = c(1:3),
labels = c("Low", "Middle", "High")
) -> hsb2$ses.f
factor(hsb2$schtyp,
levels = c(1:2),
labels = c("Public", "Private")
) -> hsb2$schtyp.f
factor(hsb2$prog,
levels = c(1:3),
labels = c("General", "Academic", "Vocational")
) -> hsb2$prog.f
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, …
- take column
female
that has values 0 and 1
- a value of 0 should be labeled Male and a value of 1 should be labeled Female
- 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.
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"
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.
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.
Exercises for practice
These are some exercises you can use to practice and build your R skills. They are not for grade.
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
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
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
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
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.
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=