Sys.setlocale(category = "LC_ALL", locale = "zh_TW.UTF-8") # 避免中文亂碼(Windows系統可將這行註解)

1. 動機與分析目的

由於最近台灣的疫情延燒,使得疫苗的討論一直居高不下,而對於疫苗所衍生的多種面向,像是政治、情緒等,都是我們想深入了解的,因此本組針對內容做以下分析:情緒分析,文字雲,正負面詞的詞頻,社群網路圖..等,深入研究在不同時間、意見領袖或是新聞媒體對於疫苗的看法以及民眾對不同疫苗的正負面情緒。

2. 初步探索資料集

安裝需要的packages和套件載入

packages = c("readr", "dplyr", "stringr", "jiebaR", "tidytext", "NLP", "readr", "tidyr", "ggplot2", "ggraph", "igraph", "scales", "reshape2", "widyr","wordcloud2","wordcloud","widyr","ggraph")
existing = as.character(installed.packages()[,1])
for(pkg in packages[!(packages %in% existing)]) install.packages(pkg)
library(readr)
library(data.table)
library(ggplot2)
library(dplyr)
library(jiebaR)
library(tidytext)
library(stringr)
library(tm)
library(topicmodels)
library(purrr)
require(RColorBrewer)
library(igraph)
library(tidyr)
library(scales)
library(wordcloud2)
library(wordcloud)
library(reshape2)
library(widyr)
library(ggraph)

資料描述

  • 資料來源: 透過中山管院文字分析平台在PTT八卦版搜 2021-05-11 ~ 2021-06-11 所有文章與留言
  • 資料集:final_articleMetaData.csv、final_articleReviews.csv
  • 關鍵字:疫苗
  • 文章篇數:總共 11588篇
# vaccine <- read_csv("C:/Users/ooolivia/Desktop/NSYSU/109(2)/sma/final_prj/k/final_articleMetaData.csv") %>%
#   mutate(sentence=gsub("[\n]{2,}", "。", sentence)) %>%
#   mutate(sentence=gsub("\n", "", sentence)) %>%
#   mutate(sentence=gsub("http(s)?[-:\\/A-Za-z0-9\\.]+", " ", sentence))
# 
# vaccine
# 
# reviews <- read_csv("C:/Users/ooolivia/Desktop/NSYSU/109(2)/sma/final_prj/k/final_articleReviews.csv")
# reviews = reviews%>%
#   mutate(cmtContent=gsub("[\n]{2,}", "。", cmtContent)) %>%
#   mutate(cmtContent=gsub("\n", "", cmtContent)) %>%
#   mutate(cmtContent=gsub("http(s)?[-:\\/A-Za-z0-9\\.]+", " ", cmtContent))
# 
# reviews

load("vaccine_preprocess.rdata")
# vaccine <- vaccine %>%
#   mutate(sentence=gsub("媒體來源|記者署名|完整新聞標題|完整新聞內文|完整新聞連結|(或短網址)|備註|備註請放最後面|違者新聞文章刪除", "", sentence))

發文數以及留言數

  • 發文數:總共 11588 篇
  • 留言數:總共 860995 篇
nrow(vaccine)
[1] 11588
nrow(reviews)
[1] 860995

疫苗討論趨勢圖

vaccine %>%
  mutate(artDate = as.Date(artDate)) %>%
  group_by(artDate) %>%
  summarise(count = n())%>%
  ggplot(aes(artDate,count))+
    geom_line(color="blue")+
    geom_point()


以五月底到六月初討論大幅上升

5/26、5/30、6/10 為高點

下列為三個高點的內容

vaccine %>%
 mutate(artDate = as.Date(artDate)) %>%
 filter(artDate == "2021-05-26") %>%
 head(15)


5/26 國產疫苗研發和採購疫苗為主

vaccine %>%
  mutate(artDate = as.Date(artDate)) %>%
  filter(artDate == "2021-05-30") %>%
  head(15)


5/30 高端疫苗和郭台銘買疫苗為主

vaccine %>%
  mutate(artDate = as.Date(artDate)) %>%
  filter(artDate == "2021-06-10") %>%
  head(15)


6/10 疫苗短缺問題

初始化斷詞器

# # 使用默認參數初始化一個斷詞引擎
# # 先不使用任何的字典和停用詞
# jieba_tokenizer <- worker(user="vaccine.txt", stop_word = "stop_words.txt")
# 
# chi_tokenizer <- function(t) {
#   lapply(t, function(x) {
#     if(nchar(x)>1){
#       tokens <- segment(x, jieba_tokenizer)
#       # 去掉字串長度爲1的詞彙
#       tokens <- tokens[nchar(tokens)>1]
#       return(tokens)
#     }
#   })
# }

斷詞與整理斷詞結果

# # 進行斷詞,並計算各詞彙在各文章中出現的次數
# vaccine_words <- vaccine %>%
#   unnest_tokens(word, sentence, token=chi_tokenizer) %>%
#   filter(!str_detect(word, regex("[0-9]"))) %>%
#   count(artUrl, word, sort = TRUE)
# vaccine_words
# 
# reviews_words <- reviews %>%
#   unnest_tokens(word, cmtContent, token=chi_tokenizer) %>%
#   filter(!str_detect(word, regex("[0-9]"))) %>%
#   count(artUrl, word, sort = TRUE)
# 
# # 把文章和留言的斷詞結果併在一起
# MToken <- vaccine %>% unnest_tokens(word, sentence, token=chi_tokenizer)
# RToken <- reviews %>% unnest_tokens(word, cmtContent, token=chi_tokenizer)
# 
# # 把資料併在一起
data <- rbind(MToken[,c("artDate","artTitle","artUrl", "word")],RToken[,c("artDate","artTitle","artUrl", "word")])
# # 格式化日期欄位
# data$artDate= data$artDate %>% as.Date("%Y/%m/%d")
# 
# # 過濾特殊字元
# data_select = data %>% 
#   filter(!grepl('[[:punct:]]',word)) %>% # 去標點符號
#   filter(!grepl("['^0-9']",word)) %>% # 去英文、數字
#   filter(nchar(.$word)>1) 
#   
# # 算每天不同字的詞頻
# # word_count:artDate,word,count
# word_count <- data_select %>%
#   select(artDate, word) %>%
#   group_by(artDate,word) %>%
#   summarise(count=n()) %>%  # 算字詞單篇總數用summarise
#   filter(count>3) %>%  # 過濾出現太少次的字
#   arrange(desc(count))
#
# save.image(file = "vaccine_preprocess.rdata")

# word_count

準備情緒字典

P <- read_file("./dict/liwc/positive.txt") # 正向字典txt檔
N <- read_file("./dict/liwc/negative.txt") # 負向字典txt檔

#字典txt檔讀進來是一整個字串
typeof(P)
[1] "character"

分割字詞,並將兩個情緒字典併在一起

# 將字串依,分割
# strsplit回傳list , 我們取出list中的第一個元素
P = strsplit(P, ",")[[1]]
N = strsplit(N, ",")[[1]]

# 建立dataframe 有兩個欄位word,sentiments,word欄位內容是字典向量
P = data.frame(word = P, sentiment = "positive")
N = data.frame(word = N, sentiment = "negative")

# 把兩個字典拼在一起
LIWC = rbind(P, N)

# 檢視字典
head(LIWC)

算出每章情緒總和(sentiment_count)

# sentiment_count:artDate,sentiment,count
sentiment_count = data_select %>%
  select(artDate,word) %>%
  inner_join(LIWC) %>% 
  group_by(artDate,sentiment) %>%
  summarise(count=n())  
Joining, by = "word"
`summarise()` has grouped output by 'artDate'. You can override using the `.groups` argument.
sentiment_count

正負情緒比例折線圖

將情緒分數標準化後,可以發現雖然正負面情緒有波動,但大部分正負面情緒各半,約在5/21後負面情緒佔比較高。

sentiment_count %>% 
  # 標準化的部分
  group_by(artDate) %>%
  mutate(ratio = count/sum(count)) %>%
  # 畫圖的部分
  ggplot()+
  geom_line(aes(x=artDate,y=ratio,colour=sentiment))+
  scale_x_date(labels = date_format("%m/%d"),
               limits = as.Date(c('2021-05-11','2021-06-11'))
               )+
  # 加上標示日期的線
  geom_vline(aes(xintercept = as.numeric(artDate[which(sentiment_count$artDate == as.Date('2021-05-30'))
[1]])),colour = "yellow")

我們挑出幾個情緒高點的日期 觀察每日情緒分數,約從5/24號開始議題被大量討論,6/4達到議題高峰,之後就慢慢下降。

# 查看每天的情緒分數排名
sentiment_count %>%
  select(count,artDate) %>%
  group_by(artDate) %>%
  summarise(sum = sum(count)) %>%
  arrange(desc(sum))

2021-05-30 文字雲

民眾對於民進黨的負面看法像是噁心、垃圾等等,高端國產疫苗未到三期,以及郭董買疫苗

# 畫出文字雲
word_count %>% 
  filter(!(word %in% c("疫苗","真的","已經","現在","台灣"))) %>%
  filter(artDate == as.Date('2021-05-30')) %>% 
  select(word,count) %>% 
  group_by(word) %>% 
  summarise(count = sum(count)) %>%
  arrange(desc(count)) %>%
  filter(count>200) %>%   # 過濾出現太少次的字
  wordcloud2()
Adding missing grouping variables: `artDate`

2021-05-30 正負情緒代表字

算出所有字詞的詞頻,找出情緒代表字: + 正面情緒:支持、相信、希望、成功 + 負面情緒:垃圾、問題、噁心、可憐

sentiment_sum_select <- 
word_count %>%
  filter(artDate == as.Date('2021-05-30')) %>% 
    inner_join(LIWC) %>%
    group_by(word,sentiment) %>%
  summarise(
    sum = sum(count)
  ) %>% 
  arrange(desc(sum)) %>%
  data.frame() 
Joining, by = "word"
`summarise()` has grouped output by 'word'. You can override using the `.groups` argument.
sentiment_sum_select   %>%
  top_n(30,wt = sum) %>%
  ungroup() %>% 
  mutate(word = reorder(word, sum)) %>%
  ggplot(aes(word, sum, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(y = "Contribution to sentiment 0530",
       x = NULL) +
  theme(text=element_text(size=14))+
  theme(text = element_text(family = "Heiti TC Light"))+ 
  coord_flip()

歸類正負面文章

之前的情緒分析大部分是全部的詞彙加總,接下來將正負面情緒的文章分開,可以發現文章屬於負面較多。

# 依據情緒值的正負比例歸類文章
article_type = 
  data_select %>%
  inner_join(LIWC) %>% 
  group_by(artUrl,sentiment) %>%
  summarise(count=n()) %>%
  spread(sentiment,count,fill = 0) %>% #把正負面情緒展開,缺值補0
  mutate(type = case_when(positive > negative ~ "positive", 
                             TRUE ~ "negative")) %>%
  data.frame() 
Joining, by = "word"
`summarise()` has grouped output by 'artUrl'. You can override using the `.groups` argument.
  
# 看一下正負比例的文章各有幾篇
article_type %>%
  group_by(type) %>%
  summarise(count = n())

正負情緒文章數量統計圖

# 
article_type_date = left_join(article_type[,c("artUrl", "type")], vaccine[,c("artUrl", "artDate")], by = "artUrl")


article_type_date %>%
  group_by(artDate,type) %>%
  summarise(count = n()) %>%
  ggplot(aes(x = artDate, y = count, fill = type)) + 
  geom_bar(stat = "identity", position = "dodge")+
  scale_x_date(labels = date_format("%m/%d"),
               limits = as.Date(c('2021-05-10','2021-06-07'))
               )
`summarise()` has grouped output by 'artDate'. You can override using the `.groups` argument.


可以看到在約5/24號之後,負面文章增加數量較多。

把正面和負面的文章挑出來,並和斷詞結果合併。

# negative_article:artUrl,word
negative_article <-
article_type %>%
  filter(type=="negative")%>%
  select(artUrl) %>%
  left_join(data_select[,c("artUrl", "word")], by = "artUrl")

# positive_article:artUrl,word
positive_article <-
article_type %>%
  filter(type=="positive")%>%
  select(artUrl) %>%
  left_join(data_select[,c("artUrl", "word")], by = "artUrl")

將資料轉換為Document Term Matrix (DTM)

dtm <- vaccine_words %>% cast_dtm(artUrl, word, n)
dtm
<<DocumentTermMatrix (documents: 11588, terms: 63316)>>
Non-/sparse entries: 583543/733122265
Sparsity           : 100%
Maximal term length: 59
Weighting          : term frequency (tf)
inspect(dtm[1:10,1:10])
<<DocumentTermMatrix (documents: 10, terms: 10)>>
Non-/sparse entries: 50/50
Sparsity           : 50%
Maximal term length: 3
Weighting          : term frequency (tf)
Sample             :
                                                          Terms
Docs                                                       復星 美國 台灣 細胞 疫苗 印度 造謠 中國 bnt the
  https://www.ptt.cc/bbs/Gossiping/M.1621518888.A.D0E.html    0   16    0    6   86    0    0   22   0   0
  https://www.ptt.cc/bbs/Gossiping/M.1621943637.A.3CE.html    0   40    0    0   95    6    0   79   0   0
  https://www.ptt.cc/bbs/Gossiping/M.1622115922.A.734.html   42    4   27    0   61    0    0   16  26   0
  https://www.ptt.cc/bbs/Gossiping/M.1622256534.A.708.html    0    1   46    0   59    0    0   31   1   0
  https://www.ptt.cc/bbs/Gossiping/M.1622419101.A.CE5.html    1   10   55    0   64    0    0   29   0   0
  https://www.ptt.cc/bbs/Gossiping/M.1622471096.A.28E.html    0    7   29    0   75    1    0    6   1   0
  https://www.ptt.cc/bbs/Gossiping/M.1622476666.A.1DB.html    0    7   28    0   70    1    0    6   1   1
  https://www.ptt.cc/bbs/Gossiping/M.1622721456.A.FBF.html    0    6   13    0   65    3    0    3   0   0
  https://www.ptt.cc/bbs/Gossiping/M.1623170954.A.83B.html    0   20   27    0   84    1    0    1   3   0
  https://www.ptt.cc/bbs/Gossiping/M.1623416895.A.409.html    0    0    0   64    1    0    0    0   0   0

3. 主題模型

建立更多主題的主題模型

嘗試3、4、5、6、7個主題數,將結果存起來,再做進一步分析。 (此部分需要跑一段時間,已經將跑完的檔案存成ldas_result.rdata,可以直接載入)

# ldas = c()
# topics = c(3,4,5,6,7)
# for(topic in topics){
#  start_time <- Sys.time()
#  lda <- LDA(dtm, k = topic, control = list(seed = 2021))
#  ldas =c(ldas,lda)
#  print(paste(topic ,paste("topic(s) and use time is ", Sys.time() -start_time)))
#  save(ldas,file = "ldas_result_.rdata") # 將模型輸出成檔案
# }

載入先前跑好的每個主題的LDA結果

load("ldas_result_.rdata")

透過perplexity找到最佳主題數

topics = c(3,4,5,6,7)
data_frame(k = topics, perplex = map_dbl(ldas, topicmodels::perplexity)) %>%
  ggplot(aes(k, perplex)) +
  geom_point() +
  geom_line() +
  labs(title = "Evaluating LDA topic models",
       subtitle = "Optimal number of topics (smaller is better)",
       x = "Number of topics",
       y = "Perplexity")


主題數愈多、複雜度愈低、內容的純度愈高。

挑選下降幅度趨緩的轉折點。

產生LDAVis結果

create LDAvis所需的json function 此function是將前面使用 “LDA function”所建立的model,轉換為“LDAVis”套件的input格式。

topicmodels_json_ldavis <- function(fitted, doc_term){
    require(LDAvis)
    require(slam)
  
    ###以下function 用來解決,主題數多會出現NA的問題
    ### 參考 https://github.com/cpsievert/LDAvis/commit/c7234d71168b1e946a361bc00593bc5c4bf8e57e
    ls_LDA = function (phi){
      jensenShannon <- function(x, y) {
        m <- 0.5 * (x + y)
        lhs <- ifelse(x == 0, 0, x * (log(x) - log(m+1e-16)))
        rhs <- ifelse(y == 0, 0, y * (log(y) - log(m+1e-16)))
        0.5 * sum(lhs) + 0.5 * sum(rhs)
      }
      dist.mat <- proxy::dist(x = phi, method = jensenShannon)
      pca.fit <- stats::cmdscale(dist.mat, k = 2)
      data.frame(x = pca.fit[, 1], y = pca.fit[, 2])
    }
  
      # Find required quantities
      phi <- as.matrix(posterior(fitted)$terms)
      theta <- as.matrix(posterior(fitted)$topics)
      vocab <- colnames(phi)
      term_freq <- slam::col_sums(doc_term)
  
      # Convert to json
      json_lda <- LDAvis::createJSON(phi = phi, theta = theta,
                                     vocab = vocab,
                                     doc.length = as.vector(table(doc_term$i)),
                                     term.frequency = term_freq, mds.method = ls_LDA)
  
      return(json_lda)
}

mac表現的部分

# the_lda = ldas[[3]]
# json_res <- topicmodels_json_ldavis(the_lda,dtm) 
# serVis(json_res,open.browser = T)

產生LDAvis檔案,存至local端

# serVis(json_res, out.dir = "vis", open.browser = T)
# writeLines(iconv(readLines("./vis/lda.json"), to = "UTF8"))


我們同時也用了LDAVis找了最佳主題數,想要再次驗證5個主題是很分開,證明可採用5個主題數。

lamda值選0.1,因為愈小的值,字愈獨特。

選定5個主題數的主題模型

the_lda = ldas[[3]]
topics_words <- tidy(the_lda, matrix = "beta") #注意!在tidy function裡面要使用"beta"來取出Phi矩陣。
colnames(topics_words) <- c("topic", "term", "phi")
topics_words %>% arrange(desc(phi)) %>% head(10)

terms依照各主題的phi值由大到小排序

topics_words %>%
  group_by(topic) %>%
  top_n(10, phi) %>%
  ungroup() %>%
  ggplot(aes(x = reorder_within(term,phi,topic), y = phi, fill = as.factor(topic))) +
  theme(text = element_text(family = "Heiti TC Light"))+
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip() +
  scale_x_reordered()

去除共通詞彙

removed_word = c("疫苗","台灣","my","疫情","sent","jptt","on","from","國家","國產","問題","中國","有沒有","不到","高端","國外","購買")

topics_words %>%
  filter(!term  %in% removed_word) %>%
  group_by(topic) %>%
  top_n(10, phi) %>%
  ungroup() %>%
  ggplot(aes(x = reorder_within(term,phi,topic), y = phi, fill = as.factor(topic))) +
  theme(text = element_text(family = "Heiti TC Light"))+
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip() +
  scale_x_reordered()

主題命名

# topics_name = c("各家疫苗比較","國產疫苗研發試驗","國產疫苗背後的政治","疫苗接種","疫苗採購")
topics_name = c("各國贈送疫苗","疫苗研發技術試驗","國產疫苗背後的政治","疫苗接種","疫苗採購")

Document 主題分佈

# for every document we have a probability distribution of its contained topics
tmResult <- posterior(the_lda)
doc_pro <- tmResult$topics
document_topics <- doc_pro[vaccine$artUrl,]
document_topics_df =data.frame(document_topics)
colnames(document_topics_df) = topics_name
rownames(document_topics_df) = NULL
ptt_topic = cbind(vaccine,document_topics_df)

# 刪除commentNum、push、boo欄位
ptt_topic$commentNum = NULL
ptt_topic$push = NULL
ptt_topic$boo = NULL

查看特定主題的文章

透過找到特定文章的分佈進行排序之後,可以看到此主題的比重高的文章在討論什麼,也可以依據文章內容來調整命名。

ptt_topic %>%
  arrange(desc(`各國贈送疫苗`)) %>%head(10)

ptt_topic %>%
  arrange(desc(`疫苗研發技術試驗`)) %>%head(10)

ptt_topic %>%
  arrange(desc(`國產疫苗背後的政治`)) %>%head(10)

ptt_topic %>%
  arrange(desc(`疫苗接種`)) %>%head(10)

ptt_topic %>%
  arrange(desc(`疫苗採購`)) %>%head(10)

更改主題命名

# 更改主題1、2名稱
# topics_name = c("各國贈送疫苗","疫苗研發技術試驗","國產疫苗背後的政治","疫苗接種","疫苗採購")

了解主題在時間的變化(去除筆數少月份)

#去除筆數<300
ptt_topic %>%
  mutate(artDate = as.Date(artDate)) %>% 
  filter( !format(artDate,'%Y%m%d') %in% c(20210511, 20210512, 20210513, 20210514, 20210515, 20210516, 20210517, 20210518, 20210519,20210520,20210521,20210522,20210523,20210524)) %>%
  group_by(artDate = format(artDate,'%Y%m%d')) %>%
  summarise_if(is.numeric, sum, na.rm = TRUE) %>%
  melt(id.vars = "artDate")%>%
  ggplot( aes(x=artDate, y=value, fill=variable)) +
  theme(text = element_text(family = "Heiti TC Light"))+
  geom_bar(stat = "identity") + ylab("value") + 
  scale_fill_manual(values=c("#cacaca","#a9c6de","#5588a3","#145374","red"))+
  theme(axis.text.x = element_text(angle = 90, hjust = 1))
The melt generic in data.table has been passed a tbl_df and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(.). In the next version, this warning will become an error.

以比例了解主題時間變化

ptt_topic %>%
  mutate(artDate = as.Date(artDate)) %>% 
  filter( !format(artDate,'%Y%m') %in% c(20210511, 20210512, 20210513, 20210514, 20210515, 20210516, 20210517, 20210518, 20210519,20210520,20210521,20210522,20210523,20210524)) %>%
  group_by(artDate = format(artDate,'%Y%m%d')) %>%
  summarise_if(is.numeric, sum, na.rm = TRUE) %>%
  melt(id.vars = "artDate")%>%
  group_by(artDate)%>%
  mutate(total_value =sum(value))%>%
  ggplot( aes(x=artDate, y=value/total_value, fill=variable)) +
  theme(text = element_text(family = "Heiti TC Light"))+
  geom_bar(stat = "identity") + ylab("proportion") + 
  scale_fill_manual(values=c("#cacaca","#a9c6de","#5588a3","#145374","red"))+
  theme(axis.text.x = element_text(angle = 90, hjust = 1))
The melt generic in data.table has been passed a tbl_df and will attempt to redirect to the relevant reshape2 method; please note that reshape2 is deprecated, and this redirection is now deprecated as well. To continue using melt methods from reshape2 while both libraries are attached, e.g. melt.list, you can prepend the namespace like reshape2::melt(.). In the next version, this warning will become an error.

可以看出國際疫苗背後的政治一直是個文章討論的重點

根據各主題代表字畫graph(已去除共同字)

removed_word = c("疫苗","台灣","my","疫情","sent","jptt","on","from","國家","國產","問題","中國","有沒有","不到","高端","國外","購買")

phi_m <- topics_words %>%
  filter(!term  %in% removed_word) %>% 
  arrange(desc(phi)) %>% 
  top_n(70)
Selecting by phi
dtm_ <-phi_m %>% cast_dtm(topic, term, phi)

dtmm<-as.matrix(dtm_)
dim(dtmm)
[1]  5 54
network=graph_from_incidence_matrix(dtmm)

# plot
set.seed(3)

plot(network, ylim=c(-1,1), xlim=c(-1,1), asp = 0,
     vertex.label.cex=0.7,vertex.size=10,vertex.label.family = "Heiti TC Light")

4. 社群網路圖

取出代表主題(topic)

每篇文章拿gamma值最大的topic當該文章的topic

# 在tidy function中使用參數"gamma"來取得 theta矩陣
vaccine_topics <- tidy(the_lda, matrix="gamma") %>% # document topic gamma
                  group_by(document) %>%
                  top_n(1, wt=gamma)
vaccine_topics

資料合併(文章和留言)

# 文章和留言
reviews <- reviews %>%
      select(artUrl, cmtPoster, cmtStatus, cmtContent)
posts_Reviews <- merge(x = vaccine, y = reviews, by = "artUrl")

# 把文章和topic合併
posts_Reviews <- merge(x = posts_Reviews, y = vaccine_topics, by.x = "artUrl", by.y="document")

# head(posts_Reviews,3)

取出 cmtPoster(回覆者)、artPoster(發文者)、artUrl(文章連結) 三個欄位

link <- posts_Reviews %>% select(cmtPoster, artPoster, artUrl)
head(link,3)

資料篩選

資料篩選的方式: + 文章: 文章日期、留言數(commentNum) + link、node: degree

# 看留言數大概都多少(方便後面篩選)
vaccine %>%
  filter(commentNum>100) %>%
  ggplot(aes(x=commentNum)) + geom_histogram()


文章回覆數量在500以後就變少,所以可以抓500為斷點。

依據發文數或回覆數篩選post和review

# 帳號發文篇數
post_count = vaccine %>%
   group_by(artPoster) %>%
   summarise(count = n()) %>%
   arrange(desc(count))

post_count
# 帳號回覆總數
review_count = reviews %>%
   group_by(cmtPoster) %>%
   summarise(count = n()) %>%
   arrange(desc(count))

review_count

篩選post大於10篇發文者及review大於300則留言者

  • 發文者:4751人
  • 回覆者:48293人
  • 總參與人數:49391人
# 發文者
poster_select <- post_count %>% filter(count >= 10)
vaccine <- vaccine %>%  filter(vaccine$artPoster %in% poster_select$artPoster)

# 回覆者
reviewer_select <- review_count %>%  filter(count >= 300)
reviews <- reviews %>%  filter(reviews$cmtPoster %in% reviewer_select$cmtPoster)
# 檢視參與人數
length(unique(posts_Reviews$artPoster)) # 發文者數量 4751
[1] 4571
length(unique(posts_Reviews$cmtPoster)) # 回覆者數量 48293
[1] 48293
allPoster <- c(posts_Reviews$artPoster, posts_Reviews$cmtPoster) # 總參與人數 49391
length(unique(allPoster))
[1] 49391

標記所有出現過的使用者

  • poster:只發過文、發過文+留過言

  • replyer:只留過言

userList <- data.frame(user=unique(allPoster)) %>%
              mutate(type=ifelse(user%in%vaccine$artPoster, "poster", "replyer"))

head(userList,3)

以主題篩選社群

  • 抓link 篩選一篇文章回覆15次以上者,且文章留言數多餘500則, 文章主題歸類為2(疫苗研發技術試驗)與3(國產疫苗背後的政治)者, 欄位只取:cmtPoster(評論者), artPoster(發文者), artUrl(文章連結), topic(主題)。
link = posts_Reviews %>%
      group_by(cmtPoster, artUrl) %>% 
      filter(n()>15) %>%
      filter(commentNum > 500) %>%
      filter(topic == 2 | topic == 3) %>% 
      select(cmtPoster, artPoster, artUrl, topic) %>% 
      unique()

link
  • 抓nodes 在所有的使用者裡面,篩選link中有出現的使用者
filtered_user <- userList %>%
          filter(user%in%link$cmtPoster | user%in%link$artPoster) %>%
          arrange(desc(type))

head(filtered_user,3)

使用者經常參與的文章種類

filter_degree = 5

# 建立網路關係
reviewNetwork <- graph_from_data_frame(d=link, v=filtered_user, directed=F)

# 依據使用者身份對點進行上色
labels <- degree(reviewNetwork)
V(reviewNetwork)$label <- names(labels)
V(reviewNetwork)$color <- ifelse(V(reviewNetwork)$type=="poster", "red", "blue")

# 依據回覆發生的文章所對應的主題,對他們的關聯線進行上色
E(reviewNetwork)$color <- ifelse(E(reviewNetwork)$topic == "2", "palevioletred", "lightgreen")

# 畫出社群網路圖(degree>5的才畫出來)
set.seed(5432)
par(family=('Heiti TC Light'))
plot(reviewNetwork, vertex.size=5, edge.width=3, vertex.label.dist=1,
     vertex.label=ifelse(degree(reviewNetwork) > filter_degree, V(reviewNetwork)$label, NA),vertex.label.font=2)

# 加入標示
legend("bottomright", c("發文者","回文者"), pch=21, 
  col="#777777", pt.bg=c("red","blue"), pt.cex=1, cex=1)
legend("topleft", c("疫苗研發技術試驗","國產疫苗背後的政治"), 
       col=c("palevioletred", "lightgreen"), lty=1, cex=0.6)

可以找出:

主題2「疫苗研發技術試驗」的主要發文者有:CavendishJr

主題3「國產疫苗背後的政治」主題的主要發文者有:COCOCCC

「疫苗研發技術試驗」的主要回文者有:JustinXD、wet

「國產疫苗背後的政治」主題的主要回文者有:yule1224、braveryhyde、weakerman、smallminhaha、AASoymilk

rasiel0919則是兩種主題都有回覆

5. 意見領袖分析:CavendishJr、COCOCCC

整理網路關係者

Vac_leader_data <- posts_Reviews %>% 
  filter((artPoster == "CavendishJr")|(artPoster == "COCOCCC")) 

Vac_leader_data$artDate = as.Date(Vac_leader_data$artDate)
Vac_leader_data = Vac_leader_data %>% mutate(months = as.Date(cut(artDate, "months")))
Vac_CavendishJr <- subset(Vac_leader_data,artPoster == "CavendishJr")
Vac_COCOCCC <- subset(Vac_leader_data,artPoster == "COCOCCC")
VacRW_CavendishJr <- subset(Vac_leader_data,artPoster == "CavendishJr")
VacRW_COCOCCC <- subset(Vac_leader_data,artPoster == "COCOCCC")

CavendishJr文章中詞彙相關性的前處理

Vac1_CavendishJr <- Vac_CavendishJr %>% 
  select(artUrl,sentence)

Vac1_CavendishJr <- strsplit(Vac1_CavendishJr$sentence,"[。!;?!?;]")

# 將每個句子和相對應的文章配對起來,整理成 dataframe
Vac1_CavendishJr  <- data.frame(
  artUrl = rep(Vac_CavendishJr$artUrl, sapply(Vac1_CavendishJr, length)), 
  sentence = unlist(Vac1_CavendishJr))
Vac1_CavendishJr$sentence <- as.character(Vac1_CavendishJr$sentence)

jieba_tokenizer <- worker(user="vaccine.txt", stop_word = "stop_words.txt")

CavendishJr_word <- Vac1_CavendishJr %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  count(artUrl, word, sort = TRUE)

CavendishJr文章中詞彙間的相關性

# 計算兩個詞彙間的相關性
CavendishJr_word_cors <- CavendishJr_word %>%
  group_by(word) %>%
  filter(n() >= 3) %>%
  pairwise_cor(word, artUrl, sort = TRUE)

CavendishJr_word_cors %>%
  filter(correlation > 0.6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation), show.legend = FALSE) + 
  geom_node_point(color = "lightblue", size = 3) +
  geom_node_text(aes(label = name), repel = TRUE, family = "Heiti TC Light") +
  theme_void()

主題2「疫苗研發技術試驗」的主要發文者有:CavendishJr

CavendishJr_sen_plo文章中,發文者和回文者情緒分析之資料前處理 準備CavendishJr的字典

CavendishJr_LIWC <- CavendishJr_word %>% 
  inner_join(LIWC) %>% 
  group_by(artUrl,sentiment) %>%
  summarise(count=sum(n))
Joining, by = "word"
`summarise()` has grouped output by 'artUrl'. You can override using the `.groups` argument.

整理Reivew-CavendishJr

CavendishJr_LIWC <-CavendishJr_LIWC%>% 
  spread(sentiment, count, fill = 0)

cont_VacRW_CavendishJr <- VacRW_CavendishJr %>% 
  select(artUrl,cmtContent)

cont_VacRW_CavendishJr<-strsplit(cont_VacRW_CavendishJr$cmtContent,"[。!;?!?;]")

# 將每個句子與所屬的文章連結配對起來,整理成 dataframe
cont_VacRW_CavendishJr <- data.frame(
  artUrl = rep(VacRW_CavendishJr$artUrl, sapply(cont_VacRW_CavendishJr, length)), 
  cmtContent = unlist(cont_VacRW_CavendishJr)) %>%
  filter(!str_detect(cmtContent, regex("^(\t|\n| )*$")))
cont_VacRW_CavendishJr$cmtContent <- as.character(cont_VacRW_CavendishJr$cmtContent)

# 進行斷詞,並計算各詞彙在各文章中出現的次數
cont_VacRW_CavendishJr_word <- cont_VacRW_CavendishJr %>%
  unnest_tokens(word, cmtContent, token=chi_tokenizer) %>%
  filter(!str_detect(word, regex("[0-9a-zA-Z]"))) %>%
  count(artUrl, word, sort = TRUE)

VacRW_CavendishJr_LIWC <- cont_VacRW_CavendishJr_word %>% 
  inner_join(LIWC) %>% 
  group_by(artUrl,sentiment) %>%
  summarise(count=sum(n)) %>% spread(sentiment, count, fill = 0)
Joining, by = "word"
`summarise()` has grouped output by 'artUrl'. You can override using the `.groups` argument.

CavendishJr與留言者的正負面情緒比例

將CavendishJr 發文和回覆的情緒合併起來

VacRW_CavendishJr_LIWC <- VacRW_CavendishJr_LIWC %>% 
  mutate(source="review")
  
CavendishJr_LIWC <- CavendishJr_LIWC %>% 
  mutate(source="article")

CavendishJr_cmt_sen <- 
  bind_rows(x = CavendishJr_LIWC, y = VacRW_CavendishJr_LIWC) 
CavendishJr_cmt_sen

CavendishJr_sen_plot <- CavendishJr_cmt_sen  %>% 
  gather(sentiment,article_number,-source,-artUrl) %>%
  group_by(artUrl,source) %>% 
  mutate(total_article =sum(article_number),ratio=article_number/total_article) %>% 
  group_by(artUrl) #發文/回復的文章數

CavendishJr_sen_plot %>%
  ggplot( aes(x=as.factor(artUrl), y=ratio, fill=sentiment)) + 
  geom_bar(stat = "identity") + ylab("proportion") + 
    #theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  theme(text=element_text(family = "Heiti TC Light",size=12))+
  facet_wrap(~source, ncol = 1)

x是每篇文章,每篇文章的正負面情緒,比較發文者與回覆者的情緒,positive文章的發文者與回覆者的情緒趨勢普遍沒有一致。

CavendishJr文字雲

CavendishJr_word %>%
   filter(!word  %in% '疫苗') %>%
   group_by(word) %>%
   summarise(sum = n()) %>%
   filter(sum > 3)  %>%
   arrange(desc(sum)) %>%
   wordcloud2()

COCOCCC文章中詞彙相關性的前處理

Vac1_COCOCCC <- Vac_COCOCCC %>% 
  select(artUrl,sentence) 

Vac1_COCOCCC <- strsplit(Vac1_COCOCCC$sentence,"[。!;?!?;]")

# 將每個句子和相對應的文章配對起來,整理成 dataframe
Vac1_COCOCCC  <- data.frame(
  artUrl = rep(Vac_COCOCCC$artUrl, sapply(Vac1_COCOCCC, length)), 
  sentence = unlist(Vac1_COCOCCC))
Vac1_COCOCCC$sentence <- as.character(Vac1_COCOCCC$sentence)

jieba_tokenizer <- worker(user="vaccine.txt", stop_word = "stop_words.txt")

COCOCCC_word <- Vac1_COCOCCC %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  count(artUrl, word, sort = TRUE)

COCOCCC文章中詞彙間的相關性

# 計算兩個詞彙間的相關性
COCOCCC_word_cors <- COCOCCC_word %>%
  group_by(word) %>%
  filter(n() >= 2) %>%
  pairwise_cor(word, artUrl, sort = TRUE)

COCOCCC_word_cors %>%
  filter(correlation > 0.6) %>%
  graph_from_data_frame() %>%
  ggraph(layout = "fr") +
  geom_edge_link(aes(edge_alpha = correlation), show.legend = FALSE) + 
  geom_node_point(color = "lightblue", size = 3) +
  geom_node_text(aes(label = name), repel = TRUE, family = "Heiti TC Light") +
  theme_void()

主題3「國產疫苗背後的政治」主題的主要發文者有:COCOCCC

…中間帶有政治色彩的…


COCOCCC文章中,發文者和回文者情緒分析之資料前處理
準備COCOCCC的字典

COCOCCC_LIWC <- COCOCCC_word %>% 
  inner_join(LIWC) %>% 
  group_by(artUrl,sentiment) %>%
  summarise(count=sum(n))
Joining, by = "word"
`summarise()` has grouped output by 'artUrl'. You can override using the `.groups` argument.

整理Reivew-COCOCCC

COCOCCC_LIWC <-COCOCCC_LIWC%>% 
  spread(sentiment, count, fill = 0)

cont_VacRW_COCOCCC <- VacRW_COCOCCC %>% 
  select(artUrl,cmtContent)

cont_VacRW_COCOCCC<-strsplit(cont_VacRW_COCOCCC$cmtContent,"[。!;?!?;]")

# 將每個句子與所屬的文章連結配對起來,整理成 dataframe
cont_VacRW_COCOCCC <- data.frame(
  artUrl = rep(VacRW_COCOCCC$artUrl, sapply(cont_VacRW_COCOCCC, length)), 
  cmtContent = unlist(cont_VacRW_COCOCCC)) %>%
  filter(!str_detect(cmtContent, regex("^(\t|\n| )*$")))
cont_VacRW_COCOCCC$cmtContent <- as.character(cont_VacRW_COCOCCC$cmtContent)

# 進行斷詞,並計算各詞彙在各文章中出現的次數
cont_VacRW_COCOCCC_word <- cont_VacRW_COCOCCC %>%
  unnest_tokens(word, cmtContent, token=chi_tokenizer) %>%
  filter(!str_detect(word, regex("[0-9a-zA-Z]"))) %>%
  count(artUrl, word, sort = TRUE)

VacRW_COCOCCC_LIWC <- cont_VacRW_COCOCCC_word %>% 
  inner_join(LIWC) %>% 
  group_by(artUrl,sentiment) %>%
  summarise(count=sum(n)) %>% spread(sentiment, count, fill = 0)
Joining, by = "word"
`summarise()` has grouped output by 'artUrl'. You can override using the `.groups` argument.

COCOCCC與留言者的正負面情緒比例

將COCOCCC 發文和回覆的情緒合併起來

VacRW_COCOCCC_LIWC <- VacRW_COCOCCC_LIWC %>% 
  mutate(source="review")
  
COCOCCC_LIWC <- COCOCCC_LIWC %>% 
  mutate(source="article")

COCOCCC_cmt_sen <- 
  bind_rows(x = COCOCCC_LIWC, y = VacRW_COCOCCC_LIWC) 
COCOCCC_cmt_sen

COCOCCC_sen_plot <- COCOCCC_cmt_sen  %>% 
  gather(sentiment,article_number,-source,-artUrl) %>%
  group_by(artUrl,source) %>% 
  mutate(total_article =sum(article_number),ratio=article_number/total_article) %>% 
  group_by(artUrl) #發文/回復的文章數

COCOCCC_sen_plot %>%
  ggplot( aes(x=as.factor(artUrl), y=ratio, fill=sentiment)) + 
  geom_bar(stat = "identity") + ylab("proportion") + 
    #theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  theme(text=element_text(family = "Heiti TC Light",size=12))+
  facet_wrap(~source, ncol = 1)


發文者正面情緒較多,而底下的留言者負面情緒較高。

COCOCCC文字雲

COCOCCC_word %>%
   filter(!word  %in% '疫苗') %>%
   group_by(word) %>%
   summarise(sum = n()) %>%
   filter(sum > 1)  %>%
   arrange(desc(sum)) %>%
   wordcloud2()

發文者對各家疫苗的正負情緒

# vaccine_BNT = 
#   vaccine_words %>%
#   filter(word == "BNT" | word == "輝瑞疫苗"| word == "bnt") %>%
#   mutate(vaccine='BNT')

vaccine_national = 
  vaccine_words %>%
  filter(word == "國產疫苗" | word == "高端疫苗"| word == "聯亞疫苗") %>%
  inner_join(article_type) %>%
  mutate(vaccine='national') %>%   
  group_by(vaccine,type)%>%
  summarise(count=n())
Joining, by = "artUrl"
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_BNT = 
  vaccine_words %>%
  filter(word == "BNT" | word == "輝瑞疫苗"| word == "bnt") %>%
  inner_join(article_type) %>%
  mutate(vaccine='BNT') %>%   
  group_by(vaccine,type)%>%
  summarise(count=n())
Joining, by = "artUrl"
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_moderna = 
  vaccine_words %>%
  filter(word == "莫德納" | word == "moderna") %>%
  inner_join(article_type) %>%
  mutate(vaccine='moderna') %>%   
  group_by(vaccine,type)%>%
  summarise(count=n())
Joining, by = "artUrl"
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_az = 
  vaccine_words %>%
  filter(word == "AZ" | word == "az"| word == "Az"| word == "阿斯特捷利康") %>%
  inner_join(article_type) %>%
  mutate(vaccine='az') %>%   
  group_by(vaccine,type)%>%
  summarise(count=n())
Joining, by = "artUrl"
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_jj = 
  vaccine_words %>%
  filter(word == "嬌生" | word == "Johnson & Johnson") %>%
  inner_join(article_type) %>%
  mutate(vaccine='jj') %>%   
  group_by(vaccine,type)%>%
  summarise(count=n())
Joining, by = "artUrl"
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
#合併各個疫苗的發文文章
vaccine_filter = rbind(vaccine_national,vaccine_BNT,vaccine_moderna,vaccine_az,vaccine_jj)
# vaccine_filter %>%
#   ggplot( aes(x=vaccine, y=count, fill=type)) + 
#   geom_bar(stat = "identity") + ylab("count") + 
#   scale_fill_manual(values=c("#145374","red"))+
#   theme(axis.text.x = element_text(angle = 90, hjust = 1)) 

以比例了解發文者對於各家疫苗的正負情緒

# vaccine_filter %>%
#   mutate(total_count =sum(count))%>%
#   ggplot( aes(x=vaccine, y=count/total_count, fill=type)) + 
#   geom_bar(stat = "identity") + ylab("proportion") + 
#   scale_fill_manual(values=c("red","#145374"))+
#   theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
#   geom_hline(aes(yintercept=0.5), colour="white",lwd=2)


發文者在談論各家疫苗時,情緒都偏中立,說明發文者是以較客觀的立場來談論這次事件。

留言者對各家疫苗的正負情緒

reviews_type = 
  reviews_words %>%
  inner_join(LIWC) %>% 
  group_by(artUrl,sentiment) %>%
  summarise(count=n()) %>%
  spread(sentiment,count,fill = 0) %>% #把正負面情緒展開,缺值補0
  mutate(type = case_when(positive > negative ~ "positive",
                          TRUE ~ "negative")) %>%
  data.frame()
Joining, by = "word"
`summarise()` has grouped output by 'artUrl'. You can override using the `.groups` argument.
names(reviews_type)[2]="reviews_negative"
names(reviews_type)[3]="reviews_positive"
vaccine_national_url = 
  vaccine_words %>%
  filter(word == "國產疫苗" | word == "高端疫苗"| word == "聯亞疫苗") %>%
  inner_join(article_type) %>%
  mutate(vaccine='national')
Joining, by = "artUrl"
vaccine_BNT_url = 
  vaccine_words %>%
  filter(word == "BNT" | word == "輝瑞疫苗"| word == "bnt") %>%
  inner_join(article_type) %>%
  mutate(vaccine='BNT')
Joining, by = "artUrl"
vaccine_moderna_url = 
  vaccine_words %>%
  filter(word == "莫德納" | word == "moderna") %>%
  inner_join(article_type) %>%
  mutate(vaccine='moderna')
Joining, by = "artUrl"
vaccine_az_url = 
  vaccine_words %>%
  filter(word == "AZ" | word == "az"| word == "Az"| word == "阿斯特捷利康") %>%
  inner_join(article_type) %>%
  mutate(vaccine='az')
Joining, by = "artUrl"
vaccine_jj_url = 
  vaccine_words %>%
  filter(word == "嬌生" | word == "Johnson & Johnson") %>%
  inner_join(article_type) %>%
  mutate(vaccine='jj')
Joining, by = "artUrl"
#合併各個疫苗的發文文章
vaccine_filter_url = rbind(vaccine_national_url,vaccine_BNT_url,vaccine_moderna_url,vaccine_az_url,vaccine_jj_url)
vaccine_arturl = inner_join(reviews_type,vaccine_filter_url,by="artUrl")
vaccine_national_reviews = 
  vaccine_arturl %>%
  filter(vaccine == "national") %>%
  group_by(vaccine,type.x)%>%
  summarise(count=n())
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_BNT_reviews = 
  vaccine_arturl %>%
  filter(vaccine == "BNT") %>%
  group_by(vaccine,type.x)%>%
  summarise(count=n())
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_moderna_reviews = 
  vaccine_arturl %>%
  filter(vaccine == "moderna") %>%
  group_by(vaccine,type.x)%>%
  summarise(count=n())
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_az_reviews = 
  vaccine_arturl %>%
  filter(vaccine == "az") %>%
  group_by(vaccine,type.x)%>%
  summarise(count=n())
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
vaccine_jj_reviews = 
  vaccine_arturl %>%
  filter(vaccine == "jj") %>%
  group_by(vaccine,type.x)%>%
  summarise(count=n())
`summarise()` has grouped output by 'vaccine'. You can override using the `.groups` argument.
#合併各個疫苗的留言
vaccine_filter_reviews = rbind(vaccine_national_reviews,vaccine_BNT_reviews,vaccine_moderna_reviews,vaccine_az_reviews,vaccine_jj_reviews)
# vaccine_filter_reviews %>%
#   ggplot( aes(x=vaccine, y=count, fill=type.x)) + 
#   geom_bar(stat = "identity") + ylab("count") + 
#   scale_fill_manual(values=c("#145374","red"))+
#   theme(axis.text.x = element_text(angle = 90, hjust = 1)) 

以比例了解留言者對於各家疫苗的正負情緒

vaccine_filter_reviews %>%
  mutate(total_count =sum(count))%>%
  ggplot( aes(x=vaccine, y=count/total_count, fill=type.x)) + 
  geom_bar(stat = "identity") + ylab("proportion") + 
  scale_fill_manual(values=c("red","#145374"))+
  theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  geom_hline(aes(yintercept=0.5), colour="white",lwd=2)


反而留言者在談論各家疫苗時,情緒偏向負面,看待本次事件較悲觀。

6. 不同新聞和ppt之間的正負面情緒比較

載入東森新聞、聯合新聞、蘋果新聞

ettoday <- fread("東森新聞_articleMetaData.csv",encoding = "UTF-8") %>% 
  mutate(sentence=gsub("[\n]{2,}", "。", sentence)) %>% 
  mutate(sentence=gsub("\n", "", sentence)) %>% 
  mutate(sentence=gsub("http(s)?[-:\\/A-Za-z0-9\\.]+", " ", sentence))

udn <- fread("聯合新聞_articleMetaData.csv",encoding = "UTF-8") %>% 
  mutate(sentence=gsub("[\n]{2,}", "。", sentence)) %>% 
  mutate(sentence=gsub("\n", "", sentence)) %>% 
  mutate(sentence=gsub("http(s)?[-:\\/A-Za-z0-9\\.]+", " ", sentence))

apple <- fread("蘋果新聞_articleMetaData.csv",encoding = "UTF-8") %>% 
  mutate(sentence=gsub("[\n]{2,}", "。", sentence)) %>% 
  mutate(sentence=gsub("\n", "", sentence)) %>% 
  mutate(sentence=gsub("http(s)?[-:\\/A-Za-z0-9\\.]+", " ", sentence))
ettoday <- ettoday %>% 
  mutate(sentence=gsub("媒體來源|記者署名|完整新聞標題|完整新聞內文|完整新聞連結|(或短網址)|備註|備註請放最後面|違者新聞文章刪除", "", sentence))

udn <- udn %>% 
  mutate(sentence=gsub("媒體來源|記者署名|完整新聞標題|完整新聞內文|完整新聞連結|(或短網址)|備註|備註請放最後面|違者新聞文章刪除", "", sentence))

apple <- apple %>% 
  mutate(sentence=gsub("媒體來源|記者署名|完整新聞標題|完整新聞內文|完整新聞連結|(或短網址)|備註|備註請放最後面|違者新聞文章刪除", "", sentence))

斷詞

jieba_tokenizer <- worker(user="vaccine.txt", stop_word = "stop_words.txt")

ettoday$sentence=ettoday$sentence %>% tolower()
ettoday_words <- ettoday %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  filter(!grepl('[[:punct:]]',word)) %>% # 去標點符號
  filter(!str_detect(word, regex("[0-9]")))  #去除數字

udn$sentence=udn$sentence %>% tolower()
udn_words <- udn %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  filter(!grepl('[[:punct:]]',word)) %>% # 去標點符號
  filter(!str_detect(word, regex("[0-9]")))  #去除數字

apple$sentence=apple$sentence %>% tolower()
apple_words <- apple %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  filter(!grepl('[[:punct:]]',word)) %>% # 去標點符號
  filter(!str_detect(word, regex("[0-9]")))  #去除數字
LIWC$sentiment = ifelse(LIWC$sentimen=="positive",1,-1)

計算情緒分數

sentiment_count = MToken %>%
  select(artTitle,artDate,word) %>%
  inner_join(LIWC) %>% 
  group_by(artTitle) %>%  
  mutate(P_N=ifelse(sum(sentiment)>0,"positive","negative"))  %>% 
  filter(row_number()==1) %>% 
    group_by(artDate) %>% 
  summarise(P_number=sum(P_N=="positive"),
         N_number=sum(P_N=="negative"),
         article_n = n(),
         p_ratio = P_number/article_n,
         n_ratio = N_number/article_n,
         source="ptt")
Joining, by = "word"
sentiment_count=sentiment_count %>% select(artDate,p_ratio,n_ratio,source) %>% tidyr::gather(sentiment,ratio,-artDate,-source)
ettoday_words = ettoday_words %>%
  select(artTitle,artDate,word) %>%
  inner_join(LIWC) %>% 
  group_by(artTitle) %>%  
  mutate(P_N=ifelse(sum(sentiment)>0,"positive","negative"))  %>% 
  filter(row_number()==1) %>% 
    group_by(artDate) %>% 
  summarise(P_number=sum(P_N=="positive"),
         N_number=sum(P_N=="negative"),
         article_n = n(),
         p_ratio = P_number/article_n,
         n_ratio = N_number/article_n,
         source="ettoday")
Joining, by = "word"
ettoday_words=ettoday_words %>% select(artDate,p_ratio,n_ratio,source) %>% tidyr::gather(sentiment,ratio,-artDate,-source)
udn_words = udn_words %>%
  select(artTitle,artDate,word) %>%
  inner_join(LIWC) %>% 
  group_by(artTitle) %>%  
  mutate(P_N=ifelse(sum(sentiment)>0,"positive","negative"))  %>% 
  filter(row_number()==1) %>% 
    group_by(artDate) %>% 
  summarise(P_number=sum(P_N=="positive"),
         N_number=sum(P_N=="negative"),
         article_n = n(),
         p_ratio = P_number/article_n,
         n_ratio = N_number/article_n,
         source="udn")
Joining, by = "word"
udn_words=udn_words %>% select(artDate,p_ratio,n_ratio,source) %>% tidyr::gather(sentiment,ratio,-artDate,-source)
apple_words = apple_words %>%
  select(artTitle,artDate,word) %>%
  inner_join(LIWC) %>% 
  group_by(artTitle) %>%  
  mutate(P_N=ifelse(sum(sentiment)>0,"positive","negative"))  %>% 
  filter(row_number()==1) %>% 
    group_by(artDate) %>% 
  summarise(P_number=sum(P_N=="positive"),
         N_number=sum(P_N=="negative"),
         article_n = n(),
         p_ratio = P_number/article_n,
         n_ratio = N_number/article_n,
         source="apple")
Joining, by = "word"
apple_words=apple_words %>% select(artDate,p_ratio,n_ratio,source) %>% tidyr::gather(sentiment,ratio,-artDate,-source)

4個dataframe合併

ettoday_words$artDate=as.Date(ettoday_words$artDate)
udn_words$artDate=as.Date(udn_words$artDate)
apple_words$artDate=as.Date(apple_words$artDate)

all_data=bind_rows(sentiment_count,ettoday_words,udn_words,apple_words)

合併後的結果

```r
mycolors <- colorRampPalette(brewer.pal(8, \Set3\))(20)

all_data%>%
  group_by(artDate)%>%
  ggplot( aes(x=artDate, y=ratio, fill=sentiment )) + 
  geom_bar(stat = \identity\) + ylab(\proportion\) + 
    theme(axis.text.x = element_text(angle = 90, hjust = 1))+
  scale_fill_manual(values=mycolors[c(1,5,8,12)])+
scale_x_date(labels = scales::date_format(\%m/%d\) )+
  facet_wrap(~source,ncol = 1, scales=\free_y\)

```

7. 結論

整體而言,民眾在疫苗此議題上大多為負面情緒,且大多圍繞在疫苗背後的政治議題,可以了解人民對於政府的疫苗政策可能不是那麼滿意,希望政府能提供足夠疫苗,讓台灣儘早度過疫情難關。

LS0tCnRpdGxlOiAi5Y+w54Gj55ar6IuX5LqL5Lu25pa8UFBU5LmL6KiO6KuW5YiG5p6QIgphdXRob3I6ICLnrKzkuZ3ntYTvvZzpmbPkvbPlkJ/jgIHmnpflubjkvbPjgIHmtKrnjp/lkJvjgIHpmbPlhqDlpoIiCmRhdGU6ICJgciBTeXMudGltZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGhpZ2hsaWdodDogcHlnbWVudHMKICAgIHRoZW1lOiBmbGF0bHkKICAgIGNzczogc3R5bGUuY3NzCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBlY2hvID0gVCwgcmVzdWx0cyA9ICdoaWRlJ30KU3lzLnNldGxvY2FsZShjYXRlZ29yeSA9ICJMQ19BTEwiLCBsb2NhbGUgPSAiemhfVFcuVVRGLTgiKSAjIOmBv+WFjeS4reaWh+S6gueivChXaW5kb3dz57O757Wx5Y+v5bCH6YCZ6KGM6Ki76KejKQpgYGAKIyAxLiDli5XmqZ/oiIfliIbmnpDnm67nmoQK55Sx5pa85pyA6L+R5Y+w54Gj55qE55ar5oOF5bu254eS77yM5L2/5b6X55ar6IuX55qE6KiO6KuW5LiA55u05bGF6auY5LiN5LiL77yM6ICM5bCN5pa855ar6IuX5omA6KGN55Sf55qE5aSa56iu6Z2i5ZCR77yM5YOP5piv5pS/5rK744CB5oOF57eS562J77yM6YO95piv5oiR5YCR5oOz5rex5YWl5LqG6Kej55qE77yM5Zug5q2k5pys57WE6Yed5bCN5YWn5a655YGa5Lul5LiL5YiG5p6Q77ya5oOF57eS5YiG5p6QLOaWh+Wtl+mbsizmraPosqDpnaLoqZ7nmoToqZ7poLss56S+576k57ay6Lev5ZyWLi7nrYnvvIzmt7HlhaXnoJTnqbblnKjkuI3lkIzmmYLplpPjgIHmhI/opovpoJjoopbmiJbmmK/mlrDogZ7lqpLpq5TlsI3mlrznlqvoi5fnmoTnnIvms5Xku6Xlj4rmsJHnnL7lsI3kuI3lkIznlqvoi5fnmoTmraPosqDpnaLmg4Xnt5LjgIIKCiMgMi4g5Yid5q2l5o6i57Si6LOH5paZ6ZuGCuWuieijnemcgOimgeeahHBhY2thZ2Vz5ZKM5aWX5Lu26LyJ5YWlCmBgYHtyfQpwYWNrYWdlcyA9IGMoInJlYWRyIiwgImRwbHlyIiwgInN0cmluZ3IiLCAiamllYmFSIiwgInRpZHl0ZXh0IiwgIk5MUCIsICJyZWFkciIsICJ0aWR5ciIsICJnZ3Bsb3QyIiwgImdncmFwaCIsICJpZ3JhcGgiLCAic2NhbGVzIiwgInJlc2hhcGUyIiwgIndpZHlyIiwid29yZGNsb3VkMiIsIndvcmRjbG91ZCIsIndpZHlyIiwiZ2dyYXBoIikKZXhpc3RpbmcgPSBhcy5jaGFyYWN0ZXIoaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKQpmb3IocGtnIGluIHBhY2thZ2VzWyEocGFja2FnZXMgJWluJSBleGlzdGluZyldKSBpbnN0YWxsLnBhY2thZ2VzKHBrZykKYGBgCgpgYGB7ciBlY2hvID0gVCwgcmVzdWx0cyA9ICdoaWRlJ30KbGlicmFyeShyZWFkcikKbGlicmFyeShkYXRhLnRhYmxlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoamllYmFSKQpsaWJyYXJ5KHRpZHl0ZXh0KQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkodG0pCmxpYnJhcnkodG9waWNtb2RlbHMpCmxpYnJhcnkocHVycnIpCnJlcXVpcmUoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeSh0aWR5cikKbGlicmFyeShzY2FsZXMpCmxpYnJhcnkod29yZGNsb3VkMikKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkod2lkeXIpCmxpYnJhcnkoZ2dyYXBoKQpgYGAKCiMjIyDos4fmlpnmj4/ov7AKKyDos4fmlpnkvobmupA6IOmAj+mBjuS4reWxseeuoemZouaWh+Wtl+WIhuaekOW5s+WPsOWcqFBUVOWFq+WNpueJiOaQnCAyMDIxLTA1LTExIH4gMjAyMS0wNi0xMSDmiYDmnInmlofnq6DoiIfnlZnoqIAKKyDos4fmlpnpm4bvvJpmaW5hbF9hcnRpY2xlTWV0YURhdGEuY3N244CBZmluYWxfYXJ0aWNsZVJldmlld3MuY3N2Cisg6Zec6Y215a2X77ya55ar6IuXCisg5paH56ug56+H5pW477ya57i95YWxIDExNTg456+HCgpgYGB7cn0KIyB2YWNjaW5lIDwtIHJlYWRfY3N2KCJDOi9Vc2Vycy9vb29saXZpYS9EZXNrdG9wL05TWVNVLzEwOSgyKS9zbWEvZmluYWxfcHJqL2svZmluYWxfYXJ0aWNsZU1ldGFEYXRhLmNzdiIpICU+JQojICAgbXV0YXRlKHNlbnRlbmNlPWdzdWIoIltcbl17Mix9IiwgIuOAgiIsIHNlbnRlbmNlKSkgJT4lCiMgICBtdXRhdGUoc2VudGVuY2U9Z3N1YigiXG4iLCAiIiwgc2VudGVuY2UpKSAlPiUKIyAgIG11dGF0ZShzZW50ZW5jZT1nc3ViKCJodHRwKHMpP1stOlxcL0EtWmEtejAtOVxcLl0rIiwgIiAiLCBzZW50ZW5jZSkpCiMgCiMgdmFjY2luZQojIAojIHJldmlld3MgPC0gcmVhZF9jc3YoIkM6L1VzZXJzL29vb2xpdmlhL0Rlc2t0b3AvTlNZU1UvMTA5KDIpL3NtYS9maW5hbF9wcmovay9maW5hbF9hcnRpY2xlUmV2aWV3cy5jc3YiKQojIHJldmlld3MgPSByZXZpZXdzJT4lCiMgICBtdXRhdGUoY210Q29udGVudD1nc3ViKCJbXG5dezIsfSIsICLjgIIiLCBjbXRDb250ZW50KSkgJT4lCiMgICBtdXRhdGUoY210Q29udGVudD1nc3ViKCJcbiIsICIiLCBjbXRDb250ZW50KSkgJT4lCiMgICBtdXRhdGUoY210Q29udGVudD1nc3ViKCJodHRwKHMpP1stOlxcL0EtWmEtejAtOVxcLl0rIiwgIiAiLCBjbXRDb250ZW50KSkKIyAKIyByZXZpZXdzCgpsb2FkKCJ2YWNjaW5lX3ByZXByb2Nlc3MucmRhdGEiKQpgYGAKCmBgYHtyfQojIHZhY2NpbmUgPC0gdmFjY2luZSAlPiUKIyAgIG11dGF0ZShzZW50ZW5jZT1nc3ViKCLlqpLpq5TkvobmupB86KiY6ICF572y5ZCNfOWujOaVtOaWsOiBnuaomemhjHzlrozmlbTmlrDogZ7lhafmlod85a6M5pW05paw6IGe6YCj57WQfCjmiJbnn63ntrLlnYApfOWCmeiou3zlgpnoqLvoq4vmlL7mnIDlvozpnaJ86YGV6ICF5paw6IGe5paH56ug5Yiq6ZmkIiwgIiIsIHNlbnRlbmNlKSkKYGBgCgojIyMg55m85paH5pW45Lul5Y+K55WZ6KiA5pW4Cisg55m85paH5pW477ya57i95YWxIDExNTg4IOevhworIOeVmeiogOaVuO+8mue4veWFsSA4NjA5OTUg56+HCmBgYHtyfQpucm93KHZhY2NpbmUpCm5yb3cocmV2aWV3cykKYGBgCiMjIyDnlqvoi5foqI7oq5botqjli6LlnJYKYGBge3J9CnZhY2NpbmUgJT4lCiAgbXV0YXRlKGFydERhdGUgPSBhcy5EYXRlKGFydERhdGUpKSAlPiUKICBncm91cF9ieShhcnREYXRlKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpJT4lCiAgZ2dwbG90KGFlcyhhcnREYXRlLGNvdW50KSkrCiAgICBnZW9tX2xpbmUoY29sb3I9ImJsdWUiKSsKICAgIGdlb21fcG9pbnQoKQpgYGAKPGJyPuS7peS6lOaciOW6leWIsOWFreaciOWIneiojuirluWkp+W5heS4iuWNhzxicj4KPGJyPiA1LzI244CBNS8zMOOAgTYvMTAg54K66auY6buePGJyPgoKIyMjIOS4i+WIl+eCuuS4ieWAi+mrmOm7nueahOWFp+WuuQoKYGBge3J9CnZhY2NpbmUgJT4lCiBtdXRhdGUoYXJ0RGF0ZSA9IGFzLkRhdGUoYXJ0RGF0ZSkpICU+JQogZmlsdGVyKGFydERhdGUgPT0gIjIwMjEtMDUtMjYiKSAlPiUKIGhlYWQoMTUpCmBgYAo8YnI+NS8yNiDlnIvnlKLnlqvoi5fnoJTnmbzlkozmjqHos7znlqvoi5fngrrkuLs8YnI+CgpgYGB7cn0KdmFjY2luZSAlPiUKICBtdXRhdGUoYXJ0RGF0ZSA9IGFzLkRhdGUoYXJ0RGF0ZSkpICU+JQogIGZpbHRlcihhcnREYXRlID09ICIyMDIxLTA1LTMwIikgJT4lCiAgaGVhZCgxNSkKYGBgCjxicj41LzMwIOmrmOerr+eWq+iLl+WSjOmDreWPsOmKmOiyt+eWq+iLl+eCuuS4uzxicj4KCmBgYHtyfQp2YWNjaW5lICU+JQogIG11dGF0ZShhcnREYXRlID0gYXMuRGF0ZShhcnREYXRlKSkgJT4lCiAgZmlsdGVyKGFydERhdGUgPT0gIjIwMjEtMDYtMTAiKSAlPiUKICBoZWFkKDE1KQpgYGAKPGJyPjYvMTAg55ar6IuX55+t57y65ZWP6aGMPGJyPgoK5Yid5aeL5YyW5pa36Kme5ZmoCmBgYHtyfQojICMg5L2/55So6buY6KqN5Y+D5pW45Yid5aeL5YyW5LiA5YCL5pa36Kme5byV5pOOCiMgIyDlhYjkuI3kvb/nlKjku7vkvZXnmoTlrZflhbjlkozlgZznlKjoqZ4KIyBqaWViYV90b2tlbml6ZXIgPC0gd29ya2VyKHVzZXI9InZhY2NpbmUudHh0Iiwgc3RvcF93b3JkID0gInN0b3Bfd29yZHMudHh0IikKIyAKIyBjaGlfdG9rZW5pemVyIDwtIGZ1bmN0aW9uKHQpIHsKIyAgIGxhcHBseSh0LCBmdW5jdGlvbih4KSB7CiMgICAgIGlmKG5jaGFyKHgpPjEpewojICAgICAgIHRva2VucyA8LSBzZWdtZW50KHgsIGppZWJhX3Rva2VuaXplcikKIyAgICAgICAjIOWOu+aOieWtl+S4sumVt+W6pueIsjHnmoToqZ7lvZkKIyAgICAgICB0b2tlbnMgPC0gdG9rZW5zW25jaGFyKHRva2Vucyk+MV0KIyAgICAgICByZXR1cm4odG9rZW5zKQojICAgICB9CiMgICB9KQojIH0KYGBgCuaWt+ipnuiIh+aVtOeQhuaWt+ipnue1kOaenApgYGB7cn0KIyAjIOmAsuihjOaWt+ipnu+8jOS4puioiOeul+WQhOipnuW9meWcqOWQhOaWh+eroOS4reWHuuePvueahOasoeaVuAojIHZhY2NpbmVfd29yZHMgPC0gdmFjY2luZSAlPiUKIyAgIHVubmVzdF90b2tlbnMod29yZCwgc2VudGVuY2UsIHRva2VuPWNoaV90b2tlbml6ZXIpICU+JQojICAgZmlsdGVyKCFzdHJfZGV0ZWN0KHdvcmQsIHJlZ2V4KCJbMC05XSIpKSkgJT4lCiMgICBjb3VudChhcnRVcmwsIHdvcmQsIHNvcnQgPSBUUlVFKQojIHZhY2NpbmVfd29yZHMKIyAKIyByZXZpZXdzX3dvcmRzIDwtIHJldmlld3MgJT4lCiMgICB1bm5lc3RfdG9rZW5zKHdvcmQsIGNtdENvbnRlbnQsIHRva2VuPWNoaV90b2tlbml6ZXIpICU+JQojICAgZmlsdGVyKCFzdHJfZGV0ZWN0KHdvcmQsIHJlZ2V4KCJbMC05XSIpKSkgJT4lCiMgICBjb3VudChhcnRVcmwsIHdvcmQsIHNvcnQgPSBUUlVFKQojIAojICMg5oqK5paH56ug5ZKM55WZ6KiA55qE5pa36Kme57WQ5p6c5L215Zyo5LiA6LW3CiMgTVRva2VuIDwtIHZhY2NpbmUgJT4lIHVubmVzdF90b2tlbnMod29yZCwgc2VudGVuY2UsIHRva2VuPWNoaV90b2tlbml6ZXIpCiMgUlRva2VuIDwtIHJldmlld3MgJT4lIHVubmVzdF90b2tlbnMod29yZCwgY210Q29udGVudCwgdG9rZW49Y2hpX3Rva2VuaXplcikKIyAKIyAjIOaKiuizh+aWmeS9teWcqOS4gOi1twpkYXRhIDwtIHJiaW5kKE1Ub2tlblssYygiYXJ0RGF0ZSIsImFydFRpdGxlIiwiYXJ0VXJsIiwgIndvcmQiKV0sUlRva2VuWyxjKCJhcnREYXRlIiwiYXJ0VGl0bGUiLCJhcnRVcmwiLCAid29yZCIpXSkKYGBgCgpgYGB7cn0KIyAjIOagvOW8j+WMluaXpeacn+ashOS9jQojIGRhdGEkYXJ0RGF0ZT0gZGF0YSRhcnREYXRlICU+JSBhcy5EYXRlKCIlWS8lbS8lZCIpCiMgCiMgIyDpgY7mv77nibnmrorlrZflhYMKIyBkYXRhX3NlbGVjdCA9IGRhdGEgJT4lIAojICAgZmlsdGVyKCFncmVwbCgnW1s6cHVuY3Q6XV0nLHdvcmQpKSAlPiUgIyDljrvmqJnpu57nrKbomZ8KIyAgIGZpbHRlcighZ3JlcGwoIlsnXjAtOSddIix3b3JkKSkgJT4lICMg5Y676Iux5paH44CB5pW45a2XCiMgICBmaWx0ZXIobmNoYXIoLiR3b3JkKT4xKSAKIyAgIAojICMg566X5q+P5aSp5LiN5ZCM5a2X55qE6Kme6aC7CiMgIyB3b3JkX2NvdW50OmFydERhdGUsd29yZCxjb3VudAojIHdvcmRfY291bnQgPC0gZGF0YV9zZWxlY3QgJT4lCiMgICBzZWxlY3QoYXJ0RGF0ZSwgd29yZCkgJT4lCiMgICBncm91cF9ieShhcnREYXRlLHdvcmQpICU+JQojICAgc3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lICAjIOeul+Wtl+ipnuWWruevh+e4veaVuOeUqHN1bW1hcmlzZQojICAgZmlsdGVyKGNvdW50PjMpICU+JSAgIyDpgY7mv77lh7rnj77lpKrlsJHmrKHnmoTlrZcKIyAgIGFycmFuZ2UoZGVzYyhjb3VudCkpCiMKIyBzYXZlLmltYWdlKGZpbGUgPSAidmFjY2luZV9wcmVwcm9jZXNzLnJkYXRhIikKCiMgd29yZF9jb3VudApgYGAKCua6luWCmeaDhee3kuWtl+WFuApgYGB7cn0KUCA8LSByZWFkX2ZpbGUoIi4vZGljdC9saXdjL3Bvc2l0aXZlLnR4dCIpICMg5q2j5ZCR5a2X5YW4dHh05qqUCk4gPC0gcmVhZF9maWxlKCIuL2RpY3QvbGl3Yy9uZWdhdGl2ZS50eHQiKSAjIOiyoOWQkeWtl+WFuHR4dOaqlAoKI+Wtl+WFuHR4dOaqlOiugOmAsuS+huaYr+S4gOaVtOWAi+Wtl+S4sgp0eXBlb2YoUCkKYGBgCgrliIblibLlrZfoqZ7vvIzkuKblsIflhanlgIvmg4Xnt5LlrZflhbjkvbXlnKjkuIDotbcKYGBge3J9CiMg5bCH5a2X5Liy5L6dLOWIhuWJsgojIHN0cnNwbGl05Zue5YKzbGlzdCAsIOaIkeWAkeWPluWHumxpc3TkuK3nmoTnrKzkuIDlgIvlhYPntKAKUCA9IHN0cnNwbGl0KFAsICIsIilbWzFdXQpOID0gc3Ryc3BsaXQoTiwgIiwiKVtbMV1dCgojIOW7uueri2RhdGFmcmFtZSDmnInlhanlgIvmrITkvY13b3JkLHNlbnRpbWVudHPvvIx3b3Jk5qyE5L2N5YWn5a655piv5a2X5YW45ZCR6YePClAgPSBkYXRhLmZyYW1lKHdvcmQgPSBQLCBzZW50aW1lbnQgPSAicG9zaXRpdmUiKQpOID0gZGF0YS5mcmFtZSh3b3JkID0gTiwgc2VudGltZW50ID0gIm5lZ2F0aXZlIikKCiMg5oqK5YWp5YCL5a2X5YW45ou85Zyo5LiA6LW3CkxJV0MgPSByYmluZChQLCBOKQoKIyDmqqLoppblrZflhbgKaGVhZChMSVdDKQpgYGAKCueul+WHuuavj+eroOaDhee3kue4veWSjChzZW50aW1lbnRfY291bnQpCmBgYHtyfQojIHNlbnRpbWVudF9jb3VudDphcnREYXRlLHNlbnRpbWVudCxjb3VudApzZW50aW1lbnRfY291bnQgPSBkYXRhX3NlbGVjdCAlPiUKICBzZWxlY3QoYXJ0RGF0ZSx3b3JkKSAlPiUKICBpbm5lcl9qb2luKExJV0MpICU+JSAKICBncm91cF9ieShhcnREYXRlLHNlbnRpbWVudCkgJT4lCiAgc3VtbWFyaXNlKGNvdW50PW4oKSkgIApzZW50aW1lbnRfY291bnQKYGBgCgojIyMg5q2j6LKg5oOF57eS5q+U5L6L5oqY57ea5ZyWCuWwh+aDhee3kuWIhuaVuOaomea6luWMluW+jO+8jOWPr+S7peeZvOePvumblueEtuato+iyoOmdouaDhee3kuacieazouWLle+8jOS9huWkp+mDqOWIhuato+iyoOmdouaDhee3kuWQhOWNiu+8jOe0hOWcqDUvMjHlvozosqDpnaLmg4Xnt5LkvZTmr5TovIPpq5jjgIIKYGBge3J9CnNlbnRpbWVudF9jb3VudCAlPiUgCiAgIyDmqJnmupbljJbnmoTpg6jliIYKICBncm91cF9ieShhcnREYXRlKSAlPiUKICBtdXRhdGUocmF0aW8gPSBjb3VudC9zdW0oY291bnQpKSAlPiUKICAjIOeVq+WclueahOmDqOWIhgogIGdncGxvdCgpKwogIGdlb21fbGluZShhZXMoeD1hcnREYXRlLHk9cmF0aW8sY29sb3VyPXNlbnRpbWVudCkpKwogIHNjYWxlX3hfZGF0ZShsYWJlbHMgPSBkYXRlX2Zvcm1hdCgiJW0vJWQiKSwKICAgICAgICAgICAgICAgbGltaXRzID0gYXMuRGF0ZShjKCcyMDIxLTA1LTExJywnMjAyMS0wNi0xMScpKQogICAgICAgICAgICAgICApKwogICMg5Yqg5LiK5qiZ56S65pel5pyf55qE57eaCiAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IGFzLm51bWVyaWMoYXJ0RGF0ZVt3aGljaChzZW50aW1lbnRfY291bnQkYXJ0RGF0ZSA9PSBhcy5EYXRlKCcyMDIxLTA1LTMwJykpClsxXV0pKSxjb2xvdXIgPSAieWVsbG93IikKYGBgCgrmiJHlgJHmjJHlh7rlub7lgIvmg4Xnt5Lpq5jpu57nmoTml6XmnJ8K6KeA5a+f5q+P5pel5oOF57eS5YiG5pW477yM57SE5b6eNS8yNOiZn+mWi+Wni+itsOmhjOiiq+Wkp+mHj+iojuirlu+8jDYvNOmBlOWIsOitsOmhjOmrmOWzsO+8jOS5i+W+jOWwseaFouaFouS4i+mZjeOAggpgYGB7cn0KIyDmn6XnnIvmr4/lpKnnmoTmg4Xnt5LliIbmlbjmjpLlkI0Kc2VudGltZW50X2NvdW50ICU+JQogIHNlbGVjdChjb3VudCxhcnREYXRlKSAlPiUKICBncm91cF9ieShhcnREYXRlKSAlPiUKICBzdW1tYXJpc2Uoc3VtID0gc3VtKGNvdW50KSkgJT4lCiAgYXJyYW5nZShkZXNjKHN1bSkpCmBgYAoKIyMjIDIwMjEtMDUtMzAg5paH5a2X6ZuyCuawkeecvuWwjeaWvOawkemAsum7qOeahOiyoOmdoueci+azleWDj+aYr+WZgeW/g+OAgeWeg+Wcvuetieetie+8jOmrmOerr+Wci+eUoueWq+iLl+acquWIsOS4ieacn++8jOS7peWPiumDreiRo+iyt+eWq+iLlwpgYGB7cn0KIyDnlavlh7rmloflrZfpm7IKd29yZF9jb3VudCAlPiUgCiAgZmlsdGVyKCEod29yZCAlaW4lIGMoIueWq+iLlyIsIuecn+eahCIsIuW3sue2kyIsIuePvuWcqCIsIuWPsOeBoyIpKSkgJT4lCiAgZmlsdGVyKGFydERhdGUgPT0gYXMuRGF0ZSgnMjAyMS0wNS0zMCcpKSAlPiUgCiAgc2VsZWN0KHdvcmQsY291bnQpICU+JSAKICBncm91cF9ieSh3b3JkKSAlPiUgCiAgc3VtbWFyaXNlKGNvdW50ID0gc3VtKGNvdW50KSkgJT4lCiAgYXJyYW5nZShkZXNjKGNvdW50KSkgJT4lCiAgZmlsdGVyKGNvdW50PjIwMCkgJT4lICAgIyDpgY7mv77lh7rnj77lpKrlsJHmrKHnmoTlrZcKICB3b3JkY2xvdWQyKCkKYGBgCgojIyMgMjAyMS0wNS0zMCDmraPosqDmg4Xnt5Lku6PooajlrZcK566X5Ye65omA5pyJ5a2X6Kme55qE6Kme6aC777yM5om+5Ye65oOF57eS5Luj6KGo5a2X77yaCisg5q2j6Z2i5oOF57eS77ya5pSv5oyB44CB55u45L+h44CB5biM5pyb44CB5oiQ5YqfCisg6LKg6Z2i5oOF57eS77ya5Z6D5Zy+44CB5ZWP6aGM44CB5ZmB5b+D44CB5Y+v5oaQCmBgYHtyfQpzZW50aW1lbnRfc3VtX3NlbGVjdCA8LSAKd29yZF9jb3VudCAlPiUKICBmaWx0ZXIoYXJ0RGF0ZSA9PSBhcy5EYXRlKCcyMDIxLTA1LTMwJykpICU+JSAKICAgIGlubmVyX2pvaW4oTElXQykgJT4lCiAgICBncm91cF9ieSh3b3JkLHNlbnRpbWVudCkgJT4lCiAgc3VtbWFyaXNlKAogICAgc3VtID0gc3VtKGNvdW50KQogICkgJT4lIAogIGFycmFuZ2UoZGVzYyhzdW0pKSAlPiUKICBkYXRhLmZyYW1lKCkgCgpzZW50aW1lbnRfc3VtX3NlbGVjdCAgICU+JQogIHRvcF9uKDMwLHd0ID0gc3VtKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBzdW0pKSAlPiUKICBnZ3Bsb3QoYWVzKHdvcmQsIHN1bSwgZmlsbCA9IHNlbnRpbWVudCkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+c2VudGltZW50LCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnMoeSA9ICJDb250cmlidXRpb24gdG8gc2VudGltZW50IDA1MzAiLAogICAgICAgeCA9IE5VTEwpICsKICB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTE0KSkrCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVpdGkgVEMgTGlnaHQiKSkrIAogIGNvb3JkX2ZsaXAoKQpgYGAKIyMjIOatuOmhnuato+iyoOmdouaWh+eroAoK5LmL5YmN55qE5oOF57eS5YiG5p6Q5aSn6YOo5YiG5piv5YWo6YOo55qE6Kme5b2Z5Yqg57i977yM5o6l5LiL5L6G5bCH5q2j6LKg6Z2i5oOF57eS55qE5paH56ug5YiG6ZaL77yM5Y+v5Lul55m854++5paH56ug5bGs5pa86LKg6Z2i6LyD5aSa44CCCgpgYGB7cn0KIyDkvp3mk5rmg4Xnt5LlgLznmoTmraPosqDmr5TkvovmrbjpoZ7mlofnq6AKYXJ0aWNsZV90eXBlID0gCiAgZGF0YV9zZWxlY3QgJT4lCiAgaW5uZXJfam9pbihMSVdDKSAlPiUgCiAgZ3JvdXBfYnkoYXJ0VXJsLHNlbnRpbWVudCkgJT4lCiAgc3VtbWFyaXNlKGNvdW50PW4oKSkgJT4lCiAgc3ByZWFkKHNlbnRpbWVudCxjb3VudCxmaWxsID0gMCkgJT4lICPmiormraPosqDpnaLmg4Xnt5LlsZXplovvvIznvLrlgLzoo5wwCiAgbXV0YXRlKHR5cGUgPSBjYXNlX3doZW4ocG9zaXRpdmUgPiBuZWdhdGl2ZSB+ICJwb3NpdGl2ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAibmVnYXRpdmUiKSkgJT4lCiAgZGF0YS5mcmFtZSgpIAogIAojIOeci+S4gOS4i+ato+iyoOavlOS+i+eahOaWh+eroOWQhOacieW5vuevhwphcnRpY2xlX3R5cGUgJT4lCiAgZ3JvdXBfYnkodHlwZSkgJT4lCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQpgYGAKCgojIyMg5q2j6LKg5oOF57eS5paH56ug5pW46YeP57Wx6KiI5ZyWCmBgYHtyfQojIAphcnRpY2xlX3R5cGVfZGF0ZSA9IGxlZnRfam9pbihhcnRpY2xlX3R5cGVbLGMoImFydFVybCIsICJ0eXBlIildLCB2YWNjaW5lWyxjKCJhcnRVcmwiLCAiYXJ0RGF0ZSIpXSwgYnkgPSAiYXJ0VXJsIikKCgphcnRpY2xlX3R5cGVfZGF0ZSAlPiUKICBncm91cF9ieShhcnREYXRlLHR5cGUpICU+JQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYXJ0RGF0ZSwgeSA9IGNvdW50LCBmaWxsID0gdHlwZSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikrCiAgc2NhbGVfeF9kYXRlKGxhYmVscyA9IGRhdGVfZm9ybWF0KCIlbS8lZCIpLAogICAgICAgICAgICAgICBsaW1pdHMgPSBhcy5EYXRlKGMoJzIwMjEtMDUtMTAnLCcyMDIxLTA2LTA3JykpCiAgICAgICAgICAgICAgICkKYGBgCjxicj7lj6/ku6XnnIvliLDlnKjntIQ1LzI06Jmf5LmL5b6M77yM6LKg6Z2i5paH56ug5aKe5Yqg5pW46YeP6LyD5aSa44CCPGJyPgoKCuaKiuato+mdouWSjOiyoOmdoueahOaWh+eroOaMkeWHuuS+hu+8jOS4puWSjOaWt+ipnue1kOaenOWQiOS9teOAggpgYGB7cn0KIyBuZWdhdGl2ZV9hcnRpY2xlOmFydFVybCx3b3JkCm5lZ2F0aXZlX2FydGljbGUgPC0KYXJ0aWNsZV90eXBlICU+JQogIGZpbHRlcih0eXBlPT0ibmVnYXRpdmUiKSU+JQogIHNlbGVjdChhcnRVcmwpICU+JQogIGxlZnRfam9pbihkYXRhX3NlbGVjdFssYygiYXJ0VXJsIiwgIndvcmQiKV0sIGJ5ID0gImFydFVybCIpCgojIHBvc2l0aXZlX2FydGljbGU6YXJ0VXJsLHdvcmQKcG9zaXRpdmVfYXJ0aWNsZSA8LQphcnRpY2xlX3R5cGUgJT4lCiAgZmlsdGVyKHR5cGU9PSJwb3NpdGl2ZSIpJT4lCiAgc2VsZWN0KGFydFVybCkgJT4lCiAgbGVmdF9qb2luKGRhdGFfc2VsZWN0WyxjKCJhcnRVcmwiLCAid29yZCIpXSwgYnkgPSAiYXJ0VXJsIikKYGBgCgrlsIfos4fmlpnovYnmj5vngrpEb2N1bWVudCBUZXJtIE1hdHJpeCAoRFRNKQpgYGB7cn0KZHRtIDwtIHZhY2NpbmVfd29yZHMgJT4lIGNhc3RfZHRtKGFydFVybCwgd29yZCwgbikKZHRtCmBgYAoKYGBge3J9Cmluc3BlY3QoZHRtWzE6MTAsMToxMF0pCmBgYAoKCiMgMy4g5Li76aGM5qih5Z6LCiMjIOW7uueri+abtOWkmuS4u+mhjOeahOS4u+mhjOaooeWeiwrlmJfoqaYz44CBNOOAgTXjgIE244CBN+WAi+S4u+mhjOaVuO+8jOWwh+e1kOaenOWtmOi1t+S+hu+8jOWGjeWBmumAsuS4gOatpeWIhuaekOOAggrvvIjmraTpg6jliIbpnIDopoHot5HkuIDmrrXmmYLplpPvvIzlt7LntpPlsIfot5HlroznmoTmqpTmoYjlrZjmiJBsZGFzX3Jlc3VsdC5yZGF0Ye+8jOWPr+S7peebtOaOpei8ieWFpe+8iQpgYGB7cn0KIyBsZGFzID0gYygpCiMgdG9waWNzID0gYygzLDQsNSw2LDcpCiMgZm9yKHRvcGljIGluIHRvcGljcyl7CiMgIHN0YXJ0X3RpbWUgPC0gU3lzLnRpbWUoKQojICBsZGEgPC0gTERBKGR0bSwgayA9IHRvcGljLCBjb250cm9sID0gbGlzdChzZWVkID0gMjAyMSkpCiMgIGxkYXMgPWMobGRhcyxsZGEpCiMgIHByaW50KHBhc3RlKHRvcGljICxwYXN0ZSgidG9waWMocykgYW5kIHVzZSB0aW1lIGlzICIsIFN5cy50aW1lKCkgLXN0YXJ0X3RpbWUpKSkKIyAgc2F2ZShsZGFzLGZpbGUgPSAibGRhc19yZXN1bHRfLnJkYXRhIikgIyDlsIfmqKHlnovovLjlh7rmiJDmqpTmoYgKIyB9CmBgYAoK6LyJ5YWl5YWI5YmN6LeR5aW955qE5q+P5YCL5Li76aGM55qETERB57WQ5p6cCmBgYHtyfQpsb2FkKCJsZGFzX3Jlc3VsdF8ucmRhdGEiKQpgYGAKCiMjIyDpgI/pgY5wZXJwbGV4aXR55om+5Yiw5pyA5L2z5Li76aGM5pW4CmBgYHtyfQp0b3BpY3MgPSBjKDMsNCw1LDYsNykKZGF0YV9mcmFtZShrID0gdG9waWNzLCBwZXJwbGV4ID0gbWFwX2RibChsZGFzLCB0b3BpY21vZGVsczo6cGVycGxleGl0eSkpICU+JQogIGdncGxvdChhZXMoaywgcGVycGxleCkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkV2YWx1YXRpbmcgTERBIHRvcGljIG1vZGVscyIsCiAgICAgICBzdWJ0aXRsZSA9ICJPcHRpbWFsIG51bWJlciBvZiB0b3BpY3MgKHNtYWxsZXIgaXMgYmV0dGVyKSIsCiAgICAgICB4ID0gIk51bWJlciBvZiB0b3BpY3MiLAogICAgICAgeSA9ICJQZXJwbGV4aXR5IikKYGBgCjxicj7kuLvpoYzmlbjmhIjlpJrjgIHopIfpm5zluqbmhIjkvY7jgIHlhaflrrnnmoTntJTluqbmhIjpq5jjgII8YnI+Cjxicj7mjJHpgbjkuIvpmY3luYXluqbotqjnt6nnmoTovYnmipjpu57jgII8YnI+CgojIyMg55Si55SfTERBVmlz57WQ5p6cCmNyZWF0ZSBMREF2aXPmiYDpnIDnmoRqc29uIGZ1bmN0aW9uCuatpGZ1bmN0aW9u5piv5bCH5YmN6Z2i5L2/55SoIOKAnExEQSBmdW5jdGlvbuKAneaJgOW7uueri+eahG1vZGVs77yM6L2J5o+b54K64oCcTERBVmlz4oCd5aWX5Lu255qEaW5wdXTmoLzlvI/jgIIKYGBge3J9CnRvcGljbW9kZWxzX2pzb25fbGRhdmlzIDwtIGZ1bmN0aW9uKGZpdHRlZCwgZG9jX3Rlcm0pewogICAgcmVxdWlyZShMREF2aXMpCiAgICByZXF1aXJlKHNsYW0pCiAgCiAgICAjIyPku6XkuItmdW5jdGlvbiDnlKjkvobop6PmsbrvvIzkuLvpoYzmlbjlpJrmnIPlh7rnj75OQeeahOWVj+mhjAogICAgIyMjIOWPg+iAgyBodHRwczovL2dpdGh1Yi5jb20vY3BzaWV2ZXJ0L0xEQXZpcy9jb21taXQvYzcyMzRkNzExNjhiMWU5NDZhMzYxYmMwMDU5M2JjNWM0YmY4ZTU3ZQogICAgbHNfTERBID0gZnVuY3Rpb24gKHBoaSl7CiAgICAgIGplbnNlblNoYW5ub24gPC0gZnVuY3Rpb24oeCwgeSkgewogICAgICAgIG0gPC0gMC41ICogKHggKyB5KQogICAgICAgIGxocyA8LSBpZmVsc2UoeCA9PSAwLCAwLCB4ICogKGxvZyh4KSAtIGxvZyhtKzFlLTE2KSkpCiAgICAgICAgcmhzIDwtIGlmZWxzZSh5ID09IDAsIDAsIHkgKiAobG9nKHkpIC0gbG9nKG0rMWUtMTYpKSkKICAgICAgICAwLjUgKiBzdW0obGhzKSArIDAuNSAqIHN1bShyaHMpCiAgICAgIH0KICAgICAgZGlzdC5tYXQgPC0gcHJveHk6OmRpc3QoeCA9IHBoaSwgbWV0aG9kID0gamVuc2VuU2hhbm5vbikKICAgICAgcGNhLmZpdCA8LSBzdGF0czo6Y21kc2NhbGUoZGlzdC5tYXQsIGsgPSAyKQogICAgICBkYXRhLmZyYW1lKHggPSBwY2EuZml0WywgMV0sIHkgPSBwY2EuZml0WywgMl0pCiAgICB9CiAgCiAgICAgICMgRmluZCByZXF1aXJlZCBxdWFudGl0aWVzCiAgICAgIHBoaSA8LSBhcy5tYXRyaXgocG9zdGVyaW9yKGZpdHRlZCkkdGVybXMpCiAgICAgIHRoZXRhIDwtIGFzLm1hdHJpeChwb3N0ZXJpb3IoZml0dGVkKSR0b3BpY3MpCiAgICAgIHZvY2FiIDwtIGNvbG5hbWVzKHBoaSkKICAgICAgdGVybV9mcmVxIDwtIHNsYW06OmNvbF9zdW1zKGRvY190ZXJtKQogIAogICAgICAjIENvbnZlcnQgdG8ganNvbgogICAgICBqc29uX2xkYSA8LSBMREF2aXM6OmNyZWF0ZUpTT04ocGhpID0gcGhpLCB0aGV0YSA9IHRoZXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdm9jYWIgPSB2b2NhYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvYy5sZW5ndGggPSBhcy52ZWN0b3IodGFibGUoZG9jX3Rlcm0kaSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVybS5mcmVxdWVuY3kgPSB0ZXJtX2ZyZXEsIG1kcy5tZXRob2QgPSBsc19MREEpCiAgCiAgICAgIHJldHVybihqc29uX2xkYSkKfQpgYGAKCm1hY+ihqOePvueahOmDqOWIhgpgYGB7cn0KIyB0aGVfbGRhID0gbGRhc1tbM11dCiMganNvbl9yZXMgPC0gdG9waWNtb2RlbHNfanNvbl9sZGF2aXModGhlX2xkYSxkdG0pIAojIHNlclZpcyhqc29uX3JlcyxvcGVuLmJyb3dzZXIgPSBUKQpgYGAKCiMjIyDnlKLnlJ9MREF2aXPmqpTmoYjvvIzlrZjoh7Nsb2NhbOerrwpgYGB7cn0KIyBzZXJWaXMoanNvbl9yZXMsIG91dC5kaXIgPSAidmlzIiwgb3Blbi5icm93c2VyID0gVCkKIyB3cml0ZUxpbmVzKGljb252KHJlYWRMaW5lcygiLi92aXMvbGRhLmpzb24iKSwgdG8gPSAiVVRGOCIpKQpgYGAKCiFbXSg1LnBuZykKPGJyPuaIkeWAkeWQjOaZguS5n+eUqOS6hkxEQVZpc+aJvuS6huacgOS9s+S4u+mhjOaVuO+8jOaDs+imgeWGjeasoempl+itiTXlgIvkuLvpoYzmmK/lvojliIbplovvvIzorYnmmI7lj6/mjqHnlKg15YCL5Li76aGM5pW444CCPGJyPgo8YnI+bGFtZGHlgLzpgbgwLjHvvIzlm6DngrrmhIjlsI/nmoTlgLzvvIzlrZfmhIjnjajnibnjgII8YnI+CgojIyMg6YG45a6aNeWAi+S4u+mhjOaVuOeahOS4u+mhjOaooeWeiwpgYGB7cn0KdGhlX2xkYSA9IGxkYXNbWzNdXQpgYGAKCmBgYHtyfQp0b3BpY3Nfd29yZHMgPC0gdGlkeSh0aGVfbGRhLCBtYXRyaXggPSAiYmV0YSIpICPms6jmhI/vvIHlnKh0aWR5IGZ1bmN0aW9u6KOh6Z2i6KaB5L2/55SoImJldGEi5L6G5Y+W5Ye6UGhp55+p6Zmj44CCCmNvbG5hbWVzKHRvcGljc193b3JkcykgPC0gYygidG9waWMiLCAidGVybSIsICJwaGkiKQp0b3BpY3Nfd29yZHMgJT4lIGFycmFuZ2UoZGVzYyhwaGkpKSAlPiUgaGVhZCgxMCkKYGBgCgojIyMgdGVybXPkvp3nhaflkITkuLvpoYznmoRwaGnlgLznlLHlpKfliLDlsI/mjpLluo8KYGBge3J9CnRvcGljc193b3JkcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oMTAsIHBoaSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXJfd2l0aGluKHRlcm0scGhpLHRvcGljKSwgeSA9IHBoaSwgZmlsbCA9IGFzLmZhY3Rvcih0b3BpYykpKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVpdGkgVEMgTGlnaHQiKSkrCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpICsKICBzY2FsZV94X3Jlb3JkZXJlZCgpCmBgYAoKIyMjIOWOu+mZpOWFsemAmuipnuW9mQpgYGB7cn0KcmVtb3ZlZF93b3JkID0gYygi55ar6IuXIiwi5Y+w54GjIiwibXkiLCLnlqvmg4UiLCJzZW50IiwianB0dCIsIm9uIiwiZnJvbSIsIuWci+WutiIsIuWci+eUoiIsIuWVj+mhjCIsIuS4reWciyIsIuacieaykuaciSIsIuS4jeWIsCIsIumrmOerryIsIuWci+WkliIsIuizvOiytyIpCgp0b3BpY3Nfd29yZHMgJT4lCiAgZmlsdGVyKCF0ZXJtICAlaW4lIHJlbW92ZWRfd29yZCkgJT4lCiAgZ3JvdXBfYnkodG9waWMpICU+JQogIHRvcF9uKDEwLCBwaGkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyX3dpdGhpbih0ZXJtLHBoaSx0b3BpYyksIHkgPSBwaGksIGZpbGwgPSBhcy5mYWN0b3IodG9waWMpKSkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlaXRpIFRDIExpZ2h0IikpKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH4gdG9waWMsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKSArCiAgc2NhbGVfeF9yZW9yZGVyZWQoKQpgYGAKCiMjIyDkuLvpoYzlkb3lkI0KYGBge3J9CiMgdG9waWNzX25hbWUgPSBjKCLlkITlrrbnlqvoi5fmr5TovIMiLCLlnIvnlKLnlqvoi5fnoJTnmbzoqabpqZciLCLlnIvnlKLnlqvoi5fog4zlvoznmoTmlL/msrsiLCLnlqvoi5fmjqXnqK4iLCLnlqvoi5fmjqHos7wiKQp0b3BpY3NfbmFtZSA9IGMoIuWQhOWci+i0iOmAgeeWq+iLlyIsIueWq+iLl+eglOeZvOaKgOihk+ippumplyIsIuWci+eUoueWq+iLl+iDjOW+jOeahOaUv+ayuyIsIueWq+iLl+aOpeeoriIsIueWq+iLl+aOoeizvCIpCmBgYAoKRG9jdW1lbnQg5Li76aGM5YiG5L2ICmBgYHtyfQojIGZvciBldmVyeSBkb2N1bWVudCB3ZSBoYXZlIGEgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIG9mIGl0cyBjb250YWluZWQgdG9waWNzCnRtUmVzdWx0IDwtIHBvc3Rlcmlvcih0aGVfbGRhKQpkb2NfcHJvIDwtIHRtUmVzdWx0JHRvcGljcwpkb2N1bWVudF90b3BpY3MgPC0gZG9jX3Byb1t2YWNjaW5lJGFydFVybCxdCmRvY3VtZW50X3RvcGljc19kZiA9ZGF0YS5mcmFtZShkb2N1bWVudF90b3BpY3MpCmNvbG5hbWVzKGRvY3VtZW50X3RvcGljc19kZikgPSB0b3BpY3NfbmFtZQpyb3duYW1lcyhkb2N1bWVudF90b3BpY3NfZGYpID0gTlVMTApwdHRfdG9waWMgPSBjYmluZCh2YWNjaW5lLGRvY3VtZW50X3RvcGljc19kZikKCiMg5Yiq6ZmkY29tbWVudE51beOAgXB1c2jjgIFib2/mrITkvY0KcHR0X3RvcGljJGNvbW1lbnROdW0gPSBOVUxMCnB0dF90b3BpYyRwdXNoID0gTlVMTApwdHRfdG9waWMkYm9vID0gTlVMTApgYGAKCiMjIyDmn6XnnIvnibnlrprkuLvpoYznmoTmlofnq6AK6YCP6YGO5om+5Yiw54m55a6a5paH56ug55qE5YiG5L2I6YCy6KGM5o6S5bqP5LmL5b6M77yM5Y+v5Lul55yL5Yiw5q2k5Li76aGM55qE5q+U6YeN6auY55qE5paH56ug5Zyo6KiO6KuW5LuA6bq877yM5Lmf5Y+v5Lul5L6d5pOa5paH56ug5YWn5a655L6G6Kq/5pW05ZG95ZCN44CCCmBgYHtyfQpwdHRfdG9waWMgJT4lCiAgYXJyYW5nZShkZXNjKGDlkITlnIvotIjpgIHnlqvoi5dgKSkgJT4laGVhZCgxMCkKCnB0dF90b3BpYyAlPiUKICBhcnJhbmdlKGRlc2MoYOeWq+iLl+eglOeZvOaKgOihk+ippumpl2ApKSAlPiVoZWFkKDEwKQoKcHR0X3RvcGljICU+JQogIGFycmFuZ2UoZGVzYyhg5ZyL55Si55ar6IuX6IOM5b6M55qE5pS/5rK7YCkpICU+JWhlYWQoMTApCgpwdHRfdG9waWMgJT4lCiAgYXJyYW5nZShkZXNjKGDnlqvoi5fmjqXnqK5gKSkgJT4laGVhZCgxMCkKCnB0dF90b3BpYyAlPiUKICBhcnJhbmdlKGRlc2MoYOeWq+iLl+aOoeizvGApKSAlPiVoZWFkKDEwKQpgYGAKCiMjIyDmm7TmlLnkuLvpoYzlkb3lkI0KYGBge3J9CiMg5pu05pS55Li76aGMMeOAgTLlkI3nqLEKIyB0b3BpY3NfbmFtZSA9IGMoIuWQhOWci+i0iOmAgeeWq+iLlyIsIueWq+iLl+eglOeZvOaKgOihk+ippumplyIsIuWci+eUoueWq+iLl+iDjOW+jOeahOaUv+ayuyIsIueWq+iLl+aOpeeoriIsIueWq+iLl+aOoeizvCIpCmBgYAoKIyMjIOS6huino+S4u+mhjOWcqOaZgumWk+eahOiuiuWMlijljrvpmaTnrYbmlbjlsJHmnIjku70pCmBgYHtyfQoj5Y676Zmk562G5pW4PDMwMApwdHRfdG9waWMgJT4lCiAgbXV0YXRlKGFydERhdGUgPSBhcy5EYXRlKGFydERhdGUpKSAlPiUgCiAgZmlsdGVyKCAhZm9ybWF0KGFydERhdGUsJyVZJW0lZCcpICVpbiUgYygyMDIxMDUxMSwgMjAyMTA1MTIsIDIwMjEwNTEzLCAyMDIxMDUxNCwgMjAyMTA1MTUsIDIwMjEwNTE2LCAyMDIxMDUxNywgMjAyMTA1MTgsIDIwMjEwNTE5LDIwMjEwNTIwLDIwMjEwNTIxLDIwMjEwNTIyLDIwMjEwNTIzLDIwMjEwNTI0KSkgJT4lCiAgZ3JvdXBfYnkoYXJ0RGF0ZSA9IGZvcm1hdChhcnREYXRlLCclWSVtJWQnKSkgJT4lCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIHN1bSwgbmEucm0gPSBUUlVFKSAlPiUKICBtZWx0KGlkLnZhcnMgPSAiYXJ0RGF0ZSIpJT4lCiAgZ2dwbG90KCBhZXMoeD1hcnREYXRlLCB5PXZhbHVlLCBmaWxsPXZhcmlhYmxlKSkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlaXRpIFRDIExpZ2h0IikpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHlsYWIoInZhbHVlIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygiI2NhY2FjYSIsIiNhOWM2ZGUiLCIjNTU4OGEzIiwiIzE0NTM3NCIsInJlZCIpKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpgYGAKCiMjIyDku6Xmr5Tkvovkuobop6PkuLvpoYzmmYLplpPororljJYKYGBge3J9CnB0dF90b3BpYyAlPiUKICBtdXRhdGUoYXJ0RGF0ZSA9IGFzLkRhdGUoYXJ0RGF0ZSkpICU+JSAKICBmaWx0ZXIoICFmb3JtYXQoYXJ0RGF0ZSwnJVklbScpICVpbiUgYygyMDIxMDUxMSwgMjAyMTA1MTIsIDIwMjEwNTEzLCAyMDIxMDUxNCwgMjAyMTA1MTUsIDIwMjEwNTE2LCAyMDIxMDUxNywgMjAyMTA1MTgsIDIwMjEwNTE5LDIwMjEwNTIwLDIwMjEwNTIxLDIwMjEwNTIyLDIwMjEwNTIzLDIwMjEwNTI0KSkgJT4lCiAgZ3JvdXBfYnkoYXJ0RGF0ZSA9IGZvcm1hdChhcnREYXRlLCclWSVtJWQnKSkgJT4lCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIHN1bSwgbmEucm0gPSBUUlVFKSAlPiUKICBtZWx0KGlkLnZhcnMgPSAiYXJ0RGF0ZSIpJT4lCiAgZ3JvdXBfYnkoYXJ0RGF0ZSklPiUKICBtdXRhdGUodG90YWxfdmFsdWUgPXN1bSh2YWx1ZSkpJT4lCiAgZ2dwbG90KCBhZXMoeD1hcnREYXRlLCB5PXZhbHVlL3RvdGFsX3ZhbHVlLCBmaWxsPXZhcmlhYmxlKSkgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlaXRpIFRDIExpZ2h0IikpKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHlsYWIoInByb3BvcnRpb24iKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjY2FjYWNhIiwiI2E5YzZkZSIsIiM1NTg4YTMiLCIjMTQ1Mzc0IiwicmVkIikpKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCmBgYArlj6/ku6XnnIvlh7rlnIvpmpvnlqvoi5fog4zlvoznmoTmlL/msrvkuIDnm7TmmK/lgIvmlofnq6DoqI7oq5bnmoTph43pu54KCiMjIyDmoLnmk5rlkITkuLvpoYzku6PooajlrZfnlatncmFwaCjlt7LljrvpmaTlhbHlkIzlrZcpCmBgYHtyfQpyZW1vdmVkX3dvcmQgPSBjKCLnlqvoi5ciLCLlj7DngaMiLCJteSIsIueWq+aDhSIsInNlbnQiLCJqcHR0Iiwib24iLCJmcm9tIiwi5ZyL5a62Iiwi5ZyL55SiIiwi5ZWP6aGMIiwi5Lit5ZyLIiwi5pyJ5rKS5pyJIiwi5LiN5YiwIiwi6auY56uvIiwi5ZyL5aSWIiwi6LO86LK3IikKCnBoaV9tIDwtIHRvcGljc193b3JkcyAlPiUKICBmaWx0ZXIoIXRlcm0gICVpbiUgcmVtb3ZlZF93b3JkKSAlPiUgCiAgYXJyYW5nZShkZXNjKHBoaSkpICU+JSAKICB0b3Bfbig3MCkKCmR0bV8gPC1waGlfbSAlPiUgY2FzdF9kdG0odG9waWMsIHRlcm0sIHBoaSkKCmR0bW08LWFzLm1hdHJpeChkdG1fKQpkaW0oZHRtbSkKYGBgCgpgYGB7cn0KbmV0d29yaz1ncmFwaF9mcm9tX2luY2lkZW5jZV9tYXRyaXgoZHRtbSkKCiMgcGxvdApzZXQuc2VlZCgzKQoKcGxvdChuZXR3b3JrLCB5bGltPWMoLTEsMSksIHhsaW09YygtMSwxKSwgYXNwID0gMCwKICAgICB2ZXJ0ZXgubGFiZWwuY2V4PTAuNyx2ZXJ0ZXguc2l6ZT0xMCx2ZXJ0ZXgubGFiZWwuZmFtaWx5ID0gIkhlaXRpIFRDIExpZ2h0IikKYGBgCgojIDQuIOekvue+pOe2sui3r+WclgojIyDlj5blh7rku6PooajkuLvpoYwodG9waWMpCuavj+evh+aWh+eroOaLv2dhbW1h5YC85pyA5aSn55qEdG9waWPnlbboqbLmlofnq6DnmoR0b3BpYwpgYGB7cn0KIyDlnKh0aWR5IGZ1bmN0aW9u5Lit5L2/55So5Y+D5pW4ImdhbW1hIuS+huWPluW+lyB0aGV0YeefqemZowp2YWNjaW5lX3RvcGljcyA8LSB0aWR5KHRoZV9sZGEsIG1hdHJpeD0iZ2FtbWEiKSAlPiUgIyBkb2N1bWVudCB0b3BpYyBnYW1tYQogICAgICAgICAgICAgICAgICBncm91cF9ieShkb2N1bWVudCkgJT4lCiAgICAgICAgICAgICAgICAgIHRvcF9uKDEsIHd0PWdhbW1hKQp2YWNjaW5lX3RvcGljcwpgYGAKCuizh+aWmeWQiOS9te+8iOaWh+eroOWSjOeVmeiogO+8iQpgYGB7cn0KIyDmlofnq6DlkoznlZnoqIAKcmV2aWV3cyA8LSByZXZpZXdzICU+JQogICAgICBzZWxlY3QoYXJ0VXJsLCBjbXRQb3N0ZXIsIGNtdFN0YXR1cywgY210Q29udGVudCkKcG9zdHNfUmV2aWV3cyA8LSBtZXJnZSh4ID0gdmFjY2luZSwgeSA9IHJldmlld3MsIGJ5ID0gImFydFVybCIpCgojIOaKiuaWh+eroOWSjHRvcGlj5ZCI5L21CnBvc3RzX1Jldmlld3MgPC0gbWVyZ2UoeCA9IHBvc3RzX1Jldmlld3MsIHkgPSB2YWNjaW5lX3RvcGljcywgYnkueCA9ICJhcnRVcmwiLCBieS55PSJkb2N1bWVudCIpCgojIGhlYWQocG9zdHNfUmV2aWV3cywzKQpgYGAKCuWPluWHuiBjbXRQb3N0ZXIo5Zue6KaG6ICFKeOAgWFydFBvc3RlcijnmbzmlofogIUp44CBYXJ0VXJsKOaWh+eroOmAo+e1kCkg5LiJ5YCL5qyE5L2NCmBgYHtyfQpsaW5rIDwtIHBvc3RzX1Jldmlld3MgJT4lIHNlbGVjdChjbXRQb3N0ZXIsIGFydFBvc3RlciwgYXJ0VXJsKQpoZWFkKGxpbmssMykKYGBgCgojIyMg6LOH5paZ56+p6YG4Cuizh+aWmeevqemBuOeahOaWueW8j++8miAKKyDmlofnq6A6IOaWh+eroOaXpeacn+OAgeeVmeiogOaVuChjb21tZW50TnVtKSAKKyBsaW5r44CBbm9kZTogZGVncmVlCmBgYHtyfQojIOeci+eVmeiogOaVuOWkp+amgumDveWkmuWwkSjmlrnkvr/lvozpnaLnr6npgbgpCnZhY2NpbmUgJT4lCiAgZmlsdGVyKGNvbW1lbnROdW0+MTAwKSAlPiUKICBnZ3Bsb3QoYWVzKHg9Y29tbWVudE51bSkpICsgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKPGJyPuaWh+eroOWbnuimhuaVuOmHj+WcqDUwMOS7peW+jOWwseiuiuWwke+8jOaJgOS7peWPr+S7peaKkzUwMOeCuuaWt+m7nuOAgjxicj4KCuS+neaTmueZvOaWh+aVuOaIluWbnuimhuaVuOevqemBuHBvc3TlkoxyZXZpZXcKYGBge3J9CiMg5biz6Jmf55m85paH56+H5pW4CnBvc3RfY291bnQgPSB2YWNjaW5lICU+JQogICBncm91cF9ieShhcnRQb3N0ZXIpICU+JQogICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogICBhcnJhbmdlKGRlc2MoY291bnQpKQoKcG9zdF9jb3VudApgYGAKCmBgYHtyfQojIOW4s+iZn+Wbnuimhue4veaVuApyZXZpZXdfY291bnQgPSByZXZpZXdzICU+JQogICBncm91cF9ieShjbXRQb3N0ZXIpICU+JQogICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogICBhcnJhbmdlKGRlc2MoY291bnQpKQoKcmV2aWV3X2NvdW50CmBgYAojIyMg56+p6YG4cG9zdOWkp+aWvDEw56+H55m85paH6ICF5Y+KcmV2aWV35aSn5pa8MzAw5YmH55WZ6KiA6ICFCisg55m85paH6ICF77yaNDc1MeS6uiAKKyDlm57opobogIXvvJo0ODI5M+S6ugorIOe4veWPg+iIh+S6uuaVuO+8mjQ5Mzkx5Lq6CgpgYGB7cn0KIyDnmbzmlofogIUKcG9zdGVyX3NlbGVjdCA8LSBwb3N0X2NvdW50ICU+JSBmaWx0ZXIoY291bnQgPj0gMTApCnZhY2NpbmUgPC0gdmFjY2luZSAlPiUgIGZpbHRlcih2YWNjaW5lJGFydFBvc3RlciAlaW4lIHBvc3Rlcl9zZWxlY3QkYXJ0UG9zdGVyKQoKIyDlm57opobogIUKcmV2aWV3ZXJfc2VsZWN0IDwtIHJldmlld19jb3VudCAlPiUgIGZpbHRlcihjb3VudCA+PSAzMDApCnJldmlld3MgPC0gcmV2aWV3cyAlPiUgIGZpbHRlcihyZXZpZXdzJGNtdFBvc3RlciAlaW4lIHJldmlld2VyX3NlbGVjdCRjbXRQb3N0ZXIpCmBgYAoKCmBgYHtyfQojIOaqouimluWPg+iIh+S6uuaVuApsZW5ndGgodW5pcXVlKHBvc3RzX1Jldmlld3MkYXJ0UG9zdGVyKSkgIyDnmbzmlofogIXmlbjph48gNDc1MQpsZW5ndGgodW5pcXVlKHBvc3RzX1Jldmlld3MkY210UG9zdGVyKSkgIyDlm57opobogIXmlbjph48gNDgyOTMKYGBgCgpgYGB7cn0KYWxsUG9zdGVyIDwtIGMocG9zdHNfUmV2aWV3cyRhcnRQb3N0ZXIsIHBvc3RzX1Jldmlld3MkY210UG9zdGVyKSAjIOe4veWPg+iIh+S6uuaVuCA0OTM5MQpsZW5ndGgodW5pcXVlKGFsbFBvc3RlcikpCmBgYAoK5qiZ6KiY5omA5pyJ5Ye654++6YGO55qE5L2/55So6ICFCgorIHBvc3Rlcu+8muWPqueZvOmBjuaWh+OAgeeZvOmBjuaWhyvnlZnpgY7oqIAgCgorIHJlcGx5ZXLvvJrlj6rnlZnpgY7oqIAKYGBge3J9CnVzZXJMaXN0IDwtIGRhdGEuZnJhbWUodXNlcj11bmlxdWUoYWxsUG9zdGVyKSkgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKHR5cGU9aWZlbHNlKHVzZXIlaW4ldmFjY2luZSRhcnRQb3N0ZXIsICJwb3N0ZXIiLCAicmVwbHllciIpKQoKaGVhZCh1c2VyTGlzdCwzKQpgYGAKCiMjIyDku6XkuLvpoYznr6npgbjnpL7nvqQKKyDmipNsaW5rCuevqemBuOS4gOevh+aWh+eroOWbnuimhjE15qyh5Lul5LiK6ICF77yM5LiU5paH56ug55WZ6KiA5pW45aSa6aSYNTAw5YmH77yMIOaWh+eroOS4u+mhjOatuOmhnueCujIo55ar6IuX56CU55m85oqA6KGT6Kmm6amXKeiIhzMo5ZyL55Si55ar6IuX6IOM5b6M55qE5pS/5rK7KeiAhe+8jCDmrITkvY3lj6rlj5bvvJpjbXRQb3N0ZXIo6KmV6KuW6ICFKSwgYXJ0UG9zdGVyKOeZvOaWh+iAhSksIGFydFVybCjmlofnq6DpgKPntZApLCB0b3BpYyjkuLvpoYwp44CCCmBgYHtyfQpsaW5rID0gcG9zdHNfUmV2aWV3cyAlPiUKICAgICAgZ3JvdXBfYnkoY210UG9zdGVyLCBhcnRVcmwpICU+JSAKICAgICAgZmlsdGVyKG4oKT4xNSkgJT4lCiAgICAgIGZpbHRlcihjb21tZW50TnVtID4gNTAwKSAlPiUKICAgICAgZmlsdGVyKHRvcGljID09IDIgfCB0b3BpYyA9PSAzKSAlPiUgCiAgICAgIHNlbGVjdChjbXRQb3N0ZXIsIGFydFBvc3RlciwgYXJ0VXJsLCB0b3BpYykgJT4lIAogICAgICB1bmlxdWUoKQoKbGluawpgYGAKCisg5oqTbm9kZXMg5Zyo5omA5pyJ55qE5L2/55So6ICF6KOh6Z2i77yM56+p6YG4bGlua+S4reacieWHuuePvueahOS9v+eUqOiAhQpgYGB7cn0KZmlsdGVyZWRfdXNlciA8LSB1c2VyTGlzdCAlPiUKICAgICAgICAgIGZpbHRlcih1c2VyJWluJWxpbmskY210UG9zdGVyIHwgdXNlciVpbiVsaW5rJGFydFBvc3RlcikgJT4lCiAgICAgICAgICBhcnJhbmdlKGRlc2ModHlwZSkpCgpoZWFkKGZpbHRlcmVkX3VzZXIsMykKYGBgCgojIyMg5L2/55So6ICF57aT5bi45Y+D6IiH55qE5paH56ug56iu6aGeCmBgYHtyfQpmaWx0ZXJfZGVncmVlID0gNQoKIyDlu7rnq4vntrLot6/pl5zkv4IKcmV2aWV3TmV0d29yayA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZD1saW5rLCB2PWZpbHRlcmVkX3VzZXIsIGRpcmVjdGVkPUYpCgojIOS+neaTmuS9v+eUqOiAhei6q+S7veWwjem7numAsuihjOS4iuiJsgpsYWJlbHMgPC0gZGVncmVlKHJldmlld05ldHdvcmspClYocmV2aWV3TmV0d29yaykkbGFiZWwgPC0gbmFtZXMobGFiZWxzKQpWKHJldmlld05ldHdvcmspJGNvbG9yIDwtIGlmZWxzZShWKHJldmlld05ldHdvcmspJHR5cGU9PSJwb3N0ZXIiLCAicmVkIiwgImJsdWUiKQoKIyDkvp3mk5rlm57opobnmbznlJ/nmoTmlofnq6DmiYDlsI3mh4nnmoTkuLvpoYzvvIzlsI3ku5blgJHnmoTpl5zoga/nt5rpgLLooYzkuIroibIKRShyZXZpZXdOZXR3b3JrKSRjb2xvciA8LSBpZmVsc2UoRShyZXZpZXdOZXR3b3JrKSR0b3BpYyA9PSAiMiIsICJwYWxldmlvbGV0cmVkIiwgImxpZ2h0Z3JlZW4iKQoKIyDnlavlh7rnpL7nvqTntrLot6/lnJYoZGVncmVlPjXnmoTmiY3nlavlh7rkvoYpCnNldC5zZWVkKDU0MzIpCnBhcihmYW1pbHk9KCdIZWl0aSBUQyBMaWdodCcpKQpwbG90KHJldmlld05ldHdvcmssIHZlcnRleC5zaXplPTUsIGVkZ2Uud2lkdGg9MywgdmVydGV4LmxhYmVsLmRpc3Q9MSwKICAgICB2ZXJ0ZXgubGFiZWw9aWZlbHNlKGRlZ3JlZShyZXZpZXdOZXR3b3JrKSA+IGZpbHRlcl9kZWdyZWUsIFYocmV2aWV3TmV0d29yaykkbGFiZWwsIE5BKSx2ZXJ0ZXgubGFiZWwuZm9udD0yKQoKIyDliqDlhaXmqJnnpLoKbGVnZW5kKCJib3R0b21yaWdodCIsIGMoIueZvOaWh+iAhSIsIuWbnuaWh+iAhSIpLCBwY2g9MjEsIAogIGNvbD0iIzc3Nzc3NyIsIHB0LmJnPWMoInJlZCIsImJsdWUiKSwgcHQuY2V4PTEsIGNleD0xKQpsZWdlbmQoInRvcGxlZnQiLCBjKCLnlqvoi5fnoJTnmbzmioDooZPoqabpqZciLCLlnIvnlKLnlqvoi5fog4zlvoznmoTmlL/msrsiKSwgCiAgICAgICBjb2w9YygicGFsZXZpb2xldHJlZCIsICJsaWdodGdyZWVuIiksIGx0eT0xLCBjZXg9MC42KQpgYGAK5Y+v5Lul5om+5Ye677yaCgrkuLvpoYwy44CM55ar6IuX56CU55m85oqA6KGT6Kmm6amX44CN55qE5Li76KaB55m85paH6ICF5pyJ77yaQ2F2ZW5kaXNoSnIKCuS4u+mhjDPjgIzlnIvnlKLnlqvoi5fog4zlvoznmoTmlL/msrvjgI3kuLvpoYznmoTkuLvopoHnmbzmlofogIXmnInvvJpDT0NPQ0NDCgrjgIznlqvoi5fnoJTnmbzmioDooZPoqabpqZfjgI3nmoTkuLvopoHlm57mlofogIXmnInvvJpKdXN0aW5YROOAgXdldAoK44CM5ZyL55Si55ar6IuX6IOM5b6M55qE5pS/5rK744CN5Li76aGM55qE5Li76KaB5Zue5paH6ICF5pyJ77yaeXVsZTEyMjTjgIFicmF2ZXJ5aHlkZeOAgXdlYWtlcm1hbuOAgXNtYWxsbWluaGFoYeOAgUFBU295bWlsawoKcmFzaWVsMDkxOeWJh+aYr+WFqeeoruS4u+mhjOmDveacieWbnuimhgoKIyA1LiDmhI/opovpoJjoopbliIbmnpDvvJpDYXZlbmRpc2hKcuOAgUNPQ09DQ0MKIyMg5pW055CG57ay6Lev6Zec5L+C6ICFCgpgYGB7cn0KVmFjX2xlYWRlcl9kYXRhIDwtIHBvc3RzX1Jldmlld3MgJT4lIAogIGZpbHRlcigoYXJ0UG9zdGVyID09ICJDYXZlbmRpc2hKciIpfChhcnRQb3N0ZXIgPT0gIkNPQ09DQ0MiKSkgCgpWYWNfbGVhZGVyX2RhdGEkYXJ0RGF0ZSA9IGFzLkRhdGUoVmFjX2xlYWRlcl9kYXRhJGFydERhdGUpClZhY19sZWFkZXJfZGF0YSA9IFZhY19sZWFkZXJfZGF0YSAlPiUgbXV0YXRlKG1vbnRocyA9IGFzLkRhdGUoY3V0KGFydERhdGUsICJtb250aHMiKSkpCmBgYAoKYGBge3J9ClZhY19DYXZlbmRpc2hKciA8LSBzdWJzZXQoVmFjX2xlYWRlcl9kYXRhLGFydFBvc3RlciA9PSAiQ2F2ZW5kaXNoSnIiKQpWYWNfQ09DT0NDQyA8LSBzdWJzZXQoVmFjX2xlYWRlcl9kYXRhLGFydFBvc3RlciA9PSAiQ09DT0NDQyIpClZhY1JXX0NhdmVuZGlzaEpyIDwtIHN1YnNldChWYWNfbGVhZGVyX2RhdGEsYXJ0UG9zdGVyID09ICJDYXZlbmRpc2hKciIpClZhY1JXX0NPQ09DQ0MgPC0gc3Vic2V0KFZhY19sZWFkZXJfZGF0YSxhcnRQb3N0ZXIgPT0gIkNPQ09DQ0MiKQpgYGAKCkNhdmVuZGlzaEpy5paH56ug5Lit6Kme5b2Z55u46Zec5oCn55qE5YmN6JmV55CGCmBgYHtyfQpWYWMxX0NhdmVuZGlzaEpyIDwtIFZhY19DYXZlbmRpc2hKciAlPiUgCiAgc2VsZWN0KGFydFVybCxzZW50ZW5jZSkKClZhYzFfQ2F2ZW5kaXNoSnIgPC0gc3Ryc3BsaXQoVmFjMV9DYXZlbmRpc2hKciRzZW50ZW5jZSwiW+OAgu+8ge+8m++8nyE/O10iKQoKIyDlsIfmr4/lgIvlj6XlrZDlkoznm7jlsI3mh4nnmoTmlofnq6DphY3lsI3otbfkvobvvIzmlbTnkIbmiJAgZGF0YWZyYW1lClZhYzFfQ2F2ZW5kaXNoSnIgIDwtIGRhdGEuZnJhbWUoCiAgYXJ0VXJsID0gcmVwKFZhY19DYXZlbmRpc2hKciRhcnRVcmwsIHNhcHBseShWYWMxX0NhdmVuZGlzaEpyLCBsZW5ndGgpKSwgCiAgc2VudGVuY2UgPSB1bmxpc3QoVmFjMV9DYXZlbmRpc2hKcikpClZhYzFfQ2F2ZW5kaXNoSnIkc2VudGVuY2UgPC0gYXMuY2hhcmFjdGVyKFZhYzFfQ2F2ZW5kaXNoSnIkc2VudGVuY2UpCgpqaWViYV90b2tlbml6ZXIgPC0gd29ya2VyKHVzZXI9InZhY2NpbmUudHh0Iiwgc3RvcF93b3JkID0gInN0b3Bfd29yZHMudHh0IikKCkNhdmVuZGlzaEpyX3dvcmQgPC0gVmFjMV9DYXZlbmRpc2hKciAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnRlbmNlLCB0b2tlbj1jaGlfdG9rZW5pemVyKSAlPiUKICBjb3VudChhcnRVcmwsIHdvcmQsIHNvcnQgPSBUUlVFKQpgYGAKCiMjIyBDYXZlbmRpc2hKcuaWh+eroOS4reipnuW9memWk+eahOebuOmXnOaApwpgYGB7cn0KIyDoqIjnrpflhanlgIvoqZ7lvZnplpPnmoTnm7jpl5zmgKcKQ2F2ZW5kaXNoSnJfd29yZF9jb3JzIDwtIENhdmVuZGlzaEpyX3dvcmQgJT4lCiAgZ3JvdXBfYnkod29yZCkgJT4lCiAgZmlsdGVyKG4oKSA+PSAzKSAlPiUKICBwYWlyd2lzZV9jb3Iod29yZCwgYXJ0VXJsLCBzb3J0ID0gVFJVRSkKCkNhdmVuZGlzaEpyX3dvcmRfY29ycyAlPiUKICBmaWx0ZXIoY29ycmVsYXRpb24gPiAwLjYpICU+JQogIGdyYXBoX2Zyb21fZGF0YV9mcmFtZSgpICU+JQogIGdncmFwaChsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBjb3JyZWxhdGlvbiksIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgZ2VvbV9ub2RlX3BvaW50KGNvbG9yID0gImxpZ2h0Ymx1ZSIsIHNpemUgPSAzKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbmFtZSksIHJlcGVsID0gVFJVRSwgZmFtaWx5ID0gIkhlaXRpIFRDIExpZ2h0IikgKwogIHRoZW1lX3ZvaWQoKQpgYGAKCuS4u+mhjDLjgIznlqvoi5fnoJTnmbzmioDooZPoqabpqZfjgI3nmoTkuLvopoHnmbzmlofogIXmnInvvJpDYXZlbmRpc2hKcgoKQ2F2ZW5kaXNoSnJfc2VuX3Bsb+aWh+eroOS4re+8jOeZvOaWh+iAheWSjOWbnuaWh+iAheaDhee3kuWIhuaekOS5i+izh+aWmeWJjeiZleeQhgrmupblgplDYXZlbmRpc2hKcueahOWtl+WFuApgYGB7cn0KQ2F2ZW5kaXNoSnJfTElXQyA8LSBDYXZlbmRpc2hKcl93b3JkICU+JSAKICBpbm5lcl9qb2luKExJV0MpICU+JSAKICBncm91cF9ieShhcnRVcmwsc2VudGltZW50KSAlPiUKICBzdW1tYXJpc2UoY291bnQ9c3VtKG4pKQpgYGAK5pW055CGUmVpdmV3LUNhdmVuZGlzaEpyCmBgYHtyfQpDYXZlbmRpc2hKcl9MSVdDIDwtQ2F2ZW5kaXNoSnJfTElXQyU+JSAKICBzcHJlYWQoc2VudGltZW50LCBjb3VudCwgZmlsbCA9IDApCgpjb250X1ZhY1JXX0NhdmVuZGlzaEpyIDwtIFZhY1JXX0NhdmVuZGlzaEpyICU+JSAKICBzZWxlY3QoYXJ0VXJsLGNtdENvbnRlbnQpCgpjb250X1ZhY1JXX0NhdmVuZGlzaEpyPC1zdHJzcGxpdChjb250X1ZhY1JXX0NhdmVuZGlzaEpyJGNtdENvbnRlbnQsIlvjgILvvIHvvJvvvJ8hPztdIikKCiMg5bCH5q+P5YCL5Y+l5a2Q6IiH5omA5bGs55qE5paH56ug6YCj57WQ6YWN5bCN6LW35L6G77yM5pW055CG5oiQIGRhdGFmcmFtZQpjb250X1ZhY1JXX0NhdmVuZGlzaEpyIDwtIGRhdGEuZnJhbWUoCiAgYXJ0VXJsID0gcmVwKFZhY1JXX0NhdmVuZGlzaEpyJGFydFVybCwgc2FwcGx5KGNvbnRfVmFjUldfQ2F2ZW5kaXNoSnIsIGxlbmd0aCkpLCAKICBjbXRDb250ZW50ID0gdW5saXN0KGNvbnRfVmFjUldfQ2F2ZW5kaXNoSnIpKSAlPiUKICBmaWx0ZXIoIXN0cl9kZXRlY3QoY210Q29udGVudCwgcmVnZXgoIl4oXHR8XG58ICkqJCIpKSkKY29udF9WYWNSV19DYXZlbmRpc2hKciRjbXRDb250ZW50IDwtIGFzLmNoYXJhY3Rlcihjb250X1ZhY1JXX0NhdmVuZGlzaEpyJGNtdENvbnRlbnQpCgojIOmAsuihjOaWt+ipnu+8jOS4puioiOeul+WQhOipnuW9meWcqOWQhOaWh+eroOS4reWHuuePvueahOasoeaVuApjb250X1ZhY1JXX0NhdmVuZGlzaEpyX3dvcmQgPC0gY29udF9WYWNSV19DYXZlbmRpc2hKciAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIGNtdENvbnRlbnQsIHRva2VuPWNoaV90b2tlbml6ZXIpICU+JQogIGZpbHRlcighc3RyX2RldGVjdCh3b3JkLCByZWdleCgiWzAtOWEtekEtWl0iKSkpICU+JQogIGNvdW50KGFydFVybCwgd29yZCwgc29ydCA9IFRSVUUpCgpWYWNSV19DYXZlbmRpc2hKcl9MSVdDIDwtIGNvbnRfVmFjUldfQ2F2ZW5kaXNoSnJfd29yZCAlPiUgCiAgaW5uZXJfam9pbihMSVdDKSAlPiUgCiAgZ3JvdXBfYnkoYXJ0VXJsLHNlbnRpbWVudCkgJT4lCiAgc3VtbWFyaXNlKGNvdW50PXN1bShuKSkgJT4lIHNwcmVhZChzZW50aW1lbnQsIGNvdW50LCBmaWxsID0gMCkKYGBgCgojIyMgQ2F2ZW5kaXNoSnLoiIfnlZnoqIDogIXnmoTmraPosqDpnaLmg4Xnt5Lmr5TkvosK5bCHQ2F2ZW5kaXNoSnIg55m85paH5ZKM5Zue6KaG55qE5oOF57eS5ZCI5L216LW35L6GCmBgYHtyfQpWYWNSV19DYXZlbmRpc2hKcl9MSVdDIDwtIFZhY1JXX0NhdmVuZGlzaEpyX0xJV0MgJT4lIAogIG11dGF0ZShzb3VyY2U9InJldmlldyIpCiAgCkNhdmVuZGlzaEpyX0xJV0MgPC0gQ2F2ZW5kaXNoSnJfTElXQyAlPiUgCiAgbXV0YXRlKHNvdXJjZT0iYXJ0aWNsZSIpCgpDYXZlbmRpc2hKcl9jbXRfc2VuIDwtIAogIGJpbmRfcm93cyh4ID0gQ2F2ZW5kaXNoSnJfTElXQywgeSA9IFZhY1JXX0NhdmVuZGlzaEpyX0xJV0MpIApDYXZlbmRpc2hKcl9jbXRfc2VuCgpDYXZlbmRpc2hKcl9zZW5fcGxvdCA8LSBDYXZlbmRpc2hKcl9jbXRfc2VuICAlPiUgCiAgZ2F0aGVyKHNlbnRpbWVudCxhcnRpY2xlX251bWJlciwtc291cmNlLC1hcnRVcmwpICU+JQogIGdyb3VwX2J5KGFydFVybCxzb3VyY2UpICU+JSAKICBtdXRhdGUodG90YWxfYXJ0aWNsZSA9c3VtKGFydGljbGVfbnVtYmVyKSxyYXRpbz1hcnRpY2xlX251bWJlci90b3RhbF9hcnRpY2xlKSAlPiUgCiAgZ3JvdXBfYnkoYXJ0VXJsKSAj55m85paHL+WbnuW+qeeahOaWh+eroOaVuAoKQ2F2ZW5kaXNoSnJfc2VuX3Bsb3QgJT4lCiAgZ2dwbG90KCBhZXMoeD1hcy5mYWN0b3IoYXJ0VXJsKSwgeT1yYXRpbywgZmlsbD1zZW50aW1lbnQpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHlsYWIoInByb3BvcnRpb24iKSArIAogICAgI3RoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpKwogIHRoZW1lKHRleHQ9ZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWl0aSBUQyBMaWdodCIsc2l6ZT0xMikpKwogIGZhY2V0X3dyYXAofnNvdXJjZSwgbmNvbCA9IDEpCmBgYAoKeOaYr+avj+evh+aWh+eroO+8jOavj+evh+aWh+eroOeahOato+iyoOmdouaDhee3ku+8jOavlOi8g+eZvOaWh+iAheiIh+WbnuimhuiAheeahOaDhee3ku+8jHBvc2l0aXZl5paH56ug55qE55m85paH6ICF6IiH5Zue6KaG6ICF55qE5oOF57eS6Lao5Yui5pmu6YGN5rKS5pyJ5LiA6Ie044CCCgojIyMgQ2F2ZW5kaXNoSnLmloflrZfpm7IKYGBge3J9CkNhdmVuZGlzaEpyX3dvcmQgJT4lCiAgIGZpbHRlcighd29yZCAgJWluJSAn55ar6IuXJykgJT4lCiAgIGdyb3VwX2J5KHdvcmQpICU+JQogICBzdW1tYXJpc2Uoc3VtID0gbigpKSAlPiUKICAgZmlsdGVyKHN1bSA+IDMpICAlPiUKICAgYXJyYW5nZShkZXNjKHN1bSkpICU+JQogICB3b3JkY2xvdWQyKCkKYGBgCiFbXSh3b3JkY2xvdWQxLnBuZykKCkNPQ09DQ0Pmlofnq6DkuK3oqZ7lvZnnm7jpl5zmgKfnmoTliY3omZXnkIYKYGBge3J9ClZhYzFfQ09DT0NDQyA8LSBWYWNfQ09DT0NDQyAlPiUgCiAgc2VsZWN0KGFydFVybCxzZW50ZW5jZSkgCgpWYWMxX0NPQ09DQ0MgPC0gc3Ryc3BsaXQoVmFjMV9DT0NPQ0NDJHNlbnRlbmNlLCJb44CC77yB77yb77yfIT87XSIpCgojIOWwh+avj+WAi+WPpeWtkOWSjOebuOWwjeaHieeahOaWh+eroOmFjeWwjei1t+S+hu+8jOaVtOeQhuaIkCBkYXRhZnJhbWUKVmFjMV9DT0NPQ0NDICA8LSBkYXRhLmZyYW1lKAogIGFydFVybCA9IHJlcChWYWNfQ09DT0NDQyRhcnRVcmwsIHNhcHBseShWYWMxX0NPQ09DQ0MsIGxlbmd0aCkpLCAKICBzZW50ZW5jZSA9IHVubGlzdChWYWMxX0NPQ09DQ0MpKQpWYWMxX0NPQ09DQ0Mkc2VudGVuY2UgPC0gYXMuY2hhcmFjdGVyKFZhYzFfQ09DT0NDQyRzZW50ZW5jZSkKCmppZWJhX3Rva2VuaXplciA8LSB3b3JrZXIodXNlcj0idmFjY2luZS50eHQiLCBzdG9wX3dvcmQgPSAic3RvcF93b3Jkcy50eHQiKQoKQ09DT0NDQ193b3JkIDwtIFZhYzFfQ09DT0NDQyAlPiUKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHNlbnRlbmNlLCB0b2tlbj1jaGlfdG9rZW5pemVyKSAlPiUKICBjb3VudChhcnRVcmwsIHdvcmQsIHNvcnQgPSBUUlVFKQpgYGAKCiMjIyBDT0NPQ0ND5paH56ug5Lit6Kme5b2Z6ZaT55qE55u46Zec5oCnCmBgYHtyfQojIOioiOeul+WFqeWAi+ipnuW9memWk+eahOebuOmXnOaApwpDT0NPQ0NDX3dvcmRfY29ycyA8LSBDT0NPQ0NDX3dvcmQgJT4lCiAgZ3JvdXBfYnkod29yZCkgJT4lCiAgZmlsdGVyKG4oKSA+PSAyKSAlPiUKICBwYWlyd2lzZV9jb3Iod29yZCwgYXJ0VXJsLCBzb3J0ID0gVFJVRSkKCkNPQ09DQ0Nfd29yZF9jb3JzICU+JQogIGZpbHRlcihjb3JyZWxhdGlvbiA+IDAuNikgJT4lCiAgZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKCkgJT4lCiAgZ2dyYXBoKGxheW91dCA9ICJmciIpICsKICBnZW9tX2VkZ2VfbGluayhhZXMoZWRnZV9hbHBoYSA9IGNvcnJlbGF0aW9uKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKyAKICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAibGlnaHRibHVlIiwgc2l6ZSA9IDMpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgcmVwZWwgPSBUUlVFLCBmYW1pbHkgPSAiSGVpdGkgVEMgTGlnaHQiKSArCiAgdGhlbWVfdm9pZCgpCmBgYAoK5Li76aGMM+OAjOWci+eUoueWq+iLl+iDjOW+jOeahOaUv+ayu+OAjeS4u+mhjOeahOS4u+imgeeZvOaWh+iAheacie+8mkNPQ09DQ0MKCi4uLuS4remWk+W4tuacieaUv+ayu+iJsuW9qeeahC4uLgoKPGJyPkNPQ09DQ0Pmlofnq6DkuK3vvIznmbzmlofogIXlkozlm57mlofogIXmg4Xnt5LliIbmnpDkuYvos4fmlpnliY3omZXnkIY8YnI+Cua6luWCmUNPQ09DQ0PnmoTlrZflhbgKYGBge3J9CkNPQ09DQ0NfTElXQyA8LSBDT0NPQ0NDX3dvcmQgJT4lIAogIGlubmVyX2pvaW4oTElXQykgJT4lIAogIGdyb3VwX2J5KGFydFVybCxzZW50aW1lbnQpICU+JQogIHN1bW1hcmlzZShjb3VudD1zdW0obikpCmBgYArmlbTnkIZSZWl2ZXctQ09DT0NDQwpgYGB7cn0KQ09DT0NDQ19MSVdDIDwtQ09DT0NDQ19MSVdDJT4lIAogIHNwcmVhZChzZW50aW1lbnQsIGNvdW50LCBmaWxsID0gMCkKCmNvbnRfVmFjUldfQ09DT0NDQyA8LSBWYWNSV19DT0NPQ0NDICU+JSAKICBzZWxlY3QoYXJ0VXJsLGNtdENvbnRlbnQpCgpjb250X1ZhY1JXX0NPQ09DQ0M8LXN0cnNwbGl0KGNvbnRfVmFjUldfQ09DT0NDQyRjbXRDb250ZW50LCJb44CC77yB77yb77yfIT87XSIpCgojIOWwh+avj+WAi+WPpeWtkOiIh+aJgOWxrOeahOaWh+eroOmAo+e1kOmFjeWwjei1t+S+hu+8jOaVtOeQhuaIkCBkYXRhZnJhbWUKY29udF9WYWNSV19DT0NPQ0NDIDwtIGRhdGEuZnJhbWUoCiAgYXJ0VXJsID0gcmVwKFZhY1JXX0NPQ09DQ0MkYXJ0VXJsLCBzYXBwbHkoY29udF9WYWNSV19DT0NPQ0NDLCBsZW5ndGgpKSwgCiAgY210Q29udGVudCA9IHVubGlzdChjb250X1ZhY1JXX0NPQ09DQ0MpKSAlPiUKICBmaWx0ZXIoIXN0cl9kZXRlY3QoY210Q29udGVudCwgcmVnZXgoIl4oXHR8XG58ICkqJCIpKSkKY29udF9WYWNSV19DT0NPQ0NDJGNtdENvbnRlbnQgPC0gYXMuY2hhcmFjdGVyKGNvbnRfVmFjUldfQ09DT0NDQyRjbXRDb250ZW50KQoKIyDpgLLooYzmlrfoqZ7vvIzkuKboqIjnrpflkIToqZ7lvZnlnKjlkITmlofnq6DkuK3lh7rnj77nmoTmrKHmlbgKY29udF9WYWNSV19DT0NPQ0NDX3dvcmQgPC0gY29udF9WYWNSV19DT0NPQ0NDICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgY210Q29udGVudCwgdG9rZW49Y2hpX3Rva2VuaXplcikgJT4lCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KHdvcmQsIHJlZ2V4KCJbMC05YS16QS1aXSIpKSkgJT4lCiAgY291bnQoYXJ0VXJsLCB3b3JkLCBzb3J0ID0gVFJVRSkKClZhY1JXX0NPQ09DQ0NfTElXQyA8LSBjb250X1ZhY1JXX0NPQ09DQ0Nfd29yZCAlPiUgCiAgaW5uZXJfam9pbihMSVdDKSAlPiUgCiAgZ3JvdXBfYnkoYXJ0VXJsLHNlbnRpbWVudCkgJT4lCiAgc3VtbWFyaXNlKGNvdW50PXN1bShuKSkgJT4lIHNwcmVhZChzZW50aW1lbnQsIGNvdW50LCBmaWxsID0gMCkKYGBgCiMjIyBDT0NPQ0ND6IiH55WZ6KiA6ICF55qE5q2j6LKg6Z2i5oOF57eS5q+U5L6LCuWwh0NPQ09DQ0Mg55m85paH5ZKM5Zue6KaG55qE5oOF57eS5ZCI5L216LW35L6GCmBgYHtyfQpWYWNSV19DT0NPQ0NDX0xJV0MgPC0gVmFjUldfQ09DT0NDQ19MSVdDICU+JSAKICBtdXRhdGUoc291cmNlPSJyZXZpZXciKQogIApDT0NPQ0NDX0xJV0MgPC0gQ09DT0NDQ19MSVdDICU+JSAKICBtdXRhdGUoc291cmNlPSJhcnRpY2xlIikKCkNPQ09DQ0NfY210X3NlbiA8LSAKICBiaW5kX3Jvd3MoeCA9IENPQ09DQ0NfTElXQywgeSA9IFZhY1JXX0NPQ09DQ0NfTElXQykgCkNPQ09DQ0NfY210X3NlbgoKQ09DT0NDQ19zZW5fcGxvdCA8LSBDT0NPQ0NDX2NtdF9zZW4gICU+JSAKICBnYXRoZXIoc2VudGltZW50LGFydGljbGVfbnVtYmVyLC1zb3VyY2UsLWFydFVybCkgJT4lCiAgZ3JvdXBfYnkoYXJ0VXJsLHNvdXJjZSkgJT4lIAogIG11dGF0ZSh0b3RhbF9hcnRpY2xlID1zdW0oYXJ0aWNsZV9udW1iZXIpLHJhdGlvPWFydGljbGVfbnVtYmVyL3RvdGFsX2FydGljbGUpICU+JSAKICBncm91cF9ieShhcnRVcmwpICPnmbzmlocv5Zue5b6p55qE5paH56ug5pW4CgpDT0NPQ0NDX3Nlbl9wbG90ICU+JQogIGdncGxvdCggYWVzKHg9YXMuZmFjdG9yKGFydFVybCksIHk9cmF0aW8sIGZpbGw9c2VudGltZW50KSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyB5bGFiKCJwcm9wb3J0aW9uIikgKyAKICAgICN0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSsKICB0aGVtZSh0ZXh0PWVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVpdGkgVEMgTGlnaHQiLHNpemU9MTIpKSsKICBmYWNldF93cmFwKH5zb3VyY2UsIG5jb2wgPSAxKQpgYGAKPGJyPueZvOaWh+iAheato+mdouaDhee3kui8g+Wkmu+8jOiAjOW6leS4i+eahOeVmeiogOiAheiyoOmdouaDhee3kui8g+mrmOOAgjxicj4KCiMjIyBDT0NPQ0ND5paH5a2X6ZuyCmBgYHtyfQpDT0NPQ0NDX3dvcmQgJT4lCiAgIGZpbHRlcighd29yZCAgJWluJSAn55ar6IuXJykgJT4lCiAgIGdyb3VwX2J5KHdvcmQpICU+JQogICBzdW1tYXJpc2Uoc3VtID0gbigpKSAlPiUKICAgZmlsdGVyKHN1bSA+IDEpICAlPiUKICAgYXJyYW5nZShkZXNjKHN1bSkpICU+JQogICB3b3JkY2xvdWQyKCkKYGBgCiFbXSh3b3JkY2xvdWQyLnBuZykKCueZvOaWh+iAheWwjeWQhOWutueWq+iLl+eahOato+iyoOaDhee3kgpgYGB7cn0KIyB2YWNjaW5lX0JOVCA9IAojICAgdmFjY2luZV93b3JkcyAlPiUKIyAgIGZpbHRlcih3b3JkID09ICJCTlQiIHwgd29yZCA9PSAi6Lyd55Ge55ar6IuXInwgd29yZCA9PSAiYm50IikgJT4lCiMgICBtdXRhdGUodmFjY2luZT0nQk5UJykKCnZhY2NpbmVfbmF0aW9uYWwgPSAKICB2YWNjaW5lX3dvcmRzICU+JQogIGZpbHRlcih3b3JkID09ICLlnIvnlKLnlqvoi5ciIHwgd29yZCA9PSAi6auY56uv55ar6IuXInwgd29yZCA9PSAi6IGv5Lqe55ar6IuXIikgJT4lCiAgaW5uZXJfam9pbihhcnRpY2xlX3R5cGUpICU+JQogIG11dGF0ZSh2YWNjaW5lPSduYXRpb25hbCcpICU+JSAgIAogIGdyb3VwX2J5KHZhY2NpbmUsdHlwZSklPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQoKdmFjY2luZV9CTlQgPSAKICB2YWNjaW5lX3dvcmRzICU+JQogIGZpbHRlcih3b3JkID09ICJCTlQiIHwgd29yZCA9PSAi6Lyd55Ge55ar6IuXInwgd29yZCA9PSAiYm50IikgJT4lCiAgaW5uZXJfam9pbihhcnRpY2xlX3R5cGUpICU+JQogIG11dGF0ZSh2YWNjaW5lPSdCTlQnKSAlPiUgICAKICBncm91cF9ieSh2YWNjaW5lLHR5cGUpJT4lCiAgc3VtbWFyaXNlKGNvdW50PW4oKSkKCgp2YWNjaW5lX21vZGVybmEgPSAKICB2YWNjaW5lX3dvcmRzICU+JQogIGZpbHRlcih3b3JkID09ICLojqvlvrfntI0iIHwgd29yZCA9PSAibW9kZXJuYSIpICU+JQogIGlubmVyX2pvaW4oYXJ0aWNsZV90eXBlKSAlPiUKICBtdXRhdGUodmFjY2luZT0nbW9kZXJuYScpICU+JSAgIAogIGdyb3VwX2J5KHZhY2NpbmUsdHlwZSklPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQoKdmFjY2luZV9heiA9IAogIHZhY2NpbmVfd29yZHMgJT4lCiAgZmlsdGVyKHdvcmQgPT0gIkFaIiB8IHdvcmQgPT0gImF6Inwgd29yZCA9PSAiQXoifCB3b3JkID09ICLpmL/mlq/nibnmjbfliKnlurciKSAlPiUKICBpbm5lcl9qb2luKGFydGljbGVfdHlwZSkgJT4lCiAgbXV0YXRlKHZhY2NpbmU9J2F6JykgJT4lICAgCiAgZ3JvdXBfYnkodmFjY2luZSx0eXBlKSU+JQogIHN1bW1hcmlzZShjb3VudD1uKCkpCgp2YWNjaW5lX2pqID0gCiAgdmFjY2luZV93b3JkcyAlPiUKICBmaWx0ZXIod29yZCA9PSAi5ayM55SfIiB8IHdvcmQgPT0gIkpvaG5zb24gJiBKb2huc29uIikgJT4lCiAgaW5uZXJfam9pbihhcnRpY2xlX3R5cGUpICU+JQogIG11dGF0ZSh2YWNjaW5lPSdqaicpICU+JSAgIAogIGdyb3VwX2J5KHZhY2NpbmUsdHlwZSklPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQoKI+WQiOS9teWQhOWAi+eWq+iLl+eahOeZvOaWh+aWh+eroAp2YWNjaW5lX2ZpbHRlciA9IHJiaW5kKHZhY2NpbmVfbmF0aW9uYWwsdmFjY2luZV9CTlQsdmFjY2luZV9tb2Rlcm5hLHZhY2NpbmVfYXosdmFjY2luZV9qaikKYGBgCgpgYGB7cn0KIyB2YWNjaW5lX2ZpbHRlciAlPiUKIyAgIGdncGxvdCggYWVzKHg9dmFjY2luZSwgeT1jb3VudCwgZmlsbD10eXBlKSkgKyAKIyAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHlsYWIoImNvdW50IikgKyAKIyAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjMTQ1Mzc0IiwicmVkIikpKwojICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgCmBgYAoKIyMjIOS7peavlOS+i+S6huino+eZvOaWh+iAheWwjeaWvOWQhOWutueWq+iLl+eahOato+iyoOaDhee3kgpgYGB7cn0KIyB2YWNjaW5lX2ZpbHRlciAlPiUKIyAgIG11dGF0ZSh0b3RhbF9jb3VudCA9c3VtKGNvdW50KSklPiUKIyAgIGdncGxvdCggYWVzKHg9dmFjY2luZSwgeT1jb3VudC90b3RhbF9jb3VudCwgZmlsbD10eXBlKSkgKyAKIyAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHlsYWIoInByb3BvcnRpb24iKSArIAojICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInJlZCIsIiMxNDUzNzQiKSkrCiMgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiMgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTAuNSksIGNvbG91cj0id2hpdGUiLGx3ZD0yKQpgYGAKCiFbXShiYXIuanBlZykKPGJyPueZvOaWh+iAheWcqOirh+irluWQhOWutueWq+iLl+aZgu+8jOaDhee3kumDveWBj+S4reeri++8jOiqquaYjueZvOaWh+iAheaYr+S7pei8g+WuouingOeahOeri+WgtOS+huirh+irlumAmeasoeS6i+S7tuOAgjxicj4KCueVmeiogOiAheWwjeWQhOWutueWq+iLl+eahOato+iyoOaDhee3kgpgYGB7cn0KcmV2aWV3c190eXBlID0gCiAgcmV2aWV3c193b3JkcyAlPiUKICBpbm5lcl9qb2luKExJV0MpICU+JSAKICBncm91cF9ieShhcnRVcmwsc2VudGltZW50KSAlPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKSAlPiUKICBzcHJlYWQoc2VudGltZW50LGNvdW50LGZpbGwgPSAwKSAlPiUgI+aKiuato+iyoOmdouaDhee3kuWxlemWi++8jOe8uuWAvOijnDAKICBtdXRhdGUodHlwZSA9IGNhc2Vfd2hlbihwb3NpdGl2ZSA+IG5lZ2F0aXZlIH4gInBvc2l0aXZlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIm5lZ2F0aXZlIikpICU+JQogIGRhdGEuZnJhbWUoKQoKbmFtZXMocmV2aWV3c190eXBlKVsyXT0icmV2aWV3c19uZWdhdGl2ZSIKbmFtZXMocmV2aWV3c190eXBlKVszXT0icmV2aWV3c19wb3NpdGl2ZSIKYGBgCgpgYGB7cn0KdmFjY2luZV9uYXRpb25hbF91cmwgPSAKICB2YWNjaW5lX3dvcmRzICU+JQogIGZpbHRlcih3b3JkID09ICLlnIvnlKLnlqvoi5ciIHwgd29yZCA9PSAi6auY56uv55ar6IuXInwgd29yZCA9PSAi6IGv5Lqe55ar6IuXIikgJT4lCiAgaW5uZXJfam9pbihhcnRpY2xlX3R5cGUpICU+JQogIG11dGF0ZSh2YWNjaW5lPSduYXRpb25hbCcpCgp2YWNjaW5lX0JOVF91cmwgPSAKICB2YWNjaW5lX3dvcmRzICU+JQogIGZpbHRlcih3b3JkID09ICJCTlQiIHwgd29yZCA9PSAi6Lyd55Ge55ar6IuXInwgd29yZCA9PSAiYm50IikgJT4lCiAgaW5uZXJfam9pbihhcnRpY2xlX3R5cGUpICU+JQogIG11dGF0ZSh2YWNjaW5lPSdCTlQnKQoKdmFjY2luZV9tb2Rlcm5hX3VybCA9IAogIHZhY2NpbmVfd29yZHMgJT4lCiAgZmlsdGVyKHdvcmQgPT0gIuiOq+W+t+e0jSIgfCB3b3JkID09ICJtb2Rlcm5hIikgJT4lCiAgaW5uZXJfam9pbihhcnRpY2xlX3R5cGUpICU+JQogIG11dGF0ZSh2YWNjaW5lPSdtb2Rlcm5hJykKCnZhY2NpbmVfYXpfdXJsID0gCiAgdmFjY2luZV93b3JkcyAlPiUKICBmaWx0ZXIod29yZCA9PSAiQVoiIHwgd29yZCA9PSAiYXoifCB3b3JkID09ICJBeiJ8IHdvcmQgPT0gIumYv+aWr+eJueaNt+WIqeW6tyIpICU+JQogIGlubmVyX2pvaW4oYXJ0aWNsZV90eXBlKSAlPiUKICBtdXRhdGUodmFjY2luZT0nYXonKQoKdmFjY2luZV9qal91cmwgPSAKICB2YWNjaW5lX3dvcmRzICU+JQogIGZpbHRlcih3b3JkID09ICLlrIznlJ8iIHwgd29yZCA9PSAiSm9obnNvbiAmIEpvaG5zb24iKSAlPiUKICBpbm5lcl9qb2luKGFydGljbGVfdHlwZSkgJT4lCiAgbXV0YXRlKHZhY2NpbmU9J2pqJykKCiPlkIjkvbXlkITlgIvnlqvoi5fnmoTnmbzmlofmlofnq6AKdmFjY2luZV9maWx0ZXJfdXJsID0gcmJpbmQodmFjY2luZV9uYXRpb25hbF91cmwsdmFjY2luZV9CTlRfdXJsLHZhY2NpbmVfbW9kZXJuYV91cmwsdmFjY2luZV9hel91cmwsdmFjY2luZV9qal91cmwpCnZhY2NpbmVfYXJ0dXJsID0gaW5uZXJfam9pbihyZXZpZXdzX3R5cGUsdmFjY2luZV9maWx0ZXJfdXJsLGJ5PSJhcnRVcmwiKQpgYGAKCmBgYHtyfQp2YWNjaW5lX25hdGlvbmFsX3Jldmlld3MgPSAKICB2YWNjaW5lX2FydHVybCAlPiUKICBmaWx0ZXIodmFjY2luZSA9PSAibmF0aW9uYWwiKSAlPiUKICBncm91cF9ieSh2YWNjaW5lLHR5cGUueCklPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQoKdmFjY2luZV9CTlRfcmV2aWV3cyA9IAogIHZhY2NpbmVfYXJ0dXJsICU+JQogIGZpbHRlcih2YWNjaW5lID09ICJCTlQiKSAlPiUKICBncm91cF9ieSh2YWNjaW5lLHR5cGUueCklPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQoKdmFjY2luZV9tb2Rlcm5hX3Jldmlld3MgPSAKICB2YWNjaW5lX2FydHVybCAlPiUKICBmaWx0ZXIodmFjY2luZSA9PSAibW9kZXJuYSIpICU+JQogIGdyb3VwX2J5KHZhY2NpbmUsdHlwZS54KSU+JQogIHN1bW1hcmlzZShjb3VudD1uKCkpCgp2YWNjaW5lX2F6X3Jldmlld3MgPSAKICB2YWNjaW5lX2FydHVybCAlPiUKICBmaWx0ZXIodmFjY2luZSA9PSAiYXoiKSAlPiUKICBncm91cF9ieSh2YWNjaW5lLHR5cGUueCklPiUKICBzdW1tYXJpc2UoY291bnQ9bigpKQoKdmFjY2luZV9qal9yZXZpZXdzID0gCiAgdmFjY2luZV9hcnR1cmwgJT4lCiAgZmlsdGVyKHZhY2NpbmUgPT0gImpqIikgJT4lCiAgZ3JvdXBfYnkodmFjY2luZSx0eXBlLngpJT4lCiAgc3VtbWFyaXNlKGNvdW50PW4oKSkKCiPlkIjkvbXlkITlgIvnlqvoi5fnmoTnlZnoqIAKdmFjY2luZV9maWx0ZXJfcmV2aWV3cyA9IHJiaW5kKHZhY2NpbmVfbmF0aW9uYWxfcmV2aWV3cyx2YWNjaW5lX0JOVF9yZXZpZXdzLHZhY2NpbmVfbW9kZXJuYV9yZXZpZXdzLHZhY2NpbmVfYXpfcmV2aWV3cyx2YWNjaW5lX2pqX3Jldmlld3MpCmBgYAoKYGBge3J9CiMgdmFjY2luZV9maWx0ZXJfcmV2aWV3cyAlPiUKIyAgIGdncGxvdCggYWVzKHg9dmFjY2luZSwgeT1jb3VudCwgZmlsbD10eXBlLngpKSArIAojICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgeWxhYigiY291bnQiKSArIAojICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiMxNDUzNzQiLCJyZWQiKSkrCiMgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSAKYGBgCgojIyMg5Lul5q+U5L6L5LqG6Kej55WZ6KiA6ICF5bCN5pa85ZCE5a6255ar6IuX55qE5q2j6LKg5oOF57eSCmBgYHtyfQp2YWNjaW5lX2ZpbHRlcl9yZXZpZXdzICU+JQogIG11dGF0ZSh0b3RhbF9jb3VudCA9c3VtKGNvdW50KSklPiUKICBnZ3Bsb3QoIGFlcyh4PXZhY2NpbmUsIHk9Y291bnQvdG90YWxfY291bnQsIGZpbGw9dHlwZS54KSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyB5bGFiKCJwcm9wb3J0aW9uIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygicmVkIiwiIzE0NTM3NCIpKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PTAuNSksIGNvbG91cj0id2hpdGUiLGx3ZD0yKQpgYGAKPGJyPuWPjeiAjOeVmeiogOiAheWcqOirh+irluWQhOWutueWq+iLl+aZgu+8jOaDhee3kuWBj+WQkeiyoOmdou+8jOeci+W+heacrOasoeS6i+S7tui8g+aCsuingOOAgjxicj4KCiMgNi4g5LiN5ZCM5paw6IGe5ZKMcHB05LmL6ZaT55qE5q2j6LKg6Z2i5oOF57eS5q+U6LyDCui8ieWFpeadseajruaWsOiBnuOAgeiBr+WQiOaWsOiBnuOAgeiYi+aenOaWsOiBngpgYGB7cn0KZXR0b2RheSA8LSBmcmVhZCgi5p2x5qOu5paw6IGeX2FydGljbGVNZXRhRGF0YS5jc3YiLGVuY29kaW5nID0gIlVURi04IikgJT4lIAogIG11dGF0ZShzZW50ZW5jZT1nc3ViKCJbXG5dezIsfSIsICLjgIIiLCBzZW50ZW5jZSkpICU+JSAKICBtdXRhdGUoc2VudGVuY2U9Z3N1YigiXG4iLCAiIiwgc2VudGVuY2UpKSAlPiUgCiAgbXV0YXRlKHNlbnRlbmNlPWdzdWIoImh0dHAocyk/Wy06XFwvQS1aYS16MC05XFwuXSsiLCAiICIsIHNlbnRlbmNlKSkKCnVkbiA8LSBmcmVhZCgi6IGv5ZCI5paw6IGeX2FydGljbGVNZXRhRGF0YS5jc3YiLGVuY29kaW5nID0gIlVURi04IikgJT4lIAogIG11dGF0ZShzZW50ZW5jZT1nc3ViKCJbXG5dezIsfSIsICLjgIIiLCBzZW50ZW5jZSkpICU+JSAKICBtdXRhdGUoc2VudGVuY2U9Z3N1YigiXG4iLCAiIiwgc2VudGVuY2UpKSAlPiUgCiAgbXV0YXRlKHNlbnRlbmNlPWdzdWIoImh0dHAocyk/Wy06XFwvQS1aYS16MC05XFwuXSsiLCAiICIsIHNlbnRlbmNlKSkKCmFwcGxlIDwtIGZyZWFkKCLomIvmnpzmlrDogZ5fYXJ0aWNsZU1ldGFEYXRhLmNzdiIsZW5jb2RpbmcgPSAiVVRGLTgiKSAlPiUgCiAgbXV0YXRlKHNlbnRlbmNlPWdzdWIoIltcbl17Mix9IiwgIuOAgiIsIHNlbnRlbmNlKSkgJT4lIAogIG11dGF0ZShzZW50ZW5jZT1nc3ViKCJcbiIsICIiLCBzZW50ZW5jZSkpICU+JSAKICBtdXRhdGUoc2VudGVuY2U9Z3N1YigiaHR0cChzKT9bLTpcXC9BLVphLXowLTlcXC5dKyIsICIgIiwgc2VudGVuY2UpKQpgYGAKCmBgYHtyfQpldHRvZGF5IDwtIGV0dG9kYXkgJT4lIAogIG11dGF0ZShzZW50ZW5jZT1nc3ViKCLlqpLpq5TkvobmupB86KiY6ICF572y5ZCNfOWujOaVtOaWsOiBnuaomemhjHzlrozmlbTmlrDogZ7lhafmlod85a6M5pW05paw6IGe6YCj57WQfCjmiJbnn63ntrLlnYApfOWCmeiou3zlgpnoqLvoq4vmlL7mnIDlvozpnaJ86YGV6ICF5paw6IGe5paH56ug5Yiq6ZmkIiwgIiIsIHNlbnRlbmNlKSkKCnVkbiA8LSB1ZG4gJT4lIAogIG11dGF0ZShzZW50ZW5jZT1nc3ViKCLlqpLpq5TkvobmupB86KiY6ICF572y5ZCNfOWujOaVtOaWsOiBnuaomemhjHzlrozmlbTmlrDogZ7lhafmlod85a6M5pW05paw6IGe6YCj57WQfCjmiJbnn63ntrLlnYApfOWCmeiou3zlgpnoqLvoq4vmlL7mnIDlvozpnaJ86YGV6ICF5paw6IGe5paH56ug5Yiq6ZmkIiwgIiIsIHNlbnRlbmNlKSkKCmFwcGxlIDwtIGFwcGxlICU+JSAKICBtdXRhdGUoc2VudGVuY2U9Z3N1Yigi5aqS6auU5L6G5rqQfOiomOiAhee9suWQjXzlrozmlbTmlrDogZ7mqJnpoYx85a6M5pW05paw6IGe5YWn5paHfOWujOaVtOaWsOiBnumAo+e1kHwo5oiW55+t57ay5Z2AKXzlgpnoqLt85YKZ6Ki76KuL5pS+5pyA5b6M6Z2ifOmBleiAheaWsOiBnuaWh+eroOWIqumZpCIsICIiLCBzZW50ZW5jZSkpCmBgYArmlrfoqZ4KYGBge3J9CmppZWJhX3Rva2VuaXplciA8LSB3b3JrZXIodXNlcj0idmFjY2luZS50eHQiLCBzdG9wX3dvcmQgPSAic3RvcF93b3Jkcy50eHQiKQoKZXR0b2RheSRzZW50ZW5jZT1ldHRvZGF5JHNlbnRlbmNlICU+JSB0b2xvd2VyKCkKZXR0b2RheV93b3JkcyA8LSBldHRvZGF5ICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgc2VudGVuY2UsIHRva2VuPWNoaV90b2tlbml6ZXIpICU+JQogIGZpbHRlcighZ3JlcGwoJ1tbOnB1bmN0Ol1dJyx3b3JkKSkgJT4lICMg5Y675qiZ6bue56ym6JmfCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KHdvcmQsIHJlZ2V4KCJbMC05XSIpKSkgICPljrvpmaTmlbjlrZcKCnVkbiRzZW50ZW5jZT11ZG4kc2VudGVuY2UgJT4lIHRvbG93ZXIoKQp1ZG5fd29yZHMgPC0gdWRuICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgc2VudGVuY2UsIHRva2VuPWNoaV90b2tlbml6ZXIpICU+JQogIGZpbHRlcighZ3JlcGwoJ1tbOnB1bmN0Ol1dJyx3b3JkKSkgJT4lICMg5Y675qiZ6bue56ym6JmfCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KHdvcmQsIHJlZ2V4KCJbMC05XSIpKSkgICPljrvpmaTmlbjlrZcKCmFwcGxlJHNlbnRlbmNlPWFwcGxlJHNlbnRlbmNlICU+JSB0b2xvd2VyKCkKYXBwbGVfd29yZHMgPC0gYXBwbGUgJT4lCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzZW50ZW5jZSwgdG9rZW49Y2hpX3Rva2VuaXplcikgJT4lCiAgZmlsdGVyKCFncmVwbCgnW1s6cHVuY3Q6XV0nLHdvcmQpKSAlPiUgIyDljrvmqJnpu57nrKbomZ8KICBmaWx0ZXIoIXN0cl9kZXRlY3Qod29yZCwgcmVnZXgoIlswLTldIikpKSAgI+WOu+mZpOaVuOWtlwpgYGAKCmBgYHtyfQpMSVdDJHNlbnRpbWVudCA9IGlmZWxzZShMSVdDJHNlbnRpbWVuPT0icG9zaXRpdmUiLDEsLTEpCmBgYAroqIjnrpfmg4Xnt5LliIbmlbgKYGBge3J9CnNlbnRpbWVudF9jb3VudCA9IE1Ub2tlbiAlPiUKICBzZWxlY3QoYXJ0VGl0bGUsYXJ0RGF0ZSx3b3JkKSAlPiUKICBpbm5lcl9qb2luKExJV0MpICU+JSAKICBncm91cF9ieShhcnRUaXRsZSkgJT4lICAKICBtdXRhdGUoUF9OPWlmZWxzZShzdW0oc2VudGltZW50KT4wLCJwb3NpdGl2ZSIsIm5lZ2F0aXZlIikpICAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoKT09MSkgJT4lIAogICAgZ3JvdXBfYnkoYXJ0RGF0ZSkgJT4lIAogIHN1bW1hcmlzZShQX251bWJlcj1zdW0oUF9OPT0icG9zaXRpdmUiKSwKICAgICAgICAgTl9udW1iZXI9c3VtKFBfTj09Im5lZ2F0aXZlIiksCiAgICAgICAgIGFydGljbGVfbiA9IG4oKSwKICAgICAgICAgcF9yYXRpbyA9IFBfbnVtYmVyL2FydGljbGVfbiwKICAgICAgICAgbl9yYXRpbyA9IE5fbnVtYmVyL2FydGljbGVfbiwKICAgICAgICAgc291cmNlPSJwdHQiKQpzZW50aW1lbnRfY291bnQ9c2VudGltZW50X2NvdW50ICU+JSBzZWxlY3QoYXJ0RGF0ZSxwX3JhdGlvLG5fcmF0aW8sc291cmNlKSAlPiUgdGlkeXI6OmdhdGhlcihzZW50aW1lbnQscmF0aW8sLWFydERhdGUsLXNvdXJjZSkKYGBgCgpgYGB7cn0KZXR0b2RheV93b3JkcyA9IGV0dG9kYXlfd29yZHMgJT4lCiAgc2VsZWN0KGFydFRpdGxlLGFydERhdGUsd29yZCkgJT4lCiAgaW5uZXJfam9pbihMSVdDKSAlPiUgCiAgZ3JvdXBfYnkoYXJ0VGl0bGUpICU+JSAgCiAgbXV0YXRlKFBfTj1pZmVsc2Uoc3VtKHNlbnRpbWVudCk+MCwicG9zaXRpdmUiLCJuZWdhdGl2ZSIpKSAgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKCk9PTEpICU+JSAKICAgIGdyb3VwX2J5KGFydERhdGUpICU+JSAKICBzdW1tYXJpc2UoUF9udW1iZXI9c3VtKFBfTj09InBvc2l0aXZlIiksCiAgICAgICAgIE5fbnVtYmVyPXN1bShQX049PSJuZWdhdGl2ZSIpLAogICAgICAgICBhcnRpY2xlX24gPSBuKCksCiAgICAgICAgIHBfcmF0aW8gPSBQX251bWJlci9hcnRpY2xlX24sCiAgICAgICAgIG5fcmF0aW8gPSBOX251bWJlci9hcnRpY2xlX24sCiAgICAgICAgIHNvdXJjZT0iZXR0b2RheSIpCgpldHRvZGF5X3dvcmRzPWV0dG9kYXlfd29yZHMgJT4lIHNlbGVjdChhcnREYXRlLHBfcmF0aW8sbl9yYXRpbyxzb3VyY2UpICU+JSB0aWR5cjo6Z2F0aGVyKHNlbnRpbWVudCxyYXRpbywtYXJ0RGF0ZSwtc291cmNlKQpgYGAKYGBge3J9CnVkbl93b3JkcyA9IHVkbl93b3JkcyAlPiUKICBzZWxlY3QoYXJ0VGl0bGUsYXJ0RGF0ZSx3b3JkKSAlPiUKICBpbm5lcl9qb2luKExJV0MpICU+JSAKICBncm91cF9ieShhcnRUaXRsZSkgJT4lICAKICBtdXRhdGUoUF9OPWlmZWxzZShzdW0oc2VudGltZW50KT4wLCJwb3NpdGl2ZSIsIm5lZ2F0aXZlIikpICAlPiUgCiAgZmlsdGVyKHJvd19udW1iZXIoKT09MSkgJT4lIAogICAgZ3JvdXBfYnkoYXJ0RGF0ZSkgJT4lIAogIHN1bW1hcmlzZShQX251bWJlcj1zdW0oUF9OPT0icG9zaXRpdmUiKSwKICAgICAgICAgTl9udW1iZXI9c3VtKFBfTj09Im5lZ2F0aXZlIiksCiAgICAgICAgIGFydGljbGVfbiA9IG4oKSwKICAgICAgICAgcF9yYXRpbyA9IFBfbnVtYmVyL2FydGljbGVfbiwKICAgICAgICAgbl9yYXRpbyA9IE5fbnVtYmVyL2FydGljbGVfbiwKICAgICAgICAgc291cmNlPSJ1ZG4iKQoKdWRuX3dvcmRzPXVkbl93b3JkcyAlPiUgc2VsZWN0KGFydERhdGUscF9yYXRpbyxuX3JhdGlvLHNvdXJjZSkgJT4lIHRpZHlyOjpnYXRoZXIoc2VudGltZW50LHJhdGlvLC1hcnREYXRlLC1zb3VyY2UpCmBgYAoKYGBge3J9CmFwcGxlX3dvcmRzID0gYXBwbGVfd29yZHMgJT4lCiAgc2VsZWN0KGFydFRpdGxlLGFydERhdGUsd29yZCkgJT4lCiAgaW5uZXJfam9pbihMSVdDKSAlPiUgCiAgZ3JvdXBfYnkoYXJ0VGl0bGUpICU+JSAgCiAgbXV0YXRlKFBfTj1pZmVsc2Uoc3VtKHNlbnRpbWVudCk+MCwicG9zaXRpdmUiLCJuZWdhdGl2ZSIpKSAgJT4lIAogIGZpbHRlcihyb3dfbnVtYmVyKCk9PTEpICU+JSAKICAgIGdyb3VwX2J5KGFydERhdGUpICU+JSAKICBzdW1tYXJpc2UoUF9udW1iZXI9c3VtKFBfTj09InBvc2l0aXZlIiksCiAgICAgICAgIE5fbnVtYmVyPXN1bShQX049PSJuZWdhdGl2ZSIpLAogICAgICAgICBhcnRpY2xlX24gPSBuKCksCiAgICAgICAgIHBfcmF0aW8gPSBQX251bWJlci9hcnRpY2xlX24sCiAgICAgICAgIG5fcmF0aW8gPSBOX251bWJlci9hcnRpY2xlX24sCiAgICAgICAgIHNvdXJjZT0iYXBwbGUiKQoKYXBwbGVfd29yZHM9YXBwbGVfd29yZHMgJT4lIHNlbGVjdChhcnREYXRlLHBfcmF0aW8sbl9yYXRpbyxzb3VyY2UpICU+JSB0aWR5cjo6Z2F0aGVyKHNlbnRpbWVudCxyYXRpbywtYXJ0RGF0ZSwtc291cmNlKQpgYGAKIyMjIDTlgItkYXRhZnJhbWXlkIjkvbUKCmBgYHtyfQpldHRvZGF5X3dvcmRzJGFydERhdGU9YXMuRGF0ZShldHRvZGF5X3dvcmRzJGFydERhdGUpCnVkbl93b3JkcyRhcnREYXRlPWFzLkRhdGUodWRuX3dvcmRzJGFydERhdGUpCmFwcGxlX3dvcmRzJGFydERhdGU9YXMuRGF0ZShhcHBsZV93b3JkcyRhcnREYXRlKQoKYWxsX2RhdGE9YmluZF9yb3dzKHNlbnRpbWVudF9jb3VudCxldHRvZGF5X3dvcmRzLHVkbl93b3JkcyxhcHBsZV93b3JkcykKYGBgCiMjIyDlkIjkvbXlvoznmoTntZDmnpwKYGBge3J9Cm15Y29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCAiU2V0MyIpKSgyMCkKCmFsbF9kYXRhJT4lCiAgZ3JvdXBfYnkoYXJ0RGF0ZSklPiUKICBnZ3Bsb3QoIGFlcyh4PWFydERhdGUsIHk9cmF0aW8sIGZpbGw9c2VudGltZW50ICkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgeWxhYigicHJvcG9ydGlvbiIpICsgCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvcnNbYygxLDUsOCwxMildKSsKc2NhbGVfeF9kYXRlKGxhYmVscyA9IHNjYWxlczo6ZGF0ZV9mb3JtYXQoIiVtLyVkIikgKSsKICBmYWNldF93cmFwKH5zb3VyY2UsbmNvbCA9IDEsIHNjYWxlcz0iZnJlZV95IikKYGBgCiMgNy4g57WQ6KuWCuaVtOmrlOiAjOiogO+8jOawkeecvuWcqOeWq+iLl+atpOitsOmhjOS4iuWkp+WkmueCuuiyoOmdouaDhee3ku+8jOS4lOWkp+WkmuWcjee5nuWcqOeWq+iLl+iDjOW+jOeahOaUv+ayu+itsOmhjO+8jOWPr+S7peS6huino+S6uuawkeWwjeaWvOaUv+W6nOeahOeWq+iLl+aUv+etluWPr+iDveS4jeaYr+mCo+m6vOa7v+aEj++8jOW4jOacm+aUv+W6nOiDveaPkOS+m+i2s+WkoOeWq+iLl++8jOiuk+WPsOeBo+WEmOaXqeW6pumBjueWq+aDhembo+mXnOOAgg==