Stage 1: Collect Data
# Clear workspace:
rm(list = ls())
# Load some R packages:
library(rvest)
library(tidyverse)
library(stringi)
# Url links of homes for sales in Hanoi:
url <- "https://alonhadat.com.vn/can-ban-nha-ha-noi-t1.htm"
# Extract links at distric level:
url %>%
read_html() %>%
html_nodes(xpath = '//*[@id="right"]/div[4]/div[1]/ul') %>%
html_nodes("a") %>%
html_attr("href") %>%
.[-1] -> district_url
# We assume that there are 1000 pages:
n_pages <- paste0("https://alonhadat.com.vn", str_sub(district_url, start = 1, end = str_count(district_url) - 4), "/trang-1000.htm")
# Function extracts n pages:
get_n_pages <- function(link) {
link %>%
read_html() %>%
html_nodes(xpath = '//*[@id="left"]/div[2]/a[11]') %>%
html_text() %>%
as.numeric() %>%
return()
}
# Use the function:
number_pages <- lapply(n_pages, get_n_pages)
actual_pages <- n_pages[as.vector(number_pages) > 0]
actual_pages <- actual_pages[!is.na(actual_pages)]
number_pages <- number_pages %>% unlist()
# Number of pages:
df_pages <- data.frame(base_url = str_sub(actual_pages, start = 1, end = str_count(actual_pages) - 8), n_pages = number_pages)
# https://alonhadat.com.vn/can-ban-nha-quan-ba-dinh-ha-noi-q407/trang-2.htm
# Function extract all links for a specific district selected:
extract_PageLinks <- function(...) {
n <- nrow(df_pages)
pageLinks <- c()
for (i in 1:n) {
my_pages <- paste0(df_pages[i, 1], 1:df_pages[i, 2], ".htm")
pageLinks <- c(pageLinks, my_pages)
}
return(pageLinks)
}
# Use the function:
system.time(all_pages_for_RE <- extract_PageLinks())
# Get all links for homes:
get_specific_RE_links <- function(x) {
x %>%
read_html() %>%
html_nodes(xpath = '//*[@id="left"]/div[1]') %>%
html_nodes("a") %>%
html_attr("href") %>%
unique() -> m
return(paste0("https://alonhadat.com.vn", m))
}
all_links_forRE <- lapply(all_pages_for_RE, get_specific_RE_links) %>% unlist()
# Function collect all information for a specific home for sale:
get_allData_RE <- function(link) {
link %>%
read_html() -> m
m %>%
html_nodes("td") %>%
html_text(trim = TRUE) %>%
matrix(nrow = 2) %>%
as.data.frame() %>%
mutate_all(as.character) -> n
n %>%
slice(2) -> main_df
names(main_df) <- stringi::stri_trans_general(n %>% slice(1) %>% as.vector(), "Latin-ASCII") %>% str_replace_all(" ", "_")
m %>%
html_nodes(xpath = '//*[@id="left"]/div[1]/div[2]') %>%
html_text(trim = TRUE) -> re_des
if (length(re_des) == 0) {
re_des <- NA
}
m %>%
html_nodes(xpath = '//*[@id="left"]/div[1]/div[3]') %>%
html_text(trim = TRUE) -> gia_dien_tich
if (length(gia_dien_tich) == 0) {
gia_dien_tich <- NA
}
m %>%
html_nodes(xpath = '//*[@id="left"]/div[1]/div[4]') %>%
html_text(trim = TRUE) -> re_add
if (length(re_add) == 0) {
re_add <- NA
}
total_df <- main_df %>%
mutate(mieu_ta = re_des, gia_dien_tich = gia_dien_tich, dia_chi = re_add, source_link = link)
Sys.sleep(1)
return(total_df)
}
get_allData_RE_tryCatch <- function(link) {
return(tryCatch(get_allData_RE(link), error = function(e) {NULL}))
}
lapply(all_links_forRE, get_allData_RE_tryCatch) -> allData_RE_list
save(allData_RE_list, file = "allData_RE_list_24_06.RData")
# load("C:\\Users\\ADMIN\\Documents\\allData_RE_list_24_06.RData")
Stage 2: Data Processing
# Clear workspace:
rm(list = ls())
# Load some R packages:
library(tidyverse)
library(stringi)
# Load data:
load("C:\\Users\\Zbook\\Desktop\\re_project\\allData_RE_list_24_06.RData")
# Convert data to data frame:
df_allData <- do.call("bind_rows", allData_RE_list)
# Convert to Latin character:
df_allData %>% mutate_all(function(x) {stri_trans_general(x, "Latin-ASCII")}) -> df_allData
# Function Extracts area data:
extract_area <- function(x) {
x %>%
str_split(":", simplify = TRUE) %>%
data.frame() -> gia_dt
gia_dt$X3 %>%
str_split("m", simplify = TRUE) %>%
data.frame() %>%
pull(X1) %>%
as.character() %>%
str_squish() %>%
as.numeric() %>%
return()
}
# Extract price data:
extract_price <- function(x) {
x %>%
str_split(":", simplify = TRUE) %>%
data.frame() -> gia_dt
gia_dt$X2 %>%
str_to_lower() %>%
str_split("ty", simplify = TRUE) %>%
data.frame() %>%
pull(X1) %>%
as.character() %>%
str_split(",", simplify = TRUE) %>%
data.frame() %>%
mutate_all(function(x) {str_squish(x)}) %>%
mutate(X3 = case_when(str_count(X2) == 1 ~ paste0(X2, "0"),
str_count(X2) == 0 ~ "00",
TRUE ~ X2)) %>%
mutate(price = as.numeric(X1) + as.numeric(X3) / 100) %>%
pull(price) %>%
return()
}
# Function extracts add (district level):
extract_add <- function(x) {
x %>%
str_to_lower() %>%
str_split("\\, quan", simplify = TRUE) %>%
data.frame() %>%
pull(X2) %>%
as.character() %>%
str_squish() %>%
str_split("\\,", simplify = TRUE) %>%
data.frame() %>%
pull(X1) %>%
as.character() %>%
return()
}
#=========================================================================================================
# Function extract agent's phone numbers
# https://stackoverflow.com/questions/17215789/extract-a-substring-in-r-according-to-a-pattern
# https://rstudio-pubs-static.s3.amazonaws.com/74603_76cd14d5983f47408fdf0b323550b846.html
#=========================================================================================================
# Function 1:
extract_phoneNumber1 <- function(x) {
x %>%
str_replace_all(" ", "") %>%
str_extract("[0-9]{10}") %>%
return()
}
df_allData %>%
mutate(phone_contact = extract_phoneNumber1(mieu_ta)) %>%
filter(is.na(phone_contact)) %>%
head() %>%
select(source_link, mieu_ta, phone_contact)
# Function 2:
extract_phoneNumber2 <- function(x) {
x %>%
str_replace_all(" ", "") %>%
str_extract("[0-9]{10}|[0-9]{4}") %>%
return()
}
df_allData %>%
mutate(phone_contact = extract_phoneNumber2(mieu_ta)) %>%
filter(is.na(phone_contact)) %>%
head() %>%
select(source_link, mieu_ta, phone_contact)
# Function 3:
extract_phoneNumber3 <- function(x) {
x %>%
str_replace_all(" ", "") %>%
str_replace_all("\\.", "") %>%
str_extract("[0-9]{10}") %>%
return()
}
df_allData %>%
mutate(phone_contact = extract_phoneNumber3(mieu_ta)) %>%
filter(is.na(phone_contact)) %>%
head() %>%
select(source_link, mieu_ta, phone_contact)
# Use functions:
df_allData %>%
transmute(dien_tich = extract_area(gia_dien_tich),
gia = extract_price(gia_dien_tich),
district = extract_add(dia_chi),
Loai_BDS = Loai_BDS,
Phap_ly = Phap_ly) -> df1
df1 %>%
filter(Loai_BDS == "Nha mat tien") %>%
group_by(district) %>%
count() %>%
ungroup() %>%
filter(str_count(district) != 0) %>%
mutate(district = case_when(str_detect(district, "hoang mai") ~ "hoang mai", TRUE ~ district)) %>%
group_by(district) %>%
summarise(n_sales = sum(n)) %>%
ungroup() %>%
arrange(n_sales) %>%
mutate(district = factor(district, levels = district)) -> df_mat_tien
df1 %>%
filter(Loai_BDS == "Nha mat tien") %>%
group_by(district) %>%
summarise(median_dien_tich = median(dien_tich, na.rm = TRUE)) %>%
ungroup() %>%
filter(str_count(district) != 0) %>%
arrange(median_dien_tich) %>%
mutate(district = factor(district, levels = district)) -> df_dien_tich
df1 %>%
filter(Loai_BDS == "Nha mat tien") %>%
group_by(district) %>%
summarise(median_gia = median(gia, na.rm = TRUE)) %>%
ungroup() %>%
filter(str_count(district) != 0) %>%
arrange(median_gia) %>%
mutate(district = factor(district, levels = district)) -> df_gia
df1 %>%
filter(Loai_BDS == "Nha mat tien") %>%
mutate(gia_m2 = 1000*gia / dien_tich) %>%
group_by(district) %>%
summarise(med_gia_m2 = median(gia_m2, na.rm = TRUE)) %>%
ungroup() %>%
mutate(med_gia_m2 = round(med_gia_m2, 0)) %>%
filter(str_count(district) != 0) %>%
arrange(med_gia_m2) %>%
mutate(district = factor(district, levels = district)) -> df_gia_median_m2
df_mat_tien %>%
ggplot(aes(district, n_sales)) +
geom_col() +
coord_flip()
df_gia %>%
ggplot(aes(district, median_gia)) +
geom_col() +
coord_flip()
df_gia_median_m2 %>%
ggplot(aes(district, med_gia_m2)) +
geom_col() +
coord_flip()
df_dien_tich %>%
ggplot(aes(district, median_dien_tich)) +
geom_col() +
coord_flip()
#====================================
# Check E agent (cò)
#====================================
df_allData %>%
mutate(phone_contact = extract_phoneNumber3(mieu_ta)) -> df_allData
df_allData %>%
group_by(phone_contact) %>%
count() %>%
ungroup() %>%
filter(!is.na(phone_contact)) %>%
arrange(-n) %>%
filter(n > 1) %>%
pull(phone_contact) -> re_agent_phones
df_allData %>%
mutate(re_agent = case_when(phone_contact %in% re_agent_phones ~ "Yes", TRUE ~ "No")) -> df_allData
# df_allData %>%
# select(phone_contact, source_link) %>%
# write.csv("re_phone_contact.csv", row.names = FALSE)
#===================
# Phone Providers
#===================
# Test your solution:
x <- c("098xdf", "012hg4")
str_detect(x, "^098")
str_detect(x, "^012")
str_detect(x, "^098|012")
p_old <- str_c("^", viettel_df$X1, sep = "") %>% str_flatten(collapse = "|")
p_new <- str_c("^", viettel_df$X2, sep = "") %>% str_flatten(collapse = "|")
viettel_pre <- str_c(p_old, p_new, sep = "|")
# Reference: https://www.gocit.vn/bai-viet/tong-hop-danh-sach-dau-so-mang-di-dong-o-viet-nam/
library(rvest)
link <- "https://www.gocit.vn/bai-viet/tong-hop-danh-sach-dau-so-mang-di-dong-o-viet-nam/"
# Extract one specific table:
link %>%
read_html() %>%
html_nodes(xpath = '//*[@id="post-11047"]/div[5]/div[1]/table[1]') %>%
html_table(fill = TRUE)
# Extract all tables:
link %>%
read_html() %>%
html_table(fill = TRUE) -> pre_phone
# lapply(pre_phone, function(df) {df %>% slice(-1)})
pre_phone[[1]] %>% slice(-1) -> viettel_df
pre_phone[[2]] %>% slice(-1) -> mobi_df
pre_phone[[3]] %>% slice(-1) -> vina_df
pre_phone[[4]] %>% slice(-1) -> vietnammobi_df
pre_phone[[5]] %>% slice(-1) -> Gmobi_df
# Function create pre-number for phones:
extrac_preNumber <- function(df) {
p_old <- str_c("^", df$X1, sep = "") %>% str_flatten(collapse = "|")
p_new <- str_c("^", df$X2, sep = "") %>% str_flatten(collapse = "|")
total_pre <- str_c(p_old, p_new, sep = "|")
return(total_pre)
}
pre_viettel <- extrac_preNumber(viettel_df)
pre_mobi <- extrac_preNumber(mobi_df)
pre_vina <- extrac_preNumber(vina_df)
pre_vietnam <- extrac_preNumber(vietnammobi_df)
pre_gmobil <- extrac_preNumber(Gmobi_df)
df_allData %>%
mutate(phone_provider = case_when(str_detect(phone_contact, pre_viettel) ~ "Viettel",
str_detect(phone_contact, pre_mobi) ~ "Mobifone",
str_detect(phone_contact, pre_vina) ~ "Vinaphone",
str_detect(phone_contact, pre_vietnam) ~ "Vietnammobile",
str_detect(phone_contact, pre_gmobil) ~ "Gmobile")) -> df_allData
df_allData %>%
select(phone_contact, phone_provider, source_link) %>%
head(n = 10)
df_allData %>%
filter(!duplicated(phone_contact)) %>%
group_by(phone_provider) %>%
count() %>%
ungroup() %>%
na.omit() %>%
arrange(-n)
LS0tDQp0aXRsZTogIk1pbmkgQ291cnNlOiBDb2xsZWN0IERhdGEgZm9yIDc4NzA0IEhvbWVzIGZvciBTYWxlIGluIEhhbm9pIg0KYXV0aG9yOiAiTmd1eWVuIENoaSBEdW5nIg0Kc3VidGl0bGU6ICJEYXRhIFNjcmFwaW5nIFNlcmllcyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICAjIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgIHRoZW1lOiBmbGF0bHkNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIHdvcmRfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCi0tLQ0KDQpgYGB7ciBzZXR1cCxpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLnJldGluYT0yKQ0KYGBgDQoNCiMgU3RhZ2UgMTogQ29sbGVjdCBEYXRhDQoNCmBgYHtyLCBldmFsPUZBTFNFfQ0KIyBDbGVhciB3b3Jrc3BhY2U6IA0Kcm0obGlzdCA9IGxzKCkpDQoNCiMgTG9hZCBzb21lIFIgcGFja2FnZXM6IA0KbGlicmFyeShydmVzdCkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzdHJpbmdpKQ0KDQojIFVybCBsaW5rcyBvZiBob21lcyBmb3Igc2FsZXMgaW4gSGFub2k6IA0KDQp1cmwgPC0gImh0dHBzOi8vYWxvbmhhZGF0LmNvbS52bi9jYW4tYmFuLW5oYS1oYS1ub2ktdDEuaHRtIg0KDQojIEV4dHJhY3QgbGlua3MgYXQgZGlzdHJpYyBsZXZlbDogDQoNCnVybCAlPiUgDQogIHJlYWRfaHRtbCgpICU+JSANCiAgaHRtbF9ub2Rlcyh4cGF0aCA9ICcvLypbQGlkPSJyaWdodCJdL2Rpdls0XS9kaXZbMV0vdWwnKSAlPiUgDQogIGh0bWxfbm9kZXMoImEiKSAlPiUgDQogIGh0bWxfYXR0cigiaHJlZiIpICU+JSANCiAgLlstMV0gLT4gZGlzdHJpY3RfdXJsDQoNCg0KIyBXZSBhc3N1bWUgdGhhdCB0aGVyZSBhcmUgMTAwMCBwYWdlczogDQoNCm5fcGFnZXMgPC0gcGFzdGUwKCJodHRwczovL2Fsb25oYWRhdC5jb20udm4iLCBzdHJfc3ViKGRpc3RyaWN0X3VybCwgc3RhcnQgPSAxLCBlbmQgPSBzdHJfY291bnQoZGlzdHJpY3RfdXJsKSAtIDQpLCAiL3RyYW5nLTEwMDAuaHRtIikNCg0KIyBGdW5jdGlvbiBleHRyYWN0cyBuIHBhZ2VzOiANCg0KZ2V0X25fcGFnZXMgPC0gZnVuY3Rpb24obGluaykgew0KICANCiAgbGluayAlPiUgDQogICAgcmVhZF9odG1sKCkgJT4lIA0KICAgIGh0bWxfbm9kZXMoeHBhdGggPSAnLy8qW0BpZD0ibGVmdCJdL2RpdlsyXS9hWzExXScpICU+JSANCiAgICBodG1sX3RleHQoKSAlPiUgDQogICAgYXMubnVtZXJpYygpICU+JSANCiAgICByZXR1cm4oKQ0KfQ0KDQoNCiMgVXNlIHRoZSBmdW5jdGlvbjogDQoNCm51bWJlcl9wYWdlcyA8LSBsYXBwbHkobl9wYWdlcywgZ2V0X25fcGFnZXMpDQoNCmFjdHVhbF9wYWdlcyA8LSBuX3BhZ2VzW2FzLnZlY3RvcihudW1iZXJfcGFnZXMpID4gMF0NCmFjdHVhbF9wYWdlcyA8LSBhY3R1YWxfcGFnZXNbIWlzLm5hKGFjdHVhbF9wYWdlcyldDQpudW1iZXJfcGFnZXMgPC0gbnVtYmVyX3BhZ2VzICU+JSB1bmxpc3QoKQ0KDQojIE51bWJlciBvZiBwYWdlczogDQpkZl9wYWdlcyA8LSBkYXRhLmZyYW1lKGJhc2VfdXJsID0gc3RyX3N1YihhY3R1YWxfcGFnZXMsIHN0YXJ0ID0gMSwgZW5kID0gc3RyX2NvdW50KGFjdHVhbF9wYWdlcykgLSA4KSwgbl9wYWdlcyA9IG51bWJlcl9wYWdlcykNCg0KDQoNCiMgaHR0cHM6Ly9hbG9uaGFkYXQuY29tLnZuL2Nhbi1iYW4tbmhhLXF1YW4tYmEtZGluaC1oYS1ub2ktcTQwNy90cmFuZy0yLmh0bQ0KDQojIEZ1bmN0aW9uIGV4dHJhY3QgYWxsIGxpbmtzIGZvciBhIHNwZWNpZmljIGRpc3RyaWN0IHNlbGVjdGVkOiANCg0KZXh0cmFjdF9QYWdlTGlua3MgPC0gZnVuY3Rpb24oLi4uKSB7DQogIG4gPC0gbnJvdyhkZl9wYWdlcykNCiAgcGFnZUxpbmtzIDwtIGMoKSANCiAgDQogIGZvciAoaSBpbiAxOm4pIHsNCiAgICBteV9wYWdlcyA8LSBwYXN0ZTAoZGZfcGFnZXNbaSwgMV0sIDE6ZGZfcGFnZXNbaSwgMl0sICIuaHRtIikNCiAgICBwYWdlTGlua3MgPC0gYyhwYWdlTGlua3MsIG15X3BhZ2VzKQ0KICB9DQogIA0KICByZXR1cm4ocGFnZUxpbmtzKQ0KICANCn0NCg0KIyBVc2UgdGhlIGZ1bmN0aW9uOiANCg0Kc3lzdGVtLnRpbWUoYWxsX3BhZ2VzX2Zvcl9SRSA8LSBleHRyYWN0X1BhZ2VMaW5rcygpKQ0KDQoNCg0KIyBHZXQgYWxsIGxpbmtzIGZvciBob21lczogDQoNCmdldF9zcGVjaWZpY19SRV9saW5rcyA8LSBmdW5jdGlvbih4KSB7DQogIA0KICB4ICU+JSANCiAgICByZWFkX2h0bWwoKSAlPiUgDQogICAgaHRtbF9ub2Rlcyh4cGF0aCA9ICcvLypbQGlkPSJsZWZ0Il0vZGl2WzFdJykgJT4lIA0KICAgIGh0bWxfbm9kZXMoImEiKSAlPiUgDQogICAgaHRtbF9hdHRyKCJocmVmIikgJT4lIA0KICAgIHVuaXF1ZSgpIC0+IG0NCiAgDQogICAgcmV0dXJuKHBhc3RlMCgiaHR0cHM6Ly9hbG9uaGFkYXQuY29tLnZuIiwgbSkpDQp9DQoNCg0KYWxsX2xpbmtzX2ZvclJFIDwtIGxhcHBseShhbGxfcGFnZXNfZm9yX1JFLCBnZXRfc3BlY2lmaWNfUkVfbGlua3MpICU+JSB1bmxpc3QoKSANCg0KDQojIEZ1bmN0aW9uIGNvbGxlY3QgYWxsIGluZm9ybWF0aW9uIGZvciBhIHNwZWNpZmljIGhvbWUgZm9yIHNhbGU6IA0KDQpnZXRfYWxsRGF0YV9SRSA8LSBmdW5jdGlvbihsaW5rKSB7DQogIA0KICBsaW5rICU+JSANCiAgICByZWFkX2h0bWwoKSAtPiBtDQogIA0KICBtICU+JSANCiAgICBodG1sX25vZGVzKCJ0ZCIpICU+JSANCiAgICBodG1sX3RleHQodHJpbSA9IFRSVUUpICU+JSANCiAgICBtYXRyaXgobnJvdyA9IDIpICU+JSANCiAgICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICAgIG11dGF0ZV9hbGwoYXMuY2hhcmFjdGVyKSAtPiBuDQogIA0KICBuICU+JSANCiAgICBzbGljZSgyKSAtPiBtYWluX2RmDQogIA0KICBuYW1lcyhtYWluX2RmKSA8LSBzdHJpbmdpOjpzdHJpX3RyYW5zX2dlbmVyYWwobiAlPiUgc2xpY2UoMSkgJT4lIGFzLnZlY3RvcigpLCAiTGF0aW4tQVNDSUkiKSAlPiUgc3RyX3JlcGxhY2VfYWxsKCIgIiwgIl8iKQ0KICANCiAgDQogIG0gJT4lIA0KICAgIGh0bWxfbm9kZXMoeHBhdGggPSAnLy8qW0BpZD0ibGVmdCJdL2RpdlsxXS9kaXZbMl0nKSAlPiUgDQogICAgaHRtbF90ZXh0KHRyaW0gPSBUUlVFKSAtPiByZV9kZXMNCiAgDQogIGlmIChsZW5ndGgocmVfZGVzKSA9PSAwKSB7DQogICAgcmVfZGVzIDwtIE5BDQogIH0NCiAgDQogIA0KICBtICU+JSANCiAgICBodG1sX25vZGVzKHhwYXRoID0gJy8vKltAaWQ9ImxlZnQiXS9kaXZbMV0vZGl2WzNdJykgJT4lIA0KICAgIGh0bWxfdGV4dCh0cmltID0gVFJVRSkgLT4gZ2lhX2RpZW5fdGljaA0KICANCiAgaWYgKGxlbmd0aChnaWFfZGllbl90aWNoKSA9PSAwKSB7DQogICAgZ2lhX2RpZW5fdGljaCA8LSBOQQ0KICB9DQogIA0KICBtICU+JSANCiAgICBodG1sX25vZGVzKHhwYXRoID0gJy8vKltAaWQ9ImxlZnQiXS9kaXZbMV0vZGl2WzRdJykgJT4lIA0KICAgIGh0bWxfdGV4dCh0cmltID0gVFJVRSkgLT4gcmVfYWRkDQogIA0KICANCiAgaWYgKGxlbmd0aChyZV9hZGQpID09IDApIHsNCiAgICByZV9hZGQgPC0gTkENCiAgfQ0KICANCiAgDQogIHRvdGFsX2RmIDwtIG1haW5fZGYgJT4lIA0KICAgIG11dGF0ZShtaWV1X3RhID0gcmVfZGVzLCBnaWFfZGllbl90aWNoID0gZ2lhX2RpZW5fdGljaCwgZGlhX2NoaSA9IHJlX2FkZCwgc291cmNlX2xpbmsgPSBsaW5rKQ0KICANCiAgU3lzLnNsZWVwKDEpDQogIA0KICByZXR1cm4odG90YWxfZGYpDQogIA0KfQ0KDQoNCg0KZ2V0X2FsbERhdGFfUkVfdHJ5Q2F0Y2ggPC0gZnVuY3Rpb24obGluaykgew0KICByZXR1cm4odHJ5Q2F0Y2goZ2V0X2FsbERhdGFfUkUobGluayksIGVycm9yID0gZnVuY3Rpb24oZSkge05VTEx9KSkNCn0NCg0KDQoNCmxhcHBseShhbGxfbGlua3NfZm9yUkUsIGdldF9hbGxEYXRhX1JFX3RyeUNhdGNoKSAtPiBhbGxEYXRhX1JFX2xpc3QNCg0Kc2F2ZShhbGxEYXRhX1JFX2xpc3QsIGZpbGUgPSAiYWxsRGF0YV9SRV9saXN0XzI0XzA2LlJEYXRhIikNCg0KIyBsb2FkKCJDOlxcVXNlcnNcXEFETUlOXFxEb2N1bWVudHNcXGFsbERhdGFfUkVfbGlzdF8yNF8wNi5SRGF0YSIpDQoNCg0KYGBgDQoNCiMgU3RhZ2UgMjogRGF0YSBQcm9jZXNzaW5nIA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgQ2xlYXIgd29ya3NwYWNlOiANCnJtKGxpc3QgPSBscygpKQ0KDQojIExvYWQgc29tZSBSIHBhY2thZ2VzOiANCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShzdHJpbmdpKQ0KDQoNCiMgTG9hZCBkYXRhOiANCmxvYWQoIkM6XFxVc2Vyc1xcWmJvb2tcXERlc2t0b3BcXHJlX3Byb2plY3RcXGFsbERhdGFfUkVfbGlzdF8yNF8wNi5SRGF0YSIpDQoNCiMgQ29udmVydCBkYXRhIHRvIGRhdGEgZnJhbWU6IA0KZGZfYWxsRGF0YSA8LSBkby5jYWxsKCJiaW5kX3Jvd3MiLCBhbGxEYXRhX1JFX2xpc3QpDQoNCiMgQ29udmVydCB0byBMYXRpbiBjaGFyYWN0ZXI6IA0KZGZfYWxsRGF0YSAlPiUgbXV0YXRlX2FsbChmdW5jdGlvbih4KSB7c3RyaV90cmFuc19nZW5lcmFsKHgsICJMYXRpbi1BU0NJSSIpfSkgLT4gZGZfYWxsRGF0YQ0KDQoNCiMgRnVuY3Rpb24gRXh0cmFjdHMgYXJlYSBkYXRhOg0KDQpleHRyYWN0X2FyZWEgPC0gZnVuY3Rpb24oeCkgew0KICANCiAgeCAlPiUgDQogICAgc3RyX3NwbGl0KCI6Iiwgc2ltcGxpZnkgPSBUUlVFKSAlPiUgDQogICAgZGF0YS5mcmFtZSgpIC0+IGdpYV9kdA0KICANCiAgZ2lhX2R0JFgzICU+JSANCiAgICBzdHJfc3BsaXQoIm0iLCBzaW1wbGlmeSA9IFRSVUUpICU+JSANCiAgICBkYXRhLmZyYW1lKCkgJT4lIA0KICAgIHB1bGwoWDEpICU+JSANCiAgICBhcy5jaGFyYWN0ZXIoKSAlPiUgDQogICAgc3RyX3NxdWlzaCgpICU+JSANCiAgICBhcy5udW1lcmljKCkgJT4lIA0KICAgIHJldHVybigpDQp9DQoNCg0KIyBFeHRyYWN0IHByaWNlIGRhdGE6IA0KDQpleHRyYWN0X3ByaWNlIDwtIGZ1bmN0aW9uKHgpIHsNCiAgDQogIHggJT4lIA0KICAgIHN0cl9zcGxpdCgiOiIsIHNpbXBsaWZ5ID0gVFJVRSkgJT4lIA0KICAgIGRhdGEuZnJhbWUoKSAtPiBnaWFfZHQNCiAgDQogIA0KICBnaWFfZHQkWDIgJT4lIA0KICAgIHN0cl90b19sb3dlcigpICU+JSANCiAgICBzdHJfc3BsaXQoInR5Iiwgc2ltcGxpZnkgPSBUUlVFKSAlPiUgDQogICAgZGF0YS5mcmFtZSgpICU+JSANCiAgICBwdWxsKFgxKSAlPiUgDQogICAgYXMuY2hhcmFjdGVyKCkgJT4lIA0KICAgIHN0cl9zcGxpdCgiLCIsIHNpbXBsaWZ5ID0gVFJVRSkgJT4lIA0KICAgIGRhdGEuZnJhbWUoKSAlPiUgDQogICAgbXV0YXRlX2FsbChmdW5jdGlvbih4KSB7c3RyX3NxdWlzaCh4KX0pICU+JSANCiAgICBtdXRhdGUoWDMgPSBjYXNlX3doZW4oc3RyX2NvdW50KFgyKSA9PSAxIH4gcGFzdGUwKFgyLCAiMCIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2NvdW50KFgyKSA9PSAwIH4gIjAwIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IFgyKSkgJT4lIA0KICAgIG11dGF0ZShwcmljZSA9IGFzLm51bWVyaWMoWDEpICsgYXMubnVtZXJpYyhYMykgLyAxMDApICU+JSANCiAgICBwdWxsKHByaWNlKSAlPiUgDQogICAgcmV0dXJuKCkNCn0NCg0KIyBGdW5jdGlvbiBleHRyYWN0cyBhZGQgKGRpc3RyaWN0IGxldmVsKTogDQoNCmV4dHJhY3RfYWRkIDwtIGZ1bmN0aW9uKHgpIHsNCiAgDQp4ICU+JSANCiAgICBzdHJfdG9fbG93ZXIoKSAlPiUgDQogICAgc3RyX3NwbGl0KCJcXCwgcXVhbiIsIHNpbXBsaWZ5ID0gVFJVRSkgJT4lIA0KICAgIGRhdGEuZnJhbWUoKSAlPiUgDQogICAgcHVsbChYMikgJT4lIA0KICAgIGFzLmNoYXJhY3RlcigpICU+JSANCiAgICBzdHJfc3F1aXNoKCkgJT4lIA0KICAgIHN0cl9zcGxpdCgiXFwsIiwgc2ltcGxpZnkgPSBUUlVFKSAlPiUgDQogICAgZGF0YS5mcmFtZSgpICU+JSANCiAgICBwdWxsKFgxKSAlPiUgDQogICAgYXMuY2hhcmFjdGVyKCkgJT4lIA0KICAgIHJldHVybigpDQogIA0KfQ0KDQoNCg0KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAgICBGdW5jdGlvbiBleHRyYWN0IGFnZW50J3MgcGhvbmUgbnVtYmVycyANCiMgICAgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTcyMTU3ODkvZXh0cmFjdC1hLXN1YnN0cmluZy1pbi1yLWFjY29yZGluZy10by1hLXBhdHRlcm4NCiMgICAgaHR0cHM6Ly9yc3R1ZGlvLXB1YnMtc3RhdGljLnMzLmFtYXpvbmF3cy5jb20vNzQ2MDNfNzZjZDE0ZDU5ODNmNDc0MDhmZGYwYjMyMzU1MGI4NDYuaHRtbCANCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KDQojIEZ1bmN0aW9uIDE6IA0KDQpleHRyYWN0X3Bob25lTnVtYmVyMSA8LSBmdW5jdGlvbih4KSB7DQogIHggJT4lIA0KICAgIHN0cl9yZXBsYWNlX2FsbCgiICIsICIiKSAlPiUgDQogICAgc3RyX2V4dHJhY3QoIlswLTldezEwfSIpICU+JSANCiAgICByZXR1cm4oKQ0KfQ0KDQoNCg0KZGZfYWxsRGF0YSAlPiUgDQogIG11dGF0ZShwaG9uZV9jb250YWN0ID0gZXh0cmFjdF9waG9uZU51bWJlcjEobWlldV90YSkpICU+JSANCiAgZmlsdGVyKGlzLm5hKHBob25lX2NvbnRhY3QpKSAlPiUgDQogIGhlYWQoKSAlPiUgDQogIHNlbGVjdChzb3VyY2VfbGluaywgbWlldV90YSwgcGhvbmVfY29udGFjdCkNCg0KDQojIEZ1bmN0aW9uIDI6IA0KDQpleHRyYWN0X3Bob25lTnVtYmVyMiA8LSBmdW5jdGlvbih4KSB7DQogIHggJT4lIA0KICAgIHN0cl9yZXBsYWNlX2FsbCgiICIsICIiKSAlPiUgDQogICAgc3RyX2V4dHJhY3QoIlswLTldezEwfXxbMC05XXs0fSIpICU+JSANCiAgICByZXR1cm4oKQ0KfQ0KDQoNCmRmX2FsbERhdGEgJT4lIA0KICBtdXRhdGUocGhvbmVfY29udGFjdCA9IGV4dHJhY3RfcGhvbmVOdW1iZXIyKG1pZXVfdGEpKSAlPiUgDQogIGZpbHRlcihpcy5uYShwaG9uZV9jb250YWN0KSkgJT4lIA0KICBoZWFkKCkgJT4lIA0KICBzZWxlY3Qoc291cmNlX2xpbmssIG1pZXVfdGEsIHBob25lX2NvbnRhY3QpDQoNCiMgRnVuY3Rpb24gMzogDQoNCmV4dHJhY3RfcGhvbmVOdW1iZXIzIDwtIGZ1bmN0aW9uKHgpIHsNCiAgDQogIHggJT4lIA0KICAgIHN0cl9yZXBsYWNlX2FsbCgiICIsICIiKSAlPiUgDQogICAgc3RyX3JlcGxhY2VfYWxsKCJcXC4iLCAiIikgJT4lIA0KICAgIHN0cl9leHRyYWN0KCJbMC05XXsxMH0iKSAlPiUgDQogICAgcmV0dXJuKCkNCn0NCg0KDQpkZl9hbGxEYXRhICU+JSANCiAgbXV0YXRlKHBob25lX2NvbnRhY3QgPSBleHRyYWN0X3Bob25lTnVtYmVyMyhtaWV1X3RhKSkgJT4lIA0KICBmaWx0ZXIoaXMubmEocGhvbmVfY29udGFjdCkpICU+JSANCiAgaGVhZCgpICU+JSANCiAgc2VsZWN0KHNvdXJjZV9saW5rLCBtaWV1X3RhLCBwaG9uZV9jb250YWN0KQ0KDQoNCiMgVXNlIGZ1bmN0aW9uczogDQoNCmRmX2FsbERhdGEgJT4lIA0KICB0cmFuc211dGUoZGllbl90aWNoID0gZXh0cmFjdF9hcmVhKGdpYV9kaWVuX3RpY2gpLCANCiAgICAgICAgICAgIGdpYSA9IGV4dHJhY3RfcHJpY2UoZ2lhX2RpZW5fdGljaCksIA0KICAgICAgICAgICAgZGlzdHJpY3QgPSBleHRyYWN0X2FkZChkaWFfY2hpKSwgDQogICAgICAgICAgICBMb2FpX0JEUyA9IExvYWlfQkRTLCANCiAgICAgICAgICAgIFBoYXBfbHkgPSBQaGFwX2x5KSAtPiBkZjENCg0KDQpkZjEgJT4lIA0KICBmaWx0ZXIoTG9haV9CRFMgPT0gIk5oYSBtYXQgdGllbiIpICU+JSANCiAgZ3JvdXBfYnkoZGlzdHJpY3QpICU+JSANCiAgY291bnQoKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGZpbHRlcihzdHJfY291bnQoZGlzdHJpY3QpICE9IDApICU+JSANCiAgbXV0YXRlKGRpc3RyaWN0ID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoZGlzdHJpY3QsICJob2FuZyBtYWkiKSB+ICJob2FuZyBtYWkiLCBUUlVFIH4gZGlzdHJpY3QpKSAlPiUgDQogIGdyb3VwX2J5KGRpc3RyaWN0KSAlPiUgDQogIHN1bW1hcmlzZShuX3NhbGVzID0gc3VtKG4pKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGFycmFuZ2Uobl9zYWxlcykgJT4lIA0KICBtdXRhdGUoZGlzdHJpY3QgPSBmYWN0b3IoZGlzdHJpY3QsIGxldmVscyA9IGRpc3RyaWN0KSkgLT4gZGZfbWF0X3RpZW4NCg0KDQpkZjEgJT4lIA0KICBmaWx0ZXIoTG9haV9CRFMgPT0gIk5oYSBtYXQgdGllbiIpICU+JSANCiAgZ3JvdXBfYnkoZGlzdHJpY3QpICU+JSANCiAgc3VtbWFyaXNlKG1lZGlhbl9kaWVuX3RpY2ggPSBtZWRpYW4oZGllbl90aWNoLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIGZpbHRlcihzdHJfY291bnQoZGlzdHJpY3QpICE9IDApICU+JSANCiAgYXJyYW5nZShtZWRpYW5fZGllbl90aWNoKSAlPiUgDQogIG11dGF0ZShkaXN0cmljdCA9IGZhY3RvcihkaXN0cmljdCwgbGV2ZWxzID0gZGlzdHJpY3QpKSAtPiBkZl9kaWVuX3RpY2gNCg0KDQoNCmRmMSAlPiUgDQogIGZpbHRlcihMb2FpX0JEUyA9PSAiTmhhIG1hdCB0aWVuIikgJT4lIA0KICBncm91cF9ieShkaXN0cmljdCkgJT4lIA0KICBzdW1tYXJpc2UobWVkaWFuX2dpYSA9IG1lZGlhbihnaWEsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZmlsdGVyKHN0cl9jb3VudChkaXN0cmljdCkgIT0gMCkgJT4lIA0KICBhcnJhbmdlKG1lZGlhbl9naWEpICU+JSANCiAgbXV0YXRlKGRpc3RyaWN0ID0gZmFjdG9yKGRpc3RyaWN0LCBsZXZlbHMgPSBkaXN0cmljdCkpIC0+IGRmX2dpYQ0KDQoNCmRmMSAlPiUgDQogIGZpbHRlcihMb2FpX0JEUyA9PSAiTmhhIG1hdCB0aWVuIikgJT4lIA0KICBtdXRhdGUoZ2lhX20yID0gMTAwMCpnaWEgLyBkaWVuX3RpY2gpICU+JSANCiAgZ3JvdXBfYnkoZGlzdHJpY3QpICU+JSANCiAgc3VtbWFyaXNlKG1lZF9naWFfbTIgPSBtZWRpYW4oZ2lhX20yLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShtZWRfZ2lhX20yID0gcm91bmQobWVkX2dpYV9tMiwgMCkpICU+JSANCiAgZmlsdGVyKHN0cl9jb3VudChkaXN0cmljdCkgIT0gMCkgJT4lIA0KICBhcnJhbmdlKG1lZF9naWFfbTIpICU+JSANCiAgbXV0YXRlKGRpc3RyaWN0ID0gZmFjdG9yKGRpc3RyaWN0LCBsZXZlbHMgPSBkaXN0cmljdCkpIC0+IGRmX2dpYV9tZWRpYW5fbTINCg0KDQpkZl9tYXRfdGllbiAlPiUgDQogIGdncGxvdChhZXMoZGlzdHJpY3QsIG5fc2FsZXMpKSArIA0KICBnZW9tX2NvbCgpICsgDQogIGNvb3JkX2ZsaXAoKQ0KDQoNCmRmX2dpYSAlPiUgDQogIGdncGxvdChhZXMoZGlzdHJpY3QsIG1lZGlhbl9naWEpKSArIA0KICBnZW9tX2NvbCgpICsgDQogIGNvb3JkX2ZsaXAoKQ0KDQoNCmRmX2dpYV9tZWRpYW5fbTIgJT4lIA0KICBnZ3Bsb3QoYWVzKGRpc3RyaWN0LCBtZWRfZ2lhX20yKSkgKyANCiAgZ2VvbV9jb2woKSArIA0KICBjb29yZF9mbGlwKCkNCiAgDQoNCg0KZGZfZGllbl90aWNoICU+JSANCiAgZ2dwbG90KGFlcyhkaXN0cmljdCwgbWVkaWFuX2RpZW5fdGljaCkpICsgDQogIGdlb21fY29sKCkgKyANCiAgY29vcmRfZmxpcCgpDQoNCg0KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQ0KIyAgICAgICAgIENoZWNrIEUgYWdlbnQgKGPDsikNCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KZGZfYWxsRGF0YSAlPiUgDQogIG11dGF0ZShwaG9uZV9jb250YWN0ID0gZXh0cmFjdF9waG9uZU51bWJlcjMobWlldV90YSkpIC0+IGRmX2FsbERhdGENCg0KDQpkZl9hbGxEYXRhICU+JSANCiAgZ3JvdXBfYnkocGhvbmVfY29udGFjdCkgJT4lIA0KICBjb3VudCgpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZmlsdGVyKCFpcy5uYShwaG9uZV9jb250YWN0KSkgJT4lIA0KICBhcnJhbmdlKC1uKSAlPiUgDQogIGZpbHRlcihuID4gMSkgJT4lIA0KICBwdWxsKHBob25lX2NvbnRhY3QpIC0+IHJlX2FnZW50X3Bob25lcw0KDQoNCg0KZGZfYWxsRGF0YSAlPiUgDQogIG11dGF0ZShyZV9hZ2VudCA9IGNhc2Vfd2hlbihwaG9uZV9jb250YWN0ICVpbiUgcmVfYWdlbnRfcGhvbmVzIH4gIlllcyIsIFRSVUUgfiAiTm8iKSkgLT4gZGZfYWxsRGF0YQ0KDQojIGRmX2FsbERhdGEgJT4lIA0KIyAgIHNlbGVjdChwaG9uZV9jb250YWN0LCBzb3VyY2VfbGluaykgJT4lIA0KIyAgIHdyaXRlLmNzdigicmVfcGhvbmVfY29udGFjdC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCg0KIz09PT09PT09PT09PT09PT09PT0NCiMgIFBob25lIFByb3ZpZGVycw0KIz09PT09PT09PT09PT09PT09PT0NCg0KDQojIFRlc3QgeW91ciBzb2x1dGlvbjogDQoNCnggPC0gYygiMDk4eGRmIiwgIjAxMmhnNCIpDQoNCnN0cl9kZXRlY3QoeCwgIl4wOTgiKQ0Kc3RyX2RldGVjdCh4LCAiXjAxMiIpDQpzdHJfZGV0ZWN0KHgsICJeMDk4fDAxMiIpDQoNCg0KcF9vbGQgPC0gc3RyX2MoIl4iLCB2aWV0dGVsX2RmJFgxLCBzZXAgPSAiIikgJT4lIHN0cl9mbGF0dGVuKGNvbGxhcHNlID0gInwiKQ0KcF9uZXcgPC0gc3RyX2MoIl4iLCB2aWV0dGVsX2RmJFgyLCBzZXAgPSAiIikgJT4lIHN0cl9mbGF0dGVuKGNvbGxhcHNlID0gInwiKQ0KdmlldHRlbF9wcmUgPC0gIHN0cl9jKHBfb2xkLCBwX25ldywgc2VwID0gInwiKQ0KDQoNCiMgUmVmZXJlbmNlOiBodHRwczovL3d3dy5nb2NpdC52bi9iYWktdmlldC90b25nLWhvcC1kYW5oLXNhY2gtZGF1LXNvLW1hbmctZGktZG9uZy1vLXZpZXQtbmFtLw0KDQpsaWJyYXJ5KHJ2ZXN0KQ0KbGluayA8LSAiaHR0cHM6Ly93d3cuZ29jaXQudm4vYmFpLXZpZXQvdG9uZy1ob3AtZGFuaC1zYWNoLWRhdS1zby1tYW5nLWRpLWRvbmctby12aWV0LW5hbS8iDQoNCg0KIyBFeHRyYWN0IG9uZSBzcGVjaWZpYyB0YWJsZTogDQpsaW5rICU+JSANCiAgcmVhZF9odG1sKCkgJT4lIA0KICBodG1sX25vZGVzKHhwYXRoID0gJy8vKltAaWQ9InBvc3QtMTEwNDciXS9kaXZbNV0vZGl2WzFdL3RhYmxlWzFdJykgJT4lIA0KICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQ0KDQoNCiMgRXh0cmFjdCBhbGwgdGFibGVzOiANCmxpbmsgJT4lIA0KICByZWFkX2h0bWwoKSAlPiUgDQogIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpIC0+IHByZV9waG9uZQ0KDQoNCiMgbGFwcGx5KHByZV9waG9uZSwgZnVuY3Rpb24oZGYpIHtkZiAlPiUgc2xpY2UoLTEpfSkNCg0KcHJlX3Bob25lW1sxXV0gJT4lIHNsaWNlKC0xKSAtPiB2aWV0dGVsX2RmDQpwcmVfcGhvbmVbWzJdXSAlPiUgc2xpY2UoLTEpIC0+IG1vYmlfZGYNCnByZV9waG9uZVtbM11dICU+JSBzbGljZSgtMSkgLT4gdmluYV9kZg0KcHJlX3Bob25lW1s0XV0gJT4lIHNsaWNlKC0xKSAtPiB2aWV0bmFtbW9iaV9kZg0KcHJlX3Bob25lW1s1XV0gJT4lIHNsaWNlKC0xKSAtPiBHbW9iaV9kZg0KDQojIEZ1bmN0aW9uIGNyZWF0ZSBwcmUtbnVtYmVyIGZvciBwaG9uZXM6IA0KDQpleHRyYWNfcHJlTnVtYmVyIDwtIGZ1bmN0aW9uKGRmKSB7DQogIA0KICBwX29sZCA8LSBzdHJfYygiXiIsIGRmJFgxLCBzZXAgPSAiIikgJT4lIHN0cl9mbGF0dGVuKGNvbGxhcHNlID0gInwiKQ0KICBwX25ldyA8LSBzdHJfYygiXiIsIGRmJFgyLCBzZXAgPSAiIikgJT4lIHN0cl9mbGF0dGVuKGNvbGxhcHNlID0gInwiKQ0KICB0b3RhbF9wcmUgPC0gIHN0cl9jKHBfb2xkLCBwX25ldywgc2VwID0gInwiKQ0KICByZXR1cm4odG90YWxfcHJlKQ0KICANCn0NCg0KDQpwcmVfdmlldHRlbCA8LSBleHRyYWNfcHJlTnVtYmVyKHZpZXR0ZWxfZGYpDQpwcmVfbW9iaSA8LSBleHRyYWNfcHJlTnVtYmVyKG1vYmlfZGYpDQpwcmVfdmluYSA8LSBleHRyYWNfcHJlTnVtYmVyKHZpbmFfZGYpDQpwcmVfdmlldG5hbSA8LSBleHRyYWNfcHJlTnVtYmVyKHZpZXRuYW1tb2JpX2RmKQ0KcHJlX2dtb2JpbCA8LSBleHRyYWNfcHJlTnVtYmVyKEdtb2JpX2RmKQ0KDQoNCmRmX2FsbERhdGEgJT4lIA0KICBtdXRhdGUocGhvbmVfcHJvdmlkZXIgPSBjYXNlX3doZW4oc3RyX2RldGVjdChwaG9uZV9jb250YWN0LCBwcmVfdmlldHRlbCkgfiAiVmlldHRlbCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChwaG9uZV9jb250YWN0LCBwcmVfbW9iaSkgfiAiTW9iaWZvbmUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QocGhvbmVfY29udGFjdCwgcHJlX3ZpbmEpIH4gIlZpbmFwaG9uZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChwaG9uZV9jb250YWN0LCBwcmVfdmlldG5hbSkgfiAiVmlldG5hbW1vYmlsZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChwaG9uZV9jb250YWN0LCBwcmVfZ21vYmlsKSB+ICJHbW9iaWxlIikpIC0+IGRmX2FsbERhdGENCg0KDQpkZl9hbGxEYXRhICU+JSANCiAgc2VsZWN0KHBob25lX2NvbnRhY3QsIHBob25lX3Byb3ZpZGVyLCBzb3VyY2VfbGluaykgJT4lIA0KICBoZWFkKG4gPSAxMCkNCg0KDQpkZl9hbGxEYXRhICU+JSANCiAgZmlsdGVyKCFkdXBsaWNhdGVkKHBob25lX2NvbnRhY3QpKSAlPiUgDQogIGdyb3VwX2J5KHBob25lX3Byb3ZpZGVyKSAlPiUgDQogIGNvdW50KCkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBuYS5vbWl0KCkgJT4lIA0KICBhcnJhbmdlKC1uKQ0KDQoNCmBgYA0KDQo=