school_sf <- st_read("./DATA/SCHOOL/SCHOOL.shp",options = "encoding=Big5" )
Vill_sf <- st_read("./DATA/Taipei_Vill/Taipei_Vill.shp",options = "encoding=Big5" )
FF_sf <- st_read("./DATA/Tpe_Fastfood/Tpe_Fastfood.shp",options = "encoding=Big5" )

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

問答題

  1. 此圖中的 X 軸表示各村里速食店密度的標準化值,Y軸則表示鄰近村里的速食店密度之平均值,即空間滯後值,也經過標準化。由於圖中大多數點分布於第一象限(高密度鄰近高密度)與第三象限(低密度鄰近低密度),顯示該城市速食店密度具有明顯的空間群聚特徵,屬於正空間自相關的型態。
  1. 該圖中的趨勢線斜率值為 0.982,代表整體速食店密度與鄰近區域速食店密度之間具有高度正向關聯,此斜率即為 Global Moran’s I 值。此數值越接近 1,表示該城市的速食店密度分布具有越強的正空間自相關,代表高密度區附近傾向出現其他高密度區,低密度區則鄰近其他低密度區,呈現出明顯的空間群聚現象。
  1. 基於圖 1,我認為其 Local Moran’s I 統計量是大於 0,因為圖中多數點分布於第一象限(高值鄰近高值)與第三象限(低值鄰近低值),顯示該城市的速食店密度呈現正向空間自相關,Local Moran’s I 值會反映其群聚的程度,因此在這種情形下其統計量會大於 0。

實作題

1.

p >= 0.05,無法拒絕虛無假設,表示無顯著差異。

FF_vill_sf <- st_join(FF_sf, Vill_sf, join = st_within, left = FALSE)
a_ff <- FF_vill_sf %>% 
  filter(TOWN %in% c("文山區", "大安區", "中正區"))

# B區:信義、南港、松山
b_ff <- FF_vill_sf %>% 
  filter(TOWN %in% c("信義區", "南港區", "松山區"))
a_buffer <- st_buffer(a_ff, dist = 1000)
b_buffer <- st_buffer(b_ff, dist = 1000)

a_school_counts <- sapply(1:nrow(a_buffer), function(i) {
  sum(st_intersects(a_buffer[i,], school_sf, sparse = FALSE))
})

b_school_counts <- sapply(1:nrow(b_buffer), function(i) {
  sum(st_intersects(b_buffer[i,], school_sf, sparse = FALSE))
})

t_result <- t.test(a_school_counts, b_school_counts, var.equal = FALSE)

cat("統計檢定摘要\n")
統計檢定摘要
cat("虛無假設 H₀:A區與B區速食店涵蓋學校數平均值無差異\n")
虛無假設 H₀:A區與B區速食店涵蓋學校數平均值無差異
cat("對立假設 H₁:A區與B區速食店涵蓋學校數平均值有差異\n")
對立假設 H₁:A區與B區速食店涵蓋學校數平均值有差異
cat("A區平均學校數:", mean(a_school_counts), "\n")
A區平均學校數: 3.870968 
cat("B區平均學校數:", mean(b_school_counts), "\n")
B區平均學校數: 3.318182 
cat("t 值:", round(t_result$statistic, 3), "\n")
t 值: 1.598 
cat("p 值:", round(t_result$p.value, 4), "\n")
p 值: 0.1163 
if(t_result$p.value < 0.05){
  cat("結果:p < 0.05,拒絕虛無假設,表示 A區與B區涵蓋學校數量有顯著差異。\n")
} else {
  cat("結果:p >= 0.05,無法拒絕虛無假設,表示無顯著差異。\n")
}
結果:p >= 0.05,無法拒絕虛無假設,表示無顯著差異。
2.

圖中為以速食店數量為變數所繪製的 Moran’s I Correlogram,X 軸代表鄰近距離的標準化單位(distance classes),Y 軸為對應距離下的 Moran’s I 自相關指標值。從圖中可以觀察到,前三個距離階層的 Moran’s I 值為正,且達統計顯著水準(紅點標記),顯示速食店於鄰近空間內具有明顯的群聚現象。而隨著距離增加,Moran’s I 呈現持續上升但仍偏低,表示速食店的空間群聚效應較為局部,整體空間結構趨向微弱正相關。基於此圖,可推估速食店的空間影響範圍約在前三個距離階層內(約 1000–1500 公尺),此範圍內的空間單元較可能因鄰近速食店影響而呈現分布集聚現象。

grid <- st_make_grid(Vill_sf, cellsize = 500, square = TRUE)
grid_sf <- st_sf(grid_id = 1:length(grid), geometry = grid)

ff_count <- st_join(grid_sf, FF_sf, join = st_contains) %>%
  group_by(grid_id) %>%
  summarise(count = n()) %>%
  st_as_sf()
View(school_sf)
View(school_sf)

nb <- poly2nb(ff_count, queen = TRUE)
lw <- nb2listw(nb, style = "W")

correlog <- sp.correlogram(nb, ff_count$count, order = 10,
                           method = "I", style = "W", zero.policy = TRUE)
correlog_mat <- matrix(unlist(correlog$res), ncol = 5, byrow = TRUE)
correlog_df <- as.data.frame(correlog_mat)
colnames(correlog_df) <- c("lag", "I", "expected", "sd", "p.value")

plot(correlog_df$lag, correlog_df$I, type = "b",
     xlab = "distance classes", ylab = "Moran I statistic",
     ylim = c(min(correlog_df$I) - 0.05, max(correlog_df$I) + 0.05))

abline(h = 0, lty = 2)

sig_lags <- correlog_df$p.value < 0.05
points(correlog_df$lag[sig_lags], correlog_df$I[sig_lags],
       col = "red", pch = 19)

3.
grid_sf$count_ff <- ff_count$count
grid_sf$count_school <- st_join(grid_sf, school_sf, join = st_contains) %>%
  group_by(grid_id) %>%
  summarise(count = n()) %>%
  pull(count)

grid_sf$count_school[is.na(grid_sf$count_school)] <- 0  # 替代 NA 為 0

nb <- poly2nb(grid_sf, queen = TRUE)
lw <- nb2listw(nb, style = "W")

gi_ff <- localG(grid_sf$count_ff, lw)
gi_school <- localG(grid_sf$count_school, lw)

p_ff <- 2 * pnorm(-abs(gi_ff))
p_school <- 2 * pnorm(-abs(gi_school))

p_ff_fdr <- p.adjust(p_ff, method = "fdr")
p_school_fdr <- p.adjust(p_school, method = "fdr")

grid_sf$ff_hotspot <- ifelse(p_ff_fdr < 0.05, gi_ff, 0)
grid_sf$school_hotspot <- ifelse(p_school_fdr < 0.05, gi_school, 0)
tm_shape(grid_sf) +
  tm_fill("ff_hotspot", palette = "-RdBu", title = "Fast food Gi*",
          style = "cont", midpoint = NA) +
  tm_borders() +
  tm_layout(main.title = "速食店熱區分析", legend.outside = TRUE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_fill()`: instead of `style = "cont"`, use fill.scale = `tm_scale_continuous()`.
ℹ Migrate the argument(s) 'midpoint', 'palette' (rename to 'values') to 'tm_scale_continuous(<HERE>)'
For small multiples, specify a 'tm_scale_' for each multiple, and put them in a list: 'fill'.scale =
list(<scale1>, <scale2>, ...)'[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(main.title = )`Multiple palettes called "rd_bu" found: "brewer.rd_bu", "matplotlib.rd_bu". The first one, "brewer.rd_bu", is returned.
[cols4all] color palettes: use palettes from the R package cols4all. Run `cols4all::c4a_gui()` to explore
them. The old palette name "-RdBu" is named "rd_bu" (in long format "brewer.rd_bu")

tm_shape(grid_sf) +
  tm_fill("school_hotspot", palette = "-RdBu", title = "School Gi*",
          style = "cont", midpoint = NA) +
  tm_borders() +
  tm_layout(main.title = "學校熱區分析", legend.outside = TRUE)

── tmap v3 code detected ─────────────────────────────────────────────────────────────────────────────────────
[v3->v4] `tm_fill()`: instead of `style = "cont"`, use fill.scale = `tm_scale_continuous()`.
ℹ Migrate the argument(s) 'midpoint', 'palette' (rename to 'values') to 'tm_scale_continuous(<HERE>)'
For small multiples, specify a 'tm_scale_' for each multiple, and put them in a list: 'fill'.scale =
list(<scale1>, <scale2>, ...)'[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(main.title = )`Multiple palettes called "rd_bu" found: "brewer.rd_bu", "matplotlib.rd_bu". The first one, "brewer.rd_bu", is returned.
[cols4all] color palettes: use palettes from the R package cols4all. Run `cols4all::c4a_gui()` to explore
them. The old palette name "-RdBu" is named "rd_bu" (in long format "brewer.rd_bu")

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

實作題

1.

此圖比較大安區、文山區與信義區學校至最近速食店的距離累積分布情形,顯示信義區的學校可及性最高,約八成學校在 500 公尺內就能抵達速食店;大安區次之,約六成學校在同樣距離內;而文山區則可及性最低,多數學校距離速食店超過 500 公尺。整體而言,信義區學校暴露於速食環境的程度最高,文山區則相對較低。

SH_vill_sf <- st_join(school_sf, Vill_sf, join = st_within, left = FALSE)
school_da <- SH_vill_sf %>% filter(TOWN == "大安區")
school_ws <- SH_vill_sf %>% filter(TOWN == "文山區")
school_xy <- SH_vill_sf %>% filter(TOWN == "信義區")
min_dist <- function(school_pts, fastfood_pts) {
  dists <- st_distance(school_pts, fastfood_pts)
  apply(dists, 1, min) %>% as.numeric()
}

dist_da <- min_dist(school_da, FF_sf)
dist_ws <- min_dist(school_ws, FF_sf)
dist_xy <- min_dist(school_xy, FF_sf)
d_range <- seq(0, 2000, by = 10)

get_Fd <- function(dist_vec, d_range) {
  sapply(d_range, function(d) mean(dist_vec <= d))
}

Fd_da <- get_Fd(dist_da, d_range)
Fd_ws <- get_Fd(dist_ws, d_range)
Fd_xy <- get_Fd(dist_xy, d_range)

plot(d_range, Fd_da, type = "l", col = "blue", lwd = 3,
     xlab = "Distance (m)", ylab = "Cumulative Proportion",
     main = "F(d): 學校到速食店距離累積分布", ylim = c(0, 1))

lines(d_range, Fd_ws, col = "green", lwd = 3)
lines(d_range, Fd_xy, col = "red", lwd = 3)

text(2000, Fd_da[length(Fd_da)], "大安區", col = "blue", pos = 4)
text(2000, Fd_ws[length(Fd_ws)], "文山區", col = "green", pos = 4)
text(2000, Fd_xy[length(Fd_xy)], "信義區", col = "red", pos = 4)

legend("bottomright", legend = c("大安區", "文山區", "信義區"),
       col = c("blue", "green", "red"), lty = 1, lwd = 3)

2.

根據 Ripley’s L 函數分析結果,速食店在 3000 公尺距離下的L值為4374.25,明顯高於 Monte Carlo 模擬所得之 95% 信賴區間(上限為 3204.45),顯示其空間分布存在顯著的群聚現象。並顯示速食店在台北市區內傾向集中開立於特定地區,而非隨機分布。

ff_sp <- st_transform(FF_sf, 3826)
win <- as.owin(st_bbox(ff_sp))  
coords <- st_coordinates(ff_sp)
pp <- ppp(x = coords[,1], y = coords[,2], window = win)
警告: data contain duplicated points
Kest_result <- Kest(pp, r = seq(0, 3000, by = 100), correction = "Ripley")

Lest_result <- Lest(pp, r = seq(0, 3000, by = 100), correction = "Ripley")

envelope_result <- envelope(pp, fun = Lest, nsim = 99, rank = 1,
                            correction = "Ripley", r = seq(0, 3000, by = 100))
Generating 99 simulations of CSR  ...
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.
plot(envelope_result, main = "L(r) with 95% Confidence Interval",
     legend = FALSE)
abline(a = 0, b = 1, lty = 2, col = "gray") 

K3000 <- Kest_result$iso[which(Kest_result$r == 3000)]
L3000 <- Lest_result$iso[which(Lest_result$r == 3000)]
L3000_lo <- envelope_result$lo[which(envelope_result$r == 3000)]
L3000_hi <- envelope_result$hi[which(envelope_result$r == 3000)]

cat("K(3000):", K3000, "\n")
K(3000): 60111493 
cat("L(3000):", L3000, "\n")
L(3000): 4374.252 
cat("L(3000) 95% 信賴區間:", L3000_lo, "~", L3000_hi, "\n")
L(3000) 95% 信賴區間: 2810.895 ~ 3202.906 
3.

Bivariate F function 分析公立與私立學校到速食店的空間可及性,並進一步結合 CSR(完全隨機分布)模擬,以顯著水準=0.05 檢定兩者是否有顯著差異。從第一張圖顯示,私立學校的 F(d) 累積曲線明顯快於公立學校,表示其在較短距離內即有較多速食店可達,可及性較高。為檢定此差異是否具統計意義,分別對私立與公立學校進行 CSR 模擬(第二與第三張圖)。

結果顯示,私立學校的 F(d) 曲線明顯高於 CSR 模擬的 95% 信賴區間上限(圖二),表示速食店在空間上有顯著集中於私立學校周邊的現象(p < 0.05)。相對地,公立學校的 F(d) 曲線多數落在 CSR 模擬區間內(圖三),表示其鄰近速食店的分布趨近於隨機,無顯著群聚特性。綜上所述,私立學校的周邊擁有顯著較多的速食店,可及性高於公立學校,且達統計顯著水準的0.05。

school_public <- SH_vill_sf %>% filter(Type == "公立")
school_private <- SH_vill_sf %>% filter(Type == "私立")
win <- as.owin(st_bbox(school_sf))
coords_public <- st_coordinates(st_transform(school_public, 3826))
coords_private <- st_coordinates(st_transform(school_private, 3826))
coords_fastfood <- st_coordinates(st_transform(FF_sf, 3826))

pp_public <- ppp(coords_public[,1], coords_public[,2], window = win)
pp_private <- ppp(coords_private[,1], coords_private[,2], window = win)
pp_fastfood <- ppp(coords_fastfood[,1], coords_fastfood[,2], window = win)
警告: data contain duplicated points
F_public <- Fest(pp_public, pp_fastfood, correction = "rs", r = seq(0, 2000, by = 25))
F_private <- Fest(pp_private, pp_fastfood, correction = "rs", r = seq(0, 2000, by = 25))
plot(F_public$r, F_public$rs, type = "l", col = "blue", lwd = 2,
     xlab = "距離 d (公尺)", ylab = "F(d)",
     main = "公立 vs 私立學校到速食店 F(d)", ylim = c(0, 1))  # 加這行!

lines(F_private$r, F_private$rs, col = "red", lwd = 2)
legend("bottomright", legend = c("公立學校", "私立學校"),
       col = c("blue", "red"), lty = 1, lwd = 2)

F_env_private <- envelope(pp_private, fun = Fest,
                          simulate = expression(runifpoint(pp_fastfood$n, win = win)),
                          nsim = 99, rank = 1,
                          correction = "rs", r = seq(0, 2000, by = 25))
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.
F_env_public <- envelope(pp_public, fun = Fest,
                         simulate = expression(runifpoint(pp_fastfood$n, win = win)),
                         nsim = 99, rank = 1,
                         correction = "rs", r = seq(0, 2000, by = 25))
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.
plot(F_env_private, main = "私立學校到速食店 F(d) 與 CSR 模擬 95% 信賴區間",
     xlab = "距離 d (公尺)", ylab = "F(d)", legend = FALSE)

lines(F_private$r, F_private$rs, col = "red", lwd = 2)


legend("bottomright", legend = c("私立學校", "CSR模擬區間"),
       col = c("red", "gray"), lty = c(1,1,NA), lwd = 2)

plot(F_env_public, main = "公立學校到速食店 F(d) 與 CSR 模擬 95% 信賴區間",
     xlab = "距離 d (公尺)", ylab = "F(d)", legend = FALSE)

lines(F_public$r, F_public$rs, col = "blue", lwd = 2)


legend("bottomright", legend = c("公立學校", "CSR模擬區間"),
       col = c("blue", "gray"), lty = c(1,1,NA), lwd = 2)

Part3

將 Vill_sf 合併為「臺北市整體邊界」
Taipei_city <- Vill_sf %>%
  summarise(geometry = st_union(geometry)) %>%
  st_make_valid() 
Taipei_city$COUNTY <- "臺北市"
school_sf增加COUNTY、TOWN、VILLAGE欄位
school_data_joined <- st_join(school_data, Vill_sf, join = st_within)
school_sf2 <- school_data_joined %>%
  select(COUNTY,TOWN,VILLAGE,Type,Name,SID)
將Vill_sf從村里層級,變成行政區層級
TOWN_sf <- Vill_sf %>%
  group_by(TOWN) %>%
  summarise(geometry = st_union(geometry)) %>%
  ungroup() %>%
  st_make_valid()  
TOWN_sf$COUNTY <- "臺北市"
TOWN_line <- TOWN_sf %>%
  st_cast("MULTILINESTRING")
STEP 1:計算每所學校的周邊速食店數量
school_buff <- st_buffer(school_sf2, dist = 500)

# 計算每個學校 buffer 內有幾間速食店
school_ff <- st_join(school_buff, FF_sf, join = st_intersects) %>%
  group_by(Name) %>%
  summarise(ff_count = n()) %>%
  st_drop_geometry()

# 合併筆數進原始學校點
school_data <- school_sf2 %>%
  left_join(school_ff, by = "Name") %>%
  mutate(ff_count = replace_na(ff_count, 0))
STEP 2:建立鄰接矩陣(以學校為點位k-nearest neighbor)
coords <- st_coordinates(st_centroid(school_data))

nb <- knn2nb(knearneigh(coords, k = 5))
lw <- nb2listw(nb, style = "W")
STEP 3:計算 Local G* 值(z-score)
gi_star <- localG(school_data$ff_count, lw)

school_data$GiZ <- as.numeric(gi_star)
# 確保 GiZ_class 分類完成
school_data <- school_data %>%
  mutate(GiZ_class = case_when(
    GiZ >= 2.58  ~ "極顯著熱點",  # p < 0.01
    GiZ >= 1.96  ~ "顯著熱點",    # p < 0.05
    GiZ <= -2.58 ~ "極顯著冷點",  # p < 0.01
    GiZ <= -1.96 ~ "顯著冷點",    # p < 0.05
    TRUE         ~ "非顯著"
  ))

# 畫出底圖 + 熱點分類地圖
ggplot() +
  geom_sf(data = Vill_sf, fill = "grey95", color = "grey80") +    # 行政區底圖
  

  geom_sf(data = school_data, aes(fill = GiZ_class), shape = 21, color = "black") +
  geom_sf(data = TOWN_line, color = "black", size = 0.5) +   # 加上邊界線
  theme_minimal()+
  
  geom_sf(data = school_data, aes(fill = GiZ_class), color = "black", size = 2, shape = 21) +
  scale_fill_manual(
    values = c(
      "極顯著熱點" = "#800026",
      "顯著熱點"   = "#FC4E2A",
      "非顯著"     = "white",
      "顯著冷點"   = "#2C7FB8",
      "極顯著冷點" = "#084081"
    ),
    name = "Local G* 分類"
  ) +
  labs(
    title = "臺北市國小周邊速食店 Local G* 熱點分析",
    caption = "註:分類依照 GiZ z-score 門檻(±1.96、±2.58)"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(size = 18, face = "bold"),
    plot.subtitle = element_text(size = 14),
    legend.position = "right"
  )

NA
NA

ggplot(data.frame(GiZ = GiZ_vector), aes(x = GiZ)) +
  geom_histogram(binwidth = 0.3, fill = "lightblue", color = "black") +
  geom_vline(xintercept = c(-2.58, -1.96, 1.96, 2.58), 
             color = "darkgray", linetype = "dotted") +
  scale_x_continuous(
    breaks = c(-2.58, -1.96, 0, 1.96, 2.58),
    labels = c("-2.58", "-1.96", "0", "1.96", "2.58")
  ) +
  labs(title = "臺北市國小 Local G* Z-score 分布圖",
       x = "GiZ 值", y = "學校數量") +
  theme_minimal()+
  theme(
    plot.title = element_text(hjust = 0.5)  
  )

LS0tDQp0aXRsZTogIjQxMDIzMjI0TOadjuWnv+eRvl9GaW5hbFJlcG9ydCINCmRhdGU6ICcyMDI1LTA2LTAyJw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19kZXB0aDogNg0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KLS0tDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHNwZGVwKQ0KbGlicmFyeShzZikNCmxpYnJhcnkodG1hcCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHNwYXRzdGF0KQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCg0KYGBgDQoNCg0KYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9DQpzY2hvb2xfc2YgPC0gc3RfcmVhZCgiLi9EQVRBL1NDSE9PTC9TQ0hPT0wuc2hwIixvcHRpb25zID0gImVuY29kaW5nPUJpZzUiICkNClZpbGxfc2YgPC0gc3RfcmVhZCgiLi9EQVRBL1RhaXBlaV9WaWxsL1RhaXBlaV9WaWxsLnNocCIsb3B0aW9ucyA9ICJlbmNvZGluZz1CaWc1IiApDQpGRl9zZiA8LSBzdF9yZWFkKCIuL0RBVEEvVHBlX0Zhc3Rmb29kL1RwZV9GYXN0Zm9vZC5zaHAiLG9wdGlvbnMgPSAiZW5jb2Rpbmc9QmlnNSIgKQ0KYGBgDQoNCiMjIyBQYXJ0IDE65LiN5ZCM6KGM5pS/5Y2A55qE6YCf6aOf5bqX5pyJ5LiN5ZCM55qE5Zyw55CG5YiG5biD54m55b615ZeO77yfDQojIyMjIOWVj+etlOmhjA0KDQo+ICgxKSDmraTlnJbkuK3nmoQgWCDou7jooajnpLrlkITmnZHph4zpgJ/po5/lupflr4bluqbnmoTmqJnmupbljJblgLzvvIxZ6Lu45YmH6KGo56S66YSw6L+R5p2R6YeM55qE6YCf6aOf5bqX5a+G5bqm5LmL5bmz5Z2H5YC877yM5Y2z56m66ZaT5ruv5b6M5YC877yM5Lmf57aT6YGO5qiZ5rqW5YyW44CC55Sx5pa85ZyW5Lit5aSn5aSa5pW46bue5YiG5biD5pa856ys5LiA6LGh6ZmQ77yI6auY5a+G5bqm6YSw6L+R6auY5a+G5bqm77yJ6IiH56ys5LiJ6LGh6ZmQ77yI5L2O5a+G5bqm6YSw6L+R5L2O5a+G5bqm77yJ77yM6aGv56S66Kmy5Z+O5biC6YCf6aOf5bqX5a+G5bqm5YW35pyJ5piO6aGv55qE56m66ZaT576k6IGa54m55b6177yM5bGs5pa85q2j56m66ZaT6Ieq55u46Zec55qE5Z6L5oWL44CCDQoNCj4gKDIpIOipsuWcluS4reeahOi2qOWLoue3muaWnOeOh+WAvOeCuiAwLjk4Mu+8jOS7o+ihqOaVtOmrlOmAn+mjn+W6l+WvhuW6puiIh+mEsOi/keWNgOWfn+mAn+mjn+W6l+WvhuW6puS5i+mWk+WFt+aciemrmOW6puato+WQkemXnOiBr++8jOatpOaWnOeOh+WNs+eCuiBHbG9iYWwgTW9yYW7igJlzIEkg5YC844CC5q2k5pW45YC86LaK5o6l6L+RIDHvvIzooajnpLroqbLln47luILnmoTpgJ/po5/lupflr4bluqbliIbluIPlhbfmnInotorlvLfnmoTmraPnqbrplpPoh6rnm7jpl5zvvIzku6Pooajpq5jlr4bluqbljYDpmYTov5Hlgr7lkJHlh7rnj77lhbbku5bpq5jlr4bluqbljYDvvIzkvY7lr4bluqbljYDliYfphLDov5Hlhbbku5bkvY7lr4bluqbljYDvvIzlkYjnj77lh7rmmI7poa/nmoTnqbrplpPnvqTogZrnj77osaHjgIINCg0KPiAoMykg5Z+65pa85ZyWIDHvvIzmiJHoqo3ngrrlhbYgTG9jYWwgTW9yYW7igJlzIEkg57Wx6KiI6YeP5piv5aSn5pa8IDDvvIzlm6DngrrlnJbkuK3lpJrmlbjpu57liIbluIPmlrznrKzkuIDosaHpmZDvvIjpq5jlgLzphLDov5Hpq5jlgLzvvInoiIfnrKzkuInosaHpmZDvvIjkvY7lgLzphLDov5HkvY7lgLzvvInvvIzpoa/npLroqbLln47luILnmoTpgJ/po5/lupflr4bluqblkYjnj77mraPlkJHnqbrplpPoh6rnm7jpl5zvvIxMb2NhbCBNb3JhbuKAmXMgSSDlgLzmnIPlj43mmKDlhbbnvqTogZrnmoTnqIvluqbvvIzlm6DmraTlnKjpgJnnqK7mg4XlvaLkuIvlhbbntbHoqIjph4/mnIPlpKfmlrwgMOOAgg0KDQojIyMjIOWvpuS9nOmhjA0KIyMjIyMgMS4NCj4gcCA+PSAwLjA177yM54Sh5rOV5ouS57WV6Jmb54Sh5YGH6Kit77yM6KGo56S654Sh6aGv6JGX5beu55Ww44CCDQoNCmBgYHtyfQ0KRkZfdmlsbF9zZiA8LSBzdF9qb2luKEZGX3NmLCBWaWxsX3NmLCBqb2luID0gc3Rfd2l0aGluLCBsZWZ0ID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQphX2ZmIDwtIEZGX3ZpbGxfc2YgJT4lIA0KICBmaWx0ZXIoVE9XTiAlaW4lIGMoIuaWh+WxseWNgCIsICLlpKflronljYAiLCAi5Lit5q2j5Y2AIikpDQoNCiMgQuWNgO+8muS/oee+qeOAgeWNl+a4r+OAgeadvuWxsQ0KYl9mZiA8LSBGRl92aWxsX3NmICU+JSANCiAgZmlsdGVyKFRPV04gJWluJSBjKCLkv6HnvqnljYAiLCAi5Y2X5riv5Y2AIiwgIuadvuWxseWNgCIpKQ0KYGBgDQoNCmBgYHtyfQ0KYV9idWZmZXIgPC0gc3RfYnVmZmVyKGFfZmYsIGRpc3QgPSAxMDAwKQ0KYl9idWZmZXIgPC0gc3RfYnVmZmVyKGJfZmYsIGRpc3QgPSAxMDAwKQ0KDQphX3NjaG9vbF9jb3VudHMgPC0gc2FwcGx5KDE6bnJvdyhhX2J1ZmZlciksIGZ1bmN0aW9uKGkpIHsNCiAgc3VtKHN0X2ludGVyc2VjdHMoYV9idWZmZXJbaSxdLCBzY2hvb2xfc2YsIHNwYXJzZSA9IEZBTFNFKSkNCn0pDQoNCmJfc2Nob29sX2NvdW50cyA8LSBzYXBwbHkoMTpucm93KGJfYnVmZmVyKSwgZnVuY3Rpb24oaSkgew0KICBzdW0oc3RfaW50ZXJzZWN0cyhiX2J1ZmZlcltpLF0sIHNjaG9vbF9zZiwgc3BhcnNlID0gRkFMU0UpKQ0KfSkNCg0KdF9yZXN1bHQgPC0gdC50ZXN0KGFfc2Nob29sX2NvdW50cywgYl9zY2hvb2xfY291bnRzLCB2YXIuZXF1YWwgPSBGQUxTRSkNCg0KY2F0KCLntbHoqIjmqqLlrprmkZjopoFcbiIpDQpjYXQoIuiZm+eEoeWBh+iorSBI4oKA77yaQeWNgOiIh0LljYDpgJ/po5/lupfmtrXok4vlrbjmoKHmlbjlubPlnYflgLznhKHlt67nlbBcbiIpDQpjYXQoIuWwjeeri+WBh+iorSBI4oKB77yaQeWNgOiIh0LljYDpgJ/po5/lupfmtrXok4vlrbjmoKHmlbjlubPlnYflgLzmnInlt67nlbBcbiIpDQpjYXQoIkHljYDlubPlnYflrbjmoKHmlbjvvJoiLCBtZWFuKGFfc2Nob29sX2NvdW50cyksICJcbiIpDQpjYXQoIkLljYDlubPlnYflrbjmoKHmlbjvvJoiLCBtZWFuKGJfc2Nob29sX2NvdW50cyksICJcbiIpDQpjYXQoInQg5YC877yaIiwgcm91bmQodF9yZXN1bHQkc3RhdGlzdGljLCAzKSwgIlxuIikNCmNhdCgicCDlgLzvvJoiLCByb3VuZCh0X3Jlc3VsdCRwLnZhbHVlLCA0KSwgIlxuIikNCmlmKHRfcmVzdWx0JHAudmFsdWUgPCAwLjA1KXsNCiAgY2F0KCLntZDmnpzvvJpwIDwgMC4wNe+8jOaLkue1leiZm+eEoeWBh+iore+8jOihqOekuiBB5Y2A6IiHQuWNgOa2teiTi+WtuOagoeaVuOmHj+aciemhr+iRl+W3rueVsOOAglxuIikNCn0gZWxzZSB7DQogIGNhdCgi57WQ5p6c77yacCA+PSAwLjA177yM54Sh5rOV5ouS57WV6Jmb54Sh5YGH6Kit77yM6KGo56S654Sh6aGv6JGX5beu55Ww44CCXG4iKQ0KfQ0KYGBgDQojIyMjIyAyLg0KDQo+IOWcluS4reeCuuS7pemAn+mjn+W6l+aVuOmHj+eCuuiuiuaVuOaJgOe5quijveeahCBNb3JhbidzIEkgQ29ycmVsb2dyYW3vvIxYIOi7uOS7o+ihqOmEsOi/kei3nemboueahOaomea6luWMluWWruS9je+8iGRpc3RhbmNlIGNsYXNzZXPvvInvvIxZIOi7uOeCuuWwjeaHiei3nembouS4i+eahCBNb3JhbuKAmXMgSSDoh6rnm7jpl5zmjIfmqJnlgLzjgILlvp7lnJbkuK3lj6/ku6Xop4Dlr5/liLDvvIzliY3kuInlgIvot53pm6Lpmo7lsaTnmoQgTW9yYW7igJlzIEkg5YC854K65q2j77yM5LiU6YGU57Wx6KiI6aGv6JGX5rC05rqW77yI57SF6bue5qiZ6KiY77yJ77yM6aGv56S66YCf6aOf5bqX5pa86YSw6L+R56m66ZaT5YWn5YW35pyJ5piO6aGv55qE576k6IGa54++6LGh44CC6ICM6Zqo6JGX6Led6Zui5aKe5Yqg77yMTW9yYW7igJlzIEkg5ZGI54++5oyB57qM5LiK5Y2H5L2G5LuN5YGP5L2O77yM6KGo56S66YCf6aOf5bqX55qE56m66ZaT576k6IGa5pWI5oeJ6LyD54K65bGA6YOo77yM5pW06auU56m66ZaT57WQ5qeL6Lao5ZCR5b6u5byx5q2j55u46Zec44CC5Z+65pa85q2k5ZyW77yM5Y+v5o6o5Lyw6YCf6aOf5bqX55qE56m66ZaT5b2x6Z+/56+E5ZyN57SE5Zyo5YmN5LiJ5YCL6Led6Zui6ZqO5bGk5YWn77yI57SEIDEwMDDigJMxNTAwIOWFrOWwuu+8ie+8jOatpOevhOWcjeWFp+eahOepuumWk+WWruWFg+i8g+WPr+iDveWboOmEsOi/kemAn+mjn+W6l+W9semfv+iAjOWRiOePvuWIhuW4g+mbhuiBmuePvuixoeOAgg0KDQpgYGB7cn0NCmdyaWQgPC0gc3RfbWFrZV9ncmlkKFZpbGxfc2YsIGNlbGxzaXplID0gNTAwLCBzcXVhcmUgPSBUUlVFKQ0KZ3JpZF9zZiA8LSBzdF9zZihncmlkX2lkID0gMTpsZW5ndGgoZ3JpZCksIGdlb21ldHJ5ID0gZ3JpZCkNCg0KZmZfY291bnQgPC0gc3Rfam9pbihncmlkX3NmLCBGRl9zZiwgam9pbiA9IHN0X2NvbnRhaW5zKSAlPiUNCiAgZ3JvdXBfYnkoZ3JpZF9pZCkgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIHN0X2FzX3NmKCkNCg0KbmIgPC0gcG9seTJuYihmZl9jb3VudCwgcXVlZW4gPSBUUlVFKQ0KbHcgPC0gbmIybGlzdHcobmIsIHN0eWxlID0gIlciKQ0KDQpjb3JyZWxvZyA8LSBzcC5jb3JyZWxvZ3JhbShuYiwgZmZfY291bnQkY291bnQsIG9yZGVyID0gMTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAiSSIsIHN0eWxlID0gIlciLCB6ZXJvLnBvbGljeSA9IFRSVUUpDQpgYGANCg0KDQpgYGB7cn0NCmNvcnJlbG9nX21hdCA8LSBtYXRyaXgodW5saXN0KGNvcnJlbG9nJHJlcyksIG5jb2wgPSA1LCBieXJvdyA9IFRSVUUpDQpjb3JyZWxvZ19kZiA8LSBhcy5kYXRhLmZyYW1lKGNvcnJlbG9nX21hdCkNCmNvbG5hbWVzKGNvcnJlbG9nX2RmKSA8LSBjKCJsYWciLCAiSSIsICJleHBlY3RlZCIsICJzZCIsICJwLnZhbHVlIikNCg0KcGxvdChjb3JyZWxvZ19kZiRsYWcsIGNvcnJlbG9nX2RmJEksIHR5cGUgPSAiYiIsDQogICAgIHhsYWIgPSAiZGlzdGFuY2UgY2xhc3NlcyIsIHlsYWIgPSAiTW9yYW4gSSBzdGF0aXN0aWMiLA0KICAgICB5bGltID0gYyhtaW4oY29ycmVsb2dfZGYkSSkgLSAwLjA1LCBtYXgoY29ycmVsb2dfZGYkSSkgKyAwLjA1KSkNCg0KYWJsaW5lKGggPSAwLCBsdHkgPSAyKQ0KDQpzaWdfbGFncyA8LSBjb3JyZWxvZ19kZiRwLnZhbHVlIDwgMC4wNQ0KcG9pbnRzKGNvcnJlbG9nX2RmJGxhZ1tzaWdfbGFnc10sIGNvcnJlbG9nX2RmJElbc2lnX2xhZ3NdLA0KICAgICAgIGNvbCA9ICJyZWQiLCBwY2ggPSAxOSkNCg0KYGBgDQojIyMjIyAzLg0KDQpgYGB7cn0NCmdyaWRfc2YkY291bnRfZmYgPC0gZmZfY291bnQkY291bnQNCmdyaWRfc2YkY291bnRfc2Nob29sIDwtIHN0X2pvaW4oZ3JpZF9zZiwgc2Nob29sX3NmLCBqb2luID0gc3RfY29udGFpbnMpICU+JQ0KICBncm91cF9ieShncmlkX2lkKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUNCiAgcHVsbChjb3VudCkNCg0KZ3JpZF9zZiRjb3VudF9zY2hvb2xbaXMubmEoZ3JpZF9zZiRjb3VudF9zY2hvb2wpXSA8LSAwICAjIOabv+S7oyBOQSDngrogMA0KDQpuYiA8LSBwb2x5Mm5iKGdyaWRfc2YsIHF1ZWVuID0gVFJVRSkNCmx3IDwtIG5iMmxpc3R3KG5iLCBzdHlsZSA9ICJXIikNCg0KZ2lfZmYgPC0gbG9jYWxHKGdyaWRfc2YkY291bnRfZmYsIGx3KQ0KZ2lfc2Nob29sIDwtIGxvY2FsRyhncmlkX3NmJGNvdW50X3NjaG9vbCwgbHcpDQoNCnBfZmYgPC0gMiAqIHBub3JtKC1hYnMoZ2lfZmYpKQ0KcF9zY2hvb2wgPC0gMiAqIHBub3JtKC1hYnMoZ2lfc2Nob29sKSkNCg0KcF9mZl9mZHIgPC0gcC5hZGp1c3QocF9mZiwgbWV0aG9kID0gImZkciIpDQpwX3NjaG9vbF9mZHIgPC0gcC5hZGp1c3QocF9zY2hvb2wsIG1ldGhvZCA9ICJmZHIiKQ0KDQpncmlkX3NmJGZmX2hvdHNwb3QgPC0gaWZlbHNlKHBfZmZfZmRyIDwgMC4wNSwgZ2lfZmYsIDApDQpncmlkX3NmJHNjaG9vbF9ob3RzcG90IDwtIGlmZWxzZShwX3NjaG9vbF9mZHIgPCAwLjA1LCBnaV9zY2hvb2wsIDApDQoNCmBgYA0KDQpgYGB7cn0NCnRtX3NoYXBlKGdyaWRfc2YpICsNCiAgdG1fZmlsbCgiZmZfaG90c3BvdCIsIHBhbGV0dGUgPSAiLVJkQnUiLCB0aXRsZSA9ICJGYXN0IGZvb2QgR2kqIiwNCiAgICAgICAgICBzdHlsZSA9ICJjb250IiwgbWlkcG9pbnQgPSBOQSkgKw0KICB0bV9ib3JkZXJzKCkgKw0KICB0bV9sYXlvdXQobWFpbi50aXRsZSA9ICLpgJ/po5/lupfnhrHljYDliIbmnpAiLCBsZWdlbmQub3V0c2lkZSA9IFRSVUUpDQoNCnRtX3NoYXBlKGdyaWRfc2YpICsNCiAgdG1fZmlsbCgic2Nob29sX2hvdHNwb3QiLCBwYWxldHRlID0gIi1SZEJ1IiwgdGl0bGUgPSAiU2Nob29sIEdpKiIsDQogICAgICAgICAgc3R5bGUgPSAiY29udCIsIG1pZHBvaW50ID0gTkEpICsNCiAgdG1fYm9yZGVycygpICsNCiAgdG1fbGF5b3V0KG1haW4udGl0bGUgPSAi5a245qCh54ax5Y2A5YiG5p6QIiwgbGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KDQpgYGANCiMjIyBQYXJ0MiDlrbjmoKHpmYTov5HnmoTpgJ/po5/lupfmlbjph4/nnJ/nmoTmr5TovIPlpJrll44/DQojIyMjIOWvpuS9nOmhjA0KIyMjIyMgMS4gDQo+IOatpOWcluavlOi8g+Wkp+WuieWNgOOAgeaWh+WxseWNgOiIh+S/oee+qeWNgOWtuOagoeiHs+acgOi/kemAn+mjn+W6l+eahOi3nemboue0r+epjeWIhuW4g+aDheW9ou+8jOmhr+ekuuS/oee+qeWNgOeahOWtuOagoeWPr+WPiuaAp+acgOmrmO+8jOe0hOWFq+aIkOWtuOagoeWcqCA1MDAg5YWs5bC65YWn5bCx6IO95oq16YGU6YCf6aOf5bqX77yb5aSn5a6J5Y2A5qyh5LmL77yM57SE5YWt5oiQ5a245qCh5Zyo5ZCM5qij6Led6Zui5YWn77yb6ICM5paH5bGx5Y2A5YmH5Y+v5Y+K5oCn5pyA5L2O77yM5aSa5pW45a245qCh6Led6Zui6YCf6aOf5bqX6LaF6YGOIDUwMCDlhazlsLrjgILmlbTpq5TogIzoqIDvvIzkv6HnvqnljYDlrbjmoKHmmrTpnLLmlrzpgJ/po5/nkrDlooPnmoTnqIvluqbmnIDpq5jvvIzmloflsbHljYDliYfnm7jlsI3ovIPkvY7jgIINCg0KYGBge3J9DQpTSF92aWxsX3NmIDwtIHN0X2pvaW4oc2Nob29sX3NmLCBWaWxsX3NmLCBqb2luID0gc3Rfd2l0aGluLCBsZWZ0ID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQpzY2hvb2xfZGEgPC0gU0hfdmlsbF9zZiAlPiUgZmlsdGVyKFRPV04gPT0gIuWkp+WuieWNgCIpDQpzY2hvb2xfd3MgPC0gU0hfdmlsbF9zZiAlPiUgZmlsdGVyKFRPV04gPT0gIuaWh+WxseWNgCIpDQpzY2hvb2xfeHkgPC0gU0hfdmlsbF9zZiAlPiUgZmlsdGVyKFRPV04gPT0gIuS/oee+qeWNgCIpDQpgYGANCg0KYGBge3J9DQptaW5fZGlzdCA8LSBmdW5jdGlvbihzY2hvb2xfcHRzLCBmYXN0Zm9vZF9wdHMpIHsNCiAgZGlzdHMgPC0gc3RfZGlzdGFuY2Uoc2Nob29sX3B0cywgZmFzdGZvb2RfcHRzKQ0KICBhcHBseShkaXN0cywgMSwgbWluKSAlPiUgYXMubnVtZXJpYygpDQp9DQoNCmRpc3RfZGEgPC0gbWluX2Rpc3Qoc2Nob29sX2RhLCBGRl9zZikNCmRpc3Rfd3MgPC0gbWluX2Rpc3Qoc2Nob29sX3dzLCBGRl9zZikNCmRpc3RfeHkgPC0gbWluX2Rpc3Qoc2Nob29sX3h5LCBGRl9zZikNCmBgYA0KDQpgYGB7cn0NCmRfcmFuZ2UgPC0gc2VxKDAsIDIwMDAsIGJ5ID0gMTApDQoNCmdldF9GZCA8LSBmdW5jdGlvbihkaXN0X3ZlYywgZF9yYW5nZSkgew0KICBzYXBwbHkoZF9yYW5nZSwgZnVuY3Rpb24oZCkgbWVhbihkaXN0X3ZlYyA8PSBkKSkNCn0NCg0KRmRfZGEgPC0gZ2V0X0ZkKGRpc3RfZGEsIGRfcmFuZ2UpDQpGZF93cyA8LSBnZXRfRmQoZGlzdF93cywgZF9yYW5nZSkNCkZkX3h5IDwtIGdldF9GZChkaXN0X3h5LCBkX3JhbmdlKQ0KDQpwbG90KGRfcmFuZ2UsIEZkX2RhLCB0eXBlID0gImwiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDMsDQogICAgIHhsYWIgPSAiRGlzdGFuY2UgKG0pIiwgeWxhYiA9ICJDdW11bGF0aXZlIFByb3BvcnRpb24iLA0KICAgICBtYWluID0gIkYoZCk6IOWtuOagoeWIsOmAn+mjn+W6l+i3nemboue0r+epjeWIhuW4gyIsIHlsaW0gPSBjKDAsIDEpKQ0KDQpsaW5lcyhkX3JhbmdlLCBGZF93cywgY29sID0gImdyZWVuIiwgbHdkID0gMykNCmxpbmVzKGRfcmFuZ2UsIEZkX3h5LCBjb2wgPSAicmVkIiwgbHdkID0gMykNCg0KdGV4dCgyMDAwLCBGZF9kYVtsZW5ndGgoRmRfZGEpXSwgIuWkp+WuieWNgCIsIGNvbCA9ICJibHVlIiwgcG9zID0gNCkNCnRleHQoMjAwMCwgRmRfd3NbbGVuZ3RoKEZkX3dzKV0sICLmloflsbHljYAiLCBjb2wgPSAiZ3JlZW4iLCBwb3MgPSA0KQ0KdGV4dCgyMDAwLCBGZF94eVtsZW5ndGgoRmRfeHkpXSwgIuS/oee+qeWNgCIsIGNvbCA9ICJyZWQiLCBwb3MgPSA0KQ0KDQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gYygi5aSn5a6J5Y2AIiwgIuaWh+WxseWNgCIsICLkv6HnvqnljYAiKSwNCiAgICAgICBjb2wgPSBjKCJibHVlIiwgImdyZWVuIiwgInJlZCIpLCBsdHkgPSAxLCBsd2QgPSAzKQ0KDQpgYGANCg0KIyMjIyMgMi4NCj4g5qC55pOaIFJpcGxleeKAmXMgTCDlh73mlbjliIbmnpDntZDmnpzvvIzpgJ/po5/lupflnKggMzAwMCDlhazlsLrot53pm6LkuIvnmoRM5YC854K6NDM3NC4yNe+8jOaYjumhr+mrmOaWvCBNb250ZSBDYXJsbyDmqKHmk6zmiYDlvpfkuYsgOTUlIOS/oeiztOWNgOmWk++8iOS4iumZkOeCuiAzMjA0LjQ177yJ77yM6aGv56S65YW256m66ZaT5YiG5biD5a2Y5Zyo6aGv6JGX55qE576k6IGa54++6LGh44CC5Lim6aGv56S66YCf6aOf5bqX5Zyo5Y+w5YyX5biC5Y2A5YWn5YK+5ZCR6ZuG5Lit6ZaL56uL5pa854m55a6a5Zyw5Y2A77yM6ICM6Z2e6Zqo5qmf5YiG5biD44CCDQoNCmBgYHtyfQ0KZmZfc3AgPC0gc3RfdHJhbnNmb3JtKEZGX3NmLCAzODI2KQ0Kd2luIDwtIGFzLm93aW4oc3RfYmJveChmZl9zcCkpICANCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhmZl9zcCkNCnBwIDwtIHBwcCh4ID0gY29vcmRzWywxXSwgeSA9IGNvb3Jkc1ssMl0sIHdpbmRvdyA9IHdpbikNCmBgYA0KYGBge3J9DQpLZXN0X3Jlc3VsdCA8LSBLZXN0KHBwLCByID0gc2VxKDAsIDMwMDAsIGJ5ID0gMTAwKSwgY29ycmVjdGlvbiA9ICJSaXBsZXkiKQ0KDQpMZXN0X3Jlc3VsdCA8LSBMZXN0KHBwLCByID0gc2VxKDAsIDMwMDAsIGJ5ID0gMTAwKSwgY29ycmVjdGlvbiA9ICJSaXBsZXkiKQ0KDQplbnZlbG9wZV9yZXN1bHQgPC0gZW52ZWxvcGUocHAsIGZ1biA9IExlc3QsIG5zaW0gPSA5OSwgcmFuayA9IDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29ycmVjdGlvbiA9ICJSaXBsZXkiLCByID0gc2VxKDAsIDMwMDAsIGJ5ID0gMTAwKSkNCg0KYGBgDQpgYGB7cn0NCnBsb3QoZW52ZWxvcGVfcmVzdWx0LCBtYWluID0gIkwocikgd2l0aCA5NSUgQ29uZmlkZW5jZSBJbnRlcnZhbCIsDQogICAgIGxlZ2VuZCA9IEZBTFNFKQ0KYWJsaW5lKGEgPSAwLCBiID0gMSwgbHR5ID0gMiwgY29sID0gImdyYXkiKSANCg0KYGBgDQpgYGB7cn0NCkszMDAwIDwtIEtlc3RfcmVzdWx0JGlzb1t3aGljaChLZXN0X3Jlc3VsdCRyID09IDMwMDApXQ0KTDMwMDAgPC0gTGVzdF9yZXN1bHQkaXNvW3doaWNoKExlc3RfcmVzdWx0JHIgPT0gMzAwMCldDQpMMzAwMF9sbyA8LSBlbnZlbG9wZV9yZXN1bHQkbG9bd2hpY2goZW52ZWxvcGVfcmVzdWx0JHIgPT0gMzAwMCldDQpMMzAwMF9oaSA8LSBlbnZlbG9wZV9yZXN1bHQkaGlbd2hpY2goZW52ZWxvcGVfcmVzdWx0JHIgPT0gMzAwMCldDQoNCmNhdCgiSygzMDAwKe+8miIsIEszMDAwLCAiXG4iKQ0KY2F0KCJMKDMwMDAp77yaIiwgTDMwMDAsICJcbiIpDQpjYXQoIkwoMzAwMCkgOTUlIOS/oeiztOWNgOmWk++8miIsIEwzMDAwX2xvLCAifiIsIEwzMDAwX2hpLCAiXG4iKQ0KDQpgYGANCiMjIyMjIDMuDQo+IEJpdmFyaWF0ZSBGIGZ1bmN0aW9uIOWIhuaekOWFrOeri+iIh+engeeri+WtuOagoeWIsOmAn+mjn+W6l+eahOepuumWk+WPr+WPiuaAp++8jOS4pumAsuS4gOatpee1kOWQiCBDU1LvvIjlrozlhajpmqjmqZ/liIbluIPvvInmqKHmk6zvvIzku6Xpoa/okZfmsLTmupY9MC4wNSDmqqLlrprlhanogIXmmK/lkKbmnInpoa/okZflt67nlbDjgILlvp7nrKzkuIDlvLXlnJbpoa/npLrvvIznp4Hnq4vlrbjmoKHnmoQgRihkKSDntK/nqY3mm7Lnt5rmmI7poa/lv6vmlrzlhaznq4vlrbjmoKHvvIzooajnpLrlhbblnKjovIPnn63ot53pm6LlhafljbPmnInovIPlpJrpgJ/po5/lupflj6/pgZTvvIzlj6/lj4rmgKfovIPpq5jjgILngrrmqqLlrprmraTlt67nlbDmmK/lkKblhbfntbHoqIjmhI/nvqnvvIzliIbliKXlsI3np4Hnq4voiIflhaznq4vlrbjmoKHpgLLooYwgQ1NSIOaooeaTrO+8iOesrOS6jOiIh+esrOS4ieW8teWclu+8ieOAgg0KDQo+IOe1kOaenOmhr+ekuu+8jOengeeri+WtuOagoeeahCBGKGQpIOabsue3muaYjumhr+mrmOaWvCBDU1Ig5qih5pOs55qEIDk1JSDkv6Hos7TljYDplpPkuIrpmZDvvIjlnJbkuozvvInvvIzooajnpLrpgJ/po5/lupflnKjnqbrplpPkuIrmnInpoa/okZfpm4bkuK3mlrznp4Hnq4vlrbjmoKHlkajpgornmoTnj77osaHvvIhwIDwgMC4wNe+8ieOAguebuOWwjeWcsO+8jOWFrOeri+WtuOagoeeahCBGKGQpIOabsue3muWkmuaVuOiQveWcqCBDU1Ig5qih5pOs5Y2A6ZaT5YWn77yI5ZyW5LiJ77yJ77yM6KGo56S65YW26YSw6L+R6YCf6aOf5bqX55qE5YiG5biD6Lao6L+R5pa86Zqo5qmf77yM54Sh6aGv6JGX576k6IGa54m55oCn44CC57ac5LiK5omA6L+w77yM56eB56uL5a245qCh55qE5ZGo6YKK5pOB5pyJ6aGv6JGX6LyD5aSa55qE6YCf6aOf5bqX77yM5Y+v5Y+K5oCn6auY5pa85YWs56uL5a245qCh77yM5LiU6YGU57Wx6KiI6aGv6JGX5rC05rqW55qEMC4wNeOAgg0KDQpgYGB7cn0NCnNjaG9vbF9wdWJsaWMgPC0gU0hfdmlsbF9zZiAlPiUgZmlsdGVyKFR5cGUgPT0gIuWFrOeriyIpDQpzY2hvb2xfcHJpdmF0ZSA8LSBTSF92aWxsX3NmICU+JSBmaWx0ZXIoVHlwZSA9PSAi56eB56uLIikNCmBgYA0KDQpgYGB7cn0NCndpbiA8LSBhcy5vd2luKHN0X2Jib3goc2Nob29sX3NmKSkNCmNvb3Jkc19wdWJsaWMgPC0gc3RfY29vcmRpbmF0ZXMoc3RfdHJhbnNmb3JtKHNjaG9vbF9wdWJsaWMsIDM4MjYpKQ0KY29vcmRzX3ByaXZhdGUgPC0gc3RfY29vcmRpbmF0ZXMoc3RfdHJhbnNmb3JtKHNjaG9vbF9wcml2YXRlLCAzODI2KSkNCmNvb3Jkc19mYXN0Zm9vZCA8LSBzdF9jb29yZGluYXRlcyhzdF90cmFuc2Zvcm0oRkZfc2YsIDM4MjYpKQ0KDQpwcF9wdWJsaWMgPC0gcHBwKGNvb3Jkc19wdWJsaWNbLDFdLCBjb29yZHNfcHVibGljWywyXSwgd2luZG93ID0gd2luKQ0KcHBfcHJpdmF0ZSA8LSBwcHAoY29vcmRzX3ByaXZhdGVbLDFdLCBjb29yZHNfcHJpdmF0ZVssMl0sIHdpbmRvdyA9IHdpbikNCnBwX2Zhc3Rmb29kIDwtIHBwcChjb29yZHNfZmFzdGZvb2RbLDFdLCBjb29yZHNfZmFzdGZvb2RbLDJdLCB3aW5kb3cgPSB3aW4pDQpgYGANCg0KYGBge3J9DQpGX3B1YmxpYyA8LSBGZXN0KHBwX3B1YmxpYywgcHBfZmFzdGZvb2QsIGNvcnJlY3Rpb24gPSAicnMiLCByID0gc2VxKDAsIDIwMDAsIGJ5ID0gMjUpKQ0KRl9wcml2YXRlIDwtIEZlc3QocHBfcHJpdmF0ZSwgcHBfZmFzdGZvb2QsIGNvcnJlY3Rpb24gPSAicnMiLCByID0gc2VxKDAsIDIwMDAsIGJ5ID0gMjUpKQ0KYGBgDQoNCg0KYGBge3J9DQpwbG90KEZfcHVibGljJHIsIEZfcHVibGljJHJzLCB0eXBlID0gImwiLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIsDQogICAgIHhsYWIgPSAi6Led6ZuiIGQgKOWFrOWwuikiLCB5bGFiID0gIkYoZCkiLA0KICAgICBtYWluID0gIuWFrOeriyB2cyDnp4Hnq4vlrbjmoKHliLDpgJ/po5/lupcgRihkKSIsIHlsaW0gPSBjKDAsIDEpKSAgIyDliqDpgJnooYzvvIENCg0KbGluZXMoRl9wcml2YXRlJHIsIEZfcHJpdmF0ZSRycywgY29sID0gInJlZCIsIGx3ZCA9IDIpDQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gYygi5YWs56uL5a245qChIiwgIuengeeri+WtuOagoSIpLA0KICAgICAgIGNvbCA9IGMoImJsdWUiLCAicmVkIiksIGx0eSA9IDEsIGx3ZCA9IDIpDQpgYGANCmBgYHtyfQ0KRl9lbnZfcHJpdmF0ZSA8LSBlbnZlbG9wZShwcF9wcml2YXRlLCBmdW4gPSBGZXN0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzaW11bGF0ZSA9IGV4cHJlc3Npb24ocnVuaWZwb2ludChwcF9mYXN0Zm9vZCRuLCB3aW4gPSB3aW4pKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbnNpbSA9IDk5LCByYW5rID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgY29ycmVjdGlvbiA9ICJycyIsIHIgPSBzZXEoMCwgMjAwMCwgYnkgPSAyNSkpDQoNCkZfZW52X3B1YmxpYyA8LSBlbnZlbG9wZShwcF9wdWJsaWMsIGZ1biA9IEZlc3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgc2ltdWxhdGUgPSBleHByZXNzaW9uKHJ1bmlmcG9pbnQocHBfZmFzdGZvb2Qkbiwgd2luID0gd2luKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgbnNpbSA9IDk5LCByYW5rID0gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjb3JyZWN0aW9uID0gInJzIiwgciA9IHNlcSgwLCAyMDAwLCBieSA9IDI1KSkNCmBgYA0KYGBge3J9DQpwbG90KEZfZW52X3ByaXZhdGUsIG1haW4gPSAi56eB56uL5a245qCh5Yiw6YCf6aOf5bqXIEYoZCkg6IiHIENTUiDmqKHmk6wgOTUlIOS/oeiztOWNgOmWkyIsDQogICAgIHhsYWIgPSAi6Led6ZuiIGQgKOWFrOWwuikiLCB5bGFiID0gIkYoZCkiLCBsZWdlbmQgPSBGQUxTRSkNCg0KbGluZXMoRl9wcml2YXRlJHIsIEZfcHJpdmF0ZSRycywgY29sID0gInJlZCIsIGx3ZCA9IDIpDQoNCg0KbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZCA9IGMoIuengeeri+WtuOagoSIsICJDU1LmqKHmk6zljYDplpMiKSwNCiAgICAgICBjb2wgPSBjKCJyZWQiLCAiZ3JheSIpLCBsdHkgPSBjKDEsMSxOQSksIGx3ZCA9IDIpDQoNCmBgYA0KDQpgYGB7cn0NCnBsb3QoRl9lbnZfcHVibGljLCBtYWluID0gIuWFrOeri+WtuOagoeWIsOmAn+mjn+W6lyBGKGQpIOiIhyBDU1Ig5qih5pOsIDk1JSDkv6Hos7TljYDplpMiLA0KICAgICB4bGFiID0gIui3nemboiBkICjlhazlsLopIiwgeWxhYiA9ICJGKGQpIiwgbGVnZW5kID0gRkFMU0UpDQoNCmxpbmVzKEZfcHVibGljJHIsIEZfcHVibGljJHJzLCBjb2wgPSAiYmx1ZSIsIGx3ZCA9IDIpDQoNCg0KbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZCA9IGMoIuWFrOeri+WtuOagoSIsICJDU1LmqKHmk6zljYDplpMiKSwNCiAgICAgICBjb2wgPSBjKCJibHVlIiwgImdyYXkiKSwgbHR5ID0gYygxLDEsTkEpLCBsd2QgPSAyKQ0KYGBgDQoNCg0KIyMjIFBhcnQzIA0KDQojIyMjIyDlsIcgVmlsbF9zZiDlkIjkvbXngrrjgIzoh7rljJfluILmlbTpq5TpgornlYzjgI0NCmBgYHtyfQ0KVGFpcGVpX2NpdHkgPC0gVmlsbF9zZiAlPiUNCiAgc3VtbWFyaXNlKGdlb21ldHJ5ID0gc3RfdW5pb24oZ2VvbWV0cnkpKSAlPiUNCiAgc3RfbWFrZV92YWxpZCgpIA0KVGFpcGVpX2NpdHkkQ09VTlRZIDwtICLoh7rljJfluIIiDQpgYGANCg0KIyMjIyMgc2Nob29sX3Nm5aKe5YqgQ09VTlRZ44CBVE9XTuOAgVZJTExBR0XmrITkvY0NCmBgYHtyfQ0Kc2Nob29sX2RhdGFfam9pbmVkIDwtIHN0X2pvaW4oc2Nob29sX2RhdGEsIFZpbGxfc2YsIGpvaW4gPSBzdF93aXRoaW4pDQpzY2hvb2xfc2YyIDwtIHNjaG9vbF9kYXRhX2pvaW5lZCAlPiUNCiAgc2VsZWN0KENPVU5UWSxUT1dOLFZJTExBR0UsVHlwZSxOYW1lLFNJRCkNCmBgYA0KDQojIyMjIyDlsIdWaWxsX3Nm5b6e5p2R6YeM5bGk57Sa77yM6K6K5oiQ6KGM5pS/5Y2A5bGk57SaDQpgYGB7cn0NClRPV05fc2YgPC0gVmlsbF9zZiAlPiUNCiAgZ3JvdXBfYnkoVE9XTikgJT4lDQogIHN1bW1hcmlzZShnZW9tZXRyeSA9IHN0X3VuaW9uKGdlb21ldHJ5KSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgc3RfbWFrZV92YWxpZCgpICANClRPV05fc2YkQ09VTlRZIDwtICLoh7rljJfluIIiDQpUT1dOX2xpbmUgPC0gVE9XTl9zZiAlPiUNCiAgc3RfY2FzdCgiTVVMVElMSU5FU1RSSU5HIikNCmBgYA0KDQoNCg0KIyMjIyMgU1RFUCAx77ya6KiI566X5q+P5omA5a245qCh55qE5ZGo6YKK6YCf6aOf5bqX5pW46YePDQpgYGB7cn0NCnNjaG9vbF9idWZmIDwtIHN0X2J1ZmZlcihzY2hvb2xfc2YyLCBkaXN0ID0gNTAwKQ0KDQpzY2hvb2xfZmYgPC0gc3Rfam9pbihzY2hvb2xfYnVmZiwgRkZfc2YsIGpvaW4gPSBzdF9pbnRlcnNlY3RzKSAlPiUNCiAgZ3JvdXBfYnkoTmFtZSkgJT4lDQogIHN1bW1hcmlzZShmZl9jb3VudCA9IG4oKSkgJT4lDQogIHN0X2Ryb3BfZ2VvbWV0cnkoKQ0KDQpzY2hvb2xfZGF0YSA8LSBzY2hvb2xfc2YyICU+JQ0KICBsZWZ0X2pvaW4oc2Nob29sX2ZmLCBieSA9ICJOYW1lIikgJT4lDQogIG11dGF0ZShmZl9jb3VudCA9IHJlcGxhY2VfbmEoZmZfY291bnQsIDApKQ0KYGBgDQoNCg0KIyMjIyMgU1RFUCAy77ya5bu656uL6YSw5o6l55+p6Zmj77yI5Lul5a245qCh54K66bue5L2Nay1uZWFyZXN0IG5laWdoYm9y77yJDQpgYGB7cn0NCmNvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhzdF9jZW50cm9pZChzY2hvb2xfZGF0YSkpDQoNCm5iIDwtIGtubjJuYihrbmVhcm5laWdoKGNvb3JkcywgayA9IDUpKQ0KbHcgPC0gbmIybGlzdHcobmIsIHN0eWxlID0gIlciKQ0KYGBgDQoNCg0KIyMjIyMgU1RFUCAz77ya6KiI566XIExvY2FsIEcqIOWAvO+8iHotc2NvcmXvvIkNCmBgYHtyfQ0KZ2lfc3RhciA8LSBsb2NhbEcoc2Nob29sX2RhdGEkZmZfY291bnQsIGx3KQ0KDQpzY2hvb2xfZGF0YSRHaVogPC0gYXMubnVtZXJpYyhnaV9zdGFyKQ0KYGBgDQoNCg0KYGBge3J9DQpzY2hvb2xfZGF0YSA8LSBzY2hvb2xfZGF0YSAlPiUNCiAgbXV0YXRlKEdpWl9jbGFzcyA9IGNhc2Vfd2hlbigNCiAgICBHaVogPj0gMi41OCAgfiAi5qW16aGv6JGX54ax6bueIiwgICMgcCA8IDAuMDENCiAgICBHaVogPj0gMS45NiAgfiAi6aGv6JGX54ax6bueIiwgICAgIyBwIDwgMC4wNQ0KICAgIEdpWiA8PSAtMi41OCB+ICLmpbXpoa/okZflhrfpu54iLCAgIyBwIDwgMC4wMQ0KICAgIEdpWiA8PSAtMS45NiB+ICLpoa/okZflhrfpu54iLCAgICAjIHAgPCAwLjA1DQogICAgVFJVRSAgICAgICAgIH4gIumdnumhr+iRlyINCiAgKSkNCg0KZ2dwbG90KCkgKw0KICBnZW9tX3NmKGRhdGEgPSBWaWxsX3NmLCBmaWxsID0gImdyZXk5NSIsIGNvbG9yID0gImdyZXk4MCIpICsgICAgDQogIA0KDQogIGdlb21fc2YoZGF0YSA9IHNjaG9vbF9kYXRhLCBhZXMoZmlsbCA9IEdpWl9jbGFzcyksIHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3NmKGRhdGEgPSBUT1dOX2xpbmUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuNSkgKyAgIA0KICB0aGVtZV9taW5pbWFsKCkrDQogIA0KICBnZW9tX3NmKGRhdGEgPSBzY2hvb2xfZGF0YSwgYWVzKGZpbGwgPSBHaVpfY2xhc3MpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyLCBzaGFwZSA9IDIxKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoDQogICAgICAi5qW16aGv6JGX54ax6bueIiA9ICIjODAwMDI2IiwNCiAgICAgICLpoa/okZfnhrHpu54iICAgPSAiI0ZDNEUyQSIsDQogICAgICAi6Z2e6aGv6JGXIiAgICAgPSAid2hpdGUiLA0KICAgICAgIumhr+iRl+WGt+m7niIgICA9ICIjMkM3RkI4IiwNCiAgICAgICLmpbXpoa/okZflhrfpu54iID0gIiMwODQwODEiDQogICAgKSwNCiAgICBuYW1lID0gIkxvY2FsIEcqIOWIhumhniINCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAi6Ie65YyX5biC5ZyL5bCP5ZGo6YKK6YCf6aOf5bqXIExvY2FsIEcqIOeGsem7nuWIhuaekCIsDQogICAgY2FwdGlvbiA9ICLoqLvvvJrliIbpoZ7kvp3nhacgR2laIHotc2NvcmUg6ZaA5qq777yIwrExLjk244CBwrEyLjU477yJIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIpLA0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwNCiAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiDQogICkNCg0KDQpgYGANCg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEuZnJhbWUoR2laID0gR2laX3ZlY3RvciksIGFlcyh4ID0gR2laKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMywgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtMi41OCwgLTEuOTYsIDEuOTYsIDIuNTgpLCANCiAgICAgICAgICAgICBjb2xvciA9ICJkYXJrZ3JheSIsIGxpbmV0eXBlID0gImRvdHRlZCIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKA0KICAgIGJyZWFrcyA9IGMoLTIuNTgsIC0xLjk2LCAwLCAxLjk2LCAyLjU4KSwNCiAgICBsYWJlbHMgPSBjKCItMi41OCIsICItMS45NiIsICIwIiwgIjEuOTYiLCAiMi41OCIpDQogICkgKw0KICBsYWJzKHRpdGxlID0gIuiHuuWMl+W4guWci+WwjyBMb2NhbCBHKiBaLXNjb3JlIOWIhuW4g+WcliIsDQogICAgICAgeCA9ICJHaVog5YC8IiwgeSA9ICLlrbjmoKHmlbjph48iKSArDQogIHRoZW1lX21pbmltYWwoKSsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkgIA0KICApDQpgYGANCg0KDQo=