Data sctructures in R
Most of the data in R are saved in a dataframe format or tibble format . This will help your data analysis and also make easy the use of many libraries in R.
library(readr)# read cvs
library(readxl) #read xls
library(dplyr)
library(knitr) # web widget
library(tidyverse) # data manipulation
library(data.table) # fast file reading
library(kableExtra) # nice table html formating
library(gridExtra) # arranging ggplot in grid
library(caTools) # split
library(plotrix)
library(MASS)
Import data
Data used in this material are downloaded from this link: https://www.kaggle.com/sonujha090/bank-marketing
data_bank <- read.csv("~/ALDA DOC/ALDA 2020/Tirana bank-desktop/Tirana bank-trajnimi qershor Alda/Tirana Bank-R materials/Data _bank dataset/bank-full.csv", sep=";")
Looking at the dataset
View(data_bank)# view dataset
head(data_bank)# head of rows
summary(data_bank)# summary statistics
str(data_bank) # structure of dataset
nrow(data_bank)# number of rows
ncol(data_bank)# number of columns
If we want to code binary data
Lets try it in loan variable with categories (yes, no). We are going to use the ifelse() function.
data_bank$housing= ifelse(data_bank$housing=='yes',1,0)
head(data_bank$housing,10)
table(data_bank$housing)
Let’s work with our first format dataset. We will import it again here.
data_bank <- read.csv("~/ALDA DOC/ALDA 2020/Tirana bank-desktop/Tirana bank-trajnimi qershor Alda/Tirana Bank-R materials/Data _bank dataset/bank-full.csv", sep=";")
head(data_bank,10)
Check “missing values, NA”
sum(!complete.cases(data_bank)) # it will turn 0 because this dataset is cleaned from NA-s
Check for dublicates in rows
sum(duplicated(data_bank))# it will return the number of dublicated , here it will return 0 because we have cleaned the dublicate
Descriptive statistics
summary(data_bank)
summary(data_bank$age)# only for age variable
summary(data_bank[,3:6])# only for variables in columns 3 up to 6
Data Wrangling
filter()
filter() is a function in dplyr that takes logical expressions and returns the rows for which all are TRUE.
library(dplyr)
# filter individuals of age less than 25 years old
filter(data_bank, age < 25)
# filter individuals housing == yes, and age less than 25 years old
filter(data_bank, housing== "yes")
filter(data_bank, age < 25)
# individuals with profession management and age =30 years old
filter(data_bank, job == "management", age == 30)
# individuals with profession management and age less than 25 years old
filter(data_bank, job == "management", age < 25)
# we want to display in job the retired and management professions
filter(data_bank, job %in% c("retired", "management"))
Exercise a. What was the average age of “management” professionals housing “yes”.
Hint: you can do this in 2 steps by assigning a variable and then using the mean() function.
- What was the average balance of the loan for secondary education?
Solution. a
av.age <- filter(data_bank, job == "management", housing== "yes")
mean(av.age$age)
select()
We use select() to subset the data on variables or columns.
We can select multiple columns with a comma, after we specify the data frame (data_bank).
The logic:
select(df, A, B ,C): Select the variables A, B and C from df dataset. select(df, A:C) : Select all variables from A to C from df dataset. select(df, -C): Exclude C from the dataset from df dataset.
The error below it is likely that you are either using a package besides dplyr that also has a select() function or you just forgot to load the dplyr package with library(dplyr).
data_bank %>% select(education) # Error in select(., education) : unused argument (education)
We can use this syntax of obtaining the results with select()
data_bank %>% dplyr::select(age)
# or select 1st variable (age)
head(data_bank[,1])
data_bank %>% dplyr::select(age, marital, duration)
We can also use - (minus) to deselect columns. The code below will de-select from our dataset the variables age and marital.
data_bank %>% dplyr::select(-age, -marital)
We can use the pipe to chain those two operations together.
We want to filter for profession (job - variable) the “management” and from these individuals to show their “education” and “loan” variable.
Exercise How can you do it for individuals “marital” status to show their “age” and “balance”
data_bank.1 <- data_bank %>%
filter(job == "management") %>%
dplyr::select(education,loan)
data_bank.1
mutate() adds new variables to dataframe
Let suppose we want to add a variable combining two or more existing variables. Or we want to add a new variable from an existing vector which is not part of the dataframe.
Let’s suppose I want to see the balance for days of the duration period. (balance/duration).
Adding a new variable from two existing variables of the dataframe, named “day.balance”
data_bank %>%
mutate(day.balance = round(balance/duration,2))
I can add from another existing variable outside the dataset.
X<-rnorm(length(data_bank$age))
length(X)
data_bank %>% mutate(X)
group_by() operates on groups
summarize() will actually only keep the columns that are grouped_by or summarized.
ungroup() removes the grouping and it’s good to get in the habit of using it after a group_by().
data_bank %>%
filter(age == 30) %>%
group_by(marital) %>%
summarize(sum(duration)) %>%
ungroup()
arrange()
arrange() function is ordered alphabetically from A to Z.
# arrange by job
data_bank %>%
group_by(marital,job) %>%
summarize(mean(duration)) %>%
arrange(job)
# arrange by marital status
data_bank %>%
group_by(marital,job) %>%
summarize(mean(duration)) %>%
arrange(marital)
Try to combine by yourself the above functions.
Exercise What is the maximum duration for all jobs at age 30?
Exercise Try to understand what the below command will give you as an output?
library(tidyverse) ## install.packages('tidyverse')
## summarize
data_bank.2 <- data_bank %>%
dplyr::select(-contact, -poutcome) %>%
dplyr::group_by(marital) %>%
dplyr::mutate(day.balance = round(balance/duration,2)) %>%
dplyr::summarize(min_day.balance = min(day.balance)) %>%
dplyr::ungroup()
GRAPHICS
plot() function
Plot only one variable.
# not a good graphical presentation of age
plot(data_bank$age)
# a better view
plot(table(data_bank$age),ylab = "Frequency",xlab="Age",main="Age of bank data",col="red",lwd=5)
Exercise Try to add text (“Graphics in R”) to your second graph at coordinates age 68 and frequency 1400.
histograms hist()
Observe the argument breaks=. For more arguments of hist() see in ?histogram.
hist(data_bank$age, main="Histogram for age variable",xlab="age",ylab="freq",col="red")
hist(data_bank$age, main="Histogram for age variable",xlab="",ylab="",col="red",breaks = 5)
hist(data_bank$age, main="Histogram for age variable",xlab="",ylab="",col="red",breaks = 10)
hist(data_bank$age, main="Histogram for age variable",xlab="",ylab="",col="red",breaks = 15)
PIE Chart pie()
When applying the pie() we need to take care of the frequencies because R will automatically produce a pie chart for each individual (observation).
# not the appropriate way to obtain a pie chart
pie(data_bank$age)
# combined with the function table()
# for housing
pie(table(data_bank$housing),main="Housing or not",col=c("red","blue"))
# for marital variable
pie(table(data_bank$marital),main="Civil status",col=c("red","blue","yellow"))
# changing colors based on the categories of marital variable
pie(table(data_bank$marital),main="Civil status",col=rainbow(length(table(data_bank$marital))))
pie(table(data_bank$month),main="Month ",col=rainbow(length(table(data_bank$marital))))
pie(table(data_bank$job),main="Profession (job)",col=rainbow(length(table(data_bank$marital))))
# Add percentage to labels
# create a vector of percentage
perc<- round(100*(table(data_bank$marital)/length(data_bank$marital)))
perc
# create a vector of labels
label<-c("divorced","married","single")
# paste percentage and labels together
label<- paste(label, perc) # combine frequencies for each civil status
# add % to each label
label <- paste(label,"%",sep="")
# obtain the pie chart
pie(table(data_bank$marital),label,main="Civil status",col=rainbow(length(table(data_bank$marital))))
# 3D Exploded Pie Chart
library(plotrix)
pie3D(table(data_bank$marital),explode=0.1,main="Civil status",col=c("red","blue","yellow"))
# explode argument changes the dimension of distance between the parts , try it =0.5
Boxplot
May be created for only one variable and also for a group of characteristics in a numerical variable and a non-numerical variable.
boxplot(data_bank$age,col="green",main="Boxplot age",ylab="age")
boxplot(data_bank$balance,col="red",main="Boxplot balance",ylab="balance")
boxplot(data_bank$age~data_bank$marital,col=c("red","green","purple"),main="BoxPlot civil status",ylab="age")
boxplot(data_bank$duration~data_bank$marital,col=c("red","green","purple"),main="BoxPlot civil status",ylab="Duration",xlab="civil status")
Correlation plots
library(psych)
#
data_bank.1 <- data_bank %>% dplyr::select(duration, age, balance)
pairs(data_bank.1,col="red",main="Scatterplot for many variables")
#
# Calculate correlations between two variables
cor1=cor(data_bank$age,data_bank$balance)
cor1
cor2=cor(data_bank$age,data_bank$duration)
cor2
library(psych)#
pairs.panels(data_bank[,c(1,6,12)])
library(corrplot) and library(RColorBrewer)
library(corrplot)
library(RColorBrewer)
M <-cor(data_bank[,c(1,6,12)]) #select only numerical variables
corrplot(M, type="upper", order="hclust",col=brewer.pal(n=8, name="RdYlBu"))
library(ggplot2) and library(GGally)
library(ggplot2)
library(GGally)
# display a pair plot of all four columns of data
GGally::ggpairs(data_bank[,c(1,6,12)])
LS0tDQp0aXRsZTogIkRhdGEgU2N0cnVjdHVyZXMgYW5kIEdyYXBoaWNzIGluIFIiDQpzdWJ0aXRsZTogIiINCmF1dGhvcjogIkVyYWxkYSBHamlrYSINCmRhdGU6ICIxMSBKYW51YXJ5IDIwMjEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBEYXRhIHNjdHJ1Y3R1cmVzIGluIFINCk1vc3Qgb2YgdGhlIGRhdGEgaW4gUiBhcmUgc2F2ZWQgaW4gYSBkYXRhZnJhbWUgZm9ybWF0IG9yIHRpYmJsZSBmb3JtYXQgLiBUaGlzIHdpbGwgaGVscCB5b3VyIGRhdGEgYW5hbHlzaXMgYW5kIGFsc28gbWFrZSBlYXN5IHRoZSB1c2Ugb2YgbWFueSBsaWJyYXJpZXMgaW4gUi4NCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWRyKSMgcmVhZCBjdnMNCmxpYnJhcnkocmVhZHhsKSAjcmVhZCB4bHMNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGtuaXRyKSAgICAgICMgd2ViIHdpZGdldA0KbGlicmFyeSh0aWR5dmVyc2UpICAjIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KGRhdGEudGFibGUpICMgZmFzdCBmaWxlIHJlYWRpbmcNCmxpYnJhcnkoa2FibGVFeHRyYSkgIyBuaWNlIHRhYmxlIGh0bWwgZm9ybWF0aW5nIA0KbGlicmFyeShncmlkRXh0cmEpICAjIGFycmFuZ2luZyBnZ3Bsb3QgaW4gZ3JpZA0KbGlicmFyeShjYVRvb2xzKSAgICAjIHNwbGl0IA0KbGlicmFyeShwbG90cml4KQ0KbGlicmFyeShNQVNTKQ0KYGBgDQoNCkltcG9ydCBkYXRhDQoNCkRhdGEgdXNlZCBpbiB0aGlzIG1hdGVyaWFsIGFyZSBkb3dubG9hZGVkIGZyb20gdGhpcyBsaW5rOiBodHRwczovL3d3dy5rYWdnbGUuY29tL3NvbnVqaGEwOTAvYmFuay1tYXJrZXRpbmcNCg0KYGBge3J9DQpkYXRhX2JhbmsgPC0gcmVhZC5jc3YoIn4vQUxEQSBET0MvQUxEQSAyMDIwL1RpcmFuYSBiYW5rLWRlc2t0b3AvVGlyYW5hIGJhbmstdHJham5pbWkgcWVyc2hvciBBbGRhL1RpcmFuYSBCYW5rLVIgbWF0ZXJpYWxzL0RhdGEgX2JhbmsgZGF0YXNldC9iYW5rLWZ1bGwuY3N2Iiwgc2VwPSI7IikNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBkYXRhc2V0DQpgYGB7cn0NClZpZXcoZGF0YV9iYW5rKSMgdmlldyBkYXRhc2V0DQpoZWFkKGRhdGFfYmFuaykjIGhlYWQgb2Ygcm93cw0Kc3VtbWFyeShkYXRhX2JhbmspIyBzdW1tYXJ5IHN0YXRpc3RpY3MNCnN0cihkYXRhX2JhbmspICMgc3RydWN0dXJlIG9mIGRhdGFzZXQNCm5yb3coZGF0YV9iYW5rKSMgbnVtYmVyIG9mIHJvd3MNCm5jb2woZGF0YV9iYW5rKSMgbnVtYmVyIG9mIGNvbHVtbnMNCmBgYA0KDQojIyMgSWYgd2Ugd2FudCB0byBjb2RlIGJpbmFyeSBkYXRhIA0KDQpMZXRzIHRyeSBpdCBpbiBsb2FuIHZhcmlhYmxlIHdpdGggY2F0ZWdvcmllcyAoeWVzLCBubykuIFdlIGFyZSBnb2luZyB0byB1c2UgdGhlIGlmZWxzZSgpIGZ1bmN0aW9uLg0KYGBge3J9DQpkYXRhX2JhbmskaG91c2luZz0gaWZlbHNlKGRhdGFfYmFuayRob3VzaW5nPT0neWVzJywxLDApDQpoZWFkKGRhdGFfYmFuayRob3VzaW5nLDEwKQ0KdGFibGUoZGF0YV9iYW5rJGhvdXNpbmcpDQpgYGANCkxldCdzIHdvcmsgd2l0aCBvdXIgZmlyc3QgZm9ybWF0IGRhdGFzZXQuIFdlIHdpbGwgaW1wb3J0IGl0IGFnYWluIGhlcmUuDQoNCmBgYHtyfQ0KZGF0YV9iYW5rIDwtIHJlYWQuY3N2KCJ+L0FMREEgRE9DL0FMREEgMjAyMC9UaXJhbmEgYmFuay1kZXNrdG9wL1RpcmFuYSBiYW5rLXRyYWpuaW1pIHFlcnNob3IgQWxkYS9UaXJhbmEgQmFuay1SIG1hdGVyaWFscy9EYXRhIF9iYW5rIGRhdGFzZXQvYmFuay1mdWxsLmNzdiIsIHNlcD0iOyIpDQpoZWFkKGRhdGFfYmFuaywxMCkNCmBgYA0KDQojIyMgQ2hlY2sgIm1pc3NpbmcgdmFsdWVzLCBOQSINCmBgYHtyfQ0Kc3VtKCFjb21wbGV0ZS5jYXNlcyhkYXRhX2JhbmspKSAjIGl0IHdpbGwgdHVybiAwIGJlY2F1c2UgdGhpcyBkYXRhc2V0IGlzIGNsZWFuZWQgZnJvbSBOQS1zDQpgYGANCiMjIyBDaGVjayBmb3IgZHVibGljYXRlcyBpbiByb3dzIA0KYGBge3J9DQpzdW0oZHVwbGljYXRlZChkYXRhX2JhbmspKSMgaXQgd2lsbCByZXR1cm4gdGhlIG51bWJlciBvZiBkdWJsaWNhdGVkICwgaGVyZSBpdCB3aWxsIHJldHVybiAwIGJlY2F1c2Ugd2UgaGF2ZSBjbGVhbmVkIHRoZSBkdWJsaWNhdGUNCmBgYA0KDQojIyMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcw0KDQpgYGB7cn0NCnN1bW1hcnkoZGF0YV9iYW5rKQ0Kc3VtbWFyeShkYXRhX2JhbmskYWdlKSMgb25seSBmb3IgYWdlIHZhcmlhYmxlDQpzdW1tYXJ5KGRhdGFfYmFua1ssMzo2XSkjIG9ubHkgZm9yIHZhcmlhYmxlcyBpbiBjb2x1bW5zIDMgdXAgdG8gNg0KYGBgDQojIyMgRGF0YSBXcmFuZ2xpbmcgDQojIyMjIGZpbHRlcigpDQpmaWx0ZXIoKSBpcyBhIGZ1bmN0aW9uIGluIGRwbHlyIHRoYXQgdGFrZXMgbG9naWNhbCBleHByZXNzaW9ucyBhbmQgcmV0dXJucyB0aGUgcm93cyBmb3Igd2hpY2ggYWxsIGFyZSBUUlVFLg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KYGBge3J9DQojIGZpbHRlciBpbmRpdmlkdWFscyBvZiBhZ2UgbGVzcyB0aGFuIDI1IHllYXJzIG9sZA0KZmlsdGVyKGRhdGFfYmFuaywgYWdlIDwgMjUpIA0KDQojIGZpbHRlciBpbmRpdmlkdWFscyBob3VzaW5nID09IHllcywgYW5kIGFnZSBsZXNzIHRoYW4gMjUgeWVhcnMgb2xkDQpmaWx0ZXIoZGF0YV9iYW5rLCBob3VzaW5nPT0gInllcyIpDQpmaWx0ZXIoZGF0YV9iYW5rLCBhZ2UgPCAyNSkNCg0KIyBpbmRpdmlkdWFscyB3aXRoIHByb2Zlc3Npb24gbWFuYWdlbWVudCBhbmQgYWdlID0zMCB5ZWFycyBvbGQNCmZpbHRlcihkYXRhX2JhbmssIGpvYiA9PSAibWFuYWdlbWVudCIsIGFnZSA9PSAzMCkNCg0KIyBpbmRpdmlkdWFscyB3aXRoIHByb2Zlc3Npb24gbWFuYWdlbWVudCBhbmQgYWdlIGxlc3MgdGhhbiAyNSB5ZWFycyBvbGQgDQpmaWx0ZXIoZGF0YV9iYW5rLCBqb2IgPT0gIm1hbmFnZW1lbnQiLCBhZ2UgPCAyNSkNCg0KIyB3ZSB3YW50IHRvIGRpc3BsYXkgaW4gam9iIHRoZSByZXRpcmVkIGFuZCBtYW5hZ2VtZW50IHByb2Zlc3Npb25zDQpmaWx0ZXIoZGF0YV9iYW5rLCBqb2IgJWluJSBjKCJyZXRpcmVkIiwgIm1hbmFnZW1lbnQiKSkgDQpgYGANCkV4ZXJjaXNlDQphLiBXaGF0IHdhcyB0aGUgYXZlcmFnZSBhZ2Ugb2YgIm1hbmFnZW1lbnQiIHByb2Zlc3Npb25hbHMgaG91c2luZyAieWVzIi4NCg0KSGludDogeW91IGNhbiBkbyB0aGlzIGluIDIgc3RlcHMgYnkgYXNzaWduaW5nIGEgdmFyaWFibGUgYW5kIHRoZW4gdXNpbmcgdGhlIG1lYW4oKSBmdW5jdGlvbi4NCg0KYi4gV2hhdCB3YXMgdGhlIGF2ZXJhZ2UgYmFsYW5jZSBvZiB0aGUgbG9hbiBmb3Igc2Vjb25kYXJ5IGVkdWNhdGlvbj8gIA0KDQpTb2x1dGlvbi4gYQ0KYGBge3J9DQphdi5hZ2UgPC0gZmlsdGVyKGRhdGFfYmFuaywgam9iID09ICJtYW5hZ2VtZW50IiwgaG91c2luZz09ICJ5ZXMiKSAgDQptZWFuKGF2LmFnZSRhZ2UpICANCmBgYA0KDQojIyMgc2VsZWN0KCkgDQoNCldlIHVzZSBzZWxlY3QoKSB0byBzdWJzZXQgdGhlIGRhdGEgb24gdmFyaWFibGVzIG9yIGNvbHVtbnMuDQoNCldlIGNhbiBzZWxlY3QgbXVsdGlwbGUgY29sdW1ucyB3aXRoIGEgY29tbWEsIGFmdGVyIHdlIHNwZWNpZnkgdGhlIGRhdGEgZnJhbWUgKGRhdGFfYmFuaykuDQoNClRoZSBsb2dpYzoNCg0KICAgc2VsZWN0KGRmLCBBLCBCICxDKTogU2VsZWN0IHRoZSB2YXJpYWJsZXMgQSwgQiBhbmQgQyBmcm9tIGRmIGRhdGFzZXQuDQogICBzZWxlY3QoZGYsIEE6QykgOiBTZWxlY3QgYWxsIHZhcmlhYmxlcyBmcm9tIEEgdG8gQyBmcm9tIGRmIGRhdGFzZXQuDQogICBzZWxlY3QoZGYsIC1DKTogRXhjbHVkZSBDIGZyb20gdGhlIGRhdGFzZXQgZnJvbSBkZiBkYXRhc2V0LgkNCg0KVGhlIGVycm9yIGJlbG93IGl0IGlzIGxpa2VseSB0aGF0IHlvdSBhcmUgZWl0aGVyIHVzaW5nIGEgcGFja2FnZSBiZXNpZGVzIGRwbHlyIHRoYXQgYWxzbyBoYXMgYSBzZWxlY3QoKSBmdW5jdGlvbiBvciB5b3UganVzdCBmb3Jnb3QgdG8gbG9hZCB0aGUgZHBseXIgcGFja2FnZSB3aXRoIGxpYnJhcnkoZHBseXIpLg0KDQpgYGB7cn0NCmRhdGFfYmFuayAlPiUgc2VsZWN0KGVkdWNhdGlvbikgIyBFcnJvciBpbiBzZWxlY3QoLiwgZWR1Y2F0aW9uKSA6IHVudXNlZCBhcmd1bWVudCAoZWR1Y2F0aW9uKQ0KDQpgYGANCldlIGNhbiB1c2UgdGhpcyBzeW50YXggb2Ygb2J0YWluaW5nIHRoZSByZXN1bHRzIHdpdGggc2VsZWN0KCkNCmBgYHtyfQ0KZGF0YV9iYW5rICU+JSBkcGx5cjo6c2VsZWN0KGFnZSkNCg0KIyBvciBzZWxlY3QgMXN0IHZhcmlhYmxlIChhZ2UpDQpoZWFkKGRhdGFfYmFua1ssMV0pIA0KDQpkYXRhX2JhbmsgJT4lIGRwbHlyOjpzZWxlY3QoYWdlLCBtYXJpdGFsLCBkdXJhdGlvbikgDQpgYGANCiANCldlIGNhbiBhbHNvIHVzZSAtIChtaW51cykgdG8gZGVzZWxlY3QgY29sdW1ucy4gDQpUaGUgY29kZSBiZWxvdyB3aWxsIGRlLXNlbGVjdCBmcm9tIG91ciBkYXRhc2V0IHRoZSB2YXJpYWJsZXMgYWdlIGFuZCBtYXJpdGFsLg0KDQpgYGB7cn0NCmRhdGFfYmFuayAlPiUgZHBseXI6OnNlbGVjdCgtYWdlLCAtbWFyaXRhbCkgDQpgYGANCg0KIyMjIyBXZSBjYW4gdXNlIHRoZSBwaXBlIHRvIGNoYWluIHRob3NlIHR3byBvcGVyYXRpb25zIHRvZ2V0aGVyLg0KDQpXZSB3YW50IHRvIGZpbHRlciBmb3IgcHJvZmVzc2lvbiAoam9iIC0gdmFyaWFibGUpIHRoZSAibWFuYWdlbWVudCIgYW5kIGZyb20gdGhlc2UgaW5kaXZpZHVhbHMgdG8gc2hvdyB0aGVpciAiZWR1Y2F0aW9uIiBhbmQgImxvYW4iIHZhcmlhYmxlLg0KDQpFeGVyY2lzZQ0KSG93IGNhbiB5b3UgZG8gaXQgZm9yIGluZGl2aWR1YWxzICJtYXJpdGFsIiBzdGF0dXMgdG8gc2hvdyB0aGVpciAiYWdlIiBhbmQgImJhbGFuY2UiDQoNCmBgYHtyfQ0KZGF0YV9iYW5rLjEgPC0gZGF0YV9iYW5rICU+JSANCiAgZmlsdGVyKGpvYiA9PSAibWFuYWdlbWVudCIpICU+JQ0KIGRwbHlyOjpzZWxlY3QoZWR1Y2F0aW9uLGxvYW4pIA0KDQpkYXRhX2JhbmsuMQ0KYGBgDQoNCiMjIyBtdXRhdGUoKSBhZGRzIG5ldyB2YXJpYWJsZXMgdG8gZGF0YWZyYW1lDQoNCkxldCBzdXBwb3NlIHdlIHdhbnQgdG8gYWRkIGEgdmFyaWFibGUgY29tYmluaW5nIHR3byBvciBtb3JlIGV4aXN0aW5nIHZhcmlhYmxlcy4gT3Igd2Ugd2FudCB0byBhZGQgYSBuZXcgdmFyaWFibGUgZnJvbSBhbiBleGlzdGluZyB2ZWN0b3Igd2hpY2ggaXMgbm90IHBhcnQgb2YgdGhlIGRhdGFmcmFtZS4NCg0KTGV0J3Mgc3VwcG9zZSBJIHdhbnQgdG8gc2VlIHRoZSBiYWxhbmNlIGZvciBkYXlzIG9mIHRoZSBkdXJhdGlvbiBwZXJpb2QuIChiYWxhbmNlL2R1cmF0aW9uKS4NCg0KQWRkaW5nIGEgbmV3IHZhcmlhYmxlIGZyb20gdHdvIGV4aXN0aW5nIHZhcmlhYmxlcyBvZiB0aGUgZGF0YWZyYW1lLCBuYW1lZCAiZGF5LmJhbGFuY2UiDQpgYGB7cn0NCmRhdGFfYmFuayAlPiUNCiAgbXV0YXRlKGRheS5iYWxhbmNlID0gcm91bmQoYmFsYW5jZS9kdXJhdGlvbiwyKSkNCmBgYA0KDQpJIGNhbiBhZGQgZnJvbSBhbm90aGVyIGV4aXN0aW5nIHZhcmlhYmxlIG91dHNpZGUgdGhlIGRhdGFzZXQuDQpgYGB7cn0NClg8LXJub3JtKGxlbmd0aChkYXRhX2JhbmskYWdlKSkNCmxlbmd0aChYKQ0KZGF0YV9iYW5rICU+JSBtdXRhdGUoWCkNCmBgYA0KDQojIyMgZ3JvdXBfYnkoKSBvcGVyYXRlcyBvbiBncm91cHMNCg0Kc3VtbWFyaXplKCkgd2lsbCBhY3R1YWxseSBvbmx5IGtlZXAgdGhlIGNvbHVtbnMgdGhhdCBhcmUgZ3JvdXBlZF9ieSBvciBzdW1tYXJpemVkLiANCg0KdW5ncm91cCgpIHJlbW92ZXMgdGhlIGdyb3VwaW5nIGFuZCBpdOKAmXMgZ29vZCB0byBnZXQgaW4gdGhlIGhhYml0IG9mIHVzaW5nIGl0IGFmdGVyIGEgZ3JvdXBfYnkoKS4NCmBgYHtyfQ0KZGF0YV9iYW5rICU+JQ0KICBmaWx0ZXIoYWdlID09IDMwKSAlPiUNCiAgZ3JvdXBfYnkobWFyaXRhbCkgJT4lDQogIHN1bW1hcml6ZShzdW0oZHVyYXRpb24pKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KIyMjIGFycmFuZ2UoKQ0KDQphcnJhbmdlKCkgZnVuY3Rpb24gaXMgb3JkZXJlZCBhbHBoYWJldGljYWxseSBmcm9tIEEgdG8gWi4NCg0KYGBge3J9DQojIGFycmFuZ2UgYnkgam9iDQpkYXRhX2JhbmsgJT4lDQogICBncm91cF9ieShtYXJpdGFsLGpvYikgJT4lDQogIHN1bW1hcml6ZShtZWFuKGR1cmF0aW9uKSkgJT4lDQogIGFycmFuZ2Uoam9iKQ0KDQojIGFycmFuZ2UgYnkgbWFyaXRhbCBzdGF0dXMNCiBkYXRhX2JhbmsgJT4lDQogICBncm91cF9ieShtYXJpdGFsLGpvYikgJT4lDQogIHN1bW1hcml6ZShtZWFuKGR1cmF0aW9uKSkgJT4lDQogIGFycmFuZ2UobWFyaXRhbCkgDQpgYGANClRyeSB0byBjb21iaW5lIGJ5IHlvdXJzZWxmIHRoZSBhYm92ZSBmdW5jdGlvbnMuIA0KDQpFeGVyY2lzZQ0KV2hhdCBpcyB0aGUgbWF4aW11bSBkdXJhdGlvbiBmb3IgYWxsIGpvYnMgYXQgYWdlIDMwPw0KDQpFeGVyY2lzZQ0KVHJ5IHRvIHVuZGVyc3RhbmQgd2hhdCB0aGUgYmVsb3cgY29tbWFuZCB3aWxsIGdpdmUgeW91IGFzIGFuIG91dHB1dD8NCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpICMjIGluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpDQojIyBzdW1tYXJpemUNCmRhdGFfYmFuay4yIDwtIGRhdGFfYmFuayAlPiUgDQogIGRwbHlyOjpzZWxlY3QoLWNvbnRhY3QsIC1wb3V0Y29tZSkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkobWFyaXRhbCkgJT4lDQogIGRwbHlyOjptdXRhdGUoZGF5LmJhbGFuY2UgPSByb3VuZChiYWxhbmNlL2R1cmF0aW9uLDIpKSAlPiUNCiAgZHBseXI6OnN1bW1hcml6ZShtaW5fZGF5LmJhbGFuY2UgPSBtaW4oZGF5LmJhbGFuY2UpKSAlPiUNCiAgZHBseXI6OnVuZ3JvdXAoKSANCg0KYGBgDQoNCiMgR1JBUEhJQ1MgDQoNCiMjIyBwbG90KCkgZnVuY3Rpb24NCg0KUGxvdCBvbmx5IG9uZSB2YXJpYWJsZS4NCmBgYHtyfQ0KIyBub3QgYSBnb29kIGdyYXBoaWNhbCBwcmVzZW50YXRpb24gb2YgYWdlDQpwbG90KGRhdGFfYmFuayRhZ2UpIA0KIyBhIGJldHRlciB2aWV3IA0KcGxvdCh0YWJsZShkYXRhX2JhbmskYWdlKSx5bGFiID0gIkZyZXF1ZW5jeSIseGxhYj0iQWdlIixtYWluPSJBZ2Ugb2YgYmFuayBkYXRhIixjb2w9InJlZCIsbHdkPTUpIA0KYGBgDQogRXhlcmNpc2UNCiBUcnkgdG8gYWRkIHRleHQgKCJHcmFwaGljcyBpbiBSIikgdG8geW91ciBzZWNvbmQgZ3JhcGggYXQgY29vcmRpbmF0ZXMgYWdlIDY4IGFuZCBmcmVxdWVuY3kgMTQwMC4NCg0KIyMjIGhpc3RvZ3JhbXMgaGlzdCgpDQoNCk9ic2VydmUgdGhlIGFyZ3VtZW50IGJyZWFrcz0uIEZvciBtb3JlIGFyZ3VtZW50cyBvZiBoaXN0KCkgc2VlIGluID9oaXN0b2dyYW0uDQpgYGB7cn0NCmhpc3QoZGF0YV9iYW5rJGFnZSwgbWFpbj0iSGlzdG9ncmFtIGZvciBhZ2UgdmFyaWFibGUiLHhsYWI9ImFnZSIseWxhYj0iZnJlcSIsY29sPSJyZWQiKQ0KaGlzdChkYXRhX2JhbmskYWdlLCBtYWluPSJIaXN0b2dyYW0gZm9yIGFnZSB2YXJpYWJsZSIseGxhYj0iIix5bGFiPSIiLGNvbD0icmVkIixicmVha3MgPSA1KQ0KaGlzdChkYXRhX2JhbmskYWdlLCBtYWluPSJIaXN0b2dyYW0gZm9yIGFnZSB2YXJpYWJsZSIseGxhYj0iIix5bGFiPSIiLGNvbD0icmVkIixicmVha3MgPSAxMCkNCmhpc3QoZGF0YV9iYW5rJGFnZSwgbWFpbj0iSGlzdG9ncmFtIGZvciBhZ2UgdmFyaWFibGUiLHhsYWI9IiIseWxhYj0iIixjb2w9InJlZCIsYnJlYWtzID0gMTUpDQoNCmBgYA0KDQojIyMgUElFIENoYXJ0ICBwaWUoKQ0KV2hlbiBhcHBseWluZyB0aGUgcGllKCkgd2UgbmVlZCB0byB0YWtlIGNhcmUgb2YgdGhlIGZyZXF1ZW5jaWVzIGJlY2F1c2UgUiB3aWxsIGF1dG9tYXRpY2FsbHkgcHJvZHVjZSBhIHBpZSBjaGFydCBmb3IgZWFjaCBpbmRpdmlkdWFsIChvYnNlcnZhdGlvbikuDQoNCmBgYHtyfQ0KIyBub3QgdGhlIGFwcHJvcHJpYXRlIHdheSB0byBvYnRhaW4gYSBwaWUgY2hhcnQNCnBpZShkYXRhX2JhbmskYWdlKQ0KDQojIGNvbWJpbmVkIHdpdGggdGhlIGZ1bmN0aW9uIHRhYmxlKCkgDQojIGZvciBob3VzaW5nIA0KcGllKHRhYmxlKGRhdGFfYmFuayRob3VzaW5nKSxtYWluPSJIb3VzaW5nIG9yIG5vdCIsY29sPWMoInJlZCIsImJsdWUiKSkNCg0KIyBmb3IgbWFyaXRhbCB2YXJpYWJsZQ0KcGllKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKSxtYWluPSJDaXZpbCBzdGF0dXMiLGNvbD1jKCJyZWQiLCJibHVlIiwieWVsbG93IikpDQoNCiMgY2hhbmdpbmcgY29sb3JzIGJhc2VkIG9uIHRoZSBjYXRlZ29yaWVzIG9mIG1hcml0YWwgdmFyaWFibGUNCnBpZSh0YWJsZShkYXRhX2JhbmskbWFyaXRhbCksbWFpbj0iQ2l2aWwgc3RhdHVzIixjb2w9cmFpbmJvdyhsZW5ndGgodGFibGUoZGF0YV9iYW5rJG1hcml0YWwpKSkpDQoNCnBpZSh0YWJsZShkYXRhX2JhbmskbW9udGgpLG1haW49Ik1vbnRoICIsY29sPXJhaW5ib3cobGVuZ3RoKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKSkpKQ0KDQpwaWUodGFibGUoZGF0YV9iYW5rJGpvYiksbWFpbj0iUHJvZmVzc2lvbiAoam9iKSIsY29sPXJhaW5ib3cobGVuZ3RoKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKSkpKQ0KDQojIEFkZCBwZXJjZW50YWdlIHRvIGxhYmVscw0KIyBjcmVhdGUgYSB2ZWN0b3Igb2YgcGVyY2VudGFnZSANCnBlcmM8LSByb3VuZCgxMDAqKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKS9sZW5ndGgoZGF0YV9iYW5rJG1hcml0YWwpKSkNCnBlcmMNCiMgY3JlYXRlIGEgdmVjdG9yIG9mIGxhYmVscw0KbGFiZWw8LWMoImRpdm9yY2VkIiwibWFycmllZCIsInNpbmdsZSIpDQoNCiMgcGFzdGUgcGVyY2VudGFnZSBhbmQgbGFiZWxzIHRvZ2V0aGVyDQpsYWJlbDwtIHBhc3RlKGxhYmVsLCBwZXJjKSAjIGNvbWJpbmUgZnJlcXVlbmNpZXMgZm9yIGVhY2ggY2l2aWwgc3RhdHVzDQogIyBhZGQgICUgdG8gZWFjaCBsYWJlbA0KbGFiZWwgPC0gcGFzdGUobGFiZWwsIiUiLHNlcD0iIikNCiMgb2J0YWluIHRoZSBwaWUgY2hhcnQNCnBpZSh0YWJsZShkYXRhX2JhbmskbWFyaXRhbCksbGFiZWwsbWFpbj0iQ2l2aWwgc3RhdHVzIixjb2w9cmFpbmJvdyhsZW5ndGgodGFibGUoZGF0YV9iYW5rJG1hcml0YWwpKSkpDQoNCiMgM0QgRXhwbG9kZWQgUGllIENoYXJ0DQpsaWJyYXJ5KHBsb3RyaXgpDQpwaWUzRCh0YWJsZShkYXRhX2JhbmskbWFyaXRhbCksZXhwbG9kZT0wLjEsbWFpbj0iQ2l2aWwgc3RhdHVzIixjb2w9YygicmVkIiwiYmx1ZSIsInllbGxvdyIpKQ0KIyBleHBsb2RlIGFyZ3VtZW50IGNoYW5nZXMgdGhlIGRpbWVuc2lvbiBvZiBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBwYXJ0cyAsIHRyeSBpdCA9MC41DQpgYGANCg0KIyMjIEJveHBsb3QNCg0KTWF5IGJlIGNyZWF0ZWQgZm9yIG9ubHkgb25lIHZhcmlhYmxlIGFuZCBhbHNvIGZvciBhIGdyb3VwIG9mIGNoYXJhY3RlcmlzdGljcyBpbiBhIG51bWVyaWNhbCB2YXJpYWJsZSBhbmQgYSBub24tbnVtZXJpY2FsIHZhcmlhYmxlLg0KYGBge3J9DQpib3hwbG90KGRhdGFfYmFuayRhZ2UsY29sPSJncmVlbiIsbWFpbj0iQm94cGxvdCBhZ2UiLHlsYWI9ImFnZSIpDQoNCmJveHBsb3QoZGF0YV9iYW5rJGJhbGFuY2UsY29sPSJyZWQiLG1haW49IkJveHBsb3QgYmFsYW5jZSIseWxhYj0iYmFsYW5jZSIpDQoNCmJveHBsb3QoZGF0YV9iYW5rJGFnZX5kYXRhX2JhbmskbWFyaXRhbCxjb2w9YygicmVkIiwiZ3JlZW4iLCJwdXJwbGUiKSxtYWluPSJCb3hQbG90IGNpdmlsIHN0YXR1cyIseWxhYj0iYWdlIikNCg0KYm94cGxvdChkYXRhX2JhbmskZHVyYXRpb25+ZGF0YV9iYW5rJG1hcml0YWwsY29sPWMoInJlZCIsImdyZWVuIiwicHVycGxlIiksbWFpbj0iQm94UGxvdCBjaXZpbCBzdGF0dXMiLHlsYWI9IkR1cmF0aW9uIix4bGFiPSJjaXZpbCBzdGF0dXMiKQ0KYGBgDQoNCiMjIyBDb3JyZWxhdGlvbiBwbG90cw0KDQojIyMjIGxpYnJhcnkocHN5Y2gpDQpgYGB7cn0NCiMNCmRhdGFfYmFuay4xIDwtIGRhdGFfYmFuayAlPiUgZHBseXI6OnNlbGVjdChkdXJhdGlvbiwgYWdlLCBiYWxhbmNlKQ0KcGFpcnMoZGF0YV9iYW5rLjEsY29sPSJyZWQiLG1haW49IlNjYXR0ZXJwbG90IGZvciBtYW55IHZhcmlhYmxlcyIpDQojIA0KIyBDYWxjdWxhdGUgY29ycmVsYXRpb25zIGJldHdlZW4gdHdvIHZhcmlhYmxlcw0KY29yMT1jb3IoZGF0YV9iYW5rJGFnZSxkYXRhX2JhbmskYmFsYW5jZSkNCmNvcjENCmNvcjI9Y29yKGRhdGFfYmFuayRhZ2UsZGF0YV9iYW5rJGR1cmF0aW9uKQ0KY29yMg0KbGlicmFyeShwc3ljaCkjICANCnBhaXJzLnBhbmVscyhkYXRhX2JhbmtbLGMoMSw2LDEyKV0pDQpgYGANCg0KIyMjIyBsaWJyYXJ5KGNvcnJwbG90KSBhbmQgbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KQW5vdGhlciBsaWJyYXJ5IGZvciBjb3JyZWxhdGlvbiBwbG90Lg0KYGBge3J9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KY29yLm1hdD1jb3IoZGF0YV9iYW5rWyxjKDEsNiwxMildKSMgY29ycmVsYXRpb24gbWF0cml4IA0KY29yLm1hdA0KY2hhcnQuQ29ycmVsYXRpb24oZGF0YV9iYW5rWyxjKDEsNiwxMildLGhpc3RvZ3JhbT1UUlVFLCBwY2g9MTkpDQoNCmBgYA0KDQojIyMjIGxpYnJhcnkoY29ycnBsb3QpIGFuZCBsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmBgYHtyfQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KTSA8LWNvcihkYXRhX2JhbmtbLGMoMSw2LDEyKV0pICNzZWxlY3Qgb25seSBudW1lcmljYWwgdmFyaWFibGVzDQpjb3JycGxvdChNLCB0eXBlPSJ1cHBlciIsIG9yZGVyPSJoY2x1c3QiLGNvbD1icmV3ZXIucGFsKG49OCwgbmFtZT0iUmRZbEJ1IikpDQpgYGANCg0KIyMjIyBsaWJyYXJ5KGdncGxvdDIpIGFuZCBsaWJyYXJ5KEdHYWxseSkNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShHR2FsbHkpDQoNCiMgZGlzcGxheSBhIHBhaXIgcGxvdCBvZiBhbGwgZm91ciBjb2x1bW5zIG9mIGRhdGENCkdHYWxseTo6Z2dwYWlycyhkYXRhX2JhbmtbLGMoMSw2LDEyKV0pDQpgYGANCg0KIyMjIEV4ZXJjaXNlDQpUcnkgdG8gd29yayBvbiB0aGlzIGJhbmsgZGF0YXNldHM6DQoNCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vaXRzbWVzdW5pbC9iYW5rLWxvYW4tbW9kZWxsaW5nDQoNCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vc2FudG9zaGQzL2JhbmstY3VzdG9tZXJzDQoNCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vemF1cmJlZ2lldi9teS1kYXRhc2V0P3NlbGVjdD1jcmVkaXRfdHJhaW4uY3N2DQoNCg0KIyBFU1FVSVNTRSBsaWJyYXJ5DQoNCiMjIEEgZ3JhcGhpY2FsIGFkZC1pbiBpbiBSLVN0dWRpbw0KDQpUaGUgcHVycG9zZSBvZiB0aGlzIGFkZC1pbiBpcyB0byBsZXQgeW91IGV4cGxvcmUgeW91ciBkYXRhIHF1aWNrbHkgdG8gZXh0cmFjdCB0aGUgaW5mb3JtYXRpb24gdGhleSBob2xkLiBZb3UgY2FuIGNyZWF0ZSB2aXN1YWxpemF0aW9uIHdpdGgge2dncGxvdDJ9LCBmaWx0ZXIgZGF0YSB3aXRoIHtkcGx5cn0gYW5kIHJldHJpZXZlIGdlbmVyYXRlZCBjb2RlLg0KDQpUbyBydW4gRXNxdWlzc2UgaW4gUiBzdHVkaW8gY29weSBhbmQgcGFzdGUgdGhlIGZvbGxvd2luZyBjb2RlIChkb24ndCBmb3JnZXQgdG8gcHJldmlvdXNseSBpbnN0YWxsICJlc3F1aXNzZSIpDQoNCiMjIyBTdGFydCAuLi4NCg0KbGlicmFyeShlc3F1aXNzZSkNCmVzcXVpc3NlOjplc3F1aXNzZXIoKQ0KDQojIyMjIHRvIGRpc3BsYXkgZXNxdWlzc2VyIHlvdSBjYW4gdHJ5IG9uZSBvZiB0aGUgdmlld2VyIG9wdGlvbnMgYmVsb3cgdG8gbGF1bmNoIGluIHlvdXIgYnJvd3NlciBvciBwYW5lDQoNCiBlc3F1aXNzZTo6ZXNxdWlzc2VyKGRhdGEsIHZpZXdlciA9ICJicm93c2VyIikNCg0KIyMjIC4uLi4gRW5kDQoNCmBgYHtyfQ0KbGlicmFyeShlc3F1aXNzZSkNCmJhbmsgPC0gcmVhZC5jc3YoIn4vQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3QvRGlkYWN0aWMgbWF0ZXJpYWxzIENyeXN0YWwgU29sdXRpb24vREFUQVNFVC1DcnlzdGFsL2JhbmsuY3N2IikNCiMgdG8gZGlzcGxheSBlc3F1aXNzZXIgeW91IGNhbiB0cnkgb25lIG9mIHRoZSB2aWV3ZXIgb3B0aW9ucyBiZWxvdw0KZXNxdWlzc2U6OmVzcXVpc3Nlcih2aWV3ZXIgPSAiYnJvd3NlciIpDQojIG9yDQplc3F1aXNzZTo6ZXNxdWlzc2VyKHZpZXdlciA9ICJwYW5lIikNCmBgYA0KDQoNCiMjIyBTZWxlY3QgZGF0YQ0KSW1wb3J0IGFueSBkYXRhIHlvdSBoYXZlIGZyb20geW91ciBzZXNzaW9uIGluIFINCg0KIVtBbHQgdGV4dF0oQzpcVXNlcnNcdXNlclxEb2N1bWVudHNcQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3RcZm90b1xlc3F1aXNzZTAucG5nKQ0KDQojIyMgQ3JlYXRlIGEgcGxvdA0KU3RhcnQgY3JlYXRlIGEgcGxvdCBieSBkcmFnIGFueSB2YXJpYWJsZSBpbiB0aGUgd2luZG93cyAoWCwgWSkgYW5kIG90aGVyIGdyb3VwaW5nIHdpbmRvd3MuDQoNCiFbQWx0IHRleHRdKEM6XFVzZXJzXHVzZXJcRG9jdW1lbnRzXENSWVNUQUwgU3lzdGVtLURhdGEgU2NpZW5jZSBwcm9qZWN0XGZvdG9cZXNxdWlzc2UxLnBuZykNCg0KIyMjIExhYmVscyBhbmQgVGl0bGVzDQoNCkFkZCBUaXRsZSwgc3VidGl0bGUgYW5kIGF4aXMgbmFtZS4NCg0KIVtBbHQgdGV4dF0oQzpcVXNlcnNcdXNlclxEb2N1bWVudHNcQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3RcZm90b1xlc3F1aXNzZTIucG5nKQ0KIyMjIFBsb3QgT3B0aW9ucw0KTW9kaWZ5IHRoZSBwbG90IG9wdGlvbnMNCg0KIVtBbHQgdGV4dF0oQzpcVXNlcnNcdXNlclxEb2N1bWVudHNcQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3RcZm90b1xlc3F1aXNzZTMucG5nKQ0KDQojIyMgRGF0YQ0KTW9kaWZ5IGRhdGEgZnJvbSB0aGUgZGF0YXNldA0KDQohW0FsdCB0ZXh0XShDOlxVc2Vyc1x1c2VyXERvY3VtZW50c1xDUllTVEFMIFN5c3RlbS1EYXRhIFNjaWVuY2UgcHJvamVjdFxmb3RvXGVzcXVpc3NlNC5wbmcpDQoNCiMjIyBFeHBvcnQgYW5kIENvZGUNCg0KRXhwb3J0IHRoZSBjb2RlIGluIFItY29uc29sZSBhbmQgbW9kaWZ5L3VzZSBpdCBieSBjaGFuZ2luZyBhcmd1bWVudHMuDQoNCiFbQWx0IHRleHRdKEM6XFVzZXJzXHVzZXJcRG9jdW1lbnRzXENSWVNUQUwgU3lzdGVtLURhdGEgU2NpZW5jZSBwcm9qZWN0XGZvdG9cZXNxdWlzc2U1LnBuZykNCg0KDQpGb3IgbW9yZSBvbiBFc3F1aXNzZSBzZWU6DQoNCmh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9lc3F1aXNzZS9yZWFkbWUvUkVBRE1FLmh0bWwNCg0KaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2VzcXVpc3NlL3ZpZ25ldHRlcy9nZXQtc3RhcnRlZC5odG1sDQoNCiMjIyAxMSBKYW51YXJ5IDIwMjENCiMjIyMgRXJhbGRhIEdqaWthIChEaGFtbykNCg0KDQoNCg==