1 Introduction

With the upcoming 2024 United States presidential election approaching, analysts are consistently searching for metrics to predict the outcomes accurately and early. While individual voter motivations are varied and nuanced, a review of the common economic measures in relation to county election results of the past can potentially provide insight into the trends of the future. In order to review these potential trends, the following data was utilized for analysis:

While these datasets include a litany of information, the areas of focus in this exploratory data analysis are the impacts of county level unemployment rate for the current election year, county level poverty rate for the year prior to the current election year, higher education rate for the four years prior to the current election year. To note, years prior to the current election year are utilized due to data availability and the impact of previous year’s socioeconomic conditions on the current voter’s opinions. The focus of this exploratory data analysis centers around analyzing potential correlations and predictive values as related to the 2020 presidential election only.

Relevant Note

All data preparation code is copied in the appendix as well as included in line with the report, visualization code is only included in line with the report. To view any of the code in line with each location it is used in the report, select the Show buttons on the right hand side of the page.

2 Data Preparation

Prior to any data visualization and analysis, all datasets were loaded into RStudio (v2024.09.0 Build 375) to prepare unique relaional datasets. A unique identifier of FIPS Code was utilized across all four (4) datasets, as well as to link the relevant geoJSON data for county mapping.

2.1 Create Relational Datasets

2.1.1 Presidential Election Data (2020 or most recent year available)

The raw presidential election data was obtained from from the publicly available MIT Election Lab Data Set: County Presidential Election Returns 2000-2020.

Election data was read into RStudio as election_raw and cleaned utilizing the following steps to create the relational dataset election:

  • Data was grouped by the county FIPS code
  • Data was sorted by descending Year
  • Data was filtered by political Party for only Democrat and Republican as the analysis focuses on the potential impacts on county presidential election winners. No other parties have ever won the presidency and thus were omitted.
    • To note: blank values were not omitted
  • Unnecessary variables were removed
  • Data was filtered for 2020 or the most recent year with available data if 2020 was not available

A searchable preview of the relational dataset election is available in the data table below.

##read in the election data as election_raw

election_raw <- read.csv('https://raw.githubusercontent.com/nlepera/sta551/refs/heads/main/HW01/data/countypresidential_election_2000-2020.csv')

##clean the election data 

election <- election_raw %>% 
  group_by(county_fips) %>%   ##group by county FIPS code
  arrange (desc(year)) %>%    ##sort by descending year
  filter (party == "DEMOCRAT" | party == "REPUBLICAN") %>%    ##filter for only dem & rep
  subset(, -c(state_po, office, version, mode)) %>%   ##remove unnecessary columns
  slice (1:2) %>%   ##pull top 2 values for each group (county_fips sorted by desc year) pulling both dem & rep for each county at max year available
  arrange (desc(candidatevotes)) %>%    ##re filter to sort by group, with highest candidate votes on top
  slice (1) %>%    ##only keep value for highest candidate votes (winner) per county
  rename (FIPS = county_fips)   ## set ID name for outer join
  
DT::datatable(election)

2.1.2 Unemployment Data (2020 or most recent year available)

The raw unemployment data was obtained from from the publicly available USDA Economic Research Service County-level Dataset: Unemployment.

Unemployment data was read into RStudio as unemploy_raw and cleaned utilizing the following steps to create the relational dataset unemploy:

  • Data was filtered for text containing the string Unemployment in the Attribute variable data values to remove other unnecessary data types.
  • In order to extract the year from the Attribute values, a new column was created by extracting the last four (4) characters of the Attribute values.
  • Data was grouped by the county FIPS code
  • Data was sorted by descending Year
  • Data was filtered for 2020 or the most recent year with available data if 2020 was not available
  • Unnecessary variables were removed

A searchable preview of the relational dataset unemploy is available in the data table below.

## read in the unemployment data as unemploy_raw

unemploy_raw <- read.csv('https://raw.githubusercontent.com/nlepera/sta551/refs/heads/main/HW01/data/Unemployment.csv')

## clean the unemployment data
unemploy <- unemploy_raw %>% 
  filter (grepl('Unemployment', Attribute)) %>%   #use dplyr package to use filter(grepl()) to filter for string of text to select only the unemployment rate values
  mutate (year = str_sub(Attribute, -4)) %>%  ##create a new column using mutate and stringr to create a new column from the last 4 values of the attribute column, to extract the year (str_sub -> stringr_subset)
  group_by (FIPS_Code) %>%  ##group by FIPS code before sorting to get max year
  arrange (desc(year)) %>%  ## sort descending year to put max available at the top
  slice(1) %>%   ##slice top value to keep only max year for each FIPS code
  subset (, c(FIPS_Code, Value)) %>%   ##remove unnecessary columns
  rename ("Unemployment_Rate (%)" = Value, FIPS = FIPS_Code)    ##rename column to reflect unemployment rate & set ID name for outer join

DT::datatable(unemploy)

2.1.3 Poverty Data (2019)

The raw poverty data was obtained from from the publicly available USDA Economic Research Service County-level Dataset: Poverty.

Poverty data was read into RStudio as poverty_raw and cleaned utilizing the following steps to create the relational dataset poverty:

  • Data was filtered for text containing the string PCTPOVALL_2019 in the Attribute variable data values to remove other unnecessary data types.
  • Unnecessary variables were removed

A searchable preview of the relational dataset unemploy is available in the data table below.

## read in the poverty data as poverty_raw

poverty_raw <- read.csv('https://raw.githubusercontent.com/nlepera/sta551/refs/heads/main/HW01/data/PovertyEstimates.csv')

##clean the poverty data
poverty <- poverty_raw %>% 
  filter (Attribute == "PCTPOVALL_2019") %>%  ##Filter for 2019 poverty values
  subset (, c(FIPStxt, Value)) %>%  ##remove unneccessary columns
  rename ("Poverty_Rate (%)" = Value, FIPS = FIPStxt)   ##rename Value column to Poverty_Rate (%) and set ID name for outer join

DT::datatable(poverty)

2.1.4 Education Data (2015 - 2019)

The raw education data was obtained from from the publicly available USDA Economic Research Service County-level Dataset: Education.

Unemployment data was read into RStudio as edu_raw and cleaned utilizing the following steps to create the relational dataset edu:

  • Unnecessary variables were removed
  • Columns were renamed to truncated values for ease of use:
    • No_HS = # of persons in the county with less than a high school diploma
    • HS = # of persons in the county with a high school diploma only
    • AS_SomeCol = # of persons in the county with either some college credit or an Associate’s Degree
    • BS_More = # of persons in the county with a Bachelor’s Degree or higher
  • Data cleaned to remove commas from numbers and convert from character to numeric values

A searchable preview of the relational dataset unemploy is available in the data table below.

## read in the unemployment data as edu_raw

edu_raw <- read.csv('https://nlepera.github.io/sta551/HW01/data/Education.csv', stringsAsFactors = FALSE)


#clean education data
edu <- edu_raw %>% 
  subset (, c(FIPS.Code, Less.than.a.high.school.diploma..2015.19, High.school.diploma.only..2015.19, Some.college.or.associate.s.degree..2015.19,  Bachelor.s.degree.or.higher..2015.19)) %>%  ##remove irrelevant columns
  rename ( "No_HS" = Less.than.a.high.school.diploma..2015.19, "HS" = High.school.diploma.only..2015.19, "AS_SomeCol" = Some.college.or.associate.s.degree..2015.19,    "BS_More" = Bachelor.s.degree.or.higher..2015.19, FIPS = FIPS.Code)  ##rename for ease and set ID name for outer join
edu$No_HS = gsub(',', '', edu$No_HS) ##remove commas from number with commas saved as charachters
edu$HS = gsub(',', '', edu$HS)
edu$AS_SomeCol = gsub(',', '', edu$AS_SomeCol)
edu$BS_More = gsub(',', '', edu$BS_More)
edu$No_HS = as.numeric(edu$No_HS)  ##transform to numeric to aggregate
edu$HS = as.numeric(edu$HS)
edu$AS_SomeCol = as.numeric(edu$AS_SomeCol)
edu$BS_More = as.numeric(edu$BS_More)
  
DT::datatable(edu)

2.2 Merge Relational Datasets & Aggregate data

2.2.1 Merge the relational datasets using county FIPS codes

Once all four (4) relational datasets (edu, election, poverty, and unemploy) were created, a new dataset named all.data was created by completing multiple full join functions. All datasets were merged using the county FIPS code as the unique identifier conserved across all datasets.

##merge election data & education data as election.education 
election.education <- full_join (
  x = election,
  y = edu,
  by = "FIPS",
  keep = FALSE
)


##merge election.education & poverty data as election.education.poverty
election.education.poverty <- full_join(
  x = election.education,
  y = poverty,
  by = "FIPS",
  keep = FALSE
)

##merge election.education.poverty & unemployment data as all.data
all.data <- full_join (
  x = election.education.poverty,
  y = unemploy,
  by = "FIPS",
  keep = FALSE
)

2.2.2 Aggregate the Education Data into % Higher Ed

As the educational data provided for each county consisted of the total number of persons in each county that received each of the four tiers of education, the data was aggregated to allow for cleaner analysis and comparison. In order to aggregate the educational data, the four education variables (No_HS, HS, AS_SomeCol, & BS_More) were aggregated to create a new variable Higher_Ed_Percent to measure the percentage of the population surveyed that completed either some college credit, an Associate’s Degree, a Bachelor’s Degree, or additional graduate level degrees.

This higher education metric was created as a measure of the average socioeconomic status of the counties being analyzed.

all.data <- all.data  %>% 
  filter(FIPS != 0) %>% ##filter out values with no associated county or state name
  filter(FIPS != 1000) %>% 
  mutate (Higher_Ed_Percent = (((`AS_SomeCol` + `BS_More`)/(`No_HS`+ `HS` + `AS_SomeCol` + `BS_More`))*100))  ##% of pop with higher ed.

3 Visualizing and Analyzing the Data

3.1 Mapping the full data

For an initial visualization of the data as a whole, the all.data dataset was mapped by county utilizing an interactive leaflet map. Due to the size of the dataset and number of counties, color specific data is not visible until the map is zoomed in to the appropriate county level. Until zoomed in the color scale (green to red) represents the number of counties nested in that group until the map is zoomed in further. Once fully zoomed in the color of the unnumbered circles (red and blue) represent the winning party (Republican and Democrat respectively) in each county for the 2020 presidential election.

county_data_raw <- sf::read_sf("https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json") ##pull in map of FIPS codes 

FIPSconvert_raw <- sf::read_sf("https://nlepera.github.io/sta551/HW01/data/fips2geocode.csv") ##pull in key of FIPS codes to lat/long

county_data <- county_data_raw %>%  #clean county data to list FIPS as numeric & match name to key for join
  mutate (id = as.numeric(as.character(id))) %>% 
  rename (FIPS = id)

FIPSconvert <- FIPSconvert_raw %>%  
  mutate (fips = as.numeric(as.character(fips)), lon = as.numeric(as.character(lon)), lat = as.numeric(as.character(lat))) %>% 
  rename (FIPS = fips, Longitude = lon, Latitude = lat)

all.data.join <- full_join(
  x = all.data,
  y = FIPSconvert,
  by = "FIPS",
  keep = FALSE
) %>% 
  ungroup()

Interactive Map of County Data

pal <- colorFactor(c("blue", "red"), domain = c("REPUBLICAN", "DEMOCRAT"))

title <- "
<style>
  .custom-title {
    color: white;
    padding: 0px;
    border-radius: 0px;
    text-align: right;
  }
</style>
Election Results, Education Rates, Unemployment Rates & Poverty Rates
<br>Zoom in to view each county individually
<br>Select a county's circle to view full data"

leaflet() %>% 
  setView(lng=-98.5795, lat=39.8283, zoom = 4) %>% ##set view to mid US
  addProviderTiles(providers$CartoDB.DarkMatter) %>%  ##set map to dark mode
  addControl(html = title, 
             position = "topright", 
             className = "custom-title") %>% 
  addPolygons(data = county_data,  ##add FIPS polygons
              color = "yellow",
              fill = FALSE,
              weight = 0.25) %>% 
  addCircleMarkers(data = all.data.join,
                   color = ~pal(party),
                   radius = 20,
                   fillOpacity = 0.5,
                   popup = ~popupTable(all.data),
                   clusterOptions = markerClusterOptions(maxClusterRadius = 50))

3.2 Plot variable distribution

Before completing any data analysis the distribution of the three numeric variables of interest was reviewed, controlling for the the winning party (Republican or Democrat) in each county for the 2020 presidential election.

Both unemployment and higher education rates appear to have normal distributions while poverty rates appear to have closer to a gamma distribution. A greater frequency of counties with democratic candidate winners was seen for all three (3) variables in question.

ggplot (all.data, aes(x = `Unemployment_Rate (%)`, fill = `party`)) +
  geom_histogram(inherit.aes = TRUE, color = "black") +
  ggtitle ("Distribution of Unemployment Rates by County Election Winner") +
  xlab ("County Unemployment Rate (x)
        [% of County Pop.]") + 
  ylab ("Counties W/ Unemployment Rate = x")  +
  scale_fill_manual(values = c("blue", "red")) +
  theme(legend.position = c(0.8, 0.8))

ggplot (all.data, aes(x = `Poverty_Rate (%)`, fill = `party`)) +
  geom_histogram(inherit.aes = TRUE, color = "black") + 
  ggtitle ("Distribution of Poverty Rates by County Election Winner") + 
  xlab ("County Poverty Rate (x)
        [% of County Pop.]") +
  ylab ("Counties W/ Poverty Rate = x")  +
  scale_fill_manual(values = c("blue", "red")) +
  theme(legend.position = c(0.8, 0.8), height = "200px")

ggplot (all.data, aes(x = `Higher_Ed_Percent`, fill = `party`)) + 
  geom_histogram(inherit.aes = TRUE, color = "black") + 
  ggtitle ("Distribution of Higher Education Rates by County Election Winner") + 
  xlab ("County Higher Ed. Rate (x)
        [% of County Pop.] ") + 
  ylab ("Counties W/ Higher Ed. Rate = x")  +
  scale_fill_manual(values = c("blue", "red")) +
  theme(legend.position = c(0.8, 0.8))

3.3 Plot Relationship Between Numeric Variables

To determine the potential relationships between the three numeric variables of interest (Unemployment Rate, Poverty Rate, and Higher Education Rate) were plotted against each other and reviewed utilizing a basic linear regression model for the normally distributed data and a GLM gamma regresion model for the gamma distributed data.

ggplot(data = all.data, aes(x =all.data$`Unemployment_Rate (%)`, y=all.data$`Poverty_Rate (%)`, color=`party`)) +
  geom_point(alpha = 0.25) +
    ggtitle ("Unemployment Rates vs. Poverty Rates by Election Winning Party") + 
    xlab ("County Unemployment Rate [% of County Pop.] (x)") + 
    ylab ("County Poverty Rate [% of County Pop.] (y)")  +
  scale_color_manual(values = c("blue", "red")) +
  stat_smooth(method = lm, se=FALSE, size = 0.1) 

pov_unem <- lm(`Poverty_Rate (%)`~`Unemployment_Rate (%)` , data = all.data)
summary(pov_unem)

Call:
lm(formula = `Poverty_Rate (%)` ~ `Unemployment_Rate (%)`, data = all.data)

Residuals:
    Min      1Q  Median      3Q     Max 
-21.355  -3.713  -0.826   2.871  34.731 

Coefficients:
                        Estimate Std. Error t value Pr(>|t|)    
(Intercept)              9.40945    0.30570   30.78   <2e-16 ***
`Unemployment_Rate (%)`  0.74165    0.04289   17.29   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 5.519 on 3189 degrees of freedom
  (128 observations deleted due to missingness)
Multiple R-squared:  0.08572,   Adjusted R-squared:  0.08544 
F-statistic:   299 on 1 and 3189 DF,  p-value: < 2.2e-16
ggplot(data = all.data, aes(x =all.data$`Higher_Ed_Percent`, y=all.data$`Poverty_Rate (%)`, color=`party`)) +
  geom_point(alpha = 0.25) +
    ggtitle ("Percent of Population w/ Higher Education vs.
             Poverty Rates by Election Winning Party") + 
    xlab ("County Higher Ed. Rate [% of County Pop.] (x)") + 
    ylab ("County Poverty Rate [% of County Pop.] (y)")  +
  scale_color_manual(values = c("blue", "red")) +
  stat_smooth(method = glm, se=FALSE, size = 0.1) 

highed_pov <- glm(`Higher_Ed_Percent`~`Poverty_Rate (%)`, data = all.data)
summary(highed_pov)

Call:
glm(formula = Higher_Ed_Percent ~ `Poverty_Rate (%)`, data = all.data)

Coefficients:
                   Estimate Std. Error t value Pr(>|t|)    
(Intercept)        68.37787    0.41629  164.26   <2e-16 ***
`Poverty_Rate (%)` -1.07151    0.02681  -39.97   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

(Dispersion parameter for gaussian family taken to be 76.33665)

    Null deviance: 365417  on 3190  degrees of freedom
Residual deviance: 243438  on 3189  degrees of freedom
  (128 observations deleted due to missingness)
AIC: 22893

Number of Fisher Scoring iterations: 2
ggplot(data = all.data, aes(x =all.data$`Higher_Ed_Percent`, y=all.data$`Unemployment_Rate (%)`, color=`party`)) +
  geom_point(alpha = 0.25) +
    ggtitle ("Percent of Population w/ Higher Education vs.
             Unemployment Rates by Election Winning Party") + 
    xlab ("County Higher Ed. Rate [% of County Pop.] (x)") + 
    ylab ("County Unemployment Rate [% of County Pop.] (y)")  +
  scale_color_manual(values = c("blue", "red")) +
  stat_smooth(method = lm, se=FALSE, size = 0.1) 

highed_unem <- lm(`Higher_Ed_Percent`~`Unemployment_Rate (%)`, data = all.data)
summary(highed_unem)

Call:
lm(formula = Higher_Ed_Percent ~ `Unemployment_Rate (%)`, data = all.data)

Residuals:
    Min      1Q  Median      3Q     Max 
-47.747  -7.579  -0.476   6.880  36.109 

Coefficients:
                        Estimate Std. Error t value Pr(>|t|)    
(Intercept)             59.02069    0.56472  104.51   <2e-16 ***
`Unemployment_Rate (%)` -0.92418    0.07816  -11.82   <2e-16 ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Residual standard error: 10.51 on 3268 degrees of freedom
  (49 observations deleted due to missingness)
Multiple R-squared:  0.04102,   Adjusted R-squared:  0.04073 
F-statistic: 139.8 on 1 and 3268 DF,  p-value: < 2.2e-16

4 Conclusions

While all three investigated potential correlations indicated a high level of statistic significance with p values well below 0.05, both the Unemployment Rates vs. Poverty Rates by Election Winning Party and Percent of Population w/ Higher Education vs. Unemployment Rates by Election Winning Party models illustrated a poor fit with very low R squared values. The Percent of Population w/ Higher Education vs. Poverty Rates by Election Winning Party also illustrated a poor fit but had the highest r squared value indicating reduced variance in this model. No clustering was noted along the party variables.

Overall a correlation between unemployment rates, poverty rates, higher education rates, and county winning party was not illustrated.

5 Code


##read in the election data as election_raw

election_raw <- read.csv('https://raw.githubusercontent.com/nlepera/sta551/refs/heads/main/HW01/data/countypresidential_election_2000-2020.csv')

##clean the election data 

election <- election_raw %>% 
  group_by(county_fips) %>%   ##group by county FIPS code
  arrange (desc(year)) %>%    ##sort by descending year
  filter (party == "DEMOCRAT" | party == "REPUBLICAN") %>%    ##filter for only dem & rep
  subset(, -c(state_po, office, version, mode)) %>%   ##remove unnecessary columns
  slice (1:2) %>%   ##pull top 2 values for each group (county_fips sorted by desc year) pulling both dem & rep for each county at max year available
  arrange (desc(candidatevotes)) %>%    ##re filter to sort by group, with highest candidate votes on top
  slice (1) %>%    ##only keep value for highest candidate votes (winner) per county
  rename (FIPS = county_fips)   ## set ID name for outer join
  
DT::datatable(election)

## read in the unemployment data as unemploy_raw

unemploy_raw <- read.csv('https://raw.githubusercontent.com/nlepera/sta551/refs/heads/main/HW01/data/Unemployment.csv')

## clean the unemployment data
unemploy <- unemploy_raw %>% 
  filter (grepl('Unemployment', Attribute)) %>%   #use dplyr package to use filter(grepl()) to filter for string of text to select only the unemployment rate values
  mutate (year = str_sub(Attribute, -4)) %>%  ##create a new column using mutate and stringr to create a new column from the last 4 values of the attribute column, to extract the year (str_sub -> stringr_subset)
  group_by (FIPS_Code) %>%  ##group by FIPS code before sorting to get max year
  arrange (desc(year)) %>%  ## sort descending year to put max available at the top
  slice(1) %>%   ##slice top value to keep only max year for each FIPS code
  subset (, c(FIPS_Code, Value)) %>%   ##remove unnecessary columns
  rename ("Unemployment_Rate (%)" = Value, FIPS = FIPS_Code)    ##rename column to reflect unemployment rate & set ID name for outer join

DT::datatable(unemploy)

## read in the poverty data as poverty_raw

poverty_raw <- read.csv('https://raw.githubusercontent.com/nlepera/sta551/refs/heads/main/HW01/data/PovertyEstimates.csv')

##clean the poverty data
poverty <- poverty_raw %>% 
  filter (Attribute == "PCTPOVALL_2019") %>%  ##Filter for 2019 poverty values
  subset (, c(FIPStxt, Value)) %>%  ##remove unneccessary columns
  rename ("Poverty_Rate (%)" = Value, FIPS = FIPStxt)   ##rename Value column to Poverty_Rate (%) and set ID name for outer join

DT::datatable(poverty)

## read in the unemployment data as edu_raw

edu_raw <- read.csv('https://nlepera.github.io/sta551/HW01/data/Education.csv', stringsAsFactors = FALSE)


#clean education data
edu <- edu_raw %>% 
  subset (, c(FIPS.Code, Less.than.a.high.school.diploma..2015.19, High.school.diploma.only..2015.19, Some.college.or.associate.s.degree..2015.19,  Bachelor.s.degree.or.higher..2015.19)) %>%  ##remove irrelevant columns
  rename ( "No_HS" = Less.than.a.high.school.diploma..2015.19, "HS" = High.school.diploma.only..2015.19, "AS_SomeCol" = Some.college.or.associate.s.degree..2015.19,    "BS_More" = Bachelor.s.degree.or.higher..2015.19, FIPS = FIPS.Code)  ##rename for ease and set ID name for outer join
edu$No_HS = gsub(',', '', edu$No_HS) ##remove commas from number with commas saved as charachters
edu$HS = gsub(',', '', edu$HS)
edu$AS_SomeCol = gsub(',', '', edu$AS_SomeCol)
edu$BS_More = gsub(',', '', edu$BS_More)
edu$No_HS = as.numeric(edu$No_HS)  ##transform to numeric to aggregate
edu$HS = as.numeric(edu$HS)
edu$AS_SomeCol = as.numeric(edu$AS_SomeCol)
edu$BS_More = as.numeric(edu$BS_More)
  
DT::datatable(edu)

##merge election data & education data as election.education 
election.education <- full_join (
  x = election,
  y = edu,
  by = "FIPS",
  keep = FALSE
)


##merge election.education & poverty data as election.education.poverty
election.education.poverty <- full_join(
  x = election.education,
  y = poverty,
  by = "FIPS",
  keep = FALSE
)

##merge election.education.poverty & unemployment data as all.data
all.data <- full_join (
  x = election.education.poverty,
  y = unemploy,
  by = "FIPS",
  keep = FALSE
)

all.data <- all.data  %>% 
  filter(FIPS != 0) %>% ##filter out values with no associated county or state name
  filter(FIPS != 1000) %>% 
  mutate (Higher_Ed_Percent = (((`AS_SomeCol` + `BS_More`)/(`No_HS`+ `HS` + `AS_SomeCol` + `BS_More`))*100))  ##% of pop with higher ed.

county_data_raw <- sf::read_sf("https://raw.githubusercontent.com/plotly/datasets/master/geojson-counties-fips.json") ##pull in map of FIPS codes 

FIPSconvert_raw <- sf::read_sf("https://nlepera.github.io/sta551/HW01/data/fips2geocode.csv") ##pull in key of FIPS codes to lat/long

county_data <- county_data_raw %>%  #clean county data to list FIPS as numeric & match name to key for join
  mutate (id = as.numeric(as.character(id))) %>% 
  rename (FIPS = id)

FIPSconvert <- FIPSconvert_raw %>%  
  mutate (fips = as.numeric(as.character(fips)), lon = as.numeric(as.character(lon)), lat = as.numeric(as.character(lat))) %>% 
  rename (FIPS = fips, Longitude = lon, Latitude = lat)

all.data.join <- full_join(
  x = all.data,
  y = FIPSconvert,
  by = "FIPS",
  keep = FALSE
) %>% 
  ungroup()
LS0tDQp0aXRsZTogIlByZXNpZGVudGlhbCBGaXRuZXNzIFRlc3Q6PGltZyBzcmM9XCJodHRwczovL25sZXBlcmEuZ2l0aHViLmlvL3N0YTU1MS9IVzAxL2ltZy9wZW5ndWluX2N1dGUucG5nXCIgc3R5bGU9XCJmbG9hdDogcmlnaHQ7IHdpZHRoOiAxMiVcIi8+Ig0Kc3VidGl0bGU6ICJUaGUgSW1wYWN0IG9mIExvY2FsIEVkdWNhdGlvbiwgUG92ZXJ0eSwgYW5kIFVuZW1wbG95bWVudCBSYXRlcyBvbiBDb3VudHkgUHJlc2lkZW50YWwgRWxlY3Rpb24gUmVzdWx0cyINCmF1dGhvcjogDQotIG5hbWU6IE5hdGFsaWUgTGVQZXJhDQogIGFmZmlsaWF0aW9uOiBXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSB8IFNUQTUxMSAtIEhXIDAxDQpkYXRlOiAiMjUgU2VwIDIwMjQiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICB0b2NfY29sbGFwc2U6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KLS0tDQoNCmBgYHtjc3MgZWNobyA9IEZBTFNFfQ0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgY29sb3I6IGRhcmttYWdlbnRhIDsNCn0NCmgxLnN1YnRpdGxlIHsgIC8qIFRpdGxlIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgcmVwb3J0IHRpdGxlICovDQogIGZvbnQtd2VpZ2h0OmJvbGQ7DQogIGNvbG9yOiBkYXJrbWFnZW50YSA7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGF1dGhvcnMgICovDQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBuYXZ5Ow0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXdlaWdodDpib2xkOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC13ZWlnaHQ6Ym9sZDsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCA0IHNlY3Rpb24gdGl0bGUgICovDQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCmg1IHsNCiAgY29sb3I6IHdoaXRlOw0KfQ0KDQpgYGANCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJkcGx5ciIpDQp9DQoNCmlmICghcmVxdWlyZSgic3RyaW5nciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic3RyaW5nciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJzdHJpbmdyIikNCn0NCg0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwbG90bHkiKQ0KfQ0KDQppZiAoIXJlcXVpcmUoImxlYWZsZXQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxlYWZsZXQiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibGVhZmxldCIpDQp9DQoNCmlmICghcmVxdWlyZSgiY3Jvc3N0YWxrIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJjcm9zc3RhbGsiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiY3Jvc3N0YWxrIikNCn0NCg0KDQppZiAoIXJlcXVpcmUoInJqc29uIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJyanNvbiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJyanNvbiIpDQp9DQppZiAoIXJlcXVpcmUoInRpZ3JpcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygidGlncmlzIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInRpZ3JpcyIpDQp9DQppZiAoIXJlcXVpcmUoInJhc3RlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicmFzdGVyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInJhc3RlciIpDQp9DQppZiAoIXJlcXVpcmUoImxlYWZwb3AiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxlYWZwb3AiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibGVhZnBvcCIpDQp9DQppZiAoIXJlcXVpcmUoImdncmlkZ2VzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3JpZGdlcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ3JpZGdlcyIpDQp9DQppZiAoIXJlcXVpcmUoImNvd3Bsb3QiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNvd3Bsb3QiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiY293cGxvdCIpDQp9DQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSkNCg0Kb3B0aW9ucyhEVC5vcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gNSwgc2Nyb2xsWCA9IFRSVUUpKQ0KYGBgDQo8YnI+DQoNCiMgSW50cm9kdWN0aW9uDQoNCldpdGggdGhlIHVwY29taW5nIDIwMjQgVW5pdGVkIFN0YXRlcyBwcmVzaWRlbnRpYWwgZWxlY3Rpb24gYXBwcm9hY2hpbmcsIGFuYWx5c3RzIGFyZSBjb25zaXN0ZW50bHkgc2VhcmNoaW5nIGZvciBtZXRyaWNzIHRvIHByZWRpY3QgdGhlIG91dGNvbWVzIGFjY3VyYXRlbHkgYW5kIGVhcmx5LiAgV2hpbGUgaW5kaXZpZHVhbCB2b3RlciBtb3RpdmF0aW9ucyBhcmUgdmFyaWVkIGFuZCBudWFuY2VkLCBhIHJldmlldyBvZiB0aGUgY29tbW9uIGVjb25vbWljIG1lYXN1cmVzIGluIHJlbGF0aW9uIHRvIGNvdW50eSBlbGVjdGlvbiByZXN1bHRzIG9mIHRoZSBwYXN0IGNhbiBwb3RlbnRpYWxseSBwcm92aWRlIGluc2lnaHQgaW50byB0aGUgdHJlbmRzIG9mIHRoZSBmdXR1cmUuICAgSW4gb3JkZXIgdG8gcmV2aWV3IHRoZXNlIHBvdGVudGlhbCB0cmVuZHMsIHRoZSBmb2xsb3dpbmcgZGF0YSB3YXMgdXRpbGl6ZWQgZm9yIGFuYWx5c2lzOg0KDQotIFByZXNpZGVudGlhbCBFbGVjdGlvbiBEYXRhLCBvYnRhaW5lZCBmcm9tIHRoZSBwdWJsaWNseSBhdmFpbGFibGUgPGEgaHJlZj0iaHR0cHM6Ly9lbGVjdGlvbmxhYi5taXQuZWR1L2RhdGEiPk1JVCBFbGVjdGlvbiBMYWIgRGF0YSBTZXRzPC9hPg0KLSBQZXIgQ291bnR5IFVuZW1wbG95bWVudCBEYXRhLCBvYnRhaW5lZCBmcm9tIHRoZSBwdWJsaWNseSBhdmFpbGFibGUgPGEgaHJlZj0iaHR0cHM6Ly93d3cuZXJzLnVzZGEuZ292L2RhdGEtcHJvZHVjdHMvY291bnR5LWxldmVsLWRhdGEtc2V0cy8iPlVTREEgRWNvbm9taWMgUmVzZWFyY2ggU2VydmljZSBDb3VudHktbGV2ZWwgZGF0YXNldHM8L2E+DQotIFBlciBDb3VudHkgUG92ZXJ0eSBEYXRhLCBvYnRhaW5lZCBmcm9tIHRoZSBwdWJsaWNseSBhdmFpbGFibGUgPGEgaHJlZj0iaHR0cHM6Ly93d3cuZXJzLnVzZGEuZ292L2RhdGEtcHJvZHVjdHMvY291bnR5LWxldmVsLWRhdGEtc2V0cy8iPlVTREEgRWNvbm9taWMgUmVzZWFyY2ggU2VydmljZSBDb3VudHktbGV2ZWwgZGF0YXNldHM8L2E+DQotIFBlciBDb3VudHkgRWR1Y2F0aW9uIERhdGEsIG9idGFpbmVkIGZyb20gdGhlIHB1YmxpY2x5IGF2YWlsYWJsZSA8YSBocmVmPSJodHRwczovL3d3dy5lcnMudXNkYS5nb3YvZGF0YS1wcm9kdWN0cy9jb3VudHktbGV2ZWwtZGF0YS1zZXRzLyI+VVNEQSBFY29ub21pYyBSZXNlYXJjaCBTZXJ2aWNlIENvdW50eS1sZXZlbCBkYXRhc2V0czwvYT4NCg0KDQpXaGlsZSB0aGVzZSBkYXRhc2V0cyBpbmNsdWRlIGEgbGl0YW55IG9mIGluZm9ybWF0aW9uLCB0aGUgYXJlYXMgb2YgZm9jdXMgaW4gdGhpcyBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIGFyZSB0aGUgaW1wYWN0cyBvZiBjb3VudHkgbGV2ZWwgdW5lbXBsb3ltZW50IHJhdGUgZm9yIHRoZSBjdXJyZW50IGVsZWN0aW9uIHllYXIsIGNvdW50eSBsZXZlbCBwb3ZlcnR5IHJhdGUgZm9yIHRoZSB5ZWFyIHByaW9yIHRvIHRoZSBjdXJyZW50IGVsZWN0aW9uIHllYXIsIGhpZ2hlciBlZHVjYXRpb24gcmF0ZSBmb3IgdGhlIGZvdXIgeWVhcnMgcHJpb3IgdG8gdGhlIGN1cnJlbnQgZWxlY3Rpb24geWVhci4gIFRvIG5vdGUsIHllYXJzIHByaW9yIHRvIHRoZSBjdXJyZW50IGVsZWN0aW9uIHllYXIgYXJlIHV0aWxpemVkIGR1ZSB0byBkYXRhIGF2YWlsYWJpbGl0eSBhbmQgdGhlIGltcGFjdCBvZiBwcmV2aW91cyB5ZWFyJ3Mgc29jaW9lY29ub21pYyBjb25kaXRpb25zIG9uIHRoZSBjdXJyZW50IHZvdGVyJ3Mgb3BpbmlvbnMuICBUaGUgZm9jdXMgb2YgdGhpcyBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIGNlbnRlcnMgYXJvdW5kIGFuYWx5emluZyBwb3RlbnRpYWwgY29ycmVsYXRpb25zIGFuZCBwcmVkaWN0aXZlIHZhbHVlcyBhcyByZWxhdGVkIHRvIHRoZSAyMDIwIHByZXNpZGVudGlhbCBlbGVjdGlvbiBvbmx5LiANCg0KPGg0PlJlbGV2YW50IE5vdGU8L2g0Pg0KQWxsIGRhdGEgcHJlcGFyYXRpb24gY29kZSBpcyBjb3BpZWQgaW4gdGhlIGFwcGVuZGl4IGFzIHdlbGwgYXMgaW5jbHVkZWQgaW4gbGluZSB3aXRoIHRoZSByZXBvcnQsIHZpc3VhbGl6YXRpb24gY29kZSBpcyBvbmx5IGluY2x1ZGVkIGluIGxpbmUgd2l0aCB0aGUgcmVwb3J0LiBUbyB2aWV3IGFueSBvZiB0aGUgY29kZSBpbiBsaW5lIHdpdGggZWFjaCBsb2NhdGlvbiBpdCBpcyB1c2VkIGluIHRoZSByZXBvcnQsIHNlbGVjdCB0aGUgYFNob3dgIGJ1dHRvbnMgb24gdGhlIHJpZ2h0IGhhbmQgc2lkZSBvZiB0aGUgcGFnZS4NCg0KIyBEYXRhIFByZXBhcmF0aW9uDQoNClByaW9yIHRvIGFueSBkYXRhIHZpc3VhbGl6YXRpb24gYW5kIGFuYWx5c2lzLCBhbGwgZGF0YXNldHMgd2VyZSBsb2FkZWQgaW50byBSU3R1ZGlvICh2MjAyNC4wOS4wIEJ1aWxkIDM3NSkgdG8gcHJlcGFyZSB1bmlxdWUgcmVsYWlvbmFsIGRhdGFzZXRzLiAgQSB1bmlxdWUgaWRlbnRpZmllciBvZiBgRklQUyBDb2RlYCB3YXMgdXRpbGl6ZWQgYWNyb3NzIGFsbCBmb3VyICg0KSBkYXRhc2V0cywgYXMgd2VsbCBhcyB0byBsaW5rIHRoZSByZWxldmFudCBnZW9KU09OIGRhdGEgZm9yIGNvdW50eSBtYXBwaW5nLiANCg0KIyMgQ3JlYXRlIFJlbGF0aW9uYWwgRGF0YXNldHMNCg0KIyMjIFByZXNpZGVudGlhbCBFbGVjdGlvbiBEYXRhICgyMDIwIG9yIG1vc3QgcmVjZW50IHllYXIgYXZhaWxhYmxlKQ0KDQpUaGUgcmF3IHByZXNpZGVudGlhbCBlbGVjdGlvbiBkYXRhIHdhcyBvYnRhaW5lZCBmcm9tIGZyb20gdGhlIHB1YmxpY2x5IGF2YWlsYWJsZSA8YSBocmVmPSJodHRwczovL2RhdGF2ZXJzZS5oYXJ2YXJkLmVkdS9kYXRhc2V0LnhodG1sP3BlcnNpc3RlbnRJZD1kb2k6MTAuNzkxMC9EVk4vVk9RQ0hRIj5NSVQgRWxlY3Rpb24gTGFiIERhdGEgU2V0OiBDb3VudHkgUHJlc2lkZW50aWFsIEVsZWN0aW9uIFJldHVybnMgMjAwMC0yMDIwPC9hPi4gDQoNCkVsZWN0aW9uIGRhdGEgd2FzIHJlYWQgaW50byBSU3R1ZGlvIGFzIGBlbGVjdGlvbl9yYXdgIGFuZCBjbGVhbmVkIHV0aWxpemluZyB0aGUgZm9sbG93aW5nIHN0ZXBzIHRvIGNyZWF0ZSB0aGUgcmVsYXRpb25hbCBkYXRhc2V0IGBlbGVjdGlvbmA6DQoNCi0gRGF0YSB3YXMgZ3JvdXBlZCBieSB0aGUgY291bnR5IGBGSVBTIGNvZGVgDQotIERhdGEgd2FzIHNvcnRlZCBieSBkZXNjZW5kaW5nIGBZZWFyYA0KLSBEYXRhIHdhcyBmaWx0ZXJlZCBieSBwb2xpdGljYWwgYFBhcnR5YCBmb3Igb25seSBgRGVtb2NyYXRgIGFuZCBgUmVwdWJsaWNhbmAgYXMgdGhlIGFuYWx5c2lzIGZvY3VzZXMgb24gdGhlIHBvdGVudGlhbCBpbXBhY3RzIG9uIGNvdW50eSBwcmVzaWRlbnRpYWwgZWxlY3Rpb24gd2lubmVycy4gIE5vIG90aGVyIHBhcnRpZXMgaGF2ZSBldmVyIHdvbiB0aGUgcHJlc2lkZW5jeSBhbmQgdGh1cyB3ZXJlIG9taXR0ZWQuICANCiAgLSBUbyBub3RlOiBibGFuayB2YWx1ZXMgd2VyZSBub3Qgb21pdHRlZA0KLSBVbm5lY2Vzc2FyeSB2YXJpYWJsZXMgd2VyZSByZW1vdmVkDQotIERhdGEgd2FzIGZpbHRlcmVkIGZvciBgMjAyMGAgb3IgdGhlIGBtb3N0IHJlY2VudCB5ZWFyIHdpdGggYXZhaWxhYmxlIGRhdGFgIGlmIDIwMjAgd2FzIG5vdCBhdmFpbGFibGUNCg0KQSBzZWFyY2hhYmxlIHByZXZpZXcgb2YgdGhlIHJlbGF0aW9uYWwgZGF0YXNldCBgZWxlY3Rpb25gIGlzIGF2YWlsYWJsZSBpbiB0aGUgZGF0YSB0YWJsZSBiZWxvdy4gIA0KDQpgYGB7cn0NCiMjcmVhZCBpbiB0aGUgZWxlY3Rpb24gZGF0YSBhcyBlbGVjdGlvbl9yYXcNCg0KZWxlY3Rpb25fcmF3IDwtIHJlYWQuY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbmxlcGVyYS9zdGE1NTEvcmVmcy9oZWFkcy9tYWluL0hXMDEvZGF0YS9jb3VudHlwcmVzaWRlbnRpYWxfZWxlY3Rpb25fMjAwMC0yMDIwLmNzdicpDQoNCiMjY2xlYW4gdGhlIGVsZWN0aW9uIGRhdGEgDQoNCmVsZWN0aW9uIDwtIGVsZWN0aW9uX3JhdyAlPiUgDQogIGdyb3VwX2J5KGNvdW50eV9maXBzKSAlPiUgICAjI2dyb3VwIGJ5IGNvdW50eSBGSVBTIGNvZGUNCiAgYXJyYW5nZSAoZGVzYyh5ZWFyKSkgJT4lICAgICMjc29ydCBieSBkZXNjZW5kaW5nIHllYXINCiAgZmlsdGVyIChwYXJ0eSA9PSAiREVNT0NSQVQiIHwgcGFydHkgPT0gIlJFUFVCTElDQU4iKSAlPiUgICAgIyNmaWx0ZXIgZm9yIG9ubHkgZGVtICYgcmVwDQogIHN1YnNldCgsIC1jKHN0YXRlX3BvLCBvZmZpY2UsIHZlcnNpb24sIG1vZGUpKSAlPiUgICAjI3JlbW92ZSB1bm5lY2Vzc2FyeSBjb2x1bW5zDQogIHNsaWNlICgxOjIpICU+JSAgICMjcHVsbCB0b3AgMiB2YWx1ZXMgZm9yIGVhY2ggZ3JvdXAgKGNvdW50eV9maXBzIHNvcnRlZCBieSBkZXNjIHllYXIpIHB1bGxpbmcgYm90aCBkZW0gJiByZXAgZm9yIGVhY2ggY291bnR5IGF0IG1heCB5ZWFyIGF2YWlsYWJsZQ0KICBhcnJhbmdlIChkZXNjKGNhbmRpZGF0ZXZvdGVzKSkgJT4lICAgICMjcmUgZmlsdGVyIHRvIHNvcnQgYnkgZ3JvdXAsIHdpdGggaGlnaGVzdCBjYW5kaWRhdGUgdm90ZXMgb24gdG9wDQogIHNsaWNlICgxKSAlPiUgICAgIyNvbmx5IGtlZXAgdmFsdWUgZm9yIGhpZ2hlc3QgY2FuZGlkYXRlIHZvdGVzICh3aW5uZXIpIHBlciBjb3VudHkNCiAgcmVuYW1lIChGSVBTID0gY291bnR5X2ZpcHMpICAgIyMgc2V0IElEIG5hbWUgZm9yIG91dGVyIGpvaW4NCiAgDQpEVDo6ZGF0YXRhYmxlKGVsZWN0aW9uKQ0KYGBgDQoNCiMjIyBVbmVtcGxveW1lbnQgRGF0YSAoMjAyMCBvciBtb3N0IHJlY2VudCB5ZWFyIGF2YWlsYWJsZSkNClRoZSByYXcgdW5lbXBsb3ltZW50IGRhdGEgd2FzIG9idGFpbmVkIGZyb20gZnJvbSB0aGUgcHVibGljbHkgYXZhaWxhYmxlIDxhIGhyZWY9Imh0dHBzOi8vd3d3LmVycy51c2RhLmdvdi9kYXRhLXByb2R1Y3RzL2NvdW50eS1sZXZlbC1kYXRhLXNldHMvIj5VU0RBIEVjb25vbWljIFJlc2VhcmNoIFNlcnZpY2UgQ291bnR5LWxldmVsIERhdGFzZXQ6IFVuZW1wbG95bWVudDwvYT4uIA0KDQpVbmVtcGxveW1lbnQgZGF0YSB3YXMgcmVhZCBpbnRvIFJTdHVkaW8gYXMgYHVuZW1wbG95X3Jhd2AgYW5kIGNsZWFuZWQgdXRpbGl6aW5nIHRoZSBmb2xsb3dpbmcgc3RlcHMgdG8gY3JlYXRlIHRoZSByZWxhdGlvbmFsIGRhdGFzZXQgYHVuZW1wbG95YDoNCg0KLSBEYXRhIHdhcyBmaWx0ZXJlZCBmb3IgdGV4dCBjb250YWluaW5nIHRoZSBzdHJpbmcgYFVuZW1wbG95bWVudGAgaW4gdGhlIGBBdHRyaWJ1dGVgIHZhcmlhYmxlIGRhdGEgdmFsdWVzIHRvIHJlbW92ZSBvdGhlciB1bm5lY2Vzc2FyeSBkYXRhIHR5cGVzLg0KLSBJbiBvcmRlciB0byBleHRyYWN0IHRoZSB5ZWFyIGZyb20gdGhlIGBBdHRyaWJ1dGVgIHZhbHVlcywgYSBuZXcgY29sdW1uIHdhcyBjcmVhdGVkIGJ5IGV4dHJhY3RpbmcgdGhlIGxhc3QgZm91ciAoNCkgY2hhcmFjdGVycyBvZiB0aGUgYEF0dHJpYnV0ZWAgdmFsdWVzLg0KLSBEYXRhIHdhcyBncm91cGVkIGJ5IHRoZSBjb3VudHkgYEZJUFMgY29kZWANCi0gRGF0YSB3YXMgc29ydGVkIGJ5IGRlc2NlbmRpbmcgYFllYXJgDQotIERhdGEgd2FzIGZpbHRlcmVkIGZvciBgMjAyMGAgb3IgdGhlIGBtb3N0IHJlY2VudCB5ZWFyIHdpdGggYXZhaWxhYmxlIGRhdGFgIGlmIDIwMjAgd2FzIG5vdCBhdmFpbGFibGUNCi0gVW5uZWNlc3NhcnkgdmFyaWFibGVzIHdlcmUgcmVtb3ZlZA0KDQpBIHNlYXJjaGFibGUgcHJldmlldyBvZiB0aGUgcmVsYXRpb25hbCBkYXRhc2V0IGB1bmVtcGxveWAgaXMgYXZhaWxhYmxlIGluIHRoZSBkYXRhIHRhYmxlIGJlbG93LiAgDQoNCmBgYHtyfQ0KIyMgcmVhZCBpbiB0aGUgdW5lbXBsb3ltZW50IGRhdGEgYXMgdW5lbXBsb3lfcmF3DQoNCnVuZW1wbG95X3JhdyA8LSByZWFkLmNzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL25sZXBlcmEvc3RhNTUxL3JlZnMvaGVhZHMvbWFpbi9IVzAxL2RhdGEvVW5lbXBsb3ltZW50LmNzdicpDQoNCiMjIGNsZWFuIHRoZSB1bmVtcGxveW1lbnQgZGF0YQ0KdW5lbXBsb3kgPC0gdW5lbXBsb3lfcmF3ICU+JSANCiAgZmlsdGVyIChncmVwbCgnVW5lbXBsb3ltZW50JywgQXR0cmlidXRlKSkgJT4lICAgI3VzZSBkcGx5ciBwYWNrYWdlIHRvIHVzZSBmaWx0ZXIoZ3JlcGwoKSkgdG8gZmlsdGVyIGZvciBzdHJpbmcgb2YgdGV4dCB0byBzZWxlY3Qgb25seSB0aGUgdW5lbXBsb3ltZW50IHJhdGUgdmFsdWVzDQogIG11dGF0ZSAoeWVhciA9IHN0cl9zdWIoQXR0cmlidXRlLCAtNCkpICU+JSAgIyNjcmVhdGUgYSBuZXcgY29sdW1uIHVzaW5nIG11dGF0ZSBhbmQgc3RyaW5nciB0byBjcmVhdGUgYSBuZXcgY29sdW1uIGZyb20gdGhlIGxhc3QgNCB2YWx1ZXMgb2YgdGhlIGF0dHJpYnV0ZSBjb2x1bW4sIHRvIGV4dHJhY3QgdGhlIHllYXIgKHN0cl9zdWIgLT4gc3RyaW5ncl9zdWJzZXQpDQogIGdyb3VwX2J5IChGSVBTX0NvZGUpICU+JSAgIyNncm91cCBieSBGSVBTIGNvZGUgYmVmb3JlIHNvcnRpbmcgdG8gZ2V0IG1heCB5ZWFyDQogIGFycmFuZ2UgKGRlc2MoeWVhcikpICU+JSAgIyMgc29ydCBkZXNjZW5kaW5nIHllYXIgdG8gcHV0IG1heCBhdmFpbGFibGUgYXQgdGhlIHRvcA0KICBzbGljZSgxKSAlPiUgICAjI3NsaWNlIHRvcCB2YWx1ZSB0byBrZWVwIG9ubHkgbWF4IHllYXIgZm9yIGVhY2ggRklQUyBjb2RlDQogIHN1YnNldCAoLCBjKEZJUFNfQ29kZSwgVmFsdWUpKSAlPiUgICAjI3JlbW92ZSB1bm5lY2Vzc2FyeSBjb2x1bW5zDQogIHJlbmFtZSAoIlVuZW1wbG95bWVudF9SYXRlICglKSIgPSBWYWx1ZSwgRklQUyA9IEZJUFNfQ29kZSkgICAgIyNyZW5hbWUgY29sdW1uIHRvIHJlZmxlY3QgdW5lbXBsb3ltZW50IHJhdGUgJiBzZXQgSUQgbmFtZSBmb3Igb3V0ZXIgam9pbg0KDQpEVDo6ZGF0YXRhYmxlKHVuZW1wbG95KQ0KICANCmBgYA0KDQojIyMgUG92ZXJ0eSBEYXRhICgyMDE5KQ0KVGhlIHJhdyBwb3ZlcnR5IGRhdGEgd2FzIG9idGFpbmVkIGZyb20gZnJvbSB0aGUgcHVibGljbHkgYXZhaWxhYmxlIDxhIGhyZWY9Imh0dHBzOi8vd3d3LmVycy51c2RhLmdvdi9kYXRhLXByb2R1Y3RzL2NvdW50eS1sZXZlbC1kYXRhLXNldHMvIj5VU0RBIEVjb25vbWljIFJlc2VhcmNoIFNlcnZpY2UgQ291bnR5LWxldmVsIERhdGFzZXQ6IFBvdmVydHk8L2E+LiANCg0KUG92ZXJ0eSBkYXRhIHdhcyByZWFkIGludG8gUlN0dWRpbyBhcyBgcG92ZXJ0eV9yYXdgIGFuZCBjbGVhbmVkIHV0aWxpemluZyB0aGUgZm9sbG93aW5nIHN0ZXBzIHRvIGNyZWF0ZSB0aGUgcmVsYXRpb25hbCBkYXRhc2V0IGBwb3ZlcnR5YDoNCg0KLSBEYXRhIHdhcyBmaWx0ZXJlZCBmb3IgdGV4dCBjb250YWluaW5nIHRoZSBzdHJpbmcgYFBDVFBPVkFMTF8yMDE5YCBpbiB0aGUgYEF0dHJpYnV0ZWAgdmFyaWFibGUgZGF0YSB2YWx1ZXMgdG8gcmVtb3ZlIG90aGVyIHVubmVjZXNzYXJ5IGRhdGEgdHlwZXMuDQotIFVubmVjZXNzYXJ5IHZhcmlhYmxlcyB3ZXJlIHJlbW92ZWQNCg0KQSBzZWFyY2hhYmxlIHByZXZpZXcgb2YgdGhlIHJlbGF0aW9uYWwgZGF0YXNldCBgdW5lbXBsb3lgIGlzIGF2YWlsYWJsZSBpbiB0aGUgZGF0YSB0YWJsZSBiZWxvdy4NCg0KYGBge3J9DQojIyByZWFkIGluIHRoZSBwb3ZlcnR5IGRhdGEgYXMgcG92ZXJ0eV9yYXcNCg0KcG92ZXJ0eV9yYXcgPC0gcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ubGVwZXJhL3N0YTU1MS9yZWZzL2hlYWRzL21haW4vSFcwMS9kYXRhL1BvdmVydHlFc3RpbWF0ZXMuY3N2JykNCg0KIyNjbGVhbiB0aGUgcG92ZXJ0eSBkYXRhDQpwb3ZlcnR5IDwtIHBvdmVydHlfcmF3ICU+JSANCiAgZmlsdGVyIChBdHRyaWJ1dGUgPT0gIlBDVFBPVkFMTF8yMDE5IikgJT4lICAjI0ZpbHRlciBmb3IgMjAxOSBwb3ZlcnR5IHZhbHVlcw0KICBzdWJzZXQgKCwgYyhGSVBTdHh0LCBWYWx1ZSkpICU+JSAgIyNyZW1vdmUgdW5uZWNjZXNzYXJ5IGNvbHVtbnMNCiAgcmVuYW1lICgiUG92ZXJ0eV9SYXRlICglKSIgPSBWYWx1ZSwgRklQUyA9IEZJUFN0eHQpICAgIyNyZW5hbWUgVmFsdWUgY29sdW1uIHRvIFBvdmVydHlfUmF0ZSAoJSkgYW5kIHNldCBJRCBuYW1lIGZvciBvdXRlciBqb2luDQoNCkRUOjpkYXRhdGFibGUocG92ZXJ0eSkNCmBgYA0KDQojIyMgRWR1Y2F0aW9uIERhdGEgKDIwMTUgLSAyMDE5KQ0KVGhlIHJhdyBlZHVjYXRpb24gZGF0YSB3YXMgb2J0YWluZWQgZnJvbSBmcm9tIHRoZSBwdWJsaWNseSBhdmFpbGFibGUgPGEgaHJlZj0iaHR0cHM6Ly93d3cuZXJzLnVzZGEuZ292L2RhdGEtcHJvZHVjdHMvY291bnR5LWxldmVsLWRhdGEtc2V0cy8iPlVTREEgRWNvbm9taWMgUmVzZWFyY2ggU2VydmljZSBDb3VudHktbGV2ZWwgRGF0YXNldDogRWR1Y2F0aW9uPC9hPi4gDQoNClVuZW1wbG95bWVudCBkYXRhIHdhcyByZWFkIGludG8gUlN0dWRpbyBhcyBgZWR1X3Jhd2AgYW5kIGNsZWFuZWQgdXRpbGl6aW5nIHRoZSBmb2xsb3dpbmcgc3RlcHMgdG8gY3JlYXRlIHRoZSByZWxhdGlvbmFsIGRhdGFzZXQgYGVkdWA6DQoNCi0gVW5uZWNlc3NhcnkgdmFyaWFibGVzIHdlcmUgcmVtb3ZlZA0KLSBDb2x1bW5zIHdlcmUgcmVuYW1lZCB0byB0cnVuY2F0ZWQgdmFsdWVzIGZvciBlYXNlIG9mIHVzZToNCiAgLSBgTm9fSFNgID0gIyBvZiBwZXJzb25zIGluIHRoZSBjb3VudHkgd2l0aCBsZXNzIHRoYW4gYSBoaWdoIHNjaG9vbCBkaXBsb21hDQogIC0gYEhTYCA9ICMgb2YgcGVyc29ucyBpbiB0aGUgY291bnR5IHdpdGggYSBoaWdoIHNjaG9vbCBkaXBsb21hIG9ubHkNCiAgLSBgQVNfU29tZUNvbGAgPSAjIG9mIHBlcnNvbnMgaW4gdGhlIGNvdW50eSB3aXRoIGVpdGhlciBzb21lIGNvbGxlZ2UgY3JlZGl0IG9yIGFuIEFzc29jaWF0ZSdzIERlZ3JlZQ0KICAtIGBCU19Nb3JlYCA9ICMgb2YgcGVyc29ucyBpbiB0aGUgY291bnR5IHdpdGggYSBCYWNoZWxvcidzIERlZ3JlZSBvciBoaWdoZXINCi0gRGF0YSBjbGVhbmVkIHRvIHJlbW92ZSBjb21tYXMgZnJvbSBudW1iZXJzIGFuZCBjb252ZXJ0IGZyb20gY2hhcmFjdGVyIHRvIG51bWVyaWMgdmFsdWVzDQoNCg0KQSBzZWFyY2hhYmxlIHByZXZpZXcgb2YgdGhlIHJlbGF0aW9uYWwgZGF0YXNldCBgdW5lbXBsb3lgIGlzIGF2YWlsYWJsZSBpbiB0aGUgZGF0YSB0YWJsZSBiZWxvdy4NCg0KDQpgYGB7cn0NCiMjIHJlYWQgaW4gdGhlIHVuZW1wbG95bWVudCBkYXRhIGFzIGVkdV9yYXcNCg0KZWR1X3JhdyA8LSByZWFkLmNzdignaHR0cHM6Ly9ubGVwZXJhLmdpdGh1Yi5pby9zdGE1NTEvSFcwMS9kYXRhL0VkdWNhdGlvbi5jc3YnLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCg0KI2NsZWFuIGVkdWNhdGlvbiBkYXRhDQplZHUgPC0gZWR1X3JhdyAlPiUgDQogIHN1YnNldCAoLCBjKEZJUFMuQ29kZSwgTGVzcy50aGFuLmEuaGlnaC5zY2hvb2wuZGlwbG9tYS4uMjAxNS4xOSwgSGlnaC5zY2hvb2wuZGlwbG9tYS5vbmx5Li4yMDE1LjE5LCBTb21lLmNvbGxlZ2Uub3IuYXNzb2NpYXRlLnMuZGVncmVlLi4yMDE1LjE5LAlCYWNoZWxvci5zLmRlZ3JlZS5vci5oaWdoZXIuLjIwMTUuMTkpKSAlPiUgICMjcmVtb3ZlIGlycmVsZXZhbnQgY29sdW1ucw0KICByZW5hbWUgKCAiTm9fSFMiID0gTGVzcy50aGFuLmEuaGlnaC5zY2hvb2wuZGlwbG9tYS4uMjAxNS4xOSwgIkhTIiA9IEhpZ2guc2Nob29sLmRpcGxvbWEub25seS4uMjAxNS4xOSwgIkFTX1NvbWVDb2wiID0gU29tZS5jb2xsZWdlLm9yLmFzc29jaWF0ZS5zLmRlZ3JlZS4uMjAxNS4xOSwJIkJTX01vcmUiID0gQmFjaGVsb3Iucy5kZWdyZWUub3IuaGlnaGVyLi4yMDE1LjE5LCBGSVBTID0gRklQUy5Db2RlKSAgIyNyZW5hbWUgZm9yIGVhc2UgYW5kIHNldCBJRCBuYW1lIGZvciBvdXRlciBqb2luDQplZHUkTm9fSFMgPSBnc3ViKCcsJywgJycsIGVkdSROb19IUykgIyNyZW1vdmUgY29tbWFzIGZyb20gbnVtYmVyIHdpdGggY29tbWFzIHNhdmVkIGFzIGNoYXJhY2h0ZXJzDQplZHUkSFMgPSBnc3ViKCcsJywgJycsIGVkdSRIUykNCmVkdSRBU19Tb21lQ29sID0gZ3N1YignLCcsICcnLCBlZHUkQVNfU29tZUNvbCkNCmVkdSRCU19Nb3JlID0gZ3N1YignLCcsICcnLCBlZHUkQlNfTW9yZSkNCmVkdSROb19IUyA9IGFzLm51bWVyaWMoZWR1JE5vX0hTKSAgIyN0cmFuc2Zvcm0gdG8gbnVtZXJpYyB0byBhZ2dyZWdhdGUNCmVkdSRIUyA9IGFzLm51bWVyaWMoZWR1JEhTKQ0KZWR1JEFTX1NvbWVDb2wgPSBhcy5udW1lcmljKGVkdSRBU19Tb21lQ29sKQ0KZWR1JEJTX01vcmUgPSBhcy5udW1lcmljKGVkdSRCU19Nb3JlKQ0KICANCkRUOjpkYXRhdGFibGUoZWR1KQ0KYGBgDQojIyBNZXJnZSBSZWxhdGlvbmFsIERhdGFzZXRzICYgQWdncmVnYXRlIGRhdGENCg0KIyMjIE1lcmdlIHRoZSByZWxhdGlvbmFsIGRhdGFzZXRzIHVzaW5nIGNvdW50eSBGSVBTIGNvZGVzDQoNCk9uY2UgYWxsIGZvdXIgKDQpIHJlbGF0aW9uYWwgZGF0YXNldHMgKGBlZHVgLCBgZWxlY3Rpb25gLCBgcG92ZXJ0eWAsIGFuZCBgdW5lbXBsb3lgKSB3ZXJlIGNyZWF0ZWQsIGEgbmV3IGRhdGFzZXQgbmFtZWQgYGFsbC5kYXRhYCB3YXMgY3JlYXRlZCBieSBjb21wbGV0aW5nIG11bHRpcGxlIGZ1bGwgam9pbiBmdW5jdGlvbnMuICBBbGwgZGF0YXNldHMgd2VyZSBtZXJnZWQgdXNpbmcgdGhlIGNvdW50eSBgRklQU2AgY29kZSBhcyB0aGUgdW5pcXVlIGlkZW50aWZpZXIgY29uc2VydmVkIGFjcm9zcyBhbGwgZGF0YXNldHMuIA0KDQpgYGB7cn0NCiMjbWVyZ2UgZWxlY3Rpb24gZGF0YSAmIGVkdWNhdGlvbiBkYXRhIGFzIGVsZWN0aW9uLmVkdWNhdGlvbiANCmVsZWN0aW9uLmVkdWNhdGlvbiA8LSBmdWxsX2pvaW4gKA0KICB4ID0gZWxlY3Rpb24sDQogIHkgPSBlZHUsDQogIGJ5ID0gIkZJUFMiLA0KICBrZWVwID0gRkFMU0UNCikNCg0KDQojI21lcmdlIGVsZWN0aW9uLmVkdWNhdGlvbiAmIHBvdmVydHkgZGF0YSBhcyBlbGVjdGlvbi5lZHVjYXRpb24ucG92ZXJ0eQ0KZWxlY3Rpb24uZWR1Y2F0aW9uLnBvdmVydHkgPC0gZnVsbF9qb2luKA0KICB4ID0gZWxlY3Rpb24uZWR1Y2F0aW9uLA0KICB5ID0gcG92ZXJ0eSwNCiAgYnkgPSAiRklQUyIsDQogIGtlZXAgPSBGQUxTRQ0KKQ0KDQojI21lcmdlIGVsZWN0aW9uLmVkdWNhdGlvbi5wb3ZlcnR5ICYgdW5lbXBsb3ltZW50IGRhdGEgYXMgYWxsLmRhdGENCmFsbC5kYXRhIDwtIGZ1bGxfam9pbiAoDQogIHggPSBlbGVjdGlvbi5lZHVjYXRpb24ucG92ZXJ0eSwNCiAgeSA9IHVuZW1wbG95LA0KICBieSA9ICJGSVBTIiwNCiAga2VlcCA9IEZBTFNFDQopDQoNCg0KYGBgDQoNCiMjIyBBZ2dyZWdhdGUgdGhlIEVkdWNhdGlvbiBEYXRhIGludG8gJSBIaWdoZXIgRWQNCg0KQXMgdGhlIGVkdWNhdGlvbmFsIGRhdGEgcHJvdmlkZWQgZm9yIGVhY2ggY291bnR5IGNvbnNpc3RlZCBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIHBlcnNvbnMgaW4gZWFjaCBjb3VudHkgdGhhdCByZWNlaXZlZCBlYWNoIG9mIHRoZSBmb3VyIHRpZXJzIG9mIGVkdWNhdGlvbiwgdGhlIGRhdGEgd2FzIGFnZ3JlZ2F0ZWQgdG8gYWxsb3cgZm9yIGNsZWFuZXIgYW5hbHlzaXMgYW5kIGNvbXBhcmlzb24uICBJbiBvcmRlciB0byBhZ2dyZWdhdGUgdGhlIGVkdWNhdGlvbmFsIGRhdGEsIHRoZSBmb3VyIGVkdWNhdGlvbiB2YXJpYWJsZXMgKGBOb19IU2AsIGBIU2AsIGBBU19Tb21lQ29sYCwgJiBgQlNfTW9yZWApIHdlcmUgYWdncmVnYXRlZCB0byBjcmVhdGUgYSBuZXcgdmFyaWFibGUgYEhpZ2hlcl9FZF9QZXJjZW50YCB0byBtZWFzdXJlIHRoZSBwZXJjZW50YWdlIG9mIHRoZSBwb3B1bGF0aW9uIHN1cnZleWVkIHRoYXQgY29tcGxldGVkIGVpdGhlciBzb21lIGNvbGxlZ2UgY3JlZGl0LCBhbiBBc3NvY2lhdGUncyBEZWdyZWUsIGEgQmFjaGVsb3IncyBEZWdyZWUsIG9yIGFkZGl0aW9uYWwgZ3JhZHVhdGUgbGV2ZWwgZGVncmVlcy4gIA0KDQpUaGlzIGhpZ2hlciBlZHVjYXRpb24gbWV0cmljIHdhcyBjcmVhdGVkIGFzIGEgbWVhc3VyZSBvZiB0aGUgYXZlcmFnZSBzb2Npb2Vjb25vbWljIHN0YXR1cyBvZiB0aGUgY291bnRpZXMgYmVpbmcgYW5hbHl6ZWQuDQoNCmBgYHtyfQ0KYWxsLmRhdGEgPC0gYWxsLmRhdGEgICU+JSANCiAgZmlsdGVyKEZJUFMgIT0gMCkgJT4lICMjZmlsdGVyIG91dCB2YWx1ZXMgd2l0aCBubyBhc3NvY2lhdGVkIGNvdW50eSBvciBzdGF0ZSBuYW1lDQogIGZpbHRlcihGSVBTICE9IDEwMDApICU+JSANCiAgbXV0YXRlIChIaWdoZXJfRWRfUGVyY2VudCA9ICgoKGBBU19Tb21lQ29sYCArIGBCU19Nb3JlYCkvKGBOb19IU2ArIGBIU2AgKyBgQVNfU29tZUNvbGAgKyBgQlNfTW9yZWApKSoxMDApKSAgIyMlIG9mIHBvcCB3aXRoIGhpZ2hlciBlZC4NCg0KYGBgDQoNCiMgVmlzdWFsaXppbmcgYW5kIEFuYWx5emluZyB0aGUgRGF0YQ0KDQojIyBNYXBwaW5nIHRoZSBmdWxsIGRhdGENCg0KRm9yIGFuIGluaXRpYWwgdmlzdWFsaXphdGlvbiBvZiB0aGUgZGF0YSBhcyBhIHdob2xlLCB0aGUgYGFsbC5kYXRhYCBkYXRhc2V0IHdhcyBtYXBwZWQgYnkgY291bnR5IHV0aWxpemluZyBhbiBpbnRlcmFjdGl2ZSBsZWFmbGV0IG1hcC4gIER1ZSB0byB0aGUgc2l6ZSBvZiB0aGUgZGF0YXNldCBhbmQgbnVtYmVyIG9mIGNvdW50aWVzLCBjb2xvciBzcGVjaWZpYyBkYXRhIGlzIG5vdCB2aXNpYmxlIHVudGlsIHRoZSBtYXAgaXMgem9vbWVkIGluIHRvIHRoZSBhcHByb3ByaWF0ZSBjb3VudHkgbGV2ZWwuICBVbnRpbCB6b29tZWQgaW4gdGhlIGNvbG9yIHNjYWxlIChncmVlbiB0byByZWQpIHJlcHJlc2VudHMgdGhlIG51bWJlciBvZiBjb3VudGllcyBuZXN0ZWQgaW4gdGhhdCBncm91cCB1bnRpbCB0aGUgbWFwIGlzIHpvb21lZCBpbiBmdXJ0aGVyLiAgT25jZSBmdWxseSB6b29tZWQgaW4gdGhlIGNvbG9yIG9mIHRoZSA8Yj51bm51bWJlcmVkIGNpcmNsZXM8L2I+IChyZWQgYW5kIGJsdWUpIHJlcHJlc2VudCB0aGUgd2lubmluZyBwYXJ0eSAoUmVwdWJsaWNhbiBhbmQgRGVtb2NyYXQgcmVzcGVjdGl2ZWx5KSBpbiBlYWNoIGNvdW50eSBmb3IgdGhlIDIwMjAgcHJlc2lkZW50aWFsIGVsZWN0aW9uLg0KDQpgYGB7cn0NCg0KY291bnR5X2RhdGFfcmF3IDwtIHNmOjpyZWFkX3NmKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcGxvdGx5L2RhdGFzZXRzL21hc3Rlci9nZW9qc29uLWNvdW50aWVzLWZpcHMuanNvbiIpICMjcHVsbCBpbiBtYXAgb2YgRklQUyBjb2RlcyANCg0KRklQU2NvbnZlcnRfcmF3IDwtIHNmOjpyZWFkX3NmKCJodHRwczovL25sZXBlcmEuZ2l0aHViLmlvL3N0YTU1MS9IVzAxL2RhdGEvZmlwczJnZW9jb2RlLmNzdiIpICMjcHVsbCBpbiBrZXkgb2YgRklQUyBjb2RlcyB0byBsYXQvbG9uZw0KDQpjb3VudHlfZGF0YSA8LSBjb3VudHlfZGF0YV9yYXcgJT4lICAjY2xlYW4gY291bnR5IGRhdGEgdG8gbGlzdCBGSVBTIGFzIG51bWVyaWMgJiBtYXRjaCBuYW1lIHRvIGtleSBmb3Igam9pbg0KICBtdXRhdGUgKGlkID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoaWQpKSkgJT4lIA0KICByZW5hbWUgKEZJUFMgPSBpZCkNCg0KRklQU2NvbnZlcnQgPC0gRklQU2NvbnZlcnRfcmF3ICU+JSAgDQogIG11dGF0ZSAoZmlwcyA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGZpcHMpKSwgbG9uID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobG9uKSksIGxhdCA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGxhdCkpKSAlPiUgDQogIHJlbmFtZSAoRklQUyA9IGZpcHMsIExvbmdpdHVkZSA9IGxvbiwgTGF0aXR1ZGUgPSBsYXQpDQoNCmFsbC5kYXRhLmpvaW4gPC0gZnVsbF9qb2luKA0KICB4ID0gYWxsLmRhdGEsDQogIHkgPSBGSVBTY29udmVydCwNCiAgYnkgPSAiRklQUyIsDQogIGtlZXAgPSBGQUxTRQ0KKSAlPiUgDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCjxoMj4gSW50ZXJhY3RpdmUgTWFwIG9mIENvdW50eSBEYXRhIDwvaDI+DQoNCmBgYHtyfQ0KcGFsIDwtIGNvbG9yRmFjdG9yKGMoImJsdWUiLCAicmVkIiksIGRvbWFpbiA9IGMoIlJFUFVCTElDQU4iLCAiREVNT0NSQVQiKSkNCg0KdGl0bGUgPC0gIg0KPHN0eWxlPg0KICAuY3VzdG9tLXRpdGxlIHsNCiAgICBjb2xvcjogd2hpdGU7DQogICAgcGFkZGluZzogMHB4Ow0KICAgIGJvcmRlci1yYWRpdXM6IDBweDsNCiAgICB0ZXh0LWFsaWduOiByaWdodDsNCiAgfQ0KPC9zdHlsZT4NCkVsZWN0aW9uIFJlc3VsdHMsIEVkdWNhdGlvbiBSYXRlcywgVW5lbXBsb3ltZW50IFJhdGVzICYgUG92ZXJ0eSBSYXRlcw0KPGJyPlpvb20gaW4gdG8gdmlldyBlYWNoIGNvdW50eSBpbmRpdmlkdWFsbHkNCjxicj5TZWxlY3QgYSBjb3VudHkncyBjaXJjbGUgdG8gdmlldyBmdWxsIGRhdGEiDQoNCmxlYWZsZXQoKSAlPiUgDQogIHNldFZpZXcobG5nPS05OC41Nzk1LCBsYXQ9MzkuODI4Mywgem9vbSA9IDQpICU+JSAjI3NldCB2aWV3IHRvIG1pZCBVUw0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLkRhcmtNYXR0ZXIpICU+JSAgIyNzZXQgbWFwIHRvIGRhcmsgbW9kZQ0KICBhZGRDb250cm9sKGh0bWwgPSB0aXRsZSwgDQogICAgICAgICAgICAgcG9zaXRpb24gPSAidG9wcmlnaHQiLCANCiAgICAgICAgICAgICBjbGFzc05hbWUgPSAiY3VzdG9tLXRpdGxlIikgJT4lIA0KICBhZGRQb2x5Z29ucyhkYXRhID0gY291bnR5X2RhdGEsICAjI2FkZCBGSVBTIHBvbHlnb25zDQogICAgICAgICAgICAgIGNvbG9yID0gInllbGxvdyIsDQogICAgICAgICAgICAgIGZpbGwgPSBGQUxTRSwNCiAgICAgICAgICAgICAgd2VpZ2h0ID0gMC4yNSkgJT4lIA0KICBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBhbGwuZGF0YS5qb2luLA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnBhbChwYXJ0eSksDQogICAgICAgICAgICAgICAgICAgcmFkaXVzID0gMjAsDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cG9wdXBUYWJsZShhbGwuZGF0YSksDQogICAgICAgICAgICAgICAgICAgY2x1c3Rlck9wdGlvbnMgPSBtYXJrZXJDbHVzdGVyT3B0aW9ucyhtYXhDbHVzdGVyUmFkaXVzID0gNTApKQ0KICANCiAgDQpgYGANCg0KIyMgUGxvdCB2YXJpYWJsZSBkaXN0cmlidXRpb24NCg0KQmVmb3JlIGNvbXBsZXRpbmcgYW55IGRhdGEgYW5hbHlzaXMgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdGhyZWUgbnVtZXJpYyB2YXJpYWJsZXMgb2YgaW50ZXJlc3Qgd2FzIHJldmlld2VkLCBjb250cm9sbGluZyBmb3IgdGhlIHRoZSB3aW5uaW5nIHBhcnR5IChSZXB1YmxpY2FuIG9yIERlbW9jcmF0KSBpbiBlYWNoIGNvdW50eSBmb3IgdGhlIDIwMjAgcHJlc2lkZW50aWFsIGVsZWN0aW9uLg0KDQpCb3RoIHVuZW1wbG95bWVudCBhbmQgaGlnaGVyIGVkdWNhdGlvbiByYXRlcyBhcHBlYXIgdG8gaGF2ZSBub3JtYWwgZGlzdHJpYnV0aW9ucyB3aGlsZSBwb3ZlcnR5IHJhdGVzIGFwcGVhciB0byBoYXZlIGNsb3NlciB0byBhIGdhbW1hIGRpc3RyaWJ1dGlvbi4gIEEgZ3JlYXRlciBmcmVxdWVuY3kgb2YgY291bnRpZXMgd2l0aCBkZW1vY3JhdGljIGNhbmRpZGF0ZSB3aW5uZXJzIHdhcyBzZWVuIGZvciBhbGwgdGhyZWUgKDMpIHZhcmlhYmxlcyBpbiBxdWVzdGlvbi4gDQpgYGB7cn0NCg0KZ2dwbG90IChhbGwuZGF0YSwgYWVzKHggPSBgVW5lbXBsb3ltZW50X1JhdGUgKCUpYCwgZmlsbCA9IGBwYXJ0eWApKSArDQogIGdlb21faGlzdG9ncmFtKGluaGVyaXQuYWVzID0gVFJVRSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdndGl0bGUgKCJEaXN0cmlidXRpb24gb2YgVW5lbXBsb3ltZW50IFJhdGVzIGJ5IENvdW50eSBFbGVjdGlvbiBXaW5uZXIiKSArDQogIHhsYWIgKCJDb3VudHkgVW5lbXBsb3ltZW50IFJhdGUgKHgpDQogICAgICAgIFslIG9mIENvdW50eSBQb3AuXSIpICsgDQogIHlsYWIgKCJDb3VudGllcyBXLyBVbmVtcGxveW1lbnQgUmF0ZSA9IHgiKSAgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjgpKQ0KDQpnZ3Bsb3QgKGFsbC5kYXRhLCBhZXMoeCA9IGBQb3ZlcnR5X1JhdGUgKCUpYCwgZmlsbCA9IGBwYXJ0eWApKSArDQogIGdlb21faGlzdG9ncmFtKGluaGVyaXQuYWVzID0gVFJVRSwgY29sb3IgPSAiYmxhY2siKSArIA0KICBnZ3RpdGxlICgiRGlzdHJpYnV0aW9uIG9mIFBvdmVydHkgUmF0ZXMgYnkgQ291bnR5IEVsZWN0aW9uIFdpbm5lciIpICsgDQogIHhsYWIgKCJDb3VudHkgUG92ZXJ0eSBSYXRlICh4KQ0KICAgICAgICBbJSBvZiBDb3VudHkgUG9wLl0iKSArDQogIHlsYWIgKCJDb3VudGllcyBXLyBQb3ZlcnR5IFJhdGUgPSB4IikgICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiYmx1ZSIsICJyZWQiKSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKDAuOCwgMC44KSwgaGVpZ2h0ID0gIjIwMHB4IikNCg0KZ2dwbG90IChhbGwuZGF0YSwgYWVzKHggPSBgSGlnaGVyX0VkX1BlcmNlbnRgLCBmaWxsID0gYHBhcnR5YCkpICsgDQogIGdlb21faGlzdG9ncmFtKGluaGVyaXQuYWVzID0gVFJVRSwgY29sb3IgPSAiYmxhY2siKSArIA0KICBnZ3RpdGxlICgiRGlzdHJpYnV0aW9uIG9mIEhpZ2hlciBFZHVjYXRpb24gUmF0ZXMgYnkgQ291bnR5IEVsZWN0aW9uIFdpbm5lciIpICsgDQogIHhsYWIgKCJDb3VudHkgSGlnaGVyIEVkLiBSYXRlICh4KQ0KICAgICAgICBbJSBvZiBDb3VudHkgUG9wLl0gIikgKyANCiAgeWxhYiAoIkNvdW50aWVzIFcvIEhpZ2hlciBFZC4gUmF0ZSA9IHgiKSAgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjgpKQ0KDQpgYGANCg0KIyMgUGxvdCBSZWxhdGlvbnNoaXAgQmV0d2VlbiBOdW1lcmljIFZhcmlhYmxlcw0KDQpUbyBkZXRlcm1pbmUgdGhlIHBvdGVudGlhbCByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIHRocmVlIG51bWVyaWMgdmFyaWFibGVzIG9mIGludGVyZXN0IChgVW5lbXBsb3ltZW50IFJhdGVgLCBgUG92ZXJ0eSBSYXRlYCwgYW5kIGBIaWdoZXIgRWR1Y2F0aW9uIFJhdGVgKSB3ZXJlIHBsb3R0ZWQgYWdhaW5zdCBlYWNoIG90aGVyIGFuZCByZXZpZXdlZCB1dGlsaXppbmcgYSBiYXNpYyBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBmb3IgdGhlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGRhdGEgYW5kIGEgR0xNIGdhbW1hIHJlZ3Jlc2lvbiBtb2RlbCBmb3IgdGhlIGdhbW1hIGRpc3RyaWJ1dGVkIGRhdGEuDQpgYGB7cn0NCmdncGxvdChkYXRhID0gYWxsLmRhdGEsIGFlcyh4ID1hbGwuZGF0YSRgVW5lbXBsb3ltZW50X1JhdGUgKCUpYCwgeT1hbGwuZGF0YSRgUG92ZXJ0eV9SYXRlICglKWAsIGNvbG9yPWBwYXJ0eWApKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjI1KSArDQogICAgZ2d0aXRsZSAoIlVuZW1wbG95bWVudCBSYXRlcyB2cy4gUG92ZXJ0eSBSYXRlcyBieSBFbGVjdGlvbiBXaW5uaW5nIFBhcnR5IikgKyANCiAgICB4bGFiICgiQ291bnR5IFVuZW1wbG95bWVudCBSYXRlIFslIG9mIENvdW50eSBQb3AuXSAoeCkiKSArIA0KICAgIHlsYWIgKCJDb3VudHkgUG92ZXJ0eSBSYXRlIFslIG9mIENvdW50eSBQb3AuXSAoeSkiKSAgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiYmx1ZSIsICJyZWQiKSkgKw0KICBzdGF0X3Ntb290aChtZXRob2QgPSBsbSwgc2U9RkFMU0UsIHNpemUgPSAwLjEpIA0KDQpgYGANCg0KYGBge3J9DQpwb3ZfdW5lbSA8LSBsbShgUG92ZXJ0eV9SYXRlICglKWB+YFVuZW1wbG95bWVudF9SYXRlICglKWAgLCBkYXRhID0gYWxsLmRhdGEpDQpzdW1tYXJ5KHBvdl91bmVtKQ0KDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGFsbC5kYXRhLCBhZXMoeCA9YWxsLmRhdGEkYEhpZ2hlcl9FZF9QZXJjZW50YCwgeT1hbGwuZGF0YSRgUG92ZXJ0eV9SYXRlICglKWAsIGNvbG9yPWBwYXJ0eWApKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjI1KSArDQogICAgZ2d0aXRsZSAoIlBlcmNlbnQgb2YgUG9wdWxhdGlvbiB3LyBIaWdoZXIgRWR1Y2F0aW9uIHZzLg0KICAgICAgICAgICAgIFBvdmVydHkgUmF0ZXMgYnkgRWxlY3Rpb24gV2lubmluZyBQYXJ0eSIpICsgDQogICAgeGxhYiAoIkNvdW50eSBIaWdoZXIgRWQuIFJhdGUgWyUgb2YgQ291bnR5IFBvcC5dICh4KSIpICsgDQogICAgeWxhYiAoIkNvdW50eSBQb3ZlcnR5IFJhdGUgWyUgb2YgQ291bnR5IFBvcC5dICh5KSIpICArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKSArDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9IGdsbSwgc2U9RkFMU0UsIHNpemUgPSAwLjEpIA0KDQpgYGANCg0KYGBge3J9DQpoaWdoZWRfcG92IDwtIGdsbShgSGlnaGVyX0VkX1BlcmNlbnRgfmBQb3ZlcnR5X1JhdGUgKCUpYCwgZGF0YSA9IGFsbC5kYXRhKQ0Kc3VtbWFyeShoaWdoZWRfcG92KQ0KDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSA9IGFsbC5kYXRhLCBhZXMoeCA9YWxsLmRhdGEkYEhpZ2hlcl9FZF9QZXJjZW50YCwgeT1hbGwuZGF0YSRgVW5lbXBsb3ltZW50X1JhdGUgKCUpYCwgY29sb3I9YHBhcnR5YCkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMjUpICsNCiAgICBnZ3RpdGxlICgiUGVyY2VudCBvZiBQb3B1bGF0aW9uIHcvIEhpZ2hlciBFZHVjYXRpb24gdnMuDQogICAgICAgICAgICAgVW5lbXBsb3ltZW50IFJhdGVzIGJ5IEVsZWN0aW9uIFdpbm5pbmcgUGFydHkiKSArIA0KICAgIHhsYWIgKCJDb3VudHkgSGlnaGVyIEVkLiBSYXRlIFslIG9mIENvdW50eSBQb3AuXSAoeCkiKSArIA0KICAgIHlsYWIgKCJDb3VudHkgVW5lbXBsb3ltZW50IFJhdGUgWyUgb2YgQ291bnR5IFBvcC5dICh5KSIpICArDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpKSArDQogIHN0YXRfc21vb3RoKG1ldGhvZCA9IGxtLCBzZT1GQUxTRSwgc2l6ZSA9IDAuMSkgDQoNCmBgYA0KDQpgYGB7cn0NCmhpZ2hlZF91bmVtIDwtIGxtKGBIaWdoZXJfRWRfUGVyY2VudGB+YFVuZW1wbG95bWVudF9SYXRlICglKWAsIGRhdGEgPSBhbGwuZGF0YSkNCnN1bW1hcnkoaGlnaGVkX3VuZW0pDQoNCmBgYA0KDQojIENvbmNsdXNpb25zDQoNCldoaWxlIGFsbCB0aHJlZSBpbnZlc3RpZ2F0ZWQgcG90ZW50aWFsIGNvcnJlbGF0aW9ucyBpbmRpY2F0ZWQgYSBoaWdoIGxldmVsIG9mIHN0YXRpc3RpYyBzaWduaWZpY2FuY2Ugd2l0aCBwIHZhbHVlcyB3ZWxsIGJlbG93IDAuMDUsIGJvdGggdGhlIGBVbmVtcGxveW1lbnQgUmF0ZXMgdnMuIFBvdmVydHkgUmF0ZXMgYnkgRWxlY3Rpb24gV2lubmluZyBQYXJ0eWAgYW5kIGBQZXJjZW50IG9mIFBvcHVsYXRpb24gdy8gSGlnaGVyIEVkdWNhdGlvbiB2cy4gVW5lbXBsb3ltZW50IFJhdGVzIGJ5IEVsZWN0aW9uIFdpbm5pbmcgUGFydHlgIG1vZGVscyBpbGx1c3RyYXRlZCBhIHBvb3IgZml0IHdpdGggdmVyeSBsb3cgUiBzcXVhcmVkIHZhbHVlcy4gIFRoZSBgUGVyY2VudCBvZiBQb3B1bGF0aW9uIHcvIEhpZ2hlciBFZHVjYXRpb24gdnMuIFBvdmVydHkgUmF0ZXMgYnkgRWxlY3Rpb24gV2lubmluZyBQYXJ0eWAgYWxzbyBpbGx1c3RyYXRlZCBhIHBvb3IgZml0IGJ1dCBoYWQgdGhlIGhpZ2hlc3QgciBzcXVhcmVkIHZhbHVlIGluZGljYXRpbmcgcmVkdWNlZCB2YXJpYW5jZSBpbiB0aGlzIG1vZGVsLiBObyBjbHVzdGVyaW5nIHdhcyBub3RlZCBhbG9uZyB0aGUgcGFydHkgdmFyaWFibGVzLiANCg0KT3ZlcmFsbCBhIGNvcnJlbGF0aW9uIGJldHdlZW4gdW5lbXBsb3ltZW50IHJhdGVzLCBwb3ZlcnR5IHJhdGVzLCBoaWdoZXIgZWR1Y2F0aW9uIHJhdGVzLCBhbmQgY291bnR5IHdpbm5pbmcgcGFydHkgd2FzIG5vdCBpbGx1c3RyYXRlZC4gDQoNCiMgQ29kZQ0KDQpgYGANCg0KIyNyZWFkIGluIHRoZSBlbGVjdGlvbiBkYXRhIGFzIGVsZWN0aW9uX3Jhdw0KDQplbGVjdGlvbl9yYXcgPC0gcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ubGVwZXJhL3N0YTU1MS9yZWZzL2hlYWRzL21haW4vSFcwMS9kYXRhL2NvdW50eXByZXNpZGVudGlhbF9lbGVjdGlvbl8yMDAwLTIwMjAuY3N2JykNCg0KIyNjbGVhbiB0aGUgZWxlY3Rpb24gZGF0YSANCg0KZWxlY3Rpb24gPC0gZWxlY3Rpb25fcmF3ICU+JSANCiAgZ3JvdXBfYnkoY291bnR5X2ZpcHMpICU+JSAgICMjZ3JvdXAgYnkgY291bnR5IEZJUFMgY29kZQ0KICBhcnJhbmdlIChkZXNjKHllYXIpKSAlPiUgICAgIyNzb3J0IGJ5IGRlc2NlbmRpbmcgeWVhcg0KICBmaWx0ZXIgKHBhcnR5ID09ICJERU1PQ1JBVCIgfCBwYXJ0eSA9PSAiUkVQVUJMSUNBTiIpICU+JSAgICAjI2ZpbHRlciBmb3Igb25seSBkZW0gJiByZXANCiAgc3Vic2V0KCwgLWMoc3RhdGVfcG8sIG9mZmljZSwgdmVyc2lvbiwgbW9kZSkpICU+JSAgICMjcmVtb3ZlIHVubmVjZXNzYXJ5IGNvbHVtbnMNCiAgc2xpY2UgKDE6MikgJT4lICAgIyNwdWxsIHRvcCAyIHZhbHVlcyBmb3IgZWFjaCBncm91cCAoY291bnR5X2ZpcHMgc29ydGVkIGJ5IGRlc2MgeWVhcikgcHVsbGluZyBib3RoIGRlbSAmIHJlcCBmb3IgZWFjaCBjb3VudHkgYXQgbWF4IHllYXIgYXZhaWxhYmxlDQogIGFycmFuZ2UgKGRlc2MoY2FuZGlkYXRldm90ZXMpKSAlPiUgICAgIyNyZSBmaWx0ZXIgdG8gc29ydCBieSBncm91cCwgd2l0aCBoaWdoZXN0IGNhbmRpZGF0ZSB2b3RlcyBvbiB0b3ANCiAgc2xpY2UgKDEpICU+JSAgICAjI29ubHkga2VlcCB2YWx1ZSBmb3IgaGlnaGVzdCBjYW5kaWRhdGUgdm90ZXMgKHdpbm5lcikgcGVyIGNvdW50eQ0KICByZW5hbWUgKEZJUFMgPSBjb3VudHlfZmlwcykgICAjIyBzZXQgSUQgbmFtZSBmb3Igb3V0ZXIgam9pbg0KICANCkRUOjpkYXRhdGFibGUoZWxlY3Rpb24pDQoNCiMjIHJlYWQgaW4gdGhlIHVuZW1wbG95bWVudCBkYXRhIGFzIHVuZW1wbG95X3Jhdw0KDQp1bmVtcGxveV9yYXcgPC0gcmVhZC5jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ubGVwZXJhL3N0YTU1MS9yZWZzL2hlYWRzL21haW4vSFcwMS9kYXRhL1VuZW1wbG95bWVudC5jc3YnKQ0KDQojIyBjbGVhbiB0aGUgdW5lbXBsb3ltZW50IGRhdGENCnVuZW1wbG95IDwtIHVuZW1wbG95X3JhdyAlPiUgDQogIGZpbHRlciAoZ3JlcGwoJ1VuZW1wbG95bWVudCcsIEF0dHJpYnV0ZSkpICU+JSAgICN1c2UgZHBseXIgcGFja2FnZSB0byB1c2UgZmlsdGVyKGdyZXBsKCkpIHRvIGZpbHRlciBmb3Igc3RyaW5nIG9mIHRleHQgdG8gc2VsZWN0IG9ubHkgdGhlIHVuZW1wbG95bWVudCByYXRlIHZhbHVlcw0KICBtdXRhdGUgKHllYXIgPSBzdHJfc3ViKEF0dHJpYnV0ZSwgLTQpKSAlPiUgICMjY3JlYXRlIGEgbmV3IGNvbHVtbiB1c2luZyBtdXRhdGUgYW5kIHN0cmluZ3IgdG8gY3JlYXRlIGEgbmV3IGNvbHVtbiBmcm9tIHRoZSBsYXN0IDQgdmFsdWVzIG9mIHRoZSBhdHRyaWJ1dGUgY29sdW1uLCB0byBleHRyYWN0IHRoZSB5ZWFyIChzdHJfc3ViIC0+IHN0cmluZ3Jfc3Vic2V0KQ0KICBncm91cF9ieSAoRklQU19Db2RlKSAlPiUgICMjZ3JvdXAgYnkgRklQUyBjb2RlIGJlZm9yZSBzb3J0aW5nIHRvIGdldCBtYXggeWVhcg0KICBhcnJhbmdlIChkZXNjKHllYXIpKSAlPiUgICMjIHNvcnQgZGVzY2VuZGluZyB5ZWFyIHRvIHB1dCBtYXggYXZhaWxhYmxlIGF0IHRoZSB0b3ANCiAgc2xpY2UoMSkgJT4lICAgIyNzbGljZSB0b3AgdmFsdWUgdG8ga2VlcCBvbmx5IG1heCB5ZWFyIGZvciBlYWNoIEZJUFMgY29kZQ0KICBzdWJzZXQgKCwgYyhGSVBTX0NvZGUsIFZhbHVlKSkgJT4lICAgIyNyZW1vdmUgdW5uZWNlc3NhcnkgY29sdW1ucw0KICByZW5hbWUgKCJVbmVtcGxveW1lbnRfUmF0ZSAoJSkiID0gVmFsdWUsIEZJUFMgPSBGSVBTX0NvZGUpICAgICMjcmVuYW1lIGNvbHVtbiB0byByZWZsZWN0IHVuZW1wbG95bWVudCByYXRlICYgc2V0IElEIG5hbWUgZm9yIG91dGVyIGpvaW4NCg0KRFQ6OmRhdGF0YWJsZSh1bmVtcGxveSkNCg0KIyMgcmVhZCBpbiB0aGUgcG92ZXJ0eSBkYXRhIGFzIHBvdmVydHlfcmF3DQoNCnBvdmVydHlfcmF3IDwtIHJlYWQuY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vbmxlcGVyYS9zdGE1NTEvcmVmcy9oZWFkcy9tYWluL0hXMDEvZGF0YS9Qb3ZlcnR5RXN0aW1hdGVzLmNzdicpDQoNCiMjY2xlYW4gdGhlIHBvdmVydHkgZGF0YQ0KcG92ZXJ0eSA8LSBwb3ZlcnR5X3JhdyAlPiUgDQogIGZpbHRlciAoQXR0cmlidXRlID09ICJQQ1RQT1ZBTExfMjAxOSIpICU+JSAgIyNGaWx0ZXIgZm9yIDIwMTkgcG92ZXJ0eSB2YWx1ZXMNCiAgc3Vic2V0ICgsIGMoRklQU3R4dCwgVmFsdWUpKSAlPiUgICMjcmVtb3ZlIHVubmVjY2Vzc2FyeSBjb2x1bW5zDQogIHJlbmFtZSAoIlBvdmVydHlfUmF0ZSAoJSkiID0gVmFsdWUsIEZJUFMgPSBGSVBTdHh0KSAgICMjcmVuYW1lIFZhbHVlIGNvbHVtbiB0byBQb3ZlcnR5X1JhdGUgKCUpIGFuZCBzZXQgSUQgbmFtZSBmb3Igb3V0ZXIgam9pbg0KDQpEVDo6ZGF0YXRhYmxlKHBvdmVydHkpDQoNCiMjIHJlYWQgaW4gdGhlIHVuZW1wbG95bWVudCBkYXRhIGFzIGVkdV9yYXcNCg0KZWR1X3JhdyA8LSByZWFkLmNzdignaHR0cHM6Ly9ubGVwZXJhLmdpdGh1Yi5pby9zdGE1NTEvSFcwMS9kYXRhL0VkdWNhdGlvbi5jc3YnLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQoNCg0KI2NsZWFuIGVkdWNhdGlvbiBkYXRhDQplZHUgPC0gZWR1X3JhdyAlPiUgDQogIHN1YnNldCAoLCBjKEZJUFMuQ29kZSwgTGVzcy50aGFuLmEuaGlnaC5zY2hvb2wuZGlwbG9tYS4uMjAxNS4xOSwgSGlnaC5zY2hvb2wuZGlwbG9tYS5vbmx5Li4yMDE1LjE5LCBTb21lLmNvbGxlZ2Uub3IuYXNzb2NpYXRlLnMuZGVncmVlLi4yMDE1LjE5LAlCYWNoZWxvci5zLmRlZ3JlZS5vci5oaWdoZXIuLjIwMTUuMTkpKSAlPiUgICMjcmVtb3ZlIGlycmVsZXZhbnQgY29sdW1ucw0KICByZW5hbWUgKCAiTm9fSFMiID0gTGVzcy50aGFuLmEuaGlnaC5zY2hvb2wuZGlwbG9tYS4uMjAxNS4xOSwgIkhTIiA9IEhpZ2guc2Nob29sLmRpcGxvbWEub25seS4uMjAxNS4xOSwgIkFTX1NvbWVDb2wiID0gU29tZS5jb2xsZWdlLm9yLmFzc29jaWF0ZS5zLmRlZ3JlZS4uMjAxNS4xOSwJIkJTX01vcmUiID0gQmFjaGVsb3Iucy5kZWdyZWUub3IuaGlnaGVyLi4yMDE1LjE5LCBGSVBTID0gRklQUy5Db2RlKSAgIyNyZW5hbWUgZm9yIGVhc2UgYW5kIHNldCBJRCBuYW1lIGZvciBvdXRlciBqb2luDQplZHUkTm9fSFMgPSBnc3ViKCcsJywgJycsIGVkdSROb19IUykgIyNyZW1vdmUgY29tbWFzIGZyb20gbnVtYmVyIHdpdGggY29tbWFzIHNhdmVkIGFzIGNoYXJhY2h0ZXJzDQplZHUkSFMgPSBnc3ViKCcsJywgJycsIGVkdSRIUykNCmVkdSRBU19Tb21lQ29sID0gZ3N1YignLCcsICcnLCBlZHUkQVNfU29tZUNvbCkNCmVkdSRCU19Nb3JlID0gZ3N1YignLCcsICcnLCBlZHUkQlNfTW9yZSkNCmVkdSROb19IUyA9IGFzLm51bWVyaWMoZWR1JE5vX0hTKSAgIyN0cmFuc2Zvcm0gdG8gbnVtZXJpYyB0byBhZ2dyZWdhdGUNCmVkdSRIUyA9IGFzLm51bWVyaWMoZWR1JEhTKQ0KZWR1JEFTX1NvbWVDb2wgPSBhcy5udW1lcmljKGVkdSRBU19Tb21lQ29sKQ0KZWR1JEJTX01vcmUgPSBhcy5udW1lcmljKGVkdSRCU19Nb3JlKQ0KICANCkRUOjpkYXRhdGFibGUoZWR1KQ0KDQojI21lcmdlIGVsZWN0aW9uIGRhdGEgJiBlZHVjYXRpb24gZGF0YSBhcyBlbGVjdGlvbi5lZHVjYXRpb24gDQplbGVjdGlvbi5lZHVjYXRpb24gPC0gZnVsbF9qb2luICgNCiAgeCA9IGVsZWN0aW9uLA0KICB5ID0gZWR1LA0KICBieSA9ICJGSVBTIiwNCiAga2VlcCA9IEZBTFNFDQopDQoNCg0KIyNtZXJnZSBlbGVjdGlvbi5lZHVjYXRpb24gJiBwb3ZlcnR5IGRhdGEgYXMgZWxlY3Rpb24uZWR1Y2F0aW9uLnBvdmVydHkNCmVsZWN0aW9uLmVkdWNhdGlvbi5wb3ZlcnR5IDwtIGZ1bGxfam9pbigNCiAgeCA9IGVsZWN0aW9uLmVkdWNhdGlvbiwNCiAgeSA9IHBvdmVydHksDQogIGJ5ID0gIkZJUFMiLA0KICBrZWVwID0gRkFMU0UNCikNCg0KIyNtZXJnZSBlbGVjdGlvbi5lZHVjYXRpb24ucG92ZXJ0eSAmIHVuZW1wbG95bWVudCBkYXRhIGFzIGFsbC5kYXRhDQphbGwuZGF0YSA8LSBmdWxsX2pvaW4gKA0KICB4ID0gZWxlY3Rpb24uZWR1Y2F0aW9uLnBvdmVydHksDQogIHkgPSB1bmVtcGxveSwNCiAgYnkgPSAiRklQUyIsDQogIGtlZXAgPSBGQUxTRQ0KKQ0KDQphbGwuZGF0YSA8LSBhbGwuZGF0YSAgJT4lIA0KICBmaWx0ZXIoRklQUyAhPSAwKSAlPiUgIyNmaWx0ZXIgb3V0IHZhbHVlcyB3aXRoIG5vIGFzc29jaWF0ZWQgY291bnR5IG9yIHN0YXRlIG5hbWUNCiAgZmlsdGVyKEZJUFMgIT0gMTAwMCkgJT4lIA0KICBtdXRhdGUgKEhpZ2hlcl9FZF9QZXJjZW50ID0gKCgoYEFTX1NvbWVDb2xgICsgYEJTX01vcmVgKS8oYE5vX0hTYCsgYEhTYCArIGBBU19Tb21lQ29sYCArIGBCU19Nb3JlYCkpKjEwMCkpICAjIyUgb2YgcG9wIHdpdGggaGlnaGVyIGVkLg0KDQpjb3VudHlfZGF0YV9yYXcgPC0gc2Y6OnJlYWRfc2YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9wbG90bHkvZGF0YXNldHMvbWFzdGVyL2dlb2pzb24tY291bnRpZXMtZmlwcy5qc29uIikgIyNwdWxsIGluIG1hcCBvZiBGSVBTIGNvZGVzIA0KDQpGSVBTY29udmVydF9yYXcgPC0gc2Y6OnJlYWRfc2YoImh0dHBzOi8vbmxlcGVyYS5naXRodWIuaW8vc3RhNTUxL0hXMDEvZGF0YS9maXBzMmdlb2NvZGUuY3N2IikgIyNwdWxsIGluIGtleSBvZiBGSVBTIGNvZGVzIHRvIGxhdC9sb25nDQoNCmNvdW50eV9kYXRhIDwtIGNvdW50eV9kYXRhX3JhdyAlPiUgICNjbGVhbiBjb3VudHkgZGF0YSB0byBsaXN0IEZJUFMgYXMgbnVtZXJpYyAmIG1hdGNoIG5hbWUgdG8ga2V5IGZvciBqb2luDQogIG11dGF0ZSAoaWQgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihpZCkpKSAlPiUgDQogIHJlbmFtZSAoRklQUyA9IGlkKQ0KDQpGSVBTY29udmVydCA8LSBGSVBTY29udmVydF9yYXcgJT4lICANCiAgbXV0YXRlIChmaXBzID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoZmlwcykpLCBsb24gPSBhcy5udW1lcmljKGFzLmNoYXJhY3Rlcihsb24pKSwgbGF0ID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobGF0KSkpICU+JSANCiAgcmVuYW1lIChGSVBTID0gZmlwcywgTG9uZ2l0dWRlID0gbG9uLCBMYXRpdHVkZSA9IGxhdCkNCg0KYWxsLmRhdGEuam9pbiA8LSBmdWxsX2pvaW4oDQogIHggPSBhbGwuZGF0YSwNCiAgeSA9IEZJUFNjb252ZXJ0LA0KICBieSA9ICJGSVBTIiwNCiAga2VlcCA9IEZBTFNFDQopICU+JSANCiAgdW5ncm91cCgpDQoNCmBgYA0KDQo=