This notebook produces some simple visualizations of arrests in Baltimore from Jan 2012 through Jun 2017. These visualizations are mostly intended to support a presentation on using open policing data that I gave to a couple of audiences in the summer of 2017. Since the Baltimore Police Department (BPD) provides one of the best open datasets available, I chose it as the basis of the talks.

The slides for the most recent talk (which also covers open data and use of open source software for analytics) are here.

It is not the intent of this notebook to draw any conclusions about the nature of crime in Baltimore, nor is it a goal to argue for or against any particular policing policy or strategy. The intent is simply to show how to create interesting visualizations from open policing data (combined with contextual data from other sources, such as the American Community Survey).

One additional caveat: it is important to remember that counting (and analyzing) arrests made by law enforcement is not the same as counting crime that has occurred, nor is it a measure of the degree to which crime impacts a neighborhood or city. Two areas could be equally impacted by crime, but have different numbers of arrests due to a range of factors, including differences in law enforcement activity, different crime-fighting strategies, differences in the tendency of the inhabitants to report crimes to the police, and so on. Still, looking at arrests can provide interesting and useful insights into how a community like Baltimore is experiencing and combatting crime.

Summary of the Dataset

According to the dataset’s page on the city’s data portal, it “represents Part I victim-based crime data”. The FBI has defined eight categories of violent and property crime as Part I crimes “because they are serious crimes, they occur with regularity in all areas of the country, and they are likely to be reported to police.”

The BPD dataset also includes records for “shootings”. A shooting could fit into multiple FBI Part I categories, and the dataset documentation does not indicate how “shootings” relate to the FBI categories. For purposes of this notebook, I aggregated them in the same manner as the other categories.

Each record in the dataset contains:

  • The date and time of the incident
  • The approximate latitude/longitude of the incident
  • The neighborhood, post, and district in which the incident occurred
  • A crime code (though the documentation does not define what the codes mean, so this variable is not very useful)
  • A description of the type of crime associated with the arrest
  • What kind of weapon, if any, was involved
  • The type of area in which the incident occurred, and a separate indicator as to whether the incident occurred inside or outside

For details on the transformations on the BPD dataset, as well as integration of the other data sources used in the notebook, see the Baltimore-ReadData.R script in the GitHub repository.

Distribution of Types of Crime

As an initial orientation to the dataset, I recoded the “description” field in the dataset slightly, and aggregated the records by the resulting crime types. Arrests for larceny and non-aggravated assault make up more than half of the records in the dataset:

Arrests %>% group_by(Type) %>% summarize(n=n()) %>% mutate(pct=n/sum(n), pcts=percent(pct)) %>%
  ggplot() +
  geom_bar(mapping=aes(x=reorder(Type, n), y=n), stat='identity') + coord_flip() +
  stat_summary(fun.y = identity, geom="text", aes(x=reorder(Type, n), y=n, label=pcts), hjust = -.25) +
  scale_y_continuous(labels=comma, limits=c(0,100000)) +
  theme_economist() +
  labs(x='Crime Type', y='Incident Count', title='Distribution of Arrests by Crime Type',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS),
       caption=paste0('n=', comma(nrow(Arrests))))

Where do Arrests Occur?

One of the more useful visualizations for incident-level data is to create maps that tell us where arrests are occurring in a jurisdiction. In the next few sections, we will explore spatial visualization from a few different perspectives.

Neighborhoods

One of Baltimore’s distinguishing features is neighborhood identity. Areas as small as a few city blocks are recognized by a specific name, with an official designation and definition by the city government, and often a distinctive architectural style or other unique characteristics.

The city provides a shapefile with neighborhood boundaries and basic demographic information for each neighborhood. This allows us to create a neighborhood-level choropleth of arrests and arrests per capita. (Note that the arrest dataset from BPD truncates some of the neighborhood names, requiring a hardcoded adjustment of these values before merging with the shapefile.)

TopCrimeNeighborhoods <- NeighborhoodDf %>%
  filter(!is.infinite(Arrests)) %>%
  arrange(desc(Arrests)) %>%
  head(10) %>%
  bind_cols(tibble(KeyAbbr=LETTERS[1:(nrow(.))])) %>%
  select(KeyAbbr, Name, Arrests, CentroidLongitude, CentroidLatitude)
NeighborhoodSDF %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=Arrests)) +
    geom_path(color='grey70') +
    geom_label(data=TopCrimeNeighborhoods,
               mapping=aes(x=CentroidLongitude, y=CentroidLatitude, label=KeyAbbr),
               inherit.aes=FALSE, size=2) +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90', labels=comma) +
    coord_map() +
    theme_void() +
  labs(fill='Arrests',
       title='Arrests in Baltimore Neighborhoods',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS))

The average number of arrests in all 278 neighborhoods is 942.9748. The ten neighborhoods with the highest number of arrests (labeled on the map above) are:

kable(TopCrimeNeighborhoods %>%
        select(Label=KeyAbbr, Neighborhood=Name, Arrests) %>% mutate(Arrests=comma(Arrests)), format='html'
) %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Label Neighborhood Arrests
A Downtown 8,726
B Frankford 6,337
C Belair-Edison 5,721
D Brooklyn 4,189
E Cherry Hill 3,898
F Sandtown-Winchester 3,850
G Canton 3,655
H Inner Harbor 3,284
I Patterson Park Neighborhood 3,227
J Upton 3,224
TopCrimeNeighborhoods <- NeighborhoodDf %>%
  filter(!is.infinite(ArrestsPerCapita)) %>%
  arrange(desc(ArrestsPerCapita)) %>%
  head(11) %>%
  bind_cols(tibble(KeyAbbr=c('X', LETTERS[1:(nrow(.)-1)]))) %>%
  select(KeyAbbr, Name, ArrestsPerCapita, Arrests, Population, CentroidLongitude, CentroidLatitude)
NeighborhoodSDF %>%
  mutate(ArrestsPerCapita=case_when(
    .$Name=='Pulaski Industrial Area' ~ as.numeric(NA),
    TRUE ~ .$ArrestsPerCapita)) %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=ArrestsPerCapita)) +
    geom_path(color='grey70') +
    geom_label(data=TopCrimeNeighborhoods,
               mapping=aes(x=CentroidLongitude, y=CentroidLatitude, label=KeyAbbr),
               inherit.aes=FALSE, size=2) +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90') +
    coord_map() +
    theme_void() +
  labs(fill='Arrests/Capita',
       title='Arrests per Capita in Baltimore Neighborhoods',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS))

The city-wide number of arrests per capita over this time period is 0.4226984. 22 neighborhoods (indicated with grey shading on the map) are industrial/commercial areas with no residential population (according to the city’s neighborhood shapefile attributes). However, one industrial area (the Pulaski Industrial Area, labeled with an “X”), does have a very small population, and a relatively large number of arrests, producing an arrests/capita value of 13.6422764. To avoid having this outlier skew the color scale on the choropleth, we have excluded it from the shaded areas on the map. The highest-crime neighborhoods, on an arrests/capita basis, are:

kable(TopCrimeNeighborhoods %>%
        select(Label=KeyAbbr, Neighborhood=Name, Arrests, Population, `Arrests per Capita`=ArrestsPerCapita) %>%
        mutate(`Arrests per Capita`=comma(`Arrests per Capita`), Arrests=comma(Arrests), Population=comma(Population)), format='html'
) %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Label Neighborhood Arrests Population Arrests per Capita
X Pulaski Industrial Area 1,678 123 13.642276
A Hopkins Bayview 436 103 4.233010
B Downtown West 1,039 366 2.838798
C Fairfield Area 397 159 2.496855
D Inner Harbor 3,284 1,484 2.212938
E Downtown 8,726 4,448 1.961781
F Montebello 214 117 1.829060
G Orangeville 359 202 1.777228
H Dunbar-Broadway 1,571 889 1.767154
I Charles North 1,865 1,059 1.761095
J University Of Maryland 656 387 1.695090

How “Approximate” are the Coordinates?

The documentation for the BPD dataset states that the latitude/longitude coordinates for each arrest are approximate. In an attempt to determine what “approximate” means, I was curious to compare the Neighborhood value in the dataset, for each arrest, to the result of overlaying the coordinates onto the city’s neighborhood shapefile.

Of the 262,657 arrests in the dataset, 680 had coordinates in a neighborhood polygon different from the neighborhood value that appears in the dataset. Of these, only 7 overlayed neighborhood polygons were not adjacent to the BPD-assigned neighborhood value–and among these, visual inspection indicated that in all cases the neighborhoods were very close. Casual inspection of the Location field in the BPD dataset, which indicates the address at which the incident occurred, suggests that BPD “approximates” the location by placing it at the nearest intersection or block. If this assumption is true, then one plausible explanation for the neighborhood mismatches is that this “jittering” of incident locations occasionally places the location in a nearby neighborhood, if the “real” location was close to a neighborhood boundary.

It is also possible, of course, that the city (or BPD) performed a neighborhood polygon overlay using the approximated coordinates, just as I did, but using different tools or a different methodology, producing slightly different results in a few cases.

This confirmation of the robustness of the approximate coordinates allows us to consider additional polygon overlays–and in particular, using Census shapefiles to assign each arrest to a Census Tract. This in turn will enable a much wider range of analyses using American Community Survey (ACS) data.

Census Tracts

After overlaying the arrest locations onto census tract polygons in the Census shapefile, we are able to create choropleths similar to the ones created above for neighborhoods. Note that the population values for each census tract are from the five-year estimates in the 2015 American Community Survey (ACS).

CensusBlockSDF %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=Arrests)) +
    geom_path(color='grey70') +
    geom_path(data=CountySDF, color='grey50') +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90', labels=comma) +
    coord_map() +
    theme_void() +
  labs(fill='Arrests',
       title='Arrests in Each Baltimore Census Tract',
       subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS))

CensusBlockSDF %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=ArrestsPerCapita)) +
    geom_path(color='grey70') +
    geom_path(data=CountySDF, color='grey50') +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90', labels=comma) +
    coord_map() +
    theme_void() +
  labs(fill='Arrests/Capita',
       title='Arrests 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))

What’s Happening Downtown?

In both the neighborhood-based and census tract-based choropleths, the downtown area has the highest number of arrests of any area in the city, and the highest (census tract) and fifth-highest (neighborhood) level of arrests per capita. There are neighborhoods with higher levels of arrests per capita, like Hopkins-Bayview, but these are relatively small areas with small populations. They are part of much larger census tracts that include lower-arrest areas as well, which results in flattening the density on the tract-based map. What’s consistent across the two approaches is strong evidence that the downtown area experiences a lot of arrests.

Here is a closer look at the downtown area:

ggmap(DowntownGoogleMap) +
  geom_path(data=CensusBlockSDF %>% filter(GEOID=='24510040100'), mapping=aes(x=long, y=lat, group=group)) +
  geom_polygon(data=NeighborhoodSDF %>% filter(Name %in% c('Downtown', 'Inner Harbor', 'Downtown West')),
               mapping=aes(x=long, y=lat, group=group, fill=Name), alpha=.35) +
  labs(fill='Neighborhood', title='Neighborhoods of Downtown Baltimore',
       subtitle='Census Tract 24510040100 outlined with a black line') + theme_void()

And the arrest and population data for these areas are:

NeighborhoodDf %>%
  filter(Name %in% c('Downtown', 'Inner Harbor', 'Downtown West')) %>%
  select(Neighborhood=Name, Population, Arrests, `Arrests per Capita`=ArrestsPerCapita) %>%
  mutate(Population=comma(Population), Arrests=comma(Arrests)) %>%
  kable(format='html') %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed"))
Neighborhood Population Arrests Arrests per Capita
Downtown 4,448 8,726 1.961781
Inner Harbor 1,484 3,284 2.212938
Downtown West 366 1,039 2.838798

The census tract has a population of 3,503, with 9,596 arrests and 2.739366 arrests per capita.

At first glance, it seems unusual that the Downtown neighborhood has a population of nearly 1,000 more residents than the largely-overlapping census tract–a difference of around 30%. Almost all of the Downtown neighborhood lies within the census tract; only the two city blocks immediately west of Lexington Market (a landmark indoor shopping establishment) lie outside the tract. One of these blocks is occupied by the parking garage for the market; the other includes a mix of retail space, public housing, and student housing for the University of Maryland. At the same time, the one-block-wide strip on the southern border of the census tract (bounded by Lombard and Pratt streets), consists mostly of hotels and commercial office space (as well as the United States courthouse). Certainly, the residential space west of the market accounts for some of the difference between the neighborhood and the census tract, but is partially offset by a small amount of residential space that is in the census tract but outside the Downtown neighborhood.

It occurred to me that population change might explain part of the difference, since my (unverified) assumption is that the neighborhood-level population data included in the city’s neighborhood shapefile are based on the 2010 census. It has been well-documented in the press that the city’s population has declined significantly. Could that explain part of the difference between the city-reported neighborhood population and the 2015 five-year ACS estimate of population for the census tract? As it turns out, the population of census tract 24510040100 actually grew from 2010 to 2015, according to the ACS estimates. As noted above, in 2015 it was 3,503; in 2010, it was 2,993.

It seems that we have a fairly good (and accurate) handle on the residential population in these areas, and therefore the denominator of our arrests per capita measures seems sound. What about the numerator: arrests? Are there really significantly more arrests downtown?

The density of arrests in the three downtown neighborhoods looks like this (keeping in mind that arrest locations are approximate):

ggmap(DowntownGoogleMap) +
  geom_density2d(data=DowntownArrests, mapping=aes(x=Longitude, y=Latitude), bins=20, na.rm=TRUE, alpha=.5) +
  geom_point(data=BPDhq, mapping=aes(x=Longitude, y=Latitude), shape=23, size=3, color='blue', fill='yellow') +
  labs(title='Density of Downtown Area Arrests',
       subtitle=paste0('Downtown, Downtown West, and Inner Harbor Neighborhoods, ', minDateS, ' - ', maxDateS)) +
  theme_void()

Clearly the highest density of arrests occurs along the Inner Harbor waterfront, centered around the intersection of Pratt and Light streets, with another area of slightly lower density occurring a few blocks east along Pratt Street. The blocks to the east of Lexington Market, running north-to-south between Eutaw and Howard Streets, and a seven-block east-west strip along Baltimore Street north of the waterfront, are also higher-density areas. Finally, the newer commercial/retail development at Harbor East has a high arrest concentration as well.

The high-density area at the eastern edge of the Downtown neighborhood (and census tract), between Fayette and Baltimore streets, is curious due to the proximity of Baltimore Police Department headquarters (as well as the headquarters for the Central district), indicated by the yellow diamond. This area is home to City Hall and the Circuit and District courthouses, and a typical range of downtown businesses, such as bars, restaurants, convenience stores, parking lots, and banks. It is plausible that many of the arrests that occur in this area occur at the police department facility, or perhaps one of the courthouses, while the alleged crime that led to the arrest occurred elsewhere in the city. Unfortunately there is nothing explicit in the dataset that would allow us to discern these arrests.

The distribution of arrests by type is somewhat different in these three downtown neighborhoods. There have been considerably more arrests for larceny, which is perhaps not surprising, considering the number of retail shops and stores in these neighborhoods. On the other hand, there have been significantly fewer burglaries and motor vehicle thefts, which is also understandable in neighborhoods with less residential space overall (and almost all of the residential space that does exist being apartment buildings that are less susceptible to burglary.)

DowntownArrests %>% group_by(Type) %>% summarize(n=n()) %>% mutate(pct=n/sum(n), pcts=percent(pct)) %>%
  ggplot() +
  geom_bar(mapping=aes(x=reorder(Type, n), y=n), stat='identity') + coord_flip() +
  stat_summary(fun.y = identity, geom="text", aes(x=reorder(Type, n), y=n, label=pcts), hjust = -.25) +
  scale_y_continuous(labels=comma, limits=c(0,10000)) +
  theme_economist() +
  labs(x='Crime Type', y='Incident Count', title='Distribution of Arrests by Crime Type',
       subtitle=paste0('Downtown Baltimore Neighborhoods, ', minDateS, ' - ', maxDateS),
       caption=paste0('n=', comma(nrow(DowntownArrests))))

When do Arrests Occur?

Looking at the geographic distribution of arrests is interesting, but we can gain additional insights from examining the temporal dimension–when arrests occur. Consideration of the date and time of arrests is particularly valuable to police command staff, but can also give a sense of the seasonal and diurnal variations in criminal activity and police response to it.

The following visualization shows a “heat map” of the time of day and day of the year, in 2015, with arrests per hour indicated by the degree of shading in each hourly cell:

Arrests %>% filter(year(ArrestDate)==2015) %>%
  group_by(ArrestDate, ArrestHour) %>%
  summarize(n=n()) %>%
  ungroup() %>%
  arrange(ArrestDate, ArrestHour) %>%
  ggplot() +
  geom_tile(aes(x=ArrestDate, y=ArrestHour, 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='Arrests/hour', title='Arrests by Time of Day, each day in 2015',
       subtitle='Arrests Made by the Baltimore Police Department')

A quick glance at this visualization suggests some hypotheses about crime and police response during 2015:

  • The civil unrest that followed the Freddie Gray death-in-custody situation in late April 2015 is very apparent
  • Nighttime (and particularly early morning) arrests seem to decline in the winter months, and increase in the warm months of July-August
  • The 1:00 am hour seems to have a spike in arrests
  • There are several other cells in the heatmap–notably 9:00 pm on December 15–that have an unusual number of arrests; these could be simple random fluctuations or record-keeping anomalies (which seems to be the case on 12/15, since a Google search reveals no extraordinary events on that date)

The phenomenon of early morning arrests (midnight - 2:00 am hours) merits a closer look to examine the associated crime types:

tdf <- Arrests %>% filter(ArrestHour %in% c(0,1,2))
tdf %>% group_by(Type) %>% summarize(n=n()) %>% mutate(pct=n/sum(n), pcts=percent(pct)) %>%
  ggplot() +
  geom_bar(mapping=aes(x=reorder(Type, n), y=n), stat='identity') + coord_flip() +
  stat_summary(fun.y = identity, geom="text", aes(x=reorder(Type, n), y=n, label=pcts), hjust = -.25) +
  scale_y_continuous(labels=comma, limits=c(0,8000)) +
  theme_economist() +
  labs(x='Crime Type', y='Incident Count', title='Distribution of Arrests by Crime Type',
       subtitle=paste0('Baltimore City, arrests occurring between midnight and 3:00 am, ', minDateS, ' - ', maxDateS),
       caption=paste0('n=', comma(nrow(tdf))))

rm(tdf)

Somewhat expectedly, about thirty percent of arrests in the very early morning hours are for violent crimes (aggravated assault, robbery, rape, and murder) and another twenty-five percent are for non-aggravated assault. Property crimes are less prevalent in the middle of the night.

Violent Crime

There is considerable variation in where violent crime occurs across the city. (In alignment with FBI guidance, we define “violent crime” as: murder, rape, robbery, shooting, and aggravated assault):

CensusBlockSDF %>%
  mutate(VCPercent=ViolentCrimeArrests/Arrests) %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=VCPercent)) +
    geom_path(color='grey70') +
    geom_path(data=CountySDF, color='grey50') +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90', labels=percent) +
    coord_map() +
    theme_void() +
  labs(fill='% Violent Crime',
       title='Percentage of Arrests for Violent Crime 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))

Zoning, Land Use, and Arrests

The state of Maryland’s Department of Planning provides a tool called MdProperty View, through which users can obtain a wide range of GIS information. The Department also makes the underlying shapefiles for each county (and Baltimore City) available. After a little recoding to simplify the categories, we are able to produce summary visualizations for Baltimore like this one for land use:

ggplot(data=landUseSDF, mapping=aes(x=long, y=lat, group=group)) +
  geom_polygon(mapping=aes(fill=LandUseCategory)) +
  scale_fill_brewer(type='qual', palette='Accent') +
  coord_map() + theme_void() + labs(fill='Land Use', title='Land Use in Baltimore City',
                                      caption='Source: Maryland Department of Planning, 2013')

And a similar one for zoning:

ggplot(data=zoningSDF, mapping=aes(x=long, y=lat, group=group)) +
  geom_polygon(mapping=aes(fill=GENZONE)) +
  scale_fill_brewer(type='qual', palette='Accent') +
  coord_equal() + theme_void() + labs(fill='Zoning Type', title='Zoning in Baltimore City',
                                      caption='Source: Maryland Department of Planning, 2013')

In what follows here, I will focus on the Land Use dataset, since it captures how areas are actually being used, versus how the city government has classified or designated land for use. Note that the Land Use data are from a study conducted in 2010, though the Department of Planning assembled the dataset in 2013.

By merging the land use and census tract shapefiles, and overlaying polygons, we are able to calculate the percentage of each census tract’s area in each of the land use categories. This in turn allows us to visualize, for example, the extent of residential land use across the city:

CensusBlockSDF %>%
  ggplot(mapping=aes(x=long, y=lat, group=group)) +
    geom_polygon(aes(fill=ResidentialPercentage)) +
    geom_path(color='grey70') +
    geom_path(data=CountySDF, color='grey50') +
    scale_fill_gradient(low = "white",high = "steelblue", na.value='grey90', labels=percent) +
    coord_map() +
    theme_void() +
  labs(fill='% Residential',
       title='Percentage of Census Tract Area Classified as Residential',
       caption='Classification source: Maryland Department of Planning, 2013')

Census tract land use classification also allows us to visualize the distribution of arrests per capita, by tract, in terms of land use and household income:

CensusBlockDf %>% mutate(PercentViolentCrime=ViolentCrimeArrests/Arrests) %>%
  ggplot() +
  geom_point(aes(x=MedianHouseholdIncome, y=ResidentialPercentage, size=ArrestsPerCapita, color=PercentViolentCrime), na.rm=TRUE) +
  scale_color_gradient(low = "#c6dbef", high = "#08306b", labels=percent) +
  scale_y_continuous(labels=percent) +
  scale_x_continuous(labels=dollar) +
  labs(
    y='% of Tract Area Classified as Residential',
    x='Median Annual Household Income',
    color='% Violent Crime',
    size='Arrests/Capita',
    title='Census Tract Arrests Per Capita, by Land Use and Income',
    subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS)
  ) +
  theme_economist() +
  theme(legend.text=element_text(size=10), legend.position='bottom')

Clearly household income is a much stronger indicator of both arrests per capita and prevalence of violent crime than land use. Highly residential areas experience about the same number of arrests per capita as non-residential (e.g., commercial or industrial) areas, but areas with poorer households experience a higher number of arrests per capita, and a larger percentage of violent crime as well.

We see a similar pattern, albeit with a more uniform x-axis distribution, when we replace household income with the percentage of the tract population that is white:

CensusBlockDf %>% mutate(PercentViolentCrime=ViolentCrimeArrests/Arrests) %>%
  ggplot() +
  geom_point(aes(x=PercentWhite, y=ResidentialPercentage, size=ArrestsPerCapita, color=PercentViolentCrime), na.rm=TRUE) +
  scale_color_gradient(low = "#c6dbef", high = "#08306b", labels=percent) +
  scale_y_continuous(labels=percent) +
  scale_x_continuous(labels=percent) +
  labs(
    y='% of Tract Area Classified as Residential',
    x='% of Tract Population that is White',
    color='% Violent Crime',
    size='Arrests/Capita',
    title='Census Tract Arrests Per Capita, by Land Use and Race',
    subtitle=paste0('Baltimore City, ', minDateS, ' - ', maxDateS)
  ) +
  theme_economist() +
  theme(legend.text=element_text(size=10), legend.position='bottom')

LS0tCnRpdGxlOiAiVmlzdWFsaXphdGlvbiBvZiBBcnJlc3RzIGluIEJhbHRpbW9yZSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKVGhpcyBub3RlYm9vayBwcm9kdWNlcyBzb21lIHNpbXBsZSB2aXN1YWxpemF0aW9ucyBvZiBhcnJlc3RzIGluIEJhbHRpbW9yZSBmcm9tIGByIG1pbkRhdGVTYCB0aHJvdWdoIGByIG1heERhdGVTYC4gVGhlc2UgdmlzdWFsaXphdGlvbnMgYXJlIG1vc3RseQppbnRlbmRlZCB0byBzdXBwb3J0IGEgcHJlc2VudGF0aW9uIG9uIHVzaW5nIG9wZW4gcG9saWNpbmcgZGF0YSB0aGF0IEkgZ2F2ZSB0byBhIGNvdXBsZSBvZiBhdWRpZW5jZXMgaW4gdGhlIHN1bW1lciBvZiAyMDE3LiAgU2luY2UgdGhlIEJhbHRpbW9yZSBQb2xpY2UgRGVwYXJ0bWVudCAoQlBEKQpbcHJvdmlkZXNdKGh0dHBzOi8vZGF0YS5iYWx0aW1vcmVjaXR5Lmdvdi9QdWJsaWMtU2FmZXR5L0JQRC1QYXJ0LTEtVmljdGltLUJhc2VkLUNyaW1lLURhdGEvd3NmcS1tdmlqKSBvbmUgb2YgdGhlIGJlc3Qgb3BlbiBkYXRhc2V0cyBhdmFpbGFibGUsCkkgY2hvc2UgaXQgYXMgdGhlIGJhc2lzIG9mIHRoZSB0YWxrcy4KClRoZSBzbGlkZXMgZm9yIHRoZSBtb3N0IHJlY2VudCB0YWxrICh3aGljaCBhbHNvIGNvdmVycyBvcGVuIGRhdGEgYW5kIHVzZSBvZiBvcGVuIHNvdXJjZSBzb2Z0d2FyZSBmb3IgYW5hbHl0aWNzKSBhcmUgW2hlcmVdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3ByZXNlbnRhdGlvbi9kLzFiWnhfRzRkWWhTVmlCNnJ3RDliZE8yMkRPMVZRTlZGR25YcnR6b1NHUUdrL2VkaXQ/dXNwPXNoYXJpbmcpLgoKSXQgaXMgbm90IHRoZSBpbnRlbnQgb2YgdGhpcyBub3RlYm9vayB0byBkcmF3IGFueSBjb25jbHVzaW9ucyBhYm91dCB0aGUgbmF0dXJlIG9mIGNyaW1lIGluIEJhbHRpbW9yZSwgbm9yIGlzIGl0IGEgZ29hbCB0byBhcmd1ZSBmb3Igb3IgYWdhaW5zdAphbnkgcGFydGljdWxhciBwb2xpY2luZyBwb2xpY3kgb3Igc3RyYXRlZ3kuICBUaGUgaW50ZW50IGlzIHNpbXBseSB0byBzaG93IGhvdyB0byBjcmVhdGUgaW50ZXJlc3RpbmcgdmlzdWFsaXphdGlvbnMgZnJvbSBvcGVuIHBvbGljaW5nIGRhdGEKKGNvbWJpbmVkIHdpdGggY29udGV4dHVhbCBkYXRhIGZyb20gb3RoZXIgc291cmNlcywgc3VjaCBhcyB0aGUgQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSkuCgpPbmUgYWRkaXRpb25hbCBjYXZlYXQ6ICBpdCBpcyBpbXBvcnRhbnQgdG8gcmVtZW1iZXIgdGhhdCBjb3VudGluZyAoYW5kIGFuYWx5emluZykgYXJyZXN0cyBtYWRlIGJ5IGxhdyBlbmZvcmNlbWVudCBpcwpub3QgdGhlIHNhbWUgYXMgY291bnRpbmcgY3JpbWUgdGhhdCBoYXMgb2NjdXJyZWQsIG5vcgppcyBpdCBhIG1lYXN1cmUgb2YgdGhlIGRlZ3JlZSB0byB3aGljaCBjcmltZSBpbXBhY3RzIGEgbmVpZ2hib3Job29kIG9yIGNpdHkuICBUd28gYXJlYXMgY291bGQgYmUgZXF1YWxseSBpbXBhY3RlZCBieSBjcmltZSwgYnV0IGhhdmUgZGlmZmVyZW50Cm51bWJlcnMgb2YgYXJyZXN0cyBkdWUgdG8gYSByYW5nZSBvZiBmYWN0b3JzLCBpbmNsdWRpbmcgZGlmZmVyZW5jZXMgaW4gbGF3IGVuZm9yY2VtZW50IGFjdGl2aXR5LCBkaWZmZXJlbnQgY3JpbWUtZmlnaHRpbmcgc3RyYXRlZ2llcywKZGlmZmVyZW5jZXMgaW4gdGhlIHRlbmRlbmN5IG9mIHRoZSBpbmhhYml0YW50cyB0byByZXBvcnQgY3JpbWVzIHRvIHRoZSBwb2xpY2UsIGFuZCBzbyBvbi4gU3RpbGwsIGxvb2tpbmcgYXQgYXJyZXN0cyBjYW4gcHJvdmlkZSBpbnRlcmVzdGluZwphbmQgdXNlZnVsIGluc2lnaHRzIGludG8gaG93IGEgY29tbXVuaXR5IGxpa2UgQmFsdGltb3JlIGlzIGV4cGVyaWVuY2luZyBhbmQgY29tYmF0dGluZyBjcmltZS4KCiMjIFN1bW1hcnkgb2YgdGhlIERhdGFzZXQKCkFjY29yZGluZyB0byB0aGUgZGF0YXNldCdzIHBhZ2Ugb24gdGhlIGNpdHkncyBkYXRhIHBvcnRhbCwgaXQgInJlcHJlc2VudHMgUGFydCBJIHZpY3RpbS1iYXNlZCBjcmltZSBkYXRhIi4KVGhlIEZCSSBoYXMgW2RlZmluZWRdKGh0dHBzOi8vd3d3LnVjcmRhdGF0b29sLmdvdi9vZmZlbnNlcy5jZm0pIGVpZ2h0IGNhdGVnb3JpZXMgb2YgdmlvbGVudCBhbmQgcHJvcGVydHkgY3JpbWUgYXMgUGFydCBJIGNyaW1lcwoiYmVjYXVzZSB0aGV5IGFyZSBzZXJpb3VzIGNyaW1lcywgdGhleSBvY2N1ciB3aXRoIHJlZ3VsYXJpdHkgaW4gYWxsIGFyZWFzIG9mIHRoZSBjb3VudHJ5LCBhbmQgdGhleSBhcmUgbGlrZWx5IHRvIGJlIHJlcG9ydGVkIHRvIHBvbGljZS4iCgpUaGUgQlBEIGRhdGFzZXQgYWxzbyBpbmNsdWRlcyByZWNvcmRzIGZvciAic2hvb3RpbmdzIi4gIEEgc2hvb3RpbmcgY291bGQgZml0IGludG8gbXVsdGlwbGUgRkJJIFBhcnQgSSBjYXRlZ29yaWVzLCBhbmQgdGhlIGRhdGFzZXQgZG9jdW1lbnRhdGlvbgpkb2VzIG5vdCBpbmRpY2F0ZSBob3cgInNob290aW5ncyIgcmVsYXRlIHRvIHRoZSBGQkkgY2F0ZWdvcmllcy4gRm9yIHB1cnBvc2VzIG9mIHRoaXMgbm90ZWJvb2ssIEkgYWdncmVnYXRlZCB0aGVtIGluIHRoZSBzYW1lIG1hbm5lciBhcyB0aGUKb3RoZXIgY2F0ZWdvcmllcy4KCkVhY2ggcmVjb3JkIGluIHRoZSBkYXRhc2V0IGNvbnRhaW5zOgoKKiBUaGUgZGF0ZSBhbmQgdGltZSBvZiB0aGUgaW5jaWRlbnQKKiBUaGUgYXBwcm94aW1hdGUgbGF0aXR1ZGUvbG9uZ2l0dWRlIG9mIHRoZSBpbmNpZGVudAoqIFRoZSBuZWlnaGJvcmhvb2QsIHBvc3QsIGFuZCBkaXN0cmljdCBpbiB3aGljaCB0aGUgaW5jaWRlbnQgb2NjdXJyZWQKKiBBIGNyaW1lIGNvZGUgKHRob3VnaCB0aGUgZG9jdW1lbnRhdGlvbiBkb2VzIG5vdCBkZWZpbmUgd2hhdCB0aGUgY29kZXMgbWVhbiwgc28gdGhpcyB2YXJpYWJsZSBpcyBub3QgdmVyeSB1c2VmdWwpCiogQSBkZXNjcmlwdGlvbiBvZiB0aGUgdHlwZSBvZiBjcmltZSBhc3NvY2lhdGVkIHdpdGggdGhlIGFycmVzdAoqIFdoYXQga2luZCBvZiB3ZWFwb24sIGlmIGFueSwgd2FzIGludm9sdmVkCiogVGhlIHR5cGUgb2YgYXJlYSBpbiB3aGljaCB0aGUgaW5jaWRlbnQgb2NjdXJyZWQsIGFuZCBhIHNlcGFyYXRlIGluZGljYXRvciBhcyB0byB3aGV0aGVyIHRoZSBpbmNpZGVudCBvY2N1cnJlZCBpbnNpZGUgb3Igb3V0c2lkZQoKRm9yIGRldGFpbHMgb24gdGhlIHRyYW5zZm9ybWF0aW9ucyBvbiB0aGUgQlBEIGRhdGFzZXQsIGFzIHdlbGwgYXMgaW50ZWdyYXRpb24gb2YgdGhlIG90aGVyIGRhdGEgc291cmNlcyB1c2VkIGluIHRoZSBub3RlYm9vaywgc2VlIHRoZQpbYEJhbHRpbW9yZS1SZWFkRGF0YS5SYCBzY3JpcHRdKGh0dHBzOi8vZ2l0aHViLmNvbS9zY290dGNhbWUvcG9saWNlLWRhdGEvYmxvYi9tYXN0ZXIvQmFsdGltb3JlLVJlYWREYXRhLlIpIGluIHRoZSBHaXRIdWIgcmVwb3NpdG9yeS4KCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlLCBxdWlldGx5PVRSVUUpCmxpYnJhcnkoZ2dwbG90MiwgcXVpZXRseT1UUlVFKQpsaWJyYXJ5KGdndGhlbWVzLCBxdWlldGx5PVRSVUUpCmxpYnJhcnkoc2NhbGVzLCBxdWlldGx5PVRSVUUpCmxpYnJhcnkoa25pdHIsIHF1aWV0bHk9VFJVRSkKbGlicmFyeShrYWJsZUV4dHJhLCBxdWlldGx5PVRSVUUpCgppZiAoIWV4aXN0cygnQXJyZXN0cycpKSB7CiAgc291cmNlKCdCYWx0aW1vcmUtUmVhZERhdGEuUicpCn0KYGBgCgojIyBEaXN0cmlidXRpb24gb2YgVHlwZXMgb2YgQ3JpbWUKCkFzIGFuIGluaXRpYWwgb3JpZW50YXRpb24gdG8gdGhlIGRhdGFzZXQsIEkgcmVjb2RlZCB0aGUgImRlc2NyaXB0aW9uIiBmaWVsZCBpbiB0aGUgZGF0YXNldCBzbGlnaHRseSwgYW5kIGFnZ3JlZ2F0ZWQgdGhlIHJlY29yZHMgYnkgdGhlCnJlc3VsdGluZyBjcmltZSB0eXBlcy4gIEFycmVzdHMgZm9yIGxhcmNlbnkgYW5kIG5vbi1hZ2dyYXZhdGVkIGFzc2F1bHQgbWFrZSB1cCBtb3JlIHRoYW4gaGFsZiBvZiB0aGUgcmVjb3JkcyBpbiB0aGUgZGF0YXNldDoKCmBgYHtyfQpBcnJlc3RzICU+JSBncm91cF9ieShUeXBlKSAlPiUgc3VtbWFyaXplKG49bigpKSAlPiUgbXV0YXRlKHBjdD1uL3N1bShuKSwgcGN0cz1wZXJjZW50KHBjdCkpICU+JQogIGdncGxvdCgpICsKICBnZW9tX2JhcihtYXBwaW5nPWFlcyh4PXJlb3JkZXIoVHlwZSwgbiksIHk9biksIHN0YXQ9J2lkZW50aXR5JykgKyBjb29yZF9mbGlwKCkgKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9IGlkZW50aXR5LCBnZW9tPSJ0ZXh0IiwgYWVzKHg9cmVvcmRlcihUeXBlLCBuKSwgeT1uLCBsYWJlbD1wY3RzKSwgaGp1c3QgPSAtLjI1KSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1jb21tYSwgbGltaXRzPWMoMCwxMDAwMDApKSArCiAgdGhlbWVfZWNvbm9taXN0KCkgKwogIGxhYnMoeD0nQ3JpbWUgVHlwZScsIHk9J0luY2lkZW50IENvdW50JywgdGl0bGU9J0Rpc3RyaWJ1dGlvbiBvZiBBcnJlc3RzIGJ5IENyaW1lIFR5cGUnLAogICAgICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUyksCiAgICAgICBjYXB0aW9uPXBhc3RlMCgnbj0nLCBjb21tYShucm93KEFycmVzdHMpKSkpCmBgYAoKIyMgV2hlcmUgZG8gQXJyZXN0cyBPY2N1cj8KCk9uZSBvZiB0aGUgbW9yZSB1c2VmdWwgdmlzdWFsaXphdGlvbnMgZm9yIGluY2lkZW50LWxldmVsIGRhdGEgaXMgdG8gY3JlYXRlIG1hcHMgdGhhdCB0ZWxsIHVzIHdoZXJlIGFycmVzdHMgYXJlIG9jY3VycmluZyBpbiBhCmp1cmlzZGljdGlvbi4gSW4gdGhlIG5leHQgZmV3IHNlY3Rpb25zLCB3ZSB3aWxsIGV4cGxvcmUgc3BhdGlhbCB2aXN1YWxpemF0aW9uIGZyb20gYSBmZXcgZGlmZmVyZW50IHBlcnNwZWN0aXZlcy4KCiMjIyBOZWlnaGJvcmhvb2RzCgpPbmUgb2YgQmFsdGltb3JlJ3MgZGlzdGluZ3Vpc2hpbmcgZmVhdHVyZXMgaXMgW25laWdoYm9yaG9vZCBpZGVudGl0eV0oaHR0cHM6Ly9saXZlYmFsdGltb3JlLmNvbS9uZWlnaGJvcmhvb2RzLykuCkFyZWFzIGFzIHNtYWxsIGFzIGEgZmV3IGNpdHkgYmxvY2tzIGFyZSByZWNvZ25pemVkIGJ5IGEgc3BlY2lmaWMKbmFtZSwgd2l0aCBhbiBvZmZpY2lhbCBkZXNpZ25hdGlvbiBhbmQgZGVmaW5pdGlvbiBieSB0aGUgY2l0eSBnb3Zlcm5tZW50LCBhbmQgb2Z0ZW4gYSBkaXN0aW5jdGl2ZSBhcmNoaXRlY3R1cmFsIHN0eWxlIG9yIG90aGVyIHVuaXF1ZQpjaGFyYWN0ZXJpc3RpY3MuCgpUaGUgY2l0eSBwcm92aWRlcyBhIFtzaGFwZWZpbGVdKGh0dHA6Ly9naXMtYmFsdGltb3JlLm9wZW5kYXRhLmFyY2dpcy5jb20vZGF0YXNldHMvMWNhOTNlNjhmMTE1NDFkNGI1OWE2MzI0MzcyNWM0YjdfMCkgd2l0aApuZWlnaGJvcmhvb2QgYm91bmRhcmllcyBhbmQgYmFzaWMgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24gZm9yIGVhY2ggbmVpZ2hib3Job29kLiBUaGlzIGFsbG93cyB1cyB0byBjcmVhdGUgYSBuZWlnaGJvcmhvb2QtbGV2ZWwKW2Nob3JvcGxldGhdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nob3JvcGxldGhfbWFwKSBvZiBhcnJlc3RzIGFuZCBhcnJlc3RzIHBlciBjYXBpdGEuIChOb3RlIHRoYXQgdGhlIGFycmVzdCBkYXRhc2V0IGZyb20gQlBECnRydW5jYXRlcyBzb21lIG9mIHRoZSBuZWlnaGJvcmhvb2QgbmFtZXMsIHJlcXVpcmluZyBhIGhhcmRjb2RlZCBhZGp1c3RtZW50IG9mIHRoZXNlIHZhbHVlcyBiZWZvcmUgbWVyZ2luZyB3aXRoIHRoZSBzaGFwZWZpbGUuKQoKYGBge3IsIGZpZy53aWR0aD0xMX0KVG9wQ3JpbWVOZWlnaGJvcmhvb2RzIDwtIE5laWdoYm9yaG9vZERmICU+JQogIGZpbHRlcighaXMuaW5maW5pdGUoQXJyZXN0cykpICU+JQogIGFycmFuZ2UoZGVzYyhBcnJlc3RzKSkgJT4lCiAgaGVhZCgxMCkgJT4lCiAgYmluZF9jb2xzKHRpYmJsZShLZXlBYmJyPUxFVFRFUlNbMToobnJvdyguKSldKSkgJT4lCiAgc2VsZWN0KEtleUFiYnIsIE5hbWUsIEFycmVzdHMsIENlbnRyb2lkTG9uZ2l0dWRlLCBDZW50cm9pZExhdGl0dWRlKQoKTmVpZ2hib3Job29kU0RGICU+JQogIGdncGxvdChtYXBwaW5nPWFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCkpICsKICAgIGdlb21fcG9seWdvbihhZXMoZmlsbD1BcnJlc3RzKSkgKwogICAgZ2VvbV9wYXRoKGNvbG9yPSdncmV5NzAnKSArCiAgICBnZW9tX2xhYmVsKGRhdGE9VG9wQ3JpbWVOZWlnaGJvcmhvb2RzLAogICAgICAgICAgICAgICBtYXBwaW5nPWFlcyh4PUNlbnRyb2lkTG9uZ2l0dWRlLCB5PUNlbnRyb2lkTGF0aXR1ZGUsIGxhYmVsPUtleUFiYnIpLAogICAgICAgICAgICAgICBpbmhlcml0LmFlcz1GQUxTRSwgc2l6ZT0yKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsaGlnaCA9ICJzdGVlbGJsdWUiLCBuYS52YWx1ZT0nZ3JleTkwJywgbGFiZWxzPWNvbW1hKSArCiAgICBjb29yZF9tYXAoKSArCiAgICB0aGVtZV92b2lkKCkgKwogIGxhYnMoZmlsbD0nQXJyZXN0cycsCiAgICAgICB0aXRsZT0nQXJyZXN0cyBpbiBCYWx0aW1vcmUgTmVpZ2hib3Job29kcycsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSkKYGBgClRoZSBhdmVyYWdlIG51bWJlciBvZiBhcnJlc3RzIGluIGFsbCBgciBucm93KE5laWdoYm9yaG9vZERmKWAgbmVpZ2hib3Job29kcyBpcyBgciBjb21tYShtZWFuKE5laWdoYm9yaG9vZERmJEFycmVzdHMpKWAuIFRoZSB0ZW4gbmVpZ2hib3Job29kcyB3aXRoIHRoZSBoaWdoZXN0IG51bWJlciBvZiBhcnJlc3RzIChsYWJlbGVkIG9uIHRoZSBtYXAgYWJvdmUpIGFyZToKYGBge3J9CmthYmxlKFRvcENyaW1lTmVpZ2hib3Job29kcyAlPiUKICAgICAgICBzZWxlY3QoTGFiZWw9S2V5QWJiciwgTmVpZ2hib3Job29kPU5hbWUsIEFycmVzdHMpICU+JSBtdXRhdGUoQXJyZXN0cz1jb21tYShBcnJlc3RzKSksIGZvcm1hdD0naHRtbCcKKSAlPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMX0KVG9wQ3JpbWVOZWlnaGJvcmhvb2RzIDwtIE5laWdoYm9yaG9vZERmICU+JQogIGZpbHRlcighaXMuaW5maW5pdGUoQXJyZXN0c1BlckNhcGl0YSkpICU+JQogIGFycmFuZ2UoZGVzYyhBcnJlc3RzUGVyQ2FwaXRhKSkgJT4lCiAgaGVhZCgxMSkgJT4lCiAgYmluZF9jb2xzKHRpYmJsZShLZXlBYmJyPWMoJ1gnLCBMRVRURVJTWzE6KG5yb3coLiktMSldKSkpICU+JQogIHNlbGVjdChLZXlBYmJyLCBOYW1lLCBBcnJlc3RzUGVyQ2FwaXRhLCBBcnJlc3RzLCBQb3B1bGF0aW9uLCBDZW50cm9pZExvbmdpdHVkZSwgQ2VudHJvaWRMYXRpdHVkZSkKCk5laWdoYm9yaG9vZFNERiAlPiUKICBtdXRhdGUoQXJyZXN0c1BlckNhcGl0YT1jYXNlX3doZW4oCiAgICAuJE5hbWU9PSdQdWxhc2tpIEluZHVzdHJpYWwgQXJlYScgfiBhcy5udW1lcmljKE5BKSwKICAgIFRSVUUgfiAuJEFycmVzdHNQZXJDYXBpdGEpKSAlPiUKICBnZ3Bsb3QobWFwcGluZz1hZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApKSArCiAgICBnZW9tX3BvbHlnb24oYWVzKGZpbGw9QXJyZXN0c1BlckNhcGl0YSkpICsKICAgIGdlb21fcGF0aChjb2xvcj0nZ3JleTcwJykgKwogICAgZ2VvbV9sYWJlbChkYXRhPVRvcENyaW1lTmVpZ2hib3Job29kcywKICAgICAgICAgICAgICAgbWFwcGluZz1hZXMoeD1DZW50cm9pZExvbmdpdHVkZSwgeT1DZW50cm9pZExhdGl0dWRlLCBsYWJlbD1LZXlBYmJyKSwKICAgICAgICAgICAgICAgaW5oZXJpdC5hZXM9RkFMU0UsIHNpemU9MikgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAid2hpdGUiLGhpZ2ggPSAic3RlZWxibHVlIiwgbmEudmFsdWU9J2dyZXk5MCcpICsKICAgIGNvb3JkX21hcCgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgbGFicyhmaWxsPSdBcnJlc3RzL0NhcGl0YScsCiAgICAgICB0aXRsZT0nQXJyZXN0cyBwZXIgQ2FwaXRhIGluIEJhbHRpbW9yZSBOZWlnaGJvcmhvb2RzJywKICAgICAgIHN1YnRpdGxlPXBhc3RlMCgnQmFsdGltb3JlIENpdHksICcsIG1pbkRhdGVTLCAnIC0gJywgbWF4RGF0ZVMpKQpgYGAKVGhlIGNpdHktd2lkZSBudW1iZXIgb2YgYXJyZXN0cyBwZXIgY2FwaXRhIG92ZXIgdGhpcyB0aW1lIHBlcmlvZCBpcyBgciBjb21tYShzdW0oTmVpZ2hib3Job29kRGYkQXJyZXN0cykvc3VtKE5laWdoYm9yaG9vZERmJFBvcHVsYXRpb24pKWAuCmByIG5yb3coTmVpZ2hib3Job29kRGYgJT4lIGZpbHRlcihQb3B1bGF0aW9uPT0wKSlgIG5laWdoYm9yaG9vZHMgKGluZGljYXRlZCB3aXRoIGdyZXkgc2hhZGluZyBvbiB0aGUgbWFwKSBhcmUKaW5kdXN0cmlhbC9jb21tZXJjaWFsIGFyZWFzIHdpdGggbm8gcmVzaWRlbnRpYWwgcG9wdWxhdGlvbiAoYWNjb3JkaW5nIHRvIHRoZSBjaXR5J3MKbmVpZ2hib3Job29kIHNoYXBlZmlsZSBhdHRyaWJ1dGVzKS4gIEhvd2V2ZXIsIG9uZSBpbmR1c3RyaWFsIGFyZWEgKHRoZSBQdWxhc2tpIEluZHVzdHJpYWwgQXJlYSwgbGFiZWxlZCB3aXRoIGFuICJYIiksIGRvZXMgaGF2ZSBhIHZlcnkgc21hbGwKcG9wdWxhdGlvbiwgYW5kIGEgcmVsYXRpdmVseSBsYXJnZSBudW1iZXIgb2YgYXJyZXN0cywgcHJvZHVjaW5nIGFuIGFycmVzdHMvY2FwaXRhIHZhbHVlIG9mCmByIFRvcENyaW1lTmVpZ2hib3Job29kcyAlPiUgZmlsdGVyKEtleUFiYnI9PSdYJykgJT4lIC4kQXJyZXN0c1BlckNhcGl0YWAuIFRvIGF2b2lkIGhhdmluZyB0aGlzIG91dGxpZXIgc2tldyB0aGUgY29sb3Igc2NhbGUgb24gdGhlIGNob3JvcGxldGgsCndlIGhhdmUgZXhjbHVkZWQgaXQgZnJvbSB0aGUgc2hhZGVkIGFyZWFzIG9uIHRoZSBtYXAuIFRoZSBoaWdoZXN0LWNyaW1lIG5laWdoYm9yaG9vZHMsIG9uIGFuIGFycmVzdHMvY2FwaXRhIGJhc2lzLCBhcmU6CmBgYHtyfQprYWJsZShUb3BDcmltZU5laWdoYm9yaG9vZHMgJT4lCiAgICAgICAgc2VsZWN0KExhYmVsPUtleUFiYnIsIE5laWdoYm9yaG9vZD1OYW1lLCBBcnJlc3RzLCBQb3B1bGF0aW9uLCBgQXJyZXN0cyBwZXIgQ2FwaXRhYD1BcnJlc3RzUGVyQ2FwaXRhKSAlPiUKICAgICAgICBtdXRhdGUoYEFycmVzdHMgcGVyIENhcGl0YWA9Y29tbWEoYEFycmVzdHMgcGVyIENhcGl0YWApLCBBcnJlc3RzPWNvbW1hKEFycmVzdHMpLCBQb3B1bGF0aW9uPWNvbW1hKFBvcHVsYXRpb24pKSwgZm9ybWF0PSdodG1sJwopICU+JSBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkKYGBgCgojIyMgSG93ICJBcHByb3hpbWF0ZSIgYXJlIHRoZSBDb29yZGluYXRlcz8KClRoZSBkb2N1bWVudGF0aW9uIGZvciB0aGUgQlBEIGRhdGFzZXQgc3RhdGVzIHRoYXQgdGhlIGxhdGl0dWRlL2xvbmdpdHVkZSBjb29yZGluYXRlcyBmb3IgZWFjaCBhcnJlc3QgYXJlIGFwcHJveGltYXRlLiBJbiBhbiBhdHRlbXB0IHRvCmRldGVybWluZSB3aGF0ICJhcHByb3hpbWF0ZSIgbWVhbnMsIEkgd2FzIGN1cmlvdXMgdG8gY29tcGFyZSB0aGUgTmVpZ2hib3Job29kIHZhbHVlIGluIHRoZSBkYXRhc2V0LCBmb3IgZWFjaCBhcnJlc3QsIHRvIHRoZSByZXN1bHQKb2Ygb3ZlcmxheWluZyB0aGUgY29vcmRpbmF0ZXMgb250byB0aGUgY2l0eSdzIG5laWdoYm9yaG9vZCBzaGFwZWZpbGUuCgpPZiB0aGUgYHIgY29tbWEobnJvdyhBcnJlc3RzKSlgIGFycmVzdHMgaW4gdGhlIGRhdGFzZXQsIGByIGNvbW1hKG5yb3coTWlzbWF0Y2hlZEFycmVzdHMpKWAgaGFkIGNvb3JkaW5hdGVzIGluIGEgbmVpZ2hib3Job29kIHBvbHlnb24KZGlmZmVyZW50IGZyb20gdGhlIG5laWdoYm9yaG9vZCB2YWx1ZSB0aGF0IGFwcGVhcnMgaW4gdGhlIGRhdGFzZXQuICBPZiB0aGVzZSwgb25seQpgciBucm93KE1pc21hdGNoZWRBcnJlc3RzICU+JSBmaWx0ZXIoIUdlb2NvZGVkQWRqYWNlbnQpKWAgb3ZlcmxheWVkCm5laWdoYm9yaG9vZCBwb2x5Z29ucyB3ZXJlIG5vdCBhZGphY2VudCB0byB0aGUgQlBELWFzc2lnbmVkIG5laWdoYm9yaG9vZCB2YWx1ZS0tYW5kIGFtb25nIHRoZXNlLCB2aXN1YWwgaW5zcGVjdGlvbiBpbmRpY2F0ZWQgdGhhdCBpbiBhbGwKY2FzZXMgdGhlIG5laWdoYm9yaG9vZHMgd2VyZSB2ZXJ5IGNsb3NlLiAgQ2FzdWFsIGluc3BlY3Rpb24gb2YgdGhlIExvY2F0aW9uIGZpZWxkIGluIHRoZSBCUEQgZGF0YXNldCwgd2hpY2ggaW5kaWNhdGVzIHRoZSBhZGRyZXNzIGF0IHdoaWNoCnRoZSBpbmNpZGVudCBvY2N1cnJlZCwgc3VnZ2VzdHMgdGhhdCBCUEQgImFwcHJveGltYXRlcyIgdGhlIGxvY2F0aW9uIGJ5IHBsYWNpbmcgaXQgYXQgdGhlIG5lYXJlc3QgaW50ZXJzZWN0aW9uIG9yIGJsb2NrLiBJZiB0aGlzIGFzc3VtcHRpb24KaXMgdHJ1ZSwgdGhlbiBvbmUgcGxhdXNpYmxlIGV4cGxhbmF0aW9uIGZvciB0aGUgbmVpZ2hib3Job29kIG1pc21hdGNoZXMgaXMgdGhhdCB0aGlzICJqaXR0ZXJpbmciIG9mIGluY2lkZW50IGxvY2F0aW9ucyBvY2Nhc2lvbmFsbHkKcGxhY2VzIHRoZSBsb2NhdGlvbiBpbiBhIG5lYXJieSBuZWlnaGJvcmhvb2QsIGlmIHRoZSAicmVhbCIgbG9jYXRpb24gd2FzIGNsb3NlIHRvIGEgbmVpZ2hib3Job29kIGJvdW5kYXJ5LgoKSXQgaXMgYWxzbyBwb3NzaWJsZSwgb2YgY291cnNlLCB0aGF0IHRoZSBjaXR5IChvciBCUEQpIHBlcmZvcm1lZCBhIG5laWdoYm9yaG9vZCBwb2x5Z29uIG92ZXJsYXkgdXNpbmcgdGhlIGFwcHJveGltYXRlZCBjb29yZGluYXRlcywKanVzdCBhcyBJIGRpZCwgYnV0IHVzaW5nIGRpZmZlcmVudCB0b29scyBvciBhIGRpZmZlcmVudCBtZXRob2RvbG9neSwgcHJvZHVjaW5nIHNsaWdodGx5IGRpZmZlcmVudCByZXN1bHRzIGluIGEgZmV3IGNhc2VzLgoKVGhpcyBjb25maXJtYXRpb24gb2YgdGhlIHJvYnVzdG5lc3Mgb2YgdGhlIGFwcHJveGltYXRlIGNvb3JkaW5hdGVzIGFsbG93cyB1cyB0byBjb25zaWRlciBhZGRpdGlvbmFsIHBvbHlnb24gb3ZlcmxheXMtLWFuZCBpbiBwYXJ0aWN1bGFyLAp1c2luZyBDZW5zdXMgc2hhcGVmaWxlcyB0byBhc3NpZ24gZWFjaCBhcnJlc3QgdG8gYSBDZW5zdXMgVHJhY3QuIFRoaXMgaW4gdHVybiB3aWxsIGVuYWJsZSBhIG11Y2ggd2lkZXIgcmFuZ2Ugb2YgYW5hbHlzZXMgdXNpbmcKQW1lcmljYW4gQ29tbXVuaXR5IFN1cnZleSAoQUNTKSBkYXRhLgoKIyMjIENlbnN1cyBUcmFjdHMKCkFmdGVyIG92ZXJsYXlpbmcgdGhlIGFycmVzdCBsb2NhdGlvbnMgb250byBjZW5zdXMgdHJhY3QgcG9seWdvbnMKaW4gdGhlIENlbnN1cyBbc2hhcGVmaWxlXShodHRwczovL3d3dy5jZW5zdXMuZ292L2dlby9tYXBzLWRhdGEvZGF0YS9jYmYvY2JmX3RyYWN0cy5odG1sKSwgd2UgYXJlIGFibGUgdG8gY3JlYXRlIGNob3JvcGxldGhzIHNpbWlsYXIgdG8KdGhlIG9uZXMgY3JlYXRlZCBhYm92ZSBmb3IgbmVpZ2hib3Job29kcy4gIE5vdGUgdGhhdCB0aGUgcG9wdWxhdGlvbiB2YWx1ZXMgZm9yIGVhY2ggY2Vuc3VzIHRyYWN0IGFyZSBmcm9tIHRoZSBmaXZlLXllYXIgZXN0aW1hdGVzCmluIHRoZSAyMDE1IEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgKEFDUykuCmBgYHtyLCBmaWcud2lkdGg9OX0KQ2Vuc3VzQmxvY2tTREYgJT4lCiAgZ2dwbG90KG1hcHBpbmc9YWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsPUFycmVzdHMpKSArCiAgICBnZW9tX3BhdGgoY29sb3I9J2dyZXk3MCcpICsKICAgIGdlb21fcGF0aChkYXRhPUNvdW50eVNERiwgY29sb3I9J2dyZXk1MCcpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIixoaWdoID0gInN0ZWVsYmx1ZSIsIG5hLnZhbHVlPSdncmV5OTAnLCBsYWJlbHM9Y29tbWEpICsKICAgIGNvb3JkX21hcCgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgbGFicyhmaWxsPSdBcnJlc3RzJywKICAgICAgIHRpdGxlPSdBcnJlc3RzIGluIEVhY2ggQmFsdGltb3JlIENlbnN1cyBUcmFjdCcsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSkKYGBgCmBgYHtyLCBmaWcud2lkdGg9OX0KQ2Vuc3VzQmxvY2tTREYgJT4lCiAgZ2dwbG90KG1hcHBpbmc9YWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSkgKwogICAgZ2VvbV9wb2x5Z29uKGFlcyhmaWxsPUFycmVzdHNQZXJDYXBpdGEpKSArCiAgICBnZW9tX3BhdGgoY29sb3I9J2dyZXk3MCcpICsKICAgIGdlb21fcGF0aChkYXRhPUNvdW50eVNERiwgY29sb3I9J2dyZXk1MCcpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIixoaWdoID0gInN0ZWVsYmx1ZSIsIG5hLnZhbHVlPSdncmV5OTAnLCBsYWJlbHM9Y29tbWEpICsKICAgIGNvb3JkX21hcCgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgbGFicyhmaWxsPSdBcnJlc3RzL0NhcGl0YScsCiAgICAgICB0aXRsZT0nQXJyZXN0cyBwZXIgQ2FwaXRhIGluIEVhY2ggQmFsdGltb3JlIENlbnN1cyBUcmFjdCcsCiAgICAgICBjYXB0aW9uPSdDZW5zdXMgVHJhY3QgcG9wdWxhdGlvbiBpcyBmcm9tIDIwMTUgNS15ZWFyIEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgKEFDUykgZXN0aW1hdGVzJywKICAgICAgIHN1YnRpdGxlPXBhc3RlMCgnQmFsdGltb3JlIENpdHksICcsIG1pbkRhdGVTLCAnIC0gJywgbWF4RGF0ZVMpKQpgYGAKIyMjIFdoYXQncyBIYXBwZW5pbmcgRG93bnRvd24/CgpJbiBib3RoIHRoZSBuZWlnaGJvcmhvb2QtYmFzZWQgYW5kIGNlbnN1cyB0cmFjdC1iYXNlZCBjaG9yb3BsZXRocywgdGhlIGRvd250b3duIGFyZWEgaGFzIHRoZSBoaWdoZXN0IG51bWJlciBvZiBhcnJlc3RzIG9mIGFueSBhcmVhCmluIHRoZSBjaXR5LCBhbmQgdGhlIGhpZ2hlc3QgKGNlbnN1cyB0cmFjdCkgYW5kIGZpZnRoLWhpZ2hlc3QgKG5laWdoYm9yaG9vZCkgbGV2ZWwgb2YgYXJyZXN0cyBwZXIgY2FwaXRhLiAgVGhlcmUgYXJlIG5laWdoYm9yaG9vZHMKd2l0aCBoaWdoZXIgbGV2ZWxzIG9mIGFycmVzdHMgcGVyIGNhcGl0YSwgbGlrZSBIb3BraW5zLUJheXZpZXcsIGJ1dCB0aGVzZSBhcmUgcmVsYXRpdmVseSBzbWFsbCBhcmVhcyB3aXRoIHNtYWxsIHBvcHVsYXRpb25zLiBUaGV5CmFyZSBwYXJ0IG9mIG11Y2ggbGFyZ2VyIGNlbnN1cyB0cmFjdHMgdGhhdCBpbmNsdWRlIGxvd2VyLWFycmVzdCBhcmVhcyBhcyB3ZWxsLCB3aGljaCByZXN1bHRzIGluIGZsYXR0ZW5pbmcgdGhlIGRlbnNpdHkgb24gdGhlIHRyYWN0LWJhc2VkCm1hcC4gV2hhdCdzIGNvbnNpc3RlbnQgYWNyb3NzIHRoZSB0d28gYXBwcm9hY2hlcyBpcyBzdHJvbmcgZXZpZGVuY2UgdGhhdCB0aGUgZG93bnRvd24gYXJlYSBleHBlcmllbmNlcyBhIGxvdCBvZiBhcnJlc3RzLgoKSGVyZSBpcyBhIGNsb3NlciBsb29rIGF0IHRoZSBkb3dudG93biBhcmVhOgpgYGB7ciwgZmlnLndpZHRoPTEwfQpnZ21hcChEb3dudG93bkdvb2dsZU1hcCkgKwogIGdlb21fcGF0aChkYXRhPUNlbnN1c0Jsb2NrU0RGICU+JSBmaWx0ZXIoR0VPSUQ9PScyNDUxMDA0MDEwMCcpLCBtYXBwaW5nPWFlcyh4PWxvbmcsIHk9bGF0LCBncm91cD1ncm91cCkpICsKICBnZW9tX3BvbHlnb24oZGF0YT1OZWlnaGJvcmhvb2RTREYgJT4lIGZpbHRlcihOYW1lICVpbiUgYygnRG93bnRvd24nLCAnSW5uZXIgSGFyYm9yJywgJ0Rvd250b3duIFdlc3QnKSksCiAgICAgICAgICAgICAgIG1hcHBpbmc9YWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwLCBmaWxsPU5hbWUpLCBhbHBoYT0uMzUpICsKICBsYWJzKGZpbGw9J05laWdoYm9yaG9vZCcsIHRpdGxlPSdOZWlnaGJvcmhvb2RzIG9mIERvd250b3duIEJhbHRpbW9yZScsCiAgICAgICBzdWJ0aXRsZT0nQ2Vuc3VzIFRyYWN0IDI0NTEwMDQwMTAwIG91dGxpbmVkIHdpdGggYSBibGFjayBsaW5lJykgKyB0aGVtZV92b2lkKCkKCmBgYApBbmQgdGhlIGFycmVzdCBhbmQgcG9wdWxhdGlvbiBkYXRhIGZvciB0aGVzZSBhcmVhcyBhcmU6CmBgYHtyfQpOZWlnaGJvcmhvb2REZiAlPiUKICBmaWx0ZXIoTmFtZSAlaW4lIGMoJ0Rvd250b3duJywgJ0lubmVyIEhhcmJvcicsICdEb3dudG93biBXZXN0JykpICU+JQogIHNlbGVjdChOZWlnaGJvcmhvb2Q9TmFtZSwgUG9wdWxhdGlvbiwgQXJyZXN0cywgYEFycmVzdHMgcGVyIENhcGl0YWA9QXJyZXN0c1BlckNhcGl0YSkgJT4lCiAgbXV0YXRlKFBvcHVsYXRpb249Y29tbWEoUG9wdWxhdGlvbiksIEFycmVzdHM9Y29tbWEoQXJyZXN0cykpICU+JQogIGthYmxlKGZvcm1hdD0naHRtbCcpICU+JSBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsICJjb25kZW5zZWQiKSkKYGBgCgpUaGUgY2Vuc3VzIHRyYWN0IGhhcyBhIHBvcHVsYXRpb24gb2YgYHIgY29tbWEoRG93bnRvd25DZW5zdXNUcmFjdCRQb3B1bGF0aW9uKWAsIHdpdGggYHIgY29tbWEoRG93bnRvd25DZW5zdXNUcmFjdCRBcnJlc3RzKWAgYXJyZXN0cyBhbmQKYHIgY29tbWEoRG93bnRvd25DZW5zdXNUcmFjdCRBcnJlc3RzUGVyQ2FwaXRhKWAgYXJyZXN0cyBwZXIgY2FwaXRhLgoKQXQgZmlyc3QgZ2xhbmNlLCBpdCBzZWVtcyB1bnVzdWFsIHRoYXQgdGhlIERvd250b3duIG5laWdoYm9yaG9vZCBoYXMgYSBwb3B1bGF0aW9uIG9mIG5lYXJseSAxLDAwMCBtb3JlIHJlc2lkZW50cyB0aGFuIHRoZQpsYXJnZWx5LW92ZXJsYXBwaW5nIGNlbnN1cyB0cmFjdC0tYSBkaWZmZXJlbmNlIG9mIGFyb3VuZCAzMCUuCkFsbW9zdCBhbGwgb2YgdGhlIERvd250b3duIG5laWdoYm9yaG9vZCBsaWVzIHdpdGhpbiB0aGUgY2Vuc3VzIHRyYWN0OyBvbmx5IHRoZSB0d28gY2l0eSBibG9ja3MgaW1tZWRpYXRlbHkgd2VzdCBvZgpMZXhpbmd0b24gTWFya2V0IChhIGxhbmRtYXJrIGluZG9vciBzaG9wcGluZyBlc3RhYmxpc2htZW50KSBsaWUgb3V0c2lkZSB0aGUgdHJhY3QuICBPbmUgb2YgdGhlc2UgYmxvY2tzIGlzIG9jY3VwaWVkIGJ5IHRoZSBwYXJraW5nIGdhcmFnZQpmb3IgdGhlIG1hcmtldDsgdGhlIG90aGVyIGluY2x1ZGVzIGEgbWl4IG9mIHJldGFpbCBzcGFjZSwgcHVibGljIGhvdXNpbmcsIGFuZCBzdHVkZW50IGhvdXNpbmcgZm9yIHRoZSBVbml2ZXJzaXR5IG9mIE1hcnlsYW5kLiBBdCB0aGUgc2FtZQp0aW1lLCB0aGUgb25lLWJsb2NrLXdpZGUgc3RyaXAgb24gdGhlIHNvdXRoZXJuIGJvcmRlciBvZiB0aGUgY2Vuc3VzIHRyYWN0IChib3VuZGVkIGJ5IExvbWJhcmQgYW5kIFByYXR0IHN0cmVldHMpLCBjb25zaXN0cyBtb3N0bHkKb2YgaG90ZWxzIGFuZCBjb21tZXJjaWFsIG9mZmljZSBzcGFjZSAoYXMgd2VsbCBhcyB0aGUgVW5pdGVkIFN0YXRlcyBjb3VydGhvdXNlKS4gIENlcnRhaW5seSwgdGhlIHJlc2lkZW50aWFsIHNwYWNlIHdlc3Qgb2YgdGhlIG1hcmtldAphY2NvdW50cyBmb3Igc29tZSBvZiB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBuZWlnaGJvcmhvb2QgYW5kIHRoZSBjZW5zdXMgdHJhY3QsIGJ1dCBpcyBwYXJ0aWFsbHkgb2Zmc2V0IGJ5IGEgc21hbGwgYW1vdW50IG9mCnJlc2lkZW50aWFsIHNwYWNlIHRoYXQgaXMgaW4gdGhlIGNlbnN1cyB0cmFjdCBidXQgb3V0c2lkZSB0aGUgRG93bnRvd24gbmVpZ2hib3Job29kLgoKSXQgb2NjdXJyZWQgdG8gbWUgdGhhdCBwb3B1bGF0aW9uIGNoYW5nZSBtaWdodCBleHBsYWluIHBhcnQgb2YgdGhlIGRpZmZlcmVuY2UsIHNpbmNlIG15ICh1bnZlcmlmaWVkKSBhc3N1bXB0aW9uIGlzIHRoYXQgdGhlCm5laWdoYm9yaG9vZC1sZXZlbCBwb3B1bGF0aW9uIGRhdGEgaW5jbHVkZWQgaW4gdGhlIGNpdHkncyBuZWlnaGJvcmhvb2Qgc2hhcGVmaWxlIGFyZSBiYXNlZCBvbiB0aGUgMjAxMCBjZW5zdXMuIEl0IGhhcyBiZWVuIHdlbGwtZG9jdW1lbnRlZAppbiB0aGUgW3ByZXNzXShodHRwOi8vd3d3LmJhbHRpbW9yZXN1bi5jb20vbmV3cy9tYXJ5bGFuZC9iYWx0aW1vcmUtY2l0eS9icy1iei1iYWx0aW1vcmUtcG9wdWxhdGlvbi1sb3NzLWp1bXBzLTIwMTcwMzIyLXN0b3J5Lmh0bWwpIHRoYXQKdGhlIGNpdHkncyBwb3B1bGF0aW9uIGhhcyBkZWNsaW5lZCBzaWduaWZpY2FudGx5LiAgQ291bGQgdGhhdCBleHBsYWluIHBhcnQgb2YgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgY2l0eS1yZXBvcnRlZCBuZWlnaGJvcmhvb2QKcG9wdWxhdGlvbiBhbmQgdGhlIDIwMTUgZml2ZS15ZWFyIEFDUyBlc3RpbWF0ZSBvZiBwb3B1bGF0aW9uIGZvciB0aGUgY2Vuc3VzIHRyYWN0PwpBcyBpdCB0dXJucyBvdXQsIHRoZSBwb3B1bGF0aW9uIG9mIGNlbnN1cyB0cmFjdCAyNDUxMDA0MDEwMCBhY3R1YWxseSBncmV3IGZyb20gMjAxMCB0byAyMDE1LCBhY2NvcmRpbmcgdG8gdGhlIEFDUyBlc3RpbWF0ZXMuIEFzIG5vdGVkIGFib3ZlLAppbiAyMDE1IGl0IHdhcyBgciBjb21tYShEb3dudG93bkNlbnN1c1RyYWN0JFBvcHVsYXRpb24pYDsgaW4gMjAxMCwgaXQgd2FzCmByIGNvbW1hKGFjc0RhdGEyMDEwICU+JSBmaWx0ZXIoR0VPSUQ9PScyNDUxMDA0MDEwMCcpICU+JSAuWzEsICdQb3B1bGF0aW9uJ10gJT4lIHVubGlzdCgpKWAuIAoKSXQgc2VlbXMgdGhhdCB3ZSBoYXZlIGEgZmFpcmx5IGdvb2QgKGFuZCBhY2N1cmF0ZSkgaGFuZGxlIG9uIHRoZSByZXNpZGVudGlhbCBwb3B1bGF0aW9uIGluIHRoZXNlIGFyZWFzLCBhbmQgdGhlcmVmb3JlIHRoZSBkZW5vbWluYXRvcgpvZiBvdXIgYXJyZXN0cyBwZXIgY2FwaXRhIG1lYXN1cmVzIHNlZW1zIHNvdW5kLiAgV2hhdCBhYm91dCB0aGUgbnVtZXJhdG9yOiBhcnJlc3RzPyBBcmUgdGhlcmUgcmVhbGx5IHNpZ25pZmljYW50bHkgbW9yZSBhcnJlc3RzIGRvd250b3duPwoKVGhlIGRlbnNpdHkgb2YgYXJyZXN0cyBpbiB0aGUgdGhyZWUgZG93bnRvd24gbmVpZ2hib3Job29kcyBsb29rcyBsaWtlIHRoaXMgKGtlZXBpbmcgaW4gbWluZCB0aGF0IGFycmVzdCBsb2NhdGlvbnMgYXJlIGFwcHJveGltYXRlKToKYGBge3IsIGZpZy53aWR0aD0xMH0KZ2dtYXAoRG93bnRvd25Hb29nbGVNYXApICsKICBnZW9tX2RlbnNpdHkyZChkYXRhPURvd250b3duQXJyZXN0cywgbWFwcGluZz1hZXMoeD1Mb25naXR1ZGUsIHk9TGF0aXR1ZGUpLCBiaW5zPTIwLCBuYS5ybT1UUlVFLCBhbHBoYT0uNSkgKwogIGdlb21fcG9pbnQoZGF0YT1CUERocSwgbWFwcGluZz1hZXMoeD1Mb25naXR1ZGUsIHk9TGF0aXR1ZGUpLCBzaGFwZT0yMywgc2l6ZT0zLCBjb2xvcj0nYmx1ZScsIGZpbGw9J3llbGxvdycpICsKICBsYWJzKHRpdGxlPSdEZW5zaXR5IG9mIERvd250b3duIEFyZWEgQXJyZXN0cycsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0Rvd250b3duLCBEb3dudG93biBXZXN0LCBhbmQgSW5uZXIgSGFyYm9yIE5laWdoYm9yaG9vZHMsICcsIG1pbkRhdGVTLCAnIC0gJywgbWF4RGF0ZVMpKSArCiAgdGhlbWVfdm9pZCgpCmBgYApDbGVhcmx5IHRoZSBoaWdoZXN0IGRlbnNpdHkgb2YgYXJyZXN0cyBvY2N1cnMgYWxvbmcgdGhlIElubmVyIEhhcmJvciB3YXRlcmZyb250LCBjZW50ZXJlZCBhcm91bmQgdGhlIGludGVyc2VjdGlvbiBvZiBQcmF0dCBhbmQgTGlnaHQKc3RyZWV0cywgd2l0aCBhbm90aGVyIGFyZWEgb2Ygc2xpZ2h0bHkgbG93ZXIgZGVuc2l0eSBvY2N1cnJpbmcgYSBmZXcgYmxvY2tzIGVhc3QgYWxvbmcgUHJhdHQgU3RyZWV0LiBUaGUgYmxvY2tzIHRvIHRoZSBlYXN0IG9mCkxleGluZ3RvbiBNYXJrZXQsIHJ1bm5pbmcgbm9ydGgtdG8tc291dGggYmV0d2VlbiBFdXRhdyBhbmQgSG93YXJkIFN0cmVldHMsIGFuZCBhIHNldmVuLWJsb2NrIGVhc3Qtd2VzdCBzdHJpcCBhbG9uZyBCYWx0aW1vcmUgU3RyZWV0Cm5vcnRoIG9mIHRoZSB3YXRlcmZyb250LCBhcmUgYWxzbyBoaWdoZXItZGVuc2l0eSBhcmVhcy4gIEZpbmFsbHksIHRoZSBuZXdlciBjb21tZXJjaWFsL3JldGFpbCBkZXZlbG9wbWVudCBhdCBIYXJib3IgRWFzdCBoYXMgYSBoaWdoCmFycmVzdCBjb25jZW50cmF0aW9uIGFzIHdlbGwuCgpUaGUgaGlnaC1kZW5zaXR5IGFyZWEgYXQgdGhlIGVhc3Rlcm4gZWRnZSBvZiB0aGUgRG93bnRvd24gbmVpZ2hib3Job29kIChhbmQgY2Vuc3VzIHRyYWN0KSwgYmV0d2VlbiBGYXlldHRlIGFuZCBCYWx0aW1vcmUgc3RyZWV0cywgaXMKY3VyaW91cyBkdWUgdG8gdGhlIHByb3hpbWl0eSBvZiBCYWx0aW1vcmUgUG9saWNlIERlcGFydG1lbnQgaGVhZHF1YXJ0ZXJzIChhcyB3ZWxsIGFzIHRoZSBoZWFkcXVhcnRlcnMgZm9yIHRoZSBDZW50cmFsIGRpc3RyaWN0KSwgaW5kaWNhdGVkCmJ5IHRoZSB5ZWxsb3cgZGlhbW9uZC4gVGhpcyBhcmVhIGlzIGhvbWUgdG8gQ2l0eSBIYWxsIGFuZCB0aGUgQ2lyY3VpdCBhbmQgRGlzdHJpY3QgY291cnRob3VzZXMsIGFuZCBhIHR5cGljYWwgcmFuZ2Ugb2YgZG93bnRvd24KYnVzaW5lc3Nlcywgc3VjaCBhcyBiYXJzLCByZXN0YXVyYW50cywgY29udmVuaWVuY2Ugc3RvcmVzLCBwYXJraW5nIGxvdHMsIGFuZCBiYW5rcy4gSXQgaXMgcGxhdXNpYmxlIHRoYXQgbWFueSBvZiB0aGUgYXJyZXN0cyB0aGF0Cm9jY3VyIGluIHRoaXMgYXJlYSBvY2N1ciBhdCB0aGUgcG9saWNlIGRlcGFydG1lbnQgZmFjaWxpdHksIG9yIHBlcmhhcHMgb25lIG9mIHRoZSBjb3VydGhvdXNlcywgd2hpbGUgdGhlIGFsbGVnZWQgY3JpbWUgdGhhdCBsZWQgdG8KdGhlIGFycmVzdCBvY2N1cnJlZCBlbHNld2hlcmUgaW4gdGhlIGNpdHkuIFVuZm9ydHVuYXRlbHkgdGhlcmUgaXMgbm90aGluZyBleHBsaWNpdCBpbiB0aGUgZGF0YXNldCB0aGF0IHdvdWxkIGFsbG93IHVzIHRvIGRpc2Nlcm4gdGhlc2UKYXJyZXN0cy4KClRoZSBkaXN0cmlidXRpb24gb2YgYXJyZXN0cyBieSB0eXBlIGlzIHNvbWV3aGF0IGRpZmZlcmVudCBpbiB0aGVzZSB0aHJlZSBkb3dudG93biBuZWlnaGJvcmhvb2RzLiBUaGVyZSBoYXZlIGJlZW4gY29uc2lkZXJhYmx5IG1vcmUgYXJyZXN0cwpmb3IgbGFyY2VueSwgd2hpY2ggaXMgcGVyaGFwcyBub3Qgc3VycHJpc2luZywgY29uc2lkZXJpbmcgdGhlIG51bWJlciBvZiByZXRhaWwgc2hvcHMgYW5kIHN0b3JlcyBpbiB0aGVzZSBuZWlnaGJvcmhvb2RzLiAgT24gdGhlIG90aGVyIGhhbmQsCnRoZXJlIGhhdmUgYmVlbiBzaWduaWZpY2FudGx5IGZld2VyIGJ1cmdsYXJpZXMgYW5kIG1vdG9yIHZlaGljbGUgdGhlZnRzLCB3aGljaCBpcyBhbHNvIHVuZGVyc3RhbmRhYmxlIGluIG5laWdoYm9yaG9vZHMgd2l0aCBsZXNzCnJlc2lkZW50aWFsIHNwYWNlIG92ZXJhbGwgKGFuZCBhbG1vc3QgYWxsIG9mIHRoZSByZXNpZGVudGlhbCBzcGFjZSB0aGF0IGRvZXMgZXhpc3QgYmVpbmcgYXBhcnRtZW50IGJ1aWxkaW5ncyB0aGF0IGFyZSBsZXNzIHN1c2NlcHRpYmxlIHRvCmJ1cmdsYXJ5LikKCmBgYHtyfQpEb3dudG93bkFycmVzdHMgJT4lIGdyb3VwX2J5KFR5cGUpICU+JSBzdW1tYXJpemUobj1uKCkpICU+JSBtdXRhdGUocGN0PW4vc3VtKG4pLCBwY3RzPXBlcmNlbnQocGN0KSkgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fYmFyKG1hcHBpbmc9YWVzKHg9cmVvcmRlcihUeXBlLCBuKSwgeT1uKSwgc3RhdD0naWRlbnRpdHknKSArIGNvb3JkX2ZsaXAoKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gaWRlbnRpdHksIGdlb209InRleHQiLCBhZXMoeD1yZW9yZGVyKFR5cGUsIG4pLCB5PW4sIGxhYmVsPXBjdHMpLCBoanVzdCA9IC0uMjUpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPWNvbW1hLCBsaW1pdHM9YygwLDEwMDAwKSkgKwogIHRoZW1lX2Vjb25vbWlzdCgpICsKICBsYWJzKHg9J0NyaW1lIFR5cGUnLCB5PSdJbmNpZGVudCBDb3VudCcsIHRpdGxlPSdEaXN0cmlidXRpb24gb2YgQXJyZXN0cyBieSBDcmltZSBUeXBlJywKICAgICAgIHN1YnRpdGxlPXBhc3RlMCgnRG93bnRvd24gQmFsdGltb3JlIE5laWdoYm9yaG9vZHMsICcsIG1pbkRhdGVTLCAnIC0gJywgbWF4RGF0ZVMpLAogICAgICAgY2FwdGlvbj1wYXN0ZTAoJ249JywgY29tbWEobnJvdyhEb3dudG93bkFycmVzdHMpKSkpCmBgYAojIyBXaGVuIGRvIEFycmVzdHMgT2NjdXI/CgpMb29raW5nIGF0IHRoZSBnZW9ncmFwaGljIGRpc3RyaWJ1dGlvbiBvZiBhcnJlc3RzIGlzIGludGVyZXN0aW5nLCBidXQgd2UgY2FuIGdhaW4gYWRkaXRpb25hbCBpbnNpZ2h0cyBmcm9tIGV4YW1pbmluZyB0aGUgdGVtcG9yYWwKZGltZW5zaW9uLS13aGVuIGFycmVzdHMgb2NjdXIuIENvbnNpZGVyYXRpb24gb2YgdGhlIGRhdGUgYW5kIHRpbWUgb2YgYXJyZXN0cyBpcyBwYXJ0aWN1bGFybHkgdmFsdWFibGUgdG8gcG9saWNlIGNvbW1hbmQgc3RhZmYsIGJ1dApjYW4gYWxzbyBnaXZlIGEgc2Vuc2Ugb2YgdGhlIHNlYXNvbmFsIGFuZCBkaXVybmFsIHZhcmlhdGlvbnMgaW4gY3JpbWluYWwgYWN0aXZpdHkgYW5kIHBvbGljZSByZXNwb25zZSB0byBpdC4KClRoZSBmb2xsb3dpbmcgdmlzdWFsaXphdGlvbiBzaG93cyBhICJoZWF0IG1hcCIgb2YgdGhlIHRpbWUgb2YgZGF5IGFuZCBkYXkgb2YgdGhlIHllYXIsIGluIDIwMTUsIHdpdGggYXJyZXN0cyBwZXIgaG91ciBpbmRpY2F0ZWQgYnkKdGhlIGRlZ3JlZSBvZiBzaGFkaW5nIGluIGVhY2ggaG91cmx5IGNlbGw6CgpgYGB7ciwgZmlnLndpZHRoPTExfQpBcnJlc3RzICU+JSBmaWx0ZXIoeWVhcihBcnJlc3REYXRlKT09MjAxNSkgJT4lCiAgZ3JvdXBfYnkoQXJyZXN0RGF0ZSwgQXJyZXN0SG91cikgJT4lCiAgc3VtbWFyaXplKG49bigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShBcnJlc3REYXRlLCBBcnJlc3RIb3VyKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV90aWxlKGFlcyh4PUFycmVzdERhdGUsIHk9QXJyZXN0SG91ciwgZmlsbD1uKSwgbmEucm09VFJVRSkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gIndoaXRlIixoaWdoID0gInN0ZWVsYmx1ZSIpICsgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHM9MDoyMykgKwogIHNjYWxlX3hfZGF0ZShkYXRlX2JyZWFrcz0nMSBtb250aHMnLCBkYXRlX2xhYmVscz0nJWItJWUnKSArCiAgdGhlbWUoYXhpcy50aWNrcy55PWVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X2JsYW5rKCksIGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGxhYnMoeD1OVUxMLCB5PU5VTEwsIGZpbGw9J0FycmVzdHMvaG91cicsIHRpdGxlPSdBcnJlc3RzIGJ5IFRpbWUgb2YgRGF5LCBlYWNoIGRheSBpbiAyMDE1JywKICAgICAgIHN1YnRpdGxlPSdBcnJlc3RzIE1hZGUgYnkgdGhlIEJhbHRpbW9yZSBQb2xpY2UgRGVwYXJ0bWVudCcpCgpgYGAKQSBxdWljayBnbGFuY2UgYXQgdGhpcyB2aXN1YWxpemF0aW9uIHN1Z2dlc3RzIHNvbWUgaHlwb3RoZXNlcyBhYm91dCBjcmltZSBhbmQgcG9saWNlIHJlc3BvbnNlIGR1cmluZyAyMDE1OgoKKiBUaGUgY2l2aWwgdW5yZXN0IHRoYXQgZm9sbG93ZWQgdGhlIEZyZWRkaWUgR3JheSBkZWF0aC1pbi1jdXN0b2R5IHNpdHVhdGlvbiBpbiBsYXRlIEFwcmlsIDIwMTUgaXMgdmVyeSBhcHBhcmVudAoqIE5pZ2h0dGltZSAoYW5kIHBhcnRpY3VsYXJseSBlYXJseSBtb3JuaW5nKSBhcnJlc3RzIHNlZW0gdG8gZGVjbGluZSBpbiB0aGUgd2ludGVyIG1vbnRocywgYW5kIGluY3JlYXNlIGluIHRoZSB3YXJtIG1vbnRocyBvZiBKdWx5LUF1Z3VzdAoqIFRoZSAxOjAwIGFtIGhvdXIgc2VlbXMgdG8gaGF2ZSBhIHNwaWtlIGluIGFycmVzdHMKKiBUaGVyZSBhcmUgc2V2ZXJhbCBvdGhlciBjZWxscyBpbiB0aGUgaGVhdG1hcC0tbm90YWJseSA5OjAwIHBtIG9uIERlY2VtYmVyIDE1LS10aGF0IGhhdmUgYW4gdW51c3VhbCBudW1iZXIgb2YgYXJyZXN0czsgdGhlc2UgY291bGQgYmUKc2ltcGxlIHJhbmRvbSBmbHVjdHVhdGlvbnMgb3IgcmVjb3JkLWtlZXBpbmcgYW5vbWFsaWVzICh3aGljaCBzZWVtcyB0byBiZSB0aGUgY2FzZSBvbiAxMi8xNSwgc2luY2UgYSBHb29nbGUgc2VhcmNoIHJldmVhbHMgbm8KZXh0cmFvcmRpbmFyeSBldmVudHMgb24gdGhhdCBkYXRlKQoKVGhlIHBoZW5vbWVub24gb2YgZWFybHkgbW9ybmluZyBhcnJlc3RzIChtaWRuaWdodCAtIDI6MDAgYW0gaG91cnMpIG1lcml0cyBhIGNsb3NlciBsb29rIHRvIGV4YW1pbmUgdGhlIGFzc29jaWF0ZWQgY3JpbWUgdHlwZXM6CmBgYHtyLCBmaWcud2lkdGg9OH0KdGRmIDwtIEFycmVzdHMgJT4lIGZpbHRlcihBcnJlc3RIb3VyICVpbiUgYygwLDEsMikpCnRkZiAlPiUgZ3JvdXBfYnkoVHlwZSkgJT4lIHN1bW1hcml6ZShuPW4oKSkgJT4lIG11dGF0ZShwY3Q9bi9zdW0obiksIHBjdHM9cGVyY2VudChwY3QpKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9iYXIobWFwcGluZz1hZXMoeD1yZW9yZGVyKFR5cGUsIG4pLCB5PW4pLCBzdGF0PSdpZGVudGl0eScpICsgY29vcmRfZmxpcCgpICsKICBzdGF0X3N1bW1hcnkoZnVuLnkgPSBpZGVudGl0eSwgZ2VvbT0idGV4dCIsIGFlcyh4PXJlb3JkZXIoVHlwZSwgbiksIHk9biwgbGFiZWw9cGN0cyksIGhqdXN0ID0gLS4yNSkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9Y29tbWEsIGxpbWl0cz1jKDAsODAwMCkpICsKICB0aGVtZV9lY29ub21pc3QoKSArCiAgbGFicyh4PSdDcmltZSBUeXBlJywgeT0nSW5jaWRlbnQgQ291bnQnLCB0aXRsZT0nRGlzdHJpYnV0aW9uIG9mIEFycmVzdHMgYnkgQ3JpbWUgVHlwZScsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCBhcnJlc3RzIG9jY3VycmluZyBiZXR3ZWVuIG1pZG5pZ2h0IGFuZCAzOjAwIGFtLCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSwKICAgICAgIGNhcHRpb249cGFzdGUwKCduPScsIGNvbW1hKG5yb3codGRmKSkpKQpybSh0ZGYpCmBgYApTb21ld2hhdCBleHBlY3RlZGx5LCBhYm91dCB0aGlydHkgcGVyY2VudCBvZiBhcnJlc3RzIGluIHRoZSB2ZXJ5IGVhcmx5IG1vcm5pbmcgaG91cnMgYXJlIGZvciB2aW9sZW50IGNyaW1lcyAoYWdncmF2YXRlZCBhc3NhdWx0LCByb2JiZXJ5LApyYXBlLCBhbmQgbXVyZGVyKSBhbmQgYW5vdGhlciB0d2VudHktZml2ZSBwZXJjZW50IGFyZSBmb3Igbm9uLWFnZ3JhdmF0ZWQgYXNzYXVsdC4gUHJvcGVydHkgY3JpbWVzIGFyZSBsZXNzIHByZXZhbGVudCBpbiB0aGUgbWlkZGxlIG9mCnRoZSBuaWdodC4KCiMjIFZpb2xlbnQgQ3JpbWUKClRoZXJlIGlzIGNvbnNpZGVyYWJsZSB2YXJpYXRpb24gaW4gd2hlcmUgdmlvbGVudCBjcmltZSBvY2N1cnMgYWNyb3NzIHRoZSBjaXR5LiAgKEluIGFsaWdubWVudCB3aXRoIEZCSSBndWlkYW5jZSwgd2UgZGVmaW5lICJ2aW9sZW50IGNyaW1lIgphczogbXVyZGVyLCByYXBlLCByb2JiZXJ5LCBzaG9vdGluZywgYW5kIGFnZ3JhdmF0ZWQgYXNzYXVsdCk6CgpgYGB7ciwgZmlnLndpZHRoPTl9CkNlbnN1c0Jsb2NrU0RGICU+JQogIG11dGF0ZShWQ1BlcmNlbnQ9VmlvbGVudENyaW1lQXJyZXN0cy9BcnJlc3RzKSAlPiUKICBnZ3Bsb3QobWFwcGluZz1hZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApKSArCiAgICBnZW9tX3BvbHlnb24oYWVzKGZpbGw9VkNQZXJjZW50KSkgKwogICAgZ2VvbV9wYXRoKGNvbG9yPSdncmV5NzAnKSArCiAgICBnZW9tX3BhdGgoZGF0YT1Db3VudHlTREYsIGNvbG9yPSdncmV5NTAnKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsaGlnaCA9ICJzdGVlbGJsdWUiLCBuYS52YWx1ZT0nZ3JleTkwJywgbGFiZWxzPXBlcmNlbnQpICsKICAgIGNvb3JkX21hcCgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgbGFicyhmaWxsPSclIFZpb2xlbnQgQ3JpbWUnLAogICAgICAgdGl0bGU9J1BlcmNlbnRhZ2Ugb2YgQXJyZXN0cyBmb3IgVmlvbGVudCBDcmltZSBpbiBFYWNoIEJhbHRpbW9yZSBDZW5zdXMgVHJhY3QnLAogICAgICAgY2FwdGlvbj0nQ2Vuc3VzIFRyYWN0IHBvcHVsYXRpb24gaXMgZnJvbSAyMDE1IDUteWVhciBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IChBQ1MpIGVzdGltYXRlcycsCiAgICAgICBzdWJ0aXRsZT1wYXN0ZTAoJ0JhbHRpbW9yZSBDaXR5LCAnLCBtaW5EYXRlUywgJyAtICcsIG1heERhdGVTKSkKYGBgCgojIyBab25pbmcsIExhbmQgVXNlLCBhbmQgQXJyZXN0cwoKVGhlIHN0YXRlIG9mIE1hcnlsYW5kJ3MgRGVwYXJ0bWVudCBvZiBQbGFubmluZyBwcm92aWRlcyBhIHRvb2wgY2FsbGVkCltNZFByb3BlcnR5IFZpZXddKGh0dHA6Ly9wbGFubmluZy5tYXJ5bGFuZC5nb3YvT3VyUHJvZHVjdHMvUHJvcGVydHlNYXBQcm9kdWN0cy9NRFByb3BlcnR5Vmlld1Byb2R1Y3RzLnNodG1sKSwKdGhyb3VnaCB3aGljaCB1c2VycyBjYW4gb2J0YWluIGEgd2lkZSByYW5nZSBvZiBHSVMgaW5mb3JtYXRpb24uIFRoZSBEZXBhcnRtZW50IGFsc28gbWFrZXMgdGhlIHVuZGVybHlpbmcgc2hhcGVmaWxlcyBmb3IgZWFjaCBjb3VudHkKKGFuZCBCYWx0aW1vcmUgQ2l0eSkgClthdmFpbGFibGVdKGh0dHA6Ly9wbGFubmluZy5tYXJ5bGFuZC5nb3YvT3VyUHJvZHVjdHMvZG93bmxvYWRGaWxlcy5zaHRtbCkuICBBZnRlciBhIGxpdHRsZSByZWNvZGluZyB0byBzaW1wbGlmeSB0aGUgY2F0ZWdvcmllcywgd2UgYXJlCmFibGUgdG8gcHJvZHVjZSBzdW1tYXJ5IHZpc3VhbGl6YXRpb25zIGZvciBCYWx0aW1vcmUgbGlrZSB0aGlzIG9uZSBmb3IgbGFuZCB1c2U6CmBgYHtyLCBmaWcud2lkdGg9OX0KZ2dwbG90KGRhdGE9bGFuZFVzZVNERiwgbWFwcGluZz1hZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApKSArCiAgZ2VvbV9wb2x5Z29uKG1hcHBpbmc9YWVzKGZpbGw9TGFuZFVzZUNhdGVnb3J5KSkgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHR5cGU9J3F1YWwnLCBwYWxldHRlPSdBY2NlbnQnKSArCiAgY29vcmRfbWFwKCkgKyB0aGVtZV92b2lkKCkgKyBsYWJzKGZpbGw9J0xhbmQgVXNlJywgdGl0bGU9J0xhbmQgVXNlIGluIEJhbHRpbW9yZSBDaXR5JywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYXB0aW9uPSdTb3VyY2U6IE1hcnlsYW5kIERlcGFydG1lbnQgb2YgUGxhbm5pbmcsIDIwMTMnKQpgYGAKQW5kIGEgc2ltaWxhciBvbmUgZm9yIHpvbmluZzoKYGBge3IsIGZpZy53aWR0aD05fQpnZ3Bsb3QoZGF0YT16b25pbmdTREYsIG1hcHBpbmc9YWVzKHg9bG9uZywgeT1sYXQsIGdyb3VwPWdyb3VwKSkgKwogIGdlb21fcG9seWdvbihtYXBwaW5nPWFlcyhmaWxsPUdFTlpPTkUpKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIodHlwZT0ncXVhbCcsIHBhbGV0dGU9J0FjY2VudCcpICsKICBjb29yZF9lcXVhbCgpICsgdGhlbWVfdm9pZCgpICsgbGFicyhmaWxsPSdab25pbmcgVHlwZScsIHRpdGxlPSdab25pbmcgaW4gQmFsdGltb3JlIENpdHknLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhcHRpb249J1NvdXJjZTogTWFyeWxhbmQgRGVwYXJ0bWVudCBvZiBQbGFubmluZywgMjAxMycpCmBgYAoKSW4gd2hhdCBmb2xsb3dzIGhlcmUsIEkgd2lsbCBmb2N1cyBvbiB0aGUgTGFuZCBVc2UgZGF0YXNldCwgc2luY2UgaXQgY2FwdHVyZXMgaG93IGFyZWFzIGFyZSBhY3R1YWxseSBiZWluZyB1c2VkLCB2ZXJzdXMgaG93IHRoZSBjaXR5CmdvdmVybm1lbnQgaGFzIGNsYXNzaWZpZWQgb3IgZGVzaWduYXRlZCBsYW5kIGZvciB1c2UuICBOb3RlIHRoYXQgdGhlIExhbmQgVXNlIGRhdGEgYXJlIGZyb20gYSBzdHVkeSBjb25kdWN0ZWQgaW4gMjAxMCwgdGhvdWdoIHRoZQpEZXBhcnRtZW50IG9mIFBsYW5uaW5nIGFzc2VtYmxlZCB0aGUgZGF0YXNldCBpbiAyMDEzLgoKQnkgbWVyZ2luZyB0aGUgbGFuZCB1c2UgYW5kIGNlbnN1cyB0cmFjdCBzaGFwZWZpbGVzLCBhbmQgb3ZlcmxheWluZyBwb2x5Z29ucywgd2UgYXJlIGFibGUgdG8gY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlIG9mIGVhY2ggY2Vuc3VzCnRyYWN0J3MgYXJlYSBpbiBlYWNoIG9mIHRoZSBsYW5kIHVzZSBjYXRlZ29yaWVzLiBUaGlzIGluIHR1cm4gYWxsb3dzIHVzIHRvIHZpc3VhbGl6ZSwgZm9yIGV4YW1wbGUsIHRoZSBleHRlbnQgb2YgcmVzaWRlbnRpYWwgbGFuZAp1c2UgYWNyb3NzIHRoZSBjaXR5OgoKYGBge3IsIGZpZy53aWR0aD05fQpDZW5zdXNCbG9ja1NERiAlPiUKICBnZ3Bsb3QobWFwcGluZz1hZXMoeD1sb25nLCB5PWxhdCwgZ3JvdXA9Z3JvdXApKSArCiAgICBnZW9tX3BvbHlnb24oYWVzKGZpbGw9UmVzaWRlbnRpYWxQZXJjZW50YWdlKSkgKwogICAgZ2VvbV9wYXRoKGNvbG9yPSdncmV5NzAnKSArCiAgICBnZW9tX3BhdGgoZGF0YT1Db3VudHlTREYsIGNvbG9yPSdncmV5NTAnKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJ3aGl0ZSIsaGlnaCA9ICJzdGVlbGJsdWUiLCBuYS52YWx1ZT0nZ3JleTkwJywgbGFiZWxzPXBlcmNlbnQpICsKICAgIGNvb3JkX21hcCgpICsKICAgIHRoZW1lX3ZvaWQoKSArCiAgbGFicyhmaWxsPSclIFJlc2lkZW50aWFsJywKICAgICAgIHRpdGxlPSdQZXJjZW50YWdlIG9mIENlbnN1cyBUcmFjdCBBcmVhIENsYXNzaWZpZWQgYXMgUmVzaWRlbnRpYWwnLAogICAgICAgY2FwdGlvbj0nQ2xhc3NpZmljYXRpb24gc291cmNlOiBNYXJ5bGFuZCBEZXBhcnRtZW50IG9mIFBsYW5uaW5nLCAyMDEzJykKYGBgCgpDZW5zdXMgdHJhY3QgbGFuZCB1c2UgY2xhc3NpZmljYXRpb24gYWxzbyBhbGxvd3MgdXMgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgYXJyZXN0cyBwZXIgY2FwaXRhLCBieSB0cmFjdCwgaW4gdGVybXMgb2YgbGFuZCB1c2UKYW5kIGhvdXNlaG9sZCBpbmNvbWU6CmBgYHtyLCBmaWcud2lkdGg9OX0KQ2Vuc3VzQmxvY2tEZiAlPiUgbXV0YXRlKFBlcmNlbnRWaW9sZW50Q3JpbWU9VmlvbGVudENyaW1lQXJyZXN0cy9BcnJlc3RzKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeD1NZWRpYW5Ib3VzZWhvbGRJbmNvbWUsIHk9UmVzaWRlbnRpYWxQZXJjZW50YWdlLCBzaXplPUFycmVzdHNQZXJDYXBpdGEsIGNvbG9yPVBlcmNlbnRWaW9sZW50Q3JpbWUpLCBuYS5ybT1UUlVFKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gIiNjNmRiZWYiLCBoaWdoID0gIiMwODMwNmIiLCBsYWJlbHM9cGVyY2VudCkgKwogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHM9cGVyY2VudCkgKwogIHNjYWxlX3hfY29udGludW91cyhsYWJlbHM9ZG9sbGFyKSArCiAgbGFicygKICAgIHk9JyUgb2YgVHJhY3QgQXJlYSBDbGFzc2lmaWVkIGFzIFJlc2lkZW50aWFsJywKICAgIHg9J01lZGlhbiBBbm51YWwgSG91c2Vob2xkIEluY29tZScsCiAgICBjb2xvcj0nJSBWaW9sZW50IENyaW1lJywKICAgIHNpemU9J0FycmVzdHMvQ2FwaXRhJywKICAgIHRpdGxlPSdDZW5zdXMgVHJhY3QgQXJyZXN0cyBQZXIgQ2FwaXRhLCBieSBMYW5kIFVzZSBhbmQgSW5jb21lJywKICAgIHN1YnRpdGxlPXBhc3RlMCgnQmFsdGltb3JlIENpdHksICcsIG1pbkRhdGVTLCAnIC0gJywgbWF4RGF0ZVMpCiAgKSArCiAgdGhlbWVfZWNvbm9taXN0KCkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwKSwgbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKQpgYGAKCkNsZWFybHkgaG91c2Vob2xkIGluY29tZSBpcyBhIG11Y2ggc3Ryb25nZXIgaW5kaWNhdG9yIG9mIGJvdGggYXJyZXN0cyBwZXIgY2FwaXRhIGFuZCBwcmV2YWxlbmNlIG9mIHZpb2xlbnQgY3JpbWUgdGhhbiBsYW5kIHVzZS4gIEhpZ2hseQpyZXNpZGVudGlhbCBhcmVhcyBleHBlcmllbmNlIGFib3V0IHRoZSBzYW1lIG51bWJlciBvZiBhcnJlc3RzIHBlciBjYXBpdGEgYXMgbm9uLXJlc2lkZW50aWFsIChlLmcuLCBjb21tZXJjaWFsIG9yIGluZHVzdHJpYWwpIGFyZWFzLCBidXQKYXJlYXMgd2l0aCBwb29yZXIgaG91c2Vob2xkcyBleHBlcmllbmNlIGEgaGlnaGVyIG51bWJlciBvZiBhcnJlc3RzIHBlciBjYXBpdGEsIGFuZCBhIGxhcmdlciBwZXJjZW50YWdlIG9mIHZpb2xlbnQgY3JpbWUgYXMgd2VsbC4KCldlIHNlZSBhIHNpbWlsYXIgcGF0dGVybiwgYWxiZWl0IHdpdGggYSBtb3JlIHVuaWZvcm0geC1heGlzIGRpc3RyaWJ1dGlvbiwgd2hlbiB3ZSByZXBsYWNlIGhvdXNlaG9sZCBpbmNvbWUgd2l0aCB0aGUgcGVyY2VudGFnZSBvZiB0aGUgdHJhY3QKcG9wdWxhdGlvbiB0aGF0IGlzIHdoaXRlOgpgYGB7ciwgZmlnLndpZHRoPTl9CkNlbnN1c0Jsb2NrRGYgJT4lIG11dGF0ZShQZXJjZW50VmlvbGVudENyaW1lPVZpb2xlbnRDcmltZUFycmVzdHMvQXJyZXN0cykgJT4lCiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHg9UGVyY2VudFdoaXRlLCB5PVJlc2lkZW50aWFsUGVyY2VudGFnZSwgc2l6ZT1BcnJlc3RzUGVyQ2FwaXRhLCBjb2xvcj1QZXJjZW50VmlvbGVudENyaW1lKSwgbmEucm09VFJVRSkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9ICIjYzZkYmVmIiwgaGlnaCA9ICIjMDgzMDZiIiwgbGFiZWxzPXBlcmNlbnQpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXBlcmNlbnQpICsKICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXBlcmNlbnQpICsKICBsYWJzKAogICAgeT0nJSBvZiBUcmFjdCBBcmVhIENsYXNzaWZpZWQgYXMgUmVzaWRlbnRpYWwnLAogICAgeD0nJSBvZiBUcmFjdCBQb3B1bGF0aW9uIHRoYXQgaXMgV2hpdGUnLAogICAgY29sb3I9JyUgVmlvbGVudCBDcmltZScsCiAgICBzaXplPSdBcnJlc3RzL0NhcGl0YScsCiAgICB0aXRsZT0nQ2Vuc3VzIFRyYWN0IEFycmVzdHMgUGVyIENhcGl0YSwgYnkgTGFuZCBVc2UgYW5kIFJhY2UnLAogICAgc3VidGl0bGU9cGFzdGUwKCdCYWx0aW1vcmUgQ2l0eSwgJywgbWluRGF0ZVMsICcgLSAnLCBtYXhEYXRlUykKICApICsKICB0aGVtZV9lY29ub21pc3QoKSArCiAgdGhlbWUobGVnZW5kLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTApLCBsZWdlbmQucG9zaXRpb249J2JvdHRvbScpCmBgYAoK