Ch.0 : 資料取得及觀念複習

1. 資料取得及套件載入

載入的資料是由中山大學管理學院文字分析平台取得,在平台資料輸出區塊選擇「文章+詞彙+詞頻」選項,即可取得相同格式之csv檔案。

資料簡介

本資料為2019/01/26 ~ 2019/03/08,將PTT武漢肺炎討論專版自01/26開版以來之資料,透過文字分析平台整理,得到4738篇文章。

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"

安裝需要的packages

packages = c("dplyr","ggplot2", "data.table", "scales")
existing = as.character(installed.packages()[,1])
for(pkg in packages[!(packages %in% existing)]) install.packages(pkg)

載入需要的packages以及資料

require(dplyr)
require(ggplot2)
require(data.table)
require(scales)
require(wordcloud2)

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

csv <- fread("./data/nCoV2019_artWordFreq.csv", encoding = "UTF-8")
csv$artDate = csv$artDate %>% as.Date("%Y/%m/%d")
str(csv)
Classes ‘data.table’ and 'data.frame':  447662 obs. of  6 variables:
 $ artTitle: chr  "[新聞]澎湖傳疑似感染武漢肺炎4人負壓病房隔" "[新聞]澎湖傳疑似感染武漢肺炎4人負壓病房隔" "[新聞]澎湖傳疑似感染武漢肺炎4人負壓病房隔" "[新聞]澎湖傳疑似感染武漢肺炎4人負壓病房隔" ...
 $ artDate : Date, format: "2020-01-25" "2020-01-25" "2020-01-25" "2020-01-25" ...
 $ artTime : chr  "20:28:07" "20:28:07" "20:28:07" "20:28:07" ...
 $ artUrl  : chr  "https://www.ptt.cc/bbs/nCoV2019/M.1579984089.A.535.html" "https://www.ptt.cc/bbs/nCoV2019/M.1579984089.A.535.html" "https://www.ptt.cc/bbs/nCoV2019/M.1579984089.A.535.html" "https://www.ptt.cc/bbs/nCoV2019/M.1579984089.A.535.html" ...
 $ word    : chr  "澎湖" "通報" "女性" "分院" ...
 $ count   : int  7 4 4 3 3 2 2 2 2 2 ...
 - attr(*, ".internal.selfref")=<externalptr> 

資料預設之日期欄位是“chr”格式,在畫圖前我們須先將其轉為“date”格式,轉換後可以透過str指令來確認各欄位型態。

檢視前6筆資料

head(csv)

資料欄位

  1. artTitle: 文章之標題,注意:不同文章可能會有完全相同的標題。
  2. artDate: 文章發佈之日期。
  3. artTime: 文章發佈之時間。
  4. artUrl: 文章之網址,每篇文章之網址為獨一無二的,可用來辨識相同標題之不同文章。
  5. word: 詞彙。
  6. count: 詞頻。

Ch.1 日期折線圖

這個章節的目的是計算出每一天文章的發表數量,可以看出特定主題討論的熱度。

資料處理

data <- csv %>% 
  select(artDate, artUrl) %>% 
  distinct()

select(): 我們只需要文章以及日期兩個欄位即可,其他欄位不需要。
distinct(): 一篇文章有很多個詞彙,所以會有很多列,但我們只需要一篇文章保留一個列即可。

article_count_by_date <- data %>% 
  group_by(artDate) %>% 
  summarise(count = n())

head(article_count_by_date, 20)

按照日期分群,計算每天共有幾篇討論文章。

plot_date <- 
  # data
  article_count_by_date %>% 
  # aesthetics
  ggplot(aes(x = artDate, y = count)) +
  # geometrics
  geom_line(color = "#00AFBB", size = 2) + 
  geom_vline(xintercept = as.numeric(as.Date("2020-02-12")), col='red') + 
  # coordinates
  scale_x_date(labels = date_format("%Y/%m/%d")) +
  ggtitle("武漢肺炎 討論文章數") + 
  xlab("日期") + 
  ylab("數量") + 
  # theme
  theme(text = element_text(family = "Heiti TC Light")) #加入中文字型設定,避免中文字顯示錯誤。

plot_date

從上圖中可以看到關於「武漢肺炎」的討論從開版以來,討論文章數有逐漸增加的趨勢。

Ch.2 文字雲

接下來我們來大略觀察討論的內容為何,使用的方式為文字雲。

data <- csv %>% 
  group_by(word) %>% 
  summarise(sum = sum(count)) %>% 
  arrange(desc(sum))

先將資料集中所有文章按照文字進行分群,計算每一個字的總詞頻。

head(data)

結果為總詞頻最多的字。

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

將整理好的資料直接送入wordcloud2 function 即可得到文字雲。此套件為互動式介面,使用滑鼠移動到特定的詞彙,畫面會同時顯示詞彙以及對應的詞頻。

按照日期進行區分

在Ch.1中的日期折線圖中,我們發現在2/12前後討論數量有一波轉折,我們來看看2/12前後的討論內容是否有不同的地方。

data_before <- csv %>% filter(artDate <= "2020-02-12")
data_after <- csv %>% filter(artDate > "2020-02-12")

按照日期切分資料

data_before <- data_before %>% 
  group_by(word) %>% 
  summarise(sum = sum(count)) %>% 
  arrange(desc(sum))

data_after <- data_after %>% 
  group_by(word) %>% 
  summarise(sum = sum(count)) %>% 
  arrange(desc(sum))

與Ch.2相同的資料處理。

plot_before <- data_before %>% filter(sum > 300) %>% wordcloud2()
#plot_before

2/12 之前的文字雲(html中會顯示不出來第一張以後的文字雲,請將程式碼下載下來即可成功繪出)

plot_after <- data_after %>% filter(sum > 300) %>% wordcloud2()
#plot_after

2/12之後的文字雲(html中會顯示不出來第一張以後的文字雲,請將程式碼下載下來即可成功繪出)

觀察兩張圖片中出現的國家可以發現,在2/12以前,有出現「中國」、「台灣」、「美國」、「日本」、「香港」。 在2/12以後,開始出現「韓國」、「南韓」、「大邱」、「伊朗」等關鍵字。大致與疾病爆發的先後順序一致。

練習練習~

可以發現本次資料集內有許多新聞用語,因為武漢肺炎版上有許多人轉貼新聞。
那麼現在請各位將所列的幾個關鍵字移除後重新繪製文字雲吧!
新聞關鍵字:發稿、新聞、原文、記者、發稿、撰稿者、連結、完整標題、今天、不是、目前、表示

### Code Here ###

Ch3. 長條圖

文字雲可以直覺看出較常提到的字,但如果想得到精確的「最常出現詞彙」,我們則可以透過長條圖來查看。

data <- rbind(data_before %>% mutate(type="before"), data_after %>% mutate(type="after"))

將2/12前後的資料加入一個欄位標示後合併起來。

head(data)

除了詞彙及詞頻以外,還多了一個type欄位來區分2/12以前或是以後。

plot_merge <- data %>% 
  group_by(type) %>% 
  top_n(10, sum) %>% 
  ungroup() %>% 
  mutate(word = reorder(word, sum)) %>%
  ggplot(aes(x=word, y=sum, fill = type)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y="詞頻") +
  facet_wrap(~type, ncol = 1, scales="free") + 
  coord_flip()+
  theme(text = element_text(family = "Heiti TC Light"))

plot_merge

由上圖可以看到,討論的內容在2/12前後大致相同。

plot_merge <- data %>% 
  group_by(type) %>% 
  top_n(50, sum) %>%
  ungroup() %>% 
  #filter(!(duplicated(word)| duplicated(word, fromLast = TRUE))) #%>% 
  group_by(word) %>%
  filter(n()==1) %>%
  ungroup() %>% 
  mutate(word = reorder(word, sum)) %>% 
  ggplot(aes(word, sum, fill = type)) +
  geom_col(show.legend = FALSE) +
  labs(x = NULL, y="總和") +
  facet_wrap(~type, ncol = 1, scales="free") + 
  coord_flip()+
  theme(text = element_text(family = "Heiti TC Light"))

plot_merge

將重複的詞彙移除後,可以看到在2/12前與2/12後的討論差異:
1. 國家不同
2. 隨著疫情蔓延,出現更多死亡
3. 最初的「入境」管制議題,到後來的「社區」傳播議題。

Ch.4 情緒折線圖

透過文字分析平台可以輸出每篇文章的情緒分數,在資料預覽頁面選擇「文章+情緒」選項即可得到資料。

載入資料

csv_sen <- fread("./data/nCoV2019_artSen.csv", encoding = "UTF-8")
head(csv_sen)

資料欄位介紹:
1. artTitle: 文章之標題,注意:不同文章可能會有完全相同的標題。
2. artDate: 文章發佈之日期。
3. artTime: 文章發佈之時間。
4. artUrl: 文章之網址,每篇文章之網址為獨一無二的,可用來辨識相同標題之不同文章。
5. positive_emotion_grade: 本篇文章出現多少次正面情緒詞彙。
6. negative_emotion_grade: 本篇文章出現多少次負面情緒詞彙。
7. neutral_emotion_grade: 本篇文章出現多少次中性情緒詞彙。

csv_sen$artDate = csv_sen$artDate %>% as.Date("%Y/%m/%d")
str(csv_sen)
Classes ‘data.table’ and 'data.frame':  4738 obs. of  7 variables:
 $ artTitle              : chr  "[新聞]澎湖傳疑似感染武漢肺炎4人負壓病房隔" "[新聞]新加坡累積確診至4人" "[討論]出門該注意哪些重點?" "[閒聊]這個板沒有板規嗎?" ...
 $ artDate               : Date, format: "2020-01-25" "2020-01-25" "2020-01-25" "2020-01-25" ...
 $ artTime               : chr  "20:28:07" "20:32:36" "20:44:18" "21:56:41" ...
 $ artUrl                : chr  "https://www.ptt.cc/bbs/nCoV2019/M.1579984089.A.535.html" "https://www.ptt.cc/bbs/nCoV2019/M.1579984359.A.6D7.html" "https://www.ptt.cc/bbs/nCoV2019/M.1579985060.A.ADD.html" "https://www.ptt.cc/bbs/nCoV2019/M.1579989403.A.239.html" ...
 $ positive_emotion_grade: int  2 0 0 0 1 0 5 5 10 1 ...
 $ negative_emotion_grade: int  0 0 0 0 0 0 11 1 6 4 ...
 $ neutral_emotion_grade : int  1 0 0 0 0 0 3 0 0 0 ...
 - attr(*, ".internal.selfref")=<externalptr> 

與Ch.1相同,資料預設日期格式為“chr”,我們需要先將其轉為“date”格式。

data_sen <- csv_sen %>% 
  group_by(artDate) %>% 
  summarise(positive = sum(positive_emotion_grade), negative = sum(negative_emotion_grade), neutral = sum(neutral_emotion_grade))

head(data_sen)

將資料按照日期分群後,加總每天的三種情緒分數,並進行標準化。

data_sen %>% ggplot(aes(x= artDate)) +
  geom_line(aes(y = positive, colour = "red")) +
  geom_line(aes(y = negative, colour = "blue")) +
  geom_line(aes(y = neutral, colour = "yellow")) + 
  scale_x_date(labels = date_format("%Y/%m/%d")) +
  scale_color_discrete(name="情緒種類", labels = c("positive","negative","neutral")) + 
  ggtitle("武漢肺炎 討論情緒") + 
  xlab("日期") + 
  ylab("分數") + 
  theme(text = element_text(family = "Heiti TC Light"))

練習練習~

剛剛我們將資料按照日期分群後,加總計算了每天的三種情緒分數。 但是我們可以發現每天的文章數量造成辭彙的數量不一樣,所以情緒圖的走勢會有很大的起伏。 請各位將每日的情緒總值進行標準化後再畫一次情緒的走勢圖。

公式:
positive = positive / (positive + negative + neutral)
negative = negative / (positive + negative + neutral)
neutral = neutral / (positive + negative + neutral)

hint:剛剛summarise那邊可以順便計算每日的情緒詞總數
hint2:最後會用到mutate喔

### Code Here ###
LS0tCnRpdGxlOiAi5Lul5paH5a2X5YiG5p6Q5bmz5Y+w542y5Y+W5LmL6LOH5paZ6YCy6KGM5Z+65pys57mq5ZyW5YiG5p6QIgphdXRob3I6ICJLdW4tSHNpYW5nIENoZW4iCmRhdGU6ICIyMDIwLzAzLzA1IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAphYnN0cmFjdDogIuS9v+eUqOaWh+Wtl+WIhuaekOW5s+WPsOi8uOWHuuS5i+izh+aWme+8jOS7pSdkcGx5ciflj4onZ2dwbG90MiflpZfku7bpgLLooYzln7rmnKzos4fmlpnoppboprrljJYiCi0tLQojIENoLjAgOiDos4fmlpnlj5blvpflj4rop4Dlv7XopIfnv5IKIyMgMS4g6LOH5paZ5Y+W5b6X5Y+K5aWX5Lu26LyJ5YWlCj4g6LyJ5YWl55qE6LOH5paZ5piv55Sx5Lit5bGx5aSn5a24566h55CG5a246Zmi5paH5a2X5YiG5p6Q5bmz5Y+w5Y+W5b6X77yM5Zyo5bmz5Y+w6LOH5paZ6Ly45Ye65Y2A5aGK6YG45pOH44CM5paH56ugK+ipnuW9mSvoqZ7poLvjgI3pgbjpoIXvvIzljbPlj6/lj5blvpfnm7jlkIzmoLzlvI/kuYtjc3bmqpTmoYjjgIIKCiMjIyDos4fmlpnnsKHku4sKPiDmnKzos4fmlpnngroyMDE5LzAxLzI2IH4gMjAxOS8wMy8wOO+8jOWwh1BUVOatpua8ouiCuueCjuiojuirluWwiOeJiOiHqjAxLzI26ZaL54mI5Lul5L6G5LmL6LOH5paZ77yM6YCP6YGO5paH5a2X5YiG5p6Q5bmz5Y+w5pW055CG77yM5b6X5YiwNDczOOevh+aWh+eroOOAggoKYGBge3J9ClN5cy5zZXRsb2NhbGUoY2F0ZWdvcnkgPSAiTENfQUxMIiwgbG9jYWxlID0gInpoX1RXLlVURi04IikgIyDpgb/lhY3kuK3mlofkuoLnorwKYGBgCgojIyMg5a6J6KOd6ZyA6KaB55qEcGFja2FnZXMKYGBge3J9CnBhY2thZ2VzID0gYygiZHBseXIiLCJnZ3Bsb3QyIiwgImRhdGEudGFibGUiLCAic2NhbGVzIikKZXhpc3RpbmcgPSBhcy5jaGFyYWN0ZXIoaW5zdGFsbGVkLnBhY2thZ2VzKClbLDFdKQpmb3IocGtnIGluIHBhY2thZ2VzWyEocGFja2FnZXMgJWluJSBleGlzdGluZyldKSBpbnN0YWxsLnBhY2thZ2VzKHBrZykKYGBgCgojIyMg6LyJ5YWl6ZyA6KaB55qEcGFja2FnZXPku6Xlj4ros4fmlpkKYGBge3IgZWNobyA9IFQsIHJlc3VsdHMgPSAnaGlkZSd9CnJlcXVpcmUoZHBseXIpCnJlcXVpcmUoZ2dwbG90MikKcmVxdWlyZShkYXRhLnRhYmxlKQpyZXF1aXJlKHNjYWxlcykKcmVxdWlyZSh3b3JkY2xvdWQyKQpgYGAKCiMjIyDovInlhaXoh6rlubPlj7DkuIvovInkuIvkvobnmoTos4fmlpkKYGBge3J9CmNzdiA8LSBmcmVhZCgiLi9kYXRhL25Db1YyMDE5X2FydFdvcmRGcmVxLmNzdiIsIGVuY29kaW5nID0gIlVURi04IikKY3N2JGFydERhdGUgPSBjc3YkYXJ0RGF0ZSAlPiUgYXMuRGF0ZSgiJVkvJW0vJWQiKQpzdHIoY3N2KQpgYGAKPiDos4fmlpnpoJDoqK3kuYvml6XmnJ/mrITkvY3mmK8iY2hyIuagvOW8j++8jOWcqOeVq+WcluWJjeaIkeWAkemgiOWFiOWwh+WFtui9ieeCuiJkYXRlIuagvOW8j++8jOi9ieaPm+W+jOWPr+S7pemAj+mBjnN0cuaMh+S7pOS+hueiuuiqjeWQhOashOS9jeWei+aFi+OAggoKIyMjIOaqouimluWJjTbnrYbos4fmlpkKYGBge3J9CmhlYWQoY3N2KQpgYGAKCiMjIyDos4fmlpnmrITkvY0KPiAxLiBhcnRUaXRsZTog5paH56ug5LmL5qiZ6aGM77yM5rOo5oSPOuS4jeWQjOaWh+eroOWPr+iDveacg+acieWujOWFqOebuOWQjOeahOaomemhjOOAggoyLiBhcnREYXRlOiDmlofnq6DnmbzkvYjkuYvml6XmnJ/jgIIKMy4gYXJ0VGltZTog5paH56ug55m85L2I5LmL5pmC6ZaT44CCCjQuIGFydFVybDog5paH56ug5LmL57ay5Z2A77yM5q+P56+H5paH56ug5LmL57ay5Z2A54K6542o5LiA54Sh5LqM55qE77yM5Y+v55So5L6G6L6o6K2Y55u45ZCM5qiZ6aGM5LmL5LiN5ZCM5paH56ug44CCCjUuIHdvcmQ6IOipnuW9meOAggo2LiBjb3VudDog6Kme6aC744CCCgojIENoLjEg5pel5pyf5oqY57ea5ZyWCj4g6YCZ5YCL56ug56+A55qE55uu55qE5piv6KiI566X5Ye65q+P5LiA5aSp5paH56ug55qE55m86KGo5pW46YeP77yM5Y+v5Lul55yL5Ye654m55a6a5Li76aGM6KiO6KuW55qE54ax5bqm44CCCgojIyDos4fmlpnomZXnkIYKCmBgYHtyfQpkYXRhIDwtIGNzdiAlPiUgCiAgc2VsZWN0KGFydERhdGUsIGFydFVybCkgJT4lIAogIGRpc3RpbmN0KCkKYGBgCj4gc2VsZWN0KCk6IOaIkeWAkeWPqumcgOimgeaWh+eroOS7peWPiuaXpeacn+WFqeWAi+ashOS9jeWNs+WPr++8jOWFtuS7luashOS9jeS4jemcgOimgeOAgjxicj4KPiBkaXN0aW5jdCgpOiDkuIDnr4fmlofnq6DmnInlvojlpJrlgIvoqZ7lvZnvvIzmiYDku6XmnIPmnInlvojlpJrliJfvvIzkvYbmiJHlgJHlj6rpnIDopoHkuIDnr4fmlofnq6Dkv53nlZnkuIDlgIvliJfljbPlj6/jgIIKCgpgYGB7cn0KYXJ0aWNsZV9jb3VudF9ieV9kYXRlIDwtIGRhdGEgJT4lIAogIGdyb3VwX2J5KGFydERhdGUpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpCgpoZWFkKGFydGljbGVfY291bnRfYnlfZGF0ZSwgMjApCmBgYAo+IOaMieeFp+aXpeacn+WIhue+pO+8jOioiOeul+avj+WkqeWFseacieW5vuevh+iojuirluaWh+eroOOAggoKYGBge3J9CnBsb3RfZGF0ZSA8LSAKICAjIGRhdGEKICBhcnRpY2xlX2NvdW50X2J5X2RhdGUgJT4lIAogICMgYWVzdGhldGljcwogIGdncGxvdChhZXMoeCA9IGFydERhdGUsIHkgPSBjb3VudCkpICsKICAjIGdlb21ldHJpY3MKICBnZW9tX2xpbmUoY29sb3IgPSAiIzAwQUZCQiIsIHNpemUgPSAyKSArIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGFzLm51bWVyaWMoYXMuRGF0ZSgiMjAyMC0wMi0xMiIpKSwgY29sPSdyZWQnKSArIAogICMgY29vcmRpbmF0ZXMKICBzY2FsZV94X2RhdGUobGFiZWxzID0gZGF0ZV9mb3JtYXQoIiVZLyVtLyVkIikpICsKICBnZ3RpdGxlKCLmrabmvKLogrrngo4g6KiO6KuW5paH56ug5pW4IikgKyAKICB4bGFiKCLml6XmnJ8iKSArIAogIHlsYWIoIuaVuOmHjyIpICsgCiAgIyB0aGVtZQogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlaXRpIFRDIExpZ2h0IikpICPliqDlhaXkuK3mloflrZflnovoqK3lrprvvIzpgb/lhY3kuK3mloflrZfpoa/npLrpjK/oqqTjgIIKCnBsb3RfZGF0ZQpgYGAKPiDlvp7kuIrlnJbkuK3lj6/ku6XnnIvliLDpl5zmlrzjgIzmrabmvKLogrrngo7jgI3nmoToqI7oq5blvp7plovniYjku6XkvobvvIzoqI7oq5bmlofnq6DmlbjmnInpgJDmvLjlop7liqDnmoTotqjli6LjgIIKCiMgQ2guMiDmloflrZfpm7IKPiDmjqXkuIvkvobmiJHlgJHkvoblpKfnlaXop4Dlr5/oqI7oq5bnmoTlhaflrrnngrrkvZXvvIzkvb/nlKjnmoTmlrnlvI/ngrrmloflrZfpm7LjgIIKCmBgYHtyfQpkYXRhIDwtIGNzdiAlPiUgCiAgZ3JvdXBfYnkod29yZCkgJT4lIAogIHN1bW1hcmlzZShzdW0gPSBzdW0oY291bnQpKSAlPiUgCiAgYXJyYW5nZShkZXNjKHN1bSkpCmBgYAo+IOWFiOWwh+izh+aWmembhuS4reaJgOacieaWh+eroOaMieeFp+aWh+Wtl+mAsuihjOWIhue+pO+8jOioiOeul+avj+S4gOWAi+Wtl+eahOe4veipnumgu+OAggoKYGBge3J9CmhlYWQoZGF0YSkKYGBgCj4g57WQ5p6c54K657i96Kme6aC75pyA5aSa55qE5a2X44CCCgpgYGB7cn0KZGF0YSAlPiUgZmlsdGVyKHN1bSA+IDUwMCkgJT4lIHdvcmRjbG91ZDIoKQpgYGAKPiDlsIfmlbTnkIblpb3nmoTos4fmlpnnm7TmjqXpgIHlhaV3b3JkY2xvdWQyIGZ1bmN0aW9uIOWNs+WPr+W+l+WIsOaWh+Wtl+mbsuOAguatpOWll+S7tueCuuS6kuWLleW8j+S7i+mdou+8jOS9v+eUqOa7kem8oOenu+WLleWIsOeJueWumueahOipnuW9me+8jOeVq+mdouacg+WQjOaZgumhr+ekuuipnuW9meS7peWPiuWwjeaHieeahOipnumgu+OAggoKIyMg5oyJ54Wn5pel5pyf6YCy6KGM5Y2A5YiGCj4g5ZyoQ2guMeS4reeahOaXpeacn+aKmOe3muWcluS4re+8jOaIkeWAkeeZvOePvuWcqDIvMTLliY3lvozoqI7oq5bmlbjph4/mnInkuIDms6LovYnmipjvvIzmiJHlgJHkvobnnIvnnIsyLzEy5YmN5b6M55qE6KiO6KuW5YWn5a655piv5ZCm5pyJ5LiN5ZCM55qE5Zyw5pa544CCCgpgYGB7cn0KZGF0YV9iZWZvcmUgPC0gY3N2ICU+JSBmaWx0ZXIoYXJ0RGF0ZSA8PSAiMjAyMC0wMi0xMiIpCmRhdGFfYWZ0ZXIgPC0gY3N2ICU+JSBmaWx0ZXIoYXJ0RGF0ZSA+ICIyMDIwLTAyLTEyIikKYGBgCj4g5oyJ54Wn5pel5pyf5YiH5YiG6LOH5paZCgpgYGB7cn0KZGF0YV9iZWZvcmUgPC0gZGF0YV9iZWZvcmUgJT4lIAogIGdyb3VwX2J5KHdvcmQpICU+JSAKICBzdW1tYXJpc2Uoc3VtID0gc3VtKGNvdW50KSkgJT4lIAogIGFycmFuZ2UoZGVzYyhzdW0pKQoKZGF0YV9hZnRlciA8LSBkYXRhX2FmdGVyICU+JSAKICBncm91cF9ieSh3b3JkKSAlPiUgCiAgc3VtbWFyaXNlKHN1bSA9IHN1bShjb3VudCkpICU+JSAKICBhcnJhbmdlKGRlc2Moc3VtKSkKYGBgCj4g6IiHQ2guMuebuOWQjOeahOizh+aWmeiZleeQhuOAggoKYGBge3J9CnBsb3RfYmVmb3JlIDwtIGRhdGFfYmVmb3JlICU+JSBmaWx0ZXIoc3VtID4gMzAwKSAlPiUgd29yZGNsb3VkMigpCiNwbG90X2JlZm9yZQpgYGAKIVtdKHdvcmRjbG91ZDEucG5nKQoKPiAyLzEyIOS5i+WJjeeahOaWh+Wtl+mbsihodG1s5Lit5pyD6aGv56S65LiN5Ye65L6G56ys5LiA5by15Lul5b6M55qE5paH5a2X6Zuy77yM6KuL5bCH56iL5byP56K85LiL6LyJ5LiL5L6G5Y2z5Y+v5oiQ5Yqf57mq5Ye6KQoKYGBge3J9CnBsb3RfYWZ0ZXIgPC0gZGF0YV9hZnRlciAlPiUgZmlsdGVyKHN1bSA+IDMwMCkgJT4lIHdvcmRjbG91ZDIoKQojcGxvdF9hZnRlcgpgYGAKIVtdKHdvcmRjbG91ZDIucG5nKQoKPiAyLzEy5LmL5b6M55qE5paH5a2X6ZuyKGh0bWzkuK3mnIPpoa/npLrkuI3lh7rkvobnrKzkuIDlvLXku6XlvoznmoTmloflrZfpm7LvvIzoq4vlsIfnqIvlvI/norzkuIvovInkuIvkvobljbPlj6/miJDlip/nuarlh7opCgo+IOingOWvn+WFqeW8teWclueJh+S4reWHuuePvueahOWci+WutuWPr+S7peeZvOePvu+8jOWcqDIvMTLku6XliY3vvIzmnInlh7rnj77jgIzkuK3lnIvjgI3jgIHjgIzlj7DngaPjgI3jgIHjgIznvo7lnIvjgI3jgIHjgIzml6XmnKzjgI3jgIHjgIzpppnmuK/jgI3jgIIg5ZyoMi8xMuS7peW+jO+8jOmWi+Wni+WHuuePvuOAjOmfk+Wci+OAjeOAgeOAjOWNl+mfk+OAjeOAgeOAjOWkp+mCseOAjeOAgeOAjOS8iuacl+OAjeetiemXnOmNteWtl+OAguWkp+iHtOiIh+eWvueXheeIhueZvOeahOWFiOW+jOmghuW6j+S4gOiHtOOAggoKIyMg57e057+S57e057+S772eCj4g5Y+v5Lul55m854++5pys5qyh6LOH5paZ6ZuG5YWn5pyJ6Kix5aSa5paw6IGe55So6Kqe77yM5Zug54K65q2m5ryi6IK654KO54mI5LiK5pyJ6Kix5aSa5Lq66L2J6LK85paw6IGe44CCPGJyPgrpgqPpurznj77lnKjoq4vlkITkvY3lsIfmiYDliJfnmoTlub7lgIvpl5zpjbXlrZfnp7vpmaTlvozph43mlrDnuaroo73mloflrZfpm7LlkKfvvIE8YnI+CuaWsOiBnumXnOmNteWtl++8mueZvOeov+OAgeaWsOiBnuOAgeWOn+aWh+OAgeiomOiAheOAgeeZvOeov+OAgeaSsOeov+iAheOAgemAo+e1kOOAgeWujOaVtOaomemhjOOAgeS7iuWkqeOAgeS4jeaYr+OAgeebruWJjeOAgeihqOekugoKYGBge3J9CiMjIyBDb2RlIEhlcmUgIyMjCmBgYAoKIyBDaDMuIOmVt+aineWclgo+IOaWh+Wtl+mbsuWPr+S7peebtOimuueci+WHuui8g+W4uOaPkOWIsOeahOWtl++8jOS9huWmguaenOaDs+W+l+WIsOeyvueiuueahOOAjOacgOW4uOWHuuePvuipnuW9meOAje+8jOaIkeWAkeWJh+WPr+S7pemAj+mBjumVt+aineWcluS+huafpeeci+OAggoKYGBge3J9CmRhdGEgPC0gcmJpbmQoZGF0YV9iZWZvcmUgJT4lIG11dGF0ZSh0eXBlPSJiZWZvcmUiKSwgZGF0YV9hZnRlciAlPiUgbXV0YXRlKHR5cGU9ImFmdGVyIikpCmBgYAo+IOWwhzIvMTLliY3lvoznmoTos4fmlpnliqDlhaXkuIDlgIvmrITkvY3mqJnnpLrlvozlkIjkvbXotbfkvobjgIIKCmBgYHtyfQpoZWFkKGRhdGEpCmBgYAo+IOmZpOS6huipnuW9meWPiuipnumgu+S7peWklu+8jOmChOWkmuS6huS4gOWAi3R5cGXmrITkvY3kvobljYDliIYyLzEy5Lul5YmN5oiW5piv5Lul5b6M44CCCgpgYGB7cn0KcGxvdF9tZXJnZSA8LSBkYXRhICU+JSAKICBncm91cF9ieSh0eXBlKSAlPiUgCiAgdG9wX24oMTAsIHN1bSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIHN1bSkpICU+JQogIGdncGxvdChhZXMoeD13b3JkLCB5PXN1bSwgZmlsbCA9IHR5cGUpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsIHk9IuipnumguyIpICsKICBmYWNldF93cmFwKH50eXBlLCBuY29sID0gMSwgc2NhbGVzPSJmcmVlIikgKyAKICBjb29yZF9mbGlwKCkrCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiSGVpdGkgVEMgTGlnaHQiKSkKCnBsb3RfbWVyZ2UKYGBgCj4g55Sx5LiK5ZyW5Y+v5Lul55yL5Yiw77yM6KiO6KuW55qE5YWn5a655ZyoMi8xMuWJjeW+jOWkp+iHtOebuOWQjOOAggoKYGBge3J9CnBsb3RfbWVyZ2UgPC0gZGF0YSAlPiUgCiAgZ3JvdXBfYnkodHlwZSkgJT4lIAogIHRvcF9uKDUwLCBzdW0pICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgI2ZpbHRlcighKGR1cGxpY2F0ZWQod29yZCl8IGR1cGxpY2F0ZWQod29yZCwgZnJvbUxhc3QgPSBUUlVFKSkpICMlPiUgCiAgZ3JvdXBfYnkod29yZCkgJT4lCiAgZmlsdGVyKG4oKT09MSkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgc3VtKSkgJT4lIAogIGdncGxvdChhZXMod29yZCwgc3VtLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwgeT0i57i95ZKMIikgKwogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2wgPSAxLCBzY2FsZXM9ImZyZWUiKSArIAogIGNvb3JkX2ZsaXAoKSsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJIZWl0aSBUQyBMaWdodCIpKQoKcGxvdF9tZXJnZQpgYGAKPiDlsIfph43opIfnmoToqZ7lvZnnp7vpmaTlvozvvIzlj6/ku6XnnIvliLDlnKgyLzEy5YmN6IiHMi8xMuW+jOeahOiojuirluW3rueVsO+8mjxicj4KMS4g5ZyL5a625LiN5ZCMPGJyPgoyLiDpmqjokZfnlqvmg4XolJPlu7bvvIzlh7rnj77mm7TlpJrmrbvkuqE8YnI+CjMuIOacgOWIneeahOOAjOWFpeWig+OAjeeuoeWItuitsOmhjO+8jOWIsOW+jOS+hueahOOAjOekvuWNgOOAjeWCs+aSreitsOmhjOOAggoKIyBDaC40IOaDhee3kuaKmOe3muWclgo+IOmAj+mBjuaWh+Wtl+WIhuaekOW5s+WPsOWPr+S7pei8uOWHuuavj+evh+aWh+eroOeahOaDhee3kuWIhuaVuO+8jOWcqOizh+aWmemgkOimvemggemdoumBuOaTh+OAjOaWh+eroCvmg4Xnt5LjgI3pgbjpoIXljbPlj6/lvpfliLDos4fmlpnjgIIKCiMjIOi8ieWFpeizh+aWmQpgYGB7cn0KY3N2X3NlbiA8LSBmcmVhZCgiLi9kYXRhL25Db1YyMDE5X2FydFNlbi5jc3YiLCBlbmNvZGluZyA9ICJVVEYtOCIpCmhlYWQoY3N2X3NlbikKYGBgCj4g6LOH5paZ5qyE5L2N5LuL57S577yaPGJyPgo+IDEuIGFydFRpdGxlOiDmlofnq6DkuYvmqJnpoYzvvIzms6jmhI865LiN5ZCM5paH56ug5Y+v6IO95pyD5pyJ5a6M5YWo55u45ZCM55qE5qiZ6aGM44CCPGJyPgo+IDIuIGFydERhdGU6IOaWh+eroOeZvOS9iOS5i+aXpeacn+OAgjxicj4KPiAzLiBhcnRUaW1lOiDmlofnq6DnmbzkvYjkuYvmmYLplpPjgII8YnI+Cj4gNC4gYXJ0VXJsOiDmlofnq6DkuYvntrLlnYDvvIzmr4/nr4fmlofnq6DkuYvntrLlnYDngrrnjajkuIDnhKHkuoznmoTvvIzlj6/nlKjkvobovqjorZjnm7jlkIzmqJnpoYzkuYvkuI3lkIzmlofnq6DjgII8YnI+Cj4gNS4gcG9zaXRpdmVfZW1vdGlvbl9ncmFkZTog5pys56+H5paH56ug5Ye654++5aSa5bCR5qyh5q2j6Z2i5oOF57eS6Kme5b2Z44CCPGJyPgo+IDYuIG5lZ2F0aXZlX2Vtb3Rpb25fZ3JhZGU6IOacrOevh+aWh+eroOWHuuePvuWkmuWwkeasoeiyoOmdouaDhee3kuipnuW9meOAgjxicj4KPiA3LiBuZXV0cmFsX2Vtb3Rpb25fZ3JhZGU6IOacrOevh+aWh+eroOWHuuePvuWkmuWwkeasoeS4reaAp+aDhee3kuipnuW9meOAggoKYGBge3J9CmNzdl9zZW4kYXJ0RGF0ZSA9IGNzdl9zZW4kYXJ0RGF0ZSAlPiUgYXMuRGF0ZSgiJVkvJW0vJWQiKQpzdHIoY3N2X3NlbikKYGBgCj4g6IiHQ2guMeebuOWQjO+8jOizh+aWmemgkOioreaXpeacn+agvOW8j+eCuiJjaHIi77yM5oiR5YCR6ZyA6KaB5YWI5bCH5YW26L2J54K6ImRhdGUi5qC85byP44CCCgpgYGB7cn0KZGF0YV9zZW4gPC0gY3N2X3NlbiAlPiUgCiAgZ3JvdXBfYnkoYXJ0RGF0ZSkgJT4lIAogIHN1bW1hcmlzZShwb3NpdGl2ZSA9IHN1bShwb3NpdGl2ZV9lbW90aW9uX2dyYWRlKSwgbmVnYXRpdmUgPSBzdW0obmVnYXRpdmVfZW1vdGlvbl9ncmFkZSksIG5ldXRyYWwgPSBzdW0obmV1dHJhbF9lbW90aW9uX2dyYWRlKSkKCmhlYWQoZGF0YV9zZW4pCmBgYAo+IOWwh+izh+aWmeaMieeFp+aXpeacn+WIhue+pOW+jO+8jOWKoOe4veavj+WkqeeahOS4ieeoruaDhee3kuWIhuaVuO+8jOS4pumAsuihjOaomea6luWMluOAggoKYGBge3J9CmRhdGFfc2VuICU+JSBnZ3Bsb3QoYWVzKHg9IGFydERhdGUpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gcG9zaXRpdmUsIGNvbG91ciA9ICJyZWQiKSkgKwogIGdlb21fbGluZShhZXMoeSA9IG5lZ2F0aXZlLCBjb2xvdXIgPSAiYmx1ZSIpKSArCiAgZ2VvbV9saW5lKGFlcyh5ID0gbmV1dHJhbCwgY29sb3VyID0gInllbGxvdyIpKSArIAogIHNjYWxlX3hfZGF0ZShsYWJlbHMgPSBkYXRlX2Zvcm1hdCgiJVkvJW0vJWQiKSkgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWU9IuaDhee3kueorumhniIsIGxhYmVscyA9IGMoInBvc2l0aXZlIiwibmVnYXRpdmUiLCJuZXV0cmFsIikpICsgCiAgZ2d0aXRsZSgi5q2m5ryi6IK654KOIOiojuirluaDhee3kiIpICsgCiAgeGxhYigi5pel5pyfIikgKyAKICB5bGFiKCLliIbmlbgiKSArIAogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIkhlaXRpIFRDIExpZ2h0IikpCmBgYAoKCiMjIOe3tOe/kue3tOe/ku+9ngo+IOWJm+WJm+aIkeWAkeWwh+izh+aWmeaMieeFp+aXpeacn+WIhue+pOW+jO+8jOWKoOe4veioiOeul+S6huavj+WkqeeahOS4ieeoruaDhee3kuWIhuaVuOOAggrkvYbmmK/miJHlgJHlj6/ku6Xnmbznj77mr4/lpKnnmoTmlofnq6Dmlbjph4/pgKDmiJDovq3lvZnnmoTmlbjph4/kuI3kuIDmqKPvvIzmiYDku6Xmg4Xnt5LlnJbnmoTotbDli6LmnIPmnInlvojlpKfnmoTotbfkvI/jgIIK6KuL5ZCE5L2N5bCH5q+P5pel55qE5oOF57eS57i95YC86YCy6KGM5qiZ5rqW5YyW5b6M5YaN55Wr5LiA5qyh5oOF57eS55qE6LWw5Yui5ZyW44CCPGJyPjxicj4K5YWs5byP77yaPGJyPgpwb3NpdGl2ZSA9IHBvc2l0aXZlIC8gKHBvc2l0aXZlICsgbmVnYXRpdmUgKyBuZXV0cmFsKTxicj4KbmVnYXRpdmUgPSBuZWdhdGl2ZSAvIChwb3NpdGl2ZSArIG5lZ2F0aXZlICsgbmV1dHJhbCk8YnI+Cm5ldXRyYWwgPSBuZXV0cmFsIC8gKHBvc2l0aXZlICsgbmVnYXRpdmUgKyBuZXV0cmFsKTxicj48YnI+CmhpbnTvvJrliZvliZtzdW1tYXJpc2XpgqPpgorlj6/ku6XpoIbkvr/oqIjnrpfmr4/ml6XnmoTmg4Xnt5LoqZ7nuL3mlbg8YnI+CmhpbnQy77ya5pyA5b6M5pyD55So5YiwbXV0YXRl5ZaUCgpgYGB7cn0KIyMjIENvZGUgSGVyZSAjIyMKYGBgCgoKCgo=