Untuk memuat bahasa Arab, jika tidak bisa muncul teks Bahasa Arab, bisa menggunakan kode Sys.setlocale("LC_ALL","Arabic") agar bisa menghasilkan output teks Bahasa Arab.

Sys.setlocale("LC_ALL","Arabic")
[1] "LC_COLLATE=Arabic_Saudi Arabia.1256;LC_CTYPE=Arabic_Saudi Arabia.1256;LC_MONETARY=Arabic_Saudi Arabia.1256;LC_NUMERIC=C;LC_TIME=Arabic_Saudi Arabia.1256"

Dapatkan teks Al-Qur’an.

Buka http://tanzil.net/download/ dan pilih file yang diinginkan. Dalam tutorial ini, saya menggunakan Quran Text Type “Simple (Clean)” tanpa tanda jeda atau opsi lain, dan memilih “Text (with aya numbers)”, dan menyimpannya di folder F:/uun/UIN/KNOWLEDGE DISCOVERY AND DATA MINING/PROJECT/.

q = read.csv("F:/uun/UIN/KNOWLEDGE DISCOVERY AND DATA MINING/PROJECT/quran-simple-clean.txt", header=F, stringsAsFactor=F, encoding="UTF-8", sep="|")
head(q)

Dibagian akhir dari data quran yang sudah didownload terdapat copyright didalamnya, maka perlu di edit manual untuk menghapus bagian yang tidak perlu. Caranya buka file quran-simple-clean.txt, bisa membukanya dengan Notepad, setelah itu langsung menuju ke bagian terakhir teks, hapus teks selain teks Bahasa Arab.

tail(q)

Merapikan Beberapa Hal

Untuk melihat struktur dari Al Quran di R. Bisa melakukan perintah berikut.

str(q)
'data.frame':   6236 obs. of  3 variables:
 $ V1: int  1 1 1 1 1 1 1 2 2 2 ...
 $ V2: int  1 2 3 4 5 6 7 1 2 3 ...
 $ V3: chr  "بسم الله الرحمن الرحيم" "الحمد لله رب العالمين" "الرحمن الرحيم" "مالك يوم الدين" ...

Kita tambahkan Heading, untuk menggantikan inisial yang sudah diberikan oleh R.

colnames(q) = c("sura", "aya", "text")

Menampilkan Ayat Pertama dalam 10 Surat Pertama

q$text[q$aya==1 & q$sura<=10]
 [1] "بسم الله الرحمن الرحيم"                                                                                                                                                          
 [2] "بسم الله الرحمن الرحيم الم"                                                                                                                                                      
 [3] "بسم الله الرحمن الرحيم الم"                                                                                                                                                      
 [4] "بسم الله الرحمن الرحيم يا أيها الناس اتقوا ربكم الذي خلقكم من نفس واحدة وخلق منها زوجها وبث منهما رجالا كثيرا ونساء واتقوا الله الذي تساءلون به والأرحام إن الله كان عليكم رقيبا"
 [5] "بسم الله الرحمن الرحيم يا أيها الذين آمنوا أوفوا بالعقود أحلت لكم بهيمة الأنعام إلا ما يتلى عليكم غير محلي الصيد وأنتم حرم إن الله يحكم ما يريد"                                 
 [6] "بسم الله الرحمن الرحيم الحمد لله الذي خلق السماوات والأرض وجعل الظلمات والنور ثم الذين كفروا بربهم يعدلون"                                                                       
 [7] "بسم الله الرحمن الرحيم المص"                                                                                                                                                     
 [8] "بسم الله الرحمن الرحيم يسألونك عن الأنفال قل الأنفال لله والرسول فاتقوا الله وأصلحوا ذات بينكم وأطيعوا الله ورسوله إن كنتم مؤمنين"                                               
 [9] "براءة من الله ورسوله إلى الذين عاهدتم من المشركين"                                                                                                                               
[10] "بسم الله الرحمن الرحيم الر تلك آيات الكتاب الحكيم"                                                                                                                               

Dari hasil yang didapatkan, kita tahu bahwa Tanzil menganggap “بسم الله الرحمن الرحيم” sebagai versi bagian pertama disemua surat, kecuali surat ke 9. Untuk itu mari kita mengecualikan “بسم الله الرحمن الرحيم” kecuali surat no 1 dan 9

q$text = gsub("^بسم الله الرحمن الرحيم ","",q$text)

Menamampilkan Surah

kali ini kita akan menampilkan Surah No.100

q[q$sura==100,]

Mencari Kata

Selanjutnya kita akan mencari Kata Muhammad (محمد) dan mengetahui berapa kali disebutkan dalam Al Quran.

q[which(grepl("محمد", q$text)),]

Text Mining

Kita masuk pada Text Mining dimana nantinya kita dapat berwksperimen dengan data menggunakan Text Mining. sebelumnya Install terbelih dahulu Packages tm dengan cara klik pada Console R Studio kemudian Ketikkan install.packages(“tm) kemudian tekan”enter", tunggu beberapa saat hingga proses instalasi selesai.

str(q)
'data.frame':   6236 obs. of  3 variables:
 $ sura: int  1 1 1 1 1 1 1 2 2 2 ...
 $ aya : int  1 2 3 4 5 6 7 1 2 3 ...
 $ text: chr  "بسم الله الرحمن الرحيم" "الحمد لله رب العالمين" "الرحمن الرحيم" "مالك يوم الدين" ...
library(tm)

Langkah pertama adalah membuat corpus yang terdiri dari Versi Text Arabic Mentah sebagai VectorSource

qCorpus = Corpus(VectorSource(q$text))

Mari kita bedah konten dari Corpus

inspect(qCorpus[1:5])
<<SimpleCorpus>>
Metadata:  corpus specific: 1, document level (indexed): 0
Content:  documents: 5

[1] بسم الله الرحمن الرحيم الحمد لله رب العالمين  الرحمن الرحيم          مالك يوم الدين         إياك نعبد وإياك نستعين

Sekarang mari kita membuat Matriks Dokumen

qTerms = DocumentTermMatrix(qCorpus)
qTerms
<<DocumentTermMatrix (documents: 6236, terms: 14865)>>
Non-/sparse entries: 73563/92624577
Sparsity           : 100%
Maximal term length: 11
Weighting          : term frequency (tf)

Ini menghasilkan matriks dokumen yang panjang (yaitu, ayat-ayat) terhadap istilah-istilah Al-Qur’an. Mari kita misalnya melihat sebagian dari matriks ini dengan melihat ke dalam dokumen 1 sampai 7 (yaitu, sura Fateha) dan istilah mengatakan 1000 sampai 1005

inspect(qTerms[1:7,1000:1005])
<<DocumentTermMatrix (documents: 7, terms: 6)>>
Non-/sparse entries: 0/42
Sparsity           : 100%
Maximal term length: 6
Weighting          : term frequency (tf)
Sample             :
    Terms
Docs وجه ولله اتخذ سبحانه قانتون ولدا
   1   0    0    0      0      0    0
   2   0    0    0      0      0    0
   3   0    0    0      0      0    0
   4   0    0    0      0      0    0
   5   0    0    0      0      0    0
   6   0    0    0      0      0    0
   7   0    0    0      0      0    0

Ini memberitahu kita bahwa tidak ada satu pun dari lima istilah yang muncul di salah satu dari 7 dokumen pertama. Sparsity adalah masalah yang dikenal dalam matriks istilah dokumen.

Beberapa Operasi dari Istilah Dokumen Matriks

Mari kita temukan beberapa istilah umum dalam Quran. Istilah apa yang digunakan 100 kali atau lebih dalam Quran?

findFreqTerms(qTerms,100)
 [1] "الله"     "رب"       "لله"      "يوم"      "الذين"    "عليهم"    "ولا"      "الكتاب"   "ذلك"      "فيه"     
[11] "لا"       "بما"      "من"       "هم"       "والذين"   "وما"      "أولئك"    "ربهم"     "على"      "أم"      
[21] "إن"       "كفروا"    "لم"       "عذاب"     "الناس"    "بالله"    "ومن"      "آمنوا"    "إلا"      "في"      
[31] "كانوا"    "إنما"     "الأرض"    "قالوا"    "لهم"      "وإذا"     "ولكن"     "إلى"      "إنا"      "الذي"    
[41] "فلما"     "ما"       "أو"       "السماء"   "والله"    "شيء"      "كل"       "ولو"      "أيها"     "ربكم"    
[51] "يا"       "به"       "فلا"      "لكم"      "كنتم"     "مما"      "وإن"      "النار"    "فإن"      "أن"      
[61] "فيها"     "قبل"      "هذا"      "وهم"      "الحق"     "بعد"      "ثم"       "عليم"     "هو"       "وهو"     
[71] "إني"      "ربك"      "قال"      "السماوات" "والأرض"   "إنه"      "عليه"     "عليكم"    "عن"       "موسى"    
[81] "خير"      "عند"      "قوم"      "حتى"      "قد"       "منكم"     "ولقد"     "كان"      "منهم"     "قل"      
[91] "الدنيا"   "بل"       "يشاء"     "له"       "ربنا"     "إذ"       "إذا"     

Bahkan kita dapat membuat daftar istilah yang paling sering muncul dan menyimpannya dalam data frame.

freq = sort(colSums(as.matrix(qTerms)),decreasing = T)
head(freq, 10)
   من  الله    في    ما    إن    لا الذين   على   إلا   ولا 
 2763  2153  1185  1013   966   812   810   670   664   658 
wf = data.frame(word=names(freq), freq=freq)

Selanjutnya kita akan menggunakan paket ggplot2, untuk memvisualisasikan dalam bentuk plot. Pertama kita harus memproses lirary ggplot2

library(ggplot2)

ambil frekuensi paling banyak dalam data frame terpisah

wfplot = subset(wf,freq>300)
ggplot(wfplot, aes(word, freq)) +
  geom_bar(stat="identity")+
  theme(axis.text.x=element_text(angle=45, hjust = 1))

barplot(wf[1:10,]$freq, las = 2, names.arg = wf[1:10,]$word,
col ="lightblue", main ="Most frequent words",
ylab = "Word frequencies")

library(wordcloud)
set.seed(114)
wordcloud(names(freq), freq, min.freq=50, scale=c(5,.5),colors=brewer.pal(6,"Dark2"), rot.per=0.2)

Panjang Kata

Kita ingin tahu lebih banyak tentang panjang kata dalam Quran.

Pertama mari kita dapatkan semua kata dalam data frame words dan panjang kata di wLen

words = as.matrix(colnames(qTerms))
wLen = data.frame(nletters=nchar(words))

Mari kita menghasilkan beberapa visualisasi dari ini.

ggplot(wLen, aes(x=nletters))+
  geom_histogram(binwidth=1) +
  geom_vline(xintercept=mean(nchar(words)),
             colour="green", size=1, alpha=.5)+
  labs(x="Number of Letters", y="Number of Words")

Hal ini menunjukkan bahwa rata-rata ukuran kata mendekati 5 huruf. Ingat kita tidak berbicara di sini tentang kata dasar, melainkan kata mentah dengan semua awalan dan akhiran.

Daftar Pustaka

http://tanzil.net/download/

http://textminingthequran.com/tutorial/tm.html

LS0tDQp0aXRsZTogIlRla3NtaW5pbmcgQWwgUXVyYW4gTWVuZ2d1bmFrYW4gUiBTdHVkaW8iDQphdXRob3I6ICJQcm9mLiBEciBTdWhhcnRvbm8gTS5Lb20gJiBVJ3VuIFNldGlhd2F0aSwgUy5Lb20iDQpkYXRlOiAiMTEvMy8yMDIxIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogc3BhY2VsYWINCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6IHRydWUNCnN1YnRpdGxlOiBNYWdpc3RlciBJbmZvcm1hdGlrYSBVSU4gTWF1bGFuYSBNYWxpayBJYnJhaGltIE1hbGFuZyANCi0tLQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQpib2R5eyAvKiBOb3JtYWwgICovDQogICAgICBmb250LXNpemU6IDE0cHg7DQogIH0NCnRkIHsgIC8qIFRhYmxlICAqLw0KICBmb250LXNpemU6IDEycHg7DQp9DQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMzhweDsNCiAgY29sb3I6IGxpZ2h0Ymx1ZTsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQp9DQpoMSB7IC8qIEhlYWRlciAxICovDQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IERhcmtCbHVlOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAqLw0KICBmb250LXNpemU6IDIwcHg7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCn0NCmgzIHsgLyogSGVhZGVyIDMgKi8NCiAgZm9udC1zaXplOiAxNnB4Ow0KIyAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCn0NCmg0IHsgLyogSGVhZGVyIDQgKi8NCiAgZm9udC1zaXplOiAxNHB4Ow0KICBjb2xvcjogRGFya0JsdWU7DQp9DQpjb2RlLnJ7IC8qIENvZGUgYmxvY2sgKi8NCiAgICBmb250LXNpemU6IDEycHg7DQp9DQpwcmUgeyAvKiBDb2RlIGJsb2NrIC0gZGV0ZXJtaW5lcyBjb2RlIHNwYWNpbmcgYmV0d2VlbiBsaW5lcyAqLw0KICAgIGZvbnQtc2l6ZTogMTJweDsNCn0NCjwvc3R5bGU+DQotLS0NCg0KVW50dWsgbWVtdWF0IGJhaGFzYSBBcmFiLCBqaWthIHRpZGFrIGJpc2EgbXVuY3VsIHRla3MgQmFoYXNhIEFyYWIsIGJpc2EgbWVuZ2d1bmFrYW4ga29kZSBgU3lzLnNldGxvY2FsZSgiTENfQUxMIiwiQXJhYmljIilgIGFnYXIgYmlzYSBtZW5naGFzaWxrYW4gb3V0cHV0IHRla3MgQmFoYXNhIEFyYWIuDQpgYGB7cn0NClN5cy5zZXRsb2NhbGUoIkxDX0FMTCIsIkFyYWJpYyIpDQpgYGANCg0KIyBEYXBhdGthbiB0ZWtzIEFsLVF1cidhbi4NCg0KQnVrYSA8YSBocmVmPSJodHRwOi8vdGFuemlsLm5ldC9kb3dubG9hZC8iPmh0dHA6Ly90YW56aWwubmV0L2Rvd25sb2FkLzwvYT4gZGFuIHBpbGloIGZpbGUgeWFuZyBkaWluZ2lua2FuLiBEYWxhbSB0dXRvcmlhbCBpbmksIHNheWEgbWVuZ2d1bmFrYW4gUXVyYW4gVGV4dCBUeXBlICJTaW1wbGUgKENsZWFuKSIgdGFucGEgdGFuZGEgamVkYSBhdGF1IG9wc2kgbGFpbiwgZGFuIG1lbWlsaWggIlRleHQgKHdpdGggYXlhIG51bWJlcnMpIiwgZGFuIG1lbnlpbXBhbm55YSBkaSBmb2xkZXIgYEY6L3V1bi9VSU4vS05PV0xFREdFIERJU0NPVkVSWSBBTkQgREFUQSBNSU5JTkcvUFJPSkVDVC9gLg0KDQpgYGB7cn0NCnEgPSByZWFkLmNzdigiRjovdXVuL1VJTi9LTk9XTEVER0UgRElTQ09WRVJZIEFORCBEQVRBIE1JTklORy9QUk9KRUNUL3F1cmFuLXNpbXBsZS1jbGVhbi50eHQiLCBoZWFkZXI9Riwgc3RyaW5nc0FzRmFjdG9yPUYsIGVuY29kaW5nPSJVVEYtOCIsIHNlcD0ifCIpDQpoZWFkKHEpDQpgYGANCkRpYmFnaWFuIGFraGlyIGRhcmkgZGF0YSBxdXJhbiB5YW5nIHN1ZGFoIGRpZG93bmxvYWQgdGVyZGFwYXQgY29weXJpZ2h0IGRpZGFsYW1ueWEsIG1ha2EgcGVybHUgZGkgZWRpdCBtYW51YWwgdW50dWsgbWVuZ2hhcHVzIGJhZ2lhbiB5YW5nIHRpZGFrIHBlcmx1LiBDYXJhbnlhIGJ1a2EgZmlsZSBgcXVyYW4tc2ltcGxlLWNsZWFuLnR4dGAsIGJpc2EgbWVtYnVrYW55YSBkZW5nYW4gTm90ZXBhZCwgc2V0ZWxhaCBpdHUgbGFuZ3N1bmcgbWVudWp1IGtlIGJhZ2lhbiB0ZXJha2hpciB0ZWtzLCBoYXB1cyB0ZWtzIHNlbGFpbiB0ZWtzIEJhaGFzYSBBcmFiLg0KDQpgYGB7cn0NCnRhaWwocSkNCmBgYA0KDQojIE1lcmFwaWthbiBCZWJlcmFwYSBIYWwNCg0KVW50dWsgbWVsaWhhdCBzdHJ1a3R1ciBkYXJpIEFsIFF1cmFuIGRpIFIuIEJpc2EgbWVsYWt1a2FuIHBlcmludGFoIGJlcmlrdXQuIA0KYGBge3J9DQpzdHIocSkNCmBgYA0KS2l0YSB0YW1iYWhrYW4gSGVhZGluZywgdW50dWsgbWVuZ2dhbnRpa2FuIGluaXNpYWwgeWFuZyBzdWRhaCBkaWJlcmlrYW4gb2xlaCBSLg0KYGBge3J9DQpjb2xuYW1lcyhxKSA9IGMoInN1cmEiLCAiYXlhIiwgInRleHQiKQ0KYGBgDQoNCk1lbmFtcGlsa2FuIEF5YXQgUGVydGFtYSBkYWxhbSAxMCBTdXJhdCBQZXJ0YW1hDQpgYGB7cn0NCnEkdGV4dFtxJGF5YT09MSAmIHEkc3VyYTw9MTBdDQpgYGANCkRhcmkgaGFzaWwgeWFuZyBkaWRhcGF0a2FuLCBraXRhIHRhaHUgYmFod2EgVGFuemlsIG1lbmdhbmdnYXAg4oCc2KjYs9mFINin2YTZhNmHINin2YTYsdit2YXZhiDYp9mE2LHYrdmK2YXigJ0gc2ViYWdhaSB2ZXJzaSBiYWdpYW4gcGVydGFtYSBkaXNlbXVhIHN1cmF0LCBrZWN1YWxpIHN1cmF0IGtlIDkuIFVudHVrIGl0dSBtYXJpIGtpdGEgbWVuZ2VjdWFsaWthbiDigJzYqNiz2YUg2KfZhNmE2Ycg2KfZhNix2K3ZhdmGINin2YTYsdit2YrZheKAnSBrZWN1YWxpIHN1cmF0IG5vIDEgZGFuIDkgDQoNCmBgYHtyfQ0KcSR0ZXh0ID0gZ3N1YigiXtio2LPZhSDYp9mE2YTZhyDYp9mE2LHYrdmF2YYg2KfZhNix2K3ZitmFICIsIiIscSR0ZXh0KQ0KYGBgDQoNCiMgTWVuYW1hbXBpbGthbiBTdXJhaCANCg0Ka2FsaSBpbmkga2l0YSBha2FuIG1lbmFtcGlsa2FuIFN1cmFoIE5vLjEwMA0KDQpgYGB7cn0NCnFbcSRzdXJhPT0xMDAsXQ0KYGBgDQoNCg0KIyBNZW5jYXJpIEthdGENCg0KU2VsYW5qdXRueWEga2l0YSBha2FuIG1lbmNhcmkgS2F0YSBgTXVoYW1tYWQgKNmF2K3ZhdivKWAgZGFuIG1lbmdldGFodWkgYmVyYXBhIGthbGkgZGlzZWJ1dGthbiBkYWxhbSBBbCBRdXJhbi4NCg0KYGBge3J9DQpxW3doaWNoKGdyZXBsKCLZhdit2YXYryIsIHEkdGV4dCkpLF0NCmBgYA0KIyBUZXh0IE1pbmluZw0KDQpLaXRhIG1hc3VrIHBhZGEgVGV4dCBNaW5pbmcgZGltYW5hIG5hbnRpbnlhIGtpdGEgZGFwYXQgYmVyd2tzcGVyaW1lbiBkZW5nYW4gZGF0YSBtZW5nZ3VuYWthbiBUZXh0IE1pbmluZy4gc2ViZWx1bW55YSBJbnN0YWxsIHRlcmJlbGloIGRhaHVsdSBQYWNrYWdlcyBgdG1gIGRlbmdhbiBjYXJhIGtsaWsgcGFkYSBDb25zb2xlIFIgU3R1ZGlvIGtlbXVkaWFuIEtldGlra2FuIGluc3RhbGwucGFja2FnZXMoInRtKSBrZW11ZGlhbiB0ZWthbiAiZW50ZXIiLCB0dW5nZ3UgYmViZXJhcGEgc2FhdCBoaW5nZ2EgcHJvc2VzIGluc3RhbGFzaSBzZWxlc2FpLiANCg0KYGBge3J9DQpzdHIocSkNCmBgYA0KDQoNCmBgYHtyfQ0KbGlicmFyeSh0bSkNCmBgYA0KDQpMYW5na2FoIHBlcnRhbWEgYWRhbGFoIG1lbWJ1YXQgY29ycHVzIHlhbmcgdGVyZGlyaSBkYXJpIFZlcnNpIFRleHQgQXJhYmljIE1lbnRhaCBzZWJhZ2FpIFZlY3RvclNvdXJjZQ0KDQpgYGB7cn0NCnFDb3JwdXMgPSBDb3JwdXMoVmVjdG9yU291cmNlKHEkdGV4dCkpDQpgYGANCg0KTWFyaSBraXRhIGJlZGFoIGtvbnRlbiBkYXJpIENvcnB1cw0KYGBge3J9DQppbnNwZWN0KHFDb3JwdXNbMTo1XSkNCmBgYA0KU2VrYXJhbmcgbWFyaSBraXRhIG1lbWJ1YXQgTWF0cmlrcyBEb2t1bWVuDQpgYGB7cn0NCnFUZXJtcyA9IERvY3VtZW50VGVybU1hdHJpeChxQ29ycHVzKQ0KcVRlcm1zDQpgYGANCkluaSBtZW5naGFzaWxrYW4gbWF0cmlrcyBkb2t1bWVuIHlhbmcgcGFuamFuZyAoeWFpdHUsIGF5YXQtYXlhdCkgdGVyaGFkYXAgaXN0aWxhaC1pc3RpbGFoIEFsLVF1cidhbi4gTWFyaSBraXRhIG1pc2FsbnlhIG1lbGloYXQgc2ViYWdpYW4gZGFyaSBtYXRyaWtzIGluaSBkZW5nYW4gbWVsaWhhdCBrZSBkYWxhbSBkb2t1bWVuIDEgc2FtcGFpIDcgKHlhaXR1LCBzdXJhIEZhdGVoYSkgZGFuIGlzdGlsYWggbWVuZ2F0YWthbiAxMDAwIHNhbXBhaSAxMDA1DQoNCmBgYHtyfQ0KaW5zcGVjdChxVGVybXNbMTo3LDEwMDA6MTAwNV0pDQpgYGANCkluaSBtZW1iZXJpdGFodSBraXRhIGJhaHdhIHRpZGFrIGFkYSBzYXR1IHB1biBkYXJpIGxpbWEgaXN0aWxhaCB5YW5nIG11bmN1bCBkaSBzYWxhaCBzYXR1IGRhcmkgNyBkb2t1bWVuIHBlcnRhbWEuIFNwYXJzaXR5IGFkYWxhaCBtYXNhbGFoIHlhbmcgZGlrZW5hbCBkYWxhbSBtYXRyaWtzIGlzdGlsYWggZG9rdW1lbi4NCg0KIyBCZWJlcmFwYSBPcGVyYXNpIGRhcmkgSXN0aWxhaCBEb2t1bWVuIE1hdHJpa3MNCg0KTWFyaSBraXRhIHRlbXVrYW4gYmViZXJhcGEgaXN0aWxhaCB1bXVtIGRhbGFtIFF1cmFuLiBJc3RpbGFoIGFwYSB5YW5nIGRpZ3VuYWthbiAxMDAga2FsaSBhdGF1IGxlYmloIGRhbGFtIFF1cmFuPw0KDQpgYGB7cn0NCmZpbmRGcmVxVGVybXMocVRlcm1zLDEwMCkNCmBgYA0KQmFoa2FuIGtpdGEgZGFwYXQgbWVtYnVhdCBkYWZ0YXIgaXN0aWxhaCB5YW5nIHBhbGluZyBzZXJpbmcgbXVuY3VsIGRhbiBtZW55aW1wYW5ueWEgZGFsYW0gZGF0YSBmcmFtZS4NCg0KYGBge3J9DQpmcmVxID0gc29ydChjb2xTdW1zKGFzLm1hdHJpeChxVGVybXMpKSxkZWNyZWFzaW5nID0gVCkNCmhlYWQoZnJlcSwgMTApDQpgYGANCg0KYGBge3J9DQp3ZiA9IGRhdGEuZnJhbWUod29yZD1uYW1lcyhmcmVxKSwgZnJlcT1mcmVxKQ0KYGBgDQoNClNlbGFuanV0bnlhIGtpdGEgYWthbiBtZW5nZ3VuYWthbiBwYWtldCBgZ2dwbG90MmAsIHVudHVrIG1lbXZpc3VhbGlzYXNpa2FuIGRhbGFtIGJlbnR1ayBwbG90LiBQZXJ0YW1hIGtpdGEgaGFydXMgbWVtcHJvc2VzIGxpcmFyeSBgZ2dwbG90MmANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KDQphbWJpbCBmcmVrdWVuc2kgcGFsaW5nIGJhbnlhayBkYWxhbSBkYXRhIGZyYW1lIHRlcnBpc2FoDQpgYGB7cn0NCndmcGxvdCA9IHN1YnNldCh3ZixmcmVxPjMwMCkNCmdncGxvdCh3ZnBsb3QsIGFlcyh3b3JkLCBmcmVxKSkgKw0KICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KDQpgYGB7cn0NCmJhcnBsb3Qod2ZbMToxMCxdJGZyZXEsIGxhcyA9IDIsIG5hbWVzLmFyZyA9IHdmWzE6MTAsXSR3b3JkLA0KY29sID0ibGlnaHRibHVlIiwgbWFpbiA9Ik1vc3QgZnJlcXVlbnQgd29yZHMiLA0KeWxhYiA9ICJXb3JkIGZyZXF1ZW5jaWVzIikNCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkod29yZGNsb3VkKQ0KYGBgDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTE0KQ0Kd29yZGNsb3VkKG5hbWVzKGZyZXEpLCBmcmVxLCBtaW4uZnJlcT01MCwgc2NhbGU9Yyg1LC41KSxjb2xvcnM9YnJld2VyLnBhbCg2LCJEYXJrMiIpLCByb3QucGVyPTAuMikNCmBgYA0KDQojIFBhbmphbmcgS2F0YQ0KS2l0YSBpbmdpbiB0YWh1IGxlYmloIGJhbnlhayB0ZW50YW5nIHBhbmphbmcga2F0YSBkYWxhbSBRdXJhbi4NCg0KUGVydGFtYSBtYXJpIGtpdGEgZGFwYXRrYW4gc2VtdWEga2F0YSBkYWxhbSBkYXRhIGZyYW1lIGB3b3Jkc2AgZGFuIHBhbmphbmcga2F0YSBkaSBgd0xlbmANCg0KYGBge3J9DQp3b3JkcyA9IGFzLm1hdHJpeChjb2xuYW1lcyhxVGVybXMpKQ0KYGBgDQoNCmBgYHtyfQ0Kd0xlbiA9IGRhdGEuZnJhbWUobmxldHRlcnM9bmNoYXIod29yZHMpKQ0KYGBgDQoNCk1hcmkga2l0YSBtZW5naGFzaWxrYW4gYmViZXJhcGEgdmlzdWFsaXNhc2kgZGFyaSBpbmkuDQoNCmBgYHtyfQ0KZ2dwbG90KHdMZW4sIGFlcyh4PW5sZXR0ZXJzKSkrDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEpICsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0PW1lYW4obmNoYXIod29yZHMpKSwNCiAgICAgICAgICAgICBjb2xvdXI9ImdyZWVuIiwgc2l6ZT0xLCBhbHBoYT0uNSkrDQogIGxhYnMoeD0iTnVtYmVyIG9mIExldHRlcnMiLCB5PSJOdW1iZXIgb2YgV29yZHMiKQ0KYGBgDQpIYWwgaW5pIG1lbnVuanVra2FuIGJhaHdhIHJhdGEtcmF0YSB1a3VyYW4ga2F0YSBtZW5kZWthdGkgNSBodXJ1Zi4gSW5nYXQga2l0YSB0aWRhayBiZXJiaWNhcmEgZGkgc2luaSB0ZW50YW5nIGthdGEgZGFzYXIsIG1lbGFpbmthbiBrYXRhIG1lbnRhaCBkZW5nYW4gc2VtdWEgYXdhbGFuIGRhbiBha2hpcmFuLg0KDQoNCkRhZnRhciBQdXN0YWthDQoNCg0KPGEgaHJlZj0iaHR0cDovL3RhbnppbC5uZXQvZG93bmxvYWQvIj5odHRwOi8vdGFuemlsLm5ldC9kb3dubG9hZC88L2E+DQoNCjxhIGhyZWYgPSJodHRwOi8vdGV4dG1pbmluZ3RoZXF1cmFuLmNvbS90dXRvcmlhbC90bS5odG1sIj5odHRwOi8vdGV4dG1pbmluZ3RoZXF1cmFuLmNvbS90dXRvcmlhbC90bS5odG1sPC9hPg==