Base R Functions

Creating Strings

The most basic way to create strings is to use quotation marks and assign a string to an object.

quote <- "The Legend"

author <- "Fraser Myers"

The paste() function under Base R is used for creating and building strings. str_c() is equivalent to the paste() function.

paste(quote, "by", author)
[1] "The Legend by Fraser Myers"

Use paste0() to paste without spaces between characters.

paste0("I", "love", "data")
[1] "Ilovedata"

Converting to Strings

Strings and characters can be tested with is.character() and any other data format can be converted into strings/characters with as.character().

is.character(quote)
[1] TRUE
as.character(pi)
[1] "3.14159265358979"

Printing Strings

Printing strings/characters can be done with the following:

Print without quotes.

print( paste(quote,author) , quote = FALSE)
[1] The Legend Fraser Myers

Same as above, but cat() does not print the numeric [1]

cat( paste(quote,author) )
The Legend Fraser Myers

Print alphabet

cat(letters)
a b c d e f g h i j k l m n o p q r s t u v w x y z

Specify a separator between the combined characters

cat(letters, sep = "-")
a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z

Print with no breaks between lines

cat(quote, author, fill = FALSE)
The Legend Fraser Myers

Print with breaks between lines

cat(letters, letters, letters, fill = TRUE)
a b c d e f g h i j k l m n o p q r s t u v w x y z a b c d e f g h i j k l m 
n o p q r s t u v w x y z a b c d e f g h i j k l m n o p q r s t u v w x y z

Counting String Elements

Count number of elements in a string using length()

length("Fraser Myers is a God")
[1] 1
length( c("How", "many", "elements", "are", "in", "this", "string?") )
[1] 7

Count how many characters in a string using nchar()

nchar("Fraser Myers is a God")
[1] 21
nchar(c("How", "many", "characters", "are", "in", "this", "string?"))
[1]  3  4 10  3  2  4  7

Upper/Lower Case Conversion

To convert all upper case characters to lower use tolower()

To convert all lower case characters to upper use toupper()

a <- "MATH2349 is AWesomE"

tolower(a)
[1] "math2349 is awesome"
toupper(a)
[1] "MATH2349 IS AWESOME"

Simple Character Replacement

To replace a character in a string use chartr()

# replace 'z' with 's'
american <- "This is how we analyze."
chartr(old = "z", new = "s", american)
[1] "This is how we analyse."
# replace 'i' with 'w', 'X' with 'h' and 's' with 'y'
x <- "MiXeD cAsE 123"
chartr(old ="iXs", new ="why", x)
[1] "MwheD cAyE 123"

Pattern Replacement

To replace a pattern in a string use gsub()

# replace "ot" pattern with "ut"
x <- "R Totorial"
gsub(pattern = "ot", replacement="ut", x)
[1] "R Tutorial"

String Abbreviations

To abbreviate strings we can use abbreviate()

streets <- c("Victoria", "Yarra", "Russell", "Williams", "Swanston")
# default abbreviations
abbreviate(streets)
Victoria    Yarra  Russell Williams Swanston 
  "Vctr"   "Yarr"   "Rssl"   "Wllm"   "Swns" 
# set minimum length of abbreviation
abbreviate(streets, minlength = 2)
Victoria    Yarra  Russell Williams Swanston 
    "Vc"     "Yr"     "Rs"     "Wl"     "Sw" 

Extract/Replace Substrings

The purpose of subtr() is to extract and replace substrings with specified starting and stopping characters.

alphabet <- paste(LETTERS, collapse = "")
alphabet
[1] "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
# extract 18-24th characters in alphabet
substr(alphabet, start = 18, stop = 24)
[1] "RSTUVWX"
# replace 19-24th characters with `R`
substr(alphabet, start = 19, stop = 24) <- "RRRRRR"
alphabet
[1] "ABCDEFGHIJKLMNOPQRRRRRRRYZ"

To split the elements of a character string use strsplit()

z <- "Victoria Yarra Russell Williams Swanston"
strsplit(z, split = " ")
[[1]]
[1] "Victoria" "Yarra"    "Russell"  "Williams" "Swanston"
a <- "Victoria-Yarra-Russell-Williams-Swanston"
strsplit(a, split = "-")
[[1]]
[1] "Victoria" "Yarra"    "Russell"  "Williams" "Swanston"

Note that the output of strsplit() is a list. To convert the output to a simple atomic vector simply use unlist()

unlist(strsplit(a, split = "-"))
[1] "Victoria" "Yarra"    "Russell"  "Williams" "Swanston"

Set Operations for Character Strings

set_1 <- c("VIC", "NSW", "WA", "TAS")
set_2 <- c("TAS", "QLD", "SA", "NSW")
union(set_1, set_2)
[1] "VIC" "NSW" "WA"  "TAS" "QLD" "SA" 
intersect(set_1, set_2)
[1] "NSW" "TAS"
setdiff(set_1, set_2)
[1] "VIC" "WA" 

Stringr Functions

Duplicate Characters within a String

The stringr provides a new functionality using str_dup() in which base R does not have a specific function for is character duplication.

library(stringr)

str_dup("apples", times = 4)
[1] "applesapplesapplesapples"
library(stringr)

str_dup("apples", times = 1:4)
[1] "apples"                   "applesapples"            
[3] "applesapplesapples"       "applesapplesapplesapples"

Remove Leading and Trailing Whitespace

In string processing, a common task is parsing text into individual words.

Often, this results in words having blank spaces (whitespaces) on either end of the word. The str_trim() can be used to remove these spaces.

text <- c("Text ", "  with", " whitespace ", " on", "both ", " sides ")
text
[1] "Text "        "  with"       " whitespace " " on"         
[5] "both "        " sides "     
str_trim(text, side = "left")
[1] "Text "       "with"        "whitespace " "on"          "both "      
[6] "sides "     
str_trim(text, side = "right")
[1] "Text"        "  with"      " whitespace" " on"         "both"       
[6] " sides"     

Pad a String With Whitespace

Conversely, to add whitespace, or to pad a string, we can use str_pad().

str_pad("apples", width = 10, side = "left")
[1] "    apples"
str_pad("apples", width = 10, side = "both")
[1] "  apples  "

Use str_pad() to pad a string with specified characters. The width argument will give width of padded strings and the pad argument will specify the padding characters.

str_pad("apples", width = 10, side = "right", pad = "!")
[1] "apples!!!!"

Pattern Detection with str_detect()

str_detect() detects the presence or absence of a pattern and returns a logical vector.

# detects pattern "ea"
x <- c("apple", "banana", "pear")
str_detect(x, pattern ="ea")
[1] FALSE FALSE  TRUE
#same as above
str_detect(x, "ea")

Regular Expressions (Regex)

While matching patterns, you can also use the regular expressions.

Regular expressions (a.k.a. regex’s) are a language that allow you to describe patterns in strings.

You can perform a case-insensitive match using ignore_case = TRUE:

bananas <- c("banana", "Banana", "BANANA")
#case insensitive match
str_detect(bananas, regex("banana",ignore_case = TRUE))
[1] TRUE TRUE TRUE

With regex, you can create your own character classes using [ ].

  • [abc]: matches a, b, or c
  • [a-z]: matches every character between a and z (in Unicode code point order).
  • [^abc]: matches anything except a, b, or c.
  • [\^\-]: matches ^ or -.

There are a number of pre-built classes that you can use inside [].

String Subsetting with str_subset()

str_subset() returns the elements of a character vector that match a regular expression.

Using starwars data set, let’s subset the character names that contain any punctuation:

stars <- c("Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia Organa", "Owen Lars")
stars
[1] "Luke Skywalker" "C-3PO"          "R2-D2"          "Darth Vader"   
[5] "Leia Organa"    "Owen Lars"     
str_subset(stars, "[:punct:]")
[1] "C-3PO" "R2-D2"

String Extract using str_extract()

str_extract() extracts text corresponding to the first match, returning a character vector.

str_extract(stars, "[:punct:]")
[1] NA  "-" "-" NA  NA  NA 

Finding Patterns using str_locate()

str_locate() locates the first position of a pattern and returns a numeric matrix with columns start and end whereas str_locate_all() locates all positions of a given pattern.

str_locate(stars, "[:punct:]") %>% head()
     start end
[1,]    NA  NA
[2,]     2   2
[3,]     3   3
[4,]    NA  NA
[5,]    NA  NA
[6,]    NA  NA

Pattern Counting using str_count()

str_count() counts the number of matches for a given string

str_count(stars, "[:punct:]")
[1] 0 1 1 0 0 0

String Replacing with str_replace()

str_replace() replaces a string with another one.

The pattern argument will give the string that is going to be replaced and replacement argument will specify the replacement string.

head(fruit)
[1] "apple"       "apricot"     "avocado"     "banana"      "bell pepper"
[6] "bilberry"   
# Replace berry with berries
head(str_replace(fruit, pattern = "berry", replacement = "berries"))
[1] "apple"       "apricot"     "avocado"     "banana"      "bell pepper"
[6] "bilberries" 
#replace first l with "" (delete first l)
str_replace("Hello world", pattern = "l", replacement = "")
[1] "Helo world"
# replace all l's with "" (delete l's)
str_replace_all("Hello world", pattern = "l", replacement = "")
[1] "Heo word"
LS0tDQp0aXRsZTogIlN0cmluZyBNYW5pcHVsYXRpb24iDQpvdXRwdXQ6DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogbm9uZQ0KLS0tDQoNCiMjIEJhc2UgUiBGdW5jdGlvbnMNCg0KIyMjIENyZWF0aW5nIFN0cmluZ3MNCg0KVGhlIG1vc3QgYmFzaWMgd2F5IHRvIGNyZWF0ZSBzdHJpbmdzIGlzIHRvIHVzZSBxdW90YXRpb24gbWFya3MgYW5kIGFzc2lnbiBhIHN0cmluZyB0byBhbiBvYmplY3QuDQoNCmBgYHtyfQ0KcXVvdGUgPC0gIlRoZSBMZWdlbmQiDQoNCmF1dGhvciA8LSAiRnJhc2VyIE15ZXJzIg0KYGBgDQoNClRoZSAqKnBhc3RlKCkqKiBmdW5jdGlvbiB1bmRlciBCYXNlIFIgaXMgdXNlZCBmb3IgY3JlYXRpbmcgYW5kIGJ1aWxkaW5nIHN0cmluZ3MuICoqc3RyX2MoKSoqIGlzIGVxdWl2YWxlbnQgdG8gdGhlICoqcGFzdGUoKSoqIGZ1bmN0aW9uLg0KDQpgYGB7cn0NCnBhc3RlKHF1b3RlLCAiYnkiLCBhdXRob3IpDQpgYGANCg0KVXNlICoqcGFzdGUwKCkqKiB0byBwYXN0ZSB3aXRob3V0IHNwYWNlcyBiZXR3ZWVuIGNoYXJhY3RlcnMuDQoNCmBgYHtyfQ0KcGFzdGUwKCJJIiwgImxvdmUiLCAiZGF0YSIpDQpgYGANCg0KIyMjIENvbnZlcnRpbmcgdG8gU3RyaW5ncw0KDQpTdHJpbmdzIGFuZCBjaGFyYWN0ZXJzIGNhbiBiZSB0ZXN0ZWQgd2l0aCAqKmlzLmNoYXJhY3RlcigpKiogYW5kIGFueSBvdGhlciBkYXRhIGZvcm1hdCBjYW4gYmUgY29udmVydGVkIGludG8gc3RyaW5ncy9jaGFyYWN0ZXJzIHdpdGggKiphcy5jaGFyYWN0ZXIoKSoqLiANCg0KYGBge3J9DQppcy5jaGFyYWN0ZXIocXVvdGUpDQpgYGANCg0KYGBge3J9DQphcy5jaGFyYWN0ZXIocGkpDQpgYGANCg0KIyMjIFByaW50aW5nIFN0cmluZ3MNCg0KUHJpbnRpbmcgc3RyaW5ncy9jaGFyYWN0ZXJzIGNhbiBiZSBkb25lIHdpdGggdGhlIGZvbGxvd2luZzoNCg0KIVtdKHByaW50LmpwZykNCg0KUHJpbnQgd2l0aG91dCBxdW90ZXMuIA0KYGBge3J9DQpwcmludCggcGFzdGUocXVvdGUsYXV0aG9yKSAsIHF1b3RlID0gRkFMU0UpDQpgYGANCg0KU2FtZSBhcyBhYm92ZSwgYnV0ICoqY2F0KCkqKiBkb2VzIG5vdCBwcmludCB0aGUgbnVtZXJpYyBbMV0NCg0KYGBge3J9DQpjYXQoIHBhc3RlKHF1b3RlLGF1dGhvcikgKQ0KYGBgDQoNClByaW50IGFscGhhYmV0DQoNCmBgYHtyfQ0KY2F0KGxldHRlcnMpDQpgYGANCg0KU3BlY2lmeSBhIHNlcGFyYXRvciBiZXR3ZWVuIHRoZSBjb21iaW5lZCBjaGFyYWN0ZXJzDQoNCmBgYHtyfQ0KY2F0KGxldHRlcnMsIHNlcCA9ICItIikNCmBgYA0KDQpQcmludCB3aXRoIG5vIGJyZWFrcyBiZXR3ZWVuIGxpbmVzDQoNCmBgYHtyfQ0KY2F0KHF1b3RlLCBhdXRob3IsIGZpbGwgPSBGQUxTRSkNCmBgYA0KDQpQcmludCB3aXRoIGJyZWFrcyBiZXR3ZWVuIGxpbmVzDQoNCmBgYHtyfQ0KY2F0KGxldHRlcnMsIGxldHRlcnMsIGxldHRlcnMsIGZpbGwgPSBUUlVFKQ0KYGBgDQoNCiMjIyBDb3VudGluZyBTdHJpbmcgRWxlbWVudHMNCg0KQ291bnQgbnVtYmVyIG9mIGVsZW1lbnRzIGluIGEgc3RyaW5nIHVzaW5nICoqbGVuZ3RoKCkqKg0KDQpgYGB7cn0NCmxlbmd0aCgiRnJhc2VyIE15ZXJzIGlzIGEgR29kIikNCmBgYA0KDQpgYGB7cn0NCmxlbmd0aCggYygiSG93IiwgIm1hbnkiLCAiZWxlbWVudHMiLCAiYXJlIiwgImluIiwgInRoaXMiLCAic3RyaW5nPyIpICkNCmBgYA0KDQpDb3VudCBob3cgbWFueSBjaGFyYWN0ZXJzIGluIGEgc3RyaW5nIHVzaW5nICoqbmNoYXIoKSoqDQoNCmBgYHtyfQ0KbmNoYXIoIkZyYXNlciBNeWVycyBpcyBhIEdvZCIpDQpgYGANCg0KYGBge3J9DQpuY2hhcihjKCJIb3ciLCAibWFueSIsICJjaGFyYWN0ZXJzIiwgImFyZSIsICJpbiIsICJ0aGlzIiwgInN0cmluZz8iKSkNCmBgYA0KDQojIyMgVXBwZXIvTG93ZXIgQ2FzZSBDb252ZXJzaW9uDQoNClRvIGNvbnZlcnQgYWxsIHVwcGVyIGNhc2UgY2hhcmFjdGVycyB0byBsb3dlciB1c2UgKip0b2xvd2VyKCkqKg0KDQpUbyBjb252ZXJ0IGFsbCBsb3dlciBjYXNlIGNoYXJhY3RlcnMgdG8gdXBwZXIgdXNlICoqdG91cHBlcigpKioNCg0KYGBge3J9DQphIDwtICJNQVRIMjM0OSBpcyBBV2Vzb21FIg0KDQp0b2xvd2VyKGEpDQpgYGANCg0KYGBge3J9DQp0b3VwcGVyKGEpDQpgYGANCg0KIyMjIFNpbXBsZSBDaGFyYWN0ZXIgUmVwbGFjZW1lbnQNCg0KVG8gcmVwbGFjZSBhIGNoYXJhY3RlciBpbiBhIHN0cmluZyB1c2UgKipjaGFydHIoKSoqDQoNCmBgYHtyfQ0KIyByZXBsYWNlICd6JyB3aXRoICdzJw0KYW1lcmljYW4gPC0gIlRoaXMgaXMgaG93IHdlIGFuYWx5emUuIg0KY2hhcnRyKG9sZCA9ICJ6IiwgbmV3ID0gInMiLCBhbWVyaWNhbikNCmBgYA0KDQpgYGB7cn0NCiMgcmVwbGFjZSAnaScgd2l0aCAndycsICdYJyB3aXRoICdoJyBhbmQgJ3MnIHdpdGggJ3knDQp4IDwtICJNaVhlRCBjQXNFIDEyMyINCmNoYXJ0cihvbGQgPSJpWHMiLCBuZXcgPSJ3aHkiLCB4KQ0KYGBgDQoNCiMjIyBQYXR0ZXJuIFJlcGxhY2VtZW50DQoNClRvIHJlcGxhY2UgYSBwYXR0ZXJuIGluIGEgc3RyaW5nIHVzZSAqKmdzdWIoKSoqDQoNCmBgYHtyfQ0KIyByZXBsYWNlICJvdCIgcGF0dGVybiB3aXRoICJ1dCINCnggPC0gIlIgVG90b3JpYWwiDQpnc3ViKHBhdHRlcm4gPSAib3QiLCByZXBsYWNlbWVudD0idXQiLCB4KQ0KYGBgDQoNCiMjIyBTdHJpbmcgQWJicmV2aWF0aW9ucw0KDQpUbyBhYmJyZXZpYXRlIHN0cmluZ3Mgd2UgY2FuIHVzZSAqKmFiYnJldmlhdGUoKSoqDQoNCmBgYHtyfQ0Kc3RyZWV0cyA8LSBjKCJWaWN0b3JpYSIsICJZYXJyYSIsICJSdXNzZWxsIiwgIldpbGxpYW1zIiwgIlN3YW5zdG9uIikNCiMgZGVmYXVsdCBhYmJyZXZpYXRpb25zDQphYmJyZXZpYXRlKHN0cmVldHMpDQpgYGANCg0KYGBge3J9DQojIHNldCBtaW5pbXVtIGxlbmd0aCBvZiBhYmJyZXZpYXRpb24NCmFiYnJldmlhdGUoc3RyZWV0cywgbWlubGVuZ3RoID0gMikNCmBgYA0KDQojIyMgRXh0cmFjdC9SZXBsYWNlIFN1YnN0cmluZ3MNCg0KVGhlIHB1cnBvc2Ugb2YgKipzdWJ0cigpKiogaXMgdG8gZXh0cmFjdCBhbmQgcmVwbGFjZSBzdWJzdHJpbmdzIHdpdGggc3BlY2lmaWVkIHN0YXJ0aW5nIGFuZCBzdG9wcGluZyBjaGFyYWN0ZXJzLg0KDQpgYGB7cn0NCmFscGhhYmV0IDwtIHBhc3RlKExFVFRFUlMsIGNvbGxhcHNlID0gIiIpDQphbHBoYWJldA0KYGBgDQoNCmBgYHtyfQ0KIyBleHRyYWN0IDE4LTI0dGggY2hhcmFjdGVycyBpbiBhbHBoYWJldA0Kc3Vic3RyKGFscGhhYmV0LCBzdGFydCA9IDE4LCBzdG9wID0gMjQpDQpgYGANCg0KYGBge3J9DQojIHJlcGxhY2UgMTktMjR0aCBjaGFyYWN0ZXJzIHdpdGggYFJgDQpzdWJzdHIoYWxwaGFiZXQsIHN0YXJ0ID0gMTksIHN0b3AgPSAyNCkgPC0gIlJSUlJSUiINCmFscGhhYmV0DQpgYGANCg0KVG8gc3BsaXQgdGhlIGVsZW1lbnRzIG9mIGEgY2hhcmFjdGVyIHN0cmluZyB1c2UgKipzdHJzcGxpdCgpKiogDQoNCmBgYHtyfQ0KeiA8LSAiVmljdG9yaWEgWWFycmEgUnVzc2VsbCBXaWxsaWFtcyBTd2Fuc3RvbiINCnN0cnNwbGl0KHosIHNwbGl0ID0gIiAiKQ0KYGBgDQoNCmBgYHtyfQ0KYSA8LSAiVmljdG9yaWEtWWFycmEtUnVzc2VsbC1XaWxsaWFtcy1Td2Fuc3RvbiINCnN0cnNwbGl0KGEsIHNwbGl0ID0gIi0iKQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGUgb3V0cHV0IG9mICoqc3Ryc3BsaXQoKSoqIGlzIGEgbGlzdC4gVG8gY29udmVydCB0aGUgb3V0cHV0IHRvIGEgc2ltcGxlIGF0b21pYyB2ZWN0b3Igc2ltcGx5IHVzZSAqKnVubGlzdCgpKioNCg0KYGBge3J9DQp1bmxpc3Qoc3Ryc3BsaXQoYSwgc3BsaXQgPSAiLSIpKQ0KYGBgDQoNCiMjIyBTZXQgT3BlcmF0aW9ucyBmb3IgQ2hhcmFjdGVyIFN0cmluZ3MNCg0KIVtdKHNldC5qcGcpDQoNCmBgYHtyfQ0Kc2V0XzEgPC0gYygiVklDIiwgIk5TVyIsICJXQSIsICJUQVMiKQ0Kc2V0XzIgPC0gYygiVEFTIiwgIlFMRCIsICJTQSIsICJOU1ciKQ0KdW5pb24oc2V0XzEsIHNldF8yKQ0KYGBgDQoNCmBgYHtyfQ0KaW50ZXJzZWN0KHNldF8xLCBzZXRfMikNCmBgYA0KDQpgYGB7cn0NCnNldGRpZmYoc2V0XzEsIHNldF8yKQ0KYGBgDQoNCiMjIFN0cmluZ3IgRnVuY3Rpb25zDQoNCiMjIyBEdXBsaWNhdGUgQ2hhcmFjdGVycyB3aXRoaW4gYSBTdHJpbmcNCg0KVGhlIHN0cmluZ3IgcHJvdmlkZXMgYSBuZXcgZnVuY3Rpb25hbGl0eSB1c2luZyBzdHJfZHVwKCkgaW4gd2hpY2ggYmFzZSBSIGRvZXMgbm90IGhhdmUgYSBzcGVjaWZpYyBmdW5jdGlvbiBmb3IgaXMgY2hhcmFjdGVyIGR1cGxpY2F0aW9uLg0KDQpgYGB7cn0NCmxpYnJhcnkoc3RyaW5ncikNCg0Kc3RyX2R1cCgiYXBwbGVzIiwgdGltZXMgPSA0KQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShzdHJpbmdyKQ0KDQpzdHJfZHVwKCJhcHBsZXMiLCB0aW1lcyA9IDE6NCkNCmBgYA0KDQojIyMgUmVtb3ZlIExlYWRpbmcgYW5kIFRyYWlsaW5nIFdoaXRlc3BhY2UNCg0KSW4gc3RyaW5nIHByb2Nlc3NpbmcsIGEgY29tbW9uIHRhc2sgaXMgcGFyc2luZyB0ZXh0IGludG8gaW5kaXZpZHVhbCB3b3Jkcy4NCg0KT2Z0ZW4sIHRoaXMgcmVzdWx0cyBpbiB3b3JkcyBoYXZpbmcgYmxhbmsgc3BhY2VzICh3aGl0ZXNwYWNlcykgb24gZWl0aGVyIGVuZCBvZiB0aGUgd29yZC4gVGhlIHN0cl90cmltKCkgY2FuIGJlIHVzZWQgdG8gcmVtb3ZlIHRoZXNlIHNwYWNlcy4NCg0KYGBge3J9DQp0ZXh0IDwtIGMoIlRleHQgIiwgIiAgd2l0aCIsICIgd2hpdGVzcGFjZSAiLCAiIG9uIiwgImJvdGggIiwgIiBzaWRlcyAiKQ0KdGV4dA0KYGBgDQoNCmBgYHtyfQ0Kc3RyX3RyaW0odGV4dCwgc2lkZSA9ICJsZWZ0IikNCmBgYA0KDQpgYGB7cn0NCnN0cl90cmltKHRleHQsIHNpZGUgPSAicmlnaHQiKQ0KYGBgDQoNCiMjIyBQYWQgYSBTdHJpbmcgV2l0aCBXaGl0ZXNwYWNlDQoNCkNvbnZlcnNlbHksIHRvIGFkZCB3aGl0ZXNwYWNlLCBvciB0byBwYWQgYSBzdHJpbmcsIHdlIGNhbiB1c2Ugc3RyX3BhZCgpLg0KDQpgYGB7cn0NCnN0cl9wYWQoImFwcGxlcyIsIHdpZHRoID0gMTAsIHNpZGUgPSAibGVmdCIpDQpgYGANCg0KYGBge3J9DQpzdHJfcGFkKCJhcHBsZXMiLCB3aWR0aCA9IDEwLCBzaWRlID0gImJvdGgiKQ0KYGBgDQoNClVzZSBzdHJfcGFkKCkgdG8gcGFkIGEgc3RyaW5nIHdpdGggc3BlY2lmaWVkIGNoYXJhY3RlcnMuIFRoZSB3aWR0aCBhcmd1bWVudCB3aWxsIGdpdmUgd2lkdGggb2YgcGFkZGVkIHN0cmluZ3MgYW5kIHRoZSBwYWQgYXJndW1lbnQgd2lsbCBzcGVjaWZ5IHRoZSBwYWRkaW5nIGNoYXJhY3RlcnMuDQoNCmBgYHtyfQ0Kc3RyX3BhZCgiYXBwbGVzIiwgd2lkdGggPSAxMCwgc2lkZSA9ICJyaWdodCIsIHBhZCA9ICIhIikNCmBgYA0KDQojIyMgUGF0dGVybiBEZXRlY3Rpb24gd2l0aCBzdHJfZGV0ZWN0KCkNCg0Kc3RyX2RldGVjdCgpIGRldGVjdHMgdGhlIHByZXNlbmNlIG9yIGFic2VuY2Ugb2YgYSBwYXR0ZXJuIGFuZCByZXR1cm5zIGEgbG9naWNhbCB2ZWN0b3IuDQoNCmBgYHtyfQ0KIyBkZXRlY3RzIHBhdHRlcm4gImVhIg0KeCA8LSBjKCJhcHBsZSIsICJiYW5hbmEiLCAicGVhciIpDQpzdHJfZGV0ZWN0KHgsIHBhdHRlcm4gPSJlYSIpDQpgYGANCg0KYGBge3J9DQojc2FtZSBhcyBhYm92ZQ0Kc3RyX2RldGVjdCh4LCAiZWEiKQ0KYGBgDQoNCiMjIyBSZWd1bGFyIEV4cHJlc3Npb25zIChSZWdleCkNCg0KV2hpbGUgbWF0Y2hpbmcgcGF0dGVybnMsIHlvdSBjYW4gYWxzbyB1c2UgdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbnMuDQoNClJlZ3VsYXIgZXhwcmVzc2lvbnMgKGEuay5hLiByZWdleCdzKSBhcmUgYSBsYW5ndWFnZSB0aGF0IGFsbG93IHlvdSB0byBkZXNjcmliZSBwYXR0ZXJucyBpbiBzdHJpbmdzLg0KDQpZb3UgY2FuIHBlcmZvcm0gYSBjYXNlLWluc2Vuc2l0aXZlIG1hdGNoIHVzaW5nIGlnbm9yZV9jYXNlID0gVFJVRToNCg0KYGBge3J9DQpiYW5hbmFzIDwtIGMoImJhbmFuYSIsICJCYW5hbmEiLCAiQkFOQU5BIikNCiNjYXNlIGluc2Vuc2l0aXZlIG1hdGNoDQpzdHJfZGV0ZWN0KGJhbmFuYXMsIHJlZ2V4KCJiYW5hbmEiLGlnbm9yZV9jYXNlID0gVFJVRSkpDQpgYGANCg0KV2l0aCByZWdleCwgeW91IGNhbiBjcmVhdGUgeW91ciBvd24gY2hhcmFjdGVyIGNsYXNzZXMgdXNpbmcgWyBdLiANCg0KLSBgW2FiY106IG1hdGNoZXMgYSwgYiwgb3IgY2ANCi0gYFthLXpdOiBtYXRjaGVzIGV2ZXJ5IGNoYXJhY3RlciBiZXR3ZWVuIGEgYW5kIHogKGluIFVuaWNvZGUgY29kZSBwb2ludCBvcmRlcikuYA0KLSBgW15hYmNdOiBtYXRjaGVzIGFueXRoaW5nIGV4Y2VwdCBhLCBiLCBvciBjLmANCi0gYFtcXlwtXTogbWF0Y2hlcyBeIG9yIC0uYA0KDQpUaGVyZSBhcmUgYSBudW1iZXIgb2YgcHJlLWJ1aWx0IGNsYXNzZXMgdGhhdCB5b3UgY2FuIHVzZSBpbnNpZGUgW10uDQoNCiFbXShwcmUuanBnKQ0KDQojIyMgU3RyaW5nIFN1YnNldHRpbmcgd2l0aCAqKnN0cl9zdWJzZXQoKSoqDQoNCnN0cl9zdWJzZXQoKSByZXR1cm5zIHRoZSBlbGVtZW50cyBvZiBhIGNoYXJhY3RlciB2ZWN0b3IgdGhhdCBtYXRjaCBhIHJlZ3VsYXIgZXhwcmVzc2lvbi4NCg0KVXNpbmcgc3RhcndhcnMgZGF0YSBzZXQsIGxldCdzIHN1YnNldCB0aGUgY2hhcmFjdGVyIG5hbWVzIHRoYXQgY29udGFpbiBhbnkgcHVuY3R1YXRpb246DQoNCmBgYHtyfQ0Kc3RhcnMgPC0gYygiTHVrZSBTa3l3YWxrZXIiLCAiQy0zUE8iLCAiUjItRDIiLCAiRGFydGggVmFkZXIiLCAiTGVpYSBPcmdhbmEiLCAiT3dlbiBMYXJzIikNCnN0YXJzDQpgYGANCg0KYGBge3J9DQpzdHJfc3Vic2V0KHN0YXJzLCAiWzpwdW5jdDpdIikNCmBgYA0KDQojIyMgU3RyaW5nIEV4dHJhY3QgdXNpbmcgKipzdHJfZXh0cmFjdCgpKioNCg0Kc3RyX2V4dHJhY3QoKSBleHRyYWN0cyB0ZXh0IGNvcnJlc3BvbmRpbmcgdG8gdGhlIGZpcnN0IG1hdGNoLCByZXR1cm5pbmcgYSBjaGFyYWN0ZXIgdmVjdG9yLg0KDQpgYGB7cn0NCnN0cl9leHRyYWN0KHN0YXJzLCAiWzpwdW5jdDpdIikNCmBgYA0KDQojIyMgRmluZGluZyBQYXR0ZXJucyB1c2luZyAqKnN0cl9sb2NhdGUoKSoqDQoNCnN0cl9sb2NhdGUoKSBsb2NhdGVzIHRoZSBmaXJzdCBwb3NpdGlvbiBvZiBhIHBhdHRlcm4gYW5kIHJldHVybnMgYSBudW1lcmljIG1hdHJpeCB3aXRoIGNvbHVtbnMgc3RhcnQgYW5kIGVuZCB3aGVyZWFzIHN0cl9sb2NhdGVfYWxsKCkgbG9jYXRlcyBhbGwgcG9zaXRpb25zIG9mIGEgZ2l2ZW4gcGF0dGVybi4NCg0KYGBge3J9DQpzdHJfbG9jYXRlKHN0YXJzLCAiWzpwdW5jdDpdIikgJT4lIGhlYWQoKQ0KYGBgDQoNCiMjIyBQYXR0ZXJuIENvdW50aW5nIHVzaW5nICoqc3RyX2NvdW50KCkqKg0KDQpzdHJfY291bnQoKSBjb3VudHMgdGhlIG51bWJlciBvZiBtYXRjaGVzIGZvciBhIGdpdmVuIHN0cmluZw0KDQpgYGB7cn0NCnN0cl9jb3VudChzdGFycywgIls6cHVuY3Q6XSIpDQpgYGANCg0KIyMjIFN0cmluZyBSZXBsYWNpbmcgd2l0aCAqKnN0cl9yZXBsYWNlKCkqKg0KDQpzdHJfcmVwbGFjZSgpIHJlcGxhY2VzIGEgc3RyaW5nIHdpdGggYW5vdGhlciBvbmUuDQoNClRoZSBwYXR0ZXJuIGFyZ3VtZW50IHdpbGwgZ2l2ZSB0aGUgc3RyaW5nIHRoYXQgaXMgZ29pbmcgdG8gYmUgcmVwbGFjZWQgYW5kIHJlcGxhY2VtZW50IGFyZ3VtZW50IHdpbGwgc3BlY2lmeSB0aGUgcmVwbGFjZW1lbnQgc3RyaW5nLg0KDQpgYGB7cn0NCmhlYWQoZnJ1aXQpDQpgYGANCg0KYGBge3J9DQojIFJlcGxhY2UgYmVycnkgd2l0aCBiZXJyaWVzDQpoZWFkKHN0cl9yZXBsYWNlKGZydWl0LCBwYXR0ZXJuID0gImJlcnJ5IiwgcmVwbGFjZW1lbnQgPSAiYmVycmllcyIpKQ0KYGBgDQoNCmBgYHtyfQ0KI3JlcGxhY2UgZmlyc3QgbCB3aXRoICIiIChkZWxldGUgZmlyc3QgbCkNCnN0cl9yZXBsYWNlKCJIZWxsbyB3b3JsZCIsIHBhdHRlcm4gPSAibCIsIHJlcGxhY2VtZW50ID0gIiIpDQpgYGANCg0KYGBge3J9DQojIHJlcGxhY2UgYWxsIGwncyB3aXRoICIiIChkZWxldGUgbCdzKQ0Kc3RyX3JlcGxhY2VfYWxsKCJIZWxsbyB3b3JsZCIsIHBhdHRlcm4gPSAibCIsIHJlcGxhY2VtZW50ID0gIiIpDQpgYGANCg0K