run a spatial regression model

library(sf)     
library(dplyr)   
library(spData) 
library(ggplot2)
library(ggthemes)
library(spdep)
library(spatialreg)
library(GWmodel)
library(tidyr)
#Let's load in some packages and data
setwd("/Users/laurendemarco/Downloads/DIDA370/chicago_airbnb")
Warning: The working directory was changed to /Users/laurendemarco/Downloads/DIDA370/chicago_airbnb 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.
chi_data <- st_read("airbnb_Chicago 2015.shp")
Reading layer `airbnb_Chicago 2015' from data source 
  `/Users/laurendemarco/Downloads/DIDA370/chicago_airbnb/airbnb_Chicago 2015.shp' using driver `ESRI Shapefile'
Simple feature collection with 77 features and 20 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -87.94011 ymin: 41.64454 xmax: -87.52414 ymax: 42.02304
Geodetic CRS:  WGS 84
#start with a general map of % in poverty in chicago
ggplot()+
  geom_sf(data = chi_data, aes(fill=as.numeric(poverty)))+
  scale_fill_steps(
    name = "% in Poverty",
    low = "lightsteelblue1",
    high = "tomato1",
    n.breaks = 5,
    show.limits = T)+
  labs(title = "Percentage of Poverty in Chicago Neighborhods") +
  theme_void()

Research Question: What is the effect of crowding, unemployment & number of crimes and theft on poverty level in Chicago neighborhoods. Independent Variables: Crowded percentage, unemployed percentage, number of crimes and number of theft Dependent Variable: Poverty percentage

#create our variables for the model:
model <- chi_data %>% 
  select(c(poverty, crowded, unemployed, num_crimes, num_theft, without_hs, 
           dependency, income_pc, population, geometry)) %>% 
  filter(!poverty %in% "NA") %>% 
  mutate(poverty = as.numeric(poverty))

#Run a basic model

summary(lm1 <- lm(poverty ~ crowded + unemployed + num_crimes + num_theft, data = model))

Call:
lm(formula = poverty ~ crowded + unemployed + num_crimes + num_theft, 
    data = model)

Residuals:
     Min       1Q   Median       3Q      Max 
-13.1427  -5.3129   0.2276   4.0086  17.1818 

Coefficients:
              Estimate Std. Error t value Pr(>|t|)    
(Intercept) -1.579e+00  2.298e+00  -0.687   0.4942    
crowded      6.526e-01  2.175e-01   3.000   0.0037 ** 
unemployed   1.217e+00  1.215e-01  10.016 2.76e-15 ***
num_crimes  -1.823e-05  2.336e-04  -0.078   0.9380    
num_theft    1.013e-03  8.314e-04   1.219   0.2269    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 6.553 on 72 degrees of freedom
Multiple R-squared:  0.694, Adjusted R-squared:  0.677 
F-statistic: 40.82 on 4 and 72 DF,  p-value: < 2.2e-16
#Let's test for spatial autocorrelation in the residuals!
chi_list <- model %>% 
  poly2nb(st_geometry(model)) %>% 
  nb2listw(zero.policy = TRUE) 

#run the Moran's I test for regression residuals
lm.morantest(lm1, chi_list)

    Global Moran I for regression residuals

data:  
model: lm(formula = poverty ~ crowded + unemployed + num_crimes + num_theft, data = model)
weights: chi_list

Moran I statistic standard deviate = 4.8264, p-value = 6.95e-07
alternative hypothesis: greater
sample estimates:
Observed Moran I      Expectation         Variance 
     0.295357914     -0.037740465      0.004763105 
#interpretation of the model:
#we have a p-value < 0.05! 
#low p value, this indicates to us that we wanna reject the null hypothesis and suggest this is not just spatial randomness, but we are see sptail autocorrelation
#take another step and run error model
#this is an improvement
lm_error <- errorsarlm(poverty ~ crowded + unemployed + num_crimes + num_theft,
              data = model,
              listw = chi_list,
              zero.policy = TRUE, 
              na.action = na.omit)

summary(lm_error)

Call:errorsarlm(formula = poverty ~ crowded + unemployed + num_crimes + 
    num_theft, data = model, listw = chi_list, na.action = na.omit,     zero.policy = TRUE)

Residuals:
      Min        1Q    Median        3Q       Max 
-13.64199  -3.64360  -0.58219   3.04990  14.07546 

Type: error 
Coefficients: (asymptotic standard errors) 
               Estimate  Std. Error z value  Pr(>|z|)
(Intercept)  2.1855e-01  2.6689e+00  0.0819 0.9347374
crowded      8.4446e-01  2.2158e-01  3.8110 0.0001384
unemployed   1.1132e+00  1.2689e-01  8.7733 < 2.2e-16
num_crimes   9.8838e-05  2.1734e-04  0.4548 0.6492840
num_theft   -1.1861e-04  8.7533e-04 -0.1355 0.8922160

Lambda: 0.59497, LR test value: 15.847, p-value: 6.8681e-05
Asymptotic standard error: 0.11113
    z-value: 5.3538, p-value: 8.6115e-08
Wald statistic: 28.663, p-value: 8.6115e-08

Log likelihood: -243.5054 for error model
ML residual variance (sigma squared): 29.918, (sigma: 5.4698)
Number of observations: 77 
Number of parameters estimated: 7 
AIC: 501.01, (AIC for lm: 514.86)
seResiduals <- rep(0, length(model$poverty))
#create an index based on the order of the residuals in the lm_error object
resIndex <- lm_error$residuals %>% names() %>% as.integer();
#use the index to fill in the seResiduals object with corresponding residual values
seResiduals[resIndex] <- lm_error$residuals

#perform the Moran Test
chi_list %>%
  moran.test(seResiduals, ., zero.policy = TRUE) 

    Moran I test under randomisation

data:  seResiduals  
weights: .    

Moran I statistic standard deviate = -0.20458, p-value = 0.581
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     -0.027859550      -0.013157895       0.005164471 
#this test is in significant, this tell us "that we effectively controlled for the spatial variation in the error term"
#compare with Lag model

lm_lag <- lagsarlm(poverty ~ crowded + unemployed + num_crimes + num_theft,
              data = model,
              listw = chi_list,
              zero.policy = TRUE, 
              na.action = na.omit)

summary(lm_lag)

Call:lagsarlm(formula = poverty ~ crowded + unemployed + num_crimes + 
    num_theft, data = model, listw = chi_list, na.action = na.omit,     zero.policy = TRUE)

Residuals:
     Min       1Q   Median       3Q      Max 
-14.0479  -3.8431  -0.4222   3.4368  15.7009 

Type: lag 
Coefficients: (asymptotic standard errors) 
               Estimate  Std. Error z value  Pr(>|z|)
(Intercept) -4.7420e+00  2.3439e+00 -2.0231  0.043065
crowded      6.4373e-01  1.9849e-01  3.2431  0.001182
unemployed   1.0104e+00  1.3194e-01  7.6581 1.887e-14
num_crimes  -6.8589e-05  2.1306e-04 -0.3219  0.747516
num_theft    1.0169e-03  7.5760e-04  1.3423  0.179493

Rho: 0.30524, LR test value: 8.0045, p-value: 0.0046661
Asymptotic standard error: 0.10369
    z-value: 2.9438, p-value: 0.0032419
Wald statistic: 8.6661, p-value: 0.0032419

Log likelihood: -247.4266 for lag model
ML residual variance (sigma squared): 35.466, (sigma: 5.9554)
Number of observations: 77 
Number of parameters estimated: 7 
AIC: 508.85, (AIC for lm: 514.86)
LM test for residual autocorrelation
test value: 5.2633, p-value: 0.02178
#signifiance, but not as strong
#AIC is also lower for the error model
seResiduals <- rep(0, length(model$poverty))
resIndex <- lm_lag$residuals %>% names() %>% as.integer();
seResiduals[resIndex] <- lm_lag$residuals

#perform the Moran Test
chi_list %>%
  moran.test(seResiduals, ., zero.policy = TRUE) 

    Moran I test under randomisation

data:  seResiduals  
weights: .    

Moran I statistic standard deviate = 1.9207, p-value = 0.02739
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
      0.124758402      -0.013157895       0.005156063 
#again, not as strong so I think spatial error is better
#can look at this LM test
LM <- lm.LMtests(lm1, chi_list, test = "all")
Please update scripts to use lm.RStests in place of lm.LMtests
LM

    Rao's score (a.k.a Lagrange multiplier) diagnostics for spatial dependence

data:  
model: lm(formula = poverty ~ crowded + unemployed + num_crimes + num_theft, data = model)
test weights: listw

RSerr = 15.761, df = 1, p-value = 7.187e-05


    Rao's score (a.k.a Lagrange multiplier) diagnostics for spatial dependence

data:  
model: lm(formula = poverty ~ crowded + unemployed + num_crimes + num_theft, data = model)
test weights: listw

RSlag = 8.9223, df = 1, p-value = 0.002817


    Rao's score (a.k.a Lagrange multiplier) diagnostics for spatial dependence

data:  
model: lm(formula = poverty ~ crowded + unemployed + num_crimes + num_theft, data = model)
test weights: listw

adjRSerr = 6.9463, df = 1, p-value = 0.008399


    Rao's score (a.k.a Lagrange multiplier) diagnostics for spatial dependence

data:  
model: lm(formula = poverty ~ crowded + unemployed + num_crimes + num_theft, data = model)
test weights: listw

adjRSlag = 0.10772, df = 1, p-value = 0.7428


    Rao's score (a.k.a Lagrange multiplier) diagnostics for spatial dependence

data:  
model: lm(formula = poverty ~ crowded + unemployed + num_crimes + num_theft, data = model)
test weights: listw

SARMA = 15.869, df = 2, p-value = 0.0003583
#this confirms that we should use the error model
#the Langronge Test  shows that it is the better choice because 
#adjRSerr, p-value = 0.008399 and ajRSlag, p-value = 0.7428. 
#The error model has a significant p-value while the lag model does not. 

Brief discussion of results from the error model that I decided was the best choice for my data: Crowding and unemployment are the only two significant variables from this regression model. They both had a positive impact on poverty levels, so as they increase… so do poverty levels in Chicago areas.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKcnVuIGEgc3BhdGlhbCByZWdyZXNzaW9uIG1vZGVsCgpgYGB7cn0KbGlicmFyeShzZikgICAgIApsaWJyYXJ5KGRwbHlyKSAgIApsaWJyYXJ5KHNwRGF0YSkgCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3RoZW1lcykKbGlicmFyeShzcGRlcCkKbGlicmFyeShzcGF0aWFscmVnKQpsaWJyYXJ5KEdXbW9kZWwpCmxpYnJhcnkodGlkeXIpCiNMZXQncyBsb2FkIGluIHNvbWUgcGFja2FnZXMgYW5kIGRhdGEKc2V0d2QoIi9Vc2Vycy9sYXVyZW5kZW1hcmNvL0Rvd25sb2Fkcy9ESURBMzcwL2NoaWNhZ29fYWlyYm5iIikKY2hpX2RhdGEgPC0gc3RfcmVhZCgiYWlyYm5iX0NoaWNhZ28gMjAxNS5zaHAiKQpgYGAKCmBgYHtyfQojc3RhcnQgd2l0aCBhIGdlbmVyYWwgbWFwIG9mICUgaW4gcG92ZXJ0eSBpbiBjaGljYWdvCmdncGxvdCgpKwogIGdlb21fc2YoZGF0YSA9IGNoaV9kYXRhLCBhZXMoZmlsbD1hcy5udW1lcmljKHBvdmVydHkpKSkrCiAgc2NhbGVfZmlsbF9zdGVwcygKICAgIG5hbWUgPSAiJSBpbiBQb3ZlcnR5IiwKICAgIGxvdyA9ICJsaWdodHN0ZWVsYmx1ZTEiLAogICAgaGlnaCA9ICJ0b21hdG8xIiwKICAgIG4uYnJlYWtzID0gNSwKICAgIHNob3cubGltaXRzID0gVCkrCiAgbGFicyh0aXRsZSA9ICJQZXJjZW50YWdlIG9mIFBvdmVydHkgaW4gQ2hpY2FnbyBOZWlnaGJvcmhvZHMiKSArCiAgdGhlbWVfdm9pZCgpCmBgYApSZXNlYXJjaCBRdWVzdGlvbjogV2hhdCBpcyB0aGUgZWZmZWN0IG9mIGNyb3dkaW5nLCB1bmVtcGxveW1lbnQgJiBudW1iZXIgb2YgY3JpbWVzIGFuZCB0aGVmdCBvbiBwb3ZlcnR5IGxldmVsIGluIENoaWNhZ28gbmVpZ2hib3Job29kcy4gCkluZGVwZW5kZW50IFZhcmlhYmxlczogQ3Jvd2RlZCBwZXJjZW50YWdlLCB1bmVtcGxveWVkIHBlcmNlbnRhZ2UsIG51bWJlciBvZiBjcmltZXMgYW5kIG51bWJlciBvZiB0aGVmdApEZXBlbmRlbnQgVmFyaWFibGU6IFBvdmVydHkgcGVyY2VudGFnZSAKCmBgYHtyfQojY3JlYXRlIG91ciB2YXJpYWJsZXMgZm9yIHRoZSBtb2RlbDoKbW9kZWwgPC0gY2hpX2RhdGEgJT4lIAogIHNlbGVjdChjKHBvdmVydHksIGNyb3dkZWQsIHVuZW1wbG95ZWQsIG51bV9jcmltZXMsIG51bV90aGVmdCwgd2l0aG91dF9ocywgCiAgICAgICAgICAgZGVwZW5kZW5jeSwgaW5jb21lX3BjLCBwb3B1bGF0aW9uLCBnZW9tZXRyeSkpICU+JSAKICBmaWx0ZXIoIXBvdmVydHkgJWluJSAiTkEiKSAlPiUgCiAgbXV0YXRlKHBvdmVydHkgPSBhcy5udW1lcmljKHBvdmVydHkpKQoKI1J1biBhIGJhc2ljIG1vZGVsCnN1bW1hcnkobG0xIDwtIGxtKHBvdmVydHkgfiBjcm93ZGVkICsgdW5lbXBsb3llZCArIG51bV9jcmltZXMgKyBudW1fdGhlZnQsIGRhdGEgPSBtb2RlbCkpCmBgYAoKYGBge3J9CiNMZXQncyB0ZXN0IGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgcmVzaWR1YWxzIQpjaGlfbGlzdCA8LSBtb2RlbCAlPiUgCiAgcG9seTJuYihzdF9nZW9tZXRyeShtb2RlbCkpICU+JSAKICBuYjJsaXN0dyh6ZXJvLnBvbGljeSA9IFRSVUUpIAoKI3J1biB0aGUgTW9yYW4ncyBJIHRlc3QgZm9yIHJlZ3Jlc3Npb24gcmVzaWR1YWxzCmxtLm1vcmFudGVzdChsbTEsIGNoaV9saXN0KQoKI2ludGVycHJldGF0aW9uIG9mIHRoZSBtb2RlbDoKI3dlIGhhdmUgYSBwLXZhbHVlIDwgMC4wNSEgVGhpcyBpcyBzaWduaWZpY2FudAojbG93IHAgdmFsdWUsIHRoaXMgaW5kaWNhdGVzIHRvIHVzIHRoYXQgd2Ugd2FudCB0byByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyBhbmQgc3VnZ2VzdCB0aGlzIGlzIE5PVCBqdXN0IHNwYXRpYWwgcmFuZG9tbmVzcywgYnV0IHdlIGFyZSBzZWVpbmcgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24hCmBgYAoKYGBge3J9CiN0YWtlIGFub3RoZXIgbGV2ZWwgdG8gdGhpcyBhbmQgcnVuIHRoZSBlcnJvciBtb2RlbAojdGhpcyBpcyBhbiBpbXByb3ZlbWVudCBmcm9tIHRoZSBsaW5lYXIgbW9kZWwKbG1fZXJyb3IgPC0gZXJyb3JzYXJsbShwb3ZlcnR5IH4gY3Jvd2RlZCArIHVuZW1wbG95ZWQgKyBudW1fY3JpbWVzICsgbnVtX3RoZWZ0LAogICAgICAgICAgICAgIGRhdGEgPSBtb2RlbCwKICAgICAgICAgICAgICBsaXN0dyA9IGNoaV9saXN0LAogICAgICAgICAgICAgIHplcm8ucG9saWN5ID0gVFJVRSwgCiAgICAgICAgICAgICAgbmEuYWN0aW9uID0gbmEub21pdCkKCnN1bW1hcnkobG1fZXJyb3IpCgojd2Ugc2VlIGEgbG93ZXIgQUlDIGhlcmUgd2hpY2ggaXMgZ29vZCAoNTAxLjAxIDwgNTE0Ljg2KQojVGhlIHZhcmlhYmxlcyB0aGF0IGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGFyZSBjcm93ZGVkIChwID0gMC4wMDAxMzg0KSBhbmQgdW5lbXBsb3llZCAocCA9IDIuMmUtMTYpCiN0aGlzIG1ha2VzIHNlbnNlIHRvIG1lIGJlY2F1c2UgaW4gYXJlYXMgd2hlcmUgdGhlcmUgaXMgaGlnaCB1bmVtcGxveW1lbnQsIEkgd291bGQgZXhwZWN0IHRvIHNlZSBoaWdoZXIgcG92ZXJ0eSBsZXZlbHMgYW5kIHRob3NlIGFyZWFzIG1heSBhbHNvIGJlIG1vcmUgY3Jvd2RlZApgYGAKCmBgYHtyfQpzZVJlc2lkdWFscyA8LSByZXAoMCwgbGVuZ3RoKG1vZGVsJHBvdmVydHkpKQojY3JlYXRlIGFuIGluZGV4IGJhc2VkIG9uIHRoZSBvcmRlciBvZiB0aGUgcmVzaWR1YWxzIGluIHRoZSBsbV9lcnJvciBvYmplY3QKcmVzSW5kZXggPC0gbG1fZXJyb3IkcmVzaWR1YWxzICU+JSBuYW1lcygpICU+JSBhcy5pbnRlZ2VyKCk7CiN1c2UgdGhlIGluZGV4IHRvIGZpbGwgaW4gdGhlIHNlUmVzaWR1YWxzIG9iamVjdCB3aXRoIGNvcnJlc3BvbmRpbmcgcmVzaWR1YWwgdmFsdWVzCnNlUmVzaWR1YWxzW3Jlc0luZGV4XSA8LSBsbV9lcnJvciRyZXNpZHVhbHMKCiNwZXJmb3JtIHRoZSBNb3JhbiBUZXN0CmNoaV9saXN0ICU+JQogIG1vcmFuLnRlc3Qoc2VSZXNpZHVhbHMsIC4sIHplcm8ucG9saWN5ID0gVFJVRSkgCgojdGhpcyB0ZXN0IGlzIGluIHNpZ25pZmljYW50LCB0aGlzIHRlbGwgdXMgInRoYXQgd2UgZWZmZWN0aXZlbHkgY29udHJvbGxlZCBmb3IgdGhlIHNwYXRpYWwgdmFyaWF0aW9uIGluIHRoZSBlcnJvciB0ZXJtIgpgYGAKCmBgYHtyfQojY29tcGFyZSB3aXRoIExhZyBtb2RlbCBhbmQgc2VlIGlmIGl0IGlzIGJldHRlciB0aGFuIGVycm9yIG1vZGVsCgpsbV9sYWcgPC0gbGFnc2FybG0ocG92ZXJ0eSB+IGNyb3dkZWQgKyB1bmVtcGxveWVkICsgbnVtX2NyaW1lcyArIG51bV90aGVmdCwKICAgICAgICAgICAgICBkYXRhID0gbW9kZWwsCiAgICAgICAgICAgICAgbGlzdHcgPSBjaGlfbGlzdCwKICAgICAgICAgICAgICB6ZXJvLnBvbGljeSA9IFRSVUUsIAogICAgICAgICAgICAgIG5hLmFjdGlvbiA9IG5hLm9taXQpCgpzdW1tYXJ5KGxtX2xhZykKCiNBSUMgaXMgYWxzbyBsb3dlciB0aGFuIGZvciBsbSB3aGljaCBpcyBnb29kLCBidXQgaXQgaXMgbm90IGFzIGxvdyBhcyB0aGUgZXJyb3IgbW9kZWwKI1RoZSB2YXJpYWJsZXMgdGhhdCBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBhcmUgY3Jvd2RlZCAocCA9IDAuMDAxMTgyKSBhbmQgdW5lbXBsb3llZCAocCA9IDEuODg3ZS0xNCksIHNhbWUgYXMgZXJyb3IgbW9kZWwKI3NhbWUgYW5hbHlzaXMgYXMgYmVmb3JlOiB0aGlzIG1ha2VzIHNlbnNlIHRvIG1lIGJlY2F1c2UgaW4gYXJlYXMgd2hlcmUgdGhlcmUgaXMgaGlnaCB1bmVtcGxveW1lbnQsIEkgd291bGQgZXhwZWN0IHRvIHNlZSBoaWdoZXIgcG92ZXJ0eSBsZXZlbHMgYW5kIHRob3NlIGFyZWFzIG1heSBhbHNvIGJlIG1vcmUgY3Jvd2RlZAojc3RpbGwgZ29pbmcgdG8gZG8gdGhlIExhbmdyb25nZSB0ZXN0IHRvIGNvbmZpcm0gZXJyb3IgaXMgYmV0dGVyIHRoYW4gbGFnCmBgYApgYGB7cn0Kc2VSZXNpZHVhbHMgPC0gcmVwKDAsIGxlbmd0aChtb2RlbCRwb3ZlcnR5KSkKcmVzSW5kZXggPC0gbG1fbGFnJHJlc2lkdWFscyAlPiUgbmFtZXMoKSAlPiUgYXMuaW50ZWdlcigpOwpzZVJlc2lkdWFsc1tyZXNJbmRleF0gPC0gbG1fbGFnJHJlc2lkdWFscwoKI3BlcmZvcm0gdGhlIE1vcmFuIFRlc3QKY2hpX2xpc3QgJT4lCiAgbW9yYW4udGVzdChzZVJlc2lkdWFscywgLiwgemVyby5wb2xpY3kgPSBUUlVFKSAKCiNhZ2Fpbiwgbm90IGFzIHN0cm9uZyBzbyBJIHRoaW5rIHNwYXRpYWwgZXJyb3IgaXMgYmV0dGVyIGJ1dCB3aWxsIGRvIHRoZSBMYW5ncm9uZ2UgVGVzdCBiZWxvdwpgYGAKYGBge3J9CiNjYW4gbG9vayBhdCB0aGlzIExNIHRlc3QKTE0gPC0gbG0uTE10ZXN0cyhsbTEsIGNoaV9saXN0LCB0ZXN0ID0gImFsbCIpCkxNCgojdGhpcyBjb25maXJtcyB0aGF0IHdlIHNob3VsZCB1c2UgdGhlIGVycm9yIG1vZGVsCiN0aGUgTGFuZ3JvbmdlIFRlc3QgIHNob3dzIHRoYXQgaXQgaXMgdGhlIGJldHRlciBjaG9pY2UgYmVjYXVzZS4uLiAKI2FkalJTZXJyLCBwLXZhbHVlID0gMC4wMDgzOTkgYW5kIGFqUlNsYWcsIHAtdmFsdWUgPSAwLjc0MjguIAojVGhlIGVycm9yIG1vZGVsIGhhcyBhIHNpZ25pZmljYW50IHAtdmFsdWUgd2hpbGUgdGhlIGxhZyBtb2RlbCBkb2VzIG5vdC4gCiNwbHVzIGZyb20gdGhlIGFuYWx5c2lzIGJlZm9yZSBJIGhhZCBhIGZlZWxpbmcgdGhlIGVycm9yIG1vZGVsIHdvdWxkIGJlIGJldHRlciBmcm9tIGxvd2VyIEFJQyBhbmQgc21hbGwgcCB2YWx1ZXMuIApgYGAKCkJyaWVmIGRpc2N1c3Npb24gb2YgcmVzdWx0cyBmcm9tIHRoZSBlcnJvciBtb2RlbCB0aGF0IEkgZGVjaWRlZCB3YXMgdGhlIGJlc3QgY2hvaWNlIGZvciBteSBkYXRhOgpDcm93ZGluZyBhbmQgdW5lbXBsb3ltZW50IGFyZSB0aGUgb25seSB0d28gc2lnbmlmaWNhbnQgdmFyaWFibGVzIGZyb20gdGhpcyByZWdyZXNzaW9uIG1vZGVsLiAKVGhleSBib3RoIGhhZCBhIHBvc2l0aXZlIGltcGFjdCBvbiBwb3ZlcnR5IGxldmVscywgc28gYXMgdGhleSBpbmNyZWFzZS4uLiBzbyBkbyBwb3ZlcnR5IGxldmVscyBpbiBDaGljYWdvIGFyZWFzLiAKCgo=