Reading Data

rm(list = ls())

# The Taipei data provided in class
tpe_vill_sf = st_read("C:/113-2_Spring/Spatial_Analysis/coursefiles/Taipei_Vill/Taipei_Vill.shp", options = "ENCODING=big5")
options:        ENCODING=big5 
Reading layer `Taipei_Vill' from data source 
  `C:\113-2_Spring\Spatial_Analysis\coursefiles\Taipei_Vill\Taipei_Vill.shp' using driver `ESRI Shapefile'
Simple feature collection with 456 features and 8 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 296094.4 ymin: 2761518 xmax: 317198.9 ymax: 2789180
Projected CRS: TWD97 / TM2 zone 121
# Population data
tpe_pop_df = read.csv("C:/113-2_Spring/Spatial_Analysis/final_project/0608_Plotting/taipei_vill_pop.csv", fileEncoding = "big5")

# All schools in Taiwan
schools <- read.csv("C:/113-2_Spring/Spatial_Analysis/final_project/0608_Plotting/school_112_csv.csv",
                     fileEncoding = "BIG5")

## Select those whose coordinate is not missing
schools_clean <- schools %>%
  filter(!is.na(`X.坐標`) & !is.na(`Y.坐標`))

## Turning into sf
schools_sf <- st_as_sf(schools_clean,
                       coords = c("X.坐標", "Y.坐標"),
                       crs = 3826)  # EPSG:3826 是 TWD97 二度分帶

# Sportfield Data
sportfield_sf = st_read("C:/113-2_Spring/Spatial_Analysis/final_project/0608_Plotting/sportfield_taipei/sportfield_taipei.shp")
Reading layer `sportfield_taipei' from data source 
  `C:\113-2_Spring\Spatial_Analysis\final_project\0608_Plotting\sportfield_taipei\sportfield_taipei.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 1359 features and 51 fields
Geometry type: POLYGON
Dimension:     XY
Bounding box:  xmin: 121.4618 ymin: 24.97106 xmax: 121.6198 ymax: 25.1691
Geodetic CRS:  WGS 84
# Gaming places data
gaming_sf = st_read("C:/113-2_Spring/Spatial_Analysis/final_project/0608_Plotting/Gaming_center/Gaming_center.shp")
Reading layer `Gaming_center' from data source 
  `C:\113-2_Spring\Spatial_Analysis\final_project\0608_Plotting\Gaming_center\Gaming_center.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 105 features and 43 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 121.4959 ymin: 24.98938 xmax: 121.6208 ymax: 25.1241
Geodetic CRS:  WGS 84
# Hospital data
hospital_sf = st_read("C:/113-2_Spring/Spatial_Analysis/final_project/0608_Plotting/Hospital_taipei/Hospital_taipei.shp")
Reading layer `Hospital_taipei' from data source 
  `C:\113-2_Spring\Spatial_Analysis\final_project\0608_Plotting\Hospital_taipei\Hospital_taipei.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 876 features and 117 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 121.493 ymin: 24.98035 xmax: 121.6211 ymax: 25.1385
Geodetic CRS:  WGS 84
# fastfood data
fastfood_sf = st_read("C:/113-2_Spring/Spatial_Analysis/final_project/0608_Plotting/Tpe_Fastfood/Tpe_Fastfood.shp")
Reading layer `Tpe_Fastfood' from data source 
  `C:\113-2_Spring\Spatial_Analysis\final_project\0608_Plotting\Tpe_Fastfood\Tpe_Fastfood.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 98 features and 8 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 297198.9 ymin: 2763885 xmax: 312205.7 ymax: 2781148
Projected CRS: TWD97 / TM2 zone 121
# park data
park_sf = st_read("C:/113-2_Spring/Spatial_Analysis/final_project/0608_Plotting/Park_centroids/Park_centroids.shp")
Reading layer `Park_centroids' from data source 
  `C:\113-2_Spring\Spatial_Analysis\final_project\0608_Plotting\Park_centroids\Park_centroids.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 3115 features and 169 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 121.4622 ymin: 24.96698 xmax: 121.6249 ymax: 25.18077
Geodetic CRS:  WGS 84
# Selecting population data
tpe_pop_df = tpe_pop_df[tpe_pop_df$性別 == "計" & tpe_pop_df$區域代碼 > 100000000 & tpe_pop_df$年份 == 113 & tpe_pop_df$月份 == 2,  ]
tpe_vill_sf$區域代碼 = as.numeric(tpe_vill_sf$TOWN_ID) * 1000 + as.numeric(tpe_vill_sf$VILLAGE_ID)

Calculation

# Calculations on population 
tpe_pop_df$age_1_6 = rowSums(tpe_pop_df[8:13])
tpe_pop_df$age_7_12 = rowSums(tpe_pop_df[14:19])
tpe_pop_df$adult = rowSums(tpe_pop_df[26:71])

# primary_ratio and secondary ratio
tpe_pop_df$primary_ratio = tpe_pop_df$age_1_6 / tpe_pop_df$adult
tpe_pop_df$secondary_ratio = tpe_pop_df$age_7_12 / tpe_pop_df$adult

tpe_vill_sf = merge(tpe_vill_sf, tpe_pop_df, by = "區域代碼")

Selecting Schools

# 選取臺北的國中
schools_sf = schools_sf %>%
  filter(學校級別 %in% c("國民中學", "附設國民中學")) %>%
  filter(縣市別 == "臺北市")

# 選取「北市公立五虎 + 景美」
five_tigers_sf = schools_sf %>%
  filter(學校名稱 %in% c("市立中正國中", "市立龍門國中", "市立敦化國中", "市立介壽國中", "市立金華國中", "市立景美國中"))

# 選取其他建北率高的學校
other_top15_sf = schools_sf %>%
  filter(學校名稱 %in% c("國立師大附中附設國中", "私立靜心高中附設國中", "私立延平中學附設國中", "市立大安國中", "市立麗山國中","市立南門國中", "市立天母國中", "市立興雅國中", "市立石牌國中"))

# 建立簡化名稱欄位
five_tigers_sf$school_short <- recode(five_tigers_sf$學校名稱,
                                      "市立中正國中" = "中正",
                                      "市立龍門國中" = "龍門",
                                      "市立敦化國中" = "敦化",
                                      "市立介壽國中" = "介壽",
                                      "市立金華國中" = "金華",
                                      "市立景美國中" = "景美")

LISA of primary ratio (1~6)

nb = poly2nb(tpe_vill_sf, queen = FALSE) 
listw = nb2listw(nb, style = "W", zero.policy = TRUE)
density = as.vector(tpe_vill_sf$primary_ratio)
lisa = localmoran(density, listw = listw, zero.policy = TRUE)

tpe_vill_sf$local_I_pri = lisa[, 1]
tpe_vill_sf$z_score_pri = lisa[, 4]
tpe_vill_sf$p_value_pri = lisa[, 5]
quadrant = attr(lisa, "quadr")$mean
quadrant = factor(quadrant, levels = c(levels(quadrant), "NoSig"))
signif_level_05 = 0.05
quadrant[lisa[, 5] > signif_level_05] = "NoSig"
tpe_vill_sf$LISA_type_pri = quadrant

lisa_colors <- c(
  'High-High' = 'red',
  'Low-Low' = 'blue',
  'High-Low' = 'pink',
  'Low-High' = 'skyblue2',
  'NoSig' = 'grey'
)

tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_pri",
              palette = lisa_colors,
              title = "LISA Cluster Type (age 1~6 / adult)") +
  tm_layout(title = "LISA Map for Age 1~6 / Adult", legend.outside = TRUE)

LISA of secondary ratio (7~12)

nb = poly2nb(tpe_vill_sf, queen = FALSE) 
listw = nb2listw(nb, style = "W", zero.policy = TRUE)
density = as.vector(tpe_vill_sf$secondary_ratio)
lisa = localmoran(density, listw = listw, zero.policy = TRUE)

tpe_vill_sf$local_I_sec = lisa[, 1]
tpe_vill_sf$z_score_sec = lisa[, 4]
tpe_vill_sf$p_value_sec = lisa[, 5]
quadrant = attr(lisa, "quadr")$mean
quadrant = factor(quadrant, levels = c(levels(quadrant), "NoSig"))
signif_level = 0.05
quadrant[lisa[, 5] > signif_level] = "NoSig"
tpe_vill_sf$LISA_type_sec = quadrant

tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_sec",
              palette = lisa_colors,
              title = "LISA Cluster Type (age 7~12 / adult)") +
  tm_layout(title = "LISA Map for Age 7~12 / Adult", legend.outside = TRUE)

density_sec = as.vector(tpe_vill_sf$secondary_ratio)
lisa_sec = localmoran(density_sec, listw = listw, zero.policy = TRUE)

tpe_vill_sf$local_I_sec = lisa_sec[, 1]
tpe_vill_sf$z_score_sec = lisa_sec[, 4]
tpe_vill_sf$p_value_sec = lisa_sec[, 5]

quadrant_sec = attr(lisa_sec, "quadr")$mean
quadrant_sec = factor(quadrant_sec, levels = c(levels(quadrant_sec), "NoSig"))

# 使用 FDR 校正
quadrant_sec[p.adjust(lisa_sec[, 5], method = "fdr") > signif_level] = "NoSig"
tpe_vill_sf$LISA_type_sec = quadrant_sec

# 繪圖(含學校點位)
tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_sec",
              palette = lisa_colors,
              title = "LISA Cluster Type (age 7~12 / adult)") +
  tm_layout(title = "LISA Map for Age 7~12 / Adult", legend.outside = TRUE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_tm_polygons()`: migrate the argument(s) related to the scale of the visual variable
`fill` namely 'palette' (rename to 'values') to fill.scale = tm_scale(<HERE>).[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the visual variable `fill`
namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`

Displaying the five tigers on the FDRd map

names(grDevices::windowsFonts())  # Windows 專用
[1] "serif"        "sans"         "mono"         "Noto Sans TC"
# 手動為每個學校加上偏移值(你可以依照實際需求微調)
five_tigers_sf <- five_tigers_sf %>%
  mutate(
    xmod = c(0.3, -0.5, 0, 1, -1, 1),   # X 偏移
    ymod = c(0.8, 0.8, 0.8, 0.8, 0.8, 0.8) # Y 偏移
  )

tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_sec",
              palette = lisa_colors,
              title = "LISA Cluster Type (age 7~12 / adult)") +
  
  # 其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +
  
  # 公立五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +
  
  # 主文字
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod",
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +
  
  tm_layout(title = "LISA Map for Age 7~12 / Adult",
            legend.outside = TRUE)

Environment

Gaming Center

# Setting sig_level
signif_gaming = 0.01

gaming_sf <- st_transform(gaming_sf, st_crs(tpe_vill_sf))

joined_gaming <- st_join(gaming_sf, tpe_vill_sf, join = st_within)

gaming_counts <- joined_gaming %>%
  group_by(TOWN_ID) %>%
  summarise(gaming_count = n())

gaming_counts_df <- st_drop_geometry(gaming_counts)

tpe_vill_sf$gaming_count <- gaming_counts_df$gaming_count[match(tpe_vill_sf$TOWN_ID, gaming_counts_df$TOWN_ID)]
tpe_vill_sf$gaming_count[is.na(tpe_vill_sf$gaming_count)] <- 0

tpe_vill_sf$AREA <- set_units(st_area(tpe_vill_sf), "km^2")
tpe_vill_sf$gaming_density <- tpe_vill_sf$gaming_count / tpe_vill_sf$AREA

coords <- st_coordinates(st_centroid(tpe_vill_sf))

nb <- dnearneigh(coords, d1 = 0, d2 = 10000)

dist_list <- nbdists(nb, coords)

inv_dist_list <- lapply(dist_list, function(x) {
  y <- 1 / (x + 0.0001)
  return(y)
})

listw <- nb2listw(nb, glist = inv_dist_list, style = "W", zero.policy = TRUE)

density <- as.vector(tpe_vill_sf$gaming_density)

global_moran <- moran.test(density, listw = listw, zero.policy = TRUE)
print(global_moran)

    Moran I test under randomisation

data:  density  
weights: listw    

Moran I statistic standard deviate = 46.138, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     1.775105e-01     -2.197802e-03      1.517092e-05 
lisa <- localmoran(density, listw = listw, zero.policy = TRUE)

tpe_vill_sf$local_I <- lisa[, 1]
tpe_vill_sf$z_score <- lisa[, 4]
tpe_vill_sf$p_value <- lisa[, 5]

quadrant <- attr(lisa, "quadr")$mean
quadrant <- factor(quadrant, levels = c(levels(quadrant), "NoSig"))

tpe_vill_sf$pvalue_FDR <- p.adjust(lisa[, 5], method = "fdr")

tpe_vill_sf$LISA_type_Gaming <- "NoSig"
tpe_vill_sf$LISA_type_Gaming[tpe_vill_sf$pvalue_FDR < signif_gaming & tpe_vill_sf$local_I > 0 & quadrant == "High-High"] <- "High-High"
tpe_vill_sf$LISA_type_Gaming[tpe_vill_sf$pvalue_FDR < signif_gaming & tpe_vill_sf$local_I > 0 & quadrant == "Low-Low"] <- "Low-Low"
tpe_vill_sf$LISA_type_Gaming[tpe_vill_sf$pvalue_FDR < signif_gaming & tpe_vill_sf$local_I < 0 & quadrant == "High-Low"] <- "High-Low"
tpe_vill_sf$LISA_type_Gaming[tpe_vill_sf$pvalue_FDR < signif_gaming & tpe_vill_sf$local_I < 0 & quadrant == "Low-High"] <- "Low-High"

tpe_vill_sf$LISA_type_Gaming <- factor(tpe_vill_sf$LISA_type_Gaming,
  levels = c("High-High", "Low-Low", "High-Low", "Low-High", "NoSig"))

lisa_colors <- c(
  'High-High' = 'red',
  'Low-Low' = 'blue',
  'High-Low' = 'pink',
  'Low-High' = 'skyblue2',
  'NoSig' = 'grey'
)

tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_Gaming",
              palette = lisa_colors,
              title = "LISA Cluster Type (Sport Facilities)") +
  
  # 其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +
  
  # 五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +
  
  # 主文字
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod",
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +
  
  tm_layout(title = "LISA Map (Inverse Distance) - Gaming Centers",
            legend.outside = TRUE)

fastfood

# 設定信心水準
signif_fastfood <- 0.05

# 投影一致
fastfood_sf <- st_transform(fastfood_sf, st_crs(tpe_vill_sf))

# 空間連接:找出每個村里有幾個 fast food 點
joined_fastfood <- st_join(fastfood_sf, tpe_vill_sf, join = st_within)

fastfood_counts <- joined_fastfood %>%
  group_by(TOWN_ID) %>%
  summarise(fastfood_count = n())

fastfood_counts_df <- st_drop_geometry(fastfood_counts)

tpe_vill_sf$fastfood_count <- fastfood_counts_df$fastfood_count[match(tpe_vill_sf$TOWN_ID, fastfood_counts_df$TOWN_ID)]
tpe_vill_sf$fastfood_count[is.na(tpe_vill_sf$fastfood_count)] <- 0

# 面積與密度
tpe_vill_sf$AREA <- set_units(st_area(tpe_vill_sf), "km^2")
tpe_vill_sf$fastfood_density <- tpe_vill_sf$fastfood_count / tpe_vill_sf$AREA

# 鄰近矩陣設定
coords <- st_coordinates(st_centroid(tpe_vill_sf))
警告: st_centroid assumes attributes are constant over geometries
nb <- dnearneigh(coords, d1 = 0, d2 = 10000)

dist_list <- nbdists(nb, coords)
inv_dist_list <- lapply(dist_list, function(x) 1 / (x + 0.0001))

listw <- nb2listw(nb, glist = inv_dist_list, style = "W", zero.policy = TRUE)

# 執行 LISA
density <- as.vector(tpe_vill_sf$fastfood_density)

global_moran <- moran.test(density, listw = listw, zero.policy = TRUE)
print(global_moran)

    Moran I test under randomisation

data:  density  
weights: listw    

Moran I statistic standard deviate = 39.132, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     1.529806e-01     -2.197802e-03      1.572547e-05 
lisa <- localmoran(density, listw = listw, zero.policy = TRUE)

tpe_vill_sf$local_I <- lisa[, 1]
tpe_vill_sf$z_score <- lisa[, 4]
tpe_vill_sf$p_value <- lisa[, 5]

quadrant <- attr(lisa, "quadr")$mean
quadrant <- factor(quadrant, levels = c(levels(quadrant), "NoSig"))

tpe_vill_sf$pvalue_FDR <- p.adjust(lisa[, 5], method = "fdr")

tpe_vill_sf$LISA_type_Fastfood <- "NoSig"
tpe_vill_sf$LISA_type_Fastfood[tpe_vill_sf$pvalue_FDR < signif_fastfood & tpe_vill_sf$local_I > 0 & quadrant == "High-High"] <- "High-High"
tpe_vill_sf$LISA_type_Fastfood[tpe_vill_sf$pvalue_FDR < signif_fastfood & tpe_vill_sf$local_I > 0 & quadrant == "Low-Low"] <- "Low-Low"
tpe_vill_sf$LISA_type_Fastfood[tpe_vill_sf$pvalue_FDR < signif_fastfood & tpe_vill_sf$local_I < 0 & quadrant == "High-Low"] <- "High-Low"
tpe_vill_sf$LISA_type_Fastfood[tpe_vill_sf$pvalue_FDR < signif_fastfood & tpe_vill_sf$local_I < 0 & quadrant == "Low-High"] <- "Low-High"

tpe_vill_sf$LISA_type_Fastfood <- factor(tpe_vill_sf$LISA_type_Fastfood,
                                         levels = c("High-High", "Low-Low", "High-Low", "Low-High", "NoSig"))

# 配色
lisa_colors <- c(
  'High-High' = 'red',
  'Low-Low' = 'blue',
  'High-Low' = 'pink',
  'Low-High' = 'skyblue2',
  'NoSig' = 'grey'
)

# 畫出地圖
tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_Fastfood",
              palette = lisa_colors,
              title = "LISA Cluster Type (Fast Food)") +
  
  # 其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +
  
  # 五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +
  
  # 標上學校名稱
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod",
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +
  
  tm_layout(title = "LISA Map (Inverse Distance) - Fast Food",
            legend.outside = TRUE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_tm_polygons()`: migrate the argument(s) related to the scale of the visual variable
`fill` namely 'palette' (rename to 'values') to fill.scale = tm_scale(<HERE>).[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the visual variable `fill`
namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`

intersecting

# 建立交集標記欄位
tpe_vill_sf$HH_Intersect <- ifelse(
  tpe_vill_sf$LISA_type_Fastfood == "High-High" & 
    tpe_vill_sf$LISA_type_Gaming == "High-High", 
  "High-High Both", 
  "Other"
)

# 設定顏色
intersect_colors <- c("High-High Both" = "purple", "Other" = "lightgrey")

# 畫出地圖
tm_shape(tpe_vill_sf) +
  tm_polygons("HH_Intersect", 
              palette = intersect_colors, 
              title = "High-High Intersection") +
  
  # 其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +
  
  # 五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +
  
  # 標上學校名稱
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod",
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +

  tm_layout(title = "Intersection of High-High Clusters: Fast Food & Gaming Centers",
            legend.outside = TRUE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_tm_polygons()`: migrate the argument(s) related to the scale of the visual variable
`fill` namely 'palette' (rename to 'values') to fill.scale = tm_scale(<HERE>).[v3->v4] `tm_polygons()`: migrate the argument(s) related to the legend of the visual variable `fill`
namely 'title' to 'fill.legend = tm_legend(<HERE>)'[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`

sportfield

sportfield_sf <- st_transform(sportfield_sf, st_crs(tpe_vill_sf))

joined_sportfield <- st_join(sportfield_sf, tpe_vill_sf, join = st_within)

sportfield_counts <- joined_sportfield %>%
  group_by(TOWN_ID) %>%
  summarise(sportfield_count = n())

sportfield_counts_df <- st_drop_geometry(sportfield_counts)

tpe_vill_sf$sportfield_count <- sportfield_counts_df$sportfield_count[match(tpe_vill_sf$TOWN_ID, sportfield_counts_df$TOWN_ID)]
tpe_vill_sf$sportfield_count[is.na(tpe_vill_sf$sportfield_count)] <- 0

tpe_vill_sf$AREA <- set_units(st_area(tpe_vill_sf), "km^2")
tpe_vill_sf$sportfield_density <- tpe_vill_sf$sportfield_count / tpe_vill_sf$AREA

coords <- st_coordinates(st_centroid(tpe_vill_sf))

# 10km
nb <- dnearneigh(coords, d1 = 0, d2 = 10000)

dist_list <- nbdists(nb, coords)

inv_dist_list <- lapply(dist_list, function(x) {
  y <- 1 / (x + 0.0001)
  return(y)
})

listw <- nb2listw(nb, glist = inv_dist_list, style = "W", zero.policy = TRUE)

density <- as.vector(tpe_vill_sf$sportfield_density)

global_moran <- moran.test(density, listw = listw, zero.policy = TRUE)
print(global_moran)

    Moran I test under randomisation

data:  density  
weights: listw    

Moran I statistic standard deviate = 22.131, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     8.543080e-02     -2.197802e-03      1.567804e-05 
lisa <- localmoran(density, listw = listw, zero.policy = TRUE)

tpe_vill_sf$local_I <- lisa[, 1]
tpe_vill_sf$z_score <- lisa[, 4]
tpe_vill_sf$p_value <- lisa[, 5]

quadrant <- attr(lisa, "quadr")$mean
quadrant <- factor(quadrant, levels = c(levels(quadrant), "NoSig"))

tpe_vill_sf$pvalue_FDR <- p.adjust(lisa[, 5], method = "fdr")

# Setting sig_level
signif_sportfield = 0.01

tpe_vill_sf$LISA_type_sportfield <- "NoSig"
tpe_vill_sf$LISA_type_sportfield[tpe_vill_sf$pvalue_FDR < signif_sportfield & tpe_vill_sf$local_I > 0 & quadrant == "High-High"] <- "High-High"
tpe_vill_sf$LISA_type_sportfield[tpe_vill_sf$pvalue_FDR < signif_sportfield & tpe_vill_sf$local_I > 0 & quadrant == "Low-Low"] <- "Low-Low"
tpe_vill_sf$LISA_type_sportfield[tpe_vill_sf$pvalue_FDR < signif_sportfield & tpe_vill_sf$local_I < 0 & quadrant == "High-Low"] <- "High-Low"
tpe_vill_sf$LISA_type_sportfield[tpe_vill_sf$pvalue_FDR < signif_sportfield & tpe_vill_sf$local_I < 0 & quadrant == "Low-High"] <- "Low-High"

tpe_vill_sf$LISA_type_sportfield <- factor(tpe_vill_sf$LISA_type_sportfield,
  levels = c("High-High", "Low-Low", "High-Low", "Low-High", "NoSig"))

lisa_colors <- c(
  'High-High' = 'red',
  'Low-Low' = 'blue',
  'High-Low' = 'pink',
  'Low-High' = 'skyblue2',
  'NoSig' = 'grey'
)

tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_sportfield",
              palette = lisa_colors,
              title = "LISA Cluster Type (Sport Facilities)") +
  
  # 其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +
  
  # 五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +
  
  # 主文字
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod",
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +
  
  tm_layout(title = "LISA Map (Inverse Distance) - Sport Facilities",
            legend.outside = TRUE)

Hospital

# 設定信心水準
signif_hospital <- 0.01

# 確保 hospital 資料與行政區使用同樣 CRS
hospital_sf <- st_transform(hospital_sf, st_crs(tpe_vill_sf))

# 加入行政區
joined_hospital <- st_join(hospital_sf, tpe_vill_sf, join = st_within)

# 每個行政區的醫療點數量
hospital_counts <- joined_hospital %>%
  group_by(TOWN_ID) %>%
  summarise(hospital_count = n())

# 移除 geometry
hospital_counts_df <- st_drop_geometry(hospital_counts)

# 對應進原本的行政區圖層
tpe_vill_sf$hospital_count <- hospital_counts_df$hospital_count[match(tpe_vill_sf$TOWN_ID, hospital_counts_df$TOWN_ID)]
tpe_vill_sf$hospital_count[is.na(tpe_vill_sf$hospital_count)] <- 0

# 計算密度(設施數 / 區域面積)
tpe_vill_sf$AREA <- set_units(st_area(tpe_vill_sf), "km^2")
tpe_vill_sf$hospital_density <- tpe_vill_sf$hospital_count / tpe_vill_sf$AREA

# 計算鄰近矩陣
coords <- st_coordinates(st_centroid(tpe_vill_sf))
nb <- dnearneigh(coords, d1 = 0, d2 = 10000)

dist_list <- nbdists(nb, coords)

inv_dist_list <- lapply(dist_list, function(x) {
  1 / (x + 0.0001)
})

listw <- nb2listw(nb, glist = inv_dist_list, style = "W", zero.policy = TRUE)

# LISA 分析
density <- as.vector(tpe_vill_sf$hospital_density)

global_moran <- moran.test(density, listw = listw, zero.policy = TRUE)
print(global_moran)

    Moran I test under randomisation

data:  density  
weights: listw    

Moran I statistic standard deviate = 40.53, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     1.585062e-01     -2.197802e-03      1.572152e-05 
lisa <- localmoran(density, listw = listw, zero.policy = TRUE)

tpe_vill_sf$local_I <- lisa[, 1]
tpe_vill_sf$z_score <- lisa[, 4]
tpe_vill_sf$p_value <- lisa[, 5]

quadrant <- attr(lisa, "quadr")$mean
quadrant <- factor(quadrant, levels = c(levels(quadrant), "NoSig"))

tpe_vill_sf$pvalue_FDR <- p.adjust(lisa[, 5], method = "fdr")

# 指定類型
tpe_vill_sf$LISA_type_Hospital <- "NoSig"
tpe_vill_sf$LISA_type_Hospital[tpe_vill_sf$pvalue_FDR < signif_hospital & tpe_vill_sf$local_I > 0 & quadrant == "High-High"] <- "High-High"
tpe_vill_sf$LISA_type_Hospital[tpe_vill_sf$pvalue_FDR < signif_hospital & tpe_vill_sf$local_I > 0 & quadrant == "Low-Low"] <- "Low-Low"
tpe_vill_sf$LISA_type_Hospital[tpe_vill_sf$pvalue_FDR < signif_hospital & tpe_vill_sf$local_I < 0 & quadrant == "High-Low"] <- "High-Low"
tpe_vill_sf$LISA_type_Hospital[tpe_vill_sf$pvalue_FDR < signif_hospital & tpe_vill_sf$local_I < 0 & quadrant == "Low-High"] <- "Low-High"

tpe_vill_sf$LISA_type_Hospital <- factor(tpe_vill_sf$LISA_type_Hospital,
                                         levels = c("High-High", "Low-Low", "High-Low", "Low-High", "NoSig"))

# 配色
lisa_colors <- c(
  'High-High' = 'red',
  'Low-Low' = 'blue',
  'High-Low' = 'pink',
  'Low-High' = 'skyblue2',
  'NoSig' = 'grey'
)

# 畫圖
tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_Hospital",
              palette = lisa_colors,
              title = "LISA Cluster Type (Hospitals)") +
  
  # 其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +
  
  # 五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +
  
  # 主文字
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod",
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +
  
  tm_layout(title = "LISA Map (Inverse Distance) - Hospitals",
            legend.outside = TRUE)

Park

# 1. 設定信心水準
signif_park <- 0.01

# 2. 座標轉換
park_sf <- st_transform(park_sf, st_crs(tpe_vill_sf))

# 3. Spatial join 與計數
joined_park <- st_join(park_sf, tpe_vill_sf, join = st_within)

park_counts <- joined_park %>%
  group_by(TOWN_ID) %>%
  summarise(park_count = n())

park_counts_df <- st_drop_geometry(park_counts)

# 4. 加入計數與密度欄位
tpe_vill_sf$park_count <- park_counts_df$park_count[match(tpe_vill_sf$TOWN_ID, park_counts_df$TOWN_ID)]
tpe_vill_sf$park_count[is.na(tpe_vill_sf$park_count)] <- 0

tpe_vill_sf$AREA <- set_units(st_area(tpe_vill_sf), "km^2")
tpe_vill_sf$park_density <- tpe_vill_sf$park_count / tpe_vill_sf$AREA

# 5. 計算鄰近矩陣與空間權重
coords <- st_coordinates(st_centroid(tpe_vill_sf))

nb <- dnearneigh(coords, d1 = 0, d2 = 10000)
dist_list <- nbdists(nb, coords)

inv_dist_list <- lapply(dist_list, function(x) {
  1 / (x + 0.0001)
})

listw <- nb2listw(nb, glist = inv_dist_list, style = "W", zero.policy = TRUE)

# 6. Global & Local Moran's I
density <- as.vector(tpe_vill_sf$park_density)

global_moran <- moran.test(density, listw = listw, zero.policy = TRUE)
print(global_moran)

    Moran I test under randomisation

data:  density  
weights: listw    

Moran I statistic standard deviate = 52.42, p-value < 2.2e-16
alternative hypothesis: greater
sample estimates:
Moran I statistic       Expectation          Variance 
     2.048055e-01     -2.197802e-03      1.559429e-05 
lisa <- localmoran(density, listw = listw, zero.policy = TRUE)

# 7. 加入 LISA 指標
tpe_vill_sf$local_I_park <- lisa[, 1]
tpe_vill_sf$z_score_park <- lisa[, 4]
tpe_vill_sf$p_value_park <- lisa[, 5]

quadrant_park <- attr(lisa, "quadr")$mean
quadrant_park <- factor(quadrant_park, levels = c(levels(quadrant_park), "NoSig"))

# FDR 校正
tpe_vill_sf$pvalue_FDR_park <- p.adjust(tpe_vill_sf$p_value_park, method = "fdr")

# 分群
tpe_vill_sf$LISA_type_Park <- "NoSig"
tpe_vill_sf$LISA_type_Park[tpe_vill_sf$pvalue_FDR_park < signif_park & tpe_vill_sf$local_I_park > 0 & quadrant_park == "High-High"] <- "High-High"
tpe_vill_sf$LISA_type_Park[tpe_vill_sf$pvalue_FDR_park < signif_park & tpe_vill_sf$local_I_park > 0 & quadrant_park == "Low-Low"] <- "Low-Low"
tpe_vill_sf$LISA_type_Park[tpe_vill_sf$pvalue_FDR_park < signif_park & tpe_vill_sf$local_I_park < 0 & quadrant_park == "High-Low"] <- "High-Low"
tpe_vill_sf$LISA_type_Park[tpe_vill_sf$pvalue_FDR_park < signif_park & tpe_vill_sf$local_I_park < 0 & quadrant_park == "Low-High"] <- "Low-High"

tpe_vill_sf$LISA_type_Park <- factor(tpe_vill_sf$LISA_type_Park,
                                     levels = c("High-High", "Low-Low", "High-Low", "Low-High", "NoSig"))

# 畫圖
tm_shape(tpe_vill_sf) +
  tm_polygons("LISA_type_Park",
              palette = lisa_colors,
              title = "LISA Cluster Type (Parks)") +
  
  # 其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +
  
  # 五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +
  
  # 主文字
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod",
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +
  
  tm_layout(title = "LISA Map (Inverse Distance) - Parks",
            legend.outside = TRUE)

intersection

# 建立 High-High 與 Low-Low 的交集欄位
tpe_vill_sf$LISA_HHH_intersection <- with(tpe_vill_sf,
  LISA_type_Park == "High-High" &
  LISA_type_sportfield == "High-High" &
  LISA_type_Hospital == "High-High"
)

tpe_vill_sf$LISA_LLL_intersection <- with(tpe_vill_sf,
  LISA_type_Park == "Low-Low" &
  LISA_type_sportfield == "Low-Low" &
  LISA_type_Hospital == "Low-Low"
)

# 建立一個新的欄位標示交集類型
tpe_vill_sf$LISA_intersection_type <- "Other"
tpe_vill_sf$LISA_intersection_type[tpe_vill_sf$LISA_HHH_intersection] <- "All High-High"
tpe_vill_sf$LISA_intersection_type[tpe_vill_sf$LISA_LLL_intersection] <- "All Low-Low"
tpe_vill_sf$LISA_intersection_type <- factor(
  tpe_vill_sf$LISA_intersection_type,
  levels = c("Other", "All High-High", "All Low-Low")
)

# 畫圖
tm_shape(tpe_vill_sf) +
  # 畫出交集區域
  tm_polygons("LISA_intersection_type",
              palette = c("gray", "green", "darkred"),
              labels = c("Not all same type", "All High-High", "All Low-Low"),
              title = "LISA Intersection Type") +

  # 疊上其他建北率高的學校
  tm_shape(other_top15_sf) +
  tm_dots(size = 0.5, col = "gray", shape = 21, border.col = "white") +

  # 疊上五虎
  tm_shape(five_tigers_sf) +
  tm_dots(size = 0.5, col = "black", shape = 21, border.col = "white") +

  # 加上學校文字標籤
  tm_text("school_short", 
          size = 0.9, 
          xmod = "xmod", 
          ymod = "ymod", 
          col = "black", 
          fontfamily = "Noto Sans TC",
          fontface = "bold") +

  # 標題與圖例配置
  tm_layout(title = "Intersection of LISA Clusters (High-High and Low-Low)",
            legend.outside = TRUE)

LS0tDQp0aXRsZTogIlNwYXRpYWwgQW5hbHlzaXMgRmluYWwiDQphdXRob3I6ICJSeWFuIEFiaWdhbuOAgeWRguaykOeUsOOAgemZs+WunOWuieOAgeWnmuWmguismeOAgem7g+S7gei8lOOAgeWKieaYsSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDYNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBpbmNsdWRlPUZBTFNFfQ0KbGlicmFyeShzZikgDQpsaWJyYXJ5KHRtYXApIA0KbGlicmFyeShkcGx5cikgDQpsaWJyYXJ5KGdyaWQpIA0KbGlicmFyeSh1bml0cykgDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHJlYWRPRFMpDQpsaWJyYXJ5KHNwZGVwKQ0KYGBgDQoNCiMjIyBSZWFkaW5nIERhdGENCg0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCnJtKGxpc3QgPSBscygpKQ0KDQojIFRoZSBUYWlwZWkgZGF0YSBwcm92aWRlZCBpbiBjbGFzcw0KdHBlX3ZpbGxfc2YgPSBzdF9yZWFkKCJDOi8xMTMtMl9TcHJpbmcvU3BhdGlhbF9BbmFseXNpcy9jb3Vyc2VmaWxlcy9UYWlwZWlfVmlsbC9UYWlwZWlfVmlsbC5zaHAiLCBvcHRpb25zID0gIkVOQ09ESU5HPWJpZzUiKQ0KDQojIFBvcHVsYXRpb24gZGF0YQ0KdHBlX3BvcF9kZiA9IHJlYWQuY3N2KCJDOi8xMTMtMl9TcHJpbmcvU3BhdGlhbF9BbmFseXNpcy9maW5hbF9wcm9qZWN0LzA2MDhfUGxvdHRpbmcvdGFpcGVpX3ZpbGxfcG9wLmNzdiIsIGZpbGVFbmNvZGluZyA9ICJiaWc1IikNCg0KIyBBbGwgc2Nob29scyBpbiBUYWl3YW4NCnNjaG9vbHMgPC0gcmVhZC5jc3YoIkM6LzExMy0yX1NwcmluZy9TcGF0aWFsX0FuYWx5c2lzL2ZpbmFsX3Byb2plY3QvMDYwOF9QbG90dGluZy9zY2hvb2xfMTEyX2Nzdi5jc3YiLA0KICAgICAgICAgICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gIkJJRzUiKQ0KDQojIyBTZWxlY3QgdGhvc2Ugd2hvc2UgY29vcmRpbmF0ZSBpcyBub3QgbWlzc2luZw0Kc2Nob29sc19jbGVhbiA8LSBzY2hvb2xzICU+JQ0KICBmaWx0ZXIoIWlzLm5hKGBYLuWdkOaomWApICYgIWlzLm5hKGBZLuWdkOaomWApKQ0KDQojIyBUdXJuaW5nIGludG8gc2YNCnNjaG9vbHNfc2YgPC0gc3RfYXNfc2Yoc2Nob29sc19jbGVhbiwNCiAgICAgICAgICAgICAgICAgICAgICAgY29vcmRzID0gYygiWC7lnZDmqJkiLCAiWS7lnZDmqJkiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgY3JzID0gMzgyNikgICMgRVBTRzozODI2IOaYryBUV0Q5NyDkuozluqbliIbluLYNCg0KIyBTcG9ydGZpZWxkIERhdGENCnNwb3J0ZmllbGRfc2YgPSBzdF9yZWFkKCJDOi8xMTMtMl9TcHJpbmcvU3BhdGlhbF9BbmFseXNpcy9maW5hbF9wcm9qZWN0LzA2MDhfUGxvdHRpbmcvc3BvcnRmaWVsZF90YWlwZWkvc3BvcnRmaWVsZF90YWlwZWkuc2hwIikNCg0KIyBHYW1pbmcgcGxhY2VzIGRhdGENCmdhbWluZ19zZiA9IHN0X3JlYWQoIkM6LzExMy0yX1NwcmluZy9TcGF0aWFsX0FuYWx5c2lzL2ZpbmFsX3Byb2plY3QvMDYwOF9QbG90dGluZy9HYW1pbmdfY2VudGVyL0dhbWluZ19jZW50ZXIuc2hwIikNCg0KIyBIb3NwaXRhbCBkYXRhDQpob3NwaXRhbF9zZiA9IHN0X3JlYWQoIkM6LzExMy0yX1NwcmluZy9TcGF0aWFsX0FuYWx5c2lzL2ZpbmFsX3Byb2plY3QvMDYwOF9QbG90dGluZy9Ib3NwaXRhbF90YWlwZWkvSG9zcGl0YWxfdGFpcGVpLnNocCIpDQoNCiMgZmFzdGZvb2QgZGF0YQ0KZmFzdGZvb2Rfc2YgPSBzdF9yZWFkKCJDOi8xMTMtMl9TcHJpbmcvU3BhdGlhbF9BbmFseXNpcy9maW5hbF9wcm9qZWN0LzA2MDhfUGxvdHRpbmcvVHBlX0Zhc3Rmb29kL1RwZV9GYXN0Zm9vZC5zaHAiKQ0KDQojIHBhcmsgZGF0YQ0KcGFya19zZiA9IHN0X3JlYWQoIkM6LzExMy0yX1NwcmluZy9TcGF0aWFsX0FuYWx5c2lzL2ZpbmFsX3Byb2plY3QvMDYwOF9QbG90dGluZy9QYXJrX2NlbnRyb2lkcy9QYXJrX2NlbnRyb2lkcy5zaHAiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBTZWxlY3RpbmcgcG9wdWxhdGlvbiBkYXRhDQp0cGVfcG9wX2RmID0gdHBlX3BvcF9kZlt0cGVfcG9wX2RmJOaAp+WIpSA9PSAi6KiIIiAmIHRwZV9wb3BfZGYk5Y2A5Z+f5Luj56K8ID4gMTAwMDAwMDAwICYgdHBlX3BvcF9kZiTlubTku70gPT0gMTEzICYgdHBlX3BvcF9kZiTmnIjku70gPT0gMiwgIF0NCnRwZV92aWxsX3NmJOWNgOWfn+S7o+eivCA9IGFzLm51bWVyaWModHBlX3ZpbGxfc2YkVE9XTl9JRCkgKiAxMDAwICsgYXMubnVtZXJpYyh0cGVfdmlsbF9zZiRWSUxMQUdFX0lEKQ0KYGBgDQoNCiMjIyBDYWxjdWxhdGlvbg0KDQpgYGB7cn0NCiMgQ2FsY3VsYXRpb25zIG9uIHBvcHVsYXRpb24gDQp0cGVfcG9wX2RmJGFnZV8xXzYgPSByb3dTdW1zKHRwZV9wb3BfZGZbODoxM10pDQp0cGVfcG9wX2RmJGFnZV83XzEyID0gcm93U3Vtcyh0cGVfcG9wX2RmWzE0OjE5XSkNCnRwZV9wb3BfZGYkYWR1bHQgPSByb3dTdW1zKHRwZV9wb3BfZGZbMjY6NzFdKQ0KDQojIHByaW1hcnlfcmF0aW8gYW5kIHNlY29uZGFyeSByYXRpbw0KdHBlX3BvcF9kZiRwcmltYXJ5X3JhdGlvID0gdHBlX3BvcF9kZiRhZ2VfMV82IC8gdHBlX3BvcF9kZiRhZHVsdA0KdHBlX3BvcF9kZiRzZWNvbmRhcnlfcmF0aW8gPSB0cGVfcG9wX2RmJGFnZV83XzEyIC8gdHBlX3BvcF9kZiRhZHVsdA0KDQp0cGVfdmlsbF9zZiA9IG1lcmdlKHRwZV92aWxsX3NmLCB0cGVfcG9wX2RmLCBieSA9ICLljYDln5/ku6PnorwiKQ0KYGBgDQoNCiMjIyBTZWxlY3RpbmcgU2Nob29scw0KDQpgYGB7cn0NCiMg6YG45Y+W6Ie65YyX55qE5ZyL5LitDQpzY2hvb2xzX3NmID0gc2Nob29sc19zZiAlPiUNCiAgZmlsdGVyKOWtuOagoee0muWIpSAlaW4lIGMoIuWci+awkeS4reWtuCIsICLpmYToqK3lnIvmsJHkuK3lrbgiKSkgJT4lDQogIGZpbHRlcijnuKPluILliKUgPT0gIuiHuuWMl+W4giIpDQoNCiMg6YG45Y+W44CM5YyX5biC5YWs56uL5LqU6JmOICsg5pmv576O44CNDQpmaXZlX3RpZ2Vyc19zZiA9IHNjaG9vbHNfc2YgJT4lDQogIGZpbHRlcijlrbjmoKHlkI3nqLEgJWluJSBjKCLluILnq4vkuK3mraPlnIvkuK0iLCAi5biC56uL6b6N6ZaA5ZyL5LitIiwgIuW4gueri+aVpuWMluWci+S4rSIsICLluILnq4vku4vlo73lnIvkuK0iLCAi5biC56uL6YeR6I+v5ZyL5LitIiwgIuW4gueri+aZr+e+juWci+S4rSIpKQ0KDQojIOmBuOWPluWFtuS7luW7uuWMl+eOh+mrmOeahOWtuOagoQ0Kb3RoZXJfdG9wMTVfc2YgPSBzY2hvb2xzX3NmICU+JQ0KICBmaWx0ZXIo5a245qCh5ZCN56ixICVpbiUgYygi5ZyL56uL5bir5aSn6ZmE5Lit6ZmE6Kit5ZyL5LitIiwgIuengeeri+mdnOW/g+mrmOS4remZhOioreWci+S4rSIsICLnp4Hnq4vlu7blubPkuK3lrbjpmYToqK3lnIvkuK0iLCAi5biC56uL5aSn5a6J5ZyL5LitIiwgIuW4gueri+m6l+WxseWci+S4rSIsIuW4gueri+WNl+mWgOWci+S4rSIsICLluILnq4vlpKnmr43lnIvkuK0iLCAi5biC56uL6IiI6ZuF5ZyL5LitIiwgIuW4gueri+efs+eJjOWci+S4rSIpKQ0KDQojIOW7uueri+ewoeWMluWQjeeoseashOS9jQ0KZml2ZV90aWdlcnNfc2Ykc2Nob29sX3Nob3J0IDwtIHJlY29kZShmaXZlX3RpZ2Vyc19zZiTlrbjmoKHlkI3nqLEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICLluILnq4vkuK3mraPlnIvkuK0iID0gIuS4reatoyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICLluILnq4vpvo3ploDlnIvkuK0iID0gIum+jemWgCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICLluILnq4vmlabljJblnIvkuK0iID0gIuaVpuWMliIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICLluILnq4vku4vlo73lnIvkuK0iID0gIuS7i+WjvSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICLluILnq4vph5Hoj6/lnIvkuK0iID0gIumHkeiPryIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICLluILnq4vmma/nvo7lnIvkuK0iID0gIuaZr+e+jiIpDQpgYGANCg0KIyMjIExJU0Egb2YgcHJpbWFyeSByYXRpbyAoMVx+NikNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCm5iID0gcG9seTJuYih0cGVfdmlsbF9zZiwgcXVlZW4gPSBGQUxTRSkgDQpsaXN0dyA9IG5iMmxpc3R3KG5iLCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KZGVuc2l0eSA9IGFzLnZlY3Rvcih0cGVfdmlsbF9zZiRwcmltYXJ5X3JhdGlvKQ0KbGlzYSA9IGxvY2FsbW9yYW4oZGVuc2l0eSwgbGlzdHcgPSBsaXN0dywgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQp0cGVfdmlsbF9zZiRsb2NhbF9JX3ByaSA9IGxpc2FbLCAxXQ0KdHBlX3ZpbGxfc2Ykel9zY29yZV9wcmkgPSBsaXNhWywgNF0NCnRwZV92aWxsX3NmJHBfdmFsdWVfcHJpID0gbGlzYVssIDVdDQpxdWFkcmFudCA9IGF0dHIobGlzYSwgInF1YWRyIikkbWVhbg0KcXVhZHJhbnQgPSBmYWN0b3IocXVhZHJhbnQsIGxldmVscyA9IGMobGV2ZWxzKHF1YWRyYW50KSwgIk5vU2lnIikpDQpzaWduaWZfbGV2ZWxfMDUgPSAwLjA1DQpxdWFkcmFudFtsaXNhWywgNV0gPiBzaWduaWZfbGV2ZWxfMDVdID0gIk5vU2lnIg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX3ByaSA9IHF1YWRyYW50DQoNCmxpc2FfY29sb3JzIDwtIGMoDQogICdIaWdoLUhpZ2gnID0gJ3JlZCcsDQogICdMb3ctTG93JyA9ICdibHVlJywNCiAgJ0hpZ2gtTG93JyA9ICdwaW5rJywNCiAgJ0xvdy1IaWdoJyA9ICdza3libHVlMicsDQogICdOb1NpZycgPSAnZ3JleScNCikNCg0KdG1fc2hhcGUodHBlX3ZpbGxfc2YpICsNCiAgdG1fcG9seWdvbnMoIkxJU0FfdHlwZV9wcmkiLA0KICAgICAgICAgICAgICBwYWxldHRlID0gbGlzYV9jb2xvcnMsDQogICAgICAgICAgICAgIHRpdGxlID0gIkxJU0EgQ2x1c3RlciBUeXBlIChhZ2UgMX42IC8gYWR1bHQpIikgKw0KICB0bV9sYXlvdXQodGl0bGUgPSAiTElTQSBNYXAgZm9yIEFnZSAxfjYgLyBBZHVsdCIsIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSkNCg0KYGBgDQoNCiMjIyBMSVNBIG9mIHNlY29uZGFyeSByYXRpbyAoN1x+MTIpDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpuYiA9IHBvbHkybmIodHBlX3ZpbGxfc2YsIHF1ZWVuID0gRkFMU0UpIA0KbGlzdHcgPSBuYjJsaXN0dyhuYiwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCmRlbnNpdHkgPSBhcy52ZWN0b3IodHBlX3ZpbGxfc2Ykc2Vjb25kYXJ5X3JhdGlvKQ0KbGlzYSA9IGxvY2FsbW9yYW4oZGVuc2l0eSwgbGlzdHcgPSBsaXN0dywgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQp0cGVfdmlsbF9zZiRsb2NhbF9JX3NlYyA9IGxpc2FbLCAxXQ0KdHBlX3ZpbGxfc2Ykel9zY29yZV9zZWMgPSBsaXNhWywgNF0NCnRwZV92aWxsX3NmJHBfdmFsdWVfc2VjID0gbGlzYVssIDVdDQpxdWFkcmFudCA9IGF0dHIobGlzYSwgInF1YWRyIikkbWVhbg0KcXVhZHJhbnQgPSBmYWN0b3IocXVhZHJhbnQsIGxldmVscyA9IGMobGV2ZWxzKHF1YWRyYW50KSwgIk5vU2lnIikpDQpzaWduaWZfbGV2ZWwgPSAwLjA1DQpxdWFkcmFudFtsaXNhWywgNV0gPiBzaWduaWZfbGV2ZWxdID0gIk5vU2lnIg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX3NlYyA9IHF1YWRyYW50DQoNCnRtX3NoYXBlKHRwZV92aWxsX3NmKSArDQogIHRtX3BvbHlnb25zKCJMSVNBX3R5cGVfc2VjIiwNCiAgICAgICAgICAgICAgcGFsZXR0ZSA9IGxpc2FfY29sb3JzLA0KICAgICAgICAgICAgICB0aXRsZSA9ICJMSVNBIENsdXN0ZXIgVHlwZSAoYWdlIDd+MTIgLyBhZHVsdCkiKSArDQogIHRtX2xheW91dCh0aXRsZSA9ICJMSVNBIE1hcCBmb3IgQWdlIDd+MTIgLyBBZHVsdCIsIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSkNCg0KYGBgDQoNCmBgYHtyfQ0KZGVuc2l0eV9zZWMgPSBhcy52ZWN0b3IodHBlX3ZpbGxfc2Ykc2Vjb25kYXJ5X3JhdGlvKQ0KbGlzYV9zZWMgPSBsb2NhbG1vcmFuKGRlbnNpdHlfc2VjLCBsaXN0dyA9IGxpc3R3LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCnRwZV92aWxsX3NmJGxvY2FsX0lfc2VjID0gbGlzYV9zZWNbLCAxXQ0KdHBlX3ZpbGxfc2Ykel9zY29yZV9zZWMgPSBsaXNhX3NlY1ssIDRdDQp0cGVfdmlsbF9zZiRwX3ZhbHVlX3NlYyA9IGxpc2Ffc2VjWywgNV0NCg0KcXVhZHJhbnRfc2VjID0gYXR0cihsaXNhX3NlYywgInF1YWRyIikkbWVhbg0KcXVhZHJhbnRfc2VjID0gZmFjdG9yKHF1YWRyYW50X3NlYywgbGV2ZWxzID0gYyhsZXZlbHMocXVhZHJhbnRfc2VjKSwgIk5vU2lnIikpDQoNCiMg5L2/55SoIEZEUiDmoKHmraMNCnF1YWRyYW50X3NlY1twLmFkanVzdChsaXNhX3NlY1ssIDVdLCBtZXRob2QgPSAiZmRyIikgPiBzaWduaWZfbGV2ZWxdID0gIk5vU2lnIg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX3NlYyA9IHF1YWRyYW50X3NlYw0KDQojIOe5quWclu+8iOWQq+WtuOagoem7nuS9je+8iQ0KdG1fc2hhcGUodHBlX3ZpbGxfc2YpICsNCiAgdG1fcG9seWdvbnMoIkxJU0FfdHlwZV9zZWMiLA0KICAgICAgICAgICAgICBwYWxldHRlID0gbGlzYV9jb2xvcnMsDQogICAgICAgICAgICAgIHRpdGxlID0gIkxJU0EgQ2x1c3RlciBUeXBlIChhZ2UgN34xMiAvIGFkdWx0KSIpICsNCiAgdG1fbGF5b3V0KHRpdGxlID0gIkxJU0EgTWFwIGZvciBBZ2UgN34xMiAvIEFkdWx0IiwgbGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KDQpgYGANCg0KIyMjIERpc3BsYXlpbmcgdGhlIGZpdmUgdGlnZXJzIG9uIHRoZSBGRFJkIG1hcA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbmFtZXMoZ3JEZXZpY2VzOjp3aW5kb3dzRm9udHMoKSkgICMgV2luZG93cyDlsIjnlKgNCg0KIyDmiYvli5Xngrrmr4/lgIvlrbjmoKHliqDkuIrlgY/np7vlgLzvvIjkvaDlj6/ku6Xkvp3nhaflr6bpmpvpnIDmsYLlvq7oqr/vvIkNCmZpdmVfdGlnZXJzX3NmIDwtIGZpdmVfdGlnZXJzX3NmICU+JQ0KICBtdXRhdGUoDQogICAgeG1vZCA9IGMoMC4zLCAtMC41LCAwLCAxLCAtMSwgMSksICAgIyBYIOWBj+enuw0KICAgIHltb2QgPSBjKDAuOCwgMC44LCAwLjgsIDAuOCwgMC44LCAwLjgpICMgWSDlgY/np7sNCiAgKQ0KDQp0bV9zaGFwZSh0cGVfdmlsbF9zZikgKw0KICB0bV9wb2x5Z29ucygiTElTQV90eXBlX3NlYyIsDQogICAgICAgICAgICAgIHBhbGV0dGUgPSBsaXNhX2NvbG9ycywNCiAgICAgICAgICAgICAgdGl0bGUgPSAiTElTQSBDbHVzdGVyIFR5cGUgKGFnZSA3fjEyIC8gYWR1bHQpIikgKw0KICANCiAgIyDlhbbku5blu7rljJfnjofpq5jnmoTlrbjmoKENCiAgdG1fc2hhcGUob3RoZXJfdG9wMTVfc2YpICsNCiAgdG1fZG90cyhzaXplID0gMC41LCBjb2wgPSAiZ3JheSIsIHNoYXBlID0gMjEsIGJvcmRlci5jb2wgPSAid2hpdGUiKSArDQogIA0KICAjIOWFrOeri+S6lOiZjg0KICB0bV9zaGFwZShmaXZlX3RpZ2Vyc19zZikgKw0KICB0bV9kb3RzKHNpemUgPSAwLjUsIGNvbCA9ICJibGFjayIsIHNoYXBlID0gMjEsIGJvcmRlci5jb2wgPSAid2hpdGUiKSArDQogIA0KICAjIOS4u+aWh+Wtlw0KICB0bV90ZXh0KCJzY2hvb2xfc2hvcnQiLCANCiAgICAgICAgICBzaXplID0gMC45LCANCiAgICAgICAgICB4bW9kID0gInhtb2QiLA0KICAgICAgICAgIHltb2QgPSAieW1vZCIsIA0KICAgICAgICAgIGNvbCA9ICJibGFjayIsIA0KICAgICAgICAgIGZvbnRmYW1pbHkgPSAiTm90byBTYW5zIFRDIiwNCiAgICAgICAgICBmb250ZmFjZSA9ICJib2xkIikgKw0KICANCiAgdG1fbGF5b3V0KHRpdGxlID0gIkxJU0EgTWFwIGZvciBBZ2UgN34xMiAvIEFkdWx0IiwNCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSkNCg0KYGBgDQoNCiMjIEVudmlyb25tZW50DQoNCiMjIyBHYW1pbmcgQ2VudGVyDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIFNldHRpbmcgc2lnX2xldmVsDQpzaWduaWZfZ2FtaW5nID0gMC4wMQ0KDQpnYW1pbmdfc2YgPC0gc3RfdHJhbnNmb3JtKGdhbWluZ19zZiwgc3RfY3JzKHRwZV92aWxsX3NmKSkNCg0Kam9pbmVkX2dhbWluZyA8LSBzdF9qb2luKGdhbWluZ19zZiwgdHBlX3ZpbGxfc2YsIGpvaW4gPSBzdF93aXRoaW4pDQoNCmdhbWluZ19jb3VudHMgPC0gam9pbmVkX2dhbWluZyAlPiUNCiAgZ3JvdXBfYnkoVE9XTl9JRCkgJT4lDQogIHN1bW1hcmlzZShnYW1pbmdfY291bnQgPSBuKCkpDQoNCmdhbWluZ19jb3VudHNfZGYgPC0gc3RfZHJvcF9nZW9tZXRyeShnYW1pbmdfY291bnRzKQ0KDQp0cGVfdmlsbF9zZiRnYW1pbmdfY291bnQgPC0gZ2FtaW5nX2NvdW50c19kZiRnYW1pbmdfY291bnRbbWF0Y2godHBlX3ZpbGxfc2YkVE9XTl9JRCwgZ2FtaW5nX2NvdW50c19kZiRUT1dOX0lEKV0NCnRwZV92aWxsX3NmJGdhbWluZ19jb3VudFtpcy5uYSh0cGVfdmlsbF9zZiRnYW1pbmdfY291bnQpXSA8LSAwDQoNCnRwZV92aWxsX3NmJEFSRUEgPC0gc2V0X3VuaXRzKHN0X2FyZWEodHBlX3ZpbGxfc2YpLCAia21eMiIpDQp0cGVfdmlsbF9zZiRnYW1pbmdfZGVuc2l0eSA8LSB0cGVfdmlsbF9zZiRnYW1pbmdfY291bnQgLyB0cGVfdmlsbF9zZiRBUkVBDQoNCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZCh0cGVfdmlsbF9zZikpDQoNCm5iIDwtIGRuZWFybmVpZ2goY29vcmRzLCBkMSA9IDAsIGQyID0gMTAwMDApDQoNCmRpc3RfbGlzdCA8LSBuYmRpc3RzKG5iLCBjb29yZHMpDQoNCmludl9kaXN0X2xpc3QgPC0gbGFwcGx5KGRpc3RfbGlzdCwgZnVuY3Rpb24oeCkgew0KICB5IDwtIDEgLyAoeCArIDAuMDAwMSkNCiAgcmV0dXJuKHkpDQp9KQ0KDQpsaXN0dyA8LSBuYjJsaXN0dyhuYiwgZ2xpc3QgPSBpbnZfZGlzdF9saXN0LCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQpkZW5zaXR5IDwtIGFzLnZlY3Rvcih0cGVfdmlsbF9zZiRnYW1pbmdfZGVuc2l0eSkNCg0KZ2xvYmFsX21vcmFuIDwtIG1vcmFuLnRlc3QoZGVuc2l0eSwgbGlzdHcgPSBsaXN0dywgemVyby5wb2xpY3kgPSBUUlVFKQ0KcHJpbnQoZ2xvYmFsX21vcmFuKQ0KDQpsaXNhIDwtIGxvY2FsbW9yYW4oZGVuc2l0eSwgbGlzdHcgPSBsaXN0dywgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQp0cGVfdmlsbF9zZiRsb2NhbF9JIDwtIGxpc2FbLCAxXQ0KdHBlX3ZpbGxfc2Ykel9zY29yZSA8LSBsaXNhWywgNF0NCnRwZV92aWxsX3NmJHBfdmFsdWUgPC0gbGlzYVssIDVdDQoNCnF1YWRyYW50IDwtIGF0dHIobGlzYSwgInF1YWRyIikkbWVhbg0KcXVhZHJhbnQgPC0gZmFjdG9yKHF1YWRyYW50LCBsZXZlbHMgPSBjKGxldmVscyhxdWFkcmFudCksICJOb1NpZyIpKQ0KDQp0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwtIHAuYWRqdXN0KGxpc2FbLCA1XSwgbWV0aG9kID0gImZkciIpDQoNCnRwZV92aWxsX3NmJExJU0FfdHlwZV9HYW1pbmcgPC0gIk5vU2lnIg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX0dhbWluZ1t0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2dhbWluZyAmIHRwZV92aWxsX3NmJGxvY2FsX0kgPiAwICYgcXVhZHJhbnQgPT0gIkhpZ2gtSGlnaCJdIDwtICJIaWdoLUhpZ2giDQp0cGVfdmlsbF9zZiRMSVNBX3R5cGVfR2FtaW5nW3RwZV92aWxsX3NmJHB2YWx1ZV9GRFIgPCBzaWduaWZfZ2FtaW5nICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA+IDAgJiBxdWFkcmFudCA9PSAiTG93LUxvdyJdIDwtICJMb3ctTG93Ig0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX0dhbWluZ1t0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2dhbWluZyAmIHRwZV92aWxsX3NmJGxvY2FsX0kgPCAwICYgcXVhZHJhbnQgPT0gIkhpZ2gtTG93Il0gPC0gIkhpZ2gtTG93Ig0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX0dhbWluZ1t0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2dhbWluZyAmIHRwZV92aWxsX3NmJGxvY2FsX0kgPCAwICYgcXVhZHJhbnQgPT0gIkxvdy1IaWdoIl0gPC0gIkxvdy1IaWdoIg0KDQp0cGVfdmlsbF9zZiRMSVNBX3R5cGVfR2FtaW5nIDwtIGZhY3Rvcih0cGVfdmlsbF9zZiRMSVNBX3R5cGVfR2FtaW5nLA0KICBsZXZlbHMgPSBjKCJIaWdoLUhpZ2giLCAiTG93LUxvdyIsICJIaWdoLUxvdyIsICJMb3ctSGlnaCIsICJOb1NpZyIpKQ0KDQpsaXNhX2NvbG9ycyA8LSBjKA0KICAnSGlnaC1IaWdoJyA9ICdyZWQnLA0KICAnTG93LUxvdycgPSAnYmx1ZScsDQogICdIaWdoLUxvdycgPSAncGluaycsDQogICdMb3ctSGlnaCcgPSAnc2t5Ymx1ZTInLA0KICAnTm9TaWcnID0gJ2dyZXknDQopDQoNCnRtX3NoYXBlKHRwZV92aWxsX3NmKSArDQogIHRtX3BvbHlnb25zKCJMSVNBX3R5cGVfR2FtaW5nIiwNCiAgICAgICAgICAgICAgcGFsZXR0ZSA9IGxpc2FfY29sb3JzLA0KICAgICAgICAgICAgICB0aXRsZSA9ICJMSVNBIENsdXN0ZXIgVHlwZSAoU3BvcnQgRmFjaWxpdGllcykiKSArDQogIA0KICAjIOWFtuS7luW7uuWMl+eOh+mrmOeahOWtuOagoQ0KICB0bV9zaGFwZShvdGhlcl90b3AxNV9zZikgKw0KICB0bV9kb3RzKHNpemUgPSAwLjUsIGNvbCA9ICJncmF5Iiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5LqU6JmODQogIHRtX3NoYXBlKGZpdmVfdGlnZXJzX3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImJsYWNrIiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5Li75paH5a2XDQogIHRtX3RleHQoInNjaG9vbF9zaG9ydCIsIA0KICAgICAgICAgIHNpemUgPSAwLjksIA0KICAgICAgICAgIHhtb2QgPSAieG1vZCIsDQogICAgICAgICAgeW1vZCA9ICJ5bW9kIiwgDQogICAgICAgICAgY29sID0gImJsYWNrIiwgDQogICAgICAgICAgZm9udGZhbWlseSA9ICJOb3RvIFNhbnMgVEMiLA0KICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiKSArDQogIA0KICB0bV9sYXlvdXQodGl0bGUgPSAiTElTQSBNYXAgKEludmVyc2UgRGlzdGFuY2UpIC0gR2FtaW5nIENlbnRlcnMiLA0KICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KDQpgYGANCg0KIyMjIGZhc3Rmb29kDQoNCmBgYHtyfQ0KIyDoqK3lrprkv6Hlv4PmsLTmupYNCnNpZ25pZl9mYXN0Zm9vZCA8LSAwLjA1DQoNCiMg5oqV5b2x5LiA6Ie0DQpmYXN0Zm9vZF9zZiA8LSBzdF90cmFuc2Zvcm0oZmFzdGZvb2Rfc2YsIHN0X2Nycyh0cGVfdmlsbF9zZikpDQoNCiMg56m66ZaT6YCj5o6l77ya5om+5Ye65q+P5YCL5p2R6YeM5pyJ5bm+5YCLIGZhc3QgZm9vZCDpu54NCmpvaW5lZF9mYXN0Zm9vZCA8LSBzdF9qb2luKGZhc3Rmb29kX3NmLCB0cGVfdmlsbF9zZiwgam9pbiA9IHN0X3dpdGhpbikNCg0KZmFzdGZvb2RfY291bnRzIDwtIGpvaW5lZF9mYXN0Zm9vZCAlPiUNCiAgZ3JvdXBfYnkoVE9XTl9JRCkgJT4lDQogIHN1bW1hcmlzZShmYXN0Zm9vZF9jb3VudCA9IG4oKSkNCg0KZmFzdGZvb2RfY291bnRzX2RmIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoZmFzdGZvb2RfY291bnRzKQ0KDQp0cGVfdmlsbF9zZiRmYXN0Zm9vZF9jb3VudCA8LSBmYXN0Zm9vZF9jb3VudHNfZGYkZmFzdGZvb2RfY291bnRbbWF0Y2godHBlX3ZpbGxfc2YkVE9XTl9JRCwgZmFzdGZvb2RfY291bnRzX2RmJFRPV05fSUQpXQ0KdHBlX3ZpbGxfc2YkZmFzdGZvb2RfY291bnRbaXMubmEodHBlX3ZpbGxfc2YkZmFzdGZvb2RfY291bnQpXSA8LSAwDQoNCiMg6Z2i56mN6IiH5a+G5bqmDQp0cGVfdmlsbF9zZiRBUkVBIDwtIHNldF91bml0cyhzdF9hcmVhKHRwZV92aWxsX3NmKSwgImttXjIiKQ0KdHBlX3ZpbGxfc2YkZmFzdGZvb2RfZGVuc2l0eSA8LSB0cGVfdmlsbF9zZiRmYXN0Zm9vZF9jb3VudCAvIHRwZV92aWxsX3NmJEFSRUENCg0KIyDphLDov5Hnn6npmaPoqK3lrpoNCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZCh0cGVfdmlsbF9zZikpDQpuYiA8LSBkbmVhcm5laWdoKGNvb3JkcywgZDEgPSAwLCBkMiA9IDEwMDAwKQ0KDQpkaXN0X2xpc3QgPC0gbmJkaXN0cyhuYiwgY29vcmRzKQ0KaW52X2Rpc3RfbGlzdCA8LSBsYXBwbHkoZGlzdF9saXN0LCBmdW5jdGlvbih4KSAxIC8gKHggKyAwLjAwMDEpKQ0KDQpsaXN0dyA8LSBuYjJsaXN0dyhuYiwgZ2xpc3QgPSBpbnZfZGlzdF9saXN0LCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQojIOWft+ihjCBMSVNBDQpkZW5zaXR5IDwtIGFzLnZlY3Rvcih0cGVfdmlsbF9zZiRmYXN0Zm9vZF9kZW5zaXR5KQ0KDQpnbG9iYWxfbW9yYW4gPC0gbW9yYW4udGVzdChkZW5zaXR5LCBsaXN0dyA9IGxpc3R3LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQpwcmludChnbG9iYWxfbW9yYW4pDQoNCmxpc2EgPC0gbG9jYWxtb3JhbihkZW5zaXR5LCBsaXN0dyA9IGxpc3R3LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCnRwZV92aWxsX3NmJGxvY2FsX0kgPC0gbGlzYVssIDFdDQp0cGVfdmlsbF9zZiR6X3Njb3JlIDwtIGxpc2FbLCA0XQ0KdHBlX3ZpbGxfc2YkcF92YWx1ZSA8LSBsaXNhWywgNV0NCg0KcXVhZHJhbnQgPC0gYXR0cihsaXNhLCAicXVhZHIiKSRtZWFuDQpxdWFkcmFudCA8LSBmYWN0b3IocXVhZHJhbnQsIGxldmVscyA9IGMobGV2ZWxzKHF1YWRyYW50KSwgIk5vU2lnIikpDQoNCnRwZV92aWxsX3NmJHB2YWx1ZV9GRFIgPC0gcC5hZGp1c3QobGlzYVssIDVdLCBtZXRob2QgPSAiZmRyIikNCg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX0Zhc3Rmb29kIDwtICJOb1NpZyINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9GYXN0Zm9vZFt0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2Zhc3Rmb29kICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA+IDAgJiBxdWFkcmFudCA9PSAiSGlnaC1IaWdoIl0gPC0gIkhpZ2gtSGlnaCINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9GYXN0Zm9vZFt0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2Zhc3Rmb29kICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA+IDAgJiBxdWFkcmFudCA9PSAiTG93LUxvdyJdIDwtICJMb3ctTG93Ig0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX0Zhc3Rmb29kW3RwZV92aWxsX3NmJHB2YWx1ZV9GRFIgPCBzaWduaWZfZmFzdGZvb2QgJiB0cGVfdmlsbF9zZiRsb2NhbF9JIDwgMCAmIHF1YWRyYW50ID09ICJIaWdoLUxvdyJdIDwtICJIaWdoLUxvdyINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9GYXN0Zm9vZFt0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2Zhc3Rmb29kICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA8IDAgJiBxdWFkcmFudCA9PSAiTG93LUhpZ2giXSA8LSAiTG93LUhpZ2giDQoNCnRwZV92aWxsX3NmJExJU0FfdHlwZV9GYXN0Zm9vZCA8LSBmYWN0b3IodHBlX3ZpbGxfc2YkTElTQV90eXBlX0Zhc3Rmb29kLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJIaWdoLUhpZ2giLCAiTG93LUxvdyIsICJIaWdoLUxvdyIsICJMb3ctSGlnaCIsICJOb1NpZyIpKQ0KDQojIOmFjeiJsg0KbGlzYV9jb2xvcnMgPC0gYygNCiAgJ0hpZ2gtSGlnaCcgPSAncmVkJywNCiAgJ0xvdy1Mb3cnID0gJ2JsdWUnLA0KICAnSGlnaC1Mb3cnID0gJ3BpbmsnLA0KICAnTG93LUhpZ2gnID0gJ3NreWJsdWUyJywNCiAgJ05vU2lnJyA9ICdncmV5Jw0KKQ0KDQojIOeVq+WHuuWcsOWclg0KdG1fc2hhcGUodHBlX3ZpbGxfc2YpICsNCiAgdG1fcG9seWdvbnMoIkxJU0FfdHlwZV9GYXN0Zm9vZCIsDQogICAgICAgICAgICAgIHBhbGV0dGUgPSBsaXNhX2NvbG9ycywNCiAgICAgICAgICAgICAgdGl0bGUgPSAiTElTQSBDbHVzdGVyIFR5cGUgKEZhc3QgRm9vZCkiKSArDQogIA0KICAjIOWFtuS7luW7uuWMl+eOh+mrmOeahOWtuOagoQ0KICB0bV9zaGFwZShvdGhlcl90b3AxNV9zZikgKw0KICB0bV9kb3RzKHNpemUgPSAwLjUsIGNvbCA9ICJncmF5Iiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5LqU6JmODQogIHRtX3NoYXBlKGZpdmVfdGlnZXJzX3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImJsYWNrIiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5qiZ5LiK5a245qCh5ZCN56ixDQogIHRtX3RleHQoInNjaG9vbF9zaG9ydCIsIA0KICAgICAgICAgIHNpemUgPSAwLjksIA0KICAgICAgICAgIHhtb2QgPSAieG1vZCIsDQogICAgICAgICAgeW1vZCA9ICJ5bW9kIiwgDQogICAgICAgICAgY29sID0gImJsYWNrIiwgDQogICAgICAgICAgZm9udGZhbWlseSA9ICJOb3RvIFNhbnMgVEMiLA0KICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiKSArDQogIA0KICB0bV9sYXlvdXQodGl0bGUgPSAiTElTQSBNYXAgKEludmVyc2UgRGlzdGFuY2UpIC0gRmFzdCBGb29kIiwNCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSkNCmBgYA0KIyMjIGludGVyc2VjdGluZw0KYGBge3J9DQojIOW7uueri+S6pOmbhuaomeiomOashOS9jQ0KdHBlX3ZpbGxfc2YkSEhfSW50ZXJzZWN0IDwtIGlmZWxzZSgNCiAgdHBlX3ZpbGxfc2YkTElTQV90eXBlX0Zhc3Rmb29kID09ICJIaWdoLUhpZ2giICYgDQogICAgdHBlX3ZpbGxfc2YkTElTQV90eXBlX0dhbWluZyA9PSAiSGlnaC1IaWdoIiwgDQogICJIaWdoLUhpZ2ggQm90aCIsIA0KICAiT3RoZXIiDQopDQoNCiMg6Kit5a6a6aGP6ImyDQppbnRlcnNlY3RfY29sb3JzIDwtIGMoIkhpZ2gtSGlnaCBCb3RoIiA9ICJwdXJwbGUiLCAiT3RoZXIiID0gImxpZ2h0Z3JleSIpDQoNCiMg55Wr5Ye65Zyw5ZyWDQp0bV9zaGFwZSh0cGVfdmlsbF9zZikgKw0KICB0bV9wb2x5Z29ucygiSEhfSW50ZXJzZWN0IiwgDQogICAgICAgICAgICAgIHBhbGV0dGUgPSBpbnRlcnNlY3RfY29sb3JzLCANCiAgICAgICAgICAgICAgdGl0bGUgPSAiSGlnaC1IaWdoIEludGVyc2VjdGlvbiIpICsNCiAgDQogICMg5YW25LuW5bu65YyX546H6auY55qE5a245qChDQogIHRtX3NoYXBlKG90aGVyX3RvcDE1X3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImdyYXkiLCBzaGFwZSA9IDIxLCBib3JkZXIuY29sID0gIndoaXRlIikgKw0KICANCiAgIyDkupTomY4NCiAgdG1fc2hhcGUoZml2ZV90aWdlcnNfc2YpICsNCiAgdG1fZG90cyhzaXplID0gMC41LCBjb2wgPSAiYmxhY2siLCBzaGFwZSA9IDIxLCBib3JkZXIuY29sID0gIndoaXRlIikgKw0KICANCiAgIyDmqJnkuIrlrbjmoKHlkI3nqLENCiAgdG1fdGV4dCgic2Nob29sX3Nob3J0IiwgDQogICAgICAgICAgc2l6ZSA9IDAuOSwgDQogICAgICAgICAgeG1vZCA9ICJ4bW9kIiwNCiAgICAgICAgICB5bW9kID0gInltb2QiLCANCiAgICAgICAgICBjb2wgPSAiYmxhY2siLCANCiAgICAgICAgICBmb250ZmFtaWx5ID0gIk5vdG8gU2FucyBUQyIsDQogICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpICsNCg0KICB0bV9sYXlvdXQodGl0bGUgPSAiSW50ZXJzZWN0aW9uIG9mIEhpZ2gtSGlnaCBDbHVzdGVyczogRmFzdCBGb29kICYgR2FtaW5nIENlbnRlcnMiLA0KICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KDQpgYGANCg0KIyMjIHNwb3J0ZmllbGQNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNwb3J0ZmllbGRfc2YgPC0gc3RfdHJhbnNmb3JtKHNwb3J0ZmllbGRfc2YsIHN0X2Nycyh0cGVfdmlsbF9zZikpDQoNCmpvaW5lZF9zcG9ydGZpZWxkIDwtIHN0X2pvaW4oc3BvcnRmaWVsZF9zZiwgdHBlX3ZpbGxfc2YsIGpvaW4gPSBzdF93aXRoaW4pDQoNCnNwb3J0ZmllbGRfY291bnRzIDwtIGpvaW5lZF9zcG9ydGZpZWxkICU+JQ0KICBncm91cF9ieShUT1dOX0lEKSAlPiUNCiAgc3VtbWFyaXNlKHNwb3J0ZmllbGRfY291bnQgPSBuKCkpDQoNCnNwb3J0ZmllbGRfY291bnRzX2RmIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoc3BvcnRmaWVsZF9jb3VudHMpDQoNCnRwZV92aWxsX3NmJHNwb3J0ZmllbGRfY291bnQgPC0gc3BvcnRmaWVsZF9jb3VudHNfZGYkc3BvcnRmaWVsZF9jb3VudFttYXRjaCh0cGVfdmlsbF9zZiRUT1dOX0lELCBzcG9ydGZpZWxkX2NvdW50c19kZiRUT1dOX0lEKV0NCnRwZV92aWxsX3NmJHNwb3J0ZmllbGRfY291bnRbaXMubmEodHBlX3ZpbGxfc2Ykc3BvcnRmaWVsZF9jb3VudCldIDwtIDANCg0KdHBlX3ZpbGxfc2YkQVJFQSA8LSBzZXRfdW5pdHMoc3RfYXJlYSh0cGVfdmlsbF9zZiksICJrbV4yIikNCnRwZV92aWxsX3NmJHNwb3J0ZmllbGRfZGVuc2l0eSA8LSB0cGVfdmlsbF9zZiRzcG9ydGZpZWxkX2NvdW50IC8gdHBlX3ZpbGxfc2YkQVJFQQ0KDQpjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQodHBlX3ZpbGxfc2YpKQ0KDQojIDEwa20NCm5iIDwtIGRuZWFybmVpZ2goY29vcmRzLCBkMSA9IDAsIGQyID0gMTAwMDApDQoNCmRpc3RfbGlzdCA8LSBuYmRpc3RzKG5iLCBjb29yZHMpDQoNCmludl9kaXN0X2xpc3QgPC0gbGFwcGx5KGRpc3RfbGlzdCwgZnVuY3Rpb24oeCkgew0KICB5IDwtIDEgLyAoeCArIDAuMDAwMSkNCiAgcmV0dXJuKHkpDQp9KQ0KDQpsaXN0dyA8LSBuYjJsaXN0dyhuYiwgZ2xpc3QgPSBpbnZfZGlzdF9saXN0LCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQpkZW5zaXR5IDwtIGFzLnZlY3Rvcih0cGVfdmlsbF9zZiRzcG9ydGZpZWxkX2RlbnNpdHkpDQoNCmdsb2JhbF9tb3JhbiA8LSBtb3Jhbi50ZXN0KGRlbnNpdHksIGxpc3R3ID0gbGlzdHcsIHplcm8ucG9saWN5ID0gVFJVRSkNCnByaW50KGdsb2JhbF9tb3JhbikNCg0KbGlzYSA8LSBsb2NhbG1vcmFuKGRlbnNpdHksIGxpc3R3ID0gbGlzdHcsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KdHBlX3ZpbGxfc2YkbG9jYWxfSSA8LSBsaXNhWywgMV0NCnRwZV92aWxsX3NmJHpfc2NvcmUgPC0gbGlzYVssIDRdDQp0cGVfdmlsbF9zZiRwX3ZhbHVlIDwtIGxpc2FbLCA1XQ0KDQpxdWFkcmFudCA8LSBhdHRyKGxpc2EsICJxdWFkciIpJG1lYW4NCnF1YWRyYW50IDwtIGZhY3RvcihxdWFkcmFudCwgbGV2ZWxzID0gYyhsZXZlbHMocXVhZHJhbnQpLCAiTm9TaWciKSkNCg0KdHBlX3ZpbGxfc2YkcHZhbHVlX0ZEUiA8LSBwLmFkanVzdChsaXNhWywgNV0sIG1ldGhvZCA9ICJmZHIiKQ0KDQojIFNldHRpbmcgc2lnX2xldmVsDQpzaWduaWZfc3BvcnRmaWVsZCA9IDAuMDENCg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX3Nwb3J0ZmllbGQgPC0gIk5vU2lnIg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX3Nwb3J0ZmllbGRbdHBlX3ZpbGxfc2YkcHZhbHVlX0ZEUiA8IHNpZ25pZl9zcG9ydGZpZWxkICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA+IDAgJiBxdWFkcmFudCA9PSAiSGlnaC1IaWdoIl0gPC0gIkhpZ2gtSGlnaCINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9zcG9ydGZpZWxkW3RwZV92aWxsX3NmJHB2YWx1ZV9GRFIgPCBzaWduaWZfc3BvcnRmaWVsZCAmIHRwZV92aWxsX3NmJGxvY2FsX0kgPiAwICYgcXVhZHJhbnQgPT0gIkxvdy1Mb3ciXSA8LSAiTG93LUxvdyINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9zcG9ydGZpZWxkW3RwZV92aWxsX3NmJHB2YWx1ZV9GRFIgPCBzaWduaWZfc3BvcnRmaWVsZCAmIHRwZV92aWxsX3NmJGxvY2FsX0kgPCAwICYgcXVhZHJhbnQgPT0gIkhpZ2gtTG93Il0gPC0gIkhpZ2gtTG93Ig0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX3Nwb3J0ZmllbGRbdHBlX3ZpbGxfc2YkcHZhbHVlX0ZEUiA8IHNpZ25pZl9zcG9ydGZpZWxkICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA8IDAgJiBxdWFkcmFudCA9PSAiTG93LUhpZ2giXSA8LSAiTG93LUhpZ2giDQoNCnRwZV92aWxsX3NmJExJU0FfdHlwZV9zcG9ydGZpZWxkIDwtIGZhY3Rvcih0cGVfdmlsbF9zZiRMSVNBX3R5cGVfc3BvcnRmaWVsZCwNCiAgbGV2ZWxzID0gYygiSGlnaC1IaWdoIiwgIkxvdy1Mb3ciLCAiSGlnaC1Mb3ciLCAiTG93LUhpZ2giLCAiTm9TaWciKSkNCg0KbGlzYV9jb2xvcnMgPC0gYygNCiAgJ0hpZ2gtSGlnaCcgPSAncmVkJywNCiAgJ0xvdy1Mb3cnID0gJ2JsdWUnLA0KICAnSGlnaC1Mb3cnID0gJ3BpbmsnLA0KICAnTG93LUhpZ2gnID0gJ3NreWJsdWUyJywNCiAgJ05vU2lnJyA9ICdncmV5Jw0KKQ0KDQp0bV9zaGFwZSh0cGVfdmlsbF9zZikgKw0KICB0bV9wb2x5Z29ucygiTElTQV90eXBlX3Nwb3J0ZmllbGQiLA0KICAgICAgICAgICAgICBwYWxldHRlID0gbGlzYV9jb2xvcnMsDQogICAgICAgICAgICAgIHRpdGxlID0gIkxJU0EgQ2x1c3RlciBUeXBlIChTcG9ydCBGYWNpbGl0aWVzKSIpICsNCiAgDQogICMg5YW25LuW5bu65YyX546H6auY55qE5a245qChDQogIHRtX3NoYXBlKG90aGVyX3RvcDE1X3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImdyYXkiLCBzaGFwZSA9IDIxLCBib3JkZXIuY29sID0gIndoaXRlIikgKw0KICANCiAgIyDkupTomY4NCiAgdG1fc2hhcGUoZml2ZV90aWdlcnNfc2YpICsNCiAgdG1fZG90cyhzaXplID0gMC41LCBjb2wgPSAiYmxhY2siLCBzaGFwZSA9IDIxLCBib3JkZXIuY29sID0gIndoaXRlIikgKw0KICANCiAgIyDkuLvmloflrZcNCiAgdG1fdGV4dCgic2Nob29sX3Nob3J0IiwgDQogICAgICAgICAgc2l6ZSA9IDAuOSwgDQogICAgICAgICAgeG1vZCA9ICJ4bW9kIiwNCiAgICAgICAgICB5bW9kID0gInltb2QiLCANCiAgICAgICAgICBjb2wgPSAiYmxhY2siLCANCiAgICAgICAgICBmb250ZmFtaWx5ID0gIk5vdG8gU2FucyBUQyIsDQogICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpICsNCiAgDQogIHRtX2xheW91dCh0aXRsZSA9ICJMSVNBIE1hcCAoSW52ZXJzZSBEaXN0YW5jZSkgLSBTcG9ydCBGYWNpbGl0aWVzIiwNCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSkNCmBgYA0KDQojIyMgSG9zcGl0YWwNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMg6Kit5a6a5L+h5b+D5rC05rqWDQpzaWduaWZfaG9zcGl0YWwgPC0gMC4wMQ0KDQojIOeiuuS/nSBob3NwaXRhbCDos4fmlpnoiIfooYzmlL/ljYDkvb/nlKjlkIzmqKMgQ1JTDQpob3NwaXRhbF9zZiA8LSBzdF90cmFuc2Zvcm0oaG9zcGl0YWxfc2YsIHN0X2Nycyh0cGVfdmlsbF9zZikpDQoNCiMg5Yqg5YWl6KGM5pS/5Y2ADQpqb2luZWRfaG9zcGl0YWwgPC0gc3Rfam9pbihob3NwaXRhbF9zZiwgdHBlX3ZpbGxfc2YsIGpvaW4gPSBzdF93aXRoaW4pDQoNCiMg5q+P5YCL6KGM5pS/5Y2A55qE6Yar55mC6bue5pW46YePDQpob3NwaXRhbF9jb3VudHMgPC0gam9pbmVkX2hvc3BpdGFsICU+JQ0KICBncm91cF9ieShUT1dOX0lEKSAlPiUNCiAgc3VtbWFyaXNlKGhvc3BpdGFsX2NvdW50ID0gbigpKQ0KDQojIOenu+mZpCBnZW9tZXRyeQ0KaG9zcGl0YWxfY291bnRzX2RmIDwtIHN0X2Ryb3BfZ2VvbWV0cnkoaG9zcGl0YWxfY291bnRzKQ0KDQojIOWwjeaHiemAsuWOn+acrOeahOihjOaUv+WNgOWcluWxpA0KdHBlX3ZpbGxfc2YkaG9zcGl0YWxfY291bnQgPC0gaG9zcGl0YWxfY291bnRzX2RmJGhvc3BpdGFsX2NvdW50W21hdGNoKHRwZV92aWxsX3NmJFRPV05fSUQsIGhvc3BpdGFsX2NvdW50c19kZiRUT1dOX0lEKV0NCnRwZV92aWxsX3NmJGhvc3BpdGFsX2NvdW50W2lzLm5hKHRwZV92aWxsX3NmJGhvc3BpdGFsX2NvdW50KV0gPC0gMA0KDQojIOioiOeul+WvhuW6pu+8iOioreaWveaVuCAvIOWNgOWfn+mdouepje+8iQ0KdHBlX3ZpbGxfc2YkQVJFQSA8LSBzZXRfdW5pdHMoc3RfYXJlYSh0cGVfdmlsbF9zZiksICJrbV4yIikNCnRwZV92aWxsX3NmJGhvc3BpdGFsX2RlbnNpdHkgPC0gdHBlX3ZpbGxfc2YkaG9zcGl0YWxfY291bnQgLyB0cGVfdmlsbF9zZiRBUkVBDQoNCiMg6KiI566X6YSw6L+R55+p6ZmjDQpjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoc3RfY2VudHJvaWQodHBlX3ZpbGxfc2YpKQ0KbmIgPC0gZG5lYXJuZWlnaChjb29yZHMsIGQxID0gMCwgZDIgPSAxMDAwMCkNCg0KZGlzdF9saXN0IDwtIG5iZGlzdHMobmIsIGNvb3JkcykNCg0KaW52X2Rpc3RfbGlzdCA8LSBsYXBwbHkoZGlzdF9saXN0LCBmdW5jdGlvbih4KSB7DQogIDEgLyAoeCArIDAuMDAwMSkNCn0pDQoNCmxpc3R3IDwtIG5iMmxpc3R3KG5iLCBnbGlzdCA9IGludl9kaXN0X2xpc3QsIHN0eWxlID0gIlciLCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCiMgTElTQSDliIbmnpANCmRlbnNpdHkgPC0gYXMudmVjdG9yKHRwZV92aWxsX3NmJGhvc3BpdGFsX2RlbnNpdHkpDQoNCmdsb2JhbF9tb3JhbiA8LSBtb3Jhbi50ZXN0KGRlbnNpdHksIGxpc3R3ID0gbGlzdHcsIHplcm8ucG9saWN5ID0gVFJVRSkNCnByaW50KGdsb2JhbF9tb3JhbikNCg0KbGlzYSA8LSBsb2NhbG1vcmFuKGRlbnNpdHksIGxpc3R3ID0gbGlzdHcsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KdHBlX3ZpbGxfc2YkbG9jYWxfSSA8LSBsaXNhWywgMV0NCnRwZV92aWxsX3NmJHpfc2NvcmUgPC0gbGlzYVssIDRdDQp0cGVfdmlsbF9zZiRwX3ZhbHVlIDwtIGxpc2FbLCA1XQ0KDQpxdWFkcmFudCA8LSBhdHRyKGxpc2EsICJxdWFkciIpJG1lYW4NCnF1YWRyYW50IDwtIGZhY3RvcihxdWFkcmFudCwgbGV2ZWxzID0gYyhsZXZlbHMocXVhZHJhbnQpLCAiTm9TaWciKSkNCg0KdHBlX3ZpbGxfc2YkcHZhbHVlX0ZEUiA8LSBwLmFkanVzdChsaXNhWywgNV0sIG1ldGhvZCA9ICJmZHIiKQ0KDQojIOaMh+WumumhnuWeiw0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX0hvc3BpdGFsIDwtICJOb1NpZyINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9Ib3NwaXRhbFt0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2hvc3BpdGFsICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA+IDAgJiBxdWFkcmFudCA9PSAiSGlnaC1IaWdoIl0gPC0gIkhpZ2gtSGlnaCINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9Ib3NwaXRhbFt0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2hvc3BpdGFsICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA+IDAgJiBxdWFkcmFudCA9PSAiTG93LUxvdyJdIDwtICJMb3ctTG93Ig0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX0hvc3BpdGFsW3RwZV92aWxsX3NmJHB2YWx1ZV9GRFIgPCBzaWduaWZfaG9zcGl0YWwgJiB0cGVfdmlsbF9zZiRsb2NhbF9JIDwgMCAmIHF1YWRyYW50ID09ICJIaWdoLUxvdyJdIDwtICJIaWdoLUxvdyINCnRwZV92aWxsX3NmJExJU0FfdHlwZV9Ib3NwaXRhbFt0cGVfdmlsbF9zZiRwdmFsdWVfRkRSIDwgc2lnbmlmX2hvc3BpdGFsICYgdHBlX3ZpbGxfc2YkbG9jYWxfSSA8IDAgJiBxdWFkcmFudCA9PSAiTG93LUhpZ2giXSA8LSAiTG93LUhpZ2giDQoNCnRwZV92aWxsX3NmJExJU0FfdHlwZV9Ib3NwaXRhbCA8LSBmYWN0b3IodHBlX3ZpbGxfc2YkTElTQV90eXBlX0hvc3BpdGFsLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJIaWdoLUhpZ2giLCAiTG93LUxvdyIsICJIaWdoLUxvdyIsICJMb3ctSGlnaCIsICJOb1NpZyIpKQ0KDQojIOmFjeiJsg0KbGlzYV9jb2xvcnMgPC0gYygNCiAgJ0hpZ2gtSGlnaCcgPSAncmVkJywNCiAgJ0xvdy1Mb3cnID0gJ2JsdWUnLA0KICAnSGlnaC1Mb3cnID0gJ3BpbmsnLA0KICAnTG93LUhpZ2gnID0gJ3NreWJsdWUyJywNCiAgJ05vU2lnJyA9ICdncmV5Jw0KKQ0KDQojIOeVq+Wclg0KdG1fc2hhcGUodHBlX3ZpbGxfc2YpICsNCiAgdG1fcG9seWdvbnMoIkxJU0FfdHlwZV9Ib3NwaXRhbCIsDQogICAgICAgICAgICAgIHBhbGV0dGUgPSBsaXNhX2NvbG9ycywNCiAgICAgICAgICAgICAgdGl0bGUgPSAiTElTQSBDbHVzdGVyIFR5cGUgKEhvc3BpdGFscykiKSArDQogIA0KICAjIOWFtuS7luW7uuWMl+eOh+mrmOeahOWtuOagoQ0KICB0bV9zaGFwZShvdGhlcl90b3AxNV9zZikgKw0KICB0bV9kb3RzKHNpemUgPSAwLjUsIGNvbCA9ICJncmF5Iiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5LqU6JmODQogIHRtX3NoYXBlKGZpdmVfdGlnZXJzX3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImJsYWNrIiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5Li75paH5a2XDQogIHRtX3RleHQoInNjaG9vbF9zaG9ydCIsIA0KICAgICAgICAgIHNpemUgPSAwLjksIA0KICAgICAgICAgIHhtb2QgPSAieG1vZCIsDQogICAgICAgICAgeW1vZCA9ICJ5bW9kIiwgDQogICAgICAgICAgY29sID0gImJsYWNrIiwgDQogICAgICAgICAgZm9udGZhbWlseSA9ICJOb3RvIFNhbnMgVEMiLA0KICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiKSArDQogIA0KICB0bV9sYXlvdXQodGl0bGUgPSAiTElTQSBNYXAgKEludmVyc2UgRGlzdGFuY2UpIC0gSG9zcGl0YWxzIiwNCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSkNCmBgYA0KIyMjIFBhcmsgDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyAxLiDoqK3lrprkv6Hlv4PmsLTmupYNCnNpZ25pZl9wYXJrIDwtIDAuMDENCg0KIyAyLiDluqfmqJnovYnmj5sNCnBhcmtfc2YgPC0gc3RfdHJhbnNmb3JtKHBhcmtfc2YsIHN0X2Nycyh0cGVfdmlsbF9zZikpDQoNCiMgMy4gU3BhdGlhbCBqb2luIOiIh+ioiOaVuA0Kam9pbmVkX3BhcmsgPC0gc3Rfam9pbihwYXJrX3NmLCB0cGVfdmlsbF9zZiwgam9pbiA9IHN0X3dpdGhpbikNCg0KcGFya19jb3VudHMgPC0gam9pbmVkX3BhcmsgJT4lDQogIGdyb3VwX2J5KFRPV05fSUQpICU+JQ0KICBzdW1tYXJpc2UocGFya19jb3VudCA9IG4oKSkNCg0KcGFya19jb3VudHNfZGYgPC0gc3RfZHJvcF9nZW9tZXRyeShwYXJrX2NvdW50cykNCg0KIyA0LiDliqDlhaXoqIjmlbjoiIflr4bluqbmrITkvY0NCnRwZV92aWxsX3NmJHBhcmtfY291bnQgPC0gcGFya19jb3VudHNfZGYkcGFya19jb3VudFttYXRjaCh0cGVfdmlsbF9zZiRUT1dOX0lELCBwYXJrX2NvdW50c19kZiRUT1dOX0lEKV0NCnRwZV92aWxsX3NmJHBhcmtfY291bnRbaXMubmEodHBlX3ZpbGxfc2YkcGFya19jb3VudCldIDwtIDANCg0KdHBlX3ZpbGxfc2YkQVJFQSA8LSBzZXRfdW5pdHMoc3RfYXJlYSh0cGVfdmlsbF9zZiksICJrbV4yIikNCnRwZV92aWxsX3NmJHBhcmtfZGVuc2l0eSA8LSB0cGVfdmlsbF9zZiRwYXJrX2NvdW50IC8gdHBlX3ZpbGxfc2YkQVJFQQ0KDQojIDUuIOioiOeul+mEsOi/keefqemZo+iIh+epuumWk+asiumHjQ0KY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKHRwZV92aWxsX3NmKSkNCg0KbmIgPC0gZG5lYXJuZWlnaChjb29yZHMsIGQxID0gMCwgZDIgPSAxMDAwMCkNCmRpc3RfbGlzdCA8LSBuYmRpc3RzKG5iLCBjb29yZHMpDQoNCmludl9kaXN0X2xpc3QgPC0gbGFwcGx5KGRpc3RfbGlzdCwgZnVuY3Rpb24oeCkgew0KICAxIC8gKHggKyAwLjAwMDEpDQp9KQ0KDQpsaXN0dyA8LSBuYjJsaXN0dyhuYiwgZ2xpc3QgPSBpbnZfZGlzdF9saXN0LCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQojIDYuIEdsb2JhbCAmIExvY2FsIE1vcmFuJ3MgSQ0KZGVuc2l0eSA8LSBhcy52ZWN0b3IodHBlX3ZpbGxfc2YkcGFya19kZW5zaXR5KQ0KDQpnbG9iYWxfbW9yYW4gPC0gbW9yYW4udGVzdChkZW5zaXR5LCBsaXN0dyA9IGxpc3R3LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQpwcmludChnbG9iYWxfbW9yYW4pDQoNCmxpc2EgPC0gbG9jYWxtb3JhbihkZW5zaXR5LCBsaXN0dyA9IGxpc3R3LCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCiMgNy4g5Yqg5YWlIExJU0Eg5oyH5qiZDQp0cGVfdmlsbF9zZiRsb2NhbF9JX3BhcmsgPC0gbGlzYVssIDFdDQp0cGVfdmlsbF9zZiR6X3Njb3JlX3BhcmsgPC0gbGlzYVssIDRdDQp0cGVfdmlsbF9zZiRwX3ZhbHVlX3BhcmsgPC0gbGlzYVssIDVdDQoNCnF1YWRyYW50X3BhcmsgPC0gYXR0cihsaXNhLCAicXVhZHIiKSRtZWFuDQpxdWFkcmFudF9wYXJrIDwtIGZhY3RvcihxdWFkcmFudF9wYXJrLCBsZXZlbHMgPSBjKGxldmVscyhxdWFkcmFudF9wYXJrKSwgIk5vU2lnIikpDQoNCiMgRkRSIOagoeatow0KdHBlX3ZpbGxfc2YkcHZhbHVlX0ZEUl9wYXJrIDwtIHAuYWRqdXN0KHRwZV92aWxsX3NmJHBfdmFsdWVfcGFyaywgbWV0aG9kID0gImZkciIpDQoNCiMg5YiG576kDQp0cGVfdmlsbF9zZiRMSVNBX3R5cGVfUGFyayA8LSAiTm9TaWciDQp0cGVfdmlsbF9zZiRMSVNBX3R5cGVfUGFya1t0cGVfdmlsbF9zZiRwdmFsdWVfRkRSX3BhcmsgPCBzaWduaWZfcGFyayAmIHRwZV92aWxsX3NmJGxvY2FsX0lfcGFyayA+IDAgJiBxdWFkcmFudF9wYXJrID09ICJIaWdoLUhpZ2giXSA8LSAiSGlnaC1IaWdoIg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX1BhcmtbdHBlX3ZpbGxfc2YkcHZhbHVlX0ZEUl9wYXJrIDwgc2lnbmlmX3BhcmsgJiB0cGVfdmlsbF9zZiRsb2NhbF9JX3BhcmsgPiAwICYgcXVhZHJhbnRfcGFyayA9PSAiTG93LUxvdyJdIDwtICJMb3ctTG93Ig0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX1BhcmtbdHBlX3ZpbGxfc2YkcHZhbHVlX0ZEUl9wYXJrIDwgc2lnbmlmX3BhcmsgJiB0cGVfdmlsbF9zZiRsb2NhbF9JX3BhcmsgPCAwICYgcXVhZHJhbnRfcGFyayA9PSAiSGlnaC1Mb3ciXSA8LSAiSGlnaC1Mb3ciDQp0cGVfdmlsbF9zZiRMSVNBX3R5cGVfUGFya1t0cGVfdmlsbF9zZiRwdmFsdWVfRkRSX3BhcmsgPCBzaWduaWZfcGFyayAmIHRwZV92aWxsX3NmJGxvY2FsX0lfcGFyayA8IDAgJiBxdWFkcmFudF9wYXJrID09ICJMb3ctSGlnaCJdIDwtICJMb3ctSGlnaCINCg0KdHBlX3ZpbGxfc2YkTElTQV90eXBlX1BhcmsgPC0gZmFjdG9yKHRwZV92aWxsX3NmJExJU0FfdHlwZV9QYXJrLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkhpZ2gtSGlnaCIsICJMb3ctTG93IiwgIkhpZ2gtTG93IiwgIkxvdy1IaWdoIiwgIk5vU2lnIikpDQoNCiMg55Wr5ZyWDQp0bV9zaGFwZSh0cGVfdmlsbF9zZikgKw0KICB0bV9wb2x5Z29ucygiTElTQV90eXBlX1BhcmsiLA0KICAgICAgICAgICAgICBwYWxldHRlID0gbGlzYV9jb2xvcnMsDQogICAgICAgICAgICAgIHRpdGxlID0gIkxJU0EgQ2x1c3RlciBUeXBlIChQYXJrcykiKSArDQogIA0KICAjIOWFtuS7luW7uuWMl+eOh+mrmOeahOWtuOagoQ0KICB0bV9zaGFwZShvdGhlcl90b3AxNV9zZikgKw0KICB0bV9kb3RzKHNpemUgPSAwLjUsIGNvbCA9ICJncmF5Iiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5LqU6JmODQogIHRtX3NoYXBlKGZpdmVfdGlnZXJzX3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImJsYWNrIiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCiAgDQogICMg5Li75paH5a2XDQogIHRtX3RleHQoInNjaG9vbF9zaG9ydCIsIA0KICAgICAgICAgIHNpemUgPSAwLjksIA0KICAgICAgICAgIHhtb2QgPSAieG1vZCIsDQogICAgICAgICAgeW1vZCA9ICJ5bW9kIiwgDQogICAgICAgICAgY29sID0gImJsYWNrIiwgDQogICAgICAgICAgZm9udGZhbWlseSA9ICJOb3RvIFNhbnMgVEMiLA0KICAgICAgICAgIGZvbnRmYWNlID0gImJvbGQiKSArDQogIA0KICB0bV9sYXlvdXQodGl0bGUgPSAiTElTQSBNYXAgKEludmVyc2UgRGlzdGFuY2UpIC0gUGFya3MiLA0KICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KYGBgDQoNCiMjIyBpbnRlcnNlY3Rpb24NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMg5bu656uLIEhpZ2gtSGlnaCDoiIcgTG93LUxvdyDnmoTkuqTpm4bmrITkvY0NCnRwZV92aWxsX3NmJExJU0FfSEhIX2ludGVyc2VjdGlvbiA8LSB3aXRoKHRwZV92aWxsX3NmLA0KICBMSVNBX3R5cGVfUGFyayA9PSAiSGlnaC1IaWdoIiAmDQogIExJU0FfdHlwZV9zcG9ydGZpZWxkID09ICJIaWdoLUhpZ2giICYNCiAgTElTQV90eXBlX0hvc3BpdGFsID09ICJIaWdoLUhpZ2giDQopDQoNCnRwZV92aWxsX3NmJExJU0FfTExMX2ludGVyc2VjdGlvbiA8LSB3aXRoKHRwZV92aWxsX3NmLA0KICBMSVNBX3R5cGVfUGFyayA9PSAiTG93LUxvdyIgJg0KICBMSVNBX3R5cGVfc3BvcnRmaWVsZCA9PSAiTG93LUxvdyIgJg0KICBMSVNBX3R5cGVfSG9zcGl0YWwgPT0gIkxvdy1Mb3ciDQopDQoNCiMg5bu656uL5LiA5YCL5paw55qE5qyE5L2N5qiZ56S65Lqk6ZuG6aGe5Z6LDQp0cGVfdmlsbF9zZiRMSVNBX2ludGVyc2VjdGlvbl90eXBlIDwtICJPdGhlciINCnRwZV92aWxsX3NmJExJU0FfaW50ZXJzZWN0aW9uX3R5cGVbdHBlX3ZpbGxfc2YkTElTQV9ISEhfaW50ZXJzZWN0aW9uXSA8LSAiQWxsIEhpZ2gtSGlnaCINCnRwZV92aWxsX3NmJExJU0FfaW50ZXJzZWN0aW9uX3R5cGVbdHBlX3ZpbGxfc2YkTElTQV9MTExfaW50ZXJzZWN0aW9uXSA8LSAiQWxsIExvdy1Mb3ciDQp0cGVfdmlsbF9zZiRMSVNBX2ludGVyc2VjdGlvbl90eXBlIDwtIGZhY3RvcigNCiAgdHBlX3ZpbGxfc2YkTElTQV9pbnRlcnNlY3Rpb25fdHlwZSwNCiAgbGV2ZWxzID0gYygiT3RoZXIiLCAiQWxsIEhpZ2gtSGlnaCIsICJBbGwgTG93LUxvdyIpDQopDQoNCiMg55Wr5ZyWDQp0bV9zaGFwZSh0cGVfdmlsbF9zZikgKw0KICAjIOeVq+WHuuS6pOmbhuWNgOWfnw0KICB0bV9wb2x5Z29ucygiTElTQV9pbnRlcnNlY3Rpb25fdHlwZSIsDQogICAgICAgICAgICAgIHBhbGV0dGUgPSBjKCJncmF5IiwgImdyZWVuIiwgImRhcmtyZWQiKSwNCiAgICAgICAgICAgICAgbGFiZWxzID0gYygiTm90IGFsbCBzYW1lIHR5cGUiLCAiQWxsIEhpZ2gtSGlnaCIsICJBbGwgTG93LUxvdyIpLA0KICAgICAgICAgICAgICB0aXRsZSA9ICJMSVNBIEludGVyc2VjdGlvbiBUeXBlIikgKw0KDQogICMg55aK5LiK5YW25LuW5bu65YyX546H6auY55qE5a245qChDQogIHRtX3NoYXBlKG90aGVyX3RvcDE1X3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImdyYXkiLCBzaGFwZSA9IDIxLCBib3JkZXIuY29sID0gIndoaXRlIikgKw0KDQogICMg55aK5LiK5LqU6JmODQogIHRtX3NoYXBlKGZpdmVfdGlnZXJzX3NmKSArDQogIHRtX2RvdHMoc2l6ZSA9IDAuNSwgY29sID0gImJsYWNrIiwgc2hhcGUgPSAyMSwgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIpICsNCg0KICAjIOWKoOS4iuWtuOagoeaWh+Wtl+aomeexpA0KICB0bV90ZXh0KCJzY2hvb2xfc2hvcnQiLCANCiAgICAgICAgICBzaXplID0gMC45LCANCiAgICAgICAgICB4bW9kID0gInhtb2QiLCANCiAgICAgICAgICB5bW9kID0gInltb2QiLCANCiAgICAgICAgICBjb2wgPSAiYmxhY2siLCANCiAgICAgICAgICBmb250ZmFtaWx5ID0gIk5vdG8gU2FucyBUQyIsDQogICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIpICsNCg0KICAjIOaomemhjOiIh+WcluS+i+mFjee9rg0KICB0bV9sYXlvdXQodGl0bGUgPSAiSW50ZXJzZWN0aW9uIG9mIExJU0EgQ2x1c3RlcnMgKEhpZ2gtSGlnaCBhbmQgTG93LUxvdykiLA0KICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KYGBg