1 PHẦN 1: TÓM TẮT SÁCH

1.1 GIỚI THIỆU CHUNG

Cuốn sách Generalized Linear Models With Examples in R do Peter K. Dunn và Gordon K. Smyth đồng tác giả, là một phần của sê-ri Springer Texts in Statistics. Sách được chuẩn bị bằng ngôn ngữ LaTeX và sử dụng phiên bản R 3.4.3 trong toàn bộ ví dụ và phân tích. Peter K. Dunn là giảng viên tại Khoa Khoa học, Sức khỏe, Giáo dục và Kỹ thuật thuộc Đại học Sunshine Coast (Úc), trong khi Gordon K. Smyth công tác tại Ban Tin sinh học của Viện Nghiên cứu Y khoa Walter và Eliza Hall (Úc).

1.2 MỤC TIÊU VÀ ĐỐI TƯỢNG ĐỘC GIẢ

Mục tiêu chính của cuốn sách là cung cấp một cách tiếp cận toàn diện đối với Mô hình Tuyến tính Tổng quát (GLMs), kết hợp giữa lý thuyết sâu sắc và ứng dụng thực tiễn thông qua phân mềm thống kê R. Tác giả mong muốn người học không chỉ hiểu được mặt kỹ thuật mà còn nắm rõ nội dung đằng sau mô hình trong phân tích dữ liệu.

Sách giả định độc giả có nền tảng cơ bản về thống kê, giả thiết và kiểm định giả thuyết. Tuy nhiên, sách vẫn cung cấp phần giới thiệu đầy đủ cho người đọc bắt đầu như hồi quy tuyến tính, giúp độc giả dễ dàng tiếp cận mô hình cho khoa học thống kê hiện đại và ứng dụng.

Ngoài ra, sách còn mở rộng phạm vi bằng cách đưa ra hướng dẫn mô hình nâng cao và xây dựng mô hình toàn diện gồm các bước từ chuẩn bị dữ liệu, khám phá dữ liệu, xây dựng mô hình, kiểm định giả thiết, và cả cách nhìn nhận kết quả từ góc độ thực tiễn phân tích trong nghiên cứu và ứng dụng thực tế.

1.3 NGÔN NGỮ VÀ CÔNG CỤ SỬ DỤNG

Ngôn ngữ lập trình R được sử dụng xuyên suốt cuốn sách, không chỉ như một công cụ tính toán mà còn là phương tiện để minh họa lý thuyết bằng thực hành. Cuốn sách bao gồm phần giới thiệu độc lập về R (Phụ lục A), cùng với các đoạn mã hoàn chỉnh kèm hướng dẫn cụ thể trong từng chương. Điều này giúp người đọc áp dụng trực tiếp các mô hình thống kê vào dữ liệu thực tế một cách linh hoạt và hiệu quả.

1.4 NỘI DUNG CHÍNH CỦA SÁCH

1.4.1 Chương 1: Mô hình thống kê

Chương 1 giới thiệu tổng quan về mô hình thống kê, tập trung đặc biệt vào các mô hình hồi quy. Nội dung chính bao gồm:

  • Khái quát về mô hình hồi quy và cách mô tả dữ liệu qua các quy ước chuẩn.

  • Sử dụng biểu đồ để khám phá và đánh giá mối quan hệ giữa các biến, ví dụ như biểu đồ FEV (dung tích thở ra gắng sức) với tuổi và chiều cao, giúp xác định tính hợp lý của mối quan hệ tuyến tính.

  • Giới thiệu cách mã hóa biến phân loại thành biến giả (dummy variables), đặc biệt là mã hóa điều trị (treatment coding) để giải thích hệ số dễ dàng hơn.

  • Phân tích cấu trúc của mô hình thống kê, bao gồm thành phần ngẫu nhiên và thành phần hệ thống.

  • Định nghĩa và ký hiệu trong mô hình hồi quy tuyến tính theo tham số, giải thích các tham số hồi quy và vai trò của biến hằng số (intercept).

  • Triết lý về mô hình thống kê: “Tất cả mô hình đều sai nhưng một số hữu ích.”

  • Thảo luận về mục đích xây dựng mô hình, cân bằng giữa độ chính xác và sự đơn giản, cũng như phân biệt giữa nghiên cứu thực nghiệm và quan sát.

  • Giới thiệu sơ lược về phần mềm R trong xây dựng và phân tích mô hình thống kê.

1.4.2 Chương 2: Mô hình hồi quy tuyến tính

Chương 2 tập trung phân tích chi tiết mô hình hồi quy tuyến tính, bao gồm:

  • Định nghĩa mô hình hồi quy tuyến tính với thành phần hệ thống là hàm tuyến tính của các biến giải thích và thành phần ngẫu nhiên giả định phân phối chuẩn với phương sai không đổi.

  • Phương pháp ước lượng tham số hồi quy𝛽, trong đó ước lượng 𝛽^là không chệch và phương sai của nó được xác định qua ma trận thiết kế.

  • Ước lượng giá trị trung bình của biến đáp ứng tại các điểm cụ thể, kèm theo tính sai số chuẩn.

  • Đánh giá mô hình thông qua Phân tích Phương sai (ANOVA), phân chia tổng phương sai thành phần do mô hình giải thích và phần dư, dùng thống kê F để kiểm định ý nghĩa tổng thể của mô hình.

  • So sánh các mô hình lồng nhau bằng ANOVA, với nguyên tắc giữ số hạng chính khi có số hạng tương tác.

  • Diễn giải ý nghĩa các hệ số hồi quy, đặc biệt với biến phân loại, và ảnh hưởng của biến giải thích khác hoặc biến đổi biến đáp ứng đến cách hiểu hệ số.

1.4.3 Chương 3: Chẩn đoán và xây dựng mô hình tuyến tínhH

Chương 3 tập trung vào đánh giá (chẩn đoán) mô hình hồi quy tuyến tính và các kỹ thuật để cải thiện mô hình, gồm:

  • Chẩn đoán mô hình qua biểu đồ phần dư, bao gồm phần dư chuẩn hóa và phần dư deviance, dùng để kiểm tra giả định phương sai không đổi.
  • Biểu đồ Q-Q chuẩn để kiểm tra giả định phân phối chuẩn của phần dư.
  • Khoảng cách Cook để xác định điểm có ảnh hưởng lớn trong mô hình.
  • Kiểm tra tự tương quan phần dư qua biểu đồ phần dư so với phần dư liền trước.
  • Biểu đồ phần dư so với biến giải thích, ví dụ biểu đồ “Partial for Ht”.
  • Xây dựng mô hình bằng cách áp dụng phép biến đổi (log, căn bậc hai) cho biến đáp ứng hoặc biến giải thích, đánh giá qua biểu đồ phần dư.
  • Thêm số hạng đa thức để mô hình hóa quan hệ phi tuyến tính giữa biến giải thích và biến đáp ứng.
  • Sử dụng hàm spline hồi quy (natural cubic splines, B-splines) để mô hình hóa các quan hệ phi tuyến phức tạp hơn.
  • Xem xét thêm số hạng tương tác giữa các biến giải thích, tuân theo nguyên lý biên (marginality principle).
  • So sánh các mô hình bằng kiểm định t-test, F-test, bảng ANOVA và tiêu chí AIC.
  • Diễn giải ý nghĩa các hệ số hồi quy trong bối cảnh các biến khác và biến đổi biến đáp ứng.
  • Ví dụ minh họa sử dụng nhiều bộ dữ liệu đa dạng như dữ liệu lungcap, naval hospital, ruminant, Australian cities, và nhiều hơn nữa.

1.4.4 Chương 4: Vượt ra ngoài hồi quy tuyến tính: Phương pháp ước lượng hợp lý cực đạI

Chương 4 giới thiệu phương pháp ước lượng hợp lý cực đại (MLE) như một cách tiếp cận tổng quát cho các mô hình không phù hợp với hồi quy tuyến tính chuẩn:

  • Trình bày lý do cần vượt ra ngoài mô hình hồi quy tuyến tính khi biến đáp ứng không thỏa mãn giả định (ví dụ: biến tỷ lệ, biến đếm, biến dương liên tục).
  • Giới thiệu gia đình phân phối (family of distributions) làm cơ sở mô hình hóa dữ liệu.
  • Phương pháp MLE chọn tham số để cực đại hóa hàm hợp lý (likelihood), hoặc log-hợp lý.
  • Ước lượng tham số bằng MLE dựa trên giải phương trình điểm số (score function) và sử dụng thông tin Fisher để đánh giá sai số chuẩn.
  • Thuật toán Fisher Scoring được sử dụng để tính toán MLE hiệu quả.
  • Tính chất của MLE gồm không chệch tiệm cận, hiệu quả, nhất quán, phân phối chuẩn tiệm cận, và bất biến.
  • Ba kiểm định giả thuyết dựa trên MLE: kiểm định Wald, kiểm định điểm số, và kiểm định tỷ lệ hợp lý (LRT).
  • Xây dựng khoảng tin cậy dựa trên các kiểm định này.
  • So sánh các mô hình không lồng nhau bằng tiêu chí AIC và BIC để chọn mô hình tốt nhất.

1.4.5 Chương 5: Mô hình tuyến tính tổng quát (GLMs): Cấu trúc

  • Giới thiệu mô hình tuyến tính tổng quát (GLMs) như mở rộng của hồi quy tuyến tính.

  • GLMs khác hồi quy tuyến tính ở chỗ không chỉ giả định phương sai không đổi mà còn cho phép thành phần ngẫu nhiên từ họ phân phối tổng quát hơn (họ phân phối mũ - EDM).

  • GLMs gồm 2 thành phần chính:

    • Thành phần ngẫu nhiên: biến phản hồi theo phân phối thuộc họ EDM (Normal, Poisson, Gamma, Binomial, Negative Binomial…).
    • Thành phần hệ thống: mối liên hệ giữa biến giải thích và trung bình biến phản hồi \(\mu\) thông qua hàm liên kết \(g()\), với bộ dự báo tuyến tính \(\eta = \beta_0 + \sum \beta_j x_j\).
  • Hàm xác suất tổng quát của EDM: \(P(y; \theta, \phi) = a(y, \phi) \exp\left\{\frac{y\theta - \kappa(\theta)}{\phi}\right\}\), trong đó \(\phi\) là tham số phân tán.

    • Với Poisson và Binomial, \(\phi\) đã biết (thường là 1).
    • Với Normal, Gamma, Negative Binomial, \(\phi\) cần ước lượng.
  • Tham số offset (biến biết trước, không ước lượng) có thể xuất hiện trong thành phần hệ thống.

  • Cấu trúc GLM được ký hiệu: glm(edm; link function).

  • Độ lệch tổng (Total Deviance) \(D(y, \mu) = \sum w_i d(y_i, \mu_i)\), dùng để đo sai khác giữa dữ liệu và mô hình.

    • Với mô hình tuyến tính chuẩn, độ lệch là tổng bình phương sai khác.
    • \(D(y, \mu)/\phi\) xấp xỉ phân phối \(\chi^2\) khi \(\phi \to 0\).
    • Độ lệch đơn vị có phân phối chi-bình phương chính xác cho một số phân phối (Normal, Inverse Gaussian) và xấp xỉ cho các phân phối khác.

1.4.6 Chương 6: Mô hình tuyến tính tổng quát (GLMs): Ước lượng

  • Tập trung ước lượng tham số hồi quy (\(\beta\)) và tham số phân tán (\(\phi\)) trong GLMs.

  • Dựa trên giả định phân phối EDM, sử dụng phương pháp ước lượng hợp lý cực đại (MLE).

  • Phương trình điểm (Score equations) và thông tin Fisher được phát triển để xây dựng thuật toán ước lượng.

  • Độ lệch dư (Residual Deviance) dùng để đo biến thiên chưa được giải thích sau khi fitted mô hình.

  • Sai số chuẩn (Standard errors) của tham số được tính toán qua công thức ma trận.

  • Thuật toán ước lượng GLM tương tự hồi quy tuyến tính với tính chất cục bộ.

  • Ước lượng tham số phân tán \(\phi\) có nhiều cách: MLE, log-likelihood biên sửa đổi, trung bình độ lệch, Pearson.

  • Ứng dụng R:

    • Hàm glm() dùng để fitted GLMs.
    • Tham số family xác định họ phân phối EDM (gaussian, binomial, poisson, Gamma, inverse.gaussian).
    • Các họ “quasi” (quasi, quasibinomial, quasipoisson) dùng cho trường hợp phương sai thay đổi hoặc quá phân tán (overdispersion).
    • Hàm tweedie() trong gói statmod cho phép fitted GLMs Tweedie.
    • Các hàm liên kết mặc định tương ứng với từng họ phân phối (logit, log, identity, inverse).

1.4.7 Chương 7: Mô hình tuyến tính tổng quát (GLMs): Suy luận

  • Áp dụng ba phương pháp suy luận trên lý thuyết hợp lý cực đại: Wald, Score, Likelihood Ratio Test (LRT) trong GLMs.

  • Khi tham số phân tán φ đã biết:

    • Sử dụng kiểm định Wald, khoảng tin cậy cho từng hệ số và giá trị trung bình μ.
    • LRT so sánh các mô hình lồng nhau với thống kê phân phối χ².
    • Bảng phân tích độ lệch (Analysis of Deviance) hỗ trợ so sánh mô hình.
    • Kiểm định Score cũng được sử dụng.
  • Kết quả phân phối các thống kê dựa trên xấp xỉ mẫu lớn (Large Sample Asymptotics).

  • Kiểm định độ phù hợp (Goodness-of-Fit Tests) để kiểm tra bộ dự báo tuyến tính mô tả đầy đủ xu hướng dữ liệu không, dựa trên phân phối phân tán nhỏ (Small Dispersion Asymptotics).

  • Khi tham số phân tán φ chưa biết:

    • Các kiểm định Wald và khoảng tin cậy vẫn được dùng.
    • LRT so sánh mô hình dùng thống kê F-test.
    • Bảng phân tích độ lệch cũng dựa trên F-test.
  • So sánh 3 bài kiểm tra Wald, ScoreLRT.

  • Lựa chọn giữa các GLMs không lồng nhau dùng AICBIC.

  • Các phương pháp tự động lựa chọn mô hình được giới thiệu.

  • Ứng dụng trong R:

    • summary() để xem kết quả kiểm định Wald.
    • glm.scoretest() (gói statmod) cho kiểm định Score.
    • anova() để so sánh các mô hình lồng nhau bằng quasi-likelihood, cho kết quả F-tests khi φ được ước lượng.

1.4.8 Chương 8: Mô hình tuyến tính tổng quát (GLMs): Chẩn đoán

  • Chương này thảo luận các phương pháp để đánh giá tính hợp lệ của các giả định trong GLMs.

  • Các giả định chính của GLMs bao gồm:

    • Sự phù hợp của mô hình tổng thể.
    • Biến phản hồi đến từ họ phân phối EDM được chỉ định.
    • Tính đúng đắn của bộ dự báo tuyến tính (Linear Predictor).
    • Phương sai không đổi (Constant Variance).
    • Tính độc lập (Independence).
    • Tính chuẩn (Normality) — chỉ áp dụng cho GLM chuẩn.
    • Thang đo lường (Measurement Scales).
  • Phần dư (Residuals) cho GLMs:

    • Phần dư phản hồi (Response Residuals) không đủ để chẩn đoán trong GLMs.

    • Các loại phần dư hữu ích hơn:

      • Pearson Residuals.
      • Deviance Residuals.
      • Quantile Residuals (còn gọi là Randomized Quantile Residuals).
  • Đòn bẩy (Leverages) trong GLMs:

    • Là thước đo mức độ ảnh hưởng của từng quan sát lên fitted model.
    • Khái niệm working leverages được sử dụng.
    • Hat matrix cũng được thảo luận.
  • Phần dư chuẩn hóa theo đòn bẩy (Leverage Standardized Residuals) cho GLMs được trình bày.

  • Hướng dẫn lựa chọn loại phần dư phù hợp để sử dụng trong chẩn đoán.

  • Việc kiểm tra các giả định mô hình được thực hiện bằng cách sử dụng các công cụ chẩn đoán này, thường qua các biểu đồ phần dư.

  • Các mô hình Quasi-Poisson (quasipoisson()) và Quasi-binomial (quasibinomial()) được đề cập lại như lựa chọn cho dữ liệu quá phân tán.

  • Suy luận cho các mô hình quasi này sử dụng các hàm R tương tự GLMs thông thường (summary(), glm.scoretest(), anova()), nhưng với F-tests dựa trên độ lệch.

1.4.9 Chương 9: Mô hình tỷ lệ – Binomial GLMs

  • Tập trung vào GLM nhị thức dùng để mô hình hóa tỷ lệ (số thành công trên tổng số thử nghiệm).

  • Phân phối Binomial thuộc họ phân phối mũ (EDM).

  • Các hàm liên kết phổ biến:

    • logit (hàm chính tắc),
    • probit,
    • complementary log-log,
    • cauchit.
  • Diễn giải ý nghĩa ngưỡng của hàm liên kết.

  • Giải thích kết quả qua odds và odds ratios với hàm logit.

  • Ứng dụng trong ước lượng liều hiệu quả trung bình ED50.

  • Vấn đề quá phân tán và xử lý bằng quasi-binomial models.

  • Cảnh báo về Hauck-Donner effect khi dùng Wald tests.

  • Kiểm định Goodness-of-Fit không thích hợp với dữ liệu nhị phân.

  • Sử dụng R qua glm(family = binomial()).

1.4.10 Chương 10: Mô hình cho dữ liệu đếm – Poisson & Negative Binomial

  • GLMs cho dữ liệu đếm, chủ yếu sử dụng phân phối Poisson (thuộc EDM).
  • Hàm xác suất Poisson với trung bình μ > 0, tham số phân tán φ = 1, phương sai V(μ) = μ.
  • Hàm liên kết log là hàm chính tắc.
  • Mô hình Poisson GLMs và cách khai triển trong R (family = poisson()).
  • Mô hình tỷ lệ (rates) sử dụng offset là log(exposure).
  • Mô hình log-linear cho bảng tần suất với biến định tính.
  • Bảng tần suất đa chiều, hiệu ứng chính và tương tác, nghịch lý Simpson.
  • Vấn đề zero inflationzero truncation trong dữ liệu đếm.
  • Xử lý quá phân tán bằng Negative Binomial GLMsquasi-Poisson models.

1.4.11 Chương 11: Mô hình GLM với Dữ liệu Liên tục Dương – Phân phối Gamma

  • Giới thiệu các mô hình cho dữ liệu liên tục dương, thường là đo lường các đại lượng vật lý luôn dương.

  • Hai GLMs phổ biến: dựa trên phân phối GammaInverse Gaussian thuộc họ EDM.

  • Phân phối Gamma:

    • Hàm xác suất,
    • Các trường hợp đặc biệt,
    • Ước lượng tham số phân tán φ.
  • Phân phối Inverse Gaussian:

    • Thuộc EDM,
    • Độ lệch đơn vị phân phối chi-bình phương chính xác,
    • Ước lượng φ.
  • Các hàm liên kết sử dụng: inverse, identity, log.

  • Hàm liên kết chính tắc của Inverse Gaussian là 1/μ².

  • Sử dụng R để fit mô hình:

    • glm(family = Gamma())glm(family = inverse.gaussian()).
  • Liệt kê các hàm liên kết có thể dùng cho từng phân phối.

  • Cung cấp ví dụ bảng phân tích độ lệch từ mô hình Gamma GLM.

1.4.12 Chương 12: Mô hình Tweedie

  • Giới thiệu GLMs dựa trên họ phân phối Tweedie (EDMs tổng quát hơn).

  • Tweedie bao gồm các phân phối đặc biệt như:

    • Chuẩn (ξ = 0),
    • Poisson (ξ = 1),
    • Gamma (ξ = 2),
    • Inverse Gaussian (ξ = 3).
  • Hàm phương sai của Tweedie: \(V(\mu) = \mu^\xi\) với ξ là tham số chỉ số (index parameter), không thuộc khoảng (0,1).

  • Ứng dụng Tweedie trong dữ liệu liên tục dương và dữ liệu liên tục dương có giá trị 0 chính xác.

  • Ước lượng tham số ξ và cách fit mô hình Tweedie GLMs.

  • Nghiên cứu trường hợp minh họa diễn giải các phân phối Poisson và Gamma qua hàm tweedie.convert() trong R (gói tweedie).

  • Sử dụng R với hàm tweedie() trong gói statmod để fit Tweedie GLMs.

  • Trình bày độ lệch đơn vị và độ lệch dư phù hợp với phân phối χ².


2 THỐNG KÊ MÔ TẢ DỮ LIỆU

2.1 Giới thiệu bộ dữ liệu

Tải dữ liệu

library("csv")
## Warning: package 'csv' was built under R version 4.3.3
library(data.table)
## Warning: package 'data.table' was built under R version 4.3.3
data <- read.csv("D:/Downloads/Supermarket Transactions.csv", header = T)
data.table(data)
##            X PurchaseDate CustomerID Gender MaritalStatus Homeowner Children
##        <int>       <char>      <int> <char>        <char>    <char>    <int>
##     1:     1   2007-12-18       7223      F             S         Y        2
##     2:     2   2007-12-20       7841      M             M         Y        5
##     3:     3   2007-12-21       8374      F             M         N        2
##     4:     4   2007-12-21       9619      M             M         Y        3
##     5:     5   2007-12-22       1900      F             S         Y        3
##    ---                                                                      
## 14055: 14055   2009-12-29       9102      F             M         Y        2
## 14056: 14056   2009-12-29       4822      F             M         Y        3
## 14057: 14057   2009-12-31        250      M             S         Y        1
## 14058: 14058   2009-12-31       6153      F             S         N        4
## 14059: 14059   2009-12-31       3656      M             S         N        3
##         AnnualIncome          City StateorProvince Country  ProductFamily
##               <char>        <char>          <char>  <char>         <char>
##     1:   $30K - $50K   Los Angeles              CA     USA           Food
##     2:   $70K - $90K   Los Angeles              CA     USA           Food
##     3:   $50K - $70K     Bremerton              WA     USA           Food
##     4:   $30K - $50K      Portland              OR     USA           Food
##     5: $130K - $150K Beverly Hills              CA     USA          Drink
##    ---                                                                   
## 14055:   $10K - $30K     Bremerton              WA     USA           Food
## 14056:   $10K - $30K   Walla Walla              WA     USA           Food
## 14057:   $30K - $50K      Portland              OR     USA          Drink
## 14058:   $50K - $70K       Spokane              WA     USA          Drink
## 14059:   $50K - $70K      Portland              OR     USA Non-Consumable
##        ProductDepartment      ProductCategory UnitsSold Revenue
##                   <char>               <char>     <int>   <num>
##     1:       Snack Foods          Snack Foods         5   27.38
##     2:           Produce           Vegetables         5   14.90
##     3:       Snack Foods          Snack Foods         3    5.52
##     4:            Snacks                Candy         4    4.44
##     5:         Beverages Carbonated Beverages         4   14.00
##    ---                                                         
## 14055:      Baking Goods         Baking Goods         3    9.64
## 14056:      Frozen Foods           Vegetables         3    7.45
## 14057:         Beverages Pure Juice Beverages         4    3.24
## 14058:             Dairy                Dairy         2    4.00
## 14059:         Household           Electrical         5   25.53
names(data)
##  [1] "X"                 "PurchaseDate"      "CustomerID"       
##  [4] "Gender"            "MaritalStatus"     "Homeowner"        
##  [7] "Children"          "AnnualIncome"      "City"             
## [10] "StateorProvince"   "Country"           "ProductFamily"    
## [13] "ProductDepartment" "ProductCategory"   "UnitsSold"        
## [16] "Revenue"

Bộ dữ liệu Supermarket Transactions bao gồm thông tin giao dịch của khách hàng tại siêu thị, với 14,059 quan sát16 biến.

Dữ liệu chứa các thông tin đa dạng như:

  • Thông tin khách hàng: giới tính, tình trạng hôn nhân, thu nhập, con cái, quyền sở hữu nhà

  • Thông tin địa lý: thành phố, bang, quốc gia

  • Thông tin sản phẩm: nhóm sản phẩm, loại, doanh thu, số lượng bán

Giải thích các biến

library(knitr)
## Warning: package 'knitr' was built under R version 4.3.3
df <- data.frame(
  `Tên biến` = c("PurchaseDate", "CustomerID", "Gender", "MaritalStatus", "Homeowner", "Children",
                 "AnnualIncome", "City", "StateorProvince", "Country", "ProductFamily",
                 "ProductDepartment", "ProductCategory", "UnitsSold", "Revenue"),
  `Giải thích` = c("Ngày mua hàng", "Mã định danh khách hàng", "Giới tính khách hàng (`M`, `F`)",
                   "Tình trạng hôn nhân (`S`, `M`)", "Có sở hữu nhà hay không (`Y`, `N`)",
                   "Số lượng con", "Thu nhập hàng năm theo khoảng", "Thành phố cư trú",
                   "Bang hoặc tỉnh", "Quốc gia", "Nhóm sản phẩm lớn", "Bộ phận sản phẩm cụ thể",
                   "Loại sản phẩm chi tiết", "Số lượng sản phẩm đã bán",
                   "Doanh thu từ giao dịch (đơn vị không xác định)")
)
kable(df, format = "markdown")
Tên.biến Giải.thích
PurchaseDate Ngày mua hàng
CustomerID Mã định danh khách hàng
Gender Giới tính khách hàng (M, F)
MaritalStatus Tình trạng hôn nhân (S, M)
Homeowner Có sở hữu nhà hay không (Y, N)
Children Số lượng con
AnnualIncome Thu nhập hàng năm theo khoảng
City Thành phố cư trú
StateorProvince Bang hoặc tỉnh
Country Quốc gia
ProductFamily Nhóm sản phẩm lớn
ProductDepartment Bộ phận sản phẩm cụ thể
ProductCategory Loại sản phẩm chi tiết
UnitsSold Số lượng sản phẩm đã bán
Revenue Doanh thu từ giao dịch (đơn vị không xác định)

2.2 Thống kê mô tả các biến

2.2.1 Phân tích các biến định tính

2.2.1.1 Biến Gender

Lập bảng tần số và tần suất

# Bảng tần số
gender_freq <- table(data$Gender)
# Bảng tần suất
gender_prop <- prop.table(gender_freq)
# Gộp tần số và tần suất vào một bảng
gender_table <- data.frame(
  Gender = names(gender_freq),
  Frequency = as.vector(gender_freq),
  Proportion = round(as.vector(gender_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(gender_table, caption = "Bảng tần số và tần suất của biến Gender")
Bảng tần số và tần suất của biến Gender
Gender Frequency Proportion
F 7170 0.51
M 6889 0.49

Nhận xét Dựa vào bảng tần số và tần suất của biến Gender, ta có:

  • Số lượng khách hàng nữ (F) là 7170, chiếm 51% tổng số quan sát.

  • Số lượng khách hàng nam (M) là 6889, chiếm 49% tổng số quan sát.

  • Tỷ lệ giới tính giữa nam và nữ gần như cân bằng, với nữ chiếm tỷ lệ nhỉnh hơn một chút.

Vẽ đồ thị

library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.3.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.3.3
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:data.table':
## 
##     between, first, last
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
data %>%
  count(Gender) %>%
  ggplot(aes(x = Gender, y = n, fill = Gender)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), vjust = -0.5, color = "black") +
  scale_fill_manual(values = c("F" = "#F4A7B9", "M" = "#89CFF0")) +
  labs(
    title = "Biểu đồ tần số theo giới tính",
    x = "Giới tính",
    y = "Số lượng"
  ) +
  theme_bw()

Biểu đồ tần số cho biến Gender cho thấy số lượng khách hàng nam và nữ là tương đương nhau, với khách hàng nữ chiếm tỷ lệ nhỉnh hơn. Điều này phù hợp với bảng tần số ở trên và xác nhận rằng tập dữ liệu không có sự mất cân bằng giới tính nghiêm trọng.

2.2.1.2 Biến MaritalStatus

Lập bảng tần số và tần suất

# Bảng tần số
marital_freq <- table(data$MaritalStatus)
# Bảng tần suất
marital_prop <- prop.table(marital_freq)
# Gộp tần số và tần suất vào một bảng
marital_table <- data.frame(
  MaritalStatus = names(marital_freq),
  Frequency = as.vector(marital_freq),
  Proportion = round(as.vector(marital_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(marital_table, caption = "Bảng tần số và tần suất của biến MaritalStatus")
Bảng tần số và tần suất của biến MaritalStatus
MaritalStatus Frequency Proportion
M 6866 0.488
S 7193 0.512

Nhận xét: Dựa vào bảng tần số và tần suất của biến MaritalStatus, ta thấy:

  • Số lượng khách hàng độc thân (S) là 7193, chiếm 51.2% tổng số quan sát.

  • Số lượng khách hàng đã kết hôn (M) là 6866, chiếm 48.8% tổng số quan sát.

  • Tỷ lệ giữa hai nhóm khá cân bằng, trong đó nhóm độc thân chiếm ưu thế nhẹ.

    Điều này cho thấy tập dữ liệu có sự phân bố tương đối đồng đều giữa hai trạng thái hôn nhân. Vẽ đồ thị

data %>%
  count(MaritalStatus) %>%
  ggplot(aes(x = MaritalStatus, y = n, fill = MaritalStatus)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), vjust = -0.5, color = "black") +
  scale_fill_manual(values = c("S" = "#FF9999", "M" = "#9999FF")) + 
  labs(
    title = "Biểu đồ tần số theo tình trạng hôn nhân",
    x = "Tình trạng hôn nhân",
    y = "Số lượng"
  ) +
  theme_bw()

2.2.1.3 Biến Homeowner

Lập bảng tần số và tần suất

# Bảng tần số
homeowner_freq <- table(data$Homeowner)
# Bảng tần suất
homeowner_prop <- prop.table(homeowner_freq)
# Gộp tần số và tần suất vào một bảng
homeowner_table <- data.frame(
  Homeowner = names(homeowner_freq),
  Frequency = as.vector(homeowner_freq),
  Proportion = round(as.vector(homeowner_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(homeowner_table, caption = "Bảng tần số và tần suất của biến Homeowner")
Bảng tần số và tần suất của biến Homeowner
Homeowner Frequency Proportion
N 5615 0.399
Y 8444 0.601

Nhận xét: Dựa vào bảng tần số và tần suất biến Homeowner, ta thấy:

  • Số lượng khách hàng không sở hữu nhà (N) là 5615, chiếm 39.9% tổng số quan sát.

  • Số lượng khách hàng sở hữu nhà (Y) là 8444, chiếm 60.1% tổng số quan sát.

Tỷ lệ giữa hai nhóm không cân bằng, trong đó nhóm sở hữu nhà chiếm ưu thế rõ rệ*. Điều này phản ánh rằng phần lớn khách hàng trong tập dữ liệu là những người đã sở hữu nhà. Vẽ đồ thị

data %>%
  count(Homeowner) %>%
  ggplot(aes(x = Homeowner, y = n, fill = Homeowner)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), vjust = -0.5, color = "black") +
  scale_fill_manual(values = c("N" = "#FF9999", "Y" = "#9999FF")) + 
  labs(
    title = "Biểu đồ tần số theo biến Homeowner",
    x = "Sở hữu nhà",
    y = "Số lượng"
  ) +
  theme_bw()

2.2.1.4 Biến AnnualIncome

Lập bảng tần số và tần suất

# Bảng tần số
annualincome_freq <- table(data$AnnualIncome)
# Bảng tần suất
annualincome_prop <- prop.table(annualincome_freq)
# Gộp tần số và tần suất vào một bảng
annualincome_table <- data.frame(
  AnnualIncome = names(annualincome_freq),
  Frequency = as.vector(annualincome_freq),
  Proportion = round(as.vector(annualincome_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(annualincome_table, caption = "Bảng tần số và tần suất của biến AnnualIncome")
Bảng tần số và tần suất của biến AnnualIncome
AnnualIncome Frequency Proportion
$10K - $30K 3090 0.220
$110K - $130K 643 0.046
$130K - $150K 760 0.054
$150K + 273 0.019
$30K - $50K 4601 0.327
$50K - $70K 2370 0.169
$70K - $90K 1709 0.122
$90K - $110K 613 0.044

Nhận xét: Dựa vào bảng tần số và tần suất biến AnnualIncome, ta thấy:

  • Nhóm ($10K - $30K) có 3090 khách hàng, chiếm 22.0% tổng số, là nhóm có thu nhập thấp nhưng số lượng khá lớn trong tập dữ liệu.

  • Nhóm ($30K - $50K) chiếm tỷ lệ cao nhất với 4601 khách hàng, tương đương 32.7%, cho thấy đây là mức thu nhập phổ biến nhất.

  • Nhóm ($50K - $70K) có 2370 khách hàng, chiếm 16.9%, là nhóm thu nhập trung bình thấp với số lượng đáng kể.

  • Nhóm ($70K - $90K) chiếm 12.2% với 1709 khách hàng, phản ánh mức thu nhập trung bình khá.

  • Nhóm ($90K - $110K) có 613 khách hàng, chiếm 4.4%, giảm rõ rệt so với nhóm thấp hơn.

  • Nhóm thu nhập cao hơn ($110K - $130K) chiếm 4.6% với 643 khách hàng.

  • Nhóm ($130K - $150K) có 760 khách hàng, chiếm 5.4%, thể hiện số lượng khách hàng thu nhập khá cao nhưng không nhiều.

  • Nhóm ($150K +) là nhóm thu nhập cao nhất, chỉ chiếm 1.9% với 273 khách hàng, là nhóm nhỏ nhất trong tập dữ liệu.

Tỷ lệ giữa các nhóm thu nhập không cân bằng, trong đó các nhóm thu nhập thấp và trung bình chiếm ưu thế rõ rệt.
Điều này phản ánh rằng phần lớn khách hàng trong tập dữ liệu có mức thu nhập chủ yếu ở mức trung bình và thấp.

Vẽ đồ thị

data %>%
  count(AnnualIncome) %>%
  ggplot(aes(x = AnnualIncome, y = n, fill = AnnualIncome)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 3) +
  scale_fill_brewer(palette = "Pastel1") +
  labs(
    title = "Biểu đồ tần số theo biến AnnualIncome",
    x = "Thu nhập hằng năm",
    y = "Số lượng"
  ) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

2.2.1.5 Biến City

Lập bảng tần số và tần suất

# Bảng tần số
city_freq <- table(data$City)
# Bảng tần suất
city_prop <- prop.table(city_freq)
# Gộp tần số và tần suất vào một bảng
city_table <- data.frame(
  City = names(city_freq),
  Frequency = as.vector(city_freq),
  Proportion = round(as.vector(city_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(city_table, caption = "Bảng tần số và tần suất của biến City")
Bảng tần số và tần suất của biến City
City Frequency Proportion
Acapulco 383 0.027
Bellingham 143 0.010
Beverly Hills 811 0.058
Bremerton 834 0.059
Camacho 452 0.032
Guadalajara 75 0.005
Hidalgo 845 0.060
Los Angeles 926 0.066
Merida 654 0.047
Mexico City 194 0.014
Orizaba 464 0.033
Portland 876 0.062
Salem 1386 0.099
San Andres 621 0.044
San Diego 866 0.062
San Francisco 130 0.009
Seattle 922 0.066
Spokane 875 0.062
Tacoma 1257 0.089
Vancouver 633 0.045
Victoria 176 0.013
Walla Walla 160 0.011
Yakima 376 0.027

Nhận xét: Dựa vào bảng tần số và tần suất của biến City, ta thấy:

  • Các thành phố có số lượng khách hàng cao nhất gồm:

    • Salem: 1386 khách hàng (9.9%)

    • Tacoma: 1257 khách hàng (8.9%)

    • Los Angeles: 926 khách hàng (6.6%)

    • Seattle: 922 khách hàng (6.6%)

    • Portland: 876 khách hàng (6.2%)

    • San Diego: 866 khách hàng (6.2%)

    • Spokane: 875 khách hàng (6.2%)

    • Hidalgo: 845 khách hàng (6.0%)

    • Bremerton: 834 khách hàng (5.9%)

  • Một số thành phố có tỷ lệ khách hàng thấp, đáng chú ý:

    • Guadalajara: 75 khách hàng (0.5%)

    • San Francisco: 130 khách hàng (0.9%)

    • Bellingham: 143 khách hàng (1.0%)

    • Walla Walla: 160 khách hàng (1.1%)

    • Victoria: 176 khách hàng (1.3%)

Tỷ lệ phân bố khách hàng theo thành phố là không đồng đều. Các thành phố như SalemTacoma chiếm tỷ lệ lớn trong khi nhiều thành phố khác có số lượng khách hàng rất nhỏ.
Điều này cho thấy dữ liệu có sự tập trung khách hàng theo khu vực địa lý, có thể ảnh hưởng đến phân tích nếu không điều chỉnh hợp lý theo địa phương.

Vẽ đồ thị

library(ggplot2)
library(dplyr)
library(patchwork)
## Warning: package 'patchwork' was built under R version 4.3.3
# Giả sử 'data' là data frame của bạn
city_counts <- data %>%
  group_by(City) %>%
  summarise(n = n()) %>%
  arrange(desc(n)) # Sắp xếp theo số lượng giảm dần

n_cities <- nrow(city_counts)
mid_point <- ceiling(n_cities / 2)

# Dữ liệu cho biểu đồ thứ nhất (nửa trên theo số lượng)
top_half_cities <- head(city_counts, mid_point)

plot1_city_count <- ggplot(top_half_cities, aes(x = City, y = n)) +
  geom_col(fill = "#FF9999", color = "black") +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 2.5) +
  labs(x = "City", y = "Number") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8))

# Dữ liệu cho biểu đồ thứ hai (nửa dưới theo số lượng)
bottom_half_cities <- tail(city_counts, n_cities - mid_point)

plot2_city_count <- ggplot(bottom_half_cities, aes(x = City, y = n)) +
  geom_col(fill = "#FF9999", color = "black") +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 2.5) +
  labs(x = "City", y = "Number") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8))

# Kết hợp hai biểu đồ lại với nhau
plot1_city_count + plot2_city_count

2.2.1.6 Biến StateorProvince

Lập bảng tần số và tần suất

# Bảng tần số
state_freq <- table(data$StateorProvince)
# Bảng tần suất
state_prop <- prop.table(state_freq)
# Gộp tần số và tần suất vào một bảng
state_table <- data.frame(
  StateorProvince = names(state_freq),
  Frequency = as.vector(state_freq),
  Proportion = round(as.vector(state_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(state_table, caption = "Bảng tần số và tần suất của biến StateorProvince")
Bảng tần số và tần suất của biến StateorProvince
StateorProvince Frequency Proportion
BC 809 0.058
CA 2733 0.194
DF 815 0.058
Guerrero 383 0.027
Jalisco 75 0.005
OR 2262 0.161
Veracruz 464 0.033
WA 4567 0.325
Yucatan 654 0.047
Zacatecas 1297 0.092

Nhận xét Dựa vào bảng tần số và tần suất của biến StateorProvince, ta thấy:

  • Bang WA (Washington) có số lượng khách hàng nhiều nhất với 4567 người, chiếm 32.5% tổng số quan sát. Đây là bang chiếm ưu thế rõ rệt trong tập dữ liệu.

  • CA (California) xếp thứ hai với 2733 người, chiếm 19.4%, cho thấy đây cũng là khu vực tập trung nhiều khách hàng.

  • OR (Oregon) có 2262 khách hàng, tương ứng 16.1%, là nhóm lớn tiếp theo.

  • Zacatecas có 1297 khách hàng, chiếm 9.2%, là bang ở Mexico có số lượng khách hàng tương đối lớn.

  • Các bang BC, DF, và Yucatan có số lượng khách hàng dao động từ 800–850, chiếm khoảng 5.8% mỗi bang.

  • Các bang còn lại như Veracruz, Guerrero, Jalisco có số lượng khách hàng thấp hơn nhiều, trong đó Jalisco là ít nhất với chỉ 75 khách hàng, chiếm 0.5% tổng số.

Tổng thể, dữ liệu khách hàng tập trung chủ yếu ở các bang của Hoa Kỳ (WA, CA, OR), trong khi một số bang của Mexico có tỷ lệ thấp hơn đáng kể.

Vẽ đồ thị

data %>%
  count(StateorProvince) %>%
  ggplot(aes(x = StateorProvince, y = n, fill = StateorProvince)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 3) +
  scale_fill_brewer(palette = "Set3") +
  labs(
    title = "Biểu đồ tần số theo biến StateorProvince",
    x = "Bang/Tỉnh",
    y = "Số lượng"
  ) +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

2.2.1.7 Biến Country

Lập bảng tần số và tần suất

# Bảng tần số
country_freq <- table(data$Country)
# Bảng tần suất
country_prop <- prop.table(country_freq)
# Gộp tần số và tần suất vào một bảng
country_table <- data.frame(
  Country = names(country_freq),
  Frequency = as.vector(country_freq),
  Proportion = round(as.vector(country_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(country_table, caption = "Bảng tần số và tần suất của biến Country")
Bảng tần số và tần suất của biến Country
Country Frequency Proportion
Canada 809 0.058
Mexico 3688 0.262
USA 9562 0.680

Nhận xét: Dựa vào bảng tần số và tần suất của biến Country, ta thấy:

  • USA là quốc gia chiếm ưu thế lớn nhất với 9562 giao dịch, tương đương 68.0% tổng số quan sát. Đây là nhóm giao dịch chiếm đa số trong tập dữ liệu.

  • Mexico đứng thứ hai với 3688 giao dịch, chiếm 26.2%, là nhóm giao dịch lớn thứ hai.

  • Canada có số lượng giao dịch ít nhất với 809 người, chiếm 5.8% tổng số, là nhóm nhỏ nhất trong ba quốc gia.

Như vậy, phần lớn giao dịch trong tập dữ liệu đến từ Hoa Kỳ (USA), tiếp theo là Mexico và một phần nhỏ hơn đến từ Canada.

Vẽ đồ thị

data %>%
  count(Country) %>%
  ggplot(aes(x = Country, y = n, fill = Country)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 4) +
  scale_fill_brewer(palette = "Set2") +
  labs(
    title = "Biểu đồ tần số theo biến Country",
    x = "Quốc gia",
    y = "Số lượng khách hàng"
  ) +
  theme_bw()

2.2.1.8 Biến ProductFamily

Lập bảng tần số và tần suất

# Bảng tần số
productfamily_freq <- table(data$ProductFamily)
# Bảng tần suất
productfamily_prop <- prop.table(productfamily_freq)
# Gộp tần số và tần suất vào một bảng
productfamily_table <- data.frame(
  ProductFamily = names(productfamily_freq),
  Frequency = as.vector(productfamily_freq),
  Proportion = round(as.vector(productfamily_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(productfamily_table, caption = "Bảng tần số và tần suất của biến ProductFamily")
Bảng tần số và tần suất của biến ProductFamily
ProductFamily Frequency Proportion
Drink 1250 0.089
Food 10153 0.722
Non-Consumable 2656 0.189

Nhận xét:

Dựa vào bảng tần số và tần suất của biến ProductFamily, ta thấy:

  • Nhóm Food chiếm đa số với 10153 khách hàng, tương đương 72.2% tổng số quan sát. Đây là nhóm sản phẩm phổ biến nhất trong tập dữ liệu.

  • Nhóm Non-Consumable có 2656 khách hàng, chiếm 18.9%, là nhóm sản phẩm không tiêu thụ chiếm tỷ lệ đáng kể.

  • Nhóm Drink chiếm tỷ lệ nhỏ nhất với 1250 khách hàng, chỉ chiếm 8.9% tổng số, phản ánh số lượng khách hàng sử dụng nhóm sản phẩm đồ uống ít hơn so với hai nhóm còn lại.

Tổng thể, khách hàng chủ yếu tập trung vào nhóm sản phẩm Food, trong khi nhóm Drink có sự tham gia thấp nhất.

Vẽ đồ thị

data %>%
  count(ProductFamily) %>%
  ggplot(aes(x = ProductFamily, y = n, fill = ProductFamily)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 4) +
  scale_fill_brewer(palette = "Pastel2") +
  labs(
    title = "Biểu đồ tần số theo biến ProductFamily",
    x = "Nhóm sản phẩm",
    y = "Số lượng khách hàng"
  ) +
  theme_bw()

2.2.1.9 Biến ProductDepartment

Lập bảng tần số và tần suất

# Bảng tần số
productdepartment_freq <- table(data$ProductDepartment)
# Bảng tần suất
productdepartment_prop <- prop.table(productdepartment_freq)
# Gộp tần số và tần suất vào một bảng
productdepartment_table <- data.frame(
  ProductDepartment = names(productdepartment_freq),
  Frequency = as.vector(productdepartment_freq),
  Proportion = round(as.vector(productdepartment_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(productdepartment_table, caption = "Bảng tần số và tần suất của biến ProductDepartment")
Bảng tần số và tần suất của biến ProductDepartment
ProductDepartment Frequency Proportion
Alcoholic Beverages 356 0.025
Baked Goods 425 0.030
Baking Goods 1072 0.076
Beverages 680 0.048
Breakfast Foods 188 0.013
Canned Foods 977 0.069
Canned Products 109 0.008
Carousel 59 0.004
Checkout 82 0.006
Dairy 903 0.064
Deli 699 0.050
Eggs 198 0.014
Frozen Foods 1382 0.098
Health and Hygiene 893 0.064
Household 1420 0.101
Meat 89 0.006
Periodicals 202 0.014
Produce 1994 0.142
Seafood 102 0.007
Snack Foods 1600 0.114
Snacks 352 0.025
Starchy Foods 277 0.020

Nhận xét:

Dựa vào bảng tần số và tần suất của biến ProductDepartment, ta thấy:

  • Nhóm Produce chiếm tỷ lệ cao nhất với 1994 khách hàng, tương đương 14.2% tổng số, cho thấy đây là nhóm sản phẩm phổ biến nhất.

  • Nhóm Snack FoodsHousehold lần lượt có 1600 (11.4%) và 1420 (10.1%) khách hàng, là những nhóm có số lượng khách hàng lớn tiếp theo.

  • Nhóm Frozen Foods cũng có số lượng đáng kể với 1382 khách hàng (9.8%).

  • Các nhóm như Baking Goods (7.6%), Canned Foods (6.9%), Dairy (6.4%), và Health and Hygiene (6.4%) chiếm tỷ lệ trung bình khá trong tập dữ liệu.

  • Một số nhóm có số lượng khách hàng rất thấp dưới 1% như Carousel (0.4%), Checkout (0.6%), Meat (0.6%), và Seafood (0.7%).

Nhìn chung, tỷ lệ phân bổ khách hàng theo các nhóm sản phẩm khá đa dạng, với một số nhóm sản phẩm chính chiếm ưu thế rõ rệt trong tập dữ liệu.

Vẽ đồ thị

# Giả sử 'data' là data frame của bạn
dept_counts <- data %>%
  group_by(ProductDepartment) %>%
  summarise(n = n()) %>%
  arrange(n) # Sắp xếp theo số lượng để chia

n_depts <- nrow(dept_counts)
mid_point <- ceiling(n_depts / 2)

# Dữ liệu cho biểu đồ thứ nhất (nửa đầu)
dept_counts_1 <- head(dept_counts, mid_point)

plot1 <- ggplot(dept_counts_1, aes(x = ProductDepartment, y = n)) +
  geom_col(fill = "#4169E1", color = "black") +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 2.5) +
  labs(x = "Product Department", y = "Number") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8))

# Dữ liệu cho biểu đồ thứ hai (nửa sau)
dept_counts_2 <- tail(dept_counts, n_depts - mid_point)

plot2 <- ggplot(dept_counts_2, aes(x = ProductDepartment, y = n)) +
  geom_col(fill = "#4169E1", color = "black") +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 2.5) +
  labs(x = "Product Department", y = "Number") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 8))

# Kết hợp hai biểu đồ lại với nhau
plot1 + plot2

2.2.1.10 Biến ProductCategory

Lập bảng tần số và tần suất

# Bảng tần số
productcategory_freq <- table(data$ProductCategory)
# Bảng tần suất
productcategory_prop <- prop.table(productcategory_freq)
# Gộp tần số và tần suất vào một bảng
productcategory_table <- data.frame(
  ProductCategory = names(productcategory_freq),
  Frequency = as.vector(productcategory_freq),
  Proportion = round(as.vector(productcategory_prop), 3)
)
# Hiển thị bảng với kable
knitr::kable(productcategory_table, caption = "Bảng tần số và tần suất của biến ProductCategory")
Bảng tần số và tần suất của biến ProductCategory
ProductCategory Frequency Proportion
Baking Goods 484 0.034
Bathroom Products 365 0.026
Beer and Wine 356 0.025
Bread 425 0.030
Breakfast Foods 417 0.030
Candles 45 0.003
Candy 352 0.025
Canned Anchovies 44 0.003
Canned Clams 53 0.004
Canned Oysters 35 0.002
Canned Sardines 40 0.003
Canned Shrimp 38 0.003
Canned Soup 404 0.029
Canned Tuna 87 0.006
Carbonated Beverages 154 0.011
Cleaning Supplies 189 0.013
Cold Remedies 93 0.007
Dairy 903 0.064
Decongestants 85 0.006
Drinks 135 0.010
Eggs 198 0.014
Electrical 355 0.025
Frozen Desserts 323 0.023
Frozen Entrees 118 0.008
Fruit 765 0.054
Hardware 129 0.009
Hot Beverages 226 0.016
Hygiene 197 0.014
Jams and Jellies 588 0.042
Kitchen Products 217 0.015
Magazines 202 0.014
Meat 761 0.054
Miscellaneous 42 0.003
Packaged Vegetables 48 0.003
Pain Relievers 192 0.014
Paper Products 345 0.025
Pizza 194 0.014
Plastic Products 141 0.010
Pure Juice Beverages 165 0.012
Seafood 102 0.007
Side Dishes 153 0.011
Snack Foods 1600 0.114
Specialty 289 0.021
Starchy Foods 277 0.020
Vegetables 1728 0.123

Nhận xét: Dựa vào bảng tần số và tần suất của biến ProductCategory, ta thấy:

  • Nhóm Snack Foods có số lượng khách hàng lớn nhất với 1600 khách hàng, chiếm 11.4% tổng số, phản ánh đây là nhóm sản phẩm phổ biến nhất.

  • Nhóm Vegetables đứng thứ hai với 1728 khách hàng (12.3%), cho thấy đây cũng là nhóm sản phẩm có sức tiêu thụ cao.

  • Nhóm Dairy chiếm tỷ lệ đáng kể với 903 khách hàng (6.4%), tiếp theo là các nhóm Fruit (5.4%) và Meat (5.4%).

  • Các nhóm sản phẩm như Jams and Jellies (4.2%), Baking Goods (3.4%), Canned Soup (2.9%), và Bread (3.0%) cũng có số lượng khách hàng trung bình.

  • Một số nhóm có tỷ lệ rất thấp dưới 1% như Candles (0.3%), Canned Anchovies (0.3%), Canned Oysters (0.2%), và Miscellaneous (0.3%).

Nhìn chung, tỷ lệ phân bố khách hàng theo các nhóm sản phẩm rất đa dạng, trong đó một số nhóm chính chiếm ưu thế rõ rệt, phản ánh sự đa dạng trong lựa chọn sản phẩm của khách hàng.

Vẽ đồ thị

# Giả sử 'data' là data frame của bạn
category_counts <- data %>%
  group_by(ProductCategory) %>%
  summarise(n = n()) %>%
  arrange(n) # Sắp xếp theo số lượng để chia

n_categories <- nrow(category_counts)
mid_point <- ceiling(n_categories / 2)

# Dữ liệu cho biểu đồ thứ nhất (nửa đầu theo số lượng)
category_counts_1 <- head(category_counts, mid_point)

plot1_cat <- ggplot(category_counts_1, aes(x = ProductCategory, y = n)) +
  geom_col(fill = "#8DA0CB", color = "black") +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 2.5) +
  labs(x = "Product Category", y = "Number") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 6))

# Dữ liệu cho biểu đồ thứ hai (nửa sau theo số lượng)
category_counts_2 <- tail(category_counts, n_categories - mid_point)

plot2_cat <- ggplot(category_counts_2, aes(x = ProductCategory, y = n)) +
  geom_col(fill = "#8DA0CB", color = "black") +
  geom_text(aes(label = n), vjust = -0.5, color = "black", size = 2.5) +
  labs(x = "Product Category", y = "Number") +
  theme_bw() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 6))

# Kết hợp hai biểu đồ lại với nhau
plot1_cat + plot2_cat

2.2.2 Phân tích các biến định lượng

2.2.2.1 Biến Children

Thống kê mô tả biến Children

Children <- summary(data$Children)
print(Children)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.00    1.00    3.00    2.53    4.00    5.00

Nhận xét: Dựa vào kết quả thống kê mô tả biến Children, ta thấy:

  • Số con tối thiểu là 0, cho thấy có những khách hàng không có con.

  • Số con tối đa là 5, phản ánh khách hàng có số con nhiều nhất là 5.

  • Giá trị trung vị (Median) là 3, biểu thị phân bố số con tập trung quanh mức 3 con.

  • Giá trị trung bình (Mean) là 2.53, hơi thấp hơn trung vị, cho thấy số con trung bình khoảng 2 đến 3 con.

  • Phân vị thứ nhất (1st Qu.) là 1 và phân vị thứ ba (3rd Qu.) là 4, nghĩa là 25% khách hàng có từ 0 đến 1 con, 50% có từ 1 đến 4 con, và 25% có từ 4 đến 5 con.

Như vậy, phần lớn khách hàng có số con dao động từ 1 đến 4, với mức trung bình khoảng 2-3 con.

Vẽ đồ thị

ggplot(data, aes(x = factor(Children))) + 
  geom_bar(fill = "pink", color = "black") +
  labs(
       x = "Số lượng con cái",
       y = "Số lượng khách hàng") +
  theme_bw() +
  geom_text(stat='count', aes(label=..count..), vjust=-0.5) 
## Warning: The dot-dot notation (`..count..`) was deprecated in ggplot2 3.4.0.
## ℹ Please use `after_stat(count)` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

2.2.2.2 UnitsSold

Thống kê mô tả biến UnitsSold

UnitsSold <- summary(data$UnitsSold)
print(UnitsSold)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   3.000   4.000   4.081   5.000   8.000

Nhận xét: Kết quả thống kê mô tả biến UnitsSold cho thấy:

  • Số đơn vị bán tối thiểu là 1, cho thấy có những giao dịch bán tối thiểu 1 sản phẩm.

  • Số đơn vị bán tối đa là 8, phản ánh có giao dịch bán nhiều nhất lên đến 8 sản phẩm.

  • Giá trị trung vị (Median) là 4, biểu thị phân bố số lượng bán tập trung ở mức 4 đơn vị.

  • Giá trị trung bình (Mean) là 4.081, gần với giá trị trung vị, cho thấy phân phối số lượng bán khá cân bằng xung quanh 4 sản phẩm.

  • Phân vị thứ nhất (1st Qu.) là 3 và phân vị thứ ba (3rd Qu.) là 5, tức 25% các giao dịch bán dưới hoặc bằng 3 đơn vị, 50% giao dịch bán từ 3 đến 5 đơn vị, và 25% còn lại bán trên 5 đơn vị.

Như vậy, phần lớn các giao dịch bán trong dữ liệu nằm trong khoảng từ 3 đến 5 đơn vị sản phẩm, với mức trung bình gần 4 đơn vị.

Vẽ đồ thị

ggplot(data, aes(x = factor(UnitsSold))) + 
  geom_bar(fill = "yellow", color = "black") +
  labs(
       x = "Số lượng đơn vị sản phẩm",
       y = "Số lượng giao dịch") +
  theme_bw() +
  geom_text(stat='count', aes(label=..count..), vjust=-0.5) 

2.2.2.3 Revenue

Thống kê mô tả biến Revenue

Revenue <- summary(data$Revenue)
print(Revenue)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    0.53    6.84   11.25   13.00   17.37   56.70

Nhận xét: Kết quả thống kê mô tả biến Revenue cho thấy:

  • Giá trị doanh thu thấp nhất (Min.) là 0.53, cho thấy có những giao dịch mang lại doanh thu rất nhỏ.

  • Giá trị doanh thu cao nhất (Max.) là 56.70, phản ánh có giao dịch mang lại doanh thu lớn nhất trong tập dữ liệu.

  • Giá trị trung vị (Median) là 11.25, cho thấy một nửa các giao dịch có doanh thu dưới hoặc bằng mức này.

  • Giá trị trung bình (Mean) là 13.00, cao hơn trung vị, điều này cho thấy phân phối doanh thu có thể bị lệch phải (có một số giá trị lớn kéo trung bình lên).

  • Phân vị thứ nhất (1st Qu.) là 6.84 và phân vị thứ ba (3rd Qu.) là 17.37, tức 25% giao dịch có doanh thu dưới hoặc bằng 6.84, 50% giao dịch có doanh thu trong khoảng từ 6.84 đến 17.37, và 25% còn lại có doanh thu trên 17.37.

Như vậy, doanh thu các giao dịch có sự phân bố rộng với xu hướng lệch phải, phần lớn doanh thu tập trung ở mức vừa phải nhưng có một số giao dịch doanh thu rất cao kéo giá trị trung bình lên.

Vẽ đồ thị

ggplot(data, aes(x = Revenue)) +
  geom_histogram(binwidth = 1,
                 fill = "lightgreen",
                 color = "black") +
  labs(
       x = "Doanh thu (USD)",
       y = "Số lượng giao dịch") +
  theme_bw()

LS0tDQp0aXRsZTogIioqTmhp4buHbSB24bulIDEqKiINCmF1dGhvcjogIlRy4buLbmggVGjhu4sgVGh1IGjDoCINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVIOiVNOiVTLCAlZCAtICVtIC0gJVknKWAiDQpvdXRwdXQ6DQogaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogeWVzDQogICAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KIyAqKlBI4bqmTiAxOiBUw5NNIFThuq5UIFPDgUNIKioNCg0KIyMgKipHSeG7mkkgVEhJ4buGVSBDSFVORyoqDQoNCkN14buRbiBzw6FjaCAqR2VuZXJhbGl6ZWQgTGluZWFyIE1vZGVscyBXaXRoIEV4YW1wbGVzIGluIFIqIGRvIFBldGVyIEsuIER1bm4gdsOgIEdvcmRvbiBLLiBTbXl0aCDEkeG7k25nIHTDoWMgZ2nhuqMsIGzDoCBt4buZdCBwaOG6p24gY+G7p2Egc8OqLXJpICpTcHJpbmdlciBUZXh0cyBpbiBTdGF0aXN0aWNzKi4gU8OhY2ggxJHGsOG7o2MgY2h14bqpbiBi4buLIGLhurFuZyBuZ8O0biBuZ+G7ryBMYVRlWCB2w6Agc+G7rSBk4bulbmcgcGhpw6puIGLhuqNuIFIgMy40LjMgdHJvbmcgdG/DoG4gYuG7mSB2w60gZOG7pSB2w6AgcGjDom4gdMOtY2guIFBldGVyIEsuIER1bm4gbMOgIGdp4bqjbmcgdmnDqm4gdOG6oWkgS2hvYSBLaG9hIGjhu41jLCBT4bupYyBraOG7j2UsIEdpw6FvIGThu6VjIHbDoCBL4bu5IHRodeG6rXQgdGh14buZYyDEkOG6oWkgaOG7jWMgU3Vuc2hpbmUgQ29hc3QgKMOaYyksIHRyb25nIGtoaSBHb3Jkb24gSy4gU215dGggY8O0bmcgdMOhYyB04bqhaSBCYW4gVGluIHNpbmggaOG7jWMgY+G7p2EgVmnhu4duIE5naGnDqm4gY+G7qXUgWSBraG9hIFdhbHRlciB2w6AgRWxpemEgSGFsbCAow5pjKS4NCg0KIyMgKipN4bukQyBUScOKVSBWw4AgxJDhu5BJIFTGr+G7ok5HIMSQ4buYQyBHSeG6oioqDQoNCk3hu6VjIHRpw6p1IGNow61uaCBj4bunYSBjdeG7kW4gc8OhY2ggbMOgIGN1bmcgY+G6pXAgbeG7mXQgY8OhY2ggdGnhur9wIGPhuq1uIHRvw6BuIGRp4buHbiDEkeG7kWkgduG7m2kgTcO0IGjDrG5oIFR1eeG6v24gdMOtbmggVOG7lW5nIHF1w6F0IChHTE1zKSwga+G6v3QgaOG7o3AgZ2nhu69hIGzDvSB0aHV54bq/dCBzw6J1IHPhuq9jIHbDoCDhu6luZyBk4bulbmcgdGjhu7FjIHRp4buFbiB0aMO0bmcgcXVhIHBow6JuIG3hu4FtIHRo4buRbmcga8OqIFIuIFTDoWMgZ2nhuqMgbW9uZyBtdeG7kW4gbmfGsOG7nWkgaOG7jWMga2jDtG5nIGNo4buJIGhp4buDdSDEkcaw4bujYyBt4bq3dCBr4bu5IHRodeG6rXQgbcOgIGPDsm4gbuG6r20gcsO1IG7hu5lpIGR1bmcgxJHhurFuZyBzYXUgbcO0IGjDrG5oIHRyb25nIHBow6JuIHTDrWNoIGThu68gbGnhu4d1Lg0KDQpTw6FjaCBnaeG6oyDEkeG7i25oIMSR4buZYyBnaeG6oyBjw7MgbuG7gW4gdOG6o25nIGPGoSBi4bqjbiB24buBIHRo4buRbmcga8OqLCBnaeG6oyB0aGnhur90IHbDoCBraeG7g20gxJHhu4tuaCBnaeG6oyB0aHV54bq/dC4gVHV5IG5oacOqbiwgc8OhY2ggduG6q24gY3VuZyBj4bqlcCBwaOG6p24gZ2nhu5tpIHRoaeG7h3UgxJHhuqd5IMSR4bunIGNobyBuZ8aw4budaSDEkeG7jWMgYuG6r3QgxJHhuqd1IG5oxrAgaOG7k2kgcXV5IHR1eeG6v24gdMOtbmgsIGdpw7pwIMSR4buZYyBnaeG6oyBk4buFIGTDoG5nIHRp4bq/cCBj4bqtbiBtw7QgaMOsbmggY2hvIGtob2EgaOG7jWMgdGjhu5FuZyBrw6ogaGnhu4duIMSR4bqhaSB2w6Ag4bupbmcgZOG7pW5nLg0KDQpOZ2/DoGkgcmEsIHPDoWNoIGPDsm4gbeG7nyBy4buZbmcgcGjhuqFtIHZpIGLhurFuZyBjw6FjaCDEkcawYSByYSBoxrDhu5tuZyBk4bqrbiBtw7QgaMOsbmggbsOibmcgY2FvIHbDoCB4w6J5IGThu7FuZyBtw7QgaMOsbmggdG/DoG4gZGnhu4duIGfhu5NtIGPDoWMgYsaw4bubYyB04burIGNodeG6qW4gYuG7iyBk4buvIGxp4buHdSwga2jDoW0gcGjDoSBk4buvIGxp4buHdSwgeMOieSBk4buxbmcgbcO0IGjDrG5oLCBraeG7g20gxJHhu4tuaCBnaeG6oyB0aGnhur90LCB2w6AgY+G6oyBjw6FjaCBuaMOsbiBuaOG6rW4ga+G6v3QgcXXhuqMgdOG7qyBnw7NjIMSR4buZIHRo4buxYyB0aeG7hW4gcGjDom4gdMOtY2ggdHJvbmcgbmdoacOqbiBj4bupdSB2w6Ag4bupbmcgZOG7pW5nIHRo4buxYyB04bq/Lg0KDQojIyAqKk5Hw5ROIE5H4buuIFbDgCBDw5RORyBD4bukIFPhu6wgROG7pE5HKioNCg0KTmfDtG4gbmfhu68gbOG6rXAgdHLDrG5oIFIgxJHGsOG7o2Mgc+G7rSBk4bulbmcgeHV5w6puIHN14buRdCBjdeG7kW4gc8OhY2gsIGtow7RuZyBjaOG7iSBuaMawIG3hu5l0IGPDtG5nIGPhu6UgdMOtbmggdG/DoW4gbcOgIGPDsm4gbMOgIHBoxrDGoW5nIHRp4buHbiDEkeG7gyBtaW5oIGjhu41hIGzDvSB0aHV54bq/dCBi4bqxbmcgdGjhu7FjIGjDoG5oLiBDdeG7kW4gc8OhY2ggYmFvIGfhu5NtIHBo4bqnbiBnaeG7m2kgdGhp4buHdSDEkeG7mWMgbOG6rXAgduG7gSBSIChQaOG7pSBs4bulYyBBKSwgY8O5bmcgduG7m2kgY8OhYyDEkW/huqFuIG3DoyBob8OgbiBjaOG7iW5oIGvDqG0gaMaw4bubbmcgZOG6q24gY+G7pSB0aOG7gyB0cm9uZyB04burbmcgY2jGsMahbmcuIMSQaeG7gXUgbsOgeSBnacO6cCBuZ8aw4budaSDEkeG7jWMgw6FwIGThu6VuZyB0cuG7sWMgdGnhur9wIGPDoWMgbcO0IGjDrG5oIHRo4buRbmcga8OqIHbDoG8gZOG7ryBsaeG7h3UgdGjhu7FjIHThur8gbeG7mXQgY8OhY2ggbGluaCBob+G6oXQgdsOgIGhp4buHdSBxdeG6oy4NCg0KIyMgKipO4buYSSBEVU5HIENIw41OSCBD4bumQSBTw4FDSCoqDQoNCiMjIyAqKkNoxrDGoW5nIDE6IE3DtCBow6xuaCB0aOG7kW5nIGvDqioqDQoNCkNoxrDGoW5nIDEgZ2nhu5tpIHRoaeG7h3UgdOG7lW5nIHF1YW4gduG7gSBtw7QgaMOsbmggdGjhu5FuZyBrw6osIHThuq1wIHRydW5nIMSR4bq3YyBiaeG7h3QgdsOgbyBjw6FjIG3DtCBow6xuaCBo4buTaSBxdXkuIE7hu5lpIGR1bmcgY2jDrW5oIGJhbyBn4buTbToNCg0KLSBLaMOhaSBxdcOhdCB24buBIG3DtCBow6xuaCBo4buTaSBxdXkgdsOgIGPDoWNoIG3DtCB04bqjIGThu68gbGnhu4d1IHF1YSBjw6FjIHF1eSDGsOG7m2MgY2h14bqpbi4NCg0KLSBT4butIGThu6VuZyBiaeG7g3UgxJHhu5MgxJHhu4Mga2jDoW0gcGjDoSB2w6AgxJHDoW5oIGdpw6EgbeG7kWkgcXVhbiBo4buHIGdp4buvYSBjw6FjIGJp4bq/biwgdsOtIGThu6UgbmjGsCBiaeG7g3UgxJHhu5MgRkVWIChkdW5nIHTDrWNoIHRo4bufIHJhIGfhuq9uZyBz4bupYykgduG7m2kgdHXhu5VpIHbDoCBjaGnhu4F1IGNhbywgZ2nDunAgeMOhYyDEkeG7i25oIHTDrW5oIGjhu6NwIGzDvSBj4bunYSBt4buRaSBxdWFuIGjhu4cgdHV54bq/biB0w61uaC4NCg0KLSBHaeG7m2kgdGhp4buHdSBjw6FjaCBtw6MgaMOzYSBiaeG6v24gcGjDom4gbG/huqFpIHRow6BuaCBiaeG6v24gZ2nhuqMgKGR1bW15IHZhcmlhYmxlcyksIMSR4bq3YyBiaeG7h3QgbMOgIG3DoyBow7NhIMSRaeG7gXUgdHLhu4sgKHRyZWF0bWVudCBjb2RpbmcpIMSR4buDIGdp4bqjaSB0aMOtY2ggaOG7hyBz4buRIGThu4UgZMOgbmcgaMahbi4NCg0KLSBQaMOibiB0w61jaCBj4bqldSB0csO6YyBj4bunYSBtw7QgaMOsbmggdGjhu5FuZyBrw6osIGJhbyBn4buTbSB0aMOgbmggcGjhuqduIG5n4bqrdSBuaGnDqm4gdsOgIHRow6BuaCBwaOG6p24gaOG7hyB0aOG7kW5nLg0KDQotIMSQ4buLbmggbmdoxKlhIHbDoCBrw70gaGnhu4d1IHRyb25nIG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCB0aGVvIHRoYW0gc+G7kSwgZ2nhuqNpIHRow61jaCBjw6FjIHRoYW0gc+G7kSBo4buTaSBxdXkgdsOgIHZhaSB0csOyIGPhu6dhIGJp4bq/biBo4bqxbmcgc+G7kSAoaW50ZXJjZXB0KS4NCg0KLSBUcmnhur90IGzDvSB24buBIG3DtCBow6xuaCB0aOG7kW5nIGvDqjogIlThuqV0IGPhuqMgbcO0IGjDrG5oIMSR4buBdSBzYWkgbmjGsG5nIG3hu5l0IHPhu5EgaOG7r3Ugw61jaC4iDQoNCi0gVGjhuqNvIGx14bqtbiB24buBIG3hu6VjIMSRw61jaCB4w6J5IGThu7FuZyBtw7QgaMOsbmgsIGPDom4gYuG6sW5nIGdp4buvYSDEkeG7mSBjaMOtbmggeMOhYyB2w6Agc+G7sSDEkcahbiBnaeG6o24sIGPFqW5nIG5oxrAgcGjDom4gYmnhu4d0IGdp4buvYSBuZ2hpw6puIGPhu6l1IHRo4buxYyBuZ2hp4buHbSB2w6AgcXVhbiBzw6F0Lg0KDQotIEdp4bubaSB0aGnhu4d1IHPGoSBsxrDhu6NjIHbhu4EgcGjhuqduIG3hu4FtIFIgdHJvbmcgeMOieSBk4buxbmcgdsOgIHBow6JuIHTDrWNoIG3DtCBow6xuaCB0aOG7kW5nIGvDqi4NCg0KIyMjICoqQ2jGsMahbmcgMjogTcO0IGjDrG5oIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oKioNCg0KQ2jGsMahbmcgMiB04bqtcCB0cnVuZyBwaMOibiB0w61jaCBjaGkgdGnhur90IG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCwgYmFvIGfhu5NtOg0KDQotIMSQ4buLbmggbmdoxKlhIG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCB24bubaSB0aMOgbmggcGjhuqduIGjhu4cgdGjhu5FuZyBsw6AgaMOgbSB0dXnhur9uIHTDrW5oIGPhu6dhIGPDoWMgYmnhur9uIGdp4bqjaSB0aMOtY2ggdsOgIHRow6BuaCBwaOG6p24gbmfhuqt1IG5oacOqbiBnaeG6oyDEkeG7i25oIHBow6JuIHBo4buRaSBjaHXhuqluIHbhu5tpIHBoxrDGoW5nIHNhaSBraMO0bmcgxJHhu5VpLg0KDQotIFBoxrDGoW5nIHBow6FwIMaw4bubYyBsxrDhu6NuZyB0aGFtIHPhu5EgaOG7k2kgcXV58J2bvSwgdHJvbmcgxJHDsyDGsOG7m2MgbMaw4bujbmcg8J2bvV5sw6Aga2jDtG5nIGNo4buHY2ggdsOgIHBoxrDGoW5nIHNhaSBj4bunYSBuw7MgxJHGsOG7o2MgeMOhYyDEkeG7i25oIHF1YSBtYSB0cuG6rW4gdGhp4bq/dCBr4bq/LiANCg0KLSDGr+G7m2MgbMaw4bujbmcgZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBj4bunYSBiaeG6v24gxJHDoXAg4bupbmcgdOG6oWkgY8OhYyDEkWnhu4NtIGPhu6UgdGjhu4MsIGvDqG0gdGhlbyB0w61uaCBzYWkgc+G7kSBjaHXhuqluLg0KDQotIMSQw6FuaCBnacOhIG3DtCBow6xuaCB0aMO0bmcgcXVhIFBow6JuIHTDrWNoIFBoxrDGoW5nIHNhaSAoQU5PVkEpLCBwaMOibiBjaGlhIHThu5VuZyBwaMawxqFuZyBzYWkgdGjDoG5oIHBo4bqnbiBkbyBtw7QgaMOsbmggZ2nhuqNpIHRow61jaCB2w6AgcGjhuqduIGTGsCwgZMO5bmcgdGjhu5FuZyBrw6ogRiDEkeG7gyBraeG7g20gxJHhu4tuaCDDvSBuZ2jEqWEgdOG7lW5nIHRo4buDIGPhu6dhIG3DtCBow6xuaC4NCg0KLSBTbyBzw6FuaCBjw6FjIG3DtCBow6xuaCBs4buTbmcgbmhhdSBi4bqxbmcgQU5PVkEsIHbhu5tpIG5ndXnDqm4gdOG6r2MgZ2nhu68gc+G7kSBo4bqhbmcgY2jDrW5oIGtoaSBjw7Mgc+G7kSBo4bqhbmcgdMawxqFuZyB0w6FjLg0KDQotIERp4buFbiBnaeG6o2kgw70gbmdoxKlhIGPDoWMgaOG7hyBz4buRIGjhu5NpIHF1eSwgxJHhurdjIGJp4buHdCB24bubaSBiaeG6v24gcGjDom4gbG/huqFpLCB2w6Ag4bqjbmggaMaw4bufbmcgY+G7p2EgYmnhur9uIGdp4bqjaSB0aMOtY2gga2jDoWMgaG/hurdjIGJp4bq/biDEkeG7lWkgYmnhur9uIMSRw6FwIOG7qW5nIMSR4bq/biBjw6FjaCBoaeG7g3UgaOG7hyBz4buRLg0KDQojIyMgKipDaMawxqFuZyAzOiBDaOG6qW4gxJFvw6FuIHbDoCB4w6J5IGThu7FuZyBtw7QgaMOsbmggdHV54bq/biB0w61uaEgqKg0KDQpDaMawxqFuZyAzIHThuq1wIHRydW5nIHbDoG8gxJHDoW5oIGdpw6EgKGNo4bqpbiDEkW/DoW4pIG3DtCBow6xuaCBo4buTaSBxdXkgdHV54bq/biB0w61uaCB2w6AgY8OhYyBr4bu5IHRodeG6rXQgxJHhu4MgY+G6o2kgdGhp4buHbiBtw7QgaMOsbmgsIGfhu5NtOg0KDQotIENo4bqpbiDEkW/DoW4gbcO0IGjDrG5oIHF1YSBiaeG7g3UgxJHhu5MgcGjhuqduIGTGsCwgYmFvIGfhu5NtIHBo4bqnbiBkxrAgY2h14bqpbiBow7NhIHbDoCBwaOG6p24gZMawIGRldmlhbmNlLCBkw7luZyDEkeG7gyBraeG7g20gdHJhIGdp4bqjIMSR4buLbmggcGjGsMahbmcgc2FpIGtow7RuZyDEkeG7lWkuDQotIEJp4buDdSDEkeG7kyBRLVEgY2h14bqpbiDEkeG7gyBraeG7g20gdHJhIGdp4bqjIMSR4buLbmggcGjDom4gcGjhu5FpIGNodeG6qW4gY+G7p2EgcGjhuqduIGTGsC4NCi0gS2hv4bqjbmcgY8OhY2ggQ29vayDEkeG7gyB4w6FjIMSR4buLbmggxJFp4buDbSBjw7Mg4bqjbmggaMaw4bufbmcgbOG7m24gdHJvbmcgbcO0IGjDrG5oLg0KLSBLaeG7g20gdHJhIHThu7EgdMawxqFuZyBxdWFuIHBo4bqnbiBkxrAgcXVhIGJp4buDdSDEkeG7kyBwaOG6p24gZMawIHNvIHbhu5tpIHBo4bqnbiBkxrAgbGnhu4FuIHRyxrDhu5tjLg0KLSBCaeG7g3UgxJHhu5MgcGjhuqduIGTGsCBzbyB24bubaSBiaeG6v24gZ2nhuqNpIHRow61jaCwgdsOtIGThu6UgYmnhu4N1IMSR4buTICJQYXJ0aWFsIGZvciBIdCIuDQotIFjDonkgZOG7sW5nIG3DtCBow6xuaCBi4bqxbmcgY8OhY2ggw6FwIGThu6VuZyBwaMOpcCBiaeG6v24gxJHhu5VpIChsb2csIGPEg24gYuG6rWMgaGFpKSBjaG8gYmnhur9uIMSRw6FwIOG7qW5nIGhv4bq3YyBiaeG6v24gZ2nhuqNpIHRow61jaCwgxJHDoW5oIGdpw6EgcXVhIGJp4buDdSDEkeG7kyBwaOG6p24gZMawLg0KLSBUaMOqbSBz4buRIGjhuqFuZyDEkWEgdGjhu6ljIMSR4buDIG3DtCBow6xuaCBow7NhIHF1YW4gaOG7hyBwaGkgdHV54bq/biB0w61uaCBnaeG7r2EgYmnhur9uIGdp4bqjaSB0aMOtY2ggdsOgIGJp4bq/biDEkcOhcCDhu6luZy4NCi0gU+G7rSBk4bulbmcgaMOgbSBzcGxpbmUgaOG7k2kgcXV5IChuYXR1cmFsIGN1YmljIHNwbGluZXMsIEItc3BsaW5lcykgxJHhu4MgbcO0IGjDrG5oIGjDs2EgY8OhYyBxdWFuIGjhu4cgcGhpIHR1eeG6v24gcGjhu6ljIHThuqFwIGjGoW4uDQotIFhlbSB4w6l0IHRow6ptIHPhu5EgaOG6oW5nIHTGsMahbmcgdMOhYyBnaeG7r2EgY8OhYyBiaeG6v24gZ2nhuqNpIHRow61jaCwgdHXDom4gdGhlbyBuZ3V5w6puIGzDvSBiacOqbiAobWFyZ2luYWxpdHkgcHJpbmNpcGxlKS4NCi0gU28gc8OhbmggY8OhYyBtw7QgaMOsbmggYuG6sW5nIGtp4buDbSDEkeG7i25oIHQtdGVzdCwgRi10ZXN0LCBi4bqjbmcgQU5PVkEgdsOgIHRpw6p1IGNow60gQUlDLg0KLSBEaeG7hW4gZ2nhuqNpIMO9IG5naMSpYSBjw6FjIGjhu4cgc+G7kSBo4buTaSBxdXkgdHJvbmcgYuG7kWkgY+G6o25oIGPDoWMgYmnhur9uIGtow6FjIHbDoCBiaeG6v24gxJHhu5VpIGJp4bq/biDEkcOhcCDhu6luZy4NCi0gVsOtIGThu6UgbWluaCBo4buNYSBz4butIGThu6VuZyBuaGnhu4F1IGLhu5kgZOG7ryBsaeG7h3UgxJFhIGThuqFuZyBuaMawIGThu68gbGnhu4d1IGx1bmdjYXAsIG5hdmFsIGhvc3BpdGFsLCBydW1pbmFudCwgQXVzdHJhbGlhbiBjaXRpZXMsIHbDoCBuaGnhu4F1IGjGoW4gbuG7r2EuDQoNCiMjIyAqKkNoxrDGoW5nIDQ6IFbGsOG7o3QgcmEgbmdvw6BpIGjhu5NpIHF1eSB0dXnhur9uIHTDrW5oOiBQaMawxqFuZyBwaMOhcCDGsOG7m2MgbMaw4bujbmcgaOG7o3AgbMO9IGPhu7FjIMSR4bqhSSoqDQoNCkNoxrDGoW5nIDQgZ2nhu5tpIHRoaeG7h3UgcGjGsMahbmcgcGjDoXAgxrDhu5tjIGzGsOG7o25nIGjhu6NwIGzDvSBj4buxYyDEkeG6oWkgKE1MRSkgbmjGsCBt4buZdCBjw6FjaCB0aeG6v3AgY+G6rW4gdOG7lW5nIHF1w6F0IGNobyBjw6FjIG3DtCBow6xuaCBraMO0bmcgcGjDuSBo4bujcCB24bubaSBo4buTaSBxdXkgdHV54bq/biB0w61uaCBjaHXhuqluOg0KDQotIFRyw6xuaCBiw6B5IGzDvSBkbyBj4bqnbiB2xrDhu6N0IHJhIG5nb8OgaSBtw7QgaMOsbmggaOG7k2kgcXV5IHR1eeG6v24gdMOtbmgga2hpIGJp4bq/biDEkcOhcCDhu6luZyBraMO0bmcgdGjhu49hIG3Do24gZ2nhuqMgxJHhu4tuaCAodsOtIGThu6U6IGJp4bq/biB04bu3IGzhu4csIGJp4bq/biDEkeG6v20sIGJp4bq/biBkxrDGoW5nIGxpw6puIHThu6VjKS4NCi0gR2nhu5tpIHRoaeG7h3UgZ2lhIMSRw6xuaCBwaMOibiBwaOG7kWkgKGZhbWlseSBvZiBkaXN0cmlidXRpb25zKSBsw6BtIGPGoSBz4bufIG3DtCBow6xuaCBow7NhIGThu68gbGnhu4d1Lg0KLSBQaMawxqFuZyBwaMOhcCBNTEUgY2jhu41uIHRoYW0gc+G7kSDEkeG7gyBj4buxYyDEkeG6oWkgaMOzYSBow6BtIGjhu6NwIGzDvSAobGlrZWxpaG9vZCksIGhv4bq3YyBsb2ctaOG7o3AgbMO9Lg0KLSDGr+G7m2MgbMaw4bujbmcgdGhhbSBz4buRIGLhurFuZyBNTEUgZOG7sWEgdHLDqm4gZ2nhuqNpIHBoxrDGoW5nIHRyw6xuaCDEkWnhu4NtIHPhu5EgKHNjb3JlIGZ1bmN0aW9uKSB2w6Agc+G7rSBk4bulbmcgdGjDtG5nIHRpbiBGaXNoZXIgxJHhu4MgxJHDoW5oIGdpw6Egc2FpIHPhu5EgY2h14bqpbi4NCi0gVGh14bqtdCB0b8OhbiBGaXNoZXIgU2NvcmluZyDEkcaw4bujYyBz4butIGThu6VuZyDEkeG7gyB0w61uaCB0b8OhbiBNTEUgaGnhu4d1IHF14bqjLg0KLSBUw61uaCBjaOG6pXQgY+G7p2EgTUxFIGfhu5NtIGtow7RuZyBjaOG7h2NoIHRp4buHbSBj4bqtbiwgaGnhu4d1IHF14bqjLCBuaOG6pXQgcXXDoW4sIHBow6JuIHBo4buRaSBjaHXhuqluIHRp4buHbSBj4bqtbiwgdsOgIGLhuqV0IGJp4bq/bi4NCi0gQmEga2nhu4NtIMSR4buLbmggZ2nhuqMgdGh1eeG6v3QgZOG7sWEgdHLDqm4gTUxFOiBraeG7g20gxJHhu4tuaCBXYWxkLCBraeG7g20gxJHhu4tuaCDEkWnhu4NtIHPhu5EsIHbDoCBraeG7g20gxJHhu4tuaCB04bu3IGzhu4cgaOG7o3AgbMO9IChMUlQpLg0KLSBYw6J5IGThu7FuZyBraG/huqNuZyB0aW4gY+G6rXkgZOG7sWEgdHLDqm4gY8OhYyBraeG7g20gxJHhu4tuaCBuw6B5Lg0KLSBTbyBzw6FuaCBjw6FjIG3DtCBow6xuaCBraMO0bmcgbOG7k25nIG5oYXUgYuG6sW5nIHRpw6p1IGNow60gQUlDIHbDoCBCSUMgxJHhu4MgY2jhu41uIG3DtCBow6xuaCB04buRdCBuaOG6pXQuDQoNCiMjIyAqKkNoxrDGoW5nIDU6IE3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNcyk6IEPhuqV1IHRyw7pjKioNCg0KLSBHaeG7m2kgdGhp4buHdSBtw7QgaMOsbmggdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQgKEdMTXMpIG5oxrAgbeG7nyBy4buZbmcgY+G7p2EgaOG7k2kgcXV5IHR1eeG6v24gdMOtbmguDQotIEdMTXMga2jDoWMgaOG7k2kgcXV5IHR1eeG6v24gdMOtbmgg4bufIGNo4buXIGtow7RuZyBjaOG7iSBnaeG6oyDEkeG7i25oIHBoxrDGoW5nIHNhaSBraMO0bmcgxJHhu5VpIG3DoCBjw7JuIGNobyBwaMOpcCB0aMOgbmggcGjhuqduIG5n4bqrdSBuaGnDqm4gdOG7qyBo4buNIHBow6JuIHBo4buRaSB04buVbmcgcXXDoXQgaMahbiAoaOG7jSBwaMOibiBwaOG7kWkgbcWpIC0gRURNKS4NCi0gR0xNcyBn4buTbSAyIHRow6BuaCBwaOG6p24gY2jDrW5oOg0KDQogIC0gVGjDoG5oIHBo4bqnbiBuZ+G6q3Ugbmhpw6puOiBiaeG6v24gcGjhuqNuIGjhu5NpIHRoZW8gcGjDom4gcGjhu5FpIHRodeG7mWMgaOG7jSBFRE0gKE5vcm1hbCwgUG9pc3NvbiwgR2FtbWEsIEJpbm9taWFsLCBOZWdhdGl2ZSBCaW5vbWlhbC4uLikuDQogIC0gVGjDoG5oIHBo4bqnbiBo4buHIHRo4buRbmc6IG3hu5FpIGxpw6puIGjhu4cgZ2nhu69hIGJp4bq/biBnaeG6o2kgdGjDrWNoIHbDoCB0cnVuZyBiw6xuaCBiaeG6v24gcGjhuqNuIGjhu5NpIFwoXG11XCkgdGjDtG5nIHF1YSBow6BtIGxpw6puIGvhur90IFwoZygpXCksIHbhu5tpIGLhu5kgZOG7sSBiw6FvIHR1eeG6v24gdMOtbmggXChcZXRhID0gXGJldGFfMCArIFxzdW0gXGJldGFfaiB4X2pcKS4NCiAgDQotIEjDoG0geMOhYyBzdeG6pXQgdOG7lW5nIHF1w6F0IGPhu6dhIEVETTogXChQKHk7IFx0aGV0YSwgXHBoaSkgPSBhKHksIFxwaGkpIFxleHBcbGVmdFx7XGZyYWN7eVx0aGV0YSAtIFxrYXBwYShcdGhldGEpfXtccGhpfVxyaWdodFx9XCksIHRyb25nIMSRw7MgXChccGhpXCkgbMOgIHRoYW0gc+G7kSBwaMOibiB0w6FuLg0KDQogIC0gVuG7m2kgUG9pc3NvbiB2w6AgQmlub21pYWwsIFwoXHBoaVwpIMSRw6MgYmnhur90ICh0aMaw4budbmcgbMOgIDEpLg0KICAtIFbhu5tpIE5vcm1hbCwgR2FtbWEsIE5lZ2F0aXZlIEJpbm9taWFsLCBcKFxwaGlcKSBj4bqnbiDGsOG7m2MgbMaw4bujbmcuDQogIA0KLSBUaGFtIHPhu5Egb2Zmc2V0IChiaeG6v24gYmnhur90IHRyxrDhu5tjLCBraMO0bmcgxrDhu5tjIGzGsOG7o25nKSBjw7MgdGjhu4MgeHXhuqV0IGhp4buHbiB0cm9uZyB0aMOgbmggcGjhuqduIGjhu4cgdGjhu5FuZy4NCi0gQ+G6pXUgdHLDumMgR0xNIMSRxrDhu6NjIGvDvSBoaeG7h3U6IGBnbG0oZWRtOyBsaW5rIGZ1bmN0aW9uKWAuDQotIMSQ4buZIGzhu4djaCB04buVbmcgKFRvdGFsIERldmlhbmNlKSBcKEQoeSwgXG11KSA9IFxzdW0gd19pIGQoeV9pLCBcbXVfaSlcKSwgZMO5bmcgxJHhu4MgxJFvIHNhaSBraMOhYyBnaeG7r2EgZOG7ryBsaeG7h3UgdsOgIG3DtCBow6xuaC4NCg0KICAtIFbhu5tpIG3DtCBow6xuaCB0dXnhur9uIHTDrW5oIGNodeG6qW4sIMSR4buZIGzhu4djaCBsw6AgdOG7lW5nIGLDrG5oIHBoxrDGoW5nIHNhaSBraMOhYy4NCiAgLSBcKEQoeSwgXG11KS9ccGhpXCkgeOG6pXAgeOG7iSBwaMOibiBwaOG7kWkgXChcY2hpXjJcKSBraGkgXChccGhpIFx0byAwXCkuDQogIC0gxJDhu5kgbOG7h2NoIMSRxqFuIHbhu4sgY8OzIHBow6JuIHBo4buRaSBjaGktYsOsbmggcGjGsMahbmcgY2jDrW5oIHjDoWMgY2hvIG3hu5l0IHPhu5EgcGjDom4gcGjhu5FpIChOb3JtYWwsIEludmVyc2UgR2F1c3NpYW4pIHbDoCB44bqlcCB44buJIGNobyBjw6FjIHBow6JuIHBo4buRaSBraMOhYy4NCg0KIyMjICoqQ2jGsMahbmcgNjogTcO0IGjDrG5oIHR1eeG6v24gdMOtbmggdOG7lW5nIHF1w6F0IChHTE1zKTogxq/hu5tjIGzGsOG7o25nKioNCg0KLSBU4bqtcCB0cnVuZyDGsOG7m2MgbMaw4bujbmcgdGhhbSBz4buRIGjhu5NpIHF1eSAoXChcYmV0YVwpKSB2w6AgdGhhbSBz4buRIHBow6JuIHTDoW4gKFwoXHBoaVwpKSB0cm9uZyBHTE1zLg0KLSBE4buxYSB0csOqbiBnaeG6oyDEkeG7i25oIHBow6JuIHBo4buRaSBFRE0sIHPhu60gZOG7pW5nIHBoxrDGoW5nIHBow6FwIMaw4bubYyBsxrDhu6NuZyBo4bujcCBsw70gY+G7sWMgxJHhuqFpIChNTEUpLg0KLSBQaMawxqFuZyB0csOsbmggxJFp4buDbSAoU2NvcmUgZXF1YXRpb25zKSB2w6AgdGjDtG5nIHRpbiBGaXNoZXIgxJHGsOG7o2MgcGjDoXQgdHJp4buDbiDEkeG7gyB4w6J5IGThu7FuZyB0aHXhuq10IHRvw6FuIMaw4bubYyBsxrDhu6NuZy4NCi0gxJDhu5kgbOG7h2NoIGTGsCAoUmVzaWR1YWwgRGV2aWFuY2UpIGTDuW5nIMSR4buDIMSRbyBiaeG6v24gdGhpw6puIGNoxrBhIMSRxrDhu6NjIGdp4bqjaSB0aMOtY2ggc2F1IGtoaSBmaXR0ZWQgbcO0IGjDrG5oLg0KLSBTYWkgc+G7kSBjaHXhuqluIChTdGFuZGFyZCBlcnJvcnMpIGPhu6dhIHRoYW0gc+G7kSDEkcaw4bujYyB0w61uaCB0b8OhbiBxdWEgY8O0bmcgdGjhu6ljIG1hIHRy4bqtbi4NCi0gVGh14bqtdCB0b8OhbiDGsOG7m2MgbMaw4bujbmcgR0xNIHTGsMahbmcgdOG7sSBo4buTaSBxdXkgdHV54bq/biB0w61uaCB24bubaSB0w61uaCBjaOG6pXQgY+G7pWMgYuG7mS4NCi0gxq/hu5tjIGzGsOG7o25nIHRoYW0gc+G7kSBwaMOibiB0w6FuIFwoXHBoaVwpIGPDsyBuaGnhu4F1IGPDoWNoOiBNTEUsIGxvZy1saWtlbGlob29kIGJpw6puIHPhu61hIMSR4buVaSwgdHJ1bmcgYsOsbmggxJHhu5kgbOG7h2NoLCBQZWFyc29uLg0KLSDhu6huZyBk4bulbmcgUjoNCg0KICAtIEjDoG0gYGdsbSgpYCBkw7luZyDEkeG7gyBmaXR0ZWQgR0xNcy4NCiAgLSBUaGFtIHPhu5EgYGZhbWlseWAgeMOhYyDEkeG7i25oIGjhu40gcGjDom4gcGjhu5FpIEVETSAoZ2F1c3NpYW4sIGJpbm9taWFsLCBwb2lzc29uLCBHYW1tYSwgaW52ZXJzZS5nYXVzc2lhbikuDQogIC0gQ8OhYyBo4buNICJxdWFzaSIgKHF1YXNpLCBxdWFzaWJpbm9taWFsLCBxdWFzaXBvaXNzb24pIGTDuW5nIGNobyB0csaw4budbmcgaOG7o3AgcGjGsMahbmcgc2FpIHRoYXkgxJHhu5VpIGhv4bq3YyBxdcOhIHBow6JuIHTDoW4gKG92ZXJkaXNwZXJzaW9uKS4NCiAgLSBIw6BtIGB0d2VlZGllKClgIHRyb25nIGfDs2kgYHN0YXRtb2RgIGNobyBwaMOpcCBmaXR0ZWQgR0xNcyBUd2VlZGllLg0KICAtIEPDoWMgaMOgbSBsacOqbiBr4bq/dCBt4bq3YyDEkeG7i25oIHTGsMahbmcg4bupbmcgduG7m2kgdOG7q25nIGjhu40gcGjDom4gcGjhu5FpIChsb2dpdCwgbG9nLCBpZGVudGl0eSwgaW52ZXJzZSkuDQoNCiMjIyAqKkNoxrDGoW5nIDc6IE3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNcyk6IFN1eSBsdeG6rW4qKg0KDQotIMOBcCBk4bulbmcgYmEgcGjGsMahbmcgcGjDoXAgc3V5IGx14bqtbiB0csOqbiBsw70gdGh1eeG6v3QgaOG7o3AgbMO9IGPhu7FjIMSR4bqhaTogKipXYWxkKiosICoqU2NvcmUqKiwgKipMaWtlbGlob29kIFJhdGlvIFRlc3QgKExSVCkqKiB0cm9uZyBHTE1zLg0KLSBLaGkgdGhhbSBz4buRIHBow6JuIHTDoW4gz4YgxJHDoyBiaeG6v3Q6DQoNCiAgLSBT4butIGThu6VuZyBraeG7g20gxJHhu4tuaCBXYWxkLCBraG/huqNuZyB0aW4gY+G6rXkgY2hvIHThu6tuZyBo4buHIHPhu5EgdsOgIGdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggzrwuDQogIC0gTFJUIHNvIHPDoW5oIGPDoWMgbcO0IGjDrG5oIGzhu5NuZyBuaGF1IHbhu5tpIHRo4buRbmcga8OqIHBow6JuIHBo4buRaSDPh8KyLg0KICAtIELhuqNuZyBwaMOibiB0w61jaCDEkeG7mSBs4buHY2ggKEFuYWx5c2lzIG9mIERldmlhbmNlKSBo4buXIHRy4bujIHNvIHPDoW5oIG3DtCBow6xuaC4NCiAgLSBLaeG7g20gxJHhu4tuaCBTY29yZSBjxaluZyDEkcaw4bujYyBz4butIGThu6VuZy4NCiAgDQotIEvhur90IHF14bqjIHBow6JuIHBo4buRaSBjw6FjIHRo4buRbmcga8OqIGThu7FhIHRyw6puIHjhuqVwIHjhu4kgbeG6q3UgbOG7m24gKExhcmdlIFNhbXBsZSBBc3ltcHRvdGljcykuDQotIEtp4buDbSDEkeG7i25oIMSR4buZIHBow7kgaOG7o3AgKEdvb2RuZXNzLW9mLUZpdCBUZXN0cykgxJHhu4Mga2nhu4NtIHRyYSBi4buZIGThu7EgYsOhbyB0dXnhur9uIHTDrW5oIG3DtCB04bqjIMSR4bqneSDEkeG7pyB4dSBoxrDhu5tuZyBk4buvIGxp4buHdSBraMO0bmcsIGThu7FhIHRyw6puIHBow6JuIHBo4buRaSBwaMOibiB0w6FuIG5o4buPIChTbWFsbCBEaXNwZXJzaW9uIEFzeW1wdG90aWNzKS4NCi0gS2hpIHRoYW0gc+G7kSBwaMOibiB0w6FuIM+GIGNoxrBhIGJp4bq/dDoNCg0KICAtIEPDoWMga2nhu4NtIMSR4buLbmggV2FsZCB2w6Aga2hv4bqjbmcgdGluIGPhuq15IHbhuqtuIMSRxrDhu6NjIGTDuW5nLg0KICAtIExSVCBzbyBzw6FuaCBtw7QgaMOsbmggZMO5bmcgdGjhu5FuZyBrw6ogRi10ZXN0Lg0KICAtIELhuqNuZyBwaMOibiB0w61jaCDEkeG7mSBs4buHY2ggY8WpbmcgZOG7sWEgdHLDqm4gRi10ZXN0Lg0KICANCi0gU28gc8OhbmggMyBiw6BpIGtp4buDbSB0cmEgKipXYWxkKiosICoqU2NvcmUqKiB2w6AgKipMUlQqKi4NCi0gTOG7sWEgY2jhu41uIGdp4buvYSBjw6FjIEdMTXMga2jDtG5nIGzhu5NuZyBuaGF1IGTDuW5nICoqQUlDKiogdsOgICoqQklDKiouDQotIEPDoWMgcGjGsMahbmcgcGjDoXAgdOG7sSDEkeG7mW5nIGzhu7FhIGNo4buNbiBtw7QgaMOsbmggxJHGsOG7o2MgZ2nhu5tpIHRoaeG7h3UuDQotIOG7qG5nIGThu6VuZyB0cm9uZyBSOg0KDQogIC0gYHN1bW1hcnkoKWAgxJHhu4MgeGVtIGvhur90IHF14bqjIGtp4buDbSDEkeG7i25oIFdhbGQuDQogIC0gYGdsbS5zY29yZXRlc3QoKWAgKGfDs2kgKipzdGF0bW9kKiopIGNobyBraeG7g20gxJHhu4tuaCBTY29yZS4NCiAgLSBgYW5vdmEoKWAgxJHhu4Mgc28gc8OhbmggY8OhYyBtw7QgaMOsbmggbOG7k25nIG5oYXUgYuG6sW5nIHF1YXNpLWxpa2VsaWhvb2QsIGNobyBr4bq/dCBxdeG6oyBGLXRlc3RzIGtoaSDPhiDEkcaw4bujYyDGsOG7m2MgbMaw4bujbmcuDQoNCiMjIyAqKkNoxrDGoW5nIDg6IE3DtCBow6xuaCB0dXnhur9uIHTDrW5oIHThu5VuZyBxdcOhdCAoR0xNcyk6IENo4bqpbiDEkW/DoW4qKg0KDQotIENoxrDGoW5nIG7DoHkgdGjhuqNvIGx14bqtbiBjw6FjIHBoxrDGoW5nIHBow6FwIMSR4buDIMSRw6FuaCBnacOhICoqdMOtbmggaOG7o3AgbOG7hyBj4bunYSBjw6FjIGdp4bqjIMSR4buLbmgqKiB0cm9uZyBHTE1zLg0KLSBDw6FjIGdp4bqjIMSR4buLbmggY2jDrW5oIGPhu6dhIEdMTXMgYmFvIGfhu5NtOg0KDQogIC0gU+G7sSBwaMO5IGjhu6NwIGPhu6dhIG3DtCBow6xuaCB04buVbmcgdGjhu4MuDQogIC0gQmnhur9uIHBo4bqjbiBo4buTaSDEkeG6v24gdOG7qyBo4buNIHBow6JuIHBo4buRaSBFRE0gxJHGsOG7o2MgY2jhu4kgxJHhu4tuaC4NCiAgLSBUw61uaCDEkcO6bmcgxJHhuq9uIGPhu6dhIGLhu5kgZOG7sSBiw6FvIHR1eeG6v24gdMOtbmggKExpbmVhciBQcmVkaWN0b3IpLg0KICAtIFBoxrDGoW5nIHNhaSBraMO0bmcgxJHhu5VpIChDb25zdGFudCBWYXJpYW5jZSkuDQogIC0gVMOtbmggxJHhu5ljIGzhuq1wIChJbmRlcGVuZGVuY2UpLg0KICAtIFTDrW5oIGNodeG6qW4gKE5vcm1hbGl0eSkg4oCUIGNo4buJIMOhcCBk4bulbmcgY2hvIEdMTSBjaHXhuqluLg0KICAtIFRoYW5nIMSRbyBsxrDhu51uZyAoTWVhc3VyZW1lbnQgU2NhbGVzKS4NCiAgDQotICoqUGjhuqduIGTGsCAoUmVzaWR1YWxzKSBjaG8gR0xNcyoqOg0KDQogIC0gUGjhuqduIGTGsCBwaOG6o24gaOG7k2kgKFJlc3BvbnNlIFJlc2lkdWFscykga2jDtG5nIMSR4bunIMSR4buDIGNo4bqpbiDEkW/DoW4gdHJvbmcgR0xNcy4NCiAgLSBDw6FjIGxv4bqhaSBwaOG6p24gZMawIGjhu691IMOtY2ggaMahbjoNCiAgDQogICAgLSAqKlBlYXJzb24gUmVzaWR1YWxzKiouDQogICAgLSAqKkRldmlhbmNlIFJlc2lkdWFscyoqLg0KICAgIC0gKipRdWFudGlsZSBSZXNpZHVhbHMqKiAoY8OybiBn4buNaSBsw6AgUmFuZG9taXplZCBRdWFudGlsZSBSZXNpZHVhbHMpLg0KICAgIA0KLSAqKsSQw7JuIGLhuql5IChMZXZlcmFnZXMpIHRyb25nIEdMTXMqKjoNCiAgLSBMw6AgdGjGsOG7m2MgxJFvIG3hu6ljIMSR4buZIOG6o25oIGjGsOG7n25nIGPhu6dhIHThu6tuZyBxdWFuIHPDoXQgbMOqbiBmaXR0ZWQgbW9kZWwuDQogIC0gS2jDoWkgbmnhu4dtICoqd29ya2luZyBsZXZlcmFnZXMqKiDEkcaw4bujYyBz4butIGThu6VuZy4NCiAgLSAqKkhhdCBtYXRyaXgqKiBjxaluZyDEkcaw4bujYyB0aOG6o28gbHXhuq1uLg0KICANCi0gKipQaOG6p24gZMawIGNodeG6qW4gaMOzYSB0aGVvIMSRw7JuIGLhuql5KiogKExldmVyYWdlIFN0YW5kYXJkaXplZCBSZXNpZHVhbHMpIGNobyBHTE1zIMSRxrDhu6NjIHRyw6xuaCBiw6B5Lg0KLSBIxrDhu5tuZyBk4bqrbiBs4buxYSBjaOG7jW4gbG/huqFpIHBo4bqnbiBkxrAgcGjDuSBo4bujcCDEkeG7gyBz4butIGThu6VuZyB0cm9uZyBjaOG6qW4gxJFvw6FuLg0KLSBWaeG7h2Mga2nhu4NtIHRyYSBjw6FjIGdp4bqjIMSR4buLbmggbcO0IGjDrG5oIMSRxrDhu6NjIHRo4buxYyBoaeG7h24gYuG6sW5nIGPDoWNoIHPhu60gZOG7pW5nIGPDoWMgY8O0bmcgY+G7pSBjaOG6qW4gxJFvw6FuIG7DoHksIHRoxrDhu51uZyBxdWEgY8OhYyAqKmJp4buDdSDEkeG7kyBwaOG6p24gZMawKiouDQotIEPDoWMgbcO0IGjDrG5oICoqUXVhc2ktUG9pc3NvbioqIChgcXVhc2lwb2lzc29uKClgKSB2w6AgKipRdWFzaS1iaW5vbWlhbCoqIChgcXVhc2liaW5vbWlhbCgpYCkgxJHGsOG7o2MgxJHhu4EgY+G6rXAgbOG6oWkgbmjGsCBs4buxYSBjaOG7jW4gY2hvIGThu68gbGnhu4d1IHF1w6EgcGjDom4gdMOhbi4NCi0gU3V5IGx14bqtbiBjaG8gY8OhYyBtw7QgaMOsbmggcXVhc2kgbsOgeSBz4butIGThu6VuZyBjw6FjIGjDoG0gUiB0xrDGoW5nIHThu7EgR0xNcyB0aMO0bmcgdGjGsOG7nW5nIChgc3VtbWFyeSgpYCwgYGdsbS5zY29yZXRlc3QoKWAsIGBhbm92YSgpYCksIG5oxrBuZyB24bubaSAqKkYtdGVzdHMgZOG7sWEgdHLDqm4gxJHhu5kgbOG7h2NoKiouDQoNCiMjIyAqKkNoxrDGoW5nIDk6IE3DtCBow6xuaCB04bu3IGzhu4cg4oCTIEJpbm9taWFsIEdMTXMqKg0KDQotIFThuq1wIHRydW5nIHbDoG8gR0xNIG5o4buLIHRo4bupYyBkw7luZyDEkeG7gyBtw7QgaMOsbmggaMOzYSB04bu3IGzhu4cgKHPhu5EgdGjDoG5oIGPDtG5nIHRyw6puIHThu5VuZyBz4buRIHRo4butIG5naGnhu4dtKS4NCi0gUGjDom4gcGjhu5FpIEJpbm9taWFsIHRodeG7mWMgaOG7jSBwaMOibiBwaOG7kWkgbcWpIChFRE0pLg0KLSBDw6FjIGjDoG0gbGnDqm4ga+G6v3QgcGjhu5UgYmnhur9uOg0KDQogIC0gKipsb2dpdCoqIChow6BtIGNow61uaCB04bqvYyksIA0KICAtICoqcHJvYml0KiosIA0KICAtICoqY29tcGxlbWVudGFyeSBsb2ctbG9nKiosIA0KICAtICoqY2F1Y2hpdCoqLg0KICANCi0gRGnhu4VuIGdp4bqjaSDDvSBuZ2jEqWEgbmfGsOG7oW5nIGPhu6dhIGjDoG0gbGnDqm4ga+G6v3QuDQotIEdp4bqjaSB0aMOtY2gga+G6v3QgcXXhuqMgcXVhIG9kZHMgdsOgIG9kZHMgcmF0aW9zIHbhu5tpIGjDoG0gbG9naXQuDQotIOG7qG5nIGThu6VuZyB0cm9uZyDGsOG7m2MgbMaw4bujbmcgbGnhu4F1IGhp4buHdSBxdeG6oyB0cnVuZyBiw6xuaCAqKkVENTAqKi4NCi0gVuG6pW4gxJHhu4EgcXXDoSBwaMOibiB0w6FuIHbDoCB44butIGzDvSBi4bqxbmcgKipxdWFzaS1iaW5vbWlhbCBtb2RlbHMqKi4NCi0gQ+G6o25oIGLDoW8gduG7gSAqKkhhdWNrLURvbm5lciBlZmZlY3QqKiBraGkgZMO5bmcgV2FsZCB0ZXN0cy4NCi0gS2nhu4NtIMSR4buLbmggR29vZG5lc3Mtb2YtRml0IGtow7RuZyB0aMOtY2ggaOG7o3AgduG7m2kgZOG7ryBsaeG7h3Ugbmjhu4sgcGjDom4uDQotIFPhu60gZOG7pW5nIFIgcXVhIGBnbG0oZmFtaWx5ID0gYmlub21pYWwoKSlgLg0KDQojIyMgKipDaMawxqFuZyAxMDogTcO0IGjDrG5oIGNobyBk4buvIGxp4buHdSDEkeG6v20g4oCTIFBvaXNzb24gJiBOZWdhdGl2ZSBCaW5vbWlhbCoqDQoNCi0gR0xNcyBjaG8gZOG7ryBsaeG7h3UgxJHhur9tLCBjaOG7pyB54bq/dSBz4butIGThu6VuZyBwaMOibiBwaOG7kWkgKipQb2lzc29uKiogKHRodeG7mWMgRURNKS4NCi0gSMOgbSB4w6FjIHN14bqldCBQb2lzc29uIHbhu5tpIHRydW5nIGLDrG5oIM68ID4gMCwgdGhhbSBz4buRIHBow6JuIHTDoW4gz4YgPSAxLCBwaMawxqFuZyBzYWkgVijOvCkgPSDOvC4NCi0gSMOgbSBsacOqbiBr4bq/dCAqKmxvZyoqIGzDoCBow6BtIGNow61uaCB04bqvYy4NCi0gTcO0IGjDrG5oIFBvaXNzb24gR0xNcyB2w6AgY8OhY2gga2hhaSB0cmnhu4NuIHRyb25nIFIgKGBmYW1pbHkgPSBwb2lzc29uKClgKS4NCi0gTcO0IGjDrG5oIHThu7cgbOG7hyAocmF0ZXMpIHPhu60gZOG7pW5nICoqb2Zmc2V0KiogbMOgIGxvZyhleHBvc3VyZSkuDQotIE3DtCBow6xuaCBsb2ctbGluZWFyIGNobyBi4bqjbmcgdOG6p24gc3XhuqV0IHbhu5tpIGJp4bq/biDEkeG7i25oIHTDrW5oLg0KLSBC4bqjbmcgdOG6p24gc3XhuqV0IMSRYSBjaGnhu4F1LCBoaeG7h3Ug4bupbmcgY2jDrW5oIHbDoCB0xrDGoW5nIHTDoWMsIG5naOG7i2NoIGzDvSBTaW1wc29uLg0KLSBW4bqlbiDEkeG7gSAqKnplcm8gaW5mbGF0aW9uKiogdsOgICoqemVybyB0cnVuY2F0aW9uKiogdHJvbmcgZOG7ryBsaeG7h3UgxJHhur9tLg0KLSBY4butIGzDvSBxdcOhIHBow6JuIHTDoW4gYuG6sW5nICoqTmVnYXRpdmUgQmlub21pYWwgR0xNcyoqIHbDoCAqKnF1YXNpLVBvaXNzb24gbW9kZWxzKiouDQoNCiMjIyAqKkNoxrDGoW5nIDExOiBNw7QgaMOsbmggR0xNIHbhu5tpIEThu68gbGnhu4d1IExpw6puIHThu6VjIETGsMahbmcg4oCTIFBow6JuIHBo4buRaSBHYW1tYSoqDQoNCi0gR2nhu5tpIHRoaeG7h3UgY8OhYyBtw7QgaMOsbmggY2hvIGThu68gbGnhu4d1IGxpw6puIHThu6VjIGTGsMahbmcsIHRoxrDhu51uZyBsw6AgxJFvIGzGsOG7nW5nIGPDoWMgxJHhuqFpIGzGsOG7o25nIHbhuq10IGzDvSBsdcO0biBkxrDGoW5nLg0KLSBIYWkgR0xNcyBwaOG7lSBiaeG6v246IGThu7FhIHRyw6puIHBow6JuIHBo4buRaSAqKkdhbW1hKiogdsOgICoqSW52ZXJzZSBHYXVzc2lhbioqIHRodeG7mWMgaOG7jSBFRE0uDQotIFBow6JuIHBo4buRaSBHYW1tYToNCg0KICAtIEjDoG0geMOhYyBzdeG6pXQsDQogIC0gQ8OhYyB0csaw4budbmcgaOG7o3AgxJHhurdjIGJp4buHdCwNCiAgLSDGr+G7m2MgbMaw4bujbmcgdGhhbSBz4buRIHBow6JuIHTDoW4gz4YuDQogIA0KLSBQaMOibiBwaOG7kWkgSW52ZXJzZSBHYXVzc2lhbjoNCg0KICAtIFRodeG7mWMgRURNLA0KICAtIMSQ4buZIGzhu4djaCDEkcahbiB24buLIHBow6JuIHBo4buRaSBjaGktYsOsbmggcGjGsMahbmcgY2jDrW5oIHjDoWMsDQogIC0gxq/hu5tjIGzGsOG7o25nIM+GLg0KICANCi0gQ8OhYyBow6BtIGxpw6puIGvhur90IHPhu60gZOG7pW5nOiAqKmludmVyc2UqKiwgKippZGVudGl0eSoqLCAqKmxvZyoqLg0KLSBIw6BtIGxpw6puIGvhur90IGNow61uaCB04bqvYyBj4bunYSBJbnZlcnNlIEdhdXNzaWFuIGzDoCAxL868wrIuDQotIFPhu60gZOG7pW5nIFIgxJHhu4MgZml0IG3DtCBow6xuaDoNCg0KICAtIGBnbG0oZmFtaWx5ID0gR2FtbWEoKSlgIHbDoCBgZ2xtKGZhbWlseSA9IGludmVyc2UuZ2F1c3NpYW4oKSlgLg0KICANCi0gTGnhu4d0IGvDqiBjw6FjIGjDoG0gbGnDqm4ga+G6v3QgY8OzIHRo4buDIGTDuW5nIGNobyB04burbmcgcGjDom4gcGjhu5FpLg0KLSBDdW5nIGPhuqVwIHbDrSBk4bulIGLhuqNuZyBwaMOibiB0w61jaCDEkeG7mSBs4buHY2ggdOG7qyBtw7QgaMOsbmggR2FtbWEgR0xNLg0KDQojIyMgKipDaMawxqFuZyAxMjogTcO0IGjDrG5oIFR3ZWVkaWUqKg0KDQotIEdp4bubaSB0aGnhu4d1IEdMTXMgZOG7sWEgdHLDqm4gaOG7jSBwaMOibiBwaOG7kWkgKipUd2VlZGllKiogKEVETXMgdOG7lW5nIHF1w6F0IGjGoW4pLg0KDQotIFR3ZWVkaWUgYmFvIGfhu5NtIGPDoWMgcGjDom4gcGjhu5FpIMSR4bq3YyBiaeG7h3QgbmjGsDoNCiAgLSBDaHXhuqluICjOviA9IDApLA0KICAtIFBvaXNzb24gKM6+ID0gMSksDQogIC0gR2FtbWEgKM6+ID0gMiksDQogIC0gSW52ZXJzZSBHYXVzc2lhbiAozr4gPSAzKS4NCiAgDQotIEjDoG0gcGjGsMahbmcgc2FpIGPhu6dhIFR3ZWVkaWU6IFwoIFYoXG11KSA9IFxtdV5ceGkgXCkgduG7m2kgzr4gbMOgIHRoYW0gc+G7kSBjaOG7iSBz4buRIChpbmRleCBwYXJhbWV0ZXIpLCBraMO0bmcgdGh14buZYyBraG/huqNuZyAoMCwxKS4NCi0g4buobmcgZOG7pW5nIFR3ZWVkaWUgdHJvbmcgZOG7ryBsaeG7h3UgbGnDqm4gdOG7pWMgZMawxqFuZyB2w6AgZOG7ryBsaeG7h3UgbGnDqm4gdOG7pWMgZMawxqFuZyBjw7MgZ2nDoSB0cuG7iyAwIGNow61uaCB4w6FjLg0KLSDGr+G7m2MgbMaw4bujbmcgdGhhbSBz4buRIM6+IHbDoCBjw6FjaCBmaXQgbcO0IGjDrG5oIFR3ZWVkaWUgR0xNcy4NCi0gTmdoacOqbiBj4bupdSB0csaw4budbmcgaOG7o3AgbWluaCBo4buNYSBkaeG7hW4gZ2nhuqNpIGPDoWMgcGjDom4gcGjhu5FpIFBvaXNzb24gdsOgIEdhbW1hIHF1YSBow6BtIGB0d2VlZGllLmNvbnZlcnQoKWAgdHJvbmcgUiAoZ8OzaSAqKnR3ZWVkaWUqKikuDQotIFPhu60gZOG7pW5nIFIgduG7m2kgaMOgbSBgdHdlZWRpZSgpYCB0cm9uZyBnw7NpICoqc3RhdG1vZCoqIMSR4buDIGZpdCBUd2VlZGllIEdMTXMuDQotIFRyw6xuaCBiw6B5IMSR4buZIGzhu4djaCDEkcahbiB24buLIHbDoCDEkeG7mSBs4buHY2ggZMawIHBow7kgaOG7o3AgduG7m2kgcGjDom4gcGjhu5FpIM+HwrIuDQoNCi0tLQ0KDQojICoqVEjhu5BORyBLw4ogTcOUIFThuqIgROG7riBMSeG7hlUqKg0KIyMgKipHaeG7m2kgdGhp4buHdSBi4buZIGThu68gbGnhu4d1KioNCioqVOG6o2kgZOG7ryBsaeG7h3UqKg0KDQpgYGB7cn0NCmxpYnJhcnkoImNzdiIpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpkYXRhIDwtIHJlYWQuY3N2KCJEOi9Eb3dubG9hZHMvU3VwZXJtYXJrZXQgVHJhbnNhY3Rpb25zLmNzdiIsIGhlYWRlciA9IFQpDQpkYXRhLnRhYmxlKGRhdGEpDQpuYW1lcyhkYXRhKQ0KYGBgDQoNCkLhu5kgZOG7ryBsaeG7h3UgKipTdXBlcm1hcmtldCBUcmFuc2FjdGlvbnMqKiBiYW8gZ+G7k20gdGjDtG5nIHRpbiBnaWFvIGThu4tjaCBj4bunYSBraMOhY2ggaMOgbmcgdOG6oWkgc2nDqnUgdGjhu4ssIHbhu5tpICoqMTQsMDU5IHF1YW4gc8OhdCoqIHbDoCAqKjE2IGJp4bq/bioqLiANCg0KROG7ryBsaeG7h3UgY2jhu6lhIGPDoWMgdGjDtG5nIHRpbiDEkWEgZOG6oW5nIG5oxrA6DQoNCi0gVGjDtG5nIHRpbiBraMOhY2ggaMOgbmc6IGdp4bubaSB0w61uaCwgdMOsbmggdHLhuqFuZyBow7RuIG5ow6JuLCB0aHUgbmjhuq1wLCBjb24gY8OhaSwgcXV54buBbiBz4bufIGjhu691IG5ow6ANCg0KLSBUaMO0bmcgdGluIMSR4buLYSBsw706IHRow6BuaCBwaOG7kSwgYmFuZywgcXXhu5FjIGdpYQ0KDQotIFRow7RuZyB0aW4gc+G6o24gcGjhuqltOiBuaMOzbSBz4bqjbiBwaOG6qW0sIGxv4bqhaSwgZG9hbmggdGh1LCBz4buRIGzGsOG7o25nIGLDoW4NCg0KKipHaeG6o2kgdGjDrWNoIGPDoWMgYmnhur9uKioNCmBgYHtyfQ0KbGlicmFyeShrbml0cikNCmRmIDwtIGRhdGEuZnJhbWUoDQogIGBUw6puIGJp4bq/bmAgPSBjKCJQdXJjaGFzZURhdGUiLCAiQ3VzdG9tZXJJRCIsICJHZW5kZXIiLCAiTWFyaXRhbFN0YXR1cyIsICJIb21lb3duZXIiLCAiQ2hpbGRyZW4iLA0KICAgICAgICAgICAgICAgICAiQW5udWFsSW5jb21lIiwgIkNpdHkiLCAiU3RhdGVvclByb3ZpbmNlIiwgIkNvdW50cnkiLCAiUHJvZHVjdEZhbWlseSIsDQogICAgICAgICAgICAgICAgICJQcm9kdWN0RGVwYXJ0bWVudCIsICJQcm9kdWN0Q2F0ZWdvcnkiLCAiVW5pdHNTb2xkIiwgIlJldmVudWUiKSwNCiAgYEdp4bqjaSB0aMOtY2hgID0gYygiTmfDoHkgbXVhIGjDoG5nIiwgIk3DoyDEkeG7i25oIGRhbmgga2jDoWNoIGjDoG5nIiwgIkdp4bubaSB0w61uaCBraMOhY2ggaMOgbmcgKGBNYCwgYEZgKSIsDQogICAgICAgICAgICAgICAgICAgIlTDrG5oIHRy4bqhbmcgaMO0biBuaMOibiAoYFNgLCBgTWApIiwgIkPDsyBz4bufIGjhu691IG5ow6AgaGF5IGtow7RuZyAoYFlgLCBgTmApIiwNCiAgICAgICAgICAgICAgICAgICAiU+G7kSBsxrDhu6NuZyBjb24iLCAiVGh1IG5o4bqtcCBow6BuZyBuxINtIHRoZW8ga2hv4bqjbmciLCAiVGjDoG5oIHBo4buRIGPGsCB0csO6IiwNCiAgICAgICAgICAgICAgICAgICAiQmFuZyBob+G6t2MgdOG7iW5oIiwgIlF14buRYyBnaWEiLCAiTmjDs20gc+G6o24gcGjhuqltIGzhu5tuIiwgIkLhu5kgcGjhuq1uIHPhuqNuIHBo4bqpbSBj4bulIHRo4buDIiwNCiAgICAgICAgICAgICAgICAgICAiTG/huqFpIHPhuqNuIHBo4bqpbSBjaGkgdGnhur90IiwgIlPhu5EgbMaw4bujbmcgc+G6o24gcGjhuqltIMSRw6MgYsOhbiIsDQogICAgICAgICAgICAgICAgICAgIkRvYW5oIHRodSB04burIGdpYW8gZOG7i2NoICjEkcahbiB24buLIGtow7RuZyB4w6FjIMSR4buLbmgpIikNCikNCmthYmxlKGRmLCBmb3JtYXQgPSAibWFya2Rvd24iKQ0KYGBgDQoNCiMjICoqVGjhu5FuZyBrw6ogbcO0IHThuqMgY8OhYyBiaeG6v24qKg0KIyMjICoqUGjDom4gdMOtY2ggY8OhYyBiaeG6v24gxJHhu4tuaCB0w61uaCoqDQojIyMjICoqQmnhur9uIEdlbmRlcioqDQoqKkzhuq1wIGLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQqKg0KYGBge3J9DQojIELhuqNuZyB04bqnbiBz4buRDQpnZW5kZXJfZnJlcSA8LSB0YWJsZShkYXRhJEdlbmRlcikNCiMgQuG6o25nIHThuqduIHN14bqldA0KZ2VuZGVyX3Byb3AgPC0gcHJvcC50YWJsZShnZW5kZXJfZnJlcSkNCiMgR+G7mXAgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IHbDoG8gbeG7mXQgYuG6o25nDQpnZW5kZXJfdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgR2VuZGVyID0gbmFtZXMoZ2VuZGVyX2ZyZXEpLA0KICBGcmVxdWVuY3kgPSBhcy52ZWN0b3IoZ2VuZGVyX2ZyZXEpLA0KICBQcm9wb3J0aW9uID0gcm91bmQoYXMudmVjdG9yKGdlbmRlcl9wcm9wKSwgMykNCikNCiMgSGnhu4NuIHRo4buLIGLhuqNuZyB24bubaSBrYWJsZQ0Ka25pdHI6OmthYmxlKGdlbmRlcl90YWJsZSwgY2FwdGlvbiA9ICJC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGPhu6dhIGJp4bq/biBHZW5kZXIiKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQqKg0KROG7sWEgdsOgbyBi4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGPhu6dhIGJp4bq/biAqKkdlbmRlcioqLCB0YSBjw7M6DQoNCi0gU+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgbuG7ryAoYEZgKSBsw6AgKio3MTcwKiosIGNoaeG6v20gKio1MSUqKiB04buVbmcgc+G7kSBxdWFuIHPDoXQuDQoNCi0gU+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgbmFtIChgTWApIGzDoCAqKjY4ODkqKiwgY2hp4bq/bSAqKjQ5JSoqIHThu5VuZyBz4buRIHF1YW4gc8OhdC4NCg0KLSBU4bu3IGzhu4cgZ2nhu5tpIHTDrW5oIGdp4buvYSBuYW0gdsOgIG7hu68gZ+G6p24gbmjGsCBjw6JuIGLhurFuZywgduG7m2kgbuG7ryBjaGnhur9tIHThu7cgbOG7hyBuaOG7iW5oIGjGoW4gbeG7mXQgY2jDunQuDQoNCioqVuG6vSDEkeG7kyB0aOG7iyoqDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpkYXRhICU+JQ0KICBjb3VudChHZW5kZXIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBHZW5kZXIsIHkgPSBuLCBmaWxsID0gR2VuZGVyKSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiRiIgPSAiI0Y0QTdCOSIsICJNIiA9ICIjODlDRkYwIikpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgdOG6p24gc+G7kSB0aGVvIGdp4bubaSB0w61uaCIsDQogICAgeCA9ICJHaeG7m2kgdMOtbmgiLA0KICAgIHkgPSAiU+G7kSBsxrDhu6NuZyINCiAgKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpCaeG7g3UgxJHhu5MgdOG6p24gc+G7kSBjaG8gYmnhur9uICoqR2VuZGVyKiogY2hvIHRo4bqleSBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBuYW0gdsOgIG7hu68gbMOgIHTGsMahbmcgxJHGsMahbmcgbmhhdSwgduG7m2kga2jDoWNoIGjDoG5nIG7hu68gY2hp4bq/bSB04bu3IGzhu4cgbmjhu4luaCBoxqFuLiDEkGnhu4F1IG7DoHkgcGjDuSBo4bujcCB24bubaSBi4bqjbmcgdOG6p24gc+G7kSDhu58gdHLDqm4gdsOgIHjDoWMgbmjhuq1uIHLhurFuZyB04bqtcCBk4buvIGxp4buHdSBraMO0bmcgY8OzIHPhu7EgbeG6pXQgY8OibiBi4bqxbmcgZ2nhu5tpIHTDrW5oIG5naGnDqm0gdHLhu41uZy4NCg0KIyMjIyAqKkJp4bq/biBNYXJpdGFsU3RhdHVzKioNCg0KKipM4bqtcCBi4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0KioNCmBgYHtyfQ0KIyBC4bqjbmcgdOG6p24gc+G7kQ0KbWFyaXRhbF9mcmVxIDwtIHRhYmxlKGRhdGEkTWFyaXRhbFN0YXR1cykNCiMgQuG6o25nIHThuqduIHN14bqldA0KbWFyaXRhbF9wcm9wIDwtIHByb3AudGFibGUobWFyaXRhbF9mcmVxKQ0KIyBH4buZcCB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgdsOgbyBt4buZdCBi4bqjbmcNCm1hcml0YWxfdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgTWFyaXRhbFN0YXR1cyA9IG5hbWVzKG1hcml0YWxfZnJlcSksDQogIEZyZXF1ZW5jeSA9IGFzLnZlY3RvcihtYXJpdGFsX2ZyZXEpLA0KICBQcm9wb3J0aW9uID0gcm91bmQoYXMudmVjdG9yKG1hcml0YWxfcHJvcCksIDMpDQopDQojIEhp4buDbiB0aOG7iyBi4bqjbmcgduG7m2kga2FibGUNCmtuaXRyOjprYWJsZShtYXJpdGFsX3RhYmxlLCBjYXB0aW9uID0gIkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uIE1hcml0YWxTdGF0dXMiKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQ6KioNCkThu7FhIHbDoG8gYuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCBj4bunYSBiaeG6v24gKipNYXJpdGFsU3RhdHVzKiosIHRhIHRo4bqleToNCg0KLSBT4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyAqKsSR4buZYyB0aMOibioqIChgU2ApIGzDoCAqKjcxOTMqKiwgY2hp4bq/bSAqKjUxLjIlKiogdOG7lW5nIHPhu5EgcXVhbiBzw6F0Lg0KDQotIFPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nICoqxJHDoyBr4bq/dCBow7RuKiogKGBNYCkgbMOgICoqNjg2NioqLCBjaGnhur9tICoqNDguOCUqKiB04buVbmcgc+G7kSBxdWFuIHPDoXQuDQoNCi0gVOG7tyBs4buHIGdp4buvYSBoYWkgbmjDs20ga2jDoSBjw6JuIGLhurFuZywgdHJvbmcgxJHDsyBuaMOzbSAqKsSR4buZYyB0aMOibiBjaGnhur9tIMawdSB0aOG6vyBuaOG6uSoqLiAgDQoNCiAgxJBp4buBdSBuw6B5IGNobyB0aOG6pXkgdOG6rXAgZOG7ryBsaeG7h3UgY8OzIHPhu7EgcGjDom4gYuG7kSB0xrDGoW5nIMSR4buRaSDEkeG7k25nIMSR4buBdSBnaeG7r2EgaGFpIHRy4bqhbmcgdGjDoWkgaMO0biBuaMOibi4NCioqVuG6vSDEkeG7kyB0aOG7iyoqDQpgYGB7cn0NCmRhdGEgJT4lDQogIGNvdW50KE1hcml0YWxTdGF0dXMpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBNYXJpdGFsU3RhdHVzLCB5ID0gbiwgZmlsbCA9IE1hcml0YWxTdGF0dXMpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIikgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJTIiA9ICIjRkY5OTk5IiwgIk0iID0gIiM5OTk5RkYiKSkgKyANCiAgbGFicygNCiAgICB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgdOG6p24gc+G7kSB0aGVvIHTDrG5oIHRy4bqhbmcgaMO0biBuaMOibiIsDQogICAgeCA9ICJUw6xuaCB0cuG6oW5nIGjDtG4gbmjDom4iLA0KICAgIHkgPSAiU+G7kSBsxrDhu6NuZyINCiAgKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyMjICoqQmnhur9uIEhvbWVvd25lcioqDQoNCioqTOG6rXAgYuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCoqDQpgYGB7cn0NCiMgQuG6o25nIHThuqduIHPhu5ENCmhvbWVvd25lcl9mcmVxIDwtIHRhYmxlKGRhdGEkSG9tZW93bmVyKQ0KIyBC4bqjbmcgdOG6p24gc3XhuqV0DQpob21lb3duZXJfcHJvcCA8LSBwcm9wLnRhYmxlKGhvbWVvd25lcl9mcmVxKQ0KIyBH4buZcCB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgdsOgbyBt4buZdCBi4bqjbmcNCmhvbWVvd25lcl90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBIb21lb3duZXIgPSBuYW1lcyhob21lb3duZXJfZnJlcSksDQogIEZyZXF1ZW5jeSA9IGFzLnZlY3Rvcihob21lb3duZXJfZnJlcSksDQogIFByb3BvcnRpb24gPSByb3VuZChhcy52ZWN0b3IoaG9tZW93bmVyX3Byb3ApLCAzKQ0KKQ0KIyBIaeG7g24gdGjhu4sgYuG6o25nIHbhu5tpIGthYmxlDQprbml0cjo6a2FibGUoaG9tZW93bmVyX3RhYmxlLCBjYXB0aW9uID0gIkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uIEhvbWVvd25lciIpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKg0KROG7sWEgdsOgbyBi4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGJp4bq/biAqKkhvbWVvd25lcioqLCB0YSB0aOG6pXk6DQoNCi0gU+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgKipraMO0bmcgc+G7nyBo4buvdSBuaMOgKiogKGBOYCkgbMOgIDU2MTUsIGNoaeG6v20gMzkuOSUgdOG7lW5nIHPhu5EgcXVhbiBzw6F0Lg0KDQotIFPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nICoqc+G7nyBo4buvdSBuaMOgKiogKGBZYCkgbMOgIDg0NDQsIGNoaeG6v20gNjAuMSUgdOG7lW5nIHPhu5EgcXVhbiBzw6F0Lg0KDQpU4bu3IGzhu4cgZ2nhu69hIGhhaSBuaMOzbSBraMO0bmcgY8OibiBi4bqxbmcsIHRyb25nIMSRw7MgbmjDs20gc+G7nyBo4buvdSBuaMOgIGNoaeG6v20gxrB1IHRo4bq/IHLDtSBy4buHKi4gxJBp4buBdSBuw6B5IHBo4bqjbiDDoW5oIHLhurFuZyBwaOG6p24gbOG7m24ga2jDoWNoIGjDoG5nIHRyb25nIHThuq1wIGThu68gbGnhu4d1IGzDoCBuaOG7r25nIG5nxrDhu51pIMSRw6Mgc+G7nyBo4buvdSBuaMOgLg0KKipW4bq9IMSR4buTIHRo4buLKioNCmBgYHtyfQ0KZGF0YSAlPiUNCiAgY291bnQoSG9tZW93bmVyKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gSG9tZW93bmVyLCB5ID0gbiwgZmlsbCA9IEhvbWVvd25lcikpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiksIHZqdXN0ID0gLTAuNSwgY29sb3IgPSAiYmxhY2siKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk4iID0gIiNGRjk5OTkiLCAiWSIgPSAiIzk5OTlGRiIpKSArIA0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJp4buDdSDEkeG7kyB04bqnbiBz4buRIHRoZW8gYmnhur9uIEhvbWVvd25lciIsDQogICAgeCA9ICJT4bufIGjhu691IG5ow6AiLA0KICAgIHkgPSAiU+G7kSBsxrDhu6NuZyINCiAgKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyMjICoqQmnhur9uIEFubnVhbEluY29tZSoqDQoNCioqTOG6rXAgYuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCoqDQpgYGB7cn0NCiMgQuG6o25nIHThuqduIHPhu5ENCmFubnVhbGluY29tZV9mcmVxIDwtIHRhYmxlKGRhdGEkQW5udWFsSW5jb21lKQ0KIyBC4bqjbmcgdOG6p24gc3XhuqV0DQphbm51YWxpbmNvbWVfcHJvcCA8LSBwcm9wLnRhYmxlKGFubnVhbGluY29tZV9mcmVxKQ0KIyBH4buZcCB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgdsOgbyBt4buZdCBi4bqjbmcNCmFubnVhbGluY29tZV90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBBbm51YWxJbmNvbWUgPSBuYW1lcyhhbm51YWxpbmNvbWVfZnJlcSksDQogIEZyZXF1ZW5jeSA9IGFzLnZlY3Rvcihhbm51YWxpbmNvbWVfZnJlcSksDQogIFByb3BvcnRpb24gPSByb3VuZChhcy52ZWN0b3IoYW5udWFsaW5jb21lX3Byb3ApLCAzKQ0KKQ0KIyBIaeG7g24gdGjhu4sgYuG6o25nIHbhu5tpIGthYmxlDQprbml0cjo6a2FibGUoYW5udWFsaW5jb21lX3RhYmxlLCBjYXB0aW9uID0gIkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uIEFubnVhbEluY29tZSIpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKg0KROG7sWEgdsOgbyBi4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGJp4bq/biAqKkFubnVhbEluY29tZSoqLCB0YSB0aOG6pXk6DQoNCi0gTmjDs20gKiooJDEwSyAtICQzMEspKiogY8OzIDMwOTAga2jDoWNoIGjDoG5nLCBjaGnhur9tIDIyLjAlIHThu5VuZyBz4buRLCBsw6AgbmjDs20gY8OzIHRodSBuaOG6rXAgdGjhuqVwIG5oxrBuZyBz4buRIGzGsOG7o25nIGtow6EgbOG7m24gdHJvbmcgdOG6rXAgZOG7ryBsaeG7h3UuDQoNCi0gTmjDs20gKiooJDMwSyAtICQ1MEspKiogY2hp4bq/bSB04bu3IGzhu4cgY2FvIG5o4bqldCB24bubaSA0NjAxIGtow6FjaCBow6BuZywgdMawxqFuZyDEkcawxqFuZyAzMi43JSwgY2hvIHRo4bqleSDEkcOieSBsw6AgbeG7qWMgdGh1IG5o4bqtcCBwaOG7lSBiaeG6v24gbmjhuqV0Lg0KDQotIE5ow7NtICoqKCQ1MEsgLSAkNzBLKSoqIGPDsyAyMzcwIGtow6FjaCBow6BuZywgY2hp4bq/bSAxNi45JSwgbMOgIG5ow7NtIHRodSBuaOG6rXAgdHJ1bmcgYsOsbmggdGjhuqVwIHbhu5tpIHPhu5EgbMaw4bujbmcgxJHDoW5nIGvhu4MuDQoNCi0gTmjDs20gKiooJDcwSyAtICQ5MEspKiogY2hp4bq/bSAxMi4yJSB24bubaSAxNzA5IGtow6FjaCBow6BuZywgcGjhuqNuIMOhbmggbeG7qWMgdGh1IG5o4bqtcCB0cnVuZyBiw6xuaCBraMOhLg0KDQotIE5ow7NtICoqKCQ5MEsgLSAkMTEwSykqKiBjw7MgNjEzIGtow6FjaCBow6BuZywgY2hp4bq/bSA0LjQlLCBnaeG6o20gcsO1IHLhu4d0IHNvIHbhu5tpIG5ow7NtIHRo4bqlcCBoxqFuLg0KDQotIE5ow7NtIHRodSBuaOG6rXAgY2FvIGjGoW4gKiooJDExMEsgLSAkMTMwSykqKiBjaGnhur9tIDQuNiUgduG7m2kgNjQzIGtow6FjaCBow6BuZy4NCg0KLSBOaMOzbSAqKigkMTMwSyAtICQxNTBLKSoqIGPDsyA3NjAga2jDoWNoIGjDoG5nLCBjaGnhur9tIDUuNCUsIHRo4buDIGhp4buHbiBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyB0aHUgbmjhuq1wIGtow6EgY2FvIG5oxrBuZyBraMO0bmcgbmhp4buBdS4NCg0KLSBOaMOzbSAqKigkMTUwSyArKSoqIGzDoCBuaMOzbSB0aHUgbmjhuq1wIGNhbyBuaOG6pXQsIGNo4buJIGNoaeG6v20gMS45JSB24bubaSAyNzMga2jDoWNoIGjDoG5nLCBsw6AgbmjDs20gbmjhu48gbmjhuqV0IHRyb25nIHThuq1wIGThu68gbGnhu4d1Lg0KDQpU4bu3IGzhu4cgZ2nhu69hIGPDoWMgbmjDs20gdGh1IG5o4bqtcCBraMO0bmcgY8OibiBi4bqxbmcsIHRyb25nIMSRw7MgY8OhYyBuaMOzbSB0aHUgbmjhuq1wIHRo4bqlcCB2w6AgdHJ1bmcgYsOsbmggY2hp4bq/bSDGsHUgdGjhur8gcsO1IHLhu4d0LiAgDQrEkGnhu4F1IG7DoHkgcGjhuqNuIMOhbmggcuG6sW5nIHBo4bqnbiBs4bubbiBraMOhY2ggaMOgbmcgdHJvbmcgdOG6rXAgZOG7ryBsaeG7h3UgY8OzIG3hu6ljIHRodSBuaOG6rXAgY2jhu6cgeeG6v3Ug4bufIG3hu6ljIHRydW5nIGLDrG5oIHbDoCB0aOG6pXAuDQoNCioqVuG6vSDEkeG7kyB0aOG7iyoqDQpgYGB7cn0NCmRhdGEgJT4lDQogIGNvdW50KEFubnVhbEluY29tZSkgJT4lDQogIGdncGxvdChhZXMoeCA9IEFubnVhbEluY29tZSwgeSA9IG4sIGZpbGwgPSBBbm51YWxJbmNvbWUpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwxIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJp4buDdSDEkeG7kyB04bqnbiBz4buRIHRoZW8gYmnhur9uIEFubnVhbEluY29tZSIsDQogICAgeCA9ICJUaHUgbmjhuq1wIGjhurFuZyBuxINtIiwNCiAgICB5ID0gIlPhu5EgbMaw4bujbmciDQogICkgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkNCmBgYA0KDQoNCiMjIyMgKipCaeG6v24gQ2l0eSoqDQoNCioqTOG6rXAgYuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCoqDQpgYGB7cn0NCiMgQuG6o25nIHThuqduIHPhu5ENCmNpdHlfZnJlcSA8LSB0YWJsZShkYXRhJENpdHkpDQojIELhuqNuZyB04bqnbiBzdeG6pXQNCmNpdHlfcHJvcCA8LSBwcm9wLnRhYmxlKGNpdHlfZnJlcSkNCiMgR+G7mXAgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IHbDoG8gbeG7mXQgYuG6o25nDQpjaXR5X3RhYmxlIDwtIGRhdGEuZnJhbWUoDQogIENpdHkgPSBuYW1lcyhjaXR5X2ZyZXEpLA0KICBGcmVxdWVuY3kgPSBhcy52ZWN0b3IoY2l0eV9mcmVxKSwNCiAgUHJvcG9ydGlvbiA9IHJvdW5kKGFzLnZlY3RvcihjaXR5X3Byb3ApLCAzKQ0KKQ0KIyBIaeG7g24gdGjhu4sgYuG6o25nIHbhu5tpIGthYmxlDQprbml0cjo6a2FibGUoY2l0eV90YWJsZSwgY2FwdGlvbiA9ICJC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGPhu6dhIGJp4bq/biBDaXR5IikNCmBgYA0KDQoqKk5o4bqtbiB4w6l0OioqDQpE4buxYSB2w6BvIGLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uICoqQ2l0eSoqLCB0YSB0aOG6pXk6DQoNCi0gQ8OhYyB0aMOgbmggcGjhu5EgY8OzIHPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nIGNhbyBuaOG6pXQgZ+G7k206DQoNCiAgLSAqKlNhbGVtKio6IDEzODYga2jDoWNoIGjDoG5nICg5LjklKQ0KICANCiAgLSAqKlRhY29tYSoqOiAxMjU3IGtow6FjaCBow6BuZyAoOC45JSkNCiAgDQogIC0gKipMb3MgQW5nZWxlcyoqOiA5MjYga2jDoWNoIGjDoG5nICg2LjYlKQ0KICANCiAgLSAqKlNlYXR0bGUqKjogOTIyIGtow6FjaCBow6BuZyAoNi42JSkNCiAgDQogIC0gKipQb3J0bGFuZCoqOiA4NzYga2jDoWNoIGjDoG5nICg2LjIlKQ0KICANCiAgLSAqKlNhbiBEaWVnbyoqOiA4NjYga2jDoWNoIGjDoG5nICg2LjIlKQ0KICANCiAgLSAqKlNwb2thbmUqKjogODc1IGtow6FjaCBow6BuZyAoNi4yJSkNCiAgDQogIC0gKipIaWRhbGdvKio6IDg0NSBraMOhY2ggaMOgbmcgKDYuMCUpDQogIA0KICAtICoqQnJlbWVydG9uKio6IDgzNCBraMOhY2ggaMOgbmcgKDUuOSUpDQoNCi0gTeG7mXQgc+G7kSB0aMOgbmggcGjhu5EgY8OzIHThu7cgbOG7hyBraMOhY2ggaMOgbmcgdGjhuqVwLCDEkcOhbmcgY2jDuiDDvToNCg0KICAtICoqR3VhZGFsYWphcmEqKjogNzUga2jDoWNoIGjDoG5nICgwLjUlKQ0KICANCiAgLSAqKlNhbiBGcmFuY2lzY28qKjogMTMwIGtow6FjaCBow6BuZyAoMC45JSkNCiAgDQogIC0gKipCZWxsaW5naGFtKio6IDE0MyBraMOhY2ggaMOgbmcgKDEuMCUpDQogIA0KICAtICoqV2FsbGEgV2FsbGEqKjogMTYwIGtow6FjaCBow6BuZyAoMS4xJSkNCiAgDQogIC0gKipWaWN0b3JpYSoqOiAxNzYga2jDoWNoIGjDoG5nICgxLjMlKQ0KDQpU4bu3IGzhu4cgcGjDom4gYuG7kSBraMOhY2ggaMOgbmcgdGhlbyB0aMOgbmggcGjhu5EgbMOgICoqa2jDtG5nIMSR4buTbmcgxJHhu4F1KiouIEPDoWMgdGjDoG5oIHBo4buRIG5oxrAgKipTYWxlbSoqIHbDoCAqKlRhY29tYSoqIGNoaeG6v20gdOG7tyBs4buHIGzhu5tuIHRyb25nIGtoaSBuaGnhu4F1IHRow6BuaCBwaOG7kSBraMOhYyBjw7Mgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgcuG6pXQgbmjhu48uICANCsSQaeG7gXUgbsOgeSBjaG8gdGjhuqV5IGThu68gbGnhu4d1IGPDsyBz4buxICoqdOG6rXAgdHJ1bmcga2jDoWNoIGjDoG5nIHRoZW8ga2h1IHbhu7FjIMSR4buLYSBsw70qKiwgY8OzIHRo4buDIOG6o25oIGjGsOG7n25nIMSR4bq/biBwaMOibiB0w61jaCBu4bq/dSBraMO0bmcgxJFp4buBdSBjaOG7iW5oIGjhu6NwIGzDvSB0aGVvIMSR4buLYSBwaMawxqFuZy4NCg0KKipW4bq9IMSR4buTIHRo4buLKioNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocGF0Y2h3b3JrKQ0KIyBHaeG6oyBz4butICdkYXRhJyBsw6AgZGF0YSBmcmFtZSBj4bunYSBi4bqhbg0KY2l0eV9jb3VudHMgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoQ2l0eSkgJT4lDQogIHN1bW1hcmlzZShuID0gbigpKSAlPiUNCiAgYXJyYW5nZShkZXNjKG4pKSAjIFPhuq9wIHjhur9wIHRoZW8gc+G7kSBsxrDhu6NuZyBnaeG6o20gZOG6p24NCg0Kbl9jaXRpZXMgPC0gbnJvdyhjaXR5X2NvdW50cykNCm1pZF9wb2ludCA8LSBjZWlsaW5nKG5fY2l0aWVzIC8gMikNCg0KIyBE4buvIGxp4buHdSBjaG8gYmnhu4N1IMSR4buTIHRo4bupIG5o4bqldCAobuG7rWEgdHLDqm4gdGhlbyBz4buRIGzGsOG7o25nKQ0KdG9wX2hhbGZfY2l0aWVzIDwtIGhlYWQoY2l0eV9jb3VudHMsIG1pZF9wb2ludCkNCg0KcGxvdDFfY2l0eV9jb3VudCA8LSBnZ3Bsb3QodG9wX2hhbGZfY2l0aWVzLCBhZXMoeCA9IENpdHksIHkgPSBuKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiNGRjk5OTkiLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIuNSkgKw0KICBsYWJzKHggPSAiQ2l0eSIsIHkgPSAiTnVtYmVyIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gOCkpDQoNCiMgROG7ryBsaeG7h3UgY2hvIGJp4buDdSDEkeG7kyB0aOG7qSBoYWkgKG7hu61hIGTGsOG7m2kgdGhlbyBz4buRIGzGsOG7o25nKQ0KYm90dG9tX2hhbGZfY2l0aWVzIDwtIHRhaWwoY2l0eV9jb3VudHMsIG5fY2l0aWVzIC0gbWlkX3BvaW50KQ0KDQpwbG90Ml9jaXR5X2NvdW50IDwtIGdncGxvdChib3R0b21faGFsZl9jaXRpZXMsIGFlcyh4ID0gQ2l0eSwgeSA9IG4pKSArDQogIGdlb21fY29sKGZpbGwgPSAiI0ZGOTk5OSIsIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiksIHZqdXN0ID0gLTAuNSwgY29sb3IgPSAiYmxhY2siLCBzaXplID0gMi41KSArDQogIGxhYnMoeCA9ICJDaXR5IiwgeSA9ICJOdW1iZXIiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSA4KSkNCg0KIyBL4bq/dCBo4bujcCBoYWkgYmnhu4N1IMSR4buTIGzhuqFpIHbhu5tpIG5oYXUNCnBsb3QxX2NpdHlfY291bnQgKyBwbG90Ml9jaXR5X2NvdW50DQoNCmBgYA0KDQojIyMjICoqQmnhur9uIFN0YXRlb3JQcm92aW5jZSoqDQoNCioqTOG6rXAgYuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCoqDQpgYGB7cn0NCiMgQuG6o25nIHThuqduIHPhu5ENCnN0YXRlX2ZyZXEgPC0gdGFibGUoZGF0YSRTdGF0ZW9yUHJvdmluY2UpDQojIELhuqNuZyB04bqnbiBzdeG6pXQNCnN0YXRlX3Byb3AgPC0gcHJvcC50YWJsZShzdGF0ZV9mcmVxKQ0KIyBH4buZcCB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgdsOgbyBt4buZdCBi4bqjbmcNCnN0YXRlX3RhYmxlIDwtIGRhdGEuZnJhbWUoDQogIFN0YXRlb3JQcm92aW5jZSA9IG5hbWVzKHN0YXRlX2ZyZXEpLA0KICBGcmVxdWVuY3kgPSBhcy52ZWN0b3Ioc3RhdGVfZnJlcSksDQogIFByb3BvcnRpb24gPSByb3VuZChhcy52ZWN0b3Ioc3RhdGVfcHJvcCksIDMpDQopDQojIEhp4buDbiB0aOG7iyBi4bqjbmcgduG7m2kga2FibGUNCmtuaXRyOjprYWJsZShzdGF0ZV90YWJsZSwgY2FwdGlvbiA9ICJC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGPhu6dhIGJp4bq/biBTdGF0ZW9yUHJvdmluY2UiKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQqKg0KROG7sWEgdsOgbyBi4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGPhu6dhIGJp4bq/biAqKlN0YXRlb3JQcm92aW5jZSoqLCB0YSB0aOG6pXk6DQoNCi0gQmFuZyAqKldBIChXYXNoaW5ndG9uKSoqIGPDsyBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBuaGnhu4F1IG5o4bqldCB24bubaSA0NTY3IG5nxrDhu51pLCBjaGnhur9tIDMyLjUlIHThu5VuZyBz4buRIHF1YW4gc8OhdC4gxJDDonkgbMOgIGJhbmcgY2hp4bq/bSDGsHUgdGjhur8gcsO1IHLhu4d0IHRyb25nIHThuq1wIGThu68gbGnhu4d1Lg0KDQotICoqQ0EgKENhbGlmb3JuaWEpKiogeOG6v3AgdGjhu6kgaGFpIHbhu5tpIDI3MzMgbmfGsOG7nWksIGNoaeG6v20gMTkuNCUsIGNobyB0aOG6pXkgxJHDonkgY8WpbmcgbMOgIGtodSB24buxYyB04bqtcCB0cnVuZyBuaGnhu4F1IGtow6FjaCBow6BuZy4NCg0KLSAqKk9SIChPcmVnb24pKiogY8OzIDIyNjIga2jDoWNoIGjDoG5nLCB0xrDGoW5nIOG7qW5nIDE2LjElLCBsw6AgbmjDs20gbOG7m24gdGnhur9wIHRoZW8uDQoNCi0gKipaYWNhdGVjYXMqKiBjw7MgMTI5NyBraMOhY2ggaMOgbmcsIGNoaeG6v20gOS4yJSwgbMOgIGJhbmcg4bufIE1leGljbyBjw7Mgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgdMawxqFuZyDEkeG7kWkgbOG7m24uDQoNCi0gQ8OhYyBiYW5nICoqQkMqKiwgKipERioqLCB2w6AgKipZdWNhdGFuKiogY8OzIHPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nIGRhbyDEkeG7mW5nIHThu6sgODAw4oCTODUwLCBjaGnhur9tIGtob+G6o25nIDUuOCUgIG3hu5dpIGJhbmcuDQoNCi0gQ8OhYyBiYW5nIGPDsm4gbOG6oWkgbmjGsCAqKlZlcmFjcnV6KiosICoqR3VlcnJlcm8qKiwgKipKYWxpc2NvKiogY8OzIHPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nIHRo4bqlcCBoxqFuIG5oaeG7gXUsIHRyb25nIMSRw7MgKipKYWxpc2NvKiogbMOgIMOtdCBuaOG6pXQgduG7m2kgY2jhu4kgNzUga2jDoWNoIGjDoG5nLCBjaGnhur9tIDAuNSUgdOG7lW5nIHPhu5EuDQoNClThu5VuZyB0aOG7gywgZOG7ryBsaeG7h3Uga2jDoWNoIGjDoG5nIHThuq1wIHRydW5nIGNo4bunIHnhur91IOG7nyBjw6FjIGJhbmcgY+G7p2EgKipIb2EgS+G7syAoV0EsIENBLCBPUikqKiwgdHJvbmcga2hpIG3hu5l0IHPhu5EgYmFuZyBj4bunYSAqKk1leGljbyoqIGPDsyB04bu3IGzhu4cgdGjhuqVwIGjGoW4gxJHDoW5nIGvhu4MuDQoNCioqVuG6vSDEkeG7kyB0aOG7iyoqDQpgYGB7cn0NCmRhdGEgJT4lDQogIGNvdW50KFN0YXRlb3JQcm92aW5jZSkgJT4lDQogIGdncGxvdChhZXMoeCA9IFN0YXRlb3JQcm92aW5jZSwgeSA9IG4sIGZpbGwgPSBTdGF0ZW9yUHJvdmluY2UpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDMpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQzIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJp4buDdSDEkeG7kyB04bqnbiBz4buRIHRoZW8gYmnhur9uIFN0YXRlb3JQcm92aW5jZSIsDQogICAgeCA9ICJCYW5nL1Thu4luaCIsDQogICAgeSA9ICJT4buRIGzGsOG7o25nIg0KICApICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpDQpgYGANCg0KIyMjIyAqKkJp4bq/biBDb3VudHJ5KioNCg0KKipM4bqtcCBi4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0KioNCmBgYHtyfQ0KIyBC4bqjbmcgdOG6p24gc+G7kQ0KY291bnRyeV9mcmVxIDwtIHRhYmxlKGRhdGEkQ291bnRyeSkNCiMgQuG6o25nIHThuqduIHN14bqldA0KY291bnRyeV9wcm9wIDwtIHByb3AudGFibGUoY291bnRyeV9mcmVxKQ0KIyBH4buZcCB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgdsOgbyBt4buZdCBi4bqjbmcNCmNvdW50cnlfdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgQ291bnRyeSA9IG5hbWVzKGNvdW50cnlfZnJlcSksDQogIEZyZXF1ZW5jeSA9IGFzLnZlY3Rvcihjb3VudHJ5X2ZyZXEpLA0KICBQcm9wb3J0aW9uID0gcm91bmQoYXMudmVjdG9yKGNvdW50cnlfcHJvcCksIDMpDQopDQojIEhp4buDbiB0aOG7iyBi4bqjbmcgduG7m2kga2FibGUNCmtuaXRyOjprYWJsZShjb3VudHJ5X3RhYmxlLCBjYXB0aW9uID0gIkLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uIENvdW50cnkiKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQ6KioNCkThu7FhIHbDoG8gYuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCBj4bunYSBiaeG6v24gKipDb3VudHJ5KiosIHRhIHRo4bqleToNCg0KLSAqKlVTQSoqIGzDoCBxdeG7kWMgZ2lhIGNoaeG6v20gxrB1IHRo4bq/IGzhu5tuIG5o4bqldCB24bubaSA5NTYyIGdpYW8gZOG7i2NoLCB0xrDGoW5nIMSRxrDGoW5nIDY4LjAlIHThu5VuZyBz4buRIHF1YW4gc8OhdC4gxJDDonkgbMOgIG5ow7NtIGdpYW8gZOG7i2NoICBjaGnhur9tIMSRYSBz4buRIHRyb25nIHThuq1wIGThu68gbGnhu4d1Lg0KDQotICoqTWV4aWNvKiogxJHhu6luZyB0aOG7qSBoYWkgduG7m2kgMzY4OCBnaWFvIGThu4tjaCwgY2hp4bq/bSAyNi4yJSwgbMOgIG5ow7NtIGdpYW8gZOG7i2NoICBs4bubbiB0aOG7qSBoYWkuDQoNCi0gKipDYW5hZGEqKiBjw7Mgc+G7kSBsxrDhu6NuZyBnaWFvIGThu4tjaCAgw610IG5o4bqldCB24bubaSA4MDkgbmfGsOG7nWksIGNoaeG6v20gNS44JSB04buVbmcgc+G7kSwgbMOgIG5ow7NtIG5o4buPIG5o4bqldCB0cm9uZyBiYSBxdeG7kWMgZ2lhLg0KDQpOaMawIHbhuq15LCBwaOG6p24gbOG7m24gZ2lhbyBk4buLY2ggIHRyb25nIHThuq1wIGThu68gbGnhu4d1IMSR4bq/biB04burICoqSG9hIEvhu7MgKFVTQSkqKiwgdGnhur9wIHRoZW8gbMOgICoqTWV4aWNvKiogdsOgIG3hu5l0IHBo4bqnbiBuaOG7jyBoxqFuIMSR4bq/biB04burICoqQ2FuYWRhKiouDQoNCioqVuG6vSDEkeG7kyB0aOG7iyoqDQpgYGB7cn0NCmRhdGEgJT4lDQogIGNvdW50KENvdW50cnkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBDb3VudHJ5LCB5ID0gbiwgZmlsbCA9IENvdW50cnkpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQpICsNCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJp4buDdSDEkeG7kyB04bqnbiBz4buRIHRoZW8gYmnhur9uIENvdW50cnkiLA0KICAgIHggPSAiUXXhu5FjIGdpYSIsDQogICAgeSA9ICJT4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyINCiAgKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyMjICoqQmnhur9uIFByb2R1Y3RGYW1pbHkqKg0KDQoqKkzhuq1wIGLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQqKg0KYGBge3J9DQojIELhuqNuZyB04bqnbiBz4buRDQpwcm9kdWN0ZmFtaWx5X2ZyZXEgPC0gdGFibGUoZGF0YSRQcm9kdWN0RmFtaWx5KQ0KIyBC4bqjbmcgdOG6p24gc3XhuqV0DQpwcm9kdWN0ZmFtaWx5X3Byb3AgPC0gcHJvcC50YWJsZShwcm9kdWN0ZmFtaWx5X2ZyZXEpDQojIEfhu5lwIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCB2w6BvIG3hu5l0IGLhuqNuZw0KcHJvZHVjdGZhbWlseV90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBQcm9kdWN0RmFtaWx5ID0gbmFtZXMocHJvZHVjdGZhbWlseV9mcmVxKSwNCiAgRnJlcXVlbmN5ID0gYXMudmVjdG9yKHByb2R1Y3RmYW1pbHlfZnJlcSksDQogIFByb3BvcnRpb24gPSByb3VuZChhcy52ZWN0b3IocHJvZHVjdGZhbWlseV9wcm9wKSwgMykNCikNCiMgSGnhu4NuIHRo4buLIGLhuqNuZyB24bubaSBrYWJsZQ0Ka25pdHI6OmthYmxlKHByb2R1Y3RmYW1pbHlfdGFibGUsIGNhcHRpb24gPSAiQuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCBj4bunYSBiaeG6v24gUHJvZHVjdEZhbWlseSIpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKg0KDQpE4buxYSB2w6BvIGLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uICoqUHJvZHVjdEZhbWlseSoqLCB0YSB0aOG6pXk6DQoNCi0gTmjDs20gKipGb29kKiogY2hp4bq/bSDEkWEgc+G7kSB24bubaSAxMDE1MyBraMOhY2ggaMOgbmcsIHTGsMahbmcgxJHGsMahbmcgNzIuMiUgdOG7lW5nIHPhu5EgcXVhbiBzw6F0LiDEkMOieSBsw6AgbmjDs20gc+G6o24gcGjhuqltIHBo4buVIGJp4bq/biBuaOG6pXQgdHJvbmcgdOG6rXAgZOG7ryBsaeG7h3UuDQoNCi0gTmjDs20gKipOb24tQ29uc3VtYWJsZSoqIGPDsyAyNjU2IGtow6FjaCBow6BuZywgY2hp4bq/bSAxOC45JSwgbMOgIG5ow7NtIHPhuqNuIHBo4bqpbSBraMO0bmcgdGnDqnUgdGjhu6UgY2hp4bq/bSB04bu3IGzhu4cgxJHDoW5nIGvhu4MuDQoNCi0gTmjDs20gKipEcmluayoqIGNoaeG6v20gdOG7tyBs4buHIG5o4buPIG5o4bqldCB24bubaSAxMjUwIGtow6FjaCBow6BuZywgY2jhu4kgY2hp4bq/bSA4LjklIHThu5VuZyBz4buRLCBwaOG6o24gw6FuaCBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBz4butIGThu6VuZyBuaMOzbSBz4bqjbiBwaOG6qW0gxJHhu5MgdeG7kW5nIMOtdCBoxqFuIHNvIHbhu5tpIGhhaSBuaMOzbSBjw7JuIGzhuqFpLg0KDQpU4buVbmcgdGjhu4MsIGtow6FjaCBow6BuZyBjaOG7pyB54bq/dSB04bqtcCB0cnVuZyB2w6BvIG5ow7NtIHPhuqNuIHBo4bqpbSAqKkZvb2QqKiwgdHJvbmcga2hpIG5ow7NtICoqRHJpbmsqKiBjw7Mgc+G7sSB0aGFtIGdpYSB0aOG6pXAgbmjhuqV0Lg0KDQoqKlbhur0gxJHhu5MgdGjhu4sqKg0KYGBge3J9DQpkYXRhICU+JQ0KICBjb3VudChQcm9kdWN0RmFtaWx5KSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gUHJvZHVjdEZhbWlseSwgeSA9IG4sIGZpbGwgPSBQcm9kdWN0RmFtaWx5KSkgKw0KICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSA0KSArDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFzdGVsMiIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJCaeG7g3UgxJHhu5MgdOG6p24gc+G7kSB0aGVvIGJp4bq/biBQcm9kdWN0RmFtaWx5IiwNCiAgICB4ID0gIk5ow7NtIHPhuqNuIHBo4bqpbSIsDQogICAgeSA9ICJT4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyINCiAgKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQojIyMjICoqQmnhur9uIFByb2R1Y3REZXBhcnRtZW50KioNCg0KKipM4bqtcCBi4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0KioNCmBgYHtyfQ0KIyBC4bqjbmcgdOG6p24gc+G7kQ0KcHJvZHVjdGRlcGFydG1lbnRfZnJlcSA8LSB0YWJsZShkYXRhJFByb2R1Y3REZXBhcnRtZW50KQ0KIyBC4bqjbmcgdOG6p24gc3XhuqV0DQpwcm9kdWN0ZGVwYXJ0bWVudF9wcm9wIDwtIHByb3AudGFibGUocHJvZHVjdGRlcGFydG1lbnRfZnJlcSkNCiMgR+G7mXAgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IHbDoG8gbeG7mXQgYuG6o25nDQpwcm9kdWN0ZGVwYXJ0bWVudF90YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBQcm9kdWN0RGVwYXJ0bWVudCA9IG5hbWVzKHByb2R1Y3RkZXBhcnRtZW50X2ZyZXEpLA0KICBGcmVxdWVuY3kgPSBhcy52ZWN0b3IocHJvZHVjdGRlcGFydG1lbnRfZnJlcSksDQogIFByb3BvcnRpb24gPSByb3VuZChhcy52ZWN0b3IocHJvZHVjdGRlcGFydG1lbnRfcHJvcCksIDMpDQopDQojIEhp4buDbiB0aOG7iyBi4bqjbmcgduG7m2kga2FibGUNCmtuaXRyOjprYWJsZShwcm9kdWN0ZGVwYXJ0bWVudF90YWJsZSwgY2FwdGlvbiA9ICJC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGPhu6dhIGJp4bq/biBQcm9kdWN0RGVwYXJ0bWVudCIpDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKg0KDQpE4buxYSB2w6BvIGLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQgY+G7p2EgYmnhur9uICoqUHJvZHVjdERlcGFydG1lbnQqKiwgdGEgdGjhuqV5Og0KDQotIE5ow7NtICoqUHJvZHVjZSoqIGNoaeG6v20gdOG7tyBs4buHIGNhbyBuaOG6pXQgduG7m2kgMTk5NCBraMOhY2ggaMOgbmcsIHTGsMahbmcgxJHGsMahbmcgMTQuMiUgdOG7lW5nIHPhu5EsIGNobyB0aOG6pXkgxJHDonkgbMOgIG5ow7NtIHPhuqNuIHBo4bqpbSBwaOG7lSBiaeG6v24gbmjhuqV0Lg0KDQotIE5ow7NtICoqU25hY2sgRm9vZHMqKiB2w6AgKipIb3VzZWhvbGQqKiBs4bqnbiBsxrDhu6N0IGPDsyAxNjAwICgxMS40JSkgdsOgIDE0MjAgKDEwLjElKSBraMOhY2ggaMOgbmcsIGzDoCBuaOG7r25nIG5ow7NtIGPDsyBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBs4bubbiB0aeG6v3AgdGhlby4NCg0KLSBOaMOzbSAqKkZyb3plbiBGb29kcyoqIGPFqW5nIGPDsyBz4buRIGzGsOG7o25nIMSRw6FuZyBr4buDIHbhu5tpIDEzODIga2jDoWNoIGjDoG5nICg5LjglKS4NCg0KLSBDw6FjIG5ow7NtIG5oxrAgKipCYWtpbmcgR29vZHMqKiAoNy42JSksICoqQ2FubmVkIEZvb2RzKiogKDYuOSUpLCAqKkRhaXJ5KiogKDYuNCUpLCB2w6AgKipIZWFsdGggYW5kIEh5Z2llbmUqKiAoNi40JSkgY2hp4bq/bSB04bu3IGzhu4cgdHJ1bmcgYsOsbmgga2jDoSB0cm9uZyB04bqtcCBk4buvIGxp4buHdS4NCg0KLSBN4buZdCBz4buRIG5ow7NtIGPDsyBz4buRIGzGsOG7o25nIGtow6FjaCBow6BuZyBy4bqldCB0aOG6pXAgZMaw4bubaSAxJSBuaMawICoqQ2Fyb3VzZWwqKiAoMC40JSksICoqQ2hlY2tvdXQqKiAoMC42JSksICoqTWVhdCoqICgwLjYlKSwgdsOgICoqU2VhZm9vZCAqKigwLjclKS4NCg0KTmjDrG4gY2h1bmcsIHThu7cgbOG7hyBwaMOibiBi4buVIGtow6FjaCBow6BuZyB0aGVvIGPDoWMgbmjDs20gc+G6o24gcGjhuqltIGtow6EgxJFhIGThuqFuZywgduG7m2kgbeG7mXQgc+G7kSBuaMOzbSBz4bqjbiBwaOG6qW0gY2jDrW5oIGNoaeG6v20gxrB1IHRo4bq/IHLDtSBy4buHdCB0cm9uZyB04bqtcCBk4buvIGxp4buHdS4NCg0KKipW4bq9IMSR4buTIHRo4buLKioNCmBgYHtyfQ0KIyBHaeG6oyBz4butICdkYXRhJyBsw6AgZGF0YSBmcmFtZSBj4bunYSBi4bqhbg0KZGVwdF9jb3VudHMgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoUHJvZHVjdERlcGFydG1lbnQpICU+JQ0KICBzdW1tYXJpc2UobiA9IG4oKSkgJT4lDQogIGFycmFuZ2UobikgIyBT4bqvcCB44bq/cCB0aGVvIHPhu5EgbMaw4bujbmcgxJHhu4MgY2hpYQ0KDQpuX2RlcHRzIDwtIG5yb3coZGVwdF9jb3VudHMpDQptaWRfcG9pbnQgPC0gY2VpbGluZyhuX2RlcHRzIC8gMikNCg0KIyBE4buvIGxp4buHdSBjaG8gYmnhu4N1IMSR4buTIHRo4bupIG5o4bqldCAobuG7rWEgxJHhuqd1KQ0KZGVwdF9jb3VudHNfMSA8LSBoZWFkKGRlcHRfY291bnRzLCBtaWRfcG9pbnQpDQoNCnBsb3QxIDwtIGdncGxvdChkZXB0X2NvdW50c18xLCBhZXMoeCA9IFByb2R1Y3REZXBhcnRtZW50LCB5ID0gbikpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjNDE2OUUxIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyLjUpICsNCiAgbGFicyh4ID0gIlByb2R1Y3QgRGVwYXJ0bWVudCIsIHkgPSAiTnVtYmVyIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gOCkpDQoNCiMgROG7ryBsaeG7h3UgY2hvIGJp4buDdSDEkeG7kyB0aOG7qSBoYWkgKG7hu61hIHNhdSkNCmRlcHRfY291bnRzXzIgPC0gdGFpbChkZXB0X2NvdW50cywgbl9kZXB0cyAtIG1pZF9wb2ludCkNCg0KcGxvdDIgPC0gZ2dwbG90KGRlcHRfY291bnRzXzIsIGFlcyh4ID0gUHJvZHVjdERlcGFydG1lbnQsIHkgPSBuKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiM0MTY5RTEiLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIuNSkgKw0KICBsYWJzKHggPSAiUHJvZHVjdCBEZXBhcnRtZW50IiwgeSA9ICJOdW1iZXIiKSArDQogIHRoZW1lX2J3KCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSA4KSkNCg0KIyBL4bq/dCBo4bujcCBoYWkgYmnhu4N1IMSR4buTIGzhuqFpIHbhu5tpIG5oYXUNCnBsb3QxICsgcGxvdDINCg0KYGBgDQoNCg0KIyMjIyAqKkJp4bq/biBQcm9kdWN0Q2F0ZWdvcnkqKg0KDQoqKkzhuq1wIGLhuqNuZyB04bqnbiBz4buRIHbDoCB04bqnbiBzdeG6pXQqKg0KYGBge3J9DQojIELhuqNuZyB04bqnbiBz4buRDQpwcm9kdWN0Y2F0ZWdvcnlfZnJlcSA8LSB0YWJsZShkYXRhJFByb2R1Y3RDYXRlZ29yeSkNCiMgQuG6o25nIHThuqduIHN14bqldA0KcHJvZHVjdGNhdGVnb3J5X3Byb3AgPC0gcHJvcC50YWJsZShwcm9kdWN0Y2F0ZWdvcnlfZnJlcSkNCiMgR+G7mXAgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IHbDoG8gbeG7mXQgYuG6o25nDQpwcm9kdWN0Y2F0ZWdvcnlfdGFibGUgPC0gZGF0YS5mcmFtZSgNCiAgUHJvZHVjdENhdGVnb3J5ID0gbmFtZXMocHJvZHVjdGNhdGVnb3J5X2ZyZXEpLA0KICBGcmVxdWVuY3kgPSBhcy52ZWN0b3IocHJvZHVjdGNhdGVnb3J5X2ZyZXEpLA0KICBQcm9wb3J0aW9uID0gcm91bmQoYXMudmVjdG9yKHByb2R1Y3RjYXRlZ29yeV9wcm9wKSwgMykNCikNCiMgSGnhu4NuIHRo4buLIGLhuqNuZyB24bubaSBrYWJsZQ0Ka25pdHI6OmthYmxlKHByb2R1Y3RjYXRlZ29yeV90YWJsZSwgY2FwdGlvbiA9ICJC4bqjbmcgdOG6p24gc+G7kSB2w6AgdOG6p24gc3XhuqV0IGPhu6dhIGJp4bq/biBQcm9kdWN0Q2F0ZWdvcnkiKQ0KYGBgDQoNCioqTmjhuq1uIHjDqXQ6KioNCkThu7FhIHbDoG8gYuG6o25nIHThuqduIHPhu5EgdsOgIHThuqduIHN14bqldCBj4bunYSBiaeG6v24gKipQcm9kdWN0Q2F0ZWdvcnkqKiwgdGEgdGjhuqV5Og0KDQotIE5ow7NtICoqU25hY2sgRm9vZHMqKiBjw7Mgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgbOG7m24gbmjhuqV0IHbhu5tpIDE2MDAga2jDoWNoIGjDoG5nLCBjaGnhur9tIDExLjQlIHThu5VuZyBz4buRLCBwaOG6o24gw6FuaCDEkcOieSBsw6AgbmjDs20gc+G6o24gcGjhuqltIHBo4buVIGJp4bq/biBuaOG6pXQuDQoNCi0gTmjDs20gKipWZWdldGFibGVzKiogxJHhu6luZyB0aOG7qSBoYWkgduG7m2kgMTcyOCBraMOhY2ggaMOgbmcgKDEyLjMlKSwgY2hvIHRo4bqleSDEkcOieSBjxaluZyBsw6AgbmjDs20gc+G6o24gcGjhuqltIGPDsyBz4bupYyB0acOqdSB0aOG7pSBjYW8uDQoNCi0gTmjDs20gKipEYWlyeSoqIGNoaeG6v20gdOG7tyBs4buHIMSRw6FuZyBr4buDIHbhu5tpIDkwMyBraMOhY2ggaMOgbmcgKDYuNCUpLCB0aeG6v3AgdGhlbyBsw6AgY8OhYyBuaMOzbSAqKkZydWl0KiogKDUuNCUpIHbDoCAqKk1lYXQqKiAoNS40JSkuDQoNCi0gQ8OhYyBuaMOzbSBz4bqjbiBwaOG6qW0gbmjGsCAqKkphbXMgYW5kIEplbGxpZXMgKiooNC4yJSksICoqQmFraW5nIEdvb2RzKiogKDMuNCUpLCAqKkNhbm5lZCBTb3VwICoqKDIuOSUpLCB2w6AgKipCcmVhZCoqICgzLjAlKSBjxaluZyBjw7Mgc+G7kSBsxrDhu6NuZyBraMOhY2ggaMOgbmcgdHJ1bmcgYsOsbmguDQoNCi0gTeG7mXQgc+G7kSBuaMOzbSBjw7MgdOG7tyBs4buHIHLhuqV0IHRo4bqlcCBkxrDhu5tpIDElIG5oxrAgKipDYW5kbGVzKiogKDAuMyUpLCAqKkNhbm5lZCBBbmNob3ZpZXMqKiAoMC4zJSksICoqQ2FubmVkIE95c3RlcnMqKiAoMC4yJSksIHbDoCAqKk1pc2NlbGxhbmVvdXMgKiooMC4zJSkuDQoNCk5ow6xuIGNodW5nLCB04bu3IGzhu4cgcGjDom4gYuG7kSBraMOhY2ggaMOgbmcgdGhlbyBjw6FjIG5ow7NtIHPhuqNuIHBo4bqpbSBy4bqldCDEkWEgZOG6oW5nLCB0cm9uZyDEkcOzIG3hu5l0IHPhu5EgbmjDs20gY2jDrW5oIGNoaeG6v20gxrB1IHRo4bq/IHLDtSBy4buHdCwgcGjhuqNuIMOhbmggc+G7sSDEkWEgZOG6oW5nIHRyb25nIGzhu7FhIGNo4buNbiBz4bqjbiBwaOG6qW0gY+G7p2Ega2jDoWNoIGjDoG5nLg0KDQoqKlbhur0gxJHhu5MgdGjhu4sqKg0KYGBge3J9DQojIEdp4bqjIHPhu60gJ2RhdGEnIGzDoCBkYXRhIGZyYW1lIGPhu6dhIGLhuqFuDQpjYXRlZ29yeV9jb3VudHMgPC0gZGF0YSAlPiUNCiAgZ3JvdXBfYnkoUHJvZHVjdENhdGVnb3J5KSAlPiUNCiAgc3VtbWFyaXNlKG4gPSBuKCkpICU+JQ0KICBhcnJhbmdlKG4pICMgU+G6r3AgeOG6v3AgdGhlbyBz4buRIGzGsOG7o25nIMSR4buDIGNoaWENCg0Kbl9jYXRlZ29yaWVzIDwtIG5yb3coY2F0ZWdvcnlfY291bnRzKQ0KbWlkX3BvaW50IDwtIGNlaWxpbmcobl9jYXRlZ29yaWVzIC8gMikNCg0KIyBE4buvIGxp4buHdSBjaG8gYmnhu4N1IMSR4buTIHRo4bupIG5o4bqldCAobuG7rWEgxJHhuqd1IHRoZW8gc+G7kSBsxrDhu6NuZykNCmNhdGVnb3J5X2NvdW50c18xIDwtIGhlYWQoY2F0ZWdvcnlfY291bnRzLCBtaWRfcG9pbnQpDQoNCnBsb3QxX2NhdCA8LSBnZ3Bsb3QoY2F0ZWdvcnlfY291bnRzXzEsIGFlcyh4ID0gUHJvZHVjdENhdGVnb3J5LCB5ID0gbikpICsNCiAgZ2VvbV9jb2woZmlsbCA9ICIjOERBMENCIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuKSwgdmp1c3QgPSAtMC41LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyLjUpICsNCiAgbGFicyh4ID0gIlByb2R1Y3QgQ2F0ZWdvcnkiLCB5ID0gIk51bWJlciIpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDYpKQ0KDQojIEThu68gbGnhu4d1IGNobyBiaeG7g3UgxJHhu5MgdGjhu6kgaGFpIChu4butYSBzYXUgdGhlbyBz4buRIGzGsOG7o25nKQ0KY2F0ZWdvcnlfY291bnRzXzIgPC0gdGFpbChjYXRlZ29yeV9jb3VudHMsIG5fY2F0ZWdvcmllcyAtIG1pZF9wb2ludCkNCg0KcGxvdDJfY2F0IDwtIGdncGxvdChjYXRlZ29yeV9jb3VudHNfMiwgYWVzKHggPSBQcm9kdWN0Q2F0ZWdvcnksIHkgPSBuKSkgKw0KICBnZW9tX2NvbChmaWxsID0gIiM4REEwQ0IiLCBjb2xvciA9ICJibGFjayIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG4pLCB2anVzdCA9IC0wLjUsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIuNSkgKw0KICBsYWJzKHggPSAiUHJvZHVjdCBDYXRlZ29yeSIsIHkgPSAiTnVtYmVyIikgKw0KICB0aGVtZV9idygpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCBzaXplID0gNikpDQoNCiMgS+G6v3QgaOG7o3AgaGFpIGJp4buDdSDEkeG7kyBs4bqhaSB24bubaSBuaGF1DQpwbG90MV9jYXQgKyBwbG90Ml9jYXQNCmBgYA0KDQojIyMgKipQaMOibiB0w61jaCBjw6FjIGJp4bq/biDEkeG7i25oIGzGsOG7o25nKioNCg0KIyMjIyAqKkJp4bq/biBDaGlsZHJlbioqDQoNCioqVGjhu5FuZyBrw6ogbcO0IHThuqMgYmnhur9uIENoaWxkcmVuKioNCmBgYHtyfQ0KQ2hpbGRyZW4gPC0gc3VtbWFyeShkYXRhJENoaWxkcmVuKQ0KcHJpbnQoQ2hpbGRyZW4pDQpgYGANCg0KKipOaOG6rW4geMOpdDoqKg0KROG7sWEgdsOgbyBr4bq/dCBxdeG6oyB0aOG7kW5nIGvDqiBtw7QgdOG6oyBiaeG6v24gKipDaGlsZHJlbioqLCB0YSB0aOG6pXk6DQoNCi0gU+G7kSBjb24gdOG7kWkgdGhp4buDdSBsw6AgMCwgY2hvIHRo4bqleSBjw7Mgbmjhu69uZyBraMOhY2ggaMOgbmcga2jDtG5nIGPDsyBjb24uDQoNCi0gU+G7kSBjb24gdOG7kWkgxJFhIGzDoCA1LCBwaOG6o24gw6FuaCBraMOhY2ggaMOgbmcgY8OzIHPhu5EgY29uIG5oaeG7gXUgbmjhuqV0IGzDoCA1Lg0KDQotIEdpw6EgdHLhu4sgdHJ1bmcgduG7iyAoTWVkaWFuKSBsw6AgMywgYmnhu4N1IHRo4buLIHBow6JuIGLhu5Egc+G7kSBjb24gdOG6rXAgdHJ1bmcgcXVhbmggbeG7qWMgMyBjb24uDQoNCi0gR2nDoSB0cuG7iyB0cnVuZyBiw6xuaCAoTWVhbikgbMOgIDIuNTMsIGjGoWkgdGjhuqVwIGjGoW4gdHJ1bmcgduG7iywgY2hvIHRo4bqleSBz4buRIGNvbiB0cnVuZyBiw6xuaCBraG/huqNuZyAyIMSR4bq/biAzIGNvbi4NCg0KLSBQaMOibiB24buLIHRo4bupIG5o4bqldCAoMXN0IFF1LikgbMOgIDEgdsOgIHBow6JuIHbhu4sgdGjhu6kgYmEgKDNyZCBRdS4pIGzDoCA0LCBuZ2jEqWEgbMOgIDI1JSBraMOhY2ggaMOgbmcgY8OzIHThu6sgMCDEkeG6v24gMSBjb24sIDUwJSBjw7MgdOG7qyAxIMSR4bq/biA0IGNvbiwgdsOgIDI1JSBjw7MgdOG7qyA0IMSR4bq/biA1IGNvbi4NCg0KTmjGsCB24bqteSwgcGjhuqduIGzhu5tuIGtow6FjaCBow6BuZyBjw7Mgc+G7kSBjb24gZGFvIMSR4buZbmcgdOG7qyAxIMSR4bq/biA0LCB24bubaSBt4bupYyB0cnVuZyBiw6xuaCBraG/huqNuZyAyLTMgY29uLg0KDQoqKlbhur0gxJHhu5MgdGjhu4sqKg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBmYWN0b3IoQ2hpbGRyZW4pKSkgKyANCiAgZ2VvbV9iYXIoZmlsbCA9ICJwaW5rIiwgY29sb3IgPSAiYmxhY2siKSArDQogIGxhYnMoDQogICAgICAgeCA9ICJT4buRIGzGsOG7o25nIGNvbiBjw6FpIiwNCiAgICAgICB5ID0gIlPhu5EgbMaw4bujbmcga2jDoWNoIGjDoG5nIikgKw0KICB0aGVtZV9idygpICsNCiAgZ2VvbV90ZXh0KHN0YXQ9J2NvdW50JywgYWVzKGxhYmVsPS4uY291bnQuLiksIHZqdXN0PS0wLjUpIA0KYGBgDQoNCiMjIyMgKipVbml0c1NvbGQqKg0KDQoqKlRo4buRbmcga8OqIG3DtCB04bqjIGJp4bq/biBVbml0c1NvbGQqKg0KYGBge3J9DQpVbml0c1NvbGQgPC0gc3VtbWFyeShkYXRhJFVuaXRzU29sZCkNCnByaW50KFVuaXRzU29sZCkNCmBgYA0KDQoqKk5o4bqtbiB4w6l0OioqDQpL4bq/dCBxdeG6oyB0aOG7kW5nIGvDqiBtw7QgdOG6oyBiaeG6v24gKipVbml0c1NvbGQqKiBjaG8gdGjhuqV5Og0KDQotIFPhu5EgxJHGoW4gduG7iyBiw6FuIHThu5FpIHRoaeG7g3UgbMOgIDEsIGNobyB0aOG6pXkgY8OzIG5o4buvbmcgZ2lhbyBk4buLY2ggYsOhbiB04buRaSB0aGnhu4N1IDEgc+G6o24gcGjhuqltLg0KDQotIFPhu5EgxJHGoW4gduG7iyBiw6FuIHThu5FpIMSRYSBsw6AgOCwgcGjhuqNuIMOhbmggY8OzIGdpYW8gZOG7i2NoIGLDoW4gbmhp4buBdSBuaOG6pXQgbMOqbiDEkeG6v24gOCBz4bqjbiBwaOG6qW0uDQoNCi0gR2nDoSB0cuG7iyB0cnVuZyB24buLIChNZWRpYW4pIGzDoCA0LCBiaeG7g3UgdGjhu4sgcGjDom4gYuG7kSBz4buRIGzGsOG7o25nIGLDoW4gdOG6rXAgdHJ1bmcg4bufIG3hu6ljIDQgxJHGoW4gduG7iy4NCg0KLSBHacOhIHRy4buLIHRydW5nIGLDrG5oIChNZWFuKSBsw6AgNC4wODEsIGfhuqduIHbhu5tpIGdpw6EgdHLhu4sgdHJ1bmcgduG7iywgY2hvIHRo4bqleSBwaMOibiBwaOG7kWkgc+G7kSBsxrDhu6NuZyBiw6FuIGtow6EgY8OibiBi4bqxbmcgeHVuZyBxdWFuaCA0IHPhuqNuIHBo4bqpbS4NCg0KLSBQaMOibiB24buLIHRo4bupIG5o4bqldCAoMXN0IFF1LikgbMOgIDMgdsOgIHBow6JuIHbhu4sgdGjhu6kgYmEgKDNyZCBRdS4pIGzDoCA1LCB04bupYyAyNSUgY8OhYyBnaWFvIGThu4tjaCBiw6FuIGTGsOG7m2kgaG/hurdjIGLhurFuZyAzIMSRxqFuIHbhu4ssIDUwJSBnaWFvIGThu4tjaCBiw6FuIHThu6sgMyDEkeG6v24gNSDEkcahbiB24buLLCB2w6AgMjUlIGPDsm4gbOG6oWkgYsOhbiB0csOqbiA1IMSRxqFuIHbhu4suDQoNCk5oxrAgduG6rXksIHBo4bqnbiBs4bubbiBjw6FjIGdpYW8gZOG7i2NoIGLDoW4gdHJvbmcgZOG7ryBsaeG7h3UgbuG6sW0gdHJvbmcga2hv4bqjbmcgdOG7qyAzIMSR4bq/biA1IMSRxqFuIHbhu4sgc+G6o24gcGjhuqltLCB24bubaSBt4bupYyB0cnVuZyBiw6xuaCBn4bqnbiA0IMSRxqFuIHbhu4suDQoNCioqVuG6vSDEkeG7kyB0aOG7iyoqDQpgYGB7cn0NCmdncGxvdChkYXRhLCBhZXMoeCA9IGZhY3RvcihVbml0c1NvbGQpKSkgKyANCiAgZ2VvbV9iYXIoZmlsbCA9ICJ5ZWxsb3ciLCBjb2xvciA9ICJibGFjayIpICsNCiAgbGFicygNCiAgICAgICB4ID0gIlPhu5EgbMaw4bujbmcgxJHGoW4gduG7iyBz4bqjbiBwaOG6qW0iLA0KICAgICAgIHkgPSAiU+G7kSBsxrDhu6NuZyBnaWFvIGThu4tjaCIpICsNCiAgdGhlbWVfYncoKSArDQogIGdlb21fdGV4dChzdGF0PSdjb3VudCcsIGFlcyhsYWJlbD0uLmNvdW50Li4pLCB2anVzdD0tMC41KSANCmBgYA0KDQojIyMjICoqUmV2ZW51ZSoqDQoNCioqVGjhu5FuZyBrw6ogbcO0IHThuqMgYmnhur9uIFJldmVudWUqKg0KYGBge3J9DQpSZXZlbnVlIDwtIHN1bW1hcnkoZGF0YSRSZXZlbnVlKQ0KcHJpbnQoUmV2ZW51ZSkNCmBgYA0KDQoqKk5o4bqtbiB4w6l0OioqIA0KS+G6v3QgcXXhuqMgdGjhu5FuZyBrw6ogbcO0IHThuqMgYmnhur9uICoqUmV2ZW51ZSoqIGNobyB0aOG6pXk6DQoNCi0gR2nDoSB0cuG7iyBkb2FuaCB0aHUgdGjhuqVwIG5o4bqldCAoTWluLikgbMOgIDAuNTMsIGNobyB0aOG6pXkgY8OzIG5o4buvbmcgZ2lhbyBk4buLY2ggbWFuZyBs4bqhaSBkb2FuaCB0aHUgcuG6pXQgbmjhu48uDQoNCi0gR2nDoSB0cuG7iyBkb2FuaCB0aHUgY2FvIG5o4bqldCAoTWF4LikgbMOgIDU2LjcwLCBwaOG6o24gw6FuaCBjw7MgZ2lhbyBk4buLY2ggbWFuZyBs4bqhaSBkb2FuaCB0aHUgbOG7m24gbmjhuqV0IHRyb25nIHThuq1wIGThu68gbGnhu4d1Lg0KDQotIEdpw6EgdHLhu4sgdHJ1bmcgduG7iyAoTWVkaWFuKSBsw6AgMTEuMjUsIGNobyB0aOG6pXkgbeG7mXQgbuG7rWEgY8OhYyBnaWFvIGThu4tjaCBjw7MgZG9hbmggdGh1IGTGsOG7m2kgaG/hurdjIGLhurFuZyBt4bupYyBuw6B5Lg0KDQotIEdpw6EgdHLhu4sgdHJ1bmcgYsOsbmggKE1lYW4pIGzDoCAxMy4wMCwgY2FvIGjGoW4gdHJ1bmcgduG7iywgxJFp4buBdSBuw6B5IGNobyB0aOG6pXkgcGjDom4gcGjhu5FpIGRvYW5oIHRodSBjw7MgdGjhu4MgYuG7iyBs4buHY2ggcGjhuqNpIChjw7MgbeG7mXQgc+G7kSBnacOhIHRy4buLIGzhu5tuIGvDqW8gdHJ1bmcgYsOsbmggbMOqbikuDQoNCi0gUGjDom4gduG7iyB0aOG7qSBuaOG6pXQgKDFzdCBRdS4pIGzDoCA2Ljg0IHbDoCBwaMOibiB24buLIHRo4bupIGJhICgzcmQgUXUuKSBsw6AgMTcuMzcsIHThu6ljIDI1JSBnaWFvIGThu4tjaCBjw7MgZG9hbmggdGh1IGTGsOG7m2kgaG/hurdjIGLhurFuZyA2Ljg0LCA1MCUgZ2lhbyBk4buLY2ggY8OzIGRvYW5oIHRodSB0cm9uZyBraG/huqNuZyB04burIDYuODQgxJHhur9uIDE3LjM3LCB2w6AgMjUlIGPDsm4gbOG6oWkgY8OzIGRvYW5oIHRodSB0csOqbiAxNy4zNy4NCg0KTmjGsCB24bqteSwgZG9hbmggdGh1IGPDoWMgZ2lhbyBk4buLY2ggY8OzIHPhu7EgcGjDom4gYuG7kSBy4buZbmcgduG7m2kgeHUgaMaw4bubbmcgbOG7h2NoIHBo4bqjaSwgcGjhuqduIGzhu5tuIGRvYW5oIHRodSB04bqtcCB0cnVuZyDhu58gbeG7qWMgduG7q2EgcGjhuqNpIG5oxrBuZyBjw7MgbeG7mXQgc+G7kSBnaWFvIGThu4tjaCBkb2FuaCB0aHUgcuG6pXQgY2FvIGvDqW8gZ2nDoSB0cuG7iyB0cnVuZyBiw6xuaCBsw6puLg0KDQoqKlbhur0gxJHhu5MgdGjhu4sqKg0KYGBge3J9DQpnZ3Bsb3QoZGF0YSwgYWVzKHggPSBSZXZlbnVlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsDQogICAgICAgICAgICAgICAgIGZpbGwgPSAibGlnaHRncmVlbiIsDQogICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikgKw0KICBsYWJzKA0KICAgICAgIHggPSAiRG9hbmggdGh1IChVU0QpIiwNCiAgICAgICB5ID0gIlPhu5EgbMaw4bujbmcgZ2lhbyBk4buLY2giKSArDQogIHRoZW1lX2J3KCkNCmBgYA==