Veri gazeteciliği süreçlerinde sayılar kadar metinler de niceliksel analizin bir parçası olarak kullanılıyor.Çünkü metin de ürettiğimiz, kullandığımız ve paylaştığımız bir veri. Özellikle siyasilerin konuşmaları, bir twitter etiketi içinde paylaşılan tweetler veyahut devletin bir kurumu tarafından yayınlanan bir rapor gazeteciler tarafından metin madenciliği sayesinde habercilik amaçları için kullanılabiliyor. Bu saydıklarıma örnek olarak Trump’ın sinirli tweetlerini Android cihazından paylaştığını, Cumhuriyetçilerin ve Demokratları söylemleri gösterebiliriz.

Metin madenciliğini veri madenciliğinin bir alt başlığı olarak tanımlamak yanlış olmaz. Yapılan işlem normalde analiz edilemeyen yapılandırılmamış büyük metin bloglarınının belirli komputasyonel yöntemlerle analiz edilmesi, anlamlandırılması ve metin verisinden belirli temalar, trendler, ilişkiler elde edilmesidir.

Metin madenciliği geniş bir alan. Python ve R gibi araçlarla gelişmiş analizler ve çalışmalar yapılabiliyor. Daha çok metin madenciliğine giriş niteliğine sahip olacak bu yazıda R ekosisteminde metin verileri nasıl analiz edilebileceğini ve görselleştirileceğini anlatacağım.

R’da farklı formatlardeki metinler analiz edilebiliyor. Bu ister html, ister word veya pdf formatında olsun R’ın sahip olduğu kütüphaneler bu analizi mümkün kılıyor. Bu uygulamada 3 ayrı formattaki metin verilerini anlamlandırmaya çalışağız: tweet verisi, bir sayfadaki html verisi ve bir düz metin (txt) dosyasındaki metin verileri. R ile bu metinlerde sıklıkla kullanılan ifadeleri ortaya çıkaracağız.

Hazırlık

#paketleri aktif hale getirmeden önce lütfen yükleyin (install.packages("paket ismi"))

library("tidytext") #metin verilerini bu paketle analiz edeceğiz
library("tidyverse") #dplyr, ggplot2, tidyr gibi paketleri barındırıyor
library("rtweet") #tweetleri çekmek için kullanacağız.
library("rvest")
library("wordcloud2") #kelime bulutu görseli için
library("stopwords")

Düz Metin Verisisini R’a aktaralım

nh <- read_lines("C:/Users/Sadettin/Desktop/nh.txt")
head(nh)
[1] "Haydarpaşa garında"                                               
[2] "1941 baharında"                                                   
[3] "        saat on beş."                                             
[4] "Merdivenlerin üstünde güneş"                                      
[5] "                                            yorgunluk"            
[6] "                                                        ve telaş."
tail(nh)
[1] "Ali kımıldamadı."                       "Ali cevap vermedi Recep’e."            
[3] "Tuttu delikanlıyı Recep"                "                    çevirdi arka üstü."
[5] "Ali’nin başı düştü."                    "Ali çoktan ölmüştü."                   
nh_tidy <- paste0(nh, collapse = " ")

nh_tablo <- tibble(nh_tidy)

str(nh_tablo)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   1 obs. of  1 variable:
 $ nh_tidy: chr "Haydarpaşa garında 1941 baharında         saat on beş. Merdivenlerin üstünde güneş                             "| __truncated__

Nazım Hikmet’in Şiiri aşağıda analiz edilecektir.

1. Trump’ın Tweetleri

trumptweets<-readRDS("C:/Users/Sadettin/Rstats/metin-maden/trumptweets.rds")

Sadece tweet metinlerini filtreleyerek bir kenara alalım

trump <- trumptweets %>% select(5)

head(trump)

Öncelikle tweetlerde kullanılan herhangi kesme işareti ve linkleri kaldırdık. Hemen sonrasında bu veri tweet verisi olduğu için token olarak “tweets” argumanını seçiyoruz. Bu sayede tweetlerdeki etiket ve @ ifadelerini kaldırmamış oluyoruz. Hemen sonrasında unnest_token komutu ile uzun tweetleri her satır bir kelimeye gelecek şekilde parçalara ayırıyoruz. Ayırdığımız kelimeleri de gereksiz ifadelerden kurtarıyoruz. Son adım ise bir reqex komutu sadece kelimelerden oluşan ifadeleri filtrelememiziz sağlıyor.


trumptidy <- trump %>%
  filter(!str_detect(text, '^"')) %>%
  mutate(text = str_replace_all(text, "https://t.co/[A-Za-z\\d]+|&amp;", ""))%>%
  unnest_tokens(word, text) %>%
  filter(!word %in% stop_words$word,
         str_detect(word, "[a-z]"))
head(trumptidy)

Sıklıkla kullanılan kelimlere göz atalım

trumptidy %>% count(word, sort = TRUE)

Tekrar oranına göz atalım

tekrar_oran <- trumptidy %>% count(word, sort = TRUE) %>%  mutate(tekrar = n/sum(n)*100)
tekrar_oran

N-gram ile sık kullanılan söz öbeklerini inceleyelim

Bir diğer analiz ise arka arkaya gelen birden fazla ifadenin ne sıklıkla kullanıldığını bulabiliriz. Buna metin madenciliğinde n-gram yöntemi adı veriliyor.

Bu kısımda en önemli nokta unnest_tokens komutu içinde tokeni ngrams olarak değiştiriyoruz ve n’i 2’ye sabitliyoruz çünkü birbirini takip eden iki ayrı ifadeden oluşan söz öbeklerini elde etmek istiyoruz. Eğer dilersek bu değeri 3’e çıkarıp daha fazla öbekten oluşan ifadelere de ulaşabiliriz. Bu analiz yöntemi tweetlerde, siyasilerin konuşmalarında, basın açıklamalarında, kitaplarda vb. metinlerde tekrar eden metinsel bir örüntü yakalamamızı sağlıyor.


trump_bigrams <- trump %>%
  filter(!str_detect(text, '^"')) %>%
  mutate(text = str_replace_all(text, "https://t.co/[A-Za-z\\d]+|&amp;", "")) %>%
  unnest_tokens(bigram, text, token = "ngrams", n = 2)
head(trump_bigrams)

Hemen count() komutu ile sıklıkla kullanılan ifadelere göz atalım

trump_bigrams %>% count(bigram, sort = TRUE)

Görüldüğü üzere iki ayrı kelimenin birlikte kullanılma sayısı tek bir kelimeye göre daha az. Şimdi bu ifadelerdeki gereksiz kelimeleri (stopwords) veri setinden def edelim. Bunun için öncekinden daha karmaşık bir yöntem kullancacağız. Öncekine nazaran iki ifadeden oluşan ilk sütunu her biri bir kelimeden oluşacak şekilde ikiye ayırıp her sütunu ayrı ayrı temizleyeceğiz. Veri temizleme işlemi sonrası sütunları birleştirip gereksiz ifadelerden arınmış bigram sutunun tekrar hesaplayacağız.

O halde öncelikle bigram sütununu ikiye ayıralım

trump_bigrams_tidy <-  trump_bigrams %>%
  separate(bigram, c("word1", "word2"), sep = " ") %>% #**ayırma**
  filter(!word1 %in% stop_words$word) %>% #temizleme aşaması 1
  filter(!word2 %in% stop_words$word) %>% #temizleme aşaması 2 
  unite(bigram, word1, word2, sep = " ")%>% #birleştirme
  count(bigram, sort = TRUE)

trump_bigrams_tidy

Nazım Hikmet’in Şiirini İnceleyelim

nh_veri <- nh_tablo %>%
  filter(!str_detect(nh_tidy, '^"')) %>%
  unnest_tokens(word, nh_tidy) %>%
  filter(!word %in% stopwords(language = "tr",source ="stopwords-iso"))

nh_veri
nh_veri %>% count(word, sort = TRUE)

Metin Verilerinin Görselleştirilmesi

Elimizdeki metin verilerileri en basit haliyle çubuk grafik veyahut kelime bulutu olarak görselleştirebiliriz.

ggplot2 temayı oluşturalım

Her iki veri setinde de en çok kullanılan 20 ifadeyi filtreleyelim.

trump_bigrams_yeni<- trump_bigrams_tidy %>% 
  filter(!bigram %in% str_detect("president")) %>% 
  top_n(20,n)
Error in type(pattern) : argument "pattern" is missing, with no default

Görselleştirme

renk <- c("söz_öbek"="",
          "kelime"="")


trump_tidy_yeni %>% ggplot(aes(fct_reorder(word, n),n, fill ="red"))+
  geom_col()+
  coord_flip()+
  geom_text(aes(x = word, y = n,label = n),check_overlap = TRUE, hjust = -0.2,size = 3.7,color= "gray25")+
  labs(x="",y="",
       title = "Trump'ın Sık Kullandığı İfadeler",
       subtitle = "Son 3 bin Tweet Analize Dahil Edilmiştir.",
       caption = "@demirelsadettin")+theme_custom2()

Söz Öbeklerş

values ="#3182bd"

trump_bigrams_yeni %>% ggplot(aes(fct_reorder(bigram, n),n, fill =values))+
  geom_col()+
  coord_flip()+ scale_fill_manual(values = "#3182bd")+
  geom_text(aes(x = bigram, y = n,label = n),check_overlap = TRUE, hjust = -0.2,size = 3.8,color= "gray25")+
  labs(x="",y="",
       title = "Trump'ın Sık Kullandığı Söz Öbekleri",
       subtitle = "Son 3 bin Tweet Analize Dahil Edilmiştir.",
       caption = "@demirelsadettin")+theme_custom2()

Wordcloud2 paketi ile gelişmiş kelime bulutu

wordcloud2(data = trump_tidy_count,color = "random-light", backgroundColor = "grey25",size =0.6)

Daha hoş bir kelime bulutu oluşturalım

wordcloud2(data = trump_tidy_count,minRotation = -pi/6, maxRotation = -pi/6,
  rotateRatio = 1, size = 0.7)
trump_wordcloud <- trump_bigrams_tidy %>% filter(!bigram %in% c("president realdonaldtrump","president trump"))

wordcloud2(data = trump_wordcloud,minRotation = -pi/6, maxRotation = -pi/6,
  rotateRatio = 1, size = 0.7)
LS0tDQp0aXRsZTogIlIgZWtvc2lzdGVtaW5kZSBtZXRpbiBtYWRlbmNpbGnEn2kgbmFzxLFsIHlhcMSxbMSxcj8iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpWZXJpIGdhemV0ZWNpbGnEn2kgc8O8cmXDp2xlcmluZGUgc2F5xLFsYXIga2FkYXIgbWV0aW5sZXIgZGUgbmljZWxpa3NlbCBhbmFsaXppbiBiaXIgcGFyw6dhc8SxIG9sYXJhayBrdWxsYW7EsWzEsXlvci7Dh8O8bmvDvCBtZXRpbiBkZSDDvHJldHRpxJ9pbWl6LCBrdWxsYW5kxLHEn8SxbcSxeiB2ZSBwYXlsYcWfdMSxxJ/EsW3EsXogYmlyIHZlcmkuIMOWemVsbGlrbGUgc2l5YXNpbGVyaW4ga29udcWfbWFsYXLEsSwgYmlyIHR3aXR0ZXIgZXRpa2V0aSBpw6dpbmRlIHBheWxhxZ/EsWxhbiB0d2VldGxlciB2ZXlhaHV0IGRldmxldGluIGJpciBrdXJ1bXUgdGFyYWbEsW5kYW4geWF5xLFubGFuYW4gYmlyIHJhcG9yIGdhemV0ZWNpbGVyIHRhcmFmxLFuZGFuIG1ldGluIG1hZGVuY2lsacSfaSBzYXllc2luZGUgaGFiZXJjaWxpayBhbWHDp2xhcsSxIGnDp2luIGt1bGxhbsSxbGFiaWxpeW9yLiBCdSBzYXlkxLFrbGFyxLFtYSDDtnJuZWsgb2xhcmFrIFRydW1wJ8SxbiBzaW5pcmxpIHR3ZWV0bGVyaW5pIEFuZHJvaWQgY2loYXrEsW5kYW4gcGF5bGHFn3TEscSfxLFuxLEsIEN1bWh1cml5ZXTDp2lsZXJpbiB2ZSBEZW1va3JhdGxhcsSxIHPDtnlsZW1sZXJpIGfDtnN0ZXJlYmlsaXJpei4gDQoNCk1ldGluIG1hZGVuY2lsacSfaW5pIHZlcmkgbWFkZW5jaWxpxJ9pbmluIGJpciBhbHQgYmHFn2zEscSfxLEgb2xhcmFrIHRhbsSxbWxhbWFrIHlhbmzEscWfIG9sbWF6LiBZYXDEsWxhbiBpxZ9sZW0gbm9ybWFsZGUgYW5hbGl6IGVkaWxlbWV5ZW4geWFwxLFsYW5kxLFyxLFsbWFtxLHFnyBiw7x5w7xrIG1ldGluIGJsb2dsYXLEsW7EsW7EsW4gYmVsaXJsaSBrb21wdXRhc3lvbmVsIHnDtm50ZW1sZXJsZSBhbmFsaXogZWRpbG1lc2ksIGFubGFtbGFuZMSxcsSxbG1hc8SxIHZlIG1ldGluIHZlcmlzaW5kZW4gYmVsaXJsaSB0ZW1hbGFyLCB0cmVuZGxlciwgaWxpxZ9raWxlciBlbGRlIGVkaWxtZXNpZGlyLg0KDQpNZXRpbiBtYWRlbmNpbGnEn2kgZ2VuacWfIGJpciBhbGFuLiBQeXRob24gdmUgUiBnaWJpIGFyYcOnbGFybGEgZ2VsacWfbWnFnyBhbmFsaXpsZXIgdmUgw6dhbMSxxZ9tYWxhciB5YXDEsWxhYmlsaXlvci4gRGFoYSDDp29rIG1ldGluIG1hZGVuY2lsacSfaW5lIGdpcmnFnyBuaXRlbGnEn2luZSBzYWhpcCBvbGFjYWsgYnUgeWF6xLFkYSBSIGVrb3Npc3RlbWluZGUgbWV0aW4gdmVyaWxlcmkgbmFzxLFsIGFuYWxpeiBlZGlsZWJpbGVjZcSfaW5pIHZlIGfDtnJzZWxsZcWfdGlyaWxlY2XEn2luaSBhbmxhdGFjYcSfxLFtLg0KDQpSJ2RhIGZhcmtsxLEgZm9ybWF0bGFyZGVraSBtZXRpbmxlciBhbmFsaXogZWRpbGViaWxpeW9yLiBCdSBpc3RlciBodG1sLCBpc3RlciB3b3JkIHZleWEgcGRmIGZvcm1hdMSxbmRhIG9sc3VuIFInxLFuIHNhaGlwIG9sZHXEn3Uga8O8dMO8cGhhbmVsZXIgYnUgYW5hbGl6aSBtw7xta8O8biBrxLFsxLF5b3IuIEJ1IHV5Z3VsYW1hZGEgMyBheXLEsSBmb3JtYXR0YWtpIG1ldGluIHZlcmlsZXJpbmkgYW5sYW1sYW5kxLFybWF5YSDDp2FsxLHFn2HEn8SxejogdHdlZXQgdmVyaXNpLCBiaXIgc2F5ZmFkYWtpIGh0bWwgdmVyaXNpIHZlIGJpciBkw7x6IG1ldGluICh0eHQpIGRvc3lhc8SxbmRha2kgbWV0aW4gdmVyaWxlcmkuIFIgaWxlIGJ1IG1ldGlubGVyZGUgc8Sxa2zEsWtsYSBrdWxsYW7EsWxhbiBpZmFkZWxlcmkgb3J0YXlhIMOnxLFrYXJhY2HEn8Sxei4gDQoNCg0KIyMjIyBIYXrEsXJsxLFrDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI3Bha2V0bGVyaSBha3RpZiBoYWxlIGdldGlybWVkZW4gw7ZuY2UgbMO8dGZlbiB5w7xrbGV5aW4gKGluc3RhbGwucGFja2FnZXMoInBha2V0IGlzbWkiKSkNCg0KbGlicmFyeSgidGlkeXRleHQiKSAjbWV0aW4gdmVyaWxlcmluaSBidSBwYWtldGxlIGFuYWxpeiBlZGVjZcSfaXoNCmxpYnJhcnkoInRpZHl2ZXJzZSIpICNkcGx5ciwgZ2dwbG90MiwgdGlkeXIgZ2liaSBwYWtldGxlcmkgYmFyxLFuZMSxcsSxeW9yDQpsaWJyYXJ5KCJydHdlZXQiKSAjdHdlZXRsZXJpIMOnZWttZWsgacOnaW4ga3VsbGFuYWNhxJ/EsXouDQpsaWJyYXJ5KCJydmVzdCIpDQpsaWJyYXJ5KCJ3b3JkY2xvdWQyIikgI2tlbGltZSBidWx1dHUgZ8O2cnNlbGkgacOnaW4NCmxpYnJhcnkoInN0b3B3b3JkcyIpDQpgYGANCg0KDQojIyMjICoqRMO8eiBNZXRpbiBWZXJpc2lzaW5pIFInYSBha3RhcmFsxLFtKioNCg0KYGBge3J9DQpuaCA8LSByZWFkX2xpbmVzKCJDOi9Vc2Vycy9TYWRldHRpbi9EZXNrdG9wL25oLnR4dCIpDQpoZWFkKG5oKQ0KYGBgDQoNCg0KYGBge3J9DQp0YWlsKG5oKQ0KYGBgDQoNCmBgYHtyfQ0KbmhfdGlkeSA8LSBwYXN0ZTAobmgsIGNvbGxhcHNlID0gIiAiKQ0KDQpuaF90YWJsbyA8LSB0aWJibGUobmhfdGlkeSkNCg0Kc3RyKG5oX3RhYmxvKQ0KYGBgDQoNCk5hesSxbSBIaWttZXQnaW4gxZ5paXJpIGHFn2HEn8SxZGEgYW5hbGl6IGVkaWxlY2VrdGlyLg0KDQoNCiMjIyMgKioxLiBUcnVtcCfEsW4gVHdlZXRsZXJpKioNCg0KYGBge3J9DQp0cnVtcHR3ZWV0czwtcmVhZFJEUygiQzovVXNlcnMvU2FkZXR0aW4vUnN0YXRzL21ldGluLW1hZGVuL3RydW1wdHdlZXRzLnJkcyIpDQpgYGANCg0KDQpTYWRlY2UgdHdlZXQgbWV0aW5sZXJpbmkgZmlsdHJlbGV5ZXJlayBiaXIga2VuYXJhIGFsYWzEsW0NCg0KYGBge3J9DQp0cnVtcCA8LSB0cnVtcHR3ZWV0cyAlPiUgc2VsZWN0KDUpDQoNCmhlYWQodHJ1bXApDQpgYGANCg0Kw5ZuY2VsaWtsZSB0d2VldGxlcmRlIGt1bGxhbsSxbGFuIGhlcmhhbmdpIGtlc21lIGnFn2FyZXRpIHZlIGxpbmtsZXJpIGthbGTEsXJkxLFrLiBIZW1lbiBzb25yYXPEsW5kYSBidSB2ZXJpIHR3ZWV0IHZlcmlzaSBvbGR1xJ91IGnDp2luIHRva2VuIG9sYXJhayAidHdlZXRzIiBhcmd1bWFuxLFuxLEgc2XDp2l5b3J1ei4gQnUgc2F5ZWRlIHR3ZWV0bGVyZGVraSBldGlrZXQgdmUgQCBpZmFkZWxlcmluaSBrYWxkxLFybWFtxLHFnyBvbHV5b3J1ei4gSGVtZW4gc29ucmFzxLFuZGEgdW5uZXN0X3Rva2VuIGtvbXV0dSBpbGUgdXp1biB0d2VldGxlcmkgaGVyIHNhdMSxciBiaXIga2VsaW1leWUgZ2VsZWNlayDFn2VraWxkZSBwYXLDp2FsYXJhIGF5xLFyxLF5b3J1ei4gQXnEsXJkxLHEn8SxbcSxeiBrZWxpbWVsZXJpIGRlIGdlcmVrc2l6IGlmYWRlbGVyZGVuIGt1cnRhcsSxeW9ydXouIFNvbiBhZMSxbSBpc2UgYmlyIHJlcWV4IGtvbXV0dSBzYWRlY2Uga2VsaW1lbGVyZGVuIG9sdcWfYW4gaWZhZGVsZXJpIGZpbHRyZWxlbWVtaXppeiBzYcSfbMSxeW9yLg0KDQpgYGB7cn0NCg0KdHJ1bXB0aWR5IDwtIHRydW1wICU+JQ0KICBmaWx0ZXIoIXN0cl9kZXRlY3QodGV4dCwgJ14iJykpICU+JQ0KICBtdXRhdGUodGV4dCA9IHN0cl9yZXBsYWNlX2FsbCh0ZXh0LCAiaHR0cHM6Ly90LmNvL1tBLVphLXpcXGRdK3wmYW1wOyIsICIiKSklPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KSAlPiUNCiAgZmlsdGVyKCF3b3JkICVpbiUgc3RvcF93b3JkcyR3b3JkLA0KICAgICAgICAgc3RyX2RldGVjdCh3b3JkLCAiW2Etel0iKSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQodHJ1bXB0aWR5KQ0KYGBgDQoNCg0KU8Sxa2zEsWtsYSBrdWxsYW7EsWxhbiBrZWxpbWxlcmUgZ8O2eiBhdGFsxLFtDQoNCmBgYHtyfQ0KdHJ1bXB0aWR5ICU+JSBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkNCmBgYA0KDQoNClRla3JhciBvcmFuxLFuYSBnw7Z6IGF0YWzEsW0gDQoNCmBgYHtyfQ0KdGVrcmFyX29yYW4gPC0gdHJ1bXB0aWR5ICU+JSBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lICBtdXRhdGUodGVrcmFyID0gbi9zdW0obikqMTAwKQ0KDQpgYGANCg0KYGBge3J9DQp0ZWtyYXJfb3Jhbg0KYGBgDQoNCg0KIyMjIyAqKk4tZ3JhbSBpbGUgc8SxayBrdWxsYW7EsWxhbiBzw7Z6IMO2YmVrbGVyaW5pIGluY2VsZXllbGltKioNCg0KQmlyIGRpxJ9lciBhbmFsaXogaXNlIGFya2EgYXJrYXlhIGdlbGVuIGJpcmRlbiBmYXpsYSBpZmFkZW5pbiBuZSBzxLFrbMSxa2xhIGt1bGxhbsSxbGTEscSfxLFuxLEgYnVsYWJpbGlyaXouIEJ1bmEgbWV0aW4gbWFkZW5jaWxpxJ9pbmRlICpuLWdyYW0qIHnDtm50ZW1pIGFkxLEgdmVyaWxpeW9yLg0KDQpCdSBrxLFzxLFtZGEgZW4gw7ZuZW1saSBub2t0YSB1bm5lc3RfdG9rZW5zIGtvbXV0dSBpw6dpbmRlICoqdG9rZW5pKiogbmdyYW1zIG9sYXJhayBkZcSfacWfdGlyaXlvcnV6IHZlICoqbioqJ2kgMid5ZSBzYWJpdGxpeW9ydXogw6fDvG5rw7wgYmlyYmlyaW5pIHRha2lwIGVkZW4gaWtpIGF5csSxIGlmYWRlZGVuIG9sdcWfYW4gc8O2eiDDtmJla2xlcmluaSBlbGRlIGV0bWVrIGlzdGl5b3J1ei4gRcSfZXIgZGlsZXJzZWsgYnUgZGXEn2VyaSAqKjMqKidlIMOnxLFrYXLEsXAgZGFoYSBmYXpsYSDDtmJla3RlbiBvbHXFn2FuIGlmYWRlbGVyZSBkZSB1bGHFn2FiaWxpcml6LiBCdSBhbmFsaXogecO2bnRlbWkgdHdlZXRsZXJkZSwgc2l5YXNpbGVyaW4ga29udcWfbWFsYXLEsW5kYSwgYmFzxLFuIGHDp8Sxa2xhbWFsYXLEsW5kYSwga2l0YXBsYXJkYSB2Yi4gbWV0aW5sZXJkZSB0ZWtyYXIgZWRlbiBtZXRpbnNlbCBiaXIgw7Zyw7xudMO8IHlha2FsYW1hbcSxesSxIHNhxJ9sxLF5b3IuIA0KDQpgYGB7cn0NCg0KdHJ1bXBfYmlncmFtcyA8LSB0cnVtcCAlPiUNCiAgZmlsdGVyKCFzdHJfZGV0ZWN0KHRleHQsICdeIicpKSAlPiUNCiAgbXV0YXRlKHRleHQgPSBzdHJfcmVwbGFjZV9hbGwodGV4dCwgImh0dHBzOi8vdC5jby9bQS1aYS16XFxkXSt8JmFtcDsiLCAiIikpICU+JQ0KICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpDQoNCmBgYA0KDQpgYGB7cn0NCmhlYWQodHJ1bXBfYmlncmFtcykNCmBgYA0KDQoNCkhlbWVuIGNvdW50KCkga29tdXR1IGlsZSBzxLFrbMSxa2xhIGt1bGxhbsSxbGFuIGlmYWRlbGVyZSBnw7Z6IGF0YWzEsW0NCg0KYGBge3J9DQp0cnVtcF9iaWdyYW1zICU+JSBjb3VudChiaWdyYW0sIHNvcnQgPSBUUlVFKQ0KYGBgDQoNCkfDtnLDvGxkw7zEn8O8IMO8emVyZSBpa2kgYXlyxLEga2VsaW1lbmluIGJpcmxpa3RlIGt1bGxhbsSxbG1hIHNhecSxc8SxIHRlayBiaXIga2VsaW1leWUgZ8O2cmUgZGFoYSBhei4gxZ5pbWRpIGJ1IGlmYWRlbGVyZGVraSBnZXJla3NpeiBrZWxpbWVsZXJpIChzdG9wd29yZHMpIHZlcmkgc2V0aW5kZW4gZGVmIGVkZWxpbS4gQnVudW4gacOnaW4gw7ZuY2VraW5kZW4gZGFoYSBrYXJtYcWfxLFrIGJpciB5w7ZudGVtIGt1bGxhbmNhY2HEn8Sxei4gw5ZuY2VraW5lIG5hemFyYW4gaWtpIGlmYWRlZGVuIG9sdcWfYW4gaWxrIHPDvHR1bnUgaGVyIGJpcmkgYmlyIGtlbGltZWRlbiBvbHXFn2FjYWsgxZ9la2lsZGUgaWtpeWUgYXnEsXLEsXAgaGVyIHPDvHR1bnUgYXlyxLEgYXlyxLEgdGVtaXpsZXllY2XEn2l6LiBWZXJpIHRlbWl6bGVtZSBpxZ9sZW1pIHNvbnJhc8SxIHPDvHR1bmxhcsSxIGJpcmxlxZ90aXJpcCBnZXJla3NpeiBpZmFkZWxlcmRlbiBhcsSxbm3EscWfIGJpZ3JhbSBzdXR1bnVuIHRla3JhciBoZXNhcGxheWFjYcSfxLF6LiANCg0KTyBoYWxkZSDDtm5jZWxpa2xlIGJpZ3JhbSBzw7x0dW51bnUgaWtpeWUgYXnEsXJhbMSxbQ0KDQpgYGB7cn0NCnRydW1wX2JpZ3JhbXNfdGlkeSA8LSAgdHJ1bXBfYmlncmFtcyAlPiUNCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpICU+JSAjKiphecSxcm1hKioNCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lICN0ZW1pemxlbWUgYcWfYW1hc8SxIDENCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lICN0ZW1pemxlbWUgYcWfYW1hc8SxIDIgDQogIHVuaXRlKGJpZ3JhbSwgd29yZDEsIHdvcmQyLCBzZXAgPSAiICIpJT4lICNiaXJsZcWfdGlybWUNCiAgY291bnQoYmlncmFtLCBzb3J0ID0gVFJVRSkNCg0KYGBgDQoNCmBgYHtyfQ0KDQp0cnVtcF9iaWdyYW1zX3RpZHkNCmBgYA0KDQoqKk5hesSxbSBIaWttZXQnaW4gxZ5paXJpbmkgxLBuY2VsZXllbGltKioNCg0KDQpgYGB7cn0NCm5oX3ZlcmkgPC0gbmhfdGFibG8gJT4lDQogIGZpbHRlcighc3RyX2RldGVjdChuaF90aWR5LCAnXiInKSkgJT4lDQogIHVubmVzdF90b2tlbnMod29yZCwgbmhfdGlkeSkgJT4lDQogIGZpbHRlcighd29yZCAlaW4lIHN0b3B3b3JkcyhsYW5ndWFnZSA9ICJ0ciIsc291cmNlID0ic3RvcHdvcmRzLWlzbyIpKQ0KDQpuaF92ZXJpDQpgYGANCg0KDQpgYGB7cn0NCm5oX3ZlcmkgJT4lIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQ0KYGBgDQoNCg0KIyMjIyAqKk1ldGluIFZlcmlsZXJpbmluIEfDtnJzZWxsZcWfdGlyaWxtZXNpKioNCg0KRWxpbWl6ZGVraSBtZXRpbiB2ZXJpbGVyaWxlcmkgZW4gYmFzaXQgaGFsaXlsZSDDp3VidWsgZ3JhZmlrIHZleWFodXQga2VsaW1lIGJ1bHV0dSBvbGFyYWsgZ8O2cnNlbGxlxZ90aXJlYmlsaXJpei4gDQoNCmdncGxvdDIgdGVtYXnEsSBvbHXFn3R1cmFsxLFtDQoNCmBgYHtyfQ0KI2dyYWZpa2xlcmRlIGt1bGxhbmFjYcSfxLFtxLF6IGZvbnR1IGJlbGlybGVkaWsNCndpbmRvd3NGb250cygiUHJveGltYSBOb3ZhIiA9IHdpbmRvd3NGb250KCJQcm94aW1hIE5vdmEiKSkNCg0KdGhlbWVfY3VzdG9tMiA8LSBmdW5jdGlvbigpIHsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgICB0aGVtZSgNCiAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoZmFtaWx5ID0gIlByb3hpbWEgTm92YSIsIGNvbG9yID0gImdyYXkyNSIpLA0KICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLHNpemUgPSAxNCksDQogICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMyksDQogICAgICBheGlzLnRleHQueD0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMSksDQogICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBjb2xvciA9ICJncmF5MzAiKSwNCiAgICAgIHBsb3QuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIiNmNmY1ZjUiKSwNCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gIiNkOWQ5ZDkiLCBmaWxsID0gIiNkOWQ5ZDkiKSwNCiAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGNvbG91ciA9ICJncmF5MjUiLCBmYWNlID0gImJvbGQiKSkNCiAgDQp9DQpgYGANCg0KSGVyIGlraSB2ZXJpIHNldGluZGUgZGUgZW4gw6dvayBrdWxsYW7EsWxhbiAyMCBpZmFkZXlpIGZpbHRyZWxleWVsaW0uDQoNCmBgYHtyfQ0KdHJ1bXBfYmlncmFtc195ZW5pPC0gdHJ1bXBfYmlncmFtc190aWR5ICU+JSANCiAgZmlsdGVyKCFiaWdyYW0gJWluJSBjKCJwcmVzaWRlbnQgcmVhbGRvbmFsZHRydW1wIiwicHJlc2lkZW50IHRydW1wIikpICU+JSANCiAgdG9wX24oMjAsbikNCnRydW1wX3RpZHlfeWVuaSA8LSB0cnVtcF90aWR5X2NvdW50ICU+JSB0b3BfbigyMCxuKSANCg0KYGBgDQoNCg0KR8O2cnNlbGxlxZ90aXJtZQ0KYGBge3J9DQpyZW5rIDwtIGMoInPDtnpfw7ZiZWsiPSIiLA0KICAgICAgICAgICJrZWxpbWUiPSIiKQ0KDQoNCnRydW1wX3RpZHlfeWVuaSAlPiUgZ2dwbG90KGFlcyhmY3RfcmVvcmRlcih3b3JkLCBuKSxuLCBmaWxsID0icmVkIikpKw0KICBnZW9tX2NvbCgpKw0KICBjb29yZF9mbGlwKCkrDQogIGdlb21fdGV4dChhZXMoeCA9IHdvcmQsIHkgPSBuLGxhYmVsID0gbiksY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIGhqdXN0ID0gLTAuMixzaXplID0gMy43LGNvbG9yPSAiZ3JheTI1IikrDQogIGxhYnMoeD0iIix5PSIiLA0KICAgICAgIHRpdGxlID0gIlRydW1wJ8SxbiBTxLFrIEt1bGxhbmTEscSfxLEgxLBmYWRlbGVyIiwNCiAgICAgICBzdWJ0aXRsZSA9ICJTb24gMyBiaW4gVHdlZXQgQW5hbGl6ZSBEYWhpbCBFZGlsbWnFn3Rpci4iLA0KICAgICAgIGNhcHRpb24gPSAiQGRlbWlyZWxzYWRldHRpbiIpK3RoZW1lX2N1c3RvbTIoKQ0KYGBgDQoNClPDtnogw5ZiZWtsZXLFnw0KDQpgYGB7cn0NCnZhbHVlcyA9IiMzMTgyYmQiDQoNCnRydW1wX2JpZ3JhbXNfeWVuaSAlPiUgZ2dwbG90KGFlcyhmY3RfcmVvcmRlcihiaWdyYW0sIG4pLG4sIGZpbGwgPXZhbHVlcykpKw0KICBnZW9tX2NvbCgpKw0KICBjb29yZF9mbGlwKCkrIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9ICIjMzE4MmJkIikrDQogIGdlb21fdGV4dChhZXMoeCA9IGJpZ3JhbSwgeSA9IG4sbGFiZWwgPSBuKSxjaGVja19vdmVybGFwID0gVFJVRSwgaGp1c3QgPSAtMC4yLHNpemUgPSAzLjgsY29sb3I9ICJncmF5MjUiKSsNCiAgbGFicyh4PSIiLHk9IiIsDQogICAgICAgdGl0bGUgPSAiVHJ1bXAnxLFuIFPEsWsgS3VsbGFuZMSxxJ/EsSBTw7Z6IMOWYmVrbGVyaSIsDQogICAgICAgc3VidGl0bGUgPSAiU29uIDMgYmluIFR3ZWV0IEFuYWxpemUgRGFoaWwgRWRpbG1pxZ90aXIuIiwNCiAgICAgICBjYXB0aW9uID0gIkBkZW1pcmVsc2FkZXR0aW4iKSt0aGVtZV9jdXN0b20yKCkNCmBgYA0KDQoNCg0KKipXb3JkY2xvdWQyIHBha2V0aSBpbGUgZ2VsacWfbWnFnyBrZWxpbWUgYnVsdXR1KioNCg0KYGBge3J9DQpsaWJyYXJ5KHdvcmRjbG91ZDIpDQoNCnRydW1wX3RpZHlfY291bnQ8LSBjb3VudCh0cnVtcHRpZHksd29yZCwgc29ydCA9IFRSVUUpICU+JSBmaWx0ZXIoIXdvcmQlaW4lIGMoInByZXNpZGVudCIsInRydW1wIiwicmVhbGRvbmFsZHRydW1wIikpDQoNCnRydW1wX3RpZHlfY291bnQNCmBgYA0KDQpgYGB7cn0NCndvcmRjbG91ZDIoZGF0YSA9IHRydW1wX3RpZHlfY291bnQsY29sb3IgPSAicmFuZG9tLWxpZ2h0IiwgYmFja2dyb3VuZENvbG9yID0gImdyZXkyNSIsc2l6ZSA9MC42KQ0KYGBgDQoNCkRhaGEgaG/FnyBiaXIga2VsaW1lIGJ1bHV0dSBvbHXFn3R1cmFsxLFtDQoNCmBgYHtyfQ0Kd29yZGNsb3VkMihkYXRhID0gdHJ1bXBfdGlkeV9jb3VudCxtaW5Sb3RhdGlvbiA9IC1waS82LCBtYXhSb3RhdGlvbiA9IC1waS82LA0KICByb3RhdGVSYXRpbyA9IDEsIHNpemUgPSAwLjcpDQpgYGANCg0KDQpgYGB7cn0NCnRydW1wX3dvcmRjbG91ZCA8LSB0cnVtcF9iaWdyYW1zX3RpZHkgJT4lIGZpbHRlcighYmlncmFtICVpbiUgYygicHJlc2lkZW50IHJlYWxkb25hbGR0cnVtcCIsInByZXNpZGVudCB0cnVtcCIpKQ0KDQp3b3JkY2xvdWQyKGRhdGEgPSB0cnVtcF93b3JkY2xvdWQsbWluUm90YXRpb24gPSAtcGkvNiwgbWF4Um90YXRpb24gPSAtcGkvNiwNCiAgcm90YXRlUmF0aW8gPSAxLCBzaXplID0gMC43KQ0KYGBgDQoNCg0K