1 Introduction

Access to transportation is an important social determinant of health (SDOH). People’s transit environment affects their ability to reach fresh food and healthcare institutions which can determine health on a daily basis. Transit access also determines what jobs people can reach which has important long-term socioeconomic consequences.

The purpose of my analysis will be to examine equity in the apportionment of funds from the $2.5 billion MARTA expansion tax approved in 2016 in the City of Atlanta. Disease mapping will be used to illustrate the distribution of funds at the census tract level and spatial regression will be used to determine if dollars received by a tract significantly covary with factors like median income, population, racial composition, etc. Necessary data on project budgets will be drawn from newspaper articles and the spatial location of new projects will be digitized into line files from project maps available on the MARTA website.

2 Data Import and Cleaning

Libraries

library(rio)
library(dplyr)
library(bit64)
library(sf)
library(tmap)
library(tigris)
library(lwgeom)
library(tidycensus)

City of Atlanta Census Tracts

City of Atlanta census tracts (prior to annexation of Emory) and associated attribute data were manually downloaded from the 500 cities project page on the CDC website. Next the geographies and attributes were imported and joined with the following code.

five_city <-import('C:/Users/dvern/Desktop/MARTA Project/500_cities.csv')
all_tracts <-st_read('C:/Users/dvern/Desktop/MARTA Project/tracts/500Cities_Tracts_Clip.shp')

atl_tracts <- all_tracts %>% filter(PlaceName == 'Atlanta')

atl_data <- five_city %>% filter(PlaceName == 'Atlanta')

atl_tracts$plctract10 <- as.character(atl_tracts$plctract10)

atl <- left_join(atl_tracts, atl_data, by= c('plctract10'='Place_TractID'))

#st_write(atl, 'C:/Users/dvern/Desktop/MARTA Project/atl_tracts.shp') 

#ESRI shapefile of atl census tracts saved to assist with georeferencing of project lines

Projection

The UTM Zone 16N projection was chosen for this project as it maintains distance relationships in small areas on the scale of cities and/or counties.

x <- 32616  #makes a variable to hold the EPSG code for UTM 16N
atl <-st_transform(atl, crs = x) %>% select(Population2010, TractFIPS)  #trim attributes I'm not concerned with for now
atl$TractFIPS <- as.character(atl$TractFIPS)
atl <- atl %>% rename(GEOID = TractFIPS) #change variable name to facilitate future join operation

Add in Emory/CDC

Emory and the CDC were not incorporated into the City of Atlanta at the time of the 500 cities project. This code will use the tidy census package to download the Dekalb county census tracts, filter down to Emory’s tract, then join it with the rest of Atlanta.

options(tigris_use_cache=TRUE) #prevent redownloading files
dek_tracts <- tracts('GA', county = 'Dekalb') %>% st_as_sf() #download census tracts to Dekalb county

emory <- dek_tracts %>% filter(NAME == '224.02') %>% st_transform(x) %>% select(GEOID) %>% 
  mutate(Population2010 = 1) # need to add placeholder population variable to enable rowbind with rest of ATL

atl_plus <- rbind(atl, emory)

Project Lines File

Digitization of the planned MARTA expansion project segments was conducted in ArcGIS. First the following project map was georeferenced to the atlanta census tracts file from earlier using directions found at this Brown university pdf.

Next the digitized segments were brought into the R environment.


dig_segs <-st_read('C:/Users/dvern/Desktop/MARTA Project/New segs/new_segs1.shp')
dig_segs.proj <-st_transform(dig_segs, crs=x)

dig_segs.proj$Name <- as.character(dig_segs.proj$Name) #assist future join

Finally the projected budget for each project was collated from this Atlanta Journal-Constitution article and imported as a table. This was the joined with the projects line file.

proj_vals <- import('C:/Users/dvern/Desktop/MARTA Project/proj_values.csv')

seg_vals <- left_join(dig_segs.proj, proj_vals) #associates each segment with the project budget for that segment

3 Geoprocessing Steps

Segment Length and budget ratio

The following steps calculate the individual segment lengths and the total length of each project (some comprise many segments, such as the addition of more frequent bus service along some routes). Then a cost per mile ratio was generated from total budget/total length. This ratio calculation took place in a separate data frame due to the use of groupby and the final step joins the ratio back to the original segments file.

#get individual segment length

seg_vals$length <- st_length(seg_vals)
seg_vals$length <- as.numeric(seg_vals$length) #st_length outputs in the units class which is incompatible with calculations
seg_vals <- seg_vals %>% mutate(km = length / 1000) %>% mutate(miles = km*0.621371)  #add lengths in km and miles


#forming the ratio variable for each project

ratio <- seg_vals %>% group_by(Name) %>% summarise(tot_len = sum(miles)) #sum lengths of segments that fall under the same project

rat1 <- left_join(ratio, proj_vals) #join in the proj values so that a $/mile ratio variable can be made
rat2 <- rat1 %>% mutate(ratio = Budg_mil / tot_len)

rat3 <- rat2 %>% st_drop_geometry() %>% select(ratio, Name) #select variables for joining back to main, ungrouped, segments file

#join ratio to original segments file

seg_vals1 <- left_join(seg_vals, rat3)

Joining segments to tracts, calculating total budgeted spending per tract

This step will join segments to tracts using st_intersection which will chop the lines into smaller sections based on what tract they intersect with. This will enable the calculation of the spending per section. Then, once the sections are joined with polygons, aggregation to the polygon will give us the total spending per polygon using the summarize step.

# intersection
int = st_intersection(seg_vals1, atl_plus)
# find out about the length of each line segment
int$len = st_length(int)
int1 <- int %>% mutate(km = len / 1000) %>% mutate(miles = km*0.621371)  #add lengths in km and miles
int2 <- int1 %>% mutate(spending = miles * ratio)

# spatial overlay
join_new = st_join(atl_plus, int2)
# use the ID of the polygon for the aggregation
out = group_by(join_new, GEOID.x) %>%
  summarize(tot_spend = sum(spending))

out[is.na(out)] <- 0 #replace NAs with zeros to assist future calculations

Next the major projects will be separated out by type to assist with future visulaization.

##separate out project types for mapping
LRT <- seg_vals %>% filter(Type == 'LRT')
FLBS <- seg_vals %>% filter(Type == 'FLBS')
BRT <- seg_vals %>% filter(Type == 'BRT')
ART <- seg_vals %>% filter(Type == 'ART' | Name == 'Cleveland AVE')

First Thematic Maps

The following code makes the first thematic maps of the spatial variation in budgeted MARTA spending. Major Project lines are shown for context. A version where the tracts with zero spending were shown as grey was experimented with.

  tm_shape(out) +
  tm_fill('tot_spend', 
          title = 'Budgeted Spending\n(Millions USD)',
          style = 'quantile',
          palette = 'BuPu') + 
  tm_borders() +
#tm_shape(out_none) + tm_fill('spend', col = 'grey', alpha = 1) + tm_borders() +
  tm_shape(LRT) + tm_lines(lwd = 2) +
  tm_shape(BRT) + tm_lines(lwd = 2, col = 'red') +
  tm_shape(ART) + tm_lines(lwd = 2, col = 'blue') + 
    #tm_shape(FLBS) + tm_lines(lwd = 1, col = 'navy') + 
  #tm_add_legend(type = 'fill', col = 'grey', labels = '0.00') +
  tm_add_legend(type = 'line', col = 'black', lwd = 3, labels = 'LRT') +
  tm_add_legend(type = 'line', col = 'blue', lwd = 3, labels = 'ART') +
  tm_add_legend(type = 'line', col = 'red', lwd = 3, labels = 'BRT') + 
  
  tm_credits('Source: Atlanta Journal-Constitution', fontface = 'bold', position = c('LEFT','BOTTOM')) +
  
  tm_layout(legend.title.fontface = 'bold')

*Note that this appears uncrowded in the normal viewer and upon export as a picture.

#make a dataset with just zero spending tracts
out$spend <- as.numeric(out$tot_spend)
out_none <- out %>% filter(spend == 0)
#map with greyed out zeros
  tm_shape(out) +
  tm_fill('tot_spend', 
          title = 'Budgeted Spending\n(Millions USD)',
          style = 'quantile',
          palette = 'BuPu') + 
  tm_borders() +
tm_shape(out_none) + tm_fill('spend', col = 'grey', alpha = 1) + tm_borders() +
  tm_shape(LRT) + tm_lines(lwd = 1) +
  tm_shape(BRT) + tm_lines(lwd = 1, col = 'red') +
  tm_shape(ART) + tm_lines(lwd = 1, col = 'blue') + 
    tm_shape(FLBS) + tm_lines(lwd = 1, col = 'navy') + 
  tm_add_legend(type = 'fill', col = 'grey', labels = '0.00') +
  tm_add_legend(type = 'line', col = 'black', lwd = 3, labels = 'LRT') +
  tm_add_legend(type = 'line', col = 'blue', lwd = 3, labels = 'ART') +
  tm_add_legend(type = 'line', col = 'red', lwd = 3, labels = 'BRT') + 
  
  tm_credits('Source: Atlanta Journal-Constitution', fontface = 'bold', position = c('LEFT','BOTTOM')) +
  
  tm_layout(legend.title.fontface = 'bold')

A close examination of this second map reveals that there are a few central tracts which probably shouldn’t be identified as zero spending. However, due to small inaccuracies in the georeferencing and digitization process, some segments and tracts don’t overlap when they probably should. I say probably because in most cases, the tract boundaries appear to follow major transportation routes.

Unfortunately, at this stage I’ve already spent a LOT of time geoprocessing and I need to get to the analysis. To improve this in the future, I would need to compare the tract boundaries in question and see if they indeed fall along major roads/ transit routes that are in the MARTA project list. From there, I’d need to redigitize segments that match up better.

On to analysis.

Downloading, cleaning, and joining other attribute data

Just kidding about being ready to do analysis. Now I need to download some covariate data (population and median income) from the ACS for my tracts. This took a fair amount of processing as Atlanta spreads across both Fulton and Dekalb counties.

#### attributes to tracts join
  
  library(tidycensus)
  
  all_vars <- load_variables(year = 2017, dataset = 'acs5', cache = T)
  
  # From my search, I found that I want the following variables: 'B25026_001','B21004_001'
  
  ## this part grabs the variables for the atl tracts in fulton county
  
  fult_pop <- get_acs(geography = 'tract',
                       variables = 'B25026_001',
                       county = 'Fulton',
                       state = 'Georgia',
                      year = 2017,
                       survey = 'acs5') %>% rename(population_2017= estimate)
  
  fult_inc <- get_acs(geography = 'tract',
                      variables = 'B21004_001',
                      county = 'Fulton',
                      state = 'Georgia',
                      year = 2017,
                      survey = 'acs5') %>% rename(med_income_17= estimate) %>% select(med_income_17, GEOID)
  
  
  atl_pop <-left_join(atl_plus, fult_pop)
  
  atl_pop_inc <- left_join(atl_pop, fult_inc)

  atl_atts_fult <- atl_pop_inc %>% select(GEOID, population_2017, Population2010, med_income_17)
  
  
  
  ### This part grabs the variables for the atl tracts in dekalb county
  
  dek_pop <- get_acs(geography = 'tract',
                      variables = 'B25026_001',
                      county = 'Dekalb',
                      state = 'Georgia',
                      year = 2017,
                      survey = 'acs5') %>% rename(population_2017= estimate)
  
  dek_inc <- get_acs(geography = 'tract',
                      variables = 'B21004_001',
                      county = 'Dekalb',
                      state = 'Georgia',
                      year = 2017,
                      survey = 'acs5') %>% rename(med_income_17= estimate) %>% select(med_income_17, GEOID)
  
  
  dek_pop <-left_join(atl_plus, dek_pop)
  
  dek_pop_inc <- left_join(dek_pop, dek_inc)
  
  atl_atts_dek <- dek_pop_inc %>% select(GEOID, population_2017, Population2010, med_income_17) %>% filter(is.na(population_2017) == F) %>% st_drop_geometry()
  
 
  
  ## now join fulton and dekalb polys into one atlanta attributes dataframe
  
  atl_atts <- left_join(atl_atts_fult, atl_atts_dek, by= 'GEOID')
  atl_atts1 <- atl_atts %>% 
    mutate(pop_17 = ifelse(is.na(population_2017.x),population_2017.y, population_2017.x)) %>%
    mutate(inc_17 = ifelse(is.na(med_income_17.x),med_income_17.y, med_income_17.x)) %>% select(GEOID, pop_17, inc_17) %>% st_drop_geometry()
    
  #above mutate code is necessary to consolidate the fulton / dekalb attributes into single columns
  
  
  # join with spending polygons
  
  
  atl_spend_atts <- left_join(out, atl_atts1, by = c('GEOID.x' = 'GEOID'))
  
  atl_spend_atts <- atl_spend_atts %>% filter(is.na(pop_17) == F) #remove some weird tracts that don't map and have no data

  

4 Analysis

4.1 Clusters

Looking at the map of budgeted spending by census tract it seems obvious that the constant risk assumption is violated. However, we’d like to know in what areas the spending is higher than expected based on the population of a tract. Thus, we will produce a map of poissson probabilities to test for and locate the local extremes.

library(spdep) #package for performing cluster analysis

atl_spend_atts$tot_spend <- as.numeric(atl_spend_atts$tot_spend) #conmvert from units class to numeric

atl_spend_atts <- atl_spend_atts %>% mutate(spend_ratio = ifelse( pop_17 > 0,(tot_spend * 1000000 / pop_17), NA)) %>%filter(is.na(spend_ratio) == F)

spend_probs <- probmap(n =atl_spend_atts$tot_spend,
                       x = atl_spend_atts$pop_17,
                       row.names = atl_spend_atts$GEOID.x)

atl_spend_atts$pmap <- spend_probs$pmap
#generate poisson probs
# This just summarizes (merges polygons) of all counties with significant HIGH risk
spend_hi <- atl_spend_atts %>% #form contiguous areas of sig high or low spending into polygons
  filter(pmap > 0.95) %>% summarise()
spend_lo <- atl_spend_atts %>%
  filter(pmap < 0.05) %>% summarise()
# make map of sig high or low spending areas
m1 <-tm_shape(atl_spend_atts) + 
  tm_fill('spend_ratio',
          palette = 'BuPu',
           style = 'quantile',
          title = 'Spending per\nPerson (USD)') +
  tm_borders() + 
  tm_layout(legend.position = c('RIGHT','TOP'),
            main.title = 'Clusters of MARTA\nExpansion Spending by Population') +
  
# Now plot only borders of high or low rate counties
  tm_shape(spend_lo) + 
  tm_borders(col = 'blue', lwd = 2) + 
  tm_shape(spend_hi) + 
  tm_borders(col = 'red', lwd = 2) +
# general layout stuff...  
tm_layout(legend.outside = F, legend.text.size = 0.55, 
          legend.title.size = 0.65, legend.title.fontface = 'bold',
          legend.position = c('LEFT', 'TOP'), inner.margins = c(0.02, .03, .02, 0),
         main.title.position = 'center', main.title.size = 1, main.title.fontface = 'bold') + 
   tm_add_legend(type = 'line',
              col = 'red',
              lwd = 2,
              labels = 'Significantly high') +
   tm_add_legend(type = 'line',
              col = 'blue',
              lwd = 2,
              labels = 'Significantly low')

Looking at the above map, almost all of the Atlanta census tracts fall into the category of significant high or low spending. Now, this was done with population count as the denominator, but what if using population density would give us a more clear picture?

The following code brings in the land area (km2) variable for each tract and calculates population density in persons per km2.

dek_tracts$ALAND <- as.numeric(dek_tracts$ALAND) #convert to numeric for calculations

dek_tracts_a <- dek_tracts %>% 
  select(GEOID, ALAND) %>% mutate(km2 = ALAND/1000000) %>% st_drop_geometry() 

#fulton tracts

ful_tracts <- dek_tracts <- tracts('GA', county = 'Fulton') %>% st_as_sf() %>% st_drop_geometry()

ful_tracts$ALAND <- as.numeric(ful_tracts$ALAND)

ful_tracts_a <- ful_tracts %>% 
  select(GEOID, ALAND) %>% mutate(km2 = ALAND/1000000)

                                                      
#joins and cleaning

fult_a <-left_join(atl_plus, ful_tracts_a)

dek_a <-left_join(atl_plus, dek_tracts_a)

all_a <- rbind(fult_a, dek_a) %>% filter(is.na(ALAND) == F) %>% select(GEOID, km2) %>% st_drop_geometry() #drop geometry to make a table that can be joined to main attributes file

area_atts <- left_join(atl_spend_atts, all_a, by=c('GEOID.x'='GEOID')) 
area_atts <-area_atts %>% mutate(pop_dens = pop_17 / km2) #create pop density variable in persons per km2

                               

Now we’ll run the test of local extremes again but with population density as the denominator. Finally, we’ll map the significant results to see how different it is than using pop counts.

Analysis:

spend_probs_dens <- probmap(n =area_atts$tot_spend,
                       x = area_atts$pop_dens,
                       row.names = area_atts$GEOID.x)
area_atts$pmap <- spend_probs_dens$pmap
#generate poisson probs
area_atts <- area_atts %>% mutate(spend_ratio_d = tot_spend *1000000/ pop_dens)

Map:

# This just summarizes (merges polygons) of all counties with significant HIGH risk
spend_hi_d <- area_atts %>% #form contiguous areas of sig high or low spending into polygons
  filter(pmap > 0.95) %>% summarise()
spend_lo_d <- area_atts %>%
  filter(pmap < 0.05) %>% summarise()
# make map of sig high or low spending areas
 m2 <-tm_shape(area_atts) + 
  tm_fill('spend_ratio_d',
          palette = 'BuPu',
           style = 'quantile',
          title = 'Spending per\nPerson Per Km2 (USD)') +
  tm_borders() + 
  tm_layout(legend.position = c('RIGHT','TOP'),
            main.title = 'Clusters of MARTA\nExpansion Spending by Pop. Density') +
  
# Now plot only borders of 'high-rate' counties
  tm_shape(spend_lo_d) + 
  tm_borders(col = 'blue', lwd = 2) + 
  tm_shape(spend_hi_d) + 
  tm_borders(col = 'red', lwd = 2) +
#general layout 
tm_layout(legend.outside = F, legend.text.size = 0.55, 
          legend.title.size = 0.65, legend.title.fontface = 'bold',
          legend.position = c('LEFT', 'TOP'), inner.margins = c(0.02, .03, .02, 0),
         main.title.position = 'center', main.title.size = 1, main.title.fontface = 'bold') + 
   tm_add_legend(type = 'line',
              col = 'red',
              lwd = 2,
              labels = 'Significantly high') +
   tm_add_legend(type = 'line',
              col = 'blue',
              lwd = 2,
              labels = 'Significantly low')
tmap_arrange(m1, m2)

The use of population density does change the distribution of significantly extreme tracts such that there are fewer significant tracts overall and some tracts switch from sig hi to low and vice versa. Either way you slice it, there are clear significant extreme clusters of spending. However, using pop density as the denominator seems to remove some of the noise caused by having similar populations in drastically differently sized tracts.

4.2 Regression

Now that I’ve established significant clustering of high and low spending exists,I’d like to see if this phenomenon can be explained by other underlying factors. In particular, I am interested to examine the correlation of median tract income with budgeted spending.

First I will fit my linear regression models.

#empty model

#before my tot_spend variable was in millions of dollars, I will convert it back to dollars for running my models

area_atts$tot_spend <- area_atts$tot_spend / 1000000

m0 <- lm(tot_spend ~ 1, data = area_atts)

#conditional model

m1 <- lm(tot_spend ~ pop_dens + inc_17, data = area_atts)

summary(m1)

The results of my conditional model show that, holding population density constant, 2017 median tract income was a significant negative predictor of budgeted MARTA spending. Based on the model, each dollar increase in median income is associated with a 796.8 dollar decrease in planned spending. However, it is important to note that since I have zero dollar values for 26 of 130 tracts, the residuals and residual standard error for this model are huge.

summary(area_atts$spend)

Now I’ll test the residuals from my model to see whether our factors explain any of the variation in spatial autocorrelation between tracts.

This will involve first choosing a neighbors definiton and then running the lm.morantest function. I have decided to use k-nearest neighbors of 3 due to the fact that emory is not touching any other atl tracts and several border tracts have only one contiguous neighbor.

##Creating nb object
#since we're using proximity based nb object, we need the tract centroid points
area_atts_sp <- as(area_atts, 'Spatial')
area_points  <- coordinates(area_atts_sp)
area_k3 <- knearneigh(area_points, k =3) #creates set of 3 nearest neighbors
area_k3_nb <- knn2nb(area_k3, row.names = area_atts_sp$GEOID.x, sym = T) #creates a symmetrical nb object
area_listw <- area_k3_nb %>% nb2listw() #creates listw object for Moran's I test
##performing lm.morantest()
#empty model residuals
lm.morantest(m0, listw = area_listw)

    Global Moran I for regression residuals

data:  
model: lm(formula = tot_spend ~ 1, data = area_atts)
weights: area_listw

Moran I statistic standard deviate = 4.5259, p-value = 3.007e-06
alternative hypothesis: greater
sample estimates:
Observed Moran I      Expectation         Variance 
     0.281758061     -0.007751938      0.004091894 
#conditional model residuals
#m1_1 <- lm(tot_spend ~ inc_17, data = area_atts) #I thought of to just test the effect of income since pop_dens wasn't significant but decided to include pop_dens after all
lm.morantest(m1, listw = area_listw)

    Global Moran I for regression residuals

data:  
model: lm(formula = tot_spend ~ pop_dens + inc_17, data = area_atts)
weights: area_listw

Moran I statistic standard deviate = 3.9708, p-value = 3.581e-05
alternative hypothesis: greater
sample estimates:
Observed Moran I      Expectation         Variance 
     0.233938193     -0.016975080      0.003992891 

From these tests we see there is relatively low spatial autocorrelation in model residuals for budgeted spending. Of that, only about 17% is explained by tract median income and population density. My conjecture is that much of the remaining autocorrelation is explained by the fact that many of the high-value projects are comprised of beltline rail. By definition, all these projects are contiguous and since the beltline follows old rail throughways in the city, their locations may be less entagled with other demographic factors.

For the final figures in this project, I’ll map the model residuals.

The maps illustrate that our factors slightly reduce spatial autocorrelation but there is still a fair amount of spatial structure present due to the periphery tracts with zero spending. Interestingly, some of the largest residuals are in the central portion of the city. This is where, as I mentioned previously, innacuracies in digitization may have missed assigning spending to some tracts.

LS0tDQp0aXRsZTogJ0VwaSA1OTAgRmluYWwgUHJvamVjdCcNCmF1dGhvcjogIkRlbm5pcyBOaWNob2xzIg0KZGF0ZTogIjEyLzEwLzE5Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogeWVzDQpzdWJ0aXRsZTogQXNzZXNzaW5nIHRoZSBlcXVpdHkgb2YgdGhlIHBsYW5uZWQgTUFSVEEgZXhwYW5zaW9uIGluIHRoZSBDaXR5IG9mIEF0bGFudGENCi0tLQ0KDQojSW50cm9kdWN0aW9uDQoNCkFjY2VzcyB0byB0cmFuc3BvcnRhdGlvbiBpcyBhbiBpbXBvcnRhbnQgc29jaWFsIGRldGVybWluYW50IG9mIGhlYWx0aCAoU0RPSCkuICBQZW9wbGUncyB0cmFuc2l0IGVudmlyb25tZW50IGFmZmVjdHMgdGhlaXIgYWJpbGl0eSB0byByZWFjaCBmcmVzaCBmb29kIGFuZCBoZWFsdGhjYXJlIGluc3RpdHV0aW9ucyB3aGljaCBjYW4gZGV0ZXJtaW5lIGhlYWx0aCBvbiBhIGRhaWx5IGJhc2lzLiAgVHJhbnNpdCBhY2Nlc3MgYWxzbyBkZXRlcm1pbmVzIHdoYXQgam9icyBwZW9wbGUgY2FuIHJlYWNoIHdoaWNoIGhhcyBpbXBvcnRhbnQgbG9uZy10ZXJtIHNvY2lvZWNvbm9taWMgY29uc2VxdWVuY2VzLiANCg0KVGhlICoqcHVycG9zZSoqIG9mIG15IGFuYWx5c2lzIHdpbGwgYmUgdG8gZXhhbWluZSBlcXVpdHkgaW4gdGhlIGFwcG9ydGlvbm1lbnQgb2YgZnVuZHMgZnJvbSB0aGUgJDIuNSBiaWxsaW9uIE1BUlRBIGV4cGFuc2lvbiB0YXggYXBwcm92ZWQgaW4gMjAxNiBpbiB0aGUgQ2l0eSBvZiBBdGxhbnRhLiBEaXNlYXNlIG1hcHBpbmcgd2lsbCBiZSB1c2VkIHRvIGlsbHVzdHJhdGUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBmdW5kcyBhdCB0aGUgY2Vuc3VzIHRyYWN0IGxldmVsIGFuZCBzcGF0aWFsIHJlZ3Jlc3Npb24gd2lsbCBiZSB1c2VkIHRvIGRldGVybWluZSBpZiBkb2xsYXJzIHJlY2VpdmVkIGJ5IGEgdHJhY3Qgc2lnbmlmaWNhbnRseSBjb3Zhcnkgd2l0aCBmYWN0b3JzIGxpa2UgbWVkaWFuIGluY29tZSwgcG9wdWxhdGlvbiwgcmFjaWFsIGNvbXBvc2l0aW9uLCBldGMuIE5lY2Vzc2FyeSBkYXRhIG9uIHByb2plY3QgYnVkZ2V0cyB3aWxsIGJlIGRyYXduIGZyb20gbmV3c3BhcGVyIGFydGljbGVzIGFuZCB0aGUgc3BhdGlhbCBsb2NhdGlvbiBvZiBuZXcgcHJvamVjdHMgd2lsbCBiZSBkaWdpdGl6ZWQgaW50byBsaW5lIGZpbGVzIGZyb20gcHJvamVjdCBtYXBzIGF2YWlsYWJsZSBvbiB0aGUgTUFSVEEgd2Vic2l0ZS4NCg0KDQojRGF0YSBJbXBvcnQgYW5kIENsZWFuaW5nDQoNCioqTGlicmFyaWVzKioNCg0KYGBge3J9DQpsaWJyYXJ5KHJpbykNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGJpdDY0KQ0KbGlicmFyeShzZikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkodGlncmlzKQ0KbGlicmFyeShsd2dlb20pDQpsaWJyYXJ5KHRpZHljZW5zdXMpDQpgYGANCg0KDQoNCioqQ2l0eSBvZiBBdGxhbnRhIENlbnN1cyBUcmFjdHMqKg0KDQpDaXR5IG9mIEF0bGFudGEgY2Vuc3VzIHRyYWN0cyAocHJpb3IgdG8gYW5uZXhhdGlvbiBvZiBFbW9yeSkgYW5kIGFzc29jaWF0ZWQgYXR0cmlidXRlIGRhdGEgd2VyZSBtYW51YWxseSBkb3dubG9hZGVkIGZyb20gdGhlIDUwMCBjaXRpZXMgcHJvamVjdCBwYWdlIG9uIHRoZSBDREMgd2Vic2l0ZS4gTmV4dCB0aGUgZ2VvZ3JhcGhpZXMgYW5kIGF0dHJpYnV0ZXMgd2VyZSBpbXBvcnRlZCBhbmQgam9pbmVkIHdpdGggdGhlIGZvbGxvd2luZyBjb2RlLg0KDQpgYGB7cn0NCmZpdmVfY2l0eSA8LWltcG9ydCgnQzovVXNlcnMvZHZlcm4vRGVza3RvcC9NQVJUQSBQcm9qZWN0LzUwMF9jaXRpZXMuY3N2JykNCmFsbF90cmFjdHMgPC1zdF9yZWFkKCdDOi9Vc2Vycy9kdmVybi9EZXNrdG9wL01BUlRBIFByb2plY3QvdHJhY3RzLzUwMENpdGllc19UcmFjdHNfQ2xpcC5zaHAnKQ0KDQphdGxfdHJhY3RzIDwtIGFsbF90cmFjdHMgJT4lIGZpbHRlcihQbGFjZU5hbWUgPT0gJ0F0bGFudGEnKQ0KDQphdGxfZGF0YSA8LSBmaXZlX2NpdHkgJT4lIGZpbHRlcihQbGFjZU5hbWUgPT0gJ0F0bGFudGEnKQ0KDQphdGxfdHJhY3RzJHBsY3RyYWN0MTAgPC0gYXMuY2hhcmFjdGVyKGF0bF90cmFjdHMkcGxjdHJhY3QxMCkNCg0KYXRsIDwtIGxlZnRfam9pbihhdGxfdHJhY3RzLCBhdGxfZGF0YSwgYnk9IGMoJ3BsY3RyYWN0MTAnPSdQbGFjZV9UcmFjdElEJykpDQoNCiNzdF93cml0ZShhdGwsICdDOi9Vc2Vycy9kdmVybi9EZXNrdG9wL01BUlRBIFByb2plY3QvYXRsX3RyYWN0cy5zaHAnKSANCg0KI0VTUkkgc2hhcGVmaWxlIG9mIGF0bCBjZW5zdXMgdHJhY3RzIHNhdmVkIHRvIGFzc2lzdCB3aXRoIGdlb3JlZmVyZW5jaW5nIG9mIHByb2plY3QgbGluZXMNCg0KYGBgDQoNCipQcm9qZWN0aW9uKg0KDQpUaGUgVVRNIFpvbmUgMTZOIHByb2plY3Rpb24gd2FzIGNob3NlbiBmb3IgdGhpcyBwcm9qZWN0IGFzIGl0IG1haW50YWlucyBkaXN0YW5jZSByZWxhdGlvbnNoaXBzIGluIHNtYWxsIGFyZWFzIG9uIHRoZSBzY2FsZSBvZiBjaXRpZXMgYW5kL29yIGNvdW50aWVzLg0KDQpgYGB7cn0NCnggPC0gMzI2MTYgICNtYWtlcyBhIHZhcmlhYmxlIHRvIGhvbGQgdGhlIEVQU0cgY29kZSBmb3IgVVRNIDE2Tg0KYXRsIDwtc3RfdHJhbnNmb3JtKGF0bCwgY3JzID0geCkgJT4lIHNlbGVjdChQb3B1bGF0aW9uMjAxMCwgVHJhY3RGSVBTKSAgI3RyaW0gYXR0cmlidXRlcyBJJ20gbm90IGNvbmNlcm5lZCB3aXRoIGZvciBub3cNCg0KYXRsJFRyYWN0RklQUyA8LSBhcy5jaGFyYWN0ZXIoYXRsJFRyYWN0RklQUykNCg0KYXRsIDwtIGF0bCAlPiUgcmVuYW1lKEdFT0lEID0gVHJhY3RGSVBTKSAjY2hhbmdlIHZhcmlhYmxlIG5hbWUgdG8gZmFjaWxpdGF0ZSBmdXR1cmUgam9pbiBvcGVyYXRpb24NCmBgYA0KDQoqQWRkIGluIEVtb3J5L0NEQyoNCg0KRW1vcnkgYW5kIHRoZSBDREMgd2VyZSBub3QgaW5jb3Jwb3JhdGVkIGludG8gdGhlIENpdHkgb2YgQXRsYW50YSBhdCB0aGUgdGltZSBvZiB0aGUgNTAwIGNpdGllcyBwcm9qZWN0LiBUaGlzIGNvZGUgd2lsbCB1c2UgdGhlIHRpZHkgY2Vuc3VzIHBhY2thZ2UgdG8gZG93bmxvYWQgdGhlIERla2FsYiBjb3VudHkgY2Vuc3VzIHRyYWN0cywgZmlsdGVyIGRvd24gdG8gRW1vcnkncyB0cmFjdCwgdGhlbiBqb2luIGl0IHdpdGggdGhlIHJlc3Qgb2YgQXRsYW50YS4NCg0KYGBge3J9DQpvcHRpb25zKHRpZ3Jpc191c2VfY2FjaGU9VFJVRSkgI3ByZXZlbnQgcmVkb3dubG9hZGluZyBmaWxlcw0KZGVrX3RyYWN0cyA8LSB0cmFjdHMoJ0dBJywgY291bnR5ID0gJ0Rla2FsYicpICU+JSBzdF9hc19zZigpICNkb3dubG9hZCBjZW5zdXMgdHJhY3RzIHRvIERla2FsYiBjb3VudHkNCg0KZW1vcnkgPC0gZGVrX3RyYWN0cyAlPiUgZmlsdGVyKE5BTUUgPT0gJzIyNC4wMicpICU+JSBzdF90cmFuc2Zvcm0oeCkgJT4lIHNlbGVjdChHRU9JRCkgJT4lIA0KICBtdXRhdGUoUG9wdWxhdGlvbjIwMTAgPSAxKSAjIG5lZWQgdG8gYWRkIHBsYWNlaG9sZGVyIHBvcHVsYXRpb24gdmFyaWFibGUgdG8gZW5hYmxlIHJvd2JpbmQgd2l0aCByZXN0IG9mIEFUTA0KDQphdGxfcGx1cyA8LSByYmluZChhdGwsIGVtb3J5KQ0KDQoNCmBgYA0KDQoNCioqUHJvamVjdCBMaW5lcyBGaWxlKioNCg0KRGlnaXRpemF0aW9uIG9mIHRoZSBwbGFubmVkIE1BUlRBIGV4cGFuc2lvbiBwcm9qZWN0IHNlZ21lbnRzIHdhcyBjb25kdWN0ZWQgaW4gQXJjR0lTLiBGaXJzdCB0aGUgZm9sbG93aW5nIHByb2plY3QgbWFwIHdhcyBnZW9yZWZlcmVuY2VkIHRvIHRoZSBhdGxhbnRhIGNlbnN1cyB0cmFjdHMgZmlsZSBmcm9tIGVhcmxpZXIgdXNpbmcgZGlyZWN0aW9ucyBmb3VuZCBhdCBbdGhpcyBCcm93biB1bml2ZXJzaXR5IHBkZl0oaHR0cHM6Ly9zNC5hZC5icm93bi5lZHUvUmVzb3VyY2VzL1R1dG9yaWFsL01vZHVsMi9HZW9yZWZlcmVuY2luZyUyMGFuZCUyMERpZ2l0aXppbmclMjAlMjBpbiUyMEFyY0dJUy5wZGYpLg0KDQohW10obWFydGFfbWFwLmpwZykNCg0KTmV4dCB0aGUgZGlnaXRpemVkIHNlZ21lbnRzIHdlcmUgYnJvdWdodCBpbnRvIHRoZSBSIGVudmlyb25tZW50Lg0KDQpgYGB7cn0NCg0KZGlnX3NlZ3MgPC1zdF9yZWFkKCdDOi9Vc2Vycy9kdmVybi9EZXNrdG9wL01BUlRBIFByb2plY3QvTmV3IHNlZ3MvbmV3X3NlZ3MxLnNocCcpDQpkaWdfc2Vncy5wcm9qIDwtc3RfdHJhbnNmb3JtKGRpZ19zZWdzLCBjcnM9eCkNCg0KZGlnX3NlZ3MucHJvaiROYW1lIDwtIGFzLmNoYXJhY3RlcihkaWdfc2Vncy5wcm9qJE5hbWUpICNhc3Npc3QgZnV0dXJlIGpvaW4NCmBgYA0KDQpGaW5hbGx5IHRoZSBwcm9qZWN0ZWQgYnVkZ2V0IGZvciBlYWNoIHByb2plY3Qgd2FzIGNvbGxhdGVkIGZyb20gdGhpcyBBdGxhbnRhIEpvdXJuYWwtQ29uc3RpdHV0aW9uIFthcnRpY2xlXShodHRwczovL3d3dy5hamMuY29tL2Jsb2cvY29tbXV0aW5nL21hcnRhLWZpbmFsLWF0bGFudGEtZXhwYW5zaW9uLXBsYW4tZGV0YWlsZWQtbG9vay91SUtHbDUySE9ZWjFGQUtmYmFWVGlKLykgYW5kIGltcG9ydGVkIGFzIGEgdGFibGUuIFRoaXMgd2FzIHRoZSBqb2luZWQgd2l0aCB0aGUgcHJvamVjdHMgbGluZSBmaWxlLg0KDQpgYGB7cn0NCnByb2pfdmFscyA8LSBpbXBvcnQoJ0M6L1VzZXJzL2R2ZXJuL0Rlc2t0b3AvTUFSVEEgUHJvamVjdC9wcm9qX3ZhbHVlcy5jc3YnKQ0KDQpzZWdfdmFscyA8LSBsZWZ0X2pvaW4oZGlnX3NlZ3MucHJvaiwgcHJval92YWxzKSAjYXNzb2NpYXRlcyBlYWNoIHNlZ21lbnQgd2l0aCB0aGUgcHJvamVjdCBidWRnZXQgZm9yIHRoYXQgc2VnbWVudA0KDQpgYGANCg0KI0dlb3Byb2Nlc3NpbmcgU3RlcHMNCg0KKipTZWdtZW50IExlbmd0aCBhbmQgYnVkZ2V0IHJhdGlvKioNCg0KVGhlIGZvbGxvd2luZyBzdGVwcyBjYWxjdWxhdGUgdGhlIGluZGl2aWR1YWwgc2VnbWVudCBsZW5ndGhzIGFuZCB0aGUgdG90YWwgbGVuZ3RoIG9mIGVhY2ggcHJvamVjdCAoc29tZSBjb21wcmlzZSBtYW55IHNlZ21lbnRzLCBzdWNoIGFzIHRoZSBhZGRpdGlvbiBvZiBtb3JlIGZyZXF1ZW50IGJ1cyBzZXJ2aWNlIGFsb25nIHNvbWUgcm91dGVzKS4gVGhlbiBhIGNvc3QgcGVyIG1pbGUgcmF0aW8gd2FzIGdlbmVyYXRlZCBmcm9tICp0b3RhbCBidWRnZXQvdG90YWwgbGVuZ3RoKi4gVGhpcyByYXRpbyBjYWxjdWxhdGlvbiB0b29rIHBsYWNlIGluIGEgc2VwYXJhdGUgZGF0YSBmcmFtZSBkdWUgdG8gdGhlIHVzZSBvZiBgZ3JvdXBieWAgYW5kIHRoZSBmaW5hbCBzdGVwIGpvaW5zIHRoZSByYXRpbyBiYWNrIHRvIHRoZSBvcmlnaW5hbCBzZWdtZW50cyBmaWxlLg0KDQpgYGB7cn0NCiNnZXQgaW5kaXZpZHVhbCBzZWdtZW50IGxlbmd0aA0KDQpzZWdfdmFscyRsZW5ndGggPC0gc3RfbGVuZ3RoKHNlZ192YWxzKQ0Kc2VnX3ZhbHMkbGVuZ3RoIDwtIGFzLm51bWVyaWMoc2VnX3ZhbHMkbGVuZ3RoKSAjc3RfbGVuZ3RoIG91dHB1dHMgaW4gdGhlIHVuaXRzIGNsYXNzIHdoaWNoIGlzIGluY29tcGF0aWJsZSB3aXRoIGNhbGN1bGF0aW9ucw0Kc2VnX3ZhbHMgPC0gc2VnX3ZhbHMgJT4lIG11dGF0ZShrbSA9IGxlbmd0aCAvIDEwMDApICU+JSBtdXRhdGUobWlsZXMgPSBrbSowLjYyMTM3MSkgICNhZGQgbGVuZ3RocyBpbiBrbSBhbmQgbWlsZXMNCg0KDQojZm9ybWluZyB0aGUgcmF0aW8gdmFyaWFibGUgZm9yIGVhY2ggcHJvamVjdA0KDQpyYXRpbyA8LSBzZWdfdmFscyAlPiUgZ3JvdXBfYnkoTmFtZSkgJT4lIHN1bW1hcmlzZSh0b3RfbGVuID0gc3VtKG1pbGVzKSkgI3N1bSBsZW5ndGhzIG9mIHNlZ21lbnRzIHRoYXQgZmFsbCB1bmRlciB0aGUgc2FtZSBwcm9qZWN0DQoNCnJhdDEgPC0gbGVmdF9qb2luKHJhdGlvLCBwcm9qX3ZhbHMpICNqb2luIGluIHRoZSBwcm9qIHZhbHVlcyBzbyB0aGF0IGEgJC9taWxlIHJhdGlvIHZhcmlhYmxlIGNhbiBiZSBtYWRlDQpyYXQyIDwtIHJhdDEgJT4lIG11dGF0ZShyYXRpbyA9IEJ1ZGdfbWlsIC8gdG90X2xlbikNCg0KcmF0MyA8LSByYXQyICU+JSBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lIHNlbGVjdChyYXRpbywgTmFtZSkgI3NlbGVjdCB2YXJpYWJsZXMgZm9yIGpvaW5pbmcgYmFjayB0byBtYWluLCB1bmdyb3VwZWQsIHNlZ21lbnRzIGZpbGUNCg0KI2pvaW4gcmF0aW8gdG8gb3JpZ2luYWwgc2VnbWVudHMgZmlsZQ0KDQpzZWdfdmFsczEgPC0gbGVmdF9qb2luKHNlZ192YWxzLCByYXQzKQ0KYGBgDQoNCg0KKipKb2luaW5nIHNlZ21lbnRzIHRvIHRyYWN0cywgY2FsY3VsYXRpbmcgdG90YWwgYnVkZ2V0ZWQgc3BlbmRpbmcgcGVyIHRyYWN0KioNCg0KVGhpcyBzdGVwIHdpbGwgam9pbiBzZWdtZW50cyB0byB0cmFjdHMgdXNpbmcgYHN0X2ludGVyc2VjdGlvbmAgd2hpY2ggd2lsbCBjaG9wIHRoZSBsaW5lcyBpbnRvIHNtYWxsZXIgc2VjdGlvbnMgYmFzZWQgb24gd2hhdCB0cmFjdCB0aGV5IGludGVyc2VjdCB3aXRoLiBUaGlzIHdpbGwgZW5hYmxlIHRoZSBjYWxjdWxhdGlvbiBvZiB0aGUgc3BlbmRpbmcgcGVyIHNlY3Rpb24uIFRoZW4sIG9uY2UgdGhlIHNlY3Rpb25zIGFyZSBqb2luZWQgd2l0aCBwb2x5Z29ucywgYWdncmVnYXRpb24gdG8gdGhlIHBvbHlnb24gd2lsbCBnaXZlIHVzIHRoZSB0b3RhbCBzcGVuZGluZyBwZXIgcG9seWdvbiB1c2luZyB0aGUgYHN1bW1hcml6ZWAgc3RlcC4NCg0KDQpgYGB7cn0NCiMgaW50ZXJzZWN0aW9uDQppbnQgPSBzdF9pbnRlcnNlY3Rpb24oc2VnX3ZhbHMxLCBhdGxfcGx1cykNCiMgZmluZCBvdXQgYWJvdXQgdGhlIGxlbmd0aCBvZiBlYWNoIGxpbmUgc2VnbWVudA0KaW50JGxlbiA9IHN0X2xlbmd0aChpbnQpDQppbnQxIDwtIGludCAlPiUgbXV0YXRlKGttID0gbGVuIC8gMTAwMCkgJT4lIG11dGF0ZShtaWxlcyA9IGttKjAuNjIxMzcxKSAgI2FkZCBsZW5ndGhzIGluIGttIGFuZCBtaWxlcw0KaW50MiA8LSBpbnQxICU+JSBtdXRhdGUoc3BlbmRpbmcgPSBtaWxlcyAqIHJhdGlvKQ0KDQojIHNwYXRpYWwgb3ZlcmxheQ0Kam9pbl9uZXcgPSBzdF9qb2luKGF0bF9wbHVzLCBpbnQyKQ0KIyB1c2UgdGhlIElEIG9mIHRoZSBwb2x5Z29uIGZvciB0aGUgYWdncmVnYXRpb24NCm91dCA9IGdyb3VwX2J5KGpvaW5fbmV3LCBHRU9JRC54KSAlPiUNCiAgc3VtbWFyaXplKHRvdF9zcGVuZCA9IHN1bShzcGVuZGluZykpDQoNCm91dFtpcy5uYShvdXQpXSA8LSAwICNyZXBsYWNlIE5BcyB3aXRoIHplcm9zIHRvIGFzc2lzdCBmdXR1cmUgY2FsY3VsYXRpb25zDQoNCmBgYA0KDQpOZXh0IHRoZSBtYWpvciBwcm9qZWN0cyB3aWxsIGJlIHNlcGFyYXRlZCBvdXQgYnkgdHlwZSB0byBhc3Npc3Qgd2l0aCBmdXR1cmUgdmlzdWxhaXphdGlvbi4NCg0KYGBge3J9DQojI3NlcGFyYXRlIG91dCBwcm9qZWN0IHR5cGVzIGZvciBtYXBwaW5nDQoNCkxSVCA8LSBzZWdfdmFscyAlPiUgZmlsdGVyKFR5cGUgPT0gJ0xSVCcpDQoNCkZMQlMgPC0gc2VnX3ZhbHMgJT4lIGZpbHRlcihUeXBlID09ICdGTEJTJykNCg0KQlJUIDwtIHNlZ192YWxzICU+JSBmaWx0ZXIoVHlwZSA9PSAnQlJUJykNCg0KQVJUIDwtIHNlZ192YWxzICU+JSBmaWx0ZXIoVHlwZSA9PSAnQVJUJyB8IE5hbWUgPT0gJ0NsZXZlbGFuZCBBVkUnKQ0KDQpgYGANCg0KDQoqKkZpcnN0IFRoZW1hdGljIE1hcHMqKg0KDQpUaGUgZm9sbG93aW5nIGNvZGUgbWFrZXMgdGhlIGZpcnN0IHRoZW1hdGljIG1hcHMgb2YgdGhlIHNwYXRpYWwgdmFyaWF0aW9uIGluIGJ1ZGdldGVkIE1BUlRBIHNwZW5kaW5nLiBNYWpvciBQcm9qZWN0IGxpbmVzIGFyZSBzaG93biBmb3IgY29udGV4dC4gQSB2ZXJzaW9uIHdoZXJlIHRoZSB0cmFjdHMgd2l0aCB6ZXJvIHNwZW5kaW5nIHdlcmUgc2hvd24gYXMgZ3JleSB3YXMgZXhwZXJpbWVudGVkIHdpdGguDQoNCmBgYHtyfQ0KICB0bV9zaGFwZShvdXQpICsNCiAgdG1fZmlsbCgndG90X3NwZW5kJywgDQogICAgICAgICAgdGl0bGUgPSAnQnVkZ2V0ZWQgU3BlbmRpbmdcbihNaWxsaW9ucyBVU0QpJywNCiAgICAgICAgICBzdHlsZSA9ICdxdWFudGlsZScsDQogICAgICAgICAgcGFsZXR0ZSA9ICdCdVB1JykgKyANCiAgdG1fYm9yZGVycygpICsNCiN0bV9zaGFwZShvdXRfbm9uZSkgKyB0bV9maWxsKCdzcGVuZCcsIGNvbCA9ICdncmV5JywgYWxwaGEgPSAxKSArIHRtX2JvcmRlcnMoKSArDQogIHRtX3NoYXBlKExSVCkgKyB0bV9saW5lcyhsd2QgPSAyKSArDQogIHRtX3NoYXBlKEJSVCkgKyB0bV9saW5lcyhsd2QgPSAyLCBjb2wgPSAncmVkJykgKw0KICB0bV9zaGFwZShBUlQpICsgdG1fbGluZXMobHdkID0gMiwgY29sID0gJ2JsdWUnKSArIA0KICAgICN0bV9zaGFwZShGTEJTKSArIHRtX2xpbmVzKGx3ZCA9IDEsIGNvbCA9ICduYXZ5JykgKyANCiAgI3RtX2FkZF9sZWdlbmQodHlwZSA9ICdmaWxsJywgY29sID0gJ2dyZXknLCBsYWJlbHMgPSAnMC4wMCcpICsNCiAgdG1fYWRkX2xlZ2VuZCh0eXBlID0gJ2xpbmUnLCBjb2wgPSAnYmxhY2snLCBsd2QgPSAzLCBsYWJlbHMgPSAnTFJUJykgKw0KICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAnbGluZScsIGNvbCA9ICdibHVlJywgbHdkID0gMywgbGFiZWxzID0gJ0FSVCcpICsNCiAgdG1fYWRkX2xlZ2VuZCh0eXBlID0gJ2xpbmUnLCBjb2wgPSAncmVkJywgbHdkID0gMywgbGFiZWxzID0gJ0JSVCcpICsgDQogIA0KICB0bV9jcmVkaXRzKCdTb3VyY2U6IEF0bGFudGEgSm91cm5hbC1Db25zdGl0dXRpb24nLCBmb250ZmFjZSA9ICdib2xkJywgcG9zaXRpb24gPSBjKCdMRUZUJywnQk9UVE9NJykpICsNCiAgDQogIHRtX2xheW91dChsZWdlbmQudGl0bGUuZm9udGZhY2UgPSAnYm9sZCcpDQpgYGANCipOb3RlIHRoYXQgdGhpcyBhcHBlYXJzIHVuY3Jvd2RlZCBpbiB0aGUgbm9ybWFsIHZpZXdlciBhbmQgdXBvbiBleHBvcnQgYXMgYSBwaWN0dXJlLg0KDQoNCmBgYHtyfQ0KDQojbWFrZSBhIGRhdGFzZXQgd2l0aCBqdXN0IHplcm8gc3BlbmRpbmcgdHJhY3RzDQpvdXQkc3BlbmQgPC0gYXMubnVtZXJpYyhvdXQkdG90X3NwZW5kKQ0KDQpvdXRfbm9uZSA8LSBvdXQgJT4lIGZpbHRlcihzcGVuZCA9PSAwKQ0KDQojbWFwIHdpdGggZ3JleWVkIG91dCB6ZXJvcw0KDQogIHRtX3NoYXBlKG91dCkgKw0KICB0bV9maWxsKCd0b3Rfc3BlbmQnLCANCiAgICAgICAgICB0aXRsZSA9ICdCdWRnZXRlZCBTcGVuZGluZ1xuKE1pbGxpb25zIFVTRCknLA0KICAgICAgICAgIHN0eWxlID0gJ3F1YW50aWxlJywNCiAgICAgICAgICBwYWxldHRlID0gJ0J1UHUnKSArIA0KICB0bV9ib3JkZXJzKCkgKw0KdG1fc2hhcGUob3V0X25vbmUpICsgdG1fZmlsbCgnc3BlbmQnLCBjb2wgPSAnZ3JleScsIGFscGhhID0gMSkgKyB0bV9ib3JkZXJzKCkgKw0KICB0bV9zaGFwZShMUlQpICsgdG1fbGluZXMobHdkID0gMSkgKw0KICB0bV9zaGFwZShCUlQpICsgdG1fbGluZXMobHdkID0gMSwgY29sID0gJ3JlZCcpICsNCiAgdG1fc2hhcGUoQVJUKSArIHRtX2xpbmVzKGx3ZCA9IDEsIGNvbCA9ICdibHVlJykgKyANCiAgICB0bV9zaGFwZShGTEJTKSArIHRtX2xpbmVzKGx3ZCA9IDEsIGNvbCA9ICduYXZ5JykgKyANCiAgdG1fYWRkX2xlZ2VuZCh0eXBlID0gJ2ZpbGwnLCBjb2wgPSAnZ3JleScsIGxhYmVscyA9ICcwLjAwJykgKw0KICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAnbGluZScsIGNvbCA9ICdibGFjaycsIGx3ZCA9IDMsIGxhYmVscyA9ICdMUlQnKSArDQogIHRtX2FkZF9sZWdlbmQodHlwZSA9ICdsaW5lJywgY29sID0gJ2JsdWUnLCBsd2QgPSAzLCBsYWJlbHMgPSAnQVJUJykgKw0KICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAnbGluZScsIGNvbCA9ICdyZWQnLCBsd2QgPSAzLCBsYWJlbHMgPSAnQlJUJykgKyANCiAgDQogIHRtX2NyZWRpdHMoJ1NvdXJjZTogQXRsYW50YSBKb3VybmFsLUNvbnN0aXR1dGlvbicsIGZvbnRmYWNlID0gJ2JvbGQnLCBwb3NpdGlvbiA9IGMoJ0xFRlQnLCdCT1RUT00nKSkgKw0KICANCiAgdG1fbGF5b3V0KGxlZ2VuZC50aXRsZS5mb250ZmFjZSA9ICdib2xkJykNCmBgYA0KDQpBIGNsb3NlIGV4YW1pbmF0aW9uIG9mIHRoaXMgc2Vjb25kIG1hcCByZXZlYWxzIHRoYXQgdGhlcmUgYXJlIGEgZmV3IGNlbnRyYWwgdHJhY3RzIHdoaWNoIHByb2JhYmx5IHNob3VsZG4ndCBiZSBpZGVudGlmaWVkIGFzIHplcm8gc3BlbmRpbmcuIEhvd2V2ZXIsIGR1ZSB0byBzbWFsbCBpbmFjY3VyYWNpZXMgaW4gdGhlIGdlb3JlZmVyZW5jaW5nIGFuZCBkaWdpdGl6YXRpb24gcHJvY2Vzcywgc29tZSBzZWdtZW50cyBhbmQgdHJhY3RzIGRvbid0IG92ZXJsYXAgd2hlbiB0aGV5IHByb2JhYmx5IHNob3VsZC4gSSBzYXkgKnByb2JhYmx5KiBiZWNhdXNlIGluIG1vc3QgY2FzZXMsIHRoZSB0cmFjdCBib3VuZGFyaWVzIGFwcGVhciB0byBmb2xsb3cgbWFqb3IgdHJhbnNwb3J0YXRpb24gcm91dGVzLg0KDQpVbmZvcnR1bmF0ZWx5LCBhdCB0aGlzIHN0YWdlIEkndmUgYWxyZWFkeSBzcGVudCBhIExPVCBvZiB0aW1lIGdlb3Byb2Nlc3NpbmcgYW5kIEkgbmVlZCB0byBnZXQgdG8gdGhlIGFuYWx5c2lzLg0KVG8gaW1wcm92ZSB0aGlzIGluIHRoZSBmdXR1cmUsIEkgd291bGQgbmVlZCB0byBjb21wYXJlIHRoZSB0cmFjdCBib3VuZGFyaWVzIGluIHF1ZXN0aW9uIGFuZCBzZWUgaWYgdGhleSBpbmRlZWQgZmFsbCBhbG9uZyBtYWpvciByb2Fkcy8gdHJhbnNpdCByb3V0ZXMgdGhhdCBhcmUgaW4gdGhlIE1BUlRBIHByb2plY3QgbGlzdC4gRnJvbSB0aGVyZSwgSSdkIG5lZWQgdG8gcmVkaWdpdGl6ZSBzZWdtZW50cyB0aGF0IG1hdGNoIHVwIGJldHRlci4NCg0KT24gdG8gYW5hbHlzaXMuDQoNCg0KKipEb3dubG9hZGluZywgY2xlYW5pbmcsIGFuZCBqb2luaW5nIG90aGVyIGF0dHJpYnV0ZSBkYXRhKioNCg0KSnVzdCBraWRkaW5nIGFib3V0IGJlaW5nIHJlYWR5IHRvIGRvIGFuYWx5c2lzLiBOb3cgSSBuZWVkIHRvIGRvd25sb2FkIHNvbWUgY292YXJpYXRlIGRhdGEgKHBvcHVsYXRpb24gYW5kIG1lZGlhbiBpbmNvbWUpIGZyb20gdGhlIEFDUyBmb3IgbXkgdHJhY3RzLg0KVGhpcyB0b29rIGEgZmFpciBhbW91bnQgb2YgcHJvY2Vzc2luZyBhcyBBdGxhbnRhIHNwcmVhZHMgYWNyb3NzIGJvdGggRnVsdG9uIGFuZCBEZWthbGIgY291bnRpZXMuDQoNCmBgYHtyfQ0KIyMjIyBhdHRyaWJ1dGVzIHRvIHRyYWN0cyBqb2luDQogIA0KICBsaWJyYXJ5KHRpZHljZW5zdXMpDQogIA0KICBhbGxfdmFycyA8LSBsb2FkX3ZhcmlhYmxlcyh5ZWFyID0gMjAxNywgZGF0YXNldCA9ICdhY3M1JywgY2FjaGUgPSBUKQ0KICANCiAgIyBGcm9tIG15IHNlYXJjaCwgSSBmb3VuZCB0aGF0IEkgd2FudCB0aGUgZm9sbG93aW5nIHZhcmlhYmxlczogJ0IyNTAyNl8wMDEnLCdCMjEwMDRfMDAxJw0KICANCiAgIyMgdGhpcyBwYXJ0IGdyYWJzIHRoZSB2YXJpYWJsZXMgZm9yIHRoZSBhdGwgdHJhY3RzIGluIGZ1bHRvbiBjb3VudHkNCiAgDQogIGZ1bHRfcG9wIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gJ3RyYWN0JywNCiAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGVzID0gJ0IyNTAyNl8wMDEnLA0KICAgICAgICAgICAgICAgICAgICAgICBjb3VudHkgPSAnRnVsdG9uJywNCiAgICAgICAgICAgICAgICAgICAgICAgc3RhdGUgPSAnR2VvcmdpYScsDQogICAgICAgICAgICAgICAgICAgICAgeWVhciA9IDIwMTcsDQogICAgICAgICAgICAgICAgICAgICAgIHN1cnZleSA9ICdhY3M1JykgJT4lIHJlbmFtZShwb3B1bGF0aW9uXzIwMTc9IGVzdGltYXRlKQ0KICANCiAgZnVsdF9pbmMgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAndHJhY3QnLA0KICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9ICdCMjEwMDRfMDAxJywNCiAgICAgICAgICAgICAgICAgICAgICBjb3VudHkgPSAnRnVsdG9uJywNCiAgICAgICAgICAgICAgICAgICAgICBzdGF0ZSA9ICdHZW9yZ2lhJywNCiAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gMjAxNywNCiAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZXkgPSAnYWNzNScpICU+JSByZW5hbWUobWVkX2luY29tZV8xNz0gZXN0aW1hdGUpICU+JSBzZWxlY3QobWVkX2luY29tZV8xNywgR0VPSUQpDQogIA0KICANCiAgYXRsX3BvcCA8LWxlZnRfam9pbihhdGxfcGx1cywgZnVsdF9wb3ApDQogIA0KICBhdGxfcG9wX2luYyA8LSBsZWZ0X2pvaW4oYXRsX3BvcCwgZnVsdF9pbmMpDQoNCiAgYXRsX2F0dHNfZnVsdCA8LSBhdGxfcG9wX2luYyAlPiUgc2VsZWN0KEdFT0lELCBwb3B1bGF0aW9uXzIwMTcsIFBvcHVsYXRpb24yMDEwLCBtZWRfaW5jb21lXzE3KQ0KICANCiAgDQogIA0KICAjIyMgVGhpcyBwYXJ0IGdyYWJzIHRoZSB2YXJpYWJsZXMgZm9yIHRoZSBhdGwgdHJhY3RzIGluIGRla2FsYiBjb3VudHkNCiAgDQogIGRla19wb3AgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAndHJhY3QnLA0KICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9ICdCMjUwMjZfMDAxJywNCiAgICAgICAgICAgICAgICAgICAgICBjb3VudHkgPSAnRGVrYWxiJywNCiAgICAgICAgICAgICAgICAgICAgICBzdGF0ZSA9ICdHZW9yZ2lhJywNCiAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gMjAxNywNCiAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZXkgPSAnYWNzNScpICU+JSByZW5hbWUocG9wdWxhdGlvbl8yMDE3PSBlc3RpbWF0ZSkNCiAgDQogIGRla19pbmMgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAndHJhY3QnLA0KICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlcyA9ICdCMjEwMDRfMDAxJywNCiAgICAgICAgICAgICAgICAgICAgICBjb3VudHkgPSAnRGVrYWxiJywNCiAgICAgICAgICAgICAgICAgICAgICBzdGF0ZSA9ICdHZW9yZ2lhJywNCiAgICAgICAgICAgICAgICAgICAgICB5ZWFyID0gMjAxNywNCiAgICAgICAgICAgICAgICAgICAgICBzdXJ2ZXkgPSAnYWNzNScpICU+JSByZW5hbWUobWVkX2luY29tZV8xNz0gZXN0aW1hdGUpICU+JSBzZWxlY3QobWVkX2luY29tZV8xNywgR0VPSUQpDQogIA0KICANCiAgZGVrX3BvcCA8LWxlZnRfam9pbihhdGxfcGx1cywgZGVrX3BvcCkNCiAgDQogIGRla19wb3BfaW5jIDwtIGxlZnRfam9pbihkZWtfcG9wLCBkZWtfaW5jKQ0KICANCiAgYXRsX2F0dHNfZGVrIDwtIGRla19wb3BfaW5jICU+JSBzZWxlY3QoR0VPSUQsIHBvcHVsYXRpb25fMjAxNywgUG9wdWxhdGlvbjIwMTAsIG1lZF9pbmNvbWVfMTcpICU+JSBmaWx0ZXIoaXMubmEocG9wdWxhdGlvbl8yMDE3KSA9PSBGKSAlPiUgc3RfZHJvcF9nZW9tZXRyeSgpDQogIA0KIA0KICANCiAgIyMgbm93IGpvaW4gZnVsdG9uIGFuZCBkZWthbGIgcG9seXMgaW50byBvbmUgYXRsYW50YSBhdHRyaWJ1dGVzIGRhdGFmcmFtZQ0KICANCiAgYXRsX2F0dHMgPC0gbGVmdF9qb2luKGF0bF9hdHRzX2Z1bHQsIGF0bF9hdHRzX2RlaywgYnk9ICdHRU9JRCcpDQogIGF0bF9hdHRzMSA8LSBhdGxfYXR0cyAlPiUgDQogICAgbXV0YXRlKHBvcF8xNyA9IGlmZWxzZShpcy5uYShwb3B1bGF0aW9uXzIwMTcueCkscG9wdWxhdGlvbl8yMDE3LnksIHBvcHVsYXRpb25fMjAxNy54KSkgJT4lDQogICAgbXV0YXRlKGluY18xNyA9IGlmZWxzZShpcy5uYShtZWRfaW5jb21lXzE3LngpLG1lZF9pbmNvbWVfMTcueSwgbWVkX2luY29tZV8xNy54KSkgJT4lIHNlbGVjdChHRU9JRCwgcG9wXzE3LCBpbmNfMTcpICU+JSBzdF9kcm9wX2dlb21ldHJ5KCkNCiAgICANCiAgI2Fib3ZlIG11dGF0ZSBjb2RlIGlzIG5lY2Vzc2FyeSB0byBjb25zb2xpZGF0ZSB0aGUgZnVsdG9uIC8gZGVrYWxiIGF0dHJpYnV0ZXMgaW50byBzaW5nbGUgY29sdW1ucw0KICANCiAgDQogICMgam9pbiB3aXRoIHNwZW5kaW5nIHBvbHlnb25zDQogIA0KICANCiAgYXRsX3NwZW5kX2F0dHMgPC0gbGVmdF9qb2luKG91dCwgYXRsX2F0dHMxLCBieSA9IGMoJ0dFT0lELngnID0gJ0dFT0lEJykpDQogIA0KICBhdGxfc3BlbmRfYXR0cyA8LSBhdGxfc3BlbmRfYXR0cyAlPiUgZmlsdGVyKGlzLm5hKHBvcF8xNykgPT0gRikgI3JlbW92ZSBzb21lIHdlaXJkIHRyYWN0cyB0aGF0IGRvbid0IG1hcCBhbmQgaGF2ZSBubyBkYXRhDQoNCiAgDQpgYGANCg0KDQojQW5hbHlzaXMNCg0KIyNDbHVzdGVycw0KTG9va2luZyBhdCB0aGUgbWFwIG9mIGJ1ZGdldGVkIHNwZW5kaW5nIGJ5IGNlbnN1cyB0cmFjdCBpdCBzZWVtcyBvYnZpb3VzIHRoYXQgdGhlIGNvbnN0YW50IHJpc2sgYXNzdW1wdGlvbiBpcyB2aW9sYXRlZC4gSG93ZXZlciwgd2UnZCBsaWtlIHRvIGtub3cgaW4gd2hhdCBhcmVhcyB0aGUgc3BlbmRpbmcgaXMgaGlnaGVyIHRoYW4gZXhwZWN0ZWQgYmFzZWQgb24gdGhlIHBvcHVsYXRpb24gb2YgYSB0cmFjdC4gVGh1cywgd2Ugd2lsbCBwcm9kdWNlIGEgbWFwIG9mIHBvaXNzc29uIHByb2JhYmlsaXRpZXMgdG8gdGVzdCBmb3IgYW5kIGxvY2F0ZSB0aGUgbG9jYWwgZXh0cmVtZXMuDQoNCmBgYHtyfQ0KbGlicmFyeShzcGRlcCkgI3BhY2thZ2UgZm9yIHBlcmZvcm1pbmcgY2x1c3RlciBhbmFseXNpcw0KDQphdGxfc3BlbmRfYXR0cyR0b3Rfc3BlbmQgPC0gYXMubnVtZXJpYyhhdGxfc3BlbmRfYXR0cyR0b3Rfc3BlbmQpICNjb25tdmVydCBmcm9tIHVuaXRzIGNsYXNzIHRvIG51bWVyaWMNCg0KYXRsX3NwZW5kX2F0dHMgPC0gYXRsX3NwZW5kX2F0dHMgJT4lIG11dGF0ZShzcGVuZF9yYXRpbyA9IGlmZWxzZSggcG9wXzE3ID4gMCwodG90X3NwZW5kICogMTAwMDAwMCAvIHBvcF8xNyksIE5BKSkgJT4lZmlsdGVyKGlzLm5hKHNwZW5kX3JhdGlvKSA9PSBGKQ0KDQpzcGVuZF9wcm9icyA8LSBwcm9ibWFwKG4gPWF0bF9zcGVuZF9hdHRzJHRvdF9zcGVuZCwNCiAgICAgICAgICAgICAgICAgICAgICAgeCA9IGF0bF9zcGVuZF9hdHRzJHBvcF8xNywNCiAgICAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gYXRsX3NwZW5kX2F0dHMkR0VPSUQueCkNCg0KYXRsX3NwZW5kX2F0dHMkcG1hcCA8LSBzcGVuZF9wcm9icyRwbWFwDQojZ2VuZXJhdGUgcG9pc3NvbiBwcm9icw0KDQpgYGANCg0KDQoNCg0KDQpgYGB7cn0NCiMgVGhpcyBqdXN0IHN1bW1hcml6ZXMgKG1lcmdlcyBwb2x5Z29ucykgb2YgYWxsIGNvdW50aWVzIHdpdGggc2lnbmlmaWNhbnQgSElHSCByaXNrDQoNCnNwZW5kX2hpIDwtIGF0bF9zcGVuZF9hdHRzICU+JSAjZm9ybSBjb250aWd1b3VzIGFyZWFzIG9mIHNpZyBoaWdoIG9yIGxvdyBzcGVuZGluZyBpbnRvIHBvbHlnb25zDQogIGZpbHRlcihwbWFwID4gMC45NSkgJT4lIHN1bW1hcmlzZSgpDQoNCnNwZW5kX2xvIDwtIGF0bF9zcGVuZF9hdHRzICU+JQ0KICBmaWx0ZXIocG1hcCA8IDAuMDUpICU+JSBzdW1tYXJpc2UoKQ0KDQoNCiMgbWFrZSBtYXAgb2Ygc2lnIGhpZ2ggb3IgbG93IHNwZW5kaW5nIGFyZWFzDQptMSA8LXRtX3NoYXBlKGF0bF9zcGVuZF9hdHRzKSArIA0KICB0bV9maWxsKCdzcGVuZF9yYXRpbycsDQogICAgICAgICAgcGFsZXR0ZSA9ICdCdVB1JywNCiAgICAgICAgICAgc3R5bGUgPSAncXVhbnRpbGUnLA0KICAgICAgICAgIHRpdGxlID0gJ1NwZW5kaW5nIHBlclxuUGVyc29uIChVU0QpJykgKw0KICB0bV9ib3JkZXJzKCkgKyANCiAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoJ1JJR0hUJywnVE9QJyksDQogICAgICAgICAgICBtYWluLnRpdGxlID0gJ0NsdXN0ZXJzIG9mIE1BUlRBXG5FeHBhbnNpb24gU3BlbmRpbmcgYnkgUG9wdWxhdGlvbicpICsNCiAgDQojIE5vdyBwbG90IG9ubHkgYm9yZGVycyBvZiBoaWdoIG9yIGxvdyByYXRlIGNvdW50aWVzDQoNCg0KICB0bV9zaGFwZShzcGVuZF9sbykgKyANCiAgdG1fYm9yZGVycyhjb2wgPSAnYmx1ZScsIGx3ZCA9IDIpICsgDQogIHRtX3NoYXBlKHNwZW5kX2hpKSArIA0KICB0bV9ib3JkZXJzKGNvbCA9ICdyZWQnLCBsd2QgPSAyKSArDQoNCiMgZ2VuZXJhbCBsYXlvdXQgc3R1ZmYuLi4gIA0KdG1fbGF5b3V0KGxlZ2VuZC5vdXRzaWRlID0gRiwgbGVnZW5kLnRleHQuc2l6ZSA9IDAuNTUsIA0KICAgICAgICAgIGxlZ2VuZC50aXRsZS5zaXplID0gMC42NSwgbGVnZW5kLnRpdGxlLmZvbnRmYWNlID0gJ2JvbGQnLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoJ0xFRlQnLCAnVE9QJyksIGlubmVyLm1hcmdpbnMgPSBjKDAuMDIsIC4wMywgLjAyLCAwKSwNCiAgICAgICAgIG1haW4udGl0bGUucG9zaXRpb24gPSAnY2VudGVyJywgbWFpbi50aXRsZS5zaXplID0gMSwgbWFpbi50aXRsZS5mb250ZmFjZSA9ICdib2xkJykgKyANCiAgIHRtX2FkZF9sZWdlbmQodHlwZSA9ICdsaW5lJywNCiAgICAgICAgICAgICAgY29sID0gJ3JlZCcsDQogICAgICAgICAgICAgIGx3ZCA9IDIsDQogICAgICAgICAgICAgIGxhYmVscyA9ICdTaWduaWZpY2FudGx5IGhpZ2gnKSArDQogICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAnbGluZScsDQogICAgICAgICAgICAgIGNvbCA9ICdibHVlJywNCiAgICAgICAgICAgICAgbHdkID0gMiwNCiAgICAgICAgICAgICAgbGFiZWxzID0gJ1NpZ25pZmljYW50bHkgbG93JykNCmBgYCANCg0KTG9va2luZyBhdCB0aGUgYWJvdmUgbWFwLCBhbG1vc3QgYWxsIG9mIHRoZSBBdGxhbnRhIGNlbnN1cyB0cmFjdHMgZmFsbCBpbnRvIHRoZSBjYXRlZ29yeSBvZiBzaWduaWZpY2FudCBoaWdoIG9yIGxvdyBzcGVuZGluZy4NCk5vdywgdGhpcyB3YXMgZG9uZSB3aXRoIHBvcHVsYXRpb24gY291bnQgYXMgdGhlIGRlbm9taW5hdG9yLCBidXQgd2hhdCBpZiB1c2luZyBwb3B1bGF0aW9uIGRlbnNpdHkgd291bGQgZ2l2ZSB1cyBhIG1vcmUgY2xlYXIgcGljdHVyZT8NCg0KVGhlIGZvbGxvd2luZyBjb2RlIGJyaW5ncyBpbiB0aGUgbGFuZCBhcmVhIChrbTIpIHZhcmlhYmxlIGZvciBlYWNoIHRyYWN0IGFuZCBjYWxjdWxhdGVzIHBvcHVsYXRpb24gZGVuc2l0eSBpbiBwZXJzb25zIHBlciBrbTIuDQoNCg0KYGBge3J9DQpkZWtfdHJhY3RzJEFMQU5EIDwtIGFzLm51bWVyaWMoZGVrX3RyYWN0cyRBTEFORCkgI2NvbnZlcnQgdG8gbnVtZXJpYyBmb3IgY2FsY3VsYXRpb25zDQoNCmRla190cmFjdHNfYSA8LSBkZWtfdHJhY3RzICU+JSANCiAgc2VsZWN0KEdFT0lELCBBTEFORCkgJT4lIG11dGF0ZShrbTIgPSBBTEFORC8xMDAwMDAwKSAlPiUgc3RfZHJvcF9nZW9tZXRyeSgpIA0KDQojZnVsdG9uIHRyYWN0cw0KDQpmdWxfdHJhY3RzIDwtIGRla190cmFjdHMgPC0gdHJhY3RzKCdHQScsIGNvdW50eSA9ICdGdWx0b24nKSAlPiUgc3RfYXNfc2YoKSAlPiUgc3RfZHJvcF9nZW9tZXRyeSgpDQoNCmZ1bF90cmFjdHMkQUxBTkQgPC0gYXMubnVtZXJpYyhmdWxfdHJhY3RzJEFMQU5EKQ0KDQpmdWxfdHJhY3RzX2EgPC0gZnVsX3RyYWN0cyAlPiUgDQogIHNlbGVjdChHRU9JRCwgQUxBTkQpICU+JSBtdXRhdGUoa20yID0gQUxBTkQvMTAwMDAwMCkNCg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQojam9pbnMgYW5kIGNsZWFuaW5nDQoNCmZ1bHRfYSA8LWxlZnRfam9pbihhdGxfcGx1cywgZnVsX3RyYWN0c19hKQ0KDQpkZWtfYSA8LWxlZnRfam9pbihhdGxfcGx1cywgZGVrX3RyYWN0c19hKQ0KDQphbGxfYSA8LSByYmluZChmdWx0X2EsIGRla19hKSAlPiUgZmlsdGVyKGlzLm5hKEFMQU5EKSA9PSBGKSAlPiUgc2VsZWN0KEdFT0lELCBrbTIpICU+JSBzdF9kcm9wX2dlb21ldHJ5KCkgI2Ryb3AgZ2VvbWV0cnkgdG8gbWFrZSBhIHRhYmxlIHRoYXQgY2FuIGJlIGpvaW5lZCB0byBtYWluIGF0dHJpYnV0ZXMgZmlsZQ0KDQphcmVhX2F0dHMgPC0gbGVmdF9qb2luKGF0bF9zcGVuZF9hdHRzLCBhbGxfYSwgYnk9YygnR0VPSUQueCc9J0dFT0lEJykpIA0KYXJlYV9hdHRzIDwtYXJlYV9hdHRzICU+JSBtdXRhdGUocG9wX2RlbnMgPSBwb3BfMTcgLyBrbTIpICNjcmVhdGUgcG9wIGRlbnNpdHkgdmFyaWFibGUgaW4gcGVyc29ucyBwZXIga20yDQoNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCmBgYA0KDQoNCk5vdyB3ZSdsbCBydW4gdGhlIHRlc3Qgb2YgbG9jYWwgZXh0cmVtZXMgYWdhaW4gYnV0IHdpdGggcG9wdWxhdGlvbiBkZW5zaXR5IGFzIHRoZSBkZW5vbWluYXRvci4gRmluYWxseSwgd2UnbGwgbWFwIHRoZSBzaWduaWZpY2FudCByZXN1bHRzIHRvIHNlZSBob3cgZGlmZmVyZW50IGl0IGlzIHRoYW4gdXNpbmcgcG9wIGNvdW50cy4NCg0KQW5hbHlzaXM6DQpgYGB7cn0NCnNwZW5kX3Byb2JzX2RlbnMgPC0gcHJvYm1hcChuID1hcmVhX2F0dHMkdG90X3NwZW5kLA0KICAgICAgICAgICAgICAgICAgICAgICB4ID0gYXJlYV9hdHRzJHBvcF9kZW5zLA0KICAgICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSBhcmVhX2F0dHMkR0VPSUQueCkNCg0KYXJlYV9hdHRzJHBtYXAgPC0gc3BlbmRfcHJvYnNfZGVucyRwbWFwDQojZ2VuZXJhdGUgcG9pc3NvbiBwcm9icw0KDQphcmVhX2F0dHMgPC0gYXJlYV9hdHRzICU+JSBtdXRhdGUoc3BlbmRfcmF0aW9fZCA9IHRvdF9zcGVuZCAqMTAwMDAwMC8gcG9wX2RlbnMpDQpgYGANCg0KTWFwOg0KDQpgYGB7cn0NCiMgVGhpcyBqdXN0IHN1bW1hcml6ZXMgKG1lcmdlcyBwb2x5Z29ucykgb2YgYWxsIGNvdW50aWVzIHdpdGggc2lnbmlmaWNhbnQgSElHSCByaXNrDQoNCnNwZW5kX2hpX2QgPC0gYXJlYV9hdHRzICU+JSAjZm9ybSBjb250aWd1b3VzIGFyZWFzIG9mIHNpZyBoaWdoIG9yIGxvdyBzcGVuZGluZyBpbnRvIHBvbHlnb25zDQogIGZpbHRlcihwbWFwID4gMC45NSkgJT4lIHN1bW1hcmlzZSgpDQoNCnNwZW5kX2xvX2QgPC0gYXJlYV9hdHRzICU+JQ0KICBmaWx0ZXIocG1hcCA8IDAuMDUpICU+JSBzdW1tYXJpc2UoKQ0KDQoNCiMgbWFrZSBtYXAgb2Ygc2lnIGhpZ2ggb3IgbG93IHNwZW5kaW5nIGFyZWFzDQogbTIgPC10bV9zaGFwZShhcmVhX2F0dHMpICsgDQogIHRtX2ZpbGwoJ3NwZW5kX3JhdGlvX2QnLA0KICAgICAgICAgIHBhbGV0dGUgPSAnQnVQdScsDQogICAgICAgICAgIHN0eWxlID0gJ3F1YW50aWxlJywNCiAgICAgICAgICB0aXRsZSA9ICdTcGVuZGluZyBwZXJcblBlcnNvbiBQZXIgS20yIChVU0QpJykgKw0KICB0bV9ib3JkZXJzKCkgKyANCiAgdG1fbGF5b3V0KGxlZ2VuZC5wb3NpdGlvbiA9IGMoJ1JJR0hUJywnVE9QJyksDQogICAgICAgICAgICBtYWluLnRpdGxlID0gJ0NsdXN0ZXJzIG9mIE1BUlRBXG5FeHBhbnNpb24gU3BlbmRpbmcgYnkgUG9wLiBEZW5zaXR5JykgKw0KICANCiMgTm93IHBsb3Qgb25seSBib3JkZXJzIG9mICdoaWdoLXJhdGUnIGNvdW50aWVzDQoNCiAgdG1fc2hhcGUoc3BlbmRfbG9fZCkgKyANCiAgdG1fYm9yZGVycyhjb2wgPSAnYmx1ZScsIGx3ZCA9IDIpICsgDQogIHRtX3NoYXBlKHNwZW5kX2hpX2QpICsgDQogIHRtX2JvcmRlcnMoY29sID0gJ3JlZCcsIGx3ZCA9IDIpICsNCg0KI2dlbmVyYWwgbGF5b3V0IA0KdG1fbGF5b3V0KGxlZ2VuZC5vdXRzaWRlID0gRiwgbGVnZW5kLnRleHQuc2l6ZSA9IDAuNTUsIA0KICAgICAgICAgIGxlZ2VuZC50aXRsZS5zaXplID0gMC42NSwgbGVnZW5kLnRpdGxlLmZvbnRmYWNlID0gJ2JvbGQnLA0KICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoJ0xFRlQnLCAnVE9QJyksIGlubmVyLm1hcmdpbnMgPSBjKDAuMDIsIC4wMywgLjAyLCAwKSwNCiAgICAgICAgIG1haW4udGl0bGUucG9zaXRpb24gPSAnY2VudGVyJywgbWFpbi50aXRsZS5zaXplID0gMSwgbWFpbi50aXRsZS5mb250ZmFjZSA9ICdib2xkJykgKyANCiAgIHRtX2FkZF9sZWdlbmQodHlwZSA9ICdsaW5lJywNCiAgICAgICAgICAgICAgY29sID0gJ3JlZCcsDQogICAgICAgICAgICAgIGx3ZCA9IDIsDQogICAgICAgICAgICAgIGxhYmVscyA9ICdTaWduaWZpY2FudGx5IGhpZ2gnKSArDQogICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAnbGluZScsDQogICAgICAgICAgICAgIGNvbCA9ICdibHVlJywNCiAgICAgICAgICAgICAgbHdkID0gMiwNCiAgICAgICAgICAgICAgbGFiZWxzID0gJ1NpZ25pZmljYW50bHkgbG93JykNCmBgYA0KDQoNCmBgYHtyfQ0KdG1hcF9hcnJhbmdlKG0xLCBtMikNCmBgYA0KDQpUaGUgdXNlIG9mIHBvcHVsYXRpb24gZGVuc2l0eSBkb2VzIGNoYW5nZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHNpZ25pZmljYW50bHkgZXh0cmVtZSB0cmFjdHMgc3VjaCB0aGF0IHRoZXJlIGFyZSBmZXdlciBzaWduaWZpY2FudCB0cmFjdHMgb3ZlcmFsbCBhbmQgc29tZSB0cmFjdHMgc3dpdGNoIGZyb20gc2lnIGhpIHRvIGxvdyBhbmQgdmljZSB2ZXJzYS4gRWl0aGVyIHdheSB5b3Ugc2xpY2UgaXQsIHRoZXJlIGFyZSBjbGVhciBzaWduaWZpY2FudCBleHRyZW1lIGNsdXN0ZXJzIG9mIHNwZW5kaW5nLiBIb3dldmVyLCB1c2luZyBwb3AgZGVuc2l0eSBhcyB0aGUgZGVub21pbmF0b3Igc2VlbXMgdG8gcmVtb3ZlIHNvbWUgb2YgdGhlIG5vaXNlIGNhdXNlZCBieSBoYXZpbmcgc2ltaWxhciBwb3B1bGF0aW9ucyBpbiBkcmFzdGljYWxseSBkaWZmZXJlbnRseSBzaXplZCB0cmFjdHMuDQoNCg0KIyNSZWdyZXNzaW9uDQoNCk5vdyB0aGF0IEkndmUgZXN0YWJsaXNoZWQgc2lnbmlmaWNhbnQgY2x1c3RlcmluZyBvZiBoaWdoIGFuZCBsb3cgc3BlbmRpbmcgZXhpc3RzLEknZCBsaWtlIHRvIHNlZSBpZiB0aGlzIHBoZW5vbWVub24gY2FuIGJlIGV4cGxhaW5lZCBieSBvdGhlciB1bmRlcmx5aW5nIGZhY3RvcnMuIEluIHBhcnRpY3VsYXIsIEkgYW0gaW50ZXJlc3RlZCB0byBleGFtaW5lIHRoZSBjb3JyZWxhdGlvbiBvZiBtZWRpYW4gdHJhY3QgaW5jb21lIHdpdGggYnVkZ2V0ZWQgc3BlbmRpbmcuDQoNCkZpcnN0IEkgd2lsbCBmaXQgbXkgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxzLg0KDQpgYGB7cn0NCiNlbXB0eSBtb2RlbA0KDQojYmVmb3JlIG15IHRvdF9zcGVuZCB2YXJpYWJsZSB3YXMgaW4gbWlsbGlvbnMgb2YgZG9sbGFycywgSSB3aWxsIGNvbnZlcnQgaXQgYmFjayB0byBkb2xsYXJzIGZvciBydW5uaW5nIG15IG1vZGVscw0KDQphcmVhX2F0dHMkdG90X3NwZW5kIDwtIGFyZWFfYXR0cyR0b3Rfc3BlbmQgLyAxMDAwMDAwDQoNCm0wIDwtIGxtKHRvdF9zcGVuZCB+IDEsIGRhdGEgPSBhcmVhX2F0dHMpDQoNCiNjb25kaXRpb25hbCBtb2RlbA0KDQptMSA8LSBsbSh0b3Rfc3BlbmQgfiBwb3BfZGVucyArIGluY18xNywgZGF0YSA9IGFyZWFfYXR0cykNCg0Kc3VtbWFyeShtMSkNCmBgYA0KDQpUaGUgcmVzdWx0cyBvZiBteSBjb25kaXRpb25hbCBtb2RlbCBzaG93IHRoYXQsIGhvbGRpbmcgcG9wdWxhdGlvbiBkZW5zaXR5IGNvbnN0YW50LCAyMDE3IG1lZGlhbiB0cmFjdCBpbmNvbWUgd2FzIGEgc2lnbmlmaWNhbnQgbmVnYXRpdmUgcHJlZGljdG9yIG9mIGJ1ZGdldGVkIE1BUlRBIHNwZW5kaW5nLiBCYXNlZCBvbiB0aGUgbW9kZWwsIGVhY2ggZG9sbGFyIGluY3JlYXNlIGluIG1lZGlhbiBpbmNvbWUgaXMgYXNzb2NpYXRlZCB3aXRoIGEgNzk2LjggZG9sbGFyIGRlY3JlYXNlIGluIHBsYW5uZWQgc3BlbmRpbmcuIEhvd2V2ZXIsIGl0IGlzIGltcG9ydGFudCB0byBub3RlIHRoYXQgc2luY2UgSSBoYXZlIHplcm8gZG9sbGFyIHZhbHVlcyBmb3IgMjYgb2YgMTMwIHRyYWN0cywgdGhlIHJlc2lkdWFscyBhbmQgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IgZm9yIHRoaXMgbW9kZWwgYXJlIGh1Z2UuDQoNCmBgYHtyfQ0Kc3VtbWFyeShhcmVhX2F0dHMkc3BlbmQpDQpgYGANCg0KDQpOb3cgSSdsbCB0ZXN0IHRoZSByZXNpZHVhbHMgZnJvbSBteSBtb2RlbCB0byBzZWUgd2hldGhlciBvdXIgZmFjdG9ycyBleHBsYWluIGFueSBvZiB0aGUgdmFyaWF0aW9uIGluIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGJldHdlZW4gdHJhY3RzLg0KDQpUaGlzIHdpbGwgaW52b2x2ZSBmaXJzdCBjaG9vc2luZyBhIG5laWdoYm9ycyBkZWZpbml0b24gYW5kIHRoZW4gcnVubmluZyB0aGUgYGxtLm1vcmFudGVzdGAgZnVuY3Rpb24uIEkgaGF2ZSBkZWNpZGVkIHRvIHVzZSBrLW5lYXJlc3QgbmVpZ2hib3JzIG9mIDMgZHVlIHRvIHRoZSBmYWN0IHRoYXQgZW1vcnkgaXMgbm90IHRvdWNoaW5nIGFueSBvdGhlciBhdGwgdHJhY3RzIGFuZCBzZXZlcmFsIGJvcmRlciB0cmFjdHMgaGF2ZSBvbmx5IG9uZSBjb250aWd1b3VzIG5laWdoYm9yLg0KDQpgYGB7cn0NCiMjQ3JlYXRpbmcgbmIgb2JqZWN0DQoNCiNzaW5jZSB3ZSdyZSB1c2luZyBwcm94aW1pdHkgYmFzZWQgbmIgb2JqZWN0LCB3ZSBuZWVkIHRoZSB0cmFjdCBjZW50cm9pZCBwb2ludHMNCg0KYXJlYV9hdHRzX3NwIDwtIGFzKGFyZWFfYXR0cywgJ1NwYXRpYWwnKQ0KYXJlYV9wb2ludHMgIDwtIGNvb3JkaW5hdGVzKGFyZWFfYXR0c19zcCkNCg0KYXJlYV9rMyA8LSBrbmVhcm5laWdoKGFyZWFfcG9pbnRzLCBrID0zKSAjY3JlYXRlcyBzZXQgb2YgMyBuZWFyZXN0IG5laWdoYm9ycw0KDQphcmVhX2szX25iIDwtIGtubjJuYihhcmVhX2szLCByb3cubmFtZXMgPSBhcmVhX2F0dHNfc3AkR0VPSUQueCwgc3ltID0gVCkgI2NyZWF0ZXMgYSBzeW1tZXRyaWNhbCBuYiBvYmplY3QNCg0KYXJlYV9saXN0dyA8LSBhcmVhX2szX25iICU+JSBuYjJsaXN0dygpICNjcmVhdGVzIGxpc3R3IG9iamVjdCBmb3IgTW9yYW4ncyBJIHRlc3QNCg0KIyNwZXJmb3JtaW5nIGxtLm1vcmFudGVzdCgpDQoNCiNlbXB0eSBtb2RlbCByZXNpZHVhbHMNCmxtLm1vcmFudGVzdChtMCwgbGlzdHcgPSBhcmVhX2xpc3R3KQ0KDQpgYGANCg0KDQoNCg0KYGBge3J9DQojY29uZGl0aW9uYWwgbW9kZWwgcmVzaWR1YWxzDQoNCiNtMV8xIDwtIGxtKHRvdF9zcGVuZCB+IGluY18xNywgZGF0YSA9IGFyZWFfYXR0cykgI0kgdGhvdWdodCBvZiB0byBqdXN0IHRlc3QgdGhlIGVmZmVjdCBvZiBpbmNvbWUgc2luY2UgcG9wX2RlbnMgd2Fzbid0IHNpZ25pZmljYW50IGJ1dCBkZWNpZGVkIHRvIGluY2x1ZGUgcG9wX2RlbnMgYWZ0ZXIgYWxsDQoNCmxtLm1vcmFudGVzdChtMSwgbGlzdHcgPSBhcmVhX2xpc3R3KQ0KYGBgDQoNCkZyb20gdGhlc2UgdGVzdHMgd2Ugc2VlIHRoZXJlIGlzIHJlbGF0aXZlbHkgbG93IHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGluIG1vZGVsIHJlc2lkdWFscyBmb3IgYnVkZ2V0ZWQgc3BlbmRpbmcuIE9mIHRoYXQsIG9ubHkgYWJvdXQgMTclIGlzIGV4cGxhaW5lZCBieSB0cmFjdCBtZWRpYW4gaW5jb21lIGFuZCBwb3B1bGF0aW9uIGRlbnNpdHkuIE15IGNvbmplY3R1cmUgaXMgdGhhdCBtdWNoIG9mIHRoZSByZW1haW5pbmcgYXV0b2NvcnJlbGF0aW9uIGlzIGV4cGxhaW5lZCBieSB0aGUgZmFjdCB0aGF0IG1hbnkgb2YgdGhlIGhpZ2gtdmFsdWUgcHJvamVjdHMgYXJlIGNvbXByaXNlZCBvZiBiZWx0bGluZSByYWlsLiBCeSBkZWZpbml0aW9uLCBhbGwgdGhlc2UgcHJvamVjdHMgYXJlIGNvbnRpZ3VvdXMgYW5kIHNpbmNlIHRoZSBiZWx0bGluZSBmb2xsb3dzIG9sZCByYWlsIHRocm91Z2h3YXlzIGluIHRoZSBjaXR5LCB0aGVpciBsb2NhdGlvbnMgbWF5IGJlIGxlc3MgZW50YWdsZWQgd2l0aCBvdGhlciBkZW1vZ3JhcGhpYyBmYWN0b3JzLg0KDQpGb3IgdGhlIGZpbmFsIGZpZ3VyZXMgaW4gdGhpcyBwcm9qZWN0LCBJJ2xsIG1hcCB0aGUgbW9kZWwgcmVzaWR1YWxzLg0KDQpgYGB7cn0NCmFyZWFfYXR0cyRtMF9yZXNpZCA8LSByZXNpZChtMCkgI2NyZWF0ZSByZXNpZHVhbCB2YXJpYWJsZXMNCmFyZWFfYXR0cyRtMV9yZXNpZCA8LSByZXNpZChtMSkNCg0KdG1fc2hhcGUoYXJlYV9hdHRzKSArDQogIHRtX2ZpbGwoYygnbTBfcmVzaWQnLCAnbTFfcmVzaWQnKSwgdGl0bGUgPSBjKCdtMCByZXNpZHNcbihtaWxsLiBVU0QpJywnIG0xIHJlc2lkc1xuKG1pbGwuIFVTRCknKSwNCiAgICAgICAgICBzdHlsZSA9ICdzZCcsDQogICAgICAgICAgcGFsZXR0ZSA9ICctUmRZbEJ1JykgKyB0bV9ib3JkZXJzKCkgKw0KICB0bV9sYXlvdXQobGVnZW5kLnBvc2l0aW9uID0gYygnbGVmdCcsJ3RvcCcpLCBsZWdlbmQuZm9ybWF0ID0gbGlzdChkaWdpdHMgPTEpLCBsZWdlbmQudGV4dC5zaXplID0gMC42LCBsZWdlbmQudGl0bGUuc2l6ZSA9IDAuOTAsIGlubmVyLm1hcmdpbnMgPSBjKDAuMDIsMC4yLCAwLjAyLCAwLjAyKSkNCg0KYGBgDQoNClRoZSBtYXBzIGlsbHVzdHJhdGUgdGhhdCBvdXIgZmFjdG9ycyAqKnNsaWdodGx5KiogcmVkdWNlIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGJ1dCB0aGVyZSBpcyBzdGlsbCBhIGZhaXIgYW1vdW50IG9mIHNwYXRpYWwgc3RydWN0dXJlIHByZXNlbnQgZHVlIHRvIHRoZSBwZXJpcGhlcnkgdHJhY3RzIHdpdGggemVybyBzcGVuZGluZy4gSW50ZXJlc3RpbmdseSwgc29tZSBvZiB0aGUgbGFyZ2VzdCByZXNpZHVhbHMgYXJlIGluIHRoZSBjZW50cmFsIHBvcnRpb24gb2YgdGhlIGNpdHkuIFRoaXMgaXMgd2hlcmUsIGFzIEkgbWVudGlvbmVkIHByZXZpb3VzbHksIGlubmFjdXJhY2llcyBpbiBkaWdpdGl6YXRpb24gbWF5IGhhdmUgbWlzc2VkIGFzc2lnbmluZyBzcGVuZGluZyB0byBzb21lIHRyYWN0cy4NCg0K