Descriptive
Statistics
url1 <- "https://raw.githubusercontent.com/novrisuhermi/dataset/refs/heads/main/data_sample.csv"
data <- read.csv(url1)
head(data)
data_stats <- summary(data)
print(data_stats)
x
Min. : 3.00
1st Qu.: 8.00
Median :10.00
Mean : 9.94
3rd Qu.:12.00
Max. :19.00
Mean
\text { Mean: } \bar{x}=\frac{1}{n} \sum_{i=1}^n x_i
sample_mean <- function(data){
n <- length(data)
sum_data <- 0
for (i in 1:n){
sum_data <- sum_data + data[i]
}
return(sum_data/n)
}
sample_mean(data$x)
[1] 9.94
# built-in function in R
mean(data$x)
[1] 9.94
Variance
\text { Variance: } S^2=\frac{1}{n-1}
\sum_{i=1}^n\left(x_i-\bar{x}\right)^2
sample_variance <- function(data){
n <- length(data)
sum_data <- 0
for (i in 1:n){
sum_data <- sum_data + data[i]
}
mean_data <- sum_data/n
var_data <- 0
for (i in 1:n){
var_data <- var_data + (data[i]-mean_data)^2
}
return(var_data/(n-1))
}
sample_variance(data$x)
[1] 10.76404
# built-in function in R
var(data$x)
[1] 10.76404
Median, Quartiles
& Percentiles
# Sorting Algorithm
bubble_sort <- function(x) {
n <- length(x)
for (i in 1:(n-1)) {
for (j in 1:(n-i)) {
if (x[j] > x[j+1]) {
temp <- x[j]
x[j] <- x[j+1]
x[j+1] <- temp
}
}
}
return(x)
}
bubble_sort(data$x)
[1] 3 4 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9
[44] 9 9 10 10 10 10 10 10 10 10 10 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 12 12 12 12 13 13 13 13
[87] 13 13 14 14 15 15 15 16 16 16 16 17 17 19
sort(data$x)
[1] 3 4 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 8 8 8 8 8 8 9 9 9 9 9 9 9 9
[44] 9 9 10 10 10 10 10 10 10 10 10 10 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 12 12 12 12 13 13 13 13
[87] 13 13 14 14 15 15 15 16 16 16 16 17 17 19
Calculating the Sample 100p-th
Percentile
- Order the data from smallest to largest.
- Determine the product (sample size) \times (proportion) = n p.
If np is not an integer, round it up
to the next integer and find the corresponding ordered value.
If np is an integer, say k, calculate the average of the kth and (k+1)st ordered values.
\begin{array}{ll}
\text { Lower (first) quartile } & \mathrm{Q}_1=25 \text {th
percentile } \\
\text { Second quartile (or median) } & \mathrm{Q}_2=50 \text {th
percentile } \\
\text { Upper (third) quartile } & \mathrm{Q}_3=75 \text {th
percentile }
\end{array}
sample_median <- function(data){
n <- length(data)
sorted_data <- sort(data)
if (n %% 2 == 1){
med <- sorted_data[(n+1)/2]
} else{
med <- 0.5*(sorted_data[n/2] + sorted_data[n/2+1])
}
return(med)
}
sample_median(data$x)
[1] 10
median(data$x)
[1] 10
sample_percentile <- function(data, p){
n <- length(data)
sorted_data <- sort(data)
np <- n*p
if(np %% 1 != 0){
k <- ceiling(np)
q <- sorted_data[k]
} else{
k <- np
q <- 0.5 * (sorted_data[k] + sorted_data[k + 1])
}
return(q)
}
cat("Sample Quartiles\n",
"-----------------\n",
"Q1 (25%) :", sample_percentile(data$x, 0.25), "\n",
"Q2 (50%) :", sample_percentile(data$x, 0.50), "\n",
"Q3 (75%) :", sample_percentile(data$x, 0.75), "\n")
Sample Quartiles
-----------------
Q1 (25%) : 8
Q2 (50%) : 10
Q3 (75%) : 12
quantile(data$x, probs = c(0.25,0.50,0.75))
25% 50% 75%
8 10 12
Modes
sample_modes <- function(data){
tab <- table(data)
max_freq <- max(tab)
modes <- names(tab)[tab == max_freq]
list(modes = modes, frequency = max_freq)
}
sample_modes(data$x)
$modes
[1] "11"
$frequency
[1] 16
table(data$x)
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 19
1 5 4 4 7 14 10 10 16 11 6 2 3 4 2 1
sample_modes(c(1,1,2,2,3,4,5,6,7))
$modes
[1] "1" "2"
$frequency
[1] 2
Bivariate &
Multivariate Statistics
Covariance
df <- mtcars
head(df)
\operatorname{Cov}(x, y)=\frac{1}{n-1}
\sum_{i=1}^n\left(x_i-\bar{x}\right)\left(y_i-\bar{y}\right)
sample_covariance <- function(x,y){
n <- length(x)
meanX <- mean(x)
meanY <- mean(y)
sample_cov <- 0
for (i in 1:n){
sample_cov <- sample_cov + (x[i]-meanX)*(y[i]-meanY)
}
return(sample_cov/(n-1))
}
sample_covariance(df$mpg, df$wt)
[1] -5.116685
cov(df$mpg, df$wt)
[1] -5.116685
Pearson’s Correlation
Coefficient
r_{x
y}=\frac{\sum_{i=1}^n\left(x_i-\bar{x}\right)\left(y_i-\bar{y}\right)}{\sqrt{\sum_{i=1}^n\left(x_i-\bar{x}\right)^2}
\sqrt{\sum_{i=1}^n\left(y_i-\bar{y}\right)^2}}
sample_correlation <- function(x,y){
n <- length(x)
meanX <- mean(x)
meanY <- mean(y)
Sxy <- 0
Sxx <- 0
Syy <- 0
for (i in 1:n){
Sxy <- Sxy + (x[i]-meanX)*(y[i]-meanY)
Sxx <- Sxx + (x[i]-meanX)^2
Syy <- Syy + (y[i]-meanY)^2
}
Sx <- sqrt(Sxx)
Sy <- sqrt(Syy)
return(Sxy/(Sx*Sy))
}
sample_correlation(df$mpg, df$wt)
[1] -0.8676594
cor(df$mpg, df$wt)
[1] -0.8676594
Variance-Covariance
Matrix
Multivariate Sample
Setup
Suppose we observe:
- n observations
- p variables
The data matrix is:
X =
\begin{pmatrix}
x_{11} & x_{12} & \cdots & x_{1p} \\
x_{21} & x_{22} & \cdots & x_{2p} \\
\vdots & \vdots & \ddots & \vdots \\
x_{n1} & x_{n2} & \cdots & x_{np}
\end{pmatrix}
The sample mean of variable j:
\bar{x}_j = \frac{1}{n}\sum_{i=1}^{n} x_{ij}
Sample Variance (variable j)
s_{jj} =
\frac{1}{n-1}
\sum_{i=1}^{n}
(x_{ij}-\bar{x}_j)^2
Sample Covariance (variables j and
k)
s_{jk} =
\frac{1}{n-1}
\sum_{i=1}^{n}
(x_{ij}-\bar{x}_j)(x_{ik}-\bar{x}_k)
The sample variance–covariance matrix is:
S=\frac{1}{n-1}\left(\begin{array}{cccc}
\sum_{i=1}^n\left(x_{i 1}-\bar{x}_1\right)^2 &
\sum_{i=1}^n\left(x_{i 1}-\bar{x}_1\right)\left(x_{i 2}-\bar{x}_2\right)
& \cdots & \sum_{i=1}^n\left(x_{i 1}-\bar{x}_1\right)\left(x_{i
p}-\bar{x}_p\right) \\
\sum_{i=1}^n\left(x_{i 2}-\bar{x}_2\right)\left(x_{i 1}-\bar{x}_1\right)
& \sum_{i=1}^n\left(x_{i 2}-\bar{x}_2\right)^2 & \cdots &
\sum_{i=1}^n\left(x_{i 2}-\bar{x}_2\right)\left(x_{i p}-\bar{x}_p\right)
\\
\vdots & \vdots & \ddots & \vdots \\
\sum_{i=1}^n\left(x_{i p}-\bar{x}_p\right)\left(x_{i 1}-\bar{x}_1\right)
& \sum_{i=1}^n\left(x_{i p}-\bar{x}_p\right)\left(x_{i
2}-\bar{x}_2\right) & \cdots & \sum_{i=1}^n\left(x_{i
p}-\bar{x}_p\right)^2
\end{array}\right)
Centering the
Data
Define the mean vector:
\bar{x} =
\begin{pmatrix}
\bar{x}_1 \\
\bar{x}_2 \\
\vdots \\
\bar{x}_p
\end{pmatrix}
Let \mathbf{1} be an n \times 1 vector of ones.
The centered data matrix:
X_c = X - \mathbf{1}\bar{x}^\top
Each row becomes:
x_i - \bar{x}
Matrix
Derivation
Consider:
X_c^\top X_c
=
\sum_{i=1}^{n}
(x_i - \bar{x})(x_i - \bar{x})^\top
The (j,k)-th element equals:
\sum_{i=1}^{n}
(x_{ij}-\bar{x}_j)(x_{ik}-\bar{x}_k)
Therefore,
S = \frac{1}{n-1} X_c^\top X_c
Alternative
Expression
Expanding:
X_c^\top X_c
=
X^\top X - n \bar{x}\bar{x}^\top
Thus,
S =
\frac{1}{n-1}
\left(
X^\top X - n \bar{x}\bar{x}^\top
\right)
Write
variance-covariance matrix function
covariance_matrix <- function(data){
n <- dim(data)[1]
MeanX <- colMeans(data)
Cov <- t(data) %*% data - n * MeanX %*% t(MeanX)
return(Cov/(n-1))
}
covariance_matrix(as.matrix(df)[,1:4])
mpg cyl disp hp
mpg 36.324103 -9.172379 -633.0972 -320.7321
cyl -9.172379 3.189516 199.6603 101.9315
disp -633.097208 199.660282 15360.7998 6721.1587
hp -320.732056 101.931452 6721.1587 4700.8669
cov(as.matrix(df)[,1:4])
mpg cyl disp hp
mpg 36.324103 -9.172379 -633.0972 -320.7321
cyl -9.172379 3.189516 199.6603 101.9315
disp -633.097208 199.660282 15360.7998 6721.1587
hp -320.732056 101.931452 6721.1587 4700.8669
Correlation
Matrix
Standardization
Define the standard deviation of variable j:
s_j = \sqrt{s_{jj}}
Define the diagonal matrix of standard deviations:
D =
\begin{pmatrix}
s_1 & 0 & \cdots & 0 \\
0 & s_2 & \cdots & 0 \\
\vdots & \vdots & \ddots & \vdots \\
0 & 0 & \cdots & s_p
\end{pmatrix}
Alternative
Derivation
Define the standardized variables:
z_{ij} =
\frac{x_{ij}-\bar{x}_j}{s_j}
Let Z be the standardized data
matrix.
Then,
R = \frac{1}{n-1} Z^\top Z
Thus, the correlation matrix is simply the covariance matrix of
standardized variables.
Write correlation
matrix function
correlation_matrix <- function(data){
S <- cov(data)
D <- diag(sqrt(diag(S)))
Corr <- solve(D) %*% S %*% solve(D)
return(Corr)
}
correlation_matrix(as.matrix(df)[,1:4])
[,1] [,2] [,3] [,4]
[1,] 1.0000000 -0.8521620 -0.8475514 -0.7761684
[2,] -0.8521620 1.0000000 0.9020329 0.8324475
[3,] -0.8475514 0.9020329 1.0000000 0.7909486
[4,] -0.7761684 0.8324475 0.7909486 1.0000000
cor(as.matrix(df)[,1:4])
mpg cyl disp hp
mpg 1.0000000 -0.8521620 -0.8475514 -0.7761684
cyl -0.8521620 1.0000000 0.9020329 0.8324475
disp -0.8475514 0.9020329 1.0000000 0.7909486
hp -0.7761684 0.8324475 0.7909486 1.0000000
correlation_matrix_std <- function(data){
n <- dim(data)[1]
Z <- scale(data, center = TRUE, scale = TRUE)
R <- (t(Z) %*% Z) / (n - 1)
return(R)
}
correlation_matrix_std(as.matrix(df)[,1:4])
mpg cyl disp hp
mpg 1.0000000 -0.8521620 -0.8475514 -0.7761684
cyl -0.8521620 1.0000000 0.9020329 0.8324475
disp -0.8475514 0.9020329 1.0000000 0.7909486
hp -0.7761684 0.8324475 0.7909486 1.0000000
Handling Categorical
Data
Contingency Table
(Crosstab)
df$Transmission <- factor(df$am,
levels = c(0,1),
labels = c("Automatic","Manual"))
df$Cylinders <- factor(df$cyl)
df$Engine <- factor(df$vs,
levels = c(0,1),
labels = c("V-shaped","Straight"))
head(df[,c("Transmission","Cylinders","Engine")], 10)
Crosstab
tab1 <- table(df$Transmission, df$Engine)
tab1
V-shaped Straight
Automatic 12 7
Manual 6 7
Row
Proportions
prop.table(tab1, margin = 1)
V-shaped Straight
Automatic 0.6315789 0.3684211
Manual 0.4615385 0.5384615
Column
Proportions
prop.table(tab1, margin = 2)
V-shaped Straight
Automatic 0.6666667 0.5000000
Manual 0.3333333 0.5000000
Three-way
Contingency Table
tab3 <- table(df$Transmission,
df$Cylinders,
df$Engine)
tab3
, , = V-shaped
4 6 8
Automatic 0 0 12
Manual 1 3 2
, , = Straight
4 6 8
Automatic 3 4 0
Manual 7 0 0
ftable(tab3)
V-shaped Straight
Automatic 4 0 3
6 0 4
8 12 0
Manual 4 1 7
6 3 0
8 2 0
LS0tCnRpdGxlOiAiQ29tcHV0YXRpb25hbCBTdGF0aXN0aWNzIFdlZWsgMiIKCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbWF0aF9tZXRob2Q6IGthdGV4CiAgICB0aGVtZTogeWV0aQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgZGZfcHJpbnQ6IHBhZ2VkCi0tLQoKIyBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzCgoKYGBge3J9CnVybDEgPC0gImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9ub3ZyaXN1aGVybWkvZGF0YXNldC9yZWZzL2hlYWRzL21haW4vZGF0YV9zYW1wbGUuY3N2IgpkYXRhIDwtIHJlYWQuY3N2KHVybDEpCmhlYWQoZGF0YSkKYGBgCgpgYGB7cn0KZGF0YV9zdGF0cyA8LSBzdW1tYXJ5KGRhdGEpCnByaW50KGRhdGFfc3RhdHMpCmBgYAoKIyMgTWVhbgoKJCQKXHRleHQgeyBNZWFuOiB9IFxiYXJ7eH09XGZyYWN7MX17bn0gXHN1bV97aT0xfV5uIHhfaQokJAoKYGBge3J9CnNhbXBsZV9tZWFuIDwtIGZ1bmN0aW9uKGRhdGEpewogIG4gPC0gbGVuZ3RoKGRhdGEpCiAgc3VtX2RhdGEgPC0gMAogIGZvciAoaSBpbiAxOm4pewogICAgc3VtX2RhdGEgPC0gc3VtX2RhdGEgKyBkYXRhW2ldCiAgfQogIHJldHVybihzdW1fZGF0YS9uKQp9CmBgYAoKCmBgYHtyfQpzYW1wbGVfbWVhbihkYXRhJHgpCmBgYAoKYGBge3J9CiMgYnVpbHQtaW4gZnVuY3Rpb24gaW4gUgptZWFuKGRhdGEkeCkKYGBgCgotLS0KCiMjIFZhcmlhbmNlCgokJApcdGV4dCB7IFZhcmlhbmNlOiB9IFNeMj1cZnJhY3sxfXtuLTF9IFxzdW1fe2k9MX1eblxsZWZ0KHhfaS1cYmFye3h9XHJpZ2h0KV4yCiQkCgpgYGB7cn0Kc2FtcGxlX3ZhcmlhbmNlIDwtIGZ1bmN0aW9uKGRhdGEpewogIG4gPC0gbGVuZ3RoKGRhdGEpCiAgCiAgc3VtX2RhdGEgPC0gMAogIGZvciAoaSBpbiAxOm4pewogICAgc3VtX2RhdGEgPC0gc3VtX2RhdGEgKyBkYXRhW2ldCiAgfQogIG1lYW5fZGF0YSA8LSBzdW1fZGF0YS9uCiAgCiAgdmFyX2RhdGEgPC0gMAogIGZvciAoaSBpbiAxOm4pewogICAgdmFyX2RhdGEgPC0gdmFyX2RhdGEgKyAoZGF0YVtpXS1tZWFuX2RhdGEpXjIKICB9CiAgCiAgcmV0dXJuKHZhcl9kYXRhLyhuLTEpKQp9CgpzYW1wbGVfdmFyaWFuY2UoZGF0YSR4KQpgYGAKCmBgYHtyfQojIGJ1aWx0LWluIGZ1bmN0aW9uIGluIFIKdmFyKGRhdGEkeCkKYGBgCgotLS0KCiMjIE1lZGlhbiwgUXVhcnRpbGVzICYgUGVyY2VudGlsZXMKCgpgYGB7cn0KIyBTb3J0aW5nIEFsZ29yaXRobQpidWJibGVfc29ydCA8LSBmdW5jdGlvbih4KSB7CiAgbiA8LSBsZW5ndGgoeCkKICAKICBmb3IgKGkgaW4gMToobi0xKSkgewogICAgZm9yIChqIGluIDE6KG4taSkpIHsKICAgICAgaWYgKHhbal0gPiB4W2orMV0pIHsKICAgICAgICB0ZW1wIDwtIHhbal0KICAgICAgICB4W2pdIDwtIHhbaisxXQogICAgICAgIHhbaisxXSA8LSB0ZW1wCiAgICAgIH0KICAgIH0KICB9CiAgCiAgcmV0dXJuKHgpCn0KYGBgCgpgYGB7cn0KYnViYmxlX3NvcnQoZGF0YSR4KQpgYGAKCmBgYHtyfQpzb3J0KGRhdGEkeCkKYGBgCgpDYWxjdWxhdGluZyB0aGUgU2FtcGxlICQxMDBwJC10aCBQZXJjZW50aWxlCgoxLiBPcmRlciB0aGUgZGF0YSBmcm9tIHNtYWxsZXN0IHRvIGxhcmdlc3QuCjIuIERldGVybWluZSB0aGUgcHJvZHVjdCAoc2FtcGxlIHNpemUpICRcdGltZXMkIChwcm9wb3J0aW9uKSAkPSBuIHAkLgoKSWYgJG5wJCBpcyBub3QgYW4gaW50ZWdlciwgcm91bmQgaXQgdXAgdG8gdGhlIG5leHQgaW50ZWdlciBhbmQgZmluZCB0aGUgY29ycmVzcG9uZGluZyBvcmRlcmVkIHZhbHVlLgoKSWYgJG5wJCBpcyBhbiBpbnRlZ2VyLCBzYXkgJGskLCBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2Ugb2YgdGhlICRrJHRoIGFuZCAkKGsrMSkkc3Qgb3JkZXJlZCB2YWx1ZXMuCgokJApcYmVnaW57YXJyYXl9e2xsfQpcdGV4dCB7IExvd2VyIChmaXJzdCkgcXVhcnRpbGUgfSAmIFxtYXRocm17UX1fMT0yNSBcdGV4dCB7dGggcGVyY2VudGlsZSB9IFxcClx0ZXh0IHsgU2Vjb25kIHF1YXJ0aWxlIChvciBtZWRpYW4pIH0gJiBcbWF0aHJte1F9XzI9NTAgXHRleHQge3RoIHBlcmNlbnRpbGUgfSBcXApcdGV4dCB7IFVwcGVyICh0aGlyZCkgcXVhcnRpbGUgfSAmIFxtYXRocm17UX1fMz03NSBcdGV4dCB7dGggcGVyY2VudGlsZSB9ClxlbmR7YXJyYXl9CiQkCgpgYGB7cn0Kc2FtcGxlX21lZGlhbiA8LSBmdW5jdGlvbihkYXRhKXsKICBuIDwtIGxlbmd0aChkYXRhKQogIHNvcnRlZF9kYXRhIDwtIHNvcnQoZGF0YSkKICBpZiAobiAlJSAyID09IDEpewogICAgbWVkIDwtIHNvcnRlZF9kYXRhWyhuKzEpLzJdCiAgfSBlbHNlewogICAgbWVkIDwtIDAuNSooc29ydGVkX2RhdGFbbi8yXSArIHNvcnRlZF9kYXRhW24vMisxXSkKICB9CiAgcmV0dXJuKG1lZCkKfQpzYW1wbGVfbWVkaWFuKGRhdGEkeCkKYGBgCgpgYGB7cn0KbWVkaWFuKGRhdGEkeCkKYGBgCgpgYGB7cn0Kc2FtcGxlX3BlcmNlbnRpbGUgPC0gZnVuY3Rpb24oZGF0YSwgcCl7CiAgbiA8LSBsZW5ndGgoZGF0YSkKICBzb3J0ZWRfZGF0YSA8LSBzb3J0KGRhdGEpCiAgbnAgPC0gbipwCiAgCiAgaWYobnAgJSUgMSAhPSAwKXsgICAgICAgICAgICAgICAgCiAgICBrIDwtIGNlaWxpbmcobnApCiAgICBxIDwtIHNvcnRlZF9kYXRhW2tdCiAgfSBlbHNleyAKICAgIGsgPC0gbnAKICAgIHEgPC0gMC41ICogKHNvcnRlZF9kYXRhW2tdICsgc29ydGVkX2RhdGFbayArIDFdKQogIH0KICByZXR1cm4ocSkKfQoKY2F0KCJTYW1wbGUgUXVhcnRpbGVzXG4iLAogICAgIi0tLS0tLS0tLS0tLS0tLS0tXG4iLAogICAgIlExICgyNSUpIDoiLCBzYW1wbGVfcGVyY2VudGlsZShkYXRhJHgsIDAuMjUpLCAiXG4iLAogICAgIlEyICg1MCUpIDoiLCBzYW1wbGVfcGVyY2VudGlsZShkYXRhJHgsIDAuNTApLCAiXG4iLAogICAgIlEzICg3NSUpIDoiLCBzYW1wbGVfcGVyY2VudGlsZShkYXRhJHgsIDAuNzUpLCAiXG4iKQpgYGAKCmBgYHtyfQpxdWFudGlsZShkYXRhJHgsIHByb2JzID0gYygwLjI1LDAuNTAsMC43NSkpCmBgYAoKLS0tCgojIyBNb2RlcwoKYGBge3J9CnNhbXBsZV9tb2RlcyA8LSBmdW5jdGlvbihkYXRhKXsKICB0YWIgPC0gdGFibGUoZGF0YSkKICBtYXhfZnJlcSA8LSBtYXgodGFiKQogIG1vZGVzIDwtIG5hbWVzKHRhYilbdGFiID09IG1heF9mcmVxXQogIGxpc3QobW9kZXMgPSBtb2RlcywgZnJlcXVlbmN5ID0gbWF4X2ZyZXEpCn0KCnNhbXBsZV9tb2RlcyhkYXRhJHgpCmBgYAoKYGBge3J9CnRhYmxlKGRhdGEkeCkKYGBgCgoKYGBge3J9CnNhbXBsZV9tb2RlcyhjKDEsMSwyLDIsMyw0LDUsNiw3KSkKYGBgCgojIEJpdmFyaWF0ZSAmIE11bHRpdmFyaWF0ZSBTdGF0aXN0aWNzCgojIyBDb3ZhcmlhbmNlIAoKYGBge3J9CmRmIDwtIG10Y2FycwpoZWFkKGRmKQpgYGAKCiQkClxvcGVyYXRvcm5hbWV7Q292fSh4LCB5KT1cZnJhY3sxfXtuLTF9IFxzdW1fe2k9MX1eblxsZWZ0KHhfaS1cYmFye3h9XHJpZ2h0KVxsZWZ0KHlfaS1cYmFye3l9XHJpZ2h0KQokJAoKYGBge3J9CnNhbXBsZV9jb3ZhcmlhbmNlIDwtIGZ1bmN0aW9uKHgseSl7CiAgbiA8LSBsZW5ndGgoeCkKICBtZWFuWCA8LSBtZWFuKHgpCiAgbWVhblkgPC0gbWVhbih5KQogIAogIHNhbXBsZV9jb3YgPC0gMAogIGZvciAoaSBpbiAxOm4pewogICAgc2FtcGxlX2NvdiA8LSBzYW1wbGVfY292ICsgKHhbaV0tbWVhblgpKih5W2ldLW1lYW5ZKQogIH0KICAKICByZXR1cm4oc2FtcGxlX2Nvdi8obi0xKSkKfQoKc2FtcGxlX2NvdmFyaWFuY2UoZGYkbXBnLCBkZiR3dCkKYGBgCgpgYGB7cn0KY292KGRmJG1wZywgZGYkd3QpCmBgYAoKLS0tCgojIyBQZWFyc29uJ3MgQ29ycmVsYXRpb24gQ29lZmZpY2llbnQKCiQkCnJfe3ggeX09XGZyYWN7XHN1bV97aT0xfV5uXGxlZnQoeF9pLVxiYXJ7eH1ccmlnaHQpXGxlZnQoeV9pLVxiYXJ7eX1ccmlnaHQpfXtcc3FydHtcc3VtX3tpPTF9Xm5cbGVmdCh4X2ktXGJhcnt4fVxyaWdodCleMn0gXHNxcnR7XHN1bV97aT0xfV5uXGxlZnQoeV9pLVxiYXJ7eX1ccmlnaHQpXjJ9fQokJApgYGB7cn0Kc2FtcGxlX2NvcnJlbGF0aW9uIDwtIGZ1bmN0aW9uKHgseSl7CiAgbiA8LSBsZW5ndGgoeCkKICBtZWFuWCA8LSBtZWFuKHgpCiAgbWVhblkgPC0gbWVhbih5KQogIAogIFN4eSA8LSAwCiAgU3h4IDwtIDAKICBTeXkgPC0gMAogIAogIGZvciAoaSBpbiAxOm4pewogICAgU3h5IDwtIFN4eSArICh4W2ldLW1lYW5YKSooeVtpXS1tZWFuWSkKICAgIFN4eCA8LSBTeHggKyAoeFtpXS1tZWFuWCleMgogICAgU3l5IDwtIFN5eSArICh5W2ldLW1lYW5ZKV4yCiAgfQogIAogIFN4IDwtIHNxcnQoU3h4KQogIFN5IDwtIHNxcnQoU3l5KQogIAogIHJldHVybihTeHkvKFN4KlN5KSkKfQoKc2FtcGxlX2NvcnJlbGF0aW9uKGRmJG1wZywgZGYkd3QpCmBgYAoKYGBge3J9CmNvcihkZiRtcGcsIGRmJHd0KQpgYGAKCi0tLQoKIyMgVmFyaWFuY2UtQ292YXJpYW5jZSBNYXRyaXgKCiMjIyBNdWx0aXZhcmlhdGUgU2FtcGxlIFNldHVwCgpTdXBwb3NlIHdlIG9ic2VydmU6CgotIFwoIG4gXCkgb2JzZXJ2YXRpb25zICAKLSBcKCBwIFwpIHZhcmlhYmxlcyAgCgpUaGUgZGF0YSBtYXRyaXggaXM6CgpcWwpYID0KXGJlZ2lue3BtYXRyaXh9CnhfezExfSAmIHhfezEyfSAmIFxjZG90cyAmIHhfezFwfSBcXAp4X3syMX0gJiB4X3syMn0gJiBcY2RvdHMgJiB4X3sycH0gXFwKXHZkb3RzICYgXHZkb3RzICYgXGRkb3RzICYgXHZkb3RzIFxcCnhfe24xfSAmIHhfe24yfSAmIFxjZG90cyAmIHhfe25wfQpcZW5ke3BtYXRyaXh9ClxdCgpUaGUgc2FtcGxlIG1lYW4gb2YgdmFyaWFibGUgXCggaiBcKToKClxbClxiYXJ7eH1faiA9IFxmcmFjezF9e259XHN1bV97aT0xfV57bn0geF97aWp9ClxdCgpTYW1wbGUgVmFyaWFuY2UgKHZhcmlhYmxlIFwoIGogXCkpCgpcWwpzX3tqan0gPQpcZnJhY3sxfXtuLTF9ClxzdW1fe2k9MX1ee259Cih4X3tpan0tXGJhcnt4fV9qKV4yClxdCgpTYW1wbGUgQ292YXJpYW5jZSAodmFyaWFibGVzIFwoIGogXCkgYW5kIFwoIGsgXCkpCgpcWwpzX3tqa30gPQpcZnJhY3sxfXtuLTF9ClxzdW1fe2k9MX1ee259Cih4X3tpan0tXGJhcnt4fV9qKSh4X3tpa30tXGJhcnt4fV9rKQpcXQoKCgoKClRoZSBzYW1wbGUgdmFyaWFuY2XigJNjb3ZhcmlhbmNlIG1hdHJpeCBpczoKCiQkClM9XGZyYWN7MX17bi0xfVxsZWZ0KFxiZWdpbnthcnJheX17Y2NjY30KXHN1bV97aT0xfV5uXGxlZnQoeF97aSAxfS1cYmFye3h9XzFccmlnaHQpXjIgJiBcc3VtX3tpPTF9Xm5cbGVmdCh4X3tpIDF9LVxiYXJ7eH1fMVxyaWdodClcbGVmdCh4X3tpIDJ9LVxiYXJ7eH1fMlxyaWdodCkgJiBcY2RvdHMgJiBcc3VtX3tpPTF9Xm5cbGVmdCh4X3tpIDF9LVxiYXJ7eH1fMVxyaWdodClcbGVmdCh4X3tpIHB9LVxiYXJ7eH1fcFxyaWdodCkgXFwKXHN1bV97aT0xfV5uXGxlZnQoeF97aSAyfS1cYmFye3h9XzJccmlnaHQpXGxlZnQoeF97aSAxfS1cYmFye3h9XzFccmlnaHQpICYgXHN1bV97aT0xfV5uXGxlZnQoeF97aSAyfS1cYmFye3h9XzJccmlnaHQpXjIgJiBcY2RvdHMgJiBcc3VtX3tpPTF9Xm5cbGVmdCh4X3tpIDJ9LVxiYXJ7eH1fMlxyaWdodClcbGVmdCh4X3tpIHB9LVxiYXJ7eH1fcFxyaWdodCkgXFwKXHZkb3RzICYgXHZkb3RzICYgXGRkb3RzICYgXHZkb3RzIFxcClxzdW1fe2k9MX1eblxsZWZ0KHhfe2kgcH0tXGJhcnt4fV9wXHJpZ2h0KVxsZWZ0KHhfe2kgMX0tXGJhcnt4fV8xXHJpZ2h0KSAmIFxzdW1fe2k9MX1eblxsZWZ0KHhfe2kgcH0tXGJhcnt4fV9wXHJpZ2h0KVxsZWZ0KHhfe2kgMn0tXGJhcnt4fV8yXHJpZ2h0KSAmIFxjZG90cyAmIFxzdW1fe2k9MX1eblxsZWZ0KHhfe2kgcH0tXGJhcnt4fV9wXHJpZ2h0KV4yClxlbmR7YXJyYXl9XHJpZ2h0KQokJAoKLS0tCgojIyMgQ2VudGVyaW5nIHRoZSBEYXRhCgpEZWZpbmUgdGhlIG1lYW4gdmVjdG9yOgoKXFsKXGJhcnt4fSA9ClxiZWdpbntwbWF0cml4fQpcYmFye3h9XzEgXFwKXGJhcnt4fV8yIFxcClx2ZG90cyBcXApcYmFye3h9X3AKXGVuZHtwbWF0cml4fQpcXQoKTGV0IFwoIFxtYXRoYmZ7MX0gXCkgYmUgYW4gXCggbiBcdGltZXMgMSBcKSB2ZWN0b3Igb2Ygb25lcy4KClRoZSBjZW50ZXJlZCBkYXRhIG1hdHJpeDoKClxbClhfYyA9IFggLSBcbWF0aGJmezF9XGJhcnt4fV5cdG9wClxdCgpFYWNoIHJvdyBiZWNvbWVzOgoKXFsKeF9pIC0gXGJhcnt4fQpcXQoKLS0tCgojIyMgTWF0cml4IERlcml2YXRpb24KCkNvbnNpZGVyOgoKXFsKWF9jXlx0b3AgWF9jCj0KXHN1bV97aT0xfV57bn0KKHhfaSAtIFxiYXJ7eH0pKHhfaSAtIFxiYXJ7eH0pXlx0b3AKXF0KClRoZSBcKCAoaixrKSBcKS10aCBlbGVtZW50IGVxdWFsczoKClxbClxzdW1fe2k9MX1ee259Cih4X3tpan0tXGJhcnt4fV9qKSh4X3tpa30tXGJhcnt4fV9rKQpcXQoKVGhlcmVmb3JlLAoKXFsKUyA9IFxmcmFjezF9e24tMX0gWF9jXlx0b3AgWF9jClxdCgotLS0KCiMjIyBBbHRlcm5hdGl2ZSBFeHByZXNzaW9uCgpFeHBhbmRpbmc6CgpcWwpYX2NeXHRvcCBYX2MKPQpYXlx0b3AgWCAtIG4gXGJhcnt4fVxiYXJ7eH1eXHRvcApcXQoKVGh1cywKClxbClMgPQpcZnJhY3sxfXtuLTF9ClxsZWZ0KApYXlx0b3AgWCAtIG4gXGJhcnt4fVxiYXJ7eH1eXHRvcApccmlnaHQpClxdCgotLS0KCiMjIyBXcml0ZSB2YXJpYW5jZS1jb3ZhcmlhbmNlIG1hdHJpeCBmdW5jdGlvbgoKYGBge3J9CmNvdmFyaWFuY2VfbWF0cml4IDwtIGZ1bmN0aW9uKGRhdGEpewogIG4gPC0gZGltKGRhdGEpWzFdCiAgTWVhblggPC0gY29sTWVhbnMoZGF0YSkKICBDb3YgPC0gdChkYXRhKSAlKiUgZGF0YSAtIG4gKiBNZWFuWCAlKiUgdChNZWFuWCkKICByZXR1cm4oQ292LyhuLTEpKQp9Cgpjb3ZhcmlhbmNlX21hdHJpeChhcy5tYXRyaXgoZGYpWywxOjRdKQpgYGAKCmBgYHtyfQpjb3YoYXMubWF0cml4KGRmKVssMTo0XSkKYGBgCgojIyBDb3JyZWxhdGlvbiBNYXRyaXgKCiMjIyBTY2FsYXIgZm9ybSBzYW1wbGUgY29ycmVsYXRpb24KClRoZSBzYW1wbGUgY29ycmVsYXRpb24gYmV0d2VlbiB2YXJpYWJsZXMgXCggaiBcKSBhbmQgXCggayBcKToKClxbCnJfe2prfSA9ClxmcmFje3Nfe2prfX17XHNxcnR7c197amp9fVxzcXJ0e3Nfe2trfX19ClxdCgp3aGVyZQoKXFsKc197amp9ID0gXHRleHR7VmFyfShYX2opLCBccXVhZApzX3tra30gPSBcdGV4dHtWYXJ9KFhfaykKXF0KCi0tLQoKIyMjIEV4cGxpY2l0IE1hdHJpeCBGb3JtCgpcWwpSID0KXGJlZ2lue3BtYXRyaXh9CjEgJiByX3sxMn0gJiBcY2RvdHMgJiByX3sxcH0gXFwKcl97MjF9ICYgMSAmIFxjZG90cyAmIHJfezJwfSBcXApcdmRvdHMgJiBcdmRvdHMgJiBcZGRvdHMgJiBcdmRvdHMgXFwKcl97cDF9ICYgcl97cDJ9ICYgXGNkb3RzICYgMQpcZW5ke3BtYXRyaXh9ClxdCgotLS0KCiMjIyBTdGFuZGFyZGl6YXRpb24KCkRlZmluZSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHZhcmlhYmxlIFwoIGogXCk6CgpcWwpzX2ogPSBcc3FydHtzX3tqan19ClxdCgpEZWZpbmUgdGhlIGRpYWdvbmFsIG1hdHJpeCBvZiBzdGFuZGFyZCBkZXZpYXRpb25zOgoKXFsKRCA9ClxiZWdpbntwbWF0cml4fQpzXzEgJiAwICYgXGNkb3RzICYgMCBcXAowICYgc18yICYgXGNkb3RzICYgMCBcXApcdmRvdHMgJiBcdmRvdHMgJiBcZGRvdHMgJiBcdmRvdHMgXFwKMCAmIDAgJiBcY2RvdHMgJiBzX3AKXGVuZHtwbWF0cml4fQpcXQoKLS0tCgojIyMgTWF0cml4IFRyYW5zZm9ybWF0aW9uCgpUaGUgY29ycmVsYXRpb24gbWF0cml4IGlzIG9idGFpbmVkIGJ5IHN0YW5kYXJkaXppbmcgdGhlIGNvdmFyaWFuY2UgbWF0cml4OgoKXFsKUiA9IEReey0xfSBTIEReey0xfQpcXQoKRWFjaCBlbGVtZW50IGJlY29tZXM6CgpcWwpyX3tqa30gPQpcZnJhY3tzX3tqa319e3NfaiBzX2t9ClxdCgotLS0KCiMjIyBBbHRlcm5hdGl2ZSBEZXJpdmF0aW9uCgpEZWZpbmUgdGhlIHN0YW5kYXJkaXplZCB2YXJpYWJsZXM6CgpcWwp6X3tpan0gPQpcZnJhY3t4X3tpan0tXGJhcnt4fV9qfXtzX2p9ClxdCgpMZXQgXCggWiBcKSBiZSB0aGUgc3RhbmRhcmRpemVkIGRhdGEgbWF0cml4LgoKVGhlbiwKClxbClIgPSBcZnJhY3sxfXtuLTF9IFpeXHRvcCBaClxdCgpUaHVzLCB0aGUgY29ycmVsYXRpb24gbWF0cml4IGlzIHNpbXBseSB0aGUgY292YXJpYW5jZSBtYXRyaXggb2Ygc3RhbmRhcmRpemVkIHZhcmlhYmxlcy4KCi0tLQoKIyMjIFdyaXRlIGNvcnJlbGF0aW9uIG1hdHJpeCBmdW5jdGlvbgoKYGBge3J9CmNvcnJlbGF0aW9uX21hdHJpeCA8LSBmdW5jdGlvbihkYXRhKXsKICBTIDwtIGNvdihkYXRhKQogIEQgPC0gZGlhZyhzcXJ0KGRpYWcoUykpKQogIENvcnIgPC0gc29sdmUoRCkgJSolIFMgJSolIHNvbHZlKEQpCiAgcmV0dXJuKENvcnIpCn0KCmNvcnJlbGF0aW9uX21hdHJpeChhcy5tYXRyaXgoZGYpWywxOjRdKQpgYGAKCmBgYHtyfQpjb3IoYXMubWF0cml4KGRmKVssMTo0XSkKYGBgCgpgYGB7cn0KY29ycmVsYXRpb25fbWF0cml4X3N0ZCA8LSBmdW5jdGlvbihkYXRhKXsKICBuIDwtIGRpbShkYXRhKVsxXQogIFogPC0gc2NhbGUoZGF0YSwgY2VudGVyID0gVFJVRSwgc2NhbGUgPSBUUlVFKQogIFIgPC0gKHQoWikgJSolIFopIC8gKG4gLSAxKQogIHJldHVybihSKQp9Cgpjb3JyZWxhdGlvbl9tYXRyaXhfc3RkKGFzLm1hdHJpeChkZilbLDE6NF0pCmBgYAoKIyBIYW5kbGluZyBDYXRlZ29yaWNhbCBEYXRhCgojIyBDb250aW5nZW5jeSBUYWJsZSAoQ3Jvc3N0YWIpCgpgYGB7cn0KZGYkVHJhbnNtaXNzaW9uIDwtIGZhY3RvcihkZiRhbSwKICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKDAsMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQXV0b21hdGljIiwiTWFudWFsIikpCgpkZiRDeWxpbmRlcnMgPC0gZmFjdG9yKGRmJGN5bCkKCmRmJEVuZ2luZSA8LSBmYWN0b3IoZGYkdnMsCiAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygwLDEpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlYtc2hhcGVkIiwiU3RyYWlnaHQiKSkKCmhlYWQoZGZbLGMoIlRyYW5zbWlzc2lvbiIsIkN5bGluZGVycyIsIkVuZ2luZSIpXSwgMTApCmBgYAoKIyMjIENyb3NzdGFiCgpgYGB7cn0KdGFiMSA8LSB0YWJsZShkZiRUcmFuc21pc3Npb24sIGRmJEVuZ2luZSkKdGFiMQpgYGAKCiMjIyBSb3cgUHJvcG9ydGlvbnMKCmBgYHtyfQpwcm9wLnRhYmxlKHRhYjEsIG1hcmdpbiA9IDEpCmBgYAoKIyMjIENvbHVtbiBQcm9wb3J0aW9ucwoKYGBge3J9CnByb3AudGFibGUodGFiMSwgbWFyZ2luID0gMikKYGBgCgojIyMgVGhyZWUtd2F5IENvbnRpbmdlbmN5IFRhYmxlCgpgYGB7cn0KdGFiMyA8LSB0YWJsZShkZiRUcmFuc21pc3Npb24sCiAgICAgICAgICAgICAgZGYkQ3lsaW5kZXJzLAogICAgICAgICAgICAgIGRmJEVuZ2luZSkKCnRhYjMKYGBgCgpgYGB7cn0KZnRhYmxlKHRhYjMpCmBgYAoKCgoK