BÀI TẬP 1: Tóm tắt cuốn sách: 2019_Generalized Linear Models With Examples in R_9781441901170.pdf

1. Mô hình thống kê và trực quan hóa dữ liệu

Khái niệm Mô hình Thống kê

Mô hình thống kê là công cụ mô tả các đặc điểm có hệ thốngngẫu nhiên trong dữ liệu. Không như các mô hình vật lý, mô hình thống kê là một sự xấp xỉ – chúng ta không kỳ vọng mô hình là đúng hoàn toàn, nhưng mong rằng nó hữu ích trong việc hiểu và phân tích dữ liệu.

“All models are wrong, but some are useful.” – George Box

Mỗi mô hình thống kê gồm hai thành phần:

  • Thành phần hệ thống: mô tả quan hệ trung bình giữa biến phản hồi \(y\) và các biến giải thích \(x\).
  • Thành phần ngẫu nhiên: mô tả sự biến thiên xung quanh trung bình \(\mu = E[y]\).

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

Tùy theo mục tiêu phân tích, mô hình thống kê có thể phục vụ:

  • Dự đoán (Prediction): tối ưu hóa khả năng dự đoán, không nhất thiết yêu cầu tất cả hệ số phải có ý nghĩa thống kê.
  • Giải thích (Interpretation): hiểu và kiểm tra mối quan hệ nhân quả, yêu cầu các hệ số có ý nghĩa rõ ràng.

Đánh đổi giữa Chính xác và Tính tiết kiệm

Một mô hình quá đơn giản có thể không nắm bắt được cấu trúc thực của dữ liệu (underfitting), trong khi mô hình quá phức tạp có thể phản ánh cả nhiễu (overfitting).

  • Chính xác (Accuracy): mô hình khớp tốt với dữ liệu hiện có.
  • Tiết kiệm (Parsimony): mô hình càng đơn giản càng dễ hiểu và tổng quát hóa.

Nguyên tắc chọn mô hình: Đơn giản nhất có thể, nhưng không đơn giản quá mức.


Trực quan hóa Dữ liệu

Trực quan hóa dữ liệu là bước đầu tiên và bắt buộc trong phân tích:

  • Kiểm tra xu hướng tuyến tính hoặc phi tuyến giữa \(y\)\(x\)
  • Phát hiện điểm bất thường (outliers), phân nhóm hoặc tương tác
  • Đề xuất mô hình phù hợp hơn

Ví dụ:
Bộ dữ liệu đo dung tích thở ra (FEV) của trẻ em, với các biến:

  • FEV: thể tích khí thở ra trong một giây (lít)

  • Chiều cao (Ht, cm)

  • Giới tính (F hoặc M)

  • Tình trạng hút thuốc (có hoặc không)

Biểu đồ sử dụng: plot(), boxplot(), interaction.plot()


Xử lý Biến phân loại (Factors)

Trong R, biến phân loại được định nghĩa qua factor() và cần mã hóa đúng để sử dụng trong mô hình:

  • Ví dụ: biến Smoke với các mức "yes""no" sẽ được ánh xạ thành biến giả (dummy variable).
  • Mức tham chiếu mặc định là mức đầu tiên theo thứ tự từ điển, có thể thay đổi bằng relevel().

2. Mô Hình Hồi Quy Tuyến Tính

Khái niệm và mục đích

Hồi quy tuyến tính là một trong những mô hình thống kê nền tảng, được sử dụ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 \(y\) và một hoặc nhiều biến giải thích \(x_1, x_2, ..., x_p\).

Đây là một trường hợp đặc biệt của GLM, với:

  • Phân phối phản hồi: phân phối chuẩn (Normal)
  • Hàm liên kết: đồng nhất (identity), \(g(\mu) = \mu\)

Ứng dụng thực tiễn:

  • Dự báo hoặc mô hình hóa biến định lượng như dung tích thở FEV theo chiều cao
  • Phân tích ảnh hưởng của lượng đường tiêu thụ lên mức độ sâu răng
  • Mô hình hóa chi phí, điểm số, chỉ số sinh học, v.v.

Cấu trúc mô hình

Dạng tổng quát (scalar): \[ y_i = \beta_0 + \beta_1 x_{1i} + \cdots + \beta_p x_{pi} + \varepsilon_i, \quad \varepsilon_i \sim N(0, \sigma^2) \]

Dạng ma trận: \[ \mathbf{y} = X\boldsymbol{\beta} + \boldsymbol{\varepsilon} \]

Trong đó:

  • \(X\): ma trận thiết kế
  • \(\boldsymbol{\beta}\): vector hệ số hồi quy
  • \(\boldsymbol{\varepsilon}\): vector sai số ngẫu nhiên

Ước lượng tham số

Phương pháp: Bình phương tối thiểu (Least Squares)

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

Ước lượng phương sai sai số: \[ \hat{\sigma}^2 = \frac{\text{RSS}}{n - p'}, \quad \text{RSS} = \sum_{i=1}^n (y_i - \hat{y}_i)^2 \]


Hồi quy tuyến tính có trọng số (Weighted Least Squares - WLS)

Khi các quan sát có phương sai khác nhau (heteroscedasticity), mô hình WLS cho phép gán trọng số phù hợp:

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

Trong đó \(W = \text{diag}(w_1, ..., w_n)\) là ma trận trọng số, thường được xác định ngược với phương sai của từng quan sát.


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

Mục đích: Kiểm định xem mô hình phức tạp hơn (nhiều biến hơn) có cải thiện đáng kể so với mô hình đơn giản.

Kiểm định F:

\[ F = \frac{(\text{RSS}_A - \text{RSS}_B)/(p'_B - p'_A)}{\text{RSS}_B/(n - p'_B)} \]

Hoặc theo hệ số xác định \(R^2\):

\[ F = \frac{R^2 / (p' - 1)}{(1 - R^2)/(n - p')} \]


Biến đổi biến phản hồi

Khi giả định phương sai không hằng hoặc phân phối sai số bị lệch, ta có thể biến đổi biến phản hồi:

  • \(\log(y)\): làm giảm độ lệch phải, biến đổi hàm mũ về tuyến tính
  • \(\sqrt{y}\): ổn định phương sai

Ví dụ với log biến phụ thuộc:

Nếu mô hình hoá \(\log(y)\), ta có:

\[ \eta = \log(\mu) \Rightarrow \mu = \exp(\eta) \]

Khi đó, một đơn vị tăng trong \(x\) làm \(\mu\) thay đổi theo hệ số \(\exp(\beta_j)\).


3. Mô Hình Phân Tán Lũy Thừa (Exponential Dispersion Models)

Khái niệm và mục đích

Exponential Dispersion Models (EDMs) là một lớp phân phối xác suất bao gồm nhiều phân phối quen thuộc:

  • Normal
  • Poisson
  • Binomial
  • Gamma
  • Inverse Gaussian
  • Tweedie

EDMs là nền tảng phân phối cho GLMs, cho phép:

  • Mô hình hóa dữ liệu với bản chất phân phối khác nhau (liên tục, đếm, nhị phân, lệch phải, nhiều giá trị 0…)
  • Liên kết giữa trung bình và phương sai: \[ \text{Var}(Y) = \phi V(\mu) \]

Dạng hàm xác suất EDM

Một phân phối thuộc EDM có dạng:

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

Trong đó:

  • \(\theta\): tham số chính tắc (canonical parameter)
  • \(\kappa(\theta)\): hàm tích lũy (cumulant)
  • \(\phi\): tham số phân tán (dispersion)
  • \(\mu = \kappa'(\theta), \quad V(\mu) = \kappa''(\theta)\)

Các phân phối thuộc EDMs

Phân phối Hàm phương sai \(V(\mu)\) Hàm liên kết chính tắc
Normal \(1\) Identity: \(g(\mu) = \mu\)
Poisson \(\mu\) Log: \(\log(\mu)\)
Binomial \(\mu(1 - \mu)\) Logit: \(\log\left(\frac{\mu}{1 - \mu}\right)\)
Gamma \(\mu^2\) Inverse / Log
Inverse Gaussian \(\mu^3\) \(1 / \mu^2\), Log

Ngoài ra, một lớp phân phối tổng quát thuộc EDMs là Tweedie distribution, với hàm phương sai có dạng:

\[ V(\mu) = \mu^\xi \]

  • Khi \(\xi = 0\): Normal
  • Khi \(\xi = 1\): Poisson
  • Khi \(\xi = 2\): Gamma
  • Khi \(1 < \xi < 2\): phân phối phù hợp với dữ liệu có giá trị 0 và liên tục (compound Poisson)

Ứng dụng phổ biến: mô hình hóa dữ liệu bảo hiểm, chi phí y tế, v.v.


4. Các Mô Hình GLM Cụ Thể

4.1 Binomial GLMs – Mô hình cho tỷ lệ

Loại dữ liệu: Dữ liệu nhị phân hoặc tỷ lệ thành công trong số lần thử cố định
Ví dụ: số hạt giống nảy mầm trong số 100 hạt.

Phân phối: Nhị thức (binomial)

\[ \phi = 1, \quad V(\mu) = \mu(1 - \mu) \]

Hàm liên kết chính tắc:

\[ g(\mu) = \log\left(\frac{\mu}{1 - \mu}\right) \]

Mục đích:
Dự đoán xác suất thành công hoặc phân tích ảnh hưởng của biến giải thích đến xác suất xảy ra sự kiện.

Ví dụ:
Tỷ lệ hạt giống nảy mầm, tỷ lệ bệnh nhân khỏi bệnh, xác suất bỏ phiếu cho một ứng viên.

Lưu ý:
Nếu có hiện tượng quá phân tán (overdispersion), nên dùng quasi-binomial hoặc beta-binomial.


4.2 Poisson GLMs – Mô hình cho dữ liệu đếm

Loại dữ liệu: Số lần xảy ra sự kiện trong thời gian/khoảng không gian

Phân phối: Poisson

\[ \phi = 1, \quad V(\mu) = \mu \]

Hàm liên kết chính tắc:

\[ g(\mu) = \log(\mu) \]

Mục đích:
Mô hình hóa số lượng sự kiện: số ca bệnh, số hành vi, số cuộc gọi, v.v.

Lưu ý:

  • Poisson giả định \(\text{Var}(Y) = \mathbb{E}[Y] = \mu\)
  • Nếu có quá phân tán: dùng Negative Binomial hoặc Quasi-Poisson

Ứng dụng mở rộng:

  • Poisson regression: có biến định lượng
  • Log-linear models: dữ liệu bảng tần số
  • Modeling rates: dùng offset để điều chỉnh theo quy mô dân số hoặc thời gian

4.3 Negative Binomial GLMs – Mô hình cho đếm có quá phân tán

Loại dữ liệu: Dữ liệu đếm có phương sai lớn hơn trung bình

Phân phối: Negative Binomial

\[ V(\mu) = \mu + \frac{\mu^2}{k}, \quad k > 0 \]

Mục đích:
Giải quyết hiện tượng quá phân tán bằng cách thêm biến ngẫu nhiên cấp hai vào cường độ xảy ra sự kiện.

Ước lượng:
Dùng Maximum Likelihood Estimation để ước lượng \(\beta\)\(k\)

Ví dụ:
Dữ liệu số vết đốm trên lá (pock data)


4.4 Quasi-Poisson GLMs – Mô hình đếm với phân tán tự do

Loại dữ liệu: Đếm, nhưng không cần phân phối xác suất cụ thể

Đặc điểm:

\[ V(\mu) = \mu, \quad \phi \ne 1 \]

Mục đích:
Giữ công thức Poisson nhưng cho phép phương sai lớn hơn trung bình

Lưu ý:
Không thể tính AIC vì không có hàm khả năng cụ thể.


4.5 Gamma GLMs – Dữ liệu liên tục dương, lệch phải

Loại dữ liệu: Liên tục > 0, ví dụ: chi phí, thời gian, lượng mưa

Phân phối: Gamma

\[ V(\mu) = \mu^2 \]

Hàm liên kết thường dùng: Log, Identity, Inverse

Ứng dụng:
Thời gian chờ, chi phí bảo hiểm, độ thấm nước

Mục đích:
Mô hình hóa mối quan hệ phi tuyến giữa biến giải thích và log(μ)


4.6 Inverse Gaussian GLMs – Khi dữ liệu lệch mạnh

Phân phối: Inverse Gaussian

\[ V(\mu) = \mu^3 \]

Ứng dụng:
Dữ liệu thời gian di chuyển, thời gian đạt ngưỡng trong các tiến trình ngẫu nhiên

So sánh với Gamma:
Thích hợp hơn khi dữ liệu có đuôi dài hoặc lệch phải rất mạnh.


4.7 Tweedie GLMs – Dữ liệu dương có thể có giá trị 0

Lớp phân phối: Tweedie

\[ V(\mu) = \mu^\xi, \quad 1 < \xi < 2 \]

Hàm liên kết phổ biến: Log

Ứng dụng:
Chi phí bảo hiểm – dữ liệu nhiều giá trị 0 nhưng cũng có giá trị dương lớn

Lưu ý:
Phải ước lượng chỉ số \(\xi\) từ dữ liệu

Tùy vào giá trị \(\xi\):

  • \(\xi = 0\): Normal
  • \(\xi = 1\): Poisson
  • \(\xi = 2\): Gamma
  • \(1 < \xi < 2\): Dữ liệu liên tục có giá trị 0 (phân phối kiểu zero-inflated)

5. Chẩn Đoán và Đánh Giá Mô Hình

Mục tiêu của chẩn đoán mô hình

  • Kiểm tra sự phù hợp của mô hình với dữ liệu
  • Phát hiện sai lệch giả định (về phân phối, phương sai, dạng hàm)
  • Nhận diện các quan sát bất thường hoặc ảnh hưởng mạnh đến mô hình

Các loại phần dư (Residuals)

  • Standardized residuals (phần dư chuẩn hóa):

    \[ r_i = \frac{y_i - \hat{\mu}_i}{\sqrt{\hat{V}(\hat{\mu}_i)}} \]

  • Deviance residuals:

    \[ r_i = \text{sign}(y_i - \hat{\mu}_i) \cdot \sqrt{d(y_i, \hat{\mu}_i)} \]

  • Pearson residuals:

    \[ r_i = \frac{y_i - \hat{\mu}_i}{\sqrt{V(\hat{\mu}_i)}} \]

  • Quantile residuals: Phần dư biến đổi sao cho nếu mô hình đúng thì \(r_i \sim N(0,1)\). Đặc biệt hữu ích với dữ liệu rời rạc như Binomial, Poisson.


Các biểu đồ kiểm tra mô hình

  • Residuals vs Fitted values: Kiểm tra phương sai hằng
  • Normal Q-Q plot: Kiểm tra phân phối chuẩn của phần dư (hồi quy tuyến tính)
  • Partial residual plots: Kiểm tra mối quan hệ riêng giữa biến phản hồi và từng biến giải thích
  • Scale-location plot: \(\sqrt{|r_i|}\) vs fitted values để đánh giá đồng nhất phương sai

Quan sát ảnh hưởng lớn (Influential observations)

  • Leverage (Đòn bẩy):
    Tính từ ma trận “hat”: \[ H = X (X^T X)^{-1} X^T, \quad h_i = H_{ii} \]

  • Cook’s Distance:
    Đo mức thay đổi khi loại bỏ quan sát: \[ D_i = \frac{(\hat{\mu} - \hat{\mu}_{(i)})^T (\hat{\mu} - \hat{\mu}_{(i)})}{p' s^2} \]

  • DFBETAS: Ảnh hưởng của từng quan sát đến từng hệ số \(\beta_j\)

  • DFFITS: Ảnh hưởng của quan sát đến giá trị fitted \(\hat{y}_i\)


Mô hình hóa từng bước và nguyên lý biên (Marginality Principle)

  • Không loại bỏ biến chính nếu nó nằm trong một tương tác có ý nghĩa.
  • Ví dụ: Nếu \(\text{Sugar}:\text{IndusNonInd}\) có ý nghĩa → giữ cả Sugar và IndusNonInd trong mô hình.

Tiêu chí chọn mô hình

Khi so sánh các mô hình tuyến tính tổng quát (GLM), ta thường sử dụng AIC và BIC – hai tiêu chí đánh đổi giữa độ phù hợp của mô hình và độ phức tạp (số lượng tham số).

  • AIC – Akaike Information Criterion:

    \[ \text{AIC} = -2 \ell(\hat{\beta}) + 2k \]

  • BIC – Bayesian Information Criterion:

    \[ \text{BIC} = -2 \ell(\hat{\beta}) + k \log n \] Trong đó:

  • \(\ell(\hat{\beta})\): log-likelihood của mô hình

  • \(k\): số tham số trong mô hình

  • \(n\): số lượng quan sát

Mô hình nào có AIC hoặc BIC nhỏ hơn sẽ được ưu tiên lựa chọn.

Mặc dù cả AIC và BIC đều hướng tới việc lựa chọn mô hình cân bằng giữa độ phù hợp và độ phức tạp, nhưng hai tiêu chí này khác nhau ở mức độ phạt dành cho số lượng tham số trong mô hình. Cụ thể:

  • AIC áp dụng mức phạt tuyến tính theo số tham số: \(2k\)

  • BIC áp dụng mức phạt lôgarit phụ thuộc vào kích thước mẫu: \(k \log(n)\)

Do đó, khi kích thước mẫu \(n\) tăng, hệ số \(\log(n)\) trong công thức BIC cũng trở nên lớn hơn, khiến BIC trừng phạt mô hình phức tạp nghiêm khắc hơn so với AIC. Điều này giúp BIC có xu hướng chọn các mô hình đơn giản hơn, tránh hiện tượng overfitting.


6. Kiểm Định Giả Thuyết (Hypothesis Testing in GLMs)

Mục tiêu

  • Đánh giá xem một hay nhiều tham số hồi quy có khác không (có ý nghĩa thống kê)
  • So sánh mô hình đầy đủ và mô hình rút gọn (nested models)

Các kiểm định chính

1. Wald Test

Dựa trên khoảng cách giữa ước lượng và giả thuyết null:

\[ W = \frac{(\hat{\beta}_j - \beta_{0j})^2}{\text{Var}(\hat{\beta}_j)} \sim \chi^2_1 \]

  • Dễ tính toán từ output của summary(glm(...))
  • Nhược điểm: ít chính xác với hệ số lớn hoặc phân phối lệch

2. Likelihood Ratio Test (LRT)

So sánh log-likelihood giữa mô hình đầy đủ và mô hình bị ràng buộc:

\[ L = 2 \left[ \ell(\hat{\beta}) - \ell(\beta^*) \right] \sim \chi^2_q \]

  • Phù hợp khi so sánh hai mô hình lồng nhau
  • Dùng được qua anova(model1, model2, test = "Chisq")

3. Score Test (Rao Test)

Dựa trên gradient của log-likelihood tại giá trị giả thuyết:

\[ S = U(\zeta^*)^T \cdot I^{-1}(\zeta^*) \cdot U(\zeta^*) \]

  • Ưu điểm: không cần fit mô hình đầy đủ
  • Khó tính toán thủ công trong R

So sánh các kiểm định

Kiểm định Ưu điểm Nhược điểm
Wald Tính toán trực tiếp từ mô hình đã fit (summary(glm)) Ít chính xác với hệ số lớn hoặc phân phối lệch
Likelihood Ratio Test (LRT) Chính xác với mô hình lồng nhau Phải fit cả hai mô hình
Score Test Không cần fit mô hình đầy đủ Phức tạp hơn để tính trong R

7. Sử Dụng R Trong Phân Tích GLMs (R Usage in GLMs)

Tổng quan

Ngôn ngữ R được sử dụng xuyên suốt trong sách để:

  • Fit các mô hình hồi quy tuyến tính và tuyến tính tổng quát (GLMs)
  • Tính toán các giá trị ước lượng, phần dư, và thống kê kiểm định
  • Vẽ biểu đồ để chẩn đoán mô hình
  • So sánh và chọn mô hình tốt nhất

Một số hàm cơ bản trong R với GLMs

Hàm / Lệnh R Mục đích
lm() Fit mô hình hồi quy tuyến tính
glm() Fit mô hình GLM với phân phối và hàm liên kết tùy chọn
summary() Hiển thị bảng kết quả với ước lượng, SE, z-value, p-value
anova() So sánh các mô hình, đặc biệt với test = "Chisq" cho LRT
plot() Vẽ biểu đồ chẩn đoán mô hình (residuals, Cook’s distance, Q-Q plot)
predict() Dự đoán giá trị fitted hoặc mới, có thể kèm khoảng tin cậy
fitted() Trích xuất giá trị fitted từ mô hình
residuals() Trích xuất phần dư theo loại (Pearson, deviance, …), ví dụ: residuals(model, type = "deviance")
cooks.distance() Tính khoảng cách Cook để phát hiện điểm ảnh hưởng
influence.measures() Tổng hợp các chỉ số ảnh hưởng
AIC(), BIC() So sánh mô hình bằng tiêu chí thông tin

BÀI TẬP 2: Thực hiện thống kê mô tả cho các biến trong file: Supermarket Transactions.csv

library(readr)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following object is masked from 'package:kableExtra':
## 
##     group_rows
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(skimr)
library(psych)
library(csv)
library(DT)
library(ggplot2)
## 
## Attaching package: 'ggplot2'
## The following objects are masked from 'package:psych':
## 
##     %+%, alpha
library(forcats)
library(knitr)
library(tibble)

1. Đọc dữ liệu từ file CSV

Vì bộ dữ liệu được lưu dưới định dạng CSV, ta tiến hành đọc dữ liệu thông qua hàm read.csv() trong R. Kết quả được lưu vào đối tượng data để sử dụng cho các phân tích tiếp theo.

data <- read.csv("F:/PTDLDT/Supermarket Transactions.csv", header = T)
datatable(data)
## Warning in instance$preRenderHook(instance): It seems your data is too big for
## client-side DataTables. You may consider server-side processing:
## https://rstudio.github.io/DT/server.html

2. Khái quát về bộ dữ liệu

2.1 Mô tả bộ dữ liệu

Tập dữ liệu ghi lại các thông tin liên quan đến giao dịch mua hàng tại siêu thị, bao gồm thông tin khách hàng, địa lý, và sản phẩm. Dữ liệu phù hợp cho các mục tiêu như:

  • Phân tích thói quen tiêu dùng

  • Nhận diện phân khúc thị trường

  • Hỗ trợ quyết định kinh doanh theo địa phương hoặc nhóm sản phẩm


2.2 Danh sách các biến và ý nghĩa

Trước khi tiến hành phân tích, ta cần tìm hiểu cụ thể các biến có trong bộ dữ liệu, bao gồm tên biến, kiểu dữ liệu và ý nghĩa nội dung. Việc hiểu rõ các biến giúp xác định chính xác phương pháp xử lý phù hợp cho từng loại biến (định tính hay định lượng).

Tên của các biến trong bộ dữ liệu có thể được liệt kê thông qua lệnh names() như sau:

names(data)
##  [1] "X"                 "PurchaseDate"      "CustomerID"       
##  [4] "Gender"            "MaritalStatus"     "Homeowner"        
##  [7] "Children"          "AnnualIncome"      "City"             
## [10] "StateorProvince"   "Country"           "ProductFamily"    
## [13] "ProductDepartment" "ProductCategory"   "UnitsSold"        
## [16] "Revenue"

Dưới đây là bảng mô tả chi tiết từng biến:

variable_description <- data.frame(
  Bien = c("Unnamed: 0", "PurchaseDate", "CustomerID", "Gender", "MaritalStatus", "Homeowner",
           "Children", "AnnualIncome", "City", "StateorProvince", "Country", "ProductFamily",
           "ProductDepartment", "ProductCategory", "UnitsSold", "Revenue"),
  Mo_ta = c("Chỉ số dòng (có thể không cần thiết)",
            "Ngày thực hiện giao dịch",
            "ID khách hàng",
            "Giới tính: F - Nữ, M - Nam",
            "Tình trạng hôn nhân: S - Độc thân, M - Đã kết hôn",
            "Sở hữu nhà: Y - Có, N - Không",
            "Số con trong gia đình",
            "Thu nhập hằng năm (phân nhóm)",
            "Thành phố cư trú",
            "Bang hoặc tỉnh",
            "Quốc gia",
            "Nhóm sản phẩm chính",
            "Bộ phận sản phẩm",
            "Danh mục sản phẩm cụ thể",
            "Số lượng sản phẩm bán ra",
            "Doanh thu giao dịch (USD)"),
  stringsAsFactors = FALSE
)

kbl(variable_description, col.names = c("Biến", "Mô tả"), booktabs = TRUE) %>%
  kable_styling(latex_options = c("striped", "hold_position"))
Biến Mô tả
Unnamed: 0 Chỉ số dòng (có thể không cần thiết)
PurchaseDate Ngày thực hiện giao dịch
CustomerID ID khách hàng
Gender Giới tính: F - Nữ, M - Nam
MaritalStatus Tình trạng hôn nhân: S - Độc thân, M - Đã kết hôn
Homeowner Sở hữu nhà: Y - Có, N - Không
Children Số con trong gia đình
AnnualIncome Thu nhập hằng năm (phân nhóm)
City Thành phố cư trú
StateorProvince Bang hoặc tỉnh
Country Quốc gia
ProductFamily Nhóm sản phẩm chính
ProductDepartment Bộ phận sản phẩm
ProductCategory Danh mục sản phẩm cụ thể
UnitsSold Số lượng sản phẩm bán ra
Revenue Doanh thu giao dịch (USD)

2.3 Cấu trúc dữ liệu và kiểu biến

Để có cái nhìn tổng quan về dữ liệu, ta sử dụng các hàm dim()str() nhằm kiểm tra kích thước bảng dữ liệu cũng như kiểu dữ liệu của từng biến.

Số dòng và số cột trong bộ dữ liệu

dim(data)
## [1] 14059    16

Kết quả cho thấy bộ dữ liệu có 14,059 dòng (giao dịch) và 16 biến (thuộc tính).

Cấu trúc dữ liệu và kiểu biến

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

Nhận xét

  • Phần lớn các biến định tính như Gender, MaritalStatus, Homeowner, City, Country,… hiện đang ở dạng character. Để phục vụ phân tích thống kê và trực quan hóa, nên chuyển các biến này sang kiểu factor.

  • Các biến định lượng như UnitsSoldRevenue có kiểu numeric – đây là định dạng phù hợp để thực hiện các phép tính như trung bình, độ lệch chuẩn,…

  • Biến PurchaseDate đang được lưu dưới dạng chuỗi (character). Nếu cần thực hiện phân tích theo thời gian, biến này nên được chuyển sang kiểu Date để xử lý chính xác hơn.

  • Ngoài ra, biến X có thể chỉ là chỉ số thứ tự quan sát, không mang ý nghĩa phân tích nên có thể loại bỏ nếu không cần thiết.

3. Thống kê mô tả

Dữ liệu bao gồm cả các biến định lượng (như số lượng bán ra, doanh thu) và định tính (như giới tính, tình trạng hôn nhân, khu vực,…).
Do đó, ta sẽ thực hiện thống kê mô tả riêng biệt cho từng nhóm biến nhằm đảm bảo lựa chọn đúng công cụ và phương pháp phân tích phù hợp với loại dữ liệu tương ứng.

3.1 Biến định lượng

Trong phần này, ta xem xét ba biến định lượng chính là Children, UnitsSold và Revenue – đại diện cho số con trong gia đình, số sản phẩm bán được và doanh thu của mỗi giao dịch.

# Tạo bảng thống kê
quant_stats <- psych::describe(select(data, Children, UnitsSold, Revenue)) %>%
  select(mean, median, min, max, sd, skew, kurtosis) %>%
  round(2) %>%
  rownames_to_column(var = "Biến")

# Đổi tên cột cho rõ ràng
colnames(quant_stats) <- c(
  "Biến", "Trung bình", "Trung vị", "Min", "Max",
  "Độ lệch chuẩn", "Skew", "Kurtosis"
)

# Hiển thị bảng đẹp hơn
kable(quant_stats, format = "html", align = "c", booktabs = TRUE,
      caption = "Bảng thống kê mô tả các biến định lượng") %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"),
                full_width = F, position = "center", font_size = 13)
Bảng thống kê mô tả các biến định lượng
Biến Trung bình Trung vị Min Max Độ lệch chuẩn Skew Kurtosis
Children 2.53 3.00 0.00 5.0 1.49 -0.02 -1.03
UnitsSold 4.08 4.00 1.00 8.0 1.17 0.01 -0.44
Revenue 13.00 11.25 0.53 56.7 8.22 1.13 1.39

Nhận xét:

  • Children: Số con trung bình mỗi hộ gia đình là 2.53, với độ lệch chuẩn 1.49 và giá trị nằm trong khoảng từ 0 đến 5. Skew và kurtosis đều gần 0 → cho thấy phân phối khá đối xứng và dẹt, đặc trưng của dữ liệu rời rạc.

  • UnitsSold: Trung bình mỗi giao dịch bán được 4 sản phẩm, với trung vị cũng là 4.00 → chỉ ra phân phối cân đối và ổn định. Độ lệch và độ nhọn phân phối nhỏ (≈ 0) xác nhận đặc tính gần chuẩn.

  • Revenue: Doanh thu trung bình mỗi giao dịch đạt 13 USD, nhưng có phương sai lớn (sd = 8.22) và phân phối lệch phải rõ rệt (skew = 1.13). Điều này cho thấy đa số các giao dịch có doanh thu thấp, và chỉ một số ít mang lại giá trị cao bất thường (kurtosis > 1).


3.2. Biến định tính

Các biến định tính sẽ được lần lượt trình bày với bảng tần số và tỷ lệ. Đây là nhóm các biến dạng chuỗi hoặc định danh, không thể áp dụng phép toán.

3.2.1 Gender (Giới tính)

Bảng tần số và tỷ lệ %:

gender_table <- table(data$Gender)
gender_prop <- prop.table(gender_table)

gender_df <- data.frame(
  Giới_tính = names(gender_table),
  `Số lượng` = as.vector(gender_table),
  `Tỷ lệ (%)` = round(100 * as.vector(gender_prop), 2),
  check.names = FALSE
)

kable(gender_df, align = "c", col.names = c("Giới tính", "Số lượng", "Tỷ lệ (%)")) %>%
  kableExtra::kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Giới tính Số lượng Tỷ lệ (%)
F 7170 51
M 6889 49

Biểu đồ:

data %>%
  ggplot(aes(x = Gender)) +
  geom_bar(fill = "#2E86C1") +
  labs(title = "Phân bố giới tính", x = "Giới tính", y = "Số lượng") +
  theme_minimal()

Nhận xét: Tỷ lệ khách hàng nam và nữ gần như cân bằng, cho thấy phân bố giới tính trong dữ liệu là đồng đều. Điều này tạo điều kiện thuận lợi để siêu thị triển khai các chiến lược marketing mang tính toàn diện, hướng đến cả hai nhóm đối tượng mà không cần phân biệt rõ rệt theo giới.


3.2.2 MaritalStatus (Tình trạng hôn nhân)

Bảng tần số và tỷ lệ %:

tbl2 <- table(data$MaritalStatus)
prop2 <- prop.table(tbl2)

df2 <- data.frame(
  Muc = names(tbl2),
  `Số lượng` = as.vector(tbl2),
  `Tỷ lệ (%)` = round(100 * as.vector(prop2), 2),
  check.names = FALSE
)

library(kableExtra)
kable(df2, align = "c", col.names = c("Tình trạng hôn nhân", "Số lượng", "Tỷ lệ (%)")) %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Tình trạng hôn nhân Số lượng Tỷ lệ (%)
M 6866 48.84
S 7193 51.16

Biểu đồ:

data %>%
  ggplot(aes(x = MaritalStatus)) +
  geom_bar(fill = "#CA6F1E") +
  labs(title = "Phân bố tình trạng hôn nhân", x = "Tình trạng", y = "Số lượng") +
  theme_minimal()

Nhận xét: Tỷ lệ khách hàng độc thân chiếm ưu thế nhẹ so với nhóm đã kết hôn, cho thấy đây là một phân khúc tiềm năng – đại diện cho nhóm khách hàng có xu hướng tự chủ về tài chính và nhu cầu chi tiêu linh hoạt hơn. Điều này mở ra cơ hội cho các chiến lược sản phẩm và khuyến mãi nhắm đến cá nhân hơn là hộ gia đình.


3.2.3 Homeowner (Sở hữu nhà)

Bảng tần số và tỷ lệ%:

tbl3 <- table(data$Homeowner)
prop3 <- prop.table(tbl3)

df3 <- data.frame(
  Muc = names(tbl3),
  `Số lượng` = as.vector(tbl3),
  `Tỷ lệ (%)` = round(100 * as.vector(prop3), 2),
  check.names = FALSE
)

kable(df3, align = "c", col.names = c("Sở hữu nhà", "Số lượng", "Tỷ lệ (%)")) %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Sở hữu nhà Số lượng Tỷ lệ (%)
N 5615 39.94
Y 8444 60.06

Biểu đồ:

data %>%
  ggplot(aes(x = Homeowner)) +
  geom_bar(fill = "#28B463") +
  labs(title = "Trạng thái sở hữu nhà", x = "Sở hữu nhà", y = "Số lượng") +
  theme_minimal()

Nhận xét: Khoảng 60% khách hàng trong dữ liệu là người sở hữu nhà, cho thấy phần lớn khách hàng thuộc nhóm có nền tảng tài chính ổn định. Điều này có thể gợi ý rằng họ có xu hướng chi tiêu cao hơn hoặc sẵn sàng đầu tư vào các sản phẩm/dịch vụ có giá trị lớn hơn so với nhóm chưa sở hữu nhà. Đây là một yếu tố quan trọng để cân nhắc trong các chiến lược định giá và phân khúc thị trường.


3.2.4 Thu nhập hàng năm (AnnualIncome)

Bảng tần số và tỷ lệ%:

data$AnnualIncome <- trimws(gsub("\\$", "", data$AnnualIncome))

income_table <- table(data$AnnualIncome)
income_prop <- prop.table(income_table)

income_df <- data.frame(
  Muc = names(income_table),
  Soluong = as.vector(income_table),
  Tile = round(100 * as.vector(income_prop), 2)
)

kable(income_df, col.names = c("Mức thu nhập", "Số lượng", "Tỷ lệ (%)"), align = "c") %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Mức thu nhập Số lượng Tỷ lệ (%)
10K - 30K 3090 21.98
110K - 130K 643 4.57
130K - 150K 760 5.41
150K + 273 1.94
30K - 50K 4601 32.73
50K - 70K 2370 16.86
70K - 90K 1709 12.16
90K - 110K 613 4.36

Biểu đồ:

data %>%
  ggplot(aes(x = fct_infreq(AnnualIncome))) +
  geom_bar(fill = "#7D3C98") +
  labs(title = "Phân bố thu nhập hàng năm", x = "Mức thu nhập", y = "Số lượng") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét: Phần lớn khách hàng (gần 50%) tập trung trong nhóm thu nhập trung bình từ 30K đến 70K, cho thấy đây là phân khúc chủ đạo và có tiềm năng chi tiêu ổn định. Điều này gợi ý rằng siêu thị nên tập trung phát triển các dòng sản phẩm phổ thông, có mức giá hợp lý, cùng với chương trình khuyến mãi và ưu đãi phù hợp với khả năng chi tiêu của nhóm này. Bên cạnh đó, đây cũng là nhóm có thể dễ dàng chuyển đổi thành khách hàng thân thiết thông qua chính sách chăm sóc và tích điểm mua sắm hiệu quả.


3.2.5 Thành phố (City)

Bảng tần số và tỷ lệ%:

city_table <- table(data$City)
city_prop <- prop.table(city_table)

city_df <- data.frame(
  City = names(city_table),
  Soluong = as.vector(city_table),
  Tile = round(100 * as.vector(city_prop), 2)
)

kable(city_df, col.names = c("Thành phố", "Số lượng", "Tỷ lệ (%)"), align = "c") %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Thành phố Số lượng Tỷ lệ (%)
Acapulco 383 2.72
Bellingham 143 1.02
Beverly Hills 811 5.77
Bremerton 834 5.93
Camacho 452 3.22
Guadalajara 75 0.53
Hidalgo 845 6.01
Los Angeles 926 6.59
Merida 654 4.65
Mexico City 194 1.38
Orizaba 464 3.30
Portland 876 6.23
Salem 1386 9.86
San Andres 621 4.42
San Diego 866 6.16
San Francisco 130 0.92
Seattle 922 6.56
Spokane 875 6.22
Tacoma 1257 8.94
Vancouver 633 4.50
Victoria 176 1.25
Walla Walla 160 1.14
Yakima 376 2.67

Biểu đồ:

data %>%
  ggplot(aes(x = fct_infreq(City))) +
  geom_bar(fill = "#F39C12") +
  labs(title = "Phân bố theo thành phố", x = "Thành phố", y = "Số lượng") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét: Dữ liệu cho thấy Salem (9.86%), Tacoma (8.94%), Los Angeles (6.59%), Seattle (6.56%), và Portland (6.23%) là những thành phố có mật độ giao dịch cao nhất, cho thấy đây là các khu vực thị trường trọng điểm mà siêu thị nên ưu tiên đầu tư về quảng bá, phân phối và chăm sóc khách hàng.

Ngoài ra, các thành phố như Bremerton, Spokane, San Diego và Hidalgo cũng chiếm tỷ trọng khá lớn (trên 5%), đóng vai trò bổ trợ quan trọng trong chiến lược mở rộng thị trường.

Ngược lại, các thành phố như Guadalajara, Victoria, San Francisco chỉ chiếm tỷ lệ dưới 1.5%, nên có thể được xem là khu vực tiềm năng dài hạn hoặc dùng để thử nghiệm các chiến dịch nhỏ trước khi mở rộng quy mô.


3.2.6 Bang/Tỉnh (StateorProvince)

Bảng tần số và tỷ lệ%:

state_table <- table(data$StateorProvince)
state_prop <- prop.table(state_table)
state_df <- data.frame(
  State = names(state_table),
  Soluong = as.vector(state_table),
  Tile = round(100 * as.vector(state_prop), 2)
)
kable(state_df, col.names = c("Bang/Tỉnh", "Số lượng", "Tỷ lệ (%)"), align = "c") %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Bang/Tỉnh Số lượng Tỷ lệ (%)
BC 809 5.75
CA 2733 19.44
DF 815 5.80
Guerrero 383 2.72
Jalisco 75 0.53
OR 2262 16.09
Veracruz 464 3.30
WA 4567 32.48
Yucatan 654 4.65
Zacatecas 1297 9.23

Biểu đồ:

data %>%
  ggplot(aes(x = fct_infreq(StateorProvince))) +
  geom_bar(fill = "#5DADE2") +
  labs(title = "Phân bố theo bang/tỉnh", x = "Bang/Tỉnh", y = "Số lượng") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét: Ba bang Washington (WA - 32.48%), California (CA - 19.44%) và Oregon (OR - 16.09%) chiếm tổng cộng gần 70% số lượng khách hàng, khẳng định đây là khu vực trọng tâm trong chiến lược thị trường.

→ Các hoạt động như phân tích hành vi tiêu dùng, tối ưu hóa danh mục sản phẩm, và triển khai chiến dịch tiếp thị nên ưu tiên tập trung vào nhóm bang này để đạt hiệu quả cao nhất.

Bên cạnh đó, Zacatecas (9.23%) và DF (5.80%) cũng thể hiện là những thị trường đáng chú ý với quy mô trung bình, phù hợp cho việc mở rộng dịch vụ hoặc thử nghiệm chính sách mới. Các bang có tỷ trọng thấp hơn như Guerrero, Veracruz, Yucatan hay Jalisco có thể được xem là thị trường vệ tinh, tiềm năng để phát triển dài hạn hoặc triển khai các chiến dịch khu vực nhỏ, có chọn lọc.


3.2.7 Quốc gia (Country)

Bảng tần số và tỷ lệ%:

country_table <- table(data$Country)
country_prop <- prop.table(country_table)
country_df <- data.frame(
  Country = names(country_table),
  Soluong = as.vector(country_table),
  Tile = round(100 * as.vector(country_prop), 2)
)
kable(country_df, col.names = c("Quốc gia", "Số lượng", "Tỷ lệ (%)"), align = "c") %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Quốc gia Số lượng Tỷ lệ (%)
Canada 809 5.75
Mexico 3688 26.23
USA 9562 68.01

Biểu đồ:

data %>%
  ggplot(aes(x = Country)) +
  geom_bar(fill = "#E74C3C") +
  labs(title = "Phân bố theo quốc gia", x = "Quốc gia", y = "Số lượng") +
  theme_minimal()

Nhận xét: Hoa Kỳ (USA) chiếm tỷ lệ áp đảo với 68.01% số khách hàng, khẳng định đây là thị trường cốt lõi cần được ưu tiên đầu tư về hạ tầng, dịch vụ và chiến lược tiếp thị.

Mexico đứng thứ hai với 26.23%, là một thị trường tiềm năng với quy mô đủ lớn để triển khai chiến dịch bản địa hóa, như điều chỉnh ngôn ngữ, giá cả, ưu đãi và sản phẩm phù hợp văn hóa.

Canada tuy chỉ chiếm 5.75%, nhưng vẫn là một thị trường vệ tinh đáng chú ý, phù hợp để duy trì hiện diện thương hiệu và thử nghiệm sản phẩm/dịch vụ mới trước khi mở rộng sâu hơn.

→ Chiến lược phân khúc địa lý rõ ràng sẽ giúp doanh nghiệp tối ưu hóa nguồn lực và nâng cao hiệu quả tiếp cận từng thị trường.


3.2.8 Nhóm sản phẩm (ProductFamily)

Bảng tần số và tỷ lệ%:

tbl <- table(data$ProductFamily)
prop <- prop.table(tbl)

df <- data.frame(
  Muc = names(tbl),
  `Số lượng` = as.vector(tbl),
  `Tỷ lệ (%)` = round(100 * as.vector(prop), 2),
  check.names = FALSE
)

kable(df, align = "c", col.names = c("Nhóm sản phẩm", "Số lượng", "Tỷ lệ (%)")) %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Nhóm sản phẩm Số lượng Tỷ lệ (%)
Drink 1250 8.89
Food 10153 72.22
Non-Consumable 2656 18.89

Biểu đồ:

data %>%
  ggplot(aes(x = ProductFamily)) +
  geom_bar(fill = "#1F618D") +
  labs(title = "Phân bố nhóm sản phẩm", x = "Nhóm sản phẩm", y = "Số lượng") +
  theme_minimal()

Nhận xét: Nhóm sản phẩm Food chiếm tỷ trọng áp đảo với hơn 72% tổng số giao dịch, khẳng định đây là mảng kinh doanh cốt lõi của siêu thị và cần được ưu tiên trong chiến lược sản phẩm, trưng bày và khuyến mãi. Trong khi đó, các nhóm Non-Consumable (18.89%) và Drink (8.89%) tuy chiếm tỷ lệ thấp hơn nhưng vẫn thể hiện tiềm năng phát triển, đặc biệt thông qua các chiến dịch bán chéo hoặc kết hợp combo để tăng giá trị đơn hàng và mở rộng hành vi tiêu dùng của khách.


3.2.9 Phòng ban sản phẩm (ProductDepartment)

Bảng tần số và tỷ lệ%:

tbl <- table(data$ProductDepartment)
prop <- prop.table(tbl)

df <- data.frame(
  Muc = names(tbl),
  `Số lượng` = as.vector(tbl),
  `Tỷ lệ (%)` = round(100 * as.vector(prop), 2),
  check.names = FALSE
)

kable(df, align = "c", col.names = c("Bộ phận", "Số lượng", "Tỷ lệ (%)")) %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Bộ phận Số lượng Tỷ lệ (%)
Alcoholic Beverages 356 2.53
Baked Goods 425 3.02
Baking Goods 1072 7.63
Beverages 680 4.84
Breakfast Foods 188 1.34
Canned Foods 977 6.95
Canned Products 109 0.78
Carousel 59 0.42
Checkout 82 0.58
Dairy 903 6.42
Deli 699 4.97
Eggs 198 1.41
Frozen Foods 1382 9.83
Health and Hygiene 893 6.35
Household 1420 10.10
Meat 89 0.63
Periodicals 202 1.44
Produce 1994 14.18
Seafood 102 0.73
Snack Foods 1600 11.38
Snacks 352 2.50
Starchy Foods 277 1.97

Biểu đồ:

data %>%
  ggplot(aes(x = fct_infreq(ProductDepartment))) +
  geom_bar(fill = "#D35400") +
  labs(title = "Phòng ban sản phẩm", x = "Phòng ban", y = "Số lượng") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét: Rau quả (Produce), Snack Foods và Household là ba bộ phận sản phẩm chiếm tỷ trọng lớn nhất trong tổng doanh số, lần lượt đạt 14,18%, 11,38% và 10,10%. Điều này cho thấy sức mua mạnh mẽ và sự quan tâm lớn của khách hàng đối với những nhóm hàng này. Do vậy, việc tập trung đầu tư vào trưng bày bắt mắt, triển khai các chương trình khuyến mãi hấp dẫn và phát triển đa dạng sản phẩm trong các nhóm này sẽ là chiến lược then chốt giúp tăng trưởng doanh thu và củng cố vị thế cạnh tranh trên thị trường.


3.2.10 Danh mục sản phẩm (ProductCategory)

Bảng tần số và tỷ lệ%:

tbl <- table(data$ProductCategory)
prop <- prop.table(tbl)

df <- data.frame(
  Muc = names(tbl),
  `Số lượng` = as.vector(tbl),
  `Tỷ lệ (%)` = round(100 * as.vector(prop), 2),
  check.names = FALSE
)

kable(df, align = "c", col.names = c("Danh mục", "Số lượng", "Tỷ lệ (%)")) %>%
  kable_styling(full_width = FALSE, position = "center", bootstrap_options = c("striped", "hover")) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1:3, width = "4cm")
Danh mục Số lượng Tỷ lệ (%)
Baking Goods 484 3.44
Bathroom Products 365 2.60
Beer and Wine 356 2.53
Bread 425 3.02
Breakfast Foods 417 2.97
Candles 45 0.32
Candy 352 2.50
Canned Anchovies 44 0.31
Canned Clams 53 0.38
Canned Oysters 35 0.25
Canned Sardines 40 0.28
Canned Shrimp 38 0.27
Canned Soup 404 2.87
Canned Tuna 87 0.62
Carbonated Beverages 154 1.10
Cleaning Supplies 189 1.34
Cold Remedies 93 0.66
Dairy 903 6.42
Decongestants 85 0.60
Drinks 135 0.96
Eggs 198 1.41
Electrical 355 2.53
Frozen Desserts 323 2.30
Frozen Entrees 118 0.84
Fruit 765 5.44
Hardware 129 0.92
Hot Beverages 226 1.61
Hygiene 197 1.40
Jams and Jellies 588 4.18
Kitchen Products 217 1.54
Magazines 202 1.44
Meat 761 5.41
Miscellaneous 42 0.30
Packaged Vegetables 48 0.34
Pain Relievers 192 1.37
Paper Products 345 2.45
Pizza 194 1.38
Plastic Products 141 1.00
Pure Juice Beverages 165 1.17
Seafood 102 0.73
Side Dishes 153 1.09
Snack Foods 1600 11.38
Specialty 289 2.06
Starchy Foods 277 1.97
Vegetables 1728 12.29

Biểu đồ:

data %>%
  ggplot(aes(x = fct_infreq(ProductCategory))) +
  geom_bar(fill = "#45B39D") +
  labs(title = "Danh mục sản phẩm cụ thể", x = "Danh mục", y = "Số lượng") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Nhận xét: Danh mục Vegetables và Snack Foods đứng đầu về số lượng giao dịch, lần lượt chiếm 12,29% và 11,38% tổng số. Điều này minh chứng cho sự ưu tiên rõ ràng của khách hàng đối với các sản phẩm thực phẩm tươi sống và đồ ăn nhanh tiện lợi trong thói quen mua sắm hàng ngày. Vì vậy, doanh nghiệp nên tiếp tục củng cố và phát triển mạnh mẽ hai nhóm hàng này thông qua việc nâng cao chất lượng, đa dạng hóa sản phẩm và xây dựng các chương trình khuyến mãi sáng tạo nhằm giữ vững lòng trung thành của khách hàng và tăng trưởng bền vững.

LS0tDQp0aXRsZTogIk5oaeG7h20gduG7pSB0deG6p24gMSINCmF1dGhvcjogIm50bnNhbmciDQpkYXRlOiAiMjAyNS0wNS0xNyINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIHRvY19kZXB0aDogNA0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRvYzogdHJ1ZQ0KICBwZGZfZG9jdW1lbnQ6DQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4DQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KPHN0eWxlPg0KYm9keSB7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgc2Fucy1zZXJpZjsNCiAgZm9udC1zaXplOiAxNnB4Ow0KICB0ZXh0LWFsaWduOiBqdXN0aWZ5Ow0KICBsaW5lLWhlaWdodDogMS41Ow0KfQ0KaDIgew0KICBjb2xvcjogcmVkOw0KfQ0KaDMgew0KICBjb2xvcjogZGFya2JsdWU7DQp9DQpoNCB7DQogIHRleHQtZGVjb3JhdGlvbjogdW5kZXJsaW5lOw0KfQ0KPC9zdHlsZT4NCg0KIyBCw4BJIFThuqxQIDE6ICBUw7NtIHThuq90IGN14buRbiBzw6FjaDogMjAxOV9HZW5lcmFsaXplZCBMaW5lYXIgTW9kZWxzIFdpdGggRXhhbXBsZXMgaW4gUl85NzgxNDQxOTAxMTcwLnBkZg0KDQojIyAxLiBNw7QgaMOsbmggdGjhu5FuZyBrw6ogdsOgIHRy4buxYyBxdWFuIGjDs2EgZOG7ryBsaeG7h3UgDQoNCiMjIyBLaMOhaSBuaeG7h20gTcO0IGjDrG5oIFRo4buRbmcga8OqDQoNCk3DtCBow6xuaCB0aOG7kW5nIGvDqiBsw6AgY8O0bmcgY+G7pSBtw7QgdOG6oyBjw6FjIMSR4bq3YyDEkWnhu4NtICpjw7MgaOG7hyB0aOG7kW5nKiB2w6AgKm5n4bqrdSBuaGnDqm4qIHRyb25nIGThu68gbGnhu4d1LiBLaMO0bmcgbmjGsCBjw6FjIG3DtCBow6xuaCB24bqtdCBsw70sIG3DtCBow6xuaCB0aOG7kW5nIGvDqiBsw6AgbeG7mXQgc+G7sSB44bqlcCB44buJIOKAkyBjaMO6bmcgdGEgKipraMO0bmcga+G7syB24buNbmcgbcO0IGjDrG5oIGzDoCDEkcO6bmcgaG/DoG4gdG/DoG4qKiwgbmjGsG5nICoqbW9uZyBy4bqxbmcgbsOzIGjhu691IMOtY2gqKiB0cm9uZyB2aeG7h2MgaGnhu4N1IHbDoCBwaMOibiB0w61jaCBk4buvIGxp4buHdS4NCg0KPiDigJxBbGwgbW9kZWxzIGFyZSB3cm9uZywgYnV0IHNvbWUgYXJlIHVzZWZ1bC7igJ0g4oCTIEdlb3JnZSBCb3gNCg0KTeG7l2kgbcO0IGjDrG5oIHRo4buRbmcga8OqIGfhu5NtIGhhaSB0aMOgbmggcGjhuqduOg0KDQotICoqVGjDoG5oIHBo4bqnbiBo4buHIHRo4buRbmcqKjogbcO0IHThuqMgcXVhbiBo4buHIHRydW5nIGLDrG5oIGdp4buvYSBiaeG6v24gcGjhuqNuIGjhu5NpIFwoIHkgXCkgdsOgIGPDoWMgYmnhur9uIGdp4bqjaSB0aMOtY2ggXCggeCBcKS4NCi0gKipUaMOgbmggcGjhuqduIG5n4bqrdSBuaGnDqm4qKjogbcO0IHThuqMgc+G7sSBiaeG6v24gdGhpw6puIHh1bmcgcXVhbmggdHJ1bmcgYsOsbmggXCggXG11ID0gRVt5XSBcKS4NCg0KLS0tDQoNCiMjIyBN4bulYyDEkcOtY2ggY+G7p2EgTcO0IGjDrG5oIFRo4buRbmcga8OqDQoNClTDuXkgdGhlbyBt4bulYyB0acOqdSBwaMOibiB0w61jaCwgbcO0IGjDrG5oIHRo4buRbmcga8OqIGPDsyB0aOG7gyBwaOG7pWMgduG7pToNCg0KLSAqKkThu7EgxJFvw6FuIChQcmVkaWN0aW9uKSoqOiB04buRaSDGsHUgaMOzYSBraOG6oyBuxINuZyBk4buxIMSRb8Ohbiwga2jDtG5nIG5o4bqldCB0aGnhur90IHnDqnUgY+G6p3UgdOG6pXQgY+G6oyBo4buHIHPhu5EgcGjhuqNpIGPDsyDDvSBuZ2jEqWEgdGjhu5FuZyBrw6ouDQotICoqR2nhuqNpIHRow61jaCAoSW50ZXJwcmV0YXRpb24pKio6IGhp4buDdSB2w6Aga2nhu4NtIHRyYSBt4buRaSBxdWFuIGjhu4cgbmjDom4gcXXhuqMsIHnDqnUgY+G6p3UgY8OhYyBo4buHIHPhu5EgY8OzIMO9IG5naMSpYSByw7UgcsOgbmcuDQoNCi0tLQ0KDQojIyMgxJDDoW5oIMSR4buVaSBnaeG7r2EgQ2jDrW5oIHjDoWMgdsOgIFTDrW5oIHRp4bq/dCBraeG7h20NCg0KTeG7mXQgbcO0IGjDrG5oICoqcXXDoSDEkcahbiBnaeG6o24qKiBjw7MgdGjhu4Mga2jDtG5nIG7huq9tIGLhuq90IMSRxrDhu6NjIGPhuqV1IHRyw7pjIHRo4buxYyBj4bunYSBk4buvIGxp4buHdSAoKnVuZGVyZml0dGluZyopLCB0cm9uZyBraGkgbcO0IGjDrG5oICoqcXXDoSBwaOG7qWMgdOG6oXAqKiBjw7MgdGjhu4MgcGjhuqNuIMOhbmggY+G6oyBuaGnhu4V1ICgqb3ZlcmZpdHRpbmcqKS4NCg0KLSAqKkNow61uaCB4w6FjIChBY2N1cmFjeSkqKjogbcO0IGjDrG5oIGto4bubcCB04buRdCB24bubaSBk4buvIGxp4buHdSBoaeG7h24gY8OzLg0KLSAqKlRp4bq/dCBraeG7h20gKFBhcnNpbW9ueSkqKjogbcO0IGjDrG5oIGPDoG5nIMSRxqFuIGdp4bqjbiBjw6BuZyBk4buFIGhp4buDdSB2w6AgdOG7lW5nIHF1w6F0IGjDs2EuDQoNCj4gKipOZ3V5w6puIHThuq9jIGNo4buNbiBtw7QgaMOsbmg6KiogxJDGoW4gZ2nhuqNuIG5o4bqldCBjw7MgdGjhu4MsIG5oxrBuZyBraMO0bmcgxJHGoW4gZ2nhuqNuIHF1w6EgbeG7qWMuDQoNCi0tLQ0KDQojIyMgVHLhu7FjIHF1YW4gaMOzYSBE4buvIGxp4buHdQ0KDQpUcuG7sWMgcXVhbiBow7NhIGThu68gbGnhu4d1IGzDoCBixrDhu5tjIMSR4bqndSB0acOqbiB2w6AgYuG6r3QgYnXhu5ljIHRyb25nIHBow6JuIHTDrWNoOg0KDQotIEtp4buDbSB0cmEgeHUgaMaw4bubbmcgdHV54bq/biB0w61uaCBob+G6t2MgcGhpIHR1eeG6v24gZ2nhu69hIFwoIHkgXCkgdsOgIFwoIHggXCkNCi0gUGjDoXQgaGnhu4duIMSRaeG7g20gYuG6pXQgdGjGsOG7nW5nIChvdXRsaWVycyksIHBow6JuIG5ow7NtIGhv4bq3YyB0xrDGoW5nIHTDoWMNCi0gxJDhu4EgeHXhuqV0IG3DtCBow6xuaCBwaMO5IGjhu6NwIGjGoW4NCg0KKipWw60gZOG7pToqKiAgDQpC4buZIGThu68gbGnhu4d1IMSRbyBkdW5nIHTDrWNoIHRo4bufIHJhIChGRVYpIGPhu6dhIHRy4bq7IGVtLCB24bubaSBjw6FjIGJp4bq/bjoNCg0KLSBGRVY6IHRo4buDIHTDrWNoIGtow60gdGjhu58gcmEgdHJvbmcgbeG7mXQgZ2nDonkgKGzDrXQpDQoNCi0gQ2hp4buBdSBjYW8gKEh0LCBjbSkNCg0KLSBHaeG7m2kgdMOtbmggKEYgaG/hurdjIE0pDQoNCi0gVMOsbmggdHLhuqFuZyBow7p0IHRodeG7kWMgKGPDsyBob+G6t2Mga2jDtG5nKQ0KDQpCaeG7g3UgxJHhu5Mgc+G7rSBk4bulbmc6IGBwbG90KClgLCBgYm94cGxvdCgpYCwgYGludGVyYWN0aW9uLnBsb3QoKWANCg0KLS0tDQoNCiMjIyBY4butIGzDvSBCaeG6v24gcGjDom4gbG/huqFpIChGYWN0b3JzKQ0KDQpUcm9uZyBSLCBiaeG6v24gcGjDom4gbG/huqFpIMSRxrDhu6NjIMSR4buLbmggbmdoxKlhIHF1YSBgZmFjdG9yKClgIHbDoCBj4bqnbiBtw6MgaMOzYSDEkcO6bmcgxJHhu4Mgc+G7rSBk4bulbmcgdHJvbmcgbcO0IGjDrG5oOg0KDQotIFbDrSBk4bulOiBiaeG6v24gYFNtb2tlYCB24bubaSBjw6FjIG3hu6ljIGAieWVzImAgdsOgIGAibm8iYCBz4bq9IMSRxrDhu6NjIMOhbmggeOG6oSB0aMOgbmggYmnhur9uIGdp4bqjIChkdW1teSB2YXJpYWJsZSkuDQotIE3hu6ljIHRoYW0gY2hp4bq/dSBt4bq3YyDEkeG7i25oIGzDoCBt4bupYyDEkeG6p3UgdGnDqm4gdGhlbyB0aOG7qSB04buxIHThu6sgxJFp4buDbiwgY8OzIHRo4buDIHRoYXkgxJHhu5VpIGLhurFuZyBgcmVsZXZlbCgpYC4NCg0KLS0tDQoNCiMjIDIuIE3DtCBIw6xuaCBI4buTaSBRdXkgVHV54bq/biBUw61uaCANCg0KIyMjIEtow6FpIG5p4buHbSB2w6AgbeG7pWMgxJHDrWNoDQoNCkjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIGzDoCBt4buZdCB0cm9uZyBuaOG7r25nIG3DtCBow6xuaCB0aOG7kW5nIGvDqiBu4buBbiB04bqjbmcsIMSRxrDhu6NjIHPhu60gZOG7pW5nIMSR4buDIG3DtCB04bqjIG3hu5FpIHF1YW4gaOG7hyB0dXnhur9uIHTDrW5oIGdp4buvYSBt4buZdCBiaeG6v24gcGjhuqNuIGjhu5NpIGxpw6puIHThu6VjIFwoIHkgXCkgdsOgIG3hu5l0IGhv4bq3YyBuaGnhu4F1IGJp4bq/biBnaeG6o2kgdGjDrWNoIFwoIHhfMSwgeF8yLCAuLi4sIHhfcCBcKS4NCg0KxJDDonkgbMOgIG3hu5l0ICoqdHLGsOG7nW5nIGjhu6NwIMSR4bq3YyBiaeG7h3QgY+G7p2EgR0xNKiosIHbhu5tpOg0KDQotICoqUGjDom4gcGjhu5FpIHBo4bqjbiBo4buTaSoqOiBwaMOibiBwaOG7kWkgY2h14bqpbiAoTm9ybWFsKQ0KLSAqKkjDoG0gbGnDqm4ga+G6v3QqKjogxJHhu5NuZyBuaOG6pXQgKGlkZW50aXR5KSwgXCggZyhcbXUpID0gXG11IFwpDQoNCioq4buobmcgZOG7pW5nIHRo4buxYyB0aeG7hW46KioNCg0KLSBE4buxIGLDoW8gaG/hurdjIG3DtCBow6xuaCBow7NhIGJp4bq/biDEkeG7i25oIGzGsOG7o25nIG5oxrAgZHVuZyB0w61jaCB0aOG7nyBGRVYgdGhlbyBjaGnhu4F1IGNhbw0KLSBQaMOibiB0w61jaCDhuqNuaCBoxrDhu59uZyBj4bunYSBsxrDhu6NuZyDEkcaw4budbmcgdGnDqnUgdGjhu6UgbMOqbiBt4bupYyDEkeG7mSBzw6J1IHLEg25nDQotIE3DtCBow6xuaCBow7NhIGNoaSBwaMOtLCDEkWnhu4NtIHPhu5EsIGNo4buJIHPhu5Egc2luaCBo4buNYywgdi52Lg0KDQotLS0NCg0KIyMjIEPhuqV1IHRyw7pjIG3DtCBow6xuaA0KDQoqKkThuqFuZyB04buVbmcgcXXDoXQgKHNjYWxhcik6KioNCiQkDQp5X2kgPSBcYmV0YV8wICsgXGJldGFfMSB4X3sxaX0gKyBcY2RvdHMgKyBcYmV0YV9wIHhfe3BpfSArIFx2YXJlcHNpbG9uX2ksIFxxdWFkIFx2YXJlcHNpbG9uX2kgXHNpbSBOKDAsIFxzaWdtYV4yKQ0KJCQNCg0KKipE4bqhbmcgbWEgdHLhuq1uOioqDQokJA0KXG1hdGhiZnt5fSA9IFhcYm9sZHN5bWJvbHtcYmV0YX0gKyBcYm9sZHN5bWJvbHtcdmFyZXBzaWxvbn0NCiQkDQoNClRyb25nIMSRw7M6DQoNCi0gXCggWCBcKTogbWEgdHLhuq1uIHRoaeG6v3Qga+G6vw0KLSBcKCBcYm9sZHN5bWJvbHtcYmV0YX0gXCk6IHZlY3RvciBo4buHIHPhu5EgaOG7k2kgcXV5DQotIFwoIFxib2xkc3ltYm9se1x2YXJlcHNpbG9ufSBcKTogdmVjdG9yIHNhaSBz4buRIG5n4bqrdSBuaGnDqm4NCg0KLS0tDQoNCiMjIyDGr+G7m2MgbMaw4bujbmcgdGhhbSBz4buRDQoNCioqUGjGsMahbmcgcGjDoXA6KiogQsOsbmggcGjGsMahbmcgdOG7kWkgdGhp4buDdSAoTGVhc3QgU3F1YXJlcykNCg0KJCQNClxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSA9IChYXlQgWCleey0xfSBYXlQgXG1hdGhiZnt5fQ0KJCQNCg0KKirGr+G7m2MgbMaw4bujbmcgcGjGsMahbmcgc2FpIHNhaSBz4buROioqDQokJA0KXGhhdHtcc2lnbWF9XjIgPSBcZnJhY3tcdGV4dHtSU1N9fXtuIC0gcCd9LCBccXVhZCBcdGV4dHtSU1N9ID0gXHN1bV97aT0xfV5uICh5X2kgLSBcaGF0e3l9X2kpXjINCiQkDQoNCi0tLQ0KDQojIyMgSOG7k2kgcXV5IHR1eeG6v24gdMOtbmggY8OzIHRy4buNbmcgc+G7kSAoV2VpZ2h0ZWQgTGVhc3QgU3F1YXJlcyAtIFdMUykNCg0KS2hpIGPDoWMgcXVhbiBzw6F0IGPDsyBwaMawxqFuZyBzYWkga2jDoWMgbmhhdSAoaGV0ZXJvc2NlZGFzdGljaXR5KSwgbcO0IGjDrG5oIFdMUyBjaG8gcGjDqXAgZ8OhbiB0cuG7jW5nIHPhu5EgcGjDuSBo4bujcDoNCg0KJCQNClxoYXR7XGJvbGRzeW1ib2x7XGJldGF9fSA9IChYXlQgVyBYKV57LTF9IFheVCBXIFxtYXRoYmZ7eX0NCiQkDQoNClRyb25nIMSRw7MgXCggVyA9IFx0ZXh0e2RpYWd9KHdfMSwgLi4uLCB3X24pIFwpIGzDoCBtYSB0cuG6rW4gdHLhu41uZyBz4buRLCB0aMaw4budbmcgxJHGsOG7o2MgeMOhYyDEkeG7i25oIG5nxrDhu6NjIHbhu5tpIHBoxrDGoW5nIHNhaSBj4bunYSB04burbmcgcXVhbiBzw6F0Lg0KDQotLS0NCg0KIyMjIFNvIHPDoW5oIG3DtCBow6xuaCBs4buTbmcgbmhhdQ0KDQoqKk3hu6VjIMSRw61jaDoqKiBLaeG7g20gxJHhu4tuaCB4ZW0gbcO0IGjDrG5oIHBo4bupYyB04bqhcCBoxqFuIChuaGnhu4F1IGJp4bq/biBoxqFuKSBjw7MgY+G6o2kgdGhp4buHbiDEkcOhbmcga+G7gyBzbyB24bubaSBtw7QgaMOsbmggxJHGoW4gZ2nhuqNuLg0KDQoqKktp4buDbSDEkeG7i25oIEY6KioNCg0KJCQNCkYgPSBcZnJhY3soXHRleHR7UlNTfV9BIC0gXHRleHR7UlNTfV9CKS8ocCdfQiAtIHAnX0EpfXtcdGV4dHtSU1N9X0IvKG4gLSBwJ19CKX0NCiQkDQoNCkhv4bq3YyB0aGVvIGjhu4cgc+G7kSB4w6FjIMSR4buLbmggXCggUl4yIFwpOg0KDQokJA0KRiA9IFxmcmFje1JeMiAvIChwJyAtIDEpfXsoMSAtIFJeMikvKG4gLSBwJyl9DQokJA0KDQotLS0NCg0KIyMjIEJp4bq/biDEkeG7lWkgYmnhur9uIHBo4bqjbiBo4buTaQ0KDQpLaGkgZ2nhuqMgxJHhu4tuaCBwaMawxqFuZyBzYWkga2jDtG5nIGjhurFuZyBob+G6t2MgcGjDom4gcGjhu5FpIHNhaSBz4buRIGLhu4sgbOG7h2NoLCB0YSBjw7MgdGjhu4MgYmnhur9uIMSR4buVaSBiaeG6v24gcGjhuqNuIGjhu5NpOg0KDQotIFwoIFxsb2coeSkgXCk6IGzDoG0gZ2nhuqNtIMSR4buZIGzhu4djaCBwaOG6o2ksIGJp4bq/biDEkeG7lWkgaMOgbSBtxakgduG7gSB0dXnhur9uIHTDrW5oDQotIFwoIFxzcXJ0e3l9IFwpOiDhu5VuIMSR4buLbmggcGjGsMahbmcgc2FpDQoNCioqVsOtIGThu6UgduG7m2kgbG9nIGJp4bq/biBwaOG7pSB0aHXhu5ljOioqDQoNCk7hur91IG3DtCBow6xuaCBob8OhIFwoIFxsb2coeSkgXCksIHRhIGPDszoNCg0KJCQNClxldGEgPSBcbG9nKFxtdSkgXFJpZ2h0YXJyb3cgXG11ID0gXGV4cChcZXRhKQ0KJCQNCg0KS2hpIMSRw7MsIG3hu5l0IMSRxqFuIHbhu4sgdMSDbmcgdHJvbmcgXCggeCBcKSBsw6BtIFwoIFxtdSBcKSB0aGF5IMSR4buVaSB0aGVvIGjhu4cgc+G7kSBcKCBcZXhwKFxiZXRhX2opIFwpLg0KDQotLS0NCg0KIyMgMy4gTcO0IEjDrG5oIFBow6JuIFTDoW4gTMWpeSBUaOG7q2EgKihFeHBvbmVudGlhbCBEaXNwZXJzaW9uIE1vZGVscykqDQoNCiMjIyBLaMOhaSBuaeG7h20gdsOgIG3hu6VjIMSRw61jaA0KDQoqKkV4cG9uZW50aWFsIERpc3BlcnNpb24gTW9kZWxzIChFRE1zKSoqIGzDoCBt4buZdCBs4bubcCBwaMOibiBwaOG7kWkgeMOhYyBzdeG6pXQgYmFvIGfhu5NtIG5oaeG7gXUgcGjDom4gcGjhu5FpIHF1ZW4gdGh14buZYzoNCg0KLSBOb3JtYWwNCi0gUG9pc3Nvbg0KLSBCaW5vbWlhbA0KLSBHYW1tYQ0KLSBJbnZlcnNlIEdhdXNzaWFuDQotIFR3ZWVkaWUNCg0KRURNcyBsw6AgKipu4buBbiB04bqjbmcgcGjDom4gcGjhu5FpIGNobyBHTE1zKiosIGNobyBwaMOpcDoNCg0KLSBNw7QgaMOsbmggaMOzYSBk4buvIGxp4buHdSB24bubaSBi4bqjbiBjaOG6pXQgcGjDom4gcGjhu5FpIGtow6FjIG5oYXUgKGxpw6puIHThu6VjLCDEkeG6v20sIG5o4buLIHBow6JuLCBs4buHY2ggcGjhuqNpLCBuaGnhu4F1IGdpw6EgdHLhu4sgMC4uLikNCi0gTGnDqm4ga+G6v3QgZ2nhu69hIHRydW5nIGLDrG5oIHbDoCBwaMawxqFuZyBzYWk6DQogICQkDQogIFx0ZXh0e1Zhcn0oWSkgPSBccGhpIFYoXG11KQ0KICAkJA0KDQotLS0NCg0KIyMjIEThuqFuZyBow6BtIHjDoWMgc3XhuqV0IEVETQ0KDQpN4buZdCBwaMOibiBwaOG7kWkgdGh14buZYyBFRE0gY8OzIGThuqFuZzoNCg0KJCQNCmYoeTsgXHRoZXRhLCBccGhpKSA9IGEoeSwgXHBoaSkgXGV4cFxsZWZ0XHsgXGZyYWN7eVx0aGV0YSAtIFxrYXBwYShcdGhldGEpfXtccGhpfSBccmlnaHRcfQ0KJCQNCg0KVHJvbmcgxJHDszoNCg0KLSBcKCBcdGhldGEgXCk6IHRoYW0gc+G7kSBjaMOtbmggdOG6r2MgKGNhbm9uaWNhbCBwYXJhbWV0ZXIpDQotIFwoIFxrYXBwYShcdGhldGEpIFwpOiBow6BtIHTDrWNoIGzFqXkgKGN1bXVsYW50KQ0KLSBcKCBccGhpIFwpOiB0aGFtIHPhu5EgcGjDom4gdMOhbiAoZGlzcGVyc2lvbikNCi0gXCggXG11ID0gXGthcHBhJyhcdGhldGEpLCBccXVhZCBWKFxtdSkgPSBca2FwcGEnJyhcdGhldGEpIFwpDQoNCi0tLQ0KDQojIyMgQ8OhYyBwaMOibiBwaOG7kWkgdGh14buZYyBFRE1zIA0KDQpgYGB7ciBlY2hvPUZBTFNFLCByZXN1bHRzPSdhc2lzJywgbWVzc2FnZT1GQUxTRX0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCg0KZGlzdF90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBgUGjDom4gcGjhu5FpYCA9IGMoIk5vcm1hbCIsICJQb2lzc29uIiwgIkJpbm9taWFsIiwgIkdhbW1hIiwgIkludmVyc2UgR2F1c3NpYW4iKSwNCiAgYEjDoG0gcGjGsMahbmcgc2FpICRWKFxcbXUpJGAgPSBjKA0KICAgICIkMSQiLA0KICAgICIkXFxtdSQiLA0KICAgICIkXFxtdSgxIC0gXFxtdSkkIiwNCiAgICAiJFxcbXVeMiQiLA0KICAgICIkXFxtdV4zJCINCiAgKSwNCiAgYEjDoG0gbGnDqm4ga+G6v3QgY2jDrW5oIHThuq9jYCA9IGMoDQogICAgIklkZW50aXR5OiAkZyhcXG11KSA9IFxcbXUkIiwNCiAgICAiTG9nOiAkXFxsb2coXFxtdSkkIiwNCiAgICAiTG9naXQ6ICRcXGxvZ1xcbGVmdChcXGZyYWN7XFxtdX17MSAtIFxcbXV9XFxyaWdodCkkIiwNCiAgICAiSW52ZXJzZSAvIExvZyIsDQogICAgIiQxIC8gXFxtdV4yJCwgTG9nIg0KICApLA0KICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCikNCg0Ka2JsKGRpc3RfdGFibGUsIGJvb2t0YWJzID0gVFJVRSwgYWxpZ24gPSAibGNjIiwgZXNjYXBlID0gRkFMU0UsDQogICAgY29sLm5hbWVzID0gYygiUGjDom4gcGjhu5FpIiwgIkjDoG0gcGjGsMahbmcgc2FpICRWKFxcbXUpJCIsICJIw6BtIGxpw6puIGvhur90IGNow61uaCB04bqvYyIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhsYXRleF9vcHRpb25zID0gYygiaG9sZF9wb3NpdGlvbiIsICJzdHJpcGVkIiwgImJvcmRlcmVkIikpDQoNCmBgYA0KDQpOZ2/DoGkgcmEsIG3hu5l0IGzhu5twIHBow6JuIHBo4buRaSB04buVbmcgcXXDoXQgdGh14buZYyBFRE1zIGzDoCBUd2VlZGllIGRpc3RyaWJ1dGlvbiwgduG7m2kgaMOgbSBwaMawxqFuZyBzYWkgY8OzIGThuqFuZzoNCg0KJCQNClYoXG11KSA9IFxtdV5ceGkNCiQkDQoNCi0gS2hpIFwoIFx4aSA9IDAgXCk6IE5vcm1hbA0KLSBLaGkgXCggXHhpID0gMSBcKTogUG9pc3Nvbg0KLSBLaGkgXCggXHhpID0gMiBcKTogR2FtbWENCi0gS2hpIFwoIDEgPCBceGkgPCAyIFwpOiBwaMOibiBwaOG7kWkgcGjDuSBo4bujcCB24bubaSBk4buvIGxp4buHdSBjw7MgZ2nDoSB0cuG7iyAwIHbDoCBsacOqbiB04bulYyAoY29tcG91bmQgUG9pc3NvbikNCg0K4buobmcgZOG7pW5nIHBo4buVIGJp4bq/bjogbcO0IGjDrG5oIGjDs2EgZOG7ryBsaeG7h3UgYuG6o28gaGnhu4NtLCBjaGkgcGjDrSB5IHThur8sIHYudi4NCg0KLS0tDQoNCiMjIDQuIEPDoWMgTcO0IEjDrG5oIEdMTSBD4bulIFRo4buDIA0KDQojIyMgNC4xIEJpbm9taWFsIEdMTXMg4oCTIE3DtCBow6xuaCBjaG8gdOG7tyBs4buHDQoNCioqTG/huqFpIGThu68gbGnhu4d1OioqIEThu68gbGnhu4d1IG5o4buLIHBow6JuIGhv4bq3YyB04bu3IGzhu4cgdGjDoG5oIGPDtG5nIHRyb25nIHPhu5EgbOG6p24gdGjhu60gY+G7kSDEkeG7i25oICANClbDrSBk4bulOiBz4buRIGjhuqF0IGdp4buRbmcgbuG6o3kgbeG6p20gdHJvbmcgc+G7kSAxMDAgaOG6oXQuDQoNCioqUGjDom4gcGjhu5FpOioqIE5o4buLIHRo4bupYyAoYmlub21pYWwpDQoNCiQkDQpccGhpID0gMSwgXHF1YWQgVihcbXUpID0gXG11KDEgLSBcbXUpDQokJA0KDQoqKkjDoG0gbGnDqm4ga+G6v3QgY2jDrW5oIHThuq9jOioqDQoNCiQkDQpnKFxtdSkgPSBcbG9nXGxlZnQoXGZyYWN7XG11fXsxIC0gXG11fVxyaWdodCkNCiQkDQoNCioqTeG7pWMgxJHDrWNoOioqICANCkThu7EgxJFvw6FuIHjDoWMgc3XhuqV0IHRow6BuaCBjw7RuZyBob+G6t2MgcGjDom4gdMOtY2gg4bqjbmggaMaw4bufbmcgY+G7p2EgYmnhur9uIGdp4bqjaSB0aMOtY2ggxJHhur9uIHjDoWMgc3XhuqV0IHjhuqN5IHJhIHPhu7Ega2nhu4duLg0KDQoqKlbDrSBk4bulOioqICANClThu7cgbOG7hyBo4bqhdCBnaeG7kW5nIG7huqN5IG3huqdtLCB04bu3IGzhu4cgYuG7h25oIG5ow6JuIGto4buPaSBi4buHbmgsIHjDoWMgc3XhuqV0IGLhu48gcGhp4bq/dSBjaG8gbeG7mXQg4bupbmcgdmnDqm4uDQoNCioqTMawdSDDvToqKiAgDQpO4bq/dSBjw7MgaGnhu4duIHTGsOG7o25nIHF1w6EgcGjDom4gdMOhbiAoKm92ZXJkaXNwZXJzaW9uKiksIG7Dqm4gZMO5bmcgcXVhc2ktYmlub21pYWwgaG/hurdjIGJldGEtYmlub21pYWwuDQoNCi0tLQ0KDQojIyMgNC4yIFBvaXNzb24gR0xNcyDigJMgTcO0IGjDrG5oIGNobyBk4buvIGxp4buHdSDEkeG6v20NCg0KKipMb+G6oWkgZOG7ryBsaeG7h3U6KiogU+G7kSBs4bqnbiB44bqjeSByYSBz4buxIGtp4buHbiB0cm9uZyB0aOG7nWkgZ2lhbi9raG/huqNuZyBraMO0bmcgZ2lhbg0KDQoqKlBow6JuIHBo4buRaToqKiBQb2lzc29uDQoNCiQkDQpccGhpID0gMSwgXHF1YWQgVihcbXUpID0gXG11DQokJA0KDQoqKkjDoG0gbGnDqm4ga+G6v3QgY2jDrW5oIHThuq9jOioqDQoNCiQkDQpnKFxtdSkgPSBcbG9nKFxtdSkNCiQkDQoNCioqTeG7pWMgxJHDrWNoOioqICANCk3DtCBow6xuaCBow7NhIHPhu5EgbMaw4bujbmcgc+G7sSBraeG7h246IHPhu5EgY2EgYuG7h25oLCBz4buRIGjDoG5oIHZpLCBz4buRIGN14buZYyBn4buNaSwgdi52Lg0KDQoqKkzGsHUgw706KioNCg0KLSBQb2lzc29uIGdp4bqjIMSR4buLbmggXCggXHRleHR7VmFyfShZKSA9IFxtYXRoYmJ7RX1bWV0gPSBcbXUgXCkNCi0gTuG6v3UgY8OzIHF1w6EgcGjDom4gdMOhbjogZMO5bmcgTmVnYXRpdmUgQmlub21pYWwgaG/hurdjIFF1YXNpLVBvaXNzb24NCg0KKirhu6huZyBk4bulbmcgbeG7nyBy4buZbmc6KioNCg0KLSAqUG9pc3NvbiByZWdyZXNzaW9uKjogY8OzIGJp4bq/biDEkeG7i25oIGzGsOG7o25nDQotICpMb2ctbGluZWFyIG1vZGVscyo6IGThu68gbGnhu4d1IGLhuqNuZyB04bqnbiBz4buRDQotICpNb2RlbGluZyByYXRlcyo6IGTDuW5nIG9mZnNldCDEkeG7gyDEkWnhu4F1IGNo4buJbmggdGhlbyBxdXkgbcO0IGTDom4gc+G7kSBob+G6t2MgdGjhu51pIGdpYW4NCg0KLS0tDQoNCiMjIyA0LjMgTmVnYXRpdmUgQmlub21pYWwgR0xNcyDigJMgTcO0IGjDrG5oIGNobyDEkeG6v20gY8OzIHF1w6EgcGjDom4gdMOhbg0KDQoqKkxv4bqhaSBk4buvIGxp4buHdToqKiBE4buvIGxp4buHdSDEkeG6v20gY8OzIHBoxrDGoW5nIHNhaSBs4bubbiBoxqFuIHRydW5nIGLDrG5oDQoNCioqUGjDom4gcGjhu5FpOioqIE5lZ2F0aXZlIEJpbm9taWFsDQoNCiQkDQpWKFxtdSkgPSBcbXUgKyBcZnJhY3tcbXVeMn17a30sIFxxdWFkIGsgPiAwDQokJA0KDQoqKk3hu6VjIMSRw61jaDoqKiAgDQpHaeG6o2kgcXV54bq/dCBoaeG7h24gdMaw4bujbmcgcXXDoSBwaMOibiB0w6FuIGLhurFuZyBjw6FjaCB0aMOqbSBiaeG6v24gbmfhuqt1IG5oacOqbiBj4bqlcCBoYWkgdsOgbyBjxrDhu51uZyDEkeG7mSB44bqjeSByYSBz4buxIGtp4buHbi4NCg0KKirGr+G7m2MgbMaw4bujbmc6KiogIA0KRMO5bmcgTWF4aW11bSBMaWtlbGlob29kIEVzdGltYXRpb24gxJHhu4MgxrDhu5tjIGzGsOG7o25nIFwoIFxiZXRhIFwpIHbDoCBcKCBrIFwpDQoNCioqVsOtIGThu6U6KiogIA0KROG7ryBsaeG7h3Ugc+G7kSB24bq/dCDEkeG7kW0gdHLDqm4gbMOhIChwb2NrIGRhdGEpDQoNCi0tLQ0KDQojIyMgNC40IFF1YXNpLVBvaXNzb24gR0xNcyDigJMgTcO0IGjDrG5oIMSR4bq/bSB24bubaSBwaMOibiB0w6FuIHThu7EgZG8NCg0KKipMb+G6oWkgZOG7ryBsaeG7h3U6KiogxJDhur9tLCBuaMawbmcga2jDtG5nIGPhuqduIHBow6JuIHBo4buRaSB4w6FjIHN14bqldCBj4bulIHRo4buDDQoNCioqxJDhurdjIMSRaeG7g206KioNCg0KJCQNClYoXG11KSA9IFxtdSwgXHF1YWQgXHBoaSBcbmUgMQ0KJCQNCg0KKipN4bulYyDEkcOtY2g6KiogIA0KR2nhu68gY8O0bmcgdGjhu6ljIFBvaXNzb24gbmjGsG5nIGNobyBwaMOpcCBwaMawxqFuZyBzYWkgbOG7m24gaMahbiB0cnVuZyBiw6xuaA0KDQoqKkzGsHUgw706KiogIA0KS2jDtG5nIHRo4buDIHTDrW5oIEFJQyB2w6wga2jDtG5nIGPDsyBow6BtIGto4bqjIG7Eg25nIGPhu6UgdGjhu4MuDQoNCi0tLQ0KDQojIyMgNC41IEdhbW1hIEdMTXMg4oCTIEThu68gbGnhu4d1IGxpw6puIHThu6VjIGTGsMahbmcsIGzhu4djaCBwaOG6o2kNCg0KKipMb+G6oWkgZOG7ryBsaeG7h3U6KiogTGnDqm4gdOG7pWMgPiAwLCB2w60gZOG7pTogY2hpIHBow60sIHRo4budaSBnaWFuLCBsxrDhu6NuZyBtxrBhDQoNCioqUGjDom4gcGjhu5FpOioqIEdhbW1hDQoNCiQkDQpWKFxtdSkgPSBcbXVeMg0KJCQNCg0KKipIw6BtIGxpw6puIGvhur90IHRoxrDhu51uZyBkw7luZzoqKiBMb2csIElkZW50aXR5LCBJbnZlcnNlDQoNCioq4buobmcgZOG7pW5nOioqICANClRo4budaSBnaWFuIGNo4budLCBjaGkgcGjDrSBi4bqjbyBoaeG7g20sIMSR4buZIHRo4bqlbSBuxrDhu5tjDQoNCioqTeG7pWMgxJHDrWNoOioqICANCk3DtCBow6xuaCBow7NhIG3hu5FpIHF1YW4gaOG7hyBwaGkgdHV54bq/biBnaeG7r2EgYmnhur9uIGdp4bqjaSB0aMOtY2ggdsOgIGxvZyjOvCkNCg0KLS0tDQoNCiMjIyA0LjYgSW52ZXJzZSBHYXVzc2lhbiBHTE1zIOKAkyBLaGkgZOG7ryBsaeG7h3UgbOG7h2NoIG3huqFuaA0KDQoqKlBow6JuIHBo4buRaToqKiBJbnZlcnNlIEdhdXNzaWFuDQoNCiQkDQpWKFxtdSkgPSBcbXVeMw0KJCQNCg0KKirhu6huZyBk4bulbmc6KiogIA0KROG7ryBsaeG7h3UgdGjhu51pIGdpYW4gZGkgY2h1eeG7g24sIHRo4budaSBnaWFuIMSR4bqhdCBuZ8aw4buhbmcgdHJvbmcgY8OhYyB0aeG6v24gdHLDrG5oIG5n4bqrdSBuaGnDqm4NCg0KKipTbyBzw6FuaCB24bubaSBHYW1tYToqKiAgDQpUaMOtY2ggaOG7o3AgaMahbiBraGkgZOG7ryBsaeG7h3UgY8OzIMSRdcO0aSBkw6BpIGhv4bq3YyBs4buHY2ggcGjhuqNpIHLhuqV0IG3huqFuaC4NCg0KLS0tDQoNCiMjIyA0LjcgVHdlZWRpZSBHTE1zIOKAkyBE4buvIGxp4buHdSBkxrDGoW5nIGPDsyB0aOG7gyBjw7MgZ2nDoSB0cuG7iyAwDQoNCioqTOG7m3AgcGjDom4gcGjhu5FpOioqIFR3ZWVkaWUNCg0KJCQNClYoXG11KSA9IFxtdV5ceGksIFxxdWFkIDEgPCBceGkgPCAyDQokJA0KDQoqKkjDoG0gbGnDqm4ga+G6v3QgcGjhu5UgYmnhur9uOioqIExvZw0KDQoqKuG7qG5nIGThu6VuZzoqKiAgDQpDaGkgcGjDrSBi4bqjbyBoaeG7g20g4oCTIGThu68gbGnhu4d1IG5oaeG7gXUgZ2nDoSB0cuG7iyAwIG5oxrBuZyBjxaluZyBjw7MgZ2nDoSB0cuG7iyBkxrDGoW5nIGzhu5tuDQoNCioqTMawdSDDvToqKiAgDQpQaOG6o2kgxrDhu5tjIGzGsOG7o25nIGNo4buJIHPhu5EgXCggXHhpIFwpIHThu6sgZOG7ryBsaeG7h3UNCg0KKipUw7l5IHbDoG8gZ2nDoSB0cuG7iyBcKCBceGkgXCk6KioNCg0KLSBcKCBceGkgPSAwIFwpOiBOb3JtYWwgIA0KLSBcKCBceGkgPSAxIFwpOiBQb2lzc29uICANCi0gXCggXHhpID0gMiBcKTogR2FtbWEgIA0KLSBcKCAxIDwgXHhpIDwgMiBcKTogROG7ryBsaeG7h3UgbGnDqm4gdOG7pWMgY8OzIGdpw6EgdHLhu4sgMCAocGjDom4gcGjhu5FpIGtp4buDdSB6ZXJvLWluZmxhdGVkKQ0KDQotLS0NCg0KIyMgNS4gQ2jhuqluIMSQb8OhbiB2w6AgxJDDoW5oIEdpw6EgTcO0IEjDrG5oDQoNCiMjIyBN4bulYyB0acOqdSBj4bunYSBjaOG6qW4gxJFvw6FuIG3DtCBow6xuaA0KDQotIEtp4buDbSB0cmEgc+G7sSBwaMO5IGjhu6NwIGPhu6dhIG3DtCBow6xuaCB24bubaSBk4buvIGxp4buHdQ0KLSBQaMOhdCBoaeG7h24gc2FpIGzhu4djaCBnaeG6oyDEkeG7i25oICh24buBIHBow6JuIHBo4buRaSwgcGjGsMahbmcgc2FpLCBk4bqhbmcgaMOgbSkNCi0gTmjhuq1uIGRp4buHbiBjw6FjIHF1YW4gc8OhdCBi4bqldCB0aMaw4budbmcgaG/hurdjIOG6o25oIGjGsOG7n25nIG3huqFuaCDEkeG6v24gbcO0IGjDrG5oDQoNCi0tLQ0KDQojIyMgQ8OhYyBsb+G6oWkgcGjhuqduIGTGsCAoUmVzaWR1YWxzKQ0KDQotICoqU3RhbmRhcmRpemVkIHJlc2lkdWFscyoqIChwaOG6p24gZMawIGNodeG6qW4gaMOzYSk6DQoNCiAgJCQNCiAgcl9pID0gXGZyYWN7eV9pIC0gXGhhdHtcbXV9X2l9e1xzcXJ0e1xoYXR7Vn0oXGhhdHtcbXV9X2kpfX0NCiAgJCQNCg0KLSAqKkRldmlhbmNlIHJlc2lkdWFscyoqOg0KDQogICQkDQogIHJfaSA9IFx0ZXh0e3NpZ259KHlfaSAtIFxoYXR7XG11fV9pKSBcY2RvdCBcc3FydHtkKHlfaSwgXGhhdHtcbXV9X2kpfQ0KICAkJA0KDQotICoqUGVhcnNvbiByZXNpZHVhbHMqKjoNCg0KICAkJA0KICByX2kgPSBcZnJhY3t5X2kgLSBcaGF0e1xtdX1faX17XHNxcnR7VihcaGF0e1xtdX1faSl9fQ0KICAkJA0KDQotICoqUXVhbnRpbGUgcmVzaWR1YWxzKio6IFBo4bqnbiBkxrAgYmnhur9uIMSR4buVaSBzYW8gY2hvIG7hur91IG3DtCBow6xuaCDEkcO6bmcgdGjDrCBcKCByX2kgXHNpbSBOKDAsMSkgXCkuIMSQ4bq3YyBiaeG7h3QgaOG7r3Ugw61jaCB24bubaSBk4buvIGxp4buHdSBy4budaSBy4bqhYyBuaMawIEJpbm9taWFsLCBQb2lzc29uLg0KDQotLS0NCg0KIyMjIEPDoWMgYmnhu4N1IMSR4buTIGtp4buDbSB0cmEgbcO0IGjDrG5oDQoNCi0gKipSZXNpZHVhbHMgdnMgRml0dGVkIHZhbHVlcyoqOiBLaeG7g20gdHJhIHBoxrDGoW5nIHNhaSBo4bqxbmcNCi0gKipOb3JtYWwgUS1RIHBsb3QqKjogS2nhu4NtIHRyYSBwaMOibiBwaOG7kWkgY2h14bqpbiBj4bunYSBwaOG6p24gZMawICho4buTaSBxdXkgdHV54bq/biB0w61uaCkNCi0gKipQYXJ0aWFsIHJlc2lkdWFsIHBsb3RzKio6IEtp4buDbSB0cmEgbeG7kWkgcXVhbiBo4buHIHJpw6puZyBnaeG7r2EgYmnhur9uIHBo4bqjbiBo4buTaSB2w6AgdOG7q25nIGJp4bq/biBnaeG6o2kgdGjDrWNoDQotICoqU2NhbGUtbG9jYXRpb24gcGxvdCoqOiBcKCBcc3FydHt8cl9pfH0gXCkgdnMgZml0dGVkIHZhbHVlcyDEkeG7gyDEkcOhbmggZ2nDoSDEkeG7k25nIG5o4bqldCBwaMawxqFuZyBzYWkNCg0KLS0tDQoNCiMjIyBRdWFuIHPDoXQg4bqjbmggaMaw4bufbmcgbOG7m24gKEluZmx1ZW50aWFsIG9ic2VydmF0aW9ucykNCg0KLSAqKkxldmVyYWdlICjEkMOybiBi4bqpeSk6KiogIA0KICBUw61uaCB04burIG1hIHRy4bqtbiAiaGF0IjoNCiAgJCQNCiAgSCA9IFggKFheVCBYKV57LTF9IFheVCwgXHF1YWQgaF9pID0gSF97aWl9DQogICQkDQoNCi0gKipDb29r4oCZcyBEaXN0YW5jZSoqOiAgDQogIMSQbyBt4bupYyB0aGF5IMSR4buVaSBraGkgbG/huqFpIGLhu48gcXVhbiBzw6F0Og0KICAkJA0KICBEX2kgPSBcZnJhY3soXGhhdHtcbXV9IC0gXGhhdHtcbXV9X3soaSl9KV5UIChcaGF0e1xtdX0gLSBcaGF0e1xtdX1feyhpKX0pfXtwJyBzXjJ9DQogICQkDQoNCi0gKipERkJFVEFTKio6IOG6om5oIGjGsOG7n25nIGPhu6dhIHThu6tuZyBxdWFuIHPDoXQgxJHhur9uIHThu6tuZyBo4buHIHPhu5EgXCggXGJldGFfaiBcKQ0KDQotICoqREZGSVRTKio6IOG6om5oIGjGsOG7n25nIGPhu6dhIHF1YW4gc8OhdCDEkeG6v24gZ2nDoSB0cuG7iyBmaXR0ZWQgXCggXGhhdHt5fV9pIFwpDQoNCi0tLQ0KDQojIyMgTcO0IGjDrG5oIGjDs2EgdOG7q25nIGLGsOG7m2MgdsOgIG5ndXnDqm4gbMO9IGJpw6puIChNYXJnaW5hbGl0eSBQcmluY2lwbGUpDQoNCi0gS2jDtG5nIGxv4bqhaSBi4buPIGJp4bq/biBjaMOtbmggbuG6v3UgbsOzIG7hurFtIHRyb25nIG3hu5l0IHTGsMahbmcgdMOhYyBjw7Mgw70gbmdoxKlhLg0KLSBWw60gZOG7pTogTuG6v3UgXCggXHRleHR7U3VnYXJ9Olx0ZXh0e0luZHVzTm9uSW5kfSBcKSBjw7Mgw70gbmdoxKlhIOKGkiBnaeG7ryBj4bqjIFN1Z2FyIHbDoCBJbmR1c05vbkluZCB0cm9uZyBtw7QgaMOsbmguDQoNCi0tLQ0KDQojIyMgVGnDqnUgY2jDrSBjaOG7jW4gbcO0IGjDrG5oDQoNCktoaSBzbyBzw6FuaCBjw6FjIG3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNKSwgdGEgdGjGsOG7nW5nIHPhu60gZOG7pW5nIEFJQyB2w6AgQklDIOKAkyBoYWkgdGnDqnUgY2jDrSDEkcOhbmggxJHhu5VpIGdp4buvYSDEkeG7mSBwaMO5IGjhu6NwIGPhu6dhIG3DtCBow6xuaCB2w6AgxJHhu5kgcGjhu6ljIHThuqFwIChz4buRIGzGsOG7o25nIHRoYW0gc+G7kSkuDQoNCi0gKipBSUMg4oCTIEFrYWlrZSBJbmZvcm1hdGlvbiBDcml0ZXJpb24qKjoNCg0KICAkJA0KICBcdGV4dHtBSUN9ID0gLTIgXGVsbChcaGF0e1xiZXRhfSkgKyAyaw0KICAkJA0KDQotICoqQklDIOKAkyBCYXllc2lhbiBJbmZvcm1hdGlvbiBDcml0ZXJpb24qKjoNCg0KICAkJA0KICBcdGV4dHtCSUN9ID0gLTIgXGVsbChcaGF0e1xiZXRhfSkgKyBrIFxsb2cgbg0KICAkJA0KVHJvbmcgxJHDszoNCg0KLSBcKCBcZWxsKFxoYXR7XGJldGF9KSBcKTogbG9nLWxpa2VsaWhvb2QgY+G7p2EgbcO0IGjDrG5oDQoNCi0gXCggayBcKTogc+G7kSB0aGFtIHPhu5EgdHJvbmcgbcO0IGjDrG5oDQoNCi0gXCggbiBcKTogc+G7kSBsxrDhu6NuZyBxdWFuIHPDoXQNCg0KPiAqKk3DtCBow6xuaCBuw6BvIGPDsyBBSUMgaG/hurdjIEJJQyBuaOG7jyBoxqFuIHPhur0gxJHGsOG7o2MgxrB1IHRpw6puIGzhu7FhIGNo4buNbi4qKg0KDQpN4bq3YyBkw7kgY+G6oyBBSUMgdsOgIEJJQyDEkeG7gXUgaMaw4bubbmcgdOG7m2kgdmnhu4djIGzhu7FhIGNo4buNbiBtw7QgaMOsbmggY8OibiBi4bqxbmcgZ2nhu69hIMSR4buZIHBow7kgaOG7o3AgdsOgIMSR4buZIHBo4bupYyB04bqhcCwgbmjGsG5nIGhhaSB0acOqdSBjaMOtIG7DoHkga2jDoWMgbmhhdSDhu58gbeG7qWMgxJHhu5kgcGjhuqF0IGTDoG5oIGNobyBz4buRIGzGsOG7o25nIHRoYW0gc+G7kSB0cm9uZyBtw7QgaMOsbmguIEPhu6UgdGjhu4M6DQoNCi0gQUlDIMOhcCBk4bulbmcgbeG7qWMgcGjhuqF0IHR1eeG6v24gdMOtbmggdGhlbyBz4buRIHRoYW0gc+G7kTogXCggMmsgXCkNCg0KLSBCSUMgw6FwIGThu6VuZyBt4bupYyBwaOG6oXQgbMO0Z2FyaXQgcGjhu6UgdGh14buZYyB2w6BvIGvDrWNoIHRoxrDhu5tjIG3huqt1OiBcKCBrIFxsb2cobikgXCkNCg0KRG8gxJHDsywga2hpIGvDrWNoIHRoxrDhu5tjIG3huqt1IFwoIG4gXCkgdMSDbmcsIGjhu4cgc+G7kSBcKCBcbG9nKG4pIFwpIHRyb25nIGPDtG5nIHRo4bupYyBCSUMgY8WpbmcgdHLhu58gbsOqbiBs4bubbiBoxqFuLCBraGnhur9uIEJJQyB0cuG7q25nIHBo4bqhdCBtw7QgaMOsbmggcGjhu6ljIHThuqFwIG5naGnDqm0ga2jhuq9jIGjGoW4gc28gduG7m2kgQUlDLiDEkGnhu4F1IG7DoHkgZ2nDunAgQklDIGPDsyB4dSBoxrDhu5tuZyBjaOG7jW4gY8OhYyBtw7QgaMOsbmggxJHGoW4gZ2nhuqNuIGjGoW4sIHRyw6FuaCBoaeG7h24gdMaw4bujbmcgb3ZlcmZpdHRpbmcuDQoNCi0tLQ0KDQojIyA2LiBLaeG7g20gxJDhu4tuaCBHaeG6oyBUaHV54bq/dCAqKEh5cG90aGVzaXMgVGVzdGluZyBpbiBHTE1zKSoNCg0KIyMjIE3hu6VjIHRpw6p1DQoNCi0gxJDDoW5oIGdpw6EgeGVtIG3hu5l0IGhheSBuaGnhu4F1IHRoYW0gc+G7kSBo4buTaSBxdXkgY8OzIGtow6FjIGtow7RuZyAoY8OzIMO9IG5naMSpYSB0aOG7kW5nIGvDqikNCi0gU28gc8OhbmggbcO0IGjDrG5oIMSR4bqneSDEkeG7pyB2w6AgbcO0IGjDrG5oIHLDunQgZ+G7jW4gKG5lc3RlZCBtb2RlbHMpDQoNCi0tLQ0KDQojIyMgQ8OhYyBraeG7g20gxJHhu4tuaCBjaMOtbmgNCg0KIyMjIyAxLiBXYWxkIFRlc3QNCg0KROG7sWEgdHLDqm4ga2hv4bqjbmcgY8OhY2ggZ2nhu69hIMaw4bubYyBsxrDhu6NuZyB2w6AgZ2nhuqMgdGh1eeG6v3QgbnVsbDoNCg0KJCQNClcgPSBcZnJhY3soXGhhdHtcYmV0YX1faiAtIFxiZXRhX3swan0pXjJ9e1x0ZXh0e1Zhcn0oXGhhdHtcYmV0YX1fail9IFxzaW0gXGNoaV4yXzENCiQkDQoNCi0gROG7hSB0w61uaCB0b8OhbiB04burIG91dHB1dCBj4bunYSBgc3VtbWFyeShnbG0oLi4uKSlgDQotIE5oxrDhu6NjIMSRaeG7g206IMOtdCBjaMOtbmggeMOhYyB24bubaSBo4buHIHPhu5EgbOG7m24gaG/hurdjIHBow6JuIHBo4buRaSBs4buHY2gNCg0KIyMjIyAyLiBMaWtlbGlob29kIFJhdGlvIFRlc3QgKExSVCkNCg0KU28gc8OhbmggbG9nLWxpa2VsaWhvb2QgZ2nhu69hIG3DtCBow6xuaCDEkeG6p3kgxJHhu6cgdsOgIG3DtCBow6xuaCBi4buLIHLDoG5nIGJ14buZYzoNCg0KJCQNCkwgPSAyIFxsZWZ0WyBcZWxsKFxoYXR7XGJldGF9KSAtIFxlbGwoXGJldGFeKikgXHJpZ2h0XSBcc2ltIFxjaGleMl9xDQokJA0KDQotIFBow7kgaOG7o3Aga2hpIHNvIHPDoW5oIGhhaSBtw7QgaMOsbmggbOG7k25nIG5oYXUNCi0gRMO5bmcgxJHGsOG7o2MgcXVhIGBhbm92YShtb2RlbDEsIG1vZGVsMiwgdGVzdCA9ICJDaGlzcSIpYA0KDQojIyMjIDMuIFNjb3JlIFRlc3QgKFJhbyBUZXN0KQ0KDQpE4buxYSB0csOqbiBncmFkaWVudCBj4bunYSBsb2ctbGlrZWxpaG9vZCB04bqhaSBnacOhIHRy4buLIGdp4bqjIHRodXnhur90Og0KDQokJA0KUyA9IFUoXHpldGFeKileVCBcY2RvdCBJXnstMX0oXHpldGFeKikgXGNkb3QgVShcemV0YV4qKQ0KJCQNCg0KLSDGr3UgxJFp4buDbToga2jDtG5nIGPhuqduIGZpdCBtw7QgaMOsbmggxJHhuqd5IMSR4bunDQotIEtow7MgdMOtbmggdG/DoW4gdGjhu6cgY8O0bmcgdHJvbmcgUg0KDQotLS0NCg0KIyMjIFNvIHPDoW5oIGPDoWMga2nhu4NtIMSR4buLbmgNCg0KYGBge3IgZWNobz1GQUxTRSwgcmVzdWx0cz0nYXNpcyd9DQpsaWJyYXJ5KGthYmxlRXh0cmEpDQoNCnRlc3RfdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgYEtp4buDbSDEkeG7i25oYCA9IGMoIldhbGQiLCAiTGlrZWxpaG9vZCBSYXRpbyBUZXN0IChMUlQpIiwgIlNjb3JlIFRlc3QiKSwNCiAgYMavdSDEkWnhu4NtYCA9IGMoDQogICAgIlTDrW5oIHRvw6FuIHRy4buxYyB0aeG6v3AgdOG7qyBtw7QgaMOsbmggxJHDoyBmaXQgKHN1bW1hcnkoZ2xtKSkiLA0KICAgICJDaMOtbmggeMOhYyB24bubaSBtw7QgaMOsbmggbOG7k25nIG5oYXUiLA0KICAgICJLaMO0bmcgY+G6p24gZml0IG3DtCBow6xuaCDEkeG6p3kgxJHhu6ciDQogICksDQogIGBOaMaw4bujYyDEkWnhu4NtYCA9IGMoDQogICAgIsONdCBjaMOtbmggeMOhYyB24bubaSBo4buHIHPhu5EgbOG7m24gaG/hurdjIHBow6JuIHBo4buRaSBs4buHY2giLA0KICAgICJQaOG6o2kgZml0IGPhuqMgaGFpIG3DtCBow6xuaCIsDQogICAgIlBo4bupYyB04bqhcCBoxqFuIMSR4buDIHTDrW5oIHRyb25nIFIiDQogICkNCikNCmtibCh0ZXN0X3RhYmxlLCBib29rdGFicyA9IFRSVUUsIGFsaWduID0gImxjYyIsIGVzY2FwZSA9IEZBTFNFLA0KICAgIGNvbC5uYW1lcyA9IGMoIktp4buDbSDEkeG7i25oIiwgIsavdSDEkWnhu4NtIiwgIk5oxrDhu6NjIMSRaeG7g20iKSkgJT4lDQogIGthYmxlX3N0eWxpbmcobGF0ZXhfb3B0aW9ucyA9IGMoImhvbGRfcG9zaXRpb24iLCAic3RyaXBlZCIsICJib3JkZXJlZCIpKQ0KYGBgDQoNCiMjIDcuIFPhu60gROG7pW5nIFIgVHJvbmcgUGjDom4gVMOtY2ggR0xNcyAqKFIgVXNhZ2UgaW4gR0xNcykqDQoNCiMjIyBU4buVbmcgcXVhbg0KDQpOZ8O0biBuZ+G7ryBSIMSRxrDhu6NjIHPhu60gZOG7pW5nIHh1ecOqbiBzdeG7kXQgdHJvbmcgc8OhY2ggxJHhu4M6DQoNCi0gRml0IGPDoWMgbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIHbDoCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNcykNCi0gVMOtbmggdG/DoW4gY8OhYyBnacOhIHRy4buLIMaw4bubYyBsxrDhu6NuZywgcGjhuqduIGTGsCwgdsOgIHRo4buRbmcga8OqIGtp4buDbSDEkeG7i25oDQotIFbhur0gYmnhu4N1IMSR4buTIMSR4buDIGNo4bqpbiDEkW/DoW4gbcO0IGjDrG5oDQotIFNvIHPDoW5oIHbDoCBjaOG7jW4gbcO0IGjDrG5oIHThu5F0IG5o4bqldA0KDQotLS0NCg0KIyMjIE3hu5l0IHPhu5EgaMOgbSBjxqEgYuG6o24gdHJvbmcgUiB24bubaSBHTE1zDQoNCnwgSMOgbSAvIEzhu4duaCBSIHwgTeG7pWMgxJHDrWNoIHwNCnwtLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tfA0KfCBgbG0oKWAgICAgICAgfCBGaXQgbcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oIHwNCnwgYGdsbSgpYCAgICAgIHwgRml0IG3DtCBow6xuaCBHTE0gduG7m2kgcGjDom4gcGjhu5FpIHbDoCBow6BtIGxpw6puIGvhur90IHTDuXkgY2jhu41uIHwNCnwgYHN1bW1hcnkoKWAgIHwgSGnhu4NuIHRo4buLIGLhuqNuZyBr4bq/dCBxdeG6oyB24bubaSDGsOG7m2MgbMaw4bujbmcsIFNFLCB6LXZhbHVlLCBwLXZhbHVlIHwNCnwgYGFub3ZhKClgICAgIHwgU28gc8OhbmggY8OhYyBtw7QgaMOsbmgsIMSR4bq3YyBiaeG7h3QgduG7m2kgYHRlc3QgPSAiQ2hpc3EiYCBjaG8gTFJUIHwNCnwgYHBsb3QoKWAgICAgIHwgVuG6vSBiaeG7g3UgxJHhu5MgY2jhuqluIMSRb8OhbiBtw7QgaMOsbmggKHJlc2lkdWFscywgQ29vaydzIGRpc3RhbmNlLCBRLVEgcGxvdCkgfA0KfCBgcHJlZGljdCgpYCAgfCBE4buxIMSRb8OhbiBnacOhIHRy4buLIGZpdHRlZCBob+G6t2MgbeG7m2ksIGPDsyB0aOG7gyBrw6htIGtob+G6o25nIHRpbiBj4bqteSB8DQp8IGBmaXR0ZWQoKWAgICB8IFRyw61jaCB4deG6pXQgZ2nDoSB0cuG7iyBmaXR0ZWQgdOG7qyBtw7QgaMOsbmggfA0KfCBgcmVzaWR1YWxzKClgfCBUcsOtY2ggeHXhuqV0IHBo4bqnbiBkxrAgdGhlbyBsb+G6oWkgKFBlYXJzb24sIGRldmlhbmNlLCAuLi4pLCB2w60gZOG7pTogYHJlc2lkdWFscyhtb2RlbCwgdHlwZSA9ICJkZXZpYW5jZSIpYCB8DQp8IGBjb29rcy5kaXN0YW5jZSgpYCB8IFTDrW5oIGtob+G6o25nIGPDoWNoIENvb2sgxJHhu4MgcGjDoXQgaGnhu4duIMSRaeG7g20g4bqjbmggaMaw4bufbmcgfA0KfCBgaW5mbHVlbmNlLm1lYXN1cmVzKClgIHwgVOG7lW5nIGjhu6NwIGPDoWMgY2jhu4kgc+G7kSDhuqNuaCBoxrDhu59uZyB8DQp8IGBBSUMoKWAsIGBCSUMoKWAgfCBTbyBzw6FuaCBtw7QgaMOsbmggYuG6sW5nIHRpw6p1IGNow60gdGjDtG5nIHRpbiB8DQoNCi0tLQ0KDQojIELDgEkgVOG6rFAgMjogIFRo4buxYyBoaeG7h24gdGjhu5FuZyBrw6ogbcO0IHThuqMgY2hvIGPDoWMgYmnhur9uIHRyb25nIGZpbGU6IFN1cGVybWFya2V0IFRyYW5zYWN0aW9ucy5jc3YNCg0KYGBge3J9DQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoc2tpbXIpDQpsaWJyYXJ5KHBzeWNoKQ0KbGlicmFyeShjc3YpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkodGliYmxlKQ0KYGBgDQoNCiMjIDEuIMSQ4buNYyBk4buvIGxp4buHdSB04burIGZpbGUgQ1NWDQoNClbDrCBi4buZIGThu68gbGnhu4d1IMSRxrDhu6NjIGzGsHUgZMaw4bubaSDEkeG7i25oIGThuqFuZyAqKkNTVioqLCB0YSB0aeG6v24gaMOgbmggxJHhu41jIGThu68gbGnhu4d1IHRow7RuZyBxdWEgaMOgbSBgcmVhZC5jc3YoKWAgdHJvbmcgUi4gS+G6v3QgcXXhuqMgxJHGsOG7o2MgbMawdSB2w6BvIMSR4buRaSB0xrDhu6NuZyBgZGF0YWAgxJHhu4Mgc+G7rSBk4bulbmcgY2hvIGPDoWMgcGjDom4gdMOtY2ggdGnhur9wIHRoZW8uDQoNCmBgYHtyfQ0KZGF0YSA8LSByZWFkLmNzdigiRjovUFRETERUL1N1cGVybWFya2V0IFRyYW5zYWN0aW9ucy5jc3YiLCBoZWFkZXIgPSBUKQ0KZGF0YXRhYmxlKGRhdGEpDQpgYGANCg0KLS0tDQoNCiMjIDIuIEtow6FpIHF1w6F0IHbhu4EgYuG7mSBk4buvIGxp4buHdSANCg0KIyMjIDIuMSBNw7QgdOG6oyBi4buZIGThu68gbGnhu4d1IA0KDQpU4bqtcCBk4buvIGxp4buHdSBnaGkgbOG6oWkgY8OhYyB0aMO0bmcgdGluIGxpw6puIHF1YW4gxJHhur9uIGdpYW8gZOG7i2NoIG11YSBow6BuZyB04bqhaSBzacOqdSB0aOG7iywgYmFvIGfhu5NtIHRow7RuZyB0aW4ga2jDoWNoIGjDoG5nLCDEkeG7i2EgbMO9LCB2w6Agc+G6o24gcGjhuqltLiBE4buvIGxp4buHdSBwaMO5IGjhu6NwIGNobyBjw6FjIG3hu6VjIHRpw6p1IG5oxrA6DQoNCi0gUGjDom4gdMOtY2ggdGjDs2kgcXVlbiB0acOqdSBkw7luZw0KDQotIE5o4bqtbiBkaeG7h24gcGjDom4ga2jDumMgdGjhu4sgdHLGsOG7nW5nDQoNCi0gSOG7lyB0cuG7oyBxdXnhur90IMSR4buLbmgga2luaCBkb2FuaCB0aGVvIMSR4buLYSBwaMawxqFuZyBob+G6t2MgbmjDs20gc+G6o24gcGjhuqltDQoNCi0tLQ0KDQojIyMgMi4yIERhbmggc8OhY2ggY8OhYyBiaeG6v24gdsOgIMO9IG5naMSpYSANCg0KVHLGsOG7m2Mga2hpIHRp4bq/biBow6BuaCBwaMOibiB0w61jaCwgdGEgY+G6p24gdMOsbSBoaeG7g3UgY+G7pSB0aOG7gyAqKmPDoWMgYmnhur9uIGPDsyB0cm9uZyBi4buZIGThu68gbGnhu4d1KiosIGJhbyBn4buTbSB0w6puIGJp4bq/biwga2nhu4N1IGThu68gbGnhu4d1IHbDoCDDvSBuZ2jEqWEgbuG7mWkgZHVuZy4gVmnhu4djIGhp4buDdSByw7UgY8OhYyBiaeG6v24gZ2nDunAgeMOhYyDEkeG7i25oIGNow61uaCB4w6FjIHBoxrDGoW5nIHBow6FwIHjhu60gbMO9IHBow7kgaOG7o3AgY2hvIHThu6tuZyBsb+G6oWkgYmnhur9uICjEkeG7i25oIHTDrW5oIGhheSDEkeG7i25oIGzGsOG7o25nKS4NCg0KVMOqbiBj4bunYSBjw6FjIGJp4bq/biB0cm9uZyBi4buZIGThu68gbGnhu4d1IGPDsyB0aOG7gyDEkcaw4bujYyBsaeG7h3Qga8OqIHRow7RuZyBxdWEgbOG7h25oIGBuYW1lcygpYCBuaMawIHNhdToNCg0KYGBge3J9DQpuYW1lcyhkYXRhKQ0KYGBgDQoNCkTGsOG7m2kgxJHDonkgbMOgIGLhuqNuZyBtw7QgdOG6oyBjaGkgdGnhur90IHThu6tuZyBiaeG6v246IA0KDQpgYGB7cn0NCnZhcmlhYmxlX2Rlc2NyaXB0aW9uIDwtIGRhdGEuZnJhbWUoDQogIEJpZW4gPSBjKCJVbm5hbWVkOiAwIiwgIlB1cmNoYXNlRGF0ZSIsICJDdXN0b21lcklEIiwgIkdlbmRlciIsICJNYXJpdGFsU3RhdHVzIiwgIkhvbWVvd25lciIsDQogICAgICAgICAgICJDaGlsZHJlbiIsICJBbm51YWxJbmNvbWUiLCAiQ2l0eSIsICJTdGF0ZW9yUHJvdmluY2UiLCAiQ291bnRyeSIsICJQcm9kdWN0RmFtaWx5IiwNCiAgICAgICAgICAgIlByb2R1Y3REZXBhcnRtZW50IiwgIlByb2R1Y3RDYXRlZ29yeSIsICJVbml0c1NvbGQiLCAiUmV2ZW51ZSIpLA0KICBNb190YSA9IGMoIkNo4buJIHPhu5EgZMOybmcgKGPDsyB0aOG7gyBraMO0bmcgY+G6p24gdGhp4bq/dCkiLA0KICAgICAgICAgICAgIk5nw6B5IHRo4buxYyBoaeG7h24gZ2lhbyBk4buLY2giLA0KICAgICAgICAgICAgIklEIGtow6FjaCBow6BuZyIsDQogICAgICAgICAgICAiR2nhu5tpIHTDrW5oOiBGIC0gTuG7rywgTSAtIE5hbSIsDQogICAgICAgICAgICAiVMOsbmggdHLhuqFuZyBow7RuIG5ow6JuOiBTIC0gxJDhu5ljIHRow6JuLCBNIC0gxJDDoyBr4bq/dCBow7RuIiwNCiAgICAgICAgICAgICJT4bufIGjhu691IG5ow6A6IFkgLSBDw7MsIE4gLSBLaMO0bmciLA0KICAgICAgICAgICAgIlPhu5EgY29uIHRyb25nIGdpYSDEkcOsbmgiLA0KICAgICAgICAgICAgIlRodSBuaOG6rXAgaOG6sW5nIG7Eg20gKHBow6JuIG5ow7NtKSIsDQogICAgICAgICAgICAiVGjDoG5oIHBo4buRIGPGsCB0csO6IiwNCiAgICAgICAgICAgICJCYW5nIGhv4bq3YyB04buJbmgiLA0KICAgICAgICAgICAgIlF14buRYyBnaWEiLA0KICAgICAgICAgICAgIk5ow7NtIHPhuqNuIHBo4bqpbSBjaMOtbmgiLA0KICAgICAgICAgICAgIkLhu5kgcGjhuq1uIHPhuqNuIHBo4bqpbSIsDQogICAgICAgICAgICAiRGFuaCBt4bulYyBz4bqjbiBwaOG6qW0gY+G7pSB0aOG7gyIsDQogICAgICAgICAgICAiU+G7kSBsxrDhu6NuZyBz4bqjbiBwaOG6qW0gYsOhbiByYSIsDQogICAgICAgICAgICAiRG9hbmggdGh1IGdpYW8gZOG7i2NoIChVU0QpIiksDQogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KKQ0KDQprYmwodmFyaWFibGVfZGVzY3JpcHRpb24sIGNvbC5uYW1lcyA9IGMoIkJp4bq/biIsICJNw7QgdOG6oyIpLCBib29rdGFicyA9IFRSVUUpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGxhdGV4X29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvbGRfcG9zaXRpb24iKSkNCg0KYGBgDQoNCi0tLQ0KDQojIyMgMi4zIEPhuqV1IHRyw7pjIGThu68gbGnhu4d1IHbDoCBraeG7g3UgYmnhur9uIA0KDQrEkOG7gyBjw7MgY8OhaSBuaMOsbiB04buVbmcgcXVhbiB24buBIGThu68gbGnhu4d1LCB0YSBz4butIGThu6VuZyBjw6FjIGjDoG0gYGRpbSgpYCB2w6AgYHN0cigpYCBuaOG6sW0ga2nhu4NtIHRyYSBrw61jaCB0aMaw4bubYyBi4bqjbmcgZOG7ryBsaeG7h3UgY8WpbmcgbmjGsCBraeG7g3UgZOG7ryBsaeG7h3UgY+G7p2EgdOG7q25nIGJp4bq/bi4NCg0KIyMjIyAqKlPhu5EgZMOybmcgdsOgIHPhu5EgY+G7mXQgdHJvbmcgYuG7mSBk4buvIGxp4buHdSoqDQpgYGB7cn0NCmRpbShkYXRhKQ0KYGBgDQpL4bq/dCBxdeG6oyBjaG8gdGjhuqV5IGLhu5kgZOG7ryBsaeG7h3UgY8OzIDE0LDA1OSBkw7JuZyAoZ2lhbyBk4buLY2gpIHbDoCAxNiBiaeG6v24gKHRodeG7mWMgdMOtbmgpLg0KDQojIyMjICoqQ+G6pXUgdHLDumMgZOG7ryBsaeG7h3UgdsOgIGtp4buDdSBiaeG6v24qKg0KDQpgYGB7cn0NCnN0cihkYXRhKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQqKg0KDQotIFBo4bqnbiBs4bubbiBjw6FjIGJp4bq/biDEkeG7i25oIHTDrW5oIG5oxrAgYEdlbmRlcmAsIGBNYXJpdGFsU3RhdHVzYCwgYEhvbWVvd25lcmAsIGBDaXR5YCwgYENvdW50cnlgLC4uLiBoaeG7h24gxJFhbmcg4bufIGThuqFuZyBgY2hhcmFjdGVyYC4gxJDhu4MgcGjhu6VjIHbhu6UgcGjDom4gdMOtY2ggdGjhu5FuZyBrw6ogdsOgIHRy4buxYyBxdWFuIGjDs2EsIG7Dqm4gY2h1eeG7g24gY8OhYyBiaeG6v24gbsOgeSBzYW5nIGtp4buDdSBgZmFjdG9yYC4NCg0KLSBDw6FjIGJp4bq/biDEkeG7i25oIGzGsOG7o25nIG5oxrAgYFVuaXRzU29sZGAgdsOgIGBSZXZlbnVlYCBjw7Mga2nhu4N1IGBudW1lcmljYCDigJMgxJHDonkgbMOgIMSR4buLbmggZOG6oW5nIHBow7kgaOG7o3AgxJHhu4MgdGjhu7FjIGhp4buHbiBjw6FjIHBow6lwIHTDrW5oIG5oxrAgdHJ1bmcgYsOsbmgsIMSR4buZIGzhu4djaCBjaHXhuqluLC4uLg0KDQotIEJp4bq/biBgUHVyY2hhc2VEYXRlYCDEkWFuZyDEkcaw4bujYyBsxrB1IGTGsOG7m2kgZOG6oW5nIGNodeG7l2kgKGBjaGFyYWN0ZXJgKS4gTuG6v3UgY+G6p24gdGjhu7FjIGhp4buHbiBwaMOibiB0w61jaCB0aGVvIHRo4budaSBnaWFuLCBiaeG6v24gbsOgeSBuw6puIMSRxrDhu6NjIGNodXnhu4NuIHNhbmcga2nhu4N1IGBEYXRlYCDEkeG7gyB44butIGzDvSBjaMOtbmggeMOhYyBoxqFuLg0KDQotIE5nb8OgaSByYSwgYmnhur9uIGBYYCBjw7MgdGjhu4MgY2jhu4kgbMOgIGNo4buJIHPhu5EgdGjhu6kgdOG7sSBxdWFuIHPDoXQsIGtow7RuZyBtYW5nIMO9IG5naMSpYSBwaMOibiB0w61jaCBuw6puIGPDsyB0aOG7gyBsb+G6oWkgYuG7jyBu4bq/dSBraMO0bmcgY+G6p24gdGhp4bq/dC4NCg0KIyMgMy4gVGjhu5FuZyBrw6ogbcO0IHThuqMgDQoNCkThu68gbGnhu4d1IGJhbyBn4buTbSBj4bqjIGPDoWMgYmnhur9uICoqxJHhu4tuaCBsxrDhu6NuZyoqIChuaMawIHPhu5EgbMaw4bujbmcgYsOhbiByYSwgZG9hbmggdGh1KSB2w6AgKirEkeG7i25oIHTDrW5oKiogKG5oxrAgZ2nhu5tpIHTDrW5oLCB0w6xuaCB0cuG6oW5nIGjDtG4gbmjDom4sIGtodSB24buxYywuLi4pLiAgDQpEbyDEkcOzLCB0YSBz4bq9IHRo4buxYyBoaeG7h24gdGjhu5FuZyBrw6ogbcO0IHThuqMgcmnDqm5nIGJp4buHdCBjaG8gdOG7q25nIG5ow7NtIGJp4bq/biBuaOG6sW0gxJHhuqNtIGLhuqNvIGzhu7FhIGNo4buNbiDEkcO6bmcgY8O0bmcgY+G7pSB2w6AgcGjGsMahbmcgcGjDoXAgcGjDom4gdMOtY2ggcGjDuSBo4bujcCB24bubaSBsb+G6oWkgZOG7ryBsaeG7h3UgdMawxqFuZyDhu6luZy4NCg0KIyMjIDMuMSBCaeG6v24gxJHhu4tuaCBsxrDhu6NuZyANCg0KVHJvbmcgcGjhuqduIG7DoHksIHRhIHhlbSB4w6l0IGJhIGJp4bq/biDEkeG7i25oIGzGsOG7o25nIGNow61uaCBsw6AgQ2hpbGRyZW4sIFVuaXRzU29sZCB2w6AgUmV2ZW51ZSDigJMgxJHhuqFpIGRp4buHbiBjaG8gc+G7kSBjb24gdHJvbmcgZ2lhIMSRw6xuaCwgc+G7kSBz4bqjbiBwaOG6qW0gYsOhbiDEkcaw4bujYyB2w6AgZG9hbmggdGh1IGPhu6dhIG3hu5dpIGdpYW8gZOG7i2NoLg0KDQpgYGB7cn0NCiMgVOG6oW8gYuG6o25nIHRo4buRbmcga8OqDQpxdWFudF9zdGF0cyA8LSBwc3ljaDo6ZGVzY3JpYmUoc2VsZWN0KGRhdGEsIENoaWxkcmVuLCBVbml0c1NvbGQsIFJldmVudWUpKSAlPiUNCiAgc2VsZWN0KG1lYW4sIG1lZGlhbiwgbWluLCBtYXgsIHNkLCBza2V3LCBrdXJ0b3NpcykgJT4lDQogIHJvdW5kKDIpICU+JQ0KICByb3duYW1lc190b19jb2x1bW4odmFyID0gIkJp4bq/biIpDQoNCiMgxJDhu5VpIHTDqm4gY+G7mXQgY2hvIHLDtSByw6BuZw0KY29sbmFtZXMocXVhbnRfc3RhdHMpIDwtIGMoDQogICJCaeG6v24iLCAiVHJ1bmcgYsOsbmgiLCAiVHJ1bmcgduG7iyIsICJNaW4iLCAiTWF4IiwNCiAgIsSQ4buZIGzhu4djaCBjaHXhuqluIiwgIlNrZXciLCAiS3VydG9zaXMiDQopDQoNCiMgSGnhu4NuIHRo4buLIGLhuqNuZyDEkeG6uXAgaMahbg0Ka2FibGUocXVhbnRfc3RhdHMsIGZvcm1hdCA9ICJodG1sIiwgYWxpZ24gPSAiYyIsIGJvb2t0YWJzID0gVFJVRSwNCiAgICAgIGNhcHRpb24gPSAiQuG6o25nIHRo4buRbmcga8OqIG3DtCB04bqjIGPDoWMgYmnhur9uIMSR4buLbmggbMaw4bujbmciKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwNCiAgICAgICAgICAgICAgICBmdWxsX3dpZHRoID0gRiwgcG9zaXRpb24gPSAiY2VudGVyIiwgZm9udF9zaXplID0gMTMpDQpgYGANCioqTmjhuq1uIHjDqXQ6KioNCg0KLSBDaGlsZHJlbjogU+G7kSBjb24gdHJ1bmcgYsOsbmggbeG7l2kgaOG7mSBnaWEgxJHDrG5oIGzDoCAyLjUzLCB24bubaSDEkeG7mSBs4buHY2ggY2h14bqpbiAxLjQ5IHbDoCBnacOhIHRy4buLIG7hurFtIHRyb25nIGtob+G6o25nIHThu6sgMCDEkeG6v24gNS4gU2tldyB2w6Aga3VydG9zaXMgxJHhu4F1IGfhuqduIDAg4oaSIGNobyB0aOG6pXkgcGjDom4gcGjhu5FpIGtow6EgxJHhu5FpIHjhu6luZyB2w6AgZOG6uXQsIMSR4bq3YyB0csawbmcgY+G7p2EgZOG7ryBsaeG7h3UgcuG7nWkgcuG6oWMuDQoNCi0gVW5pdHNTb2xkOiBUcnVuZyBiw6xuaCBt4buXaSBnaWFvIGThu4tjaCBiw6FuIMSRxrDhu6NjIDQgc+G6o24gcGjhuqltLCB24bubaSB0cnVuZyB24buLIGPFqW5nIGzDoCA0LjAwIOKGkiBjaOG7iSByYSBwaMOibiBwaOG7kWkgY8OibiDEkeG7kWkgdsOgIOG7lW4gxJHhu4tuaC4gxJDhu5kgbOG7h2NoIHbDoCDEkeG7mSBuaOG7jW4gcGjDom4gcGjhu5FpIG5o4buPICjiiYggMCkgeMOhYyBuaOG6rW4gxJHhurdjIHTDrW5oIGfhuqduIGNodeG6qW4uDQoNCi0gUmV2ZW51ZTogRG9hbmggdGh1IHRydW5nIGLDrG5oIG3hu5dpIGdpYW8gZOG7i2NoIMSR4bqhdCAxMyBVU0QsIG5oxrBuZyBjw7MgcGjGsMahbmcgc2FpIGzhu5tuIChzZCA9IDguMjIpIHbDoCBwaMOibiBwaOG7kWkgbOG7h2NoIHBo4bqjaSByw7UgcuG7h3QgKHNrZXcgPSAxLjEzKS4gxJBp4buBdSBuw6B5IGNobyB0aOG6pXkgxJFhIHPhu5EgY8OhYyBnaWFvIGThu4tjaCBjw7MgZG9hbmggdGh1IHRo4bqlcCwgdsOgIGNo4buJIG3hu5l0IHPhu5Egw610IG1hbmcgbOG6oWkgZ2nDoSB0cuG7iyBjYW8gYuG6pXQgdGjGsOG7nW5nIChrdXJ0b3NpcyA+IDEpLg0KDQotLS0NCg0KIyMjIDMuMi4gQmnhur9uIMSR4buLbmggdMOtbmgNCg0KQ8OhYyBiaeG6v24gxJHhu4tuaCB0w61uaCBz4bq9IMSRxrDhu6NjIGzhuqduIGzGsOG7o3QgdHLDrG5oIGLDoHkgduG7m2kgYuG6o25nIHThuqduIHPhu5EgdsOgIHThu7cgbOG7hy4gxJDDonkgbMOgIG5ow7NtIGPDoWMgYmnhur9uIGThuqFuZyBjaHXhu5dpIGhv4bq3YyDEkeG7i25oIGRhbmgsIGtow7RuZyB0aOG7gyDDoXAgZOG7pW5nIHBow6lwIHRvw6FuLg0KDQojIyMjIDMuMi4xIEdlbmRlciAoR2nhu5tpIHTDrW5oKQ0KDQoqKkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bu3IGzhu4cgJToqKg0KYGBge3J9DQpnZW5kZXJfdGFibGUgPC0gdGFibGUoZGF0YSRHZW5kZXIpDQpnZW5kZXJfcHJvcCA8LSBwcm9wLnRhYmxlKGdlbmRlcl90YWJsZSkNCg0KZ2VuZGVyX2RmIDwtIGRhdGEuZnJhbWUoDQogIEdp4bubaV90w61uaCA9IG5hbWVzKGdlbmRlcl90YWJsZSksDQogIGBT4buRIGzGsOG7o25nYCA9IGFzLnZlY3RvcihnZW5kZXJfdGFibGUpLA0KICBgVOG7tyBs4buHICglKWAgPSByb3VuZCgxMDAgKiBhcy52ZWN0b3IoZ2VuZGVyX3Byb3ApLCAyKSwNCiAgY2hlY2submFtZXMgPSBGQUxTRQ0KKQ0KDQprYWJsZShnZW5kZXJfZGYsIGFsaWduID0gImMiLCBjb2wubmFtZXMgPSBjKCJHaeG7m2kgdMOtbmgiLCAiU+G7kSBsxrDhu6NuZyIsICJU4bu3IGzhu4cgKCUpIikpICU+JQ0KICBrYWJsZUV4dHJhOjprYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgcG9zaXRpb24gPSAiY2VudGVyIiwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICByb3dfc3BlYygwLCBib2xkID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDE6Mywgd2lkdGggPSAiNGNtIikNCmBgYA0KKipCaeG7g3UgxJHhu5M6KioNCmBgYHtyfQ0KDQpkYXRhICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBHZW5kZXIpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiIzJFODZDMSIpICsNCiAgbGFicyh0aXRsZSA9ICJQaMOibiBi4buRIGdp4bubaSB0w61uaCIsIHggPSAiR2nhu5tpIHTDrW5oIiwgeSA9ICJT4buRIGzGsOG7o25nIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KKipOaOG6rW4geMOpdDoqKiBU4bu3IGzhu4cga2jDoWNoIGjDoG5nIG5hbSB2w6AgbuG7ryBn4bqnbiBuaMawIGPDom4gYuG6sW5nLCBjaG8gdGjhuqV5IHBow6JuIGLhu5EgZ2nhu5tpIHTDrW5oIHRyb25nIGThu68gbGnhu4d1IGzDoCDEkeG7k25nIMSR4buBdS4gxJBp4buBdSBuw6B5IHThuqFvIMSRaeG7gXUga2nhu4duIHRodeG6rW4gbOG7o2kgxJHhu4Mgc2nDqnUgdGjhu4sgdHJp4buDbiBraGFpIGPDoWMgY2hp4bq/biBsxrDhu6NjIG1hcmtldGluZyBtYW5nIHTDrW5oIHRvw6BuIGRp4buHbiwgaMaw4bubbmcgxJHhur9uIGPhuqMgaGFpIG5ow7NtIMSR4buRaSB0xrDhu6NuZyBtw6Aga2jDtG5nIGPhuqduIHBow6JuIGJp4buHdCByw7UgcuG7h3QgdGhlbyBnaeG7m2kuDQoNCi0tLQ0KDQojIyMjIDMuMi4yIE1hcml0YWxTdGF0dXMgKFTDrG5oIHRy4bqhbmcgaMO0biBuaMOibikNCg0KKipC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG7tyBs4buHICU6KioNCmBgYHtyfQ0KdGJsMiA8LSB0YWJsZShkYXRhJE1hcml0YWxTdGF0dXMpDQpwcm9wMiA8LSBwcm9wLnRhYmxlKHRibDIpDQoNCmRmMiA8LSBkYXRhLmZyYW1lKA0KICBNdWMgPSBuYW1lcyh0YmwyKSwNCiAgYFPhu5EgbMaw4bujbmdgID0gYXMudmVjdG9yKHRibDIpLA0KICBgVOG7tyBs4buHICglKWAgPSByb3VuZCgxMDAgKiBhcy52ZWN0b3IocHJvcDIpLCAyKSwNCiAgY2hlY2submFtZXMgPSBGQUxTRQ0KKQ0KDQpsaWJyYXJ5KGthYmxlRXh0cmEpDQprYWJsZShkZjIsIGFsaWduID0gImMiLCBjb2wubmFtZXMgPSBjKCJUw6xuaCB0cuG6oW5nIGjDtG4gbmjDom4iLCAiU+G7kSBsxrDhu6NuZyIsICJU4bu3IGzhu4cgKCUpIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgcG9zaXRpb24gPSAiY2VudGVyIiwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICByb3dfc3BlYygwLCBib2xkID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDE6Mywgd2lkdGggPSAiNGNtIikNCmBgYA0KKipCaeG7g3UgxJHhu5M6KioNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gTWFyaXRhbFN0YXR1cykpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICIjQ0E2RjFFIikgKw0KICBsYWJzKHRpdGxlID0gIlBow6JuIGLhu5EgdMOsbmggdHLhuqFuZyBow7RuIG5ow6JuIiwgeCA9ICJUw6xuaCB0cuG6oW5nIiwgeSA9ICJT4buRIGzGsOG7o25nIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCmBgYA0KKipOaOG6rW4geMOpdDoqKiBU4bu3IGzhu4cga2jDoWNoIGjDoG5nIMSR4buZYyB0aMOibiBjaGnhur9tIMawdSB0aOG6vyBuaOG6uSBzbyB24bubaSBuaMOzbSDEkcOjIGvhur90IGjDtG4sIGNobyB0aOG6pXkgxJHDonkgbMOgIG3hu5l0IHBow6JuIGtow7pjIHRp4buBbSBuxINuZyDigJMgxJHhuqFpIGRp4buHbiBjaG8gbmjDs20ga2jDoWNoIGjDoG5nIGPDsyB4dSBoxrDhu5tuZyB04buxIGNo4bunIHbhu4EgdMOgaSBjaMOtbmggdsOgIG5odSBj4bqndSBjaGkgdGnDqnUgbGluaCBob+G6oXQgaMahbi4gxJBp4buBdSBuw6B5IG3hu58gcmEgY8ahIGjhu5lpIGNobyBjw6FjIGNoaeG6v24gbMaw4bujYyBz4bqjbiBwaOG6qW0gdsOgIGtodXnhur9uIG3Do2kgbmjhuq9tIMSR4bq/biBjw6EgbmjDom4gaMahbiBsw6AgaOG7mSBnaWEgxJHDrG5oLg0KDQotLS0NCg0KIyMjIyAzLjIuMyBIb21lb3duZXIgKFPhu58gaOG7r3UgbmjDoCkNCg0KKipC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG7tyBs4buHJToqKg0KYGBge3J9DQp0YmwzIDwtIHRhYmxlKGRhdGEkSG9tZW93bmVyKQ0KcHJvcDMgPC0gcHJvcC50YWJsZSh0YmwzKQ0KDQpkZjMgPC0gZGF0YS5mcmFtZSgNCiAgTXVjID0gbmFtZXModGJsMyksDQogIGBT4buRIGzGsOG7o25nYCA9IGFzLnZlY3Rvcih0YmwzKSwNCiAgYFThu7cgbOG7hyAoJSlgID0gcm91bmQoMTAwICogYXMudmVjdG9yKHByb3AzKSwgMiksDQogIGNoZWNrLm5hbWVzID0gRkFMU0UNCikNCg0Ka2FibGUoZGYzLCBhbGlnbiA9ICJjIiwgY29sLm5hbWVzID0gYygiU+G7nyBo4buvdSBuaMOgIiwgIlPhu5EgbMaw4bujbmciLCAiVOG7tyBs4buHICglKSIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImNlbnRlciIsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKSAlPiUNCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUpICU+JQ0KICBjb2x1bW5fc3BlYygxOjMsIHdpZHRoID0gIjRjbSIpDQpgYGANCg0KKipCaeG7g3UgxJHhu5M6KioNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gSG9tZW93bmVyKSkgKw0KICBnZW9tX2JhcihmaWxsID0gIiMyOEI0NjMiKSArDQogIGxhYnModGl0bGUgPSAiVHLhuqFuZyB0aMOhaSBz4bufIGjhu691IG5ow6AiLCB4ID0gIlPhu58gaOG7r3UgbmjDoCIsIHkgPSAiU+G7kSBsxrDhu6NuZyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKiBLaG/huqNuZyA2MCUga2jDoWNoIGjDoG5nIHRyb25nIGThu68gbGnhu4d1IGzDoCBuZ8aw4budaSBz4bufIGjhu691IG5ow6AsIGNobyB0aOG6pXkgcGjhuqduIGzhu5tuIGtow6FjaCBow6BuZyB0aHXhu5ljIG5ow7NtIGPDsyBu4buBbiB04bqjbmcgdMOgaSBjaMOtbmgg4buVbiDEkeG7i25oLiDEkGnhu4F1IG7DoHkgY8OzIHRo4buDIGfhu6NpIMO9IHLhurFuZyBo4buNIGPDsyB4dSBoxrDhu5tuZyBjaGkgdGnDqnUgY2FvIGjGoW4gaG/hurdjIHPhurVuIHPDoG5nIMSR4bqndSB0xrAgdsOgbyBjw6FjIHPhuqNuIHBo4bqpbS9k4buLY2ggduG7pSBjw7MgZ2nDoSB0cuG7iyBs4bubbiBoxqFuIHNvIHbhu5tpIG5ow7NtIGNoxrBhIHPhu58gaOG7r3UgbmjDoC4gxJDDonkgbMOgIG3hu5l0IHnhur91IHThu5EgcXVhbiB0cuG7jW5nIMSR4buDIGPDom4gbmjhuq9jIHRyb25nIGPDoWMgY2hp4bq/biBsxrDhu6NjIMSR4buLbmggZ2nDoSB2w6AgcGjDom4ga2jDumMgdGjhu4sgdHLGsOG7nW5nLg0KDQotLS0NCg0KIyMjIyAzLjIuNCBUaHUgbmjhuq1wIGjDoG5nIG7Eg20gKEFubnVhbEluY29tZSkNCg0KKipC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG7tyBs4buHJToqKg0KYGBge3J9DQpkYXRhJEFubnVhbEluY29tZSA8LSB0cmltd3MoZ3N1YigiXFwkIiwgIiIsIGRhdGEkQW5udWFsSW5jb21lKSkNCg0KaW5jb21lX3RhYmxlIDwtIHRhYmxlKGRhdGEkQW5udWFsSW5jb21lKQ0KaW5jb21lX3Byb3AgPC0gcHJvcC50YWJsZShpbmNvbWVfdGFibGUpDQoNCmluY29tZV9kZiA8LSBkYXRhLmZyYW1lKA0KICBNdWMgPSBuYW1lcyhpbmNvbWVfdGFibGUpLA0KICBTb2x1b25nID0gYXMudmVjdG9yKGluY29tZV90YWJsZSksDQogIFRpbGUgPSByb3VuZCgxMDAgKiBhcy52ZWN0b3IoaW5jb21lX3Byb3ApLCAyKQ0KKQ0KDQprYWJsZShpbmNvbWVfZGYsIGNvbC5uYW1lcyA9IGMoIk3hu6ljIHRodSBuaOG6rXAiLCAiU+G7kSBsxrDhu6NuZyIsICJU4bu3IGzhu4cgKCUpIiksIGFsaWduID0gImMiKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImNlbnRlciIsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKSAlPiUNCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUpICU+JQ0KICBjb2x1bW5fc3BlYygxOjMsIHdpZHRoID0gIjRjbSIpDQpgYGANCg0KKipCaeG7g3UgxJHhu5M6KioNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gZmN0X2luZnJlcShBbm51YWxJbmNvbWUpKSkgKw0KICBnZW9tX2JhcihmaWxsID0gIiM3RDNDOTgiKSArDQogIGxhYnModGl0bGUgPSAiUGjDom4gYuG7kSB0aHUgbmjhuq1wIGjDoG5nIG7Eg20iLCB4ID0gIk3hu6ljIHRodSBuaOG6rXAiLCB5ID0gIlPhu5EgbMaw4bujbmciKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKiBQaOG6p24gbOG7m24ga2jDoWNoIGjDoG5nIChn4bqnbiA1MCUpIHThuq1wIHRydW5nIHRyb25nIG5ow7NtIHRodSBuaOG6rXAgdHJ1bmcgYsOsbmggdOG7qyAzMEsgxJHhur9uIDcwSywgY2hvIHRo4bqleSDEkcOieSBsw6AgcGjDom4ga2jDumMgY2jhu6cgxJHhuqFvIHbDoCBjw7MgdGnhu4FtIG7Eg25nIGNoaSB0acOqdSDhu5VuIMSR4buLbmguIMSQaeG7gXUgbsOgeSBn4bujaSDDvSBy4bqxbmcgc2nDqnUgdGjhu4sgbsOqbiB04bqtcCB0cnVuZyBwaMOhdCB0cmnhu4NuIGPDoWMgZMOybmcgc+G6o24gcGjhuqltIHBo4buVIHRow7RuZywgY8OzIG3hu6ljIGdpw6EgaOG7o3AgbMO9LCBjw7luZyB24bubaSBjaMawxqFuZyB0csOsbmgga2h1eeG6v24gbcOjaSB2w6AgxrB1IMSRw6NpIHBow7kgaOG7o3AgduG7m2kga2jhuqMgbsSDbmcgY2hpIHRpw6p1IGPhu6dhIG5ow7NtIG7DoHkuIELDqm4gY+G6oW5oIMSRw7MsIMSRw6J5IGPFqW5nIGzDoCBuaMOzbSBjw7MgdGjhu4MgZOG7hSBkw6BuZyBjaHV54buDbiDEkeG7lWkgdGjDoG5oIGtow6FjaCBow6BuZyB0aMOibiB0aGnhur90IHRow7RuZyBxdWEgY2jDrW5oIHPDoWNoIGNoxINtIHPDs2MgdsOgIHTDrWNoIMSRaeG7g20gbXVhIHPhuq9tIGhp4buHdSBxdeG6oy4NCg0KLS0tDQoNCiMjIyMgMy4yLjUgVGjDoG5oIHBo4buRIChDaXR5KQ0KDQoqKkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bu3IGzhu4clOioqDQpgYGB7cn0NCmNpdHlfdGFibGUgPC0gdGFibGUoZGF0YSRDaXR5KQ0KY2l0eV9wcm9wIDwtIHByb3AudGFibGUoY2l0eV90YWJsZSkNCg0KY2l0eV9kZiA8LSBkYXRhLmZyYW1lKA0KICBDaXR5ID0gbmFtZXMoY2l0eV90YWJsZSksDQogIFNvbHVvbmcgPSBhcy52ZWN0b3IoY2l0eV90YWJsZSksDQogIFRpbGUgPSByb3VuZCgxMDAgKiBhcy52ZWN0b3IoY2l0eV9wcm9wKSwgMikNCikNCg0Ka2FibGUoY2l0eV9kZiwgY29sLm5hbWVzID0gYygiVGjDoG5oIHBo4buRIiwgIlPhu5EgbMaw4bujbmciLCAiVOG7tyBs4buHICglKSIpLCBhbGlnbiA9ICJjIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJjZW50ZXIiLCBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkgJT4lDQogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFKSAlPiUNCiAgY29sdW1uX3NwZWMoMTozLCB3aWR0aCA9ICI0Y20iKQ0KYGBgDQoNCioqQmnhu4N1IMSR4buTOioqDQpgYGB7cn0NCmRhdGEgJT4lDQogIGdncGxvdChhZXMoeCA9IGZjdF9pbmZyZXEoQ2l0eSkpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiI0YzOUMxMiIpICsNCiAgbGFicyh0aXRsZSA9ICJQaMOibiBi4buRIHRoZW8gdGjDoG5oIHBo4buRIiwgeCA9ICJUaMOgbmggcGjhu5EiLCB5ID0gIlPhu5EgbMaw4bujbmciKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKg0KROG7ryBsaeG7h3UgY2hvIHRo4bqleSBTYWxlbSAoOS44NiUpLCBUYWNvbWEgKDguOTQlKSwgTG9zIEFuZ2VsZXMgKDYuNTklKSwgU2VhdHRsZSAoNi41NiUpLCB2w6AgUG9ydGxhbmQgKDYuMjMlKSBsw6Agbmjhu69uZyB0aMOgbmggcGjhu5EgY8OzIG3huq10IMSR4buZIGdpYW8gZOG7i2NoIGNhbyBuaOG6pXQsIGNobyB0aOG6pXkgxJHDonkgbMOgIGPDoWMga2h1IHbhu7FjIHRo4buLIHRyxrDhu51uZyB0cuG7jW5nIMSRaeG7g20gbcOgIHNpw6p1IHRo4buLIG7Dqm4gxrB1IHRpw6puIMSR4bqndSB0xrAgduG7gSBxdeG6o25nIGLDoSwgcGjDom4gcGjhu5FpIHbDoCBjaMSDbSBzw7NjIGtow6FjaCBow6BuZy4NCg0KTmdvw6BpIHJhLCBjw6FjIHRow6BuaCBwaOG7kSBuaMawIEJyZW1lcnRvbiwgU3Bva2FuZSwgU2FuIERpZWdvIHbDoCBIaWRhbGdvIGPFqW5nIGNoaeG6v20gdOG7tyB0cuG7jW5nIGtow6EgbOG7m24gKHRyw6puIDUlKSwgxJHDs25nIHZhaSB0csOyIGLhu5UgdHLhu6MgcXVhbiB0cuG7jW5nIHRyb25nIGNoaeG6v24gbMaw4bujYyBt4bufIHLhu5luZyB0aOG7iyB0csaw4budbmcuDQoNCk5nxrDhu6NjIGzhuqFpLCBjw6FjIHRow6BuaCBwaOG7kSBuaMawIEd1YWRhbGFqYXJhLCBWaWN0b3JpYSwgU2FuIEZyYW5jaXNjbyBjaOG7iSBjaGnhur9tIHThu7cgbOG7hyBkxrDhu5tpIDEuNSUsIG7Dqm4gY8OzIHRo4buDIMSRxrDhu6NjIHhlbSBsw6Aga2h1IHbhu7FjIHRp4buBbSBuxINuZyBkw6BpIGjhuqFuIGhv4bq3YyBkw7luZyDEkeG7gyB0aOG7rSBuZ2hp4buHbSBjw6FjIGNoaeG6v24gZOG7i2NoIG5o4buPIHRyxrDhu5tjIGtoaSBt4bufIHLhu5luZyBxdXkgbcO0Lg0KDQotLS0NCg0KIyMjIyAzLjIuNiBCYW5nL1Thu4luaCAoU3RhdGVvclByb3ZpbmNlKQ0KDQoqKkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bu3IGzhu4clOioqDQpgYGB7cn0NCnN0YXRlX3RhYmxlIDwtIHRhYmxlKGRhdGEkU3RhdGVvclByb3ZpbmNlKQ0Kc3RhdGVfcHJvcCA8LSBwcm9wLnRhYmxlKHN0YXRlX3RhYmxlKQ0Kc3RhdGVfZGYgPC0gZGF0YS5mcmFtZSgNCiAgU3RhdGUgPSBuYW1lcyhzdGF0ZV90YWJsZSksDQogIFNvbHVvbmcgPSBhcy52ZWN0b3Ioc3RhdGVfdGFibGUpLA0KICBUaWxlID0gcm91bmQoMTAwICogYXMudmVjdG9yKHN0YXRlX3Byb3ApLCAyKQ0KKQ0Ka2FibGUoc3RhdGVfZGYsIGNvbC5uYW1lcyA9IGMoIkJhbmcvVOG7iW5oIiwgIlPhu5EgbMaw4bujbmciLCAiVOG7tyBs4buHICglKSIpLCBhbGlnbiA9ICJjIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJjZW50ZXIiLCBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkgJT4lDQogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFKSAlPiUNCiAgY29sdW1uX3NwZWMoMTozLCB3aWR0aCA9ICI0Y20iKQ0KYGBgDQoNCioqQmnhu4N1IMSR4buTOioqDQpgYGB7cn0NCmRhdGEgJT4lDQogIGdncGxvdChhZXMoeCA9IGZjdF9pbmZyZXEoU3RhdGVvclByb3ZpbmNlKSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICIjNURBREUyIikgKw0KICBsYWJzKHRpdGxlID0gIlBow6JuIGLhu5EgdGhlbyBiYW5nL3Thu4luaCIsIHggPSAiQmFuZy9U4buJbmgiLCB5ID0gIlPhu5EgbMaw4bujbmciKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKiBCYSBiYW5nIFdhc2hpbmd0b24gKFdBIC0gMzIuNDglKSwgQ2FsaWZvcm5pYSAoQ0EgLSAxOS40NCUpIHbDoCBPcmVnb24gKE9SIC0gMTYuMDklKSBjaGnhur9tIHThu5VuZyBj4buZbmcgZ+G6p24gNzAlIHPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nLCBraOG6s25nIMSR4buLbmggxJHDonkgbMOgIGtodSB24buxYyB0cuG7jW5nIHTDom0gdHJvbmcgY2hp4bq/biBsxrDhu6NjIHRo4buLIHRyxrDhu51uZy4NCg0K4oaSIEPDoWMgaG/huqF0IMSR4buZbmcgbmjGsCBwaMOibiB0w61jaCBow6BuaCB2aSB0acOqdSBkw7luZywgdOG7kWkgxrB1IGjDs2EgZGFuaCBt4bulYyBz4bqjbiBwaOG6qW0sIHbDoCB0cmnhu4NuIGtoYWkgY2hp4bq/biBk4buLY2ggdGnhur9wIHRo4buLIG7Dqm4gxrB1IHRpw6puIHThuq1wIHRydW5nIHbDoG8gbmjDs20gYmFuZyBuw6B5IMSR4buDIMSR4bqhdCBoaeG7h3UgcXXhuqMgY2FvIG5o4bqldC4NCg0KQsOqbiBj4bqhbmggxJHDsywgWmFjYXRlY2FzICg5LjIzJSkgdsOgIERGICg1LjgwJSkgY8WpbmcgdGjhu4MgaGnhu4duIGzDoCBuaOG7r25nIHRo4buLIHRyxrDhu51uZyDEkcOhbmcgY2jDuiDDvSB24bubaSBxdXkgbcO0IHRydW5nIGLDrG5oLCBwaMO5IGjhu6NwIGNobyB2aeG7h2MgbeG7nyBy4buZbmcgZOG7i2NoIHbhu6UgaG/hurdjIHRo4butIG5naGnhu4dtIGNow61uaCBzw6FjaCBt4bubaS4gQ8OhYyBiYW5nIGPDsyB04bu3IHRy4buNbmcgdGjhuqVwIGjGoW4gbmjGsCBHdWVycmVybywgVmVyYWNydXosIFl1Y2F0YW4gaGF5IEphbGlzY28gY8OzIHRo4buDIMSRxrDhu6NjIHhlbSBsw6AgdGjhu4sgdHLGsOG7nW5nIHbhu4cgdGluaCwgdGnhu4FtIG7Eg25nIMSR4buDIHBow6F0IHRyaeG7g24gZMOgaSBo4bqhbiBob+G6t2MgdHJp4buDbiBraGFpIGPDoWMgY2hp4bq/biBk4buLY2gga2h1IHbhu7FjIG5o4buPLCBjw7MgY2jhu41uIGzhu41jLg0KDQotLS0NCg0KIyMjIyAzLjIuNyBRdeG7kWMgZ2lhIChDb3VudHJ5KQ0KDQoqKkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bu3IGzhu4clOioqDQpgYGB7cn0NCmNvdW50cnlfdGFibGUgPC0gdGFibGUoZGF0YSRDb3VudHJ5KQ0KY291bnRyeV9wcm9wIDwtIHByb3AudGFibGUoY291bnRyeV90YWJsZSkNCmNvdW50cnlfZGYgPC0gZGF0YS5mcmFtZSgNCiAgQ291bnRyeSA9IG5hbWVzKGNvdW50cnlfdGFibGUpLA0KICBTb2x1b25nID0gYXMudmVjdG9yKGNvdW50cnlfdGFibGUpLA0KICBUaWxlID0gcm91bmQoMTAwICogYXMudmVjdG9yKGNvdW50cnlfcHJvcCksIDIpDQopDQprYWJsZShjb3VudHJ5X2RmLCBjb2wubmFtZXMgPSBjKCJRdeG7kWMgZ2lhIiwgIlPhu5EgbMaw4bujbmciLCAiVOG7tyBs4buHICglKSIpLCBhbGlnbiA9ICJjIikgJT4lDQogIGthYmxlX3N0eWxpbmcoZnVsbF93aWR0aCA9IEZBTFNFLCBwb3NpdGlvbiA9ICJjZW50ZXIiLCBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkgJT4lDQogIHJvd19zcGVjKDAsIGJvbGQgPSBUUlVFKSAlPiUNCiAgY29sdW1uX3NwZWMoMTozLCB3aWR0aCA9ICI0Y20iKQ0KYGBgDQoNCioqQmnhu4N1IMSR4buTOioqDQpgYGB7cn0NCmRhdGEgJT4lDQogIGdncGxvdChhZXMoeCA9IENvdW50cnkpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiI0U3NEMzQyIpICsNCiAgbGFicyh0aXRsZSA9ICJQaMOibiBi4buRIHRoZW8gcXXhu5FjIGdpYSIsIHggPSAiUXXhu5FjIGdpYSIsIHkgPSAiU+G7kSBsxrDhu6NuZyIpICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKiBIb2EgS+G7syAoVVNBKSBjaGnhur9tIHThu7cgbOG7hyDDoXAgxJHhuqNvIHbhu5tpIDY4LjAxJSBz4buRIGtow6FjaCBow6BuZywga2jhurNuZyDEkeG7i25oIMSRw6J5IGzDoCB0aOG7iyB0csaw4budbmcgY+G7kXQgbMO1aSBj4bqnbiDEkcaw4bujYyDGsHUgdGnDqm4gxJHhuqd1IHTGsCB24buBIGjhuqEgdOG6p25nLCBk4buLY2ggduG7pSB2w6AgY2hp4bq/biBsxrDhu6NjIHRp4bq/cCB0aOG7iy4NCg0KTWV4aWNvIMSR4bupbmcgdGjhu6kgaGFpIHbhu5tpIDI2LjIzJSwgbMOgIG3hu5l0IHRo4buLIHRyxrDhu51uZyB0aeG7gW0gbsSDbmcgduG7m2kgcXV5IG3DtCDEkeG7pyBs4bubbiDEkeG7gyB0cmnhu4NuIGtoYWkgY2hp4bq/biBk4buLY2ggYuG6o24gxJHhu4thIGjDs2EsIG5oxrAgxJFp4buBdSBjaOG7iW5oIG5nw7RuIG5n4buvLCBnacOhIGPhuqMsIMawdSDEkcOjaSB2w6Agc+G6o24gcGjhuqltIHBow7kgaOG7o3AgdsSDbiBow7NhLg0KDQpDYW5hZGEgdHV5IGNo4buJIGNoaeG6v20gNS43NSUsIG5oxrBuZyB24bqrbiBsw6AgbeG7mXQgdGjhu4sgdHLGsOG7nW5nIHbhu4cgdGluaCDEkcOhbmcgY2jDuiDDvSwgcGjDuSBo4bujcCDEkeG7gyBkdXkgdHLDrCBoaeG7h24gZGnhu4duIHRoxrDGoW5nIGhp4buHdSB2w6AgdGjhu60gbmdoaeG7h20gc+G6o24gcGjhuqltL2Thu4tjaCB24bulIG3hu5tpIHRyxrDhu5tjIGtoaSBt4bufIHLhu5luZyBzw6J1IGjGoW4uDQoNCuKGkiBDaGnhur9uIGzGsOG7o2MgcGjDom4ga2jDumMgxJHhu4thIGzDvSByw7UgcsOgbmcgc+G6vSBnacO6cCBkb2FuaCBuZ2hp4buHcCB04buRaSDGsHUgaMOzYSBuZ3Xhu5NuIGzhu7FjIHbDoCBuw6JuZyBjYW8gaGnhu4d1IHF14bqjIHRp4bq/cCBj4bqtbiB04burbmcgdGjhu4sgdHLGsOG7nW5nLg0KDQotLS0NCg0KIyMjIyAzLjIuOCBOaMOzbSBz4bqjbiBwaOG6qW0gKFByb2R1Y3RGYW1pbHkpDQoNCioqQuG6o25nIHThuqduIHPhu5EgdsOgIHThu7cgbOG7hyU6KioNCmBgYHtyfQ0KdGJsIDwtIHRhYmxlKGRhdGEkUHJvZHVjdEZhbWlseSkNCnByb3AgPC0gcHJvcC50YWJsZSh0YmwpDQoNCmRmIDwtIGRhdGEuZnJhbWUoDQogIE11YyA9IG5hbWVzKHRibCksDQogIGBT4buRIGzGsOG7o25nYCA9IGFzLnZlY3Rvcih0YmwpLA0KICBgVOG7tyBs4buHICglKWAgPSByb3VuZCgxMDAgKiBhcy52ZWN0b3IocHJvcCksIDIpLA0KICBjaGVjay5uYW1lcyA9IEZBTFNFDQopDQoNCmthYmxlKGRmLCBhbGlnbiA9ICJjIiwgY29sLm5hbWVzID0gYygiTmjDs20gc+G6o24gcGjhuqltIiwgIlPhu5EgbMaw4bujbmciLCAiVOG7tyBs4buHICglKSIpKSAlPiUNCiAga2FibGVfc3R5bGluZyhmdWxsX3dpZHRoID0gRkFMU0UsIHBvc2l0aW9uID0gImNlbnRlciIsIGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKSAlPiUNCiAgcm93X3NwZWMoMCwgYm9sZCA9IFRSVUUpICU+JQ0KICBjb2x1bW5fc3BlYygxOjMsIHdpZHRoID0gIjRjbSIpDQpgYGANCg0KKipCaeG7g3UgxJHhu5M6KioNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUHJvZHVjdEZhbWlseSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICIjMUY2MThEIikgKw0KICBsYWJzKHRpdGxlID0gIlBow6JuIGLhu5EgbmjDs20gc+G6o24gcGjhuqltIiwgeCA9ICJOaMOzbSBz4bqjbiBwaOG6qW0iLCB5ID0gIlPhu5EgbMaw4bujbmciKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQ6KiogTmjDs20gc+G6o24gcGjhuqltIEZvb2QgY2hp4bq/bSB04bu3IHRy4buNbmcgw6FwIMSR4bqjbyB24bubaSBoxqFuIDcyJSB04buVbmcgc+G7kSBnaWFvIGThu4tjaCwga2jhurNuZyDEkeG7i25oIMSRw6J5IGzDoCBt4bqjbmcga2luaCBkb2FuaCBj4buRdCBsw7VpIGPhu6dhIHNpw6p1IHRo4buLIHbDoCBj4bqnbiDEkcaw4bujYyDGsHUgdGnDqm4gdHJvbmcgY2hp4bq/biBsxrDhu6NjIHPhuqNuIHBo4bqpbSwgdHLGsG5nIGLDoHkgdsOgIGtodXnhur9uIG3Do2kuIFRyb25nIGtoaSDEkcOzLCBjw6FjIG5ow7NtIE5vbi1Db25zdW1hYmxlICgxOC44OSUpIHbDoCBEcmluayAoOC44OSUpIHR1eSBjaGnhur9tIHThu7cgbOG7hyB0aOG6pXAgaMahbiBuaMawbmcgduG6q24gdGjhu4MgaGnhu4duIHRp4buBbSBuxINuZyBwaMOhdCB0cmnhu4NuLCDEkeG6t2MgYmnhu4d0IHRow7RuZyBxdWEgY8OhYyBjaGnhur9uIGThu4tjaCBiw6FuIGNow6lvIGhv4bq3YyBr4bq/dCBo4bujcCBjb21ibyDEkeG7gyB0xINuZyBnacOhIHRy4buLIMSRxqFuIGjDoG5nIHbDoCBt4bufIHLhu5luZyBow6BuaCB2aSB0acOqdSBkw7luZyBj4bunYSBraMOhY2guDQoNCi0tLQ0KDQojIyMjIDMuMi45IFBow7JuZyBiYW4gc+G6o24gcGjhuqltIChQcm9kdWN0RGVwYXJ0bWVudCkNCg0KKipC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG7tyBs4buHJToqKg0KYGBge3J9DQp0YmwgPC0gdGFibGUoZGF0YSRQcm9kdWN0RGVwYXJ0bWVudCkNCnByb3AgPC0gcHJvcC50YWJsZSh0YmwpDQoNCmRmIDwtIGRhdGEuZnJhbWUoDQogIE11YyA9IG5hbWVzKHRibCksDQogIGBT4buRIGzGsOG7o25nYCA9IGFzLnZlY3Rvcih0YmwpLA0KICBgVOG7tyBs4buHICglKWAgPSByb3VuZCgxMDAgKiBhcy52ZWN0b3IocHJvcCksIDIpLA0KICBjaGVjay5uYW1lcyA9IEZBTFNFDQopDQoNCmthYmxlKGRmLCBhbGlnbiA9ICJjIiwgY29sLm5hbWVzID0gYygiQuG7mSBwaOG6rW4iLCAiU+G7kSBsxrDhu6NuZyIsICJU4bu3IGzhu4cgKCUpIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgcG9zaXRpb24gPSAiY2VudGVyIiwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICByb3dfc3BlYygwLCBib2xkID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDE6Mywgd2lkdGggPSAiNGNtIikNCmBgYA0KDQoqKkJp4buDdSDEkeG7kzoqKg0KYGBge3J9DQpkYXRhICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBmY3RfaW5mcmVxKFByb2R1Y3REZXBhcnRtZW50KSkpICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICIjRDM1NDAwIikgKw0KICBsYWJzKHRpdGxlID0gIlBow7JuZyBiYW4gc+G6o24gcGjhuqltIiwgeCA9ICJQaMOybmcgYmFuIiwgeSA9ICJT4buRIGzGsOG7o25nIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQ6KiogUmF1IHF14bqjIChQcm9kdWNlKSwgU25hY2sgRm9vZHMgdsOgIEhvdXNlaG9sZCBsw6AgYmEgYuG7mSBwaOG6rW4gc+G6o24gcGjhuqltIGNoaeG6v20gdOG7tyB0cuG7jW5nIGzhu5tuIG5o4bqldCB0cm9uZyB04buVbmcgZG9hbmggc+G7kSwgbOG6p24gbMaw4bujdCDEkeG6oXQgMTQsMTglLCAxMSwzOCUgdsOgIDEwLDEwJS4gxJBp4buBdSBuw6B5IGNobyB0aOG6pXkgc+G7qWMgbXVhIG3huqFuaCBt4bq9IHbDoCBz4buxIHF1YW4gdMOibSBs4bubbiBj4bunYSBraMOhY2ggaMOgbmcgxJHhu5FpIHbhu5tpIG5o4buvbmcgbmjDs20gaMOgbmcgbsOgeS4gRG8gduG6rXksIHZp4buHYyB04bqtcCB0cnVuZyDEkeG6p3UgdMawIHbDoG8gdHLGsG5nIGLDoHkgYuG6r3QgbeG6r3QsIHRyaeG7g24ga2hhaSBjw6FjIGNoxrDGoW5nIHRyw6xuaCBraHV54bq/biBtw6NpIGjhuqVwIGThuqtuIHbDoCBwaMOhdCB0cmnhu4NuIMSRYSBk4bqhbmcgc+G6o24gcGjhuqltIHRyb25nIGPDoWMgbmjDs20gbsOgeSBz4bq9IGzDoCBjaGnhur9uIGzGsOG7o2MgdGhlbiBjaOG7kXQgZ2nDunAgdMSDbmcgdHLGsOG7n25nIGRvYW5oIHRodSB2w6AgY+G7p25nIGPhu5EgduG7iyB0aOG6vyBj4bqhbmggdHJhbmggdHLDqm4gdGjhu4sgdHLGsOG7nW5nLg0KDQotLS0NCg0KIyMjIyAzLjIuMTAgRGFuaCBt4bulYyBz4bqjbiBwaOG6qW0gKFByb2R1Y3RDYXRlZ29yeSkNCg0KKipC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG7tyBs4buHJToqKg0KYGBge3J9DQp0YmwgPC0gdGFibGUoZGF0YSRQcm9kdWN0Q2F0ZWdvcnkpDQpwcm9wIDwtIHByb3AudGFibGUodGJsKQ0KDQpkZiA8LSBkYXRhLmZyYW1lKA0KICBNdWMgPSBuYW1lcyh0YmwpLA0KICBgU+G7kSBsxrDhu6NuZ2AgPSBhcy52ZWN0b3IodGJsKSwNCiAgYFThu7cgbOG7hyAoJSlgID0gcm91bmQoMTAwICogYXMudmVjdG9yKHByb3ApLCAyKSwNCiAgY2hlY2submFtZXMgPSBGQUxTRQ0KKQ0KDQprYWJsZShkZiwgYWxpZ24gPSAiYyIsIGNvbC5uYW1lcyA9IGMoIkRhbmggbeG7pWMiLCAiU+G7kSBsxrDhu6NuZyIsICJU4bu3IGzhu4cgKCUpIikpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGQUxTRSwgcG9zaXRpb24gPSAiY2VudGVyIiwgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICByb3dfc3BlYygwLCBib2xkID0gVFJVRSkgJT4lDQogIGNvbHVtbl9zcGVjKDE6Mywgd2lkdGggPSAiNGNtIikNCmBgYA0KDQoqKkJp4buDdSDEkeG7kzoqKg0KYGBge3J9DQpkYXRhICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBmY3RfaW5mcmVxKFByb2R1Y3RDYXRlZ29yeSkpKSArDQogIGdlb21fYmFyKGZpbGwgPSAiIzQ1QjM5RCIpICsNCiAgbGFicyh0aXRsZSA9ICJEYW5oIG3hu6VjIHPhuqNuIHBo4bqpbSBj4bulIHRo4buDIiwgeCA9ICJEYW5oIG3hu6VjIiwgeSA9ICJT4buRIGzGsOG7o25nIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQ6KiogRGFuaCBt4bulYyBWZWdldGFibGVzIHbDoCBTbmFjayBGb29kcyDEkeG7qW5nIMSR4bqndSB24buBIHPhu5EgbMaw4bujbmcgZ2lhbyBk4buLY2gsIGzhuqduIGzGsOG7o3QgY2hp4bq/bSAxMiwyOSUgdsOgIDExLDM4JSB04buVbmcgc+G7kS4gxJBp4buBdSBuw6B5IG1pbmggY2jhu6luZyBjaG8gc+G7sSDGsHUgdGnDqm4gcsO1IHLDoG5nIGPhu6dhIGtow6FjaCBow6BuZyDEkeG7kWkgduG7m2kgY8OhYyBz4bqjbiBwaOG6qW0gdGjhu7FjIHBo4bqpbSB0xrDGoWkgc+G7kW5nIHbDoCDEkeG7kyDEg24gbmhhbmggdGnhu4duIGzhu6NpIHRyb25nIHRow7NpIHF1ZW4gbXVhIHPhuq9tIGjDoG5nIG5nw6B5LiBWw6wgduG6rXksIGRvYW5oIG5naGnhu4dwIG7Dqm4gdGnhur9wIHThu6VjIGPhu6duZyBj4buRIHbDoCBwaMOhdCB0cmnhu4NuIG3huqFuaCBt4bq9IGhhaSBuaMOzbSBow6BuZyBuw6B5IHRow7RuZyBxdWEgdmnhu4djIG7Dom5nIGNhbyBjaOG6pXQgbMaw4bujbmcsIMSRYSBk4bqhbmcgaMOzYSBz4bqjbiBwaOG6qW0gdsOgIHjDonkgZOG7sW5nIGPDoWMgY2jGsMahbmcgdHLDrG5oIGtodXnhur9uIG3Do2kgc8OhbmcgdOG6oW8gbmjhurFtIGdp4buvIHbhu69uZyBsw7JuZyB0cnVuZyB0aMOgbmggY+G7p2Ega2jDoWNoIGjDoG5nIHbDoCB0xINuZyB0csaw4bufbmcgYuG7gW4gduG7r25nLg0K