# baseline 의 RMSE :
# 출도착지를 geohash lvl 5 로 나누어, 출도착지+시간별(1), 출발지+시간별(2), 시간별(3), 전체평속(4) 을 준비한다.
# traget 경로가 들어오면, (1), (2), (3), (4) 에서 존재하는 값만 취하여 평균값을 취한다.
# traget 경로의 distance 를 위의 평균 속도 값으로 나누어, trip duration 을 예상한다.
# Try2 도 위의 아이디어와 비슷하다.
# geohash 를 기준으로 경로별 특성값과 동승자수 요일, 계절(month) 의 feature 를 더하여 GLM 으로 시도한다.
library(data.table)
library(dplyr)
library(magrittr)
library(tidyr)
library(stringi)
library(stringr)
library(xda)
library(caret)
library(doMC)
library(dummy)
library(leaflet)
library(rgdal)
library(lubridate)
library(gridExtra)
library(ggmap)
library(geohash)
train <- fread("/Users/CA/Downloads/NY_taxi/train.csv", na.strings = "")
Read 43.2% of 1458644 rows
Read 64.4% of 1458644 rows
Read 85.7% of 1458644 rows
Read 86.4% of 1458644 rows
Read 1458644 rows and 11 (of 11) columns from 0.187 GB file in 00:00:08
test <- fread("/Users/CA/Downloads/NY_taxi/test.csv", na.strings = "")
Read 56.0% of 625134 rows
Read 625134 rows and 9 (of 9) columns from 0.066 GB file in 00:00:04
numSummary(train)
numSummary(test)
charSummary(train)
charSummary(test)
coord2distance <- Vectorize(function(lng1, lat1, lng2, lat2) {
rad_per_deg = pi / 180
rkm = 6371
rm = rkm * 1000
dlng_rad = (lng2 - lng1) * rad_per_deg
dlat_rad = (lat2 - lat1) * rad_per_deg
lng1_rad = lng1 * rad_per_deg
lat1_rad = lat1 * rad_per_deg
lng2_rad = lng2 * rad_per_deg
lat2_rad = lat2 * rad_per_deg
a = sin(dlng_rad/2)**2 + cos(lng1_rad) * cos(lng2_rad) * sin(dlat_rad/2)**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
return(rm * c)
})
refine <- function(df) {
df %<>%
mutate(pickup_datetime = ymd_hms(pickup_datetime),
dropoff_datetime = ymd_hms(dropoff_datetime),
distance = coord2distance( pickup_longitude, pickup_latitude, dropoff_longitude, dropoff_latitude),
speed = round(distance / 1000 / (trip_duration/3600),2)) %>%
mutate(pickup_wday = wday(pickup_datetime, label = T), pickup_hour = hour(pickup_datetime)) %>%
mutate(dropoff_wday = wday(dropoff_datetime, label = T), dropoff_hour = hour(dropoff_datetime)) %>%
mutate(pickup_month = month(pickup_datetime, label = T), dropoff_month = month(dropoff_datetime)) %>%
mutate_each_(funs(as.factor(.)), c("vendor_id", "store_and_fwd_flag", "passenger_count"))
return(df)
}
train %<>% refine
summary(train)
id vendor_id pickup_datetime dropoff_datetime passenger_count
Length:1458644 1:678342 Min. :2016-01-01 00:00:17 Min. :2016-01-01 00:03:31 1 :1033540
Class :character 2:780302 1st Qu.:2016-02-17 16:46:04 1st Qu.:2016-02-17 17:05:32 2 : 210318
Mode :character Median :2016-04-01 17:19:40 Median :2016-04-01 17:35:12 5 : 78088
Mean :2016-04-01 10:10:24 Mean :2016-04-01 10:26:24 3 : 59896
3rd Qu.:2016-05-15 03:56:08 3rd Qu.:2016-05-15 04:10:51 6 : 48333
Max. :2016-06-30 23:59:39 Max. :2016-07-01 23:02:03 4 : 28404
(Other): 65
pickup_longitude pickup_latitude dropoff_longitude dropoff_latitude store_and_fwd_flag trip_duration distance
Min. :-121.9 Min. :34.4 Min. :-121.9 Min. :32.2 N:1450599 Min. : 1 Min. : 0
1st Qu.: -74.0 1st Qu.:40.7 1st Qu.: -74.0 1st Qu.:40.7 Y: 8045 1st Qu.: 397 1st Qu.: 848
Median : -74.0 Median :40.8 Median : -74.0 Median :40.8 Median : 662 Median : 1531
Mean : -74.0 Mean :40.8 Mean : -74.0 Mean :40.8 Mean : 959 Mean : 2876
3rd Qu.: -74.0 3rd Qu.:40.8 3rd Qu.: -74.0 3rd Qu.:40.8 3rd Qu.: 1075 3rd Qu.: 2847
Max. : -61.3 Max. :51.9 Max. : -61.3 Max. :43.9 Max. :3526282 Max. :851968
speed pickup_wday pickup_hour dropoff_wday dropoff_hour pickup_month dropoff_month
Min. : 0 Sun :195366 Min. : 0.0 Sun :197224 Min. : 0.0 Mar :256189 Min. :1.00
1st Qu.: 6 Mon :187418 1st Qu.: 9.0 Mon :187433 1st Qu.: 9.0 Apr :251645 1st Qu.:2.00
Median : 10 Tues :202749 Median :14.0 Tues :202518 Median :14.0 May :248487 Median :4.00
Mean : 11 Wed :210136 Mean :13.6 Wed :209790 Mean :13.6 Feb :238300 Mean :3.52
3rd Qu.: 15 Thurs:218574 3rd Qu.:19.0 Thurs:217746 3rd Qu.:19.0 Jun :234316 3rd Qu.:5.00
Max. :8619 Fri :223533 Max. :23.0 Fri :223031 Max. :23.0 Jan :229707 Max. :7.00
Sat :220868 Sat :220902 (Other): 0
# plot#1 : 시간별 운행량 변화
p1 <- ggplot(data = train, mapping = aes(x = pickup_wday, group = vendor_id, fill = vendor_id)) +
geom_bar(position="identity", alpha=0.7) +
theme_bw()
p2 <- ggplot(data = train, mapping = aes(x = pickup_hour, group = vendor_id, fill = vendor_id)) +
geom_bar(position="identity", alpha=0.7) +
theme_bw()
p3 <- ggplot(data = train, mapping = aes(x = pickup_month, group = vendor_id, fill = vendor_id)) +
geom_bar(position="identity", alpha=0.7) +
theme_bw()
grid.arrange(p1, p2, p3, nrow = 2, ncol = 2)

# plot#2 : 시간별 운행 속도 변화
## Q3. 요일/시간대별 평균 속도
## #=> 요일별 평균속도는 월/일/토 , 화/금 , 수/목 순으로 좋음
## #=> 시간별 평균속도는 퇴근시간대보다 일과시간이 나쁘고, 새벽시간대가 제일 좋음
## Q4. 요일/시간대별 평균 운행 직선 거리
## #=> 요일별 평균 운행 직선 거리는 월/일 이 상대적으로 길고, 기타 요일에는 대동소이
## #=> 시간별 평균 운행 직선 거리는 평균속도 패턴과 동일.
avg_speed_by_wday <- train %>% group_by(pickup_wday) %>% summarise(avg_speed_by_wday = mean(speed))
avg_distance_by_wday <- train %>% group_by(pickup_wday) %>% summarise(avg_distance_by_wday = mean(distance))
avg_speed_by_hour <- train %>% group_by(pickup_hour) %>% summarise(avg_speed_by_hour = mean(speed))
avg_distance_by_hour <- train %>% group_by(pickup_hour) %>% summarise(avg_distance_by_hour = mean(distance))
daily_data <- merge(avg_speed_by_wday, avg_distance_by_wday, by = "pickup_wday")
hourly_data <- merge(avg_speed_by_hour, avg_distance_by_hour, by = "pickup_hour")
p1 <- ggplot(data = daily_data, aes(x = pickup_wday)) +
geom_line(aes(y = avg_speed_by_wday, colour = "speed", group = 1)) +
geom_line(aes(y = round(avg_distance_by_wday/250 ,2), colour = "distance", group = 1)) +
scale_y_continuous(sec.axis = sec_axis(~.*250, name = "Distance ( meter )")) +
labs(y = "Speed ( km/h )")
p2 <- ggplot(data = hourly_data, aes(x = pickup_hour)) +
geom_line(aes(y = avg_speed_by_hour, colour = "speed")) +
geom_line(aes(y = round(avg_distance_by_hour/250 ,2), colour = "distance")) +
scale_y_continuous(sec.axis = sec_axis(~.*250, name = "Distance ( meter )")) +
labs(y = "Speed ( km/h )")
multiplot(p1, p2, cols = 1)

#p1 <- ggplot(data = hourly_data, aes(pickup_hour, avg_speed_by_hour)) + geom_line() + geom_point()
#p2 <- ggplot(data = hourly_data, aes(pickup_hour, avg_distance_by_hour)) + geom_line() + geom_point()
#grid.arrange(p1, p2, nrow = 2, ncol = 1)
#head(merge(avg_speed_by_wday, avg_distance_by_wday, by = "pickup_wday") %>% arrange(desc(avg_distance_by_wday)), n = 7)
#head(merge(avg_speed_by_hour, avg_distance_by_hour, by = "pickup_hour") %>% arrange(desc(avg_speed_by_hour)), n = 24)
# plot 3 : 탑승자 수
table(train$passenger_count)
0 1 2 3 4 5 6 7 8 9
60 1033540 210318 59896 28404 78088 48333 3 1 1
train$passenger_count <- as.numeric(as.character(train$passenger_count))
avg_speed_by_pc <- train %>%
filter(passenger_count %in% c(1,2,3,4,5,6)) %>%
group_by(passenger_count) %>%
summarise(avg_speed = mean(speed), avg_distnace = mean(distance))
avg_pc_by_hour <- train %>%
filter(passenger_count %in% c(1,2,3,4,5,6)) %>%
group_by(pickup_hour) %>%
summarise(avg_pc = mean(passenger_count))
p1 <- ggplot(data = avg_speed_by_pc, aes(x = passenger_count)) +
geom_line(aes(y = avg_speed, colour = "speed")) +
geom_line(aes(y = round(avg_distnace/260 ,2), colour = "distance")) +
scale_y_continuous(sec.axis = sec_axis(~.*260, name = "Distance ( meter )")) +
labs(y = "Speed ( km/h )")
p2 <- ggplot(data = avg_pc_by_hour, aes(x = pickup_hour)) +
geom_line(aes(y = avg_pc)) + labs(y = "average passenger count")
multiplot(p1, p2, cols = 1)

LS0tCnRpdGxlOiAiTllfVFJZMiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CiMgYmFzZWxpbmUg7J2YIFJNU0UgOiAKIyDstpzrj4TssKnsp4DrpbwgZ2VvaGFzaCBsdmwgNSDroZwg64KY64iE7Ja0LCDstpzrj4TssKnsp4Ar7Iuc6rCE67OEKDEpLCDstpzrsJzsp4Ar7Iuc6rCE67OEKDIpLCDsi5zqsITrs4QoMyksIOyghOyytO2PieyGjSg0KSDsnYQg7KSA67mE7ZWc64ukLgojIHRyYWdldCDqsr3roZzqsIAg65Ok7Ja07Jik66m0LCAoMSksICgyKSwgKDMpLCAoNCkg7JeQ7IScIOyhtOyerO2VmOuKlCDqsJLrp4wg7Leo7ZWY7JesIO2Pieq3oOqwkuydhCDst6jtlZzri6QuCiMgdHJhZ2V0IOqyveuhnOydmCBkaXN0YW5jZSDrpbwg7JyE7J2YIO2Pieq3oCDsho3rj4Qg6rCS7Jy866GcIOuCmOuIhOyWtCwgdHJpcCBkdXJhdGlvbiDsnYQg7JiI7IOB7ZWc64ukLgpgYGAKCmBgYHtyfQojIFRyeTIg64+EIOychOydmCDslYTsnbTrlJTslrTsmYAg67mE7Iq37ZWY64ukLgojIGdlb2hhc2gg66W8IOq4sOykgOycvOuhnCDqsr3roZzrs4Qg7Yq57ISx6rCS6rO8IOuPmeyKueyekOyImCDsmpTsnbwsIOqzhOygiChtb250aCkg7J2YIGZlYXR1cmUg66W8IOuNlO2VmOyXrCBHTE0g7Jy866GcIOyLnOuPhO2VnOuLpC4gCmBgYAoKYGBge3J9CmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShkcGx5cikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeSh0aWR5cikKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoeGRhKQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KGRvTUMpCmxpYnJhcnkoZHVtbXkpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShyZ2RhbCkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KGdnbWFwKQpsaWJyYXJ5KGdlb2hhc2gpCmBgYAoKYGBge3J9CnRyYWluIDwtIGZyZWFkKCIvVXNlcnMvQ0EvRG93bmxvYWRzL05ZX3RheGkvdHJhaW4uY3N2IiwgbmEuc3RyaW5ncyA9ICIiKQp0ZXN0IDwtIGZyZWFkKCIvVXNlcnMvQ0EvRG93bmxvYWRzL05ZX3RheGkvdGVzdC5jc3YiLCBuYS5zdHJpbmdzID0gIiIpCmBgYAoKYGBge3J9Cm51bVN1bW1hcnkodHJhaW4pCm51bVN1bW1hcnkodGVzdCkKYGBgCgpgYGB7cn0KY2hhclN1bW1hcnkodHJhaW4pCmNoYXJTdW1tYXJ5KHRlc3QpCmBgYAoKYGBge3J9CmNvb3JkMmRpc3RhbmNlIDwtIFZlY3Rvcml6ZShmdW5jdGlvbihsbmcxLCBsYXQxLCBsbmcyLCBsYXQyKSB7CiAgcmFkX3Blcl9kZWcgPSBwaSAvIDE4MAogIHJrbSA9IDYzNzEKICBybSA9IHJrbSAqIDEwMDAKICAKICBkbG5nX3JhZCA9IChsbmcyIC0gbG5nMSkgKiByYWRfcGVyX2RlZwogIGRsYXRfcmFkID0gKGxhdDIgLSBsYXQxKSAqIHJhZF9wZXJfZGVnCiAgCiAgbG5nMV9yYWQgPSBsbmcxICogcmFkX3Blcl9kZWcKICBsYXQxX3JhZCA9IGxhdDEgKiByYWRfcGVyX2RlZwogIGxuZzJfcmFkID0gbG5nMiAqIHJhZF9wZXJfZGVnCiAgbGF0Ml9yYWQgPSBsYXQyICogcmFkX3Blcl9kZWcKICAKICBhID0gc2luKGRsbmdfcmFkLzIpKioyICsgY29zKGxuZzFfcmFkKSAqIGNvcyhsbmcyX3JhZCkgKiBzaW4oZGxhdF9yYWQvMikqKjIKICBjID0gMiAqIGF0YW4yKHNxcnQoYSksIHNxcnQoMS1hKSkKICAKICByZXR1cm4ocm0gKiBjKQp9KQpgYGAKCmBgYHtyfQpyZWZpbmUgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiAlPD4lCiAgICBtdXRhdGUocGlja3VwX2RhdGV0aW1lICAgPSB5bWRfaG1zKHBpY2t1cF9kYXRldGltZSksIAogICAgICAgICAgIGRyb3BvZmZfZGF0ZXRpbWUgID0geW1kX2htcyhkcm9wb2ZmX2RhdGV0aW1lKSwKICAgICAgICAgICBkaXN0YW5jZSAgICAgICAgICA9IGNvb3JkMmRpc3RhbmNlKCBwaWNrdXBfbG9uZ2l0dWRlLCBwaWNrdXBfbGF0aXR1ZGUsIGRyb3BvZmZfbG9uZ2l0dWRlLCBkcm9wb2ZmX2xhdGl0dWRlKSwKICAgICAgICAgICBzcGVlZCAgICAgICAgICAgICA9IHJvdW5kKGRpc3RhbmNlIC8gMTAwMCAvICh0cmlwX2R1cmF0aW9uLzM2MDApLDIpKSAlPiUKICAgIG11dGF0ZShwaWNrdXBfd2RheSA9IHdkYXkocGlja3VwX2RhdGV0aW1lLCBsYWJlbCA9IFQpLCBwaWNrdXBfaG91ciA9IGhvdXIocGlja3VwX2RhdGV0aW1lKSkgJT4lCiAgICBtdXRhdGUoZHJvcG9mZl93ZGF5ID0gd2RheShkcm9wb2ZmX2RhdGV0aW1lLCBsYWJlbCA9IFQpLCBkcm9wb2ZmX2hvdXIgPSBob3VyKGRyb3BvZmZfZGF0ZXRpbWUpKSAlPiUKICAgIG11dGF0ZShwaWNrdXBfbW9udGggPSBtb250aChwaWNrdXBfZGF0ZXRpbWUsIGxhYmVsID0gVCksIGRyb3BvZmZfbW9udGggPSBtb250aChkcm9wb2ZmX2RhdGV0aW1lKSkgJT4lCiAgICBtdXRhdGVfZWFjaF8oZnVucyhhcy5mYWN0b3IoLikpLCBjKCJ2ZW5kb3JfaWQiLCAic3RvcmVfYW5kX2Z3ZF9mbGFnIiwgInBhc3Nlbmdlcl9jb3VudCIpKQogIAogIHJldHVybihkZikKfQoKdHJhaW4gJTw+JSByZWZpbmUKc3VtbWFyeSh0cmFpbikKYGBgCgpgYGB7cn0KIyBwbG90IzEgOiDsi5zqsITrs4Qg7Jq07ZaJ65+JIOuzgO2ZlCAKCnAxIDwtIGdncGxvdChkYXRhID0gdHJhaW4sIG1hcHBpbmcgPSBhZXMoeCA9IHBpY2t1cF93ZGF5LCBncm91cCA9IHZlbmRvcl9pZCwgZmlsbCA9IHZlbmRvcl9pZCkpICsgCiAgZ2VvbV9iYXIocG9zaXRpb249ImlkZW50aXR5IiwgYWxwaGE9MC43KSArIAogIHRoZW1lX2J3KCkKcDIgPC0gZ2dwbG90KGRhdGEgPSB0cmFpbiwgbWFwcGluZyA9IGFlcyh4ID0gcGlja3VwX2hvdXIsIGdyb3VwID0gdmVuZG9yX2lkLCBmaWxsID0gdmVuZG9yX2lkKSkgKyAKICBnZW9tX2Jhcihwb3NpdGlvbj0iaWRlbnRpdHkiLCBhbHBoYT0wLjcpICsgCiAgdGhlbWVfYncoKQpwMyA8LSBnZ3Bsb3QoZGF0YSA9IHRyYWluLCBtYXBwaW5nID0gYWVzKHggPSBwaWNrdXBfbW9udGgsIGdyb3VwID0gdmVuZG9yX2lkLCBmaWxsID0gdmVuZG9yX2lkKSkgKyAKICBnZW9tX2Jhcihwb3NpdGlvbj0iaWRlbnRpdHkiLCBhbHBoYT0wLjcpICsgCiAgdGhlbWVfYncoKQoKZ3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMsIG5yb3cgPSAyLCBuY29sID0gMikKYGBgCgpgYGB7ciwgaW5jbHVkZT1GQUxTRX0KbXVsdGlwbG90IDwtIGZ1bmN0aW9uKC4uLiwgcGxvdGxpc3Q9TlVMTCwgZmlsZSwgY29scz0xLCBsYXlvdXQ9TlVMTCkgewogIGxpYnJhcnkoZ3JpZCkKCiAgIyBNYWtlIGEgbGlzdCBmcm9tIHRoZSAuLi4gYXJndW1lbnRzIGFuZCBwbG90bGlzdAogIHBsb3RzIDwtIGMobGlzdCguLi4pLCBwbG90bGlzdCkKCiAgbnVtUGxvdHMgPSBsZW5ndGgocGxvdHMpCgogICMgSWYgbGF5b3V0IGlzIE5VTEwsIHRoZW4gdXNlICdjb2xzJyB0byBkZXRlcm1pbmUgbGF5b3V0CiAgaWYgKGlzLm51bGwobGF5b3V0KSkgewogICAgIyBNYWtlIHRoZSBwYW5lbAogICAgIyBuY29sOiBOdW1iZXIgb2YgY29sdW1ucyBvZiBwbG90cwogICAgIyBucm93OiBOdW1iZXIgb2Ygcm93cyBuZWVkZWQsIGNhbGN1bGF0ZWQgZnJvbSAjIG9mIGNvbHMKICAgIGxheW91dCA8LSBtYXRyaXgoc2VxKDEsIGNvbHMgKiBjZWlsaW5nKG51bVBsb3RzL2NvbHMpKSwKICAgICAgICAgICAgICAgICAgICBuY29sID0gY29scywgbnJvdyA9IGNlaWxpbmcobnVtUGxvdHMvY29scykpCiAgfQoKIGlmIChudW1QbG90cz09MSkgewogICAgcHJpbnQocGxvdHNbWzFdXSkKCiAgfSBlbHNlIHsKICAgICMgU2V0IHVwIHRoZSBwYWdlCiAgICBncmlkLm5ld3BhZ2UoKQogICAgcHVzaFZpZXdwb3J0KHZpZXdwb3J0KGxheW91dCA9IGdyaWQubGF5b3V0KG5yb3cobGF5b3V0KSwgbmNvbChsYXlvdXQpKSkpCgogICAgIyBNYWtlIGVhY2ggcGxvdCwgaW4gdGhlIGNvcnJlY3QgbG9jYXRpb24KICAgIGZvciAoaSBpbiAxOm51bVBsb3RzKSB7CiAgICAgICMgR2V0IHRoZSBpLGogbWF0cml4IHBvc2l0aW9ucyBvZiB0aGUgcmVnaW9ucyB0aGF0IGNvbnRhaW4gdGhpcyBzdWJwbG90CiAgICAgIG1hdGNoaWR4IDwtIGFzLmRhdGEuZnJhbWUod2hpY2gobGF5b3V0ID09IGksIGFyci5pbmQgPSBUUlVFKSkKCiAgICAgIHByaW50KHBsb3RzW1tpXV0sIHZwID0gdmlld3BvcnQobGF5b3V0LnBvcy5yb3cgPSBtYXRjaGlkeCRyb3csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF5b3V0LnBvcy5jb2wgPSBtYXRjaGlkeCRjb2wpKQogICAgfQogIH0KfQpgYGAKCmBgYHtyfQojIHBsb3QjMiA6IOyLnOqwhOuzhCDsmrTtlokg7IaN64+EIOuzgO2ZlCAKCiMjIFEzLiDsmpTsnbwv7Iuc6rCE64yA67OEIO2Pieq3oCDsho3rj4QgCiMjICAgICAgIz0+IOyalOydvOuzhCDtj4nqt6Dsho3rj4TripQg7JuUL+ydvC/thqAgLCDtmZQv6riIICwg7IiYL+uqqSDsiJzsnLzroZwg7KKL7J2MIAojIyAgICAgICM9PiDsi5zqsITrs4Qg7Y+J6reg7IaN64+E64qUIO2HtOq3vOyLnOqwhOuMgOuztOuLpCDsnbzqs7zsi5zqsITsnbQg64KY7IGY6rOgLCDsg4jrsr3si5zqsITrjIDqsIAg7KCc7J28IOyii+ydjCAKIyMgUTQuIOyalOydvC/si5zqsITrjIDrs4Qg7Y+J6regIOyatO2WiSDsp4HshKAg6rGw66asIAojIyAgICAgICM9PiDsmpTsnbzrs4Qg7Y+J6regIOyatO2WiSDsp4HshKAg6rGw66as64qUIOyblC/snbwg7J20IOyDgeuMgOyggeycvOuhnCDquLjqs6AsIOq4sO2DgCDsmpTsnbzsl5DripQg64yA64+Z7IaM7J20IAojIyAgICAgICM9PiDsi5zqsITrs4Qg7Y+J6regIOyatO2WiSDsp4HshKAg6rGw66as64qUIO2Pieq3oOyGjeuPhCDtjKjthLTqs7wg64+Z7J28LgoKYXZnX3NwZWVkX2J5X3dkYXkgICAgPC0gdHJhaW4gJT4lIGdyb3VwX2J5KHBpY2t1cF93ZGF5KSAlPiUgc3VtbWFyaXNlKGF2Z19zcGVlZF9ieV93ZGF5ID0gbWVhbihzcGVlZCkpCmF2Z19kaXN0YW5jZV9ieV93ZGF5IDwtIHRyYWluICU+JSBncm91cF9ieShwaWNrdXBfd2RheSkgJT4lIHN1bW1hcmlzZShhdmdfZGlzdGFuY2VfYnlfd2RheSA9IG1lYW4oZGlzdGFuY2UpKQoKYXZnX3NwZWVkX2J5X2hvdXIgICAgPC0gdHJhaW4gJT4lIGdyb3VwX2J5KHBpY2t1cF9ob3VyKSAlPiUgc3VtbWFyaXNlKGF2Z19zcGVlZF9ieV9ob3VyID0gbWVhbihzcGVlZCkpCmF2Z19kaXN0YW5jZV9ieV9ob3VyIDwtIHRyYWluICU+JSBncm91cF9ieShwaWNrdXBfaG91cikgJT4lIHN1bW1hcmlzZShhdmdfZGlzdGFuY2VfYnlfaG91ciA9IG1lYW4oZGlzdGFuY2UpKQoKZGFpbHlfZGF0YSAgPC0gbWVyZ2UoYXZnX3NwZWVkX2J5X3dkYXksIGF2Z19kaXN0YW5jZV9ieV93ZGF5LCBieSA9ICJwaWNrdXBfd2RheSIpCmhvdXJseV9kYXRhIDwtIG1lcmdlKGF2Z19zcGVlZF9ieV9ob3VyLCBhdmdfZGlzdGFuY2VfYnlfaG91ciwgYnkgPSAicGlja3VwX2hvdXIiKSAKCnAxIDwtIGdncGxvdChkYXRhID0gZGFpbHlfZGF0YSwgYWVzKHggPSBwaWNrdXBfd2RheSkpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBhdmdfc3BlZWRfYnlfd2RheSwgY29sb3VyID0gInNwZWVkIiwgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHJvdW5kKGF2Z19kaXN0YW5jZV9ieV93ZGF5LzI1MCAsMiksIGNvbG91ciA9ICJkaXN0YW5jZSIsIGdyb3VwID0gMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoc2VjLmF4aXMgPSBzZWNfYXhpcyh+LioyNTAsIG5hbWUgPSAiRGlzdGFuY2UgKCBtZXRlciApIikpICsKICBsYWJzKHkgPSAiU3BlZWQgKCBrbS9oICkiKQoKcDIgPC0gZ2dwbG90KGRhdGEgPSBob3VybHlfZGF0YSwgYWVzKHggPSBwaWNrdXBfaG91cikpICsKICBnZW9tX2xpbmUoYWVzKHkgPSBhdmdfc3BlZWRfYnlfaG91ciwgY29sb3VyID0gInNwZWVkIikpICsKICBnZW9tX2xpbmUoYWVzKHkgPSByb3VuZChhdmdfZGlzdGFuY2VfYnlfaG91ci8yNTAgLDIpLCBjb2xvdXIgPSAiZGlzdGFuY2UiKSkgKwogIHNjYWxlX3lfY29udGludW91cyhzZWMuYXhpcyA9IHNlY19heGlzKH4uKjI1MCwgbmFtZSA9ICJEaXN0YW5jZSAoIG1ldGVyICkiKSkgKwogIGxhYnMoeSA9ICJTcGVlZCAoIGttL2ggKSIpCgptdWx0aXBsb3QocDEsIHAyLCBjb2xzID0gMSkKCiNwMSA8LSBnZ3Bsb3QoZGF0YSA9IGhvdXJseV9kYXRhLCBhZXMocGlja3VwX2hvdXIsIGF2Z19zcGVlZF9ieV9ob3VyKSkgKyBnZW9tX2xpbmUoKSArIGdlb21fcG9pbnQoKQojcDIgPC0gZ2dwbG90KGRhdGEgPSBob3VybHlfZGF0YSwgYWVzKHBpY2t1cF9ob3VyLCBhdmdfZGlzdGFuY2VfYnlfaG91cikpICsgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkKI2dyaWQuYXJyYW5nZShwMSwgcDIsIG5yb3cgPSAyLCBuY29sID0gMSkKCiNoZWFkKG1lcmdlKGF2Z19zcGVlZF9ieV93ZGF5LCBhdmdfZGlzdGFuY2VfYnlfd2RheSwgYnkgPSAicGlja3VwX3dkYXkiKSAlPiUgYXJyYW5nZShkZXNjKGF2Z19kaXN0YW5jZV9ieV93ZGF5KSksIG4gPSA3KQojaGVhZChtZXJnZShhdmdfc3BlZWRfYnlfaG91ciwgYXZnX2Rpc3RhbmNlX2J5X2hvdXIsIGJ5ID0gInBpY2t1cF9ob3VyIikgJT4lIGFycmFuZ2UoZGVzYyhhdmdfc3BlZWRfYnlfaG91cikpLCBuID0gMjQpCmBgYAoKYGBge3J9CiMgcGxvdCAzIDog7YOR7Iq57J6QIOyImCAKdGFibGUodHJhaW4kcGFzc2VuZ2VyX2NvdW50KQp0cmFpbiRwYXNzZW5nZXJfY291bnQgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIodHJhaW4kcGFzc2VuZ2VyX2NvdW50KSkKCmF2Z19zcGVlZF9ieV9wYyA8LSB0cmFpbiAlPiUgCiAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihwYXNzZW5nZXJfY291bnQgJWluJSBjKDEsMiwzLDQsNSw2KSkgJT4lIAogICAgICAgICAgICAgICAgICAgICBncm91cF9ieShwYXNzZW5nZXJfY291bnQpICU+JSAKICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKGF2Z19zcGVlZCA9IG1lYW4oc3BlZWQpLCBhdmdfZGlzdG5hY2UgPSBtZWFuKGRpc3RhbmNlKSkKCmF2Z19wY19ieV9ob3VyIDwtIHRyYWluICU+JSAKICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHBhc3Nlbmdlcl9jb3VudCAlaW4lIGMoMSwyLDMsNCw1LDYpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KHBpY2t1cF9ob3VyKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShhdmdfcGMgPSBtZWFuKHBhc3Nlbmdlcl9jb3VudCkpCgpwMSA8LSBnZ3Bsb3QoZGF0YSA9IGF2Z19zcGVlZF9ieV9wYywgYWVzKHggPSBwYXNzZW5nZXJfY291bnQpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gYXZnX3NwZWVkLCBjb2xvdXIgPSAic3BlZWQiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IHJvdW5kKGF2Z19kaXN0bmFjZS8yNjAgLDIpLCBjb2xvdXIgPSAiZGlzdGFuY2UiKSkgKwogIHNjYWxlX3lfY29udGludW91cyhzZWMuYXhpcyA9IHNlY19heGlzKH4uKjI2MCwgbmFtZSA9ICJEaXN0YW5jZSAoIG1ldGVyICkiKSkgKwogIGxhYnMoeSA9ICJTcGVlZCAoIGttL2ggKSIpCgpwMiA8LSBnZ3Bsb3QoZGF0YSA9IGF2Z19wY19ieV9ob3VyLCBhZXMoeCA9IHBpY2t1cF9ob3VyKSkgKwogIGdlb21fbGluZShhZXMoeSA9IGF2Z19wYykpICsgbGFicyh5ID0gImF2ZXJhZ2UgcGFzc2VuZ2VyIGNvdW50IikKCm11bHRpcGxvdChwMSwgcDIsIGNvbHMgPSAxKQpgYGAKCgpgYGB7cn0KYGBg