Residual plot là gì

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()
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 Akaikes 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()

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

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)

  • 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ư

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

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

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

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)

LS0tDQp0aXRsZTogIlBow6JuIHTDrWNoIGjhu5NpIHF1eSB2w6AgdMawxqFuZyBxdWFuIg0KYXV0aG9yOiAiTmd1eeG7hW4gTmfhu41jIELDrG5oIg0KZGF0ZTogIk9jdG9iZXIgMTYsIDIwMTgiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgaGlnaGxpZ2h0OiBweWdtZW50cw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IG5vDQogIHBkZl9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICBmaWdfd2lkdGg6IDkgDQogIGZpZ19oZWlnaHQ6IDYgDQogIGZpZy5jYXAgOiAiU291cmNlOiBCSSAtIFNlQWJhbmsgIg0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogY29uc29sZQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h2bmskc2V0KGVjaG8gPSBUUlVFKQ0KbGlicmFyeShEVCkNCmxpYnJhcnkocHN5Y2gpDQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KbGlicmFyeShjYXIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KGdncmVwZWwpDQoNCg0KdGhlbWVfc2V0KHRoZW1lX2xpZ2h0KCkpDQpgYGANCg0KDQojIFBow6JuIHTDrWNoIHTGsMahbmcgcXVhbg0KDQojIyBWw60gZOG7pToNCg0KYGBge3IsIGVjaG89RkFMU0V9DQpyIDwtIGRhdGEuZnJhbWUoeTAgPSBybm9ybSgxMDAsIG1lYW4gPSAyLCBzZCA9IDEpKSAlPiUgDQogIG11dGF0ZShgTGluZWFyIGNvcnJlbGF0aW9uYCA9IC0geTAgKiAxMCArIHJub3JtKDEwMCksDQogICAgICAgICBgTm9ubGluZWFyIGNvcnJlbGF0aW9uYCA9IHkwXjIgKyBybm9ybSgxMDApLA0KICAgICAgICAgYE5vIGNvcnJlbGF0aW9uYCA9IHJub3JtKDEwMCkpIA0KDQpyICU+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/biBwaOG7pSB0aHXhu5ljIChkZXBlbmRlbnQgdmFyaWFibGUsIHJlc3BvbnNlIHZhcmlhYmxlKSBk4buxYSB2w6BvIDEgaGF5IG5oaeG7gXUgYmnhur9uIMSR4buZYyBs4bqtcCAoaW5kZXBlbmRlbnQgdmFyaWFibGVzLCBwcmVkaWN0b3IgdmFyaWFibGVzLCBleHBsYW5hdG9yeSB2YXJpYWJsZXMpDQoNClbDrSBk4bulIDE6IFBow6JuIHTDrWNoIGtoaSB0xINuZyAxIG5ow6JuIHZpw6puIHRow6wgbOG7o2kgbmh24bqtbiBuZ8OibiBow6BuZyB0xINuZyBoYXkgZ2nhuqNtIGJhbyBuaGnDqnUgdGnhu4FuLiBCaeG6v24gcGjhu6UgdGh24buZYyBsw6AgbOG7o2kgbmh24bqtbiBuZ8OibiBow6BuZywgYmnhur9uIMSR4buZYyBs4bqtcCBsw6Agc+G7kSBsxrDhu6NuZyBuaMOibiB2acOqbiAoc2ltcGxlIGxpbmVhcikNCg0KVsOtIGThu6UgMjogVMOtbmggdG/DoW4geGVtIGtoaSB0xINuZyAxIGPDonkgQVRNIHRow6wgbOG7o2kgbmh24bqtbiBuZ8OibiBow6BuZyB0xINuZyBoYXkgZ2nhuqNtIGJhbyBuaGnDqnUgcGjhuqduIHRyxINtLiBCaeG6v24gcGjhu6UgdGh2w7RjIGzDoCBsb2cobOG7o2kgbmh24bqtbiksIGJp4bq/biDEkeG7mWMgbOG6rXAgY8OzIHRo4buDIGzDoCBz4buRIGzGsOG7o25nIG3DoXkgQVRNLCBz4buRIGzGsOG7o25nIG3DoXkgQVRNIGLDrG5oIHBoxrDGoW5nIChQb2x5bm9taWFsKQ0KDQpWw60gZOG7pSAzOiBE4buxIGLDoW8ga2jhuqMgbsSDbmcgcGjDoXQgc2luaCBu4bujIHjhuqV1IGPhu6dhIGtow6FjaCBow6BuZy4gQmnhur9uIHBo4bulIHRodeG7mWMgbMOgIGto4bqjIG7Eg25nIHBow6F0IHNpbmggbuG7oyB44bqldSwgYmnhur9uIMSR4buZYyBs4bqtcCB2w60gZOG7pTogdHXhu5VpLCBnaeG7m2kgdMOtbmgsIHRyw6xuaCDEkeG7mSBo4buNYyB24bqlbiAuLi4gKE11bHRpcGxlIGxpbmVhcikNCg0KVsOtIGThu6UgNDogROG7sSBiw6FvIGdpw6EgY+G7lSBwaGnhur91IGPhu6dhIG5nw6JuIGjDoG5nIHThuqFpIGPDoWMgdGjhu51pIMSRaeG7g20gdHJvbmcgdMawxqFuZyBsYWkuIEJp4bq/biBwaOG7pSB0aHXhu5ljIGzDoCBnacOhIGPhu5UgcGhp4bq/dSwgYmnhur9uIMSR4buZYyBs4bqtcCBjw7MgdGjhu4MgY8OzIGzDoCB0cuG7hSBj4bunYSBiaeG6v24gZ2nDoSwgaG/hurdjIDEgc+G7kSB54bq/dSB04buRIG5oxrAgR1BELCBs4bqhbSBwaMOhdCAuLi4gKFRpbWUtc2VyaWVzKQ0KDQpDw7MgcuG6pXQgbmhp4buBdSBsb+G6oWkgaOG7k2kgcXV5IG5oxrA6IFNpbXBsZSBsaW5lYXIsIFBvbHlub21pYWwsIE11bHRpcGxlIGxpbmVhciwgTXVsdGlsZXZlbCwgTXVsdGl2YXJpYXRlLCBMb2dpc3RpYywgUG9pc3NvbiwgQ294IHByb3BvcnRpb25hbCBoYXphcmRzLCBUaW1lLXNlcmllcywgTm9ubGluZWFyLCBOb25wYXJhbWV0cmljDQoNClRyb25nIMSRw7MsIDQgbG/huqFpIHBo4buVIGJp4bq/biBoYXkgxJHGsOG7o2Mgc+G7rSBk4bulbmcgbMOgDQoNCmBgYHtyfQ0KdHlwZV9tb2RlbCA8LSBkYXRhLmZyYW1lKFR5cGUgPSBjKCJTaW1wbGUgbGluZWFyIiwgDQogICAgICAgICAgICAgICAgICAgICJNdWx0aXBsZSBsaW5lYXIiLA0KICAgICAgICAgICAgICAgICAgICAiTG9naXN0aWMiLA0KICAgICAgICAgICAgICAgICAgICAiVGltZS1zZXJpZXMiKSwNCiAgICAgICAgICAgYFR5cGljYWwgdXNlYCA9IGMoIlByZWRpY3RpbmcgYSBxdWFudGl0YXRpdmUgcmVzcG9uc2UgdmFyaWFibGUgZnJvbSBhIHF1YW50aXRhdGl2ZSBleHBsYW5hdG9yeSB2YXJpYWJsZSIsDQoiUHJlZGljdGluZyBhIHF1YW50aXRhdGl2ZSByZXNwb25zZSB2YXJpYWJsZSBmcm9tIHR3byBvciBtb3JlIGV4cGxhbmF0b3J5IHZhcmlhYmxlcyIsDQoiUHJlZGljdGluZyBhIGNhdGVnb3JpY2FsIHJlc3BvbnNlIHZhcmlhYmxlIGZyb20gb25lIG9yIG1vcmUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIiwNCiJNb2RlbGluZyB0aW1lLXNlcmllcyBkYXRhIHdpdGggY29ycmVsYXRlZCBlcnJvcnMiKSkNCg0KdHlwZV9tb2RlbCAlPiUgZGF0YXRhYmxlKCkNCg0KYGBgDQoNCiMjIE5o4buvbmcgaMOgbSB0aMaw4budbmcgZMO5bmcgxJHhu5FpIHbhu5tpIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oDQoNCmBgYHtyfQ0KZGF0YS5mcmFtZShGdW5jdGlvbiA9IGMoInN1bW1hcnkoKSIsICJjb2VmZmljaWVudHMoKSIsICJjb25maW50KCkiLCAiZml0dGVkKCkiLCAicmVzaWR1YWxzKCkiLCAiYW5vdmEoKSIsIA0KInZjb3YoKSIsICJBSUMoKSIsICJwbG90KCkiLCAicHJlZGljdCgpIiksIA0KQWN0aW9uID0NCmMoIkRpc3BsYXlzIGRldGFpbGVkIHJlc3VsdHMgZm9yIHRoZSBmaXR0ZWQgbW9kZWwiLCAiTGlzdHMgdGhlIG1vZGVsIHBhcmFtZXRlcnMgKGludGVyY2VwdCBhbmQgc2xvcGVzKSBmb3IgdGhlIGZpdHRlZCBtb2RlbCIsICJQcm92aWRlcyBjb25maWRlbmNlIGludGVydmFscyBmb3IgdGhlIG1vZGVsIHBhcmFtZXRlcnMgKDk1JSBieSBkZWZhdWx0KSIsDQogICJMaXN0cyB0aGUgcHJlZGljdGVkIHZhbHVlcyBpbiBhIGZpdHRlZCBtb2RlbCIsICJMaXN0cyB0aGUgcmVzaWR1YWwgdmFsdWVzIGluIGEgZml0dGVkIG1vZGVsIiwgIkdlbmVyYXRlcyBhbiBBTk9WQSB0YWJsZSBmb3IgYSBmaXR0ZWQgbW9kZWwsIG9yIGFuIEFOT1ZBIHRhYmxlIGNvbXBhcmluZyB0d28gb3IgbW9yZSBmaXR0ZWQgbW9kZWxzIiwgIkxpc3RzIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBmb3IgbW9kZWwgcGFyYW1ldGVycyIsICJQcmludHMgQWthaWtl4oCZcyBJbmZvcm1hdGlvbiBDcml0ZXJpb24iLCAiR2VuZXJhdGVzIGRpYWdub3N0aWMgcGxvdHMgZm9yIGV2YWx1YXRpbmcgdGhlIGZpdCBvZiBhIG1vZGVsIiwgIlVzZXMgYSBmaXR0ZWQgbW9kZWwgdG8gcHJlZGljdCByZXNwb25zZSB2YWx1ZXMgZm9yIGEgbmV3IGRhdGFzZXQiKSkgICU+JSAgZGF0YXRhYmxlKCkNCmBgYA0KDQoNCiMjIEjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIMSRxqFuIGJp4bq/biAoU2ltcGxlIGxpbmVhcikNCg0KTMOgIG3DtCBow6xuaCBo4buTaSBxdXkgduG7m2kgMSBiaeG6v24gcGjhu6UgdGh24buZYywgMSBiaeG6v24gxJHhu5ljIGzhuq1wLiBIw6BtIGjhu5NpIHF1eSBjw7MgZOG6oW5nIFkgPSBhICsgYlgNCg0KWSBsw6AgYmnhur9uIHBo4bulIHRodeG7mWMsIFggbMOgIGJp4bq/biDEkeG7mWMgbOG6rXAsIGEgbMOgIGjhu4cgc+G7kSBjaOG6t24gKGludGVyY2VwdCksIGIgbMOgIGjhu4cgc+G7kSBnw7NjIChjb2VmZmljaWVudCkNCg0KVsOtIGThu6U6IFbhu5tpIGLhu5kgZOG7ryBsaeG7h3Ugd29tZW4sIGPDsyAyIGJp4bq/biBsw6AgY2hp4buBdSBjYW8sIGPDom4gbuG6t25nIGPhu6dhIHBo4bulIG7hu68uIE114buRbiBk4buxIGLDoW8gY8OibiBu4bq3bmcgY+G7p2EgcGjhu6UgbuG7ryBk4buxYSB2w6BvIGNoaeG7gXUgY2FvIGPhu6dhIGjhu40gdGEgeMOieSBk4buxbmcgbcO0IGjDrG5oIGjhu5NpIHF1eSDEkcahbg0KDQpgYGB7cn0NCmRhdGEod29tZW4pDQoNCmhlYWQod29tZW4pDQoNCndvbWVuICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gaGVpZ2h0LCB5ID0gd2VpZ2h0KSkrDQogIGdlb21fcG9pbnQoKSsNCiAgbGFicyh4ID0gImhlaWdodCAoaW4gaW5jaGVzKSIsDQogICAgICAgeSA9ICJ3ZWlnaHQgKGluIHBvdW5kcykiKQ0KDQpmaXQgPC0gbG0od2VpZ2h0IH4gaGVpZ2h0LCBkYXRhPXdvbWVuKQ0KYGBgDQoNClPhu60gZOG7pW5nIHBoxrDGoW5nIHBow6FwIGLDrG5oIHBoxrDGoW5nIG5o4buPIG5o4bqldCBPTFMgKE9yZGluYXJ5IGxlYXN0IHNxdWFyZXMpDQoNCmBgYHtyfQ0Kc3VtbWFyeShmaXQpDQpgYGANCg0KTmjGsCB24bqteSBwaMawxqFuZyB0csOsbmggaOG7k2kgcXV5IGPDsyBk4bqhbmcgDQoNClxbIFxoYXR7d2VpZ2h0fSA9IC04Ny41MSArIDMuNDUgKiBoZWlnaHQgXF0NCg0KROG7sSBiw6FvIChmaXR0ZWQpDQoNCmBgYHtyfQ0KZml0dGVkKGZpdCkNCg0Kd29tZW4kd2VpZ2h0X3ByZSA8LSBmaXR0ZWQoZml0KQ0KYGBgDQoNClBo4bqnbiBkxrAgKHNhaSBs4buHY2gpDQpgYGB7cn0NCnJlc2lkdWFscyhmaXQpDQoNCndvbWVuJHJlcyA8LSByZXNpZHVhbHMoZml0KQ0KYGBgDQoNCmBgYHtyfQ0Kd29tZW4gJT4lIA0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGhlaWdodCwgeSA9IHdlaWdodCksIGNvbG9yID0gImJsdWUiKSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGhlaWdodCwgeSA9IHdlaWdodF9wcmUpLCBjb2xvciA9ICJyZWQiKSsNCiAgbGFicyh4ID0gImhlaWdodCAoaW4gaW5jaGVzKSIsDQogICAgICAgeSA9ICJ3ZWlnaHQgKGluIHBvdW5kcykiKQ0KYGBgDQoNCg0KIyMgSOG7k2kgcXV5IHR1eeG6v24gdMOtbmggxJFhIGJp4bq/bg0KDQpNw7QgaMOsbmggaOG7k2kgcXV5IGThuqFuZzogXFtcaGF0e1l9ID0gXGJldGFfezB9ICsgXGJldGFfezF9WF97MX0gKyBcYmV0YV97Mn1YX3syfSArIFxiZXRhX3szfVhfezN9ICsgLi4uXF0NCg0KVsOtIGThu6UgbOG6pXkgYuG7mSBz4buRIGxp4buHdSBnaeG6v3QgbmfGsOG7nWkg4bufIGPDoWMgYmFuZyBj4bunYSBN4bu5IHN0YXRlLng3Nw0KDQpgYGB7cn0NCnN0YXRlcyA8LSBhcy5kYXRhLmZyYW1lKHN0YXRlLng3N1ssYygiTXVyZGVyIiwgIlBvcHVsYXRpb24iLCAiSWxsaXRlcmFjeSIsICJJbmNvbWUiLCAiRnJvc3QiKV0pDQoNCnN0YXRlcyAlPiUgaGVhZCgpDQpgYGANCg0KICAtIFBow6JuIHTDrWNoIHTGsMahbmcgcXVhbg0KYGBge3J9DQpjaGFydC5Db3JyZWxhdGlvbihzdGF0ZXMsIGhpc3RvZ3JhbT1UUlVFLCBwY2g9IisiKQ0KYGBgDQoNCg0KYGBge3J9DQpmaXQgPC0gbG0oTXVyZGVyIH4gUG9wdWxhdGlvbiArIElsbGl0ZXJhY3kgKyBJbmNvbWUgKyBGcm9zdCwgZGF0YSA9IHN0YXRlcykNCnN1bW1hcnkoZml0KQ0KDQpzdGF0ZXMkcHJlIDwtIGZpdHRlZChmaXQpDQpzdGF0ZXMkcmVzIDwtIHJlc2lkdWFscyhmaXQpDQpgYGANCg0KR2nDoSB0cuG7iyBwLXZhbHVlICBJbmNvbWUgdsOgIEZyb3N0ID4gMC4wNS4gVsOsIHbhuq15LCB24bubaSBt4bupYyDDvSBuZ2jEqWEgMC4wNSBjw7MgdGjhu4Mga+G6v3QgbHXhuq1uIGjhu4cgc+G7kSBj4bunYSAyIGJp4bq/biBuw6B5IGLhurFuZyAwDQoNCi0gS2hv4bqjbmcgdGluIGPhuq15IGPhu6dhIGjhu4cgc+G7kSBo4buTaSBxdXkNCg0KYGBge3J9DQpjb25maW50KGZpdCkNCmBgYA0KDQpUaGF5IHbDrCB2aeG7h2MgbmjDrG4gdsOgbyBwdmFsdWUg4bufIG3DtCBow6xuaCBo4buTaSBxdXksIGtob+G6o25nIHRpbiBj4bqteSBj4bunYSBo4buHIHPhu5EgaOG7k2kgcXV5IGNobyBwaMOpcCDEkcOhbmggZ2nDoSB4ZW0gaOG7hyBz4buRIGjhu5NpIHF1eSBjw7Mga2jDoWMgMCBoYXkga2jDtG5nLg0KDQpLaG/huqNuZyB0aW4gY+G6rXkgY+G7p2EgSOG7hyBz4buRIGPhu6dhIEludGVyY2VwdCwgSW5jb21lLCBGcm9zdCBjw7MgY+G6rW4gZMaw4bubaSA8IDAsIHRyb25nIGtoaSBj4bqtbiB0csOqbiA+IDAgLS0+IG5ow6xuIHbDoG8gxJHDonkgY8OzIHRo4buDIGJp4bq/dCDEkcaw4bujYyAzIGjhu4cgc+G7kSBuw6B5ID0gMCB24bubaSBt4bupYyDDvSBuZ2jEqWEgMC4wNSUgDQoNCg0KYGBge3J9DQpwYXIobWZyb3c9YygyLDIpKQ0KcGxvdChmaXQpDQoNCnBhcihtZnJvdz1jKDEsMSkpDQpgYGANCiMjIEtp4buDbSDEkeG7i25oIHbDoCBs4buxYSBjaOG7jW4gbcO0IGjDrG5oDQoNCiMjIyBE4buxYSB2w6BvIMSR4buTIHRo4buLIHBo4bqnbiBkxrANCg0KICAtIEJp4buDdSDEkeG7kyAxOiBW4bq9IHTGsMahbmcgcXVhbiBnaeG7r2EgcGjhuqduIGTGsCB2w6Aga+G6v3QgcXXhuqMgZOG7sSBiw6FvLCBnacOhIHRy4buLIHBo4bqnbiBkxrAgY8Ogbmcg4bufIHF1YW5oIG3hu6ljIDAga+G6v3QgcXXhuqMgZOG7sSBiw6FvIGPDoG5nIHThu5F0LiBOaMawIHbhuq15LCB04bu3IGzhu4cgZ2nhur90IG5nxrDhu51pIMSRYW5nIMSRxrDhu6NjIGThu7EgYsOhbyBxdcOhIGNhbyBzbyB24bubaSB0aOG7sWMgdOG6vyB04bqhaSAyIGJhbmcgUmhvZGUgSXNsYW5kIHbDoCBNYXNzY2h2c2V0dHMsIGThu7EgYsOhbyBxdcOhIHRo4bqlcCB04bqhaSBiYW5nIE5ldmFkYQ0KICANCiAgLSBCaeG7g3UgxJHhu5MgMjogS2nhu4NtIHRyYSB4ZW0gcGjhuqduIGTGsCBjw7MgcGjDom4gcGjhu5FpIGNodeG6qW4gTigwLDEpIGhheSBraMO0bmcsIGvhur90IHF14bqjIGNobyB0aOG6pXkgcGjhuqduIGTGsCB04bqhaSAzIGJhbmcgTmV2YWRhIHbDoCBSaG9kZSBJc2xhbmQgdsOgIEFsYXNrYSDEkWFuZyBrbyB0aGVvIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIGNodeG6qW4NCiAgDQogIC0gQmnhu4N1IMSR4buTIDM6IMSQw6FuaCBnacOhIHBoxrDGoW5nIHNhaSBj4bunYSBwaOG6p24gZMawIGPDsyDEkeG7k25nIG5o4bqldCBoYXkga2jDtG5nDQogIA0KICAtIEJp4buBdSDEkeG7kyA0OiBDaG8gcGjDqXAgcGjDoXQgaGnhu4duIHJhIGPDoWMgb3V0bGllcnMgdHJvbmcgcGjhuqduIGTGsA0KDQojIyMgROG7sWEgdsOgbyBjw6FjIGtp4buDbSDEkeG7i25oDQoNCiMjIyBHaeG6oyB0aGnhur90IDE6IFNhaSBz4buRIG5n4bqrdSBuaGnDqm4gY8OzIHBow6JuIHBo4buRaSBjaHXhuqluDQoNCiMjIyBHaeG6oyB0aGnhur90IDI6IEvhu7MgduG7jW5nIGPhu6dhIHNhaSBz4buRIG5n4bqrdSBuaGnDqm4gdOG6oWkgbeG7l2kgZ2nDoSB0cuG7iyBi4bqxbmcgMA0KDQpgYGB7cn0NCnJlcyA8LSByZXNpZHVhbHMoZml0KQ0KDQp0LnRlc3QocmVzLCBtdSA9IDApDQpgYGANCg0KcC12YWx1ZSA9IDE6IG3DtCBow6xuaCB0aOG7j2EgbcOjbiBnaeG6oyB0aGnhur90IDINCg0KIyMjIEdp4bqjIHRoaeG6v3QgMzogUGjGsMahbmcgc2FpIGPhu6dhIHNhaSBz4buRIG5n4bqrdSBuaGnDqm4ga2jDtG5nIMSR4buVaSANCkgwOiBwaMawxqFuZyBzYWkgc2FpIHPhu5Ega2jDtG5nIMSR4buVaQ0KSGE6IFBoxrDGoW5nIHNhaSBzYWkgc+G7kSB0aGF5IMSR4buVaQ0KDQpgYGB7cn0NCm5jdlRlc3QoZml0KQ0KYGBgDQoNCnAtdmFsdWUgPSAwLjE4LCBjw7MgdGjhu4Mga+G6v3QgbHXhuq1uIHBoxrDGoW5nIHNhaSBzYWkgc+G7kSBraMO0bmcgxJHhu5VpIHbhu5tpIG3hu6ljIMO9IG5naMSpYSA8IDAuMTgNCg0KIyMjIEdp4bqjIHRoaeG6v3QgNDogR2nhu69hIGPDoWMgYmnhur9uIMSR4buZYyBs4bqtcCBraMO0bmcgY8OzIG3hu5FpIHF1YW4gaOG7hyDEkWEgY+G7mW5nIHR1eeG6v24gaG/DoG4gaOG6o28NCg0KYGBge3J9DQp2aWYoZml0KQ0KYGBgDQoNCnZpZiA8IDEwIGNobyB0aOG6pXkgY8OhYyBiaeG6v24gxJHhu5ljIGzhuq1wIGtow7RuZyBjw7MgxJFhIGPhu5luZyB0dXnhur9uDQoNCiMjIEzhu7FhIGNo4buNbiBtw7QgaMOsbmgNClNhdSBraGkga2nhu4NtIMSR4buLbmggY8OhYyBnaeG6oyB0aGnhur90IGPhu6dhIG3DtCBow6xuaCBPTFMsIHRo4butIG5naGnhu4dtIHRyxrDhu51uZyBo4bujcCBi4buPIDIgYmnhur9uIEluY29tZSwgRnJvc3QgcmEga2jhu49pIG3DtCBow6xuaA0KDQpgYGB7cn0NCmZpdCA8LSBsbShNdXJkZXIgfiBQb3B1bGF0aW9uICsgSWxsaXRlcmFjeSwgZGF0YSA9IHN0YXRlcykNCnN1bW1hcnkoZml0KQ0KYGBgDQoNCmBgYHtyfQ0KcGFyKG1mcm93PWMoMiwyKSkNCnBsb3QoZml0KQ0KDQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNCg==