主題

分析PTT八卦版對台灣停電事件的文字資料和社會網絡資料

一、動機與分析目的

  • 背景與動機
    • 五月初臺灣疫情受到航航機師與諾福特酒店事件影響,疫情開始蔓延,加上久旱未噢,各地區面臨了缺水的窘況,就在這疫情與旱象夾擊下的臺灣,5月13日受興達電廠跳機的影響造成全台分區停電、限電的事件。針對這個事件網路媒體是如何看待、是意外事件還是能源政策造成的?
  • 研究目的
    1. 台灣513大停電的討論重點有哪些? 主要分為哪幾種風向?。
    2. 目前風向最偏哪邊?
    3. 討論513大停電的社群網路如何分布?
    4. 校正回歸的意見領袖有誰?網友的推噓狀態如何?

1.資料集描述

  • 資料來源:中山大學管理學院文字分析平台收集PTT八卦版文章取得之原始csv檔案。
  • 資料集:PPT八卦版。
  • 資料日期區間:2021.05.13~2021.05.18。
  • 資料的關鍵字:檢索「停電」、「缺電」、「興達」、「電廠」、「興達電廠」五個關鍵字,共搜尋出1113篇文章。

二、前置作業

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

1.安裝需要的packages

packages = c("readr", "dplyr", "jiebaR", "tidyr", "tidytext", "igraph", "topicmodels", "ggplot2", "stringr","text2vec")
existing = as.character(installed.packages()[,1])
for(pkg in packages[!(packages %in% existing)]) install.packages(pkg)

2.將require及library載入

library(readr)
library(dplyr)
library(jiebaR)
library(tidyr)
library(tidytext)
library(igraph)
library(topicmodels)
library(stringr)
library(ggplot2)
library(reshape2)
library(wordcloud2)
require(text2vec)

3.載入自平台下載下來的資料

posts <- read_csv("0612-1_articleMetaData.csv") # 文章 
reviews <- read_csv("0612-1_articleReviews.csv") # 回覆 
rd <- read_csv("0612-1_artWordPOSFreq.csv")
head(posts)
head(reviews)
head(rd)

4.文章斷句

# # 文章斷句("\n\n"取代成"。")
mask_meta <- posts %>%
               mutate(sentence=gsub("[\n]{2,}", "。", sentence))
# 
# # 以全形或半形 驚歎號、問號、分號 以及 全形句號 爲依據進行斷句
mask_sentences <- strsplit(mask_meta$sentence,"[。!;?!?;]")
# 
# # 將每句句子,與他所屬的文章連結配對起來,整理成一個dataframe
mask_sentences <- data.frame(
                        artUrl = rep(mask_meta$artUrl, sapply(mask_sentences, length)),
                        sentence = unlist(mask_sentences)
                       ) %>%
                       filter(!str_detect(sentence, regex("^(\t|\n| )*$")))
                       # 如果有\t或\n就去掉
 
mask_sentences$sentence <- as.character(mask_sentences$sentence)
mask_sentences

5.文章斷詞

# 加入自定義的字典
jieba_tokenizer <- worker(user="user_dict.txt", stop_word = "stop_words.txt")

# 設定斷詞function
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)
    }
  })
}

rd_tokens_all <- posts %>%
  unnest_tokens(word, sentence, token=chi_tokenizer) %>% 
  select(-artTime)

# # 用剛剛初始化的斷詞器把sentence斷開
tokens <- mask_sentences %>%
     mutate(sentence = gsub("[[:punct:]]", "",sentence)) %>%
     mutate(sentence = gsub("[0-9a-zA-Z]", "",sentence)) %>%
     unnest_tokens(word, sentence, token=chi_tokenizer) %>%
   count(artUrl, word) %>% # 計算每篇文章出現的字頻
   rename(count=n)
tokens
# save.image(file = "../data/token_result.rdata")

6.清理斷詞結果

freq = 3
# 依據字頻挑字
reserved_word <- tokens %>% 
  group_by(word) %>% 
  count() %>% 
  filter(n > freq) %>% 
  unlist()

mask_removed <- tokens %>% 
  filter(word %in% reserved_word)

#mask_dtm 裡面 nrow:幾篇文章 ; ncol:幾個字
mask_dtm <- mask_removed %>% cast_dtm(artUrl, word, count) 

三、折線圖

1.資料處理

data <- rd %>% 
  dplyr::select(artDate, artUrl) %>% 
  distinct()
article_count_by_date <- data %>% 
  group_by(artDate) %>% 
  summarise(count = n())
head(article_count_by_date, 20)

2.台灣大停電事件在PTT八卦5/13~5/19各時段的文章討論數量變化

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

四、文字雲

data <- rd %>% 
  group_by(word) %>% 
  summarise(sum = sum(count), .groups = 'drop') %>% 
  arrange(desc(sum))

data %>% filter(sum > 50) %>% wordcloud2()

五、情緒分析

1.載入情緒分析字典

# 正向字典txt檔
# 以,將字分隔
P <- read_file("positive.txt")

# 負向字典txt檔
N <- read_file("negative.txt")

#將字串依,分割
#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)

rd_tokens <- rd %>%
  select(-artTime, -artUrl)
head(rd_tokens)

rd_tokens_by_date <- rd_tokens %>% 
  count(artDate, word, sort = TRUE) %>%
  filter(n > 5)
rd_tokens_by_date

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

2.台灣停電事件在PTT八卦版5/13~5/18正負面情緒聲量折線圖

sentiment_count %>%
  ggplot() +
  geom_line(aes(x=artDate,y=count,colour=sentiment)) +
  labs(x=NULL,y="數量")

3.台灣停電事件在PTT八卦版5/13~5/8正負情緒長條圖

rd_tokens_all %>%
  filter(artDate == as.Date("2021-05-13") |
         artDate == as.Date("2021-05-14") | 
         artDate == as.Date("2021-05-15") | 
         artDate == as.Date("2021-05-16") |
         artDate == as.Date("2021-05-17") |
         artDate == as.Date("2021-05-18") ) %>% 
  inner_join(LIWC) %>%
  group_by(word,sentiment) %>%
  summarise(
    count = n()
  ) %>% data.frame() %>% 
  top_n(30,wt = count) %>%
  ungroup() %>% 
  mutate(word = reorder(word, count)) %>%
  ggplot(aes(word, count, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  labs(x= "文字", y="數量") +
  facet_wrap(~sentiment, scales = "free_y") +
  theme(text=element_text(size=14))+
  coord_flip()
Joining, by = "word"
`summarise()` has grouped output by 'word'. You can override using the `.groups` argument.

六、LDA 主題分類

1.LDA 主題分析

ldas = c()
topics = c(2,4,6,10,15)
for(topic in topics){
start_time <- Sys.time()
lda <- LDA(mask_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") # 將模型輸出成檔案
}
[1] "2 topic(s) and use time is  2.04346084594727"
[1] "4 topic(s) and use time is  7.30365300178528"
[1] "6 topic(s) and use time is  16.5211598873138"
[1] "10 topic(s) and use time is  27.2711720466614"
[1] "15 topic(s) and use time is  50.7416360378265"

透過perplexity找到最佳主題數

library(purrr)

Attaching package: 愼㸱愼㸵purrr愼㸱愼㸶

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

    compose, simplify
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")
`data_frame()` was deprecated in tibble 1.1.0.
Please use `tibble()` instead.

2.畫LDAvis 模型

library(udpipe)
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)
set.seed(2021)

topic_n = 6

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)
lda_model$get_top_words(n = 10, lambda = 0.5)
lda_model$plot()
lda_model$plot(out.dir ="lda_result", open.browser = TRUE)

將剛處理好的dtm放入LDA函式分析

# LDA分成6個主題
mask_lda <- LDA(mask_dtm, k = 6, control = list(seed = 123))

取出代表字詞(term)

#removed_word = c("不是","每天","出來","覺得") 
removed_word = c("一下","不是","停電","有沒有","不會","發電") 
# 看各群的常用詞彙
tidy(mask_lda, matrix = "beta") %>% # 取出topic term beta值
  filter(! term %in% removed_word) %>% 
  group_by(topic) %>%
  top_n(10, beta) %>% # beta值前10的字
  ungroup() %>%
  mutate(topic = as.factor(topic),
         term = reorder_within(term, beta, topic)) %>%
  ggplot(aes(term, beta, fill = topic)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~ topic, scales = "free") +
  coord_flip() +
  scale_x_reordered()

可以歸納出 + topic 1 = “能源政策” + topic 2 = “抱怨政府” + topic 3 = “政治人物議題” + topic 4 = “如何讓供電穩定” + topic 5 = “嘲諷下次停電時間” + topic 6 = “停電原因究責” 以下我們挑出第一個主題、第二個主題和第六個主題來做比較。

取出代表主題(topic)

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

3.資料內容探索

posts_topic <- merge(x = posts, y = mask_topics, by.x = "artUrl", by.y="document")

# 看一下各主題在說甚麼
set.seed(12345)

posts_topic %>% # 主題一
  filter(topic==1) %>%
  select(artTitle) %>%
  unique() %>%
  sample_n(12)


posts_topic %>% # 主題二
  filter(topic==2) %>%
  select(artTitle) %>%
  unique() %>%
  sample_n(12)

posts_topic %>% # 主題三
  filter(topic==3) %>%
  select(artTitle) %>%
  unique() %>%
  sample_n(12)

posts_topic %>% # 主題四
  filter(topic==4) %>%
  select(artTitle) %>%
  unique() %>%
  sample_n(12)

posts_topic %>% # 主題五
  filter(topic==5) %>%
  select(artTitle) %>%
  unique() %>%
  sample_n(12)

posts_topic %>% # 主題六
  filter(topic==6) %>%
  select(artTitle) %>%
  unique() %>%
  sample_n(12)

4.日期主題分布

posts_topic %>%
  mutate(artDate = as.Date(artDate)) %>% 
  group_by(artDate,topic) %>%
  summarise(sum =sum(topic)) %>%
  ggplot(aes(x= artDate,y=sum,fill=as.factor(topic))) +
  geom_col(position="fill") 


posts_topic %>%
  group_by(artCat,topic) %>%
  summarise(sum = n())  %>%
  ggplot(aes(x= artCat,y=sum,fill=as.factor(topic))) +
  geom_col(position="dodge") 

七、社群網路圖

資料合併

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

# 把文章和topic
posts_Reviews <- merge(x = posts_Reviews, y = mask_topics, by.x = "artUrl", by.y="document")
head(posts_Reviews,3)

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

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

1.基本網路圖

建立網路關係

reviewNetwork <- graph_from_data_frame(d=link, directed=T)
reviewNetwork

2.資料篩選

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

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

## 帳號發文篇數
post_count = posts %>%
  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

## 發文者
poster_select <- post_count %>% filter(count >= 2)
posts <- posts %>%  filter(posts$artPoster %in% poster_select$artPoster)

## 回覆者
reviewer_select <- review_count %>%  filter(count >= 5)
reviews <- reviews %>%  filter(reviews$cmtPoster %in% reviewer_select$cmtPoster)
# 檢視參與人數
length(unique(posts_Reviews$artPoster)) # 發文者數量 911
length(unique(posts_Reviews$cmtPoster)) # 回覆者數量 15224
allPoster <- c(posts_Reviews$artPoster, posts_Reviews$cmtPoster) # 總參與人數 15641
length(unique(allPoster))

標記所有出現過得使用者

  • poster:只發過文、發過文+留過言
  • replyer:只留過言
userList <- data.frame(user=unique(allPoster)) %>%
              mutate(type=ifelse(user%in%posts$artPoster, "poster", "replyer"))
head(userList,3)

3.以日期篩選社群

link <- posts_Reviews %>%
      group_by(cmtPoster, artUrl) %>% 
      filter(n()>3) %>% 
      filter(commentNum > 5) %>%
      filter(artCat=="Gossiping") %>% 
      filter(artDate == as.Date('2021-05-13')) %>%
      select(cmtPoster, artPoster, artUrl) %>% 
      unique()
link

篩選在link裡面有出現的使用者

filtered_user <- userList %>%
          filter(user%in%link$cmtPoster | user%in%link$artPoster) %>%
          arrange(desc(type))
head(filtered_user,3)

因爲圖片箭頭有點礙眼,所以這裏我們先把關係的方向性拿掉,減少圖片中的不必要的資訊 set.seed 因為igraph呈現的方向是隨機的

set.seed(487)
# v=filtered_user

reviewNetwork = degree(reviewNetwork) > 2
reviewNetwork <- graph_from_data_frame(d=link, v=filtered_user, directed=F)
plot(reviewNetwork, vertex.size=3, edge.arrow.size=0.3,vertex.label=NA)

加上nodes的顯示資訊 用使用者的身份來區分點的顏色

  • poster:gold(有發文)
  • replyer:lightblue(只有回覆文章)
set.seed(487)
V(reviewNetwork)$color <- ifelse(V(reviewNetwork)$type=="poster", "gold", "lightblue")
plot(reviewNetwork, vertex.size=3, edge.arrow.size=0.3,vertex.label=NA)

可以稍微看出圖中的點(人)之間有一定的關聯,不過目前只有單純圖形我們無法分析其中的內容。 因此以下我們將資料集中的資訊加到我們的圖片中。

為點加上帳號名字,用degree篩選要顯示出的使用者,以免圖形被密密麻麻的文字覆蓋

filter_degree = 5
set.seed(123)

# 設定 node 的 label/ color
labels <- degree(reviewNetwork) # 算出每個點的degree
V(reviewNetwork)$label <- names(labels)
V(reviewNetwork)$color <- ifelse(V(reviewNetwork)$type=="poster", "gold", "lightblue")

plot(
  reviewNetwork, 
  vertex.size=3, 
  edge.width=3, 
  vertex.label.dist=1,
  vertex.label=ifelse(degree(reviewNetwork) > filter_degree, V(reviewNetwork)$label, NA),vertex.label.font=2)

我們可以看到基本的使用者關係,但是我們希望能夠將更進階的資訊視覺化。 例如:使用者經常參與的文章種類,或是使用者在該社群網路中是否受到歡迎。

4.以主題篩選社群

抓link 挑選出2021-05-13當天的文章, 篩選一篇文章回覆3次以上者,且文章留言數多餘5則, 文章主題歸類為1、2與6者, 欄位只取:cmtPoster(評論者), artPoster(發文者), artUrl(文章連結), topic(主題)

link <- posts_Reviews %>%
      group_by(cmtPoster, artUrl) %>% 
      filter(n()>3) %>% 
      filter(commentNum > 5) %>%
      filter(artCat=="Gossiping") %>% #HatePolitics / Gossiping
      filter(artDate == as.Date('2021-05-13')) %>%
      filter( topic == 1 | topic == 2 | topic == 6) %>% 
      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)

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

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", "gold", "lightblue")

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

# 畫出社群網路圖
set.seed(5432)
plot(reviewNetwork, vertex.size=3, 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("gold","lightblue"), pt.cex=1, cex=1)
legend("topleft", c("批評調侃","報導相關"), 
       col=c("palevioletred", "lightgreen"), lty=1, cex=1)

6.使用者是否受到歡迎

filter_degree = 6 # 使用者degree

# 過濾留言者對發文者的推噓程度
link <- posts_Reviews %>%
      filter(artCat=="Gossiping") %>% 
      filter(commentNum > 10) %>%
      filter(cmtStatus!="→") %>%
      group_by(cmtPoster, artUrl) %>%
      filter( n() > 2) %>%
      ungroup() %>% 
      select(cmtPoster, artPoster, artUrl, cmtStatus) %>% 
      unique()

# 接下來把網路圖畫出來,跟前面做的事都一樣,因此不再細述

# 篩選link中有出現的使用者
filtered_user <- userList %>%
          filter(user%in%link$cmtPoster | user%in%link$artPoster) %>%
          arrange(desc(type))

# 建立網路關係
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", "gold", "lightblue")


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

# 畫出社群網路圖
set.seed(5432)
plot(reviewNetwork, vertex.size=2, 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("gold","lightblue"), pt.cex=1, cex=1)
legend("topleft", c("推","噓"), 
       col=c("lightgreen","palevioletred"), lty=1, cex=1)

八、總結

  1. 台灣停電事件的討論重點有哪些? 主要分為哪幾種風向?
    對於2021-05-21 ~ 2021-05-23收集的文章,大概可以分成嘲諷校正回歸、客觀討論校正回歸這兩種,其他還有著重討論確診個案足跡或和疫苗相關的討論等四種。討論重點多在於統計「數字」、「公布日期」等案例的計算方式。

  2. 目前風向最偏哪邊?
    客觀討論計算方式的文章不少,但嘲諷、八卦性質的文章居多。

  3. 討論校正回歸的社群網路如何分布?
    以社群文章數來看,批評嘲諷的網友較多,但從社群網路觀察發現,兩邊的貼文討論聲量都很高。

  4. 校正回歸的意見領袖有誰?網友的推噓狀態如何?
    因為資料選取的時間較短,只要幾篇回覆量高的貼文,就有機會成為社群中心,在八卦版上,以報導討論為主的意見領袖有 centre0130,回覆推噓皆有,調侃批評部分則有 hstf,網友大多正面推文。

LS0tDQp0aXRsZTogIuekvue+pOWqkumrlOWIhuaekCDmnJ/mnKvloLHlkYog5o6i6KiOcHR05YWr5Y2m5p2/5bCN5Y+w54Gj5YGc6Zu75LqL5Lu255qE55yL5rOVIg0KYXV0aG9yOiAi56ysMTnntYQiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIGNzczogc3R5bGUuY3NzDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQojIyDkuLvpoYwNCj4g5YiG5p6QUFRU5YWr5Y2m54mI5bCN5Y+w54Gj5YGc6Zu75LqL5Lu255qE5paH5a2X6LOH5paZ5ZKM56S+5pyD57ay57Wh6LOH5paZDQoNCiMg5LiA44CB5YuV5qmf6IiH5YiG5p6Q55uu55qEDQotIOiDjOaZr+iIh+WLleapnw0KICArIOS6lOaciOWIneiHuueBo+eWq+aDheWPl+WIsOiIquiIquapn+W4q+iIh+irvuemj+eJuemFkuW6l+S6i+S7tuW9semfv++8jOeWq+aDhemWi+Wni+iUk+W7tu+8jOWKoOS4iuS5heaXseacquWZou+8jOWQhOWcsOWNgOmdouiHqOS6hue8uuawtOeahOeqmOazge+8jOWwseWcqOmAmeeWq+aDheiIh+aXseixoeWkvuaTiuS4i+eahOiHuueBo++8jDXmnIgxM+aXpeWPl+iIiOmBlOmbu+W7oOi3s+apn+eahOW9semfv+mAoOaIkOWFqOWPsOWIhuWNgOWBnOmbu+OAgemZkOmbu+eahOS6i+S7tuOAgumHneWwjemAmeWAi+S6i+S7tue2sui3r+WqkumrlOaYr+WmguS9leeci+W+heOAgeaYr+aEj+WkluS6i+S7tumChOaYr+iDvea6kOaUv+etlumAoOaIkOeahO+8nw0KDQorIOeglOeptuebrueahA0KICAgMS4g5Y+w54GjNTEz5aSn5YGc6Zu755qE6KiO6KuW6YeN6bue5pyJ5ZOq5LqbPyDkuLvopoHliIbngrrlk6rlub7nqK7poqjlkJE/44CCPC9icj4NCiAgIDIuIOebruWJjemiqOWQkeacgOWBj+WTqumCij88L2JyPg0KICAgMy4g6KiO6KuWNTEz5aSn5YGc6Zu755qE56S+576k57ay6Lev5aaC5L2V5YiG5biDPw0KICAgMy4g5qCh5q2j5Zue5q2455qE5oSP6KaL6aCY6KKW5pyJ6KqwP+e2suWPi+eahOaOqOWZk+eLgOaFi+WmguS9lT88L2JyPg0KDQoNCg0KIyMgMS7os4fmlpnpm4bmj4/ov7ANCisg6LOH5paZ5L6G5rqQ77ya5Lit5bGx5aSn5a24566h55CG5a246Zmi5paH5a2X5YiG5p6Q5bmz5Y+w5pS26ZuGUFRU5YWr5Y2m54mI5paH56ug5Y+W5b6X5LmL5Y6f5aeLY3N25qqU5qGI44CCDQorIOizh+aWmembhu+8mlBQVOWFq+WNpueJiOOAgg0KKyDos4fmlpnml6XmnJ/ljYDplpPvvJoyMDIxLjA1LjEzfjIwMjEuMDUuMTjjgIINCisg6LOH5paZ55qE6Zec6Y215a2XOuaqoue0ouOAjOWBnOmbu+OAjeOAgeOAjOe8uumbu+OAjeOAgeOAjOiIiOmBlOOAjeOAgeOAjOmbu+W7oOOAjeOAgeOAjOiIiOmBlOmbu+W7oOOAjeS6lOWAi+mXnOmNteWtl++8jOWFseaQnOWwi+WHujExMTPnr4fmlofnq6DjgIINCg0KDQojIOS6jOOAgeWJjee9ruS9nOalrQ0KYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KU3lzLnNldGxvY2FsZShjYXRlZ29yeSA9ICJMQ19BTEwiLCBsb2NhbGUgPSAiemhfVFcuVVRGLTgiKSAjIOmBv+WFjeS4reaWh+S6gueivA0KYGBgDQoNCiMjIDEu5a6J6KOd6ZyA6KaB55qEcGFja2FnZXMNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQpwYWNrYWdlcyA9IGMoInJlYWRyIiwgImRwbHlyIiwgImppZWJhUiIsICJ0aWR5ciIsICJ0aWR5dGV4dCIsICJpZ3JhcGgiLCAidG9waWNtb2RlbHMiLCAiZ2dwbG90MiIsICJzdHJpbmdyIiwidGV4dDJ2ZWMiKQ0KZXhpc3RpbmcgPSBhcy5jaGFyYWN0ZXIoaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKQ0KZm9yKHBrZyBpbiBwYWNrYWdlc1shKHBhY2thZ2VzICVpbiUgZXhpc3RpbmcpXSkgaW5zdGFsbC5wYWNrYWdlcyhwa2cpDQpgYGANCg0KIyMgMi7lsIdyZXF1aXJl5Y+KbGlicmFyeei8ieWFpQ0KYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGppZWJhUikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHRpZHl0ZXh0KQ0KbGlicmFyeShpZ3JhcGgpDQpsaWJyYXJ5KHRvcGljbW9kZWxzKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShyZXNoYXBlMikNCmxpYnJhcnkod29yZGNsb3VkMikNCnJlcXVpcmUodGV4dDJ2ZWMpDQpgYGANCg0KDQojIyAzLui8ieWFpeiHquW5s+WPsOS4i+i8ieS4i+S+hueahOizh+aWmQ0KYGBge3IgbWVzc2FnZT1GQUxTRX0NCnBvc3RzIDwtIHJlYWRfY3N2KCIwNjEyLTFfYXJ0aWNsZU1ldGFEYXRhLmNzdiIpICMg5paH56ugIA0KcmV2aWV3cyA8LSByZWFkX2NzdigiMDYxMi0xX2FydGljbGVSZXZpZXdzLmNzdiIpICMg5Zue6KaGIA0KcmQgPC0gcmVhZF9jc3YoIjA2MTItMV9hcnRXb3JkUE9TRnJlcS5jc3YiKQ0KaGVhZChwb3N0cykNCmhlYWQocmV2aWV3cykNCmhlYWQocmQpDQpgYGANCg0KIyMgNC7mlofnq6Dmlrflj6UNCmBgYHtyfQ0KIyAjIOaWh+eroOaWt+WPpSgiXG5cbiLlj5bku6PmiJAi44CCIikNCm1hc2tfbWV0YSA8LSBwb3N0cyAlPiUNCiAgICAgICAgICAgICAgIG11dGF0ZShzZW50ZW5jZT1nc3ViKCJbXG5dezIsfSIsICLjgIIiLCBzZW50ZW5jZSkpDQojIA0KIyAjIOS7peWFqOW9ouaIluWNiuW9oiDpqZrmrY7omZ/jgIHllY/omZ/jgIHliIbomZ8g5Lul5Y+KIOWFqOW9ouWPpeiZnyDniLLkvp3mk5rpgLLooYzmlrflj6UNCm1hc2tfc2VudGVuY2VzIDwtIHN0cnNwbGl0KG1hc2tfbWV0YSRzZW50ZW5jZSwiW+OAgu+8ge+8m++8nyE/O10iKQ0KIyANCiMgIyDlsIfmr4/lj6Xlj6XlrZDvvIzoiIfku5bmiYDlsaznmoTmlofnq6DpgKPntZDphY3lsI3otbfkvobvvIzmlbTnkIbmiJDkuIDlgItkYXRhZnJhbWUNCm1hc2tfc2VudGVuY2VzIDwtIGRhdGEuZnJhbWUoDQogICAgICAgICAgICAgICAgICAgICAgICBhcnRVcmwgPSByZXAobWFza19tZXRhJGFydFVybCwgc2FwcGx5KG1hc2tfc2VudGVuY2VzLCBsZW5ndGgpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbnRlbmNlID0gdW5saXN0KG1hc2tfc2VudGVuY2VzKQ0KICAgICAgICAgICAgICAgICAgICAgICApICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIXN0cl9kZXRlY3Qoc2VudGVuY2UsIHJlZ2V4KCJeKFx0fFxufCApKiQiKSkpDQogICAgICAgICAgICAgICAgICAgICAgICMg5aaC5p6c5pyJXHTmiJZcbuWwseWOu+aOiQ0KIA0KbWFza19zZW50ZW5jZXMkc2VudGVuY2UgPC0gYXMuY2hhcmFjdGVyKG1hc2tfc2VudGVuY2VzJHNlbnRlbmNlKQ0KbWFza19zZW50ZW5jZXMNCmBgYA0KDQoNCiMjIDUu5paH56ug5pa36KmeDQpgYGB7cn0NCiMg5Yqg5YWl6Ieq5a6a576p55qE5a2X5YW4DQpqaWViYV90b2tlbml6ZXIgPC0gd29ya2VyKHVzZXI9InVzZXJfZGljdC50eHQiLCBzdG9wX3dvcmQgPSAic3RvcF93b3Jkcy50eHQiKQ0KDQojIOioreWumuaWt+ipnmZ1bmN0aW9uDQpjaGlfdG9rZW5pemVyIDwtIGZ1bmN0aW9uKHQpIHsNCiAgbGFwcGx5KHQsIGZ1bmN0aW9uKHgpIHsNCiAgICBpZihuY2hhcih4KT4xKXsNCiAgICAgIHRva2VucyA8LSBzZWdtZW50KHgsIGppZWJhX3Rva2VuaXplcikNCiAgICAgICMg5Y675o6J5a2X5Liy6ZW35bqm54iyMeeahOipnuW9mQ0KICAgICAgdG9rZW5zIDwtIHRva2Vuc1tuY2hhcih0b2tlbnMpPjFdDQogICAgICByZXR1cm4odG9rZW5zKQ0KICAgIH0NCiAgfSkNCn0NCg0KcmRfdG9rZW5zX2FsbCA8LSBwb3N0cyAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzZW50ZW5jZSwgdG9rZW49Y2hpX3Rva2VuaXplcikgJT4lIA0KICBzZWxlY3QoLWFydFRpbWUpDQoNCiMgIyDnlKjliZvliZvliJ3lp4vljJbnmoTmlrfoqZ7lmajmiopzZW50ZW5jZeaWt+mWiw0KdG9rZW5zIDwtIG1hc2tfc2VudGVuY2VzICU+JQ0KICAgICBtdXRhdGUoc2VudGVuY2UgPSBnc3ViKCJbWzpwdW5jdDpdXSIsICIiLHNlbnRlbmNlKSkgJT4lDQogICAgIG11dGF0ZShzZW50ZW5jZSA9IGdzdWIoIlswLTlhLXpBLVpdIiwgIiIsc2VudGVuY2UpKSAlPiUNCiAgICAgdW5uZXN0X3Rva2Vucyh3b3JkLCBzZW50ZW5jZSwgdG9rZW49Y2hpX3Rva2VuaXplcikgJT4lDQogICBjb3VudChhcnRVcmwsIHdvcmQpICU+JSAjIOioiOeul+avj+evh+aWh+eroOWHuuePvueahOWtl+mguw0KICAgcmVuYW1lKGNvdW50PW4pDQp0b2tlbnMNCiMgc2F2ZS5pbWFnZShmaWxlID0gIi4uL2RhdGEvdG9rZW5fcmVzdWx0LnJkYXRhIikNCmBgYA0KDQojIyA2Lua4heeQhuaWt+ipnue1kOaenA0KYGBge3J9DQpmcmVxID0gMw0KIyDkvp3mk5rlrZfpoLvmjJHlrZcNCnJlc2VydmVkX3dvcmQgPC0gdG9rZW5zICU+JSANCiAgZ3JvdXBfYnkod29yZCkgJT4lIA0KICBjb3VudCgpICU+JSANCiAgZmlsdGVyKG4gPiBmcmVxKSAlPiUgDQogIHVubGlzdCgpDQoNCm1hc2tfcmVtb3ZlZCA8LSB0b2tlbnMgJT4lIA0KICBmaWx0ZXIod29yZCAlaW4lIHJlc2VydmVkX3dvcmQpDQoNCiNtYXNrX2R0bSDoo6HpnaIgbnJvdzrlub7nr4fmlofnq6AgOyBuY29sOuW5vuWAi+Wtlw0KbWFza19kdG0gPC0gbWFza19yZW1vdmVkICU+JSBjYXN0X2R0bShhcnRVcmwsIHdvcmQsIGNvdW50KSANCmBgYA0KDQojIOS4ieOAgeaKmOe3muWclg0KIyMgMS7os4fmlpnomZXnkIYNCmBgYHtyfQ0KZGF0YSA8LSByZCAlPiUgDQogIGRwbHlyOjpzZWxlY3QoYXJ0RGF0ZSwgYXJ0VXJsKSAlPiUgDQogIGRpc3RpbmN0KCkNCmFydGljbGVfY291bnRfYnlfZGF0ZSA8LSBkYXRhICU+JSANCiAgZ3JvdXBfYnkoYXJ0RGF0ZSkgJT4lIA0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpDQpoZWFkKGFydGljbGVfY291bnRfYnlfZGF0ZSwgMjApDQpgYGANCg0KDQojIyAyLuWPsOeBo+Wkp+WBnOmbu+S6i+S7tuWcqFBUVOWFq+WNpjUvMTN+NS8xOeWQhOaZguauteeahOaWh+eroOiojuirluaVuOmHj+iuiuWMlg0KYGBge3J9DQpwb3N0cyAlPiUgDQogIG11dGF0ZShhcnREYXRlID0gYXMuRGF0ZShhcnREYXRlKSkgJT4lDQogIGdyb3VwX2J5KGFydERhdGUpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpJT4lDQogIGdncGxvdChhZXMoYXJ0RGF0ZSxjb3VudCkpKw0KICAgIGdlb21fbGluZShjb2xvcj0icmVkIikrDQogICAgZ2VvbV9wb2ludCgpDQpgYGANCg0KIyDlm5vjgIHmloflrZfpm7INCmBgYHtyfQ0KZGF0YSA8LSByZCAlPiUgDQogIGdyb3VwX2J5KHdvcmQpICU+JSANCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShjb3VudCksIC5ncm91cHMgPSAnZHJvcCcpICU+JSANCiAgYXJyYW5nZShkZXNjKHN1bSkpDQoNCmRhdGEgJT4lIGZpbHRlcihzdW0gPiA1MCkgJT4lIHdvcmRjbG91ZDIoKQ0KYGBgDQoNCiMg5LqU44CB5oOF57eS5YiG5p6QDQojIyAxLui8ieWFpeaDhee3kuWIhuaekOWtl+WFuA0KYGBge3J9DQojIOato+WQkeWtl+WFuHR4dOaqlA0KIyDku6Us5bCH5a2X5YiG6ZqUDQpQIDwtIHJlYWRfZmlsZSgicG9zaXRpdmUudHh0IikNCg0KIyDosqDlkJHlrZflhbh0eHTmqpQNCk4gPC0gcmVhZF9maWxlKCJuZWdhdGl2ZS50eHQiKQ0KDQoj5bCH5a2X5Liy5L6dLOWIhuWJsg0KI3N0cnNwbGl05Zue5YKzbGlzdCAsIOaIkeWAkeWPluWHumxpc3TkuK3nmoTnrKzkuIDlgIvlhYPntKANClAgPSBzdHJzcGxpdChQLCAiLCIpW1sxXV0NCk4gPSBzdHJzcGxpdChOLCAiLCIpW1sxXV0NCg0KIyDlu7rnq4tkYXRhZnJhbWUg5pyJ5YWp5YCL5qyE5L2Nd29yZCxzZW50aW1lbnRz77yMd29yZOashOS9jeWFp+WuueaYr+Wtl+WFuOWQkemHjw0KUCA9IGRhdGEuZnJhbWUod29yZCA9IFAsIHNlbnRpbWVudCA9ICJwb3NpdGl2ZSIpDQpOID0gZGF0YS5mcmFtZSh3b3JkID0gTiwgc2VudGltZW50ID0gIm5lZ2F0aXZlIikNCkxJV0MgPSByYmluZChQLCBOKQ0KDQpyZF90b2tlbnMgPC0gcmQgJT4lDQogIHNlbGVjdCgtYXJ0VGltZSwgLWFydFVybCkNCmhlYWQocmRfdG9rZW5zKQ0KDQpyZF90b2tlbnNfYnlfZGF0ZSA8LSByZF90b2tlbnMgJT4lIA0KICBjb3VudChhcnREYXRlLCB3b3JkLCBzb3J0ID0gVFJVRSkgJT4lDQogIGZpbHRlcihuID4gNSkNCnJkX3Rva2Vuc19ieV9kYXRlDQoNCnJkX3Rva2Vuc19ieV9kYXRlICU+JQ0KICBpbm5lcl9qb2luKExJV0MpICU+JQ0KICBzZWxlY3Qod29yZCkgJT4lDQogIGlubmVyX2pvaW4oTElXQykgDQoNCg0Kc2VudGltZW50X2NvdW50ID0gcmRfdG9rZW5zX2J5X2RhdGUgJT4lDQogIHNlbGVjdChhcnREYXRlLHdvcmQsbikgJT4lDQogIGlubmVyX2pvaW4oTElXQykgJT4lIA0KICBncm91cF9ieShhcnREYXRlLHNlbnRpbWVudCkgJT4lDQogIHN1bW1hcmlzZShjb3VudD1zdW0obikpDQpgYGANCg0KDQojIyAyLuWPsOeBo+WBnOmbu+S6i+S7tuWcqFBUVOWFq+WNpueJiDUvMTN+NS8xOOato+iyoOmdouaDhee3kuiBsumHj+aKmOe3muWclg0KYGBge3J9DQpzZW50aW1lbnRfY291bnQgJT4lDQogIGdncGxvdCgpICsNCiAgZ2VvbV9saW5lKGFlcyh4PWFydERhdGUseT1jb3VudCxjb2xvdXI9c2VudGltZW50KSkgKw0KICBsYWJzKHg9TlVMTCx5PSLmlbjph48iKQ0KYGBgDQoNCiMjIDMu5Y+w54Gj5YGc6Zu75LqL5Lu25ZyoUFRU5YWr5Y2m54mINS8xM341LzjmraPosqDmg4Xnt5Lplbfmop3lnJYNCmBgYHtyfQ0KcmRfdG9rZW5zX2FsbCAlPiUNCiAgZmlsdGVyKGFydERhdGUgPT0gYXMuRGF0ZSgiMjAyMS0wNS0xMyIpIHwNCiAgICAgICAgIGFydERhdGUgPT0gYXMuRGF0ZSgiMjAyMS0wNS0xNCIpIHwgDQogICAgICAgICBhcnREYXRlID09IGFzLkRhdGUoIjIwMjEtMDUtMTUiKSB8IA0KICAgICAgICAgYXJ0RGF0ZSA9PSBhcy5EYXRlKCIyMDIxLTA1LTE2IikgfA0KICAgICAgICAgYXJ0RGF0ZSA9PSBhcy5EYXRlKCIyMDIxLTA1LTE3IikgfA0KICAgICAgICAgYXJ0RGF0ZSA9PSBhcy5EYXRlKCIyMDIxLTA1LTE4IikgKSAlPiUgDQogIGlubmVyX2pvaW4oTElXQykgJT4lDQogIGdyb3VwX2J5KHdvcmQsc2VudGltZW50KSAlPiUNCiAgc3VtbWFyaXNlKA0KICAgIGNvdW50ID0gbigpDQogICkgJT4lIGRhdGEuZnJhbWUoKSAlPiUgDQogIHRvcF9uKDMwLHd0ID0gY291bnQpICU+JQ0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgY291bnQpKSAlPiUNCiAgZ2dwbG90KGFlcyh3b3JkLCBjb3VudCwgZmlsbCA9IHNlbnRpbWVudCkpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBsYWJzKHg9ICLmloflrZciLCB5PSLmlbjph48iKSArDQogIGZhY2V0X3dyYXAofnNlbnRpbWVudCwgc2NhbGVzID0gImZyZWVfeSIpICsNCiAgdGhlbWUodGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xNCkpKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQojIOWFreOAgUxEQSDkuLvpoYzliIbpoZ4NCg0KIyMgMS5MREEg5Li76aGM5YiG5p6QDQpgYGB7cn0NCmxkYXMgPSBjKCkNCnRvcGljcyA9IGMoMiw0LDYsMTAsMTUpDQpmb3IodG9waWMgaW4gdG9waWNzKXsNCnN0YXJ0X3RpbWUgPC0gU3lzLnRpbWUoKQ0KbGRhIDwtIExEQShtYXNrX2R0bSwgayA9IHRvcGljLCBjb250cm9sID0gbGlzdChzZWVkID0gMjAyMSkpDQpsZGFzID1jKGxkYXMsbGRhKQ0KcHJpbnQocGFzdGUodG9waWMgLHBhc3RlKCJ0b3BpYyhzKSBhbmQgdXNlIHRpbWUgaXMgIiwgU3lzLnRpbWUoKSAtc3RhcnRfdGltZSkpKQ0Kc2F2ZShsZGFzLGZpbGUgPSAibGRhc19yZXN1bHQucmRhdGEiKSAjIOWwh+aooeWei+i8uOWHuuaIkOaqlOahiA0KfQ0KYGBgDQrpgI/pgY5wZXJwbGV4aXR55om+5Yiw5pyA5L2z5Li76aGM5pW4DQpgYGB7cn0NCmxpYnJhcnkocHVycnIpDQp0b3BpY3MgPSBjKDIsNCw2LDEwLDE1KQ0KZGF0YV9mcmFtZShrID0gdG9waWNzLCBwZXJwbGV4ID0gbWFwX2RibChsZGFzLCB0b3BpY21vZGVsczo6cGVycGxleGl0eSkpICU+JQ0KICBnZ3Bsb3QoYWVzKGssIHBlcnBsZXgpKSArDQogIGdlb21fcG9pbnQoKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyh0aXRsZSA9ICJFdmFsdWF0aW5nIExEQSB0b3BpYyBtb2RlbHMiLA0KICAgICAgIHN1YnRpdGxlID0gIk9wdGltYWwgbnVtYmVyIG9mIHRvcGljcyAoc21hbGxlciBpcyBiZXR0ZXIpIiwNCiAgICAgICB4ID0gIk51bWJlciBvZiB0b3BpY3MiLA0KICAgICAgIHkgPSAiUGVycGxleGl0eSIpDQpgYGANCiMjIDIu55WrTERBdmlzIOaooeWeiw0KYGBge3J9DQpsaWJyYXJ5KHVkcGlwZSkNCmR0ZiA8LSBkb2N1bWVudF90ZXJtX2ZyZXF1ZW5jaWVzKHRva2VucywgZG9jdW1lbnQgPSAiYXJ0VXJsIiwgdGVybSA9ICJ3b3JkIikNCmR0bSA8LSBkb2N1bWVudF90ZXJtX21hdHJpeCh4ID0gZHRmKQ0KZHRtX2NsZWFuIDwtIGR0bV9yZW1vdmVfbG93ZnJlcShkdG0sIG1pbmZyZXEgPSAzMCkNCmRpbShkdG1fY2xlYW4pDQpzZXQuc2VlZCgyMDIxKQ0KDQp0b3BpY19uID0gNg0KDQpsZGFfbW9kZWwgPXRleHQydmVjOjpMREEkbmV3KG5fdG9waWNzID0gdG9waWNfbixkb2NfdG9waWNfcHJpb3IgPSAwLjEsIHRvcGljX3dvcmRfcHJpb3IgPSAwLjAwMSkNCmRvY190b3BpY19kaXN0ciA9bGRhX21vZGVsJGZpdF90cmFuc2Zvcm0oZHRtX2NsZWFuLCBuX2l0ZXIgPSAxMDAwLCBjb252ZXJnZW5jZV90b2wgPSAxZS01LGNoZWNrX2NvbnZlcmdlbmNlX2V2ZXJ5X24gPSAxMDApDQpsZGFfbW9kZWwkZ2V0X3RvcF93b3JkcyhuID0gMTAsIGxhbWJkYSA9IDAuNSkNCmxkYV9tb2RlbCRwbG90KCkNCmxkYV9tb2RlbCRwbG90KG91dC5kaXIgPSJsZGFfcmVzdWx0Iiwgb3Blbi5icm93c2VyID0gVFJVRSkNCg0KYGBgDQoNCmBgYHtyIGVjaG89RkFMU0UsIGZpZy5jYXA9IkxEQXZpcyIsIG91dC53aWR0aCA9ICczMCUnfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIkxEQXZpcy5wbmciKQ0KYGBgDQoNCg0KDQoNCuWwh+WJm+iZleeQhuWlveeahGR0beaUvuWFpUxEQeWHveW8j+WIhuaekA0KYGBge3J9DQojIExEQeWIhuaIkDblgIvkuLvpoYwNCm1hc2tfbGRhIDwtIExEQShtYXNrX2R0bSwgayA9IDYsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAxMjMpKQ0KYGBgDQoNCg0K5Y+W5Ye65Luj6KGo5a2X6KmeKHRlcm0pDQpgYGB7cn0NCiNyZW1vdmVkX3dvcmQgPSBjKCLkuI3mmK8iLCLmr4/lpKkiLCLlh7rkvoYiLCLoprrlvpciKSANCnJlbW92ZWRfd29yZCA9IGMoIuS4gOS4iyIsIuS4jeaYryIsIuWBnOmbuyIsIuacieaykuaciSIsIuS4jeacgyIsIueZvOmbuyIpIA0KIyDnnIvlkITnvqTnmoTluLjnlKjoqZ7lvZkNCnRpZHkobWFza19sZGEsIG1hdHJpeCA9ICJiZXRhIikgJT4lICMg5Y+W5Ye6dG9waWMgdGVybSBiZXRh5YC8DQogIGZpbHRlcighIHRlcm0gJWluJSByZW1vdmVkX3dvcmQpICU+JSANCiAgZ3JvdXBfYnkodG9waWMpICU+JQ0KICB0b3BfbigxMCwgYmV0YSkgJT4lICMgYmV0YeWAvOWJjTEw55qE5a2XDQogIHVuZ3JvdXAoKSAlPiUNCiAgbXV0YXRlKHRvcGljID0gYXMuZmFjdG9yKHRvcGljKSwNCiAgICAgICAgIHRlcm0gPSByZW9yZGVyX3dpdGhpbih0ZXJtLCBiZXRhLCB0b3BpYykpICU+JQ0KICBnZ3Bsb3QoYWVzKHRlcm0sIGJldGEsIGZpbGwgPSB0b3BpYykpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBmYWNldF93cmFwKH4gdG9waWMsIHNjYWxlcyA9ICJmcmVlIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBzY2FsZV94X3Jlb3JkZXJlZCgpDQpgYGANCuWPr+S7peatuOe0jeWHug0KICsgdG9waWMgMSA9IOKAnOiDvea6kOaUv+etluKAnQ0KICsgdG9waWMgMiA9IOKAnOaKseaAqOaUv+W6nOKAnQ0KICsgdG9waWMgMyA9IOKAnOaUv+ayu+S6uueJqeitsOmhjOKAnQ0KICsgdG9waWMgNCA9IOKAnOWmguS9leiuk+S+m+mbu+epqeWumuKAnQ0KICsgdG9waWMgNSA9IOKAnOWYsuirt+S4i+asoeWBnOmbu+aZgumWk+KAnQ0KICsgdG9waWMgNiA9IOKAnOWBnOmbu+WOn+WboOeptuiyrOKAnQ0K5Lul5LiL5oiR5YCR5oyR5Ye656ys5LiA5YCL5Li76aGM44CB56ys5LqM5YCL5Li76aGM5ZKM56ys5YWt5YCL5Li76aGM5L6G5YGa5q+U6LyD44CCDQoNCuWPluWHuuS7o+ihqOS4u+mhjCh0b3BpYykNCmBgYHtyfQ0KIyDlnKh0aWR5IGZ1bmN0aW9u5Lit5L2/55So5Y+D5pW4ImdhbW1hIuS+huWPluW+lyB0aGV0YeefqemZow0KbWFza190b3BpY3MgPC0gdGlkeShtYXNrX2xkYSwgbWF0cml4PSJnYW1tYSIpICU+JSAjIGRvY3VtZW50IHRvcGljIGdhbW1hDQogICAgICAgICAgICAgICAgICBncm91cF9ieShkb2N1bWVudCkgJT4lDQogICAgICAgICAgICAgICAgICB0b3BfbigxLCB3dD1nYW1tYSkNCm1hc2tfdG9waWNzDQpgYGANCg0KIyMgMy7os4fmlpnlhaflrrnmjqLntKINCmBgYHtyfQ0KcG9zdHNfdG9waWMgPC0gbWVyZ2UoeCA9IHBvc3RzLCB5ID0gbWFza190b3BpY3MsIGJ5LnggPSAiYXJ0VXJsIiwgYnkueT0iZG9jdW1lbnQiKQ0KDQojIOeci+S4gOS4i+WQhOS4u+mhjOWcqOiqqueUmum6vA0Kc2V0LnNlZWQoMTIzNDUpDQoNCnBvc3RzX3RvcGljICU+JSAjIOS4u+mhjOS4gA0KICBmaWx0ZXIodG9waWM9PTEpICU+JQ0KICBzZWxlY3QoYXJ0VGl0bGUpICU+JQ0KICB1bmlxdWUoKSAlPiUNCiAgc2FtcGxlX24oMTIpDQoNCg0KcG9zdHNfdG9waWMgJT4lICMg5Li76aGM5LqMDQogIGZpbHRlcih0b3BpYz09MikgJT4lDQogIHNlbGVjdChhcnRUaXRsZSkgJT4lDQogIHVuaXF1ZSgpICU+JQ0KICBzYW1wbGVfbigxMikNCg0KcG9zdHNfdG9waWMgJT4lICMg5Li76aGM5LiJDQogIGZpbHRlcih0b3BpYz09MykgJT4lDQogIHNlbGVjdChhcnRUaXRsZSkgJT4lDQogIHVuaXF1ZSgpICU+JQ0KICBzYW1wbGVfbigxMikNCg0KcG9zdHNfdG9waWMgJT4lICMg5Li76aGM5ZubDQogIGZpbHRlcih0b3BpYz09NCkgJT4lDQogIHNlbGVjdChhcnRUaXRsZSkgJT4lDQogIHVuaXF1ZSgpICU+JQ0KICBzYW1wbGVfbigxMikNCg0KcG9zdHNfdG9waWMgJT4lICMg5Li76aGM5LqUDQogIGZpbHRlcih0b3BpYz09NSkgJT4lDQogIHNlbGVjdChhcnRUaXRsZSkgJT4lDQogIHVuaXF1ZSgpICU+JQ0KICBzYW1wbGVfbigxMikNCg0KcG9zdHNfdG9waWMgJT4lICMg5Li76aGM5YWtDQogIGZpbHRlcih0b3BpYz09NikgJT4lDQogIHNlbGVjdChhcnRUaXRsZSkgJT4lDQogIHVuaXF1ZSgpICU+JQ0KICBzYW1wbGVfbigxMikNCg0KYGBgDQoNCiMjIDQu5pel5pyf5Li76aGM5YiG5biDDQpgYGB7cn0NCnBvc3RzX3RvcGljICU+JQ0KICBtdXRhdGUoYXJ0RGF0ZSA9IGFzLkRhdGUoYXJ0RGF0ZSkpICU+JSANCiAgZ3JvdXBfYnkoYXJ0RGF0ZSx0b3BpYykgJT4lDQogIHN1bW1hcmlzZShzdW0gPXN1bSh0b3BpYykpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9IGFydERhdGUseT1zdW0sZmlsbD1hcy5mYWN0b3IodG9waWMpKSkgKw0KICBnZW9tX2NvbChwb3NpdGlvbj0iZmlsbCIpIA0KDQoNCnBvc3RzX3RvcGljICU+JQ0KICBncm91cF9ieShhcnRDYXQsdG9waWMpICU+JQ0KICBzdW1tYXJpc2Uoc3VtID0gbigpKSAgJT4lDQogIGdncGxvdChhZXMoeD0gYXJ0Q2F0LHk9c3VtLGZpbGw9YXMuZmFjdG9yKHRvcGljKSkpICsNCiAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIikgDQoNCmBgYA0KDQojIOS4g+OAgeekvue+pOe2sui3r+Wclg0KDQros4fmlpnlkIjkvbUNCmBgYHtyfQ0KIyDmlofnq6DlkoznlZnoqIANCnJldmlld3MgPC0gcmV2aWV3cyAlPiUNCiAgICAgIHNlbGVjdChhcnRVcmwsIGNtdFBvc3RlciwgY210U3RhdHVzLCBjbXRDb250ZW50KQ0KcG9zdHNfUmV2aWV3cyA8LSBtZXJnZSh4ID0gcG9zdHMsIHkgPSByZXZpZXdzLCBieSA9ICJhcnRVcmwiKQ0KDQojIOaKiuaWh+eroOWSjHRvcGljDQpwb3N0c19SZXZpZXdzIDwtIG1lcmdlKHggPSBwb3N0c19SZXZpZXdzLCB5ID0gbWFza190b3BpY3MsIGJ5LnggPSAiYXJ0VXJsIiwgYnkueT0iZG9jdW1lbnQiKQ0KaGVhZChwb3N0c19SZXZpZXdzLDMpDQpgYGANCuWPluWHuiBjbXRQb3N0ZXIo5Zue6KaG6ICFKeOAgWFydFBvc3RlcijnmbzmlofogIUp44CBYXJ0VXJsKOaWh+eroOmAo+e1kCkg5LiJ5YCL5qyE5L2NDQpgYGB7cn0NCmxpbmsgPC0gcG9zdHNfUmV2aWV3cyAlPiUgc2VsZWN0KGNtdFBvc3RlciwgYXJ0UG9zdGVyLCBhcnRVcmwpDQpoZWFkKGxpbmssMykNCmBgYA0KDQojIyAxLuWfuuacrOe2sui3r+Wclg0KDQrlu7rnq4vntrLot6/pl5zkv4INCmBgYHtyfQ0KcmV2aWV3TmV0d29yayA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZD1saW5rLCBkaXJlY3RlZD1UKQ0KcmV2aWV3TmV0d29yaw0KYGBgDQoNCiMjIDIu6LOH5paZ56+p6YG4DQpgYGB7cn0NCiMg55yL5LiA5LiL55WZ6KiA5pW45aSn5qaC6YO95aSa5bCRKOaWueS+v+W+jOmdouevqemBuCkNCnBvc3RzICU+JQ0KICBmaWx0ZXIoY29tbWVudE51bTwxMDApICU+JQ0KICBnZ3Bsb3QoYWVzKHg9Y29tbWVudE51bSkpICsgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQoNCuS+neaTmueZvOaWh+aVuOaIluWbnuimhuaVuOevqemBuHBvc3TlkoxyZXZpZXcNCmBgYHtyfQ0KIyMg5biz6Jmf55m85paH56+H5pW4DQpwb3N0X2NvdW50ID0gcG9zdHMgJT4lDQogIGdyb3VwX2J5KGFydFBvc3RlcikgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpIA0KcG9zdF9jb3VudA0KDQojIyDluLPomZ/lm57opobnuL3mlbgNCnJldmlld19jb3VudCA9IHJldmlld3MgJT4lDQogIGdyb3VwX2J5KGNtdFBvc3RlcikgJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhjb3VudCkpIA0KcmV2aWV3X2NvdW50DQoNCiMjIOeZvOaWh+iAhQ0KcG9zdGVyX3NlbGVjdCA8LSBwb3N0X2NvdW50ICU+JSBmaWx0ZXIoY291bnQgPj0gMikNCnBvc3RzIDwtIHBvc3RzICU+JSAgZmlsdGVyKHBvc3RzJGFydFBvc3RlciAlaW4lIHBvc3Rlcl9zZWxlY3QkYXJ0UG9zdGVyKQ0KDQojIyDlm57opobogIUNCnJldmlld2VyX3NlbGVjdCA8LSByZXZpZXdfY291bnQgJT4lICBmaWx0ZXIoY291bnQgPj0gNSkNCnJldmlld3MgPC0gcmV2aWV3cyAlPiUgIGZpbHRlcihyZXZpZXdzJGNtdFBvc3RlciAlaW4lIHJldmlld2VyX3NlbGVjdCRjbXRQb3N0ZXIpDQpgYGANCg0KYGBge3J9DQojIOaqouimluWPg+iIh+S6uuaVuA0KbGVuZ3RoKHVuaXF1ZShwb3N0c19SZXZpZXdzJGFydFBvc3RlcikpICMg55m85paH6ICF5pW46YePIDkxMQ0KbGVuZ3RoKHVuaXF1ZShwb3N0c19SZXZpZXdzJGNtdFBvc3RlcikpICMg5Zue6KaG6ICF5pW46YePIDE1MjI0DQphbGxQb3N0ZXIgPC0gYyhwb3N0c19SZXZpZXdzJGFydFBvc3RlciwgcG9zdHNfUmV2aWV3cyRjbXRQb3N0ZXIpICMg57i95Y+D6IiH5Lq65pW4IDE1NjQxDQpsZW5ndGgodW5pcXVlKGFsbFBvc3RlcikpDQpgYGANCg0K5qiZ6KiY5omA5pyJ5Ye654++6YGO5b6X5L2/55So6ICFDQoNCiAgKyBwb3N0ZXLvvJrlj6rnmbzpgY7mlofjgIHnmbzpgY7mlocr55WZ6YGO6KiADQogICsgcmVwbHllcu+8muWPqueVmemBjuiogA0KYGBge3J9DQp1c2VyTGlzdCA8LSBkYXRhLmZyYW1lKHVzZXI9dW5pcXVlKGFsbFBvc3RlcikpICU+JQ0KICAgICAgICAgICAgICBtdXRhdGUodHlwZT1pZmVsc2UodXNlciVpbiVwb3N0cyRhcnRQb3N0ZXIsICJwb3N0ZXIiLCAicmVwbHllciIpKQ0KaGVhZCh1c2VyTGlzdCwzKQ0KYGBgDQoNCiMjIDMu5Lul5pel5pyf56+p6YG456S+576kDQpgYGB7cn0NCmxpbmsgPC0gcG9zdHNfUmV2aWV3cyAlPiUNCiAgICAgIGdyb3VwX2J5KGNtdFBvc3RlciwgYXJ0VXJsKSAlPiUgDQogICAgICBmaWx0ZXIobigpPjMpICU+JSANCiAgICAgIGZpbHRlcihjb21tZW50TnVtID4gNSkgJT4lDQogICAgICBmaWx0ZXIoYXJ0Q2F0PT0iR29zc2lwaW5nIikgJT4lIA0KICAgICAgZmlsdGVyKGFydERhdGUgPT0gYXMuRGF0ZSgnMjAyMS0wNS0xMycpKSAlPiUNCiAgICAgIHNlbGVjdChjbXRQb3N0ZXIsIGFydFBvc3RlciwgYXJ0VXJsKSAlPiUgDQogICAgICB1bmlxdWUoKQ0KbGluaw0KYGBgDQoNCuevqemBuOWcqGxpbmvoo6HpnaLmnInlh7rnj77nmoTkvb/nlKjogIUNCmBgYHtyfQ0KZmlsdGVyZWRfdXNlciA8LSB1c2VyTGlzdCAlPiUNCiAgICAgICAgICBmaWx0ZXIodXNlciVpbiVsaW5rJGNtdFBvc3RlciB8IHVzZXIlaW4lbGluayRhcnRQb3N0ZXIpICU+JQ0KICAgICAgICAgIGFycmFuZ2UoZGVzYyh0eXBlKSkNCmhlYWQoZmlsdGVyZWRfdXNlciwzKQ0KYGBgDQoNCuWboOeIsuWclueJh+euremgreaciem7nuekmeecvO+8jOaJgOS7pemAmeijj+aIkeWAkeWFiOaKiumXnOS/gueahOaWueWQkeaAp+aLv+aOie+8jOa4m+WwkeWclueJh+S4reeahOS4jeW/heimgeeahOizh+ioiiBzZXQuc2VlZCDlm6DngrppZ3JhcGjlkYjnj77nmoTmlrnlkJHmmK/pmqjmqZ/nmoQNCmBgYHtyfQ0Kc2V0LnNlZWQoNDg3KQ0KIyB2PWZpbHRlcmVkX3VzZXINCg0KcmV2aWV3TmV0d29yayA9IGRlZ3JlZShyZXZpZXdOZXR3b3JrKSA+IDINCnJldmlld05ldHdvcmsgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGQ9bGluaywgdj1maWx0ZXJlZF91c2VyLCBkaXJlY3RlZD1GKQ0KcGxvdChyZXZpZXdOZXR3b3JrLCB2ZXJ0ZXguc2l6ZT0zLCBlZGdlLmFycm93LnNpemU9MC4zLHZlcnRleC5sYWJlbD1OQSkNCmBgYA0KDQrliqDkuIpub2Rlc+eahOmhr+ekuuizh+ioig0K55So5L2/55So6ICF55qE6Lqr5Lu95L6G5Y2A5YiG6bue55qE6aGP6ImyDQoNCiAgKyBwb3N0ZXI6Z29sZCjmnInnmbzmlocpDQogICsgcmVwbHllcjpsaWdodGJsdWUo5Y+q5pyJ5Zue6KaG5paH56ugKQ0KYGBge3J9DQpzZXQuc2VlZCg0ODcpDQpWKHJldmlld05ldHdvcmspJGNvbG9yIDwtIGlmZWxzZShWKHJldmlld05ldHdvcmspJHR5cGU9PSJwb3N0ZXIiLCAiZ29sZCIsICJsaWdodGJsdWUiKQ0KcGxvdChyZXZpZXdOZXR3b3JrLCB2ZXJ0ZXguc2l6ZT0zLCBlZGdlLmFycm93LnNpemU9MC4zLHZlcnRleC5sYWJlbD1OQSkNCmBgYA0KDQrlj6/ku6XnqI3lvq7nnIvlh7rlnJbkuK3nmoTpu54o5Lq6KeS5i+mWk+acieS4gOWumueahOmXnOiBr++8jOS4jemBjuebruWJjeWPquacieWWrue0lOWcluW9ouaIkeWAkeeEoeazleWIhuaekOWFtuS4reeahOWFp+WuueOAgg0K5Zug5q2k5Lul5LiL5oiR5YCR5bCH6LOH5paZ6ZuG5Lit55qE6LOH6KiK5Yqg5Yiw5oiR5YCR55qE5ZyW54mH5Lit44CCDQoNCueCuum7nuWKoOS4iuW4s+iZn+WQjeWtl++8jOeUqGRlZ3JlZeevqemBuOimgemhr+ekuuWHuueahOS9v+eUqOiAhe+8jOS7peWFjeWcluW9ouiiq+WvhuWvhum6u+m6u+eahOaWh+Wtl+imhuiTiw0KYGBge3J9DQpmaWx0ZXJfZGVncmVlID0gNQ0Kc2V0LnNlZWQoMTIzKQ0KDQojIOioreWumiBub2RlIOeahCBsYWJlbC8gY29sb3INCmxhYmVscyA8LSBkZWdyZWUocmV2aWV3TmV0d29yaykgIyDnrpflh7rmr4/lgIvpu57nmoRkZWdyZWUNClYocmV2aWV3TmV0d29yaykkbGFiZWwgPC0gbmFtZXMobGFiZWxzKQ0KVihyZXZpZXdOZXR3b3JrKSRjb2xvciA8LSBpZmVsc2UoVihyZXZpZXdOZXR3b3JrKSR0eXBlPT0icG9zdGVyIiwgImdvbGQiLCAibGlnaHRibHVlIikNCg0KcGxvdCgNCiAgcmV2aWV3TmV0d29yaywgDQogIHZlcnRleC5zaXplPTMsIA0KICBlZGdlLndpZHRoPTMsIA0KICB2ZXJ0ZXgubGFiZWwuZGlzdD0xLA0KICB2ZXJ0ZXgubGFiZWw9aWZlbHNlKGRlZ3JlZShyZXZpZXdOZXR3b3JrKSA+IGZpbHRlcl9kZWdyZWUsIFYocmV2aWV3TmV0d29yaykkbGFiZWwsIE5BKSx2ZXJ0ZXgubGFiZWwuZm9udD0yKQ0KYGBgDQrmiJHlgJHlj6/ku6XnnIvliLDln7rmnKznmoTkvb/nlKjogIXpl5zkv4LvvIzkvYbmmK/miJHlgJHluIzmnJvog73lpKDlsIfmm7TpgLLpmo7nmoTos4foqIroppboprrljJbjgIINCuS+i+Wmgu+8muS9v+eUqOiAhee2k+W4uOWPg+iIh+eahOaWh+eroOeorumhnu+8jOaIluaYr+S9v+eUqOiAheWcqOipsuekvue+pOe2sui3r+S4reaYr+WQpuWPl+WIsOatoei/juOAgg0KDQoNCiMjIDQu5Lul5Li76aGM56+p6YG456S+576kDQrmipNsaW5rDQrmjJHpgbjlh7oyMDIxLTA1LTEz55W25aSp55qE5paH56ug77yMIOevqemBuOS4gOevh+aWh+eroOWbnuimhjPmrKHku6XkuIrogIXvvIzkuJTmlofnq6DnlZnoqIDmlbjlpJrppJg15YmH77yMIOaWh+eroOS4u+mhjOatuOmhnueCujHjgIEy6IiHNuiAhe+8jCDmrITkvY3lj6rlj5bvvJpjbXRQb3N0ZXIo6KmV6KuW6ICFKSwgYXJ0UG9zdGVyKOeZvOaWh+iAhSksIGFydFVybCjmlofnq6DpgKPntZApLCB0b3BpYyjkuLvpoYwpDQpgYGB7cn0NCmxpbmsgPC0gcG9zdHNfUmV2aWV3cyAlPiUNCiAgICAgIGdyb3VwX2J5KGNtdFBvc3RlciwgYXJ0VXJsKSAlPiUgDQogICAgICBmaWx0ZXIobigpPjMpICU+JSANCiAgICAgIGZpbHRlcihjb21tZW50TnVtID4gNSkgJT4lDQogICAgICBmaWx0ZXIoYXJ0Q2F0PT0iR29zc2lwaW5nIikgJT4lICNIYXRlUG9saXRpY3MgLyBHb3NzaXBpbmcNCiAgICAgIGZpbHRlcihhcnREYXRlID09IGFzLkRhdGUoJzIwMjEtMDUtMTMnKSkgJT4lDQogICAgICBmaWx0ZXIoIHRvcGljID09IDEgfCB0b3BpYyA9PSAyIHwgdG9waWMgPT0gNikgJT4lIA0KICAgICAgc2VsZWN0KGNtdFBvc3RlciwgYXJ0UG9zdGVyLCBhcnRVcmwsIHRvcGljKSAlPiUgDQogICAgICB1bmlxdWUoKQ0KbGluaw0KYGBgDQrmipNub2RlcyDlnKjmiYDmnInnmoTkvb/nlKjogIXoo6HpnaLvvIznr6npgbhsaW5r5Lit5pyJ5Ye654++55qE5L2/55So6ICFDQpgYGB7cn0NCmZpbHRlcmVkX3VzZXIgPC0gdXNlckxpc3QgJT4lDQogICAgICAgICAgZmlsdGVyKHVzZXIlaW4lbGluayRjbXRQb3N0ZXIgfCB1c2VyJWluJWxpbmskYXJ0UG9zdGVyKSAlPiUNCiAgICAgICAgICBhcnJhbmdlKGRlc2ModHlwZSkpDQpoZWFkKGZpbHRlcmVkX3VzZXIsMykNCmBgYA0KDQojIyA1LuS9v+eUqOiAhee2k+W4uOWPg+iIh+eahOaWh+eroOeorumhng0KYGBge3J9DQpmaWx0ZXJfZGVncmVlID0gNQ0KDQojIOW7uueri+e2sui3r+mXnOS/gg0KcmV2aWV3TmV0d29yayA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZD1saW5rLCB2PWZpbHRlcmVkX3VzZXIsIGRpcmVjdGVkPUYpDQoNCiMg5L6d5pOa5L2/55So6ICF6Lqr5Lu95bCN6bue6YCy6KGM5LiK6ImyDQpsYWJlbHMgPC0gZGVncmVlKHJldmlld05ldHdvcmspDQpWKHJldmlld05ldHdvcmspJGxhYmVsIDwtIG5hbWVzKGxhYmVscykNClYocmV2aWV3TmV0d29yaykkY29sb3IgPC0gaWZlbHNlKFYocmV2aWV3TmV0d29yaykkdHlwZT09InBvc3RlciIsICJnb2xkIiwgImxpZ2h0Ymx1ZSIpDQoNCiMg5L6d5pOa5Zue6KaG55m855Sf55qE5paH56ug5omA5bCN5oeJ55qE5Li76aGM77yM5bCN5LuW5YCR55qE6Zec6IGv57ea6YCy6KGM5LiK6ImyDQpFKHJldmlld05ldHdvcmspJGNvbG9yIDwtIGlmZWxzZShFKHJldmlld05ldHdvcmspJHRvcGljID09ICIyIiwgInBhbGV2aW9sZXRyZWQiLCAibGlnaHRncmVlbiIpDQoNCiMg55Wr5Ye656S+576k57ay6Lev5ZyWDQpzZXQuc2VlZCg1NDMyKQ0KcGxvdChyZXZpZXdOZXR3b3JrLCB2ZXJ0ZXguc2l6ZT0zLCBlZGdlLndpZHRoPTMsIHZlcnRleC5sYWJlbC5kaXN0PTEsDQogICAgIHZlcnRleC5sYWJlbD1pZmVsc2UoZGVncmVlKHJldmlld05ldHdvcmspID4gZmlsdGVyX2RlZ3JlZSwgVihyZXZpZXdOZXR3b3JrKSRsYWJlbCwgTkEpLHZlcnRleC5sYWJlbC5mb250PTIpDQoNCiMg5Yqg5YWl5qiZ56S6DQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYygi55m85paH6ICFIiwi5Zue5paH6ICFIiksIHBjaD0yMSwgDQogIGNvbD0iIzc3Nzc3NyIsIHB0LmJnPWMoImdvbGQiLCJsaWdodGJsdWUiKSwgcHQuY2V4PTEsIGNleD0xKQ0KbGVnZW5kKCJ0b3BsZWZ0IiwgYygi5om56KmV6Kq/5L6DIiwi5aCx5bCO55u46ZecIiksIA0KICAgICAgIGNvbD1jKCJwYWxldmlvbGV0cmVkIiwgImxpZ2h0Z3JlZW4iKSwgbHR5PTEsIGNleD0xKQ0KYGBgDQoNCiMjIDYu5L2/55So6ICF5piv5ZCm5Y+X5Yiw5q2h6L+ODQpgYGB7cn0NCmZpbHRlcl9kZWdyZWUgPSA2ICMg5L2/55So6ICFZGVncmVlDQoNCiMg6YGO5r++55WZ6KiA6ICF5bCN55m85paH6ICF55qE5o6o5ZmT56iL5bqmDQpsaW5rIDwtIHBvc3RzX1Jldmlld3MgJT4lDQogICAgICBmaWx0ZXIoYXJ0Q2F0PT0iR29zc2lwaW5nIikgJT4lIA0KICAgICAgZmlsdGVyKGNvbW1lbnROdW0gPiAxMCkgJT4lDQogICAgICBmaWx0ZXIoY210U3RhdHVzIT0i4oaSIikgJT4lDQogICAgICBncm91cF9ieShjbXRQb3N0ZXIsIGFydFVybCkgJT4lDQogICAgICBmaWx0ZXIoIG4oKSA+IDIpICU+JQ0KICAgICAgdW5ncm91cCgpICU+JSANCiAgICAgIHNlbGVjdChjbXRQb3N0ZXIsIGFydFBvc3RlciwgYXJ0VXJsLCBjbXRTdGF0dXMpICU+JSANCiAgICAgIHVuaXF1ZSgpDQoNCiMg5o6l5LiL5L6G5oqK57ay6Lev5ZyW55Wr5Ye65L6G77yM6Lef5YmN6Z2i5YGa55qE5LqL6YO95LiA5qij77yM5Zug5q2k5LiN5YaN57Sw6L+wDQoNCiMg56+p6YG4bGlua+S4reacieWHuuePvueahOS9v+eUqOiAhQ0KZmlsdGVyZWRfdXNlciA8LSB1c2VyTGlzdCAlPiUNCiAgICAgICAgICBmaWx0ZXIodXNlciVpbiVsaW5rJGNtdFBvc3RlciB8IHVzZXIlaW4lbGluayRhcnRQb3N0ZXIpICU+JQ0KICAgICAgICAgIGFycmFuZ2UoZGVzYyh0eXBlKSkNCg0KIyDlu7rnq4vntrLot6/pl5zkv4INCnJldmlld05ldHdvcmsgPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGQ9bGluaywgdj1maWx0ZXJlZF91c2VyLCBkaXJlY3RlZD1GKQ0KDQojIOS+neaTmuS9v+eUqOiAhei6q+S7veWwjem7numAsuihjOS4iuiJsg0KbGFiZWxzIDwtIGRlZ3JlZShyZXZpZXdOZXR3b3JrKQ0KVihyZXZpZXdOZXR3b3JrKSRsYWJlbCA8LSBuYW1lcyhsYWJlbHMpDQpWKHJldmlld05ldHdvcmspJGNvbG9yIDwtIGlmZWxzZShWKHJldmlld05ldHdvcmspJHR5cGU9PSJwb3N0ZXIiLCAiZ29sZCIsICJsaWdodGJsdWUiKQ0KDQoNCiMg5L6d5pOa5Zue6KaG55m855Sf55qE5paH56ug5omA5bCN5oeJ55qE5Li76aGM77yM5bCN5LuW5YCR55qE6Zec6IGv57ea6YCy6KGM5LiK6ImyDQpFKHJldmlld05ldHdvcmspJGNvbG9yIDwtIGlmZWxzZShFKHJldmlld05ldHdvcmspJGNtdFN0YXR1cyA9PSAi5o6oIiwgImxpZ2h0Z3JlZW4iLCAicGFsZXZpb2xldHJlZCIpDQoNCiMg55Wr5Ye656S+576k57ay6Lev5ZyWDQpzZXQuc2VlZCg1NDMyKQ0KcGxvdChyZXZpZXdOZXR3b3JrLCB2ZXJ0ZXguc2l6ZT0yLCBlZGdlLndpZHRoPTMsIHZlcnRleC5sYWJlbC5kaXN0PTEsDQogICAgIHZlcnRleC5sYWJlbD1pZmVsc2UoZGVncmVlKHJldmlld05ldHdvcmspID4gZmlsdGVyX2RlZ3JlZSwgVihyZXZpZXdOZXR3b3JrKSRsYWJlbCwgTkEpLHZlcnRleC5sYWJlbC5mb250PTIpDQoNCiMg5Yqg5YWl5qiZ56S6DQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgYygi55m85paH6ICFIiwi5Zue5paH6ICFIiksIHBjaD0yMSwNCiAgY29sPSIjNzc3Nzc3IiwgcHQuYmc9YygiZ29sZCIsImxpZ2h0Ymx1ZSIpLCBwdC5jZXg9MSwgY2V4PTEpDQpsZWdlbmQoInRvcGxlZnQiLCBjKCLmjqgiLCLlmZMiKSwgDQogICAgICAgY29sPWMoImxpZ2h0Z3JlZW4iLCJwYWxldmlvbGV0cmVkIiksIGx0eT0xLCBjZXg9MSkNCmBgYA0KDQoNCiMg5YWr44CB57i957WQDQoNCjEuIOWPsOeBo+WBnOmbu+S6i+S7tueahOiojuirlumHjem7nuacieWTquS6mz8g5Li76KaB5YiG54K65ZOq5bm+56iu6aKo5ZCRPzwvYnI+DQrlsI3mlrwyMDIxLTA1LTIxIH4gMjAyMS0wNS0yM+aUtumbhueahOaWh+eroO+8jOWkp+amguWPr+S7peWIhuaIkOWYsuirt+agoeato+WbnuatuOOAgeWuouingOiojuirluagoeato+WbnuatuOmAmeWFqeeoru+8jOWFtuS7lumChOacieiRl+mHjeiojuirlueiuuiouuWAi+ahiOi2s+i3oeaIluWSjOeWq+iLl+ebuOmXnOeahOiojuirluetieWbm+eoruOAguiojuirlumHjem7nuWkmuWcqOaWvOe1seioiOOAjOaVuOWtl+OAjeOAgeOAjOWFrOW4g+aXpeacn+OAjeetieahiOS+i+eahOioiOeul+aWueW8j+OAgg0KDQoyLiDnm67liY3poqjlkJHmnIDlgY/lk6rpgoo/PC9icj4NCuWuouingOiojuirluioiOeul+aWueW8j+eahOaWh+eroOS4jeWwke+8jOS9huWYsuirt+OAgeWFq+WNpuaAp+izqueahOaWh+eroOWxheWkmuOAgg0KDQozLiDoqI7oq5bmoKHmraPlm57mrbjnmoTnpL7nvqTntrLot6/lpoLkvZXliIbluIM/PC9icj4NCuS7peekvue+pOaWh+eroOaVuOS+hueci++8jOaJueipleWYsuirt+eahOe2suWPi+i8g+Wkmu+8jOS9huW+nuekvue+pOe2sui3r+ingOWvn+eZvOePvu+8jOWFqemCiueahOiyvOaWh+iojuirluiBsumHj+mDveW+iOmrmOOAgg0KDQoNCjQuIOagoeato+WbnuatuOeahOaEj+imi+mgmOiiluacieiqsD/ntrLlj4vnmoTmjqjlmZPni4DmhYvlpoLkvZU/PC9icj4NCuWboOeCuuizh+aWmemBuOWPlueahOaZgumWk+i8g+efre+8jOWPquimgeW5vuevh+WbnuimhumHj+mrmOeahOiyvOaWh++8jOWwseacieapn+acg+aIkOeCuuekvue+pOS4reW/g++8jOWcqOWFq+WNpueJiOS4iu+8jOS7peWgseWwjuiojuirlueCuuS4u+eahOaEj+imi+mgmOiiluaciSBbY2VudHJlMDEzMF0oaHR0cHM6Ly93d3cucHR0LmNjL2Jicy9Hb3NzaXBpbmcvTS4xNjIxNjcwNzY4LkEuRTFCLmh0bWwp77yM5Zue6KaG5o6o5ZmT55qG5pyJ77yM6Kq/5L6D5om56KmV6YOo5YiG5YmH5pyJIFtoc3RmXShodHRwczovL3d3dy5wdHQuY2MvYmJzL0dvc3NpcGluZy9NLjE2MjE2NjM2NTMuQS45MjUuaHRtbCnvvIzntrLlj4vlpKflpJrmraPpnaLmjqjmlofjgIINCg0KDQoNCg0K