Motivation

stringr is a super useful package for dealing with character strings. We will demonstrate how to wrangle and manipulate character strings by importing a pdf.

If this tutorial convinces you that stringr is awesome 😆… you can apparently buy wall art of the hex sticker and decorate your home or office, at www.redbubble.com:

We are going to show an example of wrangling character strings from the pdf file for this article.

This article evaluated food consumption patterns in 195 countries for 15 different dietary risk factors that have probable associations with non-communicable disease (NCD). If you are interested in more about this, stay tuned for our case study.

This example will involve using many of the functions of the stringr package. This package is part of the Tidyverse. The Tidyverse is a library of packages created by RStudio. These packages make data science in R especially efficient.

Learning Objectives

  1. You will be able to identify when stringr might be useful for particular kinds of data.

  2. You will know how to use some very useful stringr functions like:

Function Use
str_replace() replace or exchange a pattern of characters for another
str_split() split or divide strings of any size (words/sentences/paragraphs/) into substrings
str_subset() select part of a string based on a characteristic
str_count() count the occurrence of a specific character
str_which() identify where an occurrence of a specific character occurs
str_remove() remove characters from your strings
str_trim() remove leading and tailing white space
str_squish() remove all white space

For information on other functions see here.

  1. You will know how to work with regular expressions.

  2. You will be able to name other packages that are useful for wrangling data that contains characters.

We will begin by loading the packages that we will need:

What are the data?

An article was recently published in the lancet journal that evaluates global dietary trends and the relationship of these dietary factors with mortality and fertility.

This article includes a table that contains dietary guidelines for dietary factors that are particularly associated with health risk.

Data Import

We are interested in this table on page 3 of the article:

First let’s import the PDF using the pdftools package.

We can use the base summary() function to get a sense of what the data looks like. By base we mean that these functions are part of the base package and are loaded automatically.Thus library(base) is not required.

   Length     Class      Mode 
       15 character character 

We can see that we have 15 different character strings. Each one contains the text on each of the 15 different pages of the PDF.

We can get similar results using the glimpse() function of the dplyr package (it is also in the tibble package).

 chr [1:15] "                                                                                                               "| __truncated__ ...

We will be using the %>% pipe for sequential steps in our code later on. This will make more sense when we have multiple sequential steps using the same data object.

We could do the same code as above using this notation. For example we first grab the paper object, then we glimpse it.

 chr [1:15] "                                                                                                               "| __truncated__ ...

Data Wrangling

Again, the table we are interested in is on the third page, so let’s grab just that portion of the PDF.

Here is what the top of this page looks like before the table:

   Length     Class      Mode 
        1 character character 

Here we can see that the table object now contains the text from the 3rd page as a single large character string.

 chr "                                                                                                                                                                                                    Articles\nin systolic blood pressure, and then estimated the                                           Disease-specific deaths and disability-adjusted\nrelationship between change in systolic blood pressure                                       life-years\nand disease outcomes.14                                                                      Data on disease-specific deaths and disability-adjusted\n                                                                                             life-years (DALYs) by age, sex, country, and year were\nOptimal level of intake        "| __truncated__

The text is difficult to read because of the column structure in the pdf. Now let’s try to grab just the text in the table.

One way to approach this is to split the string by some pattern that we notice in the table.

Only the capitalized form of the word “Diet” appears to be within the table, and is not present in the preceding text (although “diet” is). All the rows of interest of the table appear to start with the word “Diet”.

Let’s use the str_split() function of the stringr package to split the data within the object called tableby the word “Diet”. Only lines from page 3 that contain the word Diet will be selected (and not “diet” as this function is case-sensitive). Each section of the text that contains “Diet” will be split into individual pieces every time the world “Diet” occurs and the word itself will be removed.

In this case we are also using the magrittr assignment pipe or double pipe that looks like this %<>%. This allows us use the table data as input to the later steps but also reassign the output to the same data object name.

Using the base::summary() and dplyr::glimpse() function we can see that we created a list of the 17 rows in the table that contain the word “Diet”.

     Length Class  Mode     
[1,] 17     -none- character

We can see that we start with the row that contains “Diet low in fruits”.

List of 1
 $ : chr [1:17] "                                                                                                               "| __truncated__ " low in fruits                  Mean daily consumption of fruits (fresh, frozen, cooked, canned, or dried fruit"| __truncated__ " low in vegetables              Mean daily consumption of vegetables (fresh, frozen, cooked, canned, or dried v"| __truncated__ " low in legumes                 Mean daily consumption of legumes (fresh, frozen, cooked, canned, or dried legu"| __truncated__ ...

RStudio creates really helpful cheat sheets like this one which shows you all the major functions in stringr. You can download others here.

You can see that we could have also used the str_split_fixed() function which would also separate the substrings into different columns of a matrix.

Note: we would need to know the number of substrings or pieces that we would like returned.

For example…

If we used the fixed version, we will create 3 vectors of a matrix with the first 3 strings that would be created when dividing the large string based on the first 3 occurrences of “Diet”.

[1] "matrix"

We can also specify the number of splits with the str_split(), but this will create a list of substrings, not a matrix.

[1] "list"

For more information about str_split() see here and here.

Now, back to our single list of 17 character strings.

Let’s separate the values within the list using the base unlist function, this will allow us to easily select the different substrings within the object called table.

   Length     Class      Mode 
       17 character character 

It’s important to realize that the first split will include the text before the first occurrence of Diet as the first value in the output. We could use the first() function of the dplyr package to look at this value. However, we will suppress the output as this is quite large.

Instead we can take a look at the second element of the list. using the nth() function of dplyr.

[1] " low in fruits                  Mean daily consumption of fruits (fresh, frozen, cooked, canned, or dried fruits, excluding              250 g (200–300) per day                           94·9\n                                       fruit juices and salted or pickled fruits)\n   "

Indeed this looks like the first row of interest in our table:

Using the last() and the nth() functions of the dplyr package we can take a look at the last values of the list.

[1] " high in sodium                 24 h urinary sodium measured in g per day                                                                   3 g (1–5) per day*                             26·2\n  *To reflect the uncertainty in existing evidence on optimal level of intake for sodium, 1–5 g per day was considered as the uncertainty range for the optimal level of sodium where less than 2·3 g per day is the\n  intake level of sodium associated with the lowest level of blood pressure in randomised controlled trials and 4–5 g per day is the level of sodium intake associated with the lowest risk of cardiovascular disease in\n  observational studies.\n  Table: "
[1] "ary risk factor exposure definitions, optimal level, and data representativeness index, 1990–2017\nwww.thelancet.com Published online April 3, 2019 http://dx.doi.org/10.1016/S0140-6736(19)30041-8                                                                                                                         3\n"

Therefore, we don’t need this part of the table or the text before the table if we just want the consumption recommendations.

So we will select the 2nd through the second to last of the substrings. Since we have 17 substrings, we will select the 2nd through the 16th. However a better way to do this rather than selecting by index, would be to select phrases that are unique to the text within the table that we want. We will use the str_subset() function of stringr package to select the table rows with consumption guidelines. Most of the rows have the phrase “Mean daily consumption”, however, there are other phrases for some of the rows, including “Mean daily intake” and “24 h sodium.” So we will subset for each of these phrases.

Notice that we separate the different patterns to look for using vertical bar character “|” and that all of the patterns are within quotation marks together.

Question opportunity:

  1. What other string patterns could you use to subset the rows of the table that we want?

  2. Why might it be better to subset based on the text rather than the index?

Now the first row is what we want:

[1] " low in fruits                  Mean daily consumption of fruits (fresh, frozen, cooked, canned, or dried fruits, excluding              250 g (200–300) per day                           94·9\n                                       fruit juices and salted or pickled fruits)\n   "

And the last row is what we want:

[1] " high in sodium                 24 h urinary sodium measured in g per day                                                                   3 g (1–5) per day*                             26·2\n  *To reflect the uncertainty in existing evidence on optimal level of intake for sodium, 1–5 g per day was considered as the uncertainty range for the optimal level of sodium where less than 2·3 g per day is the\n  intake level of sodium associated with the lowest level of blood pressure in randomised controlled trials and 4–5 g per day is the level of sodium intake associated with the lowest risk of cardiovascular disease in\n  observational studies.\n  Table: "

Notice that there the decimal points from the pdf are being recognized as an interpunct instead of a period or decimal. An interpunct is a centered dot, as opposed to a period or decimal that is aligned to the bottom of the line.

The interpunct was previously used to separate words in certain languages, like ancient Latin.

[source]

You can produce an interpunct on a mac like this:

[source]

It is important to replace these for later when we want these values to be converted from character strings to numeric. We will again use the stringr package. This time we will use the str_replace_all() function which replaces all instances of a pattern in an individual string. In this case we want to replace all instances of the interpunct with a decimal point.

Now we will try to split the strings for each row based on the presence of 2 spaces to create the columns of the table, as there appears to be larger than a space between the columns to create substrings. The substrings will be separated by quotes.

The second page of the stringr cheat sheet has more information about using “Special Characters” in stringr. For example \\s is interpreted as a space as the \\ indicates that the s should be interpreted as a special character and not simply the letter s. The {2,} indicates 2 or more spaces, while {2} would indicate exactly 2 spaces.

So here we will separate the substrings into columns by 2 more more spaces:

List of 15
 $ : chr [1:6] " low in fruits" "Mean daily consumption of fruits (fresh, frozen, cooked, canned, or dried fruits, excluding" "250 g (200–300) per day" "94.9" ...
 $ : chr [1:7] " low in vegetables" "Mean daily consumption of vegetables (fresh, frozen, cooked, canned, or dried vegetables," "360 g (290–430) per day" "94.9" ...
 $ : chr [1:5] " low in legumes" "Mean daily consumption of legumes (fresh, frozen, cooked, canned, or dried legumes)" "60 g (50–70) per day" "94.9" ...
 $ : chr [1:7] " low in whole grains" "Mean daily consumption of whole grains (bran, germ, and endosperm in their natural" "125 g (100–150) per day" "94.9" ...
 $ : chr [1:5] " low in nuts and seeds" "Mean daily consumption of nut and seed foods" "21 g (16–25) per day" "94.9" ...
 $ : chr [1:6] " low in milk" "Mean daily consumption of milk including non-fat, low-fat, and full-fat milk, excluding soy" "435 g (350–520) per day" "94.9" ...
 $ : chr [1:6] " high in red meat" "Mean daily consumption of red meat (beef, pork, lamb, and goat, but excluding poultry, fish," "23 g (18–27) per day" "94.9" ...
 $ : chr [1:6] " high in processed meat" "Mean daily consumption of meat preserved by smoking, curing, salting, or addition of" "2 g (0–4) per day" "36.9" ...
 $ : chr [1:6] " high in sugar-sweetened Mean daily consumption of beverages with ≥50 kcal per 226.8 serving, including carbonated" "3 g (0–5) per day" "36.9" "beverages" ...
 $ : chr [1:6] " low in fibre" "Mean daily intake of fibre from all sources including fruits, vegetables, grains, legumes, and" "24 g (19–28) per day" "94.9" ...
 $ : chr [1:5] " low in calcium" "Mean daily intake of calcium from all sources, including milk, yogurt, and cheese" "1.25 g (1.00–1.50) per day" "94.9" ...
 $ : chr [1:5] " low in seafood omega-3 Mean daily intake of eicosapentaenoic acid and docosahexaenoic acid" "250 mg (200–300) per day" "94.9" "fatty acids" ...
 $ : chr [1:7] " low in polyunsaturated" "Mean daily intake of omega-6 fatty acids from all sources, mainly liquid vegetable oils," "11% (9–13) of total daily energy" "94.9" ...
 $ : chr [1:6] " high in trans fatty acids" "Mean daily intake of trans fat from all sources, mainly partially hydrogenated vegetable oils" "0.5% (0.0–1.0) of total daily energy" "36.9" ...
 $ : chr [1:8] " high in sodium" "24 h urinary sodium measured in g per day" "3 g (1–5) per day*" "26.2" ...

If we look closely, we can see that the sugar-sweetened beverage and the seafood category had only one space between the first and second columns - the columns about the dietary category and the one that describes in more detail what the consumption suggestion is about.

The values for these two columns appear to be together still in the same substring for these two categories. There are no quotation marks adjacent to the word "Mean".

Here you can see how the next substring should have started with the word "Mean" by the new inclusion of a quotation mark ".

We can add an extra space in front of the word "Mean" for these particular categories and then try splitting again.

Since we originally split based on 2 or more spaces, we can just add a space in front of the word “Mean” for all the table strings and then try subsetting again. We can use the str_which() function of the stringr package to find the index of these particular cases.

[1]  9 12

Here we can see just those strings that match the pattern:

[1] " high in sugar-sweetened Mean daily consumption of beverages with ≥50 kcal per 226.8 serving, including carbonated                          3 g (0–5) per day                              36.9\n   beverages                           beverages, sodas, energy drinks, fruit drinks, but excluding 100% fruit and vegetable juices\n   "
[2] " low in seafood omega-3 Mean daily intake of eicosapentaenoic acid and docosahexaenoic acid                                               250 mg (200–300) per day                         94.9\n   fatty acids\n   "                                                                                                                     

Now we can replace these values within the table object after adding a space in front of “Mean”.

And now we can try splitting again by 2 or more spaces:

We could also just add a space in front of all the values of “Mean” in the table since the split was performed based on 2 or more spaces. Thus the other elements in table would also be split just as before despite the additional space.

List of 15
 $ : chr [1:6] " low in fruits" "Mean daily consumption of fruits (fresh, frozen, cooked, canned, or dried fruits, excluding" "250 g (200–300) per day" "94.9" ...
 $ : chr [1:7] " low in vegetables" "Mean daily consumption of vegetables (fresh, frozen, cooked, canned, or dried vegetables," "360 g (290–430) per day" "94.9" ...
 $ : chr [1:5] " low in legumes" "Mean daily consumption of legumes (fresh, frozen, cooked, canned, or dried legumes)" "60 g (50–70) per day" "94.9" ...
 $ : chr [1:7] " low in whole grains" "Mean daily consumption of whole grains (bran, germ, and endosperm in their natural" "125 g (100–150) per day" "94.9" ...
 $ : chr [1:5] " low in nuts and seeds" "Mean daily consumption of nut and seed foods" "21 g (16–25) per day" "94.9" ...
 $ : chr [1:6] " low in milk" "Mean daily consumption of milk including non-fat, low-fat, and full-fat milk, excluding soy" "435 g (350–520) per day" "94.9" ...
 $ : chr [1:6] " high in red meat" "Mean daily consumption of red meat (beef, pork, lamb, and goat, but excluding poultry, fish," "23 g (18–27) per day" "94.9" ...
 $ : chr [1:6] " high in processed meat" "Mean daily consumption of meat preserved by smoking, curing, salting, or addition of" "2 g (0–4) per day" "36.9" ...
 $ : chr [1:7] " high in sugar-sweetened" "Mean daily consumption of beverages with ≥50 kcal per 226.8 serving, including carbonated" "3 g (0–5) per day" "36.9" ...
 $ : chr [1:6] " low in fibre" "Mean daily intake of fibre from all sources including fruits, vegetables, grains, legumes, and" "24 g (19–28) per day" "94.9" ...
 $ : chr [1:5] " low in calcium" "Mean daily intake of calcium from all sources, including milk, yogurt, and cheese" "1.25 g (1.00–1.50) per day" "94.9" ...
 $ : chr [1:6] " low in seafood omega-3" "Mean daily intake of eicosapentaenoic acid and docosahexaenoic acid" "250 mg (200–300) per day" "94.9" ...
 $ : chr [1:7] " low in polyunsaturated" "Mean daily intake of omega-6 fatty acids from all sources, mainly liquid vegetable oils," "11% (9–13) of total daily energy" "94.9" ...
 $ : chr [1:6] " high in trans fatty acids" "Mean daily intake of trans fat from all sources, mainly partially hydrogenated vegetable oils" "0.5% (0.0–1.0) of total daily energy" "36.9" ...
 $ : chr [1:8] " high in sodium" "24 h urinary sodium measured in g per day" "3 g (1–5) per day*" "26.2" ...

Looks better!

We want just the first (the food category) and third column (the optimal consumption amount suggested) for each row in the table.

We can use the map function of the purrr package to accomplish this.

The map function allows us to perform the same action multiple times across each element within an object.

This following will allow us to select the 1st or 3rd substring from each element of the table object.

[[1]]
[1] " low in fruits"

[[2]]
[1] " low in vegetables"

[[3]]
[1] " low in legumes"

[[4]]
[1] " low in whole grains"

[[5]]
[1] " low in nuts and seeds"

[[6]]
[1] " low in milk"
[[1]]
[1] "250 g (200–300) per day"

[[2]]
[1] "360 g (290–430) per day"

[[3]]
[1] "60 g (50–70) per day"

[[4]]
[1] "125 g (100–150) per day"

[[5]]
[1] "21 g (16–25) per day"

[[6]]
[1] "435 g (350–520) per day"

Now we will create a tibble using this data. However, currently both category and amount are of class list. To create a tibble we need to unlist the data to create vectors.

[1] "list"
[1] "character"

 [1] " low in fruits"             " low in vegetables"        
 [3] " low in legumes"            " low in whole grains"      
 [5] " low in nuts and seeds"     " low in milk"              
 [7] " high in red meat"          " high in processed meat"   
 [9] " high in sugar-sweetened"   " low in fibre"             
[11] " low in calcium"            " low in seafood omega-3"   
[13] " low in polyunsaturated"    " high in trans fatty acids"
[15] " high in sodium"           
 [1] "250 g (200–300) per day"             
 [2] "360 g (290–430) per day"             
 [3] "60 g (50–70) per day"                
 [4] "125 g (100–150) per day"             
 [5] "21 g (16–25) per day"                
 [6] "435 g (350–520) per day"             
 [7] "23 g (18–27) per day"                
 [8] "2 g (0–4) per day"                   
 [9] "3 g (0–5) per day"                   
[10] "24 g (19–28) per day"                
[11] "1.25 g (1.00–1.50) per day"          
[12] "250 mg (200–300) per day"            
[13] "11% (9–13) of total daily energy"    
[14] "0.5% (0.0–1.0) of total daily energy"
[15] "3 g (1–5) per day*"                  

We could have done all of this at once in one command like this:

Now we will create a tibble, which is an important data frame structure in the tidyverse which allows us to use other packages in the tidyverse with our data.

We will name our tibble columns now as we create our tibble using the tibble() function of both the tidyr and the tibble packages, as names are required in tibbles.

# A tibble: 15 x 2
   category                     amount                              
   <chr>                        <chr>                               
 1 " low in fruits"             250 g (200–300) per day             
 2 " low in vegetables"         360 g (290–430) per day             
 3 " low in legumes"            60 g (50–70) per day                
 4 " low in whole grains"       125 g (100–150) per day             
 5 " low in nuts and seeds"     21 g (16–25) per day                
 6 " low in milk"               435 g (350–520) per day             
 7 " high in red meat"          23 g (18–27) per day                
 8 " high in processed meat"    2 g (0–4) per day                   
 9 " high in sugar-sweetened"   3 g (0–5) per day                   
10 " low in fibre"              24 g (19–28) per day                
11 " low in calcium"            1.25 g (1.00–1.50) per day          
12 " low in seafood omega-3"    250 mg (200–300) per day            
13 " low in polyunsaturated"    11% (9–13) of total daily energy    
14 " high in trans fatty acids" 0.5% (0.0–1.0) of total daily energy
15 " high in sodium"            3 g (1–5) per day*                  

Looking pretty good!

However, we want to separate the different amounts within the amount column.

Recall what the original table looked like:

Separating values within a variable

We can use the tidyr::separate() function to separate the data within the amount column into three new columns based on the optimal level and the optimal range. We can separate the values based on the open parentheses "(" and the long dash "–" characters.

# A tibble: 6 x 4
  category                 optimal  lower upper       
  <chr>                    <chr>    <chr> <chr>       
1 " low in fruits"         "250 g " 200   300) per day
2 " low in vegetables"     "360 g " 290   430) per day
3 " low in legumes"        "60 g "  50    70) per day 
4 " low in whole grains"   "125 g " 100   150) per day
5 " low in nuts and seeds" "21 g "  16    25) per day 
6 " low in milk"           "435 g " 350   520) per day

Let’s Also create a new variable/column in our tibble that indicates the direction that can be harmful for each dietary factor.

# A tibble: 15 x 5
   direction food              optimal   lower upper                     
   <chr>     <chr>             <chr>     <chr> <chr>                     
 1 " low"    fruits            "250 g "  200   300) per day              
 2 " low"    vegetables        "360 g "  290   430) per day              
 3 " low"    legumes           "60 g "   50    70) per day               
 4 " low"    whole grains      "125 g "  100   150) per day              
 5 " low"    nuts and seeds    "21 g "   16    25) per day               
 6 " low"    milk              "435 g "  350   520) per day              
 7 " high"   red meat          "23 g "   18    27) per day               
 8 " high"   processed meat    "2 g "    0     4) per day                
 9 " high"   sugar-sweetened   "3 g "    0     5) per day                
10 " low"    fibre             "24 g "   19    28) per day               
11 " low"    calcium           "1.25 g " 1.00  1.50) per day             
12 " low"    seafood omega-3   "250 mg " 200   300) per day              
13 " low"    polyunsaturated   "11% "    9     13) of total daily energy 
14 " high"   trans fatty acids "0.5% "   0.0   1.0) of total daily energy
15 " high"   sodium            "3 g "    1     5) per day*               

If we wanted to remove the direction variable we could use the purrr::modify_at() function:

Data cleaning with regular expressions

OK, looking better, but we still need a bit of cleaning to remove symbols and extra words from the columns. Some of the extra symbols include: "%", ")" and the "*".

The "*" and the ")" are what we call metacharacters or regular expressions. These are characters that have special meanings.

Now we need the "\\" to indicate that we want these characters to be matched exactly and not interpreted as the meaning of the symbol.

See here for more info about regular expressions in R.

Also here we have a bit of an example using the str_count() function of stringr, which counts the number of instances of a character string. In this case we will look for individual characters but you could also search for words or phrases.

Count the letter t:

[1] "Testing for ts or\ttabs can be tricky.(yes, it really can!*)\n"
[1] 5

Count tabs:

[1] 1

Count parentheses:

[1] 1

Count the occurrence of the astrix:

[1] 1

We also want to make a unit variable so that we can make sure that our units are consistent later.

 [1] "250 g "  "360 g "  "60 g "   "125 g "  "21 g "   "435 g "  "23 g "  
 [8] "2 g "    "3 g "    "24 g "   "1.25 g " "250 mg " "11% "    "0.5% "  
[15] "3 g "   

Notice that the values that are percentages don’t have spaces between the number and the unit. We can separate the optimal values by a space or a percent symbol "%" using "|" to indicate that we want to separate by either. In this case we will lose the “%” and will need to add it back to those values.

We can specify a space using an actual space or \\s.

# A tibble: 15 x 6
   direction food              lower optimal unit  upper                   
   <chr>     <chr>             <chr> <chr>   <chr> <chr>                   
 1 " low"    fruits            200   250     g     300) per day            
 2 " low"    vegetables        290   360     g     430) per day            
 3 " low"    legumes           50    60      g     70) per day             
 4 " low"    whole grains      100   125     g     150) per day            
 5 " low"    nuts and seeds    16    21      g     25) per day             
 6 " low"    milk              350   435     g     520) per day            
 7 " high"   red meat          18    23      g     27) per day             
 8 " high"   processed meat    0     2       g     4) per day              
 9 " high"   sugar-sweetened   0     3       g     5) per day              
10 " low"    fibre             19    24      g     28) per day             
11 " low"    calcium           1.00  1.25    g     1.50) per day           
12 " low"    seafood omega-3   200   250     mg    300) per day            
13 " low"    polyunsaturated   9     11      ""    13) of total daily ener…
14 " high"   trans fatty acids 0.0   0.5     ""    1.0) of total daily ene…
15 " high"   sodium            1     3       g     5) per day*             

Great, so to now we will add “%” to the unit variable for the low in polyunsaturated and high in trans fatty acids rows.

First we need to replace the empty values with NA using the na_if() function of the dplyr package.

# A tibble: 15 x 6
   direction food              lower optimal unit  upper                   
   <chr>     <chr>             <chr> <chr>   <chr> <chr>                   
 1 " low"    fruits            200   250     g     300) per day            
 2 " low"    vegetables        290   360     g     430) per day            
 3 " low"    legumes           50    60      g     70) per day             
 4 " low"    whole grains      100   125     g     150) per day            
 5 " low"    nuts and seeds    16    21      g     25) per day             
 6 " low"    milk              350   435     g     520) per day            
 7 " high"   red meat          18    23      g     27) per day             
 8 " high"   processed meat    0     2       g     4) per day              
 9 " high"   sugar-sweetened   0     3       g     5) per day              
10 " low"    fibre             19    24      g     28) per day             
11 " low"    calcium           1.00  1.25    g     1.50) per day           
12 " low"    seafood omega-3   200   250     mg    300) per day            
13 " low"    polyunsaturated   9     11      <NA>  13) of total daily ener…
14 " high"   trans fatty acids 0.0   0.5     <NA>  1.0) of total daily ene…
15 " high"   sodium            1     3       g     5) per day*             

Then to replace the NA values, we can use the replace_na() function in the tidyr package and the mutate() function of dplyr to specify which values to replace, in this case the NA values within the variable unit. Essentially this variable gets reassigned with the new values, as we mostly think of the mutate() function as creating new variables.

# A tibble: 2 x 6
  direction food              lower optimal unit  upper                    
  <chr>     <chr>             <chr> <chr>   <chr> <chr>                    
1 " low"    polyunsaturated   9     11      %     13) of total daily energy
2 " high"   trans fatty acids 0.0   0.5     %     1.0) of total daily ener…

Let’s also move unit to be the last column. We can use the select() and everything() functions of the dplyr package to do this.

Here you can see Hadley Wickham’s (Chief Scientist at RStudio) explanation for this behavior of select():

https://github.com/tidyverse/dplyr/issues/2838#issuecomment-306062800

To remove all of the remaining extra characters and words we will again use the stringr package. This time we will use the str_remove() function to remove all instances of these characters.

# A tibble: 15 x 6
   direction food              lower optimal upper unit 
   <chr>     <chr>             <chr> <chr>   <chr> <chr>
 1 " low"    fruits            200   250     300   g    
 2 " low"    vegetables        290   360     430   g    
 3 " low"    legumes           50    60      70    g    
 4 " low"    whole grains      100   125     150   g    
 5 " low"    nuts and seeds    16    21      25    g    
 6 " low"    milk              350   435     520   g    
 7 " high"   red meat          18    23      27    g    
 8 " high"   processed meat    0     2       4     g    
 9 " high"   sugar-sweetened   0     3       5     g    
10 " low"    fibre             19    24      28    g    
11 " low"    calcium           1.00  1.25    1.50  g    
12 " low"    seafood omega-3   200   250     300   mg   
13 " low"    polyunsaturated   9     11      13    %    
14 " high"   trans fatty acids 0.0   0.5     1.0   %    
15 " high"   sodium            1     3       5     g    

Nice! that’s pretty clean but we can do a bit more.

Data type conversion

One of the next things to notice about our data is the character classes of our variables.

Notice that the optimal amounts of consumption are currently of class character as indicated by the <chr> just below the column names / variable names of the guidelines tibble:

# A tibble: 15 x 6
   direction food              lower optimal upper unit 
   <chr>     <chr>             <chr> <chr>   <chr> <chr>
 1 " low"    fruits            200   250     300   g    
 2 " low"    vegetables        290   360     430   g    
 3 " low"    legumes           50    60      70    g    
 4 " low"    whole grains      100   125     150   g    
 5 " low"    nuts and seeds    16    21      25    g    
 6 " low"    milk              350   435     520   g    
 7 " high"   red meat          18    23      27    g    
 8 " high"   processed meat    0     2       4     g    
 9 " high"   sugar-sweetened   0     3       5     g    
10 " low"    fibre             19    24      28    g    
11 " low"    calcium           1.00  1.25    1.50  g    
12 " low"    seafood omega-3   200   250     300   mg   
13 " low"    polyunsaturated   9     11      13    %    
14 " high"   trans fatty acids 0.0   0.5     1.0   %    
15 " high"   sodium            1     3       5     g    

To convert these values to numeric we can use the mutate_at() function of the dplyr package.

The mutate_at() function allows us to perform a function on specific columns/variables within a tibble. We need to indicate which variables that we would like to convert using vars(). In this case if we look at the beginning of the guidelines tibble, we can see that optimal, lower and upper should be converted. As these three columns are sequential, we can simply put a : between optimal and upper to indicate that we want all the variables in between these columns to be converted.

# A tibble: 15 x 6
   direction food              lower optimal upper unit 
   <chr>     <chr>             <dbl>   <dbl> <dbl> <chr>
 1 " low"    fruits              200  250    300   g    
 2 " low"    vegetables          290  360    430   g    
 3 " low"    legumes              50   60     70   g    
 4 " low"    whole grains        100  125    150   g    
 5 " low"    nuts and seeds       16   21     25   g    
 6 " low"    milk                350  435    520   g    
 7 " high"   red meat             18   23     27   g    
 8 " high"   processed meat        0    2      4   g    
 9 " high"   sugar-sweetened       0    3      5   g    
10 " low"    fibre                19   24     28   g    
11 " low"    calcium               1    1.25   1.5 g    
12 " low"    seafood omega-3     200  250    300   mg   
13 " low"    polyunsaturated       9   11     13   %    
14 " high"   trans fatty acids     0    0.5    1   %    
15 " high"   sodium                1    3      5   g    

Great! Now these variables are of class <dbl> (stands for double) which indicates that they are numeric. Here is a link for more info on numeric classes in R.

If we had not replaced the "·" interpunct values to a period conversion from character to numeric will be problematic and will result in NA values.

Data value reassignments

We seem to have lost the word "beverages" from the "sugar-sweetened beverages" category, as well as "fatty acids" from the "seafood omega 3 fatty acids", and the "polyunsaturated fatty acids" categories as the full category name was listed on two lines within the table. We would like to replace these values with the full name.

To select the food column we will show you several options. Only a couple will work well with reassigning the data in that particular variable within guidelines without assigning an intermediate data object. We will look using mutate_at(), pull(), select(), and two styles of brackets [,c("variable name")] and [["variablename"]].

The bracket [,c("variable name")] option and the select() option will grab a tibble (data frame) version of the food column out of guidelines. However we can’t start commands with select for assignments.

# A tibble: 15 x 1
   food             
   <chr>            
 1 fruits           
 2 vegetables       
 3 legumes          
 4 whole grains     
 5 nuts and seeds   
 6 milk             
 7 red meat         
 8 processed meat   
 9 sugar-sweetened  
10 fibre            
11 calcium          
12 seafood omega-3  
13 polyunsaturated  
14 trans fatty acids
15 sodium           
# A tibble: 15 x 1
   food             
   <chr>            
 1 fruits           
 2 vegetables       
 3 legumes          
 4 whole grains     
 5 nuts and seeds   
 6 milk             
 7 red meat         
 8 processed meat   
 9 sugar-sweetened  
10 fibre            
11 calcium          
12 seafood omega-3  
13 polyunsaturated  
14 trans fatty acids
15 sodium           

pull() and the bracket [["variable name"]] option in contrast, will grab the vector version of the food data:

 [1] "fruits"            "vegetables"        "legumes"          
 [4] "whole grains"      "nuts and seeds"    "milk"             
 [7] "red meat"          "processed meat"    "sugar-sweetened"  
[10] "fibre"             "calcium"           "seafood omega-3"  
[13] "polyunsaturated"   "trans fatty acids" "sodium"           
 [1] "fruits"            "vegetables"        "legumes"          
 [4] "whole grains"      "nuts and seeds"    "milk"             
 [7] "red meat"          "processed meat"    "sugar-sweetened"  
[10] "fibre"             "calcium"           "seafood omega-3"  
[13] "polyunsaturated"   "trans fatty acids" "sodium"           

The pull function can be very useful when combined with other functions (for example you typically want to use a vector with the str_replace() function), but just like select, we can’t start assignments with pull().

This is not possible and will result in an error:

This will only print the result, but not reassign the food variable values:

 [1] "fruits"                    "vegetables"               
 [3] "legumes"                   "whole grains"             
 [5] "nuts and seeds"            "milk"                     
 [7] "red meat"                  "processed meat"           
 [9] "sugar-sweetened beverages" "fibre"                    
[11] "calcium"                   "seafood omega-3"          
[13] "polyunsaturated"           "trans fatty acids"        
[15] "sodium"                   

Using select() would work as well to print the result (although the result structure is different):

[1] "c(\"fruits\", \"vegetables\", \"legumes\", \"whole grains\", \"nuts and seeds\", \"milk\", \"red meat\", \"processed meat\", \"sugar-sweetened beverages\", \"fibre\", \"calcium\", \"seafood omega-3\", \"polyunsaturated\", \"trans fatty acids\", \"sodium\")"

Question opportunity:

Why do these commands not reassign the food variable values?

The bracket option is great alternative and allows us to reassign the values within guidelines easily. Either of the two styles of brackets: [,c("variable name")] and [["variablename"]] will work.

# A tibble: 15 x 6
   direction food                        lower optimal upper unit 
   <chr>     <chr>                       <dbl>   <dbl> <dbl> <chr>
 1 " low"    fruits                        200  250    300   g    
 2 " low"    vegetables                    290  360    430   g    
 3 " low"    legumes                        50   60     70   g    
 4 " low"    whole grains                  100  125    150   g    
 5 " low"    nuts and seeds                 16   21     25   g    
 6 " low"    milk                          350  435    520   g    
 7 " high"   red meat                       18   23     27   g    
 8 " high"   processed meat                  0    2      4   g    
 9 " high"   sugar-sweetened beverages       0    3      5   g    
10 " low"    fibre                          19   24     28   g    
11 " low"    calcium                         1    1.25   1.5 g    
12 " low"    seafood omega-3 fatty acids   200  250    300   mg   
13 " low"    polyunsaturated                 9   11     13   %    
14 " high"   trans fatty acids               0    0.5    1   %    
15 " high"   sodium                          1    3      5   g    

Finally, the best option is probably the mutate_at() function from dplyr. In this case we need to include ~ in front of the function that we would like to use on the values in our food variables. We also include . as a replacement to reference the data that we want to use within str_replace() (which in this case is the food variable values of guidelines).

Notice we didn’t need this when we previously use mutate_at() with the as.numeric() function. This is because the str_replace() function requires us to specify what data we are using as one of the arguments, while as.numeric() does not.

# A tibble: 15 x 6
   direction food                        lower optimal upper unit 
   <chr>     <chr>                       <dbl>   <dbl> <dbl> <chr>
 1 " low"    fruits                        200  250    300   g    
 2 " low"    vegetables                    290  360    430   g    
 3 " low"    legumes                        50   60     70   g    
 4 " low"    whole grains                  100  125    150   g    
 5 " low"    nuts and seeds                 16   21     25   g    
 6 " low"    milk                          350  435    520   g    
 7 " high"   red meat                       18   23     27   g    
 8 " high"   processed meat                  0    2      4   g    
 9 " high"   sugar-sweetened beverages       0    3      5   g    
10 " low"    fibre                          19   24     28   g    
11 " low"    calcium                         1    1.25   1.5 g    
12 " low"    seafood omega-3 fatty acids   200  250    300   mg   
13 " low"    polyunsaturated fatty acids     9   11     13   %    
14 " high"   trans fatty acids               0    0.5    1   %    
15 " high"   sodium                          1    3      5   g    

This might be considered a better option because it is more readable as to where the food data came from that we are replacing values within.

There is one last minor detail… the direction variable has leading spaces still. We can use str_trim() to fix that! (You could also use str_squish() which removes all white spaces, not just leading spaces)

# A tibble: 15 x 6
   direction food                        lower optimal upper unit 
   <chr>     <chr>                       <dbl>   <dbl> <dbl> <chr>
 1 low       fruits                        200  250    300   g    
 2 low       vegetables                    290  360    430   g    
 3 low       legumes                        50   60     70   g    
 4 low       whole grains                  100  125    150   g    
 5 low       nuts and seeds                 16   21     25   g    
 6 low       milk                          350  435    520   g    
 7 high      red meat                       18   23     27   g    
 8 high      processed meat                  0    2      4   g    
 9 high      sugar-sweetened beverages       0    3      5   g    
10 low       fibre                          19   24     28   g    
11 low       calcium                         1    1.25   1.5 g    
12 low       seafood omega-3 fatty acids   200  250    300   mg   
13 low       polyunsaturated fatty acids     9   11     13   %    
14 high      trans fatty acids               0    0.5    1   %    
15 high      sodium                          1    3      5   g    
# A tibble: 15 x 6
   direction food                        lower optimal upper unit 
   <chr>     <chr>                       <dbl>   <dbl> <dbl> <chr>
 1 low       fruits                        200  250    300   g    
 2 low       vegetables                    290  360    430   g    
 3 low       legumes                        50   60     70   g    
 4 low       whole grains                  100  125    150   g    
 5 low       nuts and seeds                 16   21     25   g    
 6 low       milk                          350  435    520   g    
 7 high      red meat                       18   23     27   g    
 8 high      processed meat                  0    2      4   g    
 9 high      sugar-sweetened beverages       0    3      5   g    
10 low       fibre                          19   24     28   g    
11 low       calcium                         1    1.25   1.5 g    
12 low       seafood omega-3 fatty acids   200  250    300   mg   
13 low       polyunsaturated fatty acids     9   11     13   %    
14 high      trans fatty acids               0    0.5    1   %    
15 high      sodium                          1    3      5   g    

OK! Now we know how much of each dietary factor we generally need for optimal health according to the guidelines used in this article.

What did we learn?

  1. We know when when stringr might be useful for particular kinds of data.

Genomic sequence data, text data etc.

  1. You know how to use some very useful stringr functions like:
Function Use
str_replace() replace or exchange a pattern of characters for another
str_split() split or divide strings of any size (words/sentences/paragraphs/) into substrings
str_subset() select part of a string based on a characteristic
str_count() count the occurrence of a specific character
str_which() identify where an occurrence of a specific character occurs
str_remove() remove characters from your strings
str_trim() remove leading and tailing white space
str_squish() remove all white space
  1. You know how to work with regular expressions.

Don’t forget \\!

  1. You can name other packages that are useful for wrangling data that contains characters.

pdftools

tidyr

dplyr

purrr

LS0tCnRpdGxlOiAiW1ItTGFkaWVzIEJhbHRpbW9yZV0oaHR0cHM6Ly9ybGFkaWVzLWJhbHRpbW9yZS5naXRodWIuaW8vKTogV3JhbmdsaW5nIGEgcGRmIHdpdGggW2BzdHJpbmdyYF0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvKSAiCnN1YnRpdGxlOiBDYXJyaWUgV3JpZ2h0IGh0dHBzOi8vY2Fycmlld3JpZ2h0MTEuZ2l0aHViLmlvLwpjc3M6IHN0eWxlLmNzcwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHNlbGZfY29udGFpbmVkOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHllcwogICAgaGlnaGxpZ2h0OiB0YW5nbwogICAgbnVtYmVyX3NlY3Rpb25zOiBubwogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICBpbmNsdWRlczoKICAgICAgaW5faGVhZGVyOiBoZWFkZXIudGV4CiAgICB0b2M6IHllcwogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwoKLS0tCgoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoaW5jbHVkZSA9IFRSVUUsIGNvbW1lbnQgPSBOQSwgZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgY2FjaGUgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBvdXQud2lkdGggPSAnOTAlJykKbGlicmFyeShoZXJlKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGVtbykKYGBgCgo8c3R5bGU+CiNUT0MgewogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly9wYnMudHdpbWcuY29tL3Byb2ZpbGVfaW1hZ2VzLzEyMzY4NTU3MTUwMTg1NTk0ODgvUHVZQWpUVERfNDAweDQwMC5qcGciKTsKICBiYWNrZ3JvdW5kLXNpemU6IGNvbnRhaW47CiAgcGFkZGluZy10b3A6IDI0MHB4ICFpbXBvcnRhbnQ7CiAgYmFja2dyb3VuZC1yZXBlYXQ6IG5vLXJlcGVhdDsKfQo8L3N0eWxlPgoKCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZy9CYWx0aW1vcmUucG5nIikpCmBgYAoKIyMgTW90aXZhdGlvbgpgc3RyaW5ncmAgaXMgYSBzdXBlciB1c2VmdWwgcGFja2FnZSBmb3IgZGVhbGluZyB3aXRoIGNoYXJhY3RlciBzdHJpbmdzLiBXZSB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0byB3cmFuZ2xlIGFuZCBtYW5pcHVsYXRlIGNoYXJhY3RlciBzdHJpbmdzIGJ5IGltcG9ydGluZyBhIHBkZi4gCmBgYHtyLCBvdXQud2lkdGggPSAiMjAlIiwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIifQppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy9sb2dvLnBuZyIpCmBgYAoKSWYgdGhpcyB0dXRvcmlhbCBjb252aW5jZXMgeW91IHRoYXQgYHN0cmluZ3JgIGlzIGF3ZXNvbWUgYHIgZW1vOjpqaSgiaGFoYSIpYC4uLiB5b3UgY2FuIGFwcGFyZW50bHkgYnV5IHdhbGwgYXJ0IG9mIHRoZSBoZXggc3RpY2tlciBhbmQgZGVjb3JhdGUgeW91ciBob21lIG9yIG9mZmljZSwgYXQgW3d3dy5yZWRidWJibGUuY29tXShodHRwczovL3d3dy5yZWRidWJibGUuY29tL3Blb3BsZS9yc3R1ZGlvLWluYy8pOiAKCgpgYGB7ciwgb3V0LndpZHRoID0gIjgwJSIsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIn0KaW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9paDEucmVkYnViYmxlLm5ldC9pbWFnZS41NDMzNjIxMTQuMjE2NS9jbXAseF9zbWFsbCxnbG9zcyxwcm9kdWN0LDc1MHgxMDAwLnUxLmpwZyIpCmBgYAoKV2UgYXJlIGdvaW5nIHRvIHNob3cgYW4gZXhhbXBsZSBvZiB3cmFuZ2xpbmcgY2hhcmFjdGVyIHN0cmluZ3MgZnJvbSB0aGUgcGRmIGZpbGUgZm9yIHRoaXMgYXJ0aWNsZS4KCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJ0aGVwYXBlci5wbmciKSkKYGBgCgoKVGhpcyBhcnRpY2xlIGV2YWx1YXRlZCBmb29kIGNvbnN1bXB0aW9uIHBhdHRlcm5zIGluIDE5NSBjb3VudHJpZXMgZm9yIDE1IGRpZmZlcmVudCBkaWV0YXJ5IHJpc2sgZmFjdG9ycyB0aGF0IGhhdmUgcHJvYmFibGUgYXNzb2NpYXRpb25zIHdpdGggbm9uLWNvbW11bmljYWJsZSBkaXNlYXNlIChOQ0QpLiAgSWYgeW91IGFyZSBpbnRlcmVzdGVkIGluIG1vcmUgYWJvdXQgdGhpcywgc3RheSB0dW5lZCBmb3Igb3VyIFtjYXNlIHN0dWR5XShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8vKXt0YXJnZXQ9Il9ibGFuayJ9LgoKVGhpcyBleGFtcGxlIHdpbGwgaW52b2x2ZSB1c2luZyBtYW55IG9mIHRoZSBmdW5jdGlvbnMgb2YgdGhlIGBzdHJpbmdyYCBwYWNrYWdlLiBUaGlzIHBhY2thZ2UgaXMgcGFydCBvZiB0aGUgIFtUaWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9LiBUaGUgVGlkeXZlcnNlIGlzIGEgbGlicmFyeSBvZiBwYWNrYWdlcyBjcmVhdGVkIGJ5IFJTdHVkaW8uIFRoZXNlIHBhY2thZ2VzIG1ha2UgZGF0YSBzY2llbmNlIGluIFIgZXNwZWNpYWxseSBlZmZpY2llbnQuCgpgYGB7ciwgb3V0LndpZHRoID0gIjIwJSIsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIn0KaW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly90aWR5dmVyc2UudGlkeXZlcnNlLm9yZy9sb2dvLnBuZyIpCmBgYAoKIyMjIExlYXJuaW5nIE9iamVjdGl2ZXMKCjEpIFlvdSB3aWxsIGJlIGFibGUgdG8gaWRlbnRpZnkgd2hlbiBgc3RyaW5ncmAgbWlnaHQgYmUgdXNlZnVsIGZvciBwYXJ0aWN1bGFyIGtpbmRzIG9mIGRhdGEuCgoyKSBZb3Ugd2lsbCBrbm93IGhvdyB0byB1c2Ugc29tZSB2ZXJ5IHVzZWZ1bCBgc3RyaW5ncmAgZnVuY3Rpb25zIGxpa2U6CgpGdW5jdGlvbiAgIHwgVXNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAotLS0tLS0tLS0tIHwtLS0tLS0tLS0tLS0tCmBzdHJfcmVwbGFjZSgpYCB8IHJlcGxhY2Ugb3IgZXhjaGFuZ2UgYSBwYXR0ZXJuIG9mIGNoYXJhY3RlcnMgZm9yIGFub3RoZXIgCmBzdHJfc3BsaXQoKWAgIHwgc3BsaXQgb3IgZGl2aWRlIHN0cmluZ3Mgb2YgYW55IHNpemUgKHdvcmRzL3NlbnRlbmNlcy9wYXJhZ3JhcGhzLykgaW50byBzdWJzdHJpbmdzCmBzdHJfc3Vic2V0KClgIHwgc2VsZWN0IHBhcnQgb2YgYSBzdHJpbmcgYmFzZWQgb24gYSBjaGFyYWN0ZXJpc3RpYwpgc3RyX2NvdW50KClgIHwgY291bnQgdGhlIG9jY3VycmVuY2Ugb2YgYSBzcGVjaWZpYyBjaGFyYWN0ZXIKYHN0cl93aGljaCgpYCB8IGlkZW50aWZ5IHdoZXJlIGFuIG9jY3VycmVuY2Ugb2YgYSBzcGVjaWZpYyBjaGFyYWN0ZXIgb2NjdXJzIApgc3RyX3JlbW92ZSgpYCB8IHJlbW92ZSBjaGFyYWN0ZXJzIGZyb20geW91ciBzdHJpbmdzCmBzdHJfdHJpbSgpYCB8IHJlbW92ZSBsZWFkaW5nIGFuZCB0YWlsaW5nIHdoaXRlIHNwYWNlIApgc3RyX3NxdWlzaCgpYCB8IHJlbW92ZSBhbGwgd2hpdGUgc3BhY2UKCkZvciBpbmZvcm1hdGlvbiBvbiBvdGhlciBmdW5jdGlvbnMgc2VlIFtoZXJlXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbCkuCgozKSBZb3Ugd2lsbCBrbm93IGhvdyB0byB3b3JrIHdpdGggcmVndWxhciBleHByZXNzaW9ucy4KCjQpIFlvdSB3aWxsIGJlIGFibGUgdG8gbmFtZSBvdGhlciBwYWNrYWdlcyB0aGF0IGFyZSB1c2VmdWwgZm9yIHdyYW5nbGluZyBkYXRhIHRoYXQgY29udGFpbnMgY2hhcmFjdGVycy4KCgpXZSB3aWxsIGJlZ2luIGJ5IGxvYWRpbmcgdGhlIHBhY2thZ2VzIHRoYXQgd2Ugd2lsbCBuZWVkOgoKYGBge3J9CmxpYnJhcnkoaGVyZSkKbGlicmFyeShyZWFkcikKbGlicmFyeShkcGx5cikKbGlicmFyeShwZGZ0b29scykKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KG1hZ3JpdHRyKQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeSh0aWR5cikKYGBgCgoKIyMgV2hhdCBhcmUgdGhlIGRhdGE/CgpBbiBbYXJ0aWNsZV0oaHR0cHM6Ly93d3cudGhlbGFuY2V0LmNvbS9hY3Rpb24vc2hvd1BkZj9waWk9UzAxNDAtNjczNiUyODE5JTI5MzAwNDEtOCl7dGFyZ2V0PSJfYmxhbmsifSB3YXMgcmVjZW50bHkgcHVibGlzaGVkIGluIHRoZSBsYW5jZXQgam91cm5hbCB0aGF0IGV2YWx1YXRlcyBnbG9iYWwgZGlldGFyeSB0cmVuZHMgYW5kIHRoZSByZWxhdGlvbnNoaXAgb2YgdGhlc2UgZGlldGFyeSBmYWN0b3JzIHdpdGggbW9ydGFsaXR5IGFuZCBmZXJ0aWxpdHkuCgpUaGlzIGFydGljbGUgaW5jbHVkZXMgYSB0YWJsZSB0aGF0IGNvbnRhaW5zIGRpZXRhcnkgZ3VpZGVsaW5lcyBmb3IgZGlldGFyeSBmYWN0b3JzIHRoYXQgYXJlIHBhcnRpY3VsYXJseSBhc3NvY2lhdGVkIHdpdGggaGVhbHRoIHJpc2suCgojIyBEYXRhIEltcG9ydAoKV2UgYXJlIGludGVyZXN0ZWQgaW4gdGhpcyB0YWJsZSBvbiBwYWdlIDMgb2YgdGhlIGFydGljbGU6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNzAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiVGFibGUucG5nIikpCmBgYAoKCkZpcnN0IGxldCdzIGltcG9ydCB0aGUgUERGIHVzaW5nIHRoZSBgcGRmdG9vbHNgIHBhY2thZ2UuCmBgYHtyfQpwYXBlcjwtcGRmdG9vbHM6OnBkZl90ZXh0KGhlcmUoImRvY3MiLCAiQWZzaGluIGV0IGFsLiAyMDE5IC0gSGVhbHRoIGVmZmVjdHMgb2YgZGlldGFyeSByaXNrcyBpbiAxOTUgY291bnRyaWVzLCAgLi4uIDE3IC0gYSBzeXN0ZW1hdGljIGFuYWx5c2lzIGZvciB0aGUgR2xvYmFsIEJ1cmRlbiBvZiBEaXNlYXNlIFN0dWR5IDIwMTcucGRmIikpCmBgYAoKV2UgY2FuIHVzZSB0aGUgYGJhc2VgIGBzdW1tYXJ5KClgIGZ1bmN0aW9uIHRvIGdldCBhIHNlbnNlIG9mIHdoYXQgdGhlIGRhdGEgbG9va3MgbGlrZS4gQnkgYGJhc2VgIHdlIG1lYW4gdGhhdCB0aGVzZSBmdW5jdGlvbnMgYXJlIHBhcnQgb2YgdGhlIGBiYXNlYCBwYWNrYWdlIGFuZCBhcmUgbG9hZGVkIGF1dG9tYXRpY2FsbHkuVGh1cyBgbGlicmFyeShiYXNlKWAgaXMgbm90IHJlcXVpcmVkLgoKYGBge3J9CnN1bW1hcnkocGFwZXIpCiNUaGlzIGlzIGVxdWl2YWxlbnQgdG8gdGhlIGZvbGxvd2luZywgYnV0IHRoaXMgaXMgdW5lY2Vzc2FyeToKI2Jhc2U6OnN1bW1hcnkocGFwZXIpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHdlIGhhdmUgMTUgZGlmZmVyZW50IGNoYXJhY3RlciBzdHJpbmdzLiBFYWNoIG9uZSBjb250YWlucyB0aGUgdGV4dCBvbiBlYWNoIG9mIHRoZSAxNSBkaWZmZXJlbnQgcGFnZXMgb2YgdGhlIFBERi4KCldlIGNhbiBnZXQgc2ltaWxhciByZXN1bHRzIHVzaW5nIHRoZSBgZ2xpbXBzZSgpYCBmdW5jdGlvbiBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlIChpdCBpcyBhbHNvIGluIHRoZSBgdGliYmxlYCBwYWNrYWdlKS4KCmBgYHtyfQpnbGltcHNlKHBhcGVyKQpgYGAKCldlIHdpbGwgYmUgdXNpbmcgdGhlIGAlPiVgIHBpcGUgZm9yIHNlcXVlbnRpYWwgc3RlcHMgaW4gb3VyIGNvZGUgbGF0ZXIgb24uClRoaXMgd2lsbCBtYWtlIG1vcmUgc2Vuc2Ugd2hlbiB3ZSBoYXZlIG11bHRpcGxlIHNlcXVlbnRpYWwgc3RlcHMgdXNpbmcgdGhlIHNhbWUgZGF0YSBvYmplY3QuCgpXZSBjb3VsZCBkbyB0aGUgc2FtZSBjb2RlIGFzIGFib3ZlIHVzaW5nIHRoaXMgbm90YXRpb24uIEZvciBleGFtcGxlIHdlIGZpcnN0IGdyYWIgdGhlIHBhcGVyIG9iamVjdCwgdGhlbiB3ZSBnbGltcHNlIGl0LgoKYGBge3J9CnBhcGVyICU+JQogIGdsaW1wc2UoKQpgYGAKCgojIyBEYXRhIFdyYW5nbGluZwoKQWdhaW4sIHRoZSB0YWJsZSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBpcyBvbiB0aGUgdGhpcmQgcGFnZSwgc28gbGV0J3MgZ3JhYiBqdXN0IHRoYXQgcG9ydGlvbiBvZiB0aGUgUERGLgoKSGVyZSBpcyB3aGF0IHRoZSB0b3Agb2YgdGhpcyBwYWdlIGxvb2tzIGxpa2UgYmVmb3JlIHRoZSB0YWJsZToKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI3MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJwYWdlMy5wbmciKSkKYGBgCgpgYGB7cn0KI0hlcmUgd2Ugd2lsbCBzZWxlY3QgdGhlIDNyZCB2YWx1ZSBpbiB0aGUgcGFwZXIgb2JqZWN0CnRhYmxlIDwtIHBhcGVyWzNdCgpzdW1tYXJ5KHRhYmxlKQoKYGBgCgpIZXJlIHdlIGNhbiBzZWUgdGhhdCB0aGUgYHRhYmxlYCBvYmplY3Qgbm93IGNvbnRhaW5zIHRoZSB0ZXh0IGZyb20gdGhlIDNyZCBwYWdlIGFzIGEgKnNpbmdsZSBsYXJnZSBjaGFyYWN0ZXIgc3RyaW5nKi4KCmBgYHtyfQpnbGltcHNlKHRhYmxlLCBuY2hhci5tYXggPSA4MDApCgpgYGAKClRoZSB0ZXh0IGlzIGRpZmZpY3VsdCB0byByZWFkIGJlY2F1c2Ugb2YgdGhlIGNvbHVtbiBzdHJ1Y3R1cmUgaW4gdGhlIHBkZi4gTm93IGxldCdzIHRyeSB0byBncmFiIGp1c3QgdGhlIHRleHQgaW4gdGhlIHRhYmxlLgoKT25lIHdheSB0byBhcHByb2FjaCB0aGlzIGlzIHRvIHNwbGl0IHRoZSBzdHJpbmcgYnkgc29tZSBwYXR0ZXJuIHRoYXQgd2Ugbm90aWNlIGluIHRoZSB0YWJsZS4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI3MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJUYWJsZS5wbmciKSkKYGBgCgpPbmx5IHRoZSBjYXBpdGFsaXplZCBmb3JtIG9mIHRoZSB3b3JkICJEaWV0IiBhcHBlYXJzIHRvIGJlIHdpdGhpbiB0aGUgdGFibGUsIGFuZCBpcyBub3QgcHJlc2VudCBpbiB0aGUgcHJlY2VkaW5nIHRleHQgKGFsdGhvdWdoICJkaWV0IiBpcykuIEFsbCB0aGUgcm93cyBvZiBpbnRlcmVzdCBvZiB0aGUgdGFibGUgYXBwZWFyIHRvIHN0YXJ0IHdpdGggdGhlIHdvcmQgIkRpZXQiLgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjcwMHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIkRpZXRfb25fcGFnZTMucG5nIikpCmBgYAoKCkxldCdzIHVzZSB0aGUgYHN0cl9zcGxpdCgpYCBmdW5jdGlvbiBvZiB0aGUgYHN0cmluZ3JgIHBhY2thZ2UgdG8gc3BsaXQgdGhlIGRhdGEgd2l0aGluIHRoZSBvYmplY3QgY2FsbGVkIGB0YWJsZWBieSB0aGUgd29yZCAiRGlldCIuICBPbmx5IGxpbmVzIGZyb20gcGFnZSAzIHRoYXQgY29udGFpbiB0aGUgd29yZCBgRGlldGAgd2lsbCBiZSBzZWxlY3RlZCAoYW5kIG5vdCAiZGlldCIgYXMgdGhpcyBmdW5jdGlvbiBpcyBjYXNlLXNlbnNpdGl2ZSkuIEVhY2ggc2VjdGlvbiBvZiB0aGUgdGV4dCB0aGF0IGNvbnRhaW5zICJEaWV0IiB3aWxsIGJlIHNwbGl0IGludG8gaW5kaXZpZHVhbCBwaWVjZXMgZXZlcnkgdGltZSB0aGUgd29ybGQgIkRpZXQiIG9jY3VycyBhbmQgdGhlIHdvcmQgaXRzZWxmIHdpbGwgYmUgcmVtb3ZlZC4KCkluIHRoaXMgY2FzZSB3ZSBhcmUgYWxzbyB1c2luZyB0aGUgbWFncml0dHIgYXNzaWdubWVudCBwaXBlIG9yIGRvdWJsZSBwaXBlIHRoYXQgbG9va3MgbGlrZSB0aGlzIGAlPD4lYC4gVGhpcyBhbGxvd3MgdXMgdXNlIHRoZSB0YWJsZSBkYXRhIGFzIGlucHV0IHRvIHRoZSBsYXRlciBzdGVwcyBidXQgYWxzbyByZWFzc2lnbiB0aGUgb3V0cHV0IHRvIHRoZSBzYW1lIGRhdGEgb2JqZWN0IG5hbWUuCgpgYGB7cn0KdGFibGVyYXc8LXRhYmxlCnRhYmxlICU8PiUKICBzdHJpbmdyOjpzdHJfc3BsaXQocGF0dGVybiA9ICdEaWV0JykKYGBgCgpVc2luZyAgdGhlIGBiYXNlOjpzdW1tYXJ5KClgIGFuZCBgZHBseXI6OmdsaW1wc2UoKWAgZnVuY3Rpb24gd2UgY2FuIHNlZSB0aGF0IHdlIGNyZWF0ZWQgYSBsaXN0IG9mIHRoZSAxNyByb3dzIGluIHRoZSB0YWJsZSB0aGF0IGNvbnRhaW4gdGhlIHdvcmQgIkRpZXQiLiAKCmBgYHtyfQp0YWJsZSAlPiUKIHN1bW1hcnkoKQpgYGAKCldlIGNhbiBzZWUgdGhhdCB3ZSBzdGFydCB3aXRoIHRoZSByb3cgdGhhdCBjb250YWlucyAiRGlldCBsb3cgaW4gZnJ1aXRzIi4gCgpgYGB7cn0KdGFibGUgJT4lCiAgZ2xpbXBzZSgpICMgbm90ZSB3ZSBjcmVhdGVkIGEgbGlzdCBvZiAxNyBjaGFyYWN0ZXIgc3RyaW5ncwpgYGAKClJTdHVkaW8gY3JlYXRlcyByZWFsbHkgaGVscGZ1bCBjaGVhdCBzaGVldHMgbGlrZSB0aGlzIG9uZSB3aGljaCBzaG93cyB5b3UgYWxsIHRoZSBtYWpvciBmdW5jdGlvbnMgaW4gYHN0cmluZ3JgLiBZb3UgY2FuIGRvd25sb2FkIG90aGVycyBbaGVyZV0oaHR0cHM6Ly9yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKXt0YXJnZXQ9Il9ibGFuayJ9LgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjcwMHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInN0cmluZ3MtMV9zdHJfc3BsaXQucG5nIikpCmBgYAoKWW91IGNhbiBzZWUgdGhhdCB3ZSBjb3VsZCBoYXZlIGFsc28gdXNlZCB0aGUgYHN0cl9zcGxpdF9maXhlZCgpYCBmdW5jdGlvbiB3aGljaCB3b3VsZCBhbHNvIHNlcGFyYXRlIHRoZSBzdWJzdHJpbmdzIGludG8gZGlmZmVyZW50IGNvbHVtbnMgb2YgYSBtYXRyaXguCgpOb3RlOiB3ZSB3b3VsZCBuZWVkIHRvIGtub3cgdGhlIG51bWJlciBvZiBzdWJzdHJpbmdzIG9yIHBpZWNlcyB0aGF0IHdlIHdvdWxkIGxpa2UgcmV0dXJuZWQuCgpGb3IgZXhhbXBsZS4uLgoKSWYgd2UgdXNlZCB0aGUgZml4ZWQgdmVyc2lvbiwgd2Ugd2lsbCBjcmVhdGUgMyB2ZWN0b3JzIG9mIGEgbWF0cml4IHdpdGggdGhlIGZpcnN0IDMgc3RyaW5ncyB0aGF0IHdvdWxkIGJlIGNyZWF0ZWQgd2hlbiBkaXZpZGluZyB0aGUgbGFyZ2Ugc3RyaW5nIGJhc2VkIG9uIHRoZSBmaXJzdCAzIG9jY3VycmVuY2VzIG9mICJEaWV0Ii4KYGBge3J9CnRhYmxlcmF3ICU+JQogIHN0cmluZ3I6OnN0cl9zcGxpdF9maXhlZChwYXR0ZXJuID0gJ0RpZXQnLCBuID0gMykgJT4lIAogIGNsYXNzKCkKYGBgCgpXZSBjYW4gYWxzbyBzcGVjaWZ5IHRoZSBudW1iZXIgb2Ygc3BsaXRzIHdpdGggdGhlIGBzdHJfc3BsaXQoKWAsIGJ1dCB0aGlzIHdpbGwgY3JlYXRlIGEgbGlzdCBvZiBzdWJzdHJpbmdzLCBub3QgYSBtYXRyaXguCgpgYGB7cn0KdGFibGVyYXcgJT4lCiAgc3RyaW5ncjo6c3RyX3NwbGl0KHBhdHRlcm4gPSAnRGlldCcsIG4gPSAzKSAlPiUgCiAgY2xhc3MoKQpgYGAKCgpGb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBgc3RyX3NwbGl0KClgIHNlZSBbaGVyZV0oaHR0cDovL3JmdW5jdGlvbi5jb20vYXJjaGl2ZXMvMTQ5OSl7dGFyZ2V0PSJfYmxhbmsifQphbmQgW2hlcmVdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zdHJfc3BsaXQuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4KCk5vdywgYmFjayB0byBvdXIgc2luZ2xlIGxpc3Qgb2YgMTcgY2hhcmFjdGVyIHN0cmluZ3MuCgpMZXQncyBzZXBhcmF0ZSB0aGUgdmFsdWVzIHdpdGhpbiB0aGUgbGlzdCB1c2luZyB0aGUgYmFzZSBgdW5saXN0YCBmdW5jdGlvbiwgdGhpcyB3aWxsIGFsbG93IHVzIHRvIGVhc2lseSBzZWxlY3QgdGhlIGRpZmZlcmVudCBzdWJzdHJpbmdzIHdpdGhpbiB0aGUgb2JqZWN0IGNhbGxlZCBgdGFibGVgLgoKYGBge3J9CnRhYmxlICU8PiUKICB1bmxpc3QoKSAKCnN1bW1hcnkodGFibGUpCmBgYAoKSXQncyBpbXBvcnRhbnQgdG8gcmVhbGl6ZSB0aGF0IHRoZSBmaXJzdCBzcGxpdCB3aWxsIGluY2x1ZGUgdGhlIHRleHQgYmVmb3JlIHRoZSBmaXJzdCBvY2N1cnJlbmNlIG9mIGBEaWV0YCBhcyB0aGUgZmlyc3QgdmFsdWUgaW4gdGhlIG91dHB1dC4gV2UgY291bGQgdXNlIHRoZSBgZmlyc3QoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZSB0byBsb29rIGF0IHRoaXMgdmFsdWUuIEhvd2V2ZXIsIHdlIHdpbGwgc3VwcHJlc3MgdGhlIG91dHB1dCBhcyB0aGlzIGlzIHF1aXRlIGxhcmdlLgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZHBseXI6OmZpcnN0KHRhYmxlKQpgYGAKCkluc3RlYWQgd2UgY2FuIHRha2UgYSBsb29rIGF0IHRoZSBzZWNvbmQgZWxlbWVudCBvZiB0aGUgbGlzdC4gdXNpbmcgdGhlIGBudGgoKWAgZnVuY3Rpb24gb2YgYGRwbHlyYC4KCmBgYHtyfQpudGgodGFibGUsIDIpCmBgYAoKSW5kZWVkIHRoaXMgbG9va3MgbGlrZSB0aGUgZmlyc3Qgcm93IG9mIGludGVyZXN0IGluIG91ciB0YWJsZToKCmBgYHtyLGVjaG8gPSBGQUxTRSxvdXQud2lkdGg9ICI3MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoImltZyIsICJmaXJzdHJvdy5wbmciKSkKYGBgCgoKVXNpbmcgdGhlIGBsYXN0KClgIGFuZCB0aGUgYG50aCgpYCBmdW5jdGlvbnMgb2YgdGhlIGBkcGx5cmAgcGFja2FnZSB3ZSBjYW4gdGFrZSBhIGxvb2sgYXQgdGhlIGxhc3QgdmFsdWVzIG9mIHRoZSBsaXN0LgpgYGB7cn0KI3RvIHNlZSB0aGUgc2Vjb25kIHRvIGxhc3QgdmFsdWUgd2UgY2FuIHVzZSBudGgoKQojdGhlIC0yIHNwZWNpZmllcyB0aGF0IHdlIHdhbnQgdGhlIHNlY29uZCB0byBsYXN0IHZhbHVlCiMtMyB3b3VsZCBiZSB0aGlyZCB0byBsYXN0IGFuZCAtMSB3b3VsZCBiZSB0aGUgbGFzdCB2YWx1ZQpkcGx5cjo6bnRoKHRhYmxlLCAtMikKCiN0byBzZWUgdGhlIHZlcnkgbGFzdCB2YWx1ZSB3ZSBjYW4gdXNlIGxhc3QoKQpkcGx5cjo6bGFzdCh0YWJsZSkKCmBgYAoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjcwMHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImVuZF9vZl90YWJsZS5wbmciKSkKYGBgCgoKVGhlcmVmb3JlLCB3ZSBkb24ndCBuZWVkIHRoaXMgcGFydCBvZiB0aGUgdGFibGUgb3IgdGhlIHRleHQgYmVmb3JlIHRoZSB0YWJsZSBpZiB3ZSBqdXN0IHdhbnQgdGhlIGNvbnN1bXB0aW9uIHJlY29tbWVuZGF0aW9ucy4gCgpTbyB3ZSB3aWxsIHNlbGVjdCB0aGUgMm5kIHRocm91Z2ggdGhlIHNlY29uZCB0byBsYXN0IG9mIHRoZSBzdWJzdHJpbmdzLiBTaW5jZSB3ZSBoYXZlIDE3IHN1YnN0cmluZ3MsIHdlIHdpbGwgc2VsZWN0IHRoZSAybmQgdGhyb3VnaCB0aGUgMTZ0aC4gSG93ZXZlciBhIGJldHRlciB3YXkgdG8gZG8gdGhpcyByYXRoZXIgdGhhbiBzZWxlY3RpbmcgYnkgaW5kZXgsIHdvdWxkIGJlIHRvIHNlbGVjdCBwaHJhc2VzIHRoYXQgYXJlIHVuaXF1ZSB0byB0aGUgdGV4dCB3aXRoaW4gdGhlIHRhYmxlIHRoYXQgd2Ugd2FudC4gV2Ugd2lsbCB1c2UgdGhlIGBzdHJfc3Vic2V0KClgIGZ1bmN0aW9uIG9mIGBzdHJpbmdyYCBwYWNrYWdlIHRvIHNlbGVjdCB0aGUgdGFibGUgcm93cyB3aXRoIGNvbnN1bXB0aW9uIGd1aWRlbGluZXMuICBNb3N0IG9mIHRoZSByb3dzIGhhdmUgdGhlIHBocmFzZSAiTWVhbiBkYWlseSBjb25zdW1wdGlvbiIsIGhvd2V2ZXIsIHRoZXJlIGFyZSBvdGhlciBwaHJhc2VzIGZvciBzb21lIG9mIHRoZSByb3dzLCBpbmNsdWRpbmcgIk1lYW4gZGFpbHkgaW50YWtlIiBhbmQgIjI0IGggc29kaXVtLiIgU28gd2Ugd2lsbCBzdWJzZXQgZm9yIGVhY2ggb2YgdGhlc2UgcGhyYXNlcy4KCmBgYHtyfQojIG9uZSBjb3VsZCBzdWJzZXQgdGhlIHRhYmxlIGxpa2UgdGhpczoKI3RhYmxlIDwtIHRhYmxlWzI6MTZdCgp0YWJsZSAlPD4lCnN0cl9zdWJzZXQoCiAgcGF0dGVybiA9ICJNZWFuIGRhaWx5IGNvbnN1bXB0aW9ufE1lYW4gZGFpbHkgaW50YWtlfDI0IGgiKQpgYGAKCk5vdGljZSB0aGF0IHdlIHNlcGFyYXRlIHRoZSBkaWZmZXJlbnQgcGF0dGVybnMgdG8gbG9vayBmb3IgdXNpbmcgdmVydGljYWwgYmFyIGNoYXJhY3RlciAifCIgYW5kIHRoYXQgYWxsIG9mIHRoZSBwYXR0ZXJucyBhcmUgd2l0aGluIHF1b3RhdGlvbiBtYXJrcyB0b2dldGhlci4KCiMjIyMgey5xdWVzdGlvbl9ibG9ja30KPHU+UXVlc3Rpb24gb3Bwb3J0dW5pdHk6PC91PiAKCjEpIFdoYXQgb3RoZXIgc3RyaW5nIHBhdHRlcm5zIGNvdWxkIHlvdSB1c2UgdG8gc3Vic2V0IHRoZSByb3dzIG9mIHRoZSB0YWJsZSB0aGF0IHdlIHdhbnQ/CgoyKSBXaHkgbWlnaHQgaXQgYmUgYmV0dGVyIHRvIHN1YnNldCBiYXNlZCBvbiB0aGUgdGV4dCByYXRoZXIgdGhhbiB0aGUgaW5kZXg/CgojIyMjCgoKTm93IHRoZSBmaXJzdCByb3cgaXMgd2hhdCB3ZSB3YW50OgpgYGB7cn0KZmlyc3QodGFibGUpCmBgYAoKQW5kIHRoZSBsYXN0IHJvdyBpcyB3aGF0IHdlIHdhbnQ6CmBgYHtyfQpsYXN0KHRhYmxlKQpgYGAKCk5vdGljZSB0aGF0IHRoZXJlIHRoZSBkZWNpbWFsIHBvaW50cyBmcm9tIHRoZSBwZGYgYXJlIGJlaW5nIHJlY29nbml6ZWQgYXMgYW4gaW50ZXJwdW5jdCBpbnN0ZWFkIG9mIGEgcGVyaW9kIG9yIGRlY2ltYWwuIEFuIGludGVycHVuY3QgaXMgYSBjZW50ZXJlZCBkb3QsIGFzIG9wcG9zZWQgdG8gYSBwZXJpb2Qgb3IgZGVjaW1hbCB0aGF0IGlzIGFsaWduZWQgdG8gdGhlIGJvdHRvbSBvZiB0aGUgbGluZS4KClRoZSBpbnRlcnB1bmN0IHdhcyBwcmV2aW91c2x5IHVzZWQgdG8gc2VwYXJhdGUgd29yZHMgaW4gY2VydGFpbiBsYW5ndWFnZXMsIGxpa2UgYW5jaWVudCBMYXRpbi4KCgo8cCBhbGlnbj0iY2VudGVyIj4KICA8aW1nIHdpZHRoPSI0MDAiIHNyYz0iaHR0cHM6Ly93d3cueW91cmRpY3Rpb25hcnkuY29tL2ltYWdlL2FydGljbGVzLzM0MTcuTGF0aW4uanBnIj4KPC9wPgoKIyMjIyMjIFtbc291cmNlXShodHRwczovL3d3dy55b3VyZGljdGlvbmFyeS5jb20vaW1hZ2UvYXJ0aWNsZXMvMzQxNy5MYXRpbi5qcGcpXQoKWW91IGNhbiBwcm9kdWNlIGFuIGludGVycHVuY3Qgb24gYSBtYWMgbGlrZSB0aGlzOgoKCjxwIGFsaWduPSJjZW50ZXIiPgogIDxpbWcgd2lkdGg9IjQwMCIgc3JjPSJodHRwczovL3d3dy5zaG9ydHR1dG9yaWFscy5jb20vbWFjLW9zLXNwZWNpYWwtY2hhcmFjdGVycy1zaG9ydGN1dHMvaW1hZ2VzL21pZGRsZS1kb3QucG5nIj4KPC9wPgoKIyMjIyMjIFtbc291cmNlXShodHRwczovL3d3dy5zaG9ydHR1dG9yaWFscy5jb20vbWFjLW9zLXNwZWNpYWwtY2hhcmFjdGVycy1zaG9ydGN1dHMvbWlkZGxlLWRvdC5odG1sKV0KCgpJdCBpcyBpbXBvcnRhbnQgdG8gcmVwbGFjZSB0aGVzZSBmb3IgbGF0ZXIgd2hlbiB3ZSB3YW50IHRoZXNlIHZhbHVlcyB0byBiZSBjb252ZXJ0ZWQgZnJvbSBjaGFyYWN0ZXIgc3RyaW5ncyB0byBudW1lcmljLiBXZSB3aWxsIGFnYWluIHVzZSB0aGUgYHN0cmluZ3JgIHBhY2thZ2UuIFRoaXMgdGltZSB3ZSB3aWxsIHVzZSB0aGUgYHN0cl9yZXBsYWNlX2FsbCgpYCBmdW5jdGlvbiB3aGljaCByZXBsYWNlcyBhbGwgaW5zdGFuY2VzIG9mIGEgcGF0dGVybiBpbiBhbiBpbmRpdmlkdWFsIHN0cmluZy4gSW4gdGhpcyBjYXNlIHdlIHdhbnQgdG8gcmVwbGFjZSBhbGwgaW5zdGFuY2VzIG9mIHRoZSBpbnRlcnB1bmN0IHdpdGggYSBkZWNpbWFsIHBvaW50LgoKCmBgYHtyLH0KdGFibGUgJTw+JQogIHN0cmluZ3I6OnN0cl9yZXBsYWNlX2FsbCggcGF0dGVybiA9ICLCtyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiLiIpCmBgYAoKCk5vdyB3ZSB3aWxsIHRyeSB0byBzcGxpdCB0aGUgc3RyaW5ncyBmb3IgZWFjaCByb3cgYmFzZWQgb24gdGhlIHByZXNlbmNlIG9mIDIgc3BhY2VzIHRvIGNyZWF0ZSB0aGUgY29sdW1ucyBvZiB0aGUgdGFibGUsIGFzIHRoZXJlIGFwcGVhcnMgdG8gYmUgbGFyZ2VyIHRoYW4gYSBzcGFjZSBiZXR3ZWVuIHRoZSBjb2x1bW5zIHRvIGNyZWF0ZSBzdWJzdHJpbmdzLiBUaGUgc3Vic3RyaW5ncyB3aWxsIGJlIHNlcGFyYXRlZCBieSBxdW90ZXMuCgpgYGB7ciwgZWNobyA9IEZBTFNFLG91dC53aWR0aCA9ICI3MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoImltZyIsICJzdHJpbmdzLTJfaGlnaGxpZ2h0LnBuZyIpKQpgYGAKCgpUaGUgc2Vjb25kIHBhZ2Ugb2YgdGhlIGBzdHJpbmdyYCBjaGVhdCBzaGVldCBoYXMgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB1c2luZyAiU3BlY2lhbCBDaGFyYWN0ZXJzIiBpbiBgc3RyaW5ncmAuIEZvciBleGFtcGxlIGBcXHNgIGlzIGludGVycHJldGVkIGFzIGEgc3BhY2UgYXMgdGhlIGBcXGAgaW5kaWNhdGVzIHRoYXQgdGhlIGBzYCBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgYXMgYSBzcGVjaWFsIGNoYXJhY3RlciBhbmQgbm90IHNpbXBseSB0aGUgbGV0dGVyIHMuICBUaGUgezIsfSBpbmRpY2F0ZXMgMiBvciBtb3JlIHNwYWNlcywgd2hpbGUgezJ9IHdvdWxkIGluZGljYXRlIGV4YWN0bHkgMiBzcGFjZXMuCgpTbyBoZXJlIHdlIHdpbGwgc2VwYXJhdGUgdGhlIHN1YnN0cmluZ3MgaW50byBjb2x1bW5zIGJ5IDIgbW9yZSBtb3JlIHNwYWNlczoKCiMjIyMgey5zY3JvbGxhYmxlIH0KYGBge3J9CnRhYmxlX3NwbGl0IDwtIHN0cl9zcGxpdChzdHJpbmc9dGFibGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgcGF0dGVybj0gIlxcc3syLH0iKQpnbGltcHNlKHRhYmxlX3NwbGl0KSAjc2Nyb2xsIHRoZSBvdXRwdXQhCmBgYAojIyMjCgpJZiB3ZSBsb29rIGNsb3NlbHksIHdlIGNhbiBzZWUgdGhhdCB0aGUgc3VnYXItc3dlZXRlbmVkIGJldmVyYWdlIGFuZCB0aGUgc2VhZm9vZCBjYXRlZ29yeSBoYWQgb25seSBvbmUgc3BhY2UgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIHNlY29uZCBjb2x1bW5zIC0gdGhlIGNvbHVtbnMgYWJvdXQgdGhlIGRpZXRhcnkgY2F0ZWdvcnkgYW5kIHRoZSBvbmUgdGhhdCBkZXNjcmliZXMgaW4gbW9yZSBkZXRhaWwgd2hhdCB0aGUgY29uc3VtcHRpb24gc3VnZ2VzdGlvbiBpcyBhYm91dC4KClRoZSB2YWx1ZXMgZm9yIHRoZXNlIHR3byBjb2x1bW5zIGFwcGVhciB0byBiZSB0b2dldGhlciBzdGlsbCBpbiB0aGUgc2FtZSBzdWJzdHJpbmcgZm9yIHRoZXNlIHR3byBjYXRlZ29yaWVzLiBUaGVyZSBhcmUgbm8gcXVvdGF0aW9uIG1hcmtzIGFkamFjZW50IHRvIHRoZSB3b3JkIGAiTWVhbiJgLgoKSGVyZSB5b3UgY2FuIHNlZSBob3cgdGhlIG5leHQgc3Vic3RyaW5nIHNob3VsZCBoYXZlIHN0YXJ0ZWQgd2l0aCB0aGUgd29yZCBgIk1lYW4iYCBieSB0aGUgbmV3IGluY2x1c2lvbiBvZiBhIHF1b3RhdGlvbiBtYXJrIGAiYC4gCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGggPSAiNzAwcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlKCJpbWciLCAic3Vic3RyaW5nX3NlcC5wbmciKSkKYGBgCgpXZSBjYW4gYWRkIGFuIGV4dHJhIHNwYWNlIGluIGZyb250IG9mIHRoZSB3b3JkIGAiTWVhbiJgIGZvciB0aGVzZSBwYXJ0aWN1bGFyIGNhdGVnb3JpZXMgYW5kIHRoZW4gdHJ5IHNwbGl0dGluZyBhZ2Fpbi4KClNpbmNlIHdlIG9yaWdpbmFsbHkgc3BsaXQgYmFzZWQgb24gMiBvciBtb3JlIHNwYWNlcywgd2UgY2FuIGp1c3QgYWRkIGEgc3BhY2UgaW4gZnJvbnQgb2YgdGhlIHdvcmQgIk1lYW4iIGZvciBhbGwgdGhlIHRhYmxlIHN0cmluZ3MgYW5kIHRoZW4gdHJ5IHN1YnNldHRpbmcgYWdhaW4uIFdlIGNhbiB1c2UgdGhlIGBzdHJfd2hpY2goKWAgZnVuY3Rpb24gb2YgdGhlIGBzdHJpbmdyYCBwYWNrYWdlIHRvIGZpbmQgdGhlIGluZGV4IG9mIHRoZXNlIHBhcnRpY3VsYXIgY2FzZXMuCgpgYGB7cn0KdGFibGUlPiUKc3RyX3doaWNoKHBhdHRlcm4gPSAic2VhZm9vZHxzdWdhciIpCmBgYApIZXJlIHdlIGNhbiBzZWUganVzdCB0aG9zZSBzdHJpbmdzIHRoYXQgbWF0Y2ggdGhlIHBhdHRlcm46CmBgYHtyfQp0YWJsZVtzdHJfd2hpY2godGFibGUsIHBhdHRlcm4gPSAic2VhZm9vZHxzdWdhciIpXQpgYGAKCk5vdyB3ZSBjYW4gcmVwbGFjZSB0aGVzZSB2YWx1ZXMgd2l0aGluIHRoZSB0YWJsZSBvYmplY3QgYWZ0ZXIgYWRkaW5nIGEgc3BhY2UgaW4gZnJvbnQgb2YgIk1lYW4iLgpgYGB7cn0KdGFibGVbc3RyX3doaWNoKHRhYmxlLCAKICAgICAgcGF0dGVybiA9IAogICAgICAic2VhZm9vZHxzdWdhciIpXTwtc3RyX3JlcGxhY2UoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmcgPSB0YWJsZVtzdHJfd2hpY2godGFibGUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZWFmb29kfHN1Z2FyIildLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiTWVhbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiIE1lYW4iKQpgYGAKCkFuZCBub3cgd2UgY2FuIHRyeSBzcGxpdHRpbmcgYWdhaW4gYnkgMiBvciBtb3JlIHNwYWNlczoKYGBge3J9CnRhYmxlX3NwbGl0IDwtIHN0cl9zcGxpdCh0YWJsZSxwYXR0ZXJuPSAiXFxzezIsfSIpCmBgYAoKV2UgY291bGQgYWxzbyBqdXN0IGFkZCBhIHNwYWNlIGluIGZyb250IG9mIGFsbCB0aGUgdmFsdWVzIG9mICJNZWFuIiBpbiB0aGUgdGFibGUgc2luY2UgdGhlIHNwbGl0IHdhcyBwZXJmb3JtZWQgYmFzZWQgb24gMiBvciBtb3JlIHNwYWNlcy4gVGh1cyB0aGUgb3RoZXIgZWxlbWVudHMgaW4gYHRhYmxlYCB3b3VsZCBhbHNvIGJlIHNwbGl0IGp1c3QgYXMgYmVmb3JlIGRlc3BpdGUgdGhlIGFkZGl0aW9uYWwgc3BhY2UuCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQp0YWJsZTwtdGFibGUgJT4lCiAgc3RyaW5ncjo6c3RyX3JlcGxhY2UocGF0dGVybiA9Ik1lYW4iLCAKICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICIgTWVhbiIpCnRhYmxlX3NwbGl0IDwtIHN0cl9zcGxpdCh0YWJsZSxwYXR0ZXJuPSAiXFxzezIsfSIpCmBgYAoKIyMjIyB7LnNjcm9sbGFibGUgfQpgYGB7cn0KI3Njcm9sbCB0aGUgb3V0cHV0IQpnbGltcHNlKHRhYmxlX3NwbGl0KSAKYGBgCiMjIyMKCkxvb2tzIGJldHRlciEKCldlIHdhbnQganVzdCB0aGUgZmlyc3QgKHRoZSBmb29kICoqY2F0ZWdvcnkqKikgYW5kIHRoaXJkIGNvbHVtbiAodGhlIG9wdGltYWwgY29uc3VtcHRpb24gKiphbW91bnQqKiBzdWdnZXN0ZWQpIGZvciBlYWNoIHJvdyBpbiB0aGUgdGFibGUuCgpXZSBjYW4gdXNlIHRoZSBgbWFwYCBmdW5jdGlvbiBvZiB0aGUgYHB1cnJyYCBwYWNrYWdlIHRvIGFjY29tcGxpc2ggdGhpcy4KClRoZSBgbWFwYCBmdW5jdGlvbiBhbGxvd3MgdXMgdG8gcGVyZm9ybSB0aGUgc2FtZSBhY3Rpb24gbXVsdGlwbGUgdGltZXMgYWNyb3NzIGVhY2ggZWxlbWVudCB3aXRoaW4gYW4gb2JqZWN0LgoKVGhpcyBmb2xsb3dpbmcgd2lsbCBhbGxvdyB1cyB0byBzZWxlY3QgdGhlIDFzdCBvciAzcmQgc3Vic3RyaW5nIGZyb20gZWFjaCBlbGVtZW50IG9mIHRoZSBgdGFibGVgIG9iamVjdC4KCmBgYHtyfQpjYXRlZ29yeSA8LW1hcCh0YWJsZV9zcGxpdCwxKQphbW91bnQgPC1tYXAodGFibGVfc3BsaXQsMykKaGVhZChjYXRlZ29yeSkKaGVhZChhbW91bnQpCmBgYAoKTm93IHdlIHdpbGwgY3JlYXRlIGEgYHRpYmJsZWAgdXNpbmcgdGhpcyBkYXRhLiBIb3dldmVyLCBjdXJyZW50bHkgYm90aCBgY2F0ZWdvcnlgIGFuZCBgYW1vdW50YCBhcmUgb2YgY2xhc3MgYGxpc3RgLiBUbyBjcmVhdGUgYSBgdGliYmxlYCB3ZSBuZWVkIHRvIHVubGlzdCB0aGUgZGF0YSB0byBjcmVhdGUgdmVjdG9ycy4KCmBgYHtyfQpjbGFzcyhjYXRlZ29yeSkKY2F0ZWdvcnkgJTw+JXVubGlzdCgpCmFtb3VudCAlPD4ldW5saXN0KCkKY2xhc3MoY2F0ZWdvcnkpCmBgYAoKIyMjIyB7LnNjcm9sbGFibGUgfQpgYGB7cn0KY2F0ZWdvcnkKYW1vdW50CmBgYAojIyMjCgpXZSBjb3VsZCBoYXZlIGRvbmUgYWxsIG9mIHRoaXMgYXQgb25jZSBpbiBvbmUgY29tbWFuZCBsaWtlIHRoaXM6CgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpjYXRlZ29yeSA8LXVubGlzdChtYXAodGFibGVfc3BsaXQsMSkpCmFtb3VudCA8LXVubGlzdChtYXAodGFibGVfc3BsaXQsMykpCmBgYAoKTm93IHdlIHdpbGwgY3JlYXRlIGEgYHRpYmJsZWAsIHdoaWNoIGlzIGFuIGltcG9ydGFudCBkYXRhIGZyYW1lIHN0cnVjdHVyZSBpbiB0aGUgdGlkeXZlcnNlIHdoaWNoIGFsbG93cyB1cyB0byB1c2Ugb3RoZXIgcGFja2FnZXMgaW4gdGhlIHRpZHl2ZXJzZSB3aXRoIG91ciBkYXRhLgoKV2Ugd2lsbCBuYW1lIG91ciBgdGliYmxlYCBjb2x1bW5zIG5vdyBhcyB3ZSBjcmVhdGUgb3VyIGB0aWJibGVgIHVzaW5nIHRoZSBgdGliYmxlKClgIGZ1bmN0aW9uIG9mIGJvdGggdGhlIGB0aWR5cmAgYW5kIHRoZSBgdGliYmxlYCBwYWNrYWdlcywgYXMgbmFtZXMgYXJlIHJlcXVpcmVkIGluIHRpYmJsZXMuCgpgYGB7cn0KZ3VpZGVsaW5lcyA8LXRpYmJsZTo6dGliYmxlKGNhdGVnb3J5ID0gY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFtb3VudCA9IGFtb3VudCkKZ3VpZGVsaW5lcwpgYGAKCkxvb2tpbmcgcHJldHR5IGdvb2QhCgpIb3dldmVyLCB3ZSB3YW50IHRvIHNlcGFyYXRlIHRoZSBkaWZmZXJlbnQgYW1vdW50cyB3aXRoaW4gdGhlIGFtb3VudCBjb2x1bW4uCgpSZWNhbGwgd2hhdCB0aGUgb3JpZ2luYWwgdGFibGUgbG9va2VkIGxpa2U6CmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI3MDBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmUoImltZyIsICJmaXJzdHJvdy5wbmciKSkKYGBgCgojIyMgU2VwYXJhdGluZyB2YWx1ZXMgd2l0aGluIGEgdmFyaWFibGUKCldlIGNhbiB1c2UgdGhlIGB0aWR5cjo6c2VwYXJhdGUoKWAgZnVuY3Rpb24gdG8gc2VwYXJhdGUgdGhlIGRhdGEgd2l0aGluIHRoZSBhbW91bnQgY29sdW1uIGludG8gdGhyZWUgbmV3IGNvbHVtbnMgYmFzZWQgb24gdGhlIG9wdGltYWwgbGV2ZWwgYW5kIHRoZSBvcHRpbWFsIHJhbmdlLiBXZSBjYW4gc2VwYXJhdGUgdGhlIHZhbHVlcyBiYXNlZCBvbiB0aGUgb3BlbiBwYXJlbnRoZXNlcyBgIigiYCBhbmQgdGhlIGxvbmcgZGFzaCBgIuKAkyJgIGNoYXJhY3RlcnMuCgpgYGB7cn0KIyBUaGUgZmlyc3QgY29sdW1uIHdpbGwgYmUgY2FsbGVkIG9wdGltYWwKIyBJdCB3aWxsIGNvbnRhaW4gdGhlIDFzdCBwYXJ0IG9mIHRoZSBhbW91bnQgY29sdW1uIGRhdGEgYmVmb3JlIHRoZSAxc3QgdW5kZXJzY29yZSIoIgojIFRoZSAybmQgY29sdW1uIHdpbGwgYmUgY2FsbGVkIGxvd2VyCiMgSXQgd2lsbCBjb250YWluIHRoZSBkYXRhIGFmdGVyIHRoZSAiKCIKIyBUaGUgM3JkIGNvbHVtbiB3aWxsIGJlIGNhbGxlZCB1cHBlciAKIyBJdCB3aWxsIGNvbnRhaW4gdGhlIDJuZCBwYXJ0IG9mIHRoZSBkYXRhIGJhc2VkIG9uIHRoZSAi4oCTIgoKZ3VpZGVsaW5lcyU8PiUgCiAgdGlkeXI6OnNlcGFyYXRlKGFtb3VudCwgCiAgICAgICAgICAgICAgICAgIGMoIm9wdGltYWwiLCAibG93ZXIiLCAidXBwZXIiKSwKICAgICAgICAgICAgICAgICAgc2VwID0iW1sofOKAk11dIikgCmhlYWQoZ3VpZGVsaW5lcykKYGBgCgoKTGV0J3MgQWxzbyBjcmVhdGUgYSBuZXcgdmFyaWFibGUvY29sdW1uIGluIG91ciB0aWJibGUgdGhhdCBpbmRpY2F0ZXMgdGhlIGRpcmVjdGlvbiB0aGF0IGNhbiBiZSBoYXJtZnVsIGZvciBlYWNoIGRpZXRhcnkgZmFjdG9yLgoKYGBge3J9Cmd1aWRlbGluZXMlPD4lCiAgc2VwYXJhdGUoY2F0ZWdvcnksIGMoImRpcmVjdGlvbiIsICJmb29kIiksIHNlcCA9ICIgaW4gIikKZ3VpZGVsaW5lcwpgYGAKCklmIHdlIHdhbnRlZCB0byByZW1vdmUgdGhlIGRpcmVjdGlvbiB2YXJpYWJsZSB3ZSBjb3VsZCB1c2UgdGhlIHB1cnJyOjptb2RpZnlfYXQoKSBmdW5jdGlvbjoKYGBge3IsZXZhbCA9IEZBTFNFfQpndWlkZWxpbmVzICU+JSBwdXJycjo6bW9kaWZ5X2F0KCJkaXJlY3Rpb24iLH5OVUxMKQpgYGAKCgojIyMgRGF0YSBjbGVhbmluZyB3aXRoIHJlZ3VsYXIgZXhwcmVzc2lvbnMKCk9LLCBsb29raW5nIGJldHRlciwgYnV0IHdlIHN0aWxsIG5lZWQgYSBiaXQgb2YgY2xlYW5pbmcgdG8gcmVtb3ZlIHN5bWJvbHMgYW5kIGV4dHJhIHdvcmRzIGZyb20gdGhlIGNvbHVtbnMuIFNvbWUgb2YgdGhlIGV4dHJhIHN5bWJvbHMgaW5jbHVkZTogYCIlImAsIGAiKSJgIGFuZCB0aGUgYCIqImAuCgpUaGUgYCIqImAgYW5kIHRoZSBgIikiYCBhcmUgd2hhdCB3ZSBjYWxsIG1ldGFjaGFyYWN0ZXJzIG9yIFtyZWd1bGFyIGV4cHJlc3Npb25zXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS9yZWd1bGFyLWV4cHJlc3Npb25zLWV2ZXJ5LXItcHJvZ3JhbW1lci1zaG91bGQta25vdy8pe3RhcmdldD0iX2JsYW5rIn0uIFRoZXNlIGFyZSBjaGFyYWN0ZXJzIHRoYXQgaGF2ZSBzcGVjaWFsIG1lYW5pbmdzLgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjcwMHB4In0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgiaW1nIiwgIlJlZ0V4Q2hlYXRzaGVldC5wbmciKSkKYGBgCgpOb3cgd2UgbmVlZCB0aGUgYCJcXCJgIHRvIGluZGljYXRlIHRoYXQgd2Ugd2FudCB0aGVzZSBjaGFyYWN0ZXJzIHRvIGJlIG1hdGNoZWQgZXhhY3RseSBhbmQgbm90IGludGVycHJldGVkIGFzIHRoZSBtZWFuaW5nIG9mIHRoZSBzeW1ib2wuCgpTZWUgW2hlcmVdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zdHJpbmdyL3ZpZ25ldHRlcy9yZWd1bGFyLWV4cHJlc3Npb25zLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgaW5mbyBhYm91dCByZWd1bGFyIGV4cHJlc3Npb25zIGluIFIuIAoKQWxzbyBoZXJlIHdlIGhhdmUgYSBiaXQgb2YgYW4gZXhhbXBsZSB1c2luZyB0aGUgYHN0cl9jb3VudCgpYCBmdW5jdGlvbiBvZiBgc3RyaW5ncmAsIHdoaWNoIGNvdW50cyB0aGUgbnVtYmVyIG9mIGluc3RhbmNlcyBvZiBhIGNoYXJhY3RlciBzdHJpbmcuIEluIHRoaXMgY2FzZSB3ZSB3aWxsIGxvb2sgZm9yIGluZGl2aWR1YWwgY2hhcmFjdGVycyBidXQgeW91IGNvdWxkIGFsc28gc2VhcmNoIGZvciB3b3JkcyBvciBwaHJhc2VzLgoKQ291bnQgdGhlIGxldHRlciB0OgpgYGB7cn0KcmVnZXh0ZXN0PC1yZWFkcjo6cmVhZF9maWxlKGhlcmUoImRvY3MiLCAicmVnRXgudHh0IikpCnJlZ2V4dGVzdApzdHJfY291bnQocmVnZXh0ZXN0LCJ0Iikjbm90aWNlIHRoaXMgZG9lc24ndCBpbmNsdWRlIHRoZSB0IGluIHRoZSB0YWIKYGBgCgpDb3VudCB0YWJzOgpgYGB7cn0Kc3RyX2NvdW50KHJlZ2V4dGVzdCwiXFx0Iikjc2VhcmNoIGZvciB0YWIKYGBgCgpDb3VudCBwYXJlbnRoZXNlczoKYGBge3J9CiMgdGhpcyB3b3VsZCBub3Qgd29yayBiZWNhdXNlIHIgdGhpbmtzIHRoaXMgaXMgcGFydCBvZiB0aGUgY29kZSBpdHNlbGYKI3N0cl9jb3VudChyZWdleHRlc3QsICIpIikgCiMgdGhpcyB3b3VsZCBub3Qgd29yayBiZWNhdXNlIHIgdGhpbmtzIHRoaXMgaXMgcGFydCBvZiB0aGUgY29kZSBpdHNlbGYKI3N0cl9jb3VudChyZWdleHRlc3QsICJcKSIpCnN0cl9jb3VudChyZWdleHRlc3QsICJcXCkiKSAjdGhpcyB3b3JrcyEKYGBgCgpDb3VudCB0aGUgb2NjdXJyZW5jZSBvZiB0aGUgYXN0cml4OgpgYGB7cn0KIyB0aGlzIGFsc28gZG9lcyBub3Qgd29yawojc3RyX2NvdW50KHJlZ2V4dGVzdCwgIioiKQojIG5vciBkb2VzIHRoaXMKI3N0cl9jb3VudChyZWdleHRlc3QsICJcKiIpCnN0cl9jb3VudChyZWdleHRlc3QsICJcXCoiKSN0aGlzIHdvcmtzIQpgYGAKCldlIGFsc28gd2FudCB0byBtYWtlIGEgdW5pdCB2YXJpYWJsZSBzbyB0aGF0IHdlIGNhbiBtYWtlIHN1cmUgdGhhdCBvdXIgdW5pdHMgYXJlIGNvbnNpc3RlbnQgbGF0ZXIuIAoKYGBge3J9Cmd1aWRlbGluZXMgJT4lCnB1bGwob3B0aW1hbCkgCmBgYAoKTm90aWNlIHRoYXQgdGhlIHZhbHVlcyB0aGF0IGFyZSBwZXJjZW50YWdlcyBkb24ndCBoYXZlIHNwYWNlcyBiZXR3ZWVuIHRoZSBudW1iZXIgYW5kIHRoZSB1bml0LgpXZSBjYW4gc2VwYXJhdGUgdGhlIGBvcHRpbWFsYCB2YWx1ZXMgYnkgYSBzcGFjZSBvciBhIHBlcmNlbnQgc3ltYm9sIGAiJSJgIHVzaW5nIGAifCJgIHRvIGluZGljYXRlIHRoYXQgd2Ugd2FudCB0byBzZXBhcmF0ZSBieSBlaXRoZXIuIEluIHRoaXMgY2FzZSB3ZSB3aWxsIGxvc2UgdGhlICIlIiBhbmQgd2lsbCBuZWVkIHRvIGFkZCBpdCBiYWNrIHRvIHRob3NlIHZhbHVlcy4KCldlIGNhbiBzcGVjaWZ5IGEgc3BhY2UgdXNpbmcgYW4gYWN0dWFsIHNwYWNlIG9yIGBcXHNgLgoKYGBge3J9Cmd1aWRlbGluZXMlPiUKICBzZXBhcmF0ZShvcHRpbWFsLCBpbnRvID1jKCJvcHRpbWFsIiwgInVuaXQiKSwgc2VwID0gIiB8JSIsIHJlbW92ZSA9IEZBTFNFKQoKZ3VpZGVsaW5lcyU8PiUKICBzZXBhcmF0ZShvcHRpbWFsLCBpbnRvID1jKCJvcHRpbWFsIiwgInVuaXQiKSwgc2VwID0gIlxcc3wlIiwgcmVtb3ZlID0gRkFMU0UpCgpgYGAKCkdyZWF0LCBzbyB0byBub3cgd2Ugd2lsbCBhZGQgImAlYCIgdG8gdGhlIGB1bml0YCB2YXJpYWJsZSBmb3IgIHRoZSBgbG93IGluIHBvbHl1bnNhdHVyYXRlZGAgYW5kIGBoaWdoIGluIHRyYW5zIGZhdHR5IGFjaWRzYCByb3dzLgoKRmlyc3Qgd2UgbmVlZCB0byByZXBsYWNlIHRoZSBlbXB0eSB2YWx1ZXMgd2l0aCBOQSB1c2luZyB0aGUgYG5hX2lmKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UuCgpgYGB7cn0KZ3VpZGVsaW5lcyAlPD4lCm5hX2lmKCIiKQpndWlkZWxpbmVzCmBgYAoKClRoZW4gdG8gcmVwbGFjZSB0aGUgYE5BYCB2YWx1ZXMsIHdlIGNhbiB1c2UgdGhlIGByZXBsYWNlX25hKClgIGZ1bmN0aW9uIGluIHRoZSBgdGlkeXJgIHBhY2thZ2UgYW5kIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIG9mIGBkcGx5cmAgdG8gc3BlY2lmeSB3aGljaCB2YWx1ZXMgdG8gcmVwbGFjZSwgaW4gdGhpcyBjYXNlIHRoZSBgTkFgIHZhbHVlcyB3aXRoaW4gdGhlIHZhcmlhYmxlIGB1bml0YC4gRXNzZW50aWFsbHkgdGhpcyB2YXJpYWJsZSBnZXRzIHJlYXNzaWduZWQgd2l0aCB0aGUgbmV3IHZhbHVlcywgYXMgd2UgbW9zdGx5IHRoaW5rIG9mIHRoZSBgbXV0YXRlKClgIGZ1bmN0aW9uIGFzIGNyZWF0aW5nIG5ldyB2YXJpYWJsZXMuCgpgYGB7cn0KZ3VpZGVsaW5lcyAlPD4lIAogIGRwbHlyOjptdXRhdGUodW5pdCA9IHJlcGxhY2VfbmEodW5pdCwgIiUiKSkKCiNub3cganVzdCB0byBzaG93IHRoZXNlIHJvd3MKZ3VpZGVsaW5lcyAlPiUKICBmaWx0ZXIodW5pdCA9PSAiJSIpCgpgYGAKCkxldCdzIGFsc28gbW92ZSBgdW5pdGAgdG8gYmUgdGhlIGxhc3QgY29sdW1uLiBXZSBjYW4gdXNlIHRoZSBgc2VsZWN0KClgIGFuZCBgZXZlcnl0aGluZygpYCBmdW5jdGlvbnMgb2YgdGhlIGBkcGx5cmAgcGFja2FnZSB0byBkbyB0aGlzLgoKYGBge3J9Cmd1aWRlbGluZXMgJTw+JQogIHNlbGVjdCgtdW5pdCxldmVyeXRoaW5nKCkpCmBgYAoKSGVyZSB5b3UgY2FuIHNlZSBIYWRsZXkgV2lja2hhbSdzIChDaGllZiBTY2llbnRpc3QgYXQgUlN0dWRpbykgZXhwbGFuYXRpb24gZm9yIHRoaXMgYmVoYXZpb3Igb2YgYHNlbGVjdCgpYDoKCmBgYHtyLCBlY2hvPSBGQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZSgiaW1nIiwgInNlbGVjdC5wbmciKSkKYGBgCmh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvZHBseXIvaXNzdWVzLzI4MzgjaXNzdWVjb21tZW50LTMwNjA2MjgwMAoKVG8gcmVtb3ZlIGFsbCBvZiB0aGUgcmVtYWluaW5nIGV4dHJhIGNoYXJhY3RlcnMgYW5kIHdvcmRzIHdlIHdpbGwgYWdhaW4gdXNlIHRoZSBgc3RyaW5ncmAgcGFja2FnZS4gVGhpcyB0aW1lIHdlIHdpbGwgdXNlIHRoZSBgc3RyX3JlbW92ZSgpYCBmdW5jdGlvbiB0byByZW1vdmUgYWxsIGluc3RhbmNlcyBvZiB0aGVzZSBjaGFyYWN0ZXJzLgoKYGBge3J9Cmd1aWRlbGluZXMgPC1hc190aWJibGUoCiAgbWFwKAogICAgZ3VpZGVsaW5lcywKICAgIHN0cl9yZW1vdmUsCiAgICBwYXR0ZXJuID0gIlxcKSBwZXIgZGF5fFxcKSBvZiB0b3RhbCBkYWlseSBlbmVyZ3kiKSkKCmd1aWRlbGluZXMgPC1hc190aWJibGUoCiAgbWFwKGd1aWRlbGluZXMsIAogICAgICBzdHJfcmVtb3ZlLAogICAgICBwYXR0ZXJuID0gIlxcKiIpKQoKZ3VpZGVsaW5lcwpgYGAKCk5pY2UhIHRoYXQncyBwcmV0dHkgY2xlYW4gYnV0IHdlIGNhbiBkbyBhIGJpdCBtb3JlLgoKIyMjIERhdGEgdHlwZSBjb252ZXJzaW9uCgpPbmUgb2YgdGhlIG5leHQgdGhpbmdzIHRvIG5vdGljZSBhYm91dCBvdXIgZGF0YSBpcyB0aGUgY2hhcmFjdGVyIGNsYXNzZXMgb2Ygb3VyIHZhcmlhYmxlcy4KCk5vdGljZSB0aGF0IHRoZSBvcHRpbWFsIGFtb3VudHMgb2YgY29uc3VtcHRpb24gYXJlIGN1cnJlbnRseSBvZiAgY2xhc3MgY2hhcmFjdGVyIGFzIGluZGljYXRlZCBieSB0aGUgYDxjaHI+YCBqdXN0IGJlbG93IHRoZSBjb2x1bW4gbmFtZXMgLyB2YXJpYWJsZSBuYW1lcyBvZiB0aGUgYGd1aWRlbGluZXNgIHRpYmJsZToKCmBgYHtyfQpndWlkZWxpbmVzCmBgYAoKClRvIGNvbnZlcnQgdGhlc2UgdmFsdWVzIHRvIG51bWVyaWMgd2UgY2FuIHVzZSB0aGUgYG11dGF0ZV9hdCgpYCBmdW5jdGlvbiBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlLgoKVGhlIGBtdXRhdGVfYXQoKWAgZnVuY3Rpb24gYWxsb3dzIHVzIHRvIHBlcmZvcm0gYSBmdW5jdGlvbiBvbiBzcGVjaWZpYyBjb2x1bW5zL3ZhcmlhYmxlcyB3aXRoaW4gYSB0aWJibGUuIFdlIG5lZWQgdG8gaW5kaWNhdGUgd2hpY2ggdmFyaWFibGVzIHRoYXQgd2Ugd291bGQgbGlrZSB0byBjb252ZXJ0IHVzaW5nIGB2YXJzKClgLiBJbiB0aGlzIGNhc2UgaWYgd2UgbG9vayBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBgZ3VpZGVsaW5lc2AgdGliYmxlLCB3ZSBjYW4gc2VlIHRoYXQgYG9wdGltYWxgLCBgbG93ZXJgIGFuZCBgdXBwZXJgIHNob3VsZCBiZSBjb252ZXJ0ZWQuIEFzIHRoZXNlIHRocmVlIGNvbHVtbnMgYXJlIHNlcXVlbnRpYWwsIHdlIGNhbiBzaW1wbHkgcHV0IGEgYDpgIGJldHdlZW4gYG9wdGltYWxgIGFuZCBgdXBwZXJgIHRvIGluZGljYXRlIHRoYXQgd2Ugd2FudCBhbGwgdGhlIHZhcmlhYmxlcyBpbiBiZXR3ZWVuIHRoZXNlIGNvbHVtbnMgdG8gYmUgY29udmVydGVkLiAKCmBgYHtyfQpndWlkZWxpbmVzJTw+JQogIG11dGF0ZV9hdCh2YXJzKGxvd2VyOnVwcGVyKSwgYXMubnVtZXJpYykKZ3VpZGVsaW5lcwpgYGAKCkdyZWF0ISBOb3cgdGhlc2UgdmFyaWFibGVzIGFyZSBvZiBjbGFzcyBgPGRibD5gIChzdGFuZHMgZm9yIGRvdWJsZSkgd2hpY2ggaW5kaWNhdGVzIHRoYXQgdGhleSBhcmUgbnVtZXJpYy4gSGVyZSBpcyBhIFtsaW5rXShodHRwOi8vdWMtci5naXRodWIuaW8vaW50ZWdlcl9kb3VibGUvKXt0YXJnZXQ9Il9ibGFuayJ9IGZvciBtb3JlIGluZm8gb24gbnVtZXJpYyBjbGFzc2VzIGluIFIuCgpJZiB3ZSBoYWQgbm90IHJlcGxhY2VkIHRoZSBgIsK3ImAgaW50ZXJwdW5jdCB2YWx1ZXMgdG8gYSBwZXJpb2QgY29udmVyc2lvbiBmcm9tIGNoYXJhY3RlciB0byBudW1lcmljIHdpbGwgYmUgcHJvYmxlbWF0aWMgYW5kIHdpbGwgcmVzdWx0IGluIE5BIHZhbHVlcy4KCiMjIyBEYXRhIHZhbHVlIHJlYXNzaWdubWVudHMKCldlIHNlZW0gdG8gaGF2ZSBsb3N0IHRoZSB3b3JkIGAiYmV2ZXJhZ2VzImAgZnJvbSB0aGUgYCJzdWdhci1zd2VldGVuZWQgYmV2ZXJhZ2VzImAgY2F0ZWdvcnksICBhcyB3ZWxsIGFzIGAiZmF0dHkgYWNpZHMiYCBmcm9tIHRoZSBgInNlYWZvb2Qgb21lZ2EgMyBmYXR0eSBhY2lkcyJgLCBhbmQgdGhlIGAicG9seXVuc2F0dXJhdGVkIGZhdHR5IGFjaWRzImAgY2F0ZWdvcmllcyBhcyB0aGUgZnVsbCBjYXRlZ29yeSBuYW1lIHdhcyBsaXN0ZWQgb24gdHdvIGxpbmVzIHdpdGhpbiB0aGUgdGFibGUuIFdlIHdvdWxkIGxpa2UgdG8gcmVwbGFjZSB0aGVzZSB2YWx1ZXMgd2l0aCB0aGUgZnVsbCBuYW1lLiAKClRvIHNlbGVjdCB0aGUgYGZvb2RgIGNvbHVtbiB3ZSB3aWxsIHNob3cgeW91IHNldmVyYWwgb3B0aW9ucy4gT25seSBhIGNvdXBsZSB3aWxsIHdvcmsgd2VsbCB3aXRoIHJlYXNzaWduaW5nIHRoZSBkYXRhIGluIHRoYXQgcGFydGljdWxhciB2YXJpYWJsZSB3aXRoaW4gYGd1aWRlbGluZXNgIHdpdGhvdXQgYXNzaWduaW5nIGFuIGludGVybWVkaWF0ZSBkYXRhIG9iamVjdC4gV2Ugd2lsbCBsb29rIHVzaW5nIGBtdXRhdGVfYXQoKWAsIGBwdWxsKClgLCBgc2VsZWN0KClgLCBhbmQgdHdvIHN0eWxlcyBvZiBicmFja2V0cyBgWyxjKCJ2YXJpYWJsZSBuYW1lIildYCBhbmQgYFtbInZhcmlhYmxlbmFtZSJdXWAuCgpUaGUgYnJhY2tldCBgWyxjKCJ2YXJpYWJsZSBuYW1lIildYCBvcHRpb24gYW5kIHRoZSBzZWxlY3QoKSBvcHRpb24gd2lsbCBncmFiIGEgdGliYmxlIChkYXRhIGZyYW1lKSB2ZXJzaW9uIG9mIHRoZSBmb29kIGNvbHVtbiBvdXQgb2YgZ3VpZGVsaW5lcy4gSG93ZXZlciB3ZSBjYW4ndCBzdGFydCBjb21tYW5kcyB3aXRoIHNlbGVjdCBmb3IgYXNzaWdubWVudHMuCgpgYGB7cn0KZ3VpZGVsaW5lc1ssYygiZm9vZCIpXSAjc2FtZSBvdXRwdXQgYXMgc2VsZWN0CnNlbGVjdChndWlkZWxpbmVzLCAiZm9vZCIpICMgc2FtZSBvdXRwdXQgYXMgYnJhY2tldHMKYGBgCgoKYHB1bGwoKWAgYW5kIHRoZSBicmFja2V0IGBbWyJ2YXJpYWJsZSBuYW1lIl1dYCBvcHRpb24gaW4gY29udHJhc3QsIHdpbGwgZ3JhYiB0aGUgdmVjdG9yIHZlcnNpb24gb2YgdGhlIGZvb2QgZGF0YToKCmBgYHtyfQpwdWxsKGd1aWRlbGluZXMsICJmb29kIikgIyBnZXQgY2hhcmFjdGVyIHZlY3RvciBub3QgYSB0aWJibGUKZ3VpZGVsaW5lc1tbImZvb2QiXV0jIGdldCBjaGFyYWN0ZXIgdmVjdG9yIG5vdCBhIHRpYmJsZQpgYGAKClRoZSBwdWxsIGZ1bmN0aW9uIGNhbiBiZSB2ZXJ5IHVzZWZ1bCB3aGVuIGNvbWJpbmVkIHdpdGggb3RoZXIgZnVuY3Rpb25zIChmb3IgZXhhbXBsZSB5b3UgdHlwaWNhbGx5IHdhbnQgdG8gdXNlIGEgdmVjdG9yIHdpdGggdGhlIGBzdHJfcmVwbGFjZSgpYCBmdW5jdGlvbiksIGJ1dCBqdXN0IGxpa2Ugc2VsZWN0LCB3ZSBjYW4ndCBzdGFydCBhc3NpZ25tZW50cyB3aXRoIGBwdWxsKClgLgoKClRoaXMgaXMgbm90IHBvc3NpYmxlIGFuZCB3aWxsIHJlc3VsdCBpbiBhbiBlcnJvcjoKYGBge3IsIGV2YWwgPSBGQUxTRX0Kc2VsZWN0KGd1aWRlbGluZXMsIGZvb2QpIDwtIAogICBzdHJfcmVwbGFjZSggCiAgIHB1bGwoZ3VpZGVsaW5lcywiZm9vZCIpLCAKICAgcGF0dGVybiA9ICJzdWdhci1zd2VldGVuZWQiLCAKICAgcmVwbGFjZW1lbnQgPSAic3VnYXItc3dlZXRlbmVkIGJldmVyYWdlcyIpCmBgYAoKVGhpcyB3aWxsIG9ubHkgcHJpbnQgdGhlIHJlc3VsdCwgYnV0IG5vdCByZWFzc2lnbiB0aGUgZm9vZCB2YXJpYWJsZSB2YWx1ZXM6CgpgYGB7cn0KZ3VpZGVsaW5lcyAlPiUKICAgcHVsbChmb29kKSU+JQogICBzdHJfcmVwbGFjZSggCiAgIHBhdHRlcm4gPSAic3VnYXItc3dlZXRlbmVkIiwgCiAgIHJlcGxhY2VtZW50ID0gInN1Z2FyLXN3ZWV0ZW5lZCBiZXZlcmFnZXMiKQpgYGAgICAKClVzaW5nIGBzZWxlY3QoKWAgd291bGQgd29yayBhcyB3ZWxsIHRvIHByaW50IHRoZSByZXN1bHQgKGFsdGhvdWdoIHRoZSByZXN1bHQgc3RydWN0dXJlIGlzIGRpZmZlcmVudCk6CgpgYGB7cn0KZ3VpZGVsaW5lcyAlPiUKICAgc2VsZWN0KGZvb2QpJT4lCiAgIHN0cl9yZXBsYWNlKCAKICAgcGF0dGVybiA9ICJzdWdhci1zd2VldGVuZWQiLCAKICAgcmVwbGFjZW1lbnQgPSAic3VnYXItc3dlZXRlbmVkIGJldmVyYWdlcyIpCgpgYGAKCiMjIyMgey5xdWVzdGlvbl9ibG9ja30KCjx1PlF1ZXN0aW9uIG9wcG9ydHVuaXR5OjwvdT4gCgpXaHkgZG8gdGhlc2UgY29tbWFuZHMgbm90IHJlYXNzaWduIHRoZSBmb29kIHZhcmlhYmxlIHZhbHVlcz8KCiMjIyMKClRoZSBicmFja2V0IG9wdGlvbiBpcyBncmVhdCBhbHRlcm5hdGl2ZSBhbmQgYWxsb3dzIHVzIHRvIHJlYXNzaWduIHRoZSB2YWx1ZXMgd2l0aGluIGd1aWRlbGluZXMgZWFzaWx5LiBFaXRoZXIgb2YgdGhlIHR3byBzdHlsZXMgb2YgYnJhY2tldHM6IGBbLGMoInZhcmlhYmxlIG5hbWUiKV1gIGFuZCBgW1sidmFyaWFibGVuYW1lIl1dYCB3aWxsIHdvcmsuCgpgYGB7cn0KIzFzdCBtZXRob2Q6IGBbLGMoInZhcmlhYmxlIG5hbWUiKV1gCiNSZXBsYWNpbmcgInN1Z2FyLXN3ZWV0ZW5lZCIgd2l0aCAic3VnYXItc3dlZXRlbmVkIGJldmVyYWdlcyIKZ3VpZGVsaW5lc1ssYygiZm9vZCIpXSA8LSAKICBzdHJfcmVwbGFjZSggCiAgcHVsbChndWlkZWxpbmVzLCJmb29kIiksIAogIHBhdHRlcm4gPSAic3VnYXItc3dlZXRlbmVkIiwgCiAgcmVwbGFjZW1lbnQgPSAic3VnYXItc3dlZXRlbmVkIGJldmVyYWdlcyIpCgojMm5kIG1ldGhvZDogYFtbInZhcmlhYmxlbmFtZSJdXWAKI1JlcGxhY2luZyAic2VhZm9vZCBvbWVnYS0zIiB3aXRoInNlYWZvb2Qgb21lZ2EtMyBmYXR0eSBhY2lkcyIKZ3VpZGVsaW5lc1tbImZvb2QiXV0gPC0gCiAgc3RyX3JlcGxhY2UoIAogIHB1bGwoZ3VpZGVsaW5lcywiZm9vZCIpLCAKICBwYXR0ZXJuID0gInNlYWZvb2Qgb21lZ2EtMyIsIAogIHJlcGxhY2VtZW50ID0gInNlYWZvb2Qgb21lZ2EtMyBmYXR0eSBhY2lkcyIpCgpndWlkZWxpbmVzCmBgYAoKRmluYWxseSwgdGhlIGJlc3Qgb3B0aW9uIGlzIHByb2JhYmx5IHRoZSBgbXV0YXRlX2F0KClgIGZ1bmN0aW9uIGZyb20gYGRwbHlyYC4gSW4gdGhpcyBjYXNlIHdlIG5lZWQgdG8gaW5jbHVkZSBgfmAgaW4gZnJvbnQgb2YgdGhlIGZ1bmN0aW9uIHRoYXQgd2Ugd291bGQgbGlrZSB0byB1c2Ugb24gdGhlIHZhbHVlcyBpbiBvdXIgYGZvb2RgIHZhcmlhYmxlcy4gV2UgYWxzbyBpbmNsdWRlIGAuYCBhcyBhIHJlcGxhY2VtZW50IHRvIHJlZmVyZW5jZSB0aGUgZGF0YSB0aGF0IHdlIHdhbnQgdG8gdXNlIHdpdGhpbiBgc3RyX3JlcGxhY2UoKWAgKHdoaWNoIGluIHRoaXMgY2FzZSBpcyB0aGUgYGZvb2RgIHZhcmlhYmxlIHZhbHVlcyBvZiBgZ3VpZGVsaW5lc2ApLgoKTm90aWNlIHdlIGRpZG4ndCBuZWVkIHRoaXMgd2hlbiB3ZSBwcmV2aW91c2x5IHVzZSBgbXV0YXRlX2F0KClgIHdpdGggdGhlIGBhcy5udW1lcmljKClgIGZ1bmN0aW9uLiBUaGlzIGlzIGJlY2F1c2UgdGhlIGBzdHJfcmVwbGFjZSgpYCBmdW5jdGlvbiByZXF1aXJlcyB1cyB0byBzcGVjaWZ5IHdoYXQgZGF0YSB3ZSBhcmUgdXNpbmcgYXMgb25lIG9mIHRoZSBhcmd1bWVudHMsIHdoaWxlIGBhcy5udW1lcmljKClgIGRvZXMgbm90LgoKYGBge3J9CgojUmVwbGFjaW5nICJwb2x5dW5zYXR1cmF0ZWQiIHdpdGgicG9seXVuc2F0dXJhdGVkIGZhdHR5IGFjaWRzIgpndWlkZWxpbmVzJTw+JQogIG11dGF0ZV9hdCh2YXJzKGZvb2QpLAogIH5zdHJfcmVwbGFjZSggCiAgc3RyaW5nID0gLiwgCiAgcGF0dGVybiA9ICJwb2x5dW5zYXR1cmF0ZWQiLCAKICByZXBsYWNlbWVudCA9ICJwb2x5dW5zYXR1cmF0ZWQgZmF0dHkgYWNpZHMiKSkKCmd1aWRlbGluZXMKCmBgYAoKVGhpcyBtaWdodCBiZSBjb25zaWRlcmVkIGEgYmV0dGVyIG9wdGlvbiBiZWNhdXNlIGl0IGlzIG1vcmUgcmVhZGFibGUgYXMgdG8gd2hlcmUgdGhlIGBmb29kYCBkYXRhIGNhbWUgZnJvbSB0aGF0IHdlIGFyZSByZXBsYWNpbmcgdmFsdWVzIHdpdGhpbi4KClRoZXJlIGlzIG9uZSBsYXN0IG1pbm9yIGRldGFpbC4uLiB0aGUgYGRpcmVjdGlvbmAgdmFyaWFibGUgaGFzIGxlYWRpbmcgc3BhY2VzIHN0aWxsLiBXZSBjYW4gdXNlIGBzdHJfdHJpbSgpYCB0byBmaXggdGhhdCEgKFlvdSBjb3VsZCBhbHNvIHVzZSBgc3RyX3NxdWlzaCgpYCB3aGljaCByZW1vdmVzIGFsbCB3aGl0ZSBzcGFjZXMsIG5vdCBqdXN0IGxlYWRpbmcgc3BhY2VzKQoKYGBge3J9Cmd1aWRlbGluZXMlPiUKICBtdXRhdGVfYXQodmFycyhkaXJlY3Rpb24pLCBzdHJfdHJpbSkKCiNnaXZlcyBpZGVudGljYWwgcmVzdWx0cyBpbiB0aGlzIGNhc2UKZ3VpZGVsaW5lcyU8PiUKICBtdXRhdGVfYXQodmFycyhkaXJlY3Rpb24pLCBzdHJfc3F1aXNoKQpndWlkZWxpbmVzCmBgYAoKT0shIE5vdyB3ZSBrbm93IGhvdyBtdWNoIG9mIGVhY2ggZGlldGFyeSBmYWN0b3Igd2UgZ2VuZXJhbGx5IG5lZWQgZm9yIG9wdGltYWwgaGVhbHRoIGFjY29yZGluZyB0byB0aGUgZ3VpZGVsaW5lcyB1c2VkIGluIHRoaXMgYXJ0aWNsZS4KCiAKCgojIyBXaGF0IGRpZCB3ZSBsZWFybj8KCjEpIFdlIGtub3cgd2hlbiB3aGVuIGBzdHJpbmdyYCBtaWdodCBiZSB1c2VmdWwgZm9yIHBhcnRpY3VsYXIga2luZHMgb2YgZGF0YS4KCkdlbm9taWMgc2VxdWVuY2UgZGF0YSwgdGV4dCBkYXRhIGV0Yy4KCjIpIFlvdSBrbm93IGhvdyB0byB1c2Ugc29tZSB2ZXJ5IHVzZWZ1bCBgc3RyaW5ncmAgZnVuY3Rpb25zIGxpa2U6CgpGdW5jdGlvbiAgIHwgVXNlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAotLS0tLS0tLS0tIHwtLS0tLS0tLS0tLS0tCmBzdHJfcmVwbGFjZSgpYCB8IHJlcGxhY2Ugb3IgZXhjaGFuZ2UgYSBwYXR0ZXJuIG9mIGNoYXJhY3RlcnMgZm9yIGFub3RoZXIgCmBzdHJfc3BsaXQoKWAgIHwgc3BsaXQgb3IgZGl2aWRlIHN0cmluZ3Mgb2YgYW55IHNpemUgKHdvcmRzL3NlbnRlbmNlcy9wYXJhZ3JhcGhzLykgaW50byBzdWJzdHJpbmdzCmBzdHJfc3Vic2V0KClgIHwgc2VsZWN0IHBhcnQgb2YgYSBzdHJpbmcgYmFzZWQgb24gYSBjaGFyYWN0ZXJpc3RpYwpgc3RyX2NvdW50KClgIHwgY291bnQgdGhlIG9jY3VycmVuY2Ugb2YgYSBzcGVjaWZpYyBjaGFyYWN0ZXIKYHN0cl93aGljaCgpYCB8IGlkZW50aWZ5IHdoZXJlIGFuIG9jY3VycmVuY2Ugb2YgYSBzcGVjaWZpYyBjaGFyYWN0ZXIgb2NjdXJzIApgc3RyX3JlbW92ZSgpYCB8IHJlbW92ZSBjaGFyYWN0ZXJzIGZyb20geW91ciBzdHJpbmdzCmBzdHJfdHJpbSgpYCB8IHJlbW92ZSBsZWFkaW5nIGFuZCB0YWlsaW5nIHdoaXRlIHNwYWNlIApgc3RyX3NxdWlzaCgpYCB8IHJlbW92ZSBhbGwgd2hpdGUgc3BhY2UKCjMpIFlvdSBrbm93IGhvdyB0byB3b3JrIHdpdGggcmVndWxhciBleHByZXNzaW9ucy4gCgpEb24ndCBmb3JnZXQgYFxcYCEKCjQpIFlvdSBjYW4gbmFtZSBvdGhlciBwYWNrYWdlcyB0aGF0IGFyZSB1c2VmdWwgZm9yIHdyYW5nbGluZyBkYXRhIHRoYXQgY29udGFpbnMgY2hhcmFjdGVycy4KCmBwZGZ0b29sc2AKCmB0aWR5cmAKCmBkcGx5cmAKCmBwdXJycmAKCgojIyBIZWxwZnVsIExpbmtzCgpGb3IgbW9yZSBoZWxwZnVsIHR1dG9yaWFscyBvZiBhIHNpbWlsYXIgc3R5bGUgYXMgdGhpcyBvbmUgc2VlIFtoZXJlXShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8vKS4gTW9yZSB3aWxsIGJlIGNvbWluZyBpbmNsdWRpbmcgYSBsb25nZXIgdmVyc2lvbiBvZiB0aGlzIHR1dG9yaWFsIQoKRm9yIG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHRpZHl2ZXJzZSBzZWUgW2hlcmVdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKS4KCkZvciBpbmZvcm1hdGlvbiBvbiBvdGhlciBgc3RyaW5ncmAgZnVuY3Rpb25zIHNlZSBbaGVyZV0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2luZGV4Lmh0bWwpLgoKU2VlIFtoZXJlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3RyaW5nci92aWduZXR0ZXMvcmVndWxhci1leHByZXNzaW9ucy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9IGZvciBtb3JlIGluZm8gYWJvdXQgcmVndWxhciBleHByZXNzaW9ucyBpbiBSLiAKCkdldCBDaGVhdCBTaGVhdHMgW2hlcmVdKGh0dHBzOi8vcnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykuCgoKPGI+PHU+SGVyZSBhcmUgbGlua3MgZm9yIHRoZXNlIHBhY2thZ2VzIGFuZCB0aGUgb3RoZXJzIHVzZWQgaW4gdGhpcyB0dXRvcmlhbDo8L3U+PC9iPgoKIFBhY2thZ2UgICB8IFVzZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKLS0tLS0tLS0tLSB8LS0tLS0tLS0tLS0tLQpbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgIHwgdG8gZWFzaWx5IGxvYWQgYW5kIHNhdmUgZGF0YQpbZHBseXJdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGFycmFuZ2UvZmlsdGVyL3NlbGVjdC9jb21wYXJlIHNwZWNpZmljIHN1YnNldHMgb2YgdGhlIGRhdGEgCltwZGZ0b29sc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3BkZnRvb2xzL3BkZnRvb2xzLnBkZil7dGFyZ2V0PSJfYmxhbmsifSAgIHwgdG8gcmVhZCBhIHBkZiBpbnRvIFIgICAKW3N0cmluZ3JdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnL2FydGljbGVzL3N0cmluZ3IuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgICB8IHRvIG1hbmlwdWxhdGUgdGhlIHRleHQgd2l0aGluIHRoZSBwZGYgb2YgdGhlIGRhdGEKW21hZ3JpdHRyXShodHRwczovL21hZ3JpdHRyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvbWFncml0dHIuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgIHwgdG8gdXNlIHRoZSBgJTw+JWAgcGlwcGluZyBvcGVyYXRvcgpbcHVycnJdKGh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIHBlcmZvcm0gZnVuY3Rpb25zIG9uIGFsbCBjb2x1bW5zIG9mIGEgdGliYmxlClt0aWJibGVdKGh0dHBzOi8vdGliYmxlLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICB8IHRvIGNyZWF0ZSBkYXRhIG9iamVjdHMgdGhhdCB3ZSBjYW4gbWFuaXB1bGF0ZSB3aXRoIGRwbHlyL3N0cmluZ3IvdGlkeXIvcHVycnIKW3RpZHlyXShodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBzZXBhcmF0ZSBkYXRhIHdpdGhpbiBhIGNvbHVtbiBpbnRvIG11bHRpcGxlIGNvbHVtbnMKCgo8Yj48dT5IZXJlIGlzIGEgc3VtbWFyeSBvZiBoZWxwZnVsIGxpbmtzIGFib3V0IG1hbnkgb2YgdGhlIGZ1bmN0aW9ucyB1c2VkIGluIHRoaXMgdHV0b3JpYWw6PC91PjwvYj4KCihUaGFua3MgdG8gW0xlb25hcmRvIENvbGxhZG8tVG9ycmVzXShodHRwOi8vbGNvbGxhZG90b3IuZ2l0aHViLmlvLykgZm9yIGdhdGhlcmluZyB0aGVzZSEpCgptYWdyaXR0cjo6W2AlPiVgXShodHRwczovL21hZ3JpdHRyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3BpcGUuaHRtbCkgYW5kIFtgJTw+JWBdKGh0dHBzOi8vbWFncml0dHIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvY29tcG91bmQuaHRtbCkKCmhlcmU6OltoZXJlKCldKGh0dHBzOi8vaGVyZS5yLWxpYi5vcmcvcmVmZXJlbmNlL2hlcmUuaHRtbCkKCmRwbHlyOjpbbnRoKCkgbGFzdCgpIGFuZCBmaXJzdCgpXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL250aC5odG1sKQoKcGRmdG9vbHM6OltwZGZfdGV4dCgpXShodHRwczovL2RvY3Mucm9wZW5zY2kub3JnL3BkZnRvb2xzL3JlZmVyZW5jZS9wZGZ0b29scy5odG1sKQoKdGlkeXI6OltnbGltcHNlKCldKGh0dHBzOi8vdGliYmxlLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dsaW1wc2UuaHRtbCkKCnN0cmluZ3I6OltzdHJfc3BsaXQoKSBhbmQgc3RyX3NwbGl0X2ZpeGVkKCldKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9zdHJfc3BsaXQuaHRtbCkKCnN0cmluZ3I6OltzdHJfcmVwbGFjZV9hbGwoKV0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3N0cl9yZXBsYWNlLmh0bWwpCgpwdXJycjo6W21hcCgpXShodHRwczovL3B1cnJyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL21hcC5odG1sKQoKdGliYmxlOjpbdGliYmxlKCldKGh0dHBzOi8vdGliYmxlLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3RpYmJsZS5odG1sICsgYXNfdGliYmxlKCkpCgp0aWR5cjo6W3NlcGFyYXRlKCldKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc2VwYXJhdGUuaHRtbCkKCnB1cnJyOjpbbW9kaWZ5X2F0KCldKGh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbW9kaWZ5Lmh0bWwpCgpyZWFkcjo6W3JlYWRfZmlsZSgpXShodHRwczovL3JlYWRyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3JlYWRfZmlsZS5odG1sKQoKc3RyaW5ncjo6W3N0cl9jb3VudCgpXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2Uvc3RyX2NvdW50Lmh0bWwpCgpkcGx5cjo6W3B1bGwoKV0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9wdWxsLmh0bWwpCgpkcGx5cjo6W25hX2lmKCldKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbmFfaWYuaHRtbCkKCmRwbHlyOjpbbXV0YXRlKCldKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbXV0YXRlLmh0bWwpCgpkcGx5cjo6W2ZpbHRlcigpXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2ZpbHRlci5odG1sKSAobm90IHN0YXRzOjpmaWx0ZXIoKSAhISkKCmRwbHlyOjpbdmFycygpXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3ZhcnMuaHRtbCkKCgoKCgo8ZGl2IGFsaWduPSJjZW50ZXIiPiAqVGhhbmtzIGZvciByZWFkaW5nISogYHIgZW1vOjpqaSgic21pbGUiKWAgYHIgZW1vOjpqaSgiaGVhcnQiKWA8L2Rpdj4KCg==