1 String basics

1.1 Introduction

1.1.1 Quotes

Let’s get started by entering some strings in R. In the video you saw that you use quotes to tell R to interpret something as a string. Both double quotes (") and single (’) quotes work, but there are some guidelines for which to use.

First, you should prefer double quotes (“) to single quotes (’). That means, whenever you are defining a string your first intuition should be to use”.

Unfortunately if your string has " inside it, R will interpret the double quote as “this is the end of the string”, not as “this is the character”". This is one time you can forget the first guideline and use the single quote, ’, to define the string.

Finally, there are cases where you need both ’ and " inside the string. In this case, fall back to the first guideline and use " to define the string, but you’ll have to escape any double quotes inside the string using a backslash (i.e. \").

# Define line1
line1 <- "The table was a large one, but the three were all crowded together at one corner of it:"

# Define line2
line2 <- '"No room! No room!" they cried out when they saw Alice coming.'

# Define line3
line3 <- "\"There's plenty of room!\" said Alice indignantly, and she sat down in a large arm-chair at one end of the table."

1.1.2 What you see isn’t always what you have

Take a look at line2, the string you just defined, by printing it:

line2
[1] "\"No room! No room!\" they cried out when they saw Alice coming."

Even though you used single quotes so you didn’t have to escape any double quotes, when R prints it, you’ll see escaped double quotes (\")! R doesn’t care how you defined the string, it only knows what the string represents, in this case, a string with double quotes inside.

When you ask R for line2 it is actually calling print(line2) and the print() method for strings displays strings as you might enter them. If you want to see the string it represents you’ll need to use a different function: writeLines().

You can pass writeLines() a vector of strings and it will print them to the screen, each on a new line. This is a great way to check the string you entered really does represent the string you wanted.

# Putting lines in a vector
lines <- c(line1, line2, line3)

# Print lines
print(lines)
[1] "The table was a large one, but the three were all crowded together at one corner of it:"                           
[2] "\"No room! No room!\" they cried out when they saw Alice coming."                                                  
[3] "\"There's plenty of room!\" said Alice indignantly, and she sat down in a large arm-chair at one end of the table."
# Use writeLines() on lines
writeLines(lines)
The table was a large one, but the three were all crowded together at one corner of it:
"No room! No room!" they cried out when they saw Alice coming.
"There's plenty of room!" said Alice indignantly, and she sat down in a large arm-chair at one end of the table.
# Write lines with a space separator
writeLines(lines, sep = " ")
The table was a large one, but the three were all crowded together at one corner of it: "No room! No room!" they cried out when they saw Alice coming. "There's plenty of room!" said Alice indignantly, and she sat down in a large arm-chair at one end of the table. 
# Use writeLines() on the string "hello\n\U1F30D"
writeLines("hello\n\U1F30D")
hello
<U+0001F30D>

The function cat() is very similar to writeLines(), but by default separates elements with a space, and will attempt to convert non-character objects to a string.

1.1.3 Escape sequences

How did you get two lines from one string, and how did you get that little globe? The key is the \.

A sequence in a string that starts with a \ is called an escape sequence and allows us to include special characters in our strings. You saw one escape sequence in the first exercise: \" is used to denote a double quote.

In “hello\n\U1F30D” there are two escape sequences: \n gives a newline, and \U followed by up to 8 hex digits sequence denotes a particular Unicode character.

Unicode is a standard for representing characters that might not be on your keyboard. Each available character has a Unicode code point: a number that uniquely identifies it. These code points are generally written in hex notation, that is, using base 16 and the digits 0-9 and A-F. You can find the code point for a particular character by looking up a code chart. If you only need four digits for the codepoint, an alternative escape sequence is \u. When R comes across a \ it assumes you are starting an escape, so if you actually need a backslash in your string you’ll need the sequence \\.

# Should display: To have a \ you need \\
writeLines("To have a \\ you need \\\\")
To have a \ you need \\
# Should display: 
# This is a really 
# really really 
# long string
writeLines("This is a really\nreally really\nlong string")
This is a really
really really
long string
# Use writeLines() with 
# "\u0928\u092e\u0938\u094d\u0924\u0947 \u0926\u0941\u0928\u093f\u092f\u093e" - "Hello World" in Hindi!
writeLines("\u0928\u092e\u0938\u094d\u0924\u0947 \u0926\u0941\u0928\u093f\u092f\u093e") 
<U+0928><U+092E><U+0938><U+094D><U+0924><U+0947> <U+0926><U+0941><U+0928><U+093F><U+092F><U+093E>

1.2 Turning numbers into strings

1.2.1 Using format() with numbers

The behavior of format() can be pretty confusing, so you’ll spend most of this exercise exploring how it works.

Recall from the video, the scientific argument to format() controls whether the numbers are displayed in fixed (scientific = FALSE) or scientific (scientific = TRUE) format.

When the representation is scientific, the digits argument is the number of digits before the exponent. When the representation is fixed, digits controls the significant digits used for the smallest (in magnitude) number. Each other number will be formatted to match the number of decimal places in the smallest number. This means the number of decimal places you get in your output depends on all the values you are formatting!

For example, if the smallest number is 0.0011, and digits = 1, then 0.0011 requires 3 places after the decimal to represent it to 1 significant digit, 0.001. Every other number will be formatted to 3 places after the decimal point. Format c(0.0011, 0.011, 1) with digits = 1. This is like the example described above. Now, format c(1.0011, 2.011, 1) with digits = 1. Try to predict what you might get before you try it. Format percent_change by choosing the digits argument so that the values are presented with one place after the decimal point. Format income by choosing the digits argument so that the values are presented as whole numbers (i.e. no places after the decimal point). Format p_values using a fixed representation.

# Some vectors of numbers
percent_change  <- c(4, -1.91, 3.00, -5.002)
income <-  c(72.19, 1030.18, 10291.93, 1189192.18)
p_values <- c(0.12, 0.98, 0.0000191, 0.00000000002)

# Format c(0.0011, 0.011, 1) with digits = 1
format(c(0.0011, 0.011, 1), digits = 1)
[1] "0.001" "0.011" "1.000"
# Format c(1.0011, 2.011, 1) with digits = 1
format(c(1.0011, 2.011, 1), digits = 1)
[1] "1" "2" "1"
# Format percent_change to one place after the decimal point
format(percent_change, digits = 2)
[1] " 4.0" "-1.9" " 3.0" "-5.0"
# Format income to whole numbers
format(income, digits = 2)
[1] "     72" "   1030" "  10292" "1189192"
# Format p_values in fixed format
format(p_values, scientific = FALSE)
[1] "0.12000000000" "0.98000000000" "0.00001910000" "0.00000000002"

1.2.2 Controlling other aspects of the string

ot only does format() control the way the number is represented, it also controls some of the properties of the resulting string that affect its display.

For example, by default format() will pad the start of the strings with spaces so that the decimal points line up, which is really useful if you are presenting the numbers in a vertical column. However, if you are putting the number in the middle of a sentence, you might not want these extra spaces. You can set trim = TRUE to remove them.

When numbers are long it can be helpful to “prettify” them, for example instead of 1000000000 display 1,000,000,000. In this case a , is added every 3 digits. This can be controlled by the big.interval and big.mark arguments, e.g. format(1000000000, big.mark = “,”, big.interval = 3, scientific = FALSE). These arguments are actually passed on to prettyNum() so head there for any further details.

formatted_income <- format(income, digits = 2)

# Print formatted_income
formatted_income
[1] "     72" "   1030" "  10292" "1189192"
# Call writeLines() on the formatted income
writeLines(formatted_income)
     72
   1030
  10292
1189192
# Define trimmed_income
trimmed_income <- format(income, digits = 2, trim = TRUE)

# Call writeLines() on the trimmed_income
writeLines(trimmed_income)
72
1030
10292
1189192
# Define pretty_income
pretty_income <- format(income, digits = 2, big.mark = ",")

# Call writeLines() on the pretty_income
writeLines(pretty_income)
       72
    1,030
   10,292
1,189,192

1.2.3 formatC()

The function formatC() provides an alternative way to format numbers based on C style syntax.

Rather than a scientific argument, formatC() has a format argument that takes a code representing the required format. The most useful are:

“f” for fixed, “e” for scientific, and “g” for fixed unless scientific saves space When using scientific format, the digits argument behaves like it does in format(); it specifies the number of significant digits. However, unlike format(), when using fixed format, digits is the number of digits after the decimal point. This is more predictable than format(), because the number of places after the decimal is fixed regardless of the values being formatted.

formatC() also formats numbers individually, which means you always get the same output regardless of other numbers in the vector.

The flag argument allows you to provide some modifiers that, for example, force the display of the sign (flag = “+”), left align numbers (flag = “-”) and pad numbers with leading zeros (flag = “0”).

# From the format() exercise
x <- c(0.0011, 0.011, 1)
y <- c(1.0011, 2.011, 1)

# formatC() on x with format = "f", digits = 1
formatC(x, format = "f", digits = 1)
[1] "0.0" "0.0" "1.0"
# formatC() on y with format = "f", digits = 1
formatC(y, format = "f", digits = 1)
[1] "1.0" "2.0" "1.0"
# Format percent_change to one place after the decimal point
formatC(percent_change, format = "f", digits = 1)
[1] "4.0"  "-1.9" "3.0"  "-5.0"
# percent_change with flag = "+"
formatC(percent_change,  format = "f", digits = 1, flag = "+")
[1] "+4.0" "-1.9" "+3.0" "-5.0"
# Format p_values using format = "g" and digits = 2
formatC(p_values, format = "g", digits = 2)
[1] "0.12"    "0.98"    "1.9e-05" "2e-11"  

1.3 Putting strings together

1.3.1 Annotation of numbers

To get a handle on using paste(), you are going to annotate some of your formatted number strings.

The key points to remember are:

  • The vectors you pass to paste() are pasted together element by element, using the sep argument to combine them.
  • If the vectors passed to paste() aren’t the same length, the shorter vectors are recycled up to the length of the longest one.
  • Only use collapse if you want a single string as output. collapse specifies the string to place between different elements.
years <- 2010:2013
pretty_percent <- formatC(percent_change,  format = "f", digits = 1, flag = "+")
# Add $ to pretty_income
paste("$", pretty_income, sep = "")
[1] "$       72" "$    1,030" "$   10,292" "$1,189,192"
 
# Add % to pretty_percent
paste(pretty_percent, "%", sep = "")
[1] "+4.0%" "-1.9%" "+3.0%" "-5.0%"
# Create vector with elements like 2010: +4.0%`
year_percent <- paste(years, ": ", pretty_percent, "%", sep = "")

# Collapse all years into single string
paste(year_percent, collapse = ",")
[1] "2010: +4.0%,2011: -1.9%,2012: +3.0%,2013: -5.0%"

Specifying sep = "" is so common, there is actually another function paste0() that works like paste() but always pastes elements together without a separator between them.

1.3.2 A very simple table

Combining format() and paste() is one way to display very simple tables. Remember, since format() looks at all the values in a vector before formatting, it uses a consistent format and will, by default, align on the decimal point. This is usually the behavior you want for a column of numbers in table.

format() can also take character vectors as input. In this case, you can use the justify argument, specific to character input, to justify the text to the left, right, or center.

# Define the names vector
income_names <- c("Year 0", "Year 1", "Year 2", "Project Lifetime")

# Create pretty_income
pretty_income <- format(income, digits = 2, big.mark = ",")

# Create dollar_income
dollar_income <- paste("$", pretty_income, sep = "")

# Create formatted_names
formatted_names <- format(income_names, justify = "right")

# Create rows
rows <- paste(formatted_names, dollar_income, sep = "   ")

# Write rows
writeLines(rows)
          Year 0   $       72
          Year 1   $    1,030
          Year 2   $   10,292
Project Lifetime   $1,189,192

If you wanted the dollar signs right next to the numbers, you could format the incomes with trim = TRUE, paste on the $, then format again as a string with justify = “right”.

1.3.3 Let’s order pizza!

As a final exercise in using paste() and to celebrate getting to the end of the first chapter, let’s order some pizza.

We’ve got a list of possible pizza toppings in toppings.

You are going to randomly select three toppings, and then put them together using paste() into an order for pizza, that should result in a string like, “I want to order a pizza with mushrooms, spinach, and pineapple.”

toppings <- c("anchovies", "artichoke", "bacon", "breakfast bacon", "Canadian bacon", 
"cheese", "chicken", "chili peppers", "feta", "garlic", "green peppers", 
"grilled onions", "ground beef", "ham", "hot sauce", "meatballs", 
"mushrooms", "olives", "onions", "pepperoni", "pineapple", "sausage", 
"spinach", "sun-dried tomato", "tomatoes")
# Randomly sample 3 toppings
my_toppings <- sample(toppings, size = 3)

# Print my_toppings
my_toppings
[1] "green peppers"  "anchovies"      "grilled onions"
# Paste "and " to last element: my_toppings_and
my_toppings_and <- paste(c("", "", "and "), my_toppings, sep = "")

# Collapse with comma space: these_toppings
these_toppings <- paste(my_toppings_and, collapse = ", ")

# Add rest of sentence: my_order
my_order <- paste("I want to order a pizza with ", these_toppings, ".", sep = "")

# Order pizza with writeLines()
writeLines(my_order)
I want to order a pizza with green peppers, anchovies, and grilled onions.

2 stringr

2.1 Introduction

2.1.1 Putting strings together with stringr

For your first stringr function, we’ll look at str_c(), the c is short for concatenate, a function that works like paste(). It takes vectors of strings as input along with sep and collapse arguments.

There are two key ways str_c() differs from paste(). First, the default separator is an empty string, sep = "“, as opposed to a space, so it’s more like paste0(). This is an example of a stringr function, performing a similar operation to a base function, but using a default that is more likely to be what you want. Remember in your pizza order, you had to set sep =”" multiple times.

The second way str_c() differs to paste() is in its handling of missing values. paste() turns missing values into the string “NA”, whereas str_c() propagates missing values. That means combining any strings with a missing value will result in another missing value.

library(stringr)

my_toppings <- c("cheese", NA, NA)
my_toppings_and <- paste(c("", "", "and "), my_toppings, sep = "")

# Print my_toppings_and
my_toppings_and
[1] "cheese" "NA"     "and NA"
# Use str_c() instead of paste(): my_toppings_str
my_toppings_str <- str_c(c("", "", "and "), my_toppings)

# Print my_toppings_str
my_toppings_str
[1] "cheese" NA       NA      
# paste() my_toppings_and with collapse = ", "
paste(my_toppings_and, collapse = ", ")
[1] "cheese, NA, and NA"
# str_c() my_toppings_str with collapse = ", "
str_c(my_toppings_str, collapse = ", ")
[1] NA

This behavior is nice because you learn quickly when you might have missing values, rather than discovering later weird “NA”s inside your strings. Another stringr function that is useful when you may have missing values, is str_replace_na() which replaces missing values with any string you choose.

2.1.2 String length

Our next stringr function is str_length(). str_length() takes a vector of strings as input and returns the number of characters in each string. For example, try finding the number of characters in Batman’s name:

str_length(c("Bruce", "Wayne"))
[1] 5 5

This is very similar to the base function nchar() but you’ll see in the exercises str_length() handles factors in an intuitive way, whereas nchar() will just return an error.

Historically, nchar() was even worse, rather than returning an error if you passed it a factor, it would return the number of characters in the numeric encoding of the factor. Thankfully this behavior has been fixed, but it was one of the original motivations behind str_length().

Take your first look at babynames by asking if girls’ names are longer than boys’ names.

library(stringr)
library(babynames)
package 㤼㸱babynames㤼㸲 was built under R version 4.0.3
library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
# Extracting vectors for boys' and girls' names
babynames_2014 <- filter(babynames, year == 2014)
boy_names <- filter(babynames_2014, sex == "M")$name
girl_names <- filter(babynames_2014, sex == "F")$name

# Take a look at a few boy_names
head(boy_names)
[1] "Noah"    "Liam"    "Mason"   "Jacob"   "William" "Ethan"  
# Find the length of all boy_names
boy_length <- str_length(boy_names)

# Take a look at a few lengths
head(boy_length)
[1] 4 4 5 5 7 5
# Find the length of all girl_names
girl_length <- str_length(girl_names)

# Find the difference in mean length
mean(girl_length) - mean(boy_length)
[1] 0.3374758
# Confirm str_length() works with factors
head(str_length(factor(boy_names)))
[1] 4 4 5 5 7 5

2.1.3 Extracting substrings

The str_sub() function in stringr extracts parts of strings based on their location. As with all stringr functions, the first argument, string, is a vector of strings. The arguments start and end specify the boundaries of the piece to extract in characters.

For example, str_sub(x, 1, 4) asks for the substring starting at the first character, up to the fourth character, or in other words the first four characters. Try it with my Batman’s name:

str_sub(c("Bruce", "Wayne"), 1, 4)
[1] "Bruc" "Wayn"

Both start and end can be negative integers, in which case, they count from the end of the string. For example, str_sub(x, -4, -1), asks for the substring starting at the fourth character from the end, up to the first character from the end, i.e. the last four characters. Again, try it with Batman:

str_sub(c("Bruce", "Wayne"), -4, -1)
[1] "ruce" "ayne"
# Extract first letter from boy_names
boy_first_letter <- str_sub(boy_names, 1, 1)

# Tabulate occurrences of boy_first_letter
table(boy_first_letter)
boy_first_letter
   A    B    C    D    E    F    G    H    I    J    K    L    M    N    O    P    Q    R    S    T    U    V    W 
1454  651  770  998  549  185  334  403  235 1390 1291  537  914  424  207  230   56  778  806  771   43  160  174 
   X    Y    Z 
  56  252  379 
  
# Extract the last letter in boy_names, then tabulate
boy_last_letter <- str_sub(boy_names, -1, -1)
table(boy_last_letter)
boy_last_letter
   a    b    c    d    e    f    g    h    i    j    k    l    m    n    o    p    q    r    s    t    u    v    w 
 421  104   92  436 1148   66   82  583  705   57  349  945  389 4672  730   32   19 1011  826  292   81   71   34 
   x    y    z 
  86  697  119 
# Extract the first letter in girl_names, then tabulate
girl_first_letter <- str_sub(girl_names, 1, 1)
table(girl_first_letter)
girl_first_letter
   A    B    C    D    E    F    G    H    I    J    K    L    M    N    O    P    Q    R    S    T    U    V    W 
3101  699  946  810  933  209  345  469  373 1430 1694 1122 1746  752  143  303   38  831 1369  683   28  214   85 
   X    Y    Z 
  62  294  502 
# Extract the last letter in girl_names, then tabulate
girl_last_letter <- str_sub(girl_names, -1, -1)
table(girl_last_letter)
girl_last_letter
   a    b    c    d    e    f    g    h    i    j    k    l    m    n    o    p    q    r    s    t    u    v    w 
6632   20   13   81 3114    8   21 1942 1581   12   31  450  115 2608  105    3    2  291  326  208   59    6   17 
   x    y    z 
  50 1435   51 

Did you see that “A” is the most popular first letter for both boys and girls, and the most popular last letter for girls. However, the most popular last letter for boys’ names was “n”. You might have seen substr() a base R function that is similar to str_sub(). The big advantage of str_sub() is the ability to use negative indexes to count from the end of a string

2.2 Hunting for matches

2.2.1 Detecting matches

str_detect() is used to answer the question: Does the string contain the pattern? It returns a logical vector of the same length as that of the input vector string, with TRUE for elements that contain the pattern and FALSE otherwise.

Let’s take a look at a simple example where you have a vector of strings that represent pizza orders:

pizzas <- c("cheese", "pepperoni", 
  "sausage and green peppers")

You can ask which orders contain the pattern “pepper”, with

str_detect(pizzas, 
  pattern = fixed("pepper"))
[1] FALSE  TRUE  TRUE

Notice how both pepperoni and green peppers contain the pattern of interest.

The output from str_detect() can be used to count the number of occurrences, or to subset 0out the strings that contain the pattern.

# Look for pattern "zz" in boy_names
contains_zz <- str_detect(boy_names, pattern = fixed("zz"))

# Examine str() of contains_zz
str(contains_zz)
 logi [1:14047] FALSE FALSE FALSE FALSE FALSE FALSE ...
# How many names contain "zz"?
sum(contains_zz)
[1] 16
# Which names contain "zz"?
boy_names[contains_zz]
 [1] "Uzziah"    "Ozzie"     "Ozzy"      "Jazz"      "Uzziel"    "Chazz"     "Izzy"      "Azzam"     "Izzac"    
[10] "Izzak"     "Fabrizzio" "Jazziel"   "Azzan"     "Izzaiah"   "Muizz"     "Yazziel"  
# Which rows in boy_df have names that contain "zz"?
boy_df <- data.frame(boy_names)
boy_df[contains_zz,]
 [1] "Uzziah"    "Ozzie"     "Ozzy"      "Jazz"      "Uzziel"    "Chazz"     "Izzy"      "Azzam"     "Izzac"    
[10] "Izzak"     "Fabrizzio" "Jazziel"   "Azzan"     "Izzaiah"   "Muizz"     "Yazziel"  

That last example is another common use of str_detect() subsetting a data frame to rows where the values in a column contain the pattern of interest. In this case it lets us see these double-z names are pretty rare. For example, even the most popular, Uzziah, only accounted for 0.003% of boys born in 2014.

2.2.2 Subsetting strings based on match

Since detecting strings with a pattern and then subsetting out those strings is such a common operation, stringr provides a function str_subset() that does that in one step.

For example, let’s repeat our search for “pepper” in our pizzas using str_subset():

pizzas <- c("cheese", "pepperoni", "sausage and green peppers")
str_subset(pizzas, pattern = fixed("pepper"))
[1] "pepperoni"                 "sausage and green peppers"

We get a new vector of strings, but it only contains those original strings that contained the pattern.

str_subset() can be easily confused with str_extract(). str_extract() returns a vector of the same length as that of the input vector, but with only the parts of the strings that matched the pattern.

# Find boy_names that contain "zz"
str_subset(boy_names, pattern = fixed("zz"))
 [1] "Uzziah"    "Ozzie"     "Ozzy"      "Jazz"      "Uzziel"    "Chazz"     "Izzy"      "Azzam"     "Izzac"    
[10] "Izzak"     "Fabrizzio" "Jazziel"   "Azzan"     "Izzaiah"   "Muizz"     "Yazziel"  
# Find girl_names that contain "zz"
str_subset(girl_names, pattern = fixed("zz"))
 [1] "Izzabella"  "Jazzlyn"    "Jazzlynn"   "Lizzie"     "Izzy"       "Lizzy"      "Mazzy"      "Izzabelle" 
 [9] "Jazzmine"   "Jazzmyn"    "Jazzelle"   "Jazzmin"    "Izzah"      "Jazzalyn"   "Jazzmyne"   "Izzabell"  
[17] "Jazz"       "Mazzie"     "Alyzza"     "Izza"       "Izzie"      "Jazzlene"   "Lizzeth"    "Jazzalynn" 
[25] "Jazzy"      "Alizzon"    "Elizzabeth" "Jazzilyn"   "Jazzlynne"  "Jizzelle"   "Izzabel"    "Izzabellah"
[33] "Izzibella"  "Jazzabella" "Jazzabelle" "Jazzel"     "Jazzie"     "Jazzlin"    "Jazzlyne"   "Aizza"     
[41] "Brizza"     "Ezzah"      "Fizza"      "Izzybella"  "Rozzlyn"   
# Find girl_names that contain "U"
starts_U <- str_subset(girl_names, pattern = fixed("U"))
starts_U
 [1] "Unique"  "Uma"     "Unknown" "Una"     "Uriah"   "Ursula"  "Unity"   "Umaiza"  "Urvi"    "Ulyana"  "Ula"    
[12] "Udy"     "Urwa"    "Ulani"   "Umaima"  "Umme"    "Ugochi"  "Ulyssa"  "Umika"   "Uriyah"  "Ubah"    "Umaira" 
[23] "Umi"     "Ume"     "Urenna"  "Uriel"   "Urijah"  "Uyen"   
# Find girl_names that contain "U" and "z"
str_subset(starts_U, pattern = fixed("z"))
[1] "Umaiza"

2.2.3 Counting matches

Another stringr function that takes a vector of strings and a pattern is str_count(). str_count() answers the question “How many times does the pattern occur in each string?”. It always returns an integer vector of the same length as that of the input vector.

If you count the occurrences of “pepper” in your pizzas, you’ll find no occurrences in the first, and one each in the second and third,

pizzas <- c("cheese", "pepperoni", 
  "sausage and green peppers")
str_count(pizzas, pattern = fixed("pepper"))
[1] 0 1 1

Perhaps a little more interesting is to count how many “e”s occur in each order

str_count(pizzas, pattern = fixed("e"))
[1] 3 2 5
# Count occurrences of "a" in girl_names
number_as <- str_count(girl_names, pattern = fixed("a"))

# Count occurrences of "A" in girl_names
number_As <- str_count(girl_names, pattern = fixed("A"))

# Histograms of number_as and number_As
hist(number_as)

hist(number_As)  


# Find total "a" + "A"
total_as <- number_as +   number_As

# girl_names with more than 4 a's
girl_names[total_as > 4]
[1] "Aaradhana"

2.3 Splitting strings

2.3.1 Parsing strings into variables

A common use for str_split() is to pull apart raw string data into more useful variables. In this exercise you’ll start by pulling apart a date range, something like “23.01.2017 - 29.01.2017”, into separate variables for the start of the range, “23.01.2017”, and the end of the range, “29.01.2017”.

Remember, if the simplify argument is FALSE (the default) you’ll get back a list of the same length as that of the input vector. More commonly, you’ll want to pull out the first piece (or second piece etc.) from every element, which is easier if you specify simplify = TRUE and get a matrix as output. You’ll explore both of these output types in this exercise.

# Some date data
date_ranges <- c("23.01.2017 - 29.01.2017", "30.01.2017 - 06.02.2017")

# Split dates using " - "
split_dates <- str_split(date_ranges, pattern = fixed(" - "))
split_dates
[[1]]
[1] "23.01.2017" "29.01.2017"

[[2]]
[1] "30.01.2017" "06.02.2017"
# Split dates with n and simplify specified
split_dates_n <- str_split(date_ranges, pattern = fixed(" - "), simplify = TRUE, n = 2)
split_dates_n
     [,1]         [,2]        
[1,] "23.01.2017" "29.01.2017"
[2,] "30.01.2017" "06.02.2017"
split_dates_n <- str_split(date_ranges, fixed(" - "), n = 2, simplify = TRUE)

# Subset split_dates_n into start_dates and end_dates
start_dates <- split_dates_n[,1]

# Split start_dates into day, month and year pieces
str_split(start_dates, fixed("."), simplify = TRUE)
     [,1] [,2] [,3]  
[1,] "23" "01" "2017"
[2,] "30" "01" "2017"
both_names <- c("Box, George", "Cox, David")

# Split both_names into first_names and last_names
both_names_split <- str_split(both_names, fixed(", "), n = 2, simplify = TRUE)

# Get first names
first_names <- both_names_split[, 2]

# Get last names
last_names <- both_names_split[, 1]

2.3.2 Some simple text statistics

Generally, specifying simplify = TRUE will give you output that is easier to work with, but you’ll always get n pieces (even if some are empty, "").

Sometimes, you want to know how many pieces a string can be split into, or you want to do something with every piece before moving to a simpler structure. This is a situation where you don’t want to simplify and you’ll have to process the output with something like lapply().

As an example, you’ll be performing some simple text statistics on your lines from Alice’s Adventures in Wonderland from Chapter 1. Your goal will be to calculate how many words are in each line, and the average length of words in each line.

To do these calculations, you’ll need to split the lines into words. One way to break a sentence into words is to split on an empty space " “. This is a little naive because, for example, it wouldn’t pick up words separated by a newline escape sequence like in”two\nwords", but since this situation doesn’t occur in your lines, it will do.

# Split lines into words
words <- str_split(lines, fixed(" "), simplify = FALSE)

# Number of words per line
lapply(words, length)
[[1]]
[1] 18

[[2]]
[1] 12

[[3]]
[1] 21
  
# Number of characters in each word
word_lengths <- lapply(words, str_length)
  
# Average word length per line
lapply(word_lengths, mean)
[[1]]
[1] 3.888889

[[2]]
[1] 4.25

[[3]]
[1] 4.380952

The word lengths aren’t quite right because you were including some punctuation symbols. One way to deal with that is to replace them first with str_replace().

2.4 Replacing matches in strings

2.4.1 Replacing to tidy strings

You’ve seen one common strategy to pull variables out of strings is to split the string based on a pattern. Sometimes, it’s easier to just replace the parts you don’t want with an empty string "". This is also a common strategy to clean strings up, for example, to remove unwanted punctuation or white space.

In this exercise you’ll pull out some numbers by replacing the part of the string that isn’t a number, you’ll also play with the format of some phone numbers. Pay close attention to the difference between str_replace() and str_replace_all().

# Some IDs
ids <- c("ID#: 192", "ID#: 118", "ID#: 001")

# Replace "ID#: " with ""
id_nums <- str_replace(ids, "ID#: ", "")

# Turn id_nums into numbers
id_ints <- as.numeric(id_nums)
# Some (fake) phone numbers
phone_numbers <- c("510-555-0123", "541-555-0167")

# Use str_replace() to replace "-" with " "
str_replace(phone_numbers, "-", " ")
[1] "510 555-0123" "541 555-0167"
# Use str_replace_all() to replace "-" with " "
str_replace_all(phone_numbers, "-", " ")
[1] "510 555 0123" "541 555 0167"
# Turn phone numbers into the format xxx.xxx.xxxx
str_replace_all(phone_numbers, "-", ".")
[1] "510.555.0123" "541.555.0167"

Review You’ve covered a lot of stringr functions in this chapter:

  • str_c()
  • str_length()
  • str_sub()
  • str_detect()
  • str_subset()
  • str_count()
  • str_split()
  • str_replace()

As a review we’ve got a few tasks for you to do with some DNA sequences. We’ve put three sequences, corresponding to three genes, from the genome of Yersinia pestis – the bacteria that causes bubonic plague – into the vector genes.

Each string represents a gene, each character a particular nucleotide: Adenine, Cytosine, Guanine or Thymine.

genes <- readRDS("dna.rds")
# Find the number of nucleotides in each sequence
str_length(genes)
[1] 441 462 993
# Find the number of A's occur in each sequence
str_count(genes, fixed("A"))
[1] 118 117 267
# Return the sequences that contain "TTTTTT"
str_subset(genes, fixed("TTTTTT"))
[1] "TTAAGGAACGATCGTACGCATGATAGGGTTTTGCAGTGATATTAGTGTCTCGGTTGACTGGATCTCATCAATAGTCTGGATTTTGTTGATAAGTACCTGCTGCAATGCATCAATGGATTTACACATCACTTTAATAAATATGCTGTAGTGGCCAGTGGTGTAATAGGCCTCAACCACTTCTTCTAAGCTTTCCAATTTTTTCAAGGCGGAAGGGTAATCTTTGGCACTTTTCAAGATTATGCCAATAAAGCAGCAAACGTCGTAACCCAGTTGTTTTGGGTTAACGTGTACACAAGCTGCGGTAATGATCCCTGCTTGCCGCATCTTTTCTACTCTTACATGAATAGTTCCGGGGCTAACAGCGAGGTTTTTGGCTAATTCAGCATAGGGTGTGCGTGCATTTTCCATTAATGCTTTCAGGATGCTGCGATCGAGATTATCGATCTGATAAATTTCACTCAT"
# Replace all the "A"s in the sequences with a "_"
str_replace_all(genes, pattern = fixed("A"), replacement = "_")
[1] "TT_G_GT___TT__TCC__TCTTTG_CCC___TCTCTGCTGG_TCCTCTGGT_TTTC_TGTTGG_TG_CGTC__TTTCT__T_TTTC_CCC__CCGTTG_GC_CCTTGTGCG_TC__TTGTTG_TCC_GTTTT_TG_TTGC_CCGC_G___GTGTC_T_TTCTG_GCTGCCT___CC__CCGCCCC___GCGT_CTTGGG_T___TC_GGCTTTTGTTGTTCG_TCTGTTCT__T__TGGCTGC__GTT_TC_GGT_G_TCCCCGGC_CC_TG_GTGG_TGTC_CG_TT__CC_C_GGCC_TTC_GCGT__GTTCGTCC__CTCTGGGCC_TG__GT_TTTCTGT_G____CCC_GCTTCTTCT__TTT_TCCGCT___TGTTC_GC__C_T_TTC_GC_CT_CC__GCGT_CTGCC_CTT_TC__CGTT_TGTC_GCC_T"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
[2] "TT__GG__CG_TCGT_CGC_TG_T_GGGTTTTGC_GTG_T_TT_GTGTCTCGGTTG_CTGG_TCTC_TC__T_GTCTGG_TTTTGTTG_T__GT_CCTGCTGC__TGC_TC__TGG_TTT_C_C_TC_CTTT__T___T_TGCTGT_GTGGCC_GTGGTGT__T_GGCCTC__CC_CTTCTTCT__GCTTTCC__TTTTTTC__GGCGG__GGGT__TCTTTGGC_CTTTTC__G_TT_TGCC__T___GC_GC___CGTCGT__CCC_GTTGTTTTGGGTT__CGTGT_C_C__GCTGCGGT__TG_TCCCTGCTTGCCGC_TCTTTTCT_CTCTT_C_TG__T_GTTCCGGGGCT__C_GCG_GGTTTTTGGCT__TTC_GC_T_GGGTGTGCGTGC_TTTTCC_TT__TGCTTTC_GG_TGCTGCG_TCG_G_TT_TCG_TCTG_T___TTTC_CTC_T"                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
[3] "_TG______C__TTT_TCC_____C__C__C___TC_GCTTCGT____TC_TTCTTTTCCCGCC__TT_G_GC__C__CTTGGCTTG_TCG__GTCC_GGCTCCT_TTTTG_GCCGTGTGGGTG_TGG__CCC__G_T__CCTTTCTGGTTCTG_G___GCGGT_C_GGT____GTT__GTC_TTGCCGG_TTC__CTTTTG__GTTGT_C_TTC_TT_GCG__GTGG___CGT____CCTT_GGGCGTTTTG_TTTTGGTGCTG_CC__GGGGTGT_T_CCC_T_TG___GC_TTGCGCCC_G_TG__G_TCGCCTG_GTGCT_TTC_TTCTGT_T_TGT_G_TC_GTGGG_TTGGG__CGGGTT_TGGGGG_CGGTG__CGT__CCTGGCTT_CCTG___TCG_CTGTT__C__G_TTT_TGC_GCG_TT___G___CTG__GCGGCG_TC_GTGCTG_GTTTGGTGTG__GCCTTTCCTGCCGG_TC_T_TTC_GTTT_TCC_C_GTG___GCCTGCGGGCC_G_TTCCCTG_TTT_G_TGCT___GGCCGTG__CGTGC__TTGCC___G_GTT_GGTGCTGTCTTCCTT_T_GGG_TTGGTGGC___TTGGC_G_TGGTC__TCCC_TG_TGTTCGTGCGCC_G_TT_TG_TG_TTGG_CCTCTCCG_GTGCGG__GGTTTCTCTGG_TT___CGGCG_C_TT_TTGTCTGG__CCC__T_TTGG__G_TGCCTTTG_G_T_TCTTCT_TGGG__TTCGTGTTG_TGCCG__GCTCTT__GCGTC_GTT_GCCCTG_CTGGCG_TG__G_CCGCTTGG__CTGG__TGGC_TC__TC_CTGTTGCGCGGTG___TGCC_C___CT_TCGGGGG_GGT_TTGGTC_GTCCCGCTT_GTG_TGTT_TTGCTGC_G___C__C_T_TTGGTC_GGTGC__TGTGGTGTTTGGGGCCCTG___TC_GCG_G___GTTG_TGGCCTGCTGT__"

2.4.2 Final challenges

By combining multiple operations together in sequence you can achieve quite complicated manipulations.

As the final exercise we want to expose you to the power of combining operations. You’ll complete two tasks:

  1. You’ll turn a vector of full names, like “Bruce Wayne”, into abbreviated names like “B. Wayne”. This requires combining str_split(), str_sub() and str_c().
  2. You’ll compare how many boy names end in “ee” compared to girl names. This requires combining str_sub() with str_detect() along with the base function table().
# Define some full names
names <- c("Diana Prince", "Clark Kent")

# Split into first and last names
names_split <- str_split(names, fixed(" "), simplify = TRUE)

# Extract the first letter in the first name
abb_first <- str_sub(names_split[,1], 1, 1)

# Combine the first letter ". " and last name
str_c(abb_first, ". ", names_split[,2])
[1] "D. Prince" "C. Kent"  
# Use all names in babynames_2014
all_names <- babynames_2014$name

# Get the last two letters of all_names
last_two_letters <- str_sub(all_names, -2, -1)
# Does the name end in "ee"?
ends_in_ee <- str_detect(last_two_letters, fixed("ee"))

# Extract rows and "sex" column
sex <- babynames_2014$sex[ends_in_ee]

# Display result as a table
table(sex)
sex
  F   M 
572  84 

3 Pattern matching with regular expressions

3.1 Regular expressions

3.1.1 Matching the start or end of the string

rebus provides START and END shortcuts to specify regular expressions that match the start and end of the string. These are also known as anchors. You can try it out just by typing

START You’ll see the output ^. The denotes this is a special regex object and it has the value ^. ^ is the character used in the regular expression language to denote the start of a string.

The special operator provided by rebus, %R% allows you to compose complicated regular expressions from simple pieces. When you are reading rebus code, think of %R% as “then”. For example, you could combine START with c,

START %R% "c"

to match the pattern “the start of string then a c”, or in other words: strings that start with c. In rebus, if you want to match a specific character, or a specific sequence of characters, you simply specify them as a string, e.g. surround them with ".

library(rebus)
package 㤼㸱rebus㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱rebus㤼㸲

The following object is masked from 㤼㸱package:stringr㤼㸲:

    regex
# Some strings to practice with
x <- c("cat", "coat", "scotland", "tic toc")

# Print END
END
<regex> $
# Run me
str_view(x, pattern = START %R% "c")
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
# Match the strings that start with "co" 
str_view(x, pattern = START %R% "co")
# Match the strings that end with "at"
str_view(x, pattern = "at" %R% END)

# Match the string that is exactly "cat"
str_view(x, pattern = START %R% "cat" %R% END)

or that last example, rebus also provides the function exactly(x) which is a shortcut for START %R% x %R% END that matches only if the string is exactly x.

3.1.2 Matching any character

In a regular expression you can use a wildcard to match a single character, no matter what the character is. In rebus it is specified with ANY_CHAR. Try typing ANY_CHAR in the console. You should see that in the regular expression language this is specified by a dot, ..

For example, “c” %R% ANY_CHAR %R% “t” will look for patterns like “c_t” where the blank can be any character. Consider the strings: “cat”, “coat”, “scotland” and “tic toc”. Where would the matches to “c” %R% ANY_CHAR %R% “t” be?

Test your intuition by running:

str_view(c("cat", "coat", "scotland", "tic toc"), 
  pattern = "c" %R% ANY_CHAR %R% "t")

Notice that ANY_CHAR will match a space character (c t in tic toc). It will also match numbers or punctuation symbols, but ANY_CHAR will only ever match one character, which is why we get no match in coat.

# Match two characters, where the second is a "t"
str_view(x, pattern = ANY_CHAR %R% "t")


# Match a "t" followed by any character
str_view(x, pattern = "t" %R% ANY_CHAR)


# Match two characters
str_view(x, pattern = ANY_CHAR %R% ANY_CHAR)


# Match a string with exactly three characters
str_view(x, pattern = START %R% ANY_CHAR %R% ANY_CHAR %R% ANY_CHAR %R% END)

3.1.3 Combining with stringr functions

You can pass a regular expression as the pattern argument to any stringr function that has the pattern argument. You can use str_detect() to get a logical vector for whether there was a match, str_subset() to return just the strings with matches, and str_count() to count the number of matches in each string.

As a reminder, compare the output of those three functions with our “c_t” pattern from the previous exercise:

x <- c("cat", "coat", "scotland", "tic toc")
pattern <- "c" %R% ANY_CHAR %R% "t"
str_detect(x, pattern)
[1]  TRUE FALSE  TRUE  TRUE
str_subset(x, pattern)
[1] "cat"      "scotland" "tic toc" 
str_count(x, pattern)
[1] 1 0 1 1

It now also makes sense to add str_extract() to your repertoire. It returns just the part of the string that matched the pattern:

str_extract(x, pattern)
[1] "cat" NA    "cot" "c t"

You’ll combine your regular expression skills with stringr to ask how often a q is followed by any character in boy names.

It’s always a good idea to test your pattern, so this pattern is shown matched with four names. The first two shouldn’t have matches (can you explain why?) but the last two should.

pattern <- "q" %R% ANY_CHAR

# Find names that have the pattern
names_with_q <- str_subset(boy_names, pattern)

# How many names were there?
length(names_with_q)
[1] 96
# Find part of name that matches pattern
part_with_q <- str_extract(boy_names, pattern)

# Get a table of counts
table(part_with_q)
part_with_q
qa qe qi qm qo qu 
 1  1  2  2  1 89 
# Did any names have the pattern more than once?
count_of_q <- str_count(boy_names, pattern)

# Get a table of counts
table(count_of_q)
count_of_q
    0     1 
13951    96 
# Which babies got these names?
with_q <- str_detect(boy_names, pattern)

# What fraction of babies got these names?
mean(with_q)
[1] 0.006834199

3.2 More regular expressions

3.2.1 Alternation

The rebus function or() allows us to specify a set of alternatives, which may be single characters or character strings, to be matched. Each alternative is passed as a separate argument.

For example, or(“grey”, “gray”) allows us to detect either the American or British spelling:

x <- c("grey sky", "gray elephant")
str_view(x, pattern = or("grey", "gray"))

Since these two words only differ by one character you could equivalently specify this match with “gr” %R% or(“e”, “a”) %R% “y”, that is “a gr followed by, an e or an a, then a y”.

# Match Jeffrey or Geoffrey
whole_names <- or("Jeffrey", "Geoffrey")
str_view(boy_names, pattern = whole_names, match = TRUE)

# Match Jeffrey or Geoffrey, another way
common_ending <- or("Je", "Geo") %R% "ffrey"
str_view(boy_names, pattern = common_ending, match = TRUE)

# Match with alternate endings
by_parts <- or("Je", "Geo") %R% "ff" %R% or ("ry", "ery", "rey", "erey")
str_view(boy_names, pattern = by_parts, match = TRUE)

# Match names that start with Cath or Kath
ckath <- START %R% or("C", "K") %R% "ath"
str_view(girl_names, pattern = ckath, match = TRUE)

3.2.2 Character classes

In regular expressions a character class is a way of specifying “match one (and only one) of the following characters”. In rebus you can specify the set of allowable characters using the function char_class().

This is another way you could specify an alternate spelling, for example, specifying “a gr followed by, either an a or e, followed by a y”:

x <- c("grey sky", "gray elephant")
str_view(x, pattern = "gr" %R% char_class("ae") %R% "y")

A negated character class matches “any single character that isn’t one of the following”, and in rebus is specified with negated_char_class().

Unlike in other places in a regular expression you don’t need to escape characters that might otherwise have a special meaning inside character classes. If you want to match . you can include . directly, e.g. char_class(“.”). Matching a - is a bit trickier. If you need to do it, just make sure it comes first in the character class.

# Create character class containing vowels
vowels <- char_class("aeiouAEIOU")

# Print vowels
vowels
<regex> [aeiouAEIOU]
# See vowels in x with str_view()
str_view(x, vowels)

# See vowels in x with str_view_all()
str_view_all(x, vowels)

# Number of vowels in boy_names
num_vowels <- str_count(boy_names, vowels)

# Number of characters in boy_names
name_length <- str_length(boy_names)
# Calc mean number of vowels
mean(num_vowels)
[1] 2.385563
# Calc mean fraction of vowels per name
mean(num_vowels / name_length)
[1] 0.4000596

The names in boy_names are on average about 40% vowels.

3.2.3 Repetition

The rebus functions one_or_more(), zero_or_more() and optional() can be used to wrap parts of a regular expression to allow a pattern to match a variable number of times.

Take our vowels pattern from the last exercise. You can pass it to one_or_more() to create the pattern that matches “one or more vowels”. Take a look with these interjections:

x <- c("ow", "ooh", "yeeeah!", "shh")
str_view(x, pattern = one_or_more(vowels))

You’ll see we can match the single o in ow, the double o in ooh and the string of es followed by the a in yeeeah, but nothing in shh because there isn’t a single vowel.

In contrast zero_or_more() will match even if there isn’t an occurrence, try

str_view(x, pattern = zero_or_more(vowels))

Since both yeeeah and shh start without a vowel, they match “zero vowels”, and since regular expressions are lazy, they look no further and return the start of the string as a match.

# Vowels from last exercise
vowels <- char_class("aeiouAEIOU")

# See names with only vowels
str_view(boy_names, 
  pattern = exactly(one_or_more(vowels)), 
  match = TRUE)

# Use `negated_char_class()` for everything but vowels
not_vowels <- negated_char_class("aeiouAEIOU")

# See names with no vowels
str_view(boy_names, 
  pattern = exactly(one_or_more(not_vowels)), 
  match = TRUE)

3.3 Shortcuts

3.3.1 Hunting for phone numbers

For your first task you are going to pull out the phone numbers from this vector of contact information:

contact <- c("Call me at 555-555-0191", "123 Main St", "(555) 555 0191", 
"Phone: 555.555.0191 Mobile: 555.555.0192")

You’ll assume the phone numbers you are looking for follow the American standard of a three digit area code, a three digit exchange and then a four digit number, but each part could be separated by spaces or various punctuation symbols.

# Create a three digit pattern
three_digits <- DGT %R% DGT %R% DGT 

# Test it
str_view_all(contact, pattern = three_digits)

# Create a separator pattern
separator <- char_class("- . ( ) ")

# Test it
str_view_all(contact, pattern = separator)

# Use these components
three_digits <- DGT %R% DGT %R% DGT
four_digits <- three_digits %R% DGT
separator <- char_class("-.() ")

# Create phone pattern
phone_pattern <- optional(OPEN_PAREN) %R%
  three_digits %R%
  zero_or_more(separator) %R%
  three_digits %R% 
  zero_or_more(separator) %R%
  four_digits
        
# Test it           
str_view_all(contact, pattern = phone_pattern)

# Extract phone numbers
str_extract(contact, phone_pattern)
[1] "555-555-0191"   NA               "(555) 555 0191" "555.555.0191"  
# Extract ALL phone numbers
str_extract_all(contact, phone_pattern)
[[1]]
[1] "555-555-0191"

[[2]]
character(0)

[[3]]
[1] "(555) 555 0191"

[[4]]
[1] "555.555.0191" "555.555.0192"

rebus also provides the functions number_range() when you want to extract numbers in a certain range, and datetime() to specify common date patterns.

3.3.2 Extracting age and gender from accident narratives

Recall from the video, you want to parse out age and gender from accident narratives. For example, this narrative

19YOM-SHOULDER STRAIN-WAS TACKLED WHILE PLAYING FOOTBALL W/ FRIENDS 

describes male of age 19, and this one

TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*

a female of age 33.

You are generally looking for a pattern with a number, something to indicate the units, e.g. YO or YR for years old, or MO for months old, and a character that identifies the gender.

In this exercise you’ll build up a pattern to pull out the part of the narrative that has the age and gender information. Then, in the next exercise you’ll parse out the age and gender into separate variables.

narratives <- readRDS("narratives.rds")
# Use these patterns
age <- DGT %R% optional(DGT)
unit <- optional(SPC) %R% or("YO", "YR", "MO")
gender <- optional(SPC) %R% or("M", "F")

# Extract age, unit, gender
str_extract(narratives, pattern = age %R% unit %R% gender)
 [1] "19YOM"   "31 YOF"  "82 YOM"  "33 YOF"  "10YOM"   "53 YO F" "13 MOF"  "14YR M"  "55YOM"   "5 YOM"  

You can also use dgt(1, 2) to match one or two digits.

3.3.3 Parsing age and gender into pieces

To finish up, you need to pull out the individual pieces and tidy them into usable variables

There are a few ways you could get at one piece: you could extract out the piece you need, you could replace everything that isn’t the piece you need with "“, or you could try to split into the pieces you need. You’ll try a few of these in this exercise and you’ll see yet another way in the next chapter. For the first option, stringr has a nice convenience function, str_remove(), that works like str_replace() with replacement =”".

One benefit of building up your pattern in pieces is you already have patterns for each part that you can reuse now.

age_gender <- str_extract(narratives, pattern = age %R% unit %R% gender)
# age_gender, age, gender, unit are pre-defined
ls.str()
abb_first :  chr [1:2] "D" "C"
age :  'regex' chr "\\d[\\d]?"
age_gender :  chr [1:10] "19YOM" "31 YOF" "82 YOM" "33 YOF" "10YOM" "53 YO F" "13 MOF" "14YR M" "55YOM" "5 YOM"
all_names :  chr [1:33228] "Emma" "Olivia" "Sophia" "Isabella" "Ava" "Mia" "Emily" "Abigail" "Madison" "Charlotte" "Harper" ...
babynames_2014 : tibble [33,228 x 5] (S3: tbl_df/tbl/data.frame)
both_names :  chr [1:2] "Box, George" "Cox, David"
both_names_split :  chr [1:2, 1:2] "Box" "Cox" "George" "David"
boy_df : 'data.frame':  14047 obs. of  1 variable:
 $ boy_names: chr  "Noah" "Liam" "Mason" "Jacob" ...
boy_first_letter :  chr [1:14047] "N" "L" "M" "J" "W" "E" "M" "A" "J" "D" "E" "B" "L" "A" "J" "M" "J" "D" "L" "J" "A" "A" "S" "G" ...
boy_last_letter :  chr [1:14047] "h" "m" "n" "b" "m" "n" "l" "r" "s" "l" "h" "n" "n" "n" "n" "w" "n" "d" "s" "h" "y" "w" "l" "l" ...
boy_length :  int [1:14047] 4 4 5 5 7 5 7 9 5 6 ...
boy_names :  chr [1:14047] "Noah" "Liam" "Mason" "Jacob" "William" "Ethan" "Michael" "Alexander" "James" "Daniel" "Elijah" ...
by_parts :  'regex' chr "(?:Je|Geo)ff(?:ry|ery|rey|erey)"
ckath :  'regex' chr "^(?:C|K)ath"
common_ending :  'regex' chr "(?:Je|Geo)ffrey"
contact :  chr [1:4] "Call me at 555-555-0191" "123 Main St" "(555) 555 0191" "Phone: 555.555.0191 Mobile: 555.555.0192"
contains_zz :  logi [1:14047] FALSE FALSE FALSE FALSE FALSE FALSE ...
count_of_q :  int [1:14047] 0 0 0 0 0 0 0 0 0 0 ...
date_ranges :  chr [1:2] "23.01.2017 - 29.01.2017" "30.01.2017 - 06.02.2017"
dollar_income :  chr [1:4] "$       72" "$    1,030" "$   10,292" "$1,189,192"
ends_in_ee :  logi [1:33228] FALSE FALSE FALSE FALSE FALSE FALSE ...
first_names :  chr [1:2] "George" "David"
formatted_income :  chr [1:4] "     72" "   1030" "  10292" "1189192"
formatted_names :  chr [1:4] "          Year 0" "          Year 1" "          Year 2" "Project Lifetime"
four_digits :  'regex' chr "\\d\\d\\d\\d"
gender :  'regex' chr "[\\s]?(?:M|F)"
genes :  Named chr [1:3] "TTAGAGTAAATTAATCCAATCTTTGACCCAAATCTCTGCTGGATCCTCTGGTATTTCATGTTGGATGACGTCAATTTCTAATATTTCACCCAACCGTTGAGCACCTTGTGC"| __truncated__ ...
girl_first_letter :  chr [1:19181] "E" "O" "S" "I" "A" "M" "E" "A" "M" "C" "H" "S" "A" "E" "A" "E" "E" "C" "V" "G" "A" "Z" "N" "A" ...
girl_last_letter :  chr [1:19181] "a" "a" "a" "a" "a" "a" "y" "l" "n" "e" "r" "a" "y" "h" "a" "n" "a" "e" "a" "e" "y" "y" "e" "n" ...
girl_length :  int [1:19181] 4 6 6 8 3 3 5 7 7 9 ...
girl_names :  chr [1:19181] "Emma" "Olivia" "Sophia" "Isabella" "Ava" "Mia" "Emily" "Abigail" "Madison" "Charlotte" "Harper" ...
id_ints :  num [1:3] 192 118 1
id_nums :  chr [1:3] "192" "118" "001"
ids :  chr [1:3] "ID#: 192" "ID#: 118" "ID#: 001"
income :  num [1:4] 7.22e+01 1.03e+03 1.03e+04 1.19e+06
income_names :  chr [1:4] "Year 0" "Year 1" "Year 2" "Project Lifetime"
last_names :  chr [1:2] "Box" "Cox"
last_two_letters :  chr [1:33228] "ma" "ia" "ia" "la" "va" "ia" "ly" "il" "on" "te" "er" "ia" "ry" "th" "ia" "yn" "la" "oe" "ia" ...
line1 :  chr "The table was a large one, but the three were all crowded together at one corner of it:"
line2 :  chr "\"No room! No room!\" they cried out when they saw Alice coming."
line3 :  chr "\"There's plenty of room!\" said Alice indignantly, and she sat down in a large arm-chair at one end of the table."
lines :  chr [1:3] "The table was a large one, but the three were all crowded together at one corner of it:" ...
my_order :  chr "I want to order a pizza with green peppers, anchovies, and grilled onions."
my_toppings :  chr [1:3] "cheese" NA NA
my_toppings_and :  chr [1:3] "cheese" "NA" "and NA"
my_toppings_str :  chr [1:3] "cheese" NA NA
name_length :  int [1:14047] 4 4 5 5 7 5 7 9 5 6 ...
names :  chr [1:2] "Diana Prince" "Clark Kent"
names_split :  chr [1:2, 1:2] "Diana" "Clark" "Prince" "Kent"
names_with_q :  chr [1:96] "Joaquin" "Enrique" "Ezequiel" "Marquis" "Dominique" "Jaquan" "Marquise" "Marquez" "Marques" ...
narratives :  chr [1:10] "19YOM-SHOULDER STRAIN-WAS TACKLED WHILE PLAYING FOOTBALL W/ FRIENDS " ...
not_vowels :  'regex' chr "[^aeiouAEIOU]"
num_vowels :  int [1:14047] 2 2 2 2 3 2 3 4 2 3 ...
number_as :  int [1:19181] 1 1 1 2 1 1 0 1 1 1 ...
number_As :  int [1:19181] 0 0 0 0 1 0 0 1 0 0 ...
p_values :  num [1:4] 1.20e-01 9.80e-01 1.91e-05 2.00e-11
part_with_q :  chr [1:14047] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA ...
pattern :  'regex' chr "q."
percent_change :  num [1:4] 4 -1.91 3 -5
phone_numbers :  chr [1:2] "510-555-0123" "541-555-0167"
phone_pattern :  'regex' chr "[\\(]?\\d\\d\\d[-.() ]*\\d\\d\\d[-.() ]*\\d\\d\\d\\d"
pizzas :  chr [1:3] "cheese" "pepperoni" "sausage and green peppers"
pretty_income :  chr [1:4] "       72" "    1,030" "   10,292" "1,189,192"
pretty_percent :  chr [1:4] "+4.0" "-1.9" "+3.0" "-5.0"
rows :  chr [1:4] "          Year 0   $       72" "          Year 1   $    1,030" "          Year 2   $   10,292" ...
separator :  'regex' chr "[-.() ]"
sex :  chr [1:656] "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" "F" ...
split_dates : List of 2
 $ : chr [1:2] "23.01.2017" "29.01.2017"
 $ : chr [1:2] "30.01.2017" "06.02.2017"
split_dates_n :  chr [1:2, 1:2] "23.01.2017" "30.01.2017" "29.01.2017" "06.02.2017"
start_dates :  chr [1:2] "23.01.2017" "30.01.2017"
starts_U :  chr [1:28] "Unique" "Uma" "Unknown" "Una" "Uriah" "Ursula" "Unity" "Umaiza" "Urvi" "Ulyana" "Ula" "Udy" "Urwa" ...
these_toppings :  chr "green peppers, anchovies, and grilled onions"
three_digits :  'regex' chr "\\d\\d\\d"
toppings :  chr [1:25] "anchovies" "artichoke" "bacon" "breakfast bacon" "Canadian bacon" "cheese" "chicken" ...
total_as :  int [1:19181] 1 1 1 2 2 1 0 2 1 1 ...
trimmed_income :  chr [1:4] "72" "1030" "10292" "1189192"
unit :  'regex' chr "[\\s]?(?:YO|YR|MO)"
vowels :  'regex' chr "[aeiouAEIOU]"
whole_names :  'regex' chr "(?:Jeffrey|Geoffrey)"
with_q :  logi [1:14047] FALSE FALSE FALSE FALSE FALSE FALSE ...
word_lengths : List of 3
 $ : int [1:18] 3 5 3 1 5 4 3 3 5 4 ...
 $ : int [1:12] 3 5 2 6 4 5 3 4 4 3 ...
 $ : int [1:21] 8 6 2 6 4 5 12 3 3 3 ...
words : List of 3
 $ : chr [1:18] "The" "table" "was" "a" ...
 $ : chr [1:12] "\"No" "room!" "No" "room!\"" ...
 $ : chr [1:21] "\"There's" "plenty" "of" "room!\"" ...
x :  chr [1:4] "ow" "ooh" "yeeeah!" "shh"
y :  num [1:3] 1 2.01 1
year_percent :  chr [1:4] "2010: +4.0%" "2011: -1.9%" "2012: +3.0%" "2013: -5.0%"
years :  int [1:4] 2010 2011 2012 2013
# Extract age and make numeric
as.numeric(str_extract(age_gender, age))
 [1] 19 31 82 33 10 53 13 14 55  5
# Replace age and units with ""
genders <- str_remove(age_gender, age %R% unit) 

# Replace extra spaces
str_remove_all(genders, pattern = one_or_more(SPC))
 [1] "M" "F" "M" "F" "M" "F" "F" "M" "M" "M"
# Numeric ages, from previous step
ages_numeric <- as.numeric(str_extract(age_gender, age))

# Extract units 
time_units <- str_extract(age_gender, unit)

# Extract first word character
time_units_clean <- str_extract(time_units, pattern = WRD)

# Turn ages in months to years
ifelse(time_units_clean == "Y", ages_numeric, ages_numeric / 12)
 [1] 19.000000 31.000000 82.000000 33.000000 10.000000 53.000000  1.083333 14.000000 55.000000  5.000000

– You can imagine this would be an important first step if you wanted to investigate the frequency of accidents by age or gender. If you want a challenge, get the neiss package and see if you can extract age and gender from all accidents.

4 More advanced matching and manipulation

4.1 Capturing

4.1.1 Capturing parts of a pattern

n rebus, to denote a part of a regular expression you want to capture, you surround it with the function capture(). For example, a simple pattern to match an email address might be,

email <- one_or_more(WRD) %R% 
  "@" %R% one_or_more(WRD) %R% 
  DOT %R% one_or_more(WRD)
str_view("(wolverine@xmen.com)", pattern = email)  

If you want to capture the part before the @, you simply wrap that part of the regular expression in capture():

email <- capture(one_or_more(WRD)) %R% 
  "@" %R% one_or_more(WRD) %R% 
  DOT %R% one_or_more(WRD)
str_view("(wolverine@xmen.com)", pattern = email)  

The part of the string that matches hasn’t changed, but if we pull out the match with str_match() we get access to the captured piece:

str_match("(wolverine@xmen.com)", pattern =  email)  
     [,1]                 [,2]       
[1,] "wolverine@xmen.com" "wolverine"
hero_contacts <- c("(wolverine@xmen.com)", "wonderwoman@justiceleague.org", "thor@avengers.com"
)
# Capture parts between @ and . and after .
email <- capture(one_or_more(WRD)) %R% 
  "@" %R% capture(one_or_more(WRD)) %R% 
  DOT %R% capture(one_or_more(WRD))

# Check match hasn't changed
str_view(hero_contacts, email)
# Pattern from previous step
email <- capture(one_or_more(WRD)) %R% 
  "@" %R% capture(one_or_more(WRD)) %R% 
  DOT %R% capture(one_or_more(WRD))
  
# Pull out match and captures
email_parts <- str_match(hero_contacts, email)
email_parts
     [,1]                            [,2]          [,3]            [,4] 
[1,] "wolverine@xmen.com"            "wolverine"   "xmen"          "com"
[2,] "wonderwoman@justiceleague.org" "wonderwoman" "justiceleague" "org"
[3,] "thor@avengers.com"             "thor"        "avengers"      "com"
# Save host
host <- email_parts[,3]
host
[1] "xmen"          "justiceleague" "avengers"     

Actually, detecting an email address can be really hard.

4.1.2 Pulling out parts of a phone number

You can now go back to the phone number example from the previous chapter. You developed a pattern to extract the parts of string that looked like phone numbers, and now you have the skills to pull out the pieces of the number. Let’s see if you can put your skills together to output the first phone number in each string in a common format.

separator <- or(char_class("-.()| "))
'or' is intended to be called with at least 2 arguments in '...'. 1 was passed. Maybe you wanted 'or1' instead?
# View text containing phone numbers
contact
[1] "Call me at 555-555-0191"                  "123 Main St"                             
[3] "(555) 555 0191"                           "Phone: 555.555.0191 Mobile: 555.555.0192"
# Add capture() to get digit parts
phone_pattern <- capture(three_digits) %R% zero_or_more(separator) %R% 
           capture(three_digits) %R% zero_or_more(separator) %R%
           capture(four_digits)
           
# Pull out the parts with str_match()
phone_numbers <- str_match(contact, phone_pattern)
phone_numbers
     [,1]            [,2]  [,3]  [,4]  
[1,] "555-555-0191"  "555" "555" "0191"
[2,] NA              NA    NA    NA    
[3,] "555) 555 0191" "555" "555" "0191"
[4,] "555.555.0191"  "555" "555" "0191"
# Put them back together
str_c(
  "(",
  phone_numbers[,2],
  ")",
  phone_numbers[,3],
  "-",
  phone_numbers[,4])
[1] "(555)555-0191" NA              "(555)555-0191" "(555)555-0191"

If you wanted to extract beyond the first phone number, e.g. The second phone number in the last string, you could use str_match_all(). But, like str_split() it will return a list with one component for each input string, and you’ll need to use lapply() to handle the result.

4.1.3 Extracting age and gender again

You can now also take another look at your pattern for pulling out age and gender from the injury narratives.

# narratives has been pre-defined
narratives
 [1] "19YOM-SHOULDER STRAIN-WAS TACKLED WHILE PLAYING FOOTBALL W/ FRIENDS "                      
 [2] "31 YOF FELL FROM TOILET HITITNG HEAD SUSTAINING A CHI "                                    
 [3] "ANKLE STR. 82 YOM STRAINED ANKLE GETTING OUT OF BED "                                      
 [4] "TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*"            
 [5] "10YOM CUT THUMB ON METAL TRASH CAN DX AVULSION OF SKIN OF THUMB "                          
 [6] "53 YO F TRIPPED ON CARPET AT HOME. DX HIP CONTUSION "                                      
 [7] "13 MOF TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION"
 [8] "14YR M PLAYING FOOTBALL; DX KNEE SPRAIN "                                                  
 [9] "55YOM RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE "                      
[10] "5 YOM ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN"                
# Add capture() to get age, unit and sex
pattern <- capture(optional(DGT) %R% DGT) %R%  
  optional(SPC) %R% capture(or("YO", "YR", "MO")) %R%
  optional(SPC) %R% capture(or("M", "F"))

# Pull out from narratives
str_match(narratives, pattern)
      [,1]      [,2] [,3] [,4]
 [1,] "19YOM"   "19" "YO" "M" 
 [2,] "31 YOF"  "31" "YO" "F" 
 [3,] "82 YOM"  "82" "YO" "M" 
 [4,] "33 YOF"  "33" "YO" "F" 
 [5,] "10YOM"   "10" "YO" "M" 
 [6,] "53 YO F" "53" "YO" "F" 
 [7,] "13 MOF"  "13" "MO" "F" 
 [8,] "14YR M"  "14" "YR" "M" 
 [9,] "55YOM"   "55" "YO" "M" 
[10,] "5 YOM"   "5"  "YO" "M" 
# Edit to capture just Y and M in units
pattern2 <- capture(optional(DGT) %R% DGT) %R%  
  optional(SPC) %R% capture(or("Y", "M")) %R% optional(or("O","R")) %R%
  optional(SPC) %R% capture(or("M", "F"))

# Check pattern
str_view(narratives, pattern2)


# Pull out pieces
str_match(narratives, pattern2)
      [,1]      [,2] [,3] [,4]
 [1,] "19YOM"   "19" "Y"  "M" 
 [2,] "31 YOF"  "31" "Y"  "F" 
 [3,] "82 YOM"  "82" "Y"  "M" 
 [4,] "33 YOF"  "33" "Y"  "F" 
 [5,] "10YOM"   "10" "Y"  "M" 
 [6,] "53 YO F" "53" "Y"  "F" 
 [7,] "13 MOF"  "13" "M"  "F" 
 [8,] "14YR M"  "14" "Y"  "M" 
 [9,] "55YOM"   "55" "Y"  "M" 
[10,] "5 YOM"   "5"  "Y"  "M" 

The combination of capture() and str_match() is powerful for extracting pieces of text.

4.2 Backreferences

4.2.1 Using backreferences in patterns

Backreferences can be useful in matching because they allow you to find repeated patterns or words. Using a backreference requires two things: you need to capture() the part of the pattern you want to reference, and then you refer to it with REF1.

Take a look at this pattern: capture(LOWER) %R% REF1. It matches and captures any lower case character, then is followed by the captured character: it detects repeated characters regardless of what character is repeated. To see it in action try this:

str_view(c("hello", "sweet", "kitten"), 
  pattern = capture(LOWER) %R% REF1)

If you capture more than one thing you can refer to them with REF2, REF3 etc. up to REF9, counting the captures from the left of the pattern.

# Names with three repeated letters
repeated_three_times <- capture(LOWER) %R% REF1 %R% REF1

# Test it
str_view(boy_names, pattern = repeated_three_times, match = TRUE)

# Names with a pair of repeated letters
pair_of_repeated <- capture(LOWER %R% LOWER) %R% REF1

# Test it
str_view(boy_names, pattern = pair_of_repeated, match = TRUE)

# Names with a pair that reverses
pair_that_reverses <- capture(LOWER) %R% capture(LOWER) %R% REF2 %R% REF1

# Test it
str_view(boy_names, pattern = pair_that_reverses, match = TRUE)

# Four letter palindrome names
four_letter_palindrome <- exactly(capture(LOWER) %R% capture(LOWER) %R% REF2 %R% REF1)

# Test it
str_view(boy_names, pattern = four_letter_palindrome, match = TRUE)

In addition to matching repeated values, backreferences can also be used for replacement.

4.2.2 Replacing with regular expressions

Now, you’ve mastered matching with backreferences, you’ll build up to replacing with backreferences, but first let’s review str_replace() now that you’ve got regular expressions under your belt.

Remember str_replace() takes three arguments, string a vector of strings to do the replacements in, pattern that identifies the parts of strings to replace and replacement the thing to use as a replacement.

replacement can be a vector, the same length as string, each element specifies the replacement to be used in each string.

# View text containing phone numbers
contact
[1] "Call me at 555-555-0191"                  "123 Main St"                             
[3] "(555) 555 0191"                           "Phone: 555.555.0191 Mobile: 555.555.0192"
# Replace digits with "X"
str_replace(contact, DGT, "X")
[1] "Call me at X55-555-0191"                  "X23 Main St"                             
[3] "(X55) 555 0191"                           "Phone: X55.555.0191 Mobile: 555.555.0192"
# Replace all digits with "X"
str_replace_all(contact, DGT, "X")
[1] "Call me at XXX-XXX-XXXX"                  "XXX Main St"                             
[3] "(XXX) XXX XXXX"                           "Phone: XXX.XXX.XXXX Mobile: XXX.XXX.XXXX"
# Replace all digits with different symbol
str_replace_all(contact, DGT, c("X", ".", "*", "_"))
[1] "Call me at XXX-XXX-XXXX"                  "... Main St"                             
[3] "(***) *** ****"                           "Phone: ___.___.____ Mobile: ___.___.____"

Using "" for the replacement value is a great way to cut unwanted bits from a string.

4.2.3 Replacing with backreferences

The replacement argument to str_replace() can also include backreferences. This works just like specifying patterns with backreferences, except the capture happens in the pattern argument, and the backreference is used in the replacement argument.

x <- c("hello", "sweet", "kitten")
str_replace(x, capture(ANY_CHAR), str_c(REF1, REF1))
[1] "hhello"  "ssweet"  "kkitten"

capture(ANY_CHAR) will match the first character no matter what it is. Then the replacement str_c(REF1, REF1) combines the captured character with itself, in effect doubling the first letter of each string.

You are going to use this to create some alternative, more lively accident narratives.

The strategy you’ll take is to match words ending in “ING” then replace them with an adverb followed by the original word.

adverbs <- readRDS("adverbs.rds")
# Build pattern to match words ending in "ING"
pattern <- one_or_more(WRD) %R% "ING"
str_view(narratives, pattern)


# Test replacement
str_replace(narratives, capture(pattern), 
  str_c("CARELESSLY", REF1, sep = " "))
 [1] "19YOM-SHOULDER STRAIN-WAS TACKLED WHILE CARELESSLY PLAYING FOOTBALL W/ FRIENDS "                      
 [2] "31 YOF FELL FROM TOILET HITITNG HEAD CARELESSLY SUSTAINING A CHI "                                    
 [3] "ANKLE STR. 82 YOM STRAINED ANKLE CARELESSLY GETTING OUT OF BED "                                      
 [4] "TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*"                       
 [5] "10YOM CUT THUMB ON METAL TRASH CAN DX AVULSION OF SKIN OF THUMB "                                     
 [6] "53 YO F TRIPPED ON CARPET AT HOME. DX HIP CONTUSION "                                                 
 [7] "13 MOF CARELESSLY TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION"
 [8] "14YR M CARELESSLY PLAYING FOOTBALL; DX KNEE SPRAIN "                                                  
 [9] "55YOM RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE "                                 
[10] "5 YOM CARELESSLY ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN"                
# One adverb per narrative
adverbs_10 <- sample(adverbs, 10)

# Replace "***ing" with "adverb ***ly"
str_replace(narratives, 
  capture(pattern),
  str_c(adverbs_10, REF1, sep = " "))  
 [1] "19YOM-SHOULDER STRAIN-WAS TACKLED WHILE EXCITEDLY PLAYING FOOTBALL W/ FRIENDS "                         
 [2] "31 YOF FELL FROM TOILET HITITNG HEAD FEROCIOUSLY SUSTAINING A CHI "                                     
 [3] "ANKLE STR. 82 YOM STRAINED ANKLE HIGHLY GETTING OUT OF BED "                                            
 [4] "TRIPPED OVER CAT AND LANDED ON HARDWOOD FLOOR. LACERATION ELBOW, LEFT. 33 YOF*"                         
 [5] "10YOM CUT THUMB ON METAL TRASH CAN DX AVULSION OF SKIN OF THUMB "                                       
 [6] "53 YO F TRIPPED ON CARPET AT HOME. DX HIP CONTUSION "                                                   
 [7] "13 MOF TREMENDOUSLY TRYING TO STAND UP HOLDING ONTO BED FELL AND HIT FOREHEAD ON RADIATOR DX LACERATION"
 [8] "14YR M ACCIDENTALLY PLAYING FOOTBALL; DX KNEE SPRAIN "                                                  
 [9] "55YOM RIDER OF A BICYCLE AND FELL OFF SUSTAINED A CONTUSION TO KNEE "                                   
[10] "5 YOM RIGHTEOUSLY ROLLING ON FLOOR DOING A SOMERSAULT AND SUSTAINED A CERVICAL STRA IN"                 

Replacement combined with backreferences can be really useful for reformatting text data.

4.3 Unicode and pattern matching

4.3.1 Matching a specific code point or code groups

Things can get tricky when some characters can be specified two ways, for example è, an e with a grave accent, can be specified either with the single code point \u00e8 or the combination of a \u0065 and a combining grave accent \u0300. They look the same:

x <- c("\u00e8", "\u0065\u0300")
writeLines(x)
è
e`

But, specifying the single code point only matches that version:

str_view(x, "\u00e8")

The stringi package that stringr is built on contains functions for converting between the two forms. stri_trans_nfc() composes characters with combining accents into a single character. stri_trans_nfd() decomposes character with accents into separate letter and accent characters. You can see how the characters differ by looking at the hexadecimal codes.

library(stringi)
as.hexmode(utf8ToInt(stri_trans_nfd("\u00e8")))
[1] "065" "300"
as.hexmode(utf8ToInt(stri_trans_nfc("\u0065\u0300")))
[1] "e8"

In Unicode, an accent is known as a diacritic Unicode Property, and you can match it using the rebus value UP_DIACRITIC.

Vietnamese makes heavy use of diacritics to denote the tones in its words. In this exercise, you’ll manipulate the diacritics in the names of Vietnamese rulers.

# Names with builtin accents

(tay_son_builtin <- c(
  "Nguy\u1ec5n Nh\u1ea1c", 
  "Nguy\u1ec5n Hu\u1ec7",
  "Nguy\u1ec5n Quang To\u1ea3n"
))
[1] "Nguyễn Nhạc"       "Nguyễn Huệ"        "Nguyễn Quang Toản"
# Convert to separate accents
tay_son_separate <- stri_trans_nfd(tay_son_builtin)

# Verify that the string prints the same
tay_son_separate
[1] "Nguyễn Nhạc"       "Nguyễn Huệ"        "Nguyễn Quang Toản"
# Match all accents
str_view_all(tay_son_separate, UP_DIACRITIC)

Xin chúc mừng! ‘It is useful to be consistent about combining or separating diacritics.’ - possibly a Vietnamese proverb.

4.3.2 Matching a single grapheme

A related problem is matching a single character. You’ve used ANY_CHAR to do this up until now, but it will only match a character represented by a single code point. Take these three names:

x <- c("Adele", "Ad\u00e8le", "Ad\u0065\u0300le")
writeLines(x)
Adele
Adèle
Ade`le

They look the similar, but this regular expression only matches two of them:

str_view(x, "Ad" %R% ANY_CHAR %R% "le")

because in the third name è is represented by two code points. The Unicode standard has a concept of a grapheme that represents a display character, but may be composed of many code points. To match any grapheme you can use GRAPHEME.

str_view(x, "Ad" %R% GRAPHEME %R% "le")

Names of rulers from the Vietnamese Tây Sơn dynasty, with diacritics given as separate graphemes, is pre-defined as tay_son_separate.

# tay_son_separate has been pre-defined
tay_son_separate
[1] "Nguyễn Nhạc"       "Nguyễn Huệ"        "Nguyễn Quang Toản"
# View all the characters in tay_son_separate
str_view_all(tay_son_separate, ANY_CHAR)

# View all the graphemes in tay_son_separate
str_view_all(tay_son_separate, GRAPHEME)

# Combine the diacritics with their letters
tay_son_builtin <- stri_trans_nfc(tay_son_separate)
tay_son_builtin
[1] "Nguyễn Nhạc"       "Nguyễn Huệ"        "Nguyễn Quang Toản"
# View all the graphemes in tay_son_builtin
str_view_all(tay_son_builtin, GRAPHEME)

5 Case studies

5.1 Importance of being earnest

5.1.1 Getting the play into R

Your first step is to read the play into R using stri_read_lines().

You should take a look at the original text file: importance-of-being-earnest.txt

You’ll see there is some foreword and afterword text that Project Gutenberg has added. You’ll want to remove that, and then split the play into the introduction (the list of characters, scenes, etc.) and the main body.

earnest_file <- "importance-of-being-earnest.txt"
# Read play in using stri_read_lines()
earnest <- stri_read_lines(earnest_file)
# Detect start and end lines
start <- str_which(earnest, fixed("START OF THE PROJECT"))
end <- str_which(earnest, fixed("END OF THE PROJECT"))

# Get rid of gutenberg intro text
earnest_sub  <- earnest[(start + 1):(end - 1)]
# Read play in using stri_read_lines()
earnest <- stri_read_lines(earnest_file)

# Detect first act
lines_start <- str_which(earnest_sub, fixed("FIRST ACT"))

# Set up index
intro_line_index <- 1:(lines_start - 1)

# Split play into intro and play
intro_text <- earnest_sub[intro_line_index]
play_text <- earnest_sub[-intro_line_index]
# Take a look at the first 20 lines
writeLines(play_text[1:20])
FIRST ACT


SCENE


Morning-room in Algernon's flat in Half-Moon Street.  The room is
luxuriously and artistically furnished.  The sound of a piano is heard in
the adjoining room.

[Lane is arranging afternoon tea on the table, and after the music has
ceased, Algernon enters.]

Algernon.  Did you hear what I was playing, Lane?

Lane.  I didn't think it polite to listen, sir.

Algernon.  I'm sorry for that, for your sake.  I don't play
accurately--any one can play accurately--but I play with wonderful
expression.  As far as the piano is concerned, sentiment is my forte.  I

stri_read_lines() is a high performance alternative to readLines().

5.1.2 Identifying the lines, take 1

The first thing you might notice when you look at your vector play_text is there are lots of empty lines. They don’t really affect your task so you might want to remove them. The easiest way to find empty strings is to use the stringi function stri_isempty(), which returns a logical you can use to subset the not-empty strings:

# Get rid of empty strings
empty <- stri_isempty(play_text)
play_lines <- play_text[!empty]

So, how are you going to find the elements that indicate a character starts their line? Consider the following lines

play_lines[10:15]
[1] "Algernon.  I'm sorry for that, for your sake.  I don't play"             
[2] "accurately--any one can play accurately--but I play with wonderful"      
[3] "expression.  As far as the piano is concerned, sentiment is my forte.  I"
[4] "keep science for Life."                                                  
[5] "Lane.  Yes, sir."                                                        
[6] "Algernon.  And, speaking of the science of Life, have you got the"       

The first line is for Algernon, the next three strings are continuations of that line, then line 5 is for Lane and line 6 for Algernon.

How about looking for lines that start with a word followed by a .?

# Pattern for start, word then .
pattern_1 <- START %R% one_or_more(WRD) %R% DOT

# Test pattern_1
# str_view(play_lines, pattern_1, match = TRUE) 
# str_view(play_lines, pattern_1, match = FALSE)

# Pattern for start, capital, word then .
pattern_2 <- START %R% char_class("A-Z") %R% one_or_more(WRD) %R% DOT

# Test pattern_2
# str_view(play_lines, pattern_2, match = TRUE)
# str_view(play_lines, pattern_2, match = FALSE)

# Pattern from last step
pattern_2 <- START %R% ascii_upper() %R% one_or_more(WRD) %R% DOT

# Get subset of lines that match
lines <- str_subset(play_lines, pattern_2)

# Extract match from lines
who <- str_extract(lines, pattern_2)

# Let's see what we have
unique(who)
 [1] "Algernon."   "Lane."       "Jack."       "Cecily."     "Ernest."     "University." "Gwendolen."  "July."      
 [9] "Chasuble."   "Merriman."   "Sunday."     "Mr."         "London."     "Cardew."     "Opera."      "Markby."    
[17] "Oxonian."   

It looks like your pattern wasn’t 100% successful. It missed Lady Bracknell, and picked up lines starting with University., July. and a few others. Let’s try a slightly different strategy.

5.1.3 Identifying the lines, take 2

The pattern “starts with a capital letter, has some other characters then a full stop” wasn’t specific enough. You ended up matching lines that started with things like University., July., London., and you missed characters like Lady Bracknell and Miss Prism.

Let’s take a different approach. You know the characters names from the play introduction. So, try specifically looking for lines that start with their names. You’ll find the or1() function from the rebus package helpful. It specifies alternatives but rather than each alternative being an argument like in or(), you can pass in a vector of alternatives.

# Create vector of characters
characters <- c("Algernon", "Jack", "Lane", "Cecily", "Gwendolen", "Chasuble", 
  "Merriman", "Lady Bracknell", "Miss Prism")

# Match start, then character name, then .
pattern_3 <- START %R% or1(characters) %R% DOT

# View matches of pattern_3
# str_view(play_lines, pattern_3, match = TRUE)
  
# View non-matches of pattern_3
# str_view(play_lines, pattern_3, match = FALSE)

# Pull out matches
lines <- str_subset(play_lines, pattern_3)

# Extract match from lines
who <- str_extract(lines, pattern_3)

# Let's see what we have
unique(who)
[1] "Algernon."       "Lane."           "Jack."           "Cecily."         "Gwendolen."      "Lady Bracknell."
[7] "Miss Prism."     "Chasuble."       "Merriman."      
# Count lines per character
table(who)
who
      Algernon.         Cecily.       Chasuble.      Gwendolen.           Jack. Lady Bracknell.           Lane. 
            201             154              42             102             219              84              21 
      Merriman.     Miss Prism. 
             17              41 

Algernon and Jack get the most lines, more than ten times more than Merriman who has the fewest. If you were looking really closely you might have noticed the pattern didn’t pick up the line Jack and Algernon [Speaking together.] which you really should be counting as a line for both Jack and Algernon. One solution might be to look for these “Speaking together” lines, parse out the characters, and add to your counts.

5.2 A case study on case

5.2.1 Changing case to ease matching

A simple solution to working with strings in mixed case, is to simply transform them into all lower or all upper case. Depending on your choice, you can then specify your pattern in the same case.

For example, while looking for “cat” finds no matches in the following string,

x <- c("Cat", "CAT", "cAt") 
str_view(x, "cat")

transforming the string to lower case first ensures all variations match.

str_view(str_to_lower(x), "cat")

See if you can find the catcidents that also involved dogs. You’ll see a new rebus function called whole_word(). The argument to whole_word() will only match if it occurs as a word on its own, for example whole_word(“cat”) will match cat in “The cat” and “cat.” but not in “caterpillar”.

catcidents <- readRDS("catcidents.rds")

# catcidents has been pre-defined
head(catcidents)
[1] "79yOf Fractured fingeR tRiPPED ovER cAT ANd fell to FlOOr lAst nIGHT AT HOME*"                                                               
[2] "21 YOF REPORTS SUS LACERATION OF HER LEFT HAND WHEN SHE WAS OPENING A CAN OF CAT FOOD JUST PTA. DX HAND LACERATION%"                         
[3] "87YOF TRIPPED OVER CAT, HIT LEG ON STEP. DX LOWER LEG CONTUSION "                                                                            
[4] "bLUNT CHest trAUma, R/o RIb fX, R/O CartiLAgE InJ To RIB cAge; 32YOM walKiNG DOG, dog took OfF aFtER cAt,FelL,stRucK CHest oN STepS,hiT rIbS"
[5] "42YOF TO ER FOR BACK PAIN AFTER PUTTING DOWN SOME CAT LITTER DX: BACK PAIN, SCIATICA"                                                        
[6] "4YOf DOg jUst hAd PUpPieS, Cat TRIED 2 get PuPpIes, pT THru CaT dwn stA Irs, LoST foOTING & FELl down ~12 stePS; MInor hEaD iNJuRY"          
# Construct pattern of DOG in boundaries
whole_dog_pattern <- whole_word("DOG")

# See matches to word DOG
str_view(catcidents, whole_dog_pattern, match = TRUE)


# Transform catcidents to upper case
catcidents_upper <- str_to_upper(catcidents)

# View matches to word "DOG" again
str_view(catcidents_upper, whole_dog_pattern, match = TRUE)


# Which strings match?
has_dog <- str_detect(catcidents_upper, whole_dog_pattern)

# Pull out matching strings in original 
catcidents[has_dog]
 [1] "bLUNT CHest trAUma, R/o RIb fX, R/O CartiLAgE InJ To RIB cAge; 32YOM walKiNG DOG, dog took OfF aFtER cAt,FelL,stRucK CHest oN STepS,hiT rIbS"   
 [2] "4YOf DOg jUst hAd PUpPieS, Cat TRIED 2 get PuPpIes, pT THru CaT dwn stA Irs, LoST foOTING & FELl down ~12 stePS; MInor hEaD iNJuRY"             
 [3] "unhelmeted 14yof riding her bike with her dog when she saw a cat and sw erved c/o head/shoulder/elbow pain.dx: minor head injury,left shoulder" 
 [4] "Rt Shoulder Strain.26Yof Was Walking Dog On Leash And Dot Saw A Cat And Pulled Leash."                                                          
 [5] "67 YO F WENT TO WALK DOG, IT STARTED TO CHASE CAT JERKED LEASH PULLED H ER OFF PATIO, FELL HURT ANKLES. DX BILATERAL ANKLE FRACTURES"           
 [6] "46yof taking dog outside, dog bent her fingers back on a door. dog jerk ed when saw cat. hand holding leash caught on door jamb/ct hand"        
 [7] "PUSHING HER UTD WITH SHOTS DOG AWAY FROM THE CAT'S BOWL&BITTEN TO FINGE R>>PW/DOG BITE"                                                         
 [8] "DX R SH PN: 27YOF W/ R SH PN X 5D. STATES WAS YANK' BY HER DOG ON LEASH W DOG RAN AFTER CAT; WORSE' PN SINCE. FULL ROM BUT VERY PAINFUL TO MOVE"
 [9] "39Yof dog pulled her down the stairs while chasing a cat dx: rt ankle inj"                                                                      
[10] "44Yof Walking Dog And The Dof Took Off After A Cat And Pulled Pt Down B Y The Leash Strained Neck"                                              

5.2.2 Ignoring case when matching

Rather than transforming the input strings, another approach is to specify that the matching should be case insensitive. This is one of the options to the stringr regex() function.

Take our previous example,

x <- c("Cat", "CAT", "cAt") 
str_view(x, "cat")

o match the pattern cat in a case insensitive way, we wrap our pattern in regex() and specify the argument ignore_case = TRUE,

str_view(x, stringr::regex("cat", ignore_case = TRUE))

Notice that the matches retain their original case and any variant of cat matches.

# View matches to "TRIP"
str_view(catcidents, "TRIP", match = TRUE)


# Construct case insensitive pattern
trip_pattern <- stringr::regex("TRIP", ignore_case = TRUE)

# View case insensitive matches to "TRIP"
str_view(catcidents, trip_pattern, match = TRUE)


# Get subset of matches
trip <- str_subset(catcidents, trip_pattern)

# Extract matches
str_extract(trip, trip_pattern)
 [1] "tRiP" "TRIP" "TRIP" "TRiP" "triP" "TRIP" "TRIP" "TRIP" "Trip" "trip" "TRIP" "TRIP"

5.2.3 Fixing case problems

Finally, you might want to transform strings to a common case. You’ve seen you can use str_to_upper() and str_to_lower(), but there is also str_to_title() which transforms to title case, in which every word starts with a capital letter.

This is another situation where stringi functions offer slightly more functionality than the stringr functions. The stringi function stri_trans_totitle() allows a specification of the type which, by default, is “word”, resulting in title case, but can also be “sentence” to give sentence case: only the first word in each sentence is capitalized.

library(stringi)

# Get first five catcidents
cat5 <- catcidents[1:5]

# Take a look at original
writeLines(cat5)
79yOf Fractured fingeR tRiPPED ovER cAT ANd fell to FlOOr lAst nIGHT AT HOME*
21 YOF REPORTS SUS LACERATION OF HER LEFT HAND WHEN SHE WAS OPENING A CAN OF CAT FOOD JUST PTA. DX HAND LACERATION%
87YOF TRIPPED OVER CAT, HIT LEG ON STEP. DX LOWER LEG CONTUSION 
bLUNT CHest trAUma, R/o RIb fX, R/O CartiLAgE InJ To RIB cAge; 32YOM walKiNG DOG, dog took OfF aFtER cAt,FelL,stRucK CHest oN STepS,hiT rIbS
42YOF TO ER FOR BACK PAIN AFTER PUTTING DOWN SOME CAT LITTER DX: BACK PAIN, SCIATICA
# Transform to title case
writeLines(str_to_title(cat5))
79yof Fractured Finger Tripped Over Cat And Fell To Floor Last Night At Home*
21 Yof Reports Sus Laceration Of Her Left Hand When She Was Opening A Can Of Cat Food Just Pta. Dx Hand Laceration%
87yof Tripped Over Cat, Hit Leg On Step. Dx Lower Leg Contusion 
Blunt Chest Trauma, R/O Rib Fx, R/O Cartilage Inj To Rib Cage; 32yom Walking Dog, Dog Took Off After Cat,Fell,Struck Chest On Steps,Hit Ribs
42yof To Er For Back Pain After Putting Down Some Cat Litter Dx: Back Pain, Sciatica
# Transform to title case with stringi
writeLines(stri_trans_totitle(cat5))
79yof Fractured Finger Tripped Over Cat And Fell To Floor Last Night At Home*
21 Yof Reports Sus Laceration Of Her Left Hand When She Was Opening A Can Of Cat Food Just Pta. Dx Hand Laceration%
87yof Tripped Over Cat, Hit Leg On Step. Dx Lower Leg Contusion 
Blunt Chest Trauma, R/O Rib Fx, R/O Cartilage Inj To Rib Cage; 32yom Walking Dog, Dog Took Off After Cat,Fell,Struck Chest On Steps,Hit Ribs
42yof To Er For Back Pain After Putting Down Some Cat Litter Dx: Back Pain, Sciatica
# Transform to sentence case with stringi
writeLines(stri_trans_totitle(cat5, type = "sentence"))
79yof fractured finger tripped over cat and fell to floor last night at home*
21 yof reports sus laceration of her left hand when she was opening a can of cat food just pta. Dx hand laceration%
87yof tripped over cat, hit leg on step. Dx lower leg contusion 
Blunt chest trauma, r/o rib fx, r/o cartilage inj to rib cage; 32yom walking dog, dog took off after cat,fell,struck chest on steps,hit ribs
42yof to er for back pain after putting down some cat litter dx: back pain, sciatica
LS0tDQp0aXRsZTogIlN0cmluZyBNYW5pcHVsYXRpb24gd2l0aCBzdHJpbmdyIGluIFIiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jX2NvbGxhcHNlZDogZmFsc2UNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICANCnRvY19kZXB0aDogMw0KLS0tDQojIFN0cmluZyBiYXNpY3MNCg0KIyMgSW50cm9kdWN0aW9uDQoNCiMjIyBRdW90ZXMNCg0KTGV0J3MgZ2V0IHN0YXJ0ZWQgYnkgZW50ZXJpbmcgc29tZSBzdHJpbmdzIGluIFIuIEluIHRoZSB2aWRlbyB5b3Ugc2F3IHRoYXQgeW91IHVzZSBxdW90ZXMgdG8gdGVsbCBSIHRvIGludGVycHJldCBzb21ldGhpbmcgYXMgYSBzdHJpbmcuIEJvdGggZG91YmxlIHF1b3RlcyAoIikgYW5kIHNpbmdsZSAoJykgcXVvdGVzIHdvcmssIGJ1dCB0aGVyZSBhcmUgc29tZSBndWlkZWxpbmVzIGZvciB3aGljaCB0byB1c2UuDQoNCkZpcnN0LCB5b3Ugc2hvdWxkIHByZWZlciBkb3VibGUgcXVvdGVzICgiKSB0byBzaW5nbGUgcXVvdGVzICgnKS4gVGhhdCBtZWFucywgd2hlbmV2ZXIgeW91IGFyZSBkZWZpbmluZyBhIHN0cmluZyB5b3VyIGZpcnN0IGludHVpdGlvbiBzaG91bGQgYmUgdG8gdXNlICIuDQoNClVuZm9ydHVuYXRlbHkgaWYgeW91ciBzdHJpbmcgaGFzICIgaW5zaWRlIGl0LCBSIHdpbGwgaW50ZXJwcmV0IHRoZSBkb3VibGUgcXVvdGUgYXMgInRoaXMgaXMgdGhlIGVuZCBvZiB0aGUgc3RyaW5nIiwgbm90IGFzICJ0aGlzIGlzIHRoZSBjaGFyYWN0ZXIgIiIuIFRoaXMgaXMgb25lIHRpbWUgeW91IGNhbiBmb3JnZXQgdGhlIGZpcnN0IGd1aWRlbGluZSBhbmQgdXNlIHRoZSBzaW5nbGUgcXVvdGUsICcsIHRvIGRlZmluZSB0aGUgc3RyaW5nLg0KDQpGaW5hbGx5LCB0aGVyZSBhcmUgY2FzZXMgd2hlcmUgeW91IG5lZWQgYm90aCAnIGFuZCAiIGluc2lkZSB0aGUgc3RyaW5nLiBJbiB0aGlzIGNhc2UsIGZhbGwgYmFjayB0byB0aGUgZmlyc3QgZ3VpZGVsaW5lIGFuZCB1c2UgIiB0byBkZWZpbmUgdGhlIHN0cmluZywgYnV0IHlvdSdsbCBoYXZlIHRvIGVzY2FwZSBhbnkgZG91YmxlIHF1b3RlcyBpbnNpZGUgdGhlIHN0cmluZyB1c2luZyBhIGJhY2tzbGFzaCAoaS5lLiBcXCIpLg0KYGBge3J9DQojIERlZmluZSBsaW5lMQ0KbGluZTEgPC0gIlRoZSB0YWJsZSB3YXMgYSBsYXJnZSBvbmUsIGJ1dCB0aGUgdGhyZWUgd2VyZSBhbGwgY3Jvd2RlZCB0b2dldGhlciBhdCBvbmUgY29ybmVyIG9mIGl0OiINCg0KIyBEZWZpbmUgbGluZTINCmxpbmUyIDwtICciTm8gcm9vbSEgTm8gcm9vbSEiIHRoZXkgY3JpZWQgb3V0IHdoZW4gdGhleSBzYXcgQWxpY2UgY29taW5nLicNCg0KIyBEZWZpbmUgbGluZTMNCmxpbmUzIDwtICJcIlRoZXJlJ3MgcGxlbnR5IG9mIHJvb20hXCIgc2FpZCBBbGljZSBpbmRpZ25hbnRseSwgYW5kIHNoZSBzYXQgZG93biBpbiBhIGxhcmdlIGFybS1jaGFpciBhdCBvbmUgZW5kIG9mIHRoZSB0YWJsZS4iDQpgYGANCiMjIyBXaGF0IHlvdSBzZWUgaXNuJ3QgYWx3YXlzIHdoYXQgeW91IGhhdmUNCg0KVGFrZSBhIGxvb2sgYXQgbGluZTIsIHRoZSBzdHJpbmcgeW91IGp1c3QgZGVmaW5lZCwgYnkgcHJpbnRpbmcgaXQ6DQpgYGB7cn0NCmxpbmUyDQpgYGANCg0KDQpFdmVuIHRob3VnaCB5b3UgdXNlZCBzaW5nbGUgcXVvdGVzIHNvIHlvdSBkaWRuJ3QgaGF2ZSB0byBlc2NhcGUgYW55IGRvdWJsZSBxdW90ZXMsIHdoZW4gUiBwcmludHMgaXQsIHlvdSdsbCBzZWUgZXNjYXBlZCBkb3VibGUgcXVvdGVzIChcXCIpISBSIGRvZXNuJ3QgY2FyZSBob3cgeW91IGRlZmluZWQgdGhlIHN0cmluZywgaXQgb25seSBrbm93cyB3aGF0IHRoZSBzdHJpbmcgcmVwcmVzZW50cywgaW4gdGhpcyBjYXNlLCBhIHN0cmluZyB3aXRoIGRvdWJsZSBxdW90ZXMgaW5zaWRlLg0KDQpXaGVuIHlvdSBhc2sgUiBmb3IgbGluZTIgaXQgaXMgYWN0dWFsbHkgY2FsbGluZyBwcmludChsaW5lMikgYW5kIHRoZSBwcmludCgpIG1ldGhvZCBmb3Igc3RyaW5ncyBkaXNwbGF5cyBzdHJpbmdzIGFzIHlvdSBtaWdodCBlbnRlciB0aGVtLiBJZiB5b3Ugd2FudCB0byBzZWUgdGhlIHN0cmluZyBpdCByZXByZXNlbnRzIHlvdSdsbCBuZWVkIHRvIHVzZSBhIGRpZmZlcmVudCBmdW5jdGlvbjogd3JpdGVMaW5lcygpLg0KDQpZb3UgY2FuIHBhc3Mgd3JpdGVMaW5lcygpIGEgdmVjdG9yIG9mIHN0cmluZ3MgYW5kIGl0IHdpbGwgcHJpbnQgdGhlbSB0byB0aGUgc2NyZWVuLCBlYWNoIG9uIGEgbmV3IGxpbmUuIFRoaXMgaXMgYSBncmVhdCB3YXkgdG8gY2hlY2sgdGhlIHN0cmluZyB5b3UgZW50ZXJlZCByZWFsbHkgZG9lcyByZXByZXNlbnQgdGhlIHN0cmluZyB5b3Ugd2FudGVkLg0KYGBge3J9DQojIFB1dHRpbmcgbGluZXMgaW4gYSB2ZWN0b3INCmxpbmVzIDwtIGMobGluZTEsIGxpbmUyLCBsaW5lMykNCg0KIyBQcmludCBsaW5lcw0KcHJpbnQobGluZXMpDQoNCiMgVXNlIHdyaXRlTGluZXMoKSBvbiBsaW5lcw0Kd3JpdGVMaW5lcyhsaW5lcykNCg0KIyBXcml0ZSBsaW5lcyB3aXRoIGEgc3BhY2Ugc2VwYXJhdG9yDQp3cml0ZUxpbmVzKGxpbmVzLCBzZXAgPSAiICIpDQoNCiMgVXNlIHdyaXRlTGluZXMoKSBvbiB0aGUgc3RyaW5nICJoZWxsb1xuXFUxRjMwRCINCndyaXRlTGluZXMoImhlbGxvXG5cVTFGMzBEIikNCmBgYA0KVGhlIGZ1bmN0aW9uIGNhdCgpIGlzIHZlcnkgc2ltaWxhciB0byB3cml0ZUxpbmVzKCksIGJ1dCBieSBkZWZhdWx0IHNlcGFyYXRlcyBlbGVtZW50cyB3aXRoIGEgc3BhY2UsIGFuZCB3aWxsIGF0dGVtcHQgdG8gY29udmVydCBub24tY2hhcmFjdGVyIG9iamVjdHMgdG8gYSBzdHJpbmcuDQoNCiMjIyBFc2NhcGUgc2VxdWVuY2VzDQoNCkhvdyBkaWQgeW91IGdldCB0d28gbGluZXMgZnJvbSBvbmUgc3RyaW5nLCBhbmQgaG93IGRpZCB5b3UgZ2V0IHRoYXQgbGl0dGxlIGdsb2JlPyBUaGUga2V5IGlzIHRoZSBcXC4NCg0KQSBzZXF1ZW5jZSBpbiBhIHN0cmluZyB0aGF0IHN0YXJ0cyB3aXRoIGEgXFwgaXMgY2FsbGVkIGFuIGVzY2FwZSBzZXF1ZW5jZSBhbmQgYWxsb3dzIHVzIHRvIGluY2x1ZGUgc3BlY2lhbCBjaGFyYWN0ZXJzIGluIG91ciBzdHJpbmdzLiBZb3Ugc2F3IG9uZSBlc2NhcGUgc2VxdWVuY2UgaW4gdGhlIGZpcnN0IGV4ZXJjaXNlOiBcXCIgaXMgdXNlZCB0byBkZW5vdGUgYSBkb3VibGUgcXVvdGUuDQoNCkluICJoZWxsb1xcblxcVTFGMzBEIiB0aGVyZSBhcmUgdHdvIGVzY2FwZSBzZXF1ZW5jZXM6IFxcbiBnaXZlcyBhIG5ld2xpbmUsIGFuZCBcXFUgZm9sbG93ZWQgYnkgdXAgdG8gOCBoZXggZGlnaXRzIHNlcXVlbmNlIGRlbm90ZXMgYSBwYXJ0aWN1bGFyIFVuaWNvZGUgY2hhcmFjdGVyLg0KDQpVbmljb2RlIGlzIGEgc3RhbmRhcmQgZm9yIHJlcHJlc2VudGluZyBjaGFyYWN0ZXJzIHRoYXQgbWlnaHQgbm90IGJlIG9uIHlvdXIga2V5Ym9hcmQuIEVhY2ggYXZhaWxhYmxlIGNoYXJhY3RlciBoYXMgYSBVbmljb2RlIGNvZGUgcG9pbnQ6IGEgbnVtYmVyIHRoYXQgdW5pcXVlbHkgaWRlbnRpZmllcyBpdC4gVGhlc2UgY29kZSBwb2ludHMgYXJlIGdlbmVyYWxseSB3cml0dGVuIGluIGhleCBub3RhdGlvbiwgdGhhdCBpcywgdXNpbmcgYmFzZSAxNiBhbmQgdGhlIGRpZ2l0cyAwLTkgYW5kIEEtRi4gWW91IGNhbiBmaW5kIHRoZSBjb2RlIHBvaW50IGZvciBhIHBhcnRpY3VsYXIgY2hhcmFjdGVyIGJ5IGxvb2tpbmcgdXAgYSBbY29kZSBjaGFydF0oaHR0cDovL3d3dy51bmljb2RlLm9yZy9jaGFydHMvKS4gSWYgeW91IG9ubHkgbmVlZCBmb3VyIGRpZ2l0cyBmb3IgdGhlIGNvZGVwb2ludCwgYW4gYWx0ZXJuYXRpdmUgZXNjYXBlIHNlcXVlbmNlIGlzIFxcdS4NCldoZW4gUiBjb21lcyBhY3Jvc3MgYSBcXCBpdCBhc3N1bWVzIHlvdSBhcmUgc3RhcnRpbmcgYW4gZXNjYXBlLCBzbyBpZiB5b3UgYWN0dWFsbHkgbmVlZCBhIGJhY2tzbGFzaCBpbiB5b3VyIHN0cmluZyB5b3UnbGwgbmVlZCB0aGUgc2VxdWVuY2UgXFxcXC4NCmBgYHtyfQ0KIyBTaG91bGQgZGlzcGxheTogVG8gaGF2ZSBhIFwgeW91IG5lZWQgXFwNCndyaXRlTGluZXMoIlRvIGhhdmUgYSBcXCB5b3UgbmVlZCBcXFxcIikNCg0KIyBTaG91bGQgZGlzcGxheTogDQojIFRoaXMgaXMgYSByZWFsbHkgDQojIHJlYWxseSByZWFsbHkgDQojIGxvbmcgc3RyaW5nDQp3cml0ZUxpbmVzKCJUaGlzIGlzIGEgcmVhbGx5XG5yZWFsbHkgcmVhbGx5XG5sb25nIHN0cmluZyIpDQoNCiMgVXNlIHdyaXRlTGluZXMoKSB3aXRoIA0KIyAiXHUwOTI4XHUwOTJlXHUwOTM4XHUwOTRkXHUwOTI0XHUwOTQ3IFx1MDkyNlx1MDk0MVx1MDkyOFx1MDkzZlx1MDkyZlx1MDkzZSIgLSAiSGVsbG8gV29ybGQiIGluIEhpbmRpIQ0Kd3JpdGVMaW5lcygiXHUwOTI4XHUwOTJlXHUwOTM4XHUwOTRkXHUwOTI0XHUwOTQ3IFx1MDkyNlx1MDk0MVx1MDkyOFx1MDkzZlx1MDkyZlx1MDkzZSIpIA0KYGBgDQojIyBUdXJuaW5nIG51bWJlcnMgaW50byBzdHJpbmdzDQoNCiMjIyBVc2luZyBmb3JtYXQoKSB3aXRoIG51bWJlcnMNCg0KVGhlIGJlaGF2aW9yIG9mIGZvcm1hdCgpIGNhbiBiZSBwcmV0dHkgY29uZnVzaW5nLCBzbyB5b3UnbGwgc3BlbmQgbW9zdCBvZiB0aGlzIGV4ZXJjaXNlIGV4cGxvcmluZyBob3cgaXQgd29ya3MuDQoNClJlY2FsbCBmcm9tIHRoZSB2aWRlbywgdGhlIHNjaWVudGlmaWMgYXJndW1lbnQgdG8gZm9ybWF0KCkgY29udHJvbHMgd2hldGhlciB0aGUgbnVtYmVycyBhcmUgZGlzcGxheWVkIGluIGZpeGVkIChzY2llbnRpZmljID0gRkFMU0UpIG9yIHNjaWVudGlmaWMgKHNjaWVudGlmaWMgPSBUUlVFKSBmb3JtYXQuDQoNCldoZW4gdGhlIHJlcHJlc2VudGF0aW9uIGlzIHNjaWVudGlmaWMsIHRoZSBkaWdpdHMgYXJndW1lbnQgaXMgdGhlIG51bWJlciBvZiBkaWdpdHMgYmVmb3JlIHRoZSBleHBvbmVudC4gV2hlbiB0aGUgcmVwcmVzZW50YXRpb24gaXMgZml4ZWQsIGRpZ2l0cyBjb250cm9scyB0aGUgc2lnbmlmaWNhbnQgZGlnaXRzIHVzZWQgZm9yIHRoZSBzbWFsbGVzdCAoaW4gbWFnbml0dWRlKSBudW1iZXIuIEVhY2ggb3RoZXIgbnVtYmVyIHdpbGwgYmUgZm9ybWF0dGVkIHRvIG1hdGNoIHRoZSBudW1iZXIgb2YgZGVjaW1hbCBwbGFjZXMgaW4gdGhlIHNtYWxsZXN0IG51bWJlci4gVGhpcyBtZWFucyB0aGUgbnVtYmVyIG9mIGRlY2ltYWwgcGxhY2VzIHlvdSBnZXQgaW4geW91ciBvdXRwdXQgZGVwZW5kcyBvbiBhbGwgdGhlIHZhbHVlcyB5b3UgYXJlIGZvcm1hdHRpbmchDQoNCkZvciBleGFtcGxlLCBpZiB0aGUgc21hbGxlc3QgbnVtYmVyIGlzIDAuMDAxMSwgYW5kIGRpZ2l0cyA9IDEsIHRoZW4gMC4wMDExIHJlcXVpcmVzIDMgcGxhY2VzIGFmdGVyIHRoZSBkZWNpbWFsIHRvIHJlcHJlc2VudCBpdCB0byAxIHNpZ25pZmljYW50IGRpZ2l0LCAwLjAwMS4gRXZlcnkgb3RoZXIgbnVtYmVyIHdpbGwgYmUgZm9ybWF0dGVkIHRvIDMgcGxhY2VzIGFmdGVyIHRoZSBkZWNpbWFsIHBvaW50Lg0KRm9ybWF0IGMoMC4wMDExLCAwLjAxMSwgMSkgd2l0aCBkaWdpdHMgPSAxLiBUaGlzIGlzIGxpa2UgdGhlIGV4YW1wbGUgZGVzY3JpYmVkIGFib3ZlLg0KTm93LCBmb3JtYXQgYygxLjAwMTEsIDIuMDExLCAxKSB3aXRoIGRpZ2l0cyA9IDEuIFRyeSB0byBwcmVkaWN0IHdoYXQgeW91IG1pZ2h0IGdldCBiZWZvcmUgeW91IHRyeSBpdC4NCkZvcm1hdCBwZXJjZW50X2NoYW5nZSBieSBjaG9vc2luZyB0aGUgZGlnaXRzIGFyZ3VtZW50IHNvIHRoYXQgdGhlIHZhbHVlcyBhcmUgcHJlc2VudGVkIHdpdGggb25lIHBsYWNlIGFmdGVyIHRoZSBkZWNpbWFsIHBvaW50Lg0KRm9ybWF0IGluY29tZSBieSBjaG9vc2luZyB0aGUgZGlnaXRzIGFyZ3VtZW50IHNvIHRoYXQgdGhlIHZhbHVlcyBhcmUgcHJlc2VudGVkIGFzIHdob2xlIG51bWJlcnMgKGkuZS4gbm8gcGxhY2VzIGFmdGVyIHRoZSBkZWNpbWFsIHBvaW50KS4NCkZvcm1hdCBwX3ZhbHVlcyB1c2luZyBhIGZpeGVkIHJlcHJlc2VudGF0aW9uLg0KYGBge3J9DQojIFNvbWUgdmVjdG9ycyBvZiBudW1iZXJzDQpwZXJjZW50X2NoYW5nZSAgPC0gYyg0LCAtMS45MSwgMy4wMCwgLTUuMDAyKQ0KaW5jb21lIDwtICBjKDcyLjE5LCAxMDMwLjE4LCAxMDI5MS45MywgMTE4OTE5Mi4xOCkNCnBfdmFsdWVzIDwtIGMoMC4xMiwgMC45OCwgMC4wMDAwMTkxLCAwLjAwMDAwMDAwMDAyKQ0KDQojIEZvcm1hdCBjKDAuMDAxMSwgMC4wMTEsIDEpIHdpdGggZGlnaXRzID0gMQ0KZm9ybWF0KGMoMC4wMDExLCAwLjAxMSwgMSksIGRpZ2l0cyA9IDEpDQoNCiMgRm9ybWF0IGMoMS4wMDExLCAyLjAxMSwgMSkgd2l0aCBkaWdpdHMgPSAxDQpmb3JtYXQoYygxLjAwMTEsIDIuMDExLCAxKSwgZGlnaXRzID0gMSkNCg0KIyBGb3JtYXQgcGVyY2VudF9jaGFuZ2UgdG8gb25lIHBsYWNlIGFmdGVyIHRoZSBkZWNpbWFsIHBvaW50DQpmb3JtYXQocGVyY2VudF9jaGFuZ2UsIGRpZ2l0cyA9IDIpDQoNCiMgRm9ybWF0IGluY29tZSB0byB3aG9sZSBudW1iZXJzDQpmb3JtYXQoaW5jb21lLCBkaWdpdHMgPSAyKQ0KDQojIEZvcm1hdCBwX3ZhbHVlcyBpbiBmaXhlZCBmb3JtYXQNCmZvcm1hdChwX3ZhbHVlcywgc2NpZW50aWZpYyA9IEZBTFNFKQ0KYGBgDQojIyMgQ29udHJvbGxpbmcgb3RoZXIgYXNwZWN0cyBvZiB0aGUgc3RyaW5nDQoNCm90IG9ubHkgZG9lcyBmb3JtYXQoKSBjb250cm9sIHRoZSB3YXkgdGhlIG51bWJlciBpcyByZXByZXNlbnRlZCwgaXQgYWxzbyBjb250cm9scyBzb21lIG9mIHRoZSBwcm9wZXJ0aWVzIG9mIHRoZSByZXN1bHRpbmcgc3RyaW5nIHRoYXQgYWZmZWN0IGl0cyBkaXNwbGF5Lg0KDQpGb3IgZXhhbXBsZSwgYnkgZGVmYXVsdCBmb3JtYXQoKSB3aWxsIHBhZCB0aGUgc3RhcnQgb2YgdGhlIHN0cmluZ3Mgd2l0aCBzcGFjZXMgc28gdGhhdCB0aGUgZGVjaW1hbCBwb2ludHMgbGluZSB1cCwgd2hpY2ggaXMgcmVhbGx5IHVzZWZ1bCBpZiB5b3UgYXJlIHByZXNlbnRpbmcgdGhlIG51bWJlcnMgaW4gYSB2ZXJ0aWNhbCBjb2x1bW4uIEhvd2V2ZXIsIGlmIHlvdSBhcmUgcHV0dGluZyB0aGUgbnVtYmVyIGluIHRoZSBtaWRkbGUgb2YgYSBzZW50ZW5jZSwgeW91IG1pZ2h0IG5vdCB3YW50IHRoZXNlIGV4dHJhIHNwYWNlcy4gWW91IGNhbiBzZXQgdHJpbSA9IFRSVUUgdG8gcmVtb3ZlIHRoZW0uDQoNCldoZW4gbnVtYmVycyBhcmUgbG9uZyBpdCBjYW4gYmUgaGVscGZ1bCB0byAicHJldHRpZnkiIHRoZW0sIGZvciBleGFtcGxlIGluc3RlYWQgb2YgMTAwMDAwMDAwMCBkaXNwbGF5IDEsMDAwLDAwMCwwMDAuIEluIHRoaXMgY2FzZSBhICwgaXMgYWRkZWQgZXZlcnkgMyBkaWdpdHMuIFRoaXMgY2FuIGJlIGNvbnRyb2xsZWQgYnkgdGhlIGJpZy5pbnRlcnZhbCBhbmQgYmlnLm1hcmsgYXJndW1lbnRzLCBlLmcuIGZvcm1hdCgxMDAwMDAwMDAwLCBiaWcubWFyayA9ICIsIiwgYmlnLmludGVydmFsID0gMywgc2NpZW50aWZpYyA9IEZBTFNFKS4gVGhlc2UgYXJndW1lbnRzIGFyZSBhY3R1YWxseSBwYXNzZWQgb24gdG8gcHJldHR5TnVtKCkgc28gaGVhZCB0aGVyZSBmb3IgYW55IGZ1cnRoZXIgZGV0YWlscy4NCmBgYHtyfQ0KZm9ybWF0dGVkX2luY29tZSA8LSBmb3JtYXQoaW5jb21lLCBkaWdpdHMgPSAyKQ0KDQojIFByaW50IGZvcm1hdHRlZF9pbmNvbWUNCmZvcm1hdHRlZF9pbmNvbWUNCg0KIyBDYWxsIHdyaXRlTGluZXMoKSBvbiB0aGUgZm9ybWF0dGVkIGluY29tZQ0Kd3JpdGVMaW5lcyhmb3JtYXR0ZWRfaW5jb21lKQ0KDQojIERlZmluZSB0cmltbWVkX2luY29tZQ0KdHJpbW1lZF9pbmNvbWUgPC0gZm9ybWF0KGluY29tZSwgZGlnaXRzID0gMiwgdHJpbSA9IFRSVUUpDQoNCiMgQ2FsbCB3cml0ZUxpbmVzKCkgb24gdGhlIHRyaW1tZWRfaW5jb21lDQp3cml0ZUxpbmVzKHRyaW1tZWRfaW5jb21lKQ0KDQojIERlZmluZSBwcmV0dHlfaW5jb21lDQpwcmV0dHlfaW5jb21lIDwtIGZvcm1hdChpbmNvbWUsIGRpZ2l0cyA9IDIsIGJpZy5tYXJrID0gIiwiKQ0KDQojIENhbGwgd3JpdGVMaW5lcygpIG9uIHRoZSBwcmV0dHlfaW5jb21lDQp3cml0ZUxpbmVzKHByZXR0eV9pbmNvbWUpDQpgYGANCiMjIyBmb3JtYXRDKCkNCg0KVGhlIGZ1bmN0aW9uIGZvcm1hdEMoKSBwcm92aWRlcyBhbiBhbHRlcm5hdGl2ZSB3YXkgdG8gZm9ybWF0IG51bWJlcnMgYmFzZWQgb24gQyBzdHlsZSBzeW50YXguDQoNClJhdGhlciB0aGFuIGEgc2NpZW50aWZpYyBhcmd1bWVudCwgZm9ybWF0QygpIGhhcyBhIGZvcm1hdCBhcmd1bWVudCB0aGF0IHRha2VzIGEgY29kZSByZXByZXNlbnRpbmcgdGhlIHJlcXVpcmVkIGZvcm1hdC4gVGhlIG1vc3QgdXNlZnVsIGFyZToNCg0KImYiIGZvciBmaXhlZCwNCiJlIiBmb3Igc2NpZW50aWZpYywgYW5kDQoiZyIgZm9yIGZpeGVkIHVubGVzcyBzY2llbnRpZmljIHNhdmVzIHNwYWNlDQpXaGVuIHVzaW5nIHNjaWVudGlmaWMgZm9ybWF0LCB0aGUgZGlnaXRzIGFyZ3VtZW50IGJlaGF2ZXMgbGlrZSBpdCBkb2VzIGluIGZvcm1hdCgpOyBpdCBzcGVjaWZpZXMgdGhlIG51bWJlciBvZiBzaWduaWZpY2FudCBkaWdpdHMuIEhvd2V2ZXIsIHVubGlrZSBmb3JtYXQoKSwgd2hlbiB1c2luZyBmaXhlZCBmb3JtYXQsIGRpZ2l0cyBpcyB0aGUgbnVtYmVyIG9mIGRpZ2l0cyBhZnRlciB0aGUgZGVjaW1hbCBwb2ludC4gVGhpcyBpcyBtb3JlIHByZWRpY3RhYmxlIHRoYW4gZm9ybWF0KCksIGJlY2F1c2UgdGhlIG51bWJlciBvZiBwbGFjZXMgYWZ0ZXIgdGhlIGRlY2ltYWwgaXMgZml4ZWQgcmVnYXJkbGVzcyBvZiB0aGUgdmFsdWVzIGJlaW5nIGZvcm1hdHRlZC4NCg0KZm9ybWF0QygpIGFsc28gZm9ybWF0cyBudW1iZXJzIGluZGl2aWR1YWxseSwgd2hpY2ggbWVhbnMgeW91IGFsd2F5cyBnZXQgdGhlIHNhbWUgb3V0cHV0IHJlZ2FyZGxlc3Mgb2Ygb3RoZXIgbnVtYmVycyBpbiB0aGUgdmVjdG9yLg0KDQpUaGUgZmxhZyBhcmd1bWVudCBhbGxvd3MgeW91IHRvIHByb3ZpZGUgc29tZSBtb2RpZmllcnMgdGhhdCwgZm9yIGV4YW1wbGUsIGZvcmNlIHRoZSBkaXNwbGF5IG9mIHRoZSBzaWduIChmbGFnID0gIisiKSwgbGVmdCBhbGlnbiBudW1iZXJzIChmbGFnID0gIi0iKSBhbmQgcGFkIG51bWJlcnMgd2l0aCBsZWFkaW5nIHplcm9zIChmbGFnID0gIjAiKS4NCmBgYHtyfQ0KIyBGcm9tIHRoZSBmb3JtYXQoKSBleGVyY2lzZQ0KeCA8LSBjKDAuMDAxMSwgMC4wMTEsIDEpDQp5IDwtIGMoMS4wMDExLCAyLjAxMSwgMSkNCg0KIyBmb3JtYXRDKCkgb24geCB3aXRoIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMQ0KZm9ybWF0Qyh4LCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDEpDQoNCiMgZm9ybWF0QygpIG9uIHkgd2l0aCBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDENCmZvcm1hdEMoeSwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAxKQ0KDQojIEZvcm1hdCBwZXJjZW50X2NoYW5nZSB0byBvbmUgcGxhY2UgYWZ0ZXIgdGhlIGRlY2ltYWwgcG9pbnQNCmZvcm1hdEMocGVyY2VudF9jaGFuZ2UsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gMSkNCg0KIyBwZXJjZW50X2NoYW5nZSB3aXRoIGZsYWcgPSAiKyINCmZvcm1hdEMocGVyY2VudF9jaGFuZ2UsICBmb3JtYXQgPSAiZiIsIGRpZ2l0cyA9IDEsIGZsYWcgPSAiKyIpDQoNCiMgRm9ybWF0IHBfdmFsdWVzIHVzaW5nIGZvcm1hdCA9ICJnIiBhbmQgZGlnaXRzID0gMg0KZm9ybWF0QyhwX3ZhbHVlcywgZm9ybWF0ID0gImciLCBkaWdpdHMgPSAyKQ0KYGBgDQojIyBQdXR0aW5nIHN0cmluZ3MgdG9nZXRoZXINCg0KIyMjIEFubm90YXRpb24gb2YgbnVtYmVycw0KDQpUbyBnZXQgYSBoYW5kbGUgb24gdXNpbmcgcGFzdGUoKSwgeW91IGFyZSBnb2luZyB0byBhbm5vdGF0ZSBzb21lIG9mIHlvdXIgZm9ybWF0dGVkIG51bWJlciBzdHJpbmdzLg0KDQpUaGUga2V5IHBvaW50cyB0byByZW1lbWJlciBhcmU6DQoNCi0JVGhlIHZlY3RvcnMgeW91IHBhc3MgdG8gcGFzdGUoKSBhcmUgcGFzdGVkIHRvZ2V0aGVyIGVsZW1lbnQgYnkgZWxlbWVudCwgdXNpbmcgdGhlIHNlcCBhcmd1bWVudCB0byBjb21iaW5lIHRoZW0uDQotCUlmIHRoZSB2ZWN0b3JzIHBhc3NlZCB0byBwYXN0ZSgpIGFyZW4ndCB0aGUgc2FtZSBsZW5ndGgsIHRoZSBzaG9ydGVyIHZlY3RvcnMgYXJlIHJlY3ljbGVkIHVwIHRvIHRoZSBsZW5ndGggb2YgdGhlIGxvbmdlc3Qgb25lLg0KLQlPbmx5IHVzZSBjb2xsYXBzZSBpZiB5b3Ugd2FudCBhIHNpbmdsZSBzdHJpbmcgYXMgb3V0cHV0LiBjb2xsYXBzZSBzcGVjaWZpZXMgdGhlIHN0cmluZyB0byBwbGFjZSBiZXR3ZWVuIGRpZmZlcmVudCBlbGVtZW50cy4NCmBgYHtyfQ0KeWVhcnMgPC0gMjAxMDoyMDEzDQpwcmV0dHlfcGVyY2VudCA8LSBmb3JtYXRDKHBlcmNlbnRfY2hhbmdlLCAgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSAxLCBmbGFnID0gIisiKQ0KIyBBZGQgJCB0byBwcmV0dHlfaW5jb21lDQpwYXN0ZSgiJCIsIHByZXR0eV9pbmNvbWUsIHNlcCA9ICIiKQ0KIA0KIyBBZGQgJSB0byBwcmV0dHlfcGVyY2VudA0KcGFzdGUocHJldHR5X3BlcmNlbnQsICIlIiwgc2VwID0gIiIpDQoNCiMgQ3JlYXRlIHZlY3RvciB3aXRoIGVsZW1lbnRzIGxpa2UgMjAxMDogKzQuMCVgDQp5ZWFyX3BlcmNlbnQgPC0gcGFzdGUoeWVhcnMsICI6ICIsIHByZXR0eV9wZXJjZW50LCAiJSIsIHNlcCA9ICIiKQ0KDQojIENvbGxhcHNlIGFsbCB5ZWFycyBpbnRvIHNpbmdsZSBzdHJpbmcNCnBhc3RlKHllYXJfcGVyY2VudCwgY29sbGFwc2UgPSAiLCIpDQpgYGANClNwZWNpZnlpbmcgc2VwID0gIiIgaXMgc28gY29tbW9uLCB0aGVyZSBpcyBhY3R1YWxseSBhbm90aGVyIGZ1bmN0aW9uIHBhc3RlMCgpIHRoYXQgd29ya3MgbGlrZSBwYXN0ZSgpIGJ1dCBhbHdheXMgcGFzdGVzIGVsZW1lbnRzIHRvZ2V0aGVyIHdpdGhvdXQgYSBzZXBhcmF0b3IgYmV0d2VlbiB0aGVtLg0KDQojIyMgQSB2ZXJ5IHNpbXBsZSB0YWJsZQ0KDQpDb21iaW5pbmcgZm9ybWF0KCkgYW5kIHBhc3RlKCkgaXMgb25lIHdheSB0byBkaXNwbGF5IHZlcnkgc2ltcGxlIHRhYmxlcy4gUmVtZW1iZXIsIHNpbmNlIGZvcm1hdCgpIGxvb2tzIGF0IGFsbCB0aGUgdmFsdWVzIGluIGEgdmVjdG9yIGJlZm9yZSBmb3JtYXR0aW5nLCBpdCB1c2VzIGEgY29uc2lzdGVudCBmb3JtYXQgYW5kIHdpbGwsIGJ5IGRlZmF1bHQsIGFsaWduIG9uIHRoZSBkZWNpbWFsIHBvaW50LiBUaGlzIGlzIHVzdWFsbHkgdGhlIGJlaGF2aW9yIHlvdSB3YW50IGZvciBhIGNvbHVtbiBvZiBudW1iZXJzIGluIHRhYmxlLg0KDQpmb3JtYXQoKSBjYW4gYWxzbyB0YWtlIGNoYXJhY3RlciB2ZWN0b3JzIGFzIGlucHV0LiBJbiB0aGlzIGNhc2UsIHlvdSBjYW4gdXNlIHRoZSBqdXN0aWZ5IGFyZ3VtZW50LCBzcGVjaWZpYyB0byBjaGFyYWN0ZXIgaW5wdXQsIHRvIGp1c3RpZnkgdGhlIHRleHQgdG8gdGhlIGxlZnQsIHJpZ2h0LCBvciBjZW50ZXIuDQpgYGB7cn0NCiMgRGVmaW5lIHRoZSBuYW1lcyB2ZWN0b3INCmluY29tZV9uYW1lcyA8LSBjKCJZZWFyIDAiLCAiWWVhciAxIiwgIlllYXIgMiIsICJQcm9qZWN0IExpZmV0aW1lIikNCg0KIyBDcmVhdGUgcHJldHR5X2luY29tZQ0KcHJldHR5X2luY29tZSA8LSBmb3JtYXQoaW5jb21lLCBkaWdpdHMgPSAyLCBiaWcubWFyayA9ICIsIikNCg0KIyBDcmVhdGUgZG9sbGFyX2luY29tZQ0KZG9sbGFyX2luY29tZSA8LSBwYXN0ZSgiJCIsIHByZXR0eV9pbmNvbWUsIHNlcCA9ICIiKQ0KDQojIENyZWF0ZSBmb3JtYXR0ZWRfbmFtZXMNCmZvcm1hdHRlZF9uYW1lcyA8LSBmb3JtYXQoaW5jb21lX25hbWVzLCBqdXN0aWZ5ID0gInJpZ2h0IikNCg0KIyBDcmVhdGUgcm93cw0Kcm93cyA8LSBwYXN0ZShmb3JtYXR0ZWRfbmFtZXMsIGRvbGxhcl9pbmNvbWUsIHNlcCA9ICIgICAiKQ0KDQojIFdyaXRlIHJvd3MNCndyaXRlTGluZXMocm93cykNCmBgYA0KSWYgeW91IHdhbnRlZCB0aGUgZG9sbGFyIHNpZ25zIHJpZ2h0IG5leHQgdG8gdGhlIG51bWJlcnMsIHlvdSBjb3VsZCBmb3JtYXQgdGhlIGluY29tZXMgd2l0aCB0cmltID0gVFJVRSwgcGFzdGUgb24gdGhlICQsIHRoZW4gZm9ybWF0IGFnYWluIGFzIGEgc3RyaW5nIHdpdGgganVzdGlmeSA9ICJyaWdodCIuDQoNCiMjIyBMZXQncyBvcmRlciBwaXp6YSENCg0KQXMgYSBmaW5hbCBleGVyY2lzZSBpbiB1c2luZyBwYXN0ZSgpIGFuZCB0byBjZWxlYnJhdGUgZ2V0dGluZyB0byB0aGUgZW5kIG9mIHRoZSBmaXJzdCBjaGFwdGVyLCBsZXQncyBvcmRlciBzb21lIHBpenphLg0KDQpXZSd2ZSBnb3QgYSBsaXN0IG9mIHBvc3NpYmxlIHBpenphIHRvcHBpbmdzIGluIHRvcHBpbmdzLg0KDQpZb3UgYXJlIGdvaW5nIHRvIHJhbmRvbWx5IHNlbGVjdCB0aHJlZSB0b3BwaW5ncywgYW5kIHRoZW4gcHV0IHRoZW0gdG9nZXRoZXIgdXNpbmcgcGFzdGUoKSBpbnRvIGFuIG9yZGVyIGZvciBwaXp6YSwgdGhhdCBzaG91bGQgcmVzdWx0IGluIGEgc3RyaW5nIGxpa2UsDQoiSSB3YW50IHRvIG9yZGVyIGEgcGl6emEgd2l0aCBtdXNocm9vbXMsIHNwaW5hY2gsIGFuZCBwaW5lYXBwbGUuIg0KYGBge3J9DQp0b3BwaW5ncyA8LSBjKCJhbmNob3ZpZXMiLCAiYXJ0aWNob2tlIiwgImJhY29uIiwgImJyZWFrZmFzdCBiYWNvbiIsICJDYW5hZGlhbiBiYWNvbiIsIA0KImNoZWVzZSIsICJjaGlja2VuIiwgImNoaWxpIHBlcHBlcnMiLCAiZmV0YSIsICJnYXJsaWMiLCAiZ3JlZW4gcGVwcGVycyIsIA0KImdyaWxsZWQgb25pb25zIiwgImdyb3VuZCBiZWVmIiwgImhhbSIsICJob3Qgc2F1Y2UiLCAibWVhdGJhbGxzIiwgDQoibXVzaHJvb21zIiwgIm9saXZlcyIsICJvbmlvbnMiLCAicGVwcGVyb25pIiwgInBpbmVhcHBsZSIsICJzYXVzYWdlIiwgDQoic3BpbmFjaCIsICJzdW4tZHJpZWQgdG9tYXRvIiwgInRvbWF0b2VzIikNCmBgYA0KDQpgYGB7cn0NCiMgUmFuZG9tbHkgc2FtcGxlIDMgdG9wcGluZ3MNCm15X3RvcHBpbmdzIDwtIHNhbXBsZSh0b3BwaW5ncywgc2l6ZSA9IDMpDQoNCiMgUHJpbnQgbXlfdG9wcGluZ3MNCm15X3RvcHBpbmdzDQoNCiMgUGFzdGUgImFuZCAiIHRvIGxhc3QgZWxlbWVudDogbXlfdG9wcGluZ3NfYW5kDQpteV90b3BwaW5nc19hbmQgPC0gcGFzdGUoYygiIiwgIiIsICJhbmQgIiksIG15X3RvcHBpbmdzLCBzZXAgPSAiIikNCg0KIyBDb2xsYXBzZSB3aXRoIGNvbW1hIHNwYWNlOiB0aGVzZV90b3BwaW5ncw0KdGhlc2VfdG9wcGluZ3MgPC0gcGFzdGUobXlfdG9wcGluZ3NfYW5kLCBjb2xsYXBzZSA9ICIsICIpDQoNCiMgQWRkIHJlc3Qgb2Ygc2VudGVuY2U6IG15X29yZGVyDQpteV9vcmRlciA8LSBwYXN0ZSgiSSB3YW50IHRvIG9yZGVyIGEgcGl6emEgd2l0aCAiLCB0aGVzZV90b3BwaW5ncywgIi4iLCBzZXAgPSAiIikNCg0KIyBPcmRlciBwaXp6YSB3aXRoIHdyaXRlTGluZXMoKQ0Kd3JpdGVMaW5lcyhteV9vcmRlcikNCmBgYA0KIyBzdHJpbmdyDQoNCiMjIEludHJvZHVjdGlvbg0KDQojIyMgUHV0dGluZyBzdHJpbmdzIHRvZ2V0aGVyIHdpdGggc3RyaW5ncg0KDQpGb3IgeW91ciBmaXJzdCBzdHJpbmdyIGZ1bmN0aW9uLCB3ZSdsbCBsb29rIGF0IHN0cl9jKCksIHRoZSBjIGlzIHNob3J0IGZvciBjb25jYXRlbmF0ZSwgYSBmdW5jdGlvbiB0aGF0IHdvcmtzIGxpa2UgcGFzdGUoKS4gSXQgdGFrZXMgdmVjdG9ycyBvZiBzdHJpbmdzIGFzIGlucHV0IGFsb25nIHdpdGggc2VwIGFuZCBjb2xsYXBzZSBhcmd1bWVudHMuDQoNClRoZXJlIGFyZSB0d28ga2V5IHdheXMgc3RyX2MoKSBkaWZmZXJzIGZyb20gcGFzdGUoKS4gRmlyc3QsIHRoZSBkZWZhdWx0IHNlcGFyYXRvciBpcyBhbiBlbXB0eSBzdHJpbmcsIHNlcCA9ICIiLCBhcyBvcHBvc2VkIHRvIGEgc3BhY2UsIHNvIGl0J3MgbW9yZSBsaWtlIHBhc3RlMCgpLiBUaGlzIGlzIGFuIGV4YW1wbGUgb2YgYSBzdHJpbmdyIGZ1bmN0aW9uLCBwZXJmb3JtaW5nIGEgc2ltaWxhciBvcGVyYXRpb24gdG8gYSBiYXNlIGZ1bmN0aW9uLCBidXQgdXNpbmcgYSBkZWZhdWx0IHRoYXQgaXMgbW9yZSBsaWtlbHkgdG8gYmUgd2hhdCB5b3Ugd2FudC4gUmVtZW1iZXIgaW4geW91ciBwaXp6YSBvcmRlciwgeW91IGhhZCB0byBzZXQgc2VwID0gIiIgbXVsdGlwbGUgdGltZXMuDQoNClRoZSBzZWNvbmQgd2F5IHN0cl9jKCkgZGlmZmVycyB0byBwYXN0ZSgpIGlzIGluIGl0cyBoYW5kbGluZyBvZiBtaXNzaW5nIHZhbHVlcy4gcGFzdGUoKSB0dXJucyBtaXNzaW5nIHZhbHVlcyBpbnRvIHRoZSBzdHJpbmcgIk5BIiwgd2hlcmVhcyBzdHJfYygpIHByb3BhZ2F0ZXMgbWlzc2luZyB2YWx1ZXMuIFRoYXQgbWVhbnMgY29tYmluaW5nIGFueSBzdHJpbmdzIHdpdGggYSBtaXNzaW5nIHZhbHVlIHdpbGwgcmVzdWx0IGluIGFub3RoZXIgbWlzc2luZyB2YWx1ZS4NCmBgYHtyfQ0KbGlicmFyeShzdHJpbmdyKQ0KDQpteV90b3BwaW5ncyA8LSBjKCJjaGVlc2UiLCBOQSwgTkEpDQpteV90b3BwaW5nc19hbmQgPC0gcGFzdGUoYygiIiwgIiIsICJhbmQgIiksIG15X3RvcHBpbmdzLCBzZXAgPSAiIikNCg0KIyBQcmludCBteV90b3BwaW5nc19hbmQNCm15X3RvcHBpbmdzX2FuZA0KDQojIFVzZSBzdHJfYygpIGluc3RlYWQgb2YgcGFzdGUoKTogbXlfdG9wcGluZ3Nfc3RyDQpteV90b3BwaW5nc19zdHIgPC0gc3RyX2MoYygiIiwgIiIsICJhbmQgIiksIG15X3RvcHBpbmdzKQ0KDQojIFByaW50IG15X3RvcHBpbmdzX3N0cg0KbXlfdG9wcGluZ3Nfc3RyDQoNCiMgcGFzdGUoKSBteV90b3BwaW5nc19hbmQgd2l0aCBjb2xsYXBzZSA9ICIsICINCnBhc3RlKG15X3RvcHBpbmdzX2FuZCwgY29sbGFwc2UgPSAiLCAiKQ0KDQojIHN0cl9jKCkgbXlfdG9wcGluZ3Nfc3RyIHdpdGggY29sbGFwc2UgPSAiLCAiDQpzdHJfYyhteV90b3BwaW5nc19zdHIsIGNvbGxhcHNlID0gIiwgIikNCmBgYA0KVGhpcyBiZWhhdmlvciBpcyBuaWNlIGJlY2F1c2UgeW91IGxlYXJuIHF1aWNrbHkgd2hlbiB5b3UgbWlnaHQgaGF2ZSBtaXNzaW5nIHZhbHVlcywgcmF0aGVyIHRoYW4gZGlzY292ZXJpbmcgbGF0ZXIgd2VpcmQgIk5BInMgaW5zaWRlIHlvdXIgc3RyaW5ncy4gQW5vdGhlciBzdHJpbmdyIGZ1bmN0aW9uIHRoYXQgaXMgdXNlZnVsIHdoZW4geW91IG1heSBoYXZlIG1pc3NpbmcgdmFsdWVzLCBpcyBzdHJfcmVwbGFjZV9uYSgpIHdoaWNoIHJlcGxhY2VzIG1pc3NpbmcgdmFsdWVzIHdpdGggYW55IHN0cmluZyB5b3UgY2hvb3NlLg0KDQojIyMgU3RyaW5nIGxlbmd0aA0KDQpPdXIgbmV4dCBzdHJpbmdyIGZ1bmN0aW9uIGlzIHN0cl9sZW5ndGgoKS4gc3RyX2xlbmd0aCgpIHRha2VzIGEgdmVjdG9yIG9mIHN0cmluZ3MgYXMgaW5wdXQgYW5kIHJldHVybnMgdGhlIG51bWJlciBvZiBjaGFyYWN0ZXJzIGluIGVhY2ggc3RyaW5nLiBGb3IgZXhhbXBsZSwgdHJ5IGZpbmRpbmcgdGhlIG51bWJlciBvZiBjaGFyYWN0ZXJzIGluIEJhdG1hbidzIG5hbWU6DQoNCmBgYHtyfQ0Kc3RyX2xlbmd0aChjKCJCcnVjZSIsICJXYXluZSIpKQ0KYGBgDQoNClRoaXMgaXMgdmVyeSBzaW1pbGFyIHRvIHRoZSBiYXNlIGZ1bmN0aW9uIG5jaGFyKCkgYnV0IHlvdSdsbCBzZWUgaW4gdGhlIGV4ZXJjaXNlcyBzdHJfbGVuZ3RoKCkgaGFuZGxlcyBmYWN0b3JzIGluIGFuIGludHVpdGl2ZSB3YXksIHdoZXJlYXMgbmNoYXIoKSB3aWxsIGp1c3QgcmV0dXJuIGFuIGVycm9yLg0KDQpIaXN0b3JpY2FsbHksIG5jaGFyKCkgd2FzIGV2ZW4gd29yc2UsIHJhdGhlciB0aGFuIHJldHVybmluZyBhbiBlcnJvciBpZiB5b3UgcGFzc2VkIGl0IGEgZmFjdG9yLCBpdCB3b3VsZCByZXR1cm4gdGhlIG51bWJlciBvZiBjaGFyYWN0ZXJzIGluIHRoZSBudW1lcmljIGVuY29kaW5nIG9mIHRoZSBmYWN0b3IuIFRoYW5rZnVsbHkgdGhpcyBiZWhhdmlvciBoYXMgYmVlbiBmaXhlZCwgYnV0IGl0IHdhcyBvbmUgb2YgdGhlIG9yaWdpbmFsIG1vdGl2YXRpb25zIGJlaGluZCBzdHJfbGVuZ3RoKCkuDQoNClRha2UgeW91ciBmaXJzdCBsb29rIGF0IGJhYnluYW1lcyBieSBhc2tpbmcgaWYgZ2lybHMnIG5hbWVzIGFyZSBsb25nZXIgdGhhbiBib3lzJyBuYW1lcy4NCmBgYHtyfQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShiYWJ5bmFtZXMpDQpsaWJyYXJ5KGRwbHlyKQ0KDQojIEV4dHJhY3RpbmcgdmVjdG9ycyBmb3IgYm95cycgYW5kIGdpcmxzJyBuYW1lcw0KYmFieW5hbWVzXzIwMTQgPC0gZmlsdGVyKGJhYnluYW1lcywgeWVhciA9PSAyMDE0KQ0KYm95X25hbWVzIDwtIGZpbHRlcihiYWJ5bmFtZXNfMjAxNCwgc2V4ID09ICJNIikkbmFtZQ0KZ2lybF9uYW1lcyA8LSBmaWx0ZXIoYmFieW5hbWVzXzIwMTQsIHNleCA9PSAiRiIpJG5hbWUNCg0KIyBUYWtlIGEgbG9vayBhdCBhIGZldyBib3lfbmFtZXMNCmhlYWQoYm95X25hbWVzKQ0KDQojIEZpbmQgdGhlIGxlbmd0aCBvZiBhbGwgYm95X25hbWVzDQpib3lfbGVuZ3RoIDwtIHN0cl9sZW5ndGgoYm95X25hbWVzKQ0KDQojIFRha2UgYSBsb29rIGF0IGEgZmV3IGxlbmd0aHMNCmhlYWQoYm95X2xlbmd0aCkNCg0KIyBGaW5kIHRoZSBsZW5ndGggb2YgYWxsIGdpcmxfbmFtZXMNCmdpcmxfbGVuZ3RoIDwtIHN0cl9sZW5ndGgoZ2lybF9uYW1lcykNCg0KIyBGaW5kIHRoZSBkaWZmZXJlbmNlIGluIG1lYW4gbGVuZ3RoDQptZWFuKGdpcmxfbGVuZ3RoKSAtIG1lYW4oYm95X2xlbmd0aCkNCg0KIyBDb25maXJtIHN0cl9sZW5ndGgoKSB3b3JrcyB3aXRoIGZhY3RvcnMNCmhlYWQoc3RyX2xlbmd0aChmYWN0b3IoYm95X25hbWVzKSkpDQpgYGANCg0KIyMjIEV4dHJhY3Rpbmcgc3Vic3RyaW5ncw0KDQpUaGUgc3RyX3N1YigpIGZ1bmN0aW9uIGluIHN0cmluZ3IgZXh0cmFjdHMgcGFydHMgb2Ygc3RyaW5ncyBiYXNlZCBvbiB0aGVpciBsb2NhdGlvbi4gQXMgd2l0aCBhbGwgc3RyaW5nciBmdW5jdGlvbnMsIHRoZSBmaXJzdCBhcmd1bWVudCwgc3RyaW5nLCBpcyBhIHZlY3RvciBvZiBzdHJpbmdzLiBUaGUgYXJndW1lbnRzIHN0YXJ0IGFuZCBlbmQgc3BlY2lmeSB0aGUgYm91bmRhcmllcyBvZiB0aGUgcGllY2UgdG8gZXh0cmFjdCBpbiBjaGFyYWN0ZXJzLg0KDQpGb3IgZXhhbXBsZSwgc3RyX3N1Yih4LCAxLCA0KSBhc2tzIGZvciB0aGUgc3Vic3RyaW5nIHN0YXJ0aW5nIGF0IHRoZSBmaXJzdCBjaGFyYWN0ZXIsIHVwIHRvIHRoZSBmb3VydGggY2hhcmFjdGVyLCBvciBpbiBvdGhlciB3b3JkcyB0aGUgZmlyc3QgZm91ciBjaGFyYWN0ZXJzLiBUcnkgaXQgd2l0aCBteSBCYXRtYW4ncyBuYW1lOg0KDQpgYGB7cn0NCnN0cl9zdWIoYygiQnJ1Y2UiLCAiV2F5bmUiKSwgMSwgNCkNCmBgYA0KDQpCb3RoIHN0YXJ0IGFuZCBlbmQgY2FuIGJlIG5lZ2F0aXZlIGludGVnZXJzLCBpbiB3aGljaCBjYXNlLCB0aGV5IGNvdW50IGZyb20gdGhlIGVuZCBvZiB0aGUgc3RyaW5nLiBGb3IgZXhhbXBsZSwgc3RyX3N1Yih4LCAtNCwgLTEpLCBhc2tzIGZvciB0aGUgc3Vic3RyaW5nIHN0YXJ0aW5nIGF0IHRoZSBmb3VydGggY2hhcmFjdGVyIGZyb20gdGhlIGVuZCwgdXAgdG8gdGhlIGZpcnN0IGNoYXJhY3RlciBmcm9tIHRoZSBlbmQsIGkuZS4gdGhlIGxhc3QgZm91ciBjaGFyYWN0ZXJzLiBBZ2FpbiwgdHJ5IGl0IHdpdGggQmF0bWFuOg0KYGBge3J9DQpzdHJfc3ViKGMoIkJydWNlIiwgIldheW5lIiksIC00LCAtMSkNCmBgYA0KYGBge3J9DQojIEV4dHJhY3QgZmlyc3QgbGV0dGVyIGZyb20gYm95X25hbWVzDQpib3lfZmlyc3RfbGV0dGVyIDwtIHN0cl9zdWIoYm95X25hbWVzLCAxLCAxKQ0KDQojIFRhYnVsYXRlIG9jY3VycmVuY2VzIG9mIGJveV9maXJzdF9sZXR0ZXINCnRhYmxlKGJveV9maXJzdF9sZXR0ZXIpDQogIA0KIyBFeHRyYWN0IHRoZSBsYXN0IGxldHRlciBpbiBib3lfbmFtZXMsIHRoZW4gdGFidWxhdGUNCmJveV9sYXN0X2xldHRlciA8LSBzdHJfc3ViKGJveV9uYW1lcywgLTEsIC0xKQ0KdGFibGUoYm95X2xhc3RfbGV0dGVyKQ0KDQojIEV4dHJhY3QgdGhlIGZpcnN0IGxldHRlciBpbiBnaXJsX25hbWVzLCB0aGVuIHRhYnVsYXRlDQpnaXJsX2ZpcnN0X2xldHRlciA8LSBzdHJfc3ViKGdpcmxfbmFtZXMsIDEsIDEpDQp0YWJsZShnaXJsX2ZpcnN0X2xldHRlcikNCg0KIyBFeHRyYWN0IHRoZSBsYXN0IGxldHRlciBpbiBnaXJsX25hbWVzLCB0aGVuIHRhYnVsYXRlDQpnaXJsX2xhc3RfbGV0dGVyIDwtIHN0cl9zdWIoZ2lybF9uYW1lcywgLTEsIC0xKQ0KdGFibGUoZ2lybF9sYXN0X2xldHRlcikNCmBgYA0KRGlkIHlvdSBzZWUgdGhhdCAiQSIgaXMgdGhlIG1vc3QgcG9wdWxhciBmaXJzdCBsZXR0ZXIgZm9yIGJvdGggYm95cyBhbmQgZ2lybHMsIGFuZCB0aGUgbW9zdCBwb3B1bGFyIGxhc3QgbGV0dGVyIGZvciBnaXJscy4gSG93ZXZlciwgdGhlIG1vc3QgcG9wdWxhciBsYXN0IGxldHRlciBmb3IgYm95cycgbmFtZXMgd2FzICJuIi4gWW91IG1pZ2h0IGhhdmUgc2VlbiBzdWJzdHIoKSBhIGJhc2UgUiBmdW5jdGlvbiB0aGF0IGlzIHNpbWlsYXIgdG8gc3RyX3N1YigpLiBUaGUgYmlnIGFkdmFudGFnZSBvZiBzdHJfc3ViKCkgaXMgdGhlIGFiaWxpdHkgdG8gdXNlIG5lZ2F0aXZlIGluZGV4ZXMgdG8gY291bnQgZnJvbSB0aGUgZW5kIG9mIGEgc3RyaW5nDQoNCiMjIEh1bnRpbmcgZm9yIG1hdGNoZXMNCg0KIyMjIERldGVjdGluZyBtYXRjaGVzDQoNCnN0cl9kZXRlY3QoKSBpcyB1c2VkIHRvIGFuc3dlciB0aGUgcXVlc3Rpb246IERvZXMgdGhlIHN0cmluZyBjb250YWluIHRoZSBwYXR0ZXJuPyBJdCByZXR1cm5zIGEgbG9naWNhbCB2ZWN0b3Igb2YgdGhlIHNhbWUgbGVuZ3RoIGFzIHRoYXQgb2YgdGhlIGlucHV0IHZlY3RvciBzdHJpbmcsIHdpdGggVFJVRSBmb3IgZWxlbWVudHMgdGhhdCBjb250YWluIHRoZSBwYXR0ZXJuIGFuZCBGQUxTRSBvdGhlcndpc2UuDQoNCkxldCdzIHRha2UgYSBsb29rIGF0IGEgc2ltcGxlIGV4YW1wbGUgd2hlcmUgeW91IGhhdmUgYSB2ZWN0b3Igb2Ygc3RyaW5ncyB0aGF0IHJlcHJlc2VudCBwaXp6YSBvcmRlcnM6DQoNCg0KYGBge3J9DQpwaXp6YXMgPC0gYygiY2hlZXNlIiwgInBlcHBlcm9uaSIsIA0KICAic2F1c2FnZSBhbmQgZ3JlZW4gcGVwcGVycyIpDQpgYGANCiAgDQpZb3UgY2FuIGFzayB3aGljaCBvcmRlcnMgY29udGFpbiB0aGUgcGF0dGVybiAicGVwcGVyIiwgd2l0aA0KYGBge3J9DQpzdHJfZGV0ZWN0KHBpenphcywgDQogIHBhdHRlcm4gPSBmaXhlZCgicGVwcGVyIikpDQpgYGANCk5vdGljZSBob3cgYm90aCBwZXBwZXJvbmkgYW5kIGdyZWVuIHBlcHBlcnMgY29udGFpbiB0aGUgcGF0dGVybiBvZiBpbnRlcmVzdC4NCg0KVGhlIG91dHB1dCBmcm9tIHN0cl9kZXRlY3QoKSBjYW4gYmUgdXNlZCB0byBjb3VudCB0aGUgbnVtYmVyIG9mIG9jY3VycmVuY2VzLCBvciB0byBzdWJzZXQgMG91dCB0aGUgc3RyaW5ncyB0aGF0IGNvbnRhaW4gdGhlIHBhdHRlcm4uDQpgYGB7cn0NCiMgTG9vayBmb3IgcGF0dGVybiAienoiIGluIGJveV9uYW1lcw0KY29udGFpbnNfenogPC0gc3RyX2RldGVjdChib3lfbmFtZXMsIHBhdHRlcm4gPSBmaXhlZCgienoiKSkNCg0KIyBFeGFtaW5lIHN0cigpIG9mIGNvbnRhaW5zX3p6DQpzdHIoY29udGFpbnNfenopDQoNCiMgSG93IG1hbnkgbmFtZXMgY29udGFpbiAienoiPw0Kc3VtKGNvbnRhaW5zX3p6KQ0KDQojIFdoaWNoIG5hbWVzIGNvbnRhaW4gInp6Ij8NCmJveV9uYW1lc1tjb250YWluc196el0NCg0KIyBXaGljaCByb3dzIGluIGJveV9kZiBoYXZlIG5hbWVzIHRoYXQgY29udGFpbiAienoiPw0KYm95X2RmIDwtIGRhdGEuZnJhbWUoYm95X25hbWVzKQ0KYm95X2RmW2NvbnRhaW5zX3p6LF0NCmBgYA0KVGhhdCBsYXN0IGV4YW1wbGUgaXMgYW5vdGhlciBjb21tb24gdXNlIG9mIHN0cl9kZXRlY3QoKSBzdWJzZXR0aW5nIGEgZGF0YSBmcmFtZSB0byByb3dzIHdoZXJlIHRoZSB2YWx1ZXMgaW4gYSBjb2x1bW4gY29udGFpbiB0aGUgcGF0dGVybiBvZiBpbnRlcmVzdC4gSW4gdGhpcyBjYXNlIGl0IGxldHMgdXMgc2VlIHRoZXNlIGRvdWJsZS16IG5hbWVzIGFyZSBwcmV0dHkgcmFyZS4gRm9yIGV4YW1wbGUsIGV2ZW4gdGhlIG1vc3QgcG9wdWxhciwgVXp6aWFoLCBvbmx5IGFjY291bnRlZCBmb3IgMC4wMDMlIG9mIGJveXMgYm9ybiBpbiAyMDE0Lg0KDQojIyMgU3Vic2V0dGluZyBzdHJpbmdzIGJhc2VkIG9uIG1hdGNoDQoNClNpbmNlIGRldGVjdGluZyBzdHJpbmdzIHdpdGggYSBwYXR0ZXJuIGFuZCB0aGVuIHN1YnNldHRpbmcgb3V0IHRob3NlIHN0cmluZ3MgaXMgc3VjaCBhIGNvbW1vbiBvcGVyYXRpb24sIHN0cmluZ3IgcHJvdmlkZXMgYSBmdW5jdGlvbiBzdHJfc3Vic2V0KCkgdGhhdCBkb2VzIHRoYXQgaW4gb25lIHN0ZXAuDQoNCkZvciBleGFtcGxlLCBsZXQncyByZXBlYXQgb3VyIHNlYXJjaCBmb3IgInBlcHBlciIgaW4gb3VyIHBpenphcyB1c2luZyBzdHJfc3Vic2V0KCk6DQpgYGB7cn0NCnBpenphcyA8LSBjKCJjaGVlc2UiLCAicGVwcGVyb25pIiwgInNhdXNhZ2UgYW5kIGdyZWVuIHBlcHBlcnMiKQ0Kc3RyX3N1YnNldChwaXp6YXMsIHBhdHRlcm4gPSBmaXhlZCgicGVwcGVyIikpDQpgYGANCldlIGdldCBhIG5ldyB2ZWN0b3Igb2Ygc3RyaW5ncywgYnV0IGl0IG9ubHkgY29udGFpbnMgdGhvc2Ugb3JpZ2luYWwgc3RyaW5ncyB0aGF0IGNvbnRhaW5lZCB0aGUgcGF0dGVybi4NCg0Kc3RyX3N1YnNldCgpIGNhbiBiZSBlYXNpbHkgY29uZnVzZWQgd2l0aCBzdHJfZXh0cmFjdCgpLiBzdHJfZXh0cmFjdCgpIHJldHVybnMgYSB2ZWN0b3Igb2YgdGhlIHNhbWUgbGVuZ3RoIGFzIHRoYXQgb2YgdGhlIGlucHV0IHZlY3RvciwgYnV0IHdpdGggb25seSB0aGUgcGFydHMgb2YgdGhlIHN0cmluZ3MgdGhhdCBtYXRjaGVkIHRoZSBwYXR0ZXJuLg0KYGBge3J9DQojIEZpbmQgYm95X25hbWVzIHRoYXQgY29udGFpbiAienoiDQpzdHJfc3Vic2V0KGJveV9uYW1lcywgcGF0dGVybiA9IGZpeGVkKCJ6eiIpKQ0KDQojIEZpbmQgZ2lybF9uYW1lcyB0aGF0IGNvbnRhaW4gInp6Ig0Kc3RyX3N1YnNldChnaXJsX25hbWVzLCBwYXR0ZXJuID0gZml4ZWQoInp6IikpDQoNCiMgRmluZCBnaXJsX25hbWVzIHRoYXQgY29udGFpbiAiVSINCnN0YXJ0c19VIDwtIHN0cl9zdWJzZXQoZ2lybF9uYW1lcywgcGF0dGVybiA9IGZpeGVkKCJVIikpDQpzdGFydHNfVQ0KDQojIEZpbmQgZ2lybF9uYW1lcyB0aGF0IGNvbnRhaW4gIlUiIGFuZCAieiINCnN0cl9zdWJzZXQoc3RhcnRzX1UsIHBhdHRlcm4gPSBmaXhlZCgieiIpKQ0KYGBgDQojIyMgQ291bnRpbmcgbWF0Y2hlcw0KDQpBbm90aGVyIHN0cmluZ3IgZnVuY3Rpb24gdGhhdCB0YWtlcyBhIHZlY3RvciBvZiBzdHJpbmdzIGFuZCBhIHBhdHRlcm4gaXMgc3RyX2NvdW50KCkuIHN0cl9jb3VudCgpIGFuc3dlcnMgdGhlIHF1ZXN0aW9uICJIb3cgbWFueSB0aW1lcyBkb2VzIHRoZSBwYXR0ZXJuIG9jY3VyIGluIGVhY2ggc3RyaW5nPyIuIEl0IGFsd2F5cyByZXR1cm5zIGFuIGludGVnZXIgdmVjdG9yIG9mIHRoZSBzYW1lIGxlbmd0aCBhcyB0aGF0IG9mIHRoZSBpbnB1dCB2ZWN0b3IuDQoNCklmIHlvdSBjb3VudCB0aGUgb2NjdXJyZW5jZXMgb2YgInBlcHBlciIgaW4geW91ciBwaXp6YXMsIHlvdSdsbCBmaW5kIG5vIG9jY3VycmVuY2VzIGluIHRoZSBmaXJzdCwgYW5kIG9uZSBlYWNoIGluIHRoZSBzZWNvbmQgYW5kIHRoaXJkLA0KYGBge3J9DQpwaXp6YXMgPC0gYygiY2hlZXNlIiwgInBlcHBlcm9uaSIsIA0KICAic2F1c2FnZSBhbmQgZ3JlZW4gcGVwcGVycyIpDQpzdHJfY291bnQocGl6emFzLCBwYXR0ZXJuID0gZml4ZWQoInBlcHBlciIpKQ0KYGBgDQoNClBlcmhhcHMgYSBsaXR0bGUgbW9yZSBpbnRlcmVzdGluZyBpcyB0byBjb3VudCBob3cgbWFueSAiZSJzIG9jY3VyIGluIGVhY2ggb3JkZXINCmBgYHtyfQ0Kc3RyX2NvdW50KHBpenphcywgcGF0dGVybiA9IGZpeGVkKCJlIikpDQpgYGANCmBgYHtyfQ0KIyBDb3VudCBvY2N1cnJlbmNlcyBvZiAiYSIgaW4gZ2lybF9uYW1lcw0KbnVtYmVyX2FzIDwtIHN0cl9jb3VudChnaXJsX25hbWVzLCBwYXR0ZXJuID0gZml4ZWQoImEiKSkNCg0KIyBDb3VudCBvY2N1cnJlbmNlcyBvZiAiQSIgaW4gZ2lybF9uYW1lcw0KbnVtYmVyX0FzIDwtIHN0cl9jb3VudChnaXJsX25hbWVzLCBwYXR0ZXJuID0gZml4ZWQoIkEiKSkNCg0KIyBIaXN0b2dyYW1zIG9mIG51bWJlcl9hcyBhbmQgbnVtYmVyX0FzDQpoaXN0KG51bWJlcl9hcykNCmhpc3QobnVtYmVyX0FzKSAgDQoNCiMgRmluZCB0b3RhbCAiYSIgKyAiQSINCnRvdGFsX2FzIDwtIG51bWJlcl9hcyArICAgbnVtYmVyX0FzDQoNCiMgZ2lybF9uYW1lcyB3aXRoIG1vcmUgdGhhbiA0IGEncw0KZ2lybF9uYW1lc1t0b3RhbF9hcyA+IDRdDQpgYGANCiMjIFNwbGl0dGluZyBzdHJpbmdzDQoNCiMjIyBQYXJzaW5nIHN0cmluZ3MgaW50byB2YXJpYWJsZXMNCg0KQSBjb21tb24gdXNlIGZvciBzdHJfc3BsaXQoKSBpcyB0byBwdWxsIGFwYXJ0IHJhdyBzdHJpbmcgZGF0YSBpbnRvIG1vcmUgdXNlZnVsIHZhcmlhYmxlcy4gSW4gdGhpcyBleGVyY2lzZSB5b3UnbGwgc3RhcnQgYnkgcHVsbGluZyBhcGFydCBhIGRhdGUgcmFuZ2UsIHNvbWV0aGluZyBsaWtlICIyMy4wMS4yMDE3IC0gMjkuMDEuMjAxNyIsIGludG8gc2VwYXJhdGUgdmFyaWFibGVzIGZvciB0aGUgc3RhcnQgb2YgdGhlIHJhbmdlLCAiMjMuMDEuMjAxNyIsIGFuZCB0aGUgZW5kIG9mIHRoZSByYW5nZSwgIjI5LjAxLjIwMTciLg0KDQpSZW1lbWJlciwgaWYgdGhlIHNpbXBsaWZ5IGFyZ3VtZW50IGlzIEZBTFNFICh0aGUgZGVmYXVsdCkgeW91J2xsIGdldCBiYWNrIGEgbGlzdCBvZiB0aGUgc2FtZSBsZW5ndGggYXMgdGhhdCBvZiB0aGUgaW5wdXQgdmVjdG9yLiBNb3JlIGNvbW1vbmx5LCB5b3UnbGwgd2FudCB0byBwdWxsIG91dCB0aGUgZmlyc3QgcGllY2UgKG9yIHNlY29uZCBwaWVjZSBldGMuKSBmcm9tIGV2ZXJ5IGVsZW1lbnQsIHdoaWNoIGlzIGVhc2llciBpZiB5b3Ugc3BlY2lmeSBzaW1wbGlmeSA9IFRSVUUgYW5kIGdldCBhIG1hdHJpeCBhcyBvdXRwdXQuIFlvdSdsbCBleHBsb3JlIGJvdGggb2YgdGhlc2Ugb3V0cHV0IHR5cGVzIGluIHRoaXMgZXhlcmNpc2UuDQoNCmBgYHtyfQ0KIyBTb21lIGRhdGUgZGF0YQ0KZGF0ZV9yYW5nZXMgPC0gYygiMjMuMDEuMjAxNyAtIDI5LjAxLjIwMTciLCAiMzAuMDEuMjAxNyAtIDA2LjAyLjIwMTciKQ0KDQojIFNwbGl0IGRhdGVzIHVzaW5nICIgLSAiDQpzcGxpdF9kYXRlcyA8LSBzdHJfc3BsaXQoZGF0ZV9yYW5nZXMsIHBhdHRlcm4gPSBmaXhlZCgiIC0gIikpDQpzcGxpdF9kYXRlcw0KIyBTcGxpdCBkYXRlcyB3aXRoIG4gYW5kIHNpbXBsaWZ5IHNwZWNpZmllZA0Kc3BsaXRfZGF0ZXNfbiA8LSBzdHJfc3BsaXQoZGF0ZV9yYW5nZXMsIHBhdHRlcm4gPSBmaXhlZCgiIC0gIiksIHNpbXBsaWZ5ID0gVFJVRSwgbiA9IDIpDQpzcGxpdF9kYXRlc19uDQoNCnNwbGl0X2RhdGVzX24gPC0gc3RyX3NwbGl0KGRhdGVfcmFuZ2VzLCBmaXhlZCgiIC0gIiksIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpDQoNCiMgU3Vic2V0IHNwbGl0X2RhdGVzX24gaW50byBzdGFydF9kYXRlcyBhbmQgZW5kX2RhdGVzDQpzdGFydF9kYXRlcyA8LSBzcGxpdF9kYXRlc19uWywxXQ0KDQojIFNwbGl0IHN0YXJ0X2RhdGVzIGludG8gZGF5LCBtb250aCBhbmQgeWVhciBwaWVjZXMNCnN0cl9zcGxpdChzdGFydF9kYXRlcywgZml4ZWQoIi4iKSwgc2ltcGxpZnkgPSBUUlVFKQ0KYm90aF9uYW1lcyA8LSBjKCJCb3gsIEdlb3JnZSIsICJDb3gsIERhdmlkIikNCg0KIyBTcGxpdCBib3RoX25hbWVzIGludG8gZmlyc3RfbmFtZXMgYW5kIGxhc3RfbmFtZXMNCmJvdGhfbmFtZXNfc3BsaXQgPC0gc3RyX3NwbGl0KGJvdGhfbmFtZXMsIGZpeGVkKCIsICIpLCBuID0gMiwgc2ltcGxpZnkgPSBUUlVFKQ0KDQojIEdldCBmaXJzdCBuYW1lcw0KZmlyc3RfbmFtZXMgPC0gYm90aF9uYW1lc19zcGxpdFssIDJdDQoNCiMgR2V0IGxhc3QgbmFtZXMNCmxhc3RfbmFtZXMgPC0gYm90aF9uYW1lc19zcGxpdFssIDFdDQpgYGANCiMjIyBTb21lIHNpbXBsZSB0ZXh0IHN0YXRpc3RpY3MNCg0KR2VuZXJhbGx5LCBzcGVjaWZ5aW5nIHNpbXBsaWZ5ID0gVFJVRSB3aWxsIGdpdmUgeW91IG91dHB1dCB0aGF0IGlzIGVhc2llciB0byB3b3JrIHdpdGgsIGJ1dCB5b3UnbGwgYWx3YXlzIGdldCBuIHBpZWNlcyAoZXZlbiBpZiBzb21lIGFyZSBlbXB0eSwgIiIpLg0KDQpTb21ldGltZXMsIHlvdSB3YW50IHRvIGtub3cgaG93IG1hbnkgcGllY2VzIGEgc3RyaW5nIGNhbiBiZSBzcGxpdCBpbnRvLCBvciB5b3Ugd2FudCB0byBkbyBzb21ldGhpbmcgd2l0aCBldmVyeSBwaWVjZSBiZWZvcmUgbW92aW5nIHRvIGEgc2ltcGxlciBzdHJ1Y3R1cmUuIFRoaXMgaXMgYSBzaXR1YXRpb24gd2hlcmUgeW91IGRvbid0IHdhbnQgdG8gc2ltcGxpZnkgYW5kIHlvdSdsbCBoYXZlIHRvIHByb2Nlc3MgdGhlIG91dHB1dCB3aXRoIHNvbWV0aGluZyBsaWtlIGxhcHBseSgpLg0KDQpBcyBhbiBleGFtcGxlLCB5b3UnbGwgYmUgcGVyZm9ybWluZyBzb21lIHNpbXBsZSB0ZXh0IHN0YXRpc3RpY3Mgb24geW91ciBsaW5lcyBmcm9tIEFsaWNlJ3MgQWR2ZW50dXJlcyBpbiBXb25kZXJsYW5kIGZyb20gQ2hhcHRlciAxLiBZb3VyIGdvYWwgd2lsbCBiZSB0byBjYWxjdWxhdGUgaG93IG1hbnkgd29yZHMgYXJlIGluIGVhY2ggbGluZSwgYW5kIHRoZSBhdmVyYWdlIGxlbmd0aCBvZiB3b3JkcyBpbiBlYWNoIGxpbmUuDQoNClRvIGRvIHRoZXNlIGNhbGN1bGF0aW9ucywgeW91J2xsIG5lZWQgdG8gc3BsaXQgdGhlIGxpbmVzIGludG8gd29yZHMuIE9uZSB3YXkgdG8gYnJlYWsgYSBzZW50ZW5jZSBpbnRvIHdvcmRzIGlzIHRvIHNwbGl0IG9uIGFuIGVtcHR5IHNwYWNlICIgIi4gVGhpcyBpcyBhIGxpdHRsZSBuYWl2ZSBiZWNhdXNlLCBmb3IgZXhhbXBsZSwgaXQgd291bGRuJ3QgcGljayB1cCB3b3JkcyBzZXBhcmF0ZWQgYnkgYSBuZXdsaW5lIGVzY2FwZSBzZXF1ZW5jZSBsaWtlIGluICJ0d29cXG53b3JkcyIsIGJ1dCBzaW5jZSB0aGlzIHNpdHVhdGlvbiBkb2Vzbid0IG9jY3VyIGluIHlvdXIgbGluZXMsIGl0IHdpbGwgZG8uDQpgYGB7cn0NCiMgU3BsaXQgbGluZXMgaW50byB3b3Jkcw0Kd29yZHMgPC0gc3RyX3NwbGl0KGxpbmVzLCBmaXhlZCgiICIpLCBzaW1wbGlmeSA9IEZBTFNFKQ0KDQojIE51bWJlciBvZiB3b3JkcyBwZXIgbGluZQ0KbGFwcGx5KHdvcmRzLCBsZW5ndGgpDQogIA0KIyBOdW1iZXIgb2YgY2hhcmFjdGVycyBpbiBlYWNoIHdvcmQNCndvcmRfbGVuZ3RocyA8LSBsYXBwbHkod29yZHMsIHN0cl9sZW5ndGgpDQogIA0KIyBBdmVyYWdlIHdvcmQgbGVuZ3RoIHBlciBsaW5lDQpsYXBwbHkod29yZF9sZW5ndGhzLCBtZWFuKQ0KYGBgDQpUaGUgd29yZCBsZW5ndGhzIGFyZW4ndCBxdWl0ZSByaWdodCBiZWNhdXNlIHlvdSB3ZXJlIGluY2x1ZGluZyBzb21lIHB1bmN0dWF0aW9uIHN5bWJvbHMuIE9uZSB3YXkgdG8gZGVhbCB3aXRoIHRoYXQgaXMgdG8gcmVwbGFjZSB0aGVtIGZpcnN0IHdpdGggc3RyX3JlcGxhY2UoKS4NCg0KIyMgUmVwbGFjaW5nIG1hdGNoZXMgaW4gc3RyaW5ncw0KDQojIyMgUmVwbGFjaW5nIHRvIHRpZHkgc3RyaW5ncw0KDQpZb3UndmUgc2VlbiBvbmUgY29tbW9uIHN0cmF0ZWd5IHRvIHB1bGwgdmFyaWFibGVzIG91dCBvZiBzdHJpbmdzIGlzIHRvIHNwbGl0IHRoZSBzdHJpbmcgYmFzZWQgb24gYSBwYXR0ZXJuLiBTb21ldGltZXMsIGl0J3MgZWFzaWVyIHRvIGp1c3QgcmVwbGFjZSB0aGUgcGFydHMgeW91IGRvbid0IHdhbnQgd2l0aCBhbiBlbXB0eSBzdHJpbmcgIiIuIFRoaXMgaXMgYWxzbyBhIGNvbW1vbiBzdHJhdGVneSB0byBjbGVhbiBzdHJpbmdzIHVwLCBmb3IgZXhhbXBsZSwgdG8gcmVtb3ZlIHVud2FudGVkIHB1bmN0dWF0aW9uIG9yIHdoaXRlIHNwYWNlLg0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSdsbCBwdWxsIG91dCBzb21lIG51bWJlcnMgYnkgcmVwbGFjaW5nIHRoZSBwYXJ0IG9mIHRoZSBzdHJpbmcgdGhhdCBpc24ndCBhIG51bWJlciwgeW91J2xsIGFsc28gcGxheSB3aXRoIHRoZSBmb3JtYXQgb2Ygc29tZSBwaG9uZSBudW1iZXJzLiBQYXkgY2xvc2UgYXR0ZW50aW9uIHRvIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gc3RyX3JlcGxhY2UoKSBhbmQgc3RyX3JlcGxhY2VfYWxsKCkuDQpgYGB7cn0NCiMgU29tZSBJRHMNCmlkcyA8LSBjKCJJRCM6IDE5MiIsICJJRCM6IDExOCIsICJJRCM6IDAwMSIpDQoNCiMgUmVwbGFjZSAiSUQjOiAiIHdpdGggIiINCmlkX251bXMgPC0gc3RyX3JlcGxhY2UoaWRzLCAiSUQjOiAiLCAiIikNCg0KIyBUdXJuIGlkX251bXMgaW50byBudW1iZXJzDQppZF9pbnRzIDwtIGFzLm51bWVyaWMoaWRfbnVtcykNCiMgU29tZSAoZmFrZSkgcGhvbmUgbnVtYmVycw0KcGhvbmVfbnVtYmVycyA8LSBjKCI1MTAtNTU1LTAxMjMiLCAiNTQxLTU1NS0wMTY3IikNCg0KIyBVc2Ugc3RyX3JlcGxhY2UoKSB0byByZXBsYWNlICItIiB3aXRoICIgIg0Kc3RyX3JlcGxhY2UocGhvbmVfbnVtYmVycywgIi0iLCAiICIpDQoNCiMgVXNlIHN0cl9yZXBsYWNlX2FsbCgpIHRvIHJlcGxhY2UgIi0iIHdpdGggIiAiDQpzdHJfcmVwbGFjZV9hbGwocGhvbmVfbnVtYmVycywgIi0iLCAiICIpDQoNCiMgVHVybiBwaG9uZSBudW1iZXJzIGludG8gdGhlIGZvcm1hdCB4eHgueHh4Lnh4eHgNCnN0cl9yZXBsYWNlX2FsbChwaG9uZV9udW1iZXJzLCAiLSIsICIuIikNCmBgYA0KUmV2aWV3DQpZb3UndmUgY292ZXJlZCBhIGxvdCBvZiBzdHJpbmdyIGZ1bmN0aW9ucyBpbiB0aGlzIGNoYXB0ZXI6DQoNCi0gc3RyX2MoKQ0KLSBzdHJfbGVuZ3RoKCkNCi0gc3RyX3N1YigpDQotIHN0cl9kZXRlY3QoKQ0KLSBzdHJfc3Vic2V0KCkNCi0gc3RyX2NvdW50KCkNCi0gc3RyX3NwbGl0KCkNCi0gc3RyX3JlcGxhY2UoKQ0KDQpBcyBhIHJldmlldyB3ZSd2ZSBnb3QgYSBmZXcgdGFza3MgZm9yIHlvdSB0byBkbyB3aXRoIHNvbWUgRE5BIHNlcXVlbmNlcy4gV2UndmUgcHV0IHRocmVlIHNlcXVlbmNlcywgY29ycmVzcG9uZGluZyB0byB0aHJlZSBnZW5lcywgZnJvbSB0aGUgZ2Vub21lIG9mIFllcnNpbmlhIHBlc3RpcyDigJMgdGhlIGJhY3RlcmlhIHRoYXQgY2F1c2VzIGJ1Ym9uaWMgcGxhZ3VlIOKAkyBpbnRvIHRoZSB2ZWN0b3IgZ2VuZXMuDQoNCkVhY2ggc3RyaW5nIHJlcHJlc2VudHMgYSBnZW5lLCBlYWNoIGNoYXJhY3RlciBhIHBhcnRpY3VsYXIgbnVjbGVvdGlkZTogQWRlbmluZSwgQ3l0b3NpbmUsIEd1YW5pbmUgb3IgVGh5bWluZS4NCg0KYGBge3J9DQpnZW5lcyA8LSByZWFkUkRTKCJkbmEucmRzIikNCmBgYA0KYGBge3J9DQojIEZpbmQgdGhlIG51bWJlciBvZiBudWNsZW90aWRlcyBpbiBlYWNoIHNlcXVlbmNlDQpzdHJfbGVuZ3RoKGdlbmVzKQ0KDQojIEZpbmQgdGhlIG51bWJlciBvZiBBJ3Mgb2NjdXIgaW4gZWFjaCBzZXF1ZW5jZQ0Kc3RyX2NvdW50KGdlbmVzLCBmaXhlZCgiQSIpKQ0KDQojIFJldHVybiB0aGUgc2VxdWVuY2VzIHRoYXQgY29udGFpbiAiVFRUVFRUIg0Kc3RyX3N1YnNldChnZW5lcywgZml4ZWQoIlRUVFRUVCIpKQ0KDQojIFJlcGxhY2UgYWxsIHRoZSAiQSJzIGluIHRoZSBzZXF1ZW5jZXMgd2l0aCBhICJfIg0Kc3RyX3JlcGxhY2VfYWxsKGdlbmVzLCBwYXR0ZXJuID0gZml4ZWQoIkEiKSwgcmVwbGFjZW1lbnQgPSAiXyIpDQpgYGANCiMjIyBGaW5hbCBjaGFsbGVuZ2VzDQoNCkJ5IGNvbWJpbmluZyBtdWx0aXBsZSBvcGVyYXRpb25zIHRvZ2V0aGVyIGluIHNlcXVlbmNlIHlvdSBjYW4gYWNoaWV2ZSBxdWl0ZSBjb21wbGljYXRlZCBtYW5pcHVsYXRpb25zLg0KDQpBcyB0aGUgZmluYWwgZXhlcmNpc2Ugd2Ugd2FudCB0byBleHBvc2UgeW91IHRvIHRoZSBwb3dlciBvZiBjb21iaW5pbmcgb3BlcmF0aW9ucy4gWW91J2xsIGNvbXBsZXRlIHR3byB0YXNrczoNCg0KMS4JWW91J2xsIHR1cm4gYSB2ZWN0b3Igb2YgZnVsbCBuYW1lcywgbGlrZSAiQnJ1Y2UgV2F5bmUiLCBpbnRvIGFiYnJldmlhdGVkIG5hbWVzIGxpa2UgIkIuIFdheW5lIi4gVGhpcyByZXF1aXJlcyBjb21iaW5pbmcgc3RyX3NwbGl0KCksIHN0cl9zdWIoKSBhbmQgc3RyX2MoKS4NCjIuCVlvdSdsbCBjb21wYXJlIGhvdyBtYW55IGJveSBuYW1lcyBlbmQgaW4gImVlIiBjb21wYXJlZCB0byBnaXJsIG5hbWVzLiBUaGlzIHJlcXVpcmVzIGNvbWJpbmluZyBzdHJfc3ViKCkgd2l0aCBzdHJfZGV0ZWN0KCkgYWxvbmcgd2l0aCB0aGUgYmFzZSBmdW5jdGlvbiB0YWJsZSgpLg0KYGBge3J9DQojIERlZmluZSBzb21lIGZ1bGwgbmFtZXMNCm5hbWVzIDwtIGMoIkRpYW5hIFByaW5jZSIsICJDbGFyayBLZW50IikNCg0KIyBTcGxpdCBpbnRvIGZpcnN0IGFuZCBsYXN0IG5hbWVzDQpuYW1lc19zcGxpdCA8LSBzdHJfc3BsaXQobmFtZXMsIGZpeGVkKCIgIiksIHNpbXBsaWZ5ID0gVFJVRSkNCg0KIyBFeHRyYWN0IHRoZSBmaXJzdCBsZXR0ZXIgaW4gdGhlIGZpcnN0IG5hbWUNCmFiYl9maXJzdCA8LSBzdHJfc3ViKG5hbWVzX3NwbGl0WywxXSwgMSwgMSkNCg0KIyBDb21iaW5lIHRoZSBmaXJzdCBsZXR0ZXIgIi4gIiBhbmQgbGFzdCBuYW1lDQpzdHJfYyhhYmJfZmlyc3QsICIuICIsIG5hbWVzX3NwbGl0WywyXSkNCiMgVXNlIGFsbCBuYW1lcyBpbiBiYWJ5bmFtZXNfMjAxNA0KYWxsX25hbWVzIDwtIGJhYnluYW1lc18yMDE0JG5hbWUNCg0KIyBHZXQgdGhlIGxhc3QgdHdvIGxldHRlcnMgb2YgYWxsX25hbWVzDQpsYXN0X3R3b19sZXR0ZXJzIDwtIHN0cl9zdWIoYWxsX25hbWVzLCAtMiwgLTEpDQojIERvZXMgdGhlIG5hbWUgZW5kIGluICJlZSI/DQplbmRzX2luX2VlIDwtIHN0cl9kZXRlY3QobGFzdF90d29fbGV0dGVycywgZml4ZWQoImVlIikpDQoNCiMgRXh0cmFjdCByb3dzIGFuZCAic2V4IiBjb2x1bW4NCnNleCA8LSBiYWJ5bmFtZXNfMjAxNCRzZXhbZW5kc19pbl9lZV0NCg0KIyBEaXNwbGF5IHJlc3VsdCBhcyBhIHRhYmxlDQp0YWJsZShzZXgpDQpgYGANCiMgUGF0dGVybiBtYXRjaGluZyB3aXRoIHJlZ3VsYXIgZXhwcmVzc2lvbnMNCg0KIyMgUmVndWxhciBleHByZXNzaW9ucw0KDQojIyMgTWF0Y2hpbmcgdGhlIHN0YXJ0IG9yIGVuZCBvZiB0aGUgc3RyaW5nDQoNCnJlYnVzIHByb3ZpZGVzIFNUQVJUIGFuZCBFTkQgc2hvcnRjdXRzIHRvIHNwZWNpZnkgcmVndWxhciBleHByZXNzaW9ucyB0aGF0IG1hdGNoIHRoZSBzdGFydCBhbmQgZW5kIG9mIHRoZSBzdHJpbmcuIFRoZXNlIGFyZSBhbHNvIGtub3duIGFzIGFuY2hvcnMuIFlvdSBjYW4gdHJ5IGl0IG91dCBqdXN0IGJ5IHR5cGluZw0KDQpTVEFSVA0KWW91J2xsIHNlZSB0aGUgb3V0cHV0IDxyZWdleD4gXi4gVGhlIDxyZWdleD4gZGVub3RlcyB0aGlzIGlzIGEgc3BlY2lhbCByZWdleCBvYmplY3QgYW5kIGl0IGhhcyB0aGUgdmFsdWUgXi4gXiBpcyB0aGUgY2hhcmFjdGVyIHVzZWQgaW4gdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBsYW5ndWFnZSB0byBkZW5vdGUgdGhlIHN0YXJ0IG9mIGEgc3RyaW5nLg0KDQpUaGUgc3BlY2lhbCBvcGVyYXRvciBwcm92aWRlZCBieSByZWJ1cywgJVIlIGFsbG93cyB5b3UgdG8gY29tcG9zZSBjb21wbGljYXRlZCByZWd1bGFyIGV4cHJlc3Npb25zIGZyb20gc2ltcGxlIHBpZWNlcy4gV2hlbiB5b3UgYXJlIHJlYWRpbmcgcmVidXMgY29kZSwgdGhpbmsgb2YgJVIlIGFzICJ0aGVuIi4gRm9yIGV4YW1wbGUsIHlvdSBjb3VsZCBjb21iaW5lIFNUQVJUIHdpdGggYywNCg0KICAgIFNUQVJUICVSJSAiYyINCg0KdG8gbWF0Y2ggdGhlIHBhdHRlcm4gInRoZSBzdGFydCBvZiBzdHJpbmcgdGhlbiBhIGMiLCBvciBpbiBvdGhlciB3b3Jkczogc3RyaW5ncyB0aGF0IHN0YXJ0IHdpdGggYy4gSW4gcmVidXMsIGlmIHlvdSB3YW50IHRvIG1hdGNoIGEgc3BlY2lmaWMgY2hhcmFjdGVyLCBvciBhIHNwZWNpZmljIHNlcXVlbmNlIG9mIGNoYXJhY3RlcnMsIHlvdSBzaW1wbHkgc3BlY2lmeSB0aGVtIGFzIGEgc3RyaW5nLCBlLmcuIHN1cnJvdW5kIHRoZW0gd2l0aCAiLg0KYGBge3J9DQpsaWJyYXJ5KHJlYnVzKQ0KIyBTb21lIHN0cmluZ3MgdG8gcHJhY3RpY2Ugd2l0aA0KeCA8LSBjKCJjYXQiLCAiY29hdCIsICJzY290bGFuZCIsICJ0aWMgdG9jIikNCg0KIyBQcmludCBFTkQNCkVORA0KDQojIFJ1biBtZQ0Kc3RyX3ZpZXcoeCwgcGF0dGVybiA9IFNUQVJUICVSJSAiYyIpDQpgYGANCmBgYHtyfQ0KIyBNYXRjaCB0aGUgc3RyaW5ncyB0aGF0IHN0YXJ0IHdpdGggImNvIiANCnN0cl92aWV3KHgsIHBhdHRlcm4gPSBTVEFSVCAlUiUgImNvIikNCmBgYA0KYGBge3J9DQojIE1hdGNoIHRoZSBzdHJpbmdzIHRoYXQgZW5kIHdpdGggImF0Ig0Kc3RyX3ZpZXcoeCwgcGF0dGVybiA9ICJhdCIgJVIlIEVORCkNCiMgTWF0Y2ggdGhlIHN0cmluZyB0aGF0IGlzIGV4YWN0bHkgImNhdCINCnN0cl92aWV3KHgsIHBhdHRlcm4gPSBTVEFSVCAlUiUgImNhdCIgJVIlIEVORCkNCmBgYA0KDQpvciB0aGF0IGxhc3QgZXhhbXBsZSwgcmVidXMgYWxzbyBwcm92aWRlcyB0aGUgZnVuY3Rpb24gZXhhY3RseSh4KSB3aGljaCBpcyBhIHNob3J0Y3V0IGZvciBTVEFSVCAlUiUgeCAlUiUgRU5EIHRoYXQgbWF0Y2hlcyBvbmx5IGlmIHRoZSBzdHJpbmcgaXMgZXhhY3RseSB4Lg0KDQojIyMgTWF0Y2hpbmcgYW55IGNoYXJhY3Rlcg0KDQpJbiBhIHJlZ3VsYXIgZXhwcmVzc2lvbiB5b3UgY2FuIHVzZSBhIHdpbGRjYXJkIHRvIG1hdGNoIGEgc2luZ2xlIGNoYXJhY3Rlciwgbm8gbWF0dGVyIHdoYXQgdGhlIGNoYXJhY3RlciBpcy4gSW4gcmVidXMgaXQgaXMgc3BlY2lmaWVkIHdpdGggQU5ZX0NIQVIuIFRyeSB0eXBpbmcgQU5ZX0NIQVIgaW4gdGhlIGNvbnNvbGUuIFlvdSBzaG91bGQgc2VlIHRoYXQgaW4gdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiBsYW5ndWFnZSB0aGlzIGlzIHNwZWNpZmllZCBieSBhIGRvdCwgLi4NCg0KRm9yIGV4YW1wbGUsICJjIiAlUiUgQU5ZX0NIQVIgJVIlICJ0IiB3aWxsIGxvb2sgZm9yIHBhdHRlcm5zIGxpa2UgImNfdCIgd2hlcmUgdGhlIGJsYW5rIGNhbiBiZSBhbnkgY2hhcmFjdGVyLiBDb25zaWRlciB0aGUgc3RyaW5nczogImNhdCIsICJjb2F0IiwgInNjb3RsYW5kIiBhbmQgInRpYyB0b2MiLiBXaGVyZSB3b3VsZCB0aGUgbWF0Y2hlcyB0byAiYyIgJVIlIEFOWV9DSEFSICVSJSAidCIgYmU/DQoNClRlc3QgeW91ciBpbnR1aXRpb24gYnkgcnVubmluZzoNCmBgYHtyfQ0Kc3RyX3ZpZXcoYygiY2F0IiwgImNvYXQiLCAic2NvdGxhbmQiLCAidGljIHRvYyIpLCANCiAgcGF0dGVybiA9ICJjIiAlUiUgQU5ZX0NIQVIgJVIlICJ0IikNCmBgYA0KDQpOb3RpY2UgdGhhdCBBTllfQ0hBUiB3aWxsIG1hdGNoIGEgc3BhY2UgY2hhcmFjdGVyIChjIHQgaW4gdGljIHRvYykuIEl0IHdpbGwgYWxzbyBtYXRjaCBudW1iZXJzIG9yIHB1bmN0dWF0aW9uIHN5bWJvbHMsIGJ1dCBBTllfQ0hBUiB3aWxsIG9ubHkgZXZlciBtYXRjaCBvbmUgY2hhcmFjdGVyLCB3aGljaCBpcyB3aHkgd2UgZ2V0IG5vIG1hdGNoIGluIGNvYXQuDQpgYGB7cn0NCiMgTWF0Y2ggdHdvIGNoYXJhY3RlcnMsIHdoZXJlIHRoZSBzZWNvbmQgaXMgYSAidCINCnN0cl92aWV3KHgsIHBhdHRlcm4gPSBBTllfQ0hBUiAlUiUgInQiKQ0KDQojIE1hdGNoIGEgInQiIGZvbGxvd2VkIGJ5IGFueSBjaGFyYWN0ZXINCnN0cl92aWV3KHgsIHBhdHRlcm4gPSAidCIgJVIlIEFOWV9DSEFSKQ0KDQojIE1hdGNoIHR3byBjaGFyYWN0ZXJzDQpzdHJfdmlldyh4LCBwYXR0ZXJuID0gQU5ZX0NIQVIgJVIlIEFOWV9DSEFSKQ0KDQojIE1hdGNoIGEgc3RyaW5nIHdpdGggZXhhY3RseSB0aHJlZSBjaGFyYWN0ZXJzDQpzdHJfdmlldyh4LCBwYXR0ZXJuID0gU1RBUlQgJVIlIEFOWV9DSEFSICVSJSBBTllfQ0hBUiAlUiUgQU5ZX0NIQVIgJVIlIEVORCkNCmBgYA0KIyMjIENvbWJpbmluZyB3aXRoIHN0cmluZ3IgZnVuY3Rpb25zDQoNCllvdSBjYW4gcGFzcyBhIHJlZ3VsYXIgZXhwcmVzc2lvbiBhcyB0aGUgcGF0dGVybiBhcmd1bWVudCB0byBhbnkgc3RyaW5nciBmdW5jdGlvbiB0aGF0IGhhcyB0aGUgcGF0dGVybiBhcmd1bWVudC4gWW91IGNhbiB1c2Ugc3RyX2RldGVjdCgpIHRvIGdldCBhIGxvZ2ljYWwgdmVjdG9yIGZvciB3aGV0aGVyIHRoZXJlIHdhcyBhIG1hdGNoLCBzdHJfc3Vic2V0KCkgdG8gcmV0dXJuIGp1c3QgdGhlIHN0cmluZ3Mgd2l0aCBtYXRjaGVzLCBhbmQgc3RyX2NvdW50KCkgdG8gY291bnQgdGhlIG51bWJlciBvZiBtYXRjaGVzIGluIGVhY2ggc3RyaW5nLg0KDQpBcyBhIHJlbWluZGVyLCBjb21wYXJlIHRoZSBvdXRwdXQgb2YgdGhvc2UgdGhyZWUgZnVuY3Rpb25zIHdpdGggb3VyICJjX3QiIHBhdHRlcm4gZnJvbSB0aGUgcHJldmlvdXMgZXhlcmNpc2U6DQpgYGB7cn0NCnggPC0gYygiY2F0IiwgImNvYXQiLCAic2NvdGxhbmQiLCAidGljIHRvYyIpDQpwYXR0ZXJuIDwtICJjIiAlUiUgQU5ZX0NIQVIgJVIlICJ0Ig0Kc3RyX2RldGVjdCh4LCBwYXR0ZXJuKQ0Kc3RyX3N1YnNldCh4LCBwYXR0ZXJuKQ0Kc3RyX2NvdW50KHgsIHBhdHRlcm4pDQpgYGANCkl0IG5vdyBhbHNvIG1ha2VzIHNlbnNlIHRvIGFkZCBzdHJfZXh0cmFjdCgpIHRvIHlvdXIgcmVwZXJ0b2lyZS4gSXQgcmV0dXJucyBqdXN0IHRoZSBwYXJ0IG9mIHRoZSBzdHJpbmcgdGhhdCBtYXRjaGVkIHRoZSBwYXR0ZXJuOg0KYGBge3J9DQpzdHJfZXh0cmFjdCh4LCBwYXR0ZXJuKQ0KYGBgDQpZb3UnbGwgY29tYmluZSB5b3VyIHJlZ3VsYXIgZXhwcmVzc2lvbiBza2lsbHMgd2l0aCBzdHJpbmdyIHRvIGFzayBob3cgb2Z0ZW4gYSBxIGlzIGZvbGxvd2VkIGJ5IGFueSBjaGFyYWN0ZXIgaW4gYm95IG5hbWVzLg0KDQpJdCdzIGFsd2F5cyBhIGdvb2QgaWRlYSB0byB0ZXN0IHlvdXIgcGF0dGVybiwgc28gdGhpcyBwYXR0ZXJuIGlzIHNob3duIG1hdGNoZWQgd2l0aCBmb3VyIG5hbWVzLiBUaGUgZmlyc3QgdHdvIHNob3VsZG4ndCBoYXZlIG1hdGNoZXMgKGNhbiB5b3UgZXhwbGFpbiB3aHk/KSBidXQgdGhlIGxhc3QgdHdvIHNob3VsZC4NCmBgYHtyfQ0KcGF0dGVybiA8LSAicSIgJVIlIEFOWV9DSEFSDQoNCiMgRmluZCBuYW1lcyB0aGF0IGhhdmUgdGhlIHBhdHRlcm4NCm5hbWVzX3dpdGhfcSA8LSBzdHJfc3Vic2V0KGJveV9uYW1lcywgcGF0dGVybikNCg0KIyBIb3cgbWFueSBuYW1lcyB3ZXJlIHRoZXJlPw0KbGVuZ3RoKG5hbWVzX3dpdGhfcSkNCiMgRmluZCBwYXJ0IG9mIG5hbWUgdGhhdCBtYXRjaGVzIHBhdHRlcm4NCnBhcnRfd2l0aF9xIDwtIHN0cl9leHRyYWN0KGJveV9uYW1lcywgcGF0dGVybikNCg0KIyBHZXQgYSB0YWJsZSBvZiBjb3VudHMNCnRhYmxlKHBhcnRfd2l0aF9xKQ0KIyBEaWQgYW55IG5hbWVzIGhhdmUgdGhlIHBhdHRlcm4gbW9yZSB0aGFuIG9uY2U/DQpjb3VudF9vZl9xIDwtIHN0cl9jb3VudChib3lfbmFtZXMsIHBhdHRlcm4pDQoNCiMgR2V0IGEgdGFibGUgb2YgY291bnRzDQp0YWJsZShjb3VudF9vZl9xKQ0KIyBXaGljaCBiYWJpZXMgZ290IHRoZXNlIG5hbWVzPw0Kd2l0aF9xIDwtIHN0cl9kZXRlY3QoYm95X25hbWVzLCBwYXR0ZXJuKQ0KDQojIFdoYXQgZnJhY3Rpb24gb2YgYmFiaWVzIGdvdCB0aGVzZSBuYW1lcz8NCm1lYW4od2l0aF9xKQ0KYGBgDQojIyBNb3JlIHJlZ3VsYXIgZXhwcmVzc2lvbnMNCg0KIyMjIEFsdGVybmF0aW9uDQoNClRoZSByZWJ1cyBmdW5jdGlvbiBvcigpIGFsbG93cyB1cyB0byBzcGVjaWZ5IGEgc2V0IG9mIGFsdGVybmF0aXZlcywgd2hpY2ggbWF5IGJlIHNpbmdsZSBjaGFyYWN0ZXJzIG9yIGNoYXJhY3RlciBzdHJpbmdzLCB0byBiZSBtYXRjaGVkLiBFYWNoIGFsdGVybmF0aXZlIGlzIHBhc3NlZCBhcyBhIHNlcGFyYXRlIGFyZ3VtZW50Lg0KDQpGb3IgZXhhbXBsZSwgb3IoImdyZXkiLCAiZ3JheSIpIGFsbG93cyB1cyB0byBkZXRlY3QgZWl0aGVyIHRoZSBBbWVyaWNhbiBvciBCcml0aXNoIHNwZWxsaW5nOg0KYGBge3J9DQp4IDwtIGMoImdyZXkgc2t5IiwgImdyYXkgZWxlcGhhbnQiKQ0Kc3RyX3ZpZXcoeCwgcGF0dGVybiA9IG9yKCJncmV5IiwgImdyYXkiKSkNCmBgYA0KU2luY2UgdGhlc2UgdHdvIHdvcmRzIG9ubHkgZGlmZmVyIGJ5IG9uZSBjaGFyYWN0ZXIgeW91IGNvdWxkIGVxdWl2YWxlbnRseSBzcGVjaWZ5IHRoaXMgbWF0Y2ggd2l0aCAiZ3IiICVSJSBvcigiZSIsICJhIikgJVIlICJ5IiwgdGhhdCBpcyAiYSBnciBmb2xsb3dlZCBieSwgYW4gZSBvciBhbiBhLCB0aGVuIGEgeSIuDQpgYGB7cn0NCiMgTWF0Y2ggSmVmZnJleSBvciBHZW9mZnJleQ0Kd2hvbGVfbmFtZXMgPC0gb3IoIkplZmZyZXkiLCAiR2VvZmZyZXkiKQ0Kc3RyX3ZpZXcoYm95X25hbWVzLCBwYXR0ZXJuID0gd2hvbGVfbmFtZXMsIG1hdGNoID0gVFJVRSkNCiMgTWF0Y2ggSmVmZnJleSBvciBHZW9mZnJleSwgYW5vdGhlciB3YXkNCmNvbW1vbl9lbmRpbmcgPC0gb3IoIkplIiwgIkdlbyIpICVSJSAiZmZyZXkiDQpzdHJfdmlldyhib3lfbmFtZXMsIHBhdHRlcm4gPSBjb21tb25fZW5kaW5nLCBtYXRjaCA9IFRSVUUpDQojIE1hdGNoIHdpdGggYWx0ZXJuYXRlIGVuZGluZ3MNCmJ5X3BhcnRzIDwtIG9yKCJKZSIsICJHZW8iKSAlUiUgImZmIiAlUiUgb3IgKCJyeSIsICJlcnkiLCAicmV5IiwgImVyZXkiKQ0Kc3RyX3ZpZXcoYm95X25hbWVzLCBwYXR0ZXJuID0gYnlfcGFydHMsIG1hdGNoID0gVFJVRSkNCiMgTWF0Y2ggbmFtZXMgdGhhdCBzdGFydCB3aXRoIENhdGggb3IgS2F0aA0KY2thdGggPC0gU1RBUlQgJVIlIG9yKCJDIiwgIksiKSAlUiUgImF0aCINCnN0cl92aWV3KGdpcmxfbmFtZXMsIHBhdHRlcm4gPSBja2F0aCwgbWF0Y2ggPSBUUlVFKQ0KYGBgDQojIyMgQ2hhcmFjdGVyIGNsYXNzZXMNCg0KSW4gcmVndWxhciBleHByZXNzaW9ucyBhIGNoYXJhY3RlciBjbGFzcyBpcyBhIHdheSBvZiBzcGVjaWZ5aW5nICJtYXRjaCBvbmUgKGFuZCBvbmx5IG9uZSkgb2YgdGhlIGZvbGxvd2luZyBjaGFyYWN0ZXJzIi4gSW4gcmVidXMgeW91IGNhbiBzcGVjaWZ5IHRoZSBzZXQgb2YgYWxsb3dhYmxlIGNoYXJhY3RlcnMgdXNpbmcgdGhlIGZ1bmN0aW9uIGNoYXJfY2xhc3MoKS4NCg0KVGhpcyBpcyBhbm90aGVyIHdheSB5b3UgY291bGQgc3BlY2lmeSBhbiBhbHRlcm5hdGUgc3BlbGxpbmcsIGZvciBleGFtcGxlLCBzcGVjaWZ5aW5nICJhIGdyIGZvbGxvd2VkIGJ5LCBlaXRoZXIgYW4gYSBvciBlLCBmb2xsb3dlZCBieSBhIHkiOg0KYGBge3J9DQp4IDwtIGMoImdyZXkgc2t5IiwgImdyYXkgZWxlcGhhbnQiKQ0Kc3RyX3ZpZXcoeCwgcGF0dGVybiA9ICJnciIgJVIlIGNoYXJfY2xhc3MoImFlIikgJVIlICJ5IikNCmBgYA0KQSBuZWdhdGVkIGNoYXJhY3RlciBjbGFzcyBtYXRjaGVzICJhbnkgc2luZ2xlIGNoYXJhY3RlciB0aGF0IGlzbid0IG9uZSBvZiB0aGUgZm9sbG93aW5nIiwgYW5kIGluIHJlYnVzIGlzIHNwZWNpZmllZCB3aXRoIG5lZ2F0ZWRfY2hhcl9jbGFzcygpLg0KDQpVbmxpa2UgaW4gb3RoZXIgcGxhY2VzIGluIGEgcmVndWxhciBleHByZXNzaW9uIHlvdSBkb24ndCBuZWVkIHRvIGVzY2FwZSBjaGFyYWN0ZXJzIHRoYXQgbWlnaHQgb3RoZXJ3aXNlIGhhdmUgYSBzcGVjaWFsIG1lYW5pbmcgaW5zaWRlIGNoYXJhY3RlciBjbGFzc2VzLiBJZiB5b3Ugd2FudCB0byBtYXRjaCAuIHlvdSBjYW4gaW5jbHVkZSAuIGRpcmVjdGx5LCBlLmcuIGNoYXJfY2xhc3MoIi4iKS4gTWF0Y2hpbmcgYSAtIGlzIGEgYml0IHRyaWNraWVyLiBJZiB5b3UgbmVlZCB0byBkbyBpdCwganVzdCBtYWtlIHN1cmUgaXQgY29tZXMgZmlyc3QgaW4gdGhlIGNoYXJhY3RlciBjbGFzcy4NCmBgYHtyfQ0KIyBDcmVhdGUgY2hhcmFjdGVyIGNsYXNzIGNvbnRhaW5pbmcgdm93ZWxzDQp2b3dlbHMgPC0gY2hhcl9jbGFzcygiYWVpb3VBRUlPVSIpDQoNCiMgUHJpbnQgdm93ZWxzDQp2b3dlbHMNCg0KIyBTZWUgdm93ZWxzIGluIHggd2l0aCBzdHJfdmlldygpDQpzdHJfdmlldyh4LCB2b3dlbHMpDQojIFNlZSB2b3dlbHMgaW4geCB3aXRoIHN0cl92aWV3X2FsbCgpDQpzdHJfdmlld19hbGwoeCwgdm93ZWxzKQ0KIyBOdW1iZXIgb2Ygdm93ZWxzIGluIGJveV9uYW1lcw0KbnVtX3Zvd2VscyA8LSBzdHJfY291bnQoYm95X25hbWVzLCB2b3dlbHMpDQoNCiMgTnVtYmVyIG9mIGNoYXJhY3RlcnMgaW4gYm95X25hbWVzDQpuYW1lX2xlbmd0aCA8LSBzdHJfbGVuZ3RoKGJveV9uYW1lcykNCiMgQ2FsYyBtZWFuIG51bWJlciBvZiB2b3dlbHMNCm1lYW4obnVtX3Zvd2VscykNCg0KIyBDYWxjIG1lYW4gZnJhY3Rpb24gb2Ygdm93ZWxzIHBlciBuYW1lDQptZWFuKG51bV92b3dlbHMgLyBuYW1lX2xlbmd0aCkNCmBgYA0KVGhlIG5hbWVzIGluIGJveV9uYW1lcyBhcmUgb24gYXZlcmFnZSBhYm91dCA0MCUgdm93ZWxzLg0KDQojIyMgUmVwZXRpdGlvbg0KDQpUaGUgcmVidXMgZnVuY3Rpb25zIG9uZV9vcl9tb3JlKCksIHplcm9fb3JfbW9yZSgpIGFuZCBvcHRpb25hbCgpIGNhbiBiZSB1c2VkIHRvIHdyYXAgcGFydHMgb2YgYSByZWd1bGFyIGV4cHJlc3Npb24gdG8gYWxsb3cgYSBwYXR0ZXJuIHRvIG1hdGNoIGEgdmFyaWFibGUgbnVtYmVyIG9mIHRpbWVzLg0KDQpUYWtlIG91ciB2b3dlbHMgcGF0dGVybiBmcm9tIHRoZSBsYXN0IGV4ZXJjaXNlLiBZb3UgY2FuIHBhc3MgaXQgdG8gb25lX29yX21vcmUoKSB0byBjcmVhdGUgdGhlIHBhdHRlcm4gdGhhdCBtYXRjaGVzICJvbmUgb3IgbW9yZSB2b3dlbHMiLiBUYWtlIGEgbG9vayB3aXRoIHRoZXNlIGludGVyamVjdGlvbnM6DQpgYGB7cn0NCnggPC0gYygib3ciLCAib29oIiwgInllZWVhaCEiLCAic2hoIikNCnN0cl92aWV3KHgsIHBhdHRlcm4gPSBvbmVfb3JfbW9yZSh2b3dlbHMpKQ0KYGBgDQpZb3UnbGwgc2VlIHdlIGNhbiBtYXRjaCB0aGUgc2luZ2xlIG8gaW4gb3csIHRoZSBkb3VibGUgbyBpbiBvb2ggYW5kIHRoZSBzdHJpbmcgb2YgZXMgZm9sbG93ZWQgYnkgdGhlIGEgaW4geWVlZWFoLCBidXQgbm90aGluZyBpbiBzaGggYmVjYXVzZSB0aGVyZSBpc24ndCBhIHNpbmdsZSB2b3dlbC4NCg0KSW4gY29udHJhc3QgemVyb19vcl9tb3JlKCkgd2lsbCBtYXRjaCBldmVuIGlmIHRoZXJlIGlzbid0IGFuIG9jY3VycmVuY2UsIHRyeQ0KYGBge3J9DQpzdHJfdmlldyh4LCBwYXR0ZXJuID0gemVyb19vcl9tb3JlKHZvd2VscykpDQpgYGANClNpbmNlIGJvdGggeWVlZWFoIGFuZCBzaGggc3RhcnQgd2l0aG91dCBhIHZvd2VsLCB0aGV5IG1hdGNoICJ6ZXJvIHZvd2VscyIsIGFuZCBzaW5jZSByZWd1bGFyIGV4cHJlc3Npb25zIGFyZSBsYXp5LCB0aGV5IGxvb2sgbm8gZnVydGhlciBhbmQgcmV0dXJuIHRoZSBzdGFydCBvZiB0aGUgc3RyaW5nIGFzIGEgbWF0Y2guDQpgYGB7cn0NCiMgVm93ZWxzIGZyb20gbGFzdCBleGVyY2lzZQ0Kdm93ZWxzIDwtIGNoYXJfY2xhc3MoImFlaW91QUVJT1UiKQ0KDQojIFNlZSBuYW1lcyB3aXRoIG9ubHkgdm93ZWxzDQpzdHJfdmlldyhib3lfbmFtZXMsIA0KICBwYXR0ZXJuID0gZXhhY3RseShvbmVfb3JfbW9yZSh2b3dlbHMpKSwgDQogIG1hdGNoID0gVFJVRSkNCiMgVXNlIGBuZWdhdGVkX2NoYXJfY2xhc3MoKWAgZm9yIGV2ZXJ5dGhpbmcgYnV0IHZvd2Vscw0Kbm90X3Zvd2VscyA8LSBuZWdhdGVkX2NoYXJfY2xhc3MoImFlaW91QUVJT1UiKQ0KDQojIFNlZSBuYW1lcyB3aXRoIG5vIHZvd2Vscw0Kc3RyX3ZpZXcoYm95X25hbWVzLCANCiAgcGF0dGVybiA9IGV4YWN0bHkob25lX29yX21vcmUobm90X3Zvd2VscykpLCANCiAgbWF0Y2ggPSBUUlVFKQ0KYGBgDQojIyBTaG9ydGN1dHMNCg0KIyMjIEh1bnRpbmcgZm9yIHBob25lIG51bWJlcnMNCg0KRm9yIHlvdXIgZmlyc3QgdGFzayB5b3UgYXJlIGdvaW5nIHRvIHB1bGwgb3V0IHRoZSBwaG9uZSBudW1iZXJzIGZyb20gdGhpcyB2ZWN0b3Igb2YgY29udGFjdCBpbmZvcm1hdGlvbjoNCg0KYGBge3J9DQpjb250YWN0IDwtIGMoIkNhbGwgbWUgYXQgNTU1LTU1NS0wMTkxIiwgIjEyMyBNYWluIFN0IiwgIig1NTUpIDU1NSAwMTkxIiwgDQoiUGhvbmU6IDU1NS41NTUuMDE5MSBNb2JpbGU6IDU1NS41NTUuMDE5MiIpDQpgYGANCg0KDQpZb3UnbGwgYXNzdW1lIHRoZSBwaG9uZSBudW1iZXJzIHlvdSBhcmUgbG9va2luZyBmb3IgZm9sbG93IHRoZSBBbWVyaWNhbiBzdGFuZGFyZCBvZiBhIHRocmVlIGRpZ2l0IGFyZWEgY29kZSwgYSB0aHJlZSBkaWdpdCBleGNoYW5nZSBhbmQgdGhlbiBhIGZvdXIgZGlnaXQgbnVtYmVyLCBidXQgZWFjaCBwYXJ0IGNvdWxkIGJlIHNlcGFyYXRlZCBieSBzcGFjZXMgb3IgdmFyaW91cyBwdW5jdHVhdGlvbiBzeW1ib2xzLg0KYGBge3J9DQojIENyZWF0ZSBhIHRocmVlIGRpZ2l0IHBhdHRlcm4NCnRocmVlX2RpZ2l0cyA8LSBER1QgJVIlIERHVCAlUiUgREdUIA0KDQojIFRlc3QgaXQNCnN0cl92aWV3X2FsbChjb250YWN0LCBwYXR0ZXJuID0gdGhyZWVfZGlnaXRzKQ0KIyBDcmVhdGUgYSBzZXBhcmF0b3IgcGF0dGVybg0Kc2VwYXJhdG9yIDwtIGNoYXJfY2xhc3MoIi0gLiAoICkgIikNCg0KIyBUZXN0IGl0DQpzdHJfdmlld19hbGwoY29udGFjdCwgcGF0dGVybiA9IHNlcGFyYXRvcikNCiMgVXNlIHRoZXNlIGNvbXBvbmVudHMNCnRocmVlX2RpZ2l0cyA8LSBER1QgJVIlIERHVCAlUiUgREdUDQpmb3VyX2RpZ2l0cyA8LSB0aHJlZV9kaWdpdHMgJVIlIERHVA0Kc2VwYXJhdG9yIDwtIGNoYXJfY2xhc3MoIi0uKCkgIikNCg0KIyBDcmVhdGUgcGhvbmUgcGF0dGVybg0KcGhvbmVfcGF0dGVybiA8LSBvcHRpb25hbChPUEVOX1BBUkVOKSAlUiUNCiAgdGhyZWVfZGlnaXRzICVSJQ0KICB6ZXJvX29yX21vcmUoc2VwYXJhdG9yKSAlUiUNCiAgdGhyZWVfZGlnaXRzICVSJSANCiAgemVyb19vcl9tb3JlKHNlcGFyYXRvcikgJVIlDQogIGZvdXJfZGlnaXRzDQogICAgICAgIA0KIyBUZXN0IGl0ICAgICAgICAgICANCnN0cl92aWV3X2FsbChjb250YWN0LCBwYXR0ZXJuID0gcGhvbmVfcGF0dGVybikNCiMgRXh0cmFjdCBwaG9uZSBudW1iZXJzDQpzdHJfZXh0cmFjdChjb250YWN0LCBwaG9uZV9wYXR0ZXJuKQ0KDQojIEV4dHJhY3QgQUxMIHBob25lIG51bWJlcnMNCnN0cl9leHRyYWN0X2FsbChjb250YWN0LCBwaG9uZV9wYXR0ZXJuKQ0KYGBgDQpyZWJ1cyBhbHNvIHByb3ZpZGVzIHRoZSBmdW5jdGlvbnMgbnVtYmVyX3JhbmdlKCkgd2hlbiB5b3Ugd2FudCB0byBleHRyYWN0IG51bWJlcnMgaW4gYSBjZXJ0YWluIHJhbmdlLCBhbmQgZGF0ZXRpbWUoKSB0byBzcGVjaWZ5IGNvbW1vbiBkYXRlIHBhdHRlcm5zLg0KDQojIyMgRXh0cmFjdGluZyBhZ2UgYW5kIGdlbmRlciBmcm9tIGFjY2lkZW50IG5hcnJhdGl2ZXMNCg0KUmVjYWxsIGZyb20gdGhlIHZpZGVvLCB5b3Ugd2FudCB0byBwYXJzZSBvdXQgYWdlIGFuZCBnZW5kZXIgZnJvbSBhY2NpZGVudCBuYXJyYXRpdmVzLiBGb3IgZXhhbXBsZSwgdGhpcyBuYXJyYXRpdmUNCg0KICAgIDE5WU9NLVNIT1VMREVSIFNUUkFJTi1XQVMgVEFDS0xFRCBXSElMRSBQTEFZSU5HIEZPT1RCQUxMIFcvIEZSSUVORFMgDQoNCmRlc2NyaWJlcyBtYWxlIG9mIGFnZSAxOSwgYW5kIHRoaXMgb25lDQoNCiAgICBUUklQUEVEIE9WRVIgQ0FUIEFORCBMQU5ERUQgT04gSEFSRFdPT0QgRkxPT1IuIExBQ0VSQVRJT04gRUxCT1csIExFRlQuIDMzIFlPRioNCg0KYSBmZW1hbGUgb2YgYWdlIDMzLg0KDQpZb3UgYXJlIGdlbmVyYWxseSBsb29raW5nIGZvciBhIHBhdHRlcm4gd2l0aCBhIG51bWJlciwgc29tZXRoaW5nIHRvIGluZGljYXRlIHRoZSB1bml0cywgZS5nLiBZTyBvciBZUiBmb3IgeWVhcnMgb2xkLCBvciBNTyBmb3IgbW9udGhzIG9sZCwgYW5kIGEgY2hhcmFjdGVyIHRoYXQgaWRlbnRpZmllcyB0aGUgZ2VuZGVyLg0KDQpJbiB0aGlzIGV4ZXJjaXNlIHlvdSdsbCBidWlsZCB1cCBhIHBhdHRlcm4gdG8gcHVsbCBvdXQgdGhlIHBhcnQgb2YgdGhlIG5hcnJhdGl2ZSB0aGF0IGhhcyB0aGUgYWdlIGFuZCBnZW5kZXIgaW5mb3JtYXRpb24uIFRoZW4sIGluIHRoZSBuZXh0IGV4ZXJjaXNlIHlvdSdsbCBwYXJzZSBvdXQgdGhlIGFnZSBhbmQgZ2VuZGVyIGludG8gc2VwYXJhdGUgdmFyaWFibGVzLg0KYGBge3J9DQpuYXJyYXRpdmVzIDwtIHJlYWRSRFMoIm5hcnJhdGl2ZXMucmRzIikNCmBgYA0KDQpgYGB7cn0NCiMgVXNlIHRoZXNlIHBhdHRlcm5zDQphZ2UgPC0gREdUICVSJSBvcHRpb25hbChER1QpDQp1bml0IDwtIG9wdGlvbmFsKFNQQykgJVIlIG9yKCJZTyIsICJZUiIsICJNTyIpDQpnZW5kZXIgPC0gb3B0aW9uYWwoU1BDKSAlUiUgb3IoIk0iLCAiRiIpDQoNCiMgRXh0cmFjdCBhZ2UsIHVuaXQsIGdlbmRlcg0Kc3RyX2V4dHJhY3QobmFycmF0aXZlcywgcGF0dGVybiA9IGFnZSAlUiUgdW5pdCAlUiUgZ2VuZGVyKQ0KYGBgDQpZb3UgY2FuIGFsc28gdXNlIGRndCgxLCAyKSB0byBtYXRjaCBvbmUgb3IgdHdvIGRpZ2l0cy4NCg0KIyMjIFBhcnNpbmcgYWdlIGFuZCBnZW5kZXIgaW50byBwaWVjZXMNCg0KVG8gZmluaXNoIHVwLCB5b3UgbmVlZCB0byBwdWxsIG91dCB0aGUgaW5kaXZpZHVhbCBwaWVjZXMgYW5kIHRpZHkgdGhlbSBpbnRvIHVzYWJsZSB2YXJpYWJsZXMNCg0KDQpUaGVyZSBhcmUgYSBmZXcgd2F5cyB5b3UgY291bGQgZ2V0IGF0IG9uZSBwaWVjZTogeW91IGNvdWxkIGV4dHJhY3Qgb3V0IHRoZSBwaWVjZSB5b3UgbmVlZCwgeW91IGNvdWxkIHJlcGxhY2UgZXZlcnl0aGluZyB0aGF0IGlzbid0IHRoZSBwaWVjZSB5b3UgbmVlZCB3aXRoICIiLCBvciB5b3UgY291bGQgdHJ5IHRvIHNwbGl0IGludG8gdGhlIHBpZWNlcyB5b3UgbmVlZC4gWW91J2xsIHRyeSBhIGZldyBvZiB0aGVzZSBpbiB0aGlzIGV4ZXJjaXNlIGFuZCB5b3UnbGwgc2VlIHlldCBhbm90aGVyIHdheSBpbiB0aGUgbmV4dCBjaGFwdGVyLiBGb3IgdGhlIGZpcnN0IG9wdGlvbiwgc3RyaW5nciBoYXMgYSBuaWNlIGNvbnZlbmllbmNlIGZ1bmN0aW9uLCBzdHJfcmVtb3ZlKCksIHRoYXQgd29ya3MgbGlrZSBzdHJfcmVwbGFjZSgpIHdpdGggcmVwbGFjZW1lbnQgPSAiIi4NCg0KT25lIGJlbmVmaXQgb2YgYnVpbGRpbmcgdXAgeW91ciBwYXR0ZXJuIGluIHBpZWNlcyBpcyB5b3UgYWxyZWFkeSBoYXZlIHBhdHRlcm5zIGZvciBlYWNoIHBhcnQgdGhhdCB5b3UgY2FuIHJldXNlIG5vdy4NCmBgYHtyfQ0KYWdlX2dlbmRlciA8LSBzdHJfZXh0cmFjdChuYXJyYXRpdmVzLCBwYXR0ZXJuID0gYWdlICVSJSB1bml0ICVSJSBnZW5kZXIpDQojIGFnZV9nZW5kZXIsIGFnZSwgZ2VuZGVyLCB1bml0IGFyZSBwcmUtZGVmaW5lZA0KbHMuc3RyKCkNCg0KIyBFeHRyYWN0IGFnZSBhbmQgbWFrZSBudW1lcmljDQphcy5udW1lcmljKHN0cl9leHRyYWN0KGFnZV9nZW5kZXIsIGFnZSkpDQojIFJlcGxhY2UgYWdlIGFuZCB1bml0cyB3aXRoICIiDQpnZW5kZXJzIDwtIHN0cl9yZW1vdmUoYWdlX2dlbmRlciwgYWdlICVSJSB1bml0KSANCg0KIyBSZXBsYWNlIGV4dHJhIHNwYWNlcw0Kc3RyX3JlbW92ZV9hbGwoZ2VuZGVycywgcGF0dGVybiA9IG9uZV9vcl9tb3JlKFNQQykpDQojIE51bWVyaWMgYWdlcywgZnJvbSBwcmV2aW91cyBzdGVwDQphZ2VzX251bWVyaWMgPC0gYXMubnVtZXJpYyhzdHJfZXh0cmFjdChhZ2VfZ2VuZGVyLCBhZ2UpKQ0KDQojIEV4dHJhY3QgdW5pdHMgDQp0aW1lX3VuaXRzIDwtIHN0cl9leHRyYWN0KGFnZV9nZW5kZXIsIHVuaXQpDQoNCiMgRXh0cmFjdCBmaXJzdCB3b3JkIGNoYXJhY3Rlcg0KdGltZV91bml0c19jbGVhbiA8LSBzdHJfZXh0cmFjdCh0aW1lX3VuaXRzLCBwYXR0ZXJuID0gV1JEKQ0KDQojIFR1cm4gYWdlcyBpbiBtb250aHMgdG8geWVhcnMNCmlmZWxzZSh0aW1lX3VuaXRzX2NsZWFuID09ICJZIiwgYWdlc19udW1lcmljLCBhZ2VzX251bWVyaWMgLyAxMikNCmBgYA0KLS0NCllvdSBjYW4gaW1hZ2luZSB0aGlzIHdvdWxkIGJlIGFuIGltcG9ydGFudCBmaXJzdCBzdGVwIGlmIHlvdSB3YW50ZWQgdG8gaW52ZXN0aWdhdGUgdGhlIGZyZXF1ZW5jeSBvZiBhY2NpZGVudHMgYnkgYWdlIG9yIGdlbmRlci4gSWYgeW91IHdhbnQgYSBjaGFsbGVuZ2UsIGdldCB0aGUgbmVpc3MgcGFja2FnZSBhbmQgc2VlIGlmIHlvdSBjYW4gZXh0cmFjdCBhZ2UgYW5kIGdlbmRlciBmcm9tIGFsbCBhY2NpZGVudHMuDQoNCiMgTW9yZSBhZHZhbmNlZCBtYXRjaGluZyBhbmQgbWFuaXB1bGF0aW9uDQoNCiMjIENhcHR1cmluZw0KDQojIyMgQ2FwdHVyaW5nIHBhcnRzIG9mIGEgcGF0dGVybg0KDQpuIHJlYnVzLCB0byBkZW5vdGUgYSBwYXJ0IG9mIGEgcmVndWxhciBleHByZXNzaW9uIHlvdSB3YW50IHRvIGNhcHR1cmUsIHlvdSBzdXJyb3VuZCBpdCB3aXRoIHRoZSBmdW5jdGlvbiBjYXB0dXJlKCkuIEZvciBleGFtcGxlLCBhIHNpbXBsZSBwYXR0ZXJuIHRvIG1hdGNoIGFuIGVtYWlsIGFkZHJlc3MgbWlnaHQgYmUsDQpgYGB7cn0NCmVtYWlsIDwtIG9uZV9vcl9tb3JlKFdSRCkgJVIlIA0KICAiQCIgJVIlIG9uZV9vcl9tb3JlKFdSRCkgJVIlIA0KICBET1QgJVIlIG9uZV9vcl9tb3JlKFdSRCkNCnN0cl92aWV3KCIod29sdmVyaW5lQHhtZW4uY29tKSIsIHBhdHRlcm4gPSBlbWFpbCkgIA0KYGBgDQpJZiB5b3Ugd2FudCB0byBjYXB0dXJlIHRoZSBwYXJ0IGJlZm9yZSB0aGUgQCwgeW91IHNpbXBseSB3cmFwIHRoYXQgcGFydCBvZiB0aGUgcmVndWxhciBleHByZXNzaW9uIGluIGNhcHR1cmUoKToNCg0KYGBge3J9DQplbWFpbCA8LSBjYXB0dXJlKG9uZV9vcl9tb3JlKFdSRCkpICVSJSANCiAgIkAiICVSJSBvbmVfb3JfbW9yZShXUkQpICVSJSANCiAgRE9UICVSJSBvbmVfb3JfbW9yZShXUkQpDQpzdHJfdmlldygiKHdvbHZlcmluZUB4bWVuLmNvbSkiLCBwYXR0ZXJuID0gZW1haWwpICANCmBgYA0KVGhlIHBhcnQgb2YgdGhlIHN0cmluZyB0aGF0IG1hdGNoZXMgaGFzbid0IGNoYW5nZWQsIGJ1dCBpZiB3ZSBwdWxsIG91dCB0aGUgbWF0Y2ggd2l0aCBzdHJfbWF0Y2goKSB3ZSBnZXQgYWNjZXNzIHRvIHRoZSBjYXB0dXJlZCBwaWVjZToNCmBgYHtyfQ0Kc3RyX21hdGNoKCIod29sdmVyaW5lQHhtZW4uY29tKSIsIHBhdHRlcm4gPSAgZW1haWwpICANCmBgYA0KYGBge3J9DQpoZXJvX2NvbnRhY3RzIDwtIGMoIih3b2x2ZXJpbmVAeG1lbi5jb20pIiwgIndvbmRlcndvbWFuQGp1c3RpY2VsZWFndWUub3JnIiwgInRob3JAYXZlbmdlcnMuY29tIg0KKQ0KIyBDYXB0dXJlIHBhcnRzIGJldHdlZW4gQCBhbmQgLiBhbmQgYWZ0ZXIgLg0KZW1haWwgPC0gY2FwdHVyZShvbmVfb3JfbW9yZShXUkQpKSAlUiUgDQogICJAIiAlUiUgY2FwdHVyZShvbmVfb3JfbW9yZShXUkQpKSAlUiUgDQogIERPVCAlUiUgY2FwdHVyZShvbmVfb3JfbW9yZShXUkQpKQ0KDQojIENoZWNrIG1hdGNoIGhhc24ndCBjaGFuZ2VkDQpzdHJfdmlldyhoZXJvX2NvbnRhY3RzLCBlbWFpbCkNCmBgYA0KYGBge3J9DQojIFBhdHRlcm4gZnJvbSBwcmV2aW91cyBzdGVwDQplbWFpbCA8LSBjYXB0dXJlKG9uZV9vcl9tb3JlKFdSRCkpICVSJSANCiAgIkAiICVSJSBjYXB0dXJlKG9uZV9vcl9tb3JlKFdSRCkpICVSJSANCiAgRE9UICVSJSBjYXB0dXJlKG9uZV9vcl9tb3JlKFdSRCkpDQogIA0KIyBQdWxsIG91dCBtYXRjaCBhbmQgY2FwdHVyZXMNCmVtYWlsX3BhcnRzIDwtIHN0cl9tYXRjaChoZXJvX2NvbnRhY3RzLCBlbWFpbCkNCmVtYWlsX3BhcnRzDQoNCiMgU2F2ZSBob3N0DQpob3N0IDwtIGVtYWlsX3BhcnRzWywzXQ0KaG9zdA0KYGBgDQpBY3R1YWxseSwgZGV0ZWN0aW5nIGFuIGVtYWlsIGFkZHJlc3MgY2FuIGJlIHJlYWxseSBoYXJkLg0KDQojIyMgUHVsbGluZyBvdXQgcGFydHMgb2YgYSBwaG9uZSBudW1iZXINCg0KWW91IGNhbiBub3cgZ28gYmFjayB0byB0aGUgcGhvbmUgbnVtYmVyIGV4YW1wbGUgZnJvbSB0aGUgcHJldmlvdXMgY2hhcHRlci4gWW91IGRldmVsb3BlZCBhIHBhdHRlcm4gdG8gZXh0cmFjdCB0aGUgcGFydHMgb2Ygc3RyaW5nIHRoYXQgbG9va2VkIGxpa2UgcGhvbmUgbnVtYmVycywgYW5kIG5vdyB5b3UgaGF2ZSB0aGUgc2tpbGxzIHRvIHB1bGwgb3V0IHRoZSBwaWVjZXMgb2YgdGhlIG51bWJlci4gTGV0J3Mgc2VlIGlmIHlvdSBjYW4gcHV0IHlvdXIgc2tpbGxzIHRvZ2V0aGVyIHRvIG91dHB1dCB0aGUgZmlyc3QgcGhvbmUgbnVtYmVyIGluIGVhY2ggc3RyaW5nIGluIGEgY29tbW9uIGZvcm1hdC4NCmBgYHtyfQ0Kc2VwYXJhdG9yIDwtIG9yKGNoYXJfY2xhc3MoIi0uKCl8ICIpKQ0KIyBWaWV3IHRleHQgY29udGFpbmluZyBwaG9uZSBudW1iZXJzDQpjb250YWN0DQoNCiMgQWRkIGNhcHR1cmUoKSB0byBnZXQgZGlnaXQgcGFydHMNCnBob25lX3BhdHRlcm4gPC0gY2FwdHVyZSh0aHJlZV9kaWdpdHMpICVSJSB6ZXJvX29yX21vcmUoc2VwYXJhdG9yKSAlUiUgDQogICAgICAgICAgIGNhcHR1cmUodGhyZWVfZGlnaXRzKSAlUiUgemVyb19vcl9tb3JlKHNlcGFyYXRvcikgJVIlDQogICAgICAgICAgIGNhcHR1cmUoZm91cl9kaWdpdHMpDQogICAgICAgICAgIA0KIyBQdWxsIG91dCB0aGUgcGFydHMgd2l0aCBzdHJfbWF0Y2goKQ0KcGhvbmVfbnVtYmVycyA8LSBzdHJfbWF0Y2goY29udGFjdCwgcGhvbmVfcGF0dGVybikNCnBob25lX251bWJlcnMNCiMgUHV0IHRoZW0gYmFjayB0b2dldGhlcg0Kc3RyX2MoDQogICIoIiwNCiAgcGhvbmVfbnVtYmVyc1ssMl0sDQogICIpIiwNCiAgcGhvbmVfbnVtYmVyc1ssM10sDQogICItIiwNCiAgcGhvbmVfbnVtYmVyc1ssNF0pDQpgYGANCklmIHlvdSB3YW50ZWQgdG8gZXh0cmFjdCBiZXlvbmQgdGhlIGZpcnN0IHBob25lIG51bWJlciwgZS5nLiBUaGUgc2Vjb25kIHBob25lIG51bWJlciBpbiB0aGUgbGFzdCBzdHJpbmcsIHlvdSBjb3VsZCB1c2Ugc3RyX21hdGNoX2FsbCgpLiBCdXQsIGxpa2Ugc3RyX3NwbGl0KCkgaXQgd2lsbCByZXR1cm4gYSBsaXN0IHdpdGggb25lIGNvbXBvbmVudCBmb3IgZWFjaCBpbnB1dCBzdHJpbmcsIGFuZCB5b3UnbGwgbmVlZCB0byB1c2UgbGFwcGx5KCkgdG8gaGFuZGxlIHRoZSByZXN1bHQuDQoNCiMjIyBFeHRyYWN0aW5nIGFnZSBhbmQgZ2VuZGVyIGFnYWluDQoNCllvdSBjYW4gbm93IGFsc28gdGFrZSBhbm90aGVyIGxvb2sgYXQgeW91ciBwYXR0ZXJuIGZvciBwdWxsaW5nIG91dCBhZ2UgYW5kIGdlbmRlciBmcm9tIHRoZSBpbmp1cnkgbmFycmF0aXZlcy4NCmBgYHtyfQ0KIyBuYXJyYXRpdmVzIGhhcyBiZWVuIHByZS1kZWZpbmVkDQpuYXJyYXRpdmVzDQoNCiMgQWRkIGNhcHR1cmUoKSB0byBnZXQgYWdlLCB1bml0IGFuZCBzZXgNCnBhdHRlcm4gPC0gY2FwdHVyZShvcHRpb25hbChER1QpICVSJSBER1QpICVSJSAgDQogIG9wdGlvbmFsKFNQQykgJVIlIGNhcHR1cmUob3IoIllPIiwgIllSIiwgIk1PIikpICVSJQ0KICBvcHRpb25hbChTUEMpICVSJSBjYXB0dXJlKG9yKCJNIiwgIkYiKSkNCg0KIyBQdWxsIG91dCBmcm9tIG5hcnJhdGl2ZXMNCnN0cl9tYXRjaChuYXJyYXRpdmVzLCBwYXR0ZXJuKQ0KIyBFZGl0IHRvIGNhcHR1cmUganVzdCBZIGFuZCBNIGluIHVuaXRzDQpwYXR0ZXJuMiA8LSBjYXB0dXJlKG9wdGlvbmFsKERHVCkgJVIlIERHVCkgJVIlICANCiAgb3B0aW9uYWwoU1BDKSAlUiUgY2FwdHVyZShvcigiWSIsICJNIikpICVSJSBvcHRpb25hbChvcigiTyIsIlIiKSkgJVIlDQogIG9wdGlvbmFsKFNQQykgJVIlIGNhcHR1cmUob3IoIk0iLCAiRiIpKQ0KDQojIENoZWNrIHBhdHRlcm4NCnN0cl92aWV3KG5hcnJhdGl2ZXMsIHBhdHRlcm4yKQ0KDQojIFB1bGwgb3V0IHBpZWNlcw0Kc3RyX21hdGNoKG5hcnJhdGl2ZXMsIHBhdHRlcm4yKQ0KYGBgDQpUaGUgY29tYmluYXRpb24gb2YgY2FwdHVyZSgpIGFuZCBzdHJfbWF0Y2goKSBpcyBwb3dlcmZ1bCBmb3IgZXh0cmFjdGluZyBwaWVjZXMgb2YgdGV4dC4NCg0KIyMgQmFja3JlZmVyZW5jZXMNCg0KIyMjIFVzaW5nIGJhY2tyZWZlcmVuY2VzIGluIHBhdHRlcm5zDQoNCkJhY2tyZWZlcmVuY2VzIGNhbiBiZSB1c2VmdWwgaW4gbWF0Y2hpbmcgYmVjYXVzZSB0aGV5IGFsbG93IHlvdSB0byBmaW5kIHJlcGVhdGVkIHBhdHRlcm5zIG9yIHdvcmRzLiBVc2luZyBhIGJhY2tyZWZlcmVuY2UgcmVxdWlyZXMgdHdvIHRoaW5nczogeW91IG5lZWQgdG8gY2FwdHVyZSgpIHRoZSBwYXJ0IG9mIHRoZSBwYXR0ZXJuIHlvdSB3YW50IHRvIHJlZmVyZW5jZSwgYW5kIHRoZW4geW91IHJlZmVyIHRvIGl0IHdpdGggUkVGMS4NCg0KVGFrZSBhIGxvb2sgYXQgdGhpcyBwYXR0ZXJuOiBjYXB0dXJlKExPV0VSKSAlUiUgUkVGMS4gSXQgbWF0Y2hlcyBhbmQgY2FwdHVyZXMgYW55IGxvd2VyIGNhc2UgY2hhcmFjdGVyLCB0aGVuIGlzIGZvbGxvd2VkIGJ5IHRoZSBjYXB0dXJlZCBjaGFyYWN0ZXI6IGl0IGRldGVjdHMgcmVwZWF0ZWQgY2hhcmFjdGVycyByZWdhcmRsZXNzIG9mIHdoYXQgY2hhcmFjdGVyIGlzIHJlcGVhdGVkLiBUbyBzZWUgaXQgaW4gYWN0aW9uIHRyeSB0aGlzOg0KDQpgYGB7cn0NCnN0cl92aWV3KGMoImhlbGxvIiwgInN3ZWV0IiwgImtpdHRlbiIpLCANCiAgcGF0dGVybiA9IGNhcHR1cmUoTE9XRVIpICVSJSBSRUYxKQ0KYGBgDQoNCklmIHlvdSBjYXB0dXJlIG1vcmUgdGhhbiBvbmUgdGhpbmcgeW91IGNhbiByZWZlciB0byB0aGVtIHdpdGggUkVGMiwgUkVGMyBldGMuIHVwIHRvIFJFRjksIGNvdW50aW5nIHRoZSBjYXB0dXJlcyBmcm9tIHRoZSBsZWZ0IG9mIHRoZSBwYXR0ZXJuLg0KYGBge3J9DQojIE5hbWVzIHdpdGggdGhyZWUgcmVwZWF0ZWQgbGV0dGVycw0KcmVwZWF0ZWRfdGhyZWVfdGltZXMgPC0gY2FwdHVyZShMT1dFUikgJVIlIFJFRjEgJVIlIFJFRjENCg0KIyBUZXN0IGl0DQpzdHJfdmlldyhib3lfbmFtZXMsIHBhdHRlcm4gPSByZXBlYXRlZF90aHJlZV90aW1lcywgbWF0Y2ggPSBUUlVFKQ0KIyBOYW1lcyB3aXRoIGEgcGFpciBvZiByZXBlYXRlZCBsZXR0ZXJzDQpwYWlyX29mX3JlcGVhdGVkIDwtIGNhcHR1cmUoTE9XRVIgJVIlIExPV0VSKSAlUiUgUkVGMQ0KDQojIFRlc3QgaXQNCnN0cl92aWV3KGJveV9uYW1lcywgcGF0dGVybiA9IHBhaXJfb2ZfcmVwZWF0ZWQsIG1hdGNoID0gVFJVRSkNCiMgTmFtZXMgd2l0aCBhIHBhaXIgdGhhdCByZXZlcnNlcw0KcGFpcl90aGF0X3JldmVyc2VzIDwtIGNhcHR1cmUoTE9XRVIpICVSJSBjYXB0dXJlKExPV0VSKSAlUiUgUkVGMiAlUiUgUkVGMQ0KDQojIFRlc3QgaXQNCnN0cl92aWV3KGJveV9uYW1lcywgcGF0dGVybiA9IHBhaXJfdGhhdF9yZXZlcnNlcywgbWF0Y2ggPSBUUlVFKQ0KIyBGb3VyIGxldHRlciBwYWxpbmRyb21lIG5hbWVzDQpmb3VyX2xldHRlcl9wYWxpbmRyb21lIDwtIGV4YWN0bHkoY2FwdHVyZShMT1dFUikgJVIlIGNhcHR1cmUoTE9XRVIpICVSJSBSRUYyICVSJSBSRUYxKQ0KDQojIFRlc3QgaXQNCnN0cl92aWV3KGJveV9uYW1lcywgcGF0dGVybiA9IGZvdXJfbGV0dGVyX3BhbGluZHJvbWUsIG1hdGNoID0gVFJVRSkNCmBgYA0KSW4gYWRkaXRpb24gdG8gbWF0Y2hpbmcgcmVwZWF0ZWQgdmFsdWVzLCBiYWNrcmVmZXJlbmNlcyBjYW4gYWxzbyBiZSB1c2VkIGZvciByZXBsYWNlbWVudC4NCg0KIyMjIFJlcGxhY2luZyB3aXRoIHJlZ3VsYXIgZXhwcmVzc2lvbnMNCg0KTm93LCB5b3UndmUgbWFzdGVyZWQgbWF0Y2hpbmcgd2l0aCBiYWNrcmVmZXJlbmNlcywgeW91J2xsIGJ1aWxkIHVwIHRvIHJlcGxhY2luZyB3aXRoIGJhY2tyZWZlcmVuY2VzLCBidXQgZmlyc3QgbGV0J3MgcmV2aWV3IHN0cl9yZXBsYWNlKCkgbm93IHRoYXQgeW91J3ZlIGdvdCByZWd1bGFyIGV4cHJlc3Npb25zIHVuZGVyIHlvdXIgYmVsdC4NCg0KUmVtZW1iZXIgc3RyX3JlcGxhY2UoKSB0YWtlcyB0aHJlZSBhcmd1bWVudHMsIHN0cmluZyBhIHZlY3RvciBvZiBzdHJpbmdzIHRvIGRvIHRoZSByZXBsYWNlbWVudHMgaW4sIHBhdHRlcm4gdGhhdCBpZGVudGlmaWVzIHRoZSBwYXJ0cyBvZiBzdHJpbmdzIHRvIHJlcGxhY2UgYW5kIHJlcGxhY2VtZW50IHRoZSB0aGluZyB0byB1c2UgYXMgYSByZXBsYWNlbWVudC4NCg0KcmVwbGFjZW1lbnQgY2FuIGJlIGEgdmVjdG9yLCB0aGUgc2FtZSBsZW5ndGggYXMgc3RyaW5nLCBlYWNoIGVsZW1lbnQgc3BlY2lmaWVzIHRoZSByZXBsYWNlbWVudCB0byBiZSB1c2VkIGluIGVhY2ggc3RyaW5nLg0KYGBge3J9DQojIFZpZXcgdGV4dCBjb250YWluaW5nIHBob25lIG51bWJlcnMNCmNvbnRhY3QNCg0KIyBSZXBsYWNlIGRpZ2l0cyB3aXRoICJYIg0Kc3RyX3JlcGxhY2UoY29udGFjdCwgREdULCAiWCIpDQoNCiMgUmVwbGFjZSBhbGwgZGlnaXRzIHdpdGggIlgiDQpzdHJfcmVwbGFjZV9hbGwoY29udGFjdCwgREdULCAiWCIpDQoNCiMgUmVwbGFjZSBhbGwgZGlnaXRzIHdpdGggZGlmZmVyZW50IHN5bWJvbA0Kc3RyX3JlcGxhY2VfYWxsKGNvbnRhY3QsIERHVCwgYygiWCIsICIuIiwgIioiLCAiXyIpKQ0KYGBgDQpVc2luZyAiIiBmb3IgdGhlIHJlcGxhY2VtZW50IHZhbHVlIGlzIGEgZ3JlYXQgd2F5IHRvIGN1dCB1bndhbnRlZCBiaXRzIGZyb20gYSBzdHJpbmcuDQoNCiMjIyBSZXBsYWNpbmcgd2l0aCBiYWNrcmVmZXJlbmNlcw0KDQpUaGUgcmVwbGFjZW1lbnQgYXJndW1lbnQgdG8gc3RyX3JlcGxhY2UoKSBjYW4gYWxzbyBpbmNsdWRlIGJhY2tyZWZlcmVuY2VzLiBUaGlzIHdvcmtzIGp1c3QgbGlrZSBzcGVjaWZ5aW5nIHBhdHRlcm5zIHdpdGggYmFja3JlZmVyZW5jZXMsIGV4Y2VwdCB0aGUgY2FwdHVyZSBoYXBwZW5zIGluIHRoZSBwYXR0ZXJuIGFyZ3VtZW50LCBhbmQgdGhlIGJhY2tyZWZlcmVuY2UgaXMgdXNlZCBpbiB0aGUgcmVwbGFjZW1lbnQgYXJndW1lbnQuDQoNCmBgYHtyfQ0KeCA8LSBjKCJoZWxsbyIsICJzd2VldCIsICJraXR0ZW4iKQ0Kc3RyX3JlcGxhY2UoeCwgY2FwdHVyZShBTllfQ0hBUiksIHN0cl9jKFJFRjEsIFJFRjEpKQ0KYGBgDQpjYXB0dXJlKEFOWV9DSEFSKSB3aWxsIG1hdGNoIHRoZSBmaXJzdCBjaGFyYWN0ZXIgbm8gbWF0dGVyIHdoYXQgaXQgaXMuIFRoZW4gdGhlIHJlcGxhY2VtZW50IHN0cl9jKFJFRjEsIFJFRjEpIGNvbWJpbmVzIHRoZSBjYXB0dXJlZCBjaGFyYWN0ZXIgd2l0aCBpdHNlbGYsIGluIGVmZmVjdCBkb3VibGluZyB0aGUgZmlyc3QgbGV0dGVyIG9mIGVhY2ggc3RyaW5nLg0KDQpZb3UgYXJlIGdvaW5nIHRvIHVzZSB0aGlzIHRvIGNyZWF0ZSBzb21lIGFsdGVybmF0aXZlLCBtb3JlIGxpdmVseSBhY2NpZGVudCBuYXJyYXRpdmVzLg0KDQpUaGUgc3RyYXRlZ3kgeW91J2xsIHRha2UgaXMgdG8gbWF0Y2ggd29yZHMgZW5kaW5nIGluICJJTkciIHRoZW4gcmVwbGFjZSB0aGVtIHdpdGggYW4gYWR2ZXJiIGZvbGxvd2VkIGJ5IHRoZSBvcmlnaW5hbCB3b3JkLg0KYGBge3J9DQphZHZlcmJzIDwtIHJlYWRSRFMoImFkdmVyYnMucmRzIikNCiMgQnVpbGQgcGF0dGVybiB0byBtYXRjaCB3b3JkcyBlbmRpbmcgaW4gIklORyINCnBhdHRlcm4gPC0gb25lX29yX21vcmUoV1JEKSAlUiUgIklORyINCnN0cl92aWV3KG5hcnJhdGl2ZXMsIHBhdHRlcm4pDQoNCiMgVGVzdCByZXBsYWNlbWVudA0Kc3RyX3JlcGxhY2UobmFycmF0aXZlcywgY2FwdHVyZShwYXR0ZXJuKSwgDQogIHN0cl9jKCJDQVJFTEVTU0xZIiwgUkVGMSwgc2VwID0gIiAiKSkNCg0KIyBPbmUgYWR2ZXJiIHBlciBuYXJyYXRpdmUNCmFkdmVyYnNfMTAgPC0gc2FtcGxlKGFkdmVyYnMsIDEwKQ0KDQojIFJlcGxhY2UgIioqKmluZyIgd2l0aCAiYWR2ZXJiICoqKmx5Ig0Kc3RyX3JlcGxhY2UobmFycmF0aXZlcywgDQogIGNhcHR1cmUocGF0dGVybiksDQogIHN0cl9jKGFkdmVyYnNfMTAsIFJFRjEsIHNlcCA9ICIgIikpICANCmBgYA0KUmVwbGFjZW1lbnQgY29tYmluZWQgd2l0aCBiYWNrcmVmZXJlbmNlcyBjYW4gYmUgcmVhbGx5IHVzZWZ1bCBmb3IgcmVmb3JtYXR0aW5nIHRleHQgZGF0YS4NCg0KIyMgVW5pY29kZSBhbmQgcGF0dGVybiBtYXRjaGluZw0KDQojIyMgTWF0Y2hpbmcgYSBzcGVjaWZpYyBjb2RlIHBvaW50IG9yIGNvZGUgZ3JvdXBzDQoNClRoaW5ncyBjYW4gZ2V0IHRyaWNreSB3aGVuIHNvbWUgY2hhcmFjdGVycyBjYW4gYmUgc3BlY2lmaWVkIHR3byB3YXlzLCBmb3IgZXhhbXBsZSDDqCwgYW4gZSB3aXRoIGEgZ3JhdmUgYWNjZW50LCBjYW4gYmUgc3BlY2lmaWVkIGVpdGhlciB3aXRoIHRoZSBzaW5nbGUgY29kZSBwb2ludCBcXHUwMGU4IG9yIHRoZSBjb21iaW5hdGlvbiBvZiBhIFxcdTAwNjUgYW5kIGEgY29tYmluaW5nIGdyYXZlIGFjY2VudCBcXHUwMzAwLiBUaGV5IGxvb2sgdGhlIHNhbWU6DQpgYGB7cn0NCnggPC0gYygiXHUwMGU4IiwgIlx1MDA2NVx1MDMwMCIpDQp3cml0ZUxpbmVzKHgpDQpgYGANCkJ1dCwgc3BlY2lmeWluZyB0aGUgc2luZ2xlIGNvZGUgcG9pbnQgb25seSBtYXRjaGVzIHRoYXQgdmVyc2lvbjoNCmBgYHtyfQ0Kc3RyX3ZpZXcoeCwgIlx1MDBlOCIpDQpgYGANClRoZSBzdHJpbmdpIHBhY2thZ2UgdGhhdCBzdHJpbmdyIGlzIGJ1aWx0IG9uIGNvbnRhaW5zIGZ1bmN0aW9ucyBmb3IgY29udmVydGluZyBiZXR3ZWVuIHRoZSB0d28gZm9ybXMuIHN0cmlfdHJhbnNfbmZjKCkgY29tcG9zZXMgY2hhcmFjdGVycyB3aXRoIGNvbWJpbmluZyBhY2NlbnRzIGludG8gYSBzaW5nbGUgY2hhcmFjdGVyLiBzdHJpX3RyYW5zX25mZCgpIGRlY29tcG9zZXMgY2hhcmFjdGVyIHdpdGggYWNjZW50cyBpbnRvIHNlcGFyYXRlIGxldHRlciBhbmQgYWNjZW50IGNoYXJhY3RlcnMuIFlvdSBjYW4gc2VlIGhvdyB0aGUgY2hhcmFjdGVycyBkaWZmZXIgYnkgbG9va2luZyBhdCB0aGUgaGV4YWRlY2ltYWwgY29kZXMuDQpgYGB7cn0NCmxpYnJhcnkoc3RyaW5naSkNCmFzLmhleG1vZGUodXRmOFRvSW50KHN0cmlfdHJhbnNfbmZkKCJcdTAwZTgiKSkpDQphcy5oZXhtb2RlKHV0ZjhUb0ludChzdHJpX3RyYW5zX25mYygiXHUwMDY1XHUwMzAwIikpKQ0KYGBgDQpJbiBVbmljb2RlLCBhbiBhY2NlbnQgaXMga25vd24gYXMgYSBkaWFjcml0aWMgVW5pY29kZSBQcm9wZXJ0eSwgYW5kIHlvdSBjYW4gbWF0Y2ggaXQgdXNpbmcgdGhlIHJlYnVzIHZhbHVlIFVQX0RJQUNSSVRJQy4NCg0KVmlldG5hbWVzZSBtYWtlcyBoZWF2eSB1c2Ugb2YgZGlhY3JpdGljcyB0byBkZW5vdGUgdGhlIHRvbmVzIGluIGl0cyB3b3Jkcy4gSW4gdGhpcyBleGVyY2lzZSwgeW91J2xsIG1hbmlwdWxhdGUgdGhlIGRpYWNyaXRpY3MgaW4gdGhlIG5hbWVzIG9mIFZpZXRuYW1lc2UgcnVsZXJzLg0KYGBge3J9DQojIE5hbWVzIHdpdGggYnVpbHRpbiBhY2NlbnRzDQoNCih0YXlfc29uX2J1aWx0aW4gPC0gYygNCiAgIk5ndXlcdTFlYzVuIE5oXHUxZWExYyIsIA0KICAiTmd1eVx1MWVjNW4gSHVcdTFlYzciLA0KICAiTmd1eVx1MWVjNW4gUXVhbmcgVG9cdTFlYTNuIg0KKSkNCg0KIyBDb252ZXJ0IHRvIHNlcGFyYXRlIGFjY2VudHMNCnRheV9zb25fc2VwYXJhdGUgPC0gc3RyaV90cmFuc19uZmQodGF5X3Nvbl9idWlsdGluKQ0KDQojIFZlcmlmeSB0aGF0IHRoZSBzdHJpbmcgcHJpbnRzIHRoZSBzYW1lDQp0YXlfc29uX3NlcGFyYXRlDQoNCiMgTWF0Y2ggYWxsIGFjY2VudHMNCnN0cl92aWV3X2FsbCh0YXlfc29uX3NlcGFyYXRlLCBVUF9ESUFDUklUSUMpDQpgYGANClhpbiBjaMO6YyBt4burbmchICdJdCBpcyB1c2VmdWwgdG8gYmUgY29uc2lzdGVudCBhYm91dCBjb21iaW5pbmcgb3Igc2VwYXJhdGluZyBkaWFjcml0aWNzLicgLSBwb3NzaWJseSBhIFZpZXRuYW1lc2UgcHJvdmVyYi4NCg0KIyMjIE1hdGNoaW5nIGEgc2luZ2xlIGdyYXBoZW1lDQoNCkEgcmVsYXRlZCBwcm9ibGVtIGlzIG1hdGNoaW5nIGEgc2luZ2xlIGNoYXJhY3Rlci4gWW91J3ZlIHVzZWQgQU5ZX0NIQVIgdG8gZG8gdGhpcyB1cCB1bnRpbCBub3csIGJ1dCBpdCB3aWxsIG9ubHkgbWF0Y2ggYSBjaGFyYWN0ZXIgcmVwcmVzZW50ZWQgYnkgYSBzaW5nbGUgY29kZSBwb2ludC4gVGFrZSB0aGVzZSB0aHJlZSBuYW1lczoNCmBgYHtyfQ0KeCA8LSBjKCJBZGVsZSIsICJBZFx1MDBlOGxlIiwgIkFkXHUwMDY1XHUwMzAwbGUiKQ0Kd3JpdGVMaW5lcyh4KQ0KYGBgDQpUaGV5IGxvb2sgdGhlIHNpbWlsYXIsIGJ1dCB0aGlzIHJlZ3VsYXIgZXhwcmVzc2lvbiBvbmx5IG1hdGNoZXMgdHdvIG9mIHRoZW06DQpgYGB7cn0NCnN0cl92aWV3KHgsICJBZCIgJVIlIEFOWV9DSEFSICVSJSAibGUiKQ0KYGBgDQpiZWNhdXNlIGluIHRoZSB0aGlyZCBuYW1lIMOoIGlzIHJlcHJlc2VudGVkIGJ5IHR3byBjb2RlIHBvaW50cy4gVGhlIFVuaWNvZGUgc3RhbmRhcmQgaGFzIGEgY29uY2VwdCBvZiBhIGdyYXBoZW1lIHRoYXQgcmVwcmVzZW50cyBhIGRpc3BsYXkgY2hhcmFjdGVyLCBidXQgbWF5IGJlIGNvbXBvc2VkIG9mIG1hbnkgY29kZSBwb2ludHMuIFRvIG1hdGNoIGFueSBncmFwaGVtZSB5b3UgY2FuIHVzZSBHUkFQSEVNRS4NCmBgYHtyfQ0Kc3RyX3ZpZXcoeCwgIkFkIiAlUiUgR1JBUEhFTUUgJVIlICJsZSIpDQpgYGANCk5hbWVzIG9mIHJ1bGVycyBmcm9tIHRoZSBWaWV0bmFtZXNlIFTDonkgU8ahbiBkeW5hc3R5LCB3aXRoIGRpYWNyaXRpY3MgZ2l2ZW4gYXMgc2VwYXJhdGUgZ3JhcGhlbWVzLCBpcyBwcmUtZGVmaW5lZCBhcyB0YXlfc29uX3NlcGFyYXRlLg0KYGBge3J9DQojIHRheV9zb25fc2VwYXJhdGUgaGFzIGJlZW4gcHJlLWRlZmluZWQNCnRheV9zb25fc2VwYXJhdGUNCg0KIyBWaWV3IGFsbCB0aGUgY2hhcmFjdGVycyBpbiB0YXlfc29uX3NlcGFyYXRlDQpzdHJfdmlld19hbGwodGF5X3Nvbl9zZXBhcmF0ZSwgQU5ZX0NIQVIpDQojIFZpZXcgYWxsIHRoZSBncmFwaGVtZXMgaW4gdGF5X3Nvbl9zZXBhcmF0ZQ0Kc3RyX3ZpZXdfYWxsKHRheV9zb25fc2VwYXJhdGUsIEdSQVBIRU1FKQ0KIyBDb21iaW5lIHRoZSBkaWFjcml0aWNzIHdpdGggdGhlaXIgbGV0dGVycw0KdGF5X3Nvbl9idWlsdGluIDwtIHN0cmlfdHJhbnNfbmZjKHRheV9zb25fc2VwYXJhdGUpDQp0YXlfc29uX2J1aWx0aW4NCg0KIyBWaWV3IGFsbCB0aGUgZ3JhcGhlbWVzIGluIHRheV9zb25fYnVpbHRpbg0Kc3RyX3ZpZXdfYWxsKHRheV9zb25fYnVpbHRpbiwgR1JBUEhFTUUpDQpgYGANCiMgQ2FzZSBzdHVkaWVzDQoNCiMjIEltcG9ydGFuY2Ugb2YgYmVpbmcgZWFybmVzdA0KDQojIyMgR2V0dGluZyB0aGUgcGxheSBpbnRvIFINCg0KWW91ciBmaXJzdCBzdGVwIGlzIHRvIHJlYWQgdGhlIHBsYXkgaW50byBSIHVzaW5nIHN0cmlfcmVhZF9saW5lcygpLg0KDQpZb3Ugc2hvdWxkIHRha2UgYSBsb29rIGF0IHRoZSBvcmlnaW5hbCB0ZXh0IGZpbGU6IGltcG9ydGFuY2Utb2YtYmVpbmctZWFybmVzdC50eHQNCg0KWW91J2xsIHNlZSB0aGVyZSBpcyBzb21lIGZvcmV3b3JkIGFuZCBhZnRlcndvcmQgdGV4dCB0aGF0IFByb2plY3QgR3V0ZW5iZXJnIGhhcyBhZGRlZC4gWW91J2xsIHdhbnQgdG8gcmVtb3ZlIHRoYXQsIGFuZCB0aGVuIHNwbGl0IHRoZSBwbGF5IGludG8gdGhlIGludHJvZHVjdGlvbiAodGhlIGxpc3Qgb2YgY2hhcmFjdGVycywgc2NlbmVzLCBldGMuKSBhbmQgdGhlIG1haW4gYm9keS4NCmBgYHtyfQ0KZWFybmVzdF9maWxlIDwtICJpbXBvcnRhbmNlLW9mLWJlaW5nLWVhcm5lc3QudHh0Ig0KIyBSZWFkIHBsYXkgaW4gdXNpbmcgc3RyaV9yZWFkX2xpbmVzKCkNCmVhcm5lc3QgPC0gc3RyaV9yZWFkX2xpbmVzKGVhcm5lc3RfZmlsZSkNCiMgRGV0ZWN0IHN0YXJ0IGFuZCBlbmQgbGluZXMNCnN0YXJ0IDwtIHN0cl93aGljaChlYXJuZXN0LCBmaXhlZCgiU1RBUlQgT0YgVEhFIFBST0pFQ1QiKSkNCmVuZCA8LSBzdHJfd2hpY2goZWFybmVzdCwgZml4ZWQoIkVORCBPRiBUSEUgUFJPSkVDVCIpKQ0KDQojIEdldCByaWQgb2YgZ3V0ZW5iZXJnIGludHJvIHRleHQNCmVhcm5lc3Rfc3ViICA8LSBlYXJuZXN0WyhzdGFydCArIDEpOihlbmQgLSAxKV0NCiMgUmVhZCBwbGF5IGluIHVzaW5nIHN0cmlfcmVhZF9saW5lcygpDQplYXJuZXN0IDwtIHN0cmlfcmVhZF9saW5lcyhlYXJuZXN0X2ZpbGUpDQoNCiMgRGV0ZWN0IGZpcnN0IGFjdA0KbGluZXNfc3RhcnQgPC0gc3RyX3doaWNoKGVhcm5lc3Rfc3ViLCBmaXhlZCgiRklSU1QgQUNUIikpDQoNCiMgU2V0IHVwIGluZGV4DQppbnRyb19saW5lX2luZGV4IDwtIDE6KGxpbmVzX3N0YXJ0IC0gMSkNCg0KIyBTcGxpdCBwbGF5IGludG8gaW50cm8gYW5kIHBsYXkNCmludHJvX3RleHQgPC0gZWFybmVzdF9zdWJbaW50cm9fbGluZV9pbmRleF0NCnBsYXlfdGV4dCA8LSBlYXJuZXN0X3N1YlstaW50cm9fbGluZV9pbmRleF0NCiMgVGFrZSBhIGxvb2sgYXQgdGhlIGZpcnN0IDIwIGxpbmVzDQp3cml0ZUxpbmVzKHBsYXlfdGV4dFsxOjIwXSkNCg0KYGBgDQpzdHJpX3JlYWRfbGluZXMoKSBpcyBhIGhpZ2ggcGVyZm9ybWFuY2UgYWx0ZXJuYXRpdmUgdG8gcmVhZExpbmVzKCkuDQoNCiMjIyBJZGVudGlmeWluZyB0aGUgbGluZXMsIHRha2UgMQ0KDQpUaGUgZmlyc3QgdGhpbmcgeW91IG1pZ2h0IG5vdGljZSB3aGVuIHlvdSBsb29rIGF0IHlvdXIgdmVjdG9yIHBsYXlfdGV4dCBpcyB0aGVyZSBhcmUgbG90cyBvZiBlbXB0eSBsaW5lcy4gVGhleSBkb24ndCByZWFsbHkgYWZmZWN0IHlvdXIgdGFzayBzbyB5b3UgbWlnaHQgd2FudCB0byByZW1vdmUgdGhlbS4gVGhlIGVhc2llc3Qgd2F5IHRvIGZpbmQgZW1wdHkgc3RyaW5ncyBpcyB0byB1c2UgdGhlIHN0cmluZ2kgZnVuY3Rpb24gc3RyaV9pc2VtcHR5KCksIHdoaWNoIHJldHVybnMgYSBsb2dpY2FsIHlvdSBjYW4gdXNlIHRvIHN1YnNldCB0aGUgbm90LWVtcHR5IHN0cmluZ3M6DQpgYGB7cn0NCiMgR2V0IHJpZCBvZiBlbXB0eSBzdHJpbmdzDQplbXB0eSA8LSBzdHJpX2lzZW1wdHkocGxheV90ZXh0KQ0KcGxheV9saW5lcyA8LSBwbGF5X3RleHRbIWVtcHR5XQ0KYGBgDQpTbywgaG93IGFyZSB5b3UgZ29pbmcgdG8gZmluZCB0aGUgZWxlbWVudHMgdGhhdCBpbmRpY2F0ZSBhIGNoYXJhY3RlciBzdGFydHMgdGhlaXIgbGluZT8gQ29uc2lkZXIgdGhlIGZvbGxvd2luZyBsaW5lcw0KYGBge3J9DQpwbGF5X2xpbmVzWzEwOjE1XQ0KYGBgDQpUaGUgZmlyc3QgbGluZSBpcyBmb3IgQWxnZXJub24sIHRoZSBuZXh0IHRocmVlIHN0cmluZ3MgYXJlIGNvbnRpbnVhdGlvbnMgb2YgdGhhdCBsaW5lLCB0aGVuIGxpbmUgNSBpcyBmb3IgTGFuZSBhbmQgbGluZSA2IGZvciBBbGdlcm5vbi4NCg0KSG93IGFib3V0IGxvb2tpbmcgZm9yIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCBhIHdvcmQgZm9sbG93ZWQgYnkgYSAuPw0KYGBge3J9DQojIFBhdHRlcm4gZm9yIHN0YXJ0LCB3b3JkIHRoZW4gLg0KcGF0dGVybl8xIDwtIFNUQVJUICVSJSBvbmVfb3JfbW9yZShXUkQpICVSJSBET1QNCg0KIyBUZXN0IHBhdHRlcm5fMQ0KIyBzdHJfdmlldyhwbGF5X2xpbmVzLCBwYXR0ZXJuXzEsIG1hdGNoID0gVFJVRSkgDQojIHN0cl92aWV3KHBsYXlfbGluZXMsIHBhdHRlcm5fMSwgbWF0Y2ggPSBGQUxTRSkNCg0KIyBQYXR0ZXJuIGZvciBzdGFydCwgY2FwaXRhbCwgd29yZCB0aGVuIC4NCnBhdHRlcm5fMiA8LSBTVEFSVCAlUiUgY2hhcl9jbGFzcygiQS1aIikgJVIlIG9uZV9vcl9tb3JlKFdSRCkgJVIlIERPVA0KDQojIFRlc3QgcGF0dGVybl8yDQojIHN0cl92aWV3KHBsYXlfbGluZXMsIHBhdHRlcm5fMiwgbWF0Y2ggPSBUUlVFKQ0KIyBzdHJfdmlldyhwbGF5X2xpbmVzLCBwYXR0ZXJuXzIsIG1hdGNoID0gRkFMU0UpDQoNCiMgUGF0dGVybiBmcm9tIGxhc3Qgc3RlcA0KcGF0dGVybl8yIDwtIFNUQVJUICVSJSBhc2NpaV91cHBlcigpICVSJSBvbmVfb3JfbW9yZShXUkQpICVSJSBET1QNCg0KIyBHZXQgc3Vic2V0IG9mIGxpbmVzIHRoYXQgbWF0Y2gNCmxpbmVzIDwtIHN0cl9zdWJzZXQocGxheV9saW5lcywgcGF0dGVybl8yKQ0KDQojIEV4dHJhY3QgbWF0Y2ggZnJvbSBsaW5lcw0Kd2hvIDwtIHN0cl9leHRyYWN0KGxpbmVzLCBwYXR0ZXJuXzIpDQoNCiMgTGV0J3Mgc2VlIHdoYXQgd2UgaGF2ZQ0KdW5pcXVlKHdobykNCmBgYA0KSXQgbG9va3MgbGlrZSB5b3VyIHBhdHRlcm4gd2Fzbid0IDEwMCUgc3VjY2Vzc2Z1bC4gSXQgbWlzc2VkIExhZHkgQnJhY2tuZWxsLCBhbmQgcGlja2VkIHVwIGxpbmVzIHN0YXJ0aW5nIHdpdGggVW5pdmVyc2l0eS4sIEp1bHkuIGFuZCBhIGZldyBvdGhlcnMuIExldCdzIHRyeSBhIHNsaWdodGx5IGRpZmZlcmVudCBzdHJhdGVneS4NCg0KIyMjIElkZW50aWZ5aW5nIHRoZSBsaW5lcywgdGFrZSAyDQoNClRoZSBwYXR0ZXJuICJzdGFydHMgd2l0aCBhIGNhcGl0YWwgbGV0dGVyLCBoYXMgc29tZSBvdGhlciBjaGFyYWN0ZXJzIHRoZW4gYSBmdWxsIHN0b3AiIHdhc24ndCBzcGVjaWZpYyBlbm91Z2guIFlvdSBlbmRlZCB1cCBtYXRjaGluZyBsaW5lcyB0aGF0IHN0YXJ0ZWQgd2l0aCB0aGluZ3MgbGlrZSBVbml2ZXJzaXR5LiwgSnVseS4sIExvbmRvbi4sIGFuZCB5b3UgbWlzc2VkIGNoYXJhY3RlcnMgbGlrZSBMYWR5IEJyYWNrbmVsbCBhbmQgTWlzcyBQcmlzbS4NCg0KTGV0J3MgdGFrZSBhIGRpZmZlcmVudCBhcHByb2FjaC4gWW91IGtub3cgdGhlIGNoYXJhY3RlcnMgbmFtZXMgZnJvbSB0aGUgcGxheSBpbnRyb2R1Y3Rpb24uIFNvLCB0cnkgc3BlY2lmaWNhbGx5IGxvb2tpbmcgZm9yIGxpbmVzIHRoYXQgc3RhcnQgd2l0aCB0aGVpciBuYW1lcy4gWW91J2xsIGZpbmQgdGhlIG9yMSgpIGZ1bmN0aW9uIGZyb20gdGhlIHJlYnVzIHBhY2thZ2UgaGVscGZ1bC4gSXQgc3BlY2lmaWVzIGFsdGVybmF0aXZlcyBidXQgcmF0aGVyIHRoYW4gZWFjaCBhbHRlcm5hdGl2ZSBiZWluZyBhbiBhcmd1bWVudCBsaWtlIGluIG9yKCksIHlvdSBjYW4gcGFzcyBpbiBhIHZlY3RvciBvZiBhbHRlcm5hdGl2ZXMuDQoNCmBgYHtyfQ0KIyBDcmVhdGUgdmVjdG9yIG9mIGNoYXJhY3RlcnMNCmNoYXJhY3RlcnMgPC0gYygiQWxnZXJub24iLCAiSmFjayIsICJMYW5lIiwgIkNlY2lseSIsICJHd2VuZG9sZW4iLCAiQ2hhc3VibGUiLCANCiAgIk1lcnJpbWFuIiwgIkxhZHkgQnJhY2tuZWxsIiwgIk1pc3MgUHJpc20iKQ0KDQojIE1hdGNoIHN0YXJ0LCB0aGVuIGNoYXJhY3RlciBuYW1lLCB0aGVuIC4NCnBhdHRlcm5fMyA8LSBTVEFSVCAlUiUgb3IxKGNoYXJhY3RlcnMpICVSJSBET1QNCg0KIyBWaWV3IG1hdGNoZXMgb2YgcGF0dGVybl8zDQojIHN0cl92aWV3KHBsYXlfbGluZXMsIHBhdHRlcm5fMywgbWF0Y2ggPSBUUlVFKQ0KICANCiMgVmlldyBub24tbWF0Y2hlcyBvZiBwYXR0ZXJuXzMNCiMgc3RyX3ZpZXcocGxheV9saW5lcywgcGF0dGVybl8zLCBtYXRjaCA9IEZBTFNFKQ0KDQojIFB1bGwgb3V0IG1hdGNoZXMNCmxpbmVzIDwtIHN0cl9zdWJzZXQocGxheV9saW5lcywgcGF0dGVybl8zKQ0KDQojIEV4dHJhY3QgbWF0Y2ggZnJvbSBsaW5lcw0Kd2hvIDwtIHN0cl9leHRyYWN0KGxpbmVzLCBwYXR0ZXJuXzMpDQoNCiMgTGV0J3Mgc2VlIHdoYXQgd2UgaGF2ZQ0KdW5pcXVlKHdobykNCg0KIyBDb3VudCBsaW5lcyBwZXIgY2hhcmFjdGVyDQp0YWJsZSh3aG8pDQpgYGANCkFsZ2Vybm9uIGFuZCBKYWNrIGdldCB0aGUgbW9zdCBsaW5lcywgbW9yZSB0aGFuIHRlbiB0aW1lcyBtb3JlIHRoYW4gTWVycmltYW4gd2hvIGhhcyB0aGUgZmV3ZXN0LiBJZiB5b3Ugd2VyZSBsb29raW5nIHJlYWxseSBjbG9zZWx5IHlvdSBtaWdodCBoYXZlIG5vdGljZWQgdGhlIHBhdHRlcm4gZGlkbid0IHBpY2sgdXAgdGhlIGxpbmUgSmFjayBhbmQgQWxnZXJub24gW1NwZWFraW5nIHRvZ2V0aGVyLl0gd2hpY2ggeW91IHJlYWxseSBzaG91bGQgYmUgY291bnRpbmcgYXMgYSBsaW5lIGZvciBib3RoIEphY2sgYW5kIEFsZ2Vybm9uLiBPbmUgc29sdXRpb24gbWlnaHQgYmUgdG8gbG9vayBmb3IgdGhlc2UgIlNwZWFraW5nIHRvZ2V0aGVyIiBsaW5lcywgcGFyc2Ugb3V0IHRoZSBjaGFyYWN0ZXJzLCBhbmQgYWRkIHRvIHlvdXIgY291bnRzLg0KDQojIyBBIGNhc2Ugc3R1ZHkgb24gY2FzZQ0KDQojIyMgQ2hhbmdpbmcgY2FzZSB0byBlYXNlIG1hdGNoaW5nDQoNCkEgc2ltcGxlIHNvbHV0aW9uIHRvIHdvcmtpbmcgd2l0aCBzdHJpbmdzIGluIG1peGVkIGNhc2UsIGlzIHRvIHNpbXBseSB0cmFuc2Zvcm0gdGhlbSBpbnRvIGFsbCBsb3dlciBvciBhbGwgdXBwZXIgY2FzZS4gRGVwZW5kaW5nIG9uIHlvdXIgY2hvaWNlLCB5b3UgY2FuIHRoZW4gc3BlY2lmeSB5b3VyIHBhdHRlcm4gaW4gdGhlIHNhbWUgY2FzZS4NCg0KRm9yIGV4YW1wbGUsIHdoaWxlIGxvb2tpbmcgZm9yICJjYXQiIGZpbmRzIG5vIG1hdGNoZXMgaW4gdGhlIGZvbGxvd2luZyBzdHJpbmcsDQpgYGB7cn0NCnggPC0gYygiQ2F0IiwgIkNBVCIsICJjQXQiKSANCnN0cl92aWV3KHgsICJjYXQiKQ0KYGBgDQp0cmFuc2Zvcm1pbmcgdGhlIHN0cmluZyB0byBsb3dlciBjYXNlIGZpcnN0IGVuc3VyZXMgYWxsIHZhcmlhdGlvbnMgbWF0Y2guDQpgYGB7cn0NCnN0cl92aWV3KHN0cl90b19sb3dlcih4KSwgImNhdCIpDQpgYGANClNlZSBpZiB5b3UgY2FuIGZpbmQgdGhlIGNhdGNpZGVudHMgdGhhdCBhbHNvIGludm9sdmVkIGRvZ3MuIFlvdSdsbCBzZWUgYSBuZXcgcmVidXMgZnVuY3Rpb24gY2FsbGVkIHdob2xlX3dvcmQoKS4gVGhlIGFyZ3VtZW50IHRvIHdob2xlX3dvcmQoKSB3aWxsIG9ubHkgbWF0Y2ggaWYgaXQgb2NjdXJzIGFzIGEgd29yZCBvbiBpdHMgb3duLCBmb3IgZXhhbXBsZSB3aG9sZV93b3JkKCJjYXQiKSB3aWxsIG1hdGNoIGNhdCBpbiAiVGhlIGNhdCAiIGFuZCAiY2F0LiIgYnV0IG5vdCBpbiAiY2F0ZXJwaWxsYXIiLg0KYGBge3J9DQpjYXRjaWRlbnRzIDwtIHJlYWRSRFMoImNhdGNpZGVudHMucmRzIikNCg0KIyBjYXRjaWRlbnRzIGhhcyBiZWVuIHByZS1kZWZpbmVkDQpoZWFkKGNhdGNpZGVudHMpDQoNCiMgQ29uc3RydWN0IHBhdHRlcm4gb2YgRE9HIGluIGJvdW5kYXJpZXMNCndob2xlX2RvZ19wYXR0ZXJuIDwtIHdob2xlX3dvcmQoIkRPRyIpDQoNCiMgU2VlIG1hdGNoZXMgdG8gd29yZCBET0cNCnN0cl92aWV3KGNhdGNpZGVudHMsIHdob2xlX2RvZ19wYXR0ZXJuLCBtYXRjaCA9IFRSVUUpDQoNCiMgVHJhbnNmb3JtIGNhdGNpZGVudHMgdG8gdXBwZXIgY2FzZQ0KY2F0Y2lkZW50c191cHBlciA8LSBzdHJfdG9fdXBwZXIoY2F0Y2lkZW50cykNCg0KIyBWaWV3IG1hdGNoZXMgdG8gd29yZCAiRE9HIiBhZ2Fpbg0Kc3RyX3ZpZXcoY2F0Y2lkZW50c191cHBlciwgd2hvbGVfZG9nX3BhdHRlcm4sIG1hdGNoID0gVFJVRSkNCg0KIyBXaGljaCBzdHJpbmdzIG1hdGNoPw0KaGFzX2RvZyA8LSBzdHJfZGV0ZWN0KGNhdGNpZGVudHNfdXBwZXIsIHdob2xlX2RvZ19wYXR0ZXJuKQ0KDQojIFB1bGwgb3V0IG1hdGNoaW5nIHN0cmluZ3MgaW4gb3JpZ2luYWwgDQpjYXRjaWRlbnRzW2hhc19kb2ddDQpgYGANCiMjIyBJZ25vcmluZyBjYXNlIHdoZW4gbWF0Y2hpbmcNCg0KUmF0aGVyIHRoYW4gdHJhbnNmb3JtaW5nIHRoZSBpbnB1dCBzdHJpbmdzLCBhbm90aGVyIGFwcHJvYWNoIGlzIHRvIHNwZWNpZnkgdGhhdCB0aGUgbWF0Y2hpbmcgc2hvdWxkIGJlIGNhc2UgaW5zZW5zaXRpdmUuIFRoaXMgaXMgb25lIG9mIHRoZSBvcHRpb25zIHRvIHRoZSBzdHJpbmdyIHJlZ2V4KCkgZnVuY3Rpb24uDQoNClRha2Ugb3VyIHByZXZpb3VzIGV4YW1wbGUsDQpgYGB7cn0NCnggPC0gYygiQ2F0IiwgIkNBVCIsICJjQXQiKSANCnN0cl92aWV3KHgsICJjYXQiKQ0KYGBgDQpvIG1hdGNoIHRoZSBwYXR0ZXJuIGNhdCBpbiBhIGNhc2UgaW5zZW5zaXRpdmUgd2F5LCB3ZSB3cmFwIG91ciBwYXR0ZXJuIGluIHJlZ2V4KCkgYW5kIHNwZWNpZnkgdGhlIGFyZ3VtZW50IGlnbm9yZV9jYXNlID0gVFJVRSwNCmBgYHtyfQ0Kc3RyX3ZpZXcoeCwgc3RyaW5ncjo6cmVnZXgoImNhdCIsIGlnbm9yZV9jYXNlID0gVFJVRSkpDQpgYGANCk5vdGljZSB0aGF0IHRoZSBtYXRjaGVzIHJldGFpbiB0aGVpciBvcmlnaW5hbCBjYXNlIGFuZCBhbnkgdmFyaWFudCBvZiBjYXQgbWF0Y2hlcy4NCmBgYHtyfQ0KIyBWaWV3IG1hdGNoZXMgdG8gIlRSSVAiDQpzdHJfdmlldyhjYXRjaWRlbnRzLCAiVFJJUCIsIG1hdGNoID0gVFJVRSkNCg0KIyBDb25zdHJ1Y3QgY2FzZSBpbnNlbnNpdGl2ZSBwYXR0ZXJuDQp0cmlwX3BhdHRlcm4gPC0gc3RyaW5ncjo6cmVnZXgoIlRSSVAiLCBpZ25vcmVfY2FzZSA9IFRSVUUpDQoNCiMgVmlldyBjYXNlIGluc2Vuc2l0aXZlIG1hdGNoZXMgdG8gIlRSSVAiDQpzdHJfdmlldyhjYXRjaWRlbnRzLCB0cmlwX3BhdHRlcm4sIG1hdGNoID0gVFJVRSkNCg0KIyBHZXQgc3Vic2V0IG9mIG1hdGNoZXMNCnRyaXAgPC0gc3RyX3N1YnNldChjYXRjaWRlbnRzLCB0cmlwX3BhdHRlcm4pDQoNCiMgRXh0cmFjdCBtYXRjaGVzDQpzdHJfZXh0cmFjdCh0cmlwLCB0cmlwX3BhdHRlcm4pDQpgYGANCiMjIyBGaXhpbmcgY2FzZSBwcm9ibGVtcw0KDQpGaW5hbGx5LCB5b3UgbWlnaHQgd2FudCB0byB0cmFuc2Zvcm0gc3RyaW5ncyB0byBhIGNvbW1vbiBjYXNlLiBZb3UndmUgc2VlbiB5b3UgY2FuIHVzZSBzdHJfdG9fdXBwZXIoKSBhbmQgc3RyX3RvX2xvd2VyKCksIGJ1dCB0aGVyZSBpcyBhbHNvIHN0cl90b190aXRsZSgpIHdoaWNoIHRyYW5zZm9ybXMgdG8gdGl0bGUgY2FzZSwgaW4gd2hpY2ggZXZlcnkgd29yZCBzdGFydHMgd2l0aCBhIGNhcGl0YWwgbGV0dGVyLg0KDQpUaGlzIGlzIGFub3RoZXIgc2l0dWF0aW9uIHdoZXJlIHN0cmluZ2kgZnVuY3Rpb25zIG9mZmVyIHNsaWdodGx5IG1vcmUgZnVuY3Rpb25hbGl0eSB0aGFuIHRoZSBzdHJpbmdyIGZ1bmN0aW9ucy4gVGhlIHN0cmluZ2kgZnVuY3Rpb24gc3RyaV90cmFuc190b3RpdGxlKCkgYWxsb3dzIGEgc3BlY2lmaWNhdGlvbiBvZiB0aGUgdHlwZSB3aGljaCwgYnkgZGVmYXVsdCwgaXMgIndvcmQiLCByZXN1bHRpbmcgaW4gdGl0bGUgY2FzZSwgYnV0IGNhbiBhbHNvIGJlICJzZW50ZW5jZSIgdG8gZ2l2ZSBzZW50ZW5jZSBjYXNlOiBvbmx5IHRoZSBmaXJzdCB3b3JkIGluIGVhY2ggc2VudGVuY2UgaXMgY2FwaXRhbGl6ZWQuDQpgYGB7cn0NCmxpYnJhcnkoc3RyaW5naSkNCg0KIyBHZXQgZmlyc3QgZml2ZSBjYXRjaWRlbnRzDQpjYXQ1IDwtIGNhdGNpZGVudHNbMTo1XQ0KDQojIFRha2UgYSBsb29rIGF0IG9yaWdpbmFsDQp3cml0ZUxpbmVzKGNhdDUpDQoNCiMgVHJhbnNmb3JtIHRvIHRpdGxlIGNhc2UNCndyaXRlTGluZXMoc3RyX3RvX3RpdGxlKGNhdDUpKQ0KDQojIFRyYW5zZm9ybSB0byB0aXRsZSBjYXNlIHdpdGggc3RyaW5naQ0Kd3JpdGVMaW5lcyhzdHJpX3RyYW5zX3RvdGl0bGUoY2F0NSkpDQoNCiMgVHJhbnNmb3JtIHRvIHNlbnRlbmNlIGNhc2Ugd2l0aCBzdHJpbmdpDQp3cml0ZUxpbmVzKHN0cmlfdHJhbnNfdG90aXRsZShjYXQ1LCB0eXBlID0gInNlbnRlbmNlIikpDQpgYGANCg0K