#
Contents
Using R package:aspace
Measures of Centrality
1.(Weighted) Mean center
2.Median center
3.Central feature
Measures of Dispersion
1.Standard Distance
2.Weighted Std. Distance
3.Standard Deviational Ellipse
#
0. Laoding Data
rm(list = ls())
library(sf)
library(tmap)
library(aspace) #install.packages("aspace")
警告: 套件 ‘aspace’ 是用 R 版本 4.3.3 來建造的警告: 套件 ‘splancs’ 是用 R 版本 4.3.3 來建造的警告: 套件 ‘sp’ 是用 R 版本 4.3.3 來建造的警告: 套件 ‘Hmisc’ 是用 R 版本 4.3.3 來建造的
library(tidyverse)
schools_sf = st_read("./data/Schools.shp",options="ENCODING=BIG5")
options: ENCODING=BIG5
Reading layer `Schools' from data source `C:\Users\wenth\Desktop\WK05\data\Schools.shp' using driver `ESRI Shapefile'
Simple feature collection with 424 features and 10 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 155883 ymin: 2535016 xmax: 207754.2 ymax: 2588604
Projected CRS: Transverse Mercator
head(schools_sf)
Simple feature collection with 6 features and 10 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 156258.5 ymin: 2543261 xmax: 171221.4 ymax: 2550982
Projected CRS: Transverse Mercator
AREA PERIMETER SCHOOL_ SCHOOL_ID NAME X_coor Y_coor ID1 NEAR_FID NEAR_DIST
1 0 0 38 39 土城子國小分校 156258.5 2550982 39 29 3293.0748
2 0 0 81 82 鎮海國小 160475.1 2546815 82 18 668.7404
3 0 0 82 83 幼稚園 162749.6 2545007 83 26 327.9155
4 0 0 86 87 慈幼高工 171221.4 2543562 87 4 139.6705
5 0 0 87 88 母佑幼稚園 171159.1 2543437 88 3 139.6705
6 0 0 88 89 牧群幼稚園 171164.3 2543261 89 4 176.0769
geometry
1 POINT (156258.5 2550982)
2 POINT (160475.1 2546815)
3 POINT (162749.6 2545007)
4 POINT (171221.4 2543562)
5 POINT (171159.1 2543437)
6 POINT (171164.3 2543261)
schools_lyr <- tm_shape(schools_sf)+tm_dots(col="red", size= 0.1) + tm_layout(frame = F)
#
1. Data Preparation
(creating required fields)
# Generating no. of student in each school
schools_sf$Students<-as.integer(runif(424,1000,10000))
# Generating school type: cluster vs. isolation
for (i in 1: 424) {
if (schools_sf$NEAR_DIST[i]< 500) {
schools_sf$type[i]<- "Cluster"
} else schools_sf$type[i]<- "Isolation"
}
index<- schools_sf$type == "Cluster"
school_cluster <- schools_sf[index,]
length(school_cluster)
[1] 13
schools.c_lyr <- tm_shape(school_cluster)+tm_dots(col="red", size= 0.1) + tm_layout(frame = F)
School_df <- data.frame(x=schools_sf$X_coor, y=schools_sf$Y_coor,
type=schools_sf$type, students=schools_sf$Students)
#
2. Mean Center
# calc_mnc() Mean Centre Calculator
Mean.Center <- calc_mnc(id=1, weighted=FALSE, weights=NULL, points=School_df[,1:2])
W.Mean.Center <- calc_mnc(id=1, weighted=TRUE, weights=School_df$students, points=School_df[,1:2])
Mean.xcoor <- Mean.Center$LOCATIONS[2]
Mean.ycoor <- Mean.Center$LOCATIONS[3]
Mean.Center_sfg = st_point(c(Mean.xcoor, Mean.ycoor))
Mean.Center_sfc = st_sfc(Mean.Center_sfg)
Mean.Center_sf <- st_sf(Mean.Center_sfc)
head(Mean.Center_sf)
Simple feature collection with 1 feature and 0 fields
Geometry type: POINT
Dimension: XY
Bounding box: xmin: 173823.8 ymin: 2557130 xmax: 173823.8 ymax: 2557130
CRS: NA
Mean.Center_sfc
1 POINT (173823.8 2557130)
# using pipe coding style
Mean.Center.Coor <- c(Mean.xcoor, Mean.ycoor)
Mean.Center_sf1 <- Mean.Center.Coor %>% st_point %>% st_sfc %>% st_sf
class(Mean.Center_sf1)
[1] "sf" "data.frame"
st_crs(Mean.Center_sf1) <- st_crs(schools_sf)
Mean.Center_lyr <- tm_shape(Mean.Center_sf1)+tm_dots(fill="blue", size=1)
# Weighted Mean Center
W.Mean.xcoor <- W.Mean.Center$LOCATIONS[2]
W.Mean.ycoor <- W.Mean.Center$LOCATIONS[3]
W.Mean.Center.Coor <- c(W.Mean.xcoor, W.Mean.ycoor)
W.Mean.Center_sf <- W.Mean.Center.Coor %>% st_point %>% st_sfc %>% st_sf
st_crs(W.Mean.Center_sf) <- st_crs(schools_sf)
W.Mean.Center_lyr <- tm_shape(W.Mean.Center_sf)+tm_dots(fill="green", size= 0.5)
schools_lyr + Mean.Center_lyr + W.Mean.Center_lyr

#
3. Median Center
# calc_mdc(): Median Centre Calculator
Median.Center <- calc_mdc(id=1,points=School_df[,1:2])
Median.xcoor <- Median.Center$LOCATIONS[2]
Median.ycoor <- Median.Center$LOCATIONS[3]
Median.Center.Coor <- c(Median.xcoor, Median.ycoor)
Median.Center_sf <- Median.Center.Coor %>% st_point %>% st_sfc %>% st_sf
st_crs(Median.Center_sf) <- st_crs(schools_sf)
Median.Center_lyr <- tm_shape(Median.Center_sf)+tm_dots(fill = "green", size= 0.5)
### Compare Mean vs. Median
schools_lyr + Mean.Center_lyr + Median.Center_lyr

#
4. SDD: Standard Distance
using plot_sdd
# calc_sdd(): Calculate the Standard Distance Deviation (Standard Distance)
school.SDD <- calc_sdd(id=1, points=School_df[,1:2])
plot_sdd(school.SDD, centre.col="red")

using tmap
center.x<- school.SDD$ATTRIBUTES$CENTRE.x
center.y<- school.SDD$ATTRIBUTES$CENTRE.y
center.coor <- c(center.x, center.y)
center_sf<- center.coor %>% st_point %>% st_sfc %>% st_sf
st_crs(center_sf) <- st_crs(schools_sf)
rad<- school.SDD$ATTRIBUTES$SDD.radius
SD_sf<- st_buffer(center_sf, rad)
SD_lyr <- tm_shape(SD_sf) + tm_borders(col = "blue")+tm_layout(frame = F)
Center_lyr <- tm_shape(center_sf) + tm_dots(fill="blue", size= 0.5)
schools_lyr+ Center_lyr+ SD_lyr

# weighted SDD
school.SDD2 <- calc_sdd(id=1, points = School_df[,1:2],
weighted = TRUE, weights=School_df$students)
#
5. SDE:Standard Deviational Ellipse
using plot_sde
## SDE Center: sdeatt
# calc_sde() Calculate the Standard Deviation Ellipse
school.SDE<- calc_sde(id=1, points=School_df[,1:2])
plot_sde(school.SDE)

using tmap
## Creating SDE center
SDE.center.x<- school.SDE$ATTRIBUTES$CENTRE.x
SDE.center.y<- school.SDE$ATTRIBUTES$CENTRE.y
SDE.center.coor <- c(SDE.center.x, SDE.center.y)
SDE.center_sf <- SDE.center.coor %>% st_point %>% st_sfc %>% st_sf
st_crs(SDE.center_sf) <- st_crs(schools_sf)
SDE.center_lyr <- tm_shape(SDE.center_sf) + tm_dots(fill = "blue", size=0.5)+tm_layout(frame = F)
## Creating SDE Polygon
xcoor <- school.SDE$LOCATIONS$x
ycoor <- school.SDE$LOCATIONS$y
xy = data.frame(x=xcoor, y=ycoor)
xys = st_as_sf(xy, coords=c("x","y"))
st_crs(xys)<-st_crs(schools_sf)
SDE_sfc <- st_cast(st_combine(xys),"POLYGON")
SDE_sf <- st_sf(SDE_sfc)
SDE_lyr <- tm_shape(SDE_sf) + tm_borders(col = "blue") +tm_layout(frame = F)
schools_lyr + SDE.center_lyr + SDE_lyr

#
6. Central Feature
school.CF <- calc_cf(id=1, points=School_df[,1:2])
CF.x<- school.CF$ATTRIBUTES$CF.x
CF.y<- school.CF$ATTRIBUTES$CF.y
CF.coor <- c(CF.x, CF.y)
CF_sf <- CF.coor %>% st_point %>% st_sfc %>% st_sf
st_crs(CF_sf) <- st_crs(schools_sf)
CF_lyr <- tm_shape(CF_sf) + tm_dots(fill = "blue", size= 0.5) +tm_layout(frame = F)
schools_lyr + CF_lyr

#
7. Mean Centers by Grouping Attribubtes
type <- schools_sf$type
newid <- unique(type)
schools_lyr2 <- tm_shape(schools_sf) +
tm_dots(col="type", palette=c(Cluster='red', Isolation='orange'), size= 0.3) +
tm_layout(frame = F)
── tmap v3 code detected ────────────────────────────────────────────────────────────────────
[v3->v4] `tm_tm_dots()`: migrate the argument(s) related to the scale of the visual variable
`fill` namely 'palette' (rename to 'values') to fill.scale = tm_scale(<HERE>).
schools_lyr2

xx<-vector(); yy<-vector(); ctype<-vector()
for (i in 1:2){
index<-(type == newid[i])
newschool<-schools_sf[index,]
xcoor<-newschool$X_coor
ycoor<-newschool$Y_coor
newschool.mc <- calc_mnc(id=1, points=cbind(xcoor, ycoor))
xx[i]<-newschool.mc$LOCATIONS[2]
yy[i]<-newschool.mc$LOCATIONS[3]
ctype[i]<-newid[i]
}
newcenterxy <- data.frame(xx,yy, ctype)
New_sf <- st_as_sf(newcenterxy , coords=c("xx","yy"))
st_crs(New_sf) <- st_crs(schools_sf)
New_lyr <- tm_shape(New_sf) +
tm_dots(col="ctype", palette=c(Cluster='blue', Isolation='cyan'), size= 0.5) +
tm_layout(frame = F)
── tmap v3 code detected ────────────────────────────────────────────────────────────────────
[v3->v4] `tm_tm_dots()`: migrate the argument(s) related to the scale of the visual variable
`fill` namely 'palette' (rename to 'values') to fill.scale = tm_scale(<HERE>).
schools_lyr2 + New_lyr
Multiple palettes called "blue" found: "kovesi.blue", "tableau.blue". The first one, "kovesi.blue", is returned.

LS0tDQp0aXRsZTogIlNwYXRpYWwgQW5hbHlzaXM6IDA1Ig0KYXV0aG9yOiAiVHphaS1IdW5nIFdlbiINCmRhdGU6ICcyMDI1LTAzLTMxJw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNg0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQojIDxoMz4gKipDb250ZW50cyoqIDwvaDM+DQoNCjxoNT4NCipVc2luZyBSIHBhY2thZ2U6YXNwYWNlKg0KDQoqKk1lYXN1cmVzIG9mIENlbnRyYWxpdHkqKiA8YnI+DQoxLihXZWlnaHRlZCkgTWVhbiBjZW50ZXIgIDxicj4NCjIuTWVkaWFuIGNlbnRlciA8YnI+DQozLkNlbnRyYWwgZmVhdHVyZSA8YnI+DQoNCioqTWVhc3VyZXMgb2YgRGlzcGVyc2lvbioqIDxicj4NCjEuU3RhbmRhcmQgRGlzdGFuY2UgPGJyPg0KMi5XZWlnaHRlZCBTdGQuIERpc3RhbmNlIDxicj4NCjMuU3RhbmRhcmQgRGV2aWF0aW9uYWwgRWxsaXBzZSA8YnI+IA0KDQo8L2g1Pg0KIA0KIyA8aDM+ICoqMC4gTGFvZGluZyBEYXRhKiogPC9oMz4NCg0KYGBge3IsIG1lc3NhZ2UgPSBGQUxTRX0NCnJtKGxpc3QgPSBscygpKQ0KbGlicmFyeShzZikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkoYXNwYWNlKSAjaW5zdGFsbC5wYWNrYWdlcygiYXNwYWNlIikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpzY2hvb2xzX3NmID0gc3RfcmVhZCgiLi9kYXRhL1NjaG9vbHMuc2hwIixvcHRpb25zPSJFTkNPRElORz1CSUc1IikNCmhlYWQoc2Nob29sc19zZikNCnNjaG9vbHNfbHlyIDwtIHRtX3NoYXBlKHNjaG9vbHNfc2YpK3RtX2RvdHMoY29sPSJyZWQiLCBzaXplPSAwLjEpICsgdG1fbGF5b3V0KGZyYW1lID0gRikNCg0KYGBgDQoNCiMgPGgzPiAqKjEuIERhdGEgUHJlcGFyYXRpb24qKiA8L2gzPg0KPGg1PiAoY3JlYXRpbmcgcmVxdWlyZWQgZmllbGRzKSAgPC9oNT4NCmBgYHtyfQ0KDQojIEdlbmVyYXRpbmcgbm8uIG9mIHN0dWRlbnQgaW4gZWFjaCBzY2hvb2wNCnNjaG9vbHNfc2YkU3R1ZGVudHM8LWFzLmludGVnZXIocnVuaWYoNDI0LDEwMDAsMTAwMDApKQ0KDQojIEdlbmVyYXRpbmcgc2Nob29sIHR5cGU6IGNsdXN0ZXIgdnMuIGlzb2xhdGlvbg0KZm9yIChpIGluIDE6IDQyNCkgew0KICANCiAgaWYgKHNjaG9vbHNfc2YkTkVBUl9ESVNUW2ldPCA1MDApIHsNCiAgICBzY2hvb2xzX3NmJHR5cGVbaV08LSAiQ2x1c3RlciINCiAgfSBlbHNlIHNjaG9vbHNfc2YkdHlwZVtpXTwtICJJc29sYXRpb24iDQogIA0KfQ0KDQppbmRleDwtIHNjaG9vbHNfc2YkdHlwZSA9PSAiQ2x1c3RlciINCnNjaG9vbF9jbHVzdGVyIDwtIHNjaG9vbHNfc2ZbaW5kZXgsXQ0KDQpsZW5ndGgoc2Nob29sX2NsdXN0ZXIpDQpzY2hvb2xzLmNfbHlyIDwtIHRtX3NoYXBlKHNjaG9vbF9jbHVzdGVyKSt0bV9kb3RzKGNvbD0icmVkIiwgc2l6ZT0gMC4xKSArIHRtX2xheW91dChmcmFtZSA9IEYpDQoNClNjaG9vbF9kZiA8LSBkYXRhLmZyYW1lKHg9c2Nob29sc19zZiRYX2Nvb3IsIHk9c2Nob29sc19zZiRZX2Nvb3IsIA0KICAgICAgICAgICAgICAgICAgICAgICB0eXBlPXNjaG9vbHNfc2YkdHlwZSwgc3R1ZGVudHM9c2Nob29sc19zZiRTdHVkZW50cykNCmBgYA0KDQojIDxoMz4gKioyLiBNZWFuIENlbnRlcioqIDwvaDM+DQpgYGB7cn0NCiMgY2FsY19tbmMoKSBNZWFuIENlbnRyZSBDYWxjdWxhdG9yDQpNZWFuLkNlbnRlciA8LSBjYWxjX21uYyhpZD0xLCB3ZWlnaHRlZD1GQUxTRSwgd2VpZ2h0cz1OVUxMLCBwb2ludHM9U2Nob29sX2RmWywxOjJdKQ0KVy5NZWFuLkNlbnRlciA8LSBjYWxjX21uYyhpZD0xLCB3ZWlnaHRlZD1UUlVFLCB3ZWlnaHRzPVNjaG9vbF9kZiRzdHVkZW50cywgcG9pbnRzPVNjaG9vbF9kZlssMToyXSkNCg0KTWVhbi54Y29vciA8LSBNZWFuLkNlbnRlciRMT0NBVElPTlNbMl0NCk1lYW4ueWNvb3IgPC0gTWVhbi5DZW50ZXIkTE9DQVRJT05TWzNdDQoNCk1lYW4uQ2VudGVyX3NmZyA9IHN0X3BvaW50KGMoTWVhbi54Y29vciwgTWVhbi55Y29vcikpDQpNZWFuLkNlbnRlcl9zZmMgPSBzdF9zZmMoTWVhbi5DZW50ZXJfc2ZnKQ0KTWVhbi5DZW50ZXJfc2YgPC0gc3Rfc2YoTWVhbi5DZW50ZXJfc2ZjKQ0KaGVhZChNZWFuLkNlbnRlcl9zZikgDQoNCiMgdXNpbmcgcGlwZSBjb2Rpbmcgc3R5bGUNCk1lYW4uQ2VudGVyLkNvb3IgPC0gYyhNZWFuLnhjb29yLCBNZWFuLnljb29yKQ0KTWVhbi5DZW50ZXJfc2YxIDwtIE1lYW4uQ2VudGVyLkNvb3IgJT4lIHN0X3BvaW50ICU+JSBzdF9zZmMgJT4lIHN0X3NmIA0KY2xhc3MoTWVhbi5DZW50ZXJfc2YxKQ0KDQpzdF9jcnMoTWVhbi5DZW50ZXJfc2YxKSA8LSBzdF9jcnMoc2Nob29sc19zZikNCk1lYW4uQ2VudGVyX2x5ciA8LSB0bV9zaGFwZShNZWFuLkNlbnRlcl9zZjEpK3RtX2RvdHMoZmlsbD0iYmx1ZSIsIHNpemU9MSkNCg0KIyBXZWlnaHRlZCBNZWFuIENlbnRlcg0KDQpXLk1lYW4ueGNvb3IgPC0gVy5NZWFuLkNlbnRlciRMT0NBVElPTlNbMl0NClcuTWVhbi55Y29vciA8LSBXLk1lYW4uQ2VudGVyJExPQ0FUSU9OU1szXQ0KDQpXLk1lYW4uQ2VudGVyLkNvb3IgPC0gYyhXLk1lYW4ueGNvb3IsIFcuTWVhbi55Y29vcikNClcuTWVhbi5DZW50ZXJfc2YgPC0gVy5NZWFuLkNlbnRlci5Db29yICU+JSBzdF9wb2ludCAlPiUgc3Rfc2ZjICU+JSBzdF9zZiANCnN0X2NycyhXLk1lYW4uQ2VudGVyX3NmKSA8LSBzdF9jcnMoc2Nob29sc19zZikNClcuTWVhbi5DZW50ZXJfbHlyIDwtIHRtX3NoYXBlKFcuTWVhbi5DZW50ZXJfc2YpK3RtX2RvdHMoZmlsbD0iZ3JlZW4iLCBzaXplPSAwLjUpDQoNCnNjaG9vbHNfbHlyICsgTWVhbi5DZW50ZXJfbHlyICsgVy5NZWFuLkNlbnRlcl9seXINCmBgYA0KDQoNCiMgPGgzPiAqKjMuIE1lZGlhbiBDZW50ZXIqKiA8L2gzPg0KYGBge3J9DQojIGNhbGNfbWRjKCk6IE1lZGlhbiBDZW50cmUgQ2FsY3VsYXRvcg0KTWVkaWFuLkNlbnRlciA8LSBjYWxjX21kYyhpZD0xLHBvaW50cz1TY2hvb2xfZGZbLDE6Ml0pDQoNCk1lZGlhbi54Y29vciA8LSBNZWRpYW4uQ2VudGVyJExPQ0FUSU9OU1syXQ0KTWVkaWFuLnljb29yIDwtIE1lZGlhbi5DZW50ZXIkTE9DQVRJT05TWzNdDQoNCk1lZGlhbi5DZW50ZXIuQ29vciA8LSBjKE1lZGlhbi54Y29vciwgTWVkaWFuLnljb29yKQ0KTWVkaWFuLkNlbnRlcl9zZiA8LSBNZWRpYW4uQ2VudGVyLkNvb3IgJT4lIHN0X3BvaW50ICU+JSBzdF9zZmMgJT4lIHN0X3NmIA0Kc3RfY3JzKE1lZGlhbi5DZW50ZXJfc2YpIDwtIHN0X2NycyhzY2hvb2xzX3NmKQ0KTWVkaWFuLkNlbnRlcl9seXIgPC0gdG1fc2hhcGUoTWVkaWFuLkNlbnRlcl9zZikrdG1fZG90cyhmaWxsID0gImdyZWVuIiwgc2l6ZT0gMC41KQ0KDQojIyMgQ29tcGFyZSBNZWFuIHZzLiBNZWRpYW4NCnNjaG9vbHNfbHlyICsgTWVhbi5DZW50ZXJfbHlyICsgTWVkaWFuLkNlbnRlcl9seXINCmBgYA0KDQoNCiMgPGgzPiAqKjQuIFNERDogU3RhbmRhcmQgRGlzdGFuY2UqKiA8L2gzPg0KPGg1Pip1c2luZyBwbG90X3NkZCo8L2g1Pg0KYGBge3J9DQojIGNhbGNfc2RkKCk6IENhbGN1bGF0ZSB0aGUgU3RhbmRhcmQgRGlzdGFuY2UgRGV2aWF0aW9uIChTdGFuZGFyZCBEaXN0YW5jZSkNCnNjaG9vbC5TREQgPC0gY2FsY19zZGQoaWQ9MSwgcG9pbnRzPVNjaG9vbF9kZlssMToyXSkNCnBsb3Rfc2RkKHNjaG9vbC5TREQsIGNlbnRyZS5jb2w9InJlZCIpDQoNCmBgYA0KPGg1Pip1c2luZyB0bWFwKjwvaDU+DQpgYGB7cn0NCmNlbnRlci54PC0gc2Nob29sLlNERCRBVFRSSUJVVEVTJENFTlRSRS54DQpjZW50ZXIueTwtIHNjaG9vbC5TREQkQVRUUklCVVRFUyRDRU5UUkUueQ0KDQpjZW50ZXIuY29vciA8LSBjKGNlbnRlci54LCBjZW50ZXIueSkNCmNlbnRlcl9zZjwtIGNlbnRlci5jb29yICU+JSBzdF9wb2ludCAlPiUgc3Rfc2ZjICU+JSBzdF9zZiANCg0Kc3RfY3JzKGNlbnRlcl9zZikgPC0gIHN0X2NycyhzY2hvb2xzX3NmKQ0KDQpyYWQ8LSBzY2hvb2wuU0REJEFUVFJJQlVURVMkU0RELnJhZGl1cw0KU0Rfc2Y8LSBzdF9idWZmZXIoY2VudGVyX3NmLCByYWQpDQpTRF9seXIgPC0gdG1fc2hhcGUoU0Rfc2YpICsgdG1fYm9yZGVycyhjb2wgPSAiYmx1ZSIpK3RtX2xheW91dChmcmFtZSA9IEYpDQpDZW50ZXJfbHlyIDwtICB0bV9zaGFwZShjZW50ZXJfc2YpICsgdG1fZG90cyhmaWxsPSJibHVlIiwgc2l6ZT0gMC41KQ0Kc2Nob29sc19seXIrIENlbnRlcl9seXIrIFNEX2x5ciANCg0KIyB3ZWlnaHRlZCBTREQNCnNjaG9vbC5TREQyIDwtIGNhbGNfc2RkKGlkPTEsIHBvaW50cyA9IFNjaG9vbF9kZlssMToyXSwgDQogICAgICAgICAgICAgICAgICAgICAgIHdlaWdodGVkID0gVFJVRSwgd2VpZ2h0cz1TY2hvb2xfZGYkc3R1ZGVudHMpDQpgYGANCg0KDQoNCiMgPGgzPiAqKjUuIFNERTpTdGFuZGFyZCBEZXZpYXRpb25hbCBFbGxpcHNlKiogPC9oMz4NCg0KPGg1Pip1c2luZyBwbG90X3NkZSo8L2g1Pg0KYGBge3J9DQojIyBTREUgQ2VudGVyOiBzZGVhdHQNCiMgY2FsY19zZGUoKSBDYWxjdWxhdGUgdGhlIFN0YW5kYXJkIERldmlhdGlvbiBFbGxpcHNlDQpzY2hvb2wuU0RFPC0gY2FsY19zZGUoaWQ9MSwgcG9pbnRzPVNjaG9vbF9kZlssMToyXSkNCnBsb3Rfc2RlKHNjaG9vbC5TREUpDQpgYGANCg0KPGg1Pip1c2luZyB0bWFwKjwvaDU+DQoNCmBgYHtyfQ0KIyMgQ3JlYXRpbmcgU0RFIGNlbnRlcg0KDQpTREUuY2VudGVyLng8LSBzY2hvb2wuU0RFJEFUVFJJQlVURVMkQ0VOVFJFLngNClNERS5jZW50ZXIueTwtIHNjaG9vbC5TREUkQVRUUklCVVRFUyRDRU5UUkUueQ0KU0RFLmNlbnRlci5jb29yIDwtIGMoU0RFLmNlbnRlci54LCBTREUuY2VudGVyLnkpDQpTREUuY2VudGVyX3NmIDwtIFNERS5jZW50ZXIuY29vciAlPiUgc3RfcG9pbnQgJT4lIHN0X3NmYyAlPiUgc3Rfc2YgDQpzdF9jcnMoU0RFLmNlbnRlcl9zZikgPC0gIHN0X2NycyhzY2hvb2xzX3NmKQ0KU0RFLmNlbnRlcl9seXIgPC0gdG1fc2hhcGUoU0RFLmNlbnRlcl9zZikgKyB0bV9kb3RzKGZpbGwgPSAiYmx1ZSIsIHNpemU9MC41KSt0bV9sYXlvdXQoZnJhbWUgPSBGKQ0KDQojIyBDcmVhdGluZyBTREUgUG9seWdvbg0KDQp4Y29vciA8LSBzY2hvb2wuU0RFJExPQ0FUSU9OUyR4DQp5Y29vciA8LSBzY2hvb2wuU0RFJExPQ0FUSU9OUyR5DQp4eSA9IGRhdGEuZnJhbWUoeD14Y29vciwgeT15Y29vcikNCnh5cyA9IHN0X2FzX3NmKHh5LCBjb29yZHM9YygieCIsInkiKSkNCnN0X2Nycyh4eXMpPC1zdF9jcnMoc2Nob29sc19zZikNCg0KU0RFX3NmYyA8LSBzdF9jYXN0KHN0X2NvbWJpbmUoeHlzKSwiUE9MWUdPTiIpDQpTREVfc2YgPC0gc3Rfc2YoU0RFX3NmYykNClNERV9seXIgPC0gdG1fc2hhcGUoU0RFX3NmKSArIHRtX2JvcmRlcnMoY29sID0gImJsdWUiKSArdG1fbGF5b3V0KGZyYW1lID0gRikNCg0Kc2Nob29sc19seXIgKyBTREUuY2VudGVyX2x5ciArIFNERV9seXINCmBgYA0KDQojIDxoMz4gKio2LiBDZW50cmFsIEZlYXR1cmUqKiA8L2gzPg0KYGBge3J9DQpzY2hvb2wuQ0YgPC0gY2FsY19jZihpZD0xLCBwb2ludHM9U2Nob29sX2RmWywxOjJdKQ0KDQpDRi54PC0gc2Nob29sLkNGJEFUVFJJQlVURVMkQ0YueA0KQ0YueTwtIHNjaG9vbC5DRiRBVFRSSUJVVEVTJENGLnkNCkNGLmNvb3IgPC0gYyhDRi54LCBDRi55KQ0KQ0Zfc2YgPC0gQ0YuY29vciAlPiUgc3RfcG9pbnQgJT4lIHN0X3NmYyAlPiUgc3Rfc2YgDQpzdF9jcnMoQ0Zfc2YpIDwtICBzdF9jcnMoc2Nob29sc19zZikNCkNGX2x5ciA8LSB0bV9zaGFwZShDRl9zZikgKyB0bV9kb3RzKGZpbGwgPSAiYmx1ZSIsIHNpemU9IDAuNSkgK3RtX2xheW91dChmcmFtZSA9IEYpDQpzY2hvb2xzX2x5ciArIENGX2x5cg0KYGBgDQoNCg0KIyA8aDM+ICoqNy4gTWVhbiBDZW50ZXJzIGJ5IEdyb3VwaW5nIEF0dHJpYnVidGVzKiogPC9oMz4NCmBgYHtyfQ0KdHlwZSA8LSBzY2hvb2xzX3NmJHR5cGUNCm5ld2lkIDwtIHVuaXF1ZSh0eXBlKQ0KDQpzY2hvb2xzX2x5cjIgPC0gdG1fc2hhcGUoc2Nob29sc19zZikgKyANCiAgICAgICAgICAgICAgICB0bV9kb3RzKGNvbD0idHlwZSIsIHBhbGV0dGU9YyhDbHVzdGVyPSdyZWQnLCBJc29sYXRpb249J29yYW5nZScpLCBzaXplPSAwLjMpICsNCiAgICAgICAgICAgICAgICB0bV9sYXlvdXQoZnJhbWUgPSBGKQ0KDQpzY2hvb2xzX2x5cjINCg0KDQp4eDwtdmVjdG9yKCk7IHl5PC12ZWN0b3IoKTsgY3R5cGU8LXZlY3RvcigpDQoNCmZvciAoaSBpbiAxOjIpew0KICBpbmRleDwtKHR5cGUgPT0gbmV3aWRbaV0pDQogIG5ld3NjaG9vbDwtc2Nob29sc19zZltpbmRleCxdDQogIHhjb29yPC1uZXdzY2hvb2wkWF9jb29yDQogIHljb29yPC1uZXdzY2hvb2wkWV9jb29yDQogIG5ld3NjaG9vbC5tYyA8LSBjYWxjX21uYyhpZD0xLCBwb2ludHM9Y2JpbmQoeGNvb3IsIHljb29yKSkNCiAgDQogIHh4W2ldPC1uZXdzY2hvb2wubWMkTE9DQVRJT05TWzJdDQogIHl5W2ldPC1uZXdzY2hvb2wubWMkTE9DQVRJT05TWzNdDQogIGN0eXBlW2ldPC1uZXdpZFtpXQ0KfQ0KDQpuZXdjZW50ZXJ4eSA8LSBkYXRhLmZyYW1lKHh4LHl5LCBjdHlwZSkNCk5ld19zZiA8LSBzdF9hc19zZihuZXdjZW50ZXJ4eSAsIGNvb3Jkcz1jKCJ4eCIsInl5IikpDQpzdF9jcnMoTmV3X3NmKSA8LSAgc3RfY3JzKHNjaG9vbHNfc2YpDQoNCk5ld19seXIgPC0gdG1fc2hhcGUoTmV3X3NmKSArIA0KICAgICAgICAgICB0bV9kb3RzKGNvbD0iY3R5cGUiLCBwYWxldHRlPWMoQ2x1c3Rlcj0nYmx1ZScsIElzb2xhdGlvbj0nY3lhbicpLCBzaXplPSAwLjUpICsNCiAgICAgICAgICAgdG1fbGF5b3V0KGZyYW1lID0gRikgDQoNCg0Kc2Nob29sc19seXIyICsgTmV3X2x5ciANCmBgYA0KDQo=