Aplikasi Robust Linear Regression dengan Pendekatan Least Absolute Deviation (LAD) Menggunakan Linear Programming pada Kasus Market Value Pemain Sepak Bola Profesional Liga Inggris Musim 2020-2021

Alfidhia Rahman NJ

2023-05-14

library(dplyr)
library(tidyr)
library(readxl)
library(dichromat)
library(tidyverse)
library(skedastic)
library(car)
library(lmtest)
library(olsrr)

Input Data

df <- readxl::read_excel("EPL 2020-2021.xlsx")

str(df)
## tibble [293 × 12] (S3: tbl_df/tbl/data.frame)
##  $ MarketValue     : num [1:293] 1304 1130 956 695 782 ...
##  $ Age             : num [1:293] 21 24 29 28 26 21 21 27 22 19 ...
##  $ Starts          : num [1:293] 32 29 24 23 21 18 18 15 12 10 ...
##  $ Mins            : num [1:293] 2890 2602 2146 2010 1815 ...
##  $ Goals           : num [1:293] 6 6 0 7 0 4 4 2 6 2 ...
##  $ Assists         : num [1:293] 5 8 2 1 1 2 3 3 1 3 ...
##  $ Passes_Attempted: num [1:293] 1881 826 1504 1739 1737 ...
##  $ xG              : num [1:293] 0.21 0.41 0.04 0.31 0.05 0.28 0.37 0.15 0.56 0.12 ...
##  $ xA              : num [1:293] 0.24 0.21 0.05 0.09 0.09 0.14 0.09 0.28 0.07 0.26 ...
##  $ Yellow_Cards    : num [1:293] 2 2 7 2 4 2 2 3 0 0 ...
##  $ PlayerStatus    : num [1:293] 0 1 1 1 1 1 1 1 0 0 ...
##  $ TeamStatus      : num [1:293] 1 1 1 1 1 1 1 1 1 1 ...

Exploratory Data Analysis

ggplot(df) +
  aes(x ="", y = MarketValue, fill = as.factor(TeamStatus)) +
  geom_boxplot() +
  scale_fill_hue(direction = 1) +
  coord_flip() +
  theme_minimal()

ggplot(df) +
  aes(x ="", y = MarketValue, fill = as.factor(PlayerStatus)) +
  geom_boxplot() +
  scale_fill_hue(direction = 1) +
  coord_flip() +
  theme_minimal()

GGally::ggpairs(df[1:10])

Terlihat bahwa peubah yang memiliki korelasi paling tinggi dengan Y adalah Assists, lalu diikuti oleh Goals. Sebaran peubah respon terlihat menjulur ke kanan yang menunjukkan bahwa sebagian besar pemain liga inggris memiliki Market Value yang rendah.

Histogram

par(mfrow=c(3,3))
for(i in c(1:9)){
  hist(df[[i]], col="green",  main = paste("Histogram of",colnames(df)[i]), xlab=colnames(df)[i])
}

Barplot

par(mfrow=c(1,3))
green_palette <- colorRampPalette(c("lightgreen", "darkgreen"))
bar_colors <- green_palette(length(table(df$Yellow_Cards)))
barplot(table(df$Yellow_Cards), col=bar_colors, xlab="Category", ylab="Frequency",main = "Barplot of Yellow Cards")
barplot(table(df$TeamStatus), xlab="Category", ylab="Frequency", main = "Barplot of Team Status", col=c("green","darkgreen"))
barplot(table(df$PlayerStatus), col=c("green","darkgreen"), xlab="Category", ylab="Frequency",main = "Barplot of Player Status")

Boxplot

par(mfrow=c(1,3))
for(i in c(1:9)){
  boxplot(df[i], main = paste("Boxplot of",colnames(df)[i]), col = "green")
}

Regresi Linear Berganda (OLS)

ols <- lm(MarketValue ~ ., data = df)
summary(ols)
## 
## Call:
## lm(formula = MarketValue ~ ., data = df)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -577.54 -131.35  -14.22  126.06  757.49 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      458.62942   79.28373   5.785 1.93e-08 ***
## Age              -19.50909    3.17648  -6.142 2.78e-09 ***
## Starts            -4.68537   12.07767  -0.388   0.6984    
## Mins               0.01054    0.14408   0.073   0.9418    
## Goals             28.74046    5.85908   4.905 1.58e-06 ***
## Assists           42.62719    8.62492   4.942 1.33e-06 ***
## Passes_Attempted   0.23938    0.05296   4.520 9.12e-06 ***
## xG                58.67790  105.32516   0.557   0.5779    
## xA                32.05856  147.73562   0.217   0.8284    
## Yellow_Cards       3.56849    8.12389   0.439   0.6608    
## PlayerStatus      51.05874   28.39792   1.798   0.0733 .  
## TeamStatus       266.16171   36.17773   7.357 2.07e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 218.6 on 281 degrees of freedom
## Multiple R-squared:  0.6613, Adjusted R-squared:  0.6481 
## F-statistic: 49.88 on 11 and 281 DF,  p-value: < 2.2e-16

Cek multikolinearitas

vif(ols)
##              Age           Starts             Mins            Goals 
##         1.109478       116.682090       123.753891         3.582797 
##          Assists Passes_Attempted               xG               xA 
##         2.613650         5.694110         1.980655         1.381770 
##     Yellow_Cards     PlayerStatus       TeamStatus 
##         2.269907         1.105888         1.273737

Starts dan Mins berkorelasi kuat, sehingga perlu eliminasi peubah.

df <- as.data.frame(df)
df <- df %>% dplyr::select(-Starts)
str(df)
## 'data.frame':    293 obs. of  11 variables:
##  $ MarketValue     : num  1304 1130 956 695 782 ...
##  $ Age             : num  21 24 29 28 26 21 21 27 22 19 ...
##  $ Mins            : num  2890 2602 2146 2010 1815 ...
##  $ Goals           : num  6 6 0 7 0 4 4 2 6 2 ...
##  $ Assists         : num  5 8 2 1 1 2 3 3 1 3 ...
##  $ Passes_Attempted: num  1881 826 1504 1739 1737 ...
##  $ xG              : num  0.21 0.41 0.04 0.31 0.05 0.28 0.37 0.15 0.56 0.12 ...
##  $ xA              : num  0.24 0.21 0.05 0.09 0.09 0.14 0.09 0.28 0.07 0.26 ...
##  $ Yellow_Cards    : num  2 2 7 2 4 2 2 3 0 0 ...
##  $ PlayerStatus    : num  0 1 1 1 1 1 1 1 0 0 ...
##  $ TeamStatus      : num  1 1 1 1 1 1 1 1 1 1 ...

Cek ulang multikolinearitas

ols2 <- lm(MarketValue ~ ., data = df)
sumols <- summary(ols2)
vif(ols2)
##              Age             Mins            Goals          Assists 
##         1.095599         7.764766         3.558352         2.611615 
## Passes_Attempted               xG               xA     Yellow_Cards 
##         5.694075         1.961644         1.362415         2.257229 
##     PlayerStatus       TeamStatus 
##         1.096598         1.273583

Terlihat bahwa nilai VIF yang besar terdapat pada peubah Starting Eleven dan Minutes Played sehingga perlu dilakukan reduksi peubah pada peubah Starting Eleven.

sumols
## 
## Call:
## lm(formula = MarketValue ~ ., data = df)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -573.0 -133.4  -13.8  120.5  759.1 
## 
## Coefficients:
##                   Estimate Std. Error t value Pr(>|t|)    
## (Intercept)      457.58820   79.11884   5.784 1.94e-08 ***
## Age              -19.37127    3.15180  -6.146 2.70e-09 ***
## Mins              -0.04357    0.03604  -1.209   0.2276    
## Goals             28.55271    5.83026   4.897 1.64e-06 ***
## Assists           42.53381    8.60857   4.941 1.34e-06 ***
## Passes_Attempted   0.23933    0.05288   4.526 8.88e-06 ***
## xG                62.68093  104.66047   0.599   0.5497    
## xA                38.84167  146.47613   0.265   0.7911    
## Yellow_Cards       3.80402    8.08896   0.470   0.6385    
## PlayerStatus      50.04902   28.23576   1.773   0.0774 .  
## TeamStatus       266.00743   36.12102   7.364 1.97e-12 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 218.3 on 282 degrees of freedom
## Multiple R-squared:  0.6611, Adjusted R-squared:  0.6491 
## F-statistic: 55.02 on 10 and 282 DF,  p-value: < 2.2e-16

Output di atas menunjukkan bahwa tingkat keyakinan 95% terlihat bahwa intersep dan peubah Age, Goals, Assists, Passes Attempted, dan Klub Asal berpengaruh signifikan terhadap peubah respon (Market Value) dikarenakan p-value < alpha (0.05). Nilai residual SE sebesar 218,3 dengan nilai Adjusted R2 dan R2 yang cukup besar sehingga menunjukkan model semakin baik. Nilai Adjusted R2 diperoleh sebesar 0,649 berarti bahwa peubah penjelas dapat menjelaskan 64.9% keragaman pada peubah respon, sisanya dijelaskan oleh faktor lainnya.

Deteksi Pencilan

outt <- ols_plot_cooksd_bar(ols2)

# Observasi ke-
cat("Observasi ke-\n", which(!is.na(outt$data$txt)), "\nmerupakan pencilan dari model, yaitu sebanyak", sum(!is.na(outt$data$txt)), "Outlier")
## Observasi ke-
##  16 18 19 22 24 26 27 34 41 43 44 47 48 60 61 76 89 90 102 117 118 127 128 149 189 202 220 234 237 261 293 
## merupakan pencilan dari model, yaitu sebanyak 31 Outlier

Kebaikan model

sse_ols <- sum(ols2$residuals^2)
mse_ols <- mean(ols2$residuals^2)
r2_ols <- sumols$r.squared
aic_ols <- AIC(ols2)
bic_ols <- BIC(ols2)
data.frame(sse_ols,mse_ols,r2_ols,aic_ols,bic_ols)

Diagnostik model

par(mfrow=c(2,2))
plot(ols)

# Pengujian Asumsi ####
# Normalitas
plot(ols2,2)

qqPlot(ols2$residuals, distribution = "norm", mean = mean(ols2$residuals), 
       sd = sd(ols2$residuals), main = "Uji Normalitas QQ-Plot Sisaan Model")

## [1] 90 76
ks.test(ols2$residuals, "pnorm", 
        mean = mean(ols2$residuals), sd = sd(ols2$residuals))
## 
##  Asymptotic one-sample Kolmogorov-Smirnov test
## 
## data:  ols2$residuals
## D = 0.056537, p-value = 0.3062
## alternative hypothesis: two-sided
# Heteroskedastisitas
plot(ols2, 1)

glejser(ols2)
# Kebebasan Galat
plot(ols2$residuals, type = 'o'); abline(h = 0, col = 'red')

randtests::runs.test(ols2$residuals, alternative ="two.sided")
## 
##  Runs Test
## 
## data:  ols2$residuals
## statistic = -2.4621, runs = 126, n1 = 146, n2 = 146, n = 292, p-value =
## 0.01381
## alternative hypothesis: nonrandomness
dwtest(ols)
## 
##  Durbin-Watson test
## 
## data:  ols
## DW = 1.7426, p-value = 0.01016
## alternative hypothesis: true autocorrelation is greater than 0
#exact=p-value tepat

Secara keseluruhan, dapat ditunjukan bahwa semua asumsi model regresi tidak terpenuhi. Oleh karena itu, perlu dilakukan penanganan asumsi (tidak dilakukan pada kasus ini). Cara lainnya adalah dengan menggunakan regresi pendekatan LAD yang tidak memerlukan asumsi.

Regresi Robust (LAD)

Penjelasan lengkap mengenai konsep LAD dapat dilihat di sini

library(lpSolve)

n = nrow(df) # Number of observation
k = ncol(df) # Number of Columns

# Define the objective function
objective <- c(rep(0,k), 1)

# Define the constraints
constraints1 <- cbind(rep(-1,n), as.matrix(-df[,2:k]), rep(-1,n))
constraints2 <- cbind(rep(-1,n), as.matrix(-df[,2:k]), rep(1,n))
constraints <- as.matrix(rbind(constraints1, constraints2))
directions <- c(rep("<=", n),
                rep(">=", n))
rhs <- rep(-df$MarketValue, 2)

# Solve the linear programming problem
solution <- lp("min", objective.in = objective,
               const.mat = constraints, const.dir = directions,
               const.rhs = rhs)

# Print the solution
solution$solution
##  [1]   0.0000000   0.0000000   0.0000000  26.9549840  33.7183501   0.2934907
##  [7]   0.0000000   0.0000000   0.0000000   0.0000000  64.4369443 718.7776987

Berikut adalah rangkumannya:

Coefficients <- solution$solution[-12]
data.frame(Variable=c("Intercept",colnames(df[-1])),Coefficients)

Output di atas menunjukkan bahwa intersep, peubah Age, Minutes Played, xG, xA, Yellow Card, dan Negara asal tidak berpengaruh signifikan terhadap model yang ditandai oleh nilai dugaan parameternya sama dengan nol. Sehingga, model terbaik yang didapat dari pendugaan parameter LAD adalah:

\[ \hat{y}_i=26.955 Goals_i+33.718 Assits_i+0.293 Passess Attempted_i+64.637 Klub Asal_i \]

Sehingga didapat nilai R-Squared model sebesar 0.5457 yang artinya model dapat menjelaskan sebesar 54.57% dari keragaman total peubah respons.

Kebaikan Model

X <- as.matrix(df %>% dplyr::select(-MarketValue))
X <- cbind(1,X)
yhat <- X %*% Coefficients
y <- df$MarketValue

# Calculate the residuals
residuals <- y - yhat

# Calculate the Sum of Squared Errors (SSE)
sse <- sum(residuals^2)

# Calculate the Mean Squared Error (MSE)
mse <- mean(residuals^2)

# Calculate the R-squared value
rsq <- 1 - sse / sum((y - mean(y))^2)

# Calculate the Akaike Information Criterion (AIC)
aic <- n * log(sse/n) + 2 * k

# Calculate the Bayesian Information Criterion (BIC)
bic <- n * log(sse/n) + k * log(n)

# Display the results
cat("Sum Squared Error:", sse, "\n")
## Sum Squared Error: 18398102
cat("R-squared:", rsq, "\n")
## R-squared: 0.5360914
cat("Akaike Information Criterion (AIC):", aic, "\n")
## Akaike Information Criterion (AIC): 3258.943
cat("Bayesian Information Criterion (BIC):", bic, "\n")
## Bayesian Information Criterion (BIC): 3299.424

Perbandingan

Pemodelan regresi linear berganda dengan metode OLS dan regresi linear robust dengan metode LAD dibandingkan dengan menggunakan beberapa metrik evaluasi yang sering digunakan. Metrik-metrik tersebut antara lain Mean Squared Error (MSE), Mean Absolute Percentage Error (MAPE), R-Squared, Akaike Information Creiterion (AIC), dan Bayesian Information Criterion (BIC). Semakin rendah nilai MSE, MAPE, AIC, dan BIC dari suatu model maka model tersebut dapat dikatakan lebih baik dibandingkan model yang lainnya. Berbanding terbalik dengan nilai R-squared, semakin tinggi nilai R-squared maka semakin baik model yang dihasilkan karena menunjukkan perbedaan yang semakin kecil antara data observasi dengan fitted values (Jim 2020).

mape <- function(actual, predicted) {
  # Calculate the absolute percentage error for each observation
  ape <- abs((actual - predicted) / actual)
  
  # Calculate the mean absolute percentage error
  mean(ape)
}

compare <- data.frame(MSE=c(mse_ols,mse),MAPE=c(mape(y,ols2$fitted.values),mape(y,yhat)),R_Squared=c(r2_ols,rsq),AIC=c(aic_ols,aic),BIC=c(bic_ols,bic))
compare <- t(compare)
compare <- round(compare,4)
colnames(compare) <- c("Ordinary Least Square", "Least Absolute Deviation")
as.data.frame(compare)

Model regresi linear robust dengan metode LAD dan pendekatan linear programming memiliki kinerja yang lebih baik dalam beberapa metrik tertentu daripada model regresi linear berganda dengan metode OLS. Pertama, disebutkan bahwa model regresi linear robust dengan metode LAD memiliki nilai MAPE, AIC, dan BIC yang lebih rendah daripada metode OLS. Nilai-nilai yang lebih rendah pada metrik-metrik ini menunjukkan bahwa model LAD lebih akurat dalam memprediksi data yang memiliki pencilan. Metode LAD mampu menangani pencilan dengan lebih baik karena tidak terlalu dipengaruhi oleh observasi yang jauh dari pola umum data.

Namun, apabila dibandingkan dengan metrik MSE dan R-Squared, metode LAD tidak lebih baik daripada metode OLS. Metode OLS cenderung memberikan nilai MSE yang lebih rendah dan R-Squared yang lebih tinggi dibandingkan dengan metode LAD. MSE mengukur kesalahan rata-rata dari model prediksi terhadap data aktual, sedangkan R-Squared mengindikasikan seberapa baik model cocok dengan data. Dalam hal ini, metode OLS menghasilkan model yang lebih akurat secara keseluruhan dalam hal pengurangan kesalahan kuadrat rata-rata dan kemampuan model dalam menjelaskan keragaman data.

Berdasarkan perbandingan kedua model, dapat dikatakan bahwa metode LAD lebih unggul daripada metode OLS dalam memodelkan Market Value pemain Liga Premier Inggris musim 2020-2021 yang memiliki pencilan, seperti yang ditunjukkan oleh nilai MAPE, AIC, dan BIC yang lebih rendah. Namun, dalam hal pengurangan kesalahan kuadrat rata-rata dan kemampuan model dalam menjelaskan keragaman data, metode OLS tetap menjadi pilihan yang lebih baik, sebagaimana terindikasi oleh nilai MSE yang lebih rendah dan R-Squared yang lebih tinggi.

Kesimpulan dan Saran

Model regresi linear robust dengan metode LAD dan pendekatan linear programming merupakan model yang lebih baik dalam memodelkan data yang memiliki nilai pencilan, yaitu data Market Value pemain Liga Premier Inggris musim 2020-2021 karena memiliki nilai MAPE, AIC, dan BIC yang lebih rendah daripada model regresi linear berganda dengan metode OLS. Walaupun demikian, dalam konteks pengurangan kesalahan kuadrat rata-rata dan kemampuan model dalam menjelaskan keragaman data, metode OLS lebih baik daripada metode LAD karena memiliki nilai MSE yang lebih rendah dan R-Squared yang lebih tinggi. Faktor-faktor yang berpengaruh signifikan terhadap Market Value pemain Liga Premier Inggris musim 2020-2021 antara lain Goals, Assists, Passes Attempted, dan Klub Asal. Beberapa faktor ini dapat digunakan oleh para stakeholders suatu klub sepakbola dalam memperkirakan valuasi harga pasar seorang pemain sepak bola terutama di Liga Premier Inggris. Selain itu, dengan faktor-faktor tersebut para stakeholders juga dapat menentukan strategi yang lebih efektif dalam pencarian pemain dengan valuasi dan kemampuan yang sesuai.

LS0tDQp0aXRsZTogIkFwbGlrYXNpIFJvYnVzdCBMaW5lYXIgUmVncmVzc2lvbiBkZW5nYW4gUGVuZGVrYXRhbiBMZWFzdCBBYnNvbHV0ZSBEZXZpYXRpb24gKExBRCkgTWVuZ2d1bmFrYW4gTGluZWFyIFByb2dyYW1taW5nIHBhZGEgS2FzdXMgTWFya2V0IFZhbHVlIFBlbWFpbiBTZXBhayBCb2xhIFByb2Zlc2lvbmFsIExpZ2EgSW5nZ3JpcyBNdXNpbSAyMDIwLTIwMjEiDQphdXRob3I6ICJBbGZpZGhpYSBSYWhtYW4gTkoiDQpkYXRlOiAiMjAyMy0wNS0xNCINCm91dHB1dDogDQogIHJtZGZvcm1hdHM6OmRvd25jdXRlOg0KICAgIGRvd25jdXRlX3RoZW1lOiAiY2hhb3MiDQogICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgdGhlbWU6IGNlcnVsZWFuDQogICAgaGlnaGxpZ2h0OiAia2F0ZSINCi0tLQ0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGlkeXIpDQpsaWJyYXJ5KHJlYWR4bCkNCmxpYnJhcnkoZGljaHJvbWF0KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHNrZWRhc3RpYykNCmxpYnJhcnkoY2FyKQ0KbGlicmFyeShsbXRlc3QpDQpsaWJyYXJ5KG9sc3JyKQ0KYGBgDQoNCiMgSW5wdXQgRGF0YQ0KDQpgYGB7cn0NCmRmIDwtIHJlYWR4bDo6cmVhZF9leGNlbCgiRVBMIDIwMjAtMjAyMS54bHN4IikNCg0Kc3RyKGRmKQ0KYGBgDQoNCiMgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcw0KDQpgYGB7cn0NCmdncGxvdChkZikgKw0KICBhZXMoeCA9IiIsIHkgPSBNYXJrZXRWYWx1ZSwgZmlsbCA9IGFzLmZhY3RvcihUZWFtU3RhdHVzKSkgKw0KICBnZW9tX2JveHBsb3QoKSArDQogIHNjYWxlX2ZpbGxfaHVlKGRpcmVjdGlvbiA9IDEpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KDQpgYGB7cn0NCmdncGxvdChkZikgKw0KICBhZXMoeCA9IiIsIHkgPSBNYXJrZXRWYWx1ZSwgZmlsbCA9IGFzLmZhY3RvcihQbGF5ZXJTdGF0dXMpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgc2NhbGVfZmlsbF9odWUoZGlyZWN0aW9uID0gMSkgKw0KICBjb29yZF9mbGlwKCkgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7ciBtZXNzYWdlPUZ9DQpHR2FsbHk6OmdncGFpcnMoZGZbMToxMF0pDQpgYGANCg0KVGVybGloYXQgYmFod2EgcGV1YmFoIHlhbmcgbWVtaWxpa2kga29yZWxhc2kgcGFsaW5nIHRpbmdnaSBkZW5nYW4gWSBhZGFsYWggQXNzaXN0cywgbGFsdSBkaWlrdXRpIG9sZWggR29hbHMuIFNlYmFyYW4gcGV1YmFoIHJlc3BvbiB0ZXJsaWhhdCBtZW5qdWx1ciBrZSBrYW5hbiB5YW5nIG1lbnVuanVra2FuIGJhaHdhIHNlYmFnaWFuIGJlc2FyIHBlbWFpbiBsaWdhIGluZ2dyaXMgbWVtaWxpa2kgTWFya2V0IFZhbHVlIHlhbmcgcmVuZGFoLiANCg0KIyMgSGlzdG9ncmFtDQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMywzKSkNCmZvcihpIGluIGMoMTo5KSl7DQogIGhpc3QoZGZbW2ldXSwgY29sPSJncmVlbiIsICBtYWluID0gcGFzdGUoIkhpc3RvZ3JhbSBvZiIsY29sbmFtZXMoZGYpW2ldKSwgeGxhYj1jb2xuYW1lcyhkZilbaV0pDQp9DQpgYGANCg0KDQojIyBCYXJwbG90DQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMSwzKSkNCmdyZWVuX3BhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJsaWdodGdyZWVuIiwgImRhcmtncmVlbiIpKQ0KYmFyX2NvbG9ycyA8LSBncmVlbl9wYWxldHRlKGxlbmd0aCh0YWJsZShkZiRZZWxsb3dfQ2FyZHMpKSkNCmJhcnBsb3QodGFibGUoZGYkWWVsbG93X0NhcmRzKSwgY29sPWJhcl9jb2xvcnMsIHhsYWI9IkNhdGVnb3J5IiwgeWxhYj0iRnJlcXVlbmN5IixtYWluID0gIkJhcnBsb3Qgb2YgWWVsbG93IENhcmRzIikNCmJhcnBsb3QodGFibGUoZGYkVGVhbVN0YXR1cyksIHhsYWI9IkNhdGVnb3J5IiwgeWxhYj0iRnJlcXVlbmN5IiwgbWFpbiA9ICJCYXJwbG90IG9mIFRlYW0gU3RhdHVzIiwgY29sPWMoImdyZWVuIiwiZGFya2dyZWVuIikpDQpiYXJwbG90KHRhYmxlKGRmJFBsYXllclN0YXR1cyksIGNvbD1jKCJncmVlbiIsImRhcmtncmVlbiIpLCB4bGFiPSJDYXRlZ29yeSIsIHlsYWI9IkZyZXF1ZW5jeSIsbWFpbiA9ICJCYXJwbG90IG9mIFBsYXllciBTdGF0dXMiKQ0KYGBgDQoNCg0KIyMgQm94cGxvdA0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDEsMykpDQpmb3IoaSBpbiBjKDE6OSkpew0KICBib3hwbG90KGRmW2ldLCBtYWluID0gcGFzdGUoIkJveHBsb3Qgb2YiLGNvbG5hbWVzKGRmKVtpXSksIGNvbCA9ICJncmVlbiIpDQp9DQpgYGANCg0KDQojIFJlZ3Jlc2kgTGluZWFyIEJlcmdhbmRhIChPTFMpDQoNCmBgYHtyfQ0Kb2xzIDwtIGxtKE1hcmtldFZhbHVlIH4gLiwgZGF0YSA9IGRmKQ0Kc3VtbWFyeShvbHMpDQpgYGANCg0KIyMgQ2VrIG11bHRpa29saW5lYXJpdGFzDQoNCmBgYHtyfQ0KdmlmKG9scykNCmBgYA0KU3RhcnRzIGRhbiBNaW5zIGJlcmtvcmVsYXNpIGt1YXQsIHNlaGluZ2dhIHBlcmx1IGVsaW1pbmFzaSBwZXViYWguDQoNCmBgYHtyfQ0KZGYgPC0gYXMuZGF0YS5mcmFtZShkZikNCmRmIDwtIGRmICU+JSBkcGx5cjo6c2VsZWN0KC1TdGFydHMpDQpzdHIoZGYpDQpgYGANCiMjIENlayB1bGFuZyBtdWx0aWtvbGluZWFyaXRhcw0KDQpgYGB7cn0NCm9sczIgPC0gbG0oTWFya2V0VmFsdWUgfiAuLCBkYXRhID0gZGYpDQpzdW1vbHMgPC0gc3VtbWFyeShvbHMyKQ0KdmlmKG9sczIpDQpgYGANClRlcmxpaGF0IGJhaHdhIG5pbGFpIFZJRiB5YW5nIGJlc2FyIHRlcmRhcGF0IHBhZGEgcGV1YmFoIFN0YXJ0aW5nIEVsZXZlbiBkYW4gTWludXRlcyBQbGF5ZWQgc2VoaW5nZ2EgcGVybHUgZGlsYWt1a2FuIHJlZHVrc2kgcGV1YmFoIHBhZGEgcGV1YmFoIFN0YXJ0aW5nIEVsZXZlbi4NCg0KYGBge3J9DQpzdW1vbHMNCmBgYA0KDQpPdXRwdXQgZGkgYXRhcyBtZW51bmp1a2thbiBiYWh3YSB0aW5na2F0IGtleWFraW5hbiA5NSUgdGVybGloYXQgYmFod2EgaW50ZXJzZXAgZGFuIHBldWJhaCBBZ2UsIEdvYWxzLCBBc3Npc3RzLCBQYXNzZXMgQXR0ZW1wdGVkLCBkYW4gS2x1YiBBc2FsIGJlcnBlbmdhcnVoIHNpZ25pZmlrYW4gdGVyaGFkYXAgcGV1YmFoIHJlc3BvbiAoTWFya2V0IFZhbHVlKSBkaWthcmVuYWthbiBwLXZhbHVlIDwgYWxwaGEgKDAuMDUpLiBOaWxhaSByZXNpZHVhbCBTRSBzZWJlc2FyIDIxOCwzIGRlbmdhbiBuaWxhaSBBZGp1c3RlZCBSMiBkYW4gUjIgeWFuZyBjdWt1cCBiZXNhciBzZWhpbmdnYSBtZW51bmp1a2thbiBtb2RlbCBzZW1ha2luIGJhaWsuIE5pbGFpIEFkanVzdGVkIFIyIGRpcGVyb2xlaCBzZWJlc2FyIDAsNjQ5IGJlcmFydGkgYmFod2EgcGV1YmFoIHBlbmplbGFzIGRhcGF0IG1lbmplbGFza2FuIDY0LjklIGtlcmFnYW1hbiBwYWRhIHBldWJhaCByZXNwb24sIHNpc2FueWEgZGlqZWxhc2thbiBvbGVoIGZha3RvciBsYWlubnlhLg0KDQojIERldGVrc2kgUGVuY2lsYW4NCg0KYGBge3J9DQpvdXR0IDwtIG9sc19wbG90X2Nvb2tzZF9iYXIob2xzMikNCg0KIyBPYnNlcnZhc2kga2UtDQpjYXQoIk9ic2VydmFzaSBrZS1cbiIsIHdoaWNoKCFpcy5uYShvdXR0JGRhdGEkdHh0KSksICJcbm1lcnVwYWthbiBwZW5jaWxhbiBkYXJpIG1vZGVsLCB5YWl0dSBzZWJhbnlhayIsIHN1bSghaXMubmEob3V0dCRkYXRhJHR4dCkpLCAiT3V0bGllciIpDQpgYGANCg0KIyMgS2ViYWlrYW4gbW9kZWwNCg0KYGBge3J9DQpzc2Vfb2xzIDwtIHN1bShvbHMyJHJlc2lkdWFsc14yKQ0KbXNlX29scyA8LSBtZWFuKG9sczIkcmVzaWR1YWxzXjIpDQpyMl9vbHMgPC0gc3Vtb2xzJHIuc3F1YXJlZA0KYWljX29scyA8LSBBSUMob2xzMikNCmJpY19vbHMgPC0gQklDKG9sczIpDQpkYXRhLmZyYW1lKHNzZV9vbHMsbXNlX29scyxyMl9vbHMsYWljX29scyxiaWNfb2xzKQ0KYGBgDQoNCg0KIyMgRGlhZ25vc3RpayBtb2RlbA0KDQpgYGB7cn0NCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KG9scykNCmBgYA0KDQpgYGB7cn0NCiMgUGVuZ3VqaWFuIEFzdW1zaSAjIyMjDQojIE5vcm1hbGl0YXMNCnBsb3Qob2xzMiwyKQ0KcXFQbG90KG9sczIkcmVzaWR1YWxzLCBkaXN0cmlidXRpb24gPSAibm9ybSIsIG1lYW4gPSBtZWFuKG9sczIkcmVzaWR1YWxzKSwgDQogICAgICAgc2QgPSBzZChvbHMyJHJlc2lkdWFscyksIG1haW4gPSAiVWppIE5vcm1hbGl0YXMgUVEtUGxvdCBTaXNhYW4gTW9kZWwiKQ0Ka3MudGVzdChvbHMyJHJlc2lkdWFscywgInBub3JtIiwgDQogICAgICAgIG1lYW4gPSBtZWFuKG9sczIkcmVzaWR1YWxzKSwgc2QgPSBzZChvbHMyJHJlc2lkdWFscykpDQoNCiMgSGV0ZXJvc2tlZGFzdGlzaXRhcw0KcGxvdChvbHMyLCAxKQ0KZ2xlanNlcihvbHMyKQ0KDQojIEtlYmViYXNhbiBHYWxhdA0KcGxvdChvbHMyJHJlc2lkdWFscywgdHlwZSA9ICdvJyk7IGFibGluZShoID0gMCwgY29sID0gJ3JlZCcpDQpyYW5kdGVzdHM6OnJ1bnMudGVzdChvbHMyJHJlc2lkdWFscywgYWx0ZXJuYXRpdmUgPSJ0d28uc2lkZWQiKQ0KZHd0ZXN0KG9scykNCiNleGFjdD1wLXZhbHVlIHRlcGF0DQpgYGANCg0KU2VjYXJhIGtlc2VsdXJ1aGFuLCBkYXBhdCBkaXR1bmp1a2FuIGJhaHdhIHNlbXVhIGFzdW1zaSBtb2RlbCByZWdyZXNpIHRpZGFrIHRlcnBlbnVoaS4gT2xlaCBrYXJlbmEgaXR1LCBwZXJsdSBkaWxha3VrYW4gcGVuYW5nYW5hbiBhc3Vtc2kgKHRpZGFrIGRpbGFrdWthbiBwYWRhIGthc3VzIGluaSkuIENhcmEgbGFpbm55YSBhZGFsYWggZGVuZ2FuIG1lbmdndW5ha2FuIHJlZ3Jlc2kgcGVuZGVrYXRhbiBMQUQgeWFuZyB0aWRhayBtZW1lcmx1a2FuIGFzdW1zaS4NCg0KIyBSZWdyZXNpIFJvYnVzdCAoTEFEKQ0KDQpQZW5qZWxhc2FuIGxlbmdrYXAgbWVuZ2VuYWkga29uc2VwIExBRCBkYXBhdCBkaWxpaGF0IGRpIFtzaW5pXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MZWFzdF9hYnNvbHV0ZV9kZXZpYXRpb25zIzp+OnRleHQ9TGVhc3QlMjBhYnNvbHV0ZSUyMGRldmlhdGlvbnMlMjAoTEFEKSUyQyxyZXNpZHVhbHMlMjBvciUyMHN1bSUyMG9mJTIwYWJzb2x1dGUpDQoNCmBgYHtyfQ0KbGlicmFyeShscFNvbHZlKQ0KDQpuID0gbnJvdyhkZikgIyBOdW1iZXIgb2Ygb2JzZXJ2YXRpb24NCmsgPSBuY29sKGRmKSAjIE51bWJlciBvZiBDb2x1bW5zDQoNCiMgRGVmaW5lIHRoZSBvYmplY3RpdmUgZnVuY3Rpb24NCm9iamVjdGl2ZSA8LSBjKHJlcCgwLGspLCAxKQ0KDQojIERlZmluZSB0aGUgY29uc3RyYWludHMNCmNvbnN0cmFpbnRzMSA8LSBjYmluZChyZXAoLTEsbiksIGFzLm1hdHJpeCgtZGZbLDI6a10pLCByZXAoLTEsbikpDQpjb25zdHJhaW50czIgPC0gY2JpbmQocmVwKC0xLG4pLCBhcy5tYXRyaXgoLWRmWywyOmtdKSwgcmVwKDEsbikpDQpjb25zdHJhaW50cyA8LSBhcy5tYXRyaXgocmJpbmQoY29uc3RyYWludHMxLCBjb25zdHJhaW50czIpKQ0KZGlyZWN0aW9ucyA8LSBjKHJlcCgiPD0iLCBuKSwNCiAgICAgICAgICAgICAgICByZXAoIj49IiwgbikpDQpyaHMgPC0gcmVwKC1kZiRNYXJrZXRWYWx1ZSwgMikNCg0KIyBTb2x2ZSB0aGUgbGluZWFyIHByb2dyYW1taW5nIHByb2JsZW0NCnNvbHV0aW9uIDwtIGxwKCJtaW4iLCBvYmplY3RpdmUuaW4gPSBvYmplY3RpdmUsDQogICAgICAgICAgICAgICBjb25zdC5tYXQgPSBjb25zdHJhaW50cywgY29uc3QuZGlyID0gZGlyZWN0aW9ucywNCiAgICAgICAgICAgICAgIGNvbnN0LnJocyA9IHJocykNCg0KIyBQcmludCB0aGUgc29sdXRpb24NCnNvbHV0aW9uJHNvbHV0aW9uDQpgYGANCkJlcmlrdXQgYWRhbGFoIHJhbmdrdW1hbm55YToNCg0KYGBge3J9DQpDb2VmZmljaWVudHMgPC0gc29sdXRpb24kc29sdXRpb25bLTEyXQ0KZGF0YS5mcmFtZShWYXJpYWJsZT1jKCJJbnRlcmNlcHQiLGNvbG5hbWVzKGRmWy0xXSkpLENvZWZmaWNpZW50cykNCmBgYA0KDQpPdXRwdXQgZGkgYXRhcyBtZW51bmp1a2thbiBiYWh3YSBpbnRlcnNlcCwgcGV1YmFoIEFnZSwgTWludXRlcyBQbGF5ZWQsIHhHLCB4QSwgWWVsbG93IENhcmQsIGRhbiBOZWdhcmEgYXNhbCB0aWRhayBiZXJwZW5nYXJ1aCBzaWduaWZpa2FuIHRlcmhhZGFwIG1vZGVsIHlhbmcgZGl0YW5kYWkgb2xlaCBuaWxhaSBkdWdhYW4gcGFyYW1ldGVybnlhIHNhbWEgZGVuZ2FuIG5vbC4gU2VoaW5nZ2EsIG1vZGVsIHRlcmJhaWsgeWFuZyBkaWRhcGF0IGRhcmkgcGVuZHVnYWFuIHBhcmFtZXRlciBMQUQgYWRhbGFoOg0KDQokJA0KXGhhdHt5fV9pPTI2Ljk1NSBHb2Fsc19pKzMzLjcxOCBBc3NpdHNfaSswLjI5MyBQYXNzZXNzIEF0dGVtcHRlZF9pKzY0LjYzNyBLbHViIEFzYWxfaQ0KJCQNCg0KU2VoaW5nZ2EgZGlkYXBhdCBuaWxhaSBSLVNxdWFyZWQgbW9kZWwgc2ViZXNhciAwLjU0NTcgeWFuZyBhcnRpbnlhIG1vZGVsIGRhcGF0IG1lbmplbGFza2FuIHNlYmVzYXIgNTQuNTclIGRhcmkga2VyYWdhbWFuIHRvdGFsIHBldWJhaCByZXNwb25zLg0KDQoNCiMjIEtlYmFpa2FuIE1vZGVsDQoNCmBgYHtyfQ0KWCA8LSBhcy5tYXRyaXgoZGYgJT4lIGRwbHlyOjpzZWxlY3QoLU1hcmtldFZhbHVlKSkNClggPC0gY2JpbmQoMSxYKQ0KeWhhdCA8LSBYICUqJSBDb2VmZmljaWVudHMNCmBgYA0KDQpgYGB7cn0NCnkgPC0gZGYkTWFya2V0VmFsdWUNCg0KIyBDYWxjdWxhdGUgdGhlIHJlc2lkdWFscw0KcmVzaWR1YWxzIDwtIHkgLSB5aGF0DQoNCiMgQ2FsY3VsYXRlIHRoZSBTdW0gb2YgU3F1YXJlZCBFcnJvcnMgKFNTRSkNCnNzZSA8LSBzdW0ocmVzaWR1YWxzXjIpDQoNCiMgQ2FsY3VsYXRlIHRoZSBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkNCm1zZSA8LSBtZWFuKHJlc2lkdWFsc14yKQ0KDQojIENhbGN1bGF0ZSB0aGUgUi1zcXVhcmVkIHZhbHVlDQpyc3EgPC0gMSAtIHNzZSAvIHN1bSgoeSAtIG1lYW4oeSkpXjIpDQoNCiMgQ2FsY3VsYXRlIHRoZSBBa2Fpa2UgSW5mb3JtYXRpb24gQ3JpdGVyaW9uIChBSUMpDQphaWMgPC0gbiAqIGxvZyhzc2UvbikgKyAyICogaw0KDQojIENhbGN1bGF0ZSB0aGUgQmF5ZXNpYW4gSW5mb3JtYXRpb24gQ3JpdGVyaW9uIChCSUMpDQpiaWMgPC0gbiAqIGxvZyhzc2UvbikgKyBrICogbG9nKG4pDQoNCiMgRGlzcGxheSB0aGUgcmVzdWx0cw0KY2F0KCJTdW0gU3F1YXJlZCBFcnJvcjoiLCBzc2UsICJcbiIpDQpjYXQoIlItc3F1YXJlZDoiLCByc3EsICJcbiIpDQpjYXQoIkFrYWlrZSBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEFJQyk6IiwgYWljLCAiXG4iKQ0KY2F0KCJCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24gKEJJQyk6IiwgYmljLCAiXG4iKQ0KYGBgDQojIFBlcmJhbmRpbmdhbg0KDQpQZW1vZGVsYW4gcmVncmVzaSBsaW5lYXIgYmVyZ2FuZGEgZGVuZ2FuIG1ldG9kZSBPTFMgZGFuIHJlZ3Jlc2kgbGluZWFyIHJvYnVzdCBkZW5nYW4gbWV0b2RlIExBRCBkaWJhbmRpbmdrYW4gZGVuZ2FuIG1lbmdndW5ha2FuIGJlYmVyYXBhIG1ldHJpayBldmFsdWFzaSB5YW5nIHNlcmluZyBkaWd1bmFrYW4uIE1ldHJpay1tZXRyaWsgdGVyc2VidXQgYW50YXJhIGxhaW4gTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpLCBNZWFuIEFic29sdXRlIFBlcmNlbnRhZ2UgRXJyb3IgKE1BUEUpLCBSLVNxdWFyZWQsIEFrYWlrZSBJbmZvcm1hdGlvbiBDcmVpdGVyaW9uIChBSUMpLCBkYW4gQmF5ZXNpYW4gSW5mb3JtYXRpb24gQ3JpdGVyaW9uIChCSUMpLiBTZW1ha2luIHJlbmRhaCBuaWxhaSBNU0UsIE1BUEUsIEFJQywgZGFuIEJJQyBkYXJpIHN1YXR1IG1vZGVsIG1ha2EgbW9kZWwgdGVyc2VidXQgZGFwYXQgZGlrYXRha2FuIGxlYmloIGJhaWsgZGliYW5kaW5na2FuIG1vZGVsIHlhbmcgbGFpbm55YS4gQmVyYmFuZGluZyB0ZXJiYWxpayBkZW5nYW4gbmlsYWkgUi1zcXVhcmVkLCBzZW1ha2luIHRpbmdnaSBuaWxhaSBSLXNxdWFyZWQgbWFrYSBzZW1ha2luIGJhaWsgbW9kZWwgeWFuZyBkaWhhc2lsa2FuIGthcmVuYSBtZW51bmp1a2thbiBwZXJiZWRhYW4geWFuZyBzZW1ha2luIGtlY2lsIGFudGFyYSBkYXRhIG9ic2VydmFzaSBkZW5nYW4gZml0dGVkIHZhbHVlcyAoSmltIDIwMjApLg0KDQpgYGB7cn0NCm1hcGUgPC0gZnVuY3Rpb24oYWN0dWFsLCBwcmVkaWN0ZWQpIHsNCiAgIyBDYWxjdWxhdGUgdGhlIGFic29sdXRlIHBlcmNlbnRhZ2UgZXJyb3IgZm9yIGVhY2ggb2JzZXJ2YXRpb24NCiAgYXBlIDwtIGFicygoYWN0dWFsIC0gcHJlZGljdGVkKSAvIGFjdHVhbCkNCiAgDQogICMgQ2FsY3VsYXRlIHRoZSBtZWFuIGFic29sdXRlIHBlcmNlbnRhZ2UgZXJyb3INCiAgbWVhbihhcGUpDQp9DQoNCmNvbXBhcmUgPC0gZGF0YS5mcmFtZShNU0U9Yyhtc2Vfb2xzLG1zZSksTUFQRT1jKG1hcGUoeSxvbHMyJGZpdHRlZC52YWx1ZXMpLG1hcGUoeSx5aGF0KSksUl9TcXVhcmVkPWMocjJfb2xzLHJzcSksQUlDPWMoYWljX29scyxhaWMpLEJJQz1jKGJpY19vbHMsYmljKSkNCmNvbXBhcmUgPC0gdChjb21wYXJlKQ0KY29tcGFyZSA8LSByb3VuZChjb21wYXJlLDQpDQpjb2xuYW1lcyhjb21wYXJlKSA8LSBjKCJPcmRpbmFyeSBMZWFzdCBTcXVhcmUiLCAiTGVhc3QgQWJzb2x1dGUgRGV2aWF0aW9uIikNCmFzLmRhdGEuZnJhbWUoY29tcGFyZSkNCmBgYA0KDQpNb2RlbCByZWdyZXNpIGxpbmVhciByb2J1c3QgZGVuZ2FuIG1ldG9kZSBMQUQgZGFuIHBlbmRla2F0YW4gbGluZWFyIHByb2dyYW1taW5nIG1lbWlsaWtpIGtpbmVyamEgeWFuZyBsZWJpaCBiYWlrIGRhbGFtIGJlYmVyYXBhIG1ldHJpayB0ZXJ0ZW50dSBkYXJpcGFkYSBtb2RlbCByZWdyZXNpIGxpbmVhciBiZXJnYW5kYSBkZW5nYW4gbWV0b2RlIE9MUy4gUGVydGFtYSwgZGlzZWJ1dGthbiBiYWh3YSBtb2RlbCByZWdyZXNpIGxpbmVhciByb2J1c3QgZGVuZ2FuIG1ldG9kZSBMQUQgbWVtaWxpa2kgbmlsYWkgTUFQRSwgQUlDLCBkYW4gQklDIHlhbmcgbGViaWggcmVuZGFoIGRhcmlwYWRhIG1ldG9kZSBPTFMuIE5pbGFpLW5pbGFpIHlhbmcgbGViaWggcmVuZGFoIHBhZGEgbWV0cmlrLW1ldHJpayBpbmkgbWVudW5qdWtrYW4gYmFod2EgbW9kZWwgTEFEIGxlYmloIGFrdXJhdCBkYWxhbSBtZW1wcmVkaWtzaSBkYXRhIHlhbmcgbWVtaWxpa2kgcGVuY2lsYW4uIE1ldG9kZSBMQUQgbWFtcHUgbWVuYW5nYW5pIHBlbmNpbGFuIGRlbmdhbiBsZWJpaCBiYWlrIGthcmVuYSB0aWRhayB0ZXJsYWx1IGRpcGVuZ2FydWhpIG9sZWggb2JzZXJ2YXNpIHlhbmcgamF1aCBkYXJpIHBvbGEgdW11bSBkYXRhLg0KDQpOYW11biwgYXBhYmlsYSBkaWJhbmRpbmdrYW4gZGVuZ2FuIG1ldHJpayBNU0UgZGFuIFItU3F1YXJlZCwgbWV0b2RlIExBRCB0aWRhayBsZWJpaCBiYWlrIGRhcmlwYWRhIG1ldG9kZSBPTFMuIE1ldG9kZSBPTFMgY2VuZGVydW5nIG1lbWJlcmlrYW4gbmlsYWkgTVNFIHlhbmcgbGViaWggcmVuZGFoIGRhbiBSLVNxdWFyZWQgeWFuZyBsZWJpaCB0aW5nZ2kgZGliYW5kaW5na2FuIGRlbmdhbiBtZXRvZGUgTEFELiBNU0UgbWVuZ3VrdXIga2VzYWxhaGFuIHJhdGEtcmF0YSBkYXJpIG1vZGVsIHByZWRpa3NpIHRlcmhhZGFwIGRhdGEgYWt0dWFsLCBzZWRhbmdrYW4gUi1TcXVhcmVkIG1lbmdpbmRpa2FzaWthbiBzZWJlcmFwYSBiYWlrIG1vZGVsIGNvY29rIGRlbmdhbiBkYXRhLiBEYWxhbSBoYWwgaW5pLCBtZXRvZGUgT0xTIG1lbmdoYXNpbGthbiBtb2RlbCB5YW5nIGxlYmloIGFrdXJhdCBzZWNhcmEga2VzZWx1cnVoYW4gZGFsYW0gaGFsIHBlbmd1cmFuZ2FuIGtlc2FsYWhhbiBrdWFkcmF0IHJhdGEtcmF0YSBkYW4ga2VtYW1wdWFuIG1vZGVsIGRhbGFtIG1lbmplbGFza2FuIGtlcmFnYW1hbiBkYXRhLg0KDQpCZXJkYXNhcmthbiBwZXJiYW5kaW5nYW4ga2VkdWEgbW9kZWwsIGRhcGF0IGRpa2F0YWthbiBiYWh3YSBtZXRvZGUgTEFEIGxlYmloIHVuZ2d1bCBkYXJpcGFkYSBtZXRvZGUgT0xTIGRhbGFtIG1lbW9kZWxrYW4gTWFya2V0IFZhbHVlIHBlbWFpbiBMaWdhIFByZW1pZXIgSW5nZ3JpcyBtdXNpbSAyMDIwLTIwMjEgeWFuZyBtZW1pbGlraSBwZW5jaWxhbiwgc2VwZXJ0aSB5YW5nIGRpdHVuanVra2FuIG9sZWggbmlsYWkgTUFQRSwgQUlDLCBkYW4gQklDIHlhbmcgbGViaWggcmVuZGFoLiBOYW11biwgZGFsYW0gaGFsIHBlbmd1cmFuZ2FuIGtlc2FsYWhhbiBrdWFkcmF0IHJhdGEtcmF0YSBkYW4ga2VtYW1wdWFuIG1vZGVsIGRhbGFtIG1lbmplbGFza2FuIGtlcmFnYW1hbiBkYXRhLCBtZXRvZGUgT0xTIHRldGFwIG1lbmphZGkgcGlsaWhhbiB5YW5nIGxlYmloIGJhaWssIHNlYmFnYWltYW5hIHRlcmluZGlrYXNpIG9sZWggbmlsYWkgTVNFIHlhbmcgbGViaWggcmVuZGFoIGRhbiBSLVNxdWFyZWQgeWFuZyBsZWJpaCB0aW5nZ2kuDQoNCg0KIyBLZXNpbXB1bGFuIGRhbiBTYXJhbg0KDQpNb2RlbCByZWdyZXNpIGxpbmVhciByb2J1c3QgZGVuZ2FuIG1ldG9kZSBMQUQgZGFuIHBlbmRla2F0YW4gbGluZWFyIHByb2dyYW1taW5nIG1lcnVwYWthbiBtb2RlbCB5YW5nIGxlYmloIGJhaWsgZGFsYW0gbWVtb2RlbGthbiBkYXRhIHlhbmcgbWVtaWxpa2kgbmlsYWkgcGVuY2lsYW4sIHlhaXR1IGRhdGEgTWFya2V0IFZhbHVlIHBlbWFpbiBMaWdhIFByZW1pZXIgSW5nZ3JpcyBtdXNpbSAyMDIwLTIwMjEga2FyZW5hIG1lbWlsaWtpIG5pbGFpIE1BUEUsIEFJQywgZGFuIEJJQyB5YW5nIGxlYmloIHJlbmRhaCBkYXJpcGFkYSBtb2RlbCByZWdyZXNpIGxpbmVhciBiZXJnYW5kYSBkZW5nYW4gbWV0b2RlIE9MUy4gV2FsYXVwdW4gZGVtaWtpYW4sIGRhbGFtIGtvbnRla3MgcGVuZ3VyYW5nYW4ga2VzYWxhaGFuIGt1YWRyYXQgcmF0YS1yYXRhIGRhbiBrZW1hbXB1YW4gbW9kZWwgZGFsYW0gbWVuamVsYXNrYW4ga2VyYWdhbWFuIGRhdGEsIG1ldG9kZSBPTFMgbGViaWggYmFpayBkYXJpcGFkYSBtZXRvZGUgTEFEIGthcmVuYSBtZW1pbGlraSBuaWxhaSBNU0UgeWFuZyBsZWJpaCByZW5kYWggZGFuIFItU3F1YXJlZCB5YW5nIGxlYmloIHRpbmdnaS4gRmFrdG9yLWZha3RvciB5YW5nIGJlcnBlbmdhcnVoIHNpZ25pZmlrYW4gdGVyaGFkYXAgTWFya2V0IFZhbHVlIHBlbWFpbiBMaWdhIFByZW1pZXIgSW5nZ3JpcyBtdXNpbSAyMDIwLTIwMjEgYW50YXJhIGxhaW4gR29hbHMsIEFzc2lzdHMsIFBhc3NlcyBBdHRlbXB0ZWQsIGRhbiBLbHViIEFzYWwuIEJlYmVyYXBhIGZha3RvciBpbmkgZGFwYXQgZGlndW5ha2FuIG9sZWggcGFyYSBzdGFrZWhvbGRlcnMgc3VhdHUga2x1YiBzZXBha2JvbGEgZGFsYW0gbWVtcGVya2lyYWthbiB2YWx1YXNpIGhhcmdhIHBhc2FyIHNlb3JhbmcgcGVtYWluIHNlcGFrIGJvbGEgdGVydXRhbWEgZGkgTGlnYSBQcmVtaWVyIEluZ2dyaXMuIFNlbGFpbiBpdHUsIGRlbmdhbiBmYWt0b3ItZmFrdG9yIHRlcnNlYnV0IHBhcmEgc3Rha2Vob2xkZXJzIGp1Z2EgZGFwYXQgbWVuZW50dWthbiBzdHJhdGVnaSB5YW5nIGxlYmloIGVmZWt0aWYgZGFsYW0gcGVuY2FyaWFuIHBlbWFpbiBkZW5nYW4gdmFsdWFzaSBkYW4ga2VtYW1wdWFuIHlhbmcgc2VzdWFpLiA=