Creative Commons License
The materials in this R Markdown Notebook are licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.

Pengantar

Materi pada R Markdown Notebook ini dibawakan pada R-Ladies Jakarta 15th meetup (2 April 2022, 1400-1600 WIB) dengan tema Basic text manipulation with stringr (Rajeg 2022a). Jika materi ini digunakan kembali, menjadi dasar, ataupun dikutip untuk keperluan lokakarya dan hal-hal lainnya, mohon mengutip materi ini sebagai berikut:

Rajeg, Gede Primahadi Wijaya. 2022. Pengolahan teks dasar dengan R menggunakan stringr. Open Science Framework (OSF). doi: 10.17605/OSF.IO/9ZHDJ. https://osf.io/9zhdj/ (2 April, 2022).

Dan juga salindia pembuka sebelum pelaksanaan lokakarya (Rajeg 2022b):

Rajeg, Gede Primahadi Wijaya. 2022. Basic text manipulation with stringr. OSF Preprints. doi: 10.31219/osf.io/q9yva. https://osf.io/q9yva/ (2 April, 2022).

Pembaca disarankan mempelajari langsung dua sumber utama terkait stringr, yaitu (i) buku teks oleh Wickham dan Grolemund (2017), utamanya Bab 14, dan (ii) cheatsheet PDF untuk fungsi-fungsi kunci dalam stringr. Bacaan lain yang mungkin bisa dilihat adalah makalah kami (Rajeg, Denistia & Rajeg 2018) yang salah satu bagiannya membahas pengolahan teks dasar menggunakan sejumlah fungsi pengolahan teks dari instalasi dasar R (bukan menggunakan stringr) (kode sumber makalah tersebut dapat diakses melalui tautan berikut atau berikut).

1 Persiapan

# global option chunck
knitr::opts_chunk$set(echo = TRUE, include = TRUE, message = FALSE, warning = FALSE)
# install package stringr
install.packages("stringr")

Aktifkan modul/package stringr.

# load the packages
library(stringr)

Muat data teks berupa kalimat dan kata-kata.

eng_sentence_all <- scan(file = "data/eng_stringr_sentences_all.txt", what = "char", sep = "\n", quiet = TRUE)

length(eng_sentence_all) # ada 720 elemen kalimat
[1] 720
sample(eng_sentence_all, size = 3) # tampilkan 3 kalimat acak saja
[1] "The beach is dry and shallow at low tide."      
[2] "Leaves turn brown and yellow in the fall."      
[3] "A whiff of it will cure the most stubborn cold."
words <- stringr::words # data kata (*words*) dari package stringr
sample(words, size = 3)
[1] "self"    "example" "care"   
fruits <- stringr::fruit # data kata-kata nama buah (*fruits*) dari package stringr
sample(fruits, size = 3)
[1] "satsuma"    "breadfruit" "rock melon"

2 Pencarian dan pendeteksian (rangkaian) karakter

2.1 Menguji pola pencarian dan hasil yang ditangkap dengan str_view_all()

Fungsi str_view_all() memerlukan kita menginstall package htmlwidgets. Jika tidak, error akan muncul seperti pada gambar di bawah ini.

# install htmlwidgets package
install.packages("htmlwidgets")

Kode berikut mencoba menampilkan keberadaan rangkaian karakater "ze" dalam vektor teks eng_sentence_all.

str_view_all(string = eng_sentence_all, pattern = "ze", match = TRUE)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Latihan

  1. Ubah parameter argumen match = TRUE menjadi match = FALSE, lalu jalankan kode sebelumnya dengan pola pencarian "ze". Apa yang terjadi pada luarannya? Apa fungsi dari match = FALSE?
  2. Gunakan str_view_all() untuk menampilkan rangkaian karakter "ly" pada vektor teks eng_sentence_all.
  3. Bagaimana pola pencarian untuk menampilkan rangkaian karakter "ly" yang diikuti dengan spasi (whitespace)?

2.2 Mendeteksi keberadaan pola rangkaian karakter dengan str_detect()

Fungsi str_detect() menghasilkan luaran logis TRUE dan FALSE yang menyatakan apakah rangkaian karakter yang kita cari ditemukan (TRUE) atau tidak (FALSE) pada suatu vektor teks/karakter.

# buat 50 sampel kalimat sederhana
eng_sentence_all_sample <- sample(x = eng_sentence_all, size = 50)

# apakah rangkaian karakter "ed" ditemukan dalam `eng_sentence_all_sample`?
str_detect(string = eng_sentence_all_sample, pattern = "ed")
 [1] FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE  TRUE
[14]  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE
[27] FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE  TRUE FALSE  TRUE FALSE  TRUE FALSE
[40] FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE FALSE FALSE

Latihan

  1. Gunakan str_view_all() untuk menampilkan rangkaian karakter "ed" pada vektor teks eng_sentence_all_sample.
  2. Apakah rangkaian karakter konsonan "ck" ditemukan dalam nama-nama buah pada vektor teks fruits?
    • Jika ya, gunakan str_view_all() untuk menampilkan nama buah apa yang mengandung rangkaian karakter konsonan "ck" pada vektor teks fruits.

Fungsi str_detect() juga dapat digunakan untuk menyaring observasi yang diinginkan dalam tabel (data frame) berdasarkan rangkaian karakter tertentu. Ini dimungkinkan karena luaran str_detect() berupa struktur logis TRUE dan FALSE yang menjadi landasan dalam menyaring observasi, salah satunya dalam tabel.

# muat data tabel hanya kolom 4 - 7
verbs_idn <- read.table(file = "data/verb_semantics.txt", header = TRUE, sep = "\t")[4:7]

# lihat enam baris pertama
head(verbs_idn)

Kode berikut menyaring observasi (subset()) dalam tabel data verbs_idn berdasarkan apakah kolom example_target mengandung teks (yaitu kata kerja ber-) dengan rangkaian karakter "da" (lihat Rajeg 2013 untuk makalah yang dilandasi atas data tabel tersebut).

subset(verbs_idn, str_detect(string = example_target, pattern = "da"))

Latihan

  1. Ubah kode sebelumnya untuk menyaring/mendeteksi kata-kata dalam kolom example_source yang mengandung rangkaian karakter "ar".

2.3 Menyaring elemen vektor teks yang mengandung rangkaian karakter tertentu dengan str_subset()

Berbeda dengan str_detect(), yang mengeluarkan TRUE dan FALSE, str_subset() (i) mendeteksi dan (ii) mengeluarkan elemen dalam vektor teks yang mengandung rangkaian karakter yang dicari.

str_subset(string = eng_sentence_all_sample, pattern = "ing")
[1] "Wood is best for making toys and blocks."         
[2] "See the cat glaring at the scared mouse."         
[3] "The map had an X that meant nothing."             
[4] "The first part of the plan needs changing."       
[5] "The source of the huge river is the clear spring."
[6] "Clothes and lodging are free to new men."         
[7] "It takes heat to bring out the odor."             

Tentu kita bisa simpan hasil penyaringan ini ke vektor lain.

# simpan luaran kode berikut ke vektor `ing_output`
ing_output <- str_subset(string = eng_sentence_all_sample, pattern = "ing")
ing_output
[1] "Wood is best for making toys and blocks."         
[2] "See the cat glaring at the scared mouse."         
[3] "The map had an X that meant nothing."             
[4] "The first part of the plan needs changing."       
[5] "The source of the huge river is the clear spring."
[6] "Clothes and lodging are free to new men."         
[7] "It takes heat to bring out the odor."             

Latihan

  1. Buah apa yang namanya mengandung karakter "ck"? Gunakan str_subset() pada vektor teks fruits untuk menjawab pertanyaan ini.
  2. Gunakan str_subset() untuk menyaring kalimat dalam vektor eng_sentence_all yang mengandung rangkaian karakter "who". Apakah pola pencarian "who" ini mengeluarkan kalimat yang mengandung kata who ‘siapa’ dalam bahasa Inggris?

Catatan tentang regular expressions

Regular expressions (RegEx) adalah pola pencarian karakter yang lebih canggih dan fleksibel yang perlu dipelajari untuk pengolahan dan manipulasi teks tingkat lanjut (lihat Wickham & Grolemund 2017, sub-bagian 14.3 untuk ulasan lebih rinci).

Salah satu jenis ReGex yang penting diketahui adalah pewatas rangkaian karakter yang mencerminkan kata (word boundary) ("\\b"). Untuk kasus pencarian rangkaian karakter "who" sebelumnya, kita melakukan pencarian dengan rangkaian karakter tanpa RegEx karena kita tidak memperinci bahwa karakter "who" yang kita inginkan adalah kata who bukannya rangkaian karakter "who" yang bisa muncul dalam kata whole, whoever, dll. Untuk itu, pola pencarian kita untuk “kata” who (bukan rangkaian karakter "who") haruslah lebih spesifik dan karakter RegEx pewatas kata ("\\b") dapat membantu dalam hal ini, yaitu "\\bwho\\b".

Bandingkan luaran kedua baris kode berikut untuk pola pencarian dengan dan tanpa RegEx pewatas kata ("\\b"):

# tanpa regex pewatas kata "\\b" di kedua sisi karakter yang dicari
str_subset(string = eng_sentence_all, pattern = "who") 
[1] "Thieves who rob friends deserve jail."           
[2] "Find the twin who stole the pearl necklace."     
[3] "He took the lead and kept it the whole distance."
[4] "The facts don\u0092t always show who is right."  
# tanpa regex pewatas kata "\\b" di kedua sisi karakter yang dicari
str_view_all(string = eng_sentence_all, pattern = "who", match = TRUE)
# dengan regex pewatas kata "\\b" di kedua sisi karakter yang dicari
str_subset(string = eng_sentence_all, pattern = "\\bwho\\b") 
[1] "Thieves who rob friends deserve jail."         
[2] "Find the twin who stole the pearl necklace."   
[3] "The facts don\u0092t always show who is right."
# dengan regex pewatas kata "\\b" di kedua sisi karakter yang dicari
str_view_all(string = eng_sentence_all, pattern = "\\bwho\\b", match = TRUE)

Latihan

  1. Gunakan RegEx pewatas kata ("\\b") dengan str_subset() untuk menyaring kalimat dalam vektor eng_sentence_all yang mengandung kata-kata yang diakhiri dengan rangkaian karakter "ing" (misalnya, kata-kata seperti texting, living, ring, spring, dll.)
  2. Gunakan str_subset() dan pola pencarian dengan RegEx pewatas kata ("\\b") untuk mengeluarkan kata-kata yang diawali dengan karakter "ch" (misalnya chance, chap, dll.) dalam vektor teks words.

Catatan tentang regular expressions

Selain pewatas rangkaian karakter berupa kata, terdapat dua lagi ReGex pewatas rangkaian karakter yang penting diketahui, yaitu pewatas rangkaian karakter secara umum (anchor character): (i) pewatas posisi awal rangkaian karakter ("^") dan (ii) pewatas posisi akhir rangkaian karakter ("$").

Perhatikan kode berikut sebagai contoh penggunaan RegEx pewatas awal dan akhir rangkaian karakter (dikutip dari Wickham & Grolemund 2017:202):

# buat data sederhana
fruit_mini <- c("apple", "banana", "pear")

# menangkap karakter `"a"` yang muncul di akhir (`"$"`) rangkaian karakter nama buah pada vektor `fruit_mini`
str_view_all(string = fruit_mini, pattern = "a$")
str_subset(string = fruit_mini, pattern = "a$")
[1] "banana"
# menangkap karakter `"a"` yang muncul di akhir (`"$"`) rangkaian karakter nama buah pada vektor `fruit_mini`
str_view_all(string = fruit_mini, pattern = "^a")
str_subset(string = fruit_mini, pattern = "^a")
[1] "apple"

Kita bisa menggabungkan "^" dan "$" untuk mempertegas bahwa kita hanya ingin menangkap rangkaian karakter secara utuh. Bandingkan kedua kode berikut (dikutip dari Wickham & Grolemund 2017:202):

# buat data lain
fruit_cake <- c("apple pie", "apple", "apple cake")

# pencarian sederhana (tanpa RegEx) untuk menangkap rangkaian karakter `"apple"`
str_view_all(string = fruit_cake, pattern = "apple")
str_subset(string = fruit_cake, pattern = "apple")
[1] "apple pie"  "apple"      "apple cake"
# menangkap hanya rangkaian karakter utuh dari awal hingga akhir rangkaian karakter
str_view_all(string = fruit_cake, pattern = "^apple$")
str_subset(string = fruit_cake, pattern = "^apple$")
[1] "apple"

Latihan

  1. Buah apa saja yang namanya diawali dengan karakter konsonan "b"? Gunakan str_subset() untuk mengeluarkan dari vektor fruits nama-nama buah yang diawali dengan karakter "b".

  2. Ada berapa buah yang namanya berakhir dengan karakter vokal "o"? Gunakan str_subset() untuk mengeluarkan dari vektor fruits nama-nama buah yang diawali dengan karakter "o".

Tips

stringr juga memiliki dua fungsi khusus untuk mendeteksi keberadaan (rangkaian) karakter yang muncul (i) di awal rangkaian karakter, yaitu fungsi str_starts(), dan (ii) di akhir rangkaian karakter, yaitu fungsi str_ends(). Layaknya str_detect(), kedua fungsi ini mengeluarkan vektor logis TRUE dan FALSE.

Misalnya, str_starts(fruit_mini, "p") akan mendeteksi adakah elemen teks dalam vektor fruit_mini yang diawali dengan karakter "p", tanpa perlu menggunakan RegEx pewatas karakter awal "^". Sebaliknya, str_ends(fruit_mini, "p") akan mendeteksi keberadaan teks dalam fruit_mini yang diakhiri dengan karakter "p", tanpa perlu menggunakan RegEx pewatas karakter akhir "$".

RegEx tetap diperlukan jika menggunakan fungsi str_subset() untuk menyaring teks yang diawali atau diakhiri dengan (rangkaian) karakter tertentu.


3 Mengeluarkan (extract) pola karakter dari dalam teks

Fungsi str_extract_all() digunakan untuk “mengeluarkan” (extract) pola yang ditangkap dari pencarian karakter yang kita rancang. Sebagai contoh, vektor kalimat eng_sentence_all terdapat sejumlah nama-nama warna bahasa Inggris dan kita hanya ingin mengeluarkan kata-kata yang mencerminkan nama warna tersebut (tidak kata-kata lainnya dalam kalimat). Kita memerlukan karakter RegEx bernama “alternatif” (|) yang berarti ‘atau’. Jika kita ingin mencari warna "x" atau "y" atau "z", maka pola pencarian kita adalah "(x|y|z)".

Pertama kita rampingkan data kalimat eng_sentence_all dengan menyaring hanya kalimat-kalimat yang mengandung nama-nama warna.

# buat vektor karakter untuk regex pencarian dengan teknik "alternatif"
colour_pattern <- "(blue|orange|yellow|green|purple)" 

# deteksi dan saring terlebih dahulu hanya kalimat yang berisi nama-nama warna "biru", "oranye", "kuning", "hijau", dan "ungu".
sent_colour <- str_subset(string = eng_sentence_all, pattern = colour_pattern)

# cari tahu berapa kalimat yang mengandung nama-nama warna
length(sent_colour) # 20 kalimat mengandung nama-nama warna tersebut
[1] 20

Baru kita keluarkan kata-kata nama warna dari vektor karakter sent_colour dan simpan luarannya ke vektor karakter lainnya dengan nama word_colour.

# keluarkan nama-nama warna dari vektor `sent_colour` dan simpan ke vektor `word_colour`
word_colour <- str_extract_all(string = sent_colour, pattern = colour_pattern) 

# lihat enam elemen pertama
head(word_colour) # luaran berupa List bukan Vektor karakter (atomis)
[[1]]
[1] "blue"

[[2]]
[1] "blue"

[[3]]
[1] "blue"

[[4]]
[1] "yellow"

[[5]]
[1] "green"

[[6]]
[1] "blue"
word_colour_vector <- unlist(word_colour) # unlist menjadi vektor karakter
word_colour_vector
 [1] "blue"   "blue"   "blue"   "yellow" "green"  "blue"   "blue"   "blue"   "green" 
[10] "green"  "green"  "purple" "green"  "blue"   "blue"   "green"  "green"  "green" 
[19] "yellow" "orange"

Gunakan str_sort() untuk mengurutkan karakter secara alfabetis.

str_sort(word_colour_vector)
 [1] "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "blue"   "green" 
[10] "green"  "green"  "green"  "green"  "green"  "green"  "green"  "orange" "purple"
[19] "yellow" "yellow"

Kita bisa gunakan table() untuk mentabulasi karakter vektor nama warna untuk mengetahui distribusi/frekuensi kemunculan warna tersebut.

table(word_colour_vector)
word_colour_vector
  blue  green orange purple yellow 
     8      8      1      1      2 
# sortir nama warna dari frekuensi tertinggi ke terendah
word_colour_count <- table(word_colour_vector)
word_colour_count <- sort(word_colour_count, decreasing = TRUE)
word_colour_count
word_colour_vector
  blue  green yellow orange purple 
     8      8      2      1      1 

4 Memecah teks menjadi unit yang lebih kecil

Fungsi str_split() digunakan untuk memecah teks berdasarkan pada karakter mana teks tersebut dipecah. Contoh paling sederhana adalah memecah kalimat menjadi kata dengan memecahnya pada karakter spasi (yang merupakan pemisah kata untuk karakter Roman yang dipahami oleh komputer).

# contoh sederhana pemecahan kalimat -> kata dengan dua kalimat 
two_sentences <- eng_sentence_all_sample[1:2]

# pecah teks pada karakter spasi `" "`.
str_split(two_sentences, pattern = " ") # luarannya adalah List
[[1]]
 [1] "The"     "weight." "of"      "the"     "package" "was"     "seen"    "on"     
 [9] "the"     "high"    "scale." 

[[2]]
[1] "Tear"   "a"      "thin"   "sheet"  "from"   "the"    "yellow" "pad."  

Isunya adalah tanda baca seperti titik (.) masih menempel pada kata yang mengakhiri kalimat. Pada situasi inilah kekuatan dari RegEx menjadi penting.

Kita perlu merancang pola pencarian RegEx yang menyatakan bahwa kita ingin memisahkan/memecah teks pada kelompok karakter selain kelompok karakter pembentuk kata, yaitu kelompok karakter di luar karakter alfanumeris dan strip "-". Dalam konteks bahasa Inggris, "-" dipertahankan untuk menangkap kata seperti computer-readable sebagai satu unit, bukannya menjadi dua unit computer dan readable.

RegEx yang bisa digunakan adalah "[^a-zA-Z0-9-]+", yang artinya: satu atau lebih ("+") kelompok karakter ("[...]") yang bukan ("^") (i) alfanumeris kapital dan kecil ("a-zA-Z0-9") dan strip ("-").

# pecah teks pada karakter non-alfanumeris dan spasi `" "`.
str_split(two_sentences, pattern = "[^a-zA-Z0-9-]+")
[[1]]
 [1] "The"     "weight"  "of"      "the"     "package" "was"     "seen"    "on"     
 [9] "the"     "high"    "scale"   ""       

[[2]]
[1] "Tear"   "a"      "thin"   "sheet"  "from"   "the"    "yellow" "pad"    ""      

Tentu luaran List ini bisa kita ubah menjadi vektor atomis:

split_sentence <- str_split(two_sentences, pattern = "[^a-zA-Z0-9-]+")
unlist(split_sentence)
 [1] "The"     "weight"  "of"      "the"     "package" "was"     "seen"    "on"     
 [9] "the"     "high"    "scale"   ""        "Tear"    "a"       "thin"    "sheet"  
[17] "from"    "the"     "yellow"  "pad"     ""       

Pemecahan teks dari kalimat menjadi kata ini dapat dilihat dari dua perspektif:

  1. memecah (yaitu memisahkan dengan menghilangkan) karakter selain alfanumeris dan strip, menggunakan str_split(), atau

  2. menarik (yaitu mengeluarkan/mengekstraksi) karakter yang membangun suatu kata, yaitu karakter alfanumeris dan strip, menggunakan str_extract_all().

Jika kita mengambil perspektif kedua (dengan str_extract_all()), maka RegEx yang digunakan sedikit berbeda, yaitu dengan menghilangkan tanda negasi (‘bukan’) "^" dalam RegEx-nya. Perhatikan kode berikut dan bandingkan dengan teliti luaran str_extract_all() dengan luaran str_split() sebelumnya.

# perspektif kedua memecah kalimat menjadi kata
# dengan **mengkekstraksi** kata-kata
str_extract_all(two_sentences, pattern = "[a-zA-Z0-9-]+")
[[1]]
 [1] "The"     "weight"  "of"      "the"     "package" "was"     "seen"    "on"     
 [9] "the"     "high"    "scale"  

[[2]]
[1] "Tear"   "a"      "thin"   "sheet"  "from"   "the"    "yellow" "pad"   

Yang membedakan luaran str_split() dan str_extract_all() di atas, khususnya dalam konteks memecah teks kalimat menjadi kata adalah:

  1. luaran str_split() menandai penghilangan/pemecahan tanda baca, dalam hal ini titik, dengan karakter kosong (lihat karakter "" setelah kata "ends" dan "enough").

  2. luaran str_extract_all() hanya akan mengeluarkan/mengekstraksi karakter yang dinyatakan dalam pola pencariannya, yaitu satu atau lebih karakter alfanumeris dan strip; tidak ada karakter kosong yang mencerminkan dalam hal ini tanda titik.

Kita yang menentukan fungsi dan luaran seperti apa yang diinginkan.

5 Mengubah komponen teks

5.1 Mengubah karakter menjadi huruf besar atau kecil

Fungsi str_to_upper() mengubah karakter menjadi huruf kapital dan str_to_lower() menjadi huruf kecil.

# mengubah nama warna sebelumnya menjadi huruf kapital
word_colour_caps <- str_to_upper(string = word_colour_vector)
word_colour_caps
 [1] "BLUE"   "BLUE"   "BLUE"   "YELLOW" "GREEN"  "BLUE"   "BLUE"   "BLUE"   "GREEN" 
[10] "GREEN"  "GREEN"  "PURPLE" "GREEN"  "BLUE"   "BLUE"   "GREEN"  "GREEN"  "GREEN" 
[19] "YELLOW" "ORANGE"
# mengubah nama warna kapital menjadi huruf kecil
str_to_lower(string = word_colour_caps)
 [1] "blue"   "blue"   "blue"   "yellow" "green"  "blue"   "blue"   "blue"   "green" 
[10] "green"  "green"  "purple" "green"  "blue"   "blue"   "green"  "green"  "green" 
[19] "yellow" "orange"

Latihan

  1. Lihat isi vektor two_sentences pada konsol dan perhatikan bentuk rangkaian karakternya. Kemudian jalankan fungsi str_to_title() dengan input two_sentences. Perubahan apa yang terjadi dan apa yang dilakukan oleh str_to_title()?

5.2 Mengganti komponen teks dengan karakter lain

Terkadang kita perlu mengubah komponen teks tertentu layaknya fungsi find and replace pada peranti seperti MS Word. Dalam stringr, kita bisa menggunakan str_replace() dan str_replace_all().

Varian dasar tanpa ..._all(), yaitu str_replace(), hanya akan mengganti karakter yang pertama kali ditangkap oleh pola pencariannya, sedangkan karakter yang sama yang muncul setelahnya tidak akan ditangkap dan diganti.

Perhatikan kode berikut yang mencoba mengganti karakter "e" pada vektor fruit_cake.

# lihat kembali vektor `fruit_cake`
fruit_cake
[1] "apple pie"  "apple"      "apple cake"
# gunakan str_replace() untuk mengganti karakter `"e"` yang ditemukan pertama dengan strip `"-"`
str_replace(string = fruit_cake, pattern = "e", replacement = "-")
[1] "appl- pie"  "appl-"      "appl- cake"
# gunakan str_replace_all() untuk mengganti SEMUA karakter `"e"` dengan strip `"-"`
str_replace_all(string = fruit_cake, pattern = "e", replacement = "-")
[1] "appl- pi-"  "appl-"      "appl- cak-"

Latihan

  1. Ganti rangkaian karakter untuk kata "apple" pada vektor fruit_cake (yang berisi "apple pie", "apple", dan "apple cake") dengan kata "butter".

6 Menggabungkan vektor rangkaian karakter

Untuk menggabungkan dua atau lebih rangkaian karakter, gunakan str_c().

# menggabungkan dua rangkaian karakter: "ba" dan "li"
str_c("ba", "li")
[1] "bali"
karakter_1 <- c("b", "l", "k")
karakter_2 <- c("a", "i", "u")

# gabungkan tiga karakter dari dua vektor
str_c(karakter_1, karakter_2)
[1] "ba" "li" "ku"
# gabungkan dan apit tiga karakter dalam satu vektor dengan dua karakter lain yaitu "<m>"
str_c("<m>", karakter_1, "<m>")
[1] "<m>b<m>" "<m>l<m>" "<m>k<m>"

Jika salah satu dari dua vektor karakter yang akan digabung memiliki jumlah elemen yang lebih sedikit, maka elemen pada vektor tersebut akan digunakan ulang. Perhatikan contoh berikut.

two_word_fruit <- c("apple", "banana")
four_word_snack <- c("cake", "juice", "pie", "chocolate")

# gabungkan kedua vektor di atas
str_c(two_word_fruit, four_word_snack, sep = "_")
[1] "apple_cake"       "banana_juice"     "apple_pie"        "banana_chocolate"

Gunakan argumen sep = ... untuk mengatur pemisah di antara rangkaian karakter yang digabungkan. Secara bawaan, argumen sep = ... telah diatur menjadi sep = "".

# atur pemisah di antara karakter yang digabungkan dengan `sep = ...`
str_c(karakter_1, karakter_2, sep = "_")
[1] "b_a" "l_i" "k_u"

Argumen collapse = ... akan menyatukan sejumlah rangkaian karakter menjadi satu rangkaian karakter. Bandingkan kedua kode berikut yang menggunakan sep = ... dan collapse = ...:

# dengan sep = ..., penggabungan berikut menghasilkan tiga elemen rangkaian karakter
three_strings <- str_c("<m>", karakter_1, "<m>", sep = "_")
three_strings
[1] "<m>_b_<m>" "<m>_l_<m>" "<m>_k_<m>"
# Hitung jumlah elemen dalam vektor `three_strings`
length(three_strings)
[1] 3
# dengan collapse = ..., penggabungan berikut menghasilkan satu elemen rangkaian karakter
one_string <- str_c("<m>", karakter_1, "<m>", collapse = "_")
one_string
[1] "<m>b<m>_<m>l<m>_<m>k<m>"
# Hitung jumlah elemen dalam vektor `one_string`
length(one_string)
[1] 1

7 Menyimpan vektor karakter ke dalam berkas teks (plain text)

Gunakan fungsi cat() yang tersedia dari instalasi dasar R untuk menyimpan vektor karakter ke dalam berkas teks sederhana (plain text dengan ekstensi .txt).

# simpan vektor nama warna ke berkas `"selected-colours.txt"`
# dalam direktori `results`
# yang masing-masing elemen vektornya dipisahkan dengan baris baru (`sep="\n"`)
cat(word_colour_vector, file = "results/selected-colours.txt", sep = "\n")

Session info

devtools::session_info()
─ Session info ─────────────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.1.3 (2022-03-10)
 os       macOS Monterey 12.1
 system   aarch64, darwin20
 ui       RStudio
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       Asia/Makassar
 date     2022-04-02
 rstudio  2022.02.0+443 Prairie Trillium (desktop)
 pandoc   NA

─ Packages ─────────────────────────────────────────────────────────────────────────
 package     * version date (UTC) lib source
 brio          1.1.3   2021-11-30 [1] CRAN (R 4.1.1)
 cachem        1.0.6   2021-08-19 [1] CRAN (R 4.1.1)
 callr         3.7.0   2021-04-20 [1] CRAN (R 4.1.0)
 cli           3.2.0   2022-02-14 [1] CRAN (R 4.1.1)
 crayon        1.5.0   2022-02-14 [1] CRAN (R 4.1.1)
 desc          1.4.1   2022-03-06 [1] CRAN (R 4.1.1)
 devtools      2.4.3   2021-11-30 [1] CRAN (R 4.1.1)
 digest        0.6.29  2021-12-01 [1] CRAN (R 4.1.1)
 ellipsis      0.3.2   2021-04-29 [1] CRAN (R 4.1.0)
 fastmap       1.1.0   2021-01-25 [1] CRAN (R 4.1.0)
 fs            1.5.2   2021-12-08 [1] CRAN (R 4.1.1)
 glue          1.6.2   2022-02-24 [1] CRAN (R 4.1.1)
 htmltools     0.5.2   2021-08-25 [1] CRAN (R 4.1.1)
 htmlwidgets   1.5.4   2021-09-08 [1] CRAN (R 4.1.1)
 jsonlite      1.8.0   2022-02-22 [1] CRAN (R 4.1.1)
 knitr         1.37    2021-12-16 [1] CRAN (R 4.1.1)
 lifecycle     1.0.1   2021-09-24 [1] CRAN (R 4.1.1)
 magrittr      2.0.2   2022-01-26 [1] CRAN (R 4.1.1)
 memoise       2.0.1   2021-11-26 [1] CRAN (R 4.1.1)
 pkgbuild      1.3.1   2021-12-20 [1] CRAN (R 4.1.1)
 pkgload       1.2.4   2021-11-30 [1] CRAN (R 4.1.1)
 prettyunits   1.1.1   2020-01-24 [1] CRAN (R 4.1.0)
 processx      3.5.2   2021-04-30 [1] CRAN (R 4.1.0)
 ps            1.6.0   2021-02-28 [1] CRAN (R 4.1.0)
 purrr         0.3.4   2020-04-17 [1] CRAN (R 4.1.0)
 R6            2.5.1   2021-08-19 [1] CRAN (R 4.1.1)
 remotes       2.4.2   2021-11-30 [1] CRAN (R 4.1.1)
 rlang         1.0.2   2022-03-04 [1] CRAN (R 4.1.1)
 rprojroot     2.0.2   2020-11-15 [1] CRAN (R 4.1.0)
 sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.1.1)
 stringi       1.7.6   2021-11-29 [1] CRAN (R 4.1.1)
 stringr     * 1.4.0   2019-02-10 [1] CRAN (R 4.1.1)
 testthat      3.1.3   2022-03-29 [1] CRAN (R 4.1.1)
 usethis       2.1.5   2021-12-09 [1] CRAN (R 4.1.1)
 withr         2.5.0   2022-03-03 [1] CRAN (R 4.1.1)
 xfun          0.30    2022-03-02 [1] CRAN (R 4.1.1)
 yaml          2.3.5   2022-02-21 [1] CRAN (R 4.1.1)

 [1] /Library/Frameworks/R.framework/Versions/4.1-arm64/Resources/library

────────────────────────────────────────────────────────────────────────────────────

Daftar acuan

Rajeg, Gede Primahadi Wijaya. 2013. Metonymy in Indonesian Prefixal Word-formation. Lingual: Journal of Language and Culture 1. 64–81. doi:10.26180/5b6e403959120.
Rajeg, Gede Primahadi Wijaya. 2022a. Pengolahan teks dasar dengan R menggunakan stringr. Open Science Framework (OSF). doi:10.17605/OSF.IO/9ZHDJ. https://osf.io/9zhdj/ (2 April, 2022).
Rajeg, Gede Primahadi Wijaya. 2022b. Basic text manipulation with stringr. OSF Preprints. doi:10.31219/osf.io/q9yva. https://osf.io/q9yva/ (2 April, 2022).
Rajeg, Gede Primahadi Wijaya, Karlina Denistia & I Made Rajeg. 2018. Working with a linguistic corpus using R: An introductory note with Indonesian negating construction. Linguistik Indonesia 36(1). 1–36. doi:10.26499/li.v36i1.71.
Wickham, Hadley & Garrett Grolemund. 2017. R for Data Science. Canada: O’Reilly. http://r4ds.had.co.nz/ (7 March, 2017).
LS0tCmF1dGhvcjogCi0gbmFtZTogJ1tHZWRlIFByaW1haGFkaSBXaWpheWEgUmFqZWddKGh0dHBzOi8vdWRheWFuYW5ldHdvcmtpbmcudW51ZC5hYy5pZC9sZWN0dXJlci84ODAtZ2VkZS1wcmltYWhhZGktd2lqYXlhLXJhamVnKSA8YSBpdGVtcHJvcD0ic2FtZUFzIiBjb250ZW50PSJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItMjA0Ny04NjIxIiBocmVmPSJodHRwczovL29yY2lkLm9yZy8wMDAwLTAwMDItMjA0Ny04NjIxIiB0YXJnZXQ9Im9yY2lkLndpZGdldCIgcmVsPSJub29wZW5lciBub3JlZmVycmVyIiBzdHlsZT0idmVydGljYWwtYWxpZ246dG9wOyI+PGltZyBzcmM9Imh0dHBzOi8vb3JjaWQub3JnL3NpdGVzL2RlZmF1bHQvZmlsZXMvaW1hZ2VzL29yY2lkXzE2eDE2LnBuZyIgc3R5bGU9IndpZHRoOjFlbTttYXJnaW4tcmlnaHQ6LjVlbTsiIGFsdD0iT1JDSUQgaUQgaWNvbiI+PC9hPicKICBhZmZpbGlhdGlvbjogIlByb2dyYW0gU3R1ZGkgW1Nhc3RyYSBJbmdncmlzXShodHRwczovL3R3aXR0ZXIuY29tL3Nhc2luZ3VudWQ/bGFuZz1lbikgICYgKkNlbnRyZSBmb3IgSW50ZXJkaXNjaXBsaW5hcnkgUmVzZWFyY2ggb24gdGhlIEh1bWFuaXRpZXMgYW5kIFNvY2lhbCBTY2llbmNlcyogKFtDSVJIU1NdKGh0dHA6Ly93d3cuY2lyaHNzLm9yZykpLCBbRmFrdWx0YXMgSWxtdSBCdWRheWFdKGh0dHBzOi8vZmliLnVudWQuYWMuaWQpLCBbVW5pdmVyc2l0YXMgVWRheWFuYV0oaHR0cHM6Ly93d3cudW51ZC5hYy5pZCkiCmRhdGU6ICIqQ3JlYXRlZCo6IDEgQXByaWwsIDIwMjI7ICpMYXN0IHVwZGF0ZSo6IGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgp0aXRsZTogIlBlbmdvbGFoYW4gdGVrcyBkYXNhciBkZW5nYW4gW1JdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcpIG1lbmdndW5ha2FuIFtzdHJpbmdyXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy9pbmRleC5odG1sKSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBmaWdfd2lkdGg6IDYKICAgIHRoZW1lOiAidW5pdGVkIgogICAgaGlnaGxpZ2h0OiAicHlnbWVudCIKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNvZGVfZm9sZGluZzogc2hvdwpsaW5rLWNpdGF0aW9uczogeWVzCmJpYmxpb2dyYXBoeTogInJlZmVyZW5jZXMuYmliIgpjc2w6IHVuaWZpZWRfc3R5bGVzaGVldF9saW5ndWlzdGljcy5jc2wKLS0tCgo8c3R5bGU+CgogIHAuY29tbWVudCB7CiAgYmFja2dyb3VuZC1jb2xvcjogI0U1RkZDQzsKICBwYWRkaW5nOiAxMHB4OwogIGJvcmRlcjogMXB4IHNvbGlkIGJsYWNrOwogIG1hcmdpbi1sZWZ0OiAyNXB4OwogIGJvcmRlci1yYWRpdXM6IDVweDsKfQoKPC9zdHlsZT4KCgo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLXNhLzQuMC8iPjxpbWcgYWx0PSJDcmVhdGl2ZSBDb21tb25zIExpY2Vuc2UiIHN0eWxlPSJib3JkZXItd2lkdGg6MCIgc3JjPSJodHRwczovL2kuY3JlYXRpdmVjb21tb25zLm9yZy9sL2J5LW5jLXNhLzQuMC84OHgzMS5wbmciIC8+PC9hPiBbIVtdKGh0dHBzOi8vaW1nLnNoaWVsZHMuaW8vYmFkZ2UvZG9pLTEwLjE3NjA1L09TRi5JTy85WkhESi1saWdodGJsdWUuc3ZnKV0oaHR0cHM6Ly9kb2kub3JnLzEwLjE3NjA1L09TRi5JTy85WkhESikgPGJyIC8+PGk+VGhlIG1hdGVyaWFscyBpbiB0aGlzIFIgTWFya2Rvd24gTm90ZWJvb2sgYXJlIGxpY2Vuc2VkIHVuZGVyIGEgPGEgcmVsPSJsaWNlbnNlIiBocmVmPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1zYS80LjAvIj5DcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbkNvbW1lcmNpYWwtU2hhcmVBbGlrZSA0LjAgSW50ZXJuYXRpb25hbCBMaWNlbnNlPC9hPjwvaT4uCgojIFBlbmdhbnRhciB7LnVubnVtYmVyZWR9CgpNYXRlcmkgcGFkYSBSIE1hcmtkb3duIE5vdGVib29rIGluaSBkaWJhd2FrYW4gcGFkYSBbKlItTGFkaWVzIEpha2FydGEgMTUqXip0aCpeICptZWV0dXAqXShodHRwczovL3VkYXlhbmFuZXR3b3JraW5nLnVudWQuYWMuaWQvbGVjdHVyZXIvZ2FsbGVyeS9waG90by84ODAtZ2VkZS1wcmltYWhhZGktd2lqYXlhLXJhamVnLzEzMTgpICgyIEFwcmlsIDIwMjIsIDE0MDAtMTYwMCBXSUIpIGRlbmdhbiB0ZW1hICpCYXNpYyB0ZXh0IG1hbmlwdWxhdGlvbiB3aXRoIGBzdHJpbmdyYCogW0ByYWplZ19wZW5nb2xhaGFuXzIwMjJdLiBKaWthIG1hdGVyaSBpbmkgZGlndW5ha2FuIGtlbWJhbGksIG1lbmphZGkgZGFzYXIsIGF0YXVwdW4gZGlrdXRpcCB1bnR1ayBrZXBlcmx1YW4gbG9rYWthcnlhIGRhbiBoYWwtaGFsIGxhaW5ueWEsIG1vaG9uIG1lbmd1dGlwIG1hdGVyaSBpbmkgc2ViYWdhaSBiZXJpa3V0OgoKPiBSYWplZywgR2VkZSBQcmltYWhhZGkgV2lqYXlhLiAyMDIyLiBQZW5nb2xhaGFuIHRla3MgZGFzYXIgZGVuZ2FuIFIgbWVuZ2d1bmFrYW4gc3RyaW5nci4gKk9wZW4gU2NpZW5jZSBGcmFtZXdvcmsgKE9TRikqLiBkb2k6IFsxMC4xNzYwNS9PU0YuSU8vOVpIREpdKGh0dHBzOi8vZG9pLm9yZy8xMC4xNzYwNS9PU0YuSU8vOVpIREopLiBodHRwczovL29zZi5pby85emhkai8gKDIgQXByaWwsIDIwMjIpLgoKRGFuIGp1Z2Egc2FsaW5kaWEgcGVtYnVrYSBzZWJlbHVtIHBlbGFrc2FuYWFuIGxva2FrYXJ5YSBbQHJhamVnX2Jhc2ljXzIwMjJdOgoKPiBSYWplZywgR2VkZSBQcmltYWhhZGkgV2lqYXlhLiAyMDIyLiAqQmFzaWMgdGV4dCBtYW5pcHVsYXRpb24gd2l0aCBzdHJpbmdyKi4gT1NGIFByZXByaW50cy4gZG9pOiBbMTAuMzEyMTkvb3NmLmlvL3E5eXZhXShodHRwczovL2RvaS5vcmcvMTAuMzEyMTkvb3NmLmlvL3E5eXZhKS4gW2h0dHBzOi8vb3NmLmlvL3E5eXZhL10oaHR0cHM6Ly9kb2kub3JnLzEwLjMxMjE5L29zZi5pby9xOXl2YSkgKDIgQXByaWwsIDIwMjIpLgoKUGVtYmFjYSBkaXNhcmFua2FuIG1lbXBlbGFqYXJpIGxhbmdzdW5nIGR1YSBzdW1iZXIgdXRhbWEgdGVya2FpdCBgc3RyaW5ncmAsIHlhaXR1IChpKSBidWt1IHRla3Mgb2xlaCBXaWNraGFtIGRhbiBHcm9sZW11bmQgWy1Ad2lja2hhbV9yXzIwMTddLCB1dGFtYW55YSBbQmFiIDE0XShodHRwczovL3I0ZHMuaGFkLmNvLm56L3N0cmluZ3MuaHRtbCksIGRhbiAoaWkpIFsqY2hlYXRzaGVldCpdKGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL2NoZWF0c2hlZXRzL2Jsb2IvbWFpbi9zdHJpbmdzLnBkZikgUERGIHVudHVrIGZ1bmdzaS1mdW5nc2kga3VuY2kgZGFsYW0gYHN0cmluZ3JgLiBCYWNhYW4gbGFpbiB5YW5nIG11bmdraW4gYmlzYSBkaWxpaGF0IGFkYWxhaCBtYWthbGFoIGthbWkgW0ByYWplZ193b3JraW5nXzIwMThdIHlhbmcgc2FsYWggc2F0dSBiYWdpYW5ueWEgbWVtYmFoYXMgcGVuZ29sYWhhbiB0ZWtzIGRhc2FyIG1lbmdndW5ha2FuIHNlanVtbGFoIGZ1bmdzaSBwZW5nb2xhaGFuIHRla3MgZGFyaSBpbnN0YWxhc2kgZGFzYXIgUiAoYnVrYW4gbWVuZ2d1bmFrYW4gYHN0cmluZ3JgKSAoa29kZSBzdW1iZXIgbWFrYWxhaCB0ZXJzZWJ1dCBkYXBhdCBkaWFrc2VzIG1lbGFsdWkgW3RhdXRhbiBiZXJpa3V0XShodHRwczovL2RvaS5vcmcvMTAuNDIyNS8wMy81YTdlZTJhYzg0MzAzKSBhdGF1IFtiZXJpa3V0XShodHRwczovL29zZi5pby9zNmtoOC8pKS4KCiMgUGVyc2lhcGFuCgpgYGB7ciBzZXR1cH0KIyBnbG9iYWwgb3B0aW9uIGNodW5jawprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGluY2x1ZGUgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSkKYGBgCgpgYGAKIyBpbnN0YWxsIHBhY2thZ2Ugc3RyaW5ncgppbnN0YWxsLnBhY2thZ2VzKCJzdHJpbmdyIikKYGBgCgpBa3RpZmthbiBtb2R1bC9wYWNrYWdlIGBzdHJpbmdyYC4KCmBgYHtyIGFrdGlma2FuLW1vZHVsLXN0cmluZ3J9CiMgbG9hZCB0aGUgcGFja2FnZXMKbGlicmFyeShzdHJpbmdyKQpgYGAKCk11YXQgZGF0YSB0ZWtzIGJlcnVwYSBrYWxpbWF0IGRhbiBrYXRhLWthdGEuCgpgYGB7ciBtdWF0LWRhdGEta2FsaW1hdC1zdHJpbmdyLXNlbXVhfQplbmdfc2VudGVuY2VfYWxsIDwtIHNjYW4oZmlsZSA9ICJkYXRhL2VuZ19zdHJpbmdyX3NlbnRlbmNlc19hbGwudHh0Iiwgd2hhdCA9ICJjaGFyIiwgc2VwID0gIlxuIiwgcXVpZXQgPSBUUlVFKQoKbGVuZ3RoKGVuZ19zZW50ZW5jZV9hbGwpICMgYWRhIDcyMCBlbGVtZW4ga2FsaW1hdAoKc2FtcGxlKGVuZ19zZW50ZW5jZV9hbGwsIHNpemUgPSAzKSAjIHRhbXBpbGthbiAzIGthbGltYXQgYWNhayBzYWphCmBgYAoKYGBge3IgbXVhdC1kYXRhLWx5LCBlY2hvID0gRkFMU0UsIGluY2x1ZGUgPSBGQUxTRSwgZXZhbCA9IEZBTFNFfQplbmdfc2VudGVuY2VfbHkgPC0gc2NhbihmaWxlID0gImRhdGEvZW5nX3NlbnRlbmNlc19seS50eHQiLCB3aGF0ID0gImNoYXIiLCBzZXAgPSAiXG4iLCBxdWlldCA9IFRSVUUpCgpzYW1wbGUoZW5nX3NlbnRlbmNlX2x5LCBzaXplID0gMykKYGBgCgpgYGB7ciBtdWF0LWRhdGEtemUsIGVjaG8gPSBGQUxTRSwgaW5jbHVkZSA9IEZBTFNFLCBldmFsID0gRkFMU0V9CmVuZ19zZW50ZW5jZV96ZSA8LSBzY2FuKGZpbGUgPSAiZGF0YS9lbmdfc2VudGVuY2VzX3plLnR4dCIsIHdoYXQgPSAiY2hhciIsIHNlcCA9ICJcbiIsIHF1aWV0ID0gVFJVRSkKCnNhbXBsZShlbmdfc2VudGVuY2VfemUsIHNpemUgPSAzKQpgYGAKCmBgYHtyIG11YXQtZGF0YS1rYXRhfQp3b3JkcyA8LSBzdHJpbmdyOjp3b3JkcyAjIGRhdGEga2F0YSAoKndvcmRzKikgZGFyaSBwYWNrYWdlIHN0cmluZ3IKc2FtcGxlKHdvcmRzLCBzaXplID0gMykKYGBgCgpgYGB7ciBtdWF0LWRhdGEtbmFtYS1idWFofQpmcnVpdHMgPC0gc3RyaW5ncjo6ZnJ1aXQgIyBkYXRhIGthdGEta2F0YSBuYW1hIGJ1YWggKCpmcnVpdHMqKSBkYXJpIHBhY2thZ2Ugc3RyaW5ncgpzYW1wbGUoZnJ1aXRzLCBzaXplID0gMykKYGBgCgojIFBlbmNhcmlhbiBkYW4gcGVuZGV0ZWtzaWFuIChyYW5na2FpYW4pIGthcmFrdGVyCgojIyBNZW5ndWppIHBvbGEgcGVuY2FyaWFuIGRhbiBoYXNpbCB5YW5nIGRpdGFuZ2thcCBkZW5nYW4gYHN0cl92aWV3X2FsbCgpYAoKRnVuZ3NpIGBzdHJfdmlld19hbGwoKWAgbWVtZXJsdWthbiBraXRhIG1lbmdpbnN0YWxsIHBhY2thZ2UgYGh0bWx3aWRnZXRzYC4gSmlrYSB0aWRhaywgZXJyb3IgYWthbiBtdW5jdWwgc2VwZXJ0aSBwYWRhIGdhbWJhciBkaSBiYXdhaCBpbmkuCgpgYGB7ciBlcnJvci1odG1sLXdpZGdldCwgZWNobyA9IEZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHRtbHdpZGdldC1lcnJvci5wbmciKQpgYGAKCmBgYHtyIGluc3RhbGwtaHRtbHdpZGdldHMsIGV2YWwgPSBGQUxTRX0KIyBpbnN0YWxsIGh0bWx3aWRnZXRzIHBhY2thZ2UKaW5zdGFsbC5wYWNrYWdlcygiaHRtbHdpZGdldHMiKQpgYGAKCktvZGUgYmVyaWt1dCBtZW5jb2JhIG1lbmFtcGlsa2FuIGtlYmVyYWRhYW4gcmFuZ2thaWFuIGthcmFrYXRlciBgInplImAgZGFsYW0gdmVrdG9yIHRla3MgYGVuZ19zZW50ZW5jZV9hbGxgLgoKYGBge3IgbWVsaWhhdC1oYXNpbC1wZW5jYXJpYW4sIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpzdHJfdmlld19hbGwoc3RyaW5nID0gZW5nX3NlbnRlbmNlX2FsbCwgcGF0dGVybiA9ICJ6ZSIsIG1hdGNoID0gVFJVRSkKYGBgCgojIyMgTGF0aWhhbiB7LnVubnVtYmVyZWR9CgoxLiAgVWJhaCBwYXJhbWV0ZXIgYXJndW1lbiBgbWF0Y2ggPSBUUlVFYCBtZW5qYWRpIGBtYXRjaCA9IEZBTFNFYCwgbGFsdSBqYWxhbmthbiBrb2RlIHNlYmVsdW1ueWEgZGVuZ2FuIHBvbGEgcGVuY2FyaWFuIGAiemUiYC4gQXBhIHlhbmcgdGVyamFkaSBwYWRhIGx1YXJhbm55YT8gQXBhIGZ1bmdzaSBkYXJpIGBtYXRjaCA9IEZBTFNFYD8KMi4gIEd1bmFrYW4gYHN0cl92aWV3X2FsbCgpYCB1bnR1ayBtZW5hbXBpbGthbiByYW5na2FpYW4ga2FyYWt0ZXIgYCJseSJgIHBhZGEgdmVrdG9yIHRla3MgYGVuZ19zZW50ZW5jZV9hbGxgLgozLiAgQmFnYWltYW5hIHBvbGEgcGVuY2FyaWFuIHVudHVrIG1lbmFtcGlsa2FuIHJhbmdrYWlhbiBrYXJha3RlciBgImx5ImAgeWFuZyBkaWlrdXRpIGRlbmdhbiBzcGFzaSAoKndoaXRlc3BhY2UqKT8KCiMjIE1lbmRldGVrc2kga2ViZXJhZGFhbiBwb2xhIHJhbmdrYWlhbiBrYXJha3RlciBkZW5nYW4gYHN0cl9kZXRlY3QoKWAKCkZ1bmdzaSBgc3RyX2RldGVjdCgpYCBtZW5naGFzaWxrYW4gbHVhcmFuIGxvZ2lzIGBUUlVFYCBkYW4gYEZBTFNFYCB5YW5nIG1lbnlhdGFrYW4gYXBha2FoIHJhbmdrYWlhbiBrYXJha3RlciB5YW5nIGtpdGEgY2FyaSBkaXRlbXVrYW4gKGBUUlVFYCkgYXRhdSB0aWRhayAoYEZBTFNFYCkgcGFkYSBzdWF0dSB2ZWt0b3IgdGVrcy9rYXJha3Rlci4KCmBgYHtyIGRldGVrc2ktcG9sYS1wZW5jYXJpYW59CiMgYnVhdCA1MCBzYW1wZWwga2FsaW1hdCBzZWRlcmhhbmEKZW5nX3NlbnRlbmNlX2FsbF9zYW1wbGUgPC0gc2FtcGxlKHggPSBlbmdfc2VudGVuY2VfYWxsLCBzaXplID0gNTApCgojIGFwYWthaCByYW5na2FpYW4ga2FyYWt0ZXIgImVkIiBkaXRlbXVrYW4gZGFsYW0gYGVuZ19zZW50ZW5jZV9hbGxfc2FtcGxlYD8Kc3RyX2RldGVjdChzdHJpbmcgPSBlbmdfc2VudGVuY2VfYWxsX3NhbXBsZSwgcGF0dGVybiA9ICJlZCIpCmBgYAoKIyMjIExhdGloYW4gey51bm51bWJlcmVkfQoKMS4gIEd1bmFrYW4gYHN0cl92aWV3X2FsbCgpYCB1bnR1ayBtZW5hbXBpbGthbiByYW5na2FpYW4ga2FyYWt0ZXIgYCJlZCJgIHBhZGEgdmVrdG9yIHRla3MgYGVuZ19zZW50ZW5jZV9hbGxfc2FtcGxlYC4KMi4gIEFwYWthaCByYW5na2FpYW4ga2FyYWt0ZXIga29uc29uYW4gYCJjayJgIGRpdGVtdWthbiBkYWxhbSBuYW1hLW5hbWEgYnVhaCBwYWRhIHZla3RvciB0ZWtzIGBmcnVpdHNgPwogICAgLSAgIEppa2EgeWEsIGd1bmFrYW4gYHN0cl92aWV3X2FsbCgpYCB1bnR1ayBtZW5hbXBpbGthbiBuYW1hIGJ1YWggYXBhIHlhbmcgbWVuZ2FuZHVuZyByYW5na2FpYW4ga2FyYWt0ZXIga29uc29uYW4gYCJjayJgIHBhZGEgdmVrdG9yIHRla3MgYGZydWl0c2AuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkZ1bmdzaSBgc3RyX2RldGVjdCgpYCBqdWdhIGRhcGF0IGRpZ3VuYWthbiB1bnR1ayBtZW55YXJpbmcgb2JzZXJ2YXNpIHlhbmcgZGlpbmdpbmthbiBkYWxhbSB0YWJlbCAoKmRhdGEgZnJhbWUqKSBiZXJkYXNhcmthbiByYW5na2FpYW4ga2FyYWt0ZXIgdGVydGVudHUuIEluaSBkaW11bmdraW5rYW4ga2FyZW5hIGx1YXJhbiBgc3RyX2RldGVjdCgpYCBiZXJ1cGEgc3RydWt0dXIgbG9naXMgYFRSVUVgIGRhbiBgRkFMU0VgIHlhbmcgbWVuamFkaSBsYW5kYXNhbiBkYWxhbSBtZW55YXJpbmcgb2JzZXJ2YXNpLCBzYWxhaCBzYXR1bnlhIGRhbGFtIHRhYmVsLgoKYGBge3IgbXVhdC1kYXRhLXRhYmVsfQojIG11YXQgZGF0YSB0YWJlbCBoYW55YSBrb2xvbSA0IC0gNwp2ZXJic19pZG4gPC0gcmVhZC50YWJsZShmaWxlID0gImRhdGEvdmVyYl9zZW1hbnRpY3MudHh0IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IilbNDo3XQoKIyBsaWhhdCBlbmFtIGJhcmlzIHBlcnRhbWEKaGVhZCh2ZXJic19pZG4pCmBgYAoKS29kZSBiZXJpa3V0IG1lbnlhcmluZyBvYnNlcnZhc2kgKGBzdWJzZXQoKWApIGRhbGFtIHRhYmVsIGRhdGEgYHZlcmJzX2lkbmAgYmVyZGFzYXJrYW4gYXBha2FoIGtvbG9tIGBleGFtcGxlX3RhcmdldGAgbWVuZ2FuZHVuZyB0ZWtzICh5YWl0dSBrYXRhIGtlcmphICpiZXItKikgZGVuZ2FuIHJhbmdrYWlhbiBrYXJha3RlciBgImRhImAgW2xpaGF0IEBwcmltYWhhZGlfbWV0b255bXlfMjAxMyB1bnR1ayBtYWthbGFoIHlhbmcgZGlsYW5kYXNpIGF0YXMgZGF0YSB0YWJlbCB0ZXJzZWJ1dF0uCgpgYGB7ciBzYXJpbmctb2JzZXJ2YXNpfQpzdWJzZXQodmVyYnNfaWRuLCBzdHJfZGV0ZWN0KHN0cmluZyA9IGV4YW1wbGVfdGFyZ2V0LCBwYXR0ZXJuID0gImRhIikpCmBgYAoKIyMjIExhdGloYW4gey51bm51bWJlcmVkfQoKMS4gIFViYWgga29kZSBzZWJlbHVtbnlhIHVudHVrIG1lbnlhcmluZy9tZW5kZXRla3NpIGthdGEta2F0YSBkYWxhbSBrb2xvbSAqKmBleGFtcGxlX3NvdXJjZWAqKiB5YW5nIG1lbmdhbmR1bmcgcmFuZ2thaWFuIGthcmFrdGVyIGAiYXIiYC4KCiMjIE1lbnlhcmluZyBlbGVtZW4gdmVrdG9yIHRla3MgeWFuZyBtZW5nYW5kdW5nIHJhbmdrYWlhbiBrYXJha3RlciB0ZXJ0ZW50dSBkZW5nYW4gYHN0cl9zdWJzZXQoKWAKCkJlcmJlZGEgZGVuZ2FuIGBzdHJfZGV0ZWN0KClgLCB5YW5nIG1lbmdlbHVhcmthbiBgVFJVRWAgZGFuIGBGQUxTRWAsIGBzdHJfc3Vic2V0KClgIChpKSBtZW5kZXRla3NpICpkYW4qIChpaSkgbWVuZ2VsdWFya2FuIGVsZW1lbiBkYWxhbSB2ZWt0b3IgdGVrcyB5YW5nIG1lbmdhbmR1bmcgcmFuZ2thaWFuIGthcmFrdGVyIHlhbmcgZGljYXJpLgoKYGBge3Igc2FyaW5nLWthbGltYXQtZGVuZ2FuLWthcmFrdGVyLWluZ30Kc3RyX3N1YnNldChzdHJpbmcgPSBlbmdfc2VudGVuY2VfYWxsX3NhbXBsZSwgcGF0dGVybiA9ICJpbmciKQpgYGAKVGVudHUga2l0YSBiaXNhIHNpbXBhbiBoYXNpbCBwZW55YXJpbmdhbiBpbmkga2UgdmVrdG9yIGxhaW4uCgpgYGB7ciBzYXJpbmcta2FsaW1hdC1kZW5nYW4ta2FyYWt0ZXItaW5nLXNpbXBhbn0KIyBzaW1wYW4gbHVhcmFuIGtvZGUgYmVyaWt1dCBrZSB2ZWt0b3IgYGluZ19vdXRwdXRgCmluZ19vdXRwdXQgPC0gc3RyX3N1YnNldChzdHJpbmcgPSBlbmdfc2VudGVuY2VfYWxsX3NhbXBsZSwgcGF0dGVybiA9ICJpbmciKQppbmdfb3V0cHV0CmBgYAoKCiMjIyBMYXRpaGFuIHsudW5udW1iZXJlZH0KCjEuIEJ1YWggYXBhIHlhbmcgbmFtYW55YSBtZW5nYW5kdW5nIGthcmFrdGVyIGAiY2siYD8gR3VuYWthbiBgc3RyX3N1YnNldCgpYCBwYWRhIHZla3RvciB0ZWtzIGBmcnVpdHNgIHVudHVrIG1lbmphd2FiIHBlcnRhbnlhYW4gaW5pLgoxLiBHdW5ha2FuIGBzdHJfc3Vic2V0KClgIHVudHVrIG1lbnlhcmluZyBrYWxpbWF0IGRhbGFtIHZla3RvciBgZW5nX3NlbnRlbmNlX2FsbGAgeWFuZyBtZW5nYW5kdW5nIHJhbmdrYWlhbiBrYXJha3RlciBgIndobyJgLiBBcGFrYWggcG9sYSBwZW5jYXJpYW4gYCJ3aG8iYCBpbmkgbWVuZ2VsdWFya2FuIGthbGltYXQgeWFuZyBtZW5nYW5kdW5nIGthdGEgKndobyogJ3NpYXBhJyBkYWxhbSBiYWhhc2EgSW5nZ3Jpcz8KCi0tLS0tLS0tLQoKPHAgY2xhc3M9ImNvbW1lbnQiPioqQ2F0YXRhbiB0ZW50YW5nIF9yZWd1bGFyIGV4cHJlc3Npb25zXyoqPGJyPjxici8+PGk+W1JlZ3VsYXIgZXhwcmVzc2lvbnNdKGh0dHBzOi8vd3d3LnJlZ3VsYXItZXhwcmVzc2lvbnMuaW5mby90dXRvcmlhbC5odG1sKTwvaT4gKFJlZ0V4KSBhZGFsYWggcG9sYSBwZW5jYXJpYW4ga2FyYWt0ZXIgeWFuZyBsZWJpaCBjYW5nZ2loIGRhbiBmbGVrc2liZWwgeWFuZyBwZXJsdSBkaXBlbGFqYXJpIHVudHVrIHBlbmdvbGFoYW4gZGFuIG1hbmlwdWxhc2kgdGVrcyB0aW5na2F0IGxhbmp1dCBbbGloYXQgQHdpY2toYW1fcl8yMDE3LCBzdWItYmFnaWFuIDE0LjMgdW50dWsgdWxhc2FuIGxlYmloIHJpbmNpXS48YnIvPjxici8+IFNhbGFoIHNhdHUgamVuaXMgUmVHZXggeWFuZyBwZW50aW5nIGRpa2V0YWh1aSBhZGFsYWggKipwZXdhdGFzIHJhbmdrYWlhbiBrYXJha3RlciB5YW5nIG1lbmNlcm1pbmthbiBrYXRhKiogKCp3b3JkIGJvdW5kYXJ5KikgKGAiXFxiImApLiBVbnR1ayBrYXN1cyBwZW5jYXJpYW4gcmFuZ2thaWFuIGthcmFrdGVyIGAid2hvImAgc2ViZWx1bW55YSwga2l0YSBtZWxha3VrYW4gcGVuY2FyaWFuIGRlbmdhbiByYW5na2FpYW4ga2FyYWt0ZXIgdGFucGEgUmVnRXgga2FyZW5hIGtpdGEgdGlkYWsgbWVtcGVyaW5jaSBiYWh3YSBrYXJha3RlciBgIndobyJgIHlhbmcga2l0YSBpbmdpbmthbiBhZGFsYWgga2F0YSBfd2hvXyBidWthbm55YSByYW5na2FpYW4ga2FyYWt0ZXIgYCJ3aG8iYCB5YW5nIGJpc2EgbXVuY3VsIGRhbGFtIGthdGEgXyoqd2hvKipsZV8sIF8qKndobyoqZXZlcl8sIGRsbC4gVW50dWsgaXR1LCBwb2xhIHBlbmNhcmlhbiBraXRhIHVudHVrICJrYXRhIiBfd2hvXyAoYnVrYW4gcmFuZ2thaWFuIGthcmFrdGVyIGAid2hvImApIGhhcnVzbGFoIGxlYmloIHNwZXNpZmlrIGRhbiBrYXJha3RlciBSZWdFeCBwZXdhdGFzIGthdGEgKGAiXFxiImApIGRhcGF0IG1lbWJhbnR1IGRhbGFtIGhhbCBpbmksIHlhaXR1IGAiXFxid2hvXFxiImAuPC9wPgoKQmFuZGluZ2thbiBsdWFyYW4ga2VkdWEgYmFyaXMga29kZSBiZXJpa3V0IHVudHVrIHBvbGEgcGVuY2FyaWFuIGRlbmdhbiBkYW4gdGFucGEgUmVnRXggcGV3YXRhcyBrYXRhIChgIlxcYiJgKToKCmBgYHtyIGxpdGVyYWwtc2VhcmNofQojIHRhbnBhIHJlZ2V4IHBld2F0YXMga2F0YSAiXFxiIiBkaSBrZWR1YSBzaXNpIGthcmFrdGVyIHlhbmcgZGljYXJpCnN0cl9zdWJzZXQoc3RyaW5nID0gZW5nX3NlbnRlbmNlX2FsbCwgcGF0dGVybiA9ICJ3aG8iKSAKCiMgdGFucGEgcmVnZXggcGV3YXRhcyBrYXRhICJcXGIiIGRpIGtlZHVhIHNpc2kga2FyYWt0ZXIgeWFuZyBkaWNhcmkKc3RyX3ZpZXdfYWxsKHN0cmluZyA9IGVuZ19zZW50ZW5jZV9hbGwsIHBhdHRlcm4gPSAid2hvIiwgbWF0Y2ggPSBUUlVFKQpgYGAKYGBge3Igd29yZC1ib3VuZGFyeS1zZWFyY2h9CiMgZGVuZ2FuIHJlZ2V4IHBld2F0YXMga2F0YSAiXFxiIiBkaSBrZWR1YSBzaXNpIGthcmFrdGVyIHlhbmcgZGljYXJpCnN0cl9zdWJzZXQoc3RyaW5nID0gZW5nX3NlbnRlbmNlX2FsbCwgcGF0dGVybiA9ICJcXGJ3aG9cXGIiKSAKCiMgZGVuZ2FuIHJlZ2V4IHBld2F0YXMga2F0YSAiXFxiIiBkaSBrZWR1YSBzaXNpIGthcmFrdGVyIHlhbmcgZGljYXJpCnN0cl92aWV3X2FsbChzdHJpbmcgPSBlbmdfc2VudGVuY2VfYWxsLCBwYXR0ZXJuID0gIlxcYndob1xcYiIsIG1hdGNoID0gVFJVRSkKYGBgCgoKIyMjIExhdGloYW4gey19CgoxLiBHdW5ha2FuIFJlZ0V4IHBld2F0YXMga2F0YSAoYCJcXGIiYCkgZGVuZ2FuIGBzdHJfc3Vic2V0KClgIHVudHVrIG1lbnlhcmluZyBrYWxpbWF0IGRhbGFtIHZla3RvciBgZW5nX3NlbnRlbmNlX2FsbGAgeWFuZyBtZW5nYW5kdW5nIGthdGEta2F0YSB5YW5nICoqZGlha2hpcmkqKiBkZW5nYW4gcmFuZ2thaWFuIGthcmFrdGVyIGAiaW5nImAgKG1pc2FsbnlhLCBrYXRhLWthdGEgc2VwZXJ0aSBfdGV4dCoqaW5nKipfLCBfbGl2KippbmcqKl8sIF9yKippbmcqKl8sIF9zcHIqKmluZyoqXywgZGxsLikKMS4gR3VuYWthbiBgc3RyX3N1YnNldCgpYCBkYW4gcG9sYSBwZW5jYXJpYW4gZGVuZ2FuIFJlZ0V4IHBld2F0YXMga2F0YSAoYCJcXGIiYCkgdW50dWsgbWVuZ2VsdWFya2FuIGthdGEta2F0YSB5YW5nICoqZGlhd2FsaSoqIGRlbmdhbiBrYXJha3RlciBgImNoImAgKG1pc2FsbnlhIF8qKmNoKiphbmNlXywgXyoqY2gqKmFwXywgZGxsLikgZGFsYW0gdmVrdG9yIHRla3MgYHdvcmRzYC4KCi0tLS0tLQoKPHAgY2xhc3M9ImNvbW1lbnQiPioqQ2F0YXRhbiB0ZW50YW5nIF9yZWd1bGFyIGV4cHJlc3Npb25zXyoqPGJyPjxici8+U2VsYWluIHBld2F0YXMgcmFuZ2thaWFuIGthcmFrdGVyIGJlcnVwYSBrYXRhLCB0ZXJkYXBhdCBkdWEgbGFnaSBSZUdleCBwZXdhdGFzIHJhbmdrYWlhbiBrYXJha3RlciB5YW5nIHBlbnRpbmcgZGlrZXRhaHVpLCB5YWl0dSAqKnBld2F0YXMgcmFuZ2thaWFuIGthcmFrdGVyKiogc2VjYXJhIHVtdW0gKCphbmNob3IgY2hhcmFjdGVyKik6IChpKSBwZXdhdGFzICoqcG9zaXNpIGF3YWwqKiByYW5na2FpYW4ga2FyYWt0ZXIgKGAiXiJgKSBkYW4gKGlpKSBwZXdhdGFzICoqcG9zaXNpIGFraGlyKiogcmFuZ2thaWFuIGthcmFrdGVyIChgIiQiYCkuPC9wPiAKClBlcmhhdGlrYW4ga29kZSBiZXJpa3V0IHNlYmFnYWkgY29udG9oIHBlbmdndW5hYW4gUmVnRXggcGV3YXRhcyBhd2FsIGRhbiBha2hpciByYW5na2FpYW4ga2FyYWt0ZXIgW2Rpa3V0aXAgZGFyaSBAd2lja2hhbV9yXzIwMTcsIDIwMl06CgpgYGB7ciBlbmQtb2Ytc3RyaW5nfQojIGJ1YXQgZGF0YSBzZWRlcmhhbmEKZnJ1aXRfbWluaSA8LSBjKCJhcHBsZSIsICJiYW5hbmEiLCAicGVhciIpCgojIG1lbmFuZ2thcCBrYXJha3RlciBgImEiYCB5YW5nIG11bmN1bCBkaSBha2hpciAoYCIkImApIHJhbmdrYWlhbiBrYXJha3RlciBuYW1hIGJ1YWggcGFkYSB2ZWt0b3IgYGZydWl0X21pbmlgCnN0cl92aWV3X2FsbChzdHJpbmcgPSBmcnVpdF9taW5pLCBwYXR0ZXJuID0gImEkIikKc3RyX3N1YnNldChzdHJpbmcgPSBmcnVpdF9taW5pLCBwYXR0ZXJuID0gImEkIikKYGBgCgoKYGBge3IgYmVnaW5uaW5nLW9mLXN0cmluZ30KIyBtZW5hbmdrYXAga2FyYWt0ZXIgYCJhImAgeWFuZyBtdW5jdWwgZGkgYWtoaXIgKGAiJCJgKSByYW5na2FpYW4ga2FyYWt0ZXIgbmFtYSBidWFoIHBhZGEgdmVrdG9yIGBmcnVpdF9taW5pYApzdHJfdmlld19hbGwoc3RyaW5nID0gZnJ1aXRfbWluaSwgcGF0dGVybiA9ICJeYSIpCnN0cl9zdWJzZXQoc3RyaW5nID0gZnJ1aXRfbWluaSwgcGF0dGVybiA9ICJeYSIpCmBgYAoKS2l0YSBiaXNhIG1lbmdnYWJ1bmdrYW4gYCJeImAgZGFuIGAiJCJgIHVudHVrIG1lbXBlcnRlZ2FzIGJhaHdhIGtpdGEgaGFueWEgaW5naW4gbWVuYW5na2FwIHJhbmdrYWlhbiBrYXJha3RlciBzZWNhcmEgdXR1aC4gQmFuZGluZ2thbiBrZWR1YSBrb2RlIGJlcmlrdXQgW2Rpa3V0aXAgZGFyaSBAd2lja2hhbV9yXzIwMTcsIDIwMl06CgpgYGB7ciBwYXJ0aWFsLW1hdGNofQojIGJ1YXQgZGF0YSBsYWluCmZydWl0X2Nha2UgPC0gYygiYXBwbGUgcGllIiwgImFwcGxlIiwgImFwcGxlIGNha2UiKQoKIyBwZW5jYXJpYW4gc2VkZXJoYW5hICh0YW5wYSBSZWdFeCkgdW50dWsgbWVuYW5na2FwIHJhbmdrYWlhbiBrYXJha3RlciBgImFwcGxlImAKc3RyX3ZpZXdfYWxsKHN0cmluZyA9IGZydWl0X2Nha2UsIHBhdHRlcm4gPSAiYXBwbGUiKQpzdHJfc3Vic2V0KHN0cmluZyA9IGZydWl0X2Nha2UsIHBhdHRlcm4gPSAiYXBwbGUiKQpgYGAKCmBgYHtyIGNvbXBsZXRlLW1hdGNofQojIG1lbmFuZ2thcCBoYW55YSByYW5na2FpYW4ga2FyYWt0ZXIgdXR1aCBkYXJpIGF3YWwgaGluZ2dhIGFraGlyIHJhbmdrYWlhbiBrYXJha3RlcgpzdHJfdmlld19hbGwoc3RyaW5nID0gZnJ1aXRfY2FrZSwgcGF0dGVybiA9ICJeYXBwbGUkIikKc3RyX3N1YnNldChzdHJpbmcgPSBmcnVpdF9jYWtlLCBwYXR0ZXJuID0gIl5hcHBsZSQiKQpgYGAKCiMjIyBMYXRpaGFuIHstfQoKMS4gQnVhaCBhcGEgc2FqYSB5YW5nIG5hbWFueWEgKipkaWF3YWxpKiogZGVuZ2FuIGthcmFrdGVyIGtvbnNvbmFuIGAiYiJgPyBHdW5ha2FuIGBzdHJfc3Vic2V0KClgIHVudHVrIG1lbmdlbHVhcmthbiBkYXJpIHZla3RvciBgZnJ1aXRzYCBuYW1hLW5hbWEgYnVhaCB5YW5nIGRpYXdhbGkgZGVuZ2FuIGthcmFrdGVyIGAiYiJgLgoKMS4gQWRhIGJlcmFwYSBidWFoIHlhbmcgbmFtYW55YSAqKmJlcmFraGlyKiogZGVuZ2FuIGthcmFrdGVyIHZva2FsIGAibyJgPyBHdW5ha2FuIGBzdHJfc3Vic2V0KClgIHVudHVrIG1lbmdlbHVhcmthbiBkYXJpIHZla3RvciBgZnJ1aXRzYCBuYW1hLW5hbWEgYnVhaCB5YW5nIGRpYXdhbGkgZGVuZ2FuIGthcmFrdGVyIGAibyJgLgoKCjxwIGNsYXNzPSJjb21tZW50Ij4qKlRpcHMqKjxicj48YnIvPmBzdHJpbmdyYCBqdWdhIG1lbWlsaWtpIGR1YSBmdW5nc2kga2h1c3VzIHVudHVrIG1lbmRldGVrc2kga2ViZXJhZGFhbiAocmFuZ2thaWFuKSBrYXJha3RlciB5YW5nIG11bmN1bCAoaSkgKipkaSBhd2FsKiogcmFuZ2thaWFuIGthcmFrdGVyLCB5YWl0dSBmdW5nc2kgW2BzdHJfc3RhcnRzKClgXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc3RyX3N0YXJ0cy5odG1sKSwgZGFuIChpaSkgKipkaSBha2hpcioqIHJhbmdrYWlhbiBrYXJha3RlciwgeWFpdHUgZnVuZ3NpIFtgc3RyX2VuZHMoKWBdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zdHJfc3RhcnRzLmh0bWwpLiBMYXlha255YSBgc3RyX2RldGVjdCgpYCwga2VkdWEgZnVuZ3NpIGluaSBtZW5nZWx1YXJrYW4gdmVrdG9yIGxvZ2lzIGBUUlVFYCBkYW4gYEZBTFNFYC48YnIvPjxici8+TWlzYWxueWEsIGBzdHJfc3RhcnRzKGZydWl0X21pbmksICJwIilgIGFrYW4gbWVuZGV0ZWtzaSBhZGFrYWggZWxlbWVuIHRla3MgZGFsYW0gdmVrdG9yIGBmcnVpdF9taW5pYCB5YW5nIGRpYXdhbGkgZGVuZ2FuIGthcmFrdGVyIGAicCJgLCB0YW5wYSBwZXJsdSBtZW5nZ3VuYWthbiBSZWdFeCBwZXdhdGFzIGthcmFrdGVyIGF3YWwgYCJeImAuIFNlYmFsaWtueWEsIGBzdHJfZW5kcyhmcnVpdF9taW5pLCAicCIpYCBha2FuIG1lbmRldGVrc2kga2ViZXJhZGFhbiB0ZWtzIGRhbGFtIGBmcnVpdF9taW5pYCB5YW5nIGRpYWtoaXJpIGRlbmdhbiBrYXJha3RlciBgInAiYCwgdGFucGEgcGVybHUgbWVuZ2d1bmFrYW4gUmVnRXggcGV3YXRhcyBrYXJha3RlciBha2hpciBgIiQiYC48YnIvPjxici8+UmVnRXggdGV0YXAgZGlwZXJsdWthbiBqaWthIG1lbmdndW5ha2FuIGZ1bmdzaSBgc3RyX3N1YnNldCgpYCB1bnR1ayBtZW55YXJpbmcgdGVrcyB5YW5nIGRpYXdhbGkgYXRhdSBkaWFraGlyaSBkZW5nYW4gKHJhbmdrYWlhbikga2FyYWt0ZXIgdGVydGVudHUuPC9wPgoKLS0tLS0tCgojIE1lbmdlbHVhcmthbiAoKmV4dHJhY3QqKSBwb2xhIGthcmFrdGVyIGRhcmkgZGFsYW0gdGVrcwoKRnVuZ3NpIGBzdHJfZXh0cmFjdF9hbGwoKWAgZGlndW5ha2FuIHVudHVrICJtZW5nZWx1YXJrYW4iICgqZXh0cmFjdCopIHBvbGEgeWFuZyBkaXRhbmdrYXAgZGFyaSBwZW5jYXJpYW4ga2FyYWt0ZXIgeWFuZyBraXRhIHJhbmNhbmcuIFNlYmFnYWkgY29udG9oLCB2ZWt0b3Iga2FsaW1hdCBgZW5nX3NlbnRlbmNlX2FsbGAgdGVyZGFwYXQgc2VqdW1sYWggbmFtYS1uYW1hIHdhcm5hIGJhaGFzYSBJbmdncmlzIGRhbiBraXRhIGhhbnlhIGluZ2luIG1lbmdlbHVhcmthbiBrYXRhLWthdGEgeWFuZyBtZW5jZXJtaW5rYW4gbmFtYSB3YXJuYSB0ZXJzZWJ1dCAodGlkYWsga2F0YS1rYXRhIGxhaW5ueWEgZGFsYW0ga2FsaW1hdCkuIEtpdGEgbWVtZXJsdWthbiBrYXJha3RlciBSZWdFeCBiZXJuYW1hICJhbHRlcm5hdGlmIiAoYHxgKSB5YW5nIGJlcmFydGkgJ2F0YXUnLiBKaWthIGtpdGEgaW5naW4gbWVuY2FyaSB3YXJuYSBgIngiYCAqKmF0YXUqKiBgInkiYCAqKmF0YXUqKiBgInoiYCwgbWFrYSBwb2xhIHBlbmNhcmlhbiBraXRhIGFkYWxhaCBgIih4fHl8eikiYC4KClBlcnRhbWEga2l0YSByYW1waW5na2FuIGRhdGEga2FsaW1hdCBgZW5nX3NlbnRlbmNlX2FsbGAgZGVuZ2FuIG1lbnlhcmluZyBoYW55YSBrYWxpbWF0LWthbGltYXQgeWFuZyBtZW5nYW5kdW5nIG5hbWEtbmFtYSB3YXJuYS4KCmBgYHtyIGVrc3RyYWtzaXBlcnRhbWF9CiMgYnVhdCB2ZWt0b3Iga2FyYWt0ZXIgdW50dWsgcmVnZXggcGVuY2FyaWFuIGRlbmdhbiB0ZWtuaWsgImFsdGVybmF0aWYiCmNvbG91cl9wYXR0ZXJuIDwtICIoYmx1ZXxvcmFuZ2V8eWVsbG93fGdyZWVufHB1cnBsZSkiIAoKIyBkZXRla3NpIGRhbiBzYXJpbmcgdGVybGViaWggZGFodWx1IGhhbnlhIGthbGltYXQgeWFuZyBiZXJpc2kgbmFtYS1uYW1hIHdhcm5hICJiaXJ1IiwgIm9yYW55ZSIsICJrdW5pbmciLCAiaGlqYXUiLCBkYW4gInVuZ3UiLgpzZW50X2NvbG91ciA8LSBzdHJfc3Vic2V0KHN0cmluZyA9IGVuZ19zZW50ZW5jZV9hbGwsIHBhdHRlcm4gPSBjb2xvdXJfcGF0dGVybikKCiMgY2FyaSB0YWh1IGJlcmFwYSBrYWxpbWF0IHlhbmcgbWVuZ2FuZHVuZyBuYW1hLW5hbWEgd2FybmEKbGVuZ3RoKHNlbnRfY29sb3VyKSAjIDIwIGthbGltYXQgbWVuZ2FuZHVuZyBuYW1hLW5hbWEgd2FybmEgdGVyc2VidXQKYGBgCgpCYXJ1IGtpdGEga2VsdWFya2FuIGthdGEta2F0YSBuYW1hIHdhcm5hIGRhcmkgdmVrdG9yIGthcmFrdGVyIGBzZW50X2NvbG91cmAgZGFuIHNpbXBhbiBsdWFyYW5ueWEga2UgdmVrdG9yIGthcmFrdGVyIGxhaW5ueWEgZGVuZ2FuIG5hbWEgYHdvcmRfY29sb3VyYC4KCmBgYHtyIGVrc3RyYWtzaWtlZHVhfQojIGtlbHVhcmthbiBuYW1hLW5hbWEgd2FybmEgZGFyaSB2ZWt0b3IgYHNlbnRfY29sb3VyYCBkYW4gc2ltcGFuIGtlIHZla3RvciBgd29yZF9jb2xvdXJgCndvcmRfY29sb3VyIDwtIHN0cl9leHRyYWN0X2FsbChzdHJpbmcgPSBzZW50X2NvbG91ciwgcGF0dGVybiA9IGNvbG91cl9wYXR0ZXJuKSAKCiMgbGloYXQgZW5hbSBlbGVtZW4gcGVydGFtYQpoZWFkKHdvcmRfY29sb3VyKSAjIGx1YXJhbiBiZXJ1cGEgTGlzdCBidWthbiBWZWt0b3Iga2FyYWt0ZXIgKGF0b21pcykKYGBgCgpgYGB7ciBla3N0cmFrc2lrZXRpZ2F9CndvcmRfY29sb3VyX3ZlY3RvciA8LSB1bmxpc3Qod29yZF9jb2xvdXIpICMgdW5saXN0IG1lbmphZGkgdmVrdG9yIGthcmFrdGVyCndvcmRfY29sb3VyX3ZlY3RvcgpgYGAKCkd1bmFrYW4gYHN0cl9zb3J0KClgIHVudHVrIG1lbmd1cnV0a2FuIGthcmFrdGVyIHNlY2FyYSBhbGZhYmV0aXMuCgpgYGB7ciBtZW55b3J0aXItdGVrcy1zZWNhcmEtYWxmYWJldGlzfQpzdHJfc29ydCh3b3JkX2NvbG91cl92ZWN0b3IpCmBgYAoKS2l0YSBiaXNhIGd1bmFrYW4gYHRhYmxlKClgIHVudHVrIG1lbnRhYnVsYXNpIGthcmFrdGVyIHZla3RvciBuYW1hIHdhcm5hIHVudHVrIG1lbmdldGFodWkgZGlzdHJpYnVzaS9mcmVrdWVuc2kga2VtdW5jdWxhbiB3YXJuYSB0ZXJzZWJ1dC4KCmBgYHtyIHRhYnVsYXNpLXdhcm5hfQp0YWJsZSh3b3JkX2NvbG91cl92ZWN0b3IpCmBgYAoKYGBge3Igc29ydGlyLXRhYnVsYXNpfQojIHNvcnRpciBuYW1hIHdhcm5hIGRhcmkgZnJla3VlbnNpIHRlcnRpbmdnaSBrZSB0ZXJlbmRhaAp3b3JkX2NvbG91cl9jb3VudCA8LSB0YWJsZSh3b3JkX2NvbG91cl92ZWN0b3IpCndvcmRfY29sb3VyX2NvdW50IDwtIHNvcnQod29yZF9jb2xvdXJfY291bnQsIGRlY3JlYXNpbmcgPSBUUlVFKQp3b3JkX2NvbG91cl9jb3VudApgYGAKCiMgTWVtZWNhaCB0ZWtzIG1lbmphZGkgdW5pdCB5YW5nIGxlYmloIGtlY2lsCgpGdW5nc2kgYHN0cl9zcGxpdCgpYCBkaWd1bmFrYW4gdW50dWsgbWVtZWNhaCB0ZWtzIGJlcmRhc2Fya2FuIHBhZGEga2FyYWt0ZXIgbWFuYSB0ZWtzIHRlcnNlYnV0IGRpcGVjYWguIENvbnRvaCBwYWxpbmcgc2VkZXJoYW5hIGFkYWxhaCBtZW1lY2FoIGthbGltYXQgbWVuamFkaSBrYXRhIGRlbmdhbiBtZW1lY2FobnlhIHBhZGEga2FyYWt0ZXIgc3Bhc2kgKHlhbmcgbWVydXBha2FuIHBlbWlzYWgga2F0YSB1bnR1ayBrYXJha3RlciBSb21hbiB5YW5nIGRpcGFoYW1pIG9sZWgga29tcHV0ZXIpLgoKYGBge3IgcGVjYWgtdGVrc30KIyBjb250b2ggc2VkZXJoYW5hIHBlbWVjYWhhbiBrYWxpbWF0IC0+IGthdGEgZGVuZ2FuIGR1YSBrYWxpbWF0IAp0d29fc2VudGVuY2VzIDwtIGVuZ19zZW50ZW5jZV9hbGxfc2FtcGxlWzE6Ml0KCiMgcGVjYWggdGVrcyBwYWRhIGthcmFrdGVyIHNwYXNpIGAiICJgLgpzdHJfc3BsaXQodHdvX3NlbnRlbmNlcywgcGF0dGVybiA9ICIgIikgIyBsdWFyYW5ueWEgYWRhbGFoIExpc3QKYGBgCgpJc3VueWEgYWRhbGFoIHRhbmRhIGJhY2Egc2VwZXJ0aSB0aXRpayAoYC5gKSBtYXNpaCBtZW5lbXBlbCBwYWRhIGthdGEgeWFuZyBtZW5nYWtoaXJpIGthbGltYXQuIFBhZGEgc2l0dWFzaSBpbmlsYWgga2VrdWF0YW4gZGFyaSBSZWdFeCBtZW5qYWRpIHBlbnRpbmcuCgpLaXRhIHBlcmx1IG1lcmFuY2FuZyBwb2xhIHBlbmNhcmlhbiBSZWdFeCB5YW5nIG1lbnlhdGFrYW4gYmFod2Ega2l0YSBpbmdpbiBtZW1pc2Foa2FuL21lbWVjYWggdGVrcyBwYWRhIGtlbG9tcG9rIGthcmFrdGVyICoqc2VsYWluKioga2Vsb21wb2sga2FyYWt0ZXIgcGVtYmVudHVrIGthdGEsIHlhaXR1IGtlbG9tcG9rIGthcmFrdGVyICoqZGkgbHVhcioqIGthcmFrdGVyIGFsZmFudW1lcmlzIGRhbiBzdHJpcCBgIi0iYC4gRGFsYW0ga29udGVrcyBiYWhhc2EgSW5nZ3JpcywgYCItImAgZGlwZXJ0YWhhbmthbiB1bnR1ayBtZW5hbmdrYXAga2F0YSBzZXBlcnRpICpjb21wdXRlci1yZWFkYWJsZSogc2ViYWdhaSBzYXR1IHVuaXQsIGJ1a2FubnlhIG1lbmphZGkgZHVhIHVuaXQgKmNvbXB1dGVyKiBkYW4gKnJlYWRhYmxlKi4gCgpSZWdFeCB5YW5nIGJpc2EgZGlndW5ha2FuIGFkYWxhaCBgIlteYS16QS1aMC05LV0rImAsIHlhbmcgYXJ0aW55YTogc2F0dSBhdGF1IGxlYmloIChgIisiYCkga2Vsb21wb2sga2FyYWt0ZXIgKGAiWy4uLl0iYCkgeWFuZyBidWthbiAoYCJeImApIChpKSBhbGZhbnVtZXJpcyBrYXBpdGFsIGRhbiBrZWNpbCAoYCJhLXpBLVowLTkiYCkgZGFuIHN0cmlwIChgIi0iYCkuCgpgYGB7ciBwZWNhaC10ZWtzLXJlZ2V4fQojIHBlY2FoIHRla3MgcGFkYSBrYXJha3RlciBub24tYWxmYW51bWVyaXMgZGFuIHNwYXNpIGAiICJgLgpzdHJfc3BsaXQodHdvX3NlbnRlbmNlcywgcGF0dGVybiA9ICJbXmEtekEtWjAtOS1dKyIpCmBgYAoKVGVudHUgbHVhcmFuIExpc3QgaW5pIGJpc2Ega2l0YSB1YmFoIG1lbmphZGkgdmVrdG9yIGF0b21pczoKCmBgYHtyIHVubGlzdC1sdWFyYW4tc3RyLXNwbGl0fQpzcGxpdF9zZW50ZW5jZSA8LSBzdHJfc3BsaXQodHdvX3NlbnRlbmNlcywgcGF0dGVybiA9ICJbXmEtekEtWjAtOS1dKyIpCnVubGlzdChzcGxpdF9zZW50ZW5jZSkKYGBgCgpQZW1lY2FoYW4gdGVrcyBkYXJpIGthbGltYXQgbWVuamFkaSBrYXRhIGluaSBkYXBhdCBkaWxpaGF0IGRhcmkgZHVhIHBlcnNwZWt0aWY6CgppLiAqKm1lbWVjYWgqKiAoeWFpdHUgKiptZW1pc2Foa2FuKiogZGVuZ2FuICoqbWVuZ2hpbGFuZ2thbioqKSBrYXJha3RlciAqKnNlbGFpbioqIGFsZmFudW1lcmlzIGRhbiBzdHJpcCwgbWVuZ2d1bmFrYW4gYHN0cl9zcGxpdCgpYCwgYXRhdQoKaS4gKiptZW5hcmlrKiogKHlhaXR1ICoqbWVuZ2VsdWFya2FuKiovKiptZW5nZWtzdHJha3NpKiopIGthcmFrdGVyIHlhbmcgbWVtYmFuZ3VuIHN1YXR1IGthdGEsIHlhaXR1IGthcmFrdGVyIGFsZmFudW1lcmlzIGRhbiBzdHJpcCwgbWVuZ2d1bmFrYW4gYHN0cl9leHRyYWN0X2FsbCgpYC4KCkppa2Ega2l0YSBtZW5nYW1iaWwgcGVyc3Bla3RpZiBrZWR1YSAoZGVuZ2FuIGBzdHJfZXh0cmFjdF9hbGwoKWApLCBtYWthIFJlZ0V4IHlhbmcgZGlndW5ha2FuIHNlZGlraXQgYmVyYmVkYSwgeWFpdHUgZGVuZ2FuIG1lbmdoaWxhbmdrYW4gdGFuZGEgbmVnYXNpICgnYnVrYW4nKSBgIl4iYCBkYWxhbSBSZWdFeC1ueWEuIFBlcmhhdGlrYW4ga29kZSBiZXJpa3V0IGRhbiBiYW5kaW5na2FuIGRlbmdhbiB0ZWxpdGkgbHVhcmFuIGBzdHJfZXh0cmFjdF9hbGwoKWAgZGVuZ2FuIGx1YXJhbiBgc3RyX3NwbGl0KClgIHNlYmVsdW1ueWEuCgpgYGB7ciBla3N0cmFrLWthdGF9CiMgcGVyc3Bla3RpZiBrZWR1YSBtZW1lY2FoIGthbGltYXQgbWVuamFkaSBrYXRhCiMgZGVuZ2FuICoqbWVuZ2tla3N0cmFrc2kqKiBrYXRhLWthdGEKc3RyX2V4dHJhY3RfYWxsKHR3b19zZW50ZW5jZXMsIHBhdHRlcm4gPSAiW2EtekEtWjAtOS1dKyIpCmBgYAoKWWFuZyBtZW1iZWRha2FuIGx1YXJhbiBgc3RyX3NwbGl0KClgIGRhbiBgc3RyX2V4dHJhY3RfYWxsKClgIGRpIGF0YXMsIGtodXN1c255YSBkYWxhbSBrb250ZWtzIG1lbWVjYWggdGVrcyBrYWxpbWF0IG1lbmphZGkga2F0YSBhZGFsYWg6CgppLiBsdWFyYW4gYHN0cl9zcGxpdCgpYCBtZW5hbmRhaSBwZW5naGlsYW5nYW4vcGVtZWNhaGFuIHRhbmRhIGJhY2EsIGRhbGFtIGhhbCBpbmkgdGl0aWssIGRlbmdhbiBrYXJha3RlciBrb3NvbmcgKGxpaGF0IGthcmFrdGVyIGAiImAgc2V0ZWxhaCBrYXRhIGAiZW5kcyJgIGRhbiBgImVub3VnaCJgKS4KCmkuIGx1YXJhbiBgc3RyX2V4dHJhY3RfYWxsKClgIGhhbnlhIGFrYW4gbWVuZ2VsdWFya2FuL21lbmdla3N0cmFrc2kga2FyYWt0ZXIgeWFuZyBkaW55YXRha2FuIGRhbGFtIHBvbGEgcGVuY2FyaWFubnlhLCB5YWl0dSBzYXR1IGF0YXUgbGViaWgga2FyYWt0ZXIgYWxmYW51bWVyaXMgZGFuIHN0cmlwOyB0aWRhayBhZGEga2FyYWt0ZXIga29zb25nIHlhbmcgbWVuY2VybWlua2FuIGRhbGFtIGhhbCBpbmkgdGFuZGEgdGl0aWsuCgpLaXRhIHlhbmcgbWVuZW50dWthbiBmdW5nc2kgZGFuIGx1YXJhbiBzZXBlcnRpIGFwYSB5YW5nIGRpaW5naW5rYW4uCgoKIyBNZW5ndWJhaCBrb21wb25lbiB0ZWtzCgojIyBNZW5ndWJhaCBrYXJha3RlciBtZW5qYWRpIGh1cnVmIGJlc2FyIGF0YXUga2VjaWwKCkZ1bmdzaSBgc3RyX3RvX3VwcGVyKClgIG1lbmd1YmFoIGthcmFrdGVyIG1lbmphZGkgaHVydWYga2FwaXRhbCBkYW4gYHN0cl90b19sb3dlcigpYCBtZW5qYWRpIGh1cnVmIGtlY2lsLgoKYGBge3IgdXBwZXItY2FzZX0KIyBtZW5ndWJhaCBuYW1hIHdhcm5hIHNlYmVsdW1ueWEgbWVuamFkaSBodXJ1ZiBrYXBpdGFsCndvcmRfY29sb3VyX2NhcHMgPC0gc3RyX3RvX3VwcGVyKHN0cmluZyA9IHdvcmRfY29sb3VyX3ZlY3RvcikKd29yZF9jb2xvdXJfY2FwcwpgYGAKCmBgYHtyIGxvd2VyLWNhc2V9CiMgbWVuZ3ViYWggbmFtYSB3YXJuYSBrYXBpdGFsIG1lbmphZGkgaHVydWYga2VjaWwKc3RyX3RvX2xvd2VyKHN0cmluZyA9IHdvcmRfY29sb3VyX2NhcHMpCmBgYAoKIyMjIExhdGloYW4gey19CgoxLiBMaWhhdCBpc2kgdmVrdG9yIGB0d29fc2VudGVuY2VzYCBwYWRhIGtvbnNvbCBkYW4gcGVyaGF0aWthbiBiZW50dWsgcmFuZ2thaWFuIGthcmFrdGVybnlhLiBLZW11ZGlhbiBqYWxhbmthbiBmdW5nc2kgYHN0cl90b190aXRsZSgpYCBkZW5nYW4gaW5wdXQgYHR3b19zZW50ZW5jZXNgLiBQZXJ1YmFoYW4gYXBhIHlhbmcgdGVyamFkaSBkYW4gYXBhIHlhbmcgZGlsYWt1a2FuIG9sZWggYHN0cl90b190aXRsZSgpYD8KCiMjIE1lbmdnYW50aSBrb21wb25lbiB0ZWtzIGRlbmdhbiBrYXJha3RlciBsYWluCgpUZXJrYWRhbmcga2l0YSBwZXJsdSBtZW5ndWJhaCBrb21wb25lbiB0ZWtzIHRlcnRlbnR1IGxheWFrbnlhIGZ1bmdzaSAqZmluZCBhbmQgcmVwbGFjZSogcGFkYSBwZXJhbnRpIHNlcGVydGkgTVMgV29yZC4gRGFsYW0gc3RyaW5nciwga2l0YSBiaXNhIG1lbmdndW5ha2FuIGBzdHJfcmVwbGFjZSgpYCBkYW4gYHN0cl9yZXBsYWNlX2FsbCgpYC4gCgpWYXJpYW4gZGFzYXIgKip0YW5wYSoqIGAuLi5fYWxsKClgLCB5YWl0dSBgc3RyX3JlcGxhY2UoKWAsIGhhbnlhIGFrYW4gbWVuZ2dhbnRpIGthcmFrdGVyIHlhbmcgKipwZXJ0YW1hIGthbGkqKiBkaXRhbmdrYXAgb2xlaCBwb2xhIHBlbmNhcmlhbm55YSwgc2VkYW5na2FuIGthcmFrdGVyIHlhbmcgc2FtYSB5YW5nIG11bmN1bCBzZXRlbGFobnlhIHRpZGFrIGFrYW4gZGl0YW5na2FwIGRhbiBkaWdhbnRpLgoKUGVyaGF0aWthbiBrb2RlIGJlcmlrdXQgeWFuZyBtZW5jb2JhIG1lbmdnYW50aSBrYXJha3RlciBgImUiYCBwYWRhIHZla3RvciBgZnJ1aXRfY2FrZWAuCgpgYGB7cn0KIyBsaWhhdCBrZW1iYWxpIHZla3RvciBgZnJ1aXRfY2FrZWAKZnJ1aXRfY2FrZQpgYGAKCgpgYGB7ciBtZW5nZ2FudGktc2VkZXJoYW5hfQojIGd1bmFrYW4gc3RyX3JlcGxhY2UoKSB1bnR1ayBtZW5nZ2FudGkga2FyYWt0ZXIgYCJlImAgeWFuZyBkaXRlbXVrYW4gcGVydGFtYSBkZW5nYW4gc3RyaXAgYCItImAKc3RyX3JlcGxhY2Uoc3RyaW5nID0gZnJ1aXRfY2FrZSwgcGF0dGVybiA9ICJlIiwgcmVwbGFjZW1lbnQgPSAiLSIpCmBgYAoKYGBge3IgbWVuZ2dhbnRpLXNlbXVhfQojIGd1bmFrYW4gc3RyX3JlcGxhY2VfYWxsKCkgdW50dWsgbWVuZ2dhbnRpIFNFTVVBIGthcmFrdGVyIGAiZSJgIGRlbmdhbiBzdHJpcCBgIi0iYApzdHJfcmVwbGFjZV9hbGwoc3RyaW5nID0gZnJ1aXRfY2FrZSwgcGF0dGVybiA9ICJlIiwgcmVwbGFjZW1lbnQgPSAiLSIpCmBgYAoKIyMjIExhdGloYW4gey19CgoxLiBHYW50aSByYW5na2FpYW4ga2FyYWt0ZXIgdW50dWsga2F0YSBgImFwcGxlImAgcGFkYSB2ZWt0b3IgYGZydWl0X2Nha2VgICh5YW5nIGJlcmlzaSBgImFwcGxlIHBpZSJgLCBgImFwcGxlImAsIGRhbiBgImFwcGxlIGNha2UiYCkgZGVuZ2FuIGthdGEgYCJidXR0ZXIiYC4KCgojIE1lbmdnYWJ1bmdrYW4gdmVrdG9yIHJhbmdrYWlhbiBrYXJha3RlcgoKVW50dWsgbWVuZ2dhYnVuZ2thbiBkdWEgYXRhdSBsZWJpaCByYW5na2FpYW4ga2FyYWt0ZXIsIGd1bmFrYW4gYHN0cl9jKClgLgoKYGBge3IgZ2FidW5nLXNlZGVyaGFuYX0KIyBtZW5nZ2FidW5na2FuIGR1YSByYW5na2FpYW4ga2FyYWt0ZXI6ICJiYSIgZGFuICJsaSIKc3RyX2MoImJhIiwgImxpIikKYGBgCgpgYGB7ciBnYWJ1bmctc2VkZXJoYW5hLTF9CmthcmFrdGVyXzEgPC0gYygiYiIsICJsIiwgImsiKQprYXJha3Rlcl8yIDwtIGMoImEiLCAiaSIsICJ1IikKCiMgZ2FidW5na2FuIHRpZ2Ega2FyYWt0ZXIgZGFyaSBkdWEgdmVrdG9yCnN0cl9jKGthcmFrdGVyXzEsIGthcmFrdGVyXzIpCmBgYAoKYGBge3IgZ2FidW5nLXNlZGVyaGFuYS0yfQojIGdhYnVuZ2thbiBkYW4gYXBpdCB0aWdhIGthcmFrdGVyIGRhbGFtIHNhdHUgdmVrdG9yIGRlbmdhbiBkdWEga2FyYWt0ZXIgbGFpbiB5YWl0dSAiPG0+IgpzdHJfYygiPG0+Iiwga2FyYWt0ZXJfMSwgIjxtPiIpCmBgYAoKSmlrYSBzYWxhaCBzYXR1IGRhcmkgZHVhIHZla3RvciBrYXJha3RlciB5YW5nIGFrYW4gZGlnYWJ1bmcgbWVtaWxpa2kganVtbGFoIGVsZW1lbiB5YW5nIGxlYmloIHNlZGlraXQsIG1ha2EgZWxlbWVuIHBhZGEgdmVrdG9yIHRlcnNlYnV0IGFrYW4gZGlndW5ha2FuIHVsYW5nLiBQZXJoYXRpa2FuIGNvbnRvaCBiZXJpa3V0LgoKYGBge3IgZ2FidW5nLWJlZGEtdWt1cmFufQp0d29fd29yZF9mcnVpdCA8LSBjKCJhcHBsZSIsICJiYW5hbmEiKQpmb3VyX3dvcmRfc25hY2sgPC0gYygiY2FrZSIsICJqdWljZSIsICJwaWUiLCAiY2hvY29sYXRlIikKCiMgZ2FidW5na2FuIGtlZHVhIHZla3RvciBkaSBhdGFzCnN0cl9jKHR3b193b3JkX2ZydWl0LCBmb3VyX3dvcmRfc25hY2ssIHNlcCA9ICJfIikKYGBgCgpHdW5ha2FuIGFyZ3VtZW4gYHNlcCA9IC4uLmAgdW50dWsgbWVuZ2F0dXIgcGVtaXNhaCBkaSBhbnRhcmEgcmFuZ2thaWFuIGthcmFrdGVyIHlhbmcgZGlnYWJ1bmdrYW4uIFNlY2FyYSBiYXdhYW4sIGFyZ3VtZW4gYHNlcCA9IC4uLmAgdGVsYWggZGlhdHVyIG1lbmphZGkgYHNlcCA9ICIiYC4KCmBgYHtyIGdhYnVuZy1zZXBhcmF0b3J9CiMgYXR1ciBwZW1pc2FoIGRpIGFudGFyYSBrYXJha3RlciB5YW5nIGRpZ2FidW5na2FuIGRlbmdhbiBgc2VwID0gLi4uYApzdHJfYyhrYXJha3Rlcl8xLCBrYXJha3Rlcl8yLCBzZXAgPSAiXyIpCmBgYAoKQXJndW1lbiBgY29sbGFwc2UgPSAuLi5gIGFrYW4gbWVueWF0dWthbiBzZWp1bWxhaCByYW5na2FpYW4ga2FyYWt0ZXIgbWVuamFkaSAqKnNhdHUqKiByYW5na2FpYW4ga2FyYWt0ZXIuIEJhbmRpbmdrYW4ga2VkdWEga29kZSBiZXJpa3V0IHlhbmcgbWVuZ2d1bmFrYW4gYHNlcCA9IC4uLmAgZGFuIGBjb2xsYXBzZSA9IC4uLmA6CgpgYGB7ciBzZXBhcmF0ZWQtY29tYmluYXRpb259CiMgZGVuZ2FuIHNlcCA9IC4uLiwgcGVuZ2dhYnVuZ2FuIGJlcmlrdXQgbWVuZ2hhc2lsa2FuIHRpZ2EgZWxlbWVuIHJhbmdrYWlhbiBrYXJha3Rlcgp0aHJlZV9zdHJpbmdzIDwtIHN0cl9jKCI8bT4iLCBrYXJha3Rlcl8xLCAiPG0+Iiwgc2VwID0gIl8iKQp0aHJlZV9zdHJpbmdzCgojIEhpdHVuZyBqdW1sYWggZWxlbWVuIGRhbGFtIHZla3RvciBgdGhyZWVfc3RyaW5nc2AKbGVuZ3RoKHRocmVlX3N0cmluZ3MpCmBgYAoKYGBge3IgY29sbGFwc2V9CiMgZGVuZ2FuIGNvbGxhcHNlID0gLi4uLCBwZW5nZ2FidW5nYW4gYmVyaWt1dCBtZW5naGFzaWxrYW4gc2F0dSBlbGVtZW4gcmFuZ2thaWFuIGthcmFrdGVyCm9uZV9zdHJpbmcgPC0gc3RyX2MoIjxtPiIsIGthcmFrdGVyXzEsICI8bT4iLCBjb2xsYXBzZSA9ICJfIikKb25lX3N0cmluZwoKIyBIaXR1bmcganVtbGFoIGVsZW1lbiBkYWxhbSB2ZWt0b3IgYG9uZV9zdHJpbmdgCmxlbmd0aChvbmVfc3RyaW5nKQpgYGAKCiMgTWVueWltcGFuIHZla3RvciBrYXJha3RlciBrZSBkYWxhbSBiZXJrYXMgdGVrcyAoKnBsYWluIHRleHQqKQoKR3VuYWthbiBmdW5nc2kgYGNhdCgpYCB5YW5nIHRlcnNlZGlhIGRhcmkgaW5zdGFsYXNpIGRhc2FyIFIgdW50dWsgbWVueWltcGFuIHZla3RvciBrYXJha3RlciBrZSBkYWxhbSBiZXJrYXMgdGVrcyBzZWRlcmhhbmEgKCpwbGFpbiB0ZXh0KiBkZW5nYW4gZWtzdGVuc2kgYC50eHRgKS4KCmBgYHtyIHNhdmluZy1jaGFyYWN0ZXItdmVjdG9yfQojIHNpbXBhbiB2ZWt0b3IgbmFtYSB3YXJuYSBrZSBiZXJrYXMgYCJzZWxlY3RlZC1jb2xvdXJzLnR4dCJgCiMgZGFsYW0gZGlyZWt0b3JpIGByZXN1bHRzYAojIHlhbmcgbWFzaW5nLW1hc2luZyBlbGVtZW4gdmVrdG9ybnlhIGRpcGlzYWhrYW4gZGVuZ2FuIGJhcmlzIGJhcnUgKGBzZXA9IlxuImApCmNhdCh3b3JkX2NvbG91cl92ZWN0b3IsIGZpbGUgPSAicmVzdWx0cy9zZWxlY3RlZC1jb2xvdXJzLnR4dCIsIHNlcCA9ICJcbiIpCmBgYAoKCgojICpTZXNzaW9uIGluZm8qIHstfQoKYGBge3Igc2Vzc2luZm99CmRldnRvb2xzOjpzZXNzaW9uX2luZm8oKQpgYGAKCgojIERhZnRhciBhY3VhbiB7LX0K