11.1 Introduction

Working with data provided by R packages is a great way to learn the tools of data science, but at some point you want to stop learning and start working with your own data. In this chapter, you’ll learn how to read plain-text rectangular files into R. Here, we’ll only scratch the surface of data import, but many of the principles will translate to other forms of data. We’ll finish with a few pointers to packages that are useful for other types of data.

11.1.1 Prerequisites

In this chapter, you’ll learn how to load flat files in R with the readr package, which is part of the core tidyverse.

library(tidyverse)
package ‘tidyverse’ was built under R version 3.6.2Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ──────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.2     ✓ dplyr   1.0.7
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.1
package ‘ggplot2’ was built under R version 3.6.2package ‘tibble’ was built under R version 3.6.2package ‘tidyr’ was built under R version 3.6.2package ‘readr’ was built under R version 3.6.2package ‘purrr’ was built under R version 3.6.2package ‘dplyr’ was built under R version 3.6.2package ‘forcats’ was built under R version 3.6.2── Conflicts ─────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

11.2 Getting started

Most of readr’s functions are concerned with turning flat files into data frames:

These functions all have similar syntax: once you’ve mastered one, you can use the others with ease. For the rest of this chapter we’ll focus on read_csv(). Not only are csv files one of the most common forms of data storage, but one you understand read_csv(), you can easily apply your knowedge to all the other functions in readr.

The first argument to read_csv() is the most important: it’s the path to the file to read.

heights <- read_csv("data/heights.csv")
Error: 'data/heights.csv' does not exist in current working directory ('/Users/macgyverjuarez/Desktop/R Notes and Exercises/R for Data Science/Ch 11 - Data Import').

When you run read_csv() it prints out a column specification that gives the name and type of each column. That’s an important part of readr, which we’ll come back to in parsing a file.

You can also supply an inline csv file. This is useful for experimenting with readr and for creating reproducible examples to share with others:

read_csv("a,b,c
         1,2,3
         4,5,6")

In both cases read_csv() uses the first line of the data for the column names, which is a very common convention. There are two cases where you might want to tweak this behavior:

  1. Sometimes there are a few lines of metadata at the top of the file. You can use skip = n to skip the first n lines; or use comment = “#” to drop all lines that start with (e.g.) #.
read_csv("The first line of metadata
         The second line of metadata
         x,y,z
         1,2,3", skip = 2)
read_csv(#a comment I want to skip
  "x,y,z
  1,2,3", comment = "#")
  1. The data might not have column names. You can use col_names = FALSE to tell read_csv() not to treat the first row as headings, and instead label them sequentially from X1 to Xn:
read_csv("1,2,3\n4,5,6", col_names = FALSE)

(“” is a convenient shortcut for adding a new line. You’ll learn more about it and other types of string escape in string basics.)

Alternatively you can pass col_names a character vector which will be used as the column names:

read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z"))

This is all you need to know to read ~75% of CSV files that you’ll encounter in practice. You can also easily adapt what you’ve learned to read tab separated files with read_tsv() and fixed width files with read_fwf(). To read in more challenging files, you’ll need to learn more about how readr parses each column, turning them into R vectors.

11.2.1 Compared to base R

If you’ve used R before, you might wonder why we’re not using read.csv(). There are a few good reasons to favor readr functions over the base equivalents:

11.2.2 Exercises

  1. What function would you use to read a file where fields were separated with “|”?

read_delim() where delim = “|”

  1. Apart from file, skip, and comment, what other arguments do read_csv() and read_tsv() have in common?

col_names, col_types, locale, na, quoted_na, quote, trim_ws, guess_max, progress, skip_empty_rows.

They have all of the same arguments.

  1. What are the most important arguments to read_fwf()?

The dimensions - so probably col_positions, and n_max.

  1. Sometimes strings in a CSV file contain commas. To prevent them from causing problems they need to be surrounded by a quoting character, like " or . By default, read_csv() assumes that the quoting character will be ". What argument to read_csv() do you need to specify to read the following text into a data frame?
read_csv("x,y\n1,'a,b'", quote = "'")
  1. Identify what is wrong with each of the following inline CSV files. What happens when you run the code?
read_csv("a,b\n1,2,3\n4,5,6")
2 parsing failures.
row col  expected    actual         file
  1  -- 2 columns 3 columns literal data
  2  -- 2 columns 3 columns literal data
read_csv("a,b,c\n1,2\n1,2,3,4")
2 parsing failures.
row col  expected    actual         file
  1  -- 3 columns 2 columns literal data
  2  -- 3 columns 4 columns literal data
read_csv("a,b\n\1")
1 parsing failure.
row col  expected    actual         file
  1  -- 2 columns 1 columns literal data
read_csv("a,b\n1,2\na,b")
read_csv("a;b\n1;3")

11.3 Parsing a vector

Before we get into the details of how readr reads files from disk, we need to take a little detour to talk about the __parse_*()__ functions. These functions take a character vector and return a more specialised vector like a logical, integer, or date:

str(parse_logical(c("TRUE", "FALSE", "NA")))
 logi [1:3] TRUE FALSE NA
str(parse_integer(c("1","2","3")))
 int [1:3] 1 2 3
str(parse_date(c("2010-01-01", "1979-10-14")))
 Date[1:2], format: "2010-01-01" "1979-10-14"

These functions are useful in their own right, but are also an important building block for readr. Once you’ve learned how the individual parsers work in this section, we’ll circle back and see how they fit together to parse a complete file in the next section.

Like all functions in the tidyverse, the __parse*()__ functions are uniform: the first argument is a character vector to parse, and the na argument specifies which strings should be treated as missing:

parse_integer(c("1", "231", ".", "456"), na = ".")
[1]   1 231  NA 456

If parsing fails, you’ll get a warning:

x <- parse_integer(c("123", "345", "abc", "123.45"))
2 parsing failures.
row col               expected actual
  3  -- an integer             abc   
  4  -- no trailing characters 123.45

And the failures will be missing in the output:

x
[1] 123 345  NA  NA
attr(,"problems")

If there are many parsing failures, you’ll need to use problems() to get the complete set. This returns a tibble, which you can then manipulate with dplyr.

problems(x)

Using parsers is mostly a matter of understand what’s available and how they deal with different types of input. There are eight particularly important parsers:

  1. parse_logical() and parse_integer() parse logicals and integers respectively. There’s basically nothing that can go wrong with these parsers so I won’t describe them here further.

  2. parse_double() is a strict numeric parser, and parse_number() is a flexible numeric parser. These are more complicated than you might expect because different parts of the world write numbers in different ways.

  3. parse_character() seems so simple that it shouldn’t be necessary. But one complication makes it quite important: character encodings.

  4. parse_factor() create factors, the data structure that R uses to represent categorical variables with fixed and known values.

  5. parse_datetime(), parse_date(), and parse_time() allow you to parse various date & time specifications. These are the most complicated because there are so many different ways of writing dates.

The following sections describe these parsers in more detail.

11.3.1 Numbers

It seems like it should be straightforward to parse a number, but three problems make it tricky:

  1. People write numbers differently in different parts of the world.

  2. Numbers are often surrounded by other characters that provide some context, like “$1000” or “10%”.

  3. Numbers often contain “grouping” characters to make them easier to read, like “1,000,000”, and these grouping characters vary around the world.

To address the first problem, readr has the notion of a “locale”, an object that specifies parsing options that differ from place to place. When parsing numbers, the most important option is the character you use for the decimal mark. You can override the default value of . by creating a new locale and setting the decimal_mark argument:

parse_double("1.23")
[1] 1.23
parse_double("1,23", locale = locale(decimal_mark = ","))
[1] 1.23

readr’s default locale is US-centric, because generally R is US-centric (i.e. the documentation of base R is written in American English). An alternative approuch would be to try and guess the defaults from your operating system.

parse_number() addresses the second problem: it ignores non-numeric characters before and after the number. This is particularly useful for curriencies and percentages, but also works to extract numbers embedded in text.

parse_number("$100")
[1] 100
parse_number("20%")
[1] 20
parse_number("It cost $123.45")
[1] 123.45

The final problem is addressed by the combination of parse_number() and the locale as parse_number() will ignore the “grouping mark”:

# Used in America
parse_number("$123,456,789")
[1] 123456789
# Used in many parts of Europe
parse_number("123.456.789", locale = locale(grouping_mark = "."))
[1] 123456789
# Used in Switzerland
parse_number("123'456'789", locale = locale(grouping_mark = "'"))
[1] 123456789

11.3.2 Strings

It seems like parse_character() should be really simple - it could just return its input. Unfortunately it is not so simple. To see how computers represent strings, we can get the underlying representation using charToRaw():

charToRaw("Hadley")
[1] 48 61 64 6c 65 79

Each hexadecimal number rpresents a byte of information: 48 is H, 61 is a, and so on. The mapping from hexadecimal number to character is called the encoding, and in this case the encoding is called ASCII. ASCII does a great job of representing English characters, because it’s the American Standard Code for Information Interchange.

However there used to not be a standard for foreign languages. Today UTF-8 is the standard that is supported almost everywhere. UTF-8 can encode just about every character used by humans today, as well as many extra symboles.

readr uses UTF-8 everywhere: it assumes your data is UTF-8 encoded when you read it, and always uses it when writing. This is a good ddefault, but will fail for data produced by older systems that don’t understand UTF-8. If this happens to you, your strings will look weird when you print them. Sometimes just one or two characters might be messed up; other times you’ll get complete gibberish. For example:

x1 <- "El Ni\xf1o was particularly bad this year"
x2 <- "\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd"

x1
[1] "El Ni\xf1o was particularly bad this year"
x2
[1] "\x82\xb1\x82\xf1\x82\u0242\xbf\x82\xcd"
parse_character(x1, locale = locale(encoding = "Latin1"))
[1] "El Niño was particularly bad this year"
parse_character(x2, locale = locale(encoding = "Shift-JIS"))
[1] "こんにちは"

How do you find the correct encoding? If you’re lucky, it’ll be included somewhere in the data documentation. Unfortunately, that’s rarely the case, so readr provides guess_encoding() to help you figure it out. It’s not foolproof, and it works better when you have lots of text (unlike here), but it’s a reasonable place to start. Expect to try a few different encodings before you find the right one.

guess_encoding(charToRaw(x1))
guess_encoding(charToRaw(x2))

The first argument to guess_encoding() can either be a path to a file or, as in this case, a raw vector(useful if the strings are already in R.)

11.3.3 Factors

R uses factors to represent categorical variables that have a known set of possible values. Give parse_factor() a vector of known levels to generate a warning whenever an unexpected value is present:

fruit <- c("apple", "banana")
parse_factor(c("apple", "banana", "bananana"), levels = fruit)
1 parsing failure.
row col           expected   actual
  3  -- value in level set bananana
[1] apple  banana <NA>  
attr(,"problems")
Levels: apple banana

But if you have many problematic entries, it’s often easier to leave as character vectors and then use the tools you’ll learn about in strings and factors to clean them up.

11.3.4 Dates, date-times, and times

You pick between three parsers depending on whether you want a date (the number of days since 1970-01-01), a date-time (the number of seconds since midnight 1970-01-01), or a time(the number of seconds since midnight). When called without any additional arguments:

parse_datetime("2010-10-01T2010")
[1] "2010-10-01 20:10:00 UTC"
# If time is omitted, it will be set to midnight.
parse_datetime("20101010")
[1] "2010-10-10 UTC"

This is the most important date/time standard, and if you work with dates and times frequently, I recommend reading https://en.wikipedia.org/wiki/ISO_8601

parse_date("2010-10-01")
[1] "2010-10-01"
parse_time("01:10 am")
01:10:00
parse_time("20:10:01")
20:10:01

Base R doesn’t have a great built in class for time data, so we use the one provided in the hms package.

If these defaults don’t work for your data you can supply your own date-time format, built up of the following pieces:

Year

%Y (4 digits). %y (2 digits); 00-69 -> 2000-2069, 70-99 -> 1970-1999.

Month

Day

Time

Non-digits

The best way to figure out the correct format is to create a few examples in a character vector, and test with one of the parsing functions. For example:

parse_date("01/02/11", format = "%m/%d/%y")
[1] "2011-01-02"
parse_date("01/02/11", format = "%d/%m/%y")
[1] "2011-02-01"
parse_date("01/02/11", format = "%y/%m/%d")
[1] "2001-02-11"

If you’re using %b or %B with non-English month names, you’ll need to set the lang argument to locale(). See the list of built-in languages in date_names_langs(), or if your language is not already included, create your own with date_names().

parse_date("1 janvier 2015", "%d %B %Y", locale = locale("fr"))
[1] "2015-01-01"

11.3.5 Exercises

  1. What are the most important arguments to locale()?
  1. What happens if you try and set decimal_mark and grouping_mark to the same character? What happens to the default value of grouping_mark when you set decimal_mark to “,”? What happens to the default value of decimal_mark when you set the grouping_mark to “.”?
locale(decimal_mark = ".", grouping_mark = ".")
Error: `decimal_mark` and `grouping_mark` must be different

You receive an error message indicating the two must be different.

locale(decimal_mark = ",")
<locale>
Numbers:  123.456,78
Formats:  %AD / %AT
Timezone: UTC
Encoding: UTF-8
<date_names>
Days:   Sunday (Sun), Monday (Mon), Tuesday (Tue), Wednesday (Wed), Thursday (Thu), Friday (Fri), Saturday (Sat)
Months: January (Jan), February (Feb), March (Mar), April (Apr), May (May), June (Jun), July (Jul), August (Aug), September (Sep),
        October (Oct), November (Nov), December (Dec)
AM/PM:  AM/PM

If the decimal mark is set to “,”, then the grouping mark assumes the value of “.”.

locale(grouping_mark = ".")
<locale>
Numbers:  123.456,78
Formats:  %AD / %AT
Timezone: UTC
Encoding: UTF-8
<date_names>
Days:   Sunday (Sun), Monday (Mon), Tuesday (Tue), Wednesday (Wed), Thursday (Thu), Friday (Fri), Saturday (Sat)
Months: January (Jan), February (Feb), March (Mar), April (Apr), May (May), June (Jun), July (Jul), August (Aug), September (Sep),
        October (Oct), November (Nov), December (Dec)
AM/PM:  AM/PM

If the grouping mark is set to “.”, then the decimal mark assumes the value of “,”.

  1. I didn’t discuss the date_format and time_format options to locale(). What do they do? Construct an example that shows when they might be useful.
parse_datetime("12-13-03T035006", locale = locale(date_format = "%m/%d/%y", time_format = "%H/%M/%S"))
1 parsing failure.
row col   expected          actual
  1  -- date like  12-13-03T035006
[1] NA

*skip

  1. If you live outside the US, create a new locale object that encapsulates the settings for the types of file you read most commonly.

Basically, create a new function

foreign_locale <- locale(date_format = "%d/%m/%Y")
parse_date("02/01/2001", locale = foreign_locale)
[1] "2001-01-02"
  1. What’s the difference between read_csv() and read_csv2()?

read_csv() assumes “,” as the delimiter; read_csv2() assumes “;” as the delimiter.

  1. What are the most common encodings used in Europe? What are the most common encodings used in Asia? Do some googling to find out.

*skip

  1. Generate the correct format string to parse each of the following dates and times:
d1 <- "January 1, 2010"
d2 <- "2015-Mar-07"
d3 <- "06-Jun-2017"
d4 <- c("August 19 (2015)", "July 1 (2015)")
d5 <- "12/30/14" # Dec 30, 2014
t1 <- "1705"
t2 <- "11:15:10.12 PM"
parse_date(d1, locale = locale(date_format = "%B %d, %Y"))
[1] "2010-01-01"
parse_date(d2, locale = locale(date_format = "%Y-%b-%d"))
[1] "2015-03-07"
parse_date(d3, locale = locale(date_format = "%d-%b-%Y"))
[1] "2017-06-06"
parse_date(d4, locale = locale(date_format = "%B %d (%Y)"))
[1] "2015-08-19" "2015-07-01"
parse_date(d5, locale = locale(date_format = "%m/%d/%y"))
[1] "2014-12-30"
parse_time(t1, locale = locale(time_format = "%H%M"))
17:05:00
parse_time(t2, locale = locale(time_format = "%H:%M:%OS %p"))
23:15:10.12

11.4 Parsing a file

Learning objectives - understand:

  1. How readr automatically guesses the type of each column.
  2. How to override the default specification.
11.4.1 Strategy

readr uses a heuristic to figure out the type of each column: it reads the first 1000 rows and uses some (moderately conservative) heuristics to figure out the type of each column.

guess_parser("2010-10-01")
[1] "date"
guess_parser("15:01")
[1] "time"
guess_parser(c("TRUE", "FALSE"))
[1] "logical"
guess_parser(c("1", "5", "9"))
[1] "double"
guess_parser(c("12,352,561"))
[1] "number"
str(parse_guess("2010-10-10"))
 Date[1:1], format: "2010-10-10"

11.4.2 Problems

These defaults don’t always work for larger files. There are two basic problems:

  1. The first thousand rows might be a special case, and readr guesses a type that is not sufficiently general. For example, you might have a column of doubles that only contains integers in the first 1000 rows.

  2. The column might contain a lot of missing values. If the first 1000 rows contain only __NA__s, readr will gues that it’s a logical vector, whereas you probably want to parse it as something more specific.

readr contains a challenging CSV that illustrates both of these problems:

challenge <- read_csv(readr_example("challenge.csv"))

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  x = col_double(),
  y = col_logical()
)

1000 parsing failures.
 row col           expected     actual                                                                                         file
1001   y 1/0/T/F/TRUE/FALSE 2015-01-16 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
1002   y 1/0/T/F/TRUE/FALSE 2018-05-18 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
1003   y 1/0/T/F/TRUE/FALSE 2015-09-05 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
1004   y 1/0/T/F/TRUE/FALSE 2012-11-28 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
1005   y 1/0/T/F/TRUE/FALSE 2020-01-13 '/Library/Frameworks/R.framework/Versions/3.6/Resources/library/readr/extdata/challenge.csv'
.... ... .................. .......... ............................................................................................
See problems(...) for more details.

There are two printed outputs: the column specification generated by looking at the first 1000 rows, and the first five parsing failures. It’s always a good idea to explicitly pull out the problems(), so you can explore them in more depth:

problems(challenge)

A good strategy is to work column by column until there are no problems remaining. Here we can see that there are a lot of parsing problems with the y column. If we look at the last few rows, you’ll see that they’re dates stores in a character vector:

tail(read_csv(readr_example("challenge.csv"), guess_max = min(2000, 2000)))

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  x = col_double(),
  y = col_date(format = "")
)

That suggests we need to use a date parser instead. To fix the call, start by copying and pasting the column specification into your original call:

challenge <- read_csv(readr_example("challenge.csv"),
                      col_types = cols(x = "d",
                                       y = "D")
                      )
tail(challenge)

The strategy suggested to fix one parsing issue at a time, but I did both at the same time. Moving forward, will do one by one.

Every parse_xyz() function has a corresponding col_xyz() function. You use parse_xyz() when the data is in a character vector in R already; you use col_xyz() when you want to tell readr how to load the data.

I highly recommend always suppling col_types, building up from the print-out provided by readr. This ensures that you have a consistent and reproducible data import script. If you rely on the default guesses and your data changes, readr will continue to read it in. If you want to be really strict, use stop_for_problems(): that will throw an error and stop your script if there are any parsing problems.

11.4.3 Other strategies

There are a few other general strategies to help you parse files:

challenge2 <- read_csv(readr_example("challenge.csv"), guess_max = min(1001, 2000))

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  x = col_double(),
  y = col_date(format = "")
)
tail(challenge2)
challenge2 <- read_csv(readr_example("challenge.csv"),
                       col_types = cols(.default = col_character()))
head(challenge2)
tail(challenge2)

We use head() and tail() to view the first and last six, respectively, rows of data - showing how the data starts as integer and logical but eventually transitions into double and date.

This is particularly useful in conjunction with type_convert(), which applies the parsing heuristics to the character columns in a data frame.

df <- tribble(
  ~x, ~y,
  "1", "1.21",
  "2", "2.32",
  "3", "4.56"
)
df

# Note the column types - <character>

type_convert(df)

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  x = col_double(),
  y = col_double()
)

11.5 Writing to a file

readr also comes with two useful functions for writing data back to disk: write_csv() and write_tsv(). Both functions increase the chances of the output file being read back in correctly by:

If you want to export a csv file to Excel, use write_excel_csv() - this writes a special character (a “byte order mark”) at the start of the file which tells Excel that you’re using the UTF-8 encoding.

The most important arguments are x (the data frame to save), and path (the location to save it). You can also specify how missing values are written with na, and if you want to append to an existing file.

write_csv(challenge, "/Users/macgyverjuarez/Desktop/R Notes and Exercises/R for Data Science/Ch 11 - Data Import/challenge.csv")

Note that the type information is lost when you save to csv:

challenge
write_csv(challenge, "challenge-2.csv")
read_csv("challenge-2.csv")

── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  x = col_double(),
  y = col_logical()
)

1000 parsing failures.
 row col           expected     actual              file
1001   y 1/0/T/F/TRUE/FALSE 2015-01-16 'challenge-2.csv'
1002   y 1/0/T/F/TRUE/FALSE 2018-05-18 'challenge-2.csv'
1003   y 1/0/T/F/TRUE/FALSE 2015-09-05 'challenge-2.csv'
1004   y 1/0/T/F/TRUE/FALSE 2012-11-28 'challenge-2.csv'
1005   y 1/0/T/F/TRUE/FALSE 2020-01-13 'challenge-2.csv'
.... ... .................. .......... .................
See problems(...) for more details.

Hence the importance of specifying column types and being strict about importing data. If the script has been saved then data can easily and consistently be imported in.

Regardless, this makes CSVs a little unreliable for catching interim results-you need to recreate the column specification every time you load in. There are two alternatives:

  1. write_rds() and read_rds() are uniform wrappers around the base functions readRDS() and saveRDS(). These store data in R’s custom binary format called RDS:
write_rds(challenge, "challenge.rds")
read_rds("challenge.rds")
  1. The feather package implements a fast binary file format that can be shared across programming languages:
install.packages("feather")
trying URL 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/feather_0.3.5.tgz'
Content type 'application/x-gzip' length 778331 bytes (760 KB)
==================================================
downloaded 760 KB

The downloaded binary packages are in
    /var/folders/z2/lhwh0pjx00d23vxtcb3v87m40000gn/T//RtmpbDUbTu/downloaded_packages
library(feather)
write_feather(challenge, "challenge.feather")
read_feather("challenge.feather")

Feather tends to be faster than RDS and is usable outside of R. RDS supports list-columns (which you’ll learn about in many models); feather currently does not.

11.6 Other types of data

To get other types of data into R, we recommend starting with the tidyverse packages listed below. They’re certainly not perfect, but they are a good place to start. For rectangular data:

For hierarchical data: use jsonlite (by Jeroen Oooms) for json, and xml2 for XML. Jenny Bryan has some excellent worked examples at https://jennybc.github.io/purrr-tutorial/,

For other file types, try the R data import/export manual and the rio package.

LS0tCnRpdGxlOiAiQ2ggMTEgLSBEYXRhIGltcG9ydCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIyAxMS4xIEludHJvZHVjdGlvbgoKV29ya2luZyB3aXRoIGRhdGEgcHJvdmlkZWQgYnkgUiBwYWNrYWdlcyBpcyBhIGdyZWF0IHdheSB0byBsZWFybiB0aGUgdG9vbHMgb2YgZGF0YSBzY2llbmNlLCBidXQgYXQgc29tZSBwb2ludCB5b3Ugd2FudCB0byBzdG9wIGxlYXJuaW5nIGFuZCBzdGFydCB3b3JraW5nIHdpdGggeW91ciBvd24gZGF0YS4gSW4gdGhpcyBjaGFwdGVyLCB5b3UnbGwgbGVhcm4gaG93IHRvIHJlYWQgcGxhaW4tdGV4dCByZWN0YW5ndWxhciBmaWxlcyBpbnRvIFIuIEhlcmUsIHdlJ2xsIG9ubHkgc2NyYXRjaCB0aGUgc3VyZmFjZSBvZiBkYXRhIGltcG9ydCwgYnV0IG1hbnkgb2YgdGhlIHByaW5jaXBsZXMgd2lsbCB0cmFuc2xhdGUgdG8gb3RoZXIgZm9ybXMgb2YgZGF0YS4gV2UnbGwgZmluaXNoIHdpdGggYSBmZXcgcG9pbnRlcnMgdG8gcGFja2FnZXMgdGhhdCBhcmUgdXNlZnVsIGZvciBvdGhlciB0eXBlcyBvZiBkYXRhLgoKIyMjIyAxMS4xLjEgUHJlcmVxdWlzaXRlcwoKSW4gdGhpcyBjaGFwdGVyLCB5b3UnbGwgbGVhcm4gaG93IHRvIGxvYWQgZmxhdCBmaWxlcyBpbiBSIHdpdGggdGhlIF9fcmVhZHJfXyBwYWNrYWdlLCB3aGljaCBpcyBwYXJ0IG9mIHRoZSBjb3JlIHRpZHl2ZXJzZS4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgojIyMjIDExLjIgR2V0dGluZyBzdGFydGVkCgpNb3N0IG9mIHJlYWRyJ3MgZnVuY3Rpb25zIGFyZSBjb25jZXJuZWQgd2l0aCB0dXJuaW5nIGZsYXQgZmlsZXMgaW50byBkYXRhIGZyYW1lczoKCiogX19yZWFkX2NzdigpX18gcmVhZHMgY29tbWEgZGVsaW1pdGVkIGZpbGVzLCBfX3JlYWRfY3N2MigpX18gcmVhZHMgc2VtaWNvbG9uIHNlcGFyYXRlZCBmaWxlcyAoY29tbW9uIGluIGNvdW50cmllcyB3aGVyZSBfXyxfXyBpcyB1c2VkIGFzIHRoZSBkZWNpbWFsIHBsYWNlKSwgX19yZWFkX3RzdigpX18gcmVhZHMgdGFiIGRlbGltaXRlZCBmaWxlcywgYW5kIF9fcmVhZF9kZWxpbSgpX18gcmVhZHMgaW4gZmlsZXMgd2l0aCBhbnkgZGVsaW1pdGVyLgoKKiBfX3JlYWRfZndmKClfXyByZWFkcyBmaXhlZCB3aWR0aCBmaWxlcy4gWW91IGNhbiBzcGVjaWZ5IGZpZWxkcyBlaXRoZXIgYnkgdGhlaXIgd2lkdGhzIHdpdGggX19md2Zfd2lkdGhzKClfXyBvciB0aGVpciBwb3NpdGlvbiB3aXRoIF9fZndmX3Bvc2l0aW9ucygpX18uIF9fcmVhZF90YWJsZSgpX18gcmVhZHMgYSBjb21tb24gdmFyaWF0aW9uIG9mIGZpeGVkIHdpZHRoIGZpbGVzIHdoZXJlIGNvbHVtbnMgYXJlIHNlcGFyYXRlZCBieSB3aGl0ZSBzcGFjZS4KCiogX19yZWFkX2xvZygpX18gcmVhZHMgQXBhY2hlIHN0eWxlIGxvZyBmaWxlcy4gKEJ1dCBhbHNvIGNoZWNrIG91dCBfX3dlYnJlYWRyX18gd2hpY2ggaXMgYnVpbHQgb24gdG9wIG9mIF9fcmVhZF9sb2coKV9fIGFuZCBwcm92aWRlcyBtYW55IG1vcmUgaGVscGZ1bCB0b29scy4pCgpUaGVzZSBmdW5jdGlvbnMgYWxsIGhhdmUgc2ltaWxhciBzeW50YXg6IG9uY2UgeW91J3ZlIG1hc3RlcmVkIG9uZSwgeW91IGNhbiB1c2UgdGhlIG90aGVycyB3aXRoIGVhc2UuIEZvciB0aGUgcmVzdCBvZiB0aGlzIGNoYXB0ZXIgd2UnbGwgZm9jdXMgb24gX19yZWFkX2NzdigpX18uIE5vdCBvbmx5IGFyZSBjc3YgZmlsZXMgb25lIG9mIHRoZSBtb3N0IGNvbW1vbiBmb3JtcyBvZiBkYXRhIHN0b3JhZ2UsIGJ1dCBvbmUgeW91IHVuZGVyc3RhbmQgX19yZWFkX2NzdigpX18sIHlvdSBjYW4gZWFzaWx5IGFwcGx5IHlvdXIga25vd2VkZ2UgdG8gYWxsIHRoZSBvdGhlciBmdW5jdGlvbnMgaW4gcmVhZHIuCgpUaGUgZmlyc3QgYXJndW1lbnQgdG8gX19yZWFkX2NzdigpX18gaXMgdGhlIG1vc3QgaW1wb3J0YW50OiBpdCdzIHRoZSBwYXRoIHRvIHRoZSBmaWxlIHRvIHJlYWQuCgpgYGB7cn0KaGVpZ2h0cyA8LSByZWFkX2NzdigiZGF0YS9oZWlnaHRzLmNzdiIpCmBgYAoKV2hlbiB5b3UgcnVuIF9fcmVhZF9jc3YoKV9fIGl0IHByaW50cyBvdXQgYSBjb2x1bW4gc3BlY2lmaWNhdGlvbiB0aGF0IGdpdmVzIHRoZSBuYW1lIGFuZCB0eXBlIG9mIGVhY2ggY29sdW1uLiBUaGF0J3MgYW4gaW1wb3J0YW50IHBhcnQgb2YgcmVhZHIsIHdoaWNoIHdlJ2xsIGNvbWUgYmFjayB0byBpbiBfX3BhcnNpbmcgYSBmaWxlX18uCgpZb3UgY2FuIGFsc28gc3VwcGx5IGFuIGlubGluZSBjc3YgZmlsZS4gVGhpcyBpcyB1c2VmdWwgZm9yIGV4cGVyaW1lbnRpbmcgd2l0aCBfX3JlYWRyX18gYW5kIGZvciBjcmVhdGluZyByZXByb2R1Y2libGUgZXhhbXBsZXMgdG8gc2hhcmUgd2l0aCBvdGhlcnM6CgpgYGB7cn0KcmVhZF9jc3YoImEsYixjCiAgICAgICAgIDEsMiwzCiAgICAgICAgIDQsNSw2IikKYGBgCgpJbiBib3RoIGNhc2VzIF9fcmVhZF9jc3YoKV9fIHVzZXMgdGhlIGZpcnN0IGxpbmUgb2YgdGhlIGRhdGEgZm9yIHRoZSBjb2x1bW4gbmFtZXMsIHdoaWNoIGlzIGEgdmVyeSBjb21tb24gY29udmVudGlvbi4gVGhlcmUgYXJlIHR3byBjYXNlcyB3aGVyZSB5b3UgbWlnaHQgd2FudCB0byB0d2VhayB0aGlzIGJlaGF2aW9yOgoKMS4gU29tZXRpbWVzIHRoZXJlIGFyZSBhIGZldyBsaW5lcyBvZiBtZXRhZGF0YSBhdCB0aGUgdG9wIG9mIHRoZSBmaWxlLiBZb3UgY2FuIHVzZSBfX3NraXAgPSBuX18gdG8gc2tpcCB0aGUgZmlyc3QgX19uX18gbGluZXM7IG9yIHVzZSBfX2NvbW1lbnQgPSAiIyJfXyB0byBkcm9wIGFsbCBsaW5lcyB0aGF0IHN0YXJ0IHdpdGggKGUuZy4pIF9fI19fLiAKCmBgYHtyfQpyZWFkX2NzdigiVGhlIGZpcnN0IGxpbmUgb2YgbWV0YWRhdGEKICAgICAgICAgVGhlIHNlY29uZCBsaW5lIG9mIG1ldGFkYXRhCiAgICAgICAgIHgseSx6CiAgICAgICAgIDEsMiwzIiwgc2tpcCA9IDIpCmBgYAoKYGBge3J9CnJlYWRfY3N2KCNhIGNvbW1lbnQgSSB3YW50IHRvIHNraXAKICAieCx5LHoKICAxLDIsMyIsIGNvbW1lbnQgPSAiIyIpCmBgYAoKMi4gVGhlIGRhdGEgbWlnaHQgbm90IGhhdmUgY29sdW1uIG5hbWVzLiBZb3UgY2FuIHVzZSBfX2NvbF9uYW1lcyA9IEZBTFNFX18gdG8gdGVsbCBfX3JlYWRfY3N2KClfXyBub3QgdG8gdHJlYXQgdGhlIGZpcnN0IHJvdyBhcyBoZWFkaW5ncywgYW5kIGluc3RlYWQgbGFiZWwgdGhlbSBzZXF1ZW50aWFsbHkgZnJvbSBfX1gxX18gdG8gX19Ybl9fOgoKYGBge3J9CnJlYWRfY3N2KCIxLDIsM1xuNCw1LDYiLCBjb2xfbmFtZXMgPSBGQUxTRSkKYGBgCgooIlxuIiBpcyBhIGNvbnZlbmllbnQgc2hvcnRjdXQgZm9yIGFkZGluZyBhIG5ldyBsaW5lLiBZb3UnbGwgbGVhcm4gbW9yZSBhYm91dCBpdCBhbmQgb3RoZXIgdHlwZXMgb2Ygc3RyaW5nIGVzY2FwZSBpbiBfX3N0cmluZyBiYXNpY3NfXy4pCgpBbHRlcm5hdGl2ZWx5IHlvdSBjYW4gcGFzcyBfX2NvbF9uYW1lc19fIGEgY2hhcmFjdGVyIHZlY3RvciB3aGljaCB3aWxsIGJlIHVzZWQgYXMgdGhlIGNvbHVtbiBuYW1lczoKCmBgYHtyfQpyZWFkX2NzdigiMSwyLDNcbjQsNSw2IiwgY29sX25hbWVzID0gYygieCIsICJ5IiwgInoiKSkKYGBgCgpUaGlzIGlzIGFsbCB5b3UgbmVlZCB0byBrbm93IHRvIHJlYWQgfjc1JSBvZiBDU1YgZmlsZXMgdGhhdCB5b3UnbGwgZW5jb3VudGVyIGluIHByYWN0aWNlLiBZb3UgY2FuIGFsc28gZWFzaWx5IGFkYXB0IHdoYXQgeW91J3ZlIGxlYXJuZWQgdG8gcmVhZCB0YWIgc2VwYXJhdGVkIGZpbGVzIHdpdGggX19yZWFkX3RzdigpX18gYW5kIGZpeGVkIHdpZHRoIGZpbGVzIHdpdGggX19yZWFkX2Z3ZigpX18uIFRvIHJlYWQgaW4gbW9yZSBjaGFsbGVuZ2luZyBmaWxlcywgeW91J2xsIG5lZWQgdG8gbGVhcm4gbW9yZSBhYm91dCBob3cgX19yZWFkcl9fIHBhcnNlcyBlYWNoIGNvbHVtbiwgdHVybmluZyB0aGVtIGludG8gUiB2ZWN0b3JzLgoKIyMjIyAxMS4yLjEgQ29tcGFyZWQgdG8gYmFzZSBSCgpJZiB5b3UndmUgdXNlZCBSIGJlZm9yZSwgeW91IG1pZ2h0IHdvbmRlciB3aHkgd2UncmUgbm90IHVzaW5nIF9fcmVhZC5jc3YoKV9fLiBUaGVyZSBhcmUgYSBmZXcgZ29vZCByZWFzb25zIHRvIGZhdm9yIF9fcmVhZHJfXyBmdW5jdGlvbnMgb3ZlciB0aGUgYmFzZSBlcXVpdmFsZW50czoKCiogVGhleSBhcmUgdHlwaWNhbGx5IG11Y2ggZmFzdGVyICh+MTB4KSB0aGFuIHRoZWlyIGJhc2UgZXF1aXZhbGVudHMuIExvbmcgcnVubmluZyBqb2JzIGhhdmUgYSBwcm9ncmVzcyBiYXIsIHNvIHlvdSBjYW4gc2VlIHdoYXQncyBoYXBwZW5pbmcuIElmIHlvdSdyZSBsb29raW5nIGZvciByYXcgc3BlZWQsIHRyeSBfX2RhdGEudGFibGU6OmZyZWFkKClfXy4gSXQgZG9lc24ndCBmaXQgcXVpdGUgc28gd2VsbCBpbnRvIHRoZSB0aWR5dmVyc2UsIGJ1dCBpdCBjYW4gYmUgcXVpdGUgYSBiaXQgZmFzdGVyLgoKKiBUaGV5IHByb2R1Y2UgdGliYmxlcywgdGhleSBkb24ndCBjb252ZXJ0IGNoYXJhY3RlciB2ZWN0b3JzIHRvIGZhY3RvcnMsIHVzZSByb3cgbmFtZXMsIG9yIG11bmdlIHRoZSBjb2x1bW4gbmFtZXMuIFRoZXNlIGFyZSBjb21tb24gc291cmNlcyBvZiBmcnVzdHJhdGlvbiB3aXRoIHRoZSBiYXNlIFIgZnVuY3Rpb25zLgoKKiBUaGV5IGFyZSBtb3JlIHJlcHJvZHVjaWJsZS4gQmFzZSBSIGZ1bmN0aW9ucyBpbmhlcml0IHNvbWUgYmVoYXZpb3IgZnJvbSB5b3VyIG9wZXJhdGluZyBzeXN0ZW0gYW5kIGVudmlyb25tZW50IHZhcmlhYmxlcywgc28gaW1wb3J0IGNvZGUgdGhhdCB3b3JrcyBvbiB5b3VyIGNvbXB1dGVyIG1pZ2h0IG5vdCB3b3JrIG9uIHNvbWVvbmUgZWxzZSdzLgoKIyMjIyAxMS4yLjIgRXhlcmNpc2VzCgoxLiBXaGF0IGZ1bmN0aW9uIHdvdWxkIHlvdSB1c2UgdG8gcmVhZCBhIGZpbGUgd2hlcmUgZmllbGRzIHdlcmUgc2VwYXJhdGVkIHdpdGggInwiPwoKX19yZWFkX2RlbGltKClfXyB3aGVyZSBfX2RlbGltID0gInwiX18KCjIuIEFwYXJ0IGZyb20gX19maWxlLCBza2lwLF9fIGFuZCBfX2NvbW1lbnRfXywgd2hhdCBvdGhlciBhcmd1bWVudHMgZG8gX19yZWFkX2NzdigpX18gYW5kIF9fcmVhZF90c3YoKV9fIGhhdmUgaW4gY29tbW9uPwoKY29sX25hbWVzLCBjb2xfdHlwZXMsIGxvY2FsZSwgbmEsIHF1b3RlZF9uYSwgcXVvdGUsIHRyaW1fd3MsIGd1ZXNzX21heCwgcHJvZ3Jlc3MsIHNraXBfZW1wdHlfcm93cy4KClRoZXkgaGF2ZSBhbGwgb2YgdGhlIHNhbWUgYXJndW1lbnRzLgoKMy4gV2hhdCBhcmUgdGhlIG1vc3QgaW1wb3J0YW50IGFyZ3VtZW50cyB0byBfX3JlYWRfZndmKClfXz8KClRoZSBkaW1lbnNpb25zIC0gc28gcHJvYmFibHkgY29sX3Bvc2l0aW9ucywgYW5kIG5fbWF4LgoKNC4gU29tZXRpbWVzIHN0cmluZ3MgaW4gYSBDU1YgZmlsZSBjb250YWluIGNvbW1hcy4gVG8gcHJldmVudCB0aGVtIGZyb20gY2F1c2luZyBwcm9ibGVtcyB0aGV5IG5lZWQgdG8gYmUgc3Vycm91bmRlZCBieSBhIHF1b3RpbmcgY2hhcmFjdGVyLCBsaWtlIF9fIl9fIG9yIF9fJ19fLiBCeSBkZWZhdWx0LCBfX3JlYWRfY3N2KClfXyBhc3N1bWVzIHRoYXQgdGhlIHF1b3RpbmcgY2hhcmFjdGVyIHdpbGwgYmUgX18iX18uIFdoYXQgYXJndW1lbnQgdG8gX19yZWFkX2NzdigpX18gZG8geW91IG5lZWQgdG8gc3BlY2lmeSB0byByZWFkIHRoZSBmb2xsb3dpbmcgdGV4dCBpbnRvIGEgZGF0YSBmcmFtZT8KCmBgYHtyfQpyZWFkX2NzdigieCx5XG4xLCdhLGInIiwgcXVvdGUgPSAiJyIpCmBgYAoKNS4gSWRlbnRpZnkgd2hhdCBpcyB3cm9uZyB3aXRoIGVhY2ggb2YgdGhlIGZvbGxvd2luZyBpbmxpbmUgQ1NWIGZpbGVzLiBXaGF0IGhhcHBlbnMgd2hlbiB5b3UgcnVuIHRoZSBjb2RlPwoKYGBge3J9CnJlYWRfY3N2KCJhLGJcbjEsMiwzXG40LDUsNiIpCnJlYWRfY3N2KCJhLGIsY1xuMSwyXG4xLDIsMyw0IikKcmVhZF9jc3YoImEsYlxuXDEiKQpyZWFkX2NzdigiYSxiXG4xLDJcbmEsYiIpCnJlYWRfY3N2KCJhO2JcbjE7MyIpCmBgYAoKKiBGb3IgdGhlIGZpcnN0IGxpbmUgdGhlIGNvbHVtbnMgYXJlIG5vdCBkZWZpbmVkIHNvIGRhdGEgaXMgbG9zdC4gSXQgZXhwZWN0cyBjb2x1bW5zIF9fYSBhbmQgYl9fIHdoZW4gaW4gYWN0dWFsaXR5IHRoZXJlIGFyZSBwZXJoYXBzIHRocmVlIGNvbHVtbnM/IAoKKiBGb3IgdGhlIHNlY29uZCBsaW5lIHRoZXJlIGFyZSB0aHJlZSBleHBlY3RlZCBjb2x1bW5zOiBfX2EsIGIsIGFuZCBjX18gYnV0IHRoZW4gb25seSB0d28gb2JzZXJ2YXRpb25zIGluIHRoZSBzZWNvbmQgcm93IGFuZCBmb3VyIG9ic2VydmF0aW9ucyBpbiB0aGUgdGhpcmQgcm93LgoKKiBGb3IgdGhlIHRoaXJkIGxpbmUsIHRoZXJlIGFyZSB0d28gZXhwZWN0ZWQgY29sdW1uczogX19hIGFuZCBiX18gZm9sbG93ZWQgYnkgYSBsaW5lIGJyZWFrIGFuZCBvbmUgb2JzZXJ2YXRpb24gd3JhcHBlZCBpbiBxdW90ZXMgaW5kaWNhdGluZyBpdCBzaG91bGQgYmUgdHJlYXRlZCBhcyBhIHN0cmluZy4gSSBhbSBhbHNvIHVuc3VyZSB3aHkgdGhlcmUgaXMgYW5vdGhlciBiYWNrc2xhc2g/IEhvd2V2ZXIsIGluIHJlYWRyIGl0IHNob3VsZCBiZSB3cmFwcGVkIGluIHNpbmdsZSBxdW90ZXMuIEl0IGlzIG9kZCB0aGF0IGl0IGlzIHRyZWF0ZWQgYXMgYSBzdHJpbmcgdGhvdWdoLgoKKiBGb3IgdGhlIGZvdXJ0aCBsaW5lLCB0aGVyZSBhcmUgdHdvIGV4cGVjdGVkIGNvbHVtbnMsIHR3byBvYnNlcnZhdGlvbnMgaW4gdGhlIHNlY29uZCBsaW5lLCBhbmQgdHdvIG9ic2VydmF0aW9ucyBpbiB0aGUgdGhpcmQgbGluZS4gSXQgaXMgb2RkIHRob3VnaCB0aGF0IHRoZSB2YXJpYWJsZXMgY29udGFpbiBhIG1peCBvZiBjaGFyYWN0ZXIgYW5kIGludGVnZXIgZGF0YSB0aG91Z2guIEl0IHNlZW1zIGFzIHRob3VnaCB0aGUgbGFzdCByb3cgX19hLCBiX18gc2hvdWxkIGVhY2ggYmUgc3Vycm91bmRlZCBieSBzaW5nbGUgcXVvdGVzIHRvIGluZGljYXRlIHRoZXkgYXJlIHRydWx5IHN0cmluZ3Mgb3IgdGhlIG1pZGRsZSByb3cgc2hvdWxkIHNvIGl0IGlzIGFsbCBzdHJpbmcgZGF0YS4KCiogRm9yIHRoZSBmaWZ0aCBhbmQgZmluYWwgcm93LCB0aGUgdmFsdWVzIGFyZSBzZXBhcmF0ZWQgYnkgc2VtaWNvbG9ucyBpbnN0ZWFkIG9mIGNvbW1hcy4gVXNlIHJlYWRfY3N2Mi4KCiMjIyMgMTEuMyBQYXJzaW5nIGEgdmVjdG9yCgpCZWZvcmUgd2UgZ2V0IGludG8gdGhlIGRldGFpbHMgb2YgaG93IHJlYWRyIHJlYWRzIGZpbGVzIGZyb20gZGlzaywgd2UgbmVlZCB0byB0YWtlIGEgbGl0dGxlIGRldG91ciB0byB0YWxrIGFib3V0IHRoZSBfX3BhcnNlXyooKV9fIGZ1bmN0aW9ucy4gVGhlc2UgZnVuY3Rpb25zIHRha2UgYSBjaGFyYWN0ZXIgdmVjdG9yIGFuZCByZXR1cm4gYSBtb3JlIHNwZWNpYWxpc2VkIHZlY3RvciBsaWtlIGEgbG9naWNhbCwgaW50ZWdlciwgb3IgZGF0ZToKCmBgYHtyfQpzdHIocGFyc2VfbG9naWNhbChjKCJUUlVFIiwgIkZBTFNFIiwgIk5BIikpKQpzdHIocGFyc2VfaW50ZWdlcihjKCIxIiwiMiIsIjMiKSkpCnN0cihwYXJzZV9kYXRlKGMoIjIwMTAtMDEtMDEiLCAiMTk3OS0xMC0xNCIpKSkKYGBgCgpUaGVzZSBmdW5jdGlvbnMgYXJlIHVzZWZ1bCBpbiB0aGVpciBvd24gcmlnaHQsIGJ1dCBhcmUgYWxzbyBhbiBpbXBvcnRhbnQgYnVpbGRpbmcgYmxvY2sgZm9yIHJlYWRyLiBPbmNlIHlvdSd2ZSBsZWFybmVkIGhvdyB0aGUgaW5kaXZpZHVhbCBwYXJzZXJzIHdvcmsgaW4gdGhpcyBzZWN0aW9uLCB3ZSdsbCBjaXJjbGUgYmFjayBhbmQgc2VlIGhvdyB0aGV5IGZpdCB0b2dldGhlciB0byBwYXJzZSBhIGNvbXBsZXRlIGZpbGUgaW4gdGhlIG5leHQgc2VjdGlvbi4KCkxpa2UgYWxsIGZ1bmN0aW9ucyBpbiB0aGUgdGlkeXZlcnNlLCB0aGUgX19wYXJzZSooKV9fIGZ1bmN0aW9ucyBhcmUgdW5pZm9ybTogdGhlIGZpcnN0IGFyZ3VtZW50IGlzIGEgY2hhcmFjdGVyIHZlY3RvciB0byBwYXJzZSwgYW5kIHRoZSBfX25hX18gYXJndW1lbnQgc3BlY2lmaWVzIHdoaWNoIHN0cmluZ3Mgc2hvdWxkIGJlIHRyZWF0ZWQgYXMgbWlzc2luZzoKCmBgYHtyfQpwYXJzZV9pbnRlZ2VyKGMoIjEiLCAiMjMxIiwgIi4iLCAiNDU2IiksIG5hID0gIi4iKQpgYGAKCklmIHBhcnNpbmcgZmFpbHMsIHlvdSdsbCBnZXQgYSB3YXJuaW5nOgoKYGBge3J9CnggPC0gcGFyc2VfaW50ZWdlcihjKCIxMjMiLCAiMzQ1IiwgImFiYyIsICIxMjMuNDUiKSkKYGBgCgpBbmQgdGhlIGZhaWx1cmVzIHdpbGwgYmUgbWlzc2luZyBpbiB0aGUgb3V0cHV0OgoKYGBge3J9CngKYGBgCgpJZiB0aGVyZSBhcmUgbWFueSBwYXJzaW5nIGZhaWx1cmVzLCB5b3UnbGwgbmVlZCB0byB1c2UgX19wcm9ibGVtcygpX18gdG8gZ2V0IHRoZSBjb21wbGV0ZSBzZXQuIFRoaXMgcmV0dXJucyBhIHRpYmJsZSwgd2hpY2ggeW91IGNhbiB0aGVuIG1hbmlwdWxhdGUgd2l0aCBkcGx5ci4KCmBgYHtyfQpwcm9ibGVtcyh4KQpgYGAKClVzaW5nIHBhcnNlcnMgaXMgbW9zdGx5IGEgbWF0dGVyIG9mIHVuZGVyc3RhbmQgd2hhdCdzIGF2YWlsYWJsZSBhbmQgaG93IHRoZXkgZGVhbCB3aXRoIGRpZmZlcmVudCB0eXBlcyBvZiBpbnB1dC4gVGhlcmUgYXJlIGVpZ2h0IHBhcnRpY3VsYXJseSBpbXBvcnRhbnQgcGFyc2VyczoKCjEuIF9fcGFyc2VfbG9naWNhbCgpX18gYW5kIF9fcGFyc2VfaW50ZWdlcigpX18gcGFyc2UgbG9naWNhbHMgYW5kIGludGVnZXJzIHJlc3BlY3RpdmVseS4gVGhlcmUncyBiYXNpY2FsbHkgbm90aGluZyB0aGF0IGNhbiBnbyB3cm9uZyB3aXRoIHRoZXNlIHBhcnNlcnMgc28gSSB3b24ndCBkZXNjcmliZSB0aGVtIGhlcmUgZnVydGhlci4KCjIuIF9fcGFyc2VfZG91YmxlKClfXyBpcyBhIHN0cmljdCBudW1lcmljIHBhcnNlciwgYW5kIF9fcGFyc2VfbnVtYmVyKClfXyBpcyBhIGZsZXhpYmxlIG51bWVyaWMgcGFyc2VyLiBUaGVzZSBhcmUgbW9yZSBjb21wbGljYXRlZCB0aGFuIHlvdSBtaWdodCBleHBlY3QgYmVjYXVzZSBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIHdvcmxkIHdyaXRlIG51bWJlcnMgaW4gZGlmZmVyZW50IHdheXMuCgozLiBfX3BhcnNlX2NoYXJhY3RlcigpX18gc2VlbXMgc28gc2ltcGxlIHRoYXQgaXQgc2hvdWxkbid0IGJlIG5lY2Vzc2FyeS4gQnV0IG9uZSBjb21wbGljYXRpb24gbWFrZXMgaXQgcXVpdGUgaW1wb3J0YW50OiBjaGFyYWN0ZXIgZW5jb2RpbmdzLgoKNC4gX19wYXJzZV9mYWN0b3IoKV9fIGNyZWF0ZSBmYWN0b3JzLCB0aGUgZGF0YSBzdHJ1Y3R1cmUgdGhhdCBSIHVzZXMgdG8gcmVwcmVzZW50IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyB3aXRoIGZpeGVkIGFuZCBrbm93biB2YWx1ZXMuCgo1LiBfX3BhcnNlX2RhdGV0aW1lKCksIHBhcnNlX2RhdGUoKSxfXyBhbmQgX19wYXJzZV90aW1lKClfXyBhbGxvdyB5b3UgdG8gcGFyc2UgdmFyaW91cyBkYXRlICYgdGltZSBzcGVjaWZpY2F0aW9ucy4gVGhlc2UgYXJlIHRoZSBtb3N0IGNvbXBsaWNhdGVkIGJlY2F1c2UgdGhlcmUgYXJlIHNvIG1hbnkgZGlmZmVyZW50IHdheXMgb2Ygd3JpdGluZyBkYXRlcy4KClRoZSBmb2xsb3dpbmcgc2VjdGlvbnMgZGVzY3JpYmUgdGhlc2UgcGFyc2VycyBpbiBtb3JlIGRldGFpbC4KCiMjIyMgMTEuMy4xIE51bWJlcnMKCkl0IHNlZW1zIGxpa2UgaXQgc2hvdWxkIGJlIHN0cmFpZ2h0Zm9yd2FyZCB0byBwYXJzZSBhIG51bWJlciwgYnV0IHRocmVlIHByb2JsZW1zIG1ha2UgaXQgdHJpY2t5OgoKMS4gUGVvcGxlIHdyaXRlIG51bWJlcnMgZGlmZmVyZW50bHkgaW4gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSB3b3JsZC4KCjIuIE51bWJlcnMgYXJlIG9mdGVuIHN1cnJvdW5kZWQgYnkgb3RoZXIgY2hhcmFjdGVycyB0aGF0IHByb3ZpZGUgc29tZSBjb250ZXh0LCBsaWtlICIkMTAwMCIgb3IgIjEwJSIuCgozLiBOdW1iZXJzIG9mdGVuIGNvbnRhaW4gImdyb3VwaW5nIiBjaGFyYWN0ZXJzIHRvIG1ha2UgdGhlbSBlYXNpZXIgdG8gcmVhZCwgbGlrZSAiMSwwMDAsMDAwIiwgYW5kIHRoZXNlIGdyb3VwaW5nIGNoYXJhY3RlcnMgdmFyeSBhcm91bmQgdGhlIHdvcmxkLgoKVG8gYWRkcmVzcyB0aGUgZmlyc3QgcHJvYmxlbSwgcmVhZHIgaGFzIHRoZSBub3Rpb24gb2YgYSAibG9jYWxlIiwgYW4gb2JqZWN0IHRoYXQgc3BlY2lmaWVzIHBhcnNpbmcgb3B0aW9ucyB0aGF0IGRpZmZlciBmcm9tIHBsYWNlIHRvIHBsYWNlLiBXaGVuIHBhcnNpbmcgbnVtYmVycywgdGhlIG1vc3QgaW1wb3J0YW50IG9wdGlvbiBpcyB0aGUgY2hhcmFjdGVyIHlvdSB1c2UgZm9yIHRoZSBkZWNpbWFsIG1hcmsuIFlvdSBjYW4gb3ZlcnJpZGUgdGhlIGRlZmF1bHQgdmFsdWUgb2YgX18uX18gYnkgY3JlYXRpbmcgYSBuZXcgbG9jYWxlIGFuZCBzZXR0aW5nIHRoZSBfX2RlY2ltYWxfbWFya19fIGFyZ3VtZW50OgoKYGBge3J9CnBhcnNlX2RvdWJsZSgiMS4yMyIpCnBhcnNlX2RvdWJsZSgiMSwyMyIsIGxvY2FsZSA9IGxvY2FsZShkZWNpbWFsX21hcmsgPSAiLCIpKQpgYGAKCnJlYWRyJ3MgZGVmYXVsdCBsb2NhbGUgaXMgVVMtY2VudHJpYywgYmVjYXVzZSBnZW5lcmFsbHkgUiBpcyBVUy1jZW50cmljIChpLmUuIHRoZSBkb2N1bWVudGF0aW9uIG9mIGJhc2UgUiBpcyB3cml0dGVuIGluIEFtZXJpY2FuIEVuZ2xpc2gpLiBBbiBhbHRlcm5hdGl2ZSBhcHByb3VjaCB3b3VsZCBiZSB0byB0cnkgYW5kIGd1ZXNzIHRoZSBkZWZhdWx0cyBmcm9tIHlvdXIgb3BlcmF0aW5nIHN5c3RlbS4KCl9fcGFyc2VfbnVtYmVyKClfXyBhZGRyZXNzZXMgdGhlIHNlY29uZCBwcm9ibGVtOiBpdCBpZ25vcmVzIG5vbi1udW1lcmljIGNoYXJhY3RlcnMgYmVmb3JlIGFuZCBhZnRlciB0aGUgbnVtYmVyLiBUaGlzIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGN1cnJpZW5jaWVzIGFuZCBwZXJjZW50YWdlcywgYnV0IGFsc28gd29ya3MgdG8gZXh0cmFjdCBudW1iZXJzIGVtYmVkZGVkIGluIHRleHQuCgpgYGB7cn0KcGFyc2VfbnVtYmVyKCIkMTAwIikKcGFyc2VfbnVtYmVyKCIyMCUiKQpwYXJzZV9udW1iZXIoIkl0IGNvc3QgJDEyMy40NSIpCmBgYAoKVGhlIGZpbmFsIHByb2JsZW0gaXMgYWRkcmVzc2VkIGJ5IHRoZSBjb21iaW5hdGlvbiBvZiBfX3BhcnNlX251bWJlcigpX18gYW5kIHRoZSBsb2NhbGUgYXMgX19wYXJzZV9udW1iZXIoKV9fIHdpbGwgaWdub3JlIHRoZSAiZ3JvdXBpbmcgbWFyayI6CgpgYGB7cn0KIyBVc2VkIGluIEFtZXJpY2EKcGFyc2VfbnVtYmVyKCIkMTIzLDQ1Niw3ODkiKQoKIyBVc2VkIGluIG1hbnkgcGFydHMgb2YgRXVyb3BlCnBhcnNlX251bWJlcigiMTIzLjQ1Ni43ODkiLCBsb2NhbGUgPSBsb2NhbGUoZ3JvdXBpbmdfbWFyayA9ICIuIikpCgojIFVzZWQgaW4gU3dpdHplcmxhbmQKcGFyc2VfbnVtYmVyKCIxMjMnNDU2Jzc4OSIsIGxvY2FsZSA9IGxvY2FsZShncm91cGluZ19tYXJrID0gIiciKSkKYGBgCgojIyMjIDExLjMuMiBTdHJpbmdzCgpJdCBzZWVtcyBsaWtlIF9fcGFyc2VfY2hhcmFjdGVyKClfXyBzaG91bGQgYmUgcmVhbGx5IHNpbXBsZSAtIGl0IGNvdWxkIGp1c3QgcmV0dXJuIGl0cyBpbnB1dC4gVW5mb3J0dW5hdGVseSBpdCBpcyBub3Qgc28gc2ltcGxlLiBUbyBzZWUgaG93IGNvbXB1dGVycyByZXByZXNlbnQgc3RyaW5ncywgd2UgY2FuIGdldCB0aGUgdW5kZXJseWluZyByZXByZXNlbnRhdGlvbiB1c2luZyBfX2NoYXJUb1JhdygpX186CgpgYGB7cn0KY2hhclRvUmF3KCJIYWRsZXkiKQpgYGAKCkVhY2ggaGV4YWRlY2ltYWwgbnVtYmVyIHJwcmVzZW50cyBhIGJ5dGUgb2YgaW5mb3JtYXRpb246IF9fNDhfXyBpcyBILCBfXzYxX18gaXMgYSwgYW5kIHNvIG9uLiBUaGUgbWFwcGluZyBmcm9tIGhleGFkZWNpbWFsIG51bWJlciB0byBjaGFyYWN0ZXIgaXMgY2FsbGVkIHRoZSBlbmNvZGluZywgYW5kIGluIHRoaXMgY2FzZSB0aGUgZW5jb2RpbmcgaXMgY2FsbGVkIEFTQ0lJLiBBU0NJSSBkb2VzIGEgZ3JlYXQgam9iIG9mIHJlcHJlc2VudGluZyBFbmdsaXNoIGNoYXJhY3RlcnMsIGJlY2F1c2UgaXQncyB0aGUgX19BbWVyaWNhbl9fIFN0YW5kYXJkIENvZGUgZm9yIEluZm9ybWF0aW9uIEludGVyY2hhbmdlLgoKSG93ZXZlciB0aGVyZSB1c2VkIHRvIG5vdCBiZSBhIHN0YW5kYXJkIGZvciBmb3JlaWduIGxhbmd1YWdlcy4gVG9kYXkgX19VVEYtOF9fIGlzIHRoZSBzdGFuZGFyZCB0aGF0IGlzIHN1cHBvcnRlZCBhbG1vc3QgZXZlcnl3aGVyZS4gVVRGLTggY2FuIGVuY29kZSBqdXN0IGFib3V0IGV2ZXJ5IGNoYXJhY3RlciB1c2VkIGJ5IGh1bWFucyB0b2RheSwgYXMgd2VsbCBhcyBtYW55IGV4dHJhIHN5bWJvbGVzLgoKcmVhZHIgdXNlcyBVVEYtOCBldmVyeXdoZXJlOiBpdCBhc3N1bWVzIHlvdXIgZGF0YSBpcyBVVEYtOCBlbmNvZGVkIHdoZW4geW91IHJlYWQgaXQsIGFuZCBhbHdheXMgdXNlcyBpdCB3aGVuIHdyaXRpbmcuIFRoaXMgaXMgYSBnb29kIGRkZWZhdWx0LCBidXQgd2lsbCBmYWlsIGZvciBkYXRhIHByb2R1Y2VkIGJ5IG9sZGVyIHN5c3RlbXMgdGhhdCBkb24ndCB1bmRlcnN0YW5kIFVURi04LiAgSWYgdGhpcyBoYXBwZW5zIHRvIHlvdSwgeW91ciBzdHJpbmdzIHdpbGwgbG9vayB3ZWlyZCB3aGVuIHlvdSBwcmludCB0aGVtLiBTb21ldGltZXMganVzdCBvbmUgb3IgdHdvIGNoYXJhY3RlcnMgbWlnaHQgYmUgbWVzc2VkIHVwOyBvdGhlciB0aW1lcyB5b3UnbGwgZ2V0IGNvbXBsZXRlIGdpYmJlcmlzaC4gRm9yIGV4YW1wbGU6CgpgYGB7cn0KeDEgPC0gIkVsIE5pXHhmMW8gd2FzIHBhcnRpY3VsYXJseSBiYWQgdGhpcyB5ZWFyIgp4MiA8LSAiXHg4Mlx4YjFceDgyXHhmMVx4ODJceGM5XHg4Mlx4YmZceDgyXHhjZCIKCngxCngyCmBgYAoKYGBge3J9CnBhcnNlX2NoYXJhY3Rlcih4MSwgbG9jYWxlID0gbG9jYWxlKGVuY29kaW5nID0gIkxhdGluMSIpKQpwYXJzZV9jaGFyYWN0ZXIoeDIsIGxvY2FsZSA9IGxvY2FsZShlbmNvZGluZyA9ICJTaGlmdC1KSVMiKSkKYGBgCgpIb3cgZG8geW91IGZpbmQgdGhlIGNvcnJlY3QgZW5jb2Rpbmc/IElmIHlvdSdyZSBsdWNreSwgaXQnbGwgYmUgaW5jbHVkZWQgc29tZXdoZXJlIGluIHRoZSBkYXRhIGRvY3VtZW50YXRpb24uIFVuZm9ydHVuYXRlbHksIHRoYXQncyByYXJlbHkgdGhlIGNhc2UsIHNvIHJlYWRyIHByb3ZpZGVzIF9fZ3Vlc3NfZW5jb2RpbmcoKV9fIHRvIGhlbHAgeW91IGZpZ3VyZSBpdCBvdXQuIEl0J3Mgbm90IGZvb2xwcm9vZiwgYW5kIGl0IHdvcmtzIGJldHRlciB3aGVuIHlvdSBoYXZlIGxvdHMgb2YgdGV4dCAodW5saWtlIGhlcmUpLCBidXQgaXQncyBhIHJlYXNvbmFibGUgcGxhY2UgdG8gc3RhcnQuIEV4cGVjdCB0byB0cnkgYSBmZXcgZGlmZmVyZW50IGVuY29kaW5ncyBiZWZvcmUgeW91IGZpbmQgdGhlIHJpZ2h0IG9uZS4KCmBgYHtyfQpndWVzc19lbmNvZGluZyhjaGFyVG9SYXcoeDEpKQpndWVzc19lbmNvZGluZyhjaGFyVG9SYXcoeDIpKQpgYGAKCgpUaGUgZmlyc3QgYXJndW1lbnQgdG8gX19ndWVzc19lbmNvZGluZygpX18gY2FuIGVpdGhlciBiZSBhIHBhdGggdG8gYSBmaWxlIG9yLCBhcyBpbiB0aGlzIGNhc2UsIGEgcmF3IHZlY3Rvcih1c2VmdWwgaWYgdGhlIHN0cmluZ3MgYXJlIGFscmVhZHkgaW4gUi4pCgojIyMjIDExLjMuMyBGYWN0b3JzCgpSIHVzZXMgZmFjdG9ycyB0byByZXByZXNlbnQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRoYXQgaGF2ZSBhIGtub3duIHNldCBvZiBwb3NzaWJsZSB2YWx1ZXMuIEdpdmUgX19wYXJzZV9mYWN0b3IoKV9fIGEgdmVjdG9yIG9mIGtub3duIF9fbGV2ZWxzX18gdG8gZ2VuZXJhdGUgYSB3YXJuaW5nIHdoZW5ldmVyIGFuIHVuZXhwZWN0ZWQgdmFsdWUgaXMgcHJlc2VudDoKCmBgYHtyfQpmcnVpdCA8LSBjKCJhcHBsZSIsICJiYW5hbmEiKQpwYXJzZV9mYWN0b3IoYygiYXBwbGUiLCAiYmFuYW5hIiwgImJhbmFuYW5hIiksIGxldmVscyA9IGZydWl0KQpgYGAKCkJ1dCBpZiB5b3UgaGF2ZSBtYW55IHByb2JsZW1hdGljIGVudHJpZXMsIGl0J3Mgb2Z0ZW4gZWFzaWVyIHRvIGxlYXZlIGFzIGNoYXJhY3RlciB2ZWN0b3JzIGFuZCB0aGVuIHVzZSB0aGUgdG9vbHMgeW91J2xsIGxlYXJuIGFib3V0IGluIHN0cmluZ3MgYW5kIGZhY3RvcnMgdG8gY2xlYW4gdGhlbSB1cC4KCiMjIyMgMTEuMy40IERhdGVzLCBkYXRlLXRpbWVzLCBhbmQgdGltZXMKCllvdSBwaWNrIGJldHdlZW4gdGhyZWUgcGFyc2VycyBkZXBlbmRpbmcgb24gd2hldGhlciB5b3Ugd2FudCBhIGRhdGUgKHRoZSBudW1iZXIgb2YgZGF5cyBzaW5jZSAxOTcwLTAxLTAxKSwgYSBkYXRlLXRpbWUgKHRoZSBudW1iZXIgb2Ygc2Vjb25kcyBzaW5jZSBtaWRuaWdodCAxOTcwLTAxLTAxKSwgb3IgYSB0aW1lKHRoZSBudW1iZXIgb2Ygc2Vjb25kcyBzaW5jZSBtaWRuaWdodCkuIFdoZW4gY2FsbGVkIHdpdGhvdXQgYW55IGFkZGl0aW9uYWwgYXJndW1lbnRzOgoKKiBfX3BhcnNlX2RhdGV0aW1lKClfXyBleHBlY3RzIGFuIElTTzg2MDEgZGF0ZS10aW1lLiBJU08gODYwMSBpcyBhbiBpbnRlcm5hdGlvbmFsIHN0YW5kYXJkIGluIHdoaWNoIHRoZSBjb21wb25lbnRzIG9mIGEgZGF0ZSBhcmUgb3JnYW5pc2VkIGZyb20gYmlnZ2VzdCB0byBzbWFsbGVzdDogeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlLCBzZWNvbmQuCgpgYGB7cn0KcGFyc2VfZGF0ZXRpbWUoIjIwMTAtMTAtMDFUMjAxMCIpCiMgSWYgdGltZSBpcyBvbWl0dGVkLCBpdCB3aWxsIGJlIHNldCB0byBtaWRuaWdodC4KcGFyc2VfZGF0ZXRpbWUoIjIwMTAxMDEwIikKYGBgCgpUaGlzIGlzIHRoZSBtb3N0IGltcG9ydGFudCBkYXRlL3RpbWUgc3RhbmRhcmQsIGFuZCBpZiB5b3Ugd29yayB3aXRoIGRhdGVzIGFuZCB0aW1lcyBmcmVxdWVudGx5LCBJIHJlY29tbWVuZCByZWFkaW5nIGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0lTT184NjAxCgoqIF9fcGFyc2VfZGF0ZSgpX18gZXhwZWN0cyBhIGZvdXIgZGlnaXQgeWVhciwgYSBfXy1fXyBvciBfXy9fXywgdGhlIG1vbnRoLCBhIF9fLV9fIG9yIF9fL19fLiB0aGVuIHRoZSBkYXk6CgpgYGB7cn0KcGFyc2VfZGF0ZSgiMjAxMC0xMC0wMSIpCmBgYAoKKiBfX3BhcnNlX3RpbWUoKV9fIGV4cGVjdHMgdGhlIGhvdXIsIF9fOl9fLCBtaW51dGVzLCBvcHRpb25hbGx5IF9fOl9fIGFuZCBzZWNvbmRzLCBhbmQgYW4gb3B0aW9uYWwgYW0vcG0gc3BlY2lmaWVyOgoKYGBge3J9CnBhcnNlX3RpbWUoIjAxOjEwIGFtIikKcGFyc2VfdGltZSgiMjA6MTA6MDEiKQpgYGAKCkJhc2UgUiBkb2Vzbid0IGhhdmUgYSBncmVhdCBidWlsdCBpbiBjbGFzcyBmb3IgdGltZSBkYXRhLCBzbyB3ZSB1c2UgdGhlIG9uZSBwcm92aWRlZCBpbiB0aGUgaG1zIHBhY2thZ2UuCgpJZiB0aGVzZSBkZWZhdWx0cyBkb24ndCB3b3JrIGZvciB5b3VyIGRhdGEgeW91IGNhbiBzdXBwbHkgeW91ciBvd24gZGF0ZS10aW1lIF9fZm9ybWF0X18sIGJ1aWx0IHVwIG9mIHRoZSBmb2xsb3dpbmcgcGllY2VzOgoKX19ZZWFyX18KCipfXyVZX18gKDQgZGlnaXRzKS4KKiBfXyV5X18gKDIgZGlnaXRzKTsgMDAtNjkgLT4gMjAwMC0yMDY5LCA3MC05OSAtPiAxOTcwLTE5OTkuCgpfX01vbnRoX18KCiogX18lbV9fICgyIGRpZ2l0cykuCiogX18lYl9fIChhYmJyZXZpYXRlZCBuYW1lLCBsaWtlICJKYW4iKS4KKiBfXyVCX18gKGZ1bGwgbmFtZSwgIkphbnVhcnkiKS4KCl9fRGF5X18KCiogX18lZF9fICgyIGRpZ2l0cykuCiogX18lZV9fIChvcHRpb25hbCBsZWFkaW5nIHNwYWNlKS4KCl9fVGltZV9fCgoqIF9fJUhfXyAwLTIzIGhvdXIuCiogX18lSV9fIDAtMTIsIG11c3QgYmUgdXNlZCB3aXRoIF9fJXBfXy4KKiBfXyVwX18gQU0vUE0gaW5kaWNhdG9yLgoqIF9fJU1fXyBtaW51dGVzLgoqIF9fJVNfXyBpbnRlZ2VyIHNlY29uZHMuCiogX18lT1NfXyByZWFsIHNlY29uZHMuCgoqIF9fJVpfXyBUaW1lIHpvbmUgKGFzIG5hbWUsIGUuZy4gX19BbWVyaWNhL0NoaWNhZ29fXykuIEJld2FyZSBvZiBhYmJyZXZpYXRpb25zOiBpZiB5b3UncmUgQW1lcmljYW4sIG5vdGUgdGhhdCAiRVNUIiBpcyBhIENhbmFkaWFuIHRpbWUgem9uZSB0aGF0IGRvZXMgbm90IGhhdmUgZGF5bGlnaHQgc2F2aW5ncyB0aW1lLiBJdCBpcyAqbm90KiBFYXN0ZXJuIFN0YW5kYXJkIFRpbWUhIFdlJ2xsIGNvbWUgYmFjayB0byB0aGlzIF9fdGltZSB6b25lc19fLgoqIF9fJXpfXyAoYXMgb2Zmc2V0IGZyb20gVVRDLCBlLmcuIF9fKzA4MDBfXykuCgpfX05vbi1kaWdpdHNfXwoKKiBfXyUuX18gc2tpcHMgb25lIG5vbi1kaWdpdCBjaGFyYWN0ZXIuCiogX18lKl9fIHNraXBzIGFueSBudW1iZXIgb2Ygbm9uLWRpZ2l0cy4KClRoZSBiZXN0IHdheSB0byBmaWd1cmUgb3V0IHRoZSBjb3JyZWN0IGZvcm1hdCBpcyB0byBjcmVhdGUgYSBmZXcgZXhhbXBsZXMgaW4gYSBjaGFyYWN0ZXIgdmVjdG9yLCBhbmQgdGVzdCB3aXRoIG9uZSBvZiB0aGUgcGFyc2luZyBmdW5jdGlvbnMuIEZvciBleGFtcGxlOgoKYGBge3J9CnBhcnNlX2RhdGUoIjAxLzAyLzExIiwgZm9ybWF0ID0gIiVtLyVkLyV5IikKcGFyc2VfZGF0ZSgiMDEvMDIvMTEiLCBmb3JtYXQgPSAiJWQvJW0vJXkiKQpwYXJzZV9kYXRlKCIwMS8wMi8xMSIsIGZvcm1hdCA9ICIleS8lbS8lZCIpCmBgYAoKSWYgeW91J3JlIHVzaW5nIF9fJWJfXyBvciBfXyVCX18gd2l0aCBub24tRW5nbGlzaCBtb250aCBuYW1lcywgeW91J2xsIG5lZWQgdG8gc2V0IHRoZSBfX2xhbmdfXyBhcmd1bWVudCB0byBfX2xvY2FsZSgpX18uIFNlZSB0aGUgbGlzdCBvZiBidWlsdC1pbiBsYW5ndWFnZXMgaW4gX19kYXRlX25hbWVzX2xhbmdzKClfXywgb3IgaWYgeW91ciBsYW5ndWFnZSBpcyBub3QgYWxyZWFkeSBpbmNsdWRlZCwgY3JlYXRlIHlvdXIgb3duIHdpdGggX19kYXRlX25hbWVzKClfXy4KCmBgYHtyfQpwYXJzZV9kYXRlKCIxIGphbnZpZXIgMjAxNSIsICIlZCAlQiAlWSIsIGxvY2FsZSA9IGxvY2FsZSgiZnIiKSkKYGBgCgojIyMjIDExLjMuNSBFeGVyY2lzZXMKCjEuIFdoYXQgYXJlIHRoZSBtb3N0IGltcG9ydGFudCBhcmd1bWVudHMgdG8gX19sb2NhbGUoKV9fPwoKKiBkYXRlIGFuZCB0aW1lIGZvcm1hdHRpbmc6IGRhdGVfbmFtZXMsIGRhdGVfZm9ybWF0IAoqIHRpbWUgem9uZTogdHogKHRpbWUgem9uZSkKKiBudW1iZXIgZm9ybWF0dGluZzogZGVjaW1hbF9tYXJrLCBncm91cGluZ19tYXJrLAoqIGVuY29kaW5nIC0gZW5jb2RpbmcKCjIuIFdoYXQgaGFwcGVucyBpZiB5b3UgdHJ5IGFuZCBzZXQgX19kZWNpbWFsX21hcmtfXyBhbmQgX19ncm91cGluZ19tYXJrX18gdG8gdGhlIHNhbWUgY2hhcmFjdGVyPyBXaGF0IGhhcHBlbnMgdG8gdGhlIGRlZmF1bHQgdmFsdWUgb2YgX19ncm91cGluZ19tYXJrX18gd2hlbiB5b3Ugc2V0IF9fZGVjaW1hbF9tYXJrX18gdG8g4oCcLOKAnT8gV2hhdCBoYXBwZW5zIHRvIHRoZSBkZWZhdWx0IHZhbHVlIG9mIF9fZGVjaW1hbF9tYXJrX18gd2hlbiB5b3Ugc2V0IHRoZSBfX2dyb3VwaW5nX21hcmtfXyB0byDigJwu4oCdPwoKYGBge3J9CmxvY2FsZShkZWNpbWFsX21hcmsgPSAiLiIsIGdyb3VwaW5nX21hcmsgPSAiLiIpCmBgYApZb3UgcmVjZWl2ZSBhbiBlcnJvciBtZXNzYWdlIGluZGljYXRpbmcgdGhlIHR3byBtdXN0IGJlIGRpZmZlcmVudC4KCmBgYHtyfQpsb2NhbGUoZGVjaW1hbF9tYXJrID0gIiwiKQpgYGAKCklmIHRoZSBkZWNpbWFsIG1hcmsgaXMgc2V0IHRvICIsIiwgdGhlbiB0aGUgZ3JvdXBpbmcgbWFyayBhc3N1bWVzIHRoZSB2YWx1ZSBvZiAiLiIuCgpgYGB7cn0KbG9jYWxlKGdyb3VwaW5nX21hcmsgPSAiLiIpCmBgYAoKSWYgdGhlIGdyb3VwaW5nIG1hcmsgaXMgc2V0IHRvICIuIiwgdGhlbiB0aGUgZGVjaW1hbCBtYXJrIGFzc3VtZXMgdGhlIHZhbHVlIG9mICIsIi4KCjMuIEkgZGlkbid0IGRpc2N1c3MgdGhlIF9fZGF0ZV9mb3JtYXRfXyBhbmQgX190aW1lX2Zvcm1hdF9fIG9wdGlvbnMgdG8gX19sb2NhbGUoKV9fLiBXaGF0IGRvIHRoZXkgZG8/IENvbnN0cnVjdCBhbiBleGFtcGxlIHRoYXQgc2hvd3Mgd2hlbiB0aGV5IG1pZ2h0IGJlIHVzZWZ1bC4KCmBgYHtyfQpwYXJzZV9kYXRldGltZSgiMTItMTMtMDNUMDM1MDA2IiwgbG9jYWxlID0gbG9jYWxlKGRhdGVfZm9ybWF0ID0gIiVtLyVkLyV5IiwgdGltZV9mb3JtYXQgPSAiJUgvJU0vJVMiKSkKYGBgCgoqc2tpcAoKNC4gSWYgeW91IGxpdmUgb3V0c2lkZSB0aGUgVVMsIGNyZWF0ZSBhIG5ldyBsb2NhbGUgb2JqZWN0IHRoYXQgZW5jYXBzdWxhdGVzIHRoZSBzZXR0aW5ncyBmb3IgdGhlIHR5cGVzIG9mIGZpbGUgeW91IHJlYWQgbW9zdCBjb21tb25seS4KCkJhc2ljYWxseSwgY3JlYXRlIGEgbmV3IGZ1bmN0aW9uCgpgYGB7cn0KZm9yZWlnbl9sb2NhbGUgPC0gbG9jYWxlKGRhdGVfZm9ybWF0ID0gIiVkLyVtLyVZIikKYGBgCgpgYGB7cn0KcGFyc2VfZGF0ZSgiMDIvMDEvMjAwMSIsIGxvY2FsZSA9IGZvcmVpZ25fbG9jYWxlKQpgYGAKCjUuIFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIF9fcmVhZF9jc3YoKV9fIGFuZCBfX3JlYWRfY3N2MigpX18/CgpfX3JlYWRfY3N2KClfXyBhc3N1bWVzICIsIiBhcyB0aGUgZGVsaW1pdGVyOyBfX3JlYWRfY3N2MigpX18gYXNzdW1lcyAiOyIgYXMgdGhlIGRlbGltaXRlci4KCjYuIFdoYXQgYXJlIHRoZSBtb3N0IGNvbW1vbiBlbmNvZGluZ3MgdXNlZCBpbiBFdXJvcGU/IFdoYXQgYXJlIHRoZSBtb3N0IGNvbW1vbiBlbmNvZGluZ3MgdXNlZCBpbiBBc2lhPyBEbyBzb21lIGdvb2dsaW5nIHRvIGZpbmQgb3V0LiAKCipza2lwCgo3LiBHZW5lcmF0ZSB0aGUgY29ycmVjdCBmb3JtYXQgc3RyaW5nIHRvIHBhcnNlIGVhY2ggb2YgdGhlIGZvbGxvd2luZyBkYXRlcyBhbmQgdGltZXM6CgpgYGB7cn0KZDEgPC0gIkphbnVhcnkgMSwgMjAxMCIKZDIgPC0gIjIwMTUtTWFyLTA3IgpkMyA8LSAiMDYtSnVuLTIwMTciCmQ0IDwtIGMoIkF1Z3VzdCAxOSAoMjAxNSkiLCAiSnVseSAxICgyMDE1KSIpCmQ1IDwtICIxMi8zMC8xNCIgIyBEZWMgMzAsIDIwMTQKdDEgPC0gIjE3MDUiCnQyIDwtICIxMToxNToxMC4xMiBQTSIKYGBgCgpgYGB7cn0KcGFyc2VfZGF0ZShkMSwgbG9jYWxlID0gbG9jYWxlKGRhdGVfZm9ybWF0ID0gIiVCICVkLCAlWSIpKQpwYXJzZV9kYXRlKGQyLCBsb2NhbGUgPSBsb2NhbGUoZGF0ZV9mb3JtYXQgPSAiJVktJWItJWQiKSkKcGFyc2VfZGF0ZShkMywgbG9jYWxlID0gbG9jYWxlKGRhdGVfZm9ybWF0ID0gIiVkLSViLSVZIikpCnBhcnNlX2RhdGUoZDQsIGxvY2FsZSA9IGxvY2FsZShkYXRlX2Zvcm1hdCA9ICIlQiAlZCAoJVkpIikpCnBhcnNlX2RhdGUoZDUsIGxvY2FsZSA9IGxvY2FsZShkYXRlX2Zvcm1hdCA9ICIlbS8lZC8leSIpKQpwYXJzZV90aW1lKHQxLCBsb2NhbGUgPSBsb2NhbGUodGltZV9mb3JtYXQgPSAiJUglTSIpKQpwYXJzZV90aW1lKHQyLCBsb2NhbGUgPSBsb2NhbGUodGltZV9mb3JtYXQgPSAiJUg6JU06JU9TICVwIikpCmBgYAoKIyMjIyAxMS40IFBhcnNpbmcgYSBmaWxlCgpMZWFybmluZyBvYmplY3RpdmVzIC0gdW5kZXJzdGFuZDoKCjEuIEhvdyByZWFkciBhdXRvbWF0aWNhbGx5IGd1ZXNzZXMgdGhlIHR5cGUgb2YgZWFjaCBjb2x1bW4uCjIuIEhvdyB0byBvdmVycmlkZSB0aGUgZGVmYXVsdCBzcGVjaWZpY2F0aW9uLgoKIyMjIyMgMTEuNC4xIFN0cmF0ZWd5CgpyZWFkciB1c2VzIGEgaGV1cmlzdGljIHRvIGZpZ3VyZSBvdXQgdGhlIHR5cGUgb2YgZWFjaCBjb2x1bW46IGl0IHJlYWRzIHRoZSBmaXJzdCAxMDAwIHJvd3MgYW5kIHVzZXMgc29tZSAobW9kZXJhdGVseSBjb25zZXJ2YXRpdmUpIGhldXJpc3RpY3MgdG8gZmlndXJlIG91dCB0aGUgdHlwZSBvZiBlYWNoIGNvbHVtbi4gCgpgYGB7cn0KZ3Vlc3NfcGFyc2VyKCIyMDEwLTEwLTAxIikKZ3Vlc3NfcGFyc2VyKCIxNTowMSIpCmd1ZXNzX3BhcnNlcihjKCJUUlVFIiwgIkZBTFNFIikpCmd1ZXNzX3BhcnNlcihjKCIxIiwgIjUiLCAiOSIpKQpndWVzc19wYXJzZXIoYygiMTIsMzUyLDU2MSIpKQoKc3RyKHBhcnNlX2d1ZXNzKCIyMDEwLTEwLTEwIikpCmBgYAoKIyMjIyAxMS40LjIgUHJvYmxlbXMKClRoZXNlIGRlZmF1bHRzIGRvbid0IGFsd2F5cyB3b3JrIGZvciBsYXJnZXIgZmlsZXMuIFRoZXJlIGFyZSB0d28gYmFzaWMgcHJvYmxlbXM6CgoxLiBUaGUgZmlyc3QgdGhvdXNhbmQgcm93cyBtaWdodCBiZSBhIHNwZWNpYWwgY2FzZSwgYW5kIHJlYWRyIGd1ZXNzZXMgYSB0eXBlIHRoYXQgaXMgbm90IHN1ZmZpY2llbnRseSBnZW5lcmFsLiBGb3IgZXhhbXBsZSwgeW91IG1pZ2h0IGhhdmUgYSBjb2x1bW4gb2YgZG91YmxlcyB0aGF0IG9ubHkgY29udGFpbnMgaW50ZWdlcnMgaW4gdGhlIGZpcnN0IDEwMDAgcm93cy4KCjIuIFRoZSBjb2x1bW4gbWlnaHQgY29udGFpbiBhIGxvdCBvZiBtaXNzaW5nIHZhbHVlcy4gSWYgdGhlIGZpcnN0IDEwMDAgcm93cyBjb250YWluIG9ubHkgX19OQV9fcywgcmVhZHIgd2lsbCBndWVzIHRoYXQgaXQncyBhIGxvZ2ljYWwgdmVjdG9yLCB3aGVyZWFzIHlvdSBwcm9iYWJseSB3YW50IHRvIHBhcnNlIGl0IGFzIHNvbWV0aGluZyBtb3JlIHNwZWNpZmljLgoKcmVhZHIgY29udGFpbnMgYSBjaGFsbGVuZ2luZyBDU1YgdGhhdCBpbGx1c3RyYXRlcyBib3RoIG9mIHRoZXNlIHByb2JsZW1zOgoKYGBge3J9CmNoYWxsZW5nZSA8LSByZWFkX2NzdihyZWFkcl9leGFtcGxlKCJjaGFsbGVuZ2UuY3N2IikpCmBgYAoKVGhlcmUgYXJlIHR3byBwcmludGVkIG91dHB1dHM6IHRoZSBjb2x1bW4gc3BlY2lmaWNhdGlvbiBnZW5lcmF0ZWQgYnkgbG9va2luZyBhdCB0aGUgZmlyc3QgMTAwMCByb3dzLCBhbmQgdGhlIGZpcnN0IGZpdmUgcGFyc2luZyBmYWlsdXJlcy4gSXQncyBhbHdheXMgYSBnb29kIGlkZWEgdG8gZXhwbGljaXRseSBwdWxsIG91dCB0aGUgX19wcm9ibGVtcygpX18sIHNvIHlvdSBjYW4gZXhwbG9yZSB0aGVtIGluIG1vcmUgZGVwdGg6CgpgYGB7cn0KcHJvYmxlbXMoY2hhbGxlbmdlKQpgYGAKCkEgZ29vZCBzdHJhdGVneSBpcyB0byB3b3JrIGNvbHVtbiBieSBjb2x1bW4gdW50aWwgdGhlcmUgYXJlIG5vIHByb2JsZW1zIHJlbWFpbmluZy4gSGVyZSB3ZSBjYW4gc2VlIHRoYXQgdGhlcmUgYXJlIGEgbG90IG9mIHBhcnNpbmcgcHJvYmxlbXMgd2l0aCB0aGUgX195X18gY29sdW1uLiBJZiB3ZSBsb29rIGF0IHRoZSBsYXN0IGZldyByb3dzLCB5b3UnbGwgc2VlIHRoYXQgdGhleSdyZSBkYXRlcyBzdG9yZXMgaW4gYSBjaGFyYWN0ZXIgdmVjdG9yOgoKYGBge3J9CnRhaWwocmVhZF9jc3YocmVhZHJfZXhhbXBsZSgiY2hhbGxlbmdlLmNzdiIpLCBndWVzc19tYXggPSBtaW4oMjAwMCwgMjAwMCkpKQpgYGAKClRoYXQgc3VnZ2VzdHMgd2UgbmVlZCB0byB1c2UgYSBkYXRlIHBhcnNlciBpbnN0ZWFkLiBUbyBmaXggdGhlIGNhbGwsIHN0YXJ0IGJ5IGNvcHlpbmcgYW5kIHBhc3RpbmcgdGhlIGNvbHVtbiBzcGVjaWZpY2F0aW9uIGludG8geW91ciBvcmlnaW5hbCBjYWxsOgoKYGBge3J9CmNoYWxsZW5nZSA8LSByZWFkX2NzdihyZWFkcl9leGFtcGxlKCJjaGFsbGVuZ2UuY3N2IiksCiAgICAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXMgPSBjb2xzKHggPSAiZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAiRCIpCiAgICAgICAgICAgICAgICAgICAgICApCnRhaWwoY2hhbGxlbmdlKQpgYGAKClRoZSBzdHJhdGVneSBzdWdnZXN0ZWQgdG8gZml4IG9uZSBwYXJzaW5nIGlzc3VlIGF0IGEgdGltZSwgYnV0IEkgZGlkIGJvdGggYXQgdGhlIHNhbWUgdGltZS4gTW92aW5nIGZvcndhcmQsIHdpbGwgZG8gb25lIGJ5IG9uZS4KCkV2ZXJ5IF9fcGFyc2VfeHl6KClfXyBmdW5jdGlvbiBoYXMgYSBjb3JyZXNwb25kaW5nIF9fY29sX3h5eigpX18gZnVuY3Rpb24uIFlvdSB1c2UgX19wYXJzZV94eXooKV9fIHdoZW4gdGhlIGRhdGEgaXMgaW4gYSBjaGFyYWN0ZXIgdmVjdG9yIGluIFIgYWxyZWFkeTsgeW91IHVzZSBfX2NvbF94eXooKV9fIHdoZW4geW91IHdhbnQgdG8gdGVsbCByZWFkciBob3cgdG8gbG9hZCB0aGUgZGF0YS4KCkkgaGlnaGx5IHJlY29tbWVuZCBhbHdheXMgc3VwcGxpbmcgX19jb2xfdHlwZXNfXywgYnVpbGRpbmcgdXAgZnJvbSB0aGUgcHJpbnQtb3V0IHByb3ZpZGVkIGJ5IHJlYWRyLiBUaGlzIGVuc3VyZXMgdGhhdCB5b3UgaGF2ZSBhIGNvbnNpc3RlbnQgYW5kIHJlcHJvZHVjaWJsZSBkYXRhIGltcG9ydCBzY3JpcHQuIElmIHlvdSByZWx5IG9uIHRoZSBkZWZhdWx0IGd1ZXNzZXMgYW5kIHlvdXIgZGF0YSBjaGFuZ2VzLCByZWFkciB3aWxsIGNvbnRpbnVlIHRvIHJlYWQgaXQgaW4uIElmIHlvdSB3YW50IHRvIGJlIHJlYWxseSBzdHJpY3QsIHVzZSBfX3N0b3BfZm9yX3Byb2JsZW1zKClfXzogdGhhdCB3aWxsIHRocm93IGFuIGVycm9yIGFuZCBzdG9wIHlvdXIgc2NyaXB0IGlmIHRoZXJlIGFyZSBhbnkgcGFyc2luZyBwcm9ibGVtcy4KCiMjIyMgMTEuNC4zIE90aGVyIHN0cmF0ZWdpZXMKClRoZXJlIGFyZSBhIGZldyBvdGhlciBnZW5lcmFsIHN0cmF0ZWdpZXMgdG8gaGVscCB5b3UgcGFyc2UgZmlsZXM6CgoqIEluIHRoZSBwcmV2aW91cyBleGFtcGxlLCB3ZSBqdXN0IGdvdCB1bmx1Y2t5OiBpZiB3ZSBsb29rIGF0IGp1c3Qgb25lIG1vcmUgcm93IHRoYW4gdGhlIGRlZmF1bHQsIHdlIGNhbiBjb3JyZWN0bHkgcGFyc2UgaW4gb25lIHNob3Q6CgpgYGB7cn0KY2hhbGxlbmdlMiA8LSByZWFkX2NzdihyZWFkcl9leGFtcGxlKCJjaGFsbGVuZ2UuY3N2IiksIGd1ZXNzX21heCA9IG1pbigxMDAxLCAyMDAwKSkKdGFpbChjaGFsbGVuZ2UyKQpgYGAKCiogU29tZXRpbWVzIGl0J3MgZWFzaWVyIHRvIGRpYWdub3NlIHByb2JsZW1zIGlmIHlvdSBqdXN0IHJlYWQgaW4gYWxsIHRoZSBjb2x1bW5zIGFzIGNoYXJhY3RlciB2ZWN0b3JzOgoKYGBge3J9CmNoYWxsZW5nZTIgPC0gcmVhZF9jc3YocmVhZHJfZXhhbXBsZSgiY2hhbGxlbmdlLmNzdiIpLAogICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMoLmRlZmF1bHQgPSBjb2xfY2hhcmFjdGVyKCkpKQpoZWFkKGNoYWxsZW5nZTIpCnRhaWwoY2hhbGxlbmdlMikKYGBgCgpXZSB1c2UgX19oZWFkKClfXyBhbmQgX190YWlsKClfXyB0byB2aWV3IHRoZSBmaXJzdCBhbmQgbGFzdCBzaXgsIHJlc3BlY3RpdmVseSwgcm93cyBvZiBkYXRhIC0gc2hvd2luZyBob3cgdGhlIGRhdGEgc3RhcnRzIGFzIGludGVnZXIgYW5kIGxvZ2ljYWwgYnV0IGV2ZW50dWFsbHkgdHJhbnNpdGlvbnMgaW50byBkb3VibGUgYW5kIGRhdGUuCgpUaGlzIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgaW4gY29uanVuY3Rpb24gd2l0aCBfX3R5cGVfY29udmVydCgpX18sIHdoaWNoIGFwcGxpZXMgdGhlIHBhcnNpbmcgaGV1cmlzdGljcyB0byB0aGUgY2hhcmFjdGVyIGNvbHVtbnMgaW4gYSBkYXRhIGZyYW1lLgoKYGBge3J9CmRmIDwtIHRyaWJibGUoCiAgfngsIH55LAogICIxIiwgIjEuMjEiLAogICIyIiwgIjIuMzIiLAogICIzIiwgIjQuNTYiCikKZGYKCiMgTm90ZSB0aGUgY29sdW1uIHR5cGVzIC0gPGNoYXJhY3Rlcj4KCnR5cGVfY29udmVydChkZikKCiMgQ29udmVydGVkIHRvIDxkYmw+CmBgYAoKKiBJZiB5b3UncmUgcmVhZGluZyBhIHZlcnkgbGFyZ2UgZmlsZSwgeW91IG1pZ2h0IHdhbnQgdG8gc2V0IF9fbl9tYXhfXyB0byBhIHNtYWxsaXNoIG51bWJlciBsaWtlIDEwLDAwMCBvciAxMDAsMDAwLiBUaGF0IHdpbGwgYWNjZWxlcmF0ZSB5b3VyIGl0ZXJhdGlvbnMgd2hpbGUgeW91IGVsaW1pbmF0ZSBjb21tb24gcHJvYmxlbXMuCgoqIElmIHlvdSdyZSBoYXZpbmcgbWFqb3IgcGFyc2luZyBwcm9ibGVtcywgc29tZXRpbWVzIGl0J3MgZWFzaWVyIHRvIGp1c3QgcmVhZCBpbnRvIGEgY2hhcmFjdGVyIHZlY3RvciBvZiBsaW5lcyB3aXRoIF9fcmVhZF9saW5lcygpX18sIG9yIGV2ZW4gYSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGxlbmd0aCAxIHdpdGggX19yZWFkX2ZpbGUoKV9fLiBUaGVuIHlvdSBjYW4gdXNlIHRoZSBzdHJpbmcgcGFyc2luZyBza2lsbHMgeW91J2xsIGxlYXJuIGxhdGVyIHRvIHBhcnNlIG1vcmUgZXhvdGljIGZvcm1hdHMuCgojIyAxMS41IFdyaXRpbmcgdG8gYSBmaWxlCgpyZWFkciBhbHNvIGNvbWVzIHdpdGggdHdvIHVzZWZ1bCBmdW5jdGlvbnMgZm9yIHdyaXRpbmcgZGF0YSBiYWNrIHRvIGRpc2s6IF9fd3JpdGVfY3N2KClfXyBhbmQgX193cml0ZV90c3YoKV9fLiBCb3RoIGZ1bmN0aW9ucyBpbmNyZWFzZSB0aGUgY2hhbmNlcyBvZiB0aGUgb3V0cHV0IGZpbGUgYmVpbmcgcmVhZCBiYWNrIGluIGNvcnJlY3RseSBieToKCiogQWx3YXlzIGVuY29kaW5nIHN0cmluZ3MgaW4gVVRGLTguCiogU2F2aW5nIGRhdGVzIGFuZCBkYXRlLXRpbWVzIGluIElTTzg2MDEgZm9ybWF0IHNvIHRoZXkgYXJlIGVhc2lseSBwYXJzZWQgZWxzZXdoZXJlLgoKSWYgeW91IHdhbnQgdG8gZXhwb3J0IGEgY3N2IGZpbGUgdG8gRXhjZWwsIHVzZSBfX3dyaXRlX2V4Y2VsX2NzdigpX18gLSB0aGlzIHdyaXRlcyBhIHNwZWNpYWwgY2hhcmFjdGVyIChhICJieXRlIG9yZGVyIG1hcmsiKSBhdCB0aGUgc3RhcnQgb2YgdGhlIGZpbGUgd2hpY2ggdGVsbHMgRXhjZWwgdGhhdCB5b3UncmUgdXNpbmcgdGhlIFVURi04IGVuY29kaW5nLgoKVGhlIG1vc3QgaW1wb3J0YW50IGFyZ3VtZW50cyBhcmUgX194X18gKHRoZSBkYXRhIGZyYW1lIHRvIHNhdmUpLCBhbmQgX19wYXRoX18gKHRoZSBsb2NhdGlvbiB0byBzYXZlIGl0KS4gWW91IGNhbiBhbHNvIHNwZWNpZnkgaG93IG1pc3NpbmcgdmFsdWVzIGFyZSB3cml0dGVuIHdpdGggX19uYV9fLCBhbmQgaWYgeW91IHdhbnQgdG8gX19hcHBlbmRfXyB0byBhbiBleGlzdGluZyBmaWxlLgoKYGBge3J9CndyaXRlX2NzdihjaGFsbGVuZ2UsICIvVXNlcnMvbWFjZ3l2ZXJqdWFyZXovRGVza3RvcC9SIE5vdGVzIGFuZCBFeGVyY2lzZXMvUiBmb3IgRGF0YSBTY2llbmNlL0NoIDExIC0gRGF0YSBJbXBvcnQvY2hhbGxlbmdlLmNzdiIpCmBgYAoKTm90ZSB0aGF0IHRoZSB0eXBlIGluZm9ybWF0aW9uIGlzIGxvc3Qgd2hlbiB5b3Ugc2F2ZSB0byBjc3Y6CgpgYGB7cn0KY2hhbGxlbmdlCndyaXRlX2NzdihjaGFsbGVuZ2UsICJjaGFsbGVuZ2UtMi5jc3YiKQpyZWFkX2NzdigiY2hhbGxlbmdlLTIuY3N2IikKYGBgCgpIZW5jZSB0aGUgaW1wb3J0YW5jZSBvZiBzcGVjaWZ5aW5nIGNvbHVtbiB0eXBlcyBhbmQgYmVpbmcgc3RyaWN0IGFib3V0IGltcG9ydGluZyBkYXRhLiBJZiB0aGUgc2NyaXB0IGhhcyBiZWVuIHNhdmVkIHRoZW4gZGF0YSBjYW4gZWFzaWx5IGFuZCBjb25zaXN0ZW50bHkgYmUgaW1wb3J0ZWQgaW4uCgpSZWdhcmRsZXNzLCB0aGlzIG1ha2VzIENTVnMgYSBsaXR0bGUgdW5yZWxpYWJsZSBmb3IgY2F0Y2hpbmcgaW50ZXJpbSByZXN1bHRzLXlvdSBuZWVkIHRvIHJlY3JlYXRlIHRoZSBjb2x1bW4gc3BlY2lmaWNhdGlvbiBldmVyeSB0aW1lIHlvdSBsb2FkIGluLiBUaGVyZSBhcmUgdHdvIGFsdGVybmF0aXZlczoKCjEuIF9fd3JpdGVfcmRzKClfXyBhbmQgX19yZWFkX3JkcygpX18gYXJlIHVuaWZvcm0gd3JhcHBlcnMgYXJvdW5kIHRoZSBiYXNlIGZ1bmN0aW9ucyBfX3JlYWRSRFMoKV9fIGFuZCBfX3NhdmVSRFMoKV9fLiBUaGVzZSBzdG9yZSBkYXRhIGluIFIncyBjdXN0b20gYmluYXJ5IGZvcm1hdCBjYWxsZWQgUkRTOgoKYGBge3J9CndyaXRlX3JkcyhjaGFsbGVuZ2UsICJjaGFsbGVuZ2UucmRzIikKcmVhZF9yZHMoImNoYWxsZW5nZS5yZHMiKQpgYGAKCjIuIFRoZSBmZWF0aGVyIHBhY2thZ2UgaW1wbGVtZW50cyBhIGZhc3QgYmluYXJ5IGZpbGUgZm9ybWF0IHRoYXQgY2FuIGJlIHNoYXJlZCBhY3Jvc3MgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzOgoKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoImZlYXRoZXIiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KGZlYXRoZXIpCndyaXRlX2ZlYXRoZXIoY2hhbGxlbmdlLCAiY2hhbGxlbmdlLmZlYXRoZXIiKQpyZWFkX2ZlYXRoZXIoImNoYWxsZW5nZS5mZWF0aGVyIikKYGBgCgpGZWF0aGVyIHRlbmRzIHRvIGJlIGZhc3RlciB0aGFuIFJEUyBhbmQgaXMgdXNhYmxlIG91dHNpZGUgb2YgUi4gUkRTIHN1cHBvcnRzIGxpc3QtY29sdW1ucyAod2hpY2ggeW91J2xsIGxlYXJuIGFib3V0IGluIF9fbWFueSBtb2RlbHNfXyk7IGZlYXRoZXIgY3VycmVudGx5IGRvZXMgbm90LgoKIyMgMTEuNiBPdGhlciB0eXBlcyBvZiBkYXRhCgpUbyBnZXQgb3RoZXIgdHlwZXMgb2YgZGF0YSBpbnRvIFIsIHdlIHJlY29tbWVuZCBzdGFydGluZyB3aXRoIHRoZSB0aWR5dmVyc2UgcGFja2FnZXMgbGlzdGVkIGJlbG93LiBUaGV5J3JlIGNlcnRhaW5seSBub3QgcGVyZmVjdCwgYnV0IHRoZXkgYXJlIGEgZ29vZCBwbGFjZSB0byBzdGFydC4gRm9yIHJlY3Rhbmd1bGFyIGRhdGE6CgoqIF9faGF2ZW5fXyByZWFkcyBTUFNTLCBTdGF0YSwgYW5kIFNBUyBmaWxlcy4KKiBfX3JlYWR4bF9fIHJlYWRzIGV4Y2VsIGZpbGVzIChib3RoIF9fLnhsc19fIGFuZCBfXy54bHN4X18pLgoqIF9fREJJX18sIGFsb25nIHdpdGggYSBkYXRhYmFzZSBzcGVjaWZpYyBiYWNrZW5kIChlLmcuIF9fUk1ZU1FMX18sIF9fUlNRTElURV9fLCBfX1JQb3N0Z3JlU1FMX18gZXRjKSBhbGxvd3MgeW91IHRvIHJ1biBTUUwgcXVlcmllcyBhZ2FpbnN0IGEgZGF0YWJhc2UgYW5kIHJldHVybiBhIGRhdGEgZnJhbWUuCgpGb3IgaGllcmFyY2hpY2FsIGRhdGE6IHVzZSBfX2pzb25saXRlX18gKGJ5IEplcm9lbiBPb29tcykgZm9yIGpzb24sIGFuZCBfX3htbDJfXyBmb3IgWE1MLiBKZW5ueSBCcnlhbiBoYXMgc29tZSBleGNlbGxlbnQgd29ya2VkIGV4YW1wbGVzIGF0IGh0dHBzOi8vamVubnliYy5naXRodWIuaW8vcHVycnItdHV0b3JpYWwvLAoKRm9yIG90aGVyIGZpbGUgdHlwZXMsIHRyeSB0aGUgX19SIGRhdGEgaW1wb3J0L2V4cG9ydCBtYW51YWxfXyBhbmQgdGhlIF9fcmlvX18gcGFja2FnZS4KCgoKCg==