Author: Kayla Keyue Chen

Last updated: 2024.4.28

Learning objectives

  • Import and export your dataset: Learn how to import your dataset from csv and xlsx files and export dataframe object to csv and xlsx files.

  • Overview your dataset: A recap of functions learnt last week (in your practice!): head(), tail(), summary(), View(), ncol(), nrow(), dim(). Learn some functions to calculate descriptive statistics: mean(), median(), sd(), IQR(), max(), min(), table() (and we will need to create our own function to calculate the mode).

  • Data manipulation: Learn how to use gather(), spread(), inner_join(), filter() from the tidyverse package and subset() which is a base R function to manipulate dataframes, and compute descriptive statistics such as mean, standard deviation, and standard error and confidence interval by ourselves using group_by() and summarise(). We will learn how to write multiple lines within a pipe using %>% in tidyverse.

  • Data visualisation: Learn how to use ggplot() in ggplot2 package (included in the tidyverse) to create a variety of plots: boxplot, violin plot, histogram, density plot, scatter plot, bar plot, and line plot.

  • Normality check: Get familiar with four ways to check whether your data is normally distributed. (1) Histogram. (2) Normal Q-Q Plot. (3) Skewness and Kurtosis values. (4) Shapiro-Wilk Test.

About the dataset

Today we will be working with real data to explore the question of whether there is a relationship between voice acoustics and ratings of perceived trustworthiness.

  • Pitch (or Fundamental Frequency or F0) is a measure of the vibration of the vocal chords in Hertz (Hz); males have on average a lower pitch than females for example.

  • Dispersion (measured again in Hz) is effectively a measure of the length of someone’s vocal tract (or neck). Height and neck length are suggested to be negatively correlated with formant dispersion, so tall people tend to have smaller formant dispersion.

More recently, work has focussed on what the sound of your voice suggests about your personality. McAleer, Todorov and Belin (2014) suggested that vocal acoustics give a perception of your trustworthiness and dominance to others, regardless of whether or not it is accurate.

Today we will look at data describing male and female voices, their pitch and dispersion, and associated measures of perceived trustworthiness.

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.

  1. Create a work book createWorkbook().
  2. Add a sheet to the work book and name the sheet addWorksheet().
  3. Add a dataframe to a specified sheet writeData().
  4. 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.

  1. Check normality visually with histogram (please see above).
ggplot(data, aes(x = Dispersion)) + 
  geom_histogram(aes(y = ..density..), color = "black", fill = "white") +
  geom_density()

  1. 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)

  1. 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
  1. 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