Last week, we started talking about spatial autocorrelation and how to measure it. This week, we’ll talk more specifically about what to do if you want to run a regression model that has spatial dependence. First, we’ll start with the simplest model: a spatial error model. We use this model when we believe that our error term is spatially correlated.

setwd("~/Binghamton/DIDA 370/nyc_neighborhoods")
Warning: The working directory was changed to C:/Users/melha/OneDrive/Documents/Binghamton/DIDA 370/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\melha\OneDrive\Documents\Binghamton\DIDA 370\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 start by putting together a simple regression model

#create our variables for the model:
model <- nyc %>% 
  mutate(unemp = popunemplo/poptot,
         educ = highschool/poptot,
         fem = female/poptot,
         nonwhite = (poptot-european)/poptot) %>% 
  select(c(gini, unemp, educ, fem, nonwhite, medianinco, 
           HHsize, medianage, cartodb_id, geometry)) %>% 
  filter(!gini %in% "NA") %>% 
  mutate(gini = as.numeric(gini))

#Run a basic model

summary(lm1 <- lm(gini ~ unemp + educ + fem + nonwhite, data = model))

Call:
lm(formula = gini ~ unemp + educ + fem + nonwhite, data = model)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.111503 -0.022791 -0.001208  0.026237  0.090886 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  0.42158    0.06444   6.542 7.12e-10 ***
unemp        0.15442    0.25352   0.609   0.5433    
educ        -0.49805    0.05697  -8.743 2.34e-15 ***
fem          0.21109    0.12333   1.712   0.0888 .  
nonwhite    -0.01861    0.01476  -1.261   0.2092    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.03829 on 167 degrees of freedom
Multiple R-squared:  0.348, Adjusted R-squared:  0.3324 
F-statistic: 22.29 on 4 and 167 DF,  p-value: 9.253e-15
nyc_list <- nyc_list %>% nb2listw(zero.policy = TRUE) 

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

    Global Moran I for regression residuals

data:  
model: lm(formula = gini ~ unemp + educ + fem + nonwhite, data = model)
weights: nyc_list

Moran I statistic standard deviate = 9.8844, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Observed Moran I      Expectation         Variance 
     0.521431192     -0.017403985      0.002971725 

Clearly there is some spatial dependence in the residuals! Let’s run a spatial error model to control for it.

lm_error <- errorsarlm(gini ~ unemp + educ + fem + nonwhite,
              data = model,
              listw = nyc_list,
              zero.policy = TRUE, 
              na.action = na.omit)

summary(lm_error)

Call:errorsarlm(formula = gini ~ unemp + educ + fem + nonwhite, data = model, 
    listw = nyc_list, na.action = na.omit, zero.policy = TRUE)

Residuals:
      Min        1Q    Median        3Q       Max 
-0.071808 -0.018530  0.002033  0.016991  0.067793 

Type: error 
Coefficients: (asymptotic standard errors) 
             Estimate Std. Error z value  Pr(>|z|)
(Intercept)  0.476432   0.054592  8.7271 < 2.2e-16
unemp       -0.330687   0.175027 -1.8893   0.05885
educ        -0.265466   0.067783 -3.9164 8.988e-05
fem          0.039918   0.099192  0.4024   0.68737
nonwhite     0.024514   0.013233  1.8526   0.06394

Lambda: 0.74625, LR test value: 98.308, p-value: < 2.22e-16
Asymptotic standard error: 0.047107
    z-value: 15.842, p-value: < 2.22e-16
Wald statistic: 250.96, p-value: < 2.22e-16

Log likelihood: 368.8172 for error model
ML residual variance (sigma squared): 0.00065865, (sigma: 0.025664)
Number of observations: 172 
Number of parameters estimated: 7 
AIC: -723.63, (AIC for lm: -627.33)

From this output, we can see that the lamda value is 0.75, which is statistically significant. This suggests that there is significant spatial autocorrelation in the residuals.

Here, we can see an AIC value of -723.63, compared to an AIC for the linear model of -627.33. Because the error model’s AIC is lower, this suggests an improvement in the model.

Let’s test for spatial autocorrelation in the residuals of this model:

#This code just allows me to manually extract the residual values
#Normally, R wants an LM object for this test - 
#Running a spatial error model does not output an lm object, which is why we need to do this
#Create a blank vector the length of the variable
seResiduals <- rep(0, length(model$gini))
#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
nyc_list %>%
  moran.test(seResiduals, ., zero.policy = TRUE) 

    Moran I test under randomisation

data:  seResiduals  
weights: .    

Moran I statistic standard deviate = -0.26757, p-value = 0.6055
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     -0.020662788      -0.005847953       0.003065648 

Now, we can see that our Moran test is insignificant - this tells us that we effectively controlled for the spatial variation in the error term.

What if we wanted to perform a spatial lag model instead?

lm_lag <- lagsarlm(gini ~ unemp + educ + fem + nonwhite,
              data = model,
              listw = nyc_list,
              zero.policy = TRUE, 
              na.action = na.omit)

summary(lm_lag)

Call:lagsarlm(formula = gini ~ unemp + educ + fem + nonwhite, data = model, 
    listw = nyc_list, na.action = na.omit, zero.policy = TRUE)

Residuals:
       Min         1Q     Median         3Q        Max 
-0.0663453 -0.0180466  0.0015673  0.0183086  0.0610481 

Type: lag 
Coefficients: (asymptotic standard errors) 
              Estimate Std. Error z value  Pr(>|z|)
(Intercept)  0.1444097  0.0506729  2.8498  0.004374
unemp       -0.1887369  0.1714991 -1.1005  0.271109
educ        -0.2003493  0.0441773 -4.5351 5.757e-06
fem          0.0641006  0.0834367  0.7683  0.442336
nonwhite     0.0078877  0.0099912  0.7895  0.429841

Rho: 0.69011, LR test value: 101.67, p-value: < 2.22e-16
Asymptotic standard error: 0.051164
    z-value: 13.488, p-value: < 2.22e-16
Wald statistic: 181.94, p-value: < 2.22e-16

Log likelihood: 370.4993 for lag model
ML residual variance (sigma squared): 0.00067072, (sigma: 0.025898)
Number of observations: 172 
Number of parameters estimated: 7 
AIC: -727, (AIC for lm: -627.33)
LM test for residual autocorrelation
test value: 0.0026113, p-value: 0.95924

Check for spatial autocorrelation in these residuals:

seResiduals <- rep(0, length(model$gini))
resIndex <- lm_lag$residuals %>% names() %>% as.integer();
seResiduals[resIndex] <- lm_lag$residuals

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

    Moran I test under randomisation

data:  seResiduals  
weights: .    

Moran I statistic standard deviate = 0.079751, p-value = 0.4682
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     -0.001427726      -0.005847953       0.003071956 

Again, this model also controls for autocorrelation in the residuals, so we’re in good shape so far.

Based on AICs alone, this model performs a bit better than the error model (-727 < -723). But this difference is so small it seems almost arbitrary - let’s test more formally whether one or the other is a good fit with the Lagrange Multiplier test.

LM <- lm.LMtests(lm1, nyc_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 = gini ~ unemp + educ + fem + nonwhite, data = model)
test weights: listw

RSerr = 86.624, df = 1, p-value < 2.2e-16


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

data:  
model: lm(formula = gini ~ unemp + educ + fem + nonwhite, data = model)
test weights: listw

RSlag = 102.4, df = 1, p-value < 2.2e-16


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

data:  
model: lm(formula = gini ~ unemp + educ + fem + nonwhite, data = model)
test weights: listw

adjRSerr = 0.049827, df = 1, p-value = 0.8234


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

data:  
model: lm(formula = gini ~ unemp + educ + fem + nonwhite, data = model)
test weights: listw

adjRSlag = 15.824, df = 1, p-value = 6.953e-05


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

data:  
model: lm(formula = gini ~ unemp + educ + fem + nonwhite, data = model)
test weights: listw

SARMA = 102.45, df = 2, p-value < 2.2e-16

We’re most interested in the LMerr and LMlag results - unsurprisingly, both turn out to be significant. So this test can’t tell us which one to pick - let’s look at a more robust test. We only need to look at these tests if our initial tests are not definitive. It turns out, RLMerr and RLMlag are the robust test results in this output. As you can see, RLMlag is significant, while RLMerr is not, suggesting that we should stick with the lag model.

#Let's pick the optimum bandwidth for our regression
bwVal <- GWmodel::bw.gwr(gini ~ unemp + educ + fem + nonwhite,
                         data = model %>% sf::as_Spatial(), 
                         approach = 'AICc', kernel = 'bisquare', 
                         adaptive = TRUE)
Adaptive bandwidth (number of nearest neighbours): 113 AICc value: -684.8114 
Adaptive bandwidth (number of nearest neighbours): 78 AICc value: -692.4784 
Adaptive bandwidth (number of nearest neighbours): 54 AICc value: -695.3735 
Adaptive bandwidth (number of nearest neighbours): 42 AICc value: -691.9389 
Adaptive bandwidth (number of nearest neighbours): 64 AICc value: -693.9018 
Adaptive bandwidth (number of nearest neighbours): 50 AICc value: -694.8839 
Adaptive bandwidth (number of nearest neighbours): 58 AICc value: -694.2964 
Adaptive bandwidth (number of nearest neighbours): 52 AICc value: -695.2733 
Adaptive bandwidth (number of nearest neighbours): 55 AICc value: -695.3017 
Adaptive bandwidth (number of nearest neighbours): 53 AICc value: -695.1687 
Adaptive bandwidth (number of nearest neighbours): 54 AICc value: -695.3735 
bwVal
[1] 54
#Now we can run our model based on this!
lm.gwr <- gwr.basic(gini ~ unemp + educ + fem + nonwhite,
                     data = model %>% sf::as_Spatial(), 
                     bw = bwVal, kernel = "bisquare", adaptive = TRUE)

#Let's check the output of the model!
print(lm.gwr)
   ***********************************************************************
   *                       Package   GWmodel                             *
   ***********************************************************************
   Program starts at: 2024-03-19 15:27:46.940592 
   Call:
   gwr.basic(formula = gini ~ unemp + educ + fem + nonwhite, data = model %>% 
    sf::as_Spatial(), bw = bwVal, kernel = "bisquare", adaptive = TRUE)

   Dependent (y) variable:  gini
   Independent variables:  unemp educ fem nonwhite
   Number of data points: 172
   ***********************************************************************
   *                    Results of Global Regression                     *
   ***********************************************************************

   Call:
    lm(formula = formula, data = data)

   Residuals:
      Min        1Q    Median        3Q       Max 
-0.111503 -0.022791 -0.001208  0.026237  0.090886 

   Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
   (Intercept)  0.42158    0.06444   6.542 7.12e-10 ***
   unemp        0.15442    0.25352   0.609   0.5433    
   educ        -0.49805    0.05697  -8.743 2.34e-15 ***
   fem          0.21109    0.12333   1.712   0.0888 .  
   nonwhite    -0.01861    0.01476  -1.261   0.2092    

   ---Significance stars
   Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1 
   Residual standard error: 0.03829 on 167 degrees of freedom
   Multiple R-squared: 0.348
   Adjusted R-squared: 0.3324 
   F-statistic: 22.29 on 4 and 167 DF,  p-value: 9.253e-15 
   ***Extra Diagnostic information
   Residual sum of squares: 0.24478
   Sigma(hat): 0.03794578
   AIC:  -627.3262
   AICc:  -626.8171
   BIC:  -749.5563
   ***********************************************************************
   *          Results of Geographically Weighted Regression              *
   ***********************************************************************

   *********************Model calibration information*********************
   Kernel function: bisquare 
   Adaptive bandwidth: 54 (number of nearest neighbours)
   Regression points: the same locations as observations are used.
   Distance metric: Euclidean distance metric is used.

   ****************Summary of GWR coefficient estimates:******************
                  Min.   1st Qu.    Median   3rd Qu.   Max.
   Intercept  0.167030  0.356849  0.443673  0.676007 0.9797
   unemp     -1.471005 -0.648716 -0.095804  0.116276 1.5008
   educ      -0.918110 -0.550286 -0.337617 -0.129685 0.0748
   fem       -0.983254 -0.178452  0.103148  0.255668 0.6599
   nonwhite  -0.128888 -0.010758  0.042518  0.062026 0.1272
   ************************Diagnostic information*************************
   Number of data points: 172 
   Effective number of parameters (2trace(S) - trace(S'S)): 41.15948 
   Effective degrees of freedom (n-2trace(S) + trace(S'S)): 130.8405 
   AICc (GWR book, Fotheringham, et al. 2002, p. 61, eq 2.33): -695.3735 
   AIC (GWR book, Fotheringham, et al. 2002,GWR p. 96, eq. 4.22): -746.2521 
   BIC (GWR book, Fotheringham, et al. 2002,GWR p. 61, eq. 2.34): -784.3095 
   Residual sum of squares: 0.1089552 
   R-square value:  0.7097961 
   Adjusted R-square value:  0.6178013 

   ***********************************************************************
   Program stops at: 2024-03-19 15:27:47.039451 

Woah, there’s a lot going on here. What might be more helpful is if we just map it - let’s look at how the relationship between inequality and education varies across space:

#Add the coefficient for education to the model data
model$gwr_exp_coeff <- lm.gwr$SDF$educ
#There's a built in function to plot this, but we need to transform our sf object into an sp object instead
model1 <- as(model, Class = "Spatial")
#plot it!
p1 <- spplot(model1, "gwr_exp_coeff")

p1

Ok, here we can see where the relationship varies across space. Let’s look at another variable - are they the same?


model$gwr_exp_coeff <- lm.gwr$SDF$nonwhite
model1 <- as(model, Class = "Spatial")
#plot it!
p2 <- spplot(model1, "gwr_exp_coeff")

p2

#Let's compare them side-by-side
library(cowplot)

Attaching package: ‘cowplot’

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

    theme_map

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

    stamp
plot_grid(p1, p2, 
          labels = c('A) Education', 'B) Percent Nonwhite'),
          hjust = -0.3, vjust = 4, label_size = 10)

What do you think - what patterns do you notice here?

Resources:

Burchfield, E. (n. d.) Spatial Regression. Retrieved from: https://www.emilyburchfield.org/courses/gsa/spatial_regression_lab.

Murugesan, M. (2017). NYC Neighborhoods. [Data Set]. Retrieved from: https://geodacenter.github.io/data-and-lab/NYC-Nhood-ACS-2008-12/.

Sun, S. (2022) 4.2 Spatial Error and Lag Models. Retrieved from: http://www.geo.hunter.cuny.edu/~ssun/R-Spatial/spregression.html#spatial-error-and-lag-models.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpMYXN0IHdlZWssIHdlIHN0YXJ0ZWQgdGFsa2luZyBhYm91dCBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBhbmQgaG93IHRvIG1lYXN1cmUgaXQuIFRoaXMgd2Vlaywgd2UnbGwgdGFsayBtb3JlIHNwZWNpZmljYWxseSBhYm91dCB3aGF0IHRvIGRvIGlmIHlvdSB3YW50IHRvIHJ1biBhIHJlZ3Jlc3Npb24gbW9kZWwgdGhhdCBoYXMgc3BhdGlhbCBkZXBlbmRlbmNlLiBGaXJzdCwgd2UnbGwgc3RhcnQgd2l0aCB0aGUgc2ltcGxlc3QgbW9kZWw6IGEgc3BhdGlhbCBlcnJvciBtb2RlbC4gV2UgdXNlIHRoaXMgbW9kZWwgd2hlbiB3ZSBiZWxpZXZlIHRoYXQgb3VyIGVycm9yIHRlcm0gaXMgc3BhdGlhbGx5IGNvcnJlbGF0ZWQuIA0KDQpgYGB7cn0NCiNsb2FkIGluIGRhdGEgYW5kIHBhY2thZ2VzIGZpcnN0DQpsaWJyYXJ5KHNmKSAgICAgDQpsaWJyYXJ5KGRwbHlyKSAgIA0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkoc3BhdGlhbHJlZykNCmxpYnJhcnkoR1dtb2RlbCkNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHNwZGVwKQ0KbGlicmFyeShzcERhdGEpDQojTGV0J3MgbG9hZCBpbiBzb21lIHBhY2thZ2VzIGFuZCBkYXRhDQpzZXR3ZCgifi9CaW5naGFtdG9uL0RJREEgMzcwL255Y19uZWlnaGJvcmhvb2RzIikNCm55YyA8LSBzdF9yZWFkKCJOWUNfTmhvb2QgQUNTMjAwOF8xMi5zaHAiKQ0KDQojQXMgYWx3YXlzLCBsZXQncyBwbG90IHRoZSBkYXRhIGZpcnN0DQoNCmdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGEgPSBueWMsIGFlcyhmaWxsPWFzLm51bWVyaWMoZ2luaSkpKSsNCiAgc2NhbGVfZmlsbF9zdGVwcygNCiAgICBuYW1lID0gIkluZXF1YWxpdHkiLA0KICAgIGxvdyA9ICJsaWdodHN0ZWVsYmx1ZTEiLA0KICAgIGhpZ2ggPSAidG9tYXRvMSIsDQogICAgbi5icmVha3MgPSA1LA0KICAgIHNob3cubGltaXRzID0gVCkrDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQoNCkxldCdzIHN0YXJ0IGJ5IHB1dHRpbmcgdG9nZXRoZXIgYSBzaW1wbGUgcmVncmVzc2lvbiBtb2RlbA0KDQpgYGB7cn0NCiNjcmVhdGUgb3VyIHZhcmlhYmxlcyBmb3IgdGhlIG1vZGVsOg0KbW9kZWwgPC0gbnljICU+JSANCiAgbXV0YXRlKHVuZW1wID0gcG9wdW5lbXBsby9wb3B0b3QsDQogICAgICAgICBlZHVjID0gaGlnaHNjaG9vbC9wb3B0b3QsDQogICAgICAgICBmZW0gPSBmZW1hbGUvcG9wdG90LA0KICAgICAgICAgbm9ud2hpdGUgPSAocG9wdG90LWV1cm9wZWFuKS9wb3B0b3QpICU+JSANCiAgc2VsZWN0KGMoZ2luaSwgdW5lbXAsIGVkdWMsIGZlbSwgbm9ud2hpdGUsIG1lZGlhbmluY28sIA0KICAgICAgICAgICBISHNpemUsIG1lZGlhbmFnZSwgY2FydG9kYl9pZCwgZ2VvbWV0cnkpKSAlPiUgDQogIGZpbHRlcighZ2luaSAlaW4lICJOQSIpICU+JSANCiAgbXV0YXRlKGdpbmkgPSBhcy5udW1lcmljKGdpbmkpKQ0KDQojUnVuIGEgYmFzaWMgbW9kZWwNCg0Kc3VtbWFyeShsbTEgPC0gbG0oZ2luaSB+IHVuZW1wICsgZWR1YyArIGZlbSArIG5vbndoaXRlLCBkYXRhID0gbW9kZWwpKQ0KYGBgDQpgYGB7cn0NCiNMZXQncyB0ZXN0IGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBpbiB0aGUgcmVzaWR1YWxzIQ0KI0NyZWF0ZSB0aGUgd2VpZ2h0cyBmaXJzdA0KDQpueWNfbGlzdCA8LSBwb2x5Mm5iKHN0X2dlb21ldHJ5KG1vZGVsKSkgDQpueWNfbGlzdCA8LSBueWNfbGlzdCAlPiUgbmIybGlzdHcoemVyby5wb2xpY3kgPSBUUlVFKSANCg0KI3J1biB0aGUgTW9yYW4ncyBJIHRlc3QgZm9yIHJlZ3Jlc3Npb24gcmVzaWR1YWxzDQpsbS5tb3JhbnRlc3QobG0xLCBueWNfbGlzdCkNCmBgYA0KDQpDbGVhcmx5IHRoZXJlIGlzIHNvbWUgc3BhdGlhbCBkZXBlbmRlbmNlIGluIHRoZSByZXNpZHVhbHMhIExldCdzIHJ1biBhIHNwYXRpYWwgZXJyb3IgbW9kZWwgdG8gY29udHJvbCBmb3IgaXQuIA0KDQpgYGB7cn0NCmxtX2Vycm9yIDwtIGVycm9yc2FybG0oZ2luaSB+IHVuZW1wICsgZWR1YyArIGZlbSArIG5vbndoaXRlLA0KICAgICAgICAgICAgICBkYXRhID0gbW9kZWwsDQogICAgICAgICAgICAgIGxpc3R3ID0gbnljX2xpc3QsDQogICAgICAgICAgICAgIHplcm8ucG9saWN5ID0gVFJVRSwgDQogICAgICAgICAgICAgIG5hLmFjdGlvbiA9IG5hLm9taXQpDQoNCnN1bW1hcnkobG1fZXJyb3IpDQpgYGANCkZyb20gdGhpcyBvdXRwdXQsIHdlIGNhbiBzZWUgdGhhdCB0aGUgbGFtZGEgdmFsdWUgaXMgMC43NSwgd2hpY2ggaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZXJlIGlzIHNpZ25pZmljYW50IHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGluIHRoZSByZXNpZHVhbHMuIA0KDQpIZXJlLCB3ZSBjYW4gc2VlIGFuIEFJQyB2YWx1ZSBvZiAtNzIzLjYzLCBjb21wYXJlZCB0byBhbiBBSUMgZm9yIHRoZSBsaW5lYXIgbW9kZWwgb2YgLTYyNy4zMy4gQmVjYXVzZSB0aGUgZXJyb3IgbW9kZWwncyBBSUMgaXMgbG93ZXIsIHRoaXMgc3VnZ2VzdHMgYW4gaW1wcm92ZW1lbnQgaW4gdGhlIG1vZGVsLiANCg0KTGV0J3MgdGVzdCBmb3Igc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gaW4gdGhlIHJlc2lkdWFscyBvZiB0aGlzIG1vZGVsOg0KDQpgYGB7cn0NCiNUaGlzIGNvZGUganVzdCBhbGxvd3MgbWUgdG8gbWFudWFsbHkgZXh0cmFjdCB0aGUgcmVzaWR1YWwgdmFsdWVzDQojTm9ybWFsbHksIFIgd2FudHMgYW4gTE0gb2JqZWN0IGZvciB0aGlzIHRlc3QgLSANCiNSdW5uaW5nIGEgc3BhdGlhbCBlcnJvciBtb2RlbCBkb2VzIG5vdCBvdXRwdXQgYW4gbG0gb2JqZWN0LCB3aGljaCBpcyB3aHkgd2UgbmVlZCB0byBkbyB0aGlzDQojQ3JlYXRlIGEgYmxhbmsgdmVjdG9yIHRoZSBsZW5ndGggb2YgdGhlIHZhcmlhYmxlDQpzZVJlc2lkdWFscyA8LSByZXAoMCwgbGVuZ3RoKG1vZGVsJGdpbmkpKQ0KI2NyZWF0ZSBhbiBpbmRleCBiYXNlZCBvbiB0aGUgb3JkZXIgb2YgdGhlIHJlc2lkdWFscyBpbiB0aGUgbG1fZXJyb3Igb2JqZWN0DQpyZXNJbmRleCA8LSBsbV9lcnJvciRyZXNpZHVhbHMgJT4lIG5hbWVzKCkgJT4lIGFzLmludGVnZXIoKTsNCiN1c2UgdGhlIGluZGV4IHRvIGZpbGwgaW4gdGhlIHNlUmVzaWR1YWxzIG9iamVjdCB3aXRoIGNvcnJlc3BvbmRpbmcgcmVzaWR1YWwgdmFsdWVzDQpzZVJlc2lkdWFsc1tyZXNJbmRleF0gPC0gbG1fZXJyb3IkcmVzaWR1YWxzDQoNCiNwZXJmb3JtIHRoZSBNb3JhbiBUZXN0DQpueWNfbGlzdCAlPiUNCiAgbW9yYW4udGVzdChzZVJlc2lkdWFscywgLiwgemVyby5wb2xpY3kgPSBUUlVFKSANCmBgYA0KDQpOb3csIHdlIGNhbiBzZWUgdGhhdCBvdXIgTW9yYW4gdGVzdCBpcyBpbnNpZ25pZmljYW50IC0gdGhpcyB0ZWxscyB1cyB0aGF0IHdlIGVmZmVjdGl2ZWx5IGNvbnRyb2xsZWQgZm9yIHRoZSBzcGF0aWFsIHZhcmlhdGlvbiBpbiB0aGUgZXJyb3IgdGVybS4NCg0KV2hhdCBpZiB3ZSB3YW50ZWQgdG8gcGVyZm9ybSBhIHNwYXRpYWwgbGFnIG1vZGVsIGluc3RlYWQ/DQoNCmBgYHtyfQ0KbG1fbGFnIDwtIGxhZ3NhcmxtKGdpbmkgfiB1bmVtcCArIGVkdWMgKyBmZW0gKyBub253aGl0ZSwNCiAgICAgICAgICAgICAgZGF0YSA9IG1vZGVsLA0KICAgICAgICAgICAgICBsaXN0dyA9IG55Y19saXN0LA0KICAgICAgICAgICAgICB6ZXJvLnBvbGljeSA9IFRSVUUsIA0KICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5vbWl0KQ0KDQpzdW1tYXJ5KGxtX2xhZykNCmBgYA0KQ2hlY2sgZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGluIHRoZXNlIHJlc2lkdWFsczoNCg0KYGBge3J9DQpzZVJlc2lkdWFscyA8LSByZXAoMCwgbGVuZ3RoKG1vZGVsJGdpbmkpKQ0KcmVzSW5kZXggPC0gbG1fbGFnJHJlc2lkdWFscyAlPiUgbmFtZXMoKSAlPiUgYXMuaW50ZWdlcigpOw0Kc2VSZXNpZHVhbHNbcmVzSW5kZXhdIDwtIGxtX2xhZyRyZXNpZHVhbHMNCg0KI3BlcmZvcm0gdGhlIE1vcmFuIFRlc3QNCm55Y19saXN0ICU+JQ0KICBtb3Jhbi50ZXN0KHNlUmVzaWR1YWxzLCAuLCB6ZXJvLnBvbGljeSA9IFRSVUUpIA0KYGBgDQpBZ2FpbiwgdGhpcyBtb2RlbCBhbHNvIGNvbnRyb2xzIGZvciBhdXRvY29ycmVsYXRpb24gaW4gdGhlIHJlc2lkdWFscywgc28gd2UncmUgaW4gZ29vZCBzaGFwZSBzbyBmYXIuDQoNCkJhc2VkIG9uIEFJQ3MgYWxvbmUsIHRoaXMgbW9kZWwgcGVyZm9ybXMgYSBiaXQgYmV0dGVyIHRoYW4gdGhlIGVycm9yIG1vZGVsICgtNzI3IDwgLTcyMykuIEJ1dCB0aGlzIGRpZmZlcmVuY2UgaXMgc28gc21hbGwgaXQgc2VlbXMgYWxtb3N0IGFyYml0cmFyeSAtIGxldCdzIHRlc3QgbW9yZSBmb3JtYWxseSB3aGV0aGVyIG9uZSBvciB0aGUgb3RoZXIgaXMgYSBnb29kIGZpdCB3aXRoIHRoZSBMYWdyYW5nZSBNdWx0aXBsaWVyIHRlc3QuDQoNCmBgYHtyfQ0KTE0gPC0gbG0uTE10ZXN0cyhsbTEsIG55Y19saXN0LCB0ZXN0ID0gImFsbCIpDQpMTQ0KYGBgDQpXZSdyZSBtb3N0IGludGVyZXN0ZWQgaW4gdGhlIExNZXJyIGFuZCBMTWxhZyByZXN1bHRzIC0gdW5zdXJwcmlzaW5nbHksIGJvdGggdHVybiBvdXQgdG8gYmUgc2lnbmlmaWNhbnQuIFNvIHRoaXMgdGVzdCBjYW4ndCB0ZWxsIHVzIHdoaWNoIG9uZSB0byBwaWNrIC0gbGV0J3MgbG9vayBhdCBhIG1vcmUgcm9idXN0IHRlc3QuIFdlIG9ubHkgbmVlZCB0byBsb29rIGF0IHRoZXNlIHRlc3RzIGlmIG91ciBpbml0aWFsIHRlc3RzIGFyZSBub3QgZGVmaW5pdGl2ZS4gSXQgdHVybnMgb3V0LCBSTE1lcnIgYW5kIFJMTWxhZyBhcmUgdGhlIHJvYnVzdCB0ZXN0IHJlc3VsdHMgaW4gdGhpcyBvdXRwdXQuIEFzIHlvdSBjYW4gc2VlLCBSTE1sYWcgaXMgc2lnbmlmaWNhbnQsIHdoaWxlIFJMTWVyciBpcyBub3QsIHN1Z2dlc3RpbmcgdGhhdCB3ZSBzaG91bGQgc3RpY2sgd2l0aCB0aGUgbGFnIG1vZGVsLiANCg0KDQoNCmBgYHtyfQ0KI0xldCdzIHBpY2sgdGhlIG9wdGltdW0gYmFuZHdpZHRoIGZvciBvdXIgcmVncmVzc2lvbg0KYndWYWwgPC0gR1dtb2RlbDo6YncuZ3dyKGdpbmkgfiB1bmVtcCArIGVkdWMgKyBmZW0gKyBub253aGl0ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWwgJT4lIHNmOjphc19TcGF0aWFsKCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIGFwcHJvYWNoID0gJ0FJQ2MnLCBrZXJuZWwgPSAnYmlzcXVhcmUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICBhZGFwdGl2ZSA9IFRSVUUpDQoNCmJ3VmFsDQpgYGANCmBgYHtyfQ0KI05vdyB3ZSBjYW4gcnVuIG91ciBtb2RlbCBiYXNlZCBvbiB0aGlzIQ0KbG0uZ3dyIDwtIGd3ci5iYXNpYyhnaW5pIH4gdW5lbXAgKyBlZHVjICsgZmVtICsgbm9ud2hpdGUsDQogICAgICAgICAgICAgICAgICAgICBkYXRhID0gbW9kZWwgJT4lIHNmOjphc19TcGF0aWFsKCksIA0KICAgICAgICAgICAgICAgICAgICAgYncgPSBid1ZhbCwga2VybmVsID0gImJpc3F1YXJlIiwgYWRhcHRpdmUgPSBUUlVFKQ0KDQojTGV0J3MgY2hlY2sgdGhlIG91dHB1dCBvZiB0aGUgbW9kZWwhDQpwcmludChsbS5nd3IpDQpgYGANCg0KV29haCwgdGhlcmUncyBhIGxvdCBnb2luZyBvbiBoZXJlLiBXaGF0IG1pZ2h0IGJlIG1vcmUgaGVscGZ1bCBpcyBpZiB3ZSBqdXN0IG1hcCBpdCAtIGxldCdzIGxvb2sgYXQgaG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBpbmVxdWFsaXR5IGFuZCBlZHVjYXRpb24gdmFyaWVzIGFjcm9zcyBzcGFjZToNCmBgYHtyfQ0KI0FkZCB0aGUgY29lZmZpY2llbnQgZm9yIGVkdWNhdGlvbiB0byB0aGUgbW9kZWwgZGF0YQ0KbW9kZWwkZ3dyX2V4cF9jb2VmZiA8LSBsbS5nd3IkU0RGJGVkdWMNCiNUaGVyZSdzIGEgYnVpbHQgaW4gZnVuY3Rpb24gdG8gcGxvdCB0aGlzLCBidXQgd2UgbmVlZCB0byB0cmFuc2Zvcm0gb3VyIHNmIG9iamVjdCBpbnRvIGFuIHNwIG9iamVjdCBpbnN0ZWFkDQptb2RlbDEgPC0gYXMobW9kZWwsIENsYXNzID0gIlNwYXRpYWwiKQ0KI3Bsb3QgaXQhDQpwMSA8LSBzcHBsb3QobW9kZWwxLCAiZ3dyX2V4cF9jb2VmZiIpDQoNCnAxDQpgYGANCk9rLCBoZXJlIHdlIGNhbiBzZWUgd2hlcmUgdGhlIHJlbGF0aW9uc2hpcCB2YXJpZXMgYWNyb3NzIHNwYWNlLiBMZXQncyBsb29rIGF0IGFub3RoZXIgdmFyaWFibGUgLSBhcmUgdGhleSB0aGUgc2FtZT8NCg0KYGBge3J9DQoNCm1vZGVsJGd3cl9leHBfY29lZmYgPC0gbG0uZ3dyJFNERiRub253aGl0ZQ0KbW9kZWwxIDwtIGFzKG1vZGVsLCBDbGFzcyA9ICJTcGF0aWFsIikNCiNwbG90IGl0IQ0KcDIgPC0gc3BwbG90KG1vZGVsMSwgImd3cl9leHBfY29lZmYiKQ0KDQpwMg0KDQpgYGANCg0KYGBge3J9DQojTGV0J3MgY29tcGFyZSB0aGVtIHNpZGUtYnktc2lkZQ0KbGlicmFyeShjb3dwbG90KQ0KDQpwbG90X2dyaWQocDEsIHAyLCANCiAgICAgICAgICBsYWJlbHMgPSBjKCdBKSBFZHVjYXRpb24nLCAnQikgUGVyY2VudCBOb253aGl0ZScpLA0KICAgICAgICAgIGhqdXN0ID0gLTAuMywgdmp1c3QgPSA0LCBsYWJlbF9zaXplID0gMTApDQpgYGANCg0KV2hhdCBkbyB5b3UgdGhpbmsgLSB3aGF0IHBhdHRlcm5zIGRvIHlvdSBub3RpY2UgaGVyZT8NCg0KDQpSZXNvdXJjZXM6DQoNCkJ1cmNoZmllbGQsIEUuIChuLiBkLikgU3BhdGlhbCBSZWdyZXNzaW9uLiBSZXRyaWV2ZWQgZnJvbTogaHR0cHM6Ly93d3cuZW1pbHlidXJjaGZpZWxkLm9yZy9jb3Vyc2VzL2dzYS9zcGF0aWFsX3JlZ3Jlc3Npb25fbGFiLiANCg0KTXVydWdlc2FuLCBNLiAoMjAxNykuIE5ZQyBOZWlnaGJvcmhvb2RzLiBbRGF0YSBTZXRdLiBSZXRyaWV2ZWQgZnJvbTogaHR0cHM6Ly9nZW9kYWNlbnRlci5naXRodWIuaW8vZGF0YS1hbmQtbGFiL05ZQy1OaG9vZC1BQ1MtMjAwOC0xMi8uIA0KDQpTdW4sIFMuICgyMDIyKSA0LjIgU3BhdGlhbCBFcnJvciBhbmQgTGFnIE1vZGVscy4gUmV0cmlldmVkIGZyb206IGh0dHA6Ly93d3cuZ2VvLmh1bnRlci5jdW55LmVkdS9+c3N1bi9SLVNwYXRpYWwvc3ByZWdyZXNzaW9uLmh0bWwjc3BhdGlhbC1lcnJvci1hbmQtbGFnLW1vZGVscy4gDQo=