The output of most of the R chunks isn’t included in the HTML version of the file to keep it to a more reasonable file size. You can run the code in R to see the output.

This is an R Markdown document. Follow the link to learn more about R Markdown and the notebook format used during the workshop.

Note: This tutorial assumes you are familiar with regular expressions. There are regular expression tutorial materials available. If you’re not familiar with regular expressions, or you’re interested in the other functions in stringr, you may want the brief intro to stringr from the bonus session instead.

This tutorial also includes some practice writing regular expressions in addition to using the stringr package.

You don’t need to know or use the rest of the tidyverse to use stringr for regular expressions. However, there are a few examples below that process the results of extracting data with regular expressions with other tidyverse functions.

Setup

library(tidyverse)

stringr Intro

Like many other tidyverse packages, the stringr package provides a unified approach to working with text in R. The functionality in stringr can be achieved with other base R functions, but the inputs, names, and output vary. With stringr, there’s consistency. For example, the primary functions are named starting with str_ and they take the text you want to work with as the first input.

Other tidyverse packages also use a similar text matching approach as stringr. For example, you can select columns that match a regular expression with contains select helper. And when separating columns or pivoting data with tidyr, regular expressions can help there too.

Matching/Finding

Let’s start with matching text – just finding (i.e. detecting) which input strings match an expression. First, we need some text to match. We’ll use a list of astronauts from Wikipedia:

astronauts <- c("    United States male Joseph M. Acaba",
                "    United States male Loren Acton",
                "    United States male James Adamson",
                "    Soviet Union Russia male Viktor M. Afanasyev",
                "    Kazakhstan male Aydyn Aimbetov, first cosmonaut by KazCosmos-selection in space",
                "    United States male Thomas Akers",
                "    Japan male Toyohiro Akiyama, the first business-sponsored hi space traveler and the first Japanese person in space",
                "    Soviet Union male Vladimir Aksyonov",
                "    Saudi Arabia male Sultan Salman Al Saud, first Saudi Arabian in space, only royal person in space, first Middle Eastern person in space",
                "    United States male Buzz Aldrin, moonwalked, flew on Apollo 11; second person to walk on the Moon",
                "    Bulgaria male Aleksandr Panayotov Aleksandrov",
                "    Soviet Union male Aleksandr Pavlovich Aleksandrov",
                "    United States male Andrew M. Allen",
                "    United States male Joseph P. Allen",
                "    United Arab Emirates male Hazza Al Mansouri, first UAE astronaut",
                "    United States male Scott Altman",
                "    United States male William Anders, first Asian-born person in space (born in Hong Kong, but an American citizen)",
                "    United States male Clayton Anderson",
                "    United States male Michael P. Anderson (1959–2003), died on February 1, 2003, in the Space Shuttle Columbia disaster of STS-107[7]",
                "    Iran United States female Anousheh Ansari, fourth spaceflight participant, first woman of Muslim descent in space, and first Iranian in space",
                "    United States male Dominic A. Antonelli",
                "    United States male Jerome Apt",
                "    United States male Lee Archambault",
                "    United States male Neil Armstrong (1930–2012), moonwalked, flew on Apollo 11; first person to walk on the Moon[8]",
                "    United States male Richard R. Arnold",
                "    Russia male Oleg Artemyev",
                "    Soviet Union male Anatoly Artsebarsky",
                "    Soviet Union male Yuri Artyukhin (1930–1998)[9]",
                "    United States male Jeffrey Ashby",
                "    Soviet Union male Oleg Atkov",
                "    Soviet Union male Toktar Aubakirov, first Kazakh born person in space",
                "    United States female Serena Auñón-Chancellor",
                "    Soviet Union Russia male Sergei Avdeyev")

We’ll start by just getting an indicator, TRUE or FALSE, as to whether each piece of text contains our pattern. Let’s find lines with a number (\d) in them to start.

To type a \, we’ll need to double it. R doesn’t inherently know that it’s a regular expression, so it treats \ as an escape – to keep an actual \ in the regular expression, we do \\.

str_detect(astronauts, "\\d")

stringr functions automatically assume that any argument named “pattern” is a regular expression. It passes the input to the regex function for processing. If we wanted to match a literal string instead, we could instead wrap the input in fixed().

str_detect(astronauts, ".")  # regular expression, matches anything
str_detect(astronauts, fixed("."))  # not a regex - matches actual .

Flags

To set regular expression flags, such as case insensitive or dotall, we’ll need to use the regex function directly. For example:

str_detect(astronauts, regex("united states", 
                             ignore_case = FALSE))

Open the help page with ?stringr::modifiers.

Helper Function

stringr has a very handy helper function that will show you the matches for a regular expression. It will open a viewer for the text in the Viewer tab in the bottom right window in RStudio.

str_view_all(astronauts, "United States")

To view the full output, click on the icon to open it in a new window.

Convenience Matching

We could use ^ and $ to match the beginning and end of lines, but since this is a common operation, there are also stringr functions to do this:

str_starts(astronauts, " ")
str_ends(astronauts, "]")

There’s also a function to count the number of matches in a given string. Note that the astronauts vector contains many separate strings – this function counts the number of matches in each of these individual strings:

str_count(astronauts, "a")  # count a's
str_count(astronauts, " [A-Z]")  # count words starting with a capital letter

EXERCISE

sentences is a vector built in to stringr. It has short sentences in it. We’ll work with the first 20 sentences: sent20.

Find which sentences in sent20 have a word with at least 6 letters.

Reminders:

  • Make sure you’ve run the code to load tidyverse: library(tidyverse)
  • \w matches word characters – remember to double the \
  • Use {min, max} to specify how many times a previous character should match. For example, match a repeated at least 3 times: a{3,}
sent20 <- sentences[1:20]

Replacing

Let’s move beyond matching text to replacing text. An important thing to remember is that ALL of the text that matches the regular expression will be replaced, not just groups. You can reference captured groups with \1 for the first group, \2 for the second group, etc. When including these in the replacement expression, you need to double the \: \\1

str_replace(astronauts, "[mM]oon", "MOON")

If we look at the results, not all cases of “moon” or “Moon” were replaced. Let’s pull the lines in astronauts that contain “MOON” with str_subset:

str_replace(astronauts, "[mM]oon", "MOON") %>%
  str_subset("MOON")

Only the first instance of moon or Moon in each line was replaced. If we want to replace ALL of the times it appears, we need to use str_replace_all instead. This is equivalent to turning on the global regular expression flag.

str_replace_all(astronauts, "[mM]oon", "MOON") %>%
  str_subset("MOON")

There are *_all() versions of many of the stringr functions.

If we have capturing groups in our expression, we can use them in the replacement:

str_replace(astronauts, "    (.+) (f?e?male) (.+?)(,.+|$|\\(.+)", "\\2, \\1, \\3")

One common replacement case is when we just want to remove something. There’s a convenience function to replace matching text with an empty string, instead of having to type that out.

Remove the note markers like [1] from the end of some of the lines:

str_remove(astronauts, "\\[\\d+\\]")

EXERCISE

Using sent20 again (created above), replace any of the following words with ANIMAL: chicken, hog, salmon

Reminder:

  • You could capture multiple different words with an expression like: (word 1|word 2|word 3). | is or

Extracting

What if we want to pull out a specific piece of information? Or more than one piece of information?

There are a few variations on this. First, extracting the entire match:

str_extract(astronauts, "f?e?male")

If there may be more than one match per line, we can use str_extract_all. BUT it will make things more complicated, because instead of having a vector returned, we get a list of vectors:

str_extract_all(astronauts, "[mM]oon")

character(0) is an empty vector of type “character” which means no match was found.

We can instead get the results as a matrix of character values:

str_extract_all(astronauts, "[mM]oon", simplify=TRUE)

The character matrix may be all you need. You can turn it into a data frame with:

str_extract_all(astronauts, "[mM]oon", simplify=TRUE) %>% data.frame()
# or: data.frame(str_extract_all(astronauts, "[mM]oon", simplify=TRUE))

But, if we were working with a column in a data frame/tibble as part of a dplyr workflow, things might get complicated:

astro_df <- tibble(id=1:length(astronauts), 
                   info=astronauts)
astro_df

str_extract_all will result in a list column:

astro_df %>%
  mutate(moons = str_extract_all(info, "[mM]oon"))

We can unnest this:

astro_df %>%
  mutate(moons = str_extract_all(info, "[mM]oon")) %>% 
  unnest_wider(moons)  

EXERCISE

Instead of replacing animal names (chicken, hog, salmon) in sent20, let’s extract the animal name instead:

Extracting Groups

What if we want to use capturing groups instead of extracting all of the text that matches the expression?

str_match(astronauts, "    (.+) (f?e?male) (.+?)(,.+|$|\\(.+)")

This gives us a character matrix with 5 columns:

  • The entire text matched by the regular expression
  • Text captured by group 1
  • Text captured by group 2
  • Text captured by group 3
  • Text captured by group 4 (which we don’t need, just used for |) – we could make this group non-capturing in our expression instead with ?: - (?:,.+|$|\\(.+), or just ignore this output

We can convert this to a data frame like we did above. If we’re working with a dataframe:

astro_df %>%
  mutate(astro = str_match(info, "    (.+) (f?e?male) (.+?)(,.+|$|\\(.+)")) 

The output looks like magically got separate columns. But we didn’t:

astro_df %>%
  mutate(astro = str_match(info, "    (.+) (f?e?male) (.+?)(,.+|$|\\(.+)")) %>%
  dim()

Instead of using str_match and unnesting, when we have capturing groups, we can use tidyr::extract instead. And it lets us name the resulting columns. Use NA to omit a group.

astro_df %>%
  extract(info, 
          into=c("country", "gender", "name", NA), 
          regex="    (.+) (f?e?male) (.+?)(,.+|$|\\(.+)")

If we want to keep the original data as well:

astro_df %>%
  extract(info, 
          into=c("country", "gender", "name", NA), 
          regex="    (.+) (f?e?male) (.+?)(,.+|$|\\(.+)",
          remove = FALSE)

EXERCISE

Extract the second and third words from each sentence in sent20

Note: The third sentence makes this a little tricky. You can ignore this sentence if you want. Take it as a challenge to write a regex that also works on sentence 3.

Raw Strings

Doubling \ can get annoying and confusing. There is an alternative added recently to R. We can define a “raw” string that doesn’t require us to escape \. Instead of "...", we use r"(...)" Then we can use single \:

str_replace(astronauts, r"(    (.+) (f?e?male) (.+?)(,.+|$|\(.+))", r"(\2, \1, \3)")

Note that we needed to change 3 things:

Recap

You now know how to use regular expressions in R. As we saw in a few spots, sometimes you need tools other than just regular expressions to do what you want. Don’t be afraid to break things down into multiple steps and use all of the different tools available in R to get your data in the shape you want.

LS0tCnRpdGxlOiAnUmVndWxhciBFeHByZXNzaW9ucyB3aXRoIHN0cmluZ3InCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3IsIHNldHVwLCBpbmNsdWRlPUZBTFNFfQojIHlvdSBkb24ndCBuZWVkIHRvIHJ1biB0aGlzIHdoZW4gd29ya2luZyBpbiBSU3R1ZGlvCmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsPUZBTFNFKSAgIyB3aGVuIG1ha2luZyB0aGUgaHRtbCB2ZXJzaW9uIG9mIHRoaXMgZmlsZSwgZG9uJ3QgZXhlY3V0ZSB0aGUgY29kZQpgYGAKCipUaGUgb3V0cHV0IG9mIG1vc3Qgb2YgdGhlIFIgY2h1bmtzIGlzbid0IGluY2x1ZGVkIGluIHRoZSBIVE1MIHZlcnNpb24gb2YgdGhlIGZpbGUgdG8ga2VlcCBpdCB0byBhIG1vcmUgcmVhc29uYWJsZSBmaWxlIHNpemUuICBZb3UgY2FuIHJ1biB0aGUgY29kZSBpbiBSIHRvIHNlZSB0aGUgb3V0cHV0LioKClRoaXMgaXMgYW4gW1IgTWFya2Rvd25dKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tLykgZG9jdW1lbnQuICBGb2xsb3cgdGhlIGxpbmsgdG8gbGVhcm4gbW9yZSBhYm91dCBSIE1hcmtkb3duIGFuZCB0aGUgbm90ZWJvb2sgZm9ybWF0IHVzZWQgZHVyaW5nIHRoZSB3b3Jrc2hvcC4KCioqTm90ZToqKiBUaGlzIHR1dG9yaWFsIGFzc3VtZXMgeW91IGFyZSBmYW1pbGlhciB3aXRoIFtyZWd1bGFyIGV4cHJlc3Npb25zXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvc3RyaW5nci92aWduZXR0ZXMvcmVndWxhci1leHByZXNzaW9ucy5odG1sKS4gIFRoZXJlIGFyZSBbcmVndWxhciBleHByZXNzaW9uIHR1dG9yaWFsIG1hdGVyaWFsc10oaHR0cHM6Ly9naXRodWIuY29tL251aXRyY3MvcmVnZXhfd29ya3Nob3ApIGF2YWlsYWJsZS4gIElmIHlvdSdyZSBub3QgZmFtaWxpYXIgd2l0aCByZWd1bGFyIGV4cHJlc3Npb25zLCBvciB5b3UncmUgaW50ZXJlc3RlZCBpbiB0aGUgb3RoZXIgZnVuY3Rpb25zIGluIHN0cmluZ3IsIHlvdSBtYXkgd2FudCB0aGUgW2JyaWVmIGludHJvIHRvIHN0cmluZ3JdKGh0dHBzOi8vbnVpdHJjcy5naXRodWIuaW8vci10aWR5dmVyc2UvaHRtbC9vdGhlcnMuaHRtbCNzdHJpbmdyKSBmcm9tIHRoZSBib251cyBzZXNzaW9uIGluc3RlYWQuICAKClRoaXMgdHV0b3JpYWwgYWxzbyBpbmNsdWRlcyBzb21lIHByYWN0aWNlIHdyaXRpbmcgcmVndWxhciBleHByZXNzaW9ucyBpbiBhZGRpdGlvbiB0byB1c2luZyB0aGUgc3RyaW5nciBwYWNrYWdlLgoKWW91IGRvbid0IG5lZWQgdG8ga25vdyBvciB1c2UgdGhlIHJlc3Qgb2YgdGhlIHRpZHl2ZXJzZSB0byB1c2Ugc3RyaW5nciBmb3IgcmVndWxhciBleHByZXNzaW9ucy4gIEhvd2V2ZXIsIHRoZXJlIGFyZSBhIGZldyBleGFtcGxlcyBiZWxvdyB0aGF0IHByb2Nlc3MgdGhlIHJlc3VsdHMgb2YgZXh0cmFjdGluZyBkYXRhIHdpdGggcmVndWxhciBleHByZXNzaW9ucyB3aXRoIG90aGVyIHRpZHl2ZXJzZSBmdW5jdGlvbnMuCgojIFNldHVwCgpgYGB7ciwgZXZhbD1UUlVFLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgoKIyBzdHJpbmdyIEludHJvCgpMaWtlIG1hbnkgb3RoZXIgdGlkeXZlcnNlIHBhY2thZ2VzLCB0aGUgc3RyaW5nciBwYWNrYWdlIHByb3ZpZGVzIGEgdW5pZmllZCBhcHByb2FjaCB0byB3b3JraW5nIHdpdGggdGV4dCBpbiBSLiAgVGhlIGZ1bmN0aW9uYWxpdHkgaW4gc3RyaW5nciBjYW4gYmUgYWNoaWV2ZWQgd2l0aCBvdGhlciBiYXNlIFIgZnVuY3Rpb25zLCBidXQgdGhlIGlucHV0cywgbmFtZXMsIGFuZCBvdXRwdXQgdmFyeS4gIFdpdGggc3RyaW5nciwgdGhlcmUncyBjb25zaXN0ZW5jeS4gIEZvciBleGFtcGxlLCB0aGUgcHJpbWFyeSBmdW5jdGlvbnMgYXJlIG5hbWVkIHN0YXJ0aW5nIHdpdGggYHN0cl9gIGFuZCB0aGV5IHRha2UgdGhlIHRleHQgeW91IHdhbnQgdG8gd29yayB3aXRoIGFzIHRoZSBmaXJzdCBpbnB1dC4KCk90aGVyIHRpZHl2ZXJzZSBwYWNrYWdlcyBhbHNvIHVzZSBhIHNpbWlsYXIgdGV4dCBtYXRjaGluZyBhcHByb2FjaCBhcyBzdHJpbmdyLiAgRm9yIGV4YW1wbGUsIHlvdSBjYW4gc2VsZWN0IGNvbHVtbnMgdGhhdCBtYXRjaCBhIHJlZ3VsYXIgZXhwcmVzc2lvbiB3aXRoIGBjb250YWluc2Agc2VsZWN0IGhlbHBlci4gIEFuZCB3aGVuIFtzZXBhcmF0aW5nIGNvbHVtbnMgb3IgcGl2b3RpbmcgZGF0YV0oaHR0cHM6Ly9udWl0cmNzLmdpdGh1Yi5pby9yLXRpZHl2ZXJzZS9odG1sL3RpZHlyLmh0bWwjc2VwYXJhdGUtdmFyaWFibGUtbmFtZXMpIHdpdGggdGlkeXIsIHJlZ3VsYXIgZXhwcmVzc2lvbnMgY2FuIGhlbHAgdGhlcmUgdG9vLgoKCiMgTWF0Y2hpbmcvRmluZGluZwoKTGV0J3Mgc3RhcnQgd2l0aCBtYXRjaGluZyB0ZXh0IC0tIGp1c3QgZmluZGluZyAoaS5lLiAqKmRldGVjdGluZyoqKSB3aGljaCBpbnB1dCBzdHJpbmdzIG1hdGNoIGFuIGV4cHJlc3Npb24uICBGaXJzdCwgd2UgbmVlZCBzb21lIHRleHQgdG8gbWF0Y2guICBXZSdsbCB1c2UgYSBsaXN0IG9mIGFzdHJvbmF1dHMgZnJvbSBXaWtpcGVkaWE6CgpgYGB7cn0KYXN0cm9uYXV0cyA8LSBjKCIgICAgVW5pdGVkIFN0YXRlcyBtYWxlIEpvc2VwaCBNLiBBY2FiYSIsCiAgICAgICAgICAgICAgICAiICAgIFVuaXRlZCBTdGF0ZXMgbWFsZSBMb3JlbiBBY3RvbiIsCiAgICAgICAgICAgICAgICAiICAgIFVuaXRlZCBTdGF0ZXMgbWFsZSBKYW1lcyBBZGFtc29uIiwKICAgICAgICAgICAgICAgICIgICAgU292aWV0IFVuaW9uIFJ1c3NpYSBtYWxlIFZpa3RvciBNLiBBZmFuYXN5ZXYiLAogICAgICAgICAgICAgICAgIiAgICBLYXpha2hzdGFuIG1hbGUgQXlkeW4gQWltYmV0b3YsIGZpcnN0IGNvc21vbmF1dCBieSBLYXpDb3Ntb3Mtc2VsZWN0aW9uIGluIHNwYWNlIiwKICAgICAgICAgICAgICAgICIgICAgVW5pdGVkIFN0YXRlcyBtYWxlIFRob21hcyBBa2VycyIsCiAgICAgICAgICAgICAgICAiICAgIEphcGFuIG1hbGUgVG95b2hpcm8gQWtpeWFtYSwgdGhlIGZpcnN0IGJ1c2luZXNzLXNwb25zb3JlZCBoaSBzcGFjZSB0cmF2ZWxlciBhbmQgdGhlIGZpcnN0IEphcGFuZXNlIHBlcnNvbiBpbiBzcGFjZSIsCiAgICAgICAgICAgICAgICAiICAgIFNvdmlldCBVbmlvbiBtYWxlIFZsYWRpbWlyIEFrc3lvbm92IiwKICAgICAgICAgICAgICAgICIgICAgU2F1ZGkgQXJhYmlhIG1hbGUgU3VsdGFuIFNhbG1hbiBBbCBTYXVkLCBmaXJzdCBTYXVkaSBBcmFiaWFuIGluIHNwYWNlLCBvbmx5IHJveWFsIHBlcnNvbiBpbiBzcGFjZSwgZmlyc3QgTWlkZGxlIEVhc3Rlcm4gcGVyc29uIGluIHNwYWNlIiwKICAgICAgICAgICAgICAgICIgICAgVW5pdGVkIFN0YXRlcyBtYWxlIEJ1enogQWxkcmluLCBtb29ud2Fsa2VkLCBmbGV3IG9uIEFwb2xsbyAxMTsgc2Vjb25kIHBlcnNvbiB0byB3YWxrIG9uIHRoZSBNb29uIiwKICAgICAgICAgICAgICAgICIgICAgQnVsZ2FyaWEgbWFsZSBBbGVrc2FuZHIgUGFuYXlvdG92IEFsZWtzYW5kcm92IiwKICAgICAgICAgICAgICAgICIgICAgU292aWV0IFVuaW9uIG1hbGUgQWxla3NhbmRyIFBhdmxvdmljaCBBbGVrc2FuZHJvdiIsCiAgICAgICAgICAgICAgICAiICAgIFVuaXRlZCBTdGF0ZXMgbWFsZSBBbmRyZXcgTS4gQWxsZW4iLAogICAgICAgICAgICAgICAgIiAgICBVbml0ZWQgU3RhdGVzIG1hbGUgSm9zZXBoIFAuIEFsbGVuIiwKICAgICAgICAgICAgICAgICIgICAgVW5pdGVkIEFyYWIgRW1pcmF0ZXMgbWFsZSBIYXp6YSBBbCBNYW5zb3VyaSwgZmlyc3QgVUFFIGFzdHJvbmF1dCIsCiAgICAgICAgICAgICAgICAiICAgIFVuaXRlZCBTdGF0ZXMgbWFsZSBTY290dCBBbHRtYW4iLAogICAgICAgICAgICAgICAgIiAgICBVbml0ZWQgU3RhdGVzIG1hbGUgV2lsbGlhbSBBbmRlcnMsIGZpcnN0IEFzaWFuLWJvcm4gcGVyc29uIGluIHNwYWNlIChib3JuIGluIEhvbmcgS29uZywgYnV0IGFuIEFtZXJpY2FuIGNpdGl6ZW4pIiwKICAgICAgICAgICAgICAgICIgICAgVW5pdGVkIFN0YXRlcyBtYWxlIENsYXl0b24gQW5kZXJzb24iLAogICAgICAgICAgICAgICAgIiAgICBVbml0ZWQgU3RhdGVzIG1hbGUgTWljaGFlbCBQLiBBbmRlcnNvbiAoMTk1OeKAkzIwMDMpLCBkaWVkIG9uIEZlYnJ1YXJ5IDEsIDIwMDMsIGluIHRoZSBTcGFjZSBTaHV0dGxlIENvbHVtYmlhIGRpc2FzdGVyIG9mIFNUUy0xMDdbN10iLAogICAgICAgICAgICAgICAgIiAgICBJcmFuIFVuaXRlZCBTdGF0ZXMgZmVtYWxlIEFub3VzaGVoIEFuc2FyaSwgZm91cnRoIHNwYWNlZmxpZ2h0IHBhcnRpY2lwYW50LCBmaXJzdCB3b21hbiBvZiBNdXNsaW0gZGVzY2VudCBpbiBzcGFjZSwgYW5kIGZpcnN0IElyYW5pYW4gaW4gc3BhY2UiLAogICAgICAgICAgICAgICAgIiAgICBVbml0ZWQgU3RhdGVzIG1hbGUgRG9taW5pYyBBLiBBbnRvbmVsbGkiLAogICAgICAgICAgICAgICAgIiAgICBVbml0ZWQgU3RhdGVzIG1hbGUgSmVyb21lIEFwdCIsCiAgICAgICAgICAgICAgICAiICAgIFVuaXRlZCBTdGF0ZXMgbWFsZSBMZWUgQXJjaGFtYmF1bHQiLAogICAgICAgICAgICAgICAgIiAgICBVbml0ZWQgU3RhdGVzIG1hbGUgTmVpbCBBcm1zdHJvbmcgKDE5MzDigJMyMDEyKSwgbW9vbndhbGtlZCwgZmxldyBvbiBBcG9sbG8gMTE7IGZpcnN0IHBlcnNvbiB0byB3YWxrIG9uIHRoZSBNb29uWzhdIiwKICAgICAgICAgICAgICAgICIgICAgVW5pdGVkIFN0YXRlcyBtYWxlIFJpY2hhcmQgUi4gQXJub2xkIiwKICAgICAgICAgICAgICAgICIgICAgUnVzc2lhIG1hbGUgT2xlZyBBcnRlbXlldiIsCiAgICAgICAgICAgICAgICAiICAgIFNvdmlldCBVbmlvbiBtYWxlIEFuYXRvbHkgQXJ0c2ViYXJza3kiLAogICAgICAgICAgICAgICAgIiAgICBTb3ZpZXQgVW5pb24gbWFsZSBZdXJpIEFydHl1a2hpbiAoMTkzMOKAkzE5OTgpWzldIiwKICAgICAgICAgICAgICAgICIgICAgVW5pdGVkIFN0YXRlcyBtYWxlIEplZmZyZXkgQXNoYnkiLAogICAgICAgICAgICAgICAgIiAgICBTb3ZpZXQgVW5pb24gbWFsZSBPbGVnIEF0a292IiwKICAgICAgICAgICAgICAgICIgICAgU292aWV0IFVuaW9uIG1hbGUgVG9rdGFyIEF1YmFraXJvdiwgZmlyc3QgS2F6YWtoIGJvcm4gcGVyc29uIGluIHNwYWNlIiwKICAgICAgICAgICAgICAgICIgICAgVW5pdGVkIFN0YXRlcyBmZW1hbGUgU2VyZW5hIEF1w7HDs24tQ2hhbmNlbGxvciIsCiAgICAgICAgICAgICAgICAiICAgIFNvdmlldCBVbmlvbiBSdXNzaWEgbWFsZSBTZXJnZWkgQXZkZXlldiIpCmBgYAoKV2UnbGwgc3RhcnQgYnkganVzdCBnZXR0aW5nIGFuIGluZGljYXRvciwgVFJVRSBvciBGQUxTRSwgYXMgdG8gd2hldGhlciBlYWNoIHBpZWNlIG9mIHRleHQgY29udGFpbnMgb3VyIHBhdHRlcm4uICBMZXQncyBmaW5kIGxpbmVzIHdpdGggYSBudW1iZXIgKGBcZGApIGluIHRoZW0gdG8gc3RhcnQuCgpUbyB0eXBlIGEgYFxgLCB3ZSdsbCBuZWVkIHRvIGRvdWJsZSBpdC4gIFIgZG9lc24ndCBpbmhlcmVudGx5IGtub3cgdGhhdCBpdCdzIGEgcmVndWxhciBleHByZXNzaW9uLCBzbyBpdCB0cmVhdHMgYFxgIGFzIGFuIGVzY2FwZSAtLSB0byBrZWVwIGFuIGFjdHVhbCBgXGAgaW4gdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiwgd2UgZG8gYFxcYC4gIAoKYGBge3J9CnN0cl9kZXRlY3QoYXN0cm9uYXV0cywgIlxcZCIpCmBgYAoKc3RyaW5nciBmdW5jdGlvbnMgYXV0b21hdGljYWxseSBhc3N1bWUgdGhhdCBhbnkgYXJndW1lbnQgbmFtZWQgInBhdHRlcm4iIGlzIGEgcmVndWxhciBleHByZXNzaW9uLiAgSXQgcGFzc2VzIHRoZSBpbnB1dCB0byB0aGUgYHJlZ2V4YCBmdW5jdGlvbiBmb3IgcHJvY2Vzc2luZy4gIElmIHdlIHdhbnRlZCB0byBtYXRjaCBhIGxpdGVyYWwgc3RyaW5nIGluc3RlYWQsIHdlIGNvdWxkIGluc3RlYWQgd3JhcCB0aGUgaW5wdXQgaW4gYGZpeGVkKClgLiAgCgpgYGB7cn0Kc3RyX2RldGVjdChhc3Ryb25hdXRzLCAiLiIpICAjIHJlZ3VsYXIgZXhwcmVzc2lvbiwgbWF0Y2hlcyBhbnl0aGluZwpgYGAKCmBgYHtyfQpzdHJfZGV0ZWN0KGFzdHJvbmF1dHMsIGZpeGVkKCIuIikpICAjIG5vdCBhIHJlZ2V4IC0gbWF0Y2hlcyBhY3R1YWwgLgpgYGAKCiMjIEZsYWdzCgpUbyBzZXQgcmVndWxhciBleHByZXNzaW9uIGZsYWdzLCBzdWNoIGFzIGNhc2UgaW5zZW5zaXRpdmUgb3IgZG90YWxsLCB3ZSdsbCBuZWVkIHRvIHVzZSB0aGUgYHJlZ2V4YCBmdW5jdGlvbiBkaXJlY3RseS4gIEZvciBleGFtcGxlOgoKYGBge3J9CnN0cl9kZXRlY3QoYXN0cm9uYXV0cywgcmVnZXgoInVuaXRlZCBzdGF0ZXMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IEZBTFNFKSkKYGBgCgpPcGVuIHRoZSBoZWxwIHBhZ2Ugd2l0aCBgP3N0cmluZ3I6Om1vZGlmaWVyc2AuCgoKIyMgSGVscGVyIEZ1bmN0aW9uCgpzdHJpbmdyIGhhcyBhIHZlcnkgaGFuZHkgaGVscGVyIGZ1bmN0aW9uIHRoYXQgd2lsbCBzaG93IHlvdSB0aGUgbWF0Y2hlcyBmb3IgYSByZWd1bGFyIGV4cHJlc3Npb24uICBJdCB3aWxsIG9wZW4gYSB2aWV3ZXIgZm9yIHRoZSB0ZXh0IGluIHRoZSBWaWV3ZXIgdGFiIGluIHRoZSBib3R0b20gcmlnaHQgd2luZG93IGluIFJTdHVkaW8uICAKCmBgYHtyfQpzdHJfdmlld19hbGwoYXN0cm9uYXV0cywgIlVuaXRlZCBTdGF0ZXMiKQpgYGAKClRvIHZpZXcgdGhlIGZ1bGwgb3V0cHV0LCBjbGljayBvbiB0aGUgaWNvbiB0byBvcGVuIGl0IGluIGEgbmV3IHdpbmRvdy4gIAoKCiMjIENvbnZlbmllbmNlIE1hdGNoaW5nCgpXZSBjb3VsZCB1c2UgYF5gIGFuZCBgJGAgdG8gbWF0Y2ggdGhlIGJlZ2lubmluZyBhbmQgZW5kIG9mIGxpbmVzLCBidXQgc2luY2UgdGhpcyBpcyBhIGNvbW1vbiBvcGVyYXRpb24sIHRoZXJlIGFyZSBhbHNvIHN0cmluZ3IgZnVuY3Rpb25zIHRvIGRvIHRoaXM6CgpgYGB7cn0Kc3RyX3N0YXJ0cyhhc3Ryb25hdXRzLCAiICIpCmBgYAoKCmBgYHtyfQpzdHJfZW5kcyhhc3Ryb25hdXRzLCAiXSIpCmBgYAoKVGhlcmUncyBhbHNvIGEgZnVuY3Rpb24gdG8gY291bnQgdGhlIG51bWJlciBvZiBtYXRjaGVzIGluIGEgZ2l2ZW4gc3RyaW5nLiAgTm90ZSB0aGF0IHRoZSBgYXN0cm9uYXV0c2AgdmVjdG9yIGNvbnRhaW5zIG1hbnkgc2VwYXJhdGUgc3RyaW5ncyAtLSB0aGlzIGZ1bmN0aW9uIGNvdW50cyB0aGUgbnVtYmVyIG9mIG1hdGNoZXMgaW4gZWFjaCBvZiB0aGVzZSBpbmRpdmlkdWFsIHN0cmluZ3M6CgpgYGB7cn0Kc3RyX2NvdW50KGFzdHJvbmF1dHMsICJhIikgICMgY291bnQgYSdzCnN0cl9jb3VudChhc3Ryb25hdXRzLCAiIFtBLVpdIikgICMgY291bnQgd29yZHMgc3RhcnRpbmcgd2l0aCBhIGNhcGl0YWwgbGV0dGVyCmBgYAoKCiMjIyBFWEVSQ0lTRQoKYHNlbnRlbmNlc2AgaXMgYSB2ZWN0b3IgYnVpbHQgaW4gdG8gc3RyaW5nci4gIEl0IGhhcyBzaG9ydCBzZW50ZW5jZXMgaW4gaXQuICBXZSdsbCB3b3JrIHdpdGggdGhlIGZpcnN0IDIwIHNlbnRlbmNlczogYHNlbnQyMGAuCgpGaW5kIHdoaWNoIHNlbnRlbmNlcyBpbiBgc2VudDIwYCBoYXZlIGEgd29yZCB3aXRoIGF0IGxlYXN0IDYgbGV0dGVycy4KClJlbWluZGVyczoKCiogTWFrZSBzdXJlIHlvdSd2ZSBydW4gdGhlIGNvZGUgdG8gbG9hZCB0aWR5dmVyc2U6IGBsaWJyYXJ5KHRpZHl2ZXJzZSlgCiogYFx3YCBtYXRjaGVzIHdvcmQgY2hhcmFjdGVycyAtLSByZW1lbWJlciB0byBkb3VibGUgdGhlIGBcYAoqIFVzZSBge21pbiwgbWF4fWAgdG8gc3BlY2lmeSBob3cgbWFueSB0aW1lcyBhIHByZXZpb3VzIGNoYXJhY3RlciBzaG91bGQgbWF0Y2guICBGb3IgZXhhbXBsZSwgbWF0Y2ggYSByZXBlYXRlZCBhdCBsZWFzdCAzIHRpbWVzOiBgYXszLH1gCgpgYGB7ciwgZXZhbD1UUlVFfQpzZW50MjAgPC0gc2VudGVuY2VzWzE6MjBdCmBgYAoKCgojIFJlcGxhY2luZwoKTGV0J3MgbW92ZSBiZXlvbmQgbWF0Y2hpbmcgdGV4dCB0byByZXBsYWNpbmcgdGV4dC4gIEFuIGltcG9ydGFudCB0aGluZyB0byByZW1lbWJlciBpcyB0aGF0IEFMTCBvZiB0aGUgdGV4dCB0aGF0IG1hdGNoZXMgdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbiB3aWxsIGJlIHJlcGxhY2VkLCBub3QganVzdCBncm91cHMuICBZb3UgY2FuIHJlZmVyZW5jZSBjYXB0dXJlZCBncm91cHMgd2l0aCBgXDFgIGZvciB0aGUgZmlyc3QgZ3JvdXAsIGBcMmAgZm9yIHRoZSBzZWNvbmQgZ3JvdXAsIGV0Yy4gIFdoZW4gaW5jbHVkaW5nIHRoZXNlIGluIHRoZSByZXBsYWNlbWVudCBleHByZXNzaW9uLCB5b3UgbmVlZCB0byBkb3VibGUgdGhlIGBcYDogYFxcMWAKCmBgYHtyfQpzdHJfcmVwbGFjZShhc3Ryb25hdXRzLCAiW21NXW9vbiIsICJNT09OIikKYGBgCgpJZiB3ZSBsb29rIGF0IHRoZSByZXN1bHRzLCBub3QgYWxsIGNhc2VzIG9mICJtb29uIiBvciAiTW9vbiIgd2VyZSByZXBsYWNlZC4gIExldCdzIHB1bGwgdGhlIGxpbmVzIGluIGBhc3Ryb25hdXRzYCB0aGF0IGNvbnRhaW4gIk1PT04iIHdpdGggYHN0cl9zdWJzZXRgOgoKYGBge3J9CnN0cl9yZXBsYWNlKGFzdHJvbmF1dHMsICJbbU1db29uIiwgIk1PT04iKSAlPiUKICBzdHJfc3Vic2V0KCJNT09OIikKYGBgCgpPbmx5IHRoZSBmaXJzdCBpbnN0YW5jZSBvZiBtb29uIG9yIE1vb24gaW4gZWFjaCBsaW5lIHdhcyByZXBsYWNlZC4gIElmIHdlIHdhbnQgdG8gcmVwbGFjZSBBTEwgb2YgdGhlIHRpbWVzIGl0IGFwcGVhcnMsIHdlIG5lZWQgdG8gdXNlIGBzdHJfcmVwbGFjZV9hbGxgIGluc3RlYWQuICBUaGlzIGlzIGVxdWl2YWxlbnQgdG8gdHVybmluZyBvbiB0aGUgZ2xvYmFsIHJlZ3VsYXIgZXhwcmVzc2lvbiBmbGFnLiAgCgpgYGB7cn0Kc3RyX3JlcGxhY2VfYWxsKGFzdHJvbmF1dHMsICJbbU1db29uIiwgIk1PT04iKSAlPiUKICBzdHJfc3Vic2V0KCJNT09OIikKYGBgCgpUaGVyZSBhcmUgYCpfYWxsKClgIHZlcnNpb25zIG9mIG1hbnkgb2YgdGhlIHN0cmluZ3IgZnVuY3Rpb25zLiAgCgpJZiB3ZSBoYXZlIGNhcHR1cmluZyBncm91cHMgaW4gb3VyIGV4cHJlc3Npb24sIHdlIGNhbiB1c2UgdGhlbSBpbiB0aGUgcmVwbGFjZW1lbnQ6CgpgYGB7cn0Kc3RyX3JlcGxhY2UoYXN0cm9uYXV0cywgIiAgICAoLispIChmP2U/bWFsZSkgKC4rPykoLC4rfCR8XFwoLispIiwgIlxcMiwgXFwxLCBcXDMiKQpgYGAKCk9uZSBjb21tb24gcmVwbGFjZW1lbnQgY2FzZSBpcyB3aGVuIHdlIGp1c3Qgd2FudCB0byByZW1vdmUgc29tZXRoaW5nLiAgVGhlcmUncyBhIGNvbnZlbmllbmNlIGZ1bmN0aW9uIHRvIHJlcGxhY2UgbWF0Y2hpbmcgdGV4dCB3aXRoIGFuIGVtcHR5IHN0cmluZywgaW5zdGVhZCBvZiBoYXZpbmcgdG8gdHlwZSB0aGF0IG91dC4KClJlbW92ZSB0aGUgbm90ZSBtYXJrZXJzIGxpa2UgYFsxXWAgZnJvbSB0aGUgZW5kIG9mIHNvbWUgb2YgdGhlIGxpbmVzOgoKYGBge3J9CnN0cl9yZW1vdmUoYXN0cm9uYXV0cywgIlxcW1xcZCtcXF0iKQpgYGAKCgoKIyMjIEVYRVJDSVNFCgpVc2luZyBgc2VudDIwYCBhZ2FpbiAoY3JlYXRlZCBhYm92ZSksIHJlcGxhY2UgYW55IG9mIHRoZSBmb2xsb3dpbmcgd29yZHMgd2l0aCBBTklNQUw6IGNoaWNrZW4sIGhvZywgc2FsbW9uCgpSZW1pbmRlcjogCgoqIFlvdSBjb3VsZCBjYXB0dXJlIG11bHRpcGxlIGRpZmZlcmVudCB3b3JkcyB3aXRoIGFuIGV4cHJlc3Npb24gbGlrZTogYCh3b3JkIDF8d29yZCAyfHdvcmQgMylgLiAgYHxgIGlzIG9yCgpgYGB7cn0KCmBgYAoKCgojIEV4dHJhY3RpbmcKCldoYXQgaWYgd2Ugd2FudCB0byBwdWxsIG91dCBhIHNwZWNpZmljIHBpZWNlIG9mIGluZm9ybWF0aW9uPyAgT3IgbW9yZSB0aGFuIG9uZSBwaWVjZSBvZiBpbmZvcm1hdGlvbj8KClRoZXJlIGFyZSBhIGZldyB2YXJpYXRpb25zIG9uIHRoaXMuICBGaXJzdCwgZXh0cmFjdGluZyB0aGUgZW50aXJlIG1hdGNoOgoKYGBge3J9CnN0cl9leHRyYWN0KGFzdHJvbmF1dHMsICJmP2U/bWFsZSIpCmBgYAoKSWYgdGhlcmUgbWF5IGJlIG1vcmUgdGhhbiBvbmUgbWF0Y2ggcGVyIGxpbmUsIHdlIGNhbiB1c2UgYHN0cl9leHRyYWN0X2FsbGAuICBCVVQgaXQgd2lsbCBtYWtlIHRoaW5ncyBtb3JlIGNvbXBsaWNhdGVkLCBiZWNhdXNlIGluc3RlYWQgb2YgaGF2aW5nIGEgdmVjdG9yIHJldHVybmVkLCB3ZSBnZXQgYSBsaXN0IG9mIHZlY3RvcnM6CgpgYGB7cn0Kc3RyX2V4dHJhY3RfYWxsKGFzdHJvbmF1dHMsICJbbU1db29uIikKYGBgCgpgY2hhcmFjdGVyKDApYCBpcyBhbiBlbXB0eSB2ZWN0b3Igb2YgdHlwZSAiY2hhcmFjdGVyIiB3aGljaCBtZWFucyBubyBtYXRjaCB3YXMgZm91bmQuICAKCldlIGNhbiBpbnN0ZWFkIGdldCB0aGUgcmVzdWx0cyBhcyBhIG1hdHJpeCBvZiBjaGFyYWN0ZXIgdmFsdWVzOgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChhc3Ryb25hdXRzLCAiW21NXW9vbiIsIHNpbXBsaWZ5PVRSVUUpCmBgYAoKVGhlIGNoYXJhY3RlciBtYXRyaXggbWF5IGJlIGFsbCB5b3UgbmVlZC4gIFlvdSBjYW4gdHVybiBpdCBpbnRvIGEgZGF0YSBmcmFtZSB3aXRoOgoKYGBge3J9CnN0cl9leHRyYWN0X2FsbChhc3Ryb25hdXRzLCAiW21NXW9vbiIsIHNpbXBsaWZ5PVRSVUUpICU+JSBkYXRhLmZyYW1lKCkKIyBvcjogZGF0YS5mcmFtZShzdHJfZXh0cmFjdF9hbGwoYXN0cm9uYXV0cywgIlttTV1vb24iLCBzaW1wbGlmeT1UUlVFKSkKYGBgCgpCdXQsIGlmIHdlIHdlcmUgd29ya2luZyB3aXRoIGEgY29sdW1uIGluIGEgZGF0YSBmcmFtZS90aWJibGUgYXMgcGFydCBvZiBhIGRwbHlyIHdvcmtmbG93LCB0aGluZ3MgbWlnaHQgZ2V0IGNvbXBsaWNhdGVkOgoKYGBge3J9CmFzdHJvX2RmIDwtIHRpYmJsZShpZD0xOmxlbmd0aChhc3Ryb25hdXRzKSwgCiAgICAgICAgICAgICAgICAgICBpbmZvPWFzdHJvbmF1dHMpCmFzdHJvX2RmCmBgYAoKYHN0cl9leHRyYWN0X2FsbGAgd2lsbCByZXN1bHQgaW4gYSBsaXN0IGNvbHVtbjoKCmBgYHtyfQphc3Ryb19kZiAlPiUKICBtdXRhdGUobW9vbnMgPSBzdHJfZXh0cmFjdF9hbGwoaW5mbywgIlttTV1vb24iKSkKYGBgCgpXZSBjYW4gdW5uZXN0IHRoaXM6IAoKYGBge3J9CmFzdHJvX2RmICU+JQogIG11dGF0ZShtb29ucyA9IHN0cl9leHRyYWN0X2FsbChpbmZvLCAiW21NXW9vbiIpKSAlPiUgCiAgdW5uZXN0X3dpZGVyKG1vb25zKSAgCmBgYAoKCiMjIyBFWEVSQ0lTRQoKSW5zdGVhZCBvZiByZXBsYWNpbmcgYW5pbWFsIG5hbWVzIChjaGlja2VuLCBob2csIHNhbG1vbikgaW4gYHNlbnQyMGAsIGxldCdzIGV4dHJhY3QgdGhlIGFuaW1hbCBuYW1lIGluc3RlYWQ6CgpgYGB7cn0KCmBgYAoKCgoKIyMgRXh0cmFjdGluZyBHcm91cHMKCldoYXQgaWYgd2Ugd2FudCB0byB1c2UgY2FwdHVyaW5nIGdyb3VwcyBpbnN0ZWFkIG9mIGV4dHJhY3RpbmcgYWxsIG9mIHRoZSB0ZXh0IHRoYXQgbWF0Y2hlcyB0aGUgZXhwcmVzc2lvbj8gIAoKYGBge3J9CnN0cl9tYXRjaChhc3Ryb25hdXRzLCAiICAgICguKykgKGY/ZT9tYWxlKSAoLis/KSgsLit8JHxcXCguKykiKQpgYGAKClRoaXMgZ2l2ZXMgdXMgYSBjaGFyYWN0ZXIgbWF0cml4IHdpdGggNSBjb2x1bW5zOgoKKiBUaGUgZW50aXJlIHRleHQgbWF0Y2hlZCBieSB0aGUgcmVndWxhciBleHByZXNzaW9uCiogVGV4dCBjYXB0dXJlZCBieSBncm91cCAxCiogVGV4dCBjYXB0dXJlZCBieSBncm91cCAyCiogVGV4dCBjYXB0dXJlZCBieSBncm91cCAzCiogVGV4dCBjYXB0dXJlZCBieSBncm91cCA0ICh3aGljaCB3ZSBkb24ndCBuZWVkLCBqdXN0IHVzZWQgZm9yIGB8YCkgLS0gd2UgY291bGQgbWFrZSB0aGlzIGdyb3VwIG5vbi1jYXB0dXJpbmcgaW4gb3VyIGV4cHJlc3Npb24gaW5zdGVhZCB3aXRoIGA/OmAgLSBgKD86LC4rfCR8XFwoLispYCwgb3IganVzdCBpZ25vcmUgdGhpcyBvdXRwdXQKCldlIGNhbiBjb252ZXJ0IHRoaXMgdG8gYSBkYXRhIGZyYW1lIGxpa2Ugd2UgZGlkIGFib3ZlLiAgSWYgd2UncmUgd29ya2luZyB3aXRoIGEgZGF0YWZyYW1lOgoKYGBge3J9CmFzdHJvX2RmICU+JQogIG11dGF0ZShhc3RybyA9IHN0cl9tYXRjaChpbmZvLCAiICAgICguKykgKGY/ZT9tYWxlKSAoLis/KSgsLit8JHxcXCguKykiKSkgCmBgYAoKVGhlIG91dHB1dCBsb29rcyBsaWtlIG1hZ2ljYWxseSBnb3Qgc2VwYXJhdGUgY29sdW1ucy4gIEJ1dCB3ZSBkaWRuJ3Q6CgpgYGB7cn0KYXN0cm9fZGYgJT4lCiAgbXV0YXRlKGFzdHJvID0gc3RyX21hdGNoKGluZm8sICIgICAgKC4rKSAoZj9lP21hbGUpICguKz8pKCwuK3wkfFxcKC4rKSIpKSAlPiUKICBkaW0oKQpgYGAKCkluc3RlYWQgb2YgdXNpbmcgYHN0cl9tYXRjaGAgYW5kIHVubmVzdGluZywgd2hlbiB3ZSBoYXZlIGNhcHR1cmluZyBncm91cHMsIHdlIGNhbiB1c2UgYHRpZHlyOjpleHRyYWN0YCBpbnN0ZWFkLiAgQW5kIGl0IGxldHMgdXMgbmFtZSB0aGUgcmVzdWx0aW5nIGNvbHVtbnMuICBVc2UgYE5BYCB0byBvbWl0IGEgZ3JvdXAuICAKCmBgYHtyfQphc3Ryb19kZiAlPiUKICBleHRyYWN0KGluZm8sIAogICAgICAgICAgaW50bz1jKCJjb3VudHJ5IiwgImdlbmRlciIsICJuYW1lIiwgTkEpLCAKICAgICAgICAgIHJlZ2V4PSIgICAgKC4rKSAoZj9lP21hbGUpICguKz8pKCwuK3wkfFxcKC4rKSIpCmBgYAoKSWYgd2Ugd2FudCB0byBrZWVwIHRoZSBvcmlnaW5hbCBkYXRhIGFzIHdlbGw6CgpgYGB7cn0KYXN0cm9fZGYgJT4lCiAgZXh0cmFjdChpbmZvLCAKICAgICAgICAgIGludG89YygiY291bnRyeSIsICJnZW5kZXIiLCAibmFtZSIsIE5BKSwgCiAgICAgICAgICByZWdleD0iICAgICguKykgKGY/ZT9tYWxlKSAoLis/KSgsLit8JHxcXCguKykiLAogICAgICAgICAgcmVtb3ZlID0gRkFMU0UpCmBgYAoKCiMjIyBFWEVSQ0lTRQoKRXh0cmFjdCB0aGUgc2Vjb25kIGFuZCB0aGlyZCB3b3JkcyBmcm9tIGVhY2ggc2VudGVuY2UgaW4gYHNlbnQyMGAKCk5vdGU6IFRoZSB0aGlyZCBzZW50ZW5jZSBtYWtlcyB0aGlzIGEgbGl0dGxlIHRyaWNreS4gIFlvdSBjYW4gaWdub3JlIHRoaXMgc2VudGVuY2UgaWYgeW91IHdhbnQuICBUYWtlIGl0IGFzIGEgY2hhbGxlbmdlIHRvIHdyaXRlIGEgcmVnZXggdGhhdCBhbHNvIHdvcmtzIG9uIHNlbnRlbmNlIDMuCgpgYGB7cn0KCmBgYAoKCgojIFJhdyBTdHJpbmdzCgpEb3VibGluZyBgXGAgY2FuIGdldCBhbm5veWluZyBhbmQgY29uZnVzaW5nLiAgVGhlcmUgaXMgYW4gYWx0ZXJuYXRpdmUgYWRkZWQgcmVjZW50bHkgdG8gUi4gIFdlIGNhbiBkZWZpbmUgYSAicmF3IiBzdHJpbmcgdGhhdCBkb2Vzbid0IHJlcXVpcmUgdXMgdG8gZXNjYXBlIGBcYC4gIEluc3RlYWQgb2YgYCIuLi4iYCwgd2UgdXNlIGByIiguLi4pImAgVGhlbiB3ZSBjYW4gdXNlIHNpbmdsZSBgXGA6CgpgYGB7cn0Kc3RyX3JlcGxhY2UoYXN0cm9uYXV0cywgciIoICAgICguKykgKGY/ZT9tYWxlKSAoLis/KSgsLit8JHxcKC4rKSkiLCByIihcMiwgXDEsIFwzKSIpCmBgYAoKTm90ZSB0aGF0IHdlIG5lZWRlZCB0byBjaGFuZ2UgMyB0aGluZ3M6CgoqIGByYCBpbiBmcm9udCBvZiB0aGUgZXhwcmVzc2lvbiByaWdodCBiZWZvcmUgdGhlIGAiYAoqIGAoYCBhbmQgYClgIGluc2lkZSB0aGUgb3BlbmluZyBhbmQgY2xvc2luZyBgImAgLSB0aGVzZSBwYXJlbnRoZXNlcyBhcmUgTk9UIHBhcnQgb2YgdGhlIHJlZ3VsYXIgZXhwcmVzc2lvbgoqIENoYW5nZSBkb3VibGUgYFxcYCB0byBgXGAKCgojIFJlY2FwCgpZb3Ugbm93IGtub3cgaG93IHRvIHVzZSByZWd1bGFyIGV4cHJlc3Npb25zIGluIFIuICBBcyB3ZSBzYXcgaW4gYSBmZXcgc3BvdHMsIHNvbWV0aW1lcyB5b3UgbmVlZCB0b29scyBvdGhlciB0aGFuIGp1c3QgcmVndWxhciBleHByZXNzaW9ucyB0byBkbyB3aGF0IHlvdSB3YW50LiAgRG9uJ3QgYmUgYWZyYWlkIHRvIGJyZWFrIHRoaW5ncyBkb3duIGludG8gbXVsdGlwbGUgc3RlcHMgYW5kIHVzZSBhbGwgb2YgdGhlIGRpZmZlcmVudCB0b29scyBhdmFpbGFibGUgaW4gUiB0byBnZXQgeW91ciBkYXRhIGluIHRoZSBzaGFwZSB5b3Ugd2FudC4KCgo=