1 Phân tích tương quan

1.1 Ví dụ:

1.2 Hệ số tương quan tuyến tính (Correlation coefficient)

Hệ số tương quan đo lường mức độ quan hệ tuyến tính giữa hai biến, không phân biệt biến này phụ thuộc vào biến kia

1.3 Ðặc tính của hệ số tương quan

  • Hệ số tương quan không có đơn vị
  • Hệ số tương quan (r) nằm trong khoảng [-1,1]

  • r > 0: tương quan dương
  • r < 0: tương quan âm
  • r = 0: không tương quan

1.4 Các phương pháp tính tương quan

### Pearson’s r - Đánh giá mức độ tương quan tuyến tính của 2 biến định lượng

\[ r_{xy} = \frac{\sum_{i=1}^{n}(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}} \]

  • Ví dụ: Sử dụng số liệu dân số và thu nhập của 50 bang của Mỹ
states <- state.x77[,c(1:2)] 
head(states)
##            Population Income
## Alabama          3615   3624
## Alaska            365   6315
## Arizona          2212   4530
## Arkansas         2110   3378
## California      21198   5114
## Colorado         2541   4884
  • Công thức tính hệ số tương quan Pearson trên R: cor(df, method = “pearson”)
M <- cor(states)
M
##            Population    Income
## Population  1.0000000 0.2082276
## Income      0.2082276 1.0000000

1.4.1 Tương quan hạng (spearman)

  • Đánh giá mức độ tương quan của 2 hạng của 2 biến (rank-ordered variables), sử dụng khi phân phối của tổng thể được giả sử không phải là phân phối chuẩn hoặc trong trường hợp có các giá trị quan sát bất thường (lớn quá hoặc nhỏ quá)

\[ 1-\frac{6\sum_i^n d_{i}^2}{n(n^2-1)}\]

di: hiệu hạng của 2 biến

\[ d_{i} = rgX_{i} - rgY_{i}\]

  • Công thức tính trên R: cor(df, method = “spearman”)

  • Minh họa bằng bảng tính:

states <- state.x77[,c(1:2)] 
head(states)
##            Population Income
## Alabama          3615   3624
## Alaska            365   6315
## Arizona          2212   4530
## Arkansas         2110   3378
## California      21198   5114
## Colorado         2541   4884
n <- dim(states)[1]

states %>% 
  as.data.frame() %>% 
  mutate(rgX = rank(Population, ties.method= "first"),
         rgY = rank(Income, ties.method= "first"),
         d = rgX - rgY,
         d2 = d^2) %>% 
  mutate(Spearman_cor = 1 - 6 * sum(d2)/(n * (n^2 -1))) %>% 
  datatable()
M <- cor(states[,c("Population", "Income")], method = "spearman")
M
##            Population    Income
## Population  1.0000000 0.1246098
## Income      0.1246098 1.0000000

1.4.2 Tương quan hạng (kendall)

  • Đánh giá mức độ tương quan của 2 hạng của 2 biến (rank-ordered variables), hệ số này được sử dụng tương tự như spearman, thông thường hệ số này nhỏ hơn spearman

  • Hệ số kendall ít dùng hơn so với 2 hệ số tương quan trên

  • Công thức tính trên R: cor(df, method = “kendall”)

M <- cor(states, method = "kendall")
M
##            Population     Income
## Population 1.00000000 0.08408163
## Income     0.08408163 1.00000000

1.5 Kiểm định xem 2 biến có tương quan với nhau không

  • Cũng như phương pháp tính, kiểm định cũng có 3 method: Pearson, Spearman, Kendall

  • Giả thiết kiểm định:

    • H0: Không có tương quan (hệ số tương quan = 0)
    • Ha: Có tương quan
  • Ví dụ: Sử dụng hàm cor.test của gói stats

states <- state.x77
states[,c(3,5)] %>% cor()
##            Illiteracy    Murder
## Illiteracy  1.0000000 0.7029752
## Murder      0.7029752 1.0000000
cor.test(states[,3], states[,5])
## 
##  Pearson's product-moment correlation
## 
## data:  states[, 3] and states[, 5]
## t = 6.8479, df = 48, p-value = 1.258e-08
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  0.5279280 0.8207295
## sample estimates:
##       cor 
## 0.7029752

Hàm này chỉ dùng được với 2 biến. Trong trường hợp muốn thực hiện với nhiều biến có thể sử dụng hàm corr.test của package psych

  • Ví dụ sử dụng hàm corr.test
corr.test(states[,1:5], use = "complete", method = "pearson" )
## Call:corr.test(x = states[, 1:5], use = "complete", method = "pearson")
## Correlation matrix 
##            Population Income Illiteracy Life Exp Murder
## Population       1.00   0.21       0.11    -0.07   0.34
## Income           0.21   1.00      -0.44     0.34  -0.23
## Illiteracy       0.11  -0.44       1.00    -0.59   0.70
## Life Exp        -0.07   0.34      -0.59     1.00  -0.78
## Murder           0.34  -0.23       0.70    -0.78   1.00
## Sample Size 
## [1] 50
## Probability values (Entries above the diagonal are adjusted for multiple tests.) 
##            Population Income Illiteracy Life Exp Murder
## Population       0.00   0.44       0.91     0.91   0.09
## Income           0.15   0.00       0.01     0.09   0.43
## Illiteracy       0.46   0.00       0.00     0.00   0.00
## Life Exp         0.64   0.02       0.00     0.00   0.00
## Murder           0.01   0.11       0.00     0.00   0.00
## 
##  To see confidence intervals of the correlations, print with the short=FALSE option

Hàm này cho ra kết quả của cả hệ số tương quan và xác suất kiểm định

Xác suất > mức ý nghĩa alpha (= 0.05) có thể kết luận hệ số tương quan = 0 với mức ý nghĩa alpha

1.6 Một số đồ thị trong phân tích tương quan

  • corrplot (package: corrplot)
states <- state.x77
M <- cor(states)
corrplot(M, method = "circle")

  • corrplot.mixed (package: corrplot)
corrplot.mixed(M)

  • chart.Correlation (package: PerformanceAnalytics)
data(managers)
chart.Correlation(managers[,1:8], histogram=TRUE, pch="+")

Hàm này tương như: psych::pairs.panels, GGally::ggpairs, corrr::correlate

2 Phân tích hồi quy (regression analysis)

Sử dụng để dự báo 1 biến phụ thuộc (dependent variable, response variable) dựa vào 1 hay nhiều biến độc lập (independent variables, predictor variables, explanatory variables)

Ví dụ 1: Phân tích khi tăng 1 nhân viên thì lợi nhuận ngân hàng tăng hay giảm bao nhiêu tiền. Biến phụ thuộc là lợi nhuận ngân hàng, biến độc lập là số lượng nhân viên (simple linear)

Ví dụ 2: Tính toán xem khi tăng 1 cây ATM thì lợi nhuận ngân hàng tăng hay giảm bao nhiêu phần trăm. Biến phụ thuôc là log(lợi nhuận), biến độc lập có thể là số lượng máy ATM, số lượng máy ATM bình phương (Polynomial)

Ví dụ 3: Dự báo khả năng phát sinh nợ xấu của khách hàng. Biến phụ thuộc là khả năng phát sinh nợ xấu, biến độc lập ví dụ: tuổi, giới tính, trình độ học vấn … (Multiple linear)

Ví dụ 4: Dự báo giá cổ phiếu của ngân hàng tại các thời điểm trong tương lai. Biến phụ thuộc là giá cổ phiếu, biến độc lập có thể có là trễ của biến giá, hoặc 1 số yếu tố như GPD, lạm phát … (Time-series)

Có rất nhiều loại hồi quy như: Simple linear, Polynomial, Multiple linear, Multilevel, Multivariate, Logistic, Poisson, Cox proportional hazards, Time-series, Nonlinear, Nonparametric

Trong đó, 4 loại phổ biến hay được sử dụng là

type_model <- data.frame(Type = c("Simple linear", 
                    "Multiple linear",
                    "Logistic",
                    "Time-series"),
           `Typical use` = c("Predicting a quantitative response variable from a quantitative explanatory variable",
"Predicting a quantitative response variable from two or more explanatory variables",
"Predicting a categorical response variable from one or more explanatory variables",
"Modeling time-series data with correlated errors"))

type_model %>% datatable()

2.1 Những hàm thường dùng đối với hồi quy tuyến tính

data.frame(Function = c("summary()", "coefficients()", "confint()", "fitted()", "residuals()", "anova()", 
"vcov()", "AIC()", "plot()", "predict()"), 
Action =
c("Displays detailed results for the fitted model", "Lists the model parameters (intercept and slopes) for the fitted model", "Provides confidence intervals for the model parameters (95% by default)",
  "Lists the predicted values in a fitted model", "Lists the residual values in a fitted model", "Generates an ANOVA table for a fitted model, or an ANOVA table comparing two or more fitted models", "Lists the covariance matrix for model parameters", "Prints Akaike’s Information Criterion", "Generates diagnostic plots for evaluating the fit of a model", "Uses a fitted model to predict response values for a new dataset"))  %>%  datatable()

2.2 Hồi quy tuyến tính đơn biến (Simple linear)

Là mô hình hồi quy với 1 biến phụ thuộc, 1 biến độc lập. Hàm hồi quy có dạng Y = a + bX

Y là biến phụ thuộc, X là biến độc lập, a là hệ số chặn (intercept), b là hệ số góc (coefficient)

Ví dụ: Với bộ dữ liệu women, có 2 biến là chiều cao, cân nặng của phụ nữ. Muốn dự báo cân nặng của phụ nữ dựa vào chiều cao của họ ta xây dựng mô hình hồi quy đơn

data(women)

head(women)
##   height weight
## 1     58    115
## 2     59    117
## 3     60    120
## 4     61    123
## 5     62    126
## 6     63    129
women %>% 
  ggplot(aes(x = height, y = weight))+
  geom_point()+
  labs(x = "height (in inches)",
       y = "weight (in pounds)")

fit <- lm(weight ~ height, data=women)

Sử dụng phương pháp bình phương nhỏ nhất OLS (Ordinary least squares)

summary(fit)
## 
## Call:
## lm(formula = weight ~ height, data = women)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1.7333 -1.1333 -0.3833  0.7417  3.1167 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -87.51667    5.93694  -14.74 1.71e-09 ***
## height        3.45000    0.09114   37.85 1.09e-14 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1.525 on 13 degrees of freedom
## Multiple R-squared:  0.991,  Adjusted R-squared:  0.9903 
## F-statistic:  1433 on 1 and 13 DF,  p-value: 1.091e-14

Như vậy phương trình hồi quy có dạng

\[ \hat{weight} = -87.51 + 3.45 * height \]

Dự báo (fitted)

fitted(fit)
##        1        2        3        4        5        6        7        8 
## 112.5833 116.0333 119.4833 122.9333 126.3833 129.8333 133.2833 136.7333 
##        9       10       11       12       13       14       15 
## 140.1833 143.6333 147.0833 150.5333 153.9833 157.4333 160.8833
women$weight_pre <- fitted(fit)

Phần dư (sai lệch)

residuals(fit)
##           1           2           3           4           5           6 
##  2.41666667  0.96666667  0.51666667  0.06666667 -0.38333333 -0.83333333 
##           7           8           9          10          11          12 
## -1.28333333 -1.73333333 -1.18333333 -1.63333333 -1.08333333 -0.53333333 
##          13          14          15 
##  0.01666667  1.56666667  3.11666667
women$res <- residuals(fit)
women %>% 
  ggplot()+
  geom_point(aes(x = height, y = weight), color = "blue")+
  geom_point(aes(x = height, y = weight_pre), color = "red")+
  labs(x = "height (in inches)",
       y = "weight (in pounds)")

2.3 Hồi quy tuyến tính đa biến

Mô hình hồi quy dạng: \[\hat{Y} = \beta_{0} + \beta_{1}X_{1} + \beta_{2}X_{2} + \beta_{3}X_{3} + ...\]

Ví dụ lấy bộ số liệu giết người ở các bang của Mỹ state.x77

states <- as.data.frame(state.x77[,c("Murder", "Population", "Illiteracy", "Income", "Frost")])

states %>% head()
##            Murder Population Illiteracy Income Frost
## Alabama      15.1       3615        2.1   3624    20
## Alaska       11.3        365        1.5   6315   152
## Arizona       7.8       2212        1.8   4530    15
## Arkansas     10.1       2110        1.9   3378    65
## California   10.3      21198        1.1   5114    20
## Colorado      6.8       2541        0.7   4884   166
  • Phân tích tương quan
chart.Correlation(states, histogram=TRUE, pch="+")

fit <- lm(Murder ~ Population + Illiteracy + Income + Frost, data = states)
summary(fit)
## 
## Call:
## lm(formula = Murder ~ Population + Illiteracy + Income + Frost, 
##     data = states)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -4.7960 -1.6495 -0.0811  1.4815  7.6210 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 1.235e+00  3.866e+00   0.319   0.7510    
## Population  2.237e-04  9.052e-05   2.471   0.0173 *  
## Illiteracy  4.143e+00  8.744e-01   4.738 2.19e-05 ***
## Income      6.442e-05  6.837e-04   0.094   0.9253    
## Frost       5.813e-04  1.005e-02   0.058   0.9541    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.535 on 45 degrees of freedom
## Multiple R-squared:  0.567,  Adjusted R-squared:  0.5285 
## F-statistic: 14.73 on 4 and 45 DF,  p-value: 9.133e-08
states$pre <- fitted(fit)
states$res <- residuals(fit)

Giá trị p-value Income và Frost > 0.05. Vì vậy, với mức ý nghĩa 0.05 có thể kết luận hệ số của 2 biến này bằng 0

  • Khoảng tin cậy của hệ số hồi quy
confint(fit)
##                     2.5 %       97.5 %
## (Intercept) -6.552191e+00 9.0213182149
## Population   4.136397e-05 0.0004059867
## Illiteracy   2.381799e+00 5.9038743192
## Income      -1.312611e-03 0.0014414600
## Frost       -1.966781e-02 0.0208304170

Thay vì việc nhìn vào pvalue ở mô hình hồi quy, khoảng tin cậy của hệ số hồi quy cho phép đánh giá xem hệ số hồi quy có khác 0 hay không.

Khoảng tin cậy của Hệ số của Intercept, Income, Frost có cận dưới < 0, trong khi cận trên > 0 –> nhìn vào đây có thể biết được 3 hệ số này = 0 với mức ý nghĩa 0.05%

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

par(mfrow=c(1,1))

2.4 Kiểm định và lựa chọn mô hình

2.4.1 Dựa vào đồ thị phần dư

  • Biểu đồ 1: Vẽ tương quan giữa phần dư và kết quả dự báo, giá trị phần dư càng ở quanh mức 0 kết quả dự báo càng tốt. Như vậy, tỷ lệ giết người đang được dự báo quá cao so với thực tế tại 2 bang Rhode Island và Masschusetts, dự báo quá thấp tại bang Nevada

  • Biểu đồ 2: Kiểm tra xem phần dư có phân phối chuẩn N(0,1) hay không, kết quả cho thấy phần dư tại 3 bang Nevada và Rhode Island và Alaska đang ko theo quy luật phân phối chuẩn

  • Biểu đồ 3: Đánh giá phương sai của phần dư có đồng nhất hay không

  • Biều đồ 4: Cho phép phát hiện ra các outliers trong phần dư

2.4.2 Dựa vào các kiểm định

2.4.3 Giả thiết 1: Sai số ngẫu nhiên có phân phối chuẩn

2.4.4 Giả thiết 2: Kỳ vọng của sai số ngẫu nhiên tại mỗi giá trị bằng 0

res <- residuals(fit)

t.test(res, mu = 0)
## 
##  One Sample t-test
## 
## data:  res
## t = 6.5292e-16, df = 49, p-value = 1
## alternative hypothesis: true mean is not equal to 0
## 95 percent confidence interval:
##  -0.6903919  0.6903919
## sample estimates:
##    mean of x 
## 2.243128e-16

p-value = 1: mô hình thỏa mãn giả thiết 2

2.4.5 Giả thiết 3: Phương sai của sai số ngẫu nhiên không đổi

H0: phương sai sai số không đổi Ha: Phương sai sai số thay đổi

ncvTest(fit)
## Non-constant Variance Score Test 
## Variance formula: ~ fitted.values 
## Chisquare = 1.746514, Df = 1, p = 0.18632

p-value = 0.18, có thể kết luận phương sai sai số không đổi với mức ý nghĩa < 0.18

2.4.6 Giả thiết 4: Giữa các biến độc lập không có mối quan hệ đa cộng tuyến hoàn hảo

vif(fit)
## Population Illiteracy     Income      Frost 
##   1.245282   2.165848   1.345822   2.082547

vif < 10 cho thấy các biến độc lập không có đa cộng tuyến

2.5 Lựa chọn mô hình

Sau khi kiểm định các giả thiết của mô hình OLS, thử nghiệm trường hợp bỏ 2 biến Income, Frost ra khỏi mô hình

fit <- lm(Murder ~ Population + Illiteracy, data = states)
summary(fit)
## 
## Call:
## lm(formula = Murder ~ Population + Illiteracy, data = states)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -4.7652 -1.6561 -0.0898  1.4570  7.6758 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 1.652e+00  8.101e-01   2.039  0.04713 *  
## Population  2.242e-04  7.984e-05   2.808  0.00724 ** 
## Illiteracy  4.081e+00  5.848e-01   6.978 8.83e-09 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 2.481 on 47 degrees of freedom
## Multiple R-squared:  0.5668, Adjusted R-squared:  0.5484 
## F-statistic: 30.75 on 2 and 47 DF,  p-value: 2.893e-09
par(mfrow=c(2,2))
plot(fit)

par(mfrow=c(1,1))
LS0tDQp0aXRsZTogIlBow6JuIHTDrWNoIGjhu5NpIHF1eSB2w6AgdMawxqFuZyBxdWFuIg0KYXV0aG9yOiAiTmd1eeG7hW4gTmfhu41jIELDrG5oIg0KZGF0ZTogIk9jdG9iZXIgMTYsIDIwMTgiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IG5vDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICBmaWdfd2lkdGg6IDkgDQogIGZpZ19oZWlnaHQ6IDYgDQogIGZpZy5jYXAgOiAiU291cmNlOiBCSSAtIFNlQWJhbmsgIg0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KbGlicmFyeShEVCkNCmxpYnJhcnkocHN5Y2gpDQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KbGlicmFyeShjYXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KGdncmVwZWwpDQoNCg0KdGhlbWVfc2V0KHRoZW1lX2xpZ2h0KCkpDQpgYGANCg0KDQojIFBow6JuIHTDrWNoIHTGsMahbmcgcXVhbg0KDQojIyBWw60gZOG7pToNCg0KYGBge3IsIGVjaG89RkFMU0V9DQpyIDwtIGRhdGEuZnJhbWUoeTAgPSBybm9ybSgxMDAsIG1lYW4gPSAyLCBzZCA9IDEpKSAlPiUgDQogIG11dGF0ZShgTGluZWFyIGNvcnJlbGF0aW9uYCA9IC0geTAgKiAxMCArIHJub3JtKDEwMCksDQogICAgICAgICBgTm9ubGluZWFyIGNvcnJlbGF0aW9uYCA9IHkwXjIgKyBybm9ybSgxMDApLA0KICAgICAgICAgYE5vIGNvcnJlbGF0aW9uYCA9IHJub3JtKDEwMCkpIA0KDQpyICU+JSBnYXRoZXIoa2V5ID0gImtleSIsIHZhbHVlID0geCwgLXkwKSAlPiUgDQogIG11dGF0ZShrZXkgPSBmYWN0b3Ioa2V5LCANCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJMaW5lYXIgY29ycmVsYXRpb24iLCAiTm9ubGluZWFyIGNvcnJlbGF0aW9uIiwgIk5vIGNvcnJlbGF0aW9uIiksDQogICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTGluZWFyIGNvcnJlbGF0aW9uIiwgIk5vbmxpbmVhciBjb3JyZWxhdGlvbiIsICJObyBjb3JyZWxhdGlvbiIpKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geTAsIGZpbGwgPSBrZXksIGNvbG9yID0ga2V5KSkrDQogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSkrDQogIGZhY2V0X3dyYXAofmtleSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gZ3JhZGllbnQiLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSBOVUxMLA0KICAgICAgIGNhcHRpb24gPSAiQXV0aG9yOiBOTkIiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygic2xhdGVibHVlNCIsICJzbGF0ZWJsdWUyIiwgInNsYXRlYmx1ZSIpKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInNsYXRlYmx1ZTQiLCAic2xhdGVibHVlMiIsICJzbGF0ZWJsdWUiKSkrDQogIHRoZW1lX2xpZ2h0KCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiksDQogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAic2xhdGVibHVlNCIpLA0KICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAic2xhdGVibHVlNCIpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsDQogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0ic2xhdGVibHVlIiksDQogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gJ3NsYXRlYmx1ZTQnLCBzaXplID0gMTIpKSANCg0KYGBgDQoNCiMjIEjhu4cgc+G7kSB0xrDGoW5nIHF1YW4gdHV54bq/biB0w61uaCAoQ29ycmVsYXRpb24gY29lZmZpY2llbnQpDQoNCkjhu4cgc+G7kSB0xrDGoW5nIHF1YW4gxJFvIGzGsOG7nW5nIG3hu6ljIMSR4buZIHF1YW4gaOG7hyB0dXnhur9uIHTDrW5oIGdp4buvYSBoYWkgYmnhur9uLCBraMO0bmcgcGjDom4gYmnhu4d0IGJp4bq/biBuw6B5IHBo4bulIHRodeG7mWMgdsOgbyBiaeG6v24ga2lhDQoNCiMjIMOQ4bq3YyB0w61uaCBj4bunYSBo4buHIHPhu5EgdMawxqFuZyBxdWFuDQoNCiAgLSBI4buHIHPhu5EgdMawxqFuZyBxdWFuIGtow7RuZyBjw7MgxJHGoW4gduG7iw0KICAtIEjhu4cgc+G7kSB0xrDGoW5nIHF1YW4gKHIpIG7hurFtIHRyb25nIGtob+G6o25nIFstMSwxXQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCmNvcl9zdHJlIDwtIGRhdGEuZnJhbWUoY29lZiA9IHNlcSgtMSwgMSwgYnkgPSAwLjEpKSANCg0KY29yX3N0cmUgPC0gY29yX3N0cmUgJT4lIA0KICBtdXRhdGUoc3RyZSA9IGNhc2Vfd2hlbihhYnMoY29lZikgPT0xIH4gIlBlcmZlY3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhYnMoY29lZikgPj0gMC42NSB+ICJTdHJvbmciLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBhYnMoY29lZikgPj0gMC40NSB+ICJNb2RlcmF0ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGFicyhjb2VmKSA+PSAwLjE1IH4gIldlYWsiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk5vbmUiKSwNCiAgICAgICAgIHN0cmUgPSBmYWN0b3Ioc3RyZSwgDQogICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIk5vbmUiLCAiV2VhayIsICJNb2RlcmF0ZSIsICJTdHJvbmciLCAiUGVyZmVjdCIpLA0KICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJOb25lIiwgIldlYWsiLCAiTW9kZXJhdGUiLCAiU3Ryb25nIiwgIlBlcmZlY3QiKSksDQogICAgICAgICBhYnNfY29lZiA9IGFicyhjb2VmKSkgJT4lIA0KICBncm91cF9ieShzdHJlKSAlPiUgDQogIG11dGF0ZSh5bWF4ID0gY2FzZV93aGVuKHN0cmUgJWluJSAiUGVyZmVjdCIgfiBJbmYsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmUgJWluJSAiU3Ryb25nIiB+IDAuOTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmUgJWluJSAiTW9kZXJhdGUiIH4gMC42NSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyZSAlaW4lICJXZWFrIiB+IDAuNDUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAwLjE1KSwNCiAgICAgICAgIHltaW4gPSBjYXNlX3doZW4oc3RyZSAlaW4lICJQZXJmZWN0IiB+IDAuOTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmUgJWluJSAiU3Ryb25nIiB+IDAuNjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmUgJWluJSAiTW9kZXJhdGUiIH4gMC40NSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyZSAlaW4lICJXZWFrIiB+IDAuMTUsDQogICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAtSW5mKSkNCg0KY29yX3N0cmUgJT4lIGdncGxvdChhZXMoeCA9IGNvZWYsIHkgPSBhYnNfY29lZikpKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3JlY3QoYWVzKHhtYXggPSBJbmYsIHhtaW4gPSAtSW5mLCB5bWF4ID0geW1heCwgeW1pbiA9IHltaW4sIGZpbGwgPSBzdHJlKSwgYWxwaGEgPSAwLjMpKw0KICBnZW9tX2xhYmVsKGFlcyhsYWJlbCA9IGNvZWYsIGNvbG9yID0gc3RyZSkpKw0KICBsYWJzKHRpdGxlID0gIlN0cmVuZ3RoIG9mIGNvcnJlbGF0aW9uIiwNCiAgICAgICB4ID0gTlVMTCwNCiAgICAgICB5ID0gTlVMTCwNCiAgICAgICBjYXB0aW9uID0gIkF1dGhvcjogTk5CIikgKw0KICB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgdGV4dCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJkb2RnZXJibHVlNCIpKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEuNSwgMSkpKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJsdWVzIikgKw0KICBhbm5vdGF0ZSgidGV4dCIsIHggPSAtMS4yLCB5ID0gYygwLjA1LCAwLjMsIDAuNTUsIDAuOCwgMSksIGxhYmVsID0gYygiTm9uZSIsICJXZWFrIiwgIk1vZGVyYXRlIiwgIlN0cm9uZyIsICJQZXJmZWN0IikpDQpgYGANCg0KICAtIHIgPiAwOiB0xrDGoW5nIHF1YW4gZMawxqFuZw0KICAtIHIgPCAwOiB0xrDGoW5nIHF1YW4gw6JtDQogIC0gciA9IDA6IGtow7RuZyB0xrDGoW5nIHF1YW4NCg0KYGBge3IsIGVjaG89RkFMU0V9DQpyIDwtIGRhdGEuZnJhbWUoeTAgPSBybm9ybSgxMDAsIG1lYW4gPSA1LCBzZCA9IDEpKSAlPiUgDQogIG11dGF0ZShgUG9zaXRpdmUgY29ycmVsYXRpb25gID0geTAgKiAxMCArIHJub3JtKDEwMCksDQogICAgICAgICBgTmVnYXRpdmUgY29ycmVsYXRpb25gID0geTAgKiAoLTUpICsgcm5vcm0oMTAwLCAyKSwNCiAgICAgICAgIGBObyBjb3JyZWxhdGlvbmAgPSBybm9ybSgxMDApKSANCg0KciAlPiUgZ2F0aGVyKGtleSA9ICJrZXkiLCB2YWx1ZSA9IHgsIC15MCkgJT4lIA0KICBtdXRhdGUoa2V5ID0gZmFjdG9yKGtleSwgDQogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiUG9zaXRpdmUgY29ycmVsYXRpb24iLCAiTmVnYXRpdmUgY29ycmVsYXRpb24iLCAiTm8gY29ycmVsYXRpb24iKSwNCiAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJQb3NpdGl2ZSBjb3JyZWxhdGlvbiIsICJOZWdhdGl2ZSBjb3JyZWxhdGlvbiIsICJObyBjb3JyZWxhdGlvbiIpKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB4LCB5ID0geTAsIGZpbGwgPSBrZXksIGNvbG9yID0ga2V5KSkrDQogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSkrDQogIGZhY2V0X3dyYXAofmtleSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlX3giKSArDQogIGxhYnModGl0bGUgPSAiQ29ycmVsYXRpb24gZ3JhZGllbnQiLA0KICAgICAgIHggPSBOVUxMLA0KICAgICAgIHkgPSBOVUxMLA0KICAgICAgIGNhcHRpb24gPSAiQXV0aG9yOiBOTkIiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiZG9kZ2VyYmx1ZTQiLCAiZG9kZ2VyYmx1ZTIiLCAiZG9kZ2VyYmx1ZSIpKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRvZGdlcmJsdWU0IiwgImRvZGdlcmJsdWUyIiwgImRvZGdlcmJsdWUiKSkrDQogIHRoZW1lX2xpZ2h0KCkrDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHNpemUgPSAxNiksDQogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoY29sb3IgPSAiZG9kZ2VyYmx1ZTQiKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImRvZGdlcmJsdWU0IiksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPSJkb2RnZXJibHVlIiksDQogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoY29sb3VyID0gJ3doaXRlJywgc2l6ZSA9IDEyKSkgDQoNCmBgYA0KDQojIyBDw6FjIHBoxrDGoW5nIHBow6FwIHTDrW5oIHTGsMahbmcgcXVhbg0KDQogICMjIyBQZWFyc29uJ3Mgcg0KICAtIMSQw6FuaCBnacOhIG3hu6ljIMSR4buZIHTGsMahbmcgcXVhbiB0dXnhur9uIHTDrW5oIGPhu6dhIDIgYmnhur9uIMSR4buLbmggbMaw4bujbmcNCiAgDQogIFxbIHJfe3h5fSA9IFxmcmFje1xzdW1fe2k9MX1ee259KHhfaSAtIFxiYXJ7eH0pKHlfaSAtIFxiYXJ7eX0pfXtcc3FydFtde1xzdW1fe2k9MX1ee259KHhfaSAtIFxiYXJ7eH0pXjIgXHN1bV97aT0xfV57bn0oeV9pIC0gXGJhcnt5fSleMn19IFxdDQogIA0KICAtIFbDrSBk4bulOiBT4butIGThu6VuZyBz4buRIGxp4buHdSBkw6JuIHPhu5EgdsOgIHRodSBuaOG6rXAgY+G7p2EgNTAgYmFuZyBj4bunYSBN4bu5DQpgYGB7cn0NCnN0YXRlcyA8LSBzdGF0ZS54NzdbLGMoMToyKV0gDQpoZWFkKHN0YXRlcykNCg0KYGBgDQoNCiAgLSBDw7RuZyB0aOG7qWMgdMOtbmggaOG7hyBz4buRIHTGsMahbmcgcXVhbiBQZWFyc29uIHRyw6puIFI6IGNvcihkZiwgbWV0aG9kID0gInBlYXJzb24iKQ0KDQpgYGB7cn0NCk0gPC0gY29yKHN0YXRlcykNCk0NCmBgYA0KICAgIA0KIyMjIFTGsMahbmcgcXVhbiBo4bqhbmcgKHNwZWFybWFuKQ0KICAtIMSQw6FuaCBnacOhIG3hu6ljIMSR4buZIHTGsMahbmcgcXVhbiBj4bunYSAyIGjhuqFuZyBj4bunYSAyIGJp4bq/biAocmFuay1vcmRlcmVkIHZhcmlhYmxlcyksIHPhu60gZOG7pW5nICBraGkgcGjDom4gcGjhu5FpIGPhu6dhIHThu5VuZyB0aOG7gyDEkcaw4bujYyBnaeG6oyBz4butIGtow7RuZyBwaOG6o2kgbMOgIHBow6JuIHBo4buRaSBjaHXhuqluIGhv4bq3YyB0cm9uZyB0csaw4budbmcgaOG7o3AgY8OzIGPDoWMgZ2nDoSB0cuG7iyBxdWFuIHPDoXQgYuG6pXQgdGjGsOG7nW5nIChs4bubbiBxdcOhIGhv4bq3YyBuaOG7jyBxdcOhKSANCiAgDQogIFxbIDEtXGZyYWN7NlxzdW1faV5uIGRfe2l9XjJ9e24obl4yLTEpfVxdDQogIA0KICBkaTogaGnhu4d1IGjhuqFuZyBj4bunYSAyIGJp4bq/bg0KICANCiAgXFsgZF97aX0gPSByZ1hfe2l9IC0gcmdZX3tpfVxdIA0KICANCiAgLSBDw7RuZyB0aOG7qWMgdMOtbmggdHLDqm4gUjogY29yKGRmLCBtZXRob2QgPSAic3BlYXJtYW4iKQ0KICANCiAgLSBNaW5oIGjhu41hIGLhurFuZyBi4bqjbmcgdMOtbmg6DQogIA0KICANCmBgYHtyfQ0Kc3RhdGVzIDwtIHN0YXRlLng3N1ssYygxOjIpXSANCmhlYWQoc3RhdGVzKQ0KbiA8LSBkaW0oc3RhdGVzKVsxXQ0KDQpzdGF0ZXMgJT4lIA0KICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICBtdXRhdGUocmdYID0gcmFuayhQb3B1bGF0aW9uLCB0aWVzLm1ldGhvZD0gImZpcnN0IiksDQogICAgICAgICByZ1kgPSByYW5rKEluY29tZSwgdGllcy5tZXRob2Q9ICJmaXJzdCIpLA0KICAgICAgICAgZCA9IHJnWCAtIHJnWSwNCiAgICAgICAgIGQyID0gZF4yKSAlPiUgDQogIG11dGF0ZShTcGVhcm1hbl9jb3IgPSAxIC0gNiAqIHN1bShkMikvKG4gKiAobl4yIC0xKSkpICU+JSANCiAgZGF0YXRhYmxlKCkNCg0KTSA8LSBjb3Ioc3RhdGVzWyxjKCJQb3B1bGF0aW9uIiwgIkluY29tZSIpXSwgbWV0aG9kID0gInNwZWFybWFuIikNCk0NCg0KYGBgDQogICANCiMjIyBUxrDGoW5nIHF1YW4gaOG6oW5nIChrZW5kYWxsKQ0KICAtIMSQw6FuaCBnacOhIG3hu6ljIMSR4buZIHTGsMahbmcgcXVhbiBj4bunYSAyIGjhuqFuZyBj4bunYSAyIGJp4bq/biAocmFuay1vcmRlcmVkIHZhcmlhYmxlcyksIGjhu4cgc+G7kSBuw6B5IMSRxrDhu6NjIHPhu60gZOG7pW5nIHTGsMahbmcgdOG7sSBuaMawIHNwZWFybWFuLCB0aMO0bmcgdGjGsOG7nW5nIGjhu4cgc+G7kSBuw6B5IG5o4buPIGjGoW4gc3BlYXJtYW4gDQoNCiAgLSBI4buHIHPhu5Ega2VuZGFsbCDDrXQgZMO5bmcgaMahbiBzbyB24bubaSAyIGjhu4cgc+G7kSB0xrDGoW5nIHF1YW4gdHLDqm4NCg0KICAtIEPDtG5nIHRo4bupYyB0w61uaCB0csOqbiBSOiBjb3IoZGYsIG1ldGhvZCA9ICJrZW5kYWxsIikgIA0KICANCmBgYHtyfQ0KTSA8LSBjb3Ioc3RhdGVzLCBtZXRob2QgPSAia2VuZGFsbCIpDQpNDQpgYGANCiAgDQojIyBLaeG7g20gxJHhu4tuaCB4ZW0gMiBiaeG6v24gY8OzIHTGsMahbmcgcXVhbiB24bubaSBuaGF1IGtow7RuZw0KDQogICogIEPFqW5nIG5oxrAgcGjGsMahbmcgcGjDoXAgdMOtbmgsIGtp4buDbSDEkeG7i25oIGPFqW5nIGPDsyAzIG1ldGhvZDogUGVhcnNvbiwgU3BlYXJtYW4sIEtlbmRhbGwNCiAgDQogICogR2nhuqMgdGhp4bq/dCBraeG7g20gxJHhu4tuaDoNCiAgDQogICAgIC0gSDA6IEtow7RuZyBjw7MgdMawxqFuZyBxdWFuICho4buHIHPhu5EgdMawxqFuZyBxdWFuID0gMCkNCiAgICAgLSBIYTogQ8OzIHTGsMahbmcgcXVhbg0KDQogICogVsOtIGThu6U6IFPhu60gZOG7pW5nIGjDoG0gY29yLnRlc3QgY+G7p2EgZ8OzaSBzdGF0cw0KICANCmBgYHtyfQ0Kc3RhdGVzIDwtIHN0YXRlLng3Nw0Kc3RhdGVzWyxjKDMsNSldICU+JSBjb3IoKQ0KDQpjb3IudGVzdChzdGF0ZXNbLDNdLCBzdGF0ZXNbLDVdKQ0KYGBgDQoNCkjDoG0gbsOgeSBjaOG7iSBkw7luZyDEkcaw4bujYyB24bubaSAyIGJp4bq/bi4gVHJvbmcgdHLGsOG7nW5nIGjhu6NwIG114buRbiB0aOG7sWMgaGnhu4duIHbhu5tpIG5oaeG7gXUgYmnhur9uIGPDsyB0aOG7gyBz4butIGThu6VuZyBow6BtIGNvcnIudGVzdCBj4bunYSBwYWNrYWdlIHBzeWNoDQoNCiAgKiBWw60gZOG7pSBz4butIGThu6VuZyBow6BtIGNvcnIudGVzdA0KDQpgYGB7cn0NCmNvcnIudGVzdChzdGF0ZXNbLDE6NV0sIHVzZSA9ICJjb21wbGV0ZSIsIG1ldGhvZCA9ICJwZWFyc29uIiApDQpgYGANCg0KSMOgbSBuw6B5IGNobyByYSBr4bq/dCBxdeG6oyBj4bunYSBj4bqjIGjhu4cgc+G7kSB0xrDGoW5nIHF1YW4gdsOgIHjDoWMgc3XhuqV0IGtp4buDbSDEkeG7i25oDQoNCljDoWMgc3XhuqV0ID4gbeG7qWMgw70gbmdoxKlhIGFscGhhICg9IDAuMDUpIGPDsyB0aOG7gyBr4bq/dCBsdeG6rW4gaOG7hyBz4buRIHTGsMahbmcgcXVhbiA9IDAgduG7m2kgbeG7qWMgw70gbmdoxKlhIGFscGhhDQoNCiMjIE3hu5l0IHPhu5EgxJHhu5MgdGjhu4sgdHJvbmcgcGjDom4gdMOtY2ggdMawxqFuZyBxdWFuDQoNCiAgLSBjb3JycGxvdCAocGFja2FnZTogY29ycnBsb3QpDQogIA0KYGBge3J9DQpzdGF0ZXMgPC0gc3RhdGUueDc3DQpNIDwtIGNvcihzdGF0ZXMpDQpjb3JycGxvdChNLCBtZXRob2QgPSAiY2lyY2xlIikNCmBgYA0KDQogIC0gY29ycnBsb3QubWl4ZWQgKHBhY2thZ2U6IGNvcnJwbG90KQ0KICANCmBgYHtyfQ0KY29ycnBsb3QubWl4ZWQoTSkNCmBgYA0KDQogIC0gY2hhcnQuQ29ycmVsYXRpb24gKHBhY2thZ2U6IFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KICANCmBgYHtyfQ0KZGF0YShtYW5hZ2VycykNCmNoYXJ0LkNvcnJlbGF0aW9uKG1hbmFnZXJzWywxOjhdLCBoaXN0b2dyYW09VFJVRSwgcGNoPSIrIikNCmBgYA0KDQpIw6BtIG7DoHkgdMawxqFuZyBuaMawOiBwc3ljaDo6cGFpcnMucGFuZWxzLCBHR2FsbHk6OmdncGFpcnMsIGNvcnJyOjpjb3JyZWxhdGUNCg0KIyBQaMOibiB0w61jaCBo4buTaSBxdXkgKHJlZ3Jlc3Npb24gYW5hbHlzaXMpDQoNClPhu60gZOG7pW5nIMSR4buDIGThu7EgYsOhbyAxIGJp4bq/biBwaOG7pSB0aHXhu5ljIChkZXBlbmRlbnQgdmFyaWFibGUsIHJlc3BvbnNlIHZhcmlhYmxlKSBk4buxYSB2w6BvIDEgaGF5IG5oaeG7gXUgYmnhur9uIMSR4buZYyBs4bqtcCAoaW5kZXBlbmRlbnQgdmFyaWFibGVzLCBwcmVkaWN0b3IgdmFyaWFibGVzLCBleHBsYW5hdG9yeSB2YXJpYWJsZXMpDQoNClbDrSBk4bulIDE6IFBow6JuIHTDrWNoIGtoaSB0xINuZyAxIG5ow6JuIHZpw6puIHRow6wgbOG7o2kgbmh14bqtbiBuZ8OibiBow6BuZyB0xINuZyBoYXkgZ2nhuqNtIGJhbyBuaGnDqnUgdGnhu4FuLiBCaeG6v24gcGjhu6UgdGh14buZYyBsw6AgbOG7o2kgbmh14bqtbiBuZ8OibiBow6BuZywgYmnhur9uIMSR4buZYyBs4bqtcCBsw6Agc+G7kSBsxrDhu6NuZyBuaMOibiB2acOqbiAoc2ltcGxlIGxpbmVhcikNCg0KVsOtIGThu6UgMjogVMOtbmggdG/DoW4geGVtIGtoaSB0xINuZyAxIGPDonkgQVRNIHRow6wgbOG7o2kgbmh14bqtbiBuZ8OibiBow6BuZyB0xINuZyBoYXkgZ2nhuqNtIGJhbyBuaGnDqnUgcGjhuqduIHRyxINtLiBCaeG6v24gcGjhu6UgdGh1w7RjIGzDoCBsb2cobOG7o2kgbmh14bqtbiksIGJp4bq/biDEkeG7mWMgbOG6rXAgY8OzIHRo4buDIGzDoCBz4buRIGzGsOG7o25nIG3DoXkgQVRNLCBz4buRIGzGsOG7o25nIG3DoXkgQVRNIGLDrG5oIHBoxrDGoW5nIChQb2x5bm9taWFsKQ0KDQpWw60gZOG7pSAzOiBE4buxIGLDoW8ga2jhuqMgbsSDbmcgcGjDoXQgc2luaCBu4bujIHjhuqV1IGPhu6dhIGtow6FjaCBow6BuZy4gQmnhur9uIHBo4bulIHRodeG7mWMgbMOgIGto4bqjIG7Eg25nIHBow6F0IHNpbmggbuG7oyB44bqldSwgYmnhur9uIMSR4buZYyBs4bqtcCB2w60gZOG7pTogdHXhu5VpLCBnaeG7m2kgdMOtbmgsIHRyw6xuaCDEkeG7mSBo4buNYyB24bqlbiAuLi4gKE11bHRpcGxlIGxpbmVhcikNCg0KVsOtIGThu6UgNDogROG7sSBiw6FvIGdpw6EgY+G7lSBwaGnhur91IGPhu6dhIG5nw6JuIGjDoG5nIHThuqFpIGPDoWMgdGjhu51pIMSRaeG7g20gdHJvbmcgdMawxqFuZyBsYWkuIEJp4bq/biBwaOG7pSB0aHXhu5ljIGzDoCBnacOhIGPhu5UgcGhp4bq/dSwgYmnhur9uIMSR4buZYyBs4bqtcCBjw7MgdGjhu4MgY8OzIGzDoCB0cuG7hSBj4bunYSBiaeG6v24gZ2nDoSwgaG/hurdjIDEgc+G7kSB54bq/dSB04buRIG5oxrAgR1BELCBs4bqhbSBwaMOhdCAuLi4gKFRpbWUtc2VyaWVzKQ0KDQpDw7MgcuG6pXQgbmhp4buBdSBsb+G6oWkgaOG7k2kgcXV5IG5oxrA6IFNpbXBsZSBsaW5lYXIsIFBvbHlub21pYWwsIE11bHRpcGxlIGxpbmVhciwgTXVsdGlsZXZlbCwgTXVsdGl2YXJpYXRlLCBMb2dpc3RpYywgUG9pc3NvbiwgQ294IHByb3BvcnRpb25hbCBoYXphcmRzLCBUaW1lLXNlcmllcywgTm9ubGluZWFyLCBOb25wYXJhbWV0cmljDQoNClRyb25nIMSRw7MsIDQgbG/huqFpIHBo4buVIGJp4bq/biBoYXkgxJHGsOG7o2Mgc+G7rSBk4bulbmcgbMOgDQoNCmBgYHtyfQ0KdHlwZV9tb2RlbCA8LSBkYXRhLmZyYW1lKFR5cGUgPSBjKCJTaW1wbGUgbGluZWFyIiwgDQogICAgICAgICAgICAgICAgICAgICJNdWx0aXBsZSBsaW5lYXIiLA0KICAgICAgICAgICAgICAgICAgICAiTG9naXN0aWMiLA0KICAgICAgICAgICAgICAgICAgICAiVGltZS1zZXJpZXMiKSwNCiAgICAgICAgICAgYFR5cGljYWwgdXNlYCA9IGMoIlByZWRpY3RpbmcgYSBxdWFudGl0YXRpdmUgcmVzcG9uc2UgdmFyaWFibGUgZnJvbSBhIHF1YW50aXRhdGl2ZSBleHBsYW5hdG9yeSB2YXJpYWJsZSIsDQoiUHJlZGljdGluZyBhIHF1YW50aXRhdGl2ZSByZXNwb25zZSB2YXJpYWJsZSBmcm9tIHR3byBvciBtb3JlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyIsDQoiUHJlZGljdGluZyBhIGNhdGVnb3JpY2FsIHJlc3BvbnNlIHZhcmlhYmxlIGZyb20gb25lIG9yIG1vcmUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIiwNCiJNb2RlbGluZyB0aW1lLXNlcmllcyBkYXRhIHdpdGggY29ycmVsYXRlZCBlcnJvcnMiKSkNCg0KdHlwZV9tb2RlbCAlPiUgZGF0YXRhYmxlKCkNCg0KYGBgDQoNCiMjIE5o4buvbmcgaMOgbSB0aMaw4budbmcgZMO5bmcgxJHhu5FpIHbhu5tpIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oDQoNCmBgYHtyfQ0KZGF0YS5mcmFtZShGdW5jdGlvbiA9IGMoInN1bW1hcnkoKSIsICJjb2VmZmljaWVudHMoKSIsICJjb25maW50KCkiLCAiZml0dGVkKCkiLCAicmVzaWR1YWxzKCkiLCAiYW5vdmEoKSIsIA0KInZjb3YoKSIsICJBSUMoKSIsICJwbG90KCkiLCAicHJlZGljdCgpIiksIA0KQWN0aW9uID0NCmMoIkRpc3BsYXlzIGRldGFpbGVkIHJlc3VsdHMgZm9yIHRoZSBmaXR0ZWQgbW9kZWwiLCAiTGlzdHMgdGhlIG1vZGVsIHBhcmFtZXRlcnMgKGludGVyY2VwdCBhbmQgc2xvcGVzKSBmb3IgdGhlIGZpdHRlZCBtb2RlbCIsICJQcm92aWRlcyBjb25maWRlbmNlIGludGVydmFscyBmb3IgdGhlIG1vZGVsIHBhcmFtZXRlcnMgKDk1JSBieSBkZWZhdWx0KSIsDQogICJMaXN0cyB0aGUgcHJlZGljdGVkIHZhbHVlcyBpbiBhIGZpdHRlZCBtb2RlbCIsICJMaXN0cyB0aGUgcmVzaWR1YWwgdmFsdWVzIGluIGEgZml0dGVkIG1vZGVsIiwgIkdlbmVyYXRlcyBhbiBBTk9WQSB0YWJsZSBmb3IgYSBmaXR0ZWQgbW9kZWwsIG9yIGFuIEFOT1ZBIHRhYmxlIGNvbXBhcmluZyB0d28gb3IgbW9yZSBmaXR0ZWQgbW9kZWxzIiwgIkxpc3RzIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgbW9kZWwgcGFyYW1ldGVycyIsICJQcmludHMgQWthaWtl4oCZcyBJbmZvcm1hdGlvbiBDcml0ZXJpb24iLCAiR2VuZXJhdGVzIGRpYWdub3N0aWMgcGxvdHMgZm9yIGV2YWx1YXRpbmcgdGhlIGZpdCBvZiBhIG1vZGVsIiwgIlVzZXMgYSBmaXR0ZWQgbW9kZWwgdG8gcHJlZGljdCByZXNwb25zZSB2YWx1ZXMgZm9yIGEgbmV3IGRhdGFzZXQiKSkgICU+JSAgZGF0YXRhYmxlKCkNCmBgYA0KDQoNCiMjIEjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIMSRxqFuIGJp4bq/biAoU2ltcGxlIGxpbmVhcikNCg0KTMOgIG3DtCBow6xuaCBo4buTaSBxdXkgduG7m2kgMSBiaeG6v24gcGjhu6UgdGh14buZYywgMSBiaeG6v24gxJHhu5ljIGzhuq1wLiBIw6BtIGjhu5NpIHF1eSBjw7MgZOG6oW5nIFkgPSBhICsgYlgNCg0KWSBsw6AgYmnhur9uIHBo4bulIHRodeG7mWMsIFggbMOgIGJp4bq/biDEkeG7mWMgbOG6rXAsIGEgbMOgIGjhu4cgc+G7kSBjaOG6t24gKGludGVyY2VwdCksIGIgbMOgIGjhu4cgc+G7kSBnw7NjIChjb2VmZmljaWVudCkNCg0KVsOtIGThu6U6IFbhu5tpIGLhu5kgZOG7ryBsaeG7h3Ugd29tZW4sIGPDsyAyIGJp4bq/biBsw6AgY2hp4buBdSBjYW8sIGPDom4gbuG6t25nIGPhu6dhIHBo4bulIG7hu68uIE114buRbiBk4buxIGLDoW8gY8OibiBu4bq3bmcgY+G7p2EgcGjhu6UgbuG7ryBk4buxYSB2w6BvIGNoaeG7gXUgY2FvIGPhu6dhIGjhu40gdGEgeMOieSBk4buxbmcgbcO0IGjDrG5oIGjhu5NpIHF1eSDEkcahbg0KDQpgYGB7cn0NCmRhdGEod29tZW4pDQoNCmhlYWQod29tZW4pDQoNCndvbWVuICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gaGVpZ2h0LCB5ID0gd2VpZ2h0KSkrDQogIGdlb21fcG9pbnQoKSsNCiAgbGFicyh4ID0gImhlaWdodCAoaW4gaW5jaGVzKSIsDQogICAgICAgeSA9ICJ3ZWlnaHQgKGluIHBvdW5kcykiKQ0KDQpmaXQgPC0gbG0od2VpZ2h0IH4gaGVpZ2h0LCBkYXRhPXdvbWVuKQ0KYGBgDQoNClPhu60gZOG7pW5nIHBoxrDGoW5nIHBow6FwIGLDrG5oIHBoxrDGoW5nIG5o4buPIG5o4bqldCBPTFMgKE9yZGluYXJ5IGxlYXN0IHNxdWFyZXMpDQoNCmBgYHtyfQ0Kc3VtbWFyeShmaXQpDQpgYGANCg0KTmjGsCB24bqteSBwaMawxqFuZyB0csOsbmggaOG7k2kgcXV5IGPDsyBk4bqhbmcgDQoNClxbIFxoYXR7d2VpZ2h0fSA9IC04Ny41MSArIDMuNDUgKiBoZWlnaHQgXF0NCg0KROG7sSBiw6FvIChmaXR0ZWQpDQoNCmBgYHtyfQ0KZml0dGVkKGZpdCkNCg0Kd29tZW4kd2VpZ2h0X3ByZSA8LSBmaXR0ZWQoZml0KQ0KYGBgDQoNClBo4bqnbiBkxrAgKHNhaSBs4buHY2gpDQpgYGB7cn0NCnJlc2lkdWFscyhmaXQpDQoNCndvbWVuJHJlcyA8LSByZXNpZHVhbHMoZml0KQ0KYGBgDQoNCmBgYHtyfQ0Kd29tZW4gJT4lIA0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGhlaWdodCwgeSA9IHdlaWdodCksIGNvbG9yID0gImJsdWUiKSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGhlaWdodCwgeSA9IHdlaWdodF9wcmUpLCBjb2xvciA9ICJyZWQiKSsNCiAgbGFicyh4ID0gImhlaWdodCAoaW4gaW5jaGVzKSIsDQogICAgICAgeSA9ICJ3ZWlnaHQgKGluIHBvdW5kcykiKQ0KYGBgDQoNCg0KIyMgSOG7k2kgcXV5IHR1eeG6v24gdMOtbmggxJFhIGJp4bq/bg0KDQpNw7QgaMOsbmggaOG7k2kgcXV5IGThuqFuZzogXFtcaGF0e1l9ID0gXGJldGFfezB9ICsgXGJldGFfezF9WF97MX0gKyBcYmV0YV97Mn1YX3syfSArIFxiZXRhX3szfVhfezN9ICsgLi4uXF0NCg0KVsOtIGThu6UgbOG6pXkgYuG7mSBz4buRIGxp4buHdSBnaeG6v3QgbmfGsOG7nWkg4bufIGPDoWMgYmFuZyBj4bunYSBN4bu5IHN0YXRlLng3Nw0KDQpgYGB7cn0NCnN0YXRlcyA8LSBhcy5kYXRhLmZyYW1lKHN0YXRlLng3N1ssYygiTXVyZGVyIiwgIlBvcHVsYXRpb24iLCAiSWxsaXRlcmFjeSIsICJJbmNvbWUiLCAiRnJvc3QiKV0pDQoNCnN0YXRlcyAlPiUgaGVhZCgpDQpgYGANCg0KICAtIFBow6JuIHTDrWNoIHTGsMahbmcgcXVhbg0KYGBge3J9DQpjaGFydC5Db3JyZWxhdGlvbihzdGF0ZXMsIGhpc3RvZ3JhbT1UUlVFLCBwY2g9IisiKQ0KYGBgDQoNCg0KYGBge3J9DQpmaXQgPC0gbG0oTXVyZGVyIH4gUG9wdWxhdGlvbiArIElsbGl0ZXJhY3kgKyBJbmNvbWUgKyBGcm9zdCwgZGF0YSA9IHN0YXRlcykNCnN1bW1hcnkoZml0KQ0KDQpzdGF0ZXMkcHJlIDwtIGZpdHRlZChmaXQpDQpzdGF0ZXMkcmVzIDwtIHJlc2lkdWFscyhmaXQpDQpgYGANCg0KR2nDoSB0cuG7iyBwLXZhbHVlICBJbmNvbWUgdsOgIEZyb3N0ID4gMC4wNS4gVsOsIHbhuq15LCB24bubaSBt4bupYyDDvSBuZ2jEqWEgMC4wNSBjw7MgdGjhu4Mga+G6v3QgbHXhuq1uIGjhu4cgc+G7kSBj4bunYSAyIGJp4bq/biBuw6B5IGLhurFuZyAwDQoNCi0gS2hv4bqjbmcgdGluIGPhuq15IGPhu6dhIGjhu4cgc+G7kSBo4buTaSBxdXkNCg0KYGBge3J9DQpjb25maW50KGZpdCkNCmBgYA0KDQpUaGF5IHbDrCB2aeG7h2MgbmjDrG4gdsOgbyBwdmFsdWUg4bufIG3DtCBow6xuaCBo4buTaSBxdXksIGtob+G6o25nIHRpbiBj4bqteSBj4bunYSBo4buHIHPhu5EgaOG7k2kgcXV5IGNobyBwaMOpcCDEkcOhbmggZ2nDoSB4ZW0gaOG7hyBz4buRIGjhu5NpIHF1eSBjw7Mga2jDoWMgMCBoYXkga2jDtG5nLg0KDQpLaG/huqNuZyB0aW4gY+G6rXkgY+G7p2EgSOG7hyBz4buRIGPhu6dhIEludGVyY2VwdCwgSW5jb21lLCBGcm9zdCBjw7MgY+G6rW4gZMaw4bubaSA8IDAsIHRyb25nIGtoaSBj4bqtbiB0csOqbiA+IDAgLS0+IG5ow6xuIHbDoG8gxJHDonkgY8OzIHRo4buDIGJp4bq/dCDEkcaw4bujYyAzIGjhu4cgc+G7kSBuw6B5ID0gMCB24bubaSBt4bupYyDDvSBuZ2jEqWEgMC4wNSUgDQoNCg0KYGBge3J9DQpwYXIobWZyb3c9YygyLDIpKQ0KcGxvdChmaXQpDQoNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCiMjIEtp4buDbSDEkeG7i25oIHbDoCBs4buxYSBjaOG7jW4gbcO0IGjDrG5oDQoNCiMjIyBE4buxYSB2w6BvIMSR4buTIHRo4buLIHBo4bqnbiBkxrANCg0KICAtIEJp4buDdSDEkeG7kyAxOiBW4bq9IHTGsMahbmcgcXVhbiBnaeG7r2EgcGjhuqduIGTGsCB2w6Aga+G6v3QgcXXhuqMgZOG7sSBiw6FvLCBnacOhIHRy4buLIHBo4bqnbiBkxrAgY8Ogbmcg4bufIHF1YW5oIG3hu6ljIDAga+G6v3QgcXXhuqMgZOG7sSBiw6FvIGPDoG5nIHThu5F0LiBOaMawIHbhuq15LCB04bu3IGzhu4cgZ2nhur90IG5nxrDhu51pIMSRYW5nIMSRxrDhu6NjIGThu7EgYsOhbyBxdcOhIGNhbyBzbyB24bubaSB0aOG7sWMgdOG6vyB04bqhaSAyIGJhbmcgUmhvZGUgSXNsYW5kIHbDoCBNYXNzY2h1c2V0dHMsIGThu7EgYsOhbyBxdcOhIHRo4bqlcCB04bqhaSBiYW5nIE5ldmFkYQ0KICANCiAgLSBCaeG7g3UgxJHhu5MgMjogS2nhu4NtIHRyYSB4ZW0gcGjhuqduIGTGsCBjw7MgcGjDom4gcGjhu5FpIGNodeG6qW4gTigwLDEpIGhheSBraMO0bmcsIGvhur90IHF14bqjIGNobyB0aOG6pXkgcGjhuqduIGTGsCB04bqhaSAzIGJhbmcgTmV2YWRhIHbDoCBSaG9kZSBJc2xhbmQgdsOgIEFsYXNrYSDEkWFuZyBrbyB0aGVvIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIGNodeG6qW4NCiAgDQogIC0gQmnhu4N1IMSR4buTIDM6IMSQw6FuaCBnacOhIHBoxrDGoW5nIHNhaSBj4bunYSBwaOG6p24gZMawIGPDsyDEkeG7k25nIG5o4bqldCBoYXkga2jDtG5nDQogIA0KICAtIEJp4buBdSDEkeG7kyA0OiBDaG8gcGjDqXAgcGjDoXQgaGnhu4duIHJhIGPDoWMgb3V0bGllcnMgdHJvbmcgcGjhuqduIGTGsA0KDQojIyMgROG7sWEgdsOgbyBjw6FjIGtp4buDbSDEkeG7i25oDQoNCiMjIyBHaeG6oyB0aGnhur90IDE6IFNhaSBz4buRIG5n4bqrdSBuaGnDqm4gY8OzIHBow6JuIHBo4buRaSBjaHXhuqluDQoNCiMjIyBHaeG6oyB0aGnhur90IDI6IEvhu7MgduG7jW5nIGPhu6dhIHNhaSBz4buRIG5n4bqrdSBuaGnDqm4gdOG6oWkgbeG7l2kgZ2nDoSB0cuG7iyBi4bqxbmcgMA0KDQpgYGB7cn0NCnJlcyA8LSByZXNpZHVhbHMoZml0KQ0KDQp0LnRlc3QocmVzLCBtdSA9IDApDQpgYGANCg0KcC12YWx1ZSA9IDE6IG3DtCBow6xuaCB0aOG7j2EgbcOjbiBnaeG6oyB0aGnhur90IDINCg0KIyMjIEdp4bqjIHRoaeG6v3QgMzogUGjGsMahbmcgc2FpIGPhu6dhIHNhaSBz4buRIG5n4bqrdSBuaGnDqm4ga2jDtG5nIMSR4buVaSANCkgwOiBwaMawxqFuZyBzYWkgc2FpIHPhu5Ega2jDtG5nIMSR4buVaQ0KSGE6IFBoxrDGoW5nIHNhaSBzYWkgc+G7kSB0aGF5IMSR4buVaQ0KDQpgYGB7cn0NCm5jdlRlc3QoZml0KQ0KYGBgDQoNCnAtdmFsdWUgPSAwLjE4LCBjw7MgdGjhu4Mga+G6v3QgbHXhuq1uIHBoxrDGoW5nIHNhaSBzYWkgc+G7kSBraMO0bmcgxJHhu5VpIHbhu5tpIG3hu6ljIMO9IG5naMSpYSA8IDAuMTgNCg0KIyMjIEdp4bqjIHRoaeG6v3QgNDogR2nhu69hIGPDoWMgYmnhur9uIMSR4buZYyBs4bqtcCBraMO0bmcgY8OzIG3hu5FpIHF1YW4gaOG7hyDEkWEgY+G7mW5nIHR1eeG6v24gaG/DoG4gaOG6o28NCg0KYGBge3J9DQp2aWYoZml0KQ0KYGBgDQoNCnZpZiA8IDEwIGNobyB0aOG6pXkgY8OhYyBiaeG6v24gxJHhu5ljIGzhuq1wIGtow7RuZyBjw7MgxJFhIGPhu5luZyB0dXnhur9uDQoNCiMjIEzhu7FhIGNo4buNbiBtw7QgaMOsbmgNClNhdSBraGkga2nhu4NtIMSR4buLbmggY8OhYyBnaeG6oyB0aGnhur90IGPhu6dhIG3DtCBow6xuaCBPTFMsIHRo4butIG5naGnhu4dtIHRyxrDhu51uZyBo4bujcCBi4buPIDIgYmnhur9uIEluY29tZSwgRnJvc3QgcmEga2jhu49pIG3DtCBow6xuaA0KDQpgYGB7cn0NCmZpdCA8LSBsbShNdXJkZXIgfiBQb3B1bGF0aW9uICsgSWxsaXRlcmFjeSwgZGF0YSA9IHN0YXRlcykNCnN1bW1hcnkoZml0KQ0KYGBgDQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QoZml0KQ0KDQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNCg==