1 爬Google新聞:Scraping news report

Try to scrape and parse one news website (必須要是非得剖析背後的html不可的網站,例如鉅亨網背後是json,那就沒必要用html)

# packages needed
library(stringr)
library(tidyverse) #with tidyr, dplyr, magrittr
library(rvest) #with rvest
# library(httr)
# library(dplyr)
library(lubridate)
options(stringsAsFactors = F)
# options(encoding = "")

我選擇Google news,類別重要明確,包含熱門即時,還有自己喜歡的分類,如商業、科技等等。

1.1 抓取文章網址連結

從Google news網頁中得到連出去原新聞來源(如ETtoday, 中時電子報)的網址,以及新聞來源、標題、發佈時間。 文章內容則於後面步驟,在來源頁面取得後,一併放入Dataframe。

#news source
google <- "https://news.google.com"
google_news_url <- "https://news.google.com/topics/CAAqKggKIiRDQkFTRlFvSUwyMHZNRFZxYUdjU0JYcG9MVlJYR2dKVVZ5Z0FQAQ?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"
## Get post link
# Get and parse the url
doc  <- read_html(google_news_url)
cT <- Sys.time() #crawlingTime
# observe where the articles' links are
css <- "article a.VDXfz"
# retrieve links of news
news_links <- doc %>%
    html_nodes(css) %>% 
    html_attr("href")
# take a look at the result
head(news_links, 3)
[1] "./articles/CBMiLmh0dHBzOi8vbmV3dGFsay50dy9uZXdzL3ZpZXcvMjAxOC0xMC0yOC8xNTg4OTnSAQA?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"                                                                                                                                        
[2] "./articles/CBMiPWh0dHBzOi8vd3d3LmNoaW5hdGltZXMuY29tL3JlYWx0aW1lbmV3cy8yMDE4MTAyODAwMDkyMC0yNjA0MDfSAQA?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"                                                                                                                    
[3] "./articles/CBMiKGh0dHBzOi8vdWRuLmNvbS9uZXdzL3N0b3J5LzEwOTU4LzM0NDcxOTDSAWxodHRwczovL3Vkbi1jb20uY2RuLmFtcHByb2plY3Qub3JnL3Yvcy91ZG4uY29tL25ld3MvYW1wL3N0b3J5LzEwOTU4LzM0NDcxOTA_YW1wX2pzX3Y9MC4xI3dlYnZpZXc9MSZjYXA9c3dpcGU?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"
length(news_links)
[1] 264
# the result is not an url, can't be browsed, so I fix it
new_links <- str_replace(news_links, "\\.", google)
tail(new_links,3)
[1] "https://news.google.com/articles/CBMiugFodHRwczovL3R3Lm5ld3MueWFob28uY29tLyVFOSU5OSVCOCVFNiU4QiVCMyVFOCVCMyVCRCVFOSU5RCU5RSVFNiVCNCVCMiVFNiU4QiVCMyVFNyU4RSU4QiVFOSU4MSVBRCVFOCU5OSU5MC0lRTglQTIlQUIlRTclODglODYlRTUlOEYlQUElRTYlOTglQUYlRTclOTUlOTklRTUlQUQlQjglRTclOTQlOUYtMDUwNjM2OTgxLmh0bWzSAYwCaHR0cHM6Ly90dy1uZXdzLXlhaG9vLWNvbS5jZG4uYW1wcHJvamVjdC5vcmcvdi9zL3R3Lm5ld3MueWFob28uY29tL2FtcGh0bWwvJUU5JTk5JUI4JUU2JThCJUIzJUU4JUIzJUJEJUU5JTlEJTlFJUU2JUI0JUIyJUU2JThCJUIzJUU3JThFJThCJUU5JTgxJUFEJUU4JTk5JTkwLSVFOCVBMiVBQiVFNyU4OCU4NiVFNSU4RiVBQSVFNiU5OCVBRiVFNyU5NSU5OSVFNSVBRCVCOCVFNyU5NCU5Ri0wNTA2MzY5ODEuaHRtbD9hbXBfanNfdj0wLjEjd2Vidmlldz0xJmNhcD1zd2lwZQ?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"
[2] "https://news.google.com/articles/CAIiEOzhLWCTsUTHEB1FtyNtCt4qFwgEKg4IACoGCAowr7I3MKfqBzDPlIMG?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
[3] "https://news.google.com/articles/CBMiJ2h0dHBzOi8vdWRuLmNvbS9uZXdzL3N0b3J5LzczMzIvMzQ0NjUxNtIBa2h0dHBzOi8vdWRuLWNvbS5jZG4uYW1wcHJvamVjdC5vcmcvdi9zL3Vkbi5jb20vbmV3cy9hbXAvc3RvcnkvNzMzMi8zNDQ2NTE2P2FtcF9qc192PTAuMSN3ZWJ2aWV3PTEmY2FwPXN3aXBl?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"                                                                                                                                                                                                                                                                                                                                                                                                                              
# see if it works? browse the last one
browseURL(new_links[length(new_links)])
## Get post source: publisher
css_source <- "article .QmrVtf .KbnJ8"
news_source <- doc %>%
    html_nodes(css_source) %>% 
    html_text()
length(news_source)
[1] 264
# head(news_source)
## Get post headline
css_headline <- "article .ZulkBc a span"
news_title <- doc %>%
    html_nodes(css_headline) %>% 
    html_text()
# head(news_title) ##non-numeric to binary
# Get post datetime
css_time <- ".kybdz > div > time"
news_time <- doc %>%
    html_nodes(css_time) %>% 
    html_attr("datetime")
news_time[3]
[1] "seconds: 1540710321\n"
length(news_title);length(news_time)
[1] 264
[1] 264
# fix timestamp and converted its type
news_time <- str_replace(news_time, "seconds: ","")
news_time <- str_replace(news_time, "\n","")
news_time <- as_datetime(as.numeric(news_time))
news_time[3]
[1] "2018-10-28 07:05:21 UTC"
## Create data frame
gnews_df <- data.frame()
gnews_df <- data.frame(headline=news_title,
                       source=news_source,
                       link = new_links,
                       time = news_time
                       )
# take a look at df
View(gnews_df)
gnews_df[c(1:3, nrow(gnews_df)),]
sn <- table(gnews_df$source)
# names(sn); ##non-numeric to binary

Google news中的新聞是來自許多不同來源的新聞,從“最新”標籤下,抓取到的新聞來源常出現的包含諸如…

  • “自由時報電子報” “中時電子報” “ETtoday 新聞雲”
  • “udn 聯合新聞網” “TVBS新聞”
sn <- sort(sn[])
# names(sn[(length(sn):(length(sn)-4))])
# sn # table of all sources

1.2 嘗試取得新聞來源頁面之文章內容

遇到的問題:若直接從上面爬下來得到的連結並無法順利獲取text,因為中間會先經過跳轉的步驟,才到來源頁的真實網址。比如一則源於ETtoday的新聞:

測試:從來源網頁經過CSS Selector得到文章paragraph位置,從A網址抓不到,但從B可以順利得到。

  • 以針對單一頁面測試結果如下:
## Test ETtoday link
# paragraph css
css_ETt <- ".story p"
#A: google url
ETnews <- str_detect(gnews_df[,"source"],"ETtoday")
link <- gnews_df[ETnews,"link"][1]
link
[1] "https://news.google.com/articles/CAIiEG6qt7WqdQ0ZZESdB_eVMIIqFggEKg4IACoGCAowr7I3MKfqBzDpjAs?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant"
postprgr <- read_html(link) %>%
    html_nodes(css = css_ETt) %>% 
    html_text()
# postprgr #notebook, not preview
#B: real url
link <- "https://www.ettoday.net/news/20181022/1287141.htm"
postprgr <- read_html(link) %>%
    html_nodes(css = css_ETt) %>% 
    html_text()
# see one of the paragraph
# postprgr[3] ##non-numeric to binary
post <- paste(postprgr[c(3:length(postprgr)-2)], collapse = "")
# see the entire post
post ##non-numeric to binary
[1] "政治中心/綜合報導普悠瑪列車21日傍晚翻覆造成列車上18人死亡、183人輕重傷,行政院長賴清德凌晨趕到蘇澳榮民醫院慰問死者家屬,一聽到董姓乘客與家人共有8人罹難,當場傻住,關心慰問之餘也紅了眼眶。董家在22日上午再度痛失一名親屬。據了解,台東船長董進興,號召親友共17人北上吃喜酒,回程全家都坐上這列死亡列車上,造成夫妻與2個孫子等共8人死亡,還有2人在加護病房搶救。賴清德凌晨在宜蘭縣代理縣長陳金德陪同下,到蘇澳榮民醫院探視罹難者家屬,當他聽到董家一下失去8人,當場傻住,賴清德說,「那一定超級難過」,他還猶豫一下要不要敲休息室的門,旁人勸他還是要,他和家屬見面,紅了眼眶。▲賴清德到普悠瑪出軌意外現場探視。(圖/記者姜國輝攝)據了解,董姓一家人分別買到了車頭第8節車廂和第3節車廂的位置,死傷慘重幾乎多位於車頭,其中董進興夫妻檔、弟弟董進發和孫子董怡良、孫女董佳惠等8人皆不幸罹難,另2人則在加護病房觀察,而其他7人則因坐在第3節車廂逃過一劫。據宜蘭縣消防局統計,18名罹難者中,董家罹難者有8人,分別是董進興、王綠雲(董進興妻)、董玉蘭與何發仁(董玉蘭夫)、董進發(董進興弟)、董宜良(董孫)、董佳惠(董孫女)與何青宴。  賴清德表示,罹難者後事交通部與地方政府會共同合作,原則是優先尊重家屬意見,剛剛慰問一戶家屬,已經準備將親人大體運送回花蓮,交通部跟宜蘭縣政府會也會共同合作,辦聯合公祭, 後事會協助大家,一起完滿辦成。據了解,22日上午董家再度痛失另一名家屬,死亡人數更新為9名。""
1,

1.3 從A到B過程經過跳轉。

解決方法:在瀏覽器貼上A網址,在跳轉的載入過程中按下網址列左邊的叉。

  • 此時可以看到網頁上寫著如:「正在開啟https://www.ettoday.net/news/20181022/1287141.htm」
  • 開啟Chrome DevTools,可以看到過度網頁的HTML碼,在這之中可以找到B網址的位置,取得css selector。
# Redirecting page
redirection_css <- "c-wiz > div > a"
link <- gnews_df[ETnews,"link"][1]
real_link <- read_html(link) %>%
    html_nodes(css = redirection_css) %>% 
    html_attr("href")
real_link
[1] "https://www.ettoday.net/news/20181027/1291939.htm"
  • 擴張到所有連結的置換,改變gnews_df$link
length(gnews_df$link)
[1] 264
new_link<-NULL
i <- 0
if(str_detect(gnews_df$link[1], "news.google.com")){
    for(lnk in gnews_df$link){
        real_link <- read_html(lnk) %>%
            html_nodes(css = redirection_css) %>% 
            html_attr("href")
        new_link <- c(new_link, real_link)
        i <- i+1
        # print(paste0(i, "......", real_link))
    }
}
Warning in .Internal(lapply(X, FUN)) :
  closing unused connection 3 (https://news.google.com/articles/CAIiEATX7rPfsnzWwakr0LnRnHoqFwgEKg4IACoGCAowr7I3MKfqBzDfkoMG?hl=zh-TW&gl=TW&ceid=TW%3Azh-Hant)
# take a look at the converted link
head(new_link, 3)
[1] "https://newtalk.tw/news/view/2018-10-28/158899"               
[2] "https://www.chinatimes.com/realtimenews/20181028000920-260407"
[3] "https://udn.com/news/story/10958/3447190"                     
tail(new_link, 3)
[1] "https://tw.news.yahoo.com/%E9%99%B8%E6%8B%B3%E8%B3%BD%E9%9D%9E%E6%B4%B2%E6%8B%B3%E7%8E%8B%E9%81%AD%E8%99%90-%E8%A2%AB%E7%88%86%E5%8F%AA%E6%98%AF%E7%95%99%E5%AD%B8%E7%94%9F-050636981.html"
[2] "https://www.ettoday.net/news/20181028/1292431.htm"                                                                                                                                         
[3] "https://udn.com/news/story/7332/3446516"                                                                                                                                                   
# mutate to df
glinks <- gnews_df$link
gnews_df <- mutate(gnews_df, link = new_link)
gnews_df[1:6,]

1.4 重新嘗試取得新聞內容

現在,有了原新聞的連結,就可以依據來源,一一去找尋其文章段落所在位置。

  • get at least 100 news reports
  • 從上面,針對source做出table()再sort後,觀察可知最常出現的有:
    • ETtoday
    • 自由時報
    • udn聯合
    • TVBS
    • 中時
    • Yahoo
  • 於是針對這幾個新聞來源存取文章。
##start parsing post
#css collection
css_chinatimes <- "article > p"
css_chinatimes_firstprgr <- ".pictext > p"
css_yahoo <- "article > div > p"
css_udn <- "#story_body_content > p"
css_tvbs <- "#news_detail_div"
css_ltn <- "div.text > p"

i <- 1
posts <- NULL


for(lnk in gnews_df$link){
    post <- NA
    # css_temp <- NA
    
    # Cases
    if(gnews_df$source[i]=="ETtoday 新聞雲")
    {
        postprgr <- read_html(lnk) %>%
           html_nodes(css = css_ETt) %>%
           html_text()
        post <- paste(postprgr[c(3:(length(postprgr)-2))], collapse = " ")
    }
    else if(gnews_df$source[i]=="中時電子報")
    {
        #content
        postprgr <- read_html(lnk) %>%
           html_nodes(css = css_chinatimes) %>%
           html_text()
        post <- paste(postprgr, collapse = " ")
        #picture caption
        postprgr <- read_html(lnk) %>%
           html_nodes(css = css_chinatimes_firstprgr) %>%
           html_text()
        postprgr <- postprgr %>%
            str_remove_all("\r\n") %>%
            str_trim()
        post <- paste(postprgr, post)
    }
    else if(gnews_df$source[i] %>% str_detect("Yahoo奇摩"))
    {
        postprgr <- read_html(lnk) %>%
           html_nodes(css = css_yahoo) %>%
           html_text()
        post <- paste(postprgr[c(1:length(postprgr))], collapse = " ")
    }
    else if(gnews_df$source[i]=="udn 聯合新聞網")
    {
        postprgr <- read_html(lnk) %>%
           html_nodes(css = css_udn) %>%
           html_text() %>% 
            str_trim()
        post <- paste(postprgr, collapse = " ")
    }
    else if(gnews_df$source[i]=="自由時報電子報")
    {
        postprgr <- read_html(lnk) %>%
           html_nodes(css = css_ltn) %>%
           html_text()
        post <- paste(postprgr, collapse = " ")
    }
    else if(gnews_df$source[i]=="TVBS新聞")
    {
        postprgr <- read_html(lnk) %>%
           html_nodes(css = css_tvbs) %>%
           html_text()
        postprgr <- postprgr %>%
            str_remove_all("\r\n") %>%
            str_trim() %>% 
            str_remove_all("最HOT話題在這!想跟上時事,快點我加入TVBS新聞LINE好友!") %>% 
            str_remove("2018全台選戰風雲 TVBS最新大數據分析") %>% 
            str_remove("看不到影音請點這")
        post <- paste(postprgr, collapse = " ")
    }
    
    # print(paste0(i, "......", post))
    posts <- c(posts, post)
    i <- i + 1
    
    if(i%%5 ==0){
        Sys.sleep(sample(1:3,size = 1))
    }
}
# see the result of posts
# head(posts);tail(posts)
# convert NULL data to NA
posts[posts == ""] <- NA # posts[is.na(posts)] <- ""
# count posts with content
sum(!is.na(posts))
[1] 174
length(posts)
[1] 264

1.5 將文章合併至dataframe並儲存

  • 這邊只取出現次數最高的前6個來源(確保符合作業要>100篇的要求)文章,其餘剔除。(即保留posts非NA的資料)
  • Store news data as .rds (zip檔中含有googleNews_10_28_12.rds檔存放df)
## Add posts to gnews_df
gnews_df <- mutate(gnews_df, article = posts)
Warning message:
In scan(file = file, what = what, sep = sep, quote = quote, dec = dec,  :
  EOF within quoted string
# filter out article with content
gnews_df <- gnews_df[!is.na(gnews_df$article),]
dim(gnews_df)
[1] 174   5
gnews_df
## Saving DataFrame
format(cT)
[1] "2018-10-28 22:52:08"
# cT_tag <- paste0(month(cT),day(cT),"_",hour(cT),minute(cT))
cT_tag <- paste0(month(cT),day(cT))
rdsname <- paste("googleNews",cT_tag, hour(cT), sep = "_")
rdsname <- paste0(rdsname,".rds")
# save new gnews_df as rds
rdsname
[1] "googleNews_1028_22.rds"
saveRDS(gnews_df, rdsname)
## load existed df example
# load old df
loaded_gnews_df <- readRDS("googleNews_10_28_12.rds")
dim(loaded_gnews_df)
[1] 157   5
loaded_gnews_df

by Ivan Chen

LS0tDQp0aXRsZTogIkdvb2dsZSBOZXdzIENyYXdsZXIiDQphdXRob3I6ICJDaGVuIFNoaWggWXVuIg0KZGF0ZTogMjAxOC8xMC8yMg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQotLS0NCg0KIyDniKxHb29nbGXmlrDogZ7vvJpTY3JhcGluZyBuZXdzIHJlcG9ydA0KDQo+IFRyeSB0byBzY3JhcGUgYW5kIHBhcnNlIG9uZSBuZXdzIHdlYnNpdGUgKOW/hemgiOimgeaYr+mdnuW+l+WJluaekOiDjOW+jOeahGh0bWzkuI3lj6/nmoTntrLnq5nvvIzkvovlpoLpiYXkuqjntrLog4zlvozmmK9qc29u77yM6YKj5bCx5rKS5b+F6KaB55SoaHRtbCkNCg0KLSBlLmcuLCBsdG4ubmV3cywgYXBwbGUgbmV3cywgdWRuIG5ld3MsIC4uLg0KLSB3aXRoICoqb25lIHF1ZXJ5KiogdG8gZ2V0IGF0IGxlYXN0ICoqMTAwIG5ld3MgcmVwb3J0cyoqDQotIFN0b3JlIHlvdXIgZGF0YSBhcyAucmRzIG9yIC5yZGENCi0gWmlwIHlvdXIgZGF0YSwgLnJtZCwgLmh0bWwgZmlsZSBpbnRvIGEgemlwcGVkIGZpbGUsIHRoZW4gdXBsb2FkDQoNCmBgYHtyIGxvYWRQYWNrYWdlLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBwYWNrYWdlcyBuZWVkZWQNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkodGlkeXZlcnNlKSAjd2l0aCB0aWR5ciwgZHBseXIsIG1hZ3JpdHRyDQpsaWJyYXJ5KHJ2ZXN0KSAjd2l0aCBydmVzdA0KIyBsaWJyYXJ5KGh0dHIpDQojIGxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCm9wdGlvbnMoc3RyaW5nc0FzRmFjdG9ycyA9IEYpDQojIG9wdGlvbnMoZW5jb2RpbmcgPSAiIikNCmBgYA0KDQoNCj4g5oiR6YG45pOHW0dvb2dsZSBuZXdzXShodHRwczovL25ld3MuZ29vZ2xlLmNvbS90b3BpY3MvQ0FBcUtnZ0tJaVJEUWtGVFJsRnZTVXd5TUhaTlJGWnhZVWRqVTBKWWNHOU1WbEpZUjJkS1ZWWjVaMEZRQVE/aGw9emgtVFcmZ2w9VFcmY2VpZD1UVyUzQXpoLUhhbnQp77yM6aGe5Yil6YeN6KaB5piO56K677yM5YyF5ZCr54ax6ZaA5Y2z5pmC77yM6YKE5pyJ6Ieq5bex5Zac5q2h55qE5YiG6aGe77yM5aaC5ZWG5qWt44CB56eR5oqA562J562J44CCDQoNCg0KIyMg5oqT5Y+W5paH56ug57ay5Z2A6YCj57WQDQoNCj4g5b6eR29vZ2xlIG5ld3PntrLpoIHkuK3lvpfliLDpgKPlh7rljrvljp/mlrDogZ7kvobmupAo5aaCRVR0b2RheSwg5Lit5pmC6Zu75a2Q5aCxKeeahOe2suWdgO+8jOS7peWPiuaWsOiBnuS+hua6kOOAgeaomemhjOOAgeeZvOS9iOaZgumWk+OAgg0KPiDmlofnq6DlhaflrrnliYfmlrzlvozpnaLmraXpqZ/vvIzlnKjkvobmupDpoIHpnaLlj5blvpflvozvvIzkuIDkvbXmlL7lhaVEYXRhZnJhbWXjgIINCg0KYGBge3J9DQojbmV3cyBzb3VyY2UNCmdvb2dsZSA8LSAiaHR0cHM6Ly9uZXdzLmdvb2dsZS5jb20iDQpnb29nbGVfbmV3c191cmwgPC0gImh0dHBzOi8vbmV3cy5nb29nbGUuY29tL3RvcGljcy9DQUFxS2dnS0lpUkRRa0ZUUmxGdlNVd3lNSFpOUkZaeFlVZGpVMEpZY0c5TVZsSllSMmRLVlZaNVowRlFBUT9obD16aC1UVyZnbD1UVyZjZWlkPVRXJTNBemgtSGFudCINCg0KIyMgR2V0IHBvc3QgbGluaw0KIyBHZXQgYW5kIHBhcnNlIHRoZSB1cmwNCmRvYyAgPC0gcmVhZF9odG1sKGdvb2dsZV9uZXdzX3VybCkNCmNUIDwtIFN5cy50aW1lKCkgI2NyYXdsaW5nVGltZQ0KYGBgDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0V9DQojIG9ic2VydmUgd2hlcmUgdGhlIGFydGljbGVzJyBsaW5rcyBhcmUNCmNzcyA8LSAiYXJ0aWNsZSBhLlZEWGZ6Ig0KDQojIHJldHJpZXZlIGxpbmtzIG9mIG5ld3MNCm5ld3NfbGlua3MgPC0gZG9jICU+JQ0KICAgIGh0bWxfbm9kZXMoY3NzKSAlPiUgDQogICAgaHRtbF9hdHRyKCJocmVmIikNCg0KIyB0YWtlIGEgbG9vayBhdCB0aGUgcmVzdWx0DQpoZWFkKG5ld3NfbGlua3MsIDMpDQpsZW5ndGgobmV3c19saW5rcykNCg0KIyB0aGUgcmVzdWx0IGlzIG5vdCBhbiB1cmwsIGNhbid0IGJlIGJyb3dzZWQsIHNvIEkgZml4IGl0DQpuZXdfbGlua3MgPC0gc3RyX3JlcGxhY2UobmV3c19saW5rcywgIlxcLiIsIGdvb2dsZSkNCnRhaWwobmV3X2xpbmtzLDMpDQoNCg0KIyBzZWUgaWYgaXQgd29ya3M/IGJyb3dzZSB0aGUgbGFzdCBvbmUNCmJyb3dzZVVSTChuZXdfbGlua3NbbGVuZ3RoKG5ld19saW5rcyldKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyBHZXQgcG9zdCBzb3VyY2U6IHB1Ymxpc2hlcg0KY3NzX3NvdXJjZSA8LSAiYXJ0aWNsZSAuUW1yVnRmIC5LYm5KOCINCg0KbmV3c19zb3VyY2UgPC0gZG9jICU+JQ0KICAgIGh0bWxfbm9kZXMoY3NzX3NvdXJjZSkgJT4lIA0KICAgIGh0bWxfdGV4dCgpDQpsZW5ndGgobmV3c19zb3VyY2UpDQojIGhlYWQobmV3c19zb3VyY2UpDQpgYGANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIEdldCBwb3N0IGhlYWRsaW5lDQpjc3NfaGVhZGxpbmUgPC0gImFydGljbGUgLlp1bGtCYyBhIHNwYW4iDQoNCm5ld3NfdGl0bGUgPC0gZG9jICU+JQ0KICAgIGh0bWxfbm9kZXMoY3NzX2hlYWRsaW5lKSAlPiUgDQogICAgaHRtbF90ZXh0KCkNCiMgaGVhZChuZXdzX3RpdGxlKSAjI25vbi1udW1lcmljIHRvIGJpbmFyeQ0KDQojIEdldCBwb3N0IGRhdGV0aW1lDQpjc3NfdGltZSA8LSAiLmt5YmR6ID4gZGl2ID4gdGltZSINCm5ld3NfdGltZSA8LSBkb2MgJT4lDQogICAgaHRtbF9ub2Rlcyhjc3NfdGltZSkgJT4lIA0KICAgIGh0bWxfYXR0cigiZGF0ZXRpbWUiKQ0KbmV3c190aW1lWzNdDQpsZW5ndGgobmV3c190aXRsZSk7bGVuZ3RoKG5ld3NfdGltZSkNCg0KIyBmaXggdGltZXN0YW1wIGFuZCBjb252ZXJ0ZWQgaXRzIHR5cGUNCm5ld3NfdGltZSA8LSBzdHJfcmVwbGFjZShuZXdzX3RpbWUsICJzZWNvbmRzOiAiLCIiKQ0KbmV3c190aW1lIDwtIHN0cl9yZXBsYWNlKG5ld3NfdGltZSwgIlxuIiwiIikNCm5ld3NfdGltZSA8LSBhc19kYXRldGltZShhcy5udW1lcmljKG5ld3NfdGltZSkpDQpuZXdzX3RpbWVbM10NCg0KIyMgQ3JlYXRlIGRhdGEgZnJhbWUNCmduZXdzX2RmIDwtIGRhdGEuZnJhbWUoKQ0KZ25ld3NfZGYgPC0gZGF0YS5mcmFtZShoZWFkbGluZT1uZXdzX3RpdGxlLA0KICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2U9bmV3c19zb3VyY2UsDQogICAgICAgICAgICAgICAgICAgICAgIGxpbmsgPSBuZXdfbGlua3MsDQogICAgICAgICAgICAgICAgICAgICAgIHRpbWUgPSBuZXdzX3RpbWUNCiAgICAgICAgICAgICAgICAgICAgICAgKQ0KIyB0YWtlIGEgbG9vayBhdCBkZg0KVmlldyhnbmV3c19kZikNCmduZXdzX2RmW2MoMTozLCBucm93KGduZXdzX2RmKSksXQ0Kc24gPC0gdGFibGUoZ25ld3NfZGYkc291cmNlKQ0KIyBuYW1lcyhzbik7ICMjbm9uLW51bWVyaWMgdG8gYmluYXJ5DQpgYGANCg0KDQo+IEdvb2dsZSBuZXdz5Lit55qE5paw6IGe5piv5L6G6Ieq6Kix5aSa5LiN5ZCM5L6G5rqQ55qE5paw6IGe77yM5b6eIuacgOaWsCLmqJnnsaTkuIvvvIzmipPlj5bliLDnmoTmlrDogZ7kvobmupDluLjlh7rnj77nmoTljIXlkKvoq7jlpoIuLi4gDQoNCi0gIuiHqueUseaZguWgsembu+WtkOWgsSIgIuS4reaZgumbu+WtkOWgsSIgIkVUdG9kYXkg5paw6IGe6ZuyIg0KLSAidWRuIOiBr+WQiOaWsOiBnue2siIgIlRWQlPmlrDogZ4iIA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc24gPC0gc29ydChzbltdKQ0KDQojIG5hbWVzKHNuWyhsZW5ndGgoc24pOihsZW5ndGgoc24pLTQpKV0pDQoNCiMgc24gIyB0YWJsZSBvZiBhbGwgc291cmNlcw0KYGBgDQoNCiMjIOWYl+ippuWPluW+l+aWsOiBnuS+hua6kOmggemdouS5i+aWh+eroOWFp+WuuQ0KDQo+IOmBh+WIsOeahOWVj+mhjO+8muiLpeebtOaOpeW+nuS4iumdoueIrOS4i+S+huW+l+WIsOeahOmAo+e1kOS4pueEoeazlemghuWIqeeNsuWPlnRleHTvvIzlm6DngrrkuK3plpPmnIPlhYjntpPpgY7ot7PovYnnmoTmraXpqZ/vvIzmiY3liLDkvobmupDpoIHnmoTnnJ/lr6bntrLlnYDjgILmr5TlpoLkuIDliYfmupDmlrxFVHRvZGF555qE5paw6IGe77yaDQoNCi0gQS7niKzliLDnmoTntrLlnYDvvJpodHRwczovL25ld3MuZ29vZ2xlLmNvbS9hcnRpY2xlcy9DQUlpRUw3RG5JVUYybFJyUVdYSlQ5VnljMHNxRmdnRUtnNElBQ29HQ0Fvd3I3STNNS2ZxQnpEcGpBcz9obD16aC1UVyZnbD1UVyZjZWlkPVRXJTNBemgtSGFudA0KLSBCLuecn+WvpueahOe2suWdgO+8mmh0dHBzOi8vd3d3LmV0dG9kYXkubmV0L25ld3MvMjAxODEwMjIvMTI4NzE0MS5odG0NCg0KPiDmuKzoqabvvJrlvp7kvobmupDntrLpoIHntpPpgY5DU1MgU2VsZWN0b3LlvpfliLDmlofnq6BwYXJhZ3JhcGjkvY3nva7vvIzlvp5B57ay5Z2A5oqT5LiN5Yiw77yM5L2G5b6eQuWPr+S7pemghuWIqeW+l+WIsOOAgg0KDQotIOS7pemHneWwjeWWruS4gOmggemdoua4rOippue1kOaenOWmguS4i++8mg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KIyMgVGVzdCBFVHRvZGF5IGxpbmsNCiMgcGFyYWdyYXBoIGNzcw0KY3NzX0VUdCA8LSAiLnN0b3J5IHAiDQoNCiNBOiBnb29nbGUgdXJsDQpFVG5ld3MgPC0gc3RyX2RldGVjdChnbmV3c19kZlssInNvdXJjZSJdLCJFVHRvZGF5IikNCmxpbmsgPC0gZ25ld3NfZGZbRVRuZXdzLCJsaW5rIl1bMV0NCmxpbmsNCg0KcG9zdHByZ3IgPC0gcmVhZF9odG1sKGxpbmspICU+JQ0KICAgIGh0bWxfbm9kZXMoY3NzID0gY3NzX0VUdCkgJT4lIA0KICAgIGh0bWxfdGV4dCgpDQoNCiMgcG9zdHByZ3IgI25vdGVib29rLCBub3QgcHJldmlldw0KDQoNCiNCOiByZWFsIHVybA0KbGluayA8LSAiaHR0cHM6Ly93d3cuZXR0b2RheS5uZXQvbmV3cy8yMDE4MTAyMi8xMjg3MTQxLmh0bSINCnBvc3RwcmdyIDwtIHJlYWRfaHRtbChsaW5rKSAlPiUNCiAgICBodG1sX25vZGVzKGNzcyA9IGNzc19FVHQpICU+JSANCiAgICBodG1sX3RleHQoKQ0KDQojIHNlZSBvbmUgb2YgdGhlIHBhcmFncmFwaA0KIyBwb3N0cHJnclszXSAjI25vbi1udW1lcmljIHRvIGJpbmFyeQ0KDQpwb3N0IDwtIHBhc3RlKHBvc3RwcmdyW2MoMzpsZW5ndGgocG9zdHByZ3IpLTIpXSwgY29sbGFwc2UgPSAiIikNCiMgc2VlIHRoZSBlbnRpcmUgcG9zdA0KcG9zdA0KYGBgDQoNCg0KIyMg5b6eQeWIsELpgY7nqIvntpPpgY7ot7PovYnjgIINCg0KPiDop6Pmsbrmlrnms5XvvJrlnKjngI/opr3lmajosrzkuIpB57ay5Z2A77yM5Zyo6Lez6L2J55qE6LyJ5YWl6YGO56iL5Lit5oyJ5LiL57ay5Z2A5YiX5bem6YKK55qE5Y+J44CCDQoNCi0g5q2k5pmC5Y+v5Lul55yL5Yiw57ay6aCB5LiK5a+r6JGX5aaC77ya44CM5q2j5Zyo6ZaL5ZWfaHR0cHM6Ly93d3cuZXR0b2RheS5uZXQvbmV3cy8yMDE4MTAyMi8xMjg3MTQxLmh0beOAjQ0KLSDplovllZ9DaHJvbWUgRGV2VG9vbHPvvIzlj6/ku6XnnIvliLDpgY7luqbntrLpoIHnmoRIVE1M56K877yM5Zyo6YCZ5LmL5Lit5Y+v5Lul5om+5YiwQue2suWdgOeahOS9jee9ru+8jOWPluW+l2NzcyBzZWxlY3RvcuOAgg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KIyBSZWRpcmVjdGluZyBwYWdlDQpyZWRpcmVjdGlvbl9jc3MgPC0gImMtd2l6ID4gZGl2ID4gYSINCg0KbGluayA8LSBnbmV3c19kZltFVG5ld3MsImxpbmsiXVsxXQ0KcmVhbF9saW5rIDwtIHJlYWRfaHRtbChsaW5rKSAlPiUNCiAgICBodG1sX25vZGVzKGNzcyA9IHJlZGlyZWN0aW9uX2NzcykgJT4lIA0KICAgIGh0bWxfYXR0cigiaHJlZiIpDQoNCnJlYWxfbGluaw0KYGBgDQoNCi0g5pO05by15Yiw5omA5pyJ6YCj57WQ55qE572u5o+b77yM5pS56K6KZ25ld3NfZGYkbGluaw0KDQpgYGB7cn0NCmxlbmd0aChnbmV3c19kZiRsaW5rKQ0KDQpuZXdfbGluazwtTlVMTA0KaSA8LSAwDQoNCmlmKHN0cl9kZXRlY3QoZ25ld3NfZGYkbGlua1sxXSwgIm5ld3MuZ29vZ2xlLmNvbSIpKXsNCiAgICBmb3IobG5rIGluIGduZXdzX2RmJGxpbmspew0KICAgICAgICByZWFsX2xpbmsgPC0gcmVhZF9odG1sKGxuaykgJT4lDQogICAgICAgICAgICBodG1sX25vZGVzKGNzcyA9IHJlZGlyZWN0aW9uX2NzcykgJT4lIA0KICAgICAgICAgICAgaHRtbF9hdHRyKCJocmVmIikNCiAgICAgICAgbmV3X2xpbmsgPC0gYyhuZXdfbGluaywgcmVhbF9saW5rKQ0KICAgICAgICBpIDwtIGkrMQ0KICAgICAgICAjIHByaW50KHBhc3RlMChpLCAiLi4uLi4uIiwgcmVhbF9saW5rKSkNCiAgICB9DQp9DQojIHRha2UgYSBsb29rIGF0IHRoZSBjb252ZXJ0ZWQgbGluaw0KaGVhZChuZXdfbGluaywgMykNCnRhaWwobmV3X2xpbmssIDMpDQoNCiMgbXV0YXRlIHRvIGRmDQpnbGlua3MgPC0gZ25ld3NfZGYkbGluaw0KZ25ld3NfZGYgPC0gbXV0YXRlKGduZXdzX2RmLCBsaW5rID0gbmV3X2xpbmspDQpnbmV3c19kZlsxOjYsXQ0KYGBgDQoNCiMjIOmHjeaWsOWYl+ippuWPluW+l+aWsOiBnuWFp+WuuQ0KDQo+IOePvuWcqO+8jOacieS6huWOn+aWsOiBnueahOmAo+e1kO+8jOWwseWPr+S7peS+neaTmuS+hua6kO+8jOS4gOS4gOWOu+aJvuWwi+WFtuaWh+eroOauteiQveaJgOWcqOS9jee9ruOAgg0KDQotIGdldCBhdCBsZWFzdCAqKjEwMCBuZXdzIHJlcG9ydHMqKg0KLSDlvp7kuIrpnaLvvIzph53lsI1zb3VyY2XlgZrlh7p0YWJsZSgp5YaNc29ydOW+jO+8jOingOWvn+WPr+efpeacgOW4uOWHuuePvueahOacie+8mg0KLSAgKiBFVHRvZGF5DQotICAqIOiHqueUseaZguWgsQ0KLSAgKiB1ZG7oga/lkIgNCi0gICogVFZCUw0KLSAgKiDkuK3mmYINCi0gICogWWFob28NCi0g5pa85piv6Yed5bCN6YCZ5bm+5YCL5paw6IGe5L6G5rqQ5a2Y5Y+W5paH56ug44CCDQoNCg0KYGBge3J9DQojI3N0YXJ0IHBhcnNpbmcgcG9zdA0KI2NzcyBjb2xsZWN0aW9uDQpjc3NfY2hpbmF0aW1lcyA8LSAiYXJ0aWNsZSA+IHAiDQpjc3NfY2hpbmF0aW1lc19maXJzdHByZ3IgPC0gIi5waWN0ZXh0ID4gcCINCmNzc195YWhvbyA8LSAiYXJ0aWNsZSA+IGRpdiA+IHAiDQpjc3NfdWRuIDwtICIjc3RvcnlfYm9keV9jb250ZW50ID4gcCINCmNzc190dmJzIDwtICIjbmV3c19kZXRhaWxfZGl2Ig0KY3NzX2x0biA8LSAiZGl2LnRleHQgPiBwIg0KDQppIDwtIDENCnBvc3RzIDwtIE5VTEwNCg0KDQpmb3IobG5rIGluIGduZXdzX2RmJGxpbmspew0KICAgIHBvc3QgPC0gTkENCiAgICAjIGNzc190ZW1wIDwtIE5BDQogICAgDQogICAgIyBDYXNlcw0KICAgIGlmKGduZXdzX2RmJHNvdXJjZVtpXT09IkVUdG9kYXkg5paw6IGe6ZuyIikNCiAgICB7DQogICAgICAgIHBvc3RwcmdyIDwtIHJlYWRfaHRtbChsbmspICU+JQ0KICAgICAgICAgICBodG1sX25vZGVzKGNzcyA9IGNzc19FVHQpICU+JQ0KICAgICAgICAgICBodG1sX3RleHQoKQ0KICAgICAgICBwb3N0IDwtIHBhc3RlKHBvc3RwcmdyW2MoMzoobGVuZ3RoKHBvc3RwcmdyKS0yKSldLCBjb2xsYXBzZSA9ICIgIikNCiAgICB9DQogICAgZWxzZSBpZihnbmV3c19kZiRzb3VyY2VbaV09PSLkuK3mmYLpm7vlrZDloLEiKQ0KICAgIHsNCiAgICAgICAgI2NvbnRlbnQNCiAgICAgICAgcG9zdHByZ3IgPC0gcmVhZF9odG1sKGxuaykgJT4lDQogICAgICAgICAgIGh0bWxfbm9kZXMoY3NzID0gY3NzX2NoaW5hdGltZXMpICU+JQ0KICAgICAgICAgICBodG1sX3RleHQoKQ0KICAgICAgICBwb3N0IDwtIHBhc3RlKHBvc3RwcmdyLCBjb2xsYXBzZSA9ICIgIikNCiAgICAgICAgI3BpY3R1cmUgY2FwdGlvbg0KICAgICAgICBwb3N0cHJnciA8LSByZWFkX2h0bWwobG5rKSAlPiUNCiAgICAgICAgICAgaHRtbF9ub2Rlcyhjc3MgPSBjc3NfY2hpbmF0aW1lc19maXJzdHByZ3IpICU+JQ0KICAgICAgICAgICBodG1sX3RleHQoKQ0KICAgICAgICBwb3N0cHJnciA8LSBwb3N0cHJnciAlPiUNCiAgICAgICAgICAgIHN0cl9yZW1vdmVfYWxsKCJcclxuIikgJT4lDQogICAgICAgICAgICBzdHJfdHJpbSgpDQogICAgICAgIHBvc3QgPC0gcGFzdGUocG9zdHByZ3IsIHBvc3QpDQogICAgfQ0KICAgIGVsc2UgaWYoZ25ld3NfZGYkc291cmNlW2ldICU+JSBzdHJfZGV0ZWN0KCJZYWhvb+Wlh+aRqSIpKQ0KICAgIHsNCiAgICAgICAgcG9zdHByZ3IgPC0gcmVhZF9odG1sKGxuaykgJT4lDQogICAgICAgICAgIGh0bWxfbm9kZXMoY3NzID0gY3NzX3lhaG9vKSAlPiUNCiAgICAgICAgICAgaHRtbF90ZXh0KCkNCiAgICAgICAgcG9zdCA8LSBwYXN0ZShwb3N0cHJncltjKDE6bGVuZ3RoKHBvc3RwcmdyKSldLCBjb2xsYXBzZSA9ICIgIikNCiAgICB9DQogICAgZWxzZSBpZihnbmV3c19kZiRzb3VyY2VbaV09PSJ1ZG4g6IGv5ZCI5paw6IGe57ayIikNCiAgICB7DQogICAgICAgIHBvc3RwcmdyIDwtIHJlYWRfaHRtbChsbmspICU+JQ0KICAgICAgICAgICBodG1sX25vZGVzKGNzcyA9IGNzc191ZG4pICU+JQ0KICAgICAgICAgICBodG1sX3RleHQoKSAlPiUgDQogICAgICAgICAgICBzdHJfdHJpbSgpDQogICAgICAgIHBvc3QgPC0gcGFzdGUocG9zdHByZ3IsIGNvbGxhcHNlID0gIiAiKQ0KICAgIH0NCiAgICBlbHNlIGlmKGduZXdzX2RmJHNvdXJjZVtpXT09IuiHqueUseaZguWgsembu+WtkOWgsSIpDQogICAgew0KICAgICAgICBwb3N0cHJnciA8LSByZWFkX2h0bWwobG5rKSAlPiUNCiAgICAgICAgICAgaHRtbF9ub2Rlcyhjc3MgPSBjc3NfbHRuKSAlPiUNCiAgICAgICAgICAgaHRtbF90ZXh0KCkNCiAgICAgICAgcG9zdCA8LSBwYXN0ZShwb3N0cHJnciwgY29sbGFwc2UgPSAiICIpDQogICAgfQ0KICAgIGVsc2UgaWYoZ25ld3NfZGYkc291cmNlW2ldPT0iVFZCU+aWsOiBniIpDQogICAgew0KICAgICAgICBwb3N0cHJnciA8LSByZWFkX2h0bWwobG5rKSAlPiUNCiAgICAgICAgICAgaHRtbF9ub2Rlcyhjc3MgPSBjc3NfdHZicykgJT4lDQogICAgICAgICAgIGh0bWxfdGV4dCgpDQogICAgICAgIHBvc3RwcmdyIDwtIHBvc3RwcmdyICU+JQ0KICAgICAgICAgICAgc3RyX3JlbW92ZV9hbGwoIlxyXG4iKSAlPiUNCiAgICAgICAgICAgIHN0cl90cmltKCkgJT4lIA0KICAgICAgICAgICAgc3RyX3JlbW92ZV9hbGwoIuacgEhPVOipsemhjOWcqOmAme+8geaDs+i3n+S4iuaZguS6i++8jOW/q+m7nuaIkeWKoOWFpVRWQlPmlrDogZ5MSU5F5aW95Y+L77yBIikgJT4lIA0KICAgICAgICAgICAgc3RyX3JlbW92ZSgiMjAxOOWFqOWPsOmBuOaIsOmiqOmbsuOAgFRWQlPmnIDmlrDlpKfmlbjmk5rliIbmnpAiKSAlPiUgDQogICAgICAgICAgICBzdHJfcmVtb3ZlKCLnnIvkuI3liLDlvbHpn7Poq4vpu57pgJkiKQ0KICAgICAgICBwb3N0IDwtIHBhc3RlKHBvc3RwcmdyLCBjb2xsYXBzZSA9ICIgIikNCiAgICB9DQogICAgDQogICAgIyBwcmludChwYXN0ZTAoaSwgIi4uLi4uLiIsIHBvc3QpKQ0KICAgIHBvc3RzIDwtIGMocG9zdHMsIHBvc3QpDQogICAgaSA8LSBpICsgMQ0KICAgIA0KICAgIGlmKGklJTUgPT0wKXsNCiAgICAgICAgU3lzLnNsZWVwKHNhbXBsZSgxOjMsc2l6ZSA9IDEpKQ0KICAgIH0NCn0NCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBzZWUgdGhlIHJlc3VsdCBvZiBwb3N0cw0KIyBoZWFkKHBvc3RzKTt0YWlsKHBvc3RzKQ0KDQojIGNvbnZlcnQgTlVMTCBkYXRhIHRvIE5BDQpwb3N0c1twb3N0cyA9PSAiIl0gPC0gTkEgIyBwb3N0c1tpcy5uYShwb3N0cyldIDwtICIiDQoNCiMgY291bnQgcG9zdHMgd2l0aCBjb250ZW50DQpzdW0oIWlzLm5hKHBvc3RzKSkNCmxlbmd0aChwb3N0cykNCmBgYA0KDQojIyDlsIfmlofnq6DlkIjkvbXoh7NkYXRhZnJhbWXkuKblhLLlrZgNCg0KLSDpgJnpgorlj6rlj5blh7rnj77mrKHmlbjmnIDpq5jnmoTliY025YCL5L6G5rqQKOeiuuS/neespuWQiOS9nOalreimge+8njEwMOevh+eahOimgeaxginmlofnq6DvvIzlhbbppJjliZTpmaTjgIIo5Y2z5L+d55WZcG9zdHPpnZ5OQeeahOizh+aWmSkNCi0gU3RvcmUgbmV3cyBkYXRhIGFzIC5yZHMgKHppcOaqlOS4reWQq+aciWdvb2dsZU5ld3NfMTBfMjhfMTIucmRz5qqU5a2Y5pS+ZGYpDQoNCmBgYHtyfSANCiMjIEFkZCBwb3N0cyB0byBnbmV3c19kZg0KZ25ld3NfZGYgPC0gbXV0YXRlKGduZXdzX2RmLCBhcnRpY2xlID0gcG9zdHMpDQoNCiMgZmlsdGVyIG91dCBhcnRpY2xlIHdpdGggY29udGVudA0KZ25ld3NfZGYgPC0gZ25ld3NfZGZbIWlzLm5hKGduZXdzX2RmJGFydGljbGUpLF0NCmRpbShnbmV3c19kZikNCmduZXdzX2RmDQoNCiMjIFNhdmluZyBEYXRhRnJhbWUNCmZvcm1hdChjVCkNCiMgY1RfdGFnIDwtIHBhc3RlMChtb250aChjVCksZGF5KGNUKSwiXyIsaG91cihjVCksbWludXRlKGNUKSkNCmNUX3RhZyA8LSBwYXN0ZTAobW9udGgoY1QpLGRheShjVCkpDQpyZHNuYW1lIDwtIHBhc3RlKCJnb29nbGVOZXdzIixjVF90YWcsIGhvdXIoY1QpLCBzZXAgPSAiXyIpDQpyZHNuYW1lIDwtIHBhc3RlMChyZHNuYW1lLCIucmRzIikNCg0KDQojIHNhdmUgbmV3IGduZXdzX2RmIGFzIHJkcw0KcmRzbmFtZQ0Kc2F2ZVJEUyhnbmV3c19kZiwgcmRzbmFtZSkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMgbG9hZCBleGlzdGVkIGRmIGV4YW1wbGUNCiMgbG9hZCBvbGQgZGYNCmxvYWRlZF9nbmV3c19kZiA8LSByZWFkUkRTKCJnb29nbGVOZXdzXzEwXzI4XzEyLnJkcyIpDQoNCmRpbShsb2FkZWRfZ25ld3NfZGYpDQpsb2FkZWRfZ25ld3NfZGYNCmBgYA0KDQoNCg0KDQoNCmJ5IEl2YW4gQ2hlbg0K