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.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=