In an earlier notebook I examined the nature of arrests in Baltimore, Maryland, by creating various visualizations that used the city’s Part I arrests open dataset.

Among the feedback I received on this notebook was a suggestion to use the city’s Calls for Service dataset to look at the distribution of 911 calls across the city, as well as the relationship between arrests and calls for service. Of particular interest is whether there is a difference, across neighborhoods, in the number of arrests that originate from a 911 call versus otherwise (e.g., officers observing potential criminal activity while on patrol).

It is important to note that the Part I Arrest and Call for Service datasets are not directly linked. That is, there is no shared identifier in the datasets that would allow us to associate each arrest that originated from a 911 call (via dispatch) with the corresponding Call for Service record.We are, however, able to look at the relationship between Calls for Service and Arrests in the aggregate for various geographic areas of the city. Like the Arrest dataset, the Call for Service dataset includes approximate latitude/longitude coordinates for the incident location. This allows us to overlay the calls for service onto the city’s neighborhood shapefile and the US Census Bureau census tract shapefile, just as we did in the previous notebook analysis. Then we can, for each geographic area, compute a ratio of the number of arrests to the number of calls for service to get a sense of the relationship.

It will be key to exercise some caution in interpreting this ratio, since there are many factors that could explain a difference in the ratio across neighborhoods. If Neighborhood A sees 3 arrests per hundred 911 calls (somewhat below the city-wide average, as we will see shortly), and Neighborhood B sees 6 arrests per 100 calls, it could be the case that the police department is devoting more resources to patrolling Neighborhood B, leading to more officer-observed activity that results in an arrest without an underlying service call. Or it could be that the inhabitants of Neighborhood A are more able, willing, or otherwise likely to report incidents to the city by calling 911 than the inhabitants of Neighborhood B. Alternatively, the calls for service in Neighborhood A may involve a greater proportion of incidents that tend not to lead to arrest, such as traffic accidents, nuisance calls, medical emergencies, and the like. There is not much in the dataset that allows us to tease out these factors, but perhaps we can nonetheless gain some useful insights about the nature of crime in Baltimore that can be explored further in future analyses.

Dataset Summary

The Calls for Service dataset that I used in this notebook has 2,514,489 records covering the period 01/01/2015 to 06/17/2017. Each record contains:

  • The date and time of the call
  • The priority of the call (Emergency, High, Low, Medium, Non-Emergency, Out of Service)
  • A description of the call (which appears to be quasi-free text)
  • Location information, which includes the district and approximate address and coordinates of the incident
  • A call identifier

I subsetted the raw dataset to cover the same date range as the Part I Arrest dataset used in the prior notebook, and removed any records with missing values for call date/time or location coordinates.

For details on how I read in the raw dataset and integrated it with the other data sources used in the notebook, see the Baltimore-ReadData.R script in the GitHub repository.

Distribution of Calls for Service by Priority

To get an initial feel for the dataset, consider the following bar chart depicting the priority level of calls:

CallsForService %>% group_by(priority) %>% summarize(n=n()) %>% mutate(pct=n/sum(n), pcts=percent(pct)) %>%
  mutate(priority=ifelse(is.na(priority), '(Missing)', priority)) %>%
  ggplot() +
  geom_bar(mapping=aes(x=reorder(priority, n), y=n), stat='identity') + coord_flip() +
  stat_summary(fun.y = identity, geom="text", aes(x=reorder(priority, n), y=n, label=pcts), hjust = -.25) +
  scale_y_continuous(labels=comma, limits=c(0,1500000)) +
  theme_economist() +
  labs(x='Call Priority', y='Number of Calls', title='Distribution of Calls by Priority',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS),
       caption=paste0('n=', comma(nrow(CallsForService))))

Since the city’s dataset documentation does not define the rules used to assign these priority labels to calls, it is somewhat difficult to interpret the distribution. As the bar chart indicates, a slight majority of all calls (and by far the largest category) is “Medium” priority.

We can also look at the distribution of calls by priority across the city. In this choropleth map, we have taken the ratio of “Emergency” and “High” priority calls, in the numerator, to the number of “Low” priority calls, in the denominator:

CensusBlockSDF %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=UrgentCallRatio)) +
    geom_path(color='grey70') +
    geom_path(data=CountySDF, color='grey50') +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90') +
    coord_map() +
    theme_void() +
  theme(legend.title.align=0.5) +
  labs(fill='Ratio',
       title='Ratio of "Urgent" to "Non-Urgent" Calls in Each Baltimore Census Tract',
       caption='Urgent=Emergency or High priority, Non-Urgent=Low priority',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS))

There is considerable variation across Census tracts in the relative proportion of urgent calls, with several areas to the west and east of downtown, the Belair area northeast of downtown, and the tract to the southwest of Johns Hopkins University all having more urgent than non-urgent calls. Conversely, along the outer edges of the city, non-urgent calls are relatively more frequent.

When do Calls for Service Occur?

It’s also interesting to examine counts of calls by day of the year and time of day. To enable comparison to the heat map of arrests in the prior analysis, this heat map of calls covers calendar year 2015:

CallsForService %>% filter(year(CallDate)==2015) %>%
  group_by(CallDate, CallHour) %>%
  summarize(n=n()) %>%
  ungroup() %>%
  arrange(CallDate, CallHour) %>%
  ggplot() +
  geom_tile(aes(x=CallDate, y=CallHour, fill=n), na.rm=TRUE) +
  scale_fill_gradient(low = "white",high = "steelblue") + scale_y_discrete(limits=0:23) +
  scale_x_date(date_breaks='1 months', date_labels='%b-%e') +
  theme(axis.ticks.y=element_blank(), panel.background=element_blank(), legend.position='bottom') +
  labs(x=NULL, y=NULL, fill='Calls/hour', title='Calls for Service by Time of Day, each day in 2015',
       subtitle='Baltimore Police Department')

As with the arrest count heat map in the prior analysis, the unrest that occurred in the second half of April 2015 is apparent in an increase in calls for service. However, there are some interesting differences in calls for service versus arrests:

  • There is a distinct pattern to early morning calls (i.e., midnight - 3:00 am) that suggests a weekly (possibly every weekend) rhythm
  • There are distinct periods without any calls for service (indicated by pure white lines in the map); these could be times when the city’s data collection process (that populates the open dataset) was offline or interrupted for some reason
  • There are noticeable increases in calls on the night of July 4 (quite possibly for citizens calling about violations of the city’s ban on fireworks) and also, for an hour or so, on Halloween

Taking a closer look at early morning calls, and the distribution of those calls by day of week, confirms a significant increase on weekends:

CallsForService %>%
  filter(CallHour %in% 0:4) %>%
  mutate(down=wday(CallDate), dow=as.character(wday(CallDate, label=TRUE, abbr=FALSE))) %>%
  group_by(down, dow) %>% summarize(n=n()) %>% ungroup() %>% mutate(pct=n/sum(n), pcts=percent(pct)) %>%
  ggplot() +
  geom_bar(mapping=aes(x=reorder(dow, -down), y=n), stat='identity') + coord_flip() +
  stat_summary(fun.y = identity, geom="text", aes(x=reorder(dow, -down), y=n, label=pcts), hjust = -.25) +
  scale_y_continuous(labels=comma, limits=c(0,75000)) +
  theme_economist() +
  labs(x=NULL, y='Number of Calls', title='Distribution of Early Morning Calls by Day of Week',
       subtitle=paste0('Calls between midnight and 3 am, Baltimore City, ', minDateS, ' - ', maxDateS),
       caption=paste0('n=', comma(nrow(CallsForService))))

From Where in the City do Calls Originate?

Unlike the Arrest dataset, the city’s Calls for Service dataset does not contain a variable identifying the neighborhood associated with the call. However, since each call for service includes approximate coordinates for the related incident, we are able to overlay the city’s neighborhood shapefile and the Census tract shapefile to get a sense of the geographic distribution of calls.

TopCallNeighborhoods <- NeighborhoodDf %>%
  filter(!is.infinite(CallsForServicePerCapita)) %>%
  arrange(desc(CallsForServicePerCapita)) %>%
  head(10) %>%
  bind_cols(tibble(KeyAbbr=c(LETTERS[1:(nrow(.))]))) %>%
  select(KeyAbbr, Name, CallsForServicePerCapita, CallsForService, Population, CentroidLongitude, CentroidLatitude)
NeighborhoodSDF %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=CallsForServicePerCapita)) +
    geom_path(color='grey70') +
    geom_label(data=TopCallNeighborhoods,
               mapping=aes(x=CentroidLongitude, y=CentroidLatitude, label=KeyAbbr),
               inherit.aes=FALSE, size=2) +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90', trans='log', breaks=c(2,8,32,96)) +
    coord_map() +
    theme_void() +
  theme(legend.key.height=unit(2.5, 'line'), legend.title.align=0.5) +
  labs(fill='Calls/Capita',
       title='Calls for Service per Capita in Baltimore Neighborhoods',
       caption='Note: Neighborhood shading for Calls/Capita value is log-scaled',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS))

Similar to the distribution of arrests per capita as seen in the prior analysis, calls for service (on a per capita basis) are significantly higher in the industrial areas, not because there are significantly more calls in those areas, but because there are fewer residents living there. Note that we use a log scale for the color gradient in this choropleth map, to reduce the impact of the outliers on the gradient.

The ten neighborhoods with the highest per-capita calls over this two-and-a-half year period, are:

kable(TopCallNeighborhoods %>%
        select(Label=KeyAbbr, Neighborhood=Name, CallsForService, Population, `Calls per Capita`=CallsForServicePerCapita) %>%
        mutate(`Calls per Capita`=comma(`Calls per Capita`), `Calls for Service`=comma(CallsForService), Population=comma(Population)) %>%
        select(-CallsForService), format='html'
) %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Label Neighborhood Population Calls per Capita Calls for Service
A Pulaski Industrial Area 123 135.14634 16,623
B Fairfield Area 159 114.71698 18,240
C Hopkins Bayview 103 66.68932 6,869
D Orangeville 202 51.35644 10,374
E Downtown West 366 45.35519 16,600
F University Of Maryland 387 30.36693 11,752
G Downtown 4,448 26.98876 120,046
H Dunbar-Broadway 889 24.65467 21,918
I Charles North 1,059 14.73560 15,605
J Inner Harbor 1,484 12.36725 18,353

Aggregating calls per capita by Census tract indicates that calls per capita (like arrests per capita) are highest in the downtown area, and slightly less so to the immediate west of downtown and in the Pulaski area:

CensusBlockSDF %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=CallsForServicePerCapita)) +
    geom_path(color='grey70') +
    geom_path(data=CountySDF, color='grey50') +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90', labels=comma, trans='log', breaks=c(2,4,8,16,32)) +
    coord_map() +
    theme_void() +
  theme(legend.key.height=unit(2.5, 'line'), legend.title.align=0.5) +
  labs(fill='Calls/Capita',
       title='Calls for Service per Capita in Each Baltimore Census Tract',
       caption='Census Tract population is from 2015 5-year American Community Survey (ACS) estimates',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS))

Do Demographic and Economic Factors Correlate with Call Volume?

Citywide, over the time period covered by this dataset, there were 3.92 calls for service per capita. It is fair to ask whether this citywide average is significantly different across a spectrum of demographic and economic characteristics by area. In the following scatterplots, we see that the percentage of Census tract population that is white, the median age, and the tract’s median household income are all negatively correlated with service calls per capita (though the strength of the correlation varies somewhat across these explanatory variables).

ggplot(data=CensusBlockDf %>% filter(CallsForServicePerCapita < 30)) +
  geom_point(mapping=aes(x=PercentWhite, y=CallsForServicePerCapita), na.rm=TRUE) +
  geom_smooth(mapping=aes(x=PercentWhite, y=CallsForServicePerCapita), na.rm=TRUE,
              method='lm', color='red', size=.25, se=FALSE) +
    scale_x_continuous(labels=percent) +
  labs(title='Calls for Service per Capita and Race, by Census Tract',
       x='% of Population that is White', y='Calls for Service per Capita',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS),
       caption='Census Tract population values are from 2015 5-year American Community Survey (ACS) estimates') +
  theme_economist()

ggplot(data=CensusBlockDf %>% filter(CallsForServicePerCapita < 30)) +
  geom_point(mapping=aes(x=MedianAge, y=CallsForServicePerCapita), na.rm=TRUE) +
  geom_smooth(mapping=aes(x=MedianAge, y=CallsForServicePerCapita), na.rm=TRUE,
              method='lm', color='red', size=.25, se=FALSE) +
  labs(title='Calls for Service per Capita and Age, by Census Tract',
       x='Median Age', y='Calls for Service per Capita',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS),
       caption='Census Tract population values are from 2015 5-year American Community Survey (ACS) estimates') +
  theme_economist()

ggplot(data=CensusBlockDf %>% filter(CallsForServicePerCapita < 30)) +
  geom_point(mapping=aes(x=MedianHouseholdIncome, y=CallsForServicePerCapita), na.rm=TRUE) +
  geom_smooth(mapping=aes(x=MedianHouseholdIncome, y=CallsForServicePerCapita), na.rm=TRUE,
              method='lm', formula=y ~ log(x), color='red', size=.25, se=FALSE) +
  scale_x_continuous(labels=dollar) +
  labs(title='Calls for Service per Capita and Income, by Census Tract',
       x='Median Annual Household Income', y='Calls for Service per Capita',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS),
       caption='Census Tract population and income values are from 2015 5-year American Community Survey (ACS) estimates') +
  theme_economist()

There is also a correlation between the type of land use, as classified by the state of Maryland (in 2010), with “more residential” Census tracts exhibiting a relatively lower frequency of calling for service:

ggplot(data=CensusBlockDf %>% filter(CallsForServicePerCapita < 30)) +
  geom_point(mapping=aes(x=ResidentialPercentage, y=CallsForServicePerCapita), na.rm=TRUE) +
  geom_smooth(mapping=aes(x=ResidentialPercentage, y=CallsForServicePerCapita), na.rm=TRUE,
              method='lm', color='red', size=.25, se=FALSE) +
  scale_x_continuous(labels=percent) +
  labs(title='Calls for Service per Capita and Residential Land Use, by Census Tract',
       x='% of Census tract classified as residential', y='Calls for Service per Capita',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS),
       caption='Census Tract population values are from 2015 5-year ACS estimates, land use classification from state of Maryland') +
  theme_economist()

It is worth noting that there is intuitively a high degree of correlation among these explanatory variables. To tease out the partial effects, we run a regression model of per-capita Calls for Service on these four variables (taking the log of household income to account for the exponential effect of this variable detected in the visualization above):

model <- lm(data=CensusBlockDf,
   formula=CallsForServicePerCapita ~ log(MedianHouseholdIncome) + ResidentialPercentage + MedianAge + PercentWhite)
model %>%
  tidy() %>%
  select(-statistic) %>%
  mutate(term=case_when(
    grepl(x=term, pattern='Income') ~ 'log(Median HH Income)',
    grepl(x=term, pattern='Residential') ~ '% Residential',
    grepl(x=term, pattern='Age') ~ 'Median Age',
    grepl(x=term, pattern='White') ~ '% White',
    TRUE ~ '(Intercept)'
  )) %>%
kable(format='html', digits=c(0, 2, 2, 3), col.names=c('Variable', 'Estimate', 'Std Error', 'p(>|t|)')) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Variable Estimate Std Error p(>|t|)
(Intercept) 37.67 6.22 0.000
log(Median HH Income) -2.77 0.62 0.000
% Residential -5.09 0.85 0.000
Median Age -0.02 0.04 0.506
% White -0.06 1.01 0.951

In this model, both income and residential land use proportion are negatively correlated with calls per capita, and are statistically significant. Age and race are not statistically significant, suggesting that the effects of these variables “wash out” when income and land use are included.

The overall explanatory power of this model is not very high–an R2 of 0.023 suggests that our model only explains 2.3% or the variance in per-capita calls for service across Census tracts. Still, the model provides some evidence that differences in per-capita call volume across the city do vary by income and residential land use proportion, but not directly by the racial or age makeup of the population.

The Relationship Between Calls for Service and Arrests

As discussed in the introduction above, we can potentially learn something interesting about crime and policing in Baltimore by examining the relationship between arrests and calls for service. We do so here by computing the ratio of arrests to calls and looking at how this ratio varies across Census tracts in the city.

Between January 01, 2015 and June 17, 2017, the city-wide ratio of arrests was 0.049, or 4.9 arrests per 100 calls for service. This ratio varies somewhat across the city, however:

CensusBlockSDF %>%
  mutate(ArrestsPer100CallsForService=100*ArrestsPerCallForService) %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=ArrestsPer100CallsForService)) +
    geom_path(color='grey70') +
    geom_path(data=CountySDF, color='grey50') +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90') +
    coord_map() +
    theme_void() +
  theme(legend.key.height=unit(2.5, 'line'), legend.title.align=0.5) +
  labs(fill='Arrests/100 CFS',
       title='Arrests per Call for Service in Each Baltimore Census Tract',
       caption='Census Tract population is from 2015 5-year American Community Survey (ACS) estimates',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS))

We can also examine the variation by neighborhood, with the top and bottom five neighborhoods being as follows:

ndf <- bind_rows(
  NeighborhoodDf %>% filter(Population != 0) %>% top_n(5, ArrestsPerCallForService),
  NeighborhoodDf %>% filter(Population != 0) %>% top_n(-5, ArrestsPerCallForService)
) %>% select(Neighborhood=Name, ArrestsPerCallForService, CallsForServicePerCapita, ArrestsPerCapita, Population) %>%
  mutate(ArrestsPerCallForService=100*ArrestsPerCallForService) %>%
  arrange(desc(ArrestsPerCallForService))
kable(ndf %>%
        mutate(`Calls per Capita`=comma(CallsForServicePerCapita, digits=2, nsmall=2),
               `Arrests per Capita`=comma(ArrestsPerCapita, digits=2, nsmall=2),
               `Arrests per 100 CFS`=comma(ArrestsPerCallForService, digits=2, nsmall=2),
               Population=comma(Population, digits=0)) %>%
        select(Neighborhood,
               `Arrests per 100 CFS`,
               `Arrests per Capita`,
               `Calls per Capita`,
               Population), format='html'
) %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Neighborhood Arrests per 100 CFS Arrests per Capita Calls per Capita Population
Hoes Heights 20.75 0.206 0.99 865
Wyman Park 12.87 0.102 0.79 1,141
Villages Of Homeland 12.85 0.057 0.44 562
North Roland Park/Poplar Hill 11.79 0.066 0.56 1,450
Lake Evesham 11.54 0.149 1.29 543
Biddle Street 1.55 0.178 11.45 1,265
Winston-Govans 1.52 0.165 10.82 1,341
Blythewood 0.96 0.028 2.89 72
Fairfield Area 0.89 1.019 114.72 159
Johns Hopkins Homewood 0.85 0.076 8.93 669

By running a similar regression to the one above, but with Arrests per 100 Calls For Service as the dependent variable this time, we can examine the relationships between the ratio and key Census tract characteristics.

model <- lm(data=CensusBlockDf %>%  mutate(ArrestsPerCallForService=100*ArrestsPerCallForService),
   formula=ArrestsPerCallForService ~ log(MedianHouseholdIncome) + ResidentialPercentage + MedianAge + PercentWhite)
model %>%
  tidy() %>%
  select(-statistic) %>%
  mutate(term=case_when(
    grepl(x=term, pattern='Income') ~ 'log(Median HH Income)',
    grepl(x=term, pattern='Residential') ~ '% Residential',
    grepl(x=term, pattern='Age') ~ 'Median Age',
    grepl(x=term, pattern='White') ~ '% White',
    TRUE ~ '(Intercept)'
  )) %>%
kable(format='html', digits=c(0, 3, 3, 3), col.names=c('Variable', 'Estimate', 'Std Error', 'p(>|t|)')) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Variable Estimate Std Error p(>|t|)
(Intercept) -1.090 2.867 0.704
log(Median HH Income) 0.531 0.285 0.064
% Residential 2.324 0.394 0.000
Median Age -0.023 0.016 0.159
% White 1.193 0.463 0.011

Note that the proportion of residential land use is a significant explanatory variable here, as it was in the regression on calls per capita. However, the sign is reversed, suggesting that calls from within Census tracts that have a higher proportion of residential land use tend to have a higher ratio of arrests per call, all else being equal. Additionally, as the percentage of a tract’s population that is white increases, the ratio of arrests to calls increases. Note that neither (log of) median household income nor median age is a significant explanatory variable in this regression.

The relationship among these three variables is not quite as apparent on a scatterplot visualization, but still noticeable, with larger dots (indicating more arrests per call) in both dimensions away from the origin.

ggplot(data=CensusBlockDf %>% mutate(ArrestsPerCallForService=100*ArrestsPerCallForService)) +
  geom_point(mapping=aes(x=ResidentialPercentage, y=PercentWhite, size=ArrestsPerCallForService), na.rm=TRUE) +
  scale_x_continuous(labels=percent) +
  scale_y_continuous(labels=percent) +
  scale_size_continuous(range=c(0, 6)) +
  labs(title='Race, Land Use, and Arrest-to-Calls Ratio',
       x='% of Census tract classified as residential', y='% of Population that is White',
       size='Arrests / 100 CFS:',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS)) +
  theme_economist()

Finally, we examine whether a higher ratio of “urgent” calls in a Census tract correlates with a higher ratio of arrests per 100 calls. One might expect such a relationship, based on the reasoning that more urgent calls for service could be for incidents (such as robberies, burglaries, shootings, etc.) that tend to involve crimes that, in turn, lead to arrest. The data, however, tell a quite different story:

ggplot(data=CensusBlockDf %>% mutate(ArrestsPerCallForService=100*ArrestsPerCallForService)) +
  geom_point(mapping=aes(x=UrgentCallRatio, y=ArrestsPerCallForService), na.rm=TRUE) +
  labs(title='Arrest-to-Calls Ratio and Call Urgency, by Census Tract',
       x='Ratio of "Urgent" Calls to "Non-Urgent" Calls', y='Arrests per 100 Calls for Service',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS),
       caption='Urgent=Emergency or High priority, Non-Urgent=Low priority') +
  theme_economist()

A Pearson correlation coefficient of 0.152 here indicates a weak relationship between the variables. One possible explanation might be found in further examination of the nature of “urgent” calls. The five most frequently-occurring call descriptions, for “urgent” calls, were:

sdf <- CallsForService %>% filter(priority %in% c('Emergency', 'High')) %>%
  group_by(description) %>% summarize(n=n()) %>% top_n(5, n) %>% arrange(desc(n))
writeLines(paste('* ', paste(sdf$description, comma(sdf$n), sep=': ')))
  • Traffic Stop: 131,656
  • SILENT ALARM: 70,889
  • Field Interview: 31,689
  • AUTO ACC/INJURY: 24,821
  • RA POLICE: 24,110

Indeed, many emergency and high-priority calls with these descriptions would not necessarily lead to an arrest. Thus, it is not entirely surprising to find a weak relationship between the call urgency ratio and and the arrests-per-100 CFS measure.

Summary

This analysis of the City of Baltimore’s Calls for Service dataset, in conjunction with its Arrest dataset, has not produced any ground-breaking conclusions. However, it does show the potential of open data initiatives to enable analysts to glean interesting information from a city’s data. We were able to discern a few not-so-surprising facts from the dataset, including:

  • There is significant variation in the volume of service calls across the city, even when accounting for population density
  • High-volume days and times, like July 4 and Halloween, as well as times of civil unrest, are clearly apparent in the data
  • Call volume, normalized by population density, is negatively (and significantly) correlated with both median household income and residential classification
  • The ratio of arrests to calls is positively correlated with residential classification and the percentage of an area that is of white race

Kudos to the City of Baltimore for making these datasets available. Like many cities across the country, including those participating in the Police Data Initiative, Baltimore has made an important commitment to transparency and community engagement by publishing rich sets of data on public safety operations.

LS0tCnRpdGxlOiAiQXJyZXN0cyBhbmQgQ2FsbHMgZm9yIFNlcnZpY2UgaW4gQmFsdGltb3JlIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKLS0tCgpJbiBhbiBlYXJsaWVyIFtub3RlYm9va10oQmFsdGltb3JlLm5iLmh0bWwpIEkgZXhhbWluZWQgdGhlIG5hdHVyZSBvZiBhcnJlc3RzIGluIEJhbHRpbW9yZSwgTWFyeWxhbmQsIGJ5IGNyZWF0aW5nCnZhcmlvdXMgdmlzdWFsaXphdGlvbnMgdGhhdCB1c2VkIHRoZSBjaXR5J3MgW1BhcnQgSSBhcnJlc3RzXShodHRwczovL2RhdGEuYmFsdGltb3JlY2l0eS5nb3YvUHVibGljLVNhZmV0eS9CUEQtUGFydC0xLVZpY3RpbS1CYXNlZC1DcmltZS1EYXRhL3dzZnEtbXZpaikgb3BlbiBkYXRhc2V0LgoKQW1vbmcgdGhlIGZlZWRiYWNrIEkgcmVjZWl2ZWQgb24gdGhpcyBub3RlYm9vayB3YXMgYSBzdWdnZXN0aW9uIHRvIHVzZSB0aGUgY2l0eSdzCltDYWxscyBmb3IgU2VydmljZV0oaHR0cHM6Ly9kYXRhLmJhbHRpbW9yZWNpdHkuZ292L1B1YmxpYy1TYWZldHkvOTExLUNhbGxzLWZvci1TZXJ2aWNlL3h2aXUtZXprdCkgZGF0YXNldCB0byBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgOTExIGNhbGxzIGFjcm9zcyB0aGUgY2l0eSwgYXMKd2VsbCBhcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYXJyZXN0cyBhbmQgY2FsbHMgZm9yIHNlcnZpY2UuIE9mIHBhcnRpY3VsYXIgaW50ZXJlc3QgaXMgd2hldGhlciB0aGVyZSBpcyBhIGRpZmZlcmVuY2UsIGFjcm9zcyBuZWlnaGJvcmhvb2RzLCBpbiB0aGUKbnVtYmVyIG9mIGFycmVzdHMgdGhhdCBvcmlnaW5hdGUgZnJvbSBhIDkxMSBjYWxsIHZlcnN1cyBvdGhlcndpc2UgKGUuZy4sIG9mZmljZXJzIG9ic2VydmluZyBwb3RlbnRpYWwgY3JpbWluYWwgYWN0aXZpdHkgd2hpbGUgb24gcGF0cm9sKS4KCkl0IGlzIGltcG9ydGFudCB0byBub3RlCnRoYXQgdGhlIFBhcnQgSSBBcnJlc3QgYW5kIENhbGwgZm9yIFNlcnZpY2UgZGF0YXNldHMgYXJlIG5vdCBkaXJlY3RseSBsaW5rZWQuICBUaGF0IGlzLCB0aGVyZSBpcyBubyBzaGFyZWQgaWRlbnRpZmllciBpbiB0aGUgZGF0YXNldHMgdGhhdCB3b3VsZCBhbGxvdyB1cyB0byBhc3NvY2lhdGUKZWFjaCBhcnJlc3QgdGhhdCBvcmlnaW5hdGVkIGZyb20gYSA5MTEgY2FsbCAodmlhIGRpc3BhdGNoKSB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIENhbGwgZm9yIFNlcnZpY2UgcmVjb3JkLldlIGFyZSwgaG93ZXZlciwgYWJsZSB0byBsb29rIGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbgpDYWxscyBmb3IgU2VydmljZSBhbmQgQXJyZXN0cyBpbiB0aGUgYWdncmVnYXRlIGZvciB2YXJpb3VzIGdlb2dyYXBoaWMgYXJlYXMgb2YgdGhlIGNpdHkuICBMaWtlIHRoZSBBcnJlc3QKZGF0YXNldCwgdGhlIENhbGwgZm9yIFNlcnZpY2UgZGF0YXNldCBpbmNsdWRlcyBhcHByb3hpbWF0ZSBsYXRpdHVkZS9sb25naXR1ZGUgY29vcmRpbmF0ZXMgZm9yIHRoZSBpbmNpZGVudCBsb2NhdGlvbi4gVGhpcyBhbGxvd3MgdXMgdG8gb3ZlcmxheSB0aGUgY2FsbHMgZm9yIHNlcnZpY2Ugb250bwp0aGUgY2l0eSdzIG5laWdoYm9yaG9vZCBzaGFwZWZpbGUgYW5kIHRoZSBVUyBDZW5zdXMgQnVyZWF1IGNlbnN1cyB0cmFjdCBzaGFwZWZpbGUsIGp1c3QgYXMgd2UgZGlkIGluIHRoZSBwcmV2aW91cyBub3RlYm9vayBhbmFseXNpcy4gVGhlbiB3ZSBjYW4sIGZvciBlYWNoIGdlb2dyYXBoaWMKYXJlYSwgY29tcHV0ZSBhIHJhdGlvIG9mIHRoZSBudW1iZXIgb2YgYXJyZXN0cyB0byB0aGUgbnVtYmVyIG9mIGNhbGxzIGZvciBzZXJ2aWNlIHRvIGdldCBhIHNlbnNlIG9mIHRoZSByZWxhdGlvbnNoaXAuCgpJdCB3aWxsIGJlIGtleSB0byBleGVyY2lzZSBzb21lIGNhdXRpb24gaW4gaW50ZXJwcmV0aW5nIHRoaXMgcmF0aW8sIHNpbmNlIHRoZXJlIGFyZSBtYW55IGZhY3RvcnMgdGhhdCBjb3VsZCBleHBsYWluIGEgZGlmZmVyZW5jZSBpbiB0aGUgcmF0aW8gYWNyb3NzIG5laWdoYm9yaG9vZHMuIElmCk5laWdoYm9yaG9vZCBBIHNlZXMgMyBhcnJlc3RzIHBlciBodW5kcmVkIDkxMSBjYWxscyAoc29tZXdoYXQgYmVsb3cgdGhlIGNpdHktd2lkZSBhdmVyYWdlLCBhcyB3ZSB3aWxsIHNlZSBzaG9ydGx5KSwgYW5kIE5laWdoYm9yaG9vZCBCIHNlZXMgNiBhcnJlc3RzIHBlciAxMDAgY2FsbHMsIGl0IGNvdWxkIGJlIHRoZQpjYXNlIHRoYXQgdGhlIHBvbGljZSBkZXBhcnRtZW50IGlzIGRldm90aW5nIG1vcmUgcmVzb3VyY2VzIHRvIHBhdHJvbGxpbmcgTmVpZ2hib3Job29kIEIsIGxlYWRpbmcgdG8gbW9yZSBvZmZpY2VyLW9ic2VydmVkIGFjdGl2aXR5IHRoYXQgcmVzdWx0cyBpbiBhbiBhcnJlc3Qgd2l0aG91dCBhbgp1bmRlcmx5aW5nIHNlcnZpY2UgY2FsbC4gIE9yIGl0IGNvdWxkIGJlIHRoYXQgdGhlIGluaGFiaXRhbnRzIG9mIE5laWdoYm9yaG9vZCBBIGFyZSBtb3JlIGFibGUsIHdpbGxpbmcsIG9yIG90aGVyd2lzZSBsaWtlbHkgdG8gcmVwb3J0IGluY2lkZW50cyB0byB0aGUgY2l0eSBieSBjYWxsaW5nCjkxMSB0aGFuIHRoZSBpbmhhYml0YW50cyBvZiBOZWlnaGJvcmhvb2QgQi4gQWx0ZXJuYXRpdmVseSwgdGhlIGNhbGxzIGZvciBzZXJ2aWNlIGluIE5laWdoYm9yaG9vZCBBIG1heSBpbnZvbHZlIGEgZ3JlYXRlciBwcm9wb3J0aW9uIG9mIGluY2lkZW50cyB0aGF0IHRlbmQgbm90IHRvIGxlYWQgdG8KYXJyZXN0LCBzdWNoIGFzIHRyYWZmaWMgYWNjaWRlbnRzLCBudWlzYW5jZSBjYWxscywgbWVkaWNhbCBlbWVyZ2VuY2llcywgYW5kIHRoZSBsaWtlLiBUaGVyZSBpcyBub3QgbXVjaCBpbiB0aGUgZGF0YXNldCB0aGF0IGFsbG93cyB1cyB0byB0ZWFzZSBvdXQgdGhlc2UgZmFjdG9ycywgYnV0CnBlcmhhcHMgd2UgY2FuIG5vbmV0aGVsZXNzIGdhaW4gc29tZSB1c2VmdWwgaW5zaWdodHMgYWJvdXQgdGhlIG5hdHVyZSBvZiBjcmltZSBpbiBCYWx0aW1vcmUgdGhhdCBjYW4gYmUgZXhwbG9yZWQgZnVydGhlciBpbiBmdXR1cmUgYW5hbHlzZXMuCgpgYGB7ciBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSwgcXVpZXRseT1UUlVFLCB3YXJuLmNvbmZsaWN0cz1GQUxTRSkKbGlicmFyeShnZ3Bsb3QyLCBxdWlldGx5PVRSVUUsIHdhcm4uY29uZmxpY3RzPUZBTFNFKQpsaWJyYXJ5KGdndGhlbWVzLCBxdWlldGx5PVRSVUUsIHdhcm4uY29uZmxpY3RzPUZBTFNFKQpsaWJyYXJ5KHNjYWxlcywgcXVpZXRseT1UUlVFLCB3YXJuLmNvbmZsaWN0cz1GQUxTRSkKbGlicmFyeShrbml0ciwgcXVpZXRseT1UUlVFLCB3YXJuLmNvbmZsaWN0cz1GQUxTRSkKbGlicmFyeShrYWJsZUV4dHJhLCBxdWlldGx5PVRSVUUsIHdhcm4uY29uZmxpY3RzPUZBTFNFKQoKaWYgKCFleGlzdHMoJ2FsbE9iamVjdHMnKSkgYWxsT2JqZWN0cyA8LSByZWFkUkRTKCdhbGxPYmplY3RzLnJkcycpCkNhbGxzRm9yU2VydmljZSA8LSBhbGxPYmplY3RzJENhbGxzRm9yU2VydmljZQoKYGBgCgojIyBEYXRhc2V0IFN1bW1hcnkKClRoZSBDYWxscyBmb3IgU2VydmljZSBkYXRhc2V0IHRoYXQgSSB1c2VkIGluIHRoaXMgbm90ZWJvb2sgaGFzIGByIGNvbW1hKG5yb3coQ2FsbHNGb3JTZXJ2aWNlKSlgIHJlY29yZHMgY292ZXJpbmcgdGhlIHBlcmlvZApgciBmb3JtYXQobWluKENhbGxzRm9yU2VydmljZSRDYWxsRGF0ZSksICclbS8lZC8lWScpYCB0byBgciBmb3JtYXQobWF4KENhbGxzRm9yU2VydmljZSRDYWxsRGF0ZSksICclbS8lZC8lWScpYC4gRWFjaCByZWNvcmQgY29udGFpbnM6CgoqIFRoZSBkYXRlIGFuZCB0aW1lIG9mIHRoZSBjYWxsCiogVGhlIHByaW9yaXR5IG9mIHRoZSBjYWxsIChgciBwYXN0ZShzb3J0KHVuaXF1ZShDYWxsc0ZvclNlcnZpY2UkcHJpb3JpdHkpKSwgc2VwPScsJylgKQoqIEEgZGVzY3JpcHRpb24gb2YgdGhlIGNhbGwgKHdoaWNoIGFwcGVhcnMgdG8gYmUgcXVhc2ktZnJlZSB0ZXh0KQoqIExvY2F0aW9uIGluZm9ybWF0aW9uLCB3aGljaCBpbmNsdWRlcyB0aGUgZGlzdHJpY3QgYW5kIGFwcHJveGltYXRlIGFkZHJlc3MgYW5kIGNvb3JkaW5hdGVzIG9mIHRoZSBpbmNpZGVudAoqIEEgY2FsbCBpZGVudGlmaWVyCgpJIHN1YnNldHRlZCB0aGUgcmF3IGRhdGFzZXQgdG8gY292ZXIgdGhlIHNhbWUgZGF0ZSByYW5nZSBhcyB0aGUgUGFydCBJIEFycmVzdCBkYXRhc2V0IHVzZWQgaW4gdGhlIHByaW9yIG5vdGVib29rLCBhbmQgcmVtb3ZlZCBhbnkgcmVjb3JkcyB3aXRoIG1pc3NpbmcgdmFsdWVzCmZvciBjYWxsIGRhdGUvdGltZSBvciBsb2NhdGlvbiBjb29yZGluYXRlcy4KCkZvciBkZXRhaWxzIG9uIGhvdyBJIHJlYWQgaW4gdGhlIHJhdyBkYXRhc2V0IGFuZCBpbnRlZ3JhdGVkIGl0IHdpdGggdGhlIG90aGVyIGRhdGEgc291cmNlcyB1c2VkIGluIHRoZSBub3RlYm9vaywgc2VlIHRoZQpbYEJhbHRpbW9yZS1SZWFkRGF0YS5SYCBzY3JpcHRdKGh0dHBzOi8vZ2l0aHViLmNvbS9zY290dGNhbWUvcG9saWNlLWRhdGEvYmxvYi9tYXN0ZXIvQmFsdGltb3JlLVJlYWREYXRhLlIpIGluIHRoZSBHaXRIdWIgcmVwb3NpdG9yeS4KCiMjIERpc3RyaWJ1dGlvbiBvZiBDYWxscyBmb3IgU2VydmljZSBieSBQcmlvcml0eQoKVG8gZ2V0IGFuIGluaXRpYWwgZmVlbCBmb3IgdGhlIGRhdGFzZXQsIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgYmFyIGNoYXJ0IGRlcGljdGluZyB0aGUgcHJpb3JpdHkgbGV2ZWwgb2YgY2FsbHM6CgpgYGB7cn0KQ2FsbHNGb3JTZXJ2aWNlICU+JSBncm91cF9ieShwcmlvcml0eSkgJT4lIHN1bW1hcml6ZShuPW4oKSkgJT4lIG11dGF0ZShwY3Q9bi9zdW0obiksIHBjdHM9cGVyY2VudChwY3QpKSAlPiUKICBtdXRhdGUocHJpb3JpdHk9aWZlbHNlKGlzLm5hKHByaW9yaXR5KSwgJyhNaXNzaW5nKScsIHByaW9yaXR5KSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYmFyKG1hcHBpbmc9YWVzKHg9cmVvcmRlcihwcmlvcml0eSwgbiksIHk9biksIHN0YXQ9J2lkZW50aXR5JykgKyBjb29yZF9mbGlwKCkgKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IGlkZW50aXR5LCBnZW9tPSJ0ZXh0IiwgYWVzKHg9cmVvcmRlcihwcmlvcml0eSwgbiksIHk9biwgbGFiZWw9cGN0cyksIGhqdXN0ID0gLS4yNSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEsIGxpbWl0cz1jKDAsMTUwMDAwMCkpICsKICB0aGVtZV9lY29ub21pc3QoKSArCiAgbGFicyh4PSdDYWxsIFByaW9yaXR5JywgeT0nTnVtYmVyIG9mIENhbGxzJywgdGl0bGU9J0Rpc3RyaWJ1dGlvbiBvZiBDYWxscyBieSBQcmlvcml0eScsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSwKICAgICAgIGNhcHRpb249cGFzdGUwKCduPScsIGNvbW1hKG5yb3coQ2FsbHNGb3JTZXJ2aWNlKSkpKQpgYGAKU2luY2UgdGhlIGNpdHkncyBkYXRhc2V0IGRvY3VtZW50YXRpb24gZG9lcyBub3QgZGVmaW5lIHRoZSBydWxlcyB1c2VkIHRvIGFzc2lnbiB0aGVzZSBwcmlvcml0eSBsYWJlbHMgdG8gY2FsbHMsIGl0IGlzIHNvbWV3aGF0IGRpZmZpY3VsdAp0byBpbnRlcnByZXQgdGhlIGRpc3RyaWJ1dGlvbi4gIEFzIHRoZSBiYXIgY2hhcnQgaW5kaWNhdGVzLCBhIHNsaWdodCBtYWpvcml0eSBvZiBhbGwgY2FsbHMgKGFuZCBieSBmYXIgdGhlIGxhcmdlc3QgY2F0ZWdvcnkpIGlzCiJNZWRpdW0iIHByaW9yaXR5LgoKV2UgY2FuIGFsc28gbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIGNhbGxzIGJ5IHByaW9yaXR5IGFjcm9zcyB0aGUgY2l0eS4gIEluIHRoaXMgY2hvcm9wbGV0aCBtYXAsIHdlIGhhdmUgdGFrZW4gdGhlIHJhdGlvIG9mCiJFbWVyZ2VuY3kiIGFuZCAiSGlnaCIgcHJpb3JpdHkgY2FsbHMsIGluIHRoZSBudW1lcmF0b3IsIHRvIHRoZSBudW1iZXIgb2YgIkxvdyIgcHJpb3JpdHkgY2FsbHMsIGluIHRoZSBkZW5vbWluYXRvcjoKCmBgYHtyLCBmaWcud2lkdGg9MTF9CkNlbnN1c0Jsb2NrU0RGICU+JQogIGdncGxvdChtYXBwaW5nPWFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCkpICsKICAgIGdlb21fcG9seWdvbihhZXMoZmlsbD1VcmdlbnRDYWxsUmF0aW8pKSArCiAgICBnZW9tX3BhdGgoY29sb3I9J2dyZXk3MCcpICsKICAgIGdlb21fcGF0aChkYXRhPUNvdW50eVNERiwgY29sb3I9J2dyZXk1MCcpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIixoaWdoID0gInN0ZWVsYmx1ZSIsIG5hLnZhbHVlPSdncmV5OTAnKSArCiAgICBjb29yZF9tYXAoKSArCiAgICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC50aXRsZS5hbGlnbj0wLjUpICsKICBsYWJzKGZpbGw9J1JhdGlvJywKICAgICAgIHRpdGxlPSdSYXRpbyBvZiAiVXJnZW50IiB0byAiTm9uLVVyZ2VudCIgQ2FsbHMgaW4gRWFjaCBCYWx0aW1vcmUgQ2Vuc3VzIFRyYWN0JywKICAgICAgIGNhcHRpb249J1VyZ2VudD1FbWVyZ2VuY3kgb3IgSGlnaCBwcmlvcml0eSwgTm9uLVVyZ2VudD1Mb3cgcHJpb3JpdHknLAogICAgICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUykpCmBgYAoKVGhlcmUgaXMgY29uc2lkZXJhYmxlIHZhcmlhdGlvbiBhY3Jvc3MgQ2Vuc3VzIHRyYWN0cyBpbiB0aGUgcmVsYXRpdmUgcHJvcG9ydGlvbiBvZiB1cmdlbnQgY2FsbHMsIHdpdGggc2V2ZXJhbCBhcmVhcyB0byB0aGUgd2VzdCBhbmQgZWFzdApvZiBkb3dudG93biwgdGhlIEJlbGFpciBhcmVhIG5vcnRoZWFzdCBvZiBkb3dudG93biwgYW5kIHRoZSB0cmFjdCB0byB0aGUgc291dGh3ZXN0IG9mIEpvaG5zIEhvcGtpbnMgVW5pdmVyc2l0eSBhbGwgaGF2aW5nIG1vcmUgdXJnZW50CnRoYW4gbm9uLXVyZ2VudCBjYWxscy4gIENvbnZlcnNlbHksIGFsb25nIHRoZSBvdXRlciBlZGdlcyBvZiB0aGUgY2l0eSwgbm9uLXVyZ2VudCBjYWxscyBhcmUgcmVsYXRpdmVseSBtb3JlIGZyZXF1ZW50LgoKIyMgV2hlbiBkbyBDYWxscyBmb3IgU2VydmljZSBPY2N1cj8KCkl0J3MgYWxzbyBpbnRlcmVzdGluZyB0byBleGFtaW5lIGNvdW50cyBvZiBjYWxscyBieSBkYXkgb2YgdGhlIHllYXIgYW5kIHRpbWUgb2YgZGF5LiAgVG8gZW5hYmxlIGNvbXBhcmlzb24gdG8gdGhlIGhlYXQgbWFwIG9mIGFycmVzdHMgaW4gdGhlIHByaW9yIGFuYWx5c2lzLAp0aGlzIGhlYXQgbWFwIG9mIGNhbGxzIGNvdmVycyBjYWxlbmRhciB5ZWFyIDIwMTU6CgpgYGB7ciwgZmlnLndpZHRoPTExfQpDYWxsc0ZvclNlcnZpY2UgJT4lIGZpbHRlcih5ZWFyKENhbGxEYXRlKT09MjAxNSkgJT4lCiAgZ3JvdXBfYnkoQ2FsbERhdGUsIENhbGxIb3VyKSAlPiUKICBzdW1tYXJpemUobj1uKCkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKENhbGxEYXRlLCBDYWxsSG91cikgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fdGlsZShhZXMoeD1DYWxsRGF0ZSwgeT1DYWxsSG91ciwgZmlsbD1uKSwgbmEucm09VFJVRSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIixoaWdoID0gInN0ZWVsYmx1ZSIpICsgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHM9MDoyMykgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcz0nMSBtb250aHMnLCBkYXRlX2xhYmVscz0nJWItJWUnKSArCiAgdGhlbWUoYXhpcy50aWNrcy55PWVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGxhYnMoeD1OVUxMLCB5PU5VTEwsIGZpbGw9J0NhbGxzL2hvdXInLCB0aXRsZT0nQ2FsbHMgZm9yIFNlcnZpY2UgYnkgVGltZSBvZiBEYXksIGVhY2ggZGF5IGluIDIwMTUnLAogICAgICAgc3VidGl0bGU9J0JhbHRpbW9yZSBQb2xpY2UgRGVwYXJ0bWVudCcpCgpgYGAKCkFzIHdpdGggdGhlIGFycmVzdCBjb3VudCBoZWF0IG1hcCBpbiB0aGUgcHJpb3IgYW5hbHlzaXMsIHRoZSB1bnJlc3QgdGhhdCBvY2N1cnJlZCBpbiB0aGUgc2Vjb25kIGhhbGYgb2YgQXByaWwgMjAxNSBpcyBhcHBhcmVudCBpbiBhbiBpbmNyZWFzZSBpbiBjYWxscyBmb3IKc2VydmljZS4gSG93ZXZlciwgdGhlcmUgYXJlIHNvbWUgaW50ZXJlc3RpbmcgZGlmZmVyZW5jZXMgaW4gY2FsbHMgZm9yIHNlcnZpY2UgdmVyc3VzIGFycmVzdHM6CgoqIFRoZXJlIGlzIGEgZGlzdGluY3QgcGF0dGVybiB0byBlYXJseSBtb3JuaW5nIGNhbGxzIChpLmUuLCBtaWRuaWdodCAtIDM6MDAgYW0pIHRoYXQgc3VnZ2VzdHMgYSB3ZWVrbHkgKHBvc3NpYmx5IGV2ZXJ5IHdlZWtlbmQpIHJoeXRobQoqIFRoZXJlIGFyZSBkaXN0aW5jdCBwZXJpb2RzIHdpdGhvdXQgYW55IGNhbGxzIGZvciBzZXJ2aWNlIChpbmRpY2F0ZWQgYnkgcHVyZSB3aGl0ZSBsaW5lcyBpbiB0aGUgbWFwKTsgdGhlc2UgY291bGQgYmUgdGltZXMgd2hlbiB0aGUgY2l0eSdzIGRhdGEKY29sbGVjdGlvbiBwcm9jZXNzICh0aGF0IHBvcHVsYXRlcyB0aGUgb3BlbiBkYXRhc2V0KSB3YXMgb2ZmbGluZSBvciBpbnRlcnJ1cHRlZCBmb3Igc29tZSByZWFzb24KKiBUaGVyZSBhcmUgbm90aWNlYWJsZSBpbmNyZWFzZXMgaW4gY2FsbHMgb24gdGhlIG5pZ2h0IG9mIEp1bHkgNCAocXVpdGUgcG9zc2libHkgZm9yIGNpdGl6ZW5zIGNhbGxpbmcgYWJvdXQgdmlvbGF0aW9ucyBvZiB0aGUgY2l0eSdzIGJhbiBvbiBmaXJld29ya3MpIGFuZAphbHNvLCBmb3IgYW4gaG91ciBvciBzbywgb24gSGFsbG93ZWVuCgpUYWtpbmcgYSBjbG9zZXIgbG9vayBhdCBlYXJseSBtb3JuaW5nIGNhbGxzLCBhbmQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aG9zZSBjYWxscyBieSBkYXkgb2Ygd2VlaywgY29uZmlybXMgYSBzaWduaWZpY2FudCBpbmNyZWFzZSBvbiB3ZWVrZW5kczoKCmBgYHtyfQpDYWxsc0ZvclNlcnZpY2UgJT4lCiAgZmlsdGVyKENhbGxIb3VyICVpbiUgMDo0KSAlPiUKICBtdXRhdGUoZG93bj13ZGF5KENhbGxEYXRlKSwgZG93PWFzLmNoYXJhY3Rlcih3ZGF5KENhbGxEYXRlLCBsYWJlbD1UUlVFLCBhYmJyPUZBTFNFKSkpICU+JQogIGdyb3VwX2J5KGRvd24sIGRvdykgJT4lIHN1bW1hcml6ZShuPW4oKSkgJT4lIHVuZ3JvdXAoKSAlPiUgbXV0YXRlKHBjdD1uL3N1bShuKSwgcGN0cz1wZXJjZW50KHBjdCkpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2JhcihtYXBwaW5nPWFlcyh4PXJlb3JkZXIoZG93LCAtZG93biksIHk9biksIHN0YXQ9J2lkZW50aXR5JykgKyBjb29yZF9mbGlwKCkgKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IGlkZW50aXR5LCBnZW9tPSJ0ZXh0IiwgYWVzKHg9cmVvcmRlcihkb3csIC1kb3duKSwgeT1uLCBsYWJlbD1wY3RzKSwgaGp1c3QgPSAtLjI1KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSwgbGltaXRzPWMoMCw3NTAwMCkpICsKICB0aGVtZV9lY29ub21pc3QoKSArCiAgbGFicyh4PU5VTEwsIHk9J051bWJlciBvZiBDYWxscycsIHRpdGxlPSdEaXN0cmlidXRpb24gb2YgRWFybHkgTW9ybmluZyBDYWxscyBieSBEYXkgb2YgV2VlaycsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0NhbGxzIGJldHdlZW4gbWlkbmlnaHQgYW5kIDMgYW0sIEJhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSwKICAgICAgIGNhcHRpb249cGFzdGUwKCduPScsIGNvbW1hKG5yb3coQ2FsbHNGb3JTZXJ2aWNlKSkpKQpgYGAKCiMjIEZyb20gV2hlcmUgaW4gdGhlIENpdHkgZG8gQ2FsbHMgT3JpZ2luYXRlPwoKVW5saWtlIHRoZSBBcnJlc3QgZGF0YXNldCwgdGhlIGNpdHkncyBDYWxscyBmb3IgU2VydmljZSBkYXRhc2V0IGRvZXMgbm90IGNvbnRhaW4gYSB2YXJpYWJsZSBpZGVudGlmeWluZyB0aGUgbmVpZ2hib3Job29kIGFzc29jaWF0ZWQgd2l0aCB0aGUgY2FsbC4KSG93ZXZlciwgc2luY2UgZWFjaCBjYWxsIGZvciBzZXJ2aWNlIGluY2x1ZGVzIGFwcHJveGltYXRlIGNvb3JkaW5hdGVzIGZvciB0aGUgcmVsYXRlZCBpbmNpZGVudCwgd2UgYXJlIGFibGUgdG8gb3ZlcmxheSB0aGUgY2l0eSdzIG5laWdoYm9yaG9vZApzaGFwZWZpbGUgYW5kIHRoZSBDZW5zdXMgdHJhY3Qgc2hhcGVmaWxlIHRvIGdldCBhIHNlbnNlIG9mIHRoZSBnZW9ncmFwaGljIGRpc3RyaWJ1dGlvbiBvZiBjYWxscy4KCmBgYHtyLCBmaWcud2lkdGg9MTF9ClRvcENhbGxOZWlnaGJvcmhvb2RzIDwtIE5laWdoYm9yaG9vZERmICU+JQogIGZpbHRlcighaXMuaW5maW5pdGUoQ2FsbHNGb3JTZXJ2aWNlUGVyQ2FwaXRhKSkgJT4lCiAgYXJyYW5nZShkZXNjKENhbGxzRm9yU2VydmljZVBlckNhcGl0YSkpICU+JQogIGhlYWQoMTApICU+JQogIGJpbmRfY29scyh0aWJibGUoS2V5QWJicj1jKExFVFRFUlNbMToobnJvdyguKSldKSkpICU+JQogIHNlbGVjdChLZXlBYmJyLCBOYW1lLCBDYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEsIENhbGxzRm9yU2VydmljZSwgUG9wdWxhdGlvbiwgQ2VudHJvaWRMb25naXR1ZGUsIENlbnRyb2lkTGF0aXR1ZGUpCgpOZWlnaGJvcmhvb2RTREYgJT4lCiAgZ2dwbG90KG1hcHBpbmc9YWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsPUNhbGxzRm9yU2VydmljZVBlckNhcGl0YSkpICsKICAgIGdlb21fcGF0aChjb2xvcj0nZ3JleTcwJykgKwogICAgZ2VvbV9sYWJlbChkYXRhPVRvcENhbGxOZWlnaGJvcmhvb2RzLAogICAgICAgICAgICAgICBtYXBwaW5nPWFlcyh4PUNlbnRyb2lkTG9uZ2l0dWRlLCB5PUNlbnRyb2lkTGF0aXR1ZGUsIGxhYmVsPUtleUFiYnIpLAogICAgICAgICAgICAgICBpbmhlcml0LmFlcz1GQUxTRSwgc2l6ZT0yKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsaGlnaCA9ICJzdGVlbGJsdWUiLCBuYS52YWx1ZT0nZ3JleTkwJywgdHJhbnM9J2xvZycsIGJyZWFrcz1jKDIsOCwzMiw5NikpICsKICAgIGNvb3JkX21hcCgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLmtleS5oZWlnaHQ9dW5pdCgyLjUsICdsaW5lJyksIGxlZ2VuZC50aXRsZS5hbGlnbj0wLjUpICsKICBsYWJzKGZpbGw9J0NhbGxzL0NhcGl0YScsCiAgICAgICB0aXRsZT0nQ2FsbHMgZm9yIFNlcnZpY2UgcGVyIENhcGl0YSBpbiBCYWx0aW1vcmUgTmVpZ2hib3Job29kcycsCiAgICAgICBjYXB0aW9uPSdOb3RlOiBOZWlnaGJvcmhvb2Qgc2hhZGluZyBmb3IgQ2FsbHMvQ2FwaXRhIHZhbHVlIGlzIGxvZy1zY2FsZWQnLAogICAgICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUykpCmBgYApTaW1pbGFyIHRvIHRoZSBkaXN0cmlidXRpb24gb2YgYXJyZXN0cyBwZXIgY2FwaXRhIGFzIHNlZW4gaW4gdGhlIHByaW9yIGFuYWx5c2lzLCBjYWxscyBmb3Igc2VydmljZSAob24gYSBwZXIgY2FwaXRhIGJhc2lzKSBhcmUgc2lnbmlmaWNhbnRseQpoaWdoZXIgaW4gdGhlIGluZHVzdHJpYWwgYXJlYXMsIG5vdCBiZWNhdXNlIHRoZXJlIGFyZSBzaWduaWZpY2FudGx5IG1vcmUgY2FsbHMgaW4gdGhvc2UgYXJlYXMsIGJ1dCBiZWNhdXNlIHRoZXJlIGFyZSBmZXdlciByZXNpZGVudHMgbGl2aW5nIHRoZXJlLgpOb3RlIHRoYXQgd2UgdXNlIGEgbG9nIHNjYWxlIGZvciB0aGUgY29sb3IgZ3JhZGllbnQgaW4gdGhpcyBjaG9yb3BsZXRoIG1hcCwgdG8gcmVkdWNlIHRoZSBpbXBhY3Qgb2YgdGhlIG91dGxpZXJzIG9uIHRoZSBncmFkaWVudC4KClRoZSB0ZW4gbmVpZ2hib3Job29kcyB3aXRoIHRoZSBoaWdoZXN0IHBlci1jYXBpdGEgY2FsbHMgb3ZlciB0aGlzIHR3by1hbmQtYS1oYWxmIHllYXIgcGVyaW9kLCBhcmU6CgpgYGB7cn0Ka2FibGUoVG9wQ2FsbE5laWdoYm9yaG9vZHMgJT4lCiAgICAgICAgc2VsZWN0KExhYmVsPUtleUFiYnIsIE5laWdoYm9yaG9vZD1OYW1lLCBDYWxsc0ZvclNlcnZpY2UsIFBvcHVsYXRpb24sIGBDYWxscyBwZXIgQ2FwaXRhYD1DYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEpICU+JQogICAgICAgIG11dGF0ZShgQ2FsbHMgcGVyIENhcGl0YWA9Y29tbWEoYENhbGxzIHBlciBDYXBpdGFgKSwgYENhbGxzIGZvciBTZXJ2aWNlYD1jb21tYShDYWxsc0ZvclNlcnZpY2UpLCBQb3B1bGF0aW9uPWNvbW1hKFBvcHVsYXRpb24pKSAlPiUKICAgICAgICBzZWxlY3QoLUNhbGxzRm9yU2VydmljZSksIGZvcm1hdD0naHRtbCcKKSAlPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpCmBgYAoKQWdncmVnYXRpbmcgY2FsbHMgcGVyIGNhcGl0YSBieSBDZW5zdXMgdHJhY3QgaW5kaWNhdGVzIHRoYXQgY2FsbHMgcGVyIGNhcGl0YSAobGlrZSBhcnJlc3RzIHBlciBjYXBpdGEpIGFyZSBoaWdoZXN0IGluIHRoZSBkb3dudG93biBhcmVhLCBhbmQgc2xpZ2h0bHkKbGVzcyBzbyB0byB0aGUgaW1tZWRpYXRlIHdlc3Qgb2YgZG93bnRvd24gYW5kIGluIHRoZSBQdWxhc2tpIGFyZWE6CgpgYGB7ciwgZmlnLndpZHRoPTExfQpDZW5zdXNCbG9ja1NERiAlPiUKICBnZ3Bsb3QobWFwcGluZz1hZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApKSArCiAgICBnZW9tX3BvbHlnb24oYWVzKGZpbGw9Q2FsbHNGb3JTZXJ2aWNlUGVyQ2FwaXRhKSkgKwogICAgZ2VvbV9wYXRoKGNvbG9yPSdncmV5NzAnKSArCiAgICBnZW9tX3BhdGgoZGF0YT1Db3VudHlTREYsIGNvbG9yPSdncmV5NTAnKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsaGlnaCA9ICJzdGVlbGJsdWUiLCBuYS52YWx1ZT0nZ3JleTkwJywgbGFiZWxzPWNvbW1hLCB0cmFucz0nbG9nJywgYnJlYWtzPWMoMiw0LDgsMTYsMzIpKSArCiAgICBjb29yZF9tYXAoKSArCiAgICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5rZXkuaGVpZ2h0PXVuaXQoMi41LCAnbGluZScpLCBsZWdlbmQudGl0bGUuYWxpZ249MC41KSArCiAgbGFicyhmaWxsPSdDYWxscy9DYXBpdGEnLAogICAgICAgdGl0bGU9J0NhbGxzIGZvciBTZXJ2aWNlIHBlciBDYXBpdGEgaW4gRWFjaCBCYWx0aW1vcmUgQ2Vuc3VzIFRyYWN0JywKICAgICAgIGNhcHRpb249J0NlbnN1cyBUcmFjdCBwb3B1bGF0aW9uIGlzIGZyb20gMjAxNSA1LXllYXIgQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSAoQUNTKSBlc3RpbWF0ZXMnLAogICAgICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUykpCmBgYAoKIyMgRG8gRGVtb2dyYXBoaWMgYW5kIEVjb25vbWljIEZhY3RvcnMgQ29ycmVsYXRlIHdpdGggQ2FsbCBWb2x1bWU/CgpDaXR5d2lkZSwgb3ZlciB0aGUgdGltZSBwZXJpb2QgY292ZXJlZCBieSB0aGlzIGRhdGFzZXQsIHRoZXJlIHdlcmUKYHIgY29tbWEoc3VtKENlbnN1c0Jsb2NrRGYkQ2FsbHNGb3JTZXJ2aWNlKS9zdW0oQ2Vuc3VzQmxvY2tEZiRQb3B1bGF0aW9uKSwgbnNtYWxsPTIsIGRpZ2l0cz0yKWAgY2FsbHMgZm9yIHNlcnZpY2UgcGVyIGNhcGl0YS4gSXQgaXMgZmFpcgp0byBhc2sgd2hldGhlciB0aGlzIGNpdHl3aWRlIGF2ZXJhZ2UgaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYWNyb3NzIGEgc3BlY3RydW0gb2YgZGVtb2dyYXBoaWMgYW5kIGVjb25vbWljIGNoYXJhY3RlcmlzdGljcyBieSBhcmVhLgpJbiB0aGUgZm9sbG93aW5nIHNjYXR0ZXJwbG90cywgd2Ugc2VlIHRoYXQgdGhlIHBlcmNlbnRhZ2Ugb2YgQ2Vuc3VzIHRyYWN0IHBvcHVsYXRpb24gdGhhdCBpcyB3aGl0ZSwgdGhlIG1lZGlhbiBhZ2UsCmFuZCB0aGUgdHJhY3QncyBtZWRpYW4gaG91c2Vob2xkIGluY29tZQphcmUgYWxsIG5lZ2F0aXZlbHkgY29ycmVsYXRlZCB3aXRoIHNlcnZpY2UgY2FsbHMgcGVyIGNhcGl0YSAodGhvdWdoIHRoZSBzdHJlbmd0aCBvZiB0aGUgY29ycmVsYXRpb24gdmFyaWVzIHNvbWV3aGF0IGFjcm9zcyB0aGVzZQpleHBsYW5hdG9yeSB2YXJpYWJsZXMpLgoKYGBge3IsIGZpZy53aWR0aD0xMH0KZ2dwbG90KGRhdGE9Q2Vuc3VzQmxvY2tEZiAlPiUgZmlsdGVyKENhbGxzRm9yU2VydmljZVBlckNhcGl0YSA8IDMwKSkgKwogIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD1QZXJjZW50V2hpdGUsIHk9Q2FsbHNGb3JTZXJ2aWNlUGVyQ2FwaXRhKSwgbmEucm09VFJVRSkgKwogIGdlb21fc21vb3RoKG1hcHBpbmc9YWVzKHg9UGVyY2VudFdoaXRlLCB5PUNhbGxzRm9yU2VydmljZVBlckNhcGl0YSksIG5hLnJtPVRSVUUsCiAgICAgICAgICAgICAgbWV0aG9kPSdsbScsIGNvbG9yPSdyZWQnLCBzaXplPS4yNSwgc2U9RkFMU0UpICsKICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9cGVyY2VudCkgKwogIGxhYnModGl0bGU9J0NhbGxzIGZvciBTZXJ2aWNlIHBlciBDYXBpdGEgYW5kIFJhY2UsIGJ5IENlbnN1cyBUcmFjdCcsCiAgICAgICB4PSclIG9mIFBvcHVsYXRpb24gdGhhdCBpcyBXaGl0ZScsIHk9J0NhbGxzIGZvciBTZXJ2aWNlIHBlciBDYXBpdGEnLAogICAgICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUyksCiAgICAgICBjYXB0aW9uPSdDZW5zdXMgVHJhY3QgcG9wdWxhdGlvbiB2YWx1ZXMgYXJlIGZyb20gMjAxNSA1LXllYXIgQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSAoQUNTKSBlc3RpbWF0ZXMnKSArCiAgdGhlbWVfZWNvbm9taXN0KCkKCmBgYApgYGB7ciwgZmlnLndpZHRoPTEwfQpnZ3Bsb3QoZGF0YT1DZW5zdXNCbG9ja0RmICU+JSBmaWx0ZXIoQ2FsbHNGb3JTZXJ2aWNlUGVyQ2FwaXRhIDwgMzApKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PU1lZGlhbkFnZSwgeT1DYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEpLCBuYS5ybT1UUlVFKSArCiAgZ2VvbV9zbW9vdGgobWFwcGluZz1hZXMoeD1NZWRpYW5BZ2UsIHk9Q2FsbHNGb3JTZXJ2aWNlUGVyQ2FwaXRhKSwgbmEucm09VFJVRSwKICAgICAgICAgICAgICBtZXRob2Q9J2xtJywgY29sb3I9J3JlZCcsIHNpemU9LjI1LCBzZT1GQUxTRSkgKwogIGxhYnModGl0bGU9J0NhbGxzIGZvciBTZXJ2aWNlIHBlciBDYXBpdGEgYW5kIEFnZSwgYnkgQ2Vuc3VzIFRyYWN0JywKICAgICAgIHg9J01lZGlhbiBBZ2UnLCB5PSdDYWxscyBmb3IgU2VydmljZSBwZXIgQ2FwaXRhJywKICAgICAgIHN1YnRpdGxlPXBhc3RlMCgnQmFsdGltb3JlIENpdHksICcsIG1pbkRhdGVTLCAnIC0gJywgbWF4RGF0ZVMpLAogICAgICAgY2FwdGlvbj0nQ2Vuc3VzIFRyYWN0IHBvcHVsYXRpb24gdmFsdWVzIGFyZSBmcm9tIDIwMTUgNS15ZWFyIEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgKEFDUykgZXN0aW1hdGVzJykgKwogIHRoZW1lX2Vjb25vbWlzdCgpCgpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmdncGxvdChkYXRhPUNlbnN1c0Jsb2NrRGYgJT4lIGZpbHRlcihDYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEgPCAzMCkpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9TWVkaWFuSG91c2Vob2xkSW5jb21lLCB5PUNhbGxzRm9yU2VydmljZVBlckNhcGl0YSksIG5hLnJtPVRSVUUpICsKICBnZW9tX3Ntb290aChtYXBwaW5nPWFlcyh4PU1lZGlhbkhvdXNlaG9sZEluY29tZSwgeT1DYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEpLCBuYS5ybT1UUlVFLAogICAgICAgICAgICAgIG1ldGhvZD0nbG0nLCBmb3JtdWxhPXkgfiBsb2coeCksIGNvbG9yPSdyZWQnLCBzaXplPS4yNSwgc2U9RkFMU0UpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPWRvbGxhcikgKwogIGxhYnModGl0bGU9J0NhbGxzIGZvciBTZXJ2aWNlIHBlciBDYXBpdGEgYW5kIEluY29tZSwgYnkgQ2Vuc3VzIFRyYWN0JywKICAgICAgIHg9J01lZGlhbiBBbm51YWwgSG91c2Vob2xkIEluY29tZScsIHk9J0NhbGxzIGZvciBTZXJ2aWNlIHBlciBDYXBpdGEnLAogICAgICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUyksCiAgICAgICBjYXB0aW9uPSdDZW5zdXMgVHJhY3QgcG9wdWxhdGlvbiBhbmQgaW5jb21lIHZhbHVlcyBhcmUgZnJvbSAyMDE1IDUteWVhciBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IChBQ1MpIGVzdGltYXRlcycpICsKICB0aGVtZV9lY29ub21pc3QoKQoKYGBgCgpUaGVyZSBpcyBhbHNvIGEgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHlwZSBvZiBsYW5kIHVzZSwgYXMgW2NsYXNzaWZpZWRdKGh0dHA6Ly9wbGFubmluZy5tYXJ5bGFuZC5nb3YvT3VyUHJvZHVjdHMvZG93bmxvYWRGaWxlcy5zaHRtbCkKYnkgdGhlIHN0YXRlIG9mIE1hcnlsYW5kIChpbiAyMDEwKSwgd2l0aCAibW9yZSByZXNpZGVudGlhbCIKQ2Vuc3VzIHRyYWN0cyBleGhpYml0aW5nIGEgcmVsYXRpdmVseSBsb3dlciBmcmVxdWVuY3kgb2YgY2FsbGluZyBmb3Igc2VydmljZToKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmdncGxvdChkYXRhPUNlbnN1c0Jsb2NrRGYgJT4lIGZpbHRlcihDYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEgPCAzMCkpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9UmVzaWRlbnRpYWxQZXJjZW50YWdlLCB5PUNhbGxzRm9yU2VydmljZVBlckNhcGl0YSksIG5hLnJtPVRSVUUpICsKICBnZW9tX3Ntb290aChtYXBwaW5nPWFlcyh4PVJlc2lkZW50aWFsUGVyY2VudGFnZSwgeT1DYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEpLCBuYS5ybT1UUlVFLAogICAgICAgICAgICAgIG1ldGhvZD0nbG0nLCBjb2xvcj0ncmVkJywgc2l6ZT0uMjUsIHNlPUZBTFNFKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1wZXJjZW50KSArCiAgbGFicyh0aXRsZT0nQ2FsbHMgZm9yIFNlcnZpY2UgcGVyIENhcGl0YSBhbmQgUmVzaWRlbnRpYWwgTGFuZCBVc2UsIGJ5IENlbnN1cyBUcmFjdCcsCiAgICAgICB4PSclIG9mIENlbnN1cyB0cmFjdCBjbGFzc2lmaWVkIGFzIHJlc2lkZW50aWFsJywgeT0nQ2FsbHMgZm9yIFNlcnZpY2UgcGVyIENhcGl0YScsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSwKICAgICAgIGNhcHRpb249J0NlbnN1cyBUcmFjdCBwb3B1bGF0aW9uIHZhbHVlcyBhcmUgZnJvbSAyMDE1IDUteWVhciBBQ1MgZXN0aW1hdGVzLCBsYW5kIHVzZSBjbGFzc2lmaWNhdGlvbiBmcm9tIHN0YXRlIG9mIE1hcnlsYW5kJykgKwogIHRoZW1lX2Vjb25vbWlzdCgpCgpgYGAKCkl0IGlzIHdvcnRoIG5vdGluZyB0aGF0IHRoZXJlIGlzIGludHVpdGl2ZWx5IGEgaGlnaCBkZWdyZWUgb2YgY29ycmVsYXRpb24gX2Ftb25nXyB0aGVzZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMuICBUbyB0ZWFzZSBvdXQgdGhlCnBhcnRpYWwgZWZmZWN0cywgd2UgcnVuCmEgcmVncmVzc2lvbiBtb2RlbCBvZiBwZXItY2FwaXRhIENhbGxzIGZvciBTZXJ2aWNlIG9uIHRoZXNlIGZvdXIgdmFyaWFibGVzICh0YWtpbmcgdGhlIGxvZyBvZiBob3VzZWhvbGQgaW5jb21lIHRvIGFjY291bnQgZm9yIHRoZQpleHBvbmVudGlhbCBlZmZlY3Qgb2YgdGhpcyB2YXJpYWJsZSBkZXRlY3RlZCBpbiB0aGUgdmlzdWFsaXphdGlvbiBhYm92ZSk6CgpgYGB7cn0KbW9kZWwgPC0gbG0oZGF0YT1DZW5zdXNCbG9ja0RmLAogICBmb3JtdWxhPUNhbGxzRm9yU2VydmljZVBlckNhcGl0YSB+IGxvZyhNZWRpYW5Ib3VzZWhvbGRJbmNvbWUpICsgUmVzaWRlbnRpYWxQZXJjZW50YWdlICsgTWVkaWFuQWdlICsgUGVyY2VudFdoaXRlKQoKbW9kZWwgJT4lCiAgdGlkeSgpICU+JQogIHNlbGVjdCgtc3RhdGlzdGljKSAlPiUKICBtdXRhdGUodGVybT1jYXNlX3doZW4oCiAgICBncmVwbCh4PXRlcm0sIHBhdHRlcm49J0luY29tZScpIH4gJ2xvZyhNZWRpYW4gSEggSW5jb21lKScsCiAgICBncmVwbCh4PXRlcm0sIHBhdHRlcm49J1Jlc2lkZW50aWFsJykgfiAnJSBSZXNpZGVudGlhbCcsCiAgICBncmVwbCh4PXRlcm0sIHBhdHRlcm49J0FnZScpIH4gJ01lZGlhbiBBZ2UnLAogICAgZ3JlcGwoeD10ZXJtLCBwYXR0ZXJuPSdXaGl0ZScpIH4gJyUgV2hpdGUnLAogICAgVFJVRSB+ICcoSW50ZXJjZXB0KScKICApKSAlPiUKa2FibGUoZm9ybWF0PSdodG1sJywgZGlnaXRzPWMoMCwgMiwgMiwgMyksIGNvbC5uYW1lcz1jKCdWYXJpYWJsZScsICdFc3RpbWF0ZScsICdTdGQgRXJyb3InLCAncCg+fHR8KScpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkKYGBgCkluIHRoaXMgbW9kZWwsIGJvdGggaW5jb21lIGFuZCByZXNpZGVudGlhbCBsYW5kIHVzZSBwcm9wb3J0aW9uIGFyZSBuZWdhdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCBjYWxscyBwZXIgY2FwaXRhLAphbmQgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuICBBZ2UgYW5kIHJhY2UgYXJlIG5vdCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LCBzdWdnZXN0aW5nIHRoYXQgdGhlIGVmZmVjdHMgb2YgdGhlc2UgdmFyaWFibGVzCiJ3YXNoIG91dCIgd2hlbiBpbmNvbWUgYW5kIGxhbmQgdXNlIGFyZSBpbmNsdWRlZC4KClRoZSBvdmVyYWxsIGV4cGxhbmF0b3J5IHBvd2VyIG9mIHRoaXMgbW9kZWwgaXMgbm90IHZlcnkgaGlnaC0tYW4gUl4yXiBvZgpgciBjb21tYShzdW1tYXJ5KG1vZGVsKSRyLnNxdWFyZWQsIGRpZ2l0cz0zLCBuc21hbGw9MylgIHN1Z2dlc3RzIHRoYXQgb3VyIG1vZGVsIG9ubHkgZXhwbGFpbnMKYHIgcGVyY2VudChzdW1tYXJ5KG1vZGVsKSRyLnNxdWFyZWQpYCBvciB0aGUgdmFyaWFuY2UgaW4gcGVyLWNhcGl0YSBjYWxscyBmb3Igc2VydmljZSBhY3Jvc3MgQ2Vuc3VzIHRyYWN0cy4gIFN0aWxsLAp0aGUgbW9kZWwgcHJvdmlkZXMgc29tZSBldmlkZW5jZSB0aGF0IGRpZmZlcmVuY2VzIGluIHBlci1jYXBpdGEgY2FsbCB2b2x1bWUgYWNyb3NzIHRoZSBjaXR5IGRvIHZhcnkgYnkgaW5jb21lIGFuZCByZXNpZGVudGlhbApsYW5kIHVzZSBwcm9wb3J0aW9uLApidXQgbm90IGRpcmVjdGx5IGJ5IHRoZSByYWNpYWwgb3IgYWdlIG1ha2V1cCBvZiB0aGUgcG9wdWxhdGlvbi4KCiMjIFRoZSBSZWxhdGlvbnNoaXAgQmV0d2VlbiBDYWxscyBmb3IgU2VydmljZSBhbmQgQXJyZXN0cwoKQXMgZGlzY3Vzc2VkIGluIHRoZSBpbnRyb2R1Y3Rpb24gYWJvdmUsIHdlIGNhbiBwb3RlbnRpYWxseSBsZWFybiBzb21ldGhpbmcgaW50ZXJlc3RpbmcgYWJvdXQgY3JpbWUgYW5kIHBvbGljaW5nIGluIEJhbHRpbW9yZSBieQpleGFtaW5pbmcgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGFycmVzdHMgYW5kIGNhbGxzIGZvciBzZXJ2aWNlLiAgV2UgZG8gc28gaGVyZSBieSBjb21wdXRpbmcgdGhlIHJhdGlvIG9mIGFycmVzdHMgdG8gY2FsbHMgYW5kCmxvb2tpbmcgYXQgaG93IHRoaXMgcmF0aW8gdmFyaWVzIGFjcm9zcyBDZW5zdXMgdHJhY3RzIGluIHRoZSBjaXR5LgoKQmV0d2VlbiBgciBmb3JtYXQobWluRGF0ZSwgJyVCICVkLCAlWScpYCBhbmQgYHIgZm9ybWF0KG1heERhdGUsICclQiAlZCwgJVknKWAsIHRoZSBjaXR5LXdpZGUgcmF0aW8gb2YgYXJyZXN0cyB3YXMKYHIgY29tbWEoc3VtKENlbnN1c0Jsb2NrRGYkQXJyZXN0cykvc3VtKENlbnN1c0Jsb2NrRGYkQ2FsbHNGb3JTZXJ2aWNlKSwgZGlnaXRzPTIpYCwgb3IgCmByIGNvbW1hKDEwMCpzdW0oQ2Vuc3VzQmxvY2tEZiRBcnJlc3RzKS9zdW0oQ2Vuc3VzQmxvY2tEZiRDYWxsc0ZvclNlcnZpY2UpLCBkaWdpdHM9MilgIGFycmVzdHMgcGVyIDEwMCBjYWxscyBmb3Igc2VydmljZS4gVGhpcyByYXRpbwp2YXJpZXMgc29tZXdoYXQgYWNyb3NzIHRoZSBjaXR5LCBob3dldmVyOgoKYGBge3IsIGZpZy53aWR0aD0xMX0KQ2Vuc3VzQmxvY2tTREYgJT4lCiAgbXV0YXRlKEFycmVzdHNQZXIxMDBDYWxsc0ZvclNlcnZpY2U9MTAwKkFycmVzdHNQZXJDYWxsRm9yU2VydmljZSkgJT4lCiAgZ2dwbG90KG1hcHBpbmc9YWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsPUFycmVzdHNQZXIxMDBDYWxsc0ZvclNlcnZpY2UpKSArCiAgICBnZW9tX3BhdGgoY29sb3I9J2dyZXk3MCcpICsKICAgIGdlb21fcGF0aChkYXRhPUNvdW50eVNERiwgY29sb3I9J2dyZXk1MCcpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIixoaWdoID0gInN0ZWVsYmx1ZSIsIG5hLnZhbHVlPSdncmV5OTAnKSArCiAgICBjb29yZF9tYXAoKSArCiAgICB0aGVtZV92b2lkKCkgKwogIHRoZW1lKGxlZ2VuZC5rZXkuaGVpZ2h0PXVuaXQoMi41LCAnbGluZScpLCBsZWdlbmQudGl0bGUuYWxpZ249MC41KSArCiAgbGFicyhmaWxsPSdBcnJlc3RzLzEwMCBDRlMnLAogICAgICAgdGl0bGU9J0FycmVzdHMgcGVyIENhbGwgZm9yIFNlcnZpY2UgaW4gRWFjaCBCYWx0aW1vcmUgQ2Vuc3VzIFRyYWN0JywKICAgICAgIGNhcHRpb249J0NlbnN1cyBUcmFjdCBwb3B1bGF0aW9uIGlzIGZyb20gMjAxNSA1LXllYXIgQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSAoQUNTKSBlc3RpbWF0ZXMnLAogICAgICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUykpCmBgYAoKV2UgY2FuIGFsc28gZXhhbWluZSB0aGUgdmFyaWF0aW9uIGJ5IG5laWdoYm9yaG9vZCwgd2l0aCB0aGUgdG9wIGFuZCBib3R0b20gZml2ZSBuZWlnaGJvcmhvb2RzIGJlaW5nIGFzIGZvbGxvd3M6CgpgYGB7cn0KbmRmIDwtIGJpbmRfcm93cygKICBOZWlnaGJvcmhvb2REZiAlPiUgZmlsdGVyKFBvcHVsYXRpb24gIT0gMCkgJT4lIHRvcF9uKDUsIEFycmVzdHNQZXJDYWxsRm9yU2VydmljZSksCiAgTmVpZ2hib3Job29kRGYgJT4lIGZpbHRlcihQb3B1bGF0aW9uICE9IDApICU+JSB0b3BfbigtNSwgQXJyZXN0c1BlckNhbGxGb3JTZXJ2aWNlKQopICU+JSBzZWxlY3QoTmVpZ2hib3Job29kPU5hbWUsIEFycmVzdHNQZXJDYWxsRm9yU2VydmljZSwgQ2FsbHNGb3JTZXJ2aWNlUGVyQ2FwaXRhLCBBcnJlc3RzUGVyQ2FwaXRhLCBQb3B1bGF0aW9uKSAlPiUKICBtdXRhdGUoQXJyZXN0c1BlckNhbGxGb3JTZXJ2aWNlPTEwMCpBcnJlc3RzUGVyQ2FsbEZvclNlcnZpY2UpICU+JQogIGFycmFuZ2UoZGVzYyhBcnJlc3RzUGVyQ2FsbEZvclNlcnZpY2UpKQprYWJsZShuZGYgJT4lCiAgICAgICAgbXV0YXRlKGBDYWxscyBwZXIgQ2FwaXRhYD1jb21tYShDYWxsc0ZvclNlcnZpY2VQZXJDYXBpdGEsIGRpZ2l0cz0yLCBuc21hbGw9MiksCiAgICAgICAgICAgICAgIGBBcnJlc3RzIHBlciBDYXBpdGFgPWNvbW1hKEFycmVzdHNQZXJDYXBpdGEsIGRpZ2l0cz0yLCBuc21hbGw9MiksCiAgICAgICAgICAgICAgIGBBcnJlc3RzIHBlciAxMDAgQ0ZTYD1jb21tYShBcnJlc3RzUGVyQ2FsbEZvclNlcnZpY2UsIGRpZ2l0cz0yLCBuc21hbGw9MiksCiAgICAgICAgICAgICAgIFBvcHVsYXRpb249Y29tbWEoUG9wdWxhdGlvbiwgZGlnaXRzPTApKSAlPiUKICAgICAgICBzZWxlY3QoTmVpZ2hib3Job29kLAogICAgICAgICAgICAgICBgQXJyZXN0cyBwZXIgMTAwIENGU2AsCiAgICAgICAgICAgICAgIGBBcnJlc3RzIHBlciBDYXBpdGFgLAogICAgICAgICAgICAgICBgQ2FsbHMgcGVyIENhcGl0YWAsCiAgICAgICAgICAgICAgIFBvcHVsYXRpb24pLCBmb3JtYXQ9J2h0bWwnCikgJT4lIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIpKQpgYGAKCkJ5IHJ1bm5pbmcgYSBzaW1pbGFyIHJlZ3Jlc3Npb24gdG8gdGhlIG9uZSBhYm92ZSwgYnV0IHdpdGggQXJyZXN0cyBwZXIgMTAwIENhbGxzIEZvciBTZXJ2aWNlIGFzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgdGhpcyB0aW1lLCB3ZQpjYW4gZXhhbWluZSB0aGUgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSByYXRpbyBhbmQga2V5IENlbnN1cyB0cmFjdCBjaGFyYWN0ZXJpc3RpY3MuCgpgYGB7cn0KbW9kZWwgPC0gbG0oZGF0YT1DZW5zdXNCbG9ja0RmICU+JSAgbXV0YXRlKEFycmVzdHNQZXJDYWxsRm9yU2VydmljZT0xMDAqQXJyZXN0c1BlckNhbGxGb3JTZXJ2aWNlKSwKICAgZm9ybXVsYT1BcnJlc3RzUGVyQ2FsbEZvclNlcnZpY2UgfiBsb2coTWVkaWFuSG91c2Vob2xkSW5jb21lKSArIFJlc2lkZW50aWFsUGVyY2VudGFnZSArIE1lZGlhbkFnZSArIFBlcmNlbnRXaGl0ZSkKCm1vZGVsICU+JQogIHRpZHkoKSAlPiUKICBzZWxlY3QoLXN0YXRpc3RpYykgJT4lCiAgbXV0YXRlKHRlcm09Y2FzZV93aGVuKAogICAgZ3JlcGwoeD10ZXJtLCBwYXR0ZXJuPSdJbmNvbWUnKSB+ICdsb2coTWVkaWFuIEhIIEluY29tZSknLAogICAgZ3JlcGwoeD10ZXJtLCBwYXR0ZXJuPSdSZXNpZGVudGlhbCcpIH4gJyUgUmVzaWRlbnRpYWwnLAogICAgZ3JlcGwoeD10ZXJtLCBwYXR0ZXJuPSdBZ2UnKSB+ICdNZWRpYW4gQWdlJywKICAgIGdyZXBsKHg9dGVybSwgcGF0dGVybj0nV2hpdGUnKSB+ICclIFdoaXRlJywKICAgIFRSVUUgfiAnKEludGVyY2VwdCknCiAgKSkgJT4lCmthYmxlKGZvcm1hdD0naHRtbCcsIGRpZ2l0cz1jKDAsIDMsIDMsIDMpLCBjb2wubmFtZXM9YygnVmFyaWFibGUnLCAnRXN0aW1hdGUnLCAnU3RkIEVycm9yJywgJ3AoPnx0fCknKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpCmBgYAoKTm90ZSB0aGF0IHRoZSBwcm9wb3J0aW9uIG9mIHJlc2lkZW50aWFsIGxhbmQgdXNlIGlzIGEgc2lnbmlmaWNhbnQgZXhwbGFuYXRvcnkgdmFyaWFibGUgaGVyZSwgYXMgaXQgd2FzIGluIHRoZSByZWdyZXNzaW9uIG9uCmNhbGxzIHBlciBjYXBpdGEuICBIb3dldmVyLCB0aGUgc2lnbiBpcyByZXZlcnNlZCwgc3VnZ2VzdGluZyB0aGF0IGNhbGxzIGZyb20gd2l0aGluIENlbnN1cyB0cmFjdHMgdGhhdCBoYXZlIGEgaGlnaGVyIHByb3BvcnRpb24gb2YKcmVzaWRlbnRpYWwgbGFuZCB1c2UgdGVuZCB0byBoYXZlIGEgaGlnaGVyIHJhdGlvIG9mIGFycmVzdHMgcGVyIGNhbGwsIGFsbCBlbHNlIGJlaW5nIGVxdWFsLiBBZGRpdGlvbmFsbHksIGFzIHRoZSBwZXJjZW50YWdlIG9mIGEgdHJhY3QncyBwb3B1bGF0aW9uIHRoYXQKaXMgd2hpdGUgaW5jcmVhc2VzLCB0aGUgcmF0aW8gb2YgYXJyZXN0cyB0byBjYWxscyBpbmNyZWFzZXMuIE5vdGUgdGhhdCBuZWl0aGVyIChsb2cgb2YpIG1lZGlhbiBob3VzZWhvbGQgaW5jb21lCm5vciBtZWRpYW4gYWdlIGlzIGEgc2lnbmlmaWNhbnQgZXhwbGFuYXRvcnkgdmFyaWFibGUgaW4gdGhpcyByZWdyZXNzaW9uLgoKVGhlIHJlbGF0aW9uc2hpcCBhbW9uZyB0aGVzZSB0aHJlZSB2YXJpYWJsZXMgaXMgbm90IHF1aXRlIGFzIGFwcGFyZW50IG9uIGEgc2NhdHRlcnBsb3QgdmlzdWFsaXphdGlvbiwgYnV0IHN0aWxsIG5vdGljZWFibGUsIHdpdGgKbGFyZ2VyIGRvdHMgKGluZGljYXRpbmcgbW9yZSBhcnJlc3RzIHBlciBjYWxsKSBpbiBib3RoIGRpbWVuc2lvbnMgYXdheSBmcm9tIHRoZSBvcmlnaW4uCgpgYGB7ciwgZmlnLndpZHRoPTEwfQpnZ3Bsb3QoZGF0YT1DZW5zdXNCbG9ja0RmICU+JSBtdXRhdGUoQXJyZXN0c1BlckNhbGxGb3JTZXJ2aWNlPTEwMCpBcnJlc3RzUGVyQ2FsbEZvclNlcnZpY2UpKSArCiAgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PVJlc2lkZW50aWFsUGVyY2VudGFnZSwgeT1QZXJjZW50V2hpdGUsIHNpemU9QXJyZXN0c1BlckNhbGxGb3JTZXJ2aWNlKSwgbmEucm09VFJVRSkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9cGVyY2VudCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9cGVyY2VudCkgKwogIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZT1jKDAsIDYpKSArCiAgbGFicyh0aXRsZT0nUmFjZSwgTGFuZCBVc2UsIGFuZCBBcnJlc3QtdG8tQ2FsbHMgUmF0aW8nLAogICAgICAgeD0nJSBvZiBDZW5zdXMgdHJhY3QgY2xhc3NpZmllZCBhcyByZXNpZGVudGlhbCcsIHk9JyUgb2YgUG9wdWxhdGlvbiB0aGF0IGlzIFdoaXRlJywKICAgICAgIHNpemU9J0FycmVzdHMgLyAxMDAgQ0ZTOicsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSkgKwogIHRoZW1lX2Vjb25vbWlzdCgpCgpgYGAKRmluYWxseSwgd2UgZXhhbWluZSB3aGV0aGVyIGEgaGlnaGVyIHJhdGlvIG9mICJ1cmdlbnQiIGNhbGxzIGluIGEgQ2Vuc3VzIHRyYWN0IGNvcnJlbGF0ZXMgd2l0aCBhIGhpZ2hlciByYXRpbyBvZiBhcnJlc3RzIHBlciAxMDAKY2FsbHMuIE9uZSBtaWdodCBleHBlY3Qgc3VjaCBhIHJlbGF0aW9uc2hpcCwgYmFzZWQgb24gdGhlIHJlYXNvbmluZyB0aGF0IG1vcmUgdXJnZW50IGNhbGxzIGZvciBzZXJ2aWNlIGNvdWxkIGJlIGZvciBpbmNpZGVudHMKKHN1Y2ggYXMgcm9iYmVyaWVzLCBidXJnbGFyaWVzLCBzaG9vdGluZ3MsIGV0Yy4pIHRoYXQgdGVuZCB0byBpbnZvbHZlIGNyaW1lcyB0aGF0LCBpbiB0dXJuLCBsZWFkIHRvIGFycmVzdC4gVGhlIGRhdGEsIGhvd2V2ZXIsIHRlbGwKYSBxdWl0ZSBkaWZmZXJlbnQgc3Rvcnk6CmBgYHtyLCBmaWcud2lkdGg9MTB9CmdncGxvdChkYXRhPUNlbnN1c0Jsb2NrRGYgJT4lIG11dGF0ZShBcnJlc3RzUGVyQ2FsbEZvclNlcnZpY2U9MTAwKkFycmVzdHNQZXJDYWxsRm9yU2VydmljZSkpICsKICBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9VXJnZW50Q2FsbFJhdGlvLCB5PUFycmVzdHNQZXJDYWxsRm9yU2VydmljZSksIG5hLnJtPVRSVUUpICsKICBsYWJzKHRpdGxlPSdBcnJlc3QtdG8tQ2FsbHMgUmF0aW8gYW5kIENhbGwgVXJnZW5jeSwgYnkgQ2Vuc3VzIFRyYWN0JywKICAgICAgIHg9J1JhdGlvIG9mICJVcmdlbnQiIENhbGxzIHRvICJOb24tVXJnZW50IiBDYWxscycsIHk9J0FycmVzdHMgcGVyIDEwMCBDYWxscyBmb3IgU2VydmljZScsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSwKICAgICAgIGNhcHRpb249J1VyZ2VudD1FbWVyZ2VuY3kgb3IgSGlnaCBwcmlvcml0eSwgTm9uLVVyZ2VudD1Mb3cgcHJpb3JpdHknKSArCiAgdGhlbWVfZWNvbm9taXN0KCkKCmBgYApBIFBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQgb2YgYHIgY29tbWEoY29yKENlbnN1c0Jsb2NrRGYkQXJyZXN0c1BlckNhbGxGb3JTZXJ2aWNlLCBDZW5zdXNCbG9ja0RmJFVyZ2VudENhbGxSYXRpbyksIGRpZ2l0cz0zKWAKaGVyZSBpbmRpY2F0ZXMgYSB3ZWFrIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMuIE9uZSBwb3NzaWJsZSBleHBsYW5hdGlvbiBtaWdodCBiZSBmb3VuZCBpbiBmdXJ0aGVyIGV4YW1pbmF0aW9uIG9mIHRoZSBuYXR1cmUKb2YgInVyZ2VudCIgY2FsbHMuIFRoZSBmaXZlIG1vc3QgZnJlcXVlbnRseS1vY2N1cnJpbmcgY2FsbCBkZXNjcmlwdGlvbnMsIGZvciAidXJnZW50IiBjYWxscywgd2VyZToKCmBgYHtyIHJlc3VsdHM9J2FzaXMnfQpzZGYgPC0gQ2FsbHNGb3JTZXJ2aWNlICU+JSBmaWx0ZXIocHJpb3JpdHkgJWluJSBjKCdFbWVyZ2VuY3knLCAnSGlnaCcpKSAlPiUKICBncm91cF9ieShkZXNjcmlwdGlvbikgJT4lIHN1bW1hcml6ZShuPW4oKSkgJT4lIHRvcF9uKDUsIG4pICU+JSBhcnJhbmdlKGRlc2MobikpCndyaXRlTGluZXMocGFzdGUoJyogJywgcGFzdGUoc2RmJGRlc2NyaXB0aW9uLCBjb21tYShzZGYkbiksIHNlcD0nOiAnKSkpCmBgYApJbmRlZWQsIG1hbnkgZW1lcmdlbmN5IGFuZCBoaWdoLXByaW9yaXR5IGNhbGxzIHdpdGggdGhlc2UgZGVzY3JpcHRpb25zIHdvdWxkIG5vdCBuZWNlc3NhcmlseSBsZWFkIHRvIGFuIGFycmVzdC4gVGh1cywgaXQgaXMgbm90IGVudGlyZWx5CnN1cnByaXNpbmcgdG8gZmluZCBhIHdlYWsgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGNhbGwgdXJnZW5jeSByYXRpbyBhbmQgYW5kIHRoZSBhcnJlc3RzLXBlci0xMDAgQ0ZTIG1lYXN1cmUuCgojIyBTdW1tYXJ5CgpUaGlzIGFuYWx5c2lzIG9mIHRoZSBDaXR5IG9mIEJhbHRpbW9yZSdzIENhbGxzIGZvciBTZXJ2aWNlIGRhdGFzZXQsIGluIGNvbmp1bmN0aW9uIHdpdGggaXRzIEFycmVzdCBkYXRhc2V0LCBoYXMgbm90IHByb2R1Y2VkIGFueQpncm91bmQtYnJlYWtpbmcgY29uY2x1c2lvbnMuICBIb3dldmVyLCBpdCBkb2VzIHNob3cgdGhlIHBvdGVudGlhbCBvZiBvcGVuIGRhdGEgaW5pdGlhdGl2ZXMgdG8gZW5hYmxlIGFuYWx5c3RzIHRvIGdsZWFuIGludGVyZXN0aW5nCmluZm9ybWF0aW9uIGZyb20gYSBjaXR5J3MgZGF0YS4gIFdlIHdlcmUgYWJsZSB0byBkaXNjZXJuIGEgZmV3IG5vdC1zby1zdXJwcmlzaW5nIGZhY3RzIGZyb20gdGhlIGRhdGFzZXQsIGluY2x1ZGluZzoKCiogVGhlcmUgaXMgc2lnbmlmaWNhbnQgdmFyaWF0aW9uIGluIHRoZSB2b2x1bWUgb2Ygc2VydmljZSBjYWxscyBhY3Jvc3MgdGhlIGNpdHksIGV2ZW4gd2hlbiBhY2NvdW50aW5nIGZvciBwb3B1bGF0aW9uIGRlbnNpdHkKKiBIaWdoLXZvbHVtZSBkYXlzIGFuZCB0aW1lcywgbGlrZSBKdWx5IDQgYW5kIEhhbGxvd2VlbiwgYXMgd2VsbCBhcyB0aW1lcyBvZiBjaXZpbCB1bnJlc3QsIGFyZSBjbGVhcmx5IGFwcGFyZW50IGluIHRoZSBkYXRhCiogQ2FsbCB2b2x1bWUsIG5vcm1hbGl6ZWQgYnkgcG9wdWxhdGlvbiBkZW5zaXR5LCBpcyBuZWdhdGl2ZWx5IChhbmQgc2lnbmlmaWNhbnRseSkgY29ycmVsYXRlZCB3aXRoIGJvdGggbWVkaWFuIGhvdXNlaG9sZCBpbmNvbWUgYW5kCnJlc2lkZW50aWFsIGNsYXNzaWZpY2F0aW9uCiogVGhlIHJhdGlvIG9mIGFycmVzdHMgdG8gY2FsbHMgaXMgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggcmVzaWRlbnRpYWwgY2xhc3NpZmljYXRpb24gYW5kIHRoZSBwZXJjZW50YWdlIG9mIGFuIGFyZWEgdGhhdCBpcyBvZiB3aGl0ZSByYWNlCgpLdWRvcyB0byB0aGUgQ2l0eSBvZiBCYWx0aW1vcmUgZm9yIG1ha2luZyB0aGVzZSBkYXRhc2V0cyBhdmFpbGFibGUuICBMaWtlIG1hbnkgY2l0aWVzIGFjcm9zcyB0aGUgY291bnRyeSwgaW5jbHVkaW5nIHRob3NlIHBhcnRpY2lwYXRpbmcKaW4gdGhlIFtQb2xpY2UgRGF0YSBJbml0aWF0aXZlXShodHRwczovL3d3dy5wb2xpY2VkYXRhaW5pdGlhdGl2ZS5vcmcvKSwgQmFsdGltb3JlIGhhcyBtYWRlIGFuIGltcG9ydGFudCBjb21taXRtZW50IHRvIHRyYW5zcGFyZW5jeSBhbmQKY29tbXVuaXR5IGVuZ2FnZW1lbnQgYnkgcHVibGlzaGluZyByaWNoIHNldHMgb2YgZGF0YSBvbiBwdWJsaWMgc2FmZXR5IG9wZXJhdGlvbnMuCg==