This document addresses one solution to the data-wrangling exercise described in Exercise3.pdf. For the accompanying R Notebook with executable code, download the Rmd file from the pulldown “Code” menu on the upper right.

The input to the exercise is the raw data on 2016 Centre County, PA, precinct level votes found in “CentreCountyPrecinctResults2016GeneralElection.txt”, which were retrieved from http://centrecountypa.gov/Index.aspx?NID=802. (Update - 2020: This link is no longer accurate, and the original file does not appear to be available there any more. You can, however, find it on the Internet Archive’s Wayback Machine, for example, here: http://web.archive.org/web/20181106161514/centrecountypa.gov/index.aspx?NID=802. The file is no longer available due to the website being “updated” to provide the data in even less accessible form (pdf).)

The exercise asks you to extract the data on votes cast by precinct in statewide elections, and process them into a new table with precinct level data on total votes, Democratic share of two-party vote, and ballot rolloff from presidential votes to votes in other statewide races.

This solution uses the R tidyverse. For alternative solutions see https://burtmonroe.github.io/SoDA501/Exercises/Exercise3-2018.

Solving with the tidyverse (mainly dplyr)

Let’s load the tidyverse, read in the data, and look at the first 50 rows:

library(tidyverse)
── Attaching packages ───────────
✔ ggplot2 3.2.1     ✔ purrr   0.3.3
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   1.0.0     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ────────────────────
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
rawdata <- read_csv("CentreCountyPrecinctResults2016GeneralElection.txt") # 3520 rows, 16 columns
Parsed with column specification:
cols(
  County = col_character(),
  ElecYear = col_double(),
  Election = col_character(),
  ElecDate = col_character(),
  PrecNo = col_character(),
  PrecName = col_character(),
  Party = col_character(),
  Contest = col_character(),
  Descr = col_logical(),
  VoteFor = col_character(),
  Candidate = col_character(),
  Posn = col_double(),
  Count = col_double(),
  PctCnt = col_double(),
  Total = col_logical(),
  PctTot = col_logical()
)
dim(rawdata)
[1] 3520   16
head(rawdata, n=50)

These data have, roughly, 39 rows for each of 91 precincts in Centre County, PA, identified by the variables PrecNo (which ranges from 0001 to 0091) and PrecName (which ranges from 01 BELLEFONTE NORTH to 91 FERGUSON NORTH CENTRAL). Each precinct starts with three rows for summary information, identified in the Contest variable by values REGISTERED VOTERS - TOTAL, BALLOTS CAST - TOTAL, VOTER TURNOUT - TOTAL, followed by five rows with information on straight ticket voting for each of five parties (which can be ignored for our purposes here). From there, each row contains information for a single candidate in a particular contest. We care specifically about the statewide contests that are held in every precinct here: President (Contest=="PRESIDENTIAL ELECTORS"), US Senator (Contest=="UNITED STATES SENATOR"), Attorney General (Contest=="ATTORNEY GENERAL"), Auditor General (Contest=="AUDITOR GENERAL"), and Treasurer (Contest=="STATE TREASURER"). All of our calculations are based on the number in the Count variable.

Solve in three pieces

This solution creates three dataframes (tables) that it joins together in the last step: the total votes, the two-party shares, and the rolloffs. Each of these dataframes should ultimately have 91 rows.

Table 1: Total votes by precinct

The total vote numbers are already in the data, we just need to filter rows down to just the 91 we need (the ones that list "BALLOTS CAST - TOTAL" in the Contest column, and then select the columns we need. We’ll keep the precinct number, the precinct name, and the total votes.

Tot <- rawdata %>% 
  filter(Contest=="BALLOTS CAST - TOTAL") %>% # filter to the rows with total votes
  select(PrecNo,PrecName,Tot=Count) #select the id columns and the counts. Rename the counts to Tot
dim(Tot) # 91 rows, 3 columns
[1] 91  3
head(Tot)

Table 2: Rolloffs in down-ballot races

Now we’ll calculate the rolloff data. Ultimately, the output is 91 rows, 1 row per precinct, with four columns containing the rolloff in the non-presidential races in that precinct. To get there, we need an intermediate table of 91 rows with the total votes per statewide contest, including President, by precinct. So the trickiest step is the one where we `spread’ the data from its “long” format to a “wide” format with these variables.

The following code is presented as one long pipeline with one input and one output. The identical code is repeated below in this notebook, with output “glimpsed” for each intermediate step.

Rolloffs <- rawdata %>%                  # Start with the raw data
  select(PrecNo, Contest, Count) %>%     # For rolloff we need the data by precinct & contest
  mutate(Con=substr(Contest,1,3)) %>%    # For cleanliness, create an abbreviated contest variable
  filter(Con %in% c("PRE","UNI","ATT","AUD","STA")) %>%  # Pick the rows with just the statewide contests
  group_by(PrecNo,Con) %>%               # Group into Precinct-Contest units
  summarise(ConTot=sum(Count)) %>%       # Calculate total vote by Precinct-Contest
  spread(Con, ConTot) %>%               # Spread the data by Contest
  mutate(ROSen=100*(1-UNI/PRE),         # Rolloff for Senator (UNI TED STATES SENATOR)
         ROAtt=100*(1-ATT/PRE),         # Rolloff for Attorney General (ATT ORNEY GENERAL)
         ROAud=100*(1-AUD/PRE),         # Rolloff for Auditor General (AUD ITOR GENERAL)
         ROTre=100*(1-STA/PRE)) %>%     # Rolloff for Treasurer (STA TE TREASURER)
  select(PrecNo,ROSen,ROAtt,ROAud,ROTre) # Keep just the Precinct Number and Rolloff variables
dim(Rolloffs) # 91 rows, 5 columns
[1] 91  5
head(Rolloffs)

NB. 2020 Update

Since the original posting of this exercise / solution, the spread and gather commands have been superceded by pivot_wider and pivot_longer. The code above still works, but the preferred formulation would now be:

Rolloffs <- rawdata %>%                  # Start with the raw data
  select(PrecNo, Contest, Count) %>%     # For rolloff we need the data by precinct & contest
  mutate(Con=substr(Contest,1,3)) %>%    # For cleanliness, create an abbreviated contest variable
  filter(Con %in% c("PRE","UNI","ATT","AUD","STA")) %>%  # Pick the rows with just the statewide contests
  group_by(PrecNo,Con) %>%               # Group into Precinct-Contest units
  summarise(ConTot=sum(Count)) %>%       # Calculate total vote by Precinct-Contest
  pivot_wider(names_from = Con, values_from = ConTot) %>% 
  # replaces
  # spread(Con, ConTot) %>%           # Spread the data by Contest
  mutate(ROSen=100*(1-UNI/PRE),         # Rolloff for Senator (UNI TED STATES SENATOR)
         ROAtt=100*(1-ATT/PRE),         # Rolloff for Attorney General (ATT ORNEY GENERAL)
         ROAud=100*(1-AUD/PRE),         # Rolloff for Auditor General (AUD ITOR GENERAL)
         ROTre=100*(1-STA/PRE)) %>%     # Rolloff for Treasurer (STA TE TREASURER)
  select(PrecNo,ROSen,ROAtt,ROAud,ROTre) # Keep just the Precinct Number and Rolloff variables
dim(Rolloffs) # 91 rows, 5 columns
[1] 91  5
head(Rolloffs)

Table 3: Democratic share of two-party vote

This is similar to rolloff in that we need to calculate two intermediate quantities along the way: total vote for Republican and Democrat in each race, by precinct. With rolloff, we needed to spread the data to create contest data by precinct. With party shares, we need to spread the data to create contest-party data by precinct, so we need to create a contest-party indicator variable to act as a “key” for spreading. (2020 note: again, spread should be replaced by pivot_wider.)

DemTwoPartyVotes <- rawdata %>%            # Start with the raw data
  select(PrecNo,Party,Contest,Count) %>%   # All we need is Precinct, the "Contest", and the "Count"
  mutate(Con=substr(Contest,1,3),Pty=substr(Party,1,3)) %>% # Create abbreviated Party & Contest variables
  select(PrecNo,Pty,Con,Count) %>%         # Get rid of the unabbreviated variables
  filter(Con %in% c("PRE","UNI","ATT","AUD","STA")) %>%  # Pick just the rows with elections of interest
  filter(Pty %in% c('DEM','REP')) %>%      # Pick just Democratic and Republican candidates
  mutate(ConPty = paste(Con,Pty,sep="")) %>%  #### CREATE CONTEST-PARTY TO ACT AS KEY FOR SPREAD
  select(PrecNo,ConPty,Count) %>%          # Get rid of columns we don't need.
  pivot_wider(names_from = ConPty, values_from = Count) %>%
  # replaces
  #  spread(ConPty,Count) %>%         ##### SPREAD THE DATA BY THE CONTEST-PARTY KEY
  mutate(D2Pre=100*(PREDEM/(PREDEM+PREREP)),     # D2Pre = Dem share of 2 party vote for President
         D2Sen=100*(UNIDEM/(UNIDEM+UNIREP)),     # D2Sen = Dem share of 2 party vote for US Senator
         D2Att=100*(ATTDEM/(ATTDEM+ATTREP)),     # D2Att = Dem share of 2 party vote for Attorney Genl
         D2Aud=100*(AUDDEM/(AUDDEM+AUDREP)),     # D2Aud = Dem share of 2 party vote for Auditor Genl
         D2Tre=100*(STADEM/(STADEM+STAREP))) %>% # D2Tre = Dem share of 2 party vote for St Treasurer
  select(PrecNo,D2Pre,D2Sen,D2Att,D2Aud,D2Tre)  # Get rid of columns we don't need
dim(DemTwoPartyVotes) # 91 rows, 6 columns
[1] 91  6
head(DemTwoPartyVotes)

Final output table: Merged Data

Now we merge the tables and format the Precinct Number and Name as requested in the Exercise. The main verb here is *_join. Any of left_join, right_join, or inner_join will work in this case to match data from each table by the only matching variable, PrecNo. (These verbs differ in how they treat rows in which the join variables are missing or duplicated in one or the other table, but in this case all three tables have exactly 91 rows with unique PrecNo, so the effect is identical.)

Ex3Data <- Tot %>%                            # Start with the Tot data_frame
  left_join(DemTwoPartyVotes) %>%             # Merge the DemTwoParty table (on shared PrecNo)
  left_join(Rolloffs) %>%                     # Merge the Rolloff table (on shared PrecNo)
  mutate(PrecNo=as.integer(PrecNo)) %>%       # Turn PrecNo into a number
  mutate(PrecName=str_sub(PrecName,start=4L)) # Strip the redundant number off of the Precinct names
Joining, by = "PrecNo"Joining, by = "PrecNo"
Ex3Data

Solution repeated with intermediate steps displayed

Here we repeat the exact steps from above, but with a “glimpse” statement at each step to see all of the intermediate results.

Tot <- rawdata %>% 
  filter(Contest=="BALLOTS CAST - TOTAL") %T>% glimpse()  %>% # filter to the rows with total votes
  select(PrecNo,PrecName,Tot=Count) #select the id columns and the counts. Rename the counts to Tot
Observations: 91
Variables: 16
$ County    <chr> "CENTRE COUN…
$ ElecYear  <dbl> 2016, 2016, …
$ Election  <chr> "GENERAL ELE…
$ ElecDate  <chr> "NOVEMBER 8 …
$ PrecNo    <chr> "0001", "000…
$ PrecName  <chr> "01 BELLEFON…
$ Party     <chr> NA, NA, NA, …
$ Contest   <chr> "BALLOTS CAS…
$ Descr     <lgl> NA, NA, NA, …
$ VoteFor   <chr> NA, NA, NA, …
$ Candidate <chr> NA, NA, NA, …
$ Posn      <dbl> 11, 96, 181,…
$ Count     <dbl> 391, 687, 46…
$ PctCnt    <dbl> NA, NA, NA, …
$ Total     <lgl> NA, NA, NA, …
$ PctTot    <lgl> NA, NA, NA, …
dim(Tot) # 91 rows x 3
[1] 91  3
head(Tot)
Rolloffs <- rawdata %>%                  # Start with the raw data
  select(PrecNo, Contest, Count) %T>% glimpse()  %>%  # Pick data by precinct & contest
  mutate(Con=substr(Contest,1,3)) %T>% glimpse()  %>% # For cleanliness, create abbrev contest variable
  filter(Con %in% c("PRE","UNI","ATT","AUD","STA")) %T>% glimpse() %>% # Pick rows w statewide contests
  group_by(PrecNo,Con) %T>% glimpse() %>%               # Group into Precinct-Contest units
  summarise(ConTot=sum(Count)) %T>% glimpse() %>%       # Calculate total vote by Precinct-Contest
  spread(Con, ConTot) %T>% glimpse() %>%               # Spread the data by Contest
  mutate(ROSen=100*(1-UNI/PRE),         # Rolloff for Senator (UNI TED STATES SENATOR)
         ROAtt=100*(1-ATT/PRE),         # Rolloff for Attorney General (ATT ORNEY GENERAL)
         ROAud=100*(1-AUD/PRE),         # Rolloff for Auditor General (AUD ITOR GENERAL)
         ROTre=100*(1-STA/PRE)) %T>% glimpse()  %>%     # Rolloff for Treasurer (STA TE TREASURER)
  select(PrecNo,ROSen,ROAtt,ROAud,ROTre) # Keep just the Precinct Number and Rolloff variables
Observations: 3,520
Variables: 3
$ PrecNo  <chr> "0001", "0001"…
$ Contest <chr> "REGISTERED VO…
$ Count   <dbl> 507, 391, NA, …
Observations: 3,520
Variables: 4
$ PrecNo  <chr> "0001", "0001"…
$ Contest <chr> "REGISTERED VO…
$ Count   <dbl> 507, 391, NA, …
$ Con     <chr> "REG", "BAL", …
Observations: 2,093
Variables: 4
$ PrecNo  <chr> "0001", "0001"…
$ Contest <chr> "PRESIDENTIAL …
$ Count   <dbl> 188, 174, 2, 3…
$ Con     <chr> "PRE", "PRE", …
Observations: 2,093
Variables: 4
Groups: PrecNo, Con [455]
$ PrecNo  <chr> "0001", "0001"…
$ Contest <chr> "PRESIDENTIAL …
$ Count   <dbl> 188, 174, 2, 3…
$ Con     <chr> "PRE", "PRE", …
Observations: 455
Variables: 3
Groups: PrecNo [91]
$ PrecNo <chr> "0001", "0001",…
$ Con    <chr> "ATT", "AUD", "…
$ ConTot <dbl> 382, 364, 391, …
Observations: 91
Variables: 6
Groups: PrecNo [91]
$ PrecNo <chr> "0001", "0002",…
$ ATT    <dbl> 382, 668, 451, …
$ AUD    <dbl> 364, 657, 448, …
$ PRE    <dbl> 391, 679, 457, …
$ STA    <dbl> 368, 655, 447, …
$ UNI    <dbl> 386, 675, 452, …
Observations: 91
Variables: 10
Groups: PrecNo [91]
$ PrecNo <chr> "0001", "0002",…
$ ATT    <dbl> 382, 668, 451, …
$ AUD    <dbl> 364, 657, 448, …
$ PRE    <dbl> 391, 679, 457, …
$ STA    <dbl> 368, 655, 447, …
$ UNI    <dbl> 386, 675, 452, …
$ ROSen  <dbl> 1.2787724, 0.58…
$ ROAtt  <dbl> 2.3017903, 1.62…
$ ROAud  <dbl> 6.905371, 3.240…
$ ROTre  <dbl> 5.882353, 3.534…
Rolloffs
DemTwoPartyVotes <- rawdata %>%            # Start with the raw data
  select(PrecNo,Party,Contest,Count) %T>% glimpse() %>%   # All we need is Precinct, Contest, & "Count"
  mutate(Con=substr(Contest,1,3),Pty=substr(Party,1,3)) %T>% glimpse() %>% # Abbreviated Party & Contest
  select(PrecNo,Pty,Con,Count) %T>% glimpse() %>%         # Get rid of the unabbreviated variables
  filter(Con %in% c("PRE","UNI","ATT","AUD","STA")) %T>% glimpse() %>%  # Pick rows w elections
  filter(Pty %in% c('DEM','REP')) %T>% glimpse() %>%  # Pick just Democratic and Republican candidates
  mutate(ConPty = paste(Con,Pty,sep="")) %T>% glimpse() %>%  #### CREATE CONTEST-PARTY AS KEY FOR SPREAD
  select(PrecNo,ConPty,Count) %T>% glimpse() %>%          # Get rid of columns we don't need.
  spread(ConPty,Count) %T>% glimpse() %>%                 ##### SPREAD THE DATA BY THE CONTEST-PARTY KEY
  mutate(D2Pre=100*(PREDEM/(PREDEM+PREREP)),     # D2Pre = Dem share of 2 party vote for President
         D2Sen=100*(UNIDEM/(UNIDEM+UNIREP)),     # D2Sen = Dem share of 2 party vote for US Senator
         D2Att=100*(ATTDEM/(ATTDEM+ATTREP)),     # D2Att = Dem share of 2 party vote for Attorney Genl
         D2Aud=100*(AUDDEM/(AUDDEM+AUDREP)),     # D2Aud = Dem share of 2 party vote for Auditor Genl
         D2Tre=100*(STADEM/(STADEM+STAREP))) %T>% glimpse() %>% # D2Tre = Dem share, 2 pty vote St Treasurer
  select(PrecNo,D2Pre,D2Sen,D2Att,D2Aud,D2Tre)  # Get rid of columns we don't need
Observations: 3,520
Variables: 4
$ PrecNo  <chr> "0001", "0001"…
$ Party   <chr> NA, NA, NA, "D…
$ Contest <chr> "REGISTERED VO…
$ Count   <dbl> 507, 391, NA, …
Observations: 3,520
Variables: 6
$ PrecNo  <chr> "0001", "0001"…
$ Party   <chr> NA, NA, NA, "D…
$ Contest <chr> "REGISTERED VO…
$ Count   <dbl> 507, 391, NA, …
$ Con     <chr> "REG", "BAL", …
$ Pty     <chr> NA, NA, NA, "D…
Observations: 3,520
Variables: 4
$ PrecNo <chr> "0001", "0001",…
$ Pty    <chr> NA, NA, NA, "DE…
$ Con    <chr> "REG", "BAL", "…
$ Count  <dbl> 507, 391, NA, 4…
Observations: 2,093
Variables: 4
$ PrecNo <chr> "0001", "0001",…
$ Pty    <chr> "DEM", "REP", "…
$ Con    <chr> "PRE", "PRE", "…
$ Count  <dbl> 188, 174, 2, 3,…
Observations: 910
Variables: 4
$ PrecNo <chr> "0001", "0001",…
$ Pty    <chr> "DEM", "REP", "…
$ Con    <chr> "PRE", "PRE", "…
$ Count  <dbl> 188, 174, 169, …
Observations: 910
Variables: 5
$ PrecNo <chr> "0001", "0001",…
$ Pty    <chr> "DEM", "REP", "…
$ Con    <chr> "PRE", "PRE", "…
$ Count  <dbl> 188, 174, 169, …
$ ConPty <chr> "PREDEM", "PRER…
Observations: 910
Variables: 3
$ PrecNo <chr> "0001", "0001",…
$ ConPty <chr> "PREDEM", "PRER…
$ Count  <dbl> 188, 174, 169, …
Observations: 91
Variables: 11
$ PrecNo <chr> "0001", "0002",…
$ ATTDEM <dbl> 194, 265, 258, …
$ ATTREP <dbl> 187, 401, 192, …
$ AUDDEM <dbl> 170, 264, 238, …
$ AUDREP <dbl> 163, 354, 171, …
$ PREDEM <dbl> 188, 259, 230, …
$ PREREP <dbl> 174, 385, 193, …
$ STADEM <dbl> 177, 252, 229, …
$ STAREP <dbl> 164, 344, 169, …
$ UNIDEM <dbl> 169, 251, 229, …
$ UNIREP <dbl> 185, 386, 191, …
Observations: 91
Variables: 16
$ PrecNo <chr> "0001", "0002",…
$ ATTDEM <dbl> 194, 265, 258, …
$ ATTREP <dbl> 187, 401, 192, …
$ AUDDEM <dbl> 170, 264, 238, …
$ AUDREP <dbl> 163, 354, 171, …
$ PREDEM <dbl> 188, 259, 230, …
$ PREREP <dbl> 174, 385, 193, …
$ STADEM <dbl> 177, 252, 229, …
$ STAREP <dbl> 164, 344, 169, …
$ UNIDEM <dbl> 169, 251, 229, …
$ UNIREP <dbl> 185, 386, 191, …
$ D2Pre  <dbl> 51.93370, 40.21…
$ D2Sen  <dbl> 47.74011, 39.40…
$ D2Att  <dbl> 50.91864, 39.78…
$ D2Aud  <dbl> 51.05105, 42.71…
$ D2Tre  <dbl> 51.90616, 42.28…
DemTwoPartyVotes
Ex3Data <- Tot %>%                                # Start with the Tot data_frame
  left_join(DemTwoPartyVotes) %T>% glimpse() %>%    # Merge the DemTwoParty table (on shared PrecNo)
  left_join(Rolloffs) %T>% glimpse() %>%            # Merge the Rolloff table (on shared PrecNo)
  mutate(PrecNo=as.integer(PrecNo)) %T>% glimpse() %>%       # Turn PrecNo into a number
  mutate(PrecName=str_sub(PrecName,start=4L)) # Strip the redundant number off of the Precinct names
Joining, by = "PrecNo"
Observations: 91
Variables: 8
$ PrecNo   <chr> "0001", "0002…
$ PrecName <chr> "01 BELLEFONT…
$ Tot      <dbl> 391, 687, 461…
$ D2Pre    <dbl> 51.93370, 40.…
$ D2Sen    <dbl> 47.74011, 39.…
$ D2Att    <dbl> 50.91864, 39.…
$ D2Aud    <dbl> 51.05105, 42.…
$ D2Tre    <dbl> 51.90616, 42.…
Joining, by = "PrecNo"
Observations: 91
Variables: 12
$ PrecNo   <chr> "0001", "0002…
$ PrecName <chr> "01 BELLEFONT…
$ Tot      <dbl> 391, 687, 461…
$ D2Pre    <dbl> 51.93370, 40.…
$ D2Sen    <dbl> 47.74011, 39.…
$ D2Att    <dbl> 50.91864, 39.…
$ D2Aud    <dbl> 51.05105, 42.…
$ D2Tre    <dbl> 51.90616, 42.…
$ ROSen    <dbl> 1.2787724, 0.…
$ ROAtt    <dbl> 2.3017903, 1.…
$ ROAud    <dbl> 6.905371, 3.2…
$ ROTre    <dbl> 5.882353, 3.5…
Observations: 91
Variables: 12
$ PrecNo   <int> 1, 2, 3, 4, 5…
$ PrecName <chr> "01 BELLEFONT…
$ Tot      <dbl> 391, 687, 461…
$ D2Pre    <dbl> 51.93370, 40.…
$ D2Sen    <dbl> 47.74011, 39.…
$ D2Att    <dbl> 50.91864, 39.…
$ D2Aud    <dbl> 51.05105, 42.…
$ D2Tre    <dbl> 51.90616, 42.…
$ ROSen    <dbl> 1.2787724, 0.…
$ ROAtt    <dbl> 2.3017903, 1.…
$ ROAud    <dbl> 6.905371, 3.2…
$ ROTre    <dbl> 5.882353, 3.5…
Ex3Data # 91 rows, 12 columns
LS0tCnRpdGxlOiAiU29EQSA1MDEsIEV4ZXJjaXNlIDMgKDIwMTgpIC0gYHRpZHl2ZXJzZWAgU29sdXRpb24iCmF1dGhvcjogIkJ1cnQgTC4gTW9ucm9lIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgdGhlbWU6IHVuaXRlZAogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwotLS0KClRoaXMgZG9jdW1lbnQgYWRkcmVzc2VzIG9uZSBzb2x1dGlvbiB0byB0aGUgZGF0YS13cmFuZ2xpbmcgZXhlcmNpc2UgZGVzY3JpYmVkIGluIFtFeGVyY2lzZTMucGRmXShodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1NvREE1MDEvRXhlcmNpc2VzL0V4ZXJjaXNlMy0yMDE4L0V4ZXJjaXNlMy5wZGYpLiBGb3IgdGhlIGFjY29tcGFueWluZyBSIE5vdGVib29rIHdpdGggZXhlY3V0YWJsZSBjb2RlLCBkb3dubG9hZCB0aGUgUm1kIGZpbGUgZnJvbSB0aGUgcHVsbGRvd24gIkNvZGUiIG1lbnUgb24gdGhlIHVwcGVyIHJpZ2h0LiAgCgpUaGUgaW5wdXQgdG8gdGhlIGV4ZXJjaXNlIGlzIHRoZSByYXcgZGF0YSBvbiAyMDE2IENlbnRyZSBDb3VudHksIFBBLCBwcmVjaW5jdCBsZXZlbCB2b3RlcyBmb3VuZCBpbiBbIkNlbnRyZUNvdW50eVByZWNpbmN0UmVzdWx0czIwMTZHZW5lcmFsRWxlY3Rpb24udHh0Il0oaHR0cHM6Ly9idXJ0bW9ucm9lLmdpdGh1Yi5pby9Tb0RBNTAxL0V4ZXJjaXNlcy9FeGVyY2lzZTMtMjAxOC9DZW50cmVDb3VudHlQcmVjaW5jdFJlc3VsdHMyMDE2R2VuZXJhbEVsZWN0aW9uLnR4dCksIHdoaWNoIHdlcmUgcmV0cmlldmVkIGZyb20gW2h0dHA6Ly9jZW50cmVjb3VudHlwYS5nb3YvSW5kZXguYXNweD9OSUQ9ODAyXShodHRwOi8vY2VudHJlY291bnR5cGEuZ292L0luZGV4LmFzcHg/TklEPTgwMikuIChVcGRhdGUgLSAyMDIwOiBUaGlzIGxpbmsgaXMgbm8gbG9uZ2VyIGFjY3VyYXRlLCBhbmQgdGhlIG9yaWdpbmFsIGZpbGUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGF2YWlsYWJsZSB0aGVyZSBhbnkgbW9yZS4gWW91IGNhbiwgaG93ZXZlciwgZmluZCBpdCBvbiB0aGUgSW50ZXJuZXQgQXJjaGl2ZSdzIFdheWJhY2sgTWFjaGluZSwgZm9yIGV4YW1wbGUsIGhlcmU6IFtodHRwOi8vd2ViLmFyY2hpdmUub3JnL3dlYi8yMDE4MTEwNjE2MTUxNC9jZW50cmVjb3VudHlwYS5nb3YvaW5kZXguYXNweD9OSUQ9ODAyXShodHRwOi8vd2ViLmFyY2hpdmUub3JnL3dlYi8yMDE4MTEwNjE2MTUxNC9jZW50cmVjb3VudHlwYS5nb3YvaW5kZXguYXNweD9OSUQ9ODAyKS4gVGhlIGZpbGUgaXMgbm8gbG9uZ2VyIGF2YWlsYWJsZSBkdWUgdG8gdGhlIHdlYnNpdGUgYmVpbmcgInVwZGF0ZWQiIHRvIHByb3ZpZGUgdGhlIGRhdGEgaW4gZXZlbiBsZXNzIGFjY2Vzc2libGUgZm9ybSAocGRmKS4pCgpUaGUgZXhlcmNpc2UgYXNrcyB5b3UgdG8gZXh0cmFjdCB0aGUgZGF0YSBvbiB2b3RlcyBjYXN0IGJ5IHByZWNpbmN0IGluIHN0YXRld2lkZSBlbGVjdGlvbnMsIGFuZCBwcm9jZXNzIHRoZW0gaW50byBhIG5ldyB0YWJsZSB3aXRoIHByZWNpbmN0IGxldmVsIGRhdGEgb24gdG90YWwgdm90ZXMsIERlbW9jcmF0aWMgc2hhcmUgb2YgdHdvLXBhcnR5IHZvdGUsIGFuZCBiYWxsb3Qgcm9sbG9mZiBmcm9tIHByZXNpZGVudGlhbCB2b3RlcyB0byB2b3RlcyBpbiBvdGhlciBzdGF0ZXdpZGUgcmFjZXMuCgpUaGlzIHNvbHV0aW9uIHVzZXMgdGhlIFIgYHRpZHl2ZXJzZWAuIEZvciBhbHRlcm5hdGl2ZSBzb2x1dGlvbnMgc2VlIFtodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1NvREE1MDEvRXhlcmNpc2VzL0V4ZXJjaXNlMy0yMDE4XShodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1NvREE1MDEvRXhlcmNpc2VzL0V4ZXJjaXNlMy0yMDE4KS4KCiMgU29sdmluZyB3aXRoIHRoZSBgdGlkeXZlcnNlYCAobWFpbmx5IGBkcGx5cmApCgpMZXQncyBsb2FkIHRoZSB0aWR5dmVyc2UsIHJlYWQgaW4gdGhlIGRhdGEsIGFuZCBsb29rIGF0IHRoZSBmaXJzdCA1MCByb3dzOgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpyYXdkYXRhIDwtIHJlYWRfY3N2KCJDZW50cmVDb3VudHlQcmVjaW5jdFJlc3VsdHMyMDE2R2VuZXJhbEVsZWN0aW9uLnR4dCIpICMgMzUyMCByb3dzLCAxNiBjb2x1bW5zCmRpbShyYXdkYXRhKQpoZWFkKHJhd2RhdGEsIG49NTApCmBgYAoKVGhlc2UgZGF0YSBoYXZlLCByb3VnaGx5LCAzOSByb3dzIGZvciBlYWNoIG9mIDkxIHByZWNpbmN0cyBpbiBDZW50cmUgQ291bnR5LCBQQSwgaWRlbnRpZmllZCBieSB0aGUgdmFyaWFibGVzIGBQcmVjTm9gICh3aGljaCByYW5nZXMgZnJvbSBgMDAwMWAgdG8gYDAwOTFgKSBhbmQgYFByZWNOYW1lYCAod2hpY2ggcmFuZ2VzIGZyb20gYDAxIEJFTExFRk9OVEUgTk9SVEhgIHRvIGA5MSBGRVJHVVNPTiBOT1JUSCBDRU5UUkFMYCkuIEVhY2ggcHJlY2luY3Qgc3RhcnRzIHdpdGggdGhyZWUgcm93cyBmb3Igc3VtbWFyeSBpbmZvcm1hdGlvbiwgaWRlbnRpZmllZCBpbiB0aGUgYENvbnRlc3RgIHZhcmlhYmxlIGJ5IHZhbHVlcyBgUkVHSVNURVJFRCBWT1RFUlMgLSBUT1RBTGAsIGBCQUxMT1RTIENBU1QgLSBUT1RBTGAsIGBWT1RFUiBUVVJOT1VUIC0gVE9UQUxgLCBmb2xsb3dlZCBieSBmaXZlIHJvd3Mgd2l0aCBpbmZvcm1hdGlvbiBvbiBzdHJhaWdodCB0aWNrZXQgdm90aW5nIGZvciBlYWNoIG9mIGZpdmUgcGFydGllcyAod2hpY2ggY2FuIGJlIGlnbm9yZWQgZm9yIG91ciBwdXJwb3NlcyBoZXJlKS4gRnJvbSB0aGVyZSwgZWFjaCByb3cgY29udGFpbnMgaW5mb3JtYXRpb24gZm9yIGEgc2luZ2xlIGNhbmRpZGF0ZSBpbiBhIHBhcnRpY3VsYXIgY29udGVzdC4gV2UgY2FyZSBzcGVjaWZpY2FsbHkgYWJvdXQgdGhlIHN0YXRld2lkZSBjb250ZXN0cyB0aGF0IGFyZSBoZWxkIGluIGV2ZXJ5IHByZWNpbmN0IGhlcmU6IFByZXNpZGVudCAoYENvbnRlc3Q9PSJQUkVTSURFTlRJQUwgRUxFQ1RPUlMiYCksIFVTIFNlbmF0b3IgKGBDb250ZXN0PT0iVU5JVEVEIFNUQVRFUyBTRU5BVE9SImApLCBBdHRvcm5leSBHZW5lcmFsIChgQ29udGVzdD09IkFUVE9STkVZIEdFTkVSQUwiYCksIEF1ZGl0b3IgR2VuZXJhbCAoYENvbnRlc3Q9PSJBVURJVE9SIEdFTkVSQUwiYCksIGFuZCBUcmVhc3VyZXIgKGBDb250ZXN0PT0iU1RBVEUgVFJFQVNVUkVSImApLiBBbGwgb2Ygb3VyIGNhbGN1bGF0aW9ucyBhcmUgYmFzZWQgb24gdGhlIG51bWJlciBpbiB0aGUgYENvdW50YCB2YXJpYWJsZS4KCiMjIFNvbHZlIGluIHRocmVlIHBpZWNlcwoKVGhpcyBzb2x1dGlvbiBjcmVhdGVzIHRocmVlIGRhdGFmcmFtZXMgKHRhYmxlcykgdGhhdCBpdCBqb2lucyB0b2dldGhlciBpbiB0aGUgbGFzdCBzdGVwOiB0aGUgdG90YWwgdm90ZXMsIHRoZSB0d28tcGFydHkgc2hhcmVzLCBhbmQgdGhlIHJvbGxvZmZzLiBFYWNoIG9mIHRoZXNlIGRhdGFmcmFtZXMgc2hvdWxkIHVsdGltYXRlbHkgaGF2ZSA5MSByb3dzLgoKIyMjIFRhYmxlIDE6IFRvdGFsIHZvdGVzIGJ5IHByZWNpbmN0CgpUaGUgdG90YWwgdm90ZSBudW1iZXJzIGFyZSBhbHJlYWR5IGluIHRoZSBkYXRhLCB3ZSBqdXN0IG5lZWQgdG8gYGZpbHRlcmAgcm93cyBkb3duIHRvIGp1c3QgdGhlIDkxIHdlIG5lZWQgKHRoZSBvbmVzIHRoYXQgbGlzdCBgIkJBTExPVFMgQ0FTVCAtIFRPVEFMImAgaW4gdGhlIGBDb250ZXN0YCBjb2x1bW4sIGFuZCB0aGVuIGBzZWxlY3RgIHRoZSBjb2x1bW5zIHdlIG5lZWQuIFdlJ2xsIGtlZXAgdGhlIHByZWNpbmN0IG51bWJlciwgdGhlIHByZWNpbmN0IG5hbWUsIGFuZCB0aGUgdG90YWwgdm90ZXMuCgpgYGB7cn0KVG90IDwtIHJhd2RhdGEgJT4lIAogIGZpbHRlcihDb250ZXN0PT0iQkFMTE9UUyBDQVNUIC0gVE9UQUwiKSAlPiUgIyBmaWx0ZXIgdG8gdGhlIHJvd3Mgd2l0aCB0b3RhbCB2b3RlcwogIHNlbGVjdChQcmVjTm8sUHJlY05hbWUsVG90PUNvdW50KSAjc2VsZWN0IHRoZSBpZCBjb2x1bW5zIGFuZCB0aGUgY291bnRzLiBSZW5hbWUgdGhlIGNvdW50cyB0byBUb3QKZGltKFRvdCkgIyA5MSByb3dzLCAzIGNvbHVtbnMKaGVhZChUb3QpCmBgYAoKIyMjIFRhYmxlIDI6IFJvbGxvZmZzIGluIGRvd24tYmFsbG90IHJhY2VzCgpOb3cgd2UnbGwgY2FsY3VsYXRlIHRoZSByb2xsb2ZmIGRhdGEuIFVsdGltYXRlbHksIHRoZSBvdXRwdXQgaXMgOTEgcm93cywgMSByb3cgcGVyIHByZWNpbmN0LCB3aXRoIGZvdXIgY29sdW1ucyBjb250YWluaW5nIHRoZSByb2xsb2ZmIGluIHRoZSBub24tcHJlc2lkZW50aWFsIHJhY2VzIGluIHRoYXQgcHJlY2luY3QuIFRvIGdldCB0aGVyZSwgd2UgbmVlZCBhbiBpbnRlcm1lZGlhdGUgdGFibGUgb2YgOTEgcm93cyB3aXRoIHRoZSB0b3RhbCB2b3RlcyBwZXIgc3RhdGV3aWRlIGNvbnRlc3QsIGluY2x1ZGluZyBQcmVzaWRlbnQsIGJ5IHByZWNpbmN0LiBTbyB0aGUgdHJpY2tpZXN0IHN0ZXAgaXMgdGhlIG9uZSB3aGVyZSB3ZSBgc3ByZWFkJyB0aGUgZGF0YSBmcm9tIGl0cyAibG9uZyIgZm9ybWF0IHRvIGEgIndpZGUiIGZvcm1hdCB3aXRoIHRoZXNlIHZhcmlhYmxlcy4gCgpUaGUgZm9sbG93aW5nIGNvZGUgaXMgcHJlc2VudGVkIGFzIG9uZSBsb25nIHBpcGVsaW5lIHdpdGggb25lIGlucHV0IGFuZCBvbmUgb3V0cHV0LiBUaGUgaWRlbnRpY2FsIGNvZGUgaXMgcmVwZWF0ZWQgYmVsb3cgaW4gdGhpcyBub3RlYm9vaywgd2l0aCBvdXRwdXQgImdsaW1wc2VkIiBmb3IgZWFjaCBpbnRlcm1lZGlhdGUgc3RlcC4KCmBgYHtyfQpSb2xsb2ZmcyA8LSByYXdkYXRhICU+JSAgICAgICAgICAgICAgICAgICMgU3RhcnQgd2l0aCB0aGUgcmF3IGRhdGEKICBzZWxlY3QoUHJlY05vLCBDb250ZXN0LCBDb3VudCkgJT4lICAgICAjIEZvciByb2xsb2ZmIHdlIG5lZWQgdGhlIGRhdGEgYnkgcHJlY2luY3QgJiBjb250ZXN0CiAgbXV0YXRlKENvbj1zdWJzdHIoQ29udGVzdCwxLDMpKSAlPiUgICAgIyBGb3IgY2xlYW5saW5lc3MsIGNyZWF0ZSBhbiBhYmJyZXZpYXRlZCBjb250ZXN0IHZhcmlhYmxlCiAgZmlsdGVyKENvbiAlaW4lIGMoIlBSRSIsIlVOSSIsIkFUVCIsIkFVRCIsIlNUQSIpKSAlPiUgICMgUGljayB0aGUgcm93cyB3aXRoIGp1c3QgdGhlIHN0YXRld2lkZSBjb250ZXN0cwogIGdyb3VwX2J5KFByZWNObyxDb24pICU+JSAgICAgICAgICAgICAgICMgR3JvdXAgaW50byBQcmVjaW5jdC1Db250ZXN0IHVuaXRzCiAgc3VtbWFyaXNlKENvblRvdD1zdW0oQ291bnQpKSAlPiUgICAgICAgIyBDYWxjdWxhdGUgdG90YWwgdm90ZSBieSBQcmVjaW5jdC1Db250ZXN0CiAgc3ByZWFkKENvbiwgQ29uVG90KSAlPiUgICAgICAgICAgICAgICAjIFNwcmVhZCB0aGUgZGF0YSBieSBDb250ZXN0CiAgbXV0YXRlKFJPU2VuPTEwMCooMS1VTkkvUFJFKSwgICAgICAgICAjIFJvbGxvZmYgZm9yIFNlbmF0b3IgKFVOSSBURUQgU1RBVEVTIFNFTkFUT1IpCiAgICAgICAgIFJPQXR0PTEwMCooMS1BVFQvUFJFKSwgICAgICAgICAjIFJvbGxvZmYgZm9yIEF0dG9ybmV5IEdlbmVyYWwgKEFUVCBPUk5FWSBHRU5FUkFMKQogICAgICAgICBST0F1ZD0xMDAqKDEtQVVEL1BSRSksICAgICAgICAgIyBSb2xsb2ZmIGZvciBBdWRpdG9yIEdlbmVyYWwgKEFVRCBJVE9SIEdFTkVSQUwpCiAgICAgICAgIFJPVHJlPTEwMCooMS1TVEEvUFJFKSkgJT4lICAgICAjIFJvbGxvZmYgZm9yIFRyZWFzdXJlciAoU1RBIFRFIFRSRUFTVVJFUikKICBzZWxlY3QoUHJlY05vLFJPU2VuLFJPQXR0LFJPQXVkLFJPVHJlKSAjIEtlZXAganVzdCB0aGUgUHJlY2luY3QgTnVtYmVyIGFuZCBSb2xsb2ZmIHZhcmlhYmxlcwpkaW0oUm9sbG9mZnMpICMgOTEgcm93cywgNSBjb2x1bW5zCmhlYWQoUm9sbG9mZnMpCmBgYAoKIyMjIyBOQi4gMjAyMCBVcGRhdGUKClNpbmNlIHRoZSBvcmlnaW5hbCBwb3N0aW5nIG9mIHRoaXMgZXhlcmNpc2UgLyBzb2x1dGlvbiwgdGhlIGBzcHJlYWRgIGFuZCBgZ2F0aGVyYCBjb21tYW5kcyBoYXZlIGJlZW4gc3VwZXJjZWRlZCBieSBgcGl2b3Rfd2lkZXJgIGFuZCBgcGl2b3RfbG9uZ2VyYC4gVGhlIGNvZGUgYWJvdmUgc3RpbGwgd29ya3MsIGJ1dCB0aGUgcHJlZmVycmVkIGZvcm11bGF0aW9uIHdvdWxkIG5vdyBiZToKCmBgYHtyfQpSb2xsb2ZmcyA8LSByYXdkYXRhICU+JSAgICAgICAgICAgICAgICAgICMgU3RhcnQgd2l0aCB0aGUgcmF3IGRhdGEKICBzZWxlY3QoUHJlY05vLCBDb250ZXN0LCBDb3VudCkgJT4lICAgICAjIEZvciByb2xsb2ZmIHdlIG5lZWQgdGhlIGRhdGEgYnkgcHJlY2luY3QgJiBjb250ZXN0CiAgbXV0YXRlKENvbj1zdWJzdHIoQ29udGVzdCwxLDMpKSAlPiUgICAgIyBGb3IgY2xlYW5saW5lc3MsIGNyZWF0ZSBhbiBhYmJyZXZpYXRlZCBjb250ZXN0IHZhcmlhYmxlCiAgZmlsdGVyKENvbiAlaW4lIGMoIlBSRSIsIlVOSSIsIkFUVCIsIkFVRCIsIlNUQSIpKSAlPiUgICMgUGljayB0aGUgcm93cyB3aXRoIGp1c3QgdGhlIHN0YXRld2lkZSBjb250ZXN0cwogIGdyb3VwX2J5KFByZWNObyxDb24pICU+JSAgICAgICAgICAgICAgICMgR3JvdXAgaW50byBQcmVjaW5jdC1Db250ZXN0IHVuaXRzCiAgc3VtbWFyaXNlKENvblRvdD1zdW0oQ291bnQpKSAlPiUgICAgICAgIyBDYWxjdWxhdGUgdG90YWwgdm90ZSBieSBQcmVjaW5jdC1Db250ZXN0CiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENvbiwgdmFsdWVzX2Zyb20gPSBDb25Ub3QpICU+JSAKICAjIHJlcGxhY2VzCiAgIyBzcHJlYWQoQ29uLCBDb25Ub3QpICU+JSAgICAgICAgICAgIyBTcHJlYWQgdGhlIGRhdGEgYnkgQ29udGVzdAogIG11dGF0ZShST1Nlbj0xMDAqKDEtVU5JL1BSRSksICAgICAgICAgIyBSb2xsb2ZmIGZvciBTZW5hdG9yIChVTkkgVEVEIFNUQVRFUyBTRU5BVE9SKQogICAgICAgICBST0F0dD0xMDAqKDEtQVRUL1BSRSksICAgICAgICAgIyBSb2xsb2ZmIGZvciBBdHRvcm5leSBHZW5lcmFsIChBVFQgT1JORVkgR0VORVJBTCkKICAgICAgICAgUk9BdWQ9MTAwKigxLUFVRC9QUkUpLCAgICAgICAgICMgUm9sbG9mZiBmb3IgQXVkaXRvciBHZW5lcmFsIChBVUQgSVRPUiBHRU5FUkFMKQogICAgICAgICBST1RyZT0xMDAqKDEtU1RBL1BSRSkpICU+JSAgICAgIyBSb2xsb2ZmIGZvciBUcmVhc3VyZXIgKFNUQSBURSBUUkVBU1VSRVIpCiAgc2VsZWN0KFByZWNObyxST1NlbixST0F0dCxST0F1ZCxST1RyZSkgIyBLZWVwIGp1c3QgdGhlIFByZWNpbmN0IE51bWJlciBhbmQgUm9sbG9mZiB2YXJpYWJsZXMKZGltKFJvbGxvZmZzKSAjIDkxIHJvd3MsIDUgY29sdW1ucwpoZWFkKFJvbGxvZmZzKQpgYGAKCiMjIyBUYWJsZSAzOiBEZW1vY3JhdGljIHNoYXJlIG9mIHR3by1wYXJ0eSB2b3RlCgpUaGlzIGlzIHNpbWlsYXIgdG8gcm9sbG9mZiBpbiB0aGF0IHdlIG5lZWQgdG8gY2FsY3VsYXRlIHR3byBpbnRlcm1lZGlhdGUgcXVhbnRpdGllcyBhbG9uZyB0aGUgd2F5OiB0b3RhbCB2b3RlIGZvciBSZXB1YmxpY2FuIGFuZCBEZW1vY3JhdCBpbiBlYWNoIHJhY2UsIGJ5IHByZWNpbmN0LiBXaXRoIHJvbGxvZmYsIHdlIG5lZWRlZCB0byBzcHJlYWQgdGhlIGRhdGEgdG8gY3JlYXRlIGNvbnRlc3QgZGF0YSBieSBwcmVjaW5jdC4gV2l0aCBwYXJ0eSBzaGFyZXMsIHdlIG5lZWQgdG8gc3ByZWFkIHRoZSBkYXRhIHRvIGNyZWF0ZSBjb250ZXN0LXBhcnR5IGRhdGEgYnkgcHJlY2luY3QsIHNvIHdlIG5lZWQgdG8gY3JlYXRlIGEgY29udGVzdC1wYXJ0eSBpbmRpY2F0b3IgdmFyaWFibGUgdG8gYWN0IGFzIGEgImtleSIgZm9yIHNwcmVhZGluZy4gKDIwMjAgbm90ZTogYWdhaW4sIGBzcHJlYWRgIHNob3VsZCBiZSByZXBsYWNlZCBieSBgcGl2b3Rfd2lkZXJgLikKCmBgYHtyfQpEZW1Ud29QYXJ0eVZvdGVzIDwtIHJhd2RhdGEgJT4lICAgICAgICAgICAgIyBTdGFydCB3aXRoIHRoZSByYXcgZGF0YQogIHNlbGVjdChQcmVjTm8sUGFydHksQ29udGVzdCxDb3VudCkgJT4lICAgIyBBbGwgd2UgbmVlZCBpcyBQcmVjaW5jdCwgdGhlICJDb250ZXN0IiwgYW5kIHRoZSAiQ291bnQiCiAgbXV0YXRlKENvbj1zdWJzdHIoQ29udGVzdCwxLDMpLFB0eT1zdWJzdHIoUGFydHksMSwzKSkgJT4lICMgQ3JlYXRlIGFiYnJldmlhdGVkIFBhcnR5ICYgQ29udGVzdCB2YXJpYWJsZXMKICBzZWxlY3QoUHJlY05vLFB0eSxDb24sQ291bnQpICU+JSAgICAgICAgICMgR2V0IHJpZCBvZiB0aGUgdW5hYmJyZXZpYXRlZCB2YXJpYWJsZXMKICBmaWx0ZXIoQ29uICVpbiUgYygiUFJFIiwiVU5JIiwiQVRUIiwiQVVEIiwiU1RBIikpICU+JSAgIyBQaWNrIGp1c3QgdGhlIHJvd3Mgd2l0aCBlbGVjdGlvbnMgb2YgaW50ZXJlc3QKICBmaWx0ZXIoUHR5ICVpbiUgYygnREVNJywnUkVQJykpICU+JSAgICAgICMgUGljayBqdXN0IERlbW9jcmF0aWMgYW5kIFJlcHVibGljYW4gY2FuZGlkYXRlcwogIG11dGF0ZShDb25QdHkgPSBwYXN0ZShDb24sUHR5LHNlcD0iIikpICU+JSAgIyMjIyBDUkVBVEUgQ09OVEVTVC1QQVJUWSBUTyBBQ1QgQVMgS0VZIEZPUiBTUFJFQUQKICBzZWxlY3QoUHJlY05vLENvblB0eSxDb3VudCkgJT4lICAgICAgICAgICMgR2V0IHJpZCBvZiBjb2x1bW5zIHdlIGRvbid0IG5lZWQuCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IENvblB0eSwgdmFsdWVzX2Zyb20gPSBDb3VudCkgJT4lCiAgIyByZXBsYWNlcwogICMgIHNwcmVhZChDb25QdHksQ291bnQpICU+JSAgICAgICAgICMjIyMjIFNQUkVBRCBUSEUgREFUQSBCWSBUSEUgQ09OVEVTVC1QQVJUWSBLRVkKICBtdXRhdGUoRDJQcmU9MTAwKihQUkVERU0vKFBSRURFTStQUkVSRVApKSwgICAgICMgRDJQcmUgPSBEZW0gc2hhcmUgb2YgMiBwYXJ0eSB2b3RlIGZvciBQcmVzaWRlbnQKICAgICAgICAgRDJTZW49MTAwKihVTklERU0vKFVOSURFTStVTklSRVApKSwgICAgICMgRDJTZW4gPSBEZW0gc2hhcmUgb2YgMiBwYXJ0eSB2b3RlIGZvciBVUyBTZW5hdG9yCiAgICAgICAgIEQyQXR0PTEwMCooQVRUREVNLyhBVFRERU0rQVRUUkVQKSksICAgICAjIEQyQXR0ID0gRGVtIHNoYXJlIG9mIDIgcGFydHkgdm90ZSBmb3IgQXR0b3JuZXkgR2VubAogICAgICAgICBEMkF1ZD0xMDAqKEFVRERFTS8oQVVEREVNK0FVRFJFUCkpLCAgICAgIyBEMkF1ZCA9IERlbSBzaGFyZSBvZiAyIHBhcnR5IHZvdGUgZm9yIEF1ZGl0b3IgR2VubAogICAgICAgICBEMlRyZT0xMDAqKFNUQURFTS8oU1RBREVNK1NUQVJFUCkpKSAlPiUgIyBEMlRyZSA9IERlbSBzaGFyZSBvZiAyIHBhcnR5IHZvdGUgZm9yIFN0IFRyZWFzdXJlcgogIHNlbGVjdChQcmVjTm8sRDJQcmUsRDJTZW4sRDJBdHQsRDJBdWQsRDJUcmUpICAjIEdldCByaWQgb2YgY29sdW1ucyB3ZSBkb24ndCBuZWVkCmRpbShEZW1Ud29QYXJ0eVZvdGVzKSAjIDkxIHJvd3MsIDYgY29sdW1ucwpoZWFkKERlbVR3b1BhcnR5Vm90ZXMpCmBgYAoKCiMjIEZpbmFsIG91dHB1dCB0YWJsZTogTWVyZ2VkIERhdGEKCk5vdyB3ZSBtZXJnZSB0aGUgdGFibGVzIGFuZCBmb3JtYXQgdGhlIFByZWNpbmN0IE51bWJlciBhbmQgTmFtZSBhcyByZXF1ZXN0ZWQgaW4gdGhlIEV4ZXJjaXNlLiBUaGUgbWFpbiB2ZXJiIGhlcmUgaXMgYCpfam9pbi5gIEFueSBvZiBgbGVmdF9qb2luYCwgYHJpZ2h0X2pvaW5gLCBvciBgaW5uZXJfam9pbmAgd2lsbCB3b3JrIGluIHRoaXMgY2FzZSB0byBtYXRjaCBkYXRhIGZyb20gZWFjaCB0YWJsZSBieSB0aGUgb25seSBtYXRjaGluZyB2YXJpYWJsZSwgUHJlY05vLiAoVGhlc2UgdmVyYnMgZGlmZmVyIGluIGhvdyB0aGV5IHRyZWF0IHJvd3MgaW4gd2hpY2ggdGhlIGpvaW4gdmFyaWFibGVzIGFyZSBtaXNzaW5nIG9yIGR1cGxpY2F0ZWQgaW4gb25lIG9yIHRoZSBvdGhlciB0YWJsZSwgYnV0IGluIHRoaXMgY2FzZSBhbGwgdGhyZWUgdGFibGVzIGhhdmUgZXhhY3RseSA5MSByb3dzIHdpdGggdW5pcXVlIFByZWNObywgc28gdGhlIGVmZmVjdCBpcyBpZGVudGljYWwuKQoKYGBge3J9CkV4M0RhdGEgPC0gVG90ICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0YXJ0IHdpdGggdGhlIFRvdCBkYXRhX2ZyYW1lCiAgbGVmdF9qb2luKERlbVR3b1BhcnR5Vm90ZXMpICU+JSAgICAgICAgICAgICAjIE1lcmdlIHRoZSBEZW1Ud29QYXJ0eSB0YWJsZSAob24gc2hhcmVkIFByZWNObykKICBsZWZ0X2pvaW4oUm9sbG9mZnMpICU+JSAgICAgICAgICAgICAgICAgICAgICMgTWVyZ2UgdGhlIFJvbGxvZmYgdGFibGUgKG9uIHNoYXJlZCBQcmVjTm8pCiAgbXV0YXRlKFByZWNObz1hcy5pbnRlZ2VyKFByZWNObykpICU+JSAgICAgICAjIFR1cm4gUHJlY05vIGludG8gYSBudW1iZXIKICBtdXRhdGUoUHJlY05hbWU9c3RyX3N1YihQcmVjTmFtZSxzdGFydD00TCkpICMgU3RyaXAgdGhlIHJlZHVuZGFudCBudW1iZXIgb2ZmIG9mIHRoZSBQcmVjaW5jdCBuYW1lcwpFeDNEYXRhCmBgYAoKIyMgU29sdXRpb24gcmVwZWF0ZWQgd2l0aCBpbnRlcm1lZGlhdGUgc3RlcHMgZGlzcGxheWVkCgpIZXJlIHdlIHJlcGVhdCB0aGUgZXhhY3Qgc3RlcHMgZnJvbSBhYm92ZSwgYnV0IHdpdGggYSAiZ2xpbXBzZSIgc3RhdGVtZW50IGF0IGVhY2ggc3RlcCB0byBzZWUgYWxsIG9mIHRoZSBpbnRlcm1lZGlhdGUgcmVzdWx0cy4KCmBgYHtyfQpUb3QgPC0gcmF3ZGF0YSAlPiUgCiAgZmlsdGVyKENvbnRlc3Q9PSJCQUxMT1RTIENBU1QgLSBUT1RBTCIpICVUPiUgZ2xpbXBzZSgpICAlPiUgIyBmaWx0ZXIgdG8gdGhlIHJvd3Mgd2l0aCB0b3RhbCB2b3RlcwogIHNlbGVjdChQcmVjTm8sUHJlY05hbWUsVG90PUNvdW50KSAjc2VsZWN0IHRoZSBpZCBjb2x1bW5zIGFuZCB0aGUgY291bnRzLiBSZW5hbWUgdGhlIGNvdW50cyB0byBUb3QKZGltKFRvdCkgIyA5MSByb3dzIHggMwpoZWFkKFRvdCkKYGBgCgoKYGBge3J9ClJvbGxvZmZzIDwtIHJhd2RhdGEgJT4lICAgICAgICAgICAgICAgICAgIyBTdGFydCB3aXRoIHRoZSByYXcgZGF0YQogIHNlbGVjdChQcmVjTm8sIENvbnRlc3QsIENvdW50KSAlVD4lIGdsaW1wc2UoKSAgJT4lICAjIFBpY2sgZGF0YSBieSBwcmVjaW5jdCAmIGNvbnRlc3QKICBtdXRhdGUoQ29uPXN1YnN0cihDb250ZXN0LDEsMykpICVUPiUgZ2xpbXBzZSgpICAlPiUgIyBGb3IgY2xlYW5saW5lc3MsIGNyZWF0ZSBhYmJyZXYgY29udGVzdCB2YXJpYWJsZQogIGZpbHRlcihDb24gJWluJSBjKCJQUkUiLCJVTkkiLCJBVFQiLCJBVUQiLCJTVEEiKSkgJVQ+JSBnbGltcHNlKCkgJT4lICMgUGljayByb3dzIHcgc3RhdGV3aWRlIGNvbnRlc3RzCiAgZ3JvdXBfYnkoUHJlY05vLENvbikgJVQ+JSBnbGltcHNlKCkgJT4lICAgICAgICAgICAgICAgIyBHcm91cCBpbnRvIFByZWNpbmN0LUNvbnRlc3QgdW5pdHMKICBzdW1tYXJpc2UoQ29uVG90PXN1bShDb3VudCkpICVUPiUgZ2xpbXBzZSgpICU+JSAgICAgICAjIENhbGN1bGF0ZSB0b3RhbCB2b3RlIGJ5IFByZWNpbmN0LUNvbnRlc3QKICBzcHJlYWQoQ29uLCBDb25Ub3QpICVUPiUgZ2xpbXBzZSgpICU+JSAgICAgICAgICAgICAgICMgU3ByZWFkIHRoZSBkYXRhIGJ5IENvbnRlc3QKICBtdXRhdGUoUk9TZW49MTAwKigxLVVOSS9QUkUpLCAgICAgICAgICMgUm9sbG9mZiBmb3IgU2VuYXRvciAoVU5JIFRFRCBTVEFURVMgU0VOQVRPUikKICAgICAgICAgUk9BdHQ9MTAwKigxLUFUVC9QUkUpLCAgICAgICAgICMgUm9sbG9mZiBmb3IgQXR0b3JuZXkgR2VuZXJhbCAoQVRUIE9STkVZIEdFTkVSQUwpCiAgICAgICAgIFJPQXVkPTEwMCooMS1BVUQvUFJFKSwgICAgICAgICAjIFJvbGxvZmYgZm9yIEF1ZGl0b3IgR2VuZXJhbCAoQVVEIElUT1IgR0VORVJBTCkKICAgICAgICAgUk9UcmU9MTAwKigxLVNUQS9QUkUpKSAlVD4lIGdsaW1wc2UoKSAgJT4lICAgICAjIFJvbGxvZmYgZm9yIFRyZWFzdXJlciAoU1RBIFRFIFRSRUFTVVJFUikKICBzZWxlY3QoUHJlY05vLFJPU2VuLFJPQXR0LFJPQXVkLFJPVHJlKSAjIEtlZXAganVzdCB0aGUgUHJlY2luY3QgTnVtYmVyIGFuZCBSb2xsb2ZmIHZhcmlhYmxlcwpSb2xsb2ZmcwpgYGAKCgoKYGBge3J9CkRlbVR3b1BhcnR5Vm90ZXMgPC0gcmF3ZGF0YSAlPiUgICAgICAgICAgICAjIFN0YXJ0IHdpdGggdGhlIHJhdyBkYXRhCiAgc2VsZWN0KFByZWNObyxQYXJ0eSxDb250ZXN0LENvdW50KSAlVD4lIGdsaW1wc2UoKSAlPiUgICAjIEFsbCB3ZSBuZWVkIGlzIFByZWNpbmN0LCBDb250ZXN0LCAmICJDb3VudCIKICBtdXRhdGUoQ29uPXN1YnN0cihDb250ZXN0LDEsMyksUHR5PXN1YnN0cihQYXJ0eSwxLDMpKSAlVD4lIGdsaW1wc2UoKSAlPiUgIyBBYmJyZXZpYXRlZCBQYXJ0eSAmIENvbnRlc3QKICBzZWxlY3QoUHJlY05vLFB0eSxDb24sQ291bnQpICVUPiUgZ2xpbXBzZSgpICU+JSAgICAgICAgICMgR2V0IHJpZCBvZiB0aGUgdW5hYmJyZXZpYXRlZCB2YXJpYWJsZXMKICBmaWx0ZXIoQ29uICVpbiUgYygiUFJFIiwiVU5JIiwiQVRUIiwiQVVEIiwiU1RBIikpICVUPiUgZ2xpbXBzZSgpICU+JSAgIyBQaWNrIHJvd3MgdyBlbGVjdGlvbnMKICBmaWx0ZXIoUHR5ICVpbiUgYygnREVNJywnUkVQJykpICVUPiUgZ2xpbXBzZSgpICU+JSAgIyBQaWNrIGp1c3QgRGVtb2NyYXRpYyBhbmQgUmVwdWJsaWNhbiBjYW5kaWRhdGVzCiAgbXV0YXRlKENvblB0eSA9IHBhc3RlKENvbixQdHksc2VwPSIiKSkgJVQ+JSBnbGltcHNlKCkgJT4lICAjIyMjIENSRUFURSBDT05URVNULVBBUlRZIEFTIEtFWSBGT1IgU1BSRUFECiAgc2VsZWN0KFByZWNObyxDb25QdHksQ291bnQpICVUPiUgZ2xpbXBzZSgpICU+JSAgICAgICAgICAjIEdldCByaWQgb2YgY29sdW1ucyB3ZSBkb24ndCBuZWVkLgogIHNwcmVhZChDb25QdHksQ291bnQpICVUPiUgZ2xpbXBzZSgpICU+JSAgICAgICAgICAgICAgICAgIyMjIyMgU1BSRUFEIFRIRSBEQVRBIEJZIFRIRSBDT05URVNULVBBUlRZIEtFWQogIG11dGF0ZShEMlByZT0xMDAqKFBSRURFTS8oUFJFREVNK1BSRVJFUCkpLCAgICAgIyBEMlByZSA9IERlbSBzaGFyZSBvZiAyIHBhcnR5IHZvdGUgZm9yIFByZXNpZGVudAogICAgICAgICBEMlNlbj0xMDAqKFVOSURFTS8oVU5JREVNK1VOSVJFUCkpLCAgICAgIyBEMlNlbiA9IERlbSBzaGFyZSBvZiAyIHBhcnR5IHZvdGUgZm9yIFVTIFNlbmF0b3IKICAgICAgICAgRDJBdHQ9MTAwKihBVFRERU0vKEFUVERFTStBVFRSRVApKSwgICAgICMgRDJBdHQgPSBEZW0gc2hhcmUgb2YgMiBwYXJ0eSB2b3RlIGZvciBBdHRvcm5leSBHZW5sCiAgICAgICAgIEQyQXVkPTEwMCooQVVEREVNLyhBVURERU0rQVVEUkVQKSksICAgICAjIEQyQXVkID0gRGVtIHNoYXJlIG9mIDIgcGFydHkgdm90ZSBmb3IgQXVkaXRvciBHZW5sCiAgICAgICAgIEQyVHJlPTEwMCooU1RBREVNLyhTVEFERU0rU1RBUkVQKSkpICVUPiUgZ2xpbXBzZSgpICU+JSAjIEQyVHJlID0gRGVtIHNoYXJlLCAyIHB0eSB2b3RlIFN0IFRyZWFzdXJlcgogIHNlbGVjdChQcmVjTm8sRDJQcmUsRDJTZW4sRDJBdHQsRDJBdWQsRDJUcmUpICAjIEdldCByaWQgb2YgY29sdW1ucyB3ZSBkb24ndCBuZWVkCkRlbVR3b1BhcnR5Vm90ZXMKYGBgCgoKYGBge3J9CkV4M0RhdGEgPC0gVG90ICU+JSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdGFydCB3aXRoIHRoZSBUb3QgZGF0YV9mcmFtZQogIGxlZnRfam9pbihEZW1Ud29QYXJ0eVZvdGVzKSAlVD4lIGdsaW1wc2UoKSAlPiUgICAgIyBNZXJnZSB0aGUgRGVtVHdvUGFydHkgdGFibGUgKG9uIHNoYXJlZCBQcmVjTm8pCiAgbGVmdF9qb2luKFJvbGxvZmZzKSAlVD4lIGdsaW1wc2UoKSAlPiUgICAgICAgICAgICAjIE1lcmdlIHRoZSBSb2xsb2ZmIHRhYmxlIChvbiBzaGFyZWQgUHJlY05vKQogIG11dGF0ZShQcmVjTm89YXMuaW50ZWdlcihQcmVjTm8pKSAlVD4lIGdsaW1wc2UoKSAlPiUgICAgICAgIyBUdXJuIFByZWNObyBpbnRvIGEgbnVtYmVyCiAgbXV0YXRlKFByZWNOYW1lPXN0cl9zdWIoUHJlY05hbWUsc3RhcnQ9NEwpKSAjIFN0cmlwIHRoZSByZWR1bmRhbnQgbnVtYmVyIG9mZiBvZiB0aGUgUHJlY2luY3QgbmFtZXMKRXgzRGF0YSAjIDkxIHJvd3MsIDEyIGNvbHVtbnMKYGBgCg==