library(sf)     
library(dplyr)   
library(spData) 
library(ggplot2)
library(ggthemes)
#Let's load in some packages and data
setwd("C:/Users/Donny/OneDrive/R studio/nyc_neighborhoods")
Warning: The working directory was changed to C:/Users/Donny/OneDrive/R studio/nyc_neighborhoods inside a notebook chunk. The working directory will be reset when the chunk is finished running. Use the knitr root.dir option in the setup chunk to change the working directory for notebook chunks.
nyc <- st_read("NYC_Nhood ACS2008_12.shp")
Reading layer `NYC_Nhood ACS2008_12' from data source 
  `C:\Users\Donny\OneDrive\R studio\nyc_neighborhoods\NYC_Nhood ACS2008_12.shp' using driver `ESRI Shapefile'
Simple feature collection with 195 features and 98 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -74.25559 ymin: 40.49612 xmax: -73.70001 ymax: 40.91553
Geodetic CRS:  WGS 84
#Let's plot the data first

ggplot()+
  geom_sf(data = nyc, aes(fill=poor))+
  scale_fill_steps(
    name = "Number of \nPoor Households",
    low = "lightsteelblue1",
    high = "tomato1",
    n.breaks = 4,
    show.limits = T)+
  theme_void()

What do you think, is there spatial autocorrelation in this data?

We can test for this using a common statistic called Moran’s I. This essentially tests whether our data was produced by a spatially random process (or CSR). We will need to follow a few steps to do this. First, we need to download a new packages (spdep). Then, we will need to create some information for R about the spatial relationships between the neighborhoods in the data. We will create what are called spatial weights. Similarly to the weights used to compute a weighted average, a higher weight indicates that two neighborhoods are geographically closer to eachother, while a lower weight indicates that the neighborhoods are furthe apart. Let’s see what this looks like:

library(spdep)
Warning: package ‘spdep’ was built under R version 4.3.3
#This code creates the weights
nyc_list <- nyc %>% 
  #first convert polygons to a neighbor object
  #R needs to know a unique ID for each neighborhood    
  #(cartodb_id)
  #Neighbors determined using Queen weights
  poly2nb(st_geometry(nyc)) %>% 
  #zero.policy just tells R not to remove 0 values
  nb2listw(zero.policy = TRUE) 

#Now we can calculate the "Global Moran's I" for the data
#start with the weights, then give R the corresponding variable
nyc_list %>% 
  moran.test(nyc$poor, ., zero.policy = TRUE)

    Moran I test under randomisation

data:  nyc$poor  
weights: .    

Moran I statistic standard deviate = 8.2642, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.362862746      -0.005154639       0.001983038 

Here, with a Moran’s I statistic of 0.363 and a p-value of <0.001, we can reject the null hypothesis and conclude that our data was not generated by a spatially random process. In other words, the number of poor households by NYC neighborhood are not spatially independent - neighborhoods that are close together tend to be more similar than neigborhoods that are further apart.

Let’s look at a plot of these results!

moran.plot(nyc$poor, 
           nyc_list, 
           zero.policy = TRUE, 
           labels = F,
           xlab = 'Number of Poor Households',
           ylab = 'Lagged Poor Households (of Neighbors)',
                   pch=20)

The solid line here goes through the mean value, and slope is the Moran’s I statistic. The positive slope indicates positive autocorrelation - in other words, neighborhoods with high Poor counts tend to be near other neighborhoods with high poor counts. For each point, the x-axis represents the actual Poor count for the neighborhood, while the y-axis represents an estimate of this value for that neighborhood’s neighbors. If the points in our plot are mostly concentrated in the bottom left and top right quadrants of the graph, we have positive autocorrelation. If they are concentrated in the top left and bottom right quadrants, we have negative autocorrelation. In data with no autocorrelation, we would expect to see a random cloud of points (with no pattern).

Next, we’ll take a look at Local Indicators of Spatial Autocorrelation (LISA) plots. This will help us to identify clusters of low or high values in the graph, as well as outliers. This uses the Local Moran’s I statistic. Instead of calculating one Moran’s I value for the whole plot (as is the case with the global statistic), the local version calculates a local I for each spatial unit (neighborhood)

lisaRslt <- localmoran(nyc$poor, nyc_list, 
            zero.policy = TRUE, na.action = na.omit)

Let’s talk about the values in this output for a moment. The Pr column is our P-value. A significant result (i.e. a p-value less than 0.05) tells us that a neighborhood is either a) part of a cluster or b) a spatial outlier. The Li column gives us more info about which one it is. If Li is positive, then a neighborhood is part of a spatial cluster. Otherwise, it is a spatial outlier.

The code below reproduces a LISA plot that is commonly used in GIS software. Essentially, this plot a) helps us to identify significant neighborhoods, and b) visualizes whether they are clusters or outliers. High-High (HH) and Low-Low (LL) neighborhoods are clusters - these are units that are surrounded by units with similar values. High-Low (HL) and Low-High (LH) units are spatial outliers - they are different from their surrounding units. The L and H tell us whether the number of poor households in a neighborhood is low or high relative to the mean.

# Now we can derive the cluster/outlier types (COType in ArcGIS term) for each spatial feature in the data
library(magrittr)

Attaching package: ‘magrittr’

The following object is masked from ‘package:tidyr’:

    extract
library(tidyr)
significanceLevel <- 0.05; # 95% confidence
meanVal <- mean(nyc$poor);

lisaRslt %<>% as_tibble() %>%
  set_colnames(c("Ii","E.Ii","Var.Ii","Z.Ii","Pr(z > 0)")) %>%
  #Create a new categorical value
  #This variable will call insignificant units "insignificant"
  #Outliers are determined by LI <0 - units higher than the mean are HL and units lower than the mean are LH
  #Clusters are units with LI >= 0, and classified as HH or LL based on whether they are above or below the mean
  mutate(coType = case_when(
  `Pr(z > 0)` > 0.05 ~ "Insignificant",
  `Pr(z > 0)` <= 0.05 & Ii >= 0 & nyc$poor >= meanVal ~ "HH",
  `Pr(z > 0)` <= 0.05 & Ii >= 0 & nyc$poor < meanVal ~ "LL",
  `Pr(z > 0)` <= 0.05 & Ii < 0 & nyc$poor >= meanVal ~ "HL",
  `Pr(z > 0)` <= 0.05 & Ii < 0 & nyc$poor < meanVal ~ "LH"))

# Now add this coType to original sf data
nyc$coType <- lisaRslt$coType %>% replace_na("Insignificant")

#Now we'll plot it!
ggplot(nyc) +
  geom_sf(aes(fill=coType),color = 'lightgrey') +
  scale_fill_manual(values = c('red','brown','NA','blue','cyan'), name='Clusters & \nOutliers') +
  labs(title = "Number of Poor Families by Neighborhood")+
  theme_minimal()

Now, it’s your turn! Pick a new variable from the data and re-run the analysis. What do you find? Do you see different clustering patterns than the one above?
```r #Analysis here
setwd(“C:/Users/Donny/OneDrive/R studio/chicago_airbnb”) chicago <- st_read(“airbnb_Chicago 2015.shp”)
chicago_list <- chicago %>% #first convert polygons to a neighbor object #R needs to know a unique ID for each neighborhood #(cartodb_id) #Neighbors determined using Queen weights poly2nb(st_geometry(chicago)) %>% #zero.policy just tells R not to remove 0 values nb2listw(zero.policy = TRUE)
#Now we can calculate the “Global Moran’s I” for the data #start with the weights, then give R the corresponding variable chicago_list %>% moran.test(chicago$crowded, ., zero.policy = TRUE) ```
r moran.plot(chicago$crowded, chicago_list, zero.policy = TRUE, labels = F, xlab = 'Percent Crowding in Chicago neighborhoods', ylab = 'Percent Crowding of neighborring neighborhoods', pch = 20)
r lisaRslt <- localmoran(chicago$crowded, chicago_list, zero.policy = TRUE, na.action = na.omit)

Resources ArcGIS (2022). Global Vs. Local Spatial Autocorrelation. Retrieved from: https://storymaps.arcgis.com/stories/5b26f25bb81a437b89003423505e2f71.

Sun, S. (N. D.). 4.1: Spatial Autocorrelation. Retrieved from: http://www.geo.hunter.cuny.edu/~ssun/R-Spatial/spregression.html#spatial-autocorrelation.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3J9DQpsaWJyYXJ5KHNmKSAgICAgDQpsaWJyYXJ5KGRwbHlyKSAgIA0KbGlicmFyeShzcERhdGEpIA0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ3RoZW1lcykNCiNMZXQncyBsb2FkIGluIHNvbWUgcGFja2FnZXMgYW5kIGRhdGENCnNldHdkKCJDOi9Vc2Vycy9Eb25ueS9PbmVEcml2ZS9SIHN0dWRpby9ueWNfbmVpZ2hib3Job29kcyIpDQpueWMgPC0gc3RfcmVhZCgiTllDX05ob29kIEFDUzIwMDhfMTIuc2hwIikNCg0KI0xldCdzIHBsb3QgdGhlIGRhdGEgZmlyc3QNCg0KZ2dwbG90KCkrDQogIGdlb21fc2YoZGF0YSA9IG55YywgYWVzKGZpbGw9cG9vcikpKw0KICBzY2FsZV9maWxsX3N0ZXBzKA0KICAgIG5hbWUgPSAiTnVtYmVyIG9mIFxuUG9vciBIb3VzZWhvbGRzIiwNCiAgICBsb3cgPSAibGlnaHRzdGVlbGJsdWUxIiwNCiAgICBoaWdoID0gInRvbWF0bzEiLA0KICAgIG4uYnJlYWtzID0gNCwNCiAgICBzaG93LmxpbWl0cyA9IFQpKw0KICB0aGVtZV92b2lkKCkNCg0KYGBgDQpXaGF0IGRvIHlvdSB0aGluaywgaXMgdGhlcmUgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gaW4gdGhpcyBkYXRhPw0KDQpXZSBjYW4gdGVzdCBmb3IgdGhpcyB1c2luZyBhIGNvbW1vbiBzdGF0aXN0aWMgY2FsbGVkIE1vcmFuJ3MgSS4gVGhpcyBlc3NlbnRpYWxseSB0ZXN0cyB3aGV0aGVyIG91ciBkYXRhIHdhcyBwcm9kdWNlZCBieSBhIHNwYXRpYWxseSByYW5kb20gcHJvY2VzcyAob3IgQ1NSKS4gV2Ugd2lsbCBuZWVkIHRvIGZvbGxvdyBhIGZldyBzdGVwcyB0byBkbyB0aGlzLiBGaXJzdCwgd2UgbmVlZCB0byBkb3dubG9hZCBhIG5ldyBwYWNrYWdlcyAoc3BkZXApLiBUaGVuLCB3ZSB3aWxsIG5lZWQgdG8gY3JlYXRlIHNvbWUgaW5mb3JtYXRpb24gZm9yIFIgYWJvdXQgdGhlIHNwYXRpYWwgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIHRoZSBuZWlnaGJvcmhvb2RzIGluIHRoZSBkYXRhLiBXZSB3aWxsIGNyZWF0ZSB3aGF0IGFyZSBjYWxsZWQgc3BhdGlhbCB3ZWlnaHRzLiBTaW1pbGFybHkgdG8gdGhlIHdlaWdodHMgdXNlZCB0byBjb21wdXRlIGEgd2VpZ2h0ZWQgYXZlcmFnZSwgYSBoaWdoZXIgd2VpZ2h0IGluZGljYXRlcyB0aGF0IHR3byBuZWlnaGJvcmhvb2RzIGFyZSBnZW9ncmFwaGljYWxseSBjbG9zZXIgdG8gZWFjaG90aGVyLCB3aGlsZSBhIGxvd2VyIHdlaWdodCBpbmRpY2F0ZXMgdGhhdCB0aGUgbmVpZ2hib3Job29kcyBhcmUgZnVydGhlIGFwYXJ0LiBMZXQncyBzZWUgd2hhdCB0aGlzIGxvb2tzIGxpa2U6DQoNCmBgYHtyfQ0KbGlicmFyeShzcGRlcCkNCg0KI1RoaXMgY29kZSBjcmVhdGVzIHRoZSB3ZWlnaHRzDQpueWNfbGlzdCA8LSBueWMgJT4lIA0KICAjZmlyc3QgY29udmVydCBwb2x5Z29ucyB0byBhIG5laWdoYm9yIG9iamVjdA0KICAjUiBuZWVkcyB0byBrbm93IGEgdW5pcXVlIElEIGZvciBlYWNoIG5laWdoYm9yaG9vZCAgICANCiAgIyhjYXJ0b2RiX2lkKQ0KICAjTmVpZ2hib3JzIGRldGVybWluZWQgdXNpbmcgUXVlZW4gd2VpZ2h0cw0KICBwb2x5Mm5iKHN0X2dlb21ldHJ5KG55YykpICU+JSANCiAgI3plcm8ucG9saWN5IGp1c3QgdGVsbHMgUiBub3QgdG8gcmVtb3ZlIDAgdmFsdWVzDQogIG5iMmxpc3R3KHplcm8ucG9saWN5ID0gVFJVRSkgDQoNCiNOb3cgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgIkdsb2JhbCBNb3JhbidzIEkiIGZvciB0aGUgZGF0YQ0KI3N0YXJ0IHdpdGggdGhlIHdlaWdodHMsIHRoZW4gZ2l2ZSBSIHRoZSBjb3JyZXNwb25kaW5nIHZhcmlhYmxlDQpueWNfbGlzdCAlPiUgDQogIG1vcmFuLnRlc3QobnljJHBvb3IsIC4sIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KYGBgDQpIZXJlLCB3aXRoIGEgTW9yYW4ncyBJIHN0YXRpc3RpYyBvZiAwLjM2MyBhbmQgYSBwLXZhbHVlIG9mIDwwLjAwMSwgd2UgY2FuIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIGFuZCBjb25jbHVkZSB0aGF0IG91ciBkYXRhIHdhcyBub3QgZ2VuZXJhdGVkIGJ5IGEgc3BhdGlhbGx5IHJhbmRvbSBwcm9jZXNzLiBJbiBvdGhlciB3b3JkcywgdGhlIG51bWJlciBvZiBwb29yIGhvdXNlaG9sZHMgYnkgTllDIG5laWdoYm9yaG9vZCBhcmUgbm90IHNwYXRpYWxseSBpbmRlcGVuZGVudCAtIG5laWdoYm9yaG9vZHMgdGhhdCBhcmUgY2xvc2UgdG9nZXRoZXIgdGVuZCB0byBiZSBtb3JlIHNpbWlsYXIgdGhhbiBuZWlnYm9yaG9vZHMgdGhhdCBhcmUgZnVydGhlciBhcGFydC4gDQoNCkxldCdzIGxvb2sgYXQgYSBwbG90IG9mIHRoZXNlIHJlc3VsdHMhDQoNCmBgYHtyfQ0KbW9yYW4ucGxvdChueWMkcG9vciwgDQogICAgICAgICAgIG55Y19saXN0LCANCiAgICAgICAgICAgemVyby5wb2xpY3kgPSBUUlVFLCANCiAgICAgICAgICAgbGFiZWxzID0gRiwNCiAgICAgICAgICAgeGxhYiA9ICdOdW1iZXIgb2YgUG9vciBIb3VzZWhvbGRzJywNCiAgICAgICAgICAgeWxhYiA9ICdMYWdnZWQgUG9vciBIb3VzZWhvbGRzIChvZiBOZWlnaGJvcnMpJywNCiAgICAgICAgICAgICAgICAgICBwY2g9MjApDQpgYGANClRoZSBzb2xpZCBsaW5lIGhlcmUgZ29lcyB0aHJvdWdoIHRoZSBtZWFuIHZhbHVlLCBhbmQgc2xvcGUgaXMgdGhlIE1vcmFuJ3MgSSBzdGF0aXN0aWMuIFRoZSBwb3NpdGl2ZSBzbG9wZSBpbmRpY2F0ZXMgcG9zaXRpdmUgYXV0b2NvcnJlbGF0aW9uIC0gaW4gb3RoZXIgd29yZHMsIG5laWdoYm9yaG9vZHMgd2l0aCBoaWdoIFBvb3IgY291bnRzIHRlbmQgdG8gYmUgbmVhciBvdGhlciBuZWlnaGJvcmhvb2RzIHdpdGggaGlnaCBwb29yIGNvdW50cy4gRm9yIGVhY2ggcG9pbnQsIHRoZSB4LWF4aXMgcmVwcmVzZW50cyB0aGUgYWN0dWFsIFBvb3IgY291bnQgZm9yIHRoZSBuZWlnaGJvcmhvb2QsIHdoaWxlIHRoZSB5LWF4aXMgcmVwcmVzZW50cyBhbiBlc3RpbWF0ZSBvZiB0aGlzIHZhbHVlIGZvciB0aGF0IG5laWdoYm9yaG9vZCdzIG5laWdoYm9ycy4gSWYgdGhlIHBvaW50cyBpbiBvdXIgcGxvdCBhcmUgbW9zdGx5IGNvbmNlbnRyYXRlZCBpbiB0aGUgYm90dG9tIGxlZnQgYW5kIHRvcCByaWdodCBxdWFkcmFudHMgb2YgdGhlIGdyYXBoLCB3ZSBoYXZlIHBvc2l0aXZlIGF1dG9jb3JyZWxhdGlvbi4gSWYgdGhleSBhcmUgY29uY2VudHJhdGVkIGluIHRoZSB0b3AgbGVmdCBhbmQgYm90dG9tIHJpZ2h0IHF1YWRyYW50cywgd2UgaGF2ZSBuZWdhdGl2ZSBhdXRvY29ycmVsYXRpb24uIEluIGRhdGEgd2l0aCBubyBhdXRvY29ycmVsYXRpb24sIHdlIHdvdWxkIGV4cGVjdCB0byBzZWUgYSByYW5kb20gY2xvdWQgb2YgcG9pbnRzICh3aXRoIG5vIHBhdHRlcm4pLiANCg0KTmV4dCwgd2UnbGwgdGFrZSBhIGxvb2sgYXQgTG9jYWwgSW5kaWNhdG9ycyBvZiBTcGF0aWFsIEF1dG9jb3JyZWxhdGlvbiAoTElTQSkgcGxvdHMuIFRoaXMgd2lsbCBoZWxwIHVzIHRvIGlkZW50aWZ5IGNsdXN0ZXJzIG9mIGxvdyBvciBoaWdoIHZhbHVlcyBpbiB0aGUgZ3JhcGgsIGFzIHdlbGwgYXMgb3V0bGllcnMuIFRoaXMgdXNlcyB0aGUgTG9jYWwgTW9yYW4ncyBJIHN0YXRpc3RpYy4gSW5zdGVhZCBvZiBjYWxjdWxhdGluZyBvbmUgTW9yYW4ncyBJIHZhbHVlIGZvciB0aGUgd2hvbGUgcGxvdCAoYXMgaXMgdGhlIGNhc2Ugd2l0aCB0aGUgZ2xvYmFsIHN0YXRpc3RpYyksIHRoZSBsb2NhbCB2ZXJzaW9uIGNhbGN1bGF0ZXMgYSBsb2NhbCBJIGZvciBlYWNoIHNwYXRpYWwgdW5pdCAobmVpZ2hib3Job29kKQ0KDQpgYGB7cn0NCmxpc2FSc2x0IDwtIGxvY2FsbW9yYW4obnljJHBvb3IsIG55Y19saXN0LCANCiAgICAgICAgICAgIHplcm8ucG9saWN5ID0gVFJVRSwgbmEuYWN0aW9uID0gbmEub21pdCkNCmBgYA0KDQpMZXQncyB0YWxrIGFib3V0IHRoZSB2YWx1ZXMgaW4gdGhpcyBvdXRwdXQgZm9yIGEgbW9tZW50LiBUaGUgUHIgY29sdW1uIGlzIG91ciBQLXZhbHVlLiBBIHNpZ25pZmljYW50IHJlc3VsdCAoaS5lLiBhIHAtdmFsdWUgbGVzcyB0aGFuIDAuMDUpIHRlbGxzIHVzIHRoYXQgYSBuZWlnaGJvcmhvb2QgaXMgZWl0aGVyIGEpIHBhcnQgb2YgYSBjbHVzdGVyIG9yIGIpIGEgc3BhdGlhbCBvdXRsaWVyLiBUaGUgTGkgY29sdW1uIGdpdmVzIHVzIG1vcmUgaW5mbyBhYm91dCB3aGljaCBvbmUgaXQgaXMuIElmIExpIGlzIHBvc2l0aXZlLCB0aGVuIGEgbmVpZ2hib3Job29kIGlzIHBhcnQgb2YgYSBzcGF0aWFsIGNsdXN0ZXIuIE90aGVyd2lzZSwgaXQgaXMgYSBzcGF0aWFsIG91dGxpZXIuIA0KDQpUaGUgY29kZSBiZWxvdyByZXByb2R1Y2VzIGEgTElTQSBwbG90IHRoYXQgaXMgY29tbW9ubHkgdXNlZCBpbiBHSVMgc29mdHdhcmUuIEVzc2VudGlhbGx5LCB0aGlzIHBsb3QgYSkgaGVscHMgdXMgdG8gaWRlbnRpZnkgc2lnbmlmaWNhbnQgbmVpZ2hib3Job29kcywgYW5kIGIpIHZpc3VhbGl6ZXMgd2hldGhlciB0aGV5IGFyZSBjbHVzdGVycyBvciBvdXRsaWVycy4gSGlnaC1IaWdoIChISCkgYW5kIExvdy1Mb3cgKExMKSBuZWlnaGJvcmhvb2RzIGFyZSBjbHVzdGVycyAtIHRoZXNlIGFyZSB1bml0cyB0aGF0IGFyZSBzdXJyb3VuZGVkIGJ5IHVuaXRzIHdpdGggc2ltaWxhciB2YWx1ZXMuIEhpZ2gtTG93IChITCkgYW5kIExvdy1IaWdoIChMSCkgdW5pdHMgYXJlIHNwYXRpYWwgb3V0bGllcnMgLSB0aGV5IGFyZSBkaWZmZXJlbnQgZnJvbSB0aGVpciBzdXJyb3VuZGluZyB1bml0cy4gVGhlIEwgYW5kIEggdGVsbCB1cyB3aGV0aGVyIHRoZSBudW1iZXIgb2YgcG9vciBob3VzZWhvbGRzIGluIGEgbmVpZ2hib3Job29kIGlzIGxvdyBvciBoaWdoIHJlbGF0aXZlIHRvIHRoZSBtZWFuLiANCg0KYGBge3J9DQojIE5vdyB3ZSBjYW4gZGVyaXZlIHRoZSBjbHVzdGVyL291dGxpZXIgdHlwZXMgKENPVHlwZSBpbiBBcmNHSVMgdGVybSkgZm9yIGVhY2ggc3BhdGlhbCBmZWF0dXJlIGluIHRoZSBkYXRhDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeSh0aWR5cikNCnNpZ25pZmljYW5jZUxldmVsIDwtIDAuMDU7ICMgOTUlIGNvbmZpZGVuY2UNCm1lYW5WYWwgPC0gbWVhbihueWMkcG9vcik7DQoNCmxpc2FSc2x0ICU8PiUgYXNfdGliYmxlKCkgJT4lDQogIHNldF9jb2xuYW1lcyhjKCJJaSIsIkUuSWkiLCJWYXIuSWkiLCJaLklpIiwiUHIoeiA+IDApIikpICU+JQ0KICAjQ3JlYXRlIGEgbmV3IGNhdGVnb3JpY2FsIHZhbHVlDQogICNUaGlzIHZhcmlhYmxlIHdpbGwgY2FsbCBpbnNpZ25pZmljYW50IHVuaXRzICJpbnNpZ25pZmljYW50Ig0KICAjT3V0bGllcnMgYXJlIGRldGVybWluZWQgYnkgTEkgPDAgLSB1bml0cyBoaWdoZXIgdGhhbiB0aGUgbWVhbiBhcmUgSEwgYW5kIHVuaXRzIGxvd2VyIHRoYW4gdGhlIG1lYW4gYXJlIExIDQogICNDbHVzdGVycyBhcmUgdW5pdHMgd2l0aCBMSSA+PSAwLCBhbmQgY2xhc3NpZmllZCBhcyBISCBvciBMTCBiYXNlZCBvbiB3aGV0aGVyIHRoZXkgYXJlIGFib3ZlIG9yIGJlbG93IHRoZSBtZWFuDQogIG11dGF0ZShjb1R5cGUgPSBjYXNlX3doZW4oDQogIGBQcih6ID4gMClgID4gMC4wNSB+ICJJbnNpZ25pZmljYW50IiwNCiAgYFByKHogPiAwKWAgPD0gMC4wNSAmIElpID49IDAgJiBueWMkcG9vciA+PSBtZWFuVmFsIH4gIkhIIiwNCiAgYFByKHogPiAwKWAgPD0gMC4wNSAmIElpID49IDAgJiBueWMkcG9vciA8IG1lYW5WYWwgfiAiTEwiLA0KICBgUHIoeiA+IDApYCA8PSAwLjA1ICYgSWkgPCAwICYgbnljJHBvb3IgPj0gbWVhblZhbCB+ICJITCIsDQogIGBQcih6ID4gMClgIDw9IDAuMDUgJiBJaSA8IDAgJiBueWMkcG9vciA8IG1lYW5WYWwgfiAiTEgiKSkNCg0KIyBOb3cgYWRkIHRoaXMgY29UeXBlIHRvIG9yaWdpbmFsIHNmIGRhdGENCm55YyRjb1R5cGUgPC0gbGlzYVJzbHQkY29UeXBlICU+JSByZXBsYWNlX25hKCJJbnNpZ25pZmljYW50IikNCg0KI05vdyB3ZSdsbCBwbG90IGl0IQ0KZ2dwbG90KG55YykgKw0KICBnZW9tX3NmKGFlcyhmaWxsPWNvVHlwZSksY29sb3IgPSAnbGlnaHRncmV5JykgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdyZWQnLCdicm93bicsJ05BJywnYmx1ZScsJ2N5YW4nKSwgbmFtZT0nQ2x1c3RlcnMgJiBcbk91dGxpZXJzJykgKw0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBQb29yIEZhbWlsaWVzIGJ5IE5laWdoYm9yaG9vZCIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQpOb3csIGl0J3MgeW91ciB0dXJuISBQaWNrIGEgbmV3IHZhcmlhYmxlIGZyb20gdGhlIGRhdGEgYW5kIHJlLXJ1biB0aGUgYW5hbHlzaXMuIFdoYXQgZG8geW91IGZpbmQ/IERvIHlvdSBzZWUgZGlmZmVyZW50IGNsdXN0ZXJpbmcgcGF0dGVybnMgdGhhbiB0aGUgb25lIGFib3ZlPw0KYGBge3J9DQojQW5hbHlzaXMgaGVyZQ0KDQpzZXR3ZCgiQzovVXNlcnMvRG9ubnkvT25lRHJpdmUvUiBzdHVkaW8vY2hpY2Fnb19haXJibmIiKQ0KY2hpY2FnbyA8LSBzdF9yZWFkKCJhaXJibmJfQ2hpY2FnbyAyMDE1LnNocCIpDQoNCmNoaWNhZ29fbGlzdCA8LSBjaGljYWdvICU+JSANCiAgI2ZpcnN0IGNvbnZlcnQgcG9seWdvbnMgdG8gYSBuZWlnaGJvciBvYmplY3QNCiAgI1IgbmVlZHMgdG8ga25vdyBhIHVuaXF1ZSBJRCBmb3IgZWFjaCBuZWlnaGJvcmhvb2QgICAgDQogICMoY2FydG9kYl9pZCkNCiAgI05laWdoYm9ycyBkZXRlcm1pbmVkIHVzaW5nIFF1ZWVuIHdlaWdodHMNCiAgcG9seTJuYihzdF9nZW9tZXRyeShjaGljYWdvKSkgJT4lIA0KICAjemVyby5wb2xpY3kganVzdCB0ZWxscyBSIG5vdCB0byByZW1vdmUgMCB2YWx1ZXMNCiAgbmIybGlzdHcoemVyby5wb2xpY3kgPSBUUlVFKSANCg0KI05vdyB3ZSBjYW4gY2FsY3VsYXRlIHRoZSAiR2xvYmFsIE1vcmFuJ3MgSSIgZm9yIHRoZSBkYXRhDQojc3RhcnQgd2l0aCB0aGUgd2VpZ2h0cywgdGhlbiBnaXZlIFIgdGhlIGNvcnJlc3BvbmRpbmcgdmFyaWFibGUNCmNoaWNhZ29fbGlzdCAlPiUgDQogIG1vcmFuLnRlc3QoY2hpY2FnbyRjcm93ZGVkLCAuLCB6ZXJvLnBvbGljeSA9IFRSVUUpDQpgYGANCg0KDQpgYGB7cn0NCm1vcmFuLnBsb3QoY2hpY2FnbyRjcm93ZGVkLCANCiAgICAgICAgICAgY2hpY2Fnb19saXN0LCANCiAgICAgICAgICAgemVyby5wb2xpY3kgPSBUUlVFLCANCiAgICAgICAgICAgbGFiZWxzID0gRiwNCiAgICAgICAgICAgeGxhYiA9ICdQZXJjZW50IENyb3dkaW5nIGluIENoaWNhZ28gbmVpZ2hib3Job29kcycsDQogICAgICAgICAgIHlsYWIgPSAnUGVyY2VudCBDcm93ZGluZyBvZiBuZWlnaGJvcnJpbmcgbmVpZ2hib3Job29kcycsDQogICAgICAgICAgIHBjaCA9IDIwKQ0KYGBgDQoNCg0KYGBge3J9DQpsaXNhUnNsdCA8LSBsb2NhbG1vcmFuKGNoaWNhZ28kY3Jvd2RlZCwgY2hpY2Fnb19saXN0LCANCiAgICAgICAgICAgIHplcm8ucG9saWN5ID0gVFJVRSwgbmEuYWN0aW9uID0gbmEub21pdCkNCmBgYA0KYGBge3J9DQpzaWduaWZpY2FuY2VMZXZlbCA8LSAwLjA1OyAjIDk1JSBjb25maWRlbmNlDQptZWFuVmFsIDwtIG1lYW4oY2hpY2FnbyRjcm93ZGVkKTsNCg0KbGlzYVJzbHQgJTw+JSBhc190aWJibGUoKSAlPiUNCiAgc2V0X2NvbG5hbWVzKGMoIklpIiwiRS5JaSIsIlZhci5JaSIsIlouSWkiLCJQcih6ID4gMCkiKSkgJT4lDQoNCiAgbXV0YXRlKGNvVHlwZSA9IGNhc2Vfd2hlbigNCiAgYFByKHogPiAwKWAgPiAwLjA1IH4gIkluc2lnbmlmaWNhbnQiLA0KICBgUHIoeiA+IDApYCA8PSAwLjA1ICYgSWkgPj0gMCAmIGNoaWNhZ28kY3Jvd2RlZCA+PSBtZWFuVmFsIH4gIkhIIiwNCiAgYFByKHogPiAwKWAgPD0gMC4wNSAmIElpID49IDAgJiBjaGljYWdvJGNyb3dkZWQgPCBtZWFuVmFsIH4gIkxMIiwNCiAgYFByKHogPiAwKWAgPD0gMC4wNSAmIElpIDwgMCAmIGNoaWNhZ28kY3Jvd2RlZCA+PSBtZWFuVmFsIH4gIkhMIiwNCiAgYFByKHogPiAwKWAgPD0gMC4wNSAmIElpIDwgMCAmIGNoaWNhZ28kY3Jvd2RlZCA8IG1lYW5WYWwgfiAiTEgiKSkNCg0KIyBOb3cgYWRkIHRoaXMgY29UeXBlIHRvIG9yaWdpbmFsIHNmIGRhdGENCmNoaWNhZ28kY29UeXBlIDwtIGxpc2FSc2x0JGNvVHlwZSAlPiUgcmVwbGFjZV9uYSgiSW5zaWduaWZpY2FudCIpDQoNCiNOb3cgd2UnbGwgcGxvdCBpdCENCmdncGxvdChjaGljYWdvKSArDQogIGdlb21fc2YoYWVzKGZpbGw9Y29UeXBlKSxjb2xvciA9ICdsaWdodGdyZXknKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoJ3JlZCcsJ05BJywnYmx1ZScsJ2N5YW4nKSwgbmFtZT0nQ2x1c3RlcnMgJiBcbk91dGxpZXJzJykgKw0KICBsYWJzKHRpdGxlID0gIkNsdXN0ZXJzIG9mIENyb3dkZWQgTmVpZ2hib3Job29kcyIpKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NClJlc291cmNlcw0KQXJjR0lTICgyMDIyKS4gR2xvYmFsIFZzLiBMb2NhbCBTcGF0aWFsIEF1dG9jb3JyZWxhdGlvbi4gUmV0cmlldmVkIGZyb206IGh0dHBzOi8vc3RvcnltYXBzLmFyY2dpcy5jb20vc3Rvcmllcy81YjI2ZjI1YmI4MWE0MzdiODkwMDM0MjM1MDVlMmY3MS4gDQoNClN1biwgUy4gKE4uIEQuKS4gNC4xOiBTcGF0aWFsIEF1dG9jb3JyZWxhdGlvbi4gUmV0cmlldmVkIGZyb206IGh0dHA6Ly93d3cuZ2VvLmh1bnRlci5jdW55LmVkdS9+c3N1bi9SLVNwYXRpYWwvc3ByZWdyZXNzaW9uLmh0bWwjc3BhdGlhbC1hdXRvY29ycmVsYXRpb24uIA==