Những yêu cầu của Assignment này:

  1. Phải cố gắng làm trước khi tham dự buổi học thứ 4.
  2. Làm đúng ít nhất 50% số câu hỏi.

Assignment 1

Assignment này sử dụng bộ dữ liệu multiple_choice_responses.csv được cung cấp bởi Kaggle. Để giải quyết tốt những câu hỏi được giao ở Assignment này các bạn nên đọc trước mô tả về bộ dữ liệu này tại https://www.kaggle.com/c/kaggle-survey-2019. Đây là bộ dữ liệu được sử dụng trong cuộc thi về làm sạch (dữ liệu) và hình ảnh hóa dữ liệu với tổng giải thưởng là 30.000$.

Trước hết, đọc bộ dữ liệu này vào R:

# Clear workspace: 
rm(list = ls())


# Load package and data: 
library(readr)
library(dplyr)
library(tidyverse)
df_raw <- read_csv("D:\\Desktop\\R\\Day3\\multiple_choice_responses.csv", skip = 1)
  • Q1. Tại sao trong đoạn mã đọc dữ liệu ở trên lại sử dụng skip = 1 để loại bỏ dòng 1 khi đọc vào dữ liệu?

Trả lời: Trong bộ dữ liệu thì dòng 1 đang bị thừa và không sử dụng trong quá trình xử lý dữ liệu.

  • Q2. Tên các cột biến có sử dụng dấu cách (space). Hãy thay thế tất cả các dấu cách bằng dấu gạch dưới (underscore).
library(stringr)

names(df_raw) %>% str_replace_all(pattern = " ", replacement = "_")-> df_1

names(df_raw) <- df_1
  • Q3. Tách ra data frame chỉ gồm hai cột biến là What_is_your_gender?_-_Selected_ChoiceIn_which_country_do_you_currently_reside? rồi đổi tên cho chúng lần lượt thành gendernation.
df_raw %>% select(`What_is_your_gender?_-_Selected_Choice`, `In_which_country_do_you_currently_reside?`) -> df_raw

new_names <- c("gender", "nation")

names(df_raw) <- new_names
  • Q4. Tính toán tỉ lệ nữ làm nghề Data Science cho tất cả các quốc gia trong mẫu khảo sát.
df_raw %>% filter(gender %in% c("Male", "Female")) %>% 
  group_by(gender, nation) %>% 
  count() %>% ungroup()-> df_raw


# calculate Female Rate:
df_raw %>% 
  spread(key = "gender", value = "n") %>% 
  mutate(Rate = Female / (Female + Male)) %>% 
  arrange(Rate) %>% 
  mutate(label = round(100*Rate, 1)) %>% 
  mutate(label = as.character(label)) %>% 
  mutate(label = case_when(!str_detect(label, "\\.") ~ paste0(label, ".0"), TRUE ~ label)) %>% 
  mutate(label = paste0(label, "%")) %>% 
  mutate(nation = factor(nation, levels = nation)) -> df_rate
  • Q5. Lấy ra danh sách 20 quốc gia bao gồm Việt Nam và 19 quốc gia có tỉ lệ nữ làm Data Science lớn nhất (top 19).
# Top 19 nations

df_rate %>% 
  arrange(-Rate) %>% 
  top_n(n = 19, wt = Rate)-> top_19
# Vietnam
df_rate %>% group_by(nation) %>% 
  filter(nation == "Viet Nam")-> df_vietnam

full_join(top_19, df_vietnam)-> df_new
  • Q6. Sử dụng dữ liệu có được ở Q5 hãy tạo ra (hoặc mô phỏng) lại biểu đồ Bar ở nhằm biểu diễn tỉ lệ Nữ làm Data Science cho 20 quốc gia đã được chọn như sau:
df_raw %>% 
  mutate(nation = factor(nation, levels = df_new$nation))-> df_nat

df_nat %>% full_join(df_new, by = "nation") %>%
  filter(!is.na(nation))-> df_final

#set color
my_colors <- c("#2E74C0", "#CB454A")

df_final %>% 
  ggplot(aes(x = nation, y = n, fill = gender)) +
  geom_col(position = "fill") + coord_flip()+
  geom_text(aes(x = nation, y = 0.95, label = label), size = 3.8, color = "white") +
  scale_fill_manual(name = "", values = c(Male = my_colors[1], Female = my_colors[2]), labels = c("Female", "Male")) +
  scale_y_continuous(labels = paste0(seq(0, 100, 25), "%"), expand = c(0, 0)) +
  theme_minimal() +
  theme(plot.margin = unit(rep(0.5, 4), "cm")) +
  theme(plot.background = element_rect(fill = "#EFF2F4")) +
  theme(plot.subtitle = element_text(size = 10, color = "grey30")) +
  theme(plot.title = element_text(size = 16)) +
  theme(plot.caption = element_text(size = 8, color = "grey30")) +
  theme(axis.text.x = element_text(size = 10)) +
  theme(axis.text.y = element_text(size = 10)) +
  guides(fill = guide_legend(reverse = TRUE)) +
  theme(panel.grid.major.x = element_blank()) +
  theme(panel.grid.major.y = element_blank()) +
  theme(legend.key.height = unit(0.16, "mm"), legend.key.width = unit(5, "mm")) +
  labs(x = NULL, y = NULL,
       title = "Fact 1: Women in Machine Learning and Data Science Comunity", 
       subtitle = "There’s still a significant gender gap for data scientists, with 84% of users identifying as males.\nThe United States has a slightly smaller gender gap at 79%, while Japan has a slightly higher one at 90%.", 
       caption = "Source: 2019 Kaggle ML & DS Survey")

Assignment 2

Q1. Object có tên all_links_communes_level được tạo ra đoạn R Codes dưới đây:

library(rvest)
library(stringr)

all_links <- "https://www.citypopulation.de/Vietnam.html"

pg <- read_html(all_links)

m <- html_nodes(pg, "a")

k <- html_attr(m, "href")

all_links_communes_level <-  str_c("https://www.citypopulation.de/en/vietnam/", k[-c(1:6)])

Thuộc dạng dữ liệu gì? Mỗi phần tử của all_links_communes_level là link dẫn đến thông tin gì?

Object thuộc kiểu dữ liệu character. Mỗi phần tử là link dẫn đến thông tin dân số của các tỉnh trên cả nước Việt Nam.

Q2. Xét đoạn mã dưới đây:

library(dplyr)

specific_link <- all_links_communes_level[1]

specific_link %>% 
  read_html() %>% 
  html_nodes(xpath = '//*[@id="tl"]') %>% 
  html_table(fill = TRUE) %>% 
  .[[1]] -> df


head(df)
##                   Name         Status PopulationCensus2009-04-01         
## 1     Bình Th<U+1EE7>y Urban District                    113,565 <U+2192>
## 2       An Th<U+1EDB>i           Ward                     18,499 <U+2192>
## 3     Bình Th<U+1EE7>y           Ward                     18,307 <U+2192>
## 4 Bùi H<U+1EEF>u Nghia           Ward                     11,745 <U+2192>
## 5             Long Hòa           Ward                     16,450 <U+2192>
## 6    Long Tuy<U+1EC1>n           Ward                     15,232 <U+2192>

Trong đoạn mã trên nếu thay all_links_communes_level[1] thành all_links_communes_level[2] thì hệ quả sẽ là gì? Có thể thay thành all_links_communes_level[100] được không?

-Nếu thay đoạn mã all_links_communes_level[1] thành all_links_communes_level[2] thì khi đó trả về kết quả dân só các quận huyện nằm trong link thứ 2, cụ thể là của thành phố Đà Nẵng.

-Không thể thay thành 100 vì Việt Nam chỉ có 63 tỉnh thành, truy xuất dữ liệu lớn hơn 63 sẽ không tồn tại.

Q3. Từ phân tích và nhận xét rút ra ở Q2 hãy viết một hàm mà nhận input là địa chỉ link của HTML và trả về kết quả (output) là một Data Frame chứa thông tin về dân số theo Huyện và Xã tương ứng với địa chỉ link của HTML.

Q4. Từ dữ liệu đã có ở Q3 hãy vẽ Choropleth Map minh họa mật độ dân số của Việt Nam theo cấp: (a) Tình, (b) Huyện. Giải thuyết rằng những số liệu về dân số thu được (kể cả cấp Huyện lẫn Xã) chính là mật độ dân số.

LS0tDQp0aXRsZTogJ0Fzc2lnbm1lbnQgZm9yIERheSAzJw0KYXV0aG9yOiAnQXV0aG9yOiBOZ3V5ZW4gVGhpIE5nb2MgSHV5ZW4nDQpzdWJ0aXRsZTogIlIgY291cnNlIDExIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgIyBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4NCiAgICAjIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6ICJmbGF0bHkiDQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRSkNCg0KYGBgDQoNCk5o4buvbmcgecOqdSBj4bqndSBj4bunYSBBc3NpZ25tZW50IG7DoHk6IA0KDQoxLiBQaOG6o2kgY+G7kSBn4bqvbmcgbMOgbSB0csaw4bubYyBraGkgdGhhbSBk4buxIGJ14buVaSBo4buNYyB0aOG7qSA0LiANCjIuIEzDoG0gxJHDum5nIMOtdCBuaOG6pXQgNTAlIHPhu5EgY8OidSBo4buPaS4gDQoNCg0KIyBBc3NpZ25tZW50IDENCg0KQXNzaWdubWVudCBuw6B5IHPhu60gZOG7pW5nIGLhu5kgZOG7ryBsaeG7h3UgKiptdWx0aXBsZV9jaG9pY2VfcmVzcG9uc2VzLmNzdioqIMSRxrDhu6NjIGN1bmcgY+G6pXAgYuG7n2kgS2FnZ2xlLiDEkOG7gyBnaeG6o2kgcXV54bq/dCB04buRdCBuaOG7r25nIGPDonUgaOG7j2kgxJHGsOG7o2MgZ2lhbyDhu58gQXNzaWdubWVudCBuw6B5IGPDoWMgYuG6oW4gbsOqbiDEkeG7jWMgdHLGsOG7m2MgbcO0IHThuqMgduG7gSBi4buZIGThu68gbGnhu4d1IG7DoHkgdOG6oWkgaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9jL2thZ2dsZS1zdXJ2ZXktMjAxOS4gxJDDonkgbMOgIGLhu5kgZOG7ryBsaeG7h3UgxJHGsOG7o2Mgc+G7rSBk4bulbmcgdHJvbmcgY3Xhu5ljIHRoaSB24buBIGzDoG0gc+G6oWNoIChk4buvIGxp4buHdSkgdsOgIGjDrG5oIOG6o25oIGjDs2EgZOG7ryBsaeG7h3UgduG7m2kgdOG7lW5nIGdp4bqjaSB0aMaw4bufbmcgbMOgIDMwLjAwMCQuIA0KDQpUcsaw4bubYyBo4bq/dCwgxJHhu41jIGLhu5kgZOG7ryBsaeG7h3UgbsOgeSB2w6BvIFI6IA0KDQoNCmBgYHtyfQ0KIyBDbGVhciB3b3Jrc3BhY2U6IA0Kcm0obGlzdCA9IGxzKCkpDQoNCg0KIyBMb2FkIHBhY2thZ2UgYW5kIGRhdGE6IA0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmRmX3JhdyA8LSByZWFkX2NzdigiRDpcXERlc2t0b3BcXFJcXERheTNcXG11bHRpcGxlX2Nob2ljZV9yZXNwb25zZXMuY3N2Iiwgc2tpcCA9IDEpDQoNCg0KYGBgDQoNCi0gUTEuIFThuqFpIHNhbyB0cm9uZyDEkW/huqFuIG3DoyDEkeG7jWMgZOG7ryBsaeG7h3Ug4bufIHRyw6puIGzhuqFpIHPhu60gZOG7pW5nIGBza2lwID0gMWAgxJHhu4MgbG/huqFpIGLhu48gZMOybmcgMSBraGkgxJHhu41jIHbDoG8gZOG7ryBsaeG7h3U/IA0KDQpUcuG6oyBs4budaTogVHJvbmcgYuG7mSBk4buvIGxp4buHdSB0aMOsIGTDsm5nIDEgxJFhbmcgYuG7iyB0aOG7q2EgdsOgIGtow7RuZyBz4butIGThu6VuZyB0cm9uZyBxdcOhIHRyw6xuaCB44butIGzDvSBk4buvIGxp4buHdS4NCg0KLSBRMi4gVMOqbiBjw6FjIGPhu5l0IGJp4bq/biBjw7Mgc+G7rSBk4bulbmcgZOG6pXUgY8OhY2ggKHNwYWNlKS4gSMOjeSB0aGF5IHRo4bq/IHThuqV0IGPhuqMgY8OhYyBk4bqldSBjw6FjaCBi4bqxbmcgZOG6pXUgZ+G6oWNoIGTGsOG7m2kgKHVuZGVyc2NvcmUpLiANCmBgYHtyfQ0KbGlicmFyeShzdHJpbmdyKQ0KDQpuYW1lcyhkZl9yYXcpICU+JSBzdHJfcmVwbGFjZV9hbGwocGF0dGVybiA9ICIgIiwgcmVwbGFjZW1lbnQgPSAiXyIpLT4gZGZfMQ0KDQpuYW1lcyhkZl9yYXcpIDwtIGRmXzENCg0KYGBgDQoNCi0gUTMuIFTDoWNoIHJhIGRhdGEgZnJhbWUgY2jhu4kgZ+G7k20gaGFpIGPhu5l0IGJp4bq/biBsw6AgYFdoYXRfaXNfeW91cl9nZW5kZXI/Xy1fU2VsZWN0ZWRfQ2hvaWNlYCB2w6AgYEluX3doaWNoX2NvdW50cnlfZG9feW91X2N1cnJlbnRseV9yZXNpZGU/YCBy4buTaSDEkeG7lWkgdMOqbiBjaG8gY2jDum5nIGzhuqduIGzGsOG7o3QgdGjDoG5oICpnZW5kZXIqIHbDoCAqbmF0aW9uKi4gDQpgYGB7cn0NCg0KZGZfcmF3ICU+JSBzZWxlY3QoYFdoYXRfaXNfeW91cl9nZW5kZXI/Xy1fU2VsZWN0ZWRfQ2hvaWNlYCwgYEluX3doaWNoX2NvdW50cnlfZG9feW91X2N1cnJlbnRseV9yZXNpZGU/YCkgLT4gZGZfcmF3DQoNCm5ld19uYW1lcyA8LSBjKCJnZW5kZXIiLCAibmF0aW9uIikNCg0KbmFtZXMoZGZfcmF3KSA8LSBuZXdfbmFtZXMNCg0KDQpgYGANCg0KLSBRNC4gVMOtbmggdG/DoW4gdOG7iSBs4buHIG7hu68gbMOgbSBuZ2jhu4EgRGF0YSBTY2llbmNlIGNobyB04bqldCBj4bqjIGPDoWMgcXXhu5FjIGdpYSB0cm9uZyBt4bqrdSBraOG6o28gc8OhdC4gDQpgYGB7cn0NCg0KZGZfcmF3ICU+JSBmaWx0ZXIoZ2VuZGVyICVpbiUgYygiTWFsZSIsICJGZW1hbGUiKSkgJT4lIA0KICBncm91cF9ieShnZW5kZXIsIG5hdGlvbikgJT4lIA0KICBjb3VudCgpICU+JSB1bmdyb3VwKCktPiBkZl9yYXcNCg0KDQojIGNhbGN1bGF0ZSBGZW1hbGUgUmF0ZToNCmRmX3JhdyAlPiUgDQogIHNwcmVhZChrZXkgPSAiZ2VuZGVyIiwgdmFsdWUgPSAibiIpICU+JSANCiAgbXV0YXRlKFJhdGUgPSBGZW1hbGUgLyAoRmVtYWxlICsgTWFsZSkpICU+JSANCiAgYXJyYW5nZShSYXRlKSAlPiUgDQogIG11dGF0ZShsYWJlbCA9IHJvdW5kKDEwMCpSYXRlLCAxKSkgJT4lIA0KICBtdXRhdGUobGFiZWwgPSBhcy5jaGFyYWN0ZXIobGFiZWwpKSAlPiUgDQogIG11dGF0ZShsYWJlbCA9IGNhc2Vfd2hlbighc3RyX2RldGVjdChsYWJlbCwgIlxcLiIpIH4gcGFzdGUwKGxhYmVsLCAiLjAiKSwgVFJVRSB+IGxhYmVsKSkgJT4lIA0KICBtdXRhdGUobGFiZWwgPSBwYXN0ZTAobGFiZWwsICIlIikpICU+JSANCiAgbXV0YXRlKG5hdGlvbiA9IGZhY3RvcihuYXRpb24sIGxldmVscyA9IG5hdGlvbikpIC0+IGRmX3JhdGUNCg0KDQpgYGANCg0KLSBRNS4gTOG6pXkgcmEgZGFuaCBzw6FjaCAyMCBxdeG7kWMgZ2lhIGJhbyBn4buTbSBWaeG7h3QgTmFtIHbDoCAxOSBxdeG7kWMgZ2lhIGPDsyB04buJIGzhu4cgbuG7ryBsw6BtIERhdGEgU2NpZW5jZSBs4bubbiBuaOG6pXQgKHRvcCAxOSkuIA0KYGBge3J9DQojIFRvcCAxOSBuYXRpb25zDQoNCmRmX3JhdGUgJT4lIA0KICBhcnJhbmdlKC1SYXRlKSAlPiUgDQogIHRvcF9uKG4gPSAxOSwgd3QgPSBSYXRlKS0+IHRvcF8xOQ0KIyBWaWV0bmFtDQpkZl9yYXRlICU+JSBncm91cF9ieShuYXRpb24pICU+JSANCiAgZmlsdGVyKG5hdGlvbiA9PSAiVmlldCBOYW0iKS0+IGRmX3ZpZXRuYW0NCg0KZnVsbF9qb2luKHRvcF8xOSwgZGZfdmlldG5hbSktPiBkZl9uZXcNCg0KDQpgYGANCg0KDQotIFE2LiBT4butIGThu6VuZyBk4buvIGxp4buHdSBjw7MgxJHGsOG7o2Mg4bufIFE1IGjDo3kgdOG6oW8gcmEgKGhv4bq3YyBtw7QgcGjhu49uZykgbOG6oWkgYmnhu4N1IMSR4buTIEJhciDhu58gbmjhurFtIGJp4buDdSBkaeG7hW4gdOG7iSBs4buHIE7hu68gbMOgbSBEYXRhIFNjaWVuY2UgY2hvIDIwIHF14buRYyBnaWEgxJHDoyDEkcaw4bujYyBjaOG7jW4gbmjGsCBzYXU6IA0KDQpgYGB7cn0NCg0KZGZfcmF3ICU+JSANCiAgbXV0YXRlKG5hdGlvbiA9IGZhY3RvcihuYXRpb24sIGxldmVscyA9IGRmX25ldyRuYXRpb24pKS0+IGRmX25hdA0KDQpkZl9uYXQgJT4lIGZ1bGxfam9pbihkZl9uZXcsIGJ5ID0gIm5hdGlvbiIpICU+JQ0KICBmaWx0ZXIoIWlzLm5hKG5hdGlvbikpLT4gZGZfZmluYWwNCg0KI3NldCBjb2xvcg0KbXlfY29sb3JzIDwtIGMoIiMyRTc0QzAiLCAiI0NCNDU0QSIpDQoNCmRmX2ZpbmFsICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gbmF0aW9uLCB5ID0gbiwgZmlsbCA9IGdlbmRlcikpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZmlsbCIpICsgY29vcmRfZmxpcCgpKw0KICBnZW9tX3RleHQoYWVzKHggPSBuYXRpb24sIHkgPSAwLjk1LCBsYWJlbCA9IGxhYmVsKSwgc2l6ZSA9IDMuOCwgY29sb3IgPSAid2hpdGUiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAiIiwgdmFsdWVzID0gYyhNYWxlID0gbXlfY29sb3JzWzFdLCBGZW1hbGUgPSBteV9jb2xvcnNbMl0pLCBsYWJlbHMgPSBjKCJGZW1hbGUiLCAiTWFsZSIpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBwYXN0ZTAoc2VxKDAsIDEwMCwgMjUpLCAiJSIpLCBleHBhbmQgPSBjKDAsIDApKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChyZXAoMC41LCA0KSwgImNtIikpICsNCiAgdGhlbWUocGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0VGRjJGNCIpKSArDQogIHRoZW1lKHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvciA9ICJncmV5MzAiKSkgKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsNCiAgdGhlbWUocGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA4LCBjb2xvciA9ICJncmV5MzAiKSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTApKSArDQogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFRSVUUpKSArDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSkgKw0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgdGhlbWUobGVnZW5kLmtleS5oZWlnaHQgPSB1bml0KDAuMTYsICJtbSIpLCBsZWdlbmQua2V5LndpZHRoID0gdW5pdCg1LCAibW0iKSkgKw0KICBsYWJzKHggPSBOVUxMLCB5ID0gTlVMTCwNCiAgICAgICB0aXRsZSA9ICJGYWN0IDE6IFdvbWVuIGluIE1hY2hpbmUgTGVhcm5pbmcgYW5kIERhdGEgU2NpZW5jZSBDb211bml0eSIsIA0KICAgICAgIHN1YnRpdGxlID0gIlRoZXJl4oCZcyBzdGlsbCBhIHNpZ25pZmljYW50IGdlbmRlciBnYXAgZm9yIGRhdGEgc2NpZW50aXN0cywgd2l0aCA4NCUgb2YgdXNlcnMgaWRlbnRpZnlpbmcgYXMgbWFsZXMuXG5UaGUgVW5pdGVkIFN0YXRlcyBoYXMgYSBzbGlnaHRseSBzbWFsbGVyIGdlbmRlciBnYXAgYXQgNzklLCB3aGlsZSBKYXBhbiBoYXMgYSBzbGlnaHRseSBoaWdoZXIgb25lIGF0IDkwJS4iLCANCiAgICAgICBjYXB0aW9uID0gIlNvdXJjZTogMjAxOSBLYWdnbGUgTUwgJiBEUyBTdXJ2ZXkiKQ0KDQoNCmBgYA0KDQoNCiMgQXNzaWdubWVudCAyDQoNClExLiBPYmplY3QgY8OzIHTDqm4gKiphbGxfbGlua3NfY29tbXVuZXNfbGV2ZWwqKiDEkcaw4bujYyB04bqhbyByYSDEkW/huqFuIFIgQ29kZXMgZMaw4bubaSDEkcOieTogDQoNCmBgYHtyfQ0KbGlicmFyeShydmVzdCkNCmxpYnJhcnkoc3RyaW5ncikNCg0KYWxsX2xpbmtzIDwtICJodHRwczovL3d3dy5jaXR5cG9wdWxhdGlvbi5kZS9WaWV0bmFtLmh0bWwiDQoNCnBnIDwtIHJlYWRfaHRtbChhbGxfbGlua3MpDQoNCm0gPC0gaHRtbF9ub2RlcyhwZywgImEiKQ0KDQprIDwtIGh0bWxfYXR0cihtLCAiaHJlZiIpDQoNCmFsbF9saW5rc19jb21tdW5lc19sZXZlbCA8LSAgc3RyX2MoImh0dHBzOi8vd3d3LmNpdHlwb3B1bGF0aW9uLmRlL2VuL3ZpZXRuYW0vIiwga1stYygxOjYpXSkNCg0KYGBgDQoNClRodeG7mWMgZOG6oW5nIGThu68gbGnhu4d1IGfDrD8gTeG7l2kgcGjhuqduIHThu60gY+G7p2EgYWxsX2xpbmtzX2NvbW11bmVzX2xldmVsIGzDoCBsaW5rIGThuqtuIMSR4bq/biB0aMO0bmcgdGluIGfDrD8gDQoNCk9iamVjdCB0aHXhu5ljIGtp4buDdSBk4buvIGxp4buHdSBjaGFyYWN0ZXIuDQpN4buXaSBwaOG6p24gdOG7rSBsw6AgbGluayBk4bqrbiDEkeG6v24gdGjDtG5nIHRpbiBkw6JuIHPhu5EgY+G7p2EgY8OhYyB04buJbmggdHLDqm4gY+G6oyBuxrDhu5tjIFZp4buHdCBOYW0uDQoNClEyLiBYw6l0IMSRb+G6oW4gbcOjIGTGsOG7m2kgxJHDonk6IA0KDQpgYGB7cn0NCg0KbGlicmFyeShkcGx5cikNCg0Kc3BlY2lmaWNfbGluayA8LSBhbGxfbGlua3NfY29tbXVuZXNfbGV2ZWxbMV0NCg0Kc3BlY2lmaWNfbGluayAlPiUgDQogIHJlYWRfaHRtbCgpICU+JSANCiAgaHRtbF9ub2Rlcyh4cGF0aCA9ICcvLypbQGlkPSJ0bCJdJykgJT4lIA0KICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKSAlPiUgDQogIC5bWzFdXSAtPiBkZg0KDQoNCmhlYWQoZGYpDQoNCg0KYGBgDQoNClRyb25nIMSRb+G6oW4gbcOjIHRyw6puIG7hur91IHRoYXkgYGFsbF9saW5rc19jb21tdW5lc19sZXZlbFsxXWAgdGjDoG5oIGBhbGxfbGlua3NfY29tbXVuZXNfbGV2ZWxbMl1gIHRow6wgaOG7hyBxdeG6oyBz4bq9IGzDoCBnw6w/IEPDsyB0aOG7gyB0aGF5IHRow6BuaCBgYWxsX2xpbmtzX2NvbW11bmVzX2xldmVsWzEwMF1gIMSRxrDhu6NjIGtow7RuZz8gDQoNCi1O4bq/dSB0aGF5IMSRb+G6oW4gbcOjIGBhbGxfbGlua3NfY29tbXVuZXNfbGV2ZWxbMV1gIHRow6BuaCBgYWxsX2xpbmtzX2NvbW11bmVzX2xldmVsWzJdYCB0aMOsIGtoaSDEkcOzIHRy4bqjIHbhu4Ega+G6v3QgcXXhuqMgZMOibiBzw7MgY8OhYyBxdeG6rW4gaHV54buHbiBu4bqxbSB0cm9uZyBsaW5rIHRo4bupIDIsIGPhu6UgdGjhu4MgbMOgIGPhu6dhIHRow6BuaCBwaOG7kSDEkMOgIE7hurVuZy4NCg0KLUtow7RuZyB0aOG7gyB0aGF5IHRow6BuaCAxMDAgdsOsIFZp4buHdCBOYW0gY2jhu4kgY8OzIDYzIHThu4luaCB0aMOgbmgsIHRydXkgeHXhuqV0IGThu68gbGnhu4d1IGzhu5tuIGjGoW4gNjMgc+G6vSBraMO0bmcgdOG7k24gdOG6oWkuDQoNClEzLiBU4burIHBow6JuIHTDrWNoIHbDoCBuaOG6rW4geMOpdCByw7p0IHJhIOG7nyBRMiBow6N5IHZp4bq/dCBt4buZdCBow6BtIG3DoCBuaOG6rW4gaW5wdXQgbMOgIMSR4buLYSBjaOG7iSBsaW5rIGPhu6dhIEhUTUwgdsOgIHRy4bqjIHbhu4Ega+G6v3QgcXXhuqMgKG91dHB1dCkgbMOgIG3hu5l0IERhdGEgRnJhbWUgY2jhu6lhIHRow7RuZyB0aW4gduG7gSBkw6JuIHPhu5EgdGhlbyBIdXnhu4duIHbDoCBYw6MgdMawxqFuZyDhu6luZyB24bubaSDEkeG7i2EgY2jhu4kgbGluayBj4bunYSBIVE1MLiANCg0KDQpRNC4gVOG7qyBk4buvIGxp4buHdSDEkcOjIGPDsyDhu58gUTMgaMOjeSB24bq9IENob3JvcGxldGggTWFwIG1pbmggaOG7jWEgbeG6rXQgxJHhu5kgZMOibiBz4buRIGPhu6dhIFZp4buHdCBOYW0gdGhlbyBj4bqlcDogKGEpIFTDrG5oLCAoYikgSHV54buHbi4gR2nhuqNpIHRodXnhur90IHLhurFuZyBuaOG7r25nIHPhu5EgbGnhu4d1IHbhu4EgZMOibiBz4buRIHRodSDEkcaw4bujYyAoa+G7gyBj4bqjIGPhuqVwIEh1eeG7h24gbOG6q24gWMOjKSBjaMOtbmggbMOgIG3huq10IMSR4buZIGTDom4gc+G7kS4gDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K