Part 1:實作題(30分,每題10分)
1. 假設速食店的服務範圍是 1 公里,比較 A 區(文山+大安+中正) 與 B
區(信義+南港+松山)
這兩個地區的每一家速食店在服務可及範圍內,所涵蓋學校數量的平均值,是否有統計顯著差異。(需列出虛無假設與對立假設,統計檢定量,以及檢定的顯著水準等)。H0:μA
和 μB 具有顯著差異;H1:μA 和 μB 不具有顯著差異;α = 0.05
FF_joined <- st_join(food, vill["TOWN"])
TPV_A = dplyr::filter(FF_joined, TOWN %in% c("文山區", "大安區", "中正區"))
TPV_B = dplyr::filter(FF_joined, TOWN %in% c("信義區", "南港區", "松山區"))
buffer_A <- st_buffer(TPV_A, dist = 1000)
buffer_B <- st_buffer(TPV_B, dist = 1000)
count_schools <- function(buffers, schools) {
sapply(1:nrow(buffers), function(i) {
sum(st_intersects(buffers[i, ], schools, sparse = FALSE))
})
}
schools_in_A <- count_schools(buffer_A, school)
schools_in_B <- count_schools(buffer_B, school)
buffer_A$school_count <- schools_in_A
buffer_B$school_count <- schools_in_B
t_test_result <- t.test(schools_in_A, schools_in_B, var.equal = FALSE)
t_test_result
Welch Two Sample t-test
data: schools_in_A and schools_in_B
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 > 0.05,因此拒絕虛無假設(H0),μA 和 μB
不具有顯著差異。
2. 以 500 公尺方格的空間單位建立網格,依照 contiguity
鄰近定義,計算並繪製速食店總數的 Moran’s
correlogram,解讀其圖表資訊,並評估速食店的空間影響範圍。
taipei_boundary <- st_union(vill)
grid_500 <- st_make_grid(taipei_boundary, cellsize = 500, square = TRUE) %>% st_sf() %>%
st_intersection(taipei_boundary)
grid_500$fastfood_count <- lengths(st_intersects(grid_500, food))
grid_sp <- as(grid_500, "Spatial")
nb <- poly2nb(grid_sp, queen = TRUE)
lw <- nb2listw(nb, style = "W")
grid_500$jittered_count <- jitter(grid_500$fastfood_count, amount = 0.1)
moran_corr <- sp.correlogram(nb, grid_500$jittered_count, order = 10, method = "I", style = "W", zero.policy = TRUE)
moran_df <- data.frame(
order = 1:length(moran_corr$res),
moran_I = sapply(moran_corr$res, function(x) x[1])
)
ggplot(moran_df, aes(x = order, y = moran_I)) +
geom_line(color = "blue", size = 1) +
geom_point(size = 3) +
geom_hline(yintercept = 0, linetype = "dashed", color = "gray") +
labs(
title = "Moran's I Correlogram for Fast Food Count (500m Grid)",
x = "Order of Contiguity",
y = "Moran's I"
) +
scale_y_continuous(breaks = seq(-0.001, 0.2, by = 0.01)) +
theme_minimal()

在order ≦ 10,moran’s
I皆大於0。因此,在大約5公里(500m*10)之前的範圍內,速食店呈現群聚分布,具空間自相關現象,而隨著距離越來越遠,moran’s
I 趨近於0,速食店呈現隨機分布,無空間自相關現象。
3. 依照前一小題的網格為空間單位,依照 contiguity 鄰近定義,計算
Gi*統計量,分別繪製速食店家數與學校在 0.05
顯著水準的熱區分佈(需進行多重檢定的修正)。
grid_500m <- st_make_grid(taipei_boundary, cellsize = 500, square = TRUE) %>% st_sf() %>%
st_intersection(taipei_boundary)
grid_500m$grid_id <- 1:nrow(grid_500m)
fastfood_join <- st_join(food, grid_500m, left = FALSE)
fastfood_count <- fastfood_join %>%
group_by(grid_id) %>%
summarise(fastfood_n = n())
school_join <- st_join(school, grid_500m, left = FALSE)
school_count <- school_join %>%
group_by(grid_id) %>%
summarise(school_n = n())
grid_500m <- st_join(grid_500m, fastfood_count, by = "grid_id")
grid_500m <- st_join(grid_500m, school_count, by = "grid_id")
grid_500m$fastfood_n[is.na(grid_500m$fastfood_n)] <- 0
grid_500m$school_n[is.na(grid_500m$school_n)] <- 0
nb <- poly2nb(grid_500m, queen = TRUE)
lw <- nb2listw(nb, style = "W")
gi_fast <- localG(grid_500m$fastfood_n, lw)
gi_school <- localG(grid_500m$school_n, lw)
grid_500m$gi_fast <- as.numeric(gi_fast)
grid_500m$gi_school <- as.numeric(gi_school)
grid_500m$p_fast <- 2 * pnorm(-abs(grid_500m$gi_fast))
grid_500m$p_fast_adj <- p.adjust(grid_500m$p_fast, method = "fdr")
grid_500m$hotspot_fast <- ifelse(grid_500m$p_fast_adj < 0.05 & grid_500m$gi_fast > 0, "Hotspot",
ifelse(grid_500m$p_fast_adj < 0.05 & grid_500m$gi_fast < 0, "Coldspot", "Not Significant"))
grid_500m$p_school <- 2 * pnorm(-abs(grid_500m$gi_school))
grid_500m$p_school_adj <- p.adjust(grid_500m$p_school, method = "fdr")
grid_500m$hotspot_school <- ifelse(grid_500m$p_school_adj < 0.05 & grid_500m$gi_school > 0, "Hotspot",
ifelse(grid_500m$p_school_adj < 0.05 & grid_500m$gi_school < 0, "Coldspot", "Not Significant"))
tmap_mode("plot")
tm_shape(grid_500m) +
tm_polygons(
col = "hotspot_fast",
palette = c("red", "grey"),
legend.show = TRUE,
title = "Fast Food Hotspots (Gi*)"
) +
tm_borders() +
tm_layout(title = "速食店熱區 (Gi*) - FDR 修正", legend.outside = TRUE)

tm_shape(grid_500m) +
tm_polygons(
col = "hotspot_school",
palette = c("blue", "grey"),
legend.show = TRUE,
title = "School Hotspots (Gi*)"
) +
tm_borders() +
tm_layout(title = "學校熱區 (Gi*) - FDR 修正", legend.outside = TRUE)

Part 2:學校附近的速食店數量真的比較多嗎?
1. 請參考Transportation Research Part A, 45 (2011) 640–652(HW-09
的研讀教材)所使
用F(d)函數的作法,比較分別位於大安區、文山區與信義區的學校,到鄰近速食店的空間特徵。
target_vill <- vill %>% filter(TOWN %in% c("大安區", "文山區", "信義區"))
school_sel <- st_join(school, target_vill, join = st_within) %>% filter(!is.na(TOWN))
food_sel <- st_join(food, target_vill, join = st_within) %>% filter(!is.na(TOWN))
sf_to_ppp <- function(points_sf, window_polygon) {
win <- as.owin(st_union(window_polygon))
coords <- st_coordinates(points_sf)
ppp_obj <- ppp(x = coords[,1], y = coords[,2], window = win)
return(ppp_obj)
}
districts <- c("大安區", "文山區", "信義區")
fd_results <- list()
for (dist in districts) {
dist_vill <- target_vill %>% filter(TOWN == dist)
dist_school <- school_sel %>% filter(TOWN == dist)
dist_food <- food_sel %>% filter(TOWN == dist)
food_ppp <- sf_to_ppp(dist_food, dist_vill)
school_coords <- st_coordinates(dist_school)
marks <- rep(1, nrow(school_coords))
school_ppp <- ppp(school_coords[,1], school_coords[,2], window = food_ppp$window, marks = marks)
fd <- Fest(food_ppp, correction = "border")
fd_results[[dist]] <- fd
}
plot(fd_results[["大安區"]], main = "F(d) function(大安,信義,文山)", col = "red")
plot(fd_results[["文山區"]], add = TRUE, col = "blue")
plot(fd_results[["信義區"]], add = TRUE, col = "green")
legend("bottomright", legend = districts, col = c("red", "blue", "green"), lty = 1)

par(mfrow = c(1, 3)) # 三區並排
for (dist in districts) {
dist_vill <- target_vill %>% filter(TOWN == dist)
dist_school <- school_sel %>% filter(TOWN == dist)
dist_food <- food_sel %>% filter(TOWN == dist)
win <- as.owin(st_union(dist_vill))
school_coords <- st_coordinates(dist_school)
food_coords <- st_coordinates(dist_food)
school_ppp <- ppp(school_coords[,1], school_coords[,2], window = win)
food_ppp <- ppp(food_coords[,1], food_coords[,2], window = win)
plot(school_ppp, main = paste(dist, "學校 vs 速食店"), cols = "blue", pch = 3)
points(food_ppp, col = "red", pch = 16)
legend("topright", legend = c("學校", "速食店"), col = c("blue", "red"), pch = c(3, 16))
}

districts <- c("大安區", "信義區", "文山區")
for (dist in districts) {
dist_school <- school_sel %>% filter(TOWN == dist)
dist_food <- food_sel %>% filter(TOWN == dist)
distances <- st_distance(dist_school, dist_food) %>%
apply(1, min) %>%
as.numeric()
d_values <- seq(0, 500, by = 10)
F_empirical <- sapply(d_values, function(d) mean(distances <= d))
n_random_points <- 500
n_samples <- nrow(dist_school)
n_sim <- 99
bbox <- st_bbox(dist_school)
random_points <- st_as_sf(
data.frame(
x = runif(n_random_points, bbox["xmin"], bbox["xmax"]),
y = runif(n_random_points, bbox["ymin"], bbox["ymax"])
),
coords = c("x", "y"),
crs = st_crs(dist_school)
)
dist_food_proj <- st_transform(dist_food, 32618)
random_points_proj <- st_transform(random_points, 32618)
sim_results <- matrix(nrow = length(d_values), ncol = n_sim)
for (i in 1:n_sim) {
sampled_points <- random_points_proj %>%
sample_n(n_samples)
sim_distances <- st_distance(sampled_points, dist_food_proj) %>%
apply(1, min) %>%
as.numeric()
sim_results[, i] <- sapply(d_values, function(d) mean(sim_distances <= d))
}
envelope_lower <- apply(sim_results, 1, min)
envelope_upper <- apply(sim_results, 1, max)
plot_data <- data.frame(
d = d_values,
Empirical = F_empirical,
Lower = envelope_lower,
Upper = envelope_upper
)
p <- ggplot(plot_data, aes(x = d)) +
geom_ribbon(aes(ymin = Lower, ymax = Upper, fill = "Simulation Envelope"),
alpha = 0.7) +
geom_line(aes(y = Empirical, color = "Empirical F(d)"), linewidth = 1) +
scale_fill_manual(name = NULL, values = c("Simulation Envelope" = "grey80")) +
scale_color_manual(name = NULL, values = c("Empirical F(d)" = "#D55E00")) +
labs(
x = "距離 (公尺)",
y = "F(d)",
title = paste0("F(d): ", dist, " 學校到速食店的可近性分析")
) +
theme_minimal() +
theme(
panel.grid.minor = element_blank(),
plot.title = element_text(hjust = 0.5, size = 14),
legend.position = "bottom",
legend.text = element_text(size = 10)
)
print(p)
}



雖然從confidence
envelope來看,三區皆呈現隨機分布,但從f-function的曲線可以看到大安區學校與速食店較偏向cluster分布,信義區學校與速食店較偏向random分布,文山區學校與速食店較偏向dispersion分布,顯現出大安區附近有較多速食店,反映三區在學校與速食店實際距離關係上的結構性差異,這些差異可能來自地理面積、建成環境密度等因素
2. 利用 Ripley’s K function, k(d)計算速食店在 distance (d) = 3000
公尺時的 K(3000) 以及 L(3000),並使用 Monte Carlo significance test 計算
L(3000)的 95%信賴區間
food_proj <- st_transform(food, 3826)
vill_proj <- st_transform(vill, 3826)
taipei_win <- as.owin(st_union(vill_proj))
coords <- st_coordinates(food_proj)
food_ppp <- ppp(x = coords[,1], y = coords[,2], window = taipei_win)
K_result <- Kest(food_ppp, correction = "border")
K3000 <- K_result$border[which.min(abs(K_result$r - 3000))]
L3000 <- sqrt(K3000 / pi) - 3000
cat("K(3000) =", K3000, "\n")
K(3000) = 67640147
cat("L(3000) =", L3000, "\n")
L(3000) = 1640.1
taipei_win <- as.owin(st_union(vill))
set.seed(123)
env <- envelope(
food_ppp,
fun = Kest,
nsim = 99,
nrank = 5,
correction = "border",
savefuns = TRUE
)
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.
env_L <- eval.fv(sqrt(env / pi)-3000)
L3000_lo <- env_L$lo[which.min(abs(env_L$r - 3000))]
L3000_hi <- env_L$hi[which.min(abs(env_L$r - 3000))]
cat("L(3000) 95% 信賴區間:", L3000_lo, "~", L3000_hi, "\n")
L(3000) 95% 信賴區間: -306.9529 ~ 282.8214
plot(env_L, main = "L(d) Confidence Envelope")
abline(v = 3000, col = "blue", lty = 2)

從R-consule的計算結果可以看到L(3000)=1640.1遠大於信賴區間上限282.821,因此顯示距離3000公尺的尺度下,台北速食店分布呈現顯著「cluster」分布
從ci圖可以看到黑線(觀測值)整體高於envelope,印證了在3000公尺尺度下,台北速食店分布顯示顯著的cluster分布
3. 利用Bivariate F
function分析方法,比較公立vs.私立學校,到鄰近速食店的空間特徵,並說明哪一種類型學校的附近,會有較多的速食店?(顯著水準=0.05)
pub = school[school$Type == "公立", ]
pri = school[school$Type == "私立", ]
coord_pub = st_coordinates(pub)
coord_pri = st_coordinates(pri)
coord_ff = st_coordinates(food)
windows = as.owin(st_union(vill))
pub_pp = as.ppp(coord_pub, windows)
pri_pp = as.ppp(coord_pri, windows)
ff_pp = as.ppp(coord_ff, windows)
NND_pub = nncross(pub_pp, ff_pp)
Fd_pub = ecdf(NND_pub$dist)
NND_pri = nncross(pri_pp, ff_pp)
Fd_pri = ecdf(NND_pri$dist)
n_pub = pub_pp$n
n_pri = pri_pp$n
F_sim_pub = matrix(nrow=99, ncol=n_pub)
for(i in 1:99){
RND = rpoint(n_pub, win = windows)
F_sim_pub[i, ] = nncross(RND, ff_pp)$dist
}
F_sim_s_pub = t(apply(F_sim_pub,1,sort))
F_sim_sort_pub = apply(F_sim_s_pub, 2, sort)
F_sim_pri = matrix(nrow=99, ncol=n_pri)
for(i in 1:99){
RND = rpoint(n_pri, win = windows)
F_sim_pri[i, ] = nncross(RND, ff_pp)$dist
}
F_sim_s_pri = t(apply(F_sim_pri,1,sort))
F_sim_sort_pri = apply(F_sim_s_pri, 2, sort)
plot(Fd_pub, col="blue", cex=0, main="F function: 公立學校 → 速食店", xlab="距離", ylab="F(d)", xlim=c(0,4000))
for(i in 1:99){
lines(ecdf(F_sim_pub[i,]), col="grey", verticals=T, cex=0)
}
lines(ecdf(F_sim_sort_pub[5,]), col="black", lty=2, lwd=1.5)
lines(ecdf(F_sim_sort_pub[95,]), col="black", lty=2, lwd=1.5)
lines(Fd_pub, col="blue", lwd=3)

plot(Fd_pri, col="blue", cex=0, main="F function: 私立學校 → 速食店", xlab="距離", ylab="F(d)", xlim=c(0,4000))
for(i in 1:99){
lines(ecdf(F_sim_pri[i,]), col="grey", verticals=T, cex=0)
}
lines(ecdf(F_sim_sort_pri[5,]), col="black", lty=2, lwd=1.5)
lines(ecdf(F_sim_sort_pri[95,]), col="black", lty=2, lwd=1.5)
lines(Fd_pri, col="blue", lwd=3)

plot(Fd_pub, col="blue", main="公立 vs. 私立學校 → 速食店", xlab="距離", ylab="F(d)", xlim=c(0,4000))
lines(Fd_pub, col="blue", lwd=3)
lines(Fd_pri, col="red", lwd=3)
legend("bottomright", legend = c("公立學校", "私立學校"), col = c("blue", "red"), lwd = 3)

從前兩張圖可以看到,在顯著水準0.05的情況下,台北市公私立學校附近的速食店皆呈現cluster分布,也就是兩種類型學校附近皆圍繞著速食店,而從第三張圖可以看到公立學校f-function的上升速度較快(較cluster),換句話說,也就是公立學校附近的速食店較多
LS0tDQp0aXRsZTogImZpbmFsIGV4YW0iDQphdXRob3I6ICJncm91cCAxNiINCmRhdGU6ICIyMDI1LzA1LzI2Ig0Kb3V0cHV0OiANCiAgaHRtbF9ub3RlYm9vaw0KDQotLS0NCmBgYHtyIHNldHVwLGluY2x1ZGU9RkFMU0UsIHJlc3VsdHM9J2hpZGUnfQ0Kcm0obGlzdCA9IGxzKCkpDQoNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHRtYXApDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh1bml0cykNCmxpYnJhcnkocGFscykNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocmVzaGFwZTIpDQpsaWJyYXJ5KGFzcGFjZSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzcGF0c3RhdC5nZW9tKQ0KbGlicmFyeShzcGF0c3RhdC5leHBsb3JlKQ0KbGlicmFyeShwdXJycikNCmxpYnJhcnkoc3BkZXApDQpsaWJyYXJ5KGNhcnRvZ3JhcGh5KQ0KDQoNCnNjaG9vbCA9IHN0X3JlYWQoIkQ6L+Wkp+S6jOS4iy/nqbrplpPliIbmnpAv5ZyW6LOHL1NDSE9PTC5zaHAiLCBvcHRpb25zID0gIkVOQ09ESU5HPUJJRzUiLHF1aWV0PVQpDQp2aWxsID0gc3RfcmVhZCgiRDov5aSn5LqM5LiLL+epuumWk+WIhuaekC/lnJbos4cvVGFpcGVpX1ZpbGwuc2hwIiwgb3B0aW9ucyA9ICJFTkNPRElORz1CSUc1IikNCmZvb2QgPSBzdF9yZWFkKCJEOi/lpKfkuozkuIsv56m66ZaT5YiG5p6QL+Wcluizhy9UcGVfRmFzdGZvb2Quc2hwIiwgb3B0aW9ucyA9ICJFTkNPRElORz1CSUc1IikNCg0KYGBgDQojIyMgUGFydCAxOiDkuI3lkIzooYzmlL/ljYDnmoTpgJ/po5/lupfmnInkuI3lkIznmoTlnLDnkIbliIbluIPnibnlvrXll47vvJ8o5ZWP562U6aGMKQ0KDQojIyMjICgxKSDlnJYgMeaYr+afkOWfjuW4guWQhOadkemHjOmAn+mjn+W6l+WvhuW6pueahE1vcmFuIFNjYXR0ZXIgUGxvdOOAguewoei/sOipsuWcluihqOeahCBY6Lu46IiHIFnou7jmiYDooajnpLrnmoTmhI/mtrXvvIzku6Xlj4rlhbbnqbrplpPlnovmhYvjgILvvIg05YiG77yJDQojIyMjIE1vcmFuIFNjYXR0ZXIgUGxvdCDnlKjmlrzliIbmnpDnqbrplpPoh6rnm7jpl5zmgKfjgIJYIOi7uO+8iFN0YW5kYXJkaXplZCBWYXJpYWJsZe+8iSDooajnpLrnmoTmmK/mr4/lgIvnqbrplpPllq7lhYPvvIjlnKjmraTngrrmnZHph4zvvInnmoTpgJ/po5/lupflr4bluqbljp/lp4vop4DmuKzlgLzvvIhWYXJpYWJsZSBY77yJ44CCWSDou7jvvIhTdGFuZGFyZGl6ZWQgTGFnZ2VkIFZhcmlhYmxl77yJIOWJh+ihqOekuuavj+WAi+adkemHjOmEsOi/keWNgOWfn++8iOepuumWk+mEsOi/keWWruS9je+8ieeahOaomea6luWMluW5s+Wdh+mAn+mjn+W6l+WvhuW6pu+8jOWNs+epuumWk+S4iueahOmEsOi/keWAvOOAguagueaTmuWcliAxIOeahOaVo+m7nuWIhuW4g+WSjOi2qOWLoue3mu+8jOWFtuepuumWk+Wei+aFi+mhr+ekuuWHuumhr+iRl+eahOato+WQkeiHquebuOmXnCAoUG9zaXRpdmVBdXRvY29ycmVsYXRpb24p44CC6YCZ5oSP5ZGz6JGX6YCf6aOf5bqX5a+G5bqm6auY55qE5p2R6YeM5b6A5b6A6IGa6ZuG5Zyo5YW25LuW6YCf6aOf5bqX5a+G5bqm6auY55qE5p2R6YeM6ZmE6L+RIChIaWdoLUhpZ2gg6IGa6ZuGKe+8jOiAjOmAn+mjn+W6l+WvhuW6puS9jueahOadkemHjOS5n+WCvuWQkeaWvOiBmumbhuWcqOWFtuS7lumAn+mjn+W6l+WvhuW6puS9jueahOadkemHjOmZhOi/kSAoTG93LUxvdyDogZrpm4Yp44CCDQoNCiMjIyMgKDIpIOiri+ewoei/sOipsuWclui2qOWLoue3mihGaXQp55qE5pac546H5YC8IChzbG9wZSA9MC45ODIp77yM5omA5Luj6KGo55qE5oSP5ra144CC77yIM+WIhu+8iQ0KIyMjIyDlnKggTW9yYW4gU2NhdHRlciBQbG90IOS4re+8jOi2qOWLoue3mu+8iEZpdCBsaW5l77yJ55qE5pac546H5a+m6Zqb5LiK5bCx5pivIEdsb2JhbCBNb3JhbuKAmXMgSSDntbHoqIjph48g55qE5YC844CC6Kmy5ZyW55qE6Lao5Yui57ea5pac546H54K6IDAuOTgy77yM6Z2e5bi45o6l6L+RIDHvvIzpoa/npLrpgJ/po5/lupflr4bluqbnmoTnqbrplpPliIbluIPlhbfmnInmraPlkJHoh6rnm7jpl5zmgKfjgILpgJnooajnpLrvvJrigKIg6auY6YCf6aOf5bqX5a+G5bqm55qE5p2R6YeM77yM5YW25ZGo5ZyN6YSw6L+R5Y2A5Z+f5Lmf5YK+5ZCR5pa85pOB5pyJ6auY5a+G5bqm77yb4oCiIOS9juWvhuW6puWNgOWfn+WJh+Wkp+WkmuiIh+WFtuS7luS9juWvhuW6puWNgOWfn+ebuOmEsOOAguWboOatpO+8jOmAmeeoruWIhuW4g+Wei+aFi+WRiOePvuWHuuaYjumhr+eahOiBmumbhu+8iGNsdXN0ZXJpbmfvvInnj77osaHvvIzljbPpoZ7kvLznmoTlgLzvvIjkuI3oq5bpq5jmiJbkvY7vvInmnIPlvbzmraTogZrpm4bvvIzogIzkuI3mmK/pmqjmqZ/miJbliIbmlaPliIbluIPjgIINCg0KIyMjIyAoMykg6Zec5pa86Kmy5Z+O5biC5ZCE5p2R6YeM6YCf6aOf5bqX5a+G5bqm55qEIExvY2FsIE1vcmFu4oCZcyBJ57Wx6KiI6YeP77yM6KuL5ZWP5YW257Wx6KiI6YeP5Y+v6IO95pyD5aSn5pa8IDDjgIHnrYnmlrwgMOaIluWwj+aWvCAw77yM5Lim6Kqq5piO5Yik5pa355qE55CG55Sx44CC77yIM+WIhu+8jOeQhueUseato+eiuuaJjeacg+e1puWIhu+8iQ0KIyMjIyDmoLnmk5rlnJYgMSDmiYDnpLrvvIxHbG9iYWwgTW9yYW7igJlzIEkg5YC854K6IDAuOTgy77yM6KGo5piO5pW06auU5LiK5a2Y5Zyo5by354OI55qE5q2j5ZCR6Ieq55u46Zec5oiW6IGa6ZuG44CC5ZyoIE1vcmFuIFNjYXR0ZXIgUGxvdCDkuK3vvIzpgJnooajnpLrlpKflpJrmlbjmlaPpu57okL3lnKggSC1IICjlj7PkuIopIOWSjCBMLUwgKOW3puS4iykg5YWp5YCL6LGh6ZmQ44CCSC1IIOWSjCBMLUwg5Y2A5Z+f6YO95Luj6KGo5LqG55u45Ly85YC855qE6IGa6ZuG44CCIOWboOatpO+8jOWwjeaWvOipsuWfjuW4guWQhOadkemHjOmAn+mjn+W6l+WvhuW6pueahCBMb2NhbCBNb3JhbuKAmXMgSSDntbHoqIjph4/vvIzlpKflpJrmlbjmnZHph4znmoTntbHoqIjph4/lj6/og73mnIPlpKfmlrwgMOOAgumAmeaYr+WboOeCuiBHbG9iYWwgTW9yYW7igJlzIEkg55qE5by354OI5q2j5ZCR5YC86KGo56S66LOH5paZ5Li76KaB55SxIEgtSCDlkowgTC1MIOmAmeWFqemhnuebuOS8vOWAvOiBmumbhueahOWNgOWfn+aJgOS4u+Wwju+8jOiAjCBMb2NhbCBNb3JhbuKAmXMgSSDmnIPlm6DngrrpgJnkupvogZrpm4bljYDln5/oqIjnrpflh7rovIPlpKfnmoTmraPlgLzjgILlnJbkuK3poa/npLrmmI7poa/nmoTlkIzos6rmgKfogZrpm4bnj77osaHvvIjmraPlkJHoh6rnm7jpl5zvvInvvIzlpJrmlbjmnZHph4zoiIfphLDov5HlnLDljYDnmoTpgJ/po5/lupflr4bluqbotqjli6LkuIDoh7TjgIINCg0KIyMjIFBhcnQgMTrlr6bkvZzpoYzvvIgzMOWIhu+8jOavj+mhjDEw5YiG77yJIA0KDQojIyMjIDEuIOWBh+ioremAn+mjn+W6l+eahOacjeWLmeevhOWcjeaYryAxIOWFrOmHjO+8jOavlOi8gyBBIOWNgCjmloflsbEr5aSn5a6JK+S4reatoykg6IiHIEIg5Y2AKOS/oee+qSvljZfmuK8r5p2+5bGxKSDpgJnlhanlgIvlnLDljYDnmoTmr4/kuIDlrrbpgJ/po5/lupflnKjmnI3li5nlj6/lj4rnr4TlnI3lhafvvIzmiYDmtrXok4vlrbjmoKHmlbjph4/nmoTlubPlnYflgLzvvIzmmK/lkKbmnInntbHoqIjpoa/okZflt67nlbDjgIIo6ZyA5YiX5Ye66Jmb54Sh5YGH6Kit6IiH5bCN56uL5YGH6Kit77yM57Wx6KiI5qqi5a6a6YeP77yM5Lul5Y+K5qqi5a6a55qE6aGv6JGX5rC05rqW562JKeOAgkgw77yazrxBIOWSjCDOvEIg5YW35pyJ6aGv6JGX5beu55Ww77ybSDHvvJrOvEEg5ZKMIM68QiDkuI3lhbfmnInpoa/okZflt67nlbDvvJvOsSA9IDAuMDUNCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCkZGX2pvaW5lZCA8LSBzdF9qb2luKGZvb2QsIHZpbGxbIlRPV04iXSkNClRQVl9BID0gZHBseXI6OmZpbHRlcihGRl9qb2luZWQsIFRPV04gJWluJSBjKCLmloflsbHljYAiLCAi5aSn5a6J5Y2AIiwgIuS4reato+WNgCIpKQ0KVFBWX0IgPSBkcGx5cjo6ZmlsdGVyKEZGX2pvaW5lZCwgVE9XTiAlaW4lIGMoIuS/oee+qeWNgCIsICLljZfmuK/ljYAiLCAi5p2+5bGx5Y2AIikpDQpidWZmZXJfQSA8LSBzdF9idWZmZXIoVFBWX0EsIGRpc3QgPSAxMDAwKQ0KYnVmZmVyX0IgPC0gc3RfYnVmZmVyKFRQVl9CLCBkaXN0ID0gMTAwMCkNCg0KY291bnRfc2Nob29scyA8LSBmdW5jdGlvbihidWZmZXJzLCBzY2hvb2xzKSB7DQogIHNhcHBseSgxOm5yb3coYnVmZmVycyksIGZ1bmN0aW9uKGkpIHsNCiAgICBzdW0oc3RfaW50ZXJzZWN0cyhidWZmZXJzW2ksIF0sIHNjaG9vbHMsIHNwYXJzZSA9IEZBTFNFKSkNCiAgfSkNCn0NCg0Kc2Nob29sc19pbl9BIDwtIGNvdW50X3NjaG9vbHMoYnVmZmVyX0EsIHNjaG9vbCkNCnNjaG9vbHNfaW5fQiA8LSBjb3VudF9zY2hvb2xzKGJ1ZmZlcl9CLCBzY2hvb2wpDQoNCmJ1ZmZlcl9BJHNjaG9vbF9jb3VudCA8LSBzY2hvb2xzX2luX0ENCmJ1ZmZlcl9CJHNjaG9vbF9jb3VudCA8LSBzY2hvb2xzX2luX0INCnRfdGVzdF9yZXN1bHQgPC0gdC50ZXN0KHNjaG9vbHNfaW5fQSwgc2Nob29sc19pbl9CLCB2YXIuZXF1YWwgPSBGQUxTRSkNCnRfdGVzdF9yZXN1bHQNCmBgYA0KIyMjIyBwLXZhbHVlID0gMC4xMTYzID4gMC4wNe+8jOWboOatpOaLkue1leiZm+eEoeWBh+iorShIMCnvvIzOvEEg5ZKMIM68QiDkuI3lhbfmnInpoa/okZflt67nlbDjgIINCg0KIyMjIyAyLiDku6UgNTAwIOWFrOWwuuaWueagvOeahOepuumWk+WWruS9jeW7uueri+e2suagvO+8jOS+neeFpyBjb250aWd1aXR5IOmEsOi/keWumue+qe+8jOioiOeul+S4pue5quijvemAn+mjn+W6l+e4veaVuOeahCBNb3JhbuKAmXMgY29ycmVsb2dyYW3vvIzop6PoroDlhbblnJbooajos4foqIrvvIzkuKboqZXkvLDpgJ/po5/lupfnmoTnqbrplpPlvbHpn7/nr4TlnI3jgIINCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnRhaXBlaV9ib3VuZGFyeSA8LSBzdF91bmlvbih2aWxsKQ0KDQpncmlkXzUwMCA8LSBzdF9tYWtlX2dyaWQodGFpcGVpX2JvdW5kYXJ5LCBjZWxsc2l6ZSA9IDUwMCwgc3F1YXJlID0gVFJVRSkgJT4lIHN0X3NmKCkgJT4lDQogIHN0X2ludGVyc2VjdGlvbih0YWlwZWlfYm91bmRhcnkpDQpncmlkXzUwMCRmYXN0Zm9vZF9jb3VudCA8LSBsZW5ndGhzKHN0X2ludGVyc2VjdHMoZ3JpZF81MDAsIGZvb2QpKQ0KDQpncmlkX3NwIDwtIGFzKGdyaWRfNTAwLCAiU3BhdGlhbCIpDQoNCm5iIDwtIHBvbHkybmIoZ3JpZF9zcCwgcXVlZW4gPSBUUlVFKQ0KDQpsdyA8LSBuYjJsaXN0dyhuYiwgc3R5bGUgPSAiVyIpDQpncmlkXzUwMCRqaXR0ZXJlZF9jb3VudCA8LSBqaXR0ZXIoZ3JpZF81MDAkZmFzdGZvb2RfY291bnQsIGFtb3VudCA9IDAuMSkNCm1vcmFuX2NvcnIgPC0gc3AuY29ycmVsb2dyYW0obmIsIGdyaWRfNTAwJGppdHRlcmVkX2NvdW50LCBvcmRlciA9IDEwLCBtZXRob2QgPSAiSSIsIHN0eWxlID0gIlciLCB6ZXJvLnBvbGljeSA9IFRSVUUpDQoNCm1vcmFuX2RmIDwtIGRhdGEuZnJhbWUoDQogIG9yZGVyID0gMTpsZW5ndGgobW9yYW5fY29yciRyZXMpLA0KICBtb3Jhbl9JID0gc2FwcGx5KG1vcmFuX2NvcnIkcmVzLCBmdW5jdGlvbih4KSB4WzFdKQ0KKQ0KZ2dwbG90KG1vcmFuX2RmLCBhZXMoeCA9IG9yZGVyLCB5ID0gbW9yYW5fSSkpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gImJsdWUiLCBzaXplID0gMSkgKw0KICBnZW9tX3BvaW50KHNpemUgPSAzKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImdyYXkiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiTW9yYW4ncyBJIENvcnJlbG9ncmFtIGZvciBGYXN0IEZvb2QgQ291bnQgKDUwMG0gR3JpZCkiLA0KICAgIHggPSAiT3JkZXIgb2YgQ29udGlndWl0eSIsDQogICAgeSA9ICJNb3JhbidzIEkiDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKC0wLjAwMSwgMC4yLCBieSA9IDAuMDEpKSArIA0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyMjIOWcqG9yZGVyIOKJpiAxMO+8jG1vcmFu4oCZcyBJ55qG5aSn5pa8MOOAguWboOatpO+8jOWcqOWkp+e0hDXlhazph4woNTAwbSoxMCnkuYvliY3nmoTnr4TlnI3lhafvvIzpgJ/po5/lupflkYjnj77nvqTogZrliIbluIPvvIzlhbfnqbrplpPoh6rnm7jpl5znj77osaHvvIzogIzpmqjokZfot53pm6LotorkvobotorpgaDvvIxtb3JhbuKAmXMgSSDotqjov5Hmlrww77yM6YCf6aOf5bqX5ZGI54++6Zqo5qmf5YiG5biD77yM54Sh56m66ZaT6Ieq55u46Zec54++6LGh44CCDQoNCiMjIyMgMy4g5L6d54Wn5YmN5LiA5bCP6aGM55qE57ay5qC854K656m66ZaT5Zau5L2N77yM5L6d54WnIGNvbnRpZ3VpdHkg6YSw6L+R5a6a576p77yM6KiI566XIEdpKue1seioiOmHj++8jOWIhuWIpee5quijvemAn+mjn+W6l+WutuaVuOiIh+WtuOagoeWcqCAwLjA1IOmhr+iRl+awtOa6lueahOeGseWNgOWIhuS9iO+8iOmcgOmAsuihjOWkmumHjeaqouWumueahOS/ruato++8ieOAgg0KYGBge3IgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KZ3JpZF81MDBtIDwtIHN0X21ha2VfZ3JpZCh0YWlwZWlfYm91bmRhcnksIGNlbGxzaXplID0gNTAwLCBzcXVhcmUgPSBUUlVFKSAlPiUgc3Rfc2YoKSAlPiUgDQogIHN0X2ludGVyc2VjdGlvbih0YWlwZWlfYm91bmRhcnkpDQpncmlkXzUwMG0kZ3JpZF9pZCA8LSAxOm5yb3coZ3JpZF81MDBtKQ0KDQpmYXN0Zm9vZF9qb2luIDwtIHN0X2pvaW4oZm9vZCwgZ3JpZF81MDBtLCBsZWZ0ID0gRkFMU0UpDQpmYXN0Zm9vZF9jb3VudCA8LSBmYXN0Zm9vZF9qb2luICU+JQ0KICBncm91cF9ieShncmlkX2lkKSAlPiUNCiAgc3VtbWFyaXNlKGZhc3Rmb29kX24gPSBuKCkpDQoNCnNjaG9vbF9qb2luIDwtIHN0X2pvaW4oc2Nob29sLCBncmlkXzUwMG0sIGxlZnQgPSBGQUxTRSkNCnNjaG9vbF9jb3VudCA8LSBzY2hvb2xfam9pbiAlPiUNCiAgZ3JvdXBfYnkoZ3JpZF9pZCkgJT4lDQogIHN1bW1hcmlzZShzY2hvb2xfbiA9IG4oKSkNCg0KZ3JpZF81MDBtIDwtIHN0X2pvaW4oZ3JpZF81MDBtLCBmYXN0Zm9vZF9jb3VudCwgYnkgPSAiZ3JpZF9pZCIpDQpncmlkXzUwMG0gPC0gc3Rfam9pbihncmlkXzUwMG0sIHNjaG9vbF9jb3VudCwgYnkgPSAiZ3JpZF9pZCIpDQpncmlkXzUwMG0kZmFzdGZvb2Rfbltpcy5uYShncmlkXzUwMG0kZmFzdGZvb2RfbildIDwtIDANCmdyaWRfNTAwbSRzY2hvb2xfbltpcy5uYShncmlkXzUwMG0kc2Nob29sX24pXSA8LSAwDQpuYiA8LSBwb2x5Mm5iKGdyaWRfNTAwbSwgcXVlZW4gPSBUUlVFKQ0KbHcgPC0gbmIybGlzdHcobmIsIHN0eWxlID0gIlciKQ0KZ2lfZmFzdCA8LSBsb2NhbEcoZ3JpZF81MDBtJGZhc3Rmb29kX24sIGx3KQ0KZ2lfc2Nob29sIDwtIGxvY2FsRyhncmlkXzUwMG0kc2Nob29sX24sIGx3KQ0KDQpncmlkXzUwMG0kZ2lfZmFzdCA8LSBhcy5udW1lcmljKGdpX2Zhc3QpDQpncmlkXzUwMG0kZ2lfc2Nob29sIDwtIGFzLm51bWVyaWMoZ2lfc2Nob29sKQ0KDQpncmlkXzUwMG0kcF9mYXN0IDwtIDIgKiBwbm9ybSgtYWJzKGdyaWRfNTAwbSRnaV9mYXN0KSkNCmdyaWRfNTAwbSRwX2Zhc3RfYWRqIDwtIHAuYWRqdXN0KGdyaWRfNTAwbSRwX2Zhc3QsIG1ldGhvZCA9ICJmZHIiKQ0KZ3JpZF81MDBtJGhvdHNwb3RfZmFzdCA8LSBpZmVsc2UoZ3JpZF81MDBtJHBfZmFzdF9hZGogPCAwLjA1ICYgZ3JpZF81MDBtJGdpX2Zhc3QgPiAwLCAiSG90c3BvdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JpZF81MDBtJHBfZmFzdF9hZGogPCAwLjA1ICYgZ3JpZF81MDBtJGdpX2Zhc3QgPCAwLCAiQ29sZHNwb3QiLCAiTm90IFNpZ25pZmljYW50IikpDQoNCmdyaWRfNTAwbSRwX3NjaG9vbCA8LSAyICogcG5vcm0oLWFicyhncmlkXzUwMG0kZ2lfc2Nob29sKSkNCmdyaWRfNTAwbSRwX3NjaG9vbF9hZGogPC0gcC5hZGp1c3QoZ3JpZF81MDBtJHBfc2Nob29sLCBtZXRob2QgPSAiZmRyIikNCmdyaWRfNTAwbSRob3RzcG90X3NjaG9vbCA8LSBpZmVsc2UoZ3JpZF81MDBtJHBfc2Nob29sX2FkaiA8IDAuMDUgJiBncmlkXzUwMG0kZ2lfc2Nob29sID4gMCwgIkhvdHNwb3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JpZF81MDBtJHBfc2Nob29sX2FkaiA8IDAuMDUgJiBncmlkXzUwMG0kZ2lfc2Nob29sIDwgMCwgIkNvbGRzcG90IiwgIk5vdCBTaWduaWZpY2FudCIpKQ0KdG1hcF9tb2RlKCJwbG90IikNCg0KdG1fc2hhcGUoZ3JpZF81MDBtKSArDQogIHRtX3BvbHlnb25zKA0KICAgIGNvbCA9ICJob3RzcG90X2Zhc3QiLCANCiAgICBwYWxldHRlID0gYygicmVkIiwgImdyZXkiKSwNCiAgICBsZWdlbmQuc2hvdyA9IFRSVUUsDQogICAgdGl0bGUgPSAiRmFzdCBGb29kIEhvdHNwb3RzIChHaSopIg0KICApICsNCiAgdG1fYm9yZGVycygpICsNCiAgdG1fbGF5b3V0KHRpdGxlID0gIumAn+mjn+W6l+eGseWNgCAoR2kqKSAtIEZEUiDkv67mraMiLCBsZWdlbmQub3V0c2lkZSA9IFRSVUUpDQpgYGANCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnRtX3NoYXBlKGdyaWRfNTAwbSkgKw0KICB0bV9wb2x5Z29ucygNCiAgICBjb2wgPSAiaG90c3BvdF9zY2hvb2wiLCANCiAgICBwYWxldHRlID0gYygiYmx1ZSIsICJncmV5IiksDQogICAgbGVnZW5kLnNob3cgPSBUUlVFLA0KICAgIHRpdGxlID0gIlNjaG9vbCBIb3RzcG90cyAoR2kqKSINCiAgKSArDQogIHRtX2JvcmRlcnMoKSArDQogIHRtX2xheW91dCh0aXRsZSA9ICLlrbjmoKHnhrHljYAgKEdpKikgLSBGRFIg5L+u5q2jIiwgbGVnZW5kLm91dHNpZGUgPSBUUlVFKQ0KYGBgDQoNCiMjIyBQYXJ0IDLvvJrlrbjmoKHpmYTov5HnmoTpgJ/po5/lupfmlbjph4/nnJ/nmoTmr5TovIPlpJrll44/DQoNCiMjIyMgMS4g6KuL5Y+D6ICDVHJhbnNwb3J0YXRpb24gUmVzZWFyY2ggUGFydCBBLCA0NSAoMjAxMSkgNjQw4oCTNjUy77yISFctMDkg55qE56CU6K6A5pWZ5p2Q77yJ5omA5L2/IOeUqEYoZCnlh73mlbjnmoTkvZzms5XvvIzmr5TovIPliIbliKXkvY3mlrzlpKflronljYDjgIHmloflsbHljYDoiIfkv6HnvqnljYDnmoTlrbjmoKHvvIzliLDphLDov5HpgJ/po5/lupfnmoTnqbrplpPnibnlvrXjgIINCmBgYHtyIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCnRhcmdldF92aWxsIDwtIHZpbGwgJT4lIGZpbHRlcihUT1dOICVpbiUgYygi5aSn5a6J5Y2AIiwgIuaWh+WxseWNgCIsICLkv6HnvqnljYAiKSkNCnNjaG9vbF9zZWwgPC0gc3Rfam9pbihzY2hvb2wsIHRhcmdldF92aWxsLCBqb2luID0gc3Rfd2l0aGluKSAlPiUgZmlsdGVyKCFpcy5uYShUT1dOKSkNCmZvb2Rfc2VsIDwtIHN0X2pvaW4oZm9vZCwgdGFyZ2V0X3ZpbGwsIGpvaW4gPSBzdF93aXRoaW4pICU+JSBmaWx0ZXIoIWlzLm5hKFRPV04pKQ0KDQpzZl90b19wcHAgPC0gZnVuY3Rpb24ocG9pbnRzX3NmLCB3aW5kb3dfcG9seWdvbikgew0KICB3aW4gPC0gYXMub3dpbihzdF91bmlvbih3aW5kb3dfcG9seWdvbikpDQogIA0KICBjb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMocG9pbnRzX3NmKQ0KICANCiAgcHBwX29iaiA8LSBwcHAoeCA9IGNvb3Jkc1ssMV0sIHkgPSBjb29yZHNbLDJdLCB3aW5kb3cgPSB3aW4pDQogIHJldHVybihwcHBfb2JqKQ0KfQ0KDQpkaXN0cmljdHMgPC0gYygi5aSn5a6J5Y2AIiwgIuaWh+WxseWNgCIsICLkv6HnvqnljYAiKQ0KDQpmZF9yZXN1bHRzIDwtIGxpc3QoKQ0KDQpmb3IgKGRpc3QgaW4gZGlzdHJpY3RzKSB7DQogIGRpc3RfdmlsbCA8LSB0YXJnZXRfdmlsbCAlPiUgZmlsdGVyKFRPV04gPT0gZGlzdCkNCiAgZGlzdF9zY2hvb2wgPC0gc2Nob29sX3NlbCAlPiUgZmlsdGVyKFRPV04gPT0gZGlzdCkNCiAgZGlzdF9mb29kIDwtIGZvb2Rfc2VsICU+JSBmaWx0ZXIoVE9XTiA9PSBkaXN0KQ0KICANCiAgZm9vZF9wcHAgPC0gc2ZfdG9fcHBwKGRpc3RfZm9vZCwgZGlzdF92aWxsKQ0KICANCiAgc2Nob29sX2Nvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhkaXN0X3NjaG9vbCkNCiAgbWFya3MgPC0gcmVwKDEsIG5yb3coc2Nob29sX2Nvb3JkcykpDQogIHNjaG9vbF9wcHAgPC0gcHBwKHNjaG9vbF9jb29yZHNbLDFdLCBzY2hvb2xfY29vcmRzWywyXSwgd2luZG93ID0gZm9vZF9wcHAkd2luZG93LCBtYXJrcyA9IG1hcmtzKQ0KICANCiAgZmQgPC0gRmVzdChmb29kX3BwcCwgY29ycmVjdGlvbiA9ICJib3JkZXIiKQ0KICANCiAgZmRfcmVzdWx0c1tbZGlzdF1dIDwtIGZkDQp9DQoNCnBsb3QoZmRfcmVzdWx0c1tbIuWkp+WuieWNgCJdXSwgbWFpbiA9ICJGKGQpIGZ1bmN0aW9uKOWkp+WuiSzkv6Hnvqks5paH5bGxKSIsIGNvbCA9ICJyZWQiKQ0KcGxvdChmZF9yZXN1bHRzW1si5paH5bGx5Y2AIl1dLCBhZGQgPSBUUlVFLCBjb2wgPSAiYmx1ZSIpDQpwbG90KGZkX3Jlc3VsdHNbWyLkv6HnvqnljYAiXV0sIGFkZCA9IFRSVUUsIGNvbCA9ICJncmVlbiIpDQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbGVnZW5kID0gZGlzdHJpY3RzLCBjb2wgPSBjKCJyZWQiLCAiYmx1ZSIsICJncmVlbiIpLCBsdHkgPSAxKQ0KYGBgDQpgYGB7ciBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpwYXIobWZyb3cgPSBjKDEsIDMpKSAgIyDkuInljYDkuKbmjpINCmZvciAoZGlzdCBpbiBkaXN0cmljdHMpIHsNCiAgZGlzdF92aWxsIDwtIHRhcmdldF92aWxsICU+JSBmaWx0ZXIoVE9XTiA9PSBkaXN0KQ0KICBkaXN0X3NjaG9vbCA8LSBzY2hvb2xfc2VsICU+JSBmaWx0ZXIoVE9XTiA9PSBkaXN0KQ0KICBkaXN0X2Zvb2QgPC0gZm9vZF9zZWwgJT4lIGZpbHRlcihUT1dOID09IGRpc3QpDQoNCiAgd2luIDwtIGFzLm93aW4oc3RfdW5pb24oZGlzdF92aWxsKSkNCiAgc2Nob29sX2Nvb3JkcyA8LSBzdF9jb29yZGluYXRlcyhkaXN0X3NjaG9vbCkNCiAgZm9vZF9jb29yZHMgPC0gc3RfY29vcmRpbmF0ZXMoZGlzdF9mb29kKQ0KDQogIHNjaG9vbF9wcHAgPC0gcHBwKHNjaG9vbF9jb29yZHNbLDFdLCBzY2hvb2xfY29vcmRzWywyXSwgd2luZG93ID0gd2luKQ0KICBmb29kX3BwcCAgIDwtIHBwcChmb29kX2Nvb3Jkc1ssMV0sIGZvb2RfY29vcmRzWywyXSwgd2luZG93ID0gd2luKQ0KDQogIHBsb3Qoc2Nob29sX3BwcCwgbWFpbiA9IHBhc3RlKGRpc3QsICLlrbjmoKEgdnMg6YCf6aOf5bqXIiksIGNvbHMgPSAiYmx1ZSIsIHBjaCA9IDMpDQogIHBvaW50cyhmb29kX3BwcCwgY29sID0gInJlZCIsIHBjaCA9IDE2KQ0KICBsZWdlbmQoInRvcHJpZ2h0IiwgbGVnZW5kID0gYygi5a245qChIiwgIumAn+mjn+W6lyIpLCBjb2wgPSBjKCJibHVlIiwgInJlZCIpLCBwY2ggPSBjKDMsIDE2KSkNCn0NCmBgYA0KDQpgYGB7cn0NCg0KZGlzdHJpY3RzIDwtIGMoIuWkp+WuieWNgCIsICLkv6HnvqnljYAiLCAi5paH5bGx5Y2AIikNCg0KZm9yIChkaXN0IGluIGRpc3RyaWN0cykgew0KICANCiAgZGlzdF9zY2hvb2wgPC0gc2Nob29sX3NlbCAlPiUgZmlsdGVyKFRPV04gPT0gZGlzdCkNCiAgZGlzdF9mb29kICAgPC0gZm9vZF9zZWwgICAlPiUgZmlsdGVyKFRPV04gPT0gZGlzdCkNCg0KICBkaXN0YW5jZXMgPC0gc3RfZGlzdGFuY2UoZGlzdF9zY2hvb2wsIGRpc3RfZm9vZCkgJT4lDQogICAgYXBwbHkoMSwgbWluKSAlPiUNCiAgICBhcy5udW1lcmljKCkNCg0KICBkX3ZhbHVlcyA8LSBzZXEoMCwgNTAwLCBieSA9IDEwKQ0KICBGX2VtcGlyaWNhbCA8LSBzYXBwbHkoZF92YWx1ZXMsIGZ1bmN0aW9uKGQpIG1lYW4oZGlzdGFuY2VzIDw9IGQpKQ0KDQogIG5fcmFuZG9tX3BvaW50cyA8LSA1MDANCiAgbl9zYW1wbGVzIDwtIG5yb3coZGlzdF9zY2hvb2wpDQogIG5fc2ltIDwtIDk5DQoNCiAgYmJveCA8LSBzdF9iYm94KGRpc3Rfc2Nob29sKQ0KICByYW5kb21fcG9pbnRzIDwtIHN0X2FzX3NmKA0KICAgIGRhdGEuZnJhbWUoDQogICAgICB4ID0gcnVuaWYobl9yYW5kb21fcG9pbnRzLCBiYm94WyJ4bWluIl0sIGJib3hbInhtYXgiXSksDQogICAgICB5ID0gcnVuaWYobl9yYW5kb21fcG9pbnRzLCBiYm94WyJ5bWluIl0sIGJib3hbInltYXgiXSkNCiAgICApLA0KICAgIGNvb3JkcyA9IGMoIngiLCAieSIpLA0KICAgIGNycyA9IHN0X2NycyhkaXN0X3NjaG9vbCkNCiAgKQ0KDQogIGRpc3RfZm9vZF9wcm9qIDwtIHN0X3RyYW5zZm9ybShkaXN0X2Zvb2QsIDMyNjE4KQ0KICByYW5kb21fcG9pbnRzX3Byb2ogPC0gc3RfdHJhbnNmb3JtKHJhbmRvbV9wb2ludHMsIDMyNjE4KQ0KDQogIHNpbV9yZXN1bHRzIDwtIG1hdHJpeChucm93ID0gbGVuZ3RoKGRfdmFsdWVzKSwgbmNvbCA9IG5fc2ltKQ0KDQogIGZvciAoaSBpbiAxOm5fc2ltKSB7DQogICAgc2FtcGxlZF9wb2ludHMgPC0gcmFuZG9tX3BvaW50c19wcm9qICU+JQ0KICAgICAgc2FtcGxlX24obl9zYW1wbGVzKQ0KDQogICAgc2ltX2Rpc3RhbmNlcyA8LSBzdF9kaXN0YW5jZShzYW1wbGVkX3BvaW50cywgZGlzdF9mb29kX3Byb2opICU+JQ0KICAgICAgYXBwbHkoMSwgbWluKSAlPiUNCiAgICAgIGFzLm51bWVyaWMoKQ0KDQogICAgc2ltX3Jlc3VsdHNbLCBpXSA8LSBzYXBwbHkoZF92YWx1ZXMsIGZ1bmN0aW9uKGQpIG1lYW4oc2ltX2Rpc3RhbmNlcyA8PSBkKSkNCiAgfQ0KDQogIGVudmVsb3BlX2xvd2VyIDwtIGFwcGx5KHNpbV9yZXN1bHRzLCAxLCBtaW4pDQogIGVudmVsb3BlX3VwcGVyIDwtIGFwcGx5KHNpbV9yZXN1bHRzLCAxLCBtYXgpDQoNCiAgcGxvdF9kYXRhIDwtIGRhdGEuZnJhbWUoDQogICAgZCA9IGRfdmFsdWVzLA0KICAgIEVtcGlyaWNhbCA9IEZfZW1waXJpY2FsLA0KICAgIExvd2VyID0gZW52ZWxvcGVfbG93ZXIsDQogICAgVXBwZXIgPSBlbnZlbG9wZV91cHBlcg0KICApDQogIA0KICBwIDwtIGdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gZCkpICsNCiAgICBnZW9tX3JpYmJvbihhZXMoeW1pbiA9IExvd2VyLCB5bWF4ID0gVXBwZXIsIGZpbGwgPSAiU2ltdWxhdGlvbiBFbnZlbG9wZSIpLA0KICAgICAgICAgICAgICAgIGFscGhhID0gMC43KSArDQogICAgZ2VvbV9saW5lKGFlcyh5ID0gRW1waXJpY2FsLCBjb2xvciA9ICJFbXBpcmljYWwgRihkKSIpLCBsaW5ld2lkdGggPSAxKSArDQogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZSA9IE5VTEwsIHZhbHVlcyA9IGMoIlNpbXVsYXRpb24gRW52ZWxvcGUiID0gImdyZXk4MCIpKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSBOVUxMLCB2YWx1ZXMgPSBjKCJFbXBpcmljYWwgRihkKSIgPSAiI0Q1NUUwMCIpKSArDQogICAgbGFicygNCiAgICAgIHggPSAi6Led6ZuiICjlhazlsLopIiwNCiAgICAgIHkgPSAiRihkKSIsDQogICAgICB0aXRsZSA9IHBhc3RlMCgiRihkKTogIiwgZGlzdCwgIiDlrbjmoKHliLDpgJ/po5/lupfnmoTlj6/ov5HmgKfliIbmnpAiKQ0KICAgICkgKw0KICAgIHRoZW1lX21pbmltYWwoKSArDQogICAgdGhlbWUoDQogICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE0KSwNCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKQ0KICAgICkNCg0KICBwcmludChwKQ0KfSANCg0KYGBgDQojIyMjIOmblueEtuW+nmNvbmZpZGVuY2UgZW52ZWxvcGXkvobnnIvvvIzkuInljYDnmoblkYjnj77pmqjmqZ/liIbluIPvvIzkvYblvp5mLWZ1bmN0aW9u55qE5puy57ea5Y+v5Lul55yL5Yiw5aSn5a6J5Y2A5a245qCh6IiH6YCf6aOf5bqX6LyD5YGP5ZCRY2x1c3RlcuWIhuW4gyzkv6HnvqnljYDlrbjmoKHoiIfpgJ/po5/lupfovIPlgY/lkJFyYW5kb23liIbluIMs5paH5bGx5Y2A5a245qCh6IiH6YCf6aOf5bqX6LyD5YGP5ZCRZGlzcGVyc2lvbuWIhuW4g++8jOmhr+ePvuWHuuWkp+WuieWNgOmZhOi/keaciei8g+WkmumAn+mjn+W6l++8jOWPjeaYoOS4ieWNgOWcqOWtuOagoeiIh+mAn+mjn+W6l+Wvpumam+i3nemboumXnOS/guS4iueahOe1kOani+aAp+W3rueVsO+8jOmAmeS6m+W3rueVsOWPr+iDveS+huiHquWcsOeQhumdouepjeOAgeW7uuaIkOeSsOWig+WvhuW6puetieWboOe0oA0KDQojIyMjIDIuIOWIqeeUqCBSaXBsZXkncyBLIGZ1bmN0aW9uLCBrKGQp6KiI566X6YCf6aOf5bqX5ZyoIGRpc3RhbmNlIChkKSA9IDMwMDAg5YWs5bC65pmC55qEIEsoMzAwMCkg5Lul5Y+KIEwoMzAwMCnvvIzkuKbkvb/nlKggTW9udGUgQ2FybG8gc2lnbmlmaWNhbmNlIHRlc3Qg6KiI566XIEwoMzAwMCnnmoQgOTUl5L+h6LO05Y2A6ZaTDQpgYGB7ciBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpmb29kX3Byb2ogPC0gc3RfdHJhbnNmb3JtKGZvb2QsIDM4MjYpDQp2aWxsX3Byb2ogPC0gc3RfdHJhbnNmb3JtKHZpbGwsIDM4MjYpDQoNCnRhaXBlaV93aW4gPC0gYXMub3dpbihzdF91bmlvbih2aWxsX3Byb2opKQ0KY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKGZvb2RfcHJvaikNCmZvb2RfcHBwIDwtIHBwcCh4ID0gY29vcmRzWywxXSwgeSA9IGNvb3Jkc1ssMl0sIHdpbmRvdyA9IHRhaXBlaV93aW4pDQoNCktfcmVzdWx0IDwtIEtlc3QoZm9vZF9wcHAsIGNvcnJlY3Rpb24gPSAiYm9yZGVyIikNCkszMDAwIDwtIEtfcmVzdWx0JGJvcmRlclt3aGljaC5taW4oYWJzKEtfcmVzdWx0JHIgLSAzMDAwKSldDQpMMzAwMCA8LSBzcXJ0KEszMDAwIC8gcGkpIC0gMzAwMA0KDQpjYXQoIksoMzAwMCkgPSIsIEszMDAwLCAiXG4iKQ0KY2F0KCJMKDMwMDApID0iLCBMMzAwMCwgIlxuIikNCnRhaXBlaV93aW4gPC0gYXMub3dpbihzdF91bmlvbih2aWxsKSkNCg0Kc2V0LnNlZWQoMTIzKQ0KZW52IDwtIGVudmVsb3BlKA0KICBmb29kX3BwcCwNCiAgZnVuID0gS2VzdCwNCiAgbnNpbSA9IDk5LA0KICBucmFuayA9IDUsDQogIGNvcnJlY3Rpb24gPSAiYm9yZGVyIiwNCiAgc2F2ZWZ1bnMgPSBUUlVFDQopDQoNCmVudl9MIDwtIGV2YWwuZnYoc3FydChlbnYgLyBwaSktMzAwMCkNCg0KTDMwMDBfbG8gPC0gZW52X0wkbG9bd2hpY2gubWluKGFicyhlbnZfTCRyIC0gMzAwMCkpXQ0KTDMwMDBfaGkgPC0gZW52X0wkaGlbd2hpY2gubWluKGFicyhlbnZfTCRyIC0gMzAwMCkpXQ0KDQpjYXQoIkwoMzAwMCkgOTUlIOS/oeiztOWNgOmWk++8miIsIEwzMDAwX2xvLCAifiIsIEwzMDAwX2hpLCAiXG4iKQ0KDQpwbG90KGVudl9MLCBtYWluID0gIkwoZCkgQ29uZmlkZW5jZSBFbnZlbG9wZSIpDQphYmxpbmUodiA9IDMwMDAsIGNvbCA9ICJibHVlIiwgbHR5ID0gMikNCmBgYA0KIyMjIyDlvp5SLWNvbnN1bGXnmoToqIjnrpfntZDmnpzlj6/ku6XnnIvliLBMKDMwMDApPTE2NDAuMemBoOWkp+aWvOS/oeiztOWNgOmWk+S4iumZkDI4Mi44MjHvvIzlm6DmraTpoa/npLrot53pm6IzMDAw5YWs5bC655qE5bC65bqm5LiL77yM5Y+w5YyX6YCf6aOf5bqX5YiG5biD5ZGI54++6aGv6JGX44CMY2x1c3RlcuOAjeWIhuW4gw0KIyMjIyDlvp5jaeWcluWPr+S7peeci+WIsOm7kee3mijop4DmuKzlgLwp5pW06auU6auY5pa8ZW52ZWxvcGXvvIzljbDorYnkuoblnKgzMDAw5YWs5bC65bC65bqm5LiL77yM5Y+w5YyX6YCf6aOf5bqX5YiG5biD6aGv56S66aGv6JGX55qEY2x1c3RlcuWIhuW4gw0KDQojIyMjIDMuIOWIqeeUqEJpdmFyaWF0ZSBGIGZ1bmN0aW9u5YiG5p6Q5pa55rOV77yM5q+U6LyD5YWs56uLdnMu56eB56uL5a245qCh77yM5Yiw6YSw6L+R6YCf6aOf5bqX55qE56m66ZaT54m55b6177yM5Lim6Kqq5piO5ZOq5LiA56iu6aGe5Z6L5a245qCh55qE6ZmE6L+R77yM5pyD5pyJ6LyD5aSa55qE6YCf6aOf5bqX77yf77yI6aGv6JGX5rC05rqWPTAuMDXvvIkgDQpgYGB7ciBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpwdWIgPSBzY2hvb2xbc2Nob29sJFR5cGUgPT0gIuWFrOeriyIsIF0NCnByaSA9IHNjaG9vbFtzY2hvb2wkVHlwZSA9PSAi56eB56uLIiwgXQ0KDQpjb29yZF9wdWIgPSBzdF9jb29yZGluYXRlcyhwdWIpDQpjb29yZF9wcmkgPSBzdF9jb29yZGluYXRlcyhwcmkpDQpjb29yZF9mZiA9IHN0X2Nvb3JkaW5hdGVzKGZvb2QpDQoNCndpbmRvd3MgPSBhcy5vd2luKHN0X3VuaW9uKHZpbGwpKQ0KDQpwdWJfcHAgPSBhcy5wcHAoY29vcmRfcHViLCB3aW5kb3dzKQ0KcHJpX3BwID0gYXMucHBwKGNvb3JkX3ByaSwgd2luZG93cykNCmZmX3BwID0gYXMucHBwKGNvb3JkX2ZmLCB3aW5kb3dzKQ0KDQpOTkRfcHViID0gbm5jcm9zcyhwdWJfcHAsIGZmX3BwKQ0KRmRfcHViID0gZWNkZihOTkRfcHViJGRpc3QpDQoNCk5ORF9wcmkgPSBubmNyb3NzKHByaV9wcCwgZmZfcHApDQpGZF9wcmkgPSBlY2RmKE5ORF9wcmkkZGlzdCkNCg0Kbl9wdWIgPSBwdWJfcHAkbg0Kbl9wcmkgPSBwcmlfcHAkbg0KDQpGX3NpbV9wdWIgPSBtYXRyaXgobnJvdz05OSwgbmNvbD1uX3B1YikNCmZvcihpIGluIDE6OTkpew0KICBSTkQgPSBycG9pbnQobl9wdWIsIHdpbiA9IHdpbmRvd3MpDQogIEZfc2ltX3B1YltpLCBdID0gbm5jcm9zcyhSTkQsIGZmX3BwKSRkaXN0DQp9DQpGX3NpbV9zX3B1YiA9IHQoYXBwbHkoRl9zaW1fcHViLDEsc29ydCkpDQpGX3NpbV9zb3J0X3B1YiA9IGFwcGx5KEZfc2ltX3NfcHViLCAyLCBzb3J0KQ0KDQpGX3NpbV9wcmkgPSBtYXRyaXgobnJvdz05OSwgbmNvbD1uX3ByaSkNCmZvcihpIGluIDE6OTkpew0KICBSTkQgPSBycG9pbnQobl9wcmksIHdpbiA9IHdpbmRvd3MpDQogIEZfc2ltX3ByaVtpLCBdID0gbm5jcm9zcyhSTkQsIGZmX3BwKSRkaXN0DQp9DQpGX3NpbV9zX3ByaSA9IHQoYXBwbHkoRl9zaW1fcHJpLDEsc29ydCkpDQpGX3NpbV9zb3J0X3ByaSA9IGFwcGx5KEZfc2ltX3NfcHJpLCAyLCBzb3J0KQ0KDQpwbG90KEZkX3B1YiwgY29sPSJibHVlIiwgY2V4PTAsIG1haW49IkYgZnVuY3Rpb246IOWFrOeri+WtuOagoSDihpIg6YCf6aOf5bqXIiwgeGxhYj0i6Led6ZuiIiwgeWxhYj0iRihkKSIsIHhsaW09YygwLDQwMDApKQ0KZm9yKGkgaW4gMTo5OSl7IA0KICBsaW5lcyhlY2RmKEZfc2ltX3B1YltpLF0pLCBjb2w9ImdyZXkiLCB2ZXJ0aWNhbHM9VCwgY2V4PTApDQp9DQpsaW5lcyhlY2RmKEZfc2ltX3NvcnRfcHViWzUsXSksIGNvbD0iYmxhY2siLCBsdHk9MiwgbHdkPTEuNSkgIA0KbGluZXMoZWNkZihGX3NpbV9zb3J0X3B1Yls5NSxdKSwgY29sPSJibGFjayIsIGx0eT0yLCBsd2Q9MS41KSANCmxpbmVzKEZkX3B1YiwgY29sPSJibHVlIiwgbHdkPTMpDQoNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLHdhcm5pbmc9RkFMU0V9DQpwbG90KEZkX3ByaSwgY29sPSJibHVlIiwgY2V4PTAsIG1haW49IkYgZnVuY3Rpb246IOengeeri+WtuOagoSDihpIg6YCf6aOf5bqXIiwgeGxhYj0i6Led6ZuiIiwgeWxhYj0iRihkKSIsIHhsaW09YygwLDQwMDApKQ0KZm9yKGkgaW4gMTo5OSl7IA0KICBsaW5lcyhlY2RmKEZfc2ltX3ByaVtpLF0pLCBjb2w9ImdyZXkiLCB2ZXJ0aWNhbHM9VCwgY2V4PTApDQp9DQpsaW5lcyhlY2RmKEZfc2ltX3NvcnRfcHJpWzUsXSksIGNvbD0iYmxhY2siLCBsdHk9MiwgbHdkPTEuNSkgIA0KbGluZXMoZWNkZihGX3NpbV9zb3J0X3ByaVs5NSxdKSwgY29sPSJibGFjayIsIGx0eT0yLCBsd2Q9MS41KSANCmxpbmVzKEZkX3ByaSwgY29sPSJibHVlIiwgbHdkPTMpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KcGxvdChGZF9wdWIsIGNvbD0iYmx1ZSIsIG1haW49IuWFrOeriyB2cy4g56eB56uL5a245qChIOKGkiDpgJ/po5/lupciLCB4bGFiPSLot53pm6IiLCB5bGFiPSJGKGQpIiwgeGxpbT1jKDAsNDAwMCkpDQpsaW5lcyhGZF9wdWIsIGNvbD0iYmx1ZSIsIGx3ZD0zKQ0KbGluZXMoRmRfcHJpLCBjb2w9InJlZCIsIGx3ZD0zKQ0KbGVnZW5kKCJib3R0b21yaWdodCIsIGxlZ2VuZCA9IGMoIuWFrOeri+WtuOagoSIsICLnp4Hnq4vlrbjmoKEiKSwgY29sID0gYygiYmx1ZSIsICJyZWQiKSwgbHdkID0gMykNCmBgYA0KIyMjIyDlvp7liY3lhanlvLXlnJblj6/ku6XnnIvliLDvvIzlnKjpoa/okZfmsLTmupYwLjA155qE5oOF5rOB5LiL77yM5Y+w5YyX5biC5YWs56eB56uL5a245qCh6ZmE6L+R55qE6YCf6aOf5bqX55qG5ZGI54++Y2x1c3RlcuWIhuW4g++8jOS5n+WwseaYr+WFqeeorumhnuWei+WtuOagoemZhOi/keeahuWcjee5nuiRl+mAn+mjn+W6l++8jOiAjOW+nuesrOS4ieW8teWcluWPr+S7peeci+WIsOWFrOeri+WtuOagoWYtZnVuY3Rpb27nmoTkuIrljYfpgJ/luqbovIPlv6so6LyDY2x1c3RlcinvvIzmj5vlj6XoqbHoqqrvvIzkuZ/lsLHmmK/lhaznq4vlrbjmoKHpmYTov5HnmoTpgJ/po5/lupfovIPlpJo=