This R code is dedicated to a training workshop “Mosquito Mapping” at ILRI in Hanoi. Before we start our workshop, you need to download data here.

1. Basic syntax in R

Data types in R (R-objects)

Vectors

  • What information or data types can vector hold? logical, numeric, integer, character or string
# Create a vector from 1 to 10
myvector<-c(1:10)

myvector # Display the myvector
 [1]  1  2  3  4  5  6  7  8  9 10

Factors

  • Factors are created using the factor() function.
edu_class<-c("Good","Very good","Average","Weak","Good","Good","Very good","Average","Weak","Good","Good","Very good")

edu_class<-as.factor(edu_class)

edu_class
 [1] Good      Very good Average   Weak      Good      Good      Very good Average   Weak      Good      Good      Very good
Levels: Average Good Very good Weak
nlevels(edu_class)# Check the number of factor levels
[1] 4

Data frames

BMI <-  data.frame(
   gender = c("Male", "Male","Female"), 
   height = c(165, 171.5, 167), 
   weight = c(45,67, 86),
   Age = c(34,38,45)
)
BMI

2. Operators

Math operators

+, -, /, *, ^

num1<-1
num2<-4

num1*num2
[1] 4
print(paste("num1+num2=",num1+num2))
[1] "num1+num2= 5"
num1<-c(1:5)
num2<-c(5:9)

num1+num2
[1]  6  8 10 12 14

Relational operators

>,<,>=,<=,==, !=

v <- c(2,5.5,6,9)
t <- c(8,2.5,14,9)

print(v>t)
[1] FALSE  TRUE FALSE FALSE
v <- c(2,5.5,6,9)
t <- c(8,2.5,14,9)
print(v!=t)
[1]  TRUE  TRUE  TRUE FALSE

Logical operators

&, |, !

v <- c(3,1,TRUE,2+3i)
t <- c(4,1,FALSE,2+3i)
print(v==3 & t==4)
[1]  TRUE FALSE FALSE FALSE
t <- c(4,TRUE,0,1,FALSE,2+3i)
print(t!=3)
[1] TRUE TRUE TRUE TRUE TRUE TRUE
  1. Loop and if statement

For Loop statement

for (i in 1:10){
  print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10

If else statement

for (i in 1:10){
  if (i>5){
    print(i)
  } else{
    print("Values<=5")
  }
}
[1] "Values<=5"
[1] "Values<=5"
[1] "Values<=5"
[1] "Values<=5"
[1] "Values<=5"
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10

Functions


mymean<-function(n){
  mean_value<-sum(n)/length(n)
  return(mean_value)
}

mymean(c(20:80))
[1] 50
# Write a function to calculate mean from start
mymean<-function(n){
  mysum<-0
  mylength<-0
  for (i in n){
    mysum=mysum+i
    mylength=mylength+1
  }
  mean_value<-mysum/mylength
  return(mean_value)
}

mymean(1:10)
[1] 5.5

Working with dataframe

  • Read a csv file (excel)
df<-read.csv("https://raw.githubusercontent.com/tuyenhavan/Mosquito_ILRI/main/Dataset_temp.csv", header=T)

head(df)
  • Select columns
library(dplyr)
# Get first five columns from df dataframe
col5<-df %>% dplyr::select(1:5) #Using select function from dplyr package
# Get 4 columns from df
col4<- df[,c(1:4)]

col4<-df[,c(1,2,3,4)] # The same as above
# Select two columns from df

col2<-df[1:10,c("Forest","Count")]

head(col2)
library(tidyr)
df%>% colnames() # See column names
[1] "Count"           "Locations"       "Forest"          "Lag_temp_center" "Lag_temp_power"  "NDVI"            "Pop"             "Rice"           
[9] "Water"          
df%>% summarize_all(class) %>% pivot_longer(everything(),names_to = "Variables",values_to = "Data Types") # See data types of each column
# Another way
str(df)
'data.frame':   469 obs. of  9 variables:
 $ Count          : int  32 4 43 0 11 0 0 49 5 17 ...
 $ Locations      : chr  "Dan Phuong" "Dan Phuong" "Dan Phuong" "Dan Phuong" ...
 $ Forest         : num  0.0859 0.1211 0.1133 0.1484 0.1016 ...
 $ Lag_temp_center: num  -4.65 -4.09 -4.18 -3.59 -2.82 ...
 $ Lag_temp_power : num  21.65 16.75 17.51 12.89 7.95 ...
 $ NDVI           : num  0.317 0.338 0.33 0.355 0.339 ...
 $ Pop            : num  8.24 8.83 8.91 11.31 12.16 ...
 $ Rice           : num  0.355 0.512 0.465 0.684 0.574 ...
 $ Water          : num  0 0 0 0 0 0 0 0 0 0 ...

# Some other functions from tidyverse.

library(stringr)
df%>% filter(str_detect(`Locations`, "^D")) %>% head()# Get only data that starting with D in Locations column

df %>% select(contains("Co"),contains("Po")) %>% head()# Select all columns that contain character Co and Po

df %>% mutate(Center=Lag_temp_power-mean(Lag_temp_power)) %>% head() # Create a new variable by lag tem - mean of lag temp
NA
# Create a new column with two classes (Rice and Non-rice) from Rice column

df %>% mutate(Rice_class=case_when(Rice>0.5~"Rice", TRUE~"Non-rice")) %>% head()

Briefly, you have now understand about R and somewhat its working principles. There is no way to be an expert to read a single book, so additional reading materials are necessary. I have listed here some resources: R for data science by Hadley Wickham, Basic R program by Tutorialpoint, and Data wrangling, exploration, and analysis with R by University of British Columbia.

I. Working spatial data in R

If you have not installed the following packages, please do so.

#install.packages(c("RColorBrewer", "sp", "rgeos", "tmaptools", "sf", "downloader", "rgdal","geojsonio","raster","ggplot2"))

1. Load single raster layer

  • Read a single raster data
library(raster)
# Define the path that stores our data
path<-"F:\\Training_Material\\Second_Training\\Sub_study"
# Read NDVI raster in R using raster function from raster package
NDVI<-raster(paste(path,"\\","NDVI.tif", sep=""))
# Plot the NDVI
plot(NDVI)

  • Read multiple raster bands
library(fs)
#dir_info(path) # Print out all files in the folder
# List all files with an extension .tif
files<-list.files(path=path,full.names =T, pattern="tif$")
# Stack bands together 
layer<-stack(files)
# Plot all bands
plot(layer)

  • Preprojection and clipping raster
# Reproject raster layer
library(dplyr)

NDVI_UTM<- NDVI %>% projectRaster(., crs = "+proj=utm +zone=48 +datum=WGS84 +units=m +no_defs")

NDVI_UTM
class      : RasterLayer 
dimensions : 418, 444, 185592  (nrow, ncol, ncell)
resolution : 28, 29.8  (x, y)
extent     : 564562, 576994, 2329417, 2341873  (xmin, xmax, ymin, ymax)
crs        : +proj=utm +zone=48 +datum=WGS84 +units=m +no_defs +ellps=WGS84 +towgs84=0,0,0 
source     : memory
names      : NDVI 
values     : -0.1422169, 0.5922366  (min, max)

2. Read shapefile

  • Load our mosquito sampling locations
# Read mosquito sampling locations
library(sf)
shape<-st_read(paste(path,"\\","Mosquito.shp",sep=""))
Reading layer `Mosquito' from data source `F:\Training_Material\Second_Training\Sub_study\Mosquito.shp' using driver `ESRI Shapefile'
Simple feature collection with 87 features and 5 fields
geometry type:  MULTIPOINT
dimension:      XY
bbox:           xmin: 105.6319 ymin: 21.0775 xmax: 105.7217 ymax: 21.14083
geographic CRS: WGS 84
shape%>% st_geometry()%>% plot(shape)
Error in plot.sfc_MULTIPOINT(., shape) : missing(y) is not TRUE

Read spatial data from online sources

  • There are many online geospatial data sources which are available. For vector data (administrative boundary country), we can download from global administrative boundary website here. For raster data (climatic and elevation), we can download from World Clim here
Get Vietnam boundary map (level 1)
#library(raster)
VN<-getData(name="GADM",country="Vietnam",level=1) # Levels 1 VN, Level 2 province, Level 3 means communes

VN_sf<-st_as_sf(VN) # Convert sp class to sf class
plot(VN, main="Vietnam Map") # Plot Vietnam Map

plot(VN_sf) # Plot all attribute columns 

  • If you are interested in only Hanoi, you can select Hanoi boundary
HN <- VN_sf %>% filter(VARNAME_1=="Ha Noi")

HN %>% st_geometry() %>% plot(col=3, main="Hanoi Map")

Get Vietnam district map (level 2) and clip Hanoi district
VN2<-getData(name="GADM",country="Vietnam",level=2) # Levels 1 VN, Level 2 province, Level 3 means communes

VN2sf<-st_as_sf(VN2) %>% st_transform(st_crs(HN)) # Convert sp class to sf class and convert crs. Only demonstration
# Clip to Hanoi boundary 

HN_dist<- VN2sf %>% filter(st_contains(HN,., sparse = F))
although coordinates are longitude/latitude, st_contains assumes that they are planar
HN_dist %>% st_geometry() %>% plot(main="Hanoi District",col=rainbow(30))

Check and convert crs for shapefile file
# Default crs of data in GAMD is geographic coordinate 4326
st_crs(HN_dist)$proj4string
[1] "+proj=longlat +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +no_defs"
# Convert longlat to UTM

HN_UTM<-HN_dist %>% st_transform(., crs=32648 ) # 32648 is EPSG code for Zone 48 North cover Vietnam

st_crs(HN_UTM)$proj4string
[1] "+proj=utm +zone=48 +datum=WGS84 +units=m +no_defs"
  • Cliping the study area of interest
# Get Rainfall data
Rainfall<-getData(name = "worldclim",var="prec",res=2.5,lon=102,lat=22)

# Clipping Vietnam precipitation
crop_VN<-crop(Rainfall,extent(VN)) # Crop the raster to the extent of the shapefile

VN_rain<-mask(crop_VN, VN) # Mask out or clip

plot(VN_rain)

3. Mosquito Mapping

# Extract values from raster stack layers
library(sf)
points<-shapefile("E:\\Python_Tutorials\\HuSuk_Data\\Step1\\Step2\\Mos_Point.shp")

Value<-raster::extract(layer,points,df=T)

Value<-Value[,c(2:ncol(Value))]
library("PerformanceAnalytics")
chart.Correlation(Value, histogram=TRUE, pch=19)


mlayer<-layer[[c(1,2,3,5,6,7,9)]]

Value<-raster::extract(mlayer,points,df=T)

Value<-Value[,c(2:ncol(Value))]
library("PerformanceAnalytics")
chart.Correlation(Value, histogram=TRUE, pch=19)

Final<-cbind(Count=points$Counts,Value)
# 
Final$Count<-as.integer(Final$Count)

head(Final)
library(caTools)
library(MASS)
library(caret)
set.seed(123)
sampling = sample.split(Final$Count, SplitRatio = 0.8)
train = subset(Final, sampling == TRUE)
test  = subset(Final, sampling == FALSE)

NBR<-glm.nb(Count~August_Rain + Pop + Rice + August_Temp+  Forest+  Water, data=train)
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 convergealternation limit reached
summary(NBR)

Call:
glm.nb(formula = Count ~ August_Rain + Pop + Rice + August_Temp + 
    Forest + Water, data = train, init.theta = 0.3827343932, 
    link = log)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.9007  -1.0622  -0.7856  -0.1473   3.1336  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)  
(Intercept) -18.12144   10.87364  -1.667   0.0956 .
August_Rain   0.03937    0.01681   2.342   0.0192 *
Pop          -0.01286    0.01291  -0.996   0.3191  
Rice         -1.37881    1.26945  -1.086   0.2774  
August_Temp   0.05854    0.17728   0.330   0.7413  
Forest        4.44428    4.88246   0.910   0.3627  
Water        -4.80263    5.78660  -0.830   0.4066  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

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

    Null deviance: 95.651  on 72  degrees of freedom
Residual deviance: 86.264  on 66  degrees of freedom
AIC: 558.13

Number of Fisher Scoring iterations: 25

              Theta:  0.3827 
          Std. Err.:  0.0597 
Warning while fitting theta: alternation limit reached 

 2 x log-likelihood:  -542.1320 
#model_NB1<-glm(Count~., data=train_NB1, family = negative.binomial(theta = 0.34))
pre_NB1<- predict(NBR,newdata=test, type = "response") # Predict for new data
accuracy_NB1<-data.frame(R2 = R2(pre_NB1, test$Count), RMSE = RMSE(pre_NB1, test$Count), MAE = MAE(pre_NB1,test$Count))
accuracy_NB1
#Sosanh<-data.frame(cbind(Observed=test_NB1$Count,Predicted=pre_NB1))
#setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Predicted_Maps\\Final_Map")
out_temp<-predict(layer, NBR, type="response",progress="window")
Loading required namespace: tcltk
# Making a copy
tmap<-out_temp
# Remove outside box values
tmap[tmap==tmap[1]]<-NA
no non-missing arguments to min; returning Infno non-missing arguments to max; returning -Inf
tmap[tmap>400]<-400
library(sp)
library(fields)
library(RColorBrewer)
#display.brewer.all()
#setwd("F:\\Research\\Research_Cooperation\\ILRI_Mosquito_Mapping\\Mosquito\\Predicted_Maps\\Final_Map")
#writeRaster(tmap, filename = "Risk_map_QGIS.tif", overwrite=T)
North2 <- list("SpatialPolygonsRescale", layout.north.arrow(type=2), 
              offset = c(105.3, 20.8), scale = 1000, fill="gray", which = 1)
clrs.spec <- colorRampPalette(rev(brewer.pal(11, "Spectral")))
map1<-spplot(tmap, zcol = "layer", col.regions = clrs.spec(180), #tim.colors()
             cuts = 180, colorkey = list(space = "right"),
             sp.layout=list(North2))
map1
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIFIgY29kZSBpcyBkZWRpY2F0ZWQgdG8gYSB0cmFpbmluZyB3b3Jrc2hvcCAiTW9zcXVpdG8gTWFwcGluZyIgYXQgSUxSSSBpbiBIYW5vaS4gQmVmb3JlIHdlIHN0YXJ0IG91ciB3b3Jrc2hvcCwgeW91IG5lZWQgdG8gZG93bmxvYWQgZGF0YSBbaGVyZV0oaHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2RyaXZlL2ZvbGRlcnMvMWdSbW1qUHRHcDFhLXF4Ql9jWGdBVG1kbHB1WDZNemUwP3VzcD1zaGFyaW5nKS4gDQoNCiMgMS4gQmFzaWMgc3ludGF4IGluIFINCg0KIyMgRGF0YSB0eXBlcyBpbiBSIChSLW9iamVjdHMpDQoNCiMjIyBWZWN0b3JzDQoNCi0gV2hhdCBpbmZvcm1hdGlvbiBvciBkYXRhIHR5cGVzIGNhbiB2ZWN0b3IgaG9sZD8gYGxvZ2ljYWxgLCBgbnVtZXJpY2AsIGBpbnRlZ2VyYCwgYGNoYXJhY3RlciBvciBzdHJpbmdgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSB2ZWN0b3IgZnJvbSAxIHRvIDEwDQpteXZlY3RvcjwtYygxOjEwKQ0KDQpteXZlY3RvciAjIERpc3BsYXkgdGhlIG15dmVjdG9yDQoNCmBgYA0KDQojIyMgRmFjdG9ycyANCg0KLSBGYWN0b3JzIGFyZSBjcmVhdGVkIHVzaW5nIHRoZSBmYWN0b3IoKSBmdW5jdGlvbi4gDQoNCmBgYHtyfQ0KZWR1X2NsYXNzPC1jKCJHb29kIiwiVmVyeSBnb29kIiwiQXZlcmFnZSIsIldlYWsiLCJHb29kIiwiR29vZCIsIlZlcnkgZ29vZCIsIkF2ZXJhZ2UiLCJXZWFrIiwiR29vZCIsIkdvb2QiLCJWZXJ5IGdvb2QiKQ0KDQplZHVfY2xhc3M8LWFzLmZhY3RvcihlZHVfY2xhc3MpDQoNCmVkdV9jbGFzcw0KbmxldmVscyhlZHVfY2xhc3MpIyBDaGVjayB0aGUgbnVtYmVyIG9mIGZhY3RvciBsZXZlbHMNCg0KYGBgDQoNCiMgRGF0YSBmcmFtZXMNCg0KLSBEYXRhIGZyYW1lIGlzIGEgcmVjdGFuZ3VsYXIgZGF0YWZyYW1lIGFuZCBjb25zaXN0cyBvZiBvbmUgb3IgbW9yZSB2ZWN0b3JzLiBJdCBpcyBsaWtlIGFuIGV4Y2VsIGRhdGEgZmlsZQ0KDQpgYGB7cn0NCkJNSSA8LSAJZGF0YS5mcmFtZSgNCiAgIGdlbmRlciA9IGMoIk1hbGUiLCAiTWFsZSIsIkZlbWFsZSIpLCANCiAgIGhlaWdodCA9IGMoMTY1LCAxNzEuNSwgMTY3KSwgDQogICB3ZWlnaHQgPSBjKDQ1LDY3LCA4NiksDQogICBBZ2UgPSBjKDM0LDM4LDQ1KQ0KKQ0KQk1JDQpgYGANCg0KIyAyLiBPcGVyYXRvcnMNCg0KIyMjIE1hdGggb3BlcmF0b3JzDQoNCmArLCAtLCAvLCAqLCBeYA0KDQpgYGB7cn0NCm51bTE8LTENCm51bTI8LTQNCg0KbnVtMSpudW0yDQoNCnByaW50KHBhc3RlKCJudW0xK251bTI9IixudW0xK251bTIpKQ0KDQpudW0xPC1jKDE6NSkNCm51bTI8LWMoNTo5KQ0KDQpudW0xK251bTINCg0KYGBgDQoNCiMjIyBSZWxhdGlvbmFsIG9wZXJhdG9ycw0KDQpgPiw8LD49LDw9LD09LCAhPWANCg0KYGBge3J9DQp2IDwtIGMoMiw1LjUsNiw5KQ0KdCA8LSBjKDgsMi41LDE0LDkpDQoNCnByaW50KHY+dCkNCmBgYA0KDQpgYGB7cn0NCnYgPC0gYygyLDUuNSw2LDkpDQp0IDwtIGMoOCwyLjUsMTQsOSkNCnByaW50KHYhPXQpDQpgYGANCg0KIyMjIExvZ2ljYWwgb3BlcmF0b3JzDQoNCmAmLCB8LCAhYA0KDQpgYGB7cn0NCnYgPC0gYygzLDEsVFJVRSwyKzNpKQ0KdCA8LSBjKDQsMSxGQUxTRSwyKzNpKQ0KcHJpbnQodj09MyAmIHQ9PTQpDQpgYGANCg0KYGBge3J9DQp0IDwtIGMoNCxUUlVFLDAsMSxGQUxTRSwyKzNpKQ0KcHJpbnQodCE9MykNCmBgYA0KDQozLiBMb29wIGFuZCBpZiBzdGF0ZW1lbnQgDQoNCiMjIyBGb3IgTG9vcCBzdGF0ZW1lbnQgDQoNCmBgYHtyfQ0KZm9yIChpIGluIDE6MTApew0KICBwcmludChpKQ0KfQ0KDQpgYGANCg0KIyMjIElmIGVsc2Ugc3RhdGVtZW50IA0KDQpgYGB7cn0NCmZvciAoaSBpbiAxOjEwKXsNCiAgaWYgKGk+NSl7DQogICAgcHJpbnQoaSkNCiAgfSBlbHNlew0KICAgIHByaW50KCJWYWx1ZXM8PTUiKQ0KICB9DQp9DQpgYGANCg0KIyMjIEZ1bmN0aW9ucyANCg0KYGBge3J9DQoNCm15bWVhbjwtZnVuY3Rpb24obil7DQogIG1lYW5fdmFsdWU8LXN1bShuKS9sZW5ndGgobikNCiAgcmV0dXJuKG1lYW5fdmFsdWUpDQp9DQoNCm15bWVhbihjKDIwOjgwKSkNCmBgYA0KDQpgYGB7cn0NCiMgV3JpdGUgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgbWVhbiBmcm9tIHN0YXJ0DQpteW1lYW48LWZ1bmN0aW9uKG4pew0KICBteXN1bTwtMA0KICBteWxlbmd0aDwtMA0KICBmb3IgKGkgaW4gbil7DQogICAgbXlzdW09bXlzdW0raQ0KICAgIG15bGVuZ3RoPW15bGVuZ3RoKzENCiAgfQ0KICBtZWFuX3ZhbHVlPC1teXN1bS9teWxlbmd0aA0KICByZXR1cm4obWVhbl92YWx1ZSkNCn0NCg0KbXltZWFuKDE6MTApDQoNCmBgYA0KDQojIyMgV29ya2luZyB3aXRoIGRhdGFmcmFtZQ0KLSBSZWFkIGEgY3N2IGZpbGUgKGV4Y2VsKQ0KYGBge3J9DQpkZjwtcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS90dXllbmhhdmFuL01vc3F1aXRvX0lMUkkvbWFpbi9EYXRhc2V0X3RlbXAuY3N2IiwgaGVhZGVyPVQpDQoNCmhlYWQoZGYpDQpgYGANCg0KIC0gU2VsZWN0IGNvbHVtbnMNCiANCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCiMgR2V0IGZpcnN0IGZpdmUgY29sdW1ucyBmcm9tIGRmIGRhdGFmcmFtZQ0KY29sNTwtZGYgJT4lIGRwbHlyOjpzZWxlY3QoMTo1KSAjVXNpbmcgc2VsZWN0IGZ1bmN0aW9uIGZyb20gZHBseXIgcGFja2FnZQ0KIyBHZXQgNCBjb2x1bW5zIGZyb20gZGYNCmNvbDQ8LSBkZlssYygxOjQpXQ0KDQpjb2w0PC1kZlssYygxLDIsMyw0KV0gIyBUaGUgc2FtZSBhcyBhYm92ZQ0KIyBTZWxlY3QgdHdvIGNvbHVtbnMgZnJvbSBkZg0KDQpjb2wyPC1kZlsxOjEwLGMoIkZvcmVzdCIsIkNvdW50IildDQoNCmhlYWQoY29sMikNCmBgYA0KIA0KYGBge3J9DQpsaWJyYXJ5KHRpZHlyKQ0KZGYlPiUgY29sbmFtZXMoKSAjIFNlZSBjb2x1bW4gbmFtZXMNCg0KZGYlPiUgc3VtbWFyaXplX2FsbChjbGFzcykgJT4lIHBpdm90X2xvbmdlcihldmVyeXRoaW5nKCksbmFtZXNfdG8gPSAiVmFyaWFibGVzIix2YWx1ZXNfdG8gPSAiRGF0YSBUeXBlcyIpICMgU2VlIGRhdGEgdHlwZXMgb2YgZWFjaCBjb2x1bW4NCiMgQW5vdGhlciB3YXkNCnN0cihkZikNCg0KYGBgDQogDQogIyBTb21lIG90aGVyIGZ1bmN0aW9ucyBmcm9tIHRpZHl2ZXJzZS4gDQogDQpgYGB7cn0NCmxpYnJhcnkoc3RyaW5ncikNCmRmJT4lIGZpbHRlcihzdHJfZGV0ZWN0KGBMb2NhdGlvbnNgLCAiXkQiKSkgJT4lIGhlYWQoKSMgR2V0IG9ubHkgZGF0YSB0aGF0IHN0YXJ0aW5nIHdpdGggRCBpbiBMb2NhdGlvbnMgY29sdW1uDQoNCmRmICU+JSBzZWxlY3QoY29udGFpbnMoIkNvIiksY29udGFpbnMoIlBvIikpICU+JSBoZWFkKCkjIFNlbGVjdCBhbGwgY29sdW1ucyB0aGF0IGNvbnRhaW4gY2hhcmFjdGVyIENvIGFuZCBQbw0KDQpkZiAlPiUgbXV0YXRlKENlbnRlcj1MYWdfdGVtcF9wb3dlci1tZWFuKExhZ190ZW1wX3Bvd2VyKSkgJT4lIGhlYWQoKSAjIENyZWF0ZSBhIG5ldyB2YXJpYWJsZSBieSBsYWcgdGVtIC0gbWVhbiBvZiBsYWcgdGVtcA0KDQpgYGANCiANCmBgYHtyfQ0KIyBDcmVhdGUgYSBuZXcgY29sdW1uIHdpdGggdHdvIGNsYXNzZXMgKFJpY2UgYW5kIE5vbi1yaWNlKSBmcm9tIFJpY2UgY29sdW1uDQoNCmRmICU+JSBtdXRhdGUoUmljZV9jbGFzcz1jYXNlX3doZW4oUmljZT4wLjV+IlJpY2UiLCBUUlVFfiJOb24tcmljZSIpKSAlPiUgaGVhZCgpDQpgYGANCiANCkJyaWVmbHksIHlvdSBoYXZlIG5vdyB1bmRlcnN0YW5kIGFib3V0IFIgYW5kIHNvbWV3aGF0IGl0cyB3b3JraW5nIHByaW5jaXBsZXMuIFRoZXJlIGlzIG5vIHdheSB0byBiZSBhbiBleHBlcnQgdG8gcmVhZCBhIHNpbmdsZSBib29rLCBzbyBhZGRpdGlvbmFsIHJlYWRpbmcgbWF0ZXJpYWxzIGFyZSBuZWNlc3NhcnkuIEkgaGF2ZSBsaXN0ZWQgaGVyZSBzb21lIHJlc291cmNlczogW1IgZm9yIGRhdGEgc2NpZW5jZSBieSBIYWRsZXkgV2lja2hhbV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9kYXRhLXZpc3VhbGlzYXRpb24uaHRtbCksIFtCYXNpYyBSIHByb2dyYW0gYnkgVHV0b3JpYWxwb2ludF0oaHR0cHM6Ly93d3cudHV0b3JpYWxzcG9pbnQuY29tL3Ivcl9zdHJpbmdzLmh0bSksIGFuZCBbRGF0YSB3cmFuZ2xpbmcsIGV4cGxvcmF0aW9uLCBhbmQgYW5hbHlzaXMgd2l0aCBSIGJ5IFVuaXZlcnNpdHkgb2YgQnJpdGlzaCBDb2x1bWJpYV0oaHR0cHM6Ly9zdGF0NTQ1LmNvbS9pbmRleC5odG1sI290aGVyLWNvbnRyaWJ1dG9ycykuDQoNCiMgSS4gV29ya2luZyBzcGF0aWFsIGRhdGEgaW4gUg0KDQpJZiB5b3UgaGF2ZSBub3QgaW5zdGFsbGVkIHRoZSBmb2xsb3dpbmcgcGFja2FnZXMsIHBsZWFzZSBkbyBzby4NCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoYygiUkNvbG9yQnJld2VyIiwgInNwIiwgInJnZW9zIiwgInRtYXB0b29scyIsICJzZiIsICJkb3dubG9hZGVyIiwgInJnZGFsIiwiZ2VvanNvbmlvIiwicmFzdGVyIiwiZ2dwbG90MiIpKQ0KYGBgDQoNCiMjIyAxLiBMb2FkIHNpbmdsZSByYXN0ZXIgbGF5ZXINCi0gUmVhZCBhIHNpbmdsZSByYXN0ZXIgZGF0YSANCg0KYGBge3J9DQpsaWJyYXJ5KHJhc3RlcikNCiMgRGVmaW5lIHRoZSBwYXRoIHRoYXQgc3RvcmVzIG91ciBkYXRhDQpwYXRoPC0iRjpcXFRyYWluaW5nX01hdGVyaWFsXFxTZWNvbmRfVHJhaW5pbmdcXFN1Yl9zdHVkeSINCiMgUmVhZCBORFZJIHJhc3RlciBpbiBSIHVzaW5nIHJhc3RlciBmdW5jdGlvbiBmcm9tIHJhc3RlciBwYWNrYWdlDQpORFZJPC1yYXN0ZXIocGFzdGUocGF0aCwiXFwiLCJORFZJLnRpZiIsIHNlcD0iIikpDQojIFBsb3QgdGhlIE5EVkkNCnBsb3QoTkRWSSkNCmBgYA0KDQotIFJlYWQgbXVsdGlwbGUgcmFzdGVyIGJhbmRzDQoNCmBgYHtyfQ0KbGlicmFyeShmcykNCiNkaXJfaW5mbyhwYXRoKSAjIFByaW50IG91dCBhbGwgZmlsZXMgaW4gdGhlIGZvbGRlcg0KIyBMaXN0IGFsbCBmaWxlcyB3aXRoIGFuIGV4dGVuc2lvbiAudGlmDQpmaWxlczwtbGlzdC5maWxlcyhwYXRoPXBhdGgsZnVsbC5uYW1lcyA9VCwgcGF0dGVybj0idGlmJCIpDQojIFN0YWNrIGJhbmRzIHRvZ2V0aGVyIA0KbGF5ZXI8LXN0YWNrKGZpbGVzKQ0KIyBQbG90IGFsbCBiYW5kcw0KcGxvdChsYXllcikNCmBgYA0KDQotIFByZXByb2plY3Rpb24gYW5kIGNsaXBwaW5nIHJhc3Rlcg0KDQpgYGB7cn0NCiMgUmVwcm9qZWN0IHJhc3RlciBsYXllcg0KbGlicmFyeShkcGx5cikNCg0KTkRWSV9VVE08LSBORFZJICU+JSBwcm9qZWN0UmFzdGVyKC4sIGNycyA9ICIrcHJvaj11dG0gK3pvbmU9NDggK2RhdHVtPVdHUzg0ICt1bml0cz1tICtub19kZWZzIikNCg0KTkRWSV9VVE0NCmBgYA0KDQoNCiMjIDIuIFJlYWQgc2hhcGVmaWxlIA0KLSBMb2FkIG91ciBtb3NxdWl0byBzYW1wbGluZyBsb2NhdGlvbnMNCg0KYGBge3J9DQojIFJlYWQgbW9zcXVpdG8gc2FtcGxpbmcgbG9jYXRpb25zDQpsaWJyYXJ5KHNmKQ0Kc2hhcGU8LXN0X3JlYWQocGFzdGUocGF0aCwiXFwiLCJNb3NxdWl0by5zaHAiLHNlcD0iIikpDQpzaGFwZSU+JSBzdF9nZW9tZXRyeSgpJT4lIHBsb3Qoc2hhcGUpDQpgYGANCg0KIyMgUmVhZCBzcGF0aWFsIGRhdGEgZnJvbSBvbmxpbmUgc291cmNlcyANCg0KLSBUaGVyZSBhcmUgbWFueSBvbmxpbmUgZ2Vvc3BhdGlhbCBkYXRhIHNvdXJjZXMgd2hpY2ggYXJlIGF2YWlsYWJsZS4gRm9yIHZlY3RvciBkYXRhIChhZG1pbmlzdHJhdGl2ZSBib3VuZGFyeSBjb3VudHJ5KSwgd2UgY2FuIGRvd25sb2FkIGZyb20gZ2xvYmFsIGFkbWluaXN0cmF0aXZlIGJvdW5kYXJ5IHdlYnNpdGUgW2hlcmVdKGh0dHBzOi8vZ2FkbS5vcmcvKS4gRm9yIHJhc3RlciBkYXRhIChjbGltYXRpYyBhbmQgZWxldmF0aW9uKSwgd2UgY2FuIGRvd25sb2FkIGZyb20gV29ybGQgQ2xpbSBbaGVyZV0oaHR0cHM6Ly93d3cud29ybGRjbGltLm9yZy9kYXRhL3dvcmxkY2xpbTIxLmh0bWwpDQoNCiMjIyMjIEdldCBWaWV0bmFtIGJvdW5kYXJ5IG1hcCAobGV2ZWwgMSkNCg0KYGBge3J9DQojbGlicmFyeShyYXN0ZXIpDQpWTjwtZ2V0RGF0YShuYW1lPSJHQURNIixjb3VudHJ5PSJWaWV0bmFtIixsZXZlbD0xKSAjIExldmVscyAxIFZOLCBMZXZlbCAyIHByb3ZpbmNlLCBMZXZlbCAzIG1lYW5zIGNvbW11bmVzDQoNClZOX3NmPC1zdF9hc19zZihWTikgIyBDb252ZXJ0IHNwIGNsYXNzIHRvIHNmIGNsYXNzDQpwbG90KFZOLCBtYWluPSJWaWV0bmFtIE1hcCIpICMgUGxvdCBWaWV0bmFtIE1hcA0KcGxvdChWTl9zZikgIyBQbG90IGFsbCBhdHRyaWJ1dGUgY29sdW1ucyANCg0KYGBgDQoNCi0gSWYgeW91IGFyZSBpbnRlcmVzdGVkIGluIG9ubHkgSGFub2ksIHlvdSBjYW4gc2VsZWN0IEhhbm9pIGJvdW5kYXJ5DQoNCmBgYHtyfQ0KSE4gPC0gVk5fc2YgJT4lIGZpbHRlcihWQVJOQU1FXzE9PSJIYSBOb2kiKQ0KDQpITiAlPiUgc3RfZ2VvbWV0cnkoKSAlPiUgcGxvdChjb2w9MywgbWFpbj0iSGFub2kgTWFwIikNCg0KYGBgDQojIyMjIyBHZXQgVmlldG5hbSBkaXN0cmljdCBtYXAgKGxldmVsIDIpIGFuZCBjbGlwIEhhbm9pIGRpc3RyaWN0DQoNCmBgYHtyfQ0KVk4yPC1nZXREYXRhKG5hbWU9IkdBRE0iLGNvdW50cnk9IlZpZXRuYW0iLGxldmVsPTIpICMgTGV2ZWxzIDEgVk4sIExldmVsIDIgcHJvdmluY2UsIExldmVsIDMgbWVhbnMgY29tbXVuZXMNCg0KVk4yc2Y8LXN0X2FzX3NmKFZOMikgJT4lIHN0X3RyYW5zZm9ybShzdF9jcnMoSE4pKSAjIENvbnZlcnQgc3AgY2xhc3MgdG8gc2YgY2xhc3MgYW5kIGNvbnZlcnQgY3JzLiBPbmx5IGRlbW9uc3RyYXRpb24NCiMgQ2xpcCB0byBIYW5vaSBib3VuZGFyeSANCg0KSE5fZGlzdDwtIFZOMnNmICU+JSBmaWx0ZXIoc3RfY29udGFpbnMoSE4sLiwgc3BhcnNlID0gRikpDQoNCkhOX2Rpc3QgJT4lIHN0X2dlb21ldHJ5KCkgJT4lIHBsb3QobWFpbj0iSGFub2kgRGlzdHJpY3QiLGNvbD1yYWluYm93KDMwKSkNCg0KYGBgDQoNCiMjIyMjIENoZWNrIGFuZCBjb252ZXJ0IGNycyBmb3Igc2hhcGVmaWxlIGZpbGUgDQoNCmBgYHtyfQ0KIyBEZWZhdWx0IGNycyBvZiBkYXRhIGluIEdBTUQgaXMgZ2VvZ3JhcGhpYyBjb29yZGluYXRlIDQzMjYNCnN0X2NycyhITl9kaXN0KSRwcm9qNHN0cmluZw0KIyBDb252ZXJ0IGxvbmdsYXQgdG8gVVRNDQoNCkhOX1VUTTwtSE5fZGlzdCAlPiUgc3RfdHJhbnNmb3JtKC4sIGNycz0zMjY0OCApICMgMzI2NDggaXMgRVBTRyBjb2RlIGZvciBab25lIDQ4IE5vcnRoIGNvdmVyIFZpZXRuYW0NCg0Kc3RfY3JzKEhOX1VUTSkkcHJvajRzdHJpbmcNCg0KYGBgDQoNCi0gQ2xpcGluZyB0aGUgc3R1ZHkgYXJlYSBvZiBpbnRlcmVzdA0KDQpgYGB7cn0NCiMgR2V0IFJhaW5mYWxsIGRhdGENClJhaW5mYWxsPC1nZXREYXRhKG5hbWUgPSAid29ybGRjbGltIix2YXI9InByZWMiLHJlcz0yLjUsbG9uPTEwMixsYXQ9MjIpDQoNCiMgQ2xpcHBpbmcgVmlldG5hbSBwcmVjaXBpdGF0aW9uDQpjcm9wX1ZOPC1jcm9wKFJhaW5mYWxsLGV4dGVudChWTikpICMgQ3JvcCB0aGUgcmFzdGVyIHRvIHRoZSBleHRlbnQgb2YgdGhlIHNoYXBlZmlsZQ0KDQpWTl9yYWluPC1tYXNrKGNyb3BfVk4sIFZOKSAjIE1hc2sgb3V0IG9yIGNsaXANCg0KcGxvdChWTl9yYWluKQ0KDQpgYGANCg0KIyAzLiBNb3NxdWl0byBNYXBwaW5nDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IHZhbHVlcyBmcm9tIHJhc3RlciBzdGFjayBsYXllcnMNCmxpYnJhcnkoc2YpDQpwb2ludHM8LXNoYXBlZmlsZSgiRTpcXFB5dGhvbl9UdXRvcmlhbHNcXEh1U3VrX0RhdGFcXFN0ZXAxXFxTdGVwMlxcTW9zX1BvaW50LnNocCIpDQoNClZhbHVlPC1yYXN0ZXI6OmV4dHJhY3QobGF5ZXIscG9pbnRzLGRmPVQpDQoNClZhbHVlPC1WYWx1ZVssYygyOm5jb2woVmFsdWUpKV0NCmxpYnJhcnkoIlBlcmZvcm1hbmNlQW5hbHl0aWNzIikNCmNoYXJ0LkNvcnJlbGF0aW9uKFZhbHVlLCBoaXN0b2dyYW09VFJVRSwgcGNoPTE5KQ0KDQpgYGANCg0KYGBge3J9DQoNCm1sYXllcjwtbGF5ZXJbW2MoMSwyLDMsNSw2LDcsOSldXQ0KDQpWYWx1ZTwtcmFzdGVyOjpleHRyYWN0KG1sYXllcixwb2ludHMsZGY9VCkNCg0KVmFsdWU8LVZhbHVlWyxjKDI6bmNvbChWYWx1ZSkpXQ0KbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KY2hhcnQuQ29ycmVsYXRpb24oVmFsdWUsIGhpc3RvZ3JhbT1UUlVFLCBwY2g9MTkpDQpGaW5hbDwtY2JpbmQoQ291bnQ9cG9pbnRzJENvdW50cyxWYWx1ZSkNCiMgDQpGaW5hbCRDb3VudDwtYXMuaW50ZWdlcihGaW5hbCRDb3VudCkNCg0KaGVhZChGaW5hbCkNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FUb29scykNCmxpYnJhcnkoTUFTUykNCmxpYnJhcnkoY2FyZXQpDQpzZXQuc2VlZCgxMjMpDQpzYW1wbGluZyA9IHNhbXBsZS5zcGxpdChGaW5hbCRDb3VudCwgU3BsaXRSYXRpbyA9IDAuOCkNCnRyYWluID0gc3Vic2V0KEZpbmFsLCBzYW1wbGluZyA9PSBUUlVFKQ0KdGVzdCAgPSBzdWJzZXQoRmluYWwsIHNhbXBsaW5nID09IEZBTFNFKQ0KDQpOQlI8LWdsbS5uYihDb3VudH5BdWd1c3RfUmFpbiArIFBvcCArIFJpY2UgKyBBdWd1c3RfVGVtcCsgIEZvcmVzdCsgIFdhdGVyLCBkYXRhPXRyYWluKQ0Kc3VtbWFyeShOQlIpDQojbW9kZWxfTkIxPC1nbG0oQ291bnR+LiwgZGF0YT10cmFpbl9OQjEsIGZhbWlseSA9IG5lZ2F0aXZlLmJpbm9taWFsKHRoZXRhID0gMC4zNCkpDQpwcmVfTkIxPC0gcHJlZGljdChOQlIsbmV3ZGF0YT10ZXN0LCB0eXBlID0gInJlc3BvbnNlIikgIyBQcmVkaWN0IGZvciBuZXcgZGF0YQ0KYWNjdXJhY3lfTkIxPC1kYXRhLmZyYW1lKFIyID0gUjIocHJlX05CMSwgdGVzdCRDb3VudCksIFJNU0UgPSBSTVNFKHByZV9OQjEsIHRlc3QkQ291bnQpLCBNQUUgPSBNQUUocHJlX05CMSx0ZXN0JENvdW50KSkNCmFjY3VyYWN5X05CMQ0KI1Nvc2FuaDwtZGF0YS5mcmFtZShjYmluZChPYnNlcnZlZD10ZXN0X05CMSRDb3VudCxQcmVkaWN0ZWQ9cHJlX05CMSkpDQojc2V0d2QoIkY6XFxSZXNlYXJjaFxcUmVzZWFyY2hfQ29vcGVyYXRpb25cXElMUklfTW9zcXVpdG9fTWFwcGluZ1xcTW9zcXVpdG9cXFByZWRpY3RlZF9NYXBzXFxGaW5hbF9NYXAiKQ0Kb3V0X3RlbXA8LXByZWRpY3QobGF5ZXIsIE5CUiwgdHlwZT0icmVzcG9uc2UiLHByb2dyZXNzPSJ3aW5kb3ciKQ0KYGBgDQoNCmBgYHtyfQ0KIyBNYWtpbmcgYSBjb3B5DQp0bWFwPC1vdXRfdGVtcA0KIyBSZW1vdmUgb3V0c2lkZSBib3ggdmFsdWVzDQp0bWFwW3RtYXA9PXRtYXBbMV1dPC1OQQ0KdG1hcFt0bWFwPjQwMF08LTQwMA0KbGlicmFyeShzcCkNCmxpYnJhcnkoZmllbGRzKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQojZGlzcGxheS5icmV3ZXIuYWxsKCkNCiNzZXR3ZCgiRjpcXFJlc2VhcmNoXFxSZXNlYXJjaF9Db29wZXJhdGlvblxcSUxSSV9Nb3NxdWl0b19NYXBwaW5nXFxNb3NxdWl0b1xcUHJlZGljdGVkX01hcHNcXEZpbmFsX01hcCIpDQojd3JpdGVSYXN0ZXIodG1hcCwgZmlsZW5hbWUgPSAiUmlza19tYXBfUUdJUy50aWYiLCBvdmVyd3JpdGU9VCkNCk5vcnRoMiA8LSBsaXN0KCJTcGF0aWFsUG9seWdvbnNSZXNjYWxlIiwgbGF5b3V0Lm5vcnRoLmFycm93KHR5cGU9MiksIA0KICAgICAgICAgICAgICBvZmZzZXQgPSBjKDEwNS4zLCAyMC44KSwgc2NhbGUgPSAxMDAwLCBmaWxsPSJncmF5Iiwgd2hpY2ggPSAxKQ0KY2xycy5zcGVjIDwtIGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwoMTEsICJTcGVjdHJhbCIpKSkNCm1hcDE8LXNwcGxvdCh0bWFwLCB6Y29sID0gImxheWVyIiwgY29sLnJlZ2lvbnMgPSBjbHJzLnNwZWMoMTgwKSwgI3RpbS5jb2xvcnMoKQ0KICAgICAgICAgICAgIGN1dHMgPSAxODAsIGNvbG9ya2V5ID0gbGlzdChzcGFjZSA9ICJyaWdodCIpLA0KICAgICAgICAgICAgIHNwLmxheW91dD1saXN0KE5vcnRoMikpDQptYXAxDQpgYGANCg0K