About this Notebook





Analytics Toolkit: Require Packages



# Here we are checking if the package is installed
if(!require("rvest", quietly = TRUE)) 
  # If the package is not in the system then it will be install
  install.packages(rvest, dependencies = TRUE, quietly = TRUE)
  # Here we are loading the package
  library(rvest, quietly = TRUE)
if(!require("lubridate", quietly = TRUE)) 
  install.packages(lubridate, dependencies = TRUE, quietly = TRUE)
  library(lubridate, quietly = TRUE)
if(!require("wordcloud", quietly = TRUE)) 
  install.packages(wordcloud, dependencies = TRUE, quietly = TRUE)
  library(wordcloud, quietly = TRUE)
if(!require("tm", quietly = TRUE)) 
  install.packages(tm, dependencies = TRUE, quietly = TRUE)
  library(tm, quietly = TRUE)
if(!require(tidyverse, quietly = TRUE))
  install.packages(tidyverse, dependencies = TRUE, quietly = TRUE)
  library(tidyverse, quietly = TRUE)
library(tm)
library(wordcloud)
library(tidyverse)

Data Preparation: Extracting Google Search Information



Locate the Google archive, then find the search data. For this case, it is an html file located in “My Activity” folder" then find the “Search” folder and the html file “MyActivity.html” should be there:

  • Takeout -> My Activity -> Search -> MyActivity.html

Laveraging regular expression we can search the html document to extract:


Extract Search Time

date_search <- search_archive %>% 
  html_nodes(xpath = '//div[@class="mdl-grid"]/div/div') %>% 
  str_extract(pattern = "(?<=<br>)(.*)(?<=PM|AM)") %>%
  mdy_hms()


Extract Search Text

text_search <- search_archive %>% 
  html_nodes(xpath = '//div[@class="mdl-grid"]/div/div') %>%
  str_extract(pattern = '(?<=<a)(.*)(?=</a>)') %>% 
  str_extract(pattern = '(?<=\">)(.*)')


Extract Search Type

type_search <- search_archive %>% 
  html_nodes(xpath = '//div[@class="mdl-grid"]/div/div') %>% 
  str_extract(pattern = "(?<=mdl-typography--body-1\">)(.*)(?=<a)") %>% 
  str_extract(pattern = "(\\w+)(?=\\s)")


Create a data frame using the data extracted from the html file

search_data <- tibble(timestamp = date_search,
                      date = as_date(date_search),
                      year = year(date_search),
                      month = month(date_search, label = TRUE),
                      day = weekdays(date_search),
                      hour = hour(date_search),
                      type = type_search,
                      search = text_search)
search_data$day <- factor(search_data$day, 
                          levels = c("Sunday", "Monday", "Tuesday",
                                     "Wednesday","Thursday", "Friday",
                                     "Saturday"))
search_data <- na.omit(search_data)
head(search_data)



Data Analysis: Visualizing Google Searches



To get an overall idea of the search volume, we can plot searches by year

p <- ggplot(search_data, aes(year))
p + geom_bar()


After determine the years with the largest search volume we can plot monthly searches

monthly <- search_data[(search_data$year > 2014 & search_data$year< 2018), ]
ggplot(monthly) + geom_bar(aes(x = month, group = year)) +
  theme(axis.text.x = element_text(angle=90)) +
  facet_grid(.~year, scales="free")


Another interesting metrict is searches by Hour

p <- ggplot(search_data, aes(hour))
p + geom_bar()


We can also plot the search data by day of the week to determine day are the most active

p <- ggplot(search_data, aes(day))
p + geom_bar()


We can take it an step further and group search time with day of the week.

ggplot(search_data) + 
  geom_bar(aes(x = hour, group = day) ) +
  facet_grid(.~day, scales = "free")


We can group the search data by year and day of the week, to visualize the overall trend

wkday <- group_by(search_data, year, day) %>% summarize(count = n())
p <- ggplot(wkday, aes(day, count, fill = year)) 
p + geom_bar(stat = "identity") + labs(x = "", y = "Search Volume")



Reporting: A wordcloud from Google Search Data



First we need to extract the text and clean it using regular expressions

search <- tolower(search_data$search)
#search <- iconv(search, "ASCII", "UTF-8", ")
search <- gsub('(http|https)\\S+\\s*|(#|@)\\S+\\s*|\\n|\\"', " ", search)
search <- gsub("(.*.)\\.com(.*.)\\S+\\s|[^[:alnum:]]", " ", search)
search <- trimws(search)


After cleaning the text we can create a Text Corpus (a large and structured set of texts) and remove some words

search_corpus <-  Corpus(VectorSource(search))
search_corpus <- tm_map(search_corpus, content_transformer(removePunctuation))
search_corpus <- tm_map(search_corpus, content_transformer(removeNumbers))
stopwords <- c(stopwords("english"), "chrome", "chicago", "jlroo", "google","online","good","rate")
search_corpus <- tm_map(search_corpus, removeWords, stopwords)


search_tdm <- TermDocumentMatrix(search_corpus)
search_matrix <- as.matrix(search_tdm)


Set a threshold for the min frequency of the words to display as well as max frequency

wordcloud(d$word, d$freq, min.freq = 30, scale = c(2 , 0.5), max.words = 200)

LS0tDQp0aXRsZTogIkFuYWx5emluZyBHb29nbGUgU2VhcmNoIEhpc3RvcnkiDQphdXRob3I6ICJDaGV5ZW5uZSBQZW5ueSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBodG1sX2RvY3VtZW50OiBkZWZhdWx0DQpkYXRlOiAiSmFudWFyeSAzMCwgMjAxOCINCnN1YnRpdGxlOiAiQ01FIEdyb3VwIEZvdW5kYXRpb24gQnVzaW5lc3MgQW5hbHl0aWNzIExhYiINCi0tLQ0KDQo8YnI+DQoNCi0tLS0tLS0tLS0tLS0tDQoNCiMjIEFib3V0IHRoaXMgTm90ZWJvb2sNCg0KLS0tLS0tLS0tLS0tLS0NCg0KPGJyPg0KDQoqIFRoZSBnb29nbGUgc2VhcmNoIGRhdGEgb24gdGhpcyBub3RlYm9vayBjb21lcyBmcm9tIGEgZ29vZ2xlIGFjY291bnQgYXJjaGl2ZQ0KDQoqIFRoZSBzdGVwcyBvdXRsaW5lZCBoZXJlIHRvIGNvbGxlY3QgYW5kIGFuYWx5emUgdGhlIGRhdGEgbWF5IGNoYW5nZSBhdCBhbnkgdGltZQ0KDQoqIEJlbG93IGFyZSB0aGUgc3RlcHMgdG8gY2xhaW0geW91ciBnb29nbGUgYWNjb3VudCBkYXRhIA0KDQoNCjxicj4NCg0KLS0tLS0tLS0tLS0tLS0NCg0KIyMgQW5hbHl0aWNzIFRvb2xraXQ6IFJlcXVpcmUgUGFja2FnZXMNCg0KLS0tLS0tLS0tLS0tLS0NCg0KPGJyPg0KDQoqIFBhY2thZ2U6IHRpZHl2ZXJzZSwgbHVicmlkYXRlLCBydmVzdCwgdG0sIHdvcmRjbG91ZA0KDQpgYGB7ciwgZWNobz1UUlVFfQ0KDQojIEhlcmUgd2UgYXJlIGNoZWNraW5nIGlmIHRoZSBwYWNrYWdlIGlzIGluc3RhbGxlZA0KaWYoIXJlcXVpcmUoInJ2ZXN0IiwgcXVpZXRseSA9IFRSVUUpKSANCiAgIyBJZiB0aGUgcGFja2FnZSBpcyBub3QgaW4gdGhlIHN5c3RlbSB0aGVuIGl0IHdpbGwgYmUgaW5zdGFsbA0KICBpbnN0YWxsLnBhY2thZ2VzKHJ2ZXN0LCBkZXBlbmRlbmNpZXMgPSBUUlVFLCBxdWlldGx5ID0gVFJVRSkNCiAgIyBIZXJlIHdlIGFyZSBsb2FkaW5nIHRoZSBwYWNrYWdlDQogIGxpYnJhcnkocnZlc3QsIHF1aWV0bHkgPSBUUlVFKQ0KDQppZighcmVxdWlyZSgibHVicmlkYXRlIiwgcXVpZXRseSA9IFRSVUUpKSANCiAgaW5zdGFsbC5wYWNrYWdlcyhsdWJyaWRhdGUsIGRlcGVuZGVuY2llcyA9IFRSVUUsIHF1aWV0bHkgPSBUUlVFKQ0KICBsaWJyYXJ5KGx1YnJpZGF0ZSwgcXVpZXRseSA9IFRSVUUpDQoNCmlmKCFyZXF1aXJlKCJ3b3JkY2xvdWQiLCBxdWlldGx5ID0gVFJVRSkpIA0KICBpbnN0YWxsLnBhY2thZ2VzKHdvcmRjbG91ZCwgZGVwZW5kZW5jaWVzID0gVFJVRSwgcXVpZXRseSA9IFRSVUUpDQogIGxpYnJhcnkod29yZGNsb3VkLCBxdWlldGx5ID0gVFJVRSkNCg0KaWYoIXJlcXVpcmUoInRtIiwgcXVpZXRseSA9IFRSVUUpKSANCiAgaW5zdGFsbC5wYWNrYWdlcyh0bSwgZGVwZW5kZW5jaWVzID0gVFJVRSwgcXVpZXRseSA9IFRSVUUpDQogIGxpYnJhcnkodG0sIHF1aWV0bHkgPSBUUlVFKQ0KDQppZighcmVxdWlyZSh0aWR5dmVyc2UsIHF1aWV0bHkgPSBUUlVFKSkNCiAgaW5zdGFsbC5wYWNrYWdlcyh0aWR5dmVyc2UsIGRlcGVuZGVuY2llcyA9IFRSVUUsIHF1aWV0bHkgPSBUUlVFKQ0KICBsaWJyYXJ5KHRpZHl2ZXJzZSwgcXVpZXRseSA9IFRSVUUpDQoNCmBgYA0KYGBge3J9DQpsaWJyYXJ5KHRtKQ0KbGlicmFyeSh3b3JkY2xvdWQpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLQ0KDQojIyBEYXRhIFByZXBhcmF0aW9uOiBFeHRyYWN0aW5nIEdvb2dsZSBTZWFyY2ggSW5mb3JtYXRpb24NCg0KLS0tLS0tLS0tLS0tLS0NCg0KPGJyPg0KDQojIyMgTG9jYXRlIHRoZSBHb29nbGUgYXJjaGl2ZSwgdGhlbiBmaW5kIHRoZSBzZWFyY2ggZGF0YS4gRm9yIHRoaXMgY2FzZSwgaXQgaXMgYW4gaHRtbCBmaWxlIGxvY2F0ZWQgaW4gIk15IEFjdGl2aXR5IiBmb2xkZXIiIHRoZW4gZmluZCB0aGUgIlNlYXJjaCIgZm9sZGVyIGFuZCB0aGUgaHRtbCBmaWxlICJNeUFjdGl2aXR5Lmh0bWwiIHNob3VsZCBiZSB0aGVyZToNCg0KKiBUYWtlb3V0IC0+IE15IEFjdGl2aXR5IC0+IFNlYXJjaCAtPiBNeUFjdGl2aXR5Lmh0bWwgDQoNCiMjIyBVc2luZyB0aGUgcnZlc3QgcGFja2FnZSB3ZSBjYW4gcmVhZCB0aGUgaHRtbCBkb2N1bWVudCB0aGF0IGNvbnRhaW5zIHRoZSByZWxhdGVkIGdvb2dsZSBzZWFyY2ggZGF0YQ0KDQpgYGB7cn0NCg0KZG9jIDwtICJUYWtlb3V0XFxNeSBBY3Rpdml0eVxcU2VhcmNoXFxNeUFjdGl2aXR5Lmh0bWwiDQpzZWFyY2hfYXJjaGl2ZSA8LSByZWFkX2h0bWwoZG9jKQ0KDQpgYGANCg0KPGJyPg0KDQotLS0tLS0tLS0tLS0tLQ0KDQojIyMgTGF2ZXJhZ2luZyByZWd1bGFyIGV4cHJlc3Npb24gd2UgY2FuIHNlYXJjaCB0aGUgaHRtbCBkb2N1bWVudCB0byBleHRyYWN0Og0KDQo8YnI+DQoNCiMjIyMgRXh0cmFjdCBTZWFyY2ggVGltZQ0KYGBge3J9DQoNCmRhdGVfc2VhcmNoIDwtIHNlYXJjaF9hcmNoaXZlICU+JSANCiAgaHRtbF9ub2Rlcyh4cGF0aCA9ICcvL2RpdltAY2xhc3M9Im1kbC1ncmlkIl0vZGl2L2RpdicpICU+JSANCiAgc3RyX2V4dHJhY3QocGF0dGVybiA9ICIoPzw9PGJyPikoLiopKD88PVBNfEFNKSIpICU+JQ0KICBtZHlfaG1zKCkNCg0KYGBgDQoNCjxicj4NCg0KIyMjIyBFeHRyYWN0IFNlYXJjaCBUZXh0DQpgYGB7cn0NCg0KdGV4dF9zZWFyY2ggPC0gc2VhcmNoX2FyY2hpdmUgJT4lIA0KICBodG1sX25vZGVzKHhwYXRoID0gJy8vZGl2W0BjbGFzcz0ibWRsLWdyaWQiXS9kaXYvZGl2JykgJT4lDQogIHN0cl9leHRyYWN0KHBhdHRlcm4gPSAnKD88PTxhKSguKikoPz08L2E+KScpICU+JSANCiAgc3RyX2V4dHJhY3QocGF0dGVybiA9ICcoPzw9XCI+KSguKiknKQ0KDQpgYGANCg0KPGJyPg0KDQojIyMjIEV4dHJhY3QgU2VhcmNoIFR5cGUNCmBgYHtyfQ0KDQp0eXBlX3NlYXJjaCA8LSBzZWFyY2hfYXJjaGl2ZSAlPiUgDQogIGh0bWxfbm9kZXMoeHBhdGggPSAnLy9kaXZbQGNsYXNzPSJtZGwtZ3JpZCJdL2Rpdi9kaXYnKSAlPiUgDQogIHN0cl9leHRyYWN0KHBhdHRlcm4gPSAiKD88PW1kbC10eXBvZ3JhcGh5LS1ib2R5LTFcIj4pKC4qKSg/PTxhKSIpICU+JSANCiAgc3RyX2V4dHJhY3QocGF0dGVybiA9ICIoXFx3KykoPz1cXHMpIikNCg0KYGBgDQoNCjxicj4NCg0KIyMjIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHVzaW5nIHRoZSBkYXRhIGV4dHJhY3RlZCBmcm9tIHRoZSBodG1sIGZpbGUNCmBgYHtyfQ0KDQpzZWFyY2hfZGF0YSA8LSB0aWJibGUodGltZXN0YW1wID0gZGF0ZV9zZWFyY2gsDQogICAgICAgICAgICAgICAgICAgICAgZGF0ZSA9IGFzX2RhdGUoZGF0ZV9zZWFyY2gpLA0KICAgICAgICAgICAgICAgICAgICAgIHllYXIgPSB5ZWFyKGRhdGVfc2VhcmNoKSwNCiAgICAgICAgICAgICAgICAgICAgICBtb250aCA9IG1vbnRoKGRhdGVfc2VhcmNoLCBsYWJlbCA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICAgICAgIGRheSA9IHdlZWtkYXlzKGRhdGVfc2VhcmNoKSwNCiAgICAgICAgICAgICAgICAgICAgICBob3VyID0gaG91cihkYXRlX3NlYXJjaCksDQogICAgICAgICAgICAgICAgICAgICAgdHlwZSA9IHR5cGVfc2VhcmNoLA0KICAgICAgICAgICAgICAgICAgICAgIHNlYXJjaCA9IHRleHRfc2VhcmNoKQ0KDQpzZWFyY2hfZGF0YSRkYXkgPC0gZmFjdG9yKHNlYXJjaF9kYXRhJGRheSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIlN1bmRheSIsICJNb25kYXkiLCAiVHVlc2RheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldlZG5lc2RheSIsIlRodXJzZGF5IiwgIkZyaWRheSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNhdHVyZGF5IikpDQoNCnNlYXJjaF9kYXRhIDwtIG5hLm9taXQoc2VhcmNoX2RhdGEpDQoNCmhlYWQoc2VhcmNoX2RhdGEpDQoNCmBgYA0KDQo8YnI+DQoNCi0tLS0tLS0tLS0tLS0tDQoNCiMjIERhdGEgQW5hbHlzaXM6IFZpc3VhbGl6aW5nIEdvb2dsZSBTZWFyY2hlcw0KDQotLS0tLS0tLS0tLS0tLQ0KDQo8YnI+DQoNCiMjIyMgVG8gZ2V0IGFuIG92ZXJhbGwgaWRlYSBvZiB0aGUgc2VhcmNoIHZvbHVtZSwgd2UgY2FuIHBsb3Qgc2VhcmNoZXMgYnkgeWVhciANCg0KYGBge3J9DQoNCnAgPC0gZ2dwbG90KHNlYXJjaF9kYXRhLCBhZXMoeWVhcikpDQpwICsgZ2VvbV9iYXIoKQ0KDQpgYGANCg0KPGJyPg0KDQojIyMjIEFmdGVyIGRldGVybWluZSB0aGUgeWVhcnMgd2l0aCB0aGUgbGFyZ2VzdCBzZWFyY2ggdm9sdW1lIHdlIGNhbiBwbG90IG1vbnRobHkgc2VhcmNoZXMNCmBgYHtyfQ0KDQptb250aGx5IDwtIHNlYXJjaF9kYXRhWyhzZWFyY2hfZGF0YSR5ZWFyID4gMjAxNCAmIHNlYXJjaF9kYXRhJHllYXI8IDIwMTgpLCBdDQoNCmdncGxvdChtb250aGx5KSArIGdlb21fYmFyKGFlcyh4ID0gbW9udGgsIGdyb3VwID0geWVhcikpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTApKSArDQogIGZhY2V0X2dyaWQoLn55ZWFyLCBzY2FsZXM9ImZyZWUiKQ0KDQpgYGANCg0KPGJyPg0KDQojIyMjIEFub3RoZXIgaW50ZXJlc3RpbmcgbWV0cmljdCBpcyBzZWFyY2hlcyBieSBIb3VyDQpgYGB7cn0NCg0KcCA8LSBnZ3Bsb3Qoc2VhcmNoX2RhdGEsIGFlcyhob3VyKSkNCnAgKyBnZW9tX2JhcigpDQoNCmBgYA0KDQo8YnI+DQoNCiMjIyMgV2UgY2FuIGFsc28gcGxvdCB0aGUgc2VhcmNoIGRhdGEgYnkgZGF5IG9mIHRoZSB3ZWVrIHRvIGRldGVybWluZSBkYXkgYXJlIHRoZSBtb3N0IGFjdGl2ZQ0KYGBge3J9DQoNCnAgPC0gZ2dwbG90KHNlYXJjaF9kYXRhLCBhZXMoZGF5KSkNCnAgKyBnZW9tX2JhcigpDQoNCmBgYA0KDQo8YnI+DQoNCiMjIyMgV2UgY2FuIHRha2UgaXQgYW4gc3RlcCBmdXJ0aGVyIGFuZCBncm91cCBzZWFyY2ggdGltZSB3aXRoIGRheSBvZiB0aGUgd2Vlay4gDQpgYGB7cn0NCg0KZ2dwbG90KHNlYXJjaF9kYXRhKSArIA0KICBnZW9tX2JhcihhZXMoeCA9IGhvdXIsIGdyb3VwID0gZGF5KSApICsNCiAgZmFjZXRfZ3JpZCgufmRheSwgc2NhbGVzID0gImZyZWUiKQ0KDQpgYGANCg0KPGJyPg0KDQojIyMjIFdlIGNhbiBncm91cCB0aGUgc2VhcmNoIGRhdGEgYnkgeWVhciBhbmQgZGF5IG9mIHRoZSB3ZWVrLCB0byB2aXN1YWxpemUgdGhlIG92ZXJhbGwgdHJlbmQgDQpgYGB7cn0NCg0Kd2tkYXkgPC0gZ3JvdXBfYnkoc2VhcmNoX2RhdGEsIHllYXIsIGRheSkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkNCnAgPC0gZ2dwbG90KHdrZGF5LCBhZXMoZGF5LCBjb3VudCwgZmlsbCA9IHllYXIpKSANCnAgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyBsYWJzKHggPSAiIiwgeSA9ICJTZWFyY2ggVm9sdW1lIikNCg0KYGBgDQoNCjxicj4NCg0KLS0tLS0tLS0tLS0tLS0NCg0KIyMgUmVwb3J0aW5nOiBBIHdvcmRjbG91ZCBmcm9tIEdvb2dsZSBTZWFyY2ggRGF0YQ0KDQotLS0tLS0tLS0tLS0tLQ0KDQo8YnI+DQoNCiMjIyMgRmlyc3Qgd2UgbmVlZCB0byBleHRyYWN0IHRoZSB0ZXh0IGFuZCBjbGVhbiBpdCB1c2luZyByZWd1bGFyIGV4cHJlc3Npb25zDQpgYGB7cn0NCg0Kc2VhcmNoIDwtIHRvbG93ZXIoc2VhcmNoX2RhdGEkc2VhcmNoKQ0KI3NlYXJjaCA8LSBpY29udihzZWFyY2gsICJBU0NJSSIsICJVVEYtOCIsICIpDQpzZWFyY2ggPC0gZ3N1YignKGh0dHB8aHR0cHMpXFxTK1xccyp8KCN8QClcXFMrXFxzKnxcXG58XFwiJywgIiAiLCBzZWFyY2gpDQpzZWFyY2ggPC0gZ3N1YigiKC4qLilcXC5jb20oLiouKVxcUytcXHN8W15bOmFsbnVtOl1dIiwgIiAiLCBzZWFyY2gpDQpzZWFyY2ggPC0gdHJpbXdzKHNlYXJjaCkNCg0KYGBgDQoNCjxicj4NCg0KIyMjIyBBZnRlciBjbGVhbmluZyB0aGUgdGV4dCB3ZSBjYW4gY3JlYXRlIGEgVGV4dCBDb3JwdXMgKGEgbGFyZ2UgYW5kIHN0cnVjdHVyZWQgc2V0IG9mIHRleHRzKSBhbmQgcmVtb3ZlIHNvbWUgd29yZHMgDQpgYGB7cn0NCg0Kc2VhcmNoX2NvcnB1cyA8LSAgQ29ycHVzKFZlY3RvclNvdXJjZShzZWFyY2gpKQ0Kc2VhcmNoX2NvcnB1cyA8LSB0bV9tYXAoc2VhcmNoX2NvcnB1cywgY29udGVudF90cmFuc2Zvcm1lcihyZW1vdmVQdW5jdHVhdGlvbikpDQpzZWFyY2hfY29ycHVzIDwtIHRtX21hcChzZWFyY2hfY29ycHVzLCBjb250ZW50X3RyYW5zZm9ybWVyKHJlbW92ZU51bWJlcnMpKQ0Kc3RvcHdvcmRzIDwtIGMoc3RvcHdvcmRzKCJlbmdsaXNoIiksICJjaHJvbWUiLCAiY2hpY2FnbyIsICJqbHJvbyIsICJnb29nbGUiLCJvbmxpbmUiLCJnb29kIiwicmF0ZSIpDQpzZWFyY2hfY29ycHVzIDwtIHRtX21hcChzZWFyY2hfY29ycHVzLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKQ0KDQpgYGANCg0KPGJyPg0KDQpgYGB7cn0NCg0Kc2VhcmNoX3RkbSA8LSBUZXJtRG9jdW1lbnRNYXRyaXgoc2VhcmNoX2NvcnB1cykNCnNlYXJjaF9tYXRyaXggPC0gYXMubWF0cml4KHNlYXJjaF90ZG0pDQoNCmBgYA0KDQoNCjxicj4NCg0KIyMjIyBVc2luZyB0aGUgVGVybSBEb2N1bWVudCBtYXRyaXggd2UgY2FuIGNyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCB0aGUgd29yZHMgYW5kIGl0cyByZWxhdGVkIGZyZXF1ZW5jaWVzIA0KYGBge3J9DQoNCnYgPC0gc29ydChyb3dTdW1zKHNlYXJjaF9tYXRyaXgpLCBkZWNyZWFzaW5nID0gVFJVRSkNCnR3X25hbWVzIDwtIG5hbWVzKHYpDQpkIDwtIGRhdGEuZnJhbWUod29yZCA9IHR3X25hbWVzLCBmcmVxID0gdikNCg0KYGBgDQoNCjxicj4NCg0KIyMjIyBTZXQgYSB0aHJlc2hvbGQgZm9yIHRoZSBtaW4gZnJlcXVlbmN5IG9mIHRoZSB3b3JkcyB0byBkaXNwbGF5IGFzIHdlbGwgYXMgbWF4IGZyZXF1ZW5jeQ0KYGBge3J9DQoNCndvcmRjbG91ZChkJHdvcmQsIGQkZnJlcSwgbWluLmZyZXEgPSAzMCwgc2NhbGUgPSBjKDIgLCAwLjUpLCBtYXgud29yZHMgPSAyMDApDQoNCmBgYA0K