#讀取資料

health_sf = st_read("./data/臺北市健康餐飲.kml", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `臺北市健康餐飲' from data source 
  `D:\R\spatial analysis\Final\data\臺北市健康餐飲.kml' using driver `KML'
Simple feature collection with 234 features and 2 fields
Geometry type: MULTIPOINT
Dimension:     XY
Bounding box:  xmin: 121.4936 ymin: 24.96722 xmax: 121.6181 ymax: 25.13751
Geodetic CRS:  WGS 84
health_sf <- st_transform(health_sf, crs = 3826)
SCHOOL_sf = st_read("./data/SCHOOL.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `SCHOOL' from data source `D:\R\spatial analysis\Final\data\SCHOOL.shp' 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
Taipei_Vill_sf = st_read("./data/Taipei_Vill.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Taipei_Vill' from data source `D:\R\spatial analysis\Final\data\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_Fastfood_sf = st_read("./data/Tpe_Fastfood.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Tpe_Fastfood' from data source `D:\R\spatial analysis\Final\data\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

Part 1 - 實作題

第一題

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

  • \(H_0\): A區平均值等於B區平均值
  • \(H_A\): A區平均值不等於B區平均值
  • 顯著水準:\(\alpha = 0.05\)
FAST_COUNTY_sf = st_join(Tpe_Fastfood_sf, Taipei_Vill_sf, join = st_within)
FAST_buffer_sf = st_buffer(FAST_COUNTY_sf, dist = 1000)
FAST_SCHOOL_sf = st_join(SCHOOL_sf, FAST_buffer_sf, join = st_within)

FAST_A_sf = filter(FAST_SCHOOL_sf, TOWN %in% c("文山區", "大安區", "中正區"))
FAST_B_sf = filter(FAST_SCHOOL_sf, TOWN %in% c("信義區", "南港區", "松山區"))

count_A_table = table(FAST_A_sf$ID)
count_B_table = table(FAST_B_sf$ID)

count_A_df = as.data.frame(count_A_table)
colnames(count_A_df) <- c("ALIAS","學校數")
count_B_df = as.data.frame(count_B_table)
colnames(count_B_df) <- c("ALIAS","學校數")

t.test(count_A_df$學校數, count_B_df$學校數, alternative = "two.sided")

    Welch Two Sample t-test

data:  count_A_df$學校數 and count_B_df$學校數
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 
  • p-value=0.1163>\(\alpha\),所以無法拒絕虛無假設,因此兩區的涵蓋學校數量的平均值,沒有統計顯著差異

第二題

# 建立 500 公尺網格
grid500 <- st_make_grid(Taipei_Vill_sf, cellsize = 500, square = TRUE) %>% 
  st_sf() %>% 
  st_intersection(st_union(Taipei_Vill_sf)) %>% 
  st_sf()
st_crs(grid500) <- 3826

# 計算每格內速食店數量
grid500$Fastfood_N <- lengths(st_intersects(grid500, Tpe_Fastfood_sf))

# 建立 contiguity 鄰接關係 (queen)
nb_queen <- poly2nb(grid500, queen = TRUE)
lw_queen <- nb2listw(nb_queen, style = "W")

# 繪製網格 + 速食店數量
tmap_mode("plot")
tm_shape(grid500) +
  tm_polygons("Fastfood_N", palette = "YlOrRd", title = "the amount of fast food restaurants") +
  tm_layout(main.title = "500-meter grid of fast food restaurants")


# 計算 Moran’s I Correlogram(order = 10)
moran_corr <- sp.correlogram(nb_queen, grid500$Fastfood_N, order = 10, method = "I", style = "W")

# 繪圖
plot(moran_corr, main = "Moran's I Correlogram of Fast Food Counts")

##### 額外:使用FDR校正 #####

# 計算前 10 階的 Moran's I
moran_corr <- sp.correlogram(nb_q, grid500$Fastfood_N, order = 10, method = "I", style = "W", zero.policy = TRUE)

# 轉成 dataframe 繪圖用
df_corr <- data.frame(
  Order = 1:10,
  I = moran_corr$res[, 1],  # 第一欄是 I
  p = moran_corr$res[, 2]   # 第二欄是 p 值
)

# 繪圖
ggplot(df_corr, aes(x = Order, y = I)) +
  geom_line() +
  geom_point(aes(color = p < 0.05), size = 3) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "grey40") +
  scale_color_manual(values = c("FALSE" = "grey", "TRUE" = "red"), labels = c("Not Significant", "Significant")) +
  labs(title = "Moran's I Correlogram of Fast Food Counts",
       subtitle = "Spatial autocorrelation by contiguity order",
       x = "Contiguity Order", y = "Moran's I", color = "p < 0.05") +
  theme_minimal()

##### 額外:使用FDR校正 #####

# 建立 500m 網格
grid500 <- st_make_grid(Taipei_Vill_sf, cellsize = 500, square = TRUE) %>% 
  st_sf() %>%
  st_intersection(st_union(Taipei_Vill_sf)) %>%
  st_sf()
st_crs(grid500) <- 3826

# 加入每格速食店數量
grid500$Fastfood_N <- lengths(st_intersects(grid500, Tpe_Fastfood_sf))

# 建立鄰接關係(Queen)
nb_q <- poly2nb(grid500, queen = TRUE)
lw_q <- nb2listw(nb_q, style = "W", zero.policy = TRUE)

# 計算 Local Moran's I
lisa <- localmoran(grid500$Fastfood_N, lw_q, zero.policy = TRUE)

# 加入結果並 FDR 校正
grid500$Ii         <- lisa[,1]
grid500$EIi        <- lisa[,2]
grid500$VarIi      <- lisa[,3]
grid500$Z.Ii       <- lisa[,4]
grid500$p.Ii       <- lisa[,5]
grid500$p.Ii.fdr   <- p.adjust(lisa[,5], method = "fdr")

# LISA 分類(依照顯著性 + 正負相關)
lag_FF <- lag.listw(lw_q, grid500$Fastfood_N)
grid500$lag_FF <- lag_FF

grid500$LISA_type <- "Not significant"
grid500$LISA_type[grid500$Fastfood_N > mean(grid500$Fastfood_N) & 
                  grid500$lag_FF > mean(grid500$lag_FF) & 
                  grid500$p.Ii.fdr < 0.05] <- "High-High"
grid500$LISA_type[grid500$Fastfood_N < mean(grid500$Fastfood_N) & 
                  grid500$lag_FF < mean(grid500$lag_FF) & 
                  grid500$p.Ii.fdr < 0.05] <- "Low-Low"
grid500$LISA_type[grid500$Fastfood_N > mean(grid500$Fastfood_N) & 
                  grid500$lag_FF < mean(grid500$lag_FF) & 
                  grid500$p.Ii.fdr < 0.05] <- "High-Low"
grid500$LISA_type[grid500$Fastfood_N < mean(grid500$Fastfood_N) & 
                  grid500$lag_FF > mean(grid500$lag_FF) & 
                  grid500$p.Ii.fdr < 0.05] <- "Low-High"

grid500$LISA_type <- factor(grid500$LISA_type,
                            levels = c("High-High", "Low-Low", "High-Low", "Low-High", "Not significant"))

# 畫出 LISA 類型地圖
tmap_mode("plot")
ℹ tmap mode set to "plot".
tm_shape(grid500) +
  tm_polygons("LISA_type", palette = c("red", "blue", "orange", "skyblue", "grey80"),
              title = "LISA Type\n(FDR Corrected)") +
  tm_layout(main.title = "Local Moran's I for Fast Food Clusters in Taipei",
            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(main.title = )`

速食店在空間上具有顯著的聚集現象,尤其在第 1 階(500 公尺鄰近)時 Moran’s I 約為 0.18,顯著高於 0,代表空間自相關強烈。

隨著鄰接階數增加,Moran’s I 值逐漸下降,但在前 6 階(即約 3 公里範圍內)仍維持正向且有顯著性,表示聚集效應具有一定的空間延伸範圍。

自第 7 階之後,Moran’s I 值接近 0,且信賴區間與 0 重疊,顯示自相關趨於隨機,空間聚集效應已不明顯。

總結: 速食店在 500 公尺至 3 公里範圍內存在顯著空間聚集現象,顯示店家選址或商圈形成具地理集中性;超過此距離後,自相關趨近隨機,表示空間影響效應已消散。

第三題

# --- STEP 0: 建立學校數量欄位(與前面一樣) ---
grid500$School_N <- lengths(st_intersects(grid500, SCHOOL_sf))

# 建立 Queen 鄰接權重(如果還沒執行過)
nb_q <- poly2nb(grid500, queen = TRUE)
lw_q <- nb2listw(nb_q, style = "W", zero.policy = TRUE)

# 找出有鄰居的格子
valid_idx <- which(card(nb_q) > 0)

# 初始化並計算 Gi* 統計量
Gi_Z_FF <- rep(NA, nrow(grid500))
Gi_Z_FF[valid_idx] <- localG(grid500$Fastfood_N[valid_idx], lw_q, zero.policy = TRUE)

grid500$Gi_Z_FF <- Gi_Z_FF
grid500$p_FF <- 2 * pnorm(-abs(grid500$Gi_Z_FF))  # 雙尾 p 值
grid500$p_FF_fdr <- p.adjust(grid500$p_FF, method = "fdr")  # FDR 修正
grid500$Hotspot_FF <- ifelse(grid500$p_FF_fdr < 0.05 & grid500$Gi_Z_FF > 0, "Hotspot", "Not significant")

# 1. 計算 Gi* Z 值(學校)
Gi_Z_SC <- rep(NA, nrow(grid500))
Gi_Z_SC[valid_idx] <- localG(grid500$School_N[valid_idx], lw_q, zero.policy = TRUE)
grid500$Gi_Z_SC <- Gi_Z_SC

# 2. 計算 p 值與 FDR 校正
grid500$p_SC <- 2 * pnorm(-abs(grid500$Gi_Z_SC))
grid500$p_SC_fdr <- p.adjust(grid500$p_SC, method = "fdr")

# 3. 正確產出 Hotspot_SC
grid500$Hotspot_SC <- ifelse(
  is.na(grid500$p_SC_fdr), "Not significant",
  ifelse(grid500$p_SC_fdr < 0.05 & grid500$Gi_Z_SC > 0, "Hotspot", "Not significant")
)

# 4. 設定為 factor,避免 tmap 判定為 NA
grid500$Hotspot_SC <- factor(grid500$Hotspot_SC, levels = c("Hotspot", "Not significant"))
# --- STEP 2: 熱區圖繪製 ---

# 速食店熱區地圖
tm_shape(grid500) +
  tm_polygons("Hotspot_FF", palette = c("red", "grey80"),
              title = "Fast Food Gi* Hotspot") +
  tm_layout(main.title = "Fast Food Hotspots (Gi*, FDR corrected)",
            legend.outside = TRUE)


# 學校熱區地圖
tm_shape(grid500) +
  tm_polygons("Hotspot_SC", palette = c("blue", "grey80"),
              title = "School Gi* Hotspot") +
  tm_layout(main.title = "School Hotspots (Gi*, FDR corrected)",
            legend.outside = TRUE)

結論(速食店與學校熱區): 速食店熱區(紅色)主要集中於圖中南側偏中與東南區域,呈現連續聚集,顯示這些區域的速食店密度顯著高於其他地區。 學校熱區(藍色)則較為零星分布,雖也多集中於南側,但與速食店的熱區僅部分重疊,整體空間分布差異明顯。 兩者空間分布顯示:速食店熱區與學校熱區不完全一致,但有重疊。

Part 2

第一題

#讀取資料

fastfood <- st_read("./data/Tpe_Fastfood.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Tpe_Fastfood' from data source `D:\R\spatial analysis\Final\data\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
villages <- st_read("./data/Taipei_Vill.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Taipei_Vill' from data source `D:\R\spatial analysis\Final\data\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
schools <- st_read("./data/SCHOOL.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `SCHOOL' from data source `D:\R\spatial analysis\Final\data\SCHOOL.shp' 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
#Step 2:選出三個行政區的學校(大安、文山、信義)
villages <- villages %>%
  filter(grepl("大安區|文山區|信義區", TOWN))  # 注意欄位名稱可能需對應你的資料,如 DISTRICT_NAME

#空間相交找出屬於三區的學校
schools_three <- st_join(schools, villages, join = st_within) %>%
  filter(!is.na(TOWN))  # 確保有交集成功

#計算每所學校到所有速食店的距離,並做 F(d) 函數

#定義距離門檻(以公尺為單位),這裡設定從 0 到 1000 公尺,每 100 公尺一個門檻
d_values <- seq(0, 1000, by = 100)

#建立一個函數計算 F(d) 值
calc_Fd <- function(schools_sf, fastfood_sf, d_seq) {
  result <- data.frame(distance = d_seq, Fd = NA)
  n_school <- nrow(schools_sf)
  
  for (i in seq_along(d_seq)) {
    d <- set_units(d_seq[i], "m") #單位為公尺
    
    #每間學校是否在 d 公尺內至少有一間速食店?
    count_within_d <- sum(sapply(1:n_school, function(j) {
      any(st_distance(schools_sf[j,], fastfood_sf) <= d)
    }))
    
    #F(d) = 滿足條件的學校數 / 總學校數
    result$Fd[i] <- count_within_d / n_school
  }
  return(result)
}

#分三區做 F(d)
fd_da_an <- calc_Fd(schools_three %>% filter(grepl("大安區", TOWN)), fastfood, d_values)
fd_wen_shan <- calc_Fd(schools_three %>% filter(grepl("文山區", TOWN)), fastfood, d_values)
fd_xin_yi <- calc_Fd(schools_three %>% filter(grepl("信義區", TOWN)), fastfood, d_values)

#加上區名
fd_da_an$District <- "大安區"
fd_wen_shan$District <- "文山區"
fd_xin_yi$District <- "信義區"

#合併資料
fd_all <- rbind(fd_da_an, fd_wen_shan, fd_xin_yi)

#繪圖比較 F(d)
ggplot(fd_all, aes(x = distance, y = Fd, color = District)) +
  geom_line(size = 1.2) +
  scale_x_continuous(breaks = d_values) +
  labs(
    title = "F(d) 函數比較:三區學校周邊速食店分布",
    x = "距離 d(公尺)",
    y = "F(d) = 至少一間速食店的學校比例"
  ) +
  theme_minimal() +
  theme(legend.title = element_blank())

NA
NA
set.seed(42)  #為了結果可重現

#只處理大安區
schools_da_an <- schools_three %>% filter(grepl("大安區", TOWN))
n_schools <- nrow(schools_da_an)
da_an_area <- villages %>% filter(TOWN == "大安區")

#模擬次數
n_sim <- 99

#建立一個空的 data frame 儲存模擬結果
sim_Fd_all <- matrix(NA, nrow = length(d_values), ncol = n_sim)

#進行模擬
for (i in 1:n_sim) {
  # 在大安區隨機產生 n_schools 個點
  random_pts <- st_sample(da_an_area, size = n_schools, type = "random") %>%
    st_as_sf()
  
  # 計算 F(d)
  sim_fd <- calc_Fd(random_pts, fastfood, d_values)
  
  # 存入模擬結果矩陣
  sim_Fd_all[, i] <- sim_fd$Fd
}

#產生模擬的 envelopes(最大值與最小值)
Fd_sim_min <- apply(sim_Fd_all, 1, min)
Fd_sim_max <- apply(sim_Fd_all, 1, max)

#合併為一個資料框
fd_envelope <- data.frame(
  distance = d_values,
  min = Fd_sim_min,
  max = Fd_sim_max
)

#加上實際的 F(d)
fd_actual <- fd_da_an  # 剛剛計算過的大安區實際 F(d)

#Step 6:繪圖(實際 vs. 模擬區間)
ggplot() +
  geom_ribbon(data = fd_envelope, aes(x = distance, ymin = min, ymax = max), 
              fill = "lightblue", alpha = 0.4) +
  geom_line(data = fd_actual, aes(x = distance, y = Fd), color = "red", size = 1.2) +
  labs(
    title = "大安區學校周邊速食店的 F(d) 與模擬區間",
    subtitle = "紅線為實際值,藍色區域為模擬 99 次所得之區間",
    x = "距離 d(公尺)",
    y = "F(d)"
  ) +
  theme_minimal()

set.seed(42)

#只處理信義區
schools_da_an <- schools_three %>% filter(grepl("信義區", TOWN))
n_schools <- nrow(schools_da_an)
da_an_area <- villages %>% filter(TOWN == "信義區")

#模擬次數
n_sim <- 99

#建立一個空的 data frame 儲存模擬結果
sim_Fd_all <- matrix(NA, nrow = length(d_values), ncol = n_sim)

#進行模擬
for (i in 1:n_sim) {
  #信義區隨機生成 n_schools 個點
  random_pts <- st_sample(da_an_area, size = n_schools, type = "random") %>%
    st_as_sf()
  
  # 計算 F(d)
  sim_fd <- calc_Fd(random_pts, fastfood, d_values)
  
  # 存入模擬結果矩陣
  sim_Fd_all[, i] <- sim_fd$Fd
}

#產生模擬的 envelopes(最大值與最小值)
Fd_sim_min <- apply(sim_Fd_all, 1, min)
Fd_sim_max <- apply(sim_Fd_all, 1, max)

#合併為一個資料框
fd_envelope <- data.frame(
  distance = d_values,
  min = Fd_sim_min,
  max = Fd_sim_max
)

#加上實際的 F(d)
fd_actual <- fd_da_an  # 剛剛計算過的信義區實際 F(d)

#Step 6:繪圖(實際 vs. 模擬區間)
ggplot() +
  geom_ribbon(data = fd_envelope, aes(x = distance, ymin = min, ymax = max), 
              fill = "lightblue", alpha = 0.4) +
  geom_line(data = fd_actual, aes(x = distance, y = Fd), color = "red", size = 1.2) +
  labs(
    title = "信義區學校周邊速食店的 F(d) 與模擬區間",
    subtitle = "紅線為實際值,藍色區域為模擬 99 次所得之區間",
    x = "距離 d(公尺)",
    y = "F(d)"
  ) +
  theme_minimal()

set.seed(42)

#只處理文山區
schools_da_an <- schools_three %>% filter(grepl("文山區", TOWN))
n_schools <- nrow(schools_da_an)
da_an_area <- villages %>% filter(TOWN == "文山區")

#模擬次數
n_sim <- 99

#建立一個空的 data frame 儲存模擬結果
sim_Fd_all <- matrix(NA, nrow = length(d_values), ncol = n_sim)

#進行模擬
for (i in 1:n_sim) {
  #文山區隨機生成 n_schools 個點
  random_pts <- st_sample(da_an_area, size = n_schools, type = "random") %>%
    st_as_sf()
  
  # 計算 F(d)
  sim_fd <- calc_Fd(random_pts, fastfood, d_values)
  
  # 存入模擬結果矩陣
  sim_Fd_all[, i] <- sim_fd$Fd
}

#產生模擬的 envelopes(最大值與最小值)
Fd_sim_min <- apply(sim_Fd_all, 1, min)
Fd_sim_max <- apply(sim_Fd_all, 1, max)

#合併為一個資料框
fd_envelope <- data.frame(
  distance = d_values,
  min = Fd_sim_min,
  max = Fd_sim_max
)

#加上實際的 F(d)
fd_actual <- fd_da_an  # 剛剛計算過的文山區實際 F(d)

#Step 6:繪圖(實際 vs. 模擬區間)
ggplot() +
  geom_ribbon(data = fd_envelope, aes(x = distance, ymin = min, ymax = max), 
              fill = "lightblue", alpha = 0.4) +
  geom_line(data = fd_actual, aes(x = distance, y = Fd), color = "red", size = 1.2) +
  labs(
    title = "文山區學校周邊速食店的 F(d) 與模擬區間",
    subtitle = "紅線為實際值,藍色區域為模擬 99 次所得之區間",
    x = "距離 d(公尺)",
    y = "F(d)"
  ) +
  theme_minimal()

第二題

win_polygon <- st_as_sfc(st_bbox(Tpe_Fastfood_sf))
win <- as.owin(win_polygon)

coords <- st_coordinates(Tpe_Fastfood_sf)
fastfood_ppp <- ppp(x = coords[, 1], y = coords[, 2], window = win)

r_seq <- seq(0, 5000, by = 100)

k_result <- Kest(fastfood_ppp, r = r_seq)
k_3000 <- k_result$iso[which(k_result$r == 3000)]
l_3000 <- sqrt(k_3000 / pi) -3000

cat("K(3000) =", k_3000, "\n")
K(3000) = 60111493 
cat("L(3000) =", l_3000, "\n")
L(3000) = 1374.252 
env <- envelope(fastfood_ppp,
                fun = Kest,
                nsim = 99,
                r = r_seq,
                correction = "iso",
                savefuns = TRUE,
                savepatterns = TRUE,
                verbose = FALSE)

r_index <- which(env$r == 3000)
upper_L <- sqrt(env$hi[r_index] / pi) -3000
lower_L <- sqrt(env$lo[r_index] / pi) -3000

cat("95% CI for L(3000): [", lower_L, ",", upper_L, "]\n")
95% CI for L(3000): [ -159.3052 , 162.5662 ]
plot(env, . ~ r, main = "Ripley's k-function with 95% CI")
abline(v = 3000, col = "red", lty = 2)

第三題

public_sf <- subset(SCHOOL_sf, Type == "公立")
private_sf <- subset(SCHOOL_sf, Type == "私立")

coords_pub <- st_coordinates(public_sf)
coords_pri <- st_coordinates(private_sf)
coords_ff  <- st_coordinates(Tpe_Fastfood_sf)

win <- as.owin(st_as_sfc(st_bbox(SCHOOL_sf)))

pp_all_pub <- ppp(x = c(coords_pub[, 1], coords_ff[, 1]),
                  y = c(coords_pub[, 2], coords_ff[, 2]),
                  window = win,
                  marks = factor(c(rep("school", nrow(coords_pub)),
                                   rep("fastfood", nrow(coords_ff)))))

pp_all_pri <- ppp(x = c(coords_pri[, 1], coords_ff[, 1]),
                  y = c(coords_pri[, 2], coords_ff[, 2]),
                  window = win,
                  marks = factor(c(rep("school", nrow(coords_pri)),
                                   rep("fastfood", nrow(coords_ff)))))

# 使用更細的距離序列(每10公尺)
r_vals <- seq(0, 3000, by = 10)

# 公立學校 → 速食店
F_pub <- envelope(pp_all_pub,
                  fun = Fest,
                  i = "school",
                  j = "fastfood",
                  nsim = 99,
                  r = r_vals,
                  verbose = FALSE)

# 私立學校 → 速食店
F_pri <- envelope(pp_all_pri,
                  fun = Fest,
                  i = "school",
                  j = "fastfood",
                  nsim = 99,
                  r = r_vals,
                  verbose = FALSE)


# 公立學校
plot(F_pub, main = "公立學校 → 速食店 的 F-cross 函數", legendargs = list(cex = 0.8))
abline(h = F_pub$theo, col = "blue", lty = 2)


# 私立學校
plot(F_pri, main = "私立學校 → 速食店 的 F-cross 函數", legendargs = list(cex = 0.8))
abline(h = F_pri$theo, col = "blue", lty = 2)


r_target <- 1000  # 想比較的距離

idx_pub <- which.min(abs(F_pub$r - r_target))
idx_pri <- which.min(abs(F_pri$r - r_target))

# 取得各自的 F_cross(1000)
F_pub_obs <- F_pub$obs[idx_pub]
F_pri_obs <- F_pri$obs[idx_pri]

cat("F(1000) 公立學校 → 速食店 =", F_pub_obs, "\n")
F(1000) 公立學校 → 速食店 = 0.4083 
cat("F(1000) 私立學校 → 速食店 =", F_pri_obs, "\n")
F(1000) 私立學校 → 速食店 = 0.5777631 
plot(F_pub$r, F_pub$obs, type = "l", col = "blue", lwd = 2,
     ylim = c(0, max(F_pub$obs, F_pri$obs)),
     xlab = "距離 r", ylab = "F_cross(r)",
     main = "F-cross 比較:公立 vs 私立")
lines(F_pri$r, F_pri$obs, col = "red", lwd = 2)
legend("bottomright", legend = c("公立學校", "私立學校"), col = c("blue", "red"), 
       lty = 1, lwd = 2)
abline(a = 0, b = 1 / max(F_pub$r), col = "gray", lty = 2)  # approx theo line

Part 3

方法一:倡導學校引導學生選擇健康餐盒

healthy_sf <- health_sf %>%
  mutate(行政區 = str_extract(Description, "行政區: [^<]+") %>% 
                   str_remove("行政區: "))

health_sf <- healthy_sf %>%
  filter(行政區 == "信義區")

schools_sf = st_join(SCHOOL_sf, Taipei_Vill_sf, join = st_within)%>%
  filter(TOWN %in% c("信義區"))

win_polygon <- st_as_sfc(st_bbox(health_sf))
win <- as.owin(win_polygon)

coords <- st_coordinates(health_sf)
healthyfood_ppp <- ppp(x = coords[, 1], y = coords[, 2], window = win)

r_seq <- seq(0, 5000, by = 100)

k_result <- Kest(healthyfood_ppp, r = r_seq)
k_3000 <- k_result$iso[which(k_result$r == 3000)]
l_3000 <- sqrt(k_3000 / pi) -3000

cat("K(3000) =", k_3000, "\n")
K(3000) = NA 
cat("L(3000) =", l_3000, "\n")
L(3000) = NA 
env <- envelope(healthyfood_ppp,
                fun = Kest,
                nsim = 99,
                r = r_seq,
                correction = "iso",
                savefuns = TRUE,
                savepatterns = TRUE,
                verbose = FALSE)

r_index <- which(env$r == 3000)
upper_L <- sqrt(env$hi[r_index] / pi) -3000
lower_L <- sqrt(env$lo[r_index] / pi) -3000

cat("95% CI for L(3000): [", lower_L, ",", upper_L, "]\n")
95% CI for L(3000): [ NA , NA ]
plot(env, . ~ r, main = "Ripley's k-function with 95% CI")
abline(v = 3000, col = "red", lty = 2)


coords_pub <- st_coordinates(schools_sf)
coords_ff  <- st_coordinates(health_sf)

win <- as.owin(st_as_sfc(st_bbox(schools_sf)))

pp_all_pub <- ppp(x = c(coords_pub[, 1], coords_ff[, 1]),
                  y = c(coords_pub[, 2], coords_ff[, 2]),
                  window = win,
                  marks = factor(c(rep("school", nrow(coords_pub)),
                                   rep("healthyfood", nrow(coords_ff)))))

r_vals <- seq(0, 3000, by = 2)

F_pub <- envelope(pp_all_pub,
                  fun = Fest,
                  i = "school",
                  j = "healthyfood",
                  nsim = 99,
                  r = r_vals,
                  verbose = FALSE)

plot(F_pub, main = "學校 → 健康餐盒店 的 F-cross 函數", legendargs = list(cex = 0.8))
abline(h = F_pub$theo, col = "blue", lty = 2)

方法二:補助學校周邊健康餐盒業者

#讀取資料
villages <- st_read("./data/Taipei_Vill.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `Taipei_Vill' from data source `D:\R\spatial analysis\Final\data\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
schools <- st_read("./data/SCHOOL.shp", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `SCHOOL' from data source `D:\R\spatial analysis\Final\data\SCHOOL.shp' 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
healthy_food <- st_read("./data/臺北市健康餐飲.kml", options="ENCODING=BIG5")
options:        ENCODING=BIG5 
Reading layer `臺北市健康餐飲' from data source 
  `D:\R\spatial analysis\Final\data\臺北市健康餐飲.kml' using driver `KML'
Simple feature collection with 234 features and 2 fields
Geometry type: MULTIPOINT
Dimension:     XY
Bounding box:  xmin: 121.4936 ymin: 24.96722 xmax: 121.6181 ymax: 25.13751
Geodetic CRS:  WGS 84
#確認CRS
healthy_food <- st_transform(healthy_food, crs = st_crs(schools))

#建立 1500 公尺的緩衝區(以學校為中心)
schools_buffer <- st_buffer(schools, dist = 1500)

#找出每所學校 buffer 內的健康餐飲
school_food_join <- st_join(healthy_food, schools_buffer, join = st_within)


#統計每所學校有多少個健康餐飲據點
result <- school_food_join %>%
  st_drop_geometry() %>%
  group_by(Name.y) %>%
  summarise(HealthyFood_Count = n())

names(schools)
[1] "Type"     "Name"     "SID"      "index"    "geometry"
# [1] "編號" "NAME" "地址" "備註" "geometry"

names(result)
[1] "Name.y"            "HealthyFood_Count"
# [1] "Name.y" "HealthyFood_Count"

schools_summary <- left_join(schools, result, by = c("Name" = "Name.y"))

library(tmap)
base_lyr <- tm_shape(Taipei_Vill_sf) +
  tm_polygons(fill = "white", alpha = 1, border.col = "gray60") +
  tm_layout(frame = FALSE)

base_lyr+
tm_shape(schools_summary) +
  tm_dots(size = 0.5, col = "HealthyFood_Count", palette = "Greens", title = "1500m 內健康餐飲數") +
  tm_layout(title = "台北市學校周邊健康飲食分布")

villages <- st_transform(villages, crs = st_crs(schools))
schools_with_town <- st_join(schools, villages, join = st_within)
school_xinyi <- schools_with_town %>%
  filter(TOWN %in% c("信義區"))
school_xinyi_buffer <- st_buffer(school_xinyi, dist = 1500)

#對應健康餐飲點位
xinyi_school_food_join <- st_join(healthy_food, school_xinyi_buffer, join = st_within)

#統計每所學校的健康餐飲數量
xinyi_result <- xinyi_school_food_join %>%
  st_drop_geometry() %>%
  group_by(Name.y) %>%
  summarise(HealthyFood_Count = n())

#加回geometry
xinyi_summary <- left_join(school_xinyi, xinyi_result, by = c("Name" = "Name.y"))
base_lyr <- tm_shape(xinyi_sf) +
  tm_polygons(fill = "white", alpha = 1, border.col = "gray60") +
  tm_layout(frame = FALSE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.
base_lyr+
tm_shape(xinyi_summary) +
  tm_dots(size = 0.5, col = "HealthyFood_Count", palette = "Greens", title = "1500m 內健康餐飲數") +
  tm_layout(title = "信義區學校周邊健康飲食分布")
[tm_dots()] Argument `title` unknown.[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 "Greens" is named "brewer.greens"Multiple palettes called "greens" found: "brewer.greens", "matplotlib.greens". The first one, "brewer.greens", is returned.

library(tmap)

library(sf)
library(dplyr)
library(tmap)



# 投影轉換:確保所有資料都是一樣的 CRS
healthy_food <- st_transform(healthy_food, st_crs(schools))
villages <- st_transform(villages, st_crs(schools))

# 🔧 處理 MULTIPOINT + Z 維度問題
healthy_food <- healthy_food %>%
  st_cast("POINT") %>%
  st_zm(drop = TRUE)
# 找出信義區學校
schools_with_town <- st_join(schools, villages, join = st_within)
school_xinyi <- schools_with_town %>%
  filter(TOWN %in% c("信義區"))

# 建立緩衝區
school_xinyi_buffer <- st_buffer(school_xinyi, dist = 500)

# 加入健康餐飲點位(找落在 buffer 裡的)
xinyi_school_food_join <- st_join(healthy_food, school_xinyi_buffer, join = st_within)

# 統計每間學校的餐飲點數量
xinyi_result <- xinyi_school_food_join %>%
  st_drop_geometry() %>%
  group_by(Name.y) %>%
  summarise(HealthyFood_Count = n())

#加geometry
xinyi_summary <- left_join(school_xinyi, xinyi_result, by = c("Name" = "Name.y"))

#視覺化
tmap_mode("plot") 
ℹ tmap mode set to "plot".
# 地圖主體與圖層
base_lyr <- tm_shape(xinyi_sf) +
  tm_polygons(fill = "white", alpha = 1, border.col = "gray60") +
  tm_layout(frame = FALSE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.
base_lyr+
tm_shape(school_xinyi_buffer) +
  tm_polygons(
    alpha = 0.2,
    border.col = "blue",
    border.lwd = 1
  ) +
tm_shape(healthy_food) +
  tm_dots(
    size = 0.2,
    fill = "red",
    fill.alpha = 0.7,
    legend.show = TRUE
  ) +
tm_shape(school_xinyi) +
  tm_dots(
    size = 0.06,
    fill = "black",
    col = "white"
  ) +
  tm_text("Name", size = 0.6, col = "black", just = "top") +
tm_title("信義區學校及其 1500 公尺內健康餐飲分布") +
tm_layout(legend.outside = TRUE)
[v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[tm_polygons()] Argument `border.lwd` unknown.[tm_dots()] Arguments `fill.alpha` and `legend.show` unknown.[v3->v4] `tm_text()`: migrate the layer options 'just' to 'options = opt_tm_text(<HERE>)'

#install.packages("gt")
library(gt)
警告: 套件 ‘gt’ 是用 R 版本 4.4.3 來建造的
載入套件:‘gt’

下列物件被遮斷自 ‘package:Hmisc’:

    html

下列物件被遮斷自 ‘package:tmap’:

    metro
library(tmap)
library(gt)

xinyi_result %>%
  gt() %>%
  tab_header(
    title = "信義區學校周邊健康飲食點統計",
    subtitle = "500 公尺範圍內的健康餐飲據點數"
  ) %>%
  cols_label(
    Name.y = "學校名稱",
    HealthyFood_Count = "健康餐飲數量"
  ) %>%
  fmt_number(
    columns = HealthyFood_Count,
    decimals = 0
  )
信義區學校周邊健康飲食點統計
500 公尺範圍內的健康餐飲據點數
學校名稱 健康餐飲數量
三興國小 3
信義國小 1
光復國小 6
博愛國小 1
吳興國小 1
永吉國小 3
福德國小 1
興雅國小 1
NA 217
tmap_mode("view") 
ℹ tmap mode set to "view".
tm_shape(school_xinyi_buffer) +
  tm_polygons(
    alpha = 0.2,
    border.col = "blue",
    border.lwd = 1
  ) +
tm_shape(healthy_food) +
  tm_dots(
    size = 0.2,
    fill = "red",
    fill.alpha = 0.7,
    legend.show = TRUE
  ) +
tm_shape(school_xinyi) +
  tm_dots(
    size = 0.06,
    fill = "black",
    col = "white"
  ) +
  tm_text("Name", size = 0.6, col = "black", just = "top") +
tm_title("信義區學校及其 1500 公尺內健康餐飲分布") +
tm_layout(legend.outside = TRUE)

── tmap v3 code detected ──────────────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[tm_polygons()] Argument `border.lwd` unknown.[tm_dots()] Arguments `fill.alpha` and `legend.show` unknown.[v3->v4] `tm_text()`: migrate the layer options 'just' to 'options = opt_tm_text(<HERE>)'

方法三:綠地派對

  • 找離central feature最近的公園
find_CentralFeature = function(xy_data_frame){
  CF = calc_cf(id=1, points = xy_data_frame)
  CF.x = CF$ATTRIBUTES$CF.x
  CF.y = CF$ATTRIBUTES$CF.y
  CF.coor = c(CF.x, CF.y)
  CF_sf = CF.coor %>% st_point %>% st_sfc %>% st_sf
  return(CF_sf)
}

xinyi_index =  Taipei_Vill_sf$TOWN == "信義區"
xinyi_sf = Taipei_Vill_sf[xinyi_index,] 

Park_sf = st_read("./data/L0101-5.geojson") 
Reading layer `L0101-5' from data source `D:\R\spatial analysis\Final\data\L0101-5.geojson' using driver `GeoJSON'
Simple feature collection with 880 features and 6 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: 296575.9 ymin: 2763553 xmax: 313042.4 ymax: 2782160
Projected CRS: TWD97 / TM2 zone 121
Park_xin_poly_sf = st_join(Park_sf, Taipei_Vill_sf, join = st_within)%>%
  filter(TOWN %in% c("信義區"))
Park_xinyi_sf = st_centroid(Park_sf) %>% 
  st_join(Taipei_Vill_sf, join = st_within)%>%
  filter(TOWN %in% c("信義區"))

school_xinyi_sf = st_join(SCHOOL_sf, Taipei_Vill_sf, join = st_within)%>%
  filter(TOWN %in% c("信義區"))

school_coor = st_coordinates(school_xinyi_sf)
school_xinyi_sf$x = school_coor[, 1]
school_xinyi_sf$y = school_coor[, 2]
school_xinyi_coor_df = as.data.frame(school_xinyi_sf[,14:15])
CF_sf = find_CentralFeature(school_xinyi_coor_df[,1:2])
st_crs(CF_sf) = st_crs(SCHOOL_sf)

distances = st_distance(CF_sf, Park_xinyi_sf)
closest_index = which.min(distances)
closest_park = Park_xinyi_sf[closest_index, ]
closest_park_poly <- st_join(closest_park, Park_xin_poly_sf, join = st_intersects)
  • 畫地圖
library(tmap)
tmap_mode("plot")
ℹ tmap mode set to "plot".
# 地圖主體與圖層
base_lyr <- tm_shape(xinyi_sf) +
  tm_polygons(fill = "white", alpha = 1, border.col = "gray60") +
  tm_layout(frame = FALSE)

── tmap v3 code detected ──────────────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.
# 學校點位
school_lyr <- tm_shape(school_xinyi_sf) +
  tm_dots(fill = "blue", size = 0.2, fill.alpha = 0.8,
          title = "學校")
[tm_dots()] Arguments `fill.alpha` and `title` unknown.
# 中央點 CF
cf_lyr <- tm_shape(CF_sf) +
  tm_symbols(shape = 21, fill = "red", size = 0.6,
             title = "學校重心點")
[tm_symbols()] Argument `title` unknown.
# 所有公園(背景)
park_lyr <- tm_shape(Park_xin_poly_sf) +
  tm_polygons(fill = "lightgreen", alpha = 0.4, border.col = NA,
              legend.show = FALSE)  # 不讓背景公園出現在圖例
[v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[v3->v4] `tm_polygons()`: use `fill.legend = tm_legend_hide()` instead of `legend.show = FALSE`.
# 最近的那一塊公園(重點標記)
closest_park_poly <- st_join(closest_park, Park_xin_poly_sf, join = st_nearest_feature)

closest_park_lyr <- tm_shape(closest_park_poly) +
  tm_polygons(fill = "forestgreen", alpha = 0.7,
              border.col = "darkgreen", lwd = 2,
              title = "最近的公園")
[v3->v4] `tm_polygons()`: use `fill_alpha` instead of `alpha`.[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>)'
# 組合地圖並加上標題與圖例設定

final_map <- base_lyr + park_lyr + closest_park_lyr + school_lyr + cf_lyr +
  tm_add_legend(type = "fill", labels = c("信義區公園", "綠地派對公園"), 
                col = c("lightgreen", "forestgreen")) +
  tm_add_legend(type = "symbol", labels = c("學校Central Feature"), col = "red", shape = 21, size = 0.6) +
  tm_add_legend(type = "symbol", labels = c("學校"), col = "blue", shape = 21, size = 0.2) +
  tm_scalebar(position = c("left", "bottom")) + 
  tm_compass(position = c("right", "top")) +
  tm_layout(
    main.title = "信義區:綠地派對舉辦地點",
    main.title.size = 1.2,
    legend.position = c("left", "bottom"),
    legend.bg.color = "white",
    legend.bg.alpha = 0.7,
    frame = FALSE
  )
[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_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 = )`
print(final_map)
tmap_save(final_map, filename = "xinyi_map.jpg", width = 2000, height = 1600, dpi = 300)

LS0tDQp0aXRsZTogIuepuumWk+WIhuaekCBGaW5hbCBSZXBvcnQiDQphdXRob3I6ICLnrKzlhavntYQiDQpkYXRlOiAiMjAyNS0wNi0wNyINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDYNCiAgICB0b2NfZmxvYXQ6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnJtKGxpc3QgPSBscygpKSAjIOa4hemZpOS7u+S9leePvuacieeahFLnianku7bkuYvliY3ln7fooYzmibnmrKHlt6XkvZznmoTnqIvlvI/norwNCg0KbGlicmFyeShzZikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkocGFscykNCmxpYnJhcnkoY2FydG9ncmFwaHkpIA0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KHVuaXRzKQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGFzcGFjZSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzcGF0c3RhdCkNCmxpYnJhcnkoZ2d0aGVtZXMpDQoNCmxpYnJhcnkoc3ApDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShjbGFzc0ludCkNCmxpYnJhcnkoc3BkZXApDQoNCmxpYnJhcnkoYXNwYWNlKQ0KbGlicmFyeShzcGF0c3RhdC5nZW9tKQ0KbGlicmFyeShzcGF0c3RhdC5leHBsb3JlKQ0KbGlicmFyeShndCkNCg0Kc2V0d2QoIkQ6L1Ivc3BhdGlhbCBhbmFseXNpcy9GaW5hbCIpDQpgYGANCg0KI+iugOWPluizh+aWmQ0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpoZWFsdGhfc2YgPSBzdF9yZWFkKCIuL2RhdGEv6Ie65YyX5biC5YGl5bq36aSQ6aOyLmttbCIsIG9wdGlvbnM9IkVOQ09ESU5HPUJJRzUiKQ0KaGVhbHRoX3NmIDwtIHN0X3RyYW5zZm9ybShoZWFsdGhfc2YsIGNycyA9IDM4MjYpDQpTQ0hPT0xfc2YgPSBzdF9yZWFkKCIuL2RhdGEvU0NIT09MLnNocCIsIG9wdGlvbnM9IkVOQ09ESU5HPUJJRzUiKQ0KVGFpcGVpX1ZpbGxfc2YgPSBzdF9yZWFkKCIuL2RhdGEvVGFpcGVpX1ZpbGwuc2hwIiwgb3B0aW9ucz0iRU5DT0RJTkc9QklHNSIpDQpUcGVfRmFzdGZvb2Rfc2YgPSBzdF9yZWFkKCIuL2RhdGEvVHBlX0Zhc3Rmb29kLnNocCIsIG9wdGlvbnM9IkVOQ09ESU5HPUJJRzUiKQ0KYGBgDQoNCiMgUGFydCAxIC0g5a+m5L2c6aGMDQoNCiMjIOesrOS4gOmhjA0K5YGH6Kit6YCf6aOf5bqX55qE5pyN5YuZ56+E5ZyN5pivIDEg5YWs6YeM77yM5q+U6LyDIEEg5Y2AKOaWh+WxsSvlpKflrokr5Lit5q2jKSDoiIcgQiDljYAo5L+h576pK+WNl+a4ryvmnb7lsbEpIOmAmeWFqeWAi+WcsOWNgOeahOavj+S4gOWutumAn+mjn+W6l+WcqOacjeWLmeWPr+WPiuevhOWcjeWFp++8jOaJgOa2teiTi+WtuOagoeaVuOmHj+eahOW5s+Wdh+WAvO+8jOaYr+WQpuaciee1seioiOmhr+iRl+W3rueVsOOAgijpnIDliJflh7romZvnhKHlgYfoqK3oiIflsI3nq4vlgYfoqK3vvIzntbHoqIjmqqLlrprph4/vvIzku6Xlj4rmqqLlrprnmoTpoa/okZfmsLTmupbnrYkp44CCICANCg0KKiAkSF8wJDogQeWNgOW5s+Wdh+WAvOetieaWvELljYDlubPlnYflgLwNCiogJEhfQSQ6IEHljYDlubPlnYflgLzkuI3nrYnmlrxC5Y2A5bmz5Z2H5YC8DQoqIOmhr+iRl+awtOa6lu+8miRcYWxwaGEgPSAwLjA1JA0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpGQVNUX0NPVU5UWV9zZiA9IHN0X2pvaW4oVHBlX0Zhc3Rmb29kX3NmLCBUYWlwZWlfVmlsbF9zZiwgam9pbiA9IHN0X3dpdGhpbikNCkZBU1RfYnVmZmVyX3NmID0gc3RfYnVmZmVyKEZBU1RfQ09VTlRZX3NmLCBkaXN0ID0gMTAwMCkNCkZBU1RfU0NIT09MX3NmID0gc3Rfam9pbihTQ0hPT0xfc2YsIEZBU1RfYnVmZmVyX3NmLCBqb2luID0gc3Rfd2l0aGluKQ0KDQpGQVNUX0Ffc2YgPSBmaWx0ZXIoRkFTVF9TQ0hPT0xfc2YsIFRPV04gJWluJSBjKCLmloflsbHljYAiLCAi5aSn5a6J5Y2AIiwgIuS4reato+WNgCIpKQ0KRkFTVF9CX3NmID0gZmlsdGVyKEZBU1RfU0NIT09MX3NmLCBUT1dOICVpbiUgYygi5L+h576p5Y2AIiwgIuWNl+a4r+WNgCIsICLmnb7lsbHljYAiKSkNCg0KY291bnRfQV90YWJsZSA9IHRhYmxlKEZBU1RfQV9zZiRJRCkNCmNvdW50X0JfdGFibGUgPSB0YWJsZShGQVNUX0Jfc2YkSUQpDQoNCmNvdW50X0FfZGYgPSBhcy5kYXRhLmZyYW1lKGNvdW50X0FfdGFibGUpDQpjb2xuYW1lcyhjb3VudF9BX2RmKSA8LSBjKCJBTElBUyIsIuWtuOagoeaVuCIpDQpjb3VudF9CX2RmID0gYXMuZGF0YS5mcmFtZShjb3VudF9CX3RhYmxlKQ0KY29sbmFtZXMoY291bnRfQl9kZikgPC0gYygiQUxJQVMiLCLlrbjmoKHmlbgiKQ0KDQp0LnRlc3QoY291bnRfQV9kZiTlrbjmoKHmlbgsIGNvdW50X0JfZGYk5a245qCh5pW4LCBhbHRlcm5hdGl2ZSA9ICJ0d28uc2lkZWQiKQ0KYGBgDQoqIHAtdmFsdWU9MC4xMTYzPiRcYWxwaGEk77yM5omA5Lul54Sh5rOV5ouS57WV6Jmb54Sh5YGH6Kit77yM5Zug5q2k5YWp5Y2A55qE5ra16JOL5a245qCh5pW46YeP55qE5bmz5Z2H5YC877yM5rKS5pyJ57Wx6KiI6aGv6JGX5beu55WwDQoNCiMjIOesrOS6jOmhjA0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMg5bu656uLIDUwMCDlhazlsLrntrLmoLwNCmdyaWQ1MDAgPC0gc3RfbWFrZV9ncmlkKFRhaXBlaV9WaWxsX3NmLCBjZWxsc2l6ZSA9IDUwMCwgc3F1YXJlID0gVFJVRSkgJT4lIA0KICBzdF9zZigpICU+JSANCiAgc3RfaW50ZXJzZWN0aW9uKHN0X3VuaW9uKFRhaXBlaV9WaWxsX3NmKSkgJT4lIA0KICBzdF9zZigpDQpzdF9jcnMoZ3JpZDUwMCkgPC0gMzgyNg0KDQojIOioiOeul+avj+agvOWFp+mAn+mjn+W6l+aVuOmHjw0KZ3JpZDUwMCRGYXN0Zm9vZF9OIDwtIGxlbmd0aHMoc3RfaW50ZXJzZWN0cyhncmlkNTAwLCBUcGVfRmFzdGZvb2Rfc2YpKQ0KDQojIOW7uueriyBjb250aWd1aXR5IOmEsOaOpemXnOS/giAocXVlZW4pDQpuYl9xdWVlbiA8LSBwb2x5Mm5iKGdyaWQ1MDAsIHF1ZWVuID0gVFJVRSkNCmx3X3F1ZWVuIDwtIG5iMmxpc3R3KG5iX3F1ZWVuLCBzdHlsZSA9ICJXIikNCg0KIyDnuaroo73ntrLmoLwgKyDpgJ/po5/lupfmlbjph48NCnRtYXBfbW9kZSgicGxvdCIpDQp0bV9zaGFwZShncmlkNTAwKSArDQogIHRtX3BvbHlnb25zKCJGYXN0Zm9vZF9OIiwgcGFsZXR0ZSA9ICJZbE9yUmQiLCB0aXRsZSA9ICJ0aGUgYW1vdW50IG9mIGZhc3QgZm9vZCByZXN0YXVyYW50cyIpICsNCiAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiNTAwLW1ldGVyIGdyaWQgb2YgZmFzdCBmb29kIHJlc3RhdXJhbnRzIikNCg0KIyDoqIjnrpcgTW9yYW7igJlzIEkgQ29ycmVsb2dyYW3vvIhvcmRlciA9IDEw77yJDQptb3Jhbl9jb3JyIDwtIHNwLmNvcnJlbG9ncmFtKG5iX3F1ZWVuLCBncmlkNTAwJEZhc3Rmb29kX04sIG9yZGVyID0gMTAsIG1ldGhvZCA9ICJJIiwgc3R5bGUgPSAiVyIpDQoNCiMg57mq5ZyWDQpwbG90KG1vcmFuX2NvcnIsIG1haW4gPSAiTW9yYW4ncyBJIENvcnJlbG9ncmFtIG9mIEZhc3QgRm9vZCBDb3VudHMiKQ0KYGBgDQoNCg0KYGBge3J9DQojIyMjIyDpoY3lpJbvvJrkvb/nlKhGRFLmoKHmraMgIyMjIyMNCg0KIyDoqIjnrpfliY0gMTAg6ZqO55qEIE1vcmFuJ3MgSQ0KbW9yYW5fY29yciA8LSBzcC5jb3JyZWxvZ3JhbShuYl9xLCBncmlkNTAwJEZhc3Rmb29kX04sIG9yZGVyID0gMTAsIG1ldGhvZCA9ICJJIiwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KIyDovYnmiJAgZGF0YWZyYW1lIOe5quWclueUqA0KZGZfY29yciA8LSBkYXRhLmZyYW1lKA0KICBPcmRlciA9IDE6MTAsDQogIEkgPSBtb3Jhbl9jb3JyJHJlc1ssIDFdLCAgIyDnrKzkuIDmrITmmK8gSQ0KICBwID0gbW9yYW5fY29yciRyZXNbLCAyXSAgICMg56ys5LqM5qyE5pivIHAg5YC8DQopDQoNCiMg57mq5ZyWDQpnZ3Bsb3QoZGZfY29yciwgYWVzKHggPSBPcmRlciwgeSA9IEkpKSArDQogIGdlb21fbGluZSgpICsNCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBwIDwgMC4wNSksIHNpemUgPSAzKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyZXk0MCIpICsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIkZBTFNFIiA9ICJncmV5IiwgIlRSVUUiID0gInJlZCIpLCBsYWJlbHMgPSBjKCJOb3QgU2lnbmlmaWNhbnQiLCAiU2lnbmlmaWNhbnQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk1vcmFuJ3MgSSBDb3JyZWxvZ3JhbSBvZiBGYXN0IEZvb2QgQ291bnRzIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJTcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBieSBjb250aWd1aXR5IG9yZGVyIiwNCiAgICAgICB4ID0gIkNvbnRpZ3VpdHkgT3JkZXIiLCB5ID0gIk1vcmFuJ3MgSSIsIGNvbG9yID0gInAgPCAwLjA1IikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCmBgYHtyfQ0KIyMjIyMg6aGN5aSW77ya5L2/55SoRkRS5qCh5q2jICMjIyMjDQoNCiMg5bu656uLIDUwMG0g57ay5qC8DQpncmlkNTAwIDwtIHN0X21ha2VfZ3JpZChUYWlwZWlfVmlsbF9zZiwgY2VsbHNpemUgPSA1MDAsIHNxdWFyZSA9IFRSVUUpICU+JSANCiAgc3Rfc2YoKSAlPiUNCiAgc3RfaW50ZXJzZWN0aW9uKHN0X3VuaW9uKFRhaXBlaV9WaWxsX3NmKSkgJT4lDQogIHN0X3NmKCkNCnN0X2NycyhncmlkNTAwKSA8LSAzODI2DQoNCiMg5Yqg5YWl5q+P5qC86YCf6aOf5bqX5pW46YePDQpncmlkNTAwJEZhc3Rmb29kX04gPC0gbGVuZ3RocyhzdF9pbnRlcnNlY3RzKGdyaWQ1MDAsIFRwZV9GYXN0Zm9vZF9zZikpDQoNCiMg5bu656uL6YSw5o6l6Zec5L+C77yIUXVlZW7vvIkNCm5iX3EgPC0gcG9seTJuYihncmlkNTAwLCBxdWVlbiA9IFRSVUUpDQpsd19xIDwtIG5iMmxpc3R3KG5iX3EsIHN0eWxlID0gIlciLCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCiMg6KiI566XIExvY2FsIE1vcmFuJ3MgSQ0KbGlzYSA8LSBsb2NhbG1vcmFuKGdyaWQ1MDAkRmFzdGZvb2RfTiwgbHdfcSwgemVyby5wb2xpY3kgPSBUUlVFKQ0KDQojIOWKoOWFpee1kOaenOS4piBGRFIg5qCh5q2jDQpncmlkNTAwJElpICAgICAgICAgPC0gbGlzYVssMV0NCmdyaWQ1MDAkRUlpICAgICAgICA8LSBsaXNhWywyXQ0KZ3JpZDUwMCRWYXJJaSAgICAgIDwtIGxpc2FbLDNdDQpncmlkNTAwJFouSWkgICAgICAgPC0gbGlzYVssNF0NCmdyaWQ1MDAkcC5JaSAgICAgICA8LSBsaXNhWyw1XQ0KZ3JpZDUwMCRwLklpLmZkciAgIDwtIHAuYWRqdXN0KGxpc2FbLDVdLCBtZXRob2QgPSAiZmRyIikNCg0KIyBMSVNBIOWIhumhnu+8iOS+neeFp+mhr+iRl+aApyArIOato+iyoOebuOmXnO+8iQ0KbGFnX0ZGIDwtIGxhZy5saXN0dyhsd19xLCBncmlkNTAwJEZhc3Rmb29kX04pDQpncmlkNTAwJGxhZ19GRiA8LSBsYWdfRkYNCg0KZ3JpZDUwMCRMSVNBX3R5cGUgPC0gIk5vdCBzaWduaWZpY2FudCINCmdyaWQ1MDAkTElTQV90eXBlW2dyaWQ1MDAkRmFzdGZvb2RfTiA+IG1lYW4oZ3JpZDUwMCRGYXN0Zm9vZF9OKSAmIA0KICAgICAgICAgICAgICAgICAgZ3JpZDUwMCRsYWdfRkYgPiBtZWFuKGdyaWQ1MDAkbGFnX0ZGKSAmIA0KICAgICAgICAgICAgICAgICAgZ3JpZDUwMCRwLklpLmZkciA8IDAuMDVdIDwtICJIaWdoLUhpZ2giDQpncmlkNTAwJExJU0FfdHlwZVtncmlkNTAwJEZhc3Rmb29kX04gPCBtZWFuKGdyaWQ1MDAkRmFzdGZvb2RfTikgJiANCiAgICAgICAgICAgICAgICAgIGdyaWQ1MDAkbGFnX0ZGIDwgbWVhbihncmlkNTAwJGxhZ19GRikgJiANCiAgICAgICAgICAgICAgICAgIGdyaWQ1MDAkcC5JaS5mZHIgPCAwLjA1XSA8LSAiTG93LUxvdyINCmdyaWQ1MDAkTElTQV90eXBlW2dyaWQ1MDAkRmFzdGZvb2RfTiA+IG1lYW4oZ3JpZDUwMCRGYXN0Zm9vZF9OKSAmIA0KICAgICAgICAgICAgICAgICAgZ3JpZDUwMCRsYWdfRkYgPCBtZWFuKGdyaWQ1MDAkbGFnX0ZGKSAmIA0KICAgICAgICAgICAgICAgICAgZ3JpZDUwMCRwLklpLmZkciA8IDAuMDVdIDwtICJIaWdoLUxvdyINCmdyaWQ1MDAkTElTQV90eXBlW2dyaWQ1MDAkRmFzdGZvb2RfTiA8IG1lYW4oZ3JpZDUwMCRGYXN0Zm9vZF9OKSAmIA0KICAgICAgICAgICAgICAgICAgZ3JpZDUwMCRsYWdfRkYgPiBtZWFuKGdyaWQ1MDAkbGFnX0ZGKSAmIA0KICAgICAgICAgICAgICAgICAgZ3JpZDUwMCRwLklpLmZkciA8IDAuMDVdIDwtICJMb3ctSGlnaCINCg0KZ3JpZDUwMCRMSVNBX3R5cGUgPC0gZmFjdG9yKGdyaWQ1MDAkTElTQV90eXBlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkhpZ2gtSGlnaCIsICJMb3ctTG93IiwgIkhpZ2gtTG93IiwgIkxvdy1IaWdoIiwgIk5vdCBzaWduaWZpY2FudCIpKQ0KDQojIOeVq+WHuiBMSVNBIOmhnuWei+WcsOWclg0KdG1hcF9tb2RlKCJwbG90IikNCnRtX3NoYXBlKGdyaWQ1MDApICsNCiAgdG1fcG9seWdvbnMoIkxJU0FfdHlwZSIsIHBhbGV0dGUgPSBjKCJyZWQiLCAiYmx1ZSIsICJvcmFuZ2UiLCAic2t5Ymx1ZSIsICJncmV5ODAiKSwNCiAgICAgICAgICAgICAgdGl0bGUgPSAiTElTQSBUeXBlXG4oRkRSIENvcnJlY3RlZCkiKSArDQogIHRtX2xheW91dChtYWluLnRpdGxlID0gIkxvY2FsIE1vcmFuJ3MgSSBmb3IgRmFzdCBGb29kIENsdXN0ZXJzIGluIFRhaXBlaSIsDQogICAgICAgICAgICBsZWdlbmQub3V0c2lkZSA9IFRSVUUpDQoNCmBgYA0K6YCf6aOf5bqX5Zyo56m66ZaT5LiK5YW35pyJ6aGv6JGX55qE6IGa6ZuG54++6LGh77yM5bCk5YW25Zyo56ysIDEg6ZqO77yINTAwIOWFrOWwuumEsOi/ke+8ieaZgiBNb3JhbuKAmXMgSSDntITngrogMC4xOO+8jOmhr+iRl+mrmOaWvCAw77yM5Luj6KGo56m66ZaT6Ieq55u46Zec5by354OI44CCDQoNCumaqOiRl+mEsOaOpemajuaVuOWinuWKoO+8jE1vcmFu4oCZcyBJIOWAvOmAkOa8uOS4i+mZje+8jOS9huWcqOWJjSA2IOmaju+8iOWNs+e0hCAzIOWFrOmHjOevhOWcjeWFp++8ieS7jee2reaMgeato+WQkeS4lOaciemhr+iRl+aAp++8jOihqOekuuiBmumbhuaViOaHieWFt+acieS4gOWumueahOepuumWk+W7tuS8uOevhOWcjeOAgg0KDQroh6rnrKwgNyDpmo7kuYvlvozvvIxNb3JhbuKAmXMgSSDlgLzmjqXov5EgMO+8jOS4lOS/oeiztOWNgOmWk+iIhyAwIOmHjeeWiu+8jOmhr+ekuuiHquebuOmXnOi2qOaWvOmaqOapn++8jOepuumWk+iBmumbhuaViOaHieW3suS4jeaYjumhr+OAgg0KDQrnuL3ntZDvvJoNCumAn+mjn+W6l+WcqCA1MDAg5YWs5bC66IezIDMg5YWs6YeM56+E5ZyN5YWn5a2Y5Zyo6aGv6JGX56m66ZaT6IGa6ZuG54++6LGh77yM6aGv56S65bqX5a626YG45Z2A5oiW5ZWG5ZyI5b2i5oiQ5YW35Zyw55CG6ZuG5Lit5oCn77yb6LaF6YGO5q2k6Led6Zui5b6M77yM6Ieq55u46Zec6Lao6L+R6Zqo5qmf77yM6KGo56S656m66ZaT5b2x6Z+/5pWI5oeJ5bey5raI5pWj44CCDQoNCiMjIOesrOS4iemhjA0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQojIC0tLSBTVEVQIDA6IOW7uueri+WtuOagoeaVuOmHj+ashOS9je+8iOiIh+WJjemdouS4gOaoo++8iSAtLS0NCmdyaWQ1MDAkU2Nob29sX04gPC0gbGVuZ3RocyhzdF9pbnRlcnNlY3RzKGdyaWQ1MDAsIFNDSE9PTF9zZikpDQoNCiMg5bu656uLIFF1ZWVuIOmEsOaOpeasiumHje+8iOWmguaenOmChOaykuWft+ihjOmBju+8iQ0KbmJfcSA8LSBwb2x5Mm5iKGdyaWQ1MDAsIHF1ZWVuID0gVFJVRSkNCmx3X3EgPC0gbmIybGlzdHcobmJfcSwgc3R5bGUgPSAiVyIsIHplcm8ucG9saWN5ID0gVFJVRSkNCg0KIyDmib7lh7rmnInphLDlsYXnmoTmoLzlrZANCnZhbGlkX2lkeCA8LSB3aGljaChjYXJkKG5iX3EpID4gMCkNCg0KIyDliJ3lp4vljJbkuKboqIjnrpcgR2kqIOe1seioiOmHjw0KR2lfWl9GRiA8LSByZXAoTkEsIG5yb3coZ3JpZDUwMCkpDQpHaV9aX0ZGW3ZhbGlkX2lkeF0gPC0gbG9jYWxHKGdyaWQ1MDAkRmFzdGZvb2RfTlt2YWxpZF9pZHhdLCBsd19xLCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCmdyaWQ1MDAkR2lfWl9GRiA8LSBHaV9aX0ZGDQpncmlkNTAwJHBfRkYgPC0gMiAqIHBub3JtKC1hYnMoZ3JpZDUwMCRHaV9aX0ZGKSkgICMg6ZuZ5bC+IHAg5YC8DQpncmlkNTAwJHBfRkZfZmRyIDwtIHAuYWRqdXN0KGdyaWQ1MDAkcF9GRiwgbWV0aG9kID0gImZkciIpICAjIEZEUiDkv67mraMNCmdyaWQ1MDAkSG90c3BvdF9GRiA8LSBpZmVsc2UoZ3JpZDUwMCRwX0ZGX2ZkciA8IDAuMDUgJiBncmlkNTAwJEdpX1pfRkYgPiAwLCAiSG90c3BvdCIsICJOb3Qgc2lnbmlmaWNhbnQiKQ0KDQojIDEuIOioiOeulyBHaSogWiDlgLzvvIjlrbjmoKHvvIkNCkdpX1pfU0MgPC0gcmVwKE5BLCBucm93KGdyaWQ1MDApKQ0KR2lfWl9TQ1t2YWxpZF9pZHhdIDwtIGxvY2FsRyhncmlkNTAwJFNjaG9vbF9OW3ZhbGlkX2lkeF0sIGx3X3EsIHplcm8ucG9saWN5ID0gVFJVRSkNCmdyaWQ1MDAkR2lfWl9TQyA8LSBHaV9aX1NDDQoNCiMgMi4g6KiI566XIHAg5YC86IiHIEZEUiDmoKHmraMNCmdyaWQ1MDAkcF9TQyA8LSAyICogcG5vcm0oLWFicyhncmlkNTAwJEdpX1pfU0MpKQ0KZ3JpZDUwMCRwX1NDX2ZkciA8LSBwLmFkanVzdChncmlkNTAwJHBfU0MsIG1ldGhvZCA9ICJmZHIiKQ0KDQojIDMuIOato+eiuueUouWHuiBIb3RzcG90X1NDDQpncmlkNTAwJEhvdHNwb3RfU0MgPC0gaWZlbHNlKA0KICBpcy5uYShncmlkNTAwJHBfU0NfZmRyKSwgIk5vdCBzaWduaWZpY2FudCIsDQogIGlmZWxzZShncmlkNTAwJHBfU0NfZmRyIDwgMC4wNSAmIGdyaWQ1MDAkR2lfWl9TQyA+IDAsICJIb3RzcG90IiwgIk5vdCBzaWduaWZpY2FudCIpDQopDQoNCiMgNC4g6Kit5a6a54K6IGZhY3Rvcu+8jOmBv+WFjSB0bWFwIOWIpOWumueCuiBOQQ0KZ3JpZDUwMCRIb3RzcG90X1NDIDwtIGZhY3RvcihncmlkNTAwJEhvdHNwb3RfU0MsIGxldmVscyA9IGMoIkhvdHNwb3QiLCAiTm90IHNpZ25pZmljYW50IikpDQojIC0tLSBTVEVQIDI6IOeGseWNgOWclue5quijvSAtLS0NCg0KIyDpgJ/po5/lupfnhrHljYDlnLDlnJYNCnRtX3NoYXBlKGdyaWQ1MDApICsNCiAgdG1fcG9seWdvbnMoIkhvdHNwb3RfRkYiLCBwYWxldHRlID0gYygicmVkIiwgImdyZXk4MCIpLA0KICAgICAgICAgICAgICB0aXRsZSA9ICJGYXN0IEZvb2QgR2kqIEhvdHNwb3QiKSArDQogIHRtX2xheW91dChtYWluLnRpdGxlID0gIkZhc3QgRm9vZCBIb3RzcG90cyAoR2kqLCBGRFIgY29ycmVjdGVkKSIsDQogICAgICAgICAgICBsZWdlbmQub3V0c2lkZSA9IFRSVUUpDQoNCiMg5a245qCh54ax5Y2A5Zyw5ZyWDQp0bV9zaGFwZShncmlkNTAwKSArDQogIHRtX3BvbHlnb25zKCJIb3RzcG90X1NDIiwgcGFsZXR0ZSA9IGMoImJsdWUiLCAiZ3JleTgwIiksDQogICAgICAgICAgICAgIHRpdGxlID0gIlNjaG9vbCBHaSogSG90c3BvdCIpICsNCiAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAiU2Nob29sIEhvdHNwb3RzIChHaSosIEZEUiBjb3JyZWN0ZWQpIiwNCiAgICAgICAgICAgIGxlZ2VuZC5vdXRzaWRlID0gVFJVRSkNCg0KYGBgDQrntZDoq5bvvIjpgJ/po5/lupfoiIflrbjmoKHnhrHljYDvvInvvJoNCumAn+mjn+W6l+eGseWNgO+8iOe0heiJsu+8ieS4u+imgembhuS4reaWvOWcluS4reWNl+WBtOWBj+S4reiIh+adseWNl+WNgOWfn++8jOWRiOePvumAo+e6jOiBmumbhu+8jOmhr+ekuumAmeS6m+WNgOWfn+eahOmAn+mjn+W6l+WvhuW6pumhr+iRl+mrmOaWvOWFtuS7luWcsOWNgOOAgg0K5a245qCh54ax5Y2A77yI6JeN6Imy77yJ5YmH6LyD54K66Zu25pif5YiG5biD77yM6ZuW5Lmf5aSa6ZuG5Lit5pa85Y2X5YG077yM5L2G6IiH6YCf6aOf5bqX55qE54ax5Y2A5YOF6YOo5YiG6YeN55aK77yM5pW06auU56m66ZaT5YiG5biD5beu55Ww5piO6aGv44CCDQrlhanogIXnqbrplpPliIbluIPpoa/npLrvvJrpgJ/po5/lupfnhrHljYDoiIflrbjmoKHnhrHljYDkuI3lrozlhajkuIDoh7TvvIzkvYbmnInph43nlorjgIINCg0KIyBQYXJ0IDINCg0KIyMg56ys5LiA6aGMDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiPoroDlj5bos4fmlpkNCg0KZmFzdGZvb2QgPC0gc3RfcmVhZCgiLi9kYXRhL1RwZV9GYXN0Zm9vZC5zaHAiLCBvcHRpb25zPSJFTkNPRElORz1CSUc1IikNCnZpbGxhZ2VzIDwtIHN0X3JlYWQoIi4vZGF0YS9UYWlwZWlfVmlsbC5zaHAiLCBvcHRpb25zPSJFTkNPRElORz1CSUc1IikNCnNjaG9vbHMgPC0gc3RfcmVhZCgiLi9kYXRhL1NDSE9PTC5zaHAiLCBvcHRpb25zPSJFTkNPRElORz1CSUc1IikNCg0KDQojU3RlcCAy77ya6YG45Ye65LiJ5YCL6KGM5pS/5Y2A55qE5a245qCh77yI5aSn5a6J44CB5paH5bGx44CB5L+h576p77yJDQp2aWxsYWdlcyA8LSB2aWxsYWdlcyAlPiUNCiAgZmlsdGVyKGdyZXBsKCLlpKflronljYB85paH5bGx5Y2AfOS/oee+qeWNgCIsIFRPV04pKSAgIyDms6jmhI/mrITkvY3lkI3nqLHlj6/og73pnIDlsI3mh4nkvaDnmoTos4fmlpnvvIzlpoIgRElTVFJJQ1RfTkFNRQ0KDQoj56m66ZaT55u45Lqk5om+5Ye65bGs5pa85LiJ5Y2A55qE5a245qChDQpzY2hvb2xzX3RocmVlIDwtIHN0X2pvaW4oc2Nob29scywgdmlsbGFnZXMsIGpvaW4gPSBzdF93aXRoaW4pICU+JQ0KICBmaWx0ZXIoIWlzLm5hKFRPV04pKSAgIyDnorrkv53mnInkuqTpm4bmiJDlip8NCg0KI+ioiOeul+avj+aJgOWtuOagoeWIsOaJgOaciemAn+mjn+W6l+eahOi3nembou+8jOS4puWBmiBGKGQpIOWHveaVuA0KDQoj5a6a576p6Led6Zui6ZaA5qq777yI5Lul5YWs5bC654K65Zau5L2N77yJ77yM6YCZ6KOh6Kit5a6a5b6eIDAg5YiwIDEwMDAg5YWs5bC677yM5q+PIDEwMCDlhazlsLrkuIDlgIvploDmqrsNCmRfdmFsdWVzIDwtIHNlcSgwLCAxMDAwLCBieSA9IDEwMCkNCg0KI+W7uueri+S4gOWAi+WHveaVuOioiOeulyBGKGQpIOWAvA0KY2FsY19GZCA8LSBmdW5jdGlvbihzY2hvb2xzX3NmLCBmYXN0Zm9vZF9zZiwgZF9zZXEpIHsNCiAgcmVzdWx0IDwtIGRhdGEuZnJhbWUoZGlzdGFuY2UgPSBkX3NlcSwgRmQgPSBOQSkNCiAgbl9zY2hvb2wgPC0gbnJvdyhzY2hvb2xzX3NmKQ0KICANCiAgZm9yIChpIGluIHNlcV9hbG9uZyhkX3NlcSkpIHsNCiAgICBkIDwtIHNldF91bml0cyhkX3NlcVtpXSwgIm0iKSAj5Zau5L2N54K65YWs5bC6DQogICAgDQogICAgI+avj+mWk+WtuOagoeaYr+WQpuWcqCBkIOWFrOWwuuWFp+iHs+WwkeacieS4gOmWk+mAn+mjn+W6l++8nw0KICAgIGNvdW50X3dpdGhpbl9kIDwtIHN1bShzYXBwbHkoMTpuX3NjaG9vbCwgZnVuY3Rpb24oaikgew0KICAgICAgYW55KHN0X2Rpc3RhbmNlKHNjaG9vbHNfc2ZbaixdLCBmYXN0Zm9vZF9zZikgPD0gZCkNCiAgICB9KSkNCiAgICANCiAgICAjRihkKSA9IOa7v+i2s+aineS7tueahOWtuOagoeaVuCAvIOe4veWtuOagoeaVuA0KICAgIHJlc3VsdCRGZFtpXSA8LSBjb3VudF93aXRoaW5fZCAvIG5fc2Nob29sDQogIH0NCiAgcmV0dXJuKHJlc3VsdCkNCn0NCg0KI+WIhuS4ieWNgOWBmiBGKGQpDQpmZF9kYV9hbiA8LSBjYWxjX0ZkKHNjaG9vbHNfdGhyZWUgJT4lIGZpbHRlcihncmVwbCgi5aSn5a6J5Y2AIiwgVE9XTikpLCBmYXN0Zm9vZCwgZF92YWx1ZXMpDQpmZF93ZW5fc2hhbiA8LSBjYWxjX0ZkKHNjaG9vbHNfdGhyZWUgJT4lIGZpbHRlcihncmVwbCgi5paH5bGx5Y2AIiwgVE9XTikpLCBmYXN0Zm9vZCwgZF92YWx1ZXMpDQpmZF94aW5feWkgPC0gY2FsY19GZChzY2hvb2xzX3RocmVlICU+JSBmaWx0ZXIoZ3JlcGwoIuS/oee+qeWNgCIsIFRPV04pKSwgZmFzdGZvb2QsIGRfdmFsdWVzKQ0KDQoj5Yqg5LiK5Y2A5ZCNDQpmZF9kYV9hbiREaXN0cmljdCA8LSAi5aSn5a6J5Y2AIg0KZmRfd2VuX3NoYW4kRGlzdHJpY3QgPC0gIuaWh+WxseWNgCINCmZkX3hpbl95aSREaXN0cmljdCA8LSAi5L+h576p5Y2AIg0KDQoj5ZCI5L216LOH5paZDQpmZF9hbGwgPC0gcmJpbmQoZmRfZGFfYW4sIGZkX3dlbl9zaGFuLCBmZF94aW5feWkpDQoNCiPnuarlnJbmr5TovIMgRihkKQ0KZ2dwbG90KGZkX2FsbCwgYWVzKHggPSBkaXN0YW5jZSwgeSA9IEZkLCBjb2xvciA9IERpc3RyaWN0KSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZF92YWx1ZXMpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJGKGQpIOWHveaVuOavlOi8g++8muS4ieWNgOWtuOagoeWRqOmCiumAn+mjn+W6l+WIhuW4gyIsDQogICAgeCA9ICLot53pm6IgZO+8iOWFrOWwuu+8iSIsDQogICAgeSA9ICJGKGQpID0g6Iez5bCR5LiA6ZaT6YCf6aOf5bqX55qE5a245qCh5q+U5L6LIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQ0KDQoNCmBgYA0KDQpgYGB7cn0NCnNldC5zZWVkKDQyKSAgI+eCuuS6hue1kOaenOWPr+mHjeePvg0KDQoj5Y+q6JmV55CG5aSn5a6J5Y2ADQpzY2hvb2xzX2RhX2FuIDwtIHNjaG9vbHNfdGhyZWUgJT4lIGZpbHRlcihncmVwbCgi5aSn5a6J5Y2AIiwgVE9XTikpDQpuX3NjaG9vbHMgPC0gbnJvdyhzY2hvb2xzX2RhX2FuKQ0KZGFfYW5fYXJlYSA8LSB2aWxsYWdlcyAlPiUgZmlsdGVyKFRPV04gPT0gIuWkp+WuieWNgCIpDQoNCiPmqKHmk6zmrKHmlbgNCm5fc2ltIDwtIDk5DQoNCiPlu7rnq4vkuIDlgIvnqbrnmoQgZGF0YSBmcmFtZSDlhLLlrZjmqKHmk6zntZDmnpwNCnNpbV9GZF9hbGwgPC0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKGRfdmFsdWVzKSwgbmNvbCA9IG5fc2ltKQ0KDQoj6YCy6KGM5qih5pOsDQpmb3IgKGkgaW4gMTpuX3NpbSkgew0KICAjIOWcqOWkp+WuieWNgOmaqOapn+eUoueUnyBuX3NjaG9vbHMg5YCL6bueDQogIHJhbmRvbV9wdHMgPC0gc3Rfc2FtcGxlKGRhX2FuX2FyZWEsIHNpemUgPSBuX3NjaG9vbHMsIHR5cGUgPSAicmFuZG9tIikgJT4lDQogICAgc3RfYXNfc2YoKQ0KICANCiAgIyDoqIjnrpcgRihkKQ0KICBzaW1fZmQgPC0gY2FsY19GZChyYW5kb21fcHRzLCBmYXN0Zm9vZCwgZF92YWx1ZXMpDQogIA0KICAjIOWtmOWFpeaooeaTrOe1kOaenOefqemZow0KICBzaW1fRmRfYWxsWywgaV0gPC0gc2ltX2ZkJEZkDQp9DQoNCiPnlKLnlJ/mqKHmk6znmoQgZW52ZWxvcGVz77yI5pyA5aSn5YC86IiH5pyA5bCP5YC877yJDQpGZF9zaW1fbWluIDwtIGFwcGx5KHNpbV9GZF9hbGwsIDEsIG1pbikNCkZkX3NpbV9tYXggPC0gYXBwbHkoc2ltX0ZkX2FsbCwgMSwgbWF4KQ0KDQoj5ZCI5L2154K65LiA5YCL6LOH5paZ5qGGDQpmZF9lbnZlbG9wZSA8LSBkYXRhLmZyYW1lKA0KICBkaXN0YW5jZSA9IGRfdmFsdWVzLA0KICBtaW4gPSBGZF9zaW1fbWluLA0KICBtYXggPSBGZF9zaW1fbWF4DQopDQoNCiPliqDkuIrlr6bpmpvnmoQgRihkKQ0KZmRfYWN0dWFsIDwtIGZkX2RhX2FuICAjIOWJm+WJm+ioiOeul+mBjueahOWkp+WuieWNgOWvpumamyBGKGQpDQoNCiNTdGVwIDbvvJrnuarlnJbvvIjlr6bpmpsgdnMuIOaooeaTrOWNgOmWk++8iQ0KZ2dwbG90KCkgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gZmRfZW52ZWxvcGUsIGFlcyh4ID0gZGlzdGFuY2UsIHltaW4gPSBtaW4sIHltYXggPSBtYXgpLCANCiAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNCkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZkX2FjdHVhbCwgYWVzKHggPSBkaXN0YW5jZSwgeSA9IEZkKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIuWkp+WuieWNgOWtuOagoeWRqOmCiumAn+mjn+W6l+eahCBGKGQpIOiIh+aooeaTrOWNgOmWkyIsDQogICAgc3VidGl0bGUgPSAi57SF57ea54K65a+m6Zqb5YC877yM6JeN6Imy5Y2A5Z+f54K65qih5pOsIDk5IOasoeaJgOW+l+S5i+WNgOmWkyIsDQogICAgeCA9ICLot53pm6IgZO+8iOWFrOWwuu+8iSIsDQogICAgeSA9ICJGKGQpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCg0MikNCg0KI+WPquiZleeQhuS/oee+qeWNgA0Kc2Nob29sc19kYV9hbiA8LSBzY2hvb2xzX3RocmVlICU+JSBmaWx0ZXIoZ3JlcGwoIuS/oee+qeWNgCIsIFRPV04pKQ0Kbl9zY2hvb2xzIDwtIG5yb3coc2Nob29sc19kYV9hbikNCmRhX2FuX2FyZWEgPC0gdmlsbGFnZXMgJT4lIGZpbHRlcihUT1dOID09ICLkv6HnvqnljYAiKQ0KDQoj5qih5pOs5qyh5pW4DQpuX3NpbSA8LSA5OQ0KDQoj5bu656uL5LiA5YCL56m655qEIGRhdGEgZnJhbWUg5YSy5a2Y5qih5pOs57WQ5p6cDQpzaW1fRmRfYWxsIDwtIG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aChkX3ZhbHVlcyksIG5jb2wgPSBuX3NpbSkNCg0KI+mAsuihjOaooeaTrA0KZm9yIChpIGluIDE6bl9zaW0pIHsNCiAgI+S/oee+qeWNgOmaqOapn+eUn+aIkCBuX3NjaG9vbHMg5YCL6bueDQogIHJhbmRvbV9wdHMgPC0gc3Rfc2FtcGxlKGRhX2FuX2FyZWEsIHNpemUgPSBuX3NjaG9vbHMsIHR5cGUgPSAicmFuZG9tIikgJT4lDQogICAgc3RfYXNfc2YoKQ0KICANCiAgIyDoqIjnrpcgRihkKQ0KICBzaW1fZmQgPC0gY2FsY19GZChyYW5kb21fcHRzLCBmYXN0Zm9vZCwgZF92YWx1ZXMpDQogIA0KICAjIOWtmOWFpeaooeaTrOe1kOaenOefqemZow0KICBzaW1fRmRfYWxsWywgaV0gPC0gc2ltX2ZkJEZkDQp9DQoNCiPnlKLnlJ/mqKHmk6znmoQgZW52ZWxvcGVz77yI5pyA5aSn5YC86IiH5pyA5bCP5YC877yJDQpGZF9zaW1fbWluIDwtIGFwcGx5KHNpbV9GZF9hbGwsIDEsIG1pbikNCkZkX3NpbV9tYXggPC0gYXBwbHkoc2ltX0ZkX2FsbCwgMSwgbWF4KQ0KDQoj5ZCI5L2154K65LiA5YCL6LOH5paZ5qGGDQpmZF9lbnZlbG9wZSA8LSBkYXRhLmZyYW1lKA0KICBkaXN0YW5jZSA9IGRfdmFsdWVzLA0KICBtaW4gPSBGZF9zaW1fbWluLA0KICBtYXggPSBGZF9zaW1fbWF4DQopDQoNCiPliqDkuIrlr6bpmpvnmoQgRihkKQ0KZmRfYWN0dWFsIDwtIGZkX2RhX2FuICAjIOWJm+WJm+ioiOeul+mBjueahOS/oee+qeWNgOWvpumamyBGKGQpDQoNCiNTdGVwIDbvvJrnuarlnJbvvIjlr6bpmpsgdnMuIOaooeaTrOWNgOmWk++8iQ0KZ2dwbG90KCkgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gZmRfZW52ZWxvcGUsIGFlcyh4ID0gZGlzdGFuY2UsIHltaW4gPSBtaW4sIHltYXggPSBtYXgpLCANCiAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNCkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZkX2FjdHVhbCwgYWVzKHggPSBkaXN0YW5jZSwgeSA9IEZkKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIuS/oee+qeWNgOWtuOagoeWRqOmCiumAn+mjn+W6l+eahCBGKGQpIOiIh+aooeaTrOWNgOmWkyIsDQogICAgc3VidGl0bGUgPSAi57SF57ea54K65a+m6Zqb5YC877yM6JeN6Imy5Y2A5Z+f54K65qih5pOsIDk5IOasoeaJgOW+l+S5i+WNgOmWkyIsDQogICAgeCA9ICLot53pm6IgZO+8iOWFrOWwuu+8iSIsDQogICAgeSA9ICJGKGQpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KYGBge3J9DQpzZXQuc2VlZCg0MikNCg0KI+WPquiZleeQhuaWh+WxseWNgA0Kc2Nob29sc19kYV9hbiA8LSBzY2hvb2xzX3RocmVlICU+JSBmaWx0ZXIoZ3JlcGwoIuaWh+WxseWNgCIsIFRPV04pKQ0Kbl9zY2hvb2xzIDwtIG5yb3coc2Nob29sc19kYV9hbikNCmRhX2FuX2FyZWEgPC0gdmlsbGFnZXMgJT4lIGZpbHRlcihUT1dOID09ICLmloflsbHljYAiKQ0KDQoj5qih5pOs5qyh5pW4DQpuX3NpbSA8LSA5OQ0KDQoj5bu656uL5LiA5YCL56m655qEIGRhdGEgZnJhbWUg5YSy5a2Y5qih5pOs57WQ5p6cDQpzaW1fRmRfYWxsIDwtIG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aChkX3ZhbHVlcyksIG5jb2wgPSBuX3NpbSkNCg0KI+mAsuihjOaooeaTrA0KZm9yIChpIGluIDE6bl9zaW0pIHsNCiAgI+aWh+WxseWNgOmaqOapn+eUn+aIkCBuX3NjaG9vbHMg5YCL6bueDQogIHJhbmRvbV9wdHMgPC0gc3Rfc2FtcGxlKGRhX2FuX2FyZWEsIHNpemUgPSBuX3NjaG9vbHMsIHR5cGUgPSAicmFuZG9tIikgJT4lDQogICAgc3RfYXNfc2YoKQ0KICANCiAgIyDoqIjnrpcgRihkKQ0KICBzaW1fZmQgPC0gY2FsY19GZChyYW5kb21fcHRzLCBmYXN0Zm9vZCwgZF92YWx1ZXMpDQogIA0KICAjIOWtmOWFpeaooeaTrOe1kOaenOefqemZow0KICBzaW1fRmRfYWxsWywgaV0gPC0gc2ltX2ZkJEZkDQp9DQoNCiPnlKLnlJ/mqKHmk6znmoQgZW52ZWxvcGVz77yI5pyA5aSn5YC86IiH5pyA5bCP5YC877yJDQpGZF9zaW1fbWluIDwtIGFwcGx5KHNpbV9GZF9hbGwsIDEsIG1pbikNCkZkX3NpbV9tYXggPC0gYXBwbHkoc2ltX0ZkX2FsbCwgMSwgbWF4KQ0KDQoj5ZCI5L2154K65LiA5YCL6LOH5paZ5qGGDQpmZF9lbnZlbG9wZSA8LSBkYXRhLmZyYW1lKA0KICBkaXN0YW5jZSA9IGRfdmFsdWVzLA0KICBtaW4gPSBGZF9zaW1fbWluLA0KICBtYXggPSBGZF9zaW1fbWF4DQopDQoNCiPliqDkuIrlr6bpmpvnmoQgRihkKQ0KZmRfYWN0dWFsIDwtIGZkX2RhX2FuICAjIOWJm+WJm+ioiOeul+mBjueahOaWh+WxseWNgOWvpumamyBGKGQpDQoNCiNTdGVwIDbvvJrnuarlnJbvvIjlr6bpmpsgdnMuIOaooeaTrOWNgOmWk++8iQ0KZ2dwbG90KCkgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gZmRfZW52ZWxvcGUsIGFlcyh4ID0gZGlzdGFuY2UsIHltaW4gPSBtaW4sIHltYXggPSBtYXgpLCANCiAgICAgICAgICAgICAgZmlsbCA9ICJsaWdodGJsdWUiLCBhbHBoYSA9IDAuNCkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGZkX2FjdHVhbCwgYWVzKHggPSBkaXN0YW5jZSwgeSA9IEZkKSwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEuMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIuaWh+WxseWNgOWtuOagoeWRqOmCiumAn+mjn+W6l+eahCBGKGQpIOiIh+aooeaTrOWNgOmWkyIsDQogICAgc3VidGl0bGUgPSAi57SF57ea54K65a+m6Zqb5YC877yM6JeN6Imy5Y2A5Z+f54K65qih5pOsIDk5IOasoeaJgOW+l+S5i+WNgOmWkyIsDQogICAgeCA9ICLot53pm6IgZO+8iOWFrOWwuu+8iSIsDQogICAgeSA9ICJGKGQpIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMg56ys5LqM6aGMDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0Kd2luX3BvbHlnb24gPC0gc3RfYXNfc2ZjKHN0X2Jib3goVHBlX0Zhc3Rmb29kX3NmKSkNCndpbiA8LSBhcy5vd2luKHdpbl9wb2x5Z29uKQ0KDQpjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoVHBlX0Zhc3Rmb29kX3NmKQ0KZmFzdGZvb2RfcHBwIDwtIHBwcCh4ID0gY29vcmRzWywgMV0sIHkgPSBjb29yZHNbLCAyXSwgd2luZG93ID0gd2luKQ0KDQpyX3NlcSA8LSBzZXEoMCwgNTAwMCwgYnkgPSAxMDApDQoNCmtfcmVzdWx0IDwtIEtlc3QoZmFzdGZvb2RfcHBwLCByID0gcl9zZXEpDQprXzMwMDAgPC0ga19yZXN1bHQkaXNvW3doaWNoKGtfcmVzdWx0JHIgPT0gMzAwMCldDQpsXzMwMDAgPC0gc3FydChrXzMwMDAgLyBwaSkgLTMwMDANCg0KY2F0KCJLKDMwMDApID0iLCBrXzMwMDAsICJcbiIpDQpjYXQoIkwoMzAwMCkgPSIsIGxfMzAwMCwgIlxuIikNCg0KZW52IDwtIGVudmVsb3BlKGZhc3Rmb29kX3BwcCwNCiAgICAgICAgICAgICAgICBmdW4gPSBLZXN0LA0KICAgICAgICAgICAgICAgIG5zaW0gPSA5OSwNCiAgICAgICAgICAgICAgICByID0gcl9zZXEsDQogICAgICAgICAgICAgICAgY29ycmVjdGlvbiA9ICJpc28iLA0KICAgICAgICAgICAgICAgIHNhdmVmdW5zID0gVFJVRSwNCiAgICAgICAgICAgICAgICBzYXZlcGF0dGVybnMgPSBUUlVFLA0KICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkNCg0Kcl9pbmRleCA8LSB3aGljaChlbnYkciA9PSAzMDAwKQ0KdXBwZXJfTCA8LSBzcXJ0KGVudiRoaVtyX2luZGV4XSAvIHBpKSAtMzAwMA0KbG93ZXJfTCA8LSBzcXJ0KGVudiRsb1tyX2luZGV4XSAvIHBpKSAtMzAwMA0KDQpjYXQoIjk1JSBDSSBmb3IgTCgzMDAwKTogWyIsIGxvd2VyX0wsICIsIiwgdXBwZXJfTCwgIl1cbiIpDQoNCnBsb3QoZW52LCAuIH4gciwgbWFpbiA9ICJSaXBsZXkncyBrLWZ1bmN0aW9uIHdpdGggOTUlIENJIikNCmFibGluZSh2ID0gMzAwMCwgY29sID0gInJlZCIsIGx0eSA9IDIpDQpgYGANCg0KIyMg56ys5LiJ6aGMDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KcHVibGljX3NmIDwtIHN1YnNldChTQ0hPT0xfc2YsIFR5cGUgPT0gIuWFrOeriyIpDQpwcml2YXRlX3NmIDwtIHN1YnNldChTQ0hPT0xfc2YsIFR5cGUgPT0gIuengeeriyIpDQoNCmNvb3Jkc19wdWIgPC0gc3RfY29vcmRpbmF0ZXMocHVibGljX3NmKQ0KY29vcmRzX3ByaSA8LSBzdF9jb29yZGluYXRlcyhwcml2YXRlX3NmKQ0KY29vcmRzX2ZmICA8LSBzdF9jb29yZGluYXRlcyhUcGVfRmFzdGZvb2Rfc2YpDQoNCndpbiA8LSBhcy5vd2luKHN0X2FzX3NmYyhzdF9iYm94KFNDSE9PTF9zZikpKQ0KDQpwcF9hbGxfcHViIDwtIHBwcCh4ID0gYyhjb29yZHNfcHViWywgMV0sIGNvb3Jkc19mZlssIDFdKSwNCiAgICAgICAgICAgICAgICAgIHkgPSBjKGNvb3Jkc19wdWJbLCAyXSwgY29vcmRzX2ZmWywgMl0pLA0KICAgICAgICAgICAgICAgICAgd2luZG93ID0gd2luLA0KICAgICAgICAgICAgICAgICAgbWFya3MgPSBmYWN0b3IoYyhyZXAoInNjaG9vbCIsIG5yb3coY29vcmRzX3B1YikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoImZhc3Rmb29kIiwgbnJvdyhjb29yZHNfZmYpKSkpKQ0KDQpwcF9hbGxfcHJpIDwtIHBwcCh4ID0gYyhjb29yZHNfcHJpWywgMV0sIGNvb3Jkc19mZlssIDFdKSwNCiAgICAgICAgICAgICAgICAgIHkgPSBjKGNvb3Jkc19wcmlbLCAyXSwgY29vcmRzX2ZmWywgMl0pLA0KICAgICAgICAgICAgICAgICAgd2luZG93ID0gd2luLA0KICAgICAgICAgICAgICAgICAgbWFya3MgPSBmYWN0b3IoYyhyZXAoInNjaG9vbCIsIG5yb3coY29vcmRzX3ByaSkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoImZhc3Rmb29kIiwgbnJvdyhjb29yZHNfZmYpKSkpKQ0KDQojIOS9v+eUqOabtOe0sOeahOi3nembouW6j+WIl++8iOavjzEw5YWs5bC677yJDQpyX3ZhbHMgPC0gc2VxKDAsIDMwMDAsIGJ5ID0gMTApDQoNCiMg5YWs56uL5a245qChIOKGkiDpgJ/po5/lupcNCkZfcHViIDwtIGVudmVsb3BlKHBwX2FsbF9wdWIsDQogICAgICAgICAgICAgICAgICBmdW4gPSBGZXN0LA0KICAgICAgICAgICAgICAgICAgaSA9ICJzY2hvb2wiLA0KICAgICAgICAgICAgICAgICAgaiA9ICJmYXN0Zm9vZCIsDQogICAgICAgICAgICAgICAgICBuc2ltID0gOTksDQogICAgICAgICAgICAgICAgICByID0gcl92YWxzLA0KICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQ0KDQojIOengeeri+WtuOagoSDihpIg6YCf6aOf5bqXDQpGX3ByaSA8LSBlbnZlbG9wZShwcF9hbGxfcHJpLA0KICAgICAgICAgICAgICAgICAgZnVuID0gRmVzdCwNCiAgICAgICAgICAgICAgICAgIGkgPSAic2Nob29sIiwNCiAgICAgICAgICAgICAgICAgIGogPSAiZmFzdGZvb2QiLA0KICAgICAgICAgICAgICAgICAgbnNpbSA9IDk5LA0KICAgICAgICAgICAgICAgICAgciA9IHJfdmFscywNCiAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkNCg0KDQojIOWFrOeri+WtuOagoQ0KcGxvdChGX3B1YiwgbWFpbiA9ICLlhaznq4vlrbjmoKEg4oaSIOmAn+mjn+W6lyDnmoQgRi1jcm9zcyDlh73mlbgiLCBsZWdlbmRhcmdzID0gbGlzdChjZXggPSAwLjgpKQ0KYWJsaW5lKGggPSBGX3B1YiR0aGVvLCBjb2wgPSAiYmx1ZSIsIGx0eSA9IDIpDQoNCiMg56eB56uL5a245qChDQpwbG90KEZfcHJpLCBtYWluID0gIuengeeri+WtuOagoSDihpIg6YCf6aOf5bqXIOeahCBGLWNyb3NzIOWHveaVuCIsIGxlZ2VuZGFyZ3MgPSBsaXN0KGNleCA9IDAuOCkpDQphYmxpbmUoaCA9IEZfcHJpJHRoZW8sIGNvbCA9ICJibHVlIiwgbHR5ID0gMikNCg0Kcl90YXJnZXQgPC0gMTAwMCAgIyDmg7Pmr5TovIPnmoTot53pm6INCg0KaWR4X3B1YiA8LSB3aGljaC5taW4oYWJzKEZfcHViJHIgLSByX3RhcmdldCkpDQppZHhfcHJpIDwtIHdoaWNoLm1pbihhYnMoRl9wcmkkciAtIHJfdGFyZ2V0KSkNCg0KIyDlj5blvpflkIToh6rnmoQgRl9jcm9zcygxMDAwKQ0KRl9wdWJfb2JzIDwtIEZfcHViJG9ic1tpZHhfcHViXQ0KRl9wcmlfb2JzIDwtIEZfcHJpJG9ic1tpZHhfcHJpXQ0KDQpjYXQoIkYoMTAwMCkg5YWs56uL5a245qChIOKGkiDpgJ/po5/lupcgPSIsIEZfcHViX29icywgIlxuIikNCmNhdCgiRigxMDAwKSDnp4Hnq4vlrbjmoKEg4oaSIOmAn+mjn+W6lyA9IiwgRl9wcmlfb2JzLCAiXG4iKQ0KDQpwbG90KEZfcHViJHIsIEZfcHViJG9icywgdHlwZSA9ICJsIiwgY29sID0gImJsdWUiLCBsd2QgPSAyLA0KICAgICB5bGltID0gYygwLCBtYXgoRl9wdWIkb2JzLCBGX3ByaSRvYnMpKSwNCiAgICAgeGxhYiA9ICLot53pm6IgciIsIHlsYWIgPSAiRl9jcm9zcyhyKSIsDQogICAgIG1haW4gPSAiRi1jcm9zcyDmr5TovIPvvJrlhaznq4sgdnMg56eB56uLIikNCmxpbmVzKEZfcHJpJHIsIEZfcHJpJG9icywgY29sID0gInJlZCIsIGx3ZCA9IDIpDQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gYygi5YWs56uL5a245qChIiwgIuengeeri+WtuOagoSIpLCBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCANCiAgICAgICBsdHkgPSAxLCBsd2QgPSAyKQ0KYWJsaW5lKGEgPSAwLCBiID0gMSAvIG1heChGX3B1YiRyKSwgY29sID0gImdyYXkiLCBsdHkgPSAyKSAgIyBhcHByb3ggdGhlbyBsaW5lDQpgYGANCg0KIyBQYXJ0IDMNCg0KIyMg5pa55rOV5LiA77ya5YCh5bCO5a245qCh5byV5bCO5a2455Sf6YG45pOH5YGl5bq36aSQ55uSDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCmhlYWx0aHlfc2YgPC0gaGVhbHRoX3NmICU+JQ0KICBtdXRhdGUo6KGM5pS/5Y2AID0gc3RyX2V4dHJhY3QoRGVzY3JpcHRpb24sICLooYzmlL/ljYA6IFtePF0rIikgJT4lIA0KICAgICAgICAgICAgICAgICAgIHN0cl9yZW1vdmUoIuihjOaUv+WNgDogIikpDQoNCmhlYWx0aF9zZiA8LSBoZWFsdGh5X3NmICU+JQ0KICBmaWx0ZXIo6KGM5pS/5Y2AID09ICLkv6HnvqnljYAiKQ0KDQpzY2hvb2xzX3NmID0gc3Rfam9pbihTQ0hPT0xfc2YsIFRhaXBlaV9WaWxsX3NmLCBqb2luID0gc3Rfd2l0aGluKSU+JQ0KICBmaWx0ZXIoVE9XTiAlaW4lIGMoIuS/oee+qeWNgCIpKQ0KDQp3aW5fcG9seWdvbiA8LSBzdF9hc19zZmMoc3RfYmJveChoZWFsdGhfc2YpKQ0Kd2luIDwtIGFzLm93aW4od2luX3BvbHlnb24pDQoNCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhoZWFsdGhfc2YpDQpoZWFsdGh5Zm9vZF9wcHAgPC0gcHBwKHggPSBjb29yZHNbLCAxXSwgeSA9IGNvb3Jkc1ssIDJdLCB3aW5kb3cgPSB3aW4pDQoNCnJfc2VxIDwtIHNlcSgwLCA1MDAwLCBieSA9IDEwMCkNCg0Ka19yZXN1bHQgPC0gS2VzdChoZWFsdGh5Zm9vZF9wcHAsIHIgPSByX3NlcSkNCmtfMzAwMCA8LSBrX3Jlc3VsdCRpc29bd2hpY2goa19yZXN1bHQkciA9PSAzMDAwKV0NCmxfMzAwMCA8LSBzcXJ0KGtfMzAwMCAvIHBpKSAtMzAwMA0KDQpjYXQoIksoMzAwMCkgPSIsIGtfMzAwMCwgIlxuIikNCg0KY2F0KCJMKDMwMDApID0iLCBsXzMwMDAsICJcbiIpDQoNCmVudiA8LSBlbnZlbG9wZShoZWFsdGh5Zm9vZF9wcHAsDQogICAgICAgICAgICAgICAgZnVuID0gS2VzdCwNCiAgICAgICAgICAgICAgICBuc2ltID0gOTksDQogICAgICAgICAgICAgICAgciA9IHJfc2VxLA0KICAgICAgICAgICAgICAgIGNvcnJlY3Rpb24gPSAiaXNvIiwNCiAgICAgICAgICAgICAgICBzYXZlZnVucyA9IFRSVUUsDQogICAgICAgICAgICAgICAgc2F2ZXBhdHRlcm5zID0gVFJVRSwNCiAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpDQoNCnJfaW5kZXggPC0gd2hpY2goZW52JHIgPT0gMzAwMCkNCnVwcGVyX0wgPC0gc3FydChlbnYkaGlbcl9pbmRleF0gLyBwaSkgLTMwMDANCmxvd2VyX0wgPC0gc3FydChlbnYkbG9bcl9pbmRleF0gLyBwaSkgLTMwMDANCg0KY2F0KCI5NSUgQ0kgZm9yIEwoMzAwMCk6IFsiLCBsb3dlcl9MLCAiLCIsIHVwcGVyX0wsICJdXG4iKQ0KDQpwbG90KGVudiwgLiB+IHIsIG1haW4gPSAiUmlwbGV5J3Mgay1mdW5jdGlvbiB3aXRoIDk1JSBDSSIpDQphYmxpbmUodiA9IDMwMDAsIGNvbCA9ICJyZWQiLCBsdHkgPSAyKQ0KDQpjb29yZHNfcHViIDwtIHN0X2Nvb3JkaW5hdGVzKHNjaG9vbHNfc2YpDQpjb29yZHNfZmYgIDwtIHN0X2Nvb3JkaW5hdGVzKGhlYWx0aF9zZikNCg0Kd2luIDwtIGFzLm93aW4oc3RfYXNfc2ZjKHN0X2Jib3goc2Nob29sc19zZikpKQ0KDQpwcF9hbGxfcHViIDwtIHBwcCh4ID0gYyhjb29yZHNfcHViWywgMV0sIGNvb3Jkc19mZlssIDFdKSwNCiAgICAgICAgICAgICAgICAgIHkgPSBjKGNvb3Jkc19wdWJbLCAyXSwgY29vcmRzX2ZmWywgMl0pLA0KICAgICAgICAgICAgICAgICAgd2luZG93ID0gd2luLA0KICAgICAgICAgICAgICAgICAgbWFya3MgPSBmYWN0b3IoYyhyZXAoInNjaG9vbCIsIG5yb3coY29vcmRzX3B1YikpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXAoImhlYWx0aHlmb29kIiwgbnJvdyhjb29yZHNfZmYpKSkpKQ0KDQpyX3ZhbHMgPC0gc2VxKDAsIDMwMDAsIGJ5ID0gMikNCg0KRl9wdWIgPC0gZW52ZWxvcGUocHBfYWxsX3B1YiwNCiAgICAgICAgICAgICAgICAgIGZ1biA9IEZlc3QsDQogICAgICAgICAgICAgICAgICBpID0gInNjaG9vbCIsDQogICAgICAgICAgICAgICAgICBqID0gImhlYWx0aHlmb29kIiwNCiAgICAgICAgICAgICAgICAgIG5zaW0gPSA5OSwNCiAgICAgICAgICAgICAgICAgIHIgPSByX3ZhbHMsDQogICAgICAgICAgICAgICAgICB2ZXJib3NlID0gRkFMU0UpDQoNCnBsb3QoRl9wdWIsIG1haW4gPSAi5a245qChIOKGkiDlgaXlurfppJDnm5Llupcg55qEIEYtY3Jvc3Mg5Ye95pW4IiwgbGVnZW5kYXJncyA9IGxpc3QoY2V4ID0gMC44KSkNCmFibGluZShoID0gRl9wdWIkdGhlbywgY29sID0gImJsdWUiLCBsdHkgPSAyKQ0KYGBgDQoNCiMjIOaWueazleS6jO+8muijnOWKqeWtuOagoeWRqOmCiuWBpeW6t+mkkOebkualreiAhQ0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoj6K6A5Y+W6LOH5paZDQp2aWxsYWdlcyA8LSBzdF9yZWFkKCIuL2RhdGEvVGFpcGVpX1ZpbGwuc2hwIiwgb3B0aW9ucz0iRU5DT0RJTkc9QklHNSIpDQpzY2hvb2xzIDwtIHN0X3JlYWQoIi4vZGF0YS9TQ0hPT0wuc2hwIiwgb3B0aW9ucz0iRU5DT0RJTkc9QklHNSIpDQpoZWFsdGh5X2Zvb2QgPC0gc3RfcmVhZCgiLi9kYXRhL+iHuuWMl+W4guWBpeW6t+mkkOmjsi5rbWwiLCBvcHRpb25zPSJFTkNPRElORz1CSUc1IikNCg0KI+eiuuiqjUNSUw0KaGVhbHRoeV9mb29kIDwtIHN0X3RyYW5zZm9ybShoZWFsdGh5X2Zvb2QsIGNycyA9IHN0X2NycyhzY2hvb2xzKSkNCg0KI+W7uueriyAxNTAwIOWFrOWwuueahOe3qeihneWNgO+8iOS7peWtuOagoeeCuuS4reW/g++8iQ0Kc2Nob29sc19idWZmZXIgPC0gc3RfYnVmZmVyKHNjaG9vbHMsIGRpc3QgPSAxNTAwKQ0KDQoj5om+5Ye65q+P5omA5a245qChIGJ1ZmZlciDlhafnmoTlgaXlurfppJDpo7INCnNjaG9vbF9mb29kX2pvaW4gPC0gc3Rfam9pbihoZWFsdGh5X2Zvb2QsIHNjaG9vbHNfYnVmZmVyLCBqb2luID0gc3Rfd2l0aGluKQ0KDQoNCiPntbHoqIjmr4/miYDlrbjmoKHmnInlpJrlsJHlgIvlgaXlurfppJDpo7Lmk5rpu54NCnJlc3VsdCA8LSBzY2hvb2xfZm9vZF9qb2luICU+JQ0KICBzdF9kcm9wX2dlb21ldHJ5KCkgJT4lDQogIGdyb3VwX2J5KE5hbWUueSkgJT4lDQogIHN1bW1hcmlzZShIZWFsdGh5Rm9vZF9Db3VudCA9IG4oKSkNCg0KbmFtZXMoc2Nob29scykNCiMgWzFdICLnt6jomZ8iICJOQU1FIiAi5Zyw5Z2AIiAi5YKZ6Ki7IiAiZ2VvbWV0cnkiDQoNCm5hbWVzKHJlc3VsdCkNCiMgWzFdICJOYW1lLnkiICJIZWFsdGh5Rm9vZF9Db3VudCINCg0Kc2Nob29sc19zdW1tYXJ5IDwtIGxlZnRfam9pbihzY2hvb2xzLCByZXN1bHQsIGJ5ID0gYygiTmFtZSIgPSAiTmFtZS55IikpDQoNCmxpYnJhcnkodG1hcCkNCmJhc2VfbHlyIDwtIHRtX3NoYXBlKFRhaXBlaV9WaWxsX3NmKSArDQogIHRtX3BvbHlnb25zKGZpbGwgPSAid2hpdGUiLCBhbHBoYSA9IDEsIGJvcmRlci5jb2wgPSAiZ3JheTYwIikgKw0KICB0bV9sYXlvdXQoZnJhbWUgPSBGQUxTRSkNCg0KYmFzZV9seXIrDQp0bV9zaGFwZShzY2hvb2xzX3N1bW1hcnkpICsNCiAgdG1fZG90cyhzaXplID0gMC41LCBjb2wgPSAiSGVhbHRoeUZvb2RfQ291bnQiLCBwYWxldHRlID0gIkdyZWVucyIsIHRpdGxlID0gIjE1MDBtIOWFp+WBpeW6t+mkkOmjsuaVuCIpICsNCiAgdG1fbGF5b3V0KHRpdGxlID0gIuWPsOWMl+W4guWtuOagoeWRqOmCiuWBpeW6t+mjsumjn+WIhuW4gyIpDQpgYGANCg0KYGBge3J9DQp2aWxsYWdlcyA8LSBzdF90cmFuc2Zvcm0odmlsbGFnZXMsIGNycyA9IHN0X2NycyhzY2hvb2xzKSkNCnNjaG9vbHNfd2l0aF90b3duIDwtIHN0X2pvaW4oc2Nob29scywgdmlsbGFnZXMsIGpvaW4gPSBzdF93aXRoaW4pDQpzY2hvb2xfeGlueWkgPC0gc2Nob29sc193aXRoX3Rvd24gJT4lDQogIGZpbHRlcihUT1dOICVpbiUgYygi5L+h576p5Y2AIikpDQpzY2hvb2xfeGlueWlfYnVmZmVyIDwtIHN0X2J1ZmZlcihzY2hvb2xfeGlueWksIGRpc3QgPSAxNTAwKQ0KDQoj5bCN5oeJ5YGl5bq36aSQ6aOy6bue5L2NDQp4aW55aV9zY2hvb2xfZm9vZF9qb2luIDwtIHN0X2pvaW4oaGVhbHRoeV9mb29kLCBzY2hvb2xfeGlueWlfYnVmZmVyLCBqb2luID0gc3Rfd2l0aGluKQ0KDQoj57Wx6KiI5q+P5omA5a245qCh55qE5YGl5bq36aSQ6aOy5pW46YePDQp4aW55aV9yZXN1bHQgPC0geGlueWlfc2Nob29sX2Zvb2Rfam9pbiAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQ0KICBncm91cF9ieShOYW1lLnkpICU+JQ0KICBzdW1tYXJpc2UoSGVhbHRoeUZvb2RfQ291bnQgPSBuKCkpDQoNCiPliqDlm55nZW9tZXRyeQ0KeGlueWlfc3VtbWFyeSA8LSBsZWZ0X2pvaW4oc2Nob29sX3hpbnlpLCB4aW55aV9yZXN1bHQsIGJ5ID0gYygiTmFtZSIgPSAiTmFtZS55IikpDQpiYXNlX2x5ciA8LSB0bV9zaGFwZSh4aW55aV9zZikgKw0KICB0bV9wb2x5Z29ucyhmaWxsID0gIndoaXRlIiwgYWxwaGEgPSAxLCBib3JkZXIuY29sID0gImdyYXk2MCIpICsNCiAgdG1fbGF5b3V0KGZyYW1lID0gRkFMU0UpDQoNCmJhc2VfbHlyKw0KdG1fc2hhcGUoeGlueWlfc3VtbWFyeSkgKw0KICB0bV9kb3RzKHNpemUgPSAwLjUsIGNvbCA9ICJIZWFsdGh5Rm9vZF9Db3VudCIsIHBhbGV0dGUgPSAiR3JlZW5zIiwgdGl0bGUgPSAiMTUwMG0g5YWn5YGl5bq36aSQ6aOy5pW4IikgKw0KICB0bV9sYXlvdXQodGl0bGUgPSAi5L+h576p5Y2A5a245qCh5ZGo6YKK5YGl5bq36aOy6aOf5YiG5biDIikNCmBgYA0KDQpgYGB7cn0NCmhlYWx0aHlfZm9vZCA8LSBzdF90cmFuc2Zvcm0oaGVhbHRoeV9mb29kLCBzdF9jcnMoc2Nob29scykpDQp2aWxsYWdlcyA8LSBzdF90cmFuc2Zvcm0odmlsbGFnZXMsIHN0X2NycyhzY2hvb2xzKSkNCg0KIyDwn5SnIOiZleeQhiBNVUxUSVBPSU5UICsgWiDntq3luqbllY/poYwNCmhlYWx0aHlfZm9vZCA8LSBoZWFsdGh5X2Zvb2QgJT4lDQogIHN0X2Nhc3QoIlBPSU5UIikgJT4lDQogIHN0X3ptKGRyb3AgPSBUUlVFKQ0KIyDmib7lh7rkv6HnvqnljYDlrbjmoKENCnNjaG9vbHNfd2l0aF90b3duIDwtIHN0X2pvaW4oc2Nob29scywgdmlsbGFnZXMsIGpvaW4gPSBzdF93aXRoaW4pDQpzY2hvb2xfeGlueWkgPC0gc2Nob29sc193aXRoX3Rvd24gJT4lDQogIGZpbHRlcihUT1dOICVpbiUgYygi5L+h576p5Y2AIikpDQoNCiMg5bu656uL57ep6KGd5Y2ADQpzY2hvb2xfeGlueWlfYnVmZmVyIDwtIHN0X2J1ZmZlcihzY2hvb2xfeGlueWksIGRpc3QgPSA1MDApDQoNCiMg5Yqg5YWl5YGl5bq36aSQ6aOy6bue5L2N77yI5om+6JC95ZyoIGJ1ZmZlciDoo6HnmoTvvIkNCnhpbnlpX3NjaG9vbF9mb29kX2pvaW4gPC0gc3Rfam9pbihoZWFsdGh5X2Zvb2QsIHNjaG9vbF94aW55aV9idWZmZXIsIGpvaW4gPSBzdF93aXRoaW4pDQoNCiMg57Wx6KiI5q+P6ZaT5a245qCh55qE6aSQ6aOy6bue5pW46YePDQp4aW55aV9yZXN1bHQgPC0geGlueWlfc2Nob29sX2Zvb2Rfam9pbiAlPiUNCiAgc3RfZHJvcF9nZW9tZXRyeSgpICU+JQ0KICBncm91cF9ieShOYW1lLnkpICU+JQ0KICBzdW1tYXJpc2UoSGVhbHRoeUZvb2RfQ291bnQgPSBuKCkpDQoNCiPliqBnZW9tZXRyeQ0KeGlueWlfc3VtbWFyeSA8LSBsZWZ0X2pvaW4oc2Nob29sX3hpbnlpLCB4aW55aV9yZXN1bHQsIGJ5ID0gYygiTmFtZSIgPSAiTmFtZS55IikpDQoNCiPoppboprrljJYNCnRtYXBfbW9kZSgicGxvdCIpIA0KDQojIOWcsOWcluS4u+mrlOiIh+WcluWxpA0KYmFzZV9seXIgPC0gdG1fc2hhcGUoeGlueWlfc2YpICsNCiAgdG1fcG9seWdvbnMoZmlsbCA9ICJ3aGl0ZSIsIGFscGhhID0gMSwgYm9yZGVyLmNvbCA9ICJncmF5NjAiKSArDQogIHRtX2xheW91dChmcmFtZSA9IEZBTFNFKQ0KDQpiYXNlX2x5cisNCnRtX3NoYXBlKHNjaG9vbF94aW55aV9idWZmZXIpICsNCiAgdG1fcG9seWdvbnMoDQogICAgYWxwaGEgPSAwLjIsDQogICAgYm9yZGVyLmNvbCA9ICJibHVlIiwNCiAgICBib3JkZXIubHdkID0gMQ0KICApICsNCnRtX3NoYXBlKGhlYWx0aHlfZm9vZCkgKw0KICB0bV9kb3RzKA0KICAgIHNpemUgPSAwLjIsDQogICAgZmlsbCA9ICJyZWQiLA0KICAgIGZpbGwuYWxwaGEgPSAwLjcsDQogICAgbGVnZW5kLnNob3cgPSBUUlVFDQogICkgKw0KdG1fc2hhcGUoc2Nob29sX3hpbnlpKSArDQogIHRtX2RvdHMoDQogICAgc2l6ZSA9IDAuMDYsDQogICAgZmlsbCA9ICJibGFjayIsDQogICAgY29sID0gIndoaXRlIg0KICApICsNCiAgdG1fdGV4dCgiTmFtZSIsIHNpemUgPSAwLjYsIGNvbCA9ICJibGFjayIsIGp1c3QgPSAidG9wIikgKw0KdG1fdGl0bGUoIuS/oee+qeWNgOWtuOagoeWPiuWFtiAxNTAwIOWFrOWwuuWFp+WBpeW6t+mkkOmjsuWIhuW4gyIpICsNCnRtX2xheW91dChsZWdlbmQub3V0c2lkZSA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQp4aW55aV9yZXN1bHQgJT4lDQogIGd0KCkgJT4lDQogIHRhYl9oZWFkZXIoDQogICAgdGl0bGUgPSAi5L+h576p5Y2A5a245qCh5ZGo6YKK5YGl5bq36aOy6aOf6bue57Wx6KiIIiwNCiAgICBzdWJ0aXRsZSA9ICI1MDAg5YWs5bC656+E5ZyN5YWn55qE5YGl5bq36aSQ6aOy5pOa6bue5pW4Ig0KICApICU+JQ0KICBjb2xzX2xhYmVsKA0KICAgIE5hbWUueSA9ICLlrbjmoKHlkI3nqLEiLA0KICAgIEhlYWx0aHlGb29kX0NvdW50ID0gIuWBpeW6t+mkkOmjsuaVuOmHjyINCiAgKSAlPiUNCiAgZm10X251bWJlcigNCiAgICBjb2x1bW5zID0gSGVhbHRoeUZvb2RfQ291bnQsDQogICAgZGVjaW1hbHMgPSAwDQogICkNCmBgYA0KDQpgYGB7cn0NCnRtYXBfbW9kZSgidmlldyIpIA0KDQp0bV9zaGFwZShzY2hvb2xfeGlueWlfYnVmZmVyKSArDQogIHRtX3BvbHlnb25zKA0KICAgIGFscGhhID0gMC4yLA0KICAgIGJvcmRlci5jb2wgPSAiYmx1ZSIsDQogICAgYm9yZGVyLmx3ZCA9IDENCiAgKSArDQp0bV9zaGFwZShoZWFsdGh5X2Zvb2QpICsNCiAgdG1fZG90cygNCiAgICBzaXplID0gMC4yLA0KICAgIGZpbGwgPSAicmVkIiwNCiAgICBmaWxsLmFscGhhID0gMC43LA0KICAgIGxlZ2VuZC5zaG93ID0gVFJVRQ0KICApICsNCnRtX3NoYXBlKHNjaG9vbF94aW55aSkgKw0KICB0bV9kb3RzKA0KICAgIHNpemUgPSAwLjA2LA0KICAgIGZpbGwgPSAiYmxhY2siLA0KICAgIGNvbCA9ICJ3aGl0ZSINCiAgKSArDQogIHRtX3RleHQoIk5hbWUiLCBzaXplID0gMC42LCBjb2wgPSAiYmxhY2siLCBqdXN0ID0gInRvcCIpICsNCnRtX3RpdGxlKCLkv6HnvqnljYDlrbjmoKHlj4rlhbYgMTUwMCDlhazlsLrlhaflgaXlurfppJDpo7LliIbluIMiKSArDQp0bV9sYXlvdXQobGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KYGBgDQoNCiMjIOaWueazleS4ie+8mue2oOWcsOa0vuWwjSAgDQoqIOaJvumbomNlbnRyYWwgZmVhdHVyZeacgOi/keeahOWFrOWckg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpmaW5kX0NlbnRyYWxGZWF0dXJlID0gZnVuY3Rpb24oeHlfZGF0YV9mcmFtZSl7DQogIENGID0gY2FsY19jZihpZD0xLCBwb2ludHMgPSB4eV9kYXRhX2ZyYW1lKQ0KICBDRi54ID0gQ0YkQVRUUklCVVRFUyRDRi54DQogIENGLnkgPSBDRiRBVFRSSUJVVEVTJENGLnkNCiAgQ0YuY29vciA9IGMoQ0YueCwgQ0YueSkNCiAgQ0Zfc2YgPSBDRi5jb29yICU+JSBzdF9wb2ludCAlPiUgc3Rfc2ZjICU+JSBzdF9zZg0KICByZXR1cm4oQ0Zfc2YpDQp9DQoNCnhpbnlpX2luZGV4ID0gIFRhaXBlaV9WaWxsX3NmJFRPV04gPT0gIuS/oee+qeWNgCINCnhpbnlpX3NmID0gVGFpcGVpX1ZpbGxfc2ZbeGlueWlfaW5kZXgsXSANCg0KUGFya19zZiA9IHN0X3JlYWQoIi4vZGF0YS9MMDEwMS01Lmdlb2pzb24iKSANClBhcmtfeGluX3BvbHlfc2YgPSBzdF9qb2luKFBhcmtfc2YsIFRhaXBlaV9WaWxsX3NmLCBqb2luID0gc3Rfd2l0aGluKSU+JQ0KICBmaWx0ZXIoVE9XTiAlaW4lIGMoIuS/oee+qeWNgCIpKQ0KUGFya194aW55aV9zZiA9IHN0X2NlbnRyb2lkKFBhcmtfc2YpICU+JSANCiAgc3Rfam9pbihUYWlwZWlfVmlsbF9zZiwgam9pbiA9IHN0X3dpdGhpbiklPiUNCiAgZmlsdGVyKFRPV04gJWluJSBjKCLkv6HnvqnljYAiKSkNCg0Kc2Nob29sX3hpbnlpX3NmID0gc3Rfam9pbihTQ0hPT0xfc2YsIFRhaXBlaV9WaWxsX3NmLCBqb2luID0gc3Rfd2l0aGluKSU+JQ0KICBmaWx0ZXIoVE9XTiAlaW4lIGMoIuS/oee+qeWNgCIpKQ0KDQpzY2hvb2xfY29vciA9IHN0X2Nvb3JkaW5hdGVzKHNjaG9vbF94aW55aV9zZikNCnNjaG9vbF94aW55aV9zZiR4ID0gc2Nob29sX2Nvb3JbLCAxXQ0Kc2Nob29sX3hpbnlpX3NmJHkgPSBzY2hvb2xfY29vclssIDJdDQpzY2hvb2xfeGlueWlfY29vcl9kZiA9IGFzLmRhdGEuZnJhbWUoc2Nob29sX3hpbnlpX3NmWywxNDoxNV0pDQpDRl9zZiA9IGZpbmRfQ2VudHJhbEZlYXR1cmUoc2Nob29sX3hpbnlpX2Nvb3JfZGZbLDE6Ml0pDQpzdF9jcnMoQ0Zfc2YpID0gc3RfY3JzKFNDSE9PTF9zZikNCg0KZGlzdGFuY2VzID0gc3RfZGlzdGFuY2UoQ0Zfc2YsIFBhcmtfeGlueWlfc2YpDQpjbG9zZXN0X2luZGV4ID0gd2hpY2gubWluKGRpc3RhbmNlcykNCmNsb3Nlc3RfcGFyayA9IFBhcmtfeGlueWlfc2ZbY2xvc2VzdF9pbmRleCwgXQ0KY2xvc2VzdF9wYXJrX3BvbHkgPC0gc3Rfam9pbihjbG9zZXN0X3BhcmssIFBhcmtfeGluX3BvbHlfc2YsIGpvaW4gPSBzdF9pbnRlcnNlY3RzKQ0KYGBgDQoNCiog55Wr5Zyw5ZyWDQpgYGB7cn0NCmxpYnJhcnkodG1hcCkNCnRtYXBfbW9kZSgicGxvdCIpDQoNCiMg5Zyw5ZyW5Li76auU6IiH5ZyW5bGkDQpiYXNlX2x5ciA8LSB0bV9zaGFwZSh4aW55aV9zZikgKw0KICB0bV9wb2x5Z29ucyhmaWxsID0gIndoaXRlIiwgYWxwaGEgPSAxLCBib3JkZXIuY29sID0gImdyYXk2MCIpICsNCiAgdG1fbGF5b3V0KGZyYW1lID0gRkFMU0UpDQoNCiMg5a245qCh6bue5L2NDQpzY2hvb2xfbHlyIDwtIHRtX3NoYXBlKHNjaG9vbF94aW55aV9zZikgKw0KICB0bV9kb3RzKGZpbGwgPSAiYmx1ZSIsIHNpemUgPSAwLjIsIGZpbGwuYWxwaGEgPSAwLjgsDQogICAgICAgICAgdGl0bGUgPSAi5a245qChIikNCg0KIyDkuK3lpK7pu54gQ0YNCmNmX2x5ciA8LSB0bV9zaGFwZShDRl9zZikgKw0KICB0bV9zeW1ib2xzKHNoYXBlID0gMjEsIGZpbGwgPSAicmVkIiwgc2l6ZSA9IDAuNiwNCiAgICAgICAgICAgICB0aXRsZSA9ICLlrbjmoKHph43lv4Ppu54iKQ0KDQojIOaJgOacieWFrOWcku+8iOiDjOaZr++8iQ0KcGFya19seXIgPC0gdG1fc2hhcGUoUGFya194aW5fcG9seV9zZikgKw0KICB0bV9wb2x5Z29ucyhmaWxsID0gImxpZ2h0Z3JlZW4iLCBhbHBoYSA9IDAuNCwgYm9yZGVyLmNvbCA9IE5BLA0KICAgICAgICAgICAgICBsZWdlbmQuc2hvdyA9IEZBTFNFKSAgIyDkuI3orpPog4zmma/lhazlnJLlh7rnj77lnKjlnJbkvosNCg0KIyDmnIDov5HnmoTpgqPkuIDloYrlhazlnJLvvIjph43pu57mqJnoqJjvvIkNCmNsb3Nlc3RfcGFya19wb2x5IDwtIHN0X2pvaW4oY2xvc2VzdF9wYXJrLCBQYXJrX3hpbl9wb2x5X3NmLCBqb2luID0gc3RfbmVhcmVzdF9mZWF0dXJlKQ0KDQpjbG9zZXN0X3BhcmtfbHlyIDwtIHRtX3NoYXBlKGNsb3Nlc3RfcGFya19wb2x5KSArDQogIHRtX3BvbHlnb25zKGZpbGwgPSAiZm9yZXN0Z3JlZW4iLCBhbHBoYSA9IDAuNywNCiAgICAgICAgICAgICAgYm9yZGVyLmNvbCA9ICJkYXJrZ3JlZW4iLCBsd2QgPSAyLA0KICAgICAgICAgICAgICB0aXRsZSA9ICLmnIDov5HnmoTlhazlnJIiKQ0KDQojIOe1hOWQiOWcsOWcluS4puWKoOS4iuaomemhjOiIh+WcluS+i+ioreWumg0KDQpmaW5hbF9tYXAgPC0gYmFzZV9seXIgKyBwYXJrX2x5ciArIGNsb3Nlc3RfcGFya19seXIgKyBzY2hvb2xfbHlyICsgY2ZfbHlyICsNCiAgdG1fYWRkX2xlZ2VuZCh0eXBlID0gImZpbGwiLCBsYWJlbHMgPSBjKCLkv6HnvqnljYDlhazlnJIiLCAi57ag5Zyw5rS+5bCN5YWs5ZySIiksIA0KICAgICAgICAgICAgICAgIGNvbCA9IGMoImxpZ2h0Z3JlZW4iLCAiZm9yZXN0Z3JlZW4iKSkgKw0KICB0bV9hZGRfbGVnZW5kKHR5cGUgPSAic3ltYm9sIiwgbGFiZWxzID0gYygi5a245qChQ2VudHJhbCBGZWF0dXJlIiksIGNvbCA9ICJyZWQiLCBzaGFwZSA9IDIxLCBzaXplID0gMC42KSArDQogIHRtX2FkZF9sZWdlbmQodHlwZSA9ICJzeW1ib2wiLCBsYWJlbHMgPSBjKCLlrbjmoKEiKSwgY29sID0gImJsdWUiLCBzaGFwZSA9IDIxLCBzaXplID0gMC4yKSArDQogIHRtX3NjYWxlYmFyKHBvc2l0aW9uID0gYygibGVmdCIsICJib3R0b20iKSkgKyANCiAgdG1fY29tcGFzcyhwb3NpdGlvbiA9IGMoInJpZ2h0IiwgInRvcCIpKSArDQogIHRtX2xheW91dCgNCiAgICBtYWluLnRpdGxlID0gIuS/oee+qeWNgO+8mue2oOWcsOa0vuWwjeiIiei+puWcsOm7niIsDQogICAgbWFpbi50aXRsZS5zaXplID0gMS4yLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoImxlZnQiLCAiYm90dG9tIiksDQogICAgbGVnZW5kLmJnLmNvbG9yID0gIndoaXRlIiwNCiAgICBsZWdlbmQuYmcuYWxwaGEgPSAwLjcsDQogICAgZnJhbWUgPSBGQUxTRQ0KICApDQpwcmludChmaW5hbF9tYXApDQp0bWFwX3NhdmUoZmluYWxfbWFwLCBmaWxlbmFtZSA9ICJ4aW55aV9tYXAuanBnIiwgd2lkdGggPSAyMDAwLCBoZWlnaHQgPSAxNjAwLCBkcGkgPSAzMDApDQpgYGANCg0K