TASK 1: TÓM TẮT QUYỂN SÁCH “2019_Generalized Linear Models With Examples in R”

CHƯƠNG 1. GIỚI THIỆU MÔ HÌNH THỐNG KÊ

  • Trích dẫn câu nói của nhà thống kê người Anh:

“All models are wrong, but some are useful” — George E. P. Box

“Tất cả các mô hình đều sai, nhưng một số mô hình có ích.”

Ý nghĩa:

  • Không có mô hình nào phản ánh hoàn toàn thực tế một cách chính xác tuyệt đối. Bất kỳ mô hình thống kê nào cũng là một sự đơn giản hóa của thế giới thực.
  • Do đó, tất cả các mô hình đều sai ở một mức độ nào đó, vì chúng luôn bỏ qua hoặc giả định những điều không hoàn toàn đúng.

Vậy tại sao lại “có ích”?

  • Mặc dù sai, mô hình vẫn có thể giúp con người hiểu rõ xu hướng, mối quan hệ, hoặc đưa ra dự đoán hợp lý dựa trên dữ liệu.
  • Một mô hình được xem là “có ích” khi nó hỗ trợ tốt cho mục đích cụ thể, chẳng hạn như:
    • Dự đoán chính xác
    • Hiểu rõ cơ chế sinh dữ liệu
    • Hỗ trợ ra quyết định

Bài học rút ra:

  • Không nên tuyệt đối hóa mô hình.
  • Nên dùng mô hình như một công cụ hỗ trợ tư duy và phân tích, thay vì là chân lý cuối cùng.
  • Luôn cần kiểm tra giả định mô hình, đánh giá độ phù hợp, và diễn giải kết quả một cách cẩn trọng.

Ví dụ trong R:

model <- lm(mpg ~ wt, data = mtcars)
summary(model)
## 
## Call:
## lm(formula = mpg ~ wt, data = mtcars)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -4.5432 -2.3647 -0.1252  1.4096  6.8727 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  37.2851     1.8776  19.858  < 2e-16 ***
## wt           -5.3445     0.5591  -9.559 1.29e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 3.046 on 30 degrees of freedom
## Multiple R-squared:  0.7528, Adjusted R-squared:  0.7446 
## F-statistic: 91.38 on 1 and 30 DF,  p-value: 1.294e-10

1.1 Giới thiệu và tổng quan

Giới thiệu:

  • Chương này giới thiệu khái niệm cơ bản về mô hình thống kê như một cách để mô tả cả các đặc điểm ngẫu nhiên và có hệ thống của dữ liệu. Nó nhấn mạnh tầm quan trọng của việc sử dụng các mô hình để phân tích dữ liệu.

  • Là công cụ then chốt để hiểu và mô tả dữ liệu. Trong đó, mô hình tuyến tính tổng quát (GLM) là chủ đề chính của cuốn sách. Trước khi đi sâu vào GLM, chương này trình bày các khái niệm cơ bản giúp thiết lập ngôn ngữ, ký hiệu và phương pháp làm việc trong phân tích mô hình.

(“Data analysis: The need for models?” - Reese, 1986).

Mục đích của mô hình thống kê:

  • Mục đích của một một một hình ảnh hưởng đến cách nó được phát triển (Mục 1.9). Các mục đích có thể bao gồm mô tả, dự đoán và hiểu mối quan hệ giữa các biến.

1.2 Quy ước mô tả dữ liệu

Giới thiệu cách mô tả dữ liệu dưới dạng toán học:

  • Các biến phản hồi (\(y\)) và biến giải thích (\(x\)). Cách sắp xếp dữ liệu thành bảng quan sát và sử dụng ký hiệu chuẩn hóa để trình bày mô hình một cách thống nhất.
library(GLMsData)
data(lungcap)
summary(lungcap)
##       Age              FEV              Ht        Gender      Smoke        
##  Min.   : 3.000   Min.   :0.791   Min.   :46.00   F:318   Min.   :0.00000  
##  1st Qu.: 8.000   1st Qu.:1.981   1st Qu.:57.00   M:336   1st Qu.:0.00000  
##  Median :10.000   Median :2.547   Median :61.50           Median :0.00000  
##  Mean   : 9.931   Mean   :2.637   Mean   :61.14           Mean   :0.09939  
##  3rd Qu.:12.000   3rd Qu.:3.119   3rd Qu.:65.50           3rd Qu.:0.00000  
##  Max.   :19.000   Max.   :5.793   Max.   :74.00           Max.   :1.00000
str(lungcap)
## 'data.frame':    654 obs. of  5 variables:
##  $ Age   : int  3 4 4 4 4 4 4 5 5 5 ...
##  $ FEV   : num  1.072 0.839 1.102 1.389 1.577 ...
##  $ Ht    : num  46 48 48 48 49 49 50 46.5 49 49 ...
##  $ Gender: Factor w/ 2 levels "F","M": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Smoke : int  0 0 0 0 0 0 0 0 0 0 ...

1.3 Vẽ dữ liệu

Nhấn mạnh vai trò của việc trực quan hóa dữ liệu thông qua biểu đồ. Việc vẽ đồ thị giúp phát hiện xu hướng, bất thường và hiểu mối liên hệ giữa các biến trước khi mô hình hóa.

plot(FEV ~ Age, data = lungcap, main = "FEV vs Age", las = 1)

boxplot(FEV ~ Smoke + Gender, data = lungcap, las = 2)

interaction.plot(lungcap$Smoke, lungcap$Gender, lungcap$FEV,
                 xlab = "Smoke", ylab = "FEV", trace.label = "Gender", las = 1)

1.4 Mã hóa biến không phải số

Các biến phân loại (như giới tính, nhóm tuổi) cần được chuyển thành số để đưa vào mô hình. Mục này giải thích cách mã hóa chúng bằng biến giả (dummy variables) hoặc các phương pháp mã hóa khác.

lungcap$Gender <- factor(lungcap$Gender)
contrasts(lungcap$Gender)
##   M
## F 0
## M 1
lungcap$Smoke <- factor(lungcap$Smoke, levels = c(0, 1),
                        labels = c("Non-smoker", "Smoker"))
contrasts(lungcap$Smoke)
##            Smoker
## Non-smoker      0
## Smoker          1

1.5 Hai thành phần của mô hình thống kê

Mô hình thống kê gồm:\ - Thành phần hệ thống: mô tả xu hướng chính (predictor).\ - Thành phần ngẫu nhiên: mô tả sai số hoặc sự biến động không giải thích được.

model <- lm(FEV ~ Age + Ht + Gender + Smoke, data = lungcap)
summary(model)
## 
## Call:
## lm(formula = FEV ~ Age + Ht + Gender + Smoke, data = lungcap)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -1.37656 -0.25033  0.00894  0.25588  1.92047 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -4.456974   0.222839 -20.001  < 2e-16 ***
## Age          0.065509   0.009489   6.904 1.21e-11 ***
## Ht           0.104199   0.004758  21.901  < 2e-16 ***
## GenderM      0.157103   0.033207   4.731 2.74e-06 ***
## SmokeSmoker -0.087246   0.059254  -1.472    0.141    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4122 on 649 degrees of freedom
## Multiple R-squared:  0.7754, Adjusted R-squared:  0.774 
## F-statistic:   560 on 4 and 649 DF,  p-value: < 2.2e-16

1.6 Mô hình hồi quy

Giới thiệu lớp mô hình hồi quy – nền tảng cho tất cả các mô hình được trình bày trong sách. Mô hình hồi quy mô tả mối quan hệ tuyến tính giữa biến phản hồi và các biến giải thích.

1.7 Diễn giải mô hình

Trình bày cách hiểu và giải thích ý nghĩa của các hệ số hồi quy. Cho biết mức độ ảnh hưởng của từng biến giải thích đến biến phản hồi.

1.8 So sánh mô hình vật lý và mô hình thống kê

So sánh hai loại mô hình: vật lý (dựa vào định luật tự nhiên) và thống kê (dựa vào dữ liệu). Mô hình thống kê có tính linh hoạt hơn nhưng cũng phụ thuộc vào dữ liệu nhiều hơn.

1.9 Mục đích của mô hình thống kê

Tùy mục tiêu mà mô hình có thể được dùng để: mô tả, dự đoán, hoặc kiểm định giả thuyết. Mục tiêu ảnh hưởng đến cách xây dựng, lựa chọn và đánh giá mô hình.

1.10 Đánh giá mô hình: Chính xác và đơn giản

Một mô hình tốt cần vừa chính xác (dự đoán hoặc mô tả tốt dữ liệu), vừa đơn giản (ít biến, dễ hiểu). Đây là hai tiêu chí quan trọng trong lựa chọn mô hình.

1.11 Hiểu giới hạn của mô hình

Mô hình thống kê không thể nắm bắt toàn bộ thực tế. Việc phân biệt giữa dữ liệu thực nghiệm (có kiểm soát) và quan sát (không kiểm soát) là rất quan trọng để hiểu được tính hợp lệ của suy luận.

1.12 Khả năng khái quát hóa mô hình

Mô hình tốt cần có khả năng áp dụng vào dữ liệu mới hoặc tổng thể. Mục này thảo luận về sự liên kết giữa cách lấy mẫu dữ liệu và khả năng khái quát hóa của mô hình.

1.13 Giới thiệu sử dụng R cho mô hình hóa

R là công cụ chính được sử dụng xuyên suốt sách. Các ví dụ và phân tích mô hình trong sách đều được trình bày bằng R với mã nguồn đầy đủ.

names(lungcap)
## [1] "Age"    "FEV"    "Ht"     "Gender" "Smoke"
factor(lungcap$Gender)
##   [1] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
##  [38] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
##  [75] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
## [112] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
## [149] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
## [186] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
## [223] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
## [260] F F F F F F F F F F F F F F F F F F F F M M M M M M M M M M M M M M M M M
## [297] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
## [334] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
## [371] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
## [408] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
## [445] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
## [482] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
## [519] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M
## [556] M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M M F F F
## [593] F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F M
## [630] M M M M M M M M M M M M M M M M M M M M M M M M M
## Levels: F M
plot(lungcap$FEV ~ lungcap$Age)

CHƯƠNG 2. MÔ HÌNH HỒI QUY TUYẾN TÍNH

2.1 Giới thiệu

Mô hình hồi quy tuyến tính là một trong những công cụ cơ bản và phổ biến nhất trong thống kê. Mô hình này giúp xác định và định lượng mối quan hệ giữa một biến phản hồi (liên tục) và một hoặc nhiều biến giải thích (liên tục hoặc phân loại).

Mô hình hồi quy tuyến tính là nền tảng của nhiều mô hình thống kê. Chúng mô tả mối quan hệ tuyến tính giữa một biến phản hồi liên tục và một hoặc nhiều biến giải thích.

2.2 Định nghĩa mô hình hồi quy tuyến tính

Mô hình hồi quy tuyến tính tổng quát biểu diễn mối liên hệ tuyến tính giữa biến phản hồi và các biến giải thích. Biến sai số ε đại diện cho nhiễu loạn ngẫu nhiên và giả định phân phối chuẩn có trung bình 0 và phương sai ^2.

Mô hình hồi quy tuyến tính có dạng như sau:

\[ y_i = \beta_0 + \beta_1 x_{i1} + \cdots + \beta_p x_{ip} + \varepsilon_i,\quad \varepsilon_i \sim \mathcal{N}(0, \sigma^2) \]

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

Đây là trường hợp đặc biệt của mô hình hồi quy tuyến tính với chỉ một biến giải thích. Mục tiêu là ước lượng mối quan hệ tuyến tính giữa biến đầu vào x và biến đầu ra y.

library(GLMsData)
data(gestation)
x <- gestation$Age
y <- gestation$Weight
wts <- gestation$Births

2.3.1 Ước lượng bình phương nhỏ nhất (OLS)

Phương pháp này có dạng như sau:

\[ \min_{B_0, B_1} \sum_{i=1}^n (y_i - B_0 - B_1 x_i)^2 \]

beta0.A <- -0.9; beta1.A <- 0.1
mu.A <- beta0.A + beta1.A * x
SA <- sum(wts * (y - mu.A)^2); SA
## [1] 186.1106

2.3.2 Ước lượng hệ số:

Hệ số góc (slope) được tính theo công thức:

\[ \beta_1 = \frac{\sum_{i=1}^{n} (x_i - \bar{x})(y_i - \bar{y})}{\sum_{i=1}^{n} (x_i - \bar{x})^2} \]

Hệ số chặn (intercept) là:

\[ \beta_0 = \bar{y} - \beta_1 \bar{x} \]

xbar <- weighted.mean(x, w=wts)
SSx <- sum(wts * (x - xbar)^2)
ybar <- weighted.mean(y, w=wts)
SSxy <- sum(wts * (x - xbar) * y)
beta1 <- SSxy / SSx
beta0 <- ybar - beta1 * xbar
mu <- beta0 + beta1 * x
RSS <- sum(wts * (y - mu)^2)
c(beta0=beta0, beta1=beta1, RSS=RSS)
##      beta0      beta1        RSS 
## -2.6783891  0.1537594 11.4198322

2.3.3 Ước lượng phương sai và phần dư:

Sau khi ước lượng được mô hình hồi quy tuyến tính đơn:

\[ y_i = \beta_0 + \beta_1 x_i + \varepsilon_i, \]

trong đó \(\varepsilon_i \sim \mathcal{N}(0, \sigma^2)\), ta cần ước lượng phương sai của sai số ngẫu nhiên để đánh giá mức độ phân tán của dữ liệu so với mô hình.

Phương sai sai số được ước lượng theo công thức:

\[ \hat{\sigma}^2 = \frac{1}{n - 2} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 \]

Trong đó:

  • \(\hat{\sigma}^2\): là ước lượng phương sai sai số (residual variance),
  • \(y_i\): là giá trị quan sát,
  • \(\hat{y}_i\): là giá trị dự đoán từ mô hình,
  • \(n\): là số lượng quan sát,
  • \(n - 2\): là bậc tự do, do có hai hệ số \(\beta_0\)\(\beta_1\) được ước lượng.
df <- length(y) - 2
s2 <- RSS / df
c(df = df, s = sqrt(s2), s2 = s2)
##         df          s         s2 
## 19.0000000  0.7752701  0.6010438

Phương sai sai số càng nhỏ thì mô hình càng phù hợp với dữ liệu thực tế.

Ước lượng phương sai phần dư:

\[ s^2 = \frac{RSS}{n - p'} \] #### 2.3.4 Sai số chuẩn:

Sai số chuẩn của hệ số góc được tính theo công thức:

\[ SE(\hat{\beta}_1) = \sqrt{ \frac{\hat{\sigma}^2}{\sum_{i=1}^{n} (x_i - \bar{x})^2} } \]

Trong đó:

  • \(\hat{\sigma}^2\): là ước lượng phương sai sai số,
  • \(x_i\): là giá trị của biến độc lập,
  • \(\bar{x}\): là trung bình của \(x\),
  • \(SE(\hat{\beta}_1)\): là sai số chuẩn của hệ số hồi quy \(\hat{\beta}_1\).
var.b0 <- s2 * (1/sum(wts) + xbar^2 / SSx)
var.b1 <- s2 / SSx
sqrt(c(beta0 = var.b0, beta1 = var.b1))
##       beta0       beta1 
## 0.371172341 0.009493212

Sai số chuẩn càng nhỏ thì ước lượng \(\hat{\beta}_1\) càng chính xác.

2.4 Hồi quy tuyến tính bội

Khi có nhiều biến giải thích, mô hình trở thành hồi quy tuyến tính bội. Việc thêm biến cho phép mô hình giải thích tốt hơn sự biến thiên trong dữ liệu, nhưng cũng làm tăng nguy cơ đa cộng tuyến hoặc overfitting.

Tương tự hồi quy đơn nhưng có nhiều biến x. Phân tích bằng OLS vẫn áp dụng.

2.5 Dạng ma trận của mô hình hồi quy

Viết mô hình dưới dạng ma trận giúp biểu diễn ngắn gọn và thuận tiện hơn khi xử lý bằng máy tính. Việc ước lượng hệ số sử dụng công thức đại số tuyến tính là nền tảng của nhiều thuật toán hồi quy hiện đại.

Mô hình hồi quy tuyến tính nhiều biến có thể được viết gọn dưới dạng ma trận như sau:

\[ \mathbf{y} = \mathbf{X} \boldsymbol{\beta} + \boldsymbol{\varepsilon} \]

Trong đó:

  • \(\mathbf{y}\): là vector \(n \times 1\) chứa các giá trị biến phụ thuộc,
  • \(\mathbf{X}\): là ma trận thiết kế \(n \times p\) (gồm 1 cột toàn 1 cho hệ số chặn và các cột biến độc lập),
  • \(\boldsymbol{\beta}\): là vector hệ số hồi quy \(p \times 1\),
  • \(\boldsymbol{\varepsilon}\): là vector sai số ngẫu nhiên \(n \times 1\).

Hệ số hồi quy được ước lượng theo công thức bình phương tối thiểu:

\[ \hat{\boldsymbol{\beta}} = (\mathbf{X}^T \mathbf{X})^{-1} \mathbf{X}^T \mathbf{y} \]

Điều này giả định rằng \(\mathbf{X}^T \mathbf{X}\) khả nghịch (nghĩa là không có đa cộng tuyến hoàn toàn).

library(GLMsData)
data(lungcap)
X <- model.matrix(~ Age + Ht + Gender + Smoke, data = lungcap)
y <- log(lungcap$FEV)
beta <- solve(t(X) %*% X) %*% t(X) %*% y
fitted <- X %*% beta

2.6 Ước lượng bằng R

Ví dụ Mô hình hồi quy trong R, có thể dùng lệnh sau:

Mô hình hồi quy đơn:

\[ model_simple <- lm(mpg ~ wt, data = mtcars) \] Mô hình hồi quy bội

\[ model_multi <- lm(mpg ~ wt + hp, data = mtcars) \]

LC.m1 <- lm(log(FEV) ~ Age + Ht + Gender + Smoke, data = lungcap)
summary(LC.m1)
## 
## Call:
## lm(formula = log(FEV) ~ Age + Ht + Gender + Smoke, data = lungcap)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.63278 -0.08657  0.01146  0.09540  0.40701 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.943998   0.078639 -24.721  < 2e-16 ***
## Age          0.023387   0.003348   6.984  7.1e-12 ***
## Ht           0.042796   0.001679  25.489  < 2e-16 ***
## GenderM      0.029319   0.011719   2.502   0.0126 *  
## Smoke       -0.046068   0.020910  -2.203   0.0279 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1455 on 649 degrees of freedom
## Multiple R-squared:  0.8106, Adjusted R-squared:  0.8095 
## F-statistic: 694.6 on 4 and 649 DF,  p-value: < 2.2e-16
coef(LC.m1)
## (Intercept)         Age          Ht     GenderM       Smoke 
## -1.94399818  0.02338721  0.04279579  0.02931936 -0.04606754

2.7 Diễn giải hệ số hồi quy

Mỗi hệ số hồi quy biểu diễn sự thay đổi trung bình của biến phản hồi y khi biến giải thích tăng một đơn vị, trong khi các biến khác giữ nguyên. Việc diễn giải đúng giúp đưa ra kết luận có ý nghĩa thực tiễn.

Hệ số \(\beta_j\): đại diện cho ảnh hưởng biên của biến \(x_j\) đến \(y\).

Mỗi khi \(x_j\) thay đổi một đơn vị (giữ các biến khác không đổi), thì \(y\) thay đổi trung bình \(\beta_j\) đơn vị.

Hay nói cách khác:

Mỗi 1 đơn vị thay đổi trong \(x_j\) dẫn đến thay đổi trung bình \(\beta_j\) đơn vị trong \(y\), giả sử các biến khác được giữ nguyên.

Ví dụ hệ số của chiều cao là 0.0428 → log(FEV) tăng 0.0428 nếu chiều cao tăng 1 đơn vị (giữ các biến khác không đổi).

2.8 Suy luận thống kê

Sau khi ước lượng, cần đánh giá xem các hệ số hồi quy có ý nghĩa thống kê không. Ta sử dụng kiểm định giả thuyết và khoảng tin cậy để đưa ra kết luận về ý nghĩa và độ chính xác của các ước lượng.

Kiểm định:

\[ H_0 : \beta_j = 0 \quad \text{vs} \quad H_1 : \beta_j \ne 0 \]

Khoảng tin cậy:

\[ \hat{\beta}_j \pm t_{n - p, \alpha/2} \cdot SE(\hat{\beta}_j) \]

summary(LC.m1)
## 
## Call:
## lm(formula = log(FEV) ~ Age + Ht + Gender + Smoke, data = lungcap)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -0.63278 -0.08657  0.01146  0.09540  0.40701 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.943998   0.078639 -24.721  < 2e-16 ***
## Age          0.023387   0.003348   6.984  7.1e-12 ***
## Ht           0.042796   0.001679  25.489  < 2e-16 ***
## GenderM      0.029319   0.011719   2.502   0.0126 *  
## Smoke       -0.046068   0.020910  -2.203   0.0279 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.1455 on 649 degrees of freedom
## Multiple R-squared:  0.8106, Adjusted R-squared:  0.8095 
## F-statistic: 694.6 on 4 and 649 DF,  p-value: < 2.2e-16

2.9 Phân tích phương sai (ANOVA)

Phân tích phương sai giúp đánh giá mức độ phù hợp của mô hình bằng cách chia tổng phương sai thành phần giải thích được (SSR) và không giải thích được (SSE). Đây là bước quan trọng để kiểm định tổng thể mô hình.

Tách tổng phương sai:

  • SSR: do mô hình

  • SSE: phần dư

2.10 So sánh mô hình lồng nhau

Để so sánh hai mô hình trong R \[ - model1 <- lm(mpg ~ wt, data = mtcars) \\ - model2 <- lm(mpg ~ wt + hp, data = mtcars) \\ - anova(model1, model2) \]

2.11 So sánh mô hình không lồng nhau (AIC/BIC)

  • AIC(model1, model2)
  • BIC(model1, model2)

2.12 Chọn biến trong mô hình

  • step(model2)

2.13 Nghiên cứu tình huống

Phần này trình bày một ví dụ thực tế áp dụng mô hình hồi quy tuyến tính vào phân tích dữ liệu. Qua đó, người học có thể rèn luyện kỹ năng mô hình hóa, kiểm định, và diễn giải kết quả mô hình

Phân tích tập dữ liệu thực tế, ước lượng, diễn giải và đánh giá mô hình.

2.14 Dự đoán với predict()

\[ predict(model_multi, newdata = data.frame(wt = 3, hp = 120)) \]

2.15 Tóm tắt

Chương này đã trình bày đầy đủ từ định nghĩa đến thực hành mô hình hồi quy tuyến tính. Người đọc nắm được công cụ lý thuyết và kỹ năng thực hành để áp dụng vào phân tích dữ liệu định lượng.

Mô hình hồi quy tuyến tính là nền tảng của GLM

OLS ước lượng hệ số tốt nhất

R cung cấp công cụ hiệu quả để ước lượng, kiểm định và dự đoán

Chương 3.MÔ HÌNH TUYẾN TÍNH TỔNG QUÁT (GLM)

3.1 Giới thiệu và tổng quan

GLM là sự mở rộng của hồi quy tuyến tính để xử lý các dạng dữ liệu như đếm, nhị phân, dương liên tục… GLM gồm hai phần: phân phối (EDM) và hàm liên kết.

3.2 Họ phân phối mũ (Exponential Family)

\[ P(y; \theta, \phi) = a(y, \phi) \exp\left\{ \frac{y \theta - \kappa(\theta)}{\phi} \right\} \]

Ví dụ: - Normal: \(\theta = \mu\) - Poisson: \(\theta = \log(\mu)\) - Binomial: \(\theta = \log(\mu / (1 - \mu))\) - Gamma: \(\theta = -1/\mu\)

3.3 Kỳ vọng và phương sai

\[ E[y] = \kappa'(\theta) = \mu, \quad \text{Var}[y] = \phi \kappa''(\theta) = \phi V(\mu) \]

3.4 Mô hình GLM

\[ g(\mu_i) = o_i + \beta_0 + \sum_{j=1}^{p} \beta_j x_{ji} \]

# Kiểm tra trước khi dùng
str(lungcap)
## 'data.frame':    654 obs. of  5 variables:
##  $ Age   : int  3 4 4 4 4 4 4 5 5 5 ...
##  $ FEV   : num  1.072 0.839 1.102 1.389 1.577 ...
##  $ Ht    : num  46 48 48 48 49 49 50 46.5 49 49 ...
##  $ Gender: Factor w/ 2 levels "F","M": 1 1 1 1 1 1 1 1 1 1 ...
##  $ Smoke : int  0 0 0 0 0 0 0 0 0 0 ...
# Mô hình Gaussian
glm(FEV ~ Age + Ht + Gender + Smoke, family = gaussian(link = "identity"), data = lungcap)
## 
## Call:  glm(formula = FEV ~ Age + Ht + Gender + Smoke, family = gaussian(link = "identity"), 
##     data = lungcap)
## 
## Coefficients:
## (Intercept)          Age           Ht      GenderM        Smoke  
##    -4.45697      0.06551      0.10420      0.15710     -0.08725  
## 
## Degrees of Freedom: 653 Total (i.e. Null);  649 Residual
## Null Deviance:       490.9 
## Residual Deviance: 110.3     AIC: 703.8
# Mô hình Poisson - tạo dữ liệu phù hợp
set.seed(1)
lungcap$y_pois <- rpois(nrow(lungcap), lambda = 2)
glm(y_pois ~ Age, family = poisson(link = "log"), data = lungcap)
## 
## Call:  glm(formula = y_pois ~ Age, family = poisson(link = "log"), data = lungcap)
## 
## Coefficients:
## (Intercept)          Age  
##    0.748181    -0.004938  
## 
## Degrees of Freedom: 653 Total (i.e. Null);  652 Residual
## Null Deviance:       718.6 
## Residual Deviance: 718.3     AIC: 2224
# Mô hình logistic - tạo biến nhị phân
lungcap$y_bin <- ifelse(lungcap$Smoke == "yes", 1, 0)
glm(y_bin ~ Age, family = binomial(link = "logit"), data = lungcap)
## Warning: glm.fit: algorithm did not converge
## 
## Call:  glm(formula = y_bin ~ Age, family = binomial(link = "logit"), 
##     data = lungcap)
## 
## Coefficients:
## (Intercept)          Age  
##  -2.657e+01    1.613e-14  
## 
## Degrees of Freedom: 653 Total (i.e. Null);  652 Residual
## Null Deviance:       0 
## Residual Deviance: 3.794e-09     AIC: 4

3.5 Ước lượng hệ số

Ước lượng hợp lý tối đa (MLE) qua thuật toán IRLS (iteratively reweighted least squares).

x1 <- lungcap$Age
x2 <- lungcap$Ht
y <- lungcap$y_pois
glm(y ~ x1 + x2, family = poisson)
## 
## Call:  glm(formula = y ~ x1 + x2, family = poisson)
## 
## Coefficients:
## (Intercept)           x1           x2  
##     0.18541     -0.02394      0.01228  
## 
## Degrees of Freedom: 653 Total (i.e. Null);  651 Residual
## Null Deviance:       718.6 
## Residual Deviance: 715.9     AIC: 2224

3.6 Deviance

\[ D(y, \hat{\mu}) = 2 \sum w_i \left[ y_i \log\left(\frac{y_i}{\hat{\mu}_i}\right) - (y_i - \hat{\mu}_i) \right] \]

3.7 Quy trình ước lượng GLM

  1. Chọn phân phối (EDM)
  2. Chọn hàm liên kết
  3. Xây dựng log-likelihood
  4. Giải bằng IRLS
  5. Đánh giá sai số chuẩn, p-value
# Gán biến trước khi dùng
x <- lungcap$Age
y <- lungcap$y_pois

fit <- glm(y ~ x, family = poisson)
summary(fit)
## 
## Call:
## glm(formula = y ~ x, family = poisson)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  0.748181   0.096641   7.742  9.8e-15 ***
## x           -0.004938   0.009367  -0.527    0.598    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 718.61  on 653  degrees of freedom
## Residual deviance: 718.33  on 652  degrees of freedom
## AIC: 2224.3
## 
## Number of Fisher Scoring iterations: 5

3.8 So sánh với hồi quy tuyến tính

  • Hồi quy tuyến tính là trường hợp đặc biệt của GLM (Normal + identity link).
  • GLM mở rộng để xử lý biến đếm, nhị phân, dương liên tục…
  • Các khái niệm như phần dư, leverage vẫn được giữ lại thông qua IRLS.

3.9 Tóm tắt chương

GLM là khung mô hình hóa rất mạnh, áp dụng cho nhiều loại dữ liệu, dựa trên phân phối thuộc họ mũ và hàm liên kết.

Chương 4.ƯỚC LƯỢNG VÀ SUY LUẬN TRONG GLM

4.1 Giới thiệu và tổng quan

Sử dụng hợp lý tối đa (MLE) để ước lượng hệ số và tham số phân tán \(\phi\). Kiểm định Wald, LRT và Score được giới thiệu để kiểm định giả thuyết.

4.2 Ước lượng \(\phi\)

\tilde{\phi} = \frac{D(y, \hat{\mu})}{n - p'},\quad
\bar{\phi} = \frac{1}{n - p'} \sum \frac{w_i (y_i - \hat{\mu}_i)^2}{V(\hat{\mu}_i)}

#3# 4.3 Ước lượng GLM bằng R

x1 <- lungcap$Age
x2 <- lungcap$Ht
y <- lungcap$y_pois
glm(y ~ x1 + x2, family = poisson)
## 
## Call:  glm(formula = y ~ x1 + x2, family = poisson)
## 
## Coefficients:
## (Intercept)           x1           x2  
##     0.18541     -0.02394      0.01228  
## 
## Degrees of Freedom: 653 Total (i.e. Null);  651 Residual
## Null Deviance:       718.6 
## Residual Deviance: 715.9     AIC: 2224
glm.out <- glm(y ~ x1 + x2, family = poisson, data = lungcap)
summary(glm.out)
## 
## Call:
## glm(formula = y ~ x1 + x2, family = poisson, data = lungcap)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)
## (Intercept)  0.185413   0.376768   0.492    0.623
## x1          -0.023939   0.015577  -1.537    0.124
## x2           0.012275   0.007938   1.546    0.122
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 718.61  on 653  degrees of freedom
## Residual deviance: 715.94  on 651  degrees of freedom
## AIC: 2223.9
## 
## Number of Fisher Scoring iterations: 5

4.4 Diễn giải mô hình GLM

\log(\mu_i) = \beta_0 + \beta_1 x_i \Rightarrow \mu_i = \exp(\beta_0 + \beta_1 x_i)

4.5 Ước lượng trung bình và sai số chuẩn

pred <- predict(glm.out, newdata = data.frame(x1 = 2, x2 = 3), se.fit = TRUE, type = "link")
fit.mu <- family(glm.out)$linkinv(pred$fit)
se.mu <- pred$se.fit * family(glm.out)$mu.eta(pred$fit)

4.6 Ước lượng \(\phi\) với quasi-likelihood

Dùng deviance hoặc Pearson.

4.7 Kiểm định Wald

Z = \frac{\hat{\beta}_j - \beta_j^0}{se(\hat{\beta}_j)} \sim N(0, 1)

4.8 Kiểm định LRT

# Mô hình nhỏ hơn (ít biến hơn)
glm.fit1 <- glm(y_pois ~ Age, family = poisson, data = lungcap)

# Mô hình đầy đủ hơn (nhiều biến hơn)
glm.fit2 <- glm(y_pois ~ Age + Ht, family = poisson, data = lungcap)

# So sánh hai mô hình bằng kiểm định Chi-squared
anova(glm.fit1, glm.fit2, test = "Chisq")
## Analysis of Deviance Table
## 
## Model 1: y_pois ~ Age
## Model 2: y_pois ~ Age + Ht
##   Resid. Df Resid. Dev Df Deviance Pr(>Chi)
## 1       652     718.33                     
## 2       651     715.94  1   2.3911    0.122

4.9 Kiểm định Score

# Load thư viện
library(statmod)
## Warning: package 'statmod' was built under R version 4.4.3
# Mô hình null (chỉ có intercept)
glm.fit.null <- glm(y_pois ~ 1, family = poisson, data = lungcap)

# Tạo biến cần kiểm định thêm vào
x1 <- model.matrix(~ Age, data = lungcap)[, -1]  # bỏ intercept

# Thực hiện score test
glm.scoretest(glm.fit.null, x1)
## [1] -0.5271859

4.10 Khoảng tin cậy

confint(glm.out)
## Waiting for profiling to be done...
##                    2.5 %      97.5 %
## (Intercept) -0.553651655 0.923310593
## x1          -0.054745874 0.006317257
## x2          -0.003284268 0.027831950

4.11 Vùng tin cậy nhiều/tham số đơn

(\hat{\zeta} - \zeta)^T I(\hat{\zeta})(\hat{\zeta} - \zeta) \le \chi^2_{q, 1 - \alpha}

4.12 So sánh mô hình không lồng nhau

AIC(glm.out)
## [1] 2223.914
BIC(glm.out)
## [1] 2237.364

4.13 Tóm tắt chương

GLM dùng MLE để suy luận; dùng Wald, LRT, score để kiểm định; và AIC/BIC để so sánh mô hình.

CHƯƠNG 5: MÔ HÌNH POISON

5.1 Giới thiệu

Mô hình Poisson là một dạng cụ thể của mô hình tuyến tính tổng quát (GLM) dùng cho dữ liệu đếm. Biến phản hồi là số lần xảy ra của sự kiện trong một đơn vị thời gian hoặc không gian.

5.2 Phân phối Poisson

Giả định rằng: \[ Y_i \sim \text{Poisson}(\mu_i), \quad \mu_i = E(Y_i) \] Phân phối Poisson có kỳ vọng và phương sai bằng nhau, tức là \(Var(Y_i) = \mu_i\).

Hàm xác suất: \[ P(Y_i = y_i) = \frac{e^{-\mu_i} \mu_i^{y_i}}{y_i!} \] Trong đó: - \(y_i\): số lượng sự kiện quan sát được - \(\mu_i\): giá trị kỳ vọng của số sự kiện

5.3 Hàm liên kết

Mô hình Poisson sử dụng hàm liên kết log: \[ \log(\mu_i) = \eta_i = x_i^T \beta \] Tức là: \[ \mu_i = \exp(x_i^T \beta) \]

Giải thích: - \(x_i\): vector các biến giải thích cho quan sát thứ i - \(\beta\): vector hệ số hồi quy - \(\eta_i\): predictor tuyến tính

5.4 Ước lượng và suy luận

Các hệ số được ước lượng bằng phương pháp tối đa hóa hàm hợp lý (maximum likelihood). Mô hình được kiểm định qua z-test hoặc kiểm định deviance.

5.5 Ước lượng bằng R

Ví dụ với dữ liệu đếm:

data(AirPassengers)
counts <- as.numeric(AirPassengers)
time <- time(AirPassengers)
month <- cycle(AirPassengers)

poisson_model <- glm(counts ~ month, family = poisson(link = "log"))
summary(poisson_model)
## 
## Call:
## glm(formula = counts ~ month, family = poisson(link = "log"))
## 
## Coefficients:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) 5.584364   0.010733 520.280  < 2e-16 ***
## month       0.007865   0.001442   5.454 4.94e-08 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 7216.8  on 143  degrees of freedom
## Residual deviance: 7187.1  on 142  degrees of freedom
## AIC: 8253.9
## 
## Number of Fisher Scoring iterations: 4

5.6 Diễn giải hệ số hồi quy

Các hệ số hồi quy được diễn giải theo log tỉ lệ xảy ra (log-rate). Ví dụ: - Nếu \(\beta_j = 0.3\), thì biến liên quan làm tăng tỉ lệ xảy ra trung bình khoảng \(\exp(0.3) \approx 1.35\) lần.

5.7 Độ phân tán

Nếu phương sai lớn hơn trung bình, ta gặp hiện tượng phân tán vượt mức (overdispersion). Khi đó, mô hình Poisson có thể không phù hợp vì giả định Var = Mean bị vi phạm.

5.8 Kiểm tra phân tán và điều chỉnh

Dùng thống kê Pearson hoặc deviance để kiểm tra overdispersion. Có thể dùng mô hình quasi-Poisson hoặc negative binomial để điều chỉnh.

poisson_quasi <- glm(counts ~ month, family = quasipoisson(link = "log"))
summary(poisson_quasi)
## 
## Call:
## glm(formula = counts ~ month, family = quasipoisson(link = "log"))
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 5.584364   0.076954  72.567   <2e-16 ***
## month       0.007865   0.010340   0.761    0.448    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for quasipoisson family taken to be 51.40374)
## 
##     Null deviance: 7216.8  on 143  degrees of freedom
## Residual deviance: 7187.1  on 142  degrees of freedom
## AIC: NA
## 
## Number of Fisher Scoring iterations: 4

5.9 Dự đoán

Sử dụng predict() để dự đoán số lượng sự kiện:

predict(poisson_model, newdata = data.frame(month = 6), type = "response")
##        1 
## 279.0956

5.10 Tóm tắt

  • Mô hình Poisson dùng để phân tích dữ liệu đếm
  • Hàm liên kết log giúp chuyển đổi về tuyến tính
  • Cần kiểm tra hiện tượng phân tán để chọn mô hình phù hợp hơn nếu cần

CHƯƠNG 6.MÔ HÌNH NHỊ THỨC ÂM

6.1 Giới thiệu

Mô hình nhị thức âm (Negative Binomial - NB) là một mở rộng của mô hình Poisson để xử lý hiện tượng phân tán vượt mức (overdispersion), tức là khi phương sai lớn hơn kỳ vọng.

6.2 Phân phối nhị thức âm

Giả định: \[ Y_i \sim NB(\mu_i, \theta) \] Trong đó: - \(\mu_i\): kỳ vọng của \(Y_i\) - \(\theta\): tham số phân tán (dispersion)

Phương sai: \[ Var(Y_i) = \mu_i + \frac{\mu_i^2}{\theta} \] Phân phối NB cho phép phương sai lớn hơn kỳ vọng và linh hoạt hơn so với Poisson.

6.3 Hàm liên kết

Hàm liên kết thường dùng là log: \[ \log(\mu_i) = x_i^T \beta \] Tương tự mô hình Poisson, mô hình NB thuộc họ GLM với hàm liên kết log.

6.4 Ước lượng và suy luận

Ước lượng các hệ số \(\beta\) và tham số phân tán \(\theta\) bằng phương pháp tối đa hóa hàm hợp lý. Việc ước lượng \(\theta\) thường dùng thuật toán lặp hoặc gói chuyên biệt.

6.5 Mô hình NB trong R

Dùng gói MASS và hàm glm.nb():

library(MASS)
data(Cars93, package = "MASS")
nb_model <- glm.nb(Price ~ Horsepower + Weight, data = Cars93)
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 33.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 29.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 37.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 23.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 26.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 34.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 40.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 29.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 9.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 25.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 12.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 7.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 10.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 12.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 12.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 17.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 47.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 35.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 34.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 36.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 32.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 31.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 61.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 14.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 14.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 10.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 26.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 21.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 14.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 17.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 24.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 28.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 10.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 9.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 22.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 9.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 23.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 22.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 26.700000
summary(nb_model)
## 
## Call:
## glm.nb(formula = Price ~ Horsepower + Weight, data = Cars93, 
##     init.theta = 38.78246161, link = log)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept) 1.421e+00  1.749e-01   8.126 4.45e-16 ***
## Horsepower  5.025e-03  7.254e-04   6.927 4.29e-12 ***
## Weight      2.455e-04  7.173e-05   3.423 0.000619 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Negative Binomial(38.7825) family taken to be 1)
## 
##     Null deviance: 253.205  on 92  degrees of freedom
## Residual deviance:  77.021  on 90  degrees of freedom
## AIC: 560.3
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  38.8 
##           Std. Err.:  14.6 
## 
##  2 x log-likelihood:  -552.3

6.6 So sánh với mô hình Poisson

Khi có overdispersion, mô hình NB thường cho kết quả phù hợp hơn và sai số chuẩn chính xác hơn.

poisson_model <- glm(Price ~ Horsepower + Weight, data = Cars93, family = poisson())
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 33.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 29.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 37.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 23.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 26.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 34.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 40.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 29.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 9.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 25.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 12.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 7.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 10.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 12.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 12.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 17.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 47.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 35.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 34.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 36.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 32.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 31.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 61.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 14.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 14.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 10.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 26.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 15.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 21.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 13.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 16.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 20.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 14.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 17.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 24.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 28.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 11.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 10.900000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.500000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 8.600000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 9.800000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.400000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 18.200000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 22.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 9.100000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 19.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 23.300000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 22.700000
## Warning in dpois(y, mu, log = TRUE): non-integer x = 26.700000
dispersion <- sum(residuals(poisson_model, type = "pearson")^2) / df.residual(poisson_model)
dispersion  # Nếu > 1 đáng kể → nên dùng NB
## [1] 1.572621

6.7 Dự đoán

predict(nb_model, newdata = data.frame(Horsepower = 150, Weight = 3000), type = "response")
##        1 
## 18.37969

Dự đoán trả về kỳ vọng số sự kiện xảy ra (giá trị trung bình của \(Y\)).

6.8 Tóm tắt

  • Mô hình nhị thức âm mở rộng Poisson để xử lý phân tán vượt mức
  • Phương sai phụ thuộc vào cả \(\mu\) và tham số \(\theta\)
  • R hỗ trợ mô hình này thông qua hàm glm.nb() từ gói MASS

CHƯƠNG 7: CHẨN ĐOÁN VÀ KIỂM TRA MÔ HÌNH

7.1 Kiểm tra phần dư (Residuals)

Phần dư giúp đánh giá sự phù hợp của mô hình. Trong GLM, phần dư Pearson và phần dư deviance được sử dụng phổ biến.

  • Residual deviance: so sánh log-likelihood giữa mô hình đã fit và mô hình hoàn hảo.
model <- glm(y_pois ~ Age + Ht, family = poisson, data = lungcap)
residuals(model, type = "deviance")[1:5]  # ví dụ một số phần dư
##           1           2           3           4           5 
## -0.76444044 -0.76521674  0.02014332  1.26597499 -0.78083895

7.2 Biểu đồ chuẩn đoán

Các biểu đồ thường dùng: - Residuals vs Fitted - Normal QQ plot - Cook’s distance

plot(model)  # tạo 4 biểu đồ chẩn đoán cơ bản

7.3 Leverage và ảnh hưởng

Leverage đo lường ảnh hưởng của điểm dữ liệu đến mô hình. Điểm có leverage cao và phần dư lớn có thể là outlier ảnh hưởng.

hatvalues(model)[1:5]  # các giá trị leverage đầu tiên
##           1           2           3           4           5 
## 0.012439811 0.009705965 0.009705965 0.009705965 0.008929733

7.4 Cook’s distance

Đo mức ảnh hưởng của từng quan sát đến toàn bộ mô hình.

cooks.distance(model)[1:5]  # Cook's distance đầu tiên
##            1            2            3            4            5 
## 2.032083e-03 1.579711e-03 1.345002e-06 6.884086e-03 1.506144e-03

7.5 DFBETAs và DFFITS

Giúp đánh giá ảnh hưởng của từng điểm đến từng hệ số ước lượng.

dfbeta(model)[1:5, ]
##     (Intercept)           Age            Ht
## 1 -0.0216342892  1.940289e-04  3.085303e-04
## 2 -0.0192458365  1.432404e-04  2.777418e-04
## 3  0.0005066211 -3.770613e-06 -7.311185e-06
## 4  0.0318403224 -2.369770e-04 -4.594962e-04
## 5 -0.0165293514  2.560891e-04  2.147991e-04
dffits(model)[1:5]
##            1            2            3            4            5 
## -0.082297165 -0.072566708  0.001909434  0.120140683 -0.070971060

7.6 Kiểm định và phân tích mô hình thay thế

Dùng so sánh mô hình (ANOVA) hoặc kiểm định score/likelihood để xem biến có nên đưa vào không.

model0 <- glm(y_pois ~ 1, family = poisson, data = lungcap)
model1 <- glm(y_pois ~ Age, family = poisson, data = lungcap)
anova(model0, model1, test = "Chisq")
## Analysis of Deviance Table
## 
## Model 1: y_pois ~ 1
## Model 2: y_pois ~ Age
##   Resid. Df Resid. Dev Df Deviance Pr(>Chi)
## 1       653     718.61                     
## 2       652     718.33  1  0.27848   0.5977

CHƯƠNG 8: PHÂN TÁN QUÁ MỨC VÀ HÀM QUASI-LIKELIHOOD

8.1 Hiện tượng overdispersion

Overdispersion xảy ra khi phương sai lớn hơn kỳ vọng (Poisson giả định Var = Mean). Dễ gặp trong dữ liệu đếm.

Kiểm tra nhanh:

dispersion <- sum(residuals(model, type = "pearson")^2) / model$df.residual
dispersion  # Nếu > 1 nhiều thì có overdispersion
## [1] 0.9811308

8.2 Quasi-likelihood

Sử dụng khi không muốn giả định phân phối xác định nhưng vẫn mô hình hóa kỳ vọng và phương sai.

model_quasi <- glm(y_pois ~ Age + Ht, family = quasipoisson, data = lungcap)
summary(model_quasi)
## 
## Call:
## glm(formula = y_pois ~ Age + Ht, family = quasipoisson, data = lungcap)
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)
## (Intercept)  0.185413   0.373197   0.497    0.619
## Age         -0.023939   0.015430  -1.552    0.121
## Ht           0.012275   0.007862   1.561    0.119
## 
## (Dispersion parameter for quasipoisson family taken to be 0.9811308)
## 
##     Null deviance: 718.61  on 653  degrees of freedom
## Residual deviance: 715.94  on 651  degrees of freedom
## AIC: NA
## 
## Number of Fisher Scoring iterations: 5

8.3 Negative binomial

Thay vì dùng Poisson, có thể dùng phân phối negative binomial để xử lý overdispersion.

library(MASS)
model_nb <- glm.nb(y_pois ~ Age + Ht, data = lungcap)
## 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(model_nb)
## 
## Call:
## glm.nb(formula = y_pois ~ Age + Ht, data = lungcap, init.theta = 13311.2109, 
##     link = log)
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)
## (Intercept)  0.185408   0.376797   0.492    0.623
## Age         -0.023939   0.015578  -1.537    0.124
## Ht           0.012275   0.007938   1.546    0.122
## 
## (Dispersion parameter for Negative Binomial(13311.21) family taken to be 1)
## 
##     Null deviance: 718.51  on 653  degrees of freedom
## Residual deviance: 715.84  on 651  degrees of freedom
## AIC: 2225.9
## 
## Number of Fisher Scoring iterations: 1
## 
## 
##               Theta:  13311 
##           Std. Err.:  149147 
## Warning while fitting theta: iteration limit reached 
## 
##  2 x log-likelihood:  -2217.917

8.4 So sánh mô hình

So sánh Poisson, Quasipoisson, và Negative Binomial để chọn mô hình tốt nhất.

AIC(model, model_nb)  # AIC nhỏ hơn là mô hình tốt hơn
##          df      AIC
## model     3 2223.914
## model_nb  4 2225.917

8.5 Lý do sử dụng quasi-likelihood

Giúp ước lượng độ lệch chuẩn đúng hơn khi không biết rõ phân phối, đặc biệt hữu ích trong dữ liệu sinh học, xã hội.

Ghi chú: Các mô hình cần kiểm tra thêm giả định để đảm bảo ý nghĩa thống kê và sự phù hợp.

CHƯƠNG 9.MÔ HÌNH CẤU TRÚC

9.1 Giới thiệu

Mô hình có cấu trúc là các mô hình mở rộng của GLM trong đó có mối liên hệ nội tại giữa các tham số hoặc dữ liệu. Các mô hình này được sử dụng khi dữ liệu có cấu trúc không độc lập như: lặp lại theo thời gian, theo cụm, hoặc phân cấp.

9.2 Dữ liệu phân cấp và lặp lại

Trong dữ liệu có cấu trúc phân cấp (hierarchical) hoặc lặp lại (repeated measures), các quan sát không độc lập hoàn toàn. Ví dụ: - Đo huyết áp nhiều lần trên cùng một bệnh nhân - Sinh viên trong cùng một lớp học

9.3 Mô hình hỗn hợp tuyến tính (Linear Mixed Models)

Mô hình hỗn hợp tuyến tính thêm các hiệu ứng ngẫu nhiên (random effects) vào mô hình tuyến tính:

\[ Y_{ij} = \beta_0 + \beta_1 x_{ij} + b_j + \varepsilon_{ij} \] Trong đó: - \(b_j \sim N(0, \sigma_b^2)\): hiệu ứng ngẫu nhiên của nhóm j - \(\varepsilon_{ij} \sim N(0, \sigma^2)\): sai số ngẫu nhiên

9.4 Ước lượng trong mô hình hỗn hợp

Các tham số được ước lượng bằng phương pháp REML (Restricted Maximum Likelihood) hoặc ML (Maximum Likelihood).

Trong R, dùng hàm lmer() từ gói lme4:

library(lme4)
## Warning: package 'lme4' was built under R version 4.4.3
## Loading required package: Matrix
model_lmm <- lmer(Reaction ~ Days + (1 | Subject), data = sleepstudy)
summary(model_lmm)
## Linear mixed model fit by REML ['lmerMod']
## Formula: Reaction ~ Days + (1 | Subject)
##    Data: sleepstudy
## 
## REML criterion at convergence: 1786.5
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -3.2257 -0.5529  0.0109  0.5188  4.2506 
## 
## Random effects:
##  Groups   Name        Variance Std.Dev.
##  Subject  (Intercept) 1378.2   37.12   
##  Residual              960.5   30.99   
## Number of obs: 180, groups:  Subject, 18
## 
## Fixed effects:
##             Estimate Std. Error t value
## (Intercept) 251.4051     9.7467   25.79
## Days         10.4673     0.8042   13.02
## 
## Correlation of Fixed Effects:
##      (Intr)
## Days -0.371

9.5 Mô hình tuyến tính tổng quát hỗn hợp (GLMM)

GLMM kết hợp giữa GLM và hiệu ứng ngẫu nhiên, thích hợp cho dữ liệu đếm hoặc nhị phân có cấu trúc:

Ví dụ:

data(cbpp, package = "lme4")
glmm_model <- glmer(cbind(incidence, size - incidence) ~ period + (1 | herd), 
                    family = binomial, data = cbpp)
summary(glmm_model)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: cbind(incidence, size - incidence) ~ period + (1 | herd)
##    Data: cbpp
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     194.1     204.2     -92.0     184.1        51 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -2.3816 -0.7889 -0.2026  0.5142  2.8791 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  herd   (Intercept) 0.4123   0.6421  
## Number of obs: 56, groups:  herd, 15
## 
## Fixed effects:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -1.3983     0.2312  -6.048 1.47e-09 ***
## period2      -0.9919     0.3032  -3.272 0.001068 ** 
## period3      -1.1282     0.3228  -3.495 0.000474 ***
## period4      -1.5797     0.4220  -3.743 0.000182 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##         (Intr) perid2 perid3
## period2 -0.363              
## period3 -0.340  0.280       
## period4 -0.260  0.213  0.198

9.6 Diễn giải kết quả

Hệ số cố định (fixed effects) được diễn giải như GLM. Các hiệu ứng ngẫu nhiên phản ánh sự biến thiên giữa các nhóm hoặc đơn vị.

9.7 So sánh mô hình có cấu trúc và GLM

GLMM và LMM phù hợp hơn GLM khi dữ liệu có cấu trúc nhóm, thời gian, hoặc đo lặp. Dùng AIC hoặc kiểm định likelihood ratio để so sánh mô hình.

9.8 Tóm tắt

  • Mô hình có cấu trúc xử lý dữ liệu phụ thuộc hoặc phân nhóm
  • Bao gồm LMM (dữ liệu liên tục) và GLMM (dữ liệu đếm, nhị phân)
  • R hỗ trợ ước lượng qua lme4::lmer()lme4::glmer()

CHƯƠNG 10: MÔ HÌNH CHO DỮ LIỆU NHỊ PHÂN

10.1 Dữ liệu nhị phân và hồi quy logistic

Dữ liệu nhị phân gồm hai giá trị (thành công/thất bại, 1/0). Hồi quy logistic dùng để mô hình xác suất thành công theo các biến giải thích.

Hàm logit: \[ \text{logit}(p) = \log\left(\frac{p}{1 - p}\right) \]

model_logit <- glm(y_bin ~ Age + Ht, family = binomial(link = "logit"), data = lungcap)
## Warning: glm.fit: algorithm did not converge
summary(model_logit)
## 
## Call:
## glm(formula = y_bin ~ Age + Ht, family = binomial(link = "logit"), 
##     data = lungcap)
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)
## (Intercept) -2.657e+01  1.903e+05       0        1
## Age          4.932e-15  7.727e+03       0        1
## Ht           8.500e-15  4.002e+03       0        1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 0.0000e+00  on 653  degrees of freedom
## Residual deviance: 3.7942e-09  on 651  degrees of freedom
## AIC: 6
## 
## Number of Fisher Scoring iterations: 25

10.2 Hàm liên kết khác

Ngoài logit, còn có probit và cloglog:

$$ model_probit <- glm(y_bin ~ Age + Ht, family = binomial(link = “probit”), data = lungcap)\

model_cloglog <- glm(y_bin ~ Age + Ht, family = binomial(link = “cloglog”), data = lungcap) $$

10.3 Diễn giải hệ số

Hệ số hồi quy logistic biểu diễn log odds ratio. Để diễn giải dễ hơn, thường chuyển sang odds ratio:

exp(coef(model_logit))  # odds ratio
##  (Intercept)          Age           Ht 
## 2.900701e-12 1.000000e+00 1.000000e+00

10.4 Mô hình với biến phân loại

Nếu có biến phân loại như Gender hoặc Smoke, cần đảm bảo nó là factor:

lungcap$Gender <- as.factor(lungcap$Gender)
lungcap$Smoke <- as.factor(lungcap$Smoke)
model_cat <- glm(y_bin ~ Gender + Smoke, family = binomial, data = lungcap)
## Warning: glm.fit: algorithm did not converge
summary(model_cat)
## 
## Call:
## glm(formula = y_bin ~ Gender + Smoke, family = binomial, data = lungcap)
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)
## (Intercept) -2.657e+01  2.077e+04  -0.001    0.999
## GenderM      8.477e-14  2.794e+04   0.000    1.000
## Smoke1       6.212e-14  4.668e+04   0.000    1.000
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 0.0000e+00  on 653  degrees of freedom
## Residual deviance: 3.7942e-09  on 651  degrees of freedom
## AIC: 6
## 
## Number of Fisher Scoring iterations: 25

10.5 Kiểm định và độ phù hợp

Kiểm định tổng thể bằng deviance:

dev <- deviance(model_logit)
df <- df.residual(model_logit)
pchisq(dev, df, lower.tail = FALSE)  # p-value
## [1] 1

Ngoài ra có thể dùng Hosmer-Lemeshow test (trong gói ResourceSelection).

10.6 Đánh giá mô hình

Dùng ROC curve, AUC, hoặc bảng phân loại:

pred <- predict(model_logit, type = "response")
table(Predicted = pred > 0.5, Actual = lungcap$y_bin)
##          Actual
## Predicted   0
##     FALSE 654

Ghi chú: Mô hình nhị phân là một trong những ứng dụng phổ biến nhất của GLM trong khoa học xã hội, y tế và kinh tế lượng.

CHƯƠNG 11.DỮ LIỆU LIÊN TỤC DƯƠNG: GLM VỚI PHÂN PHỐI GAMMA VÀ INVERSE GAUSIAN

11.1 Giới thiệu

Dữ liệu liên tục dương phổ biến trong nhiều lĩnh vực (sinh học, kinh tế, kỹ thuật). Hai mô hình GLM thường dùng là: - Gamma GLM: phù hợp với dữ liệu có phương sai tăng theo bình phương trung bình. - Inverse Gaussian GLM: phù hợp khi dữ liệu lệch phải nhiều, phương sai tăng theo lập phương trung bình.

11.2 Mô hình hóa dữ liệu liên tục dương

Dữ liệu như FEV (Forced Expiratory Volume) là liên tục dương, cần mô hình phản ánh tính chất phân phối và mối quan hệ mean–variance.

11.3 Phân phối Gamma

  • Hàm phương sai: V(μ) = μ²
  • Dữ liệu có phân phối lệch nhẹ
  • Thường dùng link "log"
library(GLMsData)
data(lungcap)
gamma_model <- glm(FEV ~ Age + Ht + Gender + Smoke,
                   family = Gamma(link = "log"),
                   data = lungcap)
summary(gamma_model)
## 
## Call:
## glm(formula = FEV ~ Age + Ht + Gender + Smoke, family = Gamma(link = "log"), 
##     data = lungcap)
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.940198   0.076402 -25.395  < 2e-16 ***
## Age          0.022816   0.003253   7.013 5.86e-12 ***
## Ht           0.042985   0.001631  26.351  < 2e-16 ***
## GenderM      0.029409   0.011385   2.583   0.0100 *  
## Smoke       -0.040853   0.020315  -2.011   0.0447 *  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for Gamma family taken to be 0.01997449)
## 
##     Null deviance: 70.791  on 653  degrees of freedom
## Residual deviance: 13.414  on 649  degrees of freedom
## AIC: 525.61
## 
## Number of Fisher Scoring iterations: 4

11.4 Phân phối Inverse Gaussian

  • Hàm phương sai: V(μ) = μ³
  • Mô hình tốt khi dữ liệu lệch mạnh
  • Canonical link: 1/μ², nhưng "log" được dùng phổ biến
invgauss_model <- glm(FEV ~ Age + Ht + Gender + Smoke,
                      family = inverse.gaussian(link = "log"),
                      data = lungcap)
summary(invgauss_model)
## 
## Call:
## glm(formula = FEV ~ Age + Ht + Gender + Smoke, family = inverse.gaussian(link = "log"), 
##     data = lungcap)
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -1.964597   0.076821 -25.574  < 2e-16 ***
## Age          0.021928   0.003532   6.209 9.54e-10 ***
## Ht           0.043535   0.001701  25.597  < 2e-16 ***
## GenderM      0.028638   0.011243   2.547   0.0111 *  
## Smoke       -0.039314   0.023159  -1.698   0.0901 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for inverse.gaussian family taken to be 0.008309335)
## 
##     Null deviance: 29.0674  on 653  degrees of freedom
## Residual deviance:  5.8086  on 649  degrees of freedom
## AIC: 574.71
## 
## Number of Fisher Scoring iterations: 4

11.6 Ước lượng tham số phân tán φ

Dùng residual deviance hoặc Pearson residuals.

phi_gamma <- sum(residuals(gamma_model, type="pearson")^2) / df.residual(gamma_model)
phi_ig <- sum(residuals(invgauss_model, type="pearson")^2) / df.residual(invgauss_model)
phi_gamma; phi_ig
## [1] 0.01997449
## [1] 0.008309329

11.7 Nghiên cứu tình huống

Sách trình bày 2 case study, không dùng lungcap, nhưng quy trình áp dụng tương tự: - Mô hình hóa - Chẩn đoán - So sánh fitted values, residuals

11.8 Dùng R để fit mô hình

  • glm(..., family = Gamma(link = "log"))
  • glm(..., family = inverse.gaussian(link = "log"))

11.9 Tóm tắt

Mô hình Hàm phương sai Link phổ biến Dữ liệu phù hợp
Gamma V(μ) = μ² log Dữ liệu lệch nhẹ
Inverse Gaussian V(μ) = μ³ log, 1/μ² Dữ liệu lệch mạnh, thời gian

Tạo đồ thị chuẩn đoán dựa trên dataset trong sách

Nạp dữ liệu

library(GLMsData)
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.4.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:MASS':
## 
##     select
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
data(lungcap)
head(lungcap)
##   Age   FEV Ht Gender Smoke
## 1   3 1.072 46      F     0
## 2   4 0.839 48      F     0
## 3   4 1.102 48      F     0
## 4   4 1.389 48      F     0
## 5   4 1.577 49      F     0
## 6   4 1.418 49      F     0

Chẩn đoán mô hình Gamma

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

Chẩn đoán mô hình Inverse Gaussian

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

Residuals vs Fitted cho Gamma

lungcap$gamma_resid <- residuals(gamma_model, type = "pearson")
lungcap$gamma_fitted <- fitted(gamma_model)

ggplot(lungcap, aes(x = gamma_fitted, y = gamma_resid)) +
  geom_point(alpha = 0.6) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
  labs(title = "Residuals vs Fitted (Gamma)", x = "Fitted values", y = "Pearson Residuals")

Residuals vs Fitted cho Inverse Gaussian

lungcap$ig_resid <- residuals(invgauss_model, type = "pearson")
lungcap$ig_fitted <- fitted(invgauss_model)

ggplot(lungcap, aes(x = ig_fitted, y = ig_resid)) +
  geom_point(alpha = 0.6) +
  geom_hline(yintercept = 0, linetype = "dashed", color = "red") +
  labs(title = "Residuals vs Fitted (Inverse Gaussian)", x = "Fitted values", y = "Pearson Residuals")

CHƯƠNG 12: MÔ HÌNH CÓ HIỆU ỨNG NGẪU NHIÊN (RANDOM EFFECTS)

12.1 Mô hình hỗn hợp tuyến tính tổng quát (GLMM)

Khi dữ liệu có cấu trúc nhóm (ví dụ: bệnh nhân trong các bệnh viện), cần đưa hiệu ứng ngẫu nhiên vào mô hình để phản ánh sự phụ thuộc nội tại.

library(lme4)
lungcap$Ht_grp <- cut(lungcap$Ht, breaks = 3, labels = c("Low", "Medium", "High"))
library(GLMsData)
library(lme4)

data(lungcap)

# Tạo biến nhị phân
lungcap$y_bin <- ifelse(lungcap$FEV > 2.5, 1, 0)

# Mô hình GLMM với random intercept theo chiều cao (Ht)
model_glmm <- glmer(y_bin ~ Age + (1 | Ht), family = binomial, data = lungcap)
summary(model_glmm)
## Generalized linear mixed model fit by maximum likelihood (Laplace
##   Approximation) [glmerMod]
##  Family: binomial  ( logit )
## Formula: y_bin ~ Age + (1 | Ht)
##    Data: lungcap
## 
##       AIC       BIC    logLik -2*log(L)  df.resid 
##     507.1     520.6    -250.6     501.1       651 
## 
## Scaled residuals: 
##     Min      1Q  Median      3Q     Max 
## -6.6916 -0.2560  0.0624  0.3733  4.6827 
## 
## Random effects:
##  Groups Name        Variance Std.Dev.
##  Ht     (Intercept) 3.515    1.875   
## Number of obs: 654, groups:  Ht, 56
## 
## Fixed effects:
##             Estimate Std. Error z value Pr(>|z|)    
## (Intercept) -5.65530    0.87149  -6.489 8.63e-11 ***
## Age          0.56599    0.08892   6.365 1.95e-10 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Correlation of Fixed Effects:
##     (Intr)
## Age -0.926

12.2 Cú pháp mô hình hỗn hợp

  • (1 | Group) chỉ ra intercept thay đổi theo nhóm.
  • (Age | Group) cho phép slope và intercept thay đổi theo nhóm.

12.3 So sánh GLM và GLMM

GLMM cho phép xử lý dữ liệu có phụ thuộc nội tại (clustered/correlated). GLM giả định độc lập giữa các quan sát, nên không phù hợp khi có cấu trúc nhóm rõ ràng.

12.4 Ước lượng và hội tụ

GLMM thường dùng phương pháp tối đa hóa hợp lý xấp xỉ như Laplace hoặc quadrature. Việc hội tụ có thể chậm hoặc thất bại nếu mô hình quá phức tạp.

model_glmm2 <- glmer(y_bin ~ Age + (Age | Ht), family = binomial, data = lungcap)

KẾT LUẬN TỔNG QUAN VỀ QUYỂN SÁCH

Quyển “Generalized Linear Models with Examples in R” mang lại một cái nhìn toàn diện và thực hành về mô hình tuyến tính tổng quát (GLM). Các nội dung chính bao gồm:

  • Giới thiệu lý thuyết GLM: liên kết, phân phối thuộc họ hàm mũ, hàm tuyến tính.
  • Các mô hình quan trọng: hồi quy logistic, hồi quy Poisson, quasi-likelihood và negative binomial.
  • Kiểm tra độ phù hợp mô hình và các công cụ chẩn đoán phần dư, ảnh hưởng.
  • Mở rộng GLM với mô hình có hiệu ứng ngẫu nhiên (GLMM).

Điểm nổi bật là cách trình bày thực tế, tích hợp ví dụ cụ thể bằng ngôn ngữ R, giúp người học vừa hiểu lý thuyết vừa áp dụng thực hành. Đây là tài liệu quan trọng cho những ai làm việc với dữ liệu định lượng trong các lĩnh vực sinh học, y tế, xã hội, và kinh tế lượng.

Ghi chú cuối cùng: Mặc dù GLM là một công cụ mạnh mẽ, người dùng cần kiểm tra cẩn thận các giả định, cấu trúc dữ liệu và phù hợp mô hình để đưa ra suy luận chính xác.

TASK 2.THỐNG KÊ MÔ TẢ “SUPERMARKET TRANSACTIONS”

1. Đọc dữ liệu

data <- read.csv("C:/Users/Admin/Downloads/Supermarket Transactions.csv")
str(data)
## 'data.frame':    14059 obs. of  16 variables:
##  $ X                : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ PurchaseDate     : chr  "2007-12-18" "2007-12-20" "2007-12-21" "2007-12-21" ...
##  $ CustomerID       : int  7223 7841 8374 9619 1900 6696 9673 354 1293 7938 ...
##  $ Gender           : chr  "F" "M" "F" "M" ...
##  $ MaritalStatus    : chr  "S" "M" "M" "M" ...
##  $ Homeowner        : chr  "Y" "Y" "N" "Y" ...
##  $ Children         : int  2 5 2 3 3 3 2 2 3 1 ...
##  $ AnnualIncome     : chr  "$30K - $50K" "$70K - $90K" "$50K - $70K" "$30K - $50K" ...
##  $ City             : chr  "Los Angeles" "Los Angeles" "Bremerton" "Portland" ...
##  $ StateorProvince  : chr  "CA" "CA" "WA" "OR" ...
##  $ Country          : chr  "USA" "USA" "USA" "USA" ...
##  $ ProductFamily    : chr  "Food" "Food" "Food" "Food" ...
##  $ ProductDepartment: chr  "Snack Foods" "Produce" "Snack Foods" "Snacks" ...
##  $ ProductCategory  : chr  "Snack Foods" "Vegetables" "Snack Foods" "Candy" ...
##  $ UnitsSold        : int  5 5 3 4 4 3 4 6 1 2 ...
##  $ Revenue          : num  27.38 14.9 5.52 4.44 14 ...

2. Tổng quan dữ liệu

summary(data)
##        X         PurchaseDate         CustomerID       Gender         
##  Min.   :    1   Length:14059       Min.   :    3   Length:14059      
##  1st Qu.: 3516   Class :character   1st Qu.: 2549   Class :character  
##  Median : 7030   Mode  :character   Median : 5060   Mode  :character  
##  Mean   : 7030                      Mean   : 5117                     
##  3rd Qu.:10544                      3rd Qu.: 7633                     
##  Max.   :14059                      Max.   :10280                     
##  MaritalStatus       Homeowner            Children    AnnualIncome      
##  Length:14059       Length:14059       Min.   :0.00   Length:14059      
##  Class :character   Class :character   1st Qu.:1.00   Class :character  
##  Mode  :character   Mode  :character   Median :3.00   Mode  :character  
##                                        Mean   :2.53                     
##                                        3rd Qu.:4.00                     
##                                        Max.   :5.00                     
##      City           StateorProvince      Country          ProductFamily     
##  Length:14059       Length:14059       Length:14059       Length:14059      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##  ProductDepartment  ProductCategory      UnitsSold        Revenue     
##  Length:14059       Length:14059       Min.   :1.000   Min.   : 0.53  
##  Class :character   Class :character   1st Qu.:3.000   1st Qu.: 6.84  
##  Mode  :character   Mode  :character   Median :4.000   Median :11.25  
##                                        Mean   :4.081   Mean   :13.00  
##                                        3rd Qu.:5.000   3rd Qu.:17.37  
##                                        Max.   :8.000   Max.   :56.70

3. Thống kê mô tả các biến định lượng

data %>% 
  select(AnnualIncome, Children, UnitsSold, Revenue) %>%
  summary()
##  AnnualIncome          Children      UnitsSold        Revenue     
##  Length:14059       Min.   :0.00   Min.   :1.000   Min.   : 0.53  
##  Class :character   1st Qu.:1.00   1st Qu.:3.000   1st Qu.: 6.84  
##  Mode  :character   Median :3.00   Median :4.000   Median :11.25  
##                     Mean   :2.53   Mean   :4.081   Mean   :13.00  
##                     3rd Qu.:4.00   3rd Qu.:5.000   3rd Qu.:17.37  
##                     Max.   :5.00   Max.   :8.000   Max.   :56.70

4. Thống kê mô tả các biến định tính

categorical_vars <- c("Gender", "MaritalStatus", "Homeowner", "City", "StateorProvince", 
                      "Country", "ProductFamily", "ProductDepartment", "ProductCategory")

for (var in categorical_vars) {
  cat("\n\n###", var, "\n")
  print(table(data[[var]]))
}
## 
## 
## ### Gender 
## 
##    F    M 
## 7170 6889 
## 
## 
## ### MaritalStatus 
## 
##    M    S 
## 6866 7193 
## 
## 
## ### Homeowner 
## 
##    N    Y 
## 5615 8444 
## 
## 
## ### City 
## 
##      Acapulco    Bellingham Beverly Hills     Bremerton       Camacho 
##           383           143           811           834           452 
##   Guadalajara       Hidalgo   Los Angeles        Merida   Mexico City 
##            75           845           926           654           194 
##       Orizaba      Portland         Salem    San Andres     San Diego 
##           464           876          1386           621           866 
## San Francisco       Seattle       Spokane        Tacoma     Vancouver 
##           130           922           875          1257           633 
##      Victoria   Walla Walla        Yakima 
##           176           160           376 
## 
## 
## ### StateorProvince 
## 
##        BC        CA        DF  Guerrero   Jalisco        OR  Veracruz        WA 
##       809      2733       815       383        75      2262       464      4567 
##   Yucatan Zacatecas 
##       654      1297 
## 
## 
## ### Country 
## 
## Canada Mexico    USA 
##    809   3688   9562 
## 
## 
## ### ProductFamily 
## 
##          Drink           Food Non-Consumable 
##           1250          10153           2656 
## 
## 
## ### ProductDepartment 
## 
## Alcoholic Beverages         Baked Goods        Baking Goods           Beverages 
##                 356                 425                1072                 680 
##     Breakfast Foods        Canned Foods     Canned Products            Carousel 
##                 188                 977                 109                  59 
##            Checkout               Dairy                Deli                Eggs 
##                  82                 903                 699                 198 
##        Frozen Foods  Health and Hygiene           Household                Meat 
##                1382                 893                1420                  89 
##         Periodicals             Produce             Seafood         Snack Foods 
##                 202                1994                 102                1600 
##              Snacks       Starchy Foods 
##                 352                 277 
## 
## 
## ### ProductCategory 
## 
##         Baking Goods    Bathroom Products        Beer and Wine 
##                  484                  365                  356 
##                Bread      Breakfast Foods              Candles 
##                  425                  417                   45 
##                Candy     Canned Anchovies         Canned Clams 
##                  352                   44                   53 
##       Canned Oysters      Canned Sardines        Canned Shrimp 
##                   35                   40                   38 
##          Canned Soup          Canned Tuna Carbonated Beverages 
##                  404                   87                  154 
##    Cleaning Supplies        Cold Remedies                Dairy 
##                  189                   93                  903 
##        Decongestants               Drinks                 Eggs 
##                   85                  135                  198 
##           Electrical      Frozen Desserts       Frozen Entrees 
##                  355                  323                  118 
##                Fruit             Hardware        Hot Beverages 
##                  765                  129                  226 
##              Hygiene     Jams and Jellies     Kitchen Products 
##                  197                  588                  217 
##            Magazines                 Meat        Miscellaneous 
##                  202                  761                   42 
##  Packaged Vegetables       Pain Relievers       Paper Products 
##                   48                  192                  345 
##                Pizza     Plastic Products Pure Juice Beverages 
##                  194                  141                  165 
##              Seafood          Side Dishes          Snack Foods 
##                  102                  153                 1600 
##            Specialty        Starchy Foods           Vegetables 
##                  289                  277                 1728

5. Phân phối giới tính và tình trạng hôn nhân

table(data$Gender)
## 
##    F    M 
## 7170 6889
table(data$MaritalStatus)
## 
##    M    S 
## 6866 7193

6. Số lượng khách hàng theo thành phố (Top 10)

data %>%
  count(City, sort = TRUE) %>%
  head(10)
##             City    n
## 1          Salem 1386
## 2         Tacoma 1257
## 3    Los Angeles  926
## 4        Seattle  922
## 5       Portland  876
## 6        Spokane  875
## 7      San Diego  866
## 8        Hidalgo  845
## 9      Bremerton  834
## 10 Beverly Hills  811

7. Tổng doanh thu theo bang (Top 10)

data %>%
  group_by(StateorProvince) %>%
  summarise(TongDoanhThu = sum(Revenue, na.rm = TRUE)) %>%
  arrange(desc(TongDoanhThu)) %>%
  head(10)
## # A tibble: 10 × 2
##    StateorProvince TongDoanhThu
##    <chr>                  <dbl>
##  1 WA                    58420.
##  2 CA                    34730.
##  3 OR                    30138.
##  4 Zacatecas             17110.
##  5 BC                    11067.
##  6 DF                    10694.
##  7 Yucatan                8740.
##  8 Veracruz               6245.
##  9 Guerrero               5161.
## 10 Jalisco                 523.

8. Doanh thu theo nhóm sản phẩm

data %>%
  group_by(ProductFamily) %>%
  summarise(TongDoanhThu = sum(Revenue, na.rm = TRUE)) %>%
  arrange(desc(TongDoanhThu))
## # A tibble: 3 × 2
##   ProductFamily  TongDoanhThu
##   <chr>                 <dbl>
## 1 Food                132761.
## 2 Non-Consumable       34196.
## 3 Drink                15874.

9. Biểu đồ doanh thu theo phòng ban sản phẩm

data %>%
  group_by(ProductDepartment) %>%
  summarise(Revenue = sum(Revenue, na.rm = TRUE)) %>%
  ggplot(aes(x = reorder(ProductDepartment, Revenue), y = Revenue)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  coord_flip() +
  labs(title = "Doanh thu theo phòng ban sản phẩm", x = "Phòng ban", y = "Doanh thu")

10. Kết luận

Dữ liệu giao dịch siêu thị cung cấp thông tin đa dạng từ thông tin khách hàng đến thông tin sản phẩm và doanh thu. Qua phân tích mô tả:

  • Biến định lượng như thu nhập, số lượng bán và doanh thu có độ phân tán lớn, cho thấy sự khác biệt đáng kể giữa các khách hàng và sản phẩm.
  • Biến định tính như giới tính, tình trạng hôn nhân và nhóm sản phẩm cho thấy sự đa dạng trong đặc điểm khách hàng và danh mục sản phẩm.
  • Doanh thu tập trung nhiều ở một số bang và nhóm sản phẩm cụ thể, gợi ý tiềm năng trong việc phân khúc thị trường và tối ưu hóa danh mục sản phẩm.

Phân tích mô tả này là bước đầu quan trọng giúp hiểu rõ cấu trúc dữ liệu, làm cơ sở cho các phân tích sâu hơn như phân cụm khách hàng, dự đoán doanh thu hoặc phân tích hành vi mua hàng.

LS0tDQp0aXRsZTogIlBURExEVF9DVDJfVFXhuqZOIDEiDQphdXRob3I6ICJUcuG6p24gTmFtIFRoacOqbiINCmRhdGU6ICIyMDI1LTA1LTE3Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBoaWRlDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jOiB0cnVlDQogIHBkZl9kb2N1bWVudDoNCiAgICBsYXRleF9lbmdpbmU6IHhlbGF0ZXgNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCg0KYGBgDQoNCg0KIyAqKlRBU0sgMTogVMOTTSBU4bquVCBRVVnhu4JOIFPDgUNIICIyMDE5X0dlbmVyYWxpemVkIExpbmVhciBNb2RlbHMgV2l0aCBFeGFtcGxlcyBpbiBSIioqDQoNCjxzdHlsZT4NCmJvZHkgew0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIHNhbnMtc2VyaWY7DQogIGZvbnQtc2l6ZTogMTZweDsNCiAgdGV4dC1hbGlnbjoganVzdGlmeTsNCn0NCmgyIHsNCiAgY29sb3I6IGJsYWNrOw0KfQ0KaDMgew0KICBjb2xvcjogYmxhY2s7DQp9DQpoNCB7DQogIGNvbG9yOkJsYWNrDQp9DQo8L3N0eWxlPg0KDQoNCiMjIENIxq/GoE5HIDEuIEdJ4buaSSBUSEnhu4ZVIE3DlCBIw4xOSCBUSOG7kE5HIEvDig0KDQotIFRyw61jaCBk4bqrbiBjw6J1IG7Ds2kgY+G7p2EgbmjDoCB0aOG7kW5nIGvDqiBuZ8aw4budaSBBbmg6DQoNCj4gIkFsbCBtb2RlbHMgYXJlIHdyb25nLCBidXQgc29tZSBhcmUgdXNlZnVsIiDigJQgR2VvcmdlIEUuIFAuIEJveA0KDQo+ICoiVOG6pXQgY+G6oyBjw6FjIG3DtCBow6xuaCDEkeG7gXUgc2FpLCBuaMawbmcgbeG7mXQgc+G7kSBtw7QgaMOsbmggY8OzIMOtY2guIiogDQoNCioqKsOdIG5naMSpYToqKioNCg0KLSBLaMO0bmcgY8OzIG3DtCBow6xuaCBuw6BvIHBo4bqjbiDDoW5oIGhvw6BuIHRvw6BuIHRo4buxYyB04bq/IG3hu5l0IGPDoWNoIGNow61uaCB4w6FjIHR1eeG7h3QgxJHhu5FpLiBC4bqldCBr4buzIG3DtCBow6xuaCB0aOG7kW5nIGvDqiBuw6BvIGPFqW5nIGzDoCBt4buZdCAqKnPhu7EgxJHGoW4gZ2nhuqNuIGjDs2EqKiBj4bunYSB0aOG6vyBnaeG7m2kgdGjhu7FjLg0KLSBEbyDEkcOzLCAqKnThuqV0IGPhuqMgY8OhYyBtw7QgaMOsbmggxJHhu4F1IHNhaSoqIOG7nyBt4buZdCBt4bupYyDEkeG7mSBuw6BvIMSRw7MsIHbDrCBjaMO6bmcgbHXDtG4gYuG7jyBxdWEgaG/hurdjIGdp4bqjIMSR4buLbmggbmjhu69uZyDEkWnhu4F1IGtow7RuZyBob8OgbiB0b8OgbiDEkcO6bmcuDQoNCioqKlbhuq15IHThuqFpIHNhbyBs4bqhaSAiY8OzIMOtY2giPyoqKg0KDQotIE3hurdjIGTDuSBzYWksICoqbcO0IGjDrG5oIHbhuqtuIGPDsyB0aOG7gyBnacO6cCBjb24gbmfGsOG7nWkgaGnhu4N1IHLDtSB4dSBoxrDhu5tuZywgbeG7kWkgcXVhbiBo4buHLCBob+G6t2MgxJHGsGEgcmEgZOG7sSDEkW/DoW4gaOG7o3AgbMO9KiogZOG7sWEgdHLDqm4gZOG7ryBsaeG7h3UuDQotIE3hu5l0IG3DtCBow6xuaCDEkcaw4bujYyB4ZW0gbMOgICJjw7Mgw61jaCIga2hpIG7DsyBo4buXIHRy4bujIHThu5F0IGNobyBt4bulYyDEkcOtY2ggY+G7pSB0aOG7gywgY2jhurNuZyBo4bqhbiBuaMawOg0KICAtIEThu7EgxJFvw6FuIGNow61uaCB4w6FjDQogIC0gSGnhu4N1IHLDtSBjxqEgY2jhur8gc2luaCBk4buvIGxp4buHdQ0KICAtIEjhu5cgdHLhu6MgcmEgcXV54bq/dCDEkeG7i25oDQoNCioqKkLDoGkgaOG7jWMgcsO6dCByYToqKioNCg0KLSBLaMO0bmcgbsOqbiAqKnR1eeG7h3QgxJHhu5FpIGjDs2EgbcO0IGjDrG5oKiouDQotIE7Dqm4gZMO5bmcgbcO0IGjDrG5oIG5oxrAgbeG7mXQgKipjw7RuZyBj4bulIGjhu5cgdHLhu6MgdMawIGR1eSB2w6AgcGjDom4gdMOtY2gqKiwgdGhheSB2w6wgbMOgIGNow6JuIGzDvSBjdeG7kWkgY8O5bmcuDQotIEx1w7RuIGPhuqduICoqa2nhu4NtIHRyYSBnaeG6oyDEkeG7i25oIG3DtCBow6xuaCoqLCAqKsSRw6FuaCBnacOhIMSR4buZIHBow7kgaOG7o3AqKiwgdsOgICoqZGnhu4VuIGdp4bqjaSBr4bq/dCBxdeG6oyBt4buZdCBjw6FjaCBj4bqpbiB0cuG7jW5nKiouDQoNCiMjIyBWw60gZOG7pSB0cm9uZyBSOg0KDQoNCmBgYHtyfQ0KbW9kZWwgPC0gbG0obXBnIH4gd3QsIGRhdGEgPSBtdGNhcnMpDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNCiMjIDEuMSBHaeG7m2kgdGhp4buHdSB2w6AgdOG7lW5nIHF1YW4NCg0KICBHaeG7m2kgdGhp4buHdTogDQogDQotIENoxrDGoW5nIG7DoHkgZ2nhu5tpIHRoaeG7h3Uga2jDoWkgbmnhu4dtIGPGoSBi4bqjbiB24buBIG3DtCBow6xuaCB0aOG7kW5nIGvDqiBuaMawIG3hu5l0IGPDoWNoIMSR4buDIG3DtCB04bqjIGPhuqMgY8OhYyDEkeG6t2MgxJFp4buDbSBuZ+G6q3Ugbmhpw6puIHbDoCBjw7MgaOG7hyB0aOG7kW5nIGPhu6dhIGThu68gbGnhu4d1LiBOw7MgbmjhuqVuIG3huqFuaCB04bqnbSBxdWFuIHRy4buNbmcgY+G7p2Egdmnhu4djIHPhu60gZOG7pW5nIGPDoWMgbcO0IGjDrG5oIMSR4buDIHBow6JuIHTDrWNoIGThu68gbGnhu4d1LiANCg0KLSBMw6AgY8O0bmcgY+G7pSB0aGVuIGNo4buRdCDEkeG7gyBoaeG7g3UgdsOgIG3DtCB04bqjIGThu68gbGnhu4d1LiBUcm9uZyDEkcOzLCAqKm3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNKSoqIGzDoCBjaOG7pyDEkeG7gSBjaMOtbmggY+G7p2EgY3Xhu5FuIHPDoWNoLiBUcsaw4bubYyBraGkgxJFpIHPDonUgdsOgbyBHTE0sIGNoxrDGoW5nIG7DoHkgdHLDrG5oIGLDoHkgY8OhYyBraMOhaSBuaeG7h20gY8ahIGLhuqNuIGdpw7pwIHRoaeG6v3QgbOG6rXAgbmfDtG4gbmfhu68sIGvDvSBoaeG7h3UgdsOgIHBoxrDGoW5nIHBow6FwIGzDoG0gdmnhu4djIHRyb25nIHBow6JuIHTDrWNoIG3DtCBow6xuaC4NCg0KPiAqKCJEYXRhIGFuYWx5c2lzOiBUaGUgbmVlZCBmb3IgbW9kZWxzPyIgLSBSZWVzZSwgMTk4NikuKg0KDQogIE3hu6VjIMSRw61jaCBj4bunYSBtw7QgaMOsbmggdGjhu5FuZyBrw6o6IA0KDQotIE3hu6VjIMSRw61jaCBj4bunYSBt4buZdCBt4buZdCBt4buZdCBow6xuaCDhuqNuaCBoxrDhu59uZyDEkeG6v24gY8OhY2ggbsOzIMSRxrDhu6NjIHBow6F0IHRyaeG7g24gKE3hu6VjIDEuOSkuIEPDoWMgbeG7pWMgxJHDrWNoIGPDsyB0aOG7gyBiYW8gZ+G7k20gbcO0IHThuqMsIGThu7EgxJFvw6FuIHbDoCBoaeG7g3UgbeG7kWkgcXVhbiBo4buHIGdp4buvYSBjw6FjIGJp4bq/bi4NCg0KIyMgMS4yIFF1eSDGsOG7m2MgbcO0IHThuqMgZOG7ryBsaeG7h3UNCg0KICBHaeG7m2kgdGhp4buHdSBjw6FjaCBtw7QgdOG6oyBk4buvIGxp4buHdSBkxrDhu5tpIGThuqFuZyB0b8OhbiBo4buNYzogDQogIA0KLSBDw6FjIGJp4bq/biBwaOG6o24gaOG7k2kgKFwoeVwpKSB2w6AgYmnhur9uIGdp4bqjaSB0aMOtY2ggKFwoeFwpKS4gQ8OhY2ggc+G6r3AgeOG6v3AgZOG7ryBsaeG7h3UgdGjDoG5oIGLhuqNuZyBxdWFuIHPDoXQgdsOgIHPhu60gZOG7pW5nIGvDvSBoaeG7h3UgY2h14bqpbiBow7NhIMSR4buDIHRyw6xuaCBiw6B5IG3DtCBow6xuaCBt4buZdCBjw6FjaCB0aOG7kW5nIG5o4bqldC4NCg0KYGBge3J9DQpsaWJyYXJ5KEdMTXNEYXRhKQ0KZGF0YShsdW5nY2FwKQ0Kc3VtbWFyeShsdW5nY2FwKQ0Kc3RyKGx1bmdjYXApDQpgYGANCg0KDQojIyAxLjMgVuG6vSBk4buvIGxp4buHdQ0KDQogIE5o4bqlbiBt4bqhbmggdmFpIHRyw7IgY+G7p2Egdmnhu4djIHRy4buxYyBxdWFuIGjDs2EgZOG7ryBsaeG7h3UgdGjDtG5nIHF1YSBiaeG7g3UgxJHhu5MuIFZp4buHYyB24bq9IMSR4buTIHRo4buLIGdpw7pwIHBow6F0IGhp4buHbiB4dSBoxrDhu5tuZywgYuG6pXQgdGjGsOG7nW5nIHbDoCBoaeG7g3UgbeG7kWkgbGnDqm4gaOG7hyBnaeG7r2EgY8OhYyBiaeG6v24gdHLGsOG7m2Mga2hpIG3DtCBow6xuaCBow7NhLg0KDQpgYGB7cn0NCnBsb3QoRkVWIH4gQWdlLCBkYXRhID0gbHVuZ2NhcCwgbWFpbiA9ICJGRVYgdnMgQWdlIiwgbGFzID0gMSkNCmJveHBsb3QoRkVWIH4gU21va2UgKyBHZW5kZXIsIGRhdGEgPSBsdW5nY2FwLCBsYXMgPSAyKQ0KaW50ZXJhY3Rpb24ucGxvdChsdW5nY2FwJFNtb2tlLCBsdW5nY2FwJEdlbmRlciwgbHVuZ2NhcCRGRVYsDQogICAgICAgICAgICAgICAgIHhsYWIgPSAiU21va2UiLCB5bGFiID0gIkZFViIsIHRyYWNlLmxhYmVsID0gIkdlbmRlciIsIGxhcyA9IDEpDQpgYGANCg0KIyMgMS40IE3DoyBow7NhIGJp4bq/biBraMO0bmcgcGjhuqNpIHPhu5ENCg0KICBDw6FjIGJp4bq/biBwaMOibiBsb+G6oWkgKG5oxrAgZ2nhu5tpIHTDrW5oLCBuaMOzbSB0deG7lWkpIGPhuqduIMSRxrDhu6NjIGNodXnhu4NuIHRow6BuaCBz4buRIMSR4buDIMSRxrBhIHbDoG8gbcO0IGjDrG5oLiBN4bulYyBuw6B5IGdp4bqjaSB0aMOtY2ggY8OhY2ggbcOjIGjDs2EgY2jDum5nIGLhurFuZyBiaeG6v24gZ2nhuqMgKGR1bW15IHZhcmlhYmxlcykgaG/hurdjIGPDoWMgcGjGsMahbmcgcGjDoXAgbcOjIGjDs2Ega2jDoWMuDQoNCmBgYHtyfQ0KbHVuZ2NhcCRHZW5kZXIgPC0gZmFjdG9yKGx1bmdjYXAkR2VuZGVyKQ0KY29udHJhc3RzKGx1bmdjYXAkR2VuZGVyKQ0KDQpsdW5nY2FwJFNtb2tlIDwtIGZhY3RvcihsdW5nY2FwJFNtb2tlLCBsZXZlbHMgPSBjKDAsIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiTm9uLXNtb2tlciIsICJTbW9rZXIiKSkNCmNvbnRyYXN0cyhsdW5nY2FwJFNtb2tlKQ0KYGBgDQoNCg0KIyMgMS41IEhhaSB0aMOgbmggcGjhuqduIGPhu6dhIG3DtCBow6xuaCB0aOG7kW5nIGvDqg0KDQpNw7QgaMOsbmggdGjhu5FuZyBrw6ogZ+G7k206XFwNCiAtIFRow6BuaCBwaOG6p24gaOG7hyB0aOG7kW5nOiBtw7QgdOG6oyB4dSBoxrDhu5tuZyBjaMOtbmggKHByZWRpY3RvcikuXFwNCiAtIFRow6BuaCBwaOG6p24gbmfhuqt1IG5oacOqbjogbcO0IHThuqMgc2FpIHPhu5EgaG/hurdjIHPhu7EgYmnhur9uIMSR4buZbmcga2jDtG5nIGdp4bqjaSB0aMOtY2ggxJHGsOG7o2MuDQoNCmBgYHtyfQ0KbW9kZWwgPC0gbG0oRkVWIH4gQWdlICsgSHQgKyBHZW5kZXIgKyBTbW9rZSwgZGF0YSA9IGx1bmdjYXApDQpzdW1tYXJ5KG1vZGVsKQ0KYGBgDQoNCg0KIyMgMS42IE3DtCBow6xuaCBo4buTaSBxdXkNCg0KICBHaeG7m2kgdGhp4buHdSBs4bubcCBtw7QgaMOsbmggaOG7k2kgcXV5IOKAkyBu4buBbiB04bqjbmcgY2hvIHThuqV0IGPhuqMgY8OhYyBtw7QgaMOsbmggxJHGsOG7o2MgdHLDrG5oIGLDoHkgdHJvbmcgc8OhY2guIE3DtCBow6xuaCBo4buTaSBxdXkgbcO0IHThuqMgbeG7kWkgcXVhbiBo4buHIHR1eeG6v24gdMOtbmggZ2nhu69hIGJp4bq/biBwaOG6o24gaOG7k2kgdsOgIGPDoWMgYmnhur9uIGdp4bqjaSB0aMOtY2guDQoNCiMjIDEuNyBEaeG7hW4gZ2nhuqNpIG3DtCBow6xuaA0KDQogIFRyw6xuaCBiw6B5IGPDoWNoIGhp4buDdSB2w6AgZ2nhuqNpIHRow61jaCDDvSBuZ2jEqWEgY+G7p2EgY8OhYyBo4buHIHPhu5EgaOG7k2kgcXV5LiBDaG8gYmnhur90IG3hu6ljIMSR4buZIOG6o25oIGjGsOG7n25nIGPhu6dhIHThu6tuZyBiaeG6v24gZ2nhuqNpIHRow61jaCDEkeG6v24gYmnhur9uIHBo4bqjbiBo4buTaS4NCg0KIyMgMS44IFNvIHPDoW5oIG3DtCBow6xuaCB24bqtdCBsw70gdsOgIG3DtCBow6xuaCB0aOG7kW5nIGvDqg0KDQogIFNvIHPDoW5oIGhhaSBsb+G6oWkgbcO0IGjDrG5oOiB24bqtdCBsw70gKGThu7FhIHbDoG8gxJHhu4tuaCBsdeG6rXQgdOG7sSBuaGnDqm4pIHbDoCB0aOG7kW5nIGvDqiAoZOG7sWEgdsOgbyBk4buvIGxp4buHdSkuIE3DtCBow6xuaCB0aOG7kW5nIGvDqiBjw7MgdMOtbmggbGluaCBob+G6oXQgaMahbiBuaMawbmcgY8WpbmcgcGjhu6UgdGh14buZYyB2w6BvIGThu68gbGnhu4d1IG5oaeG7gXUgaMahbi4NCg0KIyMgMS45IE3hu6VjIMSRw61jaCBj4bunYSBtw7QgaMOsbmggdGjhu5FuZyBrw6oNCg0KICBUw7l5IG3hu6VjIHRpw6p1IG3DoCBtw7QgaMOsbmggY8OzIHRo4buDIMSRxrDhu6NjIGTDuW5nIMSR4buDOiBtw7QgdOG6oywgZOG7sSDEkW/DoW4sIGhv4bq3YyBraeG7g20gxJHhu4tuaCBnaeG6oyB0aHV54bq/dC4gTeG7pWMgdGnDqnUg4bqjbmggaMaw4bufbmcgxJHhur9uIGPDoWNoIHjDonkgZOG7sW5nLCBs4buxYSBjaOG7jW4gdsOgIMSRw6FuaCBnacOhIG3DtCBow6xuaC4NCg0KIyMgMS4xMCDEkMOhbmggZ2nDoSBtw7QgaMOsbmg6IENow61uaCB4w6FjIHbDoCDEkcahbiBnaeG6o24NCg0KICBN4buZdCBtw7QgaMOsbmggdOG7kXQgY+G6p24gduG7q2EgKipjaMOtbmggeMOhYyoqIChk4buxIMSRb8OhbiBob+G6t2MgbcO0IHThuqMgdOG7kXQgZOG7ryBsaeG7h3UpLCB24burYSAqKsSRxqFuIGdp4bqjbioqICjDrXQgYmnhur9uLCBk4buFIGhp4buDdSkuIMSQw6J5IGzDoCBoYWkgdGnDqnUgY2jDrSBxdWFuIHRy4buNbmcgdHJvbmcgbOG7sWEgY2jhu41uIG3DtCBow6xuaC4NCg0KIyMgMS4xMSBIaeG7g3UgZ2nhu5tpIGjhuqFuIGPhu6dhIG3DtCBow6xuaA0KDQogIE3DtCBow6xuaCB0aOG7kW5nIGvDqiBraMO0bmcgdGjhu4MgbuG6r20gYuG6r3QgdG/DoG4gYuG7mSB0aOG7sWMgdOG6vy4gVmnhu4djIHBow6JuIGJp4buHdCBnaeG7r2EgZOG7ryBsaeG7h3UgdGjhu7FjIG5naGnhu4dtIChjw7Mga2nhu4NtIHNvw6F0KSB2w6AgcXVhbiBzw6F0IChraMO0bmcga2nhu4NtIHNvw6F0KSBsw6AgcuG6pXQgcXVhbiB0cuG7jW5nIMSR4buDIGhp4buDdSDEkcaw4bujYyB0w61uaCBo4bujcCBs4buHIGPhu6dhIHN1eSBsdeG6rW4uDQoNCiMjIDEuMTIgS2jhuqMgbsSDbmcga2jDoWkgcXXDoXQgaMOzYSBtw7QgaMOsbmgNCg0KICBNw7QgaMOsbmggdOG7kXQgY+G6p24gY8OzIGto4bqjIG7Eg25nIMOhcCBk4bulbmcgdsOgbyBk4buvIGxp4buHdSBt4bubaSBob+G6t2MgdOG7lW5nIHRo4buDLiBN4bulYyBuw6B5IHRo4bqjbyBsdeG6rW4gduG7gSBz4buxIGxpw6puIGvhur90IGdp4buvYSBjw6FjaCBs4bqleSBt4bqrdSBk4buvIGxp4buHdSB2w6Aga2jhuqMgbsSDbmcga2jDoWkgcXXDoXQgaMOzYSBj4bunYSBtw7QgaMOsbmguDQoNCiMjIDEuMTMgR2nhu5tpIHRoaeG7h3Ugc+G7rSBk4bulbmcgUiBjaG8gbcO0IGjDrG5oIGjDs2ENCg0KICBSIGzDoCBjw7RuZyBj4bulIGNow61uaCDEkcaw4bujYyBz4butIGThu6VuZyB4dXnDqm4gc3Xhu5F0IHPDoWNoLiBDw6FjIHbDrSBk4bulIHbDoCBwaMOibiB0w61jaCBtw7QgaMOsbmggdHJvbmcgc8OhY2ggxJHhu4F1IMSRxrDhu6NjIHRyw6xuaCBiw6B5IGLhurFuZyBSIHbhu5tpIG3DoyBuZ3Xhu5NuIMSR4bqneSDEkeG7py4NCiAgDQpgYGB7cn0NCm5hbWVzKGx1bmdjYXApDQpmYWN0b3IobHVuZ2NhcCRHZW5kZXIpDQpwbG90KGx1bmdjYXAkRkVWIH4gbHVuZ2NhcCRBZ2UpDQpgYGANCiAgDQoNCiMjIENIxq/GoE5HIDIuIE3DlCBIw4xOSCBI4buSSSBRVVkgVFVZ4bq+TiBUw41OSA0KDQojIyMgMi4xIEdp4bubaSB0aGnhu4d1DQoNCk3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCBsw6AgbeG7mXQgdHJvbmcgbmjhu69uZyBjw7RuZyBj4bulIGPGoSBi4bqjbiB2w6AgcGjhu5UgYmnhur9uIG5o4bqldCB0cm9uZyB0aOG7kW5nIGvDqi4gTcO0IGjDrG5oIG7DoHkgZ2nDunAgeMOhYyDEkeG7i25oIHbDoCDEkeG7i25oIGzGsOG7o25nIG3hu5FpIHF1YW4gaOG7hyBnaeG7r2EgbeG7mXQgYmnhur9uIHBo4bqjbiBo4buTaSAobGnDqm4gdOG7pWMpIHbDoCBt4buZdCBob+G6t2Mgbmhp4buBdSBiaeG6v24gZ2nhuqNpIHRow61jaCAobGnDqm4gdOG7pWMgaG/hurdjIHBow6JuIGxv4bqhaSkuDQoNCk3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCBsw6AgbuG7gW4gdOG6o25nIGPhu6dhIG5oaeG7gXUgbcO0IGjDrG5oIHRo4buRbmcga8OqLiBDaMO6bmcgbcO0IHThuqMgbeG7kWkgcXVhbiBo4buHIHR1eeG6v24gdMOtbmggZ2nhu69hIG3hu5l0IGJp4bq/biBwaOG6o24gaOG7k2kgbGnDqm4gdOG7pWMgdsOgIG3hu5l0IGhv4bq3YyBuaGnhu4F1IGJp4bq/biBnaeG6o2kgdGjDrWNoLg0KDQojIyMgMi4yIMSQ4buLbmggbmdoxKlhIG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaA0KDQpNw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggdOG7lW5nIHF1w6F0IGJp4buDdSBkaeG7hW4gbeG7kWkgbGnDqm4gaOG7hyB0dXnhur9uIHTDrW5oIGdp4buvYSBiaeG6v24gcGjhuqNuIGjhu5NpIHbDoCBjw6FjIGJp4bq/biBnaeG6o2kgdGjDrWNoLiBCaeG6v24gc2FpIHPhu5EgzrUgxJHhuqFpIGRp4buHbiBjaG8gbmhp4buFdSBsb+G6oW4gbmfhuqt1IG5oacOqbiB2w6AgZ2nhuqMgxJHhu4tuaCBwaMOibiBwaOG7kWkgY2h14bqpbiBjw7MgdHJ1bmcgYsOsbmggMCB2w6AgcGjGsMahbmcgc2FpICBcc2lnbWFeMi4NCg0KTcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIGPDsyBk4bqhbmcgbmjGsCBzYXU6DQoNCiQkDQp5X2kgPSBcYmV0YV8wICsgXGJldGFfMSB4X3tpMX0gKyBcY2RvdHMgKyBcYmV0YV9wIHhfe2lwfSArIFx2YXJlcHNpbG9uX2ksXHF1YWQgXHZhcmVwc2lsb25faSBcc2ltIFxtYXRoY2Fse059KDAsIFxzaWdtYV4yKSANCiQkDQoNCg0KIyMjIDIuMyBI4buTaSBxdXkgdHV54bq/biB0w61uaCDEkcahbg0KDQrEkMOieSBsw6AgdHLGsOG7nW5nIGjhu6NwIMSR4bq3YyBiaeG7h3QgY+G7p2EgbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIHbhu5tpIGNo4buJIG3hu5l0IGJp4bq/biBnaeG6o2kgdGjDrWNoLiBN4bulYyB0acOqdSBsw6AgxrDhu5tjIGzGsOG7o25nIG3hu5FpIHF1YW4gaOG7hyB0dXnhur9uIHTDrW5oIGdp4buvYSBiaeG6v24gxJHhuqd1IHbDoG8geCB2w6AgYmnhur9uIMSR4bqndSByYSB5Lg0KDQpgYGB7cn0NCmxpYnJhcnkoR0xNc0RhdGEpDQpkYXRhKGdlc3RhdGlvbikNCnggPC0gZ2VzdGF0aW9uJEFnZQ0KeSA8LSBnZXN0YXRpb24kV2VpZ2h0DQp3dHMgPC0gZ2VzdGF0aW9uJEJpcnRocw0KYGBgDQoNCiMjIyMgMi4zLjEgxq/hu5tjIGzGsOG7o25nIGLDrG5oIHBoxrDGoW5nIG5o4buPIG5o4bqldCAoT0xTKQ0KDQpQaMawxqFuZyBwaMOhcCBuw6B5IGPDsyBk4bqhbmcgbmjGsCBzYXU6DQoNCiQkDQpcbWluX3tCXzAsIEJfMX0gXHN1bV97aT0xfV5uICh5X2kgLSBCXzAgLSBCXzEgeF9pKV4yDQokJA0KDQpgYGB7cn0NCmJldGEwLkEgPC0gLTAuOTsgYmV0YTEuQSA8LSAwLjENCm11LkEgPC0gYmV0YTAuQSArIGJldGExLkEgKiB4DQpTQSA8LSBzdW0od3RzICogKHkgLSBtdS5BKV4yKTsgU0ENCmBgYA0KDQojIyMjIDIuMy4yIMav4bubYyBsxrDhu6NuZyBo4buHIHPhu5E6DQoNCkjhu4cgc+G7kSBnw7NjIChzbG9wZSkgxJHGsOG7o2MgdMOtbmggdGhlbyBjw7RuZyB0aOG7qWM6DQoNCiQkDQpcYmV0YV8xID0gXGZyYWN7XHN1bV97aT0xfV57bn0gKHhfaSAtIFxiYXJ7eH0pKHlfaSAtIFxiYXJ7eX0pfXtcc3VtX3tpPTF9XntufSAoeF9pIC0gXGJhcnt4fSleMn0NCiQkDQoNCkjhu4cgc+G7kSBjaOG6t24gKGludGVyY2VwdCkgbMOgOg0KDQokJA0KXGJldGFfMCA9IFxiYXJ7eX0gLSBcYmV0YV8xIFxiYXJ7eH0NCiQkDQoNCmBgYHtyfQ0KeGJhciA8LSB3ZWlnaHRlZC5tZWFuKHgsIHc9d3RzKQ0KU1N4IDwtIHN1bSh3dHMgKiAoeCAtIHhiYXIpXjIpDQp5YmFyIDwtIHdlaWdodGVkLm1lYW4oeSwgdz13dHMpDQpTU3h5IDwtIHN1bSh3dHMgKiAoeCAtIHhiYXIpICogeSkNCmJldGExIDwtIFNTeHkgLyBTU3gNCmJldGEwIDwtIHliYXIgLSBiZXRhMSAqIHhiYXINCm11IDwtIGJldGEwICsgYmV0YTEgKiB4DQpSU1MgPC0gc3VtKHd0cyAqICh5IC0gbXUpXjIpDQpjKGJldGEwPWJldGEwLCBiZXRhMT1iZXRhMSwgUlNTPVJTUykNCmBgYA0KDQojIyMjIDIuMy4zIMav4bubYyBsxrDhu6NuZyBwaMawxqFuZyBzYWkgdsOgIHBo4bqnbiBkxrA6DQoNClNhdSBraGkgxrDhu5tjIGzGsOG7o25nIMSRxrDhu6NjIG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCDEkcahbjoNCg0KJCQNCnlfaSA9IFxiZXRhXzAgKyBcYmV0YV8xIHhfaSArIFx2YXJlcHNpbG9uX2ksDQokJA0KDQp0cm9uZyDEkcOzIFwoIFx2YXJlcHNpbG9uX2kgXHNpbSBcbWF0aGNhbHtOfSgwLCBcc2lnbWFeMikgXCksIHRhIGPhuqduIMaw4bubYyBsxrDhu6NuZyBwaMawxqFuZyBzYWkgY+G7p2Egc2FpIHPhu5Egbmfhuqt1IG5oacOqbiDEkeG7gyDEkcOhbmggZ2nDoSBt4bupYyDEkeG7mSBwaMOibiB0w6FuIGPhu6dhIGThu68gbGnhu4d1IHNvIHbhu5tpIG3DtCBow6xuaC4NCg0KUGjGsMahbmcgc2FpIHNhaSBz4buRIMSRxrDhu6NjIMaw4bubYyBsxrDhu6NuZyB0aGVvIGPDtG5nIHRo4bupYzoNCg0KJCQNClxoYXR7XHNpZ21hfV4yID0gXGZyYWN7MX17biAtIDJ9IFxzdW1fe2k9MX1ee259ICh5X2kgLSBcaGF0e3l9X2kpXjINCiQkDQoNClRyb25nIMSRw7M6DQoNCi0gXCggXGhhdHtcc2lnbWF9XjIgXCk6IGzDoCDGsOG7m2MgbMaw4bujbmcgcGjGsMahbmcgc2FpIHNhaSBz4buRIChyZXNpZHVhbCB2YXJpYW5jZSksDQotIFwoIHlfaSBcKTogbMOgIGdpw6EgdHLhu4sgcXVhbiBzw6F0LA0KLSBcKCBcaGF0e3l9X2kgXCk6IGzDoCBnacOhIHRy4buLIGThu7EgxJFvw6FuIHThu6sgbcO0IGjDrG5oLA0KLSBcKCBuIFwpOiBsw6Agc+G7kSBsxrDhu6NuZyBxdWFuIHPDoXQsDQotIFwoIG4gLSAyIFwpOiBsw6AgYuG6rWMgdOG7sSBkbywgZG8gY8OzIGhhaSBo4buHIHPhu5EgXCggXGJldGFfMCBcKSB2w6AgXCggXGJldGFfMSBcKSDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcuDQoNCmBgYHtyfQ0KZGYgPC0gbGVuZ3RoKHkpIC0gMg0KczIgPC0gUlNTIC8gZGYNCmMoZGYgPSBkZiwgcyA9IHNxcnQoczIpLCBzMiA9IHMyKQ0KYGBgDQoNClBoxrDGoW5nIHNhaSBzYWkgc+G7kSBjw6BuZyBuaOG7jyB0aMOsIG3DtCBow6xuaCBjw6BuZyBwaMO5IGjhu6NwIHbhu5tpIGThu68gbGnhu4d1IHRo4buxYyB04bq/Lg0KDQrGr+G7m2MgbMaw4bujbmcgcGjGsMahbmcgc2FpIHBo4bqnbiBkxrA6DQoNCiQkDQpzXjIgPSBcZnJhY3tSU1N9e24gLSBwJ30NCiQkDQojIyMjIDIuMy40IFNhaSBz4buRIGNodeG6qW46DQoNClNhaSBz4buRIGNodeG6qW4gY+G7p2EgaOG7hyBz4buRIGfDs2MgxJHGsOG7o2MgdMOtbmggdGhlbyBjw7RuZyB0aOG7qWM6DQoNCiQkDQpTRShcaGF0e1xiZXRhfV8xKSA9IFxzcXJ0eyBcZnJhY3tcaGF0e1xzaWdtYX1eMn17XHN1bV97aT0xfV57bn0gKHhfaSAtIFxiYXJ7eH0pXjJ9IH0NCiQkDQoNClRyb25nIMSRw7M6DQoNCi0gXCggXGhhdHtcc2lnbWF9XjIgXCk6IGzDoCDGsOG7m2MgbMaw4bujbmcgcGjGsMahbmcgc2FpIHNhaSBz4buRLA0KLSBcKCB4X2kgXCk6IGzDoCBnacOhIHRy4buLIGPhu6dhIGJp4bq/biDEkeG7mWMgbOG6rXAsDQotIFwoIFxiYXJ7eH0gXCk6IGzDoCB0cnVuZyBiw6xuaCBj4bunYSBcKCB4IFwpLA0KLSBcKCBTRShcaGF0e1xiZXRhfV8xKSBcKTogbMOgIHNhaSBz4buRIGNodeG6qW4gY+G7p2EgaOG7hyBz4buRIGjhu5NpIHF1eSBcKCBcaGF0e1xiZXRhfV8xIFwpLg0KDQpgYGB7cn0NCnZhci5iMCA8LSBzMiAqICgxL3N1bSh3dHMpICsgeGJhcl4yIC8gU1N4KQ0KdmFyLmIxIDwtIHMyIC8gU1N4DQpzcXJ0KGMoYmV0YTAgPSB2YXIuYjAsIGJldGExID0gdmFyLmIxKSkNCmBgYA0KDQpTYWkgc+G7kSBjaHXhuqluIGPDoG5nIG5o4buPIHRow6wgxrDhu5tjIGzGsOG7o25nIFwoIFxoYXR7XGJldGF9XzEgXCkgY8OgbmcgY2jDrW5oIHjDoWMuDQoNCg0KIyMjIyAyLjQgSOG7k2kgcXV5IHR1eeG6v24gdMOtbmggYuG7mWkNCg0KS2hpIGPDsyBuaGnhu4F1IGJp4bq/biBnaeG6o2kgdGjDrWNoLCBtw7QgaMOsbmggdHLhu58gdGjDoG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIGLhu5lpLiBWaeG7h2MgdGjDqm0gYmnhur9uIGNobyBwaMOpcCBtw7QgaMOsbmggZ2nhuqNpIHRow61jaCB04buRdCBoxqFuIHPhu7EgYmnhur9uIHRoacOqbiB0cm9uZyBk4buvIGxp4buHdSwgbmjGsG5nIGPFqW5nIGzDoG0gdMSDbmcgbmd1eSBjxqEgxJFhIGPhu5luZyB0dXnhur9uIGhv4bq3YyBvdmVyZml0dGluZy4NCg0KVMawxqFuZyB04buxIGjhu5NpIHF1eSDEkcahbiBuaMawbmcgY8OzIG5oaeG7gXUgYmnhur9uIHguIFBow6JuIHTDrWNoIGLhurFuZyBPTFMgduG6q24gw6FwIGThu6VuZy4NCg0KIyMjIyAyLjUgROG6oW5nIG1hIHRy4bqtbiBj4bunYSBtw7QgaMOsbmggaOG7k2kgcXV5DQoNClZp4bq/dCBtw7QgaMOsbmggZMaw4bubaSBk4bqhbmcgbWEgdHLhuq1uIGdpw7pwIGJp4buDdSBkaeG7hW4gbmfhuq9uIGfhu41uIHbDoCB0aHXhuq1uIHRp4buHbiBoxqFuIGtoaSB44butIGzDvSBi4bqxbmcgbcOheSB0w61uaC4gVmnhu4djIMaw4bubYyBsxrDhu6NuZyBo4buHIHPhu5Egc+G7rSBk4bulbmcgY8O0bmcgdGjhu6ljIMSR4bqhaSBz4buRIHR1eeG6v24gdMOtbmggbMOgIG7hu4FuIHThuqNuZyBj4bunYSBuaGnhu4F1IHRodeG6rXQgdG/DoW4gaOG7k2kgcXV5IGhp4buHbiDEkeG6oWkuDQoNCk3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCBuaGnhu4F1IGJp4bq/biBjw7MgdGjhu4MgxJHGsOG7o2Mgdmnhur90IGfhu41uIGTGsOG7m2kgZOG6oW5nIG1hIHRy4bqtbiBuaMawIHNhdToNCg0KJCQNClxtYXRoYmZ7eX0gPSBcbWF0aGJme1h9IFxib2xkc3ltYm9se1xiZXRhfSArIFxib2xkc3ltYm9se1x2YXJlcHNpbG9ufQ0KJCQNCg0KVHJvbmcgxJHDszoNCg0KLSBcKCBcbWF0aGJme3l9IFwpOiBsw6AgdmVjdG9yIFwoIG4gXHRpbWVzIDEgXCkgY2jhu6lhIGPDoWMgZ2nDoSB0cuG7iyBiaeG6v24gcGjhu6UgdGh14buZYywNCi0gXCggXG1hdGhiZntYfSBcKTogbMOgIG1hIHRy4bqtbiB0aGnhur90IGvhur8gXCggbiBcdGltZXMgcCBcKSAoZ+G7k20gMSBj4buZdCB0b8OgbiAxIGNobyBo4buHIHPhu5EgY2jhurduIHbDoCBjw6FjIGPhu5l0IGJp4bq/biDEkeG7mWMgbOG6rXApLA0KLSBcKCBcYm9sZHN5bWJvbHtcYmV0YX0gXCk6IGzDoCB2ZWN0b3IgaOG7hyBz4buRIGjhu5NpIHF1eSBcKCBwIFx0aW1lcyAxIFwpLA0KLSBcKCBcYm9sZHN5bWJvbHtcdmFyZXBzaWxvbn0gXCk6IGzDoCB2ZWN0b3Igc2FpIHPhu5Egbmfhuqt1IG5oacOqbiBcKCBuIFx0aW1lcyAxIFwpLg0KDQpI4buHIHPhu5EgaOG7k2kgcXV5IMSRxrDhu6NjIMaw4bubYyBsxrDhu6NuZyB0aGVvIGPDtG5nIHRo4bupYyBiw6xuaCBwaMawxqFuZyB04buRaSB0aGnhu4N1Og0KDQokJA0KXGhhdHtcYm9sZHN5bWJvbHtcYmV0YX19ID0gKFxtYXRoYmZ7WH1eVCBcbWF0aGJme1h9KV57LTF9IFxtYXRoYmZ7WH1eVCBcbWF0aGJme3l9DQokJA0KDQrEkGnhu4F1IG7DoHkgZ2nhuqMgxJHhu4tuaCBy4bqxbmcgXCggXG1hdGhiZntYfV5UIFxtYXRoYmZ7WH0gXCkga2jhuqMgbmdo4buLY2ggKG5naMSpYSBsw6Aga2jDtG5nIGPDsyDEkWEgY+G7mW5nIHR1eeG6v24gaG/DoG4gdG/DoG4pLg0KDQpgYGB7cn0NCmxpYnJhcnkoR0xNc0RhdGEpDQpkYXRhKGx1bmdjYXApDQpYIDwtIG1vZGVsLm1hdHJpeCh+IEFnZSArIEh0ICsgR2VuZGVyICsgU21va2UsIGRhdGEgPSBsdW5nY2FwKQ0KeSA8LSBsb2cobHVuZ2NhcCRGRVYpDQpiZXRhIDwtIHNvbHZlKHQoWCkgJSolIFgpICUqJSB0KFgpICUqJSB5DQpmaXR0ZWQgPC0gWCAlKiUgYmV0YQ0KYGBgDQoNCiMjIyMgMi42IMav4bubYyBsxrDhu6NuZyBi4bqxbmcgUg0KDQpWw60gZOG7pSAqKipNw7QgaMOsbmggaOG7k2kgcXV5IHRyb25nIFIqKiosIGPDsyB0aOG7gyBkw7luZyBs4buHbmggc2F1Og0KDQpNw7QgaMOsbmggaOG7k2kgcXV5IMSRxqFuOg0KDQokJA0KbW9kZWxfc2ltcGxlIDwtIGxtKG1wZyB+IHd0LCBkYXRhID0gbXRjYXJzKQ0KJCQNCk3DtCBow6xuaCBo4buTaSBxdXkgYuG7mWkNCg0KJCQNCm1vZGVsX211bHRpIDwtIGxtKG1wZyB+IHd0ICsgaHAsIGRhdGEgPSBtdGNhcnMpDQokJA0KDQpgYGB7cn0NCkxDLm0xIDwtIGxtKGxvZyhGRVYpIH4gQWdlICsgSHQgKyBHZW5kZXIgKyBTbW9rZSwgZGF0YSA9IGx1bmdjYXApDQpzdW1tYXJ5KExDLm0xKQ0KY29lZihMQy5tMSkNCmBgYA0KDQoNCiMjIyMgMi43IERp4buFbiBnaeG6o2kgaOG7hyBz4buRIGjhu5NpIHF1eQ0KDQpN4buXaSBo4buHIHPhu5EgaOG7k2kgcXV5IGJp4buDdSBkaeG7hW4gc+G7sSB0aGF5IMSR4buVaSB0cnVuZyBiw6xuaCBj4bunYSBiaeG6v24gcGjhuqNuIGjhu5NpIHkga2hpIGJp4bq/biBnaeG6o2kgdGjDrWNoIHTEg25nIG3hu5l0IMSRxqFuIHbhu4ssIHRyb25nIGtoaSBjw6FjIGJp4bq/biBraMOhYyBnaeG7ryBuZ3V5w6puLiBWaeG7h2MgZGnhu4VuIGdp4bqjaSDEkcO6bmcgZ2nDunAgxJHGsGEgcmEga+G6v3QgbHXhuq1uIGPDsyDDvSBuZ2jEqWEgdGjhu7FjIHRp4buFbi4NCg0KSOG7hyBz4buRIFwoIFxiZXRhX2ogXCk6IMSR4bqhaSBkaeG7h24gY2hvIOG6o25oIGjGsOG7n25nIGJpw6puIGPhu6dhIGJp4bq/biBcKCB4X2ogXCkgxJHhur9uIFwoIHkgXCkuICANCg0KPiBN4buXaSBraGkgXCggeF9qIFwpIHRoYXkgxJHhu5VpIG3hu5l0IMSRxqFuIHbhu4sgKGdp4buvIGPDoWMgYmnhur9uIGtow6FjIGtow7RuZyDEkeG7lWkpLCB0aMOsIFwoIHkgXCkgdGhheSDEkeG7lWkgdHJ1bmcgYsOsbmggXCggXGJldGFfaiBcKSDEkcahbiB24buLLg0KDQpIYXkgbsOzaSBjw6FjaCBraMOhYzoNCg0KPiBN4buXaSAxIMSRxqFuIHbhu4sgdGhheSDEkeG7lWkgdHJvbmcgXCggeF9qIFwpIGThuqtuIMSR4bq/biB0aGF5IMSR4buVaSB0cnVuZyBiw6xuaCBcKCBcYmV0YV9qIFwpIMSRxqFuIHbhu4sgdHJvbmcgXCggeSBcKSwgZ2nhuqMgc+G7rSBjw6FjIGJp4bq/biBraMOhYyDEkcaw4bujYyBnaeG7ryBuZ3V5w6puLg0KDQoqKlbDrSBk4bulIGjhu4cgc+G7kSBj4bunYSBjaGnhu4F1IGNhbyBsw6AgMC4wNDI4IOKGkiBsb2coRkVWKSB0xINuZyAwLjA0MjggbuG6v3UgY2hp4buBdSBjYW8gdMSDbmcgMSDEkcahbiB24buLIChnaeG7ryBjw6FjIGJp4bq/biBraMOhYyBraMO0bmcgxJHhu5VpKS4qKg0KDQojIyMjIDIuOCBTdXkgbHXhuq1uIHRo4buRbmcga8OqDQoNClNhdSBraGkgxrDhu5tjIGzGsOG7o25nLCBj4bqnbiDEkcOhbmggZ2nDoSB4ZW0gY8OhYyBo4buHIHPhu5EgaOG7k2kgcXV5IGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6oga2jDtG5nLiBUYSBz4butIGThu6VuZyBraeG7g20gxJHhu4tuaCBnaeG6oyB0aHV54bq/dCB2w6Aga2hv4bqjbmcgdGluIGPhuq15IMSR4buDIMSRxrBhIHJhIGvhur90IGx14bqtbiB24buBIMO9IG5naMSpYSB2w6AgxJHhu5kgY2jDrW5oIHjDoWMgY+G7p2EgY8OhYyDGsOG7m2MgbMaw4bujbmcuDQoNCj4gS2nhu4NtIMSR4buLbmg6DQoNCiQkDQpIXzAgOiBcYmV0YV9qID0gMCBccXVhZCBcdGV4dHt2c30gXHF1YWQgSF8xIDogXGJldGFfaiBcbmUgMA0KJCQNCg0KPiBLaG/huqNuZyB0aW4gY+G6rXk6DQoNCiQkDQpcaGF0e1xiZXRhfV9qIFxwbSB0X3tuIC0gcCwgXGFscGhhLzJ9IFxjZG90IFNFKFxoYXR7XGJldGF9X2opDQokJA0KDQpgYGB7cn0NCnN1bW1hcnkoTEMubTEpDQpgYGANCg0KDQojIyMjIDIuOSBQaMOibiB0w61jaCBwaMawxqFuZyBzYWkgKEFOT1ZBKQ0KDQpQaMOibiB0w61jaCBwaMawxqFuZyBzYWkgZ2nDunAgxJHDoW5oIGdpw6EgbeG7qWMgxJHhu5kgcGjDuSBo4bujcCBj4bunYSBtw7QgaMOsbmggYuG6sW5nIGPDoWNoIGNoaWEgdOG7lW5nIHBoxrDGoW5nIHNhaSB0aMOgbmggcGjhuqduIGdp4bqjaSB0aMOtY2ggxJHGsOG7o2MgKFNTUikgdsOgIGtow7RuZyBnaeG6o2kgdGjDrWNoIMSRxrDhu6NjIChTU0UpLiDEkMOieSBsw6AgYsaw4bubYyBxdWFuIHRy4buNbmcgxJHhu4Mga2nhu4NtIMSR4buLbmggdOG7lW5nIHRo4buDIG3DtCBow6xuaC4NCg0KVMOhY2ggdOG7lW5nIHBoxrDGoW5nIHNhaToNCg0KLSBTU1I6IGRvIG3DtCBow6xuaA0KDQotIFNTRTogcGjhuqduIGTGsA0KDQojIyMjIDIuMTAgU28gc8OhbmggbcO0IGjDrG5oIGzhu5NuZyBuaGF1DQoNCsSQ4buDIHNvIHPDoW5oIGhhaSBtw7QgaMOsbmggdHJvbmcgUg0KJCQNCi0gbW9kZWwxIDwtIGxtKG1wZyB+IHd0LCBkYXRhID0gbXRjYXJzKSBcXA0KLSBtb2RlbDIgPC0gbG0obXBnIH4gd3QgKyBocCwgZGF0YSA9IG10Y2FycykgXFwNCi0gYW5vdmEobW9kZWwxLCBtb2RlbDIpDQokJA0KDQojIyMjIDIuMTEgU28gc8OhbmggbcO0IGjDrG5oIGtow7RuZyBs4buTbmcgbmhhdSAoQUlDL0JJQykNCg0KLSBBSUMobW9kZWwxLCBtb2RlbDIpDQotIEJJQyhtb2RlbDEsIG1vZGVsMikNCg0KIyMjIyAyLjEyIENo4buNbiBiaeG6v24gdHJvbmcgbcO0IGjDrG5oDQoNCi0gc3RlcChtb2RlbDIpDQoNCiMjIyMgMi4xMyBOZ2hpw6puIGPhu6l1IHTDrG5oIGh14buRbmcNCg0KUGjhuqduIG7DoHkgdHLDrG5oIGLDoHkgbeG7mXQgdsOtIGThu6UgdGjhu7FjIHThur8gw6FwIGThu6VuZyBtw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggdsOgbyBwaMOibiB0w61jaCBk4buvIGxp4buHdS4gUXVhIMSRw7MsIG5nxrDhu51pIGjhu41jIGPDsyB0aOG7gyByw6huIGx1eeG7h24ga+G7uSBuxINuZyBtw7QgaMOsbmggaMOzYSwga2nhu4NtIMSR4buLbmgsIHbDoCBkaeG7hW4gZ2nhuqNpIGvhur90IHF14bqjIG3DtCBow6xuaA0KDQpQaMOibiB0w61jaCB04bqtcCBk4buvIGxp4buHdSB0aOG7sWMgdOG6vywgxrDhu5tjIGzGsOG7o25nLCBkaeG7hW4gZ2nhuqNpIHbDoCDEkcOhbmggZ2nDoSBtw7QgaMOsbmguDQoNCiMjIyMgMi4xNCBE4buxIMSRb8OhbiB24bubaSBwcmVkaWN0KCkNCg0KJCQNCnByZWRpY3QobW9kZWxfbXVsdGksIG5ld2RhdGEgPSBkYXRhLmZyYW1lKHd0ID0gMywgaHAgPSAxMjApKQ0KJCQNCg0KIyMjIyAyLjE1IFTDs20gdOG6r3QNCg0KQ2jGsMahbmcgbsOgeSDEkcOjIHRyw6xuaCBiw6B5IMSR4bqneSDEkeG7pyB04burIMSR4buLbmggbmdoxKlhIMSR4bq/biB0aOG7sWMgaMOgbmggbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oLiBOZ8aw4budaSDEkeG7jWMgbuG6r20gxJHGsOG7o2MgY8O0bmcgY+G7pSBsw70gdGh1eeG6v3QgdsOgIGvhu7kgbsSDbmcgdGjhu7FjIGjDoG5oIMSR4buDIMOhcCBk4bulbmcgdsOgbyBwaMOibiB0w61jaCBk4buvIGxp4buHdSDEkeG7i25oIGzGsOG7o25nLg0KDQpNw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggbMOgIG7hu4FuIHThuqNuZyBj4bunYSBHTE0NCg0KT0xTIMaw4bubYyBsxrDhu6NuZyBo4buHIHPhu5EgdOG7kXQgbmjhuqV0DQoNClIgY3VuZyBj4bqlcCBjw7RuZyBj4bulIGhp4buHdSBxdeG6oyDEkeG7gyDGsOG7m2MgbMaw4bujbmcsIGtp4buDbSDEkeG7i25oIHbDoCBk4buxIMSRb8Ohbg0KDQoNCiMjIENoxrDGoW5nIDMuTcOUIEjDjE5IIFRVWeG6vk4gVMONTkggVOG7lE5HIFFVw4FUIChHTE0pIA0KDQojIyMgMy4xIEdp4bubaSB0aGnhu4d1IHbDoCB04buVbmcgcXVhbg0KDQpHTE0gbMOgIHPhu7EgbeG7nyBy4buZbmcgY+G7p2EgaOG7k2kgcXV5IHR1eeG6v24gdMOtbmggxJHhu4MgeOG7rSBsw70gY8OhYyBk4bqhbmcgZOG7ryBsaeG7h3UgbmjGsCDEkeG6v20sIG5o4buLIHBow6JuLCBkxrDGoW5nIGxpw6puIHThu6VjLi4uDQpHTE0gZ+G7k20gaGFpIHBo4bqnbjogcGjDom4gcGjhu5FpIChFRE0pIHbDoCBow6BtIGxpw6puIGvhur90Lg0KDQojIyMgMy4yIEjhu40gcGjDom4gcGjhu5FpIG3FqSAoRXhwb25lbnRpYWwgRmFtaWx5KQ0KDQpcWw0KUCh5OyBcdGhldGEsIFxwaGkpID0gYSh5LCBccGhpKSBcZXhwXGxlZnRceyBcZnJhY3t5IFx0aGV0YSAtIFxrYXBwYShcdGhldGEpfXtccGhpfSBccmlnaHRcfQ0KXF0NCg0KVsOtIGThu6U6DQotIE5vcm1hbDogXCggXHRoZXRhID0gXG11IFwpDQotIFBvaXNzb246IFwoIFx0aGV0YSA9IFxsb2coXG11KSBcKQ0KLSBCaW5vbWlhbDogXCggXHRoZXRhID0gXGxvZyhcbXUgLyAoMSAtIFxtdSkpIFwpDQotIEdhbW1hOiBcKCBcdGhldGEgPSAtMS9cbXUgXCkNCg0KIyMjIDMuMyBL4buzIHbhu41uZyB2w6AgcGjGsMahbmcgc2FpDQoNClxbDQpFW3ldID0gXGthcHBhJyhcdGhldGEpID0gXG11LCBccXVhZCBcdGV4dHtWYXJ9W3ldID0gXHBoaSBca2FwcGEnJyhcdGhldGEpID0gXHBoaSBWKFxtdSkNClxdDQoNCiMjIyAzLjQgTcO0IGjDrG5oIEdMTQ0KDQpcWw0KZyhcbXVfaSkgPSBvX2kgKyBcYmV0YV8wICsgXHN1bV97aj0xfV57cH0gXGJldGFfaiB4X3tqaX0NClxdDQoNCg0KYGBge3J9DQojIEtp4buDbSB0cmEgdHLGsOG7m2Mga2hpIGTDuW5nDQpzdHIobHVuZ2NhcCkNCiMgTcO0IGjDrG5oIEdhdXNzaWFuDQpnbG0oRkVWIH4gQWdlICsgSHQgKyBHZW5kZXIgKyBTbW9rZSwgZmFtaWx5ID0gZ2F1c3NpYW4obGluayA9ICJpZGVudGl0eSIpLCBkYXRhID0gbHVuZ2NhcCkNCg0KIyBNw7QgaMOsbmggUG9pc3NvbiAtIHThuqFvIGThu68gbGnhu4d1IHBow7kgaOG7o3ANCnNldC5zZWVkKDEpDQpsdW5nY2FwJHlfcG9pcyA8LSBycG9pcyhucm93KGx1bmdjYXApLCBsYW1iZGEgPSAyKQ0KZ2xtKHlfcG9pcyB+IEFnZSwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpLCBkYXRhID0gbHVuZ2NhcCkNCg0KIyBNw7QgaMOsbmggbG9naXN0aWMgLSB04bqhbyBiaeG6v24gbmjhu4sgcGjDom4NCmx1bmdjYXAkeV9iaW4gPC0gaWZlbHNlKGx1bmdjYXAkU21va2UgPT0gInllcyIsIDEsIDApDQpnbG0oeV9iaW4gfiBBZ2UsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwgZGF0YSA9IGx1bmdjYXApDQpgYGANCg0KIyMjIDMuNSDGr+G7m2MgbMaw4bujbmcgaOG7hyBz4buRDQoNCsav4bubYyBsxrDhu6NuZyBo4bujcCBsw70gdOG7kWkgxJFhIChNTEUpIHF1YSB0aHXhuq10IHRvw6FuIElSTFMgKGl0ZXJhdGl2ZWx5IHJld2VpZ2h0ZWQgbGVhc3Qgc3F1YXJlcykuDQoNCmBgYHtyfQ0KeDEgPC0gbHVuZ2NhcCRBZ2UNCngyIDwtIGx1bmdjYXAkSHQNCnkgPC0gbHVuZ2NhcCR5X3BvaXMNCmdsbSh5IH4geDEgKyB4MiwgZmFtaWx5ID0gcG9pc3NvbikNCmBgYA0KDQojIyMgMy42IERldmlhbmNlDQoNClxbDQpEKHksIFxoYXR7XG11fSkgPSAyIFxzdW0gd19pIFxsZWZ0WyB5X2kgXGxvZ1xsZWZ0KFxmcmFje3lfaX17XGhhdHtcbXV9X2l9XHJpZ2h0KSAtICh5X2kgLSBcaGF0e1xtdX1faSkgXHJpZ2h0XQ0KXF0NCg0KIyMjIDMuNyBRdXkgdHLDrG5oIMaw4bubYyBsxrDhu6NuZyBHTE0NCg0KMS4gQ2jhu41uIHBow6JuIHBo4buRaSAoRURNKQ0KMi4gQ2jhu41uIGjDoG0gbGnDqm4ga+G6v3QNCjMuIFjDonkgZOG7sW5nIGxvZy1saWtlbGlob29kDQo0LiBHaeG6o2kgYuG6sW5nIElSTFMNCjUuIMSQw6FuaCBnacOhIHNhaSBz4buRIGNodeG6qW4sIHAtdmFsdWUNCg0KYGBge3J9DQojIEfDoW4gYmnhur9uIHRyxrDhu5tjIGtoaSBkw7luZw0KeCA8LSBsdW5nY2FwJEFnZQ0KeSA8LSBsdW5nY2FwJHlfcG9pcw0KDQpmaXQgPC0gZ2xtKHkgfiB4LCBmYW1pbHkgPSBwb2lzc29uKQ0Kc3VtbWFyeShmaXQpDQpgYGANCg0KIyMjIDMuOCBTbyBzw6FuaCB24bubaSBo4buTaSBxdXkgdHV54bq/biB0w61uaA0KDQotIEjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIGzDoCB0csaw4budbmcgaOG7o3AgxJHhurdjIGJp4buHdCBj4bunYSBHTE0gKE5vcm1hbCArIGlkZW50aXR5IGxpbmspLg0KLSBHTE0gbeG7nyBy4buZbmcgxJHhu4MgeOG7rSBsw70gYmnhur9uIMSR4bq/bSwgbmjhu4sgcGjDom4sIGTGsMahbmcgbGnDqm4gdOG7pWMuLi4NCi0gQ8OhYyBraMOhaSBuaeG7h20gbmjGsCBwaOG6p24gZMawLCBsZXZlcmFnZSB24bqrbiDEkcaw4bujYyBnaeG7ryBs4bqhaSB0aMO0bmcgcXVhIElSTFMuDQoNCiMjIyAzLjkgVMOzbSB04bqvdCBjaMawxqFuZw0KDQpHTE0gbMOgIGtodW5nIG3DtCBow6xuaCBow7NhIHLhuqV0IG3huqFuaCwgw6FwIGThu6VuZyBjaG8gbmhp4buBdSBsb+G6oWkgZOG7ryBsaeG7h3UsIGThu7FhIHRyw6puIHBow6JuIHBo4buRaSB0aHXhu5ljIGjhu40gbcWpIHbDoCBow6BtIGxpw6puIGvhur90Lg0KDQoNCiMjIENoxrDGoW5nIDQuxq/hu5pDIEzGr+G7ok5HIFbDgCBTVVkgTFXhuqxOIFRST05HIEdMTSAgDQoNCiMjIyA0LjEgR2nhu5tpIHRoaeG7h3UgdsOgIHThu5VuZyBxdWFuDQoNClPhu60gZOG7pW5nIGjhu6NwIGzDvSB04buRaSDEkWEgKE1MRSkgxJHhu4MgxrDhu5tjIGzGsOG7o25nIGjhu4cgc+G7kSB2w6AgdGhhbSBz4buRIHBow6JuIHTDoW4gXChccGhpXCkuIEtp4buDbSDEkeG7i25oIFdhbGQsIExSVCB2w6AgU2NvcmUgxJHGsOG7o2MgZ2nhu5tpIHRoaeG7h3UgxJHhu4Mga2nhu4NtIMSR4buLbmggZ2nhuqMgdGh1eeG6v3QuDQoNCiMjIyA0LjIgxq/hu5tjIGzGsOG7o25nIFwoXHBoaVwpDQoNCmBgYG1hdGgNClx0aWxkZXtccGhpfSA9IFxmcmFje0QoeSwgXGhhdHtcbXV9KX17biAtIHAnfSxccXVhZA0KXGJhcntccGhpfSA9IFxmcmFjezF9e24gLSBwJ30gXHN1bSBcZnJhY3t3X2kgKHlfaSAtIFxoYXR7XG11fV9pKV4yfXtWKFxoYXR7XG11fV9pKX0NCmBgYA0KDQojMyMgNC4zIMav4bubYyBsxrDhu6NuZyBHTE0gYuG6sW5nIFINCg0KYGBge3J9DQp4MSA8LSBsdW5nY2FwJEFnZQ0KeDIgPC0gbHVuZ2NhcCRIdA0KeSA8LSBsdW5nY2FwJHlfcG9pcw0KZ2xtKHkgfiB4MSArIHgyLCBmYW1pbHkgPSBwb2lzc29uKQ0KZ2xtLm91dCA8LSBnbG0oeSB+IHgxICsgeDIsIGZhbWlseSA9IHBvaXNzb24sIGRhdGEgPSBsdW5nY2FwKQ0Kc3VtbWFyeShnbG0ub3V0KQ0KYGBgDQoNCiMjIyA0LjQgRGnhu4VuIGdp4bqjaSBtw7QgaMOsbmggR0xNDQoNCmBgYG1hdGgNClxsb2coXG11X2kpID0gXGJldGFfMCArIFxiZXRhXzEgeF9pIFxSaWdodGFycm93IFxtdV9pID0gXGV4cChcYmV0YV8wICsgXGJldGFfMSB4X2kpDQpgYGANCg0KIyMjIDQuNSDGr+G7m2MgbMaw4bujbmcgdHJ1bmcgYsOsbmggdsOgIHNhaSBz4buRIGNodeG6qW4NCg0KYGBge3J9DQpwcmVkIDwtIHByZWRpY3QoZ2xtLm91dCwgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoeDEgPSAyLCB4MiA9IDMpLCBzZS5maXQgPSBUUlVFLCB0eXBlID0gImxpbmsiKQ0KZml0Lm11IDwtIGZhbWlseShnbG0ub3V0KSRsaW5raW52KHByZWQkZml0KQ0Kc2UubXUgPC0gcHJlZCRzZS5maXQgKiBmYW1pbHkoZ2xtLm91dCkkbXUuZXRhKHByZWQkZml0KQ0KYGBgDQoNCiMjIyA0LjYgxq/hu5tjIGzGsOG7o25nIFwoXHBoaVwpIHbhu5tpIHF1YXNpLWxpa2VsaWhvb2QNCg0KRMO5bmcgZGV2aWFuY2UgaG/hurdjIFBlYXJzb24uDQoNCiMjIyA0LjcgS2nhu4NtIMSR4buLbmggV2FsZA0KDQpgYGBtYXRoDQpaID0gXGZyYWN7XGhhdHtcYmV0YX1faiAtIFxiZXRhX2peMH17c2UoXGhhdHtcYmV0YX1fail9IFxzaW0gTigwLCAxKQ0KYGBgDQoNCiMjIyA0LjggS2nhu4NtIMSR4buLbmggTFJUDQoNCmBgYHtyfQ0KDQojIE3DtCBow6xuaCBuaOG7jyBoxqFuICjDrXQgYmnhur9uIGjGoW4pDQpnbG0uZml0MSA8LSBnbG0oeV9wb2lzIH4gQWdlLCBmYW1pbHkgPSBwb2lzc29uLCBkYXRhID0gbHVuZ2NhcCkNCg0KIyBNw7QgaMOsbmggxJHhuqd5IMSR4bunIGjGoW4gKG5oaeG7gXUgYmnhur9uIGjGoW4pDQpnbG0uZml0MiA8LSBnbG0oeV9wb2lzIH4gQWdlICsgSHQsIGZhbWlseSA9IHBvaXNzb24sIGRhdGEgPSBsdW5nY2FwKQ0KDQojIFNvIHPDoW5oIGhhaSBtw7QgaMOsbmggYuG6sW5nIGtp4buDbSDEkeG7i25oIENoaS1zcXVhcmVkDQphbm92YShnbG0uZml0MSwgZ2xtLmZpdDIsIHRlc3QgPSAiQ2hpc3EiKQ0KYGBgDQoNCiMjIyA0LjkgS2nhu4NtIMSR4buLbmggU2NvcmUNCg0KDQpgYGB7cn0NCiMgTG9hZCB0aMawIHZp4buHbg0KbGlicmFyeShzdGF0bW9kKQ0KDQojIE3DtCBow6xuaCBudWxsIChjaOG7iSBjw7MgaW50ZXJjZXB0KQ0KZ2xtLmZpdC5udWxsIDwtIGdsbSh5X3BvaXMgfiAxLCBmYW1pbHkgPSBwb2lzc29uLCBkYXRhID0gbHVuZ2NhcCkNCg0KIyBU4bqhbyBiaeG6v24gY+G6p24ga2nhu4NtIMSR4buLbmggdGjDqm0gdsOgbw0KeDEgPC0gbW9kZWwubWF0cml4KH4gQWdlLCBkYXRhID0gbHVuZ2NhcClbLCAtMV0gICMgYuG7jyBpbnRlcmNlcHQNCg0KIyBUaOG7sWMgaGnhu4duIHNjb3JlIHRlc3QNCmdsbS5zY29yZXRlc3QoZ2xtLmZpdC5udWxsLCB4MSkNCmBgYA0KDQojIyMgNC4xMCBLaG/huqNuZyB0aW4gY+G6rXkNCg0KYGBge3J9DQpjb25maW50KGdsbS5vdXQpDQpgYGANCg0KIyMjIDQuMTEgVsO5bmcgdGluIGPhuq15IG5oaeG7gXUvdGhhbSBz4buRIMSRxqFuDQoNCmBgYG1hdGgNCihcaGF0e1x6ZXRhfSAtIFx6ZXRhKV5UIEkoXGhhdHtcemV0YX0pKFxoYXR7XHpldGF9IC0gXHpldGEpIFxsZSBcY2hpXjJfe3EsIDEgLSBcYWxwaGF9DQpgYGANCg0KIyMjIDQuMTIgU28gc8OhbmggbcO0IGjDrG5oIGtow7RuZyBs4buTbmcgbmhhdQ0KDQpgYGB7cn0NCkFJQyhnbG0ub3V0KQ0KQklDKGdsbS5vdXQpDQpgYGANCg0KIyMjIDQuMTMgVMOzbSB04bqvdCBjaMawxqFuZw0KDQpHTE0gZMO5bmcgTUxFIMSR4buDIHN1eSBsdeG6rW47IGTDuW5nIFdhbGQsIExSVCwgc2NvcmUgxJHhu4Mga2nhu4NtIMSR4buLbmg7IHbDoCBBSUMvQklDIMSR4buDIHNvIHPDoW5oIG3DtCBow6xuaC4NCg0KIyMgQ0jGr8agTkcgNTogTcOUIEjDjE5IIFBPSVNPTg0KDQojIyMgNS4xIEdp4bubaSB0aGnhu4d1DQpNw7QgaMOsbmggUG9pc3NvbiBsw6AgbeG7mXQgZOG6oW5nIGPhu6UgdGjhu4MgY+G7p2EgbcO0IGjDrG5oIHR1eeG6v24gdMOtbmggdOG7lW5nIHF1w6F0IChHTE0pIGTDuW5nIGNobyBk4buvIGxp4buHdSDEkeG6v20uIEJp4bq/biBwaOG6o24gaOG7k2kgbMOgIHPhu5EgbOG6p24geOG6o3kgcmEgY+G7p2Egc+G7sSBraeG7h24gdHJvbmcgbeG7mXQgxJHGoW4gduG7iyB0aOG7nWkgZ2lhbiBob+G6t2Mga2jDtG5nIGdpYW4uDQoNCiMjIyA1LjIgUGjDom4gcGjhu5FpIFBvaXNzb24NCkdp4bqjIMSR4buLbmggcuG6sW5nOg0KJCQNCllfaSBcc2ltIFx0ZXh0e1BvaXNzb259KFxtdV9pKSwgXHF1YWQgXG11X2kgPSBFKFlfaSkNCiQkDQpQaMOibiBwaOG7kWkgUG9pc3NvbiBjw7Mga+G7syB24buNbmcgdsOgIHBoxrDGoW5nIHNhaSBi4bqxbmcgbmhhdSwgdOG7qWMgbMOgIFwoVmFyKFlfaSkgPSBcbXVfaVwpLg0KDQpIw6BtIHjDoWMgc3XhuqV0Og0KJCQNClAoWV9pID0geV9pKSA9IFxmcmFje2Veey1cbXVfaX0gXG11X2lee3lfaX19e3lfaSF9DQokJA0KVHJvbmcgxJHDszoNCi0gXCggeV9pIFwpOiBz4buRIGzGsOG7o25nIHPhu7Ega2nhu4duIHF1YW4gc8OhdCDEkcaw4bujYw0KLSBcKCBcbXVfaSBcKTogZ2nDoSB0cuG7iyBr4buzIHbhu41uZyBj4bunYSBz4buRIHPhu7Ega2nhu4duDQoNCiMjIyA1LjMgSMOgbSBsacOqbiBr4bq/dA0KTcO0IGjDrG5oIFBvaXNzb24gc+G7rSBk4bulbmcgaMOgbSBsacOqbiBr4bq/dCBsb2c6DQokJA0KXGxvZyhcbXVfaSkgPSBcZXRhX2kgPSB4X2leVCBcYmV0YQ0KJCQNClThu6ljIGzDoDoNCiQkDQpcbXVfaSA9IFxleHAoeF9pXlQgXGJldGEpDQokJA0KDQpHaeG6o2kgdGjDrWNoOg0KLSBcKCB4X2kgXCk6IHZlY3RvciBjw6FjIGJp4bq/biBnaeG6o2kgdGjDrWNoIGNobyBxdWFuIHPDoXQgdGjhu6kgaQ0KLSBcKCBcYmV0YSBcKTogdmVjdG9yIGjhu4cgc+G7kSBo4buTaSBxdXkNCi0gXCggXGV0YV9pIFwpOiBwcmVkaWN0b3IgdHV54bq/biB0w61uaA0KDQojIyMgNS40IMav4bubYyBsxrDhu6NuZyB2w6Agc3V5IGx14bqtbg0KQ8OhYyBo4buHIHPhu5EgxJHGsOG7o2MgxrDhu5tjIGzGsOG7o25nIGLhurFuZyBwaMawxqFuZyBwaMOhcCB04buRaSDEkWEgaMOzYSBow6BtIGjhu6NwIGzDvSAobWF4aW11bSBsaWtlbGlob29kKS4gTcO0IGjDrG5oIMSRxrDhu6NjIGtp4buDbSDEkeG7i25oIHF1YSB6LXRlc3QgaG/hurdjIGtp4buDbSDEkeG7i25oIGRldmlhbmNlLg0KDQojIyMgNS41IMav4bubYyBsxrDhu6NuZyBi4bqxbmcgUg0KVsOtIGThu6UgduG7m2kgZOG7ryBsaeG7h3UgxJHhur9tOg0KYGBge3J9DQpkYXRhKEFpclBhc3NlbmdlcnMpDQpjb3VudHMgPC0gYXMubnVtZXJpYyhBaXJQYXNzZW5nZXJzKQ0KdGltZSA8LSB0aW1lKEFpclBhc3NlbmdlcnMpDQptb250aCA8LSBjeWNsZShBaXJQYXNzZW5nZXJzKQ0KDQpwb2lzc29uX21vZGVsIDwtIGdsbShjb3VudHMgfiBtb250aCwgZmFtaWx5ID0gcG9pc3NvbihsaW5rID0gImxvZyIpKQ0Kc3VtbWFyeShwb2lzc29uX21vZGVsKQ0KYGBgDQoNCiMjIyA1LjYgRGnhu4VuIGdp4bqjaSBo4buHIHPhu5EgaOG7k2kgcXV5DQpDw6FjIGjhu4cgc+G7kSBo4buTaSBxdXkgxJHGsOG7o2MgZGnhu4VuIGdp4bqjaSB0aGVvIGxvZyB04buJIGzhu4cgeOG6o3kgcmEgKGxvZy1yYXRlKS4gVsOtIGThu6U6DQotIE7hur91IFwoIFxiZXRhX2ogPSAwLjMgXCksIHRow6wgYmnhur9uIGxpw6puIHF1YW4gbMOgbSB0xINuZyB04buJIGzhu4cgeOG6o3kgcmEgdHJ1bmcgYsOsbmgga2hv4bqjbmcgXCggXGV4cCgwLjMpIFxhcHByb3ggMS4zNSBcKSBs4bqnbi4NCg0KIyMjIDUuNyDEkOG7mSBwaMOibiB0w6FuDQpO4bq/dSBwaMawxqFuZyBzYWkgbOG7m24gaMahbiB0cnVuZyBiw6xuaCwgdGEgZ+G6t3AgaGnhu4duIHTGsOG7o25nIHBow6JuIHTDoW4gdsaw4bujdCBt4bupYyAob3ZlcmRpc3BlcnNpb24pLiBLaGkgxJHDsywgbcO0IGjDrG5oIFBvaXNzb24gY8OzIHRo4buDIGtow7RuZyBwaMO5IGjhu6NwIHbDrCBnaeG6oyDEkeG7i25oIFZhciA9IE1lYW4gYuG7iyB2aSBwaOG6oW0uDQoNCiMjIyA1LjggS2nhu4NtIHRyYSBwaMOibiB0w6FuIHbDoCDEkWnhu4F1IGNo4buJbmgNCkTDuW5nIHRo4buRbmcga8OqIFBlYXJzb24gaG/hurdjIGRldmlhbmNlIMSR4buDIGtp4buDbSB0cmEgb3ZlcmRpc3BlcnNpb24uIEPDsyB0aOG7gyBkw7luZyBtw7QgaMOsbmggKipxdWFzaS1Qb2lzc29uKiogaG/hurdjICoqbmVnYXRpdmUgYmlub21pYWwqKiDEkeG7gyDEkWnhu4F1IGNo4buJbmguDQpgYGB7cn0NCnBvaXNzb25fcXVhc2kgPC0gZ2xtKGNvdW50cyB+IG1vbnRoLCBmYW1pbHkgPSBxdWFzaXBvaXNzb24obGluayA9ICJsb2ciKSkNCnN1bW1hcnkocG9pc3Nvbl9xdWFzaSkNCmBgYA0KDQojIyMgNS45IEThu7EgxJFvw6FuDQpT4butIGThu6VuZyBgcHJlZGljdCgpYCDEkeG7gyBk4buxIMSRb8OhbiBz4buRIGzGsOG7o25nIHPhu7Ega2nhu4duOg0KYGBge3J9DQpwcmVkaWN0KHBvaXNzb25fbW9kZWwsIG5ld2RhdGEgPSBkYXRhLmZyYW1lKG1vbnRoID0gNiksIHR5cGUgPSAicmVzcG9uc2UiKQ0KYGBgDQoNCiMjIyA1LjEwIFTDs20gdOG6r3QNCi0gTcO0IGjDrG5oIFBvaXNzb24gZMO5bmcgxJHhu4MgcGjDom4gdMOtY2ggZOG7ryBsaeG7h3UgxJHhur9tDQotIEjDoG0gbGnDqm4ga+G6v3QgbG9nIGdpw7pwIGNodXnhu4NuIMSR4buVaSB24buBIHR1eeG6v24gdMOtbmgNCi0gQ+G6p24ga2nhu4NtIHRyYSBoaeG7h24gdMaw4bujbmcgcGjDom4gdMOhbiDEkeG7gyBjaOG7jW4gbcO0IGjDrG5oIHBow7kgaOG7o3AgaMahbiBu4bq/dSBj4bqnbg0KDQojIyBDSMavxqBORyA2Lk3DlCBIw4xOSCBOSOG7iiBUSOG7qEMgw4JNDQoNCiMjIyA2LjEgR2nhu5tpIHRoaeG7h3UNCk3DtCBow6xuaCBuaOG7iyB0aOG7qWMgw6JtIChOZWdhdGl2ZSBCaW5vbWlhbCAtIE5CKSBsw6AgbeG7mXQgbeG7nyBy4buZbmcgY+G7p2EgbcO0IGjDrG5oIFBvaXNzb24gxJHhu4MgeOG7rSBsw70gaGnhu4duIHTGsOG7o25nIHBow6JuIHTDoW4gdsaw4bujdCBt4bupYyAob3ZlcmRpc3BlcnNpb24pLCB04bupYyBsw6Aga2hpIHBoxrDGoW5nIHNhaSBs4bubbiBoxqFuIGvhu7MgduG7jW5nLg0KDQojIyMgNi4yIFBow6JuIHBo4buRaSBuaOG7iyB0aOG7qWMgw6JtDQpHaeG6oyDEkeG7i25oOg0KJCQNCllfaSBcc2ltIE5CKFxtdV9pLCBcdGhldGEpDQokJA0KVHJvbmcgxJHDszoNCi0gXChcbXVfaVwpOiBr4buzIHbhu41uZyBj4bunYSBcKFlfaVwpDQotIFwoXHRoZXRhXCk6IHRoYW0gc+G7kSBwaMOibiB0w6FuIChkaXNwZXJzaW9uKQ0KDQpQaMawxqFuZyBzYWk6DQokJA0KVmFyKFlfaSkgPSBcbXVfaSArIFxmcmFje1xtdV9pXjJ9e1x0aGV0YX0NCiQkDQpQaMOibiBwaOG7kWkgTkIgY2hvIHBow6lwIHBoxrDGoW5nIHNhaSBs4bubbiBoxqFuIGvhu7MgduG7jW5nIHbDoCBsaW5oIGhv4bqhdCBoxqFuIHNvIHbhu5tpIFBvaXNzb24uDQoNCiMjIyA2LjMgSMOgbSBsacOqbiBr4bq/dA0KSMOgbSBsacOqbiBr4bq/dCB0aMaw4budbmcgZMO5bmcgbMOgIGxvZzoNCiQkDQpcbG9nKFxtdV9pKSA9IHhfaV5UIFxiZXRhDQokJA0KVMawxqFuZyB04buxIG3DtCBow6xuaCBQb2lzc29uLCBtw7QgaMOsbmggTkIgdGh14buZYyBo4buNIEdMTSB24bubaSBow6BtIGxpw6puIGvhur90IGxvZy4NCg0KIyMjIDYuNCDGr+G7m2MgbMaw4bujbmcgdsOgIHN1eSBsdeG6rW4NCsav4bubYyBsxrDhu6NuZyBjw6FjIGjhu4cgc+G7kSBcKFxiZXRhXCkgdsOgIHRoYW0gc+G7kSBwaMOibiB0w6FuIFwoXHRoZXRhXCkgYuG6sW5nIHBoxrDGoW5nIHBow6FwIHThu5FpIMSRYSBow7NhIGjDoG0gaOG7o3AgbMO9LiBWaeG7h2MgxrDhu5tjIGzGsOG7o25nIFwoXHRoZXRhXCkgdGjGsOG7nW5nIGTDuW5nIHRodeG6rXQgdG/DoW4gbOG6t3AgaG/hurdjIGfDs2kgY2h1ecOqbiBiaeG7h3QuDQoNCiMjIyA2LjUgTcO0IGjDrG5oIE5CIHRyb25nIFINCkTDuW5nIGfDs2kgYE1BU1NgIHbDoCBow6BtIGBnbG0ubmIoKWA6DQpgYGB7cn0NCmxpYnJhcnkoTUFTUykNCmRhdGEoQ2FyczkzLCBwYWNrYWdlID0gIk1BU1MiKQ0KbmJfbW9kZWwgPC0gZ2xtLm5iKFByaWNlIH4gSG9yc2Vwb3dlciArIFdlaWdodCwgZGF0YSA9IENhcnM5MykNCnN1bW1hcnkobmJfbW9kZWwpDQpgYGANCg0KIyMjIDYuNiBTbyBzw6FuaCB24bubaSBtw7QgaMOsbmggUG9pc3Nvbg0KS2hpIGPDsyBvdmVyZGlzcGVyc2lvbiwgbcO0IGjDrG5oIE5CIHRoxrDhu51uZyBjaG8ga+G6v3QgcXXhuqMgcGjDuSBo4bujcCBoxqFuIHbDoCBzYWkgc+G7kSBjaHXhuqluIGNow61uaCB4w6FjIGjGoW4uDQpgYGB7cn0NCnBvaXNzb25fbW9kZWwgPC0gZ2xtKFByaWNlIH4gSG9yc2Vwb3dlciArIFdlaWdodCwgZGF0YSA9IENhcnM5MywgZmFtaWx5ID0gcG9pc3NvbigpKQ0KZGlzcGVyc2lvbiA8LSBzdW0ocmVzaWR1YWxzKHBvaXNzb25fbW9kZWwsIHR5cGUgPSAicGVhcnNvbiIpXjIpIC8gZGYucmVzaWR1YWwocG9pc3Nvbl9tb2RlbCkNCmRpc3BlcnNpb24gICMgTuG6v3UgPiAxIMSRw6FuZyBr4buDIOKGkiBuw6puIGTDuW5nIE5CDQpgYGANCg0KIyMjIDYuNyBE4buxIMSRb8Ohbg0KYGBge3J9DQpwcmVkaWN0KG5iX21vZGVsLCBuZXdkYXRhID0gZGF0YS5mcmFtZShIb3JzZXBvd2VyID0gMTUwLCBXZWlnaHQgPSAzMDAwKSwgdHlwZSA9ICJyZXNwb25zZSIpDQpgYGANCkThu7EgxJFvw6FuIHRy4bqjIHbhu4Ega+G7syB24buNbmcgc+G7kSBz4buxIGtp4buHbiB44bqjeSByYSAoZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBj4bunYSBcKFlcKSkuDQoNCiMjIyA2LjggVMOzbSB04bqvdA0KLSBNw7QgaMOsbmggbmjhu4sgdGjhu6ljIMOibSBt4bufIHLhu5luZyBQb2lzc29uIMSR4buDIHjhu60gbMO9IHBow6JuIHTDoW4gdsaw4bujdCBt4bupYw0KLSBQaMawxqFuZyBzYWkgcGjhu6UgdGh14buZYyB2w6BvIGPhuqMgXChcbXVcKSB2w6AgdGhhbSBz4buRIFwoXHRoZXRhXCkNCi0gUiBo4buXIHRy4bujIG3DtCBow6xuaCBuw6B5IHRow7RuZyBxdWEgaMOgbSBgZ2xtLm5iKClgIHThu6sgZ8OzaSBNQVNTDQoNCiMjIENIxq/GoE5HIDc6IENI4bqoTiDEkE/DgU4gVsOAIEtJ4buCTSBUUkEgTcOUIEjDjE5IDQoNCiMjIyA3LjEgS2nhu4NtIHRyYSBwaOG6p24gZMawIChSZXNpZHVhbHMpDQoNClBo4bqnbiBkxrAgZ2nDunAgxJHDoW5oIGdpw6Egc+G7sSBwaMO5IGjhu6NwIGPhu6dhIG3DtCBow6xuaC4gVHJvbmcgR0xNLCBwaOG6p24gZMawIFBlYXJzb24gdsOgIHBo4bqnbiBkxrAgZGV2aWFuY2UgxJHGsOG7o2Mgc+G7rSBk4bulbmcgcGjhu5UgYmnhur9uLg0KDQotICoqUmVzaWR1YWwgZGV2aWFuY2UqKjogc28gc8OhbmggbG9nLWxpa2VsaWhvb2QgZ2nhu69hIG3DtCBow6xuaCDEkcOjIGZpdCB2w6AgbcO0IGjDrG5oIGhvw6BuIGjhuqNvLg0KDQpgYGB7cn0NCm1vZGVsIDwtIGdsbSh5X3BvaXMgfiBBZ2UgKyBIdCwgZmFtaWx5ID0gcG9pc3NvbiwgZGF0YSA9IGx1bmdjYXApDQpyZXNpZHVhbHMobW9kZWwsIHR5cGUgPSAiZGV2aWFuY2UiKVsxOjVdICAjIHbDrSBk4bulIG3hu5l0IHPhu5EgcGjhuqduIGTGsA0KYGBgDQoNCiMjIyA3LjIgQmnhu4N1IMSR4buTIGNodeG6qW4gxJFvw6FuDQoNCkPDoWMgYmnhu4N1IMSR4buTIHRoxrDhu51uZyBkw7luZzoNCi0gUmVzaWR1YWxzIHZzIEZpdHRlZA0KLSBOb3JtYWwgUVEgcGxvdA0KLSBDb29rJ3MgZGlzdGFuY2UNCg0KYGBge3J9DQpwbG90KG1vZGVsKSAgIyB04bqhbyA0IGJp4buDdSDEkeG7kyBjaOG6qW4gxJFvw6FuIGPGoSBi4bqjbg0KYGBgDQoNCiMjIyA3LjMgTGV2ZXJhZ2UgdsOgIOG6o25oIGjGsOG7n25nDQoNCkxldmVyYWdlIMSRbyBsxrDhu51uZyDhuqNuaCBoxrDhu59uZyBj4bunYSDEkWnhu4NtIGThu68gbGnhu4d1IMSR4bq/biBtw7QgaMOsbmguIMSQaeG7g20gY8OzIGxldmVyYWdlIGNhbyB2w6AgcGjhuqduIGTGsCBs4bubbiBjw7MgdGjhu4MgbMOgIG91dGxpZXIg4bqjbmggaMaw4bufbmcuDQoNCmBgYHtyfQ0KaGF0dmFsdWVzKG1vZGVsKVsxOjVdICAjIGPDoWMgZ2nDoSB0cuG7iyBsZXZlcmFnZSDEkeG6p3UgdGnDqm4NCmBgYA0KDQojIyMgNy40IENvb2sncyBkaXN0YW5jZQ0KDQrEkG8gbeG7qWMg4bqjbmggaMaw4bufbmcgY+G7p2EgdOG7q25nIHF1YW4gc8OhdCDEkeG6v24gdG/DoG4gYuG7mSBtw7QgaMOsbmguDQoNCmBgYHtyfQ0KY29va3MuZGlzdGFuY2UobW9kZWwpWzE6NV0gICMgQ29vaydzIGRpc3RhbmNlIMSR4bqndSB0acOqbg0KYGBgDQoNCiMjIyA3LjUgREZCRVRBcyB2w6AgREZGSVRTDQoNCkdpw7pwIMSRw6FuaCBnacOhIOG6o25oIGjGsOG7n25nIGPhu6dhIHThu6tuZyDEkWnhu4NtIMSR4bq/biB04burbmcgaOG7hyBz4buRIMaw4bubYyBsxrDhu6NuZy4NCg0KYGBge3J9DQpkZmJldGEobW9kZWwpWzE6NSwgXQ0KZGZmaXRzKG1vZGVsKVsxOjVdDQpgYGANCg0KIyMjIDcuNiBLaeG7g20gxJHhu4tuaCB2w6AgcGjDom4gdMOtY2ggbcO0IGjDrG5oIHRoYXkgdGjhur8NCg0KRMO5bmcgc28gc8OhbmggbcO0IGjDrG5oIChBTk9WQSkgaG/hurdjIGtp4buDbSDEkeG7i25oIHNjb3JlL2xpa2VsaWhvb2QgxJHhu4MgeGVtIGJp4bq/biBjw7MgbsOqbiDEkcawYSB2w6BvIGtow7RuZy4NCg0KYGBge3J9DQptb2RlbDAgPC0gZ2xtKHlfcG9pcyB+IDEsIGZhbWlseSA9IHBvaXNzb24sIGRhdGEgPSBsdW5nY2FwKQ0KbW9kZWwxIDwtIGdsbSh5X3BvaXMgfiBBZ2UsIGZhbWlseSA9IHBvaXNzb24sIGRhdGEgPSBsdW5nY2FwKQ0KYW5vdmEobW9kZWwwLCBtb2RlbDEsIHRlc3QgPSAiQ2hpc3EiKQ0KYGBgDQoNCiMjIENIxq/GoE5HIDg6IFBIw4JOIFTDgU4gUVXDgSBN4buoQyBWw4AgSMOATSBRVUFTSS1MSUtFTElIT09EDQoNCiMjIyA4LjEgSGnhu4duIHTGsOG7o25nIG92ZXJkaXNwZXJzaW9uDQoNCk92ZXJkaXNwZXJzaW9uIHjhuqN5IHJhIGtoaSBwaMawxqFuZyBzYWkgbOG7m24gaMahbiBr4buzIHbhu41uZyAoUG9pc3NvbiBnaeG6oyDEkeG7i25oIFZhciA9IE1lYW4pLiBE4buFIGfhurdwIHRyb25nIGThu68gbGnhu4d1IMSR4bq/bS4NCg0KS2nhu4NtIHRyYSBuaGFuaDoNCmBgYHtyfQ0KZGlzcGVyc2lvbiA8LSBzdW0ocmVzaWR1YWxzKG1vZGVsLCB0eXBlID0gInBlYXJzb24iKV4yKSAvIG1vZGVsJGRmLnJlc2lkdWFsDQpkaXNwZXJzaW9uICAjIE7hur91ID4gMSBuaGnhu4F1IHRow6wgY8OzIG92ZXJkaXNwZXJzaW9uDQpgYGANCg0KIyMjIDguMiBRdWFzaS1saWtlbGlob29kDQoNClPhu60gZOG7pW5nIGtoaSBraMO0bmcgbXXhu5FuIGdp4bqjIMSR4buLbmggcGjDom4gcGjhu5FpIHjDoWMgxJHhu4tuaCBuaMawbmcgduG6q24gbcO0IGjDrG5oIGjDs2Ega+G7syB24buNbmcgdsOgIHBoxrDGoW5nIHNhaS4NCg0KYGBge3J9DQptb2RlbF9xdWFzaSA8LSBnbG0oeV9wb2lzIH4gQWdlICsgSHQsIGZhbWlseSA9IHF1YXNpcG9pc3NvbiwgZGF0YSA9IGx1bmdjYXApDQpzdW1tYXJ5KG1vZGVsX3F1YXNpKQ0KYGBgDQoNCiMjIyA4LjMgTmVnYXRpdmUgYmlub21pYWwNCg0KVGhheSB2w6wgZMO5bmcgUG9pc3NvbiwgY8OzIHRo4buDIGTDuW5nIHBow6JuIHBo4buRaSBuZWdhdGl2ZSBiaW5vbWlhbCDEkeG7gyB44butIGzDvSBvdmVyZGlzcGVyc2lvbi4NCg0KYGBge3J9DQpsaWJyYXJ5KE1BU1MpDQptb2RlbF9uYiA8LSBnbG0ubmIoeV9wb2lzIH4gQWdlICsgSHQsIGRhdGEgPSBsdW5nY2FwKQ0Kc3VtbWFyeShtb2RlbF9uYikNCmBgYA0KDQojIyMgOC40IFNvIHPDoW5oIG3DtCBow6xuaA0KDQpTbyBzw6FuaCBQb2lzc29uLCBRdWFzaXBvaXNzb24sIHbDoCBOZWdhdGl2ZSBCaW5vbWlhbCDEkeG7gyBjaOG7jW4gbcO0IGjDrG5oIHThu5F0IG5o4bqldC4NCg0KYGBge3J9DQpBSUMobW9kZWwsIG1vZGVsX25iKSAgIyBBSUMgbmjhu48gaMahbiBsw6AgbcO0IGjDrG5oIHThu5F0IGjGoW4NCmBgYA0KDQojIyMgOC41IEzDvSBkbyBz4butIGThu6VuZyBxdWFzaS1saWtlbGlob29kDQoNCkdpw7pwIMaw4bubYyBsxrDhu6NuZyDEkeG7mSBs4buHY2ggY2h14bqpbiDEkcO6bmcgaMahbiBraGkga2jDtG5nIGJp4bq/dCByw7UgcGjDom4gcGjhu5FpLCDEkeG6t2MgYmnhu4d0IGjhu691IMOtY2ggdHJvbmcgZOG7ryBsaeG7h3Ugc2luaCBo4buNYywgeMOjIGjhu5lpLg0KDQo+ICoqR2hpIGNow7o6KiogQ8OhYyBtw7QgaMOsbmggY+G6p24ga2nhu4NtIHRyYSB0aMOqbSBnaeG6oyDEkeG7i25oIMSR4buDIMSR4bqjbSBi4bqjbyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ogdsOgIHPhu7EgcGjDuSBo4bujcC4NCg0KIyMgQ0jGr8agTkcgOS5Nw5QgSMOMTkggQ+G6pFUgVFLDmkMNCg0KIyMjIDkuMSBHaeG7m2kgdGhp4buHdQ0KTcO0IGjDrG5oIGPDsyBj4bqldSB0csO6YyBsw6AgY8OhYyBtw7QgaMOsbmggbeG7nyBy4buZbmcgY+G7p2EgR0xNIHRyb25nIMSRw7MgY8OzIG3hu5FpIGxpw6puIGjhu4cgbuG7mWkgdOG6oWkgZ2nhu69hIGPDoWMgdGhhbSBz4buRIGhv4bq3YyBk4buvIGxp4buHdS4gQ8OhYyBtw7QgaMOsbmggbsOgeSDEkcaw4bujYyBz4butIGThu6VuZyBraGkgZOG7ryBsaeG7h3UgY8OzIGPhuqV1IHRyw7pjIGtow7RuZyDEkeG7mWMgbOG6rXAgbmjGsDogbOG6t3AgbOG6oWkgdGhlbyB0aOG7nWkgZ2lhbiwgdGhlbyBj4bulbSwgaG/hurdjIHBow6JuIGPhuqVwLg0KDQojIyMgOS4yIEThu68gbGnhu4d1IHBow6JuIGPhuqVwIHbDoCBs4bq3cCBs4bqhaQ0KVHJvbmcgZOG7ryBsaeG7h3UgY8OzIGPhuqV1IHRyw7pjIHBow6JuIGPhuqVwIChoaWVyYXJjaGljYWwpIGhv4bq3YyBs4bq3cCBs4bqhaSAocmVwZWF0ZWQgbWVhc3VyZXMpLCBjw6FjIHF1YW4gc8OhdCBraMO0bmcgxJHhu5ljIGzhuq1wIGhvw6BuIHRvw6BuLiBWw60gZOG7pToNCi0gxJBvIGh1eeG6v3Qgw6FwIG5oaeG7gXUgbOG6p24gdHLDqm4gY8O5bmcgbeG7mXQgYuG7h25oIG5ow6JuDQotIFNpbmggdmnDqm4gdHJvbmcgY8O5bmcgbeG7mXQgbOG7m3AgaOG7jWMNCg0KIyMjIDkuMyBNw7QgaMOsbmggaOG7l24gaOG7o3AgdHV54bq/biB0w61uaCAoTGluZWFyIE1peGVkIE1vZGVscykNCk3DtCBow6xuaCBo4buXbiBo4bujcCB0dXnhur9uIHTDrW5oIHRow6ptIGPDoWMgaGnhu4d1IOG7qW5nIG5n4bqrdSBuaGnDqm4gKHJhbmRvbSBlZmZlY3RzKSB2w6BvIG3DtCBow6xuaCB0dXnhur9uIHTDrW5oOg0KDQokJA0KWV97aWp9ID0gXGJldGFfMCArIFxiZXRhXzEgeF97aWp9ICsgYl9qICsgXHZhcmVwc2lsb25fe2lqfQ0KJCQNClRyb25nIMSRw7M6DQotIFwoYl9qIFxzaW0gTigwLCBcc2lnbWFfYl4yKVwpOiBoaeG7h3Ug4bupbmcgbmfhuqt1IG5oacOqbiBj4bunYSBuaMOzbSBqDQotIFwoXHZhcmVwc2lsb25fe2lqfSBcc2ltIE4oMCwgXHNpZ21hXjIpXCk6IHNhaSBz4buRIG5n4bqrdSBuaGnDqm4NCg0KIyMjIDkuNCDGr+G7m2MgbMaw4bujbmcgdHJvbmcgbcO0IGjDrG5oIGjhu5duIGjhu6NwDQpDw6FjIHRoYW0gc+G7kSDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcgYuG6sW5nIHBoxrDGoW5nIHBow6FwIFJFTUwgKFJlc3RyaWN0ZWQgTWF4aW11bSBMaWtlbGlob29kKSBob+G6t2MgTUwgKE1heGltdW0gTGlrZWxpaG9vZCkuIA0KDQpUcm9uZyBSLCBkw7luZyBow6BtIGBsbWVyKClgIHThu6sgZ8OzaSBgbG1lNGA6DQpgYGB7cn0NCmxpYnJhcnkobG1lNCkNCm1vZGVsX2xtbSA8LSBsbWVyKFJlYWN0aW9uIH4gRGF5cyArICgxIHwgU3ViamVjdCksIGRhdGEgPSBzbGVlcHN0dWR5KQ0Kc3VtbWFyeShtb2RlbF9sbW0pDQpgYGANCg0KIyMjIDkuNSBNw7QgaMOsbmggdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgaOG7l24gaOG7o3AgKEdMTU0pDQpHTE1NIGvhur90IGjhu6NwIGdp4buvYSBHTE0gdsOgIGhp4buHdSDhu6luZyBuZ+G6q3Ugbmhpw6puLCB0aMOtY2ggaOG7o3AgY2hvIGThu68gbGnhu4d1IMSR4bq/bSBob+G6t2Mgbmjhu4sgcGjDom4gY8OzIGPhuqV1IHRyw7pjOg0KDQpWw60gZOG7pToNCmBgYHtyfQ0KZGF0YShjYnBwLCBwYWNrYWdlID0gImxtZTQiKQ0KZ2xtbV9tb2RlbCA8LSBnbG1lcihjYmluZChpbmNpZGVuY2UsIHNpemUgLSBpbmNpZGVuY2UpIH4gcGVyaW9kICsgKDEgfCBoZXJkKSwgDQogICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gY2JwcCkNCnN1bW1hcnkoZ2xtbV9tb2RlbCkNCmBgYA0KDQojIyMgOS42IERp4buFbiBnaeG6o2kga+G6v3QgcXXhuqMNCkjhu4cgc+G7kSBj4buRIMSR4buLbmggKGZpeGVkIGVmZmVjdHMpIMSRxrDhu6NjIGRp4buFbiBnaeG6o2kgbmjGsCBHTE0uIEPDoWMgaGnhu4d1IOG7qW5nIG5n4bqrdSBuaGnDqm4gcGjhuqNuIMOhbmggc+G7sSBiaeG6v24gdGhpw6puIGdp4buvYSBjw6FjIG5ow7NtIGhv4bq3YyDEkcahbiB24buLLg0KDQojIyMgOS43IFNvIHPDoW5oIG3DtCBow6xuaCBjw7MgY+G6pXUgdHLDumMgdsOgIEdMTQ0KR0xNTSB2w6AgTE1NIHBow7kgaOG7o3AgaMahbiBHTE0ga2hpIGThu68gbGnhu4d1IGPDsyBj4bqldSB0csO6YyBuaMOzbSwgdGjhu51pIGdpYW4sIGhv4bq3YyDEkW8gbOG6t3AuIETDuW5nIEFJQyBob+G6t2Mga2nhu4NtIMSR4buLbmggbGlrZWxpaG9vZCByYXRpbyDEkeG7gyBzbyBzw6FuaCBtw7QgaMOsbmguDQoNCiMjIyA5LjggVMOzbSB04bqvdA0KLSBNw7QgaMOsbmggY8OzIGPhuqV1IHRyw7pjIHjhu60gbMO9IGThu68gbGnhu4d1IHBo4bulIHRodeG7mWMgaG/hurdjIHBow6JuIG5ow7NtDQotIEJhbyBn4buTbSBMTU0gKGThu68gbGnhu4d1IGxpw6puIHThu6VjKSB2w6AgR0xNTSAoZOG7ryBsaeG7h3UgxJHhur9tLCBuaOG7iyBwaMOibikNCi0gUiBo4buXIHRy4bujIMaw4bubYyBsxrDhu6NuZyBxdWEgYGxtZTQ6OmxtZXIoKWAgdsOgIGBsbWU0OjpnbG1lcigpYA0KDQojIyBDSMavxqBORyAxMDogTcOUIEjDjE5IIENITyBE4buuIExJ4buGVSBOSOG7iiBQSMOCTg0KDQojIyMgMTAuMSBE4buvIGxp4buHdSBuaOG7iyBwaMOibiB2w6AgaOG7k2kgcXV5IGxvZ2lzdGljDQoNCkThu68gbGnhu4d1IG5o4buLIHBow6JuIGfhu5NtIGhhaSBnacOhIHRy4buLICh0aMOgbmggY8O0bmcvdGjhuqV0IGLhuqFpLCAxLzApLiBI4buTaSBxdXkgbG9naXN0aWMgZMO5bmcgxJHhu4MgbcO0IGjDrG5oIHjDoWMgc3XhuqV0IHRow6BuaCBjw7RuZyB0aGVvIGPDoWMgYmnhur9uIGdp4bqjaSB0aMOtY2guDQoNCkjDoG0gbG9naXQ6DQpcWyBcdGV4dHtsb2dpdH0ocCkgPSBcbG9nXGxlZnQoXGZyYWN7cH17MSAtIHB9XHJpZ2h0KSBcXQ0KDQpgYGB7cn0NCm1vZGVsX2xvZ2l0IDwtIGdsbSh5X2JpbiB+IEFnZSArIEh0LCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IiksIGRhdGEgPSBsdW5nY2FwKQ0Kc3VtbWFyeShtb2RlbF9sb2dpdCkNCmBgYA0KDQojIyMgMTAuMiBIw6BtIGxpw6puIGvhur90IGtow6FjDQoNCk5nb8OgaSBsb2dpdCwgY8OybiBjw7MgcHJvYml0IHbDoCBjbG9nbG9nOg0KDQokJA0KbW9kZWxfcHJvYml0IDwtIGdsbSh5X2JpbiB+IEFnZSArIEh0LCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gInByb2JpdCIpLCBkYXRhID0gbHVuZ2NhcClcXA0KDQptb2RlbF9jbG9nbG9nIDwtIGdsbSh5X2JpbiB+IEFnZSArIEh0LCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImNsb2dsb2ciKSwgZGF0YSA9IGx1bmdjYXApDQokJA0KDQojIyMgMTAuMyBEaeG7hW4gZ2nhuqNpIGjhu4cgc+G7kQ0KDQpI4buHIHPhu5EgaOG7k2kgcXV5IGxvZ2lzdGljIGJp4buDdSBkaeG7hW4gbG9nIG9kZHMgcmF0aW8uIMSQ4buDIGRp4buFbiBnaeG6o2kgZOG7hSBoxqFuLCB0aMaw4budbmcgY2h1eeG7g24gc2FuZyBvZGRzIHJhdGlvOg0KDQpgYGB7cn0NCmV4cChjb2VmKG1vZGVsX2xvZ2l0KSkgICMgb2RkcyByYXRpbw0KYGBgDQoNCiMjIyAxMC40IE3DtCBow6xuaCB24bubaSBiaeG6v24gcGjDom4gbG/huqFpDQoNCk7hur91IGPDsyBiaeG6v24gcGjDom4gbG/huqFpIG5oxrAgYEdlbmRlcmAgaG/hurdjIGBTbW9rZWAsIGPhuqduIMSR4bqjbSBi4bqjbyBuw7MgbMOgIGZhY3RvcjoNCg0KYGBge3J9DQpsdW5nY2FwJEdlbmRlciA8LSBhcy5mYWN0b3IobHVuZ2NhcCRHZW5kZXIpDQpsdW5nY2FwJFNtb2tlIDwtIGFzLmZhY3RvcihsdW5nY2FwJFNtb2tlKQ0KbW9kZWxfY2F0IDwtIGdsbSh5X2JpbiB+IEdlbmRlciArIFNtb2tlLCBmYW1pbHkgPSBiaW5vbWlhbCwgZGF0YSA9IGx1bmdjYXApDQpzdW1tYXJ5KG1vZGVsX2NhdCkNCmBgYA0KDQojIyMgMTAuNSBLaeG7g20gxJHhu4tuaCB2w6AgxJHhu5kgcGjDuSBo4bujcA0KDQpLaeG7g20gxJHhu4tuaCB04buVbmcgdGjhu4MgYuG6sW5nIGRldmlhbmNlOg0KDQpgYGB7cn0NCmRldiA8LSBkZXZpYW5jZShtb2RlbF9sb2dpdCkNCmRmIDwtIGRmLnJlc2lkdWFsKG1vZGVsX2xvZ2l0KQ0KcGNoaXNxKGRldiwgZGYsIGxvd2VyLnRhaWwgPSBGQUxTRSkgICMgcC12YWx1ZQ0KYGBgDQoNCk5nb8OgaSByYSBjw7MgdGjhu4MgZMO5bmcgSG9zbWVyLUxlbWVzaG93IHRlc3QgKHRyb25nIGfDs2kgUmVzb3VyY2VTZWxlY3Rpb24pLg0KDQojIyMgMTAuNiDEkMOhbmggZ2nDoSBtw7QgaMOsbmgNCg0KRMO5bmcgUk9DIGN1cnZlLCBBVUMsIGhv4bq3YyBi4bqjbmcgcGjDom4gbG/huqFpOg0KDQpgYGB7cn0NCnByZWQgPC0gcHJlZGljdChtb2RlbF9sb2dpdCwgdHlwZSA9ICJyZXNwb25zZSIpDQp0YWJsZShQcmVkaWN0ZWQgPSBwcmVkID4gMC41LCBBY3R1YWwgPSBsdW5nY2FwJHlfYmluKQ0KYGBgDQoNCj4gKipHaGkgY2jDujoqKiBNw7QgaMOsbmggbmjhu4sgcGjDom4gbMOgIG3hu5l0IHRyb25nIG5o4buvbmcg4bupbmcgZOG7pW5nIHBo4buVIGJp4bq/biBuaOG6pXQgY+G7p2EgR0xNIHRyb25nIGtob2EgaOG7jWMgeMOjIGjhu5lpLCB5IHThur8gdsOgIGtpbmggdOG6vyBsxrDhu6NuZy4NCg0KIyMgQ0jGr8agTkcgMTEuROG7riBMSeG7hlUgTEnDik4gVOG7pEMgRMavxqBORzogR0xNIFbhu5pJIFBIw4JOIFBI4buQSSBHQU1NQSBWw4AgSU5WRVJTRSBHQVVTSUFOIA0KDQojIyMgMTEuMSBHaeG7m2kgdGhp4buHdQ0KDQpE4buvIGxp4buHdSBsacOqbiB04bulYyBkxrDGoW5nIHBo4buVIGJp4bq/biB0cm9uZyBuaGnhu4F1IGzEqW5oIHbhu7FjIChzaW5oIGjhu41jLCBraW5oIHThur8sIGvhu7kgdGh14bqtdCkuIEhhaSBtw7QgaMOsbmggR0xNIHRoxrDhu51uZyBkw7luZyBsw6A6DQotICoqR2FtbWEgR0xNKio6IHBow7kgaOG7o3AgduG7m2kgZOG7ryBsaeG7h3UgY8OzIHBoxrDGoW5nIHNhaSB0xINuZyB0aGVvIGLDrG5oIHBoxrDGoW5nIHRydW5nIGLDrG5oLg0KLSAqKkludmVyc2UgR2F1c3NpYW4gR0xNKio6IHBow7kgaOG7o3Aga2hpIGThu68gbGnhu4d1IGzhu4djaCBwaOG6o2kgbmhp4buBdSwgcGjGsMahbmcgc2FpIHTEg25nIHRoZW8gbOG6rXAgcGjGsMahbmcgdHJ1bmcgYsOsbmguDQoNCiMjIDExLjIgTcO0IGjDrG5oIGjDs2EgZOG7ryBsaeG7h3UgbGnDqm4gdOG7pWMgZMawxqFuZw0KDQpE4buvIGxp4buHdSBuaMawIEZFViAoRm9yY2VkIEV4cGlyYXRvcnkgVm9sdW1lKSBsw6AgbGnDqm4gdOG7pWMgZMawxqFuZywgY+G6p24gbcO0IGjDrG5oIHBo4bqjbiDDoW5oIHTDrW5oIGNo4bqldCBwaMOibiBwaOG7kWkgdsOgIG3hu5FpIHF1YW4gaOG7hyBtZWFu4oCTdmFyaWFuY2UuDQoNCiMjIDExLjMgUGjDom4gcGjhu5FpIEdhbW1hDQoNCi0gSMOgbSBwaMawxqFuZyBzYWk6IGBWKM68KSA9IM68wrJgDQotIEThu68gbGnhu4d1IGPDsyBwaMOibiBwaOG7kWkgbOG7h2NoIG5o4bq5DQotIFRoxrDhu51uZyBkw7luZyBsaW5rIGAibG9nImANCg0KYGBge3J9DQpsaWJyYXJ5KEdMTXNEYXRhKQ0KZGF0YShsdW5nY2FwKQ0KZ2FtbWFfbW9kZWwgPC0gZ2xtKEZFViB+IEFnZSArIEh0ICsgR2VuZGVyICsgU21va2UsDQogICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gR2FtbWEobGluayA9ICJsb2ciKSwNCiAgICAgICAgICAgICAgICAgICBkYXRhID0gbHVuZ2NhcCkNCnN1bW1hcnkoZ2FtbWFfbW9kZWwpDQpgYGANCg0KIyMgMTEuNCBQaMOibiBwaOG7kWkgSW52ZXJzZSBHYXVzc2lhbg0KDQotIEjDoG0gcGjGsMahbmcgc2FpOiBgVijOvCkgPSDOvMKzYA0KLSBNw7QgaMOsbmggdOG7kXQga2hpIGThu68gbGnhu4d1IGzhu4djaCBt4bqhbmgNCi0gQ2Fub25pY2FsIGxpbms6IGAxL868wrJgLCBuaMawbmcgYCJsb2ciYCDEkcaw4bujYyBkw7luZyBwaOG7lSBiaeG6v24NCg0KYGBge3J9DQppbnZnYXVzc19tb2RlbCA8LSBnbG0oRkVWIH4gQWdlICsgSHQgKyBHZW5kZXIgKyBTbW9rZSwNCiAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBpbnZlcnNlLmdhdXNzaWFuKGxpbmsgPSAibG9nIiksDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGx1bmdjYXApDQpzdW1tYXJ5KGludmdhdXNzX21vZGVsKQ0KYGBgDQoNCiMjIDExLjUgTGluayBmdW5jdGlvbg0KDQotIGBsb2dgOiBwaOG7lSBiaeG6v24gdsOgIMSR4bqjbSBi4bqjbyDEkeG6p3UgcmEgZMawxqFuZw0KLSBgaW52ZXJzZWA6IGTDuW5nIGNobyBjw6FjIG3hu5FpIHF1YW4gaOG7hyBwaGkgdHV54bq/bg0KLSBgaWRlbnRpdHlgOiBjaOG7iSBkw7luZyBraGkgYmnhur9uIHBo4bqjbiBo4buTaSBraMO0bmcgYuG7iyBnaeG7m2kgaOG6oW4NCi0gYDEvzrzCsmA6IGxpbmsgY2h14bqpbiBjaG8gSW52ZXJzZSBHYXVzc2lhbg0KDQojIyAxMS42IMav4bubYyBsxrDhu6NuZyB0aGFtIHPhu5EgcGjDom4gdMOhbiDPhg0KDQpEw7luZyByZXNpZHVhbCBkZXZpYW5jZSBob+G6t2MgUGVhcnNvbiByZXNpZHVhbHMuDQoNCmBgYHtyfQ0KcGhpX2dhbW1hIDwtIHN1bShyZXNpZHVhbHMoZ2FtbWFfbW9kZWwsIHR5cGU9InBlYXJzb24iKV4yKSAvIGRmLnJlc2lkdWFsKGdhbW1hX21vZGVsKQ0KcGhpX2lnIDwtIHN1bShyZXNpZHVhbHMoaW52Z2F1c3NfbW9kZWwsIHR5cGU9InBlYXJzb24iKV4yKSAvIGRmLnJlc2lkdWFsKGludmdhdXNzX21vZGVsKQ0KcGhpX2dhbW1hOyBwaGlfaWcNCmBgYA0KDQojIyAxMS43IE5naGnDqm4gY+G7qXUgdMOsbmggaHXhu5FuZw0KDQpTw6FjaCB0csOsbmggYsOgeSAyIGNhc2Ugc3R1ZHksIGtow7RuZyBkw7luZyBgbHVuZ2NhcGAsIG5oxrBuZyBxdXkgdHLDrG5oIMOhcCBk4bulbmcgdMawxqFuZyB04buxOg0KLSBNw7QgaMOsbmggaMOzYQ0KLSBDaOG6qW4gxJFvw6FuDQotIFNvIHPDoW5oIGZpdHRlZCB2YWx1ZXMsIHJlc2lkdWFscw0KDQojIyAxMS44IETDuW5nIFIgxJHhu4MgZml0IG3DtCBow6xuaA0KDQotIGBnbG0oLi4uLCBmYW1pbHkgPSBHYW1tYShsaW5rID0gImxvZyIpKWANCi0gYGdsbSguLi4sIGZhbWlseSA9IGludmVyc2UuZ2F1c3NpYW4obGluayA9ICJsb2ciKSlgDQoNCiMjIDExLjkgVMOzbSB04bqvdA0KDQp8IE3DtCBow6xuaCAgICAgICAgICAgIHwgSMOgbSBwaMawxqFuZyBzYWkgfCBMaW5rIHBo4buVIGJp4bq/biB8IEThu68gbGnhu4d1IHBow7kgaOG7o3AgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IEdhbW1hICAgICAgICAgICAgICB8IFYozrwpID0gzrzCsiAgICAgIHwgbG9nICAgICAgICAgICAgfCBE4buvIGxp4buHdSBs4buHY2ggbmjhurkgICAgICAgICAgICAgfA0KfCBJbnZlcnNlIEdhdXNzaWFuICAgfCBWKM68KSA9IM68wrMgICAgICB8IGxvZywgMS/OvMKyICAgICAgfCBE4buvIGxp4buHdSBs4buHY2ggbeG6oW5oLCB0aOG7nWkgZ2lhbiB8DQoNCiMjICoqVOG6oW8gxJHhu5MgdGjhu4sgY2h14bqpbiDEkW/DoW4gZOG7sWEgdHLDqm4gZGF0YXNldCB0cm9uZyBzw6FjaCoqDQoNCiMjIyBO4bqhcCBk4buvIGxp4buHdQ0KDQpgYGB7cn0NCmxpYnJhcnkoR0xNc0RhdGEpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KGRwbHlyKQ0KZGF0YShsdW5nY2FwKQ0KaGVhZChsdW5nY2FwKQ0KYGBgDQoNCiMjIyBNw7QgaMOsbmggR2FtbWEgKGxvZyBsaW5rKQ0KDQpgYGB7cn0NCmdhbW1hX21vZGVsIDwtIGdsbShGRVYgfiBBZ2UgKyBIdCArIEdlbmRlciArIFNtb2tlLA0KICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IEdhbW1hKGxpbmsgPSAibG9nIiksDQogICAgICAgICAgICAgICAgICAgZGF0YSA9IGx1bmdjYXApDQpzdW1tYXJ5KGdhbW1hX21vZGVsKQ0KYGBgDQoNCiMjIyBNw7QgaMOsbmggSW52ZXJzZSBHYXVzc2lhbiAobG9nIGxpbmspDQoNCmBgYHtyfQ0KaW52Z2F1c3NfbW9kZWwgPC0gZ2xtKEZFViB+IEFnZSArIEh0ICsgR2VuZGVyICsgU21va2UsDQogICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gaW52ZXJzZS5nYXVzc2lhbihsaW5rID0gImxvZyIpLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBsdW5nY2FwKQ0Kc3VtbWFyeShpbnZnYXVzc19tb2RlbCkNCmBgYA0KDQojIyMgQ2jhuqluIMSRb8OhbiBtw7QgaMOsbmggR2FtbWENCg0KYGBge3J9DQpwYXIobWZyb3cgPSBjKDIsIDIpKQ0KcGxvdChnYW1tYV9tb2RlbCkNCmBgYA0KDQojIyMgQ2jhuqluIMSRb8OhbiBtw7QgaMOsbmggSW52ZXJzZSBHYXVzc2lhbg0KDQpgYGB7cn0NCnBhcihtZnJvdyA9IGMoMiwgMikpDQpwbG90KGludmdhdXNzX21vZGVsKQ0KYGBgDQoNCiMjIyBSZXNpZHVhbHMgdnMgRml0dGVkIGNobyBHYW1tYQ0KDQpgYGB7cn0NCmx1bmdjYXAkZ2FtbWFfcmVzaWQgPC0gcmVzaWR1YWxzKGdhbW1hX21vZGVsLCB0eXBlID0gInBlYXJzb24iKQ0KbHVuZ2NhcCRnYW1tYV9maXR0ZWQgPC0gZml0dGVkKGdhbW1hX21vZGVsKQ0KDQpnZ3Bsb3QobHVuZ2NhcCwgYWVzKHggPSBnYW1tYV9maXR0ZWQsIHkgPSBnYW1tYV9yZXNpZCkpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNikgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArDQogIGxhYnModGl0bGUgPSAiUmVzaWR1YWxzIHZzIEZpdHRlZCAoR2FtbWEpIiwgeCA9ICJGaXR0ZWQgdmFsdWVzIiwgeSA9ICJQZWFyc29uIFJlc2lkdWFscyIpDQpgYGANCg0KIyMjIFJlc2lkdWFscyB2cyBGaXR0ZWQgY2hvIEludmVyc2UgR2F1c3NpYW4NCg0KYGBge3J9DQpsdW5nY2FwJGlnX3Jlc2lkIDwtIHJlc2lkdWFscyhpbnZnYXVzc19tb2RlbCwgdHlwZSA9ICJwZWFyc29uIikNCmx1bmdjYXAkaWdfZml0dGVkIDwtIGZpdHRlZChpbnZnYXVzc19tb2RlbCkNCg0KZ2dwbG90KGx1bmdjYXAsIGFlcyh4ID0gaWdfZml0dGVkLCB5ID0gaWdfcmVzaWQpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gIlJlc2lkdWFscyB2cyBGaXR0ZWQgKEludmVyc2UgR2F1c3NpYW4pIiwgeCA9ICJGaXR0ZWQgdmFsdWVzIiwgeSA9ICJQZWFyc29uIFJlc2lkdWFscyIpDQpgYGANCg0KIyMgQ0jGr8agTkcgMTI6IE3DlCBIw4xOSCBDw5MgSEnhu4ZVIOG7qE5HIE5H4bqqVSBOSEnDik4gKFJBTkRPTSBFRkZFQ1RTKQ0KDQojIyMgMTIuMSBNw7QgaMOsbmggaOG7l24gaOG7o3AgdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgKEdMTU0pDQoNCktoaSBk4buvIGxp4buHdSBjw7MgY+G6pXUgdHLDumMgbmjDs20gKHbDrSBk4bulOiBi4buHbmggbmjDom4gdHJvbmcgY8OhYyBi4buHbmggdmnhu4duKSwgY+G6p24gxJHGsGEgaGnhu4d1IOG7qW5nIG5n4bqrdSBuaGnDqm4gdsOgbyBtw7QgaMOsbmggxJHhu4MgcGjhuqNuIMOhbmggc+G7sSBwaOG7pSB0aHXhu5ljIG7hu5lpIHThuqFpLg0KDQpgYGB7cn0NCmxpYnJhcnkobG1lNCkNCmx1bmdjYXAkSHRfZ3JwIDwtIGN1dChsdW5nY2FwJEh0LCBicmVha3MgPSAzLCBsYWJlbHMgPSBjKCJMb3ciLCAiTWVkaXVtIiwgIkhpZ2giKSkNCmxpYnJhcnkoR0xNc0RhdGEpDQpsaWJyYXJ5KGxtZTQpDQoNCmRhdGEobHVuZ2NhcCkNCg0KIyBU4bqhbyBiaeG6v24gbmjhu4sgcGjDom4NCmx1bmdjYXAkeV9iaW4gPC0gaWZlbHNlKGx1bmdjYXAkRkVWID4gMi41LCAxLCAwKQ0KDQojIE3DtCBow6xuaCBHTE1NIHbhu5tpIHJhbmRvbSBpbnRlcmNlcHQgdGhlbyBjaGnhu4F1IGNhbyAoSHQpDQptb2RlbF9nbG1tIDwtIGdsbWVyKHlfYmluIH4gQWdlICsgKDEgfCBIdCksIGZhbWlseSA9IGJpbm9taWFsLCBkYXRhID0gbHVuZ2NhcCkNCnN1bW1hcnkobW9kZWxfZ2xtbSkNCmBgYA0KDQojIyMgMTIuMiBDw7ogcGjDoXAgbcO0IGjDrG5oIGjhu5duIGjhu6NwDQoNCi0gYCgxIHwgR3JvdXApYCBjaOG7iSByYSBpbnRlcmNlcHQgdGhheSDEkeG7lWkgdGhlbyBuaMOzbS4NCi0gYChBZ2UgfCBHcm91cClgIGNobyBwaMOpcCBzbG9wZSB2w6AgaW50ZXJjZXB0IHRoYXkgxJHhu5VpIHRoZW8gbmjDs20uDQoNCiMjIyAxMi4zIFNvIHPDoW5oIEdMTSB2w6AgR0xNTQ0KDQpHTE1NIGNobyBwaMOpcCB44butIGzDvSBk4buvIGxp4buHdSBjw7MgcGjhu6UgdGh14buZYyBu4buZaSB04bqhaSAoY2x1c3RlcmVkL2NvcnJlbGF0ZWQpLiBHTE0gZ2nhuqMgxJHhu4tuaCDEkeG7mWMgbOG6rXAgZ2nhu69hIGPDoWMgcXVhbiBzw6F0LCBuw6puIGtow7RuZyBwaMO5IGjhu6NwIGtoaSBjw7MgY+G6pXUgdHLDumMgbmjDs20gcsO1IHLDoG5nLg0KDQojIyMgMTIuNCDGr+G7m2MgbMaw4bujbmcgdsOgIGjhu5lpIHThu6UNCg0KR0xNTSB0aMaw4budbmcgZMO5bmcgcGjGsMahbmcgcGjDoXAgdOG7kWkgxJFhIGjDs2EgaOG7o3AgbMO9IHjhuqVwIHjhu4kgbmjGsCBMYXBsYWNlIGhv4bq3YyBxdWFkcmF0dXJlLiBWaeG7h2MgaOG7mWkgdOG7pSBjw7MgdGjhu4MgY2jhuq1tIGhv4bq3YyB0aOG6pXQgYuG6oWkgbuG6v3UgbcO0IGjDrG5oIHF1w6EgcGjhu6ljIHThuqFwLg0KDQpgYGB7cn0NCm1vZGVsX2dsbW0yIDwtIGdsbWVyKHlfYmluIH4gQWdlICsgKEFnZSB8IEh0KSwgZmFtaWx5ID0gYmlub21pYWwsIGRhdGEgPSBsdW5nY2FwKQ0KYGBgDQoNCg0KIyMgS+G6vlQgTFXhuqxOIFThu5RORyBRVUFOIFbhu4AgUVVZ4buCTiBTw4FDSA0KDQpRdXnhu4NuICoiR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVscyB3aXRoIEV4YW1wbGVzIGluIFIiKiBtYW5nIGzhuqFpIG3hu5l0IGPDoWkgbmjDrG4gdG/DoG4gZGnhu4duIHbDoCB0aOG7sWMgaMOgbmggduG7gSBtw7QgaMOsbmggdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgKEdMTSkuIEPDoWMgbuG7mWkgZHVuZyBjaMOtbmggYmFvIGfhu5NtOg0KDQotIEdp4bubaSB0aGnhu4d1IGzDvSB0aHV54bq/dCBHTE06IGxpw6puIGvhur90LCBwaMOibiBwaOG7kWkgdGh14buZYyBo4buNIGjDoG0gbcWpLCBow6BtIHR1eeG6v24gdMOtbmguDQotIEPDoWMgbcO0IGjDrG5oIHF1YW4gdHLhu41uZzogaOG7k2kgcXV5IGxvZ2lzdGljLCBo4buTaSBxdXkgUG9pc3NvbiwgcXVhc2ktbGlrZWxpaG9vZCB2w6AgbmVnYXRpdmUgYmlub21pYWwuDQotIEtp4buDbSB0cmEgxJHhu5kgcGjDuSBo4bujcCBtw7QgaMOsbmggdsOgIGPDoWMgY8O0bmcgY+G7pSBjaOG6qW4gxJFvw6FuIHBo4bqnbiBkxrAsIOG6o25oIGjGsOG7n25nLg0KLSBN4bufIHLhu5luZyBHTE0gduG7m2kgbcO0IGjDrG5oIGPDsyBoaeG7h3Ug4bupbmcgbmfhuqt1IG5oacOqbiAoR0xNTSkuDQoNCsSQaeG7g20gbuG7lWkgYuG6rXQgbMOgIGPDoWNoIHRyw6xuaCBiw6B5IHRo4buxYyB04bq/LCB0w61jaCBo4bujcCB2w60gZOG7pSBj4bulIHRo4buDIGLhurFuZyBuZ8O0biBuZ+G7ryBSLCBnacO6cCBuZ8aw4budaSBo4buNYyB24burYSBoaeG7g3UgbMO9IHRodXnhur90IHbhu6thIMOhcCBk4bulbmcgdGjhu7FjIGjDoG5oLiDEkMOieSBsw6AgdMOgaSBsaeG7h3UgcXVhbiB0cuG7jW5nIGNobyBuaOG7r25nIGFpIGzDoG0gdmnhu4djIHbhu5tpIGThu68gbGnhu4d1IMSR4buLbmggbMaw4bujbmcgdHJvbmcgY8OhYyBsxKluaCB24buxYyBzaW5oIGjhu41jLCB5IHThur8sIHjDoyBo4buZaSwgdsOgIGtpbmggdOG6vyBsxrDhu6NuZy4NCg0KPiAqKkdoaSBjaMO6IGN14buRaSBjw7luZzoqKiBN4bq3YyBkw7kgR0xNIGzDoCBt4buZdCBjw7RuZyBj4bulIG3huqFuaCBt4bq9LCBuZ8aw4budaSBkw7luZyBj4bqnbiBraeG7g20gdHJhIGPhuqluIHRo4bqtbiBjw6FjIGdp4bqjIMSR4buLbmgsIGPhuqV1IHRyw7pjIGThu68gbGnhu4d1IHbDoCBwaMO5IGjhu6NwIG3DtCBow6xuaCDEkeG7gyDEkcawYSByYSBzdXkgbHXhuq1uIGNow61uaCB4w6FjLg0KDQoNCiMgKipUQVNLIDIuVEjhu5BORyBLw4ogTcOUIFThuqIgIlNVUEVSTUFSS0VUIFRSQU5TQUNUSU9OUyIqKg0KDQojIyAxLiDEkOG7jWMgZOG7ryBsaeG7h3UNCg0KYGBge3J9DQpkYXRhIDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9BZG1pbi9Eb3dubG9hZHMvU3VwZXJtYXJrZXQgVHJhbnNhY3Rpb25zLmNzdiIpDQpzdHIoZGF0YSkNCmBgYA0KDQojIyAyLiBU4buVbmcgcXVhbiBk4buvIGxp4buHdQ0KDQpgYGB7cn0NCnN1bW1hcnkoZGF0YSkNCmBgYA0KDQojIyAzLiBUaOG7kW5nIGvDqiBtw7QgdOG6oyBjw6FjIGJp4bq/biDEkeG7i25oIGzGsOG7o25nDQoNCmBgYHtyfQ0KZGF0YSAlPiUgDQogIHNlbGVjdChBbm51YWxJbmNvbWUsIENoaWxkcmVuLCBVbml0c1NvbGQsIFJldmVudWUpICU+JQ0KICBzdW1tYXJ5KCkNCmBgYA0KDQojIyA0LiBUaOG7kW5nIGvDqiBtw7QgdOG6oyBjw6FjIGJp4bq/biDEkeG7i25oIHTDrW5oDQoNCmBgYHtyfQ0KY2F0ZWdvcmljYWxfdmFycyA8LSBjKCJHZW5kZXIiLCAiTWFyaXRhbFN0YXR1cyIsICJIb21lb3duZXIiLCAiQ2l0eSIsICJTdGF0ZW9yUHJvdmluY2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAiQ291bnRyeSIsICJQcm9kdWN0RmFtaWx5IiwgIlByb2R1Y3REZXBhcnRtZW50IiwgIlByb2R1Y3RDYXRlZ29yeSIpDQoNCmZvciAodmFyIGluIGNhdGVnb3JpY2FsX3ZhcnMpIHsNCiAgY2F0KCJcblxuIyMjIiwgdmFyLCAiXG4iKQ0KICBwcmludCh0YWJsZShkYXRhW1t2YXJdXSkpDQp9DQpgYGANCg0KIyMgNS4gUGjDom4gcGjhu5FpIGdp4bubaSB0w61uaCB2w6AgdMOsbmggdHLhuqFuZyBow7RuIG5ow6JuDQoNCmBgYHtyfQ0KdGFibGUoZGF0YSRHZW5kZXIpDQp0YWJsZShkYXRhJE1hcml0YWxTdGF0dXMpDQpgYGANCg0KIyMgNi4gU+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgdGhlbyB0aMOgbmggcGjhu5EgKFRvcCAxMCkNCg0KYGBge3J9DQpkYXRhICU+JQ0KICBjb3VudChDaXR5LCBzb3J0ID0gVFJVRSkgJT4lDQogIGhlYWQoMTApDQpgYGANCg0KIyMgNy4gVOG7lW5nIGRvYW5oIHRodSB0aGVvIGJhbmcgKFRvcCAxMCkNCg0KYGBge3J9DQpkYXRhICU+JQ0KICBncm91cF9ieShTdGF0ZW9yUHJvdmluY2UpICU+JQ0KICBzdW1tYXJpc2UoVG9uZ0RvYW5oVGh1ID0gc3VtKFJldmVudWUsIG5hLnJtID0gVFJVRSkpICU+JQ0KICBhcnJhbmdlKGRlc2MoVG9uZ0RvYW5oVGh1KSkgJT4lDQogIGhlYWQoMTApDQpgYGANCg0KIyMgOC4gRG9hbmggdGh1IHRoZW8gbmjDs20gc+G6o24gcGjhuqltDQoNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgZ3JvdXBfYnkoUHJvZHVjdEZhbWlseSkgJT4lDQogIHN1bW1hcmlzZShUb25nRG9hbmhUaHUgPSBzdW0oUmV2ZW51ZSwgbmEucm0gPSBUUlVFKSkgJT4lDQogIGFycmFuZ2UoZGVzYyhUb25nRG9hbmhUaHUpKQ0KYGBgDQoNCiMjIDkuIEJp4buDdSDEkeG7kyBkb2FuaCB0aHUgdGhlbyBwaMOybmcgYmFuIHPhuqNuIHBo4bqpbQ0KDQpgYGB7cn0NCmRhdGEgJT4lDQogIGdyb3VwX2J5KFByb2R1Y3REZXBhcnRtZW50KSAlPiUNCiAgc3VtbWFyaXNlKFJldmVudWUgPSBzdW0oUmV2ZW51ZSwgbmEucm0gPSBUUlVFKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoUHJvZHVjdERlcGFydG1lbnQsIFJldmVudWUpLCB5ID0gUmV2ZW51ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAic3RlZWxibHVlIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIkRvYW5oIHRodSB0aGVvIHBow7JuZyBiYW4gc+G6o24gcGjhuqltIiwgeCA9ICJQaMOybmcgYmFuIiwgeSA9ICJEb2FuaCB0aHUiKQ0KYGBgDQoNCiMjIDEwLiBL4bq/dCBsdeG6rW4NCg0KROG7ryBsaeG7h3UgZ2lhbyBk4buLY2ggc2nDqnUgdGjhu4sgY3VuZyBj4bqlcCB0aMO0bmcgdGluIMSRYSBk4bqhbmcgdOG7qyB0aMO0bmcgdGluIGtow6FjaCBow6BuZyDEkeG6v24gdGjDtG5nIHRpbiBz4bqjbiBwaOG6qW0gdsOgIGRvYW5oIHRodS4gUXVhIHBow6JuIHTDrWNoIG3DtCB04bqjOg0KDQotICoqQmnhur9uIMSR4buLbmggbMaw4bujbmcqKiBuaMawIHRodSBuaOG6rXAsIHPhu5EgbMaw4bujbmcgYsOhbiB2w6AgZG9hbmggdGh1IGPDsyDEkeG7mSBwaMOibiB0w6FuIGzhu5tuLCBjaG8gdGjhuqV5IHPhu7Ega2jDoWMgYmnhu4d0IMSRw6FuZyBr4buDIGdp4buvYSBjw6FjIGtow6FjaCBow6BuZyB2w6Agc+G6o24gcGjhuqltLg0KLSAqKkJp4bq/biDEkeG7i25oIHTDrW5oKiogbmjGsCBnaeG7m2kgdMOtbmgsIHTDrG5oIHRy4bqhbmcgaMO0biBuaMOibiB2w6AgbmjDs20gc+G6o24gcGjhuqltIGNobyB0aOG6pXkgc+G7sSDEkWEgZOG6oW5nIHRyb25nIMSR4bq3YyDEkWnhu4NtIGtow6FjaCBow6BuZyB2w6AgZGFuaCBt4bulYyBz4bqjbiBwaOG6qW0uDQotICoqRG9hbmggdGh1KiogdOG6rXAgdHJ1bmcgbmhp4buBdSDhu58gbeG7mXQgc+G7kSBiYW5nIHbDoCBuaMOzbSBz4bqjbiBwaOG6qW0gY+G7pSB0aOG7gywgZ+G7o2kgw70gdGnhu4FtIG7Eg25nIHRyb25nIHZp4buHYyBwaMOibiBraMO6YyB0aOG7iyB0csaw4budbmcgdsOgIHThu5FpIMawdSBow7NhIGRhbmggbeG7pWMgc+G6o24gcGjhuqltLg0KDQpQaMOibiB0w61jaCBtw7QgdOG6oyBuw6B5IGzDoCBixrDhu5tjIMSR4bqndSBxdWFuIHRy4buNbmcgZ2nDunAgaGnhu4N1IHLDtSBj4bqldSB0csO6YyBk4buvIGxp4buHdSwgbMOgbSBjxqEgc+G7nyBjaG8gY8OhYyBwaMOibiB0w61jaCBzw6J1IGjGoW4gbmjGsCBwaMOibiBj4bulbSBraMOhY2ggaMOgbmcsIGThu7EgxJFvw6FuIGRvYW5oIHRodSBob+G6t2MgcGjDom4gdMOtY2ggaMOgbmggdmkgbXVhIGjDoG5nLg0KDQoNCg0KDQoNCg0K