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.

  1. 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(“PerformanceAnalytics”)

Another library for correlation plot.

library(corrplot)
library("PerformanceAnalytics")
cor.mat=cor(data_bank[,c(1,6,12)])# correlation matrix 
cor.mat
chart.Correlation(data_bank[,c(1,6,12)],histogram=TRUE, pch=19)

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)])

ESQUISSE library

A graphical add-in in R-Studio

The purpose of this add-in is to let you explore your data quickly to extract the information they hold. You can create visualization with {ggplot2}, filter data with {dplyr} and retrieve generated code.

To run Esquisse in R studio copy and paste the following code (don’t forget to previously install “esquisse”)

Start …

library(esquisse) esquisse::esquisser()

to display esquisser you can try one of the viewer options below to launch in your browser or pane

esquisse::esquisser(data, viewer = “browser”)

…. End

library(esquisse)
bank <- read.csv("~/CRYSTAL System-Data Science project/Didactic materials Crystal Solution/DATASET-Crystal/bank.csv")
# to display esquisser you can try one of the viewer options below
esquisse::esquisser(viewer = "browser")
# or
esquisse::esquisser(viewer = "pane")

Select data

Import any data you have from your session in R

Alt text

Create a plot

Start create a plot by drag any variable in the windows (X, Y) and other grouping windows.

Alt text

Labels and Titles

Add Title, subtitle and axis name.

Alt text ### Plot Options Modify the plot options

Alt text

Data

Modify data from the dataset

Alt text

Export and Code

Export the code in R-console and modify/use it by changing arguments.

Alt text

For more on Esquisse see:

https://cran.r-project.org/web/packages/esquisse/readme/README.html

https://cran.r-project.org/web/packages/esquisse/vignettes/get-started.html

11 January 2021

Eralda Gjika (Dhamo)

LS0tDQp0aXRsZTogIkRhdGEgU2N0cnVjdHVyZXMgYW5kIEdyYXBoaWNzIGluIFIiDQpzdWJ0aXRsZTogIiINCmF1dGhvcjogIkVyYWxkYSBHamlrYSINCmRhdGU6ICIxMSBKYW51YXJ5IDIwMjEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBEYXRhIHNjdHJ1Y3R1cmVzIGluIFINCk1vc3Qgb2YgdGhlIGRhdGEgaW4gUiBhcmUgc2F2ZWQgaW4gYSBkYXRhZnJhbWUgZm9ybWF0IG9yIHRpYmJsZSBmb3JtYXQgLiBUaGlzIHdpbGwgaGVscCB5b3VyIGRhdGEgYW5hbHlzaXMgYW5kIGFsc28gbWFrZSBlYXN5IHRoZSB1c2Ugb2YgbWFueSBsaWJyYXJpZXMgaW4gUi4NCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWRyKSMgcmVhZCBjdnMNCmxpYnJhcnkocmVhZHhsKSAjcmVhZCB4bHMNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGtuaXRyKSAgICAgICMgd2ViIHdpZGdldA0KbGlicmFyeSh0aWR5dmVyc2UpICAjIGRhdGEgbWFuaXB1bGF0aW9uDQpsaWJyYXJ5KGRhdGEudGFibGUpICMgZmFzdCBmaWxlIHJlYWRpbmcNCmxpYnJhcnkoa2FibGVFeHRyYSkgIyBuaWNlIHRhYmxlIGh0bWwgZm9ybWF0aW5nIA0KbGlicmFyeShncmlkRXh0cmEpICAjIGFycmFuZ2luZyBnZ3Bsb3QgaW4gZ3JpZA0KbGlicmFyeShjYVRvb2xzKSAgICAjIHNwbGl0IA0KbGlicmFyeShwbG90cml4KQ0KbGlicmFyeShNQVNTKQ0KYGBgDQoNCkltcG9ydCBkYXRhDQoNCkRhdGEgdXNlZCBpbiB0aGlzIG1hdGVyaWFsIGFyZSBkb3dubG9hZGVkIGZyb20gdGhpcyBsaW5rOiBodHRwczovL3d3dy5rYWdnbGUuY29tL3NvbnVqaGEwOTAvYmFuay1tYXJrZXRpbmcNCg0KYGBge3J9DQpkYXRhX2JhbmsgPC0gcmVhZC5jc3YoIn4vQUxEQSBET0MvQUxEQSAyMDIwL1RpcmFuYSBiYW5rLWRlc2t0b3AvVGlyYW5hIGJhbmstdHJham5pbWkgcWVyc2hvciBBbGRhL1RpcmFuYSBCYW5rLVIgbWF0ZXJpYWxzL0RhdGEgX2JhbmsgZGF0YXNldC9iYW5rLWZ1bGwuY3N2Iiwgc2VwPSI7IikNCmBgYA0KDQpMb29raW5nIGF0IHRoZSBkYXRhc2V0DQpgYGB7cn0NClZpZXcoZGF0YV9iYW5rKSMgdmlldyBkYXRhc2V0DQpoZWFkKGRhdGFfYmFuaykjIGhlYWQgb2Ygcm93cw0Kc3VtbWFyeShkYXRhX2JhbmspIyBzdW1tYXJ5IHN0YXRpc3RpY3MNCnN0cihkYXRhX2JhbmspICMgc3RydWN0dXJlIG9mIGRhdGFzZXQNCm5yb3coZGF0YV9iYW5rKSMgbnVtYmVyIG9mIHJvd3MNCm5jb2woZGF0YV9iYW5rKSMgbnVtYmVyIG9mIGNvbHVtbnMNCmBgYA0KDQojIyMgSWYgd2Ugd2FudCB0byBjb2RlIGJpbmFyeSBkYXRhIA0KDQpMZXRzIHRyeSBpdCBpbiBsb2FuIHZhcmlhYmxlIHdpdGggY2F0ZWdvcmllcyAoeWVzLCBubykuIFdlIGFyZSBnb2luZyB0byB1c2UgdGhlIGlmZWxzZSgpIGZ1bmN0aW9uLg0KYGBge3J9DQpkYXRhX2JhbmskaG91c2luZz0gaWZlbHNlKGRhdGFfYmFuayRob3VzaW5nPT0neWVzJywxLDApDQpoZWFkKGRhdGFfYmFuayRob3VzaW5nLDEwKQ0KdGFibGUoZGF0YV9iYW5rJGhvdXNpbmcpDQpgYGANCkxldCdzIHdvcmsgd2l0aCBvdXIgZmlyc3QgZm9ybWF0IGRhdGFzZXQuIFdlIHdpbGwgaW1wb3J0IGl0IGFnYWluIGhlcmUuDQoNCmBgYHtyfQ0KZGF0YV9iYW5rIDwtIHJlYWQuY3N2KCJ+L0FMREEgRE9DL0FMREEgMjAyMC9UaXJhbmEgYmFuay1kZXNrdG9wL1RpcmFuYSBiYW5rLXRyYWpuaW1pIHFlcnNob3IgQWxkYS9UaXJhbmEgQmFuay1SIG1hdGVyaWFscy9EYXRhIF9iYW5rIGRhdGFzZXQvYmFuay1mdWxsLmNzdiIsIHNlcD0iOyIpDQpoZWFkKGRhdGFfYmFuaywxMCkNCmBgYA0KDQojIyMgQ2hlY2sgIm1pc3NpbmcgdmFsdWVzLCBOQSINCmBgYHtyfQ0Kc3VtKCFjb21wbGV0ZS5jYXNlcyhkYXRhX2JhbmspKSAjIGl0IHdpbGwgdHVybiAwIGJlY2F1c2UgdGhpcyBkYXRhc2V0IGlzIGNsZWFuZWQgZnJvbSBOQS1zDQpgYGANCiMjIyBDaGVjayBmb3IgZHVibGljYXRlcyBpbiByb3dzIA0KYGBge3J9DQpzdW0oZHVwbGljYXRlZChkYXRhX2JhbmspKSMgaXQgd2lsbCByZXR1cm4gdGhlIG51bWJlciBvZiBkdWJsaWNhdGVkICwgaGVyZSBpdCB3aWxsIHJldHVybiAwIGJlY2F1c2Ugd2UgaGF2ZSBjbGVhbmVkIHRoZSBkdWJsaWNhdGUNCmBgYA0KDQojIyMgRGVzY3JpcHRpdmUgc3RhdGlzdGljcw0KDQpgYGB7cn0NCnN1bW1hcnkoZGF0YV9iYW5rKQ0Kc3VtbWFyeShkYXRhX2JhbmskYWdlKSMgb25seSBmb3IgYWdlIHZhcmlhYmxlDQpzdW1tYXJ5KGRhdGFfYmFua1ssMzo2XSkjIG9ubHkgZm9yIHZhcmlhYmxlcyBpbiBjb2x1bW5zIDMgdXAgdG8gNg0KYGBgDQojIyMgRGF0YSBXcmFuZ2xpbmcgDQojIyMjIGZpbHRlcigpDQpmaWx0ZXIoKSBpcyBhIGZ1bmN0aW9uIGluIGRwbHlyIHRoYXQgdGFrZXMgbG9naWNhbCBleHByZXNzaW9ucyBhbmQgcmV0dXJucyB0aGUgcm93cyBmb3Igd2hpY2ggYWxsIGFyZSBUUlVFLg0KDQpgYGB7cn0NCmxpYnJhcnkoZHBseXIpDQpgYGANCg0KYGBge3J9DQojIGZpbHRlciBpbmRpdmlkdWFscyBvZiBhZ2UgbGVzcyB0aGFuIDI1IHllYXJzIG9sZA0KZmlsdGVyKGRhdGFfYmFuaywgYWdlIDwgMjUpIA0KDQojIGZpbHRlciBpbmRpdmlkdWFscyBob3VzaW5nID09IHllcywgYW5kIGFnZSBsZXNzIHRoYW4gMjUgeWVhcnMgb2xkDQpmaWx0ZXIoZGF0YV9iYW5rLCBob3VzaW5nPT0gInllcyIpDQpmaWx0ZXIoZGF0YV9iYW5rLCBhZ2UgPCAyNSkNCg0KIyBpbmRpdmlkdWFscyB3aXRoIHByb2Zlc3Npb24gbWFuYWdlbWVudCBhbmQgYWdlID0zMCB5ZWFycyBvbGQNCmZpbHRlcihkYXRhX2JhbmssIGpvYiA9PSAibWFuYWdlbWVudCIsIGFnZSA9PSAzMCkNCg0KIyBpbmRpdmlkdWFscyB3aXRoIHByb2Zlc3Npb24gbWFuYWdlbWVudCBhbmQgYWdlIGxlc3MgdGhhbiAyNSB5ZWFycyBvbGQgDQpmaWx0ZXIoZGF0YV9iYW5rLCBqb2IgPT0gIm1hbmFnZW1lbnQiLCBhZ2UgPCAyNSkNCg0KIyB3ZSB3YW50IHRvIGRpc3BsYXkgaW4gam9iIHRoZSByZXRpcmVkIGFuZCBtYW5hZ2VtZW50IHByb2Zlc3Npb25zDQpmaWx0ZXIoZGF0YV9iYW5rLCBqb2IgJWluJSBjKCJyZXRpcmVkIiwgIm1hbmFnZW1lbnQiKSkgDQpgYGANCkV4ZXJjaXNlDQphLiBXaGF0IHdhcyB0aGUgYXZlcmFnZSBhZ2Ugb2YgIm1hbmFnZW1lbnQiIHByb2Zlc3Npb25hbHMgaG91c2luZyAieWVzIi4NCg0KSGludDogeW91IGNhbiBkbyB0aGlzIGluIDIgc3RlcHMgYnkgYXNzaWduaW5nIGEgdmFyaWFibGUgYW5kIHRoZW4gdXNpbmcgdGhlIG1lYW4oKSBmdW5jdGlvbi4NCg0KYi4gV2hhdCB3YXMgdGhlIGF2ZXJhZ2UgYmFsYW5jZSBvZiB0aGUgbG9hbiBmb3Igc2Vjb25kYXJ5IGVkdWNhdGlvbj8gIA0KDQpTb2x1dGlvbi4gYQ0KYGBge3J9DQphdi5hZ2UgPC0gZmlsdGVyKGRhdGFfYmFuaywgam9iID09ICJtYW5hZ2VtZW50IiwgaG91c2luZz09ICJ5ZXMiKSAgDQptZWFuKGF2LmFnZSRhZ2UpICANCmBgYA0KDQojIyMgc2VsZWN0KCkgDQoNCldlIHVzZSBzZWxlY3QoKSB0byBzdWJzZXQgdGhlIGRhdGEgb24gdmFyaWFibGVzIG9yIGNvbHVtbnMuDQoNCldlIGNhbiBzZWxlY3QgbXVsdGlwbGUgY29sdW1ucyB3aXRoIGEgY29tbWEsIGFmdGVyIHdlIHNwZWNpZnkgdGhlIGRhdGEgZnJhbWUgKGRhdGFfYmFuaykuDQoNClRoZSBsb2dpYzoNCg0KICAgc2VsZWN0KGRmLCBBLCBCICxDKTogU2VsZWN0IHRoZSB2YXJpYWJsZXMgQSwgQiBhbmQgQyBmcm9tIGRmIGRhdGFzZXQuDQogICBzZWxlY3QoZGYsIEE6QykgOiBTZWxlY3QgYWxsIHZhcmlhYmxlcyBmcm9tIEEgdG8gQyBmcm9tIGRmIGRhdGFzZXQuDQogICBzZWxlY3QoZGYsIC1DKTogRXhjbHVkZSBDIGZyb20gdGhlIGRhdGFzZXQgZnJvbSBkZiBkYXRhc2V0LgkNCg0KVGhlIGVycm9yIGJlbG93IGl0IGlzIGxpa2VseSB0aGF0IHlvdSBhcmUgZWl0aGVyIHVzaW5nIGEgcGFja2FnZSBiZXNpZGVzIGRwbHlyIHRoYXQgYWxzbyBoYXMgYSBzZWxlY3QoKSBmdW5jdGlvbiBvciB5b3UganVzdCBmb3Jnb3QgdG8gbG9hZCB0aGUgZHBseXIgcGFja2FnZSB3aXRoIGxpYnJhcnkoZHBseXIpLg0KDQpgYGB7cn0NCmRhdGFfYmFuayAlPiUgc2VsZWN0KGVkdWNhdGlvbikgIyBFcnJvciBpbiBzZWxlY3QoLiwgZWR1Y2F0aW9uKSA6IHVudXNlZCBhcmd1bWVudCAoZWR1Y2F0aW9uKQ0KDQpgYGANCldlIGNhbiB1c2UgdGhpcyBzeW50YXggb2Ygb2J0YWluaW5nIHRoZSByZXN1bHRzIHdpdGggc2VsZWN0KCkNCmBgYHtyfQ0KZGF0YV9iYW5rICU+JSBkcGx5cjo6c2VsZWN0KGFnZSkNCg0KIyBvciBzZWxlY3QgMXN0IHZhcmlhYmxlIChhZ2UpDQpoZWFkKGRhdGFfYmFua1ssMV0pIA0KDQpkYXRhX2JhbmsgJT4lIGRwbHlyOjpzZWxlY3QoYWdlLCBtYXJpdGFsLCBkdXJhdGlvbikgDQpgYGANCiANCldlIGNhbiBhbHNvIHVzZSAtIChtaW51cykgdG8gZGVzZWxlY3QgY29sdW1ucy4gDQpUaGUgY29kZSBiZWxvdyB3aWxsIGRlLXNlbGVjdCBmcm9tIG91ciBkYXRhc2V0IHRoZSB2YXJpYWJsZXMgYWdlIGFuZCBtYXJpdGFsLg0KDQpgYGB7cn0NCmRhdGFfYmFuayAlPiUgZHBseXI6OnNlbGVjdCgtYWdlLCAtbWFyaXRhbCkgDQpgYGANCg0KIyMjIyBXZSBjYW4gdXNlIHRoZSBwaXBlIHRvIGNoYWluIHRob3NlIHR3byBvcGVyYXRpb25zIHRvZ2V0aGVyLg0KDQpXZSB3YW50IHRvIGZpbHRlciBmb3IgcHJvZmVzc2lvbiAoam9iIC0gdmFyaWFibGUpIHRoZSAibWFuYWdlbWVudCIgYW5kIGZyb20gdGhlc2UgaW5kaXZpZHVhbHMgdG8gc2hvdyB0aGVpciAiZWR1Y2F0aW9uIiBhbmQgImxvYW4iIHZhcmlhYmxlLg0KDQpFeGVyY2lzZQ0KSG93IGNhbiB5b3UgZG8gaXQgZm9yIGluZGl2aWR1YWxzICJtYXJpdGFsIiBzdGF0dXMgdG8gc2hvdyB0aGVpciAiYWdlIiBhbmQgImJhbGFuY2UiDQoNCmBgYHtyfQ0KZGF0YV9iYW5rLjEgPC0gZGF0YV9iYW5rICU+JSANCiAgZmlsdGVyKGpvYiA9PSAibWFuYWdlbWVudCIpICU+JQ0KIGRwbHlyOjpzZWxlY3QoZWR1Y2F0aW9uLGxvYW4pIA0KDQpkYXRhX2JhbmsuMQ0KYGBgDQoNCiMjIyBtdXRhdGUoKSBhZGRzIG5ldyB2YXJpYWJsZXMgdG8gZGF0YWZyYW1lDQoNCkxldCBzdXBwb3NlIHdlIHdhbnQgdG8gYWRkIGEgdmFyaWFibGUgY29tYmluaW5nIHR3byBvciBtb3JlIGV4aXN0aW5nIHZhcmlhYmxlcy4gT3Igd2Ugd2FudCB0byBhZGQgYSBuZXcgdmFyaWFibGUgZnJvbSBhbiBleGlzdGluZyB2ZWN0b3Igd2hpY2ggaXMgbm90IHBhcnQgb2YgdGhlIGRhdGFmcmFtZS4NCg0KTGV0J3Mgc3VwcG9zZSBJIHdhbnQgdG8gc2VlIHRoZSBiYWxhbmNlIGZvciBkYXlzIG9mIHRoZSBkdXJhdGlvbiBwZXJpb2QuIChiYWxhbmNlL2R1cmF0aW9uKS4NCg0KQWRkaW5nIGEgbmV3IHZhcmlhYmxlIGZyb20gdHdvIGV4aXN0aW5nIHZhcmlhYmxlcyBvZiB0aGUgZGF0YWZyYW1lLCBuYW1lZCAiZGF5LmJhbGFuY2UiDQpgYGB7cn0NCmRhdGFfYmFuayAlPiUNCiAgbXV0YXRlKGRheS5iYWxhbmNlID0gcm91bmQoYmFsYW5jZS9kdXJhdGlvbiwyKSkNCmBgYA0KDQpJIGNhbiBhZGQgZnJvbSBhbm90aGVyIGV4aXN0aW5nIHZhcmlhYmxlIG91dHNpZGUgdGhlIGRhdGFzZXQuDQpgYGB7cn0NClg8LXJub3JtKGxlbmd0aChkYXRhX2JhbmskYWdlKSkNCmxlbmd0aChYKQ0KZGF0YV9iYW5rICU+JSBtdXRhdGUoWCkNCmBgYA0KDQojIyMgZ3JvdXBfYnkoKSBvcGVyYXRlcyBvbiBncm91cHMNCg0Kc3VtbWFyaXplKCkgd2lsbCBhY3R1YWxseSBvbmx5IGtlZXAgdGhlIGNvbHVtbnMgdGhhdCBhcmUgZ3JvdXBlZF9ieSBvciBzdW1tYXJpemVkLiANCg0KdW5ncm91cCgpIHJlbW92ZXMgdGhlIGdyb3VwaW5nIGFuZCBpdOKAmXMgZ29vZCB0byBnZXQgaW4gdGhlIGhhYml0IG9mIHVzaW5nIGl0IGFmdGVyIGEgZ3JvdXBfYnkoKS4NCmBgYHtyfQ0KZGF0YV9iYW5rICU+JQ0KICBmaWx0ZXIoYWdlID09IDMwKSAlPiUNCiAgZ3JvdXBfYnkobWFyaXRhbCkgJT4lDQogIHN1bW1hcml6ZShzdW0oZHVyYXRpb24pKSAlPiUNCiAgdW5ncm91cCgpDQpgYGANCg0KIyMjIGFycmFuZ2UoKQ0KDQphcnJhbmdlKCkgZnVuY3Rpb24gaXMgb3JkZXJlZCBhbHBoYWJldGljYWxseSBmcm9tIEEgdG8gWi4NCg0KYGBge3J9DQojIGFycmFuZ2UgYnkgam9iDQpkYXRhX2JhbmsgJT4lDQogICBncm91cF9ieShtYXJpdGFsLGpvYikgJT4lDQogIHN1bW1hcml6ZShtZWFuKGR1cmF0aW9uKSkgJT4lDQogIGFycmFuZ2Uoam9iKQ0KDQojIGFycmFuZ2UgYnkgbWFyaXRhbCBzdGF0dXMNCiBkYXRhX2JhbmsgJT4lDQogICBncm91cF9ieShtYXJpdGFsLGpvYikgJT4lDQogIHN1bW1hcml6ZShtZWFuKGR1cmF0aW9uKSkgJT4lDQogIGFycmFuZ2UobWFyaXRhbCkgDQpgYGANClRyeSB0byBjb21iaW5lIGJ5IHlvdXJzZWxmIHRoZSBhYm92ZSBmdW5jdGlvbnMuIA0KDQpFeGVyY2lzZQ0KV2hhdCBpcyB0aGUgbWF4aW11bSBkdXJhdGlvbiBmb3IgYWxsIGpvYnMgYXQgYWdlIDMwPw0KDQpFeGVyY2lzZQ0KVHJ5IHRvIHVuZGVyc3RhbmQgd2hhdCB0aGUgYmVsb3cgY29tbWFuZCB3aWxsIGdpdmUgeW91IGFzIGFuIG91dHB1dD8NCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpICMjIGluc3RhbGwucGFja2FnZXMoJ3RpZHl2ZXJzZScpDQojIyBzdW1tYXJpemUNCmRhdGFfYmFuay4yIDwtIGRhdGFfYmFuayAlPiUgDQogIGRwbHlyOjpzZWxlY3QoLWNvbnRhY3QsIC1wb3V0Y29tZSkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkobWFyaXRhbCkgJT4lDQogIGRwbHlyOjptdXRhdGUoZGF5LmJhbGFuY2UgPSByb3VuZChiYWxhbmNlL2R1cmF0aW9uLDIpKSAlPiUNCiAgZHBseXI6OnN1bW1hcml6ZShtaW5fZGF5LmJhbGFuY2UgPSBtaW4oZGF5LmJhbGFuY2UpKSAlPiUNCiAgZHBseXI6OnVuZ3JvdXAoKSANCg0KYGBgDQoNCiMgR1JBUEhJQ1MgDQoNCiMjIyBwbG90KCkgZnVuY3Rpb24NCg0KUGxvdCBvbmx5IG9uZSB2YXJpYWJsZS4NCmBgYHtyfQ0KIyBub3QgYSBnb29kIGdyYXBoaWNhbCBwcmVzZW50YXRpb24gb2YgYWdlDQpwbG90KGRhdGFfYmFuayRhZ2UpIA0KIyBhIGJldHRlciB2aWV3IA0KcGxvdCh0YWJsZShkYXRhX2JhbmskYWdlKSx5bGFiID0gIkZyZXF1ZW5jeSIseGxhYj0iQWdlIixtYWluPSJBZ2Ugb2YgYmFuayBkYXRhIixjb2w9InJlZCIsbHdkPTUpIA0KYGBgDQogRXhlcmNpc2UNCiBUcnkgdG8gYWRkIHRleHQgKCJHcmFwaGljcyBpbiBSIikgdG8geW91ciBzZWNvbmQgZ3JhcGggYXQgY29vcmRpbmF0ZXMgYWdlIDY4IGFuZCBmcmVxdWVuY3kgMTQwMC4NCg0KIyMjIGhpc3RvZ3JhbXMgaGlzdCgpDQoNCk9ic2VydmUgdGhlIGFyZ3VtZW50IGJyZWFrcz0uIEZvciBtb3JlIGFyZ3VtZW50cyBvZiBoaXN0KCkgc2VlIGluID9oaXN0b2dyYW0uDQpgYGB7cn0NCmhpc3QoZGF0YV9iYW5rJGFnZSwgbWFpbj0iSGlzdG9ncmFtIGZvciBhZ2UgdmFyaWFibGUiLHhsYWI9ImFnZSIseWxhYj0iZnJlcSIsY29sPSJyZWQiKQ0KaGlzdChkYXRhX2JhbmskYWdlLCBtYWluPSJIaXN0b2dyYW0gZm9yIGFnZSB2YXJpYWJsZSIseGxhYj0iIix5bGFiPSIiLGNvbD0icmVkIixicmVha3MgPSA1KQ0KaGlzdChkYXRhX2JhbmskYWdlLCBtYWluPSJIaXN0b2dyYW0gZm9yIGFnZSB2YXJpYWJsZSIseGxhYj0iIix5bGFiPSIiLGNvbD0icmVkIixicmVha3MgPSAxMCkNCmhpc3QoZGF0YV9iYW5rJGFnZSwgbWFpbj0iSGlzdG9ncmFtIGZvciBhZ2UgdmFyaWFibGUiLHhsYWI9IiIseWxhYj0iIixjb2w9InJlZCIsYnJlYWtzID0gMTUpDQoNCmBgYA0KDQojIyMgUElFIENoYXJ0ICBwaWUoKQ0KV2hlbiBhcHBseWluZyB0aGUgcGllKCkgd2UgbmVlZCB0byB0YWtlIGNhcmUgb2YgdGhlIGZyZXF1ZW5jaWVzIGJlY2F1c2UgUiB3aWxsIGF1dG9tYXRpY2FsbHkgcHJvZHVjZSBhIHBpZSBjaGFydCBmb3IgZWFjaCBpbmRpdmlkdWFsIChvYnNlcnZhdGlvbikuDQoNCmBgYHtyfQ0KIyBub3QgdGhlIGFwcHJvcHJpYXRlIHdheSB0byBvYnRhaW4gYSBwaWUgY2hhcnQNCnBpZShkYXRhX2JhbmskYWdlKQ0KDQojIGNvbWJpbmVkIHdpdGggdGhlIGZ1bmN0aW9uIHRhYmxlKCkgDQojIGZvciBob3VzaW5nIA0KcGllKHRhYmxlKGRhdGFfYmFuayRob3VzaW5nKSxtYWluPSJIb3VzaW5nIG9yIG5vdCIsY29sPWMoInJlZCIsImJsdWUiKSkNCg0KIyBmb3IgbWFyaXRhbCB2YXJpYWJsZQ0KcGllKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKSxtYWluPSJDaXZpbCBzdGF0dXMiLGNvbD1jKCJyZWQiLCJibHVlIiwieWVsbG93IikpDQoNCiMgY2hhbmdpbmcgY29sb3JzIGJhc2VkIG9uIHRoZSBjYXRlZ29yaWVzIG9mIG1hcml0YWwgdmFyaWFibGUNCnBpZSh0YWJsZShkYXRhX2JhbmskbWFyaXRhbCksbWFpbj0iQ2l2aWwgc3RhdHVzIixjb2w9cmFpbmJvdyhsZW5ndGgodGFibGUoZGF0YV9iYW5rJG1hcml0YWwpKSkpDQoNCnBpZSh0YWJsZShkYXRhX2JhbmskbW9udGgpLG1haW49Ik1vbnRoICIsY29sPXJhaW5ib3cobGVuZ3RoKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKSkpKQ0KDQpwaWUodGFibGUoZGF0YV9iYW5rJGpvYiksbWFpbj0iUHJvZmVzc2lvbiAoam9iKSIsY29sPXJhaW5ib3cobGVuZ3RoKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKSkpKQ0KDQojIEFkZCBwZXJjZW50YWdlIHRvIGxhYmVscw0KIyBjcmVhdGUgYSB2ZWN0b3Igb2YgcGVyY2VudGFnZSANCnBlcmM8LSByb3VuZCgxMDAqKHRhYmxlKGRhdGFfYmFuayRtYXJpdGFsKS9sZW5ndGgoZGF0YV9iYW5rJG1hcml0YWwpKSkNCnBlcmMNCiMgY3JlYXRlIGEgdmVjdG9yIG9mIGxhYmVscw0KbGFiZWw8LWMoImRpdm9yY2VkIiwibWFycmllZCIsInNpbmdsZSIpDQoNCiMgcGFzdGUgcGVyY2VudGFnZSBhbmQgbGFiZWxzIHRvZ2V0aGVyDQpsYWJlbDwtIHBhc3RlKGxhYmVsLCBwZXJjKSAjIGNvbWJpbmUgZnJlcXVlbmNpZXMgZm9yIGVhY2ggY2l2aWwgc3RhdHVzDQogIyBhZGQgICUgdG8gZWFjaCBsYWJlbA0KbGFiZWwgPC0gcGFzdGUobGFiZWwsIiUiLHNlcD0iIikNCiMgb2J0YWluIHRoZSBwaWUgY2hhcnQNCnBpZSh0YWJsZShkYXRhX2JhbmskbWFyaXRhbCksbGFiZWwsbWFpbj0iQ2l2aWwgc3RhdHVzIixjb2w9cmFpbmJvdyhsZW5ndGgodGFibGUoZGF0YV9iYW5rJG1hcml0YWwpKSkpDQoNCiMgM0QgRXhwbG9kZWQgUGllIENoYXJ0DQpsaWJyYXJ5KHBsb3RyaXgpDQpwaWUzRCh0YWJsZShkYXRhX2JhbmskbWFyaXRhbCksZXhwbG9kZT0wLjEsbWFpbj0iQ2l2aWwgc3RhdHVzIixjb2w9YygicmVkIiwiYmx1ZSIsInllbGxvdyIpKQ0KIyBleHBsb2RlIGFyZ3VtZW50IGNoYW5nZXMgdGhlIGRpbWVuc2lvbiBvZiBkaXN0YW5jZSBiZXR3ZWVuIHRoZSBwYXJ0cyAsIHRyeSBpdCA9MC41DQpgYGANCg0KIyMjIEJveHBsb3QNCg0KTWF5IGJlIGNyZWF0ZWQgZm9yIG9ubHkgb25lIHZhcmlhYmxlIGFuZCBhbHNvIGZvciBhIGdyb3VwIG9mIGNoYXJhY3RlcmlzdGljcyBpbiBhIG51bWVyaWNhbCB2YXJpYWJsZSBhbmQgYSBub24tbnVtZXJpY2FsIHZhcmlhYmxlLg0KYGBge3J9DQpib3hwbG90KGRhdGFfYmFuayRhZ2UsY29sPSJncmVlbiIsbWFpbj0iQm94cGxvdCBhZ2UiLHlsYWI9ImFnZSIpDQoNCmJveHBsb3QoZGF0YV9iYW5rJGJhbGFuY2UsY29sPSJyZWQiLG1haW49IkJveHBsb3QgYmFsYW5jZSIseWxhYj0iYmFsYW5jZSIpDQoNCmJveHBsb3QoZGF0YV9iYW5rJGFnZX5kYXRhX2JhbmskbWFyaXRhbCxjb2w9YygicmVkIiwiZ3JlZW4iLCJwdXJwbGUiKSxtYWluPSJCb3hQbG90IGNpdmlsIHN0YXR1cyIseWxhYj0iYWdlIikNCg0KYm94cGxvdChkYXRhX2JhbmskZHVyYXRpb25+ZGF0YV9iYW5rJG1hcml0YWwsY29sPWMoInJlZCIsImdyZWVuIiwicHVycGxlIiksbWFpbj0iQm94UGxvdCBjaXZpbCBzdGF0dXMiLHlsYWI9IkR1cmF0aW9uIix4bGFiPSJjaXZpbCBzdGF0dXMiKQ0KYGBgDQoNCiMjIyBDb3JyZWxhdGlvbiBwbG90cw0KDQojIyMjIGxpYnJhcnkocHN5Y2gpDQpgYGB7cn0NCiMNCmRhdGFfYmFuay4xIDwtIGRhdGFfYmFuayAlPiUgZHBseXI6OnNlbGVjdChkdXJhdGlvbiwgYWdlLCBiYWxhbmNlKQ0KcGFpcnMoZGF0YV9iYW5rLjEsY29sPSJyZWQiLG1haW49IlNjYXR0ZXJwbG90IGZvciBtYW55IHZhcmlhYmxlcyIpDQojIA0KIyBDYWxjdWxhdGUgY29ycmVsYXRpb25zIGJldHdlZW4gdHdvIHZhcmlhYmxlcw0KY29yMT1jb3IoZGF0YV9iYW5rJGFnZSxkYXRhX2JhbmskYmFsYW5jZSkNCmNvcjENCmNvcjI9Y29yKGRhdGFfYmFuayRhZ2UsZGF0YV9iYW5rJGR1cmF0aW9uKQ0KY29yMg0KbGlicmFyeShwc3ljaCkjICANCnBhaXJzLnBhbmVscyhkYXRhX2JhbmtbLGMoMSw2LDEyKV0pDQpgYGANCg0KIyMjIyBsaWJyYXJ5KGNvcnJwbG90KSBhbmQgbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KQW5vdGhlciBsaWJyYXJ5IGZvciBjb3JyZWxhdGlvbiBwbG90Lg0KYGBge3J9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KbGlicmFyeSgiUGVyZm9ybWFuY2VBbmFseXRpY3MiKQ0KY29yLm1hdD1jb3IoZGF0YV9iYW5rWyxjKDEsNiwxMildKSMgY29ycmVsYXRpb24gbWF0cml4IA0KY29yLm1hdA0KY2hhcnQuQ29ycmVsYXRpb24oZGF0YV9iYW5rWyxjKDEsNiwxMildLGhpc3RvZ3JhbT1UUlVFLCBwY2g9MTkpDQoNCmBgYA0KDQojIyMjIGxpYnJhcnkoY29ycnBsb3QpIGFuZCBsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmBgYHtyfQ0KbGlicmFyeShjb3JycGxvdCkNCmxpYnJhcnkoUkNvbG9yQnJld2VyKQ0KTSA8LWNvcihkYXRhX2JhbmtbLGMoMSw2LDEyKV0pICNzZWxlY3Qgb25seSBudW1lcmljYWwgdmFyaWFibGVzDQpjb3JycGxvdChNLCB0eXBlPSJ1cHBlciIsIG9yZGVyPSJoY2x1c3QiLGNvbD1icmV3ZXIucGFsKG49OCwgbmFtZT0iUmRZbEJ1IikpDQpgYGANCg0KIyMjIyBsaWJyYXJ5KGdncGxvdDIpIGFuZCBsaWJyYXJ5KEdHYWxseSkNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShHR2FsbHkpDQoNCiMgZGlzcGxheSBhIHBhaXIgcGxvdCBvZiBhbGwgZm91ciBjb2x1bW5zIG9mIGRhdGENCkdHYWxseTo6Z2dwYWlycyhkYXRhX2JhbmtbLGMoMSw2LDEyKV0pDQpgYGANCg0KIyMjIEV4ZXJjaXNlDQpUcnkgdG8gd29yayBvbiB0aGlzIGJhbmsgZGF0YXNldHM6DQoNCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vaXRzbWVzdW5pbC9iYW5rLWxvYW4tbW9kZWxsaW5nDQoNCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vc2FudG9zaGQzL2JhbmstY3VzdG9tZXJzDQoNCmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vemF1cmJlZ2lldi9teS1kYXRhc2V0P3NlbGVjdD1jcmVkaXRfdHJhaW4uY3N2DQoNCg0KIyBFU1FVSVNTRSBsaWJyYXJ5DQoNCiMjIEEgZ3JhcGhpY2FsIGFkZC1pbiBpbiBSLVN0dWRpbw0KDQpUaGUgcHVycG9zZSBvZiB0aGlzIGFkZC1pbiBpcyB0byBsZXQgeW91IGV4cGxvcmUgeW91ciBkYXRhIHF1aWNrbHkgdG8gZXh0cmFjdCB0aGUgaW5mb3JtYXRpb24gdGhleSBob2xkLiBZb3UgY2FuIGNyZWF0ZSB2aXN1YWxpemF0aW9uIHdpdGgge2dncGxvdDJ9LCBmaWx0ZXIgZGF0YSB3aXRoIHtkcGx5cn0gYW5kIHJldHJpZXZlIGdlbmVyYXRlZCBjb2RlLg0KDQpUbyBydW4gRXNxdWlzc2UgaW4gUiBzdHVkaW8gY29weSBhbmQgcGFzdGUgdGhlIGZvbGxvd2luZyBjb2RlIChkb24ndCBmb3JnZXQgdG8gcHJldmlvdXNseSBpbnN0YWxsICJlc3F1aXNzZSIpDQoNCiMjIyBTdGFydCAuLi4NCg0KbGlicmFyeShlc3F1aXNzZSkNCmVzcXVpc3NlOjplc3F1aXNzZXIoKQ0KDQojIyMjIHRvIGRpc3BsYXkgZXNxdWlzc2VyIHlvdSBjYW4gdHJ5IG9uZSBvZiB0aGUgdmlld2VyIG9wdGlvbnMgYmVsb3cgdG8gbGF1bmNoIGluIHlvdXIgYnJvd3NlciBvciBwYW5lDQoNCiBlc3F1aXNzZTo6ZXNxdWlzc2VyKGRhdGEsIHZpZXdlciA9ICJicm93c2VyIikNCg0KIyMjIC4uLi4gRW5kDQoNCmBgYHtyfQ0KbGlicmFyeShlc3F1aXNzZSkNCmJhbmsgPC0gcmVhZC5jc3YoIn4vQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3QvRGlkYWN0aWMgbWF0ZXJpYWxzIENyeXN0YWwgU29sdXRpb24vREFUQVNFVC1DcnlzdGFsL2JhbmsuY3N2IikNCiMgdG8gZGlzcGxheSBlc3F1aXNzZXIgeW91IGNhbiB0cnkgb25lIG9mIHRoZSB2aWV3ZXIgb3B0aW9ucyBiZWxvdw0KZXNxdWlzc2U6OmVzcXVpc3Nlcih2aWV3ZXIgPSAiYnJvd3NlciIpDQojIG9yDQplc3F1aXNzZTo6ZXNxdWlzc2VyKHZpZXdlciA9ICJwYW5lIikNCmBgYA0KDQoNCiMjIyBTZWxlY3QgZGF0YQ0KSW1wb3J0IGFueSBkYXRhIHlvdSBoYXZlIGZyb20geW91ciBzZXNzaW9uIGluIFINCg0KIVtBbHQgdGV4dF0oQzpcVXNlcnNcdXNlclxEb2N1bWVudHNcQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3RcZm90b1xlc3F1aXNzZTAucG5nKQ0KDQojIyMgQ3JlYXRlIGEgcGxvdA0KU3RhcnQgY3JlYXRlIGEgcGxvdCBieSBkcmFnIGFueSB2YXJpYWJsZSBpbiB0aGUgd2luZG93cyAoWCwgWSkgYW5kIG90aGVyIGdyb3VwaW5nIHdpbmRvd3MuDQoNCiFbQWx0IHRleHRdKEM6XFVzZXJzXHVzZXJcRG9jdW1lbnRzXENSWVNUQUwgU3lzdGVtLURhdGEgU2NpZW5jZSBwcm9qZWN0XGZvdG9cZXNxdWlzc2UxLnBuZykNCg0KIyMjIExhYmVscyBhbmQgVGl0bGVzDQoNCkFkZCBUaXRsZSwgc3VidGl0bGUgYW5kIGF4aXMgbmFtZS4NCg0KIVtBbHQgdGV4dF0oQzpcVXNlcnNcdXNlclxEb2N1bWVudHNcQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3RcZm90b1xlc3F1aXNzZTIucG5nKQ0KIyMjIFBsb3QgT3B0aW9ucw0KTW9kaWZ5IHRoZSBwbG90IG9wdGlvbnMNCg0KIVtBbHQgdGV4dF0oQzpcVXNlcnNcdXNlclxEb2N1bWVudHNcQ1JZU1RBTCBTeXN0ZW0tRGF0YSBTY2llbmNlIHByb2plY3RcZm90b1xlc3F1aXNzZTMucG5nKQ0KDQojIyMgRGF0YQ0KTW9kaWZ5IGRhdGEgZnJvbSB0aGUgZGF0YXNldA0KDQohW0FsdCB0ZXh0XShDOlxVc2Vyc1x1c2VyXERvY3VtZW50c1xDUllTVEFMIFN5c3RlbS1EYXRhIFNjaWVuY2UgcHJvamVjdFxmb3RvXGVzcXVpc3NlNC5wbmcpDQoNCiMjIyBFeHBvcnQgYW5kIENvZGUNCg0KRXhwb3J0IHRoZSBjb2RlIGluIFItY29uc29sZSBhbmQgbW9kaWZ5L3VzZSBpdCBieSBjaGFuZ2luZyBhcmd1bWVudHMuDQoNCiFbQWx0IHRleHRdKEM6XFVzZXJzXHVzZXJcRG9jdW1lbnRzXENSWVNUQUwgU3lzdGVtLURhdGEgU2NpZW5jZSBwcm9qZWN0XGZvdG9cZXNxdWlzc2U1LnBuZykNCg0KDQpGb3IgbW9yZSBvbiBFc3F1aXNzZSBzZWU6DQoNCmh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9lc3F1aXNzZS9yZWFkbWUvUkVBRE1FLmh0bWwNCg0KaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2VzcXVpc3NlL3ZpZ25ldHRlcy9nZXQtc3RhcnRlZC5odG1sDQoNCiMjIyAxMSBKYW51YXJ5IDIwMjENCiMjIyMgRXJhbGRhIEdqaWthIChEaGFtbykNCg0KDQoNCg==