This document addresses one solution to the data-wrangling exercise described in Exercise3.pdf.
The data.table
format does not seem to display consistently – or I’m missing something – within the usual nb.html
format. The html
version here may be prettier. The R Notebook with executable code – the Rmd file – should be downloadable from the pulldown at upper right of the nb.html
file or can be downloaded separately here: Exercise3-datatableSolution.Rmd.
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 data.table
library. For alternative solutions see https://burtmonroe.github.io/SoDA501/Exercise3-2018.
Solving with data.table
We will proceed with the same general strategy as with the tidyverse solution, creating three tables – total votes, rolloffs, and two party shares – that we then merge in the final step.
First we read in the raw data (this time using the fread
command to read into a data.table
), and look at the first 50 rows.
library(data.table)
rawdata.dt <- fread("CentreCountyPrecinctResults2016GeneralElection.txt") # 3520 rows, 16 columns
dim(rawdata.dt)
[1] 3520 16
County
1: CENTRE COUNTY, PENNSYLVANIA
2: CENTRE COUNTY, PENNSYLVANIA
3: CENTRE COUNTY, PENNSYLVANIA
4: CENTRE COUNTY, PENNSYLVANIA
5: CENTRE COUNTY, PENNSYLVANIA
---
3516: CENTRE COUNTY, PENNSYLVANIA
3517: CENTRE COUNTY, PENNSYLVANIA
3518: CENTRE COUNTY, PENNSYLVANIA
3519: CENTRE COUNTY, PENNSYLVANIA
3520: CENTRE COUNTY, PENNSYLVANIA
ElecYear Election
1: 2016 GENERAL ELECTION
2: 2016 GENERAL ELECTION
3: 2016 GENERAL ELECTION
4: 2016 GENERAL ELECTION
5: 2016 GENERAL ELECTION
---
3516: 2016 GENERAL ELECTION
3517: 2016 GENERAL ELECTION
3518: 2016 GENERAL ELECTION
3519: 2016 GENERAL ELECTION
3520: 2016 GENERAL ELECTION
ElecDate PrecNo
1: NOVEMBER 8 2016 1
2: NOVEMBER 8 2016 1
3: NOVEMBER 8 2016 1
4: NOVEMBER 8 2016 1
5: NOVEMBER 8 2016 1
---
3516: NOVEMBER 8 2016 91
3517: NOVEMBER 8 2016 91
3518: NOVEMBER 8 2016 91
3519: NOVEMBER 8 2016 91
3520: NOVEMBER 8 2016 91
PrecName
1: 01 BELLEFONTE NORTH
2: 01 BELLEFONTE NORTH
3: 01 BELLEFONTE NORTH
4: 01 BELLEFONTE NORTH
5: 01 BELLEFONTE NORTH
---
3516: 91 FERGUSON NORTH CENTRAL
3517: 91 FERGUSON NORTH CENTRAL
3518: 91 FERGUSON NORTH CENTRAL
3519: 91 FERGUSON NORTH CENTRAL
3520: 91 FERGUSON NORTH CENTRAL
Party
1:
2:
3:
4: DEMOCRATIC
5: REPUBLICAN
---
3516: DEMOCRATIC
3517: REPUBLICAN
3518:
3519:
3520:
Contest
1: REGISTERED VOTERS - TOTAL
2: BALLOTS CAST - TOTAL
3: VOTER TURNOUT - TOTAL
4: STRAIGHT PARTY
5: STRAIGHT PARTY
---
3516: REPRESENTATIVE IN THE GENERAL ASSEMBLY 171ST LEGISLATIVE
3517: REPRESENTATIVE IN THE GENERAL ASSEMBLY 171ST LEGISLATIVE
3518: REPRESENTATIVE IN THE GENERAL ASSEMBLY 171ST LEGISLATIVE
3519: JUDGES AGE
3520: JUDGES AGE
Descr
1: NA
2: NA
3: NA
4: NA
5: NA
---
3516: NA
3517: NA
3518: NA
3519: NA
3520: NA
VoteFor
1:
2:
3:
4: VOTE FOR NOT MORE THAN 1
5: VOTE FOR NOT MORE THAN 1
---
3516: VOTE FOR NOT MORE THAN 1
3517: VOTE FOR NOT MORE THAN 1
3518: VOTE FOR NOT MORE THAN 1
3519: VOTE FOR NOT MORE THAN 1
3520: VOTE FOR NOT MORE THAN 1
Candidate Posn
1: 10
2: 11
3: 12
4: DEMOCRATIC 17
5: REPUBLICAN 18
---
3516: MELODY S FLECK 7700
3517: KERRY A BENNINGHOFF 7701
3518: WRITE-IN 7702
3519: YES 7707
3520: NO 7708
Count PctCnt Total PctTot
1: 507 NA NA NA
2: 391 NA NA NA
3: NA 77.12 NA NA
4: 46 40.35 NA NA
5: 66 57.89 NA NA
---
3516: 301 43.81 NA NA
3517: 385 56.04 NA NA
3518: 1 0.15 NA NA
3519: 317 53.64 NA NA
3520: 274 46.36 NA NA
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 data.tables that it joins together in the last step: the total votes, the two-party shares, and the rolloffs. Each of these data.tables should ultimately have 91 rows.
Table 1: Total votes by precinct
Creating the total vote table requires just picking the right rows and relabeling the Count variable.
Tot.dt <- rawdata.dt[Contest=="BALLOTS CAST - TOTAL",.(PrecNo,PrecName, Tot=Count)]
Tot.dt
Table 2: Rolloffs in down-ballot races
As with the tidyverse solution, there are two nontrivial steps here. The first is the grouped summary step (the third assignment below), where vote counts are summed by Precinct Number and Contest. This is more or less the core process in data.table
, so its implementation is very compact. The second is the dcast
command which does the equivalent of what pivot_wider
(spread
) does in the tidyverse. The notation here uses a formula (here PrecNo ~ Con
) to define the “key” on which the table is cast.
# Create data.table with just vars PrecNo, Count, and Con (abbreviated Contest)
NeededColumnsRows.dt <- rawdata.dt[,.(PrecNo,Count,Con=substr(Contest,1,3))] # pick columns, abbreviate
NeededColumnsRows.dt <- NeededColumnsRows.dt[Con %in% c("PRE","UNI","ATT","AUD","STA"),] # pick rows
NeededColumnsRows.dt # 2093 rows, 3 columns
# Create grouped summary data.table with Total votes by Precinct-Contest
PrecinctContestsLong.dt <- NeededColumnsRows.dt[,.(ConTot = sum(Count)), by=.(PrecNo,Con)]
PrecinctContestsLong.dt <- PrecinctContestsLong.dt[,.(PrecNo,Con,ConTot)] # could be "chained" w above
PrecinctContestsLong.dt # 455 rows, 6 columns
# "Cast" the data by Contest (spread from long to wide)
PrecinctContestsWide.dt <- dcast(PrecinctContestsLong.dt, PrecNo ~ Con, value.var = "ConTot")
PrecinctContestsWide.dt # 91 rows, 6 columns
# Calculate Rolloff Variables
Rolloffs.dt <- PrecinctContestsWide.dt[,.(PrecNo, # Keep Precinct Number
ROSen=100*(1-UNI/PRE), # Rolloff for US Senator
ROAtt=100*(1-ATT/PRE), # Rolloff for Attorney General
ROAud=100*(1-AUD/PRE), # Rolloff for Auditor General
ROTre=100*(1-STA/PRE))] # Rolloff for Treasurer
Rolloffs.dt
Table 3: Democratic share of two-party vote
In the tidyverse
version, we had to create a single column of Contest-Party indicators to act as a key. With data.table
we can use two keys, just listing them on the right side of the formula (here, PrecNo ~ Con + Pty
), and the keys from all pairings are constructed automatically. So this is a couple of commands more compact in data.table.
# Create data.table with just vars PrecNo, Count, and Con (abbreviated Contest)
NeededColumnsRows.dt <- rawdata.dt[,.(PrecNo,Pty=substr(Party,1,3),Con=substr(Contest,1,3), Count)] # pick columns, abbreviate
NeededColumnsRows.dt <- NeededColumnsRows.dt[Pty %in% c("DEM","REP") & Con %in% c("PRE","UNI","ATT","AUD","STA"),] # pick rows
NeededColumnsRows.dt # 910 rows, 3 columns
# "Cast" the data by Party & Contest (spread from long to wide)
# Note that data.table has no problem automatically creating a key based on two columns
PrecPartyContestsWide.dt <- dcast(NeededColumnsRows.dt, PrecNo ~ Con + Pty, value.var = "Count")
PrecPartyContestsWide.dt # 91 rows, 6 columns
# Calculate Democratic Two-Party Share Variables
DemTwoPartyShares.dt <- PrecPartyContestsWide.dt[,.(PrecNo, # Keep Precinct Number
D2Pre=100*(PRE_DEM/(PRE_DEM+PRE_REP)),# D2Pre = Dem 2 party share, President
D2Sen=100*(UNI_DEM/(UNI_DEM+UNI_REP)),# D2Pre = Dem 2 party share, US Senator
D2Att=100*(ATT_DEM/(ATT_DEM+ATT_REP)),# D2Pre = Dem 2 party share, Attorney Gen
D2Aud=100*(AUD_DEM/(AUD_DEM+AUD_REP)),# D2Pre = Dem 2 party share, Auditor Gen
D2Tre=100*(STA_DEM/(STA_DEM+STA_REP)))]# D2Pre = Dem 2 party share, State Treas
DemTwoPartyShares.dt
Final output table: Merged Data
Now we merge
the tables and format the Precinct Number and Name as requested in the Exercise.
Ex3Data.dt <- merge(Tot.dt,Rolloffs.dt, by="PrecNo") # Merge Total with Rolloffs
Ex3Data.dt <- merge(Ex3Data.dt,DemTwoPartyShares.dt, by="PrecNo") # Merge that with Dem 2-party Shares
Ex3Data.dt[,PrecNo := as.integer(PrecNo)] # Make Precinct number a number
Ex3Data.dt[,PrecName := substr(PrecName,4,stop=40L)] # Strip duplicate info from Name
Ex3Data.dt
LS0tCnRpdGxlOiAiU29EQSA1MDEsIEV4ZXJjaXNlIDMgLSBkYXRhLnRhYmxlIFNvbHV0aW9uIgphdXRob3I6ICJCdXJ0IEwuIE1vbnJvZSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICB0aGVtZTogdW5pdGVkCiAgICB0b2M6IHllcwotLS0KCgpUaGlzIGRvY3VtZW50IGFkZHJlc3NlcyBvbmUgc29sdXRpb24gdG8gdGhlIGRhdGEtd3JhbmdsaW5nIGV4ZXJjaXNlIGRlc2NyaWJlZCBpbiBbRXhlcmNpc2UzLnBkZl0oaHR0cHM6Ly9idXJ0bW9ucm9lLmdpdGh1Yi5pby9Tb0RBNTAxL0V4ZXJjaXNlcy9FeGVyY2lzZTMtMjAxOC9FeGVyY2lzZTMucGRmKS4KClRoZSBgZGF0YS50YWJsZWAgZm9ybWF0IGRvZXMgbm90IHNlZW0gdG8gZGlzcGxheSBjb25zaXN0ZW50bHkgLS0gb3IgSSdtIG1pc3Npbmcgc29tZXRoaW5nIC0tIHdpdGhpbiB0aGUgdXN1YWwgYG5iLmh0bWxgIGZvcm1hdC4gVGhlIGBodG1sYCB2ZXJzaW9uIFtoZXJlXShodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1NvREE1MDEvRXhlcmNpc2VzL0V4ZXJjaXNlMy0yMDE4L2RhdGF0YWJsZVNvbHV0aW9uL0V4ZXJjaXNlMy1kYXRhdGFibGVTb2x1dGlvbi5odG1sKSBtYXkgYmUgcHJldHRpZXIuIFRoZSBSIE5vdGVib29rIHdpdGggZXhlY3V0YWJsZSBjb2RlIC0tIHRoZSBSbWQgZmlsZSAtLSBzaG91bGQgYmUgZG93bmxvYWRhYmxlIGZyb20gdGhlIHB1bGxkb3duIGF0IHVwcGVyIHJpZ2h0IG9mIHRoZSBgbmIuaHRtbGAgZmlsZSBvciBjYW4gYmUgZG93bmxvYWRlZCBzZXBhcmF0ZWx5IGhlcmU6IFtFeGVyY2lzZTMtZGF0YXRhYmxlU29sdXRpb24uUm1kXShodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1NvREE1MDEvRXhlcmNpc2VzL0V4ZXJjaXNlMy0yMDE4L2RhdGF0YWJsZVNvbHV0aW9uL0V4ZXJjaXNlMy1kYXRhdGFibGVTb2x1dGlvbi5SbWQpLiAgCgpUaGUgaW5wdXQgdG8gdGhlIGV4ZXJjaXNlIGlzIHRoZSByYXcgZGF0YSBvbiAyMDE2IENlbnRyZSBDb3VudHksIFBBLCBwcmVjaW5jdCBsZXZlbCB2b3RlcyBmb3VuZCBpbiBbIkNlbnRyZUNvdW50eVByZWNpbmN0UmVzdWx0czIwMTZHZW5lcmFsRWxlY3Rpb24udHh0Il0oaHR0cHM6Ly9idXJ0bW9ucm9lLmdpdGh1Yi5pby9Tb0RBNTAxL0V4ZXJjaXNlcy9FeGVyY2lzZTMtMjAxOC9DZW50cmVDb3VudHlQcmVjaW5jdFJlc3VsdHMyMDE2R2VuZXJhbEVsZWN0aW9uLnR4dCksIHdoaWNoIHdlcmUgcmV0cmlldmVkIGZyb20gW2h0dHA6Ly9jZW50cmVjb3VudHlwYS5nb3YvSW5kZXguYXNweD9OSUQ9ODAyXShodHRwOi8vY2VudHJlY291bnR5cGEuZ292L0luZGV4LmFzcHg/TklEPTgwMikuIChVcGRhdGUgLSAyMDIwOiBUaGlzIGxpbmsgaXMgbm8gbG9uZ2VyIGFjY3VyYXRlLCBhbmQgdGhlIG9yaWdpbmFsIGZpbGUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGF2YWlsYWJsZSB0aGVyZSBhbnkgbW9yZS4gWW91IGNhbiwgaG93ZXZlciwgZmluZCBpdCBvbiB0aGUgSW50ZXJuZXQgQXJjaGl2ZSdzIFdheWJhY2sgTWFjaGluZSwgZm9yIGV4YW1wbGUsIGhlcmU6IFtodHRwOi8vd2ViLmFyY2hpdmUub3JnL3dlYi8yMDE4MTEwNjE2MTUxNC9jZW50cmVjb3VudHlwYS5nb3YvaW5kZXguYXNweD9OSUQ9ODAyXShodHRwOi8vd2ViLmFyY2hpdmUub3JnL3dlYi8yMDE4MTEwNjE2MTUxNC9jZW50cmVjb3VudHlwYS5nb3YvaW5kZXguYXNweD9OSUQ9ODAyKS4gVGhlIGZpbGUgaXMgbm8gbG9uZ2VyIGF2YWlsYWJsZSBkdWUgdG8gdGhlIHdlYnNpdGUgYmVpbmcgInVwZGF0ZWQiIHRvIHByb3ZpZGUgdGhlIGRhdGEgaW4gZXZlbiBsZXNzIGFjY2Vzc2libGUgZm9ybSAocGRmKS4pCgoKVGhlIGV4ZXJjaXNlIGFza3MgeW91IHRvIGV4dHJhY3QgdGhlIGRhdGEgb24gdm90ZXMgY2FzdCBieSBwcmVjaW5jdCBpbiBzdGF0ZXdpZGUgZWxlY3Rpb25zLCBhbmQgcHJvY2VzcyB0aGVtIGludG8gYSBuZXcgdGFibGUgd2l0aCBwcmVjaW5jdCBsZXZlbCBkYXRhIG9uIHRvdGFsIHZvdGVzLCBEZW1vY3JhdGljIHNoYXJlIG9mIHR3by1wYXJ0eSB2b3RlLCBhbmQgYmFsbG90IHJvbGxvZmYgZnJvbSBwcmVzaWRlbnRpYWwgdm90ZXMgdG8gdm90ZXMgaW4gb3RoZXIgc3RhdGV3aWRlIHJhY2VzLgoKVGhpcyBzb2x1dGlvbiB1c2VzIHRoZSBSIGBkYXRhLnRhYmxlYCBsaWJyYXJ5LiBGb3IgYWx0ZXJuYXRpdmUgc29sdXRpb25zIHNlZSBbaHR0cHM6Ly9idXJ0bW9ucm9lLmdpdGh1Yi5pby9Tb0RBNTAxL0V4ZXJjaXNlMy0yMDE4XShodHRwczovL2J1cnRtb25yb2UuZ2l0aHViLmlvL1NvREE1MDEvRXhlcmNpc2UzLTIwMTgpLgoKIyBTb2x2aW5nIHdpdGggYGRhdGEudGFibGVgCgpXZSB3aWxsIHByb2NlZWQgd2l0aCB0aGUgc2FtZSBnZW5lcmFsIHN0cmF0ZWd5IGFzIHdpdGggdGhlIHRpZHl2ZXJzZSBzb2x1dGlvbiwgY3JlYXRpbmcgdGhyZWUgdGFibGVzIC0tIHRvdGFsIHZvdGVzLCByb2xsb2ZmcywgYW5kIHR3byBwYXJ0eSBzaGFyZXMgLS0gdGhhdCB3ZSB0aGVuIG1lcmdlIGluIHRoZSBmaW5hbCBzdGVwLgoKRmlyc3Qgd2UgcmVhZCBpbiB0aGUgcmF3IGRhdGEgKHRoaXMgdGltZSB1c2luZyB0aGUgYGZyZWFkYCBjb21tYW5kIHRvIHJlYWQgaW50byBhIGBkYXRhLnRhYmxlYCksIGFuZCBsb29rIGF0IHRoZSBmaXJzdCA1MCByb3dzLgoKYGBge3J9CmxpYnJhcnkoZGF0YS50YWJsZSkKcmF3ZGF0YS5kdCA8LSBmcmVhZCgiQ2VudHJlQ291bnR5UHJlY2luY3RSZXN1bHRzMjAxNkdlbmVyYWxFbGVjdGlvbi50eHQiKSAjIDM1MjAgcm93cywgMTYgY29sdW1ucwpkaW0ocmF3ZGF0YS5kdCkKcmF3ZGF0YS5kdApgYGAKClRoZXNlIGRhdGEgaGF2ZSwgcm91Z2hseSwgMzkgcm93cyBmb3IgZWFjaCBvZiA5MSBwcmVjaW5jdHMgaW4gQ2VudHJlIENvdW50eSwgUEEsIGlkZW50aWZpZWQgYnkgdGhlIHZhcmlhYmxlcyBgUHJlY05vYCAod2hpY2ggcmFuZ2VzIGZyb20gYDAwMDFgIHRvIGAwMDkxYCkgYW5kIGBQcmVjTmFtZWAgKHdoaWNoIHJhbmdlcyBmcm9tIGAwMSBCRUxMRUZPTlRFIE5PUlRIYCB0byBgOTEgRkVSR1VTT04gTk9SVEggQ0VOVFJBTGApLiBFYWNoIHByZWNpbmN0IHN0YXJ0cyB3aXRoIHRocmVlIHJvd3MgZm9yIHN1bW1hcnkgaW5mb3JtYXRpb24sIGlkZW50aWZpZWQgaW4gdGhlIGBDb250ZXN0YCB2YXJpYWJsZSBieSB2YWx1ZXMgYFJFR0lTVEVSRUQgVk9URVJTIC0gVE9UQUxgLCBgQkFMTE9UUyBDQVNUIC0gVE9UQUxgLCBgVk9URVIgVFVSTk9VVCAtIFRPVEFMYCwgZm9sbG93ZWQgYnkgZml2ZSByb3dzIHdpdGggaW5mb3JtYXRpb24gb24gc3RyYWlnaHQgdGlja2V0IHZvdGluZyBmb3IgZWFjaCBvZiBmaXZlIHBhcnRpZXMgKHdoaWNoIGNhbiBiZSBpZ25vcmVkIGZvciBvdXIgcHVycG9zZXMgaGVyZSkuIEZyb20gdGhlcmUsIGVhY2ggcm93IGNvbnRhaW5zIGluZm9ybWF0aW9uIGZvciBhIHNpbmdsZSBjYW5kaWRhdGUgaW4gYSBwYXJ0aWN1bGFyIGNvbnRlc3QuIFdlIGNhcmUgc3BlY2lmaWNhbGx5IGFib3V0IHRoZSBzdGF0ZXdpZGUgY29udGVzdHMgdGhhdCBhcmUgaGVsZCBpbiBldmVyeSBwcmVjaW5jdCBoZXJlOiBQcmVzaWRlbnQgKGBDb250ZXN0PT0iUFJFU0lERU5USUFMIEVMRUNUT1JTImApLCBVUyBTZW5hdG9yIChgQ29udGVzdD09IlVOSVRFRCBTVEFURVMgU0VOQVRPUiJgKSwgQXR0b3JuZXkgR2VuZXJhbCAoYENvbnRlc3Q9PSJBVFRPUk5FWSBHRU5FUkFMImApLCBBdWRpdG9yIEdlbmVyYWwgKGBDb250ZXN0PT0iQVVESVRPUiBHRU5FUkFMImApLCBhbmQgVHJlYXN1cmVyIChgQ29udGVzdD09IlNUQVRFIFRSRUFTVVJFUiJgKS4gQWxsIG9mIG91ciBjYWxjdWxhdGlvbnMgYXJlIGJhc2VkIG9uIHRoZSBudW1iZXIgaW4gdGhlIGBDb3VudGAgdmFyaWFibGUuCgojIyBTb2x2ZSBpbiB0aHJlZSBwaWVjZXMKClRoaXMgc29sdXRpb24gY3JlYXRlcyB0aHJlZSBkYXRhLnRhYmxlcyB0aGF0IGl0IGpvaW5zIHRvZ2V0aGVyIGluIHRoZSBsYXN0IHN0ZXA6IHRoZSB0b3RhbCB2b3RlcywgdGhlIHR3by1wYXJ0eSBzaGFyZXMsIGFuZCB0aGUgcm9sbG9mZnMuIEVhY2ggb2YgdGhlc2UgZGF0YS50YWJsZXMgc2hvdWxkIHVsdGltYXRlbHkgaGF2ZSA5MSByb3dzLgoKIyMjIFRhYmxlIDE6IFRvdGFsIHZvdGVzIGJ5IHByZWNpbmN0CgpDcmVhdGluZyB0aGUgdG90YWwgdm90ZSB0YWJsZSByZXF1aXJlcyBqdXN0IHBpY2tpbmcgdGhlIHJpZ2h0IHJvd3MgYW5kIHJlbGFiZWxpbmcgdGhlIENvdW50IHZhcmlhYmxlLgoKYGBge3J9ClRvdC5kdCA8LSByYXdkYXRhLmR0W0NvbnRlc3Q9PSJCQUxMT1RTIENBU1QgLSBUT1RBTCIsLihQcmVjTm8sUHJlY05hbWUsIFRvdD1Db3VudCldClRvdC5kdApgYGAKCiMjIyBUYWJsZSAyOiBSb2xsb2ZmcyBpbiBkb3duLWJhbGxvdCByYWNlcwoKQXMgd2l0aCB0aGUgdGlkeXZlcnNlIHNvbHV0aW9uLCB0aGVyZSBhcmUgdHdvIG5vbnRyaXZpYWwgc3RlcHMgaGVyZS4gVGhlIGZpcnN0IGlzIHRoZSBncm91cGVkIHN1bW1hcnkgc3RlcCAodGhlIHRoaXJkIGFzc2lnbm1lbnQgYmVsb3cpLCB3aGVyZSB2b3RlIGNvdW50cyBhcmUgc3VtbWVkIGJ5IFByZWNpbmN0IE51bWJlciBhbmQgQ29udGVzdC4gVGhpcyBpcyBtb3JlIG9yIGxlc3MgdGhlIGNvcmUgcHJvY2VzcyBpbiBgZGF0YS50YWJsZWAsIHNvIGl0cyBpbXBsZW1lbnRhdGlvbiBpcyB2ZXJ5IGNvbXBhY3QuIFRoZSBzZWNvbmQgaXMgdGhlIGBkY2FzdGAgY29tbWFuZCB3aGljaCBkb2VzIHRoZSBlcXVpdmFsZW50IG9mIHdoYXQgYHBpdm90X3dpZGVyYCAoYHNwcmVhZGApIGRvZXMgaW4gdGhlIHRpZHl2ZXJzZS4gVGhlIG5vdGF0aW9uIGhlcmUgdXNlcyBhIGZvcm11bGEgKGhlcmUgYFByZWNObyB+IENvbmApIHRvIGRlZmluZSB0aGUgImtleSIgb24gd2hpY2ggdGhlIHRhYmxlIGlzIGNhc3QuCgpgYGB7cn0KIyBDcmVhdGUgZGF0YS50YWJsZSB3aXRoIGp1c3QgdmFycyBQcmVjTm8sIENvdW50LCBhbmQgQ29uIChhYmJyZXZpYXRlZCBDb250ZXN0KQpOZWVkZWRDb2x1bW5zUm93cy5kdCA8LSByYXdkYXRhLmR0WywuKFByZWNObyxDb3VudCxDb249c3Vic3RyKENvbnRlc3QsMSwzKSldICMgcGljayBjb2x1bW5zLCBhYmJyZXZpYXRlCk5lZWRlZENvbHVtbnNSb3dzLmR0IDwtIE5lZWRlZENvbHVtbnNSb3dzLmR0W0NvbiAlaW4lIGMoIlBSRSIsIlVOSSIsIkFUVCIsIkFVRCIsIlNUQSIpLF0gIyBwaWNrIHJvd3MKTmVlZGVkQ29sdW1uc1Jvd3MuZHQgICAjICAyMDkzIHJvd3MsIDMgY29sdW1ucwoKIyBDcmVhdGUgZ3JvdXBlZCBzdW1tYXJ5IGRhdGEudGFibGUgd2l0aCBUb3RhbCB2b3RlcyBieSBQcmVjaW5jdC1Db250ZXN0IApQcmVjaW5jdENvbnRlc3RzTG9uZy5kdCA8LSBOZWVkZWRDb2x1bW5zUm93cy5kdFssLihDb25Ub3QgPSBzdW0oQ291bnQpKSwgYnk9LihQcmVjTm8sQ29uKV0KUHJlY2luY3RDb250ZXN0c0xvbmcuZHQgPC0gUHJlY2luY3RDb250ZXN0c0xvbmcuZHRbLC4oUHJlY05vLENvbixDb25Ub3QpXSAjIGNvdWxkIGJlICJjaGFpbmVkIiB3IGFib3ZlClByZWNpbmN0Q29udGVzdHNMb25nLmR0ICAgICAjIDQ1NSByb3dzLCA2IGNvbHVtbnMKCiMgIkNhc3QiIHRoZSBkYXRhIGJ5IENvbnRlc3QgKHNwcmVhZCBmcm9tIGxvbmcgdG8gd2lkZSkKUHJlY2luY3RDb250ZXN0c1dpZGUuZHQgPC0gZGNhc3QoUHJlY2luY3RDb250ZXN0c0xvbmcuZHQsIFByZWNObyB+IENvbiwgdmFsdWUudmFyID0gIkNvblRvdCIpClByZWNpbmN0Q29udGVzdHNXaWRlLmR0ICAgICAjIDkxIHJvd3MsIDYgY29sdW1ucwoKIyBDYWxjdWxhdGUgUm9sbG9mZiBWYXJpYWJsZXMKUm9sbG9mZnMuZHQgPC0gUHJlY2luY3RDb250ZXN0c1dpZGUuZHRbLC4oUHJlY05vLCAgICAgICAgICAgICAgICAgIyBLZWVwIFByZWNpbmN0IE51bWJlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBST1Nlbj0xMDAqKDEtVU5JL1BSRSksICAjIFJvbGxvZmYgZm9yIFVTIFNlbmF0b3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUk9BdHQ9MTAwKigxLUFUVC9QUkUpLCAgIyBSb2xsb2ZmIGZvciBBdHRvcm5leSBHZW5lcmFsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJPQXVkPTEwMCooMS1BVUQvUFJFKSwgICMgUm9sbG9mZiBmb3IgQXVkaXRvciBHZW5lcmFsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBST1RyZT0xMDAqKDEtU1RBL1BSRSkpXSAjIFJvbGxvZmYgZm9yIFRyZWFzdXJlcgpSb2xsb2Zmcy5kdApgYGAKCiMjIyBUYWJsZSAzOiBEZW1vY3JhdGljIHNoYXJlIG9mIHR3by1wYXJ0eSB2b3RlCgpJbiB0aGUgYHRpZHl2ZXJzZWAgdmVyc2lvbiwgd2UgaGFkIHRvIGNyZWF0ZSBhIHNpbmdsZSBjb2x1bW4gb2YgQ29udGVzdC1QYXJ0eSBpbmRpY2F0b3JzIHRvIGFjdCBhcyBhIGtleS4gV2l0aCBgZGF0YS50YWJsZWAgd2UgY2FuIHVzZSB0d28ga2V5cywganVzdCBsaXN0aW5nIHRoZW0gb24gdGhlIHJpZ2h0IHNpZGUgb2YgdGhlIGZvcm11bGEgKGhlcmUsIGBQcmVjTm8gfiBDb24gKyBQdHlgKSwgYW5kIHRoZSBrZXlzIGZyb20gYWxsIHBhaXJpbmdzIGFyZSBjb25zdHJ1Y3RlZCBhdXRvbWF0aWNhbGx5LiBTbyB0aGlzIGlzIGEgY291cGxlIG9mIGNvbW1hbmRzIG1vcmUgY29tcGFjdCBpbiBgZGF0YS50YWJsZS5gCgpgYGB7cn0KIyBDcmVhdGUgZGF0YS50YWJsZSB3aXRoIGp1c3QgdmFycyBQcmVjTm8sIENvdW50LCBhbmQgQ29uIChhYmJyZXZpYXRlZCBDb250ZXN0KQpOZWVkZWRDb2x1bW5zUm93cy5kdCA8LSByYXdkYXRhLmR0WywuKFByZWNObyxQdHk9c3Vic3RyKFBhcnR5LDEsMyksQ29uPXN1YnN0cihDb250ZXN0LDEsMyksIENvdW50KV0gIyBwaWNrIGNvbHVtbnMsIGFiYnJldmlhdGUKTmVlZGVkQ29sdW1uc1Jvd3MuZHQgPC0gTmVlZGVkQ29sdW1uc1Jvd3MuZHRbUHR5ICVpbiUgYygiREVNIiwiUkVQIikgJiBDb24gJWluJSBjKCJQUkUiLCJVTkkiLCJBVFQiLCJBVUQiLCJTVEEiKSxdICMgcGljayByb3dzCk5lZWRlZENvbHVtbnNSb3dzLmR0ICAgIyAgOTEwIHJvd3MsIDMgY29sdW1ucwoKIyAiQ2FzdCIgdGhlIGRhdGEgYnkgUGFydHkgJiBDb250ZXN0IChzcHJlYWQgZnJvbSBsb25nIHRvIHdpZGUpCiMgICAgTm90ZSB0aGF0IGRhdGEudGFibGUgaGFzIG5vIHByb2JsZW0gYXV0b21hdGljYWxseSBjcmVhdGluZyBhIGtleSBiYXNlZCBvbiB0d28gY29sdW1ucwpQcmVjUGFydHlDb250ZXN0c1dpZGUuZHQgPC0gZGNhc3QoTmVlZGVkQ29sdW1uc1Jvd3MuZHQsIFByZWNObyB+IENvbiArIFB0eSwgdmFsdWUudmFyID0gIkNvdW50IikKUHJlY1BhcnR5Q29udGVzdHNXaWRlLmR0ICAgICAjIDkxIHJvd3MsIDYgY29sdW1ucwoKIyBDYWxjdWxhdGUgRGVtb2NyYXRpYyBUd28tUGFydHkgU2hhcmUgVmFyaWFibGVzCkRlbVR3b1BhcnR5U2hhcmVzLmR0IDwtIFByZWNQYXJ0eUNvbnRlc3RzV2lkZS5kdFssLihQcmVjTm8sICAgICAgICAgICAgICAgICAjIEtlZXAgUHJlY2luY3QgTnVtYmVyCiAgICAgICAgICAgICAgICAgICAgICAgICBEMlByZT0xMDAqKFBSRV9ERU0vKFBSRV9ERU0rUFJFX1JFUCkpLCMgRDJQcmUgPSBEZW0gMiBwYXJ0eSBzaGFyZSwgUHJlc2lkZW50CiAgICAgICAgICAgICAgICAgICAgICAgICBEMlNlbj0xMDAqKFVOSV9ERU0vKFVOSV9ERU0rVU5JX1JFUCkpLCMgRDJQcmUgPSBEZW0gMiBwYXJ0eSBzaGFyZSwgVVMgU2VuYXRvcgogICAgICAgICAgICAgICAgICAgICAgICAgRDJBdHQ9MTAwKihBVFRfREVNLyhBVFRfREVNK0FUVF9SRVApKSwjIEQyUHJlID0gRGVtIDIgcGFydHkgc2hhcmUsIEF0dG9ybmV5IEdlbgogICAgICAgICAgICAgICAgICAgICAgICAgRDJBdWQ9MTAwKihBVURfREVNLyhBVURfREVNK0FVRF9SRVApKSwjIEQyUHJlID0gRGVtIDIgcGFydHkgc2hhcmUsIEF1ZGl0b3IgR2VuCiAgICAgICAgICAgICAgICAgICAgICAgICBEMlRyZT0xMDAqKFNUQV9ERU0vKFNUQV9ERU0rU1RBX1JFUCkpKV0jIEQyUHJlID0gRGVtIDIgcGFydHkgc2hhcmUsIFN0YXRlIFRyZWFzCkRlbVR3b1BhcnR5U2hhcmVzLmR0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKYGBgCgojIyBGaW5hbCBvdXRwdXQgdGFibGU6IE1lcmdlZCBEYXRhCgpOb3cgd2UgYG1lcmdlYCB0aGUgdGFibGVzIGFuZCBmb3JtYXQgdGhlIFByZWNpbmN0IE51bWJlciBhbmQgTmFtZSBhcyByZXF1ZXN0ZWQgaW4gdGhlIEV4ZXJjaXNlLgoKYGBge3J9CkV4M0RhdGEuZHQgPC0gbWVyZ2UoVG90LmR0LFJvbGxvZmZzLmR0LCBieT0iUHJlY05vIikgICAgICAgICAgICAgICMgTWVyZ2UgVG90YWwgd2l0aCBSb2xsb2ZmcwpFeDNEYXRhLmR0IDwtIG1lcmdlKEV4M0RhdGEuZHQsRGVtVHdvUGFydHlTaGFyZXMuZHQsIGJ5PSJQcmVjTm8iKSAjIE1lcmdlIHRoYXQgd2l0aCBEZW0gMi1wYXJ0eSBTaGFyZXMKRXgzRGF0YS5kdFssUHJlY05vIDo9IGFzLmludGVnZXIoUHJlY05vKV0gICAgICAgICAgICAgICAgICAgICAgICAgIyBNYWtlIFByZWNpbmN0IG51bWJlciBhIG51bWJlcgpFeDNEYXRhLmR0WyxQcmVjTmFtZSA6PSBzdWJzdHIoUHJlY05hbWUsNCxzdG9wPTQwTCldICAgICAgICAgICAgICAjIFN0cmlwIGR1cGxpY2F0ZSBpbmZvIGZyb20gTmFtZQpFeDNEYXRhLmR0CmBgYAoKCg==