Installing and loading packages

You only need to install packages once, but you need to load them for every new R session.
You should do this in the first chunk in your R markdown document.

installing install.packages("package_name")
loading library(package_name)

The main package we use is the tidyverse.
So you should start all your .Rmd with a chunk of code containing library(tidyverse) (and any other libaries/packages you may be using).

Reading in data

The function you use depends on the type of file being read:

comma separated values read_csv("csv_file.csv")
tab separated values read_tsv("tsv_file.txt")
other separators read_delim("anyfile.txt",sep=";")
for non utf-8 encodings read_csv("csv_file.csv",locale = locale(encoding = "Shift_JIS"))
if you don’t know the encoding guess_encoding("csv_file.csv")

Always use plain text formats (.csv or .txt files) and not excel (.xlsx) files.
Use UTF-8 encoding (especially if you have non-roman characters!).
The read_ functions assume your encoding is UTF-8. But sometimes you get data from other people that isn’t UTF-8, then you must specify the encoding with the locale argument (you can use it in any read function). If you don’t know the encoding, guess_encoding will guess it for you.
For how to convert excel documents to plain text formats, and change the encoding to UTF-8, see the notes from Lecture 1.

Exploring datasets

Examples with the dataset cars, that comes with R.

to see… use…
the first few rows head(cars)
the number of columns/variables length(cars)/ncol(cars)
the number of rows nrow(cars)
unique rows unique(cars)
unique vals in column unique(cars$speed)
range of numbers in a column range(cars$speed)
min value in column min(cars$speed)
max value in column max(cars$speed)

Data wrangling

Dataframes

It is easier if you do this using pipes. The pipe function is %>%.
The pipe function tells R to use the output from the previous function as input to the next function.
It means you don’t have to keep typing the name of the dataset every time you use the function.
To save the output from a pipe, add an assignment operator -> at the end, or <- at the beginning. Below are two ways of doing the same thing:

cars%>%
  filter(speed<4)%>%
  select(speed,dist)->slow_cars
  
slow_cars <- cars%>%
               filter(speed<4)%>%
               select(speed,dist)
to… use Notes/Examples
filter rows cars%>%filter(speed>2)
select columns cars%>%select(speed,dist)
get only unique rows cars%>%unique()
sample i random rows cars%>%sample_n(i)
pull out a single column cars%>%pull(speed) You can use the vectors functions
in the next section on this column
arrange by a column, in ascending order cars%>%arrange(speed)
arrange by a column, in descending order cars%>%arrange(-speed)
rename a column, oldname, to newname cars%>%rename(newname=oldname
add a new column, newcol cars%>%mutate(newcol= ...) perform some operations to generate data for newcol in … (see the Vectors section for ideas)
refer to the variable being piped . cars%>%mutate(ID=1:nrow(.)) where . is cars
categorise data cars%>%mutate(cat=if_else(c,v1,v2) cars%>%
mutate(speed_cat=if_else(speed<5,‘slow’,‘fast’) # for > 2 categories, write a function to categorise them
add new values, working on groups within columns mutate() with [] cars%>%
mutate(slow_dist=sum(dist[speed_cat==‘slow’]),fast_dist=sum(dist[speed_cat=‘fast’])
add new values, working on groups within columns mutate() with group_by() cars%>%
group_by(speed_cat)%>%
mutate(total_dist=sum(dist))
calculate summary statistics over columns summarise() cars%>%
summarise(mean(speed))
calculate summary statistics over groups within columns group_by() + summarise() cars%>%
group_by(speed_cat)%>%
summarise(mean(speed)
calculate on each row separately rowwise() nettle%>%
rowwise()%>%
mutate(environment=MGS_categorise(MGS))
add data from datB to datA, matching on colA + colB left_join(datA,datB,by=c("colA","colB")) rows in datB that don’t have a match in datA will be lost.
right_join(datB,datA,by=c("colA","colB")) rows in datB that don’t have a match in datA will be lost.
combine ALL data in datA and datB full_join(datA,datB,by="colA") the by argument is optional; use it to specify columns that you want merged. All rows in both datasets are kept.
get ONLY data in both datA and datB inner_join(datA,datB,by="colA") rows that have a value in colA in one dataset, but not the other, will be lost
make data wider (add more columns) dat%>%pivot_wider(names_from=colA,values_from=colB,values_fill=0 Names for the new cols come from colA, and values to fill them come from colB. Use values_fill to specify how NAs should be represented (default is NA)
make data longer (remove columns) dat%>%pivot_longer(c("colA","colB"),names_to="colD",values_to="colE" colA + colB are collapsed into a new col, colD, and their values will go to a new col, “colE”

Vectors

You can try out these functions yourself; the cars and iris datasets both come with R.

to… use
sort items (alphabetically or numerically) sort(cars$speed)
add 10 to every item in a vector cars$speed + 10
see how many values there are length(cars$speed)
add up all the values (for numbers) sum(cars$speed)
count the values (for characters) count(iris,species)
get an idea of ‘typical’ values mean(cars$speed)
get an idea of ‘typical’ values when you have outliers median(cars$speed)
estimate the ‘spread’ (deviation) around this typical value sd(cars$speed)
get the max value max(cars$speed)
get the min value min(cars$speed)
get the range of values range(cars$speed)
get only unique values unique(cars$speed)
get the ith item in the vector cars$speed[i]
remove the ith item from the vector cars$speed[-i]
get all items between i and j cars$speed[i:j]
remove all items between i and j cars$speed[-(i:j)]
sample n random values sample(cars$speed,n)
check if a character is in a vector "chr" %in% vector
check if a character is NOT in a vector !("chr" %in% vector)
check if a number is in a vector num %in% vector

Characters/Strings

Use the package stringr (it comes with the tidyverse, so you shouldn’t have to load it separately)

to… use… e.g.
subset a string str_sub(str,start,stop) str_sub("Wednesday",1,3) = “Wed”
add - to start from the end str_sub("Wednesday",-3,-1) = “day”
convert to lowercase str_to_lower() str_to_lower("Bob") = “bob”
convert to uppercase str_to_upper() str_to_upper("bob") = “BOB”
convert to title case str_to_title() str_to_title("bob") = “Bob”
find and replace str_replace(str,find,replace) str_replace("fun","f","p")=“pun”
remove leading/trailing whitespace str_trim(str) str_trim(" hello ")=“hello”

The string_r functions are most powerful when you combine them with regular expressions. Below are some of the most useful ones:

Regular expression Meaning
“.” any character
".*" any number of any character
“\b” a word boundary
“\d” any number (digit)
“[:punct:]” punctuation marks

Important: Because characters like “.” have a special meaning in regular expressions, if you want to search for a literal period (.), you will need to use “\.”. The two slashes are called escape characters, they tell R not to treat whatever follows as a regular expression.

You can find all the regular expressions on the cheatsheet here.

Functions and Loops

  • Use these when you want to run the same code many times on different things.

Syntax for functions:

function_name <- function(inputs,inputs,inputs){
# do stuff
.
.
.
return(output)
}

Syntax for loops:

for(A in B){
  # do something
}

Usage example:

# example of a function to get the hypotenuse of a triangle from the short and middle side

get_hypotenuse <- function(shortside, middleside) { 
  # shortside and middleside are the inputs (arguments)
  hypotenuse_squared<-shortside**2+middleside**2
  hypotenuse<-sqrt(hypotenuse_squared)
  return(hypotenuse)# hypotenuse is the output
}

shortsides <- c(3,4,5,8,1,3)
middlesides <- c(8,10,12,14,2,1)

# now lets run it multiple times in a loop

# make an empty vector to store the hypotenuses
hypotenuses <- c()

# we use these 'i' values as indexes to refer to items in our two vectors shortsides and middlesides
for(i in 1:length(shortsides)){
  # run our function
  hypotenuse <- get_hypotenuse(shortsides[i],middlesides[i])
  # update the hypotenuse vector, so it contains the hypotenuses we've already calculated, plus the one we just calculated in this iteration of the loop
  hypotenuses <- c(hypotenuses,hypotenuse)
}

# after the loop the hypotenuses vector will be full 

Categorising functions

A function for categorising the environment, based on the length of the maximum growing season (mgs).

environment <- function(mgs) {
  if(mgs < 4) {
    return("dry")
  } else if(mgs < 8) {
    return("typical")
  } else {
    return("fertile")
  }
}

Conditions

Expression Meaning
X==Y X equals Y
X!=Y X is not equal to Y
X %in% Y X is in Y
!(X %in% Y) X is not in Y
X<Y X is less than Y
X>Y X is greater than Y
X>=Y X is greater than or equal to Y
X<= Y X is less than or equal to Y

if, else if, else statements

Only the block of code for the FIRST condition that evaluates to TRUE will be run; so the order of your conditions matters.

Data visualisation

We use ggplot(). These plots have a basic structure like this:

data %>% ggplot(aes(x=colA,y=colB,colour=colC)) +geom_point()/geom_line()/geom_col()

Optional extras:

  • +labs(x="X axis title",y="Y axis title",colour="Legend title",title="Plot title",caption="Caption for plot") [if you use fill instead of colour, then you will use fill="Legend title"]
  • +theme_classic() # this is a nice theme
  • If you are printing in black and white, you can use shape instead of colour

When to use which type of graph?

If you have a lot of x values:

  • geom_point() is good when your observations come from different sources, e.g. the number of covid deaths in people of different ages, and you want to communicate the relationship between your x and y values
  • geom_line() and geom_smooth() are better for plotting multiple observations from a single source, e.g. the number of covid cases in a country over multiple weeks, where you want to communicate the change in your y value over time

If you have only a few x values:

  • geom_col() is good for when you have only one y observation per x value
  • geom_violin() is good when your x values are larger categories with multiple y observations per x value, and you want to compare the distribution of the y values between the different groups

geom_histogram() is used to examine the distribution of a single variable. It’s more used in the methods section of papers rather than the results, for example to show what your sample of data looks like.

geom_histogram()

Below is a histogram showing the distribution of diamonds in the diamond dataset by their carat.

diamonds%>%
       # just put the variable you want to examine the distribution of
  ggplot(aes(carat))+
  geom_histogram()+
  theme_classic()+
  labs(title="Distribution of diamonds by carat")

Most of the diamonds in the dataset are less than 1 carat.

geom_point()

With colours:

MGS_nettle%>%ggplot(aes(x=Population,y=Langs,colour=MGS_category))+
  geom_point()+
  labs(x='Population (in millions)',y='Number of languages',colour="Environment")+
  theme_classic()   # a nicer presentation than the default ggplot theme

With shapes:

MGS_nettle%>%ggplot(aes(x=Population,y=Langs,shape=MGS_category))+
  geom_point()+
  labs(x='Population (in millions)',y='Number of languages',shape="Environment")+
  theme_classic()   # a nicer presentation than the default ggplot theme

Adding labels to points

geom_text() and geom_text_repel() can be used to add labels to points on your plot. You will need to install and load the library ggrepel to use geom_text_repel, which positions the labels nicely so they don’t overlap with anything, and you’ll need to add an argument label to your aes() function in ggplot(), to specify which column to get the labels from.

library(ggrepel)
MGS_nettle%>%
  sample_n(15)%>%
  ggplot(aes(x=Population,y=log10(Langs),label=Country)) + geom_point(aes(colour=MGS_category)) + geom_text_repel()+theme_classic()

geom_line() and geom_smooth()

Use geom_line() for a jagged line:

weird_names%>%
  ggplot(aes(x=year, y=TotalN)) + 
  geom_line()+
labs(x='Year',y='Total number of weird names',title='Popularity of weird names over time')+
  theme_classic()

Use geom_smooth() to get a smoothed line:

weird_names%>%
  ggplot(aes(x=year, y=TotalN)) + 
  geom_smooth()+
labs(x='Year',y='Total number of weird names',title='Popularity of weird names over time')+
  theme_classic()

geom_col() - bar plots

barnnamn%>%
  filter(name=='Lee')%>%
  ggplot(aes(x=year,y=n))+geom_col()+
  labs(title="Babies named Lee",x="Year",y="Number of babies")+
  scale_x_continuous(breaks=c(2010:2017))+  # to specify the breaks on the x-axis
  theme_classic()

Reordering items on the x-axis

  • add a reorder() function to the aes() for the x-axis
  • works the same way as arrange()
MGS_nettle%>%
  sample_n(6)%>%
  ggplot(aes(x=reorder(Country,Langs),y=Langs,fill=MGS_category))+
  geom_col()+
  labs(x='Country',y='Number of languages')+
  scale_fill_discrete("Environment",c("dry","fertile"))+
  theme_classic()

Grouped bar plots

  • Use fill=VarX in the aes() of your ggplot call to colour the bars by a third variable.
  • Use position="dodge()" in your geom_col() to have the bars side-by-side
  • Use scale_x_continuous (for continuous variables) to specify the breaks on the x-axis; if you have discrete (e.g. categorical) variables, the function is scale_x_discrete
barnnamn%>%
  filter(name=='Lee')%>%
  ggplot(aes(x=year,y=n,fill=sex))+
  geom_col(position="dodge")+  # bars side-by-side
  labs(title="Babies named Lee",x="Year",y="Number of babies",fill="Sex")+
  scale_x_continuous(breaks=c(2010:2017))+  # to specify the breaks on the x-axis
  theme_classic()

If you don’t use position="dodge", the different coloured bars will be stacked on top of eachother.

barnnamn%>%
  filter(name=='Lee')%>%
  ggplot(aes(x=year,y=n,fill=sex))+
  geom_col()+  
  labs(title="Babies named Lee",x="Year",y="Number of babies",fill="Sex")+
  scale_x_continuous(breaks=c(2010:2017))+  # to specify the breaks on the x-axis
  theme_classic()

Percent stacked bar plots

Use position="fill" to show proportions instead of counts:

barnnamn%>%
  filter(name=='Lee')%>%
  ggplot(aes(x=year,y=n,fill=sex))+
  geom_col(position="fill")+  # to show proportions 
  labs(title="Babies named Lee",x="Year",y="Proportion",fill="Sex")+
  scale_x_continuous(breaks=c(2010:2017))+  # to specify the breaks on the x-axis
  theme_classic()

geom_violin()

This is good to compare the distribution of the data between groups.

# mpg is a dataset of cars
mpg %>%
  # the hwy column tells you how many miles a car can go on 1 gallon of petrol
  # class is the type of car
  ggplot( aes(x=reorder(class,hwy), y=hwy)) + 
  geom_violin() +
  theme_classic()+
# we can use the stat_summary point to add points for summary statistics, here we show the mean
  stat_summary(fun=mean, geom="point")+
  labs(x="Car type",y="miles per gallon",title="Efficiency of cars on the highway",caption = "The mean miles per gallon for each car is indicated with a point")  

Making data points less clumped or less spread apart

  • Log transformations (as in the graph above) are used to make data that is very spread apart/clumped together easier to visualise.
  • The most common base is 10. When we take the log10 of a number, \(x\), we are trying to find a number \(y\) such that \(10^y=x\). The function for this in R is just log10(x). For very far apart values of \(x\), taking \(log10(x)\) will give you values that are closer together, while for very close together values of \(x\), \(log10(x)\) will give you values that are further apart. This means that \(log10(x)\) is often nicer to plot than \(x\).
  • It is perfectly acceptable to do these kinds of transformations to your data, in order to make it easier to visualise.

Making multiple plots

facet_wrap() and facet_grid() let you produce multiple different plots for each value in a column/columns

  • facet_wrap(~ColA) –> will make different versions of the same graph for every different value in colA, all wrapped around each other (works well when you have a lot of different values of a variable)
  • facet_grid(colA~colB) –> makes a grid of graphs, where the different values of colA are the rows in the grid, and the different values of colB are the columns in the grid. This works best when colA and colB don’t have too many different values.
  • +theme_minimal() is a nice theme to use with faceted plots (compared to our usual +theme_classic())
barnnamn %>% filter(name=="Lee") %>% ggplot(aes(x=year, y=n)) + geom_line() + facet_wrap("sex")+theme_minimal()

barnnamn %>% 
  mutate(FinalLetter = str_sub(name, -1, -1))  %>%  
  mutate(Final = if_else(FinalLetter %in% c("a",'ä','ö','å', "e", "i", "o", "u", "y"), "vowel", "consonant")) %>% 
  group_by(Final,year,sex) %>% 
  summarise(total=sum(n))  %>% 
  ggplot(aes(x=year, y=total)) + geom_line() + facet_grid(sex ~ Final)+theme_minimal()

Changing the number and look of ticks on the x axis

You can add more ticks to a continuous x-axis using the function +scale_x_continuous(n.breaks=num_ticks). The number of ticks that you enter won’t always be the exact number of labels it makes, but it will try to get it as close as possible while still ensuring nice break labels.

We can also make the break labels nicer by changing their angle and position. This is done with the function +theme(axis.text.x = element_text(angle=45,vjust=0.5)), and again, just play around with the values for angle and vjust (vertical adjustment) until you get something that looks right. If vjust doesn’t work for you, there is also an argument hjust (horizontal adjustment) that you can add.

barnnamn %>% 
  mutate(FinalLetter = str_sub(name, -1, -1))  %>%  
  mutate(Final = if_else(FinalLetter %in% c("a",'ä','ö','å', "e", "i", "o", "u", "y"), "vowel", "consonant")) %>% 
  group_by(Final,year,sex) %>% 
  summarise(total=sum(n))  %>% 
  ggplot(aes(x=year, y=total)) + geom_line() + facet_grid(sex ~ Final)+theme_minimal()+scale_x_continuous(n.breaks=16)+theme(axis.text.x = element_text(angle=45,vjust=0.5))

Mapping

We can get maps from the packages rnaturalearth and rnaturalearthdata, and plot them using the ggplot function geom_sf(). sf is a file format for storing maps.

# required packages
library(rnaturalearth)
library(rnaturalearthdata)
library(ggrepel)

# these packages may not function properly if you don't also have rgeos installed; if you get an error, run install.packages('rgeos') in your console and then try again

# the function ne_countries will give you country maps, if you don't specify which countries, it gives you the whole world. 
# Use returnclass="sf" to make sure you get an sf format back, as this is what geom_sf() works with
world <- ne_countries(returnclass = "sf")

# you need to have latitude and longitude information for the things you want to plot -- I get this from google maps -- just right click on a place in google maps (on a computer), and the latitude and longitude will show up and you can copy them
type <- c("rental","parent's house","in-law's house","rental","rental")
latitude <- c(59.865,-33.879,50.874,35.664,-35.284)
longitude <- c(17.641,151.112,6.037,139.482,149.136)
places <- data.frame(type,latitude,longitude)

# start by plotting the map as a base layer
# this always has to come first -- it won't work if you start with the points and try to add the map after
world%>%
  ggplot()+
  geom_sf()+
  # then you can add points on top of it -- note that you need to specify the data, because this comes from a different dataset to the one at the top of the pipe
  geom_point(data = places,aes(x=longitude,y=latitude,colour=type))+
  theme_void()+    # this is a nice theme for maps; it gets rid of the axes
  labs(title="Places I've Lived")

You can use the ‘country’ argument in ne_countries() to get a map of a single country.


Australia <- ne_countries(country="Australia",returnclass = "sf")

australian_places <- places%>%filter(latitude<0)

Australia%>%
  ggplot()+
  geom_sf()+
  # since the data isn't coming from the top of the pipe, you need to tell it the data and aesthetics
  geom_point(data = australian_places,aes(x=longitude,y=latitude))+
  # you could use labels instead of colour -- but again you need to tell it the aesthesics and data to use
  geom_text_repel(data = australian_places,aes(x=longitude,y=latitude,label=type))+
  labs(title="Places I've Lived")+
  theme_void()

You can add multiple countries if you like:

map <- ne_countries(country=c("Australia","New Zealand"),returnclass = "sf")

australian_places <- places%>%filter(latitude<0)

map%>%
  ggplot()+
  geom_sf()+
  # since the data isn't coming from the top of the pipe, you need to tell it the data and aesthetics
  geom_point(data = australian_places,aes(x=longitude,y=latitude))+
  # you could use labels instead of colour -- but again you need to tell it the aesthesics and data to use
  geom_text_repel(data = australian_places,aes(x=longitude,y=latitude,label=type))+
  labs(title="Places I've Lived")+
  theme_void()

Or you can use the argument continent to specify an entire continent:

Asia <- ne_countries(continent="Asia",returnclass = "sf")
Asia%>%
  ggplot()+
  geom_sf()+
  theme_void()

A full list of available countries and continents is shown below:

library(reactable)
data <- countries50
countries <- data@data
countries%>%
  select(name,continent)%>%
  unique()%>%
  arrange(continent,name)%>%
  rename(country=name)%>%
  reactable(searchable = TRUE,filterable = TRUE)

Visualing variation across multiple variables

If you have 3 or 4 variables, I would try to visualise the third and fourth variables by using features like colour, or by making multiple plots (with factet_wrap() and facet_grid()).

However, if you have a whole bunch of variables that are all related/form a cohesive set, and you want to get an idea of how similar or different items are overall, based on their values for all these variables, you can do this by creating a distance matrix. A distance matrix shows the overall distances between all the items in your data, by taking the sum of their distances based on each variable (for more explanation see the notes from lecture 8).

Note that this only works if your data is numerical, or if it has been binarised. For numerical data, make sure you normalise the data so that values of each variable are comparable between different items (e.g. instead of using raw counts, use proportions).

There are two different kinds of visualisations you can make from a distance matrix.

  1. You can attempt to plot these distances, by transforming them into 2 or 3 dimensional coordinates, and plotting the items as points along these dimensions.
  2. You can cluster items that are close to eachother together repeatedly, until you get a branching tree.

The first method is called multidimensional scaling, and the second is called cluster analysis.

Multidimensional scaling works well if the data is easily reduced to two or three dimensions, but this isn’t always possible. Cluster analysis works better when you have complicated data which varies along multiple dimensions/with many distinct groups.

Multidimensional scaling

  1. Convert the data into a distance matrix–make sure it is appropriately normalised/binarised first–using the function dist(). Add method="binary" if the data is binarised. If it’s numerical, the default method (euclidean) is appropriate.
  2. Find coordinates for these distances using cmdscale(distances, k=2). k sets the number of dimensions to use; the default is two.
  3. cmdscale() will make coordinates for your distances as well as it can, but it can’t always represent the distances perfectly, so you should check how closely the distances between the coordinates matches the actual distances. This is called calculating the stress on the coordinates, and we will make our own stress() function to do this.
# d is the original distances, D is the distances between the coordinates made by cmdscale()
stress <- function(d, D){
  sqrt(sum((d - D) ^ 2) / sum(d ^ 2))
}

This is how stress values are interpreted:

Value Interpretation
Stress >= 0.2 Poor
0.1 <= Stress < 0.2 Fair
0.05 <= Stress < 0.1 Good
Stress < 0.05 Excellent
  1. If the stress is okay, then you can plot the coordinates in two dimensions. If it’s not okay, then probably a three-dimensional or higher solution is needed to accurately represent the data. You can check how many dimesions are needed by playing around with different values of k in the cmdscale() function, and then checking the stress on those coordinates (see notes from lecture 8).

You will also need the functions column_to_rownames() and as_dataframe(rownames="column_name") to go between the data format required by dist(), and that needed for plotting.

Below is an example using euclidean distances (i.e. with numerical data), to visualise distances between different Japanese case particles in terms of their functions.

library(ggrepel)
There were 26 warnings (use warnings() to see them)
# toy data about the frequency of use (as a proportion of the total frequency for each particle) with different cases for some Japanese particles
particle <- c("ga","ni","de","wa","o")
nominative <- c(0.95,0,0,0.5,0)
genitive <- c(0.05,0,0,0,0)
locative <- c(0,0.6,0.7,0.1,0)
instrumental <- c(0,0,0.3,0.05,0)
allative <- c(0,0.3,0,0.1,0)
accusative <- c(0,0.1,0,0.35,1)

particles <- data.frame(particle,nominative,genitive,locative,instrumental,allative,accusative)

# 1. Make the distance matrix
particles%>%
  # turn the first column into rownames first -- your columns should only contain measurements of your variables of interest
  column_to_rownames("particle")%>%
  dist()-> distances

# 2. Make coordinates
                                   # use k=3, 4 etc. for higher-dimensional solutions
coordinates <- cmdscale(distances,k=2)

# 3. Check stress on coordinates

stress(distances,dist(coordinates))
[1] 0.09332946

Here, the stress is less than 0.1 so the two-dimensional coordinates represent the distances well, and the data can be easily plotted

# 3. Plot coordinates

coordinates%>%
        # put the rownames into a column first
  as_data_frame(rownames="particle")%>%
  ggplot(aes(x=V1,y=V2,label=particle))+geom_point()+geom_text_repel()+theme_classic()->plot1
plot1

You can see that de and ni have similar functions, as they group together, while ga and o have opposite functions, with wa falling in between ga and o, but closer to ga. This is because de and ni both have to do with location, while ga is nominative and o is accusative. But wa can take both nominative and accusative case, because it’s a topic marker–that’s why it falls in between ga and o. However, nominatively marked nouns are more often topics than accusatively marked nouns, which is why it is closer to ga than to o.

Below is an example with binary distances, using cognacy data to determine similarities between different dialects of Japanese.

japonic <- read_csv("data/japvocabmatrix.csv")

japonic%>%
  rename(language=X)%>%
  # excluded dead languages and Okinawa because it is not a dialect and will mess up the scale on the map by being super far away
  filter(language!="Old_Japanese",language!="Middle_Japanese",language!="Okinawa")%>%
  column_to_rownames("language")%>%
  dist(method = "binary")->distances

coordinates <- cmdscale(distances,k=2)

coordinates%>%
  as_data_frame(rownames="Location")%>%
  ggplot(aes(x=V2,y=-V1,label=Location))+
  geom_point()+
  geom_text_repel()+
  theme_classic()+
  labs(x="Conservative versus progressive",y="South to north")

Here, instead of having distinct groups, we actually have a continuum which we can label as reflecting the spatial location (south to north) on the one hand, as well as the conservativeness of the dialect on the other hand–if you know about Japanese dialectology.

The stress on this representation is actually fairly high

stress(distances,dist(coordinates))
[1] 0.5535626

However, a three-dimensional solution is only marginally better:

higher_solution <- cmdscale(distances,k=3)
stress(distances,dist(higher_solution))
[1] 0.4809971

Here, the high stress value is likely because we have a lot of languages, so it may be that it is hard to get the correct positioning of the languages within each small group. That is, the spatial relationship between languages that are close to eachother may not be very accurate. However, the main dialect groups and the large distances in this figure are well-represented (based on what we already know about Japonic), so I think it is still a good representation–as long as you warn people not to look at the small distances. If this two-dimensional representation was really a bad represenation, we would expect a bigger difference between the stress levels of a two-dimensional versus three-dimensional solution.

Cluster analysis

If you want to know about the close relationships (small distances) as well, you could use a cluster analysis

clusters <- hclust(distances,method="ward.D2")   # method="ward.D2" usually produces good results
plot(clusters)

Compared to multidimensional scaling, cluster analyses are better at showing distinct groups when you have a lot of data points. From the first two splits in the tree here, we already have four distinct groups–they basically represent the north and east of country, versus the south and west of the country. However, in the multidimensional scaling representation the western and eastern groups ended up squished together (it was hard to clearly separate them), so that could also contribute to high stress levels. This is probably because the eastern and western groups are less distinct from eachother than the northern and southern groups (which were preserved), and like we said the multidimensional scaling representation just shows the general trends and not the small differences.

So, if I were to present this data, I would include both the multidimensional scaling analysis (to show the overall trends), as well as the cluster analysis (to show distinct, including smaller-level, groupings).

When to use which method?

If you only have a few data points (like in the first example with the Japanese particles), multidimensional scaling and cluster analyses are usually equally good at representing the distances, because there’s enough space on the multidimensional scaled plot to keep all the groups distinct. So it’s just a matter of personal preference as to which visualisation you prefer, although if you think the data is hierarchically structured the cluster analysis would represent this hierarchical structure better.

If you have a lot of data points, however, it is hard to keep groups distinct on multidimensional scaling plots (as we saw with the Japanese data). In this case, the cluster analysis is better for showing the groupings, and it is only worth still using the multidimensional scaling plot if you think the dimensions are meaningful and represent some overall trends in the data (e.g. the north-south, conservative-progressive trends we saw in the Japanese data). If you cannot think of any meaningful labels for your dimensions, then probably all the multidimensional scaling analysis is doing is trying to represent distinct groups–rather than any kind of continuum–in which case I would just use a cluster analysis because that does grouping better when there’s lots of data points.

For instance, below is an example, plotting words based on their sensory modality ratings:

norms <- read_csv("data/lynott_connell_2009_modality.csv")

norms%>%
  select(-PropertyBritish,-DominantModality)%>%
  column_to_rownames("Word")%>%
  sample_n(50)%>%
  dist()->distances

coordinates <- cmdscale(distances)

coordinates%>%
  as_data_frame(rownames="Word")%>%
  ggplot(aes(x=V1,y=V2,label=Word))+ 
  geom_point()+
  geom_text_repel()+
  theme_classic()

There groupings are not very clear, nor can we come up with any meaningful labels for these dimensions. In this case, I would just use a cluster analysis because at least then the groupings are clear:

clusters <- hclust(distances,method="ward.D2")   # method="ward.D2" usually produces good results
plot(clusters)

We can see that the groupings are not well preserved in the multidimensional scaling plot from the high stress on the coordinates:

stress(distances,dist(coordinates))
[1] 0.2605559

A three-dimensional solution is able to represent the groupings better:

newcoordinates <- cmdscale(distances,k=3)
stress(distances,dist(newcoordinates))
[1] 0.09953989

We could extract the different groupings by plotting the dimensions one after the other... But this is a lot more work (and still it’s easier to see the groups in the cluster dendrogram), so I would just use the cluster analysis instead. I would only try plotting all the dimensions if you think they’re all meaningful.

plot(clusters)

Dealing with Errors

Error Possible explanation/fix
“Could not find function” You haven’t loaded (or installed) the library for the function you’re trying to use, or you’ve spelt the function name wrong!
“object ‘blah’ not found” You have a typo with your variable name ‘blah’, i.e. you’ve spelt blah wrong somewhere.

Getting help

  • If you want to know more about any function, if you type ?function_name, R will show you information about how the function works in the bottom right pane
  • If you still need help, try searching for your question on stack exchange or just google also usually works :)
  • There are also the R cheatsheets
  • And of course, also ask your question in the relevant discussion board on studium!
LS0tDQp0aXRsZTogIlIgQ2hlYXRzaGVldCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgDQotLS0NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSx3YXJuaW5nID0gRkFMU0UsbWVzc2FnZSA9IEZBTFNFKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJhcm5uYW1uKQ0KYGBgDQoNCg0KIyBJbnN0YWxsaW5nIGFuZCBsb2FkaW5nIHBhY2thZ2VzDQoNCllvdSBvbmx5IG5lZWQgdG8gaW5zdGFsbCBwYWNrYWdlcyBvbmNlLCBidXQgeW91IG5lZWQgdG8gbG9hZCB0aGVtIGZvciBldmVyeSBuZXcgUiBzZXNzaW9uLiAgIA0KWW91IHNob3VsZCBkbyB0aGlzIGluIHRoZSBmaXJzdCBjaHVuayBpbiB5b3VyIFIgbWFya2Rvd24gZG9jdW1lbnQuDQoNCnwgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwqaW5zdGFsbGluZyp8YGluc3RhbGwucGFja2FnZXMoInBhY2thZ2VfbmFtZSIpYHwNCnwqbG9hZGluZyogICB8YGxpYnJhcnkocGFja2FnZV9uYW1lKWAgICAgICAgICAgIHwNCg0KVGhlIG1haW4gcGFja2FnZSB3ZSB1c2UgaXMgdGhlIGB0aWR5dmVyc2VgLiAgIA0KU28geW91IHNob3VsZCBzdGFydCBhbGwgeW91ciAuUm1kIHdpdGggYSBjaHVuayBvZiBjb2RlIGNvbnRhaW5pbmcgYGxpYnJhcnkodGlkeXZlcnNlKWAgKGFuZCBhbnkgb3RoZXIgbGliYXJpZXMvcGFja2FnZXMgeW91IG1heSBiZSB1c2luZykuDQoNCiMgUmVhZGluZyBpbiBkYXRhDQoNClRoZSBmdW5jdGlvbiB5b3UgdXNlIGRlcGVuZHMgb24gdGhlIHR5cGUgb2YgZmlsZSBiZWluZyByZWFkOg0KDQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMgICAgICAgIHxgcmVhZF9jc3YoImNzdl9maWxlLmNzdiIpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8dGFiIHNlcGFyYXRlZCB2YWx1ZXMgICAgICAgICAgfGByZWFkX3RzdigidHN2X2ZpbGUudHh0IilgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnxvdGhlciBzZXBhcmF0b3JzICAgICAgICAgICAgICB8YHJlYWRfZGVsaW0oImFueWZpbGUudHh0IixzZXA9IjsiKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfGZvciBub24gdXRmLTggZW5jb2RpbmdzICAgICAgIHxgcmVhZF9jc3YoImNzdl9maWxlLmNzdiIsbG9jYWxlID0gbG9jYWxlKGVuY29kaW5nID0gIlNoaWZ0X0pJUyIpKWB8DQp8aWYgeW91IGRvbid0IGtub3cgdGhlIGVuY29kaW5nfGBndWVzc19lbmNvZGluZygiY3N2X2ZpbGUuY3N2IilgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KQWx3YXlzIHVzZSAqKnBsYWluIHRleHQgZm9ybWF0cyoqICguY3N2IG9yIC50eHQgZmlsZXMpIGFuZCBub3QgZXhjZWwgKC54bHN4KSBmaWxlcy4gIA0KVXNlICoqVVRGLTggZW5jb2RpbmcqKiAoZXNwZWNpYWxseSBpZiB5b3UgaGF2ZSBub24tcm9tYW4gY2hhcmFjdGVycyEpLiAgDQpUaGUgYHJlYWRfYCBmdW5jdGlvbnMgYXNzdW1lIHlvdXIgZW5jb2RpbmcgaXMgVVRGLTguIEJ1dCBzb21ldGltZXMgeW91IGdldCBkYXRhIGZyb20gb3RoZXIgcGVvcGxlIHRoYXQgaXNuJ3QgVVRGLTgsIHRoZW4geW91IG11c3Qgc3BlY2lmeSB0aGUgZW5jb2Rpbmcgd2l0aCB0aGUgYGxvY2FsZWAgYXJndW1lbnQgKHlvdSBjYW4gdXNlIGl0IGluIGFueSByZWFkIGZ1bmN0aW9uKS4gSWYgeW91IGRvbid0IGtub3cgdGhlIGVuY29kaW5nLCBgZ3Vlc3NfZW5jb2RpbmdgIHdpbGwgZ3Vlc3MgaXQgZm9yIHlvdS4gIA0KRm9yIGhvdyB0byBjb252ZXJ0IGV4Y2VsIGRvY3VtZW50cyB0byBwbGFpbiB0ZXh0IGZvcm1hdHMsIGFuZCBjaGFuZ2UgdGhlIGVuY29kaW5nIHRvIFVURi04LCBzZWUgdGhlIG5vdGVzIGZyb20gTGVjdHVyZSAxLg0KDQojIEV4cGxvcmluZyBkYXRhc2V0cw0KDQpFeGFtcGxlcyB3aXRoIHRoZSBkYXRhc2V0IGBjYXJzYCwgdGhhdCBjb21lcyB3aXRoIFIuDQoNCnwqdG8gc2VlLi4uKiAgICAgICAgICAgICAgICAgICAgfCp1c2UuLi4qICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnx0aGUgZmlyc3QgZmV3IHJvd3MgICAgICAgICAgICAgfGBoZWFkKGNhcnMpYCAgICAgICAgICAgICAgIHwNCnx0aGUgbnVtYmVyIG9mIGNvbHVtbnMvdmFyaWFibGVzfGBsZW5ndGgoY2FycylgL2BuY29sKGNhcnMpYHwNCnx0aGUgbnVtYmVyIG9mIHJvd3MgICAgICAgICAgICAgfGBucm93KGNhcnMpYCAgICAgICAgICAgICAgIHwNCnx1bmlxdWUgcm93cyAgICAgICAgICAgICAgICAgICAgfGB1bmlxdWUoY2FycylgICAgICAgICAgICAgIHwNCnx1bmlxdWUgdmFscyBpbiBjb2x1bW4gICAgICAgICAgfGB1bmlxdWUoY2FycyRzcGVlZClgICAgICAgIHwNCnxyYW5nZSBvZiBudW1iZXJzIGluIGEgY29sdW1uICAgfGByYW5nZShjYXJzJHNwZWVkKWAgICAgICAgIHwNCnxtaW4gdmFsdWUgaW4gY29sdW1uICAgICAgICAgICAgfGBtaW4oY2FycyRzcGVlZClgICAgICAgICAgIHwNCnxtYXggdmFsdWUgaW4gY29sdW1uICAgICAgICAgICAgfGBtYXgoY2FycyRzcGVlZClgICAgICAgICAgIHwNCg0KIyBEYXRhIHdyYW5nbGluZw0KDQojIyBEYXRhZnJhbWVzDQoNCkl0IGlzIGVhc2llciBpZiB5b3UgZG8gdGhpcyB1c2luZyBwaXBlcy4gVGhlIHBpcGUgZnVuY3Rpb24gaXMgYCU+JWAuICANClRoZSBwaXBlIGZ1bmN0aW9uIHRlbGxzIFIgdG8gdXNlIHRoZSBvdXRwdXQgZnJvbSB0aGUgcHJldmlvdXMgZnVuY3Rpb24gYXMgaW5wdXQgdG8gdGhlIG5leHQgZnVuY3Rpb24uICANCkl0IG1lYW5zIHlvdSBkb24ndCBoYXZlIHRvIGtlZXAgdHlwaW5nIHRoZSBuYW1lIG9mIHRoZSBkYXRhc2V0IGV2ZXJ5IHRpbWUgeW91IHVzZSB0aGUgZnVuY3Rpb24uICANClRvIHNhdmUgdGhlIG91dHB1dCBmcm9tIGEgcGlwZSwgYWRkIGFuIGFzc2lnbm1lbnQgb3BlcmF0b3IgYC0+YCBhdCB0aGUgZW5kLCBvciBgPC1gIGF0IHRoZSBiZWdpbm5pbmcuIEJlbG93IGFyZSB0d28gd2F5cyBvZiBkb2luZyB0aGUgc2FtZSB0aGluZzoNCg0KYGBgDQpjYXJzJT4lDQogIGZpbHRlcihzcGVlZDw0KSU+JQ0KICBzZWxlY3Qoc3BlZWQsZGlzdCktPnNsb3dfY2Fycw0KICANCnNsb3dfY2FycyA8LSBjYXJzJT4lDQogICAgICAgICAgICAgICBmaWx0ZXIoc3BlZWQ8NCklPiUNCiAgICAgICAgICAgICAgIHNlbGVjdChzcGVlZCxkaXN0KQ0KYGBgDQoNCnwqdG8uLi4qICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCp1c2UqLi4uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8Tm90ZXMvRXhhbXBsZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwgDQp8KipmaWx0ZXIgcm93cyoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JWZpbHRlcihzcGVlZD4yKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipzZWxlY3QgY29sdW1ucyoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXNlbGVjdChzcGVlZCxkaXN0KWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Z2V0IG9ubHkgKip1bmlxdWUgcm93cyoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXVuaXF1ZSgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipzYW1wbGUqKiBpICoqcmFuZG9tIHJvd3MqKiAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXNhbXBsZV9uKGkpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipwdWxsKiogb3V0IGEgc2luZ2xlIGNvbHVtbiAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXB1bGwoc3BlZWQpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfFlvdSBjYW4gdXNlIHRoZSB2ZWN0b3JzIGZ1bmN0aW9uczxiciAvPmluIHRoZSBuZXh0IHNlY3Rpb24gb24gdGhpcyBjb2x1bW4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KiphcnJhbmdlKiogYnkgYSBjb2x1bW4sIGluIGFzY2VuZGluZyBvcmRlciAgICAgICAgICAgIHxgY2FycyU+JWFycmFuZ2Uoc3BlZWQpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8YXJyYW5nZSBieSBhIGNvbHVtbiwgaW4gZGVzY2VuZGluZyBvcmRlciAgICAgICAgICAgICAgIHxgY2FycyU+JWFycmFuZ2UoLXNwZWVkKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipyZW5hbWUgYSBjb2x1bW4qKiwgb2xkbmFtZSwgdG8gbmV3bmFtZSAgICAgICAgICAgICAgIHxgY2FycyU+JXJlbmFtZShuZXduYW1lPW9sZG5hbWVgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KiphZGQgYSBuZXcgY29sdW1uKiosIG5ld2NvbCAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JW11dGF0ZShuZXdjb2w9IC4uLilgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHBlcmZvcm0gc29tZSBvcGVyYXRpb25zIHRvIGdlbmVyYXRlIGRhdGEgZm9yIG5ld2NvbCBpbiAuLi4gKHNlZSB0aGUgVmVjdG9ycyBzZWN0aW9uIGZvciBpZGVhcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8cmVmZXIgdG8gdGhlIHZhcmlhYmxlIGJlaW5nIHBpcGVkICAgICAgICAgICAgICAgICAgICAgIHxgLmAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiVtdXRhdGUoSUQ9MTpucm93KC4pKSB3aGVyZSBgLmAgaXMgYGNhcnNgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipjYXRlZ29yaXNlIGRhdGEqKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JW11dGF0ZShjYXQ9aWZfZWxzZShjLHYxLHYyKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIG11dGF0ZShzcGVlZF9jYXQ9aWZfZWxzZShzcGVlZDw1LCdzbG93JywnZmFzdCcpICMgZm9yID4gMiBjYXRlZ29yaWVzLCB3cml0ZSBhIGZ1bmN0aW9uIHRvIGNhdGVnb3Jpc2UgdGhlbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8YWRkIG5ldyB2YWx1ZXMsICoqd29ya2luZyBvbiBncm91cHMgd2l0aGluIGNvbHVtbnMqKiAgIHxgbXV0YXRlKClgIHdpdGggYFtdYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIG11dGF0ZShzbG93X2Rpc3Q9c3VtKGRpc3Rbc3BlZWRfY2F0PT0nc2xvdyddKSxmYXN0X2Rpc3Q9c3VtKGRpc3Rbc3BlZWRfY2F0PSdmYXN0J10pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8YWRkIG5ldyB2YWx1ZXMsICoqd29ya2luZyBvbiBncm91cHMgd2l0aGluIGNvbHVtbnMqKiAgIHxgbXV0YXRlKClgIHdpdGggYGdyb3VwX2J5KClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIGdyb3VwX2J5KHNwZWVkX2NhdCklPiU8YnIgLz4gIG11dGF0ZSh0b3RhbF9kaXN0PXN1bShkaXN0KSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzKiogb3ZlciBjb2x1bW5zICAgICAgICAgIHxgc3VtbWFyaXNlKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIHN1bW1hcmlzZShtZWFuKHNwZWVkKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Y2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBvdmVyIGdyb3VwcyB3aXRoaW4gY29sdW1uc3xgZ3JvdXBfYnkoKWAgKyBgc3VtbWFyaXNlKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIGdyb3VwX2J5KHNwZWVkX2NhdCklPiU8YnIgLz4gIHN1bW1hcmlzZShtZWFuKHNwZWVkKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Y2FsY3VsYXRlIG9uIGVhY2ggcm93IHNlcGFyYXRlbHkgICAgICAgICAgICAgICAgICAgICAgIHxgcm93d2lzZSgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfG5ldHRsZSU+JTxiciAvPiAgcm93d2lzZSgpJT4lPGJyIC8+ICBtdXRhdGUoZW52aXJvbm1lbnQ9TUdTX2NhdGVnb3Jpc2UoTUdTKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KiphZGQgZGF0YSBmcm9tIGRhdEIgdG8gZGF0QSoqLCBtYXRjaGluZyBvbiBjb2xBICsgY29sQnxgbGVmdF9qb2luKGRhdEEsZGF0QixieT1jKCJjb2xBIiwiY29sQiIpKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHJvd3MgaW4gZGF0QiB0aGF0IGRvbid0IGhhdmUgYSBtYXRjaCBpbiBkYXRBIHdpbGwgYmUgbG9zdC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgcmlnaHRfam9pbihkYXRCLGRhdEEsYnk9YygiY29sQSIsImNvbEIiKSlgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHJvd3MgaW4gZGF0QiB0aGF0IGRvbid0IGhhdmUgYSBtYXRjaCBpbiBkYXRBIHdpbGwgYmUgbG9zdC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Kipjb21iaW5lIEFMTCBkYXRhIGluIGRhdEEgYW5kIGRhdEIqKiAgICAgICAgICAgICAgICAgIHxgZnVsbF9qb2luKGRhdEEsZGF0QixieT0iY29sQSIpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHRoZSBgYnlgIGFyZ3VtZW50IGlzIG9wdGlvbmFsOyB1c2UgaXQgdG8gc3BlY2lmeSBjb2x1bW5zIHRoYXQgeW91IHdhbnQgbWVyZ2VkLiBBbGwgcm93cyBpbiBib3RoIGRhdGFzZXRzIGFyZSBrZXB0LiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICANCnwqKmdldCBPTkxZIGRhdGEgaW4gYm90aCBkYXRBIGFuZCBkYXRCKiogICAgICAgICAgICAgICAgfGBpbm5lcl9qb2luKGRhdEEsZGF0QixieT0iY29sQSIpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8cm93cyB0aGF0IGhhdmUgYSB2YWx1ZSBpbiBjb2xBIGluIG9uZSBkYXRhc2V0LCBidXQgbm90IHRoZSBvdGhlciwgd2lsbCBiZSBsb3N0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnxtYWtlIGRhdGEgKip3aWRlcioqIChhZGQgbW9yZSBjb2x1bW5zKSAgICAgICAgICAgICAgICAgfGBkYXQlPiVwaXZvdF93aWRlcihuYW1lc19mcm9tPWNvbEEsdmFsdWVzX2Zyb209Y29sQix2YWx1ZXNfZmlsbD0wYCAgICB8TmFtZXMgZm9yIHRoZSBuZXcgY29scyBjb21lIGZyb20gY29sQSwgYW5kIHZhbHVlcyB0byBmaWxsIHRoZW0gY29tZSBmcm9tIGNvbEIuIFVzZSB2YWx1ZXNfZmlsbCB0byBzcGVjaWZ5IGhvdyBOQXMgc2hvdWxkIGJlIHJlcHJlc2VudGVkIChkZWZhdWx0IGlzIE5BKXwNCnxtYWtlIGRhdGEgKipsb25nZXIqKiAocmVtb3ZlIGNvbHVtbnMpICAgICAgICAgICAgICAgICAgfGBkYXQlPiVwaXZvdF9sb25nZXIoYygiY29sQSIsImNvbEIiKSxuYW1lc190bz0iY29sRCIsdmFsdWVzX3RvPSJjb2xFImB8Y29sQSArIGNvbEIgYXJlIGNvbGxhcHNlZCBpbnRvIGEgbmV3IGNvbCwgY29sRCwgYW5kIHRoZWlyIHZhbHVlcyB3aWxsIGdvIHRvIGEgbmV3IGNvbCwgImNvbEUiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgDQoNCiMjIFZlY3RvcnMNCg0KWW91IGNhbiB0cnkgb3V0IHRoZXNlIGZ1bmN0aW9ucyB5b3Vyc2VsZjsgdGhlIGBjYXJzYCBhbmQgYGlyaXNgIGRhdGFzZXRzIGJvdGggY29tZSB3aXRoIFIuDQoNCnwqdG8uLi4qICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwqdXNlKi4uLiAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfHNvcnQgaXRlbXMgKGFscGhhYmV0aWNhbGx5IG9yIG51bWVyaWNhbGx5KSAgICAgICAgICAgICAgICAgfGBzb3J0KGNhcnMkc3BlZWQpYCAgICAgICAgIHwNCnxhZGQgMTAgdG8gZXZlcnkgaXRlbSBpbiBhIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyRzcGVlZCArIDEwYCAgICAgICAgICB8DQp8c2VlIGhvdyBtYW55IHZhbHVlcyB0aGVyZSBhcmUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YGxlbmd0aChjYXJzJHNwZWVkKWAgICAgICAgfA0KfGFkZCB1cCBhbGwgdGhlIHZhbHVlcyAoZm9yIG51bWJlcnMpICAgICAgICAgICAgICAgICAgICAgICAgfGBzdW0oY2FycyRzcGVlZClgICAgICAgICAgIHwNCnxjb3VudCB0aGUgdmFsdWVzIChmb3IgY2hhcmFjdGVycykgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY291bnQoaXJpcyxzcGVjaWVzKWAgICAgICB8DQp8Z2V0IGFuIGlkZWEgb2YgJ3R5cGljYWwnIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YG1lYW4oY2FycyRzcGVlZClgICAgICAgICAgfA0KfGdldCBhbiBpZGVhIG9mICd0eXBpY2FsJyB2YWx1ZXMgd2hlbiB5b3UgaGF2ZSBvdXRsaWVycyAgICAgfGBtZWRpYW4oY2FycyRzcGVlZClgICAgICAgIHwNCnxlc3RpbWF0ZSB0aGUgJ3NwcmVhZCcgKGRldmlhdGlvbikgYXJvdW5kIHRoaXMgdHlwaWNhbCB2YWx1ZXxgc2QoY2FycyRzcGVlZClgICAgICAgICAgICB8DQp8Z2V0IHRoZSBtYXggdmFsdWUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YG1heChjYXJzJHNwZWVkKWAgICAgICAgICAgfA0KfGdldCB0aGUgbWluIHZhbHVlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGBtaW4oY2FycyRzcGVlZClgICAgICAgICAgIHwNCnxnZXQgdGhlIHJhbmdlIG9mIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgcmFuZ2UoY2FycyRzcGVlZClgICAgICAgICB8DQp8Z2V0IG9ubHkgdW5pcXVlIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YHVuaXF1ZShjYXJzJHNwZWVkKWAgICAgICAgfA0KfGdldCB0aGUgaXRoIGl0ZW0gaW4gdGhlIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGBjYXJzJHNwZWVkW2ldYCAgICAgICAgICAgIHwNCnxyZW1vdmUgdGhlIGl0aCBpdGVtIGZyb20gdGhlIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyRzcGVlZFstaV1gICAgICAgICAgICB8DQp8Z2V0IGFsbCBpdGVtcyBiZXR3ZWVuIGkgYW5kIGogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YGNhcnMkc3BlZWRbaTpqXWAgICAgICAgICAgfA0KfHJlbW92ZSBhbGwgaXRlbXMgYmV0d2VlbiBpIGFuZCBqICAgICAgICAgICAgICAgICAgICAgICAgICAgfGBjYXJzJHNwZWVkWy0oaTpqKV1gICAgICAgIHwNCnxzYW1wbGUgbiByYW5kb20gdmFsdWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgc2FtcGxlKGNhcnMkc3BlZWQsbilgICAgICB8DQp8Y2hlY2sgaWYgYSBjaGFyYWN0ZXIgaXMgaW4gYSB2ZWN0b3IgICAgICAgICAgICAgICAgICAgICAgICB8YCJjaHIiICVpbiUgdmVjdG9yYCAgICAgICAgfA0KfGNoZWNrIGlmIGEgY2hhcmFjdGVyIGlzIE5PVCBpbiBhIHZlY3RvciAgICAgICAgICAgICAgICAgICAgfGAhKCJjaHIiICVpbiUgdmVjdG9yKWAgICAgIHwNCnxjaGVjayBpZiBhIG51bWJlciBpcyBpbiBhIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgbnVtICVpbiUgdmVjdG9yYCAgICAgICAgICB8DQoNCiMjIENoYXJhY3RlcnMvU3RyaW5ncw0KDQpVc2UgdGhlIHBhY2thZ2UgYHN0cmluZ3JgIChpdCBjb21lcyB3aXRoIHRoZSBgdGlkeXZlcnNlYCwgc28geW91IHNob3VsZG4ndCBoYXZlIHRvIGxvYWQgaXQgc2VwYXJhdGVseSkNCg0KfHRvLi4uICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8dXNlLi4uICAgICAgICAgICAgICAgICAgICAgICAgIHxlLmcuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnxzdWJzZXQgYSBzdHJpbmcgICAgICAgICAgICAgICAgICAgfGBzdHJfc3ViKHN0cixzdGFydCxzdG9wKWAgICAgICB8YHN0cl9zdWIoIldlZG5lc2RheSIsMSwzKWAgPSAiV2VkIiAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YWRkIGAtYCB0byBzdGFydCBmcm9tIHRoZSBlbmQgIHxgc3RyX3N1YigiV2VkbmVzZGF5IiwtMywtMSlgID0gImRheSJ8DQp8Y29udmVydCB0byBsb3dlcmNhc2UgICAgICAgICAgICAgIHxgc3RyX3RvX2xvd2VyKClgICAgICAgICAgICAgICAgfGBzdHJfdG9fbG93ZXIoIkJvYiIpYCA9ICJib2IiICAgICAgIHwNCnxjb252ZXJ0IHRvIHVwcGVyY2FzZSAgICAgICAgICAgICAgfGBzdHJfdG9fdXBwZXIoKWAgICAgICAgICAgICAgICB8YHN0cl90b191cHBlcigiYm9iIilgID0gIkJPQiIgICAgICAgfA0KfGNvbnZlcnQgdG8gdGl0bGUgY2FzZSAgICAgICAgICAgICB8YHN0cl90b190aXRsZSgpYCAgICAgICAgICAgICAgIHxgc3RyX3RvX3RpdGxlKCJib2IiKWAgPSAiQm9iIiAgICAgICB8DQp8ZmluZCBhbmQgcmVwbGFjZSAgICAgICAgICAgICAgICAgIHxgc3RyX3JlcGxhY2Uoc3RyLGZpbmQscmVwbGFjZSlgfGBzdHJfcmVwbGFjZSgiZnVuIiwiZiIsInAiKWA9InB1biIgIHwNCnxyZW1vdmUgbGVhZGluZy90cmFpbGluZyB3aGl0ZXNwYWNlfGBzdHJfdHJpbShzdHIpYCAgICAgICAgICAgICAgICB8YHN0cl90cmltKCIgaGVsbG8gIilgPSJoZWxsbyIgICAgICAgfA0KDQpUaGUgYHN0cmluZ19yYCBmdW5jdGlvbnMgYXJlIG1vc3QgcG93ZXJmdWwgd2hlbiB5b3UgY29tYmluZSB0aGVtIHdpdGggcmVndWxhciBleHByZXNzaW9ucy4gQmVsb3cgYXJlIHNvbWUgb2YgdGhlIG1vc3QgdXNlZnVsIG9uZXM6DQoNCnxSZWd1bGFyIGV4cHJlc3Npb258TWVhbmluZyAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8Ii4iICAgICAgICAgICAgICAgfGFueSBjaGFyYWN0ZXIgICAgICAgICAgICAgIHwNCnwiLioiICAgICAgICAgICAgICB8YW55IG51bWJlciBvZiBhbnkgY2hhcmFjdGVyfA0KfCJcXGIiICAgICAgICAgICAgIHxhIHdvcmQgYm91bmRhcnkgICAgICAgICAgICB8DQp8IlxcZCIgICAgICAgICAgICAgfGFueSBudW1iZXIgKGRpZ2l0KSAgICAgICAgIHwNCnwiWzpwdW5jdDpdIiAgICAgICB8cHVuY3R1YXRpb24gbWFya3MgICAgICAgICAgfA0KDQoqKkltcG9ydGFudCoqOiBCZWNhdXNlIGNoYXJhY3RlcnMgbGlrZSAiLiIgaGF2ZSBhIHNwZWNpYWwgbWVhbmluZyBpbiByZWd1bGFyIGV4cHJlc3Npb25zLCBpZiB5b3Ugd2FudCB0byBzZWFyY2ggZm9yIGEgbGl0ZXJhbCBwZXJpb2QgKC4pLCB5b3Ugd2lsbCBuZWVkIHRvIHVzZSAiXFwuIi4gVGhlIHR3byBzbGFzaGVzIGFyZSBjYWxsZWQgZXNjYXBlIGNoYXJhY3RlcnMsIHRoZXkgdGVsbCBSIG5vdCB0byB0cmVhdCB3aGF0ZXZlciBmb2xsb3dzIGFzIGEgcmVndWxhciBleHByZXNzaW9uLg0KDQpZb3UgY2FuIGZpbmQgYWxsIHRoZSByZWd1bGFyIGV4cHJlc3Npb25zIG9uIHRoZSBjaGVhdHNoZWV0IFtoZXJlXShodHRwczovL2V2b2xkeW4uZ2l0bGFiLmlvL2V2b21pY3MtMjAxOC9yZWYtc2hlZXRzL1Jfc3RyaW5ncy5wZGYpLg0KDQojIEZ1bmN0aW9ucyBhbmQgTG9vcHMNCg0KLSBVc2UgdGhlc2Ugd2hlbiB5b3Ugd2FudCB0byBydW4gdGhlIHNhbWUgY29kZSBtYW55IHRpbWVzIG9uIGRpZmZlcmVudCB0aGluZ3MuDQoNCioqU3ludGF4IGZvciBmdW5jdGlvbnMqKjoNCmBgYA0KZnVuY3Rpb25fbmFtZSA8LSBmdW5jdGlvbihpbnB1dHMsaW5wdXRzLGlucHV0cyl7DQojIGRvIHN0dWZmDQouDQouDQouDQpyZXR1cm4ob3V0cHV0KQ0KfQ0KYGBgDQoNCioqU3ludGF4IGZvciBsb29wcyoqOg0KYGBgDQpmb3IoQSBpbiBCKXsNCiAgIyBkbyBzb21ldGhpbmcNCn0NCmBgYA0KDQoqKlVzYWdlIGV4YW1wbGUqKjoNCg0KYGBgDQojIGV4YW1wbGUgb2YgYSBmdW5jdGlvbiB0byBnZXQgdGhlIGh5cG90ZW51c2Ugb2YgYSB0cmlhbmdsZSBmcm9tIHRoZSBzaG9ydCBhbmQgbWlkZGxlIHNpZGUNCg0KZ2V0X2h5cG90ZW51c2UgPC0gZnVuY3Rpb24oc2hvcnRzaWRlLCBtaWRkbGVzaWRlKSB7IA0KICAjIHNob3J0c2lkZSBhbmQgbWlkZGxlc2lkZSBhcmUgdGhlIGlucHV0cyAoYXJndW1lbnRzKQ0KICBoeXBvdGVudXNlX3NxdWFyZWQ8LXNob3J0c2lkZSoqMittaWRkbGVzaWRlKioyDQogIGh5cG90ZW51c2U8LXNxcnQoaHlwb3RlbnVzZV9zcXVhcmVkKQ0KICByZXR1cm4oaHlwb3RlbnVzZSkjIGh5cG90ZW51c2UgaXMgdGhlIG91dHB1dA0KfQ0KDQpzaG9ydHNpZGVzIDwtIGMoMyw0LDUsOCwxLDMpDQptaWRkbGVzaWRlcyA8LSBjKDgsMTAsMTIsMTQsMiwxKQ0KDQojIG5vdyBsZXRzIHJ1biBpdCBtdWx0aXBsZSB0aW1lcyBpbiBhIGxvb3ANCg0KIyBtYWtlIGFuIGVtcHR5IHZlY3RvciB0byBzdG9yZSB0aGUgaHlwb3RlbnVzZXMNCmh5cG90ZW51c2VzIDwtIGMoKQ0KDQojIHdlIHVzZSB0aGVzZSAnaScgdmFsdWVzIGFzIGluZGV4ZXMgdG8gcmVmZXIgdG8gaXRlbXMgaW4gb3VyIHR3byB2ZWN0b3JzIHNob3J0c2lkZXMgYW5kIG1pZGRsZXNpZGVzDQpmb3IoaSBpbiAxOmxlbmd0aChzaG9ydHNpZGVzKSl7DQogICMgcnVuIG91ciBmdW5jdGlvbg0KICBoeXBvdGVudXNlIDwtIGdldF9oeXBvdGVudXNlKHNob3J0c2lkZXNbaV0sbWlkZGxlc2lkZXNbaV0pDQogICMgdXBkYXRlIHRoZSBoeXBvdGVudXNlIHZlY3Rvciwgc28gaXQgY29udGFpbnMgdGhlIGh5cG90ZW51c2VzIHdlJ3ZlIGFscmVhZHkgY2FsY3VsYXRlZCwgcGx1cyB0aGUgb25lIHdlIGp1c3QgY2FsY3VsYXRlZCBpbiB0aGlzIGl0ZXJhdGlvbiBvZiB0aGUgbG9vcA0KICBoeXBvdGVudXNlcyA8LSBjKGh5cG90ZW51c2VzLGh5cG90ZW51c2UpDQp9DQoNCiMgYWZ0ZXIgdGhlIGxvb3AgdGhlIGh5cG90ZW51c2VzIHZlY3RvciB3aWxsIGJlIGZ1bGwgDQpgYGANCg0KIyMgQ2F0ZWdvcmlzaW5nIGZ1bmN0aW9ucw0KDQpBIGZ1bmN0aW9uIGZvciBjYXRlZ29yaXNpbmcgdGhlIGVudmlyb25tZW50LCBiYXNlZCBvbiB0aGUgbGVuZ3RoIG9mIHRoZSBtYXhpbXVtIGdyb3dpbmcgc2Vhc29uIChtZ3MpLg0KDQpgYGANCmVudmlyb25tZW50IDwtIGZ1bmN0aW9uKG1ncykgew0KICBpZihtZ3MgPCA0KSB7DQogICAgcmV0dXJuKCJkcnkiKQ0KICB9IGVsc2UgaWYobWdzIDwgOCkgew0KICAgIHJldHVybigidHlwaWNhbCIpDQogIH0gZWxzZSB7DQogICAgcmV0dXJuKCJmZXJ0aWxlIikNCiAgfQ0KfQ0KYGBgDQoNCiMgQ29uZGl0aW9ucw0KDQp8RXhwcmVzc2lvbiB8TWVhbmluZyAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfFg9PVkgICAgICAgfFggZXF1YWxzIFkgICAgICAgICAgICAgICAgICAgICB8DQp8WCE9WSAgICAgICB8WCBpcyBub3QgZXF1YWwgdG8gWSAgICAgICAgICAgIHwNCnxYICVpbiUgWSAgIHxYIGlzIGluIFkgICAgICAgICAgICAgICAgICAgICAgfA0KfCEoWCAlaW4lIFkpfFggaXMgbm90IGluIFkgICAgICAgICAgICAgICAgICB8DQp8WDxZICAgICAgICB8WCBpcyBsZXNzIHRoYW4gWSAgICAgICAgICAgICAgIHwNCnxYPlkgICAgICAgIHxYIGlzIGdyZWF0ZXIgdGhhbiBZICAgICAgICAgICAgfA0KfFg+PVkgICAgICAgfFggaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIFl8DQp8WDw9IFkgICAgICB8WCBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gWSAgIHwNCg0KIyMgaWYsIGVsc2UgaWYsIGVsc2Ugc3RhdGVtZW50cw0KDQohW10oaW1hZ2VzL2lmX2lmZWxzZS5QTkcpe3dpZHRoPTcwJX0NCg0KT25seSB0aGUgYmxvY2sgb2YgY29kZSBmb3IgdGhlIEZJUlNUIGNvbmRpdGlvbiB0aGF0IGV2YWx1YXRlcyB0byBUUlVFIHdpbGwgYmUgcnVuOyBzbyB0aGUgb3JkZXIgb2YgeW91ciBjb25kaXRpb25zIG1hdHRlcnMuDQoNCiMgRGF0YSB2aXN1YWxpc2F0aW9uDQoNCldlIHVzZSBgZ2dwbG90KClgLiBUaGVzZSBwbG90cyBoYXZlIGEgYmFzaWMgc3RydWN0dXJlIGxpa2UgdGhpczoNCg0KYGRhdGEgJT4lIGdncGxvdChhZXMoeD1jb2xBLHk9Y29sQixjb2xvdXI9Y29sQykpICtnZW9tX3BvaW50KCkvZ2VvbV9saW5lKCkvZ2VvbV9jb2woKWANCg0KT3B0aW9uYWwgZXh0cmFzOg0KDQoqIGArbGFicyh4PSJYIGF4aXMgdGl0bGUiLHk9IlkgYXhpcyB0aXRsZSIsY29sb3VyPSJMZWdlbmQgdGl0bGUiLHRpdGxlPSJQbG90IHRpdGxlIixjYXB0aW9uPSJDYXB0aW9uIGZvciBwbG90IilgIFtpZiB5b3UgdXNlIGBmaWxsYCBpbnN0ZWFkIG9mIGBjb2xvdXJgLCB0aGVuIHlvdSB3aWxsIHVzZSBgZmlsbD0iTGVnZW5kIHRpdGxlImBdDQoqIGArdGhlbWVfY2xhc3NpYygpYCAgICMgdGhpcyBpcyBhIG5pY2UgdGhlbWUNCiogSWYgeW91IGFyZSBwcmludGluZyBpbiBibGFjayBhbmQgd2hpdGUsIHlvdSBjYW4gdXNlIGBzaGFwZWAgaW5zdGVhZCBvZiBjb2xvdXINCg0KIyMgV2hlbiB0byB1c2Ugd2hpY2ggdHlwZSBvZiBncmFwaD8NCg0KSWYgeW91IGhhdmUgYSBsb3Qgb2YgeCB2YWx1ZXM6DQoNCiogYGdlb21fcG9pbnQoKWAgaXMgZ29vZCB3aGVuIHlvdXIgb2JzZXJ2YXRpb25zIGNvbWUgZnJvbSBkaWZmZXJlbnQgc291cmNlcywgZS5nLiB0aGUgbnVtYmVyIG9mIGNvdmlkIGRlYXRocyBpbiBwZW9wbGUgb2YgZGlmZmVyZW50IGFnZXMsIGFuZCB5b3Ugd2FudCB0byBjb21tdW5pY2F0ZSB0aGUgKnJlbGF0aW9uc2hpcCBiZXR3ZWVuIHlvdXIgeCBhbmQgeSB2YWx1ZXMqDQoqIGBnZW9tX2xpbmUoKWAgYW5kIGBnZW9tX3Ntb290aCgpYCBhcmUgYmV0dGVyIGZvciBwbG90dGluZyBtdWx0aXBsZSBvYnNlcnZhdGlvbnMgZnJvbSBhIHNpbmdsZSBzb3VyY2UsIGUuZy4gdGhlIG51bWJlciBvZiBjb3ZpZCBjYXNlcyBpbiBhIGNvdW50cnkgb3ZlciBtdWx0aXBsZSB3ZWVrcywgd2hlcmUgeW91IHdhbnQgdG8gY29tbXVuaWNhdGUgdGhlICpjaGFuZ2UgaW4geW91ciB5IHZhbHVlIG92ZXIgdGltZSoNCg0KSWYgeW91IGhhdmUgb25seSBhIGZldyB4IHZhbHVlczoNCg0KKiBgZ2VvbV9jb2woKWAgaXMgZ29vZCBmb3Igd2hlbiB5b3UgaGF2ZSBvbmx5IG9uZSB5IG9ic2VydmF0aW9uIHBlciB4IHZhbHVlDQoqIGBnZW9tX3Zpb2xpbigpYCBpcyBnb29kIHdoZW4geW91ciB4IHZhbHVlcyBhcmUgbGFyZ2VyIGNhdGVnb3JpZXMgd2l0aCBtdWx0aXBsZSB5IG9ic2VydmF0aW9ucyBwZXIgeCB2YWx1ZSwgYW5kIHlvdSB3YW50IHRvICpjb21wYXJlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHkgdmFsdWVzIGJldHdlZW4gdGhlIGRpZmZlcmVudCBncm91cHMqDQoNCmBnZW9tX2hpc3RvZ3JhbSgpYCBpcyB1c2VkIHRvIGV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHNpbmdsZSB2YXJpYWJsZS4gSXQncyBtb3JlIHVzZWQgaW4gdGhlIG1ldGhvZHMgc2VjdGlvbiBvZiBwYXBlcnMgcmF0aGVyIHRoYW4gdGhlIHJlc3VsdHMsIGZvciBleGFtcGxlIHRvIHNob3cgd2hhdCB5b3VyIHNhbXBsZSBvZiBkYXRhIGxvb2tzIGxpa2UuDQoNCiMjIGdlb21faGlzdG9ncmFtKCkNCg0KQmVsb3cgaXMgYSBoaXN0b2dyYW0gc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGRpYW1vbmRzIGluIHRoZSBkaWFtb25kIGRhdGFzZXQgYnkgdGhlaXIgY2FyYXQuDQoNCmBgYHtyfQ0KZGlhbW9uZHMlPiUNCiAgICAgICAjIGp1c3QgcHV0IHRoZSB2YXJpYWJsZSB5b3Ugd2FudCB0byBleGFtaW5lIHRoZSBkaXN0cmlidXRpb24gb2YNCiAgZ2dwbG90KGFlcyhjYXJhdCkpKw0KICBnZW9tX2hpc3RvZ3JhbSgpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBkaWFtb25kcyBieSBjYXJhdCIpDQpgYGANCg0KTW9zdCBvZiB0aGUgZGlhbW9uZHMgaW4gdGhlIGRhdGFzZXQgYXJlIGxlc3MgdGhhbiAxIGNhcmF0Lg0KDQojIyBnZW9tX3BvaW50KCkNCg0KYGBge3IsaW5jbHVkZT1GQUxTRX0NCm5ldHRsZSA8LSByZWFkX2NzdigiZGF0YS9uZXR0bGVfMTk5OV9jbGltYXRlLmNzdiIpDQpuZXR0bGUgJT4lIA0KICBtdXRhdGUoTUdTX2NhdGVnb3J5ID0gaWZfZWxzZShNR1MgPCA2LCAiZHJ5IiwgImZlcnRpbGUiKSktPk1HU19uZXR0bGUNCmBgYA0KDQpXaXRoIGNvbG91cnM6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCk1HU19uZXR0bGUlPiVnZ3Bsb3QoYWVzKHg9UG9wdWxhdGlvbix5PUxhbmdzLGNvbG91cj1NR1NfY2F0ZWdvcnkpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBsYWJzKHg9J1BvcHVsYXRpb24gKGluIG1pbGxpb25zKScseT0nTnVtYmVyIG9mIGxhbmd1YWdlcycsY29sb3VyPSJFbnZpcm9ubWVudCIpKw0KICB0aGVtZV9jbGFzc2ljKCkgICAjIGEgbmljZXIgcHJlc2VudGF0aW9uIHRoYW4gdGhlIGRlZmF1bHQgZ2dwbG90IHRoZW1lDQpgYGANCg0KV2l0aCBzaGFwZXM6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCk1HU19uZXR0bGUlPiVnZ3Bsb3QoYWVzKHg9UG9wdWxhdGlvbix5PUxhbmdzLHNoYXBlPU1HU19jYXRlZ29yeSkpKw0KICBnZW9tX3BvaW50KCkrDQogIGxhYnMoeD0nUG9wdWxhdGlvbiAoaW4gbWlsbGlvbnMpJyx5PSdOdW1iZXIgb2YgbGFuZ3VhZ2VzJyxzaGFwZT0iRW52aXJvbm1lbnQiKSsNCiAgdGhlbWVfY2xhc3NpYygpICAgIyBhIG5pY2VyIHByZXNlbnRhdGlvbiB0aGFuIHRoZSBkZWZhdWx0IGdncGxvdCB0aGVtZQ0KYGBgDQoNCiMjIEFkZGluZyBsYWJlbHMgdG8gcG9pbnRzDQoNCmBnZW9tX3RleHQoKWAgYW5kIGBnZW9tX3RleHRfcmVwZWwoKWAgY2FuIGJlIHVzZWQgdG8gYWRkIGxhYmVscyB0byBwb2ludHMgb24geW91ciBwbG90LiBZb3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlIGxpYnJhcnkgYGdncmVwZWxgIHRvIHVzZSBgZ2VvbV90ZXh0X3JlcGVsYCwgd2hpY2ggcG9zaXRpb25zIHRoZSBsYWJlbHMgbmljZWx5IHNvIHRoZXkgZG9uJ3Qgb3ZlcmxhcCB3aXRoIGFueXRoaW5nLCBhbmQgeW91J2xsIG5lZWQgdG8gYWRkIGFuIGFyZ3VtZW50IGBsYWJlbGAgdG8geW91ciBgYWVzKClgIGZ1bmN0aW9uIGluIGBnZ3Bsb3QoKWAsIHRvIHNwZWNpZnkgd2hpY2ggY29sdW1uIHRvIGdldCB0aGUgbGFiZWxzIGZyb20uDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3JlcGVsKQ0KTUdTX25ldHRsZSU+JQ0KICBzYW1wbGVfbigxNSklPiUNCiAgZ2dwbG90KGFlcyh4PVBvcHVsYXRpb24seT1sb2cxMChMYW5ncyksbGFiZWw9Q291bnRyeSkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyPU1HU19jYXRlZ29yeSkpICsgZ2VvbV90ZXh0X3JlcGVsKCkrdGhlbWVfY2xhc3NpYygpDQoNCmBgYA0KDQoNCiMjIGdlb21fbGluZSgpIGFuZCBnZW9tX3Ntb290aCgpDQoNClVzZSBgZ2VvbV9saW5lKClgIGZvciBhIGphZ2dlZCBsaW5lOg0KDQpgYGB7cixpbmNsdWRlPUZBTFNFfQ0KYmFybm5hbW4gJT4lIA0KICBtdXRhdGUoRmlyc3RMZXR0ZXI9c3RyX3N1YihuYW1lLDEsMSkpICU+JSANCiAgZmlsdGVyKEZpcnN0TGV0dGVyID09ICJYIiB8IEZpcnN0TGV0dGVyID09ICJZIiB8IEZpcnN0TGV0dGVyID09ICJaIikgJT4lIA0KICBncm91cF9ieSh5ZWFyKSAgJT4lIA0KICBzdW1tYXJpc2UoVG90YWxOPXN1bShuKSktPndlaXJkX25hbWVzDQpgYGANCg0KDQpgYGB7cn0NCndlaXJkX25hbWVzJT4lDQogIGdncGxvdChhZXMoeD15ZWFyLCB5PVRvdGFsTikpICsgDQogIGdlb21fbGluZSgpKw0KbGFicyh4PSdZZWFyJyx5PSdUb3RhbCBudW1iZXIgb2Ygd2VpcmQgbmFtZXMnLHRpdGxlPSdQb3B1bGFyaXR5IG9mIHdlaXJkIG5hbWVzIG92ZXIgdGltZScpKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpVc2UgYGdlb21fc21vb3RoKClgIHRvIGdldCBhIHNtb290aGVkIGxpbmU6DQoNCmBgYHtyLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCndlaXJkX25hbWVzJT4lDQogIGdncGxvdChhZXMoeD15ZWFyLCB5PVRvdGFsTikpICsgDQogIGdlb21fc21vb3RoKCkrDQpsYWJzKHg9J1llYXInLHk9J1RvdGFsIG51bWJlciBvZiB3ZWlyZCBuYW1lcycsdGl0bGU9J1BvcHVsYXJpdHkgb2Ygd2VpcmQgbmFtZXMgb3ZlciB0aW1lJykrDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCiMjIGdlb21fY29sKCkgLSBiYXIgcGxvdHMNCg0KYGBge3J9DQpiYXJubmFtbiU+JQ0KICBmaWx0ZXIobmFtZT09J0xlZScpJT4lDQogIGdncGxvdChhZXMoeD15ZWFyLHk9bikpK2dlb21fY29sKCkrDQogIGxhYnModGl0bGU9IkJhYmllcyBuYW1lZCBMZWUiLHg9IlllYXIiLHk9Ik51bWJlciBvZiBiYWJpZXMiKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDIwMTA6MjAxNykpKyAgIyB0byBzcGVjaWZ5IHRoZSBicmVha3Mgb24gdGhlIHgtYXhpcw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyBSZW9yZGVyaW5nIGl0ZW1zIG9uIHRoZSB4LWF4aXMNCg0KKiBhZGQgYSBgcmVvcmRlcigpYCBmdW5jdGlvbiB0byB0aGUgYGFlcygpYCBmb3IgdGhlIHgtYXhpcw0KKiB3b3JrcyB0aGUgc2FtZSB3YXkgYXMgYGFycmFuZ2UoKWANCg0KYGBge3J9DQpNR1NfbmV0dGxlJT4lDQogIHNhbXBsZV9uKDYpJT4lDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKENvdW50cnksTGFuZ3MpLHk9TGFuZ3MsZmlsbD1NR1NfY2F0ZWdvcnkpKSsNCiAgZ2VvbV9jb2woKSsNCiAgbGFicyh4PSdDb3VudHJ5Jyx5PSdOdW1iZXIgb2YgbGFuZ3VhZ2VzJykrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUoIkVudmlyb25tZW50IixjKCJkcnkiLCJmZXJ0aWxlIikpKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyBHcm91cGVkIGJhciBwbG90cw0KDQoqIFVzZSBgZmlsbD1WYXJYYCBpbiB0aGUgYGFlcygpYCBvZiB5b3VyIGdncGxvdCBjYWxsIHRvIGNvbG91ciB0aGUgYmFycyBieSBhIHRoaXJkIHZhcmlhYmxlLg0KKiBVc2UgYHBvc2l0aW9uPSJkb2RnZSgpImAgaW4geW91ciBgZ2VvbV9jb2woKWAgdG8gaGF2ZSB0aGUgYmFycyBzaWRlLWJ5LXNpZGUNCiogVXNlIGBzY2FsZV94X2NvbnRpbnVvdXNgIChmb3IgY29udGludW91cyB2YXJpYWJsZXMpIHRvIHNwZWNpZnkgdGhlIGJyZWFrcyBvbiB0aGUgeC1heGlzOyBpZiB5b3UgaGF2ZSBkaXNjcmV0ZSAoZS5nLiBjYXRlZ29yaWNhbCkgdmFyaWFibGVzLCB0aGUgZnVuY3Rpb24gaXMgYHNjYWxlX3hfZGlzY3JldGVgDQoNCmBgYHtyfQ0KYmFybm5hbW4lPiUNCiAgZmlsdGVyKG5hbWU9PSdMZWUnKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9eWVhcix5PW4sZmlsbD1zZXgpKSsNCiAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIikrICAjIGJhcnMgc2lkZS1ieS1zaWRlDQogIGxhYnModGl0bGU9IkJhYmllcyBuYW1lZCBMZWUiLHg9IlllYXIiLHk9Ik51bWJlciBvZiBiYWJpZXMiLGZpbGw9IlNleCIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMjAxMDoyMDE3KSkrICAjIHRvIHNwZWNpZnkgdGhlIGJyZWFrcyBvbiB0aGUgeC1heGlzDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCklmIHlvdSBkb24ndCB1c2UgYHBvc2l0aW9uPSJkb2RnZSJgLCB0aGUgZGlmZmVyZW50IGNvbG91cmVkIGJhcnMgd2lsbCBiZSBzdGFja2VkIG9uIHRvcCBvZiBlYWNob3RoZXIuDQoNCmBgYHtyfQ0KYmFybm5hbW4lPiUNCiAgZmlsdGVyKG5hbWU9PSdMZWUnKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9eWVhcix5PW4sZmlsbD1zZXgpKSsNCiAgZ2VvbV9jb2woKSsgIA0KICBsYWJzKHRpdGxlPSJCYWJpZXMgbmFtZWQgTGVlIix4PSJZZWFyIix5PSJOdW1iZXIgb2YgYmFiaWVzIixmaWxsPSJTZXgiKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDIwMTA6MjAxNykpKyAgIyB0byBzcGVjaWZ5IHRoZSBicmVha3Mgb24gdGhlIHgtYXhpcw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyBQZXJjZW50IHN0YWNrZWQgYmFyIHBsb3RzDQoNClVzZSBgcG9zaXRpb249ImZpbGwiYCB0byBzaG93IHByb3BvcnRpb25zIGluc3RlYWQgb2YgY291bnRzOg0KDQpgYGB7cn0NCmJhcm5uYW1uJT4lDQogIGZpbHRlcihuYW1lPT0nTGVlJyklPiUNCiAgZ2dwbG90KGFlcyh4PXllYXIseT1uLGZpbGw9c2V4KSkrDQogIGdlb21fY29sKHBvc2l0aW9uPSJmaWxsIikrICAjIHRvIHNob3cgcHJvcG9ydGlvbnMgDQogIGxhYnModGl0bGU9IkJhYmllcyBuYW1lZCBMZWUiLHg9IlllYXIiLHk9IlByb3BvcnRpb24iLGZpbGw9IlNleCIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMjAxMDoyMDE3KSkrICAjIHRvIHNwZWNpZnkgdGhlIGJyZWFrcyBvbiB0aGUgeC1heGlzDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCiMjIGdlb21fdmlvbGluKCkNCg0KVGhpcyBpcyBnb29kIHRvIGNvbXBhcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBiZXR3ZWVuIGdyb3Vwcy4NCg0KYGBge3J9DQojIG1wZyBpcyBhIGRhdGFzZXQgb2YgY2Fycw0KbXBnICU+JQ0KICAjIHRoZSBod3kgY29sdW1uIHRlbGxzIHlvdSBob3cgbWFueSBtaWxlcyBhIGNhciBjYW4gZ28gb24gMSBnYWxsb24gb2YgcGV0cm9sDQogICMgY2xhc3MgaXMgdGhlIHR5cGUgb2YgY2FyDQogIGdncGxvdCggYWVzKHg9cmVvcmRlcihjbGFzcyxod3kpLCB5PWh3eSkpICsgDQogIGdlb21fdmlvbGluKCkgKw0KICB0aGVtZV9jbGFzc2ljKCkrDQojIHdlIGNhbiB1c2UgdGhlIHN0YXRfc3VtbWFyeSBwb2ludCB0byBhZGQgcG9pbnRzIGZvciBzdW1tYXJ5IHN0YXRpc3RpY3MsIGhlcmUgd2Ugc2hvdyB0aGUgbWVhbg0KICBzdGF0X3N1bW1hcnkoZnVuPW1lYW4sIGdlb209InBvaW50IikrDQogIGxhYnMoeD0iQ2FyIHR5cGUiLHk9Im1pbGVzIHBlciBnYWxsb24iLHRpdGxlPSJFZmZpY2llbmN5IG9mIGNhcnMgb24gdGhlIGhpZ2h3YXkiLGNhcHRpb24gPSAiVGhlIG1lYW4gbWlsZXMgcGVyIGdhbGxvbiBmb3IgZWFjaCBjYXIgaXMgaW5kaWNhdGVkIHdpdGggYSBwb2ludCIpICANCg0KYGBgDQoNCg0KDQojIyBNYWtpbmcgZGF0YSBwb2ludHMgbGVzcyBjbHVtcGVkIG9yIGxlc3Mgc3ByZWFkIGFwYXJ0DQoNCiogTG9nIHRyYW5zZm9ybWF0aW9ucyAoYXMgaW4gdGhlIGdyYXBoIGFib3ZlKSBhcmUgdXNlZCB0byBtYWtlIGRhdGEgdGhhdCBpcyB2ZXJ5IHNwcmVhZCBhcGFydC9jbHVtcGVkIHRvZ2V0aGVyIGVhc2llciB0byB2aXN1YWxpc2UuDQoqIFRoZSBtb3N0IGNvbW1vbiBiYXNlIGlzIDEwLiBXaGVuIHdlIHRha2UgdGhlIGxvZzEwIG9mIGEgbnVtYmVyLCAkeCQsIHdlIGFyZSB0cnlpbmcgdG8gZmluZCBhIG51bWJlciAkeSQgc3VjaCB0aGF0ICQxMF55PXgkLiBUaGUgZnVuY3Rpb24gZm9yIHRoaXMgaW4gUiBpcyBqdXN0IGBsb2cxMCh4KWAuIEZvciB2ZXJ5IGZhciBhcGFydCB2YWx1ZXMgb2YgJHgkLCB0YWtpbmcgJGxvZzEwKHgpJCB3aWxsIGdpdmUgeW91IHZhbHVlcyB0aGF0IGFyZSBjbG9zZXIgdG9nZXRoZXIsIHdoaWxlIGZvciB2ZXJ5IGNsb3NlIHRvZ2V0aGVyIHZhbHVlcyBvZiAkeCQsICRsb2cxMCh4KSQgd2lsbCBnaXZlIHlvdSB2YWx1ZXMgdGhhdCBhcmUgZnVydGhlciBhcGFydC4gVGhpcyBtZWFucyB0aGF0ICRsb2cxMCh4KSQgaXMgb2Z0ZW4gbmljZXIgdG8gcGxvdCB0aGFuICR4JC4NCiogSXQgaXMgcGVyZmVjdGx5IGFjY2VwdGFibGUgdG8gZG8gdGhlc2Uga2luZHMgb2YgdHJhbnNmb3JtYXRpb25zIHRvIHlvdXIgZGF0YSwgaW4gb3JkZXIgdG8gbWFrZSBpdCBlYXNpZXIgdG8gdmlzdWFsaXNlLg0KDQojIyBNYWtpbmcgbXVsdGlwbGUgcGxvdHMgDQoNCmBmYWNldF93cmFwKClgIGFuZCBgZmFjZXRfZ3JpZCgpYCBsZXQgeW91IHByb2R1Y2UgbXVsdGlwbGUgZGlmZmVyZW50IHBsb3RzIGZvciBlYWNoIHZhbHVlIGluIGEgY29sdW1uL2NvbHVtbnMNCiANCiogYGZhY2V0X3dyYXAofkNvbEEpYCAtLT4gd2lsbCBtYWtlIGRpZmZlcmVudCB2ZXJzaW9ucyBvZiB0aGUgc2FtZSBncmFwaCBmb3IgZXZlcnkgZGlmZmVyZW50IHZhbHVlIGluIGNvbEEsIGFsbCB3cmFwcGVkIGFyb3VuZCBlYWNoIG90aGVyICh3b3JrcyB3ZWxsIHdoZW4geW91IGhhdmUgYSBsb3Qgb2YgZGlmZmVyZW50IHZhbHVlcyBvZiBhIHZhcmlhYmxlKQ0KKiBgZmFjZXRfZ3JpZChjb2xBfmNvbEIpYCAtLT4gbWFrZXMgYSBncmlkIG9mIGdyYXBocywgd2hlcmUgdGhlIGRpZmZlcmVudCB2YWx1ZXMgb2YgY29sQSBhcmUgdGhlIHJvd3MgaW4gdGhlIGdyaWQsIGFuZCB0aGUgZGlmZmVyZW50IHZhbHVlcyBvZiBjb2xCIGFyZSB0aGUgY29sdW1ucyBpbiB0aGUgZ3JpZC4gVGhpcyB3b3JrcyBiZXN0IHdoZW4gY29sQSBhbmQgY29sQiBkb24ndCBoYXZlIHRvbyBtYW55IGRpZmZlcmVudCB2YWx1ZXMuDQoqIGArdGhlbWVfbWluaW1hbCgpYCBpcyBhIG5pY2UgdGhlbWUgdG8gdXNlIHdpdGggZmFjZXRlZCBwbG90cyAoY29tcGFyZWQgdG8gb3VyIHVzdWFsIGArdGhlbWVfY2xhc3NpYygpYCkNCg0KYGBge3J9DQpiYXJubmFtbiAlPiUgZmlsdGVyKG5hbWU9PSJMZWUiKSAlPiUgZ2dwbG90KGFlcyh4PXllYXIsIHk9bikpICsgZ2VvbV9saW5lKCkgKyBmYWNldF93cmFwKCJzZXgiKSt0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7cixtZXNzYWdlPUZBTFNFfQ0KYmFybm5hbW4gJT4lIA0KICBtdXRhdGUoRmluYWxMZXR0ZXIgPSBzdHJfc3ViKG5hbWUsIC0xLCAtMSkpICAlPiUgIA0KICBtdXRhdGUoRmluYWwgPSBpZl9lbHNlKEZpbmFsTGV0dGVyICVpbiUgYygiYSIsJ8OkJywnw7YnLCfDpScsICJlIiwgImkiLCAibyIsICJ1IiwgInkiKSwgInZvd2VsIiwgImNvbnNvbmFudCIpKSAlPiUgDQogIGdyb3VwX2J5KEZpbmFsLHllYXIsc2V4KSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbD1zdW0obikpICAlPiUgDQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXRvdGFsKSkgKyBnZW9tX2xpbmUoKSArIGZhY2V0X2dyaWQoc2V4IH4gRmluYWwpK3RoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIENoYW5naW5nIHRoZSBudW1iZXIgYW5kIGxvb2sgb2YgdGlja3Mgb24gdGhlIHggYXhpcw0KDQpZb3UgY2FuIGFkZCBtb3JlIHRpY2tzIHRvIGEgY29udGludW91cyB4LWF4aXMgdXNpbmcgdGhlIGZ1bmN0aW9uIGArc2NhbGVfeF9jb250aW51b3VzKG4uYnJlYWtzPW51bV90aWNrcylgLiBUaGUgbnVtYmVyIG9mIHRpY2tzIHRoYXQgeW91IGVudGVyIHdvbid0IGFsd2F5cyBiZSB0aGUgZXhhY3QgbnVtYmVyIG9mIGxhYmVscyBpdCBtYWtlcywgYnV0IGl0IHdpbGwgdHJ5IHRvIGdldCBpdCBhcyBjbG9zZSBhcyBwb3NzaWJsZSB3aGlsZSBzdGlsbCBlbnN1cmluZyBuaWNlIGJyZWFrIGxhYmVscy4NCg0KV2UgY2FuIGFsc28gbWFrZSB0aGUgYnJlYWsgbGFiZWxzIG5pY2VyIGJ5IGNoYW5naW5nIHRoZWlyIGFuZ2xlIGFuZCBwb3NpdGlvbi4gVGhpcyBpcyBkb25lIHdpdGggdGhlIGZ1bmN0aW9uIGArdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsdmp1c3Q9MC41KSlgLCBhbmQgYWdhaW4sIGp1c3QgcGxheSBhcm91bmQgd2l0aCB0aGUgdmFsdWVzIGZvciBgYW5nbGVgIGFuZCBgdmp1c3RgICh2ZXJ0aWNhbCBhZGp1c3RtZW50KSB1bnRpbCB5b3UgZ2V0IHNvbWV0aGluZyB0aGF0IGxvb2tzIHJpZ2h0LiBJZiBgdmp1c3RgIGRvZXNuJ3Qgd29yayBmb3IgeW91LCB0aGVyZSBpcyBhbHNvIGFuIGFyZ3VtZW50IGBoanVzdGAgKGhvcml6b250YWwgYWRqdXN0bWVudCkgdGhhdCB5b3UgY2FuIGFkZC4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCmJhcm5uYW1uICU+JSANCiAgbXV0YXRlKEZpbmFsTGV0dGVyID0gc3RyX3N1YihuYW1lLCAtMSwgLTEpKSAgJT4lICANCiAgbXV0YXRlKEZpbmFsID0gaWZfZWxzZShGaW5hbExldHRlciAlaW4lIGMoImEiLCfDpCcsJ8O2Jywnw6UnLCAiZSIsICJpIiwgIm8iLCAidSIsICJ5IiksICJ2b3dlbCIsICJjb25zb25hbnQiKSkgJT4lIA0KICBncm91cF9ieShGaW5hbCx5ZWFyLHNleCkgJT4lIA0KICBzdW1tYXJpc2UodG90YWw9c3VtKG4pKSAgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT10b3RhbCkpICsgZ2VvbV9saW5lKCkgKyBmYWNldF9ncmlkKHNleCB+IEZpbmFsKSt0aGVtZV9taW5pbWFsKCkrc2NhbGVfeF9jb250aW51b3VzKG4uYnJlYWtzPTE2KSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSx2anVzdD0wLjUpKQ0KYGBgDQoNCiMjIE1hcHBpbmcNCg0KV2UgY2FuIGdldCBtYXBzIGZyb20gdGhlIHBhY2thZ2VzIGBybmF0dXJhbGVhcnRoYCBhbmQgYHJuYXR1cmFsZWFydGhkYXRhYCwgYW5kIHBsb3QgdGhlbSB1c2luZyB0aGUgYGdncGxvdGAgZnVuY3Rpb24gYGdlb21fc2YoKWAuIGBzZmAgaXMgYSBmaWxlIGZvcm1hdCBmb3Igc3RvcmluZyBtYXBzLiANCg0KYGBge3IgbWFwcGluZ30NCiMgcmVxdWlyZWQgcGFja2FnZXMNCmxpYnJhcnkocm5hdHVyYWxlYXJ0aCkNCmxpYnJhcnkocm5hdHVyYWxlYXJ0aGRhdGEpDQpsaWJyYXJ5KGdncmVwZWwpDQoNCiMgdGhlc2UgcGFja2FnZXMgbWF5IG5vdCBmdW5jdGlvbiBwcm9wZXJseSBpZiB5b3UgZG9uJ3QgYWxzbyBoYXZlIHJnZW9zIGluc3RhbGxlZDsgaWYgeW91IGdldCBhbiBlcnJvciwgcnVuIGluc3RhbGwucGFja2FnZXMoJ3JnZW9zJykgaW4geW91ciBjb25zb2xlIGFuZCB0aGVuIHRyeSBhZ2Fpbg0KDQojIHRoZSBmdW5jdGlvbiBuZV9jb3VudHJpZXMgd2lsbCBnaXZlIHlvdSBjb3VudHJ5IG1hcHMsIGlmIHlvdSBkb24ndCBzcGVjaWZ5IHdoaWNoIGNvdW50cmllcywgaXQgZ2l2ZXMgeW91IHRoZSB3aG9sZSB3b3JsZC4gDQojIFVzZSByZXR1cm5jbGFzcz0ic2YiIHRvIG1ha2Ugc3VyZSB5b3UgZ2V0IGFuIHNmIGZvcm1hdCBiYWNrLCBhcyB0aGlzIGlzIHdoYXQgZ2VvbV9zZigpIHdvcmtzIHdpdGgNCndvcmxkIDwtIG5lX2NvdW50cmllcyhyZXR1cm5jbGFzcyA9ICJzZiIpDQoNCiMgeW91IG5lZWQgdG8gaGF2ZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGluZm9ybWF0aW9uIGZvciB0aGUgdGhpbmdzIHlvdSB3YW50IHRvIHBsb3QgLS0gSSBnZXQgdGhpcyBmcm9tIGdvb2dsZSBtYXBzIC0tIGp1c3QgcmlnaHQgY2xpY2sgb24gYSBwbGFjZSBpbiBnb29nbGUgbWFwcyAob24gYSBjb21wdXRlciksIGFuZCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB3aWxsIHNob3cgdXAgYW5kIHlvdSBjYW4gY29weSB0aGVtDQp0eXBlIDwtIGMoInJlbnRhbCIsInBhcmVudCdzIGhvdXNlIiwiaW4tbGF3J3MgaG91c2UiLCJyZW50YWwiLCJyZW50YWwiKQ0KbGF0aXR1ZGUgPC0gYyg1OS44NjUsLTMzLjg3OSw1MC44NzQsMzUuNjY0LC0zNS4yODQpDQpsb25naXR1ZGUgPC0gYygxNy42NDEsMTUxLjExMiw2LjAzNywxMzkuNDgyLDE0OS4xMzYpDQpwbGFjZXMgPC0gZGF0YS5mcmFtZSh0eXBlLGxhdGl0dWRlLGxvbmdpdHVkZSkNCg0KIyBzdGFydCBieSBwbG90dGluZyB0aGUgbWFwIGFzIGEgYmFzZSBsYXllcg0KIyB0aGlzIGFsd2F5cyBoYXMgdG8gY29tZSBmaXJzdCAtLSBpdCB3b24ndCB3b3JrIGlmIHlvdSBzdGFydCB3aXRoIHRoZSBwb2ludHMgYW5kIHRyeSB0byBhZGQgdGhlIG1hcCBhZnRlcg0Kd29ybGQlPiUNCiAgZ2dwbG90KCkrDQogIGdlb21fc2YoKSsNCiAgIyB0aGVuIHlvdSBjYW4gYWRkIHBvaW50cyBvbiB0b3Agb2YgaXQgLS0gbm90ZSB0aGF0IHlvdSBuZWVkIHRvIHNwZWNpZnkgdGhlIGRhdGEsIGJlY2F1c2UgdGhpcyBjb21lcyBmcm9tIGEgZGlmZmVyZW50IGRhdGFzZXQgdG8gdGhlIG9uZSBhdCB0aGUgdG9wIG9mIHRoZSBwaXBlDQogIGdlb21fcG9pbnQoZGF0YSA9IHBsYWNlcyxhZXMoeD1sb25naXR1ZGUseT1sYXRpdHVkZSxjb2xvdXI9dHlwZSkpKw0KICB0aGVtZV92b2lkKCkrICAgICMgdGhpcyBpcyBhIG5pY2UgdGhlbWUgZm9yIG1hcHM7IGl0IGdldHMgcmlkIG9mIHRoZSBheGVzDQogIGxhYnModGl0bGU9IlBsYWNlcyBJJ3ZlIExpdmVkIikNCg0KYGBgDQoNCllvdSBjYW4gdXNlIHRoZSAnY291bnRyeScgYXJndW1lbnQgaW4gYG5lX2NvdW50cmllcygpYCB0byBnZXQgYSBtYXAgb2YgYSBzaW5nbGUgY291bnRyeS4NCg0KYGBge3J9DQoNCkF1c3RyYWxpYSA8LSBuZV9jb3VudHJpZXMoY291bnRyeT0iQXVzdHJhbGlhIixyZXR1cm5jbGFzcyA9ICJzZiIpDQoNCmF1c3RyYWxpYW5fcGxhY2VzIDwtIHBsYWNlcyU+JWZpbHRlcihsYXRpdHVkZTwwKQ0KDQpBdXN0cmFsaWElPiUNCiAgZ2dwbG90KCkrDQogIGdlb21fc2YoKSsNCiAgIyBzaW5jZSB0aGUgZGF0YSBpc24ndCBjb21pbmcgZnJvbSB0aGUgdG9wIG9mIHRoZSBwaXBlLCB5b3UgbmVlZCB0byB0ZWxsIGl0IHRoZSBkYXRhIGFuZCBhZXN0aGV0aWNzDQogIGdlb21fcG9pbnQoZGF0YSA9IGF1c3RyYWxpYW5fcGxhY2VzLGFlcyh4PWxvbmdpdHVkZSx5PWxhdGl0dWRlKSkrDQogICMgeW91IGNvdWxkIHVzZSBsYWJlbHMgaW5zdGVhZCBvZiBjb2xvdXIgLS0gYnV0IGFnYWluIHlvdSBuZWVkIHRvIHRlbGwgaXQgdGhlIGFlc3RoZXNpY3MgYW5kIGRhdGEgdG8gdXNlDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gYXVzdHJhbGlhbl9wbGFjZXMsYWVzKHg9bG9uZ2l0dWRlLHk9bGF0aXR1ZGUsbGFiZWw9dHlwZSkpKw0KICBsYWJzKHRpdGxlPSJQbGFjZXMgSSd2ZSBMaXZlZCIpKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQpZb3UgY2FuIGFkZCBtdWx0aXBsZSBjb3VudHJpZXMgaWYgeW91IGxpa2U6DQoNCmBgYHtyfQ0KbWFwIDwtIG5lX2NvdW50cmllcyhjb3VudHJ5PWMoIkF1c3RyYWxpYSIsIk5ldyBaZWFsYW5kIikscmV0dXJuY2xhc3MgPSAic2YiKQ0KDQphdXN0cmFsaWFuX3BsYWNlcyA8LSBwbGFjZXMlPiVmaWx0ZXIobGF0aXR1ZGU8MCkNCg0KbWFwJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX3NmKCkrDQogICMgc2luY2UgdGhlIGRhdGEgaXNuJ3QgY29taW5nIGZyb20gdGhlIHRvcCBvZiB0aGUgcGlwZSwgeW91IG5lZWQgdG8gdGVsbCBpdCB0aGUgZGF0YSBhbmQgYWVzdGhldGljcw0KICBnZW9tX3BvaW50KGRhdGEgPSBhdXN0cmFsaWFuX3BsYWNlcyxhZXMoeD1sb25naXR1ZGUseT1sYXRpdHVkZSkpKw0KICAjIHlvdSBjb3VsZCB1c2UgbGFiZWxzIGluc3RlYWQgb2YgY29sb3VyIC0tIGJ1dCBhZ2FpbiB5b3UgbmVlZCB0byB0ZWxsIGl0IHRoZSBhZXN0aGVzaWNzIGFuZCBkYXRhIHRvIHVzZQ0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGF1c3RyYWxpYW5fcGxhY2VzLGFlcyh4PWxvbmdpdHVkZSx5PWxhdGl0dWRlLGxhYmVsPXR5cGUpKSsNCiAgbGFicyh0aXRsZT0iUGxhY2VzIEkndmUgTGl2ZWQiKSsNCiAgdGhlbWVfdm9pZCgpDQoNCmBgYA0KDQpPciB5b3UgY2FuIHVzZSB0aGUgYXJndW1lbnQgYGNvbnRpbmVudGAgdG8gc3BlY2lmeSBhbiBlbnRpcmUgY29udGluZW50Og0KDQpgYGB7cn0NCkFzaWEgPC0gbmVfY291bnRyaWVzKGNvbnRpbmVudD0iQXNpYSIscmV0dXJuY2xhc3MgPSAic2YiKQ0KQXNpYSU+JQ0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9zZigpKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQpBIGZ1bGwgbGlzdCBvZiBhdmFpbGFibGUgY291bnRyaWVzIGFuZCBjb250aW5lbnRzIGlzIHNob3duIGJlbG93Og0KDQpgYGB7cn0NCmxpYnJhcnkocmVhY3RhYmxlKQ0KZGF0YSA8LSBjb3VudHJpZXM1MA0KY291bnRyaWVzIDwtIGRhdGFAZGF0YQ0KY291bnRyaWVzJT4lDQogIHNlbGVjdChuYW1lLGNvbnRpbmVudCklPiUNCiAgdW5pcXVlKCklPiUNCiAgYXJyYW5nZShjb250aW5lbnQsbmFtZSklPiUNCiAgcmVuYW1lKGNvdW50cnk9bmFtZSklPiUNCiAgcmVhY3RhYmxlKHNlYXJjaGFibGUgPSBUUlVFLGZpbHRlcmFibGUgPSBUUlVFKQ0KYGBgDQoNCiMjIFZpc3VhbGluZyB2YXJpYXRpb24gYWNyb3NzIG11bHRpcGxlIHZhcmlhYmxlcw0KDQpJZiB5b3UgaGF2ZSAzIG9yIDQgdmFyaWFibGVzLCBJIHdvdWxkIHRyeSB0byB2aXN1YWxpc2UgdGhlIHRoaXJkIGFuZCBmb3VydGggdmFyaWFibGVzIGJ5IHVzaW5nIGZlYXR1cmVzIGxpa2UgY29sb3VyLCBvciBieSBtYWtpbmcgbXVsdGlwbGUgcGxvdHMgKHdpdGggYGZhY3RldF93cmFwKClgIGFuZCBgZmFjZXRfZ3JpZCgpYCkuDQoNCkhvd2V2ZXIsIGlmIHlvdSBoYXZlIGEgd2hvbGUgYnVuY2ggb2YgdmFyaWFibGVzIHRoYXQgYXJlIGFsbCByZWxhdGVkL2Zvcm0gYSBjb2hlc2l2ZSBzZXQsIGFuZCB5b3Ugd2FudCB0byBnZXQgYW4gaWRlYSBvZiBob3cgc2ltaWxhciBvciBkaWZmZXJlbnQgaXRlbXMgYXJlIG92ZXJhbGwsIGJhc2VkIG9uIHRoZWlyIHZhbHVlcyBmb3IgKmFsbCogdGhlc2UgdmFyaWFibGVzLCB5b3UgY2FuIGRvIHRoaXMgYnkgY3JlYXRpbmcgYSAqZGlzdGFuY2UgbWF0cml4Ki4gQSBkaXN0YW5jZSBtYXRyaXggc2hvd3MgdGhlICpvdmVyYWxsKiBkaXN0YW5jZXMgYmV0d2VlbiBhbGwgdGhlIGl0ZW1zIGluIHlvdXIgZGF0YSwgYnkgdGFraW5nIHRoZSBzdW0gb2YgdGhlaXIgZGlzdGFuY2VzIGJhc2VkIG9uIGVhY2ggdmFyaWFibGUgKGZvciBtb3JlIGV4cGxhbmF0aW9uIHNlZSB0aGUgbm90ZXMgZnJvbSBsZWN0dXJlIDgpLg0KDQpOb3RlIHRoYXQgdGhpcyBvbmx5IHdvcmtzIGlmIHlvdXIgZGF0YSBpcyBudW1lcmljYWwsIG9yIGlmIGl0IGhhcyBiZWVuIGJpbmFyaXNlZC4gRm9yIG51bWVyaWNhbCBkYXRhLCBtYWtlIHN1cmUgeW91IG5vcm1hbGlzZSB0aGUgZGF0YSBzbyB0aGF0IHZhbHVlcyBvZiBlYWNoIHZhcmlhYmxlIGFyZSBjb21wYXJhYmxlIGJldHdlZW4gZGlmZmVyZW50IGl0ZW1zIChlLmcuIGluc3RlYWQgb2YgdXNpbmcgcmF3IGNvdW50cywgdXNlIHByb3BvcnRpb25zKS4NCg0KVGhlcmUgYXJlIHR3byBkaWZmZXJlbnQga2luZHMgb2YgdmlzdWFsaXNhdGlvbnMgeW91IGNhbiBtYWtlIGZyb20gYSBkaXN0YW5jZSBtYXRyaXguIA0KDQoxLiBZb3UgY2FuIGF0dGVtcHQgdG8gKnBsb3QqIHRoZXNlIGRpc3RhbmNlcywgYnkgdHJhbnNmb3JtaW5nIHRoZW0gaW50byAyIG9yIDMgZGltZW5zaW9uYWwgY29vcmRpbmF0ZXMsIGFuZCBwbG90dGluZyB0aGUgaXRlbXMgYXMgcG9pbnRzIGFsb25nIHRoZXNlIGRpbWVuc2lvbnMuDQoyLiBZb3UgY2FuIGNsdXN0ZXIgaXRlbXMgdGhhdCBhcmUgY2xvc2UgdG8gZWFjaG90aGVyIHRvZ2V0aGVyIHJlcGVhdGVkbHksIHVudGlsIHlvdSBnZXQgYSBicmFuY2hpbmcgdHJlZS4NCg0KVGhlIGZpcnN0IG1ldGhvZCBpcyBjYWxsZWQgKm11bHRpZGltZW5zaW9uYWwgc2NhbGluZyosIGFuZCB0aGUgc2Vjb25kIGlzIGNhbGxlZCAqY2x1c3RlciBhbmFseXNpcyouDQoNCk11bHRpZGltZW5zaW9uYWwgc2NhbGluZyB3b3JrcyB3ZWxsIGlmIHRoZSBkYXRhIGlzIGVhc2lseSByZWR1Y2VkIHRvIHR3byBvciB0aHJlZSBkaW1lbnNpb25zLCBidXQgdGhpcyBpc24ndCBhbHdheXMgcG9zc2libGUuIENsdXN0ZXIgYW5hbHlzaXMgd29ya3MgYmV0dGVyIHdoZW4geW91IGhhdmUgY29tcGxpY2F0ZWQgZGF0YSB3aGljaCB2YXJpZXMgYWxvbmcgbXVsdGlwbGUgZGltZW5zaW9ucy93aXRoIG1hbnkgZGlzdGluY3QgZ3JvdXBzLg0KDQojIyMgTXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nDQoNCjEuIENvbnZlcnQgdGhlIGRhdGEgaW50byBhIGRpc3RhbmNlIG1hdHJpeC0tbWFrZSBzdXJlIGl0IGlzIGFwcHJvcHJpYXRlbHkgbm9ybWFsaXNlZC9iaW5hcmlzZWQgZmlyc3QtLXVzaW5nIHRoZSBmdW5jdGlvbiBgZGlzdCgpYC4gQWRkIGBtZXRob2Q9ImJpbmFyeSJgIGlmIHRoZSBkYXRhIGlzIGJpbmFyaXNlZC4gSWYgaXQncyBudW1lcmljYWwsIHRoZSBkZWZhdWx0IG1ldGhvZCAoYGV1Y2xpZGVhbmApIGlzIGFwcHJvcHJpYXRlLg0KMi4gRmluZCBjb29yZGluYXRlcyBmb3IgdGhlc2UgZGlzdGFuY2VzIHVzaW5nIGBjbWRzY2FsZShkaXN0YW5jZXMsIGs9MilgLiBrIHNldHMgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zIHRvIHVzZTsgdGhlIGRlZmF1bHQgaXMgdHdvLiANCjMuIGBjbWRzY2FsZSgpYCB3aWxsIG1ha2UgY29vcmRpbmF0ZXMgZm9yIHlvdXIgZGlzdGFuY2VzIGFzIHdlbGwgYXMgaXQgY2FuLCBidXQgaXQgY2FuJ3QgYWx3YXlzIHJlcHJlc2VudCB0aGUgZGlzdGFuY2VzIHBlcmZlY3RseSwgc28geW91IHNob3VsZCBjaGVjayBob3cgY2xvc2VseSB0aGUgZGlzdGFuY2VzIGJldHdlZW4gdGhlIGNvb3JkaW5hdGVzIG1hdGNoZXMgdGhlIGFjdHVhbCBkaXN0YW5jZXMuIFRoaXMgaXMgY2FsbGVkIGNhbGN1bGF0aW5nIHRoZSAqc3RyZXNzKiBvbiB0aGUgY29vcmRpbmF0ZXMsIGFuZCB3ZSB3aWxsIG1ha2Ugb3VyIG93biBgc3RyZXNzKClgIGZ1bmN0aW9uIHRvIGRvIHRoaXMuDQoNCmBgYHtyLGluY2x1ZGU9VFJVRX0NCiMgZCBpcyB0aGUgb3JpZ2luYWwgZGlzdGFuY2VzLCBEIGlzIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiB0aGUgY29vcmRpbmF0ZXMgbWFkZSBieSBjbWRzY2FsZSgpDQpzdHJlc3MgPC0gZnVuY3Rpb24oZCwgRCl7DQogIHNxcnQoc3VtKChkIC0gRCkgXiAyKSAvIHN1bShkIF4gMikpDQp9DQpgYGANCg0KVGhpcyBpcyBob3cgc3RyZXNzIHZhbHVlcyBhcmUgaW50ZXJwcmV0ZWQ6DQoNClZhbHVlICAgICAgICAgICAgICAgIHwgSW50ZXJwcmV0YXRpb24NCi0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tDQpTdHJlc3MgPj0gMC4yICAgICAgICB8IFBvb3INCjAuMSA8PSBTdHJlc3MgPCAwLjIgIHwgRmFpcg0KMC4wNSA8PSBTdHJlc3MgPCAwLjEgfCBHb29kDQpTdHJlc3MgPCAwLjA1ICAgICAgICB8IEV4Y2VsbGVudA0KDQo0LiBJZiB0aGUgc3RyZXNzIGlzIG9rYXksIHRoZW4geW91IGNhbiBwbG90IHRoZSBjb29yZGluYXRlcyBpbiB0d28gZGltZW5zaW9ucy4gSWYgaXQncyBub3Qgb2theSwgdGhlbiBwcm9iYWJseSBhIHRocmVlLWRpbWVuc2lvbmFsIG9yIGhpZ2hlciBzb2x1dGlvbiBpcyBuZWVkZWQgdG8gYWNjdXJhdGVseSByZXByZXNlbnQgdGhlIGRhdGEuIFlvdSBjYW4gY2hlY2sgaG93IG1hbnkgZGltZXNpb25zIGFyZSBuZWVkZWQgYnkgcGxheWluZyBhcm91bmQgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIGsgaW4gdGhlIGBjbWRzY2FsZSgpYCBmdW5jdGlvbiwgYW5kIHRoZW4gY2hlY2tpbmcgdGhlIHN0cmVzcyBvbiB0aG9zZSBjb29yZGluYXRlcyAoc2VlIG5vdGVzIGZyb20gbGVjdHVyZSA4KS4gDQoNCllvdSB3aWxsIGFsc28gbmVlZCB0aGUgZnVuY3Rpb25zIGBjb2x1bW5fdG9fcm93bmFtZXMoKWAgYW5kIGBhc19kYXRhZnJhbWUocm93bmFtZXM9ImNvbHVtbl9uYW1lIilgIHRvIGdvIGJldHdlZW4gdGhlIGRhdGEgZm9ybWF0IHJlcXVpcmVkIGJ5IGBkaXN0KClgLCBhbmQgdGhhdCBuZWVkZWQgZm9yIHBsb3R0aW5nLiANCg0KQmVsb3cgaXMgYW4gZXhhbXBsZSB1c2luZyBldWNsaWRlYW4gZGlzdGFuY2VzIChpLmUuIHdpdGggbnVtZXJpY2FsIGRhdGEpLCB0byB2aXN1YWxpc2UgZGlzdGFuY2VzIGJldHdlZW4gZGlmZmVyZW50IEphcGFuZXNlIGNhc2UgcGFydGljbGVzIGluIHRlcm1zIG9mIHRoZWlyIGZ1bmN0aW9ucy4NCg0KYGBge3IgZXVjbGlkZWFufQ0KbGlicmFyeShnZ3JlcGVsKQ0KDQojIHRveSBkYXRhIGFib3V0IHRoZSBmcmVxdWVuY3kgb2YgdXNlIChhcyBhIHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIGZyZXF1ZW5jeSBmb3IgZWFjaCBwYXJ0aWNsZSkgd2l0aCBkaWZmZXJlbnQgY2FzZXMgZm9yIHNvbWUgSmFwYW5lc2UgcGFydGljbGVzDQpwYXJ0aWNsZSA8LSBjKCJnYSIsIm5pIiwiZGUiLCJ3YSIsIm8iKQ0Kbm9taW5hdGl2ZSA8LSBjKDAuOTUsMCwwLDAuNSwwKQ0KZ2VuaXRpdmUgPC0gYygwLjA1LDAsMCwwLDApDQpsb2NhdGl2ZSA8LSBjKDAsMC42LDAuNywwLjEsMCkNCmluc3RydW1lbnRhbCA8LSBjKDAsMCwwLjMsMC4wNSwwKQ0KYWxsYXRpdmUgPC0gYygwLDAuMywwLDAuMSwwKQ0KYWNjdXNhdGl2ZSA8LSBjKDAsMC4xLDAsMC4zNSwxKQ0KDQpwYXJ0aWNsZXMgPC0gZGF0YS5mcmFtZShwYXJ0aWNsZSxub21pbmF0aXZlLGdlbml0aXZlLGxvY2F0aXZlLGluc3RydW1lbnRhbCxhbGxhdGl2ZSxhY2N1c2F0aXZlKQ0KDQojIDEuIE1ha2UgdGhlIGRpc3RhbmNlIG1hdHJpeA0KcGFydGljbGVzJT4lDQogICMgdHVybiB0aGUgZmlyc3QgY29sdW1uIGludG8gcm93bmFtZXMgZmlyc3QgLS0geW91ciBjb2x1bW5zIHNob3VsZCBvbmx5IGNvbnRhaW4gbWVhc3VyZW1lbnRzIG9mIHlvdXIgdmFyaWFibGVzIG9mIGludGVyZXN0DQogIGNvbHVtbl90b19yb3duYW1lcygicGFydGljbGUiKSU+JQ0KICBkaXN0KCktPiBkaXN0YW5jZXMNCg0KIyAyLiBNYWtlIGNvb3JkaW5hdGVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdXNlIGs9MywgNCBldGMuIGZvciBoaWdoZXItZGltZW5zaW9uYWwgc29sdXRpb25zDQpjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMsaz0yKQ0KDQojIDMuIENoZWNrIHN0cmVzcyBvbiBjb29yZGluYXRlcw0KDQpzdHJlc3MoZGlzdGFuY2VzLGRpc3QoY29vcmRpbmF0ZXMpKQ0KYGBgDQoNCkhlcmUsIHRoZSBzdHJlc3MgaXMgbGVzcyB0aGFuIDAuMSBzbyB0aGUgdHdvLWRpbWVuc2lvbmFsIGNvb3JkaW5hdGVzIHJlcHJlc2VudCB0aGUgZGlzdGFuY2VzIHdlbGwsIGFuZCB0aGUgZGF0YSBjYW4gYmUgZWFzaWx5IHBsb3R0ZWQNCg0KYGBge3IgZXVjbGlkZWFuIHBsb3R9DQojIDMuIFBsb3QgY29vcmRpbmF0ZXMNCg0KY29vcmRpbmF0ZXMlPiUNCiAgICAgICAgIyBwdXQgdGhlIHJvd25hbWVzIGludG8gYSBjb2x1bW4gZmlyc3QNCiAgYXNfZGF0YV9mcmFtZShyb3duYW1lcz0icGFydGljbGUiKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9VjEseT1WMixsYWJlbD1wYXJ0aWNsZSkpK2dlb21fcG9pbnQoKStnZW9tX3RleHRfcmVwZWwoKSt0aGVtZV9jbGFzc2ljKCktPnBsb3QxDQpwbG90MQ0KYGBgDQoNCllvdSBjYW4gc2VlIHRoYXQgKmRlKiBhbmQgKm5pKiBoYXZlIHNpbWlsYXIgZnVuY3Rpb25zLCBhcyB0aGV5IGdyb3VwIHRvZ2V0aGVyLCB3aGlsZSAqZ2EqIGFuZCAqbyogaGF2ZSBvcHBvc2l0ZSBmdW5jdGlvbnMsIHdpdGggKndhKiBmYWxsaW5nIGluIGJldHdlZW4gKmdhKiBhbmQgKm8qLCBidXQgY2xvc2VyIHRvICpnYSouIFRoaXMgaXMgYmVjYXVzZSAqZGUqIGFuZCAqbmkqIGJvdGggaGF2ZSB0byBkbyB3aXRoIGxvY2F0aW9uLCB3aGlsZSAqZ2EqIGlzIG5vbWluYXRpdmUgYW5kICpvKiBpcyBhY2N1c2F0aXZlLiBCdXQgKndhKiBjYW4gdGFrZSBib3RoIG5vbWluYXRpdmUgYW5kIGFjY3VzYXRpdmUgY2FzZSwgYmVjYXVzZSBpdCdzIGEgdG9waWMgbWFya2VyLS10aGF0J3Mgd2h5IGl0IGZhbGxzIGluIGJldHdlZW4gKmdhKiBhbmQgKm8qLiBIb3dldmVyLCBub21pbmF0aXZlbHkgbWFya2VkIG5vdW5zIGFyZSBtb3JlIG9mdGVuIHRvcGljcyB0aGFuIGFjY3VzYXRpdmVseSBtYXJrZWQgbm91bnMsIHdoaWNoIGlzIHdoeSBpdCBpcyBjbG9zZXIgdG8gKmdhKiB0aGFuIHRvICpvKi4NCg0KQmVsb3cgaXMgYW4gZXhhbXBsZSB3aXRoIGJpbmFyeSBkaXN0YW5jZXMsIHVzaW5nIGNvZ25hY3kgZGF0YSB0byBkZXRlcm1pbmUgc2ltaWxhcml0aWVzIGJldHdlZW4gZGlmZmVyZW50IGRpYWxlY3RzIG9mIEphcGFuZXNlLg0KDQpgYGB7ciBiaW5hcnksbWVzc2FnZT1GQUxTRX0NCmphcG9uaWMgPC0gcmVhZF9jc3YoImRhdGEvamFwdm9jYWJtYXRyaXguY3N2IikNCg0KamFwb25pYyU+JQ0KICByZW5hbWUobGFuZ3VhZ2U9WCklPiUNCiAgIyBleGNsdWRlZCBkZWFkIGxhbmd1YWdlcyBhbmQgT2tpbmF3YSBiZWNhdXNlIGl0IGlzIG5vdCBhIGRpYWxlY3QgYW5kIHdpbGwgbWVzcyB1cCB0aGUgc2NhbGUgb24gdGhlIG1hcCBieSBiZWluZyBzdXBlciBmYXIgYXdheQ0KICBmaWx0ZXIobGFuZ3VhZ2UhPSJPbGRfSmFwYW5lc2UiLGxhbmd1YWdlIT0iTWlkZGxlX0phcGFuZXNlIixsYW5ndWFnZSE9Ik9raW5hd2EiKSU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoImxhbmd1YWdlIiklPiUNCiAgZGlzdChtZXRob2QgPSAiYmluYXJ5IiktPmRpc3RhbmNlcw0KDQpjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMsaz0yKQ0KDQpjb29yZGluYXRlcyU+JQ0KICBhc19kYXRhX2ZyYW1lKHJvd25hbWVzPSJMb2NhdGlvbiIpJT4lDQogIGdncGxvdChhZXMoeD1WMix5PS1WMSxsYWJlbD1Mb2NhdGlvbikpKw0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fdGV4dF9yZXBlbCgpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIGxhYnMoeD0iQ29uc2VydmF0aXZlIHZlcnN1cyBwcm9ncmVzc2l2ZSIseT0iU291dGggdG8gbm9ydGgiKQ0KYGBgDQoNCkhlcmUsIGluc3RlYWQgb2YgaGF2aW5nIGRpc3RpbmN0IGdyb3Vwcywgd2UgYWN0dWFsbHkgaGF2ZSBhIGNvbnRpbnV1bSB3aGljaCB3ZSBjYW4gbGFiZWwgYXMgcmVmbGVjdGluZyB0aGUgc3BhdGlhbCBsb2NhdGlvbiAoc291dGggdG8gbm9ydGgpIG9uIHRoZSBvbmUgaGFuZCwgYXMgd2VsbCBhcyB0aGUgY29uc2VydmF0aXZlbmVzcyBvZiB0aGUgZGlhbGVjdCBvbiB0aGUgb3RoZXIgaGFuZC0taWYgeW91IGtub3cgYWJvdXQgSmFwYW5lc2UgZGlhbGVjdG9sb2d5LiANCg0KVGhlIHN0cmVzcyBvbiB0aGlzIHJlcHJlc2VudGF0aW9uIGlzIGFjdHVhbGx5IGZhaXJseSBoaWdoDQoNCmBgYHtyfQ0Kc3RyZXNzKGRpc3RhbmNlcyxkaXN0KGNvb3JkaW5hdGVzKSkNCmBgYA0KDQpIb3dldmVyLCBhIHRocmVlLWRpbWVuc2lvbmFsIHNvbHV0aW9uIGlzIG9ubHkgbWFyZ2luYWxseSBiZXR0ZXI6DQoNCmBgYHtyfQ0KaGlnaGVyX3NvbHV0aW9uIDwtIGNtZHNjYWxlKGRpc3RhbmNlcyxrPTMpDQpzdHJlc3MoZGlzdGFuY2VzLGRpc3QoaGlnaGVyX3NvbHV0aW9uKSkNCmBgYA0KDQpIZXJlLCB0aGUgaGlnaCBzdHJlc3MgdmFsdWUgaXMgbGlrZWx5IGJlY2F1c2Ugd2UgaGF2ZSAqYSBsb3QqIG9mIGxhbmd1YWdlcywgc28gaXQgbWF5IGJlIHRoYXQgaXQgaXMgaGFyZCB0byBnZXQgdGhlIGNvcnJlY3QgcG9zaXRpb25pbmcgb2YgdGhlIGxhbmd1YWdlcyB3aXRoaW4gZWFjaCBzbWFsbCBncm91cC4gVGhhdCBpcywgdGhlIHNwYXRpYWwgcmVsYXRpb25zaGlwIGJldHdlZW4gbGFuZ3VhZ2VzIHRoYXQgYXJlIGNsb3NlIHRvIGVhY2hvdGhlciBtYXkgbm90IGJlIHZlcnkgYWNjdXJhdGUuIEhvd2V2ZXIsIHRoZSBtYWluIGRpYWxlY3QgZ3JvdXBzIGFuZCB0aGUgbGFyZ2UgZGlzdGFuY2VzIGluIHRoaXMgZmlndXJlIGFyZSB3ZWxsLXJlcHJlc2VudGVkIChiYXNlZCBvbiB3aGF0IHdlIGFscmVhZHkga25vdyBhYm91dCBKYXBvbmljKSwgc28gSSB0aGluayBpdCBpcyBzdGlsbCBhIGdvb2QgcmVwcmVzZW50YXRpb24tLWFzIGxvbmcgYXMgeW91IHdhcm4gcGVvcGxlIG5vdCB0byBsb29rIGF0IHRoZSBzbWFsbCBkaXN0YW5jZXMuIElmIHRoaXMgdHdvLWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIHdhcyByZWFsbHkgYSBiYWQgcmVwcmVzZW5hdGlvbiwgd2Ugd291bGQgZXhwZWN0IGEgYmlnZ2VyIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgc3RyZXNzIGxldmVscyBvZiBhIHR3by1kaW1lbnNpb25hbCB2ZXJzdXMgdGhyZWUtZGltZW5zaW9uYWwgc29sdXRpb24uIA0KDQojIyMgQ2x1c3RlciBhbmFseXNpcw0KDQpJZiB5b3Ugd2FudCB0byBrbm93IGFib3V0IHRoZSBjbG9zZSByZWxhdGlvbnNoaXBzIChzbWFsbCBkaXN0YW5jZXMpIGFzIHdlbGwsIHlvdSBjb3VsZCB1c2UgYSBjbHVzdGVyIGFuYWx5c2lzDQoNCmBgYHtyfQ0KY2x1c3RlcnMgPC0gaGNsdXN0KGRpc3RhbmNlcyxtZXRob2Q9IndhcmQuRDIiKSAgICMgbWV0aG9kPSJ3YXJkLkQyIiB1c3VhbGx5IHByb2R1Y2VzIGdvb2QgcmVzdWx0cw0KcGxvdChjbHVzdGVycykNCmBgYA0KDQpDb21wYXJlZCB0byBtdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcsIGNsdXN0ZXIgYW5hbHlzZXMgYXJlIGJldHRlciBhdCBzaG93aW5nIGRpc3RpbmN0IGdyb3VwcyB3aGVuIHlvdSBoYXZlIGEgbG90IG9mIGRhdGEgcG9pbnRzLiBGcm9tIHRoZSBmaXJzdCB0d28gc3BsaXRzIGluIHRoZSB0cmVlIGhlcmUsIHdlIGFscmVhZHkgaGF2ZSBmb3VyIGRpc3RpbmN0IGdyb3Vwcy0tdGhleSBiYXNpY2FsbHkgcmVwcmVzZW50IHRoZSBub3J0aCBhbmQgZWFzdCBvZiBjb3VudHJ5LCB2ZXJzdXMgdGhlIHNvdXRoIGFuZCB3ZXN0IG9mIHRoZSBjb3VudHJ5LiBIb3dldmVyLCBpbiB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHJlcHJlc2VudGF0aW9uIHRoZSB3ZXN0ZXJuIGFuZCBlYXN0ZXJuIGdyb3VwcyBlbmRlZCB1cCBzcXVpc2hlZCB0b2dldGhlciAoaXQgd2FzIGhhcmQgdG8gY2xlYXJseSBzZXBhcmF0ZSB0aGVtKSwgc28gdGhhdCBjb3VsZCBhbHNvIGNvbnRyaWJ1dGUgdG8gaGlnaCBzdHJlc3MgbGV2ZWxzLiBUaGlzIGlzIHByb2JhYmx5IGJlY2F1c2UgdGhlIGVhc3Rlcm4gYW5kIHdlc3Rlcm4gZ3JvdXBzIGFyZSBsZXNzIGRpc3RpbmN0IGZyb20gZWFjaG90aGVyIHRoYW4gdGhlIG5vcnRoZXJuIGFuZCBzb3V0aGVybiBncm91cHMgKHdoaWNoIHdlcmUgcHJlc2VydmVkKSwgYW5kIGxpa2Ugd2Ugc2FpZCB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHJlcHJlc2VudGF0aW9uIGp1c3Qgc2hvd3MgdGhlIGdlbmVyYWwgdHJlbmRzIGFuZCBub3QgdGhlIHNtYWxsIGRpZmZlcmVuY2VzLg0KDQpTbywgaWYgSSB3ZXJlIHRvIHByZXNlbnQgdGhpcyBkYXRhLCBJIHdvdWxkIGluY2x1ZGUgYm90aCB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIGFuYWx5c2lzICh0byBzaG93IHRoZSBvdmVyYWxsIHRyZW5kcyksIGFzIHdlbGwgYXMgdGhlIGNsdXN0ZXIgYW5hbHlzaXMgKHRvIHNob3cgZGlzdGluY3QsIGluY2x1ZGluZyBzbWFsbGVyLWxldmVsLCBncm91cGluZ3MpLg0KDQojIyMgV2hlbiB0byB1c2Ugd2hpY2ggbWV0aG9kPw0KDQpJZiB5b3Ugb25seSBoYXZlIGEgZmV3IGRhdGEgcG9pbnRzIChsaWtlIGluIHRoZSBmaXJzdCBleGFtcGxlIHdpdGggdGhlIEphcGFuZXNlIHBhcnRpY2xlcyksIG11bHRpZGltZW5zaW9uYWwgc2NhbGluZyBhbmQgY2x1c3RlciBhbmFseXNlcyBhcmUgdXN1YWxseSBlcXVhbGx5IGdvb2QgYXQgcmVwcmVzZW50aW5nIHRoZSBkaXN0YW5jZXMsIGJlY2F1c2UgdGhlcmUncyBlbm91Z2ggc3BhY2Ugb24gdGhlIG11bHRpZGltZW5zaW9uYWwgc2NhbGVkIHBsb3QgdG8ga2VlcCBhbGwgdGhlIGdyb3VwcyBkaXN0aW5jdC4gU28gaXQncyBqdXN0IGEgbWF0dGVyIG9mIHBlcnNvbmFsIHByZWZlcmVuY2UgYXMgdG8gd2hpY2ggdmlzdWFsaXNhdGlvbiB5b3UgcHJlZmVyLCBhbHRob3VnaCBpZiB5b3UgdGhpbmsgdGhlIGRhdGEgaXMgaGllcmFyY2hpY2FsbHkgc3RydWN0dXJlZCB0aGUgY2x1c3RlciBhbmFseXNpcyB3b3VsZCByZXByZXNlbnQgdGhpcyBoaWVyYXJjaGljYWwgc3RydWN0dXJlIGJldHRlci4NCg0KSWYgeW91IGhhdmUgYSBsb3Qgb2YgZGF0YSBwb2ludHMsIGhvd2V2ZXIsIGl0IGlzIGhhcmQgdG8ga2VlcCBncm91cHMgZGlzdGluY3Qgb24gbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHBsb3RzIChhcyB3ZSBzYXcgd2l0aCB0aGUgSmFwYW5lc2UgZGF0YSkuIEluIHRoaXMgY2FzZSwgdGhlIGNsdXN0ZXIgYW5hbHlzaXMgaXMgYmV0dGVyIGZvciBzaG93aW5nIHRoZSBncm91cGluZ3MsIGFuZCBpdCBpcyBvbmx5IHdvcnRoIHN0aWxsIHVzaW5nIHRoZSBtdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcgcGxvdCBpZiB5b3UgdGhpbmsgdGhlIGRpbWVuc2lvbnMgYXJlIG1lYW5pbmdmdWwgYW5kIHJlcHJlc2VudCBzb21lIG92ZXJhbGwgdHJlbmRzIGluIHRoZSBkYXRhIChlLmcuIHRoZSBub3J0aC1zb3V0aCwgY29uc2VydmF0aXZlLXByb2dyZXNzaXZlIHRyZW5kcyB3ZSBzYXcgaW4gdGhlIEphcGFuZXNlIGRhdGEpLiBJZiB5b3UgY2Fubm90IHRoaW5rIG9mIGFueSBtZWFuaW5nZnVsIGxhYmVscyBmb3IgeW91ciBkaW1lbnNpb25zLCB0aGVuIHByb2JhYmx5IGFsbCB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIGFuYWx5c2lzIGlzIGRvaW5nIGlzIHRyeWluZyB0byByZXByZXNlbnQgZGlzdGluY3QgZ3JvdXBzLS1yYXRoZXIgdGhhbiBhbnkga2luZCBvZiBjb250aW51dW0tLWluIHdoaWNoIGNhc2UgSSB3b3VsZCBqdXN0IHVzZSBhIGNsdXN0ZXIgYW5hbHlzaXMgYmVjYXVzZSB0aGF0IGRvZXMgZ3JvdXBpbmcgYmV0dGVyIHdoZW4gdGhlcmUncyBsb3RzIG9mIGRhdGEgcG9pbnRzLg0KDQpGb3IgaW5zdGFuY2UsIGJlbG93IGlzIGFuIGV4YW1wbGUsIHBsb3R0aW5nIHdvcmRzIGJhc2VkIG9uIHRoZWlyIHNlbnNvcnkgbW9kYWxpdHkgcmF0aW5nczoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCm5vcm1zIDwtIHJlYWRfY3N2KCJkYXRhL2x5bm90dF9jb25uZWxsXzIwMDlfbW9kYWxpdHkuY3N2IikNCg0Kbm9ybXMlPiUNCiAgc2VsZWN0KC1Qcm9wZXJ0eUJyaXRpc2gsLURvbWluYW50TW9kYWxpdHkpJT4lDQogIGNvbHVtbl90b19yb3duYW1lcygiV29yZCIpJT4lDQogIHNhbXBsZV9uKDUwKSU+JQ0KICBkaXN0KCktPmRpc3RhbmNlcw0KDQpjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMpDQoNCmNvb3JkaW5hdGVzJT4lDQogIGFzX2RhdGFfZnJhbWUocm93bmFtZXM9IldvcmQiKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9VjEseT1WMixsYWJlbD1Xb3JkKSkrIA0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fdGV4dF9yZXBlbCgpKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpUaGVyZSBncm91cGluZ3MgYXJlIG5vdCB2ZXJ5IGNsZWFyLCBub3IgY2FuIHdlIGNvbWUgdXAgd2l0aCBhbnkgbWVhbmluZ2Z1bCBsYWJlbHMgZm9yIHRoZXNlIGRpbWVuc2lvbnMuIEluIHRoaXMgY2FzZSwgSSB3b3VsZCBqdXN0IHVzZSBhIGNsdXN0ZXIgYW5hbHlzaXMgYmVjYXVzZSBhdCBsZWFzdCB0aGVuIHRoZSBncm91cGluZ3MgYXJlIGNsZWFyOg0KDQpgYGB7cn0NCmNsdXN0ZXJzIDwtIGhjbHVzdChkaXN0YW5jZXMsbWV0aG9kPSJ3YXJkLkQyIikgICAjIG1ldGhvZD0id2FyZC5EMiIgdXN1YWxseSBwcm9kdWNlcyBnb29kIHJlc3VsdHMNCnBsb3QoY2x1c3RlcnMpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRoZSBncm91cGluZ3MgYXJlIG5vdCB3ZWxsIHByZXNlcnZlZCBpbiB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHBsb3QgZnJvbSB0aGUgaGlnaCBzdHJlc3Mgb24gdGhlIGNvb3JkaW5hdGVzOg0KDQpgYGB7cn0NCnN0cmVzcyhkaXN0YW5jZXMsZGlzdChjb29yZGluYXRlcykpDQpgYGANCg0KQSB0aHJlZS1kaW1lbnNpb25hbCBzb2x1dGlvbiBpcyBhYmxlIHRvIHJlcHJlc2VudCB0aGUgZ3JvdXBpbmdzIGJldHRlcjoNCg0KYGBge3J9DQpuZXdjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMsaz0zKQ0Kc3RyZXNzKGRpc3RhbmNlcyxkaXN0KG5ld2Nvb3JkaW5hdGVzKSkNCmBgYA0KDQpBbmQgd2UgY291bGQgZXh0cmFjdCB0aGUgZGlmZmVyZW50IGdyb3VwaW5ncyBieSBwbG90dGluZyB0aGUgZGltZW5zaW9ucyBvbmUgYWZ0ZXIgdGhlIG90aGVyOg0KDQpgYGB7cn0NCm5ld2Nvb3JkaW5hdGVzJT4lDQogIGFzX2RhdGFfZnJhbWUocm93bmFtZXM9IldvcmQiKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9VjEseT1WMixsYWJlbD1Xb3JkKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV90ZXh0X3JlcGVsKCkrDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQpuZXdjb29yZGluYXRlcyU+JQ0KICBhc19kYXRhX2ZyYW1lKHJvd25hbWVzPSJXb3JkIiklPiUNCiAgZ2dwbG90KGFlcyh4PVYxLHk9VjMsbGFiZWw9V29yZCkpKw0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fdGV4dF9yZXBlbCgpKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KbmV3Y29vcmRpbmF0ZXMlPiUNCiAgYXNfZGF0YV9mcmFtZShyb3duYW1lcz0iV29yZCIpJT4lDQogIGdncGxvdChhZXMoeD1WMix5PVYzLGxhYmVsPVdvcmQpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZW9tX3RleHRfcmVwZWwoKSsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmBgYA0KDQpCdXQgdGhpcyBpcyBhIGxvdCBtb3JlIHdvcmsgKGFuZCBzdGlsbCBpdCdzIGVhc2llciB0byBzZWUgdGhlIGdyb3VwcyBpbiB0aGUgY2x1c3RlciBkZW5kcm9ncmFtKSwgc28gSSB3b3VsZCBqdXN0IHVzZSB0aGUgY2x1c3RlciBhbmFseXNpcyBpbnN0ZWFkLiBJIHdvdWxkIG9ubHkgdHJ5IHBsb3R0aW5nIGFsbCB0aGUgZGltZW5zaW9ucyBpZiB5b3UgdGhpbmsgdGhleSdyZSBhbGwgbWVhbmluZ2Z1bC4NCg0KYGBge3J9DQpwbG90KGNsdXN0ZXJzKQ0KYGBgDQoNCg0KIyBEZWFsaW5nIHdpdGggRXJyb3JzDQoNCnxFcnJvciAgICAgICAgICAgICAgICAgICAgfFBvc3NpYmxlIGV4cGxhbmF0aW9uL2ZpeCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IkNvdWxkIG5vdCBmaW5kIGZ1bmN0aW9uInxZb3UgaGF2ZW4ndCBsb2FkZWQgKG9yIGluc3RhbGxlZCkgdGhlIGxpYnJhcnkgZm9yIHRoZSBmdW5jdGlvbiB5b3UncmUgdHJ5aW5nIHRvIHVzZSwgb3IgeW91J3ZlIHNwZWx0IHRoZSBmdW5jdGlvbiBuYW1lIHdyb25nIXwNCnwib2JqZWN0ICdibGFoJyBub3QgZm91bmQifFlvdSBoYXZlIGEgdHlwbyB3aXRoIHlvdXIgdmFyaWFibGUgbmFtZSAnYmxhaCcsIGkuZS4geW91J3ZlIHNwZWx0IGJsYWggd3Jvbmcgc29tZXdoZXJlLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQojIEdldHRpbmcgaGVscA0KDQoqIElmIHlvdSB3YW50IHRvIGtub3cgbW9yZSBhYm91dCBhbnkgZnVuY3Rpb24sIGlmIHlvdSB0eXBlIGA/ZnVuY3Rpb25fbmFtZWAsIFIgd2lsbCBzaG93IHlvdSBpbmZvcm1hdGlvbiBhYm91dCBob3cgdGhlIGZ1bmN0aW9uIHdvcmtzIGluIHRoZSBib3R0b20gcmlnaHQgcGFuZQ0KKiBJZiB5b3Ugc3RpbGwgbmVlZCBoZWxwLCB0cnkgc2VhcmNoaW5nIGZvciB5b3VyIHF1ZXN0aW9uIG9uIFtzdGFjayBleGNoYW5nZV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpIG9yIGp1c3QgZ29vZ2xlIGFsc28gdXN1YWxseSB3b3JrcyA6KQ0KKiBUaGVyZSBhcmUgYWxzbyB0aGUgW1IgY2hlYXRzaGVldHNdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pDQoqIEFuZCBvZiBjb3Vyc2UsIGFsc28gYXNrIHlvdXIgcXVlc3Rpb24gaW4gdGhlIHJlbGV2YW50IGRpc2N1c3Npb24gYm9hcmQgb24gc3R1ZGl1bSENCg0K