rm(list = ls())
setwd("~/Documents/113-2/空間分析/final")
library(sf);library(tidyverse);library(readxl);library(jsonlite);library(spatstat);library(ggplot2);library(ggspatial);library(showtext);library(sfdep);library(tmap);library(dplyr)
# 設定台北市邊界資料(若有 shp)
taipei <- st_read('./data/Taipei_village/Taipei_village.shp', options = 'ENCODING = BIG5', quiet = T)
taipei <- st_transform(taipei, 4326)
taipei <- taipei %>% group_by(TOWNNAME) %>% summarise()
# 學校資料(自行提供)
School <- st_read("./data/SCHOOL/SCHOOL.shp", quiet = TRUE) # 含公私立屬性欄位
School <- st_transform(School, 4326)
# 速食店資料
fastfood <- st_read("./data/Tpe_Fastfood/Tpe_Fastfood.shp", quiet = TRUE)
fastfood <- st_transform(fastfood, 4326)
# 公園資料
Park <- fromJSON("./data/臺北市公園基本資料.json") %>%
st_as_sf(coords = c("pm_Longitude", "pm_Latitude"), crs = 4326)
# 醫院資料
Hospital <- read_excel("./data/台北市醫院清冊1130523(含經緯度).xlsx") %>%
st_as_sf(coords = c("經度", "緯度"), crs = 4326)
# 台北診所資料
clinic <- st_read("./data/台北診所資料.geojson", quiet = TRUE)
# 運動中心
Sports_Center <- read.csv("./data/臺北市運動中心.csv", encoding = "UTF-8") %>%
st_as_sf(coords = c("經度", "緯度"), crs = 4326)
# 健康飲食地點
health_food <- st_read("./data/health/health.shp", quiet = TRUE)
health_food <- st_transform(health_food, 4326)
# 河川資料
river <- st_read("./data/RIVERPOLY/riverpoly/riverpoly.shp", quiet = TRUE)
river <- st_transform(river, 4326)
river <- river[river$RIVER_NAME == "淡水河" | river$RIVER_FROM == "淡水河",]
river <- river[!is.na(river$RIVER_NAME), ]
# 登山步道資料
trail_start <- read.csv("./data/登山步道.csv", encoding = "UTF-8")
trail_start <- trail_start[-32, ]
trail_start <- st_as_sf(trail_start, coords = c("起點經度座標", "起點緯度座標"), crs = 4326)
trail_end <- read.csv("./data/登山步道.csv", encoding = "UTF-8")
trail_end <- trail_end[-32, ]
trail_end <- st_as_sf(trail_start, coords = c("終點經度座標", "終點緯度座標"), crs = 4326)
trail <- rbind(trail_start, trail_end)
School$dist_to_park <- st_distance(School, Park) %>% apply(1, min) %>% as.numeric()
School$dist_to_hospital <- st_distance(School, Hospital) %>% apply(1, min) %>% as.numeric()
School$dist_to_clinc <- st_distance(School, clinic) %>% apply(1, min) %>% as.numeric()
School$dist_to_sports <- st_distance(School, Sports_Center) %>% apply(1, min) %>% as.numeric()
School$dist_to_health <- st_distance(School, health_food) %>% apply(1, min) %>% as.numeric()
School$dist_to_fastfood <- st_distance(School, fastfood) %>% apply(1, min) %>% as.numeric()
School$dist_to_river <- st_distance(School, river) %>% apply(1, min) %>% as.numeric()
School$dist_to_trail <- st_distance(School, trail) %>% apply(1, min) %>% as.numeric()
# 各區健康生活機能指數構建(將距離轉換為 0~1 的標準化「分數」)
normalize_score <- function(x) {
1 - (x - min(x)) / (max(x) - min(x))
}
# 速食店距離標準化函數,距離越近分數越低(健康機能負面指標,扣分)
normalize_score_fastfood <- function(x) {
(x - min(x)) / (max(x) - min(x)) # 距離越大分數越高(越健康)
}
# 運動類指標標準化(距離越近越好)
School$score_park <- normalize_score(School$dist_to_park)
School$score_trail <- normalize_score(School$dist_to_trail)
School$score_sports <- normalize_score(School$dist_to_sports)
School$score_river <- normalize_score(School$dist_to_river)
# 飲食類指標標準化(健康飲食距離越近越好,速食距離越遠越好)
School$score_health <- normalize_score(School$dist_to_health)
School$score_fastfood <- normalize_score_fastfood(School$dist_to_fastfood)
# 醫療類指標標準化(距離越近越好)
School$score_hospital <- normalize_score(School$dist_to_hospital)
School$score_clinic <- normalize_score(School$dist_to_clinc)
# 合成三大類指數(簡單平均)
School$sport_index <- rowMeans(st_drop_geometry(School)[, c("score_park", "score_trail", "score_sports", "score_river")], na.rm = TRUE)
School$diet_index <- rowMeans(st_drop_geometry(School)[, c("score_health", "score_fastfood")], na.rm = TRUE)
School$medical_index <- rowMeans(st_drop_geometry(School)[, c("score_hospital", "score_clinic")], na.rm = TRUE)
# 計算整體健康生活機能指數(三項平均)
School$overall_health_index <- rowMeans(st_drop_geometry(School)[, c("sport_index", "diet_index", "medical_index")], na.rm = TRUE)
library(tmap)
tmap_mode("view")
map_sport <- tm_shape(School) +
tm_dots(col = "sport_index", palette = "Blues", size = 0.5, title = "運動指數") +
tm_layout(title = "台北市國小周邊運動生活機能指數", title.size = 1.2, title.position = c("center", "top"))
map_diet <- tm_shape(School) +
tm_dots(col = "diet_index", palette = "Oranges", size = 0.5, title = "飲食指數") +
tm_layout(title = "台北市國小周邊飲食生活機能指數", title.size = 1.2, title.position = c("center", "top"))
map_medical <- tm_shape(School) +
tm_dots(col = "medical_index", palette = "Greens", size = 0.5, title = "醫療指數") +
tm_layout(title = "台北市國小周邊醫療生活機能指數", title.size = 1.2, title.position = c("center", "top"))
map_overall <- tm_shape(School) +
tm_dots(col = "overall_health_index", palette = "RdYlGn", size = 0.5, title = "整體健康指數") +
tm_layout(title = "台北市國小周邊整體健康生活機能指數", title.size = 1.2, title.position = c("center", "top"))
tmap_arrange(map_sport, map_diet, map_medical, map_overall, ncol = 2)
NA
School %>%
slice_min(order_by = sport_index, n = 10) %>%
arrange(desc(sport_index)) %>%
mutate(
rank_factor = factor(Name, levels = Name),
fill_color = gradient_n_pal(c("#0066CC", "#CCE5FF"))(rescale(sport_index)) # 深→淺
) %>%
ggplot(aes(x = rank_factor, y = sport_index, fill = fill_color)) +
geom_col(show.legend = FALSE) +
coord_flip() +
scale_fill_identity() +
theme_minimal() +
labs(
x = "學校名稱",
y = "指數分數",
title = "臺北市國小周邊運動生活指數倒數 10 名"
) +
theme(
text = element_text(family = "PingFangSC-SemiBold"),
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.text.y = element_text(size = 8),
strip.text = element_text(size = 12, face = "bold")
)

School %>%
slice_min(order_by = diet_index, n = 10) %>%
arrange(desc(diet_index)) %>%
mutate(
rank_factor = factor(Name, levels = Name),
fill_color = gradient_n_pal(c("#FF6600", "#FFE5CC"))(rescale(diet_index)) # 深→淺
) %>%
ggplot(aes(x = rank_factor, y = diet_index, fill = fill_color)) +
geom_col(show.legend = FALSE) +
coord_flip() +
scale_fill_identity() +
theme_minimal() +
labs(
x = "學校名稱",
y = "指數分數",
title = "臺北市國小周邊飲食生活指數倒數 10 名"
) +
theme(
text = element_text(family = "PingFangSC-SemiBold"),
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.text.y = element_text(size = 8),
strip.text = element_text(size = 12, face = "bold")
)

School %>%
slice_min(order_by = medical_index, n = 10) %>%
arrange(desc(medical_index)) %>%
mutate(
rank_factor = factor(Name, levels = Name),
fill_color = gradient_n_pal(c("#009933", "#CCFFCC"))(rescale(medical_index)) # 深→淺
) %>%
ggplot(aes(x = rank_factor, y = medical_index, fill = fill_color)) +
geom_col(show.legend = FALSE) +
coord_flip() +
scale_fill_identity() +
theme_minimal() +
labs(
x = "學校名稱",
y = "指數分數",
title = "臺北市國小周邊醫療生活指數倒數 10 名"
) +
theme(
text = element_text(family = "PingFangSC-SemiBold"),
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.text.y = element_text(size = 8),
strip.text = element_text(size = 12, face = "bold"))

School %>%
slice_min(order_by = overall_health_index, n = 10) %>%
arrange(desc(overall_health_index)) %>%
mutate(
rank_factor = factor(Name, levels = Name),
fill_color = gradient_n_pal(c("#D73027", "#FEE08B"))(rescale(overall_health_index)) # 深→淺
) %>%
ggplot(aes(x = rank_factor, y = overall_health_index, fill = fill_color)) +
geom_col(show.legend = FALSE) +
coord_flip() +
scale_fill_identity() +
theme_minimal() +
labs(
x = "學校名稱",
y = "指數分數",
title = "臺北市國小周邊整體健康生活指數倒數 10 名"
) +
theme(
text = element_text(family = "PingFangSC-SemiBold"),
plot.title = element_text(hjust = 0.5, size = 16, face = "bold"),
axis.text.y = element_text(size = 8),
strip.text = element_text(size = 12, face = "bold"))

School_district <- st_join(School, taipei, left = TRUE)
# 移除空間資訊,並依行政區分組計算平均指數
district_summary <- School_district %>%
st_set_geometry(NULL) %>%
group_by(TOWNNAME) %>%
summarise(
sport_index_avg = mean(sport_index, na.rm = TRUE),
diet_index_avg = mean(diet_index, na.rm = TRUE),
medical_index_avg = mean(medical_index, na.rm = TRUE),
overall_health_index_avg = mean(overall_health_index, na.rm = TRUE)
)
# 將計算好的平均值合併回 taipei 空間資料
taipei <- taipei %>%
left_join(district_summary, by = "TOWNNAME")
# 繪圖
ggplot(taipei) +
geom_sf(aes(fill = overall_health_index_avg), color = "white") +
scale_fill_gradientn(
colours = c("#D73027", "#FEE08B", "#1A9850"),
na.value = "grey90") +
labs(fill = "整體健康指數平均", title = "台北市各行政區之整體健康指數") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5), text = element_text(family = "PingFangSC-SemiBold"))

# 繪圖
ggplot(taipei) +
geom_sf(aes(fill = sport_index_avg), color = "white") +
scale_fill_gradientn(
colours = c("#D73027", "#FEE08B", "#1A9850"),
na.value = "grey90") +
labs(fill = "運動健康指數平均", title = "台北市各行政區之運動健康指數") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5), text = element_text(family = "PingFangSC-SemiBold"))

# 建立鄰接關係
nb <- poly2nb(taipei)
# 轉換為空間權重矩陣(row-standardized)
lw <- nb2listw(nb, style = "W", zero.policy = TRUE)
# 計算 Local Moran's I
local_moran <- localmoran(taipei$overall_health_index_avg, lw, zero.policy = TRUE)
# 加入結果欄位
taipei$local_I <- local_moran[, "Ii"]
taipei$local_I_p <- local_moran[, "Pr(z != E(Ii))"] # 或用自算的 p 值
# 畫圖:Local Moran's I 值分布
ggplot(taipei) +
geom_sf(aes(fill = local_I), color = "white") +
scale_fill_gradientn(
colours = c("#D73027", "#FEE08B", "#1A9850"),
na.value = "grey90"
) +
labs(
fill = "Local Moran's I",
title = "台北市行政區之 Local Moran's I(整體健康指數)"
) +
theme_minimal() +
theme(
plot.title = element_text(hjust = 0.5),
text = element_text(family = "PingFangSC-SemiBold")
)

LS0tCnRpdGxlOiAi5Y+w5YyX5biC5ZyL5bCP5qCh5ZyS5ZGo6YKK5YGl5bq355Sf5rS76KiI55WrIgphdXRob3I6ICLnrKzkuIPntYQg6JSh5byY5q+F44CB5p2O5a6H6KyZ44CB5p2O562g5o2344CB6buD5qW354S2IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0Kcm0obGlzdCA9IGxzKCkpCnNldHdkKCJ+L0RvY3VtZW50cy8xMTMtMi/nqbrplpPliIbmnpAvZmluYWwiKQpgYGAKCmBgYHtyIHJlc3VsdCA9ICJoaWRlIiwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkoc2YpO2xpYnJhcnkodGlkeXZlcnNlKTtsaWJyYXJ5KHJlYWR4bCk7bGlicmFyeShqc29ubGl0ZSk7bGlicmFyeShzcGF0c3RhdCk7bGlicmFyeShnZ3Bsb3QyKTtsaWJyYXJ5KGdnc3BhdGlhbCk7bGlicmFyeShzaG93dGV4dCk7bGlicmFyeShzZmRlcCk7bGlicmFyeSh0bWFwKTtsaWJyYXJ5KGRwbHlyKTtsaWJyYXJ5KHNjYWxlcykKCiMg6Kit5a6a5Y+w5YyX5biC6YKK55WM6LOH5paZ77yI6Iul5pyJIHNocO+8iQp0YWlwZWkgPC0gc3RfcmVhZCgnLi9kYXRhL1RhaXBlaV92aWxsYWdlL1RhaXBlaV92aWxsYWdlLnNocCcsIG9wdGlvbnMgPSAnRU5DT0RJTkcgPSBCSUc1JywgcXVpZXQgPSBUKQp0YWlwZWkgPC0gc3RfdHJhbnNmb3JtKHRhaXBlaSwgNDMyNikKdGFpcGVpIDwtIHRhaXBlaSAlPiUgZ3JvdXBfYnkoVE9XTk5BTUUpICU+JSBzdW1tYXJpc2UoKQoKIyDlrbjmoKHos4fmlpnvvIjoh6rooYzmj5DkvpvvvIkKU2Nob29sIDwtIHN0X3JlYWQoIi4vZGF0YS9TQ0hPT0wvU0NIT09MLnNocCIsIHF1aWV0ID0gVFJVRSkgIyDlkKvlhaznp4Hnq4vlsazmgKfmrITkvY0KU2Nob29sIDwtIHN0X3RyYW5zZm9ybShTY2hvb2wsIDQzMjYpCgojIOmAn+mjn+W6l+izh+aWmQpmYXN0Zm9vZCA8LSBzdF9yZWFkKCIuL2RhdGEvVHBlX0Zhc3Rmb29kL1RwZV9GYXN0Zm9vZC5zaHAiLCBxdWlldCA9IFRSVUUpCmZhc3Rmb29kIDwtIHN0X3RyYW5zZm9ybShmYXN0Zm9vZCwgNDMyNikKCiMg5YWs5ZyS6LOH5paZClBhcmsgPC0gZnJvbUpTT04oIi4vZGF0YS/oh7rljJfluILlhazlnJLln7rmnKzos4fmlpkuanNvbiIpICU+JQogIHN0X2FzX3NmKGNvb3JkcyA9IGMoInBtX0xvbmdpdHVkZSIsICJwbV9MYXRpdHVkZSIpLCBjcnMgPSA0MzI2KQoKIyDphqvpmaLos4fmlpkKSG9zcGl0YWwgPC0gcmVhZF9leGNlbCgiLi9kYXRhL+WPsOWMl+W4gumGq+mZoua4heWGijExMzA1MjMo5ZCr57aT57ev5bqmKS54bHN4IikgJT4lCiAgc3RfYXNfc2YoY29vcmRzID0gYygi57aT5bqmIiwgIue3r+W6piIpLCBjcnMgPSA0MzI2KQoKIyDlj7DljJfoqLrmiYDos4fmlpkKY2xpbmljIDwtIHN0X3JlYWQoIi4vZGF0YS/lj7DljJfoqLrmiYDos4fmlpkuZ2VvanNvbiIsIHF1aWV0ID0gVFJVRSkKCiMg6YGL5YuV5Lit5b+DClNwb3J0c19DZW50ZXIgPC0gcmVhZC5jc3YoIi4vZGF0YS/oh7rljJfluILpgYvli5XkuK3lv4MuY3N2IiwgZW5jb2RpbmcgPSAiVVRGLTgiKSAlPiUKICBzdF9hc19zZihjb29yZHMgPSBjKCLntpPluqYiLCAi57ev5bqmIiksIGNycyA9IDQzMjYpCgojIOWBpeW6t+mjsumjn+WcsOm7ngpoZWFsdGhfZm9vZCA8LSBzdF9yZWFkKCIuL2RhdGEvaGVhbHRoL2hlYWx0aC5zaHAiLCBxdWlldCA9IFRSVUUpCmhlYWx0aF9mb29kIDwtIHN0X3RyYW5zZm9ybShoZWFsdGhfZm9vZCwgNDMyNikKCiMg5rKz5bed6LOH5paZCnJpdmVyIDwtIHN0X3JlYWQoIi4vZGF0YS9SSVZFUlBPTFkvcml2ZXJwb2x5L3JpdmVycG9seS5zaHAiLCBxdWlldCA9IFRSVUUpCnJpdmVyIDwtIHN0X3RyYW5zZm9ybShyaXZlciwgNDMyNikKcml2ZXIgPC0gcml2ZXJbcml2ZXIkUklWRVJfTkFNRSA9PSAi5reh5rC05rKzIiB8IHJpdmVyJFJJVkVSX0ZST00gPT0gIua3oeawtOaysyIsXQpyaXZlciA8LSByaXZlclshaXMubmEocml2ZXIkUklWRVJfTkFNRSksIF0KCiMg55m75bGx5q2l6YGT6LOH5paZCnRyYWlsX3N0YXJ0IDwtIHJlYWQuY3N2KCIuL2RhdGEv55m75bGx5q2l6YGTLmNzdiIsIGVuY29kaW5nID0gIlVURi04IikgCnRyYWlsX3N0YXJ0IDwtIHRyYWlsX3N0YXJ0Wy0zMiwgXQp0cmFpbF9zdGFydCA8LSBzdF9hc19zZih0cmFpbF9zdGFydCwgY29vcmRzID0gYygi6LW36bue57aT5bqm5bqn5qiZIiwgIui1t+m7nue3r+W6puW6p+aomSIpLCBjcnMgPSA0MzI2KQoKdHJhaWxfZW5kIDwtIHJlYWQuY3N2KCIuL2RhdGEv55m75bGx5q2l6YGTLmNzdiIsIGVuY29kaW5nID0gIlVURi04IikgCnRyYWlsX2VuZCA8LSB0cmFpbF9lbmRbLTMyLCBdCnRyYWlsX2VuZCA8LSBzdF9hc19zZih0cmFpbF9zdGFydCwgY29vcmRzID0gYygi57WC6bue57aT5bqm5bqn5qiZIiwgIue1gum7nue3r+W6puW6p+aomSIpLCBjcnMgPSA0MzI2KQoKdHJhaWwgPC0gcmJpbmQodHJhaWxfc3RhcnQsIHRyYWlsX2VuZCkKYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KU2Nob29sJGRpc3RfdG9fcGFyayA8LSBzdF9kaXN0YW5jZShTY2hvb2wsIFBhcmspICU+JSBhcHBseSgxLCBtaW4pICU+JSBhcy5udW1lcmljKCkKU2Nob29sJGRpc3RfdG9faG9zcGl0YWwgPC0gc3RfZGlzdGFuY2UoU2Nob29sLCBIb3NwaXRhbCkgJT4lIGFwcGx5KDEsIG1pbikgJT4lIGFzLm51bWVyaWMoKQpTY2hvb2wkZGlzdF90b19jbGluYyA8LSBzdF9kaXN0YW5jZShTY2hvb2wsIGNsaW5pYykgJT4lIGFwcGx5KDEsIG1pbikgJT4lIGFzLm51bWVyaWMoKQpTY2hvb2wkZGlzdF90b19zcG9ydHMgPC0gc3RfZGlzdGFuY2UoU2Nob29sLCBTcG9ydHNfQ2VudGVyKSAlPiUgYXBwbHkoMSwgbWluKSAlPiUgYXMubnVtZXJpYygpClNjaG9vbCRkaXN0X3RvX2hlYWx0aCA8LSBzdF9kaXN0YW5jZShTY2hvb2wsIGhlYWx0aF9mb29kKSAlPiUgYXBwbHkoMSwgbWluKSAlPiUgYXMubnVtZXJpYygpClNjaG9vbCRkaXN0X3RvX2Zhc3Rmb29kIDwtIHN0X2Rpc3RhbmNlKFNjaG9vbCwgZmFzdGZvb2QpICU+JSBhcHBseSgxLCBtaW4pICU+JSBhcy5udW1lcmljKCkKU2Nob29sJGRpc3RfdG9fcml2ZXIgPC0gc3RfZGlzdGFuY2UoU2Nob29sLCByaXZlcikgJT4lIGFwcGx5KDEsIG1pbikgJT4lIGFzLm51bWVyaWMoKQpTY2hvb2wkZGlzdF90b190cmFpbCA8LSBzdF9kaXN0YW5jZShTY2hvb2wsIHRyYWlsKSAlPiUgYXBwbHkoMSwgbWluKSAlPiUgYXMubnVtZXJpYygpCmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMg5ZCE5Y2A5YGl5bq355Sf5rS75qmf6IO95oyH5pW45qeL5bu677yI5bCH6Led6Zui6L2J5o+b54K6IDB+MSDnmoTmqJnmupbljJbjgIzliIbmlbjjgI3vvIkKbm9ybWFsaXplX3Njb3JlIDwtIGZ1bmN0aW9uKHgpIHsKICAxIC0gKHggLSBtaW4oeCkpIC8gKG1heCh4KSAtIG1pbih4KSkKfQoKIyDpgJ/po5/lupfot53pm6LmqJnmupbljJblh73mlbjvvIzot53pm6Lotorov5HliIbmlbjotorkvY7vvIjlgaXlurfmqZ/og73osqDpnaLmjIfmqJnvvIzmiaPliIbvvIkKbm9ybWFsaXplX3Njb3JlX2Zhc3Rmb29kIDwtIGZ1bmN0aW9uKHgpIHsKICAoeCAtIG1pbih4KSkgLyAobWF4KHgpIC0gbWluKHgpKSAgIyDot53pm6LotorlpKfliIbmlbjotorpq5jvvIjotorlgaXlurfvvIkKICB9CmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMg6YGL5YuV6aGe5oyH5qiZ5qiZ5rqW5YyW77yI6Led6Zui6LaK6L+R6LaK5aW977yJClNjaG9vbCRzY29yZV9wYXJrIDwtIG5vcm1hbGl6ZV9zY29yZShTY2hvb2wkZGlzdF90b19wYXJrKQpTY2hvb2wkc2NvcmVfdHJhaWwgPC0gbm9ybWFsaXplX3Njb3JlKFNjaG9vbCRkaXN0X3RvX3RyYWlsKQpTY2hvb2wkc2NvcmVfc3BvcnRzIDwtIG5vcm1hbGl6ZV9zY29yZShTY2hvb2wkZGlzdF90b19zcG9ydHMpClNjaG9vbCRzY29yZV9yaXZlciA8LSBub3JtYWxpemVfc2NvcmUoU2Nob29sJGRpc3RfdG9fcml2ZXIpCgojIOmjsumjn+mhnuaMh+aomeaomea6luWMlu+8iOWBpeW6t+mjsumjn+i3nemboui2iui/kei2iuWlve+8jOmAn+mjn+i3nemboui2iumBoOi2iuWlve+8iQpTY2hvb2wkc2NvcmVfaGVhbHRoIDwtIG5vcm1hbGl6ZV9zY29yZShTY2hvb2wkZGlzdF90b19oZWFsdGgpClNjaG9vbCRzY29yZV9mYXN0Zm9vZCA8LSBub3JtYWxpemVfc2NvcmVfZmFzdGZvb2QoU2Nob29sJGRpc3RfdG9fZmFzdGZvb2QpCgojIOmGq+eZgumhnuaMh+aomeaomea6luWMlu+8iOi3nemboui2iui/kei2iuWlve+8iQpTY2hvb2wkc2NvcmVfaG9zcGl0YWwgPC0gbm9ybWFsaXplX3Njb3JlKFNjaG9vbCRkaXN0X3RvX2hvc3BpdGFsKQpTY2hvb2wkc2NvcmVfY2xpbmljIDwtIG5vcm1hbGl6ZV9zY29yZShTY2hvb2wkZGlzdF90b19jbGluYykKCiMg5ZCI5oiQ5LiJ5aSn6aGe5oyH5pW477yI57Ch5Zau5bmz5Z2H77yJClNjaG9vbCRzcG9ydF9pbmRleCA8LSByb3dNZWFucyhzdF9kcm9wX2dlb21ldHJ5KFNjaG9vbClbLCBjKCJzY29yZV9wYXJrIiwgInNjb3JlX3RyYWlsIiwgInNjb3JlX3Nwb3J0cyIsICJzY29yZV9yaXZlciIpXSwgbmEucm0gPSBUUlVFKQpTY2hvb2wkZGlldF9pbmRleCA8LSByb3dNZWFucyhzdF9kcm9wX2dlb21ldHJ5KFNjaG9vbClbLCBjKCJzY29yZV9oZWFsdGgiLCAic2NvcmVfZmFzdGZvb2QiKV0sIG5hLnJtID0gVFJVRSkKU2Nob29sJG1lZGljYWxfaW5kZXggPC0gcm93TWVhbnMoc3RfZHJvcF9nZW9tZXRyeShTY2hvb2wpWywgYygic2NvcmVfaG9zcGl0YWwiLCAic2NvcmVfY2xpbmljIildLCBuYS5ybSA9IFRSVUUpCgojIOioiOeul+aVtOmrlOWBpeW6t+eUn+a0u+apn+iDveaMh+aVuO+8iOS4iemgheW5s+Wdh++8iQpTY2hvb2wkb3ZlcmFsbF9oZWFsdGhfaW5kZXggPC0gcm93TWVhbnMoc3RfZHJvcF9nZW9tZXRyeShTY2hvb2wpWywgYygic3BvcnRfaW5kZXgiLCAiZGlldF9pbmRleCIsICJtZWRpY2FsX2luZGV4IildLCBuYS5ybSA9IFRSVUUpCmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmxpYnJhcnkodG1hcCkKCnRtYXBfbW9kZSgidmlldyIpCgptYXBfc3BvcnQgPC0gdG1fc2hhcGUoU2Nob29sKSArCiAgdG1fZG90cyhjb2wgPSAic3BvcnRfaW5kZXgiLCBwYWxldHRlID0gIkJsdWVzIiwgc2l6ZSA9IDAuNSwgdGl0bGUgPSAi6YGL5YuV5oyH5pW4IikgKwogIHRtX2xheW91dCh0aXRsZSA9ICLlj7DljJfluILlnIvlsI/lkajpgorpgYvli5XnlJ/mtLvmqZ/og73mjIfmlbgiLCB0aXRsZS5zaXplID0gMS4yLCB0aXRsZS5wb3NpdGlvbiA9IGMoImNlbnRlciIsICJ0b3AiKSkKCm1hcF9kaWV0IDwtIHRtX3NoYXBlKFNjaG9vbCkgKwogIHRtX2RvdHMoY29sID0gImRpZXRfaW5kZXgiLCBwYWxldHRlID0gIk9yYW5nZXMiLCBzaXplID0gMC41LCB0aXRsZSA9ICLpo7Lpo5/mjIfmlbgiKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIuWPsOWMl+W4guWci+Wwj+WRqOmCiumjsumjn+eUn+a0u+apn+iDveaMh+aVuCIsIHRpdGxlLnNpemUgPSAxLjIsIHRpdGxlLnBvc2l0aW9uID0gYygiY2VudGVyIiwgInRvcCIpKQoKbWFwX21lZGljYWwgPC0gdG1fc2hhcGUoU2Nob29sKSArCiAgdG1fZG90cyhjb2wgPSAibWVkaWNhbF9pbmRleCIsIHBhbGV0dGUgPSAiR3JlZW5zIiwgc2l6ZSA9IDAuNSwgdGl0bGUgPSAi6Yar55mC5oyH5pW4IikgKwogIHRtX2xheW91dCh0aXRsZSA9ICLlj7DljJfluILlnIvlsI/lkajpgorphqvnmYLnlJ/mtLvmqZ/og73mjIfmlbgiLCB0aXRsZS5zaXplID0gMS4yLCB0aXRsZS5wb3NpdGlvbiA9IGMoImNlbnRlciIsICJ0b3AiKSkKCm1hcF9vdmVyYWxsIDwtIHRtX3NoYXBlKFNjaG9vbCkgKwogIHRtX2RvdHMoY29sID0gIm92ZXJhbGxfaGVhbHRoX2luZGV4IiwgcGFsZXR0ZSA9ICJSZFlsR24iLCBzaXplID0gMC41LCB0aXRsZSA9ICLmlbTpq5TlgaXlurfmjIfmlbgiKSArCiAgdG1fbGF5b3V0KHRpdGxlID0gIuWPsOWMl+W4guWci+Wwj+WRqOmCiuaVtOmrlOWBpeW6t+eUn+a0u+apn+iDveaMh+aVuCIsIHRpdGxlLnNpemUgPSAxLjIsIHRpdGxlLnBvc2l0aW9uID0gYygiY2VudGVyIiwgInRvcCIpKQoKdG1hcF9hcnJhbmdlKG1hcF9zcG9ydCwgbWFwX2RpZXQsIG1hcF9tZWRpY2FsLCBtYXBfb3ZlcmFsbCwgbmNvbCA9IDIpCmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9ClNjaG9vbCAlPiUKICBzbGljZV9taW4ob3JkZXJfYnkgPSBzcG9ydF9pbmRleCwgbiA9IDEwKSAlPiUKICBhcnJhbmdlKGRlc2Moc3BvcnRfaW5kZXgpKSAlPiUKICBtdXRhdGUoCiAgICByYW5rX2ZhY3RvciA9IGZhY3RvcihOYW1lLCBsZXZlbHMgPSBOYW1lKSwKICAgIGZpbGxfY29sb3IgPSBncmFkaWVudF9uX3BhbChjKCIjMDA2NkNDIiwgIiNDQ0U1RkYiKSkocmVzY2FsZShzcG9ydF9pbmRleCkpICAjIOa3seKGkua3ugogICkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmFua19mYWN0b3IsIHkgPSBzcG9ydF9pbmRleCwgZmlsbCA9IGZpbGxfY29sb3IpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF9pZGVudGl0eSgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoCiAgICB4ID0gIuWtuOagoeWQjeeosSIsCiAgICB5ID0gIuaMh+aVuOWIhuaVuCIsCiAgICB0aXRsZSA9ICLoh7rljJfluILlnIvlsI/lkajpgorpgYvli5XnlJ/mtLvmjIfmlbjlgJLmlbggMTAg5ZCNIgogICkgKwogIHRoZW1lKAogICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiUGluZ0ZhbmdTQy1TZW1pQm9sZCIpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICkKYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KU2Nob29sICU+JQogIHNsaWNlX21pbihvcmRlcl9ieSA9IGRpZXRfaW5kZXgsIG4gPSAxMCkgJT4lCiAgYXJyYW5nZShkZXNjKGRpZXRfaW5kZXgpKSAlPiUKICBtdXRhdGUoCiAgICByYW5rX2ZhY3RvciA9IGZhY3RvcihOYW1lLCBsZXZlbHMgPSBOYW1lKSwKICAgIGZpbGxfY29sb3IgPSBncmFkaWVudF9uX3BhbChjKCIjRkY2NjAwIiwgIiNGRkU1Q0MiKSkocmVzY2FsZShkaWV0X2luZGV4KSkgICMg5rex4oaS5re6CiAgKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByYW5rX2ZhY3RvciwgeSA9IGRpZXRfaW5kZXgsIGZpbGwgPSBmaWxsX2NvbG9yKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2ZpbGxfaWRlbnRpdHkoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBsYWJzKAogICAgeCA9ICLlrbjmoKHlkI3nqLEiLAogICAgeSA9ICLmjIfmlbjliIbmlbgiLAogICAgdGl0bGUgPSAi6Ie65YyX5biC5ZyL5bCP5ZGo6YKK6aOy6aOf55Sf5rS75oyH5pW45YCS5pW4IDEwIOWQjSIKICApICsKICB0aGVtZSgKICAgIHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIlBpbmdGYW5nU0MtU2VtaUJvbGQiKSwKICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gOCksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICApCmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9ClNjaG9vbCAlPiUKICBzbGljZV9taW4ob3JkZXJfYnkgPSBtZWRpY2FsX2luZGV4LCBuID0gMTApICU+JQogIGFycmFuZ2UoZGVzYyhtZWRpY2FsX2luZGV4KSkgJT4lCiAgbXV0YXRlKAogICAgcmFua19mYWN0b3IgPSBmYWN0b3IoTmFtZSwgbGV2ZWxzID0gTmFtZSksCiAgICBmaWxsX2NvbG9yID0gZ3JhZGllbnRfbl9wYWwoYygiIzAwOTkzMyIsICIjQ0NGRkNDIikpKHJlc2NhbGUobWVkaWNhbF9pbmRleCkpICAjIOa3seKGkua3ugogICkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmFua19mYWN0b3IsIHkgPSBtZWRpY2FsX2luZGV4LCBmaWxsID0gZmlsbF9jb2xvcikpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV9maWxsX2lkZW50aXR5KCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgbGFicygKICAgIHggPSAi5a245qCh5ZCN56ixIiwKICAgIHkgPSAi5oyH5pW45YiG5pW4IiwKICAgIHRpdGxlID0gIuiHuuWMl+W4guWci+Wwj+WRqOmCiumGq+eZgueUn+a0u+aMh+aVuOWAkuaVuCAxMCDlkI0iCiAgKSArCiAgdGhlbWUoCiAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJQaW5nRmFuZ1NDLVNlbWlCb2xkIiksCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpKQpgYGAKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9ClNjaG9vbCAlPiUKICBzbGljZV9taW4ob3JkZXJfYnkgPSBvdmVyYWxsX2hlYWx0aF9pbmRleCwgbiA9IDEwKSAlPiUKICBhcnJhbmdlKGRlc2Mob3ZlcmFsbF9oZWFsdGhfaW5kZXgpKSAlPiUKICBtdXRhdGUoCiAgICByYW5rX2ZhY3RvciA9IGZhY3RvcihOYW1lLCBsZXZlbHMgPSBOYW1lKSwKICAgIGZpbGxfY29sb3IgPSBncmFkaWVudF9uX3BhbChjKCIjRDczMDI3IiwgIiNGRUUwOEIiKSkocmVzY2FsZShvdmVyYWxsX2hlYWx0aF9pbmRleCkpICAjIOa3seKGkua3ugogICkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcmFua19mYWN0b3IsIHkgPSBvdmVyYWxsX2hlYWx0aF9pbmRleCwgZmlsbCA9IGZpbGxfY29sb3IpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfZmlsbF9pZGVudGl0eSgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGxhYnMoCiAgICB4ID0gIuWtuOagoeWQjeeosSIsCiAgICB5ID0gIuaMh+aVuOWIhuaVuCIsCiAgICB0aXRsZSA9ICLoh7rljJfluILlnIvlsI/lkajpgormlbTpq5TlgaXlurfnlJ/mtLvmjIfmlbjlgJLmlbggMTAg5ZCNIgogICkgKwogIHRoZW1lKAogICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiUGluZ0ZhbmdTQy1TZW1pQm9sZCIpLAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwKICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSkKYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KU2Nob29sX2Rpc3RyaWN0IDwtIHN0X2pvaW4oU2Nob29sLCB0YWlwZWksIGxlZnQgPSBUUlVFKQojIOenu+mZpOepuumWk+izh+ioiu+8jOS4puS+neihjOaUv+WNgOWIhue1hOioiOeul+W5s+Wdh+aMh+aVuApkaXN0cmljdF9zdW1tYXJ5IDwtIFNjaG9vbF9kaXN0cmljdCAlPiUKICBzdF9zZXRfZ2VvbWV0cnkoTlVMTCkgJT4lCiAgZ3JvdXBfYnkoVE9XTk5BTUUpICU+JQogIHN1bW1hcmlzZSgKICAgIHNwb3J0X2luZGV4X2F2ZyA9IG1lYW4oc3BvcnRfaW5kZXgsIG5hLnJtID0gVFJVRSksCiAgICBkaWV0X2luZGV4X2F2ZyA9IG1lYW4oZGlldF9pbmRleCwgbmEucm0gPSBUUlVFKSwKICAgIG1lZGljYWxfaW5kZXhfYXZnID0gbWVhbihtZWRpY2FsX2luZGV4LCBuYS5ybSA9IFRSVUUpLAogICAgb3ZlcmFsbF9oZWFsdGhfaW5kZXhfYXZnID0gbWVhbihvdmVyYWxsX2hlYWx0aF9pbmRleCwgbmEucm0gPSBUUlVFKQogICkKCiMg5bCH6KiI566X5aW955qE5bmz5Z2H5YC85ZCI5L215ZueIHRhaXBlaSDnqbrplpPos4fmlpkKdGFpcGVpIDwtIHRhaXBlaSAlPiUKICBsZWZ0X2pvaW4oZGlzdHJpY3Rfc3VtbWFyeSwgYnkgPSAiVE9XTk5BTUUiKQpgYGAKCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIOe5quWclgpnZ3Bsb3QodGFpcGVpKSArCiAgZ2VvbV9zZihhZXMoZmlsbCA9IG92ZXJhbGxfaGVhbHRoX2luZGV4X2F2ZyksIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKAogIGNvbG91cnMgPSBjKCIjRDczMDI3IiwgIiNGRUUwOEIiLCAiIzFBOTg1MCIpLAogIG5hLnZhbHVlID0gImdyZXk5MCIpICsKICBsYWJzKGZpbGwgPSAi5pW06auU5YGl5bq35oyH5pW45bmz5Z2HIiwgdGl0bGUgPSAi5Y+w5YyX5biC5ZCE6KGM5pS/5Y2A5LmL5pW06auU5YGl5bq35oyH5pW4IikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIlBpbmdGYW5nU0MtU2VtaUJvbGQiKSkKYGBgCgpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyDnuarlnJYKZ2dwbG90KHRhaXBlaSkgKwogIGdlb21fc2YoYWVzKGZpbGwgPSBzcG9ydF9pbmRleF9hdmcpLCBjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bigKICBjb2xvdXJzID0gYygiI0Q3MzAyNyIsICIjRkVFMDhCIiwgIiMxQTk4NTAiKSwKICBuYS52YWx1ZSA9ICJncmV5OTAiKSArCiAgbGFicyhmaWxsID0gIumBi+WLleWBpeW6t+aMh+aVuOW5s+WdhyIsIHRpdGxlID0gIuWPsOWMl+W4guWQhOihjOaUv+WNgOS5i+mBi+WLleWBpeW6t+aMh+aVuCIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLCB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJQaW5nRmFuZ1NDLVNlbWlCb2xkIikpCmBgYAoKYGBge3IgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMg5bu656uL6YSw5o6l6Zec5L+CCm5iIDwtIHBvbHkybmIodGFpcGVpKQoKIyDovYnmj5vngrrnqbrplpPmrIrph43nn6npmaPvvIhyb3ctc3RhbmRhcmRpemVk77yJCmx3IDwtIG5iMmxpc3R3KG5iLCBzdHlsZSA9ICJXIiwgemVyby5wb2xpY3kgPSBUUlVFKQoKIyDoqIjnrpcgTG9jYWwgTW9yYW4ncyBJCmxvY2FsX21vcmFuIDwtIGxvY2FsbW9yYW4odGFpcGVpJG92ZXJhbGxfaGVhbHRoX2luZGV4X2F2ZywgbHcsIHplcm8ucG9saWN5ID0gVFJVRSkKCiMg5Yqg5YWl57WQ5p6c5qyE5L2NCnRhaXBlaSRsb2NhbF9JIDwtIGxvY2FsX21vcmFuWywgIklpIl0KdGFpcGVpJGxvY2FsX0lfcCA8LSBsb2NhbF9tb3JhblssICJQcih6ICE9IEUoSWkpKSJdICAjIOaIlueUqOiHqueul+eahCBwIOWAvAoKIyDnlavlnJbvvJpMb2NhbCBNb3JhbidzIEkg5YC85YiG5biDCmdncGxvdCh0YWlwZWkpICsKICBnZW9tX3NmKGFlcyhmaWxsID0gbG9jYWxfSSksIGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKAogICAgY29sb3VycyA9IGMoIiNENzMwMjciLCAiI0ZFRTA4QiIsICIjMUE5ODUwIiksCiAgICBuYS52YWx1ZSA9ICJncmV5OTAiKSArCiAgbGFicyhmaWxsID0gIkxvY2FsIE1vcmFuJ3MgSSIsCiAgICAgICB0aXRsZSA9ICLlj7DljJfluILooYzmlL/ljYDkuYsgTG9jYWwgTW9yYW4ncyBJ77yI5pW06auU5YGl5bq35oyH5pW477yJIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiUGluZ0ZhbmdTQy1TZW1pQm9sZCIpKQoKYGBgCgoK