This tutorial is to demonstrate how to R as a tool for data science.
Before we dive into R syntax, I want to clarify some of the common terminolories in statistics and R programming.
Variable=feature=column
Observation=row=case
Independent variable=predictors (variables)=regressors
Dependent variable=response variable
Vector= a single column or variable
A dataframe = a dataset with rows and columns
NA is missing values
Numeric data: Discrete data; continuous data
Categorical data: Nomial data, ordinal data
Factors: a categorical data with mathematical computation
Random with replacement: Who knows?
Let’s experience the first taste of R
– Type a random number in R console
4
[1] 4
# Store 4 in an object named four
four=4
# Call out the object 4
four
[1] 4
– Type a random character in R console
# Why ti throws an error?
# How can we write a character in R
"University"; 'Thai Nguyen'
[1] "University"
[1] "Thai Nguyen"
# Give it a name
mycha<-"University"
mycha<-c("University","Thainguyen")
mycha
[1] "University" "Thainguyen"
# how to create a list of 10 numbers from 1 to 10
mynum<-c(1:10)
mynum
[1] 1 2 3 4 5 6 7 8 9 10
Dive in R
- How to grenerate a list of randomly normal distributed numbers
x<-rnorm(1000)
# Check the length of x
length(x)
[1] 1000
# Plot the histogram of x
hist(x, xlab = "Values", ylab="Frequency", main="Histogram or Randomly distributed Values",col=rainbow(20))

- Some of the common notations used in R
=, <-, ==, !, is.na(), &, |, and >, >=
# = or <-
a<-5
a=5
# ==
a==4
[1] FALSE
# differ
a!=5 # a is different from 5
[1] FALSE
a!=8 # a is different from 5
[1] TRUE
# > or >=, <. <=, is.na(), A|B, A&B
A<-5
B<-6
A>B
[1] FALSE
A<B
[1] TRUE
# Check the current date
Sys.Date()
[1] "2018-01-22"
- If you want to know any functions that start with
lm, type the following command
apropos("norm")
[1] ".rs.normalizeKeyboardShortcut" ".rs.normalizePath"
[3] ".rs.validateAndNormalizeEncoding" "dlnorm"
[5] "dnorm" "norm"
[7] "normalizePath" "plnorm"
[9] "pnorm" "qlnorm"
[11] "qnorm" "qqnorm"
[13] "rlnorm" "rnorm"
How to read data in R
- How to mannually generate two columns of data with one column
hight and age
# set the fixed state
set.seed(112)
Height<-round(runif(20,140,190),1) # Unit in centemtter
Age<-round(runif(20,10,70))
df<-data.frame(Height,Age)
head(df)
# If you want to fix the dataset, you can type edit(df) or fix(df)
## ANother way of creating a dataset
set.seed(123)
num<-c(1:20) # Create a sequence of number starting from 1 and ending at 20
Letter<-sample(letters,20, replace = T) # Creating a list of characters randomly selected from 26 English letters
df1<-data.frame(num, Letter) # Form a dataframe
head(df1)
- Read data from our local computer and website, and in this case is
github
– Read data in .csv format
– Read dataset in .txt format in R
head(myland_survey) # Check the first few rows of the dataset
head(myland_survey) # Check the first few rows of the dataset
names(myland_survey)
[1] "A1" "A2" "A3" "A4" "A5" "YA" "XA" "B6" "B7" "B8" "B9" "B10" "YB" "XB"
[15] "C11" "C12" "C13" "C14" "YC" "XC" "D15" "D16" "YD" "XD" "E17" "E18" "E19" "E20"
[29] "YE" "XE" "F21" "F22" "F23" "YF" "XF" "G24" "G25" "YG" "XG" "H26" "H27" "YH"
[43] "XH" "Y.TB"
names(myland_survey)
[1] "A1" "A2" "A3" "A4" "A5" "YA" "XA" "B6" "B7" "B8" "B9" "B10" "YB" "XB"
[15] "C11" "C12" "C13" "C14" "YC" "XC" "D15" "D16" "YD" "XD" "E17" "E18" "E19" "E20"
[29] "YE" "XE" "F21" "F22" "F23" "YF" "XF" "G24" "G25" "YG" "XG" "H26" "H27" "YH"
[43] "XH" "Y.TB"
dim(myland_survey) # Showing the number of rows and columns
[1] 50 44
– We can read data from SPSS using foreign package
library(foreign)
# df2<-read.spss(file.choose(), to.data.frame = T, encoding="cp1252")
– Reading data in R using readr package (reference)
library(readr)
– converting a certain data type to another using parse_
a<-c(1,2,"Ha")
# Convert a to integer
parse_integer(a)
1 parsing failure.
row # A tibble: 1 x 4 col row col expected actual expected <int> <int> <chr> <chr> actual 1 3 NA an integer Ha
[1] 1 2 NA
attr(,"problems")
# parse_number()
parse_number("230%")
[1] 230
parse_number("this is 100$")
[1] 100
# Other parsers like parse_logical(), parse_character(), parse_double(),
- Data organization and manipulation
In this section, we will demonstrate the ability of R for data manipulation and organization. while this job can be done difficultly in excel or other software, R can implement it easily with the help of a powerful package called tidyrvese, dplyr and tidyr by Hadly Wickham
# Load packages
library(tidyverse)
mydata<-read.csv("https://raw.githubusercontent.com/tuyenhavan/Land-Relocation-Survey-Data/master/Datafromspss.csv",header=T)
head(mydata)
– Get all MALES from the dataset
# Another one
males<- mydata %>% dplyr::filter(sex=="MALES")
head(males)
# Another dataset
head(diamonds)
# get all diamonds with cut==Ideal and Good, clarity= SI2 and VS1
cut_clarity<- diamonds %>% dplyr::filter(cut==c("Ideal","Good") & clarity==c("SI2","VS1") )
head(cut_clarity)
- How to select columns and rows together
mydata<- diamonds %>% dplyr::filter(cut=="Good") %>% dplyr::select(1:5)
head(mydata)
– How to encode a continous variable into a categorical variable
# if price from 0 to 5000 =Low, 5000<price<=8000=Medium, 8000<price<=10000 =High,>10000=Very high
mydf<-diamonds %>% mutate(price_Encode=cut(price,breaks = c(0, 5000, 8000,10000, 20000),labels=c("Low","Medium","High","Very High"),include.lowest = T))
Error in diamonds %>% mutate(price_Encode = cut(price, breaks = c(0, 5000, :
could not find function "%>%"
– How to create a factorial variable
# Create a dataframe
a<-c(1:20)
b<-c(21:40)
df3<-data.frame(a,b)
head(df3)
# Using `cut` to categorize the variable
df3$cut1<-cut(df3$a,3)
head(df3)
# Another
df3$cut2<-cut(df3$a,3, labels=c("Low","Middle","High"))
head(df3)
# Another way
df3$cut3<-cut(df3$a, breaks = c(0,5,10,15,21), labels=c("Low","Middle","Relative High","High"), include.lowest = T)
head(df3)
– cut an equal internval of values using cut2 function from Hmisc package
library(Hmisc)
package 㤼㸱Hmisc㤼㸲 was built under R version 3.4.3Loading required package: lattice
Loading required package: survival
Loading required package: Formula
Attaching package: 㤼㸱Hmisc㤼㸲
The following objects are masked from 㤼㸱package:dplyr㤼㸲:
combine, src, summarize
The following objects are masked from 㤼㸱package:base㤼㸲:
format.pval, round.POSIXt, trunc.POSIXt, units
# devide a into 3 groups with an equal interval from smallest to biggest values
df3$cuth<-cut2(df3$a, g=3)
head(df3,20)
- Some of the mathematical operations
# e^123
exp(123)
[1] 2.619517e+53
# log_e()
log(12)
[1] 2.484907
# log10(100)
log10(100)
[1] 2
# cos(pi)
cos(pi/4)
[1] 0.7071068
# sum of square: tổng bình phương
x<-c(1:5)
sum(x^2) # sum of square = 1^2+2^2+3^2+4^2+5^2
[1] 55
– Adjusted sum of square: tổng bình phương điều chỉnh
\[ A= \sum_{i=1}^n (x_i-\bar{x})^2 \]
# Adjusted sum of square
x<-c(1:5)
sum((x-mean(x))^2)
[1] 10
– mean square: Tính sai số bình phương
\[ X= \sum_{i=1}^n (x_i-\bar{x})^2/n \]
x<-c(1:5) # Create a vector
sum((x-mean(x))^2)/length(x) # mean square
[1] 2
– Variance: Tính phương sai
\[ S^2 = \sum_{i=1}^n (x_i-\bar{x})^2/(n-1) \]
x<-c(1:5)
var1<-sum((x-mean(x))^2)/(length(x)-1) # Phương sai
var(x)
[1] 2.5
– Standard deviation: Độ lệch chuẩn
\[ S=\sqrt{S^2} \]
# Standard deviation of x
S=sqrt(var1)
S
sd(x) # another way
– How to create date time data
date1<-as.Date("17/12/23",format="%y/ %m/ %d" )
date1
# How to create of list date between 2010 and 2017
date2<-seq(as.Date("2010-01-01"), as.Date("2017-12-01"),by='month')
head(date2)
# by 2 days
date2<-seq(as.Date("2010-01-01"), as.Date("2017-12-01"),by='2 days')
head(date2)
# by 2 weeks
date2<-seq(as.Date("2010-01-01"), as.Date("2017-12-01"),by='2 weeks')
head(date2)
How to generate random numbers or levels using seq, rep and gl
# Create a sequential numbers
seq(1,10,by=0.2)
# Create a repetition list of numbers
rep(10,times=10)
rep("Ha Van Tuyen",times=10)
rep(c(1:10),3)
# create a list levels
gl(3, 6) # 3 levels and 6*3=18 items
# levels with notations
gl(3,5,length = 20, label=c("Ha","Van","Tuyen"))
# Create a repetation of every 2
rep(1:10, each=2)
– Create a matrix
# Create a squared matrix
A<-matrix(c(1:9),nrow=3,ncol=3,byrow = T)
A
– Transposed matrix: Ma trận chuyển vị
t(A) # This will transpose matrix A to transposed matrix
– Scalar matrix: Ma trận vô hướng
B<-matrix(0,4,4)
diag(B)<-1
B
– Matrix operations
C<-matrix(1:9, ncol=3,nrow=3)
D<-matrix(-1:-9,ncol=3, byrow = T)
# Addition
C+D
# Subtraction
C-D
# Multiplication
C%*%D
– Permutation: Hoán vị prod
# Having 5 students and 5 chairs. How many ways to organize five students in 5 chairs
prod(5:1)
– Combination: Tổ hợp
Graphics- Biểu Đồ or Data visualization (Base R)
# create a random values
N<-runif(1000, -5,5)
x<-N
y<-sin(x) + 0.5*rnorm(N)
# Scatter plot
plot(y~x, main="Scatter Plot", col=rainbow(1000))

# Hítogram
hist(x,main="Histogram")

# Barplot
barplot(N,main="Barplot",col=2)

# Boxplot
boxplot(N,main="Boxplot")
# Group four plots into one single box
par(mfrow=c(2,2))

plot(y~x, main="Scatter Plot", col=rainbow(1000))
hist(x,main="Histogram")
barplot(N,main="Barplot",col=2)
boxplot(y, main="Boxplot")

– Adding title, subtitle and others
# Create a normal distributed values
a<-rnorm(2000)
hist(a,xlab="Values",ylab="Frequency",col=rainbow(50),main="Histogram Plot")
title(sub="Figure 1: Histogram showing the randomly distributed values")

– Legends: Ghi chú
x<-runif(2000,-5,5)
y<- x + 0.5*rnorm(2000)
plot(y~x,main="Linear Regression",pch=16,col=4)
abline(lm(y~x),col=2)
legend(-5,5,c("Point patterns","Regression Line"),pch=16,lty=c(0,1),bty="n",col=c(4,2))

– Barplot
# Cars93 dataset
library(MASS)
head(Cars93)
myiris<-table(Cars93$Type)
# Tweek the plot position
par(mfrow=c(1,2))
barplot(myiris,col=c(2,4),main="Barplot")
barplot(myiris, main="Barplot", horiz = T,col=c(3,4))
title(sub="Figure 2: Three types of car ")

– Barplot with two categorical variables
library(Hmisc)
cut2<-cut2(Cars93$Price,g=4)
mytwo<-table(Cars93$Type,cut2)
# Check the order of levels of Type
levels(Cars93$Type)
[1] "Compact" "Large" "Midsize" "Small" "Sporty" "Van"
barplot(mytwo, beside = T,xlab="Price Group", col = as.numeric(Cars93$Type), ylab="Frequency")

– Density plot
# Create a randomly normal distributed dataset
a<-rnorm(2000,4,5)
hist(a,prob=T,col=3,border = "blue", main="Histogram Plot")
lines(density(a),lty=1, col=2,lwd=1)

– Scatter plot using car package
library(car)
scatterplot(cars$speed~cars$dist,smooth=F, main="Scatter Plot", ylab="Speed",xlab="Distance")
– Multiple interaction plots: Biểu đồ tương quan đa biến
library(psych)
df<-Cars93
head(df)
library(tidyverse)
df1<- df %>% dplyr::select(4:8)
pairs.panels(df1)
Graphics with ggplot2, a “masterpiece” of R data visualization
library(ggplot2)
a<-rnorm(2000)
df<-data.frame(a=a, b=gl)
# ggplot2
ggplot(data=df,aes(x=a)) + geom_histogram(binwidth = 0.1,aes(y=..density..),color=4,fill="green") + geom_density() + labs(x="Values",y="Density", title="Density Plot",caption="Tuyen, V. Ha - Massey University, NZ")
# Facets
ggplot(data=Cars93,aes(x=Price)) + geom_histogram(binwidth = 0.1,aes(y=..density..),color=4,fill="green") + geom_density() + labs(x="Values",y="Density", title="Density Plot",caption="Tuyen, V. Ha - Massey University, NZ") + facet_wrap(~Type)
- Descritive statistics: Thống kê mô tả
summary(df1)
num Letter
Min. : 1.00 y :3
1st Qu.: 5.75 b :2
Median :10.50 l :2
Mean :10.50 o :2
3rd Qu.:15.25 x :2
Max. :20.00 c :1
(Other):8
# Psych can summarise data as follow
library(psych) # this package can give standard error
describe(df1)
vars n mean sd median trimmed mad min max range skew kurtosis
num 1 20 10.50 5.92 10.5 10.50 7.41 1 20 19 0.00 -1.38
Letter* 2 20 8.15 4.46 8.5 8.31 5.93 1 14 13 -0.17 -1.40
se
num 1.32
Letter* 1.00
# Summarizing data under group (psych package)
df2<-Cars93[,c(3:8)]
Error: object 'Cars93' not found
– Categorical descritive statistics
# For one categorical variable
library(tables)
tabular(Type~1,data=Cars93) # Calculate the number of each type
tabular(Type~Origin*(n=1+ Percent("col")), data=Cars93) # Display both numbers and percent
– We can integrate different categorical variables to form a descrition statistics
library(tables)
tabular(Type*Origin~DriveTrain*(n=1 +Percent("col")),data=Cars93)
– Disriptive statistics for one variable
library(gmodels)
CrossTable(Type ,Origin, data=Cars93, prop.chisq = T,chisq = T,prop.r = T,prop.c = T,prop.t = T)
– tabular
tabular(Type~Origin* Width*(n=1+mean+sd+median),data=Cars93) # For one continous variable
tabular(Origin*Type~ (Width+Horsepower)*(n=1+mean+sd+median),data=Cars93)
- Test of Normal distribution
# Using Shapiro.test to check the normal distribution
shapiro.test(cars$speed)
# p-value>0.05 => It is normally distributed
hist(cars$speed)
# Another example
a<-rnorm(2000)
shapiro.test(a)
# p-value>0.05 => a is normally distributed
hist(a) # Check it out
– T-test: there are two types of t-test. Firstly, t-test for one variable while the second for two variables
- Example (t-test type 1): If my class’s mark average is known 7 for many years, but now I collect a recent class and calculate new average. Are there any difference between new and previous average marks?
*t-test formula
\[ t-test= \frac{\bar{x}-\mu}{S/\sqrt{n}} \]
Notes: n is sample size; S is standard deviation, x bar is an average of a sample; mu is a known population mean
# A known mark: 7
set.seed(123)
myclass<-rnorm(30, 7,1.5)
t.test(myclass, mu=7)
# There is a strong evidence to say that there is no difference between mean of 7 and myclass's mean (p-value=0.794)
# Assumption 2
t.test(myclass,mu=8)
# p-value=0.004166, and therefore there is difference between mean of 8 and myclass' mean
– t-test for two variables
Formula
\[ t-test= \frac{\bar{x_2}-\bar{x_1}}{SED} \] Note: SED=sqrt(SE1^2 + SE2^2) standard error
t.test(Cars93$Price~Cars93$Origin)
# It is likely to say that there is no difference in price between USA and Non-USA as p-value >0.005: Mức độ khác biệt giữa 2 nhóm không có ý nghĩa thống kê
# If variance is assummed to be equal
t.test(Cars93$Price~Cars93$Origin,var.equal=T)
– Test of variance
var.test(Cars93$Price~Cars93$Origin)
# There is difference between two origins. USA is greater 0.4779 non-USA and statistically significant (p-value<0.005)
– If our data is not normally distributed, we gonna have an alternative test called wilcox.test
# Check normal distribution of Price in Cars93 dataset
shapiro.test(Cars93$Price)
## Ho. Price is not normally distributed
##H1: Price is normally distributed
# there is a strong evidence to show that Price is not normally distributed due to a tiny small p-value
# Wilcoxon test
wilcox.test(Cars93$Price~Cars93$Origin)
# p-value >0.05 => no difference in price mean between two groups
– T.test for a pair of objects
Example: I want to test if any difference in satisfaction of a customer when they taste my product and other’s product.
myproduct<-c(56,60,58,70,95,82,62,56,76,87)
other<-c(87,56,65,67,48,46,53,59,71,63)
df<-data.frame(myproduct,other)
attach(df) # If you like
t.test(myproduct,other,paired = T)
# There is no difference in mean between myproduct and other
wilcox.test(myproduct,other,paired = T)
# The same result of t.test
– Test for Categorical variables
# A dataset derived from Massey University, NZ
df<-read.csv("https://raw.githubusercontent.com/tuyenhavan/Statistics/Dataset/CowMilk2008.csv",header=T,sep=";")
head(df)
– Frequency or counts
# Count the number of levels or things in each categorical variable
t<-table(df$Breed,df$Mast) # Number of breads in each mast
prop.table(t,1) # It means that in Breed group, 92% is from Mast_0 and 8% from Mast_1.
– Proportion test for one event
Example: There are 200 people in my study in which 112 males and 88 females. If I want to test if the proportion of male in this study is really larger than 50%?
prop.test(112,200,0.5)
# There is no evidence to say that the proportion of males in this study is not greater than 50% due to a large p-value
# Alternative test
binom.test(112,200,0.5) # The same result as above
– Proportion test for two groups
Example: A study included 100 people (A) using medicine and 120 (B) without medicine. After 2 years, it is observed that 13 people suffered from cancer from A, and 24 from B. Are there any difference in the proportion of suffers between two groups? or medicine effective?
suffer<-c(13,24)
total<-c(100,120)
prop.test(suffer,total)
# There is no difference between using medicine and without medicine
– Using different approaches to a multiple-proportion test prop.test(); binom.test(); chisq.test(); fisher.test
# Muốn biết tỷ lệ nữ giữa các nhóm có khác nhau ko? dung prop.test()
# Converting numeric values to factorical values for two categorical variables
df$Region<-as.factor(df$Region)
df$Mast<-as.factor(df$Mast)
# @ Want to know if any difference in proportion of Mast in each Region
table(df$Mast,df$Region)
Zero<-c(10245,9321,12496,13862)
total<-c(sum(10245,887),sum(9321,688),sum(12496,1686),sum(13862,913))
prop.test(Zero,total) # Test the difference in the proportion of Mast_0 for each region
#=> There is evidence to say that the proportion of Mast_0 is different in each Region
# Another example
table(df$Breed,df$Region)
CB<-c(5271,5142,7515,7473)
total<-c(sum(5271,4983,878),sum(5142,2945,1922),sum(7515,4795,1872),sum(7473,5145,2157))
prop.test(CB,total)
# There is evidence to say that there is difference in proportion of CB in each Region (p-value<0.05)
# Alternative test 1
chisq.test(df$Mast,df$Region)
# The same result as above
Linear Regression Model
– Corelation coefficients
# Create a simulation dataset
set.seed(123)
wt<-runif(100,69,110) # Weight for 100 people
ht<-wt+0.5*rnorm(100,150,10) # height for 100 people
df1<-round(data.frame(wt,ht))
head(df1)
– Find the correlation between ht and wt using Pearson’s correlation
\[ r=\sum_{i=1}^{n}\left( \frac{(x_i-\bar{x})*(y_i-\bar{y})}{\sqrt{\sum_{i=1}^{n}(x_i-\bar{x})^2*(\sum_{i=1}^{n}(y_i-\bar{y})^2)}} \right) \]
# Pearson's correlation
cor(wt,ht)
plot(ht~wt,col=3,pch=16)
– Correlation test
# Pearson's correlation test
cor.test(wt,ht)
# - H0: there is no correlation between wt and ht
# - H1: There is a correlation between wt and ht
#=> p-value<0.05 - there is evidence to say that correlation between ht and wt is statistically significant
– Kendall correlation test using non-normally distributed data
cor.test(wt,ht,method="kendall") # Non-parametric method
-Correlation is great, but we cannot use it to predict any output or a given data point. Therefore, we have to come up with an equation to estimate parameters and use it to predict a known x data point. For this reason, Linear regression model is very good choice for this problem.
Before, we gonna go further, let’s review some of the basic theory of a linear regression model. First, we look at a simple linear regression model. Mathematically, it is presented as follows:
\[ y_i=\alpha +\beta*x_i +\epsilon_i\]
The equation above is the population equation with epsilon is noise part of the model. In practice, we don’t know the alpha and beta parameters, and therefore we gonna use statistical or sample estimates alpha hat and beta hat to predict or inference the parameters. To do so, some assumptions are needed. For example, epsilon is normally distributed and mean is zero, and equally variance. A newly re-defined equation for sample data can be written as:
\[ \hat{y_i}= \hat{\alpha} + \hat{\beta}*x_i \]
In this approach, we have to find a line that fits data well. What I mean here is that it is believed that using least square method can be best to estimate the coefficients and minimize the model errors. Coeffients alpha hat and beta hat can be estimated as follow:
\[ \hat{\beta} = \frac{\sum_{i=1}^{n}(x_i-\bar{x})*(y_i-\bar{y})}{\sum_{i=1}^{n}(x_i-\bar{x})^2} ; \hat{\alpha}= \bar{y}-\hat{\beta}*\bar{x} \]
Notes: alpha hat and beta hat are sample estimates, y hat is predicted values using the above equation. Residuals can be estimated as
\[ Residuals=y_i -\hat{y_i} \]
and variance of residuals (s^2) is a estimate of epsilon and can be written as
\[ s^2 = \frac{\sum_{i=1}^{n}(y_i-\hat{y_i})}{n-2} \]
–Solving regression problem using R
This task can be easily achieved in R with a simple command line as follow
m1<-lm(ht~wt,data=df1)
# Get the summary of the model
summary(m1)
There are three important components in the above model: Residuals; Coefficients and residual standard error, R-squared and F-statistics
R-squared (coefficient of determination) can be estimated as:
\[R^2= \frac{\sum_{i=1}^{n}(\hat{y_i}-\bar{y})^2}{\sum_{i=1}^{n}(y_i-\bar{y})^2} \]
In linear regression context, it is assumed some criteria, and should be satisfied it.
epsilon follows normal distribution
epsilon has zero mean
Variance of epsilon should be fixed at any given point x
Values of epsilon is independent; epsilon 1 is independent from epsilon 2
– Check if our model is satisfied the condition
Investigate each plot to see if they are met or not?
par(mfrow=c(2,2))
plot(m1)
– Graphic plots
pred1<-predict.lm(m1,newdata = data.frame(wt=df1$wt), interval = "prediction")
mp<-data.frame(pred1,wt=df1$wt)
plot(df1$ht~df1$wt,col=3,pch=16,main="Height is a function of Weight",xlab="Weight",ylab="Height")
abline(m1)
lines(mp$lwr~mp$wt,col=2,lwd=1)
lines(mp$upr~mp$wt,col=4,lwd=1)
— Predict new given x values
# Create x data points
wt1<-data.frame(wt=c(67,90,78,88,82,57,110,108))
y_pred1<-predict.lm(m1,newdata = wt1,interval = "confidence") # Confidence interval
y_pred2<-predict.lm(m1,newdata = wt1,interval = "prediction") # Prediction iterval
plot(ht~wt,col=4,main="Height is a function of Weight",pch=16,xlab="Weight (kg)",ylab="Height (cm)")
# Adding some lines
res1<-cbind(y_pred1,wt1)
res2<-cbind(y_pred2,wt1)
lines(res1$fit~res1$wt,col=1)
lines(res1$upr~res1$wt,col=3)
lines(res1$lwr~res1$wt,col=3)
lines(res2$lwr~res2$wt,col=2,lty=2)
lines(res2$upr~res2$wt,col=2,lty=2)
- Multiple linear regression
The theory in multiple linear regression is similar to simple linear regression’s theory. However, more independent variables are adding to the model.
# Using the following dataset to predict ...
library(MASS)
head(Boston)
names(Boston)
[1] "crim" "zn" "indus" "chas" "nox" "rm" "age"
[8] "dis" "rad" "tax" "ptratio" "black" "lstat" "medv"
bh<-Boston[,c(1,7,8,13,14)]
head(bh)
# Multiple linea regression
ml<-lm(medv~., data=bh)
summary(ml)
Call:
lm(formula = medv ~ ., data = bh)
Residuals:
Min 1Q Median 3Q Max
-16.768 -3.853 -1.503 2.131 22.966
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 38.776557 1.651987 23.473 < 2e-16 ***
crim -0.106856 0.035921 -2.975 0.00307 **
age -0.003031 0.015743 -0.193 0.84741
dis -0.789790 0.196320 -4.023 6.63e-05 ***
lstat -0.999954 0.050022 -19.990 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 6.056 on 501 degrees of freedom
Multiple R-squared: 0.5698, Adjusted R-squared: 0.5664
F-statistic: 165.9 on 4 and 501 DF, p-value: < 2.2e-16
– Using transformation to refit the linear regression model
head(trees)
# Plot the scatter
plot(trees$Volume~trees$Height)
mt1<-lm(Volume~Height,data=trees) # Simple linear regression
summary(mt1)
Call:
lm(formula = Volume ~ Height, data = trees)
Residuals:
Min 1Q Median 3Q Max
-21.274 -9.894 -2.894 12.068 29.852
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -87.1236 29.2731 -2.976 0.005835 **
Height 1.5433 0.3839 4.021 0.000378 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 13.4 on 29 degrees of freedom
Multiple R-squared: 0.3579, Adjusted R-squared: 0.3358
F-statistic: 16.16 on 1 and 29 DF, p-value: 0.0003784
# Another Example
head(Animals)
## Fitting a simple linear model
ma1<-lm(brain~body, data=Animals)
summary(ma1)
Call:
lm(formula = brain ~ body, data = Animals)
Residuals:
Min 1Q Median 3Q Max
-576.0 -554.1 -438.1 -156.3 5138.5
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.764e+02 2.659e+02 2.168 0.0395 *
body -4.326e-04 1.589e-02 -0.027 0.9785
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1360 on 26 degrees of freedom
Multiple R-squared: 2.853e-05, Adjusted R-squared: -0.03843
F-statistic: 0.0007417 on 1 and 26 DF, p-value: 0.9785
par(mfrow=c(2,2))

plot(ma1)
# It is clear that this model is not met. How about transformation both x and y
mt2<-lm(log(brain)~log(body),data=Animals)
summary(mt2)
Call:
lm(formula = log(brain) ~ log(body), data = Animals)
Residuals:
Min 1Q Median 3Q Max
-3.2890 -0.6763 0.3316 0.8646 2.5835
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.55490 0.41314 6.184 1.53e-06 ***
log(body) 0.49599 0.07817 6.345 1.02e-06 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 1.532 on 26 degrees of freedom
Multiple R-squared: 0.6076, Adjusted R-squared: 0.5925
F-statistic: 40.26 on 1 and 26 DF, p-value: 1.017e-06
par(mfrow=c(2,2))

plot(mt2)

-Choosing a best model from a pool of possible models :)
It is obvious that there are so many possible models that we can build depending the number of independent variables. However, We only choose one model that can be best explained the variations in y.To do so, a indicator called AIC Akaike Information Criterion which can be used to sellect statistically significant variables. This creteria can be identified as
\[ AIC=log(\frac{RSS}{n})+\frac{2k}{n}; \ \ which \ \ RSS=\sum_{i=1}^{n}(\hat{y_i}-y_i)^2, \ \ k=number \ of \ independent \ variables, \ n=No \ of \ observations \] We choose a model with the smallest AIC
– Practice choosing a optimal model using AIC criteria
head(CO2)
Analysis of Variance
– One-way analysis of variance (ANOVA)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIHR1dG9yaWFsIGlzIHRvIGRlbW9uc3RyYXRlIGhvdyB0byBSIGFzIGEgdG9vbCBmb3IgZGF0YSBzY2llbmNlLiANCg0KLSBCZWZvcmUgd2UgZGl2ZSBpbnRvIFIgc3ludGF4LCBJIHdhbnQgdG8gY2xhcmlmeSBzb21lIG9mIHRoZSBjb21tb24gdGVybWlub2xvcmllcyBpbiBzdGF0aXN0aWNzIGFuZCBSIHByb2dyYW1taW5nLg0KDQoqIFZhcmlhYmxlPWZlYXR1cmU9Y29sdW1uDQoNCiogT2JzZXJ2YXRpb249cm93PWNhc2UNCg0KKiBJbmRlcGVuZGVudCB2YXJpYWJsZT1wcmVkaWN0b3JzICh2YXJpYWJsZXMpPXJlZ3Jlc3NvcnMNCg0KKiBEZXBlbmRlbnQgdmFyaWFibGU9cmVzcG9uc2UgdmFyaWFibGUNCg0KKiBWZWN0b3I9IGEgc2luZ2xlIGNvbHVtbiBvciB2YXJpYWJsZQ0KDQoqIEEgZGF0YWZyYW1lID0gYSBkYXRhc2V0IHdpdGggcm93cyBhbmQgY29sdW1ucw0KDQoqIGBOQWAgaXMgbWlzc2luZyB2YWx1ZXMNCg0KKiBOdW1lcmljIGRhdGE6IERpc2NyZXRlIGRhdGE7IGNvbnRpbnVvdXMgZGF0YQ0KDQoqIENhdGVnb3JpY2FsIGRhdGE6IE5vbWlhbCBkYXRhLCBvcmRpbmFsIGRhdGEgDQoNCiogRmFjdG9yczogYSBjYXRlZ29yaWNhbCBkYXRhIHdpdGggbWF0aGVtYXRpY2FsIGNvbXB1dGF0aW9uDQoNCiogUmFuZG9tIHdpdGggcmVwbGFjZW1lbnQ6IFdobyBrbm93cz8NCg0KIyBMZXQncyBleHBlcmllbmNlIHRoZSBmaXJzdCB0YXN0ZSBvZiBSIA0KDQotLSBUeXBlIGEgcmFuZG9tIG51bWJlciBpbiBSIGNvbnNvbGUgDQoNCmBgYHtyfQ0KNA0KIyBTdG9yZSA0IGluIGFuIG9iamVjdCBuYW1lZCBmb3VyDQoNCmZvdXI9NA0KIyBDYWxsIG91dCB0aGUgb2JqZWN0IDQNCg0KZm91cg0KDQpgYGANCg0KLS0gVHlwZSBhIHJhbmRvbSBjaGFyYWN0ZXIgaW4gUiBjb25zb2xlIA0KDQpgYGB7cn0NCg0KIyBXaHkgdGkgdGhyb3dzIGFuIGVycm9yPw0KDQojIEhvdyBjYW4gd2Ugd3JpdGUgYSBjaGFyYWN0ZXIgaW4gUg0KDQoiVW5pdmVyc2l0eSI7ICdUaGFpIE5ndXllbicNCiMgR2l2ZSBpdCBhIG5hbWUNCg0KbXljaGE8LSJVbml2ZXJzaXR5Ig0KDQpteWNoYTwtYygiVW5pdmVyc2l0eSIsIlRoYWluZ3V5ZW4iKQ0KDQpteWNoYQ0KDQojIGhvdyB0byBjcmVhdGUgYSBsaXN0IG9mIDEwIG51bWJlcnMgZnJvbSAxIHRvIDEwDQoNCm15bnVtPC1jKDE6MTApDQoNCm15bnVtDQpgYGANCg0KIyBEaXZlIGluIFIgDQoNCi0gSG93IHRvIGdyZW5lcmF0ZSBhIGxpc3Qgb2YgcmFuZG9tbHkgbm9ybWFsIGRpc3RyaWJ1dGVkIG51bWJlcnMNCg0KYGBge3J9DQp4PC1ybm9ybSgxMDAwKQ0KIyBDaGVjayB0aGUgbGVuZ3RoIG9mIHgNCg0KbGVuZ3RoKHgpDQoNCiMgUGxvdCB0aGUgaGlzdG9ncmFtIG9mIHgNCg0KaGlzdCh4LCB4bGFiID0gIlZhbHVlcyIsIHlsYWI9IkZyZXF1ZW5jeSIsIG1haW49Ikhpc3RvZ3JhbSBvciBSYW5kb21seSBkaXN0cmlidXRlZCBWYWx1ZXMiLGNvbD1yYWluYm93KDIwKSkNCg0KYGBgDQoNCi0gU29tZSBvZiB0aGUgY29tbW9uIG5vdGF0aW9ucyB1c2VkIGluIFIgYD1gLCBgPC1gLCBgPT1gLCBgIWAsIGBpcy5uYSgpYCwgYCZgLCBgfGAsIGFuZCBgPiwgPj1gIA0KDQpgYGB7cn0NCiMgPSBvciA8LQ0KDQphPC01IA0KDQphPTUNCg0KIyA9PQ0KDQphPT00DQoNCiMgZGlmZmVyDQoNCmEhPTUgIyBhIGlzIGRpZmZlcmVudCBmcm9tIDUNCg0KYSE9OCAjIGEgaXMgZGlmZmVyZW50IGZyb20gNQ0KDQojID4gb3IgPj0sIDwuIDw9LCBpcy5uYSgpLCBBfEIsIEEmQg0KDQpBPC01DQoNCkI8LTYNCg0KQT5CDQpBPEINCg0KIyBDaGVjayB0aGUgY3VycmVudCBkYXRlDQoNClN5cy5EYXRlKCkNCg0KYGBgDQoNCi0gSWYgeW91IHdhbnQgdG8ga25vdyBhbnkgZnVuY3Rpb25zIHRoYXQgc3RhcnQgd2l0aCBgbG1gLCB0eXBlIHRoZSBmb2xsb3dpbmcgY29tbWFuZA0KDQpgYGB7cn0NCg0KYXByb3Bvcygibm9ybSIpDQoNCmBgYA0KDQojIEhvdyB0byByZWFkIGRhdGEgaW4gUg0KDQotIEhvdyB0byBtYW5udWFsbHkgZ2VuZXJhdGUgdHdvIGNvbHVtbnMgb2YgZGF0YSB3aXRoIG9uZSBjb2x1bW4gYGhpZ2h0YCBhbmQgYGFnZWANCg0KYGBge3J9DQojIHNldCB0aGUgZml4ZWQgc3RhdGUNCg0Kc2V0LnNlZWQoMTEyKQ0KDQpIZWlnaHQ8LXJvdW5kKHJ1bmlmKDIwLDE0MCwxOTApLDEpICMgVW5pdCBpbiBjZW50ZW10dGVyIA0KDQpBZ2U8LXJvdW5kKHJ1bmlmKDIwLDEwLDcwKSkNCg0KZGY8LWRhdGEuZnJhbWUoSGVpZ2h0LEFnZSkNCg0KaGVhZChkZikNCg0KIyBJZiB5b3Ugd2FudCB0byBmaXggdGhlIGRhdGFzZXQsIHlvdSBjYW4gdHlwZSBlZGl0KGRmKSBvciBmaXgoZGYpDQoNCiMjIEFOb3RoZXIgd2F5IG9mIGNyZWF0aW5nIGEgZGF0YXNldA0KDQpzZXQuc2VlZCgxMjMpDQoNCm51bTwtYygxOjIwKSAjIENyZWF0ZSBhIHNlcXVlbmNlIG9mIG51bWJlciBzdGFydGluZyBmcm9tIDEgYW5kIGVuZGluZyBhdCAyMA0KDQpMZXR0ZXI8LXNhbXBsZShsZXR0ZXJzLDIwLCByZXBsYWNlID0gVCkgIyBDcmVhdGluZyBhIGxpc3Qgb2YgY2hhcmFjdGVycyByYW5kb21seSBzZWxlY3RlZCBmcm9tIDI2IEVuZ2xpc2ggbGV0dGVycw0KDQoNCmRmMTwtZGF0YS5mcmFtZShudW0sIExldHRlcikgIyBGb3JtIGEgZGF0YWZyYW1lIA0KDQpoZWFkKGRmMSkNCg0KYGBgDQoNCi0gUmVhZCBkYXRhIGZyb20gb3VyIGxvY2FsIGNvbXB1dGVyIGFuZCB3ZWJzaXRlLCBhbmQgaW4gdGhpcyBjYXNlIGlzIGBnaXRodWJgIA0KDQotLSBSZWFkIGRhdGEgaW4gYC5jc3ZgIGZvcm1hdCANCg0KYGBge3J9DQojIFJlYWQgZGF0YSBmcm9tIG91ciBsb2NhbCBjb21wdXRlcg0KDQojIGRmPC1yZWFkLmNzdihmaWxlLmNob29zZSgpLGhlYWRlcj1UKQ0KDQojIFJlYWQgZGF0YSBpbiBjc3YgZm9ybWF0IGZyb20gd2Vic2l0ZQ0KDQpCbHVlX3NoZXJkPC1yZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3R1eWVuaGF2YW4vU3RhdGlzdGljcy9tYXN0ZXIvQmx1ZV9TaGVyZC5jc3YiLGhlYWRlcj1UKQ0KDQpoZWFkKEJsdWVfc2hlcmQpICMgQ2hlY2sgdGhlIGZpcnN0IGZldyByb3dzIG9mIHRoZSBkYXRhc2V0DQoNCnRhaWwoQmx1ZV9zaGVyZCkgIyBWaWV3IGxhc3QgZmV3IHJvd3Mgb2YgdGhlIGRhdGFzZXQNCg0KIyBBbm90aGVyIGRhdGFzZXQNCg0KU3VydmV5PC1yZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3R1eWVuaGF2YW4vTGFuZC1SZWxvY2F0aW9uLVN1cnZleS1EYXRhL1NhbXBsZS1EYXRhL1NhbXBsZV9EYXRhMS5jc3YiLGhlYWRlcj1ULHNlcD0iOyIpDQoNCmhlYWQoU3VydmV5KSAjIENoZWNrIHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgZGF0YXNldA0KDQp0YWlsKFN1cnZleSkgIyBDaGVjayB0aGUgbGFzdCBmZXcgcm93cyBvZiB0aGUgZGF0YXNldA0KDQoNCmBgYA0KDQotLSBSZWFkIGRhdGFzZXQgaW4gYC50eHRgIGZvcm1hdCBpbiBSDQoNCmBgYHtyfQ0KDQpteWxhbmRfc3VydmV5PC1yZWFkLnRhYmxlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdHV5ZW5oYXZhbi9MYW5kLVJlbG9jYXRpb24tU3VydmV5LURhdGEvU2FtcGxlLURhdGEvU2FtcGxlX0RhdGExLnR4dCIsaGVhZGVyPVQpDQoNCmhlYWQobXlsYW5kX3N1cnZleSkgIyBDaGVjayB0aGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIGRhdGFzZXQNCg0KIyBXZSBjYW4gY2hlY2sgdGhlIHZhcmlhYmxlIG5hbWVzDQoNCm5hbWVzKG15bGFuZF9zdXJ2ZXkpDQoNCmRpbShteWxhbmRfc3VydmV5KSAjIFNob3dpbmcgdGhlIG51bWJlciBvZiByb3dzIGFuZCBjb2x1bW5zDQoNCmBgYA0KDQoNCi0tIFdlIGNhbiByZWFkIGRhdGEgZnJvbSBgU1BTU2AgdXNpbmcgYGZvcmVpZ25gIHBhY2thZ2UNCg0KYGBge3J9DQpsaWJyYXJ5KGZvcmVpZ24pDQoNCiMgIGRmMjwtcmVhZC5zcHNzKGZpbGUuY2hvb3NlKCksIHRvLmRhdGEuZnJhbWUgPSBULCBlbmNvZGluZz0iY3AxMjUyIikNCg0KYGBgDQoNCi0tIFJlYWRpbmcgZGF0YSBpbiBSIHVzaW5nIGByZWFkcmAgcGFja2FnZSAocmVmZXJlbmNlKQ0KDQpgYGB7cn0NCmxpYnJhcnkocmVhZHIpDQoNCmBgYA0KDQotLSBjb252ZXJ0aW5nIGEgY2VydGFpbiBkYXRhIHR5cGUgdG8gYW5vdGhlciB1c2luZyBwYXJzZV8NCg0KYGBge3J9DQphPC1jKDEsMiwiSGEiKQ0KDQojIENvbnZlcnQgYSB0byBpbnRlZ2VyDQoNCnBhcnNlX2ludGVnZXIoYSkNCg0KIyBwYXJzZV9udW1iZXIoKQ0KDQpwYXJzZV9udW1iZXIoIjIzMCUiKQ0KDQpwYXJzZV9udW1iZXIoInRoaXMgaXMgMTAwJCIpDQoNCiMgT3RoZXIgcGFyc2VycyBsaWtlIHBhcnNlX2xvZ2ljYWwoKSwgcGFyc2VfY2hhcmFjdGVyKCksIHBhcnNlX2RvdWJsZSgpLCANCmBgYA0KDQoNCi0gRGF0YSBvcmdhbml6YXRpb24gYW5kIG1hbmlwdWxhdGlvbg0KDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgZGVtb25zdHJhdGUgdGhlIGFiaWxpdHkgb2YgUiBmb3IgZGF0YSBtYW5pcHVsYXRpb24gYW5kIG9yZ2FuaXphdGlvbi4gd2hpbGUgdGhpcyBqb2IgY2FuIGJlIGRvbmUgZGlmZmljdWx0bHkgaW4gZXhjZWwgb3Igb3RoZXIgc29mdHdhcmUsIFIgY2FuIGltcGxlbWVudCBpdCBlYXNpbHkgd2l0aCB0aGUgaGVscCBvZiBhIHBvd2VyZnVsIHBhY2thZ2UgY2FsbGVkIGB0aWR5cnZlc2VgLCBgZHBseXJgIGFuZCBgdGlkeXJgIGJ5IEhhZGx5IFdpY2toYW0gDQoNCmBgYHtyfQ0KIyBMb2FkIHBhY2thZ2VzDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpteWRhdGE8LXJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vdHV5ZW5oYXZhbi9MYW5kLVJlbG9jYXRpb24tU3VydmV5LURhdGEvbWFzdGVyL0RhdGFmcm9tc3Bzcy5jc3YiLGhlYWRlcj1UKQ0KDQpoZWFkKG15ZGF0YSkNCg0KYGBgDQoNCi0tIEdldCBhbGwgYE1BTEVTYCBmcm9tIHRoZSBkYXRhc2V0IA0KDQpgYGB7cn0NCiMgQW5vdGhlciBvbmUNCg0KbWFsZXM8LSBteWRhdGEgJT4lIGRwbHlyOjpmaWx0ZXIoc2V4PT0iTUFMRVMiKQ0KDQpoZWFkKG1hbGVzKQ0KDQojIEFub3RoZXIgZGF0YXNldA0KDQpoZWFkKGRpYW1vbmRzKQ0KDQojIGdldCBhbGwgZGlhbW9uZHMgd2l0aCBjdXQ9PUlkZWFsIGFuZCBHb29kLCBjbGFyaXR5PSBTSTIgYW5kIFZTMQ0KDQpjdXRfY2xhcml0eTwtIGRpYW1vbmRzICU+JSBkcGx5cjo6ZmlsdGVyKGN1dD09YygiSWRlYWwiLCJHb29kIikgJiBjbGFyaXR5PT1jKCJTSTIiLCJWUzEiKSApDQoNCmhlYWQoY3V0X2NsYXJpdHkpDQpgYGANCg0KLSBIb3cgdG8gc2VsZWN0IGNvbHVtbnMgYW5kIHJvd3MgdG9nZXRoZXINCg0KYGBge3J9DQpteWRhdGE8LSBkaWFtb25kcyAlPiUgZHBseXI6OmZpbHRlcihjdXQ9PSJHb29kIikgJT4lIGRwbHlyOjpzZWxlY3QoMTo1KQ0KDQpoZWFkKG15ZGF0YSkNCmBgYA0KDQotLSBIb3cgdG8gZW5jb2RlIGEgY29udGlub3VzIHZhcmlhYmxlIGludG8gYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSANCg0KYGBge3J9DQoNCiMgaWYgcHJpY2UgZnJvbSAwIHRvIDUwMDAgPUxvdywgNTAwMDxwcmljZTw9ODAwMD1NZWRpdW0sIDgwMDA8cHJpY2U8PTEwMDAwID1IaWdoLD4xMDAwMD1WZXJ5IGhpZ2gNCg0KbXlkZjwtZGlhbW9uZHMgJT4lIG11dGF0ZShwcmljZV9FbmNvZGU9Y3V0KHByaWNlLGJyZWFrcyA9IGMoMCwgNTAwMCwgODAwMCwxMDAwMCwgMjAwMDApLGxhYmVscz1jKCJMb3ciLCJNZWRpdW0iLCJIaWdoIiwiVmVyeSBIaWdoIiksaW5jbHVkZS5sb3dlc3QgPSBUKSkNCg0KaGVhZChteWRmKQ0KDQp0YWlsKG15ZGYpDQoNCmBgYA0KDQoNCi0tIEhvdyB0byBjcmVhdGUgYSBmYWN0b3JpYWwgdmFyaWFibGUNCg0KYGBge3J9DQojIENyZWF0ZSBhIGRhdGFmcmFtZQ0KDQphPC1jKDE6MjApDQoNCmI8LWMoMjE6NDApDQoNCmRmMzwtZGF0YS5mcmFtZShhLGIpDQoNCmhlYWQoZGYzKQ0KDQojIFVzaW5nIGBjdXRgIHRvIGNhdGVnb3JpemUgdGhlIHZhcmlhYmxlIA0KDQpkZjMkY3V0MTwtY3V0KGRmMyRhLDMpDQoNCmhlYWQoZGYzKQ0KDQojIEFub3RoZXINCg0KZGYzJGN1dDI8LWN1dChkZjMkYSwzLCBsYWJlbHM9YygiTG93IiwiTWlkZGxlIiwiSGlnaCIpKQ0KDQpoZWFkKGRmMykNCg0KIyBBbm90aGVyIHdheQ0KDQpkZjMkY3V0MzwtY3V0KGRmMyRhLCBicmVha3MgPSBjKDAsNSwxMCwxNSwyMSksIGxhYmVscz1jKCJMb3ciLCJNaWRkbGUiLCJSZWxhdGl2ZSBIaWdoIiwiSGlnaCIpLCBpbmNsdWRlLmxvd2VzdCA9IFQpDQoNCmhlYWQoZGYzKQ0KYGBgDQoNCi0tIGN1dCBhbiBlcXVhbCBpbnRlcm52YWwgb2YgdmFsdWVzIHVzaW5nIGBjdXQyYCBmdW5jdGlvbiBmcm9tIGBIbWlzY2AgcGFja2FnZSANCg0KYGBge3J9DQpsaWJyYXJ5KEhtaXNjKQ0KIyBkZXZpZGUgYSBpbnRvIDMgZ3JvdXBzIHdpdGggYW4gZXF1YWwgaW50ZXJ2YWwgZnJvbSBzbWFsbGVzdCB0byBiaWdnZXN0IHZhbHVlcw0KZGYzJGN1dGg8LWN1dDIoZGYzJGEsIGc9MykNCg0KaGVhZChkZjMsMjApDQoNCmBgYA0KDQotIFNvbWUgb2YgdGhlIG1hdGhlbWF0aWNhbCBvcGVyYXRpb25zDQoNCmBgYHtyfQ0KIyBlXjEyMw0KDQpleHAoMTIzKQ0KDQojIGxvZ19lKCkNCg0KbG9nKDEyKQ0KDQojIGxvZzEwKDEwMCkNCg0KbG9nMTAoMTAwKQ0KDQojIGNvcyhwaSkNCg0KY29zKHBpLzQpDQoNCiMgc3VtIG9mIHNxdWFyZTogdOG7lW5nIGLDrG5oIHBoxrDGoW5nDQoNCng8LWMoMTo1KQ0KDQpzdW0oeF4yKSAjIHN1bSBvZiBzcXVhcmUgPSAxXjIrMl4yKzNeMis0XjIrNV4yDQoNCmBgYA0KDQotLSBBZGp1c3RlZCBzdW0gb2Ygc3F1YXJlOiB04buVbmcgYsOsbmggcGjGsMahbmcgxJFp4buBdSBjaOG7iW5oDQoNCiQkIEE9IFxzdW1fe2k9MX1ebiAoeF9pLVxiYXJ7eH0pXjIgICAkJA0KDQpgYGB7cn0NCiMgQWRqdXN0ZWQgc3VtIG9mIHNxdWFyZSANCg0KeDwtYygxOjUpDQoNCnN1bSgoeC1tZWFuKHgpKV4yKQ0KDQpgYGANCg0KLS0gbWVhbiBzcXVhcmU6IFTDrW5oIHNhaSBz4buRIGLDrG5oIHBoxrDGoW5nDQoNCiQkIFg9IFxzdW1fe2k9MX1ebiAoeF9pLVxiYXJ7eH0pXjIvbiAkJA0KDQpgYGB7cn0NCng8LWMoMTo1KSAjIENyZWF0ZSBhIHZlY3Rvcg0KDQpzdW0oKHgtbWVhbih4KSleMikvbGVuZ3RoKHgpICMgbWVhbiBzcXVhcmUgDQoNCmBgYA0KDQotLSBWYXJpYW5jZTogVMOtbmggcGjGsMahbmcgc2FpDQoNCiQkIFNeMiA9IFxzdW1fe2k9MX1ebiAoeF9pLVxiYXJ7eH0pXjIvKG4tMSkgICQkDQoNCmBgYHtyfQ0KeDwtYygxOjUpDQoNCnZhcjE8LXN1bSgoeC1tZWFuKHgpKV4yKS8obGVuZ3RoKHgpLTEpICMgUGjGsMahbmcgc2FpIA0KDQp2YXIoeCkNCg0KYGBgDQoNCi0tIFN0YW5kYXJkIGRldmlhdGlvbjogxJDhu5kgbOG7h2NoIGNodeG6qW4NCg0KJCQgUz1cc3FydHtTXjJ9ICQkIA0KDQoNCmBgYHtyfQ0KIyBTdGFuZGFyZCBkZXZpYXRpb24gb2YgeA0KDQpTPXNxcnQodmFyMSkNCg0KUw0KDQpzZCh4KSAjIGFub3RoZXIgd2F5IA0KYGBgDQoNCg0KLS0gSG93IHRvIGNyZWF0ZSBkYXRlIHRpbWUgZGF0YQ0KDQpgYGB7cn0NCmRhdGUxPC1hcy5EYXRlKCIxNy8xMi8yMyIsZm9ybWF0PSIleS8gJW0vICVkIiApDQoNCmRhdGUxDQoNCiMgSG93IHRvIGNyZWF0ZSBvZiBsaXN0IGRhdGUgYmV0d2VlbiAyMDEwIGFuZCAyMDE3DQoNCmRhdGUyPC1zZXEoYXMuRGF0ZSgiMjAxMC0wMS0wMSIpLCBhcy5EYXRlKCIyMDE3LTEyLTAxIiksYnk9J21vbnRoJykNCg0KaGVhZChkYXRlMikNCg0KIyBieSAyIGRheXMNCg0KZGF0ZTI8LXNlcShhcy5EYXRlKCIyMDEwLTAxLTAxIiksIGFzLkRhdGUoIjIwMTctMTItMDEiKSxieT0nMiBkYXlzJykNCg0KaGVhZChkYXRlMikNCg0KIyBieSAyIHdlZWtzDQoNCmRhdGUyPC1zZXEoYXMuRGF0ZSgiMjAxMC0wMS0wMSIpLCBhcy5EYXRlKCIyMDE3LTEyLTAxIiksYnk9JzIgd2Vla3MnKQ0KDQpoZWFkKGRhdGUyKQ0KYGBgDQoNCiMgSG93IHRvIGdlbmVyYXRlIHJhbmRvbSBudW1iZXJzIG9yIGxldmVscyB1c2luZyBgc2VxYCwgYHJlcGAgYW5kIGBnbGANCg0KYGBge3J9DQojIENyZWF0ZSBhIHNlcXVlbnRpYWwgbnVtYmVycw0KDQpzZXEoMSwxMCxieT0wLjIpDQoNCiMgQ3JlYXRlIGEgcmVwZXRpdGlvbiBsaXN0IG9mIG51bWJlcnMNCg0KcmVwKDEwLHRpbWVzPTEwKQ0KDQpyZXAoIkhhIFZhbiBUdXllbiIsdGltZXM9MTApDQoNCnJlcChjKDE6MTApLDMpDQoNCiMgY3JlYXRlIGEgbGlzdCBsZXZlbHMNCg0KZ2woMywgNikgIyAzIGxldmVscyBhbmQgNiozPTE4IGl0ZW1zDQoNCiMgbGV2ZWxzIHdpdGggbm90YXRpb25zDQoNCmdsKDMsNSxsZW5ndGggPSAyMCwgbGFiZWw9YygiSGEiLCJWYW4iLCJUdXllbiIpKQ0KDQojIENyZWF0ZSBhIHJlcGV0YXRpb24gb2YgZXZlcnkgMg0KDQpyZXAoMToxMCwgZWFjaD0yKQ0KDQoNCmBgYA0KDQotLSBDcmVhdGUgYSBtYXRyaXgNCg0KYGBge3J9DQojIENyZWF0ZSBhIHNxdWFyZWQgbWF0cml4DQoNCkE8LW1hdHJpeChjKDE6OSksbnJvdz0zLG5jb2w9MyxieXJvdyA9IFQpDQoNCkENCmBgYA0KDQotLSBUcmFuc3Bvc2VkIG1hdHJpeDogTWEgdHLhuq1uIGNodXnhu4NuIHbhu4sNCg0KYGBge3J9DQp0KEEpICMgVGhpcyB3aWxsIHRyYW5zcG9zZSBtYXRyaXggQSB0byB0cmFuc3Bvc2VkIG1hdHJpeA0KDQpgYGANCg0KLS0gU2NhbGFyIG1hdHJpeDogTWEgdHLhuq1uIHbDtCBoxrDhu5tuZw0KDQpgYGB7cn0NCkI8LW1hdHJpeCgwLDQsNCkNCg0KZGlhZyhCKTwtMQ0KDQpCDQpgYGANCg0KLS0gTWF0cml4IG9wZXJhdGlvbnMNCg0KYGBge3J9DQpDPC1tYXRyaXgoMTo5LCBuY29sPTMsbnJvdz0zKQ0KDQpEPC1tYXRyaXgoLTE6LTksbmNvbD0zLCBieXJvdyA9IFQpDQoNCiMgQWRkaXRpb24NCg0KQytEDQoNCiMgU3VidHJhY3Rpb24NCg0KQy1EDQoNCiMgTXVsdGlwbGljYXRpb24NCg0KQyUqJUQNCg0KYGBgDQoNCi0tIFBlcm11dGF0aW9uOiBIb8OhbiB24buLIGBwcm9kYA0KDQpgYGB7cn0NCiMgSGF2aW5nIDUgc3R1ZGVudHMgYW5kIDUgY2hhaXJzLiBIb3cgbWFueSB3YXlzIHRvIG9yZ2FuaXplIGZpdmUgc3R1ZGVudHMgaW4gNSBjaGFpcnMNCg0KcHJvZCg1OjEpDQoNCmBgYA0KDQotLSBDb21iaW5hdGlvbjogVOG7lSBo4bujcCANCg0KIyBHcmFwaGljcy0gQmnhu4N1IMSQ4buTIG9yIERhdGEgdmlzdWFsaXphdGlvbiAoQmFzZSBSKQ0KDQpgYGB7cn0NCiMgY3JlYXRlIGEgcmFuZG9tIHZhbHVlcw0KDQpOPC1ydW5pZigxMDAwLCAtNSw1KQ0KDQp4PC1ODQoNCnk8LXNpbih4KSArIDAuNSpybm9ybShOKQ0KIyBTY2F0dGVyIHBsb3QNCg0KcGxvdCh5fngsIG1haW49IlNjYXR0ZXIgUGxvdCIsIGNvbD1yYWluYm93KDEwMDApKQ0KDQojIEjDrXRvZ3JhbQ0KDQpoaXN0KHgsbWFpbj0iSGlzdG9ncmFtIikNCg0KIyBCYXJwbG90DQoNCmJhcnBsb3QoTixtYWluPSJCYXJwbG90Iixjb2w9MikNCg0KIyBCb3hwbG90DQoNCmJveHBsb3QoTixtYWluPSJCb3hwbG90IikNCg0KIyBHcm91cCBmb3VyIHBsb3RzIGludG8gb25lIHNpbmdsZSBib3gNCg0KcGFyKG1mcm93PWMoMiwyKSkNCg0KcGxvdCh5fngsIG1haW49IlNjYXR0ZXIgUGxvdCIsIGNvbD1yYWluYm93KDEwMDApKQ0KDQpoaXN0KHgsbWFpbj0iSGlzdG9ncmFtIikNCg0KYmFycGxvdChOLG1haW49IkJhcnBsb3QiLGNvbD0yKQ0KDQpib3hwbG90KHksIG1haW49IkJveHBsb3QiKQ0KDQpgYGANCg0KLS0gQWRkaW5nIGB0aXRsZWAsIGBzdWJ0aXRsZWAgYW5kIG90aGVycw0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbm9ybWFsIGRpc3RyaWJ1dGVkIHZhbHVlcw0KDQphPC1ybm9ybSgyMDAwKQ0KDQpoaXN0KGEseGxhYj0iVmFsdWVzIix5bGFiPSJGcmVxdWVuY3kiLGNvbD1yYWluYm93KDUwKSxtYWluPSJIaXN0b2dyYW0gUGxvdCIpDQoNCnRpdGxlKHN1Yj0iRmlndXJlIDE6IEhpc3RvZ3JhbSBzaG93aW5nIHRoZSByYW5kb21seSBkaXN0cmlidXRlZCB2YWx1ZXMiKQ0KYGBgDQoNCi0tIExlZ2VuZHM6IEdoaSBjaMO6DQoNCmBgYHtyfQ0KeDwtcnVuaWYoMjAwMCwtNSw1KQ0KDQp5PC0geCArIDAuNSpybm9ybSgyMDAwKQ0KDQpwbG90KHl+eCxtYWluPSJMaW5lYXIgUmVncmVzc2lvbiIscGNoPTE2LGNvbD00KQ0KDQphYmxpbmUobG0oeX54KSxjb2w9MikNCg0KbGVnZW5kKC01LDUsYygiUG9pbnQgcGF0dGVybnMiLCJSZWdyZXNzaW9uIExpbmUiKSxwY2g9MTYsbHR5PWMoMCwxKSxidHk9Im4iLGNvbD1jKDQsMikpDQoNCmBgYA0KDQotLSBCYXJwbG90DQoNCmBgYHtyfQ0KIyBDYXJzOTMgZGF0YXNldA0KDQpsaWJyYXJ5KE1BU1MpDQoNCmhlYWQoQ2FyczkzKQ0KDQpteWlyaXM8LXRhYmxlKENhcnM5MyRUeXBlKQ0KDQojIFR3ZWVrIHRoZSBwbG90IHBvc2l0aW9uDQoNCnBhcihtZnJvdz1jKDEsMikpDQoNCmJhcnBsb3QobXlpcmlzLGNvbD1jKDIsNCksbWFpbj0iQmFycGxvdCIpDQoNCmJhcnBsb3QobXlpcmlzLCBtYWluPSJCYXJwbG90IiwgaG9yaXogPSBULGNvbD1jKDMsNCkpDQoNCnRpdGxlKHN1Yj0iRmlndXJlIDI6IFRocmVlIHR5cGVzIG9mIGNhciAiKQ0KYGBgDQoNCi0tIEJhcnBsb3Qgd2l0aCB0d28gY2F0ZWdvcmljYWwgdmFyaWFibGVzIA0KDQpgYGB7cn0NCg0KbGlicmFyeShIbWlzYykNCg0KY3V0MjwtY3V0MihDYXJzOTMkUHJpY2UsZz00KQ0KDQpteXR3bzwtdGFibGUoQ2FyczkzJFR5cGUsY3V0MikNCiMgQ2hlY2sgdGhlIG9yZGVyIG9mIGxldmVscyBvZiBUeXBlDQoNCmxldmVscyhDYXJzOTMkVHlwZSkNCg0KYmFycGxvdChteXR3bywgYmVzaWRlID0gVCx4bGFiPSJQcmljZSBHcm91cCIsIGNvbCA9IGFzLm51bWVyaWMoQ2FyczkzJFR5cGUpLCB5bGFiPSJGcmVxdWVuY3kiKQ0KYGBgDQoNCi0tIERlbnNpdHkgcGxvdA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgcmFuZG9tbHkgbm9ybWFsIGRpc3RyaWJ1dGVkIGRhdGFzZXQNCg0KYTwtcm5vcm0oMjAwMCw0LDUpDQoNCmhpc3QoYSxwcm9iPVQsY29sPTMsYm9yZGVyID0gImJsdWUiLCBtYWluPSJIaXN0b2dyYW0gUGxvdCIpDQoNCmxpbmVzKGRlbnNpdHkoYSksbHR5PTEsIGNvbD0yLGx3ZD0xKQ0KYGBgDQoNCi0tIFNjYXR0ZXIgcGxvdCB1c2luZyBgY2FyYCBwYWNrYWdlIA0KDQpgYGB7cn0NCmxpYnJhcnkoY2FyKQ0KDQpzY2F0dGVycGxvdChjYXJzJHNwZWVkfmNhcnMkZGlzdCxzbW9vdGg9RiwgbWFpbj0iU2NhdHRlciBQbG90IiwgeWxhYj0iU3BlZWQiLHhsYWI9IkRpc3RhbmNlIikNCmBgYA0KDQotLSBNdWx0aXBsZSBpbnRlcmFjdGlvbiBwbG90czogQmnhu4N1IMSR4buTIHTGsMahbmcgcXVhbiDEkWEgYmnhur9uDQoNCmBgYHtyfQ0KbGlicmFyeShwc3ljaCkNCg0KZGY8LUNhcnM5Mw0KDQpoZWFkKGRmKQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KZGYxPC0gZGYgJT4lIGRwbHlyOjpzZWxlY3QoNDo4KQ0KDQpwYWlycy5wYW5lbHMoZGYxKQ0KYGBgDQoNCiMgR3JhcGhpY3Mgd2l0aCBgZ2dwbG90MmAsIGEgIm1hc3RlcnBpZWNlIiBvZiBSIGRhdGEgdmlzdWFsaXphdGlvbiANCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQoNCmE8LXJub3JtKDIwMDApDQoNCmRmPC1kYXRhLmZyYW1lKGE9YSwgYj1nbCkNCg0KIyBnZ3Bsb3QyIA0KDQpnZ3Bsb3QoZGF0YT1kZixhZXMoeD1hKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMSxhZXMoeT0uLmRlbnNpdHkuLiksY29sb3I9NCxmaWxsPSJncmVlbiIpICsgZ2VvbV9kZW5zaXR5KCkgKyBsYWJzKHg9IlZhbHVlcyIseT0iRGVuc2l0eSIsIHRpdGxlPSJEZW5zaXR5IFBsb3QiLGNhcHRpb249IlR1eWVuLCBWLiBIYSAtIE1hc3NleSBVbml2ZXJzaXR5LCBOWiIpDQoNCiMgRmFjZXRzDQoNCmdncGxvdChkYXRhPUNhcnM5MyxhZXMoeD1QcmljZSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjEsYWVzKHk9Li5kZW5zaXR5Li4pLGNvbG9yPTQsZmlsbD0iZ3JlZW4iKSArIGdlb21fZGVuc2l0eSgpICsgbGFicyh4PSJWYWx1ZXMiLHk9IkRlbnNpdHkiLCB0aXRsZT0iRGVuc2l0eSBQbG90IixjYXB0aW9uPSJUdXllbiwgVi4gSGEgLSBNYXNzZXkgVW5pdmVyc2l0eSwgTloiKSArIGZhY2V0X3dyYXAoflR5cGUpDQpgYGANCg0KLSBEZXNjcml0aXZlIHN0YXRpc3RpY3M6IFRo4buRbmcga8OqIG3DtCB04bqjDQoNCmBgYHtyfQ0Kc3VtbWFyeShkZjEpDQojIFBzeWNoIGNhbiBzdW1tYXJpc2UgZGF0YSBhcyBmb2xsb3cNCg0KbGlicmFyeShwc3ljaCkgIyB0aGlzIHBhY2thZ2UgY2FuIGdpdmUgc3RhbmRhcmQgZXJyb3IgDQoNCmRlc2NyaWJlKGRmMSkNCg0KIyBTdW1tYXJpemluZyBkYXRhIHVuZGVyIGdyb3VwIChwc3ljaCBwYWNrYWdlKQ0KDQpkZjI8LUNhcnM5M1ssYygzOjgpXQ0KDQpoZWFkKGRmMikNCg0KZGVzY3JpYmVCeShkZjIsZ3JvdXAgPSAiVHlwZSIpDQpgYGANCg0KLS0gQ2F0ZWdvcmljYWwgZGVzY3JpdGl2ZSBzdGF0aXN0aWNzDQoNCmBgYHtyfQ0KIyBGb3Igb25lIGNhdGVnb3JpY2FsIHZhcmlhYmxlDQoNCmxpYnJhcnkodGFibGVzKQ0KDQp0YWJ1bGFyKFR5cGV+MSxkYXRhPUNhcnM5MykgIyBDYWxjdWxhdGUgdGhlIG51bWJlciBvZiBlYWNoIHR5cGUNCg0KdGFidWxhcihUeXBlfk9yaWdpbioobj0xKyBQZXJjZW50KCJjb2wiKSksIGRhdGE9Q2FyczkzKSAjIERpc3BsYXkgYm90aCBudW1iZXJzIGFuZCBwZXJjZW50IA0KDQpgYGANCg0KLS0gV2UgY2FuIGludGVncmF0ZSBkaWZmZXJlbnQgY2F0ZWdvcmljYWwgdmFyaWFibGVzIHRvIGZvcm0gYSBkZXNjcml0aW9uIHN0YXRpc3RpY3MNCg0KYGBge3J9DQpsaWJyYXJ5KHRhYmxlcykNCg0KdGFidWxhcihUeXBlKk9yaWdpbn5Ecml2ZVRyYWluKihuPTEgK1BlcmNlbnQoImNvbCIpKSxkYXRhPUNhcnM5MykNCmBgYA0KDQotLSBEaXNyaXB0aXZlIHN0YXRpc3RpY3MgZm9yIG9uZSB2YXJpYWJsZQ0KDQpgYGB7cn0NCmxpYnJhcnkoZ21vZGVscykNCg0KQ3Jvc3NUYWJsZShUeXBlICxPcmlnaW4sIGRhdGE9Q2FyczkzLCBwcm9wLmNoaXNxID0gVCxjaGlzcSA9IFQscHJvcC5yID0gVCxwcm9wLmMgPSBULHByb3AudCA9IFQpDQpgYGANCg0KLS0gYHRhYnVsYXJgDQoNCmBgYHtyfQ0KdGFidWxhcihUeXBlfk9yaWdpbiogV2lkdGgqKG49MSttZWFuK3NkK21lZGlhbiksZGF0YT1DYXJzOTMpICMgRm9yIG9uZSBjb250aW5vdXMgdmFyaWFibGUNCg0KdGFidWxhcihPcmlnaW4qVHlwZX4gKFdpZHRoK0hvcnNlcG93ZXIpKihuPTErbWVhbitzZCttZWRpYW4pLGRhdGE9Q2FyczkzKQ0KDQpgYGANCg0KLSBUZXN0IG9mIE5vcm1hbCBkaXN0cmlidXRpb24NCg0KYGBge3J9DQojIFVzaW5nIFNoYXBpcm8udGVzdCB0byBjaGVjayB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbg0KDQpzaGFwaXJvLnRlc3QoY2FycyRzcGVlZCkNCg0KIyBwLXZhbHVlPjAuMDUgPT4gSXQgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCg0KaGlzdChjYXJzJHNwZWVkKQ0KDQojIEFub3RoZXIgZXhhbXBsZQ0KDQphPC1ybm9ybSgyMDAwKQ0KDQpzaGFwaXJvLnRlc3QoYSkNCg0KIyBwLXZhbHVlPjAuMDUgPT4gYSBpcyBub3JtYWxseSBkaXN0cmlidXRlZA0KDQpoaXN0KGEpICMgQ2hlY2sgaXQgb3V0DQpgYGANCg0KLS0gVC10ZXN0OiB0aGVyZSBhcmUgdHdvIHR5cGVzIG9mIGB0LXRlc3RgLiBGaXJzdGx5LCB0LXRlc3QgZm9yIG9uZSB2YXJpYWJsZSB3aGlsZSB0aGUgc2Vjb25kIGZvciB0d28gdmFyaWFibGVzDQoNCi0gRXhhbXBsZSAodC10ZXN0IHR5cGUgMSk6IElmIG15IGNsYXNzJ3MgbWFyayBhdmVyYWdlIGlzIGtub3duIDcgZm9yIG1hbnkgeWVhcnMsIGJ1dCBub3cgSSBjb2xsZWN0IGEgcmVjZW50IGNsYXNzIGFuZCBjYWxjdWxhdGUgbmV3IGF2ZXJhZ2UuIEFyZSB0aGVyZSBhbnkgZGlmZmVyZW5jZSBiZXR3ZWVuIG5ldyBhbmQgcHJldmlvdXMgYXZlcmFnZSBtYXJrcz8NCg0KKnQtdGVzdCBmb3JtdWxhDQoNCiQkIHQtdGVzdD0gXGZyYWN7XGJhcnt4fS1cbXV9e1MvXHNxcnR7bn19ICQkDQoNCk5vdGVzOiBgbmAgaXMgc2FtcGxlIHNpemU7IFMgaXMgc3RhbmRhcmQgZGV2aWF0aW9uLCB4IGJhciBpcyBhbiBhdmVyYWdlIG9mIGEgc2FtcGxlOyBtdSBpcyBhIGtub3duIHBvcHVsYXRpb24gbWVhbg0KDQpgYGB7cn0NCiMgQSBrbm93biBtYXJrOiA3DQoNCnNldC5zZWVkKDEyMykNCg0KbXljbGFzczwtcm5vcm0oMzAsIDcsMS41KQ0KDQp0LnRlc3QobXljbGFzcywgbXU9NykNCg0KIyBUaGVyZSBpcyBhIHN0cm9uZyBldmlkZW5jZSB0byBzYXkgdGhhdCB0aGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW4gbWVhbiBvZiA3IGFuZCBteWNsYXNzJ3MgbWVhbiAocC12YWx1ZT0wLjc5NCkNCg0KIyBBc3N1bXB0aW9uIDINCg0KdC50ZXN0KG15Y2xhc3MsbXU9OCkNCg0KIyBwLXZhbHVlPTAuMDA0MTY2LCBhbmQgdGhlcmVmb3JlIHRoZXJlIGlzIGRpZmZlcmVuY2UgYmV0d2VlbiBtZWFuIG9mIDggYW5kIG15Y2xhc3MnIG1lYW4NCg0KYGBgDQotLSB0LXRlc3QgZm9yIHR3byB2YXJpYWJsZXMNCg0KIEZvcm11bGEgDQogDQokJCAgdC10ZXN0PSBcZnJhY3tcYmFye3hfMn0tXGJhcnt4XzF9fXtTRUR9ICQkDQpOb3RlOiBTRUQ9c3FydChTRTFeMiArIFNFMl4yKSBzdGFuZGFyZCBlcnJvcg0KDQpgYGB7cn0NCnQudGVzdChDYXJzOTMkUHJpY2V+Q2FyczkzJE9yaWdpbikNCg0KIyBJdCBpcyBsaWtlbHkgdG8gc2F5IHRoYXQgdGhlcmUgaXMgbm8gZGlmZmVyZW5jZSBpbiBwcmljZSBiZXR3ZWVuIFVTQSBhbmQgTm9uLVVTQSBhcyBwLXZhbHVlID4wLjAwNTogTeG7qWMgxJHhu5kga2jDoWMgYmnhu4d0IGdp4buvYSAyIG5ow7NtIGtow7RuZyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqDQoNCiMgSWYgdmFyaWFuY2UgaXMgYXNzdW1tZWQgdG8gYmUgZXF1YWwNCg0KdC50ZXN0KENhcnM5MyRQcmljZX5DYXJzOTMkT3JpZ2luLHZhci5lcXVhbD1UKQ0KYGBgDQoNCi0tIFRlc3Qgb2YgdmFyaWFuY2UNCg0KYGBge3J9DQoNCnZhci50ZXN0KENhcnM5MyRQcmljZX5DYXJzOTMkT3JpZ2luKQ0KIyBUaGVyZSBpcyBkaWZmZXJlbmNlIGJldHdlZW4gdHdvIG9yaWdpbnMuIFVTQSBpcyBncmVhdGVyIDAuNDc3OSBub24tVVNBIGFuZCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IChwLXZhbHVlPDAuMDA1KQ0KDQpgYGANCg0KLS0gSWYgb3VyIGRhdGEgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB3ZSBnb25uYSBoYXZlIGFuIGFsdGVybmF0aXZlIHRlc3QgY2FsbGVkIGB3aWxjb3gudGVzdGAgDQoNCmBgYHtyfQ0KIyBDaGVjayBub3JtYWwgZGlzdHJpYnV0aW9uIG9mIFByaWNlIGluIENhcnM5MyBkYXRhc2V0DQoNCnNoYXBpcm8udGVzdChDYXJzOTMkUHJpY2UpDQoNCiMjIEhvLiBQcmljZSBpcyBub3Qgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCg0KIyNIMTogUHJpY2UgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCg0KIyB0aGVyZSBpcyBhIHN0cm9uZyBldmlkZW5jZSB0byBzaG93IHRoYXQgUHJpY2UgaXMgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGR1ZSB0byBhIHRpbnkgc21hbGwgcC12YWx1ZQ0KDQojIFdpbGNveG9uIHRlc3QgDQoNCndpbGNveC50ZXN0KENhcnM5MyRQcmljZX5DYXJzOTMkT3JpZ2luKQ0KDQojIHAtdmFsdWUgPjAuMDUgPT4gbm8gZGlmZmVyZW5jZSBpbiBwcmljZSBtZWFuIGJldHdlZW4gdHdvIGdyb3Vwcw0KYGBgDQoNCi0tIFQudGVzdCBmb3IgYSBwYWlyIG9mIG9iamVjdHMNCg0KIEV4YW1wbGU6IEkgd2FudCB0byB0ZXN0IGlmIGFueSBkaWZmZXJlbmNlIGluIHNhdGlzZmFjdGlvbiBvZiBhIGN1c3RvbWVyIHdoZW4gdGhleSB0YXN0ZSBteSBwcm9kdWN0IGFuZCBvdGhlcidzIHByb2R1Y3QuDQogDQpgYGB7cn0NCm15cHJvZHVjdDwtYyg1Niw2MCw1OCw3MCw5NSw4Miw2Miw1Niw3Niw4NykNCg0KDQpvdGhlcjwtYyg4Nyw1Niw2NSw2Nyw0OCw0Niw1Myw1OSw3MSw2MykNCg0KZGY8LWRhdGEuZnJhbWUobXlwcm9kdWN0LG90aGVyKQ0KDQphdHRhY2goZGYpICMgSWYgeW91IGxpa2UgDQoNCnQudGVzdChteXByb2R1Y3Qsb3RoZXIscGFpcmVkID0gVCkNCg0KIyBUaGVyZSBpcyBubyBkaWZmZXJlbmNlIGluIG1lYW4gYmV0d2VlbiBteXByb2R1Y3QgYW5kIG90aGVyDQoNCndpbGNveC50ZXN0KG15cHJvZHVjdCxvdGhlcixwYWlyZWQgPSBUKQ0KDQojIFRoZSBzYW1lIHJlc3VsdCBvZiB0LnRlc3QNCmBgYA0KDQotLSBUZXN0IGZvciBDYXRlZ29yaWNhbCB2YXJpYWJsZXMNCg0KYGBge3J9DQojIEEgZGF0YXNldCBkZXJpdmVkIGZyb20gTWFzc2V5IFVuaXZlcnNpdHksIE5aDQoNCmRmPC1yZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3R1eWVuaGF2YW4vU3RhdGlzdGljcy9EYXRhc2V0L0Nvd01pbGsyMDA4LmNzdiIsaGVhZGVyPVQsc2VwPSI7IikNCg0KaGVhZChkZikNCmBgYA0KDQotLSBGcmVxdWVuY3kgb3IgY291bnRzDQoNCmBgYHtyfQ0KIyBDb3VudCB0aGUgbnVtYmVyIG9mIGxldmVscyBvciB0aGluZ3MgaW4gZWFjaCBjYXRlZ29yaWNhbCB2YXJpYWJsZQ0KDQp0PC10YWJsZShkZiRCcmVlZCxkZiRNYXN0KSAjIE51bWJlciBvZiBicmVhZHMgaW4gZWFjaCBtYXN0DQoNCnByb3AudGFibGUodCwxKSAjIEl0IG1lYW5zIHRoYXQgaW4gQnJlZWQgZ3JvdXAsIDkyJSBpcyBmcm9tIE1hc3RfMCBhbmQgOCUgZnJvbSBNYXN0XzEuDQoNCmBgYA0KDQotLSBQcm9wb3J0aW9uIHRlc3QgZm9yIG9uZSBldmVudA0KDQpFeGFtcGxlOiBUaGVyZSBhcmUgMjAwIHBlb3BsZSBpbiBteSBzdHVkeSBpbiB3aGljaCAxMTIgbWFsZXMgYW5kIDg4IGZlbWFsZXMuIElmIEkgd2FudCB0byB0ZXN0IGlmIHRoZSBwcm9wb3J0aW9uIG9mIG1hbGUgaW4gdGhpcyBzdHVkeSBpcyByZWFsbHkgbGFyZ2VyIHRoYW4gNTAlPw0KDQpgYGB7cn0NCnByb3AudGVzdCgxMTIsMjAwLDAuNSkNCiMgVGhlcmUgaXMgbm8gZXZpZGVuY2UgdG8gc2F5IHRoYXQgdGhlIHByb3BvcnRpb24gb2YgbWFsZXMgaW4gdGhpcyBzdHVkeSBpcyBub3QgZ3JlYXRlciB0aGFuIDUwJSBkdWUgdG8gYSBsYXJnZSBwLXZhbHVlDQoNCiMgQWx0ZXJuYXRpdmUgdGVzdA0KDQpiaW5vbS50ZXN0KDExMiwyMDAsMC41KSAjIFRoZSBzYW1lIHJlc3VsdCBhcyBhYm92ZQ0KYGBgDQotLSBQcm9wb3J0aW9uIHRlc3QgZm9yIHR3byBncm91cHMNCg0KRXhhbXBsZTogQSBzdHVkeSBpbmNsdWRlZCAxMDAgcGVvcGxlIChBKSB1c2luZyBtZWRpY2luZSBhbmQgMTIwIChCKSB3aXRob3V0IG1lZGljaW5lLiBBZnRlciAyIHllYXJzLCBpdCBpcyBvYnNlcnZlZCB0aGF0IDEzIHBlb3BsZSBzdWZmZXJlZCBmcm9tIGNhbmNlciBmcm9tIEEsIGFuZCAyNCBmcm9tIEIuIEFyZSB0aGVyZSBhbnkgZGlmZmVyZW5jZSBpbiB0aGUgcHJvcG9ydGlvbiBvZiBzdWZmZXJzIGJldHdlZW4gdHdvIGdyb3Vwcz8gb3IgbWVkaWNpbmUgZWZmZWN0aXZlPw0KDQpgYGB7cn0NCnN1ZmZlcjwtYygxMywyNCkNCg0KdG90YWw8LWMoMTAwLDEyMCkNCg0KcHJvcC50ZXN0KHN1ZmZlcix0b3RhbCkNCg0KIyBUaGVyZSBpcyBubyBkaWZmZXJlbmNlIGJldHdlZW4gdXNpbmcgbWVkaWNpbmUgYW5kIHdpdGhvdXQgbWVkaWNpbmUNCmBgYA0KDQotLSBVc2luZyBkaWZmZXJlbnQgYXBwcm9hY2hlcyB0byBhIG11bHRpcGxlLXByb3BvcnRpb24gdGVzdCAgYHByb3AudGVzdCgpYDsgYGJpbm9tLnRlc3QoKWA7IGBjaGlzcS50ZXN0KClgOyBgZmlzaGVyLnRlc3RgDQoNCmBgYHtyfQ0KIyBNdeG7kW4gYmnhur90IHThu7cgbOG7hyBu4buvIGdp4buvYSBjw6FjIG5ow7NtIGPDsyBraMOhYyBuaGF1IGtvPyBkdW5nIHByb3AudGVzdCgpDQoNCiMgQ29udmVydGluZyBudW1lcmljIHZhbHVlcyB0byBmYWN0b3JpY2FsIHZhbHVlcyBmb3IgdHdvIGNhdGVnb3JpY2FsIHZhcmlhYmxlcw0KDQpkZiRSZWdpb248LWFzLmZhY3RvcihkZiRSZWdpb24pDQoNCmRmJE1hc3Q8LWFzLmZhY3RvcihkZiRNYXN0KQ0KIyBAIFdhbnQgdG8ga25vdyBpZiBhbnkgZGlmZmVyZW5jZSBpbiBwcm9wb3J0aW9uIG9mIE1hc3QgaW4gZWFjaCBSZWdpb24NCg0KdGFibGUoZGYkTWFzdCxkZiRSZWdpb24pDQoNClplcm88LWMoMTAyNDUsOTMyMSwxMjQ5NiwxMzg2MikNCg0KdG90YWw8LWMoc3VtKDEwMjQ1LDg4Nyksc3VtKDkzMjEsNjg4KSxzdW0oMTI0OTYsMTY4Niksc3VtKDEzODYyLDkxMykpDQoNCnByb3AudGVzdChaZXJvLHRvdGFsKSAjIFRlc3QgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHByb3BvcnRpb24gb2YgTWFzdF8wIGZvciBlYWNoIHJlZ2lvbg0KDQojPT4gVGhlcmUgaXMgZXZpZGVuY2UgdG8gc2F5IHRoYXQgdGhlIHByb3BvcnRpb24gb2YgTWFzdF8wIGlzIGRpZmZlcmVudCBpbiBlYWNoIFJlZ2lvbg0KDQojIEFub3RoZXIgZXhhbXBsZQ0KDQp0YWJsZShkZiRCcmVlZCxkZiRSZWdpb24pDQoNCkNCPC1jKDUyNzEsNTE0Miw3NTE1LDc0NzMpDQoNCnRvdGFsPC1jKHN1bSg1MjcxLDQ5ODMsODc4KSxzdW0oNTE0MiwyOTQ1LDE5MjIpLHN1bSg3NTE1LDQ3OTUsMTg3Miksc3VtKDc0NzMsNTE0NSwyMTU3KSkNCg0KcHJvcC50ZXN0KENCLHRvdGFsKQ0KDQojIFRoZXJlIGlzIGV2aWRlbmNlIHRvIHNheSB0aGF0IHRoZXJlIGlzIGRpZmZlcmVuY2UgaW4gcHJvcG9ydGlvbiBvZiBDQiBpbiBlYWNoIFJlZ2lvbiAocC12YWx1ZTwwLjA1KQ0KDQojIEFsdGVybmF0aXZlIHRlc3QgMQ0KDQpjaGlzcS50ZXN0KGRmJE1hc3QsZGYkUmVnaW9uKQ0KDQojIFRoZSBzYW1lIHJlc3VsdCBhcyBhYm92ZQ0KYGBgDQoNCiMgTGluZWFyIFJlZ3Jlc3Npb24gTW9kZWwNCg0KLS0gQ29yZWxhdGlvbiBjb2VmZmljaWVudHMgDQoNCmBgYHtyfQ0KIyBDcmVhdGUgYSBzaW11bGF0aW9uIGRhdGFzZXQNCg0Kc2V0LnNlZWQoMTIzKQ0KDQp3dDwtcnVuaWYoMTAwLDY5LDExMCkgIyBXZWlnaHQgZm9yIDEwMCBwZW9wbGUNCg0KDQpodDwtd3QrMC41KnJub3JtKDEwMCwxNTAsMTApICMgaGVpZ2h0IGZvciAxMDAgcGVvcGxlDQoNCmRmMTwtcm91bmQoZGF0YS5mcmFtZSh3dCxodCkpDQoNCmhlYWQoZGYxKQ0KYGBgDQoNCi0tIEZpbmQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gYGh0YCBhbmQgYHd0YCB1c2luZyBQZWFyc29uJ3MgY29ycmVsYXRpb24NCg0KJCQgIHI9XHN1bV97aT0xfV57bn1cbGVmdCggXGZyYWN7KHhfaS1cYmFye3h9KSooeV9pLVxiYXJ7eX0pfXtcc3FydHtcc3VtX3tpPTF9XntufSh4X2ktXGJhcnt4fSleMiooXHN1bV97aT0xfV57bn0oeV9pLVxiYXJ7eX0pXjIpfX0gXHJpZ2h0KSAkJA0KYGBge3J9DQojIFBlYXJzb24ncyBjb3JyZWxhdGlvbg0KDQpjb3Iod3QsaHQpDQoNCnBsb3QoaHR+d3QsY29sPTMscGNoPTE2KQ0KYGBgDQoNCi0tIENvcnJlbGF0aW9uIHRlc3QNCg0KYGBge3J9DQojIFBlYXJzb24ncyBjb3JyZWxhdGlvbiB0ZXN0DQoNCmNvci50ZXN0KHd0LGh0KQ0KDQojIC0gSDA6IHRoZXJlIGlzIG5vIGNvcnJlbGF0aW9uIGJldHdlZW4gd3QgYW5kIGh0DQoNCiMgLSBIMTogVGhlcmUgaXMgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHd0IGFuZCBodA0KDQojPT4gcC12YWx1ZTwwLjA1IC0gdGhlcmUgaXMgZXZpZGVuY2UgdG8gc2F5IHRoYXQgY29ycmVsYXRpb24gYmV0d2VlbiBodCBhbmQgd3QgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCAgDQpgYGANCg0KLS0gS2VuZGFsbCBjb3JyZWxhdGlvbiB0ZXN0IHVzaW5nIG5vbi1ub3JtYWxseSBkaXN0cmlidXRlZCBkYXRhDQoNCmBgYHtyfQ0KY29yLnRlc3Qod3QsaHQsbWV0aG9kPSJrZW5kYWxsIikgIyBOb24tcGFyYW1ldHJpYyBtZXRob2QNCg0KYGBgDQoNCi1Db3JyZWxhdGlvbiBpcyBncmVhdCwgYnV0IHdlIGNhbm5vdCB1c2UgaXQgdG8gcHJlZGljdCBhbnkgb3V0cHV0IG9yIGEgZ2l2ZW4gZGF0YSBwb2ludC4gVGhlcmVmb3JlLCB3ZSBoYXZlIHRvIGNvbWUgdXAgd2l0aCBhbiBlcXVhdGlvbiB0byBlc3RpbWF0ZSBwYXJhbWV0ZXJzIGFuZCB1c2UgaXQgdG8gcHJlZGljdCBhIGtub3duIHggZGF0YSBwb2ludC4gRm9yIHRoaXMgcmVhc29uLCBgTGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxgIGlzIHZlcnkgZ29vZCBjaG9pY2UgZm9yIHRoaXMgcHJvYmxlbS4gDQoNCiBCZWZvcmUsIHdlIGdvbm5hIGdvIGZ1cnRoZXIsIGxldCdzIHJldmlldyBzb21lIG9mIHRoZSBiYXNpYyB0aGVvcnkgb2YgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbC4gRmlyc3QsIHdlIGxvb2sgYXQgYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwuIE1hdGhlbWF0aWNhbGx5LCBpdCBpcyBwcmVzZW50ZWQgYXMgZm9sbG93czoNCiANCiAkJCB5X2k9XGFscGhhICtcYmV0YSp4X2kgK1xlcHNpbG9uX2kkJA0KDQogVGhlIGVxdWF0aW9uIGFib3ZlIGlzIHRoZSBwb3B1bGF0aW9uIGVxdWF0aW9uIHdpdGggYGVwc2lsb25gIGlzIG5vaXNlIHBhcnQgb2YgdGhlIG1vZGVsLiBJbiBwcmFjdGljZSwgd2UgZG9uJ3Qga25vdyB0aGUgYGFscGhhYCBhbmQgYGJldGFgIHBhcmFtZXRlcnMsIGFuZCB0aGVyZWZvcmUgd2UgZ29ubmEgdXNlIHN0YXRpc3RpY2FsIG9yIHNhbXBsZSBlc3RpbWF0ZXMgYGFscGhhIGhhdGAgYW5kIGBiZXRhIGhhdGAgdG8gcHJlZGljdCBvciBpbmZlcmVuY2UgdGhlICBwYXJhbWV0ZXJzLiBUbyBkbyBzbywgc29tZSBhc3N1bXB0aW9ucyBhcmUgbmVlZGVkLiBGb3IgZXhhbXBsZSwgYGVwc2lsb25gIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGFuZCBtZWFuIGlzIHplcm8sIGFuZCBlcXVhbGx5IHZhcmlhbmNlLiBBIG5ld2x5IHJlLWRlZmluZWQgZXF1YXRpb24gZm9yIHNhbXBsZSBkYXRhIGNhbiBiZSB3cml0dGVuIGFzOg0KDQokJCBcaGF0e3lfaX09IFxoYXR7XGFscGhhfSArIFxoYXR7XGJldGF9KnhfaSAgICQkDQoNCkluIHRoaXMgYXBwcm9hY2gsIHdlIGhhdmUgdG8gZmluZCBhIGxpbmUgdGhhdCBmaXRzIGRhdGEgd2VsbC4gV2hhdCBJIG1lYW4gaGVyZSBpcyB0aGF0IGl0IGlzIGJlbGlldmVkIHRoYXQgdXNpbmcgYGxlYXN0IHNxdWFyZSBtZXRob2RgIGNhbiBiZSBiZXN0IHRvIGVzdGltYXRlIHRoZSBjb2VmZmljaWVudHMgYW5kIG1pbmltaXplIHRoZSBtb2RlbCBlcnJvcnMuIENvZWZmaWVudHMgYCBhbHBoYSBoYXRgIGFuZCBgYmV0YSBoYXRgIGNhbiBiZSBlc3RpbWF0ZWQgYXMgZm9sbG93Og0KDQokJCAgXGhhdHtcYmV0YX0gPSBcZnJhY3tcc3VtX3tpPTF9XntufSh4X2ktXGJhcnt4fSkqKHlfaS1cYmFye3l9KX17XHN1bV97aT0xfV57bn0oeF9pLVxiYXJ7eH0pXjJ9ICA7ICAgICBcaGF0e1xhbHBoYX09IFxiYXJ7eX0tXGhhdHtcYmV0YX0qXGJhcnt4fSAkJA0KDQpOb3RlczogYGFscGhhIGhhdGAgYW5kIGBiZXRhIGhhdGAgYXJlIHNhbXBsZSBlc3RpbWF0ZXMsIGB5IGhhdGAgaXMgcHJlZGljdGVkIHZhbHVlcyB1c2luZyB0aGUgYWJvdmUgZXF1YXRpb24uIFJlc2lkdWFscyBjYW4gYmUgZXN0aW1hdGVkIGFzDQoNCiQkIFJlc2lkdWFscz15X2kgLVxoYXR7eV9pfSAkJA0KDQphbmQgdmFyaWFuY2Ugb2YgcmVzaWR1YWxzIChzXjIpIGlzIGEgZXN0aW1hdGUgb2YgYGVwc2lsb25gIGFuZCBjYW4gYmUgd3JpdHRlbiBhcyANCg0KJCQgc14yID0gXGZyYWN7XHN1bV97aT0xfV57bn0oeV9pLVxoYXR7eV9pfSl9e24tMn0gICQkDQoNCi0tU29sdmluZyByZWdyZXNzaW9uIHByb2JsZW0gdXNpbmcgYFJgIA0KDQogVGhpcyB0YXNrIGNhbiBiZSBlYXNpbHkgYWNoaWV2ZWQgaW4gUiB3aXRoIGEgc2ltcGxlIGNvbW1hbmQgbGluZSBhcyBmb2xsb3cNCg0KYGBge3J9DQptMTwtbG0oaHR+d3QsZGF0YT1kZjEpDQoNCiMgR2V0IHRoZSBzdW1tYXJ5IG9mIHRoZSBtb2RlbA0KDQpzdW1tYXJ5KG0xKQ0KYGBgDQoNCiBUaGVyZSBhcmUgdGhyZWUgaW1wb3J0YW50IGNvbXBvbmVudHMgaW4gdGhlIGFib3ZlIG1vZGVsOiBgIFJlc2lkdWFsc2A7IGBDb2VmZmljaWVudHNgIGFuZCBgcmVzaWR1YWwgc3RhbmRhcmQgZXJyb3IsIFItc3F1YXJlZCBhbmQgRi1zdGF0aXN0aWNzYCANCg0KIFItc3F1YXJlZCAoY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbikgY2FuIGJlIGVzdGltYXRlZCBhczoNCiANCiQkUl4yPSBcZnJhY3tcc3VtX3tpPTF9XntufShcaGF0e3lfaX0tXGJhcnt5fSleMn17XHN1bV97aT0xfV57bn0oeV9pLVxiYXJ7eX0pXjJ9ICQkDQoNCiBJbiBsaW5lYXIgcmVncmVzc2lvbiBjb250ZXh0LCBpdCBpcyBhc3N1bWVkIHNvbWUgY3JpdGVyaWEsIGFuZCBzaG91bGQgYmUgc2F0aXNmaWVkIGl0LiANCiANCjEuIGBlcHNpbG9uYCBmb2xsb3dzIG5vcm1hbCBkaXN0cmlidXRpb24NCg0KMi4gYGVwc2lsb25gIGhhcyB6ZXJvIG1lYW4NCg0KMy4gVmFyaWFuY2Ugb2YgYGVwc2lsb25gIHNob3VsZCBiZSBmaXhlZCBhdCBhbnkgZ2l2ZW4gcG9pbnQgeA0KDQo0LiBWYWx1ZXMgb2YgYGVwc2lsb25gIGlzIGluZGVwZW5kZW50OyBgZXBzaWxvbiAxYCBpcyBpbmRlcGVuZGVudCBmcm9tIGBlcHNpbG9uIDJgDQoNCi0tIENoZWNrIGlmIG91ciBtb2RlbCBpcyBzYXRpc2ZpZWQgdGhlIGNvbmRpdGlvbg0KDQogSW52ZXN0aWdhdGUgZWFjaCBwbG90IHRvIHNlZSBpZiB0aGV5IGFyZSBtZXQgb3Igbm90Pw0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMikpDQoNCnBsb3QobTEpDQpgYGANCg0KLS0gR3JhcGhpYyBwbG90cw0KDQpgYGB7cn0NCg0KcHJlZDE8LXByZWRpY3QubG0obTEsbmV3ZGF0YSA9IGRhdGEuZnJhbWUod3Q9ZGYxJHd0KSwgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpDQoNCm1wPC1kYXRhLmZyYW1lKHByZWQxLHd0PWRmMSR3dCkNCg0KcGxvdChkZjEkaHR+ZGYxJHd0LGNvbD0zLHBjaD0xNixtYWluPSJIZWlnaHQgaXMgYSBmdW5jdGlvbiBvZiBXZWlnaHQiLHhsYWI9IldlaWdodCIseWxhYj0iSGVpZ2h0IikNCg0KYWJsaW5lKG0xKQ0KDQpsaW5lcyhtcCRsd3J+bXAkd3QsY29sPTIsbHdkPTEpDQoNCmxpbmVzKG1wJHVwcn5tcCR3dCxjb2w9NCxsd2Q9MSkNCmBgYA0KDQotLS0gUHJlZGljdCBuZXcgZ2l2ZW4geCB2YWx1ZXMNCg0KYGBge3J9DQojIENyZWF0ZSB4IGRhdGEgcG9pbnRzDQoNCnd0MTwtZGF0YS5mcmFtZSh3dD1jKDY3LDkwLDc4LDg4LDgyLDU3LDExMCwxMDgpKQ0KDQp5X3ByZWQxPC1wcmVkaWN0LmxtKG0xLG5ld2RhdGEgPSB3dDEsaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpICMgQ29uZmlkZW5jZSBpbnRlcnZhbA0KDQp5X3ByZWQyPC1wcmVkaWN0LmxtKG0xLG5ld2RhdGEgPSB3dDEsaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpICMgUHJlZGljdGlvbiBpdGVydmFsDQoNCnBsb3QoaHR+d3QsY29sPTQsbWFpbj0iSGVpZ2h0IGlzIGEgZnVuY3Rpb24gb2YgV2VpZ2h0IixwY2g9MTYseGxhYj0iV2VpZ2h0IChrZykiLHlsYWI9IkhlaWdodCAoY20pIikNCg0KIyBBZGRpbmcgc29tZSBsaW5lcw0KDQpyZXMxPC1jYmluZCh5X3ByZWQxLHd0MSkNCg0KcmVzMjwtY2JpbmQoeV9wcmVkMix3dDEpDQoNCmxpbmVzKHJlczEkZml0fnJlczEkd3QsY29sPTEpDQoNCmxpbmVzKHJlczEkdXByfnJlczEkd3QsY29sPTMpDQoNCmxpbmVzKHJlczEkbHdyfnJlczEkd3QsY29sPTMpDQoNCmxpbmVzKHJlczIkbHdyfnJlczIkd3QsY29sPTIsbHR5PTIpDQoNCmxpbmVzKHJlczIkdXByfnJlczIkd3QsY29sPTIsbHR5PTIpDQoNCmBgYA0KDQotIE11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uDQoNCiBUaGUgdGhlb3J5IGluIG11bHRpcGxlIGxpbmVhciByZWdyZXNzaW9uIGlzIHNpbWlsYXIgdG8gc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uJ3MgdGhlb3J5LiBIb3dldmVyLCBtb3JlIGluZGVwZW5kZW50IHZhcmlhYmxlcyBhcmUgYWRkaW5nIHRvIHRoZSBtb2RlbC4NCiANCmBgYHtyfQ0KIyBVc2luZyB0aGUgZm9sbG93aW5nIGRhdGFzZXQgdG8gcHJlZGljdCAuLi4NCg0KbGlicmFyeShNQVNTKQ0KDQpoZWFkKEJvc3RvbikNCg0KbmFtZXMoQm9zdG9uKQ0KDQpiaDwtQm9zdG9uWyxjKDEsNyw4LDEzLDE0KV0NCg0KaGVhZChiaCkNCg0KIyBNdWx0aXBsZSBsaW5lYSByZWdyZXNzaW9uDQoNCm1sPC1sbShtZWR2fi4sIGRhdGE9YmgpDQoNCnN1bW1hcnkobWwpDQpgYGANCg0KLS0gVXNpbmcgYHRyYW5zZm9ybWF0aW9uYCB0byByZWZpdCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwNCg0KYGBge3J9DQoNCmhlYWQodHJlZXMpDQojIFBsb3QgdGhlIHNjYXR0ZXIgDQoNCnBsb3QodHJlZXMkVm9sdW1lfnRyZWVzJEhlaWdodCkNCg0KbXQxPC1sbShWb2x1bWV+SGVpZ2h0LGRhdGE9dHJlZXMpICMgU2ltcGxlIGxpbmVhciByZWdyZXNzaW9uDQoNCnN1bW1hcnkobXQxKQ0KDQojIEFub3RoZXIgRXhhbXBsZQ0KaGVhZChBbmltYWxzKQ0KDQojIyBGaXR0aW5nIGEgc2ltcGxlIGxpbmVhciBtb2RlbA0KDQptYTE8LWxtKGJyYWlufmJvZHksIGRhdGE9QW5pbWFscykNCg0Kc3VtbWFyeShtYTEpDQoNCnBhcihtZnJvdz1jKDIsMikpDQoNCnBsb3QobWExKQ0KIyBJdCBpcyBjbGVhciB0aGF0IHRoaXMgbW9kZWwgaXMgbm90IG1ldC4gSG93IGFib3V0IHRyYW5zZm9ybWF0aW9uIGJvdGggeCBhbmQgeQ0KDQptdDI8LWxtKGxvZyhicmFpbil+bG9nKGJvZHkpLGRhdGE9QW5pbWFscykNCg0Kc3VtbWFyeShtdDIpDQoNCnBhcihtZnJvdz1jKDIsMikpDQoNCnBsb3QobXQyKQ0KYGBgDQoNCi1DaG9vc2luZyBhIGBiZXN0IG1vZGVsYCBmcm9tIGEgcG9vbCBvZiBwb3NzaWJsZSBtb2RlbHMgOikNCg0KIEl0IGlzIG9idmlvdXMgdGhhdCB0aGVyZSBhcmUgc28gbWFueSBwb3NzaWJsZSBtb2RlbHMgdGhhdCB3ZSBjYW4gYnVpbGQgZGVwZW5kaW5nIHRoZSBudW1iZXIgb2YgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiBIb3dldmVyLCBXZSBvbmx5IGNob29zZSBvbmUgbW9kZWwgdGhhdCBjYW4gYmUgYmVzdCBleHBsYWluZWQgdGhlIHZhcmlhdGlvbnMgaW4gYHlgLlRvIGRvIHNvLCBhIGluZGljYXRvciBjYWxsZWQgYEFJQ2AgQWthaWtlIEluZm9ybWF0aW9uIENyaXRlcmlvbiB3aGljaCBjYW4gYmUgdXNlZCB0byBzZWxsZWN0IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgdmFyaWFibGVzLiBUaGlzIGNyZXRlcmlhIGNhbiBiZSBpZGVudGlmaWVkIGFzIA0KIA0KICQkIEFJQz1sb2coXGZyYWN7UlNTfXtufSkrXGZyYWN7Mmt9e259OyBcICBcIHdoaWNoIFwgXCAgUlNTPVxzdW1fe2k9MX1ee259KFxoYXR7eV9pfS15X2kpXjIsIFwgICBcIGs9bnVtYmVyIFwgb2YgXCBpbmRlcGVuZGVudCBcIHZhcmlhYmxlcywgXCBuPU5vIFwgb2YgXCBvYnNlcnZhdGlvbnMgJCQgDQogV2UgY2hvb3NlIGEgbW9kZWwgd2l0aCB0aGUgc21hbGxlc3QgQUlDDQoNCi0tIFByYWN0aWNlIGNob29zaW5nIGEgYG9wdGltYWwgbW9kZWxgIHVzaW5nIEFJQyBjcml0ZXJpYQ0KDQpgYGB7cn0NCmhlYWQoQ08yKQ0KDQpgYGANCg0KIyBBbmFseXNpcyBvZiBWYXJpYW5jZSANCg0KLS0gT25lLXdheSBhbmFseXNpcyBvZiB2YXJpYW5jZSAoQU5PVkEpDQo=