library(data.table)
package 愼㸱愼㸵data.table愼㸱愼㸶 was built under R version 3.4.4data.table 1.10.4.3
  The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way
  Documentation: ?data.table, example(data.table) and browseVignettes("data.table")
  Release notes, videos and slides: http://r-datatable.com
library(dplyr) #data manipulation

Attaching package: 愼㸱愼㸵dplyr愼㸱愼㸶

The following objects are masked from 愼㸱愼㸵package:data.table愼㸱愼㸶:

    between, first, last

The following objects are masked from 愼㸱愼㸵package:stats愼㸱愼㸶:

    filter, lag

The following objects are masked from 愼㸱愼㸵package:base愼㸱愼㸶:

    intersect, setdiff, setequal, union
library(ggplot2) #visualizations
library(gridExtra) #viewing multiple plots together
package 愼㸱愼㸵gridExtra愼㸱愼㸶 was built under R version 3.4.4
Attaching package: 愼㸱愼㸵gridExtra愼㸱愼㸶

The following object is masked from 愼㸱愼㸵package:dplyr愼㸱愼㸶:

    combine
library(tidytext) #text mining
#library(wordcloud2) #creative visualizations
load data
lyric_data=fread('../data/prince_raw_data.csv')

4.1 Tokenizing by n-gram

library(dplyr)
library(tidytext)
library(janeaustenr)
lyric_bigrams <- lyric_data[,1:6] %>%
  unnest_tokens(bigram, text, token = "ngrams", n = 2)
lyric_bigrams

4.1.1 Counting and filtering n-grams

lyric_bigrams %>%
  count(bigram, sort = TRUE)
library(tidyr)
package 愼㸱愼㸵tidyr愼㸱愼㸶 was built under R version 3.4.4
bigrams_separated <-lyric_bigrams %>%
  separate(bigram, c("word1", "word2"), sep = " ")
bigrams_filtered <- bigrams_separated %>%
  filter(!word1 %in% stop_words$word) %>%
  filter(!word2 %in% stop_words$word)
# new bigram counts:
bigram_counts <- bigrams_filtered %>% 
  count(word1, word2, sort = TRUE)
bigram_counts
bigrams_united <- bigrams_filtered %>%
  unite(bigram, word1, word2, sep = " ")
bigrams_united
lyric_data[,1:6] %>%
  unnest_tokens(trigram, text, token = "ngrams", n = 3) %>%
  separate(trigram, c("word1", "word2", "word3"), sep = " ") %>%
  filter(!word1 %in% stop_words$word,
         !word2 %in% stop_words$word,
         !word3 %in% stop_words$word) %>%
  count(word1, word2, word3, sort = TRUE)

4.1.2 Analyzing bigrams

bigrams_filtered %>%
  filter(word2 == "street") %>%
  count(album, word1, sort = TRUE)
bigram_tf_idf <- bigrams_united %>%
  count(album, bigram) %>%
  bind_tf_idf(bigram, album, n) %>%
  arrange(desc(tf_idf))
bigram_tf_idf

4.1.3 Using bigrams to provide context in sentiment analysis

bigrams_separated %>%
  filter(word1 == "not") %>%
  count(word1, word2, sort = TRUE)
AFINN <- get_sentiments("afinn")
AFINN
not_words <- bigrams_separated %>%
  filter(word1 == "not") %>%
  inner_join(AFINN, by = c(word2 = "word")) %>%
  count(word2, score, sort = TRUE) %>%
  ungroup()
not_words
library(ggplot2)
not_words %>%
  mutate(contribution = n * score) %>%
  arrange(desc(abs(contribution))) %>%
  head(20) %>%
  mutate(word2 = reorder(word2, contribution)) %>%
  ggplot(aes(word2, n * score, fill = n * score > 0)) +
  geom_col(show.legend = FALSE) +
  xlab("Words preceded by \"not\"") +
  ylab("Sentiment score * number of occurrences") +
  coord_flip()

negation_words <- c("not", "no", "never", "without")
negated_words <- bigrams_separated %>%
  filter(word1 %in% negation_words) %>%
  inner_join(AFINN, by = c(word2 = "word")) %>%
  count(word1, word2, score, sort = TRUE) %>%
  ungroup()

4.1.4 Visualizing a network of bigrams with ggraph

library(igraph)

Attaching package: 愼㸱愼㸵igraph愼㸱愼㸶

The following object is masked from 愼㸱愼㸵package:tidyr愼㸱愼㸶:

    crossing

The following objects are masked from 愼㸱愼㸵package:dplyr愼㸱愼㸶:

    as_data_frame, groups, union

The following objects are masked from 愼㸱愼㸵package:stats愼㸱愼㸶:

    decompose, spectrum

The following object is masked from 愼㸱愼㸵package:base愼㸱愼㸶:

    union
# original counts
bigram_counts
# filter for only relatively common combinations
bigram_graph <- bigram_counts %>%
  filter(n > 20) %>%
  graph_from_data_frame()
bigram_graph
IGRAPH 8466b6a DN-- 131 126 -- 
+ attr: name (v/c), n (e/n)
+ edges from 8466b6a (vertex names):
 [1] la       ->la            yeah     ->yeah         
 [3] da       ->da            prince   ->miscellaneous
 [5] na       ->na            ha       ->ha           
 [7] baby     ->baby          doo      ->doo          
 [9] ooh      ->ooh           rock     ->rock         
[11] hey      ->hey           sexy     ->er           
[13] dance    ->dance         4        ->love         
[15] ooh      ->baby          uh       ->uh           
+ ... omitted several edges
library(ggraph)
set.seed(2017)
ggraph(bigram_graph, layout = "fr") +
  geom_edge_link() +
  geom_node_point() +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1)

set.seed(2016)
a <- grid::arrow(type = "closed", length = unit(.15, "inches"))
ggraph(bigram_graph, layout = "fr") +
  geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
                 arrow = a, end_cap = circle(.07, 'inches')) +
  geom_node_point(color = "lightblue", size = 5) +
  geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
  theme_void()

4.1.5 Visualizing bigrams in other texts

library(dplyr)
library(tidyr)
library(tidytext)
library(ggplot2)
library(igraph)
library(ggraph)
count_bigrams <- function(dataset) {
  dataset %>%
    unnest_tokens(bigram, text, token = "ngrams", n = 2) %>%
    separate(bigram, c("word1", "word2"), sep = " ") %>%
    filter(!word1 %in% stop_words$word,
           !word2 %in% stop_words$word) %>%
    count(word1, word2, sort = TRUE)
}
visualize_bigrams <- function(bigrams) {
  set.seed(2016)
  a <- grid::arrow(type = "closed", length = unit(.15, "inches"))
  
  bigrams %>%
    graph_from_data_frame() %>%
    ggraph(layout = "fr") +
    geom_edge_link(aes(edge_alpha = n), show.legend = FALSE, arrow = a) +
    geom_node_point(color = "lightblue", size = 5) +
    geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
    theme_void()
}

4.2 Counting and correlating pairs of words with the widyr package

4.2.1 Counting and correlating among sections

lyric_section_words <- lyric_data[,1:6] %>%
  
  mutate(section = row_number() %/% 10) %>%
  filter(section > 0) %>%
  unnest_tokens(word, text) %>%
  filter(!word %in% stop_words$word)
lyric_section_words
library(widyr)
# count words co-occuring within sections
word_pairs <- lyric_section_words %>%
  pairwise_count(word, section, sort = TRUE)
word_pairs
word_pairs %>%
  filter(item1 == "prince")

4.2.2 Pairwise correlation

# we need to filter for at least relatively common words first
word_cors <- lyric_section_words %>%
  group_by(word) %>%
  filter(n() >= 20) %>%
  pairwise_cor(word, section, sort = TRUE)
word_cors
word_cors %>%
  filter(item1 == "alfred")
word_cors %>%
  filter(item1 %in% c("alfred", "chelsea", "parker", "witness")) %>%
  group_by(item1) %>%
  top_n(6) %>%
  ungroup() %>%
  mutate(item2 = reorder(item2, correlation)) %>%
  ggplot(aes(item2, correlation)) +
  geom_bar(stat = "identity") +
  facet_wrap(~ item1, scales = "free") +
  coord_flip()
Selecting by correlation

set.seed(2016)
word_cors %>%
  filter(correlation > .9) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation), show.legend = FALSE) +
  geom_node_point(color = "lightblue", size = 5) +
  geom_node_text(aes(label = name), repel = TRUE) +
  theme_void()

LS0tDQp0aXRsZTogIlJlbGF0aW9uc2hpcHMgYmV0d2VlbiB3b3Jkczogbi1ncmFtcyBhbmQgY29ycmVsYXRpb25zIg0KYXV0aG9yOiAn5YqJ6IKy6YqYJw0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQpgYGB7cn0NCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkoZHBseXIpICNkYXRhIG1hbmlwdWxhdGlvbg0KbGlicmFyeShnZ3Bsb3QyKSAjdmlzdWFsaXphdGlvbnMNCmxpYnJhcnkoZ3JpZEV4dHJhKSAjdmlld2luZyBtdWx0aXBsZSBwbG90cyB0b2dldGhlcg0KbGlicmFyeSh0aWR5dGV4dCkgI3RleHQgbWluaW5nDQojbGlicmFyeSh3b3JkY2xvdWQyKSAjY3JlYXRpdmUgdmlzdWFsaXphdGlvbnMNCmBgYA0KDQojIyMjI2xvYWQgZGF0YQ0KDQpgYGB7cn0NCmx5cmljX2RhdGE9ZnJlYWQoJy4uL2RhdGEvcHJpbmNlX3Jhd19kYXRhLmNzdicpDQpgYGANCg0KDQoNCiMjIzQuMSBUb2tlbml6aW5nIGJ5IG4tZ3JhbQ0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeShqYW5lYXVzdGVucikNCg0KbHlyaWNfYmlncmFtcyA8LSBseXJpY19kYXRhWywxOjZdICU+JQ0KICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpDQoNCmx5cmljX2JpZ3JhbXMNCg0KYGBgDQoNCg0KIyMjIzQuMS4xIENvdW50aW5nIGFuZCBmaWx0ZXJpbmcgbi1ncmFtcw0KDQpgYGB7cn0NCmx5cmljX2JpZ3JhbXMgJT4lDQogIGNvdW50KGJpZ3JhbSwgc29ydCA9IFRSVUUpDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXIpDQoNCmJpZ3JhbXNfc2VwYXJhdGVkIDwtbHlyaWNfYmlncmFtcyAlPiUNCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpDQoNCmJpZ3JhbXNfZmlsdGVyZWQgPC0gYmlncmFtc19zZXBhcmF0ZWQgJT4lDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JQ0KICBmaWx0ZXIoIXdvcmQyICVpbiUgc3RvcF93b3JkcyR3b3JkKQ0KDQojIG5ldyBiaWdyYW0gY291bnRzOg0KYmlncmFtX2NvdW50cyA8LSBiaWdyYW1zX2ZpbHRlcmVkICU+JSANCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkNCg0KYmlncmFtX2NvdW50cw0KYGBgDQoNCg0KDQpgYGB7cn0NCmJpZ3JhbXNfdW5pdGVkIDwtIGJpZ3JhbXNfZmlsdGVyZWQgJT4lDQogIHVuaXRlKGJpZ3JhbSwgd29yZDEsIHdvcmQyLCBzZXAgPSAiICIpDQoNCmJpZ3JhbXNfdW5pdGVkDQpgYGANCg0KDQoNCmBgYHtyfQ0KbHlyaWNfZGF0YVssMTo2XSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh0cmlncmFtLCB0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMykgJT4lDQogIHNlcGFyYXRlKHRyaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiwgIndvcmQzIiksIHNlcCA9ICIgIikgJT4lDQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQsDQogICAgICAgICAhd29yZDMgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHdvcmQzLCBzb3J0ID0gVFJVRSkNCmBgYA0KDQoNCg0KIyMjIzQuMS4yIEFuYWx5emluZyBiaWdyYW1zDQoNCmBgYHtyfQ0KYmlncmFtc19maWx0ZXJlZCAlPiUNCiAgZmlsdGVyKHdvcmQyID09ICJzdHJlZXQiKSAlPiUNCiAgY291bnQoYWxidW0sIHdvcmQxLCBzb3J0ID0gVFJVRSkNCmBgYA0KDQoNCg0KYGBge3J9DQpiaWdyYW1fdGZfaWRmIDwtIGJpZ3JhbXNfdW5pdGVkICU+JQ0KICBjb3VudChhbGJ1bSwgYmlncmFtKSAlPiUNCiAgYmluZF90Zl9pZGYoYmlncmFtLCBhbGJ1bSwgbikgJT4lDQogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKQ0KDQpiaWdyYW1fdGZfaWRmDQpgYGANCg0KIyMjIzQuMS4zIFVzaW5nIGJpZ3JhbXMgdG8gcHJvdmlkZSBjb250ZXh0IGluIHNlbnRpbWVudCBhbmFseXNpcw0KYGBge3J9DQpiaWdyYW1zX3NlcGFyYXRlZCAlPiUNCiAgZmlsdGVyKHdvcmQxID09ICJub3QiKSAlPiUNCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkNCmBgYA0KDQoNCmBgYHtyfQ0KQUZJTk4gPC0gZ2V0X3NlbnRpbWVudHMoImFmaW5uIikNCg0KQUZJTk4NCmBgYA0KDQoNCg0KYGBge3J9DQpub3Rfd29yZHMgPC0gYmlncmFtc19zZXBhcmF0ZWQgJT4lDQogIGZpbHRlcih3b3JkMSA9PSAibm90IikgJT4lDQogIGlubmVyX2pvaW4oQUZJTk4sIGJ5ID0gYyh3b3JkMiA9ICJ3b3JkIikpICU+JQ0KICBjb3VudCh3b3JkMiwgc2NvcmUsIHNvcnQgPSBUUlVFKSAlPiUNCiAgdW5ncm91cCgpDQoNCm5vdF93b3Jkcw0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCm5vdF93b3JkcyAlPiUNCiAgbXV0YXRlKGNvbnRyaWJ1dGlvbiA9IG4gKiBzY29yZSkgJT4lDQogIGFycmFuZ2UoZGVzYyhhYnMoY29udHJpYnV0aW9uKSkpICU+JQ0KICBoZWFkKDIwKSAlPiUNCiAgbXV0YXRlKHdvcmQyID0gcmVvcmRlcih3b3JkMiwgY29udHJpYnV0aW9uKSkgJT4lDQogIGdncGxvdChhZXMod29yZDIsIG4gKiBzY29yZSwgZmlsbCA9IG4gKiBzY29yZSA+IDApKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgeGxhYigiV29yZHMgcHJlY2VkZWQgYnkgXCJub3RcIiIpICsNCiAgeWxhYigiU2VudGltZW50IHNjb3JlICogbnVtYmVyIG9mIG9jY3VycmVuY2VzIikgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQoNCmBgYHtyfQ0KbmVnYXRpb25fd29yZHMgPC0gYygibm90IiwgIm5vIiwgIm5ldmVyIiwgIndpdGhvdXQiKQ0KDQpuZWdhdGVkX3dvcmRzIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JQ0KICBmaWx0ZXIod29yZDEgJWluJSBuZWdhdGlvbl93b3JkcykgJT4lDQogIGlubmVyX2pvaW4oQUZJTk4sIGJ5ID0gYyh3b3JkMiA9ICJ3b3JkIikpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHNjb3JlLCBzb3J0ID0gVFJVRSkgJT4lDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KDQojIyMjNC4xLjQgVmlzdWFsaXppbmcgYSBuZXR3b3JrIG9mIGJpZ3JhbXMgd2l0aCBnZ3JhcGgNCg0KYGBge3J9DQpsaWJyYXJ5KGlncmFwaCkNCg0KIyBvcmlnaW5hbCBjb3VudHMNCmJpZ3JhbV9jb3VudHMNCmBgYA0KDQoNCg0KYGBge3J9DQojIGZpbHRlciBmb3Igb25seSByZWxhdGl2ZWx5IGNvbW1vbiBjb21iaW5hdGlvbnMNCmJpZ3JhbV9ncmFwaCA8LSBiaWdyYW1fY291bnRzICU+JQ0KICBmaWx0ZXIobiA+IDIwKSAlPiUNCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkNCg0KYmlncmFtX2dyYXBoDQpgYGANCg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dyYXBoKQ0Kc2V0LnNlZWQoMjAxNykNCg0KZ2dyYXBoKGJpZ3JhbV9ncmFwaCwgbGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluaygpICsNCiAgZ2VvbV9ub2RlX3BvaW50KCkgKw0KICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpDQpgYGANCg0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjAxNikNCg0KYSA8LSBncmlkOjphcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoLjE1LCAiaW5jaGVzIikpDQoNCmdncmFwaChiaWdyYW1fZ3JhcGgsIGxheW91dCA9ICJmciIpICsNCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgYXJyb3cgPSBhLCBlbmRfY2FwID0gY2lyY2xlKC4wNywgJ2luY2hlcycpKSArDQogIGdlb21fbm9kZV9wb2ludChjb2xvciA9ICJsaWdodGJsdWUiLCBzaXplID0gNSkgKw0KICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpICsNCiAgdGhlbWVfdm9pZCgpDQpgYGANCg0KDQoNCiMjIyM0LjEuNSBWaXN1YWxpemluZyBiaWdyYW1zIGluIG90aGVyIHRleHRzDQoNCg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkodGlkeXRleHQpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkoZ2dyYXBoKQ0KDQpjb3VudF9iaWdyYW1zIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsNCiAgZGF0YXNldCAlPiUNCiAgICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpICU+JQ0KICAgIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKSAlPiUNCiAgICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkLA0KICAgICAgICAgICAhd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JQ0KICAgIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpDQp9DQoNCnZpc3VhbGl6ZV9iaWdyYW1zIDwtIGZ1bmN0aW9uKGJpZ3JhbXMpIHsNCiAgc2V0LnNlZWQoMjAxNikNCiAgYSA8LSBncmlkOjphcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoLjE1LCAiaW5jaGVzIikpDQogIA0KICBiaWdyYW1zICU+JQ0KICAgIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQ0KICAgIGdncmFwaChsYXlvdXQgPSAiZnIiKSArDQogICAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwgYXJyb3cgPSBhKSArDQogICAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSA1KSArDQogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHZqdXN0ID0gMSwgaGp1c3QgPSAxKSArDQogICAgdGhlbWVfdm9pZCgpDQp9DQpgYGANCg0KDQoNCg0KDQoNCiMjIzQuMiBDb3VudGluZyBhbmQgY29ycmVsYXRpbmcgcGFpcnMgb2Ygd29yZHMgd2l0aCB0aGUgd2lkeXIgcGFja2FnZQ0KDQoNCiMjIyM0LjIuMSBDb3VudGluZyBhbmQgY29ycmVsYXRpbmcgYW1vbmcgc2VjdGlvbnMNCg0KYGBge3J9DQpseXJpY19zZWN0aW9uX3dvcmRzIDwtIGx5cmljX2RhdGFbLDE6Nl0gJT4lDQogIA0KICBtdXRhdGUoc2VjdGlvbiA9IHJvd19udW1iZXIoKSAlLyUgMTApICU+JQ0KICBmaWx0ZXIoc2VjdGlvbiA+IDApICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICU+JQ0KICBmaWx0ZXIoIXdvcmQgJWluJSBzdG9wX3dvcmRzJHdvcmQpDQoNCmx5cmljX3NlY3Rpb25fd29yZHMNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkod2lkeXIpDQoNCiMgY291bnQgd29yZHMgY28tb2NjdXJpbmcgd2l0aGluIHNlY3Rpb25zDQp3b3JkX3BhaXJzIDwtIGx5cmljX3NlY3Rpb25fd29yZHMgJT4lDQogIHBhaXJ3aXNlX2NvdW50KHdvcmQsIHNlY3Rpb24sIHNvcnQgPSBUUlVFKQ0KDQp3b3JkX3BhaXJzDQpgYGANCg0KDQpgYGB7cn0NCndvcmRfcGFpcnMgJT4lDQogIGZpbHRlcihpdGVtMSA9PSAicHJpbmNlIikNCmBgYA0KDQoNCg0KIyMjNC4yLjIgUGFpcndpc2UgY29ycmVsYXRpb24NCg0KYGBge3J9DQojIHdlIG5lZWQgdG8gZmlsdGVyIGZvciBhdCBsZWFzdCByZWxhdGl2ZWx5IGNvbW1vbiB3b3JkcyBmaXJzdA0Kd29yZF9jb3JzIDwtIGx5cmljX3NlY3Rpb25fd29yZHMgJT4lDQogIGdyb3VwX2J5KHdvcmQpICU+JQ0KICBmaWx0ZXIobigpID49IDIwKSAlPiUNCiAgcGFpcndpc2VfY29yKHdvcmQsIHNlY3Rpb24sIHNvcnQgPSBUUlVFKQ0KDQp3b3JkX2NvcnMNCmBgYA0KDQpgYGB7cn0NCndvcmRfY29ycyAlPiUNCiAgZmlsdGVyKGl0ZW0xID09ICJhbGZyZWQiKQ0KYGBgDQoNCg0KYGBge3J9DQp3b3JkX2NvcnMgJT4lDQogIGZpbHRlcihpdGVtMSAlaW4lIGMoImFsZnJlZCIsICJjaGVsc2VhIiwgInBhcmtlciIsICJ3aXRuZXNzIikpICU+JQ0KICBncm91cF9ieShpdGVtMSkgJT4lDQogIHRvcF9uKDYpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIG11dGF0ZShpdGVtMiA9IHJlb3JkZXIoaXRlbTIsIGNvcnJlbGF0aW9uKSkgJT4lDQogIGdncGxvdChhZXMoaXRlbTIsIGNvcnJlbGF0aW9uKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBmYWNldF93cmFwKH4gaXRlbTEsIHNjYWxlcyA9ICJmcmVlIikgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMjAxNikNCg0Kd29yZF9jb3JzICU+JQ0KICBmaWx0ZXIoY29ycmVsYXRpb24gPiAuOSkgJT4lDQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQ0KICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IGNvcnJlbGF0aW9uKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAibGlnaHRibHVlIiwgc2l6ZSA9IDUpICsNCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gVFJVRSkgKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQoNCjxzdHlsZT4NCg0KZW0gew0KICAgIGNvbG9yOiAjRkZFQTZDOw0KICAgIGJhY2tncm91bmQ6ICM3RDdEN0Q7DQp9DQoNCi5jYXB0aW9uIHsNCiAgY29sb3I6ICM3Nzc7DQogIG1hcmdpbi10b3A6IDEwcHg7DQp9DQpwIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnByZSB7DQogIHdvcmQtYnJlYWs6IG5vcm1hbDsNCiAgd29yZC13cmFwOiBub3JtYWw7DQogIGxpbmUtaGVpZ2h0OiAxOw0KfQ0KcHJlIGNvZGUgew0KICB3aGl0ZS1zcGFjZTogaW5oZXJpdDsNCn0NCnAsbGkgew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KLnJ7DQogIGxpbmUtaGVpZ2h0OiAxLjI7DQp9DQoNCi5xaXogew0KICBsaW5lLWhlaWdodDogMS43NTsNCiAgYmFja2dyb3VuZDogI2YwZjBmMDsNCiAgYm9yZGVyLWxlZnQ6IDEycHggc29saWQgI2NjZmZjYzsNCiAgcGFkZGluZzogNHB4Ow0KICBwYWRkaW5nLWxlZnQ6IDEwcHg7DQogIGNvbG9yOiAjMDA5OTAwOw0KfQ0KDQp0aXRsZXsNCiAgY29sb3I6ICNjYzAwMDA7DQogIGZvbnQtZmFtaWx5OiAiVHJlYnVjaGV0IE1TIiwgIuW+rui7n+ato+m7kemrlCIsICJNaWNyb3NvZnQgSmhlbmdIZWkiOw0KfQ0KDQpib2R5ew0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KaDEsaDIsaDMsaDQsaDV7DQogIGNvbG9yOiAjMDA2NmZmOw0KICBmb250LWZhbWlseTogIlRyZWJ1Y2hldCBNUyIsICLlvq7ou5/mraPpu5Hpq5QiLCAiTWljcm9zb2Z0IEpoZW5nSGVpIjsNCn0NCg0KDQpoM3sNCiAgY29sb3I6ICNiMzZiMDA7DQogIGJhY2tncm91bmQ6ICNmZmUwYjM7DQogIGxpbmUtaGVpZ2h0OiAyOw0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDV7DQogIGNvbG9yOiAjMDA2MDAwOw0KICBiYWNrZ3JvdW5kOiAjZjhmOGY4Ow0KICBsaW5lLWhlaWdodDogMS41Ow0KICBmb250LXdlaWdodDogYm9sZDsNCn0NCg0KaDYgew0KICAgIGNvbG9yOiAjMDA2MDAwOw0KICAgIGJhY2tncm91bmQ6ICMwMGZmZmY7DQogICAgbGluZS1oZWlnaHQ6IDI7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQoNCjwvc3R5bGU+DQo=