Author: Kayla Keyue Chen
Last updated: 2024.4.18
Learning objectives
Get familiar with R interface: (1) Create, save, and open a
script. (2) Run your code. (3) Set working directory.
Learn how to install and load packages. We will be using
“tidyverse” a lot because it offers many handy functions, and it also
contains many other useful packages so we don’t need to install and load
them one by one.
Differentiate object and function. Learn how to create and
manipulate scalars, vectors, and dataframes in R. Use the environment to
overview all the objects you have. Read the Console for effects of your
code.
Get familiar with R interface
Create, save, and open a script
Create a script: File -> New File -> R Script. In
the Untitled1 file, type 1+2
in your script so we will have
something to run later.
Save: There are different ways to save a script: (1) File
-> Save. (2) Click on the floppy disk icon. (3) Short cut:
Ctrl/Command + S. If it is the first time you save the script, you need
to give it a name.
Open an existing script: You can open a script from
RStudio interface or directly in your folder. (1) To open a script in
RStudio, File -> Open File -> choose the one you want to open. (2)
To open a script in your folder, you just need to double click on
it
Run your code
Run a line: Move the cursor to the line you want to run,
and press Ctrl/Command + Enter and R will run the current line and
automatically move the cursor to the next line.
Run multiple lines: Highlight the lines you want to run,
and press Ctrl/Command + Enter.
Run the entire script: First select all lines by pressing
Ctrl/Command + A, then run the code by pressing Ctrl/Command +
Enter.
You can learn more keyboard shortcuts in the RStudio from their online
support.
You can also click ‘Run’ in the top right corner of the Source window
to run selected lines.
Set working directory
It’s very useful to set working directory at the beginning of your
script so that R will know where to load and save your files. There are
several ways to set working directory.
- You can use
setwd()
to set the directory and
getwd()
to know the current directory. Note: you need to
use forward slash /
.
# set working directory
setwd("D:/PLINSTAT R for linguists/2024 materials/week1")
# get working directory
getwd()
You can also set working directory manually, from Session ->
Set Working Directory -> Choose Directory … -> choose the folder
you want to use in the pop-up window.
If you have saved the script in the folder that you want to set
as working directory, you can also use Session -> Set Working
Directory -> To Source File Location.
If you have opened the folder in the Files pane (the right bottem
pane, Files tab), you can use Session -> Set Working Directory ->
To Files Pane Location.
Install and load packages
An R package is simply a bunch of data (functions, help menus) stored
in one neat package. Different packages have different functions that
can be very useful and save a lot of trouble! You need to install (only
for the first time) and load the package if you want to use the
functions offered by the package.
Install packages using install.packages("name")
where
“name” is the name of the package. Note that you need to use quotation
marks around the package name.
Load a package using library(name)
where “name” is the
name of the package. Here quotation marks are not needed.
# install tidyverse (you only need to install a package once)
install.packages("tidyverse")
# load tidyverse (you need to do this every time you open R)
library(tidyverse)
Create and manipulate objects in R
Objects and functions
Imagine we have a bunch of values, e.g. 2, 4, 6, 9, 13, 25. These are
our objects. Now imagine we want to combine these objects into one
sequence (this is called a vector) To combine objects into a
vector, we use the ‘combine’ function c()
. It looks like
this c(x, y, z)
- where c()
is our function
and x, y, z are placeholders for our objects. You can put as many
objects into this function as you want.
# Now try the function c(x, y, z, ...) to combine 2, 4, 6, 9, 13, 25
c(2, 4, 6, 9, 13, 25)
[1] 2 4 6 9 13 25
# Check the Console: What happened? -- the code returns 2 4 6 9 13 25
# Check the Environment: Did anything happen? -- nothing
Creating and saving objects
Now let’s look at how to create and save an object. This allows you
to save them to your working space under a name. To create and save an
object you:
Type the name you want to give your object (e.g. pizza)
Type this symbol: <-
Type the function for the object you wish to create, e.g.,
c(2, 4, 6, 9, 13, 25)
Press Ctrl/Command + Enter to run the code
After you run the code, you will see an object called pizza in the
Environment pane under the Values section. It has “num [1:6] 2 4 6 9 13
25” in the second column. “num” means that this vector contains numeric
data. “[1:6]” shows that there are 6 numbers in this vector, and then it
shows the first several numbers for a preview.
# Now type the code to create the object pizza
pizza <- c(2, 4, 6, 9, 13, 25)
# Check the Console: What happened? -- the code didn't return the values
# Check the Environment: Did anything happen? -- new object pizza in the Values section
# Now, call your object by typing the name, then Ctrl/Command + Enter
pizza
[1] 2 4 6 9 13 25
# Check the Environment: What do you see? -- the code returns the values 2 4 6 9 13 25
Scalars and operations
Numeric scalar
Scalar is atomic quantity that can hold only one value at a time. We
can create numerical scalars. For example, let’s create a scalar called
x with the value 4 and another scalar called y with the value 7.
# Create x
x <- 4
# Create y
y <- 7
# Check your Environment!
We can check what kind of scalar a given object is by using the
class()
function. For example: class(x)
will
return the the type of the object x, which is “numeric”.
# check the class of x
class(x)
[1] "numeric"
# what kind of scalar is x?
We can perform arithmetic operations on our scalars, e.g., adding
(+), subtracting (-), multiplying (*), dividing (/), squaring (use ^
followed by a number, e.g., ^2), taking the root (use the function
sqrt()
)
# try the following arithmetic operations, use any number you like
# adding
2+4
[1] 6
# subtracting
1-100
[1] -99
# multiplying
4*23
[1] 92
# dividing
88/8
[1] 11
# squaring
5^3
[1] 125
# taking the root
sqrt(10)
[1] 3.162278
# combination of different operations
sqrt((1+3)/2*9)^2
[1] 18
Logical scalar
We can also create logical scalars (i.e. TRUE and FALSE). For example
let’s create a scalar m that is defined as x > y, and a scalar n that
is defined as x < y, and a scalar p that is defined as x = y.
=
is expressed as ==
in R language. Note: x
and y are defined as the values we assigned to them earlier (x=4, and
y=7).
# Define m
m <- x>y
# Define n
n <- x<y
# Define p
p <- x==y
# Check the Environment: what are the values of m, n, and p?
# m is FALSE, n is TRUE, and p is FALSE
# What kind of scalar is m? (hint: use class() function)
class(m)
[1] "logical"
Logical operators include AND and OR. For example: x > y and x
< y can be written as x > y & x < y
or
m & n
; x > y or x < y can be written as
x > y | x < y
or m | n
.
# Evaluate the code given as examples in the instruction
x > y & x < y
[1] FALSE
m & n
[1] FALSE
Character scalar
We can make character/string scalars. We must use quotation
marks (either single or double) to indicate that they are
characters. Create a character hello world
and save it to
an object named mystring
.
# Create mystring
mystring <- "hello world"
# what kind of scalar is 'mystring'? (hint: use class() function)
class(mystring)
[1] "character"
We can’t use operations on character scalars. Note that when a number
is surrounded by quotation marks, it becomes a character. For example, 1
is a number, “1” is a character. You can use class()
function to test if this is true.
# Try this
string1 <- "1" # ATTENTION: This is a character scalar because we put quotation marks!
string2 <- "2"
# Try adding them and see what happens :)
string1 + string2 # Error in string1 + string2 : non-numeric argument to binary operator
Vectors and operations
Create a vector
We can combine scalars into larger objects of the same data type:
These are called vectors. Use c()
function which has been
introduced earlier.
# creates vector a which contains values 1,2,3,4 (hint: a <- c(x,y,z,...))
a <- c(1,2,3,4)
# creates vector b containing numbers from 1 to 10
b <- c(1,2,3,4,5,6,7,8,9,10)
Two useful functions to create scalar vectors with some rules.
To create continuous integer numbers you can use :
within c()
function. For example, c(1:10)
returns numbers from 1 to 10.
seq(from, to, by)
creates vector with sequence of
values. “from” defines the starting point, “to” defines the ending
point, and “by” defines the step. For example,
seq(from=1,to=5,by=1)
creates a vector containing
1,2,3,4,5.
rep(x, times, each)
creates vector with values
repeated. “x” defines the vector to be repeated, “times” defines the
number of repetition of the entire vector, and “each” defines the number
of repetition of each element in the vector. For example,
`rep(c(1,2,3),times=2,each=2)`` creates a new vector containing
1,1,2,2,3,3,1,1,2,2,3,3.
# generate numbers from 1 to 100 using c()
c(1:100)
[1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
[19] 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
[37] 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
[55] 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
[73] 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
[91] 91 92 93 94 95 96 97 98 99 100
# create vector c with sequence of values from 1, to 20, by 2
c <- seq(from=1,to=20,by=2)
# create vector d with vector (2,4,6) repeated 2 times, with each element repeated 3 times
d <- rep(c(2,4,6),times=2,each=3)
Arithmetic operations with vectors
# Make a simple numeric vector (e)
e <- c(1,2,3)
# Make another one (f)
f <- c(5,7,9)
# Make another one (g)
g <- c(5,7,9,11)
# Check the Environment to see the length of the vectors
# e and f have 3 numbers, g has 4 numbers
We can do arithmetic operations on vectors too! If we want to do
operations between two vectors, they must have the same length, or one
is a multiple of the other.
# adds 6 to each value in e
e+6
[1] 7 8 9
# squaring each value of f
f^2
[1] 25 49 81
# adding e and f (each corresponding value in the sequence is summed)
e+f
[1] 6 9 12
# multiplying e and f (each corresponding value in the sequence is multiplied)
e*f
[1] 5 14 27
# Note: If you want to save these results to your Environment as vectors, you need to give them a name
# Now, try adding e and g, what happened?
e+g # returns Warning: longer object length is not a multiple of shorter object length[1] 6 9 12 12
Logical operations with vectors
We can also do logical operations with vectors. For example, we can
evaluate whether each item in the vector is larger than 10 by
vector_name > 10
.
# remember that we have vector b which contains integer numbers from 1 to 10
# find out whether each item in vector b is larger than 6
b > 6
[1] FALSE FALSE FALSE FALSE FALSE FALSE TRUE TRUE TRUE TRUE
# find out whether each item in vector b is larger than 5 AND smaller than 8
b > 5 & b < 8
[1] FALSE FALSE FALSE FALSE FALSE TRUE TRUE FALSE FALSE FALSE
The %in%
operation allows you to combine multiple OR
comparisons to check whether given values are present in a set. For
example: You have a vector that describes favourite letters
fave.letters <- c('a', 't', 'a', 'b', 'z')
. To check
whether the each letter in fave.letters is one of ‘a’, ‘b’ or ‘c’, you
can type: fave.letters %in% c('a', 'b', 'c')
. The results
will be TRUE FALSE TRUE TRUE FALSE, i.e., only the 2nd letter ‘t’ and
the 5th letter ‘z’ are not one of ‘a’, ‘b’ or ‘c’.
This is helpful when you want to filter your participants based on
their responses. For example, only keep participants whose favourite
letter is one of ‘a’, ‘b’ or ‘c’.
# find out whether each item in vector b is in the set 1,7,13
b %in% c(1,7,13)
[1] TRUE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
# find out whether each item in vector b is in the vector a
b %in% a
[1] TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE FALSE FALSE
Note that because FALSE = 0 and TRUE = 1, we can sum()
the logical vectors to find out the number of items that satisfy the
operation, and use mean()
to find out the proportion of
items that satisfy the operation.
# find out the number of items in vector b that are larger than 6
sum(b > 6)
[1] 4
# find out the number of items in vector b that are in the set 3,5,0
sum(b %in% c(3,5,0))
[1] 2
# find out the proportion of items in vector b are larger than 5 AND smaller than 8
mean(b > 5 & b < 8)
[1] 0.2
Indexing a vector
You can ask R to return the values of specific items within your
vectors by typing the vector name followed by []
, and put
the order number of the item into []
. For example, to get
the 2nd item in vector a, you write a[2]
. To return
multiple items, you can use c()
to combine all indices you
are interested in.
# remember that we have vector b which contains integer numbers from 1 to 10
# Get the 3rd item in vector b
b[3]
[1] 3
# Get the 2nd and 4th item in vector b
b[c(2,4)]
[1] 2 4
# Get the 2nd to 8th item in vector b
b[c(2:8)]
[1] 2 3 4 5 6 7 8
We can also use logical operations inside indexing []
.
For example, if we want to know what items in a vector is smaller than
5, we can write vector_name[vector_name < 5]
. This is
because vector_name < 5
returns a vector of logical
values (i.e., FALSEs & TRUEs), and then indexing []
will return the items that have TRUEs.
which()
function can return the indices where the value
is TRUE. We can write which(vector_name < 5)
to return
the indices of numbers which is smaller than 5.
# find out what items in vector b are smaller than 4
b[b<4]
[1] 1 2 3
# find out what items in vector b are larger than 4 but smaller than 6
b[b>4 & b<6]
[1] 5
# find out the indices of items in vector b are smaller than 4
which(b<4) # note that because b contains integer numbers from 1 to 10, the indices are the same as number values.
[1] 1 2 3
Dataframes
To create a dataframe, you need to specify column names and values in
each column. For example, let’s create a dataframe called
df1
with two columns, “sex” and “age”. We have 3 males and
they are 99, 46, 23 years old, and 2 females and they are 54, 23 years
old. Use data.frame()
function, each argument will be in
the format “column name = c(value1, value2, value3, …)”, e.g., “age =
c(99,46,23,54,23)”.
# Create df1 using the data.frame() function
df1 <- data.frame(sex = c("male","male","male","female","female"),
age = c(99,46,23,54,23))
# Now call on your dataframe: what does R return?
df1
sex age
1 male 99
2 male 46
3 male 23
4 female 54
5 female 23
Your column/vector names are your variables, you call on specific
variables within a dataframe with the dollar symbol $
,
e.g., df1$sex
(this means you extract a vector from
the dataframe!).
# Extract column age and sex
df1$sex
[1] "male" "male" "male" "female" "female"
df1$age
[1] 99 46 23 54 23
To get column names, you can use colnames(dataframe)
function. You can change a specified column name using function
rename(dataframe, new name = old name)
(Note:
rename()
is provided by tidyverse
package.
Remember to load the package using library()
first!).
Another option is to use
colnames(dataframe) <- c("xxx", "xxx", ...)
to change
ALL the column names at once.
#library(tidyverse) if you haven't
# Get the column names of df1
colnames(df1)
[1] "sex" "age"
# change column name "sex" to "gender" using rename()
rename(df1, "gender" = "sex")
gender age
1 male 99
2 male 46
3 male 23
4 female 54
5 female 23
# change column names to "GENDER" and "AGE" using colnames()
colnames(df1) <- c("GENDER", "AGE")
# call on df1 to see the change
df1
GENDER AGE
1 male 99
2 male 46
3 male 23
4 female 54
5 female 23
Factor variable
We can set the sex column to a factor variable using
as.factor()
function. If sex is set to a factor variable,
it is categorical and new observations can only be one of the specified
levels. The levels are by default arranged by alphabetical order. You
can manually specify the order using function factor()
(no
“as”!) and argument levels = c("xxx", "xxx", ...)
.
Remember you must give the results back to the object to save the
change! For example, a+1
will add 1 to the number
scalar a
, but the value of a
won’t change,
whereas a <- a+1
will update the value of
a
.
# Turn GENDER column into a factor variable using as.factor()
df1$GENDER <- as.factor(df1$GENDER)
# Set male to be level1 and female to be level2 using factor()
df1$GENDER <- factor(df1$GENDER, levels = c("male", "female"))
# Call on the GENDER column and see what levels are there in the factor (hint: use the dollor symbol $)
df1$GENDER
[1] male male male female female
Levels: male female
# Add a 6th observation to the column. Can you add "female"? Can you add "other"?
# first we extract the vector GENDER <- df1$GENDER
# then try GENDER[6] <- "female" and then GENDER[6] <- "other", What happened?
GENDER <- df1$GENDER
GENDER[6] <- "female" # works fine
GENDER[6] <- "other" # returns Warning: invalid factor level, NA generated, "other" is replaced with <NA>
Let’s practice !
Create a vector for each of the columns in the table (see the
last slide for Lab1)
Tip 1: use the combine function c()
to create the
vectors
Tip 2: assign vectors to column names, e.g., column_name =
c(value1, value2, …)
lang_name <- c('Mandarin', 'Spanish', 'English', 'Hindi', 'Portuguese', 'Bengali', 'Russian', 'Japanese')
lang_order <- c('SVO', 'SVO', 'SVO', 'SOV', 'SVO', 'SOV', 'SVO', 'SOV')
lang_popL1 <- c(921.2, 471.4, 369.9, 342.2, 232.4, 228.7, 153.7, 126.3)
lang_popL2 <- c(198.7, 71.5, 978.2, 258.3, 25.2, 39.0, 104.3, 0.12)
lang_indoeuro <- c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE)
Combine the vectors into a dataframe
lang_df <- data.frame(lang_name, lang_order, lang_popL1, lang_popL2,lang_indoeuro)
Have a look at the dataframe
# Call on the entire dataframe
lang_df
lang_name lang_order lang_popL1 lang_popL2 lang_indoeuro
1 Mandarin SVO 921.2 198.70 FALSE
2 Spanish SVO 471.4 71.50 TRUE
3 English SVO 369.9 978.20 TRUE
4 Hindi SOV 342.2 258.30 TRUE
5 Portuguese SVO 232.4 25.20 TRUE
6 Bengali SOV 228.7 39.00 TRUE
7 Russian SVO 153.7 104.30 TRUE
8 Japanese SOV 126.3 0.12 FALSE
# Call on specific columns (vectors) in your dataframe
lang_df$lang_name
[1] "Mandarin" "Spanish" "English" "Hindi" "Portuguese"
[6] "Bengali" "Russian" "Japanese"
lang_df$lang_popL1
[1] 921.2 471.4 369.9 342.2 232.4 228.7 153.7 126.3
# return all column names
colnames(lang_df)
[1] "lang_name" "lang_order" "lang_popL1" "lang_popL2"
[5] "lang_indoeuro"
- Try executing the following functions on your dataframe and see what
the functions return.
# head(df_name)
head(lang_df) # returns first several rows
lang_name lang_order lang_popL1 lang_popL2 lang_indoeuro
1 Mandarin SVO 921.2 198.7 FALSE
2 Spanish SVO 471.4 71.5 TRUE
3 English SVO 369.9 978.2 TRUE
4 Hindi SOV 342.2 258.3 TRUE
5 Portuguese SVO 232.4 25.2 TRUE
6 Bengali SOV 228.7 39.0 TRUE
# tail(df_name)
tail(lang_df) # returns last several rows
lang_name lang_order lang_popL1 lang_popL2 lang_indoeuro
3 English SVO 369.9 978.20 TRUE
4 Hindi SOV 342.2 258.30 TRUE
5 Portuguese SVO 232.4 25.20 TRUE
6 Bengali SOV 228.7 39.00 TRUE
7 Russian SVO 153.7 104.30 TRUE
8 Japanese SOV 126.3 0.12 FALSE
# View(df_name) N.B. the 'V' in View is capitalised
View(lang_df) # this will open a new tab in the Source pane and show the table of lang_df
# nrow(df_name)
nrow(lang_df) # returns total number of rows
[1] 8
# ncol(df_name)
ncol(lang_df) # returns total number of columns
[1] 5
# dim(df_name)
dim(lang_df) # returns total number of rows and columns
[1] 8 5
# summary(df_name)
summary(lang_df) # returns a summary of values in each column
lang_name lang_order lang_popL1 lang_popL2
Length:8 Length:8 Min. :126.3 Min. : 0.12
Class :character Class :character 1st Qu.:209.9 1st Qu.: 35.55
Mode :character Mode :character Median :287.3 Median : 87.90
Mean :355.7 Mean :209.41
3rd Qu.:395.3 3rd Qu.:213.60
Max. :921.2 Max. :978.20
lang_indoeuro
Mode :logical
FALSE:2
TRUE :6
# What do each of these functions do?
- Indexing a vector (column) in dataframe
# What is the L1 language population of the first language (ie, Mandarin)?
lang_df$lang_popL1[1]
[1] 921.2
# What are the word orders of the first 5 languages?
lang_df$lang_order[1:5]
[1] "SVO" "SVO" "SVO" "SOV" "SVO"
# Whether each L1 language population are greater than 300 million?
lang_df$lang_popL1 > 300
[1] TRUE TRUE TRUE TRUE FALSE FALSE FALSE FALSE
# Whether each language orders are SVO?
lang_df$lang_order == "SVO"
[1] TRUE TRUE TRUE FALSE TRUE FALSE TRUE FALSE
# What are the names of languages with L2 population lower than 100 million?
lang_df$lang_name[lang_df$lang_popL2 < 100]
[1] "Spanish" "Portuguese" "Bengali" "Japanese"
# What are the names of languages with SVO order and L2 language population more than 100 million?
lang_df$lang_name[lang_df$lang_order == "SVO" & lang_df$lang_popL2 > 100]
[1] "Mandarin" "English" "Russian"
- Counting the number of observations satisfying a specific
criteria
# How many languages have SVO order? What is the proportion?
sum(lang_df$lang_order == "SVO")
[1] 5
mean(lang_df$lang_order == "SVO")
[1] 0.625
# How many languages have L2 population greater than 200 million? What is the proportion?
sum(lang_df$lang_popL2 > 200)
[1] 2
mean(lang_df$lang_popL2 > 200)
[1] 0.25
LS0tDQp0aXRsZTogIlBMSU5TVEFUIGxhYiAxOiBHZXR0aW5nIGZhbWlsaWFyIHdpdGggUiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KcGFyYW1zOg0KICBmbGV4OiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojc3R1ZGVudHM6IHRoaXMgaXMgdGhlIHNldCB1cCBjaHVuaywgaXQgY2FuIGJlIGlnbm9yZWQNCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjb21tZW50ID0gIiIpDQpsaWJyYXJ5KGtuaXRyKQ0KYGBgDQoNCkF1dGhvcjogS2F5bGEgS2V5dWUgQ2hlbg0KDQpMYXN0IHVwZGF0ZWQ6IDIwMjQuNC4xOA0KDQojIyBMZWFybmluZyBvYmplY3RpdmVzIA0KDQoqIEdldCBmYW1pbGlhciB3aXRoIFIgaW50ZXJmYWNlOiAoMSkgQ3JlYXRlLCBzYXZlLCBhbmQgb3BlbiBhIHNjcmlwdC4gKDIpIFJ1biB5b3VyIGNvZGUuICgzKSBTZXQgd29ya2luZyBkaXJlY3RvcnkuIA0KDQoqIExlYXJuIGhvdyB0byBpbnN0YWxsIGFuZCBsb2FkIHBhY2thZ2VzLiBXZSB3aWxsIGJlIHVzaW5nICJ0aWR5dmVyc2UiIGEgbG90IGJlY2F1c2UgaXQgb2ZmZXJzIG1hbnkgaGFuZHkgZnVuY3Rpb25zLCBhbmQgaXQgYWxzbyBjb250YWlucyBtYW55IG90aGVyIHVzZWZ1bCBwYWNrYWdlcyBzbyB3ZSBkb24ndCBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlbSBvbmUgYnkgb25lLiANCg0KKiBEaWZmZXJlbnRpYXRlIG9iamVjdCBhbmQgZnVuY3Rpb24uIExlYXJuIGhvdyB0byBjcmVhdGUgYW5kIG1hbmlwdWxhdGUgc2NhbGFycywgdmVjdG9ycywgYW5kIGRhdGFmcmFtZXMgaW4gUi4gVXNlIHRoZSBlbnZpcm9ubWVudCB0byBvdmVydmlldyBhbGwgdGhlIG9iamVjdHMgeW91IGhhdmUuIFJlYWQgdGhlIENvbnNvbGUgZm9yIGVmZmVjdHMgb2YgeW91ciBjb2RlLiANCg0KIyMgR2V0IGZhbWlsaWFyIHdpdGggUiBpbnRlcmZhY2UNCg0KIyMjIENyZWF0ZSwgc2F2ZSwgYW5kIG9wZW4gYSBzY3JpcHQNCg0KMS4gPGI+Q3JlYXRlIGEgc2NyaXB0PC9iPjogRmlsZSAtPiBOZXcgRmlsZSAtPiBSIFNjcmlwdC4gSW4gdGhlIFVudGl0bGVkMSBmaWxlLCB0eXBlIGAxKzJgIGluIHlvdXIgc2NyaXB0IHNvIHdlIHdpbGwgaGF2ZSBzb21ldGhpbmcgdG8gcnVuIGxhdGVyLg0KDQoyLiA8Yj5TYXZlPC9iPjogVGhlcmUgYXJlIGRpZmZlcmVudCB3YXlzIHRvIHNhdmUgYSBzY3JpcHQ6ICgxKSBGaWxlIC0+IFNhdmUuICgyKSBDbGljayBvbiB0aGUgZmxvcHB5IGRpc2sgaWNvbi4gKDMpIFNob3J0IGN1dDogQ3RybC9Db21tYW5kICsgUy4gSWYgaXQgaXMgdGhlIGZpcnN0IHRpbWUgeW91IHNhdmUgdGhlIHNjcmlwdCwgeW91IG5lZWQgdG8gZ2l2ZSBpdCBhIG5hbWUuIA0KDQozLiA8Yj5PcGVuIGFuIGV4aXN0aW5nIHNjcmlwdDwvYj46IFlvdSBjYW4gb3BlbiBhIHNjcmlwdCBmcm9tIFJTdHVkaW8gaW50ZXJmYWNlIG9yIGRpcmVjdGx5IGluIHlvdXIgZm9sZGVyLiAoMSkgVG8gb3BlbiBhIHNjcmlwdCBpbiBSU3R1ZGlvLCBGaWxlIC0+IE9wZW4gRmlsZSAtPiBjaG9vc2UgdGhlIG9uZSB5b3Ugd2FudCB0byBvcGVuLiAoMikgVG8gb3BlbiBhIHNjcmlwdCBpbiB5b3VyIGZvbGRlciwgeW91IGp1c3QgbmVlZCB0byBkb3VibGUgY2xpY2sgb24gaXQgDQoNCiMjIyBSdW4geW91ciBjb2RlDQoNCjEuIDxiPlJ1biBhIGxpbmU8L2I+OiBNb3ZlIHRoZSBjdXJzb3IgdG8gdGhlIGxpbmUgeW91IHdhbnQgdG8gcnVuLCBhbmQgcHJlc3MgQ3RybC9Db21tYW5kICsgRW50ZXIgYW5kIFIgd2lsbCBydW4gdGhlIGN1cnJlbnQgbGluZSBhbmQgYXV0b21hdGljYWxseSBtb3ZlIHRoZSBjdXJzb3IgdG8gdGhlIG5leHQgbGluZS4gDQoNCjIuIDxiPlJ1biBtdWx0aXBsZSBsaW5lczwvYj46IEhpZ2hsaWdodCB0aGUgbGluZXMgeW91IHdhbnQgdG8gcnVuLCBhbmQgcHJlc3MgQ3RybC9Db21tYW5kICsgRW50ZXIuIA0KDQozLiA8Yj5SdW4gdGhlIGVudGlyZSBzY3JpcHQ8L2I+OiBGaXJzdCBzZWxlY3QgYWxsIGxpbmVzIGJ5IHByZXNzaW5nIEN0cmwvQ29tbWFuZCArIEEsIHRoZW4gcnVuIHRoZSBjb2RlIGJ5IHByZXNzaW5nIEN0cmwvQ29tbWFuZCArIEVudGVyLiANCg0KWW91IGNhbiBsZWFybiBtb3JlIGtleWJvYXJkIHNob3J0Y3V0cyBpbiB0aGUgUlN0dWRpbyBmcm9tIHRoZWlyIFtvbmxpbmUgc3VwcG9ydF0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDcxMTg1My1LZXlib2FyZC1TaG9ydGN1dHMtaW4tdGhlLVJTdHVkaW8tSURFKS4NCg0KWW91IGNhbiBhbHNvIGNsaWNrIOKAmFJ1buKAmSBpbiB0aGUgdG9wIHJpZ2h0IGNvcm5lciBvZiB0aGUgU291cmNlIHdpbmRvdyB0byBydW4gc2VsZWN0ZWQgbGluZXMuIA0KDQojIyMgU2V0IHdvcmtpbmcgZGlyZWN0b3J5DQoNCkl0J3MgdmVyeSB1c2VmdWwgdG8gc2V0IHdvcmtpbmcgZGlyZWN0b3J5IGF0IHRoZSBiZWdpbm5pbmcgb2YgeW91ciBzY3JpcHQgc28gdGhhdCBSIHdpbGwga25vdyB3aGVyZSB0byBsb2FkIGFuZCBzYXZlIHlvdXIgZmlsZXMuIFRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gc2V0IHdvcmtpbmcgZGlyZWN0b3J5LiANCg0KMS4gWW91IGNhbiB1c2UgYHNldHdkKClgIHRvIHNldCB0aGUgZGlyZWN0b3J5IGFuZCBgZ2V0d2QoKWAgdG8ga25vdyB0aGUgY3VycmVudCBkaXJlY3RvcnkuIE5vdGU6IHlvdSBuZWVkIHRvIHVzZSBmb3J3YXJkIHNsYXNoIGAvYC4NCg0KYGBge3IgZXZhbD1GQUxTRX0NCiMgc2V0IHdvcmtpbmcgZGlyZWN0b3J5DQpzZXR3ZCgiRDovUExJTlNUQVQgUiBmb3IgbGluZ3Vpc3RzLzIwMjQgbWF0ZXJpYWxzL3dlZWsxIikNCiMgZ2V0IHdvcmtpbmcgZGlyZWN0b3J5DQpnZXR3ZCgpDQpgYGANCg0KMi4gWW91IGNhbiBhbHNvIHNldCB3b3JraW5nIGRpcmVjdG9yeSBtYW51YWxseSwgZnJvbSBTZXNzaW9uIC0+IFNldCBXb3JraW5nIERpcmVjdG9yeSAtPiBDaG9vc2UgRGlyZWN0b3J5IC4uLiAtPiBjaG9vc2UgdGhlIGZvbGRlciB5b3Ugd2FudCB0byB1c2UgaW4gdGhlIHBvcC11cCB3aW5kb3cuIA0KDQozLiBJZiB5b3UgaGF2ZSBzYXZlZCB0aGUgc2NyaXB0IGluIHRoZSBmb2xkZXIgdGhhdCB5b3Ugd2FudCB0byBzZXQgYXMgd29ya2luZyBkaXJlY3RvcnksIHlvdSBjYW4gYWxzbyB1c2UgU2Vzc2lvbiAtPiBTZXQgV29ya2luZyBEaXJlY3RvcnkgLT4gVG8gU291cmNlIEZpbGUgTG9jYXRpb24uIA0KDQo0LiBJZiB5b3UgaGF2ZSBvcGVuZWQgdGhlIGZvbGRlciBpbiB0aGUgRmlsZXMgcGFuZSAodGhlIHJpZ2h0IGJvdHRlbSBwYW5lLCBGaWxlcyB0YWIpLCB5b3UgY2FuIHVzZSBTZXNzaW9uIC0+IFNldCBXb3JraW5nIERpcmVjdG9yeSAtPiBUbyBGaWxlcyBQYW5lIExvY2F0aW9uLiAgDQoNCiMjIEluc3RhbGwgYW5kIGxvYWQgcGFja2FnZXMNCg0KQW4gUiBwYWNrYWdlIGlzIHNpbXBseSBhIGJ1bmNoIG9mIGRhdGEgKGZ1bmN0aW9ucywgaGVscCBtZW51cykgc3RvcmVkIGluIG9uZSBuZWF0IHBhY2thZ2UuIERpZmZlcmVudCBwYWNrYWdlcyBoYXZlIGRpZmZlcmVudCBmdW5jdGlvbnMgdGhhdCBjYW4gYmUgdmVyeSB1c2VmdWwgYW5kIHNhdmUgYSBsb3Qgb2YgdHJvdWJsZSEgWW91IG5lZWQgdG8gaW5zdGFsbCAob25seSBmb3IgdGhlIGZpcnN0IHRpbWUpIGFuZCBsb2FkIHRoZSBwYWNrYWdlIGlmIHlvdSB3YW50IHRvIHVzZSB0aGUgZnVuY3Rpb25zIG9mZmVyZWQgYnkgdGhlIHBhY2thZ2UuIA0KDQpJbnN0YWxsIHBhY2thZ2VzIHVzaW5nIGBpbnN0YWxsLnBhY2thZ2VzKCJuYW1lIilgIHdoZXJlIOKAnG5hbWXigJ0gaXMgdGhlIG5hbWUgb2YgdGhlIHBhY2thZ2UuIE5vdGUgdGhhdCB5b3UgbmVlZCB0byB1c2UgcXVvdGF0aW9uIG1hcmtzIGFyb3VuZCB0aGUgcGFja2FnZSBuYW1lLiANCg0KTG9hZCBhIHBhY2thZ2UgdXNpbmcgYGxpYnJhcnkobmFtZSlgIHdoZXJlIOKAnG5hbWXigJ0gaXMgdGhlIG5hbWUgb2YgdGhlIHBhY2thZ2UuIEhlcmUgcXVvdGF0aW9uIG1hcmtzIGFyZSBub3QgbmVlZGVkLiANCg0KYGBge3IgZXZhbD1GQUxTRX0NCiMgaW5zdGFsbCB0aWR5dmVyc2UgKHlvdSBvbmx5IG5lZWQgdG8gaW5zdGFsbCBhIHBhY2thZ2Ugb25jZSkNCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpgYGANCg0KYGBge3J9DQojIGxvYWQgdGlkeXZlcnNlICh5b3UgbmVlZCB0byBkbyB0aGlzIGV2ZXJ5IHRpbWUgeW91IG9wZW4gUikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCiMjIENyZWF0ZSBhbmQgbWFuaXB1bGF0ZSBvYmplY3RzIGluIFINCg0KIyMjIE9iamVjdHMgYW5kIGZ1bmN0aW9ucw0KDQpJbWFnaW5lIHdlIGhhdmUgYSBidW5jaCBvZiB2YWx1ZXMsIGUuZy4gIDIsIDQsIDYsIDksIDEzLCAyNS4gVGhlc2UgYXJlIG91ciBvYmplY3RzLiBOb3cgaW1hZ2luZSB3ZSB3YW50IHRvIGNvbWJpbmUgdGhlc2Ugb2JqZWN0cyBpbnRvIG9uZSBzZXF1ZW5jZSAodGhpcyBpcyBjYWxsZWQgYSA8Yj52ZWN0b3I8L2I+KSBUbyBjb21iaW5lIG9iamVjdHMgaW50byBhIHZlY3Rvciwgd2UgdXNlIHRoZSAnY29tYmluZScgZnVuY3Rpb24gYGMoKWAuIEl0IGxvb2tzIGxpa2UgdGhpcyBgYyh4LCB5LCB6KWAgLSB3aGVyZSBgYygpYCBpcyBvdXIgZnVuY3Rpb24gYW5kIHgsIHksIHogYXJlIHBsYWNlaG9sZGVycyBmb3Igb3VyIG9iamVjdHMuIFlvdSBjYW4gcHV0IGFzIG1hbnkgb2JqZWN0cyBpbnRvIHRoaXMgZnVuY3Rpb24gYXMgeW91IHdhbnQuIA0KDQpgYGB7cn0NCiMgTm93IHRyeSB0aGUgZnVuY3Rpb24gYyh4LCB5LCB6LCAuLi4pIHRvIGNvbWJpbmUgMiwgNCwgNiwgOSwgMTMsIDI1DQpjKDIsIDQsIDYsIDksIDEzLCAyNSkNCiMgQ2hlY2sgdGhlIENvbnNvbGU6IFdoYXQgaGFwcGVuZWQ/IC0tIHRoZSBjb2RlIHJldHVybnMgMiAgNCAgNiAgOSAxMyAyNQ0KIyBDaGVjayB0aGUgRW52aXJvbm1lbnQ6IERpZCBhbnl0aGluZyBoYXBwZW4/IC0tIG5vdGhpbmcgDQpgYGANCg0KIyMjIENyZWF0aW5nIGFuZCBzYXZpbmcgb2JqZWN0cw0KDQpOb3cgbGV0J3MgbG9vayBhdCBob3cgdG8gY3JlYXRlIGFuZCBzYXZlIGFuIG9iamVjdC4gVGhpcyBhbGxvd3MgeW91IHRvIHNhdmUgdGhlbSB0byB5b3VyIHdvcmtpbmcgc3BhY2UgdW5kZXIgYSBuYW1lLiBUbyBjcmVhdGUgYW5kIHNhdmUgYW4gb2JqZWN0IHlvdTogDQoNCjEuIFR5cGUgdGhlIG5hbWUgeW91IHdhbnQgdG8gZ2l2ZSB5b3VyIG9iamVjdCAoZS5nLiBwaXp6YSkNCg0KMi4gVHlwZSB0aGlzIHN5bWJvbDogYDwtYA0KDQozLiBUeXBlIHRoZSBmdW5jdGlvbiBmb3IgdGhlIG9iamVjdCB5b3Ugd2lzaCB0byBjcmVhdGUsIGUuZy4sIGBjKDIsIDQsIDYsIDksIDEzLCAyNSlgDQoNCjQuIFByZXNzIEN0cmwvQ29tbWFuZCArIEVudGVyIHRvIHJ1biB0aGUgY29kZQ0KDQpBZnRlciB5b3UgcnVuIHRoZSBjb2RlLCB5b3Ugd2lsbCBzZWUgYW4gb2JqZWN0IGNhbGxlZCBwaXp6YSBpbiB0aGUgRW52aXJvbm1lbnQgcGFuZSB1bmRlciB0aGUgVmFsdWVzIHNlY3Rpb24uIEl0IGhhcyAibnVtIFsxOjZdIDIgNCA2IDkgMTMgMjUiIGluIHRoZSBzZWNvbmQgY29sdW1uLiAibnVtIiBtZWFucyB0aGF0IHRoaXMgdmVjdG9yIGNvbnRhaW5zIG51bWVyaWMgZGF0YS4gIlsxOjZdIiBzaG93cyB0aGF0IHRoZXJlIGFyZSA2IG51bWJlcnMgaW4gdGhpcyB2ZWN0b3IsIGFuZCB0aGVuIGl0IHNob3dzIHRoZSBmaXJzdCBzZXZlcmFsIG51bWJlcnMgZm9yIGEgcHJldmlldy4gDQoNCmBgYHtyfQ0KIyBOb3cgdHlwZSB0aGUgY29kZSB0byBjcmVhdGUgdGhlIG9iamVjdCBwaXp6YQ0KcGl6emEgPC0gYygyLCA0LCA2LCA5LCAxMywgMjUpDQojIENoZWNrIHRoZSBDb25zb2xlOiBXaGF0IGhhcHBlbmVkPyAtLSB0aGUgY29kZSBkaWRuJ3QgcmV0dXJuIHRoZSB2YWx1ZXMgDQojIENoZWNrIHRoZSBFbnZpcm9ubWVudDogRGlkIGFueXRoaW5nIGhhcHBlbj8gLS0gbmV3IG9iamVjdCBwaXp6YSBpbiB0aGUgVmFsdWVzIHNlY3Rpb24NCg0KIyBOb3csIGNhbGwgeW91ciBvYmplY3QgYnkgdHlwaW5nIHRoZSBuYW1lLCB0aGVuIEN0cmwvQ29tbWFuZCArIEVudGVyDQpwaXp6YQ0KDQojIENoZWNrIHRoZSBFbnZpcm9ubWVudDogV2hhdCBkbyB5b3Ugc2VlPyAtLSB0aGUgY29kZSByZXR1cm5zIHRoZSB2YWx1ZXMgMiAgNCAgNiAgOSAxMyAyNQ0KYGBgDQoNCiMjIFNjYWxhcnMgYW5kIG9wZXJhdGlvbnMNCg0KIyMjIE51bWVyaWMgc2NhbGFyDQoNClNjYWxhciBpcyBhdG9taWMgcXVhbnRpdHkgdGhhdCBjYW4gaG9sZCBvbmx5IG9uZSB2YWx1ZSBhdCBhIHRpbWUuIFdlIGNhbiBjcmVhdGUgbnVtZXJpY2FsIHNjYWxhcnMuIEZvciBleGFtcGxlLCBsZXQncyBjcmVhdGUgYSBzY2FsYXIgY2FsbGVkIHggd2l0aCB0aGUgdmFsdWUgNCBhbmQgYW5vdGhlciBzY2FsYXIgY2FsbGVkIHkgd2l0aCB0aGUgdmFsdWUgNy4gDQoNCmBgYHtyfQ0KIyBDcmVhdGUgeA0KeCA8LSA0DQoNCiMgQ3JlYXRlIHkNCnkgPC0gNw0KDQojIENoZWNrIHlvdXIgRW52aXJvbm1lbnQhDQpgYGANCg0KV2UgY2FuIGNoZWNrIHdoYXQga2luZCBvZiBzY2FsYXIgYSBnaXZlbiBvYmplY3QgaXMgYnkgdXNpbmcgdGhlIGBjbGFzcygpYCBmdW5jdGlvbi4gRm9yIGV4YW1wbGU6IGBjbGFzcyh4KWAgd2lsbCByZXR1cm4gdGhlIHRoZSB0eXBlIG9mIHRoZSBvYmplY3QgeCwgd2hpY2ggaXMgIm51bWVyaWMiLiANCg0KYGBge3J9DQojIGNoZWNrIHRoZSBjbGFzcyBvZiB4DQpjbGFzcyh4KQ0KDQojIHdoYXQga2luZCBvZiBzY2FsYXIgaXMgeD8NCg0KYGBgDQoNCldlIGNhbiBwZXJmb3JtIGFyaXRobWV0aWMgb3BlcmF0aW9ucyBvbiBvdXIgc2NhbGFycywgZS5nLiwgYWRkaW5nICgrKSwgc3VidHJhY3RpbmcgKC0pLCBtdWx0aXBseWluZyAoKiksIGRpdmlkaW5nICgvKSwgc3F1YXJpbmcgKHVzZSBeIGZvbGxvd2VkIGJ5IGEgbnVtYmVyLCBlLmcuLCBeMiksIHRha2luZyB0aGUgcm9vdCAodXNlIHRoZSBmdW5jdGlvbiBgc3FydCgpYCkNCg0KYGBge3J9DQojIHRyeSB0aGUgZm9sbG93aW5nIGFyaXRobWV0aWMgb3BlcmF0aW9ucywgdXNlIGFueSBudW1iZXIgeW91IGxpa2UNCg0KIyBhZGRpbmcNCjIrNCANCiMgc3VidHJhY3RpbmcNCjEtMTAwIA0KIyBtdWx0aXBseWluZw0KNCoyMyANCiMgZGl2aWRpbmcNCjg4LzggDQojIHNxdWFyaW5nDQo1XjMgDQojIHRha2luZyB0aGUgcm9vdA0Kc3FydCgxMCkgDQojIGNvbWJpbmF0aW9uIG9mIGRpZmZlcmVudCBvcGVyYXRpb25zDQpzcXJ0KCgxKzMpLzIqOSleMiANCmBgYA0KDQojIyMgTG9naWNhbCBzY2FsYXINCg0KV2UgY2FuIGFsc28gY3JlYXRlIGxvZ2ljYWwgc2NhbGFycyAoaS5lLiBUUlVFIGFuZCBGQUxTRSkuIEZvciBleGFtcGxlIGxldCdzIGNyZWF0ZSBhIHNjYWxhciBtIHRoYXQgaXMgZGVmaW5lZCBhcyB4ID4geSwgYW5kIGEgc2NhbGFyIG4gdGhhdCBpcyBkZWZpbmVkIGFzIHggPCB5LCBhbmQgYSBzY2FsYXIgcCB0aGF0IGlzIGRlZmluZWQgYXMgeCA9IHkuIGA9YCBpcyBleHByZXNzZWQgYXMgYD09YCBpbiBSIGxhbmd1YWdlLiBOb3RlOiB4IGFuZCB5IGFyZSBkZWZpbmVkIGFzIHRoZSB2YWx1ZXMgd2UgYXNzaWduZWQgdG8gdGhlbSBlYXJsaWVyICh4PTQsIGFuZCB5PTcpLg0KDQpgYGB7cn0NCiMgRGVmaW5lIG0NCm0gPC0geD55DQojIERlZmluZSBuDQpuIDwtIHg8eQ0KIyBEZWZpbmUgcA0KcCA8LSB4PT15DQojIENoZWNrIHRoZSBFbnZpcm9ubWVudDogd2hhdCBhcmUgdGhlIHZhbHVlcyBvZiBtLCBuLCBhbmQgcD8NCiMgbSBpcyBGQUxTRSwgbiBpcyBUUlVFLCBhbmQgcCBpcyBGQUxTRQ0KIyBXaGF0IGtpbmQgb2Ygc2NhbGFyIGlzIG0/IChoaW50OiB1c2UgY2xhc3MoKSBmdW5jdGlvbikNCmNsYXNzKG0pIA0KYGBgDQoNCkxvZ2ljYWwgb3BlcmF0b3JzIGluY2x1ZGUgQU5EIGFuZCBPUi4gRm9yIGV4YW1wbGU6IHggPiB5IGFuZCB4IDwgeSBjYW4gYmUgd3JpdHRlbiBhcyBgeCA+IHkgJiB4IDwgeWAgb3IgYG0gJiBuYDsgeCA+IHkgb3IgeCA8IHkgY2FuIGJlIHdyaXR0ZW4gYXMgYHggPiB5IHwgeCA8IHlgIG9yIGBtIHwgbmAuDQoNCmBgYHtyfQ0KIyBFdmFsdWF0ZSB0aGUgY29kZSBnaXZlbiBhcyBleGFtcGxlcyBpbiB0aGUgaW5zdHJ1Y3Rpb24NCnggPiB5ICYgeCA8IHkgDQptICYgbiANCmBgYA0KDQojIyMgQ2hhcmFjdGVyIHNjYWxhciANCg0KV2UgY2FuIG1ha2UgY2hhcmFjdGVyL3N0cmluZyBzY2FsYXJzLiBXZSBtdXN0IHVzZSA8Yj5xdW90YXRpb24gbWFya3M8L2I+IChlaXRoZXIgc2luZ2xlIG9yIGRvdWJsZSkgdG8gaW5kaWNhdGUgdGhhdCB0aGV5IGFyZSBjaGFyYWN0ZXJzLiBDcmVhdGUgYSBjaGFyYWN0ZXIgYGhlbGxvIHdvcmxkYCBhbmQgc2F2ZSBpdCB0byBhbiBvYmplY3QgbmFtZWQgYG15c3RyaW5nYC4NCg0KYGBge3J9DQojIENyZWF0ZSBteXN0cmluZw0KbXlzdHJpbmcgPC0gImhlbGxvIHdvcmxkIg0KIyB3aGF0IGtpbmQgb2Ygc2NhbGFyIGlzICdteXN0cmluZyc/IChoaW50OiB1c2UgY2xhc3MoKSBmdW5jdGlvbikNCmNsYXNzKG15c3RyaW5nKSANCmBgYA0KDQpXZSBjYW4ndCB1c2Ugb3BlcmF0aW9ucyBvbiBjaGFyYWN0ZXIgc2NhbGFycy4gTm90ZSB0aGF0IHdoZW4gYSBudW1iZXIgaXMgc3Vycm91bmRlZCBieSBxdW90YXRpb24gbWFya3MsIGl0IGJlY29tZXMgYSBjaGFyYWN0ZXIuIEZvciBleGFtcGxlLCAxIGlzIGEgbnVtYmVyLCAiMSIgaXMgYSBjaGFyYWN0ZXIuIFlvdSBjYW4gdXNlIGBjbGFzcygpYCBmdW5jdGlvbiB0byB0ZXN0IGlmIHRoaXMgaXMgdHJ1ZS4gDQoNCmBgYHtyIGV2YWw9RkFMU0V9DQojIFRyeSB0aGlzDQpzdHJpbmcxIDwtICIxIiAjIEFUVEVOVElPTjogVGhpcyBpcyBhIGNoYXJhY3RlciBzY2FsYXIgYmVjYXVzZSB3ZSBwdXQgcXVvdGF0aW9uIG1hcmtzIQ0Kc3RyaW5nMiA8LSAiMiINCiMgVHJ5IGFkZGluZyB0aGVtIGFuZCBzZWUgd2hhdCBoYXBwZW5zIDopDQpzdHJpbmcxICsgc3RyaW5nMiAjIEVycm9yIGluIHN0cmluZzEgKyBzdHJpbmcyIDogbm9uLW51bWVyaWMgYXJndW1lbnQgdG8gYmluYXJ5IG9wZXJhdG9yDQpgYGANCg0KIyMgVmVjdG9ycyBhbmQgb3BlcmF0aW9ucw0KDQojIyMgQ3JlYXRlIGEgdmVjdG9yDQoNCldlIGNhbiBjb21iaW5lIHNjYWxhcnMgaW50byBsYXJnZXIgb2JqZWN0cyBvZiB0aGUgc2FtZSBkYXRhIHR5cGU6IFRoZXNlIGFyZSBjYWxsZWQgdmVjdG9ycy4gVXNlIGBjKClgIGZ1bmN0aW9uIHdoaWNoIGhhcyBiZWVuIGludHJvZHVjZWQgZWFybGllci4gDQoNCmBgYHtyfQ0KIyBjcmVhdGVzIHZlY3RvciBhIHdoaWNoIGNvbnRhaW5zIHZhbHVlcyAxLDIsMyw0IChoaW50OiBhIDwtIGMoeCx5LHosLi4uKSkNCmEgPC0gYygxLDIsMyw0KQ0KIyBjcmVhdGVzIHZlY3RvciBiIGNvbnRhaW5pbmcgbnVtYmVycyBmcm9tIDEgdG8gMTANCmIgPC0gYygxLDIsMyw0LDUsNiw3LDgsOSwxMCkNCmBgYA0KDQpUd28gdXNlZnVsIGZ1bmN0aW9ucyB0byBjcmVhdGUgc2NhbGFyIHZlY3RvcnMgd2l0aCBzb21lIHJ1bGVzLiANCg0KMS4gVG8gY3JlYXRlIGNvbnRpbnVvdXMgaW50ZWdlciBudW1iZXJzIHlvdSBjYW4gdXNlIGA6YCB3aXRoaW4gYGMoKWAgZnVuY3Rpb24uIEZvciBleGFtcGxlLCBgYygxOjEwKWAgcmV0dXJucyBudW1iZXJzIGZyb20gMSB0byAxMC4gIA0KDQoyLiBgc2VxKGZyb20sIHRvLCBieSlgIGNyZWF0ZXMgdmVjdG9yIHdpdGggc2VxdWVuY2Ugb2YgdmFsdWVzLiAiZnJvbSIgZGVmaW5lcyB0aGUgc3RhcnRpbmcgcG9pbnQsICJ0byIgZGVmaW5lcyB0aGUgZW5kaW5nIHBvaW50LCBhbmQgImJ5IiBkZWZpbmVzIHRoZSBzdGVwLiBGb3IgZXhhbXBsZSwgYHNlcShmcm9tPTEsdG89NSxieT0xKWAgY3JlYXRlcyBhIHZlY3RvciBjb250YWluaW5nIDEsMiwzLDQsNS4gDQoNCjMuIGByZXAoeCwgdGltZXMsIGVhY2gpYCBjcmVhdGVzIHZlY3RvciB3aXRoIHZhbHVlcyByZXBlYXRlZC4gIngiIGRlZmluZXMgdGhlIHZlY3RvciB0byBiZSByZXBlYXRlZCwgInRpbWVzIiBkZWZpbmVzIHRoZSBudW1iZXIgb2YgcmVwZXRpdGlvbiBvZiB0aGUgZW50aXJlIHZlY3RvciwgYW5kICJlYWNoIiBkZWZpbmVzIHRoZSBudW1iZXIgb2YgcmVwZXRpdGlvbiBvZiBlYWNoIGVsZW1lbnQgaW4gdGhlIHZlY3Rvci4gRm9yIGV4YW1wbGUsIGByZXAoYygxLDIsMyksdGltZXM9MixlYWNoPTIpYGAgY3JlYXRlcyBhIG5ldyB2ZWN0b3IgY29udGFpbmluZyAxLDEsMiwyLDMsMywxLDEsMiwyLDMsMy4NCg0KYGBge3J9DQojIGdlbmVyYXRlIG51bWJlcnMgZnJvbSAxIHRvIDEwMCB1c2luZyBjKCkNCmMoMToxMDApDQojIGNyZWF0ZSB2ZWN0b3IgYyB3aXRoIHNlcXVlbmNlIG9mIHZhbHVlcyBmcm9tIDEsIHRvIDIwLCBieSAyDQpjIDwtIHNlcShmcm9tPTEsdG89MjAsYnk9MikNCiMgY3JlYXRlIHZlY3RvciBkIHdpdGggdmVjdG9yICgyLDQsNikgcmVwZWF0ZWQgMiB0aW1lcywgd2l0aCBlYWNoIGVsZW1lbnQgcmVwZWF0ZWQgMyB0aW1lcw0KZCA8LSByZXAoYygyLDQsNiksdGltZXM9MixlYWNoPTMpDQpgYGANCg0KIyMjIEFyaXRobWV0aWMgb3BlcmF0aW9ucyB3aXRoIHZlY3RvcnMNCg0KYGBge3J9DQojIE1ha2UgYSBzaW1wbGUgbnVtZXJpYyB2ZWN0b3IgKGUpDQplIDwtIGMoMSwyLDMpDQojIE1ha2UgYW5vdGhlciBvbmUgKGYpDQpmIDwtIGMoNSw3LDkpDQojIE1ha2UgYW5vdGhlciBvbmUgKGcpDQpnIDwtIGMoNSw3LDksMTEpDQojIENoZWNrIHRoZSBFbnZpcm9ubWVudCB0byBzZWUgdGhlIGxlbmd0aCBvZiB0aGUgdmVjdG9ycw0KIyBlIGFuZCBmIGhhdmUgMyBudW1iZXJzLCBnIGhhcyA0IG51bWJlcnMNCmBgYA0KDQpXZSBjYW4gZG8gYXJpdGhtZXRpYyBvcGVyYXRpb25zIG9uIHZlY3RvcnMgdG9vISBJZiB3ZSB3YW50IHRvIGRvIG9wZXJhdGlvbnMgYmV0d2VlbiB0d28gdmVjdG9ycywgdGhleSBtdXN0IGhhdmUgdGhlIHNhbWUgbGVuZ3RoLCBvciBvbmUgaXMgYSBtdWx0aXBsZSBvZiB0aGUgb3RoZXIuIA0KDQpgYGB7cn0NCiMgYWRkcyA2IHRvIGVhY2ggdmFsdWUgaW4gZQ0KZSs2DQojIHNxdWFyaW5nIGVhY2ggdmFsdWUgb2YgZg0KZl4yDQojIGFkZGluZyBlIGFuZCBmIChlYWNoIGNvcnJlc3BvbmRpbmcgdmFsdWUgaW4gdGhlIHNlcXVlbmNlIGlzIHN1bW1lZCkNCmUrZg0KIyBtdWx0aXBseWluZyBlIGFuZCBmIChlYWNoIGNvcnJlc3BvbmRpbmcgdmFsdWUgaW4gdGhlIHNlcXVlbmNlIGlzIG11bHRpcGxpZWQpDQplKmYNCg0KIyBOb3RlOiBJZiB5b3Ugd2FudCB0byBzYXZlIHRoZXNlIHJlc3VsdHMgdG8geW91ciBFbnZpcm9ubWVudCBhcyB2ZWN0b3JzLCB5b3UgbmVlZCB0byBnaXZlIHRoZW0gYSBuYW1lDQpgYGANCg0KYGBge3IgZXZhbD1GQUxTRX0NCiMgTm93LCB0cnkgYWRkaW5nIGUgYW5kIGcsIHdoYXQgaGFwcGVuZWQ/IA0KZStnICMgcmV0dXJucyBXYXJuaW5nOiBsb25nZXIgb2JqZWN0IGxlbmd0aCBpcyBub3QgYSBtdWx0aXBsZSBvZiBzaG9ydGVyIG9iamVjdCBsZW5ndGhbMV0gIDYgIDkgMTIgMTINCmBgYA0KDQojIyMgTG9naWNhbCBvcGVyYXRpb25zIHdpdGggdmVjdG9ycw0KDQpXZSBjYW4gYWxzbyBkbyBsb2dpY2FsIG9wZXJhdGlvbnMgd2l0aCB2ZWN0b3JzLiBGb3IgZXhhbXBsZSwgd2UgY2FuIGV2YWx1YXRlIHdoZXRoZXIgZWFjaCBpdGVtIGluIHRoZSB2ZWN0b3IgaXMgbGFyZ2VyIHRoYW4gMTAgYnkgYHZlY3Rvcl9uYW1lID4gMTBgLiANCg0KYGBge3J9DQojIHJlbWVtYmVyIHRoYXQgd2UgaGF2ZSB2ZWN0b3IgYiB3aGljaCBjb250YWlucyBpbnRlZ2VyIG51bWJlcnMgZnJvbSAxIHRvIDEwDQojIGZpbmQgb3V0IHdoZXRoZXIgZWFjaCBpdGVtIGluIHZlY3RvciBiIGlzIGxhcmdlciB0aGFuIDYNCmIgPiA2DQojIGZpbmQgb3V0IHdoZXRoZXIgZWFjaCBpdGVtIGluIHZlY3RvciBiIGlzIGxhcmdlciB0aGFuIDUgQU5EIHNtYWxsZXIgdGhhbiA4DQpiID4gNSAmIGIgPCA4DQpgYGANCg0KVGhlIGAlaW4lYCBvcGVyYXRpb24gYWxsb3dzIHlvdSB0byBjb21iaW5lIG11bHRpcGxlIE9SIGNvbXBhcmlzb25zIHRvIGNoZWNrIHdoZXRoZXIgZ2l2ZW4gdmFsdWVzIGFyZSBwcmVzZW50IGluIGEgc2V0LiBGb3IgZXhhbXBsZTogWW91IGhhdmUgYSB2ZWN0b3IgdGhhdCBkZXNjcmliZXMgZmF2b3VyaXRlIGxldHRlcnMgYGZhdmUubGV0dGVycyA8LSBjKCdhJywgJ3QnLCAnYScsICdiJywgJ3onKWAuIFRvIGNoZWNrIHdoZXRoZXIgdGhlIGVhY2ggbGV0dGVyIGluIGZhdmUubGV0dGVycyBpcyBvbmUgb2YgJ2EnLCAnYicgb3IgJ2MnLCB5b3UgY2FuIHR5cGU6IGBmYXZlLmxldHRlcnMgJWluJSBjKCdhJywgJ2InLCAnYycpYC4gVGhlIHJlc3VsdHMgd2lsbCBiZSBUUlVFIEZBTFNFIFRSVUUgVFJVRSBGQUxTRSwgaS5lLiwgb25seSB0aGUgMm5kIGxldHRlciAndCcgYW5kIHRoZSA1dGggbGV0dGVyICd6JyBhcmUgbm90IG9uZSBvZiAnYScsICdiJyBvciAnYycuDQoNClRoaXMgaXMgaGVscGZ1bCB3aGVuIHlvdSB3YW50IHRvIGZpbHRlciB5b3VyIHBhcnRpY2lwYW50cyBiYXNlZCBvbiB0aGVpciByZXNwb25zZXMuIEZvciBleGFtcGxlLCBvbmx5IGtlZXAgcGFydGljaXBhbnRzIHdob3NlIGZhdm91cml0ZSBsZXR0ZXIgaXMgb25lIG9mICdhJywgJ2InIG9yICdjJy4gDQoNCmBgYHtyfQ0KIyBmaW5kIG91dCB3aGV0aGVyIGVhY2ggaXRlbSBpbiB2ZWN0b3IgYiBpcyBpbiB0aGUgc2V0IDEsNywxMw0KYiAlaW4lIGMoMSw3LDEzKQ0KIyBmaW5kIG91dCB3aGV0aGVyIGVhY2ggaXRlbSBpbiB2ZWN0b3IgYiBpcyBpbiB0aGUgdmVjdG9yIGENCmIgJWluJSBhDQpgYGANCg0KTm90ZSB0aGF0IGJlY2F1c2UgRkFMU0UgPSAwIGFuZCBUUlVFID0gMSwgd2UgY2FuIGBzdW0oKWAgdGhlIGxvZ2ljYWwgdmVjdG9ycyB0byBmaW5kIG91dCB0aGUgbnVtYmVyIG9mIGl0ZW1zIHRoYXQgc2F0aXNmeSB0aGUgb3BlcmF0aW9uLCBhbmQgdXNlIGBtZWFuKClgIHRvIGZpbmQgb3V0IHRoZSBwcm9wb3J0aW9uIG9mIGl0ZW1zIHRoYXQgc2F0aXNmeSB0aGUgb3BlcmF0aW9uLg0KDQpgYGB7cn0NCiMgZmluZCBvdXQgdGhlIG51bWJlciBvZiBpdGVtcyBpbiB2ZWN0b3IgYiB0aGF0IGFyZSBsYXJnZXIgdGhhbiA2DQpzdW0oYiA+IDYpDQojIGZpbmQgb3V0IHRoZSBudW1iZXIgb2YgaXRlbXMgaW4gdmVjdG9yIGIgdGhhdCBhcmUgaW4gdGhlIHNldCAzLDUsMA0Kc3VtKGIgJWluJSBjKDMsNSwwKSkNCiMgZmluZCBvdXQgdGhlIHByb3BvcnRpb24gb2YgaXRlbXMgaW4gdmVjdG9yIGIgYXJlIGxhcmdlciB0aGFuIDUgQU5EIHNtYWxsZXIgdGhhbiA4DQptZWFuKGIgPiA1ICYgYiA8IDgpDQpgYGANCg0KIyMjIEluZGV4aW5nIGEgdmVjdG9yDQoNCllvdSBjYW4gYXNrIFIgdG8gcmV0dXJuIHRoZSB2YWx1ZXMgb2Ygc3BlY2lmaWMgaXRlbXMgd2l0aGluIHlvdXIgdmVjdG9ycyBieSB0eXBpbmcgdGhlIHZlY3RvciBuYW1lIGZvbGxvd2VkIGJ5IGBbXWAsIGFuZCBwdXQgdGhlIG9yZGVyIG51bWJlciBvZiB0aGUgaXRlbSBpbnRvIGBbXWAuIEZvciBleGFtcGxlLCB0byBnZXQgdGhlIDJuZCBpdGVtIGluIHZlY3RvciBhLCB5b3Ugd3JpdGUgYGFbMl1gLiBUbyByZXR1cm4gbXVsdGlwbGUgaXRlbXMsIHlvdSBjYW4gdXNlIGBjKClgIHRvIGNvbWJpbmUgYWxsIGluZGljZXMgeW91IGFyZSBpbnRlcmVzdGVkIGluLiANCg0KYGBge3J9DQojIHJlbWVtYmVyIHRoYXQgd2UgaGF2ZSB2ZWN0b3IgYiB3aGljaCBjb250YWlucyBpbnRlZ2VyIG51bWJlcnMgZnJvbSAxIHRvIDEwDQojIEdldCB0aGUgM3JkIGl0ZW0gaW4gdmVjdG9yIGINCmJbM10NCiMgR2V0IHRoZSAybmQgYW5kIDR0aCBpdGVtIGluIHZlY3RvciBiDQpiW2MoMiw0KV0NCiMgR2V0IHRoZSAybmQgdG8gOHRoIGl0ZW0gaW4gdmVjdG9yIGINCmJbYygyOjgpXQ0KYGBgDQoNCldlIGNhbiBhbHNvIHVzZSBsb2dpY2FsIG9wZXJhdGlvbnMgaW5zaWRlIGluZGV4aW5nIGBbXWAuIEZvciBleGFtcGxlLCBpZiB3ZSB3YW50IHRvIGtub3cgd2hhdCBpdGVtcyBpbiBhIHZlY3RvciBpcyBzbWFsbGVyIHRoYW4gNSwgd2UgY2FuIHdyaXRlIGB2ZWN0b3JfbmFtZVt2ZWN0b3JfbmFtZSA8IDVdYC4gVGhpcyBpcyBiZWNhdXNlIGB2ZWN0b3JfbmFtZSA8IDVgIHJldHVybnMgYSB2ZWN0b3Igb2YgbG9naWNhbCB2YWx1ZXMgKGkuZS4sIEZBTFNFcyAmIFRSVUVzKSwgYW5kIHRoZW4gaW5kZXhpbmcgYFtdYCB3aWxsIHJldHVybiB0aGUgaXRlbXMgdGhhdCBoYXZlIFRSVUVzLiANCg0KYHdoaWNoKClgIGZ1bmN0aW9uIGNhbiByZXR1cm4gdGhlIGluZGljZXMgd2hlcmUgdGhlIHZhbHVlIGlzIFRSVUUuIFdlIGNhbiB3cml0ZSBgd2hpY2godmVjdG9yX25hbWUgPCA1KWAgdG8gcmV0dXJuIHRoZSBpbmRpY2VzIG9mIG51bWJlcnMgd2hpY2ggaXMgc21hbGxlciB0aGFuIDUuIA0KDQpgYGB7cn0NCiMgZmluZCBvdXQgd2hhdCBpdGVtcyBpbiB2ZWN0b3IgYiBhcmUgc21hbGxlciB0aGFuIDQNCmJbYjw0XQ0KIyBmaW5kIG91dCB3aGF0IGl0ZW1zIGluIHZlY3RvciBiIGFyZSBsYXJnZXIgdGhhbiA0IGJ1dCBzbWFsbGVyIHRoYW4gNg0KYltiPjQgJiBiPDZdDQojIGZpbmQgb3V0IHRoZSBpbmRpY2VzIG9mIGl0ZW1zIGluIHZlY3RvciBiIGFyZSBzbWFsbGVyIHRoYW4gNA0Kd2hpY2goYjw0KSAjIG5vdGUgdGhhdCBiZWNhdXNlIGIgY29udGFpbnMgaW50ZWdlciBudW1iZXJzIGZyb20gMSB0byAxMCwgdGhlIGluZGljZXMgYXJlIHRoZSBzYW1lIGFzIG51bWJlciB2YWx1ZXMuIA0KYGBgDQoNCiMjIERhdGFmcmFtZXMNCg0KVG8gY3JlYXRlIGEgZGF0YWZyYW1lLCB5b3UgbmVlZCB0byBzcGVjaWZ5IGNvbHVtbiBuYW1lcyBhbmQgdmFsdWVzIGluIGVhY2ggY29sdW1uLiBGb3IgZXhhbXBsZSwgbGV0J3MgY3JlYXRlIGEgZGF0YWZyYW1lIGNhbGxlZCBgZGYxYCB3aXRoIHR3byBjb2x1bW5zLCAic2V4IiBhbmQgImFnZSIuIFdlIGhhdmUgMyBtYWxlcyBhbmQgdGhleSBhcmUgOTksIDQ2LCAyMyB5ZWFycyBvbGQsIGFuZCAyIGZlbWFsZXMgYW5kIHRoZXkgYXJlIDU0LCAyMyB5ZWFycyBvbGQuIFVzZSBgZGF0YS5mcmFtZSgpYCBmdW5jdGlvbiwgZWFjaCBhcmd1bWVudCB3aWxsIGJlIGluIHRoZSBmb3JtYXQgImNvbHVtbiBuYW1lID0gYyh2YWx1ZTEsIHZhbHVlMiwgdmFsdWUzLCAuLi4pIiwgZS5nLiwgImFnZSA9IGMoOTksNDYsMjMsNTQsMjMpIi4gDQoNCmBgYHtyfQ0KIyBDcmVhdGUgZGYxIHVzaW5nIHRoZSBkYXRhLmZyYW1lKCkgZnVuY3Rpb24NCmRmMSA8LSBkYXRhLmZyYW1lKHNleCA9IGMoIm1hbGUiLCJtYWxlIiwibWFsZSIsImZlbWFsZSIsImZlbWFsZSIpLCANCiAgICAgICAgICAgICAgICAgIGFnZSA9IGMoOTksNDYsMjMsNTQsMjMpKQ0KIyBOb3cgY2FsbCBvbiB5b3VyIGRhdGFmcmFtZTogd2hhdCBkb2VzIFIgcmV0dXJuPw0KZGYxDQpgYGANCg0KWW91ciBjb2x1bW4vdmVjdG9yIG5hbWVzIGFyZSB5b3VyIHZhcmlhYmxlcywgeW91IGNhbGwgb24gc3BlY2lmaWMgdmFyaWFibGVzIHdpdGhpbiBhIGRhdGFmcmFtZSB3aXRoIHRoZSBkb2xsYXIgc3ltYm9sIGAkYCwgZS5nLiwgYGRmMSRzZXhgICh0aGlzIG1lYW5zIHlvdSBleHRyYWN0IGEgPGI+dmVjdG9yPC9iPiBmcm9tIHRoZSA8Yj5kYXRhZnJhbWU8L2I+ISkuDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IGNvbHVtbiBhZ2UgYW5kIHNleCANCmRmMSRzZXgNCmRmMSRhZ2UNCmBgYA0KDQpUbyBnZXQgY29sdW1uIG5hbWVzLCB5b3UgY2FuIHVzZSBgY29sbmFtZXMoZGF0YWZyYW1lKWAgZnVuY3Rpb24uIFlvdSBjYW4gY2hhbmdlIGEgc3BlY2lmaWVkIGNvbHVtbiBuYW1lIHVzaW5nIGZ1bmN0aW9uIGByZW5hbWUoZGF0YWZyYW1lLCBuZXcgbmFtZSA9IG9sZCBuYW1lKWAgKE5vdGU6IGByZW5hbWUoKWAgaXMgcHJvdmlkZWQgYnkgYHRpZHl2ZXJzZWAgcGFja2FnZS4gUmVtZW1iZXIgdG8gbG9hZCB0aGUgcGFja2FnZSB1c2luZyBgbGlicmFyeSgpYCBmaXJzdCEpLiBBbm90aGVyIG9wdGlvbiBpcyB0byB1c2UgYGNvbG5hbWVzKGRhdGFmcmFtZSkgPC0gYygieHh4IiwgInh4eCIsIC4uLilgIHRvIGNoYW5nZSBBTEwgdGhlIGNvbHVtbiBuYW1lcyBhdCBvbmNlLiANCg0KYGBge3J9DQojbGlicmFyeSh0aWR5dmVyc2UpIGlmIHlvdSBoYXZlbid0DQojIEdldCB0aGUgY29sdW1uIG5hbWVzIG9mIGRmMQ0KY29sbmFtZXMoZGYxKQ0KIyBjaGFuZ2UgY29sdW1uIG5hbWUgInNleCIgdG8gImdlbmRlciIgdXNpbmcgcmVuYW1lKCkNCnJlbmFtZShkZjEsICJnZW5kZXIiID0gInNleCIpDQojIGNoYW5nZSBjb2x1bW4gbmFtZXMgdG8gIkdFTkRFUiIgYW5kICJBR0UiIHVzaW5nIGNvbG5hbWVzKCkNCmNvbG5hbWVzKGRmMSkgPC0gYygiR0VOREVSIiwgIkFHRSIpDQojIGNhbGwgb24gZGYxIHRvIHNlZSB0aGUgY2hhbmdlDQpkZjENCmBgYA0KDQojIyMgRmFjdG9yIHZhcmlhYmxlDQoNCldlIGNhbiBzZXQgdGhlIHNleCBjb2x1bW4gdG8gYSBmYWN0b3IgdmFyaWFibGUgdXNpbmcgYGFzLmZhY3RvcigpYCBmdW5jdGlvbi4gSWYgc2V4IGlzIHNldCB0byBhIGZhY3RvciB2YXJpYWJsZSwgaXQgaXMgY2F0ZWdvcmljYWwgYW5kIG5ldyBvYnNlcnZhdGlvbnMgY2FuIG9ubHkgYmUgb25lIG9mIHRoZSBzcGVjaWZpZWQgbGV2ZWxzLiBUaGUgbGV2ZWxzIGFyZSBieSBkZWZhdWx0IGFycmFuZ2VkIGJ5IGFscGhhYmV0aWNhbCBvcmRlci4gWW91IGNhbiBtYW51YWxseSBzcGVjaWZ5IHRoZSBvcmRlciB1c2luZyBmdW5jdGlvbiBgZmFjdG9yKClgIChubyAiYXMiISkgYW5kIGFyZ3VtZW50IGBsZXZlbHMgPSBjKCJ4eHgiLCAieHh4IiwgLi4uKWAuDQoNCjxiPlJlbWVtYmVyIHlvdSBtdXN0IGdpdmUgdGhlIHJlc3VsdHMgYmFjayB0byB0aGUgb2JqZWN0IHRvIHNhdmUgdGhlIGNoYW5nZSEgPC9iPiBGb3IgZXhhbXBsZSwgYGErMWAgd2lsbCBhZGQgMSB0byB0aGUgbnVtYmVyIHNjYWxhciBgYWAsIGJ1dCB0aGUgdmFsdWUgb2YgYGFgIHdvbid0IGNoYW5nZSwgd2hlcmVhcyBgYSA8LSBhKzFgIHdpbGwgdXBkYXRlIHRoZSB2YWx1ZSBvZiBgYWAuIA0KDQpgYGB7cn0NCiMgVHVybiBHRU5ERVIgY29sdW1uIGludG8gYSBmYWN0b3IgdmFyaWFibGUgdXNpbmcgYXMuZmFjdG9yKCkNCmRmMSRHRU5ERVIgPC0gYXMuZmFjdG9yKGRmMSRHRU5ERVIpDQojIFNldCBtYWxlIHRvIGJlIGxldmVsMSBhbmQgZmVtYWxlIHRvIGJlIGxldmVsMiB1c2luZyBmYWN0b3IoKQ0KZGYxJEdFTkRFUiA8LSBmYWN0b3IoZGYxJEdFTkRFUiwgbGV2ZWxzID0gYygibWFsZSIsICJmZW1hbGUiKSkNCiMgQ2FsbCBvbiB0aGUgR0VOREVSIGNvbHVtbiBhbmQgc2VlIHdoYXQgbGV2ZWxzIGFyZSB0aGVyZSBpbiB0aGUgZmFjdG9yIChoaW50OiB1c2UgdGhlIGRvbGxvciBzeW1ib2wgJCkNCmRmMSRHRU5ERVINCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFfQ0KIyBBZGQgYSA2dGggb2JzZXJ2YXRpb24gdG8gdGhlIGNvbHVtbi4gQ2FuIHlvdSBhZGQgImZlbWFsZSI/IENhbiB5b3UgYWRkICJvdGhlciI/DQojIGZpcnN0IHdlIGV4dHJhY3QgdGhlIHZlY3RvciBHRU5ERVIgPC0gZGYxJEdFTkRFUg0KIyB0aGVuIHRyeSBHRU5ERVJbNl0gPC0gImZlbWFsZSIgYW5kIHRoZW4gR0VOREVSWzZdIDwtICJvdGhlciIsIFdoYXQgaGFwcGVuZWQ/IA0KR0VOREVSIDwtIGRmMSRHRU5ERVINCkdFTkRFUls2XSA8LSAiZmVtYWxlIiAjIHdvcmtzIGZpbmUNCkdFTkRFUls2XSA8LSAib3RoZXIiICMgcmV0dXJucyBXYXJuaW5nOiBpbnZhbGlkIGZhY3RvciBsZXZlbCwgTkEgZ2VuZXJhdGVkLCAib3RoZXIiIGlzIHJlcGxhY2VkIHdpdGggPE5BPiANCmBgYA0KDQojIyBMZXQncyBwcmFjdGljZSAhDQoNCjEuIENyZWF0ZSBhIHZlY3RvciBmb3IgZWFjaCBvZiB0aGUgY29sdW1ucyBpbiB0aGUgdGFibGUgKHNlZSB0aGUgbGFzdCBzbGlkZSBmb3IgTGFiMSkNCg0KICAgICogVGlwIDE6IHVzZSB0aGUgY29tYmluZSBmdW5jdGlvbiBgYygpYCB0byBjcmVhdGUgdGhlIHZlY3RvcnMNCg0KICAgICogVGlwIDI6IGFzc2lnbiB2ZWN0b3JzIHRvIGNvbHVtbiBuYW1lcywgZS5nLiwgY29sdW1uX25hbWUgPSBjKHZhbHVlMSwgdmFsdWUyLCAuLi4pDQoNCmBgYHtyfQ0KbGFuZ19uYW1lIDwtIGMoJ01hbmRhcmluJywgJ1NwYW5pc2gnLCAnRW5nbGlzaCcsICdIaW5kaScsICdQb3J0dWd1ZXNlJywgJ0JlbmdhbGknLCAnUnVzc2lhbicsICdKYXBhbmVzZScpDQpsYW5nX29yZGVyIDwtIGMoJ1NWTycsICdTVk8nLCAnU1ZPJywgJ1NPVicsICdTVk8nLCAnU09WJywgJ1NWTycsICdTT1YnKQ0KbGFuZ19wb3BMMSA8LSBjKDkyMS4yLCA0NzEuNCwgMzY5LjksIDM0Mi4yLCAyMzIuNCwgMjI4LjcsIDE1My43LCAxMjYuMykNCmxhbmdfcG9wTDIgPC0gYygxOTguNywgNzEuNSwgOTc4LjIsIDI1OC4zLCAyNS4yLCAzOS4wLCAxMDQuMywgMC4xMikNCmxhbmdfaW5kb2V1cm8gPC0gYyhGQUxTRSwgVFJVRSwgVFJVRSwgVFJVRSwgVFJVRSwgVFJVRSwgVFJVRSwgRkFMU0UpDQpgYGANCg0KMi4gQ29tYmluZSB0aGUgdmVjdG9ycyBpbnRvIGEgZGF0YWZyYW1lDQoNCiAgICAqIFRpcCAxOiB1c2UgdGhlIGRhdGFmcmFtZSBmdW5jdGlvbiBgZGF0YS5mcmFtZSgpYCB0byBjcmVhdGUgdGhlIGRhdGFmcmFtZQ0KDQogICAgKiBUaXAgMjogc2F2ZSB0aGUgZGF0YWZyYW1lIHdpdGggdGhlIG5hbWUgbGFuZ19kZg0KDQpgYGB7cn0NCmxhbmdfZGYgPC0gZGF0YS5mcmFtZShsYW5nX25hbWUsIGxhbmdfb3JkZXIsIGxhbmdfcG9wTDEsIGxhbmdfcG9wTDIsbGFuZ19pbmRvZXVybykNCmBgYA0KDQozLiBIYXZlIGEgbG9vayBhdCB0aGUgZGF0YWZyYW1lDQoNCiAgICAqIENhbGwgb24gdGhlIGVudGlyZSBkYXRhZnJhbWUNCg0KICAgICogQ2FsbCBvbiBzcGVjaWZpYyBjb2x1bW5zICh2ZWN0b3JzKSBpbiB5b3VyIGRhdGFmcmFtZQ0KDQogICAgKiBDYWxsIG9uIGFsbCBjb2x1bW4gbmFtZXMNCg0KYGBge3J9DQojIENhbGwgb24gdGhlIGVudGlyZSBkYXRhZnJhbWUNCmxhbmdfZGYNCg0KIyBDYWxsIG9uIHNwZWNpZmljIGNvbHVtbnMgKHZlY3RvcnMpIGluIHlvdXIgZGF0YWZyYW1lDQpsYW5nX2RmJGxhbmdfbmFtZQ0KbGFuZ19kZiRsYW5nX3BvcEwxDQoNCiMgcmV0dXJuIGFsbCBjb2x1bW4gbmFtZXMNCmNvbG5hbWVzKGxhbmdfZGYpDQpgYGANCg0KNC4gVHJ5IGV4ZWN1dGluZyB0aGUgZm9sbG93aW5nIGZ1bmN0aW9ucyBvbiB5b3VyIGRhdGFmcmFtZSBhbmQgc2VlIHdoYXQgdGhlIGZ1bmN0aW9ucyByZXR1cm4uIA0KDQpgYGB7cn0NCiMgaGVhZChkZl9uYW1lKQ0KaGVhZChsYW5nX2RmKSAjIHJldHVybnMgZmlyc3Qgc2V2ZXJhbCByb3dzDQojIHRhaWwoZGZfbmFtZSkNCnRhaWwobGFuZ19kZikgIyByZXR1cm5zIGxhc3Qgc2V2ZXJhbCByb3dzDQojIFZpZXcoZGZfbmFtZSkgTi5CLiB0aGUgJ1YnIGluIFZpZXcgaXMgY2FwaXRhbGlzZWQNClZpZXcobGFuZ19kZikgIyB0aGlzIHdpbGwgb3BlbiBhIG5ldyB0YWIgaW4gdGhlIFNvdXJjZSBwYW5lIGFuZCBzaG93IHRoZSB0YWJsZSBvZiBsYW5nX2RmDQojIG5yb3coZGZfbmFtZSkNCm5yb3cobGFuZ19kZikgIyByZXR1cm5zIHRvdGFsIG51bWJlciBvZiByb3dzDQojIG5jb2woZGZfbmFtZSkNCm5jb2wobGFuZ19kZikgIyByZXR1cm5zIHRvdGFsIG51bWJlciBvZiBjb2x1bW5zDQojIGRpbShkZl9uYW1lKQ0KZGltKGxhbmdfZGYpICMgcmV0dXJucyB0b3RhbCBudW1iZXIgb2Ygcm93cyBhbmQgY29sdW1ucyANCiMgc3VtbWFyeShkZl9uYW1lKQ0Kc3VtbWFyeShsYW5nX2RmKSAjIHJldHVybnMgYSBzdW1tYXJ5IG9mIHZhbHVlcyBpbiBlYWNoIGNvbHVtbg0KIyBXaGF0IGRvIGVhY2ggb2YgdGhlc2UgZnVuY3Rpb25zIGRvPw0KYGBgDQoNCjUuIEluZGV4aW5nIGEgdmVjdG9yIChjb2x1bW4pIGluIGRhdGFmcmFtZQ0KDQpgYGB7cn0NCiMgV2hhdCBpcyB0aGUgTDEgbGFuZ3VhZ2UgcG9wdWxhdGlvbiBvZiB0aGUgZmlyc3QgbGFuZ3VhZ2UgKGllLCBNYW5kYXJpbik/DQpsYW5nX2RmJGxhbmdfcG9wTDFbMV0NCiMgV2hhdCBhcmUgdGhlIHdvcmQgb3JkZXJzIG9mIHRoZSBmaXJzdCA1IGxhbmd1YWdlcz8NCmxhbmdfZGYkbGFuZ19vcmRlclsxOjVdDQojIFdoZXRoZXIgZWFjaCBMMSBsYW5ndWFnZSBwb3B1bGF0aW9uIGFyZSBncmVhdGVyIHRoYW4gMzAwIG1pbGxpb24/DQpsYW5nX2RmJGxhbmdfcG9wTDEgPiAzMDANCiMgV2hldGhlciBlYWNoIGxhbmd1YWdlIG9yZGVycyBhcmUgU1ZPPyANCmxhbmdfZGYkbGFuZ19vcmRlciA9PSAiU1ZPIg0KIyBXaGF0IGFyZSB0aGUgbmFtZXMgb2YgbGFuZ3VhZ2VzIHdpdGggTDIgcG9wdWxhdGlvbiBsb3dlciB0aGFuIDEwMCBtaWxsaW9uPw0KbGFuZ19kZiRsYW5nX25hbWVbbGFuZ19kZiRsYW5nX3BvcEwyIDwgMTAwXQ0KIyBXaGF0IGFyZSB0aGUgbmFtZXMgb2YgbGFuZ3VhZ2VzIHdpdGggU1ZPIG9yZGVyIGFuZCBMMiBsYW5ndWFnZSBwb3B1bGF0aW9uIG1vcmUgdGhhbiAxMDAgbWlsbGlvbj8NCmxhbmdfZGYkbGFuZ19uYW1lW2xhbmdfZGYkbGFuZ19vcmRlciA9PSAiU1ZPIiAmIGxhbmdfZGYkbGFuZ19wb3BMMiA+IDEwMF0NCmBgYA0KDQo2LiBDb3VudGluZyB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBzYXRpc2Z5aW5nIGEgc3BlY2lmaWMgY3JpdGVyaWEgDQoNCmBgYHtyfQ0KIyBIb3cgbWFueSBsYW5ndWFnZXMgaGF2ZSBTVk8gb3JkZXI/IFdoYXQgaXMgdGhlIHByb3BvcnRpb24/DQpzdW0obGFuZ19kZiRsYW5nX29yZGVyID09ICJTVk8iKQ0KbWVhbihsYW5nX2RmJGxhbmdfb3JkZXIgPT0gIlNWTyIpDQojIEhvdyBtYW55IGxhbmd1YWdlcyBoYXZlIEwyIHBvcHVsYXRpb24gZ3JlYXRlciB0aGFuIDIwMCBtaWxsaW9uPyBXaGF0IGlzIHRoZSBwcm9wb3J0aW9uPw0Kc3VtKGxhbmdfZGYkbGFuZ19wb3BMMiA+IDIwMCkNCm1lYW4obGFuZ19kZiRsYW5nX3BvcEwyID4gMjAwKQ0KYGBgDQo=