Getting Started

This sheet builds off of “Handling Census Data in R - Part 1”. Please complete that first

Again, everytime you start R you will need to load any libraries that you need for analysis. You will need to run this bit of code every time you open R to get this to work.

library(tidycensus)
library(sf)
library(ggplot2)
library(ggthemes)
library(dplyr)

To get started working with tidycensus, users should load the package along with the tidyverse package, and set their Census API key. A key can be obtained from Census API Key. It will provide you with a 40 digit text string. Please keep track of this number. Store it in a safe place.

API_Key = '983980b9fc504149e82117csdfwefwe23dsdc507'  # non working example - please paste your own in
census_api_key(API_Key, install = TRUE, overwrite=TRUE)  

Comparing Populations Across Geographies

Let’s say you want to look at the differences in income between different parts of city, let’s say by quadrant (NE,NW,SE,SW). This can easily done using spatial statistics, joining income data into the appropriate quadrant by location.

The first step is going to be to access the DC quatrant shapefile. We can directly access data online easily here. To access the data online we just need the URL of the GeoJson file as seen below:

For this we will use read_sf from the sf package. SF will allow us to do pretty much anything that arcmap can do. In this case read_sf just reads the object from the internet for you and stores it in the same format as our census data.

Notice this is just the attribute data with a column called geometry that stores the outline of each river.

# read quadrant data from the internet
quads = read_sf('https://opendata.arcgis.com/datasets/02923e4697804406b9ee3268a160db99_11.geojson')
ggplot()+geom_sf(data=quads, fill='grey80', color='white')

Now let’s get the median income data for dc tract and plot it underneath the quadrants. Notice that I set fill = NA which makes the quadant polygons transparent, and color='white' sets thier outline color to white. Again these are not inside of aes() because we only use aes() if we want an asthetic property to be set from values in the attribute table.

# download the acs data 
med_inc =  get_acs(state = "DC", 
                   geography = "tract", 
                   year=2018,
                  variables =  c(med_inc = 'B19101_001'), 
                  geometry = TRUE,
                  output = 'wide')   # just do this
Getting data from the 2014-2018 5-year ACS
Downloading feature geometry from the Census website.  To cache shapefiles for use in future sessions, set `options(tigris_use_cache = TRUE)`.
Using FIPS code '11' for state 'DC'
# print the column names
print(names(med_inc))
[1] "GEOID"    "NAME"     "med_incE" "med_incM" "geometry"
# plot both
ggplot()+
        # plot median income
        geom_sf(data=med_inc, aes(fill = med_incE))+
  
        # plot quadrants
        geom_sf(data=quads, fill=NA, color='white')

Keep in mind the old addage ‘mapping is not doing’, ok I just made that up. But really we haven’t done any analysis here. We need to combine the attribute tables of med_inc and quads. We can do this by using a spatial aggregation using the sf package. Here we are going to get the median income of all census tracts within each quadrant (NE,NW,SE,SW).

# Calculate the median income of each quadrant
aggregate(med_inc, quads, 
          median_quad_income =  function(x) median(x,na.rm=T))  # na.rm=T avoids missing values
Error in st_geos_binop("intersects", x, y, sparse = sparse, prepared = prepared) : 
  st_crs(x) == st_crs(y) is not TRUE

Oops looks like the crs (coordinate reference system) isn’t the same for both layers. Let’s reproject them to “NAD83” details here using thier EPSG code, which can be used to uniquely identify any projection.

# reproject the data to to NAD_83  https://epsg.io/4269
med_inc = st_transform(med_inc, crs=4269)  
quads = st_transform(quads, crs=4269)  

Now let’s try getting the median income for each quadrant.

# 
quad_med_inc = aggregate(med_inc, quads,  FUN= median)  
although coordinates are longitude/latitude, st_intersects assumes that they are planar
quad_med_inc
Simple feature collection with 4 features and 4 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: -77.1198 ymin: 38.79164 xmax: -76.90915 ymax: 38.99597
epsg (SRID):    4269
proj4string:    +proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs
        GEOID                                                           NAME med_incE med_incM                       geometry
1 11001009807    Census Tract 65, District of Columbia, District of Columbia      598      124 POLYGON ((-77.06442 38.8895...
2 11001007604 Census Tract 75.04, District of Columbia, District of Columbia      628      111 POLYGON ((-77.00923 38.8393...
3 11001008701 Census Tract 84.02, District of Columbia, District of Columbia      617      106 POLYGON ((-77.0092 38.96498...
4 11001002801    Census Tract 32, District of Columbia, District of Columbia      699      117 POLYGON ((-77.1198 38.93435...
ggplot()+ geom_sf(data=quad_med_inc, aes(fill = med_incE))

Well that isn’t very interesting. Instead it might be more interesting to look at the distribution of census tract incomes in each quadrant, so we need to join the name of the quadrant to each census tract using a spatial join. A spatial join is done in sf with the st_join function.

med_inc_quads = st_join(med_inc, quads)
although coordinates are longitude/latitude, st_intersects assumes that they are planar
med_inc_quads
Simple feature collection with 216 features and 9 fields
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: -77.11976 ymin: 38.79164 xmax: -76.9094 ymax: 38.99511
epsg (SRID):    4269
proj4string:    +proj=longlat +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +no_defs
First 10 features:
          GEOID                                                         NAME.x med_incE med_incM OBJECTID QUADRANT    NAME.y
1   11001009902 Census Tract 99.02, District of Columbia, District of Columbia      586      118        2       SE Southeast
2   11001009904 Census Tract 99.04, District of Columbia, District of Columbia      506       85        2       SE Southeast
2.1 11001009904 Census Tract 99.04, District of Columbia, District of Columbia      506       85        3       NE Northeast
3   11001000100     Census Tract 1, District of Columbia, District of Columbia     1206      180        4       NW Northwest
4   11001000801  Census Tract 8.01, District of Columbia, District of Columbia     1229      165        4       NW Northwest
5   11001000802  Census Tract 8.02, District of Columbia, District of Columbia      747      136        4       NW Northwest
6   11001001001 Census Tract 10.01, District of Columbia, District of Columbia     1910      127        4       NW Northwest
7   11001006202 Census Tract 62.02, District of Columbia, District of Columbia        0       12        1       SW Southwest
7.1 11001006202 Census Tract 62.02, District of Columbia, District of Columbia        0       12        2       SE Southeast
7.2 11001006202 Census Tract 62.02, District of Columbia, District of Columbia        0       12        3       NE Northeast
    SHAPE_Length SHAPE_Area                       geometry
1       27817.06   33075057 POLYGON ((-76.953 38.86628,...
2       27817.06   33075057 POLYGON ((-76.93592 38.8859...
2.1     30312.73   40175193 POLYGON ((-76.93592 38.8859...
3       36110.52   75733371 POLYGON ((-77.06937 38.9003...
4       36110.52   75733371 POLYGON ((-77.1054 38.91719...
5       36110.52   75733371 POLYGON ((-77.09635 38.9141...
6       36110.52   75733371 POLYGON ((-77.10053 38.9490...
7       35676.50   28472526 POLYGON ((-77.06466 38.8918...
7.1     27817.06   33075057 POLYGON ((-77.06466 38.8918...
7.2     30312.73   40175193 POLYGON ((-77.06466 38.8918...

Now let’s save that data to a shapefile so we can use it later. We can use sf

# write a geojson
st_write(obj = med_inc_quads,                                    # Object to be written
         dsn = "path_to_a_folder/acs_2016_medincome.geojson",    # destination of file
         driver = "GeoJSON")                                     # file type see help(st_write)

# write a shapefile 
st_write(obj = med_inc_quads, 
         dsn =  "path_to_a_folder/acs_2016_medincome.shp",  
         driver = "ESRI Shapefile")

Great! Now for each census tract we know which quadrant it is in. Let play with a few different ways of visualizing their differences.

First we will look at using box plots. Here we use ggplot geom_boxplot to plot our census data within each quadrant. So the data=med_inc_quads tells it where to find the new data. y=med_incE indicates what value to plot on the y axis and fill=QUADRANT treats each quadrant as a unique color or grouping.

Boxplots explained.

ggplot()+geom_boxplot(data=med_inc_quads, 
                      aes(y=med_incE, fill=QUADRANT))

Cool! If you are into this sort of thing… We could also look at a grouped histogram. Notice that the function is virtually the same as the last one except we use geom_histogram and x=med_incE because we want our values along the x axis instead of the y.

ggplot()+geom_histogram(data=med_inc_quads, 
                      aes(x=med_incE, fill=QUADRANT) )

ggplot()+geom_histogram(data=med_inc_quads, 
                      aes(x=med_incE, fill=QUADRANT))

Making Beautiful ggplots

Getting good at ggplot takes a while, because frankly it can be confusing. Lucking there is a decent shortcut called the ggthemes library. Which as the name implies allows you to quickly apply a color/style theme. See some examples here.

All we have to do is apply a theme we like using +the_theme_name

ggplot()+geom_histogram(data=med_inc_quads, 
                      aes(x=med_incE, fill=QUADRANT))+
                      theme_economist()

Saving Map

Ggplot2 also makes it easy to save you maps or graphs out as images. You can save a variety of differnt graphics types including tif, png, or even svg (for use in illustrator). See the help here.

# ggsave(filename='path_to_folder/segregationmap.png', 
#        plot = last_plot(),  # save the last thing you ploted 
#        device='png')
LS0tDQp0aXRsZTogIkhhbmRsaW5nIENlbnN1cyBEYXRhIGluIFIgLSBQYXJ0IDMiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEdldHRpbmcgU3RhcnRlZA0KDQpUaGlzIHNoZWV0IGJ1aWxkcyBvZmYgb2YgIkhhbmRsaW5nIENlbnN1cyBEYXRhIGluIFIgLSBQYXJ0IDEiLiBQbGVhc2UgY29tcGxldGUgdGhhdCBmaXJzdA0KDQoNCkFnYWluLCBldmVyeXRpbWUgeW91IHN0YXJ0IFIgeW91IHdpbGwgbmVlZCB0byBsb2FkIGFueSBsaWJyYXJpZXMgdGhhdCB5b3UgbmVlZCBmb3IgYW5hbHlzaXMuICpZb3Ugd2lsbCBuZWVkIHRvIHJ1biB0aGlzIGJpdCBvZiBjb2RlIGV2ZXJ5IHRpbWUgeW91IG9wZW4gUiogdG8gZ2V0IHRoaXMgdG8gd29yay4gDQoNCmBgYHtyIGluY2x1ZGU9VH0NCmxpYnJhcnkodGlkeWNlbnN1cykNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGdndGhlbWVzKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQoNClRvIGdldCBzdGFydGVkIHdvcmtpbmcgd2l0aCB0aWR5Y2Vuc3VzLCB1c2VycyBzaG91bGQgbG9hZCB0aGUgcGFja2FnZSBhbG9uZyB3aXRoIHRoZSB0aWR5dmVyc2UgcGFja2FnZSwgYW5kIHNldCB0aGVpciBDZW5zdXMgQVBJIGtleS4gQSBrZXkgY2FuIGJlIG9idGFpbmVkIGZyb20gW0NlbnN1cyBBUEkgS2V5XShodHRwOi8vYXBpLmNlbnN1cy5nb3YvZGF0YS9rZXlfc2lnbnVwLmh0bWwpLiAgKipJdCB3aWxsIHByb3ZpZGUgeW91IHdpdGggYSA0MCBkaWdpdCB0ZXh0IHN0cmluZy4gUGxlYXNlIGtlZXAgdHJhY2sgb2YgdGhpcyBudW1iZXIuIFN0b3JlIGl0IGluIGEgc2FmZSBwbGFjZS4qKg0KIA0KYGBge3IgaW5jbHVkZT1ULCBldmFsPUZBTFNFfQ0KQVBJX0tleSA9ICc5ODM5ODBiOWZjNTA0MTQ5ZTgyMTE3Y3NkZndlZndlMjNkc2RjNTA3JyAgIyBub24gd29ya2luZyBleGFtcGxlIC0gcGxlYXNlIHBhc3RlIHlvdXIgb3duIGluDQpjZW5zdXNfYXBpX2tleShBUElfS2V5LCBpbnN0YWxsID0gVFJVRSwgb3ZlcndyaXRlPVRSVUUpICANCmBgYA0KDQojIENvbXBhcmluZyBQb3B1bGF0aW9ucyBBY3Jvc3MgR2VvZ3JhcGhpZXMNCg0KTGV0J3Mgc2F5IHlvdSB3YW50IHRvIGxvb2sgYXQgdGhlIGRpZmZlcmVuY2VzIGluIGluY29tZSBiZXR3ZWVuIGRpZmZlcmVudCBwYXJ0cyBvZiBjaXR5LCBsZXQncyBzYXkgYnkgcXVhZHJhbnQgKE5FLE5XLFNFLFNXKS4gVGhpcyBjYW4gZWFzaWx5IGRvbmUgdXNpbmcgc3BhdGlhbCBzdGF0aXN0aWNzLCBqb2luaW5nIGluY29tZSBkYXRhIGludG8gdGhlIGFwcHJvcHJpYXRlIHF1YWRyYW50IGJ5IGxvY2F0aW9uLiANCg0KVGhlIGZpcnN0IHN0ZXAgaXMgZ29pbmcgdG8gYmUgdG8gYWNjZXNzIHRoZSBEQyBxdWF0cmFudCBzaGFwZWZpbGUuIFdlIGNhbiBkaXJlY3RseSBhY2Nlc3MgZGF0YSBvbmxpbmUgZWFzaWx5IFtoZXJlXShodHRwczovL29wZW5kYXRhLmRjLmdvdi9kYXRhc2V0cy9kYy1xdWFkcmFudHMpLiBUbyBhY2Nlc3MgdGhlIGRhdGEgb25saW5lIHdlIGp1c3QgbmVlZCB0aGUgVVJMIG9mIHRoZSBHZW9Kc29uIGZpbGUgYXMgc2VlbiBiZWxvdzoNCg0KIVsgXSguL09wZW5EYXRhRENBUEkucG5nKQ0KDQoNCkZvciB0aGlzIHdlIHdpbGwgdXNlIGByZWFkX3NmYCBmcm9tIHRoZSBbc2YgcGFja2FnZV0oaHR0cHM6Ly9yLXNwYXRpYWwuZ2l0aHViLmlvL3NmLykuIFNGIHdpbGwgYWxsb3cgdXMgdG8gZG8gcHJldHR5IG11Y2ggYW55dGhpbmcgdGhhdCBhcmNtYXAgY2FuIGRvLiBJbiB0aGlzIGNhc2UgYHJlYWRfc2ZgIGp1c3QgcmVhZHMgdGhlIG9iamVjdCBmcm9tIHRoZSBpbnRlcm5ldCBmb3IgeW91IGFuZCBzdG9yZXMgaXQgaW4gdGhlIHNhbWUgZm9ybWF0IGFzIG91ciBjZW5zdXMgZGF0YS4gIA0KDQpOb3RpY2UgdGhpcyBpcyBqdXN0IHRoZSBhdHRyaWJ1dGUgZGF0YSB3aXRoIGEgY29sdW1uIGNhbGxlZCBgZ2VvbWV0cnlgIHRoYXQgc3RvcmVzIHRoZSBvdXRsaW5lIG9mIGVhY2ggcml2ZXIuDQoNCmBgYHtyfQ0KIyByZWFkIHF1YWRyYW50IGRhdGEgZnJvbSB0aGUgaW50ZXJuZXQNCnF1YWRzID0gcmVhZF9zZignaHR0cHM6Ly9vcGVuZGF0YS5hcmNnaXMuY29tL2RhdGFzZXRzLzAyOTIzZTQ2OTc4MDQ0MDZiOWVlMzI2OGExNjBkYjk5XzExLmdlb2pzb24nKQ0KZ2dwbG90KCkrZ2VvbV9zZihkYXRhPXF1YWRzLCBmaWxsPSdncmV5ODAnLCBjb2xvcj0nd2hpdGUnKQ0KYGBgDQoNCk5vdyBsZXQncyBnZXQgdGhlIG1lZGlhbiBpbmNvbWUgZGF0YSBmb3IgZGMgdHJhY3QgYW5kIHBsb3QgaXQgdW5kZXJuZWF0aCB0aGUgcXVhZHJhbnRzLiBOb3RpY2UgdGhhdCBJIHNldCBgZmlsbCA9IE5BYCB3aGljaCBtYWtlcyB0aGUgcXVhZGFudCBwb2x5Z29ucyB0cmFuc3BhcmVudCwgYW5kIGBjb2xvcj0nd2hpdGUnYCBzZXRzIHRoaWVyIG91dGxpbmUgY29sb3IgdG8gd2hpdGUuIEFnYWluIHRoZXNlIGFyZSBub3QgaW5zaWRlIG9mIGBhZXMoKWAgYmVjYXVzZSB3ZSBvbmx5IHVzZSBgYWVzKClgIGlmIHdlIHdhbnQgYW4gYXN0aGV0aWMgcHJvcGVydHkgdG8gYmUgc2V0IGZyb20gdmFsdWVzIGluIHRoZSBhdHRyaWJ1dGUgdGFibGUuIA0KDQpgYGB7cn0NCiMgZG93bmxvYWQgdGhlIGFjcyBkYXRhIA0KbWVkX2luYyA9ICBnZXRfYWNzKHN0YXRlID0gIkRDIiwgDQogICAgICAgICAgICAgICAgICAgZ2VvZ3JhcGh5ID0gInRyYWN0IiwgDQogICAgICAgICAgICAgICAgICAgeWVhcj0yMDE4LA0KICAgICAgICAgICAgICAgICAgdmFyaWFibGVzID0gIGMobWVkX2luYyA9ICdCMTkxMDFfMDAxJyksIA0KICAgICAgICAgICAgICAgICAgZ2VvbWV0cnkgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gJ3dpZGUnKSAgICMganVzdCBkbyB0aGlzDQoNCiMgcHJpbnQgdGhlIGNvbHVtbiBuYW1lcw0KcHJpbnQobmFtZXMobWVkX2luYykpDQoNCiMgcGxvdCBib3RoDQpnZ3Bsb3QoKSsNCiAgICAgICAgIyBwbG90IG1lZGlhbiBpbmNvbWUNCiAgICAgICAgZ2VvbV9zZihkYXRhPW1lZF9pbmMsIGFlcyhmaWxsID0gbWVkX2luY0UpKSsNCiAgDQogICAgICAgICMgcGxvdCBxdWFkcmFudHMNCiAgICAgICAgZ2VvbV9zZihkYXRhPXF1YWRzLCBmaWxsPU5BLCBjb2xvcj0nd2hpdGUnKQ0KYGBgDQoNCktlZXAgaW4gbWluZCB0aGUgb2xkIGFkZGFnZSAnbWFwcGluZyBpcyBub3QgZG9pbmcnLCBvayBJIGp1c3QgbWFkZSB0aGF0IHVwLiBCdXQgcmVhbGx5IHdlIGhhdmVuJ3QgZG9uZSBhbnkgYW5hbHlzaXMgaGVyZS4gV2UgbmVlZCB0byBjb21iaW5lIHRoZSBhdHRyaWJ1dGUgdGFibGVzIG9mIGBtZWRfaW5jYCBhbmQgYHF1YWRzYC4gV2UgY2FuIGRvIHRoaXMgYnkgdXNpbmcgYSBzcGF0aWFsIGFnZ3JlZ2F0aW9uIHVzaW5nIHRoZSBgc2ZgIHBhY2thZ2UuIEhlcmUgd2UgYXJlIGdvaW5nIHRvIGdldCB0aGUgbWVkaWFuIGluY29tZSBvZiBhbGwgY2Vuc3VzIHRyYWN0cyB3aXRoaW4gZWFjaCBxdWFkcmFudCAoTkUsTlcsU0UsU1cpLg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIHRoZSBtZWRpYW4gaW5jb21lIG9mIGVhY2ggcXVhZHJhbnQNCmFnZ3JlZ2F0ZShtZWRfaW5jLCBxdWFkcywgIEZVTj0gbWVkaWFuKSAgDQpgYGANCg0KKipPb3BzIGxvb2tzIGxpa2UgdGhlIGNycyAoY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtKSBpc24ndCB0aGUgc2FtZSBmb3IgYm90aCBsYXllcnMuKiogTGV0J3MgcmVwcm9qZWN0IHRoZW0gdG8gIk5BRDgzIiBbZGV0YWlscyBoZXJlXShodHRwczovL2Vwc2cuaW8vNDI2OSkgdXNpbmcgdGhpZXIgRVBTRyBjb2RlLCB3aGljaCBjYW4gYmUgdXNlZCB0byB1bmlxdWVseSBpZGVudGlmeSBhbnkgcHJvamVjdGlvbi4gDQoNCmBgYHtyfQ0KIyByZXByb2plY3QgdGhlIGRhdGEgdG8gdG8gTkFEXzgzICBodHRwczovL2Vwc2cuaW8vNDI2OQ0KbWVkX2luYyA9IHN0X3RyYW5zZm9ybShtZWRfaW5jLCBjcnM9NDI2OSkgIA0KcXVhZHMgPSBzdF90cmFuc2Zvcm0ocXVhZHMsIGNycz00MjY5KSAgDQpgYGANCg0KTm93IGxldCdzIHRyeSBnZXR0aW5nIHRoZSBtZWRpYW4gaW5jb21lIGZvciBlYWNoIHF1YWRyYW50LiANCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgbWVkaWFuIGluY29tZSBvZiBlYWNoIHF1YWRyYW50DQpxdWFkX21lZF9pbmMgPSBhZ2dyZWdhdGUobWVkX2luYywgcXVhZHMsICBGVU49IG1lZGlhbikgIA0KcXVhZF9tZWRfaW5jDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoKSsgZ2VvbV9zZihkYXRhPXF1YWRfbWVkX2luYywgYWVzKGZpbGwgPSBtZWRfaW5jRSkpDQpgYGANCg0KV2VsbCB0aGF0IGlzbid0IHZlcnkgaW50ZXJlc3RpbmcuIEluc3RlYWQgaXQgbWlnaHQgYmUgbW9yZSBpbnRlcmVzdGluZyB0byBsb29rIGF0IHRoZSBkaXN0cmlidXRpb24gb2YgY2Vuc3VzIHRyYWN0IGluY29tZXMgaW4gZWFjaCBxdWFkcmFudCwgc28gd2UgbmVlZCB0byBqb2luIHRoZSBuYW1lIG9mIHRoZSBxdWFkcmFudCB0byBlYWNoIGNlbnN1cyB0cmFjdCB1c2luZyBhIHNwYXRpYWwgam9pbi4gQSBzcGF0aWFsIGpvaW4gaXMgZG9uZSBpbiBgc2ZgIHdpdGggdGhlIGBzdF9qb2luYCBmdW5jdGlvbi4NCg0KYGBge3J9DQptZWRfaW5jX3F1YWRzID0gc3Rfam9pbihtZWRfaW5jLCBxdWFkcykNCm1lZF9pbmNfcXVhZHMNCmBgYA0KDQpOb3cgbGV0J3Mgc2F2ZSB0aGF0IGRhdGEgdG8gYSBzaGFwZWZpbGUgc28gd2UgY2FuIHVzZSBpdCBsYXRlci4gV2UgY2FuIHVzZSBgc2ZgDQoNCmBgYHtyfQ0KIyB3cml0ZSBhIGdlb2pzb24NCnN0X3dyaXRlKG9iaiA9IG1lZF9pbmNfcXVhZHMsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBPYmplY3QgdG8gYmUgd3JpdHRlbg0KICAgICAgICAgZHNuID0gInBhdGhfdG9fYV9mb2xkZXIvYWNzXzIwMTZfbWVkaW5jb21lLmdlb2pzb24iLCAgICAjIGRlc3RpbmF0aW9uIG9mIGZpbGUNCiAgICAgICAgIGRyaXZlciA9ICJHZW9KU09OIikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBmaWxlIHR5cGUgc2VlIGhlbHAoc3Rfd3JpdGUpDQoNCiMgd3JpdGUgYSBzaGFwZWZpbGUgDQpzdF93cml0ZShvYmogPSBtZWRfaW5jX3F1YWRzLCANCiAgICAgICAgIGRzbiA9ICAicGF0aF90b19hX2ZvbGRlci9hY3NfMjAxNl9tZWRpbmNvbWUuc2hwIiwgIA0KICAgICAgICAgZHJpdmVyID0gIkVTUkkgU2hhcGVmaWxlIikNCmBgYA0KDQoNCg0KR3JlYXQhIE5vdyBmb3IgZWFjaCBjZW5zdXMgdHJhY3Qgd2Uga25vdyB3aGljaCBxdWFkcmFudCBpdCBpcyBpbi4gTGV0IHBsYXkgd2l0aCBhIGZldyBkaWZmZXJlbnQgd2F5cyBvZiB2aXN1YWxpemluZyB0aGVpciBkaWZmZXJlbmNlcy4NCg0KRmlyc3Qgd2Ugd2lsbCBsb29rIGF0IHVzaW5nIGJveCBwbG90cy4gSGVyZSB3ZSB1c2UgYGdncGxvdGAgYGdlb21fYm94cGxvdGAgdG8gcGxvdCBvdXIgY2Vuc3VzIGRhdGEgd2l0aGluIGVhY2ggcXVhZHJhbnQuIFNvIHRoZSBgZGF0YT1tZWRfaW5jX3F1YWRzYCB0ZWxscyBpdCB3aGVyZSB0byBmaW5kIHRoZSBuZXcgZGF0YS4gYHk9bWVkX2luY0VgIGluZGljYXRlcyB3aGF0IHZhbHVlIHRvIHBsb3Qgb24gdGhlIHkgYXhpcyBhbmQgYGZpbGw9UVVBRFJBTlRgIHRyZWF0cyBlYWNoIHF1YWRyYW50IGFzIGEgdW5pcXVlIGNvbG9yIG9yIGdyb3VwaW5nLiANCg0KW0JveHBsb3RzIGV4cGxhaW5lZF0oaHR0cHM6Ly93d3cud2VsbGJlaW5nYXRzY2hvb2wub3JnLm56L2luZm9ybWF0aW9uLXNoZWV0L3VuZGVyc3RhbmRpbmctYW5kLWludGVycHJldGluZy1ib3gtcGxvdHMpLiAgIA0KDQpgYGB7cn0NCmdncGxvdCgpK2dlb21fYm94cGxvdChkYXRhPW1lZF9pbmNfcXVhZHMsIA0KICAgICAgICAgICAgICAgICAgICAgIGFlcyh5PW1lZF9pbmNFLCBmaWxsPVFVQURSQU5UKSkNCmBgYA0KDQoNCkNvb2whIElmIHlvdSBhcmUgaW50byB0aGlzIHNvcnQgb2YgdGhpbmcuLi4gV2UgY291bGQgYWxzbyBsb29rIGF0IGEgZ3JvdXBlZCBoaXN0b2dyYW0uIE5vdGljZSB0aGF0IHRoZSBmdW5jdGlvbiBpcyB2aXJ0dWFsbHkgdGhlIHNhbWUgYXMgdGhlIGxhc3Qgb25lIGV4Y2VwdCB3ZSB1c2UgYGdlb21faGlzdG9ncmFtYCBhbmQgYHg9bWVkX2luY0VgIGJlY2F1c2Ugd2Ugd2FudCBvdXIgdmFsdWVzIGFsb25nIHRoZSB4IGF4aXMgaW5zdGVhZCBvZiB0aGUgeS4gDQoNCmBgYHtyfQ0KZ2dwbG90KCkrZ2VvbV9oaXN0b2dyYW0oZGF0YT1tZWRfaW5jX3F1YWRzLCANCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeD1tZWRfaW5jRSwgZmlsbD1RVUFEUkFOVCkgKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KCkrZ2VvbV9oaXN0b2dyYW0oZGF0YT1tZWRfaW5jX3F1YWRzLCANCiAgICAgICAgICAgICAgICAgICAgICBhZXMoeD1tZWRfaW5jRSwgZmlsbD1RVUFEUkFOVCkpDQpgYGANCg0KDQojIE1ha2luZyBCZWF1dGlmdWwgZ2dwbG90cw0KDQpHZXR0aW5nIGdvb2QgYXQgZ2dwbG90IHRha2VzIGEgd2hpbGUsIGJlY2F1c2UgZnJhbmtseSBpdCBjYW4gYmUgY29uZnVzaW5nLiBMdWNraW5nIHRoZXJlIGlzIGEgZGVjZW50IHNob3J0Y3V0IGNhbGxlZCB0aGUgYGdndGhlbWVzYCBsaWJyYXJ5LiBXaGljaCBhcyB0aGUgbmFtZSBpbXBsaWVzIGFsbG93cyB5b3UgdG8gcXVpY2tseSBhcHBseSBhIGNvbG9yL3N0eWxlIHRoZW1lLiBbU2VlIHNvbWUgZXhhbXBsZXMgaGVyZV0oaHR0cHM6Ly95dXRhbm5paGlsYXRpb24uZ2l0aHViLmlvL2FsbFlvdXJGaWd1cmVBcmVCZWxvbmdUb1VzL2dndGhlbWVzLykuDQoNCkFsbCB3ZSBoYXZlIHRvIGRvIGlzIGFwcGx5IGEgdGhlbWUgd2UgbGlrZSB1c2luZyBgK3RoZV90aGVtZV9uYW1lYCANCg0KYGBge3J9DQpnZ3Bsb3QoKStnZW9tX2hpc3RvZ3JhbShkYXRhPW1lZF9pbmNfcXVhZHMsIA0KICAgICAgICAgICAgICAgICAgICAgIGFlcyh4PW1lZF9pbmNFLCBmaWxsPVFVQURSQU5UKSkrDQogICAgICAgICAgICAgICAgICAgICAgdGhlbWVfZWNvbm9taXN0KCkNCmBgYA0KDQoNCiMgU2F2aW5nIE1hcA0KDQpHZ3Bsb3QyIGFsc28gbWFrZXMgaXQgZWFzeSB0byBzYXZlIHlvdSBtYXBzIG9yIGdyYXBocyBvdXQgYXMgaW1hZ2VzLiBZb3UgY2FuIHNhdmUgYSB2YXJpZXR5IG9mIGRpZmZlcm50IGdyYXBoaWNzIHR5cGVzIGluY2x1ZGluZyB0aWYsIHBuZywgb3IgZXZlbiBzdmcgKGZvciB1c2UgaW4gaWxsdXN0cmF0b3IpLiBTZWUgdGhlIFtoZWxwIGhlcmVdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3NhdmUuaHRtbCkuDQoNCg0KYGBge3J9DQpnZ3NhdmUoZmlsZW5hbWU9J3BhdGhfdG9fZm9sZGVyL3NlZ3JlZ2F0aW9ubWFwLnBuZycsDQogICAgICAgcGxvdCA9IGxhc3RfcGxvdCgpLCAgIyBzYXZlIHRoZSBsYXN0IHRoaW5nIHlvdSBwbG90ZWQNCiAgICAgICBkZXZpY2U9J3BuZycpDQpgYGANCg0KDQoNCg0KDQo=