咪蒙文章考
载入必要的分析包
In strsplit(code, "\n", fixed = TRUE) :
input string 1 is invalid in this locale
library(tidytext) # 整洁文本操作
载入文本数据
先载入我通过python爬取下来的文本 ps.R语言不能像python一样在字符串前面加上r修饰符,因为我的系统是windows,所以每次都要复制路径后,在每个\后面再手动插入一个\,不知道有没有人有更好的办法
text_path = "C:\\Users\\lhs\\Desktop\\TEMP\\Machine Learning\\Data Sets\\mimeng\\mimeng_text_cut_search.csv"
mimeng <- read_delim(text_path, delim = "\t")
Parsed with column specification:
cols(
text = col_character(),
title = col_character()
)
mimeng
原始文本一共两列,text和title。 title表示标题。 text是咪蒙文章中的每一段话,我事先在python中用jieba分词对每段话都进行了分词。
简单词频分析
我们先简单统计一下每篇文章的段落数量:
mimeng_p <- mimeng %>%
group_by(title) %>%
summarize(p_num = n()) %>%
arrange(desc(p_num))
mimeng_p
段落数量最多的文章是《80本最棒的幼儿绘本推荐》,339段傲视群雄,看标题像篇软文,我找到原文看了一下,文章介绍了80个绘本,每个绘本下面配了一段介绍,所以是咪蒙最长的文章了,连她自己也在文章中吐槽自己写了篇1万字的长文。 我们把段落数量的频率分布直方图画出来,瞧一瞧整体的分布是啥样:
mimeng_p %>%
ggplot(aes(p_num )) +
geom_histogram(bins = 20) +
geom_vline(aes(xintercept = mean(mimeng_p$p_num[mimeng_p$p_num < 339])), size = 1, color = "red", linetype = 2) +
labs(x = "文章段落数量",
y = "篇数")

刨去推荐绘本的那片文章,咪蒙文章段落数量分布整体是很接近正态分布的,平均来看一篇文章大概90段是最常见的长度。
接下来我们将每段话转化为单个词。
mimeng_word <- mimeng %>%
unnest_tokens(word, text)
mimeng_word
为每篇文章统计词频:
words <- mimeng_word %>%
count(title, word, sort = TRUE)
words
我们挑选频数最高的词,画个条形图看一看:
words %>%
count(word, wt = n, sort = TRUE) %>%
top_n(10, nn) %>%
mutate(word = reorder(word, nn)) %>%
ggplot(aes(word, nn)) +
geom_col(show.legend = FALSE) +
labs(x = "词条",
y = NULL) +
coord_flip()

可以看到,词条中出现了很多意义不大的词条,我你他/她、的、了之类的,但其实咪蒙擅长以大白话的口吻以类似对话的方式编写文章,特别是标题,所以出现很多的人称代词就很正常了。
我们载入停止词,看看排除掉停止词后的咪蒙文章的词频如何。
my_stop_words <- read_csv("C:\\Users\\lhs\\Desktop\\TEMP\\Machine Learning\\Data Sets\\mimeng\\stopwords.csv")
words2 <- mimeng_word %>%
anti_join(my_stop_words) %>%
count(title, word, sort = TRUE)
words2
去除掉停止词之后就很有意思了,我们看头十条数据,次数最多的北、宽、木、图、椰子和萌都是对应文章中的人名,而孩子和出版社是跟主题相关。可见咪蒙很喜欢讲故事,而出现频率最高的词理所当然也就是各篇文章中的男女主角了。
统计每个词在全部文章中出现的频率,并画出词频最高的20个单词的条形图:
words2 %>%
count(word, wt = n, sort = TRUE) %>%
top_n(20, nn) %>%
mutate(word = reorder(word, nn)) %>%
ggplot(aes(word, nn)) +
geom_col(show.legend = FALSE) +
labs(x = "词条",
y = NULL) +
coord_flip()

“说”这个词的频繁度真是远超其他一切动词,连中国人最爱的“吃”字在其面前也要汗颜。 “朋友”算是全文频次最高的名词了,在咪蒙的文章里是个不可撼动的角色,充分体现了她“我有一个朋友”型故事的本色,毕竟女生都爱跟朋友聊天、聊八卦。 紧接着“朋友”高频出现的名词就是“孩子”了,果然孩子是女人心中重要的主题之一,除了“孩子”,“喜欢”、“爱”、“买”、“钱”、“妈”这些词也是非常的普遍,跟现实中女生的话题偏好也是重合的。我觉得有意思的是男人这种群体倒是咪蒙文章中的稀有生物,包括”老公“、”爸“等,可见咪蒙对男人并不待见。我们看一下跟男性相关的词汇出现的频率:
In strsplit(code, "\n", fixed = TRUE) :
input string 1 is invalid in this locale
words_men <- words2 %>%
filter(word %in% men) %>%
count(word, wt = n, sort = TRUE)
words_men
这些词汇,跟前面频次上千的话题比起来,真是九牛一毛。特别是“前夫”,尤其不招人待见~
画个词云看一下:
library(wordcloud2)
words2 %>%
count(word, wt = n, sort = TRUE) %>%
head(100) %>%
wordcloud2(color = "random-light", backgroundColor = "grey")
the condition has length > 1 and only the first element will be used
我们最后看一下根据tf-idf算出来的词汇排名:
words_tf_idf <- words2 %>%
bind_tf_idf(word, title, n) %>%
arrange(desc(tf_idf))
words_tf_idf
结果其实也差不多,排名靠前的主要也是人名,因为分词工具没办法正确识别出人名,而将人名切分成单个的字,所以导致一些人名的tf—idf的值很低。
文章情感分析
载入情感词典,本文采用大连理工大学情感词汇本体库(特别鸣谢~):
library(readxl)
sen_path = "C:\\Users\\lhs\\Desktop\\TEMP\\Machine Learning\\Data Sets\\mimeng\\情感词汇本体.xlsx"
my_sentiments <- read_xlsx(sen_path)
Expecting logical in K26363 / R26363C11: got 'NE'Expecting logical in K26523 / R26523C11: got 'NC'
my_sentiments
其中极性表示褒贬,0代表中性,1代表褒义,2代表贬义,3代表不确定。 我们利用该情感词汇文本对咪蒙的文章进行情感打分:
word_sentiments <- words2 %>%
inner_join(my_sentiments %>% select(`词语`, `极性`), by = c(word = "词语")) %>%
filter(`极性` != 3) %>%
mutate(sentiment = case_when(
`极性` == 1 ~ 1,
`极性` == 2 ~ -1,
TRUE ~ 0
))
word_sentiments
我们统计一下总体上,不同词汇对文章情感的贡献度
total_contribution <- word_sentiments %>%
count(word, wt = n * sentiment, sort = TRUE) %>%
transmute(word = word, contribution = nn)
total_contribution
我们挑出贡献度最高的40个词汇看一下:
total_contribution %>%
arrange(desc(abs(contribution))) %>%
head(40) %>%
mutate(word = reorder(word, contribution)) %>%
ggplot(aes(word, contribution, fill = contribution > 0)) +
geom_col(show.legend = FALSE) +
labs(x = "词汇",
y = "情感贡献") +
coord_flip() +
theme(axis.text.x = element_text(size = 15),
axis.text.y = element_text(size = 15))

很明显,情感分析的结果被“朋友”、“喜欢”整两个词给带偏了。 首先,“朋友”更多只是一个没有褒贬含义的主语或者谓语,比如“我的一个朋友”。 而“喜欢”这个词更多的是在论述某个问题,比如“你喜欢不喜欢某个人”、“你喜不喜欢这个工作”等。
我们画个单词网络看看, 首先将文本拆分成二元组:
token_2grams <- function(string) {
string1 <- str_split(string, " ")[[1]]
string2 <- lead(string1)
string2[is.na(string2)] <- ""
str_c(string1, string2, sep = " ")
}
gen_2grams <- function(x) map(x, token_2grams)
mimeng_bigrams <- mimeng %>%
unnest_tokens(bigram, text, token = gen_2grams)
bigrams_separated <- mimeng_bigrams %>%
separate(bigram, c("word1", "word2"), sep = " ")
bigrams_filtered <- bigrams_separated %>%
filter(!word1 %in% my_stop_words$word) %>%
filter(!word2 %in% my_stop_words$word)
bigrams_filtered
我们看看和朋友搭配的词语有哪些:
bigrams_filtered %>%
filter(word1 == "朋友") %>%
filter(!str_detect(word2, "朋友")) %>%
count(word1, word2, sort = TRUE)
“朋友说”这个组合是频率最高的。
我们再把同时出现次数超过100次的词汇联系图画出来:
library(igraph)
library(ggraph)
set.seed(2018)
bigrams_filtered %>%
filter(!(word1 == "" | word2 == "")) %>%
count(word1, word2, sort = TRUE) %>%
filter(n > 100) %>%
graph_from_data_frame() %>%
ggraph(layout = "fr") +
geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = "cyan4") +
geom_node_point(size = 5) +
geom_node_text(aes(label = name), repel = TRUE,
point.padding = unit(0.2, "lines")) +
theme_void()

可以很明显看到,因为中文分词的原因,联系最紧密的词往往都是重合度很高的词,如“朋友-男朋友-女朋友-朋友圈” 我们把这类词过滤掉再看看:
bigrams_filtered %>%
filter(!(word1 == "" | word2 == "")) %>%
filter(!str_detect(word1, word2)) %>%
filter(!str_detect(word2, word1)) %>%
count(word1, word2, sort = TRUE) %>%
filter(n > 80) %>%
graph_from_data_frame() %>%
ggraph(layout = "fr") +
geom_edge_link(aes(edge_alpha = n, edge_width = n), edge_colour = "cyan4") +
geom_node_point(size = 5) +
geom_node_text(aes(label = name), repel = TRUE,
point.padding = unit(0.2, "lines")) +
theme_void()

主题建模
我们探索一下,咪蒙的文章可以分为哪些主题,这里采用LDA建模。
Warning message:
In strsplit(code, "\n", fixed = TRUE) :
input string 1 is invalid in this locale
library(topicmodels)
mimeng_lda <- LDA(mimeng_dtm, k = 4, control = list(seed = 2018))
tidy_lda <- tidy(mimeng_lda)
tidy_lda
beta对应这个词在该文档中生成该主题中的概率,即这个词属于这个主题的概率。 我们再来看看每个主题排名前十的词汇:
top_terms <- tidy_lda %>%
group_by(topic) %>%
top_n(10, beta) %>%
ungroup() %>%
arrange(topic, -beta)
top_terms
画出每个主题排名前十的词汇
top_terms %>%
mutate(term = reorder(term, beta)) %>%
group_by(topic, term) %>%
arrange(desc(beta)) %>%
ungroup() %>%
mutate(term = factor(paste(term, topic, sep="__"),
levels = rev(paste(term, topic, sep = "__")))) %>%
ggplot(aes(term, beta, fill = as.factor(topic))) +
geom_col(show.legend = FALSE) +
coord_flip() +
scale_x_discrete(labels = function(x) gsub("__.+$", "", x)) +
labs(title = "每个LDA主题中排名前十的词汇",
x = NULL, y = expression(beta)) +
facet_wrap(~ topic, ncol = 2, scales = "free")

第一个主题貌似是讲恋爱的, 第二个主题像是说孩子的, 第三个主题则是谈工作的, 第四个主题就是说妈妈的。 以上四个主题除了第二个的概率高一点,其余的概率都比较第,主题不是很清晰。
结语
这篇分析算是我学完《Text Mining with R》后的一次独立尝试,先后对词频、情感、主题进行了探索式分析。 总得来说,咪蒙的文章具有简短易读、故事性强等特点,涉及的话题为女性关注的焦点问题。 通过这次实践,也让自己对使用R语言进行文本分析有了进一步的提高。但整个过程依然存在不少问题,特别是中文文本的分词和情感分析,相较英语有着很大的分析难度,一方面是由于汉语言书写形式导致的分词困难,另一个就是中文自然语言处理相关的基础设施很不完善,比如情感语料库。希望中文的自然语言处理能越做越好,越做越开放,让普通人都能参与进来。 下次准备分析小马宋,你们说好不好~
LS0tDQp0aXRsZTogIuWSquiSmeaWh+eroOiAgyINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojIOWSquiSmeaWh+eroOiAgw0KDQrovb3lhaXlv4XopoHnmoTliIbmnpDljIUNCg0KYGBge3IgZWNobz1UUlVFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKSAjIOaVtOa0geaVsOaNruahhuaTjeS9nA0KbGlicmFyeSh0aWR5dGV4dCkgIyDmlbTmtIHmlofmnKzmk43kvZwNCmBgYA0KDQojIyDovb3lhaXmlofmnKzmlbDmja4NCg0K5YWI6L295YWl5oiR6YCa6L+HcHl0aG9u54is5Y+W5LiL5p2l55qE5paH5pysDQpwcy5S6K+t6KiA5LiN6IO95YOPcHl0aG9u5LiA5qC35Zyo5a2X56ym5Liy5YmN6Z2i5Yqg5LiKYHJg5L+u6aWw56ym77yM5Zug5Li65oiR55qE57O757uf5pivd2luZG93c++8jOaJgOS7peavj+asoemDveimgeWkjeWItui3r+W+hOWQju+8jOWcqOavj+S4qmBcYOWQjumdouWGjeaJi+WKqOaPkuWFpeS4gOS4qmBcYO+8jOS4jeefpemBk+acieayoeacieS6uuacieabtOWlveeahOWKnuazlQ0KYGBge3J9DQp0ZXh0X3BhdGggPSAiQzpcXFVzZXJzXFxsaHNcXERlc2t0b3BcXFRFTVBcXE1hY2hpbmUgTGVhcm5pbmdcXERhdGEgU2V0c1xcbWltZW5nXFxtaW1lbmdfdGV4dF9jdXRfc2VhcmNoLmNzdiINCm1pbWVuZyA8LSByZWFkX2RlbGltKHRleHRfcGF0aCwgZGVsaW0gPSAiXHQiKQ0KbWltZW5nDQpgYGANCuWOn+Wni+aWh+acrOS4gOWFseS4pOWIl++8jHRleHTlkox0aXRsZeOAgg0KdGl0bGXooajnpLrmoIfpopjjgIINCnRleHTmmK/lkqrokpnmlofnq6DkuK3nmoTmr4/kuIDmrrXor53vvIzmiJHkuovlhYjlnKhweXRob27kuK3nlKhqaWViYeWIhuivjeWvueavj+auteivnemDvei/m+ihjOS6huWIhuivjeOAgg0KDQojIyDnroDljZXor43popHliIbmnpANCg0K5oiR5Lus5YWI566A5Y2V57uf6K6h5LiA5LiL5q+P56+H5paH56ug55qE5q616JC95pWw6YeP77yaDQpgYGB7cn0NCm1pbWVuZ19wIDwtIG1pbWVuZyAlPiUNCiAgZ3JvdXBfYnkodGl0bGUpICU+JQ0KICBzdW1tYXJpemUocF9udW0gPSBuKCkpICU+JQ0KICBhcnJhbmdlKGRlc2MocF9udW0pKQ0KbWltZW5nX3ANCmBgYA0K5q616JC95pWw6YeP5pyA5aSa55qE5paH56ug5piv44CKODDmnKzmnIDmo5LnmoTlubzlhL/nu5jmnKzmjqjojZDjgIssMzM55q615YKy6KeG576k6ZuE77yM55yL5qCH6aKY5YOP56+H6L2v5paH77yM5oiR5om+5Yiw5Y6f5paH55yL5LqG5LiA5LiL77yM5paH56ug5LuL57uN5LqGODDkuKrnu5jmnKzvvIzmr4/kuKrnu5jmnKzkuIvpnaLphY3kuobkuIDmrrXku4vnu43vvIzmiYDku6XmmK/lkqrokpnmnIDplb/nmoTmlofnq6DkuobvvIzov57lpbnoh6rlt7HkuZ/lnKjmlofnq6DkuK3lkJDmp73oh6rlt7Hlhpnkuobnr4cx5LiH5a2X55qE6ZW/5paH44CCDQrmiJHku6zmiormrrXokL3mlbDph4/nmoTpopHnjofliIbluIPnm7Tmlrnlm77nlLvlh7rmnaXvvIznnqfkuIDnnqfmlbTkvZPnmoTliIbluIPmmK/llaXmoLfvvJoNCmBgYHtyfQ0KbWltZW5nX3AgJT4lDQogIGdncGxvdChhZXMocF9udW0gKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjApICsgDQogIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSBtZWFuKG1pbWVuZ19wJHBfbnVtW21pbWVuZ19wJHBfbnVtIDwgMzM5XSkpLCBzaXplID0gMSwgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAyKSArDQogIGxhYnMoeCA9ICLmlofnq6DmrrXokL3mlbDph48iLA0KICAgICAgIHkgPSAi56+H5pWwIikNCmBgYA0K5Yio5Y675o6o6I2Q57uY5pys55qE6YKj54mH5paH56ug77yM5ZKq6JKZ5paH56ug5q616JC95pWw6YeP5YiG5biD5pW05L2T5piv5b6I5o6l6L+R5q2j5oCB5YiG5biD55qE77yM5bmz5Z2H5p2l55yL5LiA56+H5paH56ug5aSn5qaCOTDmrrXmmK/mnIDluLjop4HnmoTplb/luqbjgIINCg0K5o6l5LiL5p2l5oiR5Lus5bCG5q+P5q616K+d6L2s5YyW5Li65Y2V5Liq6K+N44CCDQpgYGB7cn0NCm1pbWVuZ193b3JkIDwtIG1pbWVuZyAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KQ0KbWltZW5nX3dvcmQNCmBgYA0KDQrkuLrmr4/nr4fmlofnq6Dnu5/orqHor43popHvvJoNCmBgYHtyfQ0Kd29yZHMgPC0gbWltZW5nX3dvcmQgJT4lDQogIGNvdW50KHRpdGxlLCB3b3JkLCBzb3J0ID0gVFJVRSkNCndvcmRzDQpgYGANCuaIkeS7rOaMkemAiemikeaVsOacgOmrmOeahOivje+8jOeUu+S4quadoeW9ouWbvueci+S4gOeci++8mg0KYGBge3J9DQp3b3JkcyAlPiUNCiAgY291bnQod29yZCwgd3QgPSBuLCBzb3J0ID0gVFJVRSkgJT4lIA0KICB0b3BfbigxMCwgbm4pICU+JQ0KICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbm4pKSAlPiUNCiAgZ2dwbG90KGFlcyh3b3JkLCBubikpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBsYWJzKHggPSAi6K+N5p2hIiwNCiAgICAgICB5ID0gTlVMTCkgICsNCiAgY29vcmRfZmxpcCgpDQpgYGANCg0K5Y+v5Lul55yL5Yiw77yM6K+N5p2h5Lit5Ye6546w5LqG5b6I5aSa5oSP5LmJ5LiN5aSn55qE6K+N5p2h77yM5oiR5L2g5LuWL+WlueOAgeeahOOAgeS6huS5i+exu+eahO+8jOS9huWFtuWunuWSquiSmeaThemVv+S7peWkp+eZveivneeahOWPo+WQu+S7peexu+S8vOWvueivneeahOaWueW8j+e8luWGmeaWh+eroO+8jOeJueWIq+aYr+agh+mimO+8jOaJgOS7peWHuueOsOW+iOWkmueahOS6uuensOS7o+ivjeWwseW+iOato+W4uOS6huOAgg0KDQrmiJHku6zovb3lhaXlgZzmraLor43vvIznnIvnnIvmjpLpmaTmjonlgZzmraLor43lkI7nmoTlkqrokpnmlofnq6DnmoTor43popHlpoLkvZXjgIINCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpteV9zdG9wX3dvcmRzIDwtIHJlYWRfY3N2KCJDOlxcVXNlcnNcXGxoc1xcRGVza3RvcFxcVEVNUFxcTWFjaGluZSBMZWFybmluZ1xcRGF0YSBTZXRzXFxtaW1lbmdcXHN0b3B3b3Jkcy5jc3YiKQ0KDQp3b3JkczIgPC0gbWltZW5nX3dvcmQgJT4lIA0KICBhbnRpX2pvaW4obXlfc3RvcF93b3JkcykgJT4lDQogIGNvdW50KHRpdGxlLCB3b3JkLCBzb3J0ID0gVFJVRSkNCndvcmRzMg0KYGBgDQrljrvpmaTmjonlgZzmraLor43kuYvlkI7lsLHlvojmnInmhI/mgJ3kuobvvIzmiJHku6znnIvlpLTljYHmnaHmlbDmja7vvIzmrKHmlbDmnIDlpJrnmoTljJfjgIHlrr3jgIHmnKjjgIHlm77jgIHmpLDlrZDlkozokIzpg73mmK/lr7nlupTmlofnq6DkuK3nmoTkurrlkI3vvIzogIzlranlrZDlkozlh7rniYjnpL7mmK/ot5/kuLvpopjnm7jlhbPjgILlj6/op4HlkqrokpnlvojllpzmrKLorrLmlYXkuovvvIzogIzlh7rnjrDpopHnjofmnIDpq5jnmoTor43nkIbmiYDlvZPnhLbkuZ/lsLHmmK/lkITnr4fmlofnq6DkuK3nmoTnlLflpbPkuLvop5LkuobjgIINCg0K57uf6K6h5q+P5Liq6K+N5Zyo5YWo6YOo5paH56ug5Lit5Ye6546w55qE6aKR546H77yM5bm255S75Ye66K+N6aKR5pyA6auY55qEMjDkuKrljZXor43nmoTmnaHlvaLlm77vvJoNCmBgYHtyfQ0Kd29yZHMyICU+JQ0KICBjb3VudCh3b3JkLCB3dCA9IG4sIHNvcnQgPSBUUlVFKSAlPiUgDQogIHRvcF9uKDIwLCBubikgJT4lDQogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBubikpICU+JQ0KICBnZ3Bsb3QoYWVzKHdvcmQsIG5uKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGxhYnMoeCA9ICLor43mnaEiLA0KICAgICAgIHkgPSBOVUxMKSAgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0K4oCc6K+04oCd6L+Z5Liq6K+N55qE6aKR57mB5bqm55yf5piv6L+c6LaF5YW25LuW5LiA5YiH5Yqo6K+N77yM6L+e5Lit5Zu95Lq65pyA54ix55qE4oCc5ZCD4oCd5a2X5Zyo5YW26Z2i5YmN5Lmf6KaB5rGX6aKc44CCDQrigJzmnIvlj4vigJ3nrpfmmK/lhajmlofpopHmrKHmnIDpq5jnmoTlkI3or43kuobvvIzlnKjlkqrokpnnmoTmlofnq6Dph4zmmK/kuKrkuI3lj6/mkrzliqjnmoTop5LoibLvvIzlhYXliIbkvZPnjrDkuoblpbnigJzmiJHmnInkuIDkuKrmnIvlj4vigJ3lnovmlYXkuovnmoTmnKzoibLvvIzmr5Xnq5/lpbPnlJ/pg73niLHot5/mnIvlj4vogYrlpKnjgIHogYrlhavljabjgIINCue0p+aOpeedgOKAnOaci+WPi+KAnemrmOmikeWHuueOsOeahOWQjeivjeWwseaYr+KAnOWtqeWtkOKAneS6hu+8jOaenOeEtuWtqeWtkOaYr+Wls+S6uuW/g+S4remHjeimgeeahOS4u+mimOS5i+S4gO+8jOmZpOS6huKAnOWtqeWtkOKAne+8jOKAnOWWnOasouKAneOAgeKAnOeIseKAneOAgeKAnOS5sOKAneOAgeKAnOmSseKAneOAgeKAnOWmiOKAnei/meS6m+ivjeS5n+aYr+mdnuW4uOeahOaZrumBje+8jOi3n+eOsOWunuS4reWls+eUn+eahOivnemimOWBj+WlveS5n+aYr+mHjeWQiOeahOOAguaIkeinieW+l+acieaEj+aAneeahOaYr+eUt+S6uui/meenjee+pOS9k+WAkuaYr+WSquiSmeaWh+eroOS4reeahOeogOacieeUn+eJqe+8jOWMheaLrOKAneiAgeWFrOKAnOOAgeKAneeIuOKAnOetie+8jOWPr+ingeWSquiSmeWvueeUt+S6uuW5tuS4jeW+heingeOAguaIkeS7rOeci+S4gOS4i+i3n+eUt+aAp+ebuOWFs+eahOivjeaxh+WHuueOsOeahOmikeeOh++8mg0KYGBge3J9DQptZW4gPC0gYygi55S3IiwgIueUt+S6uiIsICLnlLfmnIvlj4siLCAi55S35Y+LIiwgIuWJjeeUt+WPiyIsICLliY3ku7siLCLogIHlhawiLCAi5LiI5aSrIiwgIuWJjeWkqyIsIuWEv+WtkCIsICLniLgiLCAi54i454i4IiwgIueItuS6siIsICLniLkiLCAi5rij55S3IikNCg0Kd29yZHNfbWVuIDwtIHdvcmRzMiAlPiUNCiAgZmlsdGVyKHdvcmQgJWluJSBtZW4pICU+JQ0KICBjb3VudCh3b3JkLCB3dCA9IG4sIHNvcnQgPSBUUlVFKQ0KICANCg0Kd29yZHNfbWVuDQpgYGANCui/meS6m+ivjeaxh++8jOi3n+WJjemdoumikeasoeS4iuWNg+eahOivnemimOavlOi1t+adpe+8jOecn+aYr+S5neeJm+S4gOavm+OAgueJueWIq+aYr+KAnOWJjeWkq+KAne+8jOWwpOWFtuS4jeaLm+S6uuW+heingX4NCg0KDQrnlLvkuKror43kupHnnIvkuIDkuIvvvJoNCmBgYHtyfQ0KbGlicmFyeSh3b3JkY2xvdWQyKQ0Kd29yZHMyICU+JQ0KICBjb3VudCh3b3JkLCB3dCA9IG4sIHNvcnQgPSBUUlVFKSAlPiUgDQogIGhlYWQoMTAwKSAlPiUNCiAgd29yZGNsb3VkMihjb2xvciA9ICJyYW5kb20tbGlnaHQiLCBiYWNrZ3JvdW5kQ29sb3IgPSAiZ3JleSIpDQpgYGANCg0K5oiR5Lus5pyA5ZCO55yL5LiA5LiL5qC55o2udGYtaWRm566X5Ye65p2l55qE6K+N5rGH5o6S5ZCN77yaDQpgYGB7cn0NCndvcmRzX3RmX2lkZiA8LSB3b3JkczIgJT4lDQogIGJpbmRfdGZfaWRmKHdvcmQsIHRpdGxlLCBuKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpDQp3b3Jkc190Zl9pZGYNCmBgYA0K57uT5p6c5YW25a6e5Lmf5beu5LiN5aSa77yM5o6S5ZCN6Z2g5YmN55qE5Li76KaB5Lmf5piv5Lq65ZCN77yM5Zug5Li65YiG6K+N5bel5YW35rKh5Yqe5rOV5q2j56Gu6K+G5Yir5Ye65Lq65ZCN77yM6ICM5bCG5Lq65ZCN5YiH5YiG5oiQ5Y2V5Liq55qE5a2X77yM5omA5Lul5a+86Ie05LiA5Lqb5Lq65ZCN55qEdGbigJRpZGbnmoTlgLzlvojkvY7jgIINCg0KIyMg5paH56ug5oOF5oSf5YiG5p6QDQoNCui9veWFpeaDheaEn+ivjeWFuO+8jOacrOaWh+mHh+eUqOWkp+i/nueQhuW3peWkp+WtpuaDheaEn+ivjeaxh+acrOS9k+W6k++8iOeJueWIq+m4o+iwon7vvInvvJoNCmBgYHtyfQ0KbGlicmFyeShyZWFkeGwpDQpzZW5fcGF0aCA9ICJDOlxcVXNlcnNcXGxoc1xcRGVza3RvcFxcVEVNUFxcTWFjaGluZSBMZWFybmluZ1xcRGF0YSBTZXRzXFxtaW1lbmdcXOaDheaEn+ivjeaxh+acrOS9ky54bHN4Ig0KbXlfc2VudGltZW50cyA8LSByZWFkX3hsc3goc2VuX3BhdGgpDQpteV9zZW50aW1lbnRzDQpgYGANCuWFtuS4reaegeaAp+ihqOekuuikkui0rO+8jDDku6PooajkuK3mgKfvvIwx5Luj6KGo6KSS5LmJ77yMMuS7o+ihqOi0rOS5ie+8jDPku6PooajkuI3noa7lrprjgIINCuaIkeS7rOWIqeeUqOivpeaDheaEn+ivjeaxh+aWh+acrOWvueWSquiSmeeahOaWh+eroOi/m+ihjOaDheaEn+aJk+WIhu+8mg0KYGBge3J9DQp3b3JkX3NlbnRpbWVudHMgPC0gd29yZHMyICU+JQ0KICBpbm5lcl9qb2luKG15X3NlbnRpbWVudHMgJT4lIHNlbGVjdChg6K+N6K+tYCwgYOaegeaAp2ApLCBieSA9IGMod29yZCA9ICLor43or60iKSkgJT4lDQogIGZpbHRlcihg5p6B5oCnYCAhPSAzKSAlPiUNCiAgbXV0YXRlKHNlbnRpbWVudCA9IGNhc2Vfd2hlbigNCiAgICBg5p6B5oCnYCA9PSAxIH4gMSwNCiAgICBg5p6B5oCnYCA9PSAyIH4gLTEsDQogICAgVFJVRSB+IDANCiAgKSkNCg0Kd29yZF9zZW50aW1lbnRzDQpgYGANCuaIkeS7rOe7n+iuoeS4gOS4i+aAu+S9k+S4iu+8jOS4jeWQjOivjeaxh+WvueaWh+eroOaDheaEn+eahOi0oeeMruW6pg0KYGBge3J9DQp0b3RhbF9jb250cmlidXRpb24gPC0gd29yZF9zZW50aW1lbnRzICU+JQ0KICBjb3VudCh3b3JkLCB3dCA9IG4gKiBzZW50aW1lbnQsIHNvcnQgPSBUUlVFKSAlPiUNCiAgdHJhbnNtdXRlKHdvcmQgPSB3b3JkLCBjb250cmlidXRpb24gPSBubikNCnRvdGFsX2NvbnRyaWJ1dGlvbg0KYGBgDQrmiJHku6zmjJHlh7rotKHnjK7luqbmnIDpq5jnmoQ0MOS4quivjeaxh+eci+S4gOS4i++8mg0KYGBge3IgZmlnLmhlaWdodD0xMn0NCnRvdGFsX2NvbnRyaWJ1dGlvbiAlPiUNCiAgYXJyYW5nZShkZXNjKGFicyhjb250cmlidXRpb24pKSkgJT4lDQogIGhlYWQoNDApICU+JQ0KICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgY29udHJpYnV0aW9uKSkgJT4lDQogIGdncGxvdChhZXMod29yZCwgY29udHJpYnV0aW9uLCBmaWxsID0gY29udHJpYnV0aW9uID4gMCkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBsYWJzKHggPSAi6K+N5rGHIiwNCiAgICAgICB5ID0gIuaDheaEn+i0oeeMriIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwNCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkNCmBgYA0K5b6I5piO5pi+77yM5oOF5oSf5YiG5p6Q55qE57uT5p6c6KKr4oCc5pyL5Y+L4oCd44CB4oCc5Zac5qyi4oCd5pW05Lik5Liq6K+N57uZ5bim5YGP5LqG44CCDQrpppblhYjvvIzigJzmnIvlj4vigJ3mm7TlpJrlj6rmmK/kuIDkuKrmsqHmnInopJLotKzlkKvkuYnnmoTkuLvor63miJbogIXosJPor63vvIzmr5TlpoLigJzmiJHnmoTkuIDkuKrmnIvlj4vigJ3jgIINCuiAjOKAnOWWnOasouKAnei/meS4quivjeabtOWkmueahOaYr+WcqOiuuui/sOafkOS4qumXrumimO+8jOavlOWmguKAnOS9oOWWnOasouS4jeWWnOasouafkOS4quS6uuKAneOAgeKAnOS9oOWWnOS4jeWWnOasoui/meS4quW3peS9nOKAneetieOAgg0KDQrmiJHku6znlLvkuKrljZXor43nvZHnu5znnIvnnIvvvIwNCummluWFiOWwhuaWh+acrOaLhuWIhuaIkOS6jOWFg+e7hO+8mg0KYGBge3J9DQp0b2tlbl8yZ3JhbXMgPC0gZnVuY3Rpb24oc3RyaW5nKSB7DQogIHN0cmluZzEgPC0gc3RyX3NwbGl0KHN0cmluZywgIiAiKVtbMV1dDQogIHN0cmluZzIgPC0gbGVhZChzdHJpbmcxKQ0KICBzdHJpbmcyW2lzLm5hKHN0cmluZzIpXSA8LSAiIg0KICANCiAgc3RyX2Moc3RyaW5nMSwgc3RyaW5nMiwgc2VwID0gIiAiKQ0KfQ0KDQpnZW5fMmdyYW1zIDwtIGZ1bmN0aW9uKHgpIG1hcCh4LCB0b2tlbl8yZ3JhbXMpDQoNCm1pbWVuZ19iaWdyYW1zIDwtIG1pbWVuZyAlPiUNCiAgdW5uZXN0X3Rva2VucyhiaWdyYW0sIHRleHQsIHRva2VuID0gZ2VuXzJncmFtcykNCg0KYmlncmFtc19zZXBhcmF0ZWQgPC0gbWltZW5nX2JpZ3JhbXMgJT4lDQogIHNlcGFyYXRlKGJpZ3JhbSwgYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKQ0KDQpiaWdyYW1zX2ZpbHRlcmVkIDwtICBiaWdyYW1zX3NlcGFyYXRlZCAlPiUNCiAgZmlsdGVyKCF3b3JkMSAlaW4lIG15X3N0b3Bfd29yZHMkd29yZCkgJT4lDQogIGZpbHRlcighd29yZDIgJWluJSBteV9zdG9wX3dvcmRzJHdvcmQpDQoNCmJpZ3JhbXNfZmlsdGVyZWQNCmBgYA0K5oiR5Lus55yL55yL5ZKM5pyL5Y+L5pCt6YWN55qE6K+N6K+t5pyJ5ZOq5Lqb77yaDQpgYGB7cn0NCmJpZ3JhbXNfZmlsdGVyZWQgJT4lDQogIGZpbHRlcih3b3JkMSA9PSAi5pyL5Y+LIikgJT4lDQogIGZpbHRlcighc3RyX2RldGVjdCh3b3JkMiwgIuaci+WPiyIpKSAlPiUNCiAgY291bnQod29yZDEsIHdvcmQyLCBzb3J0ID0gVFJVRSkNCmBgYA0KIuaci+WPi+ivtCLov5nkuKrnu4TlkIjmmK/popHnjofmnIDpq5jnmoTjgIINCg0K5oiR5Lus5YaN5oqK5ZCM5pe25Ye6546w5qyh5pWw6LaF6L+HMTAw5qyh55qE6K+N5rGH6IGU57O75Zu+55S75Ye65p2l77yaDQpgYGB7ciBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRSwgcGFnZWQucHJpbnQ9VFJVRX0NCmxpYnJhcnkoaWdyYXBoKQ0KbGlicmFyeShnZ3JhcGgpDQoNCnNldC5zZWVkKDIwMTgpDQpiaWdyYW1zX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIoISh3b3JkMSA9PSAiIiB8IHdvcmQyID09ICIiKSkgJT4lDQogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpICU+JQ0KICBmaWx0ZXIobiA+IDEwMCkgJT4lDQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQ0KICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4sIGVkZ2Vfd2lkdGggPSBuKSwgZWRnZV9jb2xvdXIgPSAiY3lhbjQiKSArDQogIGdlb21fbm9kZV9wb2ludChzaXplID0gNSkgKw0KICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFLA0KICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gdW5pdCgwLjIsICJsaW5lcyIpKSArDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQrlj6/ku6XlvojmmI7mmL7nnIvliLDvvIzlm6DkuLrkuK3mlofliIbor43nmoTljp/lm6DvvIzogZTns7vmnIDntKflr4bnmoTor43lvoDlvoDpg73mmK/ph43lkIjluqblvojpq5jnmoTor43vvIzlpoLigJzmnIvlj4st55S35pyL5Y+LLeWls+aci+WPiy3mnIvlj4vlnIjigJ0NCuaIkeS7rOaKiui/meexu+ivjei/h+a7pOaOieWGjeeci+eci++8mg0KYGBge3J9DQpiaWdyYW1zX2ZpbHRlcmVkICU+JQ0KICBmaWx0ZXIoISh3b3JkMSA9PSAiIiB8IHdvcmQyID09ICIiKSkgJT4lDQogIGZpbHRlcighc3RyX2RldGVjdCh3b3JkMSwgd29yZDIpKSAlPiUNCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KHdvcmQyLCB3b3JkMSkpICU+JQ0KICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUNCiAgZmlsdGVyKG4gPiA4MCkgJT4lDQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQ0KICBnZ3JhcGgobGF5b3V0ID0gImZyIikgKw0KICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IG4sIGVkZ2Vfd2lkdGggPSBuKSwgZWRnZV9jb2xvdXIgPSAiY3lhbjQiKSArDQogIGdlb21fbm9kZV9wb2ludChzaXplID0gNSkgKw0KICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFLA0KICAgICAgICAgICAgICAgICBwb2ludC5wYWRkaW5nID0gdW5pdCgwLjIsICJsaW5lcyIpKSArDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQoNCiMjIOS4u+mimOW7uuaooQ0KDQrmiJHku6zmjqLntKLkuIDkuIvvvIzlkqrokpnnmoTmlofnq6Dlj6/ku6XliIbkuLrlk6rkupvkuLvpopgs6L+Z6YeM6YeH55SoTERB5bu65qih44CCDQpgYGB7cn0NCm1pbWVuZ19kdG0gPC0gd29yZHMyICU+JQ0KICBmaWx0ZXIodGl0bGUgIT0gIjgw5pys5pyA5qOS55qE5bm85YS/57uY5pys5o6o6I2QIikgJT4lIA0KICBmaWx0ZXIod29yZCAhPSAi6K+0IikgJT4lICMg4oCc6K+04oCd6L+Z5Liq6K+N5aSq6aKR57mB5LqG77yM5oiR5Lus5oqK5a6D6L+H5ruk5o6JDQogIGNhc3RfZHRtKHRpdGxlLCB3b3JkLCBuKQ0KDQpsaWJyYXJ5KHRvcGljbW9kZWxzKQ0KDQptaW1lbmdfbGRhIDwtIExEQShtaW1lbmdfZHRtLCBrID0gNCwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDIwMTgpKQ0KdGlkeV9sZGEgPC0gdGlkeShtaW1lbmdfbGRhKQ0KdGlkeV9sZGENCmBgYA0KDQpiZXRh5a+55bqU6L+Z5Liq6K+N5Zyo6K+l5paH5qGj5Lit55Sf5oiQ6K+l5Li76aKY5Lit55qE5qaC546H77yM5Y2z6L+Z5Liq6K+N5bGe5LqO6L+Z5Liq5Li76aKY55qE5qaC546H44CCDQrmiJHku6zlho3mnaXnnIvnnIvmr4/kuKrkuLvpopjmjpLlkI3liY3ljYHnmoTor43msYfvvJoNCmBgYHtyfQ0KdG9wX3Rlcm1zIDwtIHRpZHlfbGRhICU+JQ0KICBncm91cF9ieSh0b3BpYykgJT4lDQogIHRvcF9uKDEwLCBiZXRhKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBhcnJhbmdlKHRvcGljLCAtYmV0YSkNCg0KdG9wX3Rlcm1zDQpgYGANCueUu+WHuuavj+S4quS4u+mimOaOkuWQjeWJjeWNgeeahOivjeaxhw0KYGBge3IgZmlnLmhlaWdodD04fQ0KdG9wX3Rlcm1zICU+JQ0KICBtdXRhdGUodGVybSA9IHJlb3JkZXIodGVybSwgYmV0YSkpICU+JQ0KICBncm91cF9ieSh0b3BpYywgdGVybSkgJT4lDQogIGFycmFuZ2UoZGVzYyhiZXRhKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgbXV0YXRlKHRlcm0gPSBmYWN0b3IocGFzdGUodGVybSwgdG9waWMsIHNlcD0iX18iKSwNCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSByZXYocGFzdGUodGVybSwgdG9waWMsIHNlcCA9ICJfXyIpKSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHRlcm0sIGJldGEsIGZpbGwgPSBhcy5mYWN0b3IodG9waWMpKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gZnVuY3Rpb24oeCkgZ3N1YigiX18uKyQiLCAiIiwgeCkpICsNCiAgbGFicyh0aXRsZSA9ICLmr4/kuKpMREHkuLvpopjkuK3mjpLlkI3liY3ljYHnmoTor43msYciLA0KICAgICAgIHggPSBOVUxMLCB5ID0gZXhwcmVzc2lvbihiZXRhKSkgKw0KICBmYWNldF93cmFwKH4gdG9waWMsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZSIpDQpgYGANCuesrOS4gOS4quS4u+mimOiyjOS8vOaYr+iusuaBi+eIseeahO+8jA0K56ys5LqM5Liq5Li76aKY5YOP5piv6K+05a2p5a2Q55qE77yMDQrnrKzkuInkuKrkuLvpopjliJnmmK/osIjlt6XkvZznmoTvvIwNCuesrOWbm+S4quS4u+mimOWwseaYr+ivtOWmiOWmiOeahOOAgg0K5Lul5LiK5Zub5Liq5Li76aKY6Zmk5LqG56ys5LqM5Liq55qE5qaC546H6auY5LiA54K577yM5YW25L2Z55qE5qaC546H6YO95q+U6L6D56ys77yM5Li76aKY5LiN5piv5b6I5riF5pmw44CCDQoNCiMjIOe7k+ivrQ0K6L+Z56+H5YiG5p6Q566X5piv5oiR5a2m5a6M44CKVGV4dCBNaW5pbmcgd2l0aCBS44CL5ZCO55qE5LiA5qyh54us56uL5bCd6K+V77yM5YWI5ZCO5a+56K+N6aKR44CB5oOF5oSf44CB5Li76aKY6L+b6KGM5LqG5o6i57Si5byP5YiG5p6Q44CCDQrmgLvlvpfmnaXor7TvvIzlkqrokpnnmoTmlofnq6DlhbfmnInnroDnn63mmJPor7vjgIHmlYXkuovmgKflvLrnrYnnibnngrnvvIzmtonlj4rnmoTor53popjkuLrlpbPmgKflhbPms6jnmoTnhKbngrnpl67popjjgIINCumAmui/h+i/measoeWunui3te+8jOS5n+iuqeiHquW3seWvueS9v+eUqFLor63oqIDov5vooYzmlofmnKzliIbmnpDmnInkuobov5vkuIDmraXnmoTmj5Dpq5jjgILkvYbmlbTkuKrov4fnqIvkvp3nhLblrZjlnKjkuI3lsJHpl67popjvvIznibnliKvmmK/kuK3mlofmlofmnKznmoTliIbor43lkozmg4XmhJ/liIbmnpDvvIznm7jovoPoi7Hor63mnInnnYDlvojlpKfnmoTliIbmnpDpmr7luqbvvIzkuIDmlrnpnaLmmK/nlLHkuo7msYnor63oqIDkuablhpnlvaLlvI/lr7zoh7TnmoTliIbor43lm7Dpmr7vvIzlj6bkuIDkuKrlsLHmmK/kuK3mlofoh6rnhLbor63oqIDlpITnkIbnm7jlhbPnmoTln7rnoYDorr7mlr3lvojkuI3lrozlloTvvIzmr5TlpoLmg4XmhJ/or63mlpnlupPjgILluIzmnJvkuK3mlofnmoToh6rnhLbor63oqIDlpITnkIbog73otorlgZrotorlpb3vvIzotorlgZrotorlvIDmlL7vvIzorqnmma7pgJrkurrpg73og73lj4LkuI7ov5vmnaXjgIINCuS4i+asoeWHhuWkh+WIhuaekOWwj+mprOWui++8jOS9oOS7rOivtOWlveS4jeWlvX4NCg==