Phần 1: Tóm tắt cuốn sách: 2019_Generalized Linear Models With Examples in R

Cuốn sách này được biên soạn nhằm kết hợp phần giới thiệu dễ hiểu về các mô hình tuyến tính tổng quát (GLM) với phần giải thích kỹ lưỡng về mặt lý thuyết, phù hợp với trình độ cơ bản của sinh viên. Sách giả định sinh viên có kiến thức nền tảng về thống kê và giải tích. Nội dung sách bao gồm phần giới thiệu độc lập về R và tích hợp việc sử dụng R xuyên suốt, thông qua các ví dụ code R toàn diện và code hoàn chỉnh cho hầu hết các phân tích dữ liệu và nghiên cứu điển hình. Cách tiếp cận thực tế được phát triển thông qua các bộ dữ liệu thực và nhiều nghiên cứu điển hình.

Cuốn sách phù hợp với sinh viên tốt nghiệp ngành thống kê ở trình độ thạc sĩ hoặc tiến sĩ, sinh viên đại học nâng cao chuyên ngành thống kê ở Anh hoặc Úc, và sinh viên tâm lý học, sinh học và các ngành liên quan. Nói chung, sách dành cho bất kỳ ai muốn có kiến thức thực tế về GLM cùng với nền tảng lý thuyết vững chắc.

Chương 1: Statistical Model

1.1 Nội dung chính

Chương này giới thiệu mô hình thống kê là gì, gồm hai thành phần chính là hệ thống (systematic) và ngẫu nhiên (random). Mục đích là mô hình hóa sự thay đổi trung bình của biến phản hồi y theo các biến giải thích x1, x2,…, xn. Mô hình hồi quy tuyến tính (linear regression) được giới thiệu như một trường hợp đặc biệt của mô hình tuyến tính tổng quát (GLM). Ngoài ra, chương còn thảo luận về việc mã hóa biến định tính (dùng dummy variables), sự cần thiết của việc trực quan hóa dữ liệu, và tiêu chí để đánh giá mô hình (độ chính xác và tính đơn giản).

1.2 Mô hình và các công thức quan trọng

Mô hình hồi quy tuyến tính tổng quát: \[ E[y_i] = \mu_i = f(\beta_0 + \beta_1 x_{1i} + \cdots + \beta_p x_{pi}) \] với ui là giá trị kỳ vọng của yi và f là hàm liên kết (identity trong hồi quy tuyến tính thường).

Mã hóa biến phân loại sử dụng hàm factor() trong R. Với một biến có k mức, cần k-1 biến giả

1.3 Biểu đồ minh hoạ

Dữ liệu sử dụng là từ khảo sát lung capacity (lungcap) gồm các biến: Age, FEV, Ht, Gender, Smoke. Các biểu đồ bên dưới cho thấy mối quan hệ giữa FEV và từng biến:

# Tải và gọi dữ liệu
library(GLMsData)
data(lungcap)

# Thiết lập cấu hình hiển thị 2x2
par(mfrow=c(2,2))

# FEV vs Age
plot(FEV ~ Age, data=lungcap,
     xlab="Age (in years)", ylab="FEV (in L)",
     main="FEV vs age", xlim=c(0, 20), ylim=c(0, 6), las=1)

# FEV vs Height
plot(FEV ~ Ht, data=lungcap,
     xlab="Height (in inches)", ylab="FEV (in L)",
     main="FEV vs height", ylim=c(0, 6), las=1)

# FEV vs Gender
plot(FEV ~ Gender, data=lungcap,
     main="FEV vs gender", ylab="FEV (in L)",
     ylim=c(0, 6), las=1)

# FEV vs Smoking status
lungcap$Smoke <- factor(lungcap$Smoke, levels=c(0,1), labels=c("Non-smoker","Smoker"))
plot(FEV ~ Smoke, data=lungcap,
     main="FEV vs Smoking status", ylab="FEV (in L)",
     xlab="Smoking status", ylim=c(0, 6), las=1)

Chương 2: Linear Regression Models

2.1 Nội dung chính

Chương này trình bày chi tiết mô hình hồi quy tuyến tính — nền tảng của hầu hết các mô hình thống kê. Mô hình hồi quy tuyến tính gồm hai thành phần:

\[ \begin{aligned} \text{Thành phần ngẫu nhiên:} &\quad \text{Var}(y_i) = \sigma^2 / w_i \\ \text{Thành phần hệ thống:} &\quad \mu_i = \beta_0 + \sum_{j=1}^p \beta_j x_{ji} \\ \text{Với:} &\quad E[y_i] = \mu_i = \beta_0 + \beta_1 x_{1i} + \cdots + \beta_p x_{pi} \end{aligned} \]

2.2 Mô hình hồi quy tuyến tính nhiều biến

fit <- lm(log(FEV) ~ Age + Ht + Gender + Smoke, data = lungcap)
plot(log(FEV) ~ Age, data=lungcap)
abline(coef(fit)[1], coef(fit)[2], col="blue", lwd=2)

Kiểm định giả thuyết Ho: Bj= 0 dùng kiểm định t \[ t = \frac{\hat{\beta}_j}{se(\hat{\beta}_j)} \]

Khoảng tin cậy 100(1-α)% cho Bj: \[ \hat{\beta}_j \pm t^*_{\alpha/2, \, n - p'} \cdot se(\hat{\beta}_j) \]

step(fit, direction="both")               # Stepwise chọn mô hình
## Start:  AIC=-2516.58
## log(FEV) ~ Age + Ht + Gender + Smoke
## 
##          Df Sum of Sq    RSS     AIC
## <none>                13.734 -2516.6
## - Smoke   1    0.1027 13.836 -2513.7
## - Gender  1    0.1325 13.866 -2512.3
## - Age     1    1.0323 14.766 -2471.2
## - Ht      1   13.7485 27.482 -2064.9
## 
## Call:
## lm(formula = log(FEV) ~ Age + Ht + Gender + Smoke, data = lungcap)
## 
## Coefficients:
## (Intercept)          Age           Ht      GenderM  SmokeSmoker  
##    -1.94400      0.02339      0.04280      0.02932     -0.04607
extractAIC(fit)                           # AIC của mô hình
## [1]     5.000 -2516.575

Kết quả mô hình (Output từ R) Hệ số hồi quy:

Intercept: -1.94400

Age: 0.02339 (tăng 1 tuổi → log(FEV) tăng khoảng 0.023)

Ht (chiều cao): 0.04280

GenderM: 0.02932 (nam cao hơn nữ)

SmokeSmoker: -0.04607 (hút thuốc làm giảm FEV)

Bảng AIC dùng để so sánh các mô hình khi loại bỏ từng biến → cho thấy biến Ht là quan trọng nhất (tăng AIC nhiều nhất khi bị loại).

Chương 3: Linear Regression Models: Diagnostics and Model-Building

3.1 Nội dung chính:

Chương này tập trung vào việc kiểm tra các giả định của mô hình hồi quy tuyến tính (linearity, constant variance, normality, independence) thông qua phần dư và giá trị đòn bẩy (leverage). Nó cũng giới thiệu các phương pháp phát hiện điểm dị biệt (outliers), các quan sát ảnh hưởng mạnh (influential points), và cách khắc phục thông qua biến đổi biến (transformation), spline hoặc loại bỏ quan sát không phù hợp. Ngoài ra, chương còn đề cập đến vấn đề đa cộng tuyến (collinearity) và mô hình hóa hiệu quả.

3.2 Biểu đồ phần dư kiểm tra tuyến tính và phương sai không đổi

# Giả sử bạn đã có mô hình hồi quy:
fit <- lm(FEV ~ Ht + Gender + Smoke, data = lungcap)

# Vẽ biểu đồ phần dư chuẩn hóa theo giá trị dự đoán
plot(fit$fitted.values, rstandard(fit),
     main = "Residuals vs Fitted",
     xlab = "Fitted Values", ylab = "Standardized Residuals")
abline(h = 0, col = "red")

Ý nghĩa: Nếu mô hình hợp lý, các điểm sẽ phân bố ngẫu nhiên xung quanh đường 0 mà không có hình dạng cụ thể.

Chương 4: Beyond Linear Regression: The Method of Maximum Likelihood

4.1 Nội dung chính:

Chương này giới thiệu phương pháp ước lượng hợp lý tối đa (Maximum Likelihood Estimation – MLE). Đây là cơ sở toán học cho việc ước lượng trong GLM, đặc biệt khi dữ liệu không còn tuân theo phân phối chuẩn như trong hồi quy tuyến tính. Tác giả trình bày khái niệm likelihood, score function, Fisher information, và cách tìm MLE bằng đạo hàm của hàm log-likelihood.

4.2 Hàm log-likelihood cho phân phối chuẩn

logLik_normal <- function(mu, sigma, y) {
  n <- length(y)
  -n/2*log(2*pi*sigma^2) - sum((y - mu)^2) / (2*sigma^2)
}

y <- c(2.3, 2.5, 2.1, 2.6)
logLik_normal(mean(y), sd(y), y)
## [1] 0.8493247

Chương 5: Generalized Linear Models: Structure

5.1 Nội dung chính

Nội dung chính: GLM gồm 3 phần:

Thành phần ngẫu nhiên: phân phối thuộc họ hàm mũ (exponential family).

Thành phần hệ thống: predictor tuyến tính (η = Xβ).

Hàm liên kết (link): nối E(y) = μ với η.

Các phân phối phổ biến:

Normal, Binomial, Poisson, Gamma, Inverse Gaussian…

Giá trị trung bình và phương sai:

E[y] = μ = dκ/dθ

Var[y] = φV(μ) GLM linh hoạt hơn hồi quy tuyến tính vì chọn riêng phân phối và hàm liên kết, giúp phù hợp bản chất dữ liệu hơn

5.2 Ví dụ mô hình GLM với phân phối Poisson

Xây dựng một mô hình GLM với phân phối Poisson để dự đoán biến y theo biến độc lập x. Hàm liên kết là log.

data <- data.frame(y = c(2, 3, 4), x = c(1, 2, 3))
fit <- glm(y ~ x, family = poisson(link = "log"), data = data)
summary(fit)
## 
## Call:
## glm(formula = y ~ x, family = poisson(link = "log"), data = data)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)
## (Intercept)   0.3811     0.9912   0.384    0.701
## x             0.3397     0.4200   0.809    0.419
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 0.6795961  on 2  degrees of freedom
## Residual deviance: 0.0066234  on 1  degrees of freedom
## AIC: 12.878
## 
## Number of Fisher Scoring iterations: 3

Kết quả chính:

Hệ số intercept ≈ 0.81 và x ≈ 0.34.

Tuy nhiên, p-value của hệ số x là 0.419, nên biến này không có ý nghĩa thống kê.

Residual deviance rất nhỏ (≈ 0.0068), cho thấy mô hình khớp dữ liệu rất tốt.

Kết luận: Mô hình có thể đang bị overfit (vì số lượng quan sát nhỏ), hoặc biến x thực sự không ảnh hưởng đến y

Chương 6: Generalized Linear Models: Estimation

6.1 Nội dung chính:

Chương trình bày cách ước lượng tham số β và φ trong GLM:

Xây dựng phương trình score và ma trận thông tin (information matrix).

Sử dụng thuật toán IRLS (Iteratively Reweighted Least Squares) để tìm β̂.

Độ lệch (Deviance) đo lường mức sai lệch còn lại của mô hình.

Ma trận chuẩn hóa và sai số chuẩn của β̂.

Ước lượng tham số phân tán φ: theo deviance, Pearson hoặc hồ sơ hợp lý.

Quan trọng: GLM được fit tương tự hồi quy tuyến tính nhờ IRLS nên kế thừa nhiều công cụ như leverage, Cook’s distance…

6.2 Minh hoạ độ lệch (Deviance)

Sử dụng GLM với phân phối Poisson để kiểm tra độ phù hợp mô hình thông qua residual deviance và null deviance.

# Tạo bộ dữ liệu mô phỏng
data <- data.frame(
  y = c(2, 3, 4, 6, 8),
  x = c(1, 2, 3, 4, 5)
)

# Fit mô hình GLM với phân phối Poisson và hàm liên kết log
fit <- glm(y ~ x, family = poisson(link = "log"), data = data)

# Tóm tắt kết quả ước lượng hệ số β
summary(fit)
## 
## Call:
## glm(formula = y ~ x, family = poisson(link = "log"), data = data)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)  
## (Intercept)   0.3847     0.6158   0.625   0.5322  
## x             0.3423     0.1586   2.158   0.0309 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 5.028206  on 4  degrees of freedom
## Residual deviance: 0.018205  on 3  degrees of freedom
## AIC: 20.485
## 
## Number of Fisher Scoring iterations: 3
# Tính độ lệch (deviance) của mô hình
deviance(fit)
## [1] 0.01820542
# So sánh với null deviance (mô hình chỉ có intercept)
fit$null.deviance
## [1] 5.028206

Kết quả chính:

Null deviance ≈ 5.03: độ lệch của mô hình chỉ có intercept.

Residual deviance ≈ 0.018: mô hình có biến x đã giải thích hầu hết biến thiên.

p-value cho hệ số x = 0.0309 < 0.05 ⇒ x có ý nghĩa thống kê.

Kết luận: Biến x là biến giải thích có ý nghĩa, và mô hình phù hợp tốt với dữ liệu.

Chương 7: Generalized Linear Models: Inference

7.1 Nội dung chính:

Chương này hướng dẫn cách suy luận thống kê trong GLMs:

Ước lượng khoảng tin cậy (confidence intervals) cho hệ số,

Kiểm định Wald, kiểm định likelihood ratio, score test,

So sánh mô hình lồng (nested models),

AIC/BIC để so sánh mô hình không lồng.

7.2 So sánh mô hình với likehood ratio test

fit1 <- glm(y ~ 1, family = poisson, data = data)
fit2 <- glm(y ~ x, family = poisson, data = data)
anova(fit1, fit2, test = "Chisq")
## Analysis of Deviance Table
## 
## Model 1: y ~ 1
## Model 2: y ~ x
##   Resid. Df Resid. Dev Df Deviance Pr(>Chi)  
## 1         4     5.0282                       
## 2         3     0.0182  1     5.01   0.0252 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Chương 8: Generalized Linear Models: Diagnostics

8.1 Nội dung chính:

Mục tiêu là phát hiện và xử lý vi phạm giả định:

Các giả định cần kiểm tra: phân phối phù hợp, không có outliers, hàm liên kết đúng, tuyến tính, phương sai đúng, độc lập.

Các loại phần dư: Pearson, deviance, quantile (đặc biệt hữu ích cho dữ liệu rời rạc).

Đánh giá ảnh hưởng: leverage, Cook’s distance, dfbetas…

Chẩn đoán đồ thị: biểu đồ phần dư, Q-Q, plot residuals vs fitted.

Khắc phục: biến đổi biến, thêm spline, mô hình quasi, xử lý collinearity

8.2: Minh họa residuals trong GLM (với warpbreaks)

mod <- glm(breaks ~ wool + tension, data = warpbreaks, family = poisson)
par(mfrow = c(2, 2))
plot(mod)

Chương 9: Models for Proportions: Binomial GLMs

9.1 Nội dung chính:

Chương này mô hình hóa dữ liệu nhị phân hoặc tỷ lệ bằng GLM với phân phối nhị thức:

Link function phổ biến: logit, probit, complementary log-log.

Giải thích và ước lượng odds ratio, ED50.

Kiểm tra overdispersion → dùng quasi-binomial nếu cần.

Có ví dụ cho cả dữ liệu tỉ lệ (success/trials) và nhị phân (0/1).

9.2 Minh họa: Mô hình logit cho dữ liệu nhị phân

Xây dựng một mô hình hồi quy logistic để dự đoán xác suất success dựa trên x.

data <- data.frame(
  success = c(1, 0, 1, 1, 0),
  x = c(10, 12, 14, 16, 18)
)
fit <- glm(success ~ x, family = binomial(link = "logit"), data = data)
summary(fit)
## 
## Call:
## glm(formula = success ~ x, family = binomial(link = "logit"), 
##     data = data)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)
## (Intercept)   3.5200     5.1061   0.689    0.491
## x            -0.2197     0.3498  -0.628    0.530
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 6.7301  on 4  degrees of freedom
## Residual deviance: 6.3024  on 3  degrees of freedom
## AIC: 10.302
## 
## Number of Fisher Scoring iterations: 4

Kết quả chính:

Intercept ≈ 3.52, hệ số x ≈ -0.22 nhưng không có ý nghĩa thống kê (p = 0.530).

Residual deviance ≈ 6.3, không khác biệt nhiều so với null deviance.

Kết luận: Biến x không giải thích tốt xác suất thành công. Có thể cần thêm biến hoặc thay đổi mô hình.

Chương 10: Models for Counts: Poisson and Negative Binomial GLMs

10.1 Nội dung chính:

Áp dụng GLM cho dữ liệu đếm:

Poisson GLM: giả định E(y) = Var(y).

Negative Binomial GLM: cho phép Var(y) > E(y) → khắc phục overdispersion.

Sử dụng log-link phổ biến.

Mô hình log-linear cho bảng chéo (contingency tables).

Trình bày Simpson’s paradox và cách kiểm soát bằng biến phân tầng.

10.2 Minh hoạ: So sánh Poisson vs Negative Binomial

So sánh hai mô hình: một với Poisson và một với Negative Binomial để kiểm tra hiện tượng

library(MASS)
counts <- c(1, 2, 4, 7, 12, 18)
x <- c(1, 2, 3, 4, 5, 6)
poisson_model <- glm(counts ~ x, family = poisson)
nb_model <- glm.nb(counts ~ x)
## Warning in theta.ml(Y, mu, sum(w), w, limit = control$maxit, trace =
## control$trace > : iteration limit reached

## Warning in theta.ml(Y, mu, sum(w), w, limit = control$maxit, trace =
## control$trace > : iteration limit reached
summary(poisson_model)
## 
## Call:
## glm(formula = counts ~ x, family = poisson)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -0.2713     0.5600  -0.484    0.628    
## x             0.5362     0.1114   4.812 1.49e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 29.46297  on 5  degrees of freedom
## Residual deviance:  0.26899  on 4  degrees of freedom
## AIC: 25.03
## 
## Number of Fisher Scoring iterations: 3
summary(nb_model)
## 
## Call:
## glm.nb(formula = counts ~ x, init.theta = 1320612.356, link = log)
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -0.2713     0.5600  -0.484    0.628    
## x             0.5362     0.1114   4.812 1.49e-06 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(1320612) family taken to be 1)
## 
##     Null deviance: 29.46281  on 5  degrees of freedom
## Residual deviance:  0.26899  on 4  degrees of freedom
## AIC: 27.03
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  1320612 
##           Std. Err.:  127812204 
## Warning while fitting theta: iteration limit reached 
## 
##  2 x log-likelihood:  -21.03

Cả hai mô hình có hệ số gần giống nhau.

Residual deviance ≈ 0.27 ở cả hai mô hình.

AIC hơi khác: Poisson = 25.03, NB = 27.03.

Kết luận: Dù nghi ngờ overdispersion, Poisson vẫn hoạt động tốt và không cần thiết chuyển sang Negative Binomial trong trường hợp này. Tuy nhiên, cảnh báo “iteration limit reached” cho thấy có thể cần điều chỉnh kỹ thuật fitting. ## Chương 11: Positive Continuous Data: Gamma and Inverse Gaussian GLMs ### 11.1 Nội dung chính: Chương này phù hợp với dữ liệu dương như chi phí, thời gian, tuổi thọ:

Gamma GLM: Var(y) = μ²

Inverse Gaussian GLM: Var(y) = μ³

Link functions: log, identity, inverse.

Cách chọn phân phối phù hợp tùy theo độ lệch và phân tán của dữ liệu.

11.2 Gamma GLM với hàm liên kết log

Mô hình hóa một biến dương liên tục (y) theo biến x bằng mô hình GLM với phân phối Gamma.

y <- c(2.1, 3.4, 4.0, 5.5)
x <- c(1, 2, 3, 4)
fit <- glm(y ~ x, family = Gamma(link = "log"))
summary(fit)
## 
## Call:
## glm(formula = y ~ x, family = Gamma(link = "log"))
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)  
## (Intercept)  0.50678    0.11949   4.241   0.0513 .
## x            0.30388    0.04363   6.965   0.0200 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Gamma family taken to be 0.009518036)
## 
##     Null deviance: 0.460536  on 3  degrees of freedom
## Residual deviance: 0.018323  on 2  degrees of freedom
## AIC: 5.9245
## 
## Number of Fisher Scoring iterations: 4

Kết quả chính:

Cả intercept và hệ số x đều có ý nghĩa thống kê (p < 0.05).

Residual deviance rất nhỏ (≈ 0.018).

Dispersion parameter rất nhỏ ≈ 0.0095 → mô hình khớp rất tốt.

Kết luận: Biến x ảnh hưởng tích cực đến y, và Gamma GLM là lựa chọn phù hợp cho dữ liệu dương lệch phải.

Chương 12: Tweedie GLMs

12.1 Nội dung chính:

Phân phối Tweedie là họ phân phối liên tục mà chứa:

Normal (p=0), Poisson (p=1), Gamma (p=2).

Compound Poisson-Gamma (1 < p < 2) → thường gặp trong bảo hiểm.

Cho phép dữ liệu vừa có nhiều số 0 vừa có giá trị dương.

Cần ước lượng chỉ số p, dùng gói tweedie và statmod trong R để fit mô hình.

12.2: Minh họa mô hình Tweedie GLM

Áp dụng mô hình Tweedie cho dữ liệu có giá trị bằng 0 và liên tục dương – rất phổ biến trong bảo hiểm, ví dụ: tổn thất do tai nạn.

# Cài gói cần thiết nếu chưa có
if (!require("statmod")) install.packages("statmod")
## Loading required package: statmod
## Warning: package 'statmod' was built under R version 4.3.3
if (!require("tweedie")) install.packages("tweedie")
## Loading required package: tweedie
## Warning: package 'tweedie' was built under R version 4.3.3
# Nạp gói
library(statmod)
library(tweedie)

# Tạo dữ liệu mô phỏng: có cả số 0 và số dương
set.seed(123)
n <- 100
x <- runif(n, 0, 10)
mu <- exp(1 + 0.2 * x)
y <- rtweedie(n = n, mu = mu, phi = 1, power = 1.5)  # Tweedie với p = 1.5

data <- data.frame(y = y, x = x)

# Fit mô hình Tweedie GLM
model <- glm(y ~ x, family = tweedie(var.power = 1.5, link.power = 0), data = data)

# Tóm tắt kết quả
summary(model)
## 
## Call:
## glm(formula = y ~ x, family = tweedie(var.power = 1.5, link.power = 0), 
##     data = data)
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  1.10538    0.12796   8.639 1.08e-13 ***
## x            0.18435    0.02011   9.169 7.70e-15 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Tweedie family taken to be 0.8959382)
## 
##     Null deviance: 164.192  on 99  degrees of freedom
## Residual deviance:  84.748  on 98  degrees of freedom
## AIC: NA
## 
## Number of Fisher Scoring iterations: 4
# Vẽ fitted vs observed
plot(data$x, data$y, pch = 16, col = "grey", main = "Tweedie GLM Fit")
lines(data$x, fitted(model), col = "blue", lwd = 2)

## Chương 13: Extra Problems

13.1 Nội dung chính:

Chương 13 không giới thiệu lý thuyết mới, mà là bộ sưu tập các bài tập mở rộng yêu cầu người học:

Tự lựa chọn mô hình phù hợp cho nhiều loại dữ liệu.

Xác định đúng phân phối và link function.

Thực hiện chẩn đoán mô hình (diagnostics).

Giải thích kết quả mô hình theo ngữ cảnh nghiên cứu.

Các bài tập được thiết kế để tổng hợp kiến thức từ các chương trước như:

Nhị thức (Binomial)

Poisson / Negative Binomial

Gamma / Inverse Gaussian

Tweedie

Biến đổi dữ liệu, phát hiện dị biệt, mô hình tương tác…

Phần 2: Thống kê mô tả các biến trong tập Supermarket Transactions.csv

2.1 Tổng quan dữ liệu

Supermarket Transactions là bộ dữ liệu ghi nhận các giao dịch mua hàng tại siêu thị, được tổ chức theo từng đơn hàng. Quy mô dữ liệu gồm 14.059 quan sát (dòng) và 16 biến (cột), phản ánh khối lượng và đa dạng thông tin được thu thập.

Sau khi loại bỏ cột chỉ mục dư thừa, cấu trúc dữ liệu được kiểm tra lại để đảm bảo tính nhất quán. Đáng chú ý, biến Purchase Date chứa thông tin ngày mua hàng được định dạng ngày tháng (Date), kéo dài từ cuối năm 2007 đến cuối năm 2009, cho thấy phạm vi thời gian quan sát gần 2 năm. Việc chuyển cột này về định dạng ngày giúp hỗ trợ hiệu quả cho các phân tích theo chuỗi thời gian.

Cấu trúc bộ dữ liệu bao gồm các biến:

1 Purchase Date – Ngày mua hàng

2 Customer ID – Mã định danh khách hàng

3 Gender – Giới tính: F (Female) là nữ, M (Male) là nam

4 Marital Status – Tình trạng hôn nhân: S (Single) là độc thân, M (Married) là đã kết hôn

5 Homeowner – Tình trạng sở hữu nhà: Y (Yes) là có nhà, N (No) là chưa có nhà

6 Children – Số lượng con cái

7 Annual Income – Thu nhập hằng năm

8 City – Thành phố cư trú

9 State or Province – Mã bang hoặc tỉnh

10 Country – Quốc gia

11 Product Family – Nhóm sản phẩm chính:

12 Food: Thực phẩm

13 Drink: Đồ uống

14 Non-Consumable: Hàng không tiêu dùng

15 Product Department – Phân nhóm sản phẩm chi tiết hơn

16 Product Category – Danh mục sản phẩm cụ thể

17 Units Sold – Số lượng sản phẩm bán được

18 Revenue – Doanh thu thu được từ đơn hàng

2.2 Thống kê mô tả

2.2.1 Purchase date

long <- read.csv(file.choose(), header = T) # load data
long$PurchaseDate <- as.Date(long$PurchaseDate)
long$Year <- format(long$PurchaseDate, "%Y")
long$Month <- format(long$PurchaseDate, "%Y-%m")
library(ggplot2)
ggplot(long, aes(x = as.Date(paste0(Year, "-", format(as.Date(long$PurchaseDate), "%m"), "-01")))) +
  geom_histogram(binwidth = 30, fill="yellow", color="white") +
  labs(title="Số giao dịch theo tháng", x="Tháng", y="Số giao dịch") +
  theme_minimal()
## Warning: Use of `long$PurchaseDate` is discouraged.
## ℹ Use `PurchaseDate` instead.

Phân tích theo thời gian cho thấy năm 2009 ghi nhận số lượng giao dịch cao nhất với 9.332 đơn hàng, trong khi năm 2008 có khoảng 4.692 đơn. Năm 2007 chỉ đóng góp một phần nhỏ (35 đơn hàng) do dữ liệu bắt đầu từ cuối năm. Điều này cho thấy tập dữ liệu chủ yếu tập trung vào giai đoạn 2008–2009.

Biểu đồ tần suất giao dịch theo tháng trong khoảng thời gian này cho thấy xu hướng tăng dần trong năm 2009, đặc biệt nổi bật từ tháng 5 đến tháng 10, với số lượng giao dịch ở mức cao. Đáng chú ý, giai đoạn cuối năm 2008 và đầu năm 2009 có mức tăng vọt, có thể liên quan đến các chương trình khuyến mãi theo mùa lễ hội hoặc sự gia tăng lượng khách hàng.

Thông tin về thời gian không chỉ phản ánh độ bao phủ thời gian của dữ liệu, mà còn gợi ý về xu hướng tăng trưởng tại điểm bán – khả năng đến từ việc mở rộng hệ thống bán lẻ hoặc sự gia tăng nhu cầu tiêu dùng trong giai đoạn này.

2.2.2 Custom ID

total_customers <- length(unique(data$CustomerID))
total_transactions <- nrow(long)
avg_trans_per_cust <- total_transactions / total_customers
list(total_transactions=total_transactions,
     total_customers=total_customers,
     avg_trans_per_customer=round(avg_trans_per_cust,2))
## $total_transactions
## [1] 14059
## 
## $total_customers
## [1] 0
## 
## $avg_trans_per_customer
## [1] Inf

Tổng cộng có 14059 giao dịch được ghi nhận từ 5404 khách hàng khác nhau trong khoảng thời gian gần 2 năm. Trung bình, mỗi khách hàng thực hiện khoảng r avg_trans_per_cust giao dịch.

Điều này cho thấy phần lớn khách hàng chỉ mua hàng 1–2 lần, phản ánh mức độ đa dạng cao trong tập khách hàng. Một số trường hợp có thể là khách hàng quay lại, cho thấy dấu hiệu ban đầu của mức độ trung thành, tuy nhiên dữ liệu chưa cung cấp đầy đủ thông tin để đánh giá trực tiếp yếu tố này.

Tỷ lệ số lượng khách hàng cao so với tổng số giao dịch cho thấy thị trường có sự tham gia của nhiều người mua khác nhau, đặc trưng cho một hệ thống bán lẻ có phạm vi tiếp cận rộng và đa dạng về đối tượng khách hàng.

2.2.3 Gender

thong_ke_dinh_tinh <- function(data, var_name) {
  # Check if variable exists in data
  if (!var_name %in% names(data)) {
    stop("Variable does not exist in data")
  }
  
  # Convert to factor if needed
  variable <- as.factor(data[[var_name]])
  
  # freq
  freq <- table(variable)
  percent <- prop.table(freq) * 100
  
  # result
  result <- data.frame(
    Gia_tri = names(freq),
    Tan_so = as.vector(freq),
    Tan_suat = round(as.vector(percent), 2)
  )
  return(result)
}
library(ggplot2)
thong_ke_dinh_tinh(long, 'Gender')
##   Gia_tri Tan_so Tan_suat
## 1       F   7170       51
## 2       M   6889       49
ggplot(data = long, aes(x = "", fill = Gender)) +
  geom_bar(width = 1) +
  coord_polar("y") +
  labs(title = "Giới tính") +
  theme_void()

Phân bố giới tính trong bộ dữ liệu khá đồng đều, với sự chênh lệch nhỏ giữa hai nhóm giới tính: nữ (F) và nam (M).

Điều này cho thấy dữ liệu có tính đại diện giới tương đối tốt, giúp giảm thiểu nguy cơ thiên lệch do giới tính trong quá trình phân tích và suy luận thống kê.

2.2.4 MaritalStatus

thong_ke_dinh_tinh(long,'MaritalStatus')
##   Gia_tri Tan_so Tan_suat
## 1       M   6866    48.84
## 2       S   7193    51.16

Gần một nửa khách hàng là độc thân, còn lại là đã kết hôn.

Tỷ lệ này cũng tương đối cân bằng, giúp so sánh hành vi theo tình trạng hôn nhân mà không lo thiên lệch dữ liệu.

ggplot(long, aes(x = MaritalStatus, fill=MaritalStatus)) +
  geom_bar(color="white") +
  scale_fill_manual(values=c("yellow","skyblue")) +
  labs(title="Phân phối khách hàng theo Tình trạng hôn nhân", 
       x="Tình trạng", y="Số lượng giao dịch") +
  theme_minimal()

2.2.5 Homeowner

thong_ke_dinh_tinh(long, 'Homeowner')
##   Gia_tri Tan_so Tan_suat
## 1       N   5615    39.94
## 2       Y   8444    60.06

Phần lớn khách hàng là người sở hữu nhà (chiếm ~60%).

Điều này có thể ảnh hưởng đến thói quen chi tiêu hoặc ưu tiên mua sắm, là một đặc điểm quan trọng để phân khúc khách hàng.

ggplot(data = long, aes(x = Homeowner)) +
  geom_bar(fill = "yellow") +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
  labs(title = "Sở hữu nhà",
       x = "Có hoặc không", y = "Tần số") +
  theme_minimal()
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

2.2.6 Children

Children chỉ số con mà khách hàng có, biến này là 1 biến định lượng vì các giá trị của nó có thể so sánh với nhau.

library(skimr)
## Warning: package 'skimr' was built under R version 4.3.3
skim(long$Children)
Data summary
Name long$Children
Number of rows 14059
Number of columns 1
_______________________
Column type frequency:
numeric 1
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
data 0 1 2.53 1.49 0 1 3 4 5 ▇▆▆▆▃

Trung bình 2.53 và trung vị 3 cho thấy dữ liệu có xu hướng phân bố đều.

Không có dấu hiệu lệch nghiêm trọng hoặc có giá trị ngoại lệ bất thường.

ggplot(long, aes(x = as.factor(Children))) +
  geom_bar(fill = "yellow") +
  geom_text(stat = "count", aes(label = ..count..), vjust = -0.5) +
  labs(x = "Số con", y = "Số khách hàng", title = "Phân bố số con của khách hàng")

2.2.7 AnnualIncome

thong_ke_dinh_tinh(long, 'AnnualIncome')
##         Gia_tri Tan_so Tan_suat
## 1   $10K - $30K   3090    21.98
## 2 $110K - $130K    643     4.57
## 3 $130K - $150K    760     5.41
## 4       $150K +    273     1.94
## 5   $30K - $50K   4601    32.73
## 6   $50K - $70K   2370    16.86
## 7   $70K - $90K   1709    12.16
## 8  $90K - $110K    613     4.36

$30K - $50K chiếm hơn 1/3 (32.73%) tổng số quan sát → là nhóm đông nhất. Kết hợp với $10K - $30K, ta có khoảng 55% người thuộc nhóm dưới $50K cho thấy mức thu nhập trung bình hoặc thấp là phổ biến.

Nhóm thu nhập cao (trên $90K) Gồm $90K - $110K, $110K - $130K, $130K - $150K, $150K+. Tổng tần suất chỉ khoảng 16.28% cho thấy nhóm thu nhập cao khá nhỏ.

ggplot(long, aes(x = AnnualIncome)) +
  geom_bar(fill = "yellow", color = "black") +
  labs(x = "Khoảng thu nhập", y = "Tần suất", title = "Biểu đồ tần suất thu nhập hàng năm") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

2.2.8 City

thong_ke_dinh_tinh(long,'City')
##          Gia_tri Tan_so Tan_suat
## 1       Acapulco    383     2.72
## 2     Bellingham    143     1.02
## 3  Beverly Hills    811     5.77
## 4      Bremerton    834     5.93
## 5        Camacho    452     3.22
## 6    Guadalajara     75     0.53
## 7        Hidalgo    845     6.01
## 8    Los Angeles    926     6.59
## 9         Merida    654     4.65
## 10   Mexico City    194     1.38
## 11       Orizaba    464     3.30
## 12      Portland    876     6.23
## 13         Salem   1386     9.86
## 14    San Andres    621     4.42
## 15     San Diego    866     6.16
## 16 San Francisco    130     0.92
## 17       Seattle    922     6.56
## 18       Spokane    875     6.22
## 19        Tacoma   1257     8.94
## 20     Vancouver    633     4.50
## 21      Victoria    176     1.25
## 22   Walla Walla    160     1.14
## 23        Yakima    376     2.67

Salem có tần số cao nhất (1386 lần xuất hiện, chiếm 9.86). Tiếp theo là Tacoma (1257 lần, 8.94%).

Guadalajara có tần số thấp nhất (75 lần, chỉ 0.53%).

Nhìn chung, số lượng khách hàng ở các thành phố phân bố khá chênh lệch, điều này có thể dẫn đến những kết quả thiên lệch trong việc phân tích và so sánh giữa các thành phố.

ggplot(long, aes(x = City)) +
  geom_bar(fill = "yellow", color = "black") +
  labs(x = "Thành phố", y = "Tần số", title = "Biểu đồ tần số khách hàng ở các thành phố") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

2.2.9 StateorProvince

thong_ke_dinh_tinh(long,'StateorProvince')
##      Gia_tri Tan_so Tan_suat
## 1         BC    809     5.75
## 2         CA   2733    19.44
## 3         DF    815     5.80
## 4   Guerrero    383     2.72
## 5    Jalisco     75     0.53
## 6         OR   2262    16.09
## 7   Veracruz    464     3.30
## 8         WA   4567    32.48
## 9    Yucatan    654     4.65
## 10 Zacatecas   1297     9.23

WA (Washington) chiếm tỷ lệ áp đảo (32.48%, 4567 lần xuất hiện), CA (California) đứng thứ hai (19.44%, 2733 lần), OR (Oregon) đứng thứ ba (16.09%, 2262 lần). => Ba bang này thuộc Tây Bắc Thái Bình Dương của Hoa Kỳ chiếm tổng cộng 68.01% dữ liệu

Các tỉnh Mexico (DF, Guerrero, Jalisco, Veracruz, Yucatan, Zacatecas) có tần suất thấp hơn nhiều. Tổng cộng các tỉnh Mexico chỉ chiếm khoảng 26.23% dữ liệu

Kết quả này cho thấy sự tập trung địa lý rõ rệt trong tập dữ liệu, với phần lớn dữ liệu đến từ ba bang phía Tây Bắc Hoa Kỳ.

ggplot(long, aes(x = StateorProvince)) +
  geom_bar(fill = "yellow", color = "black") +
  labs(x = "Bang", y = "Tần số", title = "Biểu đồ tần số khách hàng ở các bang") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

2.2.10 Country

thong_ke_dinh_tinh(long,'Country')
##   Gia_tri Tan_so Tan_suat
## 1  Canada    809     5.75
## 2  Mexico   3688    26.23
## 3     USA   9562    68.01

Dữ liệu cho thấy sự phân bố không đồng đều giữa các quốc gia, với Hoa Kỳ (USA) chiếm tỷ lệ áp đảo (68.01%, 9.562 lần xuất hiện), tiếp theo là Mexico (26.23%, 3.688 lần) và Canada (5.75%, 809 lần). Điều này cho thấy tập dữ liệu chủ yếu tập trung vào thị trường Mỹ, có thể do dữ liệu được thu thập từ nguồn ưu tiên các giao dịch hoặc sự kiện tại Hoa Kỳ.

ggplot(long, aes(x = Country)) +
  geom_bar(fill="yellow", color="white") +
  labs(title="Phân phối giao dịch theo quốc gia", x="Quốc gia", y="Số giao dịch") +
  theme_minimal()

2.2.11 ProductFamily

thong_ke_dinh_tinh(long,'ProductFamily')
##          Gia_tri Tan_so Tan_suat
## 1          Drink   1250     8.89
## 2           Food  10153    72.22
## 3 Non-Consumable   2656    18.89

Dữ liệu cho thấy sản phẩm thực phẩm (Food) chiếm tỷ lệ áp đảo (72.22%, 10.153 lần xuất hiện), trong khi nhóm đồ uống (Drink) chỉ chiếm 8.89% và sản phẩm không tiêu dùng (Non-Consumable) chiếm 18.89%. Sự chênh lệch rõ rệt này (Food gấp 8 lần Drink và gần 4 lần Non-Consumable) phản ánh trọng tâm của tập dữ liệu chủ yếu tập trung vào các sản phẩm thực phẩm.

ggplot(long, aes(x = ProductFamily, fill=ProductFamily)) +
  geom_bar(color="white") +
  labs(title="Số giao dịch theo Product Family", 
       x="Nhóm sản phẩm", y="Số giao dịch") +
  theme_minimal()

2.2.12 ProductDepartment

thong_ke_dinh_tinh(long,'ProductDepartment')
##                Gia_tri Tan_so Tan_suat
## 1  Alcoholic Beverages    356     2.53
## 2          Baked Goods    425     3.02
## 3         Baking Goods   1072     7.63
## 4            Beverages    680     4.84
## 5      Breakfast Foods    188     1.34
## 6         Canned Foods    977     6.95
## 7      Canned Products    109     0.78
## 8             Carousel     59     0.42
## 9             Checkout     82     0.58
## 10               Dairy    903     6.42
## 11                Deli    699     4.97
## 12                Eggs    198     1.41
## 13        Frozen Foods   1382     9.83
## 14  Health and Hygiene    893     6.35
## 15           Household   1420    10.10
## 16                Meat     89     0.63
## 17         Periodicals    202     1.44
## 18             Produce   1994    14.18
## 19             Seafood    102     0.73
## 20         Snack Foods   1600    11.38
## 21              Snacks    352     2.50
## 22       Starchy Foods    277     1.97

Tập trung vào thực phẩm tươi và đồ gia dụng: Hai nhóm Produce và Household chiếm tổng cộng gần 25% dữ liệu, cho thấy đây có thể là các mặt hàng chủ lực.

Sự thiếu cân đối rõ rệt: Trong khi một số nhóm như Produce chiếm tới 14.18% thì nhiều nhóm khác như Meat, Seafood lại chiếm chưa tới 1%.

ggplot(long, aes(x = ProductDepartment)) +
  geom_bar(fill = "yellow", color = "black") +
  labs(x = "Sản phẩm", y = "Tần số", title = "Bộ phận sản phẩm khách hàng mua") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

2.2.13 ProductCategory

thong_ke_dinh_tinh(long,'ProductCategory')
##                 Gia_tri Tan_so Tan_suat
## 1          Baking Goods    484     3.44
## 2     Bathroom Products    365     2.60
## 3         Beer and Wine    356     2.53
## 4                 Bread    425     3.02
## 5       Breakfast Foods    417     2.97
## 6               Candles     45     0.32
## 7                 Candy    352     2.50
## 8      Canned Anchovies     44     0.31
## 9          Canned Clams     53     0.38
## 10       Canned Oysters     35     0.25
## 11      Canned Sardines     40     0.28
## 12        Canned Shrimp     38     0.27
## 13          Canned Soup    404     2.87
## 14          Canned Tuna     87     0.62
## 15 Carbonated Beverages    154     1.10
## 16    Cleaning Supplies    189     1.34
## 17        Cold Remedies     93     0.66
## 18                Dairy    903     6.42
## 19        Decongestants     85     0.60
## 20               Drinks    135     0.96
## 21                 Eggs    198     1.41
## 22           Electrical    355     2.53
## 23      Frozen Desserts    323     2.30
## 24       Frozen Entrees    118     0.84
## 25                Fruit    765     5.44
## 26             Hardware    129     0.92
## 27        Hot Beverages    226     1.61
## 28              Hygiene    197     1.40
## 29     Jams and Jellies    588     4.18
## 30     Kitchen Products    217     1.54
## 31            Magazines    202     1.44
## 32                 Meat    761     5.41
## 33        Miscellaneous     42     0.30
## 34  Packaged Vegetables     48     0.34
## 35       Pain Relievers    192     1.37
## 36       Paper Products    345     2.45
## 37                Pizza    194     1.38
## 38     Plastic Products    141     1.00
## 39 Pure Juice Beverages    165     1.17
## 40              Seafood    102     0.73
## 41          Side Dishes    153     1.09
## 42          Snack Foods   1600    11.38
## 43            Specialty    289     2.06
## 44        Starchy Foods    277     1.97
## 45           Vegetables   1728    12.29

Dữ liệu cho thấy Snack Foods (11.38%) và Vegetables (12.29%) là hai danh mục phổ biến nhất, chiếm tổng cộng gần 1/4 dữ liệu. Các danh mục đáng chú ý khác bao gồm Dairy (6.42%), Fruit (5.44%), và Meat (5.41%), trong khi nhiều danh mục như đồ hải sản đóng hộp (Canned Oysters 0.25%, Canned Shrimp 0.27%) hoặc các sản phẩm ít phổ biến (Candles 0.32%, Miscellaneous 0.30%) có tần suất rất thấp. Sự phân bố này phản ánh tập trung chính vào các mặt hàng tiêu dùng nhanh và thực phẩm tươi sống, trong khi các sản phẩm đặc biệt hoặc ít phổ biến chỉ chiếm tỷ lệ nhỏ.

ggplot(long, aes(x = ProductCategory)) +
  geom_bar(fill = "yellow", color = "black") +
  labs(x = "Sản phẩm", y = "Tần số", title = "Danh mục sản phẩm khách hàng mua") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

2.2.14 UnitsSold

skim(long$UnitsSold)
Data summary
Name long$UnitsSold
Number of rows 14059
Number of columns 1
_______________________
Column type frequency:
numeric 1
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
data 0 1 4.08 1.17 1 3 4 5 8 ▁▃▇▂▁

Kết quả cho thấy trung bình 1 đơn mua sẽ mua khoảng 4.08 đơn vị sản phẩm, giá trị độ lệch chuẩn bằng 1.17 cho thấy mức độ phân tán tương đối thấp xung quanh giá trị trung bình.

Có thể thấy rằng có 50% giao dịch bán từ 3-5 đơn vị (khoảng tứ phân vị) và 75% giao dịch bán ≤5 đơn vị.

boxplot(long$UnitsSold, horizontal = TRUE, col = "yellow",
        main = "Phân phối UnitsSold", xlab = "Số đơn vị bán ra")

2.2.15 Revenue

skim(long$Revenue)
Data summary
Name long$Revenue
Number of rows 14059
Number of columns 1
_______________________
Column type frequency:
numeric 1
________________________
Group variables None

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
data 0 1 13 8.22 0.53 6.84 11.25 17.37 56.7 ▇▅▂▁▁

Biến Revenue (doanh thu) cho thấy một phân phối lệch phải rõ rệt với giá trị trung bình 13.00 và trung vị 11.25, phản ánh sự hiện diện của nhiều giao dịch có doanh thu cao kéo trung bình lên. Độ lệch chuẩn lớn (8.22) cùng khoảng biến thiên rộng (từ 0.53 đến 56.7) cho thấy mức độ chênh lệch đáng kể giữa các giao dịch.

boxplot(long$Revenue, horizontal = TRUE, col = "yellow",
        main = "Phân phối Revenue", xlab = "Doanh thu")

Boxplot này 1 lần nữa xác nhận rõ ràng phân phối lệch phải mạnh của biến Revenue. Các điểm riêng lẻ xuất hiện ở vùng 30-50, trùng khớp với giá trị tối đa 56.7 trong thống kê. Khoảng cách xa từ p75 (~17) đến các điểm này cho thấy chúng thực sự là các giá trị cực trị

LS0tDQp0aXRsZTogIk5oaeG7h20gduG7pSAxIg0KYXV0aG9yOiAiTWFpIFRow6BuaCBMb25nIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHRydWUNCiAgICBudW1iZXIgc2VjdGlvbjogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVIOiVNOiVTLCAlZCAtICVtIC0gJVknKWAiDQotLS0NCg0KPHN0eWxlPg0KYm9keSB7DQogIHRleHQtYWxpZ246IGp1c3RpZnk7DQp9DQo8L3N0eWxlPg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyBQaOG6p24gMTogVMOzbSB04bqvdCBjdeG7kW4gc8OhY2g6IDIwMTlfR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVscyBXaXRoIEV4YW1wbGVzIGluIFINCg0KQ3Xhu5FuIHPDoWNoIG7DoHkgxJHGsOG7o2MgYmnDqm4gc2/huqFuIG5o4bqxbSBr4bq/dCBo4bujcCBwaOG6p24gZ2nhu5tpIHRoaeG7h3UgZOG7hSBoaeG7g3UgduG7gSBjw6FjIG3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNKSB24bubaSBwaOG6p24gZ2nhuqNpIHRow61jaCBr4bu5IGzGsOG7oW5nIHbhu4EgbeG6t3QgbMO9IHRodXnhur90LCBwaMO5IGjhu6NwIHbhu5tpIHRyw6xuaCDEkeG7mSBjxqEgYuG6o24gY+G7p2Egc2luaCB2acOqbi4gU8OhY2ggZ2nhuqMgxJHhu4tuaCBzaW5oIHZpw6puIGPDsyBraeG6v24gdGjhu6ljIG7hu4FuIHThuqNuZyB24buBIHRo4buRbmcga8OqIHbDoCBnaeG6o2kgdMOtY2guIE7hu5lpIGR1bmcgc8OhY2ggYmFvIGfhu5NtIHBo4bqnbiBnaeG7m2kgdGhp4buHdSDEkeG7mWMgbOG6rXAgduG7gSBSIHbDoCB0w61jaCBo4bujcCB2aeG7h2Mgc+G7rSBk4bulbmcgUiB4dXnDqm4gc3Xhu5F0LCB0aMO0bmcgcXVhIGPDoWMgdsOtIGThu6UgY29kZSBSIHRvw6BuIGRp4buHbiB2w6AgY29kZSBob8OgbiBjaOG7iW5oIGNobyBo4bqndSBo4bq/dCBjw6FjIHBow6JuIHTDrWNoIGThu68gbGnhu4d1IHbDoCBuZ2hpw6puIGPhu6l1IMSRaeG7g24gaMOsbmguIEPDoWNoIHRp4bq/cCBj4bqtbiB0aOG7sWMgdOG6vyDEkcaw4bujYyBwaMOhdCB0cmnhu4NuIHRow7RuZyBxdWEgY8OhYyBi4buZIGThu68gbGnhu4d1IHRo4buxYyB2w6Agbmhp4buBdSBuZ2hpw6puIGPhu6l1IMSRaeG7g24gaMOsbmguDQoNCkN14buRbiBzw6FjaCBwaMO5IGjhu6NwIHbhu5tpIHNpbmggdmnDqm4gdOG7kXQgbmdoaeG7h3AgbmfDoG5oIHRo4buRbmcga8OqIOG7nyB0csOsbmggxJHhu5kgdGjhuqFjIHPEqSBob+G6t2MgdGnhur9uIHPEqSwgc2luaCB2acOqbiDEkeG6oWkgaOG7jWMgbsOibmcgY2FvIGNodXnDqm4gbmfDoG5oIHRo4buRbmcga8OqIOG7nyBBbmggaG/hurdjIMOaYywgdsOgIHNpbmggdmnDqm4gdMOibSBsw70gaOG7jWMsIHNpbmggaOG7jWMgdsOgIGPDoWMgbmfDoG5oIGxpw6puIHF1YW4uIE7Ds2kgY2h1bmcsIHPDoWNoIGTDoG5oIGNobyBi4bqldCBr4buzIGFpIG114buRbiBjw7Mga2nhur9uIHRo4bupYyB0aOG7sWMgdOG6vyB24buBIEdMTSBjw7luZyB24bubaSBu4buBbiB04bqjbmcgbMO9IHRodXnhur90IHbhu69uZyBjaOG6r2MuDQoNCiMjIENoxrDGoW5nIDE6IFN0YXRpc3RpY2FsIE1vZGVsDQojIyMgMS4xIE7hu5lpIGR1bmcgY2jDrW5oDQpDaMawxqFuZyBuw6B5IGdp4bubaSB0aGnhu4d1IG3DtCBow6xuaCB0aOG7kW5nIGvDqiBsw6AgZ8OsLCBn4buTbSBoYWkgdGjDoG5oIHBo4bqnbiBjaMOtbmggbMOgIGjhu4cgdGjhu5FuZyAoc3lzdGVtYXRpYykgdsOgIG5n4bqrdSBuaGnDqm4gKHJhbmRvbSkuIE3hu6VjIMSRw61jaCBsw6AgbcO0IGjDrG5oIGjDs2Egc+G7sSB0aGF5IMSR4buVaSB0cnVuZyBiw6xuaCBj4bunYSBiaeG6v24gcGjhuqNuIGjhu5NpIHkgdGhlbyBjw6FjIGJp4bq/biBnaeG6o2kgdGjDrWNoIHgxLCB4MiwuLi4sIHhuLiBNw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggKGxpbmVhciByZWdyZXNzaW9uKSDEkcaw4bujYyBnaeG7m2kgdGhp4buHdSBuaMawIG3hu5l0IHRyxrDhu51uZyBo4bujcCDEkeG6t2MgYmnhu4d0IGPhu6dhIG3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNKS4gTmdvw6BpIHJhLCBjaMawxqFuZyBjw7JuIHRo4bqjbyBsdeG6rW4gduG7gSB2aeG7h2MgbcOjIGjDs2EgYmnhur9uIMSR4buLbmggdMOtbmggKGTDuW5nIGR1bW15IHZhcmlhYmxlcyksIHPhu7EgY+G6p24gdGhp4bq/dCBj4bunYSB2aeG7h2MgdHLhu7FjIHF1YW4gaMOzYSBk4buvIGxp4buHdSwgdsOgIHRpw6p1IGNow60gxJHhu4MgxJHDoW5oIGdpw6EgbcO0IGjDrG5oICjEkeG7mSBjaMOtbmggeMOhYyB2w6AgdMOtbmggxJHGoW4gZ2nhuqNuKS4NCg0KIyMjIDEuMiBNw7QgaMOsbmggdsOgIGPDoWMgY8O0bmcgdGjhu6ljIHF1YW4gdHLhu41uZw0KDQpNw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggdOG7lW5nIHF1w6F0Og0KICQkIEVbeV9pXSA9IFxtdV9pID0gZihcYmV0YV8wICsgXGJldGFfMSB4X3sxaX0gKyBcY2RvdHMgKyBcYmV0YV9wIHhfe3BpfSkgJCQNCnbhu5tpIHVpIGzDoCBnacOhIHRy4buLIGvhu7MgduG7jW5nIGPhu6dhIHlpIHbDoCBmIGzDoCBow6BtIGxpw6puIGvhur90IChpZGVudGl0eSB0cm9uZyBo4buTaSBxdXkgdHV54bq/biB0w61uaCB0aMaw4budbmcpLg0KDQpNw6MgaMOzYSBiaeG6v24gcGjDom4gbG/huqFpIHPhu60gZOG7pW5nIGjDoG0gZmFjdG9yKCkgdHJvbmcgUi4gVuG7m2kgbeG7mXQgYmnhur9uIGPDsyBrIG3hu6ljLCBj4bqnbiBrLTEgYmnhur9uIGdp4bqjDQoNCiMjIyAxLjMgQmnhu4N1IMSR4buTIG1pbmggaG/huqEgDQpE4buvIGxp4buHdSBz4butIGThu6VuZyBsw6AgdOG7qyBraOG6o28gc8OhdCBsdW5nIGNhcGFjaXR5IChsdW5nY2FwKSBn4buTbSBjw6FjIGJp4bq/bjogQWdlLCBGRVYsIEh0LCBHZW5kZXIsIFNtb2tlLiBDw6FjIGJp4buDdSDEkeG7kyBiw6puIGTGsOG7m2kgY2hvIHRo4bqleSBt4buRaSBxdWFuIGjhu4cgZ2nhu69hIEZFViB2w6AgdOG7q25nIGJp4bq/bjoNCmBgYHtyfQ0KIyBU4bqjaSB2w6AgZ+G7jWkgZOG7ryBsaeG7h3UNCmxpYnJhcnkoR0xNc0RhdGEpDQpkYXRhKGx1bmdjYXApDQoNCiMgVGhp4bq/dCBs4bqtcCBj4bqldSBow6xuaCBoaeG7g24gdGjhu4sgMngyDQpwYXIobWZyb3c9YygyLDIpKQ0KDQojIEZFViB2cyBBZ2UNCnBsb3QoRkVWIH4gQWdlLCBkYXRhPWx1bmdjYXAsDQogICAgIHhsYWI9IkFnZSAoaW4geWVhcnMpIiwgeWxhYj0iRkVWIChpbiBMKSIsDQogICAgIG1haW49IkZFViB2cyBhZ2UiLCB4bGltPWMoMCwgMjApLCB5bGltPWMoMCwgNiksIGxhcz0xKQ0KDQojIEZFViB2cyBIZWlnaHQNCnBsb3QoRkVWIH4gSHQsIGRhdGE9bHVuZ2NhcCwNCiAgICAgeGxhYj0iSGVpZ2h0IChpbiBpbmNoZXMpIiwgeWxhYj0iRkVWIChpbiBMKSIsDQogICAgIG1haW49IkZFViB2cyBoZWlnaHQiLCB5bGltPWMoMCwgNiksIGxhcz0xKQ0KDQojIEZFViB2cyBHZW5kZXINCnBsb3QoRkVWIH4gR2VuZGVyLCBkYXRhPWx1bmdjYXAsDQogICAgIG1haW49IkZFViB2cyBnZW5kZXIiLCB5bGFiPSJGRVYgKGluIEwpIiwNCiAgICAgeWxpbT1jKDAsIDYpLCBsYXM9MSkNCg0KIyBGRVYgdnMgU21va2luZyBzdGF0dXMNCmx1bmdjYXAkU21va2UgPC0gZmFjdG9yKGx1bmdjYXAkU21va2UsIGxldmVscz1jKDAsMSksIGxhYmVscz1jKCJOb24tc21va2VyIiwiU21va2VyIikpDQpwbG90KEZFViB+IFNtb2tlLCBkYXRhPWx1bmdjYXAsDQogICAgIG1haW49IkZFViB2cyBTbW9raW5nIHN0YXR1cyIsIHlsYWI9IkZFViAoaW4gTCkiLA0KICAgICB4bGFiPSJTbW9raW5nIHN0YXR1cyIsIHlsaW09YygwLCA2KSwgbGFzPTEpDQoNCg0KYGBgDQoNCiMjIENoxrDGoW5nIDI6IExpbmVhciBSZWdyZXNzaW9uIE1vZGVscw0KIyMjIDIuMSBO4buZaSBkdW5nIGNow61uaA0KQ2jGsMahbmcgbsOgeSB0csOsbmggYsOgeSBjaGkgdGnhur90IG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCDigJQgbuG7gW4gdOG6o25nIGPhu6dhIGjhuqd1IGjhur90IGPDoWMgbcO0IGjDrG5oIHRo4buRbmcga8OqLiBNw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggZ+G7k20gaGFpIHRow6BuaCBwaOG6p246DQoNCg0KJCQNClxiZWdpbnthbGlnbmVkfQ0KXHRleHR7VGjDoG5oIHBo4bqnbiBuZ+G6q3Ugbmhpw6puOn0gJlxxdWFkIFx0ZXh0e1Zhcn0oeV9pKSA9IFxzaWdtYV4yIC8gd19pIFxcDQpcdGV4dHtUaMOgbmggcGjhuqduIGjhu4cgdGjhu5FuZzp9ICZccXVhZCBcbXVfaSA9IFxiZXRhXzAgKyBcc3VtX3tqPTF9XnAgXGJldGFfaiB4X3tqaX0gXFwNClx0ZXh0e1bhu5tpOn0gJlxxdWFkIEVbeV9pXSA9IFxtdV9pID0gXGJldGFfMCArIFxiZXRhXzEgeF97MWl9ICsgXGNkb3RzICsgXGJldGFfcCB4X3twaX0NClxlbmR7YWxpZ25lZH0NCiQkDQoNCiMjIyAyLjIgTcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIG5oaeG7gXUgYmnhur9uDQoNCmBgYHtyfQ0KZml0IDwtIGxtKGxvZyhGRVYpIH4gQWdlICsgSHQgKyBHZW5kZXIgKyBTbW9rZSwgZGF0YSA9IGx1bmdjYXApDQpwbG90KGxvZyhGRVYpIH4gQWdlLCBkYXRhPWx1bmdjYXApDQphYmxpbmUoY29lZihmaXQpWzFdLCBjb2VmKGZpdClbMl0sIGNvbD0iYmx1ZSIsIGx3ZD0yKQ0KYGBgDQoNCktp4buDbSDEkeG7i25oIGdp4bqjIHRodXnhur90IEhvOiBCaj0gMCBkw7luZyBraeG7g20gxJHhu4tuaCB0DQokJA0KdCA9IFxmcmFje1xoYXR7XGJldGF9X2p9e3NlKFxoYXR7XGJldGF9X2opfQ0KJCQNCg0KDQogIEtob+G6o25nIHRpbiBj4bqteSAxMDAoMS3OsSklIGNobyBCajoNCiQkDQpcaGF0e1xiZXRhfV9qIFxwbSB0Xipfe1xhbHBoYS8yLCBcLCBuIC0gcCd9IFxjZG90IHNlKFxoYXR7XGJldGF9X2opDQokJA0KYGBge3J9DQpzdGVwKGZpdCwgZGlyZWN0aW9uPSJib3RoIikgICAgICAgICAgICAgICAjIFN0ZXB3aXNlIGNo4buNbiBtw7QgaMOsbmgNCmV4dHJhY3RBSUMoZml0KSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgQUlDIGPhu6dhIG3DtCBow6xuaA0KYGBgDQoNCkvhur90IHF14bqjIG3DtCBow6xuaCAoT3V0cHV0IHThu6sgUikNCkjhu4cgc+G7kSBo4buTaSBxdXk6DQoNCkludGVyY2VwdDogLTEuOTQ0MDANCg0KQWdlOiAwLjAyMzM5ICh0xINuZyAxIHR14buVaSDihpIgbG9nKEZFVikgdMSDbmcga2hv4bqjbmcgMC4wMjMpDQoNCkh0IChjaGnhu4F1IGNhbyk6IDAuMDQyODANCg0KR2VuZGVyTTogMC4wMjkzMiAobmFtIGNhbyBoxqFuIG7hu68pDQoNClNtb2tlU21va2VyOiAtMC4wNDYwNyAoaMO6dCB0aHXhu5FjIGzDoG0gZ2nhuqNtIEZFVikNCg0KQuG6o25nIEFJQyBkw7luZyDEkeG7gyBzbyBzw6FuaCBjw6FjIG3DtCBow6xuaCBraGkgbG/huqFpIGLhu48gdOG7q25nIGJp4bq/biDihpIgY2hvIHRo4bqleSBiaeG6v24gSHQgbMOgIHF1YW4gdHLhu41uZyBuaOG6pXQgKHTEg25nIEFJQyBuaGnhu4F1IG5o4bqldCBraGkgYuG7iyBsb+G6oWkpLg0KDQojIyBDaMawxqFuZyAzOiBMaW5lYXIgUmVncmVzc2lvbiBNb2RlbHM6IERpYWdub3N0aWNzIGFuZCBNb2RlbC1CdWlsZGluZw0KIyMjIDMuMSBO4buZaSBkdW5nIGNow61uaDoNCkNoxrDGoW5nIG7DoHkgdOG6rXAgdHJ1bmcgdsOgbyB2aeG7h2Mga2nhu4NtIHRyYSBjw6FjIGdp4bqjIMSR4buLbmggY+G7p2EgbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIChsaW5lYXJpdHksIGNvbnN0YW50IHZhcmlhbmNlLCBub3JtYWxpdHksIGluZGVwZW5kZW5jZSkgdGjDtG5nIHF1YSBwaOG6p24gZMawIHbDoCBnacOhIHRy4buLIMSRw7JuIGLhuql5IChsZXZlcmFnZSkuIE7DsyBjxaluZyBnaeG7m2kgdGhp4buHdSBjw6FjIHBoxrDGoW5nIHBow6FwIHBow6F0IGhp4buHbiDEkWnhu4NtIGThu4sgYmnhu4d0IChvdXRsaWVycyksIGPDoWMgcXVhbiBzw6F0IOG6o25oIGjGsOG7n25nIG3huqFuaCAoaW5mbHVlbnRpYWwgcG9pbnRzKSwgdsOgIGPDoWNoIGto4bqvYyBwaOG7pWMgdGjDtG5nIHF1YSBiaeG6v24gxJHhu5VpIGJp4bq/biAodHJhbnNmb3JtYXRpb24pLCBzcGxpbmUgaG/hurdjIGxv4bqhaSBi4buPIHF1YW4gc8OhdCBraMO0bmcgcGjDuSBo4bujcC4gTmdvw6BpIHJhLCBjaMawxqFuZyBjw7JuIMSR4buBIGPhuq1wIMSR4bq/biB24bqlbiDEkeG7gSDEkWEgY+G7mW5nIHR1eeG6v24gKGNvbGxpbmVhcml0eSkgdsOgIG3DtCBow6xuaCBow7NhIGhp4buHdSBxdeG6oy4NCg0KIyMjIDMuMiBCaeG7g3UgxJHhu5MgcGjhuqduIGTGsCBraeG7g20gdHJhIHR1eeG6v24gdMOtbmggdsOgIHBoxrDGoW5nIHNhaSBraMO0bmcgxJHhu5VpDQpgYGB7cn0NCiMgR2nhuqMgc+G7rSBi4bqhbiDEkcOjIGPDsyBtw7QgaMOsbmggaOG7k2kgcXV5Og0KZml0IDwtIGxtKEZFViB+IEh0ICsgR2VuZGVyICsgU21va2UsIGRhdGEgPSBsdW5nY2FwKQ0KDQojIFbhur0gYmnhu4N1IMSR4buTIHBo4bqnbiBkxrAgY2h14bqpbiBow7NhIHRoZW8gZ2nDoSB0cuG7iyBk4buxIMSRb8Ohbg0KcGxvdChmaXQkZml0dGVkLnZhbHVlcywgcnN0YW5kYXJkKGZpdCksDQogICAgIG1haW4gPSAiUmVzaWR1YWxzIHZzIEZpdHRlZCIsDQogICAgIHhsYWIgPSAiRml0dGVkIFZhbHVlcyIsIHlsYWIgPSAiU3RhbmRhcmRpemVkIFJlc2lkdWFscyIpDQphYmxpbmUoaCA9IDAsIGNvbCA9ICJyZWQiKQ0KYGBgDQoNCsOdIG5naMSpYTogTuG6v3UgbcO0IGjDrG5oIGjhu6NwIGzDvSwgY8OhYyDEkWnhu4NtIHPhur0gcGjDom4gYuG7kSBuZ+G6q3Ugbmhpw6puIHh1bmcgcXVhbmggxJHGsOG7nW5nIDAgbcOgIGtow7RuZyBjw7MgaMOsbmggZOG6oW5nIGPhu6UgdGjhu4MuDQoNCiMjIENoxrDGoW5nIDQ6IEJleW9uZCBMaW5lYXIgUmVncmVzc2lvbjogVGhlIE1ldGhvZCBvZiBNYXhpbXVtIExpa2VsaWhvb2QNCiMjIyA0LjEgTuG7mWkgZHVuZyBjaMOtbmg6IA0KQ2jGsMahbmcgbsOgeSBnaeG7m2kgdGhp4buHdSBwaMawxqFuZyBwaMOhcCDGsOG7m2MgbMaw4bujbmcgaOG7o3AgbMO9IHThu5FpIMSRYSAoTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24g4oCTIE1MRSkuIMSQw6J5IGzDoCBjxqEgc+G7nyB0b8OhbiBo4buNYyBjaG8gdmnhu4djIMaw4bubYyBsxrDhu6NuZyB0cm9uZyBHTE0sIMSR4bq3YyBiaeG7h3Qga2hpIGThu68gbGnhu4d1IGtow7RuZyBjw7JuIHR1w6JuIHRoZW8gcGjDom4gcGjhu5FpIGNodeG6qW4gbmjGsCB0cm9uZyBo4buTaSBxdXkgdHV54bq/biB0w61uaC4gVMOhYyBnaeG6oyB0csOsbmggYsOgeSBraMOhaSBuaeG7h20gbGlrZWxpaG9vZCwgc2NvcmUgZnVuY3Rpb24sIEZpc2hlciBpbmZvcm1hdGlvbiwgdsOgIGPDoWNoIHTDrG0gTUxFIGLhurFuZyDEkeG6oW8gaMOgbSBj4bunYSBow6BtIGxvZy1saWtlbGlob29kLg0KDQojIyMgNC4yIEjDoG0gbG9nLWxpa2VsaWhvb2QgY2hvIHBow6JuIHBo4buRaSBjaHXhuqluDQpgYGB7cn0NCmxvZ0xpa19ub3JtYWwgPC0gZnVuY3Rpb24obXUsIHNpZ21hLCB5KSB7DQogIG4gPC0gbGVuZ3RoKHkpDQogIC1uLzIqbG9nKDIqcGkqc2lnbWFeMikgLSBzdW0oKHkgLSBtdSleMikgLyAoMipzaWdtYV4yKQ0KfQ0KDQp5IDwtIGMoMi4zLCAyLjUsIDIuMSwgMi42KQ0KbG9nTGlrX25vcm1hbChtZWFuKHkpLCBzZCh5KSwgeSkNCmBgYA0KIyMgQ2jGsMahbmcgNTogR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVsczogU3RydWN0dXJlDQojIyMgNS4xIE7hu5lpIGR1bmcgY2jDrW5oDQpO4buZaSBkdW5nIGNow61uaDoNCkdMTSBn4buTbSAzIHBo4bqnbjoNCg0KVGjDoG5oIHBo4bqnbiBuZ+G6q3Ugbmhpw6puOiBwaMOibiBwaOG7kWkgdGh14buZYyBo4buNIGjDoG0gbcWpIChleHBvbmVudGlhbCBmYW1pbHkpLg0KDQpUaMOgbmggcGjhuqduIGjhu4cgdGjhu5FuZzogcHJlZGljdG9yIHR1eeG6v24gdMOtbmggKM63ID0gWM6yKS4NCg0KSMOgbSBsacOqbiBr4bq/dCAobGluayk6IG7hu5FpIEUoeSkgPSDOvCB24bubaSDOty4NCg0KQ8OhYyBwaMOibiBwaOG7kWkgcGjhu5UgYmnhur9uOg0KDQpOb3JtYWwsIEJpbm9taWFsLCBQb2lzc29uLCBHYW1tYSwgSW52ZXJzZSBHYXVzc2lhbi4uLg0KDQpHacOhIHRy4buLIHRydW5nIGLDrG5oIHbDoCBwaMawxqFuZyBzYWk6DQoNCkVbeV0gPSDOvCA9IGTOui9kzrgNCg0KVmFyW3ldID0gz4ZWKM68KQ0KR0xNIGxpbmggaG/huqF0IGjGoW4gaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggdsOsIGNo4buNbiByacOqbmcgcGjDom4gcGjhu5FpIHbDoCBow6BtIGxpw6puIGvhur90LCBnacO6cCBwaMO5IGjhu6NwIGLhuqNuIGNo4bqldCBk4buvIGxp4buHdSBoxqFuDQoNCiMjIyA1LjIgVsOtIGThu6UgbcO0IGjDrG5oIEdMTSB24bubaSBwaMOibiBwaOG7kWkgUG9pc3Nvbg0KWMOieSBk4buxbmcgbeG7mXQgbcO0IGjDrG5oIEdMTSB24bubaSBwaMOibiBwaOG7kWkgUG9pc3NvbiDEkeG7gyBk4buxIMSRb8OhbiBiaeG6v24geSB0aGVvIGJp4bq/biDEkeG7mWMgbOG6rXAgeC4gSMOgbSBsacOqbiBr4bq/dCBsw6AgbG9nLg0KYGBge3J9DQpkYXRhIDwtIGRhdGEuZnJhbWUoeSA9IGMoMiwgMywgNCksIHggPSBjKDEsIDIsIDMpKQ0KZml0IDwtIGdsbSh5IH4geCwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpLCBkYXRhID0gZGF0YSkNCnN1bW1hcnkoZml0KQ0KYGBgDQpL4bq/dCBxdeG6oyBjaMOtbmg6DQoNCkjhu4cgc+G7kSBpbnRlcmNlcHQg4omIIDAuODEgdsOgIHgg4omIIDAuMzQuDQoNClR1eSBuaGnDqm4sIHAtdmFsdWUgY+G7p2EgaOG7hyBz4buRIHggbMOgIDAuNDE5LCBuw6puIGJp4bq/biBuw6B5IGtow7RuZyBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqLg0KDQpSZXNpZHVhbCBkZXZpYW5jZSBy4bqldCBuaOG7jyAo4omIIDAuMDA2OCksIGNobyB0aOG6pXkgbcO0IGjDrG5oIGto4bubcCBk4buvIGxp4buHdSBy4bqldCB04buRdC4NCg0KS+G6v3QgbHXhuq1uOiBNw7QgaMOsbmggY8OzIHRo4buDIMSRYW5nIGLhu4sgb3ZlcmZpdCAodsOsIHPhu5EgbMaw4bujbmcgcXVhbiBzw6F0IG5o4buPKSwgaG/hurdjIGJp4bq/biB4IHRo4buxYyBz4buxIGtow7RuZyDhuqNuaCBoxrDhu59uZyDEkeG6v24geQ0KDQojIyBDaMawxqFuZyA2OiBHZW5lcmFsaXplZCBMaW5lYXIgTW9kZWxzOiBFc3RpbWF0aW9uDQojIyMgNi4xIE7hu5lpIGR1bmcgY2jDrW5oOg0KQ2jGsMahbmcgdHLDrG5oIGLDoHkgY8OhY2ggxrDhu5tjIGzGsOG7o25nIHRoYW0gc+G7kSDOsiB2w6Agz4YgdHJvbmcgR0xNOg0KDQpYw6J5IGThu7FuZyBwaMawxqFuZyB0csOsbmggc2NvcmUgdsOgIG1hIHRy4bqtbiB0aMO0bmcgdGluIChpbmZvcm1hdGlvbiBtYXRyaXgpLg0KDQpT4butIGThu6VuZyB0aHXhuq10IHRvw6FuIElSTFMgKEl0ZXJhdGl2ZWx5IFJld2VpZ2h0ZWQgTGVhc3QgU3F1YXJlcykgxJHhu4MgdMOsbSDOssyCLg0KDQrEkOG7mSBs4buHY2ggKERldmlhbmNlKSDEkW8gbMaw4budbmcgbeG7qWMgc2FpIGzhu4djaCBjw7JuIGzhuqFpIGPhu6dhIG3DtCBow6xuaC4NCg0KTWEgdHLhuq1uIGNodeG6qW4gaMOzYSB2w6Agc2FpIHPhu5EgY2h14bqpbiBj4bunYSDOssyCLg0KDQrGr+G7m2MgbMaw4bujbmcgdGhhbSBz4buRIHBow6JuIHTDoW4gz4Y6IHRoZW8gZGV2aWFuY2UsIFBlYXJzb24gaG/hurdjIGjhu5Mgc8ahIGjhu6NwIGzDvS4NCg0KUXVhbiB0cuG7jW5nOiBHTE0gxJHGsOG7o2MgZml0IHTGsMahbmcgdOG7sSBo4buTaSBxdXkgdHV54bq/biB0w61uaCBuaOG7nSBJUkxTIG7Dqm4ga+G6vyB0aOG7q2Egbmhp4buBdSBjw7RuZyBj4bulIG5oxrAgbGV2ZXJhZ2UsIENvb2vigJlzIGRpc3RhbmNlLi4uDQoNCiMjIyA2LjIgTWluaCBob+G6oSDEkeG7mSBs4buHY2ggKERldmlhbmNlKQ0KU+G7rSBk4bulbmcgR0xNIHbhu5tpIHBow6JuIHBo4buRaSBQb2lzc29uIMSR4buDIGtp4buDbSB0cmEgxJHhu5kgcGjDuSBo4bujcCBtw7QgaMOsbmggdGjDtG5nIHF1YSByZXNpZHVhbCBkZXZpYW5jZSB2w6AgbnVsbCBkZXZpYW5jZS4NCmBgYHtyfQ0KIyBU4bqhbyBi4buZIGThu68gbGnhu4d1IG3DtCBwaOG7j25nDQpkYXRhIDwtIGRhdGEuZnJhbWUoDQogIHkgPSBjKDIsIDMsIDQsIDYsIDgpLA0KICB4ID0gYygxLCAyLCAzLCA0LCA1KQ0KKQ0KDQojIEZpdCBtw7QgaMOsbmggR0xNIHbhu5tpIHBow6JuIHBo4buRaSBQb2lzc29uIHbDoCBow6BtIGxpw6puIGvhur90IGxvZw0KZml0IDwtIGdsbSh5IH4geCwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpLCBkYXRhID0gZGF0YSkNCg0KIyBUw7NtIHThuq90IGvhur90IHF14bqjIMaw4bubYyBsxrDhu6NuZyBo4buHIHPhu5EgzrINCnN1bW1hcnkoZml0KQ0KDQojIFTDrW5oIMSR4buZIGzhu4djaCAoZGV2aWFuY2UpIGPhu6dhIG3DtCBow6xuaA0KZGV2aWFuY2UoZml0KQ0KDQojIFNvIHPDoW5oIHbhu5tpIG51bGwgZGV2aWFuY2UgKG3DtCBow6xuaCBjaOG7iSBjw7MgaW50ZXJjZXB0KQ0KZml0JG51bGwuZGV2aWFuY2UNCmBgYA0KS+G6v3QgcXXhuqMgY2jDrW5oOg0KDQpOdWxsIGRldmlhbmNlIOKJiCA1LjAzOiDEkeG7mSBs4buHY2ggY+G7p2EgbcO0IGjDrG5oIGNo4buJIGPDsyBpbnRlcmNlcHQuDQoNClJlc2lkdWFsIGRldmlhbmNlIOKJiCAwLjAxODogbcO0IGjDrG5oIGPDsyBiaeG6v24geCDEkcOjIGdp4bqjaSB0aMOtY2ggaOG6p3UgaOG6v3QgYmnhur9uIHRoacOqbi4NCg0KcC12YWx1ZSBjaG8gaOG7hyBz4buRIHggPSAwLjAzMDkgPCAwLjA1IOKHkiB4IGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ouDQoNCkvhur90IGx14bqtbjogQmnhur9uIHggbMOgIGJp4bq/biBnaeG6o2kgdGjDrWNoIGPDsyDDvSBuZ2jEqWEsIHbDoCBtw7QgaMOsbmggcGjDuSBo4bujcCB04buRdCB24bubaSBk4buvIGxp4buHdS4NCg0KIyMgQ2jGsMahbmcgNzogR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVsczogSW5mZXJlbmNlDQojIyMgNy4xIE7hu5lpIGR1bmcgY2jDrW5oOg0KQ2jGsMahbmcgbsOgeSBoxrDhu5tuZyBk4bqrbiBjw6FjaCBzdXkgbHXhuq1uIHRo4buRbmcga8OqIHRyb25nIEdMTXM6DQoNCsav4bubYyBsxrDhu6NuZyBraG/huqNuZyB0aW4gY+G6rXkgKGNvbmZpZGVuY2UgaW50ZXJ2YWxzKSBjaG8gaOG7hyBz4buRLA0KDQpLaeG7g20gxJHhu4tuaCBXYWxkLCBraeG7g20gxJHhu4tuaCBsaWtlbGlob29kIHJhdGlvLCBzY29yZSB0ZXN0LA0KDQpTbyBzw6FuaCBtw7QgaMOsbmggbOG7k25nIChuZXN0ZWQgbW9kZWxzKSwNCg0KQUlDL0JJQyDEkeG7gyBzbyBzw6FuaCBtw7QgaMOsbmgga2jDtG5nIGzhu5NuZy4NCg0KIyMjIDcuMiBTbyBzw6FuaCBtw7QgaMOsbmggduG7m2kgbGlrZWhvb2QgcmF0aW8gdGVzdA0KYGBge3J9DQpmaXQxIDwtIGdsbSh5IH4gMSwgZmFtaWx5ID0gcG9pc3NvbiwgZGF0YSA9IGRhdGEpDQpmaXQyIDwtIGdsbSh5IH4geCwgZmFtaWx5ID0gcG9pc3NvbiwgZGF0YSA9IGRhdGEpDQphbm92YShmaXQxLCBmaXQyLCB0ZXN0ID0gIkNoaXNxIikNCmBgYA0KIyMgQ2jGsMahbmcgODogR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVsczogRGlhZ25vc3RpY3MNCiMjIyA4LjEgTuG7mWkgZHVuZyBjaMOtbmg6DQpN4bulYyB0acOqdSBsw6AgcGjDoXQgaGnhu4duIHbDoCB44butIGzDvSB2aSBwaOG6oW0gZ2nhuqMgxJHhu4tuaDoNCg0KQ8OhYyBnaeG6oyDEkeG7i25oIGPhuqduIGtp4buDbSB0cmE6IHBow6JuIHBo4buRaSBwaMO5IGjhu6NwLCBraMO0bmcgY8OzIG91dGxpZXJzLCBow6BtIGxpw6puIGvhur90IMSRw7puZywgdHV54bq/biB0w61uaCwgcGjGsMahbmcgc2FpIMSRw7puZywgxJHhu5ljIGzhuq1wLg0KDQpDw6FjIGxv4bqhaSBwaOG6p24gZMawOiBQZWFyc29uLCBkZXZpYW5jZSwgcXVhbnRpbGUgKMSR4bq3YyBiaeG7h3QgaOG7r3Ugw61jaCBjaG8gZOG7ryBsaeG7h3UgcuG7nWkgcuG6oWMpLg0KDQrEkMOhbmggZ2nDoSDhuqNuaCBoxrDhu59uZzogbGV2ZXJhZ2UsIENvb2vigJlzIGRpc3RhbmNlLCBkZmJldGFzLi4uDQoNCkNo4bqpbiDEkW/DoW4gxJHhu5MgdGjhu4s6IGJp4buDdSDEkeG7kyBwaOG6p24gZMawLCBRLVEsIHBsb3QgcmVzaWR1YWxzIHZzIGZpdHRlZC4NCg0KS2jhuq9jIHBo4bulYzogYmnhur9uIMSR4buVaSBiaeG6v24sIHRow6ptIHNwbGluZSwgbcO0IGjDrG5oIHF1YXNpLCB44butIGzDvSBjb2xsaW5lYXJpdHkNCg0KIyMjIDguMjogIE1pbmggaOG7jWEgcmVzaWR1YWxzIHRyb25nIEdMTSAoduG7m2kgd2FycGJyZWFrcykNCmBgYHtyfQ0KbW9kIDwtIGdsbShicmVha3MgfiB3b29sICsgdGVuc2lvbiwgZGF0YSA9IHdhcnBicmVha3MsIGZhbWlseSA9IHBvaXNzb24pDQpwYXIobWZyb3cgPSBjKDIsIDIpKQ0KcGxvdChtb2QpDQpgYGANCg0KIyMgQ2jGsMahbmcgOTogTW9kZWxzIGZvciBQcm9wb3J0aW9uczogQmlub21pYWwgR0xNcw0KIyMjIDkuMSBO4buZaSBkdW5nIGNow61uaDoNCkNoxrDGoW5nIG7DoHkgbcO0IGjDrG5oIGjDs2EgZOG7ryBsaeG7h3Ugbmjhu4sgcGjDom4gaG/hurdjIHThu7cgbOG7hyBi4bqxbmcgR0xNIHbhu5tpIHBow6JuIHBo4buRaSBuaOG7iyB0aOG7qWM6DQoNCkxpbmsgZnVuY3Rpb24gcGjhu5UgYmnhur9uOiBsb2dpdCwgcHJvYml0LCBjb21wbGVtZW50YXJ5IGxvZy1sb2cuDQoNCkdp4bqjaSB0aMOtY2ggdsOgIMaw4bubYyBsxrDhu6NuZyBvZGRzIHJhdGlvLCBFRDUwLg0KDQpLaeG7g20gdHJhIG92ZXJkaXNwZXJzaW9uIOKGkiBkw7luZyBxdWFzaS1iaW5vbWlhbCBu4bq/dSBj4bqnbi4NCg0KQ8OzIHbDrSBk4bulIGNobyBj4bqjIGThu68gbGnhu4d1IHThu4kgbOG7hyAoc3VjY2Vzcy90cmlhbHMpIHbDoCBuaOG7iyBwaMOibiAoMC8xKS4NCg0KIyMjIDkuMiAgTWluaCBo4buNYTogTcO0IGjDrG5oIGxvZ2l0IGNobyBk4buvIGxp4buHdSBuaOG7iyBwaMOibg0KWMOieSBk4buxbmcgbeG7mXQgbcO0IGjDrG5oIGjhu5NpIHF1eSBsb2dpc3RpYyDEkeG7gyBk4buxIMSRb8OhbiB4w6FjIHN14bqldCBzdWNjZXNzIGThu7FhIHRyw6puIHguDQpgYGB7cn0NCmRhdGEgPC0gZGF0YS5mcmFtZSgNCiAgc3VjY2VzcyA9IGMoMSwgMCwgMSwgMSwgMCksDQogIHggPSBjKDEwLCAxMiwgMTQsIDE2LCAxOCkNCikNCmZpdCA8LSBnbG0oc3VjY2VzcyB+IHgsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwgZGF0YSA9IGRhdGEpDQpzdW1tYXJ5KGZpdCkNCmBgYA0KDQpL4bq/dCBxdeG6oyBjaMOtbmg6DQoNCkludGVyY2VwdCDiiYggMy41MiwgaOG7hyBz4buRIHgg4omIIC0wLjIyIG5oxrBuZyBraMO0bmcgY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqiAocCA9IDAuNTMwKS4NCg0KUmVzaWR1YWwgZGV2aWFuY2Ug4omIIDYuMywga2jDtG5nIGtow6FjIGJp4buHdCBuaGnhu4F1IHNvIHbhu5tpIG51bGwgZGV2aWFuY2UuDQoNCkvhur90IGx14bqtbjogQmnhur9uIHgga2jDtG5nIGdp4bqjaSB0aMOtY2ggdOG7kXQgeMOhYyBzdeG6pXQgdGjDoG5oIGPDtG5nLiBDw7MgdGjhu4MgY+G6p24gdGjDqm0gYmnhur9uIGhv4bq3YyB0aGF5IMSR4buVaSBtw7QgaMOsbmguDQoNCiMjIENoxrDGoW5nIDEwOiBNb2RlbHMgZm9yIENvdW50czogUG9pc3NvbiBhbmQgTmVnYXRpdmUgQmlub21pYWwgR0xNcw0KIyMjIDEwLjEgTuG7mWkgZHVuZyBjaMOtbmg6DQrDgXAgZOG7pW5nIEdMTSBjaG8gZOG7ryBsaeG7h3UgxJHhur9tOg0KDQpQb2lzc29uIEdMTTogZ2nhuqMgxJHhu4tuaCBFKHkpID0gVmFyKHkpLg0KDQpOZWdhdGl2ZSBCaW5vbWlhbCBHTE06IGNobyBwaMOpcCBWYXIoeSkgPiBFKHkpIOKGkiBraOG6r2MgcGjhu6VjIG92ZXJkaXNwZXJzaW9uLg0KDQpT4butIGThu6VuZyBsb2ctbGluayBwaOG7lSBiaeG6v24uDQoNCk3DtCBow6xuaCBsb2ctbGluZWFyIGNobyBi4bqjbmcgY2jDqW8gKGNvbnRpbmdlbmN5IHRhYmxlcykuDQoNClRyw6xuaCBiw6B5IFNpbXBzb24ncyBwYXJhZG94IHbDoCBjw6FjaCBraeG7g20gc2/DoXQgYuG6sW5nIGJp4bq/biBwaMOibiB04bqnbmcuDQoNCiMjIyAxMC4yIE1pbmggaG/huqE6IFNvIHPDoW5oIFBvaXNzb24gdnMgTmVnYXRpdmUgQmlub21pYWwNClNvIHPDoW5oIGhhaSBtw7QgaMOsbmg6IG3hu5l0IHbhu5tpIFBvaXNzb24gdsOgIG3hu5l0IHbhu5tpIE5lZ2F0aXZlIEJpbm9taWFsIMSR4buDIGtp4buDbSB0cmEgaGnhu4duIHTGsOG7o25nDQpgYGB7cn0NCmxpYnJhcnkoTUFTUykNCmNvdW50cyA8LSBjKDEsIDIsIDQsIDcsIDEyLCAxOCkNCnggPC0gYygxLCAyLCAzLCA0LCA1LCA2KQ0KcG9pc3Nvbl9tb2RlbCA8LSBnbG0oY291bnRzIH4geCwgZmFtaWx5ID0gcG9pc3NvbikNCm5iX21vZGVsIDwtIGdsbS5uYihjb3VudHMgfiB4KQ0KDQpzdW1tYXJ5KHBvaXNzb25fbW9kZWwpDQpzdW1tYXJ5KG5iX21vZGVsKQ0KYGBgDQoNCkPhuqMgaGFpIG3DtCBow6xuaCBjw7MgaOG7hyBz4buRIGfhuqduIGdp4buRbmcgbmhhdS4NCg0KUmVzaWR1YWwgZGV2aWFuY2Ug4omIIDAuMjcg4bufIGPhuqMgaGFpIG3DtCBow6xuaC4NCg0KQUlDIGjGoWkga2jDoWM6IFBvaXNzb24gPSAyNS4wMywgTkIgPSAyNy4wMy4NCg0KS+G6v3QgbHXhuq1uOiBEw7kgbmdoaSBuZ+G7nSBvdmVyZGlzcGVyc2lvbiwgUG9pc3NvbiB24bqrbiBob+G6oXQgxJHhu5luZyB04buRdCB2w6Aga2jDtG5nIGPhuqduIHRoaeG6v3QgY2h1eeG7g24gc2FuZyBOZWdhdGl2ZSBCaW5vbWlhbCB0cm9uZyB0csaw4budbmcgaOG7o3AgbsOgeS4gVHV5IG5oacOqbiwgY+G6o25oIGLDoW8g4oCcaXRlcmF0aW9uIGxpbWl0IHJlYWNoZWTigJ0gY2hvIHRo4bqleSBjw7MgdGjhu4MgY+G6p24gxJFp4buBdSBjaOG7iW5oIGvhu7kgdGh14bqtdCBmaXR0aW5nLg0KIyMgQ2jGsMahbmcgMTE6IFBvc2l0aXZlIENvbnRpbnVvdXMgRGF0YTogR2FtbWEgYW5kIEludmVyc2UgR2F1c3NpYW4gR0xNcw0KIyMjIDExLjEgTuG7mWkgZHVuZyBjaMOtbmg6DQpDaMawxqFuZyBuw6B5IHBow7kgaOG7o3AgduG7m2kgZOG7ryBsaeG7h3UgZMawxqFuZyBuaMawIGNoaSBwaMOtLCB0aOG7nWkgZ2lhbiwgdHXhu5VpIHRo4buNOg0KDQpHYW1tYSBHTE06IFZhcih5KSA9IM68wrINCg0KSW52ZXJzZSBHYXVzc2lhbiBHTE06IFZhcih5KSA9IM68wrMNCg0KTGluayBmdW5jdGlvbnM6IGxvZywgaWRlbnRpdHksIGludmVyc2UuDQoNCkPDoWNoIGNo4buNbiBwaMOibiBwaOG7kWkgcGjDuSBo4bujcCB0w7l5IHRoZW8gxJHhu5kgbOG7h2NoIHbDoCBwaMOibiB0w6FuIGPhu6dhIGThu68gbGnhu4d1Lg0KDQojIyMgMTEuMiBHYW1tYSBHTE0gduG7m2kgaMOgbSBsacOqbiBr4bq/dCBsb2cNCk3DtCBow6xuaCBow7NhIG3hu5l0IGJp4bq/biBkxrDGoW5nIGxpw6puIHThu6VjICh5KSB0aGVvIGJp4bq/biB4IGLhurFuZyBtw7QgaMOsbmggR0xNIHbhu5tpIHBow6JuIHBo4buRaSBHYW1tYS4NCmBgYHtyfQ0KeSA8LSBjKDIuMSwgMy40LCA0LjAsIDUuNSkNCnggPC0gYygxLCAyLCAzLCA0KQ0KZml0IDwtIGdsbSh5IH4geCwgZmFtaWx5ID0gR2FtbWEobGluayA9ICJsb2ciKSkNCnN1bW1hcnkoZml0KQ0KYGBgDQoNCkvhur90IHF14bqjIGNow61uaDoNCg0KQ+G6oyBpbnRlcmNlcHQgdsOgIGjhu4cgc+G7kSB4IMSR4buBdSBjw7Mgw70gbmdoxKlhIHRo4buRbmcga8OqIChwIDwgMC4wNSkuDQoNClJlc2lkdWFsIGRldmlhbmNlIHLhuqV0IG5o4buPICjiiYggMC4wMTgpLg0KDQpEaXNwZXJzaW9uIHBhcmFtZXRlciBy4bqldCBuaOG7jyDiiYggMC4wMDk1IOKGkiBtw7QgaMOsbmgga2jhu5twIHLhuqV0IHThu5F0Lg0KDQpL4bq/dCBsdeG6rW46IEJp4bq/biB4IOG6o25oIGjGsOG7n25nIHTDrWNoIGPhu7FjIMSR4bq/biB5LCB2w6AgR2FtbWEgR0xNIGzDoCBs4buxYSBjaOG7jW4gcGjDuSBo4bujcCBjaG8gZOG7ryBsaeG7h3UgZMawxqFuZyBs4buHY2ggcGjhuqNpLg0KDQojIyBDaMawxqFuZyAxMjogVHdlZWRpZSBHTE1zDQojIyMgMTIuMSBO4buZaSBkdW5nIGNow61uaDoNClBow6JuIHBo4buRaSBUd2VlZGllIGzDoCBo4buNIHBow6JuIHBo4buRaSBsacOqbiB04bulYyBtw6AgY2jhu6lhOg0KDQpOb3JtYWwgKHA9MCksIFBvaXNzb24gKHA9MSksIEdhbW1hIChwPTIpLg0KDQpDb21wb3VuZCBQb2lzc29uLUdhbW1hICgxIDwgcCA8IDIpIOKGkiB0aMaw4budbmcgZ+G6t3AgdHJvbmcgYuG6o28gaGnhu4NtLg0KDQpDaG8gcGjDqXAgZOG7ryBsaeG7h3UgduG7q2EgY8OzIG5oaeG7gXUgc+G7kSAwIHbhu6thIGPDsyBnacOhIHRy4buLIGTGsMahbmcuDQoNCkPhuqduIMaw4bubYyBsxrDhu6NuZyBjaOG7iSBz4buRIHAsIGTDuW5nIGfDs2kgdHdlZWRpZSB2w6Agc3RhdG1vZCB0cm9uZyBSIMSR4buDIGZpdCBtw7QgaMOsbmguDQoNCiMjIyAxMi4yOiBNaW5oIGjhu41hIG3DtCBow6xuaCBUd2VlZGllIEdMTQ0Kw4FwIGThu6VuZyBtw7QgaMOsbmggVHdlZWRpZSBjaG8gZOG7ryBsaeG7h3UgY8OzIGdpw6EgdHLhu4sgYuG6sW5nIDAgdsOgIGxpw6puIHThu6VjIGTGsMahbmcg4oCTIHLhuqV0IHBo4buVIGJp4bq/biB0cm9uZyBi4bqjbyBoaeG7g20sIHbDrSBk4bulOiB04buVbiB0aOG6pXQgZG8gdGFpIG7huqFuLg0KDQpgYGB7cn0NCiMgQ8OgaSBnw7NpIGPhuqduIHRoaeG6v3QgbuG6v3UgY2jGsGEgY8OzDQppZiAoIXJlcXVpcmUoInN0YXRtb2QiKSkgaW5zdGFsbC5wYWNrYWdlcygic3RhdG1vZCIpDQppZiAoIXJlcXVpcmUoInR3ZWVkaWUiKSkgaW5zdGFsbC5wYWNrYWdlcygidHdlZWRpZSIpDQoNCiMgTuG6oXAgZ8OzaQ0KbGlicmFyeShzdGF0bW9kKQ0KbGlicmFyeSh0d2VlZGllKQ0KDQojIFThuqFvIGThu68gbGnhu4d1IG3DtCBwaOG7j25nOiBjw7MgY+G6oyBz4buRIDAgdsOgIHPhu5EgZMawxqFuZw0Kc2V0LnNlZWQoMTIzKQ0KbiA8LSAxMDANCnggPC0gcnVuaWYobiwgMCwgMTApDQptdSA8LSBleHAoMSArIDAuMiAqIHgpDQp5IDwtIHJ0d2VlZGllKG4gPSBuLCBtdSA9IG11LCBwaGkgPSAxLCBwb3dlciA9IDEuNSkgICMgVHdlZWRpZSB24bubaSBwID0gMS41DQoNCmRhdGEgPC0gZGF0YS5mcmFtZSh5ID0geSwgeCA9IHgpDQoNCiMgRml0IG3DtCBow6xuaCBUd2VlZGllIEdMTQ0KbW9kZWwgPC0gZ2xtKHkgfiB4LCBmYW1pbHkgPSB0d2VlZGllKHZhci5wb3dlciA9IDEuNSwgbGluay5wb3dlciA9IDApLCBkYXRhID0gZGF0YSkNCg0KIyBUw7NtIHThuq90IGvhur90IHF14bqjDQpzdW1tYXJ5KG1vZGVsKQ0KDQojIFbhur0gZml0dGVkIHZzIG9ic2VydmVkDQpwbG90KGRhdGEkeCwgZGF0YSR5LCBwY2ggPSAxNiwgY29sID0gImdyZXkiLCBtYWluID0gIlR3ZWVkaWUgR0xNIEZpdCIpDQpsaW5lcyhkYXRhJHgsIGZpdHRlZChtb2RlbCksIGNvbCA9ICJibHVlIiwgbHdkID0gMikNCmBgYA0KIyMgQ2jGsMahbmcgMTM6IEV4dHJhIFByb2JsZW1zDQoNCiMjIyAxMy4xIE7hu5lpIGR1bmcgY2jDrW5oOg0KQ2jGsMahbmcgMTMga2jDtG5nIGdp4bubaSB0aGnhu4d1IGzDvSB0aHV54bq/dCBt4bubaSwgbcOgIGzDoCBi4buZIHPGsHUgdOG6rXAgY8OhYyBiw6BpIHThuq1wIG3hu58gcuG7mW5nIHnDqnUgY+G6p3UgbmfGsOG7nWkgaOG7jWM6DQoNClThu7EgbOG7sWEgY2jhu41uIG3DtCBow6xuaCBwaMO5IGjhu6NwIGNobyBuaGnhu4F1IGxv4bqhaSBk4buvIGxp4buHdS4NCg0KWMOhYyDEkeG7i25oIMSRw7puZyBwaMOibiBwaOG7kWkgdsOgIGxpbmsgZnVuY3Rpb24uDQoNClRo4buxYyBoaeG7h24gY2jhuqluIMSRb8OhbiBtw7QgaMOsbmggKGRpYWdub3N0aWNzKS4NCg0KR2nhuqNpIHRow61jaCBr4bq/dCBxdeG6oyBtw7QgaMOsbmggdGhlbyBuZ+G7ryBj4bqjbmggbmdoacOqbiBj4bupdS4NCg0KQ8OhYyBiw6BpIHThuq1wIMSRxrDhu6NjIHRoaeG6v3Qga+G6vyDEkeG7gyB04buVbmcgaOG7o3Aga2nhur9uIHRo4bupYyB04burIGPDoWMgY2jGsMahbmcgdHLGsOG7m2MgbmjGsDoNCg0KTmjhu4sgdGjhu6ljIChCaW5vbWlhbCkNCg0KUG9pc3NvbiAvIE5lZ2F0aXZlIEJpbm9taWFsDQoNCkdhbW1hIC8gSW52ZXJzZSBHYXVzc2lhbg0KDQpUd2VlZGllDQoNCkJp4bq/biDEkeG7lWkgZOG7ryBsaeG7h3UsIHBow6F0IGhp4buHbiBk4buLIGJp4buHdCwgbcO0IGjDrG5oIHTGsMahbmcgdMOhYy4uLg0KDQojIFBo4bqnbiAyOiBUaOG7kW5nIGvDqiBtw7QgdOG6oyBjw6FjIGJp4bq/biB0cm9uZyB04bqtcCAqKlN1cGVybWFya2V0IFRyYW5zYWN0aW9ucy5jc3YqKg0KIyMgMi4xIFThu5VuZyBxdWFuIGThu68gbGnhu4d1DQpTdXBlcm1hcmtldCBUcmFuc2FjdGlvbnMgbMOgIGLhu5kgZOG7ryBsaeG7h3UgZ2hpIG5o4bqtbiBjw6FjIGdpYW8gZOG7i2NoIG11YSBow6BuZyB04bqhaSBzacOqdSB0aOG7iywgxJHGsOG7o2MgdOG7lSBjaOG7qWMgdGhlbyB04burbmcgxJHGoW4gaMOgbmcuIFF1eSBtw7QgZOG7ryBsaeG7h3UgZ+G7k20gMTQuMDU5IHF1YW4gc8OhdCAoZMOybmcpIHbDoCAxNiBiaeG6v24gKGPhu5l0KSwgcGjhuqNuIMOhbmgga2jhu5FpIGzGsOG7o25nIHbDoCDEkWEgZOG6oW5nIHRow7RuZyB0aW4gxJHGsOG7o2MgdGh1IHRo4bqtcC4NCg0KU2F1IGtoaSBsb+G6oWkgYuG7jyBj4buZdCBjaOG7iSBt4bulYyBkxrAgdGjhu6thLCBj4bqldSB0csO6YyBk4buvIGxp4buHdSDEkcaw4bujYyBraeG7g20gdHJhIGzhuqFpIMSR4buDIMSR4bqjbSBi4bqjbyB0w61uaCBuaOG6pXQgcXXDoW4uIMSQw6FuZyBjaMO6IMO9LCBiaeG6v24gUHVyY2hhc2UgRGF0ZSBjaOG7qWEgdGjDtG5nIHRpbiBuZ8OgeSBtdWEgaMOgbmcgxJHGsOG7o2MgxJHhu4tuaCBk4bqhbmcgbmfDoHkgdGjDoW5nIChEYXRlKSwga8OpbyBkw6BpIHThu6sgY3Xhu5FpIG7Eg20gMjAwNyDEkeG6v24gY3Xhu5FpIG7Eg20gMjAwOSwgY2hvIHRo4bqleSBwaOG6oW0gdmkgdGjhu51pIGdpYW4gcXVhbiBzw6F0IGfhuqduIDIgbsSDbS4gVmnhu4djIGNodXnhu4NuIGPhu5l0IG7DoHkgduG7gSDEkeG7i25oIGThuqFuZyBuZ8OgeSBnacO6cCBo4buXIHRy4bujIGhp4buHdSBxdeG6oyBjaG8gY8OhYyBwaMOibiB0w61jaCB0aGVvIGNodeG7l2kgdGjhu51pIGdpYW4uDQoNCkPhuqV1IHRyw7pjIGLhu5kgZOG7ryBsaeG7h3UgYmFvIGfhu5NtIGPDoWMgYmnhur9uOg0KDQoxIFB1cmNoYXNlIERhdGUg4oCTIE5nw6B5IG11YSBow6BuZw0KDQoyIEN1c3RvbWVyIElEIOKAkyBNw6MgxJHhu4tuaCBkYW5oIGtow6FjaCBow6BuZw0KDQozIEdlbmRlciDigJMgR2nhu5tpIHTDrW5oOiBGIChGZW1hbGUpIGzDoCBu4buvLCBNIChNYWxlKSBsw6AgbmFtDQoNCjQgTWFyaXRhbCBTdGF0dXMg4oCTIFTDrG5oIHRy4bqhbmcgaMO0biBuaMOibjogUyAoU2luZ2xlKSBsw6AgxJHhu5ljIHRow6JuLCBNIChNYXJyaWVkKSBsw6AgxJHDoyBr4bq/dCBow7RuDQoNCjUgSG9tZW93bmVyIOKAkyBUw6xuaCB0cuG6oW5nIHPhu58gaOG7r3UgbmjDoDogWSAoWWVzKSBsw6AgY8OzIG5ow6AsIE4gKE5vKSBsw6AgY2jGsGEgY8OzIG5ow6ANCg0KNiBDaGlsZHJlbiDigJMgU+G7kSBsxrDhu6NuZyBjb24gY8OhaQ0KDQo3IEFubnVhbCBJbmNvbWUg4oCTIFRodSBuaOG6rXAgaOG6sW5nIG7Eg20NCg0KOCBDaXR5IOKAkyBUaMOgbmggcGjhu5EgY8awIHRyw7oNCg0KOSBTdGF0ZSBvciBQcm92aW5jZSDigJMgTcOjIGJhbmcgaG/hurdjIHThu4luaA0KDQoxMCBDb3VudHJ5IOKAkyBRdeG7kWMgZ2lhDQoNCjExIFByb2R1Y3QgRmFtaWx5IOKAkyBOaMOzbSBz4bqjbiBwaOG6qW0gY2jDrW5oOg0KDQoxMiBGb29kOiBUaOG7sWMgcGjhuqltDQoNCjEzIERyaW5rOiDEkOG7kyB14buRbmcNCg0KMTQgTm9uLUNvbnN1bWFibGU6IEjDoG5nIGtow7RuZyB0acOqdSBkw7luZw0KDQoxNSBQcm9kdWN0IERlcGFydG1lbnQg4oCTIFBow6JuIG5ow7NtIHPhuqNuIHBo4bqpbSBjaGkgdGnhur90IGjGoW4NCg0KMTYgUHJvZHVjdCBDYXRlZ29yeSDigJMgRGFuaCBt4bulYyBz4bqjbiBwaOG6qW0gY+G7pSB0aOG7gw0KDQoxNyBVbml0cyBTb2xkIOKAkyBT4buRIGzGsOG7o25nIHPhuqNuIHBo4bqpbSBiw6FuIMSRxrDhu6NjDQoNCjE4IFJldmVudWUg4oCTIERvYW5oIHRodSB0aHUgxJHGsOG7o2MgdOG7qyDEkcahbiBow6BuZw0KDQojIyAyLjIgVGjhu5FuZyBrw6ogbcO0IHThuqMNCg0KIyMjIDIuMi4xIFB1cmNoYXNlIGRhdGUNCmBgYHtyfQ0KbG9uZyA8LSByZWFkLmNzdihmaWxlLmNob29zZSgpLCBoZWFkZXIgPSBUKSAjIGxvYWQgZGF0YQ0KbG9uZyRQdXJjaGFzZURhdGUgPC0gYXMuRGF0ZShsb25nJFB1cmNoYXNlRGF0ZSkNCmxvbmckWWVhciA8LSBmb3JtYXQobG9uZyRQdXJjaGFzZURhdGUsICIlWSIpDQpsb25nJE1vbnRoIDwtIGZvcm1hdChsb25nJFB1cmNoYXNlRGF0ZSwgIiVZLSVtIikNCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdChsb25nLCBhZXMoeCA9IGFzLkRhdGUocGFzdGUwKFllYXIsICItIiwgZm9ybWF0KGFzLkRhdGUobG9uZyRQdXJjaGFzZURhdGUpLCAiJW0iKSwgIi0wMSIpKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAzMCwgZmlsbD0ieWVsbG93IiwgY29sb3I9IndoaXRlIikgKw0KICBsYWJzKHRpdGxlPSJT4buRIGdpYW8gZOG7i2NoIHRoZW8gdGjDoW5nIiwgeD0iVGjDoW5nIiwgeT0iU+G7kSBnaWFvIGThu4tjaCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KUGjDom4gdMOtY2ggdGhlbyB0aOG7nWkgZ2lhbiBjaG8gdGjhuqV5IG7Eg20gMjAwOSBnaGkgbmjhuq1uIHPhu5EgbMaw4bujbmcgZ2lhbyBk4buLY2ggY2FvIG5o4bqldCB24bubaSA5LjMzMiDEkcahbiBow6BuZywgdHJvbmcga2hpIG7Eg20gMjAwOCBjw7Mga2hv4bqjbmcgNC42OTIgxJHGoW4uIE7Eg20gMjAwNyBjaOG7iSDEkcOzbmcgZ8OzcCBt4buZdCBwaOG6p24gbmjhu48gKDM1IMSRxqFuIGjDoG5nKSBkbyBk4buvIGxp4buHdSBi4bqvdCDEkeG6p3UgdOG7qyBjdeG7kWkgbsSDbS4gxJBp4buBdSBuw6B5IGNobyB0aOG6pXkgdOG6rXAgZOG7ryBsaeG7h3UgY2jhu6cgeeG6v3UgdOG6rXAgdHJ1bmcgdsOgbyBnaWFpIMSRb+G6oW4gMjAwOOKAkzIwMDkuDQoNCkJp4buDdSDEkeG7kyB04bqnbiBzdeG6pXQgZ2lhbyBk4buLY2ggdGhlbyB0aMOhbmcgdHJvbmcga2hv4bqjbmcgdGjhu51pIGdpYW4gbsOgeSBjaG8gdGjhuqV5IHh1IGjGsOG7m25nIHTEg25nIGThuqduIHRyb25nIG7Eg20gMjAwOSwgxJHhurdjIGJp4buHdCBu4buVaSBi4bqtdCB04burIHRow6FuZyA1IMSR4bq/biB0aMOhbmcgMTAsIHbhu5tpIHPhu5EgbMaw4bujbmcgZ2lhbyBk4buLY2gg4bufIG3hu6ljIGNhby4gxJDDoW5nIGNow7ogw70sIGdpYWkgxJFv4bqhbiBjdeG7kWkgbsSDbSAyMDA4IHbDoCDEkeG6p3UgbsSDbSAyMDA5IGPDsyBt4bupYyB0xINuZyB24buNdCwgY8OzIHRo4buDIGxpw6puIHF1YW4gxJHhur9uIGPDoWMgY2jGsMahbmcgdHLDrG5oIGtodXnhur9uIG3Do2kgdGhlbyBtw7lhIGzhu4UgaOG7mWkgaG/hurdjIHPhu7EgZ2lhIHTEg25nIGzGsOG7o25nIGtow6FjaCBow6BuZy4NCg0KVGjDtG5nIHRpbiB24buBIHRo4budaSBnaWFuIGtow7RuZyBjaOG7iSBwaOG6o24gw6FuaCDEkeG7mSBiYW8gcGjhu6cgdGjhu51pIGdpYW4gY+G7p2EgZOG7ryBsaeG7h3UsIG3DoCBjw7JuIGfhu6NpIMO9IHbhu4EgeHUgaMaw4bubbmcgdMSDbmcgdHLGsOG7n25nIHThuqFpIMSRaeG7g20gYsOhbiDigJMga2jhuqMgbsSDbmcgxJHhur9uIHThu6sgdmnhu4djIG3hu58gcuG7mW5nIGjhu4cgdGjhu5FuZyBiw6FuIGzhursgaG/hurdjIHPhu7EgZ2lhIHTEg25nIG5odSBj4bqndSB0acOqdSBkw7luZyB0cm9uZyBnaWFpIMSRb+G6oW4gbsOgeS4NCg0KIyMjIDIuMi4yIEN1c3RvbSBJRA0KYGBge3J9DQp0b3RhbF9jdXN0b21lcnMgPC0gbGVuZ3RoKHVuaXF1ZShkYXRhJEN1c3RvbWVySUQpKQ0KdG90YWxfdHJhbnNhY3Rpb25zIDwtIG5yb3cobG9uZykNCmF2Z190cmFuc19wZXJfY3VzdCA8LSB0b3RhbF90cmFuc2FjdGlvbnMgLyB0b3RhbF9jdXN0b21lcnMNCmxpc3QodG90YWxfdHJhbnNhY3Rpb25zPXRvdGFsX3RyYW5zYWN0aW9ucywNCiAgICAgdG90YWxfY3VzdG9tZXJzPXRvdGFsX2N1c3RvbWVycywNCiAgICAgYXZnX3RyYW5zX3Blcl9jdXN0b21lcj1yb3VuZChhdmdfdHJhbnNfcGVyX2N1c3QsMikpDQpgYGANClThu5VuZyBj4buZbmcgY8OzIDE0MDU5IGdpYW8gZOG7i2NoIMSRxrDhu6NjIGdoaSBuaOG6rW4gdOG7qyA1NDA0IGtow6FjaCBow6BuZyBraMOhYyBuaGF1IHRyb25nIGtob+G6o25nIHRo4budaSBnaWFuIGfhuqduIDIgbsSDbS4gVHJ1bmcgYsOsbmgsIG3hu5dpIGtow6FjaCBow6BuZyB0aOG7sWMgaGnhu4duIGtob+G6o25nIHIgYXZnX3RyYW5zX3Blcl9jdXN0IGdpYW8gZOG7i2NoLg0KDQrEkGnhu4F1IG7DoHkgY2hvIHRo4bqleSBwaOG6p24gbOG7m24ga2jDoWNoIGjDoG5nIGNo4buJIG11YSBow6BuZyAx4oCTMiBs4bqnbiwgcGjhuqNuIMOhbmggbeG7qWMgxJHhu5kgxJFhIGThuqFuZyBjYW8gdHJvbmcgdOG6rXAga2jDoWNoIGjDoG5nLiBN4buZdCBz4buRIHRyxrDhu51uZyBo4bujcCBjw7MgdGjhu4MgbMOgIGtow6FjaCBow6BuZyBxdWF5IGzhuqFpLCBjaG8gdGjhuqV5IGThuqV1IGhp4buHdSBiYW4gxJHhuqd1IGPhu6dhIG3hu6ljIMSR4buZIHRydW5nIHRow6BuaCwgdHV5IG5oacOqbiBk4buvIGxp4buHdSBjaMawYSBjdW5nIGPhuqVwIMSR4bqneSDEkeG7pyB0aMO0bmcgdGluIMSR4buDIMSRw6FuaCBnacOhIHRy4buxYyB0aeG6v3AgeeG6v3UgdOG7kSBuw6B5Lg0KDQpU4bu3IGzhu4cgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgY2FvIHNvIHbhu5tpIHThu5VuZyBz4buRIGdpYW8gZOG7i2NoIGNobyB0aOG6pXkgdGjhu4sgdHLGsOG7nW5nIGPDsyBz4buxIHRoYW0gZ2lhIGPhu6dhIG5oaeG7gXUgbmfGsOG7nWkgbXVhIGtow6FjIG5oYXUsIMSR4bq3YyB0csawbmcgY2hvIG3hu5l0IGjhu4cgdGjhu5FuZyBiw6FuIGzhursgY8OzIHBo4bqhbSB2aSB0aeG6v3AgY+G6rW4gcuG7mW5nIHbDoCDEkWEgZOG6oW5nIHbhu4EgxJHhu5FpIHTGsOG7o25nIGtow6FjaCBow6BuZy4NCg0KIyMjIDIuMi4zIEdlbmRlcg0KYGBge3J9DQp0aG9uZ19rZV9kaW5oX3RpbmggPC0gZnVuY3Rpb24oZGF0YSwgdmFyX25hbWUpIHsNCiAgIyBDaGVjayBpZiB2YXJpYWJsZSBleGlzdHMgaW4gZGF0YQ0KICBpZiAoIXZhcl9uYW1lICVpbiUgbmFtZXMoZGF0YSkpIHsNCiAgICBzdG9wKCJWYXJpYWJsZSBkb2VzIG5vdCBleGlzdCBpbiBkYXRhIikNCiAgfQ0KICANCiAgIyBDb252ZXJ0IHRvIGZhY3RvciBpZiBuZWVkZWQNCiAgdmFyaWFibGUgPC0gYXMuZmFjdG9yKGRhdGFbW3Zhcl9uYW1lXV0pDQogIA0KICAjIGZyZXENCiAgZnJlcSA8LSB0YWJsZSh2YXJpYWJsZSkNCiAgcGVyY2VudCA8LSBwcm9wLnRhYmxlKGZyZXEpICogMTAwDQogIA0KICAjIHJlc3VsdA0KICByZXN1bHQgPC0gZGF0YS5mcmFtZSgNCiAgICBHaWFfdHJpID0gbmFtZXMoZnJlcSksDQogICAgVGFuX3NvID0gYXMudmVjdG9yKGZyZXEpLA0KICAgIFRhbl9zdWF0ID0gcm91bmQoYXMudmVjdG9yKHBlcmNlbnQpLCAyKQ0KICApDQogIHJldHVybihyZXN1bHQpDQp9DQoNCmBgYA0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQp0aG9uZ19rZV9kaW5oX3RpbmgobG9uZywgJ0dlbmRlcicpDQpnZ3Bsb3QoZGF0YSA9IGxvbmcsIGFlcyh4ID0gIiIsIGZpbGwgPSBHZW5kZXIpKSArDQogIGdlb21fYmFyKHdpZHRoID0gMSkgKw0KICBjb29yZF9wb2xhcigieSIpICsNCiAgbGFicyh0aXRsZSA9ICJHaeG7m2kgdMOtbmgiKSArDQogIHRoZW1lX3ZvaWQoKQ0KYGBgDQpQaMOibiBi4buRIGdp4bubaSB0w61uaCB0cm9uZyBi4buZIGThu68gbGnhu4d1IGtow6EgxJHhu5NuZyDEkeG7gXUsIHbhu5tpIHPhu7EgY2jDqm5oIGzhu4djaCBuaOG7jyBnaeG7r2EgaGFpIG5ow7NtIGdp4bubaSB0w61uaDogbuG7ryAoRikgdsOgIG5hbSAoTSkuDQoNCsSQaeG7gXUgbsOgeSBjaG8gdGjhuqV5IGThu68gbGnhu4d1IGPDsyB0w61uaCDEkeG6oWkgZGnhu4duIGdp4bubaSB0xrDGoW5nIMSR4buRaSB04buRdCwgZ2nDunAgZ2nhuqNtIHRoaeG7g3Ugbmd1eSBjxqEgdGhpw6puIGzhu4djaCBkbyBnaeG7m2kgdMOtbmggdHJvbmcgcXXDoSB0csOsbmggcGjDom4gdMOtY2ggdsOgIHN1eSBsdeG6rW4gdGjhu5FuZyBrw6ouDQoNCiMjIyAyLjIuNCBNYXJpdGFsU3RhdHVzDQoNCmBgYHtyfQ0KdGhvbmdfa2VfZGluaF90aW5oKGxvbmcsJ01hcml0YWxTdGF0dXMnKQ0KYGBgDQpH4bqnbiBt4buZdCBu4butYSBraMOhY2ggaMOgbmcgbMOgIMSR4buZYyB0aMOibiwgY8OybiBs4bqhaSBsw6AgxJHDoyBr4bq/dCBow7RuLg0KDQpU4bu3IGzhu4cgbsOgeSBjxaluZyB0xrDGoW5nIMSR4buRaSBjw6JuIGLhurFuZywgZ2nDunAgc28gc8OhbmggaMOgbmggdmkgdGhlbyB0w6xuaCB0cuG6oW5nIGjDtG4gbmjDom4gbcOgIGtow7RuZyBsbyB0aGnDqm4gbOG7h2NoIGThu68gbGnhu4d1Lg0KDQpgYGB7cn0NCmdncGxvdChsb25nLCBhZXMoeCA9IE1hcml0YWxTdGF0dXMsIGZpbGw9TWFyaXRhbFN0YXR1cykpICsNCiAgZ2VvbV9iYXIoY29sb3I9IndoaXRlIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YygieWVsbG93Iiwic2t5Ymx1ZSIpKSArDQogIGxhYnModGl0bGU9IlBow6JuIHBo4buRaSBraMOhY2ggaMOgbmcgdGhlbyBUw6xuaCB0cuG6oW5nIGjDtG4gbmjDom4iLCANCiAgICAgICB4PSJUw6xuaCB0cuG6oW5nIiwgeT0iU+G7kSBsxrDhu6NuZyBnaWFvIGThu4tjaCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMjIDIuMi41IEhvbWVvd25lcg0KDQpgYGB7cn0NCnRob25nX2tlX2RpbmhfdGluaChsb25nLCAnSG9tZW93bmVyJykNCmBgYA0KUGjhuqduIGzhu5tuIGtow6FjaCBow6BuZyBsw6AgbmfGsOG7nWkgc+G7nyBo4buvdSBuaMOgIChjaGnhur9tIH42MCUpLg0KDQrEkGnhu4F1IG7DoHkgY8OzIHRo4buDIOG6o25oIGjGsOG7n25nIMSR4bq/biB0aMOzaSBxdWVuIGNoaSB0acOqdSBob+G6t2MgxrB1IHRpw6puIG11YSBz4bqvbSwgbMOgIG3hu5l0IMSR4bq3YyDEkWnhu4NtIHF1YW4gdHLhu41uZyDEkeG7gyBwaMOibiBraMO6YyBraMOhY2ggaMOgbmcuDQoNCmBgYHtyfQ0KZ2dwbG90KGRhdGEgPSBsb25nLCBhZXMoeCA9IEhvbWVvd25lcikpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJ5ZWxsb3ciKSArDQogIGdlb21fdGV4dChzdGF0ID0gImNvdW50IiwgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgdmp1c3QgPSAtMC41KSArDQogIGxhYnModGl0bGUgPSAiU+G7nyBo4buvdSBuaMOgIiwNCiAgICAgICB4ID0gIkPDsyBob+G6t2Mga2jDtG5nIiwgeSA9ICJU4bqnbiBz4buRIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQojIyMgMi4yLjYgQ2hpbGRyZW4NCg0KKipDaGlsZHJlbioqIGNo4buJIHPhu5EgY29uIG3DoCBraMOhY2ggaMOgbmcgY8OzLCBiaeG6v24gbsOgeSBsw6AgMSBiaeG6v24gxJHhu4tuaCBsxrDhu6NuZyB2w6wgY8OhYyBnacOhIHRy4buLIGPhu6dhIG7DsyBjw7MgdGjhu4Mgc28gc8OhbmggduG7m2kgbmhhdS4NCmBgYHtyfQ0KbGlicmFyeShza2ltcikNCnNraW0obG9uZyRDaGlsZHJlbikNCmBgYA0KVHJ1bmcgYsOsbmggMi41MyB2w6AgdHJ1bmcgduG7iyAzIGNobyB0aOG6pXkgZOG7ryBsaeG7h3UgY8OzIHh1IGjGsOG7m25nIHBow6JuIGLhu5EgxJHhu4F1Lg0KDQpLaMO0bmcgY8OzIGThuqV1IGhp4buHdSBs4buHY2ggbmdoacOqbSB0cuG7jW5nIGhv4bq3YyBjw7MgZ2nDoSB0cuG7iyBuZ2/huqFpIGzhu4cgYuG6pXQgdGjGsOG7nW5nLg0KDQpgYGB7cn0NCg0KZ2dwbG90KGxvbmcsIGFlcyh4ID0gYXMuZmFjdG9yKENoaWxkcmVuKSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJ5ZWxsb3ciKSArDQogIGdlb21fdGV4dChzdGF0ID0gImNvdW50IiwgYWVzKGxhYmVsID0gLi5jb3VudC4uKSwgdmp1c3QgPSAtMC41KSArDQogIGxhYnMoeCA9ICJT4buRIGNvbiIsIHkgPSAiU+G7kSBraMOhY2ggaMOgbmciLCB0aXRsZSA9ICJQaMOibiBi4buRIHPhu5EgY29uIGPhu6dhIGtow6FjaCBow6BuZyIpDQoNCg0KYGBgDQoNCiMjIyAyLjIuNyBBbm51YWxJbmNvbWUNCg0KYGBge3J9DQp0aG9uZ19rZV9kaW5oX3RpbmgobG9uZywgJ0FubnVhbEluY29tZScpDQpgYGANCg0KJDMwSyAtICQ1MEsgY2hp4bq/bSBoxqFuIDEvMyAoMzIuNzMlKSB04buVbmcgc+G7kSBxdWFuIHPDoXQg4oaSIGzDoCBuaMOzbSDEkcO0bmcgbmjhuqV0LiBL4bq/dCBo4bujcCB24bubaSAkMTBLIC0gJDMwSywgdGEgY8OzIGtob+G6o25nIDU1JSBuZ8aw4budaSB0aHXhu5ljIG5ow7NtIGTGsOG7m2kgJDUwSyBjaG8gdGjhuqV5IG3hu6ljIHRodSBuaOG6rXAgdHJ1bmcgYsOsbmggaG/hurdjIHRo4bqlcCBsw6AgcGjhu5UgYmnhur9uLg0KDQpOaMOzbSB0aHUgbmjhuq1wIGNhbyAodHLDqm4gJDkwSykgR+G7k20gJDkwSyAtICQxMTBLLCAkMTEwSyAtICQxMzBLLCAkMTMwSyAtICQxNTBLLCAkMTUwSysuIFThu5VuZyB04bqnbiBzdeG6pXQgY2jhu4kga2hv4bqjbmcgMTYuMjglIGNobyB0aOG6pXkgbmjDs20gdGh1IG5o4bqtcCBjYW8ga2jDoSBuaOG7jy4NCg0KYGBge3J9DQpnZ3Bsb3QobG9uZywgYWVzKHggPSBBbm51YWxJbmNvbWUpKSArDQogIGdlb21fYmFyKGZpbGwgPSAieWVsbG93IiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnMoeCA9ICJLaG/huqNuZyB0aHUgbmjhuq1wIiwgeSA9ICJU4bqnbiBzdeG6pXQiLCB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgdOG6p24gc3XhuqV0IHRodSBuaOG6rXAgaMOgbmcgbsSDbSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQojIyMgMi4yLjggQ2l0eQ0KDQpgYGB7cn0NCnRob25nX2tlX2RpbmhfdGluaChsb25nLCdDaXR5JykNCmBgYA0KDQpTYWxlbSBjw7MgdOG6p24gc+G7kSBjYW8gbmjhuqV0ICgxMzg2IGzhuqduIHh14bqldCBoaeG7h24sIGNoaeG6v20gOS44NikuIFRp4bq/cCB0aGVvIGzDoCBUYWNvbWEgKDEyNTcgbOG6p24sIDguOTQlKS4NCg0KR3VhZGFsYWphcmEgY8OzIHThuqduIHPhu5EgdGjhuqVwIG5o4bqldCAoNzUgbOG6p24sIGNo4buJIDAuNTMlKS4NCg0KTmjDrG4gY2h1bmcsIHPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nIOG7nyBjw6FjIHRow6BuaCBwaOG7kSBwaMOibiBi4buRIGtow6EgY2jDqm5oIGzhu4djaCwgxJFp4buBdSBuw6B5IGPDsyB0aOG7gyBk4bqrbiDEkeG6v24gbmjhu69uZyBr4bq/dCBxdeG6oyB0aGnDqm4gbOG7h2NoIHRyb25nIHZp4buHYyBwaMOibiB0w61jaCB2w6Agc28gc8OhbmggZ2nhu69hIGPDoWMgdGjDoG5oIHBo4buRLg0KDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbmcsIGFlcyh4ID0gQ2l0eSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICJ5ZWxsb3ciLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicyh4ID0gIlRow6BuaCBwaOG7kSIsIHkgPSAiVOG6p24gc+G7kSIsIHRpdGxlID0gIkJp4buDdSDEkeG7kyB04bqnbiBz4buRIGtow6FjaCBow6BuZyDhu58gY8OhYyB0aMOgbmggcGjhu5EiKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KIyMjIDIuMi45IFN0YXRlb3JQcm92aW5jZQ0KYGBge3J9DQp0aG9uZ19rZV9kaW5oX3RpbmgobG9uZywnU3RhdGVvclByb3ZpbmNlJykNCmBgYA0KDQpXQSAoV2FzaGluZ3RvbikgY2hp4bq/bSB04bu3IGzhu4cgw6FwIMSR4bqjbyAoMzIuNDglLCA0NTY3IGzhuqduIHh14bqldCBoaeG7h24pLCBDQSAoQ2FsaWZvcm5pYSkgxJHhu6luZyB0aOG7qSBoYWkgKDE5LjQ0JSwgMjczMyBs4bqnbiksIE9SIChPcmVnb24pIMSR4bupbmcgdGjhu6kgYmEgKDE2LjA5JSwgMjI2MiBs4bqnbikuDQo9PiBCYSBiYW5nIG7DoHkgdGh14buZYyBUw6J5IELhuq9jIFRow6FpIELDrG5oIETGsMahbmcgY+G7p2EgSG9hIEvhu7MgY2hp4bq/bSB04buVbmcgY+G7mW5nIDY4LjAxJSBk4buvIGxp4buHdQ0KDQpDw6FjIHThu4luaCBNZXhpY28gKERGLCBHdWVycmVybywgSmFsaXNjbywgVmVyYWNydXosIFl1Y2F0YW4sIFphY2F0ZWNhcykgY8OzIHThuqduIHN14bqldCB0aOG6pXAgaMahbiBuaGnhu4F1LiBU4buVbmcgY+G7mW5nIGPDoWMgdOG7iW5oIE1leGljbyBjaOG7iSBjaGnhur9tIGtob+G6o25nIDI2LjIzJSBk4buvIGxp4buHdQ0KDQpL4bq/dCBxdeG6oyBuw6B5IGNobyB0aOG6pXkgc+G7sSB04bqtcCB0cnVuZyDEkeG7i2EgbMO9IHLDtSBy4buHdCB0cm9uZyB04bqtcCBk4buvIGxp4buHdSwgduG7m2kgcGjhuqduIGzhu5tuIGThu68gbGnhu4d1IMSR4bq/biB04burIGJhIGJhbmcgcGjDrWEgVMOieSBC4bqvYyBIb2EgS+G7sy4NCg0KYGBge3J9DQpnZ3Bsb3QobG9uZywgYWVzKHggPSBTdGF0ZW9yUHJvdmluY2UpKSArDQogIGdlb21fYmFyKGZpbGwgPSAieWVsbG93IiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnMoeCA9ICJCYW5nIiwgeSA9ICJU4bqnbiBz4buRIiwgdGl0bGUgPSAiQmnhu4N1IMSR4buTIHThuqduIHPhu5Ega2jDoWNoIGjDoG5nIOG7nyBjw6FjIGJhbmciKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KIyMjIDIuMi4xMCBDb3VudHJ5DQoNCmBgYHtyfQ0KdGhvbmdfa2VfZGluaF90aW5oKGxvbmcsJ0NvdW50cnknKQ0KYGBgDQoNCkThu68gbGnhu4d1IGNobyB0aOG6pXkgc+G7sSBwaMOibiBi4buRIGtow7RuZyDEkeG7k25nIMSR4buBdSBnaeG7r2EgY8OhYyBxdeG7kWMgZ2lhLCB24bubaSBIb2EgS+G7syAoVVNBKSBjaGnhur9tIHThu7cgbOG7hyDDoXAgxJHhuqNvICg2OC4wMSUsIDkuNTYyIGzhuqduIHh14bqldCBoaeG7h24pLCB0aeG6v3AgdGhlbyBsw6AgTWV4aWNvICgyNi4yMyUsIDMuNjg4IGzhuqduKSB2w6AgQ2FuYWRhICg1Ljc1JSwgODA5IGzhuqduKS4gxJBp4buBdSBuw6B5IGNobyB0aOG6pXkgdOG6rXAgZOG7ryBsaeG7h3UgY2jhu6cgeeG6v3UgdOG6rXAgdHJ1bmcgdsOgbyB0aOG7iyB0csaw4budbmcgTeG7uSwgY8OzIHRo4buDIGRvIGThu68gbGnhu4d1IMSRxrDhu6NjIHRodSB0aOG6rXAgdOG7qyBuZ3Xhu5NuIMawdSB0acOqbiBjw6FjIGdpYW8gZOG7i2NoIGhv4bq3YyBz4buxIGtp4buHbiB04bqhaSBIb2EgS+G7sy4NCg0KYGBge3J9DQpnZ3Bsb3QobG9uZywgYWVzKHggPSBDb3VudHJ5KSkgKw0KICBnZW9tX2JhcihmaWxsPSJ5ZWxsb3ciLCBjb2xvcj0id2hpdGUiKSArDQogIGxhYnModGl0bGU9IlBow6JuIHBo4buRaSBnaWFvIGThu4tjaCB0aGVvIHF14buRYyBnaWEiLCB4PSJRdeG7kWMgZ2lhIiwgeT0iU+G7kSBnaWFvIGThu4tjaCIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KIyMjIDIuMi4xMSBQcm9kdWN0RmFtaWx5DQpgYGB7cn0NCnRob25nX2tlX2RpbmhfdGluaChsb25nLCdQcm9kdWN0RmFtaWx5JykNCmBgYA0KDQpE4buvIGxp4buHdSBjaG8gdGjhuqV5IHPhuqNuIHBo4bqpbSB0aOG7sWMgcGjhuqltIChGb29kKSBjaGnhur9tIHThu7cgbOG7hyDDoXAgxJHhuqNvICg3Mi4yMiUsIDEwLjE1MyBs4bqnbiB4deG6pXQgaGnhu4duKSwgdHJvbmcga2hpIG5ow7NtIMSR4buTIHXhu5FuZyAoRHJpbmspIGNo4buJIGNoaeG6v20gOC44OSUgdsOgIHPhuqNuIHBo4bqpbSBraMO0bmcgdGnDqnUgZMO5bmcgKE5vbi1Db25zdW1hYmxlKSBjaGnhur9tIDE4Ljg5JS4gU+G7sSBjaMOqbmggbOG7h2NoIHLDtSBy4buHdCBuw6B5IChGb29kIGfhuqVwIDggbOG6p24gRHJpbmsgdsOgIGfhuqduIDQgbOG6p24gTm9uLUNvbnN1bWFibGUpIHBo4bqjbiDDoW5oIHRy4buNbmcgdMOibSBj4bunYSB04bqtcCBk4buvIGxp4buHdSBjaOG7pyB54bq/dSB04bqtcCB0cnVuZyB2w6BvIGPDoWMgc+G6o24gcGjhuqltIHRo4buxYyBwaOG6qW0uDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbmcsIGFlcyh4ID0gUHJvZHVjdEZhbWlseSwgZmlsbD1Qcm9kdWN0RmFtaWx5KSkgKw0KICBnZW9tX2Jhcihjb2xvcj0id2hpdGUiKSArDQogIGxhYnModGl0bGU9IlPhu5EgZ2lhbyBk4buLY2ggdGhlbyBQcm9kdWN0IEZhbWlseSIsIA0KICAgICAgIHg9Ik5ow7NtIHPhuqNuIHBo4bqpbSIsIHk9IlPhu5EgZ2lhbyBk4buLY2giKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIyAyLjIuMTIgUHJvZHVjdERlcGFydG1lbnQNCg0KYGBge3J9DQp0aG9uZ19rZV9kaW5oX3RpbmgobG9uZywnUHJvZHVjdERlcGFydG1lbnQnKQ0KYGBgDQoNClThuq1wIHRydW5nIHbDoG8gdGjhu7FjIHBo4bqpbSB0xrDGoWkgdsOgIMSR4buTIGdpYSBk4bulbmc6IEhhaSBuaMOzbSBQcm9kdWNlIHbDoCBIb3VzZWhvbGQgY2hp4bq/bSB04buVbmcgY+G7mW5nIGfhuqduIDI1JSBk4buvIGxp4buHdSwgY2hvIHRo4bqleSDEkcOieSBjw7MgdGjhu4MgbMOgIGPDoWMgbeG6t3QgaMOgbmcgY2jhu6cgbOG7sWMuDQoNClPhu7EgdGhp4bq/dSBjw6JuIMSR4buRaSByw7UgcuG7h3Q6IFRyb25nIGtoaSBt4buZdCBz4buRIG5ow7NtIG5oxrAgUHJvZHVjZSBjaGnhur9tIHThu5tpIDE0LjE4JSB0aMOsIG5oaeG7gXUgbmjDs20ga2jDoWMgbmjGsCBNZWF0LCBTZWFmb29kIGzhuqFpIGNoaeG6v20gY2jGsGEgdOG7m2kgMSUuDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbmcsIGFlcyh4ID0gUHJvZHVjdERlcGFydG1lbnQpKSArDQogIGdlb21fYmFyKGZpbGwgPSAieWVsbG93IiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnMoeCA9ICJT4bqjbiBwaOG6qW0iLCB5ID0gIlThuqduIHPhu5EiLCB0aXRsZSA9ICJC4buZIHBo4bqtbiBz4bqjbiBwaOG6qW0ga2jDoWNoIGjDoG5nIG11YSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQojIyMgMi4yLjEzIFByb2R1Y3RDYXRlZ29yeQ0KYGBge3J9DQp0aG9uZ19rZV9kaW5oX3RpbmgobG9uZywnUHJvZHVjdENhdGVnb3J5JykNCmBgYA0KDQpE4buvIGxp4buHdSBjaG8gdGjhuqV5IFNuYWNrIEZvb2RzICgxMS4zOCUpIHbDoCBWZWdldGFibGVzICgxMi4yOSUpIGzDoCBoYWkgZGFuaCBt4bulYyBwaOG7lSBiaeG6v24gbmjhuqV0LCBjaGnhur9tIHThu5VuZyBj4buZbmcgZ+G6p24gMS80IGThu68gbGnhu4d1LiBDw6FjIGRhbmggbeG7pWMgxJHDoW5nIGNow7ogw70ga2jDoWMgYmFvIGfhu5NtIERhaXJ5ICg2LjQyJSksIEZydWl0ICg1LjQ0JSksIHbDoCBNZWF0ICg1LjQxJSksIHRyb25nIGtoaSBuaGnhu4F1IGRhbmggbeG7pWMgbmjGsCDEkeG7kyBo4bqjaSBz4bqjbiDEkcOzbmcgaOG7mXAgKENhbm5lZCBPeXN0ZXJzIDAuMjUlLCBDYW5uZWQgU2hyaW1wIDAuMjclKSBob+G6t2MgY8OhYyBz4bqjbiBwaOG6qW0gw610IHBo4buVIGJp4bq/biAoQ2FuZGxlcyAwLjMyJSwgTWlzY2VsbGFuZW91cyAwLjMwJSkgY8OzIHThuqduIHN14bqldCBy4bqldCB0aOG6pXAuIFPhu7EgcGjDom4gYuG7kSBuw6B5IHBo4bqjbiDDoW5oIHThuq1wIHRydW5nIGNow61uaCB2w6BvIGPDoWMgbeG6t3QgaMOgbmcgdGnDqnUgZMO5bmcgbmhhbmggdsOgIHRo4buxYyBwaOG6qW0gdMawxqFpIHPhu5FuZywgdHJvbmcga2hpIGPDoWMgc+G6o24gcGjhuqltIMSR4bq3YyBiaeG7h3QgaG/hurdjIMOtdCBwaOG7lSBiaeG6v24gY2jhu4kgY2hp4bq/bSB04bu3IGzhu4cgbmjhu48uDQoNCmBgYHtyfQ0KZ2dwbG90KGxvbmcsIGFlcyh4ID0gUHJvZHVjdENhdGVnb3J5KSkgKw0KICBnZW9tX2JhcihmaWxsID0gInllbGxvdyIsIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKHggPSAiU+G6o24gcGjhuqltIiwgeSA9ICJU4bqnbiBz4buRIiwgdGl0bGUgPSAiRGFuaCBt4bulYyBz4bqjbiBwaOG6qW0ga2jDoWNoIGjDoG5nIG11YSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQojIyMgMi4yLjE0IFVuaXRzU29sZA0KDQpgYGB7cn0NCnNraW0obG9uZyRVbml0c1NvbGQpDQpgYGANCg0KS+G6v3QgcXXhuqMgY2hvIHRo4bqleSB0cnVuZyBiw6xuaCAxIMSRxqFuIG11YSBz4bq9IG11YSBraG/huqNuZyA0LjA4IMSRxqFuIHbhu4sgc+G6o24gcGjhuqltLCBnacOhIHRy4buLIMSR4buZIGzhu4djaCBjaHXhuqluIGLhurFuZyAxLjE3IGNobyB0aOG6pXkgbeG7qWMgxJHhu5kgcGjDom4gdMOhbiB0xrDGoW5nIMSR4buRaSB0aOG6pXAgeHVuZyBxdWFuaCBnacOhIHRy4buLIHRydW5nIGLDrG5oLg0KDQpDw7MgdGjhu4MgdGjhuqV5IHLhurFuZyBjw7MgNTAlIGdpYW8gZOG7i2NoIGLDoW4gdOG7qyAzLTUgxJHGoW4gduG7iyAoa2hv4bqjbmcgdOG7qSBwaMOibiB24buLKSB2w6AgNzUlIGdpYW8gZOG7i2NoIGLDoW4g4omkNSDEkcahbiB24buLLg0KYGBge3J9DQpib3hwbG90KGxvbmckVW5pdHNTb2xkLCBob3Jpem9udGFsID0gVFJVRSwgY29sID0gInllbGxvdyIsDQogICAgICAgIG1haW4gPSAiUGjDom4gcGjhu5FpIFVuaXRzU29sZCIsIHhsYWIgPSAiU+G7kSDEkcahbiB24buLIGLDoW4gcmEiKQ0KYGBgDQoNCiMjIyAyLjIuMTUgUmV2ZW51ZQ0KDQpgYGB7cn0NCnNraW0obG9uZyRSZXZlbnVlKQ0KYGBgDQoNCkJp4bq/biBSZXZlbnVlIChkb2FuaCB0aHUpIGNobyB0aOG6pXkgbeG7mXQgcGjDom4gcGjhu5FpIGzhu4djaCBwaOG6o2kgcsO1IHLhu4d0IHbhu5tpIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggMTMuMDAgdsOgIHRydW5nIHbhu4sgMTEuMjUsIHBo4bqjbiDDoW5oIHPhu7EgaGnhu4duIGRp4buHbiBj4bunYSBuaGnhu4F1IGdpYW8gZOG7i2NoIGPDsyBkb2FuaCB0aHUgY2FvIGvDqW8gdHJ1bmcgYsOsbmggbMOqbi4gxJDhu5kgbOG7h2NoIGNodeG6qW4gbOG7m24gKDguMjIpIGPDuW5nIGtob+G6o25nIGJp4bq/biB0aGnDqm4gcuG7mW5nICh04burIDAuNTMgxJHhur9uIDU2LjcpIGNobyB0aOG6pXkgbeG7qWMgxJHhu5kgY2jDqm5oIGzhu4djaCDEkcOhbmcga+G7gyBnaeG7r2EgY8OhYyBnaWFvIGThu4tjaC4NCg0KYGBge3J9DQpib3hwbG90KGxvbmckUmV2ZW51ZSwgaG9yaXpvbnRhbCA9IFRSVUUsIGNvbCA9ICJ5ZWxsb3ciLA0KICAgICAgICBtYWluID0gIlBow6JuIHBo4buRaSBSZXZlbnVlIiwgeGxhYiA9ICJEb2FuaCB0aHUiKQ0KYGBgDQoNCkJveHBsb3QgbsOgeSAxIGzhuqduIG7hu69hIHjDoWMgbmjhuq1uIHLDtSByw6BuZyBwaMOibiBwaOG7kWkgbOG7h2NoIHBo4bqjaSBt4bqhbmggY+G7p2EgYmnhur9uIFJldmVudWUuIEPDoWMgxJFp4buDbSByacOqbmcgbOG6uyB4deG6pXQgaGnhu4duIOG7nyB2w7luZyAzMC01MCwgdHLDuW5nIGto4bubcCB24bubaSBnacOhIHRy4buLIHThu5FpIMSRYSA1Ni43IHRyb25nIHRo4buRbmcga8OqLiBLaG/huqNuZyBjw6FjaCB4YSB04burIHA3NSAofjE3KSDEkeG6v24gY8OhYyDEkWnhu4NtIG7DoHkgY2hvIHRo4bqleSBjaMO6bmcgdGjhu7FjIHPhu7EgbMOgIGPDoWMgZ2nDoSB0cuG7iyBj4buxYyB0cuG7iw0K