This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.

Load necessary libraries:

library(rgdal)
library(ggplot2)
library(maptools)
library(rgeos)
library(sp)
library(raster)
library(ggmap)
library(rJava)
Error: package or namespace load failed for <U+6186>Java?
 .onLoad failed in loadNamespace() for 'rJava', details:
  call: fun(libname, pkgname)
  error: No CurrentVersion entry in Software/JavaSoft registry! Try re-installing Java and make sure R and Java have matching architectures.

Load in some map data on China. The getData() function from the raster package can be used to download administrative boundaries directly from GADM.org.

#Get data directly from website (GADM.org)
#chinaMap<-getData('GADM',country="CHN",level=1) #province level; getData is part of package raster
#Load in pre-downloaded files; data folder is local to folder where R notebook is saved
ChinaRD0<-readRDS("data/CHN_adm0.rds") #Country outline
ChinaRD1<-readRDS("data/CHN_adm1.rds") #provinces
ChinaRD2<-readRDS("data/CHN_adm2.rds") #prefectures
ChinaRD3<-readRDS("data/CHN_adm3.rds") #counties
#Plot Mainland China
plot(ChinaRD0,main="Mainland China country outline")

Let’s plot Shanghai and its counties

names(ChinaRD1) #variables in ChinaRD1 provincial dataset
 [1] "OBJECTID"  "ID_0"      "ISO"       "NAME_0"    "ID_1"      "NAME_1"    "HASC_1"    "CCN_1"     "CCA_1"     "TYPE_1"   
[11] "ENGTYPE_1" "NL_NAME_1" "VARNAME_1"
ChinaRD1@data[1:10,] #this takes data from ChinaRD1 and shows first 10 rows
#slot(ChinaRD1,"data")[1:10,] #another way to do same as ChinaRD1@data[1:10,]
names(ChinaRD3) #names of columns in data
 [1] "OBJECTID"  "ID_0"      "ISO"       "NAME_0"    "ID_1"      "NAME_1"    "ID_2"      "NAME_2"    "ID_3"      "NAME_3"   
[11] "CCN_3"     "CCA_3"     "TYPE_3"    "ENGTYPE_3" "NL_NAME_3" "VARNAME_3"
shanghaiCities<-ChinaRD3[ChinaRD3$NAME_2=="Shanghai",] #Selects only cities in Shanghai municipality
plot(shanghaiCities)
plot(shanghaiCities[shanghaiCities$NAME_3=="Shanghai",],border="blue",add=TRUE) #Notice add=T; this draws on top of current plot
#Add names
text(coordinates(shanghaiCities),
     labels=shanghaiCities$NAME_3,col="red",cex=0.7)

#Get some external data and merge
popData<-read.table("data/china_pop_9293.csv",header=T,sep=",",stringsAsFactors =FALSE)
str(popData)
'data.frame':   289 obs. of  5 variables:
 $ GB_code      : int  110000 120000 130100 130200 130300 130400 130500 130600 130700 130800 ...
 $ province_name: chr  "Beijing" "Tianjin" "Hebei" "Hebei" ...
 $ city_name    : chr  "Beijing" "Tianjin" "Shijiazhuang" "Tangshan" ...
 $ pop92        : num  1045 883 290 668 252 ...
 $ pop93        : num  1051 888 827 672 255 ...
ChinaData2 <- slot(ChinaRD2, "data")
names(ChinaData2)
 [1] "OBJECTID"  "ID_0"      "ISO"       "NAME_0"    "ID_1"      "NAME_1"    "ID_2"      "NAME_2"    "HASC_2"    "CCN_2"    
[11] "CCA_2"     "TYPE_2"    "ENGTYPE_2" "NL_NAME_2" "VARNAME_2"
names(ChinaData2)[c(6,8)]<-c("province_name","city_name")
table(ChinaData2$province_name) #Show frequency table of prefectures by province

         Anhui        Beijing      Chongqing         Fujian          Gansu      Guangdong        Guangxi        Guizhou 
            17              1              1              9             14             21             14              9 
        Hainan          Hebei   Heilongjiang          Henan          Hubei          Hunan        Jiangsu        Jiangxi 
             3             11             13             18             17             14             13             11 
         Jilin       Liaoning     Nei Mongol    Ningxia Hui        Qinghai        Shaanxi       Shandong       Shanghai 
             9             13             12              5              8             10             17              1 
        Shanxi        Sichuan        Tianjin Xinjiang Uygur         Xizang         Yunnan       Zhejiang 
            11             22              1             15              7             16             11 
#This is an example of merging a simple data table by two columns
prefectureData<-merge(ChinaData2, popData, by=c("province_name", "city_name"),all.x=TRUE)
prefectureData$city_name[is.na(prefectureData$pop92)] #not a great way to match
  [1] "Chizhou"                   "Lu'an"                     "Ma'anshan"                 "Xuancheng"                
  [5] "Dingxi"                    "Gannan Tibetan"            "Linxia Hui"                "Longnan"                  
  [9] "Qingyang"                  "Yunfu"                     "Chongzuo"                  "Fangchenggang"            
 [13] "Guilin"                    "Hezhou"                    "Laibin"                    "Bijie"                    
 [17] "Qiandongnan Miao and Dong" "Qiannan Buyei and Miao"    "Qianxinan Buyei and Miao"  "Tongren"                  
 [21] "Haikou"                    "Hainan"                    "Sanya"                     "Daxing'anling"            
 [25] "Jiyuan shi"                "Enshi Tujia and Miao"      "Huanggang"                 "Jingzhou"                 
 [29] "Qianjiang"                 "Shennongjia"               "Suizhou Shi"               "Tianmen"                  
 [33] "Xiantao"                   "Xiangxi Tujia and Miao"    "Zhangjiajie"               "Huai'an"                  
 [37] "Suqian"                    "Taizhou"                   "Fuzhou"                    "Ji'an"                    
 [41] "Yanbian Korean"            "Huludao"                   "Alxa"                      "Baotou"                   
 [45] "Baynnur"                   "Chifeng"                   "Hohhot"                    "Hulunbuir"                
 [49] "Ordos"                     "Tongliao"                  "Ulaan Chab"                "Wuhai"                    
 [53] "Xilin Gol"                 "Xing'an"                   "Guyuan"                    "Shizuishan"               
 [57] "Wuzhong"                   "Yinchuan"                  "Zhongwei"                  "Golog Tibetan"            
 [61] "Gyêgu Tibetan"             "Haibei Tibetan"            "Haidong"                   "Hainan Tibetan"           
 [65] "Haixi Mongol and Tibetan"  "Huangnan Tibetan"          "Shangluo"                  "Xi'an"                    
 [69] "Yan'an"                    "Tai'an"                    "Jinzhong"                  "Luliang"                  
 [73] "Bazhong"                   "Dazhou"                    "Garzê Tibetan"             "Guang'an"                 
 [77] "Liangshan Yi"              "Meishan"                   "Neijiang]]"                "Ngawa Tibetan and Qiang"  
 [81] "Ya'an"                     "Ziyang"                    "Aksu"                      "Altay"                    
 [85] "Bayin'gholin Mongol"       "Börtala Mongol"            "Changji Hui"               "Hami"                     
 [89] "Ili Kazakh"                "Karamay"                   "Kashgar"                   "Khotan"                   
 [93] "Kizilsu Kirghiz"           "Shihezi"                   "Tacheng"                   "Turfan"                   
 [97] "Ürümqi"                    "Chamdo"                    "Lhasa"                     "Nagchu"                   
[101] "Ngari"                     "Nyingtri"                  "Shannan"                   "Shigatse"                 
[105] "Baoshan"                   "Chuxiong Yi"               "Dali Bai"                  "Dehong Dai and Jingpo"    
[109] "Dêqên Tibetan"             "Honghe Hani and Yi"        "Kunming"                   "Lijiang"                  
[113] "Lincang"                   "Nujiang Lisu"              "Pu'er"                     "Qujing"                   
[117] "Wenshan Zhuang and Miao"   "Xishuangbanna Dai"         "Yuxi"                      "Zhaotong"                 
[121] "Taizhou"                  
#Because we are using the SP package, we can also easily merge directly onto SpatialPolygonsDataFrame
names(ChinaRD2)[c(6,8)]<-c("province_name","city_name")
popDataSP<-merge(ChinaRD2, popData, by=c("province_name", "city_name"),all.x=TRUE)
#rm(ChinaRD2) #Removes old object--not really necessary
summary(popDataSP)
Object of class SpatialPolygonsDataFrame
Coordinates:
       min       max
x 73.55770 134.77393
y 18.15931  53.56086
Is projected: FALSE 
proj4string :
[+proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0]
Data attributes:
 province_name       city_name            OBJECTID           ID_0        ISO               NAME_0               ID_1      
 Length:344         Length:344         Min.   :  1.00   Min.   :49   Length:344         Length:344         Min.   : 1.00  
 Class :character   Class :character   1st Qu.: 86.75   1st Qu.:49   Class :character   Class :character   1st Qu.: 8.75  
 Mode  :character   Mode  :character   Median :172.50   Median :49   Mode  :character   Mode  :character   Median :15.00  
                                       Mean   :172.50   Mean   :49                                         Mean   :16.12  
                                       3rd Qu.:258.25   3rd Qu.:49                                         3rd Qu.:23.00  
                                       Max.   :344.00   Max.   :49                                         Max.   :31.00  
                                                                                                                          
      ID_2           HASC_2              CCN_2        CCA_2              TYPE_2           ENGTYPE_2          NL_NAME_2        
 Min.   :  1.00   Length:344         Min.   : NA   Length:344         Length:344         Length:344         Length:344        
 1st Qu.: 86.75   Class :character   1st Qu.: NA   Class :character   Class :character   Class :character   Class :character  
 Median :172.50   Mode  :character   Median : NA   Mode  :character   Mode  :character   Mode  :character   Mode  :character  
 Mean   :172.50                      Mean   :NaN                                                                              
 3rd Qu.:258.25                      3rd Qu.: NA                                                                              
 Max.   :344.00                      Max.   : NA                                                                              
                                     NA's   :344                                                                              
  VARNAME_2            GB_code           pop92             pop93       
 Length:344         Min.   :110000   Min.   :  10.93   Min.   :  11.4  
 Class :character   1st Qu.:320550   1st Qu.:  88.78   1st Qu.: 105.9  
 Mode  :character   Median :371100   Median : 223.25   Median : 245.7  
                    Mean   :372528   Mean   : 281.87   Mean   : 305.3  
                    3rd Qu.:441650   3rd Qu.: 416.97   3rd Qu.: 445.8  
                    Max.   :630100   Max.   :1496.96   Max.   :1503.7  
                    NA's   :97       NA's   :121       NA's   :119     
#Chloropleth Plots: colors polygons according to some underlying variable
#Chloropleth Plotting Method 1: you can find different color palettes in R Helpfiles
brks <- quantile(popDataSP$pop92,seq(0,1,1/100),na.rm=TRUE) # Use 100 break points
grayCols <- gray(seq(1,.25,length=100)) # This is 0 to 1 inclusive
ints <- findInterval(popDataSP$pop92,brks,all.inside=TRUE) # Inputs: variable, break points, use all.inside=TRUE
polCols <- grayCols[ints]
#pdf("pop92_map.pdf")
plot(popDataSP,col=polCols,main="1992 Population by Prefecture")
plot(popDataSP[is.na(popDataSP$pop92),],border="red",add=TRUE) #draw red border around prefectures with missing values

#polCols[is.na(popDataSP$pop92)]<-"red"
#plot(popDataSP,col=polCols,main="1992 Population by Prefecture")
#dev.off()

Another way to do chloropleth plots using ggplot()

#Chloropleth Plotting Method 2: using ggplot2
city_map<-fortify(ChinaRD2, region= "OBJECTID")
Error: isTRUE(gpclibPermitStatus()) is not TRUE

Next, we work with points data

#Work with points data
provinceCentroids<-coordinates(ChinaRD1)
plot(ChinaRD1)
points(provinceCentroids,col="red") #adds points to current plot
plot(ChinaRD3,border="blue",add=T)

#Which prefecture is province centroid in?
spdf_pts <- SpatialPointsDataFrame(coords=provinceCentroids, data=data.frame(ChinaRD1$NAME_1))
#Make sure projection is the same (should be since comes from same data source)
proj4string(spdf_pts) <-proj4string(popDataSP)
#proj4string(spdf_pts)<-CRS(" +proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0")
plot(popDataSP)
plot(spdf_pts,col="red",add=TRUE)

pref_pts<-over(spdf_pts,popDataSP)
pref_pts$x<-slot(spdf_pts,"coords")[,1]
pref_pts$y<-slot(spdf_pts,"coords")[,2]
plot(ChinaRD1)
points(provinceCentroids,col="red") #adds points to current plot
#Add names
text(pref_pts$x,pref_pts$y,
     labels=pref_pts$city_name,col="blue",cex=0.7)

#Now write to CSV
write.table(pref_pts, file = "centroids_in_cities.CSV", sep = ",", col.names=names(pref_pts),row.names=FALSE,na="")

Using OpenStreetMap

#Mapping with OSM
sufe_names="SUFE"
sufe_pts<-SpatialPointsDataFrame(coords=cbind(121.5153883,31.2948375), data=data.frame(sufe_names))
sufe_pts$lon<-coordinates(sufe_pts)[1,1]
sufe_pts$lat<-coordinates(sufe_pts)[1,2]
sufe_map <- openmap(c(sufe_pts$lat+0.03,sufe_pts$lon-0.03),
               c(sufe_pts$lat-0.03,sufe_pts$lon+0.03),
               minNumTiles=10,type="osm")
Error in openmap(c(sufe_pts$lat + 0.03, sufe_pts$lon - 0.03), c(sufe_pts$lat -  : 
  could not find function "openmap"
LS0tDQp0aXRsZTogJ0xlY3R1cmUgMTE6IFNwYXRpYWwgTWV0aG9kcyBpbiBSJw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoNClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIE5vdGVib29rLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuIA0KDQpMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXM6DQpgYGB7cn0NCmxpYnJhcnkocmdkYWwpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KG1hcHRvb2xzKQ0KbGlicmFyeShyZ2VvcykNCmxpYnJhcnkoc3ApDQpsaWJyYXJ5KHJhc3RlcikNCmxpYnJhcnkoZ2dtYXApDQojbGlicmFyeShySmF2YSkNCiNsaWJyYXJ5KE9wZW5TdHJlZXRNYXApDQpgYGANCkxvYWQgaW4gc29tZSBtYXAgZGF0YSBvbiBDaGluYS4gVGhlIGdldERhdGEoKSBmdW5jdGlvbiBmcm9tIHRoZSByYXN0ZXIgcGFja2FnZSBjYW4gYmUgdXNlZCB0byBkb3dubG9hZCBhZG1pbmlzdHJhdGl2ZSBib3VuZGFyaWVzIGRpcmVjdGx5IGZyb20gR0FETS5vcmcuDQpgYGB7cn0NCiNHZXQgZGF0YSBkaXJlY3RseSBmcm9tIHdlYnNpdGUgKEdBRE0ub3JnKQ0KI2NoaW5hTWFwPC1nZXREYXRhKCdHQURNJyxjb3VudHJ5PSJDSE4iLGxldmVsPTEpICNwcm92aW5jZSBsZXZlbDsgZ2V0RGF0YSBpcyBwYXJ0IG9mIHBhY2thZ2UgcmFzdGVyDQoNCiNMb2FkIGluIHByZS1kb3dubG9hZGVkIGZpbGVzOyBkYXRhIGZvbGRlciBpcyBsb2NhbCB0byBmb2xkZXIgd2hlcmUgUiBub3RlYm9vayBpcyBzYXZlZA0KQ2hpbmFSRDA8LXJlYWRSRFMoImRhdGEvQ0hOX2FkbTAucmRzIikgI0NvdW50cnkgb3V0bGluZQ0KQ2hpbmFSRDE8LXJlYWRSRFMoImRhdGEvQ0hOX2FkbTEucmRzIikgI3Byb3ZpbmNlcw0KQ2hpbmFSRDI8LXJlYWRSRFMoImRhdGEvQ0hOX2FkbTIucmRzIikgI3ByZWZlY3R1cmVzDQpDaGluYVJEMzwtcmVhZFJEUygiZGF0YS9DSE5fYWRtMy5yZHMiKSAjY291bnRpZXMNCg0KI1Bsb3QgTWFpbmxhbmQgQ2hpbmENCnBsb3QoQ2hpbmFSRDAsbWFpbj0iTWFpbmxhbmQgQ2hpbmEgY291bnRyeSBvdXRsaW5lIikNCmBgYA0KTGV0J3MgcGxvdCBTaGFuZ2hhaSBhbmQgaXRzIGNvdW50aWVzDQpgYGB7cn0NCm5hbWVzKENoaW5hUkQxKSAjdmFyaWFibGVzIGluIENoaW5hUkQxIHByb3ZpbmNpYWwgZGF0YXNldA0KQ2hpbmFSRDFAZGF0YVsxOjEwLF0gI3RoaXMgdGFrZXMgZGF0YSBmcm9tIENoaW5hUkQxIGFuZCBzaG93cyBmaXJzdCAxMCByb3dzDQojc2xvdChDaGluYVJEMSwiZGF0YSIpWzE6MTAsXSAjYW5vdGhlciB3YXkgdG8gZG8gc2FtZSBhcyBDaGluYVJEMUBkYXRhWzE6MTAsXQ0KbmFtZXMoQ2hpbmFSRDMpICNuYW1lcyBvZiBjb2x1bW5zIGluIGRhdGENCnNoYW5naGFpQ2l0aWVzPC1DaGluYVJEM1tDaGluYVJEMyROQU1FXzI9PSJTaGFuZ2hhaSIsXSAjU2VsZWN0cyBvbmx5IGNpdGllcyBpbiBTaGFuZ2hhaSBtdW5pY2lwYWxpdHkNCnBsb3Qoc2hhbmdoYWlDaXRpZXMpDQpwbG90KHNoYW5naGFpQ2l0aWVzW3NoYW5naGFpQ2l0aWVzJE5BTUVfMz09IlNoYW5naGFpIixdLGJvcmRlcj0iYmx1ZSIsYWRkPVRSVUUpICNOb3RpY2UgYWRkPVQ7IHRoaXMgZHJhd3Mgb24gdG9wIG9mIGN1cnJlbnQgcGxvdA0KI0FkZCBuYW1lcw0KdGV4dChjb29yZGluYXRlcyhzaGFuZ2hhaUNpdGllcyksDQogICAgIGxhYmVscz1zaGFuZ2hhaUNpdGllcyROQU1FXzMsY29sPSJyZWQiLGNleD0wLjcpDQpgYGANCmBgYHtyfQ0KI0dldCBzb21lIGV4dGVybmFsIGRhdGEgYW5kIG1lcmdlDQpwb3BEYXRhPC1yZWFkLnRhYmxlKCJkYXRhL2NoaW5hX3BvcF85MjkzLmNzdiIsaGVhZGVyPVQsc2VwPSIsIixzdHJpbmdzQXNGYWN0b3JzID1GQUxTRSkNCnN0cihwb3BEYXRhKQ0KQ2hpbmFEYXRhMiA8LSBzbG90KENoaW5hUkQyLCAiZGF0YSIpDQpuYW1lcyhDaGluYURhdGEyKQ0KbmFtZXMoQ2hpbmFEYXRhMilbYyg2LDgpXTwtYygicHJvdmluY2VfbmFtZSIsImNpdHlfbmFtZSIpDQp0YWJsZShDaGluYURhdGEyJHByb3ZpbmNlX25hbWUpICNTaG93IGZyZXF1ZW5jeSB0YWJsZSBvZiBwcmVmZWN0dXJlcyBieSBwcm92aW5jZQ0KI1RoaXMgaXMgYW4gZXhhbXBsZSBvZiBtZXJnaW5nIGEgc2ltcGxlIGRhdGEgdGFibGUgYnkgdHdvIGNvbHVtbnMNCnByZWZlY3R1cmVEYXRhPC1tZXJnZShDaGluYURhdGEyLCBwb3BEYXRhLCBieT1jKCJwcm92aW5jZV9uYW1lIiwgImNpdHlfbmFtZSIpLGFsbC54PVRSVUUpDQpwcmVmZWN0dXJlRGF0YSRjaXR5X25hbWVbaXMubmEocHJlZmVjdHVyZURhdGEkcG9wOTIpXSAjbm90IGEgZ3JlYXQgd2F5IHRvIG1hdGNoDQojQmVjYXVzZSB3ZSBhcmUgdXNpbmcgdGhlIFNQIHBhY2thZ2UsIHdlIGNhbiBhbHNvIGVhc2lseSBtZXJnZSBkaXJlY3RseSBvbnRvIFNwYXRpYWxQb2x5Z29uc0RhdGFGcmFtZQ0KbmFtZXMoQ2hpbmFSRDIpW2MoNiw4KV08LWMoInByb3ZpbmNlX25hbWUiLCJjaXR5X25hbWUiKQ0KcG9wRGF0YVNQPC1tZXJnZShDaGluYVJEMiwgcG9wRGF0YSwgYnk9YygicHJvdmluY2VfbmFtZSIsICJjaXR5X25hbWUiKSxhbGwueD1UUlVFKQ0KI3JtKENoaW5hUkQyKSAjUmVtb3ZlcyBvbGQgb2JqZWN0LS1ub3QgcmVhbGx5IG5lY2Vzc2FyeQ0Kc3VtbWFyeShwb3BEYXRhU1ApDQpgYGANCmBgYHtyfQ0KI0NobG9yb3BsZXRoIFBsb3RzOiBjb2xvcnMgcG9seWdvbnMgYWNjb3JkaW5nIHRvIHNvbWUgdW5kZXJseWluZyB2YXJpYWJsZQ0KI0NobG9yb3BsZXRoIFBsb3R0aW5nIE1ldGhvZCAxOiB5b3UgY2FuIGZpbmQgZGlmZmVyZW50IGNvbG9yIHBhbGV0dGVzIGluIFIgSGVscGZpbGVzDQpicmtzIDwtIHF1YW50aWxlKHBvcERhdGFTUCRwb3A5MixzZXEoMCwxLDEvMTAwKSxuYS5ybT1UUlVFKSAjIFVzZSAxMDAgYnJlYWsgcG9pbnRzDQpncmF5Q29scyA8LSBncmF5KHNlcSgxLC4yNSxsZW5ndGg9MTAwKSkgIyBUaGlzIGlzIDAgdG8gMSBpbmNsdXNpdmUNCmludHMgPC0gZmluZEludGVydmFsKHBvcERhdGFTUCRwb3A5MixicmtzLGFsbC5pbnNpZGU9VFJVRSkgIyBJbnB1dHM6IHZhcmlhYmxlLCBicmVhayBwb2ludHMsIHVzZSBhbGwuaW5zaWRlPVRSVUUNCnBvbENvbHMgPC0gZ3JheUNvbHNbaW50c10NCiNwZGYoInBvcDkyX21hcC5wZGYiKQ0KcGxvdChwb3BEYXRhU1AsY29sPXBvbENvbHMsbWFpbj0iMTk5MiBQb3B1bGF0aW9uIGJ5IFByZWZlY3R1cmUiKQ0KcGxvdChwb3BEYXRhU1BbaXMubmEocG9wRGF0YVNQJHBvcDkyKSxdLGJvcmRlcj0icmVkIixhZGQ9VFJVRSkgI2RyYXcgcmVkIGJvcmRlciBhcm91bmQgcHJlZmVjdHVyZXMgd2l0aCBtaXNzaW5nIHZhbHVlcw0KI3BvbENvbHNbaXMubmEocG9wRGF0YVNQJHBvcDkyKV08LSJyZWQiDQojcGxvdChwb3BEYXRhU1AsY29sPXBvbENvbHMsbWFpbj0iMTk5MiBQb3B1bGF0aW9uIGJ5IFByZWZlY3R1cmUiKQ0KI2Rldi5vZmYoKQ0KYGBgDQpBbm90aGVyIHdheSB0byBkbyBjaGxvcm9wbGV0aCBwbG90cyB1c2luZyBnZ3Bsb3QoKQ0KYGBge3J9DQojQ2hsb3JvcGxldGggUGxvdHRpbmcgTWV0aG9kIDI6IHVzaW5nIGdncGxvdDINCmNpdHlfbWFwPC1mb3J0aWZ5KENoaW5hUkQyLCByZWdpb249ICJPQkpFQ1RJRCIpDQpnZ3Bsb3QoKSArIGdlb21fbWFwKGRhdGEgPSBzbG90KHBvcERhdGFTUCwgImRhdGEiKSwgYWVzKG1hcF9pZCA9IE9CSkVDVElELCBmaWxsID0gcG9wOTIpLA0KICAgIG1hcCA9IGNpdHlfbWFwKSArIGV4cGFuZF9saW1pdHMoeCA9IGNpdHlfbWFwJGxvbmcsIHkgPSBjaXR5X21hcCRsYXQpDQpgYGANCk5leHQsIHdlIHdvcmsgd2l0aCBwb2ludHMgZGF0YQ0KYGBge3J9DQojV29yayB3aXRoIHBvaW50cyBkYXRhDQpwcm92aW5jZUNlbnRyb2lkczwtY29vcmRpbmF0ZXMoQ2hpbmFSRDEpDQpwbG90KENoaW5hUkQxKQ0KcG9pbnRzKHByb3ZpbmNlQ2VudHJvaWRzLGNvbD0icmVkIikgI2FkZHMgcG9pbnRzIHRvIGN1cnJlbnQgcGxvdA0KcGxvdChDaGluYVJEMyxib3JkZXI9ImJsdWUiLGFkZD1UKQ0KI1doaWNoIHByZWZlY3R1cmUgaXMgcHJvdmluY2UgY2VudHJvaWQgaW4/DQpzcGRmX3B0cyA8LSBTcGF0aWFsUG9pbnRzRGF0YUZyYW1lKGNvb3Jkcz1wcm92aW5jZUNlbnRyb2lkcywgZGF0YT1kYXRhLmZyYW1lKENoaW5hUkQxJE5BTUVfMSkpDQojTWFrZSBzdXJlIHByb2plY3Rpb24gaXMgdGhlIHNhbWUgKHNob3VsZCBiZSBzaW5jZSBjb21lcyBmcm9tIHNhbWUgZGF0YSBzb3VyY2UpDQpwcm9qNHN0cmluZyhzcGRmX3B0cykgPC1wcm9qNHN0cmluZyhwb3BEYXRhU1ApDQojcHJvajRzdHJpbmcoc3BkZl9wdHMpPC1DUlMoIiArcHJvaj1sb25nbGF0ICtkYXR1bT1XR1M4NCArZWxscHM9V0dTODQgK3Rvd2dzODQ9MCwwLDAiKQ0KcGxvdChwb3BEYXRhU1ApDQpwbG90KHNwZGZfcHRzLGNvbD0icmVkIixhZGQ9VFJVRSkNCnByZWZfcHRzPC1vdmVyKHNwZGZfcHRzLHBvcERhdGFTUCkNCnByZWZfcHRzJHg8LXNsb3Qoc3BkZl9wdHMsImNvb3JkcyIpWywxXQ0KcHJlZl9wdHMkeTwtc2xvdChzcGRmX3B0cywiY29vcmRzIilbLDJdDQpwbG90KENoaW5hUkQxKQ0KcG9pbnRzKHByb3ZpbmNlQ2VudHJvaWRzLGNvbD0icmVkIikgI2FkZHMgcG9pbnRzIHRvIGN1cnJlbnQgcGxvdA0KI0FkZCBuYW1lcw0KdGV4dChwcmVmX3B0cyR4LHByZWZfcHRzJHksDQogICAgIGxhYmVscz1wcmVmX3B0cyRjaXR5X25hbWUsY29sPSJibHVlIixjZXg9MC43KQ0KI05vdyB3cml0ZSB0byBDU1YNCndyaXRlLnRhYmxlKHByZWZfcHRzLCBmaWxlID0gImNlbnRyb2lkc19pbl9jaXRpZXMuQ1NWIiwgc2VwID0gIiwiLCBjb2wubmFtZXM9bmFtZXMocHJlZl9wdHMpLHJvdy5uYW1lcz1GQUxTRSxuYT0iIikNCmBgYA0KVXNpbmcgT3BlblN0cmVldE1hcA0KYGBge3J9DQojTWFwcGluZyB3aXRoIE9TTQ0Kc3VmZV9uYW1lcz0iU1VGRSINCnN1ZmVfcHRzPC1TcGF0aWFsUG9pbnRzRGF0YUZyYW1lKGNvb3Jkcz1jYmluZCgxMjEuNTE1Mzg4MywzMS4yOTQ4Mzc1KSwgZGF0YT1kYXRhLmZyYW1lKHN1ZmVfbmFtZXMpKQ0Kc3VmZV9wdHMkbG9uPC1jb29yZGluYXRlcyhzdWZlX3B0cylbMSwxXQ0Kc3VmZV9wdHMkbGF0PC1jb29yZGluYXRlcyhzdWZlX3B0cylbMSwyXQ0Kc3VmZV9tYXAgPC0gb3Blbm1hcChjKHN1ZmVfcHRzJGxhdCswLjAzLHN1ZmVfcHRzJGxvbi0wLjAzKSwNCiAgICAgICAgICAgICAgIGMoc3VmZV9wdHMkbGF0LTAuMDMsc3VmZV9wdHMkbG9uKzAuMDMpLA0KICAgICAgICAgICAgICAgbWluTnVtVGlsZXM9MTAsdHlwZT0ib3NtIikNCnBsb3Qoc3VmZV9tYXApDQpgYGANCg0K