rm(list = ls())
setwd("C:/Users/618am/OneDrive/桌面/空間分析期末報告")

library(sf)
library(tmap)
library(ggplot2)
library(dplyr)
library(units)
library(reshape2)
library(aspace)
library(spatstat)
library(spdep)
library(tidyverse)
library(pals)
library(cartography)
library(readr)
Popn_TWN2 <- st_read("C:/Users/618am/OneDrive/桌面/空間分析期末報告/Popn_TWN2", options = "ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Popn_TWN2' from data source 
  `C:\Users\618am\OneDrive\桌面\空間分析期末報告\Popn_TWN2' using driver `ESRI Shapefile'
Simple feature collection with 368 features and 14 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -197572.5 ymin: 2295201 xmax: 606875.6 ymax: 2919551
Projected CRS: TWD97 / TM2 zone 121
SCHOOL <- st_read("C:/Users/618am/OneDrive/桌面/空間分析期末報告/SCHOOL", options = "ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `SCHOOL' from data source 
  `C:\Users\618am\OneDrive\桌面\空間分析期末報告\SCHOOL' using driver `ESRI Shapefile'
Simple feature collection with 148 features and 4 fields
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: 297078.6 ymin: 2763290 xmax: 312516.7 ymax: 2784542
Projected CRS: TWD97 / TM2 zone 121
Tpe_Fastfood <- st_read("C:/users/618am/OneDrive/桌面/空間分析期末報告/Tpe_Fastfood", options = "ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Tpe_Fastfood' from data source 
  `C:\Users\618am\OneDrive\桌面\空間分析期末報告\Tpe_Fastfood' 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
TPE_Vill <- st_read("C:/Users/618am/OneDrive/桌面/空間分析期末報告/Taipei_Vill/Taipei_Vill.shp", options = "ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Taipei_Vill' from data source 
  `C:\Users\618am\OneDrive\桌面\空間分析期末報告\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
TPE_Village_basemap <- tm_shape(TPE_Vill) +
  tm_polygons(col = "grey") +
  tm_layout(title = "臺北市公私立國小與速食店點位分布圖")
[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
SCHOOL_map <- tm_shape(SCHOOL) +
  tm_dots(fill = "blue", size = 0.25)

Fastfood_map <- tm_shape(Tpe_Fastfood) +
  tm_dots(fill = "red", size = 0.25)

TPE_Village_basemap + SCHOOL_map + Fastfood_map + tm_add_legend(type = "symbol",
              labels = c("學校", "速食店"),
              col = c("blue", "red"),
              title = "類別") + tm_scale_bar() + tm_compass() + tm_layout()
[v3->v4] `tm_add_legend()`: use `type = "symbols"` instead of `type = "symbol"`.[v3->v4] `tm_add_legend()`: use `fill` instead of `col` for the fill color of symbols.! `tm_scale_bar()` is deprecated. Please use `tm_scalebar()` instead.

Part 1: 不同行政區的速食店有不同的地理分布特徵嗎?

問答題

圖 1 是某城市各村里速食店密度的 Moran Scatter Plot。

(1)

簡述該圖表的 X 軸與 Y 軸所表示的意涵,以及其空間型態。

X 軸代表各空間單位的速食店密度,經過標準化處理。數值越高,表示該區域速食店密度高於平均。 Y 軸表示「鄰近區域的速食店密度」的平均值,也經過標準化。這是計算空間自相關的關鍵:看某一區的速食店密度與其鄰近區域的密度是否一致。

(2)

請簡述該圖趨勢線(Fit)的斜率值 (slope =0.982),所代表的意涵。

數值代表空間自相關的強度, 趨勢線的斜率近似 1,表示變數(速食店密度)與其鄰近區域的值之間有非常強的正向線性關係。

(3)

關於該城市各村里速食店密度的 Local Moran’s I 統計量,請問其統計量可能會大於 0、等於 0 或小於 0,並說明判斷的理由。

Local Moran’s I 可能大於 0(高-高、低-低聚集)、小於 0(離群區,如高-低、低-高),或等於 0(無空間關聯)。即使整體呈現正向空間自相關,局部仍可能存在異常點與離群現象,因此需使用 Local Moran’s I 進行細部判斷。

實作題

(1)

假設速食店的服務範圍是 1 公里,比較 A 區(文山+大安+中正) 與 B 區(信義+南港+松山) 這兩個地區的每一家速食店在服務可及範圍內,所涵蓋學校數量的平均值,是否有統計顯著差異。(需列出虛無假設與對立假設,統計檢定量,以及檢定的顯著水準等)。

# 定義 A 區、B 區行政區名稱
A_towns <- c("文山區","大安區","中正區")
B_towns <- c("信義區","南港區","松山區")

# 1. 建立 1km 緩衝區 around each fastfood 
fast_buf <- st_buffer(Tpe_Fastfood, dist = set_units(1, km))

# 2. 空間連結:每家速食店緩衝內的學校數量
ff_school <- st_join(fast_buf, SCHOOL, join = st_contains)
counts <- ff_school %>% 
  st_drop_geometry() %>% 
  group_by(ID) %>%               
  summarise(school_n = n())

# 3. 對速食店附加其所屬行政區
ff_town <- st_join(Tpe_Fastfood, TPE_Vill, join = st_within)
ff_stats <- left_join(ff_town, counts, by = "ID")

# 4. 加入 A/B 類別標籤
ff_stats <- ff_stats %>%
  mutate(area_group = case_when(
    TOWN %in% A_towns ~ "A區",
    TOWN %in% B_towns ~ "B區",
    TRUE              ~ NA_character_
  )) %>%
  filter(!is.na(area_group))

# 5. 列出 A、B 區中每家速食店 的 school_n 向量,
#     並做 t.test(或 Wilcox test)
A_vals <- ff_stats$school_n[ff_stats$area_group=="A區"]
B_vals <- ff_stats$school_n[ff_stats$area_group=="B區"]

# 虛無假設 H0: μ_A = μ_B;對立假設 H1: μ_A ≠ μ_B
t_res <- t.test(A_vals, B_vals, var.equal = FALSE)
t_res

    Welch Two Sample t-test

data:  A_vals and B_vals
t = 1.5977, df = 51, p-value = 0.1163
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -0.1418044  1.2473762
sample estimates:
mean of x mean of y 
 3.870968  3.318182 

虛無假設(H₀):A 區與 B 區的速食店服務範圍內涵蓋的學校數平均值相同(μ₁ = μ₂)。 對立假設(H₁):A 區與 B 區的速食店涵蓋學校數平均值不同(μ₁ ≠ μ₂)。

A區與B區速食店涵蓋的學校數平均值無顯著差異(p = 0.1163 > 0.05),無法拒絕虛無假設。顯示兩區速食店服務效能在統計上無明顯差異。

(2)

以 500 公尺方格的空間單位建立網格,依照 contiguity 鄰近定義,計算並繪製速食店總數的 Moran’s correlogram,解讀其圖表資訊,並評估速食店的空間影響範圍。

# 1. 建立 500 公尺格網,並裁切至台北市行政區內部
grid_500 <- st_make_grid(TPE_Vill, cellsize = 500, what = "polygons") %>%
  st_sf() %>%
  st_intersection(st_union(TPE_Vill))

grid_500$grid_id <- 1:nrow(grid_500)

# 2. 空間連結速食店到格網,統計每格速食店數量
grid_join <- st_join(Tpe_Fastfood, grid_500, join = st_within)
count_grid <- grid_join %>%
  group_by(grid_id) %>%
  summarise(n = n())

# 3. 將計數結果合併回格網資料,將 NA 補為 0(無速食店)
count_grid_nogeo <- count_grid %>% st_drop_geometry()
grid_500 <- left_join(grid_500, count_grid_nogeo, by = "grid_id") %>%
  mutate(n = replace_na(n, 0))

# 4. 建立格網之鄰接矩陣(以 Queen 鄰接為準)
nb_grid <- poly2nb(grid_500, queen = TRUE)
lw_grid <- nb2listw(nb_grid, style = "W", zero.policy = TRUE)

# 5. 提取速食店數值向量
n_vector <- grid_500$n

# 6. 計算 Moran’s I Correlogram(鄰近階數從 1~6)
moran_correlogram <- sp.correlogram(
  nb_grid, 
  n_vector, 
  order = 6, 
  method = "I", 
  style = "W", 
  zero.policy = TRUE
)

# 7. 繪製 correlogram 結果圖
plot(moran_correlogram, main = "Moran's I Correlogram of Fast Food Counts (500m Grid)")

Moran’s I 在第一階層鄰近(Lag 1)顯著為正,顯示速食店有明確的局部群聚性,但隨距離增加自相關性下降,影響範圍約在 500–1000 公尺內。

tm_shape(grid_500) +
  tm_polygons(col = "n", palette = "YlOrRd", title = "速食店數") +
  tm_layout(title = "臺北市 500m 網格中速食店分布")

── 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 = )`[cols4all] color palettes: use palettes from the R package cols4all. Run `cols4all::c4a_gui()` to
explore them. The old palette name "YlOrRd" is named "brewer.yl_or_rd"Multiple palettes called "yl_or_rd" found: "brewer.yl_or_rd", "matplotlib.yl_or_rd". The first one, "brewer.yl_or_rd", is returned.

(3)

依照前一小題的網格為空間單位,依照 contiguity 鄰近定義,計算 Gi*統計量,分別繪製速食店家數與學校在 0.05 顯著水準的熱區分佈(需進行多重檢定的修正)。

school_join <- st_join(SCHOOL, grid_500, join = st_within)
school_count <- school_join %>%
  group_by(grid_id) %>%
  summarise(schools = n())

school_count_nogeo <- school_count %>% st_drop_geometry()
grid_500 <- left_join(grid_500, school_count_nogeo, by = "grid_id") %>%
  mutate(schools = replace_na(schools, 0))

nb_grid <- poly2nb(grid_500, queen = TRUE)
lw_grid <- nb2listw(nb_grid, style = "W", zero.policy = TRUE)

# 計算 Gi* 統計量
Gi_fast <- localG(grid_500$n, lw_grid)
Gi_school <- localG(grid_500$schools, lw_grid)

# 計算 p-value 並進行 FDR 修正(雙尾)
grid_500$Gi_fast <- as.numeric(Gi_fast)
grid_500$p_fast <- 2 * pnorm(-abs(grid_500$Gi_fast))
grid_500$FDR_fast <- p.adjust(grid_500$p_fast, method = "fdr")
grid_500$Gi_school <- as.numeric(Gi_school)
grid_500$p_school <- 2 * pnorm(-abs(grid_500$Gi_school))
grid_500$FDR_school <- p.adjust(grid_500$p_school, method = "fdr")

# 熱區分類標籤(顯著性標示)
grid_500 <- grid_500 %>%
  mutate(hotspot_fast = case_when(
    FDR_fast < 0.05 & Gi_fast > 0 ~ "High-High",
    FDR_fast < 0.05 & Gi_fast < 0 ~ "Low-Low",
    TRUE ~ "Not significant"
  )) %>%
  mutate(hotspot_school = case_when(
    FDR_school < 0.05 & Gi_school > 0 ~ "High-High",
    FDR_school < 0.05 & Gi_school < 0 ~ "Low-Low",
    TRUE ~ "Not significant"
  ))

# 熱區圖繪製(速食店)
tm_shape(grid_500) +
  tm_fill("hotspot_fast",
          palette = c("red", "white", "gray80"),
          title = "速食店熱區",
          labels = c("High-High", "Low-Low", "Not significant")) +
  tm_borders() +
  tm_layout(title = "速食店 Gi* 熱區分析 (FDR < 0.05)")

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_tm_fill()`: migrate the argument(s) related to the scale of the visual variable
`fill` namely 'palette' (rename to 'values'), 'labels' to fill.scale = tm_scale(<HERE>).[v3->v4] `tm_fill()`: 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 = )`警告: labels do not have the same length as levels, so they are repeated

# 熱區圖繪製(學校)
tm_shape(grid_500) +
  tm_fill("hotspot_school",
          palette = c("red", "white", "gray80"),
          title = "學校熱區",
          labels = c("High-High", "Low-Low", "Not significant")) +
  tm_borders() +
  tm_layout(title = "學校 Gi* 熱區分析 (FDR < 0.05)")

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_tm_fill()`: migrate the argument(s) related to the scale of the visual variable
`fill` namely 'palette' (rename to 'values'), 'labels' to fill.scale = tm_scale(<HERE>).[v3->v4] `tm_fill()`: 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 = )`警告: labels do not have the same length as levels, so they are repeated

速食店與學校的熱區皆集中於臺北市西南部,顯示兩者空間分布具有重疊群聚現象。該區可視為學生容易接觸速食的高風險環境。

Part 2: 學校附近的速食店數量真的比較多嗎?

(1)

請參考 Transportation Research Part A, 45 (2011) 640–652(HW-09 的研讀教材)所使用 F(d)函數的作法,比較分別位於大安區、文山區與信義區的學校,到鄰近速食店的空間特徵。

SCHOOL <- st_join(SCHOOL, TPE_Vill[, c("TOWN")], join = st_within)
table(SCHOOL$TOWN)

士林區 大同區 大安區 中山區 中正區 內湖區 文山區 北投區 松山區 信義區 南港區 萬華區 
    20      9     13     11      8     12     22     17      8      9      7     12 
districts <- c("大安區", "文山區", "信義區")
schools_by_district <- SCHOOL %>% filter(TOWN %in% districts)

# 計算距離最近速食店的距離
dist_list <- lapply(districts, function(dist){
  sch <- schools_by_district %>% filter(TOWN == dist)
  m <- st_distance(sch, Tpe_Fastfood)
  dmin <- apply(m, 1, min) %>% set_units("m") %>% drop_units()
  data.frame(
    district = dist,
    dist_m   = dmin
  )
})
df_dist <- bind_rows(dist_list)

# 繪製 F(d) ECDF 圖
ggplot(df_dist, aes(x = dist_m, colour = district)) +
  stat_ecdf(size = 1.2) +
  scale_x_continuous(limits = c(0, 2000),
                     breaks = seq(0, 2000, 500),
                     labels = function(x) paste0(x, "m")) +
  labs(
    title = "臺北市三區學校到最近速食店的 F(d) 距離分布",
    x = "距離 d(公尺)",
    y = "F(d) = 學校距離 ≤ d 的比例",
    colour = "行政區"
  ) +
  theme_minimal() +
  theme(text = element_text(family = "Heiti TC"), legend.position = "bottom")


# KS檢定(兩兩組合)
ks_results <- combn(districts, 2, simplify = FALSE, FUN = function(pair){
  d1 <- df_dist %>% filter(district == pair[1]) %>% pull(dist_m)
  d2 <- df_dist %>% filter(district == pair[2]) %>% pull(dist_m)
  kt <- ks.test(d1, d2)
  data.frame(
    group1 = pair[1],
    group2 = pair[2],
    statistic = kt$statistic,
    p_value  = kt$p.value
  )
})
ks_df <- bind_rows(ks_results)
print(ks_df)

信義區學校距速食店最近,文山區最遠,顯示信義區速食店密度高、可及性強;文山區則相對分散,學生暴露速食環境風險較低。

(2)

利用 Ripley’s K function, k(d)計算速食店在 distance (d) = 3000 公尺時的 K(3000) 以及L(3000),並使用 Monte Carlo significance test 計算 L(3000)的 95%信賴區間。

# 1. 準備 ppp 資料(速食店點與分析範圍)
# 轉換為 spatstat 分析視窗(以 TPE_Vill 範圍為主)
win <- as.owin(st_union(TPE_Vill))
fast_xy <- st_coordinates(Tpe_Fastfood)
ppp_fast <- ppp(x = fast_xy[,1], y = fast_xy[,2], window = win)
警告: data contain duplicated points
# 2. 計算 K(d) 與 L(d)(使用 border 修正)
K_result <- Kest(ppp_fast, correction = "border")
L_result <- Lest(ppp_fast, correction = "border")

# 3. 提取 d = 3000 時的 K 與 L 值
target_d <- 3000
k_index  <- which.min(abs(K_result$r - target_d)) 

k_r_3000 <- K_result$r[k_index]                    
k_3000   <- K_result$border[k_index]               
l_3000   <- L_result$border[k_index]               

cat("K(", k_r_3000, ") =", round(k_3000, 2), "\n")
K( 2998.733 ) = 67640147 
cat("L(", k_r_3000, ") =", round(l_3000, 2), "\n")
L( 2998.733 ) = 4640.1 
# 4. Monte Carlo 模擬信賴區間(99 次 CSR 模擬)
set.seed(1234)
L_env <- envelope(
  ppp_fast,
  fun = Lest,
  nsim = 99,
  correction = "border",
  global = FALSE,
  simulate = expression(rpoispp(lambda = intensity(ppp_fast), win = win))
)
Generating 99 simulations by evaluating expression  ...
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96,
97, 98, 
99.

Done.
L_3000_obs <- L_env$obs[k_index]
L_3000_lo  <- L_env$lo[k_index]
L_3000_hi  <- L_env$hi[k_index]

cat("L(", k_r_3000, ") =", round(L_3000_obs, 2), 
    "\n95% CI = [", round(L_3000_lo, 2), ",", round(L_3000_hi, 2), "]\n")
L( 2998.733 ) = 4640.1 
95% CI = [ 2357.99 , 3418.9 ]
# 5. 繪圖:Ripley's L with 信賴區間與標示線 
plot(L_env,
     main = "Ripley's L function with 95% Monte Carlo Envelope",
     xlab = "距離 d (公尺)", ylab = "L(d)",
     xlim = c(0, 4000),
     legend = FALSE)
abline(h = 0, col = "gray30", lty = 2)             
abline(v = k_r_3000, col = "red", lty = 3)

# 在圖上加上實際值與信賴區間的點
points(x = k_r_3000, y = L_3000_obs, pch = 16, col = "black")
segments(x0 = k_r_3000, x1 = k_r_3000, y0 = L_3000_lo, y1 = L_3000_hi, col = "darkblue", lwd = 2)

L(3000) 值顯著高於 CSR 模擬信賴區間,表示在 3 公里尺度下速食店存在顯著空間群聚,顯示其分布集中且非隨機。

(3)

利用 Bivariate F function 分析方法,比較公立 vs.私立學校,到鄰近速食店的空間特徵,並說明哪一種類型學校的附近,會有較多的速食店?(顯著水準=0.05)

table(SCHOOL$Type)

公立 私立 
  51   97 
school_public <- SCHOOL %>% filter(Type == "公立")
school_private <- SCHOOL %>% filter(Type == "私立")

# 計算兩群學校到最近速食店的距離
get_nearest_dist <- function(school_sf, fast_sf){
  dist_matrix <- st_distance(school_sf, fast_sf)
  dmin <- apply(dist_matrix, 1, min) %>% set_units("m") %>% drop_units()
  return(dmin)
}

dist_pub <- get_nearest_dist(school_public, Tpe_Fastfood)
dist_pri <- get_nearest_dist(school_private, Tpe_Fastfood)

# 合併資料
df_dist <- data.frame(
  dist_m = c(dist_pub, dist_pri),
  Type   = c(rep("公立", length(dist_pub)), rep("私立", length(dist_pri)))
)

# 繪圖:F(d) 比較(使用 ECDF)
ggplot(df_dist, aes(x = dist_m, colour = Type)) +
  stat_ecdf(size = 1.2) +
  scale_x_continuous(limits = c(0, 2000),
                     breaks = seq(0,2000,500),
                     labels = function(x) paste0(x, "m")) +
  labs(
    title = "公私立學校到速食店的最近距離 F(d) 比較",
    x     = "距離 d(公尺)",
    y     = "F(d) = 學校距離 ≤ d 的比例",
    colour= "學校類型"
  ) +
  theme_minimal() +
  theme(text = element_text(family = "Heiti TC"), legend.position = "bottom")


# KS 檢定:比較距離分布是否顯著不同
ks_test <- ks.test(dist_pub, dist_pri)
print(ks_test)

    Exact two-sample Kolmogorov-Smirnov test

data:  dist_pub and dist_pri
D = 0.35052, p-value = 0.0003497
alternative hypothesis: two-sided

公立學校 F(d)曲線上升較快,表示距離速食店較近、可及性更高。私立學校相對距離較遠,顯示公立學校周邊速食密度較高。

Part 3: 校園健康生活計畫

根據臺北市衛生局於113年公布之健康元素餐飲業者清冊,記錄每間健康飲食餐廳點位,並針對臺北市每間國小方圓一公里進行環域分析,記錄每間學校環域內健康飲食店家,提供學童在外食上更多元且營養豐富的消費選擇,並針對環域內未記錄到健康飲食店家之學校制定進一步校園健康飲食計畫。

healthy_restaurant <- read.csv("C:/Users/618am/OneDrive/桌面/空間分析期末報告/臺北市健康飲食餐廳資料表 - healthy_food_shops_with_address.csv", fileEncoding = "UTF-8")
head(healthy_restaurant)
healthy_geom <- st_as_sf(
  healthy_restaurant,
  coords = c("經度", "緯度"),
  crs = 4326
)

healthy_geom_3826 <- st_transform(healthy_geom, 3826)
SCHOOL_buffer <- st_buffer(SCHOOL, dist = 1000)

school_healthy <- st_join(SCHOOL_buffer, healthy_geom_3826, join = st_intersects, left = TRUE)
summary_table <- school_healthy %>%
  st_drop_geometry() %>%
  group_by(Name) %>%   
  summarise(店家數 = sum(!is.na(商家名稱)))  
school_healthy_info <- school_healthy %>%
  st_drop_geometry()
SCHOOL_enriched <- SCHOOL %>%
  left_join(school_healthy_info, by = "SID")

healthy_geom_df <- healthy_geom_3826 %>%
  mutate(healthy_geom = geometry) %>%
  st_drop_geometry() %>%
  select(商家名稱, healthy_geom)

school_healthy_distance <- SCHOOL_enriched %>%
  left_join(healthy_geom_df, by = "商家名稱") %>%
  mutate(距離 = as.numeric(st_distance(geometry, healthy_geom, by_element = TRUE)))
schools_no_restaurant <- school_healthy %>% filter(is.na(商家名稱))
schools_no_restaurant$Name
 [1] "民權國小" "三民國小" "大直國小" "永安國小" "濱江國小" "博嘉國小" "萬芳國小" "力行國小"
 [9] "東湖國小" "明湖國小" "南湖國小" "麗湖國小" "陽明國小" "社子國小" "富安國小" "劍潭國小"
[17] "溪山國小" "平等國小" "百齡國小" "雙溪國小" "葫蘆國小" "湖田國小" "泉源國小" "大屯國小"
[25] "湖山國小"

一公里環域內無健康飲食餐廳學校

TPE_Village_basemap_2 <- tm_shape(TPE_Vill) +
  tm_polygons(col = "grey") +
  tm_layout(title = "臺北市公私立國小與健康飲食餐廳與速食店點位分布圖")
[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(title = )`
SCHOOL_map <- tm_shape(SCHOOL) +
  tm_dots(fill = "blue", size = 0.25)

Healthy_food_map <- tm_shape(healthy_geom_3826) +
  tm_dots(fill = "green", size = 0.25)

TPE_Village_basemap_2 + SCHOOL_map + Healthy_food_map+ Fastfood_map + 
  tm_add_legend(type = "symbol",
              labels = c("學校", "健康飲食餐廳", "速食店"),
              col = c("blue", "green", "red"),
              title = "類別") + 
  tm_scale_bar() + 
  tm_compass() + 
  tm_layout()
[v3->v4] `tm_add_legend()`: use `type = "symbols"` instead of `type = "symbol"`.[v3->v4] `tm_add_legend()`: use `fill` instead of `col` for the fill color of symbols.! `tm_scale_bar()` is deprecated. Please use `tm_scalebar()` instead.

tm_shape(TPE_Vill) + tm_polygons(col = "grey") + 
  SCHOOL_map + Healthy_food_map + 
  tm_shape(SCHOOL_buffer) + 
  tm_borders(col = "black", lwd = 1) +
  tm_add_legend(type = "symbol", labels = c("學校", "健康飲食餐廳"), col = c("blue", "green"), title = "類別")
[v3->v4] `tm_add_legend()`: use `type = "symbols"` instead of `type = "symbol"`.[v3->v4] `tm_add_legend()`: use `fill` instead of `col` for the fill color of symbols.

school_healthy_df <- st_drop_geometry(school_healthy_distance)
write_csv(school_healthy_df, "school_healthy_result_with_distance.csv")

學校與速食店熱區重疊篩選及健康餐車推薦設置地點

grid_500 <- grid_500 %>%
  mutate(
    joint = case_when(hotspot_fast == "High-High" & hotspot_school == "High-High" ~ "Both High",
                      hotspot_fast == "High-High" ~ "Fast-food Only",
                      hotspot_school == "High-High" ~ "Schools Only",
                      TRUE ~ "Neither"),
    joint = factor(joint, levels = c("Both High","Fast-food Only", "Schools Only", "Neither"))
  )
buf300 <- st_buffer(SCHOOL, dist = 300)
candidates <- grid_500 %>%
  filter(joint == "Both High") %>%
  filter(lengths(st_intersects(.,buf300))>0)
healthy_trucks_for_both_high <- st_centroid(st_geometry(candidates)) %>% st_as_sf()
healthy_trucks_for_no_restaurant <- st_centroid(st_geometry(schools_no_restaurant)) %>% st_as_sf()

tm_shape(grid_500) + tm_polygons(col = "joint", fill.scale = tm_scale(values = c("red","orange","blue","gray80")), fill.legend = tm_legend(title = "Hotspot Category"),
                                 border.col = "white",
                                 col_alpha = 0.3) +
  tm_layout(main.title = "School vs Fast-Food Bivariate Hotspots",
            legend.outside = TRUE,
            frame = FALSE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(main.title = )`

尋找位於高風險區域學校

candidates_buf <- st_buffer(candidates, dist = 300)
school_near <- SCHOOL %>% filter(lengths(st_intersects(., candidates_buf))>0)
school_names <- unique(school_near$Name)
school_names
[1] "信義國小" "仁愛國小" "南門國小" "北教實小" "蓬萊國小" "日新國小" "雙蓮國小" "大同國小"
tm_shape(TPE_Vill) +
  tm_borders(col = "gray70") +

tm_shape(candidates) +
  tm_polygons(
    col         = "red",
    fill_alpha  = 0.2,
    border.col  = "red",
    fill.legend = "High-risk Grid (Both Hotspots)"
  ) +

tm_shape(healthy_trucks_for_no_restaurant) +
  tm_symbols(
    col         = "green",
    shape       = 21,
    size        = 0.25,
    border.col  = "darkgreen",
    legend.show = TRUE,
    fill.legend = "Recommended Food Truck Site"
  ) +
tm_add_legend(type = "fill", 
              labels = "High-risk Grid", 
              col = "red", 
              alpha = 0.2) +

tm_add_legend(type = "symbol", 
              labels = "Recommended Food Truck Site", 
              col = "green", 
              shape = 21) +
tm_layout(
  main.title      = "Recommended Healthy Food Truck Sites",
  main.title.size = 1.2,
  main.title.position = "center",
  legend.outside  = TRUE,
  legend.title.size = 1.0,
  legend.text.size  = 0.8,
  frame            = FALSE
)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────
[tm_symbols()] Argument `legend.show` unknown.[v3->v4] `tm_add_legend()`: use `type = "polygons"` instead of `type = "fill"`.[v3->v4] `tm_add_legend()`: use `fill` instead of `col` for the fill color of polygons.[v3->v4] `tm_add_legend()`: use `type = "symbols"` instead of `type = "symbol"`.[v3->v4] `tm_add_legend()`: use `fill` instead of `col` for the fill color of symbols.[v3->v4] `tm_layout()`: use `tm_title()` instead of `tm_layout(main.title = )`

針對高風險地區及方圓一公里範圍內無健康飲食餐廳之學校,提供健康飲食餐車建議設置地點。

LS0tDQp0aXRsZTogIkcxMiINCm91dHB1dDoNCiAgIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiA2DQogICAgdG9jX2Zsb2F0OiB0cnVlDQotLS0NCmBgYHtyfQ0Kcm0obGlzdCA9IGxzKCkpDQpgYGANCmBgYHtyfQ0Kc2V0d2QoIkM6L1VzZXJzLzYxOGFtL09uZURyaXZlL+ahjOmdoi/nqbrplpPliIbmnpDmnJ/mnKvloLHlkYoiKQ0KDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0bWFwKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodW5pdHMpDQpsaWJyYXJ5KHJlc2hhcGUyKQ0KbGlicmFyeShhc3BhY2UpDQpsaWJyYXJ5KHNwYXRzdGF0KQ0KbGlicmFyeShzcGRlcCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShwYWxzKQ0KbGlicmFyeShjYXJ0b2dyYXBoeSkNCmxpYnJhcnkocmVhZHIpDQpgYGANCmBgYHtyfQ0KUG9wbl9UV04yIDwtIHN0X3JlYWQoIkM6L1VzZXJzLzYxOGFtL09uZURyaXZlL+ahjOmdoi/nqbrplpPliIbmnpDmnJ/mnKvloLHlkYovUG9wbl9UV04yIiwgb3B0aW9ucyA9ICJFTkNPRElORz1CSUc1IikNClNDSE9PTCA8LSBzdF9yZWFkKCJDOi9Vc2Vycy82MThhbS9PbmVEcml2ZS/moYzpnaIv56m66ZaT5YiG5p6Q5pyf5pyr5aCx5ZGKL1NDSE9PTCIsIG9wdGlvbnMgPSAiRU5DT0RJTkc9QklHNSIpDQpUcGVfRmFzdGZvb2QgPC0gc3RfcmVhZCgiQzovdXNlcnMvNjE4YW0vT25lRHJpdmUv5qGM6Z2iL+epuumWk+WIhuaekOacn+acq+WgseWRii9UcGVfRmFzdGZvb2QiLCBvcHRpb25zID0gIkVOQ09ESU5HPUJJRzUiKQ0KVFBFX1ZpbGwgPC0gc3RfcmVhZCgiQzovVXNlcnMvNjE4YW0vT25lRHJpdmUv5qGM6Z2iL+epuumWk+WIhuaekOacn+acq+WgseWRii9UYWlwZWlfVmlsbC9UYWlwZWlfVmlsbC5zaHAiLCBvcHRpb25zID0gIkVOQ09ESU5HPUJJRzUiKQ0KYGBgDQpgYGB7cn0NClRQRV9WaWxsYWdlX2Jhc2VtYXAgPC0gdG1fc2hhcGUoVFBFX1ZpbGwpICsNCiAgdG1fcG9seWdvbnMoY29sID0gImdyZXkiKSArDQogIHRtX2xheW91dCh0aXRsZSA9ICLoh7rljJfluILlhaznp4Hnq4vlnIvlsI/oiIfpgJ/po5/lupfpu57kvY3liIbluIPlnJYiKQ0KDQpTQ0hPT0xfbWFwIDwtIHRtX3NoYXBlKFNDSE9PTCkgKw0KICB0bV9kb3RzKGZpbGwgPSAiYmx1ZSIsIHNpemUgPSAwLjI1KQ0KDQpGYXN0Zm9vZF9tYXAgPC0gdG1fc2hhcGUoVHBlX0Zhc3Rmb29kKSArDQogIHRtX2RvdHMoZmlsbCA9ICJyZWQiLCBzaXplID0gMC4yNSkNCg0KVFBFX1ZpbGxhZ2VfYmFzZW1hcCArIFNDSE9PTF9tYXAgKyBGYXN0Zm9vZF9tYXAgKyB0bV9hZGRfbGVnZW5kKHR5cGUgPSAic3ltYm9sIiwNCiAgICAgICAgICAgICAgbGFiZWxzID0gYygi5a245qChIiwgIumAn+mjn+W6lyIpLA0KICAgICAgICAgICAgICBjb2wgPSBjKCJibHVlIiwgInJlZCIpLA0KICAgICAgICAgICAgICB0aXRsZSA9ICLpoZ7liKUiKSArIHRtX3NjYWxlX2JhcigpICsgdG1fY29tcGFzcygpICsgdG1fbGF5b3V0KCkNCmBgYA0KIyMgUGFydCAxOiDkuI3lkIzooYzmlL/ljYDnmoTpgJ/po5/lupfmnInkuI3lkIznmoTlnLDnkIbliIbluIPnibnlvrXll47vvJ8NCiMjIOWVj+etlOmhjA0K5ZyWIDEg5piv5p+Q5Z+O5biC5ZCE5p2R6YeM6YCf6aOf5bqX5a+G5bqm55qEIE1vcmFuIFNjYXR0ZXIgUGxvdOOAgg0KDQojIyMgKDEpIA0K57Ch6L+w6Kmy5ZyW6KGo55qEIFgg6Lu46IiHIFkg6Lu45omA6KGo56S655qE5oSP5ra177yM5Lul5Y+K5YW256m66ZaT5Z6L5oWL44CCDQoNClgg6Lu45Luj6KGo5ZCE56m66ZaT5Zau5L2N55qE6YCf6aOf5bqX5a+G5bqm77yM57aT6YGO5qiZ5rqW5YyW6JmV55CG44CC5pW45YC86LaK6auY77yM6KGo56S66Kmy5Y2A5Z+f6YCf6aOf5bqX5a+G5bqm6auY5pa85bmz5Z2H44CCDQpZIOi7uOihqOekuuOAjOmEsOi/keWNgOWfn+eahOmAn+mjn+W6l+WvhuW6puOAjeeahOW5s+Wdh+WAvO+8jOS5n+e2k+mBjuaomea6luWMluOAgumAmeaYr+ioiOeul+epuumWk+iHquebuOmXnOeahOmXnOmNte+8mueci+afkOS4gOWNgOeahOmAn+mjn+W6l+WvhuW6puiIh+WFtumEsOi/keWNgOWfn+eahOWvhuW6puaYr+WQpuS4gOiHtOOAgg0KDQojIyMgKDIpIA0K6KuL57Ch6L+w6Kmy5ZyW6Lao5Yui57eaKEZpdCnnmoTmlpznjoflgLwgKHNsb3BlID0wLjk4MinvvIzmiYDku6PooajnmoTmhI/mtrXjgIINCg0K5pW45YC85Luj6KGo56m66ZaT6Ieq55u46Zec55qE5by35bqm77yMDQrotqjli6Lnt5rnmoTmlpznjofov5HkvLwgMe+8jOihqOekuuiuiuaVuO+8iOmAn+mjn+W6l+WvhuW6pu+8ieiIh+WFtumEsOi/keWNgOWfn+eahOWAvOS5i+mWk+aciemdnuW4uOW8t+eahOato+WQkee3muaAp+mXnOS/guOAgg0KDQojIyMgKDMpIA0K6Zec5pa86Kmy5Z+O5biC5ZCE5p2R6YeM6YCf6aOf5bqX5a+G5bqm55qEIExvY2FsIE1vcmFu4oCZcyBJIOe1seioiOmHj++8jOiri+WVj+WFtue1seioiOmHj+WPr+iDveacg+Wkp+aWvCAw44CB562J5pa8IDAg5oiW5bCP5pa8IDDvvIzkuKboqqrmmI7liKTmlrfnmoTnkIbnlLHjgIINCg0KTG9jYWwgTW9yYW7igJlzIEkg5Y+v6IO95aSn5pa8IDDvvIjpq5gt6auY44CB5L2OLeS9juiBmumbhu+8ieOAgeWwj+aWvCAw77yI6Zui576k5Y2A77yM5aaC6auYLeS9juOAgeS9ji3pq5jvvInvvIzmiJbnrYnmlrwgMO+8iOeEoeepuumWk+mXnOiBr++8ieOAguWNs+S9v+aVtOmrlOWRiOePvuato+WQkeepuumWk+iHquebuOmXnO+8jOWxgOmDqOS7jeWPr+iDveWtmOWcqOeVsOW4uOm7nuiIh+mboue+pOePvuixoe+8jOWboOatpOmcgOS9v+eUqCBMb2NhbCBNb3JhbuKAmXMgSSDpgLLooYzntLDpg6jliKTmlrfjgIINCg0KIyMg5a+m5L2c6aGMDQojIyMgKDEpDQrlgYfoqK3pgJ/po5/lupfnmoTmnI3li5nnr4TlnI3mmK8gMSDlhazph4zvvIzmr5TovIMgQSDljYAo5paH5bGxK+Wkp+WuiSvkuK3mraMpIOiIhyBCIOWNgCjkv6Hnvqkr5Y2X5rivK+advuWxsSkg6YCZ5YWp5YCL5Zyw5Y2A55qE5q+P5LiA5a626YCf6aOf5bqX5Zyo5pyN5YuZ5Y+v5Y+K56+E5ZyN5YWn77yM5omA5ra16JOL5a245qCh5pW46YeP55qE5bmz5Z2H5YC877yM5piv5ZCm5pyJ57Wx6KiI6aGv6JGX5beu55Ww44CCKOmcgOWIl+WHuuiZm+eEoeWBh+ioreiIh+Wwjeeri+WBh+iore+8jOe1seioiOaqouWumumHj++8jOS7peWPiuaqouWumueahOmhr+iRl+awtOa6luetiSnjgIINCmBgYHtyfQ0KIyDlrprnvqkgQSDljYDjgIFCIOWNgOihjOaUv+WNgOWQjeeosQ0KQV90b3ducyA8LSBjKCLmloflsbHljYAiLCLlpKflronljYAiLCLkuK3mraPljYAiKQ0KQl90b3ducyA8LSBjKCLkv6HnvqnljYAiLCLljZfmuK/ljYAiLCLmnb7lsbHljYAiKQ0KDQojIDEuIOW7uueriyAxa20g57ep6KGd5Y2AIGFyb3VuZCBlYWNoIGZhc3Rmb29kIA0KZmFzdF9idWYgPC0gc3RfYnVmZmVyKFRwZV9GYXN0Zm9vZCwgZGlzdCA9IHNldF91bml0cygxLCBrbSkpDQoNCiMgMi4g56m66ZaT6YCj57WQ77ya5q+P5a626YCf6aOf5bqX57ep6KGd5YWn55qE5a245qCh5pW46YePDQpmZl9zY2hvb2wgPC0gc3Rfam9pbihmYXN0X2J1ZiwgU0NIT09MLCBqb2luID0gc3RfY29udGFpbnMpDQpjb3VudHMgPC0gZmZfc2Nob29sICU+JSANCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JSANCiAgZ3JvdXBfYnkoSUQpICU+JSAgICAgICAgICAgICAgIA0KICBzdW1tYXJpc2Uoc2Nob29sX24gPSBuKCkpDQoNCiMgMy4g5bCN6YCf6aOf5bqX6ZmE5Yqg5YW25omA5bGs6KGM5pS/5Y2ADQpmZl90b3duIDwtIHN0X2pvaW4oVHBlX0Zhc3Rmb29kLCBUUEVfVmlsbCwgam9pbiA9IHN0X3dpdGhpbikNCmZmX3N0YXRzIDwtIGxlZnRfam9pbihmZl90b3duLCBjb3VudHMsIGJ5ID0gIklEIikNCg0KIyA0LiDliqDlhaUgQS9CIOmhnuWIpeaomeexpA0KZmZfc3RhdHMgPC0gZmZfc3RhdHMgJT4lDQogIG11dGF0ZShhcmVhX2dyb3VwID0gY2FzZV93aGVuKA0KICAgIFRPV04gJWluJSBBX3Rvd25zIH4gIkHljYAiLA0KICAgIFRPV04gJWluJSBCX3Rvd25zIH4gIkLljYAiLA0KICAgIFRSVUUgICAgICAgICAgICAgIH4gTkFfY2hhcmFjdGVyXw0KICApKSAlPiUNCiAgZmlsdGVyKCFpcy5uYShhcmVhX2dyb3VwKSkNCg0KIyA1LiDliJflh7ogQeOAgUIg5Y2A5Lit5q+P5a626YCf6aOf5bqXIOeahCBzY2hvb2xfbiDlkJHph4/vvIwNCiMgICAgIOS4puWBmiB0LnRlc3TvvIjmiJYgV2lsY294IHRlc3TvvIkNCkFfdmFscyA8LSBmZl9zdGF0cyRzY2hvb2xfbltmZl9zdGF0cyRhcmVhX2dyb3VwPT0iQeWNgCJdDQpCX3ZhbHMgPC0gZmZfc3RhdHMkc2Nob29sX25bZmZfc3RhdHMkYXJlYV9ncm91cD09IkLljYAiXQ0KDQojIOiZm+eEoeWBh+iorSBIMDogzrxfQSA9IM68X0LvvJvlsI3nq4vlgYfoqK0gSDE6IM68X0Eg4omgIM68X0INCnRfcmVzIDwtIHQudGVzdChBX3ZhbHMsIEJfdmFscywgdmFyLmVxdWFsID0gRkFMU0UpDQp0X3Jlcw0KYGBgDQromZvnhKHlgYfoqK3vvIhI4oKA77yJ77yaQSDljYDoiIcgQiDljYDnmoTpgJ/po5/lupfmnI3li5nnr4TlnI3lhafmtrXok4vnmoTlrbjmoKHmlbjlubPlnYflgLznm7jlkIzvvIjOvOKCgSA9IM684oKC77yJ44CCDQrlsI3nq4vlgYfoqK3vvIhI4oKB77yJ77yaQSDljYDoiIcgQiDljYDnmoTpgJ/po5/lupfmtrXok4vlrbjmoKHmlbjlubPlnYflgLzkuI3lkIzvvIjOvOKCgSDiiaAgzrzigoLvvInjgIINCg0KQeWNgOiIh0LljYDpgJ/po5/lupfmtrXok4vnmoTlrbjmoKHmlbjlubPlnYflgLznhKHpoa/okZflt67nlbDvvIhwID0gMC4xMTYzID4gMC4wNe+8ie+8jOeEoeazleaLkue1leiZm+eEoeWBh+ioreOAgumhr+ekuuWFqeWNgOmAn+mjn+W6l+acjeWLmeaViOiDveWcqOe1seioiOS4iueEoeaYjumhr+W3rueVsOOAgg0KDQojIyMgKDIpDQrku6UgNTAwIOWFrOWwuuaWueagvOeahOepuumWk+WWruS9jeW7uueri+e2suagvO+8jOS+neeFpyBjb250aWd1aXR5IOmEsOi/keWumue+qe+8jOioiOeul+S4pue5quijvemAn+mjn+W6l+e4veaVuOeahCBNb3JhbidzIGNvcnJlbG9ncmFt77yM6Kej6K6A5YW25ZyW6KGo6LOH6KiK77yM5Lim6KmV5Lyw6YCf6aOf5bqX55qE56m66ZaT5b2x6Z+/56+E5ZyN44CCDQpgYGB7cn0NCiMgMS4g5bu656uLIDUwMCDlhazlsLrmoLzntrLvvIzkuKboo4HliIfoh7Plj7DljJfluILooYzmlL/ljYDlhafpg6gNCmdyaWRfNTAwIDwtIHN0X21ha2VfZ3JpZChUUEVfVmlsbCwgY2VsbHNpemUgPSA1MDAsIHdoYXQgPSAicG9seWdvbnMiKSAlPiUNCiAgc3Rfc2YoKSAlPiUNCiAgc3RfaW50ZXJzZWN0aW9uKHN0X3VuaW9uKFRQRV9WaWxsKSkNCg0KZ3JpZF81MDAkZ3JpZF9pZCA8LSAxOm5yb3coZ3JpZF81MDApDQoNCiMgMi4g56m66ZaT6YCj57WQ6YCf6aOf5bqX5Yiw5qC857ay77yM57Wx6KiI5q+P5qC86YCf6aOf5bqX5pW46YePDQpncmlkX2pvaW4gPC0gc3Rfam9pbihUcGVfRmFzdGZvb2QsIGdyaWRfNTAwLCBqb2luID0gc3Rfd2l0aGluKQ0KY291bnRfZ3JpZCA8LSBncmlkX2pvaW4gJT4lDQogIGdyb3VwX2J5KGdyaWRfaWQpICU+JQ0KICBzdW1tYXJpc2UobiA9IG4oKSkNCg0KIyAzLiDlsIfoqIjmlbjntZDmnpzlkIjkvbXlm57moLzntrLos4fmlpnvvIzlsIcgTkEg6KOc54K6IDDvvIjnhKHpgJ/po5/lupfvvIkNCmNvdW50X2dyaWRfbm9nZW8gPC0gY291bnRfZ3JpZCAlPiUgc3RfZHJvcF9nZW9tZXRyeSgpDQpncmlkXzUwMCA8LSBsZWZ0X2pvaW4oZ3JpZF81MDAsIGNvdW50X2dyaWRfbm9nZW8sIGJ5ID0gImdyaWRfaWQiKSAlPiUNCiAgbXV0YXRlKG4gPSByZXBsYWNlX25hKG4sIDApKQ0KDQojIDQuIOW7uueri+agvOe2suS5i+mEsOaOpeefqemZo++8iOS7pSBRdWVlbiDphLDmjqXngrrmupbvvIkNCm5iX2dyaWQgPC0gcG9seTJuYihncmlkXzUwMCwgcXVlZW4gPSBUUlVFKQ0KbHdfZ3JpZCA8LSBuYjJsaXN0dyhuYl9ncmlkLCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQojIDUuIOaPkOWPlumAn+mjn+W6l+aVuOWAvOWQkemHjw0Kbl92ZWN0b3IgPC0gZ3JpZF81MDAkbg0KDQojIDYuIOioiOeulyBNb3JhbuKAmXMgSSBDb3JyZWxvZ3Jhbe+8iOmEsOi/kemajuaVuOW+niAxfjbvvIkNCm1vcmFuX2NvcnJlbG9ncmFtIDwtIHNwLmNvcnJlbG9ncmFtKA0KICBuYl9ncmlkLCANCiAgbl92ZWN0b3IsIA0KICBvcmRlciA9IDYsIA0KICBtZXRob2QgPSAiSSIsIA0KICBzdHlsZSA9ICJXIiwgDQogIHplcm8ucG9saWN5ID0gVFJVRQ0KKQ0KDQojIDcuIOe5quijvSBjb3JyZWxvZ3JhbSDntZDmnpzlnJYNCnBsb3QobW9yYW5fY29ycmVsb2dyYW0sIG1haW4gPSAiTW9yYW4ncyBJIENvcnJlbG9ncmFtIG9mIEZhc3QgRm9vZCBDb3VudHMgKDUwMG0gR3JpZCkiKQ0KYGBgDQpNb3JhbidzIEkg5Zyo56ys5LiA6ZqO5bGk6YSw6L+R77yITGFnIDHvvInpoa/okZfngrrmraPvvIzpoa/npLrpgJ/po5/lupfmnInmmI7norrnmoTlsYDpg6jnvqTogZrmgKfvvIzkvYbpmqjot53pm6Llop7liqDoh6rnm7jpl5zmgKfkuIvpmY3vvIzlvbHpn7/nr4TlnI3ntITlnKggNTAw4oCTMTAwMCDlhazlsLrlhafjgIINCg0KYGBge3J9DQp0bV9zaGFwZShncmlkXzUwMCkgKw0KICB0bV9wb2x5Z29ucyhjb2wgPSAibiIsIHBhbGV0dGUgPSAiWWxPclJkIiwgdGl0bGUgPSAi6YCf6aOf5bqX5pW4IikgKw0KICB0bV9sYXlvdXQodGl0bGUgPSAi6Ie65YyX5biCIDUwMG0g57ay5qC85Lit6YCf6aOf5bqX5YiG5biDIikNCmBgYA0KIyMjICgzKQ0K5L6d54Wn5YmN5LiA5bCP6aGM55qE57ay5qC854K656m66ZaT5Zau5L2N77yM5L6d54WnIGNvbnRpZ3VpdHkg6YSw6L+R5a6a576p77yM6KiI566XIEdpKue1seioiOmHj++8jOWIhuWIpee5quijvemAn+mjn+W6l+WutuaVuOiIh+WtuOagoeWcqCAwLjA1IOmhr+iRl+awtOa6lueahOeGseWNgOWIhuS9iO+8iOmcgOmAsuihjOWkmumHjeaqouWumueahOS/ruato++8ieOAgiANCmBgYHtyfQ0Kc2Nob29sX2pvaW4gPC0gc3Rfam9pbihTQ0hPT0wsIGdyaWRfNTAwLCBqb2luID0gc3Rfd2l0aGluKQ0Kc2Nob29sX2NvdW50IDwtIHNjaG9vbF9qb2luICU+JQ0KICBncm91cF9ieShncmlkX2lkKSAlPiUNCiAgc3VtbWFyaXNlKHNjaG9vbHMgPSBuKCkpDQoNCnNjaG9vbF9jb3VudF9ub2dlbyA8LSBzY2hvb2xfY291bnQgJT4lIHN0X2Ryb3BfZ2VvbWV0cnkoKQ0KZ3JpZF81MDAgPC0gbGVmdF9qb2luKGdyaWRfNTAwLCBzY2hvb2xfY291bnRfbm9nZW8sIGJ5ID0gImdyaWRfaWQiKSAlPiUNCiAgbXV0YXRlKHNjaG9vbHMgPSByZXBsYWNlX25hKHNjaG9vbHMsIDApKQ0KDQpuYl9ncmlkIDwtIHBvbHkybmIoZ3JpZF81MDAsIHF1ZWVuID0gVFJVRSkNCmx3X2dyaWQgPC0gbmIybGlzdHcobmJfZ3JpZCwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KIyDoqIjnrpcgR2kqIOe1seioiOmHjw0KR2lfZmFzdCA8LSBsb2NhbEcoZ3JpZF81MDAkbiwgbHdfZ3JpZCkNCkdpX3NjaG9vbCA8LSBsb2NhbEcoZ3JpZF81MDAkc2Nob29scywgbHdfZ3JpZCkNCg0KIyDoqIjnrpcgcC12YWx1ZSDkuKbpgLLooYwgRkRSIOS/ruato++8iOmbmeWwvu+8iQ0KZ3JpZF81MDAkR2lfZmFzdCA8LSBhcy5udW1lcmljKEdpX2Zhc3QpDQpncmlkXzUwMCRwX2Zhc3QgPC0gMiAqIHBub3JtKC1hYnMoZ3JpZF81MDAkR2lfZmFzdCkpDQpncmlkXzUwMCRGRFJfZmFzdCA8LSBwLmFkanVzdChncmlkXzUwMCRwX2Zhc3QsIG1ldGhvZCA9ICJmZHIiKQ0KZ3JpZF81MDAkR2lfc2Nob29sIDwtIGFzLm51bWVyaWMoR2lfc2Nob29sKQ0KZ3JpZF81MDAkcF9zY2hvb2wgPC0gMiAqIHBub3JtKC1hYnMoZ3JpZF81MDAkR2lfc2Nob29sKSkNCmdyaWRfNTAwJEZEUl9zY2hvb2wgPC0gcC5hZGp1c3QoZ3JpZF81MDAkcF9zY2hvb2wsIG1ldGhvZCA9ICJmZHIiKQ0KDQojIOeGseWNgOWIhumhnuaomeexpO+8iOmhr+iRl+aAp+aomeekuu+8iQ0KZ3JpZF81MDAgPC0gZ3JpZF81MDAgJT4lDQogIG11dGF0ZShob3RzcG90X2Zhc3QgPSBjYXNlX3doZW4oDQogICAgRkRSX2Zhc3QgPCAwLjA1ICYgR2lfZmFzdCA+IDAgfiAiSGlnaC1IaWdoIiwNCiAgICBGRFJfZmFzdCA8IDAuMDUgJiBHaV9mYXN0IDwgMCB+ICJMb3ctTG93IiwNCiAgICBUUlVFIH4gIk5vdCBzaWduaWZpY2FudCINCiAgKSkgJT4lDQogIG11dGF0ZShob3RzcG90X3NjaG9vbCA9IGNhc2Vfd2hlbigNCiAgICBGRFJfc2Nob29sIDwgMC4wNSAmIEdpX3NjaG9vbCA+IDAgfiAiSGlnaC1IaWdoIiwNCiAgICBGRFJfc2Nob29sIDwgMC4wNSAmIEdpX3NjaG9vbCA8IDAgfiAiTG93LUxvdyIsDQogICAgVFJVRSB+ICJOb3Qgc2lnbmlmaWNhbnQiDQogICkpDQoNCiMg54ax5Y2A5ZyW57mq6KO977yI6YCf6aOf5bqX77yJDQp0bV9zaGFwZShncmlkXzUwMCkgKw0KICB0bV9maWxsKCJob3RzcG90X2Zhc3QiLA0KICAgICAgICAgIHBhbGV0dGUgPSBjKCJyZWQiLCAid2hpdGUiLCAiZ3JheTgwIiksDQogICAgICAgICAgdGl0bGUgPSAi6YCf6aOf5bqX54ax5Y2AIiwNCiAgICAgICAgICBsYWJlbHMgPSBjKCJIaWdoLUhpZ2giLCAiTG93LUxvdyIsICJOb3Qgc2lnbmlmaWNhbnQiKSkgKw0KICB0bV9ib3JkZXJzKCkgKw0KICB0bV9sYXlvdXQodGl0bGUgPSAi6YCf6aOf5bqXIEdpKiDnhrHljYDliIbmnpAgKEZEUiA8IDAuMDUpIikNCg0KIyDnhrHljYDlnJbnuaroo73vvIjlrbjmoKHvvIkNCnRtX3NoYXBlKGdyaWRfNTAwKSArDQogIHRtX2ZpbGwoImhvdHNwb3Rfc2Nob29sIiwNCiAgICAgICAgICBwYWxldHRlID0gYygicmVkIiwgIndoaXRlIiwgImdyYXk4MCIpLA0KICAgICAgICAgIHRpdGxlID0gIuWtuOagoeeGseWNgCIsDQogICAgICAgICAgbGFiZWxzID0gYygiSGlnaC1IaWdoIiwgIkxvdy1Mb3ciLCAiTm90IHNpZ25pZmljYW50IikpICsNCiAgdG1fYm9yZGVycygpICsNCiAgdG1fbGF5b3V0KHRpdGxlID0gIuWtuOagoSBHaSog54ax5Y2A5YiG5p6QIChGRFIgPCAwLjA1KSIpDQpgYGANCumAn+mjn+W6l+iIh+WtuOagoeeahOeGseWNgOeahumbhuS4reaWvOiHuuWMl+W4guilv+WNl+mDqO+8jOmhr+ekuuWFqeiAheepuumWk+WIhuW4g+WFt+aciemHjeeWiue+pOiBmuePvuixoeOAguipsuWNgOWPr+imlueCuuWtuOeUn+WuueaYk+aOpeinuOmAn+mjn+eahOmrmOmiqOmaqueSsOWig+OAgg0KDQojIyBQYXJ0IDI6IOWtuOagoemZhOi/keeahOmAn+mjn+W6l+aVuOmHj+ecn+eahOavlOi8g+WkmuWXjj8NCiMjIyAoMSkgDQroq4vlj4PogIMgVHJhbnNwb3J0YXRpb24gUmVzZWFyY2ggUGFydCBBLCA0NSAoMjAxMSkgNjQw4oCTNjUy77yISFctMDkg55qE56CU6K6A5pWZ5p2Q77yJ5omA5L2/55SoIEYoZCnlh73mlbjnmoTkvZzms5XvvIzmr5TovIPliIbliKXkvY3mlrzlpKflronljYDjgIHmloflsbHljYDoiIfkv6HnvqnljYDnmoTlrbjmoKHvvIzliLDphLDov5HpgJ/po5/lupfnmoTnqbrplpPnibnlvrXjgIINCmBgYHtyfQ0KU0NIT09MIDwtIHN0X2pvaW4oU0NIT09MLCBUUEVfVmlsbFssIGMoIlRPV04iKV0sIGpvaW4gPSBzdF93aXRoaW4pDQp0YWJsZShTQ0hPT0wkVE9XTikNCmRpc3RyaWN0cyA8LSBjKCLlpKflronljYAiLCAi5paH5bGx5Y2AIiwgIuS/oee+qeWNgCIpDQpzY2hvb2xzX2J5X2Rpc3RyaWN0IDwtIFNDSE9PTCAlPiUgZmlsdGVyKFRPV04gJWluJSBkaXN0cmljdHMpDQoNCiMg6KiI566X6Led6Zui5pyA6L+R6YCf6aOf5bqX55qE6Led6ZuiDQpkaXN0X2xpc3QgPC0gbGFwcGx5KGRpc3RyaWN0cywgZnVuY3Rpb24oZGlzdCl7DQogIHNjaCA8LSBzY2hvb2xzX2J5X2Rpc3RyaWN0ICU+JSBmaWx0ZXIoVE9XTiA9PSBkaXN0KQ0KICBtIDwtIHN0X2Rpc3RhbmNlKHNjaCwgVHBlX0Zhc3Rmb29kKQ0KICBkbWluIDwtIGFwcGx5KG0sIDEsIG1pbikgJT4lIHNldF91bml0cygibSIpICU+JSBkcm9wX3VuaXRzKCkNCiAgZGF0YS5mcmFtZSgNCiAgICBkaXN0cmljdCA9IGRpc3QsDQogICAgZGlzdF9tICAgPSBkbWluDQogICkNCn0pDQpkZl9kaXN0IDwtIGJpbmRfcm93cyhkaXN0X2xpc3QpDQoNCiMg57mq6KO9IEYoZCkgRUNERiDlnJYNCmdncGxvdChkZl9kaXN0LCBhZXMoeCA9IGRpc3RfbSwgY29sb3VyID0gZGlzdHJpY3QpKSArDQogIHN0YXRfZWNkZihzaXplID0gMS4yKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDIwMDApLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsIDIwMDAsIDUwMCksDQogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBmdW5jdGlvbih4KSBwYXN0ZTAoeCwgIm0iKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIuiHuuWMl+W4guS4ieWNgOWtuOagoeWIsOacgOi/kemAn+mjn+W6l+eahCBGKGQpIOi3nembouWIhuW4gyIsDQogICAgeCA9ICLot53pm6IgZO+8iOWFrOWwuu+8iSIsDQogICAgeSA9ICJGKGQpID0g5a245qCh6Led6ZuiIOKJpCBkIOeahOavlOS+iyIsDQogICAgY29sb3VyID0gIuihjOaUv+WNgCINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlaXRpIFRDIiksIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQojIEtT5qqi5a6a77yI5YWp5YWp57WE5ZCI77yJDQprc19yZXN1bHRzIDwtIGNvbWJuKGRpc3RyaWN0cywgMiwgc2ltcGxpZnkgPSBGQUxTRSwgRlVOID0gZnVuY3Rpb24ocGFpcil7DQogIGQxIDwtIGRmX2Rpc3QgJT4lIGZpbHRlcihkaXN0cmljdCA9PSBwYWlyWzFdKSAlPiUgcHVsbChkaXN0X20pDQogIGQyIDwtIGRmX2Rpc3QgJT4lIGZpbHRlcihkaXN0cmljdCA9PSBwYWlyWzJdKSAlPiUgcHVsbChkaXN0X20pDQogIGt0IDwtIGtzLnRlc3QoZDEsIGQyKQ0KICBkYXRhLmZyYW1lKA0KICAgIGdyb3VwMSA9IHBhaXJbMV0sDQogICAgZ3JvdXAyID0gcGFpclsyXSwNCiAgICBzdGF0aXN0aWMgPSBrdCRzdGF0aXN0aWMsDQogICAgcF92YWx1ZSAgPSBrdCRwLnZhbHVlDQogICkNCn0pDQprc19kZiA8LSBiaW5kX3Jvd3Moa3NfcmVzdWx0cykNCnByaW50KGtzX2RmKQ0KYGBgDQrkv6HnvqnljYDlrbjmoKHot53pgJ/po5/lupfmnIDov5HvvIzmloflsbHljYDmnIDpgaDvvIzpoa/npLrkv6HnvqnljYDpgJ/po5/lupflr4bluqbpq5jjgIHlj6/lj4rmgKflvLfvvJvmloflsbHljYDliYfnm7jlsI3liIbmlaPvvIzlrbjnlJ/mmrTpnLLpgJ/po5/nkrDlooPpoqjpmqrovIPkvY7jgIINCg0KIyMjICgyKSANCuWIqeeUqCBSaXBsZXkncyBLIGZ1bmN0aW9uLCBrKGQp6KiI566X6YCf6aOf5bqX5ZyoIGRpc3RhbmNlIChkKSA9IDMwMDAg5YWs5bC65pmC55qEIEsoMzAwMCkg5Lul5Y+KTCgzMDAwKe+8jOS4puS9v+eUqCBNb250ZSBDYXJsbyBzaWduaWZpY2FuY2UgdGVzdCDoqIjnrpcgTCgzMDAwKeeahCA5NSXkv6Hos7TljYDplpPjgIINCmBgYHtyfQ0KIyAxLiDmupblgpkgcHBwIOizh+aWme+8iOmAn+mjn+W6l+m7nuiIh+WIhuaekOevhOWcje+8iQ0KIyDovYnmj5vngrogc3BhdHN0YXQg5YiG5p6Q6KaW56qX77yI5LulIFRQRV9WaWxsIOevhOWcjeeCuuS4u++8iQ0Kd2luIDwtIGFzLm93aW4oc3RfdW5pb24oVFBFX1ZpbGwpKQ0KZmFzdF94eSA8LSBzdF9jb29yZGluYXRlcyhUcGVfRmFzdGZvb2QpDQpwcHBfZmFzdCA8LSBwcHAoeCA9IGZhc3RfeHlbLDFdLCB5ID0gZmFzdF94eVssMl0sIHdpbmRvdyA9IHdpbikNCg0KIyAyLiDoqIjnrpcgSyhkKSDoiIcgTChkKe+8iOS9v+eUqCBib3JkZXIg5L+u5q2j77yJDQpLX3Jlc3VsdCA8LSBLZXN0KHBwcF9mYXN0LCBjb3JyZWN0aW9uID0gImJvcmRlciIpDQpMX3Jlc3VsdCA8LSBMZXN0KHBwcF9mYXN0LCBjb3JyZWN0aW9uID0gImJvcmRlciIpDQoNCiMgMy4g5o+Q5Y+WIGQgPSAzMDAwIOaZgueahCBLIOiIhyBMIOWAvA0KdGFyZ2V0X2QgPC0gMzAwMA0Ka19pbmRleCAgPC0gd2hpY2gubWluKGFicyhLX3Jlc3VsdCRyIC0gdGFyZ2V0X2QpKSANCg0Ka19yXzMwMDAgPC0gS19yZXN1bHQkcltrX2luZGV4XSAgICAgICAgICAgICAgICAgICAgDQprXzMwMDAgICA8LSBLX3Jlc3VsdCRib3JkZXJba19pbmRleF0gICAgICAgICAgICAgICANCmxfMzAwMCAgIDwtIExfcmVzdWx0JGJvcmRlcltrX2luZGV4XSAgICAgICAgICAgICAgIA0KDQpjYXQoIksoIiwga19yXzMwMDAsICIpID0iLCByb3VuZChrXzMwMDAsIDIpLCAiXG4iKQ0KY2F0KCJMKCIsIGtfcl8zMDAwLCAiKSA9Iiwgcm91bmQobF8zMDAwLCAyKSwgIlxuIikNCg0KIyA0LiBNb250ZSBDYXJsbyDmqKHmk6zkv6Hos7TljYDplpPvvIg5OSDmrKEgQ1NSIOaooeaTrO+8iQ0Kc2V0LnNlZWQoMTIzNCkNCkxfZW52IDwtIGVudmVsb3BlKA0KICBwcHBfZmFzdCwNCiAgZnVuID0gTGVzdCwNCiAgbnNpbSA9IDk5LA0KICBjb3JyZWN0aW9uID0gImJvcmRlciIsDQogIGdsb2JhbCA9IEZBTFNFLA0KICBzaW11bGF0ZSA9IGV4cHJlc3Npb24ocnBvaXNwcChsYW1iZGEgPSBpbnRlbnNpdHkocHBwX2Zhc3QpLCB3aW4gPSB3aW4pKQ0KKQ0KDQpMXzMwMDBfb2JzIDwtIExfZW52JG9ic1trX2luZGV4XQ0KTF8zMDAwX2xvICA8LSBMX2VudiRsb1trX2luZGV4XQ0KTF8zMDAwX2hpICA8LSBMX2VudiRoaVtrX2luZGV4XQ0KDQpjYXQoIkwoIiwga19yXzMwMDAsICIpID0iLCByb3VuZChMXzMwMDBfb2JzLCAyKSwgDQogICAgIlxuOTUlIENJID0gWyIsIHJvdW5kKExfMzAwMF9sbywgMiksICIsIiwgcm91bmQoTF8zMDAwX2hpLCAyKSwgIl1cbiIpDQoNCiMgNS4g57mq5ZyW77yaUmlwbGV5J3MgTCB3aXRoIOS/oeiztOWNgOmWk+iIh+aomeekuue3miANCnBsb3QoTF9lbnYsDQogICAgIG1haW4gPSAiUmlwbGV5J3MgTCBmdW5jdGlvbiB3aXRoIDk1JSBNb250ZSBDYXJsbyBFbnZlbG9wZSIsDQogICAgIHhsYWIgPSAi6Led6ZuiIGQgKOWFrOWwuikiLCB5bGFiID0gIkwoZCkiLA0KICAgICB4bGltID0gYygwLCA0MDAwKSwNCiAgICAgbGVnZW5kID0gRkFMU0UpDQphYmxpbmUoaCA9IDAsIGNvbCA9ICJncmF5MzAiLCBsdHkgPSAyKSAgICAgICAgICAgICANCmFibGluZSh2ID0ga19yXzMwMDAsIGNvbCA9ICJyZWQiLCBsdHkgPSAzKQ0KDQojIOWcqOWcluS4iuWKoOS4iuWvpumam+WAvOiIh+S/oeiztOWNgOmWk+eahOm7ng0KcG9pbnRzKHggPSBrX3JfMzAwMCwgeSA9IExfMzAwMF9vYnMsIHBjaCA9IDE2LCBjb2wgPSAiYmxhY2siKQ0Kc2VnbWVudHMoeDAgPSBrX3JfMzAwMCwgeDEgPSBrX3JfMzAwMCwgeTAgPSBMXzMwMDBfbG8sIHkxID0gTF8zMDAwX2hpLCBjb2wgPSAiZGFya2JsdWUiLCBsd2QgPSAyKQ0KYGBgDQpMKDMwMDApIOWAvOmhr+iRl+mrmOaWvCBDU1Ig5qih5pOs5L+h6LO05Y2A6ZaT77yM6KGo56S65ZyoIDMg5YWs6YeM5bC65bqm5LiL6YCf6aOf5bqX5a2Y5Zyo6aGv6JGX56m66ZaT576k6IGa77yM6aGv56S65YW25YiG5biD6ZuG5Lit5LiU6Z2e6Zqo5qmf44CCDQoNCiMjIyAoMykNCuWIqeeUqCBCaXZhcmlhdGUgRiBmdW5jdGlvbiDliIbmnpDmlrnms5XvvIzmr5TovIPlhaznq4sgdnMu56eB56uL5a245qCh77yM5Yiw6YSw6L+R6YCf6aOf5bqX55qE56m66ZaT54m55b6177yM5Lim6Kqq5piO5ZOq5LiA56iu6aGe5Z6L5a245qCh55qE6ZmE6L+R77yM5pyD5pyJ6LyD5aSa55qE6YCf6aOf5bqX77yf77yI6aGv6JGX5rC05rqWPTAuMDXvvIkNCmBgYHtyfQ0KdGFibGUoU0NIT09MJFR5cGUpDQpzY2hvb2xfcHVibGljIDwtIFNDSE9PTCAlPiUgZmlsdGVyKFR5cGUgPT0gIuWFrOeriyIpDQpzY2hvb2xfcHJpdmF0ZSA8LSBTQ0hPT0wgJT4lIGZpbHRlcihUeXBlID09ICLnp4Hnq4siKQ0KDQojIOioiOeul+WFqee+pOWtuOagoeWIsOacgOi/kemAn+mjn+W6l+eahOi3nembog0KZ2V0X25lYXJlc3RfZGlzdCA8LSBmdW5jdGlvbihzY2hvb2xfc2YsIGZhc3Rfc2Ypew0KICBkaXN0X21hdHJpeCA8LSBzdF9kaXN0YW5jZShzY2hvb2xfc2YsIGZhc3Rfc2YpDQogIGRtaW4gPC0gYXBwbHkoZGlzdF9tYXRyaXgsIDEsIG1pbikgJT4lIHNldF91bml0cygibSIpICU+JSBkcm9wX3VuaXRzKCkNCiAgcmV0dXJuKGRtaW4pDQp9DQoNCmRpc3RfcHViIDwtIGdldF9uZWFyZXN0X2Rpc3Qoc2Nob29sX3B1YmxpYywgVHBlX0Zhc3Rmb29kKQ0KZGlzdF9wcmkgPC0gZ2V0X25lYXJlc3RfZGlzdChzY2hvb2xfcHJpdmF0ZSwgVHBlX0Zhc3Rmb29kKQ0KDQojIOWQiOS9teizh+aWmQ0KZGZfZGlzdCA8LSBkYXRhLmZyYW1lKA0KICBkaXN0X20gPSBjKGRpc3RfcHViLCBkaXN0X3ByaSksDQogIFR5cGUgICA9IGMocmVwKCLlhaznq4siLCBsZW5ndGgoZGlzdF9wdWIpKSwgcmVwKCLnp4Hnq4siLCBsZW5ndGgoZGlzdF9wcmkpKSkNCikNCg0KIyDnuarlnJbvvJpGKGQpIOavlOi8g++8iOS9v+eUqCBFQ0RG77yJDQpnZ3Bsb3QoZGZfZGlzdCwgYWVzKHggPSBkaXN0X20sIGNvbG91ciA9IFR5cGUpKSArDQogIHN0YXRfZWNkZihzaXplID0gMS4yKSArDQogIHNjYWxlX3hfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDIwMDApLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gc2VxKDAsMjAwMCw1MDApLA0KICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gZnVuY3Rpb24oeCkgcGFzdGUwKHgsICJtIikpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICLlhaznp4Hnq4vlrbjmoKHliLDpgJ/po5/lupfnmoTmnIDov5Hot53pm6IgRihkKSDmr5TovIMiLA0KICAgIHggICAgID0gIui3nemboiBk77yI5YWs5bC677yJIiwNCiAgICB5ICAgICA9ICJGKGQpID0g5a245qCh6Led6ZuiIOKJpCBkIOeahOavlOS+iyIsDQogICAgY29sb3VyPSAi5a245qCh6aGe5Z6LIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVpdGkgVEMiKSwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCiMgS1Mg5qqi5a6a77ya5q+U6LyD6Led6Zui5YiG5biD5piv5ZCm6aGv6JGX5LiN5ZCMDQprc190ZXN0IDwtIGtzLnRlc3QoZGlzdF9wdWIsIGRpc3RfcHJpKQ0KcHJpbnQoa3NfdGVzdCkNCmBgYA0K5YWs56uL5a245qChIEYoZCnmm7Lnt5rkuIrljYfovIPlv6vvvIzooajnpLrot53pm6LpgJ/po5/lupfovIPov5HjgIHlj6/lj4rmgKfmm7Tpq5jjgILnp4Hnq4vlrbjmoKHnm7jlsI3ot53pm6LovIPpgaDvvIzpoa/npLrlhaznq4vlrbjmoKHlkajpgorpgJ/po5/lr4bluqbovIPpq5jjgIINCg0KIyMgUGFydCAzOiDmoKHlnJLlgaXlurfnlJ/mtLvoqIjnlasNCuagueaTmuiHuuWMl+W4guihm+eUn+WxgOaWvDExM+W5tOWFrOW4g+S5i+WBpeW6t+WFg+e0oOmkkOmjsualreiAhea4heWGiu+8jOiomOmMhOavj+mWk+WBpeW6t+mjsumjn+mkkOW7s+m7nuS9je+8jOS4pumHneWwjeiHuuWMl+W4guavj+mWk+Wci+Wwj+aWueWck+S4gOWFrOmHjOmAsuihjOeSsOWfn+WIhuaekO+8jOiomOmMhOavj+mWk+WtuOagoeeSsOWfn+WFp+WBpeW6t+mjsumjn+W6l+Wutu+8jOaPkOS+m+WtuOerpeWcqOWklumjn+S4iuabtOWkmuWFg+S4lOeHn+mkiuixkOWvjOeahOa2iOiyu+mBuOaTh++8jOS4pumHneWwjeeSsOWfn+WFp+acquiomOmMhOWIsOWBpeW6t+mjsumjn+W6l+WutuS5i+WtuOagoeWItuWumumAsuS4gOatpeagoeWckuWBpeW6t+mjsumjn+ioiOeVq+OAgg0KYGBge3J9DQpoZWFsdGh5X3Jlc3RhdXJhbnQgPC0gcmVhZC5jc3YoIkM6L1VzZXJzLzYxOGFtL09uZURyaXZlL+ahjOmdoi/nqbrplpPliIbmnpDmnJ/mnKvloLHlkYov6Ie65YyX5biC5YGl5bq36aOy6aOf6aSQ5buz6LOH5paZ6KGoIC0gaGVhbHRoeV9mb29kX3Nob3BzX3dpdGhfYWRkcmVzcy5jc3YiLCBmaWxlRW5jb2RpbmcgPSAiVVRGLTgiKQ0KaGVhZChoZWFsdGh5X3Jlc3RhdXJhbnQpDQpgYGANCmBgYHtyfQ0KaGVhbHRoeV9nZW9tIDwtIHN0X2FzX3NmKA0KICBoZWFsdGh5X3Jlc3RhdXJhbnQsDQogIGNvb3JkcyA9IGMoIue2k+W6piIsICLnt6/luqYiKSwNCiAgY3JzID0gNDMyNg0KKQ0KDQpoZWFsdGh5X2dlb21fMzgyNiA8LSBzdF90cmFuc2Zvcm0oaGVhbHRoeV9nZW9tLCAzODI2KQ0KU0NIT09MX2J1ZmZlciA8LSBzdF9idWZmZXIoU0NIT09MLCBkaXN0ID0gMTAwMCkNCg0Kc2Nob29sX2hlYWx0aHkgPC0gc3Rfam9pbihTQ0hPT0xfYnVmZmVyLCBoZWFsdGh5X2dlb21fMzgyNiwgam9pbiA9IHN0X2ludGVyc2VjdHMsIGxlZnQgPSBUUlVFKQ0Kc3VtbWFyeV90YWJsZSA8LSBzY2hvb2xfaGVhbHRoeSAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQ0KICBncm91cF9ieShOYW1lKSAlPiUgICANCiAgc3VtbWFyaXNlKOW6l+WutuaVuCA9IHN1bSghaXMubmEo5ZWG5a625ZCN56ixKSkpICANCmBgYA0KYGBge3J9DQpzY2hvb2xfaGVhbHRoeV9pbmZvIDwtIHNjaG9vbF9oZWFsdGh5ICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KCkNClNDSE9PTF9lbnJpY2hlZCA8LSBTQ0hPT0wgJT4lDQogIGxlZnRfam9pbihzY2hvb2xfaGVhbHRoeV9pbmZvLCBieSA9ICJTSUQiKQ0KDQpoZWFsdGh5X2dlb21fZGYgPC0gaGVhbHRoeV9nZW9tXzM4MjYgJT4lDQogIG11dGF0ZShoZWFsdGh5X2dlb20gPSBnZW9tZXRyeSkgJT4lDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKSAlPiUNCiAgc2VsZWN0KOWVhuWutuWQjeeosSwgaGVhbHRoeV9nZW9tKQ0KDQpzY2hvb2xfaGVhbHRoeV9kaXN0YW5jZSA8LSBTQ0hPT0xfZW5yaWNoZWQgJT4lDQogIGxlZnRfam9pbihoZWFsdGh5X2dlb21fZGYsIGJ5ID0gIuWVhuWutuWQjeeosSIpICU+JQ0KICBtdXRhdGUo6Led6ZuiID0gYXMubnVtZXJpYyhzdF9kaXN0YW5jZShnZW9tZXRyeSwgaGVhbHRoeV9nZW9tLCBieV9lbGVtZW50ID0gVFJVRSkpKQ0KYGBgDQpgYGB7cn0NCnNjaG9vbHNfbm9fcmVzdGF1cmFudCA8LSBzY2hvb2xfaGVhbHRoeSAlPiUgZmlsdGVyKGlzLm5hKOWVhuWutuWQjeeosSkpDQpzY2hvb2xzX25vX3Jlc3RhdXJhbnQkTmFtZQ0KYGBgDQrkuIDlhazph4znkrDln5/lhafnhKHlgaXlurfpo7Lpo5/ppJDlu7PlrbjmoKENCg0KYGBge3J9DQpUUEVfVmlsbGFnZV9iYXNlbWFwXzIgPC0gdG1fc2hhcGUoVFBFX1ZpbGwpICsNCiAgdG1fcG9seWdvbnMoY29sID0gImdyZXkiKSArDQogIHRtX2xheW91dCh0aXRsZSA9ICLoh7rljJfluILlhaznp4Hnq4vlnIvlsI/oiIflgaXlurfpo7Lpo5/ppJDlu7PoiIfpgJ/po5/lupfpu57kvY3liIbluIPlnJYiKQ0KDQpTQ0hPT0xfbWFwIDwtIHRtX3NoYXBlKFNDSE9PTCkgKw0KICB0bV9kb3RzKGZpbGwgPSAiYmx1ZSIsIHNpemUgPSAwLjI1KQ0KDQpIZWFsdGh5X2Zvb2RfbWFwIDwtIHRtX3NoYXBlKGhlYWx0aHlfZ2VvbV8zODI2KSArDQogIHRtX2RvdHMoZmlsbCA9ICJncmVlbiIsIHNpemUgPSAwLjI1KQ0KDQpUUEVfVmlsbGFnZV9iYXNlbWFwXzIgKyBTQ0hPT0xfbWFwICsgSGVhbHRoeV9mb29kX21hcCsgRmFzdGZvb2RfbWFwICsgDQogIHRtX2FkZF9sZWdlbmQodHlwZSA9ICJzeW1ib2wiLA0KICAgICAgICAgICAgICBsYWJlbHMgPSBjKCLlrbjmoKEiLCAi5YGl5bq36aOy6aOf6aSQ5buzIiwgIumAn+mjn+W6lyIpLA0KICAgICAgICAgICAgICBjb2wgPSBjKCJibHVlIiwgImdyZWVuIiwgInJlZCIpLA0KICAgICAgICAgICAgICB0aXRsZSA9ICLpoZ7liKUiKSArIA0KICB0bV9zY2FsZV9iYXIoKSArIA0KICB0bV9jb21wYXNzKCkgKyANCiAgdG1fbGF5b3V0KCkNCmBgYA0KYGBge3J9DQp0bV9zaGFwZShUUEVfVmlsbCkgKyB0bV9wb2x5Z29ucyhjb2wgPSAiZ3JleSIpICsgDQogIFNDSE9PTF9tYXAgKyBIZWFsdGh5X2Zvb2RfbWFwICsgDQogIHRtX3NoYXBlKFNDSE9PTF9idWZmZXIpICsgDQogIHRtX2JvcmRlcnMoY29sID0gImJsYWNrIiwgbHdkID0gMSkgKw0KICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAic3ltYm9sIiwgbGFiZWxzID0gYygi5a245qChIiwgIuWBpeW6t+mjsumjn+mkkOW7syIpLCBjb2wgPSBjKCJibHVlIiwgImdyZWVuIiksIHRpdGxlID0gIumhnuWIpSIpDQpgYGANCmBgYHtyfQ0Kc2Nob29sX2hlYWx0aHlfZGYgPC0gc3RfZHJvcF9nZW9tZXRyeShzY2hvb2xfaGVhbHRoeV9kaXN0YW5jZSkNCndyaXRlX2NzdihzY2hvb2xfaGVhbHRoeV9kZiwgInNjaG9vbF9oZWFsdGh5X3Jlc3VsdF93aXRoX2Rpc3RhbmNlLmNzdiIpDQpgYGANCg0KDQrlrbjmoKHoiIfpgJ/po5/lupfnhrHljYDph43nlornr6npgbjlj4rlgaXlurfppJDou4rmjqjolqboqK3nva7lnLDpu54NCmBgYHtyfQ0KZ3JpZF81MDAgPC0gZ3JpZF81MDAgJT4lDQogIG11dGF0ZSgNCiAgICBqb2ludCA9IGNhc2Vfd2hlbihob3RzcG90X2Zhc3QgPT0gIkhpZ2gtSGlnaCIgJiBob3RzcG90X3NjaG9vbCA9PSAiSGlnaC1IaWdoIiB+ICJCb3RoIEhpZ2giLA0KICAgICAgICAgICAgICAgICAgICAgIGhvdHNwb3RfZmFzdCA9PSAiSGlnaC1IaWdoIiB+ICJGYXN0LWZvb2QgT25seSIsDQogICAgICAgICAgICAgICAgICAgICAgaG90c3BvdF9zY2hvb2wgPT0gIkhpZ2gtSGlnaCIgfiAiU2Nob29scyBPbmx5IiwNCiAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5laXRoZXIiKSwNCiAgICBqb2ludCA9IGZhY3Rvcihqb2ludCwgbGV2ZWxzID0gYygiQm90aCBIaWdoIiwiRmFzdC1mb29kIE9ubHkiLCAiU2Nob29scyBPbmx5IiwgIk5laXRoZXIiKSkNCiAgKQ0KYnVmMzAwIDwtIHN0X2J1ZmZlcihTQ0hPT0wsIGRpc3QgPSAzMDApDQpjYW5kaWRhdGVzIDwtIGdyaWRfNTAwICU+JQ0KICBmaWx0ZXIoam9pbnQgPT0gIkJvdGggSGlnaCIpICU+JQ0KICBmaWx0ZXIobGVuZ3RocyhzdF9pbnRlcnNlY3RzKC4sYnVmMzAwKSk+MCkNCmhlYWx0aHlfdHJ1Y2tzX2Zvcl9ib3RoX2hpZ2ggPC0gc3RfY2VudHJvaWQoc3RfZ2VvbWV0cnkoY2FuZGlkYXRlcykpICU+JSBzdF9hc19zZigpDQpoZWFsdGh5X3RydWNrc19mb3Jfbm9fcmVzdGF1cmFudCA8LSBzdF9jZW50cm9pZChzdF9nZW9tZXRyeShzY2hvb2xzX25vX3Jlc3RhdXJhbnQpKSAlPiUgc3RfYXNfc2YoKQ0KDQp0bV9zaGFwZShncmlkXzUwMCkgKyB0bV9wb2x5Z29ucyhjb2wgPSAiam9pbnQiLCBmaWxsLnNjYWxlID0gdG1fc2NhbGUodmFsdWVzID0gYygicmVkIiwib3JhbmdlIiwiYmx1ZSIsImdyYXk4MCIpKSwgZmlsbC5sZWdlbmQgPSB0bV9sZWdlbmQodGl0bGUgPSAiSG90c3BvdCBDYXRlZ29yeSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm9yZGVyLmNvbCA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xfYWxwaGEgPSAwLjMpICsNCiAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiU2Nob29sIHZzIEZhc3QtRm9vZCBCaXZhcmlhdGUgSG90c3BvdHMiLA0KICAgICAgICAgICAgbGVnZW5kLm91dHNpZGUgPSBUUlVFLA0KICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSkNCmBgYA0K5bCL5om+5L2N5pa86auY6aKo6Zqq5Y2A5Z+f5a245qChDQpgYGB7cn0NCmNhbmRpZGF0ZXNfYnVmIDwtIHN0X2J1ZmZlcihjYW5kaWRhdGVzLCBkaXN0ID0gMzAwKQ0Kc2Nob29sX25lYXIgPC0gU0NIT09MICU+JSBmaWx0ZXIobGVuZ3RocyhzdF9pbnRlcnNlY3RzKC4sIGNhbmRpZGF0ZXNfYnVmKSk+MCkNCnNjaG9vbF9uYW1lcyA8LSB1bmlxdWUoc2Nob29sX25lYXIkTmFtZSkNCnNjaG9vbF9uYW1lcw0KYGBgDQpgYGB7cn0NCnRtX3NoYXBlKFRQRV9WaWxsKSArDQogIHRtX2JvcmRlcnMoY29sID0gImdyYXk3MCIpICsNCg0KdG1fc2hhcGUoY2FuZGlkYXRlcykgKw0KICB0bV9wb2x5Z29ucygNCiAgICBjb2wgICAgICAgICA9ICJyZWQiLA0KICAgIGZpbGxfYWxwaGEgID0gMC4yLA0KICAgIGJvcmRlci5jb2wgID0gInJlZCIsDQogICAgZmlsbC5sZWdlbmQgPSAiSGlnaC1yaXNrIEdyaWQgKEJvdGggSG90c3BvdHMpIg0KICApICsNCg0KdG1fc2hhcGUoaGVhbHRoeV90cnVja3NfZm9yX25vX3Jlc3RhdXJhbnQpICsNCiAgdG1fc3ltYm9scygNCiAgICBjb2wgICAgICAgICA9ICJncmVlbiIsDQogICAgc2hhcGUgICAgICAgPSAyMSwNCiAgICBzaXplICAgICAgICA9IDAuMjUsDQogICAgYm9yZGVyLmNvbCAgPSAiZGFya2dyZWVuIiwNCiAgICBsZWdlbmQuc2hvdyA9IFRSVUUsDQogICAgZmlsbC5sZWdlbmQgPSAiUmVjb21tZW5kZWQgRm9vZCBUcnVjayBTaXRlIg0KICApICsNCnRtX2FkZF9sZWdlbmQodHlwZSA9ICJmaWxsIiwgDQogICAgICAgICAgICAgIGxhYmVscyA9ICJIaWdoLXJpc2sgR3JpZCIsIA0KICAgICAgICAgICAgICBjb2wgPSAicmVkIiwgDQogICAgICAgICAgICAgIGFscGhhID0gMC4yKSArDQoNCnRtX2FkZF9sZWdlbmQodHlwZSA9ICJzeW1ib2wiLCANCiAgICAgICAgICAgICAgbGFiZWxzID0gIlJlY29tbWVuZGVkIEZvb2QgVHJ1Y2sgU2l0ZSIsIA0KICAgICAgICAgICAgICBjb2wgPSAiZ3JlZW4iLCANCiAgICAgICAgICAgICAgc2hhcGUgPSAyMSkgKw0KdG1fbGF5b3V0KA0KICBtYWluLnRpdGxlICAgICAgPSAiUmVjb21tZW5kZWQgSGVhbHRoeSBGb29kIFRydWNrIFNpdGVzIiwNCiAgbWFpbi50aXRsZS5zaXplID0gMS4yLA0KICBtYWluLnRpdGxlLnBvc2l0aW9uID0gImNlbnRlciIsDQogIGxlZ2VuZC5vdXRzaWRlICA9IFRSVUUsDQogIGxlZ2VuZC50aXRsZS5zaXplID0gMS4wLA0KICBsZWdlbmQudGV4dC5zaXplICA9IDAuOCwNCiAgZnJhbWUgICAgICAgICAgICA9IEZBTFNFDQopDQpgYGANCumHneWwjemrmOmiqOmaquWcsOWNgOWPiuaWueWck+S4gOWFrOmHjOevhOWcjeWFp+eEoeWBpeW6t+mjsumjn+mkkOW7s+S5i+WtuOagoe+8jOaPkOS+m+WBpeW6t+mjsumjn+mkkOi7iuW7uuitsOioree9ruWcsOm7nuOAgg0KDQoNCg0KDQoNCg0KDQo=