This R code aims to produce mosquito risk maps using remote sensing data.

Part 1: Using collected variables

1. Investigating correlation among raw predictors (17 raw variables)

library(raster); library(sf)
# Specify the path that contains the raster data layers
path<-"F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Mosquito_Test1"
# List all files with tif extension from above folder
predictors<-list.files(path=path,pattern = "tif$",full.names = T)
# Stack all layers together 
predictors<-stack(predictors)

# Read the shapefile containing mosquito count sampling locations (you can use data processed above shape_final)

Mosquito_sampling<-shapefile("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Mosquito_Sampling\\Mix_House_Mosquito.shp") # Upload mosquito points
# Convert it to sf object 
Mos_points<-st_as_sf(Mosquito_sampling)

rownames(Mos_points)<-NULL # Reset index 

1.1. Extract variable values at each mosquito sampling locations

# Extract values from raster layers using sampling locations.
Value_Extract<-raster::extract(predictors,Mos_points,df=T)

# Select only varibles from column 2. The first column is ID, and it is useless
my_data <- Value_Extract[, c(2:ncol(Value_Extract))]
# Look at first few rows
head(my_data)

1.2. Looking at correlation among predictors and its histograms.

# Combine corelation coeffieints and tests

library(correlation)
library(psych)

head(correlation::correlation(plot_data, include_factors = TRUE, method = "pearson"))
Parameter1 |     Parameter2 |     r |         95% CI |     t |  df |      p |  Method | n_Obs
---------------------------------------------------------------------------------------------
DEM        |   EVI_Mean_1km | -0.23 | [-0.32, -0.15] | -5.19 | 467 | < .001 | Pearson |   469
DEM        |  EVI_Mean_500m | -0.23 | [-0.32, -0.15] | -5.20 | 467 | < .001 | Pearson |   469
DEM        |  NDVI_Mean_1km | -0.07 | [-0.16,  0.02] | -1.47 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_200m | -0.07 | [-0.16,  0.02] | -1.60 | 467 | > .999 | Pearson |   469
DEM        |  NDVI_Mean_2km | -0.08 | [-0.17,  0.01] | -1.82 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_500m | -0.07 | [-0.15,  0.03] | -1.41 | 467 | > .999 | Pearson |   469
# Plot correlation matrix 
library("PerformanceAnalytics")
chart.Correlation(my_data, histogram=TRUE, pch=19)

# Making histogram
multi.hist(my_data,dcol="red")

1.3. Correlation test

We see that some variables tend to be strong correlated, particularly EVI, NDVI, temperature data. Hence, correlation test is conducted to see if there is a signigicant correlation or not for those variables.

  • Correlation test for EVI variables
EVI_variable<-my_data[,c(2,3,4)]

EVI_test=correlation::correlation(EVI_variable, include_factors = TRUE, method = "pearson")
# setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Test")
# write.csv(EVI_test,file="EVI_corelation.csv",row.names = F)

EVI_test
Parameter1 | Parameter2 |    r |       95% CI |     t |  df |      p |  Method | n_Obs
--------------------------------------------------------------------------------------
EVI_max    |   EVI_mean | 0.97 | [0.96, 0.97] | 81.15 | 467 | < .001 | Pearson |   469
EVI_max    |    EVI_min | 0.87 | [0.85, 0.89] | 38.54 | 467 | < .001 | Pearson |   469
EVI_mean   |    EVI_min | 0.96 | [0.95, 0.97] | 73.37 | 467 | < .001 | Pearson |   469
  • Correlation test for NDVI variables
library(dplyr)

NDVI_varaible<-my_data %>% select(NDVI_max:NDVI_min)

NDVI_test=correlation::correlation(NDVI_varaible, include_factors = TRUE, method = "pearson")
NDVI_test
Parameter1 | Parameter2 |    r |       95% CI |     t |  df |      p |  Method | n_Obs
--------------------------------------------------------------------------------------
NDVI_max   |  NDVI_mean | 0.96 | [0.95, 0.96] | 69.89 | 467 | < .001 | Pearson |   469
NDVI_max   |   NDVI_min | 0.79 | [0.75, 0.82] | 27.68 | 467 | < .001 | Pearson |   469
NDVI_mean  |   NDVI_min | 0.93 | [0.92, 0.95] | 56.90 | 467 | < .001 | Pearson |   469
# write.csv(NDVI_test,file="NDVI_corelation.csv",row.names = F)
  • Correlation test for Temp variables
Temp_variable<-my_data %>% select(Temp_lag_mean:Temp_min)
Temp_test=correlation::correlation(Temp_variable, include_factors = TRUE, method = "pearson")
Temp_test
Parameter1    | Parameter2 |    r |       95% CI |     t |  df |      p |  Method | n_Obs
-----------------------------------------------------------------------------------------
Temp_lag_mean |   Temp_max | 0.87 | [0.85, 0.89] | 38.66 | 467 | < .001 | Pearson |   469
Temp_lag_mean |  Temp_mean | 0.80 | [0.76, 0.83] | 28.34 | 467 | < .001 | Pearson |   469
Temp_lag_mean |   Temp_min | 0.28 | [0.20, 0.37] |  6.41 | 467 | < .001 | Pearson |   469
Temp_max      |  Temp_mean | 0.86 | [0.83, 0.88] | 35.90 | 467 | < .001 | Pearson |   469
Temp_max      |   Temp_min | 0.34 | [0.25, 0.41] |  7.73 | 467 | < .001 | Pearson |   469
Temp_mean     |   Temp_min | 0.72 | [0.67, 0.76] | 22.47 | 467 | < .001 | Pearson |   469

As the land cover has five classes, so we need to convert it to factors, not integers.

# Encode land cover as factor and number of mosquito (Count) as integer 
my_data$Landcover<-as.factor(my_data$Landcover)
# Add Count column to the dataset
my_data$Count<-Mos_points$Sum_ttl

my_data$Count<-as.integer(my_data$Count) # Convert it to be count data (integer)
# Check overdisspersion for Count variable
print( mean(my_data$Count)); print(var(my_data$Count)) # Our count data is overdispersion as variance is greater than mean
[1] 34.78038
[1] 30698.91
# If someone wants to export data to your local computer
# setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito")
# write.csv(my_data,"Myfile.csv")
# Summary of our dataset all variables 
# summary(my_data)

Splitting data into training and testing sets

library(caTools)
# Splitting data into training and testing
set.seed(123) 
sample = sample.split(my_data$Count, SplitRatio = 0.8)
train = subset(my_data, sample == TRUE)
test  = subset(my_data, sample == FALSE)
  • Negative binomial model
library(caret)
ne_model<-glm(Count~., data=train, family =  negative.binomial(theta = 1) ) # Set the theta=2 provides better model
glm.fit: algorithm did not converge
predictions<- predict(ne_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 
  • Stepwise selection with negative binomial model
variable_select<-stepAIC(ne_model, trace = F)
glm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not converge
variable_select$anova
Stepwise Model Path 
Analysis of Deviance Table

Initial Model:
Count ~ DEM + EVI_max + EVI_mean + EVI_min + Landcover + NDVI_max + 
    NDVI_mean + NDVI_min + NDWI + Pop_VN + Rain_lag_mean + Rain_max + 
    Rain_mean + Rain_min + Temp_lag_mean + Temp_max + Temp_mean + 
    Temp_min

Final Model:
Count ~ EVI_max + EVI_mean + EVI_min + NDVI_max + NDVI_mean + 
    NDVI_min + NDWI + Pop_VN + Rain_lag_mean + Rain_max + Rain_mean + 
    Rain_min + Temp_lag_mean + Temp_max + Temp_mean

         Step Df  Deviance Resid. Df Resid. Dev       AIC
1                                361  30396.867 32250.929
2 - Landcover  3  7693.381       364  22703.487 24551.548
3       - DEM  1 21664.025       365   1039.461  2885.522

2. Investigating correlation among raw predictors after filtering strongly correlated raw variables and model testing

library(raster); library(sf)
# Specify the path that contains the raster data layers
path<-"F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Mosquito_Test1\\Variable_Selection"
# List all files with tif extension from above folder
predictors<-list.files(path=path,pattern = "tif$",full.names = T)
# Stack all layers together 
predictors<-stack(predictors)

2.1. Extract variable values at each mosquito sampling locations

# Extract values from raster layers using sampling locations.
Value_Extract<-raster::extract(predictors,Mos_points,df=T)

# Select only varibles from column 2. The first column is ID, and it is useless
my_data <- Value_Extract[, c(2:ncol(Value_Extract))]
# Look at first few rows
head(my_data)

2.2. Looking at correlation among predictors and its histograms.

# Combine corelation coeffieints and tests

library(correlation)
library(psych)

head(correlation::correlation(plot_data, include_factors = TRUE, method = "pearson"))
Parameter1 |     Parameter2 |     r |         95% CI |     t |  df |      p |  Method | n_Obs
---------------------------------------------------------------------------------------------
DEM        |   EVI_Mean_1km | -0.23 | [-0.32, -0.15] | -5.19 | 467 | < .001 | Pearson |   469
DEM        |  EVI_Mean_500m | -0.23 | [-0.32, -0.15] | -5.20 | 467 | < .001 | Pearson |   469
DEM        |  NDVI_Mean_1km | -0.07 | [-0.16,  0.02] | -1.47 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_200m | -0.07 | [-0.16,  0.02] | -1.60 | 467 | > .999 | Pearson |   469
DEM        |  NDVI_Mean_2km | -0.08 | [-0.17,  0.01] | -1.82 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_500m | -0.07 | [-0.15,  0.03] | -1.41 | 467 | > .999 | Pearson |   469
# Plot correlation matrix 
library("PerformanceAnalytics")
chart.Correlation(my_data, histogram=TRUE, pch=19)

# Making histogram
multi.hist(my_data,dcol="red")

2.3. Mosquito Risk prediction using negative binomial

  • As the land cover has five classes, so we need to convert it to factors, not integers.
# Encode land cover as factor and number of mosquito (Count) as integer 
my_data$Landcover<-as.factor(my_data$Landcover)
# Add Count column to the dataset
my_data$Count<-Mos_points$Sum_ttl

my_data$Count<-as.integer(my_data$Count) # Convert it to be count data (integer)
# Check overdisspersion for Count variable
print( mean(my_data$Count)); print(var(my_data$Count)) # Our count data is overdispersion as variance is greater than mean
[1] 34.78038
[1] 30698.91
# If someone wants to export data to your local computer
# setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito")
# write.csv(my_data,"Myfile.csv")
# Summary of our dataset all variables 
# summary(my_data)
  • Splitting data into training and testing sets
library(caTools)
# Splitting data into training and testing
set.seed(123) 
sample = sample.split(my_data$Count, SplitRatio = 0.80)
train = subset(my_data, sample == TRUE)
test  = subset(my_data, sample == FALSE)
  • Negative binomial model
library(caret)
ne_model<-glm(Count~., data=train, family =  negative.binomial(theta = 1) ) # Set the theta=2 provides better model
glm.fit: algorithm did not converge
predictions<- predict(ne_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 
  • Summarize the model
summary(ne_model)

Call:
glm(formula = Count ~ ., family = negative.binomial(theta = 1), 
    data = train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.4496  -1.7864  -1.0716  -0.3327   8.5400  

Coefficients:
                Estimate Std. Error t value Pr(>|t|)    
(Intercept)   -11.087016   8.537302  -1.299 0.194878    
DEM            -0.011971   0.066005  -0.181 0.856181    
EVI_mean        6.541186   3.169654   2.064 0.039750 *  
Landcover2     -1.786185   1.009456  -1.769 0.077649 .  
Landcover3     -0.743459   1.925351  -0.386 0.699615    
Landcover4      0.387634   0.628261   0.617 0.537620    
NDVI_mean      -0.065095   4.540120  -0.014 0.988568    
NDWI            0.702064   2.010056   0.349 0.727083    
Pop_VN         -0.020802   0.005441  -3.823 0.000155 ***
Rain_lag_mean  -0.100331   0.095621  -1.049 0.294749    
Rain_max        0.002300   0.041928   0.055 0.956286    
Rain_mean       0.204739   0.293565   0.697 0.485980    
Rain_min        1.400129   0.990191   1.414 0.158210    
Temp_lag_mean  -0.302510   0.242506  -1.247 0.213033    
Temp_mean       0.665115   0.258315   2.575 0.010420 *  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for Negative Binomial(1) family taken to be 9.285492)

    Null deviance: 1803.8  on 381  degrees of freedom
Residual deviance: 1071.7  on 367  degrees of freedom
  (4 observations deleted due to missingness)
AIC: 2913.7

Number of Fisher Scoring iterations: 25
Sen_output<-predict(predictors, ne_model, type="response",progress="window")

setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Predicted_Maps")
The working directory was changed to F:/Research/Research_Cooperation/ILRI_Mosquito_Mapping/Mosquito/Predicted_Maps 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.
writeRaster(Sen_output, filename = "Map_Test1.tif")
  • Stepwise selection with negative binomial model
variable_select<-stepAIC(ne_model, trace = F)
glm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not converge
variable_select$anova
Stepwise Model Path 
Analysis of Deviance Table

Initial Model:
Count ~ DEM + EVI_mean + Landcover + NDVI_mean + NDWI + Pop_VN + 
    Rain_lag_mean + Rain_max + Rain_mean + Rain_min + Temp_lag_mean + 
    Temp_mean

Final Model:
Count ~ DEM + EVI_mean + NDVI_mean + NDWI + Pop_VN + Rain_lag_mean + 
    Rain_max + Rain_mean + Rain_min + Temp_lag_mean + Temp_mean


  Step Df Deviance Resid. Df Resid. Dev      AIC
1                        367   1071.675 2913.736

2.4. Mosquito Risk prediction using zero-inflated negative binomial

library(pscl)
package 㤼㸱pscl㤼㸲 was built under R version 4.0.3Classes and Methods for R developed in the
Political Science Computational Laboratory
Department of Political Science
Stanford University
Simon Jackman
hurdle and zeroinfl functions by Achim Zeileis
zero_model<-zeroinfl(Count~., data=train, dist = "negbin",link = "logit")
system is computationally singular: reciprocal condition number = 4.02227e-39FALSE
predictions<- predict(zero_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 

Part 2: Using buffered computed variables

3. Investigating correlation of derived variables using buffer distance and model testing (36 derived variables)

library(raster); library(sf)
# Specify the path that contains the raster data layers
path<-"F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Cover_Ratio"
# List all files with tif extension from above folder
predictors<-list.files(path=path,pattern = "tif$",full.names = T)
# Stack all layers together 
predictors<-stack(predictors)

3.1. Extract variable values at each mosquito sampling locations

# Extract values from raster layers using sampling locations.
Value_Extract<-raster::extract(predictors,Mos_points,df=T)

# Select only varibles from column 2. The first column is ID, and it is useless
my_data <- Value_Extract[, c(2:ncol(Value_Extract))]
# Look at first few rows
head(my_data)

write.csv(my_data,file="Buffered_computed.csv",row.names =F)

3.2. Looking at correlation among predictors and its histograms.

# Combine corelation coeffieints and tests

library(correlation)
library(psych)

head(correlation::correlation(plot_data, include_factors = TRUE, method = "pearson"))
Parameter1 |     Parameter2 |     r |         95% CI |     t |  df |      p |  Method | n_Obs
---------------------------------------------------------------------------------------------
DEM        |   EVI_Mean_1km | -0.23 | [-0.32, -0.15] | -5.19 | 467 | < .001 | Pearson |   469
DEM        |  EVI_Mean_500m | -0.23 | [-0.32, -0.15] | -5.20 | 467 | < .001 | Pearson |   469
DEM        |  NDVI_Mean_1km | -0.07 | [-0.16,  0.02] | -1.47 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_200m | -0.07 | [-0.16,  0.02] | -1.60 | 467 | > .999 | Pearson |   469
DEM        |  NDVI_Mean_2km | -0.08 | [-0.17,  0.01] | -1.82 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_500m | -0.07 | [-0.15,  0.03] | -1.41 | 467 | > .999 | Pearson |   469
# Plot correlation matrix 
library("PerformanceAnalytics")
chart.Correlation(my_data, histogram=TRUE, pch=19)

# Making histogram
multi.hist(my_data,dcol="red")

3.3. Mosquito Risk prediction using negative binomial

# Add Count column to the dataset
my_data$Count<-Mos_points$Sum_ttl

my_data$Count<-as.integer(my_data$Count) # Convert it to be count data (integer)
head(my_data)
  • Splitting data into training and testing sets
library(caTools)
# Splitting data into training and testing
set.seed(123) 
sample = sample.split(my_data$Count, SplitRatio = 0.80)
train = subset(my_data, sample == TRUE)
test  = subset(my_data, sample == FALSE)
  • Negative binomial model
ne_model<-glm(Count~., data=train, family =  negative.binomial(theta = 1)) # Set the theta=2 provides better model
glm.fit: algorithm did not converge
predictions<- predict(ne_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 
  • Stepwise selection with negative binomial model

There are six variables excluded from the final model, NDVI_200m, NDVI_500m, EVI_1km, EVI_2km, Mean_rain_1km, Urban_200m.

variable_select<-stepAIC(ne_model, trace = F,direction ="backward")
glm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not converge
variable_select$anova
Stepwise Model Path 
Analysis of Deviance Table

Initial Model:
Count ~ DEM + EVI_Mean_1km + EVI_Mean_2km + EVI_Mean_500m + NDVI_Mean_1km + 
    NDVI_Mean_200m + NDVI_Mean_2km + NDVI_Mean_500m + NDWI_1km + 
    NDWI_200m + NDWI_2km + NDWI_500m + Pop_1km + Pop_2km + Rain_lag_1km + 
    Rain_lag_2km + Rain_max_1km + Rain_max_2km + Rain_Mean_1km + 
    Rain_Mean_2km + Rain_min_1km + Rain_min_2km + Rice_1km + 
    Rice_200m + Rice_2km + Rice_500m + Temp_lag_1km + Temp_Mean_1km + 
    Temp_Mean_2km + Urban_1km + Urban_200m + Urban_2km + Urban_500m

Final Model:
Count ~ EVI_Mean_500m + NDVI_Mean_1km + NDVI_Mean_2km + NDWI_1km + 
    NDWI_200m + NDWI_2km + NDWI_500m + Pop_1km + Pop_2km + Rain_lag_1km + 
    Rain_lag_2km + Rain_max_1km + Rain_max_2km + Rain_Mean_2km + 
    Rain_min_1km + Rain_min_2km + Rice_1km + Rice_200m + Rice_2km + 
    Rice_500m + Temp_lag_1km + Temp_Mean_1km + Temp_Mean_2km + 
    Urban_1km + Urban_2km + Urban_500m

              Step Df   Deviance Resid. Df Resid. Dev      AIC
1                                      352   840.8793 2735.734
2 - NDVI_Mean_200m  1 18.4776746       353   822.4016 2715.256
3     - Urban_200m  1  0.8779154       354   821.5237 2712.378
4  - Rain_Mean_1km  1  0.4261576       355   821.9498 2710.804
5 - NDVI_Mean_500m  1  0.5709151       356   822.5208 2709.375
6   - EVI_Mean_1km  1  0.8417544       357   823.3625 2708.217
7   - EVI_Mean_2km  1  1.8423857       358   825.2049 2708.059

3.4. Mosquito Risk prediction using zero-inflated negative binomial

library(pscl)
zero_model<-zeroinfl(Count~., data=train, dist = "negbin",link = "logit")
system is computationally singular: reciprocal condition number = 2.51058e-22FALSE
predictions<- predict(zero_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 

4.0 Investigating correlation of buffer derived variables after removing variables from first stepwise selection and model testing

library(raster); library(sf)
# Specify the path that contains the raster data layers
path<-"F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Cover_Ratio\\Variable_Selection"
# List all files with tif extension from above folder
predictors<-list.files(path=path,pattern = "tif$",full.names = T)
# Stack all layers together 
predictors<-stack(predictors)

4.1. Extract variable values at each mosquito sampling locations

# Extract values from raster layers using sampling locations.
Value_Extract<-raster::extract(predictors,Mos_points,df=T)

# Select only varibles from column 2. The first column is ID, and it is useless
my_data <- Value_Extract[, c(2:ncol(Value_Extract))]
# Look at first few rows
head(my_data)

4.2. Looking at correlation among predictors and its histograms.

# Combine corelation coeffieints and tests

library(correlation)
library(psych)

head(correlation::correlation(plot_data, include_factors = TRUE, method = "pearson"))
Parameter1 |     Parameter2 |     r |         95% CI |     t |  df |      p |  Method | n_Obs
---------------------------------------------------------------------------------------------
DEM        |   EVI_Mean_1km | -0.23 | [-0.32, -0.15] | -5.19 | 467 | < .001 | Pearson |   469
DEM        |  EVI_Mean_500m | -0.23 | [-0.32, -0.15] | -5.20 | 467 | < .001 | Pearson |   469
DEM        |  NDVI_Mean_1km | -0.07 | [-0.16,  0.02] | -1.47 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_200m | -0.07 | [-0.16,  0.02] | -1.60 | 467 | > .999 | Pearson |   469
DEM        |  NDVI_Mean_2km | -0.08 | [-0.17,  0.01] | -1.82 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_500m | -0.07 | [-0.15,  0.03] | -1.41 | 467 | > .999 | Pearson |   469
# Plot correlation matrix 
library("PerformanceAnalytics")
chart.Correlation(my_data, histogram=TRUE, pch=19)

# Making histogram
multi.hist(my_data,dcol="red")

4.3. Mosquito Risk prediction using negative binomial

# Add Count column to the dataset
my_data$Count<-Mos_points$Sum_ttl

my_data$Count<-as.integer(my_data$Count) # Convert it to be count data (integer)
head(my_data)
  • Splitting data into training and testing sets
library(caTools)
# Splitting data into training and testing
set.seed(123) 
sample = sample.split(my_data$Count, SplitRatio = 0.8)
train = subset(my_data, sample == TRUE)
test  = subset(my_data, sample == FALSE)
  • Negative binomial model
library(caret)
ne_model<-glm(Count~., data=train, family =  negative.binomial(theta = 1) ) # Set the theta=2 provides better model
predictions<- predict(ne_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 
  • Stepwise selection with negative binomial model
variable_select<-stepAIC(ne_model, trace = F)
glm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not converge
variable_select$anova
Stepwise Model Path 
Analysis of Deviance Table

Initial Model:
Count ~ DEM + EVI_Mean_500m + NDVI_Mean_1km + NDVI_Mean_2km + 
    NDWI_1km + NDWI_200m + NDWI_2km + NDWI_500m + Pop_1km + Pop_2km + 
    Rain_lag_1km + Rain_lag_2km + Rain_max_1km + Rain_max_2km + 
    Rain_Mean_2km + Rain_min_1km + Rain_min_2km + Rice_1km + 
    Rice_200m + Rice_2km + Rice_500m + Temp_lag_1km + Temp_Mean_1km + 
    Temp_Mean_2km + Urban_1km + Urban_2km + Urban_500m

Final Model:
Count ~ EVI_Mean_500m + NDVI_Mean_1km + NDVI_Mean_2km + NDWI_1km + 
    NDWI_200m + NDWI_2km + NDWI_500m + Pop_1km + Pop_2km + Rain_lag_1km + 
    Rain_lag_2km + Rain_max_1km + Rain_max_2km + Rain_Mean_2km + 
    Rain_min_1km + Rain_min_2km + Rice_1km + Rice_200m + Rice_2km + 
    Rice_500m + Temp_lag_1km + Temp_Mean_1km + Temp_Mean_2km + 
    Urban_1km + Urban_2km + Urban_500m


  Step Df Deviance Resid. Df Resid. Dev      AIC
1                        358   825.2049 2708.059

5.0 Investigating buffer derived variables after removing strongly correlated variables (section 4) and model testing

library(raster); library(sf)
# Specify the path that contains the raster data layers
path<-"F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Cover_Ratio\\Test_Selection\\Test_Stepwise"
# List all files with tif extension from above folder
predictors<-list.files(path=path,pattern = "tif$",full.names = T)
# Stack all layers together 
predictors<-stack(predictors)

5.1. Extract variable values at each mosquito sampling locations

# Extract values from raster layers using sampling locations.
Value_Extract<-raster::extract(predictors,Mos_points,df=T)

# Select only varibles from column 2. The first column is ID, and it is useless
my_data <- Value_Extract[, c(2:ncol(Value_Extract))]
# Look at first few rows
head(my_data)

5.2. Looking at correlation among predictors and its histograms.

# Combine corelation coeffieints and tests

library(correlation)
library(psych)

head(correlation::correlation(plot_data, include_factors = TRUE, method = "pearson"))
Parameter1 |     Parameter2 |     r |         95% CI |     t |  df |      p |  Method | n_Obs
---------------------------------------------------------------------------------------------
DEM        |   EVI_Mean_1km | -0.23 | [-0.32, -0.15] | -5.19 | 467 | < .001 | Pearson |   469
DEM        |  EVI_Mean_500m | -0.23 | [-0.32, -0.15] | -5.20 | 467 | < .001 | Pearson |   469
DEM        |  NDVI_Mean_1km | -0.07 | [-0.16,  0.02] | -1.47 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_200m | -0.07 | [-0.16,  0.02] | -1.60 | 467 | > .999 | Pearson |   469
DEM        |  NDVI_Mean_2km | -0.08 | [-0.17,  0.01] | -1.82 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_500m | -0.07 | [-0.15,  0.03] | -1.41 | 467 | > .999 | Pearson |   469
# Plot correlation matrix 
library("PerformanceAnalytics")
chart.Correlation(my_data, histogram=TRUE, pch=19)

# Making histogram
multi.hist(my_data,dcol="red")

5.3. Mosquito Risk prediction using negative binomial

# Add Count column to the dataset
my_data$Count<-Mos_points$Sum_ttl

my_data$Count<-as.integer(my_data$Count) # Convert it to be count data (integer)
head(my_data)
  • Splitting data into training and testing sets
library(caTools)
# Splitting data into training and testing
set.seed(123) 
sample = sample.split(my_data$Count, SplitRatio = 0.8)
train = subset(my_data, sample == TRUE)
test  = subset(my_data, sample == FALSE)
  • Negative binomial model
library(caret)
ne_model<-glm(Count~., data=train, family =  negative.binomial(theta = 1) ) # Set the theta=1
predictions<- predict(ne_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 
  • Stepwise selection with negative binomial model
variable_select<-stepAIC(ne_model, trace = F)
glm.fit: algorithm did not converge
variable_select$anova
Stepwise Model Path 
Analysis of Deviance Table

Initial Model:
Count ~ DEM + NDVI_Mean_1km + Pop_1km + Rain_lag_1km + Rain_max_1km + 
    Rain_min_1km

Final Model:
Count ~ DEM + NDVI_Mean_1km + Pop_1km + Rain_max_1km + Rain_min_1km


  Step Df Deviance Resid. Df Resid. Dev      AIC
1                        379   1105.799 2946.653
Sen_output<-predict(predictors, ne_model, type="response",progress="window")

setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Predicted_Maps")
The working directory was changed to F:/Research/Research_Cooperation/ILRI_Mosquito_Mapping/Mosquito/Predicted_Maps 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.
writeRaster(Sen_output, filename = "Risk_Map_stepwise.tif", overwrite=T)

5.4. Mosquito Risk prediction using zero-inflated negative binomial

library(pscl)
zero_model<-zeroinfl(Count~., data=train, dist = "negbin",link = "logit")

predictions<- predict(zero_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 

6.0 Investigating buffer derived variables with 1km buffer

library(raster); library(sf)
# Specify the path that contains the raster data layers
path<-"F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Cover_Ratio\\Stepwise_1km"
# List all files with tif extension from above folder
predictors<-list.files(path=path,pattern = "tif$",full.names = T)
# Stack all layers together 
predictors<-stack(predictors)

6.1. Extract variable values at each mosquito sampling locations

# Extract values from raster layers using sampling locations.
Value_Extract<-raster::extract(predictors,Mos_points,df=T)

# Select only varibles from column 2. The first column is ID, and it is useless
my_data <- Value_Extract[, c(2:ncol(Value_Extract))]
# Look at first few rows
head(my_data)

6.2. Looking at correlation among predictors and its histograms.

Note: Rice and NDVI had strong correlation >0.7

# Combine corelation coeffieints and tests

library(correlation)
library(psych)

head(correlation::correlation(plot_data, include_factors = TRUE, method = "pearson"))
Parameter1 |     Parameter2 |     r |         95% CI |     t |  df |      p |  Method | n_Obs
---------------------------------------------------------------------------------------------
DEM        |   EVI_Mean_1km | -0.23 | [-0.32, -0.15] | -5.19 | 467 | < .001 | Pearson |   469
DEM        |  EVI_Mean_500m | -0.23 | [-0.32, -0.15] | -5.20 | 467 | < .001 | Pearson |   469
DEM        |  NDVI_Mean_1km | -0.07 | [-0.16,  0.02] | -1.47 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_200m | -0.07 | [-0.16,  0.02] | -1.60 | 467 | > .999 | Pearson |   469
DEM        |  NDVI_Mean_2km | -0.08 | [-0.17,  0.01] | -1.82 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_500m | -0.07 | [-0.15,  0.03] | -1.41 | 467 | > .999 | Pearson |   469
# Plot correlation matrix 
library("PerformanceAnalytics")
chart.Correlation(my_data, histogram=TRUE, pch=19)

# Making histogram
multi.hist(my_data,dcol="red")

6.3. Mosquito Risk prediction using negative binomial

# Add Count column to the dataset
my_data$Count<-Mos_points$Sum_ttl

my_data$Count<-as.integer(my_data$Count) # Convert it to be count data (integer)
head(my_data)
  • Splitting data into training and testing sets
library(caTools)
# Splitting data into training and testing
set.seed(123) 
sample = sample.split(my_data$Count, SplitRatio = 0.8)
train = subset(my_data, sample == TRUE)
test  = subset(my_data, sample == FALSE)
  • Negative binomial model
library(caret)
ne_model<-glm(Count~., data=train, family =  negative.binomial(theta = 1) ) # Set the theta=1
predictions<- predict(ne_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 
  • Stepwise selection with negative binomial model
variable_select<-stepAIC(ne_model, trace = F)
glm.fit: algorithm did not convergeglm.fit: algorithm did not convergeglm.fit: algorithm did not converge
variable_select$anova
Stepwise Model Path 
Analysis of Deviance Table

Initial Model:
Count ~ DEM + NDVI_Mean_1km + NDWI_1km + Pop_1km + Rain_lag_1km + 
    Rain_max_1km + Rain_min_1km + Rice_1km + Temp_lag_1km

Final Model:
Count ~ DEM + NDVI_Mean_1km + Pop_1km + Rain_lag_1km + Rain_max_1km + 
    Rain_min_1km

        Step Df  Deviance Resid. Df Resid. Dev      AIC
1                               376   1098.774 2945.628
2 - NDWI_1km  1 0.5204301       377   1099.294 2944.149
3 - Rice_1km  1 1.9125103       378   1101.207 2944.061
Sen_output<-predict(predictors, ne_model, type="response",progress="window")

setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Predicted_Maps")
The working directory was changed to F:/Research/Research_Cooperation/ILRI_Mosquito_Mapping/Mosquito/Predicted_Maps 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.
writeRaster(Sen_output, filename = "Risk_Map_1km.tif",overwrite=T)

6.4. Mosquito Risk prediction using zero-inflated negative binomial

7. Test with selected variables like in Section 6, but all 2km buffered.

library(raster); library(sf)
# Specify the path that contains the raster data layers
path<-"F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Cover_Ratio\\2km_buffer"
# List all files with tif extension from above folder
predictors<-list.files(path=path,pattern = "tif$",full.names = T)
# Stack all layers together 
predictors<-stack(predictors)

7.1. Extract variable values at each mosquito sampling locations

# Extract values from raster layers using sampling locations.
Value_Extract<-raster::extract(predictors,Mos_points,df=T)

# Select only varibles from column 2. The first column is ID, and it is useless
my_data <- Value_Extract[, c(2:ncol(Value_Extract))]
# Look at first few rows
head(my_data)

7.2. Looking at correlation among predictors and its histograms.

# Combine corelation coeffieints and tests

library(correlation)
library(psych)

head(correlation::correlation(plot_data, include_factors = TRUE, method = "pearson"))
Parameter1 |     Parameter2 |     r |         95% CI |     t |  df |      p |  Method | n_Obs
---------------------------------------------------------------------------------------------
DEM        |   EVI_Mean_1km | -0.23 | [-0.32, -0.15] | -5.19 | 467 | < .001 | Pearson |   469
DEM        |  EVI_Mean_500m | -0.23 | [-0.32, -0.15] | -5.20 | 467 | < .001 | Pearson |   469
DEM        |  NDVI_Mean_1km | -0.07 | [-0.16,  0.02] | -1.47 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_200m | -0.07 | [-0.16,  0.02] | -1.60 | 467 | > .999 | Pearson |   469
DEM        |  NDVI_Mean_2km | -0.08 | [-0.17,  0.01] | -1.82 | 467 | > .999 | Pearson |   469
DEM        | NDVI_Mean_500m | -0.07 | [-0.15,  0.03] | -1.41 | 467 | > .999 | Pearson |   469
# Plot correlation matrix 
library("PerformanceAnalytics")
chart.Correlation(my_data, histogram=TRUE, pch=19)

# Making histogram
multi.hist(my_data,dcol="red")

7.3. Mosquito Risk prediction using negative binomial

# Add Count column to the dataset
my_data$Count<-Mos_points$Sum_ttl

my_data$Count<-as.integer(my_data$Count) # Convert it to be count data (integer)
head(my_data)
  • Splitting data into training and testing sets
library(caTools)
# Splitting data into training and testing
set.seed(123) 
sample = sample.split(my_data$Count, SplitRatio = 0.8)
train = subset(my_data, sample == TRUE)
test  = subset(my_data, sample == FALSE)
  • Negative binomial model
library(caret)
ne_model<-glm(Count~., data=train, family =  negative.binomial(theta = 1) ) # Set the theta=1
predictions<- predict(ne_model,newdata=test)

data.frame(R2 = R2(predictions, test$Count), RMSE = RMSE(predictions, test$Count), MAE = MAE(predictions,test$Count)) 
  • Stepwise selection with negative binomial model
variable_select<-stepAIC(ne_model, trace = F)

variable_select$anova
Stepwise Model Path 
Analysis of Deviance Table

Initial Model:
Count ~ NDVI_Mean_2km + NDWI_2km + Pop_2km + Rain_Mean_2km + 
    Rice_2km + Temp_Mean_2km

Final Model:
Count ~ NDVI_Mean_2km + Pop_2km + Rain_Mean_2km

             Step Df   Deviance Resid. Df Resid. Dev      AIC
1                                     379   1193.511 3034.366
2 - Temp_Mean_2km  1 0.12233845       380   1193.634 3032.488
3      - NDWI_2km  1 0.09926757       381   1193.733 3030.588
Sen_output<-predict(predictors, ne_model, type="response",progress="window")

setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Predicted_Maps")
The working directory was changed to F:/Research/Research_Cooperation/ILRI_Mosquito_Mapping/Mosquito/Predicted_Maps 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.
writeRaster(Sen_output, filename = "Risk_Map_7.tif",overwrite=T)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIFIgY29kZSBhaW1zIHRvIHByb2R1Y2UgbW9zcXVpdG8gcmlzayBtYXBzIHVzaW5nIHJlbW90ZSBzZW5zaW5nIGRhdGEuDQoNCiMgUGFydCAxOiBVc2luZyBjb2xsZWN0ZWQgdmFyaWFibGVzIA0KDQojIDEuIEludmVzdGlnYXRpbmcgY29ycmVsYXRpb24gYW1vbmcgcmF3IHByZWRpY3RvcnMgKDE3IHJhdyB2YXJpYWJsZXMpDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpOyBsaWJyYXJ5KHNmKQ0KIyBTcGVjaWZ5IHRoZSBwYXRoIHRoYXQgY29udGFpbnMgdGhlIHJhc3RlciBkYXRhIGxheWVycw0KcGF0aDwtIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXE1vc3F1aXRvX1Rlc3QxIg0KIyBMaXN0IGFsbCBmaWxlcyB3aXRoIHRpZiBleHRlbnNpb24gZnJvbSBhYm92ZSBmb2xkZXINCnByZWRpY3RvcnM8LWxpc3QuZmlsZXMocGF0aD1wYXRoLHBhdHRlcm4gPSAidGlmJCIsZnVsbC5uYW1lcyA9IFQpDQojIFN0YWNrIGFsbCBsYXllcnMgdG9nZXRoZXIgDQpwcmVkaWN0b3JzPC1zdGFjayhwcmVkaWN0b3JzKQ0KDQojIFJlYWQgdGhlIHNoYXBlZmlsZSBjb250YWluaW5nIG1vc3F1aXRvIGNvdW50IHNhbXBsaW5nIGxvY2F0aW9ucyAoeW91IGNhbiB1c2UgZGF0YSBwcm9jZXNzZWQgYWJvdmUgc2hhcGVfZmluYWwpDQoNCk1vc3F1aXRvX3NhbXBsaW5nPC1zaGFwZWZpbGUoIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXE1vc3F1aXRvX1NhbXBsaW5nXFxNaXhfSG91c2VfTW9zcXVpdG8uc2hwIikgIyBVcGxvYWQgbW9zcXVpdG8gcG9pbnRzDQojIENvbnZlcnQgaXQgdG8gc2Ygb2JqZWN0IA0KTW9zX3BvaW50czwtc3RfYXNfc2YoTW9zcXVpdG9fc2FtcGxpbmcpDQoNCnJvd25hbWVzKE1vc19wb2ludHMpPC1OVUxMICMgUmVzZXQgaW5kZXggDQoNCmBgYA0KDQojIyMgMS4xLiBFeHRyYWN0IHZhcmlhYmxlIHZhbHVlcyBhdCBlYWNoIG1vc3F1aXRvIHNhbXBsaW5nIGxvY2F0aW9ucw0KDQpgYGB7cn0NCiMgRXh0cmFjdCB2YWx1ZXMgZnJvbSByYXN0ZXIgbGF5ZXJzIHVzaW5nIHNhbXBsaW5nIGxvY2F0aW9ucy4NClZhbHVlX0V4dHJhY3Q8LXJhc3Rlcjo6ZXh0cmFjdChwcmVkaWN0b3JzLE1vc19wb2ludHMsZGY9VCkNCg0KIyBTZWxlY3Qgb25seSB2YXJpYmxlcyBmcm9tIGNvbHVtbiAyLiBUaGUgZmlyc3QgY29sdW1uIGlzIElELCBhbmQgaXQgaXMgdXNlbGVzcw0KbXlfZGF0YSA8LSBWYWx1ZV9FeHRyYWN0WywgYygyOm5jb2woVmFsdWVfRXh0cmFjdCkpXQ0KIyBMb29rIGF0IGZpcnN0IGZldyByb3dzDQpoZWFkKG15X2RhdGEpDQpgYGANCg0KIyMjIDEuMi4gTG9va2luZyBhdCBjb3JyZWxhdGlvbiBhbW9uZyBwcmVkaWN0b3JzIGFuZCBpdHMgaGlzdG9ncmFtcy4NCg0KYGBge3J9DQojIENvbWJpbmUgY29yZWxhdGlvbiBjb2VmZmllaW50cyBhbmQgdGVzdHMNCg0KbGlicmFyeShjb3JyZWxhdGlvbikNCmxpYnJhcnkocHN5Y2gpDQoNCmhlYWQoY29ycmVsYXRpb246OmNvcnJlbGF0aW9uKHBsb3RfZGF0YSwgaW5jbHVkZV9mYWN0b3JzID0gVFJVRSwgbWV0aG9kID0gInBlYXJzb24iKSkNCiMgUGxvdCBjb3JyZWxhdGlvbiBtYXRyaXggDQpsaWJyYXJ5KCJQZXJmb3JtYW5jZUFuYWx5dGljcyIpDQpjaGFydC5Db3JyZWxhdGlvbihteV9kYXRhLCBoaXN0b2dyYW09VFJVRSwgcGNoPTE5KQ0KIyBNYWtpbmcgaGlzdG9ncmFtDQptdWx0aS5oaXN0KG15X2RhdGEsZGNvbD0icmVkIikNCg0KYGBgDQoNCiMjIyAxLjMuIENvcnJlbGF0aW9uIHRlc3QNCg0KV2Ugc2VlIHRoYXQgc29tZSB2YXJpYWJsZXMgdGVuZCB0byBiZSBzdHJvbmcgY29ycmVsYXRlZCwgcGFydGljdWxhcmx5IEVWSSwgTkRWSSwgdGVtcGVyYXR1cmUgZGF0YS4gSGVuY2UsIGNvcnJlbGF0aW9uIHRlc3QgaXMgY29uZHVjdGVkIHRvIHNlZSBpZiB0aGVyZSBpcyBhIHNpZ25pZ2ljYW50IGNvcnJlbGF0aW9uIG9yIG5vdCBmb3IgdGhvc2UgdmFyaWFibGVzLg0KDQotIENvcnJlbGF0aW9uIHRlc3QgZm9yIGBFVklgIHZhcmlhYmxlcw0KDQpgYGB7cn0NCkVWSV92YXJpYWJsZTwtbXlfZGF0YVssYygyLDMsNCldDQoNCkVWSV90ZXN0PWNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihFVklfdmFyaWFibGUsIGluY2x1ZGVfZmFjdG9ycyA9IFRSVUUsIG1ldGhvZCA9ICJwZWFyc29uIikNCiMgc2V0d2QoIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXFRlc3QiKQ0KIyB3cml0ZS5jc3YoRVZJX3Rlc3QsZmlsZT0iRVZJX2NvcmVsYXRpb24uY3N2Iixyb3cubmFtZXMgPSBGKQ0KDQpFVklfdGVzdA0KYGBgDQoNCi0gQ29ycmVsYXRpb24gdGVzdCBmb3IgYE5EVklgIHZhcmlhYmxlcw0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQoNCk5EVklfdmFyYWlibGU8LW15X2RhdGEgJT4lIHNlbGVjdChORFZJX21heDpORFZJX21pbikNCg0KTkRWSV90ZXN0PWNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihORFZJX3ZhcmFpYmxlLCBpbmNsdWRlX2ZhY3RvcnMgPSBUUlVFLCBtZXRob2QgPSAicGVhcnNvbiIpDQpORFZJX3Rlc3QNCiMgd3JpdGUuY3N2KE5EVklfdGVzdCxmaWxlPSJORFZJX2NvcmVsYXRpb24uY3N2Iixyb3cubmFtZXMgPSBGKQ0KYGBgDQoNCi0gQ29ycmVsYXRpb24gdGVzdCBmb3IgYFRlbXBgIHZhcmlhYmxlcw0KDQpgYGB7cn0NClRlbXBfdmFyaWFibGU8LW15X2RhdGEgJT4lIHNlbGVjdChUZW1wX2xhZ19tZWFuOlRlbXBfbWluKQ0KVGVtcF90ZXN0PWNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihUZW1wX3ZhcmlhYmxlLCBpbmNsdWRlX2ZhY3RvcnMgPSBUUlVFLCBtZXRob2QgPSAicGVhcnNvbiIpDQpUZW1wX3Rlc3QNCmBgYA0KDQogQXMgdGhlIGxhbmQgY292ZXIgaGFzIGZpdmUgY2xhc3Nlcywgc28gd2UgbmVlZCB0byBjb252ZXJ0IGl0IHRvIGZhY3RvcnMsIG5vdCBpbnRlZ2Vycy4NCg0KYGBge3J9DQojIEVuY29kZSBsYW5kIGNvdmVyIGFzIGZhY3RvciBhbmQgbnVtYmVyIG9mIG1vc3F1aXRvIChDb3VudCkgYXMgaW50ZWdlciANCm15X2RhdGEkTGFuZGNvdmVyPC1hcy5mYWN0b3IobXlfZGF0YSRMYW5kY292ZXIpDQojIEFkZCBDb3VudCBjb2x1bW4gdG8gdGhlIGRhdGFzZXQNCm15X2RhdGEkQ291bnQ8LU1vc19wb2ludHMkU3VtX3R0bA0KDQpteV9kYXRhJENvdW50PC1hcy5pbnRlZ2VyKG15X2RhdGEkQ291bnQpICMgQ29udmVydCBpdCB0byBiZSBjb3VudCBkYXRhIChpbnRlZ2VyKQ0KIyBDaGVjayBvdmVyZGlzc3BlcnNpb24gZm9yIENvdW50IHZhcmlhYmxlDQpwcmludCggbWVhbihteV9kYXRhJENvdW50KSk7IHByaW50KHZhcihteV9kYXRhJENvdW50KSkgIyBPdXIgY291bnQgZGF0YSBpcyBvdmVyZGlzcGVyc2lvbiBhcyB2YXJpYW5jZSBpcyBncmVhdGVyIHRoYW4gbWVhbg0KIyBJZiBzb21lb25lIHdhbnRzIHRvIGV4cG9ydCBkYXRhIHRvIHlvdXIgbG9jYWwgY29tcHV0ZXINCiMgc2V0d2QoIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG8iKQ0KIyB3cml0ZS5jc3YobXlfZGF0YSwiTXlmaWxlLmNzdiIpDQojIFN1bW1hcnkgb2Ygb3VyIGRhdGFzZXQgYWxsIHZhcmlhYmxlcyANCiMgc3VtbWFyeShteV9kYXRhKQ0KYGBgDQoNCg0KIFNwbGl0dGluZyBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyANCg0KYGBge3J9DQpsaWJyYXJ5KGNhVG9vbHMpDQojIFNwbGl0dGluZyBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3RpbmcNCnNldC5zZWVkKDEyMykgDQpzYW1wbGUgPSBzYW1wbGUuc3BsaXQobXlfZGF0YSRDb3VudCwgU3BsaXRSYXRpbyA9IDAuOCkNCnRyYWluID0gc3Vic2V0KG15X2RhdGEsIHNhbXBsZSA9PSBUUlVFKQ0KdGVzdCAgPSBzdWJzZXQobXlfZGF0YSwgc2FtcGxlID09IEZBTFNFKQ0KYGBgDQoNCi0gTmVnYXRpdmUgYmlub21pYWwgbW9kZWwNCg0KYGBge3J9DQpsaWJyYXJ5KGNhcmV0KQ0KbmVfbW9kZWw8LWdsbShDb3VudH4uLCBkYXRhPXRyYWluLCBmYW1pbHkgPSAgbmVnYXRpdmUuYmlub21pYWwodGhldGEgPSAxKSApICMgU2V0IHRoZSB0aGV0YT0yIHByb3ZpZGVzIGJldHRlciBtb2RlbA0KcHJlZGljdGlvbnM8LSBwcmVkaWN0KG5lX21vZGVsLG5ld2RhdGE9dGVzdCkNCg0KZGF0YS5mcmFtZShSMiA9IFIyKHByZWRpY3Rpb25zLCB0ZXN0JENvdW50KSwgUk1TRSA9IFJNU0UocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBNQUUgPSBNQUUocHJlZGljdGlvbnMsdGVzdCRDb3VudCkpIA0KYGBgDQoNCg0KLSBTdGVwd2lzZSBzZWxlY3Rpb24gd2l0aCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCnZhcmlhYmxlX3NlbGVjdDwtc3RlcEFJQyhuZV9tb2RlbCwgdHJhY2UgPSBGKQ0KDQp2YXJpYWJsZV9zZWxlY3QkYW5vdmENCmBgYA0KDQoNCiMgMi4gSW52ZXN0aWdhdGluZyBjb3JyZWxhdGlvbiBhbW9uZyByYXcgcHJlZGljdG9ycyBhZnRlciBmaWx0ZXJpbmcgc3Ryb25nbHkgY29ycmVsYXRlZCByYXcgdmFyaWFibGVzIGFuZCBtb2RlbCB0ZXN0aW5nDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpOyBsaWJyYXJ5KHNmKQ0KIyBTcGVjaWZ5IHRoZSBwYXRoIHRoYXQgY29udGFpbnMgdGhlIHJhc3RlciBkYXRhIGxheWVycw0KcGF0aDwtIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXE1vc3F1aXRvX1Rlc3QxXFxWYXJpYWJsZV9TZWxlY3Rpb24iDQojIExpc3QgYWxsIGZpbGVzIHdpdGggdGlmIGV4dGVuc2lvbiBmcm9tIGFib3ZlIGZvbGRlcg0KcHJlZGljdG9yczwtbGlzdC5maWxlcyhwYXRoPXBhdGgscGF0dGVybiA9ICJ0aWYkIixmdWxsLm5hbWVzID0gVCkNCiMgU3RhY2sgYWxsIGxheWVycyB0b2dldGhlciANCnByZWRpY3RvcnM8LXN0YWNrKHByZWRpY3RvcnMpDQoNCmBgYA0KDQojIyMgMi4xLiBFeHRyYWN0IHZhcmlhYmxlIHZhbHVlcyBhdCBlYWNoIG1vc3F1aXRvIHNhbXBsaW5nIGxvY2F0aW9ucw0KDQpgYGB7cn0NCiMgRXh0cmFjdCB2YWx1ZXMgZnJvbSByYXN0ZXIgbGF5ZXJzIHVzaW5nIHNhbXBsaW5nIGxvY2F0aW9ucy4NClZhbHVlX0V4dHJhY3Q8LXJhc3Rlcjo6ZXh0cmFjdChwcmVkaWN0b3JzLE1vc19wb2ludHMsZGY9VCkNCg0KIyBTZWxlY3Qgb25seSB2YXJpYmxlcyBmcm9tIGNvbHVtbiAyLiBUaGUgZmlyc3QgY29sdW1uIGlzIElELCBhbmQgaXQgaXMgdXNlbGVzcw0KbXlfZGF0YSA8LSBWYWx1ZV9FeHRyYWN0WywgYygyOm5jb2woVmFsdWVfRXh0cmFjdCkpXQ0KIyBMb29rIGF0IGZpcnN0IGZldyByb3dzDQpoZWFkKG15X2RhdGEpDQpgYGANCiMjIyAyLjIuIExvb2tpbmcgYXQgY29ycmVsYXRpb24gYW1vbmcgcHJlZGljdG9ycyBhbmQgaXRzIGhpc3RvZ3JhbXMuDQoNCmBgYHtyfQ0KIyBDb21iaW5lIGNvcmVsYXRpb24gY29lZmZpZWludHMgYW5kIHRlc3RzDQoNCmxpYnJhcnkoY29ycmVsYXRpb24pDQpsaWJyYXJ5KHBzeWNoKQ0KDQpoZWFkKGNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihwbG90X2RhdGEsIGluY2x1ZGVfZmFjdG9ycyA9IFRSVUUsIG1ldGhvZCA9ICJwZWFyc29uIikpDQojIFBsb3QgY29ycmVsYXRpb24gbWF0cml4IA0KbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KY2hhcnQuQ29ycmVsYXRpb24obXlfZGF0YSwgaGlzdG9ncmFtPVRSVUUsIHBjaD0xOSkNCiMgTWFraW5nIGhpc3RvZ3JhbQ0KbXVsdGkuaGlzdChteV9kYXRhLGRjb2w9InJlZCIpDQoNCmBgYA0KDQojIyMgMi4zLiBNb3NxdWl0byBSaXNrIHByZWRpY3Rpb24gdXNpbmcgbmVnYXRpdmUgYmlub21pYWwNCg0KLSBBcyB0aGUgbGFuZCBjb3ZlciBoYXMgZml2ZSBjbGFzc2VzLCBzbyB3ZSBuZWVkIHRvIGNvbnZlcnQgaXQgdG8gZmFjdG9ycywgbm90IGludGVnZXJzLg0KDQpgYGB7cn0NCiMgRW5jb2RlIGxhbmQgY292ZXIgYXMgZmFjdG9yIGFuZCBudW1iZXIgb2YgbW9zcXVpdG8gKENvdW50KSBhcyBpbnRlZ2VyIA0KbXlfZGF0YSRMYW5kY292ZXI8LWFzLmZhY3RvcihteV9kYXRhJExhbmRjb3ZlcikNCiMgQWRkIENvdW50IGNvbHVtbiB0byB0aGUgZGF0YXNldA0KbXlfZGF0YSRDb3VudDwtTW9zX3BvaW50cyRTdW1fdHRsDQoNCm15X2RhdGEkQ291bnQ8LWFzLmludGVnZXIobXlfZGF0YSRDb3VudCkgIyBDb252ZXJ0IGl0IHRvIGJlIGNvdW50IGRhdGEgKGludGVnZXIpDQojIENoZWNrIG92ZXJkaXNzcGVyc2lvbiBmb3IgQ291bnQgdmFyaWFibGUNCnByaW50KCBtZWFuKG15X2RhdGEkQ291bnQpKTsgcHJpbnQodmFyKG15X2RhdGEkQ291bnQpKSAjIE91ciBjb3VudCBkYXRhIGlzIG92ZXJkaXNwZXJzaW9uIGFzIHZhcmlhbmNlIGlzIGdyZWF0ZXIgdGhhbiBtZWFuDQojIElmIHNvbWVvbmUgd2FudHMgdG8gZXhwb3J0IGRhdGEgdG8geW91ciBsb2NhbCBjb21wdXRlcg0KIyBzZXR3ZCgiRjpcXFJlc2VhcmNoXFxSZXNlYXJjaF9Db29wZXJhdGlvblxcSUxSSV9Nb3NxdWl0b19NYXBwaW5nXFxNb3NxdWl0byIpDQojIHdyaXRlLmNzdihteV9kYXRhLCJNeWZpbGUuY3N2IikNCiMgU3VtbWFyeSBvZiBvdXIgZGF0YXNldCBhbGwgdmFyaWFibGVzIA0KIyBzdW1tYXJ5KG15X2RhdGEpDQpgYGANCg0KLSBTcGxpdHRpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgDQoNCmBgYHtyfQ0KbGlicmFyeShjYVRvb2xzKQ0KIyBTcGxpdHRpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nDQpzZXQuc2VlZCgxMjMpIA0Kc2FtcGxlID0gc2FtcGxlLnNwbGl0KG15X2RhdGEkQ291bnQsIFNwbGl0UmF0aW8gPSAwLjgwKQ0KdHJhaW4gPSBzdWJzZXQobXlfZGF0YSwgc2FtcGxlID09IFRSVUUpDQp0ZXN0ICA9IHN1YnNldChteV9kYXRhLCBzYW1wbGUgPT0gRkFMU0UpDQpgYGANCg0KLSBOZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQpuZV9tb2RlbDwtZ2xtKENvdW50fi4sIGRhdGE9dHJhaW4sIGZhbWlseSA9ICBuZWdhdGl2ZS5iaW5vbWlhbCh0aGV0YSA9IDEpICkgIyBTZXQgdGhlIHRoZXRhPTIgcHJvdmlkZXMgYmV0dGVyIG1vZGVsDQpwcmVkaWN0aW9uczwtIHByZWRpY3QobmVfbW9kZWwsbmV3ZGF0YT10ZXN0KQ0KDQpkYXRhLmZyYW1lKFIyID0gUjIocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBSTVNFID0gUk1TRShwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIE1BRSA9IE1BRShwcmVkaWN0aW9ucyx0ZXN0JENvdW50KSkgDQpgYGANCg0KLSBTdW1tYXJpemUgdGhlIG1vZGVsDQoNCmBgYHtyfQ0Kc3VtbWFyeShuZV9tb2RlbCkNCmBgYA0KDQpgYGB7cn0NCiNTZW5fb3V0cHV0PC1wcmVkaWN0KHByZWRpY3RvcnMsIG5lX21vZGVsLCB0eXBlPSJyZXNwb25zZSIscHJvZ3Jlc3M9IndpbmRvdyIpDQoNCiNzZXR3ZCgiRjpcXFJlc2VhcmNoXFxSZXNlYXJjaF9Db29wZXJhdGlvblxcSUxSSV9Nb3NxdWl0b19NYXBwaW5nXFxNb3NxdWl0b1xcUHJlZGljdGVkX01hcHMiKQ0KDQojd3JpdGVSYXN0ZXIoU2VuX291dHB1dCwgZmlsZW5hbWUgPSAiTWFwX1Rlc3QxLnRpZiIpDQpgYGANCg0KLSBTdGVwd2lzZSBzZWxlY3Rpb24gd2l0aCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCnZhcmlhYmxlX3NlbGVjdDwtc3RlcEFJQyhuZV9tb2RlbCwgdHJhY2UgPSBGKQ0KDQp2YXJpYWJsZV9zZWxlY3QkYW5vdmENCmBgYA0KDQojIyMgMi40LiBNb3NxdWl0byBSaXNrIHByZWRpY3Rpb24gdXNpbmcgemVyby1pbmZsYXRlZCBuZWdhdGl2ZSBiaW5vbWlhbCANCg0KYGBge3J9DQpsaWJyYXJ5KHBzY2wpDQp6ZXJvX21vZGVsPC16ZXJvaW5mbChDb3VudH4uLCBkYXRhPXRyYWluLCBkaXN0ID0gIm5lZ2JpbiIsbGluayA9ICJsb2dpdCIpDQoNCnByZWRpY3Rpb25zPC0gcHJlZGljdCh6ZXJvX21vZGVsLG5ld2RhdGE9dGVzdCkNCg0KZGF0YS5mcmFtZShSMiA9IFIyKHByZWRpY3Rpb25zLCB0ZXN0JENvdW50KSwgUk1TRSA9IFJNU0UocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBNQUUgPSBNQUUocHJlZGljdGlvbnMsdGVzdCRDb3VudCkpIA0KYGBgDQoNCiMgUGFydCAyOiBVc2luZyBidWZmZXJlZCBjb21wdXRlZCB2YXJpYWJsZXMgDQoNCiMgMy4gSW52ZXN0aWdhdGluZyBjb3JyZWxhdGlvbiBvZiBkZXJpdmVkIHZhcmlhYmxlcyB1c2luZyBidWZmZXIgZGlzdGFuY2UgYW5kIG1vZGVsIHRlc3RpbmcgKDM2IGRlcml2ZWQgdmFyaWFibGVzKQ0KDQpgYGB7cn0NCmxpYnJhcnkocmFzdGVyKTsgbGlicmFyeShzZikNCiMgU3BlY2lmeSB0aGUgcGF0aCB0aGF0IGNvbnRhaW5zIHRoZSByYXN0ZXIgZGF0YSBsYXllcnMNCnBhdGg8LSJGOlxcUmVzZWFyY2hcXFJlc2VhcmNoX0Nvb3BlcmF0aW9uXFxJTFJJX01vc3F1aXRvX01hcHBpbmdcXE1vc3F1aXRvXFxDb3Zlcl9SYXRpbyINCiMgTGlzdCBhbGwgZmlsZXMgd2l0aCB0aWYgZXh0ZW5zaW9uIGZyb20gYWJvdmUgZm9sZGVyDQpwcmVkaWN0b3JzPC1saXN0LmZpbGVzKHBhdGg9cGF0aCxwYXR0ZXJuID0gInRpZiQiLGZ1bGwubmFtZXMgPSBUKQ0KIyBTdGFjayBhbGwgbGF5ZXJzIHRvZ2V0aGVyIA0KcHJlZGljdG9yczwtc3RhY2socHJlZGljdG9ycykNCmBgYA0KDQojIyMgMy4xLiBFeHRyYWN0IHZhcmlhYmxlIHZhbHVlcyBhdCBlYWNoIG1vc3F1aXRvIHNhbXBsaW5nIGxvY2F0aW9ucw0KDQpgYGB7cn0NCiMgRXh0cmFjdCB2YWx1ZXMgZnJvbSByYXN0ZXIgbGF5ZXJzIHVzaW5nIHNhbXBsaW5nIGxvY2F0aW9ucy4NClZhbHVlX0V4dHJhY3Q8LXJhc3Rlcjo6ZXh0cmFjdChwcmVkaWN0b3JzLE1vc19wb2ludHMsZGY9VCkNCg0KIyBTZWxlY3Qgb25seSB2YXJpYmxlcyBmcm9tIGNvbHVtbiAyLiBUaGUgZmlyc3QgY29sdW1uIGlzIElELCBhbmQgaXQgaXMgdXNlbGVzcw0KbXlfZGF0YSA8LSBWYWx1ZV9FeHRyYWN0WywgYygyOm5jb2woVmFsdWVfRXh0cmFjdCkpXQ0KIyBMb29rIGF0IGZpcnN0IGZldyByb3dzDQpoZWFkKG15X2RhdGEpDQoNCiMgd3JpdGUuY3N2KG15X2RhdGEsZmlsZT0iQnVmZmVyZWRfY29tcHV0ZWQuY3N2Iixyb3cubmFtZXMgPUYpDQpgYGANCg0KIyMjIDMuMi4gTG9va2luZyBhdCBjb3JyZWxhdGlvbiBhbW9uZyBwcmVkaWN0b3JzIGFuZCBpdHMgaGlzdG9ncmFtcy4NCg0KYGBge3J9DQojIENvbWJpbmUgY29yZWxhdGlvbiBjb2VmZmllaW50cyBhbmQgdGVzdHMNCg0KbGlicmFyeShjb3JyZWxhdGlvbikNCmxpYnJhcnkocHN5Y2gpDQoNCmhlYWQoY29ycmVsYXRpb246OmNvcnJlbGF0aW9uKHBsb3RfZGF0YSwgaW5jbHVkZV9mYWN0b3JzID0gVFJVRSwgbWV0aG9kID0gInBlYXJzb24iKSkNCiMgUGxvdCBjb3JyZWxhdGlvbiBtYXRyaXggDQpsaWJyYXJ5KCJQZXJmb3JtYW5jZUFuYWx5dGljcyIpDQpjaGFydC5Db3JyZWxhdGlvbihteV9kYXRhLCBoaXN0b2dyYW09VFJVRSwgcGNoPTE5KQ0KIyBNYWtpbmcgaGlzdG9ncmFtDQptdWx0aS5oaXN0KG15X2RhdGEsZGNvbD0icmVkIikNCg0KYGBgDQoNCiMjIyAzLjMuIE1vc3F1aXRvIFJpc2sgcHJlZGljdGlvbiB1c2luZyBuZWdhdGl2ZSBiaW5vbWlhbA0KDQpgYGB7cn0NCiMgQWRkIENvdW50IGNvbHVtbiB0byB0aGUgZGF0YXNldA0KbXlfZGF0YSRDb3VudDwtTW9zX3BvaW50cyRTdW1fdHRsDQoNCm15X2RhdGEkQ291bnQ8LWFzLmludGVnZXIobXlfZGF0YSRDb3VudCkgIyBDb252ZXJ0IGl0IHRvIGJlIGNvdW50IGRhdGEgKGludGVnZXIpDQpoZWFkKG15X2RhdGEpDQpgYGANCg0KLSBTcGxpdHRpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHNldHMgDQoNCmBgYHtyfQ0KbGlicmFyeShjYVRvb2xzKQ0KIyBTcGxpdHRpbmcgZGF0YSBpbnRvIHRyYWluaW5nIGFuZCB0ZXN0aW5nDQpzZXQuc2VlZCgxMjMpIA0Kc2FtcGxlID0gc2FtcGxlLnNwbGl0KG15X2RhdGEkQ291bnQsIFNwbGl0UmF0aW8gPSAwLjgwKQ0KdHJhaW4gPSBzdWJzZXQobXlfZGF0YSwgc2FtcGxlID09IFRSVUUpDQp0ZXN0ICA9IHN1YnNldChteV9kYXRhLCBzYW1wbGUgPT0gRkFMU0UpDQpgYGANCg0KLSBOZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCm5lX21vZGVsPC1nbG0oQ291bnR+LiwgZGF0YT10cmFpbiwgZmFtaWx5ID0gIG5lZ2F0aXZlLmJpbm9taWFsKHRoZXRhID0gMSkpICMgU2V0IHRoZSB0aGV0YT0yIHByb3ZpZGVzIGJldHRlciBtb2RlbA0KcHJlZGljdGlvbnM8LSBwcmVkaWN0KG5lX21vZGVsLG5ld2RhdGE9dGVzdCkNCg0KZGF0YS5mcmFtZShSMiA9IFIyKHByZWRpY3Rpb25zLCB0ZXN0JENvdW50KSwgUk1TRSA9IFJNU0UocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBNQUUgPSBNQUUocHJlZGljdGlvbnMsdGVzdCRDb3VudCkpIA0KYGBgDQoNCg0KLSBTdGVwd2lzZSBzZWxlY3Rpb24gd2l0aCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpUaGVyZSBhcmUgc2l4IHZhcmlhYmxlcyBleGNsdWRlZCBmcm9tIHRoZSBmaW5hbCBtb2RlbCwgTkRWSV8yMDBtLCBORFZJXzUwMG0sIEVWSV8xa20sIEVWSV8ya20sIE1lYW5fcmFpbl8xa20sIFVyYmFuXzIwMG0uDQoNCmBgYHtyfQ0KdmFyaWFibGVfc2VsZWN0PC1zdGVwQUlDKG5lX21vZGVsLCB0cmFjZSA9IEYsZGlyZWN0aW9uID0iYmFja3dhcmQiKQ0KDQp2YXJpYWJsZV9zZWxlY3QkYW5vdmENCmBgYA0KDQoNCiMjIyAzLjQuIE1vc3F1aXRvIFJpc2sgcHJlZGljdGlvbiB1c2luZyB6ZXJvLWluZmxhdGVkIG5lZ2F0aXZlIGJpbm9taWFsIA0KDQpgYGB7cn0NCmxpYnJhcnkocHNjbCkNCnplcm9fbW9kZWw8LXplcm9pbmZsKENvdW50fi4sIGRhdGE9dHJhaW4sIGRpc3QgPSAibmVnYmluIixsaW5rID0gImxvZ2l0IikNCg0KcHJlZGljdGlvbnM8LSBwcmVkaWN0KHplcm9fbW9kZWwsbmV3ZGF0YT10ZXN0KQ0KDQpkYXRhLmZyYW1lKFIyID0gUjIocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBSTVNFID0gUk1TRShwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIE1BRSA9IE1BRShwcmVkaWN0aW9ucyx0ZXN0JENvdW50KSkgDQpgYGANCg0KIyA0LjAgSW52ZXN0aWdhdGluZyBjb3JyZWxhdGlvbiBvZiBidWZmZXIgZGVyaXZlZCB2YXJpYWJsZXMgYWZ0ZXIgcmVtb3ZpbmcgdmFyaWFibGVzIGZyb20gZmlyc3Qgc3RlcHdpc2Ugc2VsZWN0aW9uIGFuZCBtb2RlbCB0ZXN0aW5nDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpOyBsaWJyYXJ5KHNmKQ0KIyBTcGVjaWZ5IHRoZSBwYXRoIHRoYXQgY29udGFpbnMgdGhlIHJhc3RlciBkYXRhIGxheWVycw0KcGF0aDwtIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXENvdmVyX1JhdGlvXFxWYXJpYWJsZV9TZWxlY3Rpb24iDQojIExpc3QgYWxsIGZpbGVzIHdpdGggdGlmIGV4dGVuc2lvbiBmcm9tIGFib3ZlIGZvbGRlcg0KcHJlZGljdG9yczwtbGlzdC5maWxlcyhwYXRoPXBhdGgscGF0dGVybiA9ICJ0aWYkIixmdWxsLm5hbWVzID0gVCkNCiMgU3RhY2sgYWxsIGxheWVycyB0b2dldGhlciANCnByZWRpY3RvcnM8LXN0YWNrKHByZWRpY3RvcnMpDQpgYGANCg0KIyMjIDQuMS4gRXh0cmFjdCB2YXJpYWJsZSB2YWx1ZXMgYXQgZWFjaCBtb3NxdWl0byBzYW1wbGluZyBsb2NhdGlvbnMNCg0KYGBge3J9DQojIEV4dHJhY3QgdmFsdWVzIGZyb20gcmFzdGVyIGxheWVycyB1c2luZyBzYW1wbGluZyBsb2NhdGlvbnMuDQpWYWx1ZV9FeHRyYWN0PC1yYXN0ZXI6OmV4dHJhY3QocHJlZGljdG9ycyxNb3NfcG9pbnRzLGRmPVQpDQoNCiMgU2VsZWN0IG9ubHkgdmFyaWJsZXMgZnJvbSBjb2x1bW4gMi4gVGhlIGZpcnN0IGNvbHVtbiBpcyBJRCwgYW5kIGl0IGlzIHVzZWxlc3MNCm15X2RhdGEgPC0gVmFsdWVfRXh0cmFjdFssIGMoMjpuY29sKFZhbHVlX0V4dHJhY3QpKV0NCiMgTG9vayBhdCBmaXJzdCBmZXcgcm93cw0KaGVhZChteV9kYXRhKQ0KYGBgDQoNCiMjIyA0LjIuIExvb2tpbmcgYXQgY29ycmVsYXRpb24gYW1vbmcgcHJlZGljdG9ycyBhbmQgaXRzIGhpc3RvZ3JhbXMuDQoNCmBgYHtyfQ0KIyBDb21iaW5lIGNvcmVsYXRpb24gY29lZmZpZWludHMgYW5kIHRlc3RzDQoNCmxpYnJhcnkoY29ycmVsYXRpb24pDQpsaWJyYXJ5KHBzeWNoKQ0KDQpoZWFkKGNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihwbG90X2RhdGEsIGluY2x1ZGVfZmFjdG9ycyA9IFRSVUUsIG1ldGhvZCA9ICJwZWFyc29uIikpDQojIFBsb3QgY29ycmVsYXRpb24gbWF0cml4IA0KbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KY2hhcnQuQ29ycmVsYXRpb24obXlfZGF0YSwgaGlzdG9ncmFtPVRSVUUsIHBjaD0xOSkNCiMgTWFraW5nIGhpc3RvZ3JhbQ0KbXVsdGkuaGlzdChteV9kYXRhLGRjb2w9InJlZCIpDQoNCmBgYA0KDQojIyMgNC4zLiBNb3NxdWl0byBSaXNrIHByZWRpY3Rpb24gdXNpbmcgbmVnYXRpdmUgYmlub21pYWwNCg0KYGBge3J9DQojIEFkZCBDb3VudCBjb2x1bW4gdG8gdGhlIGRhdGFzZXQNCm15X2RhdGEkQ291bnQ8LU1vc19wb2ludHMkU3VtX3R0bA0KDQpteV9kYXRhJENvdW50PC1hcy5pbnRlZ2VyKG15X2RhdGEkQ291bnQpICMgQ29udmVydCBpdCB0byBiZSBjb3VudCBkYXRhIChpbnRlZ2VyKQ0KaGVhZChteV9kYXRhKQ0KYGBgDQoNCi0gU3BsaXR0aW5nIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FUb29scykNCiMgU3BsaXR0aW5nIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZw0Kc2V0LnNlZWQoMTIzKSANCnNhbXBsZSA9IHNhbXBsZS5zcGxpdChteV9kYXRhJENvdW50LCBTcGxpdFJhdGlvID0gMC44KQ0KdHJhaW4gPSBzdWJzZXQobXlfZGF0YSwgc2FtcGxlID09IFRSVUUpDQp0ZXN0ICA9IHN1YnNldChteV9kYXRhLCBzYW1wbGUgPT0gRkFMU0UpDQpgYGANCg0KLSBOZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQpuZV9tb2RlbDwtZ2xtKENvdW50fi4sIGRhdGE9dHJhaW4sIGZhbWlseSA9ICBuZWdhdGl2ZS5iaW5vbWlhbCh0aGV0YSA9IDEpICkgIyBTZXQgdGhlIHRoZXRhPTIgcHJvdmlkZXMgYmV0dGVyIG1vZGVsDQpwcmVkaWN0aW9uczwtIHByZWRpY3QobmVfbW9kZWwsbmV3ZGF0YT10ZXN0KQ0KDQpkYXRhLmZyYW1lKFIyID0gUjIocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBSTVNFID0gUk1TRShwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIE1BRSA9IE1BRShwcmVkaWN0aW9ucyx0ZXN0JENvdW50KSkgDQpgYGANCg0KLSBTdGVwd2lzZSBzZWxlY3Rpb24gd2l0aCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCnZhcmlhYmxlX3NlbGVjdDwtc3RlcEFJQyhuZV9tb2RlbCwgdHJhY2UgPSBGKQ0KDQp2YXJpYWJsZV9zZWxlY3QkYW5vdmENCmBgYA0KDQojIDUuMCBJbnZlc3RpZ2F0aW5nIGJ1ZmZlciBkZXJpdmVkIHZhcmlhYmxlcyBhZnRlciByZW1vdmluZyBzdHJvbmdseSBjb3JyZWxhdGVkIHZhcmlhYmxlcyAoc2VjdGlvbiA0KSBhbmQgbW9kZWwgdGVzdGluZyANCg0KYGBge3J9DQpsaWJyYXJ5KHJhc3Rlcik7IGxpYnJhcnkoc2YpDQojIFNwZWNpZnkgdGhlIHBhdGggdGhhdCBjb250YWlucyB0aGUgcmFzdGVyIGRhdGEgbGF5ZXJzDQpwYXRoPC0iRjpcXFJlc2VhcmNoXFxSZXNlYXJjaF9Db29wZXJhdGlvblxcSUxSSV9Nb3NxdWl0b19NYXBwaW5nXFxNb3NxdWl0b1xcQ292ZXJfUmF0aW9cXFRlc3RfU2VsZWN0aW9uXFxUZXN0X1N0ZXB3aXNlIg0KIyBMaXN0IGFsbCBmaWxlcyB3aXRoIHRpZiBleHRlbnNpb24gZnJvbSBhYm92ZSBmb2xkZXINCnByZWRpY3RvcnM8LWxpc3QuZmlsZXMocGF0aD1wYXRoLHBhdHRlcm4gPSAidGlmJCIsZnVsbC5uYW1lcyA9IFQpDQojIFN0YWNrIGFsbCBsYXllcnMgdG9nZXRoZXIgDQpwcmVkaWN0b3JzPC1zdGFjayhwcmVkaWN0b3JzKQ0KYGBgDQoNCiMjIyA1LjEuIEV4dHJhY3QgdmFyaWFibGUgdmFsdWVzIGF0IGVhY2ggbW9zcXVpdG8gc2FtcGxpbmcgbG9jYXRpb25zDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHZhbHVlcyBmcm9tIHJhc3RlciBsYXllcnMgdXNpbmcgc2FtcGxpbmcgbG9jYXRpb25zLg0KVmFsdWVfRXh0cmFjdDwtcmFzdGVyOjpleHRyYWN0KHByZWRpY3RvcnMsTW9zX3BvaW50cyxkZj1UKQ0KDQojIFNlbGVjdCBvbmx5IHZhcmlibGVzIGZyb20gY29sdW1uIDIuIFRoZSBmaXJzdCBjb2x1bW4gaXMgSUQsIGFuZCBpdCBpcyB1c2VsZXNzDQpteV9kYXRhIDwtIFZhbHVlX0V4dHJhY3RbLCBjKDI6bmNvbChWYWx1ZV9FeHRyYWN0KSldDQojIExvb2sgYXQgZmlyc3QgZmV3IHJvd3MNCmhlYWQobXlfZGF0YSkNCmBgYA0KDQojIyMgNS4yLiBMb29raW5nIGF0IGNvcnJlbGF0aW9uIGFtb25nIHByZWRpY3RvcnMgYW5kIGl0cyBoaXN0b2dyYW1zLg0KDQpgYGB7cn0NCiMgQ29tYmluZSBjb3JlbGF0aW9uIGNvZWZmaWVpbnRzIGFuZCB0ZXN0cw0KDQpsaWJyYXJ5KGNvcnJlbGF0aW9uKQ0KbGlicmFyeShwc3ljaCkNCg0KaGVhZChjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24ocGxvdF9kYXRhLCBpbmNsdWRlX2ZhY3RvcnMgPSBUUlVFLCBtZXRob2QgPSAicGVhcnNvbiIpKQ0KIyBQbG90IGNvcnJlbGF0aW9uIG1hdHJpeCANCmxpYnJhcnkoIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikNCmNoYXJ0LkNvcnJlbGF0aW9uKG15X2RhdGEsIGhpc3RvZ3JhbT1UUlVFLCBwY2g9MTkpDQojIE1ha2luZyBoaXN0b2dyYW0NCm11bHRpLmhpc3QobXlfZGF0YSxkY29sPSJyZWQiKQ0KDQpgYGANCg0KIyMjIDUuMy4gTW9zcXVpdG8gUmlzayBwcmVkaWN0aW9uIHVzaW5nIG5lZ2F0aXZlIGJpbm9taWFsDQoNCmBgYHtyfQ0KIyBBZGQgQ291bnQgY29sdW1uIHRvIHRoZSBkYXRhc2V0DQpteV9kYXRhJENvdW50PC1Nb3NfcG9pbnRzJFN1bV90dGwNCg0KbXlfZGF0YSRDb3VudDwtYXMuaW50ZWdlcihteV9kYXRhJENvdW50KSAjIENvbnZlcnQgaXQgdG8gYmUgY291bnQgZGF0YSAoaW50ZWdlcikNCmhlYWQobXlfZGF0YSkNCmBgYA0KDQotIFNwbGl0dGluZyBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3Rpbmcgc2V0cyANCg0KYGBge3J9DQpsaWJyYXJ5KGNhVG9vbHMpDQojIFNwbGl0dGluZyBkYXRhIGludG8gdHJhaW5pbmcgYW5kIHRlc3RpbmcNCnNldC5zZWVkKDEyMykgDQpzYW1wbGUgPSBzYW1wbGUuc3BsaXQobXlfZGF0YSRDb3VudCwgU3BsaXRSYXRpbyA9IDAuOCkNCnRyYWluID0gc3Vic2V0KG15X2RhdGEsIHNhbXBsZSA9PSBUUlVFKQ0KdGVzdCAgPSBzdWJzZXQobXlfZGF0YSwgc2FtcGxlID09IEZBTFNFKQ0KYGBgDQoNCi0gTmVnYXRpdmUgYmlub21pYWwgbW9kZWwNCg0KYGBge3J9DQpsaWJyYXJ5KGNhcmV0KQ0KbmVfbW9kZWw8LWdsbShDb3VudH4uLCBkYXRhPXRyYWluLCBmYW1pbHkgPSAgbmVnYXRpdmUuYmlub21pYWwodGhldGEgPSAxKSApICMgU2V0IHRoZSB0aGV0YT0xDQpwcmVkaWN0aW9uczwtIHByZWRpY3QobmVfbW9kZWwsbmV3ZGF0YT10ZXN0KQ0KDQpkYXRhLmZyYW1lKFIyID0gUjIocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBSTVNFID0gUk1TRShwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIE1BRSA9IE1BRShwcmVkaWN0aW9ucyx0ZXN0JENvdW50KSkgDQpgYGANCg0KLSBTdGVwd2lzZSBzZWxlY3Rpb24gd2l0aCBuZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCnZhcmlhYmxlX3NlbGVjdDwtc3RlcEFJQyhuZV9tb2RlbCwgdHJhY2UgPSBGKQ0KDQp2YXJpYWJsZV9zZWxlY3QkYW5vdmENCmBgYA0KDQpgYGB7cn0NClNlbl9vdXRwdXQ8LXByZWRpY3QocHJlZGljdG9ycywgbmVfbW9kZWwsIHR5cGU9InJlc3BvbnNlIixwcm9ncmVzcz0id2luZG93IikNCg0Kc2V0d2QoIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXFByZWRpY3RlZF9NYXBzIikNCg0Kd3JpdGVSYXN0ZXIoU2VuX291dHB1dCwgZmlsZW5hbWUgPSAiUmlza19NYXBfc3RlcHdpc2UudGlmIiwgb3ZlcndyaXRlPVQpDQpgYGANCg0KIyMjIDUuNC4gTW9zcXVpdG8gUmlzayBwcmVkaWN0aW9uIHVzaW5nIHplcm8taW5mbGF0ZWQgbmVnYXRpdmUgYmlub21pYWwgDQoNCmBgYHtyfQ0KbGlicmFyeShwc2NsKQ0KemVyb19tb2RlbDwtemVyb2luZmwoQ291bnR+LiwgZGF0YT10cmFpbiwgZGlzdCA9ICJuZWdiaW4iLGxpbmsgPSAibG9naXQiKQ0KDQpwcmVkaWN0aW9uczwtIHByZWRpY3QoemVyb19tb2RlbCxuZXdkYXRhPXRlc3QpDQoNCmRhdGEuZnJhbWUoUjIgPSBSMihwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIFJNU0UgPSBSTVNFKHByZWRpY3Rpb25zLCB0ZXN0JENvdW50KSwgTUFFID0gTUFFKHByZWRpY3Rpb25zLHRlc3QkQ291bnQpKSANCmBgYA0KDQojIDYuMCBJbnZlc3RpZ2F0aW5nIGJ1ZmZlciBkZXJpdmVkIHZhcmlhYmxlcyB3aXRoIDFrbSBidWZmZXIgDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpOyBsaWJyYXJ5KHNmKQ0KIyBTcGVjaWZ5IHRoZSBwYXRoIHRoYXQgY29udGFpbnMgdGhlIHJhc3RlciBkYXRhIGxheWVycw0KcGF0aDwtIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXENvdmVyX1JhdGlvXFxTdGVwd2lzZV8xa20iDQojIExpc3QgYWxsIGZpbGVzIHdpdGggdGlmIGV4dGVuc2lvbiBmcm9tIGFib3ZlIGZvbGRlcg0KcHJlZGljdG9yczwtbGlzdC5maWxlcyhwYXRoPXBhdGgscGF0dGVybiA9ICJ0aWYkIixmdWxsLm5hbWVzID0gVCkNCiMgU3RhY2sgYWxsIGxheWVycyB0b2dldGhlciANCnByZWRpY3RvcnM8LXN0YWNrKHByZWRpY3RvcnMpDQpgYGANCg0KIyMjIDYuMS4gRXh0cmFjdCB2YXJpYWJsZSB2YWx1ZXMgYXQgZWFjaCBtb3NxdWl0byBzYW1wbGluZyBsb2NhdGlvbnMNCg0KYGBge3J9DQojIEV4dHJhY3QgdmFsdWVzIGZyb20gcmFzdGVyIGxheWVycyB1c2luZyBzYW1wbGluZyBsb2NhdGlvbnMuDQpWYWx1ZV9FeHRyYWN0PC1yYXN0ZXI6OmV4dHJhY3QocHJlZGljdG9ycyxNb3NfcG9pbnRzLGRmPVQpDQoNCiMgU2VsZWN0IG9ubHkgdmFyaWJsZXMgZnJvbSBjb2x1bW4gMi4gVGhlIGZpcnN0IGNvbHVtbiBpcyBJRCwgYW5kIGl0IGlzIHVzZWxlc3MNCm15X2RhdGEgPC0gVmFsdWVfRXh0cmFjdFssIGMoMjpuY29sKFZhbHVlX0V4dHJhY3QpKV0NCiMgTG9vayBhdCBmaXJzdCBmZXcgcm93cw0KaGVhZChteV9kYXRhKQ0KYGBgDQoNCiMjIyA2LjIuIExvb2tpbmcgYXQgY29ycmVsYXRpb24gYW1vbmcgcHJlZGljdG9ycyBhbmQgaXRzIGhpc3RvZ3JhbXMuDQoNCk5vdGU6IFJpY2UgYW5kIE5EVkkgaGFkIHN0cm9uZyBjb3JyZWxhdGlvbiA+MC43DQoNCmBgYHtyfQ0KIyBDb21iaW5lIGNvcmVsYXRpb24gY29lZmZpZWludHMgYW5kIHRlc3RzDQoNCmxpYnJhcnkoY29ycmVsYXRpb24pDQpsaWJyYXJ5KHBzeWNoKQ0KDQpoZWFkKGNvcnJlbGF0aW9uOjpjb3JyZWxhdGlvbihwbG90X2RhdGEsIGluY2x1ZGVfZmFjdG9ycyA9IFRSVUUsIG1ldGhvZCA9ICJwZWFyc29uIikpDQojIFBsb3QgY29ycmVsYXRpb24gbWF0cml4IA0KbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KY2hhcnQuQ29ycmVsYXRpb24obXlfZGF0YSwgaGlzdG9ncmFtPVRSVUUsIHBjaD0xOSkNCiMgTWFraW5nIGhpc3RvZ3JhbQ0KbXVsdGkuaGlzdChteV9kYXRhLGRjb2w9InJlZCIpDQoNCmBgYA0KDQojIyMgNi4zLiBNb3NxdWl0byBSaXNrIHByZWRpY3Rpb24gdXNpbmcgbmVnYXRpdmUgYmlub21pYWwNCg0KYGBge3J9DQojIEFkZCBDb3VudCBjb2x1bW4gdG8gdGhlIGRhdGFzZXQNCm15X2RhdGEkQ291bnQ8LU1vc19wb2ludHMkU3VtX3R0bA0KDQpteV9kYXRhJENvdW50PC1hcy5pbnRlZ2VyKG15X2RhdGEkQ291bnQpICMgQ29udmVydCBpdCB0byBiZSBjb3VudCBkYXRhIChpbnRlZ2VyKQ0KaGVhZChteV9kYXRhKQ0KYGBgDQoNCi0gU3BsaXR0aW5nIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FUb29scykNCiMgU3BsaXR0aW5nIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZw0Kc2V0LnNlZWQoMTIzKSANCnNhbXBsZSA9IHNhbXBsZS5zcGxpdChteV9kYXRhJENvdW50LCBTcGxpdFJhdGlvID0gMC44KQ0KdHJhaW4gPSBzdWJzZXQobXlfZGF0YSwgc2FtcGxlID09IFRSVUUpDQp0ZXN0ICA9IHN1YnNldChteV9kYXRhLCBzYW1wbGUgPT0gRkFMU0UpDQpgYGANCg0KLSBOZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQpuZV9tb2RlbDwtZ2xtKENvdW50fi4sIGRhdGE9dHJhaW4sIGZhbWlseSA9ICBuZWdhdGl2ZS5iaW5vbWlhbCh0aGV0YSA9IDEpICkgIyBTZXQgdGhlIHRoZXRhPTENCnByZWRpY3Rpb25zPC0gcHJlZGljdChuZV9tb2RlbCxuZXdkYXRhPXRlc3QpDQoNCmRhdGEuZnJhbWUoUjIgPSBSMihwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIFJNU0UgPSBSTVNFKHByZWRpY3Rpb25zLCB0ZXN0JENvdW50KSwgTUFFID0gTUFFKHByZWRpY3Rpb25zLHRlc3QkQ291bnQpKSANCmBgYA0KDQotIFN0ZXB3aXNlIHNlbGVjdGlvbiB3aXRoIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVsDQoNCmBgYHtyfQ0KdmFyaWFibGVfc2VsZWN0PC1zdGVwQUlDKG5lX21vZGVsLCB0cmFjZSA9IEYpDQoNCnZhcmlhYmxlX3NlbGVjdCRhbm92YQ0KYGBgDQoNCmBgYHtyfQ0KU2VuX291dHB1dDwtcHJlZGljdChwcmVkaWN0b3JzLCBuZV9tb2RlbCwgdHlwZT0icmVzcG9uc2UiLHByb2dyZXNzPSJ3aW5kb3ciKQ0KDQpzZXR3ZCgiRjpcXFJlc2VhcmNoXFxSZXNlYXJjaF9Db29wZXJhdGlvblxcSUxSSV9Nb3NxdWl0b19NYXBwaW5nXFxNb3NxdWl0b1xcUHJlZGljdGVkX01hcHMiKQ0KDQp3cml0ZVJhc3RlcihTZW5fb3V0cHV0LCBmaWxlbmFtZSA9ICJSaXNrX01hcF8xa20udGlmIixvdmVyd3JpdGU9VCkNCmBgYA0KDQoNCiMjIyA2LjQuIE1vc3F1aXRvIFJpc2sgcHJlZGljdGlvbiB1c2luZyB6ZXJvLWluZmxhdGVkIG5lZ2F0aXZlIGJpbm9taWFsIA0KDQpgYGB7cn0NCmxpYnJhcnkocHNjbCkNCnplcm9fbW9kZWw8LXplcm9pbmZsKENvdW50fi4sIGRhdGE9dHJhaW4sIGRpc3QgPSAibmVnYmluIixsaW5rID0gImxvZ2l0IikNCg0KcHJlZGljdGlvbnM8LSBwcmVkaWN0KHplcm9fbW9kZWwsbmV3ZGF0YT10ZXN0KQ0KDQpkYXRhLmZyYW1lKFIyID0gUjIocHJlZGljdGlvbnMsIHRlc3QkQ291bnQpLCBSTVNFID0gUk1TRShwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIE1BRSA9IE1BRShwcmVkaWN0aW9ucyx0ZXN0JENvdW50KSkgDQpgYGANCg0KIyA3LiBUZXN0IHdpdGggc2VsZWN0ZWQgdmFyaWFibGVzIGxpa2UgaW4gU2VjdGlvbiA2LCBidXQgYWxsIDJrbSBidWZmZXJlZC4NCg0KDQoNCmBgYHtyfQ0KbGlicmFyeShyYXN0ZXIpOyBsaWJyYXJ5KHNmKQ0KIyBTcGVjaWZ5IHRoZSBwYXRoIHRoYXQgY29udGFpbnMgdGhlIHJhc3RlciBkYXRhIGxheWVycw0KcGF0aDwtIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXENvdmVyX1JhdGlvXFwya21fYnVmZmVyIg0KIyBMaXN0IGFsbCBmaWxlcyB3aXRoIHRpZiBleHRlbnNpb24gZnJvbSBhYm92ZSBmb2xkZXINCnByZWRpY3RvcnM8LWxpc3QuZmlsZXMocGF0aD1wYXRoLHBhdHRlcm4gPSAidGlmJCIsZnVsbC5uYW1lcyA9IFQpDQojIFN0YWNrIGFsbCBsYXllcnMgdG9nZXRoZXIgDQpwcmVkaWN0b3JzPC1zdGFjayhwcmVkaWN0b3JzKQ0KYGBgDQoNCiMjIyA3LjEuIEV4dHJhY3QgdmFyaWFibGUgdmFsdWVzIGF0IGVhY2ggbW9zcXVpdG8gc2FtcGxpbmcgbG9jYXRpb25zDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHZhbHVlcyBmcm9tIHJhc3RlciBsYXllcnMgdXNpbmcgc2FtcGxpbmcgbG9jYXRpb25zLg0KVmFsdWVfRXh0cmFjdDwtcmFzdGVyOjpleHRyYWN0KHByZWRpY3RvcnMsTW9zX3BvaW50cyxkZj1UKQ0KDQojIFNlbGVjdCBvbmx5IHZhcmlibGVzIGZyb20gY29sdW1uIDIuIFRoZSBmaXJzdCBjb2x1bW4gaXMgSUQsIGFuZCBpdCBpcyB1c2VsZXNzDQpteV9kYXRhIDwtIFZhbHVlX0V4dHJhY3RbLCBjKDI6bmNvbChWYWx1ZV9FeHRyYWN0KSldDQojIExvb2sgYXQgZmlyc3QgZmV3IHJvd3MNCmhlYWQobXlfZGF0YSkNCmBgYA0KDQojIyMgNy4yLiBMb29raW5nIGF0IGNvcnJlbGF0aW9uIGFtb25nIHByZWRpY3RvcnMgYW5kIGl0cyBoaXN0b2dyYW1zLg0KDQpgYGB7cn0NCiMgQ29tYmluZSBjb3JlbGF0aW9uIGNvZWZmaWVpbnRzIGFuZCB0ZXN0cw0KDQpsaWJyYXJ5KGNvcnJlbGF0aW9uKQ0KbGlicmFyeShwc3ljaCkNCg0KaGVhZChjb3JyZWxhdGlvbjo6Y29ycmVsYXRpb24ocGxvdF9kYXRhLCBpbmNsdWRlX2ZhY3RvcnMgPSBUUlVFLCBtZXRob2QgPSAicGVhcnNvbiIpKQ0KIyBQbG90IGNvcnJlbGF0aW9uIG1hdHJpeCANCmxpYnJhcnkoIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikNCmNoYXJ0LkNvcnJlbGF0aW9uKG15X2RhdGEsIGhpc3RvZ3JhbT1UUlVFLCBwY2g9MTkpDQojIE1ha2luZyBoaXN0b2dyYW0NCm11bHRpLmhpc3QobXlfZGF0YSxkY29sPSJyZWQiKQ0KDQpgYGANCg0KDQojIyMgNy4zLiBNb3NxdWl0byBSaXNrIHByZWRpY3Rpb24gdXNpbmcgbmVnYXRpdmUgYmlub21pYWwNCg0KYGBge3J9DQojIEFkZCBDb3VudCBjb2x1bW4gdG8gdGhlIGRhdGFzZXQNCm15X2RhdGEkQ291bnQ8LU1vc19wb2ludHMkU3VtX3R0bA0KDQpteV9kYXRhJENvdW50PC1hcy5pbnRlZ2VyKG15X2RhdGEkQ291bnQpICMgQ29udmVydCBpdCB0byBiZSBjb3VudCBkYXRhIChpbnRlZ2VyKQ0KaGVhZChteV9kYXRhKQ0KYGBgDQoNCi0gU3BsaXR0aW5nIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBzZXRzIA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FUb29scykNCiMgU3BsaXR0aW5nIGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZw0Kc2V0LnNlZWQoMTIzKSANCnNhbXBsZSA9IHNhbXBsZS5zcGxpdChteV9kYXRhJENvdW50LCBTcGxpdFJhdGlvID0gMC44KQ0KdHJhaW4gPSBzdWJzZXQobXlfZGF0YSwgc2FtcGxlID09IFRSVUUpDQp0ZXN0ICA9IHN1YnNldChteV9kYXRhLCBzYW1wbGUgPT0gRkFMU0UpDQpgYGANCg0KLSBOZWdhdGl2ZSBiaW5vbWlhbCBtb2RlbA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyZXQpDQpuZV9tb2RlbDwtZ2xtKENvdW50fi4sIGRhdGE9dHJhaW4sIGZhbWlseSA9ICBuZWdhdGl2ZS5iaW5vbWlhbCh0aGV0YSA9IDEpICkgIyBTZXQgdGhlIHRoZXRhPTENCnByZWRpY3Rpb25zPC0gcHJlZGljdChuZV9tb2RlbCxuZXdkYXRhPXRlc3QpDQoNCmRhdGEuZnJhbWUoUjIgPSBSMihwcmVkaWN0aW9ucywgdGVzdCRDb3VudCksIFJNU0UgPSBSTVNFKHByZWRpY3Rpb25zLCB0ZXN0JENvdW50KSwgTUFFID0gTUFFKHByZWRpY3Rpb25zLHRlc3QkQ291bnQpKSANCmBgYA0KDQotIFN0ZXB3aXNlIHNlbGVjdGlvbiB3aXRoIG5lZ2F0aXZlIGJpbm9taWFsIG1vZGVsDQoNCmBgYHtyfQ0KdmFyaWFibGVfc2VsZWN0PC1zdGVwQUlDKG5lX21vZGVsLCB0cmFjZSA9IEYpDQoNCnZhcmlhYmxlX3NlbGVjdCRhbm92YQ0KYGBgDQoNCmBgYHtyfQ0KU2VuX291dHB1dDwtcHJlZGljdChwcmVkaWN0b3JzLCBuZV9tb2RlbCwgdHlwZT0icmVzcG9uc2UiLHByb2dyZXNzPSJ3aW5kb3ciKQ0KDQpzZXR3ZCgiRjpcXFJlc2VhcmNoXFxSZXNlYXJjaF9Db29wZXJhdGlvblxcSUxSSV9Nb3NxdWl0b19NYXBwaW5nXFxNb3NxdWl0b1xcUHJlZGljdGVkX01hcHMiKQ0KDQp3cml0ZVJhc3RlcihTZW5fb3V0cHV0LCBmaWxlbmFtZSA9ICJSaXNrX01hcF83LnRpZiIsb3ZlcndyaXRlPVQpDQpgYGA=