ch0.套件取得及資料載入

避免中文亂碼

Sys.setlocale(category = "LC_ALL", locale = "zh_TW.UTF-8") # 避免中文亂碼
[1] "zh_TW.UTF-8/zh_TW.UTF-8/zh_TW.UTF-8/C/zh_TW.UTF-8/zh_TW.UTF-8"

套件

library(data.table)
library(ggplot2)
library(dplyr)
library(jiebaR)
library(tidytext)
library(stringr)
library(tm)
library(topicmodels)
library(purrr)
require(RColorBrewer)
library(scales)
mycolors <- colorRampPalette(brewer.pal(8, "Set3"))(20)

資料簡介

本資料內容為將PTT八卦板的文章,自 2020/09/01 到 2021/05/15 為止,透過文字分析平台進行關鍵字[藻礁]搜尋,共得到 517 篇文章。

metadata <- fread("ttt_articleMetaData.csv", encoding = "UTF-8")

可以看到藻礁議題在2~3月過後的新聞報導數量增加

metadata = metadata %>% select(-artPoster,-artCat,-commentNum,-push,-boo) 
metadata%>% 
  mutate(artDate = as.Date(artDate)) %>%
  group_by(artDate) %>%
  summarise(count = n())%>%
  ggplot(aes(artDate,count))+
    geom_line(color="red")+
    geom_point()

Ch1. Document Term Matrix (DTM)

資料前處理

Remove stop words

載入stop words字典

# load stop words
stop_words <- scan(file = "./dict/stop_words.txt", what=character(),sep='\n', 
                   encoding='utf-8',fileEncoding='utf-8')
Read 1211 items

載入自建字典

# load water_lexicon
lexicon <- scan(file = "./dict/lexicon.txt", what=character(),sep='\n', 
                   encoding='utf-8',fileEncoding='utf-8',quiet = T)
# 自建水情相關字典
lexicon
 [1] "凱道"           "環保團體"       "蔡總統"         "上千個"         "何宗勳"        
 [6] "公民不合作運動" "生態協會"       "環團"           "環境權"         "申請案"        
[11] "海岸生態小組"   "面紗"           "北律環境法"     "許嘉容"         "洪申翰"        
[16] "吳達彥"         "長期"           "張譽尹"         "胡智皓"         "蔡雅瀅"        
[21] "陳憲政"         "關注"           "團體獎"         "保育類"         "大潭"          
[26] "觀新藻礁"       "多杯孔珊瑚"     "領銜人"         "潘忠政"         "陳吉仲"        
[31] "洪申翰"         "藻礁"           "黃暐瀚"         "羅秉成"         "蔡英文"        
[36] "綠共"           "核四"           "重啟"           "反核"           "在地"          
[41] "陳椒華"         "柴山"           "殼狀"           "柯金源"         "公民行動聯盟"  
[46] "抗中保台"       "行動聯盟"       "在地"           "林飛帆"         "羅秉成"        
[51] "林鶴明"         "三接開發案"     "張惇涵"         "陳奕齊"         "陳逸樺"        
[56] "空污"          

使用自行建立的詞典進行斷詞

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

jieba_tokenizer = worker()

# 使用疫情相關字典重新斷詞
new_user_word(jieba_tokenizer, c(lexicon))
[1] TRUE
chi_tokenizer <- function(t) {
  lapply(t, function(x) {
    if(nchar(x)>1){
      tokens <- segment(x, jieba_tokenizer)
      tokens <- tokens[!tokens %in% stop_words]
      # 去掉字串長度爲1的詞彙
      tokens <- tokens[nchar(tokens)>1]
      return(tokens)
    }
  })
}

計算每篇文章各token出現次數

tokens <- metadata %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  filter((!str_detect(word, regex("[0-9a-zA-Z]"))) | str_detect(word, regex("[Aa][Zz]"))) %>%
  count(artUrl, word) %>%
  rename(count=n)#把數字和英文拿掉
tokens %>% head(20)

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

dtm <-tokens %>% cast_dtm(artUrl, word, count)
dtm
<<DocumentTermMatrix (documents: 517, terms: 10867)>>
Non-/sparse entries: 36035/5582204
Sparsity           : 99%
Maximal term length: 7
Weighting          : term frequency (tf)
inspect(dtm[1:10,1:10])#列出前10個document的前10個terms
<<DocumentTermMatrix (documents: 10, terms: 10)>>
Non-/sparse entries: 18/82
Sparsity           : 82%
Maximal term length: 3
Weighting          : term frequency (tf)
Sample             :
                                                          Terms
Docs                                                       白紙 保護 杯子 標語 波波
  https://www.ptt.cc/bbs/Gossiping/M.1599316636.A.647.html    1    1    3    1    1
  https://www.ptt.cc/bbs/Gossiping/M.1599736664.A.F32.html    0    2    0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1603116289.A.E0A.html    0    0    0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1610188174.A.1A3.html    0    2    0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1611161003.A.2CC.html    0    0    0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613811584.A.0FC.html    0    0    0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613821692.A.A97.html    0    0    0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613888324.A.AB9.html    0    0    0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613894790.A.454.html    0    2    0    1    0
  https://www.ptt.cc/bbs/Gossiping/M.1613944743.A.DF7.html    0    2    0    0    0
                                                          Terms
Docs                                                       蔡總統 參與 參與者 承諾 出面
  https://www.ptt.cc/bbs/Gossiping/M.1599316636.A.647.html      1    2      1    3    1
  https://www.ptt.cc/bbs/Gossiping/M.1599736664.A.F32.html      0    4      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1603116289.A.E0A.html      0    1      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1610188174.A.1A3.html      0    0      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1611161003.A.2CC.html      0    0      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613811584.A.0FC.html      0    0      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613821692.A.A97.html      0    0      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613888324.A.AB9.html      0    0      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613894790.A.454.html      0    1      0    0    0
  https://www.ptt.cc/bbs/Gossiping/M.1613944743.A.DF7.html      0    0      0    0    0

ch2. 主題模型

建立LDA模型

lda <- LDA(dtm, k = 2, control = list(seed = 2021))#兩個topics
# lda <- LDA(dtm, k = 2, control = list(seed = 2021,alpha = 2,delta=0.1),method = "Gibbs") #調整alpha即delta
lda
A LDA_VEM topic model with 2 topics.

利用LDA模型建立phi矩陣

topics_words <- tidy(lda, matrix = "beta") # 注意,在tidy function裡面要使用"beta"來取出Phi矩陣。
colnames(topics_words) <- c("topic", "term", "phi")
topics_words#phi值越大,代表那個字越傾向那個topic

尋找Topic的代表字

terms依照各主題的phi值由大到小排序,列出前10大

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

ch3. 尋找最佳主題數

建立更多主題的主題模型

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

# ldas = c()
# topics = c(2,4,6,10,15)
# 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_result2.rdata") # 將模型輸出成檔案
#   }

載入每個主題的LDA結果

load("ldas_result2.rdata")

透過perplexity找到最佳主題數

topics = c(2,4,6,10,15)
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")

#通常挑topic數會挑在轉折點上,

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)
}

產生LDAvis結果

the_lda = ldas[[2]]
json_res <- topicmodels_json_ldavis(the_lda,dtm)
serVis(json_res,open.browser = T)#landa越往左邊拉,可以看到每個主題裡面越獨特的字
#topic圓圈圈如果分得很開,代表每個主題都有他的唯一性
#圓圈的大小,代表裡面有多少的document 

產生LDAvis檔案,存至local端

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

ch4. LDA分析

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

the_lda = ldas[[2]] ## 選定topic 為 4 的結果
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(12, phi) %>%
  ungroup() %>%
  ggplot(aes(x = reorder_within(term,phi,topic), y = phi, fill = as.factor(topic))) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip() +theme(text = element_text(family='STHeitiTC-Light'))+
  scale_x_reordered()

去除共通詞彙,以及沒有意義的詞彙

removed_word = c("不是","沒有","什麼","藻礁","大潭","可以","表示","就是","台灣","真的","有沒有")

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

主題命名

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[metadata$artUrl,]
document_topics_df =data.frame(document_topics)
colnames(document_topics_df) = topics_name
rownames(document_topics_df) = NULL
news_topic = cbind(metadata,document_topics_df)
#LDA alpha:

現在我們看每一篇的文章分佈了!

查看特定主題的文章

  • 透過找到特定文章的分佈進行排序之後,可以看到此主題的比重高的文章在討論什麼。
news_topic %>%
  arrange(desc(`公投反對方立場`)) %>%head(10) 

了解主題在時間的變化

news_topic %>% 
  mutate(artDate = as.Date(artDate)) %>%
  group_by(artDate = format(artDate,'%Y%m')) %>%
  summarise_if(is.numeric, sum, na.rm = TRUE) %>%
  melt(id.vars = "artDate")%>%
  ggplot( aes(x=artDate, y=value, fill=variable)) + 
  geom_bar(stat = "identity") + ylab("value") + 
  scale_fill_manual(values=mycolors[c(1,5,8,12)])+theme(text = element_text(family='STHeitiTC-Light'))+
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

去除筆數少月份

news_topic %>%
  mutate(artDate = as.Date(artDate)) %>% 
  filter( !format(artDate,'%Y%m') %in% c(202009,202010,202101))%>%
  group_by(artDate = format(artDate,'%Y%m')) %>%
  summarise_if(is.numeric, sum, na.rm = TRUE) %>%
  melt(id.vars = "artDate")%>%
  ggplot( aes(x=artDate, y=value, fill=variable)) + 
  geom_bar(stat = "identity") + ylab("value")+scale_fill_manual(values=mycolors[c(1,5,8,12)])+theme(text = element_text(family='STHeitiTC-Light'))+
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

以比例了解主題時間變化

news_topic %>%
  mutate(artDate = as.Date(artDate)) %>% 
  filter( !format(artDate,'%Y%m') %in% c(202009,202010,202101))%>%
  group_by(artDate = format(artDate,'%Y%m')) %>%
  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)) + 
  geom_bar(stat = "identity") + ylab("proportion") + 
    scale_fill_manual(values=mycolors[c(1,5,8,12)])+theme(text = element_text(family='STHeitiTC-Light'))+
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

補充 - 不同訓練LDA模型套件

參考 http://text2vec.org/topic_modeling.html#latent_dirichlet_allocation

library(text2vec)
library(udpipe)
tokens <- metadata %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>%
  filter(!str_detect(word, regex("[0-9a-zA-Z]"))| str_detect(word, regex("[Aa][Zz]")))

建立DTM matrix

dtf <- document_term_frequencies(tokens, document = "artUrl", term = "word")
dtm <- document_term_matrix(x = dtf)
dtm_clean <- dtm_remove_lowfreq(dtm, minfreq = 30)
dim(dtm_clean)
[1] 517 274

LDA 模型


set.seed(2019)

topic_n = 4

lda_model =text2vec::LDA$new(n_topics = topic_n,doc_topic_prior = 0.1, topic_word_prior = 0.001)
doc_topic_distr =lda_model$fit_transform(dtm_clean, n_iter = 1000, convergence_tol = 1e-5,check_convergence_every_n = 100)

  |                                                                                     
  |                                                                               |   0%
  |                                                                                     
  |                                                                               |   1%
  |                                                                                     
  |=                                                                              |   1%
  |                                                                                     
  |=                                                                              |   2%
  |                                                                                     
  |==                                                                             |   2%
  |                                                                                     
  |==                                                                             |   3%
  |                                                                                     
  |===                                                                            |   3%
  |                                                                                     
  |===                                                                            |   4%
  |                                                                                     
  |====                                                                           |   4%
  |                                                                                     
  |====                                                                           |   5%
  |                                                                                     
  |====                                                                           |   6%
  |                                                                                     
  |=====                                                                          |   6%
  |                                                                                     
  |=====                                                                          |   7%
  |                                                                                     
  |======                                                                         |   7%
  |                                                                                     
  |======                                                                         |   8%
  |                                                                                     
  |=======                                                                        |   8%
  |                                                                                     
  |=======                                                                        |   9%
  |                                                                                     
  |========                                                                       |  10%
  |                                                                                     
  |========                                                                       |  11%
  |                                                                                     
  |=========                                                                      |  11%
  |                                                                                     
  |===============================================================================| 100%INFO  [17:59:01.410] early stopping at 110 iteration 


  |                                                                                     
  |                                                                               |   0%
  |                                                                                     
  |                                                                               |   1%
  |                                                                                     
  |=                                                                              |   1%
  |                                                                                     
  |=                                                                              |   2%
  |                                                                                     
  |==                                                                             |   2%
  |                                                                                     
  |==                                                                             |   3%
  |                                                                                     
  |===                                                                            |   3%
  |                                                                                     
  |===                                                                            |   4%
  |                                                                                     
  |====                                                                           |   4%
  |                                                                                     
  |====                                                                           |   5%
  |                                                                                     
  |===============================================================================| 100%INFO  [17:59:01.545] early stopping at 50 iteration 

這個比topicmodels的package跑快超多倍

一樣可以用LDAvis的套件來看

lda_model$get_top_words(n = 10, lambda = 0.5) ## 查看 前10主題字
      [,1]       [,2]     [,3]     [,4]    
 [1,] "藻礁"     "藻礁"   "天然氣" "連署"  
 [2,] "大潭"     "現在"   "能源"   "公投"  
 [3,] "桃園"     "台灣"   "方案"   "藻礁"  
 [4,] "海岸"     "問題"   "電廠"   "珍愛"  
 [5,] "生物"     "一個"   "接收站" "民進黨"
 [6,] "生態"     "真的"   "轉型"   "環團"  
 [7,] "地方"     "支持"   "政府"   "溝通"  
 [8,] "觀新藻礁" "重要"   "三接"   "議題"  
 [9,] "研究"     "鳳梨"   "減煤"   "同路人"
[10,] "中油"     "是不是" "增加"   "蔡英文"
lda_model$plot()
Loading required namespace: servr
Failed with error:  ‘there is no package called ‘servr’’
If the visualization doesn't render, install the servr package
and re-run serVis: 
 install.packages('servr') 
Alternatively, you could configure your default browser to allow
access to local files as some browsers block this by default
# lda_model$plot(out.dir ="lda_result", open.browser = TRUE)
LS0tCnRpdGxlOiAi5L2/55So5Li76aGM5qih5Z6L5YiG5p6QUFRU6Je756SB6LOH5paZIgphdXRob3I6ICJHcm91cDcg6YeR5aaN5b2jIEIwNjQwMjAwMzYg6JiH5Lqt5pa5IE0wOTQwMjAwMDEg5rGq6ae/6I+vIE0wOTQwMjAwMTcg6buD5byI5pm0IE0wOTQwMjAwMzYg5ZCz5L2p546yIE0wOTQwMjAwNTAg5p6X5r+s57SYIE0wOTQwMjAwNjIgIgpkYXRlOiAiMjAyMS8wNS8xOCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjc3M6IHN0eWxlLmNzcwogICAgaGlnaGxpZ2h0OiBweWdtZW50cwogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKLS0tCgojIGNoMC7lpZfku7blj5blvpflj4ros4fmlpnovInlhaUKIyMg6YG/5YWN5Lit5paH5LqC56K8CmBgYHtyfQpTeXMuc2V0bG9jYWxlKGNhdGVnb3J5ID0gIkxDX0FMTCIsIGxvY2FsZSA9ICJ6aF9UVy5VVEYtOCIpICMg6YG/5YWN5Lit5paH5LqC56K8CmBgYAoKIyMg5aWX5Lu2CmBgYHtyfQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShqaWViYVIpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeSh0bSkKbGlicmFyeSh0b3BpY21vZGVscykKbGlicmFyeShwdXJycikKcmVxdWlyZShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoc2NhbGVzKQpteWNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoOCwgIlNldDMiKSkoMjApCmBgYAoKCiMjIOizh+aWmeewoeS7iwoKPiDmnKzos4fmlpnlhaflrrnngrrlsIdQVFTlhavljabmnb/nmoTmlofnq6DvvIzoh6ogMjAyMC8wOS8wMSDliLAgMjAyMS8wNS8xNSDngrrmraLvvIzpgI/pgY7mloflrZfliIbmnpDlubPlj7DpgLLooYzpl5zpjbXlrZdb6Je756SBXeaQnOWwi++8jOWFseW+l+WIsCA1MTcg56+H5paH56ug44CCCgpgYGB7cn0KbWV0YWRhdGEgPC0gZnJlYWQoInR0dF9hcnRpY2xlTWV0YURhdGEuY3N2IiwgZW5jb2RpbmcgPSAiVVRGLTgiKQpgYGAKCj4g5Y+v5Lul55yL5Yiw6Je756SB6K2w6aGM5ZyoMn4z5pyI6YGO5b6M55qE5paw6IGe5aCx5bCO5pW46YeP5aKe5YqgCgpgYGB7cn0KbWV0YWRhdGEgPSBtZXRhZGF0YSAlPiUgc2VsZWN0KC1hcnRQb3N0ZXIsLWFydENhdCwtY29tbWVudE51bSwtcHVzaCwtYm9vKSAKbWV0YWRhdGElPiUgCiAgbXV0YXRlKGFydERhdGUgPSBhcy5EYXRlKGFydERhdGUpKSAlPiUKICBncm91cF9ieShhcnREYXRlKSAlPiUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpJT4lCiAgZ2dwbG90KGFlcyhhcnREYXRlLGNvdW50KSkrCiAgICBnZW9tX2xpbmUoY29sb3I9InJlZCIpKwogICAgZ2VvbV9wb2ludCgpCmBgYAoKIyBDaDEuIERvY3VtZW50IFRlcm0gTWF0cml4IChEVE0pCgojIyDos4fmlpnliY3omZXnkIYKCiMjIFJlbW92ZSBzdG9wIHdvcmRzCiMjIyDovInlhaVzdG9wIHdvcmRz5a2X5YW4CmBgYHtyfQojIGxvYWQgc3RvcCB3b3JkcwpzdG9wX3dvcmRzIDwtIHNjYW4oZmlsZSA9ICIuL2RpY3Qvc3RvcF93b3Jkcy50eHQiLCB3aGF0PWNoYXJhY3RlcigpLHNlcD0nXG4nLCAKICAgICAgICAgICAgICAgICAgIGVuY29kaW5nPSd1dGYtOCcsZmlsZUVuY29kaW5nPSd1dGYtOCcpCmBgYAoKIyMjIOi8ieWFpeiHquW7uuWtl+WFuApgYGB7cn0KIyBsb2FkIHdhdGVyX2xleGljb24KbGV4aWNvbiA8LSBzY2FuKGZpbGUgPSAiLi9kaWN0L2xleGljb24udHh0Iiwgd2hhdD1jaGFyYWN0ZXIoKSxzZXA9J1xuJywgCiAgICAgICAgICAgICAgICAgICBlbmNvZGluZz0ndXRmLTgnLGZpbGVFbmNvZGluZz0ndXRmLTgnLHF1aWV0ID0gVCkKIyDoh6rlu7rmsLTmg4Xnm7jpl5zlrZflhbgKbGV4aWNvbgpgYGAKPuS9v+eUqOiHquihjOW7uueri+eahOipnuWFuOmAsuihjOaWt+ipngoKYGBge3J9Cm1ldGFkYXRhID0gbWV0YWRhdGEgJT4lIAogIG11dGF0ZShzZW50ZW5jZT1nc3ViKCLlqpLpq5TkvobmupB86KiY6ICF572y5ZCNfOWujOaVtOaWsOiBnuaomemhjHzlrozmlbTmlrDogZ7lhafmlod85a6M5pW05paw6IGe6YCj57WQfCjmiJbnn63ntrLlnYApfOWCmeiou3zlgpnoqLvoq4vmlL7mnIDlvozpnaJ86YGV6ICF5paw6IGe5paH56ug5Yiq6ZmkIiwgIiIsIHNlbnRlbmNlKSkKCmppZWJhX3Rva2VuaXplciA9IHdvcmtlcigpCgojIOS9v+eUqOeWq+aDheebuOmXnOWtl+WFuOmHjeaWsOaWt+ipngpuZXdfdXNlcl93b3JkKGppZWJhX3Rva2VuaXplciwgYyhsZXhpY29uKSkKCmNoaV90b2tlbml6ZXIgPC0gZnVuY3Rpb24odCkgewogIGxhcHBseSh0LCBmdW5jdGlvbih4KSB7CiAgICBpZihuY2hhcih4KT4xKXsKICAgICAgdG9rZW5zIDwtIHNlZ21lbnQoeCwgamllYmFfdG9rZW5pemVyKQogICAgICB0b2tlbnMgPC0gdG9rZW5zWyF0b2tlbnMgJWluJSBzdG9wX3dvcmRzXQogICAgICAjIOWOu+aOieWtl+S4sumVt+W6pueIsjHnmoToqZ7lvZkKICAgICAgdG9rZW5zIDwtIHRva2Vuc1tuY2hhcih0b2tlbnMpPjFdCiAgICAgIHJldHVybih0b2tlbnMpCiAgICB9CiAgfSkKfQoKYGBgCgo+IOioiOeul+avj+evh+aWh+eroOWQhHRva2Vu5Ye654++5qyh5pW4CgpgYGB7cn0KdG9rZW5zIDwtIG1ldGFkYXRhICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgc2VudGVuY2UsIHRva2VuPWNoaV90b2tlbml6ZXIpICU+JQogIGZpbHRlcigoIXN0cl9kZXRlY3Qod29yZCwgcmVnZXgoIlswLTlhLXpBLVpdIikpKSB8IHN0cl9kZXRlY3Qod29yZCwgcmVnZXgoIltBYV1bWnpdIikpKSAlPiUKICBjb3VudChhcnRVcmwsIHdvcmQpICU+JQogIHJlbmFtZShjb3VudD1uKSPmiormlbjlrZflkozoi7Hmlofmi7/mjokKdG9rZW5zICU+JSBoZWFkKDIwKQpgYGAKCiMjIOWwh+izh+aWmei9ieaPm+eCukRvY3VtZW50IFRlcm0gTWF0cml4IChEVE0pCgpgYGB7cn0KZHRtIDwtdG9rZW5zICU+JSBjYXN0X2R0bShhcnRVcmwsIHdvcmQsIGNvdW50KQpkdG0KaW5zcGVjdChkdG1bMToxMCwxOjEwXSkj5YiX5Ye65YmNMTDlgItkb2N1bWVudOeahOWJjTEw5YCLdGVybXMKYGBgCiMgY2gyLiDkuLvpoYzmqKHlnosKCiMjIOW7uueri0xEQeaooeWeiwoKYGBge3J9CmxkYSA8LSBMREEoZHRtLCBrID0gMiwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDIwMjEpKSPlhanlgIt0b3BpY3MKIyBsZGEgPC0gTERBKGR0bSwgayA9IDIsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAyMDIxLGFscGhhID0gMixkZWx0YT0wLjEpLG1ldGhvZCA9ICJHaWJicyIpICPoqr/mlbRhbHBoYeWNs2RlbHRhCmxkYQpgYGAKCiMjIOWIqeeUqExEQeaooeWei+W7uueri3BoaeefqemZowpgYGB7cn0KdG9waWNzX3dvcmRzIDwtIHRpZHkobGRhLCBtYXRyaXggPSAiYmV0YSIpICMg5rOo5oSP77yM5ZyodGlkeSBmdW5jdGlvbuijoemdouimgeS9v+eUqCJiZXRhIuS+huWPluWHulBoaeefqemZo+OAggpjb2xuYW1lcyh0b3BpY3Nfd29yZHMpIDwtIGMoInRvcGljIiwgInRlcm0iLCAicGhpIikKdG9waWNzX3dvcmRzI3BoaeWAvOi2iuWkp++8jOS7o+ihqOmCo+WAi+Wtl+i2iuWCvuWQkemCo+WAi3RvcGljCmBgYAoKIyMg5bCL5om+VG9waWPnmoTku6PooajlrZcKCj4gdGVybXPkvp3nhaflkITkuLvpoYznmoRwaGnlgLznlLHlpKfliLDlsI/mjpLluo/vvIzliJflh7rliY0xMOWkpwoKYGBge3J9CnRvcGljc193b3JkcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oMTAsIHBoaSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZSh0b3Bfd29yZHMgPSByZW9yZGVyX3dpdGhpbih0ZXJtLHBoaSx0b3BpYykpICU+JQogIGdncGxvdChhZXMoeCA9IHRvcF93b3JkcywgeSA9IHBoaSwgZmlsbCA9IGFzLmZhY3Rvcih0b3BpYykpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpICt0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseT0nU1RIZWl0aVRDLUxpZ2h0JykpKwogIHNjYWxlX3hfcmVvcmRlcmVkKCkKYGBgCgojIGNoMy4g5bCL5om+5pyA5L2z5Li76aGM5pW4CgojIyDlu7rnq4vmm7TlpJrkuLvpoYznmoTkuLvpoYzmqKHlnosKCj4g5ZiX6KmmMuOAgTTjgIE244CBMTDjgIExNeWAi+S4u+mhjOaVuO+8jOWwh+e1kOaenOWtmOi1t+S+hu+8jOWGjeWBmumAsuS4gOatpeWIhuaekOOAggrmraTpg6jliIbpnIDopoHot5HkuIDmrrXmmYLplpPvvIzlt7LntpPlsIfot5HlroznmoTmqpTmoYjlrZjmiJBsZGFzX3Jlc3VsdDIucmRhdGHvvIzlj6/ku6Xnm7TmjqXovInlhaUKCmBgYHtyIGV2YWw9RkFMU0V9CiMgbGRhcyA9IGMoKQojIHRvcGljcyA9IGMoMiw0LDYsMTAsMTUpCiMgZm9yKHRvcGljIGluIHRvcGljcyl7CiMgICBzdGFydF90aW1lIDwtIFN5cy50aW1lKCkKIyAgIGxkYSA8LSBMREEoZHRtLCBrID0gdG9waWMsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAyMDIxKSkKIyAgIGxkYXMgPWMobGRhcyxsZGEpCiMgICBwcmludChwYXN0ZSh0b3BpYyAscGFzdGUoInRvcGljKHMpIGFuZCB1c2UgdGltZSBpcyAiLCBTeXMudGltZSgpIC1zdGFydF90aW1lKSkpCiMgICBzYXZlKGxkYXMsZmlsZSA9ICJsZGFzX3Jlc3VsdDIucmRhdGEiKSAjIOWwh+aooeWei+i8uOWHuuaIkOaqlOahiAojICAgfQpgYGAKCj4g6LyJ5YWl5q+P5YCL5Li76aGM55qETERB57WQ5p6cCgpgYGB7cn0KbG9hZCgibGRhc19yZXN1bHQyLnJkYXRhIikKYGBgCgojIyDpgI/pgY5wZXJwbGV4aXR55om+5Yiw5pyA5L2z5Li76aGM5pW4CmBgYHtyfQp0b3BpY3MgPSBjKDIsNCw2LDEwLDE1KQpkYXRhX2ZyYW1lKGsgPSB0b3BpY3MsIHBlcnBsZXggPSBtYXBfZGJsKGxkYXMsIHRvcGljbW9kZWxzOjpwZXJwbGV4aXR5KSkgJT4lCiAgZ2dwbG90KGFlcyhrLCBwZXJwbGV4KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiRXZhbHVhdGluZyBMREEgdG9waWMgbW9kZWxzIiwKICAgICAgIHN1YnRpdGxlID0gIk9wdGltYWwgbnVtYmVyIG9mIHRvcGljcyAoc21hbGxlciBpcyBiZXR0ZXIpIiwKICAgICAgIHggPSAiTnVtYmVyIG9mIHRvcGljcyIsCiAgICAgICB5ID0gIlBlcnBsZXhpdHkiKQoj6YCa5bi45oyRdG9waWPmlbjmnIPmjJHlnKjovYnmipjpu57kuIrvvIwKYGBgCgoKPiBjcmVhdGUgTERBdmlz5omA6ZyA55qEanNvbiBmdW5jdGlvbgrmraRmdW5jdGlvbuaYr+Wwh+WJjemdouS9v+eUqCAiTERBIGZ1bmN0aW9uIuaJgOW7uueri+eahG1vZGVs77yM6L2J5o+b54K6IkxEQVZpcyLlpZfku7bnmoRpbnB1dOagvOW8j+OAggoKYGBge3J9Cgp0b3BpY21vZGVsc19qc29uX2xkYXZpcyA8LSBmdW5jdGlvbihmaXR0ZWQsIGRvY190ZXJtKXsKICAgIHJlcXVpcmUoTERBdmlzKQogICAgcmVxdWlyZShzbGFtKQogIAogICAgIyMj5Lul5LiLZnVuY3Rpb24g55So5L6G6Kej5rG677yM5Li76aGM5pW45aSa5pyD5Ye654++TkHnmoTllY/poYwKICAgICMjIyDlj4PogIMgaHR0cHM6Ly9naXRodWIuY29tL2Nwc2lldmVydC9MREF2aXMvY29tbWl0L2M3MjM0ZDcxMTY4YjFlOTQ2YTM2MWJjMDA1OTNiYzVjNGJmOGU1N2UKICAgIGxzX0xEQSA9IGZ1bmN0aW9uIChwaGkpewogICAgICBqZW5zZW5TaGFubm9uIDwtIGZ1bmN0aW9uKHgsIHkpIHsKICAgICAgICBtIDwtIDAuNSAqICh4ICsgeSkKICAgICAgICBsaHMgPC0gaWZlbHNlKHggPT0gMCwgMCwgeCAqIChsb2coeCkgLSBsb2cobSsxZS0xNikpKQogICAgICAgIHJocyA8LSBpZmVsc2UoeSA9PSAwLCAwLCB5ICogKGxvZyh5KSAtIGxvZyhtKzFlLTE2KSkpCiAgICAgICAgMC41ICogc3VtKGxocykgKyAwLjUgKiBzdW0ocmhzKQogICAgICB9CiAgICAgIGRpc3QubWF0IDwtIHByb3h5OjpkaXN0KHggPSBwaGksIG1ldGhvZCA9IGplbnNlblNoYW5ub24pCiAgICAgIHBjYS5maXQgPC0gc3RhdHM6OmNtZHNjYWxlKGRpc3QubWF0LCBrID0gMikKICAgICAgZGF0YS5mcmFtZSh4ID0gcGNhLmZpdFssIDFdLCB5ID0gcGNhLmZpdFssIDJdKQogICAgfQogIAogICAgICAjIEZpbmQgcmVxdWlyZWQgcXVhbnRpdGllcwogICAgICBwaGkgPC0gYXMubWF0cml4KHBvc3RlcmlvcihmaXR0ZWQpJHRlcm1zKQogICAgICB0aGV0YSA8LSBhcy5tYXRyaXgocG9zdGVyaW9yKGZpdHRlZCkkdG9waWNzKQogICAgICB2b2NhYiA8LSBjb2xuYW1lcyhwaGkpCiAgICAgIHRlcm1fZnJlcSA8LSBzbGFtOjpjb2xfc3Vtcyhkb2NfdGVybSkKICAKICAgICAgIyBDb252ZXJ0IHRvIGpzb24KICAgICAganNvbl9sZGEgPC0gTERBdmlzOjpjcmVhdGVKU09OKHBoaSA9IHBoaSwgdGhldGEgPSB0aGV0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZvY2FiID0gdm9jYWIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb2MubGVuZ3RoID0gYXMudmVjdG9yKHRhYmxlKGRvY190ZXJtJGkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlcm0uZnJlcXVlbmN5ID0gdGVybV9mcmVxLCBtZHMubWV0aG9kID0gbHNfTERBKQogIAogICAgICByZXR1cm4oanNvbl9sZGEpCn0KYGBgCgojIyDnlKLnlJ9MREF2aXPntZDmnpwKCmBgYHtyIGV2YWw9RkFMU0V9Cgp0aGVfbGRhID0gbGRhc1tbMl1dCmpzb25fcmVzIDwtIHRvcGljbW9kZWxzX2pzb25fbGRhdmlzKHRoZV9sZGEsZHRtKQpzZXJWaXMoanNvbl9yZXMsb3Blbi5icm93c2VyID0gVCkjbGFuZGHotorlvoDlt6bpgormi4nvvIzlj6/ku6XnnIvliLDmr4/lgIvkuLvpoYzoo6HpnaLotornjajnibnnmoTlrZcKI3RvcGlj5ZyT5ZyI5ZyI5aaC5p6c5YiG5b6X5b6I6ZaL77yM5Luj6KGo5q+P5YCL5Li76aGM6YO95pyJ5LuW55qE5ZSv5LiA5oCnCiPlnJPlnIjnmoTlpKflsI/vvIzku6Pooajoo6HpnaLmnInlpJrlsJHnmoRkb2N1bWVudCAKYGBgCgojIyMg55Si55SfTERBdmlz5qqU5qGI77yM5a2Y6IezbG9jYWznq68KYGBge3IgZXZhbD1GQUxTRX0Kc2VyVmlzKGpzb25fcmVzLCBvdXQuZGlyID0gInZpcyIsIG9wZW4uYnJvd3NlciA9IFQpCndyaXRlTGluZXMoaWNvbnYocmVhZExpbmVzKCIuL3Zpcy9sZGEuanNvbiIpLCB0byA9ICJVVEY4IikpCmBgYAoKCgojIGNoNC4gTERB5YiG5p6QCgojIyDpgbjlrpo05YCL5Li76aGM5pW455qE5Li76aGM5qih5Z6LCmBgYHtyfQp0aGVfbGRhID0gbGRhc1tbMl1dICMjIOmBuOWumnRvcGljIOeCuiA0IOeahOe1kOaenApgYGAKCmBgYHtyfQp0b3BpY3Nfd29yZHMgPC0gdGlkeSh0aGVfbGRhLCBtYXRyaXggPSAiYmV0YSIpICMg5rOo5oSP77yM5ZyodGlkeSBmdW5jdGlvbuijoemdouimgeS9v+eUqCJiZXRhIuS+huWPluWHulBoaeefqemZo+OAggpjb2xuYW1lcyh0b3BpY3Nfd29yZHMpIDwtIGMoInRvcGljIiwgInRlcm0iLCAicGhpIikKdG9waWNzX3dvcmRzICU+JSBhcnJhbmdlKGRlc2MocGhpKSkgJT4lIGhlYWQoMTApCmBgYAoKPiB0ZXJtc+S+neeFp+WQhOS4u+mhjOeahHBoaeWAvOeUseWkp+WIsOWwj+aOkuW6jwoKYGBge3J9CnRvcGljc193b3JkcyAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oMTIsIHBoaSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXJfd2l0aGluKHRlcm0scGhpLHRvcGljKSwgeSA9IHBoaSwgZmlsbCA9IGFzLmZhY3Rvcih0b3BpYykpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpICt0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseT0nU1RIZWl0aVRDLUxpZ2h0JykpKwogIHNjYWxlX3hfcmVvcmRlcmVkKCkKYGBgCgo+5Y676Zmk5YWx6YCa6Kme5b2Z77yM5Lul5Y+K5rKS5pyJ5oSP576p55qE6Kme5b2ZCgpgYGB7cn0KcmVtb3ZlZF93b3JkID0gYygi5LiN5pivIiwi5rKS5pyJIiwi5LuA6bq8Iiwi6Je756SBIiwi5aSn5r2tIiwi5Y+v5LulIiwi6KGo56S6Iiwi5bCx5pivIiwi5Y+w54GjIiwi55yf55qEIiwi5pyJ5rKS5pyJIikKCnRvcGljc193b3JkcyAlPiUKICBmaWx0ZXIoIXRlcm0gICVpbiUgcmVtb3ZlZF93b3JkKSAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgdG9wX24oMTIsIHBoaSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXJfd2l0aGluKHRlcm0scGhpLHRvcGljKSwgeSA9IHBoaSwgZmlsbCA9IGFzLmZhY3Rvcih0b3BpYykpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHk9J1NUSGVpdGlUQy1MaWdodCcpKSsKICBzY2FsZV94X3Jlb3JkZXJlZCgpCmBgYAoKIyMjIOS4u+mhjOWRveWQjQpgYGB7cn0KdG9waWNzX25hbWUgPSBjKCLmlL/lupznq4vloLQiLCLol7vnpIHlhazmipXllY/poYwiLCLlhazmipXmlK/mjIHmlrnnq4vloLQiLCLlhazmipXlj43lsI3mlrnnq4vloLQiKQpgYGAKCiMjIERvY3VtZW50IOS4u+mhjOWIhuS9iApgYGB7cn0KIyBmb3IgZXZlcnkgZG9jdW1lbnQgd2UgaGF2ZSBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbiBvZiBpdHMgY29udGFpbmVkIHRvcGljcwp0bVJlc3VsdCA8LSBwb3N0ZXJpb3IodGhlX2xkYSkKZG9jX3BybyA8LSB0bVJlc3VsdCR0b3BpY3MKZG9jdW1lbnRfdG9waWNzIDwtIGRvY19wcm9bbWV0YWRhdGEkYXJ0VXJsLF0KZG9jdW1lbnRfdG9waWNzX2RmID1kYXRhLmZyYW1lKGRvY3VtZW50X3RvcGljcykKY29sbmFtZXMoZG9jdW1lbnRfdG9waWNzX2RmKSA9IHRvcGljc19uYW1lCnJvd25hbWVzKGRvY3VtZW50X3RvcGljc19kZikgPSBOVUxMCm5ld3NfdG9waWMgPSBjYmluZChtZXRhZGF0YSxkb2N1bWVudF90b3BpY3NfZGYpCiNMREEgYWxwaGE6CmBgYAoKPiDnj77lnKjmiJHlgJHnnIvmr4/kuIDnr4fnmoTmlofnq6DliIbkvYjkuobvvIEKCiMjIyDmn6XnnIvnibnlrprkuLvpoYznmoTmlofnq6AKKyDpgI/pgY7mib7liLDnibnlrprmlofnq6DnmoTliIbkvYjpgLLooYzmjpLluo/kuYvlvozvvIzlj6/ku6XnnIvliLDmraTkuLvpoYznmoTmr5Tph43pq5jnmoTmlofnq6DlnKjoqI7oq5bku4DpurzjgIIKCmBgYHtyfQpuZXdzX3RvcGljICU+JQogIGFycmFuZ2UoZGVzYyhg5YWs5oqV5Y+N5bCN5pa556uL5aC0YCkpICU+JWhlYWQoMTApIApgYGAKCiMjIyDkuobop6PkuLvpoYzlnKjmmYLplpPnmoTororljJYKYGBge3Igd2FybmluZz1GQUxTRX0KbmV3c190b3BpYyAlPiUgCiAgbXV0YXRlKGFydERhdGUgPSBhcy5EYXRlKGFydERhdGUpKSAlPiUKICBncm91cF9ieShhcnREYXRlID0gZm9ybWF0KGFydERhdGUsJyVZJW0nKSkgJT4lCiAgc3VtbWFyaXNlX2lmKGlzLm51bWVyaWMsIHN1bSwgbmEucm0gPSBUUlVFKSAlPiUKICBtZWx0KGlkLnZhcnMgPSAiYXJ0RGF0ZSIpJT4lCiAgZ2dwbG90KCBhZXMoeD1hcnREYXRlLCB5PXZhbHVlLCBmaWxsPXZhcmlhYmxlKSkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyB5bGFiKCJ2YWx1ZSIpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3JzW2MoMSw1LDgsMTIpXSkrdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHk9J1NUSGVpdGlUQy1MaWdodCcpKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpgYGAKCiMjIyDljrvpmaTnrYbmlbjlsJHmnIjku70KYGBge3Igd2FybmluZz1GQUxTRX0KbmV3c190b3BpYyAlPiUKICBtdXRhdGUoYXJ0RGF0ZSA9IGFzLkRhdGUoYXJ0RGF0ZSkpICU+JSAKICBmaWx0ZXIoICFmb3JtYXQoYXJ0RGF0ZSwnJVklbScpICVpbiUgYygyMDIwMDksMjAyMDEwLDIwMjEwMSkpJT4lCiAgZ3JvdXBfYnkoYXJ0RGF0ZSA9IGZvcm1hdChhcnREYXRlLCclWSVtJykpICU+JQogIHN1bW1hcmlzZV9pZihpcy5udW1lcmljLCBzdW0sIG5hLnJtID0gVFJVRSkgJT4lCiAgbWVsdChpZC52YXJzID0gImFydERhdGUiKSU+JQogIGdncGxvdCggYWVzKHg9YXJ0RGF0ZSwgeT12YWx1ZSwgZmlsbD12YXJpYWJsZSkpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgeWxhYigidmFsdWUiKStzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvcnNbYygxLDUsOCwxMildKSt0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseT0nU1RIZWl0aVRDLUxpZ2h0JykpKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKYGBgCgojIyMg5Lul5q+U5L6L5LqG6Kej5Li76aGM5pmC6ZaT6K6K5YyWCmBgYHtyIHdhcm5pbmc9RkFMU0V9Cm5ld3NfdG9waWMgJT4lCiAgbXV0YXRlKGFydERhdGUgPSBhcy5EYXRlKGFydERhdGUpKSAlPiUgCiAgZmlsdGVyKCAhZm9ybWF0KGFydERhdGUsJyVZJW0nKSAlaW4lIGMoMjAyMDA5LDIwMjAxMCwyMDIxMDEpKSU+JQogIGdyb3VwX2J5KGFydERhdGUgPSBmb3JtYXQoYXJ0RGF0ZSwnJVklbScpKSAlPiUKICBzdW1tYXJpc2VfaWYoaXMubnVtZXJpYywgc3VtLCBuYS5ybSA9IFRSVUUpICU+JQogIG1lbHQoaWQudmFycyA9ICJhcnREYXRlIiklPiUKICBncm91cF9ieShhcnREYXRlKSU+JQogIG11dGF0ZSh0b3RhbF92YWx1ZSA9c3VtKHZhbHVlKSklPiUKICBnZ3Bsb3QoIGFlcyh4PWFydERhdGUsIHk9dmFsdWUvdG90YWxfdmFsdWUsIGZpbGw9dmFyaWFibGUpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIHlsYWIoInByb3BvcnRpb24iKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3JzW2MoMSw1LDgsMTIpXSkrdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHk9J1NUSGVpdGlUQy1MaWdodCcpKSsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpCmBgYAoKIyDoo5zlhYUgLSDkuI3lkIzoqJPnt7RMREHmqKHlnovlpZfku7YKCj4g5Y+D6ICDIGh0dHA6Ly90ZXh0MnZlYy5vcmcvdG9waWNfbW9kZWxpbmcuaHRtbCNsYXRlbnRfZGlyaWNobGV0X2FsbG9jYXRpb24KCmBgYHtyfQpsaWJyYXJ5KHRleHQydmVjKQpsaWJyYXJ5KHVkcGlwZSkKdG9rZW5zIDwtIG1ldGFkYXRhICU+JQogIHVubmVzdF90b2tlbnMod29yZCwgc2VudGVuY2UsIHRva2VuPWNoaV90b2tlbml6ZXIpICU+JQogIGZpbHRlcighc3RyX2RldGVjdCh3b3JkLCByZWdleCgiWzAtOWEtekEtWl0iKSl8IHN0cl9kZXRlY3Qod29yZCwgcmVnZXgoIltBYV1bWnpdIikpKQpgYGAKCiMjIOW7uueri0RUTSBtYXRyaXgKYGBge3J9CmR0ZiA8LSBkb2N1bWVudF90ZXJtX2ZyZXF1ZW5jaWVzKHRva2VucywgZG9jdW1lbnQgPSAiYXJ0VXJsIiwgdGVybSA9ICJ3b3JkIikKZHRtIDwtIGRvY3VtZW50X3Rlcm1fbWF0cml4KHggPSBkdGYpCmR0bV9jbGVhbiA8LSBkdG1fcmVtb3ZlX2xvd2ZyZXEoZHRtLCBtaW5mcmVxID0gMzApCmRpbShkdG1fY2xlYW4pCmBgYAoKIyMgTERBIOaooeWeiwpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQoKc2V0LnNlZWQoMjAxOSkKCnRvcGljX24gPSA0CgpsZGFfbW9kZWwgPXRleHQydmVjOjpMREEkbmV3KG5fdG9waWNzID0gdG9waWNfbixkb2NfdG9waWNfcHJpb3IgPSAwLjEsIHRvcGljX3dvcmRfcHJpb3IgPSAwLjAwMSkKZG9jX3RvcGljX2Rpc3RyID1sZGFfbW9kZWwkZml0X3RyYW5zZm9ybShkdG1fY2xlYW4sIG5faXRlciA9IDEwMDAsIGNvbnZlcmdlbmNlX3RvbCA9IDFlLTUsY2hlY2tfY29udmVyZ2VuY2VfZXZlcnlfbiA9IDEwMCkKYGBgCgo+IOmAmeWAi+avlHRvcGljbW9kZWxz55qEcGFja2FnZei3keW/q+i2heWkmuWAjQoKIyMg5LiA5qij5Y+v5Lul55SoTERBdmlz55qE5aWX5Lu25L6G55yLCmBgYHtyfQpsZGFfbW9kZWwkZ2V0X3RvcF93b3JkcyhuID0gMTAsIGxhbWJkYSA9IDAuNSkgIyMg5p+l55yLIOWJjTEw5Li76aGM5a2XCmxkYV9tb2RlbCRwbG90KCkKIyBsZGFfbW9kZWwkcGxvdChvdXQuZGlyID0ibGRhX3Jlc3VsdCIsIG9wZW4uYnJvd3NlciA9IFRSVUUpCmBgYA==