c <- read.csv("Contacts.csv")
c <- select(c, -("contact_id"))
colnames(c) <- c("visitor_id", "pro_id", "hired")
v <- read.csv("Visitors.csv")
v <- v[, c("visitor_id", "pro_user_id", "category", "result_position", "num_reviews",
"avg_rating", "cost_estimate_cents", "search_timestamp", "pro_last_active_time_before_search",
"service_page_viewed")]
v$cost_estimate_cents <- v$cost_estimate_cents/100
colnames(v) <- c("visitor_id", "pro_id", "category", "search_rank", "num_reviews",
"avg_rating", "cost_est($)", "search_timestamp", "pro_last_active_time_before_search",
"page_viewed")
Fixing Date and Time stamps
v <- v %>% mutate(search_timestamp = parse_date_time(search_timestamp, orders = "Y-m-d HMS"))
v$s_date <- format(v$search_timestamp, "%Y-%m-%d") %>% as.Date()
v$s_day <- as.factor(weekdays(v$s_date))
levels(v$s_day) <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday")
v$s_hr <- format(v$search_timestamp, "%H.%M") %>% as.numeric()
v$s_hr <- as.integer(floor(v$s_hr))
v <- select(v, -("search_timestamp"))
v <- v %>% mutate(pro_last_active_time_before_search = parse_date_time(pro_last_active_time_before_search,
orders = "Y-m-d HMS"))
v$p.l_date <- format(v$pro_last_active_time_before_search, "%Y-%m-%d") %>% as.Date()
v$p.l_hr <- format(v$pro_last_active_time_before_search, "%H.%M") %>% as.numeric()
v$p.l_hr <- floor(v$p.l_hr)
v <- select(v, -("pro_last_active_time_before_search"))
v <- v %>% mutate(diff = as.integer(s_date - p.l_date))
Handling and Joining data files
explo <- v
data <- v %>% left_join(c, by = c("visitor_id", "pro_id"))
data <- data[!is.na(data$hired), ]
rm(c)
rm(v)
dt_total <- select(data, -c("visitor_id", "pro_id", "s_date", "p.l_date", "p.l_hr"))
rm(data)
dt_total$hired <- as.factor(ifelse(dt_total$hired == T, "Hired", "Not Hired"))
levels(dt_total$hired) <- c("Hired", "Not Hired")
Checking the Variable of concern
table(dt_total$hired)
This is an unbalenced data set and required Over Sampling for the Hired part to get better results.
SMOTE for equalizing the data
set.seed(1)
dt_total <- SMOTE(hired ~ ., dt_total, perc.over = 500, k = 5, perc.under = 100)
table(dt_total$hired)
Spliting into data for two different categories
# HC for House cleaning and LM for Local moving
table(dt_total$category)
dt_hc <- dt_total %>% filter(category == "House Cleaning") %>% select(-"category")
dt_lm <- dt_total %>% filter(category != "House Cleaning") %>% select(-"category")
Checking for parameters that actually effect the Hiring For the total data as a whole
fit <- glm(hired ~ ., data = dt_total, family = binomial)
summary(fit)
data.frame(summary(fit)$coef[summary(fit)$coef[, 4] <= 0.05, 4])
For Local Moving
fit <- glm(hired ~ ., data = dt_lm, family = binomial)
summary(fit)
data.frame(summary(fit)$coef[summary(fit)$coef[, 4] <= 0.05, 4])
For Home Cleaning
fit <- glm(hired ~ ., data = dt_hc, family = binomial)
summary(fit)
data.frame(summary(fit)$coef[summary(fit)$coef[, 4] <= 0.05, 4])
rm(fit)
EXPLORATORY ANALYSIS
num_reviews VS hiring
p <- dt_total %>% na.omit(c("hired", "num_reviews")) %>% ggplot(aes(x = hired,
y = as.integer(num_reviews), fill = hired)) + geom_boxplot(color = "black",
outlier.size = 0, outlier.shape = NA) + ggtitle("Number of Reviews Vs Hiring (For the total data)") +
labs(y = "Number of reviews", x = "Hired or Not", fill = "Hired or Not")
p
p <- dt_lm %>% na.omit(c("hired", "num_reviews")) %>% ggplot(aes(x = hired,
y = as.integer(num_reviews), fill = hired)) + geom_boxplot(color = "black",
outlier.size = 0, outlier.shape = NA) + ggtitle("Number of Reviews Vs Hiring (For Local Moving)") +
labs(y = "Number of reviews", x = "Hired or Not", fill = "Hired or Not")
p
p <- dt_hc %>% na.omit(c("hired", "num_reviews")) %>% ggplot(aes(x = hired,
y = as.integer(num_reviews), fill = hired)) + geom_boxplot(color = "black",
outlier.size = 0, outlier.shape = NA) + ggtitle("Number of Reviews Vs Hiring (For Home Cleaning)") +
labs(y = "Number of reviews", x = "Hired or Not", fill = "Hired or Not")
p
rm(p)
It is very clear that the number of reviews have a significant effect on the hiring. As the number of reviews increases, the probability of being hired also increases.
Search Rank Vs Hiring
p <- dt_total %>% na.omit(c("search_rank", "hired")) %>% filter(search_rank > 5) %>% ggplot(aes(x = hired, y = search_rank, fill = hired))+geom_boxplot(color = "black", outlier.size = 0, outlier.shape = NA)+ggtitle("Effect of Search Ranks beyond 5 on Hiring")+labs(y = "Search Rank", x = "Hired or Not", fill = "Hired or Not")
p
p <- dt_lm %>% na.omit(c("search_rank", "hired")) %>% filter(search_rank > 5) %>% ggplot(aes(x = hired, y = search_rank, fill = hired))+geom_boxplot(color = "black", outlier.size = 0, outlier.shape = NA)+ggtitle("Effect of Search Ranks beyond 5 on Hiring (Local Movers)")+labs(y = "Search Rank", x = "Hired or Not", fill = "Hired or Not")
p
p <- dt_hc %>% na.omit(c("search_rank", "hired")) %>% filter(search_rank > 5) %>% ggplot(aes(x = hired, y = search_rank, fill = hired))+geom_boxplot(color = "black", outlier.size = 0, outlier.shape = NA)+ggtitle("Effect of Search Ranks beyond 5 on Hiring (Home Cleaners)")+labs(y = "Search Rank", x = "Hired or Not", fill = "Hired or Not")
p
rm(p)
There is a significant effect of search ranks beyond 5 on Home Cleaners and a mild effect on Local Movers. As the search rank increases there posibility of being hired decreases.
Number of Reviews, Avg Reviews Vs Hiring
p <- dt_total %>% na.omit(c("num_reviews", "avg_reviews", "hired")) %>% ggplot(aes(x = avg_rating, y = num_reviews, shape = hired, col = hired))+scale_shape_manual(values=c(3, 1))+ scale_color_manual(values=c('#E69F00', '#56B4E9'))+geom_point()+ggtitle("Hiring for higher Rating and higher number of reviews")+labs(y = "Number of Reviews", x = "Avg Rating", fill = "Hired or Not")
p
p <- dt_lm %>% na.omit(c("num_reviews", "avg_reviews", "hired")) %>% ggplot(aes(x = avg_rating, y = num_reviews, shape = hired, col = hired))+scale_shape_manual(values=c(3, 1))+ scale_color_manual(values=c('#E69F00', '#56B4E9'))+geom_point()+ggtitle("Hiring for higher Rating and higher number of reviews (Local Movers)")+labs(y = "Number of Reviews", x = "Avg Rating", fill = "Hired or Not")
p
p <- dt_hc %>% na.omit(c("num_reviews", "avg_reviews", "hired")) %>% ggplot(aes(x = avg_rating, y = num_reviews, shape = hired, col = hired))+scale_shape_manual(values=c(3, 1))+ scale_color_manual(values=c('#E69F00', '#56B4E9'))+geom_point()+ggtitle("Hiring for higher Rating and higher number of reviews (Home Cleaners)")+labs(y = "Number of Reviews", x = "Avg Rating", fill = "Hired or Not")
p
rm(p)
In all of the plots, it is pretty evident that for higher Number of reviews and higher rating the change of being hired is high
Effect of Search time on hiring
p <- dt_total %>% na.omit(c("s_hr", "hired")) %>% ggplot(aes(x = hired, y = s_hr, fill = hired))+geom_boxplot(color = "black", outlier.size = 0, outlier.shape = NA)+ggtitle("Search Time vs Hiring")+labs(y = "Search time (24 hr format)", x = "Hired or Not", fill = "Hired or Not")
p
p <- dt_lm %>% na.omit(c("s_hr", "hired")) %>% ggplot(aes(x = hired, y = s_hr, fill = hired))+geom_boxplot(color = "black", outlier.size = 0, outlier.shape = NA)+ggtitle("Search Time vs Hiring (Local Moving)")+labs(y = "Search time (24 hr format)", x = "Hired or Not", fill = "Hired or Not")
p
p <- dt_hc %>% na.omit(c("s_hr", "hired")) %>% ggplot(aes(x = hired, y = s_hr, fill = hired))+geom_boxplot(color = "black", outlier.size = 0, outlier.shape = NA)+ggtitle("Search Time vs Hiring (Home Cleaning)")+labs(y = "Search time (24 hr format)", x = "Hired or Not", fill = "Hired or Not")
p
rm(p)
All plots indicated most of the hiring happens in the second half of the day, sometime between 2-8pm.
Effect of Day on the hiring
p <- dt_total %>% na.omit(c("s_day", "hired")) %>% ggplot(aes(x = hired, fill = hired)) + geom_bar(color = "black", position = "identity")+ facet_wrap(s_day~.)+
ggtitle("Day of the week Vs Hiring")+labs(x = "", y = "Frequency of Hiring")
p
p <- dt_lm %>% na.omit(c("s_day", "hired")) %>% ggplot(aes(x = hired, fill = hired)) + geom_bar(color = "black", position = "identity")+ facet_wrap(s_day~.)+
ggtitle("Day of the week Vs Hiring (Local Moving)")+labs(x = "", y = "Frequency of Hiring")
p
p <- dt_hc %>% na.omit(c("s_day", "hired")) %>% ggplot(aes(x = hired, fill = hired)) + geom_bar(color = "black", position = "identity")+ facet_wrap(s_day~.)+
ggtitle("Day of the week Vs Hiring (Home Cleaning)")+labs(x = "", y = "Frequency of Hiring")
p
rm(p)
Friday seems to the be “The Day” when the traffic is high and a lot of hirings actually happen for both the categories.
Number of Reviews, Cost est Vs Hiring
p <- dt_total %>% na.omit(c("num_reviews", "`cost_est($)`", "hired")) %>% ggplot(aes(x = `cost_est($)`, y = num_reviews, shape = hired, col = hired))+scale_shape_manual(values=c(3, 1))+ scale_color_manual(values=c('#E69F00', '#56B4E9'))+geom_point()+ggtitle("Hiring for Number of reviews and Cost estimate")+labs(y = "Number of Reviews", x = "Cost estimate($)`", fill = "Hired or Not")
p
p <- dt_lm %>% na.omit(c("num_reviews", "`cost_est($)`", "hired")) %>% ggplot(aes(x = `cost_est($)`, y = num_reviews, shape = hired, col = hired))+scale_shape_manual(values=c(3, 1))+ scale_color_manual(values=c('#E69F00', '#56B4E9'))+geom_point()+ggtitle("Hiring for Number of reviews and Cost estimate (Local Moving)")+labs(y = "Number of Reviews", x = "Cost estimate($)`", fill = "Hired or Not")
p
p <- dt_hc %>% na.omit(c("num_reviews", "`cost_est($)`", "hired")) %>% ggplot(aes(x = `cost_est($)`, y = num_reviews, shape = hired, col = hired))+scale_shape_manual(values=c(3, 1))+ scale_color_manual(values=c('#E69F00', '#56B4E9'))+geom_point()+ggtitle("Hiring for Number of reviews and Cost estimate (Home Cleaning)")+labs(y = "Number of Reviews", x = "Cost estimate($)`", fill = "Hired or Not")
p
rm(p)
For almost all the hiring are concentrated between $75 - $150 of cost estimate and have high number of reviews (beyond 200).
With this, I can conclude that the hirings are impacted by Search Rank (Lower Ranks preferred), Number of reviews (More the reviews better it is),
Average Ratings, Cost estimate ($75 - $150) Day of the Week (more hirings are on Friday), Time of the day (2-8 favorable).
Using these factors I would conclude that
posting ads on the website on all days except on Fridays between 2-10 pm, and
charging pros to promote in the top 5 of search results could increase the revenues to the company.
When bookings are not made the ads would generate money during less hiring spots (Every day except on Fridays between 2-10 pm).
When a booking is made, then the top 5 pros would pay a premium to ThumbTack, for driving more eye balls onto them.
So in both cases it’s a win-win to the company.
LS0tDQp0aXRsZTogIkFuYWx5dGljcyBDaGFsbGVuZ2UgTm90ZWJvb2siDQphdXRob3I6ICJTdXNoYW50aCBDaGludGFsYXBhdGkiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30NCmxpYnJhcnkoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KCJsdWJyaWRhdGUiKQ0KbGlicmFyeSgicGxvdGx5IikNCmxpYnJhcnkoImNhcmV0IikNCmxpYnJhcnkoImUxMDcxIikNCmxpYnJhcnkoIkRNd1IiKQ0KbGlicmFyeSgiZm9ybWF0UiIpDQpgYGANCg0KYGBge3IgbG9hZGluZyBkYXRhLCB0aWR5ID0gVFJVRX0NCmMgPC0gcmVhZC5jc3YoIkNvbnRhY3RzLmNzdiIpDQpjIDwtIHNlbGVjdChjLCAtKCJjb250YWN0X2lkIikpDQpjb2xuYW1lcyhjKSA8LWMoInZpc2l0b3JfaWQiLCAicHJvX2lkIiwgImhpcmVkIikgDQoNCnYgPC0gcmVhZC5jc3YoIlZpc2l0b3JzLmNzdiIpDQp2IDwtIHZbLGMoInZpc2l0b3JfaWQiLCAicHJvX3VzZXJfaWQiLCAiY2F0ZWdvcnkiLCAicmVzdWx0X3Bvc2l0aW9uIiwgIm51bV9yZXZpZXdzIiwgImF2Z19yYXRpbmciLCAiY29zdF9lc3RpbWF0ZV9jZW50cyIsICJzZWFyY2hfdGltZXN0YW1wIiwgInByb19sYXN0X2FjdGl2ZV90aW1lX2JlZm9yZV9zZWFyY2giLCAgInNlcnZpY2VfcGFnZV92aWV3ZWQiKV0NCnYkY29zdF9lc3RpbWF0ZV9jZW50cyA8LSB2JGNvc3RfZXN0aW1hdGVfY2VudHMvMTAwDQpjb2xuYW1lcyh2KSA8LSBjKCJ2aXNpdG9yX2lkIiwgInByb19pZCIsICJjYXRlZ29yeSIsICJzZWFyY2hfcmFuayIsICJudW1fcmV2aWV3cyIsICJhdmdfcmF0aW5nIiwgImNvc3RfZXN0KCQpIiwgInNlYXJjaF90aW1lc3RhbXAiLCAicHJvX2xhc3RfYWN0aXZlX3RpbWVfYmVmb3JlX3NlYXJjaCIsICAicGFnZV92aWV3ZWQiKQ0KYGBgDQoNCkZpeGluZyBEYXRlIGFuZCBUaW1lIHN0YW1wcw0KDQpgYGB7ciBzcGxpdGluZyBkYXRlIGFuZCB0aW1lLCB0aWR5ID0gVFJVRX0NCnYgPC0gdiAlPiUgbXV0YXRlKHNlYXJjaF90aW1lc3RhbXAgPSBwYXJzZV9kYXRlX3RpbWUoc2VhcmNoX3RpbWVzdGFtcCwgb3JkZXJzID0gIlktbS1kIEhNUyIpKQ0KdiRzX2RhdGUgPC0gZm9ybWF0KHYkc2VhcmNoX3RpbWVzdGFtcCwgIiVZLSVtLSVkIikgJT4lIGFzLkRhdGUoKQ0KdiRzX2RheSA8LSBhcy5mYWN0b3Iod2Vla2RheXModiRzX2RhdGUpKQ0KbGV2ZWxzKHYkc19kYXkpIDwtIGMoIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIsICJGcmlkYXkiLCAiU2F0dXJkYXkiLCAiU3VuZGF5IikNCnYkc19ociA8LSBmb3JtYXQodiRzZWFyY2hfdGltZXN0YW1wLCAiJUguJU0iKSAlPiUgYXMubnVtZXJpYygpDQp2JHNfaHIgPC0gYXMuaW50ZWdlcihmbG9vcih2JHNfaHIpKQ0KdiA8LSBzZWxlY3QodiwgLSgic2VhcmNoX3RpbWVzdGFtcCIpKQ0KDQp2IDwtIHYgJT4lIG11dGF0ZShwcm9fbGFzdF9hY3RpdmVfdGltZV9iZWZvcmVfc2VhcmNoID0gcGFyc2VfZGF0ZV90aW1lKHByb19sYXN0X2FjdGl2ZV90aW1lX2JlZm9yZV9zZWFyY2gsIG9yZGVycyA9ICJZLW0tZCBITVMiKSkNCnYkcC5sX2RhdGUgPC0gZm9ybWF0KHYkcHJvX2xhc3RfYWN0aXZlX3RpbWVfYmVmb3JlX3NlYXJjaCwgIiVZLSVtLSVkIikgJT4lIGFzLkRhdGUoKQ0KdiRwLmxfaHIgPC0gZm9ybWF0KHYkcHJvX2xhc3RfYWN0aXZlX3RpbWVfYmVmb3JlX3NlYXJjaCwgIiVILiVNIikgJT4lIGFzLm51bWVyaWMoKQ0KdiRwLmxfaHIgPC0gZmxvb3IodiRwLmxfaHIpDQp2IDwtIHNlbGVjdCh2LCAtKCJwcm9fbGFzdF9hY3RpdmVfdGltZV9iZWZvcmVfc2VhcmNoIikpDQoNCnYgPC0gdiAlPiUgbXV0YXRlKGRpZmYgPSBhcy5pbnRlZ2VyKHNfZGF0ZS0gcC5sX2RhdGUpKQ0KYGBgDQoNCkhhbmRsaW5nIGFuZCBKb2luaW5nIGRhdGEgZmlsZXMNCg0KYGBge3IgSm9pbmluZyBkYXRhLCB0aWR5ID0gVFJVRX0NCmV4cGxvIDwtIHYNCmRhdGEgPC0gdiAlPiUgbGVmdF9qb2luKGMsIGJ5ID0gYygidmlzaXRvcl9pZCIsICJwcm9faWQiKSkNCmRhdGEgPC0gZGF0YVshaXMubmEoZGF0YSRoaXJlZCksIF0NCnJtKGMpDQpybSh2KQ0KZHRfdG90YWwgPC0gc2VsZWN0KGRhdGEsIC1jKCJ2aXNpdG9yX2lkIiwgInByb19pZCIsInNfZGF0ZSIsICJwLmxfZGF0ZSIsICJwLmxfaHIiKSkNCg0Kcm0oZGF0YSkNCmR0X3RvdGFsJGhpcmVkIDwtIGFzLmZhY3RvcihpZmVsc2UoZHRfdG90YWwkaGlyZWQgPT0gVCwgIkhpcmVkIiwgIk5vdCBIaXJlZCIpKQ0KbGV2ZWxzKGR0X3RvdGFsJGhpcmVkKSA8LSBjKCJIaXJlZCIsICJOb3QgSGlyZWQiKQ0KYGBgDQoNCkNoZWNraW5nIHRoZSBWYXJpYWJsZSBvZiBjb25jZXJuDQoNCmBgYHtyIEluc3BlY3RpbmcgdGhlIHZhcmlhYmxlIG9mIGNvbmNlcm4sIHRpZHkgPSBUUlVFfQ0KdGFibGUoZHRfdG90YWwkaGlyZWQpDQpgYGANCg0KVGhpcyBpcyBhbiB1bmJhbGVuY2VkIGRhdGEgc2V0IGFuZCByZXF1aXJlZCBPdmVyIFNhbXBsaW5nIGZvciB0aGUgSGlyZWQgcGFydCB0byBnZXQgYmV0dGVyIHJlc3VsdHMuDQoNCg0KU01PVEUgZm9yIGVxdWFsaXppbmcgdGhlIGRhdGENCg0KYGBge3IgU21vdGUgdGVjaG5pcXVlLCB0aWR5ID0gVFJVRX0NCnNldC5zZWVkKDEpDQpkdF90b3RhbCA8LSBTTU9URShoaXJlZH4uLCBkdF90b3RhbCwgcGVyYy5vdmVyID0gNTAwLCBrID0gNSwgcGVyYy51bmRlciA9IDEwMCkNCnRhYmxlKGR0X3RvdGFsJGhpcmVkKQ0KYGBgDQoNClNwbGl0aW5nIGludG8gZGF0YSBmb3IgdHdvIGRpZmZlcmVudCBjYXRlZ29yaWVzDQoNCmBgYHtyIFNwbGl0aW5nIGRhdGEsIHRpZHkgPSBUUlVFfQ0KI0hDIGZvciBIb3VzZSBjbGVhbmluZyBhbmQgTE0gZm9yIExvY2FsIG1vdmluZw0KdGFibGUoZHRfdG90YWwkY2F0ZWdvcnkpDQpkdF9oYyA8LSBkdF90b3RhbCAlPiUgZmlsdGVyKGNhdGVnb3J5ID09ICJIb3VzZSBDbGVhbmluZyIpICU+JSBzZWxlY3QoLSJjYXRlZ29yeSIpDQpkdF9sbSA8LSBkdF90b3RhbCAlPiUgZmlsdGVyKGNhdGVnb3J5ICE9ICJIb3VzZSBDbGVhbmluZyIpICU+JSBzZWxlY3QoLSJjYXRlZ29yeSIpDQpgYGANCg0KQ2hlY2tpbmcgZm9yIHBhcmFtZXRlcnMgdGhhdCBhY3R1YWxseSBlZmZlY3QgdGhlIEhpcmluZw0KRm9yIHRoZSB0b3RhbCBkYXRhIGFzIGEgd2hvbGUNCg0KYGBge3IgTW9kZWxpbmcsIHRpZHkgPSBUUlVFfQ0KZml0IDwtIGdsbShoaXJlZH4uLCBkYXRhID0gZHRfdG90YWwsIGZhbWlseSA9IGJpbm9taWFsKQ0Kc3VtbWFyeShmaXQpDQpkYXRhLmZyYW1lKHN1bW1hcnkoZml0KSRjb2VmW3N1bW1hcnkoZml0KSRjb2VmWyw0XSA8PSAuMDUsIDRdKQ0KYGBgDQoNCkZvciBMb2NhbCBNb3ZpbmcNCg0KYGBge3IgTW9kZWxpbmcgTE0sIHRpZHkgPSBUUlVFfQ0KZml0IDwtIGdsbShoaXJlZH4uLCBkYXRhID0gZHRfbG0sIGZhbWlseSA9IGJpbm9taWFsKQ0Kc3VtbWFyeShmaXQpDQpkYXRhLmZyYW1lKHN1bW1hcnkoZml0KSRjb2VmW3N1bW1hcnkoZml0KSRjb2VmWyw0XSA8PSAuMDUsIDRdKQ0KYGBgDQoNCkZvciBIb21lIENsZWFuaW5nDQoNCmBgYHtyIE1vZGVsaW5nIEhDLCB0aWR5ID0gVFJVRX0NCmZpdCA8LSBnbG0oaGlyZWR+LiwgZGF0YSA9IGR0X2hjLCBmYW1pbHkgPSBiaW5vbWlhbCkNCnN1bW1hcnkoZml0KQ0KZGF0YS5mcmFtZShzdW1tYXJ5KGZpdCkkY29lZltzdW1tYXJ5KGZpdCkkY29lZlssNF0gPD0gLjA1LCA0XSkNCnJtKGZpdCkNCmBgYA0KDQoNCkVYUExPUkFUT1JZIEFOQUxZU0lTDQoNCg0KbnVtX3Jldmlld3MgVlMgaGlyaW5nDQpgYGB7ciBuciB2cyBoaXJpbmcsIHRpZHkgPSBUUlVFfQ0KcCA8LSBkdF90b3RhbCAlPiUgbmEub21pdChjKCJoaXJlZCIsICJudW1fcmV2aWV3cyIpKSAlPiUgZ2dwbG90KGFlcyh4ID0gaGlyZWQsIHkgPSBhcy5pbnRlZ2VyKG51bV9yZXZpZXdzKSwgZmlsbCA9IGhpcmVkKSkrZ2VvbV9ib3hwbG90KGNvbG9yID0gImJsYWNrIiwgb3V0bGllci5zaXplID0gMCwgb3V0bGllci5zaGFwZSA9IE5BKStnZ3RpdGxlKCJOdW1iZXIgb2YgUmV2aWV3cyBWcyBIaXJpbmcgKEZvciB0aGUgdG90YWwgZGF0YSkiKStsYWJzKHkgPSAiTnVtYmVyIG9mIHJldmlld3MiLCB4ID0gIkhpcmVkIG9yIE5vdCIsIGZpbGwgPSAiSGlyZWQgb3IgTm90IikNCnANCg0KcCA8LSBkdF9sbSAlPiUgbmEub21pdChjKCJoaXJlZCIsICJudW1fcmV2aWV3cyIpKSAlPiUgZ2dwbG90KGFlcyh4ID0gaGlyZWQsIHkgPSBhcy5pbnRlZ2VyKG51bV9yZXZpZXdzKSwgZmlsbCA9IGhpcmVkKSkrZ2VvbV9ib3hwbG90KGNvbG9yID0gImJsYWNrIiwgb3V0bGllci5zaXplID0gMCwgb3V0bGllci5zaGFwZSA9IE5BKStnZ3RpdGxlKCJOdW1iZXIgb2YgUmV2aWV3cyBWcyBIaXJpbmcgKEZvciBMb2NhbCBNb3ZpbmcpIikrbGFicyh5ID0gIk51bWJlciBvZiByZXZpZXdzIiwgeCA9ICJIaXJlZCBvciBOb3QiLCBmaWxsID0gIkhpcmVkIG9yIE5vdCIpDQpwDQoNCnAgPC0gZHRfaGMgJT4lIG5hLm9taXQoYygiaGlyZWQiLCAibnVtX3Jldmlld3MiKSkgJT4lIGdncGxvdChhZXMoeCA9IGhpcmVkLCB5ID0gYXMuaW50ZWdlcihudW1fcmV2aWV3cyksIGZpbGwgPSBoaXJlZCkpK2dlb21fYm94cGxvdChjb2xvciA9ICJibGFjayIsIG91dGxpZXIuc2l6ZSA9IDAsIG91dGxpZXIuc2hhcGUgPSBOQSkrZ2d0aXRsZSgiTnVtYmVyIG9mIFJldmlld3MgVnMgSGlyaW5nIChGb3IgSG9tZSBDbGVhbmluZykiKStsYWJzKHkgPSAiTnVtYmVyIG9mIHJldmlld3MiLCB4ID0gIkhpcmVkIG9yIE5vdCIsIGZpbGwgPSAiSGlyZWQgb3IgTm90IikNCnANCnJtKHApDQpgYGANCg0KSXQgaXMgdmVyeSBjbGVhciB0aGF0IHRoZSBudW1iZXIgb2YgcmV2aWV3cyBoYXZlIGEgc2lnbmlmaWNhbnQgZWZmZWN0IG9uIHRoZSBoaXJpbmcuDQpBcyB0aGUgbnVtYmVyIG9mIHJldmlld3MgaW5jcmVhc2VzLCB0aGUgcHJvYmFiaWxpdHkgb2YgYmVpbmcgaGlyZWQgYWxzbyBpbmNyZWFzZXMuDQoNCg0KU2VhcmNoIFJhbmsgVnMgSGlyaW5nICANCiAgDQpgYGB7ciBzciB2cyBoaXJpbmd9DQpwIDwtIGR0X3RvdGFsICU+JSBuYS5vbWl0KGMoInNlYXJjaF9yYW5rIiwgImhpcmVkIikpICU+JSBmaWx0ZXIoc2VhcmNoX3JhbmsgPiA1KSAlPiUgZ2dwbG90KGFlcyh4ID0gaGlyZWQsIHkgPSBzZWFyY2hfcmFuaywgZmlsbCA9IGhpcmVkKSkrZ2VvbV9ib3hwbG90KGNvbG9yID0gImJsYWNrIiwgb3V0bGllci5zaXplID0gMCwgb3V0bGllci5zaGFwZSA9IE5BKStnZ3RpdGxlKCJFZmZlY3Qgb2YgU2VhcmNoIFJhbmtzIGJleW9uZCA1IG9uIEhpcmluZyIpK2xhYnMoeSA9ICJTZWFyY2ggUmFuayIsIHggPSAiSGlyZWQgb3IgTm90IiwgZmlsbCA9ICJIaXJlZCBvciBOb3QiKQ0KcA0KDQpwIDwtIGR0X2xtICU+JSBuYS5vbWl0KGMoInNlYXJjaF9yYW5rIiwgImhpcmVkIikpICU+JSBmaWx0ZXIoc2VhcmNoX3JhbmsgPiA1KSAlPiUgZ2dwbG90KGFlcyh4ID0gaGlyZWQsIHkgPSBzZWFyY2hfcmFuaywgZmlsbCA9IGhpcmVkKSkrZ2VvbV9ib3hwbG90KGNvbG9yID0gImJsYWNrIiwgb3V0bGllci5zaXplID0gMCwgb3V0bGllci5zaGFwZSA9IE5BKStnZ3RpdGxlKCJFZmZlY3Qgb2YgU2VhcmNoIFJhbmtzIGJleW9uZCA1IG9uIEhpcmluZyAoTG9jYWwgTW92ZXJzKSIpK2xhYnMoeSA9ICJTZWFyY2ggUmFuayIsIHggPSAiSGlyZWQgb3IgTm90IiwgZmlsbCA9ICJIaXJlZCBvciBOb3QiKQ0KcA0KDQoNCnAgPC0gZHRfaGMgJT4lIG5hLm9taXQoYygic2VhcmNoX3JhbmsiLCAiaGlyZWQiKSkgJT4lIGZpbHRlcihzZWFyY2hfcmFuayA+IDUpICU+JSBnZ3Bsb3QoYWVzKHggPSBoaXJlZCwgeSA9IHNlYXJjaF9yYW5rLCBmaWxsID0gaGlyZWQpKStnZW9tX2JveHBsb3QoY29sb3IgPSAiYmxhY2siLCBvdXRsaWVyLnNpemUgPSAwLCBvdXRsaWVyLnNoYXBlID0gTkEpK2dndGl0bGUoIkVmZmVjdCBvZiBTZWFyY2ggUmFua3MgYmV5b25kIDUgb24gSGlyaW5nIChIb21lIENsZWFuZXJzKSIpK2xhYnMoeSA9ICJTZWFyY2ggUmFuayIsIHggPSAiSGlyZWQgb3IgTm90IiwgZmlsbCA9ICJIaXJlZCBvciBOb3QiKQ0KcA0Kcm0ocCkNCmBgYA0KDQoNClRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZWZmZWN0IG9mIHNlYXJjaCByYW5rcyBiZXlvbmQgNSBvbiBIb21lIENsZWFuZXJzIGFuZCBhIG1pbGQgZWZmZWN0IG9uIExvY2FsIE1vdmVycy4gQXMgdGhlIHNlYXJjaCByYW5rIGluY3JlYXNlcyB0aGVyZSBwb3NpYmlsaXR5IG9mIGJlaW5nIGhpcmVkIGRlY3JlYXNlcy4NCg0KDQpOdW1iZXIgb2YgUmV2aWV3cywgQXZnIFJldmlld3MgVnMgSGlyaW5nDQpgYGB7ciBuciAmIGFyIHZzIGhpcmluZ30NCnAgPC0gZHRfdG90YWwgJT4lIG5hLm9taXQoYygibnVtX3Jldmlld3MiLCAiYXZnX3Jldmlld3MiLCAiaGlyZWQiKSkgJT4lIGdncGxvdChhZXMoeCA9IGF2Z19yYXRpbmcsIHkgPSBudW1fcmV2aWV3cywgc2hhcGUgPSBoaXJlZCwgY29sID0gaGlyZWQpKStzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMywgMSkpKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJyNFNjlGMDAnLCAnIzU2QjRFOScpKStnZW9tX3BvaW50KCkrZ2d0aXRsZSgiSGlyaW5nIGZvciBoaWdoZXIgUmF0aW5nIGFuZCBoaWdoZXIgbnVtYmVyIG9mIHJldmlld3MiKStsYWJzKHkgPSAiTnVtYmVyIG9mIFJldmlld3MiLCB4ID0gIkF2ZyBSYXRpbmciLCBmaWxsID0gIkhpcmVkIG9yIE5vdCIpDQpwDQoNCnAgPC0gZHRfbG0gJT4lIG5hLm9taXQoYygibnVtX3Jldmlld3MiLCAiYXZnX3Jldmlld3MiLCAiaGlyZWQiKSkgJT4lIGdncGxvdChhZXMoeCA9IGF2Z19yYXRpbmcsIHkgPSBudW1fcmV2aWV3cywgc2hhcGUgPSBoaXJlZCwgY29sID0gaGlyZWQpKStzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMywgMSkpKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJyNFNjlGMDAnLCAnIzU2QjRFOScpKStnZW9tX3BvaW50KCkrZ2d0aXRsZSgiSGlyaW5nIGZvciBoaWdoZXIgUmF0aW5nIGFuZCBoaWdoZXIgbnVtYmVyIG9mIHJldmlld3MgKExvY2FsIE1vdmVycykiKStsYWJzKHkgPSAiTnVtYmVyIG9mIFJldmlld3MiLCB4ID0gIkF2ZyBSYXRpbmciLCBmaWxsID0gIkhpcmVkIG9yIE5vdCIpDQpwDQoNCnAgPC0gZHRfaGMgJT4lIG5hLm9taXQoYygibnVtX3Jldmlld3MiLCAiYXZnX3Jldmlld3MiLCAiaGlyZWQiKSkgJT4lIGdncGxvdChhZXMoeCA9IGF2Z19yYXRpbmcsIHkgPSBudW1fcmV2aWV3cywgc2hhcGUgPSBoaXJlZCwgY29sID0gaGlyZWQpKStzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMywgMSkpKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoJyNFNjlGMDAnLCAnIzU2QjRFOScpKStnZW9tX3BvaW50KCkrZ2d0aXRsZSgiSGlyaW5nIGZvciBoaWdoZXIgUmF0aW5nIGFuZCBoaWdoZXIgbnVtYmVyIG9mIHJldmlld3MgKEhvbWUgQ2xlYW5lcnMpIikrbGFicyh5ID0gIk51bWJlciBvZiBSZXZpZXdzIiwgeCA9ICJBdmcgUmF0aW5nIiwgZmlsbCA9ICJIaXJlZCBvciBOb3QiKQ0KcA0KDQpybShwKQ0KYGBgDQoNCkluIGFsbCBvZiB0aGUgcGxvdHMsIGl0IGlzIHByZXR0eSBldmlkZW50IHRoYXQgZm9yIGhpZ2hlciBOdW1iZXIgb2YgcmV2aWV3cyBhbmQgaGlnaGVyIHJhdGluZyB0aGUgY2hhbmdlIG9mIGJlaW5nIGhpcmVkIGlzIGhpZ2gNCg0KDQoNCkVmZmVjdCBvZiBTZWFyY2ggdGltZSBvbiBoaXJpbmcNCg0KYGBge3Igc3QgdnMgaGlyaW5nfQ0KcCA8LSBkdF90b3RhbCAlPiUgbmEub21pdChjKCJzX2hyIiwgImhpcmVkIikpICU+JSBnZ3Bsb3QoYWVzKHggPSBoaXJlZCwgeSA9IHNfaHIsIGZpbGwgPSBoaXJlZCkpK2dlb21fYm94cGxvdChjb2xvciA9ICJibGFjayIsIG91dGxpZXIuc2l6ZSA9IDAsIG91dGxpZXIuc2hhcGUgPSBOQSkrZ2d0aXRsZSgiU2VhcmNoIFRpbWUgdnMgSGlyaW5nIikrbGFicyh5ID0gIlNlYXJjaCB0aW1lICgyNCBociBmb3JtYXQpIiwgeCA9ICJIaXJlZCBvciBOb3QiLCBmaWxsID0gIkhpcmVkIG9yIE5vdCIpDQpwDQoNCnAgPC0gZHRfbG0gJT4lIG5hLm9taXQoYygic19ociIsICJoaXJlZCIpKSAlPiUgZ2dwbG90KGFlcyh4ID0gaGlyZWQsIHkgPSBzX2hyLCBmaWxsID0gaGlyZWQpKStnZW9tX2JveHBsb3QoY29sb3IgPSAiYmxhY2siLCBvdXRsaWVyLnNpemUgPSAwLCBvdXRsaWVyLnNoYXBlID0gTkEpK2dndGl0bGUoIlNlYXJjaCBUaW1lIHZzIEhpcmluZyAoTG9jYWwgTW92aW5nKSIpK2xhYnMoeSA9ICJTZWFyY2ggdGltZSAoMjQgaHIgZm9ybWF0KSIsIHggPSAiSGlyZWQgb3IgTm90IiwgZmlsbCA9ICJIaXJlZCBvciBOb3QiKQ0KcA0KDQpwIDwtIGR0X2hjICU+JSBuYS5vbWl0KGMoInNfaHIiLCAiaGlyZWQiKSkgJT4lIGdncGxvdChhZXMoeCA9IGhpcmVkLCB5ID0gc19ociwgZmlsbCA9IGhpcmVkKSkrZ2VvbV9ib3hwbG90KGNvbG9yID0gImJsYWNrIiwgb3V0bGllci5zaXplID0gMCwgb3V0bGllci5zaGFwZSA9IE5BKStnZ3RpdGxlKCJTZWFyY2ggVGltZSB2cyBIaXJpbmcgKEhvbWUgQ2xlYW5pbmcpIikrbGFicyh5ID0gIlNlYXJjaCB0aW1lICgyNCBociBmb3JtYXQpIiwgeCA9ICJIaXJlZCBvciBOb3QiLCBmaWxsID0gIkhpcmVkIG9yIE5vdCIpDQpwDQpybShwKQ0KYGBgDQoNCkFsbCBwbG90cyBpbmRpY2F0ZWQgbW9zdCBvZiB0aGUgaGlyaW5nIGhhcHBlbnMgaW4gdGhlIHNlY29uZCBoYWxmIG9mIHRoZSBkYXksIHNvbWV0aW1lIGJldHdlZW4gMi04cG0uDQoNCg0KDQpFZmZlY3Qgb2YgRGF5IG9uIHRoZSBoaXJpbmcNCmBgYHtyIGRheSB2cyBoaXJpbmd9DQpwIDwtIGR0X3RvdGFsICU+JSBuYS5vbWl0KGMoInNfZGF5IiwgImhpcmVkIikpICU+JSBnZ3Bsb3QoYWVzKHggPSBoaXJlZCwgZmlsbCA9IGhpcmVkKSkgKyBnZW9tX2Jhcihjb2xvciA9ICJibGFjayIsIHBvc2l0aW9uID0gImlkZW50aXR5IikrIGZhY2V0X3dyYXAoc19kYXl+LikrDQogIGdndGl0bGUoIkRheSBvZiB0aGUgd2VlayBWcyBIaXJpbmciKStsYWJzKHggPSAiIiwgeSA9ICJGcmVxdWVuY3kgb2YgSGlyaW5nIikNCnANCg0KcCA8LSBkdF9sbSAlPiUgbmEub21pdChjKCJzX2RheSIsICJoaXJlZCIpKSAlPiUgZ2dwbG90KGFlcyh4ID0gaGlyZWQsIGZpbGwgPSBoaXJlZCkpICsgZ2VvbV9iYXIoY29sb3IgPSAiYmxhY2siLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpKyBmYWNldF93cmFwKHNfZGF5fi4pKw0KICBnZ3RpdGxlKCJEYXkgb2YgdGhlIHdlZWsgVnMgSGlyaW5nIChMb2NhbCBNb3ZpbmcpIikrbGFicyh4ID0gIiIsIHkgPSAiRnJlcXVlbmN5IG9mIEhpcmluZyIpDQpwDQoNCnAgPC0gZHRfaGMgJT4lIG5hLm9taXQoYygic19kYXkiLCAiaGlyZWQiKSkgJT4lIGdncGxvdChhZXMoeCA9IGhpcmVkLCBmaWxsID0gaGlyZWQpKSArIGdlb21fYmFyKGNvbG9yID0gImJsYWNrIiwgcG9zaXRpb24gPSAiaWRlbnRpdHkiKSsgZmFjZXRfd3JhcChzX2RheX4uKSsNCiAgZ2d0aXRsZSgiRGF5IG9mIHRoZSB3ZWVrIFZzIEhpcmluZyAoSG9tZSBDbGVhbmluZykiKStsYWJzKHggPSAiIiwgeSA9ICJGcmVxdWVuY3kgb2YgSGlyaW5nIikNCnANCnJtKHApDQpgYGANCg0KDQpGcmlkYXkgc2VlbXMgdG8gdGhlIGJlICJUaGUgRGF5IiB3aGVuIHRoZSB0cmFmZmljIGlzIGhpZ2ggYW5kIGEgbG90IG9mIGhpcmluZ3MgYWN0dWFsbHkgaGFwcGVuIGZvciBib3RoIHRoZSBjYXRlZ29yaWVzLg0KDQoNCg0KDQpOdW1iZXIgb2YgUmV2aWV3cywgQ29zdCBlc3QgVnMgSGlyaW5nDQpgYGB7ciBuciAmIGNlIHZzIGhpcmluZ30NCnAgPC0gZHRfdG90YWwgJT4lIG5hLm9taXQoYygibnVtX3Jldmlld3MiLCAiYGNvc3RfZXN0KCQpYCIsICJoaXJlZCIpKSAlPiUgZ2dwbG90KGFlcyh4ID0gYGNvc3RfZXN0KCQpYCwgeSA9IG51bV9yZXZpZXdzLCBzaGFwZSA9IGhpcmVkLCBjb2wgPSBoaXJlZCkpK3NjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygzLCAxKSkrIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnI0U2OUYwMCcsICcjNTZCNEU5JykpK2dlb21fcG9pbnQoKStnZ3RpdGxlKCJIaXJpbmcgZm9yIE51bWJlciBvZiByZXZpZXdzIGFuZCBDb3N0IGVzdGltYXRlIikrbGFicyh5ID0gIk51bWJlciBvZiBSZXZpZXdzIiwgeCA9ICJDb3N0IGVzdGltYXRlKCQpYCIsIGZpbGwgPSAiSGlyZWQgb3IgTm90IikNCnANCg0KcCA8LSBkdF9sbSAlPiUgbmEub21pdChjKCJudW1fcmV2aWV3cyIsICJgY29zdF9lc3QoJClgIiwgImhpcmVkIikpICU+JSBnZ3Bsb3QoYWVzKHggPSBgY29zdF9lc3QoJClgLCB5ID0gbnVtX3Jldmlld3MsIHNoYXBlID0gaGlyZWQsIGNvbCA9IGhpcmVkKSkrc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDMsIDEpKSsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCcjRTY5RjAwJywgJyM1NkI0RTknKSkrZ2VvbV9wb2ludCgpK2dndGl0bGUoIkhpcmluZyBmb3IgTnVtYmVyIG9mIHJldmlld3MgYW5kIENvc3QgZXN0aW1hdGUgKExvY2FsIE1vdmluZykiKStsYWJzKHkgPSAiTnVtYmVyIG9mIFJldmlld3MiLCB4ID0gIkNvc3QgZXN0aW1hdGUoJClgIiwgZmlsbCA9ICJIaXJlZCBvciBOb3QiKQ0KcA0KDQoNCnAgPC0gZHRfaGMgJT4lIG5hLm9taXQoYygibnVtX3Jldmlld3MiLCAiYGNvc3RfZXN0KCQpYCIsICJoaXJlZCIpKSAlPiUgZ2dwbG90KGFlcyh4ID0gYGNvc3RfZXN0KCQpYCwgeSA9IG51bV9yZXZpZXdzLCBzaGFwZSA9IGhpcmVkLCBjb2wgPSBoaXJlZCkpK3NjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygzLCAxKSkrIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9YygnI0U2OUYwMCcsICcjNTZCNEU5JykpK2dlb21fcG9pbnQoKStnZ3RpdGxlKCJIaXJpbmcgZm9yIE51bWJlciBvZiByZXZpZXdzIGFuZCBDb3N0IGVzdGltYXRlIChIb21lIENsZWFuaW5nKSIpK2xhYnMoeSA9ICJOdW1iZXIgb2YgUmV2aWV3cyIsIHggPSAiQ29zdCBlc3RpbWF0ZSgkKWAiLCBmaWxsID0gIkhpcmVkIG9yIE5vdCIpDQpwDQoNCnJtKHApDQpgYGANCg0KDQpGb3IgYWxtb3N0IGFsbCB0aGUgaGlyaW5nIGFyZSBjb25jZW50cmF0ZWQgYmV0d2VlbiAkNzUgLSAkMTUwIG9mIGNvc3QgZXN0aW1hdGUgYW5kIGhhdmUgaGlnaCBudW1iZXIgb2YgcmV2aWV3cyAoYmV5b25kIDIwMCkuDQoNCg0KDQpXaXRoIHRoaXMsIEkgY2FuIGNvbmNsdWRlIHRoYXQgdGhlIGhpcmluZ3MgYXJlIGltcGFjdGVkIGJ5IFNlYXJjaCBSYW5rIChMb3dlciBSYW5rcyBwcmVmZXJyZWQpLCBOdW1iZXIgb2YgcmV2aWV3cyAoTW9yZSB0aGUgcmV2aWV3cyBiZXR0ZXIgaXQgaXMpLA0KDQpBdmVyYWdlIFJhdGluZ3MsIENvc3QgZXN0aW1hdGUgKCQ3NSAtICQxNTApIERheSBvZiB0aGUgV2VlayAobW9yZSBoaXJpbmdzIGFyZSBvbiBGcmlkYXkpLCBUaW1lIG9mIHRoZSBkYXkgKDItOCBmYXZvcmFibGUpLg0KDQpVc2luZyB0aGVzZSBmYWN0b3JzIEkgd291bGQgY29uY2x1ZGUgdGhhdA0KDQooaSkgcG9zdGluZyBhZHMgb24gdGhlIHdlYnNpdGUgb24gYWxsIGRheXMgZXhjZXB0IG9uIEZyaWRheXMgYmV0d2VlbiAyLTEwIHBtLCBhbmQNCg0KKGlpKSBjaGFyZ2luZyBwcm9zIHRvIHByb21vdGUgaW4gdGhlIHRvcCA1IG9mIHNlYXJjaCByZXN1bHRzIGNvdWxkIGluY3JlYXNlIHRoZSByZXZlbnVlcyB0byB0aGUgY29tcGFueS4NCg0KV2hlbiBib29raW5ncyBhcmUgbm90IG1hZGUgdGhlIGFkcyB3b3VsZCBnZW5lcmF0ZSBtb25leSBkdXJpbmcgbGVzcyBoaXJpbmcgc3BvdHMgKEV2ZXJ5IGRheSBleGNlcHQgb24gRnJpZGF5cyBiZXR3ZWVuIDItMTAgcG0pLg0KDQpXaGVuIGEgYm9va2luZyBpcyBtYWRlLCB0aGVuIHRoZSB0b3AgNSBwcm9zIHdvdWxkIHBheSBhIHByZW1pdW0gdG8gVGh1bWJUYWNrLCBmb3IgZHJpdmluZyBtb3JlIGV5ZSBiYWxscyBvbnRvIHRoZW0uDQoNCg0KU28gaW4gYm90aCBjYXNlcyBpdCdzIGEgd2luLXdpbiB0byB0aGUgY29tcGFueS4=