1 Gamlss là một thư viện khổng lồ về quy luật phân phối

Trong bài trước, Nhi đã giới thiệu với các bạn package Gamlss, một bảo bối cho mô hình tuyến tính tổng quát. Một trong những ưu thế của gamlss là cấu trúc tinh tế của mô hình. Trước hết, GAM là những mô hình hồi quy bán tham số chuyên biệt về phân phối (distribution based semiparametric regression). Có nghĩa là trước tiên người dùng phải xác định 1 họ phân phối chuyên biệt cho biến kết quả ; sau đó tùy vào bản chất của phân phối này,từ 2 đến 4 mô hình khác nhau sẽ được xây dựng cho toàn bộ các tham số của phân phối.

Bốn tham số này được gọi tên là Mu, Sigma, Nu và Tau, tương ứng với 4 đặc tính : Vị trí trung tâm (Location = Mu), Scale (sai biệt, Sigma), Skewness (Nu) và Kurtosis (Tau). Như vậy Mu và Sigma xác định giá trị Trung bìhn và độ biến thiên của phân phối, trong khi Tau và Nu xác định kiểu hình của phân phối (hình dạng của đồ thị hàm pdf).

Thành tố thứ nhất của mô hình gamlss chính là họ phân phối và các tham số này. Thành tố thứ hai là các phần tử hồi quy (predictors) bao gồm hằng số (intercept), các biến độc lập, bậc đa thức và tương tác giữa chúng. Thành tố thứ 3 là phần tử bù trừ bao gồm tất cả các phương pháp hiệu chỉnh nhằm tăng khả năng (capacity) của mô hình, bao gồm smoothing splines, penalized splines, neural network, decision tree, piece wise…

Trong bài này chúng ta chỉ quan tâm đến Thành tố thứ nhất là Phân phối và tham số của nó.

Thống kê mô tả là một quy trình quen thuộc ta hay dùng để thăm dò dữ liệu, và mục tiêu của nó chính là nắm các đặc tính phân phối của dữ liệu trong mẫu. Nhi và các bạn vẫn hay tính giá trị trung bình, SD, skewness và kurtosis. Ta còn vẽ histogram để phân tích trực quan đặc tính này. Việc tính toán này mang lại thông tin khá đầy đủ về đặc tính phân phối của biến số. Giáo trình xác suất trong trường đại học đã mô tả cho chúng ta một số quy luật phân phối cổ điển như Gaussian, Poisson, Binomial, t và Chi2. Nhưng, để giúp các bạn liên hệ giữa biến số và mô hình GAMLSS, Nhi muốn chúng ta hình dung về biến số như 1 hàm toán học chứ không phải như 1 đại lượng. Thật vậy, bất cứ biến số nào cũng có thể xem như một hàm. Kết quả của hàm này là một con số (giá trị) và mỗi giá trị như vậy gắn với một xác suất mà giá trị đó có thể xuất hiện trong quần thể. Thí dụ, theo lý thuyết số lượng bạch cầu trong một mL máu tĩnh mạch có thể nhận bất cứ giá trị số nguyên dương nào trong khoảng từ 0 đến 10.000 nhưng xác suất quan sát được mỗi giá trị có thể khác nhau.

Hàm mật độ xác suất (probability density function – pdf ) cho phép ước tính xác suất cho mỗi giá trị bất kì trong 1 khoảng (thang đo) xác định. Hàm pdf có liên hệ rất gần gũi với biểu đồ tần suất (histogram). Trong thực hành, việc vẽ histogram kết hợp với đồ thị của hàm pdf (density curve) cho phép mô tả trực quan đặc tính phân phối của một biến số mà ta cần thăm dò. Từ histogram có thể ước tính hàm pdf nhưng bản chất của 2 thứ này hoàn toàn khác nhau : histogram là 1 phép thống kê (dựa vào chọn mẫu) trong khi PDF thuần túy là xác suất.

Ngoài ra, hàm mật độ xác suất tích lũy (cumulative density function, cdf) là một cách khác để biểu diễn đặc tính phân phối của một biến. CDF biểu thị tổng mật độ xác suất của tất cả trường hợp Y tính đến vị trí Yi mà ta cần khảo sát. Thực ra cả pdf và cdf đều cung cấp thông tin như nhau, và có thể hoán chuyển .

Như Nhi đã nói ở trên, thông tin quan trọng nhất ta thường khai thác đó là vị trí trung tâm của dữ liệu nằm ở đâu, và sai biệt bao lớn (cách vị trí trung tâm bao xa) ? Vị trí trung tâm được xác định bằng Mean, Median, Mode…, sai biệt được ước tính bằng độ lệch chuẩn (sigma, sd) hay phương sai (variance, sigma^2). Ta thường hiểu Mean như là arithmetric mean (cho số thực hay số nguyên) hoặc geometric mean (cho tỉ lệ), và ta cũng quen báo cáo Mean +/- SD với giả định là biến số có phân phối chuẩn. Tuy nhiên vị trí trung tâm thực ra còn có thể xem (một cách tổng quát) như một mô hình tối giản có phương trình là Y ~ 1 (chỉ chứa hằng số Intercept) với link function = identity và họ phân phối tùy chọn. Lúc này thực chất ta ước tính Mu (trung bình, giá trị kì vọng) cho một phân phối dựa trên dữ liệu quan sát được. Ngoài Mean và Sd, ta còn quan tâm đến Skewness và Kurtosis, Skewness cho biết số liệu của chúng ta có hình dạng đối xứng hay bị lệch về 1 phía, Kurtosis cho biết hình dạng (đỉnh) của phân phối. Một cách tổng quát, Gamlss cung cấp 1 framework để mô hình hóa (ước tính giá trị của tham số đích tùy theo 1 hay nhiều hiệp biến số/yếu tố khác) cho cả Mu, sigma và tham số kiểu hình (Nu, Tau) của một phân phối bất kì.

Trước khi thực hành mô hình GAMLSS các bạn cần biết 1 sự thật gây shock, đó là : Trong thế giới thực, phân phối chuẩn không tồn tại (suy rộng ra là dữ liệu thực tế không bao giờ phù hợp tuyệt đối với bất cứ một dạng phân phối lý thuyết nào mà trường lớp dạy cho các bạn, bao gồm phân phối chuẩn). Do đó, thay vì bỏ công làm những kiểm định để kiểm chứng dữ liệu của mình có phân phối « bình thường » hay không, ta cần tìm một quy luật phân phối gần (phù hợp) nhất với dữ liệu đang có để dựng mô hình.

Bây giờ bắt đầu phần hay nhất của câu chuyện hôm nay, đó là Gamlss hỗ trợ tất cả (xin nhấn mạnh, tất cả) họ phân phối thông qua module gamlss.dist ; điều này có nghĩa là gamlss là một thư viện khổng lồ về lý thuyết xác suất. Bạn có thể bước vào, chọn bất kì phân phối nào trong danh mục hàng trăm họ phân phối khác nhau cho số thực, số nguyên dương, tỉ lệ, … và chơi đùa thỏa thích với hàm pdf, hàm cdf, mô phỏng với hàm random, ước tính quantiles, vặn vẹo density curve và histogram với 2-4 tham số,bạn có thể chặn 1 hay 2 đầu, thay đổi function link từ identity sang logarithm, logit, probit… vân vân, có thể trộn phân phối số nguyên và số thực với nhau, và tạo ra một quy luật phân phối của riêng mình.

Sau khi load package gamlss, theo mặc định packae gamlss.dist cũng được gọi lên đồng thời.

library(tidyverse)

library(gamlss)

nC<-detectCores()

Trong thí dụ minh họa sau, Nhi dùng hàm dNBI và pNBI trong gamlss.dist để mô phỏng hàm PDF và CDF của 1 phân phối Negative binomial Type I (NBI) có Mu=10, sigma=0.5

plot(function(y) dNBI(y, mu = 10, sigma =0.5 ), from=0, to=50,n=50+1)%>%as_tibble()%>%ggplot(aes(x=x,y=y))+geom_area(alpha=0.5,fill="red",color="red")+theme_bw()+ggtitle("pdf of Negative Binomial type I;Mu=10;sigma=0.5")

cdf=stepfun(0:49, c(0, pNBI(0:49, mu=10, sigma=0.5 )),f=0)
p=plot(cdf,do.points=FALSE)

p$t=p$t[-1]

p=p%>%as_tibble()
p2 <- bind_rows(old = p,new = p %>% mutate(y = lag(y)),.id = "source")%>%arrange(t, source)

ggplot(p) + 
  geom_ribbon(aes(x =t, ymin = 0, ymax = y),data=p2,alpha=0.5,fill="red",color="red")+
  theme_bw()+scale_x_continuous("X")+
  scale_y_continuous("f(X)")+
  ggtitle("cdf of Negative Binomial type I;Mu=10;sigma=0.5")

Nhưng quan trọng nhất, đó là bạn có thể mang áp dụng bất kì họ phân phối nào trong danh mục cho mô hình của mình.

Cũng như trong phương pháp GLM cổ điển, tất cả 3 thành tố trong mô hình Gamlss đều có thể được lựa chọn, tinh chỉnh và tối ưu hóa nhờ vào quá trình chọn lọc, thử nghiệm, so sánh. Thí dụ, thành tố thứ nhất : phân phối (và tham số) có thể được chọn lọc ở nhiều cấp độ :

  1. Trước công đoạn mô hình hóa : họ phân phối phù hợp được chỉ định dựa vào suy luận (bản chất biến số) và trực giác sau khi thăm dò số liệu (thống kê mô tả, histogram, density curve).

  2. Quá trình thăm dò dữ liệu bằng thống kê mô tả có thể xem như tương đương với việc dựng 1 Mô hình tối giản : Thử nghiệm hàng loạt họ phân phối cho mô hình chỉ chứa intercept

  3. Kết hợp với quy trình xây dựng mô hình : Model training (fitting), sử dụng các phương thức như Step-wise AIC, Likelihood ratio, K-fold cross validation, bootstrapping … nhằm chọn lọc mô hình chứa những biến số và họ phân phối phù hợp nhất.

  4. Trong quá trình kiểm định mô hình : Khảo sát residual (sai số dự báo) của mô hình để xác nhận tính chính xác của họ phân phối đã chọn, so sánh hiệu năng mô hình (benchmark study)… Gamlss cung cấp gần như đầy đủ khả năng để làm tất cả 4 công đoạn nêu trên, kể cả K-folds cross validation (gamlss là 1 framework machine learning thật sự dành riêng cho algorithm GAM)

2 Một thí dụ minh họa: CD4 dataset

Nhi sẽ lấy 1 dataset đơn giản làm thí dụ. Đây là bộ số liệu về số lượng bạch cầu CD4 đo được trên 600 trẻ em sinh ra từ người mẹ bị nhiễm HIV. Mục tiêu của thí dụ là xây dựng mô hình dự báo giá trị lượng bạch cầu CD4 theo tuổi trong giới hạn từ sơ sinh đến 8 tuổi.

data(CD4)

psych::describe(CD4)
##     vars   n   mean     sd median trimmed    mad  min     max   range skew
## cd4    1 609 557.53 462.50 435.00  489.91 367.68 0.00 2327.00 2327.00 1.26
## age    2 609   2.62   1.31   2.35    2.49   1.20 0.25    8.18    7.93 1.08
##     kurtosis    se
## cd4     1.13 18.74
## age     1.55  0.05

Thăm dò số liệu cho ta thấy Biến cd4 có trung bình = 557.53, giá trị nhỏ nhất là 0, lớn nhất là 2327 (đơn vị tế bào/ml máu), skewness = 1.26, kurtosis = 1.13 và sd=462.5 Những giá trị này gợi ý về một phân phối điển hình của biến số đếm (count data), thực chất số lượng tế bào là 1 biến kiểu số và rời rạc chỉ nhận giá trị số nguyên dương.

3 Nhận định trực quan về đặc tính phân phối

Quy trình thăm dò, mô tả và thử nghiệm có thể được thực thi thông qua 2 hàm trong gamlss : truehist, histDist

Hàm truehist chỉ đơn giản vẽ ra 1 histogram, t có cũng thể làm việc này dễ dàng với base R hay cầu kì hơn với ggplot2

truehist(CD4$cd4)

hist(CD4$cd4)

CD4%>%ggplot(aes(x=cd4))+
  geom_density(alpha=.8,fill="#c507ff")+
  geom_histogram(aes(y=..density..,fill=..density..),colour="black",alpha=0.7,show.legend = F)+
  theme_bw()+scale_fill_gradient(low="#ff9c07",high="#ff072f")+
  ggtitle("Density + Histogram")

cdfdf=cbind.data.frame(cd4=unique(CD4$cd4),ecdf = ecdf(CD4$cd4)(unique(CD4$cd4)))

ggplot(cdfdf) + 
  geom_area(aes(x=cd4, y=ecdf),color="red",fill="#ff0741",alpha=0.8)+
  theme_bw()+scale_x_continuous("CD4 count")+
  scale_y_continuous("ecdf")+
  ggtitle("ECDF of cd4")

CD4%>%ggplot(aes(x=age,y=cd4))+
  geom_point(shape=21,aes(color=age,fill=age),show.legend = F)+
  geom_smooth(se=F,color="darkviolet")+
  theme_bw()+
  scale_fill_gradient2(high="#8e00b2",mid="#ff9c07",low="#ef9b09")+
  scale_color_gradient2(high="#8e00b2",mid="#ff9c07",low="#ef9b09")

4 Thăm dò phân phối dựa vào mô hình tối giản

Hàm histDist cho phép thăm dò 1 họ phân phối bất kì mà gamlss.dist hỗ trợ, thí dụ Negative binomial, bản chất của nó chính là một mô hình tối giản chỉ chứa Intercept với 1 họ phân phối xác định. Mô hình này cũng được lượng giá bằng BIC, AIC, Likelihood ratio… để dựa vào đó ta có thể so sánh các họ phân phối với nhau.

histDist(data=CD4,cd4,family=NBI())

## 
## Family:  c("NBI", "Negative Binomial type I") 
## Fitting method: "nlminb" 
## 
## Call:  gamlssML(formula = cd4, family = "NBI", data = CD4) 
## 
## Mu Coefficients:
## [1]  6.324
## Sigma Coefficients:
## [1]  -0.1869
## 
##  Degrees of Freedom for the fit: 2 Residual Deg. of Freedom   607 
## Global Deviance:     8909.14 
##             AIC:     8913.14 
##             SBC:     8921.96

fitDist là 1 hàm “thần kì”, khi áp dụng nó cho 1 biến số Y ta chỉ cần cung cấp thông tin về bản chất của thang đo (thí dụ số thực, số thực mở rộng, hỗn hợp, số đếm, xác suất…), gamlss sẽ sử dụng brute-force để kiểm tra toàn bộ (từ 20-40 loại) những họ phân phối tiềm năng cho biến số này, và tự động xác định phân phối tối ưu nhất.

Trong thí dụ này, hàm fitDist cho biết phân phối thích hợp nhất cho CD4 là Zero inflated negative binomial (ZINBI). Lưu ý là tiêu chuẩn lựa chọn ở đây là BIC (AIC hiệu chỉnh với k=log(cỡ mẫu))

Object xuất ra là 1 mô hình tối giản với phân phối ZINBI, ta có thể khảo sát nó : Ta có thể xâm nhập vào object để xem xét BIC của 16 họ phân phối mà mô hình có thể converged, ZINBI đứng đầu danh sách còn Poisson xếp chót bảng

set.seed=123

explore=fitDist(cd4,data=CD4,type="counts",k=log(nrow(CD4)),parallel="multicore",ncpus = nC)

explore
## 
## Family:  c("ZINBI", "Zero inflated negative binomial type I") 
## Fitting method: "nlminb" 
## 
## Call:  gamlssML(formula = y, family = DIST[i], parallel = "multicore",  
##     ncpus = ..2, data = sys.parent()) 
## 
## Mu Coefficients:
## [1]  6.335
## Sigma Coefficients:
## [1]  -0.3108
## Nu Coefficients:
## [1]  -4.478
## 
##  Degrees of Freedom for the fit: 3 Residual Deg. of Freedom   606 
## Global Deviance:     8875.95 
##             AIC:     8881.95 
##             SBC:     8895.19
plot(explore)

## ******************************************************************
##   Summary of the Randomised Quantile Residuals
##                            mean   =  0.008977322 
##                        variance   =  0.9817547 
##                coef. of skewness  =  -0.1344454 
##                coef. of kurtosis  =  3.020631 
## Filliben correlation coefficient  =  0.9966616 
## ******************************************************************
explore$fits
##      ZINBI      ZANBI       NBII        NBI       GEOM     SICHEL 
##   8895.188   8895.188   8921.963   8921.963   8927.554   8928.375 
##        DEL     WARING      ZIPIG        PIG       ZALG       YULE 
##   8928.375   8933.966   9222.426   9454.245   9931.132  14307.884 
##       ZIP2        ZAP        ZIP         PO 
## 213819.609 213819.609 213819.609 221587.433

Có 1 package khác trong R là fitdistrplus cũng cho phép làm điều tương tự, chỉ khác là nó không phổ quát như gamlss.dist, vì số họ phân phối mà hàm descdist hỗ trợ chỉ đếm trên đầu ngón tay. Ta thử khảo sát biến cd4 bằng package fitdistrplus như sau:

Dựa vào 1 bootstrap 10 ngàn lượt và Biểu đồ Cullen&Frey , ta có thể khẳng định phân phối của cd4 KHÔNG thể là Poisson, mà cũng chẳng phải là Negative binomial và chắc chắn không thể là normal

library(fitdistrplus)
## Warning: package 'fitdistrplus' was built under R version 3.4.1
## Loading required package: survival
descdist(CD4$cd4,discrete = TRUE,boot=10000,boot.col="gold",obs.pch=16,obs.col="red4")

## summary statistics
## ------
## min:  0   max:  2327 
## median:  435 
## mean:  557.5337 
## estimated sd:  462.4983 
## estimated skewness:  1.263977 
## estimated kurtosis:  4.163153

Gamlss hỗ trợ 24 họ phân phối cho số đếm (count data) , đặc tính của chúng nằm trong bảng sau đây: Phân phối ZINBI có 3 tham số Mu (link function= log), Sigma (link function = log) và Nu (link function = logit).

Ta có thể tìm hiểu về bản chất của họ phân phối bằng hàm gamlss.family và show.link

gamlss.family(ZINBI)
## 
## GAMLSS Family: ZINBI Zero inflated negative binomial type I 
## Link function for mu   : log 
## Link function for sigma: log 
## Link function for nu   : logit
show.link(ZINBI)
## $mu
## c("inverse", "log", "identity")
## 
## $sigma
## c("inverse", "log", "identity")
## 
## $nu
## c("logit", "probit", "cloglog", "log", "own")

Module gamlss.tr cho phép người dùng tạo ra 1 phân phối của riêng mình bằng cách chặn 2 đầu 1 phân phối cho trước, thí dụ ta có thể tạo ra 1 phân phối mới có tên là ZINBI1000 với giá trị dao động trong khoảng từ 0-1000

Từ lúc này, họ phân phối mới có thể được đưa vào hàm fitDist và cả hàm gamlss để sử dụng như bất cứ họ phân phối nào được gamlss chính thức hỗ trợ.

library(gamlss.tr)
gen.trun(par=c(0,1000),family="ZINBI", name="1000", type="right",varying=T)
## A truncated family of distributions from ZINBI has been generated 
##  and saved under the names:  
##  dZINBI1000 pZINBI1000 qZINBI1000 rZINBI1000 ZINBI1000 
## The type of truncation is right 
##  and the truncation parameter is 0 1000
gamlss.family(ZINBI1000)
## 
## GAMLSS Family: ZINBI1000 right truncated Zero inflated negative binomial type I 
## Link function for mu   : log 
## Link function for sigma: log 
## Link function for nu   : logit
fitDist(cd4,data=CD4,type="counts",k=log(nrow(CD4)),extra="ZINBI1000",parallel="multicore",ncpus = nC)
## 
## Family:  c("ZINBI", "Zero inflated negative binomial type I") 
## Fitting method: "nlminb" 
## 
## Call:  gamlssML(formula = y, family = DIST[i], parallel = "multicore",  
##     ncpus = ..2, data = sys.parent()) 
## 
## Mu Coefficients:
## [1]  6.335
## Sigma Coefficients:
## [1]  -0.3108
## Nu Coefficients:
## [1]  -4.478
## 
##  Degrees of Freedom for the fit: 3 Residual Deg. of Freedom   606 
## Global Deviance:     8875.95 
##             AIC:     8881.95 
##             SBC:     8895.19

5 Chọn lọc họ phân phối trong quá trình xây dựng mô hình: Vuong test và stepwise GAIC

Bây giờ Nhi sẽ thử cách làm khác, đó là kiểm tra họ phân phối TRONG KHI dựng mô hình, tức là mô hình sẽ chứa đầy đủ predictor chứ không chỉ có Intercept. Cách làm thủ công đơn giản nhất đó là so sánh bắt cặp 2 mô hình với 2 họ phân phối khác nhau (thí dụ Poisson và ZINBI), sau đó sử dụng 2 kiểm định Vương-Clarke để so sánh độ phù hợp dữ liệu của 2 mô hình (ta không thể dùng Likelihood ratio test thông thường do 2 mô hình này non-nested)

mPO=gamlss(formula=cd4~pb(age),data=CD4,family=PO(),
          parallel="multicore",
          ncpus = nC)

mZINBI=gamlss(formula=cd4~pb(age),data=CD4,family=ZINBI(),
              parallel="multicore",
              ncpus = nC)

Kết quả của Vuong test cho ra chỉ số thống kê Z rất thấp, còn Clarke test thì cho ra giá trị p rất thấp, cho thấy mô hình ZINBI tốt hơn so với Poisson

VC.test(mPO, mZINBI, sig.lev = 0.05)
##  Vuong's test: -16.973 model mZINBI is preferred over mPO 
## Clarke's test: 83 p-value= 0 mZINBI is preferred over mPO

Ta cũng có thể dùng tiêu chuẩn AIC và BIC để lựa chọn mô hình : Kết quả của cả BIC và AIC đều ủng hộ cho mô hình ZINBI

GAIC(mPO,mZINBI)
##              df        AIC
## mZINBI 6.657584   8672.814
## mPO    6.951758 147256.463
GAIC(mPO,mZINBI,k=log(nrow(CD4)))
##              df        AIC
## mZINBI 6.657584   8702.186
## mPO    6.951758 147287.132

Ta có thể thực hiện quy trình này cho hàng loạt biến số nhưng sử dụng trực tiếp tiêu chí AIC hoặc BIC để quyết định lựa chọn phân phối nào. Trong thí dụ sau Nhi so sánh 4 mô hình với phân phối Poisson, Negative binomial, Zero inflated poisson, và zero inflated negative binomial.

#GAIC

mPO=gamlss(cd4~pb(age),
            data=CD4,
            family=PO(), 
            rand=rand,
            parallel="multicore",
            ncpus = nC)

mZINBI=gamlss(cd4~pb(age),
            data=CD4,
            family=ZINBI(), 
            rand=rand,
            parallel="multicore",
            ncpus = nC)

mNBI=gamlss(cd4~pb(age),
          data=CD4,
          family=NBII(), 
          rand=rand,
          parallel="multicore",
          ncpus = nC)

mZIP=gamlss(cd4~pb(age),
            data=CD4,
            family=ZIP(), 
            rand=rand,
            parallel="multicore",
            ncpus = nC,method=RS(500))
GAIC(mPO,mZIP,mZINBI,mNBI,k=log(nrow(CD4)))
##               df        AIC
## mZINBI  6.657584   8702.186
## mNBI   10.950083   8834.964
## mZIP   23.446697 133872.144
## mPO     6.951758 147287.132

6 Chọn lọc quy luật phân phối dựa vào K folds cross validation

Kiểm chứng chéo lặp lại là 1 cách làm khác. Ngoài quy trình stepwise, gamlss còn cho phép dùng K fold cross validation để so sánh độ phù hợp của hàng loạt họ phân phối, thí dụ Poisson, Negative binomial, Zero inflated poisson, zero inflated negative binomial. Chúng ta sẽ thực hiện 1 quy trình như vậy với 10 folds cross validation, chia ngẫu nhiên dataset CD4 ra thành 10 blocks, dùng 9 blocks để dựng mô hình rồi kiểm định trên 1 block còn lại, cứ thế lặp lại 10 lần.

set.seed(123)
rand=sample (10, nrow(CD4), replace=TRUE)

mcvPO=gamlssCV(cd4~pb(age),
               data=CD4,
               family=PO(), 
               rand=rand,
               parallel="multicore",
               ncpus = nC)

mcvNBI=gamlssCV(cd4~pb(age),
            data=CD4,
            family=NBII(), 
            rand=rand,
            parallel="multicore",
            ncpus = nC)

mcvZINBI=gamlssCV(cd4~pb(age),
            data=CD4,
            family=ZINBI(), 
            rand=rand,
            parallel="multicore",
            ncpus = nC)

mcvZIP=gamlssCV(cd4~pb(age),
            data=CD4,
            family=ZIP(), 
            rand=rand,
            parallel="multicore",
            ncpus = nC)
CV(mcvZIP,mcvZINBI,mcvNBI,mcvPO)
##          val[o.val]
## mcvZINBI   8671.850
## mcvNBI     8813.045
## mcvZIP   142360.871
## mcvPO    150765.188

7 Chọn lọc biến số cho mô hình của từng tham số, sử dụng StepwiseGAIC

Một khi đã chắc chắn về họ phân phối, ta có thể đi bước tiếp theo, đó là xác định xem liệu có cần mô hình hóa cho tất cả 3 tham số không ? và mô hình phức tạp (sâu) đến mức nào ? Hay nói cách khác: Ta sẽ phân phối predictor vào mỗi tham số Mu, Sigma, Nu như thế nào cho hợp lý ? Vì theo mặc định, nếu không khai báo formula cho Sigma và Nu thì gamlss ưu tiên dựng mô hình cho Mu (trung bình dự báo), còn sigma, Nu và Tau chỉ được mô hình bằng hằng số Intercept. Câu hỏi này có thể được giải quyết bằng hàm stepGAICAll.A; hàm này sẽ thực hiện 1 quy trình lựa chọn biến số cho từng tham số Mu, Sigma, Nu (và Tau, nếu cần) theo quy tắc stepwise, tức là loại bỏ dần dần biến số, dựa vào tiêu chuẩn AIC hoặc BIC. Trong thí dụ dưới đây, chúng ta đặt ra 2 ngưỡng: thấp nhất là 1 mô hình chỉ chứa Intercept, cao nhất là mô hình đa thức bậc 5 cho biến số age kèm theo penalize B spline cho age, tiêu chuẩn lựa chọn là BIC.

Đặc biệt, gamlss cho phép thực hiện quy trình song song và tận dụng tối đa là 4 cores trong máy tính để tăng tốc độ xử lý (tăng đến 50% cho 1 máy tính Core i5)

m1<-gamlss(cd4~1, data=CD4, family=ZINBI(), trace=FALSE)
m2<-stepGAICAll.A(m1, 
                  scope=list(lower=~1, 
                             upper=~poly(age,5)+pb(age)
                             ),
                  k=log(nrow(CD4)),
                  parallel="multicore",
                  ncpus = nC
                  )

Kết quả không bất ngờ lắm: quy trình stepwiseAIC cho thấy chỉ cần pbspline cho Mu, và Intercept cho 2 tham số Sigma và Nu.

summary(m2)
## ******************************************************************
## Family:  c("ZINBI", "Zero inflated negative binomial type I") 
## 
## Call:  gamlss(formula = cd4 ~ pb(age), family = ZINBI(), data = CD4,  
##     trace = FALSE, sigma.formula = ~1, nu.formula = ~1) 
## 
## Fitting method: RS() 
## 
## ------------------------------------------------------------------
## Mu link function:  log
## Mu Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  7.67254    0.06711  114.32   <2e-16 ***
## pb(age)     -0.43241    0.02284  -18.93   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## Sigma link function:  log
## Sigma Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -0.62324    0.05441  -11.45   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## Nu link function:  logit 
## Nu Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -4.4559     0.3808   -11.7   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## NOTE: Additive smoothing terms exist in the formulas: 
##  i) Std. Error for smoothers are for the linear effect only. 
## ii) Std. Error for the linear terms maybe are not accurate. 
## ------------------------------------------------------------------
## No. of observations in the fit:  609 
## Degrees of Freedom for the fit:  6.657584
##       Residual Deg. of Freedom:  602.3424 
##                       at cycle:  5 
##  
## Global Deviance:     8659.499 
##             AIC:     8672.814 
##             SBC:     8702.186 
## ******************************************************************
model<-gamlss(cd4~pb(age),data=CD4, family=ZINBI(), trace=FALSE)

Chúng ta kết thúc bài hôm nay bằng 2 hình vẽ đẹp mắt biểu diễn đồ thị của 1 mô hình zeroinflated negatitive binomial kèm theo spline bù trừ nhằm ước tính số lượng tế bào bạch cầu CD4 theo tuổi ở trẻ nhỏ.

summary(model)
## ******************************************************************
## Family:  c("ZINBI", "Zero inflated negative binomial type I") 
## 
## Call:  gamlss(formula = cd4 ~ pb(age), family = ZINBI(), data = CD4,  
##     trace = FALSE) 
## 
## Fitting method: RS() 
## 
## ------------------------------------------------------------------
## Mu link function:  log
## Mu Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  7.67254    0.06711  114.32   <2e-16 ***
## pb(age)     -0.43241    0.02284  -18.93   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## Sigma link function:  log
## Sigma Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -0.62324    0.05441  -11.45   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## Nu link function:  logit 
## Nu Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  -4.4559     0.3808   -11.7   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ------------------------------------------------------------------
## NOTE: Additive smoothing terms exist in the formulas: 
##  i) Std. Error for smoothers are for the linear effect only. 
## ii) Std. Error for the linear terms maybe are not accurate. 
## ------------------------------------------------------------------
## No. of observations in the fit:  609 
## Degrees of Freedom for the fit:  6.657584
##       Residual Deg. of Freedom:  602.3424 
##                       at cycle:  5 
##  
## Global Deviance:     8659.499 
##             AIC:     8672.814 
##             SBC:     8702.186 
## ******************************************************************
plot(model)

## ******************************************************************
##   Summary of the Randomised Quantile Residuals
##                            mean   =  0.02955598 
##                        variance   =  0.9040999 
##                coef. of skewness  =  -0.3523449 
##                coef. of kurtosis  =  2.879732 
## Filliben correlation coefficient  =  0.9942961 
## ******************************************************************
CD4$predict=predict(model,type="response")
CD4%>%ggplot()+
  geom_point(shape=21,aes(x=age,y=cd4,color=age,fill=age),show.legend = F)+
  geom_smooth(aes(x=age,y=predict),se=F,color="darkviolet",linetype=2)+
  theme_bw()+
  scale_fill_gradient2(high="#8e00b2",mid="#ff9c07",low="#ef9b09")+
  scale_color_gradient2(high="#8e00b2",mid="#ff9c07",low="#ef9b09")
## `geom_smooth()` using method = 'loess'

library(gamlss.util)
## Warning: package 'gamlss.util' was built under R version 3.4.1
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
library(viridisLite)

plotSimpleGamlss(y=cd4,x=age,formula=cd4~pb(age),family=ZINBI(),data=CD4,cols=viridis(option="A",n=100),x.val=c(1,2,3,4,5,6,7,8),val=200,N=1000,ylim=c(0,2300))

## new prediction

8 Kết luận

Trong bài thực hành thứ 2 của series về Gamlss này, Nhi muốn chuyển đến các bạn 3 thông điệp như sau:

  1. Khái niệm về thành phần phân phối trong cấu trúc mô hình GAM

  2. Cách thăm dò và chọn lọc quy luật phân phối phù hợp cho mô hình

  3. Quy trình Stepwise và Kfold cross validation

Gamlss là một thư viện khổng lồ về lý thuyết xác suất. Tuy nhiên chúng ta chỉ mới hé mở cánh cửa này. Hy vọng với sự tò mò của mình, các bạn có thể tự mình khám phá hơn 100 họ phân phối còn lại cho biến số thực, biến liên tục dương, biến liên tục giới hạn, tỉ lệ, xác suất… Trong những bài tiếp theo chúng ta sẽ tiếp tục khám phá cấu trúc bên trong mô hình GAM bao gồm thành tố hồi quy và các yếu tố bù trừ.

Xin cảm ơn và hẹn gặp lại.

LS0tDQp0aXRsZTogIlPhu60gZOG7pW5nIHBhY2thZ2UgR2FtbHNzIg0Kc3VidGl0bGU6ICJCw6BpIDI6IFjDoWMgxJHhu4tuaCBxdXkgbHXhuq10IHBow6JuIHBo4buRaSBjaG8gbcO0IGjDrG5oIEdBTSINCmF1dGhvcjogIkzDqiBOZ+G7jWMgS2jhuqMgTmhpIg0KZGF0ZTogIjI1IFRow6FuZyA3IDIwMTciDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiAiZGVmYXVsdCINCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCg0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmxpYnJhcnkoZ2FtbHNzKQ0KDQpuQzwtZGV0ZWN0Q29yZXMoKQ0KYGBgDQoNCiFbXShnYW1sc3MyLnBuZykNCg0KDQojIEdhbWxzcyBsw6AgbeG7mXQgdGjGsCB2aeG7h24ga2jhu5VuZyBs4buTIHbhu4EgcXV5IGx14bqtdCBwaMOibiBwaOG7kWkgDQoNClRyb25nIGLDoGkgdHLGsOG7m2MsIE5oaSDEkcOjIGdp4bubaSB0aGnhu4d1IHbhu5tpIGPDoWMgYuG6oW4gcGFja2FnZSBHYW1sc3MsIG3hu5l0IGLhuqNvIGLhu5FpIGNobyBtw7QgaMOsbmggdHV54bq/biB0w61uaCB04buVbmcgcXXDoXQuIE3hu5l0IHRyb25nIG5o4buvbmcgxrB1IHRo4bq/IGPhu6dhIGdhbWxzcyBsw6AgY+G6pXUgdHLDumMgdGluaCB04bq/IGPhu6dhIG3DtCBow6xuaC4gVHLGsOG7m2MgaOG6v3QsIEdBTSBsw6Agbmjhu69uZyBtw7QgaMOsbmggaOG7k2kgcXV5IGLDoW4gdGhhbSBz4buRIGNodXnDqm4gYmnhu4d0IHbhu4EgcGjDom4gcGjhu5FpIChkaXN0cmlidXRpb24gYmFzZWQgc2VtaXBhcmFtZXRyaWMgcmVncmVzc2lvbikuIEPDsyBuZ2jEqWEgbMOgIHRyxrDhu5tjIHRpw6puIG5nxrDhu51pIGTDuW5nIHBo4bqjaSB4w6FjIMSR4buLbmggMSBo4buNIHBow6JuIHBo4buRaSBjaHV5w6puIGJp4buHdCBjaG8gYmnhur9uIGvhur90IHF14bqjIDsgc2F1IMSRw7MgdMO5eSB2w6BvIGLhuqNuIGNo4bqldCBj4bunYSBwaMOibiBwaOG7kWkgbsOgeSx04burIDIgxJHhur9uIDQgbcO0IGjDrG5oIGtow6FjIG5oYXUgc+G6vSDEkcaw4bujYyB4w6J5IGThu7FuZyBjaG8gdG/DoG4gYuG7mSBjw6FjIHRoYW0gc+G7kSBj4bunYSBwaMOibiBwaOG7kWkuDQoNCkLhu5FuIHRoYW0gc+G7kSBuw6B5IMSRxrDhu6NjIGfhu41pIHTDqm4gbMOgIE11LCBTaWdtYSwgTnUgdsOgIFRhdSwgdMawxqFuZyDhu6luZyB24bubaSA0IMSR4bq3YyB0w61uaCA6IFbhu4sgdHLDrSB0cnVuZyB0w6JtIChMb2NhdGlvbiA9IE11KSwgU2NhbGUgKHNhaSBiaeG7h3QsIFNpZ21hKSwgU2tld25lc3MgKE51KSB2w6AgS3VydG9zaXMgKFRhdSkuIE5oxrAgduG6rXkgTXUgdsOgIFNpZ21hIHjDoWMgxJHhu4tuaCBnacOhIHRy4buLIFRydW5nIGLDrGhuIHbDoCDEkeG7mSBiaeG6v24gdGhpw6puIGPhu6dhIHBow6JuIHBo4buRaSwgdHJvbmcga2hpIFRhdSB2w6AgTnUgeMOhYyDEkeG7i25oIGtp4buDdSBow6xuaCBj4bunYSBwaMOibiBwaOG7kWkgKGjDrG5oIGThuqFuZyBj4bunYSDEkeG7kyB0aOG7iyBow6BtIHBkZikuDQoNClRow6BuaCB04buRIHRo4bupIG5o4bqldCBj4bunYSBtw7QgaMOsbmggZ2FtbHNzIGNow61uaCBsw6AgaOG7jSBwaMOibiBwaOG7kWkgdsOgIGPDoWMgdGhhbSBz4buRIG7DoHkuIFRow6BuaCB04buRIHRo4bupIGhhaSBsw6AgY8OhYyBwaOG6p24gdOG7rSBo4buTaSBxdXkgKHByZWRpY3RvcnMpIGJhbyBn4buTbSBo4bqxbmcgc+G7kSAoaW50ZXJjZXB0KSwgY8OhYyBiaeG6v24gxJHhu5ljIGzhuq1wLCBi4bqtYyDEkWEgdGjhu6ljIHbDoCB0xrDGoW5nIHTDoWMgZ2nhu69hIGNow7puZy4gVGjDoG5oIHThu5EgdGjhu6kgMyBsw6AgcGjhuqduIHThu60gYsO5IHRy4burIGJhbyBn4buTbSB04bqldCBj4bqjIGPDoWMgcGjGsMahbmcgcGjDoXAgaGnhu4d1IGNo4buJbmggbmjhurFtIHTEg25nIGto4bqjIG7Eg25nIChjYXBhY2l0eSkgY+G7p2EgbcO0IGjDrG5oLCBiYW8gZ+G7k20gc21vb3RoaW5nIHNwbGluZXMsIHBlbmFsaXplZCBzcGxpbmVzLCBuZXVyYWwgbmV0d29yaywgZGVjaXNpb24gdHJlZSwgcGllY2Ugd2lzZeKApiANCg0KVHJvbmcgYsOgaSBuw6B5IGNow7puZyB0YSBjaOG7iSBxdWFuIHTDom0gxJHhur9uIFRow6BuaCB04buRIHRo4bupIG5o4bqldCBsw6AgUGjDom4gcGjhu5FpIHbDoCB0aGFtIHPhu5EgY+G7p2EgbsOzLg0KDQpUaOG7kW5nIGvDqiBtw7QgdOG6oyBsw6AgbeG7mXQgcXV5IHRyw6xuaCBxdWVuIHRodeG7mWMgdGEgaGF5IGTDuW5nIMSR4buDIHRoxINtIGTDsiBk4buvIGxp4buHdSwgdsOgIG3hu6VjIHRpw6p1IGPhu6dhIG7DsyBjaMOtbmggbMOgIG7huq9tIGPDoWMgxJHhurdjIHTDrW5oIHBow6JuIHBo4buRaSBj4bunYSBk4buvIGxp4buHdSB0cm9uZyBt4bqrdS4gTmhpIHbDoCBjw6FjIGLhuqFuIHbhuqtuIGhheSB0w61uaCBnacOhIHRy4buLIHRydW5nIGLDrG5oLCBTRCwgc2tld25lc3MgdsOgIGt1cnRvc2lzLiBUYSBjw7JuIHbhur0gaGlzdG9ncmFtIMSR4buDIHBow6JuIHTDrWNoIHRy4buxYyBxdWFuIMSR4bq3YyB0w61uaCBuw6B5LiBWaeG7h2MgdMOtbmggdG/DoW4gbsOgeSBtYW5nIGzhuqFpIHRow7RuZyB0aW4ga2jDoSDEkeG6p3kgxJHhu6cgduG7gSDEkeG6t2MgdMOtbmggcGjDom4gcGjhu5FpIGPhu6dhIGJp4bq/biBz4buRLiBHacOhbyB0csOsbmggeMOhYyBzdeG6pXQgdHJvbmcgdHLGsOG7nW5nIMSR4bqhaSBo4buNYyDEkcOjIG3DtCB04bqjIGNobyBjaMO6bmcgdGEgbeG7mXQgc+G7kSBxdXkgbHXhuq10IHBow6JuIHBo4buRaSBj4buVIMSRaeG7g24gbmjGsCBHYXVzc2lhbiwgUG9pc3NvbiwgQmlub21pYWwsIHQgdsOgIENoaTIuIE5oxrBuZywgxJHhu4MgZ2nDunAgY8OhYyBi4bqhbiBsacOqbiBo4buHIGdp4buvYSBiaeG6v24gc+G7kSB2w6AgbcO0IGjDrG5oIEdBTUxTUywgTmhpIG114buRbiBjaMO6bmcgdGEgaMOsbmggZHVuZyB24buBIGJp4bq/biBz4buRIG5oxrAgMSBow6BtIHRvw6FuIGjhu41jIGNo4bupIGtow7RuZyBwaOG6o2kgbmjGsCAxIMSR4bqhaSBsxrDhu6NuZy4gVGjhuq10IHbhuq15LCBi4bqldCBj4bupIGJp4bq/biBz4buRIG7DoG8gY8WpbmcgY8OzIHRo4buDIHhlbSBuaMawIG3hu5l0IGjDoG0uIEvhur90IHF14bqjIGPhu6dhIGjDoG0gbsOgeSBsw6AgbeG7mXQgY29uIHPhu5EgKGdpw6EgdHLhu4spIHbDoCBt4buXaSBnacOhIHRy4buLIG5oxrAgduG6rXkgZ+G6r24gduG7m2kgbeG7mXQgeMOhYyBzdeG6pXQgbcOgIGdpw6EgdHLhu4sgxJHDsyBjw7MgdGjhu4MgeHXhuqV0IGhp4buHbiB0cm9uZyBxdeG6p24gdGjhu4MuIFRow60gZOG7pSwgdGhlbyBsw70gdGh1eeG6v3Qgc+G7kSBsxrDhu6NuZyBi4bqhY2ggY+G6p3UgdHJvbmcgbeG7mXQgbUwgbcOhdSB0xKluaCBt4bqhY2ggY8OzIHRo4buDIG5o4bqtbiBi4bqldCBj4bupIGdpw6EgdHLhu4sgc+G7kSBuZ3V5w6puIGTGsMahbmcgbsOgbyB0cm9uZyBraG/huqNuZyB04burIDAgxJHhur9uIDEwLjAwMCBuaMawbmcgeMOhYyBzdeG6pXQgcXVhbiBzw6F0IMSRxrDhu6NjIG3hu5dpIGdpw6EgdHLhu4sgY8OzIHRo4buDIGtow6FjIG5oYXUuIA0KDQpIw6BtIG3huq10IMSR4buZIHjDoWMgc3XhuqV0IChwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIOKAkyBwZGYgKSBjaG8gcGjDqXAgxrDhu5tjIHTDrW5oIHjDoWMgc3XhuqV0IGNobyBt4buXaSBnacOhIHRy4buLIGLhuqV0IGvDrCB0cm9uZyAxIGtob+G6o25nICh0aGFuZyDEkW8pIHjDoWMgxJHhu4tuaC4gSMOgbSBwZGYgY8OzIGxpw6puIGjhu4cgcuG6pXQgZ+G6p24gZ8WpaSB24bubaSBiaeG7g3UgxJHhu5MgdOG6p24gc3XhuqV0IChoaXN0b2dyYW0pLiBUcm9uZyB0aOG7sWMgaMOgbmgsIHZp4buHYyB24bq9IGhpc3RvZ3JhbSBr4bq/dCBo4bujcCB24bubaSDEkeG7kyB0aOG7iyBj4bunYSBow6BtIHBkZiAoZGVuc2l0eSBjdXJ2ZSkgY2hvIHBow6lwIG3DtCB04bqjIHRy4buxYyBxdWFuIMSR4bq3YyB0w61uaCBwaMOibiBwaOG7kWkgY+G7p2EgbeG7mXQgYmnhur9uIHPhu5EgbcOgIHRhIGPhuqduIHRoxINtIGTDsi4gVOG7qyBoaXN0b2dyYW0gY8OzIHRo4buDIMaw4bubYyB0w61uaCBow6BtIHBkZiBuaMawbmcgYuG6o24gY2jhuqV0IGPhu6dhIDIgdGjhu6kgbsOgeSBob8OgbiB0b8OgbiBraMOhYyBuaGF1IDogaGlzdG9ncmFtIGzDoCAxIHBow6lwIHRo4buRbmcga8OqIChk4buxYSB2w6BvIGNo4buNbiBt4bqrdSkgdHJvbmcga2hpIFBERiB0aHXhuqduIHTDunkgbMOgIHjDoWMgc3XhuqV0LiANCg0KTmdvw6BpIHJhLCBow6BtIG3huq10IMSR4buZIHjDoWMgc3XhuqV0IHTDrWNoIGzFqXkgKGN1bXVsYXRpdmUgZGVuc2l0eSBmdW5jdGlvbiwgY2RmKSBsw6AgbeG7mXQgY8OhY2gga2jDoWMgxJHhu4MgYmnhu4N1IGRp4buFbiDEkeG6t2MgdMOtbmggcGjDom4gcGjhu5FpIGPhu6dhIG3hu5l0IGJp4bq/bi4gQ0RGIGJp4buDdSB0aOG7iyB04buVbmcgbeG6rXQgxJHhu5kgeMOhYyBzdeG6pXQgY+G7p2EgdOG6pXQgY+G6oyB0csaw4budbmcgaOG7o3AgWSB0w61uaCDEkeG6v24gduG7iyB0csOtIFlpIG3DoCB0YSBj4bqnbiBraOG6o28gc8OhdC4gVGjhu7FjIHJhIGPhuqMgcGRmIHbDoCBjZGYgxJHhu4F1IGN1bmcgY+G6pXAgdGjDtG5nIHRpbiBuaMawIG5oYXUsIHbDoCBjw7MgdGjhu4MgaG/DoW4gY2h1eeG7g24gLg0KDQpOaMawIE5oaSDEkcOjIG7Ds2kg4bufIHRyw6puLCB0aMO0bmcgdGluIHF1YW4gdHLhu41uZyBuaOG6pXQgdGEgdGjGsOG7nW5nIGtoYWkgdGjDoWMgxJHDsyBsw6AgduG7iyB0csOtIHRydW5nIHTDom0gY+G7p2EgZOG7ryBsaeG7h3UgbuG6sW0g4bufIMSRw6J1LCB2w6Agc2FpIGJp4buHdCBiYW8gbOG7m24gKGPDoWNoIHbhu4sgdHLDrSB0cnVuZyB0w6JtIGJhbyB4YSkgPyBW4buLIHRyw60gdHJ1bmcgdMOibSDEkcaw4bujYyB4w6FjIMSR4buLbmggYuG6sW5nIE1lYW4sIE1lZGlhbiwgTW9kZeKApiwgc2FpIGJp4buHdCDEkcaw4bujYyDGsOG7m2MgdMOtbmggYuG6sW5nIMSR4buZIGzhu4djaCBjaHXhuqluIChzaWdtYSwgc2QpIGhheSBwaMawxqFuZyBzYWkgKHZhcmlhbmNlLCBzaWdtYV4yKS4gVGEgdGjGsOG7nW5nIGhp4buDdSBNZWFuIG5oxrAgbMOgIGFyaXRobWV0cmljIG1lYW4gKGNobyBz4buRIHRo4buxYyBoYXkgc+G7kSBuZ3V5w6puKSBob+G6t2MgZ2VvbWV0cmljIG1lYW4gKGNobyB04buJIGzhu4cpLCB2w6AgdGEgY8WpbmcgcXVlbiBiw6FvIGPDoW8gTWVhbiArLy0gU0QgduG7m2kgZ2nhuqMgxJHhu4tuaCBsw6AgYmnhur9uIHPhu5EgY8OzIHBow6JuIHBo4buRaSBjaHXhuqluLiBUdXkgbmhpw6puIHbhu4sgdHLDrSB0cnVuZyB0w6JtIHRo4buxYyByYSBjw7JuIGPDsyB0aOG7gyB4ZW0gKG3hu5l0IGPDoWNoIHThu5VuZyBxdcOhdCkgbmjGsCBt4buZdCBtw7QgaMOsbmggdOG7kWkgZ2nhuqNuIGPDsyBwaMawxqFuZyB0csOsbmggbMOgIFkgfiAxIChjaOG7iSBjaOG7qWEgaOG6sW5nIHPhu5EgSW50ZXJjZXB0KSB24bubaSBsaW5rIGZ1bmN0aW9uID0gaWRlbnRpdHkgdsOgIGjhu40gcGjDom4gcGjhu5FpIHTDuXkgY2jhu41uLiBMw7pjIG7DoHkgdGjhu7FjIGNo4bqldCB0YSDGsOG7m2MgdMOtbmggTXUgKHRydW5nIGLDrG5oLCBnacOhIHRy4buLIGvDrCB24buNbmcpIGNobyBt4buZdCBwaMOibiBwaOG7kWkgZOG7sWEgdHLDqm4gZOG7ryBsaeG7h3UgcXVhbiBzw6F0IMSRxrDhu6NjLiBOZ2/DoGkgTWVhbiB2w6AgU2QsIHRhIGPDsm4gcXVhbiB0w6JtIMSR4bq/biBTa2V3bmVzcyB2w6AgS3VydG9zaXMsIFNrZXduZXNzIGNobyBiaeG6v3Qgc+G7kSBsaeG7h3UgY+G7p2EgY2jDum5nIHRhIGPDsyBow6xuaCBk4bqhbmcgxJHhu5FpIHjhu6luZyBoYXkgYuG7iyBs4buHY2ggduG7gSAxIHBow61hLCBLdXJ0b3NpcyBjaG8gYmnhur90IGjDrG5oIGThuqFuZyAoxJHhu4luaCkgY+G7p2EgcGjDom4gcGjhu5FpLiBN4buZdCBjw6FjaCB04buVbmcgcXXDoXQsIEdhbWxzcyBjdW5nIGPhuqVwIDEgZnJhbWV3b3JrIMSR4buDIG3DtCBow6xuaCBow7NhICjGsOG7m2MgdMOtbmggZ2nDoSB0cuG7iyBj4bunYSB0aGFtIHPhu5EgxJHDrWNoIHTDuXkgdGhlbyAxIGhheSBuaGnhu4F1IGhp4buHcCBiaeG6v24gc+G7kS954bq/dSB04buRIGtow6FjKSBjaG8gY+G6oyBNdSwgc2lnbWEgdsOgIHRoYW0gc+G7kSBraeG7g3UgaMOsbmggKE51LCBUYXUpIGPhu6dhIG3hu5l0IHBow6JuIHBo4buRaSBi4bqldCBrw6wuIA0KDQpUcsaw4bubYyBraGkgdGjhu7FjIGjDoG5oIG3DtCBow6xuaCBHQU1MU1MgY8OhYyBi4bqhbiBj4bqnbiBiaeG6v3QgMSBz4buxIHRo4bqtdCBnw6J5IHNob2NrLCDEkcOzIGzDoCA6IFRyb25nIHRo4bq/IGdp4bubaSB0aOG7sWMsIHBow6JuIHBo4buRaSBjaHXhuqluIGtow7RuZyB04buTbiB04bqhaSAoc3V5IHLhu5luZyByYSBsw6AgZOG7ryBsaeG7h3UgdGjhu7FjIHThur8ga2jDtG5nIGJhbyBnaeG7nSBwaMO5IGjhu6NwIHR1eeG7h3QgxJHhu5FpIHbhu5tpIGLhuqV0IGPhu6kgbeG7mXQgZOG6oW5nIHBow6JuIHBo4buRaSBsw70gdGh1eeG6v3QgbsOgbyBtw6AgdHLGsOG7nW5nIGzhu5twIGThuqF5IGNobyBjw6FjIGLhuqFuLCBiYW8gZ+G7k20gcGjDom4gcGjhu5FpIGNodeG6qW4pLiBEbyDEkcOzLCB0aGF5IHbDrCBi4buPIGPDtG5nIGzDoG0gbmjhu69uZyBraeG7g20gxJHhu4tuaCDEkeG7gyBraeG7g20gY2jhu6luZyBk4buvIGxp4buHdSBj4bunYSBtw6xuaCBjw7MgcGjDom4gcGjhu5FpIMKrIGLDrG5oIHRoxrDhu51uZyDCuyBoYXkga2jDtG5nLCB0YSBj4bqnbiB0w6xtIG3hu5l0IHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIGfhuqduIChwaMO5IGjhu6NwKSBuaOG6pXQgduG7m2kgZOG7ryBsaeG7h3UgxJFhbmcgY8OzIMSR4buDIGThu7FuZyBtw7QgaMOsbmguIA0KDQpCw6J5IGdp4budIGLhuq90IMSR4bqndSBwaOG6p24gaGF5IG5o4bqldCBj4bunYSBjw6J1IGNodXnhu4duIGjDtG0gbmF5LCDEkcOzIGzDoCBHYW1sc3MgaOG7lyB0cuG7oyB04bqldCBj4bqjICh4aW4gbmjhuqVuIG3huqFuaCwgdOG6pXQgY+G6oykgaOG7jSBwaMOibiBwaOG7kWkgdGjDtG5nIHF1YSBtb2R1bGUgZ2FtbHNzLmRpc3QgOyDEkWnhu4F1IG7DoHkgY8OzIG5naMSpYSBsw6AgZ2FtbHNzIGzDoCBt4buZdCB0aMawIHZp4buHbiBraOG7lW5nIGzhu5MgduG7gSBsw70gdGh1eeG6v3QgeMOhYyBzdeG6pXQuIELhuqFuIGPDsyB0aOG7gyBixrDhu5tjIHbDoG8sIGNo4buNbiBi4bqldCBrw6wgcGjDom4gcGjhu5FpIG7DoG8gdHJvbmcgZGFuaCBt4bulYyBow6BuZyB0csSDbSBo4buNIHBow6JuIHBo4buRaSBraMOhYyBuaGF1IGNobyBz4buRIHRo4buxYywgc+G7kSBuZ3V5w6puIGTGsMahbmcsIHThu4kgbOG7hywg4oCmIHbDoCBjaMahaSDEkcO5YSB0aOG7j2EgdGjDrWNoIHbhu5tpIGjDoG0gcGRmLCBow6BtIGNkZiwgbcO0IHBo4buPbmcgduG7m2kgaMOgbSByYW5kb20sIMaw4bubYyB0w61uaCBxdWFudGlsZXMsIHbhurduIHbhurlvIGRlbnNpdHkgY3VydmUgdsOgIGhpc3RvZ3JhbSB24bubaSAyLTQgdGhhbSBz4buRLGLhuqFuIGPDsyB0aOG7gyBjaOG6t24gMSBoYXkgMiDEkeG6p3UsIHRoYXkgxJHhu5VpIGZ1bmN0aW9uIGxpbmsgdOG7qyBpZGVudGl0eSBzYW5nIGxvZ2FyaXRobSwgbG9naXQsIHByb2JpdOKApiB2w6JuIHbDom4sIGPDsyB0aOG7gyB0cuG7mW4gcGjDom4gcGjhu5FpIHPhu5Egbmd1ecOqbiB2w6Agc+G7kSB0aOG7sWMgduG7m2kgbmhhdSwgdsOgIHThuqFvIHJhIG3hu5l0IHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIGPhu6dhIHJpw6puZyBtw6xuaC4gDQoNClNhdSBraGkgbG9hZCBwYWNrYWdlIGdhbWxzcywgdGhlbyBt4bq3YyDEkeG7i25oIHBhY2thZSBnYW1sc3MuZGlzdCBjxaluZyDEkcaw4bujYyBn4buNaSBsw6puIMSR4buTbmcgdGjhu51pLg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpsaWJyYXJ5KGdhbWxzcykNCg0KbkM8LWRldGVjdENvcmVzKCkNCg0KYGBgDQoNClRyb25nIHRow60gZOG7pSBtaW5oIGjhu41hIHNhdSwgTmhpIGTDuW5nIGjDoG0gZE5CSSB2w6AgcE5CSSB0cm9uZyBnYW1sc3MuZGlzdCDEkeG7gyBtw7QgcGjhu49uZyBow6BtIFBERiB2w6AgQ0RGIGPhu6dhIDEgcGjDom4gcGjhu5FpIE5lZ2F0aXZlIGJpbm9taWFsIFR5cGUgSSAoTkJJKSBjw7MgTXU9MTAsIHNpZ21hPTAuNQ0KDQpgYGB7cn0NCnBsb3QoZnVuY3Rpb24oeSkgZE5CSSh5LCBtdSA9IDEwLCBzaWdtYSA9MC41ICksIGZyb209MCwgdG89NTAsbj01MCsxKSU+JWFzX3RpYmJsZSgpJT4lZ2dwbG90KGFlcyh4PXgseT15KSkrZ2VvbV9hcmVhKGFscGhhPTAuNSxmaWxsPSJyZWQiLGNvbG9yPSJyZWQiKSt0aGVtZV9idygpK2dndGl0bGUoInBkZiBvZiBOZWdhdGl2ZSBCaW5vbWlhbCB0eXBlIEk7TXU9MTA7c2lnbWE9MC41IikNCg0KY2RmPXN0ZXBmdW4oMDo0OSwgYygwLCBwTkJJKDA6NDksIG11PTEwLCBzaWdtYT0wLjUgKSksZj0wKQ0KcD1wbG90KGNkZixkby5wb2ludHM9RkFMU0UpDQpwJHQ9cCR0Wy0xXQ0KDQpwPXAlPiVhc190aWJibGUoKQ0KcDIgPC0gYmluZF9yb3dzKG9sZCA9IHAsbmV3ID0gcCAlPiUgbXV0YXRlKHkgPSBsYWcoeSkpLC5pZCA9ICJzb3VyY2UiKSU+JWFycmFuZ2UodCwgc291cmNlKQ0KDQpnZ3Bsb3QocCkgKyANCiAgZ2VvbV9yaWJib24oYWVzKHggPXQsIHltaW4gPSAwLCB5bWF4ID0geSksZGF0YT1wMixhbHBoYT0wLjUsZmlsbD0icmVkIixjb2xvcj0icmVkIikrDQogIHRoZW1lX2J3KCkrc2NhbGVfeF9jb250aW51b3VzKCJYIikrDQogIHNjYWxlX3lfY29udGludW91cygiZihYKSIpKw0KICBnZ3RpdGxlKCJjZGYgb2YgTmVnYXRpdmUgQmlub21pYWwgdHlwZSBJO011PTEwO3NpZ21hPTAuNSIpDQoNCmBgYA0KDQoNCg0KTmjGsG5nIHF1YW4gdHLhu41uZyBuaOG6pXQsIMSRw7MgbMOgIGLhuqFuIGPDsyB0aOG7gyBtYW5nIMOhcCBk4bulbmcgYuG6pXQga8OsIGjhu40gcGjDom4gcGjhu5FpIG7DoG8gdHJvbmcgZGFuaCBt4bulYyBjaG8gbcO0IGjDrG5oIGPhu6dhIG3DrG5oLiANCg0KQ8WpbmcgbmjGsCB0cm9uZyBwaMawxqFuZyBwaMOhcCBHTE0gY+G7lSDEkWnhu4NuLCB04bqldCBj4bqjIDMgdGjDoG5oIHThu5EgdHJvbmcgbcO0IGjDrG5oIEdhbWxzcyDEkeG7gXUgY8OzIHRo4buDIMSRxrDhu6NjIGzhu7FhIGNo4buNbiwgdGluaCBjaOG7iW5oIHbDoCB04buRaSDGsHUgaMOzYSBuaOG7nSB2w6BvIHF1w6EgdHLDrG5oIGNo4buNbiBs4buNYywgdGjhu60gbmdoaeG7h20sIHNvIHPDoW5oLiBUaMOtIGThu6UsIHRow6BuaCB04buRIHRo4bupIG5o4bqldCA6IHBow6JuIHBo4buRaSAodsOgIHRoYW0gc+G7kSkgY8OzIHRo4buDIMSRxrDhu6NjIGNo4buNbiBs4buNYyDhu58gbmhp4buBdSBj4bqlcCDEkeG7mSA6DQoNCjEpCVRyxrDhu5tjIGPDtG5nIMSRb+G6oW4gbcO0IGjDrG5oIGjDs2EgOiBo4buNIHBow6JuIHBo4buRaSBwaMO5IGjhu6NwIMSRxrDhu6NjIGNo4buJIMSR4buLbmggZOG7sWEgdsOgbyBzdXkgbHXhuq1uIChi4bqjbiBjaOG6pXQgYmnhur9uIHPhu5EpIHbDoCB0cuG7sWMgZ2nDoWMgc2F1IGtoaSB0aMSDbSBkw7Igc+G7kSBsaeG7h3UgKHRo4buRbmcga8OqIG3DtCB04bqjLCBoaXN0b2dyYW0sIGRlbnNpdHkgY3VydmUpLg0KDQoyKQlRdcOhIHRyw6xuaCB0aMSDbSBkw7IgZOG7ryBsaeG7h3UgYuG6sW5nIHRo4buRbmcga8OqIG3DtCB04bqjIGPDsyB0aOG7gyB4ZW0gbmjGsCB0xrDGoW5nIMSRxrDGoW5nIHbhu5tpIHZp4buHYyBk4buxbmcgMSBNw7QgaMOsbmggdOG7kWkgZ2nhuqNuIDogVGjhu60gbmdoaeG7h20gaMOgbmcgbG/huqF0IGjhu40gcGjDom4gcGjhu5FpIGNobyBtw7QgaMOsbmggY2jhu4kgY2jhu6lhIGludGVyY2VwdA0KDQozKQlL4bq/dCBo4bujcCB24bubaSBxdXkgdHLDrG5oIHjDonkgZOG7sW5nIG3DtCBow6xuaCA6IE1vZGVsIHRyYWluaW5nIChmaXR0aW5nKSwgc+G7rSBk4bulbmcgY8OhYyBwaMawxqFuZyB0aOG7qWMgbmjGsCBTdGVwLXdpc2UgQUlDLCBMaWtlbGlob29kIHJhdGlvLCBLLWZvbGQgY3Jvc3MgdmFsaWRhdGlvbiwgYm9vdHN0cmFwcGluZyDigKYgbmjhurFtIGNo4buNbiBs4buNYyBtw7QgaMOsbmggY2jhu6lhIG5o4buvbmcgYmnhur9uIHPhu5EgdsOgIGjhu40gcGjDom4gcGjhu5FpIHBow7kgaOG7o3AgbmjhuqV0Lg0KDQo0KQlUcm9uZyBxdcOhIHRyw6xuaCBraeG7g20gxJHhu4tuaCBtw7QgaMOsbmggOiBLaOG6o28gc8OhdCByZXNpZHVhbCAoc2FpIHPhu5EgZOG7sSBiw6FvKSBj4bunYSBtw7QgaMOsbmggxJHhu4MgeMOhYyBuaOG6rW4gdMOtbmggY2jDrW5oIHjDoWMgY+G7p2EgaOG7jSBwaMOibiBwaOG7kWkgxJHDoyBjaOG7jW4sIHNvIHPDoW5oIGhp4buHdSBuxINuZyBtw7QgaMOsbmggKGJlbmNobWFyayBzdHVkeSnigKYNCkdhbWxzcyBjdW5nIGPhuqVwIGfhuqduIG5oxrAgxJHhuqd5IMSR4bunIGto4bqjIG7Eg25nIMSR4buDIGzDoG0gdOG6pXQgY+G6oyA0IGPDtG5nIMSRb+G6oW4gbsOqdSB0csOqbiwga+G7gyBj4bqjIEstZm9sZHMgY3Jvc3MgdmFsaWRhdGlvbiAoZ2FtbHNzIGzDoCAxIGZyYW1ld29yayBtYWNoaW5lIGxlYXJuaW5nIHRo4bqtdCBz4buxIGTDoG5oIHJpw6puZyBjaG8gYWxnb3JpdGhtIEdBTSkNCg0KIyBN4buZdCB0aMOtIGThu6UgbWluaCBo4buNYTogQ0Q0IGRhdGFzZXQNCg0KTmhpIHPhur0gbOG6pXkgMSBkYXRhc2V0IMSRxqFuIGdp4bqjbiBsw6BtIHRow60gZOG7pS4gxJDDonkgbMOgIGLhu5kgc+G7kSBsaeG7h3UgduG7gSBz4buRIGzGsOG7o25nIGLhuqFjaCBj4bqndSBDRDQgxJFvIMSRxrDhu6NjIHRyw6puIDYwMCB0cuG6uyBlbSBzaW5oIHJhIHThu6sgbmfGsOG7nWkgbeG6uSBi4buLIG5oaeG7hW0gSElWLiBN4bulYyB0acOqdSBj4bunYSB0aMOtIGThu6UgbMOgIHjDonkgZOG7sW5nIG3DtCBow6xuaCBk4buxIGLDoW8gZ2nDoSB0cuG7iyBsxrDhu6NuZyBi4bqhY2ggY+G6p3UgQ0Q0IHRoZW8gdHXhu5VpIHRyb25nIGdp4bubaSBo4bqhbiB04burIHPGoSBzaW5oIMSR4bq/biA4IHR14buVaS4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCmRhdGEoQ0Q0KQ0KDQpwc3ljaDo6ZGVzY3JpYmUoQ0Q0KQ0KDQpgYGANCg0KVGjEg20gZMOyIHPhu5EgbGnhu4d1IGNobyB0YSB0aOG6pXkgQmnhur9uIGNkNCBjw7MgdHJ1bmcgYsOsbmggPSA1NTcuNTMsIGdpw6EgdHLhu4sgbmjhu48gbmjhuqV0IGzDoCAwLCBs4bubbiBuaOG6pXQgbMOgIDIzMjcgKMSRxqFuIHbhu4sgdOG6vyBiw6BvL21sIG3DoXUpLCBza2V3bmVzcyA9IDEuMjYsIGt1cnRvc2lzID0gMS4xMyB2w6Agc2Q9NDYyLjUNCk5o4buvbmcgZ2nDoSB0cuG7iyBuw6B5IGfhu6NpIMO9IHbhu4EgbeG7mXQgcGjDom4gcGjhu5FpIMSRaeG7g24gaMOsbmggY+G7p2EgYmnhur9uIHPhu5EgxJHhur9tIChjb3VudCBkYXRhKSwgdGjhu7FjIGNo4bqldCBz4buRIGzGsOG7o25nIHThur8gYsOgbyBsw6AgMSBiaeG6v24ga2nhu4N1IHPhu5EgdsOgIHLhu51pIHLhuqFjIGNo4buJIG5o4bqtbiBnacOhIHRy4buLIHPhu5Egbmd1ecOqbiBkxrDGoW5nLiANCg0KIyBOaOG6rW4gxJHhu4tuaCB0cuG7sWMgcXVhbiB24buBIMSR4bq3YyB0w61uaCBwaMOibiBwaOG7kWkNCg0KUXV5IHRyw6xuaCB0aMSDbSBkw7IsIG3DtCB04bqjIHbDoCB0aOG7rSBuZ2hp4buHbSBjw7MgdGjhu4MgxJHGsOG7o2MgdGjhu7FjIHRoaSB0aMO0bmcgcXVhIDIgaMOgbSB0cm9uZyBnYW1sc3MgOiB0cnVlaGlzdCwgaGlzdERpc3QNCg0KSMOgbSB0cnVlaGlzdCBjaOG7iSDEkcahbiBnaeG6o24gduG6vSByYSAxIGhpc3RvZ3JhbSwgdCBjw7MgY8WpbmcgdGjhu4MgbMOgbSB2aeG7h2MgbsOgeSBk4buFIGTDoG5nIHbhu5tpIGJhc2UgUiBoYXkgY+G6p3Uga8OsIGjGoW4gduG7m2kgZ2dwbG90Mg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCnRydWVoaXN0KENENCRjZDQpDQoNCmhpc3QoQ0Q0JGNkNCkNCg0KQ0Q0JT4lZ2dwbG90KGFlcyh4PWNkNCkpKw0KICBnZW9tX2RlbnNpdHkoYWxwaGE9LjgsZmlsbD0iI2M1MDdmZiIpKw0KICBnZW9tX2hpc3RvZ3JhbShhZXMoeT0uLmRlbnNpdHkuLixmaWxsPS4uZGVuc2l0eS4uKSxjb2xvdXI9ImJsYWNrIixhbHBoYT0wLjcsc2hvdy5sZWdlbmQgPSBGKSsNCiAgdGhlbWVfYncoKStzY2FsZV9maWxsX2dyYWRpZW50KGxvdz0iI2ZmOWMwNyIsaGlnaD0iI2ZmMDcyZiIpKw0KICBnZ3RpdGxlKCJEZW5zaXR5ICsgSGlzdG9ncmFtIikNCg0KY2RmZGY9Y2JpbmQuZGF0YS5mcmFtZShjZDQ9dW5pcXVlKENENCRjZDQpLGVjZGYgPSBlY2RmKENENCRjZDQpKHVuaXF1ZShDRDQkY2Q0KSkpDQoNCmdncGxvdChjZGZkZikgKyANCiAgZ2VvbV9hcmVhKGFlcyh4PWNkNCwgeT1lY2RmKSxjb2xvcj0icmVkIixmaWxsPSIjZmYwNzQxIixhbHBoYT0wLjgpKw0KICB0aGVtZV9idygpK3NjYWxlX3hfY29udGludW91cygiQ0Q0IGNvdW50IikrDQogIHNjYWxlX3lfY29udGludW91cygiZWNkZiIpKw0KICBnZ3RpdGxlKCJFQ0RGIG9mIGNkNCIpDQpgYGANCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCkNENCU+JWdncGxvdChhZXMoeD1hZ2UseT1jZDQpKSsNCiAgZ2VvbV9wb2ludChzaGFwZT0yMSxhZXMoY29sb3I9YWdlLGZpbGw9YWdlKSxzaG93LmxlZ2VuZCA9IEYpKw0KICBnZW9tX3Ntb290aChzZT1GLGNvbG9yPSJkYXJrdmlvbGV0IikrDQogIHRoZW1lX2J3KCkrDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGhpZ2g9IiM4ZTAwYjIiLG1pZD0iI2ZmOWMwNyIsbG93PSIjZWY5YjA5IikrDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50MihoaWdoPSIjOGUwMGIyIixtaWQ9IiNmZjljMDciLGxvdz0iI2VmOWIwOSIpDQoNCmBgYA0KDQojIFRoxINtIGTDsiBwaMOibiBwaOG7kWkgZOG7sWEgdsOgbyBtw7QgaMOsbmggdOG7kWkgZ2nhuqNuDQoNCkjDoG0gaGlzdERpc3QgY2hvIHBow6lwIHRoxINtIGTDsiAxIGjhu40gcGjDom4gcGjhu5FpIGLhuqV0IGvDrCBtw6AgZ2FtbHNzLmRpc3QgaOG7lyB0cuG7oywgdGjDrSBk4bulIE5lZ2F0aXZlIGJpbm9taWFsLCBi4bqjbiBjaOG6pXQgY+G7p2EgbsOzIGNow61uaCBsw6AgbeG7mXQgbcO0IGjDrG5oIHThu5FpIGdp4bqjbiBjaOG7iSBjaOG7qWEgSW50ZXJjZXB0IHbhu5tpIDEgaOG7jSBwaMOibiBwaOG7kWkgeMOhYyDEkeG7i25oLiBNw7QgaMOsbmggbsOgeSBjxaluZyDEkcaw4bujYyBsxrDhu6NuZyBnacOhIGLhurFuZyBCSUMsIEFJQywgTGlrZWxpaG9vZCByYXRpb+KApiDEkeG7gyBk4buxYSB2w6BvIMSRw7MgdGEgY8OzIHRo4buDIHNvIHPDoW5oIGPDoWMgaOG7jSBwaMOibiBwaOG7kWkgduG7m2kgbmhhdS4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQpoaXN0RGlzdChkYXRhPUNENCxjZDQsZmFtaWx5PU5CSSgpKQ0KDQpgYGANCg0KZml0RGlzdCBsw6AgMSBow6BtIOKAnHRo4bqnbiBrw6zigJ0sIGtoaSDDoXAgZOG7pW5nIG7DsyBjaG8gMSBiaeG6v24gc+G7kSBZIHRhIGNo4buJIGPhuqduIGN1bmcgY+G6pXAgdGjDtG5nIHRpbiB24buBIGLhuqNuIGNo4bqldCBj4bunYSB0aGFuZyDEkW8gKHRow60gZOG7pSBz4buRIHRo4buxYywgc+G7kSB0aOG7sWMgbeG7nyBy4buZbmcsIGjhu5duIGjhu6NwLCBz4buRIMSR4bq/bSwgeMOhYyBzdeG6pXTigKYpLCBnYW1sc3Mgc+G6vSBz4butIGThu6VuZyBicnV0ZS1mb3JjZSDEkeG7gyBraeG7g20gdHJhIHRvw6BuIGLhu5kgKHThu6sgMjAtNDAgbG/huqFpKSBuaOG7r25nIGjhu40gcGjDom4gcGjhu5FpIHRp4buBbSBuxINuZyBjaG8gYmnhur9uIHPhu5EgbsOgeSwgdsOgIHThu7EgxJHhu5luZyB4w6FjIMSR4buLbmggcGjDom4gcGjhu5FpIHThu5FpIMawdSBuaOG6pXQuIA0KDQpUcm9uZyB0aMOtIGThu6UgbsOgeSwgaMOgbSBmaXREaXN0IGNobyBiaeG6v3QgcGjDom4gcGjhu5FpIHRow61jaCBo4bujcCBuaOG6pXQgY2hvIENENCBsw6AgWmVybyBpbmZsYXRlZCBuZWdhdGl2ZSBiaW5vbWlhbCAoWklOQkkpLiBMxrB1IMO9IGzDoCB0acOqdSBjaHXhuqluIGzhu7FhIGNo4buNbiDhu58gxJHDonkgbMOgIEJJQyAoQUlDIGhp4buHdSBjaOG7iW5oIHbhu5tpIGs9bG9nKGPhu6EgbeG6q3UpKQ0KDQpPYmplY3QgeHXhuqV0IHJhIGzDoCAxIG3DtCBow6xuaCB04buRaSBnaeG6o24gduG7m2kgcGjDom4gcGjhu5FpIFpJTkJJLCB0YSBjw7MgdGjhu4Mga2jhuqNvIHPDoXQgbsOzIDoNClRhIGPDsyB0aOG7gyB4w6JtIG5o4bqtcCB2w6BvIG9iamVjdCDEkeG7gyB4ZW0geMOpdCBCSUMgY+G7p2EgMTYgaOG7jSBwaMOibiBwaOG7kWkgbcOgIG3DtCBow6xuaCBjw7MgdGjhu4MgY29udmVyZ2VkLCBaSU5CSSDEkeG7qW5nIMSR4bqndSBkYW5oIHPDoWNoIGPDsm4gUG9pc3NvbiB44bq/cCBjaMOzdCBi4bqjbmcNCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNCnNldC5zZWVkPTEyMw0KDQpleHBsb3JlPWZpdERpc3QoY2Q0LGRhdGE9Q0Q0LHR5cGU9ImNvdW50cyIsaz1sb2cobnJvdyhDRDQpKSxwYXJhbGxlbD0ibXVsdGljb3JlIixuY3B1cyA9IG5DKQ0KDQpleHBsb3JlDQoNCnBsb3QoZXhwbG9yZSkNCg0KZXhwbG9yZSRmaXRzDQoNCmBgYA0KDQpDw7MgMSBwYWNrYWdlIGtow6FjIHRyb25nIFIgbMOgIGZpdGRpc3RycGx1cyBjxaluZyBjaG8gcGjDqXAgbMOgbSDEkWnhu4F1IHTGsMahbmcgdOG7sSwgY2jhu4kga2jDoWMgbMOgIG7DsyBraMO0bmcgcGjhu5UgcXXDoXQgbmjGsCBnYW1sc3MuZGlzdCwgdsOsIHPhu5EgaOG7jSBwaMOibiBwaOG7kWkgbcOgIGjDoG0gZGVzY2Rpc3QgaOG7lyB0cuG7oyBjaOG7iSDEkeG6v20gdHLDqm4gxJHhuqd1IG5nw7NuIHRheS4gVGEgdGjhu60ga2jhuqNvIHPDoXQgYmnhur9uIGNkNCBi4bqxbmcgcGFja2FnZSBmaXRkaXN0cnBsdXMgbmjGsCBzYXU6DQoNCkThu7FhIHbDoG8gMSBib290c3RyYXAgMTAgbmfDoG4gbMaw4bujdCB2w6AgQmnhu4N1IMSR4buTIEN1bGxlbiZGcmV5ICwgdGEgY8OzIHRo4buDIGto4bqzbmcgxJHhu4tuaCBwaMOibiBwaOG7kWkgY+G7p2EgY2Q0IEtIw5RORyB0aOG7gyBsw6AgUG9pc3NvbiwgbcOgIGPFqW5nIGNo4bqzbmcgcGjhuqNpIGzDoCBOZWdhdGl2ZSBiaW5vbWlhbCB2w6AgY2jhuq9jIGNo4bqvbiBraMO0bmcgdGjhu4MgbMOgIG5vcm1hbA0KDQpgYGB7cn0NCmxpYnJhcnkoZml0ZGlzdHJwbHVzKQ0KZGVzY2Rpc3QoQ0Q0JGNkNCxkaXNjcmV0ZSA9IFRSVUUsYm9vdD0xMDAwMCxib290LmNvbD0iZ29sZCIsb2JzLnBjaD0xNixvYnMuY29sPSJyZWQ0IikNCmBgYA0KDQpHYW1sc3MgaOG7lyB0cuG7oyAyNCBo4buNIHBow6JuIHBo4buRaSBjaG8gc+G7kSDEkeG6v20gKGNvdW50IGRhdGEpICwgxJHhurdjIHTDrW5oIGPhu6dhIGNow7puZyBu4bqxbSB0cm9uZyBi4bqjbmcgc2F1IMSRw6J5Og0KUGjDom4gcGjhu5FpIFpJTkJJIGPDsyAzIHRoYW0gc+G7kSBNdSAobGluayBmdW5jdGlvbj0gbG9nKSwgU2lnbWEgKGxpbmsgZnVuY3Rpb24gPSBsb2cpIHbDoCBOdSAobGluayBmdW5jdGlvbiA9IGxvZ2l0KS4NCg0KIVtdKGNvdW50ZGlzdC5wbmcpIA0KDQpUYSBjw7MgdGjhu4MgdMOsbSBoaeG7g3UgduG7gSBi4bqjbiBjaOG6pXQgY+G7p2EgaOG7jSBwaMOibiBwaOG7kWkgYuG6sW5nIGjDoG0gZ2FtbHNzLmZhbWlseSB2w6Agc2hvdy5saW5rDQoNCmBgYHtyfQ0KZ2FtbHNzLmZhbWlseShaSU5CSSkNCg0Kc2hvdy5saW5rKFpJTkJJKQ0KYGBgDQoNCk1vZHVsZSBnYW1sc3MudHIgY2hvIHBow6lwIG5nxrDhu51pIGTDuW5nIHThuqFvIHJhIDEgcGjDom4gcGjhu5FpIGPhu6dhIHJpw6puZyBtw6xuaCBi4bqxbmcgY8OhY2ggY2jhurduIDIgxJHhuqd1IDEgcGjDom4gcGjhu5FpIGNobyB0csaw4bubYywgdGjDrSBk4bulIHRhIGPDsyB0aOG7gyB04bqhbyByYSAxIHBow6JuIHBo4buRaSBt4bubaSBjw7MgdMOqbiBsw6AgWklOQkkxMDAwIHbhu5tpIGdpw6EgdHLhu4sgZGFvIMSR4buZbmcgdHJvbmcga2hv4bqjbmcgdOG7qyAwLTEwMDANCg0KVOG7qyBsw7pjIG7DoHksIGjhu40gcGjDom4gcGjhu5FpIG3hu5tpIGPDsyB0aOG7gyDEkcaw4bujYyDEkcawYSB2w6BvIGjDoG0gZml0RGlzdCB2w6AgY+G6oyBow6BtIGdhbWxzcyDEkeG7gyBz4butIGThu6VuZyBuaMawIGLhuqV0IGPhu6kgaOG7jSBwaMOibiBwaOG7kWkgbsOgbyDEkcaw4bujYyBnYW1sc3MgY2jDrW5oIHRo4bupYyBo4buXIHRy4bujLg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoZ2FtbHNzLnRyKQ0KZ2VuLnRydW4ocGFyPWMoMCwxMDAwKSxmYW1pbHk9IlpJTkJJIiwgbmFtZT0iMTAwMCIsIHR5cGU9InJpZ2h0Iix2YXJ5aW5nPVQpDQoNCmdhbWxzcy5mYW1pbHkoWklOQkkxMDAwKQ0KDQpmaXREaXN0KGNkNCxkYXRhPUNENCx0eXBlPSJjb3VudHMiLGs9bG9nKG5yb3coQ0Q0KSksZXh0cmE9IlpJTkJJMTAwMCIscGFyYWxsZWw9Im11bHRpY29yZSIsbmNwdXMgPSBuQykNCg0KYGBgDQoNCiMgQ2jhu41uIGzhu41jIGjhu40gcGjDom4gcGjhu5FpIHRyb25nIHF1w6EgdHLDrG5oIHjDonkgZOG7sW5nIG3DtCBow6xuaDogVnVvbmcgdGVzdCB2w6Agc3RlcHdpc2UgR0FJQw0KDQpCw6J5IGdp4budIE5oaSBz4bq9IHRo4butIGPDoWNoIGzDoG0ga2jDoWMsIMSRw7MgbMOgIGtp4buDbSB0cmEgaOG7jSBwaMOibiBwaOG7kWkgVFJPTkcgS0hJIGThu7FuZyBtw7QgaMOsbmgsIHThu6ljIGzDoCBtw7QgaMOsbmggc+G6vSBjaOG7qWEgxJHhuqd5IMSR4bunIHByZWRpY3RvciBjaOG7qSBraMO0bmcgY2jhu4kgY8OzIEludGVyY2VwdC4gQ8OhY2ggbMOgbSB0aOG7pyBjw7RuZyDEkcahbiBnaeG6o24gbmjhuqV0IMSRw7MgbMOgIHNvIHPDoW5oIGLhuq90IGPhurdwIDIgbcO0IGjDrG5oIHbhu5tpIDIgaOG7jSBwaMOibiBwaOG7kWkga2jDoWMgbmhhdSAodGjDrSBk4bulIFBvaXNzb24gdsOgIFpJTkJJKSwgc2F1IMSRw7Mgc+G7rSBk4bulbmcgMiBraeG7g20gxJHhu4tuaCBWxrDGoW5nLUNsYXJrZSDEkeG7gyBzbyBzw6FuaCDEkeG7mSBwaMO5IGjhu6NwIGThu68gbGnhu4d1IGPhu6dhIDIgbcO0IGjDrG5oICh0YSBraMO0bmcgdGjhu4MgZMO5bmcgTGlrZWxpaG9vZCByYXRpbyB0ZXN0IHRow7RuZyB0aMaw4budbmcgZG8gMiBtw7QgaMOsbmggbsOgeSBub24tbmVzdGVkKQ0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSJoaWRlIn0NCm1QTz1nYW1sc3MoZm9ybXVsYT1jZDR+cGIoYWdlKSxkYXRhPUNENCxmYW1pbHk9UE8oKSwNCiAgICAgICAgICBwYXJhbGxlbD0ibXVsdGljb3JlIiwNCiAgICAgICAgICBuY3B1cyA9IG5DKQ0KDQptWklOQkk9Z2FtbHNzKGZvcm11bGE9Y2Q0fnBiKGFnZSksZGF0YT1DRDQsZmFtaWx5PVpJTkJJKCksDQogICAgICAgICAgICAgIHBhcmFsbGVsPSJtdWx0aWNvcmUiLA0KICAgICAgICAgICAgICBuY3B1cyA9IG5DKQ0KDQpgYGANCg0KS+G6v3QgcXXhuqMgY+G7p2EgVnVvbmcgdGVzdCBjaG8gcmEgY2jhu4kgc+G7kSB0aOG7kW5nIGvDqiBaIHLhuqV0IHRo4bqlcCwgY8OybiBDbGFya2UgdGVzdCB0aMOsIGNobyByYSBnacOhIHRy4buLIHAgcuG6pXQgdGjhuqVwLCBjaG8gdGjhuqV5IG3DtCBow6xuaCBaSU5CSSB04buRdCBoxqFuIHNvIHbhu5tpIFBvaXNzb24NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0V9DQoNClZDLnRlc3QobVBPLCBtWklOQkksIHNpZy5sZXYgPSAwLjA1KQ0KYGBgDQoNClRhIGPFqW5nIGPDsyB0aOG7gyBkw7luZyB0acOqdSBjaHXhuqluIEFJQyB2w6AgQklDIMSR4buDIGzhu7FhIGNo4buNbiBtw7QgaMOsbmggOg0KS+G6v3QgcXXhuqMgY+G7p2EgY+G6oyBCSUMgdsOgIEFJQyDEkeG7gXUg4bunbmcgaOG7mSBjaG8gbcO0IGjDrG5oIFpJTkJJDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFfQ0KR0FJQyhtUE8sbVpJTkJJKQ0KDQpgYGANCg0KYGBge3J9DQpHQUlDKG1QTyxtWklOQkksaz1sb2cobnJvdyhDRDQpKSkNCg0KYGBgDQoNClRhIGPDsyB0aOG7gyB0aOG7sWMgaGnhu4duIHF1eSB0csOsbmggbsOgeSBjaG8gaMOgbmcgbG/huqF0IGJp4bq/biBz4buRIG5oxrBuZyBz4butIGThu6VuZyB0cuG7sWMgdGnhur9wIHRpw6p1IGNow60gQUlDIGhv4bq3YyBCSUMgxJHhu4MgcXV54bq/dCDEkeG7i25oIGzhu7FhIGNo4buNbiBwaMOibiBwaOG7kWkgbsOgby4gVHJvbmcgdGjDrSBk4bulIHNhdSBOaGkgc28gc8OhbmggNCBtw7QgaMOsbmggduG7m2kgcGjDom4gcGjhu5FpIFBvaXNzb24sIE5lZ2F0aXZlIGJpbm9taWFsLCBaZXJvIGluZmxhdGVkIHBvaXNzb24sICB2w6AgemVybyBpbmZsYXRlZCBuZWdhdGl2ZSBiaW5vbWlhbC4NCg0KYGBge3IsbWVzc2FnZSA9IEZBTFNFLHdhcm5pbmc9RkFMU0UscmVzdWx0cz0iaGlkZSJ9DQojR0FJQw0KDQptUE89Z2FtbHNzKGNkNH5wYihhZ2UpLA0KICAgICAgICAgICAgZGF0YT1DRDQsDQogICAgICAgICAgICBmYW1pbHk9UE8oKSwgDQogICAgICAgICAgICByYW5kPXJhbmQsDQogICAgICAgICAgICBwYXJhbGxlbD0ibXVsdGljb3JlIiwNCiAgICAgICAgICAgIG5jcHVzID0gbkMpDQoNCm1aSU5CST1nYW1sc3MoY2Q0fnBiKGFnZSksDQogICAgICAgICAgICBkYXRhPUNENCwNCiAgICAgICAgICAgIGZhbWlseT1aSU5CSSgpLCANCiAgICAgICAgICAgIHJhbmQ9cmFuZCwNCiAgICAgICAgICAgIHBhcmFsbGVsPSJtdWx0aWNvcmUiLA0KICAgICAgICAgICAgbmNwdXMgPSBuQykNCg0KbU5CST1nYW1sc3MoY2Q0fnBiKGFnZSksDQogICAgICAgICAgZGF0YT1DRDQsDQogICAgICAgICAgZmFtaWx5PU5CSUkoKSwgDQogICAgICAgICAgcmFuZD1yYW5kLA0KICAgICAgICAgIHBhcmFsbGVsPSJtdWx0aWNvcmUiLA0KICAgICAgICAgIG5jcHVzID0gbkMpDQoNCm1aSVA9Z2FtbHNzKGNkNH5wYihhZ2UpLA0KICAgICAgICAgICAgZGF0YT1DRDQsDQogICAgICAgICAgICBmYW1pbHk9WklQKCksIA0KICAgICAgICAgICAgcmFuZD1yYW5kLA0KICAgICAgICAgICAgcGFyYWxsZWw9Im11bHRpY29yZSIsDQogICAgICAgICAgICBuY3B1cyA9IG5DLG1ldGhvZD1SUyg1MDApKQ0KDQpgYGANCg0KYGBge3J9DQpHQUlDKG1QTyxtWklQLG1aSU5CSSxtTkJJLGs9bG9nKG5yb3coQ0Q0KSkpDQoNCmBgYA0KDQojIENo4buNbiBs4buNYyBxdXkgbHXhuq10IHBow6JuIHBo4buRaSBk4buxYSB2w6BvIEsgZm9sZHMgY3Jvc3MgdmFsaWRhdGlvbg0KDQpLaeG7g20gY2jhu6luZyBjaMOpbyBs4bq3cCBs4bqhaSBsw6AgMSBjw6FjaCBsw6BtIGtow6FjLiBOZ2/DoGkgcXV5IHRyw6xuaCBzdGVwd2lzZSwgZ2FtbHNzIGPDsm4gY2hvIHBow6lwIGTDuW5nIEsgZm9sZCBjcm9zcyB2YWxpZGF0aW9uIMSR4buDIHNvIHPDoW5oIMSR4buZIHBow7kgaOG7o3AgY+G7p2EgaMOgbmcgbG/huqF0IGjhu40gcGjDom4gcGjhu5FpLCB0aMOtIGThu6UgUG9pc3NvbiwgTmVnYXRpdmUgYmlub21pYWwsIFplcm8gaW5mbGF0ZWQgcG9pc3NvbiwgIHplcm8gaW5mbGF0ZWQgbmVnYXRpdmUgYmlub21pYWwuIENow7puZyB0YSBz4bq9IHRo4buxYyBoaeG7h24gMSBxdXkgdHLDrG5oIG5oxrAgduG6rXkgduG7m2kgMTAgZm9sZHMgY3Jvc3MgdmFsaWRhdGlvbiwgY2hpYSBuZ+G6q3Ugbmhpw6puIGRhdGFzZXQgQ0Q0IHJhIHRow6BuaCAxMCBibG9ja3MsIGTDuW5nIDkgYmxvY2tzIMSR4buDIGThu7FuZyBtw7QgaMOsbmggcuG7k2kga2nhu4NtIMSR4buLbmggdHLDqm4gMSBibG9jayBjw7JuIGzhuqFpLCBj4bupIHRo4bq/IGzhurdwIGzhuqFpIDEwIGzhuqduLg0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSJoaWRlIn0NCnNldC5zZWVkKDEyMykNCnJhbmQ9c2FtcGxlICgxMCwgbnJvdyhDRDQpLCByZXBsYWNlPVRSVUUpDQoNCm1jdlBPPWdhbWxzc0NWKGNkNH5wYihhZ2UpLA0KICAgICAgICAgICAgICAgZGF0YT1DRDQsDQogICAgICAgICAgICAgICBmYW1pbHk9UE8oKSwgDQogICAgICAgICAgICAgICByYW5kPXJhbmQsDQogICAgICAgICAgICAgICBwYXJhbGxlbD0ibXVsdGljb3JlIiwNCiAgICAgICAgICAgICAgIG5jcHVzID0gbkMpDQoNCm1jdk5CST1nYW1sc3NDVihjZDR+cGIoYWdlKSwNCiAgICAgICAgICAgIGRhdGE9Q0Q0LA0KICAgICAgICAgICAgZmFtaWx5PU5CSUkoKSwgDQogICAgICAgICAgICByYW5kPXJhbmQsDQogICAgICAgICAgICBwYXJhbGxlbD0ibXVsdGljb3JlIiwNCiAgICAgICAgICAgIG5jcHVzID0gbkMpDQoNCm1jdlpJTkJJPWdhbWxzc0NWKGNkNH5wYihhZ2UpLA0KICAgICAgICAgICAgZGF0YT1DRDQsDQogICAgICAgICAgICBmYW1pbHk9WklOQkkoKSwgDQogICAgICAgICAgICByYW5kPXJhbmQsDQogICAgICAgICAgICBwYXJhbGxlbD0ibXVsdGljb3JlIiwNCiAgICAgICAgICAgIG5jcHVzID0gbkMpDQoNCm1jdlpJUD1nYW1sc3NDVihjZDR+cGIoYWdlKSwNCiAgICAgICAgICAgIGRhdGE9Q0Q0LA0KICAgICAgICAgICAgZmFtaWx5PVpJUCgpLCANCiAgICAgICAgICAgIHJhbmQ9cmFuZCwNCiAgICAgICAgICAgIHBhcmFsbGVsPSJtdWx0aWNvcmUiLA0KICAgICAgICAgICAgbmNwdXMgPSBuQykNCg0KYGBgDQoNCmBgYHtyfQ0KQ1YobWN2WklQLG1jdlpJTkJJLG1jdk5CSSxtY3ZQTykNCg0KYGBgDQoNCiMgQ2jhu41uIGzhu41jIGJp4bq/biBz4buRIGNobyBtw7QgaMOsbmggY+G7p2EgdOG7q25nIHRoYW0gc+G7kSwgc+G7rSBk4bulbmcgU3RlcHdpc2VHQUlDDQoNCk3hu5l0IGtoaSDEkcOjIGNo4bqvYyBjaOG6r24gduG7gSBo4buNIHBow6JuIHBo4buRaSwgdGEgY8OzIHRo4buDIMSRaSBixrDhu5tjIHRp4bq/cCB0aGVvLCDEkcOzIGzDoCB4w6FjIMSR4buLbmggeGVtIGxp4buHdSBjw7MgY+G6p24gbcO0IGjDrG5oIGjDs2EgY2hvIHThuqV0IGPhuqMgMyB0aGFtIHPhu5Ega2jDtG5nID8gdsOgIG3DtCBow6xuaCBwaOG7qWMgdOG6oXAgKHPDonUpIMSR4bq/biBt4bupYyBuw6BvID8gSGF5IG7Ds2kgY8OhY2gga2jDoWM6IFRhIHPhur0gcGjDom4gcGjhu5FpIHByZWRpY3RvciB2w6BvIG3hu5dpIHRoYW0gc+G7kSBNdSwgU2lnbWEsIE51IG5oxrAgdGjhur8gbsOgbyBjaG8gaOG7o3AgbMO9ID8gVsOsIHRoZW8gbeG6t2MgxJHhu4tuaCwgbuG6v3Uga2jDtG5nIGtoYWkgYsOhbyBmb3JtdWxhIGNobyBTaWdtYSB2w6AgTnUgdGjDrCBnYW1sc3MgxrB1IHRpw6puIGThu7FuZyBtw7QgaMOsbmggY2hvIE11ICh0cnVuZyBiw6xuaCBk4buxIGLDoW8pLCBjw7JuIHNpZ21hLCBOdSB2w6AgVGF1IGNo4buJIMSRxrDhu6NjIG3DtCBow6xuaCBi4bqxbmcgaOG6sW5nIHPhu5EgSW50ZXJjZXB0LiBDw6J1IGjhu49pIG7DoHkgY8OzIHRo4buDIMSRxrDhu6NjIGdp4bqjaSBxdXnhur90IGLhurFuZyBow6BtIHN0ZXBHQUlDQWxsLkE7IGjDoG0gbsOgeSBz4bq9IHRo4buxYyBoaeG7h24gMSBxdXkgdHLDrG5oIGzhu7FhIGNo4buNbiBiaeG6v24gc+G7kSBjaG8gdOG7q25nIHRoYW0gc+G7kSBNdSwgU2lnbWEsIE51ICh2w6AgVGF1LCBu4bq/dSBj4bqnbikgdGhlbyBxdXkgdOG6r2Mgc3RlcHdpc2UsIHThu6ljIGzDoCBsb+G6oWkgYuG7jyBk4bqnbiBk4bqnbiBiaeG6v24gc+G7kSwgZOG7sWEgdsOgbyB0acOqdSBjaHXhuqluIEFJQyBob+G6t2MgQklDLiBUcm9uZyB0aMOtIGThu6UgZMaw4bubaSDEkcOieSwgY2jDum5nIHRhIMSR4bq3dCByYSAyIG5nxrDhu6FuZzogdGjhuqVwIG5o4bqldCBsw6AgMSBtw7QgaMOsbmggY2jhu4kgY2jhu6lhIEludGVyY2VwdCwgY2FvIG5o4bqldCBsw6AgbcO0IGjDrG5oIMSRYSB0aOG7qWMgYuG6rWMgNSBjaG8gYmnhur9uIHPhu5EgYWdlIGvDqG0gdGhlbyBwZW5hbGl6ZSBCIHNwbGluZSBjaG8gYWdlLCB0acOqdSBjaHXhuqluIGzhu7FhIGNo4buNbiBsw6AgQklDLiANCg0KxJDhurdjIGJp4buHdCwgZ2FtbHNzIGNobyBwaMOpcCB0aOG7sWMgaGnhu4duIHF1eSB0csOsbmggc29uZyBzb25nIHbDoCB04bqtbiBk4bulbmcgdOG7kWkgxJFhIGzDoCA0IGNvcmVzIHRyb25nIG3DoXkgdMOtbmggxJHhu4MgdMSDbmcgdOG7kWMgxJHhu5kgeOG7rSBsw70gKHTEg25nIMSR4bq/biA1MCUgY2hvIDEgbcOheSB0w61uaCBDb3JlIGk1KQ0KDQoNCmBgYHtyLG1lc3NhZ2UgPSBGQUxTRSx3YXJuaW5nPUZBTFNFLHJlc3VsdHM9ImhpZGUifQ0KbTE8LWdhbWxzcyhjZDR+MSwgZGF0YT1DRDQsIGZhbWlseT1aSU5CSSgpLCB0cmFjZT1GQUxTRSkNCm0yPC1zdGVwR0FJQ0FsbC5BKG0xLCANCiAgICAgICAgICAgICAgICAgIHNjb3BlPWxpc3QobG93ZXI9fjEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cHBlcj1+cG9seShhZ2UsNSkrcGIoYWdlKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLA0KICAgICAgICAgICAgICAgICAgaz1sb2cobnJvdyhDRDQpKSwNCiAgICAgICAgICAgICAgICAgIHBhcmFsbGVsPSJtdWx0aWNvcmUiLA0KICAgICAgICAgICAgICAgICAgbmNwdXMgPSBuQw0KICAgICAgICAgICAgICAgICAgKQ0KYGBgDQoNCkvhur90IHF14bqjIGtow7RuZyBi4bqldCBuZ+G7nSBs4bqvbTogcXV5IHRyw6xuaCBzdGVwd2lzZUFJQyBjaG8gdGjhuqV5IGNo4buJIGPhuqduIHBic3BsaW5lIGNobyBNdSwgdsOgIEludGVyY2VwdCBjaG8gMiB0aGFtIHPhu5EgU2lnbWEgdsOgIE51Lg0KDQpgYGB7cn0NCnN1bW1hcnkobTIpDQoNCmBgYA0KDQpgYGB7cixtZXNzYWdlID0gRkFMU0Usd2FybmluZz1GQUxTRSxyZXN1bHRzPSJoaWRlIn0NCm1vZGVsPC1nYW1sc3MoY2Q0fnBiKGFnZSksZGF0YT1DRDQsIGZhbWlseT1aSU5CSSgpLCB0cmFjZT1GQUxTRSkNCg0KYGBgDQoNCg0KQ2jDum5nIHRhIGvhur90IHRow7pjIGLDoGkgaMO0bSBuYXkgYuG6sW5nIDIgaMOsbmggduG6vSDEkeG6uXAgbeG6r3QgYmnhu4N1IGRp4buFbiDEkeG7kyB0aOG7iyBj4bunYSAxICBtw7QgaMOsbmggemVyb2luZmxhdGVkIG5lZ2F0aXRpdmUgYmlub21pYWwga8OobSB0aGVvIHNwbGluZSBiw7kgdHLhu6sgbmjhurFtIMaw4bubYyB0w61uaCBz4buRIGzGsOG7o25nIHThur8gYsOgbyBi4bqhY2ggY+G6p3UgQ0Q0IHRoZW8gdHXhu5VpIOG7nyB0cuG6uyBuaOG7jy4gDQoNCmBgYHtyfQ0Kc3VtbWFyeShtb2RlbCkNCg0KcGxvdChtb2RlbCkNCg0KQ0Q0JHByZWRpY3Q9cHJlZGljdChtb2RlbCx0eXBlPSJyZXNwb25zZSIpDQpDRDQlPiVnZ3Bsb3QoKSsNCiAgZ2VvbV9wb2ludChzaGFwZT0yMSxhZXMoeD1hZ2UseT1jZDQsY29sb3I9YWdlLGZpbGw9YWdlKSxzaG93LmxlZ2VuZCA9IEYpKw0KICBnZW9tX3Ntb290aChhZXMoeD1hZ2UseT1wcmVkaWN0KSxzZT1GLGNvbG9yPSJkYXJrdmlvbGV0IixsaW5ldHlwZT0yKSsNCiAgdGhlbWVfYncoKSsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudDIoaGlnaD0iIzhlMDBiMiIsbWlkPSIjZmY5YzA3Iixsb3c9IiNlZjliMDkiKSsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGhpZ2g9IiM4ZTAwYjIiLG1pZD0iI2ZmOWMwNyIsbG93PSIjZWY5YjA5IikNCg0KbGlicmFyeShnYW1sc3MudXRpbCkNCmxpYnJhcnkodmlyaWRpc0xpdGUpDQoNCnBsb3RTaW1wbGVHYW1sc3MoeT1jZDQseD1hZ2UsZm9ybXVsYT1jZDR+cGIoYWdlKSxmYW1pbHk9WklOQkkoKSxkYXRhPUNENCxjb2xzPXZpcmlkaXMob3B0aW9uPSJBIixuPTEwMCkseC52YWw9YygxLDIsMyw0LDUsNiw3LDgpLHZhbD0yMDAsTj0xMDAwLHlsaW09YygwLDIzMDApKQ0KDQpgYGANCg0KIyBL4bq/dCBsdeG6rW4NCg0KVHJvbmcgYsOgaSB0aOG7sWMgaMOgbmggdGjhu6kgMiBj4bunYSBzZXJpZXMgduG7gSBHYW1sc3MgbsOgeSwgTmhpIG114buRbiBjaHV54buDbiDEkeG6v24gY8OhYyBi4bqhbiAzIHRow7RuZyDEkWnhu4dwIG5oxrAgc2F1Og0KDQoxKQlLaMOhaSBuaeG7h20gduG7gSB0aMOgbmggcGjhuqduIHBow6JuIHBo4buRaSB0cm9uZyBj4bqldSB0csO6YyBtw7QgaMOsbmggR0FNDQoNCjIpCUPDoWNoIHRoxINtIGTDsiB2w6AgY2jhu41uIGzhu41jIHF1eSBsdeG6rXQgcGjDom4gcGjhu5FpIHBow7kgaOG7o3AgY2hvIG3DtCBow6xuaA0KDQozKQlRdXkgdHLDrG5oIFN0ZXB3aXNlIHbDoCBLZm9sZCBjcm9zcyB2YWxpZGF0aW9uDQoNCkdhbWxzcyBsw6AgbeG7mXQgdGjGsCB2aeG7h24ga2jhu5VuZyBs4buTIHbhu4EgbMO9IHRodXnhur90IHjDoWMgc3XhuqV0LiBUdXkgbmhpw6puIGNow7puZyB0YSBjaOG7iSBt4bubaSBow6kgbeG7nyBjw6FuaCBj4butYSBuw6B5LiBIeSB24buNbmcgduG7m2kgc+G7sSB0w7IgbcOyIGPhu6dhIG3DrG5oLCBjw6FjIGLhuqFuIGPDsyB0aOG7gyB04buxIG3DrG5oIGtow6FtIHBow6EgaMahbiAxMDAgaOG7jSBwaMOibiBwaOG7kWkgY8OybiBs4bqhaSBjaG8gYmnhur9uIHPhu5EgdGjhu7FjLCBiaeG6v24gbGnDqm4gdOG7pWMgZMawxqFuZywgYmnhur9uIGxpw6puIHThu6VjIGdp4bubaSBo4bqhbiwgdOG7iSBs4buHLCB4w6FjIHN14bqldOKApiBUcm9uZyBuaOG7r25nIGLDoGkgdGnhur9wIHRoZW8gY2jDum5nIHRhIHPhur0gdGnhur9wIHThu6VjIGtow6FtIHBow6EgY+G6pXUgdHLDumMgYsOqbiB0cm9uZyBtw7QgaMOsbmggR0FNIGJhbyBn4buTbSB0aMOgbmggdOG7kSBo4buTaSBxdXkgdsOgIGPDoWMgeeG6v3UgdOG7kSBiw7kgdHLhu6suIA0KDQoNCioqWGluIGPhuqNtIMahbiB2w6AgaOG6uW4gZ+G6t3AgbOG6oWkuKioNCg==