Phân tích tương quan
Ví dụ:

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
Ðặ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

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”)
## Population Income
## Population 1.0000000 0.2082276
## Income 0.2082276 1.0000000
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}\]
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
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
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
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)

- 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
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()
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()
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
## 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)
##
## 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)
## 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)
## 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)")

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

Kiểm định và lựa chọn mô hình
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ư
Dựa vào các kiểm định
Giả thiết 1: Sai số ngẫu nhiên có phân phối chuẩn
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
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
## 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
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
## 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
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)

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==