Part 1: Import your dataset
The first thing when writing a script is to set your working
directory to make sure that R knows where to import and export (read and
save) your files.
We can read csv files by using function read.csv()
, the
default parameters of this functions include header = TRUE
(the first row will be set as column names), sep = ","
,
(the separator of different columns is a comma) and
fill = TRUE
(missing values will be set to NA). If your
file is not formatted this way, you should set your parameters
accordingly. For example, if your file doesn’t have a header, you should
set header = "FALSE"
. Our csv files have header and use “,”
as the separator.
If you have an xlsx file, you can use read.xlsx()
function from the openxlsx package. This package have many functions
that enable you to create and edit complex xlsx files (with all the
tables, equations, format and styles) without using Excel! But at least,
this package will be very handy if you want to save multiple dataframes
to different sheets in one xlsx file (We will come back to saving files
later).
If you need help for a function (what does it do, what arguments does
it need, etc.), you can search it in “Help” which should be located at
the 4th tab of the bottom-right panel.
# set working directory
#setwd(......)
# import your dataset from csv file
# "acoustic" contains measures of pitch and dispersion of each voice
acoustic <- read.csv("voice_acoustics.csv")
# "ratings" contains rating of trustworthiness of each voice by 28 participants
ratings <- read.csv("voice_ratings.csv")
# recap: how to install and load a package
# install.packages("openxlsx")
library(openxlsx)
# import your dataset from xlsx file
acoustic <- read.xlsx("voice_acoustics.xlsx")
Part 2: Overview your dataset
Have a look at your data
First, we can have a look at our data. Basically, we want to make
sure that R has successfully imported all the columns and rows and the
numbers and characters look right. See last week’s lab materials if you
don’t remember how to do this.
# show first 6 rows
head(acoustic)
VoiceID sex measures value
1 1 M Pitch 118.6140
2 1 M Dispersion 1061.1148
3 2 M Pitch 215.2936
4 2 M Dispersion 1023.9048
5 3 M Pitch 147.9080
6 3 M Dispersion 1043.0630
# show last 6 rows
tail(acoustic)
VoiceID sex measures value
123 62 F Pitch 151.3625
124 62 F Dispersion 1010.2170
125 63 F Pitch 181.8106
126 63 F Dispersion 1113.5282
127 64 F Pitch 159.6188
128 64 F Dispersion 1044.4419
# View the dataset in a new tab. NB: the 'V' in View is capitalised
#View(acoustic)
# get the number of rows
nrow(acoustic)
[1] 128
# get the number of columns
ncol(acoustic)
[1] 4
# get the number of rows and columns (the first number is row, the second is column)
dim(acoustic)
[1] 128 4
# show a summary of each column
# if it is numeric data, R will return mininum, Q1, median, mean, Q3, and maximum, and the number of NAs (if any)
summary(acoustic)
VoiceID sex measures value
Min. : 1.00 Length:128 Length:128 Min. : 104.4
1st Qu.:16.75 Class :character Class :character 1st Qu.: 140.2
Median :32.50 Mode :character Mode :character Median : 573.3
Mean :32.50 Mean : 604.8
3rd Qu.:48.25 3rd Qu.:1052.0
Max. :64.00 Max. :1213.7
Calculate descriptive statistics
We can calculate descriptive statistics (both central tendencies and
dispersion) by using R functions, but remember that these functions only
accept vectors but not dataframes as their argument so
we’ll first need to extract a column (vector) from the dataframe using
$
. The function names are shown below. They all have very
straightforward function names!
# mean - mean()
mean(acoustic$value)
[1] 604.8287
# median - median()
median(acoustic$value)
[1] 573.3268
# standard deviation - sd()
sd(acoustic$value)
[1] 468.0679
# interquartile range. - IQR(), NB: upper case!
IQR(acoustic$value)
[1] 911.855
# minimum - min()
min(acoustic$value)
[1] 104.4441
# maximum - max()
max(acoustic$value)
[1] 1213.74
We can summarise a character/factor vector (i.e., categorical
variable) with table()
function. It will return the number
of observations in each category. For example, there are two categories
in the sex column, M and F (male and female). table()
will
count how many observations are M and how many observations are F. HINT:
We still need to use $
.
# summarise the sex column
table(acoustic$sex)
F M
64 64
# summarise the measures column
table(acoustic$measures)
Dispersion Pitch
64 64
R does not have a built-in function for the mode so we will need to
create our own function to calculate the mode. Let’s create a “getmode”
function. You can create your own function by using the syntax
function_name <- function(arguments) {...some code here ...}
.
After you run the code, you can use the function just like other
functions we have used, i.e.,
function_name(arguments=...)
- Create a bimodal vector (anything you like). HINT: use the
c()
function
- Create a function called getmode (this is already provided)
- Use the function to get the mode of your vector
# create a bimodal vector
some_numbers <- c(1,1,2,3,4,5,6,7,7,8,8,9)
# create a function called getmode
getmode <- function(x) {
count <- table(x)
count[which(count == max(count))]
}
# use the function
getmode(some_numbers)
x
1 7 8
2 2 2
# The first row is the observations
# The second row is the number of corresponding observations
Part 3: Data manipulation
Wide and long data format
In wide data, each row represents an individual
case, with observations for that case in separate columns. For example,
a row contains all data from one participant. The first column is the
participant’s response to the first trial, the second column is response
to the second trial, so on and so forth.
In long data, each row represents a single
observation, and the observations are grouped together into cases based
on the value of a variable. For example, one participant occupies
multiple rows. The first row is the participant’s response to the first
trial, the second row is response to the second trial, so on and so
forth.
Now have a look at the ratings dataset. Is it in the wide or long
format? HINT: use head()
# Have a look at the ratings dataset
head(ratings)
VoiceID P1 P2 P3 P4 P5 P6 P7 P8 P9 P10 P11 P12 P13 P14 P15 P16 P17
1 1 7.0 7.5 5.5 4.5 5.0 4.0 5.5 5.0 3.0 4.0 3.5 2.5 3.0 4.0 7.0 6.5 3.5
2 2 8.0 7.5 7.5 6.5 3.5 4.0 5.5 6.0 5.0 6.5 7.5 7.0 5.0 8.0 6.0 7.5 4.0
3 3 7.5 7.0 6.5 8.0 7.0 5.5 7.0 6.0 5.5 5.0 6.0 5.5 6.0 3.0 6.5 7.5 4.5
4 4 6.0 6.5 4.0 5.5 5.0 4.0 3.0 3.5 4.0 3.5 1.5 4.0 4.5 3.5 4.5 5.0 4.5
5 5 4.0 5.0 4.0 3.5 3.0 4.0 4.0 4.5 3.5 4.0 4.0 3.5 2.5 2.5 6.0 5.5 3.5
6 6 5.0 6.0 2.5 4.0 4.5 3.5 3.5 3.5 2.5 2.5 2.5 4.5 4.5 2.5 5.0 6.0 4.5
P18 P19 P20 P21 P22 P23 P24 P25 P26 P27 P28
1 3.5 5.5 6.5 4.5 4.0 4.5 5.0 7.5 6.0 2.5 4.0
2 5.5 7.0 5.5 8.5 7.0 5.5 7.0 7.5 6.5 9.0 8.5
3 6.0 5.5 6.0 6.5 6.0 4.5 4.5 6.0 6.0 6.0 4.5
4 5.0 5.0 4.5 3.5 3.5 5.0 5.0 4.0 4.0 5.0 4.5
5 3.5 5.0 4.0 4.5 3.0 3.5 4.0 5.5 3.0 2.0 3.5
6 4.0 4.5 4.5 4.0 3.5 2.5 4.5 3.5 3.5 2.5 2.5
# It has a wide data format. Each row is a voice (identified by voice ID), and each column is an observation, i.e., the rating of the voice by one participant.
Then have a look at the acoustic dataset. Is it in the wide or long
format?
# Have a look at the acoustic dataset
head(acoustic)
VoiceID sex measures value
1 1 M Pitch 118.6140
2 1 M Dispersion 1061.1148
3 2 M Pitch 215.2936
4 2 M Dispersion 1023.9048
5 3 M Pitch 147.9080
6 3 M Dispersion 1043.0630
# It has a long data format. Each row is an observation. You can see that voice 1 has two rows: the first row is the measure of pitch, the second is dispersion.
We can use spread()
to transform a long format into a
wide format and use gather()
to transform a wide format
into a long format.
Use spread(dataframe, key, value)
to transform a long
format into a wide format. key=the column containing labels; value = the
column containing values
If we transform the acoustic dataset into a wide format, the key is
the column “measures” which contains the name of values (Pitch,
Dispersion). The value is the column “value” which contains the actual
measures in numbers (e.g., 119, 1061).
Use gather(dataframe, key, value, ...)
to transform a
wide format into a long format. key = a new column name for all old
column names, value = a new column name containing values, “…” should
include a vector of all old column names
If we transform the rating dataset into a long format, the key is a
new column name such as “participant” that we want to use to include
participants (P1, P2, etc.). The value is a new column name such as
“rating” that we want to use to include the actual ratings in numbers
(e.g., 7, 7.8, 8). “a vector of old columns to include” need to include
all columns from P1 to P28, you can write P1:P28
(given
that these columns are located consecutively without break in
between).
Now transform the acoustic dataset into a wide format, and then the
ratings dataset into a long format. Check if you have made the correct
transformation. What do the tranformed data look like?
# transform acoustic into a wide format
acoustic_wide <- spread(acoustic, key = measures, value = value)
# Have a look at the transformed dataset
head(acoustic_wide)
VoiceID sex Dispersion Pitch
1 1 M 1061.1148 118.6140
2 2 M 1023.9048 215.2936
3 3 M 1043.0630 147.9080
4 4 M 931.3600 104.4441
5 5 M 942.3216 106.2731
6 6 M 1049.7560 144.5150
# transform ratings into a long format
ratings_long <- gather(ratings, key = participant, value = rating, P1:P28)
# Have a look at the transformed dataset
head(ratings_long)
VoiceID participant rating
1 1 P1 7.0
2 2 P1 8.0
3 3 P1 7.5
4 4 P1 6.0
5 5 P1 4.0
6 6 P1 5.0
Group by and Summarise
Now we have each participants’ rating for each voice. How can we
calculate the average rating of each voice?
Group by: Do your operations within each category
specified in the group_by()
function (NB: this step only
specifies the groups, and nothing has done yet to these groups).
Summarise: Specify the column names and values to
give to the columns. The parameters take the format:
column_name = function(arguments, ...)
.
Operations/calculations will be done within each group specified by the
previous group_by()
function.
Pipe %>%
: A pipe allows you to pass
the result of a previous function as an argument to the next function
without saving it as an object. It’s like a production line!
We need to do this step by step and connect the steps by pipes, like
dataframe name <- dataframe name %>% group_by(some column name) %>% summarise(column name = function(arguments, ...))
.
Be careful! Which rating dataset do we need, wide or long format?
# calculate the average rating of each voice and save it to "rating_ave"
rating_ave <- ratings_long %>%
group_by(VoiceID) %>%
summarize(rating = mean(rating))
head(rating_ave)
# A tibble: 6 × 2
VoiceID rating
<int> <dbl>
1 1 4.80
2 2 6.52
3 3 5.91
4 4 4.34
5 5 3.88
6 6 3.80
Join two dataframe by inner_join
Now we have the average rating for each voice in the “rating_ave”
dataframe, and we have the measure of pitch and dispersion of each voice
in the “acoustic_wide” dataframe. To find out the relationship between
perceived trustworthiness (the rating scores) and acoustic features of
the voice (i.e., pitch and dispersion), we need to join the two
dataframe together.
We can join two dataframes by using the function
inner_join()
. It takes a parameter
by = the common column in the two dataframes
(the common
column can also be detected by R automatically). After this, we can
remove dataframes we don’t need by using rm()
.
HINT: inner_join(dataframe1, dataframe2, by="...")
.
Remember to save the combined dataframe as “data”.
data <- inner_join(acoustic_wide, rating_ave, by = "VoiceID")
head(data)
VoiceID sex Dispersion Pitch rating
1 1 M 1061.1148 118.6140 4.803571
2 2 M 1023.9048 215.2936 6.517857
3 3 M 1043.0630 147.9080 5.910714
4 4 M 931.3600 104.4441 4.339286
5 5 M 942.3216 106.2731 3.875000
6 6 M 1049.7560 144.5150 3.803571
# we can remove dataframes we don't need any more
rm(acoustic_wide, ratings, ratings_long)
Calculate Mean, SD, SEM/SE, and CI.
We can get basic descriptive statics by using summary()
,
but it doesn’t calculate Standard Error of the Mean (SEM) (or simply SE)
or Confidence Interval of the mean (CI). We can calculate SE and CI
ourselves based on the equations (review the lecture slides for the
equations!)
Let’s calculate these statistics for each sec (male and female) and
each measures (pitch and dispersion).
- We have all data combined in one dataframe called “data”, but it is
in a wide format. To make things easier, we first transform the data to
a long format.
- Then we use
group_by()
and summarise()
as
we’ve practiced before. This time, we calculate more statistics,
including
Mean = ..., SD = ..., SE = ..., CI_lower = ..., CI_upper = ...
HINT: n()
can be used to count the number of
observations in a given group.
# to make things easier, we first change the data to a long format
data_long <- data %>%
gather(key = measures, value = value, Dispersion:rating)
# summarise by sex and measures, and save the results to a dataframe called "summary"
summary <- data_long %>%
group_by(sex, measures) %>%
summarise(Mean = mean(value),
SD = sd(value),
SE = SD/sqrt(n()),
CI_upper = Mean + 1.96 * SE,
CI_lower = Mean - 1.96 * SE)
Sometimes we may want to use a subset of the data. For example, if
you’re interested in female voices but not males, you will need to
filter out all the observations of males. We can achieve this by
filter()
or subset()
.
filter(dataframe, logical operations)
subset(dataframe, logical operations)
You may already note that the parameters for the two functions are
the same, and they do the same thing.
Logical operations return TRUE or FALSE. For example, if we write
sex == "M"
(sex equals M), all observations of “M” will
return TRUE, and “F” will return FALSE. Other operations include: not
equal to !=
, more than >
, less than
<
. You can also use &
(and) and
|
(or) to make more complex logical operations.
# using filter to filter out males in the summary dataframe
sum_f <- filter(summary, sex == "F")
sum_f
# A tibble: 3 × 7
# Groups: sex [1]
sex measures Mean SD SE CI_upper CI_lower
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 F Dispersion 1059. 62.7 11.1 1081. 1037.
2 F Pitch 141. 27.8 4.92 151. 132.
3 F rating 5.37 0.685 0.121 5.60 5.13
# using subset to subset female voices in the summary dataframe
sum_f2 <- subset(summary, sex == "F")
sum_f2
# A tibble: 3 × 7
# Groups: sex [1]
sex measures Mean SD SE CI_upper CI_lower
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl>
1 F Dispersion 1059. 62.7 11.1 1081. 1037.
2 F Pitch 141. 27.8 4.92 151. 132.
3 F rating 5.37 0.685 0.121 5.60 5.13
Part 4: Data visualisation
We can use ggplot()
function to create all the plots we
need. This function is included in the ggplot package which is
specialized in data visualisation, but because the ggplot package is
included in tidyverse, we don’t need to install and load ggplot once we
have installed and load tidyverse.
The general syntax for ggplot is
ggplot(dataframe_name, aes(x = , y = , ...)) + geom_xxx() + ....
.
Inside the aes()
is the variable on x and y axis and your
grouping variable (if any). The geom_xxx()
specifies which
plot to create, e.g., geom_boxplot()
(boxplot),
geom_bar()
(barplot), etc.
For the following practice, think carefully which dataframe to use,
and what should be put on the x and y axis.
Boxplot
A boxplot is specified by geom_boxplot()
.
# Create a boxplot to compare dispersion of voice betweem male and female
ggplot(data, aes(x = sex, y = Dispersion)) +
geom_boxplot() +
# specify x and y axis title and the title of the plot
labs(x = "Sex", y = "Dispersion", title = "Dispersion by sex")
Violin plot
A violin plot is specified by geom_violin()
.
# (with points) Create a violin plot to compare dispersion of voice betweem male and female
ggplot(data, aes(x = sex, y = Dispersion)) +
geom_violin() +
# adding points on a violin, adding random horizontal shift, changing the shape of the points
geom_point(position = position_jitter(width=0.1), shape = 1) +
# specify x and y axis title and the title of the plot
labs(x = "Sex", y = "Dispersion", title = "Dispersion by sex")
# (with boxplot) Create a violin plot to compare dispersion of voice betweem male and female
ggplot(data, aes(x = sex, y = Dispersion)) +
geom_violin() +
# adding box and whiskers, changing transparency
geom_boxplot(alpha = 0.5) +
# specify x and y axis title and the title of the plot
labs(x = "Sex", y = "Dispersion", title = "Dispersion by sex")
Histogram
A histogram is specified by geom_histogram()
.
# Create a histogram to show the distribution of Dispersion
ggplot(data, aes(x = Dispersion)) +
# changing binwith, changing fill and color
geom_histogram(binwidth = 10, fill = "white", color = "black") +
# specify x and y axis title and the title of the plot
labs(x = "Dispersion", y = "Frequency", title = "Histogram of Dispersion")
Density plot
A density plot is specified by geom_density()
.
# Create a density plot to show the distribution of Dispersion
ggplot(data, aes(x = Dispersion)) +
# changing fill and color
geom_density(fill = "white", color = "black") +
# specify x and y axis title and the title of the plot
labs(x = "Dispersion", y = "Density", title = "Density of Dispersion")
How to plot histogram and density together when their y axis is in
different scale? In order to overlay a kernel density estimate over a
histogram in ggplot2 you will need to pass
aes(y = ..density..)
to geom_histogram
and add
geom_density
.
# Create a histogram to show the distribution of Dispersion, and overlay the density
ggplot(data, aes(x = Dispersion)) +
# adding a histogram(use aes(y = ..density..)!), changing color and fill
geom_histogram(aes(y = ..density..), color = "black", fill = "white") +
# adding density
geom_density() +
# specify x and y axis title and the title of the plot
labs(x = "Dispersion", y = "Frequency", title = "Histogram of Dispersion")
Scatter plot
A scatter plot is specified by geom_point()
.
If we want to show the relationship between dispersion and
trustworthiness for male and female voices separately, we can specify
color = sex
in the aes()
to show male and
female voices in the same plot but with different color. Alternatively,
we can add + facet_wrap(~sex)
to draw two separate plots
for male and female voices.
We can use + geom_smooth(method = "lm")
to add a linear
regression line.
# Create a scatter plot to reflect the relationship between dispersion and trustworthiness
# (separate plot) Group by sex
ggplot(data, aes(x = Dispersion, y = rating)) +
geom_point() +
# adding a regression line
geom_smooth(method = "lm") +
# specify x and y axis title and the title of the plot
labs(x = "Dispersion", y = "Perceived trustworthiness") +
# group by sex (draw separate plot for sex)
facet_wrap(~sex)
# (different color in one plot) Group by sex
ggplot(data, aes(x = Dispersion, y = rating, color = sex)) +
geom_point() +
# adding a regression line
geom_smooth(method = "lm") +
# specify x and y axis title and the title of the plot
labs(x = "Dispersion", y = "Perceived trustworthiness") +
# change the color
scale_color_manual(values = c("black", "blue")) +
# change theme
theme_bw()
Bar plot
A bar plot is specified by geom_bar()
. A bar plot will
count the number of observations in different categories, but people
usually use bar plot to show mean and CI (which is called error bar, we
can add with + geom_errorbar(aes(ymin = ..., ymax = ...))
.
For this purpose, we will need to specify a parameter
stat = "identity"
. Alternatively, we can use
geom_col()
, which is equivalent to
geom_bar(stat = "identity")
.
NB: We have created a summary dataframe with mean and CI of each
measure for male and female voices. We’ll need to filter each measure a
time.
# create a bar plot with mean and CI of Dispersion for male and female
ggplot(filter(summary, measures == "Dispersion"), aes(x = sex, y = Mean)) +
# adding the bar, changing fill and color
geom_bar(stat = "identity", fill = "white", color = "black") +
# adding error bar, changing the width
geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper), width = 0.5) +
# change y axis range
ylim(0, 1300) +
# specify x and y axis title and the title of the plot
labs(x = "Sex", y = "Mean Dispersion", title = "")
# it's the same to use geom_col, no need to specify stat = "identity" then
ggplot(filter(summary, measures == "Dispersion"), aes(x = sex, y = Mean)) +
# adding the bar, changing fill and color
geom_col(fill = "white", color = "black") +
# adding error bar, changing the width
geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper), width = 0.5) +
# change y axis range
ylim(0, 1500) +
# specify x and y axis title and the title of the plot
labs(x = "Sex", y = "Mean Dispersion", title = "")
line plot
A line plot is specified by geom_line()
. It’s also used
to show the mean and CI with error bars. HINT:
+ geom_errorbar(aes(ymin = ..., ymax = ...))
NB: We have created a summary dataframe with mean and CI of each
measure for male and female voices. We’ll need to filter each measure a
time.
# create a line plot with mean and CI of Dispersion and group by sex
# If we don't have a second factor, we should specify group = 1. Otherwise, group = factor name
ggplot(filter(summary, measures == "Dispersion"), aes(x = sex, y = Mean, group = 1)) +
# adding point to show the mean
geom_point() +
# adding a line to connect the mean
geom_line() +
# adding error bars to show CI
geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper), width = 0.4) +
# change the range of y axis
ylim(1000, 1130) +
# specify x and y axis title and the title of the plot
labs(x = "Sex", y = "Mean Dispersion", title = "")
# What if you want to plot Dispersion, Pitch and rating all in one?
# We can specify group = measures (and not filtering measures == "Dispersion")
# If you want to give different colors to different measures, specify color = measures
ggplot(summary, aes(x = sex, y = Mean, group = measures, color = measures)) +
geom_point() +
geom_line() +
geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper), width = 0.4) +
# specify x and y axis title and the title of the plot
labs(x = "Sex", y = "Mean Dispersion", title = "")
Because the scales of the measures are so different (about 1000, 150,
or 5!), this plot is quite confusing, thus not a good choice. This is
just used to illustrate how to use the group=...
argument
in aes()
to plot separate lines for different groups.
Save dataframes and plots to your folder
We can use write.csv(dataframe, filename)
to save a
dataframe to csv file, or write.xlsx(dataframe, filename)
(from the openxlsx library) to save as xlsx file. If output directory is
not specified, It will save the file to the working directory.
NB: By default, in write.csv()
the argument
row.names = TRUE
will assign a number to each row starting
from 1 (excluding the header row). If you don’t want this, remember to
set row.names = FALSE
.
write.csv(summary, "summary_3_measures.csv", row.names = FALSE)
write.xlsx(summary, "summary_3_measures.csv")
You can also use functions in the openxlsx package to save dataframes
to multiple sheets in one xlsx file.
- Create a work book
createWorkbook()
.
- Add a sheet to the work book and name the sheet
addWorksheet()
.
- Add a dataframe to a specified sheet
writeData()
.
- Save the work book to your computer by a given name
saveWorkbook()
.
# first we create a work book
wb <- createWorkbook()
# add a sheet to the work book, and name the sheet
addWorksheet(wb, "data_joined")
addWorksheet(wb, "summary")
# add a dataframe to the sheet. You need to specify the sheet name and the dataframe name
writeData(wb, "data_joined", data)
writeData(wb, "summary", summary)
# save the work book
saveWorkbook(wb, "joined_data_summary.xlsx", overwrite = TRUE)
You can save the most recent plot by ggsave(filename)
.
You can choose from many common file types, such as pdf, png, and
jpg.
# Let's create a plot
ggplot(data, aes(x = sex, y = Dispersion)) +
geom_boxplot() +
labs(x = "Sex", y = "Dispersion", title = "Dispersion by sex")
# save it by ggsave (pdf is recommended)
ggsave("dispersion_line.pdf")
ggsave("dispersion_line.png")
ggsave("dispersion_line.jpg")
Part 5: Normality check
Normality check is an important step before the a statistical test
(along with other assumption checking).
We’ve learnt four ways to check the normality of your data, and they
are often used together.
- Check normality visually with histogram (please see above).
ggplot(data, aes(x = Dispersion)) +
geom_histogram(aes(y = ..density..), color = "black", fill = "white") +
geom_density()
- Check normality visually with Normal Q-Q plot. Normal Q-Q Plots
compare your data set’s probability distribution to the normal
distribution. If they are similar enough, you will see the data points
form a nice straight line (or approximate one). We use
qqnorm(dataframe$colum)
to create dots and
qqnorm(dataframe$colum)
to add the line.
# check the normality of Dispersion
qqnorm(data$Dispersion)
qqline(data$Dispersion)
- Check normality numerically (Skewness & Kurtosis values). There
are different functions for this depending on the packages you use.
Using the ‘psych’ package, we can check skewness and kurtosis with
function
describe(dataframe$colum)
and look for the
skew
and kurtosis
columns. This function also
returns many other descriptive statistics.
# install and load the psych package
# install.packages("psych")
library(psych)
# check descriptive statistics using describe()
describe(data$Dispersion)
vars n mean sd median trimmed mad min max range skew
X1 1 64 1068.22 67.96 1054.32 1067.25 68.31 931.36 1213.74 282.38 0.2
kurtosis se
X1 -0.82 8.5
# what's the Skewness and Kurtosis values?
# skew: 0.2; kurtosis -0.82
- Check normality statistically (Shapiro-Wilk Test). Shapiro-Wilk Test
compares your distribution to the normal distribution (like a Q-Q plot,
but with a p-value output). Remember the null hypothesis is there is no
significant difference between the distribution of this data set and the
normal distribution (i.e. this data set is normally distributed). If p
> .05, the data is normally distributed because we can’t reject the
null hypothesis. Use
shapiro.test(dataframe$colum)
to run
the test.
# run the Shapiro-Wilk Test
shapiro.test(data$Dispersion)
Shapiro-Wilk normality test
data: data$Dispersion
W = 0.97459, p-value = 0.2086
# what's the p value?
# p = 0.2086, not significant, so the distribution is normal!
LS0tDQp0aXRsZTogIlBMSU5TVEFUIGxhYiAyOiBEYXRhIG1hbmlwdWxhdGlvbiBhbmQgdmlzdWFsaXNhdGlvbiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KcGFyYW1zOg0KICBmbGV4OiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQojc3R1ZGVudHM6IHRoaXMgaXMgdGhlIHNldCB1cCBjaHVuaywgaXQgY2FuIGJlIGlnbm9yZWQNCmtuaXRyOjpvcHRzX2NodW5rJHNldCh3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBjb21tZW50ID0gIiIpDQpsaWJyYXJ5KGtuaXRyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KG9wZW54bHN4KQ0KbGlicmFyeShwc3ljaCkNCmBgYA0KDQpBdXRob3I6IEtheWxhIEtleXVlIENoZW4NCg0KTGFzdCB1cGRhdGVkOiAyMDI0LjQuMjgNCg0KIyMgTGVhcm5pbmcgb2JqZWN0aXZlcyANCg0KKiBJbXBvcnQgYW5kIGV4cG9ydCB5b3VyIGRhdGFzZXQ6IExlYXJuIGhvdyB0byBpbXBvcnQgeW91ciBkYXRhc2V0IGZyb20gY3N2IGFuZCB4bHN4IGZpbGVzIGFuZCBleHBvcnQgZGF0YWZyYW1lIG9iamVjdCB0byBjc3YgYW5kIHhsc3ggZmlsZXMuIA0KDQoqIE92ZXJ2aWV3IHlvdXIgZGF0YXNldDogQSByZWNhcCBvZiBmdW5jdGlvbnMgbGVhcm50IGxhc3Qgd2VlayAoaW4geW91ciBwcmFjdGljZSEpOiBgaGVhZCgpYCwgYHRhaWwoKWAsIGBzdW1tYXJ5KClgLCBgVmlldygpYCwgYG5jb2woKWAsIGBucm93KClgLCBgZGltKClgLiBMZWFybiBzb21lIGZ1bmN0aW9ucyB0byBjYWxjdWxhdGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljczogYG1lYW4oKWAsIGBtZWRpYW4oKWAsIGBzZCgpYCwgYElRUigpYCwgYG1heCgpYCwgYG1pbigpYCwgYHRhYmxlKClgIChhbmQgd2Ugd2lsbCBuZWVkIHRvIGNyZWF0ZSBvdXIgb3duIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSB0aGUgbW9kZSkuIA0KDQoqIERhdGEgbWFuaXB1bGF0aW9uOiBMZWFybiBob3cgdG8gdXNlIGBnYXRoZXIoKWAsIGBzcHJlYWQoKWAsIGBpbm5lcl9qb2luKClgLCBgZmlsdGVyKClgIGZyb20gdGhlIHRpZHl2ZXJzZSBwYWNrYWdlIGFuZCBgc3Vic2V0KClgIHdoaWNoIGlzIGEgYmFzZSBSIGZ1bmN0aW9uIHRvIG1hbmlwdWxhdGUgZGF0YWZyYW1lcywgYW5kIGNvbXB1dGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyBzdWNoIGFzIG1lYW4sIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIHN0YW5kYXJkIGVycm9yIGFuZCBjb25maWRlbmNlIGludGVydmFsIGJ5IG91cnNlbHZlcyB1c2luZyBgZ3JvdXBfYnkoKWAgYW5kIGBzdW1tYXJpc2UoKWAuIFdlIHdpbGwgbGVhcm4gaG93IHRvIHdyaXRlIG11bHRpcGxlIGxpbmVzIHdpdGhpbiBhIHBpcGUgdXNpbmcgYCU+JWAgaW4gdGlkeXZlcnNlLiANCg0KKiBEYXRhIHZpc3VhbGlzYXRpb246IExlYXJuIGhvdyB0byB1c2UgYGdncGxvdCgpYCBpbiBnZ3Bsb3QyIHBhY2thZ2UgKGluY2x1ZGVkIGluIHRoZSB0aWR5dmVyc2UpIHRvIGNyZWF0ZSBhIHZhcmlldHkgb2YgcGxvdHM6IGJveHBsb3QsIHZpb2xpbiBwbG90LCBoaXN0b2dyYW0sIGRlbnNpdHkgcGxvdCwgc2NhdHRlciBwbG90LCBiYXIgcGxvdCwgYW5kIGxpbmUgcGxvdC4NCg0KKiBOb3JtYWxpdHkgY2hlY2s6IEdldCBmYW1pbGlhciB3aXRoIGZvdXIgd2F5cyB0byBjaGVjayB3aGV0aGVyIHlvdXIgZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZC4gKDEpIEhpc3RvZ3JhbS4gKDIpIE5vcm1hbCBRLVEgUGxvdC4gKDMpIFNrZXduZXNzIGFuZCBLdXJ0b3NpcyB2YWx1ZXMuICg0KSBTaGFwaXJvLVdpbGsgVGVzdC4NCg0KIyMgQWJvdXQgdGhlIGRhdGFzZXQNCg0KVG9kYXkgd2Ugd2lsbCBiZSB3b3JraW5nIHdpdGggcmVhbCBkYXRhIHRvIGV4cGxvcmUgdGhlIHF1ZXN0aW9uIG9mIHdoZXRoZXIgdGhlcmUgaXMgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2b2ljZSBhY291c3RpY3MgYW5kIHJhdGluZ3Mgb2YgcGVyY2VpdmVkIHRydXN0d29ydGhpbmVzcy4NCg0KKiAqKlBpdGNoKiogKG9yIEZ1bmRhbWVudGFsIEZyZXF1ZW5jeSBvciBGMCkgaXMgYSBtZWFzdXJlIG9mIHRoZSB2aWJyYXRpb24gb2YgdGhlIHZvY2FsIGNob3JkcyBpbiBIZXJ0eiAoSHopOyBtYWxlcyBoYXZlIG9uIGF2ZXJhZ2UgYSBsb3dlciBwaXRjaCB0aGFuIGZlbWFsZXMgZm9yIGV4YW1wbGUuIA0KDQoqICoqRGlzcGVyc2lvbioqIChtZWFzdXJlZCBhZ2FpbiBpbiBIeikgaXMgZWZmZWN0aXZlbHkgYSBtZWFzdXJlIG9mIHRoZSBsZW5ndGggb2Ygc29tZW9uZeKAmXMgdm9jYWwgdHJhY3QgKG9yIG5lY2spLiBIZWlnaHQgYW5kIG5lY2sgbGVuZ3RoIGFyZSBzdWdnZXN0ZWQgdG8gYmUgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggZm9ybWFudCBkaXNwZXJzaW9uLCBzbyB0YWxsIHBlb3BsZSB0ZW5kIHRvIGhhdmUgc21hbGxlciBmb3JtYW50IGRpc3BlcnNpb24uDQoNCk1vcmUgcmVjZW50bHksIHdvcmsgaGFzIGZvY3Vzc2VkIG9uIHdoYXQgdGhlIHNvdW5kIG9mIHlvdXIgdm9pY2Ugc3VnZ2VzdHMgYWJvdXQgeW91ciBwZXJzb25hbGl0eS4gW01jQWxlZXIsIFRvZG9yb3YgYW5kIEJlbGluICgyMDE0KV0oaHR0cHM6Ly9qb3VybmFscy5wbG9zLm9yZy9wbG9zb25lL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBvbmUuMDA5MDc3OSkgc3VnZ2VzdGVkIHRoYXQgdm9jYWwgYWNvdXN0aWNzIGdpdmUgYSBwZXJjZXB0aW9uIG9mIHlvdXIgdHJ1c3R3b3J0aGluZXNzIGFuZCBkb21pbmFuY2UgdG8gb3RoZXJzLCByZWdhcmRsZXNzIG9mIHdoZXRoZXIgb3Igbm90IGl0IGlzIGFjY3VyYXRlLiANCg0KVG9kYXkgd2Ugd2lsbCBsb29rIGF0IGRhdGEgZGVzY3JpYmluZyBtYWxlIGFuZCBmZW1hbGUgdm9pY2VzLCB0aGVpciAqKnBpdGNoKiogYW5kICoqZGlzcGVyc2lvbioqLCBhbmQgYXNzb2NpYXRlZCBtZWFzdXJlcyBvZiAqKnBlcmNlaXZlZCB0cnVzdHdvcnRoaW5lc3MqKi4NCg0KIyMgUGFydCAxOiBJbXBvcnQgeW91ciBkYXRhc2V0DQoNClRoZSBmaXJzdCB0aGluZyB3aGVuIHdyaXRpbmcgYSBzY3JpcHQgaXMgdG8gc2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkgdG8gbWFrZSBzdXJlIHRoYXQgUiBrbm93cyB3aGVyZSB0byBpbXBvcnQgYW5kIGV4cG9ydCAocmVhZCBhbmQgc2F2ZSkgeW91ciBmaWxlcy4gDQoNCldlIGNhbiByZWFkIGNzdiBmaWxlcyBieSB1c2luZyBmdW5jdGlvbiBgcmVhZC5jc3YoKWAsIHRoZSBkZWZhdWx0IHBhcmFtZXRlcnMgb2YgdGhpcyBmdW5jdGlvbnMgaW5jbHVkZSBgaGVhZGVyID0gVFJVRWAgKHRoZSBmaXJzdCByb3cgd2lsbCBiZSBzZXQgYXMgY29sdW1uIG5hbWVzKSwgYHNlcCA9ICIsImAsICh0aGUgc2VwYXJhdG9yIG9mIGRpZmZlcmVudCBjb2x1bW5zIGlzIGEgY29tbWEpIGFuZCBgZmlsbCA9IFRSVUVgIChtaXNzaW5nIHZhbHVlcyB3aWxsIGJlIHNldCB0byBOQSkuIElmIHlvdXIgZmlsZSBpcyBub3QgZm9ybWF0dGVkIHRoaXMgd2F5LCB5b3Ugc2hvdWxkIHNldCB5b3VyIHBhcmFtZXRlcnMgYWNjb3JkaW5nbHkuIEZvciBleGFtcGxlLCBpZiB5b3VyIGZpbGUgZG9lc24ndCBoYXZlIGEgaGVhZGVyLCB5b3Ugc2hvdWxkIHNldCBgaGVhZGVyID0gIkZBTFNFImAuIE91ciBjc3YgZmlsZXMgaGF2ZSBoZWFkZXIgYW5kIHVzZSAiLCIgYXMgdGhlIHNlcGFyYXRvci4NCg0KSWYgeW91IGhhdmUgYW4geGxzeCBmaWxlLCB5b3UgY2FuIHVzZSBgcmVhZC54bHN4KClgIGZ1bmN0aW9uIGZyb20gdGhlIG9wZW54bHN4IHBhY2thZ2UuIFRoaXMgcGFja2FnZSBoYXZlIG1hbnkgZnVuY3Rpb25zIHRoYXQgZW5hYmxlIHlvdSB0byBjcmVhdGUgYW5kIGVkaXQgY29tcGxleCB4bHN4IGZpbGVzICh3aXRoIGFsbCB0aGUgdGFibGVzLCBlcXVhdGlvbnMsIGZvcm1hdCBhbmQgc3R5bGVzKSB3aXRob3V0IHVzaW5nIEV4Y2VsISBCdXQgYXQgbGVhc3QsIHRoaXMgcGFja2FnZSB3aWxsIGJlIHZlcnkgaGFuZHkgaWYgeW91IHdhbnQgdG8gc2F2ZSBtdWx0aXBsZSBkYXRhZnJhbWVzIHRvIGRpZmZlcmVudCBzaGVldHMgaW4gb25lIHhsc3ggZmlsZSAoV2Ugd2lsbCBjb21lIGJhY2sgdG8gc2F2aW5nIGZpbGVzIGxhdGVyKS4NCg0KSWYgeW91IG5lZWQgaGVscCBmb3IgYSBmdW5jdGlvbiAod2hhdCBkb2VzIGl0IGRvLCB3aGF0IGFyZ3VtZW50cyBkb2VzIGl0IG5lZWQsIGV0Yy4pLCB5b3UgY2FuIHNlYXJjaCBpdCBpbiAiSGVscCIgd2hpY2ggc2hvdWxkIGJlIGxvY2F0ZWQgYXQgdGhlIDR0aCB0YWIgb2YgdGhlIGJvdHRvbS1yaWdodCBwYW5lbC4gIA0KDQpgYGB7cn0NCiMgc2V0IHdvcmtpbmcgZGlyZWN0b3J5DQojc2V0d2QoLi4uLi4uKQ0KDQojIGltcG9ydCB5b3VyIGRhdGFzZXQgZnJvbSBjc3YgZmlsZQ0KIyAiYWNvdXN0aWMiIGNvbnRhaW5zIG1lYXN1cmVzIG9mIHBpdGNoIGFuZCBkaXNwZXJzaW9uIG9mIGVhY2ggdm9pY2UNCmFjb3VzdGljIDwtIHJlYWQuY3N2KCJ2b2ljZV9hY291c3RpY3MuY3N2IikNCiMgInJhdGluZ3MiIGNvbnRhaW5zIHJhdGluZyBvZiB0cnVzdHdvcnRoaW5lc3Mgb2YgZWFjaCB2b2ljZSBieSAyOCBwYXJ0aWNpcGFudHMNCnJhdGluZ3MgPC0gcmVhZC5jc3YoInZvaWNlX3JhdGluZ3MuY3N2IikNCg0KIyByZWNhcDogaG93IHRvIGluc3RhbGwgYW5kIGxvYWQgYSBwYWNrYWdlIA0KIyBpbnN0YWxsLnBhY2thZ2VzKCJvcGVueGxzeCIpDQpsaWJyYXJ5KG9wZW54bHN4KSANCg0KIyBpbXBvcnQgeW91ciBkYXRhc2V0IGZyb20geGxzeCBmaWxlDQphY291c3RpYyA8LSByZWFkLnhsc3goInZvaWNlX2Fjb3VzdGljcy54bHN4IikNCmBgYA0KDQojIyBQYXJ0IDI6IE92ZXJ2aWV3IHlvdXIgZGF0YXNldA0KDQojIyMgSGF2ZSBhIGxvb2sgYXQgeW91ciBkYXRhDQoNCkZpcnN0LCB3ZSBjYW4gaGF2ZSBhIGxvb2sgYXQgb3VyIGRhdGEuIEJhc2ljYWxseSwgd2Ugd2FudCB0byBtYWtlIHN1cmUgdGhhdCBSIGhhcyBzdWNjZXNzZnVsbHkgaW1wb3J0ZWQgYWxsIHRoZSBjb2x1bW5zIGFuZCByb3dzIGFuZCB0aGUgbnVtYmVycyBhbmQgY2hhcmFjdGVycyBsb29rIHJpZ2h0LiBTZWUgbGFzdCB3ZWVrJ3MgbGFiIG1hdGVyaWFscyBpZiB5b3UgZG9uJ3QgcmVtZW1iZXIgaG93IHRvIGRvIHRoaXMuIA0KDQpgYGB7cn0NCiMgc2hvdyBmaXJzdCA2IHJvd3MNCmhlYWQoYWNvdXN0aWMpDQojIHNob3cgbGFzdCA2IHJvd3MNCnRhaWwoYWNvdXN0aWMpDQojIFZpZXcgdGhlIGRhdGFzZXQgaW4gYSBuZXcgdGFiLiBOQjogdGhlICdWJyBpbiBWaWV3IGlzIGNhcGl0YWxpc2VkDQojVmlldyhhY291c3RpYykgDQojIGdldCB0aGUgbnVtYmVyIG9mIHJvd3MNCm5yb3coYWNvdXN0aWMpDQojIGdldCB0aGUgbnVtYmVyIG9mIGNvbHVtbnMNCm5jb2woYWNvdXN0aWMpDQojIGdldCB0aGUgbnVtYmVyIG9mIHJvd3MgYW5kIGNvbHVtbnMgKHRoZSBmaXJzdCBudW1iZXIgaXMgcm93LCB0aGUgc2Vjb25kIGlzIGNvbHVtbikNCmRpbShhY291c3RpYykNCiMgc2hvdyBhIHN1bW1hcnkgb2YgZWFjaCBjb2x1bW4NCiMgaWYgaXQgaXMgbnVtZXJpYyBkYXRhLCBSIHdpbGwgcmV0dXJuIG1pbmludW0sIFExLCBtZWRpYW4sIG1lYW4sIFEzLCBhbmQgbWF4aW11bSwgYW5kIHRoZSBudW1iZXIgb2YgTkFzIChpZiBhbnkpDQpzdW1tYXJ5KGFjb3VzdGljKQ0KYGBgDQoNCiMjIyBDYWxjdWxhdGUgZGVzY3JpcHRpdmUgc3RhdGlzdGljcw0KDQpXZSBjYW4gY2FsY3VsYXRlIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3MgKGJvdGggY2VudHJhbCB0ZW5kZW5jaWVzIGFuZCBkaXNwZXJzaW9uKSBieSB1c2luZyBSIGZ1bmN0aW9ucywgYnV0IHJlbWVtYmVyIHRoYXQgdGhlc2UgZnVuY3Rpb25zIG9ubHkgYWNjZXB0ICoqdmVjdG9ycyoqIGJ1dCBub3QgZGF0YWZyYW1lcyBhcyB0aGVpciBhcmd1bWVudCBzbyB3ZSdsbCBmaXJzdCBuZWVkIHRvIGV4dHJhY3QgYSBjb2x1bW4gKHZlY3RvcikgZnJvbSB0aGUgZGF0YWZyYW1lIHVzaW5nIGAkYC4gVGhlIGZ1bmN0aW9uIG5hbWVzIGFyZSBzaG93biBiZWxvdy4gVGhleSBhbGwgaGF2ZSB2ZXJ5IHN0cmFpZ2h0Zm9yd2FyZCBmdW5jdGlvbiBuYW1lcyENCg0KYGBge3J9DQojIG1lYW4gLSBtZWFuKCkNCm1lYW4oYWNvdXN0aWMkdmFsdWUpDQojIG1lZGlhbiAtIG1lZGlhbigpDQptZWRpYW4oYWNvdXN0aWMkdmFsdWUpDQojIHN0YW5kYXJkIGRldmlhdGlvbiAtIHNkKCkNCnNkKGFjb3VzdGljJHZhbHVlKQ0KIyBpbnRlcnF1YXJ0aWxlIHJhbmdlLiAtIElRUigpLCBOQjogdXBwZXIgY2FzZSENCklRUihhY291c3RpYyR2YWx1ZSkNCiMgbWluaW11bSAtIG1pbigpDQptaW4oYWNvdXN0aWMkdmFsdWUpDQojIG1heGltdW0gLSBtYXgoKQ0KbWF4KGFjb3VzdGljJHZhbHVlKQ0KYGBgDQoNCldlIGNhbiBzdW1tYXJpc2UgYSBjaGFyYWN0ZXIvZmFjdG9yIHZlY3RvciAoaS5lLiwgY2F0ZWdvcmljYWwgdmFyaWFibGUpIHdpdGggYHRhYmxlKClgIGZ1bmN0aW9uLiBJdCB3aWxsIHJldHVybiB0aGUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIGNhdGVnb3J5LiBGb3IgZXhhbXBsZSwgdGhlcmUgYXJlIHR3byBjYXRlZ29yaWVzIGluIHRoZSBzZXggY29sdW1uLCBNIGFuZCBGIChtYWxlIGFuZCBmZW1hbGUpLiBgdGFibGUoKWAgd2lsbCBjb3VudCBob3cgbWFueSBvYnNlcnZhdGlvbnMgYXJlIE0gYW5kIGhvdyBtYW55IG9ic2VydmF0aW9ucyBhcmUgRi4gSElOVDogV2Ugc3RpbGwgbmVlZCB0byB1c2UgYCRgLg0KDQpgYGB7cn0NCiMgc3VtbWFyaXNlIHRoZSBzZXggY29sdW1uDQp0YWJsZShhY291c3RpYyRzZXgpDQoNCiMgc3VtbWFyaXNlIHRoZSBtZWFzdXJlcyBjb2x1bW4NCnRhYmxlKGFjb3VzdGljJG1lYXN1cmVzKQ0KYGBgDQoNClIgZG9lcyBub3QgaGF2ZSBhIGJ1aWx0LWluIGZ1bmN0aW9uIGZvciB0aGUgbW9kZSBzbyB3ZSB3aWxsIG5lZWQgdG8gY3JlYXRlIG91ciBvd24gZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBtb2RlLiBMZXQncyBjcmVhdGUgYSAiZ2V0bW9kZSIgZnVuY3Rpb24uIFlvdSBjYW4gY3JlYXRlIHlvdXIgb3duIGZ1bmN0aW9uIGJ5IHVzaW5nIHRoZSBzeW50YXggYGZ1bmN0aW9uX25hbWUgPC0gZnVuY3Rpb24oYXJndW1lbnRzKSB7Li4uc29tZSBjb2RlIGhlcmUgLi4ufWAuIEFmdGVyIHlvdSBydW4gdGhlIGNvZGUsIHlvdSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBqdXN0IGxpa2Ugb3RoZXIgZnVuY3Rpb25zIHdlIGhhdmUgdXNlZCwgaS5lLiwgYGZ1bmN0aW9uX25hbWUoYXJndW1lbnRzPS4uLilgDQoNCiogQ3JlYXRlIGEgYmltb2RhbCB2ZWN0b3IgKGFueXRoaW5nIHlvdSBsaWtlKS4gSElOVDogdXNlIHRoZSBgYygpYCBmdW5jdGlvbg0KKiBDcmVhdGUgYSBmdW5jdGlvbiBjYWxsZWQgZ2V0bW9kZSAodGhpcyBpcyBhbHJlYWR5IHByb3ZpZGVkKQ0KKiBVc2UgdGhlIGZ1bmN0aW9uIHRvIGdldCB0aGUgbW9kZSBvZiB5b3VyIHZlY3Rvcg0KDQpgYGB7cn0NCiMgY3JlYXRlIGEgYmltb2RhbCB2ZWN0b3INCnNvbWVfbnVtYmVycyA8LSBjKDEsMSwyLDMsNCw1LDYsNyw3LDgsOCw5KQ0KDQojIGNyZWF0ZSBhIGZ1bmN0aW9uIGNhbGxlZCBnZXRtb2RlDQpnZXRtb2RlIDwtIGZ1bmN0aW9uKHgpIHsNCiAgY291bnQgPC0gdGFibGUoeCkNCiAgY291bnRbd2hpY2goY291bnQgPT0gbWF4KGNvdW50KSldDQp9DQoNCiMgdXNlIHRoZSBmdW5jdGlvbg0KZ2V0bW9kZShzb21lX251bWJlcnMpDQojIFRoZSBmaXJzdCByb3cgaXMgdGhlIG9ic2VydmF0aW9ucw0KIyBUaGUgc2Vjb25kIHJvdyBpcyB0aGUgbnVtYmVyIG9mIGNvcnJlc3BvbmRpbmcgb2JzZXJ2YXRpb25zIA0KYGBgDQoNCiMjIFBhcnQgMzogRGF0YSBtYW5pcHVsYXRpb24NCg0KIyMjIFdpZGUgYW5kIGxvbmcgZGF0YSBmb3JtYXQgDQoNCkluICoqd2lkZSoqIGRhdGEsIGVhY2ggcm93IHJlcHJlc2VudHMgYW4gaW5kaXZpZHVhbCBjYXNlLCB3aXRoIG9ic2VydmF0aW9ucyBmb3IgdGhhdCBjYXNlIGluIHNlcGFyYXRlIGNvbHVtbnMuIEZvciBleGFtcGxlLCBhIHJvdyBjb250YWlucyBhbGwgZGF0YSBmcm9tIG9uZSBwYXJ0aWNpcGFudC4gVGhlIGZpcnN0IGNvbHVtbiBpcyB0aGUgcGFydGljaXBhbnQncyByZXNwb25zZSB0byB0aGUgZmlyc3QgdHJpYWwsIHRoZSBzZWNvbmQgY29sdW1uIGlzIHJlc3BvbnNlIHRvIHRoZSBzZWNvbmQgdHJpYWwsIHNvIG9uIGFuZCBzbyBmb3J0aC4gDQoNCkluICoqbG9uZyoqIGRhdGEsIGVhY2ggcm93IHJlcHJlc2VudHMgYSBzaW5nbGUgb2JzZXJ2YXRpb24sIGFuZCB0aGUgb2JzZXJ2YXRpb25zIGFyZSBncm91cGVkIHRvZ2V0aGVyIGludG8gY2FzZXMgYmFzZWQgb24gdGhlIHZhbHVlIG9mIGEgdmFyaWFibGUuIEZvciBleGFtcGxlLCBvbmUgcGFydGljaXBhbnQgb2NjdXBpZXMgbXVsdGlwbGUgcm93cy4gVGhlIGZpcnN0IHJvdyBpcyB0aGUgcGFydGljaXBhbnQncyByZXNwb25zZSB0byB0aGUgZmlyc3QgdHJpYWwsIHRoZSBzZWNvbmQgcm93IGlzIHJlc3BvbnNlIHRvIHRoZSBzZWNvbmQgdHJpYWwsIHNvIG9uIGFuZCBzbyBmb3J0aC4gIA0KDQpOb3cgaGF2ZSBhIGxvb2sgYXQgdGhlIHJhdGluZ3MgZGF0YXNldC4gSXMgaXQgaW4gdGhlIHdpZGUgb3IgbG9uZyBmb3JtYXQ/IEhJTlQ6IHVzZSBgaGVhZCgpYA0KDQpgYGB7cn0NCiMgSGF2ZSBhIGxvb2sgYXQgdGhlIHJhdGluZ3MgZGF0YXNldA0KaGVhZChyYXRpbmdzKQ0KIyBJdCBoYXMgYSB3aWRlIGRhdGEgZm9ybWF0LiBFYWNoIHJvdyBpcyBhIHZvaWNlIChpZGVudGlmaWVkIGJ5IHZvaWNlIElEKSwgYW5kIGVhY2ggY29sdW1uIGlzIGFuIG9ic2VydmF0aW9uLCBpLmUuLCB0aGUgcmF0aW5nIG9mIHRoZSB2b2ljZSBieSBvbmUgcGFydGljaXBhbnQuIA0KYGBgDQoNClRoZW4gaGF2ZSBhIGxvb2sgYXQgdGhlIGFjb3VzdGljIGRhdGFzZXQuIElzIGl0IGluIHRoZSB3aWRlIG9yIGxvbmcgZm9ybWF0PyANCg0KYGBge3J9DQojIEhhdmUgYSBsb29rIGF0IHRoZSBhY291c3RpYyBkYXRhc2V0DQpoZWFkKGFjb3VzdGljKQ0KIyBJdCBoYXMgYSBsb25nIGRhdGEgZm9ybWF0LiBFYWNoIHJvdyBpcyBhbiBvYnNlcnZhdGlvbi4gWW91IGNhbiBzZWUgdGhhdCB2b2ljZSAxIGhhcyB0d28gcm93czogdGhlIGZpcnN0IHJvdyBpcyB0aGUgbWVhc3VyZSBvZiBwaXRjaCwgdGhlIHNlY29uZCBpcyBkaXNwZXJzaW9uLiANCmBgYA0KDQpXZSBjYW4gdXNlIGBzcHJlYWQoKWAgdG8gdHJhbnNmb3JtIGEgbG9uZyBmb3JtYXQgaW50byBhIHdpZGUgZm9ybWF0IGFuZCB1c2UgYGdhdGhlcigpYCB0byB0cmFuc2Zvcm0gYSB3aWRlIGZvcm1hdCBpbnRvIGEgbG9uZyBmb3JtYXQuIA0KDQpVc2UgYHNwcmVhZChkYXRhZnJhbWUsIGtleSwgdmFsdWUpYCB0byB0cmFuc2Zvcm0gYSBsb25nIGZvcm1hdCBpbnRvIGEgd2lkZSBmb3JtYXQuIGtleT10aGUgY29sdW1uIGNvbnRhaW5pbmcgbGFiZWxzOyB2YWx1ZSA9IHRoZSBjb2x1bW4gY29udGFpbmluZyB2YWx1ZXMNCg0KSWYgd2UgdHJhbnNmb3JtIHRoZSBhY291c3RpYyBkYXRhc2V0IGludG8gYSB3aWRlIGZvcm1hdCwgdGhlIGtleSBpcyB0aGUgY29sdW1uICJtZWFzdXJlcyIgd2hpY2ggY29udGFpbnMgdGhlIG5hbWUgb2YgdmFsdWVzIChQaXRjaCwgRGlzcGVyc2lvbikuIFRoZSB2YWx1ZSBpcyB0aGUgY29sdW1uICJ2YWx1ZSIgd2hpY2ggY29udGFpbnMgdGhlIGFjdHVhbCBtZWFzdXJlcyBpbiBudW1iZXJzIChlLmcuLCAxMTksIDEwNjEpLiANCg0KVXNlIGBnYXRoZXIoZGF0YWZyYW1lLCBrZXksIHZhbHVlLCAuLi4pYCB0byB0cmFuc2Zvcm0gYSB3aWRlIGZvcm1hdCBpbnRvIGEgbG9uZyBmb3JtYXQuIGtleSA9IGEgbmV3IGNvbHVtbiBuYW1lIGZvciBhbGwgb2xkIGNvbHVtbiBuYW1lcywgdmFsdWUgPSBhIG5ldyBjb2x1bW4gbmFtZSBjb250YWluaW5nIHZhbHVlcywgIi4uLiIgc2hvdWxkIGluY2x1ZGUgYSB2ZWN0b3Igb2YgYWxsIG9sZCBjb2x1bW4gbmFtZXMNCg0KSWYgd2UgdHJhbnNmb3JtIHRoZSByYXRpbmcgZGF0YXNldCBpbnRvIGEgbG9uZyBmb3JtYXQsIHRoZSBrZXkgaXMgYSBuZXcgY29sdW1uIG5hbWUgc3VjaCBhcyAicGFydGljaXBhbnQiIHRoYXQgd2Ugd2FudCB0byB1c2UgdG8gaW5jbHVkZSBwYXJ0aWNpcGFudHMgKFAxLCBQMiwgZXRjLikuIFRoZSB2YWx1ZSBpcyBhIG5ldyBjb2x1bW4gbmFtZSBzdWNoIGFzICJyYXRpbmciIHRoYXQgd2Ugd2FudCB0byB1c2UgdG8gaW5jbHVkZSB0aGUgYWN0dWFsIHJhdGluZ3MgaW4gbnVtYmVycyAoZS5nLiwgNywgNy44LCA4KS4gImEgdmVjdG9yIG9mIG9sZCBjb2x1bW5zIHRvIGluY2x1ZGUiIG5lZWQgdG8gaW5jbHVkZSBhbGwgY29sdW1ucyBmcm9tIFAxIHRvIFAyOCwgeW91IGNhbiB3cml0ZSBgUDE6UDI4YCAoZ2l2ZW4gdGhhdCB0aGVzZSBjb2x1bW5zIGFyZSBsb2NhdGVkIGNvbnNlY3V0aXZlbHkgd2l0aG91dCBicmVhayBpbiBiZXR3ZWVuKS4gDQoNCk5vdyB0cmFuc2Zvcm0gdGhlIGFjb3VzdGljIGRhdGFzZXQgaW50byBhIHdpZGUgZm9ybWF0LCBhbmQgdGhlbiB0aGUgcmF0aW5ncyBkYXRhc2V0IGludG8gYSBsb25nIGZvcm1hdC4gQ2hlY2sgaWYgeW91IGhhdmUgbWFkZSB0aGUgY29ycmVjdCB0cmFuc2Zvcm1hdGlvbi4gV2hhdCBkbyB0aGUgdHJhbmZvcm1lZCBkYXRhIGxvb2sgbGlrZT8gDQoNCmBgYHtyfQ0KIyB0cmFuc2Zvcm0gYWNvdXN0aWMgaW50byBhIHdpZGUgZm9ybWF0DQphY291c3RpY193aWRlIDwtIHNwcmVhZChhY291c3RpYywga2V5ID0gbWVhc3VyZXMsIHZhbHVlID0gdmFsdWUpDQojIEhhdmUgYSBsb29rIGF0IHRoZSB0cmFuc2Zvcm1lZCBkYXRhc2V0DQpoZWFkKGFjb3VzdGljX3dpZGUpDQoNCiMgdHJhbnNmb3JtIHJhdGluZ3MgaW50byBhIGxvbmcgZm9ybWF0DQpyYXRpbmdzX2xvbmcgPC0gZ2F0aGVyKHJhdGluZ3MsIGtleSA9IHBhcnRpY2lwYW50LCB2YWx1ZSA9IHJhdGluZywgUDE6UDI4KQ0KIyBIYXZlIGEgbG9vayBhdCB0aGUgdHJhbnNmb3JtZWQgZGF0YXNldA0KaGVhZChyYXRpbmdzX2xvbmcpDQpgYGANCg0KIyMjIEdyb3VwIGJ5IGFuZCBTdW1tYXJpc2UNCg0KTm93IHdlIGhhdmUgZWFjaCBwYXJ0aWNpcGFudHMnIHJhdGluZyBmb3IgZWFjaCB2b2ljZS4gSG93IGNhbiB3ZSBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgcmF0aW5nIG9mIGVhY2ggdm9pY2U/IA0KDQoqKkdyb3VwIGJ5Kio6IERvIHlvdXIgb3BlcmF0aW9ucyB3aXRoaW4gZWFjaCBjYXRlZ29yeSBzcGVjaWZpZWQgaW4gdGhlIGBncm91cF9ieSgpYCBmdW5jdGlvbiAoTkI6IHRoaXMgc3RlcCBvbmx5IHNwZWNpZmllcyB0aGUgZ3JvdXBzLCBhbmQgbm90aGluZyBoYXMgZG9uZSB5ZXQgdG8gdGhlc2UgZ3JvdXBzKS4NCg0KKipTdW1tYXJpc2UqKjogU3BlY2lmeSB0aGUgY29sdW1uIG5hbWVzIGFuZCB2YWx1ZXMgdG8gZ2l2ZSB0byB0aGUgY29sdW1ucy4gVGhlIHBhcmFtZXRlcnMgdGFrZSB0aGUgZm9ybWF0OiBgY29sdW1uX25hbWUgPSBmdW5jdGlvbihhcmd1bWVudHMsIC4uLilgLiBPcGVyYXRpb25zL2NhbGN1bGF0aW9ucyB3aWxsIGJlIGRvbmUgd2l0aGluIGVhY2ggZ3JvdXAgc3BlY2lmaWVkIGJ5IHRoZSBwcmV2aW91cyBgZ3JvdXBfYnkoKWAgZnVuY3Rpb24uIA0KDQoqKlBpcGUgYCU+JWAqKjogQSBwaXBlIGFsbG93cyB5b3UgdG8gcGFzcyB0aGUgcmVzdWx0IG9mIGEgcHJldmlvdXMgZnVuY3Rpb24gYXMgYW4gYXJndW1lbnQgdG8gdGhlIG5leHQgZnVuY3Rpb24gd2l0aG91dCBzYXZpbmcgaXQgYXMgYW4gb2JqZWN0LiBJdCdzIGxpa2UgYSBwcm9kdWN0aW9uIGxpbmUhIA0KDQpXZSBuZWVkIHRvIGRvIHRoaXMgc3RlcCBieSBzdGVwIGFuZCBjb25uZWN0IHRoZSBzdGVwcyBieSBwaXBlcywgbGlrZSBgZGF0YWZyYW1lIG5hbWUgPC0gZGF0YWZyYW1lIG5hbWUgJT4lIGdyb3VwX2J5KHNvbWUgY29sdW1uIG5hbWUpICU+JSBzdW1tYXJpc2UoY29sdW1uIG5hbWUgPSBmdW5jdGlvbihhcmd1bWVudHMsIC4uLikpYC4gDQoNCkJlIGNhcmVmdWwhIFdoaWNoIHJhdGluZyBkYXRhc2V0IGRvIHdlIG5lZWQsIHdpZGUgb3IgbG9uZyBmb3JtYXQ/IA0KDQpgYGB7cn0NCiMgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIHJhdGluZyBvZiBlYWNoIHZvaWNlIGFuZCBzYXZlIGl0IHRvICJyYXRpbmdfYXZlIg0KcmF0aW5nX2F2ZSA8LSByYXRpbmdzX2xvbmcgJT4lIA0KICBncm91cF9ieShWb2ljZUlEKSAlPiUgDQogIHN1bW1hcml6ZShyYXRpbmcgPSBtZWFuKHJhdGluZykpDQpoZWFkKHJhdGluZ19hdmUpDQpgYGANCg0KIyMjIEpvaW4gdHdvIGRhdGFmcmFtZSBieSBpbm5lcl9qb2luDQoNCk5vdyB3ZSBoYXZlIHRoZSBhdmVyYWdlIHJhdGluZyBmb3IgZWFjaCB2b2ljZSBpbiB0aGUgInJhdGluZ19hdmUiIGRhdGFmcmFtZSwgYW5kIHdlIGhhdmUgdGhlIG1lYXN1cmUgb2YgcGl0Y2ggYW5kIGRpc3BlcnNpb24gb2YgZWFjaCB2b2ljZSBpbiB0aGUgImFjb3VzdGljX3dpZGUiIGRhdGFmcmFtZS4gVG8gZmluZCBvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHBlcmNlaXZlZCB0cnVzdHdvcnRoaW5lc3MgKHRoZSByYXRpbmcgc2NvcmVzKSBhbmQgYWNvdXN0aWMgZmVhdHVyZXMgb2YgdGhlIHZvaWNlIChpLmUuLCBwaXRjaCBhbmQgZGlzcGVyc2lvbiksIHdlIG5lZWQgdG8gam9pbiB0aGUgdHdvIGRhdGFmcmFtZSB0b2dldGhlci4gDQoNCldlIGNhbiBqb2luIHR3byBkYXRhZnJhbWVzIGJ5IHVzaW5nIHRoZSBmdW5jdGlvbiBgaW5uZXJfam9pbigpYC4gSXQgdGFrZXMgYSBwYXJhbWV0ZXIgYGJ5ID0gdGhlIGNvbW1vbiBjb2x1bW4gaW4gdGhlIHR3byBkYXRhZnJhbWVzYCAodGhlIGNvbW1vbiBjb2x1bW4gY2FuIGFsc28gYmUgZGV0ZWN0ZWQgYnkgUiBhdXRvbWF0aWNhbGx5KS4gQWZ0ZXIgdGhpcywgd2UgY2FuIHJlbW92ZSBkYXRhZnJhbWVzIHdlIGRvbid0IG5lZWQgYnkgdXNpbmcgYHJtKClgLiANCg0KSElOVDogYGlubmVyX2pvaW4oZGF0YWZyYW1lMSwgZGF0YWZyYW1lMiwgYnk9Ii4uLiIpYC4gUmVtZW1iZXIgdG8gc2F2ZSB0aGUgY29tYmluZWQgZGF0YWZyYW1lIGFzICJkYXRhIi4gDQoNCmBgYHtyfQ0KZGF0YSA8LSBpbm5lcl9qb2luKGFjb3VzdGljX3dpZGUsIHJhdGluZ19hdmUsIGJ5ID0gIlZvaWNlSUQiKQ0KaGVhZChkYXRhKQ0KDQojIHdlIGNhbiByZW1vdmUgZGF0YWZyYW1lcyB3ZSBkb24ndCBuZWVkIGFueSBtb3JlDQpybShhY291c3RpY193aWRlLCByYXRpbmdzLCByYXRpbmdzX2xvbmcpDQpgYGANCg0KIyMjIENhbGN1bGF0ZSBNZWFuLCBTRCwgU0VNL1NFLCBhbmQgQ0kuIA0KDQpXZSBjYW4gZ2V0IGJhc2ljIGRlc2NyaXB0aXZlIHN0YXRpY3MgYnkgdXNpbmcgYHN1bW1hcnkoKWAsIGJ1dCBpdCBkb2Vzbid0IGNhbGN1bGF0ZSBTdGFuZGFyZCBFcnJvciBvZiB0aGUgTWVhbiAoU0VNKSAob3Igc2ltcGx5IFNFKSBvciBDb25maWRlbmNlIEludGVydmFsIG9mIHRoZSBtZWFuIChDSSkuIFdlIGNhbiBjYWxjdWxhdGUgU0UgYW5kIENJIG91cnNlbHZlcyBiYXNlZCBvbiB0aGUgZXF1YXRpb25zIChyZXZpZXcgdGhlIGxlY3R1cmUgc2xpZGVzIGZvciB0aGUgZXF1YXRpb25zISkNCg0KTGV0J3MgY2FsY3VsYXRlIHRoZXNlIHN0YXRpc3RpY3MgZm9yIGVhY2ggc2VjIChtYWxlIGFuZCBmZW1hbGUpIGFuZCBlYWNoIG1lYXN1cmVzIChwaXRjaCBhbmQgZGlzcGVyc2lvbikuDQoNCiogV2UgaGF2ZSBhbGwgZGF0YSBjb21iaW5lZCBpbiBvbmUgZGF0YWZyYW1lIGNhbGxlZCAiZGF0YSIsIGJ1dCBpdCBpcyBpbiBhIHdpZGUgZm9ybWF0LiBUbyBtYWtlIHRoaW5ncyBlYXNpZXIsIHdlIGZpcnN0IHRyYW5zZm9ybSB0aGUgZGF0YSB0byBhIGxvbmcgZm9ybWF0LiANCiogVGhlbiB3ZSB1c2UgYGdyb3VwX2J5KClgIGFuZCBgc3VtbWFyaXNlKClgIGFzIHdlJ3ZlIHByYWN0aWNlZCBiZWZvcmUuIFRoaXMgdGltZSwgd2UgY2FsY3VsYXRlIG1vcmUgc3RhdGlzdGljcywgaW5jbHVkaW5nIGBNZWFuID0gLi4uLCBTRCA9IC4uLiwgU0UgPSAuLi4sIENJX2xvd2VyID0gLi4uLCBDSV91cHBlciA9IC4uLmANCg0KSElOVDogYG4oKWAgY2FuIGJlIHVzZWQgdG8gY291bnQgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gYSBnaXZlbiBncm91cC4gDQoNCmBgYHtyfQ0KIyB0byBtYWtlIHRoaW5ncyBlYXNpZXIsIHdlIGZpcnN0IGNoYW5nZSB0aGUgZGF0YSB0byBhIGxvbmcgZm9ybWF0DQpkYXRhX2xvbmcgPC0gZGF0YSAlPiUNCiAgZ2F0aGVyKGtleSA9IG1lYXN1cmVzLCB2YWx1ZSA9IHZhbHVlLCBEaXNwZXJzaW9uOnJhdGluZykNCg0KIyBzdW1tYXJpc2UgYnkgc2V4IGFuZCBtZWFzdXJlcywgYW5kIHNhdmUgdGhlIHJlc3VsdHMgdG8gYSBkYXRhZnJhbWUgY2FsbGVkICJzdW1tYXJ5Ig0Kc3VtbWFyeSA8LSBkYXRhX2xvbmcgJT4lDQogIGdyb3VwX2J5KHNleCwgbWVhc3VyZXMpICU+JQ0KICBzdW1tYXJpc2UoTWVhbiA9IG1lYW4odmFsdWUpLA0KICAgICAgICAgICAgU0QgPSBzZCh2YWx1ZSksDQogICAgICAgICAgICBTRSA9IFNEL3NxcnQobigpKSwNCiAgICAgICAgICAgIENJX3VwcGVyID0gTWVhbiArIDEuOTYgKiBTRSwNCiAgICAgICAgICAgIENJX2xvd2VyID0gTWVhbiAtIDEuOTYgKiBTRSkNCmBgYA0KDQpTb21ldGltZXMgd2UgbWF5IHdhbnQgdG8gdXNlIGEgc3Vic2V0IG9mIHRoZSBkYXRhLiBGb3IgZXhhbXBsZSwgaWYgeW91J3JlIGludGVyZXN0ZWQgaW4gZmVtYWxlIHZvaWNlcyBidXQgbm90IG1hbGVzLCB5b3Ugd2lsbCBuZWVkIHRvIGZpbHRlciBvdXQgYWxsIHRoZSBvYnNlcnZhdGlvbnMgb2YgbWFsZXMuIFdlIGNhbiBhY2hpZXZlIHRoaXMgYnkgYGZpbHRlcigpYCBvciBgc3Vic2V0KClgLiANCg0KYGBmaWx0ZXIoZGF0YWZyYW1lLCBsb2dpY2FsIG9wZXJhdGlvbnMpYGANCg0KYGBzdWJzZXQoZGF0YWZyYW1lLCBsb2dpY2FsIG9wZXJhdGlvbnMpYGANCg0KWW91IG1heSBhbHJlYWR5IG5vdGUgdGhhdCB0aGUgcGFyYW1ldGVycyBmb3IgdGhlIHR3byBmdW5jdGlvbnMgYXJlIHRoZSBzYW1lLCBhbmQgdGhleSBkbyB0aGUgc2FtZSB0aGluZy4gDQoNCkxvZ2ljYWwgb3BlcmF0aW9ucyByZXR1cm4gVFJVRSBvciBGQUxTRS4gRm9yIGV4YW1wbGUsIGlmIHdlIHdyaXRlIGBzZXggPT0gIk0iYCAoc2V4IGVxdWFscyBNKSwgYWxsIG9ic2VydmF0aW9ucyBvZiAiTSIgd2lsbCByZXR1cm4gVFJVRSwgYW5kICJGIiB3aWxsIHJldHVybiBGQUxTRS4gT3RoZXIgb3BlcmF0aW9ucyBpbmNsdWRlOiBub3QgZXF1YWwgdG8gYCE9YCwgbW9yZSB0aGFuIGA+YCwgbGVzcyB0aGFuIGA8YC4gWW91IGNhbiBhbHNvIHVzZSBgJmAgKGFuZCkgYW5kIGB8YCAob3IpIHRvIG1ha2UgbW9yZSBjb21wbGV4IGxvZ2ljYWwgb3BlcmF0aW9ucy4gDQoNCmBgYHtyfQ0KIyB1c2luZyBmaWx0ZXIgdG8gZmlsdGVyIG91dCBtYWxlcyBpbiB0aGUgc3VtbWFyeSBkYXRhZnJhbWUNCnN1bV9mIDwtIGZpbHRlcihzdW1tYXJ5LCBzZXggPT0gIkYiKQ0Kc3VtX2YNCg0KIyB1c2luZyBzdWJzZXQgdG8gc3Vic2V0IGZlbWFsZSB2b2ljZXMgaW4gdGhlIHN1bW1hcnkgZGF0YWZyYW1lDQpzdW1fZjIgPC0gc3Vic2V0KHN1bW1hcnksIHNleCA9PSAiRiIpDQpzdW1fZjINCmBgYA0KDQoNCg0KDQojIyBQYXJ0IDQ6IERhdGEgdmlzdWFsaXNhdGlvbg0KDQpXZSBjYW4gdXNlIGBnZ3Bsb3QoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGFsbCB0aGUgcGxvdHMgd2UgbmVlZC4gVGhpcyBmdW5jdGlvbiBpcyBpbmNsdWRlZCBpbiB0aGUgZ2dwbG90IHBhY2thZ2Ugd2hpY2ggaXMgc3BlY2lhbGl6ZWQgaW4gZGF0YSB2aXN1YWxpc2F0aW9uLCBidXQgYmVjYXVzZSB0aGUgZ2dwbG90IHBhY2thZ2UgaXMgaW5jbHVkZWQgaW4gdGlkeXZlcnNlLCB3ZSBkb24ndCBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgZ2dwbG90IG9uY2Ugd2UgaGF2ZSBpbnN0YWxsZWQgYW5kIGxvYWQgdGlkeXZlcnNlLiANCg0KVGhlIGdlbmVyYWwgc3ludGF4IGZvciBnZ3Bsb3QgaXMgYGdncGxvdChkYXRhZnJhbWVfbmFtZSwgYWVzKHggPSAsIHkgPSAsIC4uLikpICsgZ2VvbV94eHgoKSArIC4uLi5gLiBJbnNpZGUgdGhlIGBhZXMoKWAgaXMgdGhlIHZhcmlhYmxlIG9uIHggYW5kIHkgYXhpcyBhbmQgeW91ciBncm91cGluZyB2YXJpYWJsZSAoaWYgYW55KS4gVGhlIGBnZW9tX3h4eCgpYCBzcGVjaWZpZXMgd2hpY2ggcGxvdCB0byBjcmVhdGUsIGUuZy4sIGBnZW9tX2JveHBsb3QoKWAgKGJveHBsb3QpLCBgZ2VvbV9iYXIoKWAgKGJhcnBsb3QpLCBldGMuIA0KDQpGb3IgdGhlIGZvbGxvd2luZyBwcmFjdGljZSwgdGhpbmsgY2FyZWZ1bGx5IHdoaWNoIGRhdGFmcmFtZSB0byB1c2UsIGFuZCB3aGF0IHNob3VsZCBiZSBwdXQgb24gdGhlIHggYW5kIHkgYXhpcy4gDQoNCiMjIyBCb3hwbG90IA0KDQpBIGJveHBsb3QgaXMgc3BlY2lmaWVkIGJ5IGBnZW9tX2JveHBsb3QoKWAuIA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgYm94cGxvdCB0byBjb21wYXJlIGRpc3BlcnNpb24gb2Ygdm9pY2UgYmV0d2VlbSBtYWxlIGFuZCBmZW1hbGUNCmdncGxvdChkYXRhLCBhZXMoeCA9IHNleCwgeSA9IERpc3BlcnNpb24pKSArIA0KICBnZW9tX2JveHBsb3QoKSArIA0KICAjIHNwZWNpZnkgeCBhbmQgeSBheGlzIHRpdGxlIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHBsb3QNCiAgbGFicyh4ID0gIlNleCIsIHkgPSAiRGlzcGVyc2lvbiIsIHRpdGxlID0gIkRpc3BlcnNpb24gYnkgc2V4IikgDQpgYGANCg0KIyMjIFZpb2xpbiBwbG90DQoNCkEgdmlvbGluIHBsb3QgaXMgc3BlY2lmaWVkIGJ5IGBnZW9tX3Zpb2xpbigpYC4gDQoNCmBgYHtyfQ0KIyAod2l0aCBwb2ludHMpIENyZWF0ZSBhIHZpb2xpbiBwbG90IHRvIGNvbXBhcmUgZGlzcGVyc2lvbiBvZiB2b2ljZSBiZXR3ZWVtIG1hbGUgYW5kIGZlbWFsZQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc2V4LCB5ID0gRGlzcGVyc2lvbikpICsgDQogIGdlb21fdmlvbGluKCkgKyANCiAgIyBhZGRpbmcgcG9pbnRzIG9uIGEgdmlvbGluLCBhZGRpbmcgcmFuZG9tIGhvcml6b250YWwgc2hpZnQsIGNoYW5naW5nIHRoZSBzaGFwZSBvZiB0aGUgcG9pbnRzDQogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGg9MC4xKSwgc2hhcGUgPSAxKSArIA0KICAjIHNwZWNpZnkgeCBhbmQgeSBheGlzIHRpdGxlIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHBsb3QNCiAgbGFicyh4ID0gIlNleCIsIHkgPSAiRGlzcGVyc2lvbiIsIHRpdGxlID0gIkRpc3BlcnNpb24gYnkgc2V4IikNCg0KIyAod2l0aCBib3hwbG90KSBDcmVhdGUgYSB2aW9saW4gcGxvdCB0byBjb21wYXJlIGRpc3BlcnNpb24gb2Ygdm9pY2UgYmV0d2VlbSBtYWxlIGFuZCBmZW1hbGUNCmdncGxvdChkYXRhLCBhZXMoeCA9IHNleCwgeSA9IERpc3BlcnNpb24pKSArIA0KICBnZW9tX3Zpb2xpbigpICsgDQogICMgYWRkaW5nIGJveCBhbmQgd2hpc2tlcnMsIGNoYW5naW5nIHRyYW5zcGFyZW5jeSANCiAgZ2VvbV9ib3hwbG90KGFscGhhID0gMC41KSArIA0KICAjIHNwZWNpZnkgeCBhbmQgeSBheGlzIHRpdGxlIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHBsb3QNCiAgbGFicyh4ID0gIlNleCIsIHkgPSAiRGlzcGVyc2lvbiIsIHRpdGxlID0gIkRpc3BlcnNpb24gYnkgc2V4IikNCmBgYA0KDQoNCiMjIyBIaXN0b2dyYW0NCg0KQSBoaXN0b2dyYW0gaXMgc3BlY2lmaWVkIGJ5IGBnZW9tX2hpc3RvZ3JhbSgpYC4gDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSBoaXN0b2dyYW0gdG8gc2hvdyB0aGUgZGlzdHJpYnV0aW9uIG9mIERpc3BlcnNpb24NCmdncGxvdChkYXRhLCBhZXMoeCA9IERpc3BlcnNpb24pKSArDQogICMgY2hhbmdpbmcgYmlud2l0aCwgY2hhbmdpbmcgZmlsbCBhbmQgY29sb3IgDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAsIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpICsgDQogICMgc3BlY2lmeSB4IGFuZCB5IGF4aXMgdGl0bGUgYW5kIHRoZSB0aXRsZSBvZiB0aGUgcGxvdA0KICBsYWJzKHggPSAiRGlzcGVyc2lvbiIsIHkgPSAiRnJlcXVlbmN5IiwgdGl0bGUgPSAiSGlzdG9ncmFtIG9mIERpc3BlcnNpb24iKQ0KYGBgDQoNCiMjIyBEZW5zaXR5IHBsb3QNCg0KQSBkZW5zaXR5IHBsb3QgaXMgc3BlY2lmaWVkIGJ5IGBnZW9tX2RlbnNpdHkoKWAuIA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgZGVuc2l0eSBwbG90IHRvIHNob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBEaXNwZXJzaW9uDQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBEaXNwZXJzaW9uKSkgKw0KICAjIGNoYW5naW5nIGZpbGwgYW5kIGNvbG9yIA0KICBnZW9tX2RlbnNpdHkoZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikgKyANCiAgIyBzcGVjaWZ5IHggYW5kIHkgYXhpcyB0aXRsZSBhbmQgdGhlIHRpdGxlIG9mIHRoZSBwbG90DQogIGxhYnMoeCA9ICJEaXNwZXJzaW9uIiwgeSA9ICJEZW5zaXR5IiwgdGl0bGUgPSAiRGVuc2l0eSBvZiBEaXNwZXJzaW9uIikNCmBgYA0KDQpIb3cgdG8gcGxvdCBoaXN0b2dyYW0gYW5kIGRlbnNpdHkgdG9nZXRoZXIgd2hlbiB0aGVpciB5IGF4aXMgaXMgaW4gZGlmZmVyZW50IHNjYWxlPyBJbiBvcmRlciB0byBvdmVybGF5IGEga2VybmVsIGRlbnNpdHkgZXN0aW1hdGUgb3ZlciBhIGhpc3RvZ3JhbSBpbiBnZ3Bsb3QyIHlvdSB3aWxsIG5lZWQgdG8gcGFzcyBgYWVzKHkgPSAuLmRlbnNpdHkuLilgIHRvIGBnZW9tX2hpc3RvZ3JhbWAgYW5kIGFkZCBgZ2VvbV9kZW5zaXR5YC4NCg0KYGBge3J9DQojIENyZWF0ZSBhIGhpc3RvZ3JhbSB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgRGlzcGVyc2lvbiwgYW5kIG92ZXJsYXkgdGhlIGRlbnNpdHkNCmdncGxvdChkYXRhLCBhZXMoeCA9IERpc3BlcnNpb24pKSArIA0KICAjIGFkZGluZyBhIGhpc3RvZ3JhbSh1c2UgYWVzKHkgPSAuLmRlbnNpdHkuLikhKSwgY2hhbmdpbmcgY29sb3IgYW5kIGZpbGwNCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHkgPSAuLmRlbnNpdHkuLiksIGNvbG9yID0gImJsYWNrIiwgZmlsbCA9ICJ3aGl0ZSIpICsNCiAgIyBhZGRpbmcgZGVuc2l0eQ0KICBnZW9tX2RlbnNpdHkoKSArIA0KICAjIHNwZWNpZnkgeCBhbmQgeSBheGlzIHRpdGxlIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHBsb3QNCiAgbGFicyh4ID0gIkRpc3BlcnNpb24iLCB5ID0gIkZyZXF1ZW5jeSIsIHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBEaXNwZXJzaW9uIikNCmBgYA0KDQojIyMgU2NhdHRlciBwbG90DQoNCkEgc2NhdHRlciBwbG90IGlzIHNwZWNpZmllZCBieSBgZ2VvbV9wb2ludCgpYC4gDQoNCklmIHdlIHdhbnQgdG8gc2hvdyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZGlzcGVyc2lvbiBhbmQgdHJ1c3R3b3J0aGluZXNzIGZvciBtYWxlIGFuZCBmZW1hbGUgdm9pY2VzIHNlcGFyYXRlbHksIHdlIGNhbiBzcGVjaWZ5IGBjb2xvciA9IHNleGAgaW4gdGhlIGBhZXMoKWAgdG8gc2hvdyBtYWxlIGFuZCBmZW1hbGUgdm9pY2VzIGluIHRoZSBzYW1lIHBsb3QgYnV0IHdpdGggZGlmZmVyZW50IGNvbG9yLiBBbHRlcm5hdGl2ZWx5LCB3ZSBjYW4gYWRkIGArIGZhY2V0X3dyYXAofnNleClgIHRvIGRyYXcgdHdvIHNlcGFyYXRlIHBsb3RzIGZvciBtYWxlIGFuZCBmZW1hbGUgdm9pY2VzLg0KDQpXZSBjYW4gdXNlIGArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpYCB0byBhZGQgYSBsaW5lYXIgcmVncmVzc2lvbiBsaW5lLiANCg0KYGBge3J9DQojIENyZWF0ZSBhIHNjYXR0ZXIgcGxvdCB0byByZWZsZWN0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBkaXNwZXJzaW9uIGFuZCB0cnVzdHdvcnRoaW5lc3MNCiMgKHNlcGFyYXRlIHBsb3QpIEdyb3VwIGJ5IHNleA0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gRGlzcGVyc2lvbiwgeSA9IHJhdGluZykpICsgDQogIGdlb21fcG9pbnQoKSArDQogICMgYWRkaW5nIGEgcmVncmVzc2lvbiBsaW5lDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsgDQogICMgc3BlY2lmeSB4IGFuZCB5IGF4aXMgdGl0bGUgYW5kIHRoZSB0aXRsZSBvZiB0aGUgcGxvdA0KICBsYWJzKHggPSAiRGlzcGVyc2lvbiIsIHkgPSAiUGVyY2VpdmVkIHRydXN0d29ydGhpbmVzcyIpICsgDQogICMgZ3JvdXAgYnkgc2V4IChkcmF3IHNlcGFyYXRlIHBsb3QgZm9yIHNleCkNCiAgZmFjZXRfd3JhcCh+c2V4KQ0KDQojIChkaWZmZXJlbnQgY29sb3IgaW4gb25lIHBsb3QpIEdyb3VwIGJ5IHNleA0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gRGlzcGVyc2lvbiwgeSA9IHJhdGluZywgY29sb3IgPSBzZXgpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICAjIGFkZGluZyBhIHJlZ3Jlc3Npb24gbGluZQ0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArIA0KICAjIHNwZWNpZnkgeCBhbmQgeSBheGlzIHRpdGxlIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHBsb3QNCiAgbGFicyh4ID0gIkRpc3BlcnNpb24iLCB5ID0gIlBlcmNlaXZlZCB0cnVzdHdvcnRoaW5lc3MiKSArIA0KICAjIGNoYW5nZSB0aGUgY29sb3INCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwgImJsdWUiKSkgKw0KICAjIGNoYW5nZSB0aGVtZQ0KICB0aGVtZV9idygpDQpgYGANCg0KIyMjIEJhciBwbG90DQoNCkEgYmFyIHBsb3QgaXMgc3BlY2lmaWVkIGJ5IGBnZW9tX2JhcigpYC4gQSBiYXIgcGxvdCB3aWxsIGNvdW50IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGRpZmZlcmVudCBjYXRlZ29yaWVzLCBidXQgcGVvcGxlIHVzdWFsbHkgdXNlIGJhciBwbG90IHRvIHNob3cgbWVhbiBhbmQgQ0kgKHdoaWNoIGlzIGNhbGxlZCBlcnJvciBiYXIsIHdlIGNhbiBhZGQgd2l0aCBgKyBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gLi4uLCB5bWF4ID0gLi4uKSlgLiBGb3IgdGhpcyBwdXJwb3NlLCB3ZSB3aWxsIG5lZWQgdG8gc3BlY2lmeSBhIHBhcmFtZXRlciBgc3RhdCA9ICJpZGVudGl0eSJgLiBBbHRlcm5hdGl2ZWx5LCB3ZSBjYW4gdXNlIGBnZW9tX2NvbCgpYCwgd2hpY2ggaXMgZXF1aXZhbGVudCB0byBgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpYC4NCg0KTkI6IFdlIGhhdmUgY3JlYXRlZCBhIHN1bW1hcnkgZGF0YWZyYW1lIHdpdGggbWVhbiBhbmQgQ0kgb2YgZWFjaCBtZWFzdXJlIGZvciBtYWxlIGFuZCBmZW1hbGUgdm9pY2VzLiBXZSdsbCBuZWVkIHRvIGZpbHRlciBlYWNoIG1lYXN1cmUgYSB0aW1lLiANCg0KYGBge3IsIGZpZy53aWR0aD00LCBmaWcuaGVpZ2h0PTZ9DQojIGNyZWF0ZSBhIGJhciBwbG90IHdpdGggbWVhbiBhbmQgQ0kgb2YgRGlzcGVyc2lvbiBmb3IgbWFsZSBhbmQgZmVtYWxlDQpnZ3Bsb3QoZmlsdGVyKHN1bW1hcnksIG1lYXN1cmVzID09ICJEaXNwZXJzaW9uIiksIGFlcyh4ID0gc2V4LCB5ID0gTWVhbikpICsgDQogICMgYWRkaW5nIHRoZSBiYXIsIGNoYW5naW5nIGZpbGwgYW5kIGNvbG9yDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKSArIA0KICAjIGFkZGluZyBlcnJvciBiYXIsIGNoYW5naW5nIHRoZSB3aWR0aA0KICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gQ0lfbG93ZXIsIHltYXggPSBDSV91cHBlciksIHdpZHRoID0gMC41KSArIA0KICAjIGNoYW5nZSB5IGF4aXMgcmFuZ2UNCiAgeWxpbSgwLCAxMzAwKSArIA0KICAjIHNwZWNpZnkgeCBhbmQgeSBheGlzIHRpdGxlIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHBsb3QNCiAgbGFicyh4ID0gIlNleCIsIHkgPSAiTWVhbiBEaXNwZXJzaW9uIiwgdGl0bGUgPSAiIikNCmBgYA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgaXQncyB0aGUgc2FtZSB0byB1c2UgZ2VvbV9jb2wsIG5vIG5lZWQgdG8gc3BlY2lmeSBzdGF0ID0gImlkZW50aXR5IiB0aGVuDQpnZ3Bsb3QoZmlsdGVyKHN1bW1hcnksIG1lYXN1cmVzID09ICJEaXNwZXJzaW9uIiksIGFlcyh4ID0gc2V4LCB5ID0gTWVhbikpICsgDQogICMgYWRkaW5nIHRoZSBiYXIsIGNoYW5naW5nIGZpbGwgYW5kIGNvbG9yDQogIGdlb21fY29sKGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpICsgDQogICMgYWRkaW5nIGVycm9yIGJhciwgY2hhbmdpbmcgdGhlIHdpZHRoDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBDSV9sb3dlciwgeW1heCA9IENJX3VwcGVyKSwgd2lkdGggPSAwLjUpICsgDQogICMgY2hhbmdlIHkgYXhpcyByYW5nZQ0KICB5bGltKDAsIDE1MDApICsgDQogICMgc3BlY2lmeSB4IGFuZCB5IGF4aXMgdGl0bGUgYW5kIHRoZSB0aXRsZSBvZiB0aGUgcGxvdA0KICBsYWJzKHggPSAiU2V4IiwgeSA9ICJNZWFuIERpc3BlcnNpb24iLCB0aXRsZSA9ICIiKQ0KYGBgDQoNCg0KIyMjIGxpbmUgcGxvdA0KDQpBIGxpbmUgcGxvdCBpcyBzcGVjaWZpZWQgYnkgYGdlb21fbGluZSgpYC4gSXQncyBhbHNvIHVzZWQgdG8gc2hvdyB0aGUgbWVhbiBhbmQgQ0kgd2l0aCBlcnJvciBiYXJzLiBISU5UOiBgKyBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gLi4uLCB5bWF4ID0gLi4uKSlgDQoNCk5COiBXZSBoYXZlIGNyZWF0ZWQgYSBzdW1tYXJ5IGRhdGFmcmFtZSB3aXRoIG1lYW4gYW5kIENJIG9mIGVhY2ggbWVhc3VyZSBmb3IgbWFsZSBhbmQgZmVtYWxlIHZvaWNlcy4gV2UnbGwgbmVlZCB0byBmaWx0ZXIgZWFjaCBtZWFzdXJlIGEgdGltZS4gDQoNCmBgYHtyLCBmaWcud2lkdGg9NCwgZmlnLmhlaWdodD02fQ0KIyBjcmVhdGUgYSBsaW5lIHBsb3Qgd2l0aCBtZWFuIGFuZCBDSSBvZiBEaXNwZXJzaW9uIGFuZCBncm91cCBieSBzZXgNCiMgSWYgd2UgZG9uJ3QgaGF2ZSBhIHNlY29uZCBmYWN0b3IsIHdlIHNob3VsZCBzcGVjaWZ5IGdyb3VwID0gMS4gT3RoZXJ3aXNlLCBncm91cCA9IGZhY3RvciBuYW1lDQpnZ3Bsb3QoZmlsdGVyKHN1bW1hcnksIG1lYXN1cmVzID09ICJEaXNwZXJzaW9uIiksIGFlcyh4ID0gc2V4LCB5ID0gTWVhbiwgZ3JvdXAgPSAxKSkgKyANCiAgIyBhZGRpbmcgcG9pbnQgdG8gc2hvdyB0aGUgbWVhbg0KICBnZW9tX3BvaW50KCkgKyANCiAgIyBhZGRpbmcgYSBsaW5lIHRvIGNvbm5lY3QgdGhlIG1lYW4NCiAgZ2VvbV9saW5lKCkgKyANCiAgIyBhZGRpbmcgZXJyb3IgYmFycyB0byBzaG93IENJDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBDSV9sb3dlciwgeW1heCA9IENJX3VwcGVyKSwgd2lkdGggPSAwLjQpICsgDQogICMgY2hhbmdlIHRoZSByYW5nZSBvZiB5IGF4aXMNCiAgeWxpbSgxMDAwLCAxMTMwKSArIA0KICAjIHNwZWNpZnkgeCBhbmQgeSBheGlzIHRpdGxlIGFuZCB0aGUgdGl0bGUgb2YgdGhlIHBsb3QNCiAgbGFicyh4ID0gIlNleCIsIHkgPSAiTWVhbiBEaXNwZXJzaW9uIiwgdGl0bGUgPSAiIikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9MTB9DQojIFdoYXQgaWYgeW91IHdhbnQgdG8gcGxvdCBEaXNwZXJzaW9uLCBQaXRjaCBhbmQgcmF0aW5nIGFsbCBpbiBvbmU/DQojIFdlIGNhbiBzcGVjaWZ5IGdyb3VwID0gbWVhc3VyZXMgKGFuZCBub3QgZmlsdGVyaW5nIG1lYXN1cmVzID09ICJEaXNwZXJzaW9uIikNCiMgSWYgeW91IHdhbnQgdG8gZ2l2ZSBkaWZmZXJlbnQgY29sb3JzIHRvIGRpZmZlcmVudCBtZWFzdXJlcywgc3BlY2lmeSBjb2xvciA9IG1lYXN1cmVzDQpnZ3Bsb3Qoc3VtbWFyeSwgYWVzKHggPSBzZXgsIHkgPSBNZWFuLCBncm91cCA9IG1lYXN1cmVzLCBjb2xvciA9IG1lYXN1cmVzKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fbGluZSgpICsgDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBDSV9sb3dlciwgeW1heCA9IENJX3VwcGVyKSwgd2lkdGggPSAwLjQpICsgDQogICMgc3BlY2lmeSB4IGFuZCB5IGF4aXMgdGl0bGUgYW5kIHRoZSB0aXRsZSBvZiB0aGUgcGxvdA0KICBsYWJzKHggPSAiU2V4IiwgeSA9ICJNZWFuIERpc3BlcnNpb24iLCB0aXRsZSA9ICIiKQ0KYGBgDQoNCg0KQmVjYXVzZSB0aGUgc2NhbGVzIG9mIHRoZSBtZWFzdXJlcyBhcmUgc28gZGlmZmVyZW50IChhYm91dCAxMDAwLCAxNTAsIG9yIDUhKSwgdGhpcyBwbG90IGlzIHF1aXRlIGNvbmZ1c2luZywgdGh1cyBub3QgYSBnb29kIGNob2ljZS4gVGhpcyBpcyBqdXN0IHVzZWQgdG8gaWxsdXN0cmF0ZSBob3cgdG8gdXNlIHRoZSBgZ3JvdXA9Li4uYCBhcmd1bWVudCBpbiBgYWVzKClgIHRvIHBsb3Qgc2VwYXJhdGUgbGluZXMgZm9yIGRpZmZlcmVudCBncm91cHMuIA0KDQojIyMgU2F2ZSBkYXRhZnJhbWVzIGFuZCBwbG90cyB0byB5b3VyIGZvbGRlcg0KDQpXZSBjYW4gdXNlIGB3cml0ZS5jc3YoZGF0YWZyYW1lLCBmaWxlbmFtZSlgIHRvIHNhdmUgYSBkYXRhZnJhbWUgdG8gY3N2IGZpbGUsIG9yIGB3cml0ZS54bHN4KGRhdGFmcmFtZSwgZmlsZW5hbWUpYCAoZnJvbSB0aGUgb3Blbnhsc3ggbGlicmFyeSkgdG8gc2F2ZSBhcyB4bHN4IGZpbGUuIElmIG91dHB1dCBkaXJlY3RvcnkgaXMgbm90IHNwZWNpZmllZCwgSXQgd2lsbCBzYXZlIHRoZSBmaWxlIHRvIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4gDQoNCk5COiBCeSBkZWZhdWx0LCBpbiBgd3JpdGUuY3N2KClgIHRoZSBhcmd1bWVudCBgcm93Lm5hbWVzID0gVFJVRWAgd2lsbCBhc3NpZ24gYSBudW1iZXIgdG8gZWFjaCByb3cgc3RhcnRpbmcgZnJvbSAxIChleGNsdWRpbmcgdGhlIGhlYWRlciByb3cpLiBJZiB5b3UgZG9uJ3Qgd2FudCB0aGlzLCByZW1lbWJlciB0byBzZXQgYHJvdy5uYW1lcyA9IEZBTFNFYC4NCg0KYGBge3IsIGV2YWw9RkFMU0V9DQp3cml0ZS5jc3Yoc3VtbWFyeSwgInN1bW1hcnlfM19tZWFzdXJlcy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCndyaXRlLnhsc3goc3VtbWFyeSwgInN1bW1hcnlfM19tZWFzdXJlcy5jc3YiKQ0KYGBgDQoNCllvdSBjYW4gYWxzbyB1c2UgZnVuY3Rpb25zIGluIHRoZSBvcGVueGxzeCBwYWNrYWdlIHRvIHNhdmUgZGF0YWZyYW1lcyB0byBtdWx0aXBsZSBzaGVldHMgaW4gb25lIHhsc3ggZmlsZS4gDQoNCjEuIENyZWF0ZSBhIHdvcmsgYm9vayBgY3JlYXRlV29ya2Jvb2soKWAuDQoyLiBBZGQgYSBzaGVldCB0byB0aGUgd29yayBib29rIGFuZCBuYW1lIHRoZSBzaGVldCBgYWRkV29ya3NoZWV0KClgLg0KMy4gQWRkIGEgZGF0YWZyYW1lIHRvIGEgc3BlY2lmaWVkIHNoZWV0IGB3cml0ZURhdGEoKWAuIA0KNC4gU2F2ZSB0aGUgd29yayBib29rIHRvIHlvdXIgY29tcHV0ZXIgYnkgYSBnaXZlbiBuYW1lIGBzYXZlV29ya2Jvb2soKWAuIA0KDQpgYGB7cn0NCiMgZmlyc3Qgd2UgY3JlYXRlIGEgd29yayBib29rDQp3YiA8LSBjcmVhdGVXb3JrYm9vaygpDQojIGFkZCBhIHNoZWV0IHRvIHRoZSB3b3JrIGJvb2ssIGFuZCBuYW1lIHRoZSBzaGVldA0KYWRkV29ya3NoZWV0KHdiLCAiZGF0YV9qb2luZWQiKQ0KYWRkV29ya3NoZWV0KHdiLCAic3VtbWFyeSIpDQojIGFkZCBhIGRhdGFmcmFtZSB0byB0aGUgc2hlZXQuIFlvdSBuZWVkIHRvIHNwZWNpZnkgdGhlIHNoZWV0IG5hbWUgYW5kIHRoZSBkYXRhZnJhbWUgbmFtZQ0Kd3JpdGVEYXRhKHdiLCAiZGF0YV9qb2luZWQiLCBkYXRhKQ0Kd3JpdGVEYXRhKHdiLCAic3VtbWFyeSIsIHN1bW1hcnkpDQojIHNhdmUgdGhlIHdvcmsgYm9vaw0Kc2F2ZVdvcmtib29rKHdiLCAiam9pbmVkX2RhdGFfc3VtbWFyeS54bHN4Iiwgb3ZlcndyaXRlID0gVFJVRSkNCmBgYA0KDQpZb3UgY2FuIHNhdmUgdGhlIG1vc3QgcmVjZW50IHBsb3QgYnkgYGdnc2F2ZShmaWxlbmFtZSlgLiBZb3UgY2FuIGNob29zZSBmcm9tIG1hbnkgY29tbW9uIGZpbGUgdHlwZXMsIHN1Y2ggYXMgcGRmLCBwbmcsIGFuZCBqcGcuIA0KDQpgYGB7ciwgZXZhbD1GQUxTRX0NCiMgTGV0J3MgY3JlYXRlIGEgcGxvdA0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gc2V4LCB5ID0gRGlzcGVyc2lvbikpICsgDQogIGdlb21fYm94cGxvdCgpICsgDQogIGxhYnMoeCA9ICJTZXgiLCB5ID0gIkRpc3BlcnNpb24iLCB0aXRsZSA9ICJEaXNwZXJzaW9uIGJ5IHNleCIpDQoNCiMgc2F2ZSBpdCBieSBnZ3NhdmUgKHBkZiBpcyByZWNvbW1lbmRlZCkNCmdnc2F2ZSgiZGlzcGVyc2lvbl9saW5lLnBkZiIpDQpnZ3NhdmUoImRpc3BlcnNpb25fbGluZS5wbmciKQ0KZ2dzYXZlKCJkaXNwZXJzaW9uX2xpbmUuanBnIikNCmBgYA0KDQoNCiMjIFBhcnQgNTogTm9ybWFsaXR5IGNoZWNrDQoNCk5vcm1hbGl0eSBjaGVjayBpcyBhbiBpbXBvcnRhbnQgc3RlcCBiZWZvcmUgdGhlIGEgc3RhdGlzdGljYWwgdGVzdCAoYWxvbmcgd2l0aCBvdGhlciBhc3N1bXB0aW9uIGNoZWNraW5nKS4gDQoNCldlJ3ZlIGxlYXJudCBmb3VyIHdheXMgdG8gY2hlY2sgdGhlIG5vcm1hbGl0eSBvZiB5b3VyIGRhdGEsIGFuZCB0aGV5IGFyZSBvZnRlbiB1c2VkIHRvZ2V0aGVyLiANCg0KMS4gQ2hlY2sgbm9ybWFsaXR5IHZpc3VhbGx5IHdpdGggaGlzdG9ncmFtIChwbGVhc2Ugc2VlIGFib3ZlKS4gDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEsIGFlcyh4ID0gRGlzcGVyc2lvbikpICsgDQogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAid2hpdGUiKSArDQogIGdlb21fZGVuc2l0eSgpDQpgYGANCg0KMi4gQ2hlY2sgbm9ybWFsaXR5IHZpc3VhbGx5IHdpdGggTm9ybWFsIFEtUSBwbG90LiBOb3JtYWwgUS1RIFBsb3RzIGNvbXBhcmUgeW91ciBkYXRhIHNldOKAmXMgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIHRvIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uLiBJZiB0aGV5IGFyZSBzaW1pbGFyIGVub3VnaCwgeW91IHdpbGwgc2VlIHRoZSBkYXRhIHBvaW50cyBmb3JtIGEgbmljZSBzdHJhaWdodCBsaW5lIChvciBhcHByb3hpbWF0ZSBvbmUpLiBXZSB1c2UgYHFxbm9ybShkYXRhZnJhbWUkY29sdW0pYCB0byBjcmVhdGUgZG90cyBhbmQgYHFxbm9ybShkYXRhZnJhbWUkY29sdW0pYCB0byBhZGQgdGhlIGxpbmUuIA0KDQpgYGB7cn0NCiMgY2hlY2sgdGhlIG5vcm1hbGl0eSBvZiBEaXNwZXJzaW9uDQpxcW5vcm0oZGF0YSREaXNwZXJzaW9uKQ0KcXFsaW5lKGRhdGEkRGlzcGVyc2lvbikNCmBgYA0KDQozLiBDaGVjayBub3JtYWxpdHkgbnVtZXJpY2FsbHkgKFNrZXduZXNzICYgS3VydG9zaXMgdmFsdWVzKS4gVGhlcmUgYXJlIGRpZmZlcmVudCBmdW5jdGlvbnMgZm9yIHRoaXMgZGVwZW5kaW5nIG9uIHRoZSBwYWNrYWdlcyB5b3UgdXNlLiBVc2luZyB0aGUgJ3BzeWNoJyBwYWNrYWdlLCB3ZSBjYW4gY2hlY2sgc2tld25lc3MgYW5kIGt1cnRvc2lzIHdpdGggZnVuY3Rpb24gYGRlc2NyaWJlKGRhdGFmcmFtZSRjb2x1bSlgIGFuZCBsb29rIGZvciB0aGUgYHNrZXdgIGFuZCBga3VydG9zaXNgIGNvbHVtbnMuIFRoaXMgZnVuY3Rpb24gYWxzbyByZXR1cm5zIG1hbnkgb3RoZXIgZGVzY3JpcHRpdmUgc3RhdGlzdGljcy4gDQoNCmBgYHtyfQ0KIyBpbnN0YWxsIGFuZCBsb2FkIHRoZSBwc3ljaCBwYWNrYWdlDQojIGluc3RhbGwucGFja2FnZXMoInBzeWNoIikNCmxpYnJhcnkocHN5Y2gpDQoNCiMgY2hlY2sgZGVzY3JpcHRpdmUgc3RhdGlzdGljcyB1c2luZyBkZXNjcmliZSgpDQpkZXNjcmliZShkYXRhJERpc3BlcnNpb24pDQojIHdoYXQncyB0aGUgU2tld25lc3MgYW5kIEt1cnRvc2lzIHZhbHVlcz8NCiMgc2tldzogMC4yOyBrdXJ0b3NpcyAtMC44Mg0KYGBgDQoNCjQuIENoZWNrIG5vcm1hbGl0eSBzdGF0aXN0aWNhbGx5IChTaGFwaXJvLVdpbGsgVGVzdCkuIFNoYXBpcm8tV2lsayBUZXN0IGNvbXBhcmVzIHlvdXIgZGlzdHJpYnV0aW9uIHRvIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIChsaWtlIGEgUS1RIHBsb3QsIGJ1dCB3aXRoIGEgcC12YWx1ZSBvdXRwdXQpLiBSZW1lbWJlciB0aGUgbnVsbCBoeXBvdGhlc2lzIGlzIHRoZXJlIGlzIG5vIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoaXMgZGF0YSBzZXQgYW5kIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIChpLmUuIHRoaXMgZGF0YSBzZXQgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQpLiBJZiBwID4gLjA1LCB0aGUgZGF0YSBpcyBub3JtYWxseSBkaXN0cmlidXRlZCBiZWNhdXNlIHdlIGNhbid0IHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzLiBVc2UgYHNoYXBpcm8udGVzdChkYXRhZnJhbWUkY29sdW0pYCB0byBydW4gdGhlIHRlc3QuIA0KDQpgYGB7cn0NCiMgcnVuIHRoZSBTaGFwaXJvLVdpbGsgVGVzdA0Kc2hhcGlyby50ZXN0KGRhdGEkRGlzcGVyc2lvbikNCiMgd2hhdCdzIHRoZSBwIHZhbHVlPyANCiMgcCA9IDAuMjA4Niwgbm90IHNpZ25pZmljYW50LCBzbyB0aGUgZGlzdHJpYnV0aW9uIGlzIG5vcm1hbCEgDQpgYGANCg0K