作業ディレクトリの設定

setwd("/cloud/project")
install.packages(c("cleanNLP","udpipe","igraph","ggraph","ggplot2"))

コメント記入時の注意

スクリプト実行セルに全角スペースを入れるとエラーがでるので気をつけてください。

  • 半角スペース
x<-10 
y<-20
res_sum0 <- x + y
paste("Sum = ", res_sum0) #足し算の結果をプリントアウト出力
[1] "Sum =  30"
  • 全角スペース
x<-10
y<-20
res_sum0 <- x + y
paste("Sum = ", res_sum0) # 足し算の結果をプリントアウト出力
Error: unexpected invalid token in "paste("Sum = ", res_sum0) "

関数の作成

関数:複数処理を一括で呼び出す時に便利

教科書的な説明

戻り値なし関数(関数から戻る値はない)

  • 引数: x, y
  • 戻り値:なし
# Define a function
test_plus0 <- function(x = 0, y = 0) {
  res_sum0 <- x + y
  paste("Sum = ", res_sum0) #足し算の結果をプリントアウト出力
}

test_plus0の実行

test_plus0(10, 20)
[1] "Sum =  30"
#res0 <- test_plus0(10, 20)
#res0 + 10

戻り値あり関数:test_plus1関数を作成

  • 引数: x, y
  • 戻り値: res_sum1
# Define a function
test_plus1 <- function(x = 0, y = 0) {
  res_sum1 <- x + y
  return(res_sum1)
}

test_plus1の実行

# Run test_plus1
test_plus1(10, 20)
[1] 30
#res1 <- test_plus1(10, 20)
#res1 + 10

補足

return()関数をつけなくても戻り値ありの関数になる

  • test_plus2関数
# Define a function
test_plus2 <- function(x = 0, y = 0) {
  res_sum2 <- x + y
  print(res_sum2)
}

# Run test_plus2
test_plus2(10, 20)
[1] 30
#res2 <- test_plus2(10, 20)
#res2 + 10
  • test_plus3関数
# Define a function
test_plus3 <- function(x = 0, y = 0) {
  x + y
}

# Run test_plus3
test_plus3(10, 20)
[1] 30
#res3 <- test_plus3(10, 20)
#res3 + 10

テキスト処理

ライブラリの読み込み

library(httr)
library(rvest)

使用テキスト

# URL of the Mainichi Shinbun's article
url_ja <- "https://mainichi.jp/articles/20240924/k00/00m/040/226000c"

# Send a GET request with a user agent
response <- GET(url_ja, user_agent("Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15"))
page <- read_html(response)

本文を抽出

  • 抽出箇所: articledetail-body内のpタグ
# <section class='articledetail-body' id='articledetail-body'>
article_content_ja <- html_text(html_nodes(page, "section.articledetail-body p"), trim = TRUE) 
#article_content_ja[1]

テキスト処理: cleanNLP, UDPipe

cleanNLP

ライブラリの読み込み

library(cleanNLP)

日本語処理

cnlp_init_udpipe(model_name = "japanese")

アノテーション(形態素解析)

  • cnlp_annotate関数
anno_data <- cnlp_annotate(input = article_content_ja[3])

結果一部抽出

head(anno_data$token)
anno_data$token$token[1:10]

View関数

View(anno_data$token)

列名を抽出

  • upos: universal POS tags
  • xpos: treebank-specific POS tag
  • feats: additional morphological features
  • relation: Dependency Relation
colnames(anno_data$token)
 [1] "doc_id"        "sid"           "tid"           "token"        
 [5] "token_with_ws" "lemma"         "upos"          "xpos"         
 [9] "feats"         "tid_source"    "relation"     

結果の部分抽出(列名指定)

head(anno_data$token$token)
[1] "学費"     "は"       "法科大学" "院"       "を"       "除く"    
head(anno_data$token$lemma)
[1] "学費"     "は"       "法科大学" "院"       "を"       "除く"    

条件抽出

anno_data$token[anno_data$token$upos == "NOUN",]

複数条件抽出(その1)

anno_data$token[(anno_data$token$upos == "NUM"|anno_data$token$upos == "PUNCT"),]

全文

fulltext <- paste(article_content_ja, collapse = "")

fulltext_anno_data <- cnlp_annotate(input = fulltext)
tail(fulltext_anno_data$token)

複数条件抽出(その2)

%in% operator

search_terms = c("大学", "学費", "値上げ")
fulltext_anno_data$token[fulltext_anno_data$token$token%in%search_terms,]

頻度集計

freqBycnlp<-table(fulltext_anno_data$token$token)
head(sort(freqBycnlp, decreasing=TRUE))

の 。 は に を た 
24 16 13 12 12 11 
#sort(freqBycnlp, decreasing=TRUE)[1:20]

UDPipe Natural Language Processing

ライブラリの読み込み

library(udpipe)

Annotation with UDPipe

sample_en <- udpipe("Tuition for doctoral programs will remain unchanged.", "english")

View関数

View(sample_en)

列名を抽出

colnames(sample_en)
 [1] "doc_id"        "paragraph_id"  "sentence_id"   "sentence"      "start"        
 [6] "end"           "term_id"       "token_id"      "token"         "lemma"        
[11] "upos"          "xpos"          "feats"         "head_token_id" "dep_rel"      
[16] "deps"          "misc"         

View

View(sample[c("token_id", "token", "head_token_id")])
Error in View : object of type 'closure' is not subsettable

係受け解析の視覚化

関数ファイルの読み込み

source("func_plot_annotation.R")

視覚化

plot_annotation(sample_en, size = 4)

日本語処理

sample_ja <- udpipe("博士課程の学費は据え置いた。", "japanese")
plot_annotation(sample_ja, size = 4)

Tree structure

LS0tCnRpdGxlOiAiTGVjMDM6IOmWouaVsOOBruS9nOaIkCwg44OG44Kt44K544OI5Yem55CGIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCiMjIOS9nOalreODh+OCo+ODrOOCr+ODiOODquOBruioreWumgpgYGB7cn0Kc2V0d2QoIi9jbG91ZC9wcm9qZWN0IikKYGBgCgpgYGB7ciwgZXZhbCA9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoYygiY2xlYW5OTFAiLCJ1ZHBpcGUiLCJpZ3JhcGgiLCJnZ3JhcGgiLCJnZ3Bsb3QyIikpCmBgYAoKIyMg44Kz44Oh44Oz44OI6KiY5YWl5pmC44Gu5rOo5oSPCiMjIyDjgrnjgq/jg6rjg5fjg4jlrp/ooYzjgrvjg6vjgavlhajop5Ljgrnjg5rjg7zjgrnjgpLlhaXjgozjgovjgajjgqjjg6njg7zjgYzjgafjgovjga7jgafmsJfjgpLjgaTjgZHjgabjgY/jgaDjgZXjgYTjgIIKLSDljYrop5Ljgrnjg5rjg7zjgrkKYGBge3J9Cng8LTEwIAp5PC0yMApyZXNfc3VtMCA8LSB4ICsgeQpwYXN0ZSgiU3VtID0gIiwgcmVzX3N1bTApICPotrPjgZfnrpfjga7ntZDmnpzjgpLjg5fjg6rjg7Pjg4jjgqLjgqbjg4jlh7rlipsKYGBgCgotIOWFqOinkuOCueODmuODvOOCuQpgYGB7cn0KeDwtMTAKeTwtMjAKcmVzX3N1bTAgPC0geCArIHkKcGFzdGUoIlN1bSA9ICIsIHJlc19zdW0wKeOAgCPjgIDotrPjgZfnrpfjga7ntZDmnpzjgpLjg5fjg6rjg7Pjg4jjgqLjgqbjg4jlh7rlipsKYGBgCgoKIyDplqLmlbDjga7kvZzmiJAKIyMg6Zai5pWw77ya6KSH5pWw5Yem55CG44KS5LiA5ous44Gn5ZG844Gz5Ye644GZ5pmC44Gr5L6/5YipCi0gPGEgaHJlZj0iaHR0cHM6Ly9zdGF0cy5iaW9wYXB5cnVzLmpwL3IvYmFzaWMvZnVuY3Rpb24uaHRtbCIgdGFyZ2V0PSJfYmxhbmsiPuWPguiAg+izh+aWmTogYmlvc3RhdGlzdGljcyDplqLmlbDjga7kvZzjgormlrk8L2E+CgojIyDmlZnnp5Hmm7jnmoTjgaroqqzmmI4KIyMjIOaIu+OCiuWApOOBquOBl+mWouaVsO+8iOmWouaVsOOBi+OCieaIu+OCi+WApOOBr+OBquOBhO+8iQotIOW8leaVsDogeCwgeQotIOaIu+OCiuWApDrjgarjgZcKYGBge3J9CiMgRGVmaW5lIGEgZnVuY3Rpb24KdGVzdF9wbHVzMCA8LSBmdW5jdGlvbih4ID0gMCwgeSA9IDApIHsKICByZXNfc3VtMCA8LSB4ICsgeQogIHBhc3RlKCJTdW0gPSAiLCByZXNfc3VtMCkgI+i2s+OBl+eul+OBrue1kOaenOOCkuODl+ODquODs+ODiOOCouOCpuODiOWHuuWKmwp9CmBgYAoKIyMjIHRlc3RfcGx1czDjga7lrp/ooYwKYGBge3J9CnRlc3RfcGx1czAoMTAsIDIwKQoKI3JlczAgPC0gdGVzdF9wbHVzMCgxMCwgMjApCiNyZXMwICsgMTAKYGBgCgojIyMg5oi744KK5YCk44GC44KK6Zai5pWw77yadGVzdF9wbHVzMemWouaVsOOCkuS9nOaIkAotIOW8leaVsDogeCwgeQotIOaIu+OCiuWApDogcmVzX3N1bTEKYGBge3J9CiMgRGVmaW5lIGEgZnVuY3Rpb24KdGVzdF9wbHVzMSA8LSBmdW5jdGlvbih4ID0gMCwgeSA9IDApIHsKICByZXNfc3VtMSA8LSB4ICsgeQogIHJldHVybihyZXNfc3VtMSkKfQpgYGAKCiMjIyB0ZXN0X3BsdXMx44Gu5a6f6KGMCmBgYHtyfQojIFJ1biB0ZXN0X3BsdXMxCnRlc3RfcGx1czEoMTAsIDIwKQoKI3JlczEgPC0gdGVzdF9wbHVzMSgxMCwgMjApCiNyZXMxICsgMTAKYGBgCgojIyDoo5zotrMKIyMjIHJldHVybigp6Zai5pWw44KS44Gk44GR44Gq44GP44Gm44KC5oi744KK5YCk44GC44KK44Gu6Zai5pWw44Gr44Gq44KLCi0gdGVzdF9wbHVzMumWouaVsApgYGB7cn0KIyBEZWZpbmUgYSBmdW5jdGlvbgp0ZXN0X3BsdXMyIDwtIGZ1bmN0aW9uKHggPSAwLCB5ID0gMCkgewogIHJlc19zdW0yIDwtIHggKyB5CiAgcHJpbnQocmVzX3N1bTIpCn0KCiMgUnVuIHRlc3RfcGx1czIKdGVzdF9wbHVzMigxMCwgMjApCgojcmVzMiA8LSB0ZXN0X3BsdXMyKDEwLCAyMCkKI3JlczIgKyAxMApgYGAKCi0gdGVzdF9wbHVzM+mWouaVsApgYGB7cn0KIyBEZWZpbmUgYSBmdW5jdGlvbgp0ZXN0X3BsdXMzIDwtIGZ1bmN0aW9uKHggPSAwLCB5ID0gMCkgewogIHggKyB5Cn0KCiMgUnVuIHRlc3RfcGx1czMKdGVzdF9wbHVzMygxMCwgMjApCgojcmVzMyA8LSB0ZXN0X3BsdXMzKDEwLCAyMCkKI3JlczMgKyAxMApgYGAKCiMjIOODhuOCreOCueODiOWHpueQhgojIyMg44Op44Kk44OW44Op44Oq44Gu6Kqt44G/6L6844G/CmBgYHtyfQpsaWJyYXJ5KGh0dHIpCmxpYnJhcnkocnZlc3QpCmBgYAoKIyMjIOS9v+eUqOODhuOCreOCueODiAotIDxhIGhyZWY9Imh0dHBzOi8vbWFpbmljaGkuanAvYXJ0aWNsZXMvMjAyNDA5MjQvazAwLzAwbS8wNDAvMjI2MDAwYyIgdGFyZ2V0PSJfYmxhbmsiPiAi44CM5p2x5Lqs5aSn44CB5p2l5pil5YWl5a2m44GL44KJ5o6I5qWt5paZMuWJsuWApOS4iuOBkuOCkuato+W8j+axuuWumuOAgOS7luWkp+WtpuOBq+W9semfv+OCguOAje+8iOavjuaXpeaWsOiBniAyMDI0LzkvMjTvvIkiPC9hPgpgYGB7cn0KIyBVUkwgb2YgdGhlIE1haW5pY2hpIFNoaW5idW4ncyBhcnRpY2xlCnVybF9qYSA8LSAiaHR0cHM6Ly9tYWluaWNoaS5qcC9hcnRpY2xlcy8yMDI0MDkyNC9rMDAvMDBtLzA0MC8yMjYwMDBjIgoKIyBTZW5kIGEgR0VUIHJlcXVlc3Qgd2l0aCBhIHVzZXIgYWdlbnQKcmVzcG9uc2UgPC0gR0VUKHVybF9qYSwgdXNlcl9hZ2VudCgiTW96aWxsYS81LjAgKE1hY2ludG9zaDsgSW50ZWwgTWFjIE9TIFggMTRfNykgQXBwbGVXZWJLaXQvNjA1LjEuMTUgKEtIVE1MLCBsaWtlIEdlY2tvKSBWZXJzaW9uLzE4LjAgU2FmYXJpLzYwNS4xLjE1IikpCnBhZ2UgPC0gcmVhZF9odG1sKHJlc3BvbnNlKQpgYGAKCiMjIyDmnKzmlofjgpLmir3lh7oKLSDmir3lh7rnrofmiYA6IGFydGljbGVkZXRhaWwtYm9keeWGheOBrnDjgr/jgrAKYGBge3J9CiMgPHNlY3Rpb24gY2xhc3M9J2FydGljbGVkZXRhaWwtYm9keScgaWQ9J2FydGljbGVkZXRhaWwtYm9keSc+CmFydGljbGVfY29udGVudF9qYSA8LSBodG1sX3RleHQoaHRtbF9ub2RlcyhwYWdlLCAic2VjdGlvbi5hcnRpY2xlZGV0YWlsLWJvZHkgcCIpLCB0cmltID0gVFJVRSkgCiNhcnRpY2xlX2NvbnRlbnRfamFbMV0KYGBgCgojIOODhuOCreOCueODiOWHpueQhjogY2xlYW5OTFAsIFVEUGlwZQojIyMg5Y+C6ICD6LOH5paZCi0gPGEgaHJlZj0iaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3VkcGlwZS92aWduZXR0ZXMvdWRwaXBlLWFubm90YXRpb24uaHRtbCIgdGFyZ2V0PSJfYmxhbmsiPlVEUGlwZSBOYXR1cmFsIExhbmd1YWdlIFByb2Nlc3Npbmc8L2E+Ci0gPGEgaHJlZj0iaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3VkcGlwZS92aWduZXR0ZXMvdWRwaXBlLWFubm90YXRpb24uaHRtbCIgdGFyZ2V0PSJfYmxhbmsiPlVEUGlwZSBOYXR1cmFsIExhbmd1YWdlIFByb2Nlc3Npbmc8L2E+Ci0gPGEgaHJlZj0iaHR0cHM6Ly91bml2ZXJzYWxkZXBlbmRlbmNpZXMub3JnLyIgdGFyZ2V0PSJfYmxhbmsiPlVuaXZlcnNhbCBEZXBlbmRlbmNpZXM8L2E+Ci0gPGEgaHJlZj0iaHR0cDovL2thbmppLnppbmJ1bi5reW90by11LmFjLmpwL355YXN1b2thL3B1YmxpY2F0aW9ucy8yMDIxLTA2LTIyLnBkZiIgdGFyZ2V0PSJfYmxhbmsiPuWuieWyoeWtneS4gCgyMDIxKeOAjOS4lueVjOOBrlVuaXZlcnNhbCBEZXBlbmRlbmNpZXPjgajkv4Ljgorlj5fjgZHop6PmnpDjg4Tjg7zjg6vnvqTjgI3jgI7nrKwz5ZueVW5pdmVyc2FsIERlcGVuZGVuY2llc+WFrOmWi+eglOeptuS8muOAjzwvYT4KCiMgY2xlYW5OTFAKIyMjIOODqeOCpOODluODqeODquOBruiqreOBv+i+vOOBvwpgYGB7cn0KbGlicmFyeShjbGVhbk5MUCkKYGBgCgojIyDml6XmnKzoqp7lh6bnkIYKYGBge3J9CmNubHBfaW5pdF91ZHBpcGUobW9kZWxfbmFtZSA9ICJqYXBhbmVzZSIpCmBgYAoKIyMg44Ki44OO44OG44O844K344On44Oz77yI5b2i5oWL57Sg6Kej5p6Q77yJCi0gY25scF9hbm5vdGF0ZemWouaVsApgYGB7cn0KYW5ub19kYXRhIDwtIGNubHBfYW5ub3RhdGUoaW5wdXQgPSBhcnRpY2xlX2NvbnRlbnRfamFbM10pCmBgYAoKIyMjIOe1kOaenOS4gOmDqOaKveWHugpgYGB7ciwgZXZhbD1GQUxTRX0KaGVhZChhbm5vX2RhdGEkdG9rZW4pCmFubm9fZGF0YSR0b2tlbiR0b2tlblsxOjEwXQpgYGAKIyMjIFZpZXfplqLmlbAKYGBge3IsIGV2YWwgPSBGQUxTRX0KVmlldyhhbm5vX2RhdGEkdG9rZW4pCmBgYAoKIyMg5YiX5ZCN44KS5oq95Ye6Ci0gdXBvczogdW5pdmVyc2FsIFBPUyB0YWdzCi0geHBvczogdHJlZWJhbmstc3BlY2lmaWMgUE9TIHRhZwotIGZlYXRzOiBhZGRpdGlvbmFsIG1vcnBob2xvZ2ljYWwgZmVhdHVyZXMgCi0gcmVsYXRpb246IERlcGVuZGVuY3kgUmVsYXRpb24KYGBge3J9CmNvbG5hbWVzKGFubm9fZGF0YSR0b2tlbikKYGBgCgojIyDntZDmnpzjga7pg6jliIbmir3lh7rvvIjliJflkI3mjIflrprvvIkKYGBge3J9CmhlYWQoYW5ub19kYXRhJHRva2VuJHRva2VuKQpoZWFkKGFubm9fZGF0YSR0b2tlbiRsZW1tYSkKYGBgCgojIyDmnaHku7bmir3lh7oKYGBge3J9CmFubm9fZGF0YSR0b2tlblthbm5vX2RhdGEkdG9rZW4kdXBvcyA9PSAiTk9VTiIsXQpgYGAKIyMg6KSH5pWw5p2h5Lu25oq95Ye6KOOBneOBru+8kSkKYGBge3J9CmFubm9fZGF0YSR0b2tlblsoYW5ub19kYXRhJHRva2VuJHVwb3MgPT0gIk5VTSJ8YW5ub19kYXRhJHRva2VuJHVwb3MgPT0gIlBVTkNUIiksXQpgYGAKCiMjIyDlhajmlocKYGBge3J9CmZ1bGx0ZXh0IDwtIHBhc3RlKGFydGljbGVfY29udGVudF9qYSwgY29sbGFwc2UgPSAiIikKCmZ1bGx0ZXh0X2Fubm9fZGF0YSA8LSBjbmxwX2Fubm90YXRlKGlucHV0ID0gZnVsbHRleHQpCnRhaWwoZnVsbHRleHRfYW5ub19kYXRhJHRva2VuKQpgYGAKCiMjIOikh+aVsOadoeS7tuaKveWHuijjgZ3jga7vvJIpCiMjIyAlaW4lIG9wZXJhdG9yCmBgYHtyfQpzZWFyY2hfdGVybXMgPSBjKCLlpKflraYiLCAi5a2m6LK7IiwgIuWApOS4iuOBkiIpCmZ1bGx0ZXh0X2Fubm9fZGF0YSR0b2tlbltmdWxsdGV4dF9hbm5vX2RhdGEkdG9rZW4kdG9rZW4laW4lc2VhcmNoX3Rlcm1zLF0KYGBgCgojIyDpoLvluqbpm4boqIggCmBgYHtyfQpmcmVxQnljbmxwPC10YWJsZShmdWxsdGV4dF9hbm5vX2RhdGEkdG9rZW4kdG9rZW4pCmhlYWQoc29ydChmcmVxQnljbmxwLCBkZWNyZWFzaW5nPVRSVUUpKQojc29ydChmcmVxQnljbmxwLCBkZWNyZWFzaW5nPVRSVUUpWzE6MjBdCmBgYAoKIyA8YSBocmVmPSJodHRwczovL2Jub3NhYy5naXRodWIuaW8vdWRwaXBlL2VuL2luZGV4Lmh0bWwiIHRhcmdldD0iX2JsYW5rIj5VRFBpcGUgTmF0dXJhbCBMYW5ndWFnZSBQcm9jZXNzaW5nPC9hPgoKIyMjIOODqeOCpOODluODqeODquOBruiqreOBv+i+vOOBvwpgYGB7cn0KbGlicmFyeSh1ZHBpcGUpCmBgYAoKIyMjIEFubm90YXRpb24gd2l0aCBVRFBpcGUKYGBge3J9CnNhbXBsZV9lbiA8LSB1ZHBpcGUoIlR1aXRpb24gZm9yIGRvY3RvcmFsIHByb2dyYW1zIHdpbGwgcmVtYWluIHVuY2hhbmdlZC4iLCAiZW5nbGlzaCIpCmBgYAojIyMgVmlld+mWouaVsApgYGB7ciwgZXZhbCA9IEZBTFNFfQpWaWV3KHNhbXBsZV9lbikKYGBgCgojIyMg5YiX5ZCN44KS5oq95Ye6CmBgYHtyfQpjb2xuYW1lcyhzYW1wbGVfZW4pCmBgYAojIyMgVmlldwpgYGB7cn0KVmlldyhzYW1wbGVfZW5bYygidG9rZW5faWQiLCAidG9rZW4iLCAiaGVhZF90b2tlbl9pZCIpXSkKYGBgCgojIOS/guWPl+OBkeino+aekOOBruimluimmuWMlgotIHJlZi4gPGEgaHJlZj0iaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAxOS8wNy9kZXBlbmRlbmN5LXBhcnNpbmctd2l0aC11ZHBpcGUvIiB0YXJnZXQ9Il9ibGFuayI+ZGVwZW5kZW5jeSBwYXJzaW5nIHdpdGggdWRwaXBlPC9hPgoKLSA8YSBocmVmPSJodHRwczovL2dpdGh1Yi5jb20vYm5vc2FjL3RleHRwbG90IiB0YXJnZXQ9Il9ibGFuayI+dGV4dHBsb3Q8L2E+CgojIyMg6Zai5pWw44OV44Kh44Kk44Or44Gu6Kqt44G/6L6844G/CmBgYHtyfQpzb3VyY2UoImZ1bmNfcGxvdF9hbm5vdGF0aW9uLlIiKQpgYGAKCiMjIOimluimmuWMlgpgYGB7cn0KcGxvdF9hbm5vdGF0aW9uKHNhbXBsZV9lbiwgc2l6ZSA9IDQpCmBgYAoKIyMjIOaXpeacrOiqnuWHpueQhgpgYGB7cn0Kc2FtcGxlX2phIDwtIHVkcGlwZSgi5Y2a5aOr6Kqy56iL44Gu5a2m6LK744Gv5o2u44GI572u44GE44Gf44CCIiwgImphcGFuZXNlIikKcGxvdF9hbm5vdGF0aW9uKHNhbXBsZV9qYSwgc2l6ZSA9IDQpCmBgYApgYGB7cn0KVmlldyhzYW1wbGVfamFbYygidG9rZW5faWQiLCAidG9rZW4iLCAiaGVhZF90b2tlbl9pZCIpXSkKYGBgCgojIyMjIFRyZWUgc3RydWN0dXJlCjxpbWcgc3JjPSJsZWMwM190cmVlLnBuZyIgd2lkdGg9IjUwMCI+Cgo=