In the previous week we learned how to clean and prepare data with several {dplyr} functions – select(), filter(), summarise(), mutate(), group_by(), case_when(), and so on. In this module we will tackle other challenging problems and lean on another package, {tidyr} to do so.

1 {tidyr}

This library seems to have only four core functions, separate, unite(), pivot_longer, and pivot_wider() but each of them packs a wallop.

1.1 separate()

You will, at times, end up with columns that contain multiple pieces of information, all mashed up into some alphanumeric string or sequence of numbers. separate() allows us to split this mashed up column into specific pieces. For example, here are some data from the Census Bureau:

## Observations: 2,789
## Variables: 1
## $ NAME <chr> "Abilene, TX", "Callahan County, TX", "Jones County, TX", "Taylo…

This data-set contains population estimates for CBSAs – core-based statistical areas. What are these?

Metropolitan and Micropolitan Statistical Areas are collectively referred to as Core-Based Statistical Areas. Metropolitan statistical areas have at least one urbanized area of 50,000 or more population, plus adjacent territory that has a high degree of social and economic integration with the core as measured by commuting ties. Micropolitan statistical areas are a new set of statistical areas that have at least one urban cluster of at least 10,000 but less than 50,000 population, plus adjacent territory that has a high degree of social and economic integration with the core as measured by commuting ties. Metropolitan and micropolitan statistical areas are defined in terms of whole counties or county equivalents, including the six New England states. As of June 6, 2003, there are 362 metropolitan statistical areas and 560 micropolitan statistical areas in the United States. Source

Look at the column called NAME and note how It combines the state’s name (abbreviated) and the name of the area, “Abilene, TX”, “Callahan County, TX”, “Jones County, TX”, and so on. This is a common issue with many census data-sets and so it would be nice to be able to split up this NAME column into two pieces – placename (“Abilene”, “Callahan County”, etc) and stateabb (“TX”, “TX”, etc.) We do this below, with the separation occurring where a "," is seen in NAME.

##                  NAME       placename stateabb
## 1         Abilene, TX         Abilene       TX
## 2 Callahan County, TX Callahan County       TX
## 3    Jones County, TX    Jones County       TX
## 4   Taylor County, TX   Taylor County       TX
## 5           Akron, OH           Akron       OH
## 6  Portage County, OH  Portage County       OH

Here is what each piece of code is doing:

code what it does …
col = identifies the column to be separated
into = creates the names for the new columns that will result
sep = indicates where the separation should occur
remove = indicates whether the column to be separated should be removed from the data-set or retained once the new columns have been created. Setting it equal to FALSE will keep the original column, TRUE will remove it.

What if the column to be separated was made up of numbers rather than text? Take the STCOU column, for instance. Look like numbers, right? Except these numbers are really identifiers, FIPS codes to be exact, with the first two digits flagging what state this area is in and the next three digits identifying the area itself. Ohio’s FIPS code is, for instance, 39, and Portage County’s FIPS code is 133. So here, it would be nice to create two new columns, one with the state FIPS code (stfips) and the second with the county FIPS code (coufips). We do this below, but this time setting sep = 2 because we want the separation to happen after the second digit.

##   STCOU stfips coufips
## 1    NA   <NA>    <NA>
## 2 48059     48     059
## 3 48253     48     253
## 4 48441     48     441
## 5    NA   <NA>    <NA>
## 6 39133     39     133

1.2 unite()

This is the very opposite of separate() – two or more columns are united into ONE column. For example, take the file I am reading in as coudf. This file has similar content to what we read in for the CBSAs but this one has data for counties and states.

Let us filter this file so that we retain rows only for counties. I am doing this with filter(COUNTY != "000") because the state rows are the ones with COUNTY == "000".

##    STNAME        CTYNAME
## 1 Alabama        Alabama
## 2 Alabama Autauga County
## 3 Alabama Baldwin County
## 4 Alabama Barbour County
## 5 Alabama    Bibb County
## 6 Alabama  Blount County

Now I want to combine the county name (CTYNAME) and the state name (STNAME) into a single column, with the two names separated by a comma and a single white-space, i.e., by ", ".

##          CTYNAME  STNAME             countystate
## 1 Autauga County Alabama Autauga County, Alabama
## 2 Baldwin County Alabama Baldwin County, Alabama
## 3 Barbour County Alabama Barbour County, Alabama
## 4    Bibb County Alabama    Bibb County, Alabama
## 5  Blount County Alabama  Blount County, Alabama
## 6 Bullock County Alabama Bullock County, Alabama

Key elements here, are,

code what it does …
col = identifies the new column to be created
c(“..”) identifies the columns to be combined, as in c(“column1”, “column2”, “column3”)
sep = indicates if we want the merged elements to be separated in some manner. Here we are using “,” to separate with a comma followed by a single white-space. But we could have used any separator or no separator at all
remove = indicates if we want the original columns deleted (TRUE) or not (FALSE)

1.3 Pivoting data

If I look at the original CBSA file cbsa, I see that it has been setup very oddly. In particular, starting with column 6 we have a jumble of information …

  • CENSUS2010POP, ESTIMATESBASE2010, POPESTIMATE2010 all have the population total for 2010.
  • POPESTIMATE2011 through POPESTIMATE2018 are the population totals for 2011-2018
  • NPOPCHG_20XY give us net population change for 2010-2018
  • BIRTHS20XY give us the number of births for 2010-2018
  • DEATHS20XY give us the number of deaths for 2010-2018
  • NATURALINC20XY gives us the natural increase = births - deaths for 2010-2018
  • INTERNATIONALMIG20XY gives us international immigrant totals for for 2010-2018
  • DOMESTICMIG20XY gives us domestic migrant totals for 2010-2018
  • NETMIG20XY give us net migration totals for 2010-2018
  • RESIDUAL20XY gives us some small numbers left over after adding and subtracting the

Let us keep only a few columns to see what the current layout looks like.

## # A tibble: 6 x 10
##   NAME  POPESTIMATE2010 POPESTIMATE2011 POPESTIMATE2012 POPESTIMATE2013
##   <chr>           <dbl>           <dbl>           <dbl>           <dbl>
## 1 Abil…          165583          166616          167447          167472
## 2 Call…           13513           13511           13488           13501
## 3 Jone…           20237           20266           19870           20034
## 4 Tayl…          131833          132839          134089          133937
## 5 Akro…          703035          703123          702080          703625
## 6 Port…          161389          161857          161375          161691
## # … with 5 more variables: POPESTIMATE2014 <dbl>, POPESTIMATE2015 <dbl>,
## #   POPESTIMATE2016 <dbl>, POPESTIMATE2017 <dbl>, POPESTIMATE2018 <dbl>

1.3.1 Wide-to-Long with pivot_longer()

Why did they not setup the data in such a way that it had the following structure? This would make a lot more sense rather than having each year be a column all its own.

NAME YEAR POPULATION
Abilene, TX 2010 165583
Abilene, TX 2011 166616
Abilene, TX 2012 167447
…. …. ….
Callahan County, TX 2010 13513
Callahan County, TX 2011 13511
Callahan County, TX 2012 13488
…. …. ….

Well, we can easily create the proper structure of the data-set, as shown below

## # A tibble: 6 x 3
## # Groups:   NAME [2,787]
##   NAME        variable        POPULATION
##   <fct>       <chr>                <int>
## 1 Abilene, TX POPESTIMATE2010     165583
## 2 Abilene, TX POPESTIMATE2011     166616
## 3 Abilene, TX POPESTIMATE2012     167447
## 4 Abilene, TX POPESTIMATE2013     167472
## 5 Abilene, TX POPESTIMATE2014     168355
## 6 Abilene, TX POPESTIMATE2015     169704
code what it does …
names_to = identifies the name of the new column that will be created
values_to = identifies the name of the new column in which values will be stored
2:10 identifies the columns that will be pivoted from wide to long
group_by() holds unique combinations of whatever column names you put in group_by() fixed while it pivots the other columns

I still need to clean up the variable column so that it only shows the four-digit year rather than POPESTIMATE2010, and so on. Let us do this next.

## # A tibble: 6 x 3
## # Groups:   NAME [2,787]
##   NAME         YEAR POPULATION
##   <fct>       <dbl>      <int>
## 1 Abilene, TX  2010     165583
## 2 Abilene, TX  2011     166616
## 3 Abilene, TX  2012     167447
## 4 Abilene, TX  2013     167472
## 5 Abilene, TX  2014     168355
## 6 Abilene, TX  2015     169704

1.3.2 Long-to-wide

Now, let us say the data-set was a different one, perhaps the one shown below. This data-set comes from the 2017 American Community Survey and along with state FIPS codes (geoid) and state name (NAME) it contains data on income = median yearly income, rent = median monthly rent, and moe = the margin of error at the 90% confidence level.

## # A tibble: 6 x 5
##   GEOID NAME    variable estimate   moe
##   <chr> <chr>   <chr>       <dbl> <dbl>
## 1 01    Alabama income      24476   136
## 2 01    Alabama rent          747     3
## 3 02    Alaska  income      32940   508
## 4 02    Alaska  rent         1200    13
## 5 04    Arizona income      27517   148
## 6 04    Arizona rent          972     4

Notice here the setup looks weird because two different variables have been combined in a single column. Instead, the data-set should have been setup as follows:

GEOID NAME income rent moe_income moe_rent
01 Alabama 24476 747 136 3
02 Alaska 32940 1200 508 13
03 Arizona 27517 972 148 4

Well, this can be done as well, with the pivot_wider() function that takes from the “long” format to the “wide” format.

## # A tibble: 6 x 6
## # Groups:   GEOID, NAME [6]
##   GEOID NAME       estimate_income estimate_rent moe_income moe_rent
##   <chr> <chr>                <dbl>         <dbl>      <dbl>    <dbl>
## 1 01    Alabama              24476           747        136        3
## 2 02    Alaska               32940          1200        508       13
## 3 04    Arizona              27517           972        148        4
## 4 05    Arkansas             23789           709        165        5
## 5 06    California           29454          1358        109        3
## 6 08    Colorado             32401          1125        109        5
code what it does …
names_from = identifies the column from which unique values will be taken to create the names of the new columns that will result
values_from = identifies the column(s) from which the values should be assigned to the new columns that will result
group_by() holds unique value combinations of whatever column names you put in group_by() fixed while it pivots the rows to new columns

The example that follows is a tricky one so be careful!!

Here is another example, this time with the cbsa data but showing you how we might use a combination of pivot_longer() and pivot_wider()

## # A tibble: 6 x 5
## # Groups:   NAME [2,787]
##   STCOU NAME        LSAD                          variable        estimate
##   <int> <fct>       <fct>                         <chr>              <int>
## 1    NA Abilene, TX Metropolitan Statistical Area POPESTIMATE2010   165583
## 2    NA Abilene, TX Metropolitan Statistical Area POPESTIMATE2011   166616
## 3    NA Abilene, TX Metropolitan Statistical Area POPESTIMATE2012   167447
## 4    NA Abilene, TX Metropolitan Statistical Area POPESTIMATE2013   167472
## 5    NA Abilene, TX Metropolitan Statistical Area POPESTIMATE2014   168355
## 6    NA Abilene, TX Metropolitan Statistical Area POPESTIMATE2015   169704

Now I will clean up the contents of cbsa.01 so that year is a separate column.

## # A tibble: 6 x 7
## # Groups:   NAME [2,787]
##   STCOU NAME       LSAD                   variable      vartype   year  estimate
##   <int> <fct>      <fct>                  <chr>         <chr>     <chr>    <int>
## 1    NA Abilene, … Metropolitan Statisti… POPESTIMATE2… POPESTIM… 2010    165583
## 2    NA Abilene, … Metropolitan Statisti… POPESTIMATE2… POPESTIM… 2011    166616
## 3    NA Abilene, … Metropolitan Statisti… POPESTIMATE2… POPESTIM… 2012    167447
## 4    NA Abilene, … Metropolitan Statisti… POPESTIMATE2… POPESTIM… 2013    167472
## 5    NA Abilene, … Metropolitan Statisti… POPESTIMATE2… POPESTIM… 2014    168355
## 6    NA Abilene, … Metropolitan Statisti… POPESTIMATE2… POPESTIM… 2015    169704

Now the final flip to wide format …

## Observations: 25,083
## Variables: 11
## Groups: NAME, year [25,083]
## $ NAME             <fct> "Abilene, TX", "Abilene, TX", "Abilene, TX", "Abilen…
## $ year             <chr> "2010", "2011", "2012", "2013", "2014", "2015", "201…
## $ POPESTIMATE      <list> [165583, 166616, 167447, 167472, 168355, 169704, 17…
## $ NPOPCHG          <list> [337, 1033, 831, 25, 883, 1349, 314, 498, 935, -33,…
## $ BIRTHS           <list> [540, 2295, 2358, 2390, 2382, 2417, 2379, 2427, 238…
## $ DEATHS           <list> [406, 1506, 1587, 1694, 1598, 1698, 1726, 1705, 173…
## $ NATURALINC       <list> [134, 789, 771, 696, 784, 719, 653, 722, 642, -29, …
## $ INTERNATIONALMIG <list> [84, 205, 516, 361, 419, 484, 388, 325, 282, 0, 4, …
## $ DOMESTICMIG      <list> [124, 54, -448, -1051, -301, 162, -723, -544, 19, -…
## $ NETMIG           <list> [208, 259, 68, -690, 118, 646, -335, -219, 301, -3,…
## $ RESIDUAL         <list> [-5, -15, -8, 19, -19, -16, -4, -5, -8, -1, -1, -2,…

2 Practice Exercises

2.1 Exercise 1

Using the cmhflights data from last week, create a column that unites the three columns Year, Month, and DayofMonth into a single column that we will name date_of_flight. This column should separate the three fields by “-”.

Hint: You will have to use load(...) with here(...).

2.2 Exercise 2

Sticking with cmhflights, separate OriginCityName into two new columns, origin_city and origin_state. Do the same for DestCityname, calling the new columns destination_city and destination_state, respectively. Both city columns should only display the name of the city, while both state columns should only display the abbreviated state name (for example, “CA”, “OH”, etc.)

2.3 Exercise 3

Tidy the weather data such that the resulting data-set, called wdf, has the days (the d1-d31 columns) as rows and TMIN and TMAX as columns. The end result should be as shown below:

id year month days TMAX TMIN
MX000017004 2010 1 d1 NA NA
MX000017004 2010 1 d2 NA NA
MX000017004 2010 1 d3 NA NA
MX000017004 2010 1 d4 NA NA
MX000017004 2010 1 d5 NA NA
MX000017004 2010 1 d6 NA NA
MX000017004 2010 1 d7 NA NA
MX000017004 2010 1 d8 NA NA
MX000017004 2010 1 d9 NA NA
MX000017004 2010 1 d10 NA NA
LS0tCnRpdGxlOiAiTVBBIDU4MzAgLSBNb2R1bGUgMDMiCnN1YnRpdGxlOiAiU3ByaW5nIDIwMjAiCmF1dGhvcjogIlByb2Zlc3NvciBSdWhpbCIKZGF0ZTogIlVwZGF0ZWQgb24gYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDogCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoKYm9keXsgLyogTm9ybWFsICAqLwovKiAgICBmb250LWZhbWlseTogTGF0bywgc2Fucy1zZXJpZjsgIAogICAgICBmb250LWZhbWlseTogTXVrdGEsIHNhbnMtc2VyaWY7IAogICAgICBmb250LWZhbWlseTogJ051bml0byBTYW5zJywgc2Fucy1zZXJpZjsKICAgICAgZm9udC1mYW1pbHk6IEthcmxhLCBzYW5zLXNlcmlmOyAgKi8KICAgICAgZm9udC1mYW1pbHk6ICdNZXJyaXdlYXRoZXIgU2FucycsIHNhbnMtc2VyaWY7IAogICAgICBmb250LXNpemU6IDE4cHg7CiAgfQoKaDEudGl0bGUgewogIGZvbnQtc2l6ZTogMzhweDsKICBjb2xvcjogRGFya1JlZDsKfQoKaDEgeyAvKiBIZWFkZXIgMSAqLwogIGZvbnQtc2l6ZTogMjhweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgyIHsgLyogSGVhZGVyIDIgKi8KICAgIGZvbnQtc2l6ZTogMjJweDsKICBjb2xvcjogRGFya0JsdWU7Cn0KCmgzIHsgLyogSGVhZGVyIDMgKi8KICBmb250LXNpemU6IDE4cHg7CiAgY29sb3I6IERhcmtCbHVlOwp9Cgpjb2RlLnJ7IC8qIENvZGUgYmxvY2sgKi8KICAgIGZvbnQtZmFtaWx5OiBNdWt0YSwgc2Fucy1zZXJpZjsgCiAgICBmb250LXdlaWdodDogNjAwOyAgCiAgICBmb250LXNpemU6IDE2cHg7Cn0KCi8qIHByZSB7IC8qIENvZGUgYmxvY2sgLSBkZXRlcm1pbmVzIGNvZGUgc3BhY2luZyBiZXR3ZWVuIGxpbmVzICovCiAgICBmb250LXNpemU6IDE2cHg7Cn0gKi8KPC9zdHlsZT4KCgpgYGB7ciBrbGlwcHksIGVjaG8gPSBGQUxTRSwgaW5jbHVkZSA9IFRSVUV9CmtsaXBweTo6a2xpcHB5KHRvb2x0aXBfbWVzc2FnZSA9ICdDbGljayB0byBjb3B5JywgdG9vbHRpcF9zdWNjZXNzID0gJ0RvbmUnLCBjb2xvciA9ICdjb3JuZmxvd2VyYmx1ZScsIHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpCmBgYAoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGRwaSA9IDMwMCwgY2FjaGUgPSBUUlVFLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4LCBvdXQud2lkdGggPSAiMTAwJSIsIGhpZ2hsaWdodCA9IFRSVUUpIApgYGAKCmBgYHtyIHNldHVwbiwgaW5jbHVkZT1GQUxTRX0KIyBjYW1vID0ga25pdHI6OmtuaXRfdGhlbWUkZ2V0KCJjYW1vIikKIyBrbml0cjo6a25pdF90aGVtZSRzZXQoY2FtbykKYGBgCgpJbiB0aGUgcHJldmlvdXMgd2VlayB3ZSBsZWFybmVkIGhvdyB0byBjbGVhbiBhbmQgcHJlcGFyZSBkYXRhIHdpdGggc2V2ZXJhbCB7ZHBseXJ9IGZ1bmN0aW9ucyAtLSBgc2VsZWN0KClgLCBgZmlsdGVyKClgLCBgc3VtbWFyaXNlKClgLCBgbXV0YXRlKClgLCBgZ3JvdXBfYnkoKWAsIGBjYXNlX3doZW4oKWAsIGFuZCBzbyBvbi4gSW4gdGhpcyBtb2R1bGUgd2Ugd2lsbCB0YWNrbGUgb3RoZXIgY2hhbGxlbmdpbmcgcHJvYmxlbXMgYW5kIGxlYW4gb24gYW5vdGhlciBwYWNrYWdlLCB7dGlkeXJ9IHRvIGRvIHNvLiAKCiMge3RpZHlyfQpUaGlzIGxpYnJhcnkgc2VlbXMgdG8gaGF2ZSBvbmx5IGZvdXIgY29yZSBmdW5jdGlvbnMsIGBzZXBhcmF0ZWAsIGB1bml0ZSgpYCwgYHBpdm90X2xvbmdlcmAsIGFuZCBgcGl2b3Rfd2lkZXIoKWAgYnV0IGVhY2ggb2YgdGhlbSBwYWNrcyBhIHdhbGxvcC4gIAoKIyMgc2VwYXJhdGUoKQpZb3Ugd2lsbCwgYXQgdGltZXMsIGVuZCB1cCB3aXRoIGNvbHVtbnMgdGhhdCBjb250YWluIG11bHRpcGxlIHBpZWNlcyBvZiBpbmZvcm1hdGlvbiwgYWxsIG1hc2hlZCB1cCBpbnRvIHNvbWUgYWxwaGFudW1lcmljIHN0cmluZyBvciBzZXF1ZW5jZSBvZiBudW1iZXJzLiBgc2VwYXJhdGUoKWAgYWxsb3dzIHVzIHRvIHNwbGl0IHRoaXMgbWFzaGVkIHVwIGNvbHVtbiBpbnRvIHNwZWNpZmljIHBpZWNlcy4gRm9yIGV4YW1wbGUsIGhlcmUgYXJlIHNvbWUgZGF0YSBmcm9tIHRoZSBDZW5zdXMgQnVyZWF1OiAKCmBgYHtyIHRpZHkwMX0KbGlicmFyeSh0aWR5dmVyc2UpCgpyZWFkX2NzdigKICAiaHR0cHM6Ly93d3cyLmNlbnN1cy5nb3YvcHJvZ3JhbXMtc3VydmV5cy9wb3Blc3QvZGF0YXNldHMvMjAxMC0yMDE4L21ldHJvL3RvdGFscy9jYnNhLWVzdDIwMTgtYWxsZGF0YS5jc3YiCiAgKSAtPiBjYnNhCgpjYnNhICU+JQogIHNlbGVjdChOQU1FKSAlPiUKICBnbGltcHNlKCkKICAKYGBgCgpUaGlzIGRhdGEtc2V0IGNvbnRhaW5zIHBvcHVsYXRpb24gZXN0aW1hdGVzIGZvciBDQlNBcyAtLSBjb3JlLWJhc2VkIHN0YXRpc3RpY2FsIGFyZWFzLiBXaGF0IGFyZSB0aGVzZT8gCgo+IE1ldHJvcG9saXRhbiBhbmQgTWljcm9wb2xpdGFuIFN0YXRpc3RpY2FsIEFyZWFzIGFyZSBjb2xsZWN0aXZlbHkgcmVmZXJyZWQgdG8gYXMgQ29yZS1CYXNlZCBTdGF0aXN0aWNhbCBBcmVhcy4KPiBNZXRyb3BvbGl0YW4gc3RhdGlzdGljYWwgYXJlYXMgaGF2ZSBhdCBsZWFzdCBvbmUgdXJiYW5pemVkIGFyZWEgb2YgNTAsMDAwIG9yIG1vcmUgcG9wdWxhdGlvbiwgcGx1cyBhZGphY2VudCB0ZXJyaXRvcnkgdGhhdCBoYXMgYSBoaWdoIGRlZ3JlZSBvZiBzb2NpYWwgYW5kIGVjb25vbWljIGludGVncmF0aW9uIHdpdGggdGhlIGNvcmUgYXMgbWVhc3VyZWQgYnkgY29tbXV0aW5nIHRpZXMuCk1pY3JvcG9saXRhbiBzdGF0aXN0aWNhbCBhcmVhcyBhcmUgYSBuZXcgc2V0IG9mIHN0YXRpc3RpY2FsIGFyZWFzIHRoYXQgaGF2ZSBhdCBsZWFzdCBvbmUgdXJiYW4gY2x1c3RlciBvZiBhdCBsZWFzdCAxMCwwMDAgYnV0IGxlc3MgdGhhbiA1MCwwMDAgcG9wdWxhdGlvbiwgcGx1cyBhZGphY2VudCB0ZXJyaXRvcnkgdGhhdCBoYXMgYSBoaWdoIGRlZ3JlZSBvZiBzb2NpYWwgYW5kIGVjb25vbWljIGludGVncmF0aW9uIHdpdGggdGhlIGNvcmUgYXMgbWVhc3VyZWQgYnkgY29tbXV0aW5nIHRpZXMuCj4gTWV0cm9wb2xpdGFuIGFuZCBtaWNyb3BvbGl0YW4gc3RhdGlzdGljYWwgYXJlYXMgYXJlIGRlZmluZWQgaW4gdGVybXMgb2Ygd2hvbGUgY291bnRpZXMgb3IgY291bnR5IGVxdWl2YWxlbnRzLCBpbmNsdWRpbmcgdGhlIHNpeCBOZXcgRW5nbGFuZCBzdGF0ZXMuIEFzIG9mIEp1bmUgNiwgMjAwMywgdGhlcmUgYXJlIDM2MiBtZXRyb3BvbGl0YW4gc3RhdGlzdGljYWwgYXJlYXMgYW5kIDU2MCBtaWNyb3BvbGl0YW4gc3RhdGlzdGljYWwgYXJlYXMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMuIFtTb3VyY2VdKGh0dHBzOi8vd3d3LmNlbnN1cy5nb3YvdG9waWNzL2hvdXNpbmcvaG91c2luZy1wYXR0ZXJucy9hYm91dC9jb3JlLWJhc2VkLXN0YXRpc3RpY2FsLWFyZWFzLmh0bWwpCgpMb29rIGF0IHRoZSBjb2x1bW4gY2FsbGVkIGBOQU1FYCBhbmQgbm90ZSBob3cgSXQgY29tYmluZXMgdGhlIHN0YXRlJ3MgbmFtZSAoYWJicmV2aWF0ZWQpIGFuZCB0aGUgbmFtZSBvZiB0aGUgYXJlYSwgIkFiaWxlbmUsIFRYIiwgIkNhbGxhaGFuIENvdW50eSwgVFgiLCAiSm9uZXMgQ291bnR5LCBUWCIsIGFuZCBzbyBvbi4gVGhpcyBpcyBhIGNvbW1vbiBpc3N1ZSB3aXRoIG1hbnkgY2Vuc3VzIGRhdGEtc2V0cyBhbmQgc28gaXQgd291bGQgYmUgbmljZSB0byBiZSBhYmxlIHRvIHNwbGl0IHVwIHRoaXMgYE5BTUVgIGNvbHVtbiBpbnRvIHR3byBwaWVjZXMgLS0gYHBsYWNlbmFtZWAgKCJBYmlsZW5lIiwgIkNhbGxhaGFuIENvdW50eSIsIGV0YykgYW5kIGBzdGF0ZWFiYmAgKCJUWCIsICJUWCIsIGV0Yy4pIFdlIGRvIHRoaXMgYmVsb3csIHdpdGggdGhlIHNlcGFyYXRpb24gb2NjdXJyaW5nIHdoZXJlIGEgYCIsImAgaXMgc2VlbiBpbiBgTkFNRWAuIAoKYGBge3IgdGlkeTAyfQpjYnNhICU+JQogIHNlcGFyYXRlKAogICAgY29sID0gTkFNRSwgCiAgICBpbnRvID0gYygicGxhY2VuYW1lIiwgInN0YXRlYWJiIiksCiAgICBzZXAgPSAiLCIsCiAgICByZW1vdmUgPSBGQUxTRQogICkgLT4gY2JzYSAKCmNic2EgJT4lCiAgc2VsZWN0KE5BTUUsIHBsYWNlbmFtZSwgc3RhdGVhYmIpICU+JQogIGhlYWQoKQpgYGAKCkhlcmUgaXMgd2hhdCBlYWNoIHBpZWNlIG9mIGNvZGUgaXMgZG9pbmc6Cgp8IGNvZGUgfCB3aGF0IGl0IGRvZXMgLi4uIHwKfDotLSAgIHwgOi0tLS0gICAgICAgICAgICB8CnwgY29sID0gfCBpZGVudGlmaWVzIHRoZSBjb2x1bW4gdG8gYmUgc2VwYXJhdGVkIHwKfCBpbnRvID0gfCBjcmVhdGVzIHRoZSBuYW1lcyBmb3IgdGhlIG5ldyBjb2x1bW5zIHRoYXQgd2lsbCByZXN1bHQgfAp8IHNlcCA9IHwgaW5kaWNhdGVzIHdoZXJlIHRoZSBzZXBhcmF0aW9uIHNob3VsZCBvY2N1ciB8CnwgcmVtb3ZlID0gfCBpbmRpY2F0ZXMgd2hldGhlciB0aGUgY29sdW1uIHRvIGJlIHNlcGFyYXRlZCBzaG91bGQgYmUgcmVtb3ZlZCBmcm9tIHRoZSBkYXRhLXNldCBvciByZXRhaW5lZCBvbmNlIHRoZSBuZXcgY29sdW1ucyBoYXZlIGJlZW4gY3JlYXRlZC4gU2V0dGluZyBpdCBlcXVhbCB0byBgRkFMU0VgIHdpbGwga2VlcCB0aGUgb3JpZ2luYWwgY29sdW1uLCBgVFJVRWAgd2lsbCByZW1vdmUgaXQuIHwKCldoYXQgaWYgdGhlIGNvbHVtbiB0byBiZSBzZXBhcmF0ZWQgd2FzIG1hZGUgdXAgb2YgbnVtYmVycyByYXRoZXIgdGhhbiB0ZXh0PyBUYWtlIHRoZSBgU1RDT1VgIGNvbHVtbiwgZm9yIGluc3RhbmNlLiBMb29rIGxpa2UgbnVtYmVycywgcmlnaHQ/IEV4Y2VwdCB0aGVzZSBudW1iZXJzIGFyZSByZWFsbHkgaWRlbnRpZmllcnMsIEZJUFMgY29kZXMgdG8gYmUgZXhhY3QsIHdpdGggdGhlIGZpcnN0IHR3byBkaWdpdHMgZmxhZ2dpbmcgd2hhdCBzdGF0ZSB0aGlzIGFyZWEgaXMgaW4gYW5kIHRoZSBuZXh0IHRocmVlIGRpZ2l0cyBpZGVudGlmeWluZyB0aGUgYXJlYSBpdHNlbGYuIE9oaW8ncyBGSVBTIGNvZGUgaXMsIGZvciBpbnN0YW5jZSwgYDM5YCwgYW5kIFBvcnRhZ2UgQ291bnR5J3MgRklQUyBjb2RlIGlzIGAxMzNgLiBTbyBoZXJlLCBpdCB3b3VsZCBiZSBuaWNlIHRvIGNyZWF0ZSB0d28gbmV3IGNvbHVtbnMsIG9uZSB3aXRoIHRoZSBzdGF0ZSBGSVBTIGNvZGUgKGBzdGZpcHNgKSBhbmQgdGhlIHNlY29uZCB3aXRoIHRoZSBjb3VudHkgRklQUyBjb2RlIChgY291Zmlwc2ApLiBXZSBkbyB0aGlzIGJlbG93LCBidXQgdGhpcyB0aW1lIHNldHRpbmcgYHNlcCA9IDJgIGJlY2F1c2Ugd2Ugd2FudCB0aGUgc2VwYXJhdGlvbiB0byBoYXBwZW4gYWZ0ZXIgdGhlIHNlY29uZCBkaWdpdC4gCgpgYGB7ciB0aWR5MDN9CmNic2EgJT4lCiAgc2VwYXJhdGUoCiAgICBjb2wgPSBTVENPVSwgCiAgICBpbnRvID0gYygic3RmaXBzIiwgImNvdWZpcHMiKSwKICAgIHNlcCA9IDIsCiAgICByZW1vdmUgPSBGQUxTRQogICkgLT4gY2JzYQoKY2JzYSAlPiUKICBzZWxlY3QoU1RDT1UsIHN0ZmlwcywgY291ZmlwcykgJT4lCiAgaGVhZCgpCmBgYAoKCiMjIHVuaXRlKCkKVGhpcyBpcyB0aGUgdmVyeSBvcHBvc2l0ZSBvZiBgc2VwYXJhdGUoKWAgLS0gdHdvIG9yIG1vcmUgY29sdW1ucyBhcmUgdW5pdGVkIGludG8gT05FIGNvbHVtbi4gRm9yIGV4YW1wbGUsIHRha2UgdGhlIGZpbGUgSSBhbSByZWFkaW5nIGluIGFzIGBjb3VkZmAuIFRoaXMgZmlsZSBoYXMgc2ltaWxhciBjb250ZW50IHRvIHdoYXQgd2UgcmVhZCBpbiBmb3IgdGhlIENCU0FzIGJ1dCB0aGlzIG9uZSBoYXMgZGF0YSBmb3IgY291bnRpZXMgYW5kIHN0YXRlcy4gCgpgYGB7ciB0aWR5MDR9CnJlYWRfY3N2KAogICJodHRwczovL3d3dzIuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL3BvcGVzdC9kYXRhc2V0cy8yMDEwLTIwMTgvY291bnRpZXMvdG90YWxzL2NvLWVzdDIwMTgtYWxsZGF0YS5jc3YiCiAgKSAtPiBjb3VkZgpgYGAKCkxldCB1cyBmaWx0ZXIgdGhpcyBmaWxlIHNvIHRoYXQgd2UgcmV0YWluIHJvd3Mgb25seSBmb3IgY291bnRpZXMuIEkgYW0gZG9pbmcgdGhpcyB3aXRoIGBmaWx0ZXIoQ09VTlRZICE9ICIwMDAiKWAgYmVjYXVzZSB0aGUgc3RhdGUgcm93cyBhcmUgdGhlIG9uZXMgd2l0aCBgQ09VTlRZID09ICIwMDAiYC4gCgpgYGB7ciB0aWR5MDV9CmNvdWRmICU+JQogIGZpbHRlcihDT1VOVFkgIT0gIjAwMCIpIC0+IGNvdWRmMgoKY291ZGYyICU+JQogIHNlbGVjdChTVE5BTUUsIENUWU5BTUUpICU+JQogIGhlYWQoKQpgYGAKCk5vdyBJIHdhbnQgdG8gY29tYmluZSB0aGUgY291bnR5IG5hbWUgKGBDVFlOQU1FYCkgYW5kIHRoZSBzdGF0ZSBuYW1lIChgU1ROQU1FYCkgaW50byBhIHNpbmdsZSBjb2x1bW4sIHdpdGggdGhlIHR3byBuYW1lcyBzZXBhcmF0ZWQgYnkgYSBjb21tYSBhbmQgYSBzaW5nbGUgd2hpdGUtc3BhY2UsIGkuZS4sIGJ5IGAiLCAiYC4gCgpgYGB7ciB0aWR5MDZ9CmNvdWRmMiAlPiUKICB1bml0ZSgKICAgIGNvbCA9ICJjb3VudHlzdGF0ZSIsCiAgICBjKCJDVFlOQU1FIiwgIlNUTkFNRSIpLAogICAgc2VwID0gIiwgIiwKICAgIHJlbW92ZSA9IEZBTFNFCiAgKSAtPiBjb3VkZjIKCmNvdWRmMiAlPiUKICBzZWxlY3QoQ1RZTkFNRSwgU1ROQU1FLCBjb3VudHlzdGF0ZSkgJT4lCiAgaGVhZCgpCmBgYAoKS2V5IGVsZW1lbnRzIGhlcmUsIGFyZSwgCgp8IGNvZGUgfCB3aGF0IGl0IGRvZXMgLi4uIHwKfCA6LS0gIHwgOi0tICAgICAgICAgICAgICB8CnwgY29sID0gfCBpZGVudGlmaWVzIHRoZSBgbmV3IGNvbHVtbmAgdG8gYmUgY3JlYXRlZCB8CnwgYygiLi4iKSB8IGlkZW50aWZpZXMgdGhlIGNvbHVtbnMgdG8gYmUgY29tYmluZWQsIGFzIGluIGMoImNvbHVtbjEiLCAiY29sdW1uMiIsICJjb2x1bW4zIikgfAp8IHNlcCA9IHwgaW5kaWNhdGVzIGlmIHdlIHdhbnQgdGhlIG1lcmdlZCBlbGVtZW50cyB0byBiZSBzZXBhcmF0ZWQgaW4gc29tZSBtYW5uZXIuIEhlcmUgd2UgYXJlIHVzaW5nICIsICIgdG8gc2VwYXJhdGUgd2l0aCBhIGNvbW1hIGZvbGxvd2VkIGJ5IGEgc2luZ2xlIHdoaXRlLXNwYWNlLiBCdXQgd2UgY291bGQgaGF2ZSB1c2VkIGFueSBzZXBhcmF0b3Igb3Igbm8gc2VwYXJhdG9yIGF0IGFsbCB8CnwgcmVtb3ZlID0gfCBpbmRpY2F0ZXMgaWYgd2Ugd2FudCB0aGUgb3JpZ2luYWwgY29sdW1ucyBkZWxldGVkIGAoVFJVRSlgIG9yIG5vdCBgKEZBTFNFKWAgfAoKCiMjIFBpdm90aW5nIGRhdGEgCklmIEkgbG9vayBhdCB0aGUgb3JpZ2luYWwgQ0JTQSBmaWxlIGBjYnNhYCwgSSBzZWUgdGhhdCBpdCBoYXMgYmVlbiBzZXR1cCB2ZXJ5IG9kZGx5LiBJbiBwYXJ0aWN1bGFyLCBzdGFydGluZyB3aXRoIGNvbHVtbiA2IHdlIGhhdmUgYSBqdW1ibGUgb2YgaW5mb3JtYXRpb24gLi4uCgorIENFTlNVUzIwMTBQT1AsIEVTVElNQVRFU0JBU0UyMDEwLCBQT1BFU1RJTUFURTIwMTAgYWxsIGhhdmUgdGhlIHBvcHVsYXRpb24gdG90YWwgZm9yIDIwMTAuIAorIFBPUEVTVElNQVRFMjAxMSB0aHJvdWdoIFBPUEVTVElNQVRFMjAxOCBhcmUgdGhlIHBvcHVsYXRpb24gdG90YWxzIGZvciAyMDExLTIwMTggCisgTlBPUENIR18yMFhZIGdpdmUgdXMgbmV0IHBvcHVsYXRpb24gY2hhbmdlIGZvciAyMDEwLTIwMTggCisgQklSVEhTMjBYWSBnaXZlIHVzIHRoZSBudW1iZXIgb2YgYmlydGhzIGZvciAyMDEwLTIwMTggCisgREVBVEhTMjBYWSBnaXZlIHVzIHRoZSBudW1iZXIgb2YgZGVhdGhzIGZvciAyMDEwLTIwMTggCisgTkFUVVJBTElOQzIwWFkgZ2l2ZXMgdXMgdGhlIG5hdHVyYWwgaW5jcmVhc2UgPSBiaXJ0aHMgLSBkZWF0aHMgZm9yIDIwMTAtMjAxOCAKKyBJTlRFUk5BVElPTkFMTUlHMjBYWSBnaXZlcyB1cyBpbnRlcm5hdGlvbmFsIGltbWlncmFudCB0b3RhbHMgZm9yIGZvciAyMDEwLTIwMTggCisgRE9NRVNUSUNNSUcyMFhZIGdpdmVzIHVzIGRvbWVzdGljIG1pZ3JhbnQgdG90YWxzIGZvciAyMDEwLTIwMTggCisgTkVUTUlHMjBYWSBnaXZlIHVzIG5ldCBtaWdyYXRpb24gdG90YWxzIGZvciAyMDEwLTIwMTggCisgUkVTSURVQUwyMFhZIGdpdmVzIHVzIHNvbWUgc21hbGwgbnVtYmVycyBsZWZ0IG92ZXIgYWZ0ZXIgYWRkaW5nIGFuZCBzdWJ0cmFjdGluZyB0aGUgCgpMZXQgdXMga2VlcCBvbmx5IGEgZmV3IGNvbHVtbnMgdG8gc2VlIHdoYXQgdGhlIGN1cnJlbnQgbGF5b3V0IGxvb2tzIGxpa2UuCgpgYGB7ciB0aWR5MDd9CnJlYWRfY3N2KAogICJodHRwczovL3d3dzIuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL3BvcGVzdC9kYXRhc2V0cy8yMDEwLTIwMTgvbWV0cm8vdG90YWxzL2Nic2EtZXN0MjAxOC1hbGxkYXRhLmNzdiIKICApIC0+IGNic2EKCmNic2EgJT4lCiAgc2VsZWN0KGMoNCwgODoxNikpIC0+IGNic2EwMQoKY2JzYTAxICU+JQogIGhlYWQoKQpgYGAKCiMjIyBXaWRlLXRvLUxvbmcgd2l0aCBwaXZvdF9sb25nZXIoKSAKV2h5IGRpZCB0aGV5IG5vdCBzZXR1cCB0aGUgZGF0YSBpbiBzdWNoIGEgd2F5IHRoYXQgaXQgaGFkIHRoZSBmb2xsb3dpbmcgc3RydWN0dXJlPyBUaGlzIHdvdWxkIG1ha2UgYSBsb3QgbW9yZSBzZW5zZSByYXRoZXIgdGhhbiBoYXZpbmcgZWFjaCB5ZWFyIGJlIGEgY29sdW1uIGFsbCBpdHMgb3duLiAKCnwgTkFNRSB8IFlFQVIgfCBQT1BVTEFUSU9OIHwKfCA6LS0gIHwgOi0tICB8IDotLSAgICAgICAgfAp8IEFiaWxlbmUsIFRYIHwgMjAxMCB8IDE2NTU4MyB8CnwgQWJpbGVuZSwgVFggfCAyMDExIHwgMTY2NjE2IHwKfCBBYmlsZW5lLCBUWCB8IDIwMTIgfCAxNjc0NDcgfAp8IC4uLi4gICAgICAgIHwgLi4uLiB8IC4uLi4gICB8CnwgQ2FsbGFoYW4gQ291bnR5LCBUWCB8IDIwMTAgfCAxMzUxMyB8CnwgQ2FsbGFoYW4gQ291bnR5LCBUWCB8IDIwMTEgfCAxMzUxMSB8CnwgQ2FsbGFoYW4gQ291bnR5LCBUWCB8IDIwMTIgfCAxMzQ4OCB8CnwgLi4uLiAgICAgICAgfCAuLi4uIHwgLi4uLiAgIHwKCldlbGwsIHdlIGNhbiBlYXNpbHkgY3JlYXRlIHRoZSBwcm9wZXIgc3RydWN0dXJlIG9mIHRoZSBkYXRhLXNldCwgYXMgc2hvd24gYmVsb3cKCmBgYHtyIHRpZHkwOH0KY2JzYTAxICU+JQogIGdyb3VwX2J5KE5BTUUpICU+JQogIHBpdm90X2xvbmdlcigKICAgIG5hbWVzX3RvID0gInZhcmlhYmxlIiwKICAgIHZhbHVlc190byA9ICJQT1BVTEFUSU9OIiwKICAgIDI6MTAKICApIC0+IGNic2EwMS5sb25nCgpjYnNhMDEubG9uZyAlPiUKICBoZWFkKCkKYGBgCgp8IGNvZGUgfCB3aGF0IGl0IGRvZXMgLi4uIHwKfCA6LS0gfCA6LS0gfAp8IG5hbWVzX3RvID0gfCBpZGVudGlmaWVzIHRoZSBuYW1lIG9mIHRoZSBuZXcgY29sdW1uIHRoYXQgd2lsbCBiZSBjcmVhdGVkIHwKfCB2YWx1ZXNfdG8gPSB8IGlkZW50aWZpZXMgdGhlIG5hbWUgb2YgdGhlIG5ldyBjb2x1bW4gaW4gd2hpY2ggdmFsdWVzIHdpbGwgYmUgc3RvcmVkIHwKfCAyOjEwIHwgaWRlbnRpZmllcyB0aGUgY29sdW1ucyB0aGF0IHdpbGwgYmUgcGl2b3RlZCBmcm9tIHdpZGUgdG8gbG9uZyB8CnwgZ3JvdXBfYnkoKSB8IGhvbGRzIHVuaXF1ZSBjb21iaW5hdGlvbnMgb2Ygd2hhdGV2ZXIgY29sdW1uIG5hbWVzIHlvdSBwdXQgaW4gYGdyb3VwX2J5KClgIGZpeGVkIHdoaWxlIGl0IHBpdm90cyB0aGUgb3RoZXIgY29sdW1ucyB8CgoKSSBzdGlsbCBuZWVkIHRvIGNsZWFuIHVwIHRoZSB2YXJpYWJsZSBjb2x1bW4gc28gdGhhdCBpdCBvbmx5IHNob3dzIHRoZSBmb3VyLWRpZ2l0IHllYXIgcmF0aGVyIHRoYW4gUE9QRVNUSU1BVEUyMDEwLCBhbmQgc28gb24uIExldCB1cyBkbyB0aGlzIG5leHQuIAoKYGBge3IgdGlkeTA5fQpjYnNhMDEubG9uZyAlPiUKICBzZXBhcmF0ZSgKICAgIGNvbCA9IHZhcmlhYmxlLAogICAgaW50byA9IGMoInRvZGlzY2FyZCIsICJ0b3llYXIiKSwKICAgIHNlcCA9IDExLAogICAgcmVtb3ZlID0gVFJVRSkgLT4gY2JzYTAxLmxvbmcyCgpjYnNhMDEubG9uZzIgJT4lCiAgbXV0YXRlKFlFQVIgPSBhcy5udW1lcmljKHRveWVhcikpICU+JQogICAgc2VsZWN0KGMoTkFNRSwgWUVBUiwgUE9QVUxBVElPTikpIC0+IGNic2EwMS5sb25nMwoKY2JzYTAxLmxvbmczICU+JQogIGhlYWQoKQpgYGAKCiMjIyBMb25nLXRvLXdpZGUgCgpOb3csIGxldCB1cyBzYXkgdGhlIGRhdGEtc2V0IHdhcyBhIGRpZmZlcmVudCBvbmUsIHBlcmhhcHMgdGhlIG9uZSBzaG93biBiZWxvdy4gVGhpcyBkYXRhLXNldCBjb21lcyBmcm9tIHRoZSAyMDE3IEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgYW5kIGFsb25nIHdpdGggc3RhdGUgRklQUyBjb2RlcyAoYGdlb2lkYCkgYW5kIHN0YXRlIG5hbWUgKGBOQU1FYCkgaXQgY29udGFpbnMgZGF0YSBvbiBgaW5jb21lYCA9IG1lZGlhbiB5ZWFybHkgaW5jb21lLCBgcmVudGAgPSBtZWRpYW4gbW9udGhseSByZW50LCBhbmQgYG1vZWAgPSB0aGUgbWFyZ2luIG9mIGVycm9yIGF0IHRoZSA5MCUgY29uZmlkZW5jZSBsZXZlbC4KCmBgYHtyIHRpZHkxMH0KdXNfcmVudF9pbmNvbWUgJT4lCiAgaGVhZCgpCmBgYAoKTm90aWNlIGhlcmUgdGhlIHNldHVwIGxvb2tzIHdlaXJkIGJlY2F1c2UgdHdvIGRpZmZlcmVudCB2YXJpYWJsZXMgaGF2ZSBiZWVuIGNvbWJpbmVkIGluIGEgc2luZ2xlIGNvbHVtbi4gSW5zdGVhZCwgdGhlIGRhdGEtc2V0IHNob3VsZCBoYXZlIGJlZW4gc2V0dXAgYXMgZm9sbG93czoKCnwgR0VPSUQgfCBOQU1FICAgIHwgaW5jb21lIHwgcmVudCB8IG1vZV9pbmNvbWUgfCBtb2VfcmVudCB8CnwgOi0tICAgfCA6LS0gICAgIHwgLS06ICAgIHwgLS06ICB8IC0tOiAgICAgICAgfCAtLTogICAgICB8CnwgMDEgICAgfCBBbGFiYW1hIHwgMjQ0NzYgIHwgNzQ3ICB8IDEzNiAgICAgICAgfCAzICAgICAgICB8CnwgMDIgICAgfCBBbGFza2EgIHwgMzI5NDAgIHwgMTIwMCB8IDUwOCAgICAgICAgfCAxMyAgICAgICB8CnwgMDMgICAgfCBBcml6b25hIHwgMjc1MTcgIHwgOTcyICB8IDE0OCAgICAgICAgfCA0ICAgICAgICB8CnwgLi4uICAgfCAuLi4gICAgIHwgLi4uICAgIHwgLi4uICB8IC4uLiAgICAgICAgfCAuLi4gICAgICB8CgoKV2VsbCwgdGhpcyBjYW4gYmUgZG9uZSBhcyB3ZWxsLCB3aXRoIHRoZSBgcGl2b3Rfd2lkZXIoKWAgZnVuY3Rpb24gdGhhdCB0YWtlcyBmcm9tIHRoZSAibG9uZyIgZm9ybWF0IHRvIHRoZSAid2lkZSIgZm9ybWF0LiAKCmBgYHtyIHRpZHkxMX0KdXNfcmVudF9pbmNvbWUgJT4lCiAgZ3JvdXBfYnkoR0VPSUQsIE5BTUUpICU+JQogIHBpdm90X3dpZGVyKAogICAgbmFtZXNfZnJvbSA9IHZhcmlhYmxlLAogICAgdmFsdWVzX2Zyb20gPSBjKGVzdGltYXRlLCBtb2UpCiAgKSAtPiB1c3JpLndpZGUKCnVzcmkud2lkZSAlPiUKICBoZWFkKCkKYGBgCgp8IGNvZGUgfCB3aGF0IGl0IGRvZXMgLi4uIHwKfCA6LS0gfCA6LS0gfAp8IG5hbWVzX2Zyb20gPSB8IGlkZW50aWZpZXMgdGhlIGNvbHVtbiBmcm9tIHdoaWNoIHVuaXF1ZSB2YWx1ZXMgd2lsbCBiZSB0YWtlbiB0byBjcmVhdGUgdGhlIG5hbWVzIG9mIHRoZSBuZXcgY29sdW1ucyB0aGF0IHdpbGwgcmVzdWx0IHwKfCB2YWx1ZXNfZnJvbSA9IHwgaWRlbnRpZmllcyB0aGUgY29sdW1uKHMpIGZyb20gd2hpY2ggdGhlIHZhbHVlcyBzaG91bGQgYmUgYXNzaWduZWQgdG8gdGhlIG5ldyBjb2x1bW5zIHRoYXQgd2lsbCByZXN1bHQgfAp8IGdyb3VwX2J5KCkgfCBob2xkcyB1bmlxdWUgdmFsdWUgY29tYmluYXRpb25zIG9mIHdoYXRldmVyIGNvbHVtbiBuYW1lcyB5b3UgcHV0IGluIGBncm91cF9ieSgpYCBmaXhlZCB3aGlsZSBpdCBwaXZvdHMgdGhlIHJvd3MgdG8gbmV3IGNvbHVtbnMgfAoKCiMjIyBgciBmb250YXdlc29tZTo6ZmEoImZpcmUiLCBmaWxsID0gImRhcmtyZWQiKWAgVGhlIGV4YW1wbGUgdGhhdCBmb2xsb3dzIGlzIGEgdHJpY2t5IG9uZSBzbyBiZSBjYXJlZnVsISEgey19IAoKSGVyZSBpcyBhbm90aGVyIGV4YW1wbGUsIHRoaXMgdGltZSB3aXRoIHRoZSBgY2JzYWAgZGF0YSBidXQgc2hvd2luZyB5b3UgaG93IHdlIG1pZ2h0IHVzZSBhIGNvbWJpbmF0aW9uIG9mIGBwaXZvdF9sb25nZXIoKWAgYW5kIGBwaXZvdF93aWRlcigpYCAKCmBgYHtyIHRpZHkxMn0KY2JzYSAlPiUKICBzZWxlY3QoMzo1LCA4Ojg4KSAlPiUKICBncm91cF9ieShOQU1FKSAlPiUKICBwaXZvdF9sb25nZXIoCiAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsCiAgICB2YWx1ZXNfdG8gPSAiZXN0aW1hdGUiLAogICAgNDo4NAogICAgKSAtPiBjYnNhLjAxCgpjYnNhLjAxICU+JQogIGhlYWQoKQpgYGAKCk5vdyBJIHdpbGwgY2xlYW4gdXAgdGhlIGNvbnRlbnRzIG9mIGNic2EuMDEgc28gdGhhdCB5ZWFyIGlzIGEgc2VwYXJhdGUgY29sdW1uLgoKYGBge3IgdGlkeTEzfQpjYnNhLjAxICU+JQogIHNlcGFyYXRlKAogICAgY29sID0gInZhcmlhYmxlIiwKICAgIGludG8gPSBjKCJ2YXJ0eXBlIiwgInllYXIiKSwKICAgIHNlcCA9ICIoPz1bWzpkaWdpdDpdXSkiLAogICAgZXh0cmEgPSAibWVyZ2UiLAogICAgcmVtb3ZlID0gRkFMU0UKICApIC0+IGNic2EuMDIKCmNic2EuMDIgJT4lCiAgaGVhZCgpCmBgYAoKTm93IHRoZSBmaW5hbCBmbGlwIHRvIHdpZGUgZm9ybWF0IC4uLgoKYGBge3IgdGlkeTE0fQpjYnNhLjAyICU+JQogIHNlbGVjdChjKDIsIDU6NykpICU+JQogIGdyb3VwX2J5KE5BTUUsIHllYXIpICU+JQogIHBpdm90X3dpZGVyKAogICAgbmFtZXNfZnJvbSA9ICJ2YXJ0eXBlIiwKICAgIHZhbHVlc19mcm9tID0gImVzdGltYXRlIgogICkgLT4gY2JzYS4wMwoKY2JzYS4wMyAlPiUKICBnbGltcHNlKCkKYGBgCgoKIyBQcmFjdGljZSBFeGVyY2lzZXMKCiMjIEV4ZXJjaXNlIDEgClVzaW5nIHRoZSBgY21oZmxpZ2h0c2AgZGF0YSBmcm9tIGxhc3Qgd2VlaywgY3JlYXRlIGEgY29sdW1uIHRoYXQgdW5pdGVzIHRoZSB0aHJlZSBjb2x1bW5zIGBZZWFyYCwgYE1vbnRoYCwgYW5kIGBEYXlvZk1vbnRoYCBpbnRvIGEgc2luZ2xlIGNvbHVtbiB0aGF0IHdlIHdpbGwgbmFtZSBgZGF0ZV9vZl9mbGlnaHRgLiBUaGlzIGNvbHVtbiBzaG91bGQgc2VwYXJhdGUgdGhlIHRocmVlIGZpZWxkcyBieSAiLSIuIAoKPiBIaW50OiBZb3Ugd2lsbCBoYXZlIHRvIHVzZSBgbG9hZCguLi4pYCB3aXRoIGBoZXJlKC4uLilgLgoKIyMgRXhlcmNpc2UgMgpTdGlja2luZyB3aXRoIGBjbWhmbGlnaHRzYCwgc2VwYXJhdGUgYE9yaWdpbkNpdHlOYW1lYCBpbnRvIHR3byBuZXcgY29sdW1ucywgYG9yaWdpbl9jaXR5YCBhbmQgYG9yaWdpbl9zdGF0ZWAuIERvIHRoZSBzYW1lIGZvciBgRGVzdENpdHluYW1lYCwgY2FsbGluZyB0aGUgbmV3IGNvbHVtbnMgYGRlc3RpbmF0aW9uX2NpdHlgIGFuZCBgZGVzdGluYXRpb25fc3RhdGVgLCByZXNwZWN0aXZlbHkuIEJvdGggY2l0eSBjb2x1bW5zIHNob3VsZCBvbmx5IGRpc3BsYXkgdGhlIG5hbWUgb2YgdGhlIGNpdHksIHdoaWxlIGJvdGggc3RhdGUgY29sdW1ucyBzaG91bGQgb25seSBkaXNwbGF5IHRoZSBhYmJyZXZpYXRlZCBzdGF0ZSBuYW1lIChmb3IgZXhhbXBsZSwgIkNBIiwgIk9IIiwgZXRjLikgIAoKIyMgRXhlcmNpc2UgMwpUaWR5IHRoZSBgd2VhdGhlcmAgZGF0YSBzdWNoIHRoYXQgdGhlIHJlc3VsdGluZyBkYXRhLXNldCwgY2FsbGVkIGB3ZGZgLCBoYXMgdGhlIGBkYXlzYCAodGhlIGQxLWQzMSBjb2x1bW5zKSBhcyByb3dzIGFuZCBgVE1JTmAgYW5kIGBUTUFYYCBhcyBjb2x1bW5zLiBUaGUgZW5kIHJlc3VsdCBzaG91bGQgYmUgYXMgc2hvd24gYmVsb3c6CgpgYGB7ciB0aXlleDAxfQpyZWFkLmRlbGltKAogZmlsZSA9ICJodHRwOi8vc3RhdDQwNS5oYWQuY28ubnovZGF0YS93ZWF0aGVyLnR4dCIsCiBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICkgLT4gd2VhdGhlcgpgYGAKCnwgaWQgICAgICAgICAgfCB5ZWFyICB8IG1vbnRoIHwgZGF5cyAgfCBUTUFYICB8IFRNSU4gIHwKfCA6LS0gICAgICAgICB8IDotLSAgIHwgOi0tICAgfCA6LS0gICB8IDotLSAgIHwgOi0tICAgfAp8IE1YMDAwMDE3MDA0IHwgMjAxMCAgfCAgIDEgfCBkMSAgIHwgICAgTkEgfCAgIE5BIHwKfCBNWDAwMDAxNzAwNCB8IDIwMTAgIHwgICAxIHwgZDIgICB8ICAgIE5BICB8ICBOQSB8CnwgTVgwMDAwMTcwMDQgfCAyMDEwICB8ICAgMSB8IGQzICAgfCAgICAgTkEgfCAgIE5BIHwKfCBNWDAwMDAxNzAwNCB8IDIwMTAgIHwgICAxIHwgZDQgICB8ICAgIE5BICB8ICBOQSB8CnwgTVgwMDAwMTcwMDQgfCAyMDEwICB8ICAgMSB8IGQ1ICAgfCAgICAgTkEgfCAgIE5BIHwKfCBNWDAwMDAxNzAwNCB8IDIwMTAgIHwgICAxIHwgZDYgICB8ICAgICBOQSB8ICAgTkEgfAp8IE1YMDAwMDE3MDA0IHwgMjAxMCAgfCAgIDEgfCBkNyAgIHwgICAgIE5BIHwgICBOQSB8CnwgTVgwMDAwMTcwMDQgfCAyMDEwICB8ICAgMSB8IGQ4ICAgfCAgICBOQSAgfCAgTkEgfAp8IE1YMDAwMDE3MDA0IHwgMjAxMCAgfCAgIDEgfCBkOSAgIHwgICAgTkEgIHwgIE5BIHwKfCBNWDAwMDAxNzAwNCB8IDIwMTAgIHwgICAxIHwgZDEwICB8ICAgICBOQSB8ICAgTkEgfAoKCg==