7.2 芝加哥汽車竊案、資料探索
7.2.1 讀進、轉換資料
# Load our data:
mvt = read.csv("data/mvt.csv", stringsAsFactors=FALSE)
str(mvt)
'data.frame': 191641 obs. of 3 variables:
$ Date : chr "12/31/12 23:15" "12/31/12 22:00" "12/31/12 22:00" "12/31/12 22:00" ...
$ Latitude : num 41.8 41.9 42 41.8 41.8 ...
$ Longitude: num -87.6 -87.7 -87.8 -87.7 -87.6 ...
# Convert the Date variable to a format that R will recognize:
mvt$Date = strptime(mvt$Date, format="%m/%d/%y %H:%M")
7.2.2 星期換算
# Extract the hour and the day of the week:
mvt$Weekday = weekdays(mvt$Date)
mvt$Hour = mvt$Date$hour
str(mvt)
'data.frame': 191641 obs. of 5 variables:
$ Date : POSIXlt, format: "2012-12-31 23:15:00" "2012-12-31 22:00:00" ...
$ Latitude : num 41.8 41.9 42 41.8 41.8 ...
$ Longitude: num -87.6 -87.7 -87.8 -87.7 -87.6 ...
$ Weekday : chr "Monday" "Monday" "Monday" "Monday" ...
$ Hour : int 23 22 22 22 21 20 20 20 19 18 ...
# Create a simple line plot - need the total number of crimes on
#each day of the week. We can get this information by creating a table:
table(mvt$Weekday)
Friday Monday Saturday Sunday Thursday Tuesday Wednesday
29284 27397 27118 26316 27319 26791 27416
# Save this table as a data frame:
WeekdayCounts = as.data.frame(table(mvt$Weekday))
str(WeekdayCounts)
'data.frame': 7 obs. of 2 variables:
$ Var1: Factor w/ 7 levels "Friday","Monday",..: 1 2 3 4 5 6 7
$ Freq: int 29284 27397 27118 26316 27319 26791 27416
7.2.3 簡單線圖
# Load the ggplot2 library:
library(ggplot2)
ggplot(WeekdayCounts, aes(x=Var1, y=Freq)) +
geom_line(aes(group=1))

7.2.4 星期類別順序
# Make the "Var1" variable an ORDERED factor variable
WeekdayCounts$Var1 = factor(WeekdayCounts$Var1, ordered=TRUE,
levels=c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday","Saturday"))
# Try again:
ggplot(WeekdayCounts, aes(x=Var1, y=Freq)) +
geom_line(aes(group=1))

7.2.5 改變X、Y軸標題
# Change our x and y labels:
ggplot(WeekdayCounts, aes(x=Var1, y=Freq)) +
geom_line(aes(group=1), alpha=0.3) +
xlab("Day of the Week") + ylab("Total Motor Vehicle Thefts")

7.2.5 七天、24小時
# VIDEO 4 - Adding the Hour of the Day
# Create a counts table for the weekday and hour:
table(mvt$Weekday, mvt$Hour)
0 1 2 3 4 5 6 7 8 9 10 11 12
Friday 1873 932 743 560 473 602 839 1203 1268 1286 938 822 1207
Monday 1900 825 712 527 415 542 772 1123 1323 1235 971 737 1129
Saturday 2050 1267 985 836 652 508 541 650 858 1039 946 789 1204
Sunday 2028 1236 1019 838 607 461 478 483 615 864 884 787 1192
Thursday 1856 816 696 508 400 534 799 1135 1298 1301 932 731 1093
Tuesday 1691 777 603 464 414 520 845 1118 1175 1174 948 786 1108
Wednesday 1814 790 619 469 396 561 862 1140 1329 1237 947 763 1225
13 14 15 16 17 18 19 20 21 22 23
Friday 857 937 1140 1165 1318 1623 1652 1736 1881 2308 1921
Monday 824 958 1059 1136 1252 1518 1503 1622 1815 2009 1490
Saturday 767 963 1086 1055 1084 1348 1390 1570 1702 2078 1750
Sunday 789 959 1037 1083 1160 1389 1342 1706 1696 2079 1584
Thursday 752 831 1044 1131 1258 1510 1537 1668 1776 2134 1579
Tuesday 762 908 1071 1090 1274 1553 1496 1696 1816 2044 1458
Wednesday 804 863 1075 1076 1289 1580 1507 1718 1748 2093 1511
# Save this to a data frame:
DayHourCounts = as.data.frame(table(mvt$Weekday, mvt$Hour))
str(DayHourCounts)
'data.frame': 168 obs. of 3 variables:
$ Var1: Factor w/ 7 levels "Friday","Monday",..: 1 2 3 4 5 6 7 1 2 3 ...
$ Var2: Factor w/ 24 levels "0","1","2","3",..: 1 1 1 1 1 1 1 2 2 2 ...
$ Freq: int 1873 1900 2050 2028 1856 1691 1814 932 825 1267 ...
# Convert the second variable, Var2, to numbers and call it Hour:
DayHourCounts$Hour = as.numeric(as.character(DayHourCounts$Var2))
7.2.6 畫出 7 x 24 趨勢線圖
# Create out plot:
ggplot(DayHourCounts, aes(x=Hour, y=Freq)) +
geom_line(aes(group=Var1))

# Change the colors
ggplot(DayHourCounts, aes(x=Hour, y=Freq)) +
geom_line(aes(group=Var1, color=Var1), size=2)

# 區分周末、周間
DayHourCounts$Type = ifelse(
(DayHourCounts$Var1 == "Sunday") | (DayHourCounts$Var1 == "Saturday"),
"Weekend", "Weekday")
# Redo our plot, this time coloring by Type:
ggplot(DayHourCounts, aes(x=Hour, y=Freq)) +
geom_line(aes(group=Var1, color=Type), size=2)

# Make the lines a little transparent:
ggplot(DayHourCounts, aes(x=Hour, y=Freq)) +
geom_line(aes(group=Var1, color=Type), size=2, alpha=0.5)

7.2.6 畫出 7 x 24 熱圖
# 星期類別順序重整
DayHourCounts$Var1 = factor(DayHourCounts$Var1, ordered=TRUE,
levels=c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday"))
# Make a heatmap:
ggplot(DayHourCounts, aes(x = Hour, y = Var1)) +
geom_tile(aes(fill = Freq))

# Change the label on the legend, and get rid of the y-label:
ggplot(DayHourCounts, aes(x = Hour, y = Var1)) +
geom_tile(aes(fill = Freq)) +
scale_fill_gradient(name="Total MV Thefts") +
theme(axis.title.y = element_blank())

# Change the color scheme
ggplot(DayHourCounts, aes(x = Hour, y = Var1)) +
geom_tile(aes(fill = Freq)) +
scale_fill_gradient(name="Total MV Thefts", low="white", high="red") +
theme(axis.title.y = element_blank())

7.2.7 互動式熱圖
table(format(mvt$Date,'%H'), format(mvt$Date,'%w'))%>% t %>%
heatmap(NA,NA,scale='none',col=cm.colors(25))

7.2 芝加哥汽車竊案、地圖套製
7.2.8 透過 maps 和 ggmap 套件抓取地圖
library(maps)
library(ggmap)
# Load a map of Chicago into R:
chicago = get_map(location = "chicago", zoom = 11)
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=chicago&zoom=11&size=640x640&scale=2&maptype=terrain&language=en-EN&sensor=false
Information from URL : http://maps.googleapis.com/maps/api/geocode/json?address=chicago&sensor=false
# Look at the map
chicago = ggmap(chicago)
chicago

# 可以畫高雄市嗎 ?
ggmap(get_map(location = "kaohsiung", zoom = 12))
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=kaohsiung&zoom=12&size=640x640&scale=2&maptype=terrain&language=en-EN&sensor=false
Information from URL : http://maps.googleapis.com/maps/api/geocode/json?address=kaohsiung&sensor=false

7.2.9 標記單一事件
# Plot the first 100 motor vehicle thefts:
chicago + geom_point(
data = mvt[1:100,], aes(x = Longitude, y = Latitude))

7.2.9 依座標集收事件
# Round our latitude and longitude to 2 digits of accuracy,
# and create a crime counts data frame for each area:
LatLonCounts = as.data.frame(table(round(mvt$Longitude,2),
round(mvt$Latitude,2)))
str(LatLonCounts)
'data.frame': 1638 obs. of 3 variables:
$ Var1: Factor w/ 42 levels "-87.93","-87.92",..: 1 2 3 4 5 6 7 8 9 10 ...
$ Var2: Factor w/ 39 levels "41.64","41.65",..: 1 1 1 1 1 1 1 1 1 1 ...
$ Freq: int 0 0 0 0 0 0 0 0 0 0 ...
# Convert our Longitude and Latitude variable to numbers:
LatLonCounts$Long = as.numeric(as.character(LatLonCounts$Var1))
LatLonCounts$Lat = as.numeric(as.character(LatLonCounts$Var2))
# Plot these points on our map:
chicago + geom_point(data = LatLonCounts,
aes(x = Long, y = Lat, color = Freq, size=Freq))

# Change the color scheme:
chicago + geom_point(data = LatLonCounts,
aes(x=Long, y=Lat, color=Freq, size=Freq)) +
scale_colour_gradient(low="yellow", high="red")

7.2.10 格狀圖
# We can also use the geom_tile geometry
chicago + geom_tile(data = LatLonCounts,
aes(x = Long, y = Lat, alpha = Freq),
fill="red")

# 移除沒有事件的區格
LatLonCounts2 = subset(LatLonCounts, Freq > 0)
chicago + geom_tile(data = LatLonCounts2,
aes(x = Long, y = Lat, alpha = Freq),
fill="red")

7.2.11 事件密度圖
# density plot
chicago + stat_density_2d(data=mvt,
aes(x=Longitude, y=Latitude, alpha=..level..),
fill='orange', color='pink', size=0.01, bins=8, geom='polygon') +
scale_alpha(range = c(0.05, 0.45))

7.2 槍枝持有率與謀殺比率
# VIDEO 6 - Geographical Map on US
# Load our data:
murders = read.csv("data/murders.csv")
str(murders)
'data.frame': 51 obs. of 6 variables:
$ State : Factor w/ 51 levels "Alabama","Alaska",..: 1 2 3 4 5 6 7 8 9 10 ...
$ Population : int 4779736 710231 6392017 2915918 37253956 5029196 3574097 897934 601723 19687653 ...
$ PopulationDensity: num 94.65 1.26 57.05 56.43 244.2 ...
$ Murders : int 199 31 352 130 1811 117 131 48 131 987 ...
$ GunMurders : int 135 19 232 93 1257 65 97 38 99 669 ...
$ GunOwnership : num 0.517 0.578 0.311 0.553 0.213 0.347 0.167 0.255 0.036 0.245 ...
7.2.12 Read and Plot US Map
# Load the map of the US
statesMap = map_data("state")
str(statesMap)
'data.frame': 15537 obs. of 6 variables:
$ long : num -87.5 -87.5 -87.5 -87.5 -87.6 ...
$ lat : num 30.4 30.4 30.4 30.3 30.3 ...
$ group : num 1 1 1 1 1 1 1 1 1 1 ...
$ order : int 1 2 3 4 5 6 7 8 9 10 ...
$ region : chr "alabama" "alabama" "alabama" "alabama" ...
$ subregion: chr NA NA NA NA ...
# Plot the map:
ggplot(statesMap, aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "white", color = "black")

7.2.13 Merge the Dataframes (stateMap and murders)
# Create a new variable called region with the lowercase names to
# match the statesMap:
murders$region = tolower(murders$State)
# Join the statesMap data and the murders data into one dataframe:
murderMap = merge(statesMap, murders, by="region")
str(murderMap)
'data.frame': 15537 obs. of 12 variables:
$ region : chr "alabama" "alabama" "alabama" "alabama" ...
$ long : num -87.5 -87.5 -87.5 -87.5 -87.6 ...
$ lat : num 30.4 30.4 30.4 30.3 30.3 ...
$ group : num 1 1 1 1 1 1 1 1 1 1 ...
$ order : int 1 2 3 4 5 6 7 8 9 10 ...
$ subregion : chr NA NA NA NA ...
$ State : Factor w/ 51 levels "Alabama","Alaska",..: 1 1 1 1 1 1 1 1 1 1 ...
$ Population : int 4779736 4779736 4779736 4779736 4779736 4779736 4779736 4779736 4779736 4779736 ...
$ PopulationDensity: num 94.7 94.7 94.7 94.7 94.7 ...
$ Murders : int 199 199 199 199 199 199 199 199 199 199 ...
$ GunMurders : int 135 135 135 135 135 135 135 135 135 135 ...
$ GunOwnership : num 0.517 0.517 0.517 0.517 0.517 0.517 0.517 0.517 0.517 0.517 ...
7.2.14 Map-1: No. Murders per State
# Plot the number of murder on our map of the United States:
ggplot(murderMap, aes(x = long, y = lat, group = group, fill = Murders)) +
geom_polygon(color = "black") +
scale_fill_gradient(low = "black", high = "red", guide = "legend")

7.2.15 Map-2: Populations per State
# Plot a map of the population:
ggplot(murderMap, aes(x = long, y = lat, group = group, fill = Population)) +
geom_polygon(color = "black") +
scale_fill_gradient(low = "black", high = "red", guide = "legend")

7.2.16 Map-3: No. Murders per 100K Population
# Create a new variable that is the number of murders per 100,000 population:
murderMap$MurderRate = murderMap$Murders / murderMap$Population * 100000
# Redo our plot with murder rate:
ggplot(murderMap, aes(x = long, y = lat, group = group, fill = MurderRate)) +
geom_polygon(color = "black") +
scale_fill_gradient(low = "black", high = "red", guide = "legend")

7.2.17 Map-4: No. Murders per 100K Population with Filter
# Redo the plot, Cap the Murderrate at 10:
ggplot(murderMap, aes(x = long, y = lat, group = group, fill = MurderRate)) +
geom_polygon(color = "black") +
scale_fill_gradient(low = "black", high = "red",
guide = "legend", limits = c(0,10))

7.2.18 Map-5: Gun Ownership (%) by States
ggplot(murderMap,aes(x=long,y=lat,group=group,fill=GunOwnership)) +
geom_polygon(color = "black") +
scale_fill_gradient(low = "black", high = "red", guide = "legend")

7.2.19 Gun Ownership (%) by States
tapply(murderMap$GunOwnership, murderMap$region, mean) %>% sort %>%
barplot(las=2, cex.names=0.6, main="Averge GunOwnerShip (%)")

LS0tDQp0aXRsZTogIkFTNy0wQiDlnLDnkIbos4fmlpnoo73lnJYiDQphdXRob3I6ICLljZPpm43nhLYgRDk5NDAxMDAwMSwgMjAxOC8wNy8yMiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCjxicj4NCg0KYGBge3IgZWNobz1ULCBtZXNzYWdlPUYsIGNhY2hlPUYsIHdhcm5pbmc9Rn0NCnJtKGxpc3Q9bHMoYWxsPVQpKQ0Kb3B0aW9ucyhkaWdpdHM9NCwgc2NpcGVuPTEyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkobWFwcykNCmxpYnJhcnkoZ2dtYXApDQpgYGANCg0KLSAtIC0NCg0KIyMjIDcuMiDoip3liqDlk6Xmsb3ou4rnq4rmoYjjgIHos4fmlpnmjqLntKINCg0KIyMjIyMgNy4yLjEg6K6A6YCy44CB6L2J5o+b6LOH5paZDQpgYGB7cn0NCiMgTG9hZCBvdXIgZGF0YToNCm12dCA9IHJlYWQuY3N2KCJkYXRhL212dC5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQ0Kc3RyKG12dCkNCg0KIyBDb252ZXJ0IHRoZSBEYXRlIHZhcmlhYmxlIHRvIGEgZm9ybWF0IHRoYXQgUiB3aWxsIHJlY29nbml6ZToNCm12dCREYXRlID0gc3RycHRpbWUobXZ0JERhdGUsIGZvcm1hdD0iJW0vJWQvJXkgJUg6JU0iKQ0KYGBgDQoNCiMjIyMjIDcuMi4yIOaYn+acn+aPm+eulw0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGhvdXIgYW5kIHRoZSBkYXkgb2YgdGhlIHdlZWs6DQptdnQkV2Vla2RheSA9IHdlZWtkYXlzKG12dCREYXRlKQ0KbXZ0JEhvdXIgPSBtdnQkRGF0ZSRob3VyDQpzdHIobXZ0KQ0KDQojIENyZWF0ZSBhIHNpbXBsZSBsaW5lIHBsb3QgLSBuZWVkIHRoZSB0b3RhbCBudW1iZXIgb2YgY3JpbWVzIG9uIA0KI2VhY2ggZGF5IG9mIHRoZSB3ZWVrLiBXZSBjYW4gZ2V0IHRoaXMgaW5mb3JtYXRpb24gYnkgY3JlYXRpbmcgYSB0YWJsZToNCnRhYmxlKG12dCRXZWVrZGF5KQ0KDQojIFNhdmUgdGhpcyB0YWJsZSBhcyBhIGRhdGEgZnJhbWU6DQpXZWVrZGF5Q291bnRzID0gYXMuZGF0YS5mcmFtZSh0YWJsZShtdnQkV2Vla2RheSkpDQpzdHIoV2Vla2RheUNvdW50cykgDQpgYGANCg0KIyMjIyMgNy4yLjMg57Ch5Zau57ea5ZyWDQpgYGB7cn0NCiMgTG9hZCB0aGUgZ2dwbG90MiBsaWJyYXJ5Og0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KFdlZWtkYXlDb3VudHMsIGFlcyh4PVZhcjEsIHk9RnJlcSkpICsgDQogIGdlb21fbGluZShhZXMoZ3JvdXA9MSkpICANCmBgYA0KDQojIyMjIyA3LjIuNCDmmJ/mnJ/poZ7liKXpoIbluo8NCmBgYHtyfQ0KIyBNYWtlIHRoZSAiVmFyMSIgdmFyaWFibGUgYW4gT1JERVJFRCBmYWN0b3IgdmFyaWFibGUNCldlZWtkYXlDb3VudHMkVmFyMSA9IGZhY3RvcihXZWVrZGF5Q291bnRzJFZhcjEsIG9yZGVyZWQ9VFJVRSwgDQogIGxldmVscz1jKCJTdW5kYXkiLCAiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgDQogICAgICAgICAgICJGcmlkYXkiLCJTYXR1cmRheSIpKQ0KDQojIFRyeSBhZ2FpbjoNCmdncGxvdChXZWVrZGF5Q291bnRzLCBhZXMoeD1WYXIxLCB5PUZyZXEpKSArIA0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPTEpKQ0KYGBgDQoNCiMjIyMjIDcuMi41IOaUueiuiljjgIFZ6Lu45qiZ6aGMDQpgYGB7cn0NCiMgQ2hhbmdlIG91ciB4IGFuZCB5IGxhYmVsczoNCmdncGxvdChXZWVrZGF5Q291bnRzLCBhZXMoeD1WYXIxLCB5PUZyZXEpKSArIA0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPTEpLCBhbHBoYT0wLjMpICsgDQogIHhsYWIoIkRheSBvZiB0aGUgV2VlayIpICsgeWxhYigiVG90YWwgTW90b3IgVmVoaWNsZSBUaGVmdHMiKQ0KYGBgDQoNCiMjIyMjIDcuMi41IOS4g+WkqeOAgTI05bCP5pmCDQpgYGB7cn0NCiMgVklERU8gNCAtIEFkZGluZyB0aGUgSG91ciBvZiB0aGUgRGF5DQojIENyZWF0ZSBhIGNvdW50cyB0YWJsZSBmb3IgdGhlIHdlZWtkYXkgYW5kIGhvdXI6DQp0YWJsZShtdnQkV2Vla2RheSwgbXZ0JEhvdXIpDQoNCiMgU2F2ZSB0aGlzIHRvIGEgZGF0YSBmcmFtZToNCkRheUhvdXJDb3VudHMgPSBhcy5kYXRhLmZyYW1lKHRhYmxlKG12dCRXZWVrZGF5LCBtdnQkSG91cikpDQpzdHIoRGF5SG91ckNvdW50cykNCg0KIyBDb252ZXJ0IHRoZSBzZWNvbmQgdmFyaWFibGUsIFZhcjIsIHRvIG51bWJlcnMgYW5kIGNhbGwgaXQgSG91cjoNCkRheUhvdXJDb3VudHMkSG91ciA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKERheUhvdXJDb3VudHMkVmFyMikpDQpgYGANCg0KIyMjIyMgNy4yLjYg55Wr5Ye6IDcgeCAyNCDotqjli6Lnt5rlnJYgDQpgYGB7cn0NCiMgQ3JlYXRlIG91dCBwbG90Og0KZ2dwbG90KERheUhvdXJDb3VudHMsIGFlcyh4PUhvdXIsIHk9RnJlcSkpICsNCiAgZ2VvbV9saW5lKGFlcyhncm91cD1WYXIxKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ2hhbmdlIHRoZSBjb2xvcnMNCmdncGxvdChEYXlIb3VyQ291bnRzLCBhZXMoeD1Ib3VyLCB5PUZyZXEpKSArIA0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPVZhcjEsIGNvbG9yPVZhcjEpLCBzaXplPTIpDQpgYGANCg0KYGBge3J9DQojIOWNgOWIhuWRqOacq+OAgeWRqOmWkw0KRGF5SG91ckNvdW50cyRUeXBlID0gaWZlbHNlKA0KICAoRGF5SG91ckNvdW50cyRWYXIxID09ICJTdW5kYXkiKSB8IChEYXlIb3VyQ291bnRzJFZhcjEgPT0gIlNhdHVyZGF5IiksIA0KICAiV2Vla2VuZCIsICJXZWVrZGF5IikNCg0KIyBSZWRvIG91ciBwbG90LCB0aGlzIHRpbWUgY29sb3JpbmcgYnkgVHlwZToNCmdncGxvdChEYXlIb3VyQ291bnRzLCBhZXMoeD1Ib3VyLCB5PUZyZXEpKSArIA0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPVZhcjEsIGNvbG9yPVR5cGUpLCBzaXplPTIpIA0KYGBgDQoNCmBgYHtyfQ0KIyBNYWtlIHRoZSBsaW5lcyBhIGxpdHRsZSB0cmFuc3BhcmVudDoNCmdncGxvdChEYXlIb3VyQ291bnRzLCBhZXMoeD1Ib3VyLCB5PUZyZXEpKSArIA0KICBnZW9tX2xpbmUoYWVzKGdyb3VwPVZhcjEsIGNvbG9yPVR5cGUpLCBzaXplPTIsIGFscGhhPTAuNSkgDQpgYGANCg0KIyMjIyMgNy4yLjYg55Wr5Ye6IDcgeCAyNCDnhrHlnJYNCg0KYGBge3J9DQojIOaYn+acn+mhnuWIpemghuW6j+mHjeaVtA0KRGF5SG91ckNvdW50cyRWYXIxID0gZmFjdG9yKERheUhvdXJDb3VudHMkVmFyMSwgb3JkZXJlZD1UUlVFLCANCiAgbGV2ZWxzPWMoIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIsICJGcmlkYXkiLCANCiAgICAgICAgICAgIlNhdHVyZGF5IiwgIlN1bmRheSIpKQ0KDQojIE1ha2UgYSBoZWF0bWFwOg0KZ2dwbG90KERheUhvdXJDb3VudHMsIGFlcyh4ID0gSG91ciwgeSA9IFZhcjEpKSArIA0KICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBGcmVxKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ2hhbmdlIHRoZSBsYWJlbCBvbiB0aGUgbGVnZW5kLCBhbmQgZ2V0IHJpZCBvZiB0aGUgeS1sYWJlbDoNCmdncGxvdChEYXlIb3VyQ291bnRzLCBhZXMoeCA9IEhvdXIsIHkgPSBWYXIxKSkgKyANCiAgZ2VvbV90aWxlKGFlcyhmaWxsID0gRnJlcSkpICsgDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobmFtZT0iVG90YWwgTVYgVGhlZnRzIikgKyANCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDaGFuZ2UgdGhlIGNvbG9yIHNjaGVtZQ0KZ2dwbG90KERheUhvdXJDb3VudHMsIGFlcyh4ID0gSG91ciwgeSA9IFZhcjEpKSArIA0KICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBGcmVxKSkgKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudChuYW1lPSJUb3RhbCBNViBUaGVmdHMiLCBsb3c9IndoaXRlIiwgaGlnaD0icmVkIikgKyANCiAgdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpKQ0KYGBgDQoNCiMjIyMjIDcuMi43IOS6kuWLleW8j+eGseWclg0KYGBge3J9DQp0YWJsZShmb3JtYXQobXZ0JERhdGUsJyVIJyksIGZvcm1hdChtdnQkRGF0ZSwnJXcnKSklPiUgdCAlPiUgDQogIGhlYXRtYXAoTkEsTkEsc2NhbGU9J25vbmUnLGNvbD1jbS5jb2xvcnMoMjUpKQ0KYGBgDQo8YnI+DQoNCi0gLSAtDQoNCiMjIyA3LjIg6Iqd5Yqg5ZOl5rG96LuK56uK5qGI44CB5Zyw5ZyW5aWX6KO9DQoNCiMjIyMjIDcuMi44IOmAj+mBjiBgbWFwc2Ag5ZKMIGBnZ21hcGAg5aWX5Lu25oqT5Y+W5Zyw5ZyWDQpgYGB7cn0NCmxpYnJhcnkobWFwcykNCmxpYnJhcnkoZ2dtYXApDQoNCiMgTG9hZCBhIG1hcCBvZiBDaGljYWdvIGludG8gUjoNCmNoaWNhZ28gPSBnZXRfbWFwKGxvY2F0aW9uID0gImNoaWNhZ28iLCB6b29tID0gMTEpDQoNCiMgTG9vayBhdCB0aGUgbWFwDQpjaGljYWdvID0gZ2dtYXAoY2hpY2FnbykNCmNoaWNhZ28NCmBgYA0KDQpgYGB7cn0NCiMg5Y+v5Lul55Wr6auY6ZuE5biC5ZeOID8gDQpnZ21hcChnZXRfbWFwKGxvY2F0aW9uID0gImthb2hzaXVuZyIsIHpvb20gPSAxMikpDQpgYGANCg0KIyMjIyMgNy4yLjkg5qiZ6KiY5Zau5LiA5LqL5Lu2DQpgYGB7cn0NCiMgUGxvdCB0aGUgZmlyc3QgMTAwIG1vdG9yIHZlaGljbGUgdGhlZnRzOg0KY2hpY2FnbyArIGdlb21fcG9pbnQoDQogIGRhdGEgPSBtdnRbMToxMDAsXSwgYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSkpDQpgYGANCg0KIyMjIyMgNy4yLjkg5L6d5bqn5qiZ6ZuG5pS25LqL5Lu2DQpgYGB7cn0NCiMgUm91bmQgb3VyIGxhdGl0dWRlIGFuZCBsb25naXR1ZGUgdG8gMiBkaWdpdHMgb2YgYWNjdXJhY3ksIA0KIyBhbmQgY3JlYXRlIGEgY3JpbWUgY291bnRzIGRhdGEgZnJhbWUgZm9yIGVhY2ggYXJlYToNCkxhdExvbkNvdW50cyA9IGFzLmRhdGEuZnJhbWUodGFibGUocm91bmQobXZ0JExvbmdpdHVkZSwyKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKG12dCRMYXRpdHVkZSwyKSkpDQpzdHIoTGF0TG9uQ291bnRzKQ0KDQojIENvbnZlcnQgb3VyIExvbmdpdHVkZSBhbmQgTGF0aXR1ZGUgdmFyaWFibGUgdG8gbnVtYmVyczoNCkxhdExvbkNvdW50cyRMb25nID0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoTGF0TG9uQ291bnRzJFZhcjEpKQ0KTGF0TG9uQ291bnRzJExhdCA9IGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKExhdExvbkNvdW50cyRWYXIyKSkNCg0KIyBQbG90IHRoZXNlIHBvaW50cyBvbiBvdXIgbWFwOg0KY2hpY2FnbyArIGdlb21fcG9pbnQoZGF0YSA9IExhdExvbkNvdW50cywgDQogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IExvbmcsIHkgPSBMYXQsIGNvbG9yID0gRnJlcSwgc2l6ZT1GcmVxKSkNCmBgYA0KDQpgYGB7cn0NCiMgQ2hhbmdlIHRoZSBjb2xvciBzY2hlbWU6DQpjaGljYWdvICsgZ2VvbV9wb2ludChkYXRhID0gTGF0TG9uQ291bnRzLCANCiAgICAgICAgICAgICAgICAgICAgIGFlcyh4PUxvbmcsIHk9TGF0LCBjb2xvcj1GcmVxLCBzaXplPUZyZXEpKSArIA0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93PSJ5ZWxsb3ciLCBoaWdoPSJyZWQiKQ0KYGBgDQoNCiMjIyMjIDcuMi4xMCDmoLzni4DlnJYNCmBgYHtyfQ0KIyBXZSBjYW4gYWxzbyB1c2UgdGhlIGdlb21fdGlsZSBnZW9tZXRyeQ0KY2hpY2FnbyArIGdlb21fdGlsZShkYXRhID0gTGF0TG9uQ291bnRzLCANCiAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBMb25nLCB5ID0gTGF0LCBhbHBoYSA9IEZyZXEpLCANCiAgICAgICAgICAgICAgICAgICAgZmlsbD0icmVkIikNCmBgYA0KDQpgYGB7cn0NCiMg56e76Zmk5rKS5pyJ5LqL5Lu255qE5Y2A5qC8DQpMYXRMb25Db3VudHMyID0gc3Vic2V0KExhdExvbkNvdW50cywgRnJlcSA+IDApDQpjaGljYWdvICsgZ2VvbV90aWxlKGRhdGEgPSBMYXRMb25Db3VudHMyLCANCiAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBMb25nLCB5ID0gTGF0LCBhbHBoYSA9IEZyZXEpLCANCiAgICAgICAgICAgICAgICAgICAgZmlsbD0icmVkIikNCmBgYA0KDQojIyMjIyA3LjIuMTEg5LqL5Lu25a+G5bqm5ZyWDQpgYGB7cn0NCiMgZGVuc2l0eSBwbG90DQpjaGljYWdvICsgc3RhdF9kZW5zaXR5XzJkKGRhdGE9bXZ0LCANCiAgICBhZXMoeD1Mb25naXR1ZGUsIHk9TGF0aXR1ZGUsIGFscGhhPS4ubGV2ZWwuLiksIA0KICAgIGZpbGw9J29yYW5nZScsIGNvbG9yPSdwaW5rJywgc2l6ZT0wLjAxLCBiaW5zPTgsIGdlb209J3BvbHlnb24nKSArDQogIHNjYWxlX2FscGhhKHJhbmdlID0gYygwLjA1LCAwLjQ1KSkNCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQojIyMgNy4yIOanjeaeneaMgeacieeOh+iIh+isgOauuuavlOeOhw0KDQpgYGB7cn0NCiMgVklERU8gNiAtIEdlb2dyYXBoaWNhbCBNYXAgb24gVVMNCiMgTG9hZCBvdXIgZGF0YToNCm11cmRlcnMgPSByZWFkLmNzdigiZGF0YS9tdXJkZXJzLmNzdiIpDQpzdHIobXVyZGVycykNCmBgYA0KDQojIyMjIyA3LjIuMTIgUmVhZCBhbmQgUGxvdCBVUyBNYXANCmBgYHtyfQ0KIyBMb2FkIHRoZSBtYXAgb2YgdGhlIFVTDQpzdGF0ZXNNYXAgPSBtYXBfZGF0YSgic3RhdGUiKQ0Kc3RyKHN0YXRlc01hcCkNCg0KIyBQbG90IHRoZSBtYXA6DQpnZ3Bsb3Qoc3RhdGVzTWFwLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApKSArIA0KICBnZW9tX3BvbHlnb24oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikgDQoNCmBgYA0KDQojIyMjIyA3LjIuMTMgTWVyZ2UgdGhlIERhdGFmcmFtZXMgKGBzdGF0ZU1hcGAgYW5kIGBtdXJkZXJzYCkNCmBgYHtyfQ0KIyBDcmVhdGUgYSBuZXcgdmFyaWFibGUgY2FsbGVkIHJlZ2lvbiB3aXRoIHRoZSBsb3dlcmNhc2UgbmFtZXMgdG8gDQojIG1hdGNoIHRoZSBzdGF0ZXNNYXA6DQptdXJkZXJzJHJlZ2lvbiA9IHRvbG93ZXIobXVyZGVycyRTdGF0ZSkNCg0KIyBKb2luIHRoZSBzdGF0ZXNNYXAgZGF0YSBhbmQgdGhlIG11cmRlcnMgZGF0YSBpbnRvIG9uZSBkYXRhZnJhbWU6DQptdXJkZXJNYXAgPSBtZXJnZShzdGF0ZXNNYXAsIG11cmRlcnMsIGJ5PSJyZWdpb24iKQ0Kc3RyKG11cmRlck1hcCkNCmBgYA0KDQojIyMjIyA3LjIuMTQgTWFwLTE6IE5vLiBNdXJkZXJzIHBlciBTdGF0ZQ0KYGBge3J9DQojIFBsb3QgdGhlIG51bWJlciBvZiBtdXJkZXIgb24gb3VyIG1hcCBvZiB0aGUgVW5pdGVkIFN0YXRlczoNCmdncGxvdChtdXJkZXJNYXAsIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IE11cmRlcnMpKSArIA0KICBnZW9tX3BvbHlnb24oY29sb3IgPSAiYmxhY2siKSArIA0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJibGFjayIsIGhpZ2ggPSAicmVkIiwgZ3VpZGUgPSAibGVnZW5kIikNCmBgYA0KDQojIyMjIyA3LjIuMTUgTWFwLTI6IFBvcHVsYXRpb25zIHBlciBTdGF0ZQ0KYGBge3J9DQojIFBsb3QgYSBtYXAgb2YgdGhlIHBvcHVsYXRpb246DQpnZ3Bsb3QobXVyZGVyTWFwLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBQb3B1bGF0aW9uKSkgKyANCiAgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImJsYWNrIikgKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiYmxhY2siLCBoaWdoID0gInJlZCIsIGd1aWRlID0gImxlZ2VuZCIpDQpgYGANCg0KIyMjIyMgNy4yLjE2IE1hcC0zOiBOby4gTXVyZGVycyBwZXIgMTAwSyBQb3B1bGF0aW9uDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbmV3IHZhcmlhYmxlIHRoYXQgaXMgdGhlIG51bWJlciBvZiBtdXJkZXJzIHBlciAxMDAsMDAwIHBvcHVsYXRpb246DQptdXJkZXJNYXAkTXVyZGVyUmF0ZSA9IG11cmRlck1hcCRNdXJkZXJzIC8gbXVyZGVyTWFwJFBvcHVsYXRpb24gKiAxMDAwMDANCg0KIyBSZWRvIG91ciBwbG90IHdpdGggbXVyZGVyIHJhdGU6DQpnZ3Bsb3QobXVyZGVyTWFwLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXAsIGZpbGwgPSBNdXJkZXJSYXRlKSkgKyANCiAgZ2VvbV9wb2x5Z29uKGNvbG9yID0gImJsYWNrIikgKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiYmxhY2siLCBoaWdoID0gInJlZCIsIGd1aWRlID0gImxlZ2VuZCIpDQpgYGANCg0KIyMjIyMgNy4yLjE3IE1hcC00OiBOby4gTXVyZGVycyBwZXIgMTAwSyBQb3B1bGF0aW9uIHdpdGggRmlsdGVyDQpgYGB7cn0NCiMgUmVkbyB0aGUgcGxvdCwgQ2FwIHRoZSBNdXJkZXJyYXRlIGF0IDEwOg0KZ2dwbG90KG11cmRlck1hcCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwLCBmaWxsID0gTXVyZGVyUmF0ZSkpICsgDQogIGdlb21fcG9seWdvbihjb2xvciA9ICJibGFjayIpICsgDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImJsYWNrIiwgaGlnaCA9ICJyZWQiLCANCiAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9ICJsZWdlbmQiLCBsaW1pdHMgPSBjKDAsMTApKQ0KYGBgDQoNCiMjIyMjIDcuMi4xOCBNYXAtNTogR3VuIE93bmVyc2hpcCAoJSkgYnkgU3RhdGVzDQpgYGB7cn0NCmdncGxvdChtdXJkZXJNYXAsYWVzKHg9bG9uZyx5PWxhdCxncm91cD1ncm91cCxmaWxsPUd1bk93bmVyc2hpcCkpICsgDQogIGdlb21fcG9seWdvbihjb2xvciA9ICJibGFjayIpICsgDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQobG93ID0gImJsYWNrIiwgaGlnaCA9ICJyZWQiLCBndWlkZSA9ICJsZWdlbmQiKQ0KYGBgDQoNCiMjIyMjIDcuMi4xOSBHdW4gT3duZXJzaGlwICglKSBieSBTdGF0ZXMNCmBgYHtyIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTMuMn0NCnRhcHBseShtdXJkZXJNYXAkR3VuT3duZXJzaGlwLCBtdXJkZXJNYXAkcmVnaW9uLCBtZWFuKSAlPiUgc29ydCAlPiUgDQogIGJhcnBsb3QobGFzPTIsIGNleC5uYW1lcz0wLjYsIG1haW49IkF2ZXJnZSBHdW5Pd25lclNoaXAgKCUpIikNCmBgYA0KPGJyPg0KDQotIC0gLQ0KDQo8YnI+PGJyPjxicj48YnI+PGJyPg0KDQo8c3R5bGU+DQouY2FwdGlvbiB7DQogIGNvbG9yOiAjNzc3Ow0KICBtYXJnaW4tdG9wOiAxMHB4Ow0KfQ0KcCBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwcmUgew0KICB3b3JkLWJyZWFrOiBub3JtYWw7DQogIHdvcmQtd3JhcDogbm9ybWFsOw0KICBsaW5lLWhlaWdodDogMTsNCn0NCnByZSBjb2RlIHsNCiAgd2hpdGUtc3BhY2U6IGluaGVyaXQ7DQp9DQpwLGxpIHsNCiAgZm9udC1mYW1pbHk6ICJUcmVidWNoZXQgTVMiLCAi5b6u6Luf5q2j6buR6auUIiwgIk1pY3Jvc29mdCBKaGVuZ0hlaSI7DQp9DQoNCi5yew0KICBsaW5lLWhlaWdodDogMS4yOw0KfQ0KDQp0aXRsZXsNCiAgY29sb3I6ICNjYzAwMDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpib2R5ew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDEsaDIsaDMsaDQsaDV7DQogIGNvbG9yOiAjMDA4ODAwOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDN7DQogIGNvbG9yOiAjYjM2YjAwOw0KICBiYWNrZ3JvdW5kOiAjZmZlMGIzOw0KICBsaW5lLWhlaWdodDogMjsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCmg1ew0KICBjb2xvcjogIzAwNjAwMDsNCiAgYmFja2dyb3VuZDogI2ZmZmZlMDsNCiAgbGluZS1oZWlnaHQ6IDI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQplbXsNCiAgY29sb3I6ICMwMDAwYzA7DQogIGJhY2tncm91bmQ6ICNmMGYwZjA7DQogIH0NCjwvc3R5bGU+DQoNCg==